crimsonland 0.1.0.dev15__py3-none-any.whl → 0.1.0.dev16__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 (75) hide show
  1. crimson/cli.py +61 -0
  2. crimson/creatures/damage.py +111 -36
  3. crimson/creatures/runtime.py +246 -156
  4. crimson/creatures/spawn.py +7 -3
  5. crimson/demo.py +38 -45
  6. crimson/effects.py +7 -13
  7. crimson/frontend/high_scores_layout.py +81 -0
  8. crimson/frontend/panels/base.py +4 -1
  9. crimson/frontend/panels/controls.py +0 -15
  10. crimson/frontend/panels/databases.py +291 -3
  11. crimson/frontend/panels/mods.py +0 -15
  12. crimson/frontend/panels/play_game.py +0 -16
  13. crimson/game.py +441 -1
  14. crimson/gameplay.py +905 -569
  15. crimson/modes/base_gameplay_mode.py +33 -12
  16. crimson/modes/components/__init__.py +2 -0
  17. crimson/modes/components/highscore_record_builder.py +58 -0
  18. crimson/modes/components/perk_menu_controller.py +325 -0
  19. crimson/modes/quest_mode.py +58 -273
  20. crimson/modes/rush_mode.py +12 -43
  21. crimson/modes/survival_mode.py +71 -328
  22. crimson/modes/tutorial_mode.py +46 -247
  23. crimson/modes/typo_mode.py +11 -38
  24. crimson/oracle.py +396 -0
  25. crimson/perks.py +5 -2
  26. crimson/player_damage.py +94 -37
  27. crimson/projectiles.py +539 -320
  28. crimson/render/projectile_draw_registry.py +637 -0
  29. crimson/render/projectile_render_registry.py +110 -0
  30. crimson/render/secondary_projectile_draw_registry.py +206 -0
  31. crimson/render/world_renderer.py +58 -707
  32. crimson/sim/world_state.py +118 -61
  33. crimson/typo/spawns.py +5 -12
  34. crimson/ui/demo_trial_overlay.py +3 -11
  35. crimson/ui/formatting.py +24 -0
  36. crimson/ui/game_over.py +12 -58
  37. crimson/ui/hud.py +72 -39
  38. crimson/ui/layout.py +20 -0
  39. crimson/ui/perk_menu.py +9 -34
  40. crimson/ui/quest_results.py +12 -64
  41. crimson/ui/text_input.py +20 -0
  42. crimson/views/_ui_helpers.py +27 -0
  43. crimson/views/aim_debug.py +15 -32
  44. crimson/views/animations.py +18 -28
  45. crimson/views/arsenal_debug.py +22 -32
  46. crimson/views/bonuses.py +23 -36
  47. crimson/views/camera_debug.py +16 -29
  48. crimson/views/camera_shake.py +9 -33
  49. crimson/views/corpse_stamp_debug.py +13 -21
  50. crimson/views/decals_debug.py +36 -23
  51. crimson/views/fonts.py +8 -25
  52. crimson/views/ground.py +4 -21
  53. crimson/views/lighting_debug.py +42 -45
  54. crimson/views/particles.py +33 -42
  55. crimson/views/perk_menu_debug.py +3 -10
  56. crimson/views/player.py +50 -44
  57. crimson/views/player_sprite_debug.py +24 -31
  58. crimson/views/projectile_fx.py +57 -52
  59. crimson/views/projectile_render_debug.py +24 -33
  60. crimson/views/projectiles.py +24 -37
  61. crimson/views/spawn_plan.py +13 -29
  62. crimson/views/sprites.py +14 -29
  63. crimson/views/terrain.py +6 -23
  64. crimson/views/ui.py +7 -24
  65. crimson/views/wicons.py +28 -33
  66. {crimsonland-0.1.0.dev15.dist-info → crimsonland-0.1.0.dev16.dist-info}/METADATA +1 -1
  67. {crimsonland-0.1.0.dev15.dist-info → crimsonland-0.1.0.dev16.dist-info}/RECORD +72 -64
  68. {crimsonland-0.1.0.dev15.dist-info → crimsonland-0.1.0.dev16.dist-info}/WHEEL +2 -2
  69. grim/config.py +29 -1
  70. grim/console.py +7 -10
  71. grim/math.py +12 -0
  72. crimson/.DS_Store +0 -0
  73. crimson/creatures/.DS_Store +0 -0
  74. grim/.DS_Store +0 -0
  75. {crimsonland-0.1.0.dev15.dist-info → crimsonland-0.1.0.dev16.dist-info}/entry_points.txt +0 -0
@@ -0,0 +1,110 @@
1
+ from __future__ import annotations
2
+
3
+ from dataclasses import dataclass
4
+
5
+ from ..projectiles import ProjectileTypeId
6
+
7
+
8
+ @dataclass(frozen=True, slots=True)
9
+ class PlasmaProjectileRenderConfig:
10
+ rgb: tuple[float, float, float]
11
+ spacing: float
12
+ seg_limit: int
13
+ tail_size: float
14
+ head_size: float
15
+ head_alpha_mul: float
16
+ aura_rgb: tuple[float, float, float]
17
+ aura_size: float
18
+ aura_alpha_mul: float
19
+
20
+
21
+ _DEFAULT_PLASMA_RENDER_CONFIG = PlasmaProjectileRenderConfig(
22
+ rgb=(1.0, 1.0, 1.0),
23
+ spacing=2.1,
24
+ seg_limit=3,
25
+ tail_size=12.0,
26
+ head_size=16.0,
27
+ head_alpha_mul=0.45,
28
+ aura_rgb=(1.0, 1.0, 1.0),
29
+ aura_size=120.0,
30
+ aura_alpha_mul=0.15,
31
+ )
32
+
33
+
34
+ PLASMA_PROJECTILE_RENDER_CONFIG_BY_TYPE_ID: dict[int, PlasmaProjectileRenderConfig] = {
35
+ int(ProjectileTypeId.PLASMA_RIFLE): PlasmaProjectileRenderConfig(
36
+ rgb=(1.0, 1.0, 1.0),
37
+ spacing=2.5,
38
+ seg_limit=8,
39
+ tail_size=22.0,
40
+ head_size=56.0,
41
+ head_alpha_mul=0.45,
42
+ aura_rgb=(1.0, 1.0, 1.0),
43
+ aura_size=256.0,
44
+ aura_alpha_mul=0.3,
45
+ ),
46
+ int(ProjectileTypeId.PLASMA_MINIGUN): _DEFAULT_PLASMA_RENDER_CONFIG,
47
+ int(ProjectileTypeId.PLASMA_CANNON): PlasmaProjectileRenderConfig(
48
+ rgb=(1.0, 1.0, 1.0),
49
+ spacing=2.6,
50
+ seg_limit=18,
51
+ tail_size=44.0,
52
+ head_size=84.0,
53
+ head_alpha_mul=0.45,
54
+ aura_rgb=(1.0, 1.0, 1.0),
55
+ aura_size=256.0,
56
+ aura_alpha_mul=0.4,
57
+ ),
58
+ int(ProjectileTypeId.SPIDER_PLASMA): PlasmaProjectileRenderConfig(
59
+ rgb=(0.3, 1.0, 0.3),
60
+ spacing=_DEFAULT_PLASMA_RENDER_CONFIG.spacing,
61
+ seg_limit=_DEFAULT_PLASMA_RENDER_CONFIG.seg_limit,
62
+ tail_size=_DEFAULT_PLASMA_RENDER_CONFIG.tail_size,
63
+ head_size=_DEFAULT_PLASMA_RENDER_CONFIG.head_size,
64
+ head_alpha_mul=_DEFAULT_PLASMA_RENDER_CONFIG.head_alpha_mul,
65
+ aura_rgb=(0.3, 1.0, 0.3),
66
+ aura_size=_DEFAULT_PLASMA_RENDER_CONFIG.aura_size,
67
+ aura_alpha_mul=_DEFAULT_PLASMA_RENDER_CONFIG.aura_alpha_mul,
68
+ ),
69
+ int(ProjectileTypeId.SHRINKIFIER): PlasmaProjectileRenderConfig(
70
+ rgb=(0.3, 0.3, 1.0),
71
+ spacing=_DEFAULT_PLASMA_RENDER_CONFIG.spacing,
72
+ seg_limit=_DEFAULT_PLASMA_RENDER_CONFIG.seg_limit,
73
+ tail_size=_DEFAULT_PLASMA_RENDER_CONFIG.tail_size,
74
+ head_size=_DEFAULT_PLASMA_RENDER_CONFIG.head_size,
75
+ head_alpha_mul=_DEFAULT_PLASMA_RENDER_CONFIG.head_alpha_mul,
76
+ aura_rgb=(0.3, 0.3, 1.0),
77
+ aura_size=_DEFAULT_PLASMA_RENDER_CONFIG.aura_size,
78
+ aura_alpha_mul=_DEFAULT_PLASMA_RENDER_CONFIG.aura_alpha_mul,
79
+ ),
80
+ }
81
+
82
+
83
+ def plasma_projectile_render_config(type_id: int) -> PlasmaProjectileRenderConfig:
84
+ return PLASMA_PROJECTILE_RENDER_CONFIG_BY_TYPE_ID.get(int(type_id), _DEFAULT_PLASMA_RENDER_CONFIG)
85
+
86
+
87
+ BEAM_EFFECT_SCALE_BY_TYPE_ID: dict[int, float] = {
88
+ int(ProjectileTypeId.ION_MINIGUN): 1.05,
89
+ int(ProjectileTypeId.ION_RIFLE): 2.2,
90
+ int(ProjectileTypeId.ION_CANNON): 3.5,
91
+ }
92
+
93
+
94
+ def beam_effect_scale(type_id: int) -> float:
95
+ return float(BEAM_EFFECT_SCALE_BY_TYPE_ID.get(int(type_id), 0.8))
96
+
97
+
98
+ KNOWN_PROJ_RGB_BY_TYPE_ID: dict[int, tuple[int, int, int]] = {
99
+ int(ProjectileTypeId.ION_RIFLE): (120, 200, 255),
100
+ int(ProjectileTypeId.ION_MINIGUN): (120, 200, 255),
101
+ int(ProjectileTypeId.ION_CANNON): (120, 200, 255),
102
+ int(ProjectileTypeId.FIRE_BULLETS): (255, 170, 90),
103
+ int(ProjectileTypeId.SHRINKIFIER): (160, 255, 170),
104
+ int(ProjectileTypeId.BLADE_GUN): (240, 120, 255),
105
+ }
106
+
107
+
108
+ def known_proj_rgb(type_id: int) -> tuple[int, int, int]:
109
+ return KNOWN_PROJ_RGB_BY_TYPE_ID.get(int(type_id), (240, 220, 160))
110
+
@@ -0,0 +1,206 @@
1
+ from __future__ import annotations
2
+
3
+ from dataclasses import dataclass
4
+ import math
5
+ from typing import TYPE_CHECKING
6
+
7
+ import pyray as rl
8
+
9
+ from grim.math import clamp
10
+
11
+ from ..effects_atlas import EFFECT_ID_ATLAS_TABLE_BY_ID, SIZE_CODE_GRID
12
+
13
+ if TYPE_CHECKING:
14
+ from .world_renderer import WorldRenderer
15
+
16
+
17
+ @dataclass(frozen=True, slots=True)
18
+ class SecondaryProjectileDrawCtx:
19
+ renderer: WorldRenderer
20
+ proj: object
21
+ proj_type: int
22
+ sx: float
23
+ sy: float
24
+ angle: float
25
+ scale: float
26
+ alpha: float
27
+
28
+
29
+ def _draw_secondary_rocket_like(ctx: SecondaryProjectileDrawCtx) -> bool:
30
+ renderer = ctx.renderer
31
+ proj_type = int(ctx.proj_type)
32
+ if proj_type not in (1, 2, 4):
33
+ return False
34
+
35
+ texture = renderer.projs_texture
36
+ if texture is None:
37
+ return False
38
+
39
+ cell_w = float(texture.width) / 4.0
40
+ if cell_w <= 1e-6:
41
+ return True
42
+
43
+ alpha = float(ctx.alpha)
44
+ base_alpha = clamp(alpha * 0.9, 0.0, 1.0)
45
+ base_tint = renderer._color_from_rgba((0.8, 0.8, 0.8, base_alpha))
46
+ base_size = 14.0
47
+ if proj_type == 2:
48
+ base_size = 10.0
49
+ elif proj_type == 4:
50
+ base_size = 8.0
51
+ sprite_scale = (base_size * float(ctx.scale)) / cell_w
52
+
53
+ fx_detail_1 = bool(renderer.config.data.get("fx_detail_1", 0)) if renderer.config is not None else True
54
+ if fx_detail_1 and renderer.particles_texture is not None:
55
+ particles_texture = renderer.particles_texture
56
+ atlas = EFFECT_ID_ATLAS_TABLE_BY_ID.get(0x0D)
57
+ if atlas is not None:
58
+ grid = SIZE_CODE_GRID.get(int(atlas.size_code))
59
+ if grid:
60
+ particle_cell_w = float(particles_texture.width) / float(grid)
61
+ particle_cell_h = float(particles_texture.height) / float(grid)
62
+ frame = int(atlas.frame)
63
+ col = frame % grid
64
+ row = frame // grid
65
+ src = rl.Rectangle(
66
+ particle_cell_w * float(col),
67
+ particle_cell_h * float(row),
68
+ max(0.0, particle_cell_w - 2.0),
69
+ max(0.0, particle_cell_h - 2.0),
70
+ )
71
+
72
+ angle = float(ctx.angle)
73
+ dir_x = math.cos(angle - math.pi / 2.0)
74
+ dir_y = math.sin(angle - math.pi / 2.0)
75
+
76
+ sx = float(ctx.sx)
77
+ sy = float(ctx.sy)
78
+ scale = float(ctx.scale)
79
+
80
+ def _draw_rocket_fx(
81
+ *,
82
+ size: float,
83
+ offset: float,
84
+ rgba: tuple[float, float, float, float],
85
+ ) -> None:
86
+ fx_alpha = rgba[3]
87
+ if fx_alpha <= 1e-3:
88
+ return
89
+ tint = renderer._color_from_rgba(rgba)
90
+ fx_sx = sx - dir_x * offset * scale
91
+ fx_sy = sy - dir_y * offset * scale
92
+ dst_size = float(size) * scale
93
+ dst = rl.Rectangle(float(fx_sx), float(fx_sy), float(dst_size), float(dst_size))
94
+ origin = rl.Vector2(dst_size * 0.5, dst_size * 0.5)
95
+ rl.draw_texture_pro(particles_texture, src, dst, origin, 0.0, tint)
96
+
97
+ rl.begin_blend_mode(rl.BLEND_ADDITIVE)
98
+ # Large bloom around the rocket (effect_id=0x0D).
99
+ _draw_rocket_fx(size=140.0, offset=5.0, rgba=(1.0, 1.0, 1.0, alpha * 0.48))
100
+
101
+ if proj_type == 4:
102
+ _draw_rocket_fx(size=30.0, offset=9.0, rgba=(0.7, 0.7, 1.0, alpha * 0.158))
103
+ elif proj_type == 2:
104
+ _draw_rocket_fx(size=40.0, offset=9.0, rgba=(1.0, 1.0, 1.0, alpha * 0.58))
105
+ else:
106
+ _draw_rocket_fx(size=60.0, offset=9.0, rgba=(1.0, 1.0, 1.0, alpha * 0.68))
107
+
108
+ rl.end_blend_mode()
109
+
110
+ renderer._draw_atlas_sprite(
111
+ texture,
112
+ grid=4,
113
+ frame=3,
114
+ x=float(ctx.sx),
115
+ y=float(ctx.sy),
116
+ scale=sprite_scale,
117
+ rotation_rad=float(ctx.angle),
118
+ tint=base_tint,
119
+ )
120
+ return True
121
+
122
+
123
+ def _draw_secondary_type4_fallback(ctx: SecondaryProjectileDrawCtx) -> bool:
124
+ if int(ctx.proj_type) != 4:
125
+ return False
126
+ rl.draw_circle(
127
+ int(ctx.sx),
128
+ int(ctx.sy),
129
+ max(1.0, 12.0 * float(ctx.scale)),
130
+ rl.Color(200, 120, 255, int(255 * float(ctx.alpha) + 0.5)),
131
+ )
132
+ return True
133
+
134
+
135
+ def _draw_secondary_detonation(ctx: SecondaryProjectileDrawCtx) -> bool:
136
+ renderer = ctx.renderer
137
+ if int(ctx.proj_type) != 3:
138
+ return False
139
+
140
+ # Secondary projectile detonation visuals (secondary_projectile_update + render).
141
+ t = clamp(float(getattr(ctx.proj, "vel_x", 0.0)), 0.0, 1.0)
142
+ det_scale = float(getattr(ctx.proj, "vel_y", 1.0))
143
+ fade = (1.0 - t) * float(ctx.alpha)
144
+ if fade <= 1e-3 or det_scale <= 1e-6:
145
+ return True
146
+
147
+ sx = float(ctx.sx)
148
+ sy = float(ctx.sy)
149
+ scale = float(ctx.scale)
150
+ if renderer.particles_texture is None:
151
+ radius = det_scale * t * 80.0
152
+ alpha_byte = int(clamp((1.0 - t) * 180.0 * float(ctx.alpha), 0.0, 255.0) + 0.5)
153
+ color = rl.Color(255, 180, 100, alpha_byte)
154
+ rl.draw_circle_lines(int(sx), int(sy), max(1.0, radius * scale), color)
155
+ return True
156
+
157
+ atlas = EFFECT_ID_ATLAS_TABLE_BY_ID.get(0x0D)
158
+ if atlas is None:
159
+ return True
160
+ grid = SIZE_CODE_GRID.get(int(atlas.size_code))
161
+ if not grid:
162
+ return True
163
+ frame = int(atlas.frame)
164
+ col = frame % grid
165
+ row = frame // grid
166
+ cell_w = float(renderer.particles_texture.width) / float(grid)
167
+ cell_h = float(renderer.particles_texture.height) / float(grid)
168
+ src = rl.Rectangle(
169
+ cell_w * float(col),
170
+ cell_h * float(row),
171
+ max(0.0, cell_w - 2.0),
172
+ max(0.0, cell_h - 2.0),
173
+ )
174
+
175
+ def _draw_detonation_quad(*, size: float, alpha_mul: float) -> None:
176
+ a = fade * alpha_mul
177
+ if a <= 1e-3:
178
+ return
179
+ dst_size = float(size) * scale
180
+ if dst_size <= 1e-3:
181
+ return
182
+ tint = renderer._color_from_rgba((1.0, 0.6, 0.1, a))
183
+ dst = rl.Rectangle(float(sx), float(sy), float(dst_size), float(dst_size))
184
+ origin = rl.Vector2(float(dst_size) * 0.5, float(dst_size) * 0.5)
185
+ rl.draw_texture_pro(renderer.particles_texture, src, dst, origin, 0.0, tint)
186
+
187
+ rl.begin_blend_mode(rl.BLEND_ADDITIVE)
188
+ _draw_detonation_quad(size=det_scale * t * 64.0, alpha_mul=1.0)
189
+ _draw_detonation_quad(size=det_scale * t * 200.0, alpha_mul=0.3)
190
+ rl.end_blend_mode()
191
+ return True
192
+
193
+
194
+ SECONDARY_PROJECTILE_DRAW_HANDLERS = (
195
+ _draw_secondary_rocket_like,
196
+ _draw_secondary_type4_fallback,
197
+ _draw_secondary_detonation,
198
+ )
199
+
200
+
201
+ def draw_secondary_projectile_from_registry(ctx: SecondaryProjectileDrawCtx) -> bool:
202
+ for handler in SECONDARY_PROJECTILE_DRAW_HANDLERS:
203
+ if handler(ctx):
204
+ return True
205
+ return False
206
+