romdevtools 0.27.0 → 0.28.0
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 +5 -3
- package/CHANGELOG.md +309 -0
- package/README.md +1 -1
- package/examples/README.md +1 -1
- package/examples/atari2600/templates/platformer.asm +18 -9
- package/examples/atari2600/templates/racing.asm +25 -4
- package/examples/atari2600/templates/shmup.asm +30 -5
- package/examples/atari2600/templates/sports.asm +41 -9
- package/examples/atari7800/templates/hello_sprite.c +8 -4
- package/examples/atari7800/templates/platformer.c +12 -8
- package/examples/atari7800/templates/puzzle.c +7 -4
- package/examples/atari7800/templates/racing.c +5 -2
- package/examples/atari7800/templates/shmup.c +8 -4
- package/examples/atari7800/templates/sports.c +6 -3
- package/examples/c64/templates/platformer.c +28 -24
- package/examples/c64/templates/puzzle.c +77 -16
- package/examples/c64/templates/racing.c +9 -0
- package/examples/c64/templates/shmup.c +13 -1
- package/examples/c64/templates/sports.c +9 -4
- package/examples/gb/templates/platformer.c +6 -2
- package/examples/gb/templates/puzzle.c +279 -101
- package/examples/gb/templates/racing.c +13 -1
- package/examples/gb/templates/shmup.c +13 -1
- package/examples/gb/templates/sports.c +9 -3
- package/examples/gba/templates/platformer.c +7 -13
- package/examples/gba/templates/puzzle.c +93 -15
- package/examples/gba/templates/racing.c +13 -1
- package/examples/gba/templates/shmup.c +13 -1
- package/examples/gba/templates/sports.c +17 -5
- package/examples/gbc/templates/platformer.c +6 -2
- package/examples/gbc/templates/puzzle.c +878 -178
- package/examples/gbc/templates/racing.c +13 -1
- package/examples/gbc/templates/shmup.c +13 -1
- package/examples/gbc/templates/sports.c +9 -3
- package/examples/genesis/templates/puzzle.c +76 -15
- package/examples/genesis/templates/racing.c +13 -1
- package/examples/genesis/templates/shmup_2p.c +13 -1
- package/examples/gg/templates/platformer.c +4 -0
- package/examples/gg/templates/puzzle.c +80 -14
- package/examples/gg/templates/racing.c +17 -1
- package/examples/gg/templates/shmup.c +17 -1
- package/examples/gg/templates/sports.c +4 -0
- package/examples/lynx/templates/platformer.c +25 -6
- package/examples/lynx/templates/puzzle.c +77 -14
- package/examples/lynx/templates/shmup.c +13 -1
- package/examples/lynx/templates/sports.c +5 -2
- package/examples/msx/platformer/main.c +2 -0
- package/examples/msx/puzzle/main.c +78 -15
- package/examples/msx/racing/main.c +1 -0
- package/examples/msx/shmup/main.c +1 -0
- package/examples/msx/sports/main.c +3 -2
- package/examples/nes/templates/platformer.c +11 -3
- package/examples/nes/templates/puzzle.c +81 -21
- package/examples/nes/templates/racing.c +15 -1
- package/examples/nes/templates/shmup.c +1 -0
- package/examples/nes/templates/sports.c +1 -0
- package/examples/pce/platformer/main.c +3 -1
- package/examples/pce/puzzle/main.c +78 -12
- package/examples/pce/racing/main.c +1 -0
- package/examples/pce/shmup/main.c +5 -4
- package/examples/pce/sports/main.c +4 -3
- package/examples/sms/templates/platformer.c +4 -0
- package/examples/sms/templates/puzzle.c +80 -14
- package/examples/sms/templates/racing.c +17 -1
- package/examples/sms/templates/shmup.c +17 -1
- package/examples/sms/templates/shmup_2p.c +17 -1
- package/examples/sms/templates/sports.c +4 -0
- package/examples/snes/templates/platformer.c +32 -15
- package/examples/snes/templates/puzzle.c +84 -16
- package/examples/snes/templates/racing.c +20 -1
- package/examples/snes/templates/shmup.c +20 -2
- package/examples/snes/templates/sports.c +7 -0
- package/package.json +12 -12
- package/src/cores/wasm/bluemsx_libretro.js +1 -1
- package/src/cores/wasm/bluemsx_libretro.wasm +0 -0
- package/src/cores/wasm/fceumm_libretro.js +1 -1
- package/src/cores/wasm/fceumm_libretro.wasm +0 -0
- package/src/cores/wasm/gambatte_libretro.js +1 -1
- package/src/cores/wasm/gambatte_libretro.wasm +0 -0
- package/src/cores/wasm/geargrafx_libretro.js +1 -1
- package/src/cores/wasm/geargrafx_libretro.wasm +0 -0
- package/src/cores/wasm/genesis_plus_gx_libretro.js +1 -1
- package/src/cores/wasm/genesis_plus_gx_libretro.wasm +0 -0
- package/src/cores/wasm/handy_libretro.js +1 -1
- package/src/cores/wasm/handy_libretro.wasm +0 -0
- package/src/cores/wasm/mgba_libretro.js +1 -1
- package/src/cores/wasm/mgba_libretro.wasm +0 -0
- package/src/cores/wasm/prosystem_libretro.js +1 -1
- package/src/cores/wasm/prosystem_libretro.wasm +0 -0
- package/src/cores/wasm/snes9x_libretro.js +1 -1
- package/src/cores/wasm/snes9x_libretro.wasm +0 -0
- package/src/cores/wasm/stella2014_libretro.js +1 -1
- package/src/cores/wasm/stella2014_libretro.wasm +0 -0
- package/src/cores/wasm/vice_x64_libretro.js +1 -1
- package/src/cores/wasm/vice_x64_libretro.wasm +0 -0
- package/src/host/LibretroHost.js +245 -10
- package/src/mcp/server.js +6 -0
- package/src/mcp/tools/disasm-rebuild.js +315 -65
- package/src/mcp/tools/disasm.js +149 -28
- package/src/mcp/tools/find-references.js +216 -51
- package/src/mcp/tools/frame.js +11 -4
- package/src/mcp/tools/index.js +15 -1
- package/src/mcp/tools/input.js +26 -3
- package/src/mcp/tools/memory.js +208 -39
- package/src/mcp/tools/playtest.js +56 -4
- package/src/mcp/tools/project.js +35 -9
- package/src/mcp/tools/toolchain.js +43 -10
- package/src/mcp/tools/watch-memory.js +141 -24
- package/src/platforms/_guides/ROMHACKING_PLAYBOOK.md +64 -17
- package/src/platforms/atari2600/MENTAL_MODEL.md +5 -1
- package/src/platforms/atari2600/TROUBLESHOOTING.md +40 -0
- package/src/platforms/atari7800/MENTAL_MODEL.md +27 -6
- package/src/platforms/gb/MENTAL_MODEL.md +16 -1
- package/src/platforms/gb/TROUBLESHOOTING.md +42 -0
- package/src/platforms/gb/lib/c/patch-header.js +7 -4
- package/src/platforms/gbc/MENTAL_MODEL.md +12 -0
- package/src/platforms/gbc/TROUBLESHOOTING.md +21 -0
- package/src/platforms/gbc/lib/c/font.h +43 -0
- package/src/platforms/gbc/lib/c/patch-header.js +7 -4
- package/src/platforms/genesis/MENTAL_MODEL.md +40 -6
- package/src/platforms/genesis/lib/c/genesis_sfx.c +37 -0
- package/src/platforms/genesis/lib/c/genesis_sfx.h +1 -0
- package/src/platforms/gg/TROUBLESHOOTING.md +13 -17
- package/src/platforms/gg/lib/c/gg_crt0.s +14 -2
- package/src/platforms/lynx/lib/c/lynx_sfx.c +38 -2
- package/src/platforms/lynx/lib/c/lynx_sfx.h +1 -0
- package/src/platforms/msx/MENTAL_MODEL.md +6 -0
- package/src/platforms/msx/TROUBLESHOOTING.md +21 -0
- package/src/platforms/msx/lib/c/msx_crt0.s +27 -0
- package/src/platforms/msx/lib/c/msx_hw.h +2 -0
- package/src/platforms/msx/lib/c/msx_vdp.c +45 -0
- package/src/platforms/nes/MENTAL_MODEL.md +10 -3
- package/src/platforms/nes/lib/c/nes_runtime.c +41 -0
- package/src/platforms/nes/lib/c/nes_runtime.h +2 -0
- package/src/platforms/pce/MENTAL_MODEL.md +9 -0
- package/src/platforms/pce/TROUBLESHOOTING.md +9 -0
- package/src/platforms/pce/lib/c/pce_hw.h +2 -1
- package/src/platforms/pce/lib/c/pce_sound.c +22 -0
- package/src/platforms/sms/MENTAL_MODEL.md +5 -0
- package/src/platforms/sms/TROUBLESHOOTING.md +6 -0
- package/src/platforms/sms/lib/c/sms_crt0.s +14 -2
- package/src/platforms/snes/MENTAL_MODEL.md +5 -0
- package/src/playtest/playtest.js +73 -3
- package/src/toolchains/index.js +37 -8
|
@@ -63,6 +63,13 @@ static const u32 tile_blue[8] = {
|
|
|
63
63
|
/* Backdrop tile (colour index 4 = steel grey): a dither so the whole screen
|
|
64
64
|
* reads as a "cabinet" behind the playfield instead of flat black — a lone
|
|
65
65
|
* 6x12 grid floating on black looks blank to a human (frame verify <92%). */
|
|
66
|
+
/* Solid light-grey wall tile for the well border. */
|
|
67
|
+
static const u32 tile_wall[8] = {
|
|
68
|
+
0x55555555, 0x55555555, 0x55555555, 0x55555555,
|
|
69
|
+
0x55555555, 0x55555555, 0x55555555, 0x55555555,
|
|
70
|
+
};
|
|
71
|
+
#define TILE_WALL 5
|
|
72
|
+
|
|
66
73
|
static const u32 tile_back[8] = {
|
|
67
74
|
0x40404040, 0x04040404, 0x40404040, 0x04040404,
|
|
68
75
|
0x40404040, 0x04040404, 0x40404040, 0x04040404,
|
|
@@ -141,26 +148,87 @@ static int collides(int col, int row) {
|
|
|
141
148
|
return 0;
|
|
142
149
|
}
|
|
143
150
|
|
|
151
|
+
/* ── match / clear / gravity core (ported from the GBC reference puzzle).
|
|
152
|
+
* The old scan was horizontal-only AND cleared cells mid-scan, so vertical
|
|
153
|
+
* and diagonal runs never cleared, 4+ runs half-cleared, and nothing ever
|
|
154
|
+
* fell afterwards ("rows don't shift down"). This marks every 3+ run in all
|
|
155
|
+
* 4 directions, clears them, applies per-column gravity, and loops so
|
|
156
|
+
* cascades chain (score scales with chain depth). */
|
|
157
|
+
static u8 matched[ROWS][COLS];
|
|
158
|
+
static const s8 DIRS4[4][2] = { {0,1}, {1,0}, {1,1}, {1,-1} };
|
|
159
|
+
|
|
160
|
+
static u8 mark_and_count(void) {
|
|
161
|
+
u8 r, c, d, len, k, cnt;
|
|
162
|
+
u8 col;
|
|
163
|
+
s8 dr, dc;
|
|
164
|
+
int sr, sc;
|
|
165
|
+
cnt = 0;
|
|
166
|
+
for (r = 0; r < ROWS; r++) for (c = 0; c < COLS; c++) matched[r][c] = 0;
|
|
167
|
+
for (r = 0; r < ROWS; r++) {
|
|
168
|
+
for (c = 0; c < COLS; c++) {
|
|
169
|
+
col = grid[r][c];
|
|
170
|
+
if (col == 0) continue;
|
|
171
|
+
for (d = 0; d < 4; d++) {
|
|
172
|
+
dr = DIRS4[d][0]; dc = DIRS4[d][1];
|
|
173
|
+
sr = (int)r - dr; sc = (int)c - dc;
|
|
174
|
+
if (sr >= 0 && sr < ROWS && sc >= 0 && sc < COLS
|
|
175
|
+
&& grid[sr][sc] == col) continue; /* not the run's start */
|
|
176
|
+
len = 1;
|
|
177
|
+
sr = (int)r + dr; sc = (int)c + dc;
|
|
178
|
+
while (sr >= 0 && sr < ROWS && sc >= 0 && sc < COLS
|
|
179
|
+
&& grid[sr][sc] == col) { len++; sr += dr; sc += dc; }
|
|
180
|
+
if (len >= 3) {
|
|
181
|
+
sr = r; sc = c;
|
|
182
|
+
for (k = 0; k < len; k++) {
|
|
183
|
+
if (!matched[sr][sc]) { matched[sr][sc] = 1; cnt++; }
|
|
184
|
+
sr += dr; sc += dc;
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
return cnt;
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
/* collapse each column so survivors rest on the floor (in place: walk
|
|
194
|
+
* from the bottom, copying gems down to a write cursor, then zero above) */
|
|
195
|
+
static void apply_gravity(void) {
|
|
196
|
+
u8 c;
|
|
197
|
+
int r, w;
|
|
198
|
+
for (c = 0; c < COLS; c++) {
|
|
199
|
+
w = ROWS - 1;
|
|
200
|
+
for (r = ROWS - 1; r >= 0; r--) {
|
|
201
|
+
if (grid[r][c] != 0) { grid[w][c] = grid[r][c]; w--; }
|
|
202
|
+
}
|
|
203
|
+
for (; w >= 0; w--) grid[w][c] = 0;
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
static void resolve_board(void) {
|
|
208
|
+
u8 n, r, c, chain;
|
|
209
|
+
unsigned int amt;
|
|
210
|
+
chain = 0;
|
|
211
|
+
while (1) {
|
|
212
|
+
n = mark_and_count();
|
|
213
|
+
if (n == 0) break;
|
|
214
|
+
chain++;
|
|
215
|
+
for (r = 0; r < ROWS; r++)
|
|
216
|
+
for (c = 0; c < COLS; c++)
|
|
217
|
+
if (matched[r][c]) grid[r][c] = 0;
|
|
218
|
+
amt = (unsigned int)n * 10u;
|
|
219
|
+
if (chain > 1) amt = amt * chain;
|
|
220
|
+
if (score < 65500u) score += amt;
|
|
221
|
+
sfx_tone(1, 1700, 10); /* clear chime */
|
|
222
|
+
apply_gravity();
|
|
223
|
+
}
|
|
224
|
+
}
|
|
225
|
+
|
|
144
226
|
static void lock_piece(void) {
|
|
145
227
|
for (int i = 0; i < 3; i++) {
|
|
146
228
|
int r = piece_y + i;
|
|
147
229
|
if (r >= 0 && r < ROWS) grid[r][piece_x] = piece[i];
|
|
148
230
|
}
|
|
149
|
-
|
|
150
|
-
for (int i = 0; i < 3; i++) {
|
|
151
|
-
int r = piece_y + i;
|
|
152
|
-
if (r < 0 || r >= ROWS) continue;
|
|
153
|
-
for (int c = 0; c <= COLS - 3; c++) {
|
|
154
|
-
u8 a = grid[r][c], b = grid[r][c + 1], d = grid[r][c + 2];
|
|
155
|
-
if (a != 0 && a == b && b == d) {
|
|
156
|
-
grid[r][c] = 0;
|
|
157
|
-
grid[r][c + 1] = 0;
|
|
158
|
-
grid[r][c + 2] = 0;
|
|
159
|
-
if (score < 65500u) score += 30;
|
|
160
|
-
sfx_tone(1, 1700, 10); /* triple-clear chime */
|
|
161
|
-
}
|
|
162
|
-
}
|
|
163
|
-
}
|
|
231
|
+
resolve_board();
|
|
164
232
|
draw_grid();
|
|
165
233
|
}
|
|
166
234
|
|
|
@@ -177,18 +245,28 @@ int main(void) {
|
|
|
177
245
|
pal_bg_mem[2] = CLR_LIME;
|
|
178
246
|
pal_bg_mem[3] = CLR_BLUE;
|
|
179
247
|
pal_bg_mem[4] = RGB15(6, 6, 9); /* steel grey backdrop */
|
|
248
|
+
pal_bg_mem[5] = RGB15(20, 20, 22); /* well border grey */
|
|
180
249
|
|
|
181
250
|
/* BG tile graphics in char-block 3 (separate from TTE which used 2). */
|
|
182
251
|
tonccpy(&tile_mem[3][TILE_RED], tile_red, sizeof(tile_red));
|
|
183
252
|
tonccpy(&tile_mem[3][TILE_GREEN], tile_green, sizeof(tile_green));
|
|
184
253
|
tonccpy(&tile_mem[3][TILE_BLUE], tile_blue, sizeof(tile_blue));
|
|
185
254
|
tonccpy(&tile_mem[3][TILE_BACK], tile_back, sizeof(tile_back));
|
|
255
|
+
tonccpy(&tile_mem[3][TILE_WALL], tile_wall, sizeof(tile_wall));
|
|
186
256
|
|
|
187
257
|
/* Fill screen-block 28 (BG0 map) with the backdrop tile so the whole
|
|
188
258
|
* screen is covered; the grid cells draw over it. (A blank/black map left
|
|
189
259
|
* the playfield floating on black — reads as blank.) */
|
|
190
260
|
SCR_ENTRY *map = se_mem[28];
|
|
191
261
|
for (int i = 0; i < 32 * 32; i++) map[i] = SE_BUILD(TILE_BACK, 0, 0, 0);
|
|
262
|
+
/* Well border — playtest: "needs border around play area". One wall
|
|
263
|
+
* cell left/right of the grid columns + a floor row underneath. */
|
|
264
|
+
for (int r = 0; r <= ROWS; r++) {
|
|
265
|
+
map[(GRID_TY + r) * 32 + (GRID_TX - 1)] = SE_BUILD(TILE_WALL, 0, 0, 0);
|
|
266
|
+
map[(GRID_TY + r) * 32 + (GRID_TX + COLS)] = SE_BUILD(TILE_WALL, 0, 0, 0);
|
|
267
|
+
}
|
|
268
|
+
for (int c = -1; c <= COLS; c++)
|
|
269
|
+
map[(GRID_TY + ROWS) * 32 + (GRID_TX + c)] = SE_BUILD(TILE_WALL, 0, 0, 0);
|
|
192
270
|
|
|
193
271
|
REG_BG0CNT = BG_CBB(3) | BG_SBB(28) | BG_REG_32x32 | BG_4BPP | BG_PRIO(0);
|
|
194
272
|
/* Bump TTE's BG1 to a LOWER priority so the grid (BG0, prio 0) renders
|
|
@@ -100,10 +100,22 @@ static void reset_run(void) {
|
|
|
100
100
|
game_over_timer = 0;
|
|
101
101
|
}
|
|
102
102
|
|
|
103
|
+
/* Galois LFSR (taps $B8), period 255 -- real per-spawn randomness.
|
|
104
|
+
* The old code derived the spawn column from spawn_timer, but the caller
|
|
105
|
+
* resets spawn_timer just before calling here, so it was CONSTANT and
|
|
106
|
+
* every enemy spawned in the same left column/lane. */
|
|
107
|
+
static u8 rng_state = 0xA5;
|
|
108
|
+
static u8 rand8(void) {
|
|
109
|
+
u8 lsb = (u8)(rng_state & 1);
|
|
110
|
+
rng_state >>= 1;
|
|
111
|
+
if (lsb) rng_state ^= 0xB8;
|
|
112
|
+
return rng_state;
|
|
113
|
+
}
|
|
114
|
+
|
|
103
115
|
static void spawn_obstacle(void) {
|
|
104
116
|
for (int i = 0; i < MAX_OBSTACLES; i++) {
|
|
105
117
|
if (!obstacles[i].alive) {
|
|
106
|
-
obstacles[i].x = lane_x[(
|
|
118
|
+
obstacles[i].x = lane_x[rand8() % 3];
|
|
107
119
|
obstacles[i].y = -8;
|
|
108
120
|
obstacles[i].alive = 1;
|
|
109
121
|
return;
|
|
@@ -104,11 +104,23 @@ static void fire_bullet(void) {
|
|
|
104
104
|
}
|
|
105
105
|
}
|
|
106
106
|
|
|
107
|
+
/* Galois LFSR (taps $B8), period 255 -- real per-spawn randomness.
|
|
108
|
+
* The old code derived the spawn column from spawn_timer, but the caller
|
|
109
|
+
* resets spawn_timer just before calling here, so it was CONSTANT and
|
|
110
|
+
* every enemy spawned in the same left column/lane. */
|
|
111
|
+
static u8 rng_state = 0xA5;
|
|
112
|
+
static u8 rand8(void) {
|
|
113
|
+
u8 lsb = (u8)(rng_state & 1);
|
|
114
|
+
rng_state >>= 1;
|
|
115
|
+
if (lsb) rng_state ^= 0xB8;
|
|
116
|
+
return rng_state;
|
|
117
|
+
}
|
|
118
|
+
|
|
107
119
|
static void spawn_enemy(void) {
|
|
108
120
|
for (int i = 0; i < MAX_ENEMIES; i++) {
|
|
109
121
|
if (!enemies[i].alive) {
|
|
110
122
|
/* cheap deterministic x scatter */
|
|
111
|
-
enemies[i].x = (
|
|
123
|
+
enemies[i].x = rand8() % (240 - 16) + 8;
|
|
112
124
|
enemies[i].y = -8;
|
|
113
125
|
enemies[i].alive = 1;
|
|
114
126
|
return;
|
|
@@ -201,11 +201,23 @@ int main(void) {
|
|
|
201
201
|
|
|
202
202
|
oam_copy(oam_mem, obj_buffer, 7);
|
|
203
203
|
|
|
204
|
-
/* Score digits.
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
204
|
+
/* Score digits — via tte_write, NOT tte_printf. tte_printf is
|
|
205
|
+
* broken in this libtonc build (GBA-1): it crashes with an
|
|
206
|
+
* undefined-instruction exception, and since this ran EVERY
|
|
207
|
+
* frame the whole game froze on iteration 1 ("game never
|
|
208
|
+
* starts"). racing/puzzle already avoided it the same way. */
|
|
209
|
+
{
|
|
210
|
+
char sb[12];
|
|
211
|
+
sb[0]='#'; sb[1]='{'; sb[2]='P'; sb[3]=':';
|
|
212
|
+
tte_erase_rect(28, 2, 36, 14);
|
|
213
|
+
sb[4]='2'; sb[5]='8'; sb[6]=','; sb[7]='2'; sb[8]='}';
|
|
214
|
+
sb[9] = (char)('0' + (score_p1 % 10)); sb[10] = 0;
|
|
215
|
+
tte_write(sb);
|
|
216
|
+
tte_erase_rect(220, 2, 228, 14);
|
|
217
|
+
tte_write("#{P:220,2}");
|
|
218
|
+
sb[0] = (char)('0' + (score_p2 % 10)); sb[1] = 0;
|
|
219
|
+
tte_write(sb);
|
|
220
|
+
}
|
|
209
221
|
}
|
|
210
222
|
return 0;
|
|
211
223
|
}
|
|
@@ -119,10 +119,11 @@ void main(void) {
|
|
|
119
119
|
const Rect *p;
|
|
120
120
|
const int16_t GRAVITY = 10;
|
|
121
121
|
const int16_t MOVE = 20;
|
|
122
|
-
const int16_t JUMP = -180
|
|
122
|
+
const int16_t JUMP = -140; /* was -180: ~100px peak (most of the screen) — 'jumps a little too high' */
|
|
123
123
|
const int16_t MAXFALL = 280;
|
|
124
124
|
|
|
125
125
|
lcd_init_default();
|
|
126
|
+
sound_init();
|
|
126
127
|
enable_vblank_irq(); /* MANDATORY: HALT-driven wait_vblank. Without this,
|
|
127
128
|
* busy-poll wait_vblank runs ~1/30 speed on the WASM
|
|
128
129
|
* emulator and the game loop appears to hang. */
|
|
@@ -172,7 +173,10 @@ void main(void) {
|
|
|
172
173
|
if (pad & PAD_RIGHT) vx = MOVE;
|
|
173
174
|
|
|
174
175
|
grounded = on_platform(ipx, ipy);
|
|
175
|
-
if ((pad & PAD_A) && !(prev & PAD_A) && grounded)
|
|
176
|
+
if ((pad & PAD_A) && !(prev & PAD_A) && grounded) {
|
|
177
|
+
vy = JUMP;
|
|
178
|
+
sound_play_tone(1, 1750, 8); /* jump blip (ch2 square) */
|
|
179
|
+
}
|
|
176
180
|
prev = pad;
|
|
177
181
|
|
|
178
182
|
vy += GRAVITY;
|