crimsonland 0.1.0.dev7__tar.gz → 0.1.0.dev10__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.dev7 → crimsonland-0.1.0.dev10}/PKG-INFO +1 -1
- {crimsonland-0.1.0.dev7 → crimsonland-0.1.0.dev10}/pyproject.toml +1 -1
- {crimsonland-0.1.0.dev7 → crimsonland-0.1.0.dev10}/src/crimson/creatures/anim.py +1 -0
- {crimsonland-0.1.0.dev7 → crimsonland-0.1.0.dev10}/src/crimson/demo.py +77 -35
- {crimsonland-0.1.0.dev7 → crimsonland-0.1.0.dev10}/src/crimson/game.py +5 -0
- {crimsonland-0.1.0.dev7 → crimsonland-0.1.0.dev10}/src/crimson/gameplay.py +38 -11
- {crimsonland-0.1.0.dev7 → crimsonland-0.1.0.dev10}/src/crimson/modes/base_gameplay_mode.py +75 -1
- crimsonland-0.1.0.dev10/src/crimson/modes/quest_mode.py +929 -0
- {crimsonland-0.1.0.dev7 → crimsonland-0.1.0.dev10}/src/crimson/modes/rush_mode.py +12 -3
- {crimsonland-0.1.0.dev7 → crimsonland-0.1.0.dev10}/src/crimson/modes/survival_mode.py +13 -2
- {crimsonland-0.1.0.dev7 → crimsonland-0.1.0.dev10}/src/crimson/modes/tutorial_mode.py +12 -1
- {crimsonland-0.1.0.dev7 → crimsonland-0.1.0.dev10}/src/crimson/modes/typo_mode.py +12 -4
- {crimsonland-0.1.0.dev7 → crimsonland-0.1.0.dev10}/src/crimson/render/world_renderer.py +12 -16
- {crimsonland-0.1.0.dev7 → crimsonland-0.1.0.dev10}/src/crimson/ui/hud.py +274 -51
- {crimsonland-0.1.0.dev7 → crimsonland-0.1.0.dev10}/src/crimson/views/arsenal_debug.py +46 -1
- {crimsonland-0.1.0.dev7 → crimsonland-0.1.0.dev10}/src/crimson/views/player.py +2 -2
- {crimsonland-0.1.0.dev7 → crimsonland-0.1.0.dev10}/src/grim/console.py +14 -0
- crimsonland-0.1.0.dev7/src/crimson/modes/quest_mode.py +0 -502
- {crimsonland-0.1.0.dev7 → crimsonland-0.1.0.dev10}/src/crimson/__init__.py +0 -0
- {crimsonland-0.1.0.dev7 → crimsonland-0.1.0.dev10}/src/crimson/assets_fetch.py +0 -0
- {crimsonland-0.1.0.dev7 → crimsonland-0.1.0.dev10}/src/crimson/atlas.py +0 -0
- {crimsonland-0.1.0.dev7 → crimsonland-0.1.0.dev10}/src/crimson/audio_router.py +0 -0
- {crimsonland-0.1.0.dev7 → crimsonland-0.1.0.dev10}/src/crimson/bonuses.py +0 -0
- {crimsonland-0.1.0.dev7 → crimsonland-0.1.0.dev10}/src/crimson/camera.py +0 -0
- {crimsonland-0.1.0.dev7 → crimsonland-0.1.0.dev10}/src/crimson/cli.py +0 -0
- {crimsonland-0.1.0.dev7 → crimsonland-0.1.0.dev10}/src/crimson/creatures/__init__.py +0 -0
- {crimsonland-0.1.0.dev7 → crimsonland-0.1.0.dev10}/src/crimson/creatures/ai.py +0 -0
- {crimsonland-0.1.0.dev7 → crimsonland-0.1.0.dev10}/src/crimson/creatures/damage.py +0 -0
- {crimsonland-0.1.0.dev7 → crimsonland-0.1.0.dev10}/src/crimson/creatures/runtime.py +0 -0
- {crimsonland-0.1.0.dev7 → crimsonland-0.1.0.dev10}/src/crimson/creatures/spawn.py +0 -0
- {crimsonland-0.1.0.dev7 → crimsonland-0.1.0.dev10}/src/crimson/debug.py +0 -0
- {crimsonland-0.1.0.dev7 → crimsonland-0.1.0.dev10}/src/crimson/demo_trial.py +0 -0
- {crimsonland-0.1.0.dev7 → crimsonland-0.1.0.dev10}/src/crimson/effects.py +0 -0
- {crimsonland-0.1.0.dev7 → crimsonland-0.1.0.dev10}/src/crimson/effects_atlas.py +0 -0
- {crimsonland-0.1.0.dev7 → crimsonland-0.1.0.dev10}/src/crimson/frontend/__init__.py +0 -0
- {crimsonland-0.1.0.dev7 → crimsonland-0.1.0.dev10}/src/crimson/frontend/assets.py +0 -0
- {crimsonland-0.1.0.dev7 → crimsonland-0.1.0.dev10}/src/crimson/frontend/boot.py +0 -0
- {crimsonland-0.1.0.dev7 → crimsonland-0.1.0.dev10}/src/crimson/frontend/menu.py +0 -0
- {crimsonland-0.1.0.dev7 → crimsonland-0.1.0.dev10}/src/crimson/frontend/panels/__init__.py +0 -0
- {crimsonland-0.1.0.dev7 → crimsonland-0.1.0.dev10}/src/crimson/frontend/panels/base.py +0 -0
- {crimsonland-0.1.0.dev7 → crimsonland-0.1.0.dev10}/src/crimson/frontend/panels/controls.py +0 -0
- {crimsonland-0.1.0.dev7 → crimsonland-0.1.0.dev10}/src/crimson/frontend/panels/mods.py +0 -0
- {crimsonland-0.1.0.dev7 → crimsonland-0.1.0.dev10}/src/crimson/frontend/panels/options.py +0 -0
- {crimsonland-0.1.0.dev7 → crimsonland-0.1.0.dev10}/src/crimson/frontend/panels/play_game.py +0 -0
- {crimsonland-0.1.0.dev7 → crimsonland-0.1.0.dev10}/src/crimson/frontend/panels/stats.py +0 -0
- {crimsonland-0.1.0.dev7 → crimsonland-0.1.0.dev10}/src/crimson/frontend/transitions.py +0 -0
- {crimsonland-0.1.0.dev7 → crimsonland-0.1.0.dev10}/src/crimson/game_modes.py +0 -0
- {crimsonland-0.1.0.dev7 → crimsonland-0.1.0.dev10}/src/crimson/game_world.py +0 -0
- {crimsonland-0.1.0.dev7 → crimsonland-0.1.0.dev10}/src/crimson/input_codes.py +0 -0
- {crimsonland-0.1.0.dev7 → crimsonland-0.1.0.dev10}/src/crimson/modes/__init__.py +0 -0
- {crimsonland-0.1.0.dev7 → crimsonland-0.1.0.dev10}/src/crimson/paths.py +0 -0
- {crimsonland-0.1.0.dev7 → crimsonland-0.1.0.dev10}/src/crimson/perks.py +0 -0
- {crimsonland-0.1.0.dev7 → crimsonland-0.1.0.dev10}/src/crimson/persistence/__init__.py +0 -0
- {crimsonland-0.1.0.dev7 → crimsonland-0.1.0.dev10}/src/crimson/persistence/highscores.py +0 -0
- {crimsonland-0.1.0.dev7 → crimsonland-0.1.0.dev10}/src/crimson/persistence/save_status.py +0 -0
- {crimsonland-0.1.0.dev7 → crimsonland-0.1.0.dev10}/src/crimson/player_damage.py +0 -0
- {crimsonland-0.1.0.dev7 → crimsonland-0.1.0.dev10}/src/crimson/projectiles.py +0 -0
- {crimsonland-0.1.0.dev7 → crimsonland-0.1.0.dev10}/src/crimson/quests/__init__.py +0 -0
- {crimsonland-0.1.0.dev7 → crimsonland-0.1.0.dev10}/src/crimson/quests/helpers.py +0 -0
- {crimsonland-0.1.0.dev7 → crimsonland-0.1.0.dev10}/src/crimson/quests/registry.py +0 -0
- {crimsonland-0.1.0.dev7 → crimsonland-0.1.0.dev10}/src/crimson/quests/results.py +0 -0
- {crimsonland-0.1.0.dev7 → crimsonland-0.1.0.dev10}/src/crimson/quests/runtime.py +0 -0
- {crimsonland-0.1.0.dev7 → crimsonland-0.1.0.dev10}/src/crimson/quests/tier1.py +0 -0
- {crimsonland-0.1.0.dev7 → crimsonland-0.1.0.dev10}/src/crimson/quests/tier2.py +0 -0
- {crimsonland-0.1.0.dev7 → crimsonland-0.1.0.dev10}/src/crimson/quests/tier3.py +0 -0
- {crimsonland-0.1.0.dev7 → crimsonland-0.1.0.dev10}/src/crimson/quests/tier4.py +0 -0
- {crimsonland-0.1.0.dev7 → crimsonland-0.1.0.dev10}/src/crimson/quests/tier5.py +0 -0
- {crimsonland-0.1.0.dev7 → crimsonland-0.1.0.dev10}/src/crimson/quests/timeline.py +0 -0
- {crimsonland-0.1.0.dev7 → crimsonland-0.1.0.dev10}/src/crimson/quests/types.py +0 -0
- {crimsonland-0.1.0.dev7 → crimsonland-0.1.0.dev10}/src/crimson/render/__init__.py +0 -0
- {crimsonland-0.1.0.dev7 → crimsonland-0.1.0.dev10}/src/crimson/render/terrain_fx.py +0 -0
- {crimsonland-0.1.0.dev7 → crimsonland-0.1.0.dev10}/src/crimson/sim/__init__.py +0 -0
- {crimsonland-0.1.0.dev7 → crimsonland-0.1.0.dev10}/src/crimson/sim/world_defs.py +0 -0
- {crimsonland-0.1.0.dev7 → crimsonland-0.1.0.dev10}/src/crimson/sim/world_state.py +0 -0
- {crimsonland-0.1.0.dev7 → crimsonland-0.1.0.dev10}/src/crimson/terrain_assets.py +0 -0
- {crimsonland-0.1.0.dev7 → crimsonland-0.1.0.dev10}/src/crimson/tutorial/__init__.py +0 -0
- {crimsonland-0.1.0.dev7 → crimsonland-0.1.0.dev10}/src/crimson/tutorial/timeline.py +0 -0
- {crimsonland-0.1.0.dev7 → crimsonland-0.1.0.dev10}/src/crimson/typo/__init__.py +0 -0
- {crimsonland-0.1.0.dev7 → crimsonland-0.1.0.dev10}/src/crimson/typo/names.py +0 -0
- {crimsonland-0.1.0.dev7 → crimsonland-0.1.0.dev10}/src/crimson/typo/player.py +0 -0
- {crimsonland-0.1.0.dev7 → crimsonland-0.1.0.dev10}/src/crimson/typo/spawns.py +0 -0
- {crimsonland-0.1.0.dev7 → crimsonland-0.1.0.dev10}/src/crimson/typo/typing.py +0 -0
- {crimsonland-0.1.0.dev7 → crimsonland-0.1.0.dev10}/src/crimson/ui/__init__.py +0 -0
- {crimsonland-0.1.0.dev7 → crimsonland-0.1.0.dev10}/src/crimson/ui/cursor.py +0 -0
- {crimsonland-0.1.0.dev7 → crimsonland-0.1.0.dev10}/src/crimson/ui/demo_trial_overlay.py +0 -0
- {crimsonland-0.1.0.dev7 → crimsonland-0.1.0.dev10}/src/crimson/ui/game_over.py +0 -0
- {crimsonland-0.1.0.dev7 → crimsonland-0.1.0.dev10}/src/crimson/ui/perk_menu.py +0 -0
- {crimsonland-0.1.0.dev7 → crimsonland-0.1.0.dev10}/src/crimson/views/__init__.py +0 -0
- {crimsonland-0.1.0.dev7 → crimsonland-0.1.0.dev10}/src/crimson/views/aim_debug.py +0 -0
- {crimsonland-0.1.0.dev7 → crimsonland-0.1.0.dev10}/src/crimson/views/animations.py +0 -0
- {crimsonland-0.1.0.dev7 → crimsonland-0.1.0.dev10}/src/crimson/views/audio_bootstrap.py +0 -0
- {crimsonland-0.1.0.dev7 → crimsonland-0.1.0.dev10}/src/crimson/views/bonuses.py +0 -0
- {crimsonland-0.1.0.dev7 → crimsonland-0.1.0.dev10}/src/crimson/views/camera_debug.py +0 -0
- {crimsonland-0.1.0.dev7 → crimsonland-0.1.0.dev10}/src/crimson/views/camera_shake.py +0 -0
- {crimsonland-0.1.0.dev7 → crimsonland-0.1.0.dev10}/src/crimson/views/corpse_stamp_debug.py +0 -0
- {crimsonland-0.1.0.dev7 → crimsonland-0.1.0.dev10}/src/crimson/views/decals_debug.py +0 -0
- {crimsonland-0.1.0.dev7 → crimsonland-0.1.0.dev10}/src/crimson/views/empty.py +0 -0
- {crimsonland-0.1.0.dev7 → crimsonland-0.1.0.dev10}/src/crimson/views/fonts.py +0 -0
- {crimsonland-0.1.0.dev7 → crimsonland-0.1.0.dev10}/src/crimson/views/game_over.py +0 -0
- {crimsonland-0.1.0.dev7 → crimsonland-0.1.0.dev10}/src/crimson/views/ground.py +0 -0
- {crimsonland-0.1.0.dev7 → crimsonland-0.1.0.dev10}/src/crimson/views/lighting_debug.py +0 -0
- {crimsonland-0.1.0.dev7 → crimsonland-0.1.0.dev10}/src/crimson/views/particles.py +0 -0
- {crimsonland-0.1.0.dev7 → crimsonland-0.1.0.dev10}/src/crimson/views/perk_menu_debug.py +0 -0
- {crimsonland-0.1.0.dev7 → crimsonland-0.1.0.dev10}/src/crimson/views/perks.py +0 -0
- {crimsonland-0.1.0.dev7 → crimsonland-0.1.0.dev10}/src/crimson/views/player_sprite_debug.py +0 -0
- {crimsonland-0.1.0.dev7 → crimsonland-0.1.0.dev10}/src/crimson/views/projectile_fx.py +0 -0
- {crimsonland-0.1.0.dev7 → crimsonland-0.1.0.dev10}/src/crimson/views/projectile_render_debug.py +0 -0
- {crimsonland-0.1.0.dev7 → crimsonland-0.1.0.dev10}/src/crimson/views/projectiles.py +0 -0
- {crimsonland-0.1.0.dev7 → crimsonland-0.1.0.dev10}/src/crimson/views/quest_title_overlay.py +0 -0
- {crimsonland-0.1.0.dev7 → crimsonland-0.1.0.dev10}/src/crimson/views/registry.py +0 -0
- {crimsonland-0.1.0.dev7 → crimsonland-0.1.0.dev10}/src/crimson/views/rush.py +0 -0
- {crimsonland-0.1.0.dev7 → crimsonland-0.1.0.dev10}/src/crimson/views/small_font_debug.py +0 -0
- {crimsonland-0.1.0.dev7 → crimsonland-0.1.0.dev10}/src/crimson/views/spawn_plan.py +0 -0
- {crimsonland-0.1.0.dev7 → crimsonland-0.1.0.dev10}/src/crimson/views/sprites.py +0 -0
- {crimsonland-0.1.0.dev7 → crimsonland-0.1.0.dev10}/src/crimson/views/survival.py +0 -0
- {crimsonland-0.1.0.dev7 → crimsonland-0.1.0.dev10}/src/crimson/views/terrain.py +0 -0
- {crimsonland-0.1.0.dev7 → crimsonland-0.1.0.dev10}/src/crimson/views/ui.py +0 -0
- {crimsonland-0.1.0.dev7 → crimsonland-0.1.0.dev10}/src/crimson/views/wicons.py +0 -0
- {crimsonland-0.1.0.dev7 → crimsonland-0.1.0.dev10}/src/crimson/weapon_sfx.py +0 -0
- {crimsonland-0.1.0.dev7 → crimsonland-0.1.0.dev10}/src/crimson/weapons.py +0 -0
- {crimsonland-0.1.0.dev7 → crimsonland-0.1.0.dev10}/src/grim/__init__.py +0 -0
- {crimsonland-0.1.0.dev7 → crimsonland-0.1.0.dev10}/src/grim/app.py +0 -0
- {crimsonland-0.1.0.dev7 → crimsonland-0.1.0.dev10}/src/grim/assets.py +0 -0
- {crimsonland-0.1.0.dev7 → crimsonland-0.1.0.dev10}/src/grim/audio.py +0 -0
- {crimsonland-0.1.0.dev7 → crimsonland-0.1.0.dev10}/src/grim/config.py +0 -0
- {crimsonland-0.1.0.dev7 → crimsonland-0.1.0.dev10}/src/grim/fonts/__init__.py +0 -0
- {crimsonland-0.1.0.dev7 → crimsonland-0.1.0.dev10}/src/grim/fonts/grim_mono.py +0 -0
- {crimsonland-0.1.0.dev7 → crimsonland-0.1.0.dev10}/src/grim/fonts/small.py +0 -0
- {crimsonland-0.1.0.dev7 → crimsonland-0.1.0.dev10}/src/grim/input.py +0 -0
- {crimsonland-0.1.0.dev7 → crimsonland-0.1.0.dev10}/src/grim/jaz.py +0 -0
- {crimsonland-0.1.0.dev7 → crimsonland-0.1.0.dev10}/src/grim/math.py +0 -0
- {crimsonland-0.1.0.dev7 → crimsonland-0.1.0.dev10}/src/grim/music.py +0 -0
- {crimsonland-0.1.0.dev7 → crimsonland-0.1.0.dev10}/src/grim/paq.py +0 -0
- {crimsonland-0.1.0.dev7 → crimsonland-0.1.0.dev10}/src/grim/rand.py +0 -0
- {crimsonland-0.1.0.dev7 → crimsonland-0.1.0.dev10}/src/grim/sfx.py +0 -0
- {crimsonland-0.1.0.dev7 → crimsonland-0.1.0.dev10}/src/grim/sfx_map.py +0 -0
- {crimsonland-0.1.0.dev7 → crimsonland-0.1.0.dev10}/src/grim/terrain_render.py +0 -0
- {crimsonland-0.1.0.dev7 → crimsonland-0.1.0.dev10}/src/grim/view.py +0 -0
|
@@ -475,6 +475,9 @@ class DemoView:
|
|
|
475
475
|
player = self._world.players[idx]
|
|
476
476
|
player.pos_x = float(x)
|
|
477
477
|
player.pos_y = float(y)
|
|
478
|
+
# Keep aim anchored to the spawn position so demo aim starts stable.
|
|
479
|
+
player.aim_x = float(x)
|
|
480
|
+
player.aim_y = float(y)
|
|
478
481
|
weapon_assign_player(player, int(weapon_id))
|
|
479
482
|
self._demo_targets = [None] * len(self._world.players)
|
|
480
483
|
|
|
@@ -549,7 +552,8 @@ class DemoView:
|
|
|
549
552
|
|
|
550
553
|
def _setup_variant_0(self) -> None:
|
|
551
554
|
self._demo_time_limit_ms = 4000
|
|
552
|
-
weapon_id
|
|
555
|
+
# demo_setup_variant_0 uses weapon_id=0x0B.
|
|
556
|
+
weapon_id = 11
|
|
553
557
|
self._setup_world_players(
|
|
554
558
|
[
|
|
555
559
|
(448.0, 384.0, weapon_id),
|
|
@@ -567,7 +571,8 @@ class DemoView:
|
|
|
567
571
|
|
|
568
572
|
def _setup_variant_1(self) -> None:
|
|
569
573
|
self._demo_time_limit_ms = 5000
|
|
570
|
-
weapon_id
|
|
574
|
+
# demo_setup_variant_1 uses weapon_id=0x05.
|
|
575
|
+
weapon_id = 5
|
|
571
576
|
self._setup_world_players(
|
|
572
577
|
[
|
|
573
578
|
(490.0, 448.0, weapon_id),
|
|
@@ -586,7 +591,8 @@ class DemoView:
|
|
|
586
591
|
|
|
587
592
|
def _setup_variant_2(self) -> None:
|
|
588
593
|
self._demo_time_limit_ms = 5000
|
|
589
|
-
weapon_id
|
|
594
|
+
# demo_setup_variant_2 uses weapon_id=0x15.
|
|
595
|
+
weapon_id = 21
|
|
590
596
|
self._setup_world_players([(512.0, 512.0, weapon_id)])
|
|
591
597
|
y = 128
|
|
592
598
|
i = 0
|
|
@@ -601,7 +607,8 @@ class DemoView:
|
|
|
601
607
|
|
|
602
608
|
def _setup_variant_3(self) -> None:
|
|
603
609
|
self._demo_time_limit_ms = 4000
|
|
604
|
-
weapon_id
|
|
610
|
+
# demo_setup_variant_3 uses weapon_id=0x12.
|
|
611
|
+
weapon_id = 18
|
|
605
612
|
self._setup_world_players([(512.0, 512.0, weapon_id)])
|
|
606
613
|
for idx in range(20):
|
|
607
614
|
x = float(self._crand_mod(200) + 32)
|
|
@@ -881,10 +888,10 @@ class DemoView:
|
|
|
881
888
|
def _update_world(self, dt: float) -> None:
|
|
882
889
|
if not self._world.players:
|
|
883
890
|
return
|
|
884
|
-
inputs = self._build_demo_inputs()
|
|
891
|
+
inputs = self._build_demo_inputs(dt)
|
|
885
892
|
self._world.update(dt, inputs=inputs, auto_pick_perks=False, game_mode=0, perk_progression_enabled=False)
|
|
886
893
|
|
|
887
|
-
def _build_demo_inputs(self) -> list[PlayerInput]:
|
|
894
|
+
def _build_demo_inputs(self, dt: float) -> list[PlayerInput]:
|
|
888
895
|
players = self._world.players
|
|
889
896
|
creatures = self._world.creatures.entries
|
|
890
897
|
if len(self._demo_targets) != len(players):
|
|
@@ -892,42 +899,77 @@ class DemoView:
|
|
|
892
899
|
center_x = float(self._world.world_size) * 0.5
|
|
893
900
|
center_y = float(self._world.world_size) * 0.5
|
|
894
901
|
|
|
902
|
+
dt = float(dt)
|
|
903
|
+
|
|
904
|
+
def _turn_towards_heading(cur: float, target: float) -> tuple[float, float]:
|
|
905
|
+
cur = cur % math.tau
|
|
906
|
+
target = target % math.tau
|
|
907
|
+
delta = (target - cur + math.pi) % math.tau - math.pi
|
|
908
|
+
diff = abs(delta)
|
|
909
|
+
if diff <= 1e-9:
|
|
910
|
+
return cur, 0.0
|
|
911
|
+
step = dt * diff * 5.0
|
|
912
|
+
cur = (cur + step) % math.tau if delta > 0.0 else (cur - step) % math.tau
|
|
913
|
+
return cur, diff
|
|
914
|
+
|
|
895
915
|
inputs: list[PlayerInput] = []
|
|
896
916
|
for idx, player in enumerate(players):
|
|
897
917
|
target_idx = self._select_demo_target(idx, player, creatures)
|
|
898
|
-
aim_x = center_x
|
|
899
|
-
aim_y = center_y
|
|
900
918
|
target = None
|
|
901
919
|
if target_idx is not None and 0 <= target_idx < len(creatures):
|
|
902
920
|
candidate = creatures[target_idx]
|
|
903
|
-
if candidate.hp > 0.0:
|
|
921
|
+
if candidate.active and candidate.hp > 0.0:
|
|
904
922
|
target = candidate
|
|
905
|
-
aim_x = candidate.x
|
|
906
|
-
aim_y = candidate.y
|
|
907
|
-
|
|
908
|
-
move_x, move_y = 0.0, 0.0
|
|
909
|
-
to_cx = center_x - player.pos_x
|
|
910
|
-
to_cy = center_y - player.pos_y
|
|
911
|
-
nx, ny, d = _normalize(to_cx, to_cy)
|
|
912
|
-
if d > 120.0:
|
|
913
|
-
move_x += nx
|
|
914
|
-
move_y += ny
|
|
915
923
|
|
|
924
|
+
# Aim: ease the aim point toward the target.
|
|
925
|
+
aim_x = float(player.aim_x)
|
|
926
|
+
aim_y = float(player.aim_y)
|
|
927
|
+
auto_fire = False
|
|
916
928
|
if target is not None:
|
|
917
|
-
|
|
918
|
-
|
|
919
|
-
|
|
920
|
-
if
|
|
921
|
-
|
|
922
|
-
|
|
923
|
-
|
|
924
|
-
|
|
925
|
-
|
|
926
|
-
|
|
927
|
-
|
|
928
|
-
|
|
929
|
-
|
|
930
|
-
|
|
929
|
+
aim_dx = float(target.x) - aim_x
|
|
930
|
+
aim_dy = float(target.y) - aim_y
|
|
931
|
+
aim_dir_x, aim_dir_y, aim_dist = _normalize(aim_dx, aim_dy)
|
|
932
|
+
if aim_dist >= 4.0:
|
|
933
|
+
step = aim_dist * 6.0 * dt
|
|
934
|
+
aim_x += aim_dir_x * step
|
|
935
|
+
aim_y += aim_dir_y * step
|
|
936
|
+
else:
|
|
937
|
+
aim_x = float(target.x)
|
|
938
|
+
aim_y = float(target.y)
|
|
939
|
+
auto_fire = aim_dist < 128.0
|
|
940
|
+
else:
|
|
941
|
+
ax, ay, amag = _normalize(float(player.pos_x) - center_x, float(player.pos_y) - center_y)
|
|
942
|
+
if amag <= 1e-6:
|
|
943
|
+
ax, ay = 0.0, -1.0
|
|
944
|
+
aim_x = float(player.pos_x) + ax * 60.0
|
|
945
|
+
aim_y = float(player.pos_y) + ay * 60.0
|
|
946
|
+
|
|
947
|
+
# Movement:
|
|
948
|
+
# - orbit center if no target
|
|
949
|
+
# - chase target when near center
|
|
950
|
+
# - return to center when too far
|
|
951
|
+
if target is None:
|
|
952
|
+
move_dx = -(float(player.pos_y) - center_y)
|
|
953
|
+
move_dy = float(player.pos_x) - center_x
|
|
954
|
+
else:
|
|
955
|
+
center_dist = math.hypot(float(player.pos_x) - center_x, float(player.pos_y) - center_y)
|
|
956
|
+
if center_dist <= 300.0:
|
|
957
|
+
move_dx = float(target.x) - float(player.pos_x)
|
|
958
|
+
move_dy = float(target.y) - float(player.pos_y)
|
|
959
|
+
else:
|
|
960
|
+
move_dx = center_x - float(player.pos_x)
|
|
961
|
+
move_dy = center_y - float(player.pos_y)
|
|
962
|
+
|
|
963
|
+
desired_x, desired_y, desired_mag = _normalize(move_dx, move_dy)
|
|
964
|
+
if desired_mag <= 1e-6:
|
|
965
|
+
move_x = 0.0
|
|
966
|
+
move_y = 0.0
|
|
967
|
+
else:
|
|
968
|
+
desired_heading = math.atan2(desired_y, desired_x) + math.pi / 2.0
|
|
969
|
+
smoothed_heading, angle_diff = _turn_towards_heading(float(player.heading), desired_heading)
|
|
970
|
+
move_mag = max(0.001, (math.pi - angle_diff) / math.pi)
|
|
971
|
+
move_x = math.cos(smoothed_heading - math.pi / 2.0) * move_mag
|
|
972
|
+
move_y = math.sin(smoothed_heading - math.pi / 2.0) * move_mag
|
|
931
973
|
|
|
932
974
|
inputs.append(
|
|
933
975
|
PlayerInput(
|
|
@@ -935,8 +977,8 @@ class DemoView:
|
|
|
935
977
|
move_y=move_y,
|
|
936
978
|
aim_x=aim_x,
|
|
937
979
|
aim_y=aim_y,
|
|
938
|
-
fire_down=
|
|
939
|
-
fire_pressed=
|
|
980
|
+
fire_down=auto_fire,
|
|
981
|
+
fire_pressed=auto_fire,
|
|
940
982
|
reload_pressed=False,
|
|
941
983
|
)
|
|
942
984
|
)
|
|
@@ -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,
|
|
@@ -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)
|
|
@@ -968,8 +974,8 @@ def perk_apply(
|
|
|
968
974
|
weapon_id = int(current)
|
|
969
975
|
for _ in range(100):
|
|
970
976
|
candidate = int(weapon_pick_random_available(state))
|
|
971
|
-
|
|
972
|
-
|
|
977
|
+
weapon_id = candidate
|
|
978
|
+
if candidate != int(WeaponId.PISTOL) and candidate != current:
|
|
973
979
|
break
|
|
974
980
|
weapon_assign_player(owner, weapon_id, state=state)
|
|
975
981
|
return
|
|
@@ -1923,7 +1929,9 @@ def player_update(player: PlayerState, input_state: PlayerInput, dt: float, stat
|
|
|
1923
1929
|
raw_move_x = float(input_state.move_x)
|
|
1924
1930
|
raw_move_y = float(input_state.move_y)
|
|
1925
1931
|
raw_mag = math.hypot(raw_move_x, raw_move_y)
|
|
1926
|
-
|
|
1932
|
+
# Demo/autoplay uses very small analog magnitudes to represent turn-in-place and
|
|
1933
|
+
# heading alignment slowdown; don't apply a deadzone there.
|
|
1934
|
+
moving_input = raw_mag > (0.0 if state.demo_mode_active else 0.2)
|
|
1927
1935
|
|
|
1928
1936
|
if moving_input:
|
|
1929
1937
|
inv = 1.0 / raw_mag if raw_mag > 1e-9 else 0.0
|
|
@@ -2356,8 +2364,8 @@ def bonus_apply(
|
|
|
2356
2364
|
return
|
|
2357
2365
|
|
|
2358
2366
|
|
|
2359
|
-
def bonus_hud_update(state: GameplayState, players: list[PlayerState]) -> None:
|
|
2360
|
-
"""Refresh HUD slots based on current timer values."""
|
|
2367
|
+
def bonus_hud_update(state: GameplayState, players: list[PlayerState], *, dt: float = 0.0) -> None:
|
|
2368
|
+
"""Refresh HUD slots based on current timer values + advance slide animation."""
|
|
2361
2369
|
|
|
2362
2370
|
def _timer_value(ref: _TimerRef | None) -> float:
|
|
2363
2371
|
if ref is None:
|
|
@@ -2371,16 +2379,35 @@ def bonus_hud_update(state: GameplayState, players: list[PlayerState]) -> None:
|
|
|
2371
2379
|
return float(getattr(players[idx], ref.key, 0.0) or 0.0)
|
|
2372
2380
|
return 0.0
|
|
2373
2381
|
|
|
2374
|
-
|
|
2382
|
+
player_count = len(players)
|
|
2383
|
+
dt = max(0.0, float(dt))
|
|
2384
|
+
|
|
2385
|
+
for slot_index, slot in enumerate(state.bonus_hud.slots):
|
|
2375
2386
|
if not slot.active:
|
|
2376
2387
|
continue
|
|
2377
|
-
timer = _timer_value(slot.timer_ref)
|
|
2378
|
-
if slot.timer_ref_alt is not None
|
|
2379
|
-
|
|
2380
|
-
|
|
2388
|
+
timer = max(0.0, _timer_value(slot.timer_ref))
|
|
2389
|
+
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
|
|
2390
|
+
slot.timer_value = float(timer)
|
|
2391
|
+
slot.timer_value_alt = float(timer_alt)
|
|
2392
|
+
|
|
2393
|
+
if timer > 0.0 or timer_alt > 0.0:
|
|
2394
|
+
slot.slide_x += dt * 350.0
|
|
2395
|
+
else:
|
|
2396
|
+
slot.slide_x -= dt * 320.0
|
|
2397
|
+
|
|
2398
|
+
if slot.slide_x > -2.0:
|
|
2399
|
+
slot.slide_x = -2.0
|
|
2400
|
+
|
|
2401
|
+
if slot.slide_x < -184.0 and not any(other.active for other in state.bonus_hud.slots[slot_index + 1 :]):
|
|
2381
2402
|
slot.active = False
|
|
2403
|
+
slot.bonus_id = 0
|
|
2404
|
+
slot.label = ""
|
|
2405
|
+
slot.icon_id = -1
|
|
2406
|
+
slot.slide_x = -184.0
|
|
2382
2407
|
slot.timer_ref = None
|
|
2383
2408
|
slot.timer_ref_alt = None
|
|
2409
|
+
slot.timer_value = 0.0
|
|
2410
|
+
slot.timer_value_alt = 0.0
|
|
2384
2411
|
|
|
2385
2412
|
|
|
2386
2413
|
def bonus_telekinetic_update(
|
|
@@ -2495,6 +2522,6 @@ def bonus_update(
|
|
|
2495
2522
|
state.bonuses.freeze = max(0.0, state.bonuses.freeze - dt)
|
|
2496
2523
|
|
|
2497
2524
|
if update_hud:
|
|
2498
|
-
bonus_hud_update(state, players)
|
|
2525
|
+
bonus_hud_update(state, players, dt=dt)
|
|
2499
2526
|
|
|
2500
2527
|
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
|