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
crimson/weapons.py ADDED
@@ -0,0 +1,860 @@
1
+ from __future__ import annotations
2
+
3
+ """
4
+ Weapon definitions for the rewrite runtime.
5
+
6
+ This file is **manually maintained** (do not auto-generate).
7
+
8
+ It was originally seeded from `weapon_table_init` (`FUN_004519b0`) and the
9
+ rewrite now uses the **native 1-based weapon ids** (e.g. pistol is
10
+ `weapon_id=1`). In the native code, projectile `type_id` values passed into
11
+ `projectile_spawn` are **weapon table indices** (same numeric domain as weapon
12
+ ids). They are **not** a 1:1 mapping: multiple weapons can share a projectile
13
+ type id (e.g. Sawed-off and Jackhammer use the Shotgun template), and some
14
+ weapons bypass `projectile_spawn` entirely (particles / secondary pool).
15
+
16
+ Use `projectile_type_id_from_weapon_id` for the primary projectile `type_id`
17
+ (or `None` for non-projectile weapons), and `projectile_type_ids_from_weapon_id`
18
+ when you need the full set.
19
+
20
+ Reference material:
21
+ - `docs/weapon-table.md` (native struct + fields)
22
+ - `docs/weapon-id-map.md` (native ids + names)
23
+ """
24
+
25
+ from dataclasses import dataclass
26
+ from enum import IntEnum
27
+
28
+ MANUALLY_MAINTAINED = True
29
+
30
+
31
+ class WeaponId(IntEnum):
32
+ NONE = 0
33
+ PISTOL = 1
34
+ ASSAULT_RIFLE = 2
35
+ SHOTGUN = 3
36
+ SAWED_OFF_SHOTGUN = 4
37
+ SUBMACHINE_GUN = 5
38
+ GAUSS_GUN = 6
39
+ MEAN_MINIGUN = 7
40
+ FLAMETHROWER = 8
41
+ PLASMA_RIFLE = 9
42
+ MULTI_PLASMA = 10
43
+ PLASMA_MINIGUN = 11
44
+ ROCKET_LAUNCHER = 12
45
+ SEEKER_ROCKETS = 13
46
+ PLASMA_SHOTGUN = 14
47
+ BLOW_TORCH = 15
48
+ HR_FLAMER = 16
49
+ MINI_ROCKET_SWARMERS = 17
50
+ ROCKET_MINIGUN = 18
51
+ PULSE_GUN = 19
52
+ JACKHAMMER = 20
53
+ ION_RIFLE = 21
54
+ ION_MINIGUN = 22
55
+ ION_CANNON = 23
56
+ SHRINKIFIER_5K = 24
57
+ BLADE_GUN = 25
58
+ SPIDER_PLASMA = 26
59
+ EVIL_SCYTHE = 27
60
+ PLASMA_CANNON = 28
61
+ SPLITTER_GUN = 29
62
+ GAUSS_SHOTGUN = 30
63
+ ION_SHOTGUN = 31
64
+ FLAMEBURST = 32
65
+ RAYGUN = 33
66
+ UNKNOWN_34 = 34
67
+ UNKNOWN_35 = 35
68
+ UNKNOWN_36 = 36
69
+ UNKNOWN_37 = 37
70
+ UNKNOWN_38 = 38
71
+ UNKNOWN_39 = 39
72
+ UNKNOWN_40 = 40
73
+ PLAGUE_SPHREADER_GUN = 41
74
+ BUBBLEGUN = 42
75
+ RAINBOW_GUN = 43
76
+ GRIM_WEAPON = 44
77
+ FIRE_BULLETS = 45
78
+ UNKNOWN_46 = 46
79
+ UNKNOWN_47 = 47
80
+ UNKNOWN_48 = 48
81
+ UNKNOWN_49 = 49
82
+ TRANSMUTATOR = 50
83
+ BLASTER_R_300 = 51
84
+ LIGHTING_RIFLE = 52
85
+ NUKE_LAUNCHER = 53
86
+
87
+
88
+ @dataclass(frozen=True)
89
+ class Weapon:
90
+ weapon_id: int
91
+ name: str | None
92
+ ammo_class: int | None
93
+ clip_size: int | None
94
+ shot_cooldown: float | None
95
+ reload_time: float | None
96
+ spread_heat_inc: float | None
97
+ fire_sound: str | None
98
+ reload_sound: str | None
99
+ icon_index: int | None
100
+ flags: int | None
101
+ projectile_meta: int | None
102
+ damage_scale: float | None
103
+ pellet_count: int | None
104
+
105
+
106
+ WEAPON_TABLE = [
107
+ Weapon(
108
+ weapon_id=1,
109
+ name='Pistol',
110
+ ammo_class=0,
111
+ clip_size=12,
112
+ shot_cooldown=0.7117000222206116,
113
+ reload_time=1.2000000476837158,
114
+ spread_heat_inc=0.2199999988079071,
115
+ fire_sound='sfx_pistol_fire',
116
+ reload_sound='sfx_pistol_reload',
117
+ icon_index=0,
118
+ flags=5,
119
+ projectile_meta=55,
120
+ damage_scale=4.099999904632568,
121
+ pellet_count=1,
122
+ ),
123
+ Weapon(
124
+ weapon_id=2,
125
+ name='Assault Rifle',
126
+ ammo_class=0,
127
+ clip_size=25,
128
+ shot_cooldown=0.11699999868869781,
129
+ reload_time=1.2000000476837158,
130
+ spread_heat_inc=0.09000000357627869,
131
+ fire_sound='sfx_autorifle_fire',
132
+ reload_sound='sfx_autorifle_reload',
133
+ icon_index=1,
134
+ flags=1,
135
+ projectile_meta=50,
136
+ damage_scale=1.0,
137
+ pellet_count=1,
138
+ ),
139
+ Weapon(
140
+ weapon_id=3,
141
+ name='Shotgun',
142
+ ammo_class=0,
143
+ clip_size=12,
144
+ shot_cooldown=0.8500000238418579,
145
+ reload_time=1.899999976158142,
146
+ spread_heat_inc=0.27000001072883606,
147
+ fire_sound='sfx_shotgun_fire',
148
+ reload_sound='_DAT_004d93bc',
149
+ icon_index=2,
150
+ flags=1,
151
+ projectile_meta=60,
152
+ damage_scale=1.2000000476837158,
153
+ pellet_count=12,
154
+ ),
155
+ Weapon(
156
+ weapon_id=4,
157
+ name='Sawed-off Shotgun',
158
+ ammo_class=0,
159
+ clip_size=12,
160
+ shot_cooldown=0.8700000047683716,
161
+ reload_time=1.899999976158142,
162
+ spread_heat_inc=0.12999999523162842,
163
+ fire_sound='_DAT_004d8434',
164
+ reload_sound='_DAT_004d93bc',
165
+ icon_index=3,
166
+ flags=1,
167
+ projectile_meta=45,
168
+ damage_scale=1.0,
169
+ pellet_count=12,
170
+ ),
171
+ Weapon(
172
+ weapon_id=5,
173
+ name='Submachine Gun',
174
+ ammo_class=0,
175
+ clip_size=30,
176
+ shot_cooldown=0.08811700344085693,
177
+ reload_time=1.2000000476837158,
178
+ spread_heat_inc=0.0820000022649765,
179
+ fire_sound='sfx_hrpm_fire',
180
+ reload_sound='_DAT_004d83c0',
181
+ icon_index=4,
182
+ flags=5,
183
+ projectile_meta=45,
184
+ damage_scale=1.0,
185
+ pellet_count=1,
186
+ ),
187
+ Weapon(
188
+ weapon_id=6,
189
+ name='Gauss Gun',
190
+ ammo_class=0,
191
+ clip_size=6,
192
+ shot_cooldown=0.6000000238418579,
193
+ reload_time=1.600000023841858,
194
+ spread_heat_inc=0.41999998688697815,
195
+ fire_sound='sfx_gauss_fire',
196
+ reload_sound='_DAT_004d93bc',
197
+ icon_index=5,
198
+ flags=1,
199
+ projectile_meta=215,
200
+ damage_scale=1.0,
201
+ pellet_count=1,
202
+ ),
203
+ Weapon(
204
+ weapon_id=7,
205
+ name='Mean Minigun',
206
+ ammo_class=0,
207
+ clip_size=120,
208
+ shot_cooldown=0.09000000357627869,
209
+ reload_time=4.0,
210
+ spread_heat_inc=0.06199999898672104,
211
+ fire_sound='sfx_autorifle_fire',
212
+ reload_sound='_DAT_004d83c0',
213
+ icon_index=6,
214
+ flags=3,
215
+ projectile_meta=45,
216
+ damage_scale=1.0,
217
+ pellet_count=1,
218
+ ),
219
+ Weapon(
220
+ weapon_id=8,
221
+ name='Flamethrower',
222
+ ammo_class=1,
223
+ clip_size=30,
224
+ shot_cooldown=0.008112999610602856,
225
+ reload_time=2.0,
226
+ spread_heat_inc=0.014999999664723873,
227
+ fire_sound='sfx_flamer_fire_01',
228
+ reload_sound='_DAT_004d83c0',
229
+ icon_index=7,
230
+ flags=8,
231
+ projectile_meta=45,
232
+ damage_scale=1.0,
233
+ pellet_count=1,
234
+ ),
235
+ Weapon(
236
+ weapon_id=9,
237
+ name='Plasma Rifle',
238
+ ammo_class=0,
239
+ clip_size=20,
240
+ shot_cooldown=0.290811687707901,
241
+ reload_time=1.2000000476837158,
242
+ spread_heat_inc=0.18199999630451202,
243
+ fire_sound='sfx_shock_fire',
244
+ reload_sound='_DAT_004d83c0',
245
+ icon_index=8,
246
+ flags=None,
247
+ projectile_meta=30,
248
+ damage_scale=5.0,
249
+ pellet_count=1,
250
+ ),
251
+ Weapon(
252
+ weapon_id=10,
253
+ name='Multi-Plasma',
254
+ ammo_class=0,
255
+ clip_size=8,
256
+ shot_cooldown=0.6208117008209229,
257
+ reload_time=1.399999976158142,
258
+ spread_heat_inc=0.3199999928474426,
259
+ fire_sound='sfx_shock_fire',
260
+ reload_sound='_DAT_004d83c0',
261
+ icon_index=9,
262
+ flags=None,
263
+ projectile_meta=45,
264
+ damage_scale=1.0,
265
+ pellet_count=3,
266
+ ),
267
+ Weapon(
268
+ weapon_id=11,
269
+ name='Plasma Minigun',
270
+ ammo_class=0,
271
+ clip_size=30,
272
+ shot_cooldown=0.10999999940395355,
273
+ reload_time=1.2999999523162842,
274
+ spread_heat_inc=0.09700000286102295,
275
+ fire_sound='sfx_plasmaminigun_fire',
276
+ reload_sound='_DAT_004d83c0',
277
+ icon_index=10,
278
+ flags=None,
279
+ projectile_meta=35,
280
+ damage_scale=2.0999999046325684,
281
+ pellet_count=1,
282
+ ),
283
+ Weapon(
284
+ weapon_id=12,
285
+ name='Rocket Launcher',
286
+ ammo_class=2,
287
+ clip_size=5,
288
+ shot_cooldown=0.7408117055892944,
289
+ reload_time=1.2000000476837158,
290
+ spread_heat_inc=0.41999998688697815,
291
+ fire_sound='sfx_rocket_fire',
292
+ reload_sound='sfx_autorifle_reload_alt',
293
+ icon_index=11,
294
+ flags=8,
295
+ projectile_meta=45,
296
+ damage_scale=1.0,
297
+ pellet_count=1,
298
+ ),
299
+ Weapon(
300
+ weapon_id=13,
301
+ name='Seeker Rockets',
302
+ ammo_class=2,
303
+ clip_size=8,
304
+ shot_cooldown=0.31081169843673706,
305
+ reload_time=1.2000000476837158,
306
+ spread_heat_inc=0.3199999928474426,
307
+ fire_sound='sfx_rocket_fire',
308
+ reload_sound='sfx_autorifle_reload_alt',
309
+ icon_index=12,
310
+ flags=8,
311
+ projectile_meta=45,
312
+ damage_scale=1.0,
313
+ pellet_count=1,
314
+ ),
315
+ Weapon(
316
+ weapon_id=14,
317
+ name='Plasma Shotgun',
318
+ ammo_class=0,
319
+ clip_size=8,
320
+ shot_cooldown=0.47999998927116394,
321
+ reload_time=3.0999999046325684,
322
+ spread_heat_inc=0.10999999940395355,
323
+ fire_sound='sfx_plasmashotgun_fire',
324
+ reload_sound='_DAT_004d93bc',
325
+ icon_index=13,
326
+ flags=None,
327
+ projectile_meta=45,
328
+ damage_scale=1.0,
329
+ pellet_count=14,
330
+ ),
331
+ Weapon(
332
+ weapon_id=15,
333
+ name='Blow Torch',
334
+ ammo_class=1,
335
+ clip_size=30,
336
+ shot_cooldown=0.006113000214099884,
337
+ reload_time=1.5,
338
+ spread_heat_inc=0.009999999776482582,
339
+ fire_sound='sfx_flamer_fire_01',
340
+ reload_sound='_DAT_004d83c0',
341
+ icon_index=14,
342
+ flags=8,
343
+ projectile_meta=45,
344
+ damage_scale=1.0,
345
+ pellet_count=1,
346
+ ),
347
+ Weapon(
348
+ weapon_id=16,
349
+ name='HR Flamer',
350
+ ammo_class=1,
351
+ clip_size=30,
352
+ shot_cooldown=0.008500000461935997,
353
+ reload_time=1.7999999523162842,
354
+ spread_heat_inc=0.009999999776482582,
355
+ fire_sound='sfx_flamer_fire_01',
356
+ reload_sound='_DAT_004d83c0',
357
+ icon_index=15,
358
+ flags=8,
359
+ projectile_meta=45,
360
+ damage_scale=1.0,
361
+ pellet_count=1,
362
+ ),
363
+ Weapon(
364
+ weapon_id=17,
365
+ name='Mini-Rocket Swarmers',
366
+ ammo_class=2,
367
+ clip_size=5,
368
+ shot_cooldown=1.7999999523162842,
369
+ reload_time=1.7999999523162842,
370
+ spread_heat_inc=0.11999999731779099,
371
+ fire_sound='sfx_rocket_fire',
372
+ reload_sound='sfx_autorifle_reload_alt',
373
+ icon_index=16,
374
+ flags=8,
375
+ projectile_meta=45,
376
+ damage_scale=1.0,
377
+ pellet_count=1,
378
+ ),
379
+ Weapon(
380
+ weapon_id=18,
381
+ name='Rocket Minigun',
382
+ ammo_class=2,
383
+ clip_size=16,
384
+ shot_cooldown=0.11999999731779099,
385
+ reload_time=1.7999999523162842,
386
+ spread_heat_inc=0.11999999731779099,
387
+ fire_sound='sfx_rocketmini_fire',
388
+ reload_sound='sfx_autorifle_reload_alt',
389
+ icon_index=17,
390
+ flags=8,
391
+ projectile_meta=45,
392
+ damage_scale=1.0,
393
+ pellet_count=1,
394
+ ),
395
+ Weapon(
396
+ weapon_id=19,
397
+ name='Pulse Gun',
398
+ ammo_class=3,
399
+ clip_size=16,
400
+ shot_cooldown=0.10000000149011612,
401
+ reload_time=0.10000000149011612,
402
+ spread_heat_inc=0.0,
403
+ fire_sound='sfx_pulse_fire',
404
+ reload_sound='sfx_autorifle_reload',
405
+ icon_index=18,
406
+ flags=8,
407
+ projectile_meta=20,
408
+ damage_scale=1.0,
409
+ pellet_count=1,
410
+ ),
411
+ Weapon(
412
+ weapon_id=20,
413
+ name='Jackhammer',
414
+ ammo_class=0,
415
+ clip_size=16,
416
+ shot_cooldown=0.14000000059604645,
417
+ reload_time=3.0,
418
+ spread_heat_inc=0.1599999964237213,
419
+ fire_sound='sfx_shotgun_fire',
420
+ reload_sound='_DAT_004d93bc',
421
+ icon_index=19,
422
+ flags=1,
423
+ projectile_meta=45,
424
+ damage_scale=1.0,
425
+ pellet_count=4,
426
+ ),
427
+ Weapon(
428
+ weapon_id=21,
429
+ name='Ion Rifle',
430
+ ammo_class=4,
431
+ clip_size=8,
432
+ shot_cooldown=0.4000000059604645,
433
+ reload_time=1.350000023841858,
434
+ spread_heat_inc=0.1120000034570694,
435
+ fire_sound='sfx_shock_fire_alt',
436
+ reload_sound='_DAT_004d86a8',
437
+ icon_index=20,
438
+ flags=8,
439
+ projectile_meta=15,
440
+ damage_scale=3.0,
441
+ pellet_count=1,
442
+ ),
443
+ Weapon(
444
+ weapon_id=22,
445
+ name='Ion Minigun',
446
+ ammo_class=4,
447
+ clip_size=20,
448
+ shot_cooldown=0.10000000149011612,
449
+ reload_time=1.7999999523162842,
450
+ spread_heat_inc=0.09000000357627869,
451
+ fire_sound='sfx_shockminigun_fire',
452
+ reload_sound='_DAT_004d86a8',
453
+ icon_index=21,
454
+ flags=8,
455
+ projectile_meta=20,
456
+ damage_scale=1.399999976158142,
457
+ pellet_count=1,
458
+ ),
459
+ Weapon(
460
+ weapon_id=23,
461
+ name='Ion Cannon',
462
+ ammo_class=4,
463
+ clip_size=3,
464
+ shot_cooldown=1.0,
465
+ reload_time=3.0,
466
+ spread_heat_inc=0.6800000071525574,
467
+ fire_sound='sfx_shock_fire_alt',
468
+ reload_sound='_DAT_004d86a8',
469
+ icon_index=22,
470
+ flags=None,
471
+ projectile_meta=10,
472
+ damage_scale=16.700000762939453,
473
+ pellet_count=1,
474
+ ),
475
+ Weapon(
476
+ weapon_id=24,
477
+ name='Shrinkifier 5k',
478
+ ammo_class=0,
479
+ clip_size=8,
480
+ shot_cooldown=0.20999999344348907,
481
+ reload_time=1.2200000286102295,
482
+ spread_heat_inc=0.03999999910593033,
483
+ fire_sound='sfx_shock_fire_alt',
484
+ reload_sound='_DAT_004d86a8',
485
+ icon_index=23,
486
+ flags=8,
487
+ projectile_meta=45,
488
+ damage_scale=0.0,
489
+ pellet_count=1,
490
+ ),
491
+ Weapon(
492
+ weapon_id=25,
493
+ name='Blade Gun',
494
+ ammo_class=0,
495
+ clip_size=6,
496
+ shot_cooldown=0.3499999940395355,
497
+ reload_time=3.5,
498
+ spread_heat_inc=0.03999999910593033,
499
+ fire_sound='sfx_shock_fire_alt',
500
+ reload_sound='sfx_shock_reload',
501
+ icon_index=24,
502
+ flags=8,
503
+ projectile_meta=20,
504
+ damage_scale=11.0,
505
+ pellet_count=1,
506
+ ),
507
+ Weapon(
508
+ weapon_id=26,
509
+ name='Spider Plasma',
510
+ ammo_class=0,
511
+ clip_size=5,
512
+ shot_cooldown=0.20000000298023224,
513
+ reload_time=1.2000000476837158,
514
+ spread_heat_inc=0.03999999910593033,
515
+ fire_sound='_DAT_004d92bc',
516
+ reload_sound='_DAT_004d93bc',
517
+ icon_index=25,
518
+ flags=8,
519
+ projectile_meta=10,
520
+ damage_scale=0.5,
521
+ pellet_count=1,
522
+ ),
523
+ Weapon(
524
+ weapon_id=27,
525
+ name='Evil Scythe',
526
+ ammo_class=4,
527
+ clip_size=3,
528
+ shot_cooldown=1.0,
529
+ reload_time=3.0,
530
+ spread_heat_inc=0.6800000071525574,
531
+ fire_sound='sfx_shock_fire_alt',
532
+ reload_sound='_DAT_004d86a8',
533
+ icon_index=25,
534
+ flags=None,
535
+ projectile_meta=45,
536
+ damage_scale=1.0,
537
+ pellet_count=1,
538
+ ),
539
+ Weapon(
540
+ weapon_id=28,
541
+ name='Plasma Cannon',
542
+ ammo_class=0,
543
+ clip_size=3,
544
+ shot_cooldown=0.8999999761581421,
545
+ reload_time=2.700000047683716,
546
+ spread_heat_inc=0.6000000238418579,
547
+ fire_sound='sfx_shock_fire',
548
+ reload_sound='_DAT_004d86a8',
549
+ icon_index=25,
550
+ flags=None,
551
+ projectile_meta=10,
552
+ damage_scale=28.0,
553
+ pellet_count=1,
554
+ ),
555
+ Weapon(
556
+ weapon_id=29,
557
+ name='Splitter Gun',
558
+ ammo_class=0,
559
+ clip_size=6,
560
+ shot_cooldown=0.699999988079071,
561
+ reload_time=2.200000047683716,
562
+ spread_heat_inc=0.2800000011920929,
563
+ fire_sound='sfx_shock_fire_alt',
564
+ reload_sound='_DAT_004d86a8',
565
+ icon_index=28,
566
+ flags=None,
567
+ projectile_meta=30,
568
+ damage_scale=6.0,
569
+ pellet_count=1,
570
+ ),
571
+ Weapon(
572
+ weapon_id=30,
573
+ name='Gauss Shotgun',
574
+ ammo_class=0,
575
+ clip_size=4,
576
+ shot_cooldown=1.0499999523162842,
577
+ reload_time=2.0999999046325684,
578
+ spread_heat_inc=0.27000001072883606,
579
+ fire_sound='sfx_gauss_fire',
580
+ reload_sound='_DAT_004d93bc',
581
+ icon_index=30,
582
+ flags=1,
583
+ projectile_meta=45,
584
+ damage_scale=1.0,
585
+ pellet_count=1,
586
+ ),
587
+ Weapon(
588
+ weapon_id=31,
589
+ name='Ion Shotgun',
590
+ ammo_class=4,
591
+ clip_size=10,
592
+ shot_cooldown=0.8500000238418579,
593
+ reload_time=1.899999976158142,
594
+ spread_heat_inc=0.27000001072883606,
595
+ fire_sound='sfx_shock_fire_alt',
596
+ reload_sound='_DAT_004d86a8',
597
+ icon_index=31,
598
+ flags=1,
599
+ projectile_meta=45,
600
+ damage_scale=1.0,
601
+ pellet_count=8,
602
+ ),
603
+ Weapon(
604
+ weapon_id=32,
605
+ name='Flameburst',
606
+ ammo_class=4,
607
+ clip_size=60,
608
+ shot_cooldown=0.019999999552965164,
609
+ reload_time=3.0,
610
+ spread_heat_inc=0.18000000715255737,
611
+ fire_sound='sfx_flamer_fire_01',
612
+ reload_sound='_DAT_004d86a8',
613
+ icon_index=29,
614
+ flags=None,
615
+ projectile_meta=45,
616
+ damage_scale=1.0,
617
+ pellet_count=1,
618
+ ),
619
+ Weapon(
620
+ weapon_id=33,
621
+ name='RayGun',
622
+ ammo_class=4,
623
+ clip_size=12,
624
+ shot_cooldown=0.699999988079071,
625
+ reload_time=2.0,
626
+ spread_heat_inc=0.3799999952316284,
627
+ fire_sound='sfx_shock_fire_alt',
628
+ reload_sound='_DAT_004d86a8',
629
+ icon_index=30,
630
+ flags=None,
631
+ projectile_meta=45,
632
+ damage_scale=1.0,
633
+ pellet_count=1,
634
+ ),
635
+ Weapon(
636
+ weapon_id=41,
637
+ name='Plague Sphreader Gun',
638
+ ammo_class=None,
639
+ clip_size=5,
640
+ shot_cooldown=0.20000000298023224,
641
+ reload_time=1.2000000476837158,
642
+ spread_heat_inc=0.03999999910593033,
643
+ fire_sound='sfx_bloodspill_01',
644
+ reload_sound='_DAT_004d93bc',
645
+ icon_index=40,
646
+ flags=8,
647
+ projectile_meta=15,
648
+ damage_scale=0.0,
649
+ pellet_count=1,
650
+ ),
651
+ Weapon(
652
+ weapon_id=42,
653
+ name='Bubblegun',
654
+ ammo_class=None,
655
+ clip_size=15,
656
+ shot_cooldown=0.16130000352859497,
657
+ reload_time=1.2000000476837158,
658
+ spread_heat_inc=0.05000000074505806,
659
+ fire_sound='_DAT_004d92bc',
660
+ reload_sound='_DAT_004d93bc',
661
+ icon_index=41,
662
+ flags=8,
663
+ projectile_meta=45,
664
+ damage_scale=1.0,
665
+ pellet_count=1,
666
+ ),
667
+ Weapon(
668
+ weapon_id=43,
669
+ name='Rainbow Gun',
670
+ ammo_class=None,
671
+ clip_size=10,
672
+ shot_cooldown=0.20000000298023224,
673
+ reload_time=1.2000000476837158,
674
+ spread_heat_inc=0.09000000357627869,
675
+ fire_sound='_DAT_004d92bc',
676
+ reload_sound='_DAT_004d93bc',
677
+ icon_index=42,
678
+ flags=8,
679
+ projectile_meta=10,
680
+ damage_scale=1.0,
681
+ pellet_count=1,
682
+ ),
683
+ Weapon(
684
+ weapon_id=44,
685
+ name='Grim Weapon',
686
+ ammo_class=None,
687
+ clip_size=3,
688
+ shot_cooldown=0.5,
689
+ reload_time=1.2000000476837158,
690
+ spread_heat_inc=0.4000000059604645,
691
+ fire_sound='_DAT_004d92bc',
692
+ reload_sound='_DAT_004d93bc',
693
+ icon_index=43,
694
+ flags=None,
695
+ projectile_meta=45,
696
+ damage_scale=1.0,
697
+ pellet_count=1,
698
+ ),
699
+ Weapon(
700
+ weapon_id=45,
701
+ name='Fire bullets',
702
+ ammo_class=None,
703
+ clip_size=112,
704
+ shot_cooldown=0.14000000059604645,
705
+ reload_time=1.2000000476837158,
706
+ spread_heat_inc=0.2199999988079071,
707
+ fire_sound='_DAT_004d7b7c',
708
+ reload_sound='sfx_pistol_reload',
709
+ icon_index=44,
710
+ flags=1,
711
+ projectile_meta=60,
712
+ damage_scale=0.25,
713
+ pellet_count=1,
714
+ ),
715
+ Weapon(
716
+ weapon_id=50,
717
+ name='Transmutator',
718
+ ammo_class=None,
719
+ clip_size=50,
720
+ shot_cooldown=0.03999999910593033,
721
+ reload_time=5.0,
722
+ spread_heat_inc=0.03999999910593033,
723
+ fire_sound='sfx_bloodspill_01',
724
+ reload_sound='_DAT_004d93bc',
725
+ icon_index=49,
726
+ flags=9,
727
+ projectile_meta=45,
728
+ damage_scale=1.0,
729
+ pellet_count=1,
730
+ ),
731
+ Weapon(
732
+ weapon_id=51,
733
+ name='Blaster R-300',
734
+ ammo_class=None,
735
+ clip_size=20,
736
+ shot_cooldown=0.07999999821186066,
737
+ reload_time=2.0,
738
+ spread_heat_inc=0.05000000074505806,
739
+ fire_sound='sfx_shock_fire',
740
+ reload_sound='_DAT_004d93bc',
741
+ icon_index=50,
742
+ flags=9,
743
+ projectile_meta=45,
744
+ damage_scale=1.0,
745
+ pellet_count=1,
746
+ ),
747
+ Weapon(
748
+ weapon_id=52,
749
+ name='Lighting Rifle',
750
+ ammo_class=None,
751
+ clip_size=500,
752
+ shot_cooldown=4.0,
753
+ reload_time=8.0,
754
+ spread_heat_inc=1.0,
755
+ fire_sound='sfx_explosion_large',
756
+ reload_sound='sfx_shotgun_reload',
757
+ icon_index=51,
758
+ flags=8,
759
+ projectile_meta=45,
760
+ damage_scale=1.0,
761
+ pellet_count=1,
762
+ ),
763
+ Weapon(
764
+ weapon_id=53,
765
+ name='Nuke Launcher',
766
+ ammo_class=None,
767
+ clip_size=1,
768
+ shot_cooldown=4.0,
769
+ reload_time=8.0,
770
+ spread_heat_inc=1.0,
771
+ fire_sound='_DAT_004d93b4',
772
+ reload_sound='_DAT_004d93bc',
773
+ icon_index=52,
774
+ flags=8,
775
+ projectile_meta=45,
776
+ damage_scale=1.0,
777
+ pellet_count=1,
778
+ ),
779
+ ]
780
+
781
+ WEAPON_BY_ID = {
782
+ entry.weapon_id: entry for entry in WEAPON_TABLE
783
+ }
784
+
785
+ WEAPON_PROJECTILE_TYPE_IDS: dict[int, tuple[int, ...]] = {
786
+ # Source: analysis/ghidra/raw/crimsonland.exe_decompiled.c (`player_fire_weapon`).
787
+ # Weapon ids not listed here use `type_id == weapon_id` in the native
788
+ # `projectile_spawn` path.
789
+ 1: (0x01,), # Pistol
790
+ 2: (0x02,), # Assault Rifle
791
+ 3: (0x03,), # Shotgun
792
+ 4: (0x03,), # Sawed-off Shotgun
793
+ 5: (0x05,), # Submachine Gun
794
+ 6: (0x06,), # Gauss Gun
795
+ 7: (0x01,), # Mean Minigun
796
+ 8: (), # Flamethrower (particle path)
797
+ 9: (0x09,), # Plasma Rifle
798
+ 10: (0x09, 0x0B), # Multi-Plasma (spread includes 0x0B)
799
+ 11: (0x0B,), # Plasma Minigun
800
+ 12: (), # Rocket Launcher (secondary projectile pool)
801
+ 13: (), # Seeker Rockets (secondary projectile pool)
802
+ 14: (0x0B,), # Plasma Shotgun
803
+ 15: (), # Blow Torch (particle path)
804
+ 16: (), # HR Flamer (particle path)
805
+ 17: (), # Mini-Rocket Swarmers (secondary projectile pool)
806
+ 18: (), # Rocket Minigun (secondary projectile pool)
807
+ 19: (0x13,), # Pulse Gun
808
+ 20: (0x03,), # Jackhammer
809
+ 21: (0x15,), # Ion Rifle
810
+ 22: (0x16,), # Ion Minigun
811
+ 23: (0x17,), # Ion Cannon
812
+ 24: (0x18,), # Shrinkifier 5k
813
+ 25: (0x19,), # Blade Gun
814
+ 28: (0x1C,), # Plasma Cannon
815
+ 29: (0x1D,), # Splitter Gun
816
+ 30: (0x06,), # Gauss Shotgun
817
+ 31: (0x16,), # Ion Shotgun
818
+ 41: (0x29,), # Plague Spreader Gun
819
+ 42: (), # Bubblegun (particle slow)
820
+ 43: (0x2B,), # Rainbow Gun
821
+ 45: (0x2D,), # Fire Bullets
822
+ }
823
+
824
+ def weapon_entry_for_projectile_type_id(type_id: int) -> Weapon | None:
825
+ # Native `projectile_spawn` indexes the weapon table by `type_id`.
826
+ return WEAPON_BY_ID.get(int(type_id))
827
+
828
+
829
+ def projectile_type_id_from_weapon_id(weapon_id: int) -> int | None:
830
+ """Return the primary projectile `type_id` used by `weapon_id`.
831
+
832
+ Returns `None` for weapons that don't use the main projectile pool.
833
+ """
834
+
835
+ weapon_id = int(weapon_id)
836
+ type_ids = WEAPON_PROJECTILE_TYPE_IDS.get(weapon_id)
837
+ if type_ids is not None:
838
+ return int(type_ids[0]) if type_ids else None
839
+
840
+ # Default native behavior for projectile weapons is `type_id == weapon_id`.
841
+ if weapon_id in WEAPON_BY_ID:
842
+ return weapon_id
843
+ return None
844
+
845
+
846
+ def projectile_type_ids_from_weapon_id(weapon_id: int) -> tuple[int, ...]:
847
+ """Return all projectile `type_id` values produced by `weapon_id`."""
848
+
849
+ weapon_id = int(weapon_id)
850
+ type_ids = WEAPON_PROJECTILE_TYPE_IDS.get(weapon_id)
851
+ if type_ids is not None:
852
+ return tuple(int(v) for v in type_ids)
853
+ if weapon_id in WEAPON_BY_ID:
854
+ return (weapon_id,)
855
+ return ()
856
+
857
+
858
+ WEAPON_BY_NAME = {
859
+ entry.name: entry for entry in WEAPON_TABLE if entry.name is not None
860
+ }