Skip to content

Scrapyard

A Lua-only mod: a charging scrap truck the announcer counts, a card that pulls cash from wrecks, a difficulty with its own rule, a run modifier, a weapon that fires a ring of scrap and shows its reach, a new trait for the game's drone rack, and a Safehouse line with code.

Mod id Kind Files
scrapyard Lua code 3

Copy the whole folder into mods/ in the save folder to try it. Browse it on GitHub.

mod.json

{"id": "scrapyard", "title": "Scrapyard", "version": "1.0", "api": 1, "author": "Taxation Evasion",
 "description": "A Lua-only mod: a charging scrap truck the announcer counts, a card that pulls cash from wrecks, a difficulty with its own rule, a run modifier, a weapon that fires a ring of scrap and shows its reach, a new trait for the game's drone rack, and a Safehouse line with code.",
 "scripts": ["main.lua"]}

main.lua

-- Everything here goes through the `game` API (docs/modding/API.md).

-- An enemy: a gang car that charges when close and bursts when destroyed.
game.add('enemies', {
  id = 'scrapyard.truck', base = 'gang', label = 'SCRAP TRUCK',
  sprite = 'player_van', tint = {150, 110, 70},
  hp = 40, speed = 300, gold = 60,
  spawn = {heat = 3, every = 25, max_alive = 2},
  behavior = 'scrapyard.truck',
})
game.enemy('scrapyard.truck', {
  think = function(s, e, dt)
    if game.distance(e, s.player) < 140 then
      game.drive(s, e, s.player.x, s.player.y, 480, dt)
      return true
    end
  end,
  death = function(s, e)
    game.blast(s, e.x, e.y, 48, 200)
    game.sound(s, 'scrapyard.clank', 0.5)   -- sfx/clank.wav
    -- The announcer keeps the tally, with the live number in its line.
    s.scrapyard_wrecks = (s.scrapyard_wrecks or 0) + 1
    game.say(s, 'scrapyard.truck_down', {'Scrap truck down.', s.scrapyard_wrecks .. ' wrecked this run.'})
  end,
})
game.add('announcer', {
  id = 'scrapyard.truck_down', lines = {'Scrap truck down.', 'Keep them coming.'},
  priority = 2, cooldown = 20,
})

-- A card: every wreck pays a little cash per rank.
game.effect('scrapyard.salvage', {
  kills = function(s, count, rank) game.cash(s, 5 * count * rank) end,
})
game.add('cards', {
  id = 'scrapyard.salvage', name = 'SALVAGE', tier = 'part', max = 3, icon = 18,
  description = 'Each wreck pays $5 per rank.', effect = 'scrapyard.salvage',
})

-- A difficulty with a rule only code can express: NOS drains slowly.
game.effect('scrapyard.leaky_tank', {
  tick = function(s, dt) game.nos(s, -0.02 * dt) end,
})
game.add('difficulties', {
  id = 'scrapyard.leaky', numeral = 'L', name = 'LEAKY TANK', short = 'LEAKY NOS',
  rule = 'YOUR NOS TANK SLOWLY DRAINS', payout = 1.5,
  crowd = 1.25, effect = 'scrapyard.leaky_tank',
})

-- A run modifier with code: every wreck mends half a heart.
game.effect('scrapyard.scrap_repairs', {
  kills = function(s, count) game.heal(s, 0.5 * count) end,
})
game.add('modifiers', {
  id = 'scrapyard.scrap_repairs', name = 'SCRAP REPAIRS', description = 'EVERY WRECK MENDS HALF A HEART',
  payout = 0.75, effect = 'scrapyard.scrap_repairs',
})

-- A weapon with a pattern of its own: a ring of scrap whenever an enemy is
-- near. Its gear card and numbers are content; only the firing is code.
game.weapon('scrapyard.ring', {
  fire = function(s, w, dt)
    w.state.wait = (w.state.wait or 0) - dt
    if w.state.wait > 0 or not game.target(s, w.stats.range) then return end
    w.state.wait = w.stats.interval
    for i = 1, w.stats.rounds do
      game.fire(s, w, i / w.stats.rounds * 2 * math.pi)
    end
  end,
  -- Scrap hits undamaged cars twice as hard.
  hit = function(s, e, damage)
    if e.hp >= (e.max_hp or e.hp) then return damage * 2 end
  end,
  -- A faint ring shows the cannon's reach while it is loaded.
  draw = function(s, w, d)
    if (w.state.wait or 0) <= 0 then d.ring(s.player.x, s.player.y, w.stats.range, {170, 140, 100}, 0.35) end
  end,
})
game.add('cards', {
  id = 'scrapyard.scrap_cannon', name = 'SCRAP CANNON', tier = 'gear', branch = 'scrap', max = 1, icon = 12,
  description = 'Throws a ring of scrap when an enemy is near.',
})
game.add('weapons', {
  id = 'scrapyard.scrap_cannon', pattern = 'scrapyard.ring',
  range = 90, interval = 1.5, damage = 2, rounds = 8, speed = 240, color = {170, 140, 100},
})

-- A trait for one of the game's own weapons: with SCRAP COILS, every fifth
-- zap from the drone rack hits three times as hard. The card is an upgrade
-- on the base drone row; the rack keeps all its other cards.
game.trait('drone', 'scrapyard.overcharge', {
  hit = function(s, e, damage, w)
    w.state.zaps = (w.state.zaps or 0) + 1
    if w.state.zaps % 5 == 0 then return damage * 3 end
  end,
})
game.add('cards', {
  id = 'scrapyard.scrap_coils', name = 'SCRAP COILS', tier = 'part', max = 1, icon = 12, requires = 'drone',
  description = 'Every fifth drone zap hits three times as hard.',
})
game.change('weapons', 'drone', {upgrades = {['scrapyard.scrap_coils'] = {['scrapyard.overcharge'] = true}}})

-- A Safehouse line with code: bought between runs, it runs in every run
-- with its level as the rank. Each wreck refills 2% of the NOS tank a level.
game.effect('scrapyard.junk_magnet', {
  kills = function(s, count, rank) game.nos(s, 0.02 * rank * count) end,
})
game.add('safehouse', {
  id = 'scrapyard.junk_magnet', label = 'JUNK MAGNET', line = 'EACH WRECK REFILLS 2% NOS PER LEVEL',
  max = 5, base = 600, effect = 'scrapyard.junk_magnet',
})

sfx/clank.wav