Compare commits

..

5 Commits

Author SHA1 Message Date
Brian Fertig a728a51cd2 Level updates and campaign 2 assets 2026-08-23 16:20:26 -06:00
Brian Fertig fba3ed040d Add halfpipe and reverseHalfpipe section generators
- Register `halfpipe` and `reverseHalfpipe` in SECTION_TYPES and GENERATORS
- Implement both as single cubic Bezier curves with vertical tangents at entrance/exit:
  - halfpipe: near-vertical drop into a rounded bottom, then a shorter climb; exit ends below entrance (net decline)
  - reverseHalfpipe: vertical mirror of halfpipe — short rise arcing up and over, then a longer drop; exit also ends below entrance
- Use monotonic x(t) = W * t^2 * (3 - 2t) to keep terrain valid as y(x)
- Sample each profile with 24 points and snap endpoints exactly for seamless section joins
- Scale vertical magnitudes by vScale; chosen defaults yield ~350 base-unit drop and ~210 net decline at default width
2026-08-23 13:57:18 -06:00
Brian Fertig b8b6e74c8d Updates to audio and levels 2026-08-23 13:17:34 -06:00
Brian Fertig 864e76098a Smooth washboard section: half-sine arches instead of spiky zig-zag 2026-08-23 12:56:34 -06:00
Brian Fertig 3be7b857ed Add washboard section types (flat / up / down) to the level editor 2026-08-23 12:46:35 -06:00
23 changed files with 2817 additions and 649 deletions

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.6 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.4 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.9 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 14 KiB

After

Width:  |  Height:  |  Size: 2.6 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 10 KiB

After

Width:  |  Height:  |  Size: 25 KiB

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@ -8,6 +8,7 @@ import level07 from './level07.js';
import level08 from './level08.js';
import level09 from './level09.js';
import level10 from './level10.js';
import level11 from './level11.js';
// Campaigns group levels into themed "maps" for the level select screen.
// Each campaign gets its own background art (bgKey) and theme, so adding a
@ -32,10 +33,13 @@ export const CAMPAIGNS = [
id: 'campaign02',
name: 'Underprivileged Community',
bgKey: 'campaign02_bg',
// 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: [],
levels: [level11],
},
];

View File

@ -1,11 +1,10 @@
// Scaffolding level - terrain is a copy of level06.
// Build out the real layout in editor.html, then replace this file.
// Generated by editor.html - review before dropping into src/data/levels/.
export default {
id: "level08",
name: "Pothole Panic",
description: "Roads not built for a bus... or its cargo.",
kidsAboard: 3,
timeLimit: 41,
description: "Hold on kids!!",
kidsAboard: 4,
timeLimit: 35,
startPosition: { x: 200, y: 700 },
startAngle: 0,
terrain: [
@ -31,220 +30,461 @@ export default {
{ x: 860, y: 840 },
{ x: 930, y: 840 },
{ x: 1000, y: 840 },
{ x: 1070, y: 840 },
{ x: 1140, y: 840 },
{ x: 1210, y: 840 },
{ x: 1280, y: 840 },
{ x: 1350, y: 840 },
{ x: 1420, y: 840 },
{ x: 1490, y: 840 },
{ x: 1560, y: 840 },
{ x: 1630, y: 840 },
{ x: 1700, y: 840 },
{ x: 1770, y: 840 },
{ x: 1840, y: 840 },
{ x: 1910, y: 840 },
{ x: 1980, y: 840 },
{ x: 2050, y: 840 },
{ x: 2120, y: 840 },
{ x: 2190, y: 840 },
{ x: 2260, y: 840 },
{ x: 2330, y: 840 },
{ x: 2400, y: 840 },
{ x: 2470, y: 840 },
{ x: 2540, y: 840 },
{ x: 2610, y: 840 },
{ x: 2680, y: 840 },
{ x: 2750, y: 840 },
{ x: 2820, y: 840 },
{ x: 2890, y: 840 },
{ x: 2960, y: 840 },
{ x: 3030, y: 840 },
{ x: 3100, y: 840 },
{ x: 3170, y: 840 },
{ x: 3240, y: 840 },
{ x: 3310, y: 840 },
{ x: 3380, y: 840 },
{ x: 3450, y: 840 },
{ x: 3520, y: 840 },
{ x: 3590, y: 840 },
{ x: 3660, y: 840 },
{ x: 3730, y: 840 },
{ x: 3800, y: 840 },
{ x: 3870, y: 842 },
{ x: 3940, y: 846 },
{ x: 4010, y: 850 },
{ x: 4080, y: 858 },
{ x: 4150, y: 868 },
{ x: 4220, y: 878 },
{ x: 4290, y: 890 },
{ x: 4360, y: 904 },
{ x: 4430, y: 916 },
{ x: 4500, y: 930 },
{ x: 4570, y: 944 },
{ x: 4640, y: 956 },
{ x: 4710, y: 970 },
{ x: 4780, y: 982 },
{ x: 4850, y: 992 },
{ x: 4920, y: 1002 },
{ x: 4990, y: 1010 },
{ x: 5060, y: 1014 },
{ x: 5130, y: 1018 },
{ x: 5200, y: 1020 },
{ x: 5270, y: 1020 },
{ x: 5340, y: 1020 },
{ x: 5410, y: 1020 },
{ x: 5480, y: 1020 },
{ x: 5550, y: 1020 },
{ x: 5620, y: 1020 },
{ x: 5690, y: 1020 },
{ x: 5760, y: 1020 },
{ x: 5830, y: 1020 },
{ x: 5900, y: 1020 },
{ x: 5970, y: 1020 },
{ x: 6040, y: 1020 },
{ x: 6110, y: 1020 },
{ x: 6180, y: 1020 },
{ x: 6250, y: 1020 },
{ x: 6320, y: 1020 },
{ x: 6390, y: 1020 },
{ x: 6460, y: 1020 },
{ x: 6530, y: 1020 },
{ x: 6600, y: 1020 },
{ x: 6670, y: 1018 },
{ x: 6740, y: 1014 },
{ x: 6810, y: 1010 },
{ x: 6880, y: 1002 },
{ x: 6950, y: 992 },
{ x: 7020, y: 982 },
{ x: 7090, y: 970 },
{ x: 7160, y: 956 },
{ x: 7230, y: 944 },
{ x: 7300, y: 930 },
{ x: 7370, y: 916 },
{ x: 7440, y: 904 },
{ x: 7510, y: 890 },
{ x: 7580, y: 878 },
{ x: 7650, y: 868 },
{ x: 7720, y: 858 },
{ x: 7790, y: 850 },
{ x: 7860, y: 846 },
{ x: 7930, y: 842 },
{ x: 8000, y: 840 },
{ x: 8070, y: 840 },
{ x: 8140, y: 840 },
{ x: 8210, y: 840 },
{ x: 8280, y: 840 },
{ x: 8350, y: 840 },
{ x: 8420, y: 840 },
{ x: 8490, y: 840 },
{ x: 8560, y: 840 },
{ x: 8630, y: 840 },
{ x: 8700, y: 840 },
{ x: 8770, y: 840 },
{ x: 8840, y: 840 },
{ x: 8910, y: 840 },
{ x: 8980, y: 840 },
{ x: 9050, y: 840 },
{ x: 9120, y: 840 },
{ x: 9190, y: 840 },
{ x: 9260, y: 840 },
{ x: 9330, y: 840 },
{ x: 9400, y: 840 },
{ x: 9470, y: 840 },
{ x: 9540, y: 840 },
{ x: 9610, y: 840 },
{ x: 9680, y: 840 },
{ x: 9750, y: 840 },
{ x: 9820, y: 840 },
{ x: 9890, y: 840 },
{ x: 9960, y: 840 },
{ x: 10030, y: 840 },
{ x: 10100, y: 840 },
{ x: 10170, y: 840 },
{ x: 10240, y: 840 },
{ x: 10310, y: 840 },
{ x: 10380, y: 840 },
{ x: 10450, y: 840 },
{ x: 10520, y: 840 },
{ x: 10590, y: 840 },
{ x: 10660, y: 840 },
{ x: 10730, y: 840 },
{ x: 10800, y: 840 },
{ x: 10870, y: 840 },
{ x: 10940, y: 840 },
{ x: 11010, y: 840 },
{ x: 11080, y: 840 },
{ x: 11150, y: 840 },
{ x: 11220, y: 840 },
{ x: 11290, y: 840 },
{ x: 11360, y: 840 },
{ x: 11430, y: 840 },
{ x: 11500, y: 840 },
{ x: 11570, y: 840 },
{ x: 11640, y: 840 },
{ x: 11710, y: 840 },
{ x: 11780, y: 840 },
{ x: 11808, y: 840 },
{ x: 11878, y: 836 },
{ x: 11948, y: 822 },
{ x: 12018, y: 800 },
{ x: 12088, y: 768 },
{ x: 12158, y: 728 },
{ x: 12200, y: 700 },
{ x: 12270, y: 700 },
{ x: 1070, y: 842 },
{ x: 1140, y: 846 },
{ x: 1210, y: 850 },
{ x: 1280, y: 858 },
{ x: 1350, y: 868 },
{ x: 1420, y: 878 },
{ x: 1490, y: 890 },
{ x: 1560, y: 904 },
{ x: 1630, y: 916 },
{ x: 1700, y: 930 },
{ x: 1770, y: 944 },
{ x: 1840, y: 956 },
{ x: 1910, y: 970 },
{ x: 1980, y: 982 },
{ x: 2050, y: 992 },
{ x: 2120, y: 1002 },
{ x: 2190, y: 1010 },
{ x: 2260, y: 1014 },
{ x: 2330, y: 1018 },
{ x: 2400, y: 1020 },
{ x: 2417, y: 1020 },
{ x: 2433, y: 1020 },
{ x: 2450, y: 1022 },
{ x: 2467, y: 1022 },
{ x: 2483, y: 1022 },
{ x: 2500, y: 1022 },
{ x: 2517, y: 1020 },
{ x: 2533, y: 1020 },
{ x: 2550, y: 1020 },
{ x: 2567, y: 1018 },
{ x: 2583, y: 1016 },
{ x: 2600, y: 1016 },
{ x: 2617, y: 1014 },
{ x: 2633, y: 1010 },
{ x: 2650, y: 1008 },
{ x: 2667, y: 1006 },
{ x: 2683, y: 1002 },
{ x: 2700, y: 998 },
{ x: 2717, y: 994 },
{ x: 2733, y: 990 },
{ x: 2750, y: 986 },
{ x: 2767, y: 982 },
{ x: 2783, y: 976 },
{ x: 2800, y: 972 },
{ x: 2817, y: 966 },
{ x: 2833, y: 962 },
{ x: 2850, y: 958 },
{ x: 2867, y: 954 },
{ x: 2883, y: 954 },
{ x: 2900, y: 954 },
{ x: 2917, y: 958 },
{ x: 2933, y: 966 },
{ x: 2950, y: 974 },
{ x: 2967, y: 984 },
{ x: 2983, y: 996 },
{ x: 3000, y: 1008 },
{ x: 3017, y: 1020 },
{ x: 3033, y: 1032 },
{ x: 3050, y: 1042 },
{ x: 3067, y: 1052 },
{ x: 3083, y: 1058 },
{ x: 3100, y: 1062 },
{ x: 3117, y: 1064 },
{ x: 3133, y: 1064 },
{ x: 3150, y: 1060 },
{ x: 3167, y: 1056 },
{ x: 3183, y: 1050 },
{ x: 3200, y: 1044 },
{ x: 3217, y: 1038 },
{ x: 3233, y: 1032 },
{ x: 3250, y: 1026 },
{ x: 3267, y: 1024 },
{ x: 3283, y: 1024 },
{ x: 3300, y: 1024 },
{ x: 3317, y: 1028 },
{ x: 3333, y: 1034 },
{ x: 3350, y: 1044 },
{ x: 3367, y: 1054 },
{ x: 3383, y: 1064 },
{ x: 3400, y: 1076 },
{ x: 3417, y: 1088 },
{ x: 3433, y: 1100 },
{ x: 3450, y: 1110 },
{ x: 3467, y: 1118 },
{ x: 3483, y: 1124 },
{ x: 3500, y: 1126 },
{ x: 3517, y: 1128 },
{ x: 3533, y: 1126 },
{ x: 3550, y: 1122 },
{ x: 3567, y: 1116 },
{ x: 3583, y: 1108 },
{ x: 3600, y: 1100 },
{ x: 3617, y: 1092 },
{ x: 3633, y: 1086 },
{ x: 3650, y: 1078 },
{ x: 3667, y: 1074 },
{ x: 3683, y: 1072 },
{ x: 3700, y: 1072 },
{ x: 3717, y: 1074 },
{ x: 3733, y: 1078 },
{ x: 3750, y: 1084 },
{ x: 3767, y: 1092 },
{ x: 3783, y: 1100 },
{ x: 3800, y: 1110 },
{ x: 3817, y: 1120 },
{ x: 3833, y: 1130 },
{ x: 3850, y: 1140 },
{ x: 3867, y: 1148 },
{ x: 3883, y: 1156 },
{ x: 3900, y: 1164 },
{ x: 3917, y: 1172 },
{ x: 3933, y: 1178 },
{ x: 3950, y: 1182 },
{ x: 3967, y: 1186 },
{ x: 3983, y: 1190 },
{ x: 4000, y: 1192 },
{ x: 4017, y: 1194 },
{ x: 4033, y: 1196 },
{ x: 4050, y: 1196 },
{ x: 4067, y: 1194 },
{ x: 4083, y: 1192 },
{ x: 4100, y: 1190 },
{ x: 4117, y: 1186 },
{ x: 4133, y: 1182 },
{ x: 4150, y: 1176 },
{ x: 4167, y: 1172 },
{ x: 4183, y: 1166 },
{ x: 4200, y: 1158 },
{ x: 4217, y: 1152 },
{ x: 4233, y: 1144 },
{ x: 4250, y: 1138 },
{ x: 4267, y: 1132 },
{ x: 4283, y: 1130 },
{ x: 4300, y: 1128 },
{ x: 4317, y: 1130 },
{ x: 4333, y: 1132 },
{ x: 4350, y: 1138 },
{ x: 4367, y: 1144 },
{ x: 4383, y: 1152 },
{ x: 4400, y: 1160 },
{ x: 4417, y: 1168 },
{ x: 4433, y: 1176 },
{ x: 4450, y: 1182 },
{ x: 4467, y: 1186 },
{ x: 4483, y: 1190 },
{ x: 4500, y: 1192 },
{ x: 4517, y: 1190 },
{ x: 4533, y: 1186 },
{ x: 4550, y: 1182 },
{ x: 4567, y: 1176 },
{ x: 4583, y: 1168 },
{ x: 4600, y: 1160 },
{ x: 4617, y: 1152 },
{ x: 4633, y: 1144 },
{ x: 4650, y: 1138 },
{ x: 4667, y: 1132 },
{ x: 4683, y: 1130 },
{ x: 4700, y: 1128 },
{ x: 4717, y: 1130 },
{ x: 4733, y: 1132 },
{ x: 4750, y: 1138 },
{ x: 4767, y: 1144 },
{ x: 4783, y: 1152 },
{ x: 4800, y: 1160 },
{ x: 4817, y: 1168 },
{ x: 4833, y: 1176 },
{ x: 4850, y: 1182 },
{ x: 4867, y: 1186 },
{ x: 4883, y: 1190 },
{ x: 4900, y: 1192 },
{ x: 4917, y: 1190 },
{ x: 4933, y: 1186 },
{ x: 4950, y: 1182 },
{ x: 4967, y: 1176 },
{ x: 4983, y: 1168 },
{ x: 5000, y: 1160 },
{ x: 5017, y: 1152 },
{ x: 5033, y: 1144 },
{ x: 5050, y: 1138 },
{ x: 5067, y: 1132 },
{ x: 5083, y: 1130 },
{ x: 5100, y: 1128 },
{ x: 5117, y: 1130 },
{ x: 5133, y: 1132 },
{ x: 5150, y: 1138 },
{ x: 5167, y: 1144 },
{ x: 5183, y: 1152 },
{ x: 5200, y: 1160 },
{ x: 5217, y: 1168 },
{ x: 5233, y: 1176 },
{ x: 5250, y: 1184 },
{ x: 5267, y: 1190 },
{ x: 5283, y: 1196 },
{ x: 5300, y: 1202 },
{ x: 5317, y: 1208 },
{ x: 5333, y: 1212 },
{ x: 5350, y: 1216 },
{ x: 5367, y: 1218 },
{ x: 5383, y: 1220 },
{ x: 5400, y: 1222 },
{ x: 5417, y: 1222 },
{ x: 5433, y: 1220 },
{ x: 5450, y: 1218 },
{ x: 5467, y: 1216 },
{ x: 5483, y: 1212 },
{ x: 5500, y: 1208 },
{ x: 5517, y: 1202 },
{ x: 5533, y: 1196 },
{ x: 5550, y: 1190 },
{ x: 5567, y: 1182 },
{ x: 5583, y: 1174 },
{ x: 5600, y: 1166 },
{ x: 5617, y: 1158 },
{ x: 5633, y: 1148 },
{ x: 5650, y: 1140 },
{ x: 5667, y: 1132 },
{ x: 5683, y: 1126 },
{ x: 5700, y: 1124 },
{ x: 5717, y: 1122 },
{ x: 5733, y: 1122 },
{ x: 5750, y: 1126 },
{ x: 5767, y: 1130 },
{ x: 5783, y: 1134 },
{ x: 5800, y: 1140 },
{ x: 5817, y: 1146 },
{ x: 5833, y: 1150 },
{ x: 5850, y: 1154 },
{ x: 5867, y: 1156 },
{ x: 5883, y: 1158 },
{ x: 5900, y: 1156 },
{ x: 5917, y: 1152 },
{ x: 5933, y: 1146 },
{ x: 5950, y: 1140 },
{ x: 5967, y: 1130 },
{ x: 5983, y: 1120 },
{ x: 6000, y: 1110 },
{ x: 6017, y: 1098 },
{ x: 6033, y: 1088 },
{ x: 6050, y: 1080 },
{ x: 6067, y: 1072 },
{ x: 6083, y: 1066 },
{ x: 6100, y: 1064 },
{ x: 6117, y: 1062 },
{ x: 6133, y: 1064 },
{ x: 6150, y: 1066 },
{ x: 6167, y: 1070 },
{ x: 6183, y: 1076 },
{ x: 6200, y: 1082 },
{ x: 6217, y: 1088 },
{ x: 6233, y: 1092 },
{ x: 6250, y: 1098 },
{ x: 6267, y: 1100 },
{ x: 6283, y: 1102 },
{ x: 6300, y: 1100 },
{ x: 6317, y: 1098 },
{ x: 6333, y: 1094 },
{ x: 6350, y: 1086 },
{ x: 6367, y: 1078 },
{ x: 6383, y: 1070 },
{ x: 6400, y: 1060 },
{ x: 6417, y: 1052 },
{ x: 6433, y: 1042 },
{ x: 6450, y: 1036 },
{ x: 6467, y: 1030 },
{ x: 6483, y: 1026 },
{ x: 6500, y: 1024 },
{ x: 6517, y: 1024 },
{ x: 6533, y: 1026 },
{ x: 6550, y: 1032 },
{ x: 6567, y: 1038 },
{ x: 6583, y: 1044 },
{ x: 6600, y: 1052 },
{ x: 6600, y: 1054 },
{ x: 6670, y: 1038 },
{ x: 6740, y: 1022 },
{ x: 6810, y: 1006 },
{ x: 6880, y: 990 },
{ x: 6950, y: 974 },
{ x: 7020, y: 958 },
{ x: 7090, y: 942 },
{ x: 7160, y: 926 },
{ x: 7230, y: 910 },
{ x: 7300, y: 894 },
{ x: 7370, y: 878 },
{ x: 7440, y: 862 },
{ x: 7510, y: 846 },
{ x: 7580, y: 830 },
{ x: 7650, y: 814 },
{ x: 7720, y: 798 },
{ x: 7790, y: 782 },
{ x: 7860, y: 766 },
{ x: 7930, y: 750 },
{ x: 8000, y: 734 },
{ x: 8070, y: 716 },
{ x: 8140, y: 696 },
{ x: 8210, y: 668 },
{ x: 8280, y: 634 },
{ x: 8350, y: 596 },
{ x: 8420, y: 556 },
{ x: 8490, y: 514 },
{ x: 8560, y: 474 },
{ x: 8630, y: 434 },
{ x: 8700, y: 392 },
{ x: 8770, y: 352 },
{ x: 8840, y: 312 },
{ x: 8910, y: 270 },
{ x: 8980, y: 230 },
{ x: 9020, y: 208 },
{ x: 9020, y: 206 },
{ x: 9090, y: 206 },
] },
{ points: [
{ x: 13704, y: 1180 },
{ x: 13774, y: 1180 },
{ x: 13844, y: 1180 },
{ x: 13914, y: 1180 },
{ x: 13984, y: 1180 },
{ x: 14054, y: 1180 },
{ x: 14080, y: 1180 },
{ x: 14150, y: 1180 },
{ x: 14220, y: 1180 },
{ x: 14290, y: 1180 },
{ x: 14360, y: 1180 },
{ x: 14430, y: 1180 },
{ x: 14500, y: 1180 },
{ x: 14570, y: 1180 },
{ x: 14640, y: 1180 },
{ x: 14710, y: 1180 },
{ x: 14780, y: 1180 },
{ x: 14850, y: 1180 },
{ x: 14920, y: 1180 },
{ x: 14990, y: 1180 },
{ x: 15060, y: 1180 },
{ x: 15130, y: 1180 },
{ x: 15200, y: 1180 },
{ x: 15270, y: 1180 },
{ x: 15340, y: 1180 },
{ x: 15410, y: 1180 },
{ x: 15480, y: 1180 },
{ x: 15550, y: 1180 },
{ x: 15620, y: 1180 },
{ x: 15690, y: 1180 },
{ x: 15760, y: 1180 },
{ x: 15830, y: 1180 },
{ x: 15900, y: 1180 },
{ x: 15970, y: 1180 },
{ x: 16040, y: 1180 },
{ x: 16110, y: 1180 },
{ x: 16180, y: 1180 },
{ x: 16250, y: 1180 },
{ x: 16320, y: 1180 },
{ x: 16390, y: 1180 },
{ x: 16460, y: 1180 },
{ x: 16530, y: 1180 },
{ x: 16600, y: 1180 },
{ x: 16670, y: 1180 },
{ x: 16740, y: 1180 },
{ x: 16810, y: 1180 },
{ x: 16880, y: 1180 },
{ x: 10140, y: 686 },
{ x: 10210, y: 686 },
{ x: 10280, y: 686 },
{ x: 10350, y: 686 },
{ x: 10420, y: 686 },
{ x: 10437, y: 680 },
{ x: 10453, y: 672 },
{ x: 10470, y: 668 },
{ x: 10487, y: 664 },
{ x: 10503, y: 660 },
{ x: 10520, y: 660 },
{ x: 10537, y: 662 },
{ x: 10553, y: 666 },
{ x: 10570, y: 670 },
{ x: 10587, y: 678 },
{ x: 10603, y: 686 },
{ x: 10620, y: 694 },
{ x: 10637, y: 702 },
{ x: 10653, y: 712 },
{ x: 10670, y: 718 },
{ x: 10687, y: 724 },
{ x: 10703, y: 728 },
{ x: 10720, y: 732 },
{ x: 10737, y: 732 },
{ x: 10753, y: 730 },
{ x: 10770, y: 728 },
{ x: 10787, y: 724 },
{ x: 10803, y: 718 },
{ x: 10820, y: 714 },
{ x: 10837, y: 708 },
{ x: 10853, y: 702 },
{ x: 10870, y: 698 },
{ x: 10887, y: 696 },
{ x: 10903, y: 696 },
{ x: 10920, y: 696 },
{ x: 10937, y: 700 },
{ x: 10953, y: 706 },
{ x: 10970, y: 712 },
{ x: 10987, y: 720 },
{ x: 11003, y: 730 },
{ x: 11020, y: 738 },
{ x: 11037, y: 748 },
{ x: 11053, y: 758 },
{ x: 11070, y: 766 },
{ x: 11087, y: 772 },
{ x: 11103, y: 778 },
{ x: 11120, y: 782 },
{ x: 11137, y: 782 },
{ x: 11153, y: 782 },
{ x: 11170, y: 780 },
{ x: 11187, y: 776 },
{ x: 11203, y: 772 },
{ x: 11220, y: 766 },
{ x: 11237, y: 762 },
{ x: 11253, y: 758 },
{ x: 11270, y: 754 },
{ x: 11287, y: 752 },
{ x: 11303, y: 750 },
{ x: 11320, y: 752 },
{ x: 11337, y: 754 },
{ x: 11353, y: 760 },
{ x: 11370, y: 766 },
{ x: 11387, y: 774 },
{ x: 11403, y: 784 },
{ x: 11420, y: 792 },
{ x: 11437, y: 802 },
{ x: 11453, y: 810 },
{ x: 11470, y: 818 },
{ x: 11487, y: 824 },
{ x: 11503, y: 830 },
{ x: 11520, y: 832 },
{ x: 11537, y: 832 },
{ x: 11553, y: 832 },
{ x: 11570, y: 828 },
{ x: 11587, y: 824 },
{ x: 11603, y: 818 },
{ x: 11620, y: 812 },
{ x: 11637, y: 806 },
{ x: 11653, y: 800 },
{ x: 11670, y: 794 },
{ x: 11687, y: 790 },
{ x: 11703, y: 788 },
{ x: 11720, y: 788 },
{ x: 11737, y: 790 },
{ x: 11753, y: 794 },
{ x: 11770, y: 798 },
{ x: 11787, y: 804 },
{ x: 11803, y: 812 },
{ x: 11820, y: 818 },
{ x: 11890, y: 818 },
{ x: 11960, y: 818 },
{ x: 12030, y: 818 },
{ x: 12100, y: 818 },
{ x: 12170, y: 818 },
{ x: 12240, y: 818 },
{ x: 12310, y: 818 },
{ x: 12380, y: 818 },
{ x: 12450, y: 818 },
{ x: 12520, y: 818 },
{ x: 12590, y: 818 },
{ x: 12660, y: 818 },
{ x: 12730, y: 818 },
{ x: 12800, y: 818 },
{ x: 12870, y: 818 },
{ x: 12940, y: 818 },
{ x: 13010, y: 818 },
{ x: 13080, y: 818 },
{ x: 13150, y: 818 },
{ x: 13220, y: 818 },
{ x: 13290, y: 818 },
{ x: 13360, y: 818 },
{ x: 13430, y: 818 },
{ x: 13500, y: 818 },
{ x: 13570, y: 818 },
{ x: 13640, y: 818 },
{ x: 13710, y: 818 },
{ x: 13780, y: 818 },
{ x: 13850, y: 818 },
{ x: 13920, y: 818 },
{ x: 13990, y: 818 },
{ x: 14060, y: 818 },
{ x: 14130, y: 818 },
{ x: 14200, y: 818 },
{ x: 14270, y: 818 },
{ x: 14340, y: 818 },
{ x: 14410, y: 818 },
{ x: 14480, y: 818 },
{ x: 14550, y: 818 },
{ x: 14620, y: 818 },
] },
],
obstacles: [],
goal: { x: 16640, y: 940, width: 280, height: 640 },
cameraBounds: { x: -600, y: -100, width: 18280, height: 2080 },
items: [
{ type: "cone", mode: "gravity", x: 11988, y: 573 },
{ type: "cone", mode: "gravity", x: 12238, y: 556 },
{ type: "cone", mode: "gravity", x: 12413, y: 564 },
{ type: "cone", mode: "gravity", x: 12679, y: 614 },
{ type: "tv", mode: "gravity", x: 13538, y: 698 },
],
bonusItems: [
{ type: "cash", x: 3588, y: 689 },
{ type: "guitar", x: 10054, y: 206 },
],
goal: { x: 14380, y: 579, width: 280, height: 640 },
cameraBounds: { x: -600, y: -594, width: 16020, height: 2616 },
};

View File

@ -1,11 +1,10 @@
// Scaffolding level - terrain is a copy of level06.
// Build out the real layout in editor.html, then replace this file.
// Generated by editor.html - review before dropping into src/data/levels/.
export default {
id: "level09",
name: "The Switchback",
description: "Left, right, left, right. Stay calm.",
name: "Halfpipe Highway",
description: "Hold on for dear life kids!",
kidsAboard: 3,
timeLimit: 41,
timeLimit: 42,
startPosition: { x: 200, y: 700 },
startAngle: 0,
terrain: [
@ -51,200 +50,273 @@ export default {
{ x: 2260, y: 840 },
{ x: 2330, y: 840 },
{ x: 2400, y: 840 },
{ x: 2470, y: 840 },
{ x: 2540, y: 840 },
{ x: 2610, y: 840 },
{ x: 2680, y: 840 },
{ x: 2750, y: 840 },
{ x: 2820, y: 840 },
{ x: 2890, y: 840 },
{ x: 2960, y: 840 },
{ x: 3030, y: 840 },
{ x: 3100, y: 840 },
{ x: 3170, y: 840 },
{ x: 3240, y: 840 },
{ x: 3310, y: 840 },
{ x: 3380, y: 840 },
{ x: 3450, y: 840 },
{ x: 3520, y: 840 },
{ x: 3590, y: 840 },
{ x: 3660, y: 840 },
{ x: 3730, y: 840 },
{ x: 3800, y: 840 },
{ x: 3870, y: 842 },
{ x: 3940, y: 846 },
{ x: 4010, y: 850 },
{ x: 4080, y: 858 },
{ x: 4150, y: 868 },
{ x: 4220, y: 878 },
{ x: 4290, y: 890 },
{ x: 4360, y: 904 },
{ x: 4430, y: 916 },
{ x: 4500, y: 930 },
{ x: 4570, y: 944 },
{ x: 4640, y: 956 },
{ x: 4710, y: 970 },
{ x: 4780, y: 982 },
{ x: 4850, y: 992 },
{ x: 4920, y: 1002 },
{ x: 4990, y: 1010 },
{ x: 5060, y: 1014 },
{ x: 5130, y: 1018 },
{ x: 5200, y: 1020 },
{ x: 5270, y: 1020 },
{ x: 5340, y: 1020 },
{ x: 5410, y: 1020 },
{ x: 5480, y: 1020 },
{ x: 5550, y: 1020 },
{ x: 5620, y: 1020 },
{ x: 5690, y: 1020 },
{ x: 5760, y: 1020 },
{ x: 5830, y: 1020 },
{ x: 5900, y: 1020 },
{ x: 5970, y: 1020 },
{ x: 6040, y: 1020 },
{ x: 6110, y: 1020 },
{ x: 6180, y: 1020 },
{ x: 6250, y: 1020 },
{ x: 6320, y: 1020 },
{ x: 6390, y: 1020 },
{ x: 6460, y: 1020 },
{ x: 6530, y: 1020 },
{ x: 6600, y: 1020 },
{ x: 6670, y: 1018 },
{ x: 6740, y: 1014 },
{ x: 6810, y: 1010 },
{ x: 6880, y: 1002 },
{ x: 6950, y: 992 },
{ x: 7020, y: 982 },
{ x: 7090, y: 970 },
{ x: 7160, y: 956 },
{ x: 7230, y: 944 },
{ x: 7300, y: 930 },
{ x: 7370, y: 916 },
{ x: 7440, y: 904 },
{ x: 7510, y: 890 },
{ x: 7580, y: 878 },
{ x: 7650, y: 868 },
{ x: 7720, y: 858 },
{ x: 7790, y: 850 },
{ x: 7860, y: 846 },
{ x: 7930, y: 842 },
{ x: 8000, y: 840 },
{ x: 8070, y: 840 },
{ x: 8140, y: 840 },
{ x: 8210, y: 840 },
{ x: 8280, y: 840 },
{ x: 8350, y: 840 },
{ x: 8420, y: 840 },
{ x: 8490, y: 840 },
{ x: 8560, y: 840 },
{ x: 8630, y: 840 },
{ x: 8700, y: 840 },
{ x: 8770, y: 840 },
{ x: 8840, y: 840 },
{ x: 8910, y: 840 },
{ x: 8980, y: 840 },
{ x: 9050, y: 840 },
{ x: 9120, y: 840 },
{ x: 9190, y: 840 },
{ x: 9260, y: 840 },
{ x: 9330, y: 840 },
{ x: 9400, y: 840 },
{ x: 9470, y: 840 },
{ x: 9540, y: 840 },
{ x: 9610, y: 840 },
{ x: 9680, y: 840 },
{ x: 9750, y: 840 },
{ x: 9820, y: 840 },
{ x: 9890, y: 840 },
{ x: 9960, y: 840 },
{ x: 10030, y: 840 },
{ x: 10100, y: 840 },
{ x: 10170, y: 840 },
{ x: 10240, y: 840 },
{ x: 10310, y: 840 },
{ x: 10380, y: 840 },
{ x: 10450, y: 840 },
{ x: 10520, y: 840 },
{ x: 10590, y: 840 },
{ x: 10660, y: 840 },
{ x: 10730, y: 840 },
{ x: 10800, y: 840 },
{ x: 10870, y: 840 },
{ x: 10940, y: 840 },
{ x: 11010, y: 840 },
{ x: 11080, y: 840 },
{ x: 11150, y: 840 },
{ x: 11220, y: 840 },
{ x: 11290, y: 840 },
{ x: 11360, y: 840 },
{ x: 11430, y: 840 },
{ x: 11500, y: 840 },
{ x: 11570, y: 840 },
{ x: 11640, y: 840 },
{ x: 11710, y: 840 },
{ x: 11780, y: 840 },
{ x: 11808, y: 840 },
{ x: 11878, y: 836 },
{ x: 11948, y: 822 },
{ x: 12018, y: 800 },
{ x: 12088, y: 768 },
{ x: 12158, y: 728 },
{ x: 12200, y: 700 },
{ x: 12270, y: 700 },
] },
{ points: [
{ x: 13704, y: 1180 },
{ x: 13774, y: 1180 },
{ x: 13844, y: 1180 },
{ x: 13914, y: 1180 },
{ x: 13984, y: 1180 },
{ x: 14054, y: 1180 },
{ x: 14080, y: 1180 },
{ x: 14150, y: 1180 },
{ x: 14220, y: 1180 },
{ x: 14290, y: 1180 },
{ x: 14360, y: 1180 },
{ x: 14430, y: 1180 },
{ x: 14500, y: 1180 },
{ x: 14570, y: 1180 },
{ x: 14640, y: 1180 },
{ x: 14710, y: 1180 },
{ x: 14780, y: 1180 },
{ x: 14850, y: 1180 },
{ x: 14920, y: 1180 },
{ x: 14990, y: 1180 },
{ x: 15060, y: 1180 },
{ x: 15130, y: 1180 },
{ x: 15200, y: 1180 },
{ x: 15270, y: 1180 },
{ x: 15340, y: 1180 },
{ x: 15410, y: 1180 },
{ x: 15480, y: 1180 },
{ x: 15550, y: 1180 },
{ x: 15620, y: 1180 },
{ x: 15690, y: 1180 },
{ x: 15760, y: 1180 },
{ x: 15830, y: 1180 },
{ x: 15900, y: 1180 },
{ x: 15970, y: 1180 },
{ x: 16040, y: 1180 },
{ x: 16110, y: 1180 },
{ x: 16180, y: 1180 },
{ x: 16250, y: 1180 },
{ x: 16320, y: 1180 },
{ x: 16390, y: 1180 },
{ x: 16460, y: 1180 },
{ x: 16530, y: 1180 },
{ x: 16600, y: 1180 },
{ x: 16670, y: 1180 },
{ x: 16740, y: 1180 },
{ x: 16810, y: 1180 },
{ x: 16880, y: 1180 },
{ x: 2470, y: 842 },
{ x: 2540, y: 848 },
{ x: 2610, y: 862 },
{ x: 2680, y: 884 },
{ x: 2750, y: 918 },
{ x: 2820, y: 956 },
{ x: 2890, y: 996 },
{ x: 2960, y: 1036 },
{ x: 3030, y: 1076 },
{ x: 3100, y: 1116 },
{ x: 3170, y: 1156 },
{ x: 3240, y: 1196 },
{ x: 3310, y: 1236 },
{ x: 3380, y: 1276 },
{ x: 3450, y: 1316 },
{ x: 3520, y: 1356 },
{ x: 3590, y: 1396 },
{ x: 3660, y: 1436 },
{ x: 3730, y: 1476 },
{ x: 3800, y: 1516 },
{ x: 3870, y: 1562 },
{ x: 3940, y: 1632 },
{ x: 4010, y: 1730 },
{ x: 4080, y: 1842 },
{ x: 4150, y: 1954 },
{ x: 4220, y: 2066 },
{ x: 4290, y: 2178 },
{ x: 4300, y: 2194 },
{ x: 4306, y: 2204 },
{ x: 4322, y: 2230 },
{ x: 4348, y: 2270 },
{ x: 4384, y: 2322 },
{ x: 4428, y: 2382 },
{ x: 4480, y: 2440 },
{ x: 4540, y: 2494 },
{ x: 4606, y: 2540 },
{ x: 4678, y: 2578 },
{ x: 4756, y: 2612 },
{ x: 4838, y: 2642 },
{ x: 4924, y: 2670 },
{ x: 5012, y: 2692 },
{ x: 5104, y: 2710 },
{ x: 5198, y: 2726 },
{ x: 5292, y: 2736 },
{ x: 5386, y: 2744 },
{ x: 5480, y: 2748 },
{ x: 5574, y: 2748 },
{ x: 5664, y: 2746 },
{ x: 5754, y: 2740 },
{ x: 5840, y: 2730 },
{ x: 5922, y: 2718 },
{ x: 6000, y: 2702 },
{ x: 6072, y: 2684 },
{ x: 6140, y: 2664 },
{ x: 6200, y: 2640 },
{ x: 6254, y: 2612 },
{ x: 6298, y: 2584 },
{ x: 6336, y: 2552 },
{ x: 6362, y: 2518 },
{ x: 6380, y: 2482 },
{ x: 6450, y: 2352 },
{ x: 6520, y: 2250 },
{ x: 6590, y: 2180 },
{ x: 6660, y: 2136 },
{ x: 6730, y: 2108 },
{ x: 6800, y: 2090 },
{ x: 6870, y: 2074 },
{ x: 6940, y: 2058 },
{ x: 7010, y: 2042 },
{ x: 7080, y: 2026 },
{ x: 7150, y: 2010 },
{ x: 7220, y: 1994 },
{ x: 7290, y: 1978 },
{ x: 7360, y: 1962 },
{ x: 7430, y: 1946 },
{ x: 7500, y: 1930 },
{ x: 7570, y: 1914 },
{ x: 7640, y: 1898 },
{ x: 7710, y: 1882 },
{ x: 7780, y: 1866 },
{ x: 7850, y: 1860 },
{ x: 7920, y: 1880 },
{ x: 7990, y: 1936 },
{ x: 8060, y: 2006 },
{ x: 8130, y: 2074 },
{ x: 8200, y: 2144 },
{ x: 8270, y: 2214 },
{ x: 8280, y: 2224 },
{ x: 8284, y: 2228 },
{ x: 8294, y: 2238 },
{ x: 8312, y: 2256 },
{ x: 8334, y: 2278 },
{ x: 8364, y: 2306 },
{ x: 8398, y: 2336 },
{ x: 8438, y: 2370 },
{ x: 8480, y: 2400 },
{ x: 8528, y: 2432 },
{ x: 8578, y: 2460 },
{ x: 8632, y: 2484 },
{ x: 8688, y: 2508 },
{ x: 8748, y: 2528 },
{ x: 8808, y: 2546 },
{ x: 8870, y: 2562 },
{ x: 8932, y: 2572 },
{ x: 8994, y: 2580 },
{ x: 9056, y: 2584 },
{ x: 9118, y: 2584 },
{ x: 9180, y: 2582 },
{ x: 9240, y: 2576 },
{ x: 9298, y: 2566 },
{ x: 9354, y: 2554 },
{ x: 9406, y: 2538 },
{ x: 9456, y: 2520 },
{ x: 9502, y: 2498 },
{ x: 9544, y: 2476 },
{ x: 9582, y: 2448 },
{ x: 9616, y: 2420 },
{ x: 9642, y: 2388 },
{ x: 9664, y: 2354 },
{ x: 9680, y: 2318 },
{ x: 9696, y: 2282 },
{ x: 9718, y: 2234 },
{ x: 9744, y: 2182 },
{ x: 9778, y: 2120 },
{ x: 9816, y: 2064 },
{ x: 9858, y: 2016 },
{ x: 9904, y: 1976 },
{ x: 9954, y: 1946 },
{ x: 10006, y: 1924 },
{ x: 10062, y: 1910 },
{ x: 10120, y: 1900 },
{ x: 10180, y: 1894 },
{ x: 10242, y: 1892 },
{ x: 10304, y: 1892 },
{ x: 10366, y: 1896 },
{ x: 10428, y: 1904 },
{ x: 10490, y: 1914 },
{ x: 10552, y: 1930 },
{ x: 10612, y: 1948 },
{ x: 10672, y: 1972 },
{ x: 10728, y: 1998 },
{ x: 10782, y: 2028 },
{ x: 10832, y: 2064 },
{ x: 10880, y: 2102 },
{ x: 10922, y: 2146 },
{ x: 10962, y: 2194 },
{ x: 10996, y: 2246 },
{ x: 11026, y: 2302 },
{ x: 11048, y: 2364 },
{ x: 11066, y: 2430 },
{ x: 11076, y: 2502 },
{ x: 11080, y: 2578 },
{ x: 11080, y: 2580 },
{ x: 11084, y: 2656 },
{ x: 11094, y: 2726 },
{ x: 11112, y: 2794 },
{ x: 11134, y: 2854 },
{ x: 11164, y: 2912 },
{ x: 11198, y: 2964 },
{ x: 11238, y: 3012 },
{ x: 11280, y: 3056 },
{ x: 11328, y: 3094 },
{ x: 11378, y: 3130 },
{ x: 11432, y: 3160 },
{ x: 11488, y: 3186 },
{ x: 11548, y: 3210 },
{ x: 11608, y: 3228 },
{ x: 11670, y: 3242 },
{ x: 11732, y: 3254 },
{ x: 11794, y: 3262 },
{ x: 11856, y: 3266 },
{ x: 11918, y: 3266 },
{ x: 11980, y: 3264 },
{ x: 12040, y: 3258 },
{ x: 12098, y: 3248 },
{ x: 12154, y: 3236 },
{ x: 12206, y: 3220 },
{ x: 12256, y: 3202 },
{ x: 12302, y: 3180 },
{ x: 12344, y: 3156 },
{ x: 12382, y: 3130 },
{ x: 12416, y: 3102 },
{ x: 12442, y: 3070 },
{ x: 12464, y: 3036 },
{ x: 12480, y: 3000 },
{ x: 12550, y: 2898 },
{ x: 12620, y: 2892 },
{ x: 12690, y: 2964 },
{ x: 12760, y: 3076 },
{ x: 12830, y: 3188 },
{ x: 12900, y: 3300 },
{ x: 12970, y: 3412 },
{ x: 12980, y: 3428 },
{ x: 13050, y: 3534 },
{ x: 13120, y: 3626 },
{ x: 13190, y: 3694 },
{ x: 13260, y: 3746 },
{ x: 13330, y: 3784 },
{ x: 13400, y: 3816 },
{ x: 13470, y: 3846 },
{ x: 13540, y: 3874 },
{ x: 13610, y: 3902 },
{ x: 13680, y: 3932 },
{ x: 13750, y: 3960 },
{ x: 13820, y: 3990 },
{ x: 13890, y: 4018 },
{ x: 13960, y: 4046 },
{ x: 14030, y: 4076 },
{ x: 14100, y: 4104 },
{ x: 14170, y: 4134 },
{ x: 14240, y: 4162 },
{ x: 14310, y: 4190 },
{ x: 14380, y: 4220 },
{ x: 14380, y: 4218 },
{ x: 14450, y: 4246 },
{ x: 14520, y: 4272 },
{ x: 14590, y: 4290 },
{ x: 14660, y: 4300 },
{ x: 14730, y: 4304 },
{ x: 14800, y: 4306 },
{ x: 14870, y: 4306 },
{ x: 14940, y: 4306 },
{ x: 15010, y: 4306 },
{ x: 15080, y: 4306 },
{ x: 15150, y: 4306 },
{ x: 15220, y: 4306 },
{ x: 15290, y: 4306 },
{ x: 15360, y: 4306 },
{ x: 15430, y: 4306 },
{ x: 15500, y: 4306 },
{ x: 15570, y: 4306 },
{ x: 15640, y: 4306 },
{ x: 15710, y: 4306 },
{ x: 15780, y: 4306 },
{ x: 15850, y: 4306 },
{ x: 15920, y: 4306 },
{ x: 15990, y: 4306 },
{ x: 16060, y: 4306 },
{ x: 16130, y: 4306 },
{ x: 16200, y: 4306 },
{ x: 16270, y: 4306 },
{ x: 16340, y: 4306 },
{ x: 16410, y: 4306 },
{ x: 16480, y: 4306 },
{ x: 16550, y: 4306 },
{ x: 16620, y: 4306 },
{ x: 16690, y: 4306 },
{ x: 16760, y: 4306 },
{ x: 16830, y: 4306 },
{ x: 16900, y: 4306 },
{ x: 16970, y: 4306 },
{ x: 17040, y: 4306 },
{ x: 17110, y: 4306 },
{ x: 17180, y: 4306 },
] },
],
obstacles: [],
goal: { x: 16640, y: 940, width: 280, height: 640 },
cameraBounds: { x: -600, y: -100, width: 18280, height: 2080 },
items: [
{ type: "tv", mode: "gravity", x: 16079, y: 4033 },
{ type: "tv", mode: "gravity", x: 16288, y: 3992 },
{ type: "tv", mode: "gravity", x: 16613, y: 3933 },
],
bonusItems: [
{ type: "ctc", x: 7188, y: 1808 },
{ type: "guitar", x: 10396, y: 1708 },
{ type: "cash", x: 12654, y: 2583 },
],
goal: { x: 16940, y: 4067, width: 280, height: 640 },
cameraBounds: { x: -600, y: 0, width: 18580, height: 5106 },
};

File diff suppressed because it is too large Load Diff

899
src/data/levels/level11.js Normal file
View File

@ -0,0 +1,899 @@
// Generated by editor.html - review before dropping into src/data/levels/.
export default {
id: "level11",
name: "Uh oh!",
description: "What have I gotten myself into?",
kidsAboard: 3,
timeLimit: 119,
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: 842 },
{ x: 1140, y: 846 },
{ x: 1210, y: 850 },
{ x: 1280, y: 858 },
{ x: 1350, y: 868 },
{ x: 1420, y: 878 },
{ x: 1490, y: 890 },
{ x: 1560, y: 904 },
{ x: 1630, y: 916 },
{ x: 1700, y: 930 },
{ x: 1770, y: 944 },
{ x: 1840, y: 956 },
{ x: 1910, y: 970 },
{ x: 1980, y: 982 },
{ x: 2050, y: 992 },
{ x: 2120, y: 1002 },
{ x: 2190, y: 1010 },
{ x: 2260, y: 1014 },
{ x: 2330, y: 1018 },
{ x: 2400, y: 1020 },
{ x: 2470, y: 1022 },
{ x: 2540, y: 1026 },
{ x: 2610, y: 1030 },
{ x: 2680, y: 1038 },
{ x: 2750, y: 1048 },
{ x: 2820, y: 1058 },
{ x: 2890, y: 1070 },
{ x: 2960, y: 1084 },
{ x: 3030, y: 1096 },
{ x: 3100, y: 1110 },
{ x: 3170, y: 1124 },
{ x: 3240, y: 1136 },
{ x: 3310, y: 1150 },
{ x: 3380, y: 1162 },
{ x: 3450, y: 1172 },
{ x: 3520, y: 1182 },
{ x: 3590, y: 1190 },
{ x: 3660, y: 1194 },
{ x: 3730, y: 1198 },
{ x: 3800, y: 1200 },
{ x: 3870, y: 1200 },
{ x: 3940, y: 1200 },
{ x: 4010, y: 1200 },
{ x: 4080, y: 1200 },
{ x: 4150, y: 1200 },
{ x: 4220, y: 1200 },
{ x: 4290, y: 1200 },
{ x: 4360, y: 1200 },
{ x: 4430, y: 1200 },
{ x: 4500, y: 1200 },
{ x: 4570, y: 1200 },
{ x: 4640, y: 1200 },
{ x: 4710, y: 1200 },
{ x: 4780, y: 1200 },
{ x: 4850, y: 1200 },
{ x: 4920, y: 1200 },
{ x: 4990, y: 1200 },
{ x: 5060, y: 1200 },
{ x: 5130, y: 1200 },
{ x: 5200, y: 1200 },
{ x: 5270, y: 1202 },
{ x: 5340, y: 1206 },
{ x: 5410, y: 1218 },
{ x: 5480, y: 1238 },
{ x: 5550, y: 1264 },
{ x: 5620, y: 1296 },
{ x: 5690, y: 1330 },
{ x: 5760, y: 1362 },
{ x: 5830, y: 1396 },
{ x: 5900, y: 1428 },
{ x: 5970, y: 1460 },
{ x: 6040, y: 1494 },
{ x: 6110, y: 1526 },
{ x: 6180, y: 1560 },
{ x: 6250, y: 1592 },
{ x: 6320, y: 1624 },
{ x: 6390, y: 1658 },
{ x: 6460, y: 1690 },
{ x: 6530, y: 1724 },
{ x: 6600, y: 1756 },
{ x: 6670, y: 1788 },
{ x: 6740, y: 1818 },
{ x: 6810, y: 1848 },
{ x: 6880, y: 1878 },
{ x: 6950, y: 1904 },
{ x: 7020, y: 1930 },
{ x: 7090, y: 1956 },
{ x: 7160, y: 1982 },
{ x: 7230, y: 2006 },
{ x: 7300, y: 2030 },
{ x: 7370, y: 2054 },
{ x: 7440, y: 2078 },
{ x: 7510, y: 2102 },
{ x: 7580, y: 2128 },
{ x: 7650, y: 2152 },
{ x: 7720, y: 2176 },
{ x: 7790, y: 2200 },
{ x: 7860, y: 2224 },
{ x: 7930, y: 2250 },
{ x: 8000, y: 2274 },
{ x: 8070, y: 2298 },
{ x: 8140, y: 2322 },
{ x: 8210, y: 2346 },
{ x: 8280, y: 2370 },
{ x: 8350, y: 2396 },
{ x: 8420, y: 2420 },
{ x: 8490, y: 2444 },
{ x: 8560, y: 2468 },
{ x: 8630, y: 2492 },
{ x: 8700, y: 2516 },
{ x: 8770, y: 2542 },
{ x: 8840, y: 2566 },
{ x: 8900, y: 2586 },
{ x: 8970, y: 2606 },
{ x: 9040, y: 2618 },
{ x: 9110, y: 2614 },
{ x: 9180, y: 2594 },
{ x: 9250, y: 2556 },
{ x: 9320, y: 2508 },
{ x: 9390, y: 2456 },
{ x: 9460, y: 2404 },
{ x: 9530, y: 2404 },
] },
{ points: [
{ x: 9880, y: 2346 },
{ x: 9950, y: 2364 },
{ x: 10020, y: 2408 },
{ x: 10090, y: 2466 },
{ x: 10160, y: 2524 },
{ x: 10230, y: 2568 },
{ x: 10300, y: 2586 },
{ x: 10370, y: 2604 },
{ x: 10440, y: 2626 },
{ x: 10510, y: 2654 },
{ x: 10580, y: 2686 },
{ x: 10650, y: 2722 },
{ x: 10720, y: 2760 },
{ x: 10790, y: 2798 },
{ x: 10860, y: 2834 },
{ x: 10930, y: 2872 },
{ x: 11000, y: 2910 },
{ x: 11070, y: 2946 },
{ x: 11140, y: 2984 },
{ x: 11210, y: 3022 },
{ x: 11280, y: 3058 },
{ x: 11350, y: 3096 },
{ x: 11420, y: 3134 },
{ x: 11440, y: 3144 },
{ x: 11510, y: 3178 },
{ x: 11580, y: 3206 },
{ x: 11650, y: 3226 },
{ x: 11720, y: 3240 },
{ x: 11790, y: 3244 },
{ x: 11860, y: 3246 },
{ x: 11930, y: 3246 },
{ x: 12000, y: 3246 },
{ x: 12070, y: 3246 },
{ x: 12140, y: 3246 },
{ x: 12210, y: 3246 },
{ x: 12280, y: 3246 },
{ x: 12350, y: 3246 },
{ x: 12420, y: 3246 },
{ x: 12448, y: 3246 },
{ x: 12518, y: 3242 },
{ x: 12588, y: 3228 },
{ x: 12658, y: 3206 },
{ x: 12728, y: 3174 },
{ x: 12798, y: 3134 },
{ x: 12840, y: 3106 },
{ x: 12842, y: 3182 },
{ x: 12844, y: 3254 },
{ x: 12850, y: 3320 },
{ x: 12856, y: 3382 },
{ x: 12864, y: 3438 },
{ x: 12876, y: 3490 },
{ x: 12886, y: 3538 },
{ x: 12900, y: 3582 },
{ x: 12914, y: 3622 },
{ x: 12930, y: 3656 },
{ x: 12946, y: 3686 },
{ x: 12964, y: 3714 },
{ x: 12982, y: 3736 },
{ x: 13002, y: 3754 },
{ x: 13022, y: 3770 },
{ x: 13042, y: 3780 },
{ x: 13062, y: 3788 },
{ x: 13082, y: 3792 },
{ x: 13104, y: 3792 },
{ x: 13124, y: 3790 },
{ x: 13146, y: 3784 },
{ x: 13166, y: 3774 },
{ x: 13188, y: 3762 },
{ x: 13208, y: 3748 },
{ x: 13226, y: 3728 },
{ x: 13246, y: 3708 },
{ x: 13264, y: 3684 },
{ x: 13282, y: 3658 },
{ x: 13298, y: 3628 },
{ x: 13312, y: 3596 },
{ x: 13328, y: 3562 },
{ x: 13340, y: 3526 },
{ x: 13354, y: 3484 },
{ x: 13372, y: 3434 },
{ x: 13392, y: 3384 },
{ x: 13416, y: 3332 },
{ x: 13442, y: 3288 },
{ x: 13472, y: 3250 },
{ x: 13502, y: 3222 },
{ x: 13536, y: 3198 },
{ x: 13570, y: 3182 },
{ x: 13606, y: 3170 },
{ x: 13644, y: 3162 },
{ x: 13682, y: 3156 },
{ x: 13720, y: 3154 },
{ x: 13760, y: 3154 },
{ x: 13800, y: 3158 },
{ x: 13838, y: 3166 },
{ x: 13878, y: 3176 },
{ x: 13916, y: 3192 },
{ x: 13954, y: 3210 },
{ x: 13990, y: 3232 },
{ x: 14024, y: 3260 },
{ x: 14058, y: 3290 },
{ x: 14088, y: 3324 },
{ x: 14118, y: 3364 },
{ x: 14144, y: 3408 },
{ x: 14168, y: 3456 },
{ x: 14188, y: 3508 },
{ x: 14206, y: 3564 },
{ x: 14220, y: 3626 },
{ x: 14232, y: 3692 },
{ x: 14238, y: 3764 },
{ x: 14240, y: 3840 },
{ x: 14310, y: 3880 },
{ x: 14380, y: 3920 },
{ x: 14450, y: 3960 },
{ x: 14520, y: 4000 },
{ x: 14590, y: 4040 },
{ x: 14660, y: 4080 },
{ x: 14730, y: 4120 },
{ x: 14800, y: 4160 },
{ x: 14870, y: 4200 },
{ x: 14940, y: 4240 },
{ x: 15010, y: 4280 },
{ x: 15080, y: 4320 },
{ x: 15150, y: 4360 },
{ x: 15220, y: 4400 },
{ x: 15290, y: 4440 },
{ x: 15360, y: 4480 },
{ x: 15430, y: 4520 },
{ x: 15500, y: 4560 },
{ x: 15570, y: 4600 },
{ x: 15640, y: 4640 },
{ x: 15710, y: 4678 },
{ x: 15780, y: 4710 },
{ x: 15850, y: 4734 },
{ x: 15920, y: 4748 },
{ x: 15990, y: 4754 },
{ x: 16060, y: 4756 },
{ x: 16130, y: 4756 },
{ x: 16200, y: 4756 },
{ x: 16270, y: 4756 },
{ x: 16340, y: 4756 },
{ x: 16410, y: 4756 },
{ x: 16480, y: 4756 },
{ x: 16550, y: 4756 },
{ x: 16620, y: 4756 },
{ x: 16690, y: 4756 },
{ x: 16760, y: 4756 },
{ x: 16830, y: 4756 },
{ x: 16900, y: 4756 },
{ x: 16970, y: 4756 },
{ x: 17040, y: 4756 },
{ x: 17110, y: 4756 },
{ x: 17180, y: 4754 },
{ x: 17250, y: 4750 },
{ x: 17320, y: 4742 },
{ x: 17390, y: 4732 },
{ x: 17460, y: 4720 },
{ x: 17530, y: 4708 },
{ x: 17600, y: 4696 },
{ x: 17670, y: 4682 },
{ x: 17740, y: 4670 },
{ x: 17810, y: 4656 },
{ x: 17880, y: 4642 },
{ x: 17950, y: 4630 },
{ x: 18020, y: 4618 },
{ x: 18090, y: 4608 },
{ x: 18160, y: 4598 },
{ x: 18230, y: 4590 },
{ x: 18300, y: 4584 },
{ x: 18370, y: 4580 },
{ x: 18440, y: 4580 },
{ x: 18510, y: 4580 },
{ x: 18580, y: 4574 },
{ x: 18650, y: 4564 },
{ x: 18720, y: 4548 },
{ x: 18790, y: 4526 },
{ x: 18860, y: 4500 },
{ x: 18930, y: 4474 },
{ x: 19000, y: 4446 },
{ x: 19070, y: 4418 },
{ x: 19140, y: 4392 },
{ x: 19210, y: 4364 },
{ x: 19280, y: 4338 },
{ x: 19350, y: 4310 },
{ x: 19420, y: 4282 },
{ x: 19490, y: 4256 },
{ x: 19560, y: 4228 },
{ x: 19630, y: 4202 },
{ x: 19700, y: 4174 },
{ x: 19770, y: 4146 },
{ x: 19840, y: 4120 },
{ x: 19910, y: 4096 },
{ x: 19980, y: 4074 },
{ x: 20050, y: 4058 },
{ x: 20120, y: 4048 },
{ x: 20190, y: 4044 },
{ x: 20260, y: 4044 },
{ x: 20330, y: 4044 },
{ x: 20400, y: 4044 },
{ x: 20470, y: 4044 },
{ x: 20540, y: 4044 },
{ x: 20610, y: 4044 },
{ x: 20680, y: 4044 },
{ x: 20750, y: 4044 },
{ x: 20820, y: 4044 },
{ x: 20848, y: 4044 },
{ x: 20918, y: 4040 },
{ x: 20988, y: 4026 },
{ x: 21058, y: 4004 },
{ x: 21128, y: 3972 },
{ x: 21198, y: 3932 },
{ x: 21240, y: 3904 },
{ x: 21310, y: 3904 },
] },
{ points: [
{ x: 21800, y: 3904 },
{ x: 21870, y: 3904 },
{ x: 21940, y: 3904 },
{ x: 22010, y: 3904 },
{ x: 22080, y: 3904 },
{ x: 22150, y: 3904 },
{ x: 22220, y: 3904 },
{ x: 22290, y: 3904 },
{ x: 22360, y: 3904 },
{ x: 22430, y: 3904 },
{ x: 22500, y: 3904 },
{ x: 22570, y: 3904 },
{ x: 22640, y: 3904 },
{ x: 22710, y: 3888 },
{ x: 22780, y: 3872 },
{ x: 22850, y: 3856 },
{ x: 22920, y: 3840 },
{ x: 22990, y: 3824 },
{ x: 23060, y: 3808 },
{ x: 23130, y: 3792 },
{ x: 23200, y: 3776 },
{ x: 23270, y: 3760 },
{ x: 23340, y: 3744 },
{ x: 23410, y: 3728 },
{ x: 23480, y: 3712 },
{ x: 23550, y: 3696 },
{ x: 23620, y: 3680 },
{ x: 23690, y: 3664 },
{ x: 23760, y: 3648 },
{ x: 23830, y: 3632 },
{ x: 23900, y: 3616 },
{ x: 23970, y: 3600 },
{ x: 24040, y: 3584 },
{ x: 24110, y: 3568 },
{ x: 24180, y: 3554 },
{ x: 24250, y: 3540 },
{ x: 24320, y: 3528 },
{ x: 24390, y: 3518 },
{ x: 24460, y: 3510 },
{ x: 24530, y: 3498 },
{ x: 24600, y: 3478 },
{ x: 24670, y: 3454 },
{ x: 24740, y: 3426 },
{ x: 24810, y: 3398 },
{ x: 24880, y: 3372 },
{ x: 24950, y: 3352 },
{ x: 25020, y: 3340 },
{ x: 25090, y: 3336 },
{ x: 25160, y: 3340 },
{ x: 25230, y: 3352 },
{ x: 25300, y: 3372 },
{ x: 25370, y: 3398 },
{ x: 25440, y: 3426 },
{ x: 25440, y: 3424 },
{ x: 25510, y: 3452 },
{ x: 25580, y: 3478 },
{ x: 25650, y: 3498 },
{ x: 25720, y: 3510 },
{ x: 25790, y: 3514 },
{ x: 25860, y: 3510 },
{ x: 25930, y: 3498 },
{ x: 26000, y: 3478 },
{ x: 26070, y: 3452 },
{ x: 26140, y: 3424 },
{ x: 26210, y: 3396 },
{ x: 26280, y: 3372 },
{ x: 26350, y: 3352 },
{ x: 26420, y: 3338 },
{ x: 26490, y: 3334 },
{ x: 26560, y: 3338 },
{ x: 26630, y: 3352 },
{ x: 26700, y: 3372 },
{ x: 26770, y: 3396 },
{ x: 26840, y: 3424 },
{ x: 26910, y: 3452 },
{ x: 26980, y: 3478 },
{ x: 27050, y: 3498 },
{ x: 27120, y: 3510 },
{ x: 27190, y: 3514 },
{ x: 27260, y: 3510 },
{ x: 27330, y: 3498 },
{ x: 27400, y: 3478 },
{ x: 27470, y: 3452 },
{ x: 27540, y: 3424 },
{ x: 27610, y: 3396 },
{ x: 27680, y: 3372 },
{ x: 27750, y: 3352 },
{ x: 27820, y: 3338 },
{ x: 27890, y: 3334 },
{ x: 27960, y: 3338 },
{ x: 28030, y: 3352 },
{ x: 28100, y: 3372 },
{ x: 28170, y: 3396 },
{ x: 28240, y: 3424 },
{ x: 28310, y: 3424 },
{ x: 28380, y: 3424 },
{ x: 28450, y: 3424 },
{ x: 28520, y: 3424 },
{ x: 28590, y: 3424 },
{ x: 28660, y: 3424 },
{ x: 28730, y: 3424 },
{ x: 28800, y: 3424 },
{ x: 28870, y: 3424 },
{ x: 28940, y: 3424 },
{ x: 29010, y: 3424 },
{ x: 29080, y: 3424 },
{ x: 29150, y: 3424 },
{ x: 29220, y: 3424 },
{ x: 29248, y: 3424 },
{ x: 29318, y: 3420 },
{ x: 29388, y: 3406 },
{ x: 29458, y: 3384 },
{ x: 29528, y: 3352 },
{ x: 29598, y: 3312 },
{ x: 29640, y: 3284 },
{ x: 29657, y: 3272 },
{ x: 29673, y: 3258 },
{ x: 29690, y: 3248 },
{ x: 29707, y: 3240 },
{ x: 29723, y: 3236 },
{ x: 29740, y: 3236 },
{ x: 29757, y: 3238 },
{ x: 29773, y: 3246 },
{ x: 29790, y: 3256 },
{ x: 29807, y: 3268 },
{ x: 29823, y: 3282 },
{ x: 29840, y: 3298 },
{ x: 29857, y: 3314 },
{ x: 29873, y: 3328 },
{ x: 29890, y: 3342 },
{ x: 29907, y: 3352 },
{ x: 29923, y: 3360 },
{ x: 29940, y: 3364 },
{ x: 29957, y: 3366 },
{ x: 29973, y: 3364 },
{ x: 29990, y: 3358 },
{ x: 30007, y: 3352 },
{ x: 30023, y: 3342 },
{ x: 30040, y: 3332 },
{ x: 30057, y: 3322 },
{ x: 30073, y: 3314 },
{ x: 30090, y: 3306 },
{ x: 30107, y: 3302 },
{ x: 30123, y: 3300 },
{ x: 30140, y: 3302 },
{ x: 30157, y: 3308 },
{ x: 30173, y: 3318 },
{ x: 30190, y: 3330 },
{ x: 30207, y: 3344 },
{ x: 30223, y: 3362 },
{ x: 30240, y: 3378 },
{ x: 30257, y: 3396 },
{ x: 30273, y: 3414 },
{ x: 30290, y: 3428 },
{ x: 30307, y: 3440 },
{ x: 30323, y: 3450 },
{ x: 30340, y: 3456 },
{ x: 30357, y: 3458 },
{ x: 30373, y: 3458 },
{ x: 30390, y: 3454 },
{ x: 30407, y: 3448 },
{ x: 30423, y: 3440 },
{ x: 30440, y: 3430 },
{ x: 30457, y: 3420 },
{ x: 30473, y: 3412 },
{ x: 30490, y: 3406 },
{ x: 30507, y: 3402 },
{ x: 30523, y: 3400 },
{ x: 30540, y: 3402 },
{ x: 30557, y: 3408 },
{ x: 30573, y: 3418 },
{ x: 30590, y: 3430 },
{ x: 30607, y: 3444 },
{ x: 30623, y: 3460 },
{ x: 30640, y: 3476 },
{ x: 30657, y: 3494 },
{ x: 30673, y: 3510 },
{ x: 30690, y: 3524 },
{ x: 30707, y: 3536 },
{ x: 30723, y: 3544 },
{ x: 30740, y: 3548 },
{ x: 30757, y: 3550 },
{ x: 30773, y: 3546 },
{ x: 30790, y: 3540 },
{ x: 30807, y: 3532 },
{ x: 30823, y: 3522 },
{ x: 30840, y: 3512 },
{ x: 30857, y: 3500 },
{ x: 30873, y: 3488 },
{ x: 30890, y: 3480 },
{ x: 30907, y: 3474 },
{ x: 30923, y: 3470 },
{ x: 30940, y: 3468 },
{ x: 30957, y: 3472 },
{ x: 30973, y: 3478 },
{ x: 30990, y: 3486 },
{ x: 31007, y: 3498 },
{ x: 31023, y: 3510 },
{ x: 31040, y: 3524 },
{ x: 31057, y: 3538 },
{ x: 31073, y: 3552 },
{ x: 31090, y: 3564 },
{ x: 31107, y: 3578 },
{ x: 31123, y: 3590 },
{ x: 31140, y: 3600 },
{ x: 31157, y: 3612 },
{ x: 31173, y: 3620 },
{ x: 31190, y: 3628 },
{ x: 31207, y: 3636 },
{ x: 31223, y: 3642 },
{ x: 31240, y: 3648 },
{ x: 31257, y: 3652 },
{ x: 31273, y: 3656 },
{ x: 31290, y: 3658 },
{ x: 31307, y: 3660 },
{ x: 31323, y: 3660 },
{ x: 31340, y: 3660 },
{ x: 31357, y: 3660 },
{ x: 31373, y: 3658 },
{ x: 31390, y: 3656 },
{ x: 31407, y: 3654 },
{ x: 31423, y: 3650 },
{ x: 31440, y: 3646 },
{ x: 31457, y: 3644 },
{ x: 31473, y: 3640 },
{ x: 31490, y: 3636 },
{ x: 31507, y: 3634 },
{ x: 31523, y: 3634 },
{ x: 31540, y: 3634 },
{ x: 31557, y: 3638 },
{ x: 31573, y: 3642 },
{ x: 31590, y: 3646 },
{ x: 31607, y: 3654 },
{ x: 31623, y: 3660 },
{ x: 31640, y: 3668 },
{ x: 31657, y: 3676 },
{ x: 31673, y: 3684 },
{ x: 31690, y: 3692 },
{ x: 31707, y: 3696 },
{ x: 31723, y: 3702 },
{ x: 31740, y: 3704 },
{ x: 31757, y: 3704 },
{ x: 31773, y: 3704 },
{ x: 31790, y: 3702 },
{ x: 31807, y: 3700 },
{ x: 31823, y: 3696 },
{ x: 31840, y: 3692 },
{ x: 31857, y: 3688 },
{ x: 31873, y: 3684 },
{ x: 31890, y: 3680 },
{ x: 31907, y: 3680 },
{ x: 31923, y: 3678 },
{ x: 31940, y: 3680 },
{ x: 31957, y: 3682 },
{ x: 31973, y: 3686 },
{ x: 31990, y: 3692 },
{ x: 32007, y: 3698 },
{ x: 32023, y: 3706 },
{ x: 32040, y: 3712 },
{ x: 32057, y: 3720 },
{ x: 32073, y: 3728 },
{ x: 32090, y: 3734 },
{ x: 32107, y: 3740 },
{ x: 32123, y: 3742 },
{ x: 32140, y: 3746 },
{ x: 32157, y: 3746 },
{ x: 32173, y: 3744 },
{ x: 32190, y: 3742 },
{ x: 32207, y: 3738 },
{ x: 32223, y: 3734 },
{ x: 32240, y: 3728 },
{ x: 32257, y: 3724 },
{ x: 32273, y: 3718 },
{ x: 32290, y: 3714 },
{ x: 32307, y: 3712 },
{ x: 32323, y: 3710 },
{ x: 32340, y: 3710 },
{ x: 32357, y: 3710 },
{ x: 32373, y: 3714 },
{ x: 32390, y: 3718 },
{ x: 32407, y: 3722 },
{ x: 32423, y: 3728 },
{ x: 32440, y: 3734 },
{ x: 32510, y: 3734 },
{ x: 32580, y: 3734 },
{ x: 32650, y: 3734 },
{ x: 32720, y: 3734 },
{ x: 32790, y: 3734 },
{ x: 32860, y: 3734 },
{ x: 32930, y: 3734 },
{ x: 33000, y: 3734 },
{ x: 33070, y: 3734 },
{ x: 33140, y: 3734 },
{ x: 33210, y: 3734 },
{ x: 33280, y: 3734 },
{ x: 33350, y: 3734 },
{ x: 33420, y: 3734 },
{ x: 33490, y: 3734 },
{ x: 33560, y: 3734 },
{ x: 33630, y: 3734 },
{ x: 33700, y: 3734 },
{ x: 33770, y: 3734 },
{ x: 33840, y: 3734 },
{ x: 33910, y: 3732 },
{ x: 33980, y: 3728 },
{ x: 34050, y: 3714 },
{ x: 34120, y: 3694 },
{ x: 34190, y: 3666 },
{ x: 34260, y: 3632 },
{ x: 34330, y: 3596 },
{ x: 34400, y: 3562 },
{ x: 34470, y: 3562 },
] },
{ points: [
{ x: 34820, y: 3574 },
{ x: 34890, y: 3586 },
{ x: 34960, y: 3614 },
{ x: 35030, y: 3654 },
{ x: 35100, y: 3692 },
{ x: 35170, y: 3722 },
{ x: 35240, y: 3734 },
{ x: 35310, y: 3774 },
{ x: 35380, y: 3814 },
{ x: 35450, y: 3852 },
{ x: 35520, y: 3886 },
{ x: 35590, y: 3918 },
{ x: 35660, y: 3944 },
{ x: 35730, y: 3966 },
{ x: 35800, y: 3980 },
{ x: 35870, y: 3990 },
{ x: 35940, y: 3994 },
{ x: 36010, y: 3990 },
{ x: 36080, y: 3980 },
{ x: 36150, y: 3966 },
{ x: 36220, y: 3944 },
{ x: 36290, y: 3918 },
{ x: 36360, y: 3886 },
{ x: 36430, y: 3852 },
{ x: 36500, y: 3814 },
{ x: 36570, y: 3774 },
{ x: 36640, y: 3734 },
{ x: 36710, y: 3698 },
{ x: 36780, y: 3664 },
{ x: 36850, y: 3628 },
{ x: 36920, y: 3594 },
{ x: 36990, y: 3558 },
{ x: 37060, y: 3524 },
{ x: 37130, y: 3488 },
{ x: 37200, y: 3454 },
{ x: 37270, y: 3454 },
] },
{ points: [
{ x: 37620, y: 3574 },
{ x: 37690, y: 3586 },
{ x: 37760, y: 3614 },
{ x: 37830, y: 3654 },
{ x: 37900, y: 3692 },
{ x: 37970, y: 3722 },
{ x: 38040, y: 3734 },
{ x: 38110, y: 3774 },
{ x: 38180, y: 3814 },
{ x: 38250, y: 3852 },
{ x: 38320, y: 3886 },
{ x: 38390, y: 3918 },
{ x: 38460, y: 3944 },
{ x: 38530, y: 3966 },
{ x: 38600, y: 3980 },
{ x: 38670, y: 3990 },
{ x: 38740, y: 3994 },
{ x: 38810, y: 3990 },
{ x: 38880, y: 3980 },
{ x: 38950, y: 3966 },
{ x: 39020, y: 3944 },
{ x: 39090, y: 3918 },
{ x: 39160, y: 3886 },
{ x: 39230, y: 3852 },
{ x: 39300, y: 3814 },
{ x: 39370, y: 3774 },
{ x: 39440, y: 3734 },
{ x: 39510, y: 3698 },
{ x: 39580, y: 3664 },
{ x: 39650, y: 3628 },
{ x: 39720, y: 3594 },
{ x: 39790, y: 3558 },
{ x: 39860, y: 3524 },
{ x: 39930, y: 3488 },
{ x: 40000, y: 3454 },
{ x: 40070, y: 3454 },
] },
{ points: [
{ x: 40420, y: 3574 },
{ x: 40490, y: 3586 },
{ x: 40560, y: 3614 },
{ x: 40630, y: 3654 },
{ x: 40700, y: 3692 },
{ x: 40770, y: 3722 },
{ x: 40840, y: 3734 },
{ x: 40910, y: 3774 },
{ x: 40980, y: 3814 },
{ x: 41050, y: 3852 },
{ x: 41120, y: 3886 },
{ x: 41190, y: 3918 },
{ x: 41260, y: 3944 },
{ x: 41330, y: 3966 },
{ x: 41400, y: 3980 },
{ x: 41470, y: 3990 },
{ x: 41540, y: 3994 },
{ x: 41610, y: 3990 },
{ x: 41680, y: 3980 },
{ x: 41750, y: 3966 },
{ x: 41820, y: 3944 },
{ x: 41890, y: 3918 },
{ x: 41960, y: 3886 },
{ x: 42030, y: 3852 },
{ x: 42100, y: 3814 },
{ x: 42170, y: 3774 },
{ x: 42240, y: 3734 },
{ x: 42310, y: 3698 },
{ x: 42380, y: 3664 },
{ x: 42450, y: 3628 },
{ x: 42520, y: 3594 },
{ x: 42590, y: 3558 },
{ x: 42660, y: 3524 },
{ x: 42730, y: 3488 },
{ x: 42800, y: 3454 },
{ x: 42870, y: 3454 },
] },
{ points: [
{ x: 43220, y: 3574 },
{ x: 43290, y: 3586 },
{ x: 43360, y: 3614 },
{ x: 43430, y: 3654 },
{ x: 43500, y: 3692 },
{ x: 43570, y: 3722 },
{ x: 43640, y: 3734 },
{ x: 43710, y: 3750 },
{ x: 43780, y: 3766 },
{ x: 43850, y: 3782 },
{ x: 43920, y: 3798 },
{ x: 43990, y: 3814 },
{ x: 44060, y: 3830 },
{ x: 44130, y: 3846 },
{ x: 44200, y: 3862 },
{ x: 44270, y: 3878 },
{ x: 44340, y: 3894 },
{ x: 44410, y: 3910 },
{ x: 44480, y: 3926 },
{ x: 44550, y: 3942 },
{ x: 44620, y: 3958 },
{ x: 44690, y: 3974 },
{ x: 44760, y: 3990 },
{ x: 44830, y: 4006 },
{ x: 44900, y: 4022 },
{ x: 44970, y: 4038 },
{ x: 45040, y: 4054 },
{ x: 45110, y: 4054 },
{ x: 45180, y: 4054 },
{ x: 45250, y: 4054 },
{ x: 45320, y: 4054 },
{ x: 45390, y: 4054 },
{ x: 45460, y: 4054 },
{ x: 45530, y: 4054 },
{ x: 45600, y: 4054 },
{ x: 45670, y: 4054 },
{ x: 45740, y: 4054 },
{ x: 45810, y: 4054 },
{ x: 45880, y: 4054 },
{ x: 45950, y: 4054 },
{ x: 46020, y: 4054 },
{ x: 46090, y: 4054 },
{ x: 46160, y: 4054 },
{ x: 46230, y: 4054 },
{ x: 46300, y: 4054 },
{ x: 46370, y: 4054 },
{ x: 46440, y: 4054 },
{ x: 46510, y: 4054 },
{ x: 46580, y: 4054 },
{ x: 46650, y: 4054 },
{ x: 46720, y: 4054 },
{ x: 46790, y: 4054 },
{ x: 46860, y: 4054 },
{ x: 46930, y: 4054 },
{ x: 47000, y: 4054 },
{ x: 47070, y: 4054 },
{ x: 47140, y: 4054 },
{ x: 47210, y: 4054 },
{ x: 47280, y: 4054 },
{ x: 47350, y: 4054 },
{ x: 47420, y: 4054 },
{ x: 47490, y: 4054 },
{ x: 47560, y: 4054 },
{ x: 47630, y: 4054 },
{ x: 47700, y: 4054 },
{ x: 47770, y: 4054 },
{ x: 47840, y: 4054 },
] },
],
obstacles: [],
items: [
{ type: "tv", mode: "gravity", x: 4563, y: 1067 },
{ type: "chair", mode: "gravity", x: 4279, y: 1058 },
{ type: "chair", mode: "gravity", x: 4796, y: 1042 },
{ type: "cone", mode: "gravity", x: 16321, y: 4583 },
{ type: "cone", mode: "gravity", x: 16563, y: 4525 },
{ type: "cone", mode: "gravity", x: 16871, y: 4550 },
{ type: "tv", mode: "float", x: 37346, y: 3083 },
{ type: "tv", mode: "float", x: 37979, y: 3367 },
{ type: "cone", mode: "float", x: 40179, y: 3058 },
{ type: "cone", mode: "float", x: 40779, y: 3317 },
{ type: "chair", mode: "float", x: 34529, y: 3300 },
{ type: "chair", mode: "float", x: 35038, y: 3433 },
{ type: "chair", mode: "float", x: 46904, y: 3933 },
{ type: "tv", mode: "float", x: 46688, y: 3892 },
],
bonusItems: [
{ type: "ctc", x: 21546, y: 3450 },
{ type: "ctc", x: 30488, y: 2908 },
{ type: "cash", x: 37663, y: 3200 },
{ type: "cash", x: 40488, y: 3200 },
{ type: "cash", x: 43246, y: 3250 },
{ type: "guitar", x: 25296, y: 3075 },
{ type: "guitar", x: 5538, y: 992 },
{ type: "cash", x: 10054, y: 2133 },
{ type: "ctc", x: 13729, y: 2933 },
{ type: "cash", x: 34788, y: 3258 },
{ type: "cash", x: 46429, y: 3875 },
],
goal: { x: 47600, y: 3813, width: 280, height: 640 },
cameraBounds: { x: -600, y: 0, width: 49240, height: 5556 },
};

View File

@ -22,12 +22,17 @@ export const SECTION_TYPES = [
{ id: 'smoothDecline', label: 'Smooth Decline' },
{ id: 'sharpDecline', label: 'Sharp Decline' },
{ id: 'rollingHills', label: 'Rolling Hills' },
{ id: 'washboard', label: 'Washboard' },
{ id: 'washboardUp', label: 'Washboard Up' },
{ id: 'washboardDown', label: 'Washboard Down' },
{ id: 'ditch', label: 'Ditch' },
{ id: 'jump', label: 'Jump' },
{ id: 'dropOff', label: 'Drop Off' },
{ id: 'gap', label: 'Gap' },
{ id: 'lip', label: 'Lip' },
{ id: 'dropGap', label: 'Drop Gap' },
{ id: 'halfpipe', label: 'Halfpipe' },
{ id: 'reverseHalfpipe', label: 'Reverse Halfpipe' },
];
const RISE_SMOOTH = 90;
@ -35,6 +40,33 @@ const RISE_SHARP = 160;
const AMP_HILLS = 45;
const DEPTH_DITCH = 130;
// Washboard: a repeating run of smooth arches (half-sine humps, like a
// corrugated/washboard road in Trials-style games) over a flat or sloped
// baseline. The arch count is derived from the section's width (target
// WASH_PERIOD base units per arch) so the arches keep a roughly constant
// width when the user stretches/contracts a section instead of every arch
// stretching with it, and the floor keeps the pattern visible even at the
// editor's minimum section width. A half-sine arch's steepest tangent is
// only PI*amplitude/period, so at these numbers each hump reads as a
// gentle rounded arch (~40 degrees at its steepest) rather than a spiky
// wave. Amplitude, and for the up/down variants the overall rise too,
// scale with vScale like every other section's rise/amplitude/depth.
const WASH_AMPLITUDE = 26;
const WASH_PERIOD = 100;
const WASH_RISE = 120;
function washBumps(width) {
return Math.max(4, Math.round(width / WASH_PERIOD));
}
// Samples per arch. A half-sine arch only reads as a smooth arch once it's
// sampled densely - at 4 samples per arch the points (foot, crest, foot,
// midline) joined into a zig-zag diamond that looked like sharp spikes
// instead of rounded arches, so this is 12: every arch is a run of short
// segments that reads as one smooth hump, while the default 700-wide
// section stays a modest ~85 points.
const WASH_SAMPLES_PER_BUMP = 12;
const JUMP_TAKEOFF_FRACTION = 0.45;
const JUMP_GAP_FRACTION = 0.25;
// JUMP_LANDING_FRACTION is implicit: 1 - JUMP_TAKEOFF_FRACTION - JUMP_GAP_FRACTION
@ -76,11 +108,13 @@ function smoothstep(t) {
return c * c * (3 - 2 * c);
}
// Samples x from startX to startX+width every POINT_STEP (inclusive of both
// ends), calling shape(t) for the y-offset at each sample, t in [0,1].
function sampleBlock(startX, startY, width, shape) {
// Samples x from startX to startX+width every `step` (inclusive of both
// ends), calling shape(t) for the y-offset at each sample, t in [0,1]. The
// washboard passes a custom step (samples per arch, see
// WASH_SAMPLES_PER_BUMP); every other generator keeps the POINT_STEP grid.
function sampleBlock(startX, startY, width, shape, step = POINT_STEP) {
const points = [];
for (let x = startX; x <= startX + width; x += POINT_STEP) {
for (let x = startX; x <= startX + width; x += step) {
const t = (x - startX) / width;
points.push({ x, y: Math.round(startY + shape(t)) });
}
@ -134,6 +168,39 @@ function ditch(startX, startY, width, vScale) {
return { type: 'simple', points, endY: startY };
}
// Washboard (flat): N full half-sine arches (humps). Sin starts and ends
// at zero displacement, so both ends sit exactly on the neighbor sections'
// baseline (no seam step) while the interior is a uniform run of smooth
// humps.
function washboard(startX, startY, width, vScale) {
const amp = WASH_AMPLITUDE * vScale;
const bumps = washBumps(width);
const points = sampleBlock(startX, startY, width, (t) => -amp * Math.sin(Math.PI * bumps * t), width / (WASH_SAMPLES_PER_BUMP * bumps));
return { type: 'simple', points, endY: startY };
}
// Washboard Up: the same smooth arches riding on an overall incline. The
// baseline uses smoothstep (zero slope at both ends) like smoothIncline, so
// the section's ends match a flat neighbor's baseline and the climb eases
// in/out instead of ramping at a constant angle under the arches.
function washboardUp(startX, startY, width, vScale) {
const amp = WASH_AMPLITUDE * vScale;
const rise = WASH_RISE * vScale;
const bumps = washBumps(width);
const points = sampleBlock(startX, startY, width, (t) => -rise * smoothstep(t) - amp * Math.sin(Math.PI * bumps * t), width / (WASH_SAMPLES_PER_BUMP * bumps));
return { type: 'simple', points, endY: startY - rise };
}
// Washboard Down: mirror of washboardUp - a smoothstep decline baseline
// with the smooth arches on top.
function washboardDown(startX, startY, width, vScale) {
const amp = WASH_AMPLITUDE * vScale;
const rise = WASH_RISE * vScale;
const bumps = washBumps(width);
const points = sampleBlock(startX, startY, width, (t) => rise * smoothstep(t) - amp * Math.sin(Math.PI * bumps * t), width / (WASH_SAMPLES_PER_BUMP * bumps));
return { type: 'simple', points, endY: startY + rise };
}
// The one type that produces two separate point runs (a real terrain gap in
// between), mirroring how level02.js builds its jump: a "runway" segment
// ending at a raised lip, then a real x-gap, then a "landing" segment
@ -232,6 +299,172 @@ function dropGap(startX, startY, width, vScale) {
return { type: 'gap', takeoffPoints, landingPoints, endY: landingY };
}
// "Halfpipe" - a backwards J. One smooth profile, left to right:
// a near-vertical drop straight down (the tangent at the entrance is
// exactly vertical) that eases into a rounded, halfpipe-style bottom,
// then a steep climb (angled to HALFPIPE_EXIT_VERT at the very end) up
// to the exit.
//
// The exit is deliberately BELOW the entrance: the climb is only HALF of
// the drop, so the section is a net decline (a half-pipe's exit wall is
// lower than its entrance wall) and the profile reads as a backwards J -
// tall stem on the left, round hook at the bottom, short stub climbing
// back up on the right.
//
// Built as a single cubic Bezier: the entry control handle hangs straight
// down (vertical entry tangent), while the exit handle is angled up to
// HALFPIPE_EXIT_VERT (0.80 = 80% vertical = 72° from horizontal) so the
// climb-out is steep but not quite vertical. A circular arc can't do this
// job - a circle tangent to both walls would force the two junctions to
// the same height (a half-circle), so the different wall heights need a
// cubic, which is also the shape a vert-ramp transition actually uses.
//
// P0 = entrance P1 = (P0.x, P0.y + drop) [entry handle]
// P3 = exit P2 = (P3.x - climb*cosA, P3.y + climb*sinA) [exit handle]
// where A = HALFPIPE_EXIT_VERT * 90°
//
// x(t) = W * t^2 * (3 - 2t) (all x-control points in [0, W] =>
// strictly monotonic in x, so the terrain stays a valid y(x) curve)
//
// drop/climb (both vScale-scaled) are the end-handle lengths: they set how
// long the near-vertical ends are and how deep the rounded bottom sits
// (~0.84 * drop below the entrance at the default 2:1 ratio, ~13% further
// right of center). At the default 700-wide section the profile falls ~344
// base units ("drops for a ways") and the exit ends ~210 below the
// entrance ("significantly lower"). The sample count is chosen so that
// even at the editor's minimum width the end segments stay wider than
// Terrain._buildSegment's |dx| < 1 skip threshold.
const HALFPIPE_DROP = 420;
const HALFPIPE_CLIMB_RATIO = 0.5;
// Exit-wall verticalness as a fraction of a straight-up wall: 0.80 means
// the exit tangent is 80% of the way to vertical, i.e. 72° above
// horizontal. 1.0 would be a fully vertical exit (the old shape).
const HALFPIPE_EXIT_VERT = 0.80;
const HALFPIPE_SAMPLES = 32;
function halfpipe(startX, startY, width, vScale) {
const drop = HALFPIPE_DROP * vScale;
const climb = HALFPIPE_CLIMB_RATIO * drop;
const net = drop - climb; // entrance -> exit (positive: exit is lower)
const endY = startY + net;
// Endpoint-anchored C1 cubic Bezier. The entrance handle hangs straight DOWN
// (vertical descent), while the exit handle is angled to HALFPIPE_EXIT_VERT
// (0.80 -> 72° from horizontal) so the climb-out is steep but not quite
// vertical:
// P0 = (0, 0)
// P1 = (0, drop) entrance handle -> 90° descent
// P2 = (width - climb*cos72, net + climb*sin72) exit handle -> 72° climb
// P3 = (width, net) exit
// Because the curve passes through P0 and P3 exactly, the endpoints are
// always pinned to (0,0) and (width,net) regardless of width.
const exitAngle = HALFPIPE_EXIT_VERT * Math.PI / 2; // 0.80 -> 72°
const exitDx = width - climb * Math.cos(exitAngle);
const exitDy = net + climb * Math.sin(exitAngle);
const points = [];
for (let i = 0; i <= HALFPIPE_SAMPLES; i++) {
const t = i / HALFPIPE_SAMPLES;
const u = 1 - t;
// Cubic Bezier evaluated at parameter t (control points relative to start).
const b1 = 3 * u * u * t; // weight on P1
const b2 = 3 * u * t * t; // weight on P2
const b3 = t * t * t; // weight on P3
const x = b2 * exitDx + b3 * width;
const y = b1 * drop + b2 * exitDy + b3 * net;
points.push({ x: Math.round(startX + x), y: Math.round(startY + y) });
}
// Snap the ends exactly so the section joins its neighbors seamlessly.
points[0] = { x: startX, y: Math.round(startY) };
points[points.length - 1] = { x: startX + width, y: Math.round(endY) };
return { type: 'simple', points, endY };
}
// "Reverse Halfpipe" - the halfpipe flipped upside down (an upside-down J).
// One smooth profile, left to right: a steep climb (angled to
// REVERSE_HALFPIPE_ENTRY_VERT at the entrance) that arcs up and over like a
// rainbow, then a near-vertical drop (vertical tangent at the exit) that
// ends BELOW the entrance.
//
// It's the mirror family of halfpipe: same x(t), same Bezier structure,
// signs flipped, and the two ends swapped. Where halfpipe's entry is the
// long leg (drop) and exit the short one (climb), here the entry is the
// short leg (rise, ~half the drop) and the exit the long one (drop), so
// the section still nets lower on the right - the mirror of halfpipe's
// net-decline shape. Like the halfpipe, its angled end is tuned to 80%
// vertical (72 degrees) rather than fully vertical.
//
// Control handles (both stick UP from their endpoints, mirror of the
// halfpipe's hanging handles):
//
// P0 = entrance P1 = (P0.x + rise*cosA, P0.y - rise*sinA) [entry handle, angled]
// P3 = exit P2 = (P3.x, P3.y - drop) [exit handle, vertical]
// where A = REVERSE_HALFPIPE_ENTRY_VERT * 90 degrees
//
// x(t) = cubic Bezier with P1.x = rise*cosA, P2.x = P3.x = W
// (control-point x's are non-decreasing, so strictly monotonic in x
// - same construction as the halfpipe, valid y(x) terrain)
// y(t) = -3*rise*sinA*t*(1-t)^2 - 3*rise*t^2*(1-t) + (drop - rise)*t^3
// (with rise*sinA the effective entry-handle depth)
//
// rise/drop (both vScale-scaled) are the end-handle lengths. At the
// default 700-wide section the arch crests ~176 base units above the
// entrance ("arcs up and over"), and the exit ends ~210 below it ("ends
// lower than the entrance") - the mirror image of halfpipe's 348-deep,
// 210-net-decline profile. End-segment widths stay above
// Terrain._buildSegment's |dx| < 1 skip threshold even at the editor's
// minimum width.
const REVERSE_HALFPIPE_RISE = 210;
const REVERSE_HALFPIPE_DROP = 420; // = 2 * RISE, the longer leg
// Entrance-wall verticalness as a fraction of a straight-up wall: 0.80
// means the entry tangent is 80% of the way to vertical, i.e. 72 degrees
// above horizontal (mirrors the halfpipe's exit). 1.0 would be fully
// vertical.
const REVERSE_HALFPIPE_ENTRY_VERT = 0.80;
const REVERSE_HALFPIPE_SAMPLES = 32;
function reverseHalfpipe(startX, startY, width, vScale) {
const rise = REVERSE_HALFPIPE_RISE * vScale;
const drop = REVERSE_HALFPIPE_DROP * vScale;
const net = drop - rise; // positive: exit is lower than entrance
const endY = startY + net;
// Endpoint-anchored C1 cubic Bezier. The exit handle sticks straight UP
// from the exit (vertical drop-out), while the entry handle is angled to
// REVERSE_HALFPIPE_ENTRY_VERT (0.80 -> 72 degrees from horizontal) so the
// climb-in is steep but not quite vertical:
// P0 = (0, 0)
// P1 = (rise*cosA, -rise*sinA) entrance handle -> 72 deg climb
// P2 = (width, -rise) exit handle -> vertical drop
// P3 = (width, net) exit
// Because the curve passes through P0 and P3 exactly, the endpoints are
// always pinned to (0,0) and (width,net) regardless of width.
const entryAngle = REVERSE_HALFPIPE_ENTRY_VERT * Math.PI / 2; // 0.80 -> 72 degrees
const entryDx = rise * Math.cos(entryAngle);
const entryRise = rise * Math.sin(entryAngle); // effective entry-handle depth
const points = [];
for (let i = 0; i <= REVERSE_HALFPIPE_SAMPLES; i++) {
const t = i / REVERSE_HALFPIPE_SAMPLES;
const u = 1 - t;
// Cubic Bezier evaluated at parameter t (control points relative to start).
const b1 = 3 * u * u * t; // weight on P1
const b2 = 3 * u * t * t; // weight on P2
const b3 = t * t * t; // weight on P3
const x = b1 * entryDx + b2 * width + b3 * width;
const y = -b1 * entryRise - b2 * rise + b3 * net;
points.push({ x: Math.round(startX + x), y: Math.round(startY + y) });
}
// Snap the ends exactly so the section joins its neighbors seamlessly.
points[0] = { x: startX, y: Math.round(startY) };
points[points.length - 1] = { x: startX + width, y: Math.round(endY) };
return { type: 'simple', points, endY };
}
const GENERATORS = {
flat,
smoothIncline,
@ -239,12 +472,17 @@ const GENERATORS = {
smoothDecline,
sharpDecline,
rollingHills,
washboard,
washboardUp,
washboardDown,
ditch,
jump,
dropOff,
gap,
lip,
dropGap,
halfpipe,
reverseHalfpipe,
};
export function generateSection(typeId, startX, startY, width = SECTION_WIDTH, vScale = 1) {

View File

@ -3,7 +3,7 @@ import { GAME_WIDTH, GAME_HEIGHT, WORLD_SCALE } from '../config.js';
import { CAMPAIGNS } from '../data/levels/index.js';
import { autoLayoutPoints, sampleSpline } from '../data/levels/positioning.js';
import { isLevelComplete, getBestScore } from '../util/progress.js';
import { playMenuMusic } from '../util/music.js';
import { playMenuTrack } from '../util/music.js';
import { nodeDisplaySize, scalePos, BADGE_HIT_FRAC, BADGE_HIT_RADIUS_MULT } from '../util/mapArt.js';
import { createPill, pillInteractive } from '../util/pill.js';
@ -56,7 +56,10 @@ export default class LevelSelectScene extends Phaser.Scene {
create() {
this.cameras.main.setBackgroundColor('#8fc7e8');
playMenuMusic(this);
// Menu music (main title or the campaign's own theme) is started by
// _buildCampaign -> playMenuTrack, which knows which campaign is being
// shown; it handles first-time start, campaign-to-campaign swaps, and
// re-entry from the main menu alike.
this.campaignIndex = 0;
this._groups = { bg: [], path: [], nodes: [], tags: [] };
@ -113,6 +116,12 @@ export default class LevelSelectScene extends Phaser.Scene {
const campaign = CAMPAIGNS[this.campaignIndex];
const levels = campaign.levels;
// Menu music follows the campaign: this campaign's theme (musicKey) or
// the main title for campaigns without one. Handles first entry, campaign
// switches (from _shiftCampaign), and re-entry after a level (activeKey
// was cleared by stopMenuMusic, so the track restarts from the top).
playMenuTrack(this, campaign.musicKey);
const points = (campaign.positions || autoLayoutPoints(levels.length)).map(scalePos);
const completed = levels.map((l) => isLevelComplete(l.id));
// A campaign's first level is playable as soon as the PREVIOUS campaign
@ -293,7 +302,14 @@ export default class LevelSelectScene extends Phaser.Scene {
v: best,
duration: Math.min(1500, 500 + Math.sqrt(best) * 40),
ease: 'Cubic.easeOut',
onUpdate: () => pill.setText(`${Math.round(counter.v).toLocaleString()}`),
onUpdate: () => {
// The pill is destroyed by _buildCampaign when the campaign
// switches, but this tween targets a plain object (the counter)
// and outlives the pill - guard the setText (crashes on the
// destroyed Text's nulled texture frame).
if (pill.isDestroyed) return;
pill.setText(`${Math.round(counter.v).toLocaleString()}`);
},
});
},
});

View File

@ -28,6 +28,9 @@ export default class PreloadScene extends Phaser.Scene {
}
this.load.audio('main_title_music', 'assets/music/main-title.mp3');
// Campaign 2's level-select theme (see musicKey in data/levels/index.js
// and playMenuTrack in util/music.js).
this.load.audio('urban_trap_theme', 'assets/music/urban-trap-theme.mp3');
this.load.audio('engine_heavy', 'assets/fx/engine-heavy.mp3');
this.load.audio('crowd_cheer', 'assets/fx/crowd-cheer.mp3');
this.load.audio('kid_count', 'assets/fx/kid-count.mp3');

View File

@ -1,21 +1,68 @@
// Menu music spans Intro -> MainMenu -> LevelSelect as one continuous track
// Menu music spans Intro -> MainMenu -> LevelSelect as one continuous bed
// (Phaser's sound manager lives on the game, not the scene, so a Sound
// object started in one scene keeps playing through scene.start() calls
// into the next). playMenuMusic is safe to call from every one of those
// scenes' create() - it no-ops if the track is already playing so
// navigating between them doesn't restart it.
const KEY = 'main_title_music';
// into the next). Only ONE menu track is active at a time:
// - the main title theme (DEFAULT_KEY) - the default, and what Intro /
// MainMenu always play;
// - a campaign's own theme, if the campaign defines one (musicKey, see
// data/levels/index.js) - LevelSelect switches to it while that
// campaign's map is showing (campaign 2 -> urban-trap-theme) and back
// to the main title for campaigns without one.
// playMenuTrack swaps the active track (stopping the old one); it resumes
// rather than restarts a track that's already active but paused, and no-ops
// if it's already playing. playMenuMusic is the default (main title)
// shorthand the menu scenes call from create().
const DEFAULT_KEY = 'main_title_music';
export function playMenuMusic(scene) {
const existing = scene.sound.get(KEY);
if (existing && existing.isPlaying) return;
// The track currently active across the menu scenes (or null if none).
let activeKey = null;
/**
* Makes `key` the active menu track (stopping the current one if it's a
* different key), or resumes it if it's already active but paused. `key`
* defaults to the main title theme, so `playMenuTrack(scene, undefined)`
* and `playMenuMusic(scene)` are equivalent. A key that never loaded
* (missing file / load failure) silently falls back to the main title -
* the same load-tolerant pattern as voiceLine.js.
*/
export function playMenuTrack(scene, key = DEFAULT_KEY) {
if (!scene.cache.audio.exists(key)) key = DEFAULT_KEY;
if (activeKey === key) {
// Already the active track: keep it - resume if it was stopped (e.g. by
// PlayScene), no-op if it's still playing.
const existing = scene.sound.get(key);
if (!existing) {
scene.sound.add(key, { loop: true, volume: 0.5 }).play();
return;
}
if (!existing.isPlaying) existing.play();
return;
}
// Swap: stop the current track, then start (or resume) the new one.
if (activeKey) scene.sound.get(activeKey)?.stop();
activeKey = key;
const existing = scene.sound.get(key);
if (existing) {
existing.play();
return;
}
scene.sound.add(KEY, { loop: true, volume: 0.5 }).play();
scene.sound.add(key, { loop: true, volume: 0.5 }).play();
}
export function stopMenuMusic(scene) {
scene.sound.get(KEY)?.stop();
// Intro / MainMenu always want the main title theme.
export function playMenuMusic(scene) {
playMenuTrack(scene, DEFAULT_KEY);
}
// PlayScene calls this when gameplay starts: stop WHATEVER menu track is
// active (main title or a campaign theme) and clear the active pointer, so
// the next menu scene's play* call starts its track fresh.
export function stopMenuMusic(scene) {
if (!activeKey) return;
const key = activeKey;
activeKey = null;
scene.sound.get(key)?.stop();
}