romdevtools 0.85.0 → 0.87.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/AGENTS.md +1 -1
- package/CHANGELOG.md +22 -0
- package/README.md +5 -2
- package/examples/README.md +1 -0
- package/examples/pico8/templates/platformer.p8 +168 -0
- package/examples/pico8/templates/puzzle.p8 +240 -0
- package/examples/pico8/templates/racing.p8 +122 -0
- package/examples/pico8/templates/shmup.p8 +165 -0
- package/examples/pico8/templates/sports.p8 +140 -0
- package/package.json +7 -4
- package/src/cheats/gamegenie.js +101 -0
- package/src/cores/capabilities.js +28 -1
- package/src/cores/registry.js +5 -0
- package/src/host/LibretroHost.js +44 -1
- package/src/mcp/tools/cheats.js +4 -0
- package/src/mcp/tools/disasm.js +56 -2
- package/src/mcp/tools/project.js +27 -0
- package/src/toolchains/index.js +39 -1
- package/src/toolchains/pico8/pack.js +67 -0
package/AGENTS.md
CHANGED
|
@@ -4,7 +4,7 @@ This is romdev's GENERIC orientation — read it once. The platform-specific det
|
|
|
4
4
|
|
|
5
5
|
## What this server does
|
|
6
6
|
|
|
7
|
-
Drives the full homebrew ROM dev loop for 18 retro game platforms (NES, SNES, Game Boy, Game Boy Color, Game Boy Advance, Genesis, Sega Master System, Game Gear, Atari 2600/7800, Atari Lynx, Commodore 64, PC Engine / TurboGrafx-16, and MSX / MSX2, plus the open-hardware GameTank — and the 3D consoles Nintendo 64, Sony PlayStation, and Sega Dreamcast). Build → run → screenshot → inspect → patch → iterate. Also a strong reverse-engineering kit: disassemble existing ROMs into byte-exact rebuildable projects (`disasm({target:'project'})`/`disasm({target:'references'})` — the workhorse for any structural hack), find a value's address with the Cheat-Engine search loop (`memory({op:'search'})`/`memory({op:'searchNext'})`), find the EXACT instruction that wrote a RAM byte (`breakpoint({on:'write'})`, a core-level write watchpoint), confirm a patch is live in the running image (`memory({op:'readCart'})`), tell whether a "found table" is really ASCII (`memory({op:'classify'})`), trace where an on-screen graphic comes from (`watch({on:'copy'})` on the 15 classic platforms — writer PC per VRAM write; `watch({on:'dma'})` for Genesis DMA sources), drive menus by screen-change (`navigate`), and look up cheats (`cheats({op:'lookup'})`/`cheats({op:'search'})`: a free, crowd-sourced labeled RAM/code map for known ROMs), apply + create cheats, convert assets, study patterns from real games. **Doing a romhack? Start with `platform({op:'doc', platform:'romhacking', name:'playbook'})`** — the decision tree that wires all of the above together. Bundled WASM toolchains and emulator cores — no system dependencies, no installs.
|
|
7
|
+
Drives the full homebrew ROM dev loop for 18 retro game platforms (NES, SNES, Game Boy, Game Boy Color, Game Boy Advance, Genesis, Sega Master System, Game Gear, Atari 2600/7800, Atari Lynx, Commodore 64, PC Engine / TurboGrafx-16, and MSX / MSX2, plus the open-hardware GameTank — and the 3D consoles Nintendo 64, Sony PlayStation, and Sega Dreamcast) — plus the **PICO-8** fantasy console (via FAKE-08: a Lua VM, so its own tier — `build` PACKAGES a `.p8` from Lua, `disasm({target:'source'})` reads the cart's Lua instead of machine code, `memory({region:'system_ram'})` reads its 64 KB; cpuState/decompile/tile-inspectors are N/A). Build → run → screenshot → inspect → patch → iterate. Also a strong reverse-engineering kit: disassemble existing ROMs into byte-exact rebuildable projects (`disasm({target:'project'})`/`disasm({target:'references'})` — the workhorse for any structural hack), find a value's address with the Cheat-Engine search loop (`memory({op:'search'})`/`memory({op:'searchNext'})`), find the EXACT instruction that wrote a RAM byte (`breakpoint({on:'write'})`, a core-level write watchpoint), confirm a patch is live in the running image (`memory({op:'readCart'})`), tell whether a "found table" is really ASCII (`memory({op:'classify'})`), trace where an on-screen graphic comes from (`watch({on:'copy'})` on the 15 classic platforms — writer PC per VRAM write; `watch({on:'dma'})` for Genesis DMA sources), drive menus by screen-change (`navigate`), and look up cheats (`cheats({op:'lookup'})`/`cheats({op:'search'})`: a free, crowd-sourced labeled RAM/code map for known ROMs), apply + create cheats, convert assets, study patterns from real games. **Doing a romhack? Start with `platform({op:'doc', platform:'romhacking', name:'playbook'})`** — the decision tree that wires all of the above together. Bundled WASM toolchains and emulator cores — no system dependencies, no installs.
|
|
8
8
|
|
|
9
9
|
You drive the work. The human is a director — they may want a game, a ROM disassembly, a tool-assisted reverse-engineering session, or anything else this server can do.
|
|
10
10
|
|
package/CHANGELOG.md
CHANGED
|
@@ -4,6 +4,28 @@ All notable changes to `romdevtools`. Dates are release dates.
|
|
|
4
4
|
(Published as `romdev-mcp` through 0.11.0; renamed to `romdevtools` in 0.13.0 —
|
|
5
5
|
the `romdev-mcp` bin is kept as an alias.)
|
|
6
6
|
|
|
7
|
+
## 0.86.0 — 2026-07-01
|
|
8
|
+
|
|
9
|
+
- **GameTank Game Genie — a brand-new cheat-code format.** Nobody had made GameTank cheat codes
|
|
10
|
+
before; this adds a Game-Genie-style read-substitution device to the emulated GameTank core plus
|
|
11
|
+
a code format that is HARDWARE-COMPATIBLE (the same codes would work on a physical Game Genie
|
|
12
|
+
built for the console's open cart bus).
|
|
13
|
+
- Core (`romdev-core-gametank@0.2.0`): a value-override cheat device in the shared debug lib
|
|
14
|
+
(`romdev_cheat_set`/`romdev_cheat_read`/`romdev_cheat_get`, 24 slots). `MemoryRead` returns the
|
|
15
|
+
substitute byte on an address match, with optional compare-against-original (survives bank
|
|
16
|
+
switching) — exactly what a hardware Game Genie does. Any core that adds the one-line
|
|
17
|
+
`romdev_cheat_read` call to its bus read gets this for free.
|
|
18
|
+
- Format: `encodeGameTankGameGenie`/`decodeGameTankGameGenie` — a distinct 16-letter wheel
|
|
19
|
+
(`KLMNPQRSTVWXYZ23`), 16-bit address + 8-bit value (+ optional compare), scrambled with a
|
|
20
|
+
checksum. Plain codes are `XXX-XXXX`, compare codes `XXXX-XXXXX` (e.g. `$8100→0x42` = KTM-LPK3).
|
|
21
|
+
Wired into every device dispatcher; `cheats({op:'make', platform:'gametank', …})` generates
|
|
22
|
+
codes and `cheats({op:'apply'})` applies them live. GameTank's `retro_cheat_set` is a stub, so
|
|
23
|
+
`LibretroHost.setCheat` routes GameTank cheats through the romdev value-override device.
|
|
24
|
+
- **jsgame test no longer skips.** It ran only under `--experimental-vm-modules`, so the default
|
|
25
|
+
suite skipped it. It now runs in an isolated forked child process (SDL is main-thread-only;
|
|
26
|
+
rungame leaks handles) driven over rawr JSON-RPC — the child carries the flag and is killed when
|
|
27
|
+
the RPC resolves, so the main suite stays clean. Suite: 1099 tests, 1099 pass, 0 skipped.
|
|
28
|
+
|
|
7
29
|
## 0.85.0 — 2026-07-01
|
|
8
30
|
|
|
9
31
|
- **Native-runtime game kinds — wasmcart + jsgame are now first-class hosts.** romdev is a
|
package/README.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# romdev
|
|
2
2
|
|
|
3
|
-
The entry point for **romdev** — vibe-code real retro games. Build, run, inspect, and reverse-engineer actual homebrew ROMs (NES, SNES, Game Boy, Genesis, Atari, C64, GBA, PC Engine, MSX, GameTank — and the 3D consoles N64, PlayStation, and Dreamcast) with one command — drive it yourself or let a coding assistant do it.
|
|
3
|
+
The entry point for **romdev** — vibe-code real retro games. Build, run, inspect, and reverse-engineer actual homebrew ROMs (NES, SNES, Game Boy, Genesis, Atari, C64, GBA, PC Engine, MSX, GameTank — and the 3D consoles N64, PlayStation, and Dreamcast), plus the **PICO-8** fantasy console (via FAKE-08), with one command — drive it yourself or let a coding assistant do it.
|
|
4
4
|
|
|
5
5
|
```bash
|
|
6
6
|
npx romdevtools
|
|
@@ -13,6 +13,8 @@ npx romdevtools
|
|
|
13
13
|
- **Inspect + romhack** — read CPU/video/save RAM, watch memory, write-breakpoints, the Cheat-Engine value-search loop, a bundled cheat database, mapper-aware disassembly, and a byte-exact rebuildable-project disassembler.
|
|
14
14
|
- **Reverse-engineering analysis engine (all 18 platforms — incl. the 3D consoles' MIPS R3000/R4300 + SH-4)** — control-flow graphs, deep cross-references, auto-detected functions (ranked real-code-first), a one-shot structural map, and a Ghidra **decompiler** (C-like pseudocode, with hardware registers named and 6502 SLEIGH clutter folded to readable C): `disasm({target:'cfg'|'xrefs'|'functions'|'decompile'})` and `symbols({op:'analyze'})`. And the piece no static tool has: **live computed-jumptable recovery** — `breakpoint({on:'jumptable'})` runs the emulator to resolve the `JMP (table,X)` / RTS-trick dispatchers (state machines, script/battle VMs) that static analysis collapses to "could not recover." Understand *how* a routine works before you touch it — no $3,000 IDA license, no install.
|
|
15
15
|
- **Convert assets** — PNG → platform tiles/tilemaps, quantize-to-palette, audio importers (BRR for SNES, XGM2 PCM for Genesis).
|
|
16
|
+
- **Native game runtimes (beyond emulation)** — the same run/see/drive loop also hosts two *native* game formats: **wasmcart** (`.wasc` — WASM games from any language; 2026) and **jsgame** (`.jsgame` — JavaScript canvas/WebGL games; 2024). `loadMedia({platform:'wasmcart'|'jsgame'})` → `frame({op:'step'|'screenshot'})` → `input`, over the same tools — plus V8/WASM introspection (the cart's live WASM heap + exports; the JS game's globals) an emulator can't offer, and `pack({target:'wasc'|'jsgame'})` to zip a source dir into the distributable archive (the "build" step; romdev doesn't compile the WASM — bring your own). A capability descriptor marks the emulator-only tools (memory regions / cpuState / disasm) *not-applicable* for these kinds. Depends on the [`wasmcart`](https://www.npmjs.com/package/wasmcart) + [`rungame`](https://www.npmjs.com/package/rungame) packages (jsgame runs in a `vm` realm — the server self-re-execs with `--experimental-vm-modules`).
|
|
17
|
+
- **PICO-8 (fantasy console)** — the [FAKE-08](https://github.com/jtothebell/fake-08) player (MIT, no BIOS) runs PICO-8 `.p8` (Lua source) and `.p8.png` (cart-in-a-label-PNG) carts at 128×128 with sound. `loadMedia({platform:'pico8'})` → `frame`/`input` work like any core; `build({platform:'pico8', source: lua})` PACKAGES a runnable `.p8` from Lua (+ optional gfx/sfx/map sections) — it's a cart assembler, not a CPU compiler (the Lua IS the code). PICO-8 is a Lua VM, so instead of machine-code disasm/decompile you read the cart's Lua directly with `disasm({target:'source'})`; `memory({region:'system_ram'})` exposes the full 64KB PICO-8 address space (sprite sheet, map, sfx, general RAM, screen buffer). Its capability descriptor is a `fantasy` tier — cpuState/decompile/tile-inspectors report *not-applicable* (there's no CPU or tile hardware to inspect). Ships the [`romdev-core-fake08`](https://www.npmjs.com/package/romdev-core-fake08) core package.
|
|
16
18
|
|
|
17
19
|
Point any coding agent at it three ways:
|
|
18
20
|
|
|
@@ -36,13 +38,14 @@ This package contains all the JavaScript — the tool surface, the WASM emulator
|
|
|
36
38
|
|
|
37
39
|
`romdevtools` depends on the binary/data packages it needs (exact-pinned), so a single install gets a matched, tested set:
|
|
38
40
|
|
|
39
|
-
- 2D cores: `romdev-core-{fceumm,gambatte,gpgx,vice,handy,prosystem,geargrafx,bluemsx,gametank}`
|
|
41
|
+
- 2D cores: `romdev-core-{fceumm,gambatte,gpgx,vice,handy,prosystem,geargrafx,bluemsx,gametank,fake08}`
|
|
40
42
|
- 3D / GPU cores (rendered through `native-gles`): `romdev-core-{parallel-n64,beetle-psx-hw,flycast}`
|
|
41
43
|
- Platforms (core + dedicated toolchain bundled): `romdev-platform-{snes,gba,atari2600}`
|
|
42
44
|
- Toolchains: `romdev-toolchain-{cc65,sdcc,m68k-gcc,vasm,rgbds,mips-gcc,sh-gcc}` (mips-gcc = N64/PS1 C; sh-gcc = Dreamcast C)
|
|
43
45
|
- GPU deps (OPTIONAL — only the 3D cores need them; the 2D cores never touch GL): `native-gles` + `webgl-node`. A headless user without a GPU stack can still run all the 2D platforms.
|
|
44
46
|
- Analysis: `romdev-analysis` (Rizin → WASM: control-flow graphs, cross-references, function detection) and `romdev-analysis-decompiler` (Ghidra's C++ decompiler → WASM + SLEIGH processor specs for all 18 CPUs, incl. MIPS R3000/R4300 + SH-4). Power `disasm({target:'cfg'|'xrefs'|'functions'|'decompile'})` and `symbols({op:'analyze'})`. Lazy-loaded on first use.
|
|
45
47
|
- Data: `romdev_game_codes` — the bundled game-code / cheat database (a free labeled RAM/code map for thousands of known ROMs), split out so it can grow independently. Lazy-loaded one platform at a time.
|
|
48
|
+
- Native runtimes: [`wasmcart`](https://www.npmjs.com/package/wasmcart) (runs `.wasc` WASM game carts) and [`rungame`](https://www.npmjs.com/package/rungame) (runs `.jsgame` JavaScript games headlessly). Also `romdev-audio-resampler` (WASM+SIMD S16-stereo resampler, used by the live-window audio sink).
|
|
46
49
|
|
|
47
50
|
`@kmamal/sdl` is used only by `playtest()` / `romdevtools-cli play` (the live window). It ships its native binary via its own install script, which npm skips when romdev is a transitive dep (e.g. under `npx`) — so romdev's `postinstall` fetches it, and `playtest()` also self-heals at runtime if the binary is still missing (downloading the prebuilt before the first window open). Either way, if the binary can't be fetched (offline/locked-down network), the headless server is unaffected — only the live window degrades, and the error tells you the one command to fix it.
|
|
48
51
|
|
package/examples/README.md
CHANGED
|
@@ -27,6 +27,7 @@ Each example fits the convention:
|
|
|
27
27
|
| pce | `pce/<template>/main.c` | cc65 (HuC6280) | HuCard homebrew, no BIOS. Ships a direct-register VDC/PSG helper lib (`pce.h` + `pce.lib`) — cc65 has no PCE sprite/sound library. Examples: `sprite_move`, `catch_game`, `music_sfx`, plus the 5 genre games (shmup/platformer/puzzle/sports/racing). **`#include <stdint.h>`** for int8/16/32_t — `pce.h` only typedefs u8/u16. The genre games fill the BAT (32×32 virtual screen); the platformer smooth-scrolls via the VDC BXR register. |
|
|
28
28
|
| msx | `msx/<template>/main.c` | sdcc (z80) | Boots cartridge homebrew on the open C-BIOS (no proprietary ROM). Ships an AY-3-8910 + TMS9918/V9938 VDP helper lib (`msx_hw.h` + `msx_vdp.c`). Examples: `sprite_move`, `catch_game`, `music_sfx`, plus the 5 genre games. The bundled `msx_crt0.s` (applied by the dir-build recipe automatically) emits the `"AB"` cartridge header at $4000 + INIT pointer — **C-BIOS shows its logo for ~2-3 s, then CALLs INIT**, so run ≥240 frames before screenshotting. The platformer column-streams the SCREEN 2 name table for a tile-by-tile scroll. |
|
|
29
29
|
| gametank | `gametank/templates/*.c` | cc65 (W65C02S) | Build with `linkerConfig:"sdk"` + the bundled SDK runtime (`src/platforms/gametank/lib/gt/*`). FIVE complete genre games, fully reworked and play-tested on real hardware (RetroDeck): `shmup`, `platformer`, `puzzle` (CHROMA WELL — a Columns-style 3-jewel matcher), `sports` (2-player paddle), `racing`. The GameTank is a **framebuffer + blitter** machine — NO tilemap/nametable: every frame is redrawn from scratch via the SDK **draw QUEUE** (`gt_draw.h`), the only correct way to drive the blitter (synchronous blits corrupt the FB + tank the framerate). Shared helper headers the games fork: `gt_draw.h` (queue wrappers + `rnd8`), `gt_palette.h` (VERIFIED color indices), `gt_sprites.h` (GRAM sprite load/blit), `gt_hud.h` (box-font digits + 3×5 A–Z text), `gt_sound.h` (one-shot ACP SFX). **HARD-WON GOTCHAS (all documented in `gt_draw.h`): (1) a box of width OR height 128 is silently DROPPED — clamp to 127; (2) box top-edge scanlines flicker between the double-buffer pages — finish a frame that draws near the top with `queue_clear_border(topColor)`; (3) use `rnd8()`, NOT the SDK `rnd()`, which corrupts state on this single-bank build.** Per-frame GRAM **sprite** blits are the expensive part — too many overrun vblank and the queue drops draws (flicker), so keep sprites few (e.g. the platformer blits only the hero; coins are rects). |
|
|
30
|
+
| pico8 | `pico8/templates/*.p8` | Lua (FAKE-08) | FIVE complete genre games, each a full `.p8` cart (no compiler/crt0 — the Lua IS the code): `shmup` (STAR SWEEPER), `platformer` (HOP QUEST), `puzzle` (COLOR DROP), `sports` (RALLY VOLLEY, 1P-vs-CPU or 2P), `racing` (LANE RUNNER). Each carries hand-authored `__gfx__` pixel-art sprites (drawn with `spr()`), looping `__music__` + `__sfx__` banks, title/play/game-over states, and the genre's core mechanics. **`build({platform:'pico8', source: <the .p8 text>})` just PACKAGES the cart** (a `.p8` = header + `__lua__` + optional `__gfx__`/`__gff__`/`__map__`/`__sfx__`/`__music__` sections). PICO-8 is a Lua VM (128×128, 16-color, 6 buttons) — read/edit the Lua directly, no disassembly needed. |
|
|
30
31
|
|
|
31
32
|
## Guides
|
|
32
33
|
|
|
@@ -0,0 +1,168 @@
|
|
|
1
|
+
pico-8 cartridge // http://www.pico-8.com
|
|
2
|
+
version 18
|
|
3
|
+
__lua__
|
|
4
|
+
-- hop quest — a platformer scaffold
|
|
5
|
+
-- genre example for romdev/pico8. real hero sprite w/ walk + jump frames
|
|
6
|
+
-- (see __gfx__), looping music, sfx, gravity + solid-box collision,
|
|
7
|
+
-- animated spinning coins, a goal flag, parallax hills. fork it.
|
|
8
|
+
|
|
9
|
+
-- sprites: 1 idle,2 walk,3 jump · 5..8 coin spin frames · 10 flag
|
|
10
|
+
|
|
11
|
+
function _init()
|
|
12
|
+
best=99
|
|
13
|
+
state="title"
|
|
14
|
+
boot=0
|
|
15
|
+
build_level()
|
|
16
|
+
reset_player()
|
|
17
|
+
music(0)
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
function build_level()
|
|
21
|
+
solids={
|
|
22
|
+
{0,120,128,8},{24,100,24,6},{60,84,30,6},
|
|
23
|
+
{98,64,24,6},{6,58,18,6},{40,42,28,6},
|
|
24
|
+
}
|
|
25
|
+
coins={}
|
|
26
|
+
for c in all({{34,90},{72,74},{108,54},{14,48},{50,32}}) do
|
|
27
|
+
add(coins,{x=c[1],y=c[2],f=rnd(4)})
|
|
28
|
+
end
|
|
29
|
+
goal={x=52,y=24}
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
function reset_player()
|
|
33
|
+
p={x=6,y=104,dx=0,dy=0,grounded=false,face=1,anim=0}
|
|
34
|
+
collected=0 total=#coins t=0
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
function solid_at(x,y)
|
|
38
|
+
for s in all(solids) do
|
|
39
|
+
if x>=s[1] and x<s[1]+s[3] and y>=s[2] and y<s[2]+s[4] then return true end
|
|
40
|
+
end
|
|
41
|
+
return false
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
-- the top Y of a platform the feet CROSS this step: feet were at/above the top (oldy)
|
|
45
|
+
-- and reach at/below it (newy). returns the highest such top, or nil. this catch-the-
|
|
46
|
+
-- crossing test is what stops fast falls from tunneling straight through a platform.
|
|
47
|
+
function ground_top(x,oldy,newy)
|
|
48
|
+
local best=nil
|
|
49
|
+
for s in all(solids) do
|
|
50
|
+
local top=s[2]
|
|
51
|
+
if x>=s[1] and x<s[1]+s[3] and oldy<=top+1 and newy>=top then
|
|
52
|
+
if best==nil or top<best then best=top end
|
|
53
|
+
end
|
|
54
|
+
end
|
|
55
|
+
return best
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
function _update()
|
|
59
|
+
t+=1
|
|
60
|
+
for c in all(coins) do c.f+=.2 end
|
|
61
|
+
if state=="title" then
|
|
62
|
+
boot+=1
|
|
63
|
+
if boot>10 and (btnp(4) or btnp(5)) then state="play" build_level() reset_player() music(-1) end
|
|
64
|
+
return
|
|
65
|
+
end
|
|
66
|
+
if state=="win" then
|
|
67
|
+
if btnp(4) or btnp(5) then state="title" end
|
|
68
|
+
return
|
|
69
|
+
end
|
|
70
|
+
|
|
71
|
+
local ax=0
|
|
72
|
+
if btn(0) then ax=-1 p.face=-1 end
|
|
73
|
+
if btn(1) then ax=1 p.face=1 end
|
|
74
|
+
p.dx=ax*1.6
|
|
75
|
+
if ax~=0 then p.anim+=.25 else p.anim=0 end
|
|
76
|
+
|
|
77
|
+
if (btn(4) or btn(2)) and p.grounded then p.dy=-4.2 p.grounded=false sfx(0) end
|
|
78
|
+
p.dy=min(p.dy+.3,4)
|
|
79
|
+
|
|
80
|
+
-- horizontal move (block at body height)
|
|
81
|
+
local nx=p.x+p.dx
|
|
82
|
+
if not solid_at(nx+(p.dx>0 and 3 or -3),p.y-3) then p.x=nx end
|
|
83
|
+
|
|
84
|
+
-- vertical move. p.y = the FEET. land exactly on a platform's top edge.
|
|
85
|
+
local ny=p.y+p.dy
|
|
86
|
+
if p.dy>=0 then
|
|
87
|
+
-- falling: if the feet CROSS a platform top this step, snap onto it (no tunneling).
|
|
88
|
+
local gt=ground_top(p.x,p.y,ny)
|
|
89
|
+
if gt~=nil then
|
|
90
|
+
p.y=gt p.dy=0 p.grounded=true
|
|
91
|
+
else
|
|
92
|
+
p.y=ny p.grounded=false
|
|
93
|
+
end
|
|
94
|
+
else
|
|
95
|
+
-- rising: bonk head on a ceiling
|
|
96
|
+
if solid_at(p.x,ny-7) then p.dy=0 else p.y=ny end
|
|
97
|
+
p.grounded=false
|
|
98
|
+
end
|
|
99
|
+
p.x=mid(4,p.x,124)
|
|
100
|
+
if p.y>140 then reset_player() sfx(3) end
|
|
101
|
+
|
|
102
|
+
for c in all(coins) do
|
|
103
|
+
if abs(c.x-p.x)<6 and abs(c.y-p.y)<7 then del(coins,c) collected+=1 sfx(1) end
|
|
104
|
+
end
|
|
105
|
+
if abs(goal.x-p.x)<7 and abs(goal.y-p.y)<9 then
|
|
106
|
+
best=min(best,t\30) state="win" sfx(2) music(-1)
|
|
107
|
+
end
|
|
108
|
+
end
|
|
109
|
+
|
|
110
|
+
function _draw()
|
|
111
|
+
cls(12)
|
|
112
|
+
-- parallax hills
|
|
113
|
+
for i=0,4 do circfill(i*34-8,126,22,3) end
|
|
114
|
+
for i=0,3 do circfill(i*44+20,128,26,11) end
|
|
115
|
+
|
|
116
|
+
if state=="title" then
|
|
117
|
+
print("hop quest",44,44,7)
|
|
118
|
+
spr(1,60,60)
|
|
119
|
+
print("z/up jump",40,80,6)
|
|
120
|
+
print("z/x start",40,92,7)
|
|
121
|
+
return
|
|
122
|
+
end
|
|
123
|
+
|
|
124
|
+
for s in all(solids) do
|
|
125
|
+
rectfill(s[1],s[2],s[1]+s[3]-1,s[2]+s[4]-1,4)
|
|
126
|
+
rectfill(s[1],s[2],s[1]+s[3]-1,s[2],9) -- grass top
|
|
127
|
+
end
|
|
128
|
+
for c in all(coins) do spr(5+flr(c.f)%4,c.x-4,c.y-4) end
|
|
129
|
+
-- flag
|
|
130
|
+
rectfill(goal.x,goal.y,goal.x,goal.y+12,6)
|
|
131
|
+
spr(10,goal.x,goal.y-1)
|
|
132
|
+
-- hero
|
|
133
|
+
local s=p.grounded and (p.dx~=0 and (5+flr(p.anim)%2==5 and 2 or (flr(p.anim)%2==0 and 1 or 2)) or 1) or 3
|
|
134
|
+
spr(p.dx~=0 and (flr(p.anim)%2==0 and 1 or 2) or (p.grounded and 1 or 3),p.x-4,p.y-7,1,1,p.face<0)
|
|
135
|
+
|
|
136
|
+
rectfill(0,0,127,7,0)
|
|
137
|
+
print("coins "..collected.."/"..total,2,1,10)
|
|
138
|
+
if best<99 then print("best "..best.."s",84,1,6) end
|
|
139
|
+
if state=="win" then
|
|
140
|
+
rectfill(28,50,100,80,0) rect(28,50,100,80,11)
|
|
141
|
+
print("you win!",44,55,11)
|
|
142
|
+
print("time "..(t\30).."s",44,64,7)
|
|
143
|
+
print("z/x title",40,72,6)
|
|
144
|
+
end
|
|
145
|
+
end
|
|
146
|
+
|
|
147
|
+
__gfx__
|
|
148
|
+
0000000000fff00000fff00080fff0080000000000aaaa00000aa0000000a000000aa00000066000066000000000000000000000000000000000000000000000
|
|
149
|
+
0000000000fff00000fff00008fff880000000000a9779a000a99a00000a9a0000a99a0000066660066600000000000000000000000000000000000000000000
|
|
150
|
+
0000000000f4f00000f4f00000f4f00000000000a977779a0a9779a0000a9a000a9779a000060000066660000000000000000000000000000000000000000000
|
|
151
|
+
0000000008888000088880000088880000000000a977779a0a9779a0000a9a000a9779a000060000066600000000000000000000000000000000000000000000
|
|
152
|
+
0000000008888000088880000088880000000000a999999a0a9999a0000a9a000a9999a000600000066000000000000000000000000000000000000000000000
|
|
153
|
+
0000000008880000088800000008800000000000a999999a0a9999a0000a9a000a9999a000060000060000000000000000000000000000000000000000000000
|
|
154
|
+
00000000008008000088000000800800000000000a9999a000a99a00000a9a0000a99a0000060000060000000000000000000000000000000000000000000000
|
|
155
|
+
0000000000b00b00000bb00000b00b000000000000aaaa00000aa0000000a000000aa00000060000060000000000000000000000000000000000000000000000
|
|
156
|
+
__sfx__
|
|
157
|
+
000300001f04025040290400000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
|
|
158
|
+
000200002465527655296550000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
|
|
159
|
+
011000002465521655216551d6551a655000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
|
|
160
|
+
010600000865505655036550000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
|
|
161
|
+
000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
|
|
162
|
+
000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
|
|
163
|
+
000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
|
|
164
|
+
000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
|
|
165
|
+
001400001c4501c4501d4501f4501f4501d4501c4501a45018450184501a4501c4501c4501a4501a4501a4501c4501c4501d4501f4501f4501d4501c4501a45018450184501a4501c4501a450184501845018450
|
|
166
|
+
001400000c2300c2300c2301323013230132300c230132300c2300c230132300c2300c2301323013230132300c2300c2300c2301323013230132300c230132300c2300c230132300c230132300c2300c2300c230
|
|
167
|
+
__music__
|
|
168
|
+
03 08094040
|
|
@@ -0,0 +1,240 @@
|
|
|
1
|
+
pico-8 cartridge // http://www.pico-8.com
|
|
2
|
+
version 18
|
|
3
|
+
__lua__
|
|
4
|
+
-- color drop — a falling-column match puzzle (Columns-style) scaffold
|
|
5
|
+
-- genre example for romdev/pico8. THE GAME: a vertical column of 3 jewels
|
|
6
|
+
-- falls into an 8-wide well. move left/right, soft-drop (down), hard-drop
|
|
7
|
+
-- (x), and CYCLE the 3 colors (o). match 3+ of one color horizontally,
|
|
8
|
+
-- vertically, or DIAGONALLY to clear; gravity pulls survivors down, which
|
|
9
|
+
-- can CASCADE into more clears. fork it and reshape one thing at a time.
|
|
10
|
+
|
|
11
|
+
cols=8 rows=13 cell=9 ox=26 oy=3
|
|
12
|
+
ncol=5 -- jewel colors 1..5
|
|
13
|
+
-- well fits the 128px screen: oy + rows*cell = 3 + 117 = 120 (bottom row fully visible).
|
|
14
|
+
|
|
15
|
+
function _init()
|
|
16
|
+
hi=0
|
|
17
|
+
state="title"
|
|
18
|
+
boot=0
|
|
19
|
+
reset_game()
|
|
20
|
+
|
|
21
|
+
music(0)
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
function reset_game()
|
|
25
|
+
grid={}
|
|
26
|
+
for y=1,rows do grid[y]={} for x=1,cols do grid[y][x]=0 end end
|
|
27
|
+
score=0 chain=0 level=1 cleared=0
|
|
28
|
+
fall=0 t=0
|
|
29
|
+
flashing=nil -- {cells=..,ttl=..} during a clear
|
|
30
|
+
nextp=randcol3()
|
|
31
|
+
spawn_piece()
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
function randcol3()
|
|
35
|
+
return {flr(rnd(ncol))+1,flr(rnd(ncol))+1,flr(rnd(ncol))+1}
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
-- the falling column: 3 colors top->bottom at (px, py=top cell)
|
|
39
|
+
function spawn_piece()
|
|
40
|
+
pcol=nextp
|
|
41
|
+
nextp=randcol3()
|
|
42
|
+
px=flr(cols/2) py=1
|
|
43
|
+
-- game over if the top cells are blocked
|
|
44
|
+
if grid[1][px]~=0 or grid[2][px]~=0 or grid[3][px]~=0 then
|
|
45
|
+
hi=max(hi,score) state="over"
|
|
46
|
+
end
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
function can_be(x,y)
|
|
50
|
+
-- can the 3-tall column sit with its TOP cell at (x,y)?
|
|
51
|
+
for i=0,2 do
|
|
52
|
+
local cy=y+i
|
|
53
|
+
if cy>rows then return false end
|
|
54
|
+
if cy>=1 and grid[cy][x]~=0 then return false end
|
|
55
|
+
end
|
|
56
|
+
return true
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
function _update()
|
|
60
|
+
t+=1
|
|
61
|
+
|
|
62
|
+
-- resolve an in-progress clear (flash then remove + cascade)
|
|
63
|
+
if flashing then
|
|
64
|
+
flashing.ttl-=1
|
|
65
|
+
if flashing.ttl<=0 then
|
|
66
|
+
for c in all(flashing.cells) do grid[c.y][c.x]=0 end
|
|
67
|
+
collapse()
|
|
68
|
+
local m=find_matches()
|
|
69
|
+
if #m>0 then chain+=1 start_clear(m)
|
|
70
|
+
else chain=0 flashing=nil spawn_piece() end
|
|
71
|
+
end
|
|
72
|
+
return
|
|
73
|
+
end
|
|
74
|
+
|
|
75
|
+
if state=="title" then
|
|
76
|
+
boot+=1
|
|
77
|
+
if boot>10 and (btnp(4) or btnp(5)) then state="play" reset_game() music(-1) end
|
|
78
|
+
return
|
|
79
|
+
end
|
|
80
|
+
if state=="over" then
|
|
81
|
+
if btnp(4) or btnp(5) then state="title" end
|
|
82
|
+
return
|
|
83
|
+
end
|
|
84
|
+
|
|
85
|
+
-- move — snappy custom repeat (move on press, then again every 4 frames while held)
|
|
86
|
+
-- so a hold slides smoothly instead of pico-8's slow 15-frame btnp repeat.
|
|
87
|
+
hold_l = btn(0) and (hold_l or 0)+1 or 0
|
|
88
|
+
hold_r = btn(1) and (hold_r or 0)+1 or 0
|
|
89
|
+
if (hold_l==1 or (hold_l>4 and hold_l%3==0)) and px>1 and can_be(px-1,py) then px-=1 sfx(2) end
|
|
90
|
+
if (hold_r==1 or (hold_r>4 and hold_r%3==0)) and px<cols and can_be(px+1,py) then px+=1 sfx(2) end
|
|
91
|
+
-- cycle colors (o = btn4): rotate the 3-jewel column
|
|
92
|
+
if btnp(4) then
|
|
93
|
+
pcol={pcol[3],pcol[1],pcol[2]} sfx(3)
|
|
94
|
+
end
|
|
95
|
+
-- hard drop (x = btn5)
|
|
96
|
+
if btnp(5) then
|
|
97
|
+
while can_be(px,py+1) do py+=1 end
|
|
98
|
+
lock_piece() return
|
|
99
|
+
end
|
|
100
|
+
|
|
101
|
+
-- gravity (soft-drop on down)
|
|
102
|
+
local speed=btn(3) and 2 or max(6,26-level*2)
|
|
103
|
+
fall+=1
|
|
104
|
+
if fall>=speed then
|
|
105
|
+
fall=0
|
|
106
|
+
if can_be(px,py+1) then py+=1
|
|
107
|
+
else lock_piece() end
|
|
108
|
+
end
|
|
109
|
+
end
|
|
110
|
+
|
|
111
|
+
function lock_piece()
|
|
112
|
+
for i=0,2 do
|
|
113
|
+
local cy=py+i
|
|
114
|
+
if cy>=1 and cy<=rows then grid[cy][px]=pcol[i+1] end
|
|
115
|
+
end
|
|
116
|
+
sfx(0)
|
|
117
|
+
local m=find_matches()
|
|
118
|
+
if #m>0 then chain=1 start_clear(m)
|
|
119
|
+
else spawn_piece() end
|
|
120
|
+
end
|
|
121
|
+
|
|
122
|
+
function start_clear(cells)
|
|
123
|
+
flashing={cells=cells,ttl=12}
|
|
124
|
+
score+=#cells*10*chain
|
|
125
|
+
cleared+=#cells
|
|
126
|
+
level=1+flr(cleared/20)
|
|
127
|
+
sfx(1)
|
|
128
|
+
end
|
|
129
|
+
|
|
130
|
+
-- scan H, V, and both diagonals for runs of 3+ same color.
|
|
131
|
+
-- returns a list of {x,y} cells to clear (deduped).
|
|
132
|
+
local dirs={{1,0},{0,1},{1,1},{1,-1}}
|
|
133
|
+
function find_matches()
|
|
134
|
+
local hit={} -- key "x,y" -> {x,y}
|
|
135
|
+
for y=1,rows do for x=1,cols do
|
|
136
|
+
local c=grid[y][x]
|
|
137
|
+
if c~=0 then
|
|
138
|
+
for d in all(dirs) do
|
|
139
|
+
-- only start a run at its beginning (no same-color cell behind us)
|
|
140
|
+
local bx,by=x-d[1],y-d[2]
|
|
141
|
+
local prev = (bx>=1 and bx<=cols and by>=1 and by<=rows) and grid[by][bx] or -1
|
|
142
|
+
if prev~=c then
|
|
143
|
+
local run={}
|
|
144
|
+
local cx,cy=x,y
|
|
145
|
+
while cx>=1 and cx<=cols and cy>=1 and cy<=rows and grid[cy][cx]==c do
|
|
146
|
+
add(run,{x=cx,y=cy}) cx+=d[1] cy+=d[2]
|
|
147
|
+
end
|
|
148
|
+
if #run>=3 then for r in all(run) do hit[r.x..","..r.y]={x=r.x,y=r.y} end end
|
|
149
|
+
end
|
|
150
|
+
end
|
|
151
|
+
end
|
|
152
|
+
end end
|
|
153
|
+
local out={} for _,v in pairs(hit) do add(out,v) end
|
|
154
|
+
return out
|
|
155
|
+
end
|
|
156
|
+
|
|
157
|
+
-- gravity: drop every column's survivors to the bottom
|
|
158
|
+
function collapse()
|
|
159
|
+
for x=1,cols do
|
|
160
|
+
local w=rows
|
|
161
|
+
for y=rows,1,-1 do
|
|
162
|
+
if grid[y][x]~=0 then
|
|
163
|
+
grid[w][x]=grid[y][x] if w~=y then grid[y][x]=0 end w-=1
|
|
164
|
+
end
|
|
165
|
+
end
|
|
166
|
+
end
|
|
167
|
+
end
|
|
168
|
+
|
|
169
|
+
-- ── draw ──
|
|
170
|
+
gemcol={8,9,10,11,12}
|
|
171
|
+
function drawgem(gx,gy,c)
|
|
172
|
+
local base=gemcol[c] or 8
|
|
173
|
+
rectfill(gx+1,gy,gx+6,gy+7,base)
|
|
174
|
+
rectfill(gx,gy+1,gx+7,gy+6,base)
|
|
175
|
+
rectfill(gx+2,gy+1,gx+3,gy+2,7) -- sparkle
|
|
176
|
+
line(gx+1,gy+6,gx+6,gy+6,1) -- shadow
|
|
177
|
+
end
|
|
178
|
+
|
|
179
|
+
function _draw()
|
|
180
|
+
cls(0)
|
|
181
|
+
rectfill(0,0,ox-2,127,1)
|
|
182
|
+
rectfill(ox+cols*cell+1,0,127,127,1)
|
|
183
|
+
|
|
184
|
+
if state=="title" then
|
|
185
|
+
print("color drop",42,40,12)
|
|
186
|
+
-- show a sample 3-jewel column
|
|
187
|
+
for i=1,3 do drawgem(60,50+i*9,i) end
|
|
188
|
+
print("arrows move",34,86,7)
|
|
189
|
+
print("o cycle x drop",26,96,6)
|
|
190
|
+
print("z/x start",40,108,7)
|
|
191
|
+
return
|
|
192
|
+
end
|
|
193
|
+
|
|
194
|
+
rect(ox-1,oy-1,ox+cols*cell,oy+rows*cell,5)
|
|
195
|
+
-- placed gems
|
|
196
|
+
for y=1,rows do for x=1,cols do
|
|
197
|
+
if grid[y][x]~=0 then drawgem(ox+(x-1)*cell,oy+(y-1)*cell,grid[y][x]) end
|
|
198
|
+
end end
|
|
199
|
+
-- flashing clears
|
|
200
|
+
if flashing then
|
|
201
|
+
for c in all(flashing.cells) do
|
|
202
|
+
if t%4<2 then rectfill(ox+(c.x-1)*cell,oy+(c.y-1)*cell,ox+(c.x-1)*cell+8,oy+(c.y-1)*cell+8,7) end
|
|
203
|
+
end
|
|
204
|
+
elseif state=="play" then
|
|
205
|
+
-- the falling 3-jewel column
|
|
206
|
+
for i=0,2 do
|
|
207
|
+
local cy=py+i
|
|
208
|
+
if cy>=1 then drawgem(ox+(px-1)*cell,oy+(cy-1)*cell,pcol[i+1]) end
|
|
209
|
+
end
|
|
210
|
+
end
|
|
211
|
+
|
|
212
|
+
-- panel
|
|
213
|
+
print("score",102,8,7) print(score,102,16,10)
|
|
214
|
+
print("next",102,36,7)
|
|
215
|
+
for i=1,3 do drawgem(106,44+i*9,nextp[i]) end
|
|
216
|
+
print("lv "..level,102,76,7)
|
|
217
|
+
if chain>1 then print("x"..chain,102,88,8) end
|
|
218
|
+
|
|
219
|
+
if state=="over" then
|
|
220
|
+
rectfill(18,52,110,76,0) rect(18,52,110,76,8)
|
|
221
|
+
print("game over",44,56,8)
|
|
222
|
+
print("hi "..hi,52,66,10)
|
|
223
|
+
end
|
|
224
|
+
end
|
|
225
|
+
|
|
226
|
+
__gfx__
|
|
227
|
+
00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
|
|
228
|
+
__sfx__
|
|
229
|
+
010800001c0551c055000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
|
|
230
|
+
011000002465527655296552d65500000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
|
|
231
|
+
010400001865518655000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
|
|
232
|
+
010c00002465521655000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
|
|
233
|
+
000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
|
|
234
|
+
000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
|
|
235
|
+
000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
|
|
236
|
+
000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
|
|
237
|
+
0014000028450274502845027450284502345026450244502145000000184501c4502145023450000001c450204502345024450000001c4502845027450284502745028450234502645024450214500000000000
|
|
238
|
+
001400001523000000000000000015230000000000000000102300000000000000000c23000000000000000015230000000000000000152300000000000000001023000000000000000015230000000000000000
|
|
239
|
+
__music__
|
|
240
|
+
03 08094040
|
|
@@ -0,0 +1,122 @@
|
|
|
1
|
+
pico-8 cartridge // http://www.pico-8.com
|
|
2
|
+
version 18
|
|
3
|
+
__lua__
|
|
4
|
+
-- lane runner — a top-down endless racing scaffold
|
|
5
|
+
-- genre example for romdev/pico8. car sprites (see __gfx__), looping
|
|
6
|
+
-- music, engine/crash sfx, 3-lane dodging, scrolling road + dashes,
|
|
7
|
+
-- speed ramp, distance score + best. fork it.
|
|
8
|
+
|
|
9
|
+
function _init()
|
|
10
|
+
best=0
|
|
11
|
+
state="title"
|
|
12
|
+
boot=0
|
|
13
|
+
reset_run()
|
|
14
|
+
music(0)
|
|
15
|
+
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
function reset_run()
|
|
19
|
+
car={x=64,lane=1}
|
|
20
|
+
lanes={40,64,88}
|
|
21
|
+
rivals={}
|
|
22
|
+
dist=0 speed=2 spawn=0 road=0 t=0 shake=0
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
function _update()
|
|
26
|
+
t+=1
|
|
27
|
+
road=(road+speed)%16
|
|
28
|
+
if shake>0 then shake-=1 end
|
|
29
|
+
if state=="title" then
|
|
30
|
+
boot+=1
|
|
31
|
+
if boot>10 and (btnp(4) or btnp(5)) then state="play" reset_run() music(-1) end
|
|
32
|
+
return
|
|
33
|
+
end
|
|
34
|
+
if state=="over" then
|
|
35
|
+
if btnp(4) or btnp(5) then state="title" end
|
|
36
|
+
return
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
if btnp(0) and car.lane>0 then car.lane-=1 sfx(0) end
|
|
40
|
+
if btnp(1) and car.lane<2 then car.lane+=1 sfx(0) end
|
|
41
|
+
car.x=car.x+(lanes[car.lane+1]-car.x)*.4
|
|
42
|
+
|
|
43
|
+
speed=min(2+dist/700,5.5)
|
|
44
|
+
dist+=speed
|
|
45
|
+
|
|
46
|
+
spawn-=1
|
|
47
|
+
if spawn<=0 then
|
|
48
|
+
add(rivals,{lane=flr(rnd(3)),y=-14,c=flr(rnd(3))})
|
|
49
|
+
spawn=max(16,44-flr(dist/110))
|
|
50
|
+
end
|
|
51
|
+
for r in all(rivals) do
|
|
52
|
+
r.y+=speed
|
|
53
|
+
if r.y>140 then del(rivals,r) end
|
|
54
|
+
if r.lane==car.lane and abs(r.y-104)<13 then
|
|
55
|
+
sfx(2) shake=12 best=max(best,flr(dist/10)) state="over"
|
|
56
|
+
end
|
|
57
|
+
end
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
function _draw()
|
|
61
|
+
local sx=shake>0 and (rnd(4)-2) or 0
|
|
62
|
+
cls(3)
|
|
63
|
+
camera(sx,0)
|
|
64
|
+
-- grass texture
|
|
65
|
+
for i=0,8 do for j=0,8 do if (i+j)%2==0 then pset(i*16+2,j*16+8,11) end end end
|
|
66
|
+
-- road
|
|
67
|
+
rectfill(28,0,100,128,5)
|
|
68
|
+
rectfill(28,0,31,128,10) rectfill(97,0,100,128,10)
|
|
69
|
+
for y=-16,128,16 do
|
|
70
|
+
rectfill(52,y+road,54,y+road+9,7)
|
|
71
|
+
rectfill(74,y+road,76,y+road+9,7)
|
|
72
|
+
end
|
|
73
|
+
|
|
74
|
+
if state=="title" then
|
|
75
|
+
camera()
|
|
76
|
+
rectfill(12,42,116,94,0)
|
|
77
|
+
print("lane runner",40,48,10)
|
|
78
|
+
spr(1,60,64)
|
|
79
|
+
print("left/right swerve",22,80,6)
|
|
80
|
+
print("z/x start",40,88,7)
|
|
81
|
+
if best>0 then print("best "..best,50,100,7) end
|
|
82
|
+
return
|
|
83
|
+
end
|
|
84
|
+
|
|
85
|
+
for r in all(rivals) do spr(2+r.c,lanes[r.lane+1]-4,r.y-6) end
|
|
86
|
+
spr(1,car.x-4,104-6)
|
|
87
|
+
|
|
88
|
+
camera()
|
|
89
|
+
rectfill(0,0,127,8,0)
|
|
90
|
+
print("dist "..flr(dist/10),2,1,7)
|
|
91
|
+
print("spd "..flr(speed*10),88,1,10)
|
|
92
|
+
|
|
93
|
+
if state=="over" then
|
|
94
|
+
rectfill(22,52,106,80,0) rect(22,52,106,80,8)
|
|
95
|
+
print("crashed!",46,56,8)
|
|
96
|
+
print("dist "..flr(dist/10),44,66,7)
|
|
97
|
+
print("z/x title",40,74,6)
|
|
98
|
+
end
|
|
99
|
+
end
|
|
100
|
+
|
|
101
|
+
__gfx__
|
|
102
|
+
000000000008800000099000000aa000000ee0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
|
|
103
|
+
0000000000888800009999000aaaaa000eeeee000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
|
|
104
|
+
0000000008777780097777900a7777a00e7777e00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
|
|
105
|
+
0000000008888880099999900aaaaaa00eeeeee00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
|
|
106
|
+
0000000008888880099999900aaaaaa00eeeeee00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
|
|
107
|
+
0000000008777780097777900a7777a00e7777e00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
|
|
108
|
+
000000000088880000999900000aa000000ee0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
|
|
109
|
+
000000000080080000900900000aa000000ee0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
|
|
110
|
+
__sfx__
|
|
111
|
+
010800001505013050000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
|
|
112
|
+
010400001f05522055000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
|
|
113
|
+
010600000c05008050050550000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
|
|
114
|
+
000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
|
|
115
|
+
000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
|
|
116
|
+
000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
|
|
117
|
+
000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
|
|
118
|
+
000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
|
|
119
|
+
0012000017350183501a3501c3501a35018350173501a350133501f3501c350133501f3501c350133501f35017350183501a3501c3501a35018350173501a3501c3501d3501f3501d3501c3501a3501835017350
|
|
120
|
+
001200000b230000000b230000000b230000000b230000000c230000000c230000000c230000000c230000000b230000000b230000000b230000000b230000001023000000102300000010230000000b23000000
|
|
121
|
+
__music__
|
|
122
|
+
03 08094040
|