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
grim/config.py ADDED
@@ -0,0 +1,294 @@
1
+ from __future__ import annotations
2
+
3
+ from dataclasses import dataclass
4
+ from pathlib import Path
5
+
6
+ from construct import Byte, Bytes, Float32l, Int32ul, Struct
7
+
8
+ CRIMSON_CFG_NAME = "crimson.cfg"
9
+ CRIMSON_CFG_SIZE = 0x480
10
+
11
+ CRIMSON_CFG_STRUCT = Struct(
12
+ "sound_disable" / Byte,
13
+ "music_disable" / Byte,
14
+ "highscore_date_mode" / Byte,
15
+ "highscore_duplicate_mode" / Byte,
16
+ "hud_indicators" / Bytes(2),
17
+ "unknown_06" / Bytes(2),
18
+ "unknown_08" / Int32ul,
19
+ "unknown_0c" / Bytes(2),
20
+ "fx_detail_0" / Byte,
21
+ "unknown_0f" / Byte,
22
+ "fx_detail_1" / Byte,
23
+ "fx_detail_2" / Byte,
24
+ "unknown_12" / Bytes(2),
25
+ "player_count" / Int32ul,
26
+ "game_mode" / Int32ul,
27
+ "unknown_1c" / Bytes(0x28),
28
+ "unknown_44" / Int32ul,
29
+ "unknown_48" / Int32ul,
30
+ "unknown_4c" / Bytes(0x20),
31
+ "unknown_6c" / Int32ul,
32
+ "texture_scale" / Float32l,
33
+ "name_tag" / Bytes(12),
34
+ "selected_name_slot" / Int32ul,
35
+ "saved_name_index" / Int32ul,
36
+ "saved_name_order" / Bytes(0x20),
37
+ "saved_names" / Bytes(0xD8),
38
+ "player_name" / Bytes(0x20),
39
+ "player_name_len" / Int32ul,
40
+ "unknown_1a4" / Int32ul,
41
+ "unknown_1a8" / Int32ul,
42
+ "unknown_1ac" / Int32ul,
43
+ "unknown_1b0" / Int32ul,
44
+ "unknown_1b4" / Int32ul,
45
+ "screen_bpp" / Int32ul,
46
+ "screen_width" / Int32ul,
47
+ "screen_height" / Int32ul,
48
+ "windowed_flag" / Byte,
49
+ "unknown_1c5" / Bytes(3),
50
+ "keybinds" / Bytes(0x80),
51
+ "unknown_248" / Bytes(0x1F8),
52
+ "unknown_440" / Int32ul,
53
+ "unknown_444" / Int32ul,
54
+ "hardcore_flag" / Byte,
55
+ # `crimsonland.exe` uses this byte as the "UI Info texts" toggle (it gates the perk prompt text).
56
+ "ui_info_texts" / Byte,
57
+ "unknown_44a" / Bytes(2),
58
+ "perk_prompt_counter" / Int32ul,
59
+ "unknown_450" / Int32ul,
60
+ "unknown_454" / Bytes(0x0C),
61
+ "unknown_460" / Int32ul,
62
+ "sfx_volume" / Float32l,
63
+ "music_volume" / Float32l,
64
+ "fx_toggle" / Byte,
65
+ "score_load_gate" / Byte,
66
+ "unknown_46e" / Byte,
67
+ "unknown_46f" / Byte,
68
+ "detail_preset" / Int32ul,
69
+ "mouse_sensitivity" / Float32l,
70
+ "keybind_pick_perk" / Int32ul,
71
+ "keybind_reload" / Int32ul,
72
+ )
73
+
74
+
75
+ @dataclass(slots=True)
76
+ class CrimsonConfig:
77
+ path: Path
78
+ data: dict
79
+
80
+ @property
81
+ def texture_scale(self) -> float:
82
+ return float(self.data["texture_scale"])
83
+
84
+ @texture_scale.setter
85
+ def texture_scale(self, value: float) -> None:
86
+ self.data["texture_scale"] = float(value)
87
+
88
+ @property
89
+ def screen_bpp(self) -> int:
90
+ return int(self.data["screen_bpp"])
91
+
92
+ @screen_bpp.setter
93
+ def screen_bpp(self, value: int) -> None:
94
+ self.data["screen_bpp"] = int(value)
95
+
96
+ @property
97
+ def screen_width(self) -> int:
98
+ return int(self.data["screen_width"])
99
+
100
+ @screen_width.setter
101
+ def screen_width(self, value: int) -> None:
102
+ self.data["screen_width"] = int(value)
103
+
104
+ @property
105
+ def screen_height(self) -> int:
106
+ return int(self.data["screen_height"])
107
+
108
+ @screen_height.setter
109
+ def screen_height(self, value: int) -> None:
110
+ self.data["screen_height"] = int(value)
111
+
112
+ @property
113
+ def windowed_flag(self) -> int:
114
+ return int(self.data["windowed_flag"])
115
+
116
+ @windowed_flag.setter
117
+ def windowed_flag(self, value: int) -> None:
118
+ self.data["windowed_flag"] = int(value) & 0xFF
119
+
120
+ def save(self) -> None:
121
+ self.path.write_bytes(CRIMSON_CFG_STRUCT.build(self.data))
122
+
123
+
124
+ def default_crimson_cfg_data() -> dict:
125
+ data = CRIMSON_CFG_STRUCT.parse(bytes(CRIMSON_CFG_SIZE))
126
+ config = CrimsonConfig(path=Path("<memory>"), data=data)
127
+ config.data["hud_indicators"] = b"\x01\x01"
128
+ config.data["unknown_08"] = 8
129
+ config.data["fx_detail_0"] = 1
130
+ config.data["fx_detail_1"] = 1
131
+ config.data["fx_detail_2"] = 1
132
+ config.texture_scale = 1.0
133
+ config.screen_bpp = 32
134
+ config.screen_width = 1024
135
+ config.screen_height = 768
136
+ config.windowed_flag = 1
137
+ config.data["player_count"] = 1
138
+ config.data["game_mode"] = 1
139
+ config.data["ui_info_texts"] = 1
140
+ # `config_init_defaults` (0x004028f0): defaults to 0 (enables blood splatter and "Bloody Mess" perk naming).
141
+ config.data["fx_toggle"] = 0
142
+ config.data["sfx_volume"] = 1.0
143
+ config.data["music_volume"] = 1.0
144
+ config.data["detail_preset"] = 5
145
+ config.data["mouse_sensitivity"] = 1.0
146
+ # Matches `config_init_defaults` (0x004028f0): Mouse2 for perk pick, Mouse3 for reload.
147
+ config.data["keybind_pick_perk"] = 0x101
148
+ config.data["keybind_reload"] = 0x102
149
+ config.data["selected_name_slot"] = 0
150
+ config.data["saved_name_index"] = 1
151
+ config.data["unknown_1a4"] = 100
152
+ config.data["unknown_1b0"] = 9000
153
+ config.data["unknown_1b4"] = 27000
154
+
155
+ saved_name_order = bytearray()
156
+ for idx in range(8):
157
+ saved_name_order += idx.to_bytes(4, "little")
158
+ config.data["saved_name_order"] = bytes(saved_name_order)
159
+
160
+ name_entry = b"default" + b"\x00" * (0x1B - len("default"))
161
+ config.data["saved_names"] = name_entry * 8
162
+
163
+ player_name = b"10tons" + b"\x00" * (0x20 - len("10tons"))
164
+ config.data["player_name"] = player_name
165
+ config.data["player_name_len"] = 0
166
+
167
+ keybinds = [
168
+ 0x11,
169
+ 0x1F,
170
+ 0x1E,
171
+ 0x20,
172
+ 0x100,
173
+ 0x17E,
174
+ 0x17E,
175
+ 0x10,
176
+ 0x12,
177
+ 0x13F,
178
+ 0x140,
179
+ 0x141,
180
+ 0x153,
181
+ 0x17E,
182
+ 0x17E,
183
+ 0x17E,
184
+ 200,
185
+ 0xD0,
186
+ 0xCB,
187
+ 0xCD,
188
+ 0x9D,
189
+ 0x17E,
190
+ 0x17E,
191
+ 0xD3,
192
+ 0xD1,
193
+ 0x13F,
194
+ 0x140,
195
+ 0x141,
196
+ 0x153,
197
+ 0x17E,
198
+ 0x17E,
199
+ 0x17E,
200
+ ]
201
+ keybind_blob = b"".join(value.to_bytes(4, "little") for value in keybinds)
202
+ if len(keybind_blob) != 0x80:
203
+ raise ValueError(f"expected 0x80 bytes of keybinds, got {len(keybind_blob)}")
204
+ config.data["keybinds"] = keybind_blob
205
+ return data
206
+
207
+
208
+ def ensure_crimson_cfg(base_dir: Path) -> CrimsonConfig:
209
+ path = base_dir / CRIMSON_CFG_NAME
210
+ if path.exists():
211
+ data = path.read_bytes()
212
+ if len(data) != CRIMSON_CFG_SIZE:
213
+ raise ValueError(f"{path} has unexpected size {len(data)} (expected {CRIMSON_CFG_SIZE})")
214
+ parsed = CRIMSON_CFG_STRUCT.parse(data)
215
+ config = CrimsonConfig(path=path, data=parsed)
216
+ # Patch up configs produced by older revisions of this project.
217
+ # `crimsonland.exe` expects player_count in [1..4], but our repo historically had 0 here.
218
+ player_count = int(config.data.get("player_count", 1))
219
+ if player_count < 1 or player_count > 4:
220
+ config.data["player_count"] = 1
221
+ config.save()
222
+ if (
223
+ int(config.data.get("detail_preset", 0)) == 0
224
+ and int(config.data.get("fx_detail_0", 0)) == 0
225
+ and int(config.data.get("fx_detail_1", 0)) == 0
226
+ and int(config.data.get("fx_detail_2", 0)) == 0
227
+ ):
228
+ config.data["fx_detail_0"] = 1
229
+ config.data["fx_detail_1"] = 1
230
+ config.data["fx_detail_2"] = 1
231
+ config.data["detail_preset"] = 5
232
+ config.save()
233
+ # Patch up missing keybind defaults (older revisions left these as 0).
234
+ keybind_patched = False
235
+ if int(config.data.get("keybind_pick_perk", 0) or 0) == 0:
236
+ config.data["keybind_pick_perk"] = 0x101
237
+ keybind_patched = True
238
+ if int(config.data.get("keybind_reload", 0) or 0) == 0:
239
+ config.data["keybind_reload"] = 0x102
240
+ keybind_patched = True
241
+ if keybind_patched:
242
+ config.save()
243
+ # Patch up missing keybind defaults (older revisions left the entire keybind blob as 0).
244
+ keybind_blob = config.data.get("keybinds")
245
+ if isinstance(keybind_blob, (bytes, bytearray)) and len(keybind_blob) == 0x80:
246
+ default_keybinds = default_crimson_cfg_data().get("keybinds")
247
+ if isinstance(default_keybinds, (bytes, bytearray)) and len(default_keybinds) == 0x80:
248
+ patched = bytearray(keybind_blob)
249
+ changed = False
250
+ for offset in range(0, 0x80, 4):
251
+ value = int.from_bytes(patched[offset : offset + 4], "little")
252
+ if value != 0:
253
+ continue
254
+ patched[offset : offset + 4] = default_keybinds[offset : offset + 4]
255
+ changed = True
256
+ if changed:
257
+ config.data["keybinds"] = bytes(patched)
258
+ config.save()
259
+ return config
260
+ parsed = default_crimson_cfg_data()
261
+ config = CrimsonConfig(path=path, data=parsed)
262
+ config.save()
263
+ return config
264
+
265
+
266
+ def load_crimson_cfg(path: Path) -> CrimsonConfig:
267
+ data = path.read_bytes()
268
+ if len(data) != CRIMSON_CFG_SIZE:
269
+ raise ValueError(f"{path} has unexpected size {len(data)} (expected {CRIMSON_CFG_SIZE})")
270
+ parsed = CRIMSON_CFG_STRUCT.parse(data)
271
+ return CrimsonConfig(path=path, data=parsed)
272
+
273
+
274
+ def apply_detail_preset(config: CrimsonConfig, preset: int | None = None) -> int:
275
+ if preset is None:
276
+ preset = int(config.data.get("detail_preset", 0))
277
+ preset = int(preset)
278
+ if preset < 1:
279
+ preset = 1
280
+ if preset > 5:
281
+ preset = 5
282
+ config.data["detail_preset"] = preset
283
+ if preset <= 1:
284
+ config.data["fx_detail_0"] = 0
285
+ config.data["fx_detail_1"] = 0
286
+ config.data["fx_detail_2"] = 0
287
+ elif preset == 2:
288
+ config.data["fx_detail_0"] = 0
289
+ config.data["fx_detail_1"] = 0
290
+ else:
291
+ config.data["fx_detail_0"] = 1
292
+ config.data["fx_detail_1"] = 1
293
+ config.data["fx_detail_2"] = 1
294
+ return preset