crimsonland 0.1.0.dev11__py3-none-any.whl → 0.1.0.dev13__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.
- crimson/assets_fetch.py +23 -8
- crimson/creatures/runtime.py +15 -0
- crimson/demo.py +47 -38
- crimson/effects.py +46 -1
- crimson/frontend/boot.py +2 -1
- crimson/frontend/high_scores_layout.py +26 -0
- crimson/frontend/menu.py +24 -43
- crimson/frontend/panels/base.py +27 -29
- crimson/frontend/panels/controls.py +152 -65
- crimson/frontend/panels/credits.py +221 -0
- crimson/frontend/panels/databases.py +307 -0
- crimson/frontend/panels/mods.py +1 -3
- crimson/frontend/panels/options.py +36 -42
- crimson/frontend/panels/play_game.py +82 -74
- crimson/frontend/panels/stats.py +255 -298
- crimson/frontend/pause_menu.py +425 -0
- crimson/game.py +512 -505
- crimson/gameplay.py +35 -6
- crimson/modes/base_gameplay_mode.py +3 -0
- crimson/modes/quest_mode.py +54 -44
- crimson/modes/rush_mode.py +4 -1
- crimson/modes/survival_mode.py +15 -10
- crimson/modes/tutorial_mode.py +15 -5
- crimson/modes/typo_mode.py +4 -1
- crimson/persistence/highscores.py +6 -2
- crimson/render/world_renderer.py +1 -1
- crimson/sim/world_state.py +8 -1
- crimson/typo/spawns.py +3 -4
- crimson/ui/demo_trial_overlay.py +3 -3
- crimson/ui/game_over.py +18 -2
- crimson/ui/menu_panel.py +127 -0
- crimson/ui/perk_menu.py +101 -44
- crimson/ui/quest_results.py +669 -0
- crimson/ui/shadow.py +39 -0
- crimson/views/particles.py +1 -1
- crimson/views/perk_menu_debug.py +2 -2
- crimson/views/perks.py +2 -2
- crimson/weapons.py +110 -110
- {crimsonland-0.1.0.dev11.dist-info → crimsonland-0.1.0.dev13.dist-info}/METADATA +1 -1
- {crimsonland-0.1.0.dev11.dist-info → crimsonland-0.1.0.dev13.dist-info}/RECORD +43 -36
- grim/app.py +3 -0
- {crimsonland-0.1.0.dev11.dist-info → crimsonland-0.1.0.dev13.dist-info}/WHEEL +0 -0
- {crimsonland-0.1.0.dev11.dist-info → crimsonland-0.1.0.dev13.dist-info}/entry_points.txt +0 -0
crimson/gameplay.py
CHANGED
|
@@ -585,7 +585,7 @@ def _creature_find_in_radius(creatures: list[_CreatureForPerks], *, pos_x: float
|
|
|
585
585
|
continue
|
|
586
586
|
|
|
587
587
|
dist = math.hypot(float(creature.x) - pos_x, float(creature.y) - pos_y) - radius
|
|
588
|
-
threshold = float(creature.size) * 0.
|
|
588
|
+
threshold = float(creature.size) * 0.14285715 + 3.0
|
|
589
589
|
if threshold < dist:
|
|
590
590
|
continue
|
|
591
591
|
if float(creature.hitbox_size) < 5.0:
|
|
@@ -1511,9 +1511,9 @@ def player_start_reload(player: PlayerState, state: GameplayState) -> None:
|
|
|
1511
1511
|
player.reload_active = True
|
|
1512
1512
|
|
|
1513
1513
|
if perk_active(player, PerkId.FASTLOADER):
|
|
1514
|
-
reload_time *= 0.
|
|
1514
|
+
reload_time *= 0.7
|
|
1515
1515
|
if state.bonuses.weapon_power_up > 0.0:
|
|
1516
|
-
reload_time *= 0.
|
|
1516
|
+
reload_time *= 0.6
|
|
1517
1517
|
|
|
1518
1518
|
player.reload_timer = max(0.0, reload_time)
|
|
1519
1519
|
player.reload_timer_max = player.reload_timer
|
|
@@ -1618,7 +1618,14 @@ def _perk_update_fire_cough(player: PlayerState, dt: float, state: GameplayState
|
|
|
1618
1618
|
state.perk_intervals.fire_cough = float(state.rng.rand() % 4) + 2.0
|
|
1619
1619
|
|
|
1620
1620
|
|
|
1621
|
-
def player_fire_weapon(
|
|
1621
|
+
def player_fire_weapon(
|
|
1622
|
+
player: PlayerState,
|
|
1623
|
+
input_state: PlayerInput,
|
|
1624
|
+
dt: float,
|
|
1625
|
+
state: GameplayState,
|
|
1626
|
+
*,
|
|
1627
|
+
detail_preset: int = 5,
|
|
1628
|
+
) -> None:
|
|
1622
1629
|
dt = float(dt)
|
|
1623
1630
|
|
|
1624
1631
|
weapon_id = int(player.weapon_id)
|
|
@@ -1689,6 +1696,20 @@ def player_fire_weapon(player: PlayerState, input_state: PlayerInput, dt: float,
|
|
|
1689
1696
|
aim_y = float(input_state.aim_y)
|
|
1690
1697
|
dx = aim_x - float(player.pos_x)
|
|
1691
1698
|
dy = aim_y - float(player.pos_y)
|
|
1699
|
+
aim_heading = math.atan2(dy, dx) + math.pi / 2.0
|
|
1700
|
+
|
|
1701
|
+
muzzle_dir = (aim_heading - math.pi / 2.0) - 0.150915
|
|
1702
|
+
muzzle_shell_x = player.pos_x + math.cos(muzzle_dir) * 16.0
|
|
1703
|
+
muzzle_shell_y = player.pos_y + math.sin(muzzle_dir) * 16.0
|
|
1704
|
+
state.effects.spawn_shell_casing(
|
|
1705
|
+
pos_x=muzzle_shell_x,
|
|
1706
|
+
pos_y=muzzle_shell_y,
|
|
1707
|
+
aim_heading=aim_heading,
|
|
1708
|
+
weapon_flags=int(weapon.flags or 0),
|
|
1709
|
+
rand=state.rng.rand,
|
|
1710
|
+
detail_preset=int(detail_preset),
|
|
1711
|
+
)
|
|
1712
|
+
|
|
1692
1713
|
dist = math.hypot(dx, dy)
|
|
1693
1714
|
max_offset = dist * float(player.spread_heat) * 0.5
|
|
1694
1715
|
dir_angle = float(int(state.rng.rand()) & 0x1FF) * (math.tau / 512.0)
|
|
@@ -1885,7 +1906,15 @@ def player_fire_weapon(player: PlayerState, input_state: PlayerInput, dt: float,
|
|
|
1885
1906
|
player_start_reload(player, state)
|
|
1886
1907
|
|
|
1887
1908
|
|
|
1888
|
-
def player_update(
|
|
1909
|
+
def player_update(
|
|
1910
|
+
player: PlayerState,
|
|
1911
|
+
input_state: PlayerInput,
|
|
1912
|
+
dt: float,
|
|
1913
|
+
state: GameplayState,
|
|
1914
|
+
*,
|
|
1915
|
+
detail_preset: int = 5,
|
|
1916
|
+
world_size: float = 1024.0,
|
|
1917
|
+
) -> None:
|
|
1889
1918
|
"""Port of `player_update` (0x004136b0) for the rewrite runtime."""
|
|
1890
1919
|
|
|
1891
1920
|
if dt <= 0.0:
|
|
@@ -2048,7 +2077,7 @@ def player_update(player: PlayerState, input_state: PlayerInput, dt: float, stat
|
|
|
2048
2077
|
elif player.reload_timer == 0.0:
|
|
2049
2078
|
player_start_reload(player, state)
|
|
2050
2079
|
|
|
2051
|
-
player_fire_weapon(player, input_state, dt, state)
|
|
2080
|
+
player_fire_weapon(player, input_state, dt, state, detail_preset=int(detail_preset))
|
|
2052
2081
|
|
|
2053
2082
|
while player.move_phase > 14.0:
|
|
2054
2083
|
player.move_phase -= 14.0
|
|
@@ -283,6 +283,9 @@ class BaseGameplayMode:
|
|
|
283
283
|
self._action = None
|
|
284
284
|
return action
|
|
285
285
|
|
|
286
|
+
def draw_pause_background(self) -> None:
|
|
287
|
+
self._world.draw(draw_aim_indicators=False)
|
|
288
|
+
|
|
286
289
|
def _draw_screen_fade(self) -> None:
|
|
287
290
|
fade_alpha = 0.0
|
|
288
291
|
if self._screen_fade is not None:
|
crimson/modes/quest_mode.py
CHANGED
|
@@ -24,7 +24,9 @@ from ..quests.types import QuestContext, QuestDefinition, SpawnEntry
|
|
|
24
24
|
from ..terrain_assets import terrain_texture_by_id
|
|
25
25
|
from ..ui.cursor import draw_aim_cursor, draw_menu_cursor
|
|
26
26
|
from ..ui.hud import draw_hud_overlay, hud_flags_for_game_mode
|
|
27
|
+
from ..ui.menu_panel import draw_classic_menu_panel
|
|
27
28
|
from ..ui.perk_menu import (
|
|
29
|
+
PERK_MENU_TRANSITION_MS,
|
|
28
30
|
PerkMenuAssets,
|
|
29
31
|
PerkMenuLayout,
|
|
30
32
|
UiButtonState,
|
|
@@ -32,10 +34,10 @@ from ..ui.perk_menu import (
|
|
|
32
34
|
button_update,
|
|
33
35
|
button_width,
|
|
34
36
|
draw_menu_item,
|
|
35
|
-
draw_menu_panel,
|
|
36
37
|
draw_ui_text,
|
|
37
38
|
load_perk_menu_assets,
|
|
38
39
|
menu_item_hit_rect,
|
|
40
|
+
perk_menu_panel_slide_x,
|
|
39
41
|
perk_menu_compute_layout,
|
|
40
42
|
ui_origin,
|
|
41
43
|
ui_scale,
|
|
@@ -78,8 +80,6 @@ PERK_PROMPT_LEVEL_UP_SHIFT_Y = -4.0
|
|
|
78
80
|
PERK_PROMPT_TEXT_MARGIN_X = 16.0
|
|
79
81
|
PERK_PROMPT_TEXT_OFFSET_Y = 8.0
|
|
80
82
|
|
|
81
|
-
PERK_MENU_TRANSITION_MS = 500.0
|
|
82
|
-
|
|
83
83
|
|
|
84
84
|
@dataclass(slots=True)
|
|
85
85
|
class _QuestRunState:
|
|
@@ -306,7 +306,8 @@ class QuestMode(BaseGameplayMode):
|
|
|
306
306
|
self._paused = not self._paused
|
|
307
307
|
|
|
308
308
|
if rl.is_key_pressed(rl.KeyboardKey.KEY_ESCAPE):
|
|
309
|
-
self.
|
|
309
|
+
self._action = "open_pause_menu"
|
|
310
|
+
return
|
|
310
311
|
|
|
311
312
|
def _build_input(self):
|
|
312
313
|
keybinds = config_keybinds(self._config)
|
|
@@ -439,8 +440,7 @@ class QuestMode(BaseGameplayMode):
|
|
|
439
440
|
screen_h = float(rl.get_screen_height())
|
|
440
441
|
scale = ui_scale(screen_w, screen_h)
|
|
441
442
|
origin_x, origin_y = ui_origin(screen_w, screen_h, scale)
|
|
442
|
-
|
|
443
|
-
slide_x = (menu_t - 1.0) * (self._perk_ui_layout.panel_w * scale)
|
|
443
|
+
slide_x = perk_menu_panel_slide_x(self._perk_menu_timeline_ms, width=self._perk_ui_layout.panel_w)
|
|
444
444
|
|
|
445
445
|
mouse = self._ui_mouse_pos()
|
|
446
446
|
click = rl.is_mouse_button_pressed(rl.MouseButton.MOUSE_BUTTON_LEFT)
|
|
@@ -450,12 +450,13 @@ class QuestMode(BaseGameplayMode):
|
|
|
450
450
|
computed = perk_menu_compute_layout(
|
|
451
451
|
self._perk_ui_layout,
|
|
452
452
|
screen_w=screen_w,
|
|
453
|
-
origin_x=origin_x
|
|
453
|
+
origin_x=origin_x,
|
|
454
454
|
origin_y=origin_y,
|
|
455
455
|
scale=scale,
|
|
456
456
|
choice_count=len(choices),
|
|
457
457
|
expert_owned=expert_owned,
|
|
458
458
|
master_owned=master_owned,
|
|
459
|
+
panel_slide_x=slide_x,
|
|
459
460
|
)
|
|
460
461
|
|
|
461
462
|
fx_toggle = int(self._config.data.get("fx_toggle", 0) or 0) if self._config is not None else 0
|
|
@@ -517,6 +518,42 @@ class QuestMode(BaseGameplayMode):
|
|
|
517
518
|
self._world.audio_router.play_sfx("sfx_ui_bonus")
|
|
518
519
|
self._close_perk_menu()
|
|
519
520
|
|
|
521
|
+
def _close_failed_run(self) -> None:
|
|
522
|
+
if self._outcome is None:
|
|
523
|
+
fired = 0
|
|
524
|
+
hit = 0
|
|
525
|
+
try:
|
|
526
|
+
fired = int(self._state.shots_fired[int(self._player.index)])
|
|
527
|
+
hit = int(self._state.shots_hit[int(self._player.index)])
|
|
528
|
+
except Exception:
|
|
529
|
+
fired = 0
|
|
530
|
+
hit = 0
|
|
531
|
+
fired = max(0, int(fired))
|
|
532
|
+
hit = max(0, min(int(hit), fired))
|
|
533
|
+
most_used_weapon_id = most_used_weapon_id_for_player(
|
|
534
|
+
self._state,
|
|
535
|
+
player_index=int(self._player.index),
|
|
536
|
+
fallback_weapon_id=int(self._player.weapon_id),
|
|
537
|
+
)
|
|
538
|
+
player2_health = None
|
|
539
|
+
if len(self._world.players) >= 2:
|
|
540
|
+
player2_health = float(self._world.players[1].health)
|
|
541
|
+
self._outcome = QuestRunOutcome(
|
|
542
|
+
kind="failed",
|
|
543
|
+
level=str(self._quest.level),
|
|
544
|
+
base_time_ms=int(self._quest.spawn_timeline_ms),
|
|
545
|
+
player_health=float(self._player.health),
|
|
546
|
+
player2_health=player2_health,
|
|
547
|
+
pending_perk_count=int(self._state.perk_selection.pending_count),
|
|
548
|
+
experience=int(self._player.experience),
|
|
549
|
+
kill_count=int(self._creatures.kill_count),
|
|
550
|
+
weapon_id=int(self._player.weapon_id),
|
|
551
|
+
shots_fired=fired,
|
|
552
|
+
shots_hit=hit,
|
|
553
|
+
most_used_weapon_id=int(most_used_weapon_id),
|
|
554
|
+
)
|
|
555
|
+
self.close_requested = True
|
|
556
|
+
|
|
520
557
|
def _draw_perk_prompt(self) -> None:
|
|
521
558
|
if self._perk_menu_open or self._perk_menu_timeline_ms > 1e-3:
|
|
522
559
|
return
|
|
@@ -597,24 +634,26 @@ class QuestMode(BaseGameplayMode):
|
|
|
597
634
|
screen_h = float(rl.get_screen_height())
|
|
598
635
|
scale = ui_scale(screen_w, screen_h)
|
|
599
636
|
origin_x, origin_y = ui_origin(screen_w, screen_h, scale)
|
|
600
|
-
slide_x = (
|
|
637
|
+
slide_x = perk_menu_panel_slide_x(self._perk_menu_timeline_ms, width=self._perk_ui_layout.panel_w)
|
|
601
638
|
|
|
602
639
|
master_owned = int(self._player.perk_counts[int(PerkId.PERK_MASTER)]) > 0
|
|
603
640
|
expert_owned = int(self._player.perk_counts[int(PerkId.PERK_EXPERT)]) > 0
|
|
604
641
|
computed = perk_menu_compute_layout(
|
|
605
642
|
self._perk_ui_layout,
|
|
606
643
|
screen_w=screen_w,
|
|
607
|
-
origin_x=origin_x
|
|
644
|
+
origin_x=origin_x,
|
|
608
645
|
origin_y=origin_y,
|
|
609
646
|
scale=scale,
|
|
610
647
|
choice_count=len(choices),
|
|
611
648
|
expert_owned=expert_owned,
|
|
612
649
|
master_owned=master_owned,
|
|
650
|
+
panel_slide_x=slide_x,
|
|
613
651
|
)
|
|
614
652
|
|
|
615
653
|
panel_tex = self._perk_menu_assets.menu_panel
|
|
616
654
|
if panel_tex is not None:
|
|
617
|
-
|
|
655
|
+
fx_detail = bool(int(self._config.data.get("fx_detail_0", 0) or 0)) if self._config is not None else False
|
|
656
|
+
draw_classic_menu_panel(panel_tex, dst=computed.panel, shadow=fx_detail)
|
|
618
657
|
|
|
619
658
|
title_tex = self._perk_menu_assets.title_pick_perk
|
|
620
659
|
if title_tex is not None:
|
|
@@ -665,6 +704,8 @@ class QuestMode(BaseGameplayMode):
|
|
|
665
704
|
|
|
666
705
|
dt_frame, dt_ui_ms = self._tick_frame(dt)
|
|
667
706
|
self._handle_input()
|
|
707
|
+
if self._action == "open_pause_menu":
|
|
708
|
+
return
|
|
668
709
|
|
|
669
710
|
if self.close_requested:
|
|
670
711
|
return
|
|
@@ -719,6 +760,8 @@ class QuestMode(BaseGameplayMode):
|
|
|
719
760
|
|
|
720
761
|
dt_world = 0.0 if self._paused or (not any_alive) or perk_menu_active else dt_frame
|
|
721
762
|
if dt_world <= 0.0:
|
|
763
|
+
if not any(player.health > 0.0 for player in self._world.players):
|
|
764
|
+
self._close_failed_run()
|
|
722
765
|
return
|
|
723
766
|
|
|
724
767
|
self._quest.quest_name_timer_ms += dt_world * 1000.0
|
|
@@ -734,40 +777,7 @@ class QuestMode(BaseGameplayMode):
|
|
|
734
777
|
|
|
735
778
|
any_alive_after = any(player.health > 0.0 for player in self._world.players)
|
|
736
779
|
if not any_alive_after:
|
|
737
|
-
|
|
738
|
-
fired = 0
|
|
739
|
-
hit = 0
|
|
740
|
-
try:
|
|
741
|
-
fired = int(self._state.shots_fired[int(self._player.index)])
|
|
742
|
-
hit = int(self._state.shots_hit[int(self._player.index)])
|
|
743
|
-
except Exception:
|
|
744
|
-
fired = 0
|
|
745
|
-
hit = 0
|
|
746
|
-
fired = max(0, int(fired))
|
|
747
|
-
hit = max(0, min(int(hit), fired))
|
|
748
|
-
most_used_weapon_id = most_used_weapon_id_for_player(
|
|
749
|
-
self._state,
|
|
750
|
-
player_index=int(self._player.index),
|
|
751
|
-
fallback_weapon_id=int(self._player.weapon_id),
|
|
752
|
-
)
|
|
753
|
-
player2_health = None
|
|
754
|
-
if len(self._world.players) >= 2:
|
|
755
|
-
player2_health = float(self._world.players[1].health)
|
|
756
|
-
self._outcome = QuestRunOutcome(
|
|
757
|
-
kind="failed",
|
|
758
|
-
level=str(self._quest.level),
|
|
759
|
-
base_time_ms=int(self._quest.spawn_timeline_ms),
|
|
760
|
-
player_health=float(self._player.health),
|
|
761
|
-
player2_health=player2_health,
|
|
762
|
-
pending_perk_count=int(self._state.perk_selection.pending_count),
|
|
763
|
-
experience=int(self._player.experience),
|
|
764
|
-
kill_count=int(self._creatures.kill_count),
|
|
765
|
-
weapon_id=int(self._player.weapon_id),
|
|
766
|
-
shots_fired=fired,
|
|
767
|
-
shots_hit=hit,
|
|
768
|
-
most_used_weapon_id=int(most_used_weapon_id),
|
|
769
|
-
)
|
|
770
|
-
self.close_requested = True
|
|
780
|
+
self._close_failed_run()
|
|
771
781
|
return
|
|
772
782
|
|
|
773
783
|
creatures_none_active = not bool(self._creatures.iter_active())
|
crimson/modes/rush_mode.py
CHANGED
|
@@ -95,7 +95,8 @@ class RushMode(BaseGameplayMode):
|
|
|
95
95
|
self._paused = not self._paused
|
|
96
96
|
|
|
97
97
|
if rl.is_key_pressed(rl.KeyboardKey.KEY_ESCAPE):
|
|
98
|
-
self.
|
|
98
|
+
self._action = "open_pause_menu"
|
|
99
|
+
return
|
|
99
100
|
|
|
100
101
|
def _build_input(self) -> PlayerInput:
|
|
101
102
|
keybinds = config_keybinds(self._config)
|
|
@@ -174,6 +175,8 @@ class RushMode(BaseGameplayMode):
|
|
|
174
175
|
|
|
175
176
|
dt_frame = self._tick_frame(dt)[0]
|
|
176
177
|
self._handle_input()
|
|
178
|
+
if self._action == "open_pause_menu":
|
|
179
|
+
return
|
|
177
180
|
|
|
178
181
|
if self._game_over_active:
|
|
179
182
|
record = self._game_over_record
|
crimson/modes/survival_mode.py
CHANGED
|
@@ -19,18 +19,20 @@ from ..persistence.highscores import HighScoreRecord
|
|
|
19
19
|
from ..perks import PerkId, perk_display_description, perk_display_name
|
|
20
20
|
from ..ui.cursor import draw_aim_cursor, draw_menu_cursor
|
|
21
21
|
from ..ui.hud import draw_hud_overlay, hud_flags_for_game_mode
|
|
22
|
+
from ..ui.menu_panel import draw_classic_menu_panel
|
|
22
23
|
from ..input_codes import config_keybinds, input_code_is_down, input_code_is_pressed, player_move_fire_binds
|
|
23
24
|
from ..ui.perk_menu import (
|
|
25
|
+
PERK_MENU_TRANSITION_MS,
|
|
24
26
|
PerkMenuLayout,
|
|
25
27
|
UiButtonState,
|
|
26
28
|
button_draw,
|
|
27
29
|
button_update,
|
|
28
30
|
button_width,
|
|
29
|
-
draw_menu_panel,
|
|
30
31
|
draw_menu_item,
|
|
31
32
|
draw_ui_text,
|
|
32
33
|
load_perk_menu_assets,
|
|
33
34
|
menu_item_hit_rect,
|
|
35
|
+
perk_menu_panel_slide_x,
|
|
34
36
|
perk_menu_compute_layout,
|
|
35
37
|
ui_origin,
|
|
36
38
|
ui_scale,
|
|
@@ -69,8 +71,6 @@ PERK_PROMPT_LEVEL_UP_SHIFT_Y = -4.0
|
|
|
69
71
|
PERK_PROMPT_TEXT_MARGIN_X = 16.0
|
|
70
72
|
PERK_PROMPT_TEXT_OFFSET_Y = 8.0
|
|
71
73
|
|
|
72
|
-
PERK_MENU_TRANSITION_MS = 500.0
|
|
73
|
-
|
|
74
74
|
|
|
75
75
|
@dataclass(slots=True)
|
|
76
76
|
class _SurvivalState:
|
|
@@ -186,7 +186,8 @@ class SurvivalMode(BaseGameplayMode):
|
|
|
186
186
|
survival_check_level_up(self._player, self._state.perk_selection)
|
|
187
187
|
|
|
188
188
|
if rl.is_key_pressed(rl.KeyboardKey.KEY_ESCAPE):
|
|
189
|
-
self.
|
|
189
|
+
self._action = "open_pause_menu"
|
|
190
|
+
return
|
|
190
191
|
|
|
191
192
|
def _build_input(self) -> PlayerInput:
|
|
192
193
|
keybinds = config_keybinds(self._config)
|
|
@@ -357,8 +358,7 @@ class SurvivalMode(BaseGameplayMode):
|
|
|
357
358
|
screen_h = float(rl.get_screen_height())
|
|
358
359
|
scale = ui_scale(screen_w, screen_h)
|
|
359
360
|
origin_x, origin_y = ui_origin(screen_w, screen_h, scale)
|
|
360
|
-
|
|
361
|
-
slide_x = (menu_t - 1.0) * (self._perk_ui_layout.panel_w * scale)
|
|
361
|
+
slide_x = perk_menu_panel_slide_x(self._perk_menu_timeline_ms, width=self._perk_ui_layout.panel_w)
|
|
362
362
|
|
|
363
363
|
mouse = self._ui_mouse_pos()
|
|
364
364
|
click = rl.is_mouse_button_pressed(rl.MouseButton.MOUSE_BUTTON_LEFT)
|
|
@@ -368,12 +368,13 @@ class SurvivalMode(BaseGameplayMode):
|
|
|
368
368
|
computed = perk_menu_compute_layout(
|
|
369
369
|
self._perk_ui_layout,
|
|
370
370
|
screen_w=screen_w,
|
|
371
|
-
origin_x=origin_x
|
|
371
|
+
origin_x=origin_x,
|
|
372
372
|
origin_y=origin_y,
|
|
373
373
|
scale=scale,
|
|
374
374
|
choice_count=len(choices),
|
|
375
375
|
expert_owned=expert_owned,
|
|
376
376
|
master_owned=master_owned,
|
|
377
|
+
panel_slide_x=slide_x,
|
|
377
378
|
)
|
|
378
379
|
|
|
379
380
|
fx_toggle = int(self._config.data.get("fx_toggle", 0) or 0) if self._config is not None else 0
|
|
@@ -441,6 +442,8 @@ class SurvivalMode(BaseGameplayMode):
|
|
|
441
442
|
dt_frame, dt_ui_ms = self._tick_frame(dt)
|
|
442
443
|
self._cursor_time += dt_frame
|
|
443
444
|
self._handle_input()
|
|
445
|
+
if self._action == "open_pause_menu":
|
|
446
|
+
return
|
|
444
447
|
|
|
445
448
|
if self._game_over_active:
|
|
446
449
|
record = self._game_over_record
|
|
@@ -651,24 +654,26 @@ class SurvivalMode(BaseGameplayMode):
|
|
|
651
654
|
screen_h = float(rl.get_screen_height())
|
|
652
655
|
scale = ui_scale(screen_w, screen_h)
|
|
653
656
|
origin_x, origin_y = ui_origin(screen_w, screen_h, scale)
|
|
654
|
-
slide_x = (
|
|
657
|
+
slide_x = perk_menu_panel_slide_x(self._perk_menu_timeline_ms, width=self._perk_ui_layout.panel_w)
|
|
655
658
|
|
|
656
659
|
master_owned = int(self._player.perk_counts[int(PerkId.PERK_MASTER)]) > 0
|
|
657
660
|
expert_owned = int(self._player.perk_counts[int(PerkId.PERK_EXPERT)]) > 0
|
|
658
661
|
computed = perk_menu_compute_layout(
|
|
659
662
|
self._perk_ui_layout,
|
|
660
663
|
screen_w=screen_w,
|
|
661
|
-
origin_x=origin_x
|
|
664
|
+
origin_x=origin_x,
|
|
662
665
|
origin_y=origin_y,
|
|
663
666
|
scale=scale,
|
|
664
667
|
choice_count=len(choices),
|
|
665
668
|
expert_owned=expert_owned,
|
|
666
669
|
master_owned=master_owned,
|
|
670
|
+
panel_slide_x=slide_x,
|
|
667
671
|
)
|
|
668
672
|
|
|
669
673
|
panel_tex = self._perk_menu_assets.menu_panel
|
|
670
674
|
if panel_tex is not None:
|
|
671
|
-
|
|
675
|
+
fx_detail = bool(int(self._config.data.get("fx_detail_0", 0) or 0)) if self._config is not None else False
|
|
676
|
+
draw_classic_menu_panel(panel_tex, dst=computed.panel, shadow=fx_detail)
|
|
672
677
|
|
|
673
678
|
title_tex = self._perk_menu_assets.title_pick_perk
|
|
674
679
|
if title_tex is not None:
|
crimson/modes/tutorial_mode.py
CHANGED
|
@@ -21,7 +21,9 @@ from ..perks import PerkId, perk_display_description, perk_display_name
|
|
|
21
21
|
from ..tutorial.timeline import TutorialFrameActions, TutorialState, tick_tutorial_timeline
|
|
22
22
|
from ..ui.cursor import draw_aim_cursor, draw_menu_cursor
|
|
23
23
|
from ..ui.hud import draw_hud_overlay, hud_flags_for_game_mode, hud_ui_scale
|
|
24
|
+
from ..ui.menu_panel import draw_classic_menu_panel
|
|
24
25
|
from ..ui.perk_menu import (
|
|
26
|
+
PERK_MENU_TRANSITION_MS,
|
|
25
27
|
PerkMenuAssets,
|
|
26
28
|
PerkMenuLayout,
|
|
27
29
|
UiButtonState,
|
|
@@ -29,10 +31,10 @@ from ..ui.perk_menu import (
|
|
|
29
31
|
button_update,
|
|
30
32
|
button_width,
|
|
31
33
|
draw_menu_item,
|
|
32
|
-
draw_menu_panel,
|
|
33
34
|
draw_ui_text,
|
|
34
35
|
load_perk_menu_assets,
|
|
35
36
|
menu_item_hit_rect,
|
|
37
|
+
perk_menu_panel_slide_x,
|
|
36
38
|
perk_menu_compute_layout,
|
|
37
39
|
ui_origin,
|
|
38
40
|
ui_scale,
|
|
@@ -143,7 +145,8 @@ class TutorialMode(BaseGameplayMode):
|
|
|
143
145
|
self._paused = not self._paused
|
|
144
146
|
|
|
145
147
|
if rl.is_key_pressed(rl.KeyboardKey.KEY_ESCAPE):
|
|
146
|
-
self.
|
|
148
|
+
self._action = "open_pause_menu"
|
|
149
|
+
return
|
|
147
150
|
|
|
148
151
|
def _build_input(self) -> PlayerInput:
|
|
149
152
|
keybinds = config_keybinds(self._config)
|
|
@@ -281,6 +284,8 @@ class TutorialMode(BaseGameplayMode):
|
|
|
281
284
|
screen_h = float(rl.get_screen_height())
|
|
282
285
|
scale = ui_scale(screen_w, screen_h)
|
|
283
286
|
origin_x, origin_y = ui_origin(screen_w, screen_h, scale)
|
|
287
|
+
slide_x = perk_menu_panel_slide_x(self._perk_menu_timeline_ms, width=self._perk_ui_layout.panel_w)
|
|
288
|
+
slide_x = perk_menu_panel_slide_x(self._perk_menu_timeline_ms, width=self._perk_ui_layout.panel_w)
|
|
284
289
|
|
|
285
290
|
mouse = self._ui_mouse_pos()
|
|
286
291
|
click = rl.is_mouse_button_pressed(rl.MouseButton.MOUSE_BUTTON_LEFT)
|
|
@@ -296,6 +301,7 @@ class TutorialMode(BaseGameplayMode):
|
|
|
296
301
|
choice_count=len(choices),
|
|
297
302
|
expert_owned=expert_owned,
|
|
298
303
|
master_owned=master_owned,
|
|
304
|
+
panel_slide_x=slide_x,
|
|
299
305
|
)
|
|
300
306
|
|
|
301
307
|
fx_toggle = int(self._config.data.get("fx_toggle", 0) or 0) if self._config is not None else 0
|
|
@@ -346,6 +352,8 @@ class TutorialMode(BaseGameplayMode):
|
|
|
346
352
|
dt_frame, dt_ui_ms = self._tick_frame(dt, clamp_cursor_pulse=True)
|
|
347
353
|
|
|
348
354
|
self._handle_input()
|
|
355
|
+
if self._action == "open_pause_menu":
|
|
356
|
+
return
|
|
349
357
|
if self.close_requested:
|
|
350
358
|
return
|
|
351
359
|
|
|
@@ -358,9 +366,9 @@ class TutorialMode(BaseGameplayMode):
|
|
|
358
366
|
self._perk_menu_handle_input(dt_frame, dt_ui_ms)
|
|
359
367
|
|
|
360
368
|
if self._perk_menu_open:
|
|
361
|
-
self._perk_menu_timeline_ms = _clamp(self._perk_menu_timeline_ms + dt_ui_ms, 0.0,
|
|
369
|
+
self._perk_menu_timeline_ms = _clamp(self._perk_menu_timeline_ms + dt_ui_ms, 0.0, PERK_MENU_TRANSITION_MS)
|
|
362
370
|
else:
|
|
363
|
-
self._perk_menu_timeline_ms = _clamp(self._perk_menu_timeline_ms - dt_ui_ms, 0.0,
|
|
371
|
+
self._perk_menu_timeline_ms = _clamp(self._perk_menu_timeline_ms - dt_ui_ms, 0.0, PERK_MENU_TRANSITION_MS)
|
|
364
372
|
|
|
365
373
|
dt_world = 0.0 if self._paused or perk_menu_active else dt_frame
|
|
366
374
|
|
|
@@ -601,11 +609,13 @@ class TutorialMode(BaseGameplayMode):
|
|
|
601
609
|
choice_count=len(choices),
|
|
602
610
|
expert_owned=expert_owned,
|
|
603
611
|
master_owned=master_owned,
|
|
612
|
+
panel_slide_x=slide_x,
|
|
604
613
|
)
|
|
605
614
|
|
|
606
615
|
panel_tex = assets.menu_panel
|
|
607
616
|
if panel_tex is not None:
|
|
608
|
-
|
|
617
|
+
fx_detail = bool(int(self._config.data.get("fx_detail_0", 0) or 0)) if self._config is not None else False
|
|
618
|
+
draw_classic_menu_panel(panel_tex, dst=computed.panel, shadow=fx_detail)
|
|
609
619
|
|
|
610
620
|
title_tex = assets.title_pick_perk
|
|
611
621
|
if title_tex is not None:
|
crimson/modes/typo_mode.py
CHANGED
|
@@ -120,7 +120,8 @@ class TypoShooterMode(BaseGameplayMode):
|
|
|
120
120
|
self._paused = not self._paused
|
|
121
121
|
|
|
122
122
|
if rl.is_key_pressed(rl.KeyboardKey.KEY_ESCAPE):
|
|
123
|
-
self.
|
|
123
|
+
self._action = "open_pause_menu"
|
|
124
|
+
return
|
|
124
125
|
|
|
125
126
|
def _active_mask(self) -> list[bool]:
|
|
126
127
|
return [bool(entry.active) for entry in self._creatures.entries]
|
|
@@ -257,6 +258,8 @@ class TypoShooterMode(BaseGameplayMode):
|
|
|
257
258
|
|
|
258
259
|
dt_frame = self._tick_frame(dt)[0]
|
|
259
260
|
self._handle_input()
|
|
261
|
+
if self._action == "open_pause_menu":
|
|
262
|
+
return
|
|
260
263
|
|
|
261
264
|
if self._game_over_active:
|
|
262
265
|
self._update_game_over_ui(dt)
|
|
@@ -239,7 +239,9 @@ def scores_path_for_mode(
|
|
|
239
239
|
# Native `highscore_build_path` uses `questhc*.hi` when hardcore is OFF,
|
|
240
240
|
# and `quest*.hi` when hardcore is ON.
|
|
241
241
|
prefix = "quest" if hardcore else "questhc"
|
|
242
|
-
|
|
242
|
+
major = int(quest_stage_major)
|
|
243
|
+
minor = int(quest_stage_minor)
|
|
244
|
+
return root / f"{prefix}{major}_{minor}.hi"
|
|
243
245
|
return root / "unknown.hi"
|
|
244
246
|
|
|
245
247
|
|
|
@@ -272,7 +274,9 @@ def scores_path_for_config(base_dir: Path, config: CrimsonConfig, *, quest_stage
|
|
|
272
274
|
# Native `highscore_build_path` uses `questhc*.hi` when hardcore is OFF,
|
|
273
275
|
# and `quest*.hi` when hardcore is ON.
|
|
274
276
|
prefix = "quest" if hardcore else "questhc"
|
|
275
|
-
|
|
277
|
+
major = int(quest_stage_major)
|
|
278
|
+
minor = int(quest_stage_minor)
|
|
279
|
+
return root / f"{prefix}{major}_{minor}.hi"
|
|
276
280
|
return root / "unknown.hi"
|
|
277
281
|
|
|
278
282
|
|
crimson/render/world_renderer.py
CHANGED
|
@@ -947,7 +947,7 @@ class WorldRenderer:
|
|
|
947
947
|
if float(getattr(creature, "hitbox_size", 0.0)) <= 5.0:
|
|
948
948
|
continue
|
|
949
949
|
d = math.hypot(float(creature.x) - pos_x, float(creature.y) - pos_y)
|
|
950
|
-
threshold = float(creature.size) * 0.
|
|
950
|
+
threshold = float(creature.size) * 0.14285715 + 3.0
|
|
951
951
|
if d - radius < threshold:
|
|
952
952
|
targets.append(creature)
|
|
953
953
|
|
crimson/sim/world_state.py
CHANGED
|
@@ -290,7 +290,14 @@ class WorldState:
|
|
|
290
290
|
|
|
291
291
|
for idx, player in enumerate(self.players):
|
|
292
292
|
input_state = inputs[idx] if idx < len(inputs) else PlayerInput()
|
|
293
|
-
player_update(
|
|
293
|
+
player_update(
|
|
294
|
+
player,
|
|
295
|
+
input_state,
|
|
296
|
+
dt,
|
|
297
|
+
self.state,
|
|
298
|
+
detail_preset=int(detail_preset),
|
|
299
|
+
world_size=float(world_size),
|
|
300
|
+
)
|
|
294
301
|
|
|
295
302
|
if dt > 0.0:
|
|
296
303
|
self._advance_creature_anim(dt)
|
crimson/typo/spawns.py
CHANGED
|
@@ -47,9 +47,9 @@ def tick_typo_spawns(
|
|
|
47
47
|
y = math.cos(t) * 256.0 + float(world_height) * 0.5
|
|
48
48
|
|
|
49
49
|
tint_t = float(elapsed_ms + 1)
|
|
50
|
-
tint_r = _clamp(tint_t * 0.0000083333334 + 0.
|
|
51
|
-
tint_g = _clamp(tint_t * 10000.0 + 0.
|
|
52
|
-
tint_b = _clamp(math.sin(tint_t * 0.0001) + 0.
|
|
50
|
+
tint_r = _clamp(tint_t * 0.0000083333334 + 0.3, 0.0, 1.0)
|
|
51
|
+
tint_g = _clamp(tint_t * 10000.0 + 0.3, 0.0, 1.0)
|
|
52
|
+
tint_b = _clamp(math.sin(tint_t * 0.0001) + 0.3, 0.0, 1.0)
|
|
53
53
|
tint = (tint_r, tint_g, tint_b, 1.0)
|
|
54
54
|
|
|
55
55
|
spawns.append(
|
|
@@ -70,4 +70,3 @@ def tick_typo_spawns(
|
|
|
70
70
|
)
|
|
71
71
|
|
|
72
72
|
return cooldown, spawns
|
|
73
|
-
|
crimson/ui/demo_trial_overlay.py
CHANGED
|
@@ -66,13 +66,14 @@ class DemoTrialOverlayUi:
|
|
|
66
66
|
|
|
67
67
|
if self._assets is None:
|
|
68
68
|
cursor = cache.get_or_load("ui_cursor", "ui/ui_cursor.jaz").texture
|
|
69
|
-
|
|
69
|
+
button_sm = cache.get_or_load("ui_buttonSm", "ui/ui_button_64x32.jaz").texture
|
|
70
|
+
button_md = cache.get_or_load("ui_buttonMd", "ui/ui_button_128x32.jaz").texture
|
|
70
71
|
self._assets = PerkMenuAssets(
|
|
71
72
|
menu_panel=None,
|
|
72
73
|
title_pick_perk=None,
|
|
73
74
|
title_level_up=None,
|
|
74
75
|
menu_item=None,
|
|
75
|
-
button_sm=
|
|
76
|
+
button_sm=button_sm,
|
|
76
77
|
button_md=button_md,
|
|
77
78
|
cursor=cursor,
|
|
78
79
|
aim=None,
|
|
@@ -232,4 +233,3 @@ class DemoTrialOverlayUi:
|
|
|
232
233
|
scale=float(scale),
|
|
233
234
|
)
|
|
234
235
|
cursor_draw(assets, mouse=rl.get_mouse_position(), scale=1.0, alpha=1.0)
|
|
235
|
-
|
crimson/ui/game_over.py
CHANGED
|
@@ -31,6 +31,7 @@ from .perk_menu import (
|
|
|
31
31
|
draw_ui_text,
|
|
32
32
|
load_perk_menu_assets,
|
|
33
33
|
)
|
|
34
|
+
from .shadow import UI_SHADOW_OFFSET, draw_ui_quad_shadow
|
|
34
35
|
|
|
35
36
|
|
|
36
37
|
UI_BASE_WIDTH = 640.0
|
|
@@ -568,9 +569,24 @@ class GameOverUi:
|
|
|
568
569
|
|
|
569
570
|
# Panel background
|
|
570
571
|
if self.assets.menu_panel is not None:
|
|
571
|
-
|
|
572
|
+
panel_tex = self.assets.menu_panel
|
|
573
|
+
src = rl.Rectangle(0.0, 0.0, float(panel_tex.width), float(panel_tex.height))
|
|
572
574
|
dst = rl.Rectangle(panel.x, panel.y, panel.width, panel.height)
|
|
573
|
-
|
|
575
|
+
fx_detail = bool(int(getattr(self.config, "data", {}).get("fx_detail_0", 0) or 0))
|
|
576
|
+
if fx_detail:
|
|
577
|
+
draw_ui_quad_shadow(
|
|
578
|
+
texture=panel_tex,
|
|
579
|
+
src=src,
|
|
580
|
+
dst=rl.Rectangle(
|
|
581
|
+
float(dst.x + UI_SHADOW_OFFSET),
|
|
582
|
+
float(dst.y + UI_SHADOW_OFFSET),
|
|
583
|
+
float(dst.width),
|
|
584
|
+
float(dst.height),
|
|
585
|
+
),
|
|
586
|
+
origin=rl.Vector2(0.0, 0.0),
|
|
587
|
+
rotation_deg=0.0,
|
|
588
|
+
)
|
|
589
|
+
rl.draw_texture_pro(panel_tex, src, dst, rl.Vector2(0.0, 0.0), 0.0, rl.WHITE)
|
|
574
590
|
|
|
575
591
|
# Banner (Reaper / Well done)
|
|
576
592
|
banner = self.assets.text_reaper if banner_kind == "reaper" else self.assets.text_well_done
|