crimsonland 0.1.0.dev5__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (139) hide show
  1. crimson/__init__.py +24 -0
  2. crimson/assets_fetch.py +60 -0
  3. crimson/atlas.py +92 -0
  4. crimson/audio_router.py +155 -0
  5. crimson/bonuses.py +167 -0
  6. crimson/camera.py +75 -0
  7. crimson/cli.py +380 -0
  8. crimson/creatures/__init__.py +8 -0
  9. crimson/creatures/ai.py +186 -0
  10. crimson/creatures/anim.py +173 -0
  11. crimson/creatures/damage.py +103 -0
  12. crimson/creatures/runtime.py +1019 -0
  13. crimson/creatures/spawn.py +2871 -0
  14. crimson/debug.py +7 -0
  15. crimson/demo.py +1360 -0
  16. crimson/demo_trial.py +140 -0
  17. crimson/effects.py +1086 -0
  18. crimson/effects_atlas.py +73 -0
  19. crimson/frontend/__init__.py +1 -0
  20. crimson/frontend/assets.py +43 -0
  21. crimson/frontend/boot.py +424 -0
  22. crimson/frontend/menu.py +700 -0
  23. crimson/frontend/panels/__init__.py +1 -0
  24. crimson/frontend/panels/base.py +410 -0
  25. crimson/frontend/panels/controls.py +132 -0
  26. crimson/frontend/panels/mods.py +128 -0
  27. crimson/frontend/panels/options.py +409 -0
  28. crimson/frontend/panels/play_game.py +627 -0
  29. crimson/frontend/panels/stats.py +351 -0
  30. crimson/frontend/transitions.py +31 -0
  31. crimson/game.py +2533 -0
  32. crimson/game_modes.py +15 -0
  33. crimson/game_world.py +652 -0
  34. crimson/gameplay.py +2467 -0
  35. crimson/input_codes.py +176 -0
  36. crimson/modes/__init__.py +1 -0
  37. crimson/modes/base_gameplay_mode.py +219 -0
  38. crimson/modes/quest_mode.py +502 -0
  39. crimson/modes/rush_mode.py +300 -0
  40. crimson/modes/survival_mode.py +792 -0
  41. crimson/modes/tutorial_mode.py +648 -0
  42. crimson/modes/typo_mode.py +472 -0
  43. crimson/paths.py +23 -0
  44. crimson/perks.py +828 -0
  45. crimson/persistence/__init__.py +1 -0
  46. crimson/persistence/highscores.py +385 -0
  47. crimson/persistence/save_status.py +245 -0
  48. crimson/player_damage.py +77 -0
  49. crimson/projectiles.py +1133 -0
  50. crimson/quests/__init__.py +18 -0
  51. crimson/quests/helpers.py +147 -0
  52. crimson/quests/registry.py +49 -0
  53. crimson/quests/results.py +164 -0
  54. crimson/quests/runtime.py +91 -0
  55. crimson/quests/tier1.py +620 -0
  56. crimson/quests/tier2.py +652 -0
  57. crimson/quests/tier3.py +579 -0
  58. crimson/quests/tier4.py +721 -0
  59. crimson/quests/tier5.py +886 -0
  60. crimson/quests/timeline.py +115 -0
  61. crimson/quests/types.py +70 -0
  62. crimson/render/__init__.py +1 -0
  63. crimson/render/terrain_fx.py +88 -0
  64. crimson/render/world_renderer.py +1941 -0
  65. crimson/sim/__init__.py +1 -0
  66. crimson/sim/world_defs.py +67 -0
  67. crimson/sim/world_state.py +422 -0
  68. crimson/terrain_assets.py +19 -0
  69. crimson/tutorial/__init__.py +12 -0
  70. crimson/tutorial/timeline.py +291 -0
  71. crimson/typo/__init__.py +2 -0
  72. crimson/typo/names.py +233 -0
  73. crimson/typo/player.py +43 -0
  74. crimson/typo/spawns.py +73 -0
  75. crimson/typo/typing.py +52 -0
  76. crimson/ui/__init__.py +3 -0
  77. crimson/ui/cursor.py +95 -0
  78. crimson/ui/demo_trial_overlay.py +235 -0
  79. crimson/ui/game_over.py +660 -0
  80. crimson/ui/hud.py +601 -0
  81. crimson/ui/perk_menu.py +388 -0
  82. crimson/views/__init__.py +40 -0
  83. crimson/views/aim_debug.py +276 -0
  84. crimson/views/animations.py +274 -0
  85. crimson/views/arsenal_debug.py +404 -0
  86. crimson/views/audio_bootstrap.py +47 -0
  87. crimson/views/bonuses.py +201 -0
  88. crimson/views/camera_debug.py +359 -0
  89. crimson/views/camera_shake.py +229 -0
  90. crimson/views/corpse_stamp_debug.py +324 -0
  91. crimson/views/decals_debug.py +739 -0
  92. crimson/views/empty.py +19 -0
  93. crimson/views/fonts.py +114 -0
  94. crimson/views/game_over.py +117 -0
  95. crimson/views/ground.py +259 -0
  96. crimson/views/lighting_debug.py +1166 -0
  97. crimson/views/particles.py +293 -0
  98. crimson/views/perk_menu_debug.py +430 -0
  99. crimson/views/perks.py +398 -0
  100. crimson/views/player.py +434 -0
  101. crimson/views/player_sprite_debug.py +314 -0
  102. crimson/views/projectile_fx.py +609 -0
  103. crimson/views/projectile_render_debug.py +393 -0
  104. crimson/views/projectiles.py +221 -0
  105. crimson/views/quest_title_overlay.py +108 -0
  106. crimson/views/registry.py +34 -0
  107. crimson/views/rush.py +16 -0
  108. crimson/views/small_font_debug.py +204 -0
  109. crimson/views/spawn_plan.py +363 -0
  110. crimson/views/sprites.py +214 -0
  111. crimson/views/survival.py +15 -0
  112. crimson/views/terrain.py +132 -0
  113. crimson/views/ui.py +123 -0
  114. crimson/views/wicons.py +166 -0
  115. crimson/weapon_sfx.py +63 -0
  116. crimson/weapons.py +860 -0
  117. crimsonland-0.1.0.dev5.dist-info/METADATA +9 -0
  118. crimsonland-0.1.0.dev5.dist-info/RECORD +139 -0
  119. crimsonland-0.1.0.dev5.dist-info/WHEEL +4 -0
  120. crimsonland-0.1.0.dev5.dist-info/entry_points.txt +4 -0
  121. grim/__init__.py +20 -0
  122. grim/app.py +92 -0
  123. grim/assets.py +231 -0
  124. grim/audio.py +106 -0
  125. grim/config.py +294 -0
  126. grim/console.py +737 -0
  127. grim/fonts/__init__.py +7 -0
  128. grim/fonts/grim_mono.py +111 -0
  129. grim/fonts/small.py +120 -0
  130. grim/input.py +44 -0
  131. grim/jaz.py +103 -0
  132. grim/math.py +17 -0
  133. grim/music.py +403 -0
  134. grim/paq.py +76 -0
  135. grim/rand.py +37 -0
  136. grim/sfx.py +276 -0
  137. grim/sfx_map.py +103 -0
  138. grim/terrain_render.py +840 -0
  139. grim/view.py +16 -0
@@ -0,0 +1,886 @@
1
+ from __future__ import annotations
2
+
3
+ import math
4
+
5
+ from ..perks import PerkId
6
+ from ..creatures.spawn import SpawnId
7
+ from .helpers import (
8
+ center_point,
9
+ ring_points,
10
+ spawn,
11
+ )
12
+ from .registry import register_quest
13
+ from .types import QuestContext, SpawnEntry
14
+
15
+
16
+ @register_quest(
17
+ level="5.1",
18
+ title="The Beating",
19
+ time_limit_ms=480000,
20
+ start_weapon_id=1,
21
+ unlock_weapon_id=0x1F,
22
+ builder_address=0x00435610,
23
+ )
24
+ def build_5_1_the_beating(ctx: QuestContext) -> list[SpawnEntry]:
25
+ entries: list[SpawnEntry] = [
26
+ spawn(x=256.0, y=256.0, heading=0.0, spawn_id=SpawnId.ALIEN_CONST_WEAPON_BONUS_27, trigger_ms=500, count=1),
27
+ spawn(
28
+ x=ctx.width + 32.0,
29
+ y=float(ctx.height // 2),
30
+ heading=0.0,
31
+ spawn_id=SpawnId.ALIEN_CONST_GREY_BRUTE_29,
32
+ trigger_ms=8000,
33
+ count=3,
34
+ ),
35
+ ]
36
+
37
+ trigger = 10000
38
+ x_offset = 0x40
39
+ for _ in range(8):
40
+ entries.append(
41
+ spawn(
42
+ x=float(ctx.width + x_offset),
43
+ y=float(ctx.height // 2),
44
+ heading=0.0,
45
+ spawn_id=SpawnId.ALIEN_CONST_GREEN_SMALL_25,
46
+ trigger_ms=trigger,
47
+ count=8,
48
+ )
49
+ )
50
+ trigger += 100
51
+ x_offset += 0x20
52
+
53
+ entries.append(
54
+ spawn(
55
+ x=-32.0,
56
+ y=float(ctx.height // 2),
57
+ heading=0.0,
58
+ spawn_id=SpawnId.ALIEN_CONST_GREY_BRUTE_29,
59
+ trigger_ms=18000,
60
+ count=3,
61
+ )
62
+ )
63
+
64
+ trigger = 20000
65
+ x = -64
66
+ for _ in range(8):
67
+ entries.append(
68
+ spawn(
69
+ x=float(x),
70
+ y=float(ctx.height // 2),
71
+ heading=0.0,
72
+ spawn_id=SpawnId.ALIEN_CONST_GREEN_SMALL_25,
73
+ trigger_ms=trigger,
74
+ count=8,
75
+ )
76
+ )
77
+ trigger += 100
78
+ x -= 32
79
+
80
+ trigger = 40000
81
+ y = -64
82
+ for _ in range(6):
83
+ entries.append(
84
+ spawn(
85
+ x=float(ctx.width // 2),
86
+ y=float(y),
87
+ heading=0.0,
88
+ spawn_id=SpawnId.ALIEN_CONST_BROWN_TRANSPARENT_0F,
89
+ trigger_ms=trigger,
90
+ count=4,
91
+ )
92
+ )
93
+ trigger += 100
94
+ y -= 42
95
+
96
+ trigger = 40000
97
+ y = ctx.width + 0x2C
98
+ for _ in range(6):
99
+ entries.append(
100
+ spawn(
101
+ x=float(ctx.width // 2),
102
+ y=float(y),
103
+ heading=0.0,
104
+ spawn_id=SpawnId.FORMATION_RING_ALIEN_8_12,
105
+ trigger_ms=trigger,
106
+ count=2,
107
+ )
108
+ )
109
+ trigger += 100
110
+ y += 0x20
111
+
112
+ return entries
113
+
114
+
115
+ @register_quest(
116
+ level="5.2",
117
+ title="The Spanking Of The Dead",
118
+ time_limit_ms=480000,
119
+ start_weapon_id=1,
120
+ unlock_perk_id=PerkId.DEATH_CLOCK,
121
+ builder_address=0x004358A0,
122
+ )
123
+ def build_5_2_the_spanking_of_the_dead(ctx: QuestContext) -> list[SpawnEntry]:
124
+ entries: list[SpawnEntry] = [
125
+ spawn(x=256.0, y=512.0, heading=0.0, spawn_id=SpawnId.ALIEN_CONST_WEAPON_BONUS_27, trigger_ms=500, count=1),
126
+ spawn(x=768.0, y=512.0, heading=0.0, spawn_id=SpawnId.ALIEN_CONST_WEAPON_BONUS_27, trigger_ms=500, count=1),
127
+ ]
128
+
129
+ trigger = 5000
130
+ step_index = 0
131
+ while trigger < 0xA988:
132
+ angle = step_index * 0.33333334
133
+ radius = 512.0 - step_index * 3.8
134
+ x = math.cos(angle) * radius + 512.0
135
+ y = math.sin(angle) * radius + 512.0
136
+ entries.append(
137
+ spawn(
138
+ x=x,
139
+ y=y,
140
+ heading=angle,
141
+ spawn_id=SpawnId.ZOMBIE_RANDOM_41,
142
+ trigger_ms=trigger,
143
+ count=1,
144
+ )
145
+ )
146
+ trigger += 300
147
+ step_index += 1
148
+
149
+ offset = step_index * 300
150
+ entries.append(
151
+ spawn(
152
+ x=1280.0,
153
+ y=512.0,
154
+ heading=0.0,
155
+ spawn_id=SpawnId.ZOMBIE_CONST_GREY_42,
156
+ trigger_ms=offset + 10000,
157
+ count=16,
158
+ )
159
+ )
160
+ entries.append(
161
+ spawn(
162
+ x=-256.0,
163
+ y=512.0,
164
+ heading=0.0,
165
+ spawn_id=SpawnId.ZOMBIE_CONST_GREY_42,
166
+ trigger_ms=offset + 20000,
167
+ count=16,
168
+ )
169
+ )
170
+ return entries
171
+
172
+
173
+ @register_quest(
174
+ level="5.3",
175
+ title="The Fortress",
176
+ time_limit_ms=480000,
177
+ start_weapon_id=1,
178
+ unlock_perk_id=PerkId.MY_FAVOURITE_WEAPON,
179
+ builder_address=0x004352D0,
180
+ )
181
+ def build_5_3_the_fortress(ctx: QuestContext) -> list[SpawnEntry]:
182
+ entries: list[SpawnEntry] = [
183
+ spawn(
184
+ x=-50.0,
185
+ y=float(ctx.height // 2),
186
+ heading=0.0,
187
+ spawn_id=SpawnId.SPIDER_SP1_CONST_BLUE_40,
188
+ trigger_ms=100,
189
+ count=6,
190
+ ),
191
+ ]
192
+
193
+ trigger = 1100
194
+ y_seed = 0x200
195
+ while trigger < 0x14B4:
196
+ y = y_seed * 0.125 + 256.0
197
+ entries.append(
198
+ spawn(
199
+ x=768.0,
200
+ y=float(y),
201
+ heading=0.0,
202
+ spawn_id=SpawnId.ALIEN_SPAWNER_CHILD_1D_LIMITED_09,
203
+ trigger_ms=trigger,
204
+ count=1,
205
+ )
206
+ )
207
+ trigger += 600
208
+ y_seed += 0x200
209
+
210
+ entry_count = 8
211
+ x_seed = 0x180
212
+ while x_seed < 0x901:
213
+ trigger = entry_count * 600 + 0x157C
214
+ for row in range(1, 7):
215
+ if row != 1 or x_seed not in (0x480, 0x600):
216
+ x = x_seed * 0.16666667 + 256.0
217
+ y = 512.0 - (row * 0x180) * 0.16666667
218
+ entries.append(
219
+ spawn(
220
+ x=float(x),
221
+ y=float(y),
222
+ heading=0.0,
223
+ spawn_id=SpawnId.ALIEN_SPAWNER_CHILD_32_SLOW_0A,
224
+ trigger_ms=trigger,
225
+ count=1,
226
+ )
227
+ )
228
+ trigger += 600
229
+ entry_count += 1
230
+ x_seed += 0x180
231
+
232
+ return entries
233
+
234
+
235
+ @register_quest(
236
+ level="5.4",
237
+ title="The Gang Wars",
238
+ time_limit_ms=480000,
239
+ start_weapon_id=1,
240
+ unlock_weapon_id=0x1E,
241
+ builder_address=0x00435120,
242
+ )
243
+ def build_5_4_the_gang_wars(ctx: QuestContext) -> list[SpawnEntry]:
244
+ entries: list[SpawnEntry] = [
245
+ spawn(
246
+ x=-150.0,
247
+ y=float(ctx.height // 2),
248
+ heading=0.0,
249
+ spawn_id=SpawnId.FORMATION_RING_ALIEN_8_12,
250
+ trigger_ms=100,
251
+ count=1,
252
+ ),
253
+ spawn(
254
+ x=1174.0,
255
+ y=float(ctx.height // 2),
256
+ heading=0.0,
257
+ spawn_id=SpawnId.FORMATION_RING_ALIEN_8_12,
258
+ trigger_ms=2500,
259
+ count=1,
260
+ ),
261
+ ]
262
+
263
+ trigger = 5500
264
+ for _ in range(10):
265
+ entries.append(
266
+ spawn(
267
+ x=1174.0,
268
+ y=float(ctx.height // 2),
269
+ heading=0.0,
270
+ spawn_id=SpawnId.FORMATION_RING_ALIEN_8_12,
271
+ trigger_ms=trigger,
272
+ count=2,
273
+ )
274
+ )
275
+ trigger += 4000
276
+
277
+ entries.append(
278
+ spawn(
279
+ x=512.0,
280
+ y=1152.0,
281
+ heading=0.0,
282
+ spawn_id=SpawnId.FORMATION_CHAIN_ALIEN_10_13,
283
+ trigger_ms=50500,
284
+ count=1,
285
+ )
286
+ )
287
+
288
+ trigger = 59500
289
+ while trigger < 0x184AC:
290
+ entries.append(
291
+ spawn(
292
+ x=-150.0,
293
+ y=float(ctx.height // 2),
294
+ heading=0.0,
295
+ spawn_id=SpawnId.FORMATION_RING_ALIEN_8_12,
296
+ trigger_ms=trigger,
297
+ count=2,
298
+ )
299
+ )
300
+ trigger += 4000
301
+
302
+ entries.append(
303
+ spawn(
304
+ x=512.0,
305
+ y=1152.0,
306
+ heading=0.0,
307
+ spawn_id=SpawnId.FORMATION_CHAIN_ALIEN_10_13,
308
+ trigger_ms=107500,
309
+ count=3,
310
+ )
311
+ )
312
+ return entries
313
+
314
+
315
+ @register_quest(
316
+ level="5.5",
317
+ title="Knee-deep in the Dead",
318
+ time_limit_ms=480000,
319
+ start_weapon_id=1,
320
+ unlock_perk_id=PerkId.BANDAGE,
321
+ builder_address=0x00434F00,
322
+ )
323
+ def build_5_5_knee_deep_in_the_dead(ctx: QuestContext) -> list[SpawnEntry]:
324
+ entries: list[SpawnEntry] = [
325
+ spawn(
326
+ x=-50.0,
327
+ y=float(ctx.height * 0.5),
328
+ heading=0.0,
329
+ spawn_id=SpawnId.ZOMBIE_CONST_GREEN_BRUTE_43,
330
+ trigger_ms=100,
331
+ count=1,
332
+ ),
333
+ ]
334
+
335
+ trigger = 500
336
+ wave = 0
337
+ while trigger < 0x178F4:
338
+ if wave % 8 == 0:
339
+ entries.append(
340
+ spawn(
341
+ x=-50.0,
342
+ y=float(ctx.height * 0.5),
343
+ heading=0.0,
344
+ spawn_id=SpawnId.ZOMBIE_CONST_GREEN_BRUTE_43,
345
+ trigger_ms=trigger - 2,
346
+ count=1,
347
+ )
348
+ )
349
+ count = 2 if wave > 0x20 else 1
350
+ entries.append(
351
+ spawn(
352
+ x=-50.0,
353
+ y=float(ctx.height * 0.5),
354
+ heading=0.0,
355
+ spawn_id=SpawnId.ZOMBIE_RANDOM_41,
356
+ trigger_ms=trigger,
357
+ count=count,
358
+ )
359
+ )
360
+ if trigger > 0x30D4:
361
+ entries.append(
362
+ spawn(
363
+ x=-50.0,
364
+ y=float(ctx.height * 0.5 + 158.0),
365
+ heading=0.0,
366
+ spawn_id=SpawnId.ZOMBIE_RANDOM_41,
367
+ trigger_ms=trigger + 500,
368
+ count=1,
369
+ )
370
+ )
371
+ if trigger > 0x5FB4:
372
+ entries.append(
373
+ spawn(
374
+ x=-50.0,
375
+ y=float(ctx.height * 0.5 - 158.0),
376
+ heading=0.0,
377
+ spawn_id=SpawnId.ZOMBIE_RANDOM_41,
378
+ trigger_ms=trigger + 1000,
379
+ count=1,
380
+ )
381
+ )
382
+ if trigger > 0x8E94:
383
+ entries.append(
384
+ spawn(
385
+ x=-50.0,
386
+ y=float(ctx.height * 0.5 - 258.0),
387
+ heading=0.0,
388
+ spawn_id=SpawnId.ZOMBIE_CONST_GREY_42,
389
+ trigger_ms=trigger + 0x514,
390
+ count=1,
391
+ )
392
+ )
393
+ if trigger > 0xBD74:
394
+ entries.append(
395
+ spawn(
396
+ x=-50.0,
397
+ y=float(ctx.height * 0.5 + 258.0),
398
+ heading=0.0,
399
+ spawn_id=SpawnId.ZOMBIE_CONST_GREY_42,
400
+ trigger_ms=trigger + 300,
401
+ count=1,
402
+ )
403
+ )
404
+ trigger += 0x5DC
405
+ wave += 1
406
+
407
+ return entries
408
+
409
+
410
+ @register_quest(
411
+ level="5.6",
412
+ title="Cross Fire",
413
+ time_limit_ms=480000,
414
+ start_weapon_id=1,
415
+ unlock_perk_id=PerkId.ANGRY_RELOADER,
416
+ builder_address=0x00435480,
417
+ )
418
+ def build_5_6_cross_fire(ctx: QuestContext) -> list[SpawnEntry]:
419
+ return [
420
+ spawn(
421
+ x=1074.0,
422
+ y=float(ctx.height * 0.5),
423
+ heading=0.0,
424
+ spawn_id=SpawnId.SPIDER_SP1_CONST_BLUE_40,
425
+ trigger_ms=100,
426
+ count=6,
427
+ ),
428
+ spawn(
429
+ x=-40.0,
430
+ y=512.0,
431
+ heading=0.0,
432
+ spawn_id=SpawnId.SPIDER_SP1_CONST_RANGED_VARIANT_3C,
433
+ trigger_ms=5500,
434
+ count=4,
435
+ ),
436
+ spawn(
437
+ x=-40.0,
438
+ y=512.0,
439
+ heading=0.0,
440
+ spawn_id=SpawnId.SPIDER_SP1_CONST_RANGED_VARIANT_3C,
441
+ trigger_ms=15500,
442
+ count=6,
443
+ ),
444
+ spawn(
445
+ x=512.0,
446
+ y=512.0,
447
+ heading=0.0,
448
+ spawn_id=SpawnId.SPIDER_SP2_SPLITTER_01,
449
+ trigger_ms=18500,
450
+ count=2,
451
+ ),
452
+ spawn(
453
+ x=-100.0,
454
+ y=512.0,
455
+ heading=0.0,
456
+ spawn_id=SpawnId.SPIDER_SP1_CONST_RANGED_VARIANT_3C,
457
+ trigger_ms=25500,
458
+ count=8,
459
+ ),
460
+ spawn(
461
+ x=512.0,
462
+ y=1152.0,
463
+ heading=0.0,
464
+ spawn_id=SpawnId.SPIDER_SP1_CONST_BLUE_40,
465
+ trigger_ms=26000,
466
+ count=6,
467
+ ),
468
+ spawn(
469
+ x=512.0,
470
+ y=-128.0,
471
+ heading=0.0,
472
+ spawn_id=SpawnId.SPIDER_SP1_CONST_BLUE_40,
473
+ trigger_ms=26000,
474
+ count=6,
475
+ ),
476
+ ]
477
+
478
+
479
+ @register_quest(
480
+ level="5.7",
481
+ title="Army of Three",
482
+ time_limit_ms=480000,
483
+ start_weapon_id=1,
484
+ builder_address=0x00434CA0,
485
+ )
486
+ def build_5_7_army_of_three(ctx: QuestContext) -> list[SpawnEntry]:
487
+ return [
488
+ spawn(x=-64.0, y=256.0, heading=0.0, spawn_id=SpawnId.FORMATION_GRID_ALIEN_WHITE_15, trigger_ms=500, count=1),
489
+ spawn(
490
+ x=-64.0,
491
+ y=512.0,
492
+ heading=0.0,
493
+ spawn_id=SpawnId.FORMATION_GRID_ALIEN_WHITE_15,
494
+ trigger_ms=5500,
495
+ count=1,
496
+ ),
497
+ spawn(
498
+ x=-64.0,
499
+ y=768.0,
500
+ heading=0.0,
501
+ spawn_id=SpawnId.FORMATION_GRID_ALIEN_WHITE_15,
502
+ trigger_ms=15000,
503
+ count=1,
504
+ ),
505
+ spawn(
506
+ x=-64.0,
507
+ y=768.0,
508
+ heading=0.0,
509
+ spawn_id=SpawnId.FORMATION_GRID_SPIDER_SP1_WHITE_17,
510
+ trigger_ms=19500,
511
+ count=1,
512
+ ),
513
+ spawn(
514
+ x=-64.0,
515
+ y=512.0,
516
+ heading=0.0,
517
+ spawn_id=SpawnId.FORMATION_GRID_SPIDER_SP1_WHITE_17,
518
+ trigger_ms=22500,
519
+ count=1,
520
+ ),
521
+ spawn(
522
+ x=-64.0,
523
+ y=256.0,
524
+ heading=0.0,
525
+ spawn_id=SpawnId.FORMATION_GRID_SPIDER_SP1_WHITE_17,
526
+ trigger_ms=26500,
527
+ count=1,
528
+ ),
529
+ spawn(
530
+ x=-64.0,
531
+ y=256.0,
532
+ heading=0.0,
533
+ spawn_id=SpawnId.FORMATION_GRID_LIZARD_WHITE_16,
534
+ trigger_ms=35500,
535
+ count=1,
536
+ ),
537
+ spawn(
538
+ x=-64.0,
539
+ y=512.0,
540
+ heading=0.0,
541
+ spawn_id=SpawnId.FORMATION_GRID_LIZARD_WHITE_16,
542
+ trigger_ms=39500,
543
+ count=1,
544
+ ),
545
+ spawn(
546
+ x=-64.0,
547
+ y=768.0,
548
+ heading=0.0,
549
+ spawn_id=SpawnId.FORMATION_GRID_LIZARD_WHITE_16,
550
+ trigger_ms=42500,
551
+ count=1,
552
+ ),
553
+ spawn(
554
+ x=512.0,
555
+ y=1152.0,
556
+ heading=0.0,
557
+ spawn_id=SpawnId.FORMATION_GRID_ALIEN_WHITE_15,
558
+ trigger_ms=52500,
559
+ count=3,
560
+ ),
561
+ spawn(
562
+ x=512.0,
563
+ y=-256.0,
564
+ heading=0.0,
565
+ spawn_id=SpawnId.FORMATION_GRID_SPIDER_SP1_WHITE_17,
566
+ trigger_ms=56500,
567
+ count=3,
568
+ ),
569
+ ]
570
+
571
+
572
+ @register_quest(
573
+ level="5.8",
574
+ title="Monster Blues",
575
+ time_limit_ms=480000,
576
+ start_weapon_id=1,
577
+ unlock_perk_id=PerkId.ION_GUN_MASTER,
578
+ builder_address=0x00434860,
579
+ )
580
+ def build_5_8_monster_blues(ctx: QuestContext) -> list[SpawnEntry]:
581
+ entries: list[SpawnEntry] = [
582
+ spawn(
583
+ x=-50.0,
584
+ y=float(ctx.height * 0.5),
585
+ heading=0.0,
586
+ spawn_id=SpawnId.LIZARD_RANDOM_04,
587
+ trigger_ms=500,
588
+ count=10,
589
+ ),
590
+ spawn(
591
+ x=1074.0,
592
+ y=float(ctx.height * 0.5),
593
+ heading=0.0,
594
+ spawn_id=SpawnId.ALIEN_RANDOM_06,
595
+ trigger_ms=7500,
596
+ count=10,
597
+ ),
598
+ spawn(
599
+ x=512.0,
600
+ y=1088.0,
601
+ heading=0.0,
602
+ spawn_id=SpawnId.SPIDER_SP1_RANDOM_03,
603
+ trigger_ms=17500,
604
+ count=12,
605
+ ),
606
+ spawn(
607
+ x=512.0,
608
+ y=-64.0,
609
+ heading=0.0,
610
+ spawn_id=SpawnId.SPIDER_SP1_RANDOM_03,
611
+ trigger_ms=17500,
612
+ count=12,
613
+ ),
614
+ ]
615
+
616
+ trigger = 27500
617
+ for idx in range(0x40):
618
+ if idx % 4 == 0:
619
+ spawn_id = SpawnId.ALIEN_RANDOM_06
620
+ elif idx % 4 == 1:
621
+ spawn_id = SpawnId.SPIDER_SP1_RANDOM_03
622
+ else:
623
+ spawn_id = SpawnId.SPIDER_SP2_RANDOM_05
624
+ count = idx // 8 + 2
625
+ entries.append(
626
+ spawn(
627
+ x=-64.0,
628
+ y=512.0,
629
+ heading=0.0,
630
+ spawn_id=spawn_id,
631
+ trigger_ms=trigger,
632
+ count=count,
633
+ )
634
+ )
635
+ trigger += 900
636
+ return entries
637
+
638
+
639
+ @register_quest(
640
+ level="5.9",
641
+ title="Nagolipoli",
642
+ time_limit_ms=480000,
643
+ start_weapon_id=1,
644
+ unlock_perk_id=PerkId.STATIONARY_RELOADER,
645
+ builder_address=0x00434480,
646
+ )
647
+ def build_5_9_nagolipoli(ctx: QuestContext) -> list[SpawnEntry]:
648
+ entries: list[SpawnEntry] = []
649
+
650
+ center_x, center_y = center_point(ctx.width, ctx.height)
651
+ for x, y, angle in ring_points(center_x, center_y, 128.0, 8, step=0.7853982):
652
+ entries.append(spawn(x=x, y=y, heading=angle, spawn_id=SpawnId.SPIDER_SP1_CONST_BLUE_40, trigger_ms=2000, count=1))
653
+
654
+ for x, y, angle in ring_points(center_x, center_y, 178.0, 12, step=0.5235988):
655
+ entries.append(spawn(x=x, y=y, heading=angle, spawn_id=SpawnId.SPIDER_SP1_CONST_BLUE_40, trigger_ms=8000, count=1))
656
+
657
+ trigger = 13000
658
+ wave = 0
659
+ while trigger < 0x96C8:
660
+ count = wave // 8 + 1
661
+ entries.extend(
662
+ [
663
+ spawn(
664
+ x=-64.0,
665
+ y=-64.0,
666
+ heading=1.0471976,
667
+ spawn_id=SpawnId.AI1_LIZARD_BLUE_TINT_1C,
668
+ trigger_ms=trigger,
669
+ count=count,
670
+ ),
671
+ spawn(
672
+ x=1088.0,
673
+ y=-64.0,
674
+ heading=-1.0471976,
675
+ spawn_id=SpawnId.AI1_LIZARD_BLUE_TINT_1C,
676
+ trigger_ms=trigger,
677
+ count=count,
678
+ ),
679
+ spawn(
680
+ x=-64.0,
681
+ y=1088.0,
682
+ heading=-1.0471976,
683
+ spawn_id=SpawnId.AI1_LIZARD_BLUE_TINT_1C,
684
+ trigger_ms=trigger,
685
+ count=count,
686
+ ),
687
+ spawn(
688
+ x=1088.0,
689
+ y=1088.0,
690
+ heading=3.926991,
691
+ spawn_id=SpawnId.AI1_LIZARD_BLUE_TINT_1C,
692
+ trigger_ms=trigger,
693
+ count=count,
694
+ ),
695
+ ]
696
+ )
697
+ trigger += 800
698
+ wave += 1
699
+
700
+ last_wave = max(wave - 1, 0)
701
+ base_left = (last_wave + 0x97 + wave * 4) * 0xA0
702
+ for idx in range(6):
703
+ y = idx * 85.333336 + 256.0
704
+ entries.append(
705
+ spawn(
706
+ x=64.0,
707
+ y=y,
708
+ heading=0.0,
709
+ spawn_id=SpawnId.ALIEN_SPAWNER_CHILD_32_SLOW_0A,
710
+ trigger_ms=base_left,
711
+ count=1,
712
+ )
713
+ )
714
+ base_left += 100
715
+
716
+ base_right = wave * 800 + 25000
717
+ for idx in range(6):
718
+ y = idx * 85.333336 + 256.0
719
+ entries.append(
720
+ spawn(
721
+ x=960.0,
722
+ y=y,
723
+ heading=0.0,
724
+ spawn_id=SpawnId.ALIEN_SPAWNER_CHILD_32_SLOW_0A,
725
+ trigger_ms=base_right,
726
+ count=1,
727
+ )
728
+ )
729
+ base_right += 100
730
+
731
+ base_mid = (last_wave + 0xB0 + wave * 4) * 0xA0
732
+ entries.append(
733
+ spawn(
734
+ x=512.0,
735
+ y=256.0,
736
+ heading=math.pi,
737
+ spawn_id=SpawnId.ALIEN_SPAWNER_CHILD_3C_SLOW_0B,
738
+ trigger_ms=base_mid,
739
+ count=1,
740
+ )
741
+ )
742
+ entries.append(
743
+ spawn(
744
+ x=512.0,
745
+ y=768.0,
746
+ heading=math.pi,
747
+ spawn_id=SpawnId.ALIEN_SPAWNER_CHILD_3C_SLOW_0B,
748
+ trigger_ms=base_mid,
749
+ count=1,
750
+ )
751
+ )
752
+
753
+ base_vertical = wave * 800 + 0x6F54
754
+ entries.append(
755
+ spawn(
756
+ x=512.0,
757
+ y=1088.0,
758
+ heading=3.926991,
759
+ spawn_id=SpawnId.AI1_LIZARD_BLUE_TINT_1C,
760
+ trigger_ms=base_vertical,
761
+ count=8,
762
+ )
763
+ )
764
+ entries.append(
765
+ spawn(
766
+ x=512.0,
767
+ y=-64.0,
768
+ heading=3.926991,
769
+ spawn_id=SpawnId.AI1_LIZARD_BLUE_TINT_1C,
770
+ trigger_ms=base_vertical,
771
+ count=8,
772
+ )
773
+ )
774
+ return entries
775
+
776
+
777
+ @register_quest(
778
+ level="5.10",
779
+ title="The Gathering",
780
+ time_limit_ms=480000,
781
+ start_weapon_id=1,
782
+ unlock_weapon_id=0x1C,
783
+ builder_address=0x004349C0,
784
+ )
785
+ def build_5_10_the_gathering(ctx: QuestContext) -> list[SpawnEntry]:
786
+ return [
787
+ spawn(x=256.0, y=512.0, heading=0.0, spawn_id=SpawnId.SPIDER_SP2_SPLITTER_01, trigger_ms=500, count=1),
788
+ spawn(x=768.0, y=512.0, heading=0.0, spawn_id=SpawnId.SPIDER_SP2_SPLITTER_01, trigger_ms=9500, count=2),
789
+ spawn(
790
+ x=256.0,
791
+ y=512.0,
792
+ heading=0.0,
793
+ spawn_id=SpawnId.SPIDER_SP1_CONST_SHOCK_BOSS_3A,
794
+ trigger_ms=15500,
795
+ count=2,
796
+ ),
797
+ spawn(
798
+ x=768.0,
799
+ y=512.0,
800
+ heading=0.0,
801
+ spawn_id=SpawnId.SPIDER_SP1_CONST_SHOCK_BOSS_3A,
802
+ trigger_ms=24500,
803
+ count=2,
804
+ ),
805
+ spawn(
806
+ x=256.0,
807
+ y=512.0,
808
+ heading=0.0,
809
+ spawn_id=SpawnId.ZOMBIE_BOSS_SPAWNER_00,
810
+ trigger_ms=30500,
811
+ count=2,
812
+ ),
813
+ spawn(
814
+ x=768.0,
815
+ y=512.0,
816
+ heading=0.0,
817
+ spawn_id=SpawnId.ZOMBIE_BOSS_SPAWNER_00,
818
+ trigger_ms=39500,
819
+ count=2,
820
+ ),
821
+ spawn(x=64.0, y=64.0, heading=0.0, spawn_id=SpawnId.SPIDER_SP1_CONST_RANGED_VARIANT_3C, trigger_ms=54500, count=2),
822
+ spawn(
823
+ x=960.0,
824
+ y=64.0,
825
+ heading=0.0,
826
+ spawn_id=SpawnId.SPIDER_SP1_CONST_RANGED_VARIANT_3C,
827
+ trigger_ms=54500,
828
+ count=1,
829
+ ),
830
+ spawn(
831
+ x=64.0,
832
+ y=960.0,
833
+ heading=0.0,
834
+ spawn_id=SpawnId.SPIDER_SP1_CONST_RANGED_VARIANT_3C,
835
+ trigger_ms=54500,
836
+ count=2,
837
+ ),
838
+ spawn(
839
+ x=960.0,
840
+ y=960.0,
841
+ heading=0.0,
842
+ spawn_id=SpawnId.SPIDER_SP1_CONST_RANGED_VARIANT_3C,
843
+ trigger_ms=54500,
844
+ count=1,
845
+ ),
846
+ spawn(
847
+ x=-128.0,
848
+ y=512.0,
849
+ heading=0.0,
850
+ spawn_id=SpawnId.SPIDER_SP1_CONST_SHOCK_BOSS_3A,
851
+ trigger_ms=90500,
852
+ count=6,
853
+ ),
854
+ spawn(
855
+ x=1152.0,
856
+ y=512.0,
857
+ heading=0.0,
858
+ spawn_id=SpawnId.SPIDER_SP2_SPLITTER_01,
859
+ trigger_ms=99500,
860
+ count=4,
861
+ ),
862
+ spawn(
863
+ x=1152.0,
864
+ y=512.0,
865
+ heading=0.0,
866
+ spawn_id=SpawnId.SPIDER_SP2_SPLITTER_01,
867
+ trigger_ms=109500,
868
+ count=2,
869
+ ),
870
+ ]
871
+
872
+
873
+ __all__ = [
874
+ "QuestContext",
875
+ "SpawnEntry",
876
+ "build_5_1_the_beating",
877
+ "build_5_2_the_spanking_of_the_dead",
878
+ "build_5_3_the_fortress",
879
+ "build_5_4_the_gang_wars",
880
+ "build_5_5_knee_deep_in_the_dead",
881
+ "build_5_6_cross_fire",
882
+ "build_5_7_army_of_three",
883
+ "build_5_8_monster_blues",
884
+ "build_5_9_nagolipoli",
885
+ "build_5_10_the_gathering",
886
+ ]