crimsonland 0.1.0.dev2__tar.gz → 0.1.0.dev8__tar.gz
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.
- {crimsonland-0.1.0.dev2 → crimsonland-0.1.0.dev8}/PKG-INFO +1 -1
- {crimsonland-0.1.0.dev2 → crimsonland-0.1.0.dev8}/pyproject.toml +1 -1
- {crimsonland-0.1.0.dev2 → crimsonland-0.1.0.dev8}/src/crimson/audio_router.py +15 -3
- {crimsonland-0.1.0.dev2 → crimsonland-0.1.0.dev8}/src/crimson/creatures/anim.py +1 -0
- {crimsonland-0.1.0.dev2 → crimsonland-0.1.0.dev8}/src/crimson/demo.py +2 -2
- {crimsonland-0.1.0.dev2 → crimsonland-0.1.0.dev8}/src/crimson/effects.py +1 -1
- {crimsonland-0.1.0.dev2 → crimsonland-0.1.0.dev8}/src/crimson/game.py +5 -0
- {crimsonland-0.1.0.dev2 → crimsonland-0.1.0.dev8}/src/crimson/game_world.py +3 -14
- {crimsonland-0.1.0.dev2 → crimsonland-0.1.0.dev8}/src/crimson/gameplay.py +138 -63
- {crimsonland-0.1.0.dev2 → crimsonland-0.1.0.dev8}/src/crimson/modes/base_gameplay_mode.py +75 -1
- crimsonland-0.1.0.dev8/src/crimson/modes/quest_mode.py +929 -0
- {crimsonland-0.1.0.dev2 → crimsonland-0.1.0.dev8}/src/crimson/modes/rush_mode.py +12 -3
- {crimsonland-0.1.0.dev2 → crimsonland-0.1.0.dev8}/src/crimson/modes/survival_mode.py +13 -2
- {crimsonland-0.1.0.dev2 → crimsonland-0.1.0.dev8}/src/crimson/modes/tutorial_mode.py +12 -1
- {crimsonland-0.1.0.dev2 → crimsonland-0.1.0.dev8}/src/crimson/modes/typo_mode.py +12 -4
- {crimsonland-0.1.0.dev2 → crimsonland-0.1.0.dev8}/src/crimson/projectiles.py +318 -61
- {crimsonland-0.1.0.dev2 → crimsonland-0.1.0.dev8}/src/crimson/render/world_renderer.py +789 -46
- {crimsonland-0.1.0.dev2 → crimsonland-0.1.0.dev8}/src/crimson/sim/world_defs.py +16 -5
- {crimsonland-0.1.0.dev2 → crimsonland-0.1.0.dev8}/src/crimson/sim/world_state.py +1 -0
- {crimsonland-0.1.0.dev2 → crimsonland-0.1.0.dev8}/src/crimson/ui/hud.py +274 -51
- {crimsonland-0.1.0.dev2 → crimsonland-0.1.0.dev8}/src/crimson/views/arsenal_debug.py +57 -1
- {crimsonland-0.1.0.dev2 → crimsonland-0.1.0.dev8}/src/crimson/views/player.py +3 -2
- {crimsonland-0.1.0.dev2 → crimsonland-0.1.0.dev8}/src/crimson/views/projectile_fx.py +1 -0
- {crimsonland-0.1.0.dev2 → crimsonland-0.1.0.dev8}/src/crimson/views/projectile_render_debug.py +9 -2
- {crimsonland-0.1.0.dev2 → crimsonland-0.1.0.dev8}/src/grim/console.py +14 -0
- crimsonland-0.1.0.dev2/src/crimson/modes/quest_mode.py +0 -502
- {crimsonland-0.1.0.dev2 → crimsonland-0.1.0.dev8}/src/crimson/__init__.py +0 -0
- {crimsonland-0.1.0.dev2 → crimsonland-0.1.0.dev8}/src/crimson/assets_fetch.py +0 -0
- {crimsonland-0.1.0.dev2 → crimsonland-0.1.0.dev8}/src/crimson/atlas.py +0 -0
- {crimsonland-0.1.0.dev2 → crimsonland-0.1.0.dev8}/src/crimson/bonuses.py +0 -0
- {crimsonland-0.1.0.dev2 → crimsonland-0.1.0.dev8}/src/crimson/camera.py +0 -0
- {crimsonland-0.1.0.dev2 → crimsonland-0.1.0.dev8}/src/crimson/cli.py +0 -0
- {crimsonland-0.1.0.dev2 → crimsonland-0.1.0.dev8}/src/crimson/creatures/__init__.py +0 -0
- {crimsonland-0.1.0.dev2 → crimsonland-0.1.0.dev8}/src/crimson/creatures/ai.py +0 -0
- {crimsonland-0.1.0.dev2 → crimsonland-0.1.0.dev8}/src/crimson/creatures/damage.py +0 -0
- {crimsonland-0.1.0.dev2 → crimsonland-0.1.0.dev8}/src/crimson/creatures/runtime.py +0 -0
- {crimsonland-0.1.0.dev2 → crimsonland-0.1.0.dev8}/src/crimson/creatures/spawn.py +0 -0
- {crimsonland-0.1.0.dev2 → crimsonland-0.1.0.dev8}/src/crimson/debug.py +0 -0
- {crimsonland-0.1.0.dev2 → crimsonland-0.1.0.dev8}/src/crimson/demo_trial.py +0 -0
- {crimsonland-0.1.0.dev2 → crimsonland-0.1.0.dev8}/src/crimson/effects_atlas.py +0 -0
- {crimsonland-0.1.0.dev2 → crimsonland-0.1.0.dev8}/src/crimson/frontend/__init__.py +0 -0
- {crimsonland-0.1.0.dev2 → crimsonland-0.1.0.dev8}/src/crimson/frontend/assets.py +0 -0
- {crimsonland-0.1.0.dev2 → crimsonland-0.1.0.dev8}/src/crimson/frontend/boot.py +0 -0
- {crimsonland-0.1.0.dev2 → crimsonland-0.1.0.dev8}/src/crimson/frontend/menu.py +0 -0
- {crimsonland-0.1.0.dev2 → crimsonland-0.1.0.dev8}/src/crimson/frontend/panels/__init__.py +0 -0
- {crimsonland-0.1.0.dev2 → crimsonland-0.1.0.dev8}/src/crimson/frontend/panels/base.py +0 -0
- {crimsonland-0.1.0.dev2 → crimsonland-0.1.0.dev8}/src/crimson/frontend/panels/controls.py +0 -0
- {crimsonland-0.1.0.dev2 → crimsonland-0.1.0.dev8}/src/crimson/frontend/panels/mods.py +0 -0
- {crimsonland-0.1.0.dev2 → crimsonland-0.1.0.dev8}/src/crimson/frontend/panels/options.py +0 -0
- {crimsonland-0.1.0.dev2 → crimsonland-0.1.0.dev8}/src/crimson/frontend/panels/play_game.py +0 -0
- {crimsonland-0.1.0.dev2 → crimsonland-0.1.0.dev8}/src/crimson/frontend/panels/stats.py +0 -0
- {crimsonland-0.1.0.dev2 → crimsonland-0.1.0.dev8}/src/crimson/frontend/transitions.py +0 -0
- {crimsonland-0.1.0.dev2 → crimsonland-0.1.0.dev8}/src/crimson/game_modes.py +0 -0
- {crimsonland-0.1.0.dev2 → crimsonland-0.1.0.dev8}/src/crimson/input_codes.py +0 -0
- {crimsonland-0.1.0.dev2 → crimsonland-0.1.0.dev8}/src/crimson/modes/__init__.py +0 -0
- {crimsonland-0.1.0.dev2 → crimsonland-0.1.0.dev8}/src/crimson/paths.py +0 -0
- {crimsonland-0.1.0.dev2 → crimsonland-0.1.0.dev8}/src/crimson/perks.py +0 -0
- {crimsonland-0.1.0.dev2 → crimsonland-0.1.0.dev8}/src/crimson/persistence/__init__.py +0 -0
- {crimsonland-0.1.0.dev2 → crimsonland-0.1.0.dev8}/src/crimson/persistence/highscores.py +0 -0
- {crimsonland-0.1.0.dev2 → crimsonland-0.1.0.dev8}/src/crimson/persistence/save_status.py +0 -0
- {crimsonland-0.1.0.dev2 → crimsonland-0.1.0.dev8}/src/crimson/player_damage.py +0 -0
- {crimsonland-0.1.0.dev2 → crimsonland-0.1.0.dev8}/src/crimson/quests/__init__.py +0 -0
- {crimsonland-0.1.0.dev2 → crimsonland-0.1.0.dev8}/src/crimson/quests/helpers.py +0 -0
- {crimsonland-0.1.0.dev2 → crimsonland-0.1.0.dev8}/src/crimson/quests/registry.py +0 -0
- {crimsonland-0.1.0.dev2 → crimsonland-0.1.0.dev8}/src/crimson/quests/results.py +0 -0
- {crimsonland-0.1.0.dev2 → crimsonland-0.1.0.dev8}/src/crimson/quests/runtime.py +0 -0
- {crimsonland-0.1.0.dev2 → crimsonland-0.1.0.dev8}/src/crimson/quests/tier1.py +0 -0
- {crimsonland-0.1.0.dev2 → crimsonland-0.1.0.dev8}/src/crimson/quests/tier2.py +0 -0
- {crimsonland-0.1.0.dev2 → crimsonland-0.1.0.dev8}/src/crimson/quests/tier3.py +0 -0
- {crimsonland-0.1.0.dev2 → crimsonland-0.1.0.dev8}/src/crimson/quests/tier4.py +0 -0
- {crimsonland-0.1.0.dev2 → crimsonland-0.1.0.dev8}/src/crimson/quests/tier5.py +0 -0
- {crimsonland-0.1.0.dev2 → crimsonland-0.1.0.dev8}/src/crimson/quests/timeline.py +0 -0
- {crimsonland-0.1.0.dev2 → crimsonland-0.1.0.dev8}/src/crimson/quests/types.py +0 -0
- {crimsonland-0.1.0.dev2 → crimsonland-0.1.0.dev8}/src/crimson/render/__init__.py +0 -0
- {crimsonland-0.1.0.dev2 → crimsonland-0.1.0.dev8}/src/crimson/render/terrain_fx.py +0 -0
- {crimsonland-0.1.0.dev2 → crimsonland-0.1.0.dev8}/src/crimson/sim/__init__.py +0 -0
- {crimsonland-0.1.0.dev2 → crimsonland-0.1.0.dev8}/src/crimson/terrain_assets.py +0 -0
- {crimsonland-0.1.0.dev2 → crimsonland-0.1.0.dev8}/src/crimson/tutorial/__init__.py +0 -0
- {crimsonland-0.1.0.dev2 → crimsonland-0.1.0.dev8}/src/crimson/tutorial/timeline.py +0 -0
- {crimsonland-0.1.0.dev2 → crimsonland-0.1.0.dev8}/src/crimson/typo/__init__.py +0 -0
- {crimsonland-0.1.0.dev2 → crimsonland-0.1.0.dev8}/src/crimson/typo/names.py +0 -0
- {crimsonland-0.1.0.dev2 → crimsonland-0.1.0.dev8}/src/crimson/typo/player.py +0 -0
- {crimsonland-0.1.0.dev2 → crimsonland-0.1.0.dev8}/src/crimson/typo/spawns.py +0 -0
- {crimsonland-0.1.0.dev2 → crimsonland-0.1.0.dev8}/src/crimson/typo/typing.py +0 -0
- {crimsonland-0.1.0.dev2 → crimsonland-0.1.0.dev8}/src/crimson/ui/__init__.py +0 -0
- {crimsonland-0.1.0.dev2 → crimsonland-0.1.0.dev8}/src/crimson/ui/cursor.py +0 -0
- {crimsonland-0.1.0.dev2 → crimsonland-0.1.0.dev8}/src/crimson/ui/demo_trial_overlay.py +0 -0
- {crimsonland-0.1.0.dev2 → crimsonland-0.1.0.dev8}/src/crimson/ui/game_over.py +0 -0
- {crimsonland-0.1.0.dev2 → crimsonland-0.1.0.dev8}/src/crimson/ui/perk_menu.py +0 -0
- {crimsonland-0.1.0.dev2 → crimsonland-0.1.0.dev8}/src/crimson/views/__init__.py +0 -0
- {crimsonland-0.1.0.dev2 → crimsonland-0.1.0.dev8}/src/crimson/views/aim_debug.py +0 -0
- {crimsonland-0.1.0.dev2 → crimsonland-0.1.0.dev8}/src/crimson/views/animations.py +0 -0
- {crimsonland-0.1.0.dev2 → crimsonland-0.1.0.dev8}/src/crimson/views/audio_bootstrap.py +0 -0
- {crimsonland-0.1.0.dev2 → crimsonland-0.1.0.dev8}/src/crimson/views/bonuses.py +0 -0
- {crimsonland-0.1.0.dev2 → crimsonland-0.1.0.dev8}/src/crimson/views/camera_debug.py +0 -0
- {crimsonland-0.1.0.dev2 → crimsonland-0.1.0.dev8}/src/crimson/views/camera_shake.py +0 -0
- {crimsonland-0.1.0.dev2 → crimsonland-0.1.0.dev8}/src/crimson/views/corpse_stamp_debug.py +0 -0
- {crimsonland-0.1.0.dev2 → crimsonland-0.1.0.dev8}/src/crimson/views/decals_debug.py +0 -0
- {crimsonland-0.1.0.dev2 → crimsonland-0.1.0.dev8}/src/crimson/views/empty.py +0 -0
- {crimsonland-0.1.0.dev2 → crimsonland-0.1.0.dev8}/src/crimson/views/fonts.py +0 -0
- {crimsonland-0.1.0.dev2 → crimsonland-0.1.0.dev8}/src/crimson/views/game_over.py +0 -0
- {crimsonland-0.1.0.dev2 → crimsonland-0.1.0.dev8}/src/crimson/views/ground.py +0 -0
- {crimsonland-0.1.0.dev2 → crimsonland-0.1.0.dev8}/src/crimson/views/lighting_debug.py +0 -0
- {crimsonland-0.1.0.dev2 → crimsonland-0.1.0.dev8}/src/crimson/views/particles.py +0 -0
- {crimsonland-0.1.0.dev2 → crimsonland-0.1.0.dev8}/src/crimson/views/perk_menu_debug.py +0 -0
- {crimsonland-0.1.0.dev2 → crimsonland-0.1.0.dev8}/src/crimson/views/perks.py +0 -0
- {crimsonland-0.1.0.dev2 → crimsonland-0.1.0.dev8}/src/crimson/views/player_sprite_debug.py +0 -0
- {crimsonland-0.1.0.dev2 → crimsonland-0.1.0.dev8}/src/crimson/views/projectiles.py +0 -0
- {crimsonland-0.1.0.dev2 → crimsonland-0.1.0.dev8}/src/crimson/views/quest_title_overlay.py +0 -0
- {crimsonland-0.1.0.dev2 → crimsonland-0.1.0.dev8}/src/crimson/views/registry.py +0 -0
- {crimsonland-0.1.0.dev2 → crimsonland-0.1.0.dev8}/src/crimson/views/rush.py +0 -0
- {crimsonland-0.1.0.dev2 → crimsonland-0.1.0.dev8}/src/crimson/views/small_font_debug.py +0 -0
- {crimsonland-0.1.0.dev2 → crimsonland-0.1.0.dev8}/src/crimson/views/spawn_plan.py +0 -0
- {crimsonland-0.1.0.dev2 → crimsonland-0.1.0.dev8}/src/crimson/views/sprites.py +0 -0
- {crimsonland-0.1.0.dev2 → crimsonland-0.1.0.dev8}/src/crimson/views/survival.py +0 -0
- {crimsonland-0.1.0.dev2 → crimsonland-0.1.0.dev8}/src/crimson/views/terrain.py +0 -0
- {crimsonland-0.1.0.dev2 → crimsonland-0.1.0.dev8}/src/crimson/views/ui.py +0 -0
- {crimsonland-0.1.0.dev2 → crimsonland-0.1.0.dev8}/src/crimson/views/wicons.py +0 -0
- {crimsonland-0.1.0.dev2 → crimsonland-0.1.0.dev8}/src/crimson/weapon_sfx.py +0 -0
- {crimsonland-0.1.0.dev2 → crimsonland-0.1.0.dev8}/src/crimson/weapons.py +0 -0
- {crimsonland-0.1.0.dev2 → crimsonland-0.1.0.dev8}/src/grim/__init__.py +0 -0
- {crimsonland-0.1.0.dev2 → crimsonland-0.1.0.dev8}/src/grim/app.py +0 -0
- {crimsonland-0.1.0.dev2 → crimsonland-0.1.0.dev8}/src/grim/assets.py +0 -0
- {crimsonland-0.1.0.dev2 → crimsonland-0.1.0.dev8}/src/grim/audio.py +0 -0
- {crimsonland-0.1.0.dev2 → crimsonland-0.1.0.dev8}/src/grim/config.py +0 -0
- {crimsonland-0.1.0.dev2 → crimsonland-0.1.0.dev8}/src/grim/fonts/__init__.py +0 -0
- {crimsonland-0.1.0.dev2 → crimsonland-0.1.0.dev8}/src/grim/fonts/grim_mono.py +0 -0
- {crimsonland-0.1.0.dev2 → crimsonland-0.1.0.dev8}/src/grim/fonts/small.py +0 -0
- {crimsonland-0.1.0.dev2 → crimsonland-0.1.0.dev8}/src/grim/input.py +0 -0
- {crimsonland-0.1.0.dev2 → crimsonland-0.1.0.dev8}/src/grim/jaz.py +0 -0
- {crimsonland-0.1.0.dev2 → crimsonland-0.1.0.dev8}/src/grim/math.py +0 -0
- {crimsonland-0.1.0.dev2 → crimsonland-0.1.0.dev8}/src/grim/music.py +0 -0
- {crimsonland-0.1.0.dev2 → crimsonland-0.1.0.dev8}/src/grim/paq.py +0 -0
- {crimsonland-0.1.0.dev2 → crimsonland-0.1.0.dev8}/src/grim/rand.py +0 -0
- {crimsonland-0.1.0.dev2 → crimsonland-0.1.0.dev8}/src/grim/sfx.py +0 -0
- {crimsonland-0.1.0.dev2 → crimsonland-0.1.0.dev8}/src/grim/sfx_map.py +0 -0
- {crimsonland-0.1.0.dev2 → crimsonland-0.1.0.dev8}/src/grim/terrain_render.py +0 -0
- {crimsonland-0.1.0.dev2 → crimsonland-0.1.0.dev8}/src/grim/view.py +0 -0
|
@@ -9,7 +9,7 @@ from grim.audio import AudioState, play_sfx, trigger_game_tune
|
|
|
9
9
|
from .creatures.spawn import CreatureTypeId
|
|
10
10
|
from .game_modes import GameMode
|
|
11
11
|
from .weapon_sfx import resolve_weapon_sfx_ref
|
|
12
|
-
from .weapons import WEAPON_BY_ID
|
|
12
|
+
from .weapons import WEAPON_BY_ID, WeaponId
|
|
13
13
|
|
|
14
14
|
_MAX_HIT_SFX_PER_FRAME = 4
|
|
15
15
|
_MAX_DEATH_SFX_PER_FRAME = 3
|
|
@@ -96,7 +96,17 @@ class AudioRouter:
|
|
|
96
96
|
return
|
|
97
97
|
|
|
98
98
|
if int(getattr(player, "shot_seq", 0)) > int(prev_shot_seq):
|
|
99
|
-
|
|
99
|
+
if float(getattr(player, "fire_bullets_timer", 0.0)) > 0.0:
|
|
100
|
+
# player_update (crimsonland.exe): when Fire Bullets is active, the regular per-weapon
|
|
101
|
+
# shot sfx is suppressed and replaced by Fire Bullets + Plasma Minigun fire sfx.
|
|
102
|
+
fire_bullets = WEAPON_BY_ID.get(int(WeaponId.FIRE_BULLETS))
|
|
103
|
+
plasma_minigun = WEAPON_BY_ID.get(int(WeaponId.PLASMA_MINIGUN))
|
|
104
|
+
if fire_bullets is not None:
|
|
105
|
+
self.play_sfx(resolve_weapon_sfx_ref(fire_bullets.fire_sound))
|
|
106
|
+
if plasma_minigun is not None:
|
|
107
|
+
self.play_sfx(resolve_weapon_sfx_ref(plasma_minigun.fire_sound))
|
|
108
|
+
else:
|
|
109
|
+
self.play_sfx(resolve_weapon_sfx_ref(weapon.fire_sound))
|
|
100
110
|
|
|
101
111
|
reload_active = bool(getattr(player, "reload_active", False))
|
|
102
112
|
reload_timer = float(getattr(player, "reload_timer", 0.0))
|
|
@@ -111,7 +121,9 @@ class AudioRouter:
|
|
|
111
121
|
beam_types: frozenset[int],
|
|
112
122
|
rand: Callable[[], int],
|
|
113
123
|
) -> str | None:
|
|
114
|
-
|
|
124
|
+
weapon = WEAPON_BY_ID.get(int(type_id))
|
|
125
|
+
ammo_class = weapon.ammo_class if weapon is not None else None
|
|
126
|
+
if ammo_class == 4:
|
|
115
127
|
return "sfx_shock_hit_01"
|
|
116
128
|
return self._rand_choice(rand, _BULLET_HIT_SFX)
|
|
117
129
|
|
|
@@ -690,8 +690,8 @@ class DemoView:
|
|
|
690
690
|
rl.draw_circle(int(sx), int(sy), radius, rl.Color(200, 120, 255, 255))
|
|
691
691
|
continue
|
|
692
692
|
if proj.type_id == 3:
|
|
693
|
-
t = _clamp(proj.
|
|
694
|
-
radius = proj.
|
|
693
|
+
t = _clamp(proj.vel_x, 0.0, 1.0)
|
|
694
|
+
radius = proj.vel_y * t * 80.0
|
|
695
695
|
alpha = int((1.0 - t) * 180.0)
|
|
696
696
|
color = rl.Color(200, 120, 255, alpha)
|
|
697
697
|
rl.draw_circle_lines(int(sx), int(sy), max(1.0, radius * scale), color)
|
|
@@ -261,7 +261,7 @@ class ParticlePool:
|
|
|
261
261
|
entry.age = alpha
|
|
262
262
|
entry.scale_x = shade
|
|
263
263
|
entry.scale_y = shade
|
|
264
|
-
|
|
264
|
+
# Native only updates scale_x/scale_y; scale_z stays at its spawn value (1.0).
|
|
265
265
|
|
|
266
266
|
alive = entry.intensity > (0.0 if style == 0 else 0.8)
|
|
267
267
|
if not alive:
|
|
@@ -789,6 +789,7 @@ class SurvivalGameView:
|
|
|
789
789
|
ViewContext(assets_dir=state.assets_dir),
|
|
790
790
|
texture_cache=state.texture_cache,
|
|
791
791
|
config=state.config,
|
|
792
|
+
console=state.console,
|
|
792
793
|
audio=state.audio,
|
|
793
794
|
audio_rng=state.rng,
|
|
794
795
|
)
|
|
@@ -848,6 +849,7 @@ class RushGameView:
|
|
|
848
849
|
ViewContext(assets_dir=state.assets_dir),
|
|
849
850
|
texture_cache=state.texture_cache,
|
|
850
851
|
config=state.config,
|
|
852
|
+
console=state.console,
|
|
851
853
|
audio=state.audio,
|
|
852
854
|
audio_rng=state.rng,
|
|
853
855
|
)
|
|
@@ -905,6 +907,7 @@ class TypoShooterGameView:
|
|
|
905
907
|
ViewContext(assets_dir=state.assets_dir),
|
|
906
908
|
texture_cache=state.texture_cache,
|
|
907
909
|
config=state.config,
|
|
910
|
+
console=state.console,
|
|
908
911
|
audio=state.audio,
|
|
909
912
|
audio_rng=state.rng,
|
|
910
913
|
)
|
|
@@ -962,6 +965,7 @@ class TutorialGameView:
|
|
|
962
965
|
ViewContext(assets_dir=state.assets_dir),
|
|
963
966
|
texture_cache=state.texture_cache,
|
|
964
967
|
config=state.config,
|
|
968
|
+
console=state.console,
|
|
965
969
|
audio=state.audio,
|
|
966
970
|
audio_rng=state.rng,
|
|
967
971
|
demo_mode_active=state.demo_enabled,
|
|
@@ -1011,6 +1015,7 @@ class QuestGameView:
|
|
|
1011
1015
|
ViewContext(assets_dir=state.assets_dir),
|
|
1012
1016
|
texture_cache=state.texture_cache,
|
|
1013
1017
|
config=state.config,
|
|
1018
|
+
console=state.console,
|
|
1014
1019
|
audio=state.audio,
|
|
1015
1020
|
audio_rng=state.rng,
|
|
1016
1021
|
demo_mode_active=state.demo_enabled,
|
|
@@ -31,7 +31,7 @@ from .render.world_renderer import WorldRenderer
|
|
|
31
31
|
from .audio_router import AudioRouter
|
|
32
32
|
from .perks import PerkId
|
|
33
33
|
from .projectiles import ProjectileTypeId
|
|
34
|
-
from .sim.world_defs import BEAM_TYPES, CREATURE_ASSET
|
|
34
|
+
from .sim.world_defs import BEAM_TYPES, CREATURE_ASSET, ION_TYPES
|
|
35
35
|
from .sim.world_state import ProjectileHit, WorldState
|
|
36
36
|
from .weapons import WEAPON_TABLE
|
|
37
37
|
from .game_modes import GameMode
|
|
@@ -510,19 +510,8 @@ class GameWorld:
|
|
|
510
510
|
pos_y=float(target_y) + dir_y * dist * 20.0,
|
|
511
511
|
rand=rand,
|
|
512
512
|
)
|
|
513
|
-
elif type_id in
|
|
514
|
-
|
|
515
|
-
size = float(int(rand()) % 18 + 18)
|
|
516
|
-
rotation = float(int(rand()) % 628) * 0.01
|
|
517
|
-
self.fx_queue.add(
|
|
518
|
-
effect_id=0x01,
|
|
519
|
-
pos_x=float(hit_x),
|
|
520
|
-
pos_y=float(hit_y),
|
|
521
|
-
width=size,
|
|
522
|
-
height=size,
|
|
523
|
-
rotation=rotation,
|
|
524
|
-
rgba=(0.7, 0.9, 1.0, 1.0),
|
|
525
|
-
)
|
|
513
|
+
elif type_id in ION_TYPES:
|
|
514
|
+
pass
|
|
526
515
|
elif not freeze_active:
|
|
527
516
|
for _ in range(3):
|
|
528
517
|
spread = float(int(rand()) % 0x14 - 10) * 0.1
|
|
@@ -64,8 +64,8 @@ class PlayerState:
|
|
|
64
64
|
health: float = 100.0
|
|
65
65
|
size: float = 50.0
|
|
66
66
|
|
|
67
|
-
|
|
68
|
-
|
|
67
|
+
speed_multiplier: float = 2.0
|
|
68
|
+
move_speed: float = 0.0
|
|
69
69
|
move_phase: float = 0.0
|
|
70
70
|
heading: float = 0.0
|
|
71
71
|
death_timer: float = 16.0
|
|
@@ -159,8 +159,11 @@ class BonusHudSlot:
|
|
|
159
159
|
bonus_id: int = 0
|
|
160
160
|
label: str = ""
|
|
161
161
|
icon_id: int = -1
|
|
162
|
+
slide_x: float = -184.0
|
|
162
163
|
timer_ref: _TimerRef | None = None
|
|
163
164
|
timer_ref_alt: _TimerRef | None = None
|
|
165
|
+
timer_value: float = 0.0
|
|
166
|
+
timer_value_alt: float = 0.0
|
|
164
167
|
|
|
165
168
|
|
|
166
169
|
BONUS_HUD_SLOT_COUNT = 16
|
|
@@ -199,8 +202,11 @@ class BonusHudState:
|
|
|
199
202
|
slot.bonus_id = int(bonus_id)
|
|
200
203
|
slot.label = label
|
|
201
204
|
slot.icon_id = int(icon_id)
|
|
205
|
+
slot.slide_x = -184.0
|
|
202
206
|
slot.timer_ref = timer_ref
|
|
203
207
|
slot.timer_ref_alt = timer_ref_alt
|
|
208
|
+
slot.timer_value = 0.0
|
|
209
|
+
slot.timer_value_alt = 0.0
|
|
204
210
|
|
|
205
211
|
|
|
206
212
|
@dataclass(slots=True)
|
|
@@ -358,11 +364,49 @@ class BonusPool:
|
|
|
358
364
|
return None
|
|
359
365
|
|
|
360
366
|
rng = state.rng
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
367
|
+
# Native special-case: while any player has Pistol, 3/4 chance to force a Weapon drop.
|
|
368
|
+
if players and any(int(player.weapon_id) == int(WeaponId.PISTOL) for player in players):
|
|
369
|
+
if (int(rng.rand()) & 3) < 3:
|
|
370
|
+
entry = self.spawn_at_pos(
|
|
371
|
+
pos_x,
|
|
372
|
+
pos_y,
|
|
373
|
+
state=state,
|
|
374
|
+
players=players,
|
|
375
|
+
world_width=world_width,
|
|
376
|
+
world_height=world_height,
|
|
377
|
+
)
|
|
378
|
+
if entry is None:
|
|
379
|
+
return None
|
|
380
|
+
|
|
381
|
+
entry.bonus_id = int(BonusId.WEAPON)
|
|
382
|
+
weapon_id = int(weapon_pick_random_available(state))
|
|
383
|
+
entry.amount = int(weapon_id)
|
|
384
|
+
if weapon_id == int(WeaponId.PISTOL):
|
|
385
|
+
weapon_id = int(weapon_pick_random_available(state))
|
|
386
|
+
entry.amount = int(weapon_id)
|
|
387
|
+
|
|
388
|
+
matches = sum(1 for bonus in self._entries if bonus.bonus_id == entry.bonus_id)
|
|
389
|
+
if matches > 1:
|
|
390
|
+
self._clear_entry(entry)
|
|
391
|
+
return None
|
|
392
|
+
|
|
393
|
+
if entry.amount == int(WeaponId.PISTOL) or (players and perk_active(players[0], PerkId.MY_FAVOURITE_WEAPON)):
|
|
394
|
+
self._clear_entry(entry)
|
|
395
|
+
return None
|
|
396
|
+
|
|
397
|
+
return entry
|
|
398
|
+
|
|
399
|
+
base_roll = int(rng.rand())
|
|
400
|
+
if base_roll % 9 != 1:
|
|
401
|
+
allow_without_magnet = False
|
|
402
|
+
if players and int(players[0].weapon_id) == int(WeaponId.PISTOL):
|
|
403
|
+
allow_without_magnet = int(rng.rand()) % 5 == 1
|
|
404
|
+
|
|
405
|
+
if not allow_without_magnet:
|
|
406
|
+
if not (players and perk_active(players[0], PerkId.BONUS_MAGNET)):
|
|
407
|
+
return None
|
|
408
|
+
if int(rng.rand()) % 10 != 2:
|
|
409
|
+
return None
|
|
366
410
|
|
|
367
411
|
entry = self.spawn_at_pos(
|
|
368
412
|
pos_x,
|
|
@@ -377,11 +421,9 @@ class BonusPool:
|
|
|
377
421
|
|
|
378
422
|
if entry.bonus_id == int(BonusId.WEAPON):
|
|
379
423
|
near_sq = BONUS_WEAPON_NEAR_RADIUS * BONUS_WEAPON_NEAR_RADIUS
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
entry.amount = 100
|
|
384
|
-
break
|
|
424
|
+
if players and _distance_sq(pos_x, pos_y, players[0].pos_x, players[0].pos_y) < near_sq:
|
|
425
|
+
entry.bonus_id = int(BonusId.POINTS)
|
|
426
|
+
entry.amount = 100
|
|
385
427
|
|
|
386
428
|
if entry.bonus_id != int(BonusId.POINTS):
|
|
387
429
|
matches = sum(1 for bonus in self._entries if bonus.bonus_id == entry.bonus_id)
|
|
@@ -390,10 +432,9 @@ class BonusPool:
|
|
|
390
432
|
return None
|
|
391
433
|
|
|
392
434
|
if entry.bonus_id == int(BonusId.WEAPON):
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
return None
|
|
435
|
+
if players and entry.amount == players[0].weapon_id:
|
|
436
|
+
self._clear_entry(entry)
|
|
437
|
+
return None
|
|
397
438
|
|
|
398
439
|
return entry
|
|
399
440
|
|
|
@@ -1592,6 +1633,7 @@ def player_fire_weapon(player: PlayerState, input_state: PlayerInput, dt: float,
|
|
|
1592
1633
|
|
|
1593
1634
|
firing_during_reload = False
|
|
1594
1635
|
ammo_cost = 1.0
|
|
1636
|
+
is_fire_bullets = float(player.fire_bullets_timer) > 0.0
|
|
1595
1637
|
if player.reload_timer > 0.0:
|
|
1596
1638
|
if player.ammo <= 0 and player.experience > 0:
|
|
1597
1639
|
if perk_active(player, PerkId.REGRESSION_BULLETS):
|
|
@@ -1616,7 +1658,7 @@ def player_fire_weapon(player: PlayerState, input_state: PlayerInput, dt: float,
|
|
|
1616
1658
|
else:
|
|
1617
1659
|
return
|
|
1618
1660
|
|
|
1619
|
-
if player.ammo <= 0 and not firing_during_reload:
|
|
1661
|
+
if player.ammo <= 0 and not firing_during_reload and not is_fire_bullets:
|
|
1620
1662
|
player_start_reload(player, state)
|
|
1621
1663
|
return
|
|
1622
1664
|
|
|
@@ -1624,20 +1666,18 @@ def player_fire_weapon(player: PlayerState, input_state: PlayerInput, dt: float,
|
|
|
1624
1666
|
fire_bullets_weapon = weapon_entry_for_projectile_type_id(int(ProjectileTypeId.FIRE_BULLETS))
|
|
1625
1667
|
|
|
1626
1668
|
shot_cooldown = float(weapon.shot_cooldown) if weapon.shot_cooldown is not None else 0.0
|
|
1627
|
-
|
|
1628
|
-
|
|
1629
|
-
|
|
1669
|
+
spread_heat_base = float(weapon.spread_heat_inc) if weapon.spread_heat_inc is not None else 0.0
|
|
1670
|
+
if is_fire_bullets and pellet_count == 1 and fire_bullets_weapon is not None and fire_bullets_weapon.spread_heat_inc is not None:
|
|
1671
|
+
spread_heat_base = float(fire_bullets_weapon.spread_heat_inc)
|
|
1672
|
+
|
|
1673
|
+
if is_fire_bullets and pellet_count == 1 and fire_bullets_weapon is not None:
|
|
1630
1674
|
shot_cooldown = (
|
|
1631
1675
|
float(fire_bullets_weapon.shot_cooldown)
|
|
1632
1676
|
if fire_bullets_weapon.shot_cooldown is not None
|
|
1633
1677
|
else 0.0
|
|
1634
1678
|
)
|
|
1635
|
-
|
|
1636
|
-
|
|
1637
|
-
if fire_bullets_weapon.spread_heat_inc is not None
|
|
1638
|
-
else 0.0
|
|
1639
|
-
)
|
|
1640
|
-
spread_inc *= 1.3
|
|
1679
|
+
|
|
1680
|
+
spread_inc = spread_heat_base * 1.3
|
|
1641
1681
|
|
|
1642
1682
|
if perk_active(player, PerkId.FASTSHOT):
|
|
1643
1683
|
shot_cooldown *= 0.88
|
|
@@ -1657,6 +1697,7 @@ def player_fire_weapon(player: PlayerState, input_state: PlayerInput, dt: float,
|
|
|
1657
1697
|
aim_jitter_x = aim_x + math.cos(dir_angle) * offset
|
|
1658
1698
|
aim_jitter_y = aim_y + math.sin(dir_angle) * offset
|
|
1659
1699
|
shot_angle = math.atan2(aim_jitter_y - float(player.pos_y), aim_jitter_x - float(player.pos_x)) + math.pi / 2.0
|
|
1700
|
+
particle_angle = shot_angle - math.pi / 2.0
|
|
1660
1701
|
|
|
1661
1702
|
muzzle_x = player.pos_x + player.aim_dir_x * 16.0
|
|
1662
1703
|
muzzle_y = player.pos_y + player.aim_dir_y * 16.0
|
|
@@ -1676,14 +1717,12 @@ def player_fire_weapon(player: PlayerState, input_state: PlayerInput, dt: float,
|
|
|
1676
1717
|
return 0.0013
|
|
1677
1718
|
return 0.0015
|
|
1678
1719
|
|
|
1679
|
-
if
|
|
1720
|
+
if is_fire_bullets:
|
|
1680
1721
|
pellets = max(1, int(pellet_count))
|
|
1681
1722
|
shot_count = pellets
|
|
1682
1723
|
meta = _projectile_meta_for_type_id(ProjectileTypeId.FIRE_BULLETS)
|
|
1683
1724
|
for _ in range(pellets):
|
|
1684
|
-
angle = shot_angle
|
|
1685
|
-
if pellets > 1:
|
|
1686
|
-
angle += float(int(state.rng.rand()) % 200 - 100) * 0.0015
|
|
1725
|
+
angle = shot_angle + float(int(state.rng.rand()) % 200 - 100) * 0.0015
|
|
1687
1726
|
state.projectiles.spawn(
|
|
1688
1727
|
pos_x=muzzle_x,
|
|
1689
1728
|
pos_y=muzzle_y,
|
|
@@ -1713,16 +1752,16 @@ def player_fire_weapon(player: PlayerState, input_state: PlayerInput, dt: float,
|
|
|
1713
1752
|
state.secondary_projectiles.spawn(pos_x=muzzle_x, pos_y=muzzle_y, angle=shot_angle, type_id=4, owner_id=owner_id)
|
|
1714
1753
|
elif weapon_id == WeaponId.FLAMETHROWER:
|
|
1715
1754
|
# Flamethrower -> fast particle weapon (style 0), fractional ammo drain.
|
|
1716
|
-
state.particles.spawn_particle(pos_x=muzzle_x, pos_y=muzzle_y, angle=
|
|
1755
|
+
state.particles.spawn_particle(pos_x=muzzle_x, pos_y=muzzle_y, angle=particle_angle, intensity=1.0, owner_id=owner_id)
|
|
1717
1756
|
ammo_cost = 0.1
|
|
1718
1757
|
elif weapon_id == WeaponId.BLOW_TORCH:
|
|
1719
1758
|
# Blow Torch -> fast particle weapon (style 1), fractional ammo drain.
|
|
1720
|
-
particle_id = state.particles.spawn_particle(pos_x=muzzle_x, pos_y=muzzle_y, angle=
|
|
1759
|
+
particle_id = state.particles.spawn_particle(pos_x=muzzle_x, pos_y=muzzle_y, angle=particle_angle, intensity=1.0, owner_id=owner_id)
|
|
1721
1760
|
state.particles.entries[particle_id].style_id = 1
|
|
1722
1761
|
ammo_cost = 0.05
|
|
1723
1762
|
elif weapon_id == WeaponId.HR_FLAMER:
|
|
1724
1763
|
# HR Flamer -> fast particle weapon (style 2), fractional ammo drain.
|
|
1725
|
-
particle_id = state.particles.spawn_particle(pos_x=muzzle_x, pos_y=muzzle_y, angle=
|
|
1764
|
+
particle_id = state.particles.spawn_particle(pos_x=muzzle_x, pos_y=muzzle_y, angle=particle_angle, intensity=1.0, owner_id=owner_id)
|
|
1726
1765
|
state.particles.entries[particle_id].style_id = 2
|
|
1727
1766
|
ammo_cost = 0.1
|
|
1728
1767
|
elif weapon_id == WeaponId.BUBBLEGUN:
|
|
@@ -1833,15 +1872,17 @@ def player_fire_weapon(player: PlayerState, input_state: PlayerInput, dt: float,
|
|
|
1833
1872
|
player.spread_heat = min(0.48, max(0.0, player.spread_heat + spread_inc))
|
|
1834
1873
|
|
|
1835
1874
|
muzzle_inc = float(weapon.spread_heat_inc) if weapon.spread_heat_inc is not None else 0.0
|
|
1875
|
+
if is_fire_bullets and pellet_count == 1 and fire_bullets_weapon is not None and fire_bullets_weapon.spread_heat_inc is not None:
|
|
1876
|
+
muzzle_inc = float(fire_bullets_weapon.spread_heat_inc)
|
|
1836
1877
|
player.muzzle_flash_alpha = min(1.0, player.muzzle_flash_alpha)
|
|
1837
1878
|
player.muzzle_flash_alpha = min(1.0, player.muzzle_flash_alpha + muzzle_inc)
|
|
1838
1879
|
player.muzzle_flash_alpha = min(0.8, player.muzzle_flash_alpha)
|
|
1839
1880
|
|
|
1840
1881
|
player.shot_seq += 1
|
|
1841
|
-
if not firing_during_reload and state.bonuses.reflex_boost <= 0.0:
|
|
1882
|
+
if (not firing_during_reload) and state.bonuses.reflex_boost <= 0.0 and not is_fire_bullets:
|
|
1842
1883
|
player.ammo = max(0.0, float(player.ammo) - float(ammo_cost))
|
|
1843
|
-
|
|
1844
|
-
|
|
1884
|
+
if (not firing_during_reload) and player.ammo <= 0.0 and player.reload_timer <= 0.0:
|
|
1885
|
+
player_start_reload(player, state)
|
|
1845
1886
|
|
|
1846
1887
|
|
|
1847
1888
|
def player_update(player: PlayerState, input_state: PlayerInput, dt: float, state: GameplayState, *, world_size: float = 1024.0) -> None:
|
|
@@ -1885,35 +1926,50 @@ def player_update(player: PlayerState, input_state: PlayerInput, dt: float, stat
|
|
|
1885
1926
|
player.aim_heading = math.atan2(aim_dir_y, aim_dir_x) + math.pi / 2.0
|
|
1886
1927
|
|
|
1887
1928
|
# Movement.
|
|
1888
|
-
|
|
1889
|
-
|
|
1890
|
-
|
|
1891
|
-
|
|
1892
|
-
|
|
1893
|
-
|
|
1929
|
+
raw_move_x = float(input_state.move_x)
|
|
1930
|
+
raw_move_y = float(input_state.move_y)
|
|
1931
|
+
raw_mag = math.hypot(raw_move_x, raw_move_y)
|
|
1932
|
+
moving_input = raw_mag > 0.2
|
|
1933
|
+
|
|
1934
|
+
if moving_input:
|
|
1935
|
+
inv = 1.0 / raw_mag if raw_mag > 1e-9 else 0.0
|
|
1936
|
+
move_x = raw_move_x * inv
|
|
1937
|
+
move_y = raw_move_y * inv
|
|
1938
|
+
player.heading = math.atan2(move_y, move_x) + math.pi / 2.0
|
|
1939
|
+
if perk_active(player, PerkId.LONG_DISTANCE_RUNNER):
|
|
1940
|
+
if player.move_speed < 2.0:
|
|
1941
|
+
player.move_speed = float(player.move_speed + dt * 4.0)
|
|
1942
|
+
player.move_speed = float(player.move_speed + dt)
|
|
1943
|
+
if player.move_speed > 2.8:
|
|
1944
|
+
player.move_speed = 2.8
|
|
1894
1945
|
else:
|
|
1895
|
-
player.
|
|
1896
|
-
|
|
1946
|
+
player.move_speed = float(player.move_speed + dt * 5.0)
|
|
1947
|
+
if player.move_speed > 2.0:
|
|
1948
|
+
player.move_speed = 2.0
|
|
1897
1949
|
else:
|
|
1898
|
-
player.
|
|
1950
|
+
player.move_speed = float(player.move_speed - dt * 15.0)
|
|
1951
|
+
if player.move_speed < 0.0:
|
|
1952
|
+
player.move_speed = 0.0
|
|
1953
|
+
move_x = math.cos(player.heading - math.pi / 2.0)
|
|
1954
|
+
move_y = math.sin(player.heading - math.pi / 2.0)
|
|
1899
1955
|
|
|
1900
|
-
|
|
1956
|
+
if player.weapon_id == WeaponId.MEAN_MINIGUN and player.move_speed > 0.8:
|
|
1957
|
+
player.move_speed = 0.8
|
|
1958
|
+
|
|
1959
|
+
speed_multiplier = float(player.speed_multiplier)
|
|
1901
1960
|
if player.speed_bonus_timer > 0.0:
|
|
1902
1961
|
speed_multiplier += 1.0
|
|
1903
|
-
|
|
1962
|
+
|
|
1963
|
+
speed = player.move_speed * speed_multiplier * 25.0
|
|
1964
|
+
if moving_input:
|
|
1965
|
+
speed *= min(1.0, raw_mag)
|
|
1904
1966
|
if perk_active(player, PerkId.ALTERNATE_WEAPON):
|
|
1905
1967
|
speed *= 0.8
|
|
1968
|
+
|
|
1906
1969
|
player.pos_x = _clamp(player.pos_x + move_x * speed * dt, 0.0, float(world_size))
|
|
1907
1970
|
player.pos_y = _clamp(player.pos_y + move_y * speed * dt, 0.0, float(world_size))
|
|
1908
1971
|
|
|
1909
|
-
|
|
1910
|
-
player.heading = math.atan2(move_y, move_x) + math.pi / 2.0
|
|
1911
|
-
|
|
1912
|
-
move_dist = math.hypot(player.pos_x - prev_x, player.pos_y - prev_y)
|
|
1913
|
-
if move_dist > 1e-9:
|
|
1914
|
-
# Port of `move_phase += frame_dt * move_speed * 19.0` (player_update).
|
|
1915
|
-
move_speed = move_dist / dt / 120.0
|
|
1916
|
-
player.move_phase += dt * move_speed * 19.0
|
|
1972
|
+
player.move_phase += dt * player.move_speed * 19.0
|
|
1917
1973
|
|
|
1918
1974
|
stationary = abs(player.pos_x - prev_x) <= 1e-9 and abs(player.pos_y - prev_y) <= 1e-9
|
|
1919
1975
|
reload_scale = 1.0
|
|
@@ -2306,8 +2362,8 @@ def bonus_apply(
|
|
|
2306
2362
|
return
|
|
2307
2363
|
|
|
2308
2364
|
|
|
2309
|
-
def bonus_hud_update(state: GameplayState, players: list[PlayerState]) -> None:
|
|
2310
|
-
"""Refresh HUD slots based on current timer values."""
|
|
2365
|
+
def bonus_hud_update(state: GameplayState, players: list[PlayerState], *, dt: float = 0.0) -> None:
|
|
2366
|
+
"""Refresh HUD slots based on current timer values + advance slide animation."""
|
|
2311
2367
|
|
|
2312
2368
|
def _timer_value(ref: _TimerRef | None) -> float:
|
|
2313
2369
|
if ref is None:
|
|
@@ -2321,16 +2377,35 @@ def bonus_hud_update(state: GameplayState, players: list[PlayerState]) -> None:
|
|
|
2321
2377
|
return float(getattr(players[idx], ref.key, 0.0) or 0.0)
|
|
2322
2378
|
return 0.0
|
|
2323
2379
|
|
|
2324
|
-
|
|
2380
|
+
player_count = len(players)
|
|
2381
|
+
dt = max(0.0, float(dt))
|
|
2382
|
+
|
|
2383
|
+
for slot_index, slot in enumerate(state.bonus_hud.slots):
|
|
2325
2384
|
if not slot.active:
|
|
2326
2385
|
continue
|
|
2327
|
-
timer = _timer_value(slot.timer_ref)
|
|
2328
|
-
if slot.timer_ref_alt is not None
|
|
2329
|
-
|
|
2330
|
-
|
|
2386
|
+
timer = max(0.0, _timer_value(slot.timer_ref))
|
|
2387
|
+
timer_alt = max(0.0, _timer_value(slot.timer_ref_alt)) if (slot.timer_ref_alt is not None and player_count > 1) else 0.0
|
|
2388
|
+
slot.timer_value = float(timer)
|
|
2389
|
+
slot.timer_value_alt = float(timer_alt)
|
|
2390
|
+
|
|
2391
|
+
if timer > 0.0 or timer_alt > 0.0:
|
|
2392
|
+
slot.slide_x += dt * 350.0
|
|
2393
|
+
else:
|
|
2394
|
+
slot.slide_x -= dt * 320.0
|
|
2395
|
+
|
|
2396
|
+
if slot.slide_x > -2.0:
|
|
2397
|
+
slot.slide_x = -2.0
|
|
2398
|
+
|
|
2399
|
+
if slot.slide_x < -184.0 and not any(other.active for other in state.bonus_hud.slots[slot_index + 1 :]):
|
|
2331
2400
|
slot.active = False
|
|
2401
|
+
slot.bonus_id = 0
|
|
2402
|
+
slot.label = ""
|
|
2403
|
+
slot.icon_id = -1
|
|
2404
|
+
slot.slide_x = -184.0
|
|
2332
2405
|
slot.timer_ref = None
|
|
2333
2406
|
slot.timer_ref_alt = None
|
|
2407
|
+
slot.timer_value = 0.0
|
|
2408
|
+
slot.timer_value_alt = 0.0
|
|
2334
2409
|
|
|
2335
2410
|
|
|
2336
2411
|
def bonus_telekinetic_update(
|
|
@@ -2445,6 +2520,6 @@ def bonus_update(
|
|
|
2445
2520
|
state.bonuses.freeze = max(0.0, state.bonuses.freeze - dt)
|
|
2446
2521
|
|
|
2447
2522
|
if update_hud:
|
|
2448
|
-
bonus_hud_update(state, players)
|
|
2523
|
+
bonus_hud_update(state, players, dt=dt)
|
|
2449
2524
|
|
|
2450
2525
|
return pickups
|
|
@@ -8,14 +8,17 @@ import pyray as rl
|
|
|
8
8
|
|
|
9
9
|
from grim.assets import PaqTextureCache
|
|
10
10
|
from grim.audio import AudioState, update_audio
|
|
11
|
+
from grim.console import ConsoleState
|
|
11
12
|
from grim.config import CrimsonConfig
|
|
12
13
|
from grim.fonts.small import SmallFontData, draw_small_text, load_small_font, measure_small_text_width
|
|
13
14
|
from grim.view import ViewContext
|
|
14
15
|
|
|
16
|
+
from ..gameplay import _creature_find_in_radius, perk_count_get
|
|
15
17
|
from ..game_world import GameWorld
|
|
16
18
|
from ..persistence.highscores import HighScoreRecord
|
|
19
|
+
from ..perks import PerkId
|
|
17
20
|
from ..ui.game_over import GameOverUi
|
|
18
|
-
from ..ui.hud import HudAssets, load_hud_assets
|
|
21
|
+
from ..ui.hud import HudAssets, draw_target_health_bar, load_hud_assets
|
|
19
22
|
|
|
20
23
|
if TYPE_CHECKING:
|
|
21
24
|
from ..persistence.save_status import GameStatus
|
|
@@ -45,6 +48,7 @@ class BaseGameplayMode:
|
|
|
45
48
|
hardcore: bool = False,
|
|
46
49
|
texture_cache: PaqTextureCache | None = None,
|
|
47
50
|
config: CrimsonConfig | None = None,
|
|
51
|
+
console: ConsoleState | None = None,
|
|
48
52
|
audio: AudioState | None = None,
|
|
49
53
|
audio_rng: random.Random | None = None,
|
|
50
54
|
) -> None:
|
|
@@ -54,7 +58,9 @@ class BaseGameplayMode:
|
|
|
54
58
|
self._small: SmallFontData | None = None
|
|
55
59
|
self._hud_assets: HudAssets | None = None
|
|
56
60
|
|
|
61
|
+
self._default_game_mode_id = int(default_game_mode_id)
|
|
57
62
|
self._config = config
|
|
63
|
+
self._console = console
|
|
58
64
|
self._base_dir = config.path.parent if config is not None else Path.cwd()
|
|
59
65
|
|
|
60
66
|
self.close_requested = False
|
|
@@ -90,6 +96,74 @@ class BaseGameplayMode:
|
|
|
90
96
|
self._last_dt_ms = 0.0
|
|
91
97
|
self._screen_fade: _ScreenFade | None = None
|
|
92
98
|
|
|
99
|
+
def _cvar_float(self, name: str, default: float = 0.0) -> float:
|
|
100
|
+
console = self._console
|
|
101
|
+
if console is None:
|
|
102
|
+
return float(default)
|
|
103
|
+
cvar = console.cvars.get(name)
|
|
104
|
+
if cvar is None:
|
|
105
|
+
return float(default)
|
|
106
|
+
return float(cvar.value_f)
|
|
107
|
+
|
|
108
|
+
def _hud_small_indicators(self) -> bool:
|
|
109
|
+
return self._cvar_float("cv_uiSmallIndicators", 0.0) != 0.0
|
|
110
|
+
|
|
111
|
+
def _config_game_mode_id(self) -> int:
|
|
112
|
+
config = self._config
|
|
113
|
+
if config is None:
|
|
114
|
+
return int(self._default_game_mode_id)
|
|
115
|
+
try:
|
|
116
|
+
value = config.data.get("game_mode", self._default_game_mode_id)
|
|
117
|
+
return int(value or self._default_game_mode_id)
|
|
118
|
+
except Exception:
|
|
119
|
+
return int(self._default_game_mode_id)
|
|
120
|
+
|
|
121
|
+
def _draw_target_health_bar(self, *, alpha: float = 1.0) -> None:
|
|
122
|
+
creatures = getattr(self._creatures, "entries", [])
|
|
123
|
+
if not creatures:
|
|
124
|
+
return
|
|
125
|
+
|
|
126
|
+
if perk_count_get(self._player, PerkId.DOCTOR) <= 0:
|
|
127
|
+
return
|
|
128
|
+
|
|
129
|
+
target_idx = _creature_find_in_radius(
|
|
130
|
+
creatures,
|
|
131
|
+
pos_x=float(getattr(self._player, "aim_x", 0.0)),
|
|
132
|
+
pos_y=float(getattr(self._player, "aim_y", 0.0)),
|
|
133
|
+
radius=12.0,
|
|
134
|
+
start_index=0,
|
|
135
|
+
)
|
|
136
|
+
if target_idx == -1:
|
|
137
|
+
return
|
|
138
|
+
|
|
139
|
+
creature = creatures[target_idx]
|
|
140
|
+
if not bool(getattr(creature, "active", False)):
|
|
141
|
+
return
|
|
142
|
+
hp = float(getattr(creature, "hp", 0.0))
|
|
143
|
+
max_hp = float(getattr(creature, "max_hp", 0.0))
|
|
144
|
+
if hp <= 0.0 or max_hp <= 0.0:
|
|
145
|
+
return
|
|
146
|
+
|
|
147
|
+
ratio = hp / max_hp
|
|
148
|
+
if ratio < 0.0:
|
|
149
|
+
ratio = 0.0
|
|
150
|
+
if ratio > 1.0:
|
|
151
|
+
ratio = 1.0
|
|
152
|
+
|
|
153
|
+
x0, y0 = self._world.world_to_screen(float(creature.x) - 32.0, float(creature.y) + 32.0)
|
|
154
|
+
x1, _y1 = self._world.world_to_screen(float(creature.x) + 32.0, float(creature.y) + 32.0)
|
|
155
|
+
width = float(x1) - float(x0)
|
|
156
|
+
if width <= 1e-3:
|
|
157
|
+
return
|
|
158
|
+
draw_target_health_bar(
|
|
159
|
+
x=float(x0),
|
|
160
|
+
y=float(y0),
|
|
161
|
+
width=width,
|
|
162
|
+
ratio=ratio,
|
|
163
|
+
alpha=float(alpha),
|
|
164
|
+
scale=width / 64.0,
|
|
165
|
+
)
|
|
166
|
+
|
|
93
167
|
def _bind_world(self) -> None:
|
|
94
168
|
self._state = self._world.state
|
|
95
169
|
self._creatures = self._world.creatures
|