crimsonland 0.1.0.dev1__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 (138) 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 +153 -0
  5. crimson/bonuses.py +167 -0
  6. crimson/camera.py +75 -0
  7. crimson/cli.py +377 -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 +663 -0
  34. crimson/gameplay.py +2450 -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 +1039 -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 +1338 -0
  65. crimson/sim/__init__.py +1 -0
  66. crimson/sim/world_defs.py +56 -0
  67. crimson/sim/world_state.py +421 -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 +414 -0
  86. crimson/views/bonuses.py +201 -0
  87. crimson/views/camera_debug.py +359 -0
  88. crimson/views/camera_shake.py +229 -0
  89. crimson/views/corpse_stamp_debug.py +324 -0
  90. crimson/views/decals_debug.py +739 -0
  91. crimson/views/empty.py +19 -0
  92. crimson/views/fonts.py +114 -0
  93. crimson/views/game_over.py +117 -0
  94. crimson/views/ground.py +259 -0
  95. crimson/views/lighting_debug.py +1166 -0
  96. crimson/views/particles.py +293 -0
  97. crimson/views/perk_menu_debug.py +430 -0
  98. crimson/views/perks.py +398 -0
  99. crimson/views/player.py +433 -0
  100. crimson/views/player_sprite_debug.py +314 -0
  101. crimson/views/projectile_fx.py +608 -0
  102. crimson/views/projectile_render_debug.py +407 -0
  103. crimson/views/projectiles.py +221 -0
  104. crimson/views/quest_title_overlay.py +108 -0
  105. crimson/views/registry.py +34 -0
  106. crimson/views/rush.py +16 -0
  107. crimson/views/small_font_debug.py +204 -0
  108. crimson/views/spawn_plan.py +363 -0
  109. crimson/views/sprites.py +214 -0
  110. crimson/views/survival.py +15 -0
  111. crimson/views/terrain.py +132 -0
  112. crimson/views/ui.py +123 -0
  113. crimson/views/wicons.py +166 -0
  114. crimson/weapon_sfx.py +63 -0
  115. crimson/weapons.py +860 -0
  116. crimsonland-0.1.0.dev1.dist-info/METADATA +9 -0
  117. crimsonland-0.1.0.dev1.dist-info/RECORD +138 -0
  118. crimsonland-0.1.0.dev1.dist-info/WHEEL +4 -0
  119. crimsonland-0.1.0.dev1.dist-info/entry_points.txt +4 -0
  120. grim/__init__.py +20 -0
  121. grim/app.py +92 -0
  122. grim/assets.py +231 -0
  123. grim/audio.py +106 -0
  124. grim/config.py +294 -0
  125. grim/console.py +737 -0
  126. grim/fonts/__init__.py +7 -0
  127. grim/fonts/grim_mono.py +111 -0
  128. grim/fonts/small.py +120 -0
  129. grim/input.py +44 -0
  130. grim/jaz.py +103 -0
  131. grim/math.py +17 -0
  132. grim/music.py +403 -0
  133. grim/paq.py +76 -0
  134. grim/rand.py +37 -0
  135. grim/sfx.py +276 -0
  136. grim/sfx_map.py +103 -0
  137. grim/terrain_render.py +840 -0
  138. grim/view.py +16 -0
@@ -0,0 +1,721 @@
1
+ from __future__ import annotations
2
+
3
+ from ..perks import PerkId
4
+ from ..creatures.spawn import SpawnId
5
+ from .helpers import (
6
+ center_point,
7
+ edge_midpoints,
8
+ ring_points,
9
+ spawn,
10
+ spawn_at,
11
+ )
12
+ from .registry import register_quest
13
+ from .types import QuestContext, SpawnEntry
14
+
15
+
16
+ @register_quest(
17
+ level="4.1",
18
+ title="Major Alien Breach",
19
+ time_limit_ms=300000,
20
+ start_weapon_id=18,
21
+ unlock_perk_id=PerkId.JINXED,
22
+ builder_address=0x00437AF0,
23
+ )
24
+ def build_4_1_major_alien_breach(ctx: QuestContext) -> list[SpawnEntry]:
25
+ entries: list[SpawnEntry] = []
26
+ edges = edge_midpoints(ctx.width)
27
+ trigger = 4000
28
+ for offset in range(0, 0x5DC, 0xF):
29
+ entries.append(
30
+ spawn_at(
31
+ edges.right,
32
+ heading=0.0,
33
+ spawn_id=SpawnId.ALIEN_RANDOM_GREEN_20,
34
+ trigger_ms=trigger,
35
+ count=2,
36
+ )
37
+ )
38
+ entries.append(
39
+ spawn_at(
40
+ edges.top,
41
+ heading=0.0,
42
+ spawn_id=SpawnId.ALIEN_RANDOM_GREEN_20,
43
+ trigger_ms=trigger,
44
+ count=2,
45
+ )
46
+ )
47
+ trigger += 2000 - offset
48
+ if trigger < 1000:
49
+ trigger = 1000
50
+ return entries
51
+
52
+
53
+ @register_quest(
54
+ level="4.2",
55
+ title="Zombie Time",
56
+ time_limit_ms=300000,
57
+ start_weapon_id=1,
58
+ unlock_weapon_id=0x13,
59
+ builder_address=0x00437D70,
60
+ )
61
+ def build_4_2_zombie_time(ctx: QuestContext) -> list[SpawnEntry]:
62
+ entries: list[SpawnEntry] = []
63
+ edges = edge_midpoints(ctx.width)
64
+ trigger = 1500
65
+ while trigger < 0x17CDC:
66
+ entries.append(
67
+ spawn_at(
68
+ edges.right,
69
+ heading=0.0,
70
+ spawn_id=SpawnId.ZOMBIE_RANDOM_41,
71
+ trigger_ms=trigger,
72
+ count=8,
73
+ )
74
+ )
75
+ entries.append(
76
+ spawn_at(
77
+ edges.left,
78
+ heading=0.0,
79
+ spawn_id=SpawnId.ZOMBIE_RANDOM_41,
80
+ trigger_ms=trigger,
81
+ count=8,
82
+ )
83
+ )
84
+ trigger += 8000
85
+ return entries
86
+
87
+
88
+ @register_quest(
89
+ level="4.3",
90
+ title="Lizard Zombie Pact",
91
+ time_limit_ms=300000,
92
+ start_weapon_id=1,
93
+ unlock_perk_id=PerkId.PERK_MASTER,
94
+ builder_address=0x00438700,
95
+ )
96
+ def build_4_3_lizard_zombie_pact(ctx: QuestContext) -> list[SpawnEntry]:
97
+ entries: list[SpawnEntry] = []
98
+ edges = edge_midpoints(ctx.width)
99
+ trigger = 1500
100
+ wave = 0
101
+ while trigger < 0x1BB5C:
102
+ entries.append(
103
+ spawn_at(
104
+ edges.right,
105
+ heading=0.0,
106
+ spawn_id=SpawnId.ZOMBIE_RANDOM_41,
107
+ trigger_ms=trigger,
108
+ count=6,
109
+ )
110
+ )
111
+ entries.append(
112
+ spawn_at(
113
+ edges.left,
114
+ heading=0.0,
115
+ spawn_id=SpawnId.ZOMBIE_RANDOM_41,
116
+ trigger_ms=trigger,
117
+ count=6,
118
+ )
119
+ )
120
+ if wave % 5 == 0:
121
+ idx = wave // 5
122
+ entries.append(
123
+ spawn(
124
+ x=356.0,
125
+ y=float(idx * 0xB4 + 0x100),
126
+ heading=0.0,
127
+ spawn_id=SpawnId.ALIEN_SPAWNER_CHILD_31_FAST_0C,
128
+ trigger_ms=trigger,
129
+ count=idx + 1,
130
+ )
131
+ )
132
+ entries.append(
133
+ spawn(
134
+ x=356.0,
135
+ y=float(idx * 0xB4 + 0x180),
136
+ heading=0.0,
137
+ spawn_id=SpawnId.ALIEN_SPAWNER_CHILD_31_FAST_0C,
138
+ trigger_ms=trigger,
139
+ count=idx + 2,
140
+ )
141
+ )
142
+ trigger += 7000
143
+ wave += 1
144
+ return entries
145
+
146
+
147
+ @register_quest(
148
+ level="4.4",
149
+ title="The Collaboration",
150
+ time_limit_ms=360000,
151
+ start_weapon_id=1,
152
+ unlock_weapon_id=0x0E,
153
+ builder_address=0x00437F30,
154
+ )
155
+ def build_4_4_the_collaboration(ctx: QuestContext) -> list[SpawnEntry]:
156
+ entries: list[SpawnEntry] = []
157
+ edges = edge_midpoints(ctx.width)
158
+ trigger = 1500
159
+ wave = 0
160
+ while trigger < 0x2B55C:
161
+ count = int(wave * 0.8 + 7)
162
+ entries.append(
163
+ spawn_at(
164
+ edges.right,
165
+ heading=0.0,
166
+ spawn_id=SpawnId.AI1_ALIEN_BLUE_TINT_1A,
167
+ trigger_ms=trigger,
168
+ count=count,
169
+ )
170
+ )
171
+ entries.append(
172
+ spawn_at(
173
+ edges.bottom,
174
+ heading=0.0,
175
+ spawn_id=SpawnId.AI1_SPIDER_SP1_BLUE_TINT_1B,
176
+ trigger_ms=trigger,
177
+ count=count,
178
+ )
179
+ )
180
+ entries.append(
181
+ spawn_at(
182
+ edges.left,
183
+ heading=0.0,
184
+ spawn_id=SpawnId.AI1_LIZARD_BLUE_TINT_1C,
185
+ trigger_ms=trigger,
186
+ count=count,
187
+ )
188
+ )
189
+ entries.append(
190
+ spawn_at(
191
+ edges.top,
192
+ heading=0.0,
193
+ spawn_id=SpawnId.ZOMBIE_RANDOM_41,
194
+ trigger_ms=trigger,
195
+ count=count,
196
+ )
197
+ )
198
+ trigger += 11000
199
+ wave += 1
200
+ return entries
201
+
202
+
203
+ @register_quest(
204
+ level="4.5",
205
+ title="The Massacre",
206
+ time_limit_ms=300000,
207
+ start_weapon_id=1,
208
+ unlock_perk_id=PerkId.REFLEX_BOOSTED,
209
+ builder_address=0x004383E0,
210
+ )
211
+ def build_4_5_the_massacre(ctx: QuestContext) -> list[SpawnEntry]:
212
+ entries: list[SpawnEntry] = []
213
+ edges = edge_midpoints(ctx.width)
214
+ edges_wide = edge_midpoints(ctx.width, offset=128.0)
215
+ trigger = 1500
216
+ wave = 0
217
+ while trigger < 0x1656C:
218
+ entries.append(
219
+ spawn_at(
220
+ edges.right,
221
+ heading=0.0,
222
+ spawn_id=SpawnId.ZOMBIE_RANDOM_41,
223
+ trigger_ms=trigger,
224
+ count=wave + 3,
225
+ )
226
+ )
227
+ if wave % 2 == 0:
228
+ entries.append(
229
+ spawn_at(
230
+ edges_wide.right,
231
+ heading=0.0,
232
+ spawn_id=SpawnId.ALIEN_CONST_RED_FAST_2B,
233
+ trigger_ms=trigger,
234
+ count=wave + 1,
235
+ )
236
+ )
237
+ trigger += 5000
238
+ wave += 1
239
+ return entries
240
+
241
+
242
+ @register_quest(
243
+ level="4.6",
244
+ title="The Unblitzkrieg",
245
+ time_limit_ms=600000,
246
+ start_weapon_id=1,
247
+ unlock_weapon_id=0x11,
248
+ builder_address=0x00438A40,
249
+ )
250
+ def build_4_6_the_unblitzkrieg(ctx: QuestContext) -> list[SpawnEntry]:
251
+ def spawn_id_for(toggle: bool) -> int:
252
+ return SpawnId.ALIEN_SPAWNER_CHILD_31_SLOW_0D if toggle else SpawnId.ALIEN_SPAWNER_CHILD_1D_FAST_07
253
+
254
+ entries: list[SpawnEntry] = []
255
+ trigger = 500
256
+
257
+ i_var5 = 0
258
+ for idx in range(10):
259
+ y = float(i_var5 // 10 + 200)
260
+ entries.append(
261
+ spawn(
262
+ x=824.0,
263
+ y=y,
264
+ heading=0.0,
265
+ spawn_id=spawn_id_for(idx % 2 == 1),
266
+ trigger_ms=trigger,
267
+ count=1,
268
+ )
269
+ )
270
+ trigger += 1800
271
+ i_var5 += 0x270
272
+
273
+ i_var5 = 0
274
+ toggle = False
275
+ for _ in range(10):
276
+ x = float(0x338 - i_var5 // 10)
277
+ entries.append(
278
+ spawn(
279
+ x=x,
280
+ y=824.0,
281
+ heading=0.0,
282
+ spawn_id=spawn_id_for(toggle),
283
+ trigger_ms=trigger,
284
+ count=1,
285
+ )
286
+ )
287
+ trigger += 1500
288
+ toggle = not toggle
289
+ i_var5 += 0x270
290
+
291
+ entries.append(
292
+ spawn(
293
+ x=512.0,
294
+ y=512.0,
295
+ heading=0.0,
296
+ spawn_id=SpawnId.ALIEN_SPAWNER_CHILD_1D_FAST_07,
297
+ trigger_ms=trigger,
298
+ count=1,
299
+ )
300
+ )
301
+
302
+ i_var5 = 0
303
+ toggle = False
304
+ for _ in range(10):
305
+ y = float(0x338 - i_var5 // 10)
306
+ entries.append(
307
+ spawn(
308
+ x=200.0,
309
+ y=y,
310
+ heading=0.0,
311
+ spawn_id=spawn_id_for(toggle),
312
+ trigger_ms=trigger,
313
+ count=1,
314
+ )
315
+ )
316
+ trigger += 1200
317
+ toggle = not toggle
318
+ i_var5 += 0x270
319
+
320
+ i_var5 = 0
321
+ toggle = False
322
+ for _ in range(10):
323
+ x = float(i_var5 // 10 + 200)
324
+ entries.append(
325
+ spawn(
326
+ x=x,
327
+ y=200.0,
328
+ heading=0.0,
329
+ spawn_id=spawn_id_for(toggle),
330
+ trigger_ms=trigger,
331
+ count=1,
332
+ )
333
+ )
334
+ trigger += 800
335
+ toggle = not toggle
336
+ i_var5 += 0x270
337
+
338
+ i_var5 = 0
339
+ toggle = False
340
+ for _ in range(10):
341
+ y = float(i_var5 // 10 + 200)
342
+ entries.append(
343
+ spawn(
344
+ x=824.0,
345
+ y=y,
346
+ heading=0.0,
347
+ spawn_id=spawn_id_for(toggle),
348
+ trigger_ms=trigger,
349
+ count=1,
350
+ )
351
+ )
352
+ trigger += 800
353
+ toggle = not toggle
354
+ i_var5 += 0x270
355
+
356
+ i_var5 = 0
357
+ toggle = False
358
+ for _ in range(10):
359
+ x = float(0x338 - i_var5 // 10)
360
+ entries.append(
361
+ spawn(
362
+ x=x,
363
+ y=824.0,
364
+ heading=0.0,
365
+ spawn_id=spawn_id_for(toggle),
366
+ trigger_ms=trigger,
367
+ count=1,
368
+ )
369
+ )
370
+ trigger += 700
371
+ toggle = not toggle
372
+ i_var5 += 0x270
373
+
374
+ i_var5 = 0
375
+ toggle = False
376
+ for _ in range(10):
377
+ y = float(0x338 - i_var5 // 10)
378
+ entries.append(
379
+ spawn(
380
+ x=200.0,
381
+ y=y,
382
+ heading=0.0,
383
+ spawn_id=spawn_id_for(toggle),
384
+ trigger_ms=trigger,
385
+ count=1,
386
+ )
387
+ )
388
+ trigger += 700
389
+ toggle = not toggle
390
+ i_var5 += 0x270
391
+
392
+ i_var5 = 0
393
+ toggle = False
394
+ for _ in range(10):
395
+ x = float(i_var5 // 10 + 200)
396
+ entries.append(
397
+ spawn(
398
+ x=x,
399
+ y=200.0,
400
+ heading=0.0,
401
+ spawn_id=spawn_id_for(toggle),
402
+ trigger_ms=trigger,
403
+ count=1,
404
+ )
405
+ )
406
+ trigger += 800
407
+ toggle = not toggle
408
+ i_var5 += 0x270
409
+ return entries
410
+
411
+
412
+ @register_quest(
413
+ level="4.7",
414
+ title="Gauntlet",
415
+ time_limit_ms=300000,
416
+ start_weapon_id=1,
417
+ unlock_perk_id=PerkId.GREATER_REGENERATION,
418
+ builder_address=0x004369A0,
419
+ )
420
+ def build_4_7_gauntlet(ctx: QuestContext, full_version: bool = True) -> list[SpawnEntry]:
421
+ entries: list[SpawnEntry] = []
422
+ player_count = ctx.player_count + (4 if full_version else 0)
423
+ center_x, center_y = center_point(ctx.width, ctx.height)
424
+ edges = edge_midpoints(ctx.width)
425
+
426
+ ring_count = player_count + 9
427
+ if ring_count > 0:
428
+ trigger = 0
429
+ for x, y, _angle in ring_points(center_x, center_y, 158.0, ring_count):
430
+ entries.append(
431
+ spawn(
432
+ x=x,
433
+ y=y,
434
+ heading=0.0,
435
+ spawn_id=SpawnId.ALIEN_SPAWNER_CHILD_32_SLOW_0A,
436
+ trigger_ms=trigger,
437
+ count=1,
438
+ )
439
+ )
440
+ trigger += 200
441
+
442
+ if ring_count > 0:
443
+ trigger = 4000
444
+ for count in range(2, ring_count + 2):
445
+ entries.append(
446
+ spawn_at(
447
+ edges.right,
448
+ heading=0.0,
449
+ spawn_id=SpawnId.ZOMBIE_RANDOM_41,
450
+ trigger_ms=trigger,
451
+ count=count,
452
+ )
453
+ )
454
+ entries.append(
455
+ spawn_at(
456
+ edges.left,
457
+ heading=0.0,
458
+ spawn_id=SpawnId.ZOMBIE_RANDOM_41,
459
+ trigger_ms=trigger,
460
+ count=count,
461
+ )
462
+ )
463
+ entries.append(
464
+ spawn_at(
465
+ edges.bottom,
466
+ heading=0.0,
467
+ spawn_id=SpawnId.ZOMBIE_RANDOM_41,
468
+ trigger_ms=trigger,
469
+ count=count,
470
+ )
471
+ )
472
+ entries.append(
473
+ spawn_at(
474
+ edges.top,
475
+ heading=0.0,
476
+ spawn_id=SpawnId.ZOMBIE_RANDOM_41,
477
+ trigger_ms=trigger,
478
+ count=count,
479
+ )
480
+ )
481
+ trigger += 5500
482
+
483
+ outer_count = player_count + 0x11
484
+ if outer_count > 0:
485
+ trigger = 42500
486
+ for x, y, _angle in ring_points(center_x, center_y, 258.0, outer_count):
487
+ entries.append(
488
+ spawn(
489
+ x=x,
490
+ y=y,
491
+ heading=0.0,
492
+ spawn_id=SpawnId.ALIEN_SPAWNER_CHILD_32_SLOW_0A,
493
+ trigger_ms=trigger,
494
+ count=1,
495
+ )
496
+ )
497
+ trigger += 500
498
+ return entries
499
+
500
+
501
+ @register_quest(
502
+ level="4.8",
503
+ title="Syntax Terror",
504
+ time_limit_ms=300000,
505
+ start_weapon_id=1,
506
+ unlock_weapon_id=0x16,
507
+ builder_address=0x00436C10,
508
+ )
509
+ def build_4_8_syntax_terror(ctx: QuestContext, full_version: bool = True) -> list[SpawnEntry]:
510
+ entries: list[SpawnEntry] = []
511
+ player_count = ctx.player_count + (4 if full_version else 0)
512
+ outer_seed = 0x14C9
513
+ outer_index = 0
514
+ trigger_base = 1500
515
+ while outer_seed < 0x159D:
516
+ if player_count + 9 > 0:
517
+ trigger = trigger_base
518
+ inner_seed = 0x4C5
519
+ for i in range(player_count + 9):
520
+ x = (((i * i * 0x4C + 0xEC) * i + outer_seed * outer_index) % 0x380) + 0x40
521
+ y = ((inner_seed * i + (outer_index * outer_index * 0x4C + 0x1B) * outer_index) % 0x380) + 0x40
522
+ entries.append(
523
+ spawn(
524
+ x=float(x),
525
+ y=float(y),
526
+ heading=0.0,
527
+ spawn_id=SpawnId.ALIEN_SPAWNER_CHILD_1D_FAST_07,
528
+ trigger_ms=trigger,
529
+ count=1,
530
+ )
531
+ )
532
+ trigger += 300
533
+ inner_seed += 0x15
534
+ trigger_base += 30000
535
+ outer_seed += 0x35
536
+ outer_index += 1
537
+ return entries
538
+
539
+
540
+ @register_quest(
541
+ level="4.9",
542
+ title="The Annihilation",
543
+ time_limit_ms=300000,
544
+ start_weapon_id=1,
545
+ unlock_perk_id=PerkId.BREATHING_ROOM,
546
+ builder_address=0x004382C0,
547
+ )
548
+ def build_4_9_the_annihilation(ctx: QuestContext) -> list[SpawnEntry]:
549
+ entries: list[SpawnEntry] = []
550
+ half_w = ctx.width // 2
551
+ entries.append(
552
+ spawn(
553
+ x=128.0,
554
+ y=float(half_w),
555
+ heading=0.0,
556
+ spawn_id=SpawnId.ALIEN_CONST_RED_FAST_2B,
557
+ trigger_ms=500,
558
+ count=2,
559
+ )
560
+ )
561
+
562
+ trigger = 500
563
+ i_var5 = 0
564
+ for idx in range(12):
565
+ y = float(i_var5 // 12 + 0x80)
566
+ x = 832.0 if idx % 2 == 0 else 896.0
567
+ entries.append(spawn(x=x, y=y, heading=0.0, spawn_id=SpawnId.ALIEN_SPAWNER_CHILD_1D_FAST_07, trigger_ms=trigger, count=1))
568
+ trigger += 500
569
+ i_var5 += 0x300
570
+
571
+ trigger = 45000
572
+ i_var5 = 0
573
+ toggle = False
574
+ for _ in range(12):
575
+ y = float(i_var5 // 12 + 0x80)
576
+ x = 832.0 if toggle else 896.0
577
+ entries.append(spawn(x=x, y=y, heading=0.0, spawn_id=SpawnId.ALIEN_SPAWNER_CHILD_1D_FAST_07, trigger_ms=trigger, count=1))
578
+ trigger += 300
579
+ toggle = not toggle
580
+ i_var5 += 0x300
581
+ return entries
582
+
583
+
584
+ @register_quest(
585
+ level="4.10",
586
+ title="The End of All",
587
+ time_limit_ms=480000,
588
+ start_weapon_id=1,
589
+ unlock_weapon_id=0x17,
590
+ builder_address=0x00438E10,
591
+ )
592
+ def build_4_10_the_end_of_all(ctx: QuestContext, full_version: bool = True) -> list[SpawnEntry]:
593
+ entries: list[SpawnEntry] = [
594
+ spawn(
595
+ x=128.0,
596
+ y=128.0,
597
+ heading=0.0,
598
+ spawn_id=SpawnId.SPIDER_SP1_CONST_RANGED_VARIANT_3C,
599
+ trigger_ms=3000,
600
+ count=1,
601
+ ),
602
+ spawn(
603
+ x=896.0,
604
+ y=128.0,
605
+ heading=0.0,
606
+ spawn_id=SpawnId.SPIDER_SP1_CONST_RANGED_VARIANT_3C,
607
+ trigger_ms=6000,
608
+ count=1,
609
+ ),
610
+ spawn(
611
+ x=128.0,
612
+ y=896.0,
613
+ heading=0.0,
614
+ spawn_id=SpawnId.SPIDER_SP1_CONST_RANGED_VARIANT_3C,
615
+ trigger_ms=9000,
616
+ count=1,
617
+ ),
618
+ spawn(
619
+ x=896.0,
620
+ y=896.0,
621
+ heading=0.0,
622
+ spawn_id=SpawnId.SPIDER_SP1_CONST_RANGED_VARIANT_3C,
623
+ trigger_ms=12000,
624
+ count=1,
625
+ ),
626
+ ]
627
+
628
+ center_x, center_y = center_point(ctx.width, ctx.height)
629
+ edges_wide = edge_midpoints(ctx.width, ctx.height, offset=128.0)
630
+
631
+ trigger = 13000
632
+ for x, y, _angle in ring_points(center_x, center_y, 80.0, 6, step=1.0471976):
633
+ entries.append(spawn(x=x, y=y, heading=0.0, spawn_id=SpawnId.ALIEN_SPAWNER_CHILD_1D_FAST_07, trigger_ms=trigger, count=1))
634
+ trigger += 300
635
+
636
+ entries.append(
637
+ spawn(
638
+ x=512.0,
639
+ y=512.0,
640
+ heading=0.0,
641
+ spawn_id=SpawnId.ALIEN_SPAWNER_CHILD_3C_SLOW_0B,
642
+ trigger_ms=trigger,
643
+ count=1,
644
+ )
645
+ )
646
+
647
+ trigger = 18000
648
+ y = 0x100
649
+ toggle = False
650
+ while y < 0x300:
651
+ x = edges_wide.right[0] if toggle else edges_wide.left[0]
652
+ entries.append(
653
+ spawn(
654
+ x=x,
655
+ y=float(y),
656
+ heading=0.0,
657
+ spawn_id=SpawnId.SPIDER_SP1_CONST_RANGED_VARIANT_3C,
658
+ trigger_ms=trigger,
659
+ count=2,
660
+ )
661
+ )
662
+ trigger += 1000
663
+ toggle = not toggle
664
+ y += 0x80
665
+
666
+ trigger = 43000
667
+ for x, y, _angle in ring_points(center_x, center_y, 80.0, 6, step=1.0471976, start=0.5235988):
668
+ entries.append(spawn(x=x, y=y, heading=0.0, spawn_id=SpawnId.ALIEN_SPAWNER_CHILD_1D_FAST_07, trigger_ms=trigger, count=1))
669
+ trigger += 300
670
+
671
+ if full_version:
672
+ trigger = 62800
673
+ for x, y, _angle in ring_points(center_x, center_y, 180.0, 12, step=0.5235988, start=0.5235988):
674
+ entries.append(
675
+ spawn(
676
+ x=x,
677
+ y=y,
678
+ heading=0.0,
679
+ spawn_id=SpawnId.ALIEN_SPAWNER_CHILD_1D_FAST_07,
680
+ trigger_ms=trigger,
681
+ count=1,
682
+ )
683
+ )
684
+ trigger += 500
685
+
686
+ trigger = 48000
687
+ y = 0x100
688
+ toggle = False
689
+ while y < 0x300:
690
+ x = edges_wide.right[0] if toggle else edges_wide.left[0]
691
+ entries.append(
692
+ spawn(
693
+ x=x,
694
+ y=float(y),
695
+ heading=0.0,
696
+ spawn_id=SpawnId.SPIDER_SP1_CONST_RANGED_VARIANT_3C,
697
+ trigger_ms=trigger,
698
+ count=2,
699
+ )
700
+ )
701
+ trigger += 1000
702
+ toggle = not toggle
703
+ y += 0x80
704
+
705
+ return entries
706
+
707
+
708
+ __all__ = [
709
+ "QuestContext",
710
+ "SpawnEntry",
711
+ "build_4_1_major_alien_breach",
712
+ "build_4_2_zombie_time",
713
+ "build_4_3_lizard_zombie_pact",
714
+ "build_4_4_the_collaboration",
715
+ "build_4_5_the_massacre",
716
+ "build_4_6_the_unblitzkrieg",
717
+ "build_4_7_gauntlet",
718
+ "build_4_8_syntax_terror",
719
+ "build_4_9_the_annihilation",
720
+ "build_4_10_the_end_of_all",
721
+ ]