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,108 @@
1
+ from __future__ import annotations
2
+
3
+ from dataclasses import dataclass
4
+
5
+ import pyray as rl
6
+
7
+ from grim.fonts.grim_mono import GrimMonoFont, draw_grim_mono_text
8
+
9
+ QUEST_TITLE_ALPHA = 1.0
10
+ QUEST_NUMBER_ALPHA_RATIO = 0.5
11
+
12
+ # Game base scale: 0.75 at 640px width, 0.8 at larger widths.
13
+ QUEST_TITLE_SCALE_SMALL = 0.75
14
+ QUEST_TITLE_SCALE_LARGE = 0.8
15
+ QUEST_TITLE_SCALE_THRESHOLD_PX = 640
16
+
17
+ # Title overlay baseline is centered vertically and shifted up by 32px (0x20).
18
+ QUEST_TITLE_Y_OFFSET = 32.0
19
+
20
+ # Number is drawn at a slightly smaller scale.
21
+ QUEST_NUMBER_SCALE_DELTA = 0.2
22
+
23
+ # Game X formula: x = title_x - (strlen * scale * 8.0) - (scale * 32.0) - 4.0
24
+ # where 8.0 = advance/2, 32.0 = base gap, 4.0 = fixed offset.
25
+ QUEST_NUMBER_HALF_ADVANCE = 8.0
26
+ QUEST_NUMBER_BASE_GAP = 32.0
27
+ QUEST_NUMBER_FIXED_OFFSET = 4.0
28
+
29
+ # Game Y formula: y = title_y + number_scale * (23.36 - 16.0) = title_y + number_scale * 7.36
30
+ QUEST_NUMBER_Y_MULTIPLIER = 7.36
31
+
32
+
33
+ @dataclass(frozen=True, slots=True)
34
+ class QuestTitleOverlayLayout:
35
+ title_x: float
36
+ title_y: float
37
+ title_scale: float
38
+ number_x: float
39
+ number_y: float
40
+ number_scale: float
41
+
42
+
43
+ def quest_title_base_scale(screen_width: int) -> float:
44
+ return QUEST_TITLE_SCALE_SMALL if screen_width <= QUEST_TITLE_SCALE_THRESHOLD_PX else QUEST_TITLE_SCALE_LARGE
45
+
46
+
47
+ def quest_number_scale(title_scale: float) -> float:
48
+ return max(0.0, title_scale - QUEST_NUMBER_SCALE_DELTA)
49
+
50
+
51
+ def layout_quest_title_overlay(
52
+ *,
53
+ screen_width: float,
54
+ screen_height: float,
55
+ title: str,
56
+ number: str,
57
+ font_advance: float,
58
+ ) -> QuestTitleOverlayLayout:
59
+ title_scale = quest_title_base_scale(int(screen_width))
60
+ number_scale = quest_number_scale(title_scale)
61
+
62
+ title_width = len(title) * font_advance * title_scale
63
+ # The game uses integer division for screen center (width/2, height/2) before converting to float.
64
+ center_x = float(int(screen_width) // 2)
65
+ center_y = float(int(screen_height) // 2)
66
+ title_x = center_x - (title_width / 2.0)
67
+ title_y = center_y - QUEST_TITLE_Y_OFFSET
68
+
69
+ number_x = (
70
+ title_x
71
+ - (len(number) * number_scale * QUEST_NUMBER_HALF_ADVANCE)
72
+ - (number_scale * QUEST_NUMBER_BASE_GAP)
73
+ - QUEST_NUMBER_FIXED_OFFSET
74
+ )
75
+ number_y = title_y + (number_scale * QUEST_NUMBER_Y_MULTIPLIER)
76
+
77
+ return QuestTitleOverlayLayout(
78
+ title_x=title_x,
79
+ title_y=title_y,
80
+ title_scale=title_scale,
81
+ number_x=number_x,
82
+ number_y=number_y,
83
+ number_scale=number_scale,
84
+ )
85
+
86
+
87
+ def draw_quest_title_overlay(font: GrimMonoFont, title: str, number: str, *, alpha: float = 1.0) -> None:
88
+ alpha = max(0.0, min(1.0, float(alpha)))
89
+ layout = layout_quest_title_overlay(
90
+ screen_width=rl.get_screen_width(),
91
+ screen_height=rl.get_screen_height(),
92
+ title=title,
93
+ number=number,
94
+ font_advance=font.advance,
95
+ )
96
+
97
+ title_color = rl.Color(255, 255, 255, int(255 * QUEST_TITLE_ALPHA * alpha))
98
+ number_color = rl.Color(255, 255, 255, int(255 * QUEST_TITLE_ALPHA * QUEST_NUMBER_ALPHA_RATIO * alpha))
99
+
100
+ draw_grim_mono_text(font, title, layout.title_x, layout.title_y, layout.title_scale, title_color)
101
+ draw_grim_mono_text(
102
+ font,
103
+ number,
104
+ layout.number_x,
105
+ layout.number_y,
106
+ layout.number_scale,
107
+ number_color,
108
+ )
@@ -0,0 +1,34 @@
1
+ from __future__ import annotations
2
+
3
+ from dataclasses import dataclass
4
+ from typing import Callable
5
+
6
+ from grim.view import View
7
+
8
+
9
+ @dataclass(frozen=True, slots=True)
10
+ class ViewDefinition:
11
+ name: str
12
+ title: str
13
+ factory: Callable[..., View]
14
+
15
+
16
+ _VIEW_REGISTRY: dict[str, ViewDefinition] = {}
17
+
18
+
19
+ def register_view(name: str, title: str) -> Callable[[Callable[..., View]], Callable[..., View]]:
20
+ def decorator(factory: Callable[..., View]) -> Callable[..., View]:
21
+ if name in _VIEW_REGISTRY:
22
+ raise ValueError(f"view already registered: {name}")
23
+ _VIEW_REGISTRY[name] = ViewDefinition(name=name, title=title, factory=factory)
24
+ return factory
25
+
26
+ return decorator
27
+
28
+
29
+ def all_views() -> list[ViewDefinition]:
30
+ return [_VIEW_REGISTRY[name] for name in sorted(_VIEW_REGISTRY.keys(), key=str.casefold)]
31
+
32
+
33
+ def view_by_name(name: str) -> ViewDefinition | None:
34
+ return _VIEW_REGISTRY.get(name)
crimson/views/rush.py ADDED
@@ -0,0 +1,16 @@
1
+ from __future__ import annotations
2
+
3
+ from grim.view import ViewContext
4
+
5
+ from ..modes.rush_mode import RushMode
6
+ from .registry import register_view
7
+
8
+
9
+ class RushView(RushMode):
10
+ pass
11
+
12
+
13
+ @register_view("rush", "Rush")
14
+ def _create_rush_view(*, ctx: ViewContext) -> RushView:
15
+ return RushView(ctx)
16
+
@@ -0,0 +1,204 @@
1
+ from __future__ import annotations
2
+
3
+ from pathlib import Path
4
+
5
+ import pyray as rl
6
+
7
+ from grim.fonts.small import SmallFontData, load_small_font
8
+ from grim.view import View, ViewContext
9
+
10
+ from ..ui.perk_menu import MENU_ITEM_ALPHA_IDLE, MENU_ITEM_RGB, draw_menu_item
11
+ from .registry import register_view
12
+
13
+ UI_TEXT_COLOR = rl.Color(220, 220, 220, 255)
14
+ UI_ERROR_COLOR = rl.Color(240, 80, 80, 255)
15
+
16
+ SAMPLE_LINES = [
17
+ "Regeneration",
18
+ "My Favourite Weapon",
19
+ "Ammo Maniac",
20
+ "Pyromaniac",
21
+ "Evil Eyes",
22
+ ]
23
+
24
+ ARIAL_FONT_LOAD_SIZE = 26
25
+ ARIAL_FONT_DRAW_SIZE = 13
26
+ PIXEL_ARIAL_LOAD_SIZE = 22
27
+ PIXEL_ARIAL_DRAW_SIZE = 11
28
+ VECTOR_FONT_SPACING = 1.0
29
+ VECTOR_FONT_FILTER = rl.TEXTURE_FILTER_BILINEAR
30
+
31
+
32
+ class SmallFontDebugView:
33
+ def __init__(self, ctx: ViewContext) -> None:
34
+ self._assets_root = ctx.assets_dir
35
+ self._missing_assets: list[str] = []
36
+ self._small: SmallFontData | None = None
37
+ self._screenshot_requested = False
38
+ self._vector_font: rl.Font | None = None
39
+ self._vector_font_path: Path | None = None
40
+ self._vector_font_alt: rl.Font | None = None
41
+ self._vector_font_alt_path: Path | None = None
42
+
43
+ def open(self) -> None:
44
+ self._missing_assets.clear()
45
+ self._small = load_small_font(self._assets_root, self._missing_assets)
46
+ self._vector_font = None
47
+ self._vector_font_path = None
48
+ self._vector_font_alt = None
49
+ self._vector_font_alt_path = None
50
+ for candidate in (
51
+ self._assets_root / "crimson" / "load" / "arial.ttf",
52
+ self._assets_root / "fonts" / "arial.ttf",
53
+ self._assets_root / "arial.ttf",
54
+ ):
55
+ if not candidate.is_file():
56
+ continue
57
+ try:
58
+ self._vector_font = rl.load_font_ex(str(candidate), ARIAL_FONT_LOAD_SIZE, None, 0)
59
+ rl.set_texture_filter(self._vector_font.texture, VECTOR_FONT_FILTER)
60
+ self._vector_font_path = candidate
61
+ break
62
+ except Exception:
63
+ self._vector_font = None
64
+ self._vector_font_path = None
65
+ for candidate in (
66
+ self._assets_root / "crimson" / "load" / "PIXEARG_.TTF",
67
+ self._assets_root / "fonts" / "PIXEARG_.TTF",
68
+ self._assets_root / "PIXEARG_.TTF",
69
+ ):
70
+ if not candidate.is_file():
71
+ continue
72
+ try:
73
+ self._vector_font_alt = rl.load_font_ex(str(candidate), PIXEL_ARIAL_LOAD_SIZE, None, 0)
74
+ rl.set_texture_filter(self._vector_font_alt.texture, VECTOR_FONT_FILTER)
75
+ self._vector_font_alt_path = candidate
76
+ break
77
+ except Exception:
78
+ self._vector_font_alt = None
79
+ self._vector_font_alt_path = None
80
+
81
+ def close(self) -> None:
82
+ if self._small is not None:
83
+ rl.unload_texture(self._small.texture)
84
+ self._small = None
85
+ if self._vector_font is not None:
86
+ rl.unload_font(self._vector_font)
87
+ self._vector_font = None
88
+ self._vector_font_path = None
89
+ if self._vector_font_alt is not None:
90
+ rl.unload_font(self._vector_font_alt)
91
+ self._vector_font_alt = None
92
+ self._vector_font_alt_path = None
93
+
94
+ def update(self, dt: float) -> None:
95
+ del dt
96
+ if rl.is_key_pressed(rl.KeyboardKey.KEY_P):
97
+ self._screenshot_requested = True
98
+
99
+ def consume_screenshot_request(self) -> bool:
100
+ requested = self._screenshot_requested
101
+ self._screenshot_requested = False
102
+ return requested
103
+
104
+ def draw(self) -> None:
105
+ rl.clear_background(rl.Color(12, 12, 14, 255))
106
+ if self._missing_assets:
107
+ rl.draw_text(
108
+ "Missing assets: " + ", ".join(self._missing_assets),
109
+ 24,
110
+ 24,
111
+ 20,
112
+ UI_ERROR_COLOR,
113
+ )
114
+ return
115
+ if self._small is None:
116
+ return
117
+
118
+ margin = 24
119
+ gap = 40
120
+ header_y = 20
121
+ row_step = 19.0
122
+ vector_step = row_step
123
+
124
+ rl.draw_text("smallWhite atlas", margin, header_y, 20, UI_TEXT_COLOR)
125
+ atlas_y = header_y + 28
126
+ rl.draw_texture(self._small.texture, margin, atlas_y, rl.WHITE)
127
+ atlas_bottom = atlas_y + self._small.texture.height
128
+
129
+ right_x = margin + self._small.texture.width + gap
130
+ rl.draw_text("perk menu render", right_x, header_y, 20, UI_TEXT_COLOR)
131
+ text_y = float(atlas_y)
132
+ for line in SAMPLE_LINES:
133
+ draw_menu_item(self._small, line, x=float(right_x), y=text_y, scale=1.0, hovered=False)
134
+ text_y += row_step
135
+ perk_list_bottom = text_y
136
+ section_y = perk_list_bottom + 16.0
137
+
138
+ vector_x = right_x
139
+ rl.draw_text("Pixel Arial (PIXEARG_.TTF)", vector_x, int(section_y), 20, UI_TEXT_COLOR)
140
+ if self._vector_font_alt is None:
141
+ hint_y = section_y + 28.0
142
+ rl.draw_text("Place PIXEARG_.TTF in:", vector_x, int(hint_y), 16, UI_TEXT_COLOR)
143
+ rl.draw_text(str(self._assets_root / "crimson" / "load"), vector_x, int(hint_y + 18), 16, UI_TEXT_COLOR)
144
+ rl.draw_text(str(self._assets_root / "fonts"), vector_x, int(hint_y + 36), 16, UI_TEXT_COLOR)
145
+ vector_list_end = hint_y + 54.0
146
+ else:
147
+ vector_y = section_y + 28.0
148
+ for line in SAMPLE_LINES:
149
+ self._draw_vector_menu_item(
150
+ self._vector_font_alt,
151
+ line,
152
+ x=float(vector_x),
153
+ y=float(vector_y),
154
+ font_size_px=PIXEL_ARIAL_DRAW_SIZE,
155
+ spacing=VECTOR_FONT_SPACING,
156
+ )
157
+ vector_y += vector_step
158
+ vector_list_end = vector_y
159
+ alt_x = vector_x
160
+ alt_header_y = vector_list_end + 24.0
161
+ rl.draw_text("Arial (ttf)", alt_x, int(alt_header_y), 20, UI_TEXT_COLOR)
162
+ if self._vector_font is None:
163
+ hint_y = alt_header_y + 28.0
164
+ rl.draw_text("Place arial.ttf in:", alt_x, int(hint_y), 16, UI_TEXT_COLOR)
165
+ rl.draw_text(str(self._assets_root / "crimson" / "load"), alt_x, int(hint_y + 18), 16, UI_TEXT_COLOR)
166
+ rl.draw_text(str(self._assets_root / "fonts"), alt_x, int(hint_y + 36), 16, UI_TEXT_COLOR)
167
+ else:
168
+ alt_y = alt_header_y + 28.0
169
+ for line in SAMPLE_LINES:
170
+ self._draw_vector_menu_item(
171
+ self._vector_font,
172
+ line,
173
+ x=float(alt_x),
174
+ y=float(alt_y),
175
+ font_size_px=ARIAL_FONT_DRAW_SIZE,
176
+ spacing=VECTOR_FONT_SPACING,
177
+ )
178
+ alt_y += vector_step
179
+
180
+ def _draw_vector_menu_item(
181
+ self,
182
+ font: rl.Font,
183
+ text: str,
184
+ *,
185
+ x: float,
186
+ y: float,
187
+ font_size_px: float,
188
+ spacing: float,
189
+ ) -> None:
190
+ r, g, b = MENU_ITEM_RGB
191
+ color = rl.Color(int(r), int(g), int(b), int(255 * MENU_ITEM_ALPHA_IDLE))
192
+ rl.draw_text_ex(font, text, rl.Vector2(float(x), float(y)), float(font_size_px), float(spacing), color)
193
+ try:
194
+ size = rl.measure_text_ex(font, text, float(font_size_px), float(spacing))
195
+ width = float(size.x)
196
+ except Exception:
197
+ width = float(rl.measure_text(text, int(font_size_px)))
198
+ line_y = y + 13.0
199
+ rl.draw_line(int(x), int(line_y), int(x + width), int(line_y), color)
200
+
201
+
202
+ @register_view("small-font-debug", "Small font debug")
203
+ def build_small_font_debug_view(ctx: ViewContext) -> View:
204
+ return SmallFontDebugView(ctx)