diff --git a/README.md b/README.md index 94fa1ab..52c2439 100644 --- a/README.md +++ b/README.md @@ -53,8 +53,8 @@ no code changes needed.** | `bus_wheel` | `assets/sprites/bus_wheel.png` | 52x52 | used for both wheels | | `kid_idle` | `assets/sprites/kid_idle.png` | 28x28 | passenger while aboard | | `kid_ejected` | `assets/sprites/kid_ejected.png` | 28x28 | passenger after being thrown off | -| `smash_items` | `assets/sprites/smash-items.png` | 64x64 x 6 frames | 1x6 spritesheet: intact/smashed TV, chair, cone (see Smashables below) | -| `bonus_items` | `assets/sprites/bonus-items.png` | 64x64 x 12 frames | 1x12 spritesheet: CTC box + 2 firework objects, guitar + 2, cash + 2, flour + 2 (see Bonus Items below) | +| `smash_items` | `assets/sprites/smash-items.png` | 64x64 x 12 frames | 8x2 spritesheet: intact/smashed TV, chair, cone, trash can, tent, cat (see Smashables below) | +| `bonus_items` | `assets/sprites/bonus-items.png` | 64x64 x 18 frames | 9x2 spritesheet: CTC box + 2 firework objects, guitar + 2, cash + 2, flour + 2, spray paint + 2, backpack + 2 (see Bonus Items below) | | `bg_far` | `assets/backgrounds/bg_far.png` | 256x540 | tiled, slowest parallax layer | | `bg_mid` | `assets/backgrounds/bg_mid.png` | 256x540 | tiled, mid parallax layer | | `bg_near` | `assets/backgrounds/bg_near.png` | 256x540 | tiled, fastest parallax layer | @@ -80,17 +80,21 @@ Everything worth tweaking lives in `src/config.js`, notably: Set `DEBUG = true` in `config.js` to show a live g-force readout and Matter's physics debug overlay in `PlayScene`. -## Smashables (TV / chair / cone) +## Smashables (TV / chair / cone / trash can / tent / cat) -`assets/sprites/smash-items.png` is a 1x6 spritesheet (TV, smashed TV, chair, -smashed chair, cone, smashed cone), loaded as the `smash_items` texture +`assets/sprites/smash-items.png` is an 8x2 spritesheet (TV, smashed TV, chair, +smashed chair, cone, smashed cone, trash can, smashed trash can, tent, smashed +tent, cat, smashed cat), loaded as the `smash_items` texture (64x64 per frame). Any level can carry an optional `items` array (see the editor's export for the exact shape): ```js items: [ - { type: 'tv', mode: 'float', x: 2500, y: 660 }, - { type: 'chair', mode: 'gravity', x: 3500, y: 760 }, + { type: 'tv', mode: 'float', x: 2500, y: 660 }, + { type: 'chair', mode: 'gravity', x: 3500, y: 760 }, + { type: 'trashcan', mode: 'float', x: 4500, y: 640 }, + { type: 'tent', mode: 'float', x: 5500, y: 620 }, + { type: 'cat', mode: 'gravity', x: 6500, y: 780 }, ] ``` @@ -107,8 +111,8 @@ Smashed items also tally as +`SCORE.perSmashItem` (50) each on the end-of-level score screen. The **level editor** (`editor.html`) has a "Placeables" section with a kind -toggle: **Smashables** (TV / chair / cone, float or gravity mode) and -**Bonus items** (CTC box / guitar / cash / flour, float-only). Pick the kind + type, +toggle: **Smashables** (TV / chair / cone / trash can / tent / cat, float or +gravity mode) and **Bonus items** (CTC box / guitar / cash / flour, float-only). Pick the kind + type, then click anywhere on the preview map to drop one there; click an existing item (on the map or in the list) to remove it. Exporting the level writes both the `items` and `bonusItems` arrays into the level file automatically. @@ -123,11 +127,12 @@ description / kids) and both placeable types stay editable, so you can tweak them and re-export. "Start a new level" clears the import and unlocks the section controls again. -## Bonus Items (CTC box / guitar / cash / flour) +## Bonus Items (CTC box / guitar / cash / flour / spray paint / backpack) -`assets/sprites/bonus-items.png` is a 1x12 spritesheet (CTC box, 2 firework +`assets/sprites/bonus-items.png` is a 9x2 spritesheet (CTC box, 2 firework frames, guitar, 2 firework frames, cash, 2 firework frames, flour, 2 firework -frames - 64x64 per frame), loaded as the `bonus_items` texture. Any level can carry an optional +frames, spray paint, 2 firework frames, backpack, 2 firework frames - 64x64 per +frame), loaded as the `bonus_items` texture. Any level can carry an optional `bonusItems` array (see the editor's export for the exact shape): ```js @@ -155,11 +160,13 @@ kids (same trick as `SmashItemManager._smash`) so it can never bump the bus or flick a kid out of the compartment. It does still collide with terrain so it lands where it lands. Particles are tinted per the bonus type's own color scheme (`BONUS_ITEM.colors`): CTC = warm toast-brown/cinnamon, guitar -= bright/deep green, cash = muted sage/olive-grey, flour = warm off-white/tan. += bright/deep green, cash = muted sage/olive-grey, flour = warm off-white/tan, +spray paint = pale red/blue, backpack = tan/brown. Some types override the burst's look entirely via `BONUS_ITEM.fireworkVariants`: flour's puffs launch 2-3x the standard particle size at half opacity (a soft cloud of dust, still with the -standard-sized physics bodies underneath). +standard-sized physics bodies underneath), and spray paint's puffs keep the +standard size but run at half opacity (a semi-transparent spray mist). Collected bonus items tally as `SCORE.perBonusItem` (100) each on the end-of-level score screen, revealed right after the smashed-props line. diff --git a/assets/fx/bonus-backpack.mp3 b/assets/fx/bonus-backpack.mp3 new file mode 100644 index 0000000..17aafa4 Binary files /dev/null and b/assets/fx/bonus-backpack.mp3 differ diff --git a/assets/fx/bonus-spraypaint.mp3 b/assets/fx/bonus-spraypaint.mp3 new file mode 100644 index 0000000..91de19d Binary files /dev/null and b/assets/fx/bonus-spraypaint.mp3 differ diff --git a/assets/fx/smash-cat.mp3 b/assets/fx/smash-cat.mp3 new file mode 100644 index 0000000..3bc16f6 Binary files /dev/null and b/assets/fx/smash-cat.mp3 differ diff --git a/assets/fx/smash-tent.mp3 b/assets/fx/smash-tent.mp3 new file mode 100644 index 0000000..3ff024e Binary files /dev/null and b/assets/fx/smash-tent.mp3 differ diff --git a/assets/fx/smash-trashcan.mp3 b/assets/fx/smash-trashcan.mp3 new file mode 100644 index 0000000..8cf70da Binary files /dev/null and b/assets/fx/smash-trashcan.mp3 differ diff --git a/assets/sprites/bonus-items.png b/assets/sprites/bonus-items.png index 8c9ed0b..1f6e1f8 100644 Binary files a/assets/sprites/bonus-items.png and b/assets/sprites/bonus-items.png differ diff --git a/assets/sprites/bonus-items.psd b/assets/sprites/bonus-items.psd index 3a04124..8ad9814 100644 Binary files a/assets/sprites/bonus-items.psd and b/assets/sprites/bonus-items.psd differ diff --git a/assets/sprites/smash-items.png b/assets/sprites/smash-items.png index 7cdea0c..533b97e 100644 Binary files a/assets/sprites/smash-items.png and b/assets/sprites/smash-items.png differ diff --git a/assets/sprites/smash-items.psd b/assets/sprites/smash-items.psd index c15fe61..b798222 100644 Binary files a/assets/sprites/smash-items.psd and b/assets/sprites/smash-items.psd differ diff --git a/src/config.js b/src/config.js index c9019ef..a5e9aa0 100644 --- a/src/config.js +++ b/src/config.js @@ -216,16 +216,18 @@ export const SCORE = { perKidSaved: 100, perKidLost: -50, perSecondRemaining: 10, - // Per smashable item (TV/chair/cone, see SMASH_ITEM) knocked off its - // perch by the bus - revealed on the tally between the kids and the clock. + // Per smashable item (TV/chair/cone/trash can/tent/cat, see SMASH_ITEM) + // knocked off its perch by the bus - revealed on the tally between the + // kids and the clock. perSmashItem: 50, // Per bonus item (CTC box / guitar / cash - see BONUS_ITEM) the bus plows // through - revealed on the tally right after the smashed-props line. perBonusItem: 100, }; -// Smashable items (assets/sprites/smash-items.png - 6 frames of 64x64, one -// intact + one smashed variant per prop, see src/entities/SmashItemManager.js +// Smashable items (assets/sprites/smash-items.png - 12 frames of 64x64 in +// an 8-wide x 2-row grid, one intact + one smashed variant per prop: +// TV, chair, cone, trash can, tent, cat - see src/entities/SmashItemManager.js // for the type/frame table). Placed per-level via the editor ("Smashables" // section) and exported into the level's `items` array. // @@ -317,15 +319,17 @@ export const BONUS_ITEM = { particleDisplaySize: 40 * WORLD_SCALE, // each firework particle is shown square at this size (80 world px, half the bonus item's own 128) // Optional per-type firework overrides (defaults: plain particleDisplaySize, - // full opacity). Flour's dust puffs are the only type that uses one: each - // firework sprite is shown 2-3x the normal particle size at ~half opacity, - // so a hit reads as a soft cloud of dust billowing over the bus instead of - // a confetti pop. Read per item in BonusItemManager._explode (variant = + // full opacity). Two types use one: flour's dust puffs are shown 2-3x the + // normal particle size at ~half opacity, so a hit reads as a soft cloud of + // dust billowing over the bus instead of a confetti pop; spraypaint's puffs + // keep the standard size but go half-opacity, reading as a semi-transparent + // spray mist. Read per item in BonusItemManager._explode (variant = // BONUS_ITEM.fireworkVariants[item.spec.type] || {}). NOTE: this is purely // visual - the particles' physics bodies stay at the standard size (below), - // so the bigger sprites don't change how they settle on the terrain. + // so the bigger/transparent sprites don't change how they settle on the terrain. fireworkVariants: { flour: { scaleMin: 2, scaleMax: 3, alpha: 0.5 }, + spraypaint: { alpha: 0.5 }, }, // Tint per bonus type, matched to each item's art: the firework sprites @@ -339,6 +343,8 @@ export const BONUS_ITEM = { guitar: { main: 0x50c050, accent: 0x105018 }, // bright green / deep forest cash: { main: 0xaab888, accent: 0x404030 }, // muted sage / olive-grey (cash's own scheme is near-white grey-green) flour: { main: 0xf4f1e8, accent: 0xcbb58e }, // warm off-white / tan (flour's own scheme is pale beige) + spraypaint: { main: 0xe8b0a0, accent: 0x4058b8 }, // pale red / blue (its two firework frames are red and blue) + backpack: { main: 0xd8c0a0, accent: 0x705030 }, // pale tan / dark brown (backpack's own scheme is warm brown) }, }; diff --git a/src/data/levels/index.js b/src/data/levels/index.js index ee9aefd..0a8ab03 100644 --- a/src/data/levels/index.js +++ b/src/data/levels/index.js @@ -9,6 +9,15 @@ import level08 from './level08.js'; import level09 from './level09.js'; import level10 from './level10.js'; import level11 from './level11.js'; +import level12 from './level12.js'; +import level13 from './level13.js'; +import level14 from './level14.js'; +import level15 from './level15.js'; +import level16 from './level16.js'; +import level17 from './level17.js'; +import level18 from './level18.js'; +import level19 from './level19.js'; +import level20 from './level20.js'; // Campaigns group levels into themed "maps" for the level select screen. // Each campaign gets its own background art (bgKey), menu theme music @@ -37,10 +46,34 @@ export const CAMPAIGNS = [ // Menu music for this campaign's map in LevelSelect (util/music.js // playMenuTrack); undefined = the main title theme. musicKey: 'urban_trap_theme', - // Its old levels (level04-06) moved into campaign01 while it grows toward - // 10; add campaign02's own levels here as they're built. An empty - // campaign renders as a bare map with "0/0 cleared" - fine as scaffolding. - levels: [level11], + // Its old levels (level04-06) moved into campaign01 while it grew toward + // 10; now full at 10 (level11-level20). level12 is a real designed level + // (built via editor/levelBuilder.js's section pipeline, not hand-drawn in + // the browser editor - review/playtest before shipping); level13-level20 + // are placeholder stubs (flat filler terrain, "Coming Soon" name/desc) to + // reserve their slots on the map - replace each in editor.html as it gets + // designed for real. + levels: [level11, level12, level13, level14, level15, level16, level17, level18, level19, level20], + // Custom layout (see positioning.js / the module comment above) so this + // campaign's map doesn't read as a reskin of campaign01's 2-lane loop: a + // 3-row serpentine (row1 left->right, row2 right->left, row3 left->right) + // instead of campaign01's top/bottom lane pair. Same 960x540 design-unit + // space; rows sit at y=220/320/420 (vs campaign01's 268/412 two-lane + // band) and x stays within 100-750 on every row so the flag's nudge past + // the last node (see LevelSelectScene._buildCampaign) lands in-bounds + // without needing to flip direction. + positions: [ + { x: 100, y: 220 }, // level11 + { x: 317, y: 220 }, // level12 + { x: 533, y: 220 }, // level13 + { x: 750, y: 220 }, // level14 + { x: 750, y: 320 }, // level15 + { x: 425, y: 320 }, // level16 + { x: 100, y: 320 }, // level17 + { x: 100, y: 420 }, // level18 + { x: 425, y: 420 }, // level19 + { x: 750, y: 420 }, // level20 + ], }, ]; diff --git a/src/data/levels/level12.js b/src/data/levels/level12.js new file mode 100644 index 0000000..52dc95b --- /dev/null +++ b/src/data/levels/level12.js @@ -0,0 +1,597 @@ +// Generated by editor.html - review before dropping into src/data/levels/. +export default { + id: "level12", + name: "Curb Check", + description: "Check yo self before you wreck yo self", + kidsAboard: 4, + timeLimit: 66, + startPosition: { x: 200, y: 700 }, + startAngle: 0, + terrain: [ + { points: [ + { x: -400, y: 840 }, + { x: -330, y: 840 }, + { x: -260, y: 840 }, + { x: -190, y: 840 }, + { x: -120, y: 840 }, + { x: -50, y: 840 }, + { x: 20, y: 840 }, + { x: 90, y: 840 }, + { x: 160, y: 840 }, + { x: 230, y: 840 }, + { x: 300, y: 840 }, + { x: 370, y: 840 }, + { x: 440, y: 840 }, + { x: 510, y: 840 }, + { x: 580, y: 840 }, + { x: 650, y: 840 }, + { x: 720, y: 840 }, + { x: 790, y: 840 }, + { x: 860, y: 840 }, + { x: 930, y: 840 }, + { x: 1000, y: 840 }, + { x: 1070, y: 840 }, + { x: 1140, y: 838 }, + { x: 1210, y: 836 }, + { x: 1280, y: 834 }, + { x: 1350, y: 828 }, + { x: 1420, y: 822 }, + { x: 1490, y: 816 }, + { x: 1560, y: 808 }, + { x: 1630, y: 802 }, + { x: 1700, y: 794 }, + { x: 1770, y: 786 }, + { x: 1840, y: 776 }, + { x: 1910, y: 768 }, + { x: 1980, y: 758 }, + { x: 2050, y: 750 }, + { x: 2120, y: 742 }, + { x: 2190, y: 732 }, + { x: 2260, y: 724 }, + { x: 2330, y: 714 }, + { x: 2400, y: 706 }, + { x: 2470, y: 698 }, + { x: 2540, y: 692 }, + { x: 2610, y: 684 }, + { x: 2680, y: 678 }, + { x: 2750, y: 674 }, + { x: 2820, y: 668 }, + { x: 2890, y: 666 }, + { x: 2960, y: 662 }, + { x: 3030, y: 660 }, + { x: 3100, y: 660 }, + { x: 3170, y: 660 }, + { x: 3240, y: 656 }, + { x: 3310, y: 650 }, + { x: 3380, y: 640 }, + { x: 3450, y: 628 }, + { x: 3520, y: 612 }, + { x: 3590, y: 596 }, + { x: 3660, y: 580 }, + { x: 3730, y: 564 }, + { x: 3800, y: 548 }, + { x: 3870, y: 532 }, + { x: 3940, y: 516 }, + { x: 4010, y: 500 }, + { x: 4080, y: 484 }, + { x: 4150, y: 468 }, + { x: 4220, y: 452 }, + { x: 4290, y: 436 }, + { x: 4360, y: 420 }, + { x: 4430, y: 404 }, + { x: 4500, y: 388 }, + { x: 4570, y: 372 }, + { x: 4640, y: 354 }, + { x: 4710, y: 332 }, + { x: 4780, y: 308 }, + { x: 4850, y: 280 }, + { x: 4920, y: 250 }, + { x: 4990, y: 220 }, + { x: 5060, y: 192 }, + { x: 5130, y: 162 }, + { x: 5200, y: 132 }, + { x: 5270, y: 102 }, + { x: 5340, y: 72 }, + { x: 5410, y: 44 }, + { x: 5480, y: 14 }, + { x: 5550, y: -16 }, + { x: 5620, y: -46 }, + { x: 5690, y: -76 }, + { x: 5760, y: -104 }, + { x: 5830, y: -134 }, + { x: 5900, y: -164 }, + { x: 5970, y: -194 }, + { x: 6040, y: -226 }, + { x: 6110, y: -260 }, + { x: 6180, y: -296 }, + { x: 6250, y: -334 }, + { x: 6320, y: -374 }, + { x: 6390, y: -414 }, + { x: 6460, y: -454 }, + { x: 6530, y: -494 }, + { x: 6600, y: -534 }, + { x: 6670, y: -574 }, + { x: 6740, y: -614 }, + { x: 6810, y: -654 }, + { x: 6880, y: -694 }, + { x: 6950, y: -734 }, + { x: 7020, y: -774 }, + { x: 7090, y: -814 }, + { x: 7160, y: -854 }, + { x: 7230, y: -894 }, + { x: 7300, y: -934 }, + { x: 7370, y: -976 }, + { x: 7440, y: -1024 }, + { x: 7510, y: -1082 }, + { x: 7580, y: -1148 }, + { x: 7650, y: -1216 }, + { x: 7720, y: -1284 }, + { x: 7790, y: -1354 }, + { x: 7860, y: -1422 }, + { x: 7930, y: -1490 }, + { x: 8000, y: -1558 }, + { x: 8070, y: -1626 }, + { x: 8120, y: -1676 }, + { x: 8120, y: -1674 }, + { x: 8190, y: -1074 }, + { x: 8260, y: -1068 }, + { x: 8330, y: -1062 }, + { x: 8400, y: -1056 }, + { x: 8470, y: -1052 }, + { x: 8540, y: -1046 }, + { x: 8610, y: -1040 }, + { x: 8680, y: -1034 }, + { x: 8750, y: -1028 }, + { x: 8820, y: -1022 }, + { x: 8890, y: -1016 }, + { x: 8960, y: -1010 }, + { x: 9030, y: -1004 }, + { x: 9100, y: -1000 }, + { x: 9170, y: -994 }, + { x: 9240, y: -988 }, + { x: 9310, y: -982 }, + { x: 9380, y: -976 }, + { x: 9450, y: -970 }, + { x: 9520, y: -964 }, + { x: 9590, y: -958 }, + { x: 9660, y: -948 }, + { x: 9730, y: -934 }, + { x: 9800, y: -916 }, + { x: 9870, y: -892 }, + { x: 9940, y: -866 }, + { x: 10010, y: -838 }, + { x: 10080, y: -810 }, + { x: 10150, y: -784 }, + { x: 10220, y: -756 }, + { x: 10290, y: -730 }, + { x: 10360, y: -702 }, + { x: 10430, y: -674 }, + { x: 10500, y: -648 }, + { x: 10570, y: -620 }, + { x: 10640, y: -594 }, + { x: 10710, y: -566 }, + { x: 10780, y: -538 }, + { x: 10850, y: -512 }, + { x: 10920, y: -484 }, + { x: 10990, y: -458 }, + { x: 11060, y: -434 }, + { x: 11130, y: -418 }, + { x: 11200, y: -408 }, + { x: 11270, y: -402 }, + { x: 11340, y: -402 }, + { x: 11410, y: -402 }, + { x: 11480, y: -402 }, + { x: 11550, y: -402 }, + { x: 11620, y: -402 }, + { x: 11690, y: -402 }, + { x: 11760, y: -402 }, + { x: 11830, y: -402 }, + { x: 11900, y: -402 }, + { x: 11970, y: -402 }, + { x: 12040, y: -402 }, + { x: 12110, y: -402 }, + { x: 12180, y: -402 }, + { x: 12250, y: -402 }, + { x: 12320, y: -402 }, + { x: 12390, y: -402 }, + { x: 12460, y: -402 }, + { x: 12530, y: -402 }, + { x: 12600, y: -402 }, + { x: 12670, y: -402 }, + { x: 12740, y: -402 }, + { x: 12810, y: -402 }, + { x: 12880, y: -402 }, + { x: 12950, y: -402 }, + { x: 13020, y: -402 }, + { x: 13090, y: -402 }, + { x: 13160, y: -402 }, + { x: 13230, y: -402 }, + { x: 13300, y: -402 }, + { x: 13328, y: -402 }, + { x: 13398, y: -406 }, + { x: 13468, y: -420 }, + { x: 13538, y: -442 }, + { x: 13608, y: -474 }, + { x: 13678, y: -514 }, + { x: 13720, y: -542 }, + { x: 13790, y: -542 }, + ] }, + { points: [ + { x: 14280, y: -542 }, + { x: 14350, y: -542 }, + { x: 14420, y: -542 }, + { x: 14490, y: -542 }, + { x: 14560, y: -542 }, + { x: 14630, y: -542 }, + { x: 14700, y: -542 }, + { x: 14770, y: -542 }, + { x: 14840, y: -542 }, + { x: 14910, y: -542 }, + { x: 14980, y: -542 }, + { x: 15050, y: -542 }, + { x: 15120, y: -542 }, + { x: 15190, y: -542 }, + ] }, + { points: [ + { x: 15568, y: -62 }, + { x: 15638, y: -62 }, + { x: 15680, y: -62 }, + { x: 15750, y: -62 }, + { x: 15820, y: -62 }, + { x: 15890, y: -62 }, + { x: 15960, y: -62 }, + { x: 16030, y: -62 }, + { x: 16100, y: -62 }, + { x: 16170, y: -62 }, + { x: 16240, y: -62 }, + { x: 16310, y: -62 }, + { x: 16380, y: -62 }, + { x: 16450, y: -62 }, + { x: 16520, y: -62 }, + { x: 16590, y: -62 }, + { x: 16660, y: -62 }, + { x: 16730, y: -62 }, + { x: 16800, y: -62 }, + { x: 16870, y: -62 }, + { x: 16940, y: -62 }, + { x: 17010, y: -62 }, + { x: 17080, y: -62 }, + { x: 17150, y: -98 }, + { x: 17220, y: -132 }, + { x: 17290, y: -168 }, + { x: 17360, y: -202 }, + { x: 17430, y: -238 }, + { x: 17500, y: -272 }, + { x: 17570, y: -308 }, + { x: 17640, y: -342 }, + { x: 17710, y: -342 }, + ] }, + { points: [ + { x: 18060, y: -222 }, + { x: 18130, y: -210 }, + { x: 18200, y: -180 }, + { x: 18270, y: -142 }, + { x: 18340, y: -104 }, + { x: 18410, y: -74 }, + { x: 18480, y: -62 }, + { x: 18550, y: -62 }, + { x: 18620, y: -62 }, + { x: 18690, y: -62 }, + { x: 18760, y: -62 }, + { x: 18830, y: -62 }, + { x: 18900, y: -62 }, + { x: 18970, y: -62 }, + { x: 19040, y: -62 }, + { x: 19110, y: -62 }, + { x: 19180, y: -62 }, + { x: 19250, y: -62 }, + { x: 19320, y: -62 }, + { x: 19390, y: -62 }, + { x: 19460, y: -62 }, + { x: 19488, y: -62 }, + { x: 19558, y: -66 }, + { x: 19628, y: -80 }, + { x: 19698, y: -102 }, + { x: 19768, y: -134 }, + { x: 19838, y: -174 }, + { x: 19880, y: -202 }, + { x: 19897, y: -216 }, + { x: 19913, y: -228 }, + { x: 19930, y: -238 }, + { x: 19947, y: -246 }, + { x: 19963, y: -250 }, + { x: 19980, y: -250 }, + { x: 19997, y: -248 }, + { x: 20013, y: -240 }, + { x: 20030, y: -232 }, + { x: 20047, y: -218 }, + { x: 20063, y: -204 }, + { x: 20080, y: -188 }, + { x: 20097, y: -174 }, + { x: 20113, y: -158 }, + { x: 20130, y: -146 }, + { x: 20147, y: -134 }, + { x: 20163, y: -126 }, + { x: 20180, y: -122 }, + { x: 20197, y: -120 }, + { x: 20213, y: -122 }, + { x: 20230, y: -128 }, + { x: 20247, y: -136 }, + { x: 20263, y: -144 }, + { x: 20280, y: -154 }, + { x: 20297, y: -164 }, + { x: 20313, y: -174 }, + { x: 20330, y: -180 }, + { x: 20347, y: -184 }, + { x: 20363, y: -186 }, + { x: 20380, y: -184 }, + { x: 20397, y: -178 }, + { x: 20413, y: -170 }, + { x: 20430, y: -156 }, + { x: 20447, y: -142 }, + { x: 20463, y: -126 }, + { x: 20480, y: -108 }, + { x: 20497, y: -90 }, + { x: 20513, y: -74 }, + { x: 20530, y: -58 }, + { x: 20547, y: -46 }, + { x: 20563, y: -36 }, + { x: 20580, y: -30 }, + { x: 20597, y: -28 }, + { x: 20613, y: -28 }, + { x: 20630, y: -32 }, + { x: 20647, y: -38 }, + { x: 20663, y: -48 }, + { x: 20680, y: -56 }, + { x: 20697, y: -66 }, + { x: 20713, y: -74 }, + { x: 20730, y: -80 }, + { x: 20747, y: -84 }, + { x: 20763, y: -86 }, + { x: 20780, y: -84 }, + { x: 20797, y: -78 }, + { x: 20813, y: -70 }, + { x: 20830, y: -58 }, + { x: 20847, y: -42 }, + { x: 20863, y: -26 }, + { x: 20880, y: -10 }, + { x: 20897, y: 8 }, + { x: 20913, y: 24 }, + { x: 20930, y: 38 }, + { x: 20947, y: 48 }, + { x: 20963, y: 56 }, + { x: 20980, y: 62 }, + { x: 20997, y: 62 }, + { x: 21013, y: 60 }, + { x: 21030, y: 54 }, + { x: 21047, y: 46 }, + { x: 21063, y: 36 }, + { x: 21080, y: 24 }, + { x: 21097, y: 14 }, + { x: 21113, y: 2 }, + { x: 21130, y: -6 }, + { x: 21147, y: -14 }, + { x: 21163, y: -16 }, + { x: 21180, y: -18 }, + { x: 21197, y: -14 }, + { x: 21213, y: -8 }, + { x: 21230, y: 0 }, + { x: 21247, y: 12 }, + { x: 21263, y: 24 }, + { x: 21280, y: 38 }, + { x: 21297, y: 52 }, + { x: 21313, y: 66 }, + { x: 21330, y: 78 }, + { x: 21346, y: 92 }, + { x: 21363, y: 104 }, + { x: 21379, y: 114 }, + { x: 21396, y: 124 }, + { x: 21412, y: 134 }, + { x: 21429, y: 142 }, + { x: 21445, y: 150 }, + { x: 21462, y: 156 }, + { x: 21478, y: 162 }, + { x: 21495, y: 166 }, + { x: 21512, y: 168 }, + { x: 21528, y: 172 }, + { x: 21545, y: 172 }, + { x: 21561, y: 174 }, + { x: 21578, y: 174 }, + { x: 21594, y: 172 }, + { x: 21611, y: 170 }, + { x: 21627, y: 168 }, + { x: 21644, y: 166 }, + { x: 21660, y: 162 }, + { x: 21677, y: 158 }, + { x: 21693, y: 152 }, + { x: 21710, y: 148 }, + { x: 21727, y: 142 }, + { x: 21743, y: 138 }, + { x: 21760, y: 134 }, + { x: 21776, y: 134 }, + { x: 21793, y: 136 }, + { x: 21809, y: 140 }, + { x: 21826, y: 146 }, + { x: 21842, y: 154 }, + { x: 21859, y: 162 }, + { x: 21875, y: 172 }, + { x: 21892, y: 180 }, + { x: 21908, y: 190 }, + { x: 21925, y: 196 }, + { x: 21942, y: 202 }, + { x: 21958, y: 206 }, + { x: 21975, y: 208 }, + { x: 21991, y: 208 }, + { x: 22008, y: 206 }, + { x: 22024, y: 204 }, + { x: 22041, y: 198 }, + { x: 22057, y: 192 }, + { x: 22074, y: 184 }, + { x: 22090, y: 178 }, + { x: 22107, y: 172 }, + { x: 22123, y: 166 }, + { x: 22140, y: 162 }, + { x: 22157, y: 160 }, + { x: 22173, y: 160 }, + { x: 22190, y: 162 }, + { x: 22206, y: 168 }, + { x: 22223, y: 174 }, + { x: 22239, y: 182 }, + { x: 22256, y: 190 }, + { x: 22272, y: 200 }, + { x: 22289, y: 208 }, + { x: 22305, y: 218 }, + { x: 22322, y: 226 }, + { x: 22338, y: 232 }, + { x: 22355, y: 236 }, + { x: 22372, y: 238 }, + { x: 22388, y: 240 }, + { x: 22405, y: 238 }, + { x: 22421, y: 234 }, + { x: 22438, y: 228 }, + { x: 22454, y: 222 }, + { x: 22471, y: 216 }, + { x: 22487, y: 210 }, + { x: 22504, y: 202 }, + { x: 22520, y: 198 }, + { x: 22537, y: 194 }, + { x: 22553, y: 192 }, + { x: 22570, y: 192 }, + { x: 22587, y: 196 }, + { x: 22603, y: 200 }, + { x: 22620, y: 206 }, + { x: 22636, y: 214 }, + { x: 22653, y: 222 }, + { x: 22669, y: 232 }, + { x: 22686, y: 242 }, + { x: 22702, y: 250 }, + { x: 22719, y: 258 }, + { x: 22735, y: 264 }, + { x: 22752, y: 270 }, + { x: 22768, y: 272 }, + { x: 22785, y: 272 }, + { x: 22802, y: 270 }, + { x: 22818, y: 266 }, + { x: 22835, y: 262 }, + { x: 22851, y: 256 }, + { x: 22868, y: 248 }, + { x: 22884, y: 242 }, + { x: 22901, y: 236 }, + { x: 22917, y: 230 }, + { x: 22934, y: 226 }, + { x: 22950, y: 224 }, + { x: 22967, y: 224 }, + { x: 22983, y: 228 }, + { x: 23000, y: 232 }, + { x: 23017, y: 238 }, + { x: 23033, y: 246 }, + { x: 23050, y: 254 }, + { x: 23066, y: 264 }, + { x: 23083, y: 272 }, + { x: 23099, y: 282 }, + { x: 23116, y: 290 }, + { x: 23132, y: 296 }, + { x: 23149, y: 300 }, + { x: 23165, y: 302 }, + { x: 23182, y: 302 }, + { x: 23198, y: 300 }, + { x: 23215, y: 296 }, + { x: 23232, y: 290 }, + { x: 23248, y: 284 }, + { x: 23265, y: 276 }, + { x: 23281, y: 270 }, + { x: 23298, y: 262 }, + { x: 23314, y: 258 }, + { x: 23331, y: 254 }, + { x: 23347, y: 252 }, + { x: 23364, y: 250 }, + { x: 23380, y: 252 }, + { x: 23397, y: 256 }, + { x: 23413, y: 262 }, + { x: 23430, y: 270 }, + { x: 23447, y: 278 }, + { x: 23463, y: 286 }, + { x: 23480, y: 296 }, + { x: 23496, y: 304 }, + { x: 23513, y: 310 }, + { x: 23529, y: 316 }, + { x: 23546, y: 320 }, + { x: 23562, y: 322 }, + { x: 23579, y: 322 }, + { x: 23595, y: 318 }, + { x: 23612, y: 314 }, + { x: 23628, y: 308 }, + { x: 23645, y: 302 }, + { x: 23662, y: 294 }, + { x: 23678, y: 286 }, + { x: 23695, y: 278 }, + { x: 23711, y: 272 }, + { x: 23728, y: 268 }, + { x: 23744, y: 266 }, + { x: 23761, y: 264 }, + { x: 23777, y: 266 }, + { x: 23794, y: 268 }, + { x: 23810, y: 274 }, + { x: 23827, y: 280 }, + { x: 23843, y: 288 }, + { x: 23860, y: 296 }, + { x: 23930, y: 296 }, + { x: 24000, y: 296 }, + { x: 24070, y: 296 }, + { x: 24140, y: 296 }, + { x: 24210, y: 296 }, + { x: 24280, y: 296 }, + { x: 24350, y: 296 }, + { x: 24420, y: 296 }, + { x: 24490, y: 296 }, + { x: 24560, y: 296 }, + { x: 24630, y: 296 }, + { x: 24700, y: 296 }, + { x: 24770, y: 296 }, + { x: 24840, y: 296 }, + { x: 24910, y: 296 }, + { x: 24980, y: 296 }, + { x: 25050, y: 296 }, + { x: 25120, y: 296 }, + { x: 25190, y: 296 }, + { x: 25260, y: 296 }, + { x: 25330, y: 296 }, + { x: 25400, y: 296 }, + { x: 25470, y: 296 }, + { x: 25540, y: 296 }, + { x: 25610, y: 296 }, + { x: 25680, y: 296 }, + { x: 25750, y: 296 }, + { x: 25820, y: 296 }, + { x: 25890, y: 296 }, + { x: 25960, y: 296 }, + { x: 26030, y: 296 }, + { x: 26100, y: 296 }, + { x: 26170, y: 296 }, + { x: 26240, y: 296 }, + { x: 26310, y: 296 }, + { x: 26380, y: 296 }, + { x: 26450, y: 296 }, + { x: 26520, y: 296 }, + { x: 26590, y: 296 }, + { x: 26660, y: 296 }, + ] }, + ], + obstacles: [], + items: [ + { type: "trashcan", mode: "gravity", x: 16821, y: -468 }, + { type: "cat", mode: "gravity", x: 17154, y: -443 }, + { type: "cat", mode: "gravity", x: 11904, y: -826 }, + { type: "tent", mode: "gravity", x: 19496, y: -268 }, + { type: "trashcan", mode: "gravity", x: 26054, y: -9 }, + { type: "trashcan", mode: "gravity", x: 25796, y: 41 }, + { type: "trashcan", mode: "gravity", x: 25546, y: -134 }, + ], + bonusItems: [ + { type: "spraypaint", x: 8371, y: -1534 }, + { type: "backpack", x: 15579, y: -476 }, + { type: "flour", x: 20263, y: -659 }, + ], + goal: { x: 26420, y: 57, width: 280, height: 640 }, + cameraBounds: { x: -600, y: -2476, width: 28060, height: 4116 }, +}; diff --git a/src/data/levels/level13.js b/src/data/levels/level13.js new file mode 100644 index 0000000..a6b6dcc --- /dev/null +++ b/src/data/levels/level13.js @@ -0,0 +1,51 @@ +// Generated by editor.html - review before dropping into src/data/levels/. +export default { + id: "level13", + name: "Coming Soon", + description: "Level design in progress - stub terrain, replace in editor.html.", + kidsAboard: 3, + timeLimit: 15, + startPosition: { x: 200, y: 700 }, + startAngle: 0, + terrain: [ + { points: [ + { x: -400, y: 840 }, + { x: -330, y: 840 }, + { x: -260, y: 840 }, + { x: -190, y: 840 }, + { x: -120, y: 840 }, + { x: -50, y: 840 }, + { x: 20, y: 840 }, + { x: 90, y: 840 }, + { x: 160, y: 840 }, + { x: 230, y: 840 }, + { x: 300, y: 840 }, + { x: 370, y: 840 }, + { x: 440, y: 840 }, + { x: 510, y: 840 }, + { x: 580, y: 840 }, + { x: 600, y: 840 }, + { x: 670, y: 836 }, + { x: 740, y: 828 }, + { x: 810, y: 814 }, + { x: 880, y: 800 }, + { x: 950, y: 784 }, + { x: 1020, y: 770 }, + { x: 1090, y: 758 }, + { x: 1160, y: 752 }, + { x: 1200, y: 750 }, + { x: 1270, y: 750 }, + { x: 1340, y: 750 }, + { x: 1410, y: 750 }, + { x: 1480, y: 750 }, + { x: 1550, y: 750 }, + { x: 1620, y: 750 }, + { x: 1690, y: 750 }, + { x: 1760, y: 750 }, + { x: 1800, y: 750 }, + ] }, + ], + obstacles: [], + goal: { x: 1560, y: 510, width: 280, height: 640 }, + cameraBounds: { x: -600, y: -50, width: 3200, height: 2000 }, +}; diff --git a/src/data/levels/level14.js b/src/data/levels/level14.js new file mode 100644 index 0000000..42b033a --- /dev/null +++ b/src/data/levels/level14.js @@ -0,0 +1,51 @@ +// Generated by editor.html - review before dropping into src/data/levels/. +export default { + id: "level14", + name: "Coming Soon", + description: "Level design in progress - stub terrain, replace in editor.html.", + kidsAboard: 3, + timeLimit: 15, + startPosition: { x: 200, y: 700 }, + startAngle: 0, + terrain: [ + { points: [ + { x: -400, y: 840 }, + { x: -330, y: 840 }, + { x: -260, y: 840 }, + { x: -190, y: 840 }, + { x: -120, y: 840 }, + { x: -50, y: 840 }, + { x: 20, y: 840 }, + { x: 90, y: 840 }, + { x: 160, y: 840 }, + { x: 230, y: 840 }, + { x: 300, y: 840 }, + { x: 370, y: 840 }, + { x: 440, y: 840 }, + { x: 510, y: 840 }, + { x: 580, y: 840 }, + { x: 600, y: 840 }, + { x: 670, y: 836 }, + { x: 740, y: 828 }, + { x: 810, y: 814 }, + { x: 880, y: 800 }, + { x: 950, y: 784 }, + { x: 1020, y: 770 }, + { x: 1090, y: 758 }, + { x: 1160, y: 752 }, + { x: 1200, y: 750 }, + { x: 1270, y: 750 }, + { x: 1340, y: 750 }, + { x: 1410, y: 750 }, + { x: 1480, y: 750 }, + { x: 1550, y: 750 }, + { x: 1620, y: 750 }, + { x: 1690, y: 750 }, + { x: 1760, y: 750 }, + { x: 1800, y: 750 }, + ] }, + ], + obstacles: [], + goal: { x: 1560, y: 510, width: 280, height: 640 }, + cameraBounds: { x: -600, y: -50, width: 3200, height: 2000 }, +}; diff --git a/src/data/levels/level15.js b/src/data/levels/level15.js new file mode 100644 index 0000000..1f72b81 --- /dev/null +++ b/src/data/levels/level15.js @@ -0,0 +1,51 @@ +// Generated by editor.html - review before dropping into src/data/levels/. +export default { + id: "level15", + name: "Coming Soon", + description: "Level design in progress - stub terrain, replace in editor.html.", + kidsAboard: 3, + timeLimit: 15, + startPosition: { x: 200, y: 700 }, + startAngle: 0, + terrain: [ + { points: [ + { x: -400, y: 840 }, + { x: -330, y: 840 }, + { x: -260, y: 840 }, + { x: -190, y: 840 }, + { x: -120, y: 840 }, + { x: -50, y: 840 }, + { x: 20, y: 840 }, + { x: 90, y: 840 }, + { x: 160, y: 840 }, + { x: 230, y: 840 }, + { x: 300, y: 840 }, + { x: 370, y: 840 }, + { x: 440, y: 840 }, + { x: 510, y: 840 }, + { x: 580, y: 840 }, + { x: 600, y: 840 }, + { x: 670, y: 836 }, + { x: 740, y: 828 }, + { x: 810, y: 814 }, + { x: 880, y: 800 }, + { x: 950, y: 784 }, + { x: 1020, y: 770 }, + { x: 1090, y: 758 }, + { x: 1160, y: 752 }, + { x: 1200, y: 750 }, + { x: 1270, y: 750 }, + { x: 1340, y: 750 }, + { x: 1410, y: 750 }, + { x: 1480, y: 750 }, + { x: 1550, y: 750 }, + { x: 1620, y: 750 }, + { x: 1690, y: 750 }, + { x: 1760, y: 750 }, + { x: 1800, y: 750 }, + ] }, + ], + obstacles: [], + goal: { x: 1560, y: 510, width: 280, height: 640 }, + cameraBounds: { x: -600, y: -50, width: 3200, height: 2000 }, +}; diff --git a/src/data/levels/level16.js b/src/data/levels/level16.js new file mode 100644 index 0000000..08a78ad --- /dev/null +++ b/src/data/levels/level16.js @@ -0,0 +1,51 @@ +// Generated by editor.html - review before dropping into src/data/levels/. +export default { + id: "level16", + name: "Coming Soon", + description: "Level design in progress - stub terrain, replace in editor.html.", + kidsAboard: 3, + timeLimit: 15, + startPosition: { x: 200, y: 700 }, + startAngle: 0, + terrain: [ + { points: [ + { x: -400, y: 840 }, + { x: -330, y: 840 }, + { x: -260, y: 840 }, + { x: -190, y: 840 }, + { x: -120, y: 840 }, + { x: -50, y: 840 }, + { x: 20, y: 840 }, + { x: 90, y: 840 }, + { x: 160, y: 840 }, + { x: 230, y: 840 }, + { x: 300, y: 840 }, + { x: 370, y: 840 }, + { x: 440, y: 840 }, + { x: 510, y: 840 }, + { x: 580, y: 840 }, + { x: 600, y: 840 }, + { x: 670, y: 836 }, + { x: 740, y: 828 }, + { x: 810, y: 814 }, + { x: 880, y: 800 }, + { x: 950, y: 784 }, + { x: 1020, y: 770 }, + { x: 1090, y: 758 }, + { x: 1160, y: 752 }, + { x: 1200, y: 750 }, + { x: 1270, y: 750 }, + { x: 1340, y: 750 }, + { x: 1410, y: 750 }, + { x: 1480, y: 750 }, + { x: 1550, y: 750 }, + { x: 1620, y: 750 }, + { x: 1690, y: 750 }, + { x: 1760, y: 750 }, + { x: 1800, y: 750 }, + ] }, + ], + obstacles: [], + goal: { x: 1560, y: 510, width: 280, height: 640 }, + cameraBounds: { x: -600, y: -50, width: 3200, height: 2000 }, +}; diff --git a/src/data/levels/level17.js b/src/data/levels/level17.js new file mode 100644 index 0000000..3101273 --- /dev/null +++ b/src/data/levels/level17.js @@ -0,0 +1,51 @@ +// Generated by editor.html - review before dropping into src/data/levels/. +export default { + id: "level17", + name: "Coming Soon", + description: "Level design in progress - stub terrain, replace in editor.html.", + kidsAboard: 3, + timeLimit: 15, + startPosition: { x: 200, y: 700 }, + startAngle: 0, + terrain: [ + { points: [ + { x: -400, y: 840 }, + { x: -330, y: 840 }, + { x: -260, y: 840 }, + { x: -190, y: 840 }, + { x: -120, y: 840 }, + { x: -50, y: 840 }, + { x: 20, y: 840 }, + { x: 90, y: 840 }, + { x: 160, y: 840 }, + { x: 230, y: 840 }, + { x: 300, y: 840 }, + { x: 370, y: 840 }, + { x: 440, y: 840 }, + { x: 510, y: 840 }, + { x: 580, y: 840 }, + { x: 600, y: 840 }, + { x: 670, y: 836 }, + { x: 740, y: 828 }, + { x: 810, y: 814 }, + { x: 880, y: 800 }, + { x: 950, y: 784 }, + { x: 1020, y: 770 }, + { x: 1090, y: 758 }, + { x: 1160, y: 752 }, + { x: 1200, y: 750 }, + { x: 1270, y: 750 }, + { x: 1340, y: 750 }, + { x: 1410, y: 750 }, + { x: 1480, y: 750 }, + { x: 1550, y: 750 }, + { x: 1620, y: 750 }, + { x: 1690, y: 750 }, + { x: 1760, y: 750 }, + { x: 1800, y: 750 }, + ] }, + ], + obstacles: [], + goal: { x: 1560, y: 510, width: 280, height: 640 }, + cameraBounds: { x: -600, y: -50, width: 3200, height: 2000 }, +}; diff --git a/src/data/levels/level18.js b/src/data/levels/level18.js new file mode 100644 index 0000000..b25bc9b --- /dev/null +++ b/src/data/levels/level18.js @@ -0,0 +1,51 @@ +// Generated by editor.html - review before dropping into src/data/levels/. +export default { + id: "level18", + name: "Coming Soon", + description: "Level design in progress - stub terrain, replace in editor.html.", + kidsAboard: 3, + timeLimit: 15, + startPosition: { x: 200, y: 700 }, + startAngle: 0, + terrain: [ + { points: [ + { x: -400, y: 840 }, + { x: -330, y: 840 }, + { x: -260, y: 840 }, + { x: -190, y: 840 }, + { x: -120, y: 840 }, + { x: -50, y: 840 }, + { x: 20, y: 840 }, + { x: 90, y: 840 }, + { x: 160, y: 840 }, + { x: 230, y: 840 }, + { x: 300, y: 840 }, + { x: 370, y: 840 }, + { x: 440, y: 840 }, + { x: 510, y: 840 }, + { x: 580, y: 840 }, + { x: 600, y: 840 }, + { x: 670, y: 836 }, + { x: 740, y: 828 }, + { x: 810, y: 814 }, + { x: 880, y: 800 }, + { x: 950, y: 784 }, + { x: 1020, y: 770 }, + { x: 1090, y: 758 }, + { x: 1160, y: 752 }, + { x: 1200, y: 750 }, + { x: 1270, y: 750 }, + { x: 1340, y: 750 }, + { x: 1410, y: 750 }, + { x: 1480, y: 750 }, + { x: 1550, y: 750 }, + { x: 1620, y: 750 }, + { x: 1690, y: 750 }, + { x: 1760, y: 750 }, + { x: 1800, y: 750 }, + ] }, + ], + obstacles: [], + goal: { x: 1560, y: 510, width: 280, height: 640 }, + cameraBounds: { x: -600, y: -50, width: 3200, height: 2000 }, +}; diff --git a/src/data/levels/level19.js b/src/data/levels/level19.js new file mode 100644 index 0000000..061b9d8 --- /dev/null +++ b/src/data/levels/level19.js @@ -0,0 +1,51 @@ +// Generated by editor.html - review before dropping into src/data/levels/. +export default { + id: "level19", + name: "Coming Soon", + description: "Level design in progress - stub terrain, replace in editor.html.", + kidsAboard: 3, + timeLimit: 15, + startPosition: { x: 200, y: 700 }, + startAngle: 0, + terrain: [ + { points: [ + { x: -400, y: 840 }, + { x: -330, y: 840 }, + { x: -260, y: 840 }, + { x: -190, y: 840 }, + { x: -120, y: 840 }, + { x: -50, y: 840 }, + { x: 20, y: 840 }, + { x: 90, y: 840 }, + { x: 160, y: 840 }, + { x: 230, y: 840 }, + { x: 300, y: 840 }, + { x: 370, y: 840 }, + { x: 440, y: 840 }, + { x: 510, y: 840 }, + { x: 580, y: 840 }, + { x: 600, y: 840 }, + { x: 670, y: 836 }, + { x: 740, y: 828 }, + { x: 810, y: 814 }, + { x: 880, y: 800 }, + { x: 950, y: 784 }, + { x: 1020, y: 770 }, + { x: 1090, y: 758 }, + { x: 1160, y: 752 }, + { x: 1200, y: 750 }, + { x: 1270, y: 750 }, + { x: 1340, y: 750 }, + { x: 1410, y: 750 }, + { x: 1480, y: 750 }, + { x: 1550, y: 750 }, + { x: 1620, y: 750 }, + { x: 1690, y: 750 }, + { x: 1760, y: 750 }, + { x: 1800, y: 750 }, + ] }, + ], + obstacles: [], + goal: { x: 1560, y: 510, width: 280, height: 640 }, + cameraBounds: { x: -600, y: -50, width: 3200, height: 2000 }, +}; diff --git a/src/data/levels/level20.js b/src/data/levels/level20.js new file mode 100644 index 0000000..3d0911f --- /dev/null +++ b/src/data/levels/level20.js @@ -0,0 +1,51 @@ +// Generated by editor.html - review before dropping into src/data/levels/. +export default { + id: "level20", + name: "Coming Soon", + description: "Level design in progress - stub terrain, replace in editor.html.", + kidsAboard: 3, + timeLimit: 15, + startPosition: { x: 200, y: 700 }, + startAngle: 0, + terrain: [ + { points: [ + { x: -400, y: 840 }, + { x: -330, y: 840 }, + { x: -260, y: 840 }, + { x: -190, y: 840 }, + { x: -120, y: 840 }, + { x: -50, y: 840 }, + { x: 20, y: 840 }, + { x: 90, y: 840 }, + { x: 160, y: 840 }, + { x: 230, y: 840 }, + { x: 300, y: 840 }, + { x: 370, y: 840 }, + { x: 440, y: 840 }, + { x: 510, y: 840 }, + { x: 580, y: 840 }, + { x: 600, y: 840 }, + { x: 670, y: 836 }, + { x: 740, y: 828 }, + { x: 810, y: 814 }, + { x: 880, y: 800 }, + { x: 950, y: 784 }, + { x: 1020, y: 770 }, + { x: 1090, y: 758 }, + { x: 1160, y: 752 }, + { x: 1200, y: 750 }, + { x: 1270, y: 750 }, + { x: 1340, y: 750 }, + { x: 1410, y: 750 }, + { x: 1480, y: 750 }, + { x: 1550, y: 750 }, + { x: 1620, y: 750 }, + { x: 1690, y: 750 }, + { x: 1760, y: 750 }, + { x: 1800, y: 750 }, + ] }, + ], + obstacles: [], + goal: { x: 1560, y: 510, width: 280, height: 640 }, + cameraBounds: { x: -600, y: -50, width: 3200, height: 2000 }, +}; diff --git a/src/editor/main.js b/src/editor/main.js index 05082b3..84c918d 100644 --- a/src/editor/main.js +++ b/src/editor/main.js @@ -28,8 +28,8 @@ const state = { // items remain editable, and the level can be re-exported. imported: null, // Smashable props + bonus items placed on the course. Each entry has a - // `kind`: 'smashable' (TV/chair/cone - can float or fall, smash off on hit) - // or 'bonus' (CTC/guitar/cash/flour - always float, pop into fireworks on hit). + // `kind`: 'smashable' (TV/chair/cone/trash can/tent/cat - can float or fall, smash off on hit) + // or 'bonus' (CTC/guitar/cash/flour/spray paint/backpack - always float, pop into fireworks on hit). // x/y are in the SAME scaled world units the preview canvas plots (and that // the exported level uses); refIndex is the track section it was added // under (rebased on reordering). @@ -190,34 +190,43 @@ const ITEM_TYPES = [ { id: 'tv', label: 'TV' }, { id: 'chair', label: 'Chair' }, { id: 'cone', label: 'Cone' }, + { id: 'trashcan', label: 'Trash Can' }, + { id: 'tent', label: 'Tent' }, + { id: 'cat', label: 'Cat' }, ]; -const ITEM_COLORS = { tv: '#4a90d9', chair: '#d9a04a', cone: '#d96a4a' }; -const ITEM_ICONS = { tv: 'TV', chair: 'CH', cone: 'CO' }; +const ITEM_COLORS = { tv: '#4a90d9', chair: '#d9a04a', cone: '#d96a4a', trashcan: '#6a9a5a', tent: '#d98a3a', cat: '#9a8a6a' }; +const ITEM_ICONS = { tv: 'TV', chair: 'CH', cone: 'CO', trashcan: 'TR', tent: 'TN', cat: 'CA' }; // The real spritesheet, loaded directly as an for the preview map // (frames: 0 TV, 1 smashed TV, 2 chair, 3 smashed chair, 4 cone, 5 smashed -// cone - 64x64 cells, same order as ITEM_TYPES pairs above). The circle +// cone, 6 trash can, 7 smashed trash can, 8 tent, 9 smashed tent, 10 cat, +// 11 smashed cat - 64x64 cells in an 8-wide x 2-row grid, same order as the +// ITEM_TYPES pairs above; frames 6+ live on the second row). The circle // fallback in drawPreview covers the brief moment before it finishes loading // and any file:// context where the image is blocked. const ITEM_SPRITE = new Image(); ITEM_SPRITE.src = 'assets/sprites/smash-items.png'; -const ITEM_SPRITE_FRAMES = { tv: 0, chair: 2, cone: 4 }; +const ITEM_SPRITE_FRAMES = { tv: 0, chair: 2, cone: 4, trashcan: 6, tent: 8, cat: 10 }; const ITEM_SPRITE_CELL = 64; // Bonus items ("Placeables" toggle -> "Bonus items"): float-only collectible -// prizes - CTC box / guitar / cash / flour. See BonusItemManager.js in the -// game for the type/frame/color table; frames here are the BONUS frame of -// each pair (0 CTC, 3 guitar, 6 cash, 9 flour). +// prizes - CTC box / guitar / cash / flour / spray paint / backpack. See +// BonusItemManager.js in the game for the type/frame/color table; frames +// here are the BONUS frame of each pair (0 CTC, 3 guitar, 6 cash, 9 flour, +// 12 spray paint, 15 backpack - on the 9-wide x 2-row sheet, frames 9+ sit +// on the second row). const BONUS_TYPES = [ { id: 'ctc', label: 'CTC Box' }, { id: 'guitar', label: 'Guitar' }, { id: 'cash', label: 'Cash' }, { id: 'flour', label: 'Flour' }, + { id: 'spraypaint', label: 'Spray Paint' }, + { id: 'backpack', label: 'Backpack' }, ]; -const BONUS_COLORS = { ctc: '#e0a040', guitar: '#4b9a45', cash: '#b3ad9c', flour: '#cbb58e' }; -const BONUS_ICONS = { ctc: 'CT', guitar: 'GT', cash: '$', flour: 'FL' }; +const BONUS_COLORS = { ctc: '#e0a040', guitar: '#4b9a45', cash: '#b3ad9c', flour: '#cbb58e', spraypaint: '#c04838', backpack: '#8a6a48' }; +const BONUS_ICONS = { ctc: 'CT', guitar: 'GT', cash: '$', flour: 'FL', spraypaint: 'SP', backpack: 'BP' }; const BONUS_SPRITE = new Image(); BONUS_SPRITE.src = 'assets/sprites/bonus-items.png'; -const BONUS_SPRITE_FRAMES = { ctc: 0, guitar: 3, cash: 6, flour: 9 }; +const BONUS_SPRITE_FRAMES = { ctc: 0, guitar: 3, cash: 6, flour: 9, spraypaint: 12, backpack: 15 }; // Samples the terrain surface Y (highest ground) at a given world X - used // to draw the "falls to here" drop line for gravity-mode items in the @@ -250,8 +259,9 @@ function buildItemsForm() { itemsFormEl.appendChild(fieldset); // --- Placeable kind toggle: smashables (TV/chair/cone) vs bonus items - // (CTC/guitar/cash/flour). Only the relevant type+mode controls show below, - // and clicks on the preview place whichever kind is selected. + // (CTC/guitar/cash/flour/spray paint/backpack). Only the relevant type+mode + // controls show below, and clicks on the preview place whichever kind is + // selected. const kindRadios = []; for (const kind of ['smashable', 'bonus']) { const radio = document.createElement('input'); @@ -266,7 +276,7 @@ function buildItemsForm() { }); const label = document.createElement('label'); label.className = 'checkbox-row'; - label.append(radio, kind === 'smashable' ? 'Smashables (TV / Chair / Cone)' : 'Bonus items (CTC / Guitar / Cash / Flour)'); + label.append(radio, kind === 'smashable' ? 'Smashables (TV / Chair / Cone / Trash Can / Tent / Cat)' : 'Bonus items (CTC / Guitar / Cash / Flour / Spray Paint / Backpack)'); kindRadios.push(label); } const kindWrap = document.createElement('div'); @@ -752,9 +762,15 @@ function drawPreview(levelData) { } // Both spritesheets share the same 64px cell size, so ITEM_SPRITE_CELL // works for bonus frames too (no separate cell constant needed). + // Each sheet is a multi-row grid (smash: 8 wide, bonus: 9 wide), so + // derive the cell's row from the frame index - frames on row 2+ are + // out of bounds with a single-row assumption. + const cols = Math.max(1, Math.floor(spriteTable.naturalWidth / ITEM_SPRITE_CELL)); + const sx = (frame % cols) * ITEM_SPRITE_CELL; + const sy = Math.floor(frame / cols) * ITEM_SPRITE_CELL; ctx.drawImage( spriteTable, - frame * ITEM_SPRITE_CELL, 0, ITEM_SPRITE_CELL, ITEM_SPRITE_CELL, + sx, sy, ITEM_SPRITE_CELL, ITEM_SPRITE_CELL, ix - half, iy - half, itemDrawScale, itemDrawScale, ); } else { diff --git a/src/entities/BonusItemManager.js b/src/entities/BonusItemManager.js index 69a4c6c..467d5c3 100644 --- a/src/entities/BonusItemManager.js +++ b/src/entities/BonusItemManager.js @@ -1,10 +1,12 @@ import { BONUS_ITEM } from '../config.js'; -// bonus-items.png is 12 frames of 64x64, in order: +// bonus-items.png is 18 frames of 64x64 in a 9-wide x 2-row grid, in order: // 0 CTC box, 1 CTC firework object 1, 2 CTC firework object 2, // 3 Guitar, 4 Guitar firework object 1, 5 Guitar firework object 2, // 6 Cash, 7 Cash firework object 1, 8 Cash firework object 2, -// 9 Flour, 10 Flour firework object 1, 11 Flour firework object 2. +// 9 Flour, 10 Flour firework object 1, 11 Flour firework object 2, +// 12 Spray paint, 13 Spray paint firework object 1, 14 Spray paint firework object 2, +// 15 Backpack, 16 Backpack firework object 1, 17 Backpack firework object 2. // Each bonus item is followed by its two firework-object frames, which are // the sprites the explosion burst is made of (see _explode below). const BONUS_TYPES = { @@ -12,6 +14,8 @@ const BONUS_TYPES = { guitar: { frame: 3, fireworks: [4, 5], label: 'Guitar', soundKey: 'bonus_guitar' }, cash: { frame: 6, fireworks: [7, 8], label: 'Cash', soundKey: 'bonus_cash' }, flour: { frame: 9, fireworks: [10, 11], label: 'Flour', soundKey: 'bonus_flour' }, + spraypaint: { frame: 12, fireworks: [13, 14], label: 'Spray Paint', soundKey: 'bonus_spraypaint' }, + backpack: { frame: 15, fireworks: [16, 17], label: 'Backpack', soundKey: 'bonus_backpack' }, }; export default class BonusItemManager { diff --git a/src/entities/SmashItemManager.js b/src/entities/SmashItemManager.js index 95a5e42..926febc 100644 --- a/src/entities/SmashItemManager.js +++ b/src/entities/SmashItemManager.js @@ -1,12 +1,17 @@ import { SMASH_ITEM } from '../config.js'; -// smash-items.png is 6 frames of 64x64, in order: +// smash-items.png is 12 frames of 64x64 in an 8-wide x 2-row grid +// (frame 6 onward wraps to row 2), in order: // 0 intact TV, 1 smashed TV, 2 intact chair, 3 smashed chair, -// 4 intact cone, 5 smashed cone. +// 4 intact cone, 5 smashed cone, 6 intact trash can, 7 smashed trash can, +// 8 intact tent, 9 smashed tent, 10 intact cat, 11 smashed cat. const ITEM_TYPES = { tv: { baseFrame: 0, smashedFrame: 1, label: 'TV', soundKey: 'smash_tv' }, chair: { baseFrame: 2, smashedFrame: 3, label: 'Chair', soundKey: 'smash_chair' }, cone: { baseFrame: 4, smashedFrame: 5, label: 'Cone', soundKey: 'smash_cone' }, + trashcan: { baseFrame: 6, smashedFrame: 7, label: 'Trash Can', soundKey: 'smash_trashcan' }, + tent: { baseFrame: 8, smashedFrame: 9, label: 'Tent', soundKey: 'smash_tent' }, + cat: { baseFrame: 10, smashedFrame: 11, label: 'Cat', soundKey: 'smash_cat' }, }; export default class SmashItemManager { diff --git a/src/scenes/PreloadScene.js b/src/scenes/PreloadScene.js index 139c8aa..7bb3813 100644 --- a/src/scenes/PreloadScene.js +++ b/src/scenes/PreloadScene.js @@ -39,10 +39,23 @@ export default class PreloadScene extends Phaser.Scene { this.load.audio('smash_tv', 'assets/fx/smash-tv.mp3'); this.load.audio('smash_chair', 'assets/fx/smash-chair.mp3'); this.load.audio('smash_cone', 'assets/fx/smash-cone.mp3'); + // Newer smashable props (trash can / tent / cat). These files don't ship + // yet - a missing one just 404s into FILE_LOAD_ERROR like the voice lines, + // and SmashItemManager._smash checks cache.audio.exists before playing, + // so they degrade to silent. Drop a real MP3 at the path and it plays. + this.load.audio('smash_trashcan', 'assets/fx/smash-trashcan.mp3'); + this.load.audio('smash_tent', 'assets/fx/smash-tent.mp3'); + this.load.audio('smash_cat', 'assets/fx/smash-cat.mp3'); this.load.audio('bonus_cash', 'assets/fx/bonus-cash.mp3'); this.load.audio('bonus_ctc', 'assets/fx/bonus-ctc.mp3'); this.load.audio('bonus_guitar', 'assets/fx/bonus-guitar.mp3'); this.load.audio('bonus_flour', 'assets/fx/bonus-flour.mp3'); + // Newer bonus props (spray paint / backpack). These files don't ship yet + // - a missing one just 404s into FILE_LOAD_ERROR like the voice lines, + // and BonusItemManager._explode checks cache.audio.exists before playing, + // so they degrade to silent. Drop a real MP3 at the path and it plays. + this.load.audio('bonus_spraypaint', 'assets/fx/bonus-spraypaint.mp3'); + this.load.audio('bonus_backpack', 'assets/fx/bonus-backpack.mp3'); loadKidFallSounds(this.load); // Not every level has a voice-over - this just attempts one per level diff --git a/src/util/assetManifest.js b/src/util/assetManifest.js index 1110f51..5b66b5f 100644 --- a/src/util/assetManifest.js +++ b/src/util/assetManifest.js @@ -21,13 +21,16 @@ export const ASSET_MANIFEST = [ // as before - only used for the single-frame placeholder fallback. { key: 'kid_idle', path: 'assets/sprites/kid_idle.png', width: 28 * WORLD_SCALE, height: 28 * WORLD_SCALE, kind: 'circle', color: 0xf2c14e, frameWidth: 56, frameHeight: 56 }, { key: 'kid_ejected', path: 'assets/sprites/kid_ejected.png', width: 28 * WORLD_SCALE, height: 28 * WORLD_SCALE, kind: 'circle', color: 0xe0553b, frameWidth: 56, frameHeight: 56 }, - // 6 frames of 64x64 smashable props (intact/smashed TV, chair, cone) - - // see SMASH_ITEM in config.js and src/entities/SmashItemManager.js. + // 12 frames of 64x64 smashable props (intact/smashed TV, chair, cone, + // trash can, tent, cat) in an 8-wide x 2-row grid - see SMASH_ITEM in + // config.js and src/entities/SmashItemManager.js. // frameWidth/frameHeight are raw SOURCE pixels (one 64x64 cell each), - // same convention as the kid sheets above. + // same convention as the kid sheets above; Phaser derives the 8x2 grid + // from the sheet's own dimensions, so adding rows is frame-safe. { key: 'smash_items', path: 'assets/sprites/smash-items.png', width: 64 * WORLD_SCALE, height: 64 * WORLD_SCALE, kind: 'rect', color: 0x4c8a5c, frameWidth: 64, frameHeight: 64 }, - // 12 frames of 64x64 bonus items (CTC box + 2 firework frames, guitar + 2, - // cash + 2, flour + 2 - see BONUS_ITEM in config.js and BonusItemManager.js). + // 18 frames of 64x64 bonus items (CTC box + 2 firework frames, guitar + 2, + // cash + 2, flour + 2, spray paint + 2, backpack + 2) in a 9-wide x 2-row + // grid - see BONUS_ITEM in config.js and BonusItemManager.js. { key: 'bonus_items', path: 'assets/sprites/bonus-items.png', width: 64 * WORLD_SCALE, height: 64 * WORLD_SCALE, kind: 'rect', color: 0xe0a040, frameWidth: 64, frameHeight: 64 }, // Campaign 1 ("Evergreen Lake") parallax set - see DEFAULT_THEME in // data/themes.js. The _2 set is campaign 2's; both are 3136x672 natively