romdevtools 0.71.1 → 0.84.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 +19 -16
- package/CHANGELOG.md +184 -0
- package/README.md +5 -5
- package/examples/README.md +1 -0
- package/examples/gametank/templates/gt_draw.h +91 -0
- package/examples/gametank/templates/gt_hud.h +89 -0
- package/examples/gametank/templates/gt_palette.h +59 -0
- package/examples/gametank/templates/gt_sound.h +90 -0
- package/examples/gametank/templates/gt_sprites.h +53 -0
- package/examples/gametank/templates/platformer.c +318 -0
- package/examples/gametank/templates/puzzle.c +295 -0
- package/examples/gametank/templates/racing.c +139 -0
- package/examples/gametank/templates/shmup.c +277 -0
- package/examples/gametank/templates/sports.c +119 -0
- package/package.json +24 -22
- package/src/cores/capabilities.js +24 -0
- package/src/cores/registry.js +7 -1
- package/src/host/LibretroHost.js +90 -17
- package/src/host/callbacks.js +20 -2
- package/src/host/cpu-state.js +17 -0
- package/src/host/gametank-acp-state.js +53 -0
- package/src/http/skill-doc.js +10 -6
- package/src/mcp/tools/audio.js +1 -1
- package/src/mcp/tools/cart-parts.js +76 -0
- package/src/mcp/tools/lifecycle.js +1 -1
- package/src/mcp/tools/platform-tools.js +9 -1
- package/src/platforms/gametank/lib/gt/audio/acp_image.bin +0 -0
- package/src/platforms/gametank/lib/gt/audio/audio_coprocessor.c +55 -0
- package/src/platforms/gametank/lib/gt/audio/audio_coprocessor.h +34 -0
- package/src/platforms/gametank/lib/gt/audio/audio_fw.asm +223 -0
- package/src/platforms/gametank/lib/gt/audio/audio_fw_blob.s +8 -0
- package/src/platforms/gametank/lib/gt/audio/instruments.c +79 -0
- package/src/platforms/gametank/lib/gt/audio/instruments.h +25 -0
- package/src/platforms/gametank/lib/gt/audio/music.c +370 -0
- package/src/platforms/gametank/lib/gt/audio/music.h +35 -0
- package/src/platforms/gametank/lib/gt/audio/note_numbers.h +132 -0
- package/src/platforms/gametank/lib/gt/audio/scaled_sines.raw +0 -0
- package/src/platforms/gametank/lib/gt/audio/sine_256_-63_63.bin +0 -0
- package/src/platforms/gametank/lib/gt/banking.c +29 -0
- package/src/platforms/gametank/lib/gt/banking.h +8 -0
- package/src/platforms/gametank/lib/gt/banking2.s +41 -0
- package/src/platforms/gametank/lib/gt/crt0.s +102 -0
- package/src/platforms/gametank/lib/gt/draw_logo.s +55 -0
- package/src/platforms/gametank/lib/gt/feature/persist/persist.c +103 -0
- package/src/platforms/gametank/lib/gt/feature/persist/persist.h +13 -0
- package/src/platforms/gametank/lib/gt/feature/random/random.c +40 -0
- package/src/platforms/gametank/lib/gt/feature/random/random.h +18 -0
- package/src/platforms/gametank/lib/gt/feature/text/text.c +111 -0
- package/src/platforms/gametank/lib/gt/feature/text/text.h +25 -0
- package/src/platforms/gametank/lib/gt/gametank.c +12 -0
- package/src/platforms/gametank/lib/gt/gametank.h +80 -0
- package/src/platforms/gametank/lib/gt/gametank_logo.inc +393 -0
- package/src/platforms/gametank/lib/gt/gen/assets/assets_index.h +9 -0
- package/src/platforms/gametank/lib/gt/gen/assets/sdk_default.h +5 -0
- package/src/platforms/gametank/lib/gt/gen/bank_nums.h +11 -0
- package/src/platforms/gametank/lib/gt/gen/modules_enabled.h +4 -0
- package/src/platforms/gametank/lib/gt/gen/modules_enabled.inc +4 -0
- package/src/platforms/gametank/lib/gt/gfx/draw_direct.c +132 -0
- package/src/platforms/gametank/lib/gt/gfx/draw_direct.h +75 -0
- package/src/platforms/gametank/lib/gt/gfx/draw_queue.c +177 -0
- package/src/platforms/gametank/lib/gt/gfx/draw_queue.h +35 -0
- package/src/platforms/gametank/lib/gt/gfx/draw_util.s +157 -0
- package/src/platforms/gametank/lib/gt/gfx/gfx_sys.c +43 -0
- package/src/platforms/gametank/lib/gt/gfx/gfx_sys.h +46 -0
- package/src/platforms/gametank/lib/gt/gfx/sprites.c +216 -0
- package/src/platforms/gametank/lib/gt/gfx/sprites.h +41 -0
- package/src/platforms/gametank/lib/gt/init.c +9 -0
- package/src/platforms/gametank/lib/gt/input.c +26 -0
- package/src/platforms/gametank/lib/gt/input.h +20 -0
- package/src/platforms/gametank/lib/gt/interrupt.s +67 -0
- package/src/platforms/gametank/lib/gt/vectors.s +14 -0
- package/src/platforms/gametank/lib/gt/wait.s +53 -0
- package/src/playtest/playtest.js +174 -26
- package/src/toolchains/arm-none-eabi-gcc/gcc.js +39 -188
- package/src/toolchains/asar/asar.js +10 -15
- package/src/toolchains/cc65/cc65.js +82 -92
- package/src/toolchains/cc65/da65.js +12 -17
- package/src/toolchains/cc65/preset-resolver.js +13 -2
- package/src/toolchains/cc65/presets/gametank/gametank.h +80 -0
- package/src/toolchains/cc65/presets/gametank/sdk.cfg +32 -0
- package/src/toolchains/cc65/presets/gametank/sdk.crt0.s +61 -0
- package/src/toolchains/cc65/presets/gametank/sdk.vectors.s +11 -0
- package/src/toolchains/cc65/presets/gametank/single-bank.cfg +28 -0
- package/src/toolchains/cc65/presets/gametank/single-bank.crt0.s +71 -0
- package/src/toolchains/cc65/presets/gametank/single-bank.vectors.s +12 -0
- package/src/toolchains/common/c-build.js +109 -0
- package/src/toolchains/common/gcc-toolchain.js +164 -0
- package/src/toolchains/common/wasm-tool.js +101 -0
- package/src/toolchains/dasm/dasm.js +12 -18
- package/src/toolchains/gba-c/gba-c.js +253 -305
- package/src/toolchains/genesis-c/genesis-c.js +196 -225
- package/src/toolchains/index.js +58 -4
- package/src/toolchains/m68k-elf-gcc/gcc.js +39 -202
- package/src/toolchains/mips-c/mips-c.js +68 -78
- package/src/toolchains/mips-elf-gcc/gcc.js +55 -118
- package/src/toolchains/rgbds/rgbds.js +7 -19
- package/src/toolchains/sdcc/sdcc.js +35 -26
- package/src/toolchains/sh-c/sh-c.js +44 -52
- package/src/toolchains/sh-elf-gcc/gcc.js +55 -110
- package/src/toolchains/sjasm/sjasm.js +10 -14
- package/src/toolchains/snes-c/snes-c.js +125 -150
- package/src/toolchains/tcc816/tcc816.js +10 -15
- package/src/toolchains/vasm68k/vasm68k.js +11 -16
- package/src/toolchains/wladx/wladx.js +5 -17
- package/src/toolchains/z80/binutils.js +5 -11
|
@@ -0,0 +1,295 @@
|
|
|
1
|
+
/* ── puzzle.c — CHROMA WELL: GameTank falling-jewel matcher (complete example) ──
|
|
2
|
+
*
|
|
3
|
+
* A COMPLETE, working game on the bundled GameTank SDK draw-queue runtime, in the
|
|
4
|
+
* Columns mold: a vertical COLUMN OF 3 JEWELS falls as a unit into an 8-wide ×
|
|
5
|
+
* 13-tall well. Move it left/right, CYCLE the three colors (A / B), soft-drop
|
|
6
|
+
* (Down), hard-drop (Start). Line up 3+ of one color horizontally, vertically, or
|
|
7
|
+
* DIAGONALLY to clear them; gravity pulls survivors down, which can CHAIN into
|
|
8
|
+
* cascades for bonus points. Title → play → game-over → restart.
|
|
9
|
+
*
|
|
10
|
+
* FORK THIS. Markers:
|
|
11
|
+
* HARDWARE IDIOM (load-bearing) — every frame is redrawn from the grid as
|
|
12
|
+
* blitter rects via the draw QUEUE (gt_draw.h); the top scanline is re-cleaned
|
|
13
|
+
* with queue_clear_border() last (sprite/edge draws otherwise leave a seam).
|
|
14
|
+
* GAME LOGIC (clay) — well size, colors, fall speed, scoring, the cycle keys.
|
|
15
|
+
*
|
|
16
|
+
* CONTROLS: ←/→ move · A cycle colors up · B cycle colors down · Down soft-drop ·
|
|
17
|
+
* A/START to begin. SCREEN: 128x128. PLAYERS: 1.
|
|
18
|
+
*/
|
|
19
|
+
#include "gametank.h"
|
|
20
|
+
#include "draw_queue.h"
|
|
21
|
+
#include "input.h"
|
|
22
|
+
#include "gt_palette.h"
|
|
23
|
+
#include "gt_draw.h"
|
|
24
|
+
#include "gt_hud.h"
|
|
25
|
+
#include "gt_sound.h"
|
|
26
|
+
|
|
27
|
+
/* ── GAME LOGIC (clay) ── board geometry */
|
|
28
|
+
#define COLS 8
|
|
29
|
+
#define ROWS 13
|
|
30
|
+
#define CELL 8 /* jewel pixel size (8*8 = 64-wide well) */
|
|
31
|
+
#define ORIGIN_X 30 /* well left edge on screen (centered) */
|
|
32
|
+
#define ORIGIN_Y 16 /* well top edge (below the HUD bar) */
|
|
33
|
+
#define N_COLORS 6
|
|
34
|
+
|
|
35
|
+
#define C_BG GT_DKBLUE /* backdrop */
|
|
36
|
+
#define C_WELL GT_NIGHT /* empty well interior */
|
|
37
|
+
#define C_FRAME GT_NAVY /* well frame + HUD bar */
|
|
38
|
+
#define C_HUD GT_WHITE
|
|
39
|
+
|
|
40
|
+
/* 6 jewel colors chosen to be clearly distinct hues — no two oranges. Bevel is a
|
|
41
|
+
* lighter shade of each for the top-left highlight.
|
|
42
|
+
* red · orange · green · cyan · blue · magenta (≈ a 6-hue rainbow) */
|
|
43
|
+
static const unsigned char JEWEL[N_COLORS] = { GT_RED, GT_ORANGE, GT_GREEN, GT_CYAN, GT_SKY, GT_MAGENTA };
|
|
44
|
+
static const unsigned char JLITE[N_COLORS] = { GT_ROSE, GT_GOLD, GT_LIME, GT_TEAL, GT_WHITE, GT_PINK };
|
|
45
|
+
|
|
46
|
+
/* Draw one beveled jewel (1-based color) at screen (x,y), with its 4 CORNER pixels
|
|
47
|
+
* left as the well color so the gem reads as rounded — an octagon, not a flat box.
|
|
48
|
+
* The body is a 7x7 with the corners cut: a tall center strip + a wide middle strip
|
|
49
|
+
* cover everything except the 4 corner cells, which stay C_WELL (the backdrop).
|
|
50
|
+
* (Background param so the title pile / NEXT preview round against their own bg.) */
|
|
51
|
+
static void draw_jewel_on(unsigned char x, unsigned char y, unsigned char c, unsigned char bg) {
|
|
52
|
+
unsigned char base = JEWEL[c - 1], lite = JLITE[c - 1];
|
|
53
|
+
unsigned char w = CELL - 1; /* 7 */
|
|
54
|
+
queue_draw_box(x, y, w, w, bg); /* clear the cell to bg (rounds corners) */
|
|
55
|
+
queue_draw_box(x + 1, y, w - 2, w, base);/* center vertical strip (cols 1..5) */
|
|
56
|
+
queue_draw_box(x, y + 1, w, w - 2, base);/* center horizontal strip (rows 1..5) */
|
|
57
|
+
queue_draw_box(x + 1, y, w - 2, 2, lite);/* top highlight (inside the rounding) */
|
|
58
|
+
queue_draw_box(x, y + 1, 2, w - 2, lite);/* left highlight */
|
|
59
|
+
}
|
|
60
|
+
/* in-well jewels round against the well interior */
|
|
61
|
+
static void draw_jewel(unsigned char x, unsigned char y, unsigned char c) {
|
|
62
|
+
draw_jewel_on(x, y, c, C_WELL);
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
/* ── GAME LOGIC (clay) ── state */
|
|
66
|
+
static unsigned char grid[ROWS][COLS]; /* 0 = empty, 1..N_COLORS = a jewel */
|
|
67
|
+
static unsigned char piece[3]; /* the 3 falling colors, top→bottom */
|
|
68
|
+
static unsigned char nextp[3]; /* previewed next column */
|
|
69
|
+
static unsigned char piece_x, piece_y; /* well coords of the column's TOP cell */
|
|
70
|
+
static unsigned char fall_t;
|
|
71
|
+
static unsigned int score;
|
|
72
|
+
static unsigned char move_cool;
|
|
73
|
+
|
|
74
|
+
/* tiny xorshift PRNG comes from gt_draw.h (rnd8) — the SDK rnd() corrupts state. */
|
|
75
|
+
static void roll(unsigned char *p) {
|
|
76
|
+
p[0] = 1 + (unsigned char)(rnd8() % N_COLORS);
|
|
77
|
+
p[1] = 1 + (unsigned char)(rnd8() % N_COLORS);
|
|
78
|
+
p[2] = 1 + (unsigned char)(rnd8() % N_COLORS);
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
/* is well cell (r,c) off the bottom or filled? */
|
|
82
|
+
static unsigned char blocked(unsigned char r, unsigned char c) {
|
|
83
|
+
if (r >= ROWS) return 1;
|
|
84
|
+
return grid[r][c] ? 1 : 0;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
/* would the 3-tall column collide with its top cell at (c, topy)? */
|
|
88
|
+
static unsigned char collides(unsigned char c, unsigned char topy) {
|
|
89
|
+
if (c >= COLS) return 1;
|
|
90
|
+
if (blocked(topy, c)) return 1;
|
|
91
|
+
if (blocked((unsigned char)(topy + 1), c)) return 1;
|
|
92
|
+
if (blocked((unsigned char)(topy + 2), c)) return 1;
|
|
93
|
+
return 0;
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
/* start a new column at top-center from the preview; roll the next preview. */
|
|
97
|
+
static void spawn(void) {
|
|
98
|
+
piece[0] = nextp[0]; piece[1] = nextp[1]; piece[2] = nextp[2];
|
|
99
|
+
roll(nextp);
|
|
100
|
+
piece_x = COLS / 2 - 1;
|
|
101
|
+
piece_y = 0;
|
|
102
|
+
fall_t = 0;
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
/* ── match resolution: flag 3+ runs in all 4 directions, clear, gravity, chain ──
|
|
106
|
+
* the 4 line directions: horizontal, vertical, both diagonals. */
|
|
107
|
+
static const signed char DR[4] = { 0, 1, 1, 1 };
|
|
108
|
+
static const signed char DC[4] = { 1, 0, 1, -1 };
|
|
109
|
+
|
|
110
|
+
static unsigned char marked[ROWS][COLS];
|
|
111
|
+
|
|
112
|
+
/* flag every cell in a 3+ same-color run; return count flagged. */
|
|
113
|
+
static unsigned char mark_matches(void) {
|
|
114
|
+
unsigned char r, c, d, len, k, col, cnt;
|
|
115
|
+
signed char sr, sc;
|
|
116
|
+
for (r = 0; r < ROWS; r++) for (c = 0; c < COLS; c++) marked[r][c] = 0;
|
|
117
|
+
for (r = 0; r < ROWS; r++) {
|
|
118
|
+
for (c = 0; c < COLS; c++) {
|
|
119
|
+
col = grid[r][c];
|
|
120
|
+
if (!col) continue;
|
|
121
|
+
for (d = 0; d < 4; d++) {
|
|
122
|
+
/* only walk each run from its lowest end */
|
|
123
|
+
sr = (signed char)r - DR[d];
|
|
124
|
+
sc = (signed char)c - DC[d];
|
|
125
|
+
if (sr >= 0 && sr < ROWS && sc >= 0 && sc < COLS && grid[sr][sc] == col) continue;
|
|
126
|
+
len = 1;
|
|
127
|
+
sr = (signed char)r + DR[d];
|
|
128
|
+
sc = (signed char)c + DC[d];
|
|
129
|
+
while (sr >= 0 && sr < ROWS && sc >= 0 && sc < COLS && grid[sr][sc] == col) {
|
|
130
|
+
len++; sr += DR[d]; sc += DC[d];
|
|
131
|
+
}
|
|
132
|
+
if (len >= 3) {
|
|
133
|
+
sr = (signed char)r; sc = (signed char)c;
|
|
134
|
+
for (k = 0; k < len; k++) { marked[sr][sc] = 1; sr += DR[d]; sc += DC[d]; }
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
cnt = 0;
|
|
140
|
+
for (r = 0; r < ROWS; r++) for (c = 0; c < COLS; c++) if (marked[r][c]) cnt++;
|
|
141
|
+
return cnt;
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
/* drop every column so jewels rest on the floor with no gaps. */
|
|
145
|
+
static void apply_gravity(void) {
|
|
146
|
+
unsigned char c, r, n, w;
|
|
147
|
+
unsigned char buf[ROWS];
|
|
148
|
+
for (c = 0; c < COLS; c++) {
|
|
149
|
+
n = 0;
|
|
150
|
+
for (r = 0; r < ROWS; r++) if (grid[r][c]) { buf[n] = grid[r][c]; n++; }
|
|
151
|
+
for (r = 0; r < (unsigned char)(ROWS - n); r++) grid[r][c] = 0;
|
|
152
|
+
w = 0;
|
|
153
|
+
for (r = (unsigned char)(ROWS - n); r < ROWS; r++) { grid[r][c] = buf[w]; w++; }
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
/* settle the board: find→clear→gravity, looping so cascades chain. */
|
|
158
|
+
static void resolve_board(void) {
|
|
159
|
+
unsigned char n, r, c, chain = 0;
|
|
160
|
+
for (;;) {
|
|
161
|
+
n = mark_matches();
|
|
162
|
+
if (!n) break;
|
|
163
|
+
chain++;
|
|
164
|
+
for (r = 0; r < ROWS; r++) for (c = 0; c < COLS; c++) if (marked[r][c]) grid[r][c] = 0;
|
|
165
|
+
score += (unsigned int)n * (chain > 1 ? (unsigned int)(10 * chain) : 10);
|
|
166
|
+
gt_sfx(GT_SFX_HIT);
|
|
167
|
+
apply_gravity();
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
/* stamp the falling column where it rests, then resolve matches. */
|
|
172
|
+
static void lock_piece(void) {
|
|
173
|
+
unsigned char i, r;
|
|
174
|
+
for (i = 0; i < 3; i++) {
|
|
175
|
+
r = (unsigned char)(piece_y + i);
|
|
176
|
+
if (r < ROWS) grid[r][piece_x] = piece[i];
|
|
177
|
+
}
|
|
178
|
+
gt_sfx(GT_SFX_EXPLODE);
|
|
179
|
+
resolve_board();
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
static void reset_game(void) {
|
|
183
|
+
unsigned char r, c;
|
|
184
|
+
for (r = 0; r < ROWS; r++) for (c = 0; c < COLS; c++) grid[r][c] = 0;
|
|
185
|
+
score = 0; fall_t = 0; move_cool = 0;
|
|
186
|
+
roll(nextp);
|
|
187
|
+
spawn();
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
/* ── draw the whole frame: well + locked jewels + falling column + preview + HUD ── */
|
|
191
|
+
static void draw_frame(void) {
|
|
192
|
+
unsigned char r, c, i;
|
|
193
|
+
gt_clear(C_BG);
|
|
194
|
+
/* well frame + interior */
|
|
195
|
+
gt_rect(ORIGIN_X - 2, ORIGIN_Y - 2, COLS * CELL + 4, ROWS * CELL + 4, C_FRAME);
|
|
196
|
+
gt_rect(ORIGIN_X, ORIGIN_Y, COLS * CELL, ROWS * CELL, C_WELL);
|
|
197
|
+
/* locked jewels */
|
|
198
|
+
for (r = 0; r < ROWS; r++) for (c = 0; c < COLS; c++) if (grid[r][c])
|
|
199
|
+
draw_jewel((unsigned char)(ORIGIN_X + c * CELL), (unsigned char)(ORIGIN_Y + r * CELL), grid[r][c]);
|
|
200
|
+
/* the falling 3-jewel column */
|
|
201
|
+
for (i = 0; i < 3; i++) {
|
|
202
|
+
unsigned char rr = (unsigned char)(piece_y + i);
|
|
203
|
+
if (rr < ROWS)
|
|
204
|
+
draw_jewel((unsigned char)(ORIGIN_X + piece_x * CELL),
|
|
205
|
+
(unsigned char)(ORIGIN_Y + rr * CELL), piece[i]);
|
|
206
|
+
}
|
|
207
|
+
/* NEXT preview, to the right of the well (rounds against the C_BG backdrop) */
|
|
208
|
+
for (i = 0; i < 3; i++)
|
|
209
|
+
draw_jewel_on((unsigned char)(ORIGIN_X + COLS * CELL + 6),
|
|
210
|
+
(unsigned char)(ORIGIN_Y + 4 + i * CELL), nextp[i], C_BG);
|
|
211
|
+
/* re-clean the top border FIRST (it covers rows 0-6), THEN draw the score on top
|
|
212
|
+
* at y=3 so the border-clean doesn't erase it. hud_number anchors the RIGHTMOST
|
|
213
|
+
* digit at x and grows LEFT, so x=52 fits a 6-digit score. */
|
|
214
|
+
queue_clear_border(C_BG);
|
|
215
|
+
hud_number(score, 52, 3, 2, C_HUD);
|
|
216
|
+
gt_present();
|
|
217
|
+
gt_music_tick();
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
static unsigned char title_or_over(unsigned char over) {
|
|
221
|
+
unsigned char i;
|
|
222
|
+
gt_start_reset();
|
|
223
|
+
while (1) {
|
|
224
|
+
gt_clear(C_BG);
|
|
225
|
+
if (!over) {
|
|
226
|
+
hud_text("JEWELS", 28, 28, 3, C_HUD); /* title */
|
|
227
|
+
/* a little pile of colored jewels as the title art (rounds against C_BG) */
|
|
228
|
+
for (i = 0; i < N_COLORS; i++)
|
|
229
|
+
draw_jewel_on((unsigned char)(30 + i * 11), 64, (unsigned char)(i + 1), C_BG);
|
|
230
|
+
hud_text("PRESS A", 34, 96, 2, C_HUD); /* prompt */
|
|
231
|
+
} else {
|
|
232
|
+
hud_text("GAME", 40, 36, 3, C_HUD);
|
|
233
|
+
hud_text("OVER", 40, 54, 3, C_HUD);
|
|
234
|
+
hud_number(score, 80, 78, 2, C_HUD);
|
|
235
|
+
}
|
|
236
|
+
queue_clear_border(C_BG);
|
|
237
|
+
gt_present();
|
|
238
|
+
gt_music_tick();
|
|
239
|
+
update_inputs();
|
|
240
|
+
if (gt_start_pressed()) return 1;
|
|
241
|
+
}
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
void main(void) {
|
|
245
|
+
unsigned char t;
|
|
246
|
+
for (;;) {
|
|
247
|
+
title_or_over(0);
|
|
248
|
+
reset_game();
|
|
249
|
+
|
|
250
|
+
for (;;) {
|
|
251
|
+
update_inputs();
|
|
252
|
+
if (move_cool) move_cool--;
|
|
253
|
+
|
|
254
|
+
/* move left/right */
|
|
255
|
+
if (!move_cool) {
|
|
256
|
+
if ((player1_buttons & INPUT_MASK_LEFT) && piece_x > 0
|
|
257
|
+
&& !collides((unsigned char)(piece_x - 1), piece_y)) { piece_x--; move_cool = 5; gt_sfx(GT_SFX_SHOOT); }
|
|
258
|
+
if ((player1_buttons & INPUT_MASK_RIGHT) && piece_x < COLS - 1
|
|
259
|
+
&& !collides((unsigned char)(piece_x + 1), piece_y)) { piece_x++; move_cool = 5; gt_sfx(GT_SFX_SHOOT); }
|
|
260
|
+
}
|
|
261
|
+
/* A = cycle colors up, B = cycle colors down (edge-triggered) */
|
|
262
|
+
if (player1_new_buttons & INPUT_MASK_A) {
|
|
263
|
+
t = piece[0]; piece[0] = piece[1]; piece[1] = piece[2]; piece[2] = t; gt_sfx(GT_SFX_COIN);
|
|
264
|
+
}
|
|
265
|
+
if (player1_new_buttons & INPUT_MASK_B) {
|
|
266
|
+
t = piece[2]; piece[2] = piece[1]; piece[1] = piece[0]; piece[0] = t; gt_sfx(GT_SFX_COIN);
|
|
267
|
+
}
|
|
268
|
+
/* hard drop on START */
|
|
269
|
+
if (player1_new_buttons & INPUT_MASK_START) {
|
|
270
|
+
while (!collides(piece_x, (unsigned char)(piece_y + 1))) piece_y++;
|
|
271
|
+
lock_piece();
|
|
272
|
+
spawn();
|
|
273
|
+
if (collides(piece_x, piece_y)) goto over; /* well full → game over */
|
|
274
|
+
fall_t = 0;
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
/* gravity: a row every ~14 frames; Down soft-drops every 3. */
|
|
278
|
+
fall_t++;
|
|
279
|
+
if (((player1_buttons & INPUT_MASK_DOWN) && fall_t >= 3) || fall_t >= 14) {
|
|
280
|
+
fall_t = 0;
|
|
281
|
+
if (collides(piece_x, (unsigned char)(piece_y + 1))) {
|
|
282
|
+
lock_piece();
|
|
283
|
+
spawn();
|
|
284
|
+
if (collides(piece_x, piece_y)) goto over;
|
|
285
|
+
} else {
|
|
286
|
+
piece_y++;
|
|
287
|
+
}
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
draw_frame();
|
|
291
|
+
}
|
|
292
|
+
over:
|
|
293
|
+
title_or_over(1);
|
|
294
|
+
}
|
|
295
|
+
}
|
|
@@ -0,0 +1,139 @@
|
|
|
1
|
+
/* ── racing.c — GameTank top-down road racer (complete example) ──────────────
|
|
2
|
+
*
|
|
3
|
+
* A COMPLETE, working game on the bundled GameTank SDK draw-queue runtime: a
|
|
4
|
+
* vertically-scrolling road with moving lane stripes, your car steering between
|
|
5
|
+
* lanes, rival cars + obstacles to dodge, a distance score that climbs with
|
|
6
|
+
* speed, and crash → lives → restart. The framebuffer makes the scrolling road
|
|
7
|
+
* EASY — the stripes are rects at a moving y-offset; no scroll register, no
|
|
8
|
+
* tilemap streaming.
|
|
9
|
+
*
|
|
10
|
+
* FORK THIS. Markers:
|
|
11
|
+
* HARDWARE IDIOM (load-bearing) — the "scroll" is a phase counter; road, stripes
|
|
12
|
+
* and rivals are rects drawn at (phase-shifted) y each frame. The road slab is
|
|
13
|
+
* drawn at HEIGHT 127, never 128: a full-screen-dimension box is silently
|
|
14
|
+
* dropped by the blitter (the road would vanish — see gt_draw.h).
|
|
15
|
+
* GAME LOGIC (clay) — speed curve, spawn rate, lane count, scoring: tune freely.
|
|
16
|
+
*
|
|
17
|
+
* High-contrast palette (dark asphalt vs bright grass) so the road reads clearly.
|
|
18
|
+
* SFX (gt_sound.h): a crash explosion when you hit a rival. Box-drawn HUD.
|
|
19
|
+
* SCREEN: 128x128. PLAYERS: 1. CONTROLS: ←/→ steer · A throttle · A/START to begin.
|
|
20
|
+
*/
|
|
21
|
+
#include "gametank.h"
|
|
22
|
+
#include "draw_queue.h"
|
|
23
|
+
#include "input.h"
|
|
24
|
+
|
|
25
|
+
#include "gt_palette.h"
|
|
26
|
+
#include "gt_draw.h"
|
|
27
|
+
#include "gt_hud.h"
|
|
28
|
+
#include "gt_sound.h"
|
|
29
|
+
|
|
30
|
+
/* high-contrast palette: DARK road against BRIGHT grass, bright stripes/edges, and
|
|
31
|
+
* vivid distinct cars so everything reads clearly. */
|
|
32
|
+
#define C_GRASS GT_GREEN /* bright green roadside */
|
|
33
|
+
#define C_ROAD GT_NIGHT /* near-black asphalt (pops against the grass) */
|
|
34
|
+
#define C_STRIPE GT_YELLOW /* bright lane stripes */
|
|
35
|
+
#define C_EDGE GT_WHITE /* white road edges */
|
|
36
|
+
#define C_CAR GT_CYAN /* player car (bright cyan) */
|
|
37
|
+
#define C_RIVAL GT_RED /* rival cars */
|
|
38
|
+
#define C_RIVAL2 GT_MAGENTA /* alt rival (vivid, distinct from red) */
|
|
39
|
+
#define C_HUD GT_WHITE
|
|
40
|
+
|
|
41
|
+
#define ROAD_X 24 /* road left edge */
|
|
42
|
+
#define ROAD_W 80 /* road width (24..104) */
|
|
43
|
+
#define N_RIVAL 4
|
|
44
|
+
|
|
45
|
+
static unsigned char car_x; /* player car left edge */
|
|
46
|
+
static unsigned char rvx[N_RIVAL], rvy[N_RIVAL], rvc[N_RIVAL], rv_on[N_RIVAL];
|
|
47
|
+
static unsigned int score;
|
|
48
|
+
static unsigned char lives, phase, speed, spawn_t;
|
|
49
|
+
|
|
50
|
+
static void reset_game(void) {
|
|
51
|
+
unsigned char i;
|
|
52
|
+
car_x = 56; score = 0; lives = 3; phase = 0; speed = 3; spawn_t = 0;
|
|
53
|
+
for (i = 0; i < N_RIVAL; i++) rv_on[i] = 0;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
static unsigned char absd(unsigned char a, unsigned char b) { return a > b ? a - b : b - a; }
|
|
57
|
+
|
|
58
|
+
static void spawn_rival(void) {
|
|
59
|
+
unsigned char i;
|
|
60
|
+
for (i = 0; i < N_RIVAL; i++) if (!rv_on[i]) {
|
|
61
|
+
rvx[i] = ROAD_X + 6 + (unsigned char)(rnd8() % (ROAD_W - 24));
|
|
62
|
+
rvy[i] = 0; rvc[i] = (unsigned char)(rnd8() & 1); rv_on[i] = 1; return;
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
static unsigned char title_or_over(unsigned char over) {
|
|
67
|
+
gt_start_reset();
|
|
68
|
+
while (1) {
|
|
69
|
+
gt_clear(C_GRASS);
|
|
70
|
+
gt_rect(ROAD_X, 0, ROAD_W, 127, C_ROAD);
|
|
71
|
+
if (!over) {
|
|
72
|
+
gt_rect(30, 38, 68, 10, C_STRIPE);
|
|
73
|
+
gt_rect(54, 70, 14, 22, C_CAR); /* car */
|
|
74
|
+
gt_rect(ROAD_X + ROAD_W / 2 - 1, 100, 2, 12, C_STRIPE);
|
|
75
|
+
} else { gt_rect(26, 52, 76, 24, C_GRASS); hud_number(score, 70, 58, 3, C_HUD); }
|
|
76
|
+
gt_present(); gt_music_tick();
|
|
77
|
+
update_inputs();
|
|
78
|
+
if (gt_start_pressed()) return 1;
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
void main(void) {
|
|
83
|
+
unsigned char i, y;
|
|
84
|
+
reset_game();
|
|
85
|
+
|
|
86
|
+
for (;;) {
|
|
87
|
+
title_or_over(0);
|
|
88
|
+
reset_game();
|
|
89
|
+
|
|
90
|
+
for (;;) {
|
|
91
|
+
update_inputs();
|
|
92
|
+
|
|
93
|
+
/* steer (clamped to the road) — a soft tick on each steer input */
|
|
94
|
+
if ((player1_buttons & INPUT_MASK_LEFT) && car_x > ROAD_X + 2) car_x -= 3;
|
|
95
|
+
if ((player1_buttons & INPUT_MASK_RIGHT) && car_x < ROAD_X + ROAD_W - 16) car_x += 3;
|
|
96
|
+
/* throttle with A = faster (more score, more danger) */
|
|
97
|
+
speed = (player1_buttons & INPUT_MASK_A) ? 5 : 3;
|
|
98
|
+
|
|
99
|
+
phase = (phase + speed) & 0x1F;
|
|
100
|
+
score += speed;
|
|
101
|
+
|
|
102
|
+
/* rivals */
|
|
103
|
+
spawn_t++;
|
|
104
|
+
if (spawn_t >= (speed > 4 ? 24 : 36)) { spawn_t = 0; spawn_rival(); }
|
|
105
|
+
for (i = 0; i < N_RIVAL; i++) if (rv_on[i]) {
|
|
106
|
+
rvy[i] += speed;
|
|
107
|
+
if (rvy[i] > 124) { rv_on[i] = 0; score += 10; } /* dodged (no sound) */
|
|
108
|
+
else if (rvy[i] + 20 >= 92 && rvy[i] <= 114 && absd(rvx[i], car_x) < 14) {
|
|
109
|
+
rv_on[i] = 0; gt_sfx(GT_SFX_EXPLODE); /* CRASH */
|
|
110
|
+
if (--lives == 0) goto dead;
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
/* ── HARDWARE IDIOM: grass + road + moving stripes + cars, all rects ── */
|
|
115
|
+
gt_clear(C_GRASS);
|
|
116
|
+
gt_rect(ROAD_X, 0, ROAD_W, 127, C_ROAD);
|
|
117
|
+
/* center dashes scroll by phase */
|
|
118
|
+
for (y = 0; y < 128; y += 16) {
|
|
119
|
+
unsigned char yy = (y + phase) & 0x7F;
|
|
120
|
+
gt_rect(ROAD_X + ROAD_W / 2 - 1, yy, 2, 8, C_STRIPE);
|
|
121
|
+
}
|
|
122
|
+
/* road edges */
|
|
123
|
+
gt_rect(ROAD_X - 2, 0, 2, 128, C_EDGE);
|
|
124
|
+
gt_rect(ROAD_X + ROAD_W, 0, 2, 128, C_EDGE);
|
|
125
|
+
for (i = 0; i < N_RIVAL; i++) if (rv_on[i])
|
|
126
|
+
gt_rect(rvx[i], rvy[i], 14, 20, rvc[i] ? C_RIVAL : C_RIVAL2);
|
|
127
|
+
gt_rect(car_x, 92, 14, 22, C_CAR); /* player car */
|
|
128
|
+
gt_rect(car_x + 3, 96, 8, 6, GT_DKBLUE); /* windshield */
|
|
129
|
+
hud_number(score, 28, 8, 2, C_HUD);
|
|
130
|
+
hud_pips(lives, 100, 8, 5, C_CAR);
|
|
131
|
+
/* Re-clean the top BORDER (rows 0-6) LAST so the full-height road edges don't
|
|
132
|
+
* leave a stray flickering line on the framebuffer's top scanline. */
|
|
133
|
+
queue_clear_border(C_GRASS);
|
|
134
|
+
gt_present(); gt_music_tick();
|
|
135
|
+
}
|
|
136
|
+
dead:
|
|
137
|
+
title_or_over(1);
|
|
138
|
+
}
|
|
139
|
+
}
|