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
|
@@ -9,11 +9,13 @@ from grim.fonts.small import SmallFontData, draw_small_text, load_small_font
|
|
|
9
9
|
from ..menu import (
|
|
10
10
|
MENU_PANEL_OFFSET_X,
|
|
11
11
|
MENU_PANEL_OFFSET_Y,
|
|
12
|
+
MENU_PANEL_HEIGHT,
|
|
12
13
|
MENU_PANEL_WIDTH,
|
|
13
14
|
MenuView,
|
|
14
15
|
_draw_menu_cursor,
|
|
15
16
|
)
|
|
16
17
|
from ..transitions import _draw_screen_fade
|
|
18
|
+
from ...ui.menu_panel import draw_classic_menu_panel
|
|
17
19
|
from .base import PANEL_TIMELINE_END_MS, PANEL_TIMELINE_START_MS, PanelMenuView
|
|
18
20
|
from ...input_codes import config_keybinds, input_code_name, player_move_fire_binds
|
|
19
21
|
|
|
@@ -21,20 +23,46 @@ if TYPE_CHECKING:
|
|
|
21
23
|
from ...game import GameState
|
|
22
24
|
|
|
23
25
|
|
|
26
|
+
# Measured from ui_render_trace_oracle_1024x768.json (state_3:Configure for:, timeline=300).
|
|
27
|
+
CONTROLS_LEFT_PANEL_POS_X = -165.0
|
|
28
|
+
CONTROLS_LEFT_PANEL_POS_Y = 200.0
|
|
29
|
+
CONTROLS_RIGHT_PANEL_POS_X = 590.0
|
|
30
|
+
CONTROLS_RIGHT_PANEL_POS_Y = 110.0
|
|
31
|
+
CONTROLS_RIGHT_PANEL_HEIGHT = 378.0
|
|
32
|
+
CONTROLS_BACK_POS_X = -155.0
|
|
33
|
+
CONTROLS_BACK_POS_Y = 420.0
|
|
34
|
+
|
|
35
|
+
|
|
24
36
|
class ControlsMenuView(PanelMenuView):
|
|
25
37
|
def __init__(self, state: GameState) -> None:
|
|
26
|
-
super().__init__(
|
|
38
|
+
super().__init__(
|
|
39
|
+
state,
|
|
40
|
+
title="Controls",
|
|
41
|
+
back_action="open_options",
|
|
42
|
+
panel_pos_x=CONTROLS_LEFT_PANEL_POS_X,
|
|
43
|
+
panel_pos_y=CONTROLS_LEFT_PANEL_POS_Y,
|
|
44
|
+
back_pos_x=CONTROLS_BACK_POS_X,
|
|
45
|
+
back_pos_y=CONTROLS_BACK_POS_Y,
|
|
46
|
+
)
|
|
27
47
|
self._small_font: SmallFontData | None = None
|
|
28
|
-
self.
|
|
48
|
+
self._text_controls: rl.Texture2D | None = None
|
|
49
|
+
self._drop_off: rl.Texture2D | None = None
|
|
50
|
+
self._check_on: rl.Texture2D | None = None
|
|
51
|
+
self._check_off: rl.Texture2D | None = None
|
|
52
|
+
|
|
53
|
+
self._config_player = 1
|
|
29
54
|
|
|
30
55
|
def open(self) -> None:
|
|
31
56
|
super().open()
|
|
32
|
-
|
|
57
|
+
cache = self._ensure_cache()
|
|
58
|
+
# UI elements used by the classic controls screen.
|
|
59
|
+
self._text_controls = cache.get_or_load("ui_textControls", "ui/ui_textControls.jaz").texture
|
|
60
|
+
self._drop_off = cache.get_or_load("ui_dropOff", "ui/ui_dropDownOff.jaz").texture
|
|
61
|
+
self._check_on = cache.get_or_load("ui_checkOn", "ui/ui_checkOn.jaz").texture
|
|
62
|
+
self._check_off = cache.get_or_load("ui_checkOff", "ui/ui_checkOff.jaz").texture
|
|
33
63
|
|
|
34
64
|
def draw(self) -> None:
|
|
35
|
-
|
|
36
|
-
if self._ground is not None:
|
|
37
|
-
self._ground.draw(0.0, 0.0)
|
|
65
|
+
self._draw_background()
|
|
38
66
|
_draw_screen_fade(self._state)
|
|
39
67
|
assets = self._assets
|
|
40
68
|
entry = self._entry
|
|
@@ -53,9 +81,17 @@ class ControlsMenuView(PanelMenuView):
|
|
|
53
81
|
self._small_font = load_small_font(self._state.assets_dir, missing_assets)
|
|
54
82
|
return self._small_font
|
|
55
83
|
|
|
56
|
-
def
|
|
57
|
-
|
|
84
|
+
def _draw_panel(self) -> None:
|
|
85
|
+
assets = self._assets
|
|
86
|
+
if assets is None or assets.panel is None:
|
|
87
|
+
return
|
|
88
|
+
panel = assets.panel
|
|
89
|
+
|
|
90
|
+
fx_detail = bool(self._state.config.data.get("fx_detail_0", 0))
|
|
91
|
+
panel_scale, _local_y_shift = self._menu_item_scale(0)
|
|
58
92
|
panel_w = MENU_PANEL_WIDTH * panel_scale
|
|
93
|
+
|
|
94
|
+
# Left (controls options) panel: standard 254px height => a single quad.
|
|
59
95
|
_angle_rad, slide_x = MenuView._ui_element_anim(
|
|
60
96
|
self,
|
|
61
97
|
index=1,
|
|
@@ -63,23 +99,93 @@ class ControlsMenuView(PanelMenuView):
|
|
|
63
99
|
end_ms=PANEL_TIMELINE_END_MS,
|
|
64
100
|
width=panel_w,
|
|
65
101
|
)
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
102
|
+
left_x0 = self._panel_pos_x + slide_x + MENU_PANEL_OFFSET_X * panel_scale
|
|
103
|
+
left_y0 = self._panel_pos_y + self._widescreen_y_shift + MENU_PANEL_OFFSET_Y * panel_scale
|
|
104
|
+
left_h = MENU_PANEL_HEIGHT * panel_scale
|
|
105
|
+
draw_classic_menu_panel(panel, dst=rl.Rectangle(left_x0, left_y0, panel_w, left_h), tint=rl.WHITE, shadow=fx_detail)
|
|
106
|
+
|
|
107
|
+
# Right (configured bindings) panel: tall 378px panel rendered as 3 vertical slices.
|
|
108
|
+
_angle_rad, slide_x = MenuView._ui_element_anim(
|
|
109
|
+
self,
|
|
110
|
+
index=3,
|
|
111
|
+
start_ms=PANEL_TIMELINE_START_MS,
|
|
112
|
+
end_ms=PANEL_TIMELINE_END_MS,
|
|
113
|
+
width=panel_w,
|
|
114
|
+
direction_flag=1,
|
|
115
|
+
)
|
|
116
|
+
right_x0 = CONTROLS_RIGHT_PANEL_POS_X + slide_x + MENU_PANEL_OFFSET_X * panel_scale
|
|
117
|
+
right_y0 = CONTROLS_RIGHT_PANEL_POS_Y + self._widescreen_y_shift + MENU_PANEL_OFFSET_Y * panel_scale
|
|
118
|
+
right_h = float(CONTROLS_RIGHT_PANEL_HEIGHT) * panel_scale
|
|
119
|
+
draw_classic_menu_panel(panel, dst=rl.Rectangle(right_x0, right_y0, panel_w, right_h), tint=rl.WHITE, shadow=fx_detail)
|
|
120
|
+
|
|
121
|
+
def _draw_contents(self) -> None:
|
|
122
|
+
# Positions are expressed relative to the panel top-left corners and scaled with the panel scale.
|
|
123
|
+
panel_scale, _local_y_shift = self._menu_item_scale(0)
|
|
124
|
+
|
|
125
|
+
left_x0 = self._panel_pos_x + MENU_PANEL_OFFSET_X * panel_scale
|
|
126
|
+
left_y0 = self._panel_pos_y + self._widescreen_y_shift + MENU_PANEL_OFFSET_Y * panel_scale
|
|
127
|
+
right_x0 = CONTROLS_RIGHT_PANEL_POS_X + MENU_PANEL_OFFSET_X * panel_scale
|
|
128
|
+
right_y0 = CONTROLS_RIGHT_PANEL_POS_Y + self._widescreen_y_shift + MENU_PANEL_OFFSET_Y * panel_scale
|
|
129
|
+
|
|
130
|
+
font = self._ensure_small_font()
|
|
131
|
+
text_color = rl.Color(255, 255, 255, 255)
|
|
132
|
+
|
|
133
|
+
# --- Left panel: "Configure for" + method selectors (state_3 in trace) ---
|
|
134
|
+
if self._text_controls is not None:
|
|
135
|
+
MenuView._draw_ui_quad(
|
|
136
|
+
texture=self._text_controls,
|
|
137
|
+
src=rl.Rectangle(0.0, 0.0, float(self._text_controls.width), float(self._text_controls.height)),
|
|
138
|
+
dst=rl.Rectangle(left_x0 + 206.0 * panel_scale, left_y0 + 44.0 * panel_scale, 128.0 * panel_scale, 32.0 * panel_scale),
|
|
139
|
+
origin=rl.Vector2(0.0, 0.0),
|
|
140
|
+
rotation_deg=0.0,
|
|
141
|
+
tint=rl.WHITE,
|
|
142
|
+
)
|
|
143
|
+
|
|
144
|
+
draw_small_text(font, "Configure for:", left_x0 + 339.0 * panel_scale, left_y0 + 41.0 * panel_scale, 1.0 * panel_scale, text_color)
|
|
145
|
+
draw_small_text(font, f"Player {int(self._config_player)}", left_x0 + 344.0 * panel_scale, left_y0 + 57.0 * panel_scale, 1.0 * panel_scale, text_color)
|
|
146
|
+
|
|
147
|
+
draw_small_text(font, "Aiming method:", left_x0 + 213.0 * panel_scale, left_y0 + 86.0 * panel_scale, 1.0 * panel_scale, text_color)
|
|
148
|
+
draw_small_text(font, "Mouse", left_x0 + 218.0 * panel_scale, left_y0 + 103.0 * panel_scale, 1.0 * panel_scale, text_color)
|
|
149
|
+
|
|
150
|
+
draw_small_text(font, "Moving method:", left_x0 + 213.0 * panel_scale, left_y0 + 128.0 * panel_scale, 1.0 * panel_scale, text_color)
|
|
151
|
+
draw_small_text(font, "Static", left_x0 + 218.0 * panel_scale, left_y0 + 145.0 * panel_scale, 1.0 * panel_scale, text_color)
|
|
152
|
+
|
|
153
|
+
check_tex = self._check_on
|
|
154
|
+
if check_tex is not None:
|
|
155
|
+
MenuView._draw_ui_quad(
|
|
156
|
+
texture=check_tex,
|
|
157
|
+
src=rl.Rectangle(0.0, 0.0, float(check_tex.width), float(check_tex.height)),
|
|
158
|
+
dst=rl.Rectangle(left_x0 + 213.0 * panel_scale, left_y0 + 174.0 * panel_scale, 16.0 * panel_scale, 16.0 * panel_scale),
|
|
159
|
+
origin=rl.Vector2(0.0, 0.0),
|
|
160
|
+
rotation_deg=0.0,
|
|
161
|
+
tint=rl.WHITE,
|
|
162
|
+
)
|
|
163
|
+
draw_small_text(
|
|
164
|
+
font,
|
|
165
|
+
"Show direction arrow",
|
|
166
|
+
left_x0 + 235.0 * panel_scale,
|
|
167
|
+
left_y0 + 175.0 * panel_scale,
|
|
168
|
+
1.0 * panel_scale,
|
|
169
|
+
text_color,
|
|
170
|
+
)
|
|
171
|
+
|
|
172
|
+
drop_tex = self._drop_off
|
|
173
|
+
if drop_tex is not None:
|
|
174
|
+
for ox, oy in (
|
|
175
|
+
(418.0, 56.0), # player dropdown
|
|
176
|
+
(336.0, 102.0), # aiming dropdown
|
|
177
|
+
(336.0, 144.0), # moving dropdown
|
|
178
|
+
):
|
|
179
|
+
MenuView._draw_ui_quad(
|
|
180
|
+
texture=drop_tex,
|
|
181
|
+
src=rl.Rectangle(0.0, 0.0, float(drop_tex.width), float(drop_tex.height)),
|
|
182
|
+
dst=rl.Rectangle(left_x0 + ox * panel_scale, left_y0 + oy * panel_scale, 16.0 * panel_scale, 16.0 * panel_scale),
|
|
183
|
+
origin=rl.Vector2(0.0, 0.0),
|
|
184
|
+
rotation_deg=0.0,
|
|
185
|
+
tint=rl.WHITE,
|
|
186
|
+
)
|
|
187
|
+
|
|
188
|
+
# --- Right panel: configured bindings list ---
|
|
83
189
|
config = self._state.config
|
|
84
190
|
pick_perk_key = int(config.data.get("keybind_pick_perk", 0x101) or 0x101)
|
|
85
191
|
reload_key = int(config.data.get("keybind_reload", 0x102) or 0x102)
|
|
@@ -89,44 +195,25 @@ class ControlsMenuView(PanelMenuView):
|
|
|
89
195
|
keybinds = (0x11, 0x1F, 0x1E, 0x20, 0x100) + (0x17E,) * 11 + (0xC8, 0xD0, 0xCB, 0xCD, 0x9D)
|
|
90
196
|
|
|
91
197
|
p1_up, p1_down, p1_left, p1_right, p1_fire = player_move_fire_binds(keybinds, 0)
|
|
92
|
-
p2_up, p2_down, p2_left, p2_right, p2_fire = player_move_fire_binds(keybinds, 1)
|
|
93
|
-
|
|
94
|
-
return [
|
|
95
|
-
f"Level up: {input_code_name(pick_perk_key)} / Space / KeyPad+",
|
|
96
|
-
f"Reload: {input_code_name(reload_key)}",
|
|
97
|
-
"",
|
|
98
|
-
"Player 1:",
|
|
99
|
-
f" Up: {input_code_name(p1_up)}",
|
|
100
|
-
f" Down: {input_code_name(p1_down)}",
|
|
101
|
-
f" Left: {input_code_name(p1_left)}",
|
|
102
|
-
f" Right: {input_code_name(p1_right)}",
|
|
103
|
-
f" Fire: {input_code_name(p1_fire)}",
|
|
104
|
-
"",
|
|
105
|
-
"Player 2:",
|
|
106
|
-
f" Up: {input_code_name(p2_up)}",
|
|
107
|
-
f" Down: {input_code_name(p2_down)}",
|
|
108
|
-
f" Left: {input_code_name(p2_left)}",
|
|
109
|
-
f" Right: {input_code_name(p2_right)}",
|
|
110
|
-
f" Fire: {input_code_name(p2_fire)}",
|
|
111
|
-
]
|
|
112
198
|
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
font
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
draw_small_text(font,
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
199
|
+
draw_small_text(font, "Configured controls", right_x0 + 120.0 * panel_scale, right_y0 + 38.0 * panel_scale, 1.0 * panel_scale, text_color)
|
|
200
|
+
|
|
201
|
+
draw_small_text(font, "Aiming", right_x0 + 44.0 * panel_scale, right_y0 + 64.0 * panel_scale, 1.0 * panel_scale, text_color)
|
|
202
|
+
draw_small_text(font, "Fire:", right_x0 + 52.0 * panel_scale, right_y0 + 82.0 * panel_scale, 1.0 * panel_scale, text_color)
|
|
203
|
+
draw_small_text(font, input_code_name(p1_fire), right_x0 + 180.0 * panel_scale, right_y0 + 82.0 * panel_scale, 1.0 * panel_scale, text_color)
|
|
204
|
+
|
|
205
|
+
draw_small_text(font, "Moving", right_x0 + 44.0 * panel_scale, right_y0 + 106.0 * panel_scale, 1.0 * panel_scale, text_color)
|
|
206
|
+
draw_small_text(font, "Move Up:", right_x0 + 52.0 * panel_scale, right_y0 + 124.0 * panel_scale, 1.0 * panel_scale, text_color)
|
|
207
|
+
draw_small_text(font, input_code_name(p1_up), right_x0 + 180.0 * panel_scale, right_y0 + 124.0 * panel_scale, 1.0 * panel_scale, text_color)
|
|
208
|
+
draw_small_text(font, "Move Down:", right_x0 + 52.0 * panel_scale, right_y0 + 140.0 * panel_scale, 1.0 * panel_scale, text_color)
|
|
209
|
+
draw_small_text(font, input_code_name(p1_down), right_x0 + 180.0 * panel_scale, right_y0 + 140.0 * panel_scale, 1.0 * panel_scale, text_color)
|
|
210
|
+
draw_small_text(font, "Move Left:", right_x0 + 52.0 * panel_scale, right_y0 + 156.0 * panel_scale, 1.0 * panel_scale, text_color)
|
|
211
|
+
draw_small_text(font, input_code_name(p1_left), right_x0 + 180.0 * panel_scale, right_y0 + 156.0 * panel_scale, 1.0 * panel_scale, text_color)
|
|
212
|
+
draw_small_text(font, "Move Right:", right_x0 + 52.0 * panel_scale, right_y0 + 172.0 * panel_scale, 1.0 * panel_scale, text_color)
|
|
213
|
+
draw_small_text(font, input_code_name(p1_right), right_x0 + 180.0 * panel_scale, right_y0 + 172.0 * panel_scale, 1.0 * panel_scale, text_color)
|
|
214
|
+
|
|
215
|
+
draw_small_text(font, "Misc", right_x0 + 44.0 * panel_scale, right_y0 + 196.0 * panel_scale, 1.0 * panel_scale, text_color)
|
|
216
|
+
draw_small_text(font, "Level Up:", right_x0 + 52.0 * panel_scale, right_y0 + 214.0 * panel_scale, 1.0 * panel_scale, text_color)
|
|
217
|
+
draw_small_text(font, input_code_name(pick_perk_key), right_x0 + 180.0 * panel_scale, right_y0 + 214.0 * panel_scale, 1.0 * panel_scale, text_color)
|
|
218
|
+
draw_small_text(font, "Reload:", right_x0 + 52.0 * panel_scale, right_y0 + 230.0 * panel_scale, 1.0 * panel_scale, text_color)
|
|
219
|
+
draw_small_text(font, input_code_name(reload_key), right_x0 + 180.0 * panel_scale, right_y0 + 230.0 * panel_scale, 1.0 * panel_scale, text_color)
|
|
@@ -0,0 +1,221 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from typing import TYPE_CHECKING
|
|
4
|
+
|
|
5
|
+
import pyray as rl
|
|
6
|
+
|
|
7
|
+
from grim.audio import play_sfx, update_audio
|
|
8
|
+
from grim.fonts.small import SmallFontData, draw_small_text, load_small_font
|
|
9
|
+
|
|
10
|
+
from ...ui.menu_panel import draw_classic_menu_panel
|
|
11
|
+
from ...ui.perk_menu import UiButtonState, UiButtonTextureSet, button_draw, button_update, button_width
|
|
12
|
+
from ..assets import MenuAssets, _ensure_texture_cache, load_menu_assets
|
|
13
|
+
from ..menu import (
|
|
14
|
+
MENU_PANEL_OFFSET_X,
|
|
15
|
+
MENU_PANEL_OFFSET_Y,
|
|
16
|
+
MENU_PANEL_WIDTH,
|
|
17
|
+
MENU_SCALE_SMALL_THRESHOLD,
|
|
18
|
+
MENU_SIGN_HEIGHT,
|
|
19
|
+
MENU_SIGN_OFFSET_X,
|
|
20
|
+
MENU_SIGN_OFFSET_Y,
|
|
21
|
+
MENU_SIGN_POS_X_PAD,
|
|
22
|
+
MENU_SIGN_POS_Y,
|
|
23
|
+
MENU_SIGN_POS_Y_SMALL,
|
|
24
|
+
MENU_SIGN_WIDTH,
|
|
25
|
+
UI_SHADOW_OFFSET,
|
|
26
|
+
MenuView,
|
|
27
|
+
_draw_menu_cursor,
|
|
28
|
+
ensure_menu_ground,
|
|
29
|
+
)
|
|
30
|
+
from ..transitions import _draw_screen_fade
|
|
31
|
+
|
|
32
|
+
if TYPE_CHECKING:
|
|
33
|
+
from ...game import GameState
|
|
34
|
+
from grim.terrain_render import GroundRenderer
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
# Measured from ui_render_trace_oracle_1024x768.json (state_17:credits, timeline=300).
|
|
38
|
+
CREDITS_PANEL_POS_X = -119.0
|
|
39
|
+
CREDITS_PANEL_POS_Y = 185.0
|
|
40
|
+
CREDITS_PANEL_HEIGHT = 378.0
|
|
41
|
+
|
|
42
|
+
# Child layout inside the panel (relative to panel top-left).
|
|
43
|
+
_TITLE_X = 202.0
|
|
44
|
+
_TITLE_Y = 46.0
|
|
45
|
+
|
|
46
|
+
_BACK_BUTTON_X = 298.0
|
|
47
|
+
_BACK_BUTTON_Y = 310.0
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
class CreditsView:
|
|
51
|
+
def __init__(self, state: GameState) -> None:
|
|
52
|
+
self._state = state
|
|
53
|
+
self._assets: MenuAssets | None = None
|
|
54
|
+
self._ground: GroundRenderer | None = None
|
|
55
|
+
self._small_font: SmallFontData | None = None
|
|
56
|
+
self._button_textures: UiButtonTextureSet | None = None
|
|
57
|
+
|
|
58
|
+
self._cursor_pulse_time = 0.0
|
|
59
|
+
self._widescreen_y_shift = 0.0
|
|
60
|
+
self._action: str | None = None
|
|
61
|
+
|
|
62
|
+
self._back_button = UiButtonState("Back", force_wide=False)
|
|
63
|
+
|
|
64
|
+
def open(self) -> None:
|
|
65
|
+
layout_w = float(self._state.config.screen_width)
|
|
66
|
+
self._widescreen_y_shift = MenuView._menu_widescreen_y_shift(layout_w)
|
|
67
|
+
self._assets = load_menu_assets(self._state)
|
|
68
|
+
self._ground = None if self._state.pause_background is not None else ensure_menu_ground(self._state)
|
|
69
|
+
self._small_font = None
|
|
70
|
+
self._cursor_pulse_time = 0.0
|
|
71
|
+
self._action = None
|
|
72
|
+
|
|
73
|
+
cache = _ensure_texture_cache(self._state)
|
|
74
|
+
button_md = cache.get_or_load("ui_buttonMd", "ui/ui_button_128x32.jaz").texture
|
|
75
|
+
button_sm = cache.get_or_load("ui_buttonSm", "ui/ui_button_64x32.jaz").texture
|
|
76
|
+
self._button_textures = UiButtonTextureSet(button_sm=button_sm, button_md=button_md)
|
|
77
|
+
self._back_button = UiButtonState("Back", force_wide=False)
|
|
78
|
+
|
|
79
|
+
if self._state.audio is not None:
|
|
80
|
+
play_sfx(self._state.audio, "sfx_ui_panelclick", rng=self._state.rng)
|
|
81
|
+
|
|
82
|
+
def close(self) -> None:
|
|
83
|
+
if self._small_font is not None:
|
|
84
|
+
rl.unload_texture(self._small_font.texture)
|
|
85
|
+
self._small_font = None
|
|
86
|
+
self._button_textures = None
|
|
87
|
+
self._assets = None
|
|
88
|
+
self._ground = None
|
|
89
|
+
self._action = None
|
|
90
|
+
|
|
91
|
+
def take_action(self) -> str | None:
|
|
92
|
+
action = self._action
|
|
93
|
+
self._action = None
|
|
94
|
+
return action
|
|
95
|
+
|
|
96
|
+
def _ensure_small_font(self) -> SmallFontData:
|
|
97
|
+
if self._small_font is not None:
|
|
98
|
+
return self._small_font
|
|
99
|
+
missing_assets: list[str] = []
|
|
100
|
+
self._small_font = load_small_font(self._state.assets_dir, missing_assets)
|
|
101
|
+
return self._small_font
|
|
102
|
+
|
|
103
|
+
def _panel_top_left(self, *, scale: float) -> tuple[float, float]:
|
|
104
|
+
x0 = CREDITS_PANEL_POS_X + MENU_PANEL_OFFSET_X * scale
|
|
105
|
+
y0 = CREDITS_PANEL_POS_Y + self._widescreen_y_shift + MENU_PANEL_OFFSET_Y * scale
|
|
106
|
+
return float(x0), float(y0)
|
|
107
|
+
|
|
108
|
+
def update(self, dt: float) -> None:
|
|
109
|
+
if self._state.audio is not None:
|
|
110
|
+
update_audio(self._state.audio, dt)
|
|
111
|
+
if self._ground is not None:
|
|
112
|
+
self._ground.process_pending()
|
|
113
|
+
self._cursor_pulse_time += min(float(dt), 0.1) * 1.1
|
|
114
|
+
|
|
115
|
+
if rl.is_key_pressed(rl.KeyboardKey.KEY_ESCAPE):
|
|
116
|
+
if self._state.audio is not None:
|
|
117
|
+
play_sfx(self._state.audio, "sfx_ui_buttonclick", rng=self._state.rng)
|
|
118
|
+
self._action = "back_to_previous"
|
|
119
|
+
return
|
|
120
|
+
|
|
121
|
+
textures = self._button_textures
|
|
122
|
+
if textures is None or (textures.button_md is None and textures.button_sm is None):
|
|
123
|
+
return
|
|
124
|
+
|
|
125
|
+
scale = 0.9 if float(self._state.config.screen_width) < 641.0 else 1.0
|
|
126
|
+
panel_x0, panel_y0 = self._panel_top_left(scale=scale)
|
|
127
|
+
|
|
128
|
+
mouse = rl.get_mouse_position()
|
|
129
|
+
click = rl.is_mouse_button_pressed(rl.MOUSE_BUTTON_LEFT)
|
|
130
|
+
dt_ms = min(float(dt), 0.1) * 1000.0
|
|
131
|
+
|
|
132
|
+
w = button_width(None, self._back_button.label, scale=scale, force_wide=self._back_button.force_wide)
|
|
133
|
+
if button_update(
|
|
134
|
+
self._back_button,
|
|
135
|
+
x=panel_x0 + _BACK_BUTTON_X * scale,
|
|
136
|
+
y=panel_y0 + _BACK_BUTTON_Y * scale,
|
|
137
|
+
width=w,
|
|
138
|
+
dt_ms=dt_ms,
|
|
139
|
+
mouse=mouse,
|
|
140
|
+
click=click,
|
|
141
|
+
):
|
|
142
|
+
if self._state.audio is not None:
|
|
143
|
+
play_sfx(self._state.audio, "sfx_ui_buttonclick", rng=self._state.rng)
|
|
144
|
+
self._action = "back_to_previous"
|
|
145
|
+
|
|
146
|
+
def draw(self) -> None:
|
|
147
|
+
rl.clear_background(rl.BLACK)
|
|
148
|
+
pause_background = self._state.pause_background
|
|
149
|
+
if pause_background is not None:
|
|
150
|
+
pause_background.draw_pause_background()
|
|
151
|
+
elif self._ground is not None:
|
|
152
|
+
self._ground.draw(0.0, 0.0)
|
|
153
|
+
_draw_screen_fade(self._state)
|
|
154
|
+
|
|
155
|
+
assets = self._assets
|
|
156
|
+
if assets is None or assets.panel is None:
|
|
157
|
+
return
|
|
158
|
+
|
|
159
|
+
scale = 0.9 if float(self._state.config.screen_width) < 641.0 else 1.0
|
|
160
|
+
panel_x0, panel_y0 = self._panel_top_left(scale=scale)
|
|
161
|
+
dst = rl.Rectangle(panel_x0, panel_y0, MENU_PANEL_WIDTH * scale, CREDITS_PANEL_HEIGHT * scale)
|
|
162
|
+
fx_detail = bool(self._state.config.data.get("fx_detail_0", 0))
|
|
163
|
+
draw_classic_menu_panel(assets.panel, dst=dst, tint=rl.WHITE, shadow=fx_detail)
|
|
164
|
+
|
|
165
|
+
font = self._ensure_small_font()
|
|
166
|
+
draw_small_text(
|
|
167
|
+
font,
|
|
168
|
+
"credits",
|
|
169
|
+
panel_x0 + _TITLE_X * scale,
|
|
170
|
+
panel_y0 + _TITLE_Y * scale,
|
|
171
|
+
1.0 * scale,
|
|
172
|
+
rl.Color(255, 255, 255, 255),
|
|
173
|
+
)
|
|
174
|
+
|
|
175
|
+
textures = self._button_textures
|
|
176
|
+
if textures is not None and (textures.button_md is not None or textures.button_sm is not None):
|
|
177
|
+
back_w = button_width(None, self._back_button.label, scale=scale, force_wide=self._back_button.force_wide)
|
|
178
|
+
button_draw(
|
|
179
|
+
textures,
|
|
180
|
+
font,
|
|
181
|
+
self._back_button,
|
|
182
|
+
x=panel_x0 + _BACK_BUTTON_X * scale,
|
|
183
|
+
y=panel_y0 + _BACK_BUTTON_Y * scale,
|
|
184
|
+
width=back_w,
|
|
185
|
+
scale=scale,
|
|
186
|
+
)
|
|
187
|
+
|
|
188
|
+
self._draw_sign()
|
|
189
|
+
_draw_menu_cursor(self._state, pulse_time=self._cursor_pulse_time)
|
|
190
|
+
|
|
191
|
+
def _draw_sign(self) -> None:
|
|
192
|
+
assets = self._assets
|
|
193
|
+
if assets is None or assets.sign is None:
|
|
194
|
+
return
|
|
195
|
+
sign = assets.sign
|
|
196
|
+
screen_w = float(self._state.config.screen_width)
|
|
197
|
+
sign_scale, shift_x = MenuView._sign_layout_scale(int(screen_w))
|
|
198
|
+
pos_x = screen_w + MENU_SIGN_POS_X_PAD
|
|
199
|
+
pos_y = MENU_SIGN_POS_Y if screen_w > MENU_SCALE_SMALL_THRESHOLD else MENU_SIGN_POS_Y_SMALL
|
|
200
|
+
sign_w = MENU_SIGN_WIDTH * sign_scale
|
|
201
|
+
sign_h = MENU_SIGN_HEIGHT * sign_scale
|
|
202
|
+
offset_x = MENU_SIGN_OFFSET_X * sign_scale + shift_x
|
|
203
|
+
offset_y = MENU_SIGN_OFFSET_Y * sign_scale
|
|
204
|
+
rotation_deg = 0.0
|
|
205
|
+
fx_detail = bool(self._state.config.data.get("fx_detail_0", 0))
|
|
206
|
+
if fx_detail:
|
|
207
|
+
MenuView._draw_ui_quad_shadow(
|
|
208
|
+
texture=sign,
|
|
209
|
+
src=rl.Rectangle(0.0, 0.0, float(sign.width), float(sign.height)),
|
|
210
|
+
dst=rl.Rectangle(pos_x + UI_SHADOW_OFFSET, pos_y + UI_SHADOW_OFFSET, sign_w, sign_h),
|
|
211
|
+
origin=rl.Vector2(-offset_x, -offset_y),
|
|
212
|
+
rotation_deg=rotation_deg,
|
|
213
|
+
)
|
|
214
|
+
MenuView._draw_ui_quad(
|
|
215
|
+
texture=sign,
|
|
216
|
+
src=rl.Rectangle(0.0, 0.0, float(sign.width), float(sign.height)),
|
|
217
|
+
dst=rl.Rectangle(pos_x, pos_y, sign_w, sign_h),
|
|
218
|
+
origin=rl.Vector2(-offset_x, -offset_y),
|
|
219
|
+
rotation_deg=rotation_deg,
|
|
220
|
+
tint=rl.WHITE,
|
|
221
|
+
)
|