crimsonland 0.1.0.dev12__py3-none-any.whl → 0.1.0.dev14__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/frontend/high_scores_layout.py +26 -0
- crimson/frontend/menu.py +22 -20
- crimson/frontend/panels/base.py +14 -26
- crimson/frontend/panels/controls.py +151 -62
- crimson/frontend/panels/credits.py +221 -0
- crimson/frontend/panels/databases.py +307 -0
- crimson/frontend/panels/options.py +4 -3
- crimson/frontend/panels/play_game.py +4 -4
- crimson/frontend/panels/stats.py +255 -296
- crimson/game.py +219 -81
- crimson/modes/quest_mode.py +10 -9
- crimson/modes/survival_mode.py +10 -9
- crimson/modes/tutorial_mode.py +10 -4
- crimson/ui/menu_panel.py +127 -0
- crimson/ui/perk_menu.py +54 -89
- crimson/ui/quest_results.py +24 -18
- crimson/views/perk_menu_debug.py +2 -2
- crimson/views/perks.py +2 -2
- crimsonland-0.1.0.dev14.dist-info/METADATA +197 -0
- {crimsonland-0.1.0.dev12.dist-info → crimsonland-0.1.0.dev14.dist-info}/RECORD +23 -19
- crimsonland-0.1.0.dev12.dist-info/METADATA +0 -9
- {crimsonland-0.1.0.dev12.dist-info → crimsonland-0.1.0.dev14.dist-info}/WHEEL +0 -0
- {crimsonland-0.1.0.dev12.dist-info → crimsonland-0.1.0.dev14.dist-info}/entry_points.txt +0 -0
|
@@ -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
|
+
)
|
|
@@ -0,0 +1,307 @@
|
|
|
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
|
+
from .base import PANEL_TIMELINE_END_MS, PANEL_TIMELINE_START_MS
|
|
32
|
+
|
|
33
|
+
if TYPE_CHECKING:
|
|
34
|
+
from ...game import GameState
|
|
35
|
+
from grim.terrain_render import GroundRenderer
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
# Shared panel layout (state_14/15/16 in the oracle): tall left panel + short right panel.
|
|
39
|
+
LEFT_PANEL_POS_X = -119.0
|
|
40
|
+
LEFT_PANEL_POS_Y = 185.0
|
|
41
|
+
LEFT_PANEL_HEIGHT = 378.0
|
|
42
|
+
|
|
43
|
+
RIGHT_PANEL_POS_X = 609.0
|
|
44
|
+
RIGHT_PANEL_POS_Y = 200.0
|
|
45
|
+
RIGHT_PANEL_HEIGHT = 254.0
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
class _DatabaseBaseView:
|
|
49
|
+
def __init__(self, state: GameState) -> None:
|
|
50
|
+
self._state = state
|
|
51
|
+
self._assets: MenuAssets | None = None
|
|
52
|
+
self._ground: GroundRenderer | None = None
|
|
53
|
+
self._small_font: SmallFontData | None = None
|
|
54
|
+
self._button_textures: UiButtonTextureSet | None = None
|
|
55
|
+
|
|
56
|
+
self._cursor_pulse_time = 0.0
|
|
57
|
+
self._widescreen_y_shift = 0.0
|
|
58
|
+
self._timeline_ms = 0
|
|
59
|
+
self._timeline_max_ms = PANEL_TIMELINE_START_MS
|
|
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._timeline_ms = 0
|
|
72
|
+
self._timeline_max_ms = PANEL_TIMELINE_START_MS
|
|
73
|
+
self._action = None
|
|
74
|
+
|
|
75
|
+
cache = _ensure_texture_cache(self._state)
|
|
76
|
+
button_md = cache.get_or_load("ui_buttonMd", "ui/ui_button_128x32.jaz").texture
|
|
77
|
+
button_sm = cache.get_or_load("ui_buttonSm", "ui/ui_button_64x32.jaz").texture
|
|
78
|
+
self._button_textures = UiButtonTextureSet(button_sm=button_sm, button_md=button_md)
|
|
79
|
+
self._back_button = UiButtonState("Back", force_wide=False)
|
|
80
|
+
|
|
81
|
+
if self._state.audio is not None:
|
|
82
|
+
play_sfx(self._state.audio, "sfx_ui_panelclick", rng=self._state.rng)
|
|
83
|
+
|
|
84
|
+
def close(self) -> None:
|
|
85
|
+
if self._small_font is not None:
|
|
86
|
+
rl.unload_texture(self._small_font.texture)
|
|
87
|
+
self._small_font = None
|
|
88
|
+
self._button_textures = None
|
|
89
|
+
self._assets = None
|
|
90
|
+
self._ground = None
|
|
91
|
+
self._action = None
|
|
92
|
+
|
|
93
|
+
def take_action(self) -> str | None:
|
|
94
|
+
action = self._action
|
|
95
|
+
self._action = None
|
|
96
|
+
return action
|
|
97
|
+
|
|
98
|
+
def _ensure_small_font(self) -> SmallFontData:
|
|
99
|
+
if self._small_font is not None:
|
|
100
|
+
return self._small_font
|
|
101
|
+
missing_assets: list[str] = []
|
|
102
|
+
self._small_font = load_small_font(self._state.assets_dir, missing_assets)
|
|
103
|
+
return self._small_font
|
|
104
|
+
|
|
105
|
+
def _panel_top_left(self, *, pos_x: float, pos_y: float, scale: float) -> tuple[float, float]:
|
|
106
|
+
x0 = pos_x + MENU_PANEL_OFFSET_X * scale
|
|
107
|
+
y0 = pos_y + self._widescreen_y_shift + MENU_PANEL_OFFSET_Y * scale
|
|
108
|
+
return float(x0), float(y0)
|
|
109
|
+
|
|
110
|
+
def _draw_sign(self) -> None:
|
|
111
|
+
assets = self._assets
|
|
112
|
+
if assets is None or assets.sign is None:
|
|
113
|
+
return
|
|
114
|
+
sign = assets.sign
|
|
115
|
+
screen_w = float(self._state.config.screen_width)
|
|
116
|
+
sign_scale, shift_x = MenuView._sign_layout_scale(int(screen_w))
|
|
117
|
+
pos_x = screen_w + MENU_SIGN_POS_X_PAD
|
|
118
|
+
pos_y = MENU_SIGN_POS_Y if screen_w > MENU_SCALE_SMALL_THRESHOLD else MENU_SIGN_POS_Y_SMALL
|
|
119
|
+
sign_w = MENU_SIGN_WIDTH * sign_scale
|
|
120
|
+
sign_h = MENU_SIGN_HEIGHT * sign_scale
|
|
121
|
+
offset_x = MENU_SIGN_OFFSET_X * sign_scale + shift_x
|
|
122
|
+
offset_y = MENU_SIGN_OFFSET_Y * sign_scale
|
|
123
|
+
rotation_deg = 0.0
|
|
124
|
+
fx_detail = bool(self._state.config.data.get("fx_detail_0", 0))
|
|
125
|
+
if fx_detail:
|
|
126
|
+
MenuView._draw_ui_quad_shadow(
|
|
127
|
+
texture=sign,
|
|
128
|
+
src=rl.Rectangle(0.0, 0.0, float(sign.width), float(sign.height)),
|
|
129
|
+
dst=rl.Rectangle(pos_x + UI_SHADOW_OFFSET, pos_y + UI_SHADOW_OFFSET, sign_w, sign_h),
|
|
130
|
+
origin=rl.Vector2(-offset_x, -offset_y),
|
|
131
|
+
rotation_deg=rotation_deg,
|
|
132
|
+
)
|
|
133
|
+
MenuView._draw_ui_quad(
|
|
134
|
+
texture=sign,
|
|
135
|
+
src=rl.Rectangle(0.0, 0.0, float(sign.width), float(sign.height)),
|
|
136
|
+
dst=rl.Rectangle(pos_x, pos_y, sign_w, sign_h),
|
|
137
|
+
origin=rl.Vector2(-offset_x, -offset_y),
|
|
138
|
+
rotation_deg=rotation_deg,
|
|
139
|
+
tint=rl.WHITE,
|
|
140
|
+
)
|
|
141
|
+
|
|
142
|
+
def update(self, dt: float) -> None:
|
|
143
|
+
if self._state.audio is not None:
|
|
144
|
+
update_audio(self._state.audio, dt)
|
|
145
|
+
if self._ground is not None:
|
|
146
|
+
self._ground.process_pending()
|
|
147
|
+
self._cursor_pulse_time += min(float(dt), 0.1) * 1.1
|
|
148
|
+
|
|
149
|
+
dt_ms = int(min(float(dt), 0.1) * 1000.0)
|
|
150
|
+
if dt_ms > 0:
|
|
151
|
+
self._timeline_ms = min(self._timeline_max_ms, int(self._timeline_ms + dt_ms))
|
|
152
|
+
|
|
153
|
+
enabled = self._timeline_ms >= self._timeline_max_ms
|
|
154
|
+
|
|
155
|
+
if rl.is_key_pressed(rl.KeyboardKey.KEY_ESCAPE) and enabled:
|
|
156
|
+
if self._state.audio is not None:
|
|
157
|
+
play_sfx(self._state.audio, "sfx_ui_buttonclick", rng=self._state.rng)
|
|
158
|
+
self._action = "back_to_previous"
|
|
159
|
+
return
|
|
160
|
+
|
|
161
|
+
textures = self._button_textures
|
|
162
|
+
if textures is None or (textures.button_md is None and textures.button_sm is None):
|
|
163
|
+
return
|
|
164
|
+
if not enabled:
|
|
165
|
+
return
|
|
166
|
+
|
|
167
|
+
scale = 0.9 if float(self._state.config.screen_width) < 641.0 else 1.0
|
|
168
|
+
left_x0, left_y0 = self._panel_top_left(pos_x=LEFT_PANEL_POS_X, pos_y=LEFT_PANEL_POS_Y, scale=scale)
|
|
169
|
+
|
|
170
|
+
mouse = rl.get_mouse_position()
|
|
171
|
+
click = rl.is_mouse_button_pressed(rl.MOUSE_BUTTON_LEFT)
|
|
172
|
+
|
|
173
|
+
bx, by = self._back_button_pos()
|
|
174
|
+
back_w = button_width(None, self._back_button.label, scale=scale, force_wide=self._back_button.force_wide)
|
|
175
|
+
if button_update(
|
|
176
|
+
self._back_button,
|
|
177
|
+
x=left_x0 + float(bx) * scale,
|
|
178
|
+
y=left_y0 + float(by) * scale,
|
|
179
|
+
width=back_w,
|
|
180
|
+
dt_ms=dt_ms,
|
|
181
|
+
mouse=mouse,
|
|
182
|
+
click=click,
|
|
183
|
+
):
|
|
184
|
+
if self._state.audio is not None:
|
|
185
|
+
play_sfx(self._state.audio, "sfx_ui_buttonclick", rng=self._state.rng)
|
|
186
|
+
self._action = "back_to_previous"
|
|
187
|
+
|
|
188
|
+
def draw(self) -> None:
|
|
189
|
+
rl.clear_background(rl.BLACK)
|
|
190
|
+
pause_background = self._state.pause_background
|
|
191
|
+
if pause_background is not None:
|
|
192
|
+
pause_background.draw_pause_background()
|
|
193
|
+
elif self._ground is not None:
|
|
194
|
+
self._ground.draw(0.0, 0.0)
|
|
195
|
+
_draw_screen_fade(self._state)
|
|
196
|
+
|
|
197
|
+
assets = self._assets
|
|
198
|
+
if assets is None or assets.panel is None:
|
|
199
|
+
return
|
|
200
|
+
|
|
201
|
+
scale = 0.9 if float(self._state.config.screen_width) < 641.0 else 1.0
|
|
202
|
+
fx_detail = bool(self._state.config.data.get("fx_detail_0", 0))
|
|
203
|
+
|
|
204
|
+
panel_w = MENU_PANEL_WIDTH * scale
|
|
205
|
+
_angle_rad, left_slide_x = MenuView._ui_element_anim(
|
|
206
|
+
self,
|
|
207
|
+
index=1,
|
|
208
|
+
start_ms=PANEL_TIMELINE_START_MS,
|
|
209
|
+
end_ms=PANEL_TIMELINE_END_MS,
|
|
210
|
+
width=panel_w,
|
|
211
|
+
direction_flag=0,
|
|
212
|
+
)
|
|
213
|
+
_angle_rad, right_slide_x = MenuView._ui_element_anim(
|
|
214
|
+
self,
|
|
215
|
+
index=2,
|
|
216
|
+
start_ms=PANEL_TIMELINE_START_MS,
|
|
217
|
+
end_ms=PANEL_TIMELINE_END_MS,
|
|
218
|
+
width=panel_w,
|
|
219
|
+
direction_flag=1,
|
|
220
|
+
)
|
|
221
|
+
|
|
222
|
+
left_x0, left_y0 = self._panel_top_left(pos_x=LEFT_PANEL_POS_X, pos_y=LEFT_PANEL_POS_Y, scale=scale)
|
|
223
|
+
right_x0, right_y0 = self._panel_top_left(pos_x=RIGHT_PANEL_POS_X, pos_y=RIGHT_PANEL_POS_Y, scale=scale)
|
|
224
|
+
left_x0 += float(left_slide_x)
|
|
225
|
+
right_x0 += float(right_slide_x)
|
|
226
|
+
|
|
227
|
+
draw_classic_menu_panel(
|
|
228
|
+
assets.panel,
|
|
229
|
+
dst=rl.Rectangle(left_x0, left_y0, panel_w, LEFT_PANEL_HEIGHT * scale),
|
|
230
|
+
tint=rl.WHITE,
|
|
231
|
+
shadow=fx_detail,
|
|
232
|
+
)
|
|
233
|
+
draw_classic_menu_panel(
|
|
234
|
+
assets.panel,
|
|
235
|
+
dst=rl.Rectangle(right_x0, right_y0, panel_w, RIGHT_PANEL_HEIGHT * scale),
|
|
236
|
+
tint=rl.WHITE,
|
|
237
|
+
shadow=fx_detail,
|
|
238
|
+
)
|
|
239
|
+
|
|
240
|
+
font = self._ensure_small_font()
|
|
241
|
+
self._draw_contents(left_x0, left_y0, right_x0, right_y0, scale=scale, font=font)
|
|
242
|
+
|
|
243
|
+
textures = self._button_textures
|
|
244
|
+
if textures is not None and (textures.button_md is not None or textures.button_sm is not None):
|
|
245
|
+
bx, by = self._back_button_pos()
|
|
246
|
+
back_w = button_width(None, self._back_button.label, scale=scale, force_wide=self._back_button.force_wide)
|
|
247
|
+
button_draw(
|
|
248
|
+
textures,
|
|
249
|
+
font,
|
|
250
|
+
self._back_button,
|
|
251
|
+
x=left_x0 + float(bx) * scale,
|
|
252
|
+
y=left_y0 + float(by) * scale,
|
|
253
|
+
width=back_w,
|
|
254
|
+
scale=scale,
|
|
255
|
+
)
|
|
256
|
+
|
|
257
|
+
self._draw_sign()
|
|
258
|
+
_draw_menu_cursor(self._state, pulse_time=self._cursor_pulse_time)
|
|
259
|
+
|
|
260
|
+
def _back_button_pos(self) -> tuple[float, float]:
|
|
261
|
+
raise NotImplementedError
|
|
262
|
+
|
|
263
|
+
def _draw_contents(
|
|
264
|
+
self,
|
|
265
|
+
left_x0: float,
|
|
266
|
+
left_y0: float,
|
|
267
|
+
right_x0: float,
|
|
268
|
+
right_y0: float,
|
|
269
|
+
*,
|
|
270
|
+
scale: float,
|
|
271
|
+
font: SmallFontData,
|
|
272
|
+
) -> None:
|
|
273
|
+
raise NotImplementedError
|
|
274
|
+
|
|
275
|
+
|
|
276
|
+
class UnlockedWeaponsDatabaseView(_DatabaseBaseView):
|
|
277
|
+
def _back_button_pos(self) -> tuple[float, float]:
|
|
278
|
+
# state_15: ui_buttonSm bbox [270,507]..[352,539] => relative to left panel (-98,194): (368, 313)
|
|
279
|
+
return (368.0, 313.0)
|
|
280
|
+
|
|
281
|
+
def _draw_contents(self, left_x0: float, left_y0: float, right_x0: float, right_y0: float, *, scale: float, font: SmallFontData) -> None:
|
|
282
|
+
# state_15 title at (153,244) => relative to left panel (-98,194): (251,50)
|
|
283
|
+
draw_small_text(
|
|
284
|
+
font,
|
|
285
|
+
"Unlocked Weapons Database",
|
|
286
|
+
left_x0 + 251.0 * scale,
|
|
287
|
+
left_y0 + 50.0 * scale,
|
|
288
|
+
1.0 * scale,
|
|
289
|
+
rl.Color(255, 255, 255, 255),
|
|
290
|
+
)
|
|
291
|
+
|
|
292
|
+
|
|
293
|
+
class UnlockedPerksDatabaseView(_DatabaseBaseView):
|
|
294
|
+
def _back_button_pos(self) -> tuple[float, float]:
|
|
295
|
+
# state_16: ui_buttonSm bbox [258,509]..[340,541] => relative to left panel (-98,194): (356, 315)
|
|
296
|
+
return (356.0, 315.0)
|
|
297
|
+
|
|
298
|
+
def _draw_contents(self, left_x0: float, left_y0: float, right_x0: float, right_y0: float, *, scale: float, font: SmallFontData) -> None:
|
|
299
|
+
# state_16 title at (163,244) => relative to left panel (-98,194): (261,50)
|
|
300
|
+
draw_small_text(
|
|
301
|
+
font,
|
|
302
|
+
"Unlocked Perks Database",
|
|
303
|
+
left_x0 + 261.0 * scale,
|
|
304
|
+
left_y0 + 50.0 * scale,
|
|
305
|
+
1.0 * scale,
|
|
306
|
+
rl.Color(255, 255, 255, 255),
|
|
307
|
+
)
|
|
@@ -85,6 +85,7 @@ class OptionsMenuView(PanelMenuView):
|
|
|
85
85
|
|
|
86
86
|
config = self._state.config
|
|
87
87
|
layout = self._content_layout()
|
|
88
|
+
base_x = layout["base_x"]
|
|
88
89
|
label_x = layout["label_x"]
|
|
89
90
|
base_y = layout["base_y"]
|
|
90
91
|
scale = layout["scale"]
|
|
@@ -126,8 +127,8 @@ class OptionsMenuView(PanelMenuView):
|
|
|
126
127
|
|
|
127
128
|
textures = self._button_textures
|
|
128
129
|
if textures is not None and textures.button_md is not None:
|
|
129
|
-
# `sub_4475d0`: controls button
|
|
130
|
-
x =
|
|
130
|
+
# `sub_4475d0`: controls button is aligned with the panel content base.
|
|
131
|
+
x = base_x
|
|
131
132
|
y = base_y + 155.0 * scale
|
|
132
133
|
dt_ms = min(float(dt), 0.1) * 1000.0
|
|
133
134
|
mouse = rl.get_mouse_position()
|
|
@@ -373,7 +374,7 @@ class OptionsMenuView(PanelMenuView):
|
|
|
373
374
|
button = self._button_tex
|
|
374
375
|
textures = self._button_textures
|
|
375
376
|
if button is not None and textures is not None:
|
|
376
|
-
button_x =
|
|
377
|
+
button_x = base_x
|
|
377
378
|
button_y = base_y + 155.0 * scale
|
|
378
379
|
button_w = button_width(font, self._controls_button.label, scale=scale, force_wide=self._controls_button.force_wide)
|
|
379
380
|
button_draw(textures, font, self._controls_button, x=button_x, y=button_y, width=button_w, scale=scale)
|
|
@@ -15,8 +15,6 @@ from ..menu import (
|
|
|
15
15
|
MENU_LABEL_ROW_HEIGHT,
|
|
16
16
|
MENU_LABEL_ROW_PLAY_GAME,
|
|
17
17
|
MENU_LABEL_WIDTH,
|
|
18
|
-
MENU_PANEL_OFFSET_X,
|
|
19
|
-
MENU_PANEL_OFFSET_Y,
|
|
20
18
|
MENU_PANEL_WIDTH,
|
|
21
19
|
MenuView,
|
|
22
20
|
_draw_menu_cursor,
|
|
@@ -50,6 +48,8 @@ class PlayGameMenuView(PanelMenuView):
|
|
|
50
48
|
super().__init__(
|
|
51
49
|
state,
|
|
52
50
|
title="Play Game",
|
|
51
|
+
panel_offset_x=-63.0,
|
|
52
|
+
panel_height=278.0,
|
|
53
53
|
back_pos_y=462.0,
|
|
54
54
|
)
|
|
55
55
|
self._small_font: SmallFontData | None = None
|
|
@@ -211,8 +211,8 @@ class PlayGameMenuView(PanelMenuView):
|
|
|
211
211
|
)
|
|
212
212
|
panel_x = self._panel_pos_x + slide_x
|
|
213
213
|
panel_y = self._panel_pos_y + self._widescreen_y_shift
|
|
214
|
-
origin_x = -(
|
|
215
|
-
origin_y = -(
|
|
214
|
+
origin_x = -(self._panel_offset_x * panel_scale)
|
|
215
|
+
origin_y = -(self._panel_offset_y * panel_scale)
|
|
216
216
|
panel_left = panel_x - origin_x
|
|
217
217
|
panel_top = panel_y - origin_y
|
|
218
218
|
|