pi-extensions 0.1.9
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/.ralph/import-cc-codex.md +31 -0
- package/.ralph/import-cc-codex.state.json +14 -0
- package/.ralph/mario-not-impl.md +69 -0
- package/.ralph/mario-not-impl.state.json +14 -0
- package/.ralph/mario-not-spec.md +163 -0
- package/.ralph/mario-not-spec.state.json +14 -0
- package/LICENSE +21 -0
- package/README.md +65 -0
- package/RELEASING.md +34 -0
- package/agent-guidance/CHANGELOG.md +4 -0
- package/agent-guidance/README.md +102 -0
- package/agent-guidance/agent-guidance.ts +147 -0
- package/agent-guidance/package.json +22 -0
- package/agent-guidance/setup.sh +75 -0
- package/agent-guidance/templates/CLAUDE.md +5 -0
- package/agent-guidance/templates/CODEX.md +92 -0
- package/agent-guidance/templates/GEMINI.md +5 -0
- package/arcade/CHANGELOG.md +4 -0
- package/arcade/README.md +85 -0
- package/arcade/assets/picman.png +0 -0
- package/arcade/assets/ping.png +0 -0
- package/arcade/assets/spice-invaders.png +0 -0
- package/arcade/assets/tetris.png +0 -0
- package/arcade/mario-not/README.md +30 -0
- package/arcade/mario-not/boss.js +103 -0
- package/arcade/mario-not/camera.js +59 -0
- package/arcade/mario-not/collision.js +91 -0
- package/arcade/mario-not/colors.js +36 -0
- package/arcade/mario-not/constants.js +97 -0
- package/arcade/mario-not/core.js +39 -0
- package/arcade/mario-not/death.js +77 -0
- package/arcade/mario-not/effects.js +84 -0
- package/arcade/mario-not/enemies.js +31 -0
- package/arcade/mario-not/engine.js +171 -0
- package/arcade/mario-not/fireballs.js +98 -0
- package/arcade/mario-not/items.js +24 -0
- package/arcade/mario-not/levels.js +403 -0
- package/arcade/mario-not/logic.js +104 -0
- package/arcade/mario-not/mario-not.ts +297 -0
- package/arcade/mario-not/player.js +244 -0
- package/arcade/mario-not/render.js +257 -0
- package/arcade/mario-not/spec.md +548 -0
- package/arcade/mario-not/state.js +246 -0
- package/arcade/mario-not/tests/e2e.test.js +855 -0
- package/arcade/mario-not/tests/engine.test.js +888 -0
- package/arcade/mario-not/tests/fixtures/story0-frame.txt +4 -0
- package/arcade/mario-not/tests/fixtures/story1-camera.txt +4 -0
- package/arcade/mario-not/tests/fixtures/story1-glyphs.txt +4 -0
- package/arcade/mario-not/tests/fixtures/story10-item.txt +4 -0
- package/arcade/mario-not/tests/fixtures/story11-hazards.txt +4 -0
- package/arcade/mario-not/tests/fixtures/story12-used-block.txt +4 -0
- package/arcade/mario-not/tests/fixtures/story13-pipes.txt +4 -0
- package/arcade/mario-not/tests/fixtures/story14-goal.txt +4 -0
- package/arcade/mario-not/tests/fixtures/story15-hud-narrow.txt +2 -0
- package/arcade/mario-not/tests/fixtures/story16-unknown-tile.txt +4 -0
- package/arcade/mario-not/tests/fixtures/story17-mix.txt +4 -0
- package/arcade/mario-not/tests/fixtures/story18-hud-score.txt +2 -0
- package/arcade/mario-not/tests/fixtures/story19-cue.txt +4 -0
- package/arcade/mario-not/tests/fixtures/story2-enemy.txt +4 -0
- package/arcade/mario-not/tests/fixtures/story20-camera-offset.txt +4 -0
- package/arcade/mario-not/tests/fixtures/story21-hud-zero.txt +2 -0
- package/arcade/mario-not/tests/fixtures/story22-big-viewport.txt +4 -0
- package/arcade/mario-not/tests/fixtures/story23-camera-negative.txt +4 -0
- package/arcade/mario-not/tests/fixtures/story24-camera-width.txt +4 -0
- package/arcade/mario-not/tests/fixtures/story25-camera-positive.txt +4 -0
- package/arcade/mario-not/tests/fixtures/story26-hud-lives.txt +2 -0
- package/arcade/mario-not/tests/fixtures/story27-hud-coins.txt +2 -0
- package/arcade/mario-not/tests/fixtures/story28-item-viewport.txt +4 -0
- package/arcade/mario-not/tests/fixtures/story29-enemy-viewport.txt +4 -0
- package/arcade/mario-not/tests/fixtures/story3-hud.txt +2 -0
- package/arcade/mario-not/tests/fixtures/story30-hud-score.txt +2 -0
- package/arcade/mario-not/tests/fixtures/story31-particles-viewport.txt +4 -0
- package/arcade/mario-not/tests/fixtures/story32-paused-frame.txt +4 -0
- package/arcade/mario-not/tests/fixtures/story4-big.txt +4 -0
- package/arcade/mario-not/tests/fixtures/story5-resume-hud.txt +2 -0
- package/arcade/mario-not/tests/fixtures/story6-particles.txt +4 -0
- package/arcade/mario-not/tests/fixtures/story6-paused.txt +4 -0
- package/arcade/mario-not/tests/fixtures/story7-powerup.txt +4 -0
- package/arcade/mario-not/tests/fixtures/story8-hud-time.txt +2 -0
- package/arcade/mario-not/tests/fixtures/story9-hud-level.txt +2 -0
- package/arcade/mario-not/tiles.js +79 -0
- package/arcade/mario-not/tsconfig.json +14 -0
- package/arcade/mario-not/types.js +225 -0
- package/arcade/package.json +26 -0
- package/arcade/picman.ts +328 -0
- package/arcade/ping.ts +594 -0
- package/arcade/spice-invaders.ts +1104 -0
- package/arcade/tetris.ts +662 -0
- package/code-actions/CHANGELOG.md +4 -0
- package/code-actions/README.md +65 -0
- package/code-actions/actions.ts +107 -0
- package/code-actions/index.ts +148 -0
- package/code-actions/package.json +22 -0
- package/code-actions/search.ts +79 -0
- package/code-actions/snippets.ts +179 -0
- package/code-actions/ui.ts +120 -0
- package/files-widget/CHANGELOG.md +90 -0
- package/files-widget/DESIGN.md +452 -0
- package/files-widget/README.md +122 -0
- package/files-widget/TODO.md +141 -0
- package/files-widget/browser.ts +922 -0
- package/files-widget/comment.ts +5 -0
- package/files-widget/constants.ts +18 -0
- package/files-widget/demo.svg +1 -0
- package/files-widget/file-tree.ts +224 -0
- package/files-widget/file-viewer.ts +93 -0
- package/files-widget/git.ts +107 -0
- package/files-widget/index.ts +140 -0
- package/files-widget/input-utils.ts +3 -0
- package/files-widget/package.json +22 -0
- package/files-widget/types.ts +28 -0
- package/files-widget/utils.ts +26 -0
- package/files-widget/viewer.ts +424 -0
- package/import-cc-codex/research/import-chats-from-other-agents.md +135 -0
- package/import-cc-codex/spec.md +79 -0
- package/package.json +29 -0
- package/ralph-wiggum/CHANGELOG.md +7 -0
- package/ralph-wiggum/README.md +96 -0
- package/ralph-wiggum/SKILL.md +73 -0
- package/ralph-wiggum/index.ts +792 -0
- package/ralph-wiggum/package.json +25 -0
- package/raw-paste/CHANGELOG.md +7 -0
- package/raw-paste/README.md +52 -0
- package/raw-paste/index.ts +112 -0
- package/raw-paste/package.json +22 -0
- package/tab-status/CHANGELOG.md +4 -0
- package/tab-status/README.md +61 -0
- package/tab-status/assets/tab-status.png +0 -0
- package/tab-status/package.json +22 -0
- package/tab-status/tab-status.ts +179 -0
- package/usage-extension/CHANGELOG.md +17 -0
- package/usage-extension/README.md +120 -0
- package/usage-extension/index.ts +628 -0
- package/usage-extension/package.json +22 -0
- package/usage-extension/screenshot.png +0 -0
|
@@ -0,0 +1,403 @@
|
|
|
1
|
+
// @ts-check
|
|
2
|
+
"use strict";
|
|
3
|
+
|
|
4
|
+
const LEVEL_1_WIDTH = 160;
|
|
5
|
+
const LEVEL_1_HEIGHT = 15;
|
|
6
|
+
|
|
7
|
+
/** @param {number} width @param {number} height @param {string} fill @returns {string[][]} */
|
|
8
|
+
function makeGrid(width, height, fill) {
|
|
9
|
+
return Array.from({ length: height }, () => Array(width).fill(fill));
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
/** @param {string[][]} grid @param {number} x @param {number} y @param {string} tile */
|
|
13
|
+
function setTile(grid, x, y, tile) {
|
|
14
|
+
if (y < 0 || y >= grid.length) return;
|
|
15
|
+
if (x < 0 || x >= grid[0].length) return;
|
|
16
|
+
grid[y][x] = tile;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
/** @param {string[][]} grid @param {number} y @param {number} x0 @param {number} x1 @param {string} tile */
|
|
20
|
+
function fillRow(grid, y, x0, x1, tile) {
|
|
21
|
+
for (let x = x0; x <= x1; x += 1) {
|
|
22
|
+
setTile(grid, x, y, tile);
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
/** @param {string[][]} grid @param {number} x @param {number} baseY @param {number} height */
|
|
27
|
+
function makeStaircase(grid, x, baseY, height) {
|
|
28
|
+
for (let step = 0; step < height; step++) {
|
|
29
|
+
for (let row = 0; row <= step; row++) {
|
|
30
|
+
setTile(grid, x + step, baseY - row, "B");
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
/** @param {string[][]} grid @param {number} x @param {number} topY @param {number} baseY */
|
|
36
|
+
function makeFlagpole(grid, x, topY, baseY) {
|
|
37
|
+
setTile(grid, x, topY, "G");
|
|
38
|
+
for (let y = topY + 1; y <= baseY; y++) {
|
|
39
|
+
setTile(grid, x, y, "F");
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
/** @param {string[][]} grid @param {number} x @param {number} baseY */
|
|
44
|
+
function makePipe(grid, x, baseY) {
|
|
45
|
+
setTile(grid, x, baseY - 1, "T");
|
|
46
|
+
setTile(grid, x + 1, baseY - 1, "T");
|
|
47
|
+
setTile(grid, x, baseY, "P");
|
|
48
|
+
setTile(grid, x + 1, baseY, "P");
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
/** @returns {string[]} */
|
|
52
|
+
function buildLevel1() {
|
|
53
|
+
const grid = makeGrid(LEVEL_1_WIDTH, LEVEL_1_HEIGHT, " ");
|
|
54
|
+
|
|
55
|
+
// Ground baseline.
|
|
56
|
+
fillRow(grid, 14, 0, LEVEL_1_WIDTH - 1, "#");
|
|
57
|
+
|
|
58
|
+
// Segment 1: flat ground, 3-coin line, low brick step.
|
|
59
|
+
setTile(grid, 6, 10, "o");
|
|
60
|
+
setTile(grid, 7, 10, "o");
|
|
61
|
+
setTile(grid, 8, 10, "o");
|
|
62
|
+
setTile(grid, 20, 13, "B");
|
|
63
|
+
setTile(grid, 21, 13, "B");
|
|
64
|
+
setTile(grid, 22, 12, "B");
|
|
65
|
+
|
|
66
|
+
// Segment 2: 4-tile gap, single goomba, question block.
|
|
67
|
+
setTile(grid, 50, 14, " ");
|
|
68
|
+
setTile(grid, 51, 14, " ");
|
|
69
|
+
setTile(grid, 52, 14, " ");
|
|
70
|
+
setTile(grid, 53, 14, " ");
|
|
71
|
+
setTile(grid, 60, 13, "E");
|
|
72
|
+
setTile(grid, 70, 11, "?");
|
|
73
|
+
|
|
74
|
+
// Segment 3: pipe, two goombas, brick stack.
|
|
75
|
+
makePipe(grid, 90, 14);
|
|
76
|
+
setTile(grid, 100, 13, "E");
|
|
77
|
+
setTile(grid, 106, 13, "E");
|
|
78
|
+
setTile(grid, 110, 13, "B");
|
|
79
|
+
setTile(grid, 110, 12, "B");
|
|
80
|
+
setTile(grid, 112, 13, "B");
|
|
81
|
+
|
|
82
|
+
// Segment 4: 2-tile gap, staircase, goal.
|
|
83
|
+
setTile(grid, 130, 14, " ");
|
|
84
|
+
setTile(grid, 131, 14, " ");
|
|
85
|
+
makeStaircase(grid, 140, 13, 4);
|
|
86
|
+
makeFlagpole(grid, 149, 5, 13);
|
|
87
|
+
|
|
88
|
+
return grid.map((row) => row.join(""));
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
/** @returns {string[]} */
|
|
92
|
+
function buildLevel2() {
|
|
93
|
+
const grid = makeGrid(LEVEL_1_WIDTH, LEVEL_1_HEIGHT, " ");
|
|
94
|
+
|
|
95
|
+
// Ground baseline with more gaps.
|
|
96
|
+
fillRow(grid, 14, 0, 45, "#");
|
|
97
|
+
fillRow(grid, 14, 50, 85, "#");
|
|
98
|
+
fillRow(grid, 14, 90, LEVEL_1_WIDTH - 1, "#");
|
|
99
|
+
|
|
100
|
+
// Segment 1: elevated platform, coins above.
|
|
101
|
+
setTile(grid, 8, 11, "B");
|
|
102
|
+
setTile(grid, 9, 11, "B");
|
|
103
|
+
setTile(grid, 10, 11, "B");
|
|
104
|
+
setTile(grid, 11, 11, "B");
|
|
105
|
+
setTile(grid, 9, 8, "o");
|
|
106
|
+
setTile(grid, 10, 8, "o");
|
|
107
|
+
|
|
108
|
+
// Segment 2: pipe with goomba behind, question block.
|
|
109
|
+
makePipe(grid, 25, 14);
|
|
110
|
+
setTile(grid, 30, 13, "E");
|
|
111
|
+
setTile(grid, 35, 10, "?");
|
|
112
|
+
|
|
113
|
+
// Large gap (50-90 is ground, so 46-49 is gap) - need platforms.
|
|
114
|
+
setTile(grid, 47, 12, "B");
|
|
115
|
+
setTile(grid, 48, 12, "B");
|
|
116
|
+
|
|
117
|
+
// Segment 3: brick staircase up then down.
|
|
118
|
+
setTile(grid, 55, 13, "B");
|
|
119
|
+
setTile(grid, 56, 13, "B");
|
|
120
|
+
setTile(grid, 56, 12, "B");
|
|
121
|
+
setTile(grid, 57, 13, "B");
|
|
122
|
+
setTile(grid, 57, 12, "B");
|
|
123
|
+
setTile(grid, 57, 11, "B");
|
|
124
|
+
setTile(grid, 58, 13, "B");
|
|
125
|
+
setTile(grid, 58, 12, "B");
|
|
126
|
+
setTile(grid, 59, 13, "B");
|
|
127
|
+
// Coins on top
|
|
128
|
+
setTile(grid, 56, 9, "o");
|
|
129
|
+
setTile(grid, 57, 8, "o");
|
|
130
|
+
setTile(grid, 58, 9, "o");
|
|
131
|
+
|
|
132
|
+
// Multiple goombas patrolling.
|
|
133
|
+
setTile(grid, 65, 13, "E");
|
|
134
|
+
setTile(grid, 70, 13, "E");
|
|
135
|
+
setTile(grid, 75, 13, "E");
|
|
136
|
+
|
|
137
|
+
// Another question block.
|
|
138
|
+
setTile(grid, 80, 10, "?");
|
|
139
|
+
|
|
140
|
+
// Gap at 86-89.
|
|
141
|
+
setTile(grid, 86, 14, " ");
|
|
142
|
+
setTile(grid, 87, 14, " ");
|
|
143
|
+
setTile(grid, 88, 14, " ");
|
|
144
|
+
setTile(grid, 89, 14, " ");
|
|
145
|
+
|
|
146
|
+
// Final staircase to flag.
|
|
147
|
+
makeStaircase(grid, 100, 13, 5);
|
|
148
|
+
makeFlagpole(grid, 110, 4, 13);
|
|
149
|
+
|
|
150
|
+
return grid.map((row) => row.join(""));
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
/** @returns {string[]} */
|
|
154
|
+
function buildLevel3() {
|
|
155
|
+
const grid = makeGrid(LEVEL_1_WIDTH, LEVEL_1_HEIGHT, " ");
|
|
156
|
+
|
|
157
|
+
// Sparse ground with many gaps - this is the hard level!
|
|
158
|
+
fillRow(grid, 14, 0, 12, "#");
|
|
159
|
+
fillRow(grid, 14, 18, 30, "#");
|
|
160
|
+
fillRow(grid, 14, 38, 50, "#");
|
|
161
|
+
fillRow(grid, 14, 58, 70, "#");
|
|
162
|
+
fillRow(grid, 14, 80, 95, "#");
|
|
163
|
+
fillRow(grid, 14, 105, 120, "#");
|
|
164
|
+
fillRow(grid, 14, 130, LEVEL_1_WIDTH - 1, "#");
|
|
165
|
+
|
|
166
|
+
// Segment 1: spike pit with floating platforms above
|
|
167
|
+
fillRow(grid, 14, 13, 17, "^"); // spikes in first gap
|
|
168
|
+
setTile(grid, 14, 11, "B"); // floating platform
|
|
169
|
+
setTile(grid, 15, 11, "B");
|
|
170
|
+
setTile(grid, 16, 9, "B"); // higher platform
|
|
171
|
+
setTile(grid, 10, 13, "E"); // goomba before gap
|
|
172
|
+
|
|
173
|
+
// Coins reward for taking upper path
|
|
174
|
+
setTile(grid, 14, 8, "o");
|
|
175
|
+
setTile(grid, 15, 8, "o");
|
|
176
|
+
setTile(grid, 16, 6, "o");
|
|
177
|
+
|
|
178
|
+
// Segment 2: enemy gauntlet
|
|
179
|
+
setTile(grid, 20, 13, "E");
|
|
180
|
+
setTile(grid, 23, 13, "E");
|
|
181
|
+
setTile(grid, 26, 13, "E");
|
|
182
|
+
setTile(grid, 29, 13, "E");
|
|
183
|
+
setTile(grid, 25, 10, "?"); // power-up to help
|
|
184
|
+
|
|
185
|
+
// Gap with water (instant death)
|
|
186
|
+
fillRow(grid, 14, 31, 37, "~");
|
|
187
|
+
setTile(grid, 33, 11, "B"); // small platform in middle
|
|
188
|
+
setTile(grid, 34, 11, "B");
|
|
189
|
+
setTile(grid, 33, 8, "o");
|
|
190
|
+
setTile(grid, 34, 8, "o");
|
|
191
|
+
|
|
192
|
+
// Segment 3: vertical climb with enemies
|
|
193
|
+
setTile(grid, 40, 13, "B");
|
|
194
|
+
setTile(grid, 40, 12, "B");
|
|
195
|
+
setTile(grid, 42, 11, "B");
|
|
196
|
+
setTile(grid, 42, 10, "B");
|
|
197
|
+
setTile(grid, 44, 9, "B");
|
|
198
|
+
setTile(grid, 44, 8, "B");
|
|
199
|
+
setTile(grid, 46, 7, "B");
|
|
200
|
+
setTile(grid, 47, 7, "B");
|
|
201
|
+
// Coins at the top
|
|
202
|
+
setTile(grid, 46, 4, "o");
|
|
203
|
+
setTile(grid, 47, 4, "o");
|
|
204
|
+
setTile(grid, 48, 13, "E");
|
|
205
|
+
|
|
206
|
+
// Spike gap
|
|
207
|
+
fillRow(grid, 14, 51, 57, "^");
|
|
208
|
+
setTile(grid, 53, 10, "B");
|
|
209
|
+
setTile(grid, 54, 10, "B");
|
|
210
|
+
setTile(grid, 55, 10, "B");
|
|
211
|
+
|
|
212
|
+
// Segment 4: pipe maze with goombas
|
|
213
|
+
makePipe(grid, 60, 14);
|
|
214
|
+
setTile(grid, 64, 13, "E");
|
|
215
|
+
makePipe(grid, 66, 14);
|
|
216
|
+
setTile(grid, 68, 13, "E");
|
|
217
|
+
setTile(grid, 63, 10, "?");
|
|
218
|
+
|
|
219
|
+
// Long water gap - need precise jumping
|
|
220
|
+
fillRow(grid, 14, 71, 79, "~");
|
|
221
|
+
setTile(grid, 73, 11, "B");
|
|
222
|
+
setTile(grid, 76, 12, "B");
|
|
223
|
+
setTile(grid, 77, 12, "B");
|
|
224
|
+
|
|
225
|
+
// Segment 5: descending platforms with enemies
|
|
226
|
+
setTile(grid, 82, 10, "B");
|
|
227
|
+
setTile(grid, 83, 10, "B");
|
|
228
|
+
setTile(grid, 84, 10, "E");
|
|
229
|
+
setTile(grid, 86, 11, "B");
|
|
230
|
+
setTile(grid, 87, 11, "B");
|
|
231
|
+
setTile(grid, 89, 12, "B");
|
|
232
|
+
setTile(grid, 90, 12, "B");
|
|
233
|
+
setTile(grid, 91, 13, "E");
|
|
234
|
+
setTile(grid, 93, 13, "E");
|
|
235
|
+
|
|
236
|
+
// Another spike gap
|
|
237
|
+
fillRow(grid, 14, 96, 104, "^");
|
|
238
|
+
setTile(grid, 98, 10, "B");
|
|
239
|
+
setTile(grid, 99, 10, "B");
|
|
240
|
+
setTile(grid, 101, 11, "B");
|
|
241
|
+
setTile(grid, 102, 11, "B");
|
|
242
|
+
|
|
243
|
+
// Segment 6: final gauntlet
|
|
244
|
+
setTile(grid, 107, 13, "E");
|
|
245
|
+
setTile(grid, 110, 13, "E");
|
|
246
|
+
setTile(grid, 113, 13, "E");
|
|
247
|
+
setTile(grid, 116, 13, "E");
|
|
248
|
+
setTile(grid, 112, 10, "?"); // last power-up
|
|
249
|
+
|
|
250
|
+
// Water before final stretch
|
|
251
|
+
fillRow(grid, 14, 121, 129, "~");
|
|
252
|
+
setTile(grid, 123, 11, "B");
|
|
253
|
+
setTile(grid, 124, 11, "B");
|
|
254
|
+
setTile(grid, 126, 10, "B");
|
|
255
|
+
setTile(grid, 127, 10, "B");
|
|
256
|
+
|
|
257
|
+
// Final staircase - extra tall
|
|
258
|
+
makeStaircase(grid, 135, 13, 6);
|
|
259
|
+
makeFlagpole(grid, 146, 3, 13);
|
|
260
|
+
|
|
261
|
+
return grid.map((row) => row.join(""));
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
/** @returns {string[]} */
|
|
265
|
+
function buildLevel4() {
|
|
266
|
+
const grid = makeGrid(LEVEL_1_WIDTH, LEVEL_1_HEIGHT, " ");
|
|
267
|
+
|
|
268
|
+
// This is Bowser's Castle - lava floor, fireballs, and a boss fight!
|
|
269
|
+
|
|
270
|
+
// Lava baseline - deadly floor throughout most of the level
|
|
271
|
+
fillRow(grid, 14, 0, 20, "L");
|
|
272
|
+
fillRow(grid, 14, 25, 60, "L");
|
|
273
|
+
fillRow(grid, 14, 65, 100, "L");
|
|
274
|
+
fillRow(grid, 14, 105, 130, "L");
|
|
275
|
+
|
|
276
|
+
// Safe ground sections
|
|
277
|
+
fillRow(grid, 14, 0, 8, "C"); // Starting platform
|
|
278
|
+
fillRow(grid, 14, 131, LEVEL_1_WIDTH - 1, "C"); // Boss arena
|
|
279
|
+
|
|
280
|
+
// Segment 1: Opening gauntlet with floating platforms over lava
|
|
281
|
+
setTile(grid, 10, 12, "C");
|
|
282
|
+
setTile(grid, 11, 12, "C");
|
|
283
|
+
setTile(grid, 14, 10, "C");
|
|
284
|
+
setTile(grid, 15, 10, "C");
|
|
285
|
+
setTile(grid, 18, 11, "C");
|
|
286
|
+
setTile(grid, 19, 11, "C");
|
|
287
|
+
|
|
288
|
+
// Coins to guide the way
|
|
289
|
+
setTile(grid, 10, 9, "o");
|
|
290
|
+
setTile(grid, 14, 7, "o");
|
|
291
|
+
|
|
292
|
+
// Segment 2: Bridge section with gaps and goombas
|
|
293
|
+
fillRow(grid, 11, 22, 35, "C"); // Bridge
|
|
294
|
+
setTile(grid, 26, 11, " "); // Gap in bridge
|
|
295
|
+
setTile(grid, 27, 11, " ");
|
|
296
|
+
setTile(grid, 30, 10, "E"); // Goomba on bridge
|
|
297
|
+
setTile(grid, 33, 10, "E");
|
|
298
|
+
|
|
299
|
+
// Fireball spawner shooting across the bridge
|
|
300
|
+
setTile(grid, 22, 9, ">");
|
|
301
|
+
|
|
302
|
+
// Segment 3: Vertical climb section
|
|
303
|
+
setTile(grid, 38, 12, "C");
|
|
304
|
+
setTile(grid, 39, 12, "C");
|
|
305
|
+
setTile(grid, 41, 10, "C");
|
|
306
|
+
setTile(grid, 42, 10, "C");
|
|
307
|
+
setTile(grid, 44, 8, "C");
|
|
308
|
+
setTile(grid, 45, 8, "C");
|
|
309
|
+
setTile(grid, 47, 6, "C");
|
|
310
|
+
setTile(grid, 48, 6, "C");
|
|
311
|
+
setTile(grid, 50, 8, "C");
|
|
312
|
+
setTile(grid, 51, 8, "C");
|
|
313
|
+
setTile(grid, 53, 10, "C");
|
|
314
|
+
setTile(grid, 54, 10, "C");
|
|
315
|
+
setTile(grid, 56, 12, "C");
|
|
316
|
+
setTile(grid, 57, 12, "C");
|
|
317
|
+
|
|
318
|
+
// Power-up for the climb
|
|
319
|
+
setTile(grid, 47, 4, "?");
|
|
320
|
+
|
|
321
|
+
// Segment 4: Descending platforms
|
|
322
|
+
fillRow(grid, 10, 60, 64, "C");
|
|
323
|
+
|
|
324
|
+
setTile(grid, 67, 11, "C");
|
|
325
|
+
setTile(grid, 68, 11, "C");
|
|
326
|
+
setTile(grid, 71, 12, "C");
|
|
327
|
+
setTile(grid, 72, 12, "C");
|
|
328
|
+
setTile(grid, 75, 11, "C");
|
|
329
|
+
setTile(grid, 76, 11, "C");
|
|
330
|
+
|
|
331
|
+
// Goombas on the descent
|
|
332
|
+
setTile(grid, 75, 10, "E");
|
|
333
|
+
|
|
334
|
+
// Segment 5: Lava corridor with tight jumps
|
|
335
|
+
setTile(grid, 80, 10, "C");
|
|
336
|
+
setTile(grid, 83, 11, "C");
|
|
337
|
+
setTile(grid, 86, 10, "C");
|
|
338
|
+
setTile(grid, 89, 11, "C");
|
|
339
|
+
setTile(grid, 92, 10, "C");
|
|
340
|
+
setTile(grid, 95, 11, "C");
|
|
341
|
+
setTile(grid, 98, 10, "C");
|
|
342
|
+
|
|
343
|
+
// Fireball shooting through the corridor
|
|
344
|
+
setTile(grid, 84, 9, ">");
|
|
345
|
+
|
|
346
|
+
// Segment 6: Final approach to boss
|
|
347
|
+
fillRow(grid, 12, 102, 110, "C");
|
|
348
|
+
setTile(grid, 105, 11, "E");
|
|
349
|
+
setTile(grid, 108, 11, "E");
|
|
350
|
+
|
|
351
|
+
// Power-up before boss
|
|
352
|
+
setTile(grid, 106, 9, "?");
|
|
353
|
+
|
|
354
|
+
// Bridge to boss arena
|
|
355
|
+
fillRow(grid, 12, 112, 130, "C");
|
|
356
|
+
setTile(grid, 118, 12, " "); // Gap
|
|
357
|
+
setTile(grid, 119, 12, " ");
|
|
358
|
+
setTile(grid, 124, 12, " "); // Gap
|
|
359
|
+
setTile(grid, 125, 12, " ");
|
|
360
|
+
|
|
361
|
+
// Segment 7: Boss Arena
|
|
362
|
+
// Solid floor for boss fight
|
|
363
|
+
fillRow(grid, 13, 131, LEVEL_1_WIDTH - 5, "C");
|
|
364
|
+
|
|
365
|
+
// Walls to contain the fight
|
|
366
|
+
for (let y = 8; y <= 13; y++) {
|
|
367
|
+
setTile(grid, 131, y, "C");
|
|
368
|
+
}
|
|
369
|
+
for (let y = 8; y <= 13; y++) {
|
|
370
|
+
setTile(grid, LEVEL_1_WIDTH - 5, y, "C");
|
|
371
|
+
}
|
|
372
|
+
|
|
373
|
+
// Bowser!
|
|
374
|
+
setTile(grid, 145, 12, "W");
|
|
375
|
+
|
|
376
|
+
// Fireball spawner in arena
|
|
377
|
+
setTile(grid, 150, 10, "<");
|
|
378
|
+
|
|
379
|
+
// Coins as a reward for entering
|
|
380
|
+
setTile(grid, 138, 10, "o");
|
|
381
|
+
setTile(grid, 140, 10, "o");
|
|
382
|
+
setTile(grid, 142, 10, "o");
|
|
383
|
+
|
|
384
|
+
return grid.map((row) => row.join(""));
|
|
385
|
+
}
|
|
386
|
+
|
|
387
|
+
const LEVEL_1_LINES = buildLevel1();
|
|
388
|
+
const LEVEL_2_LINES = buildLevel2();
|
|
389
|
+
const LEVEL_3_LINES = buildLevel3();
|
|
390
|
+
const LEVEL_4_LINES = buildLevel4();
|
|
391
|
+
|
|
392
|
+
/** @type {string[][]} */
|
|
393
|
+
const ALL_LEVELS = [LEVEL_1_LINES, LEVEL_2_LINES, LEVEL_3_LINES, LEVEL_4_LINES];
|
|
394
|
+
|
|
395
|
+
module.exports = {
|
|
396
|
+
LEVEL_1_LINES,
|
|
397
|
+
LEVEL_2_LINES,
|
|
398
|
+
LEVEL_3_LINES,
|
|
399
|
+
LEVEL_4_LINES,
|
|
400
|
+
ALL_LEVELS,
|
|
401
|
+
LEVEL_1_WIDTH,
|
|
402
|
+
LEVEL_1_HEIGHT,
|
|
403
|
+
};
|
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
// @ts-check
|
|
2
|
+
"use strict";
|
|
3
|
+
|
|
4
|
+
const { updateCamera } = require("./camera.js");
|
|
5
|
+
const { updateParticles, setCue, updateCue } = require("./effects.js");
|
|
6
|
+
const { updateEnemies } = require("./enemies.js");
|
|
7
|
+
const { updateItems } = require("./items.js");
|
|
8
|
+
const { stepDeath } = require("./death.js");
|
|
9
|
+
const { updateFireballSpawners, updateFireballs, checkFireballCollision } = require("./fireballs.js");
|
|
10
|
+
const { updateBoss, resolveBossCollision } = require("./boss.js");
|
|
11
|
+
const {
|
|
12
|
+
applyPlayerDamage,
|
|
13
|
+
checkGoal,
|
|
14
|
+
checkHazard,
|
|
15
|
+
collectCoin,
|
|
16
|
+
collectItems,
|
|
17
|
+
resolveEnemyCollisions,
|
|
18
|
+
stepPlayerMovement,
|
|
19
|
+
} = require("./player.js");
|
|
20
|
+
const { GAME_MODES } = require("./constants.js");
|
|
21
|
+
|
|
22
|
+
/** @typedef {import("./types").GameState} GameState */
|
|
23
|
+
/** @typedef {import("./types").InputState} InputState */
|
|
24
|
+
|
|
25
|
+
/** @param {GameState} state @param {InputState} [input] @returns {GameState} */
|
|
26
|
+
function stepGame(state, input) {
|
|
27
|
+
const dt = state.config.dt;
|
|
28
|
+
if (state.mode === GAME_MODES.dead) {
|
|
29
|
+
return stepDeath(state, dt);
|
|
30
|
+
}
|
|
31
|
+
if (state.mode !== GAME_MODES.playing) {
|
|
32
|
+
state.tick += 1;
|
|
33
|
+
return state;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
const { prevY, height } = stepPlayerMovement(state, input);
|
|
37
|
+
updateEnemies(state);
|
|
38
|
+
updateItems(state);
|
|
39
|
+
updateFireballSpawners(state);
|
|
40
|
+
updateFireballs(state);
|
|
41
|
+
updateBoss(state);
|
|
42
|
+
|
|
43
|
+
resolveEnemyCollisions(state, prevY);
|
|
44
|
+
|
|
45
|
+
// Check boss collision
|
|
46
|
+
if (state.boss && state.boss.alive) {
|
|
47
|
+
const bossHurt = resolveBossCollision(state, prevY);
|
|
48
|
+
if (bossHurt) {
|
|
49
|
+
applyPlayerDamage(state);
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
// Check fireball collision
|
|
54
|
+
if (checkFireballCollision(state)) {
|
|
55
|
+
applyPlayerDamage(state);
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
collectCoin(state);
|
|
59
|
+
collectItems(state);
|
|
60
|
+
|
|
61
|
+
// Only check goal if no boss or boss is defeated
|
|
62
|
+
if (!state.boss || !state.boss.alive) {
|
|
63
|
+
checkGoal(state, height);
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
if (state.mode === GAME_MODES.playing) {
|
|
67
|
+
// Check hazards
|
|
68
|
+
if (checkHazard(state)) {
|
|
69
|
+
applyPlayerDamage(state);
|
|
70
|
+
} else if (state.player.y >= state.level.height) {
|
|
71
|
+
applyPlayerDamage(state, true);
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
// Update world (only if still playing after hazard check)
|
|
75
|
+
if (state.mode === GAME_MODES.playing) {
|
|
76
|
+
updateCamera(state);
|
|
77
|
+
updateParticles(state, dt);
|
|
78
|
+
updateCue(state, dt);
|
|
79
|
+
state.time = Math.max(0, state.time - dt);
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
state.tick += 1;
|
|
84
|
+
return state;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
/** @param {GameState} state @param {boolean} paused */
|
|
88
|
+
function setPaused(state, paused) {
|
|
89
|
+
if (state.mode === GAME_MODES.dead || state.mode === GAME_MODES.gameOver || state.mode === GAME_MODES.levelClear) {
|
|
90
|
+
return state;
|
|
91
|
+
}
|
|
92
|
+
state.mode = paused ? GAME_MODES.paused : GAME_MODES.playing;
|
|
93
|
+
if (paused) {
|
|
94
|
+
setCue(state, "PAUSED", 0, true);
|
|
95
|
+
} else if (state.cue && state.cue.persist) {
|
|
96
|
+
state.cue = null;
|
|
97
|
+
}
|
|
98
|
+
return state;
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
module.exports = {
|
|
102
|
+
stepGame,
|
|
103
|
+
setPaused,
|
|
104
|
+
};
|