-- SWATTER -- bug-swatting for the Maestro Pointer. -- Sixty seconds. Click bugs. Bigger bugs are slower but worth more. The high -- score is held in cart RAM between sessions. Press SELECT to eject. local W, H = gfx.W, gfx.H -- ---------------- state ---------------------------------------------------- local state = "title" -- "title" | "play" | "over" local clock = 0 -- seconds remaining in round local roundLength = 60 local score = 0 local high = 0 local bugs = {} -- in-play bugs local titleBugs = {} -- decorative bugs on the title screen local stains = {} -- {x, y, t} splat decals local pops = {} -- floating "+N" score popups local particles = {} -- splat particles local swatT = 0 -- frames left of swat-down animation local fadeT = 0 -- transition fade timer local overT = 0 -- frames since round ended local spawnTimer = 0 local function rnd(a, b) return a + (b - a) * math.random() end local function chance(p) return math.random() < p end -- ---------------- palette -------------------------------------------------- local PAL = { trans = 0, black = 1, shadow = 4, midgrey = 8, ltgrey = 12, white = 15, amberD = 36, amberL = 44, redD = 50, redL = 60, greenD = 84, greenL = 92, brownD = 96, brownM = 97, stain = 98, pink = 99, bg = 100, bgDk = 101, } local function pal_setup() -- Components 0..63 (the native 18-bit master palette). gfx.pal(96, 20, 12, 6) -- dark brown (legs) gfx.pal(97, 36, 22, 10) -- mid brown gfx.pal(98, 28, 6, 4) -- splat gfx.pal(99, 60, 30, 32) -- pink gfx.pal(100, 58, 50, 26) -- floor sandy yellow gfx.pal(101, 48, 38, 18) -- floor darker end -- ---------------- sprite art ---------------------------------------------- local CHARS = { ["."] = 0, [" "] = 0, ["#"] = 1, ["D"] = 4, ["M"] = 8, ["L"] = 12, ["W"] = 15, ["y"] = 36, ["Y"] = 44, ["r"] = 50, ["R"] = 60, ["g"] = 84, ["G"] = 92, ["b"] = 96, ["B"] = 97, ["T"] = 98, ["p"] = 99, } local SHEET_W, SHEET_H, TILE = 96, 16, 16 local SWATTER = { "......MMMMM.....", ".....MWWWWWM....", "....MWWWWWWWM...", "....MWWWWWWWM...", "....MWWWWWWWM...", ".....MWWWWWM....", "......MMMMM.....", "........M.......", ".........M......", "..........M.....", "...........M....", "............M...", ".............M..", "..............M.", "...............M", "................", } local FLY = { "................", "................", ".....DD.........", "....DMMD........", "...LDMMDL.......", "..LLLMMLLL......", "..LLWWMWWLL.....", "...LMM.MML......", "....DMMD........", "....DMMD........", "....DMMD........", "....D##D........", "...D....D.......", "..D......D......", "................", "................", } local ROACH = { "................", "......RRR.......", ".....RRRRR......", "....RRRRRRR.....", "....RRDDRRR.....", "....RRDDRRR.....", "...rRRRRRRr.....", "...rRRRRRRr.....", "...rRRRRRRr.....", "...rRRRRRRr.....", "...rRRRRRRr.....", ".r.rRRRRRRr.r...", "..rrRRRRRRrr....", "....rrrrrr......", ".....r..r.......", "................", } local BEETLE = { "................", ".....YYYY.......", "....YYYYYY......", "...bYYYYYYb.....", "..bYYYYYYYYb....", ".bYYYWWYYYYb....", ".byyyyyyyyyb....", ".byYYYYYYYyb....", ".byYYYYYYYyb....", ".byYYYYYYYyb....", ".bbbbbbbbbbb....", "b...b.bb.b...b..", "....bb..bb......", "....bb..bb......", "................", "................", } local SPIDER = { "................", ".D.D.D....D.D.D.", "..DDD......DDD..", "...DD.MMMM.DD...", "....DDDDDDDDD...", "...DDDDDDDDDD...", "...DDRR..RRDD...", "...DDDDDDDDDD...", "....DDDDDDDD....", "....DDDDDDDD....", "....DDDDDDDD....", "....DDDDDDDD....", "....DD....DD....", "...D........D...", "..D..........D..", "................", } local STAIN = { "................", "................", "...TT....TT.....", "....TTTTTTT.....", "...TTTTTTTTT....", "..TTTTTTTTTTT...", ".TTTTTpppTTTT...", "..TTTTpppTTTT...", "...TTTpppTTTT...", "....TTTTTTTT....", ".....TTTTTT.....", "......TTT.......", ".......T........", "................", "................", "................", } local function build_sheet() local sheet = {} for i = 1, SHEET_W * SHEET_H do sheet[i] = 0 end local function paint(col, art) for y = 1, TILE do local row = art[y] for x = 1, TILE do local ch = row:sub(x, x) local c = CHARS[ch] or 0 sheet[(y - 1) * SHEET_W + (col * TILE + x - 1) + 1] = c end end end paint(0, SWATTER) paint(1, FLY) paint(2, ROACH) paint(3, BEETLE) paint(4, SPIDER) paint(5, STAIN) local hex = {} for i = 1, #sheet do hex[i] = string.format("%02x", sheet[i]) end gfx.spr_sheet(1, SHEET_W, SHEET_H, TILE, TILE, table.concat(hex)) end -- ---------------- bug types ----------------------------------------------- -- col is the sheet column. bb is hit-box in sprite-local pixel coords. local TYPES = { fly = { col = 1, hp = 1, points = 5, speed = 1.4, bb = { 2, 2, 13, 13 } }, roach = { col = 2, hp = 1, points = 10, speed = 1.0, bb = { 1, 1, 14, 14 } }, beetle = { col = 3, hp = 2, points = 25, speed = 0.55, bb = { 1, 1, 14, 14 } }, spider = { col = 4, hp = 1, points = 15, speed = 1.2, bb = { 1, 1, 14, 14 } }, } local function spawn_bug() local r = math.random() local kind = "fly" if r < 0.50 then kind = "fly" elseif r < 0.78 then kind = "roach" elseif r < 0.92 then kind = "spider" else kind = "beetle" end local edge = math.random(1, 4) local x, y, vx, vy local sp = TYPES[kind].speed * rnd(0.8, 1.3) if edge == 1 then x = rnd(0, W - 16); y = -16 vx = rnd(-1, 1); vy = rnd(0.5, 1) * sp elseif edge == 2 then x = W; y = rnd(16, H - 16) vx = -rnd(0.5, 1) * sp; vy = rnd(-1, 1) elseif edge == 3 then x = rnd(0, W - 16); y = H vx = rnd(-1, 1); vy = -rnd(0.5, 1) * sp else x = -16; y = rnd(16, H - 16) vx = rnd(0.5, 1) * sp; vy = rnd(-1, 1) end bugs[#bugs + 1] = { kind = kind, x = x, y = y, vx = vx, vy = vy, hp = TYPES[kind].hp, age = 0, stunT = 0, flutter = rnd(0, math.pi * 2), } end local function step_bug(b) b.age = b.age + 1 b.flutter = b.flutter + 0.2 if b.stunT > 0 then b.stunT = b.stunT - 1; return true end if b.age % 30 == 0 then b.vx = b.vx + rnd(-0.4, 0.4) b.vy = b.vy + rnd(-0.4, 0.4) end local sp = TYPES[b.kind].speed local m = math.sqrt(b.vx * b.vx + b.vy * b.vy) if m > sp * 1.6 then b.vx = b.vx * (sp * 1.4) / m b.vy = b.vy * (sp * 1.4) / m end b.x = b.x + b.vx b.y = b.y + b.vy if b.x < -32 or b.x > W + 16 or b.y < -32 or b.y > H + 16 then return false end return true end local function bug_aabb_world(b) local bb = TYPES[b.kind].bb return b.x + bb[1], b.y + bb[2], b.x + bb[3], b.y + bb[4] end -- ---------------- effects ------------------------------------------------- local function add_stain(x, y) stains[#stains + 1] = { x = x, y = y, t = 240 } if #stains > 28 then table.remove(stains, 1) end end local function add_pop(x, y, n) pops[#pops + 1] = { x = x, y = y, n = n, t = 60 } end local function add_particles(x, y) for i = 1, 8 do particles[#particles + 1] = { x = x, y = y, vx = rnd(-2, 2), vy = rnd(-2.5, 0), t = 22 + math.random(0, 10), c = (i % 2 == 0) and PAL.stain or PAL.pink, } end end local function step_particles() for i = #particles, 1, -1 do local p = particles[i] p.x = p.x + p.vx p.y = p.y + p.vy p.vy = p.vy + 0.18 p.t = p.t - 1 if p.t <= 0 then table.remove(particles, i) end end end local function step_stains() for i = #stains, 1, -1 do stains[i].t = stains[i].t - 1 if stains[i].t <= 0 then table.remove(stains, i) end end end -- ---------------- swatting ------------------------------------------------ local SWAT_R = 11 local function swat_at(mx, my) swatT = 6 snd.fm(2, 240, 0.05, 1.0, 0.5, 0.18) for i = #bugs, 1, -1 do local b = bugs[i] local x0, y0, x1, y1 = bug_aabb_world(b) local cx = math.max(x0, math.min(mx, x1)) local cy = math.max(y0, math.min(my, y1)) local dx, dy = mx - cx, my - cy if dx * dx + dy * dy <= SWAT_R * SWAT_R then b.hp = b.hp - 1 if b.hp <= 0 then local pts = TYPES[b.kind].points score = score + pts add_stain(b.x, b.y) add_pop(b.x + 8, b.y, pts) add_particles(b.x + 8, b.y + 8) snd.noise(3, 0.10, 0.32, 1400) snd.fm(0, 660 + math.random(0, 240), 0.06, 2.0, 3.0, 0.20) table.remove(bugs, i) else b.stunT = 30 b.vx = b.vx * 0.4 b.vy = b.vy * 0.4 snd.fm(0, 200, 0.06, 1.0, 1.0, 0.20) end end end end -- ---------------- title --------------------------------------------------- local function spawn_title_bug() local kinds = { "fly", "roach", "spider", "fly", "roach" } titleBugs[#titleBugs + 1] = { kind = kinds[math.random(1, #kinds)], x = rnd(-16, W), y = rnd(40, H - 32), vx = rnd(-0.6, 0.6), vy = rnd(-0.4, 0.4), flutter = rnd(0, math.pi * 2), } end -- ---------------- drawing ------------------------------------------------- local function draw_floor() for ty = 0, math.ceil(H / 16) do for tx = 0, math.ceil(W / 16) do local c = ((tx + ty) % 2 == 0) and PAL.bg or PAL.bgDk gfx.rect(tx * 16, ty * 16, 16, 16, c) end end for y = 0, H, 16 do gfx.rect(0, y, W, 1, 8) end for x = 0, W, 16 do gfx.rect(x, 0, 1, H, 8) end end local function draw_stains() for _, s in ipairs(stains) do if s.t > 60 or (sys.frame() % 4 < 2) then gfx.spr(1, 5, 0, math.floor(s.x), math.floor(s.y)) end end end local function draw_bugs(list) for _, b in ipairs(list) do local col = TYPES[b.kind].col local oy = 0 if b.kind == "fly" then oy = math.floor(math.sin(b.flutter) * 1) end if b.stunT and b.stunT > 0 and (sys.frame() % 4 < 2) then -- Stun flicker — draw at slight offset. gfx.spr(1, col, 0, math.floor(b.x) + 1, math.floor(b.y + oy)) else gfx.spr(1, col, 0, math.floor(b.x), math.floor(b.y + oy)) end end end local function draw_particles() for _, p in ipairs(particles) do gfx.rect(math.floor(p.x), math.floor(p.y), 2, 2, p.c) end end local function draw_pops() for i = #pops, 1, -1 do local p = pops[i] local dy = math.floor((60 - p.t) / 3) local s = "+" .. p.n gfx.text(s, math.floor(p.x - #s * 4), math.floor(p.y - dy), 15) p.t = p.t - 1 if p.t <= 0 then table.remove(pops, i) end end end local function draw_cursor(mx, my) if mx < 0 or my < 0 then return end local sx = mx - 7 local sy = my - 3 if swatT > 0 then -- Twelve-point flash ring centred on the head. for i = 0, 11 do local ang = i * math.pi / 6 gfx.pset(math.floor(mx + math.cos(ang) * 9), math.floor(my + math.sin(ang) * 9), 15) end sy = sy + 1 swatT = swatT - 1 end gfx.spr(1, 0, 0, sx, sy) end local function draw_hud() gfx.rect(0, 0, W, 16, 1) gfx.rect(0, 15, W, 1, 44) gfx.text(string.format("SCORE %05d", score), 6, 4, 15) local cx = W / 2 - 40 gfx.text("TIME", cx, 4, 15) local barX, barY = cx + 36, 6 local barW = 80 gfx.rect(barX, barY, barW, 4, 8) local fill = math.floor(barW * math.max(0, clock) / roundLength) gfx.rect(barX, barY, fill, 4, clock > 10 and 92 or 60) gfx.text(string.format("HI %05d", high), W - 80, 4, 12) end -- ---------------- per-state update / draw --------------------------------- local function tick_title() if #titleBugs < 6 and chance(0.04) then spawn_title_bug() end for i = #titleBugs, 1, -1 do local b = titleBugs[i] b.x = b.x + b.vx b.y = b.y + b.vy b.flutter = b.flutter + 0.2 if b.x < -20 or b.x > W + 4 or b.y < -20 or b.y > H + 4 then table.remove(titleBugs, i) end end if inp.mouse_btnp("left") or inp.btnp("a") then state = "play" clock = roundLength score = 0 bugs, stains, pops, particles = {}, {}, {}, {} spawnTimer = 0 fadeT = 18 end end local function draw_title() draw_floor() draw_bugs(titleBugs) local plateX, plateY, plateW, plateH = 50, 60, W - 100, 80 gfx.rect(plateX, plateY, plateW, plateH, 1) gfx.rect(plateX, plateY, plateW, 2, 60) gfx.rect(plateX, plateY + plateH - 2, plateW, 2, 60) gfx.rect(plateX, plateY, 2, plateH, 60) gfx.rect(plateX + plateW - 2, plateY, 2, plateH, 60) local function centred(s, y, c) gfx.text(s, math.floor(W / 2 - (#s * 8) / 2), y, c) end centred("SWATTER", plateY + 10, 15) centred("DAMOCLES PEST CONTROL SIM.", plateY + 28, 92) centred("(C) 1995 DAMOCLES INTERACTIVE", plateY + 44, 12) centred("REQUIRES MAESTRO POINTER", plateY + 60, 44) if (sys.frame() // 30) % 2 == 0 then centred("CLICK OR PRESS A TO BEGIN", H - 40, 15) end if high > 0 then centred(string.format("HIGH SCORE %05d", high), H - 22, 92) end draw_cursor(inp.mouse_x(), inp.mouse_y()) end local function tick_play() clock = clock - 1 / 60 if clock <= 0 then state = "over" overT = 0 if score > high then high = score sys.save_set("high", high) end snd.fm(0, 110, 0.6, 1.0, 1.0, 0.30) snd.fm(1, 138, 0.6, 1.0, 1.0, 0.26) return end spawnTimer = spawnTimer - 1 local elapsed = roundLength - clock local interval = math.max(15, 50 - math.floor(elapsed * 0.5)) if spawnTimer <= 0 then spawn_bug() spawnTimer = interval + math.random(-5, 5) end for i = #bugs, 1, -1 do if not step_bug(bugs[i]) then table.remove(bugs, i) end end step_particles() step_stains() if inp.mouse_btnp("left") then swat_at(inp.mouse_x(), inp.mouse_y()) end if clock <= 10 and (sys.frame() % 60 == 0) then snd.fm(1, 880, 0.05, 1.0, 0.5, 0.18) end end local function draw_play() draw_floor() draw_stains() draw_bugs(bugs) draw_particles() draw_pops() draw_hud() draw_cursor(inp.mouse_x(), inp.mouse_y()) end local function tick_over() overT = overT + 1 if overT > 30 and (inp.mouse_btnp("left") or inp.btnp("a")) then state = "title" titleBugs = {} end end local function draw_over() draw_floor() draw_stains() local f = math.min(0.55, overT / 30 * 0.55) gfx.fade(f) local pw, ph = 220, 100 local px, py = math.floor(W / 2 - pw / 2), math.floor(H / 2 - ph / 2) gfx.rect(px, py, pw, ph, 1) gfx.rect(px, py, pw, 2, 60) gfx.rect(px, py + ph - 2, pw, 2, 60) local function centred(s, y, c) gfx.text(s, math.floor(W / 2 - (#s * 8) / 2), y, c) end centred("TIME UP", py + 10, 15) centred(string.format("SCORE %05d", score), py + 32, 44) centred(string.format("HIGH %05d", high), py + 46, 12) if score == high and score > 0 then centred("NEW HIGH SCORE", py + 60, 92) end if overT > 30 and (sys.frame() // 30) % 2 == 0 then centred("CLICK OR A TO REPLAY", py + 80, 15) end end -- ---------------- entry points -------------------------------------------- function _init() pal_setup() build_sheet() high = tonumber(sys.save_get("high", 0)) or 0 state = "title" end function _update() -- Reset transient screen registers each frame. gfx.fade(0) gfx.mosaic(0) if state == "title" then tick_title() elseif state == "play" then tick_play() elseif state == "over" then tick_over() end if fadeT > 0 then fadeT = fadeT - 1 end end function _draw() gfx.cls(PAL.bg) if state == "title" then draw_title() elseif state == "play" then draw_play() elseif state == "over" then draw_over() end if fadeT > 0 then gfx.fade(fadeT / 18) end end