crimsonland 0.1.0.dev14__py3-none-any.whl → 0.1.0.dev16__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/cli.py +63 -0
- crimson/creatures/damage.py +111 -36
- crimson/creatures/runtime.py +246 -156
- crimson/creatures/spawn.py +7 -3
- crimson/debug.py +9 -0
- crimson/demo.py +38 -45
- crimson/effects.py +7 -13
- crimson/frontend/high_scores_layout.py +81 -0
- crimson/frontend/panels/base.py +4 -1
- crimson/frontend/panels/controls.py +0 -15
- crimson/frontend/panels/databases.py +291 -3
- crimson/frontend/panels/mods.py +0 -15
- crimson/frontend/panels/play_game.py +0 -16
- crimson/game.py +689 -3
- crimson/gameplay.py +921 -569
- crimson/modes/base_gameplay_mode.py +33 -12
- crimson/modes/components/__init__.py +2 -0
- crimson/modes/components/highscore_record_builder.py +58 -0
- crimson/modes/components/perk_menu_controller.py +325 -0
- crimson/modes/quest_mode.py +94 -272
- crimson/modes/rush_mode.py +12 -43
- crimson/modes/survival_mode.py +109 -330
- crimson/modes/tutorial_mode.py +46 -247
- crimson/modes/typo_mode.py +11 -38
- crimson/oracle.py +396 -0
- crimson/perks.py +5 -2
- crimson/player_damage.py +95 -36
- crimson/projectiles.py +539 -320
- crimson/render/projectile_draw_registry.py +637 -0
- crimson/render/projectile_render_registry.py +110 -0
- crimson/render/secondary_projectile_draw_registry.py +206 -0
- crimson/render/world_renderer.py +58 -707
- crimson/sim/world_state.py +118 -61
- crimson/typo/spawns.py +5 -12
- crimson/ui/demo_trial_overlay.py +3 -11
- crimson/ui/formatting.py +24 -0
- crimson/ui/game_over.py +12 -58
- crimson/ui/hud.py +72 -39
- crimson/ui/layout.py +20 -0
- crimson/ui/perk_menu.py +9 -34
- crimson/ui/quest_results.py +28 -70
- crimson/ui/text_input.py +20 -0
- crimson/views/_ui_helpers.py +27 -0
- crimson/views/aim_debug.py +15 -32
- crimson/views/animations.py +18 -28
- crimson/views/arsenal_debug.py +22 -32
- crimson/views/bonuses.py +23 -36
- crimson/views/camera_debug.py +16 -29
- crimson/views/camera_shake.py +9 -33
- crimson/views/corpse_stamp_debug.py +13 -21
- crimson/views/decals_debug.py +36 -23
- crimson/views/fonts.py +8 -25
- crimson/views/ground.py +4 -21
- crimson/views/lighting_debug.py +42 -45
- crimson/views/particles.py +33 -42
- crimson/views/perk_menu_debug.py +3 -10
- crimson/views/player.py +50 -44
- crimson/views/player_sprite_debug.py +24 -31
- crimson/views/projectile_fx.py +57 -52
- crimson/views/projectile_render_debug.py +24 -33
- crimson/views/projectiles.py +24 -37
- crimson/views/spawn_plan.py +13 -29
- crimson/views/sprites.py +14 -29
- crimson/views/terrain.py +6 -23
- crimson/views/ui.py +7 -24
- crimson/views/wicons.py +28 -33
- {crimsonland-0.1.0.dev14.dist-info → crimsonland-0.1.0.dev16.dist-info}/METADATA +1 -1
- {crimsonland-0.1.0.dev14.dist-info → crimsonland-0.1.0.dev16.dist-info}/RECORD +73 -62
- {crimsonland-0.1.0.dev14.dist-info → crimsonland-0.1.0.dev16.dist-info}/WHEEL +1 -1
- grim/config.py +29 -1
- grim/console.py +7 -10
- grim/math.py +12 -0
- {crimsonland-0.1.0.dev14.dist-info → crimsonland-0.1.0.dev16.dist-info}/entry_points.txt +0 -0
crimson/modes/quest_mode.py
CHANGED
|
@@ -10,13 +10,14 @@ from grim.audio import AudioState
|
|
|
10
10
|
from grim.console import ConsoleState
|
|
11
11
|
from grim.config import CrimsonConfig
|
|
12
12
|
from grim.fonts.grim_mono import GrimMonoFont, load_grim_mono_font
|
|
13
|
+
from grim.math import clamp
|
|
13
14
|
from grim.view import ViewContext
|
|
14
15
|
|
|
16
|
+
from ..debug import debug_enabled
|
|
15
17
|
from ..game_modes import GameMode
|
|
16
|
-
from ..gameplay import most_used_weapon_id_for_player,
|
|
18
|
+
from ..gameplay import most_used_weapon_id_for_player, weapon_assign_player
|
|
17
19
|
from ..input_codes import config_keybinds, input_code_is_down, input_code_is_pressed, player_move_fire_binds
|
|
18
20
|
from ..persistence.save_status import GameStatus
|
|
19
|
-
from ..perks import PerkId, perk_display_description, perk_display_name
|
|
20
21
|
from ..quests import quest_by_level
|
|
21
22
|
from ..quests.runtime import build_quest_spawn_table, tick_quest_completion_transition
|
|
22
23
|
from ..quests.timeline import quest_spawn_table_empty, tick_quest_mode_spawns
|
|
@@ -24,27 +25,11 @@ from ..quests.types import QuestContext, QuestDefinition, SpawnEntry
|
|
|
24
25
|
from ..terrain_assets import terrain_texture_by_id
|
|
25
26
|
from ..ui.cursor import draw_aim_cursor, draw_menu_cursor
|
|
26
27
|
from ..ui.hud import draw_hud_overlay, hud_flags_for_game_mode
|
|
27
|
-
from ..ui.
|
|
28
|
-
from ..ui.perk_menu import (
|
|
29
|
-
PERK_MENU_TRANSITION_MS,
|
|
30
|
-
PerkMenuAssets,
|
|
31
|
-
PerkMenuLayout,
|
|
32
|
-
UiButtonState,
|
|
33
|
-
button_draw,
|
|
34
|
-
button_update,
|
|
35
|
-
button_width,
|
|
36
|
-
draw_menu_item,
|
|
37
|
-
draw_ui_text,
|
|
38
|
-
load_perk_menu_assets,
|
|
39
|
-
menu_item_hit_rect,
|
|
40
|
-
perk_menu_panel_slide_x,
|
|
41
|
-
perk_menu_compute_layout,
|
|
42
|
-
ui_origin,
|
|
43
|
-
ui_scale,
|
|
44
|
-
wrap_ui_text,
|
|
45
|
-
)
|
|
28
|
+
from ..ui.perk_menu import PerkMenuAssets, draw_ui_text, load_perk_menu_assets
|
|
46
29
|
from ..views.quest_title_overlay import draw_quest_title_overlay
|
|
47
|
-
from
|
|
30
|
+
from ..weapons import WEAPON_BY_ID
|
|
31
|
+
from .base_gameplay_mode import BaseGameplayMode
|
|
32
|
+
from .components.perk_menu_controller import PerkMenuContext, PerkMenuController
|
|
48
33
|
|
|
49
34
|
WORLD_SIZE = 1024.0
|
|
50
35
|
QUEST_TITLE_FADE_IN_MS = 500.0
|
|
@@ -78,6 +63,8 @@ PERK_PROMPT_LEVEL_UP_SHIFT_X = -46.0
|
|
|
78
63
|
PERK_PROMPT_LEVEL_UP_SHIFT_Y = -4.0
|
|
79
64
|
|
|
80
65
|
PERK_PROMPT_TEXT_MARGIN_X = 16.0
|
|
66
|
+
|
|
67
|
+
_DEBUG_WEAPON_IDS = tuple(sorted(WEAPON_BY_ID))
|
|
81
68
|
PERK_PROMPT_TEXT_OFFSET_Y = 8.0
|
|
82
69
|
|
|
83
70
|
|
|
@@ -173,11 +160,7 @@ class QuestMode(BaseGameplayMode):
|
|
|
173
160
|
self._perk_prompt_timer_ms = 0.0
|
|
174
161
|
self._perk_prompt_hover = False
|
|
175
162
|
self._perk_prompt_pulse = 0.0
|
|
176
|
-
self.
|
|
177
|
-
self._perk_menu_selected = 0
|
|
178
|
-
self._perk_menu_timeline_ms = 0.0
|
|
179
|
-
self._perk_ui_layout = PerkMenuLayout()
|
|
180
|
-
self._perk_cancel_button = UiButtonState("Cancel")
|
|
163
|
+
self._perk_menu = PerkMenuController(on_close=self._reset_perk_prompt)
|
|
181
164
|
|
|
182
165
|
def open(self) -> None:
|
|
183
166
|
super().open()
|
|
@@ -194,11 +177,7 @@ class QuestMode(BaseGameplayMode):
|
|
|
194
177
|
self._perk_prompt_timer_ms = 0.0
|
|
195
178
|
self._perk_prompt_hover = False
|
|
196
179
|
self._perk_prompt_pulse = 0.0
|
|
197
|
-
self.
|
|
198
|
-
self._perk_menu_selected = 0
|
|
199
|
-
self._perk_menu_timeline_ms = 0.0
|
|
200
|
-
self._perk_ui_layout = PerkMenuLayout()
|
|
201
|
-
self._perk_cancel_button = UiButtonState("Cancel")
|
|
180
|
+
self._perk_menu.reset()
|
|
202
181
|
|
|
203
182
|
def close(self) -> None:
|
|
204
183
|
if self._grim_mono is not None:
|
|
@@ -207,6 +186,33 @@ class QuestMode(BaseGameplayMode):
|
|
|
207
186
|
self._perk_menu_assets = None
|
|
208
187
|
super().close()
|
|
209
188
|
|
|
189
|
+
def _reset_perk_prompt(self) -> None:
|
|
190
|
+
if int(self._state.perk_selection.pending_count) > 0:
|
|
191
|
+
# Reset the prompt swing so each pending perk replays the intro.
|
|
192
|
+
self._perk_prompt_timer_ms = 0.0
|
|
193
|
+
self._perk_prompt_hover = False
|
|
194
|
+
self._perk_prompt_pulse = 0.0
|
|
195
|
+
|
|
196
|
+
def _perk_menu_context(self) -> PerkMenuContext:
|
|
197
|
+
fx_toggle = int(self._config.data.get("fx_toggle", 0) or 0) if self._config is not None else 0
|
|
198
|
+
fx_detail = bool(int(self._config.data.get("fx_detail_0", 0) or 0)) if self._config is not None else False
|
|
199
|
+
players = self._world.players
|
|
200
|
+
return PerkMenuContext(
|
|
201
|
+
state=self._state,
|
|
202
|
+
perk_state=self._state.perk_selection,
|
|
203
|
+
players=players,
|
|
204
|
+
creatures=self._creatures.entries,
|
|
205
|
+
player=self._player,
|
|
206
|
+
game_mode=int(GameMode.QUESTS),
|
|
207
|
+
player_count=len(players),
|
|
208
|
+
fx_toggle=fx_toggle,
|
|
209
|
+
fx_detail=fx_detail,
|
|
210
|
+
font=self._small,
|
|
211
|
+
assets=self._perk_menu_assets,
|
|
212
|
+
mouse=self._ui_mouse_pos(),
|
|
213
|
+
play_sfx=self._world.audio_router.play_sfx,
|
|
214
|
+
)
|
|
215
|
+
|
|
210
216
|
def select_level(self, level: str | None) -> None:
|
|
211
217
|
self._selected_level = level
|
|
212
218
|
|
|
@@ -297,18 +303,43 @@ class QuestMode(BaseGameplayMode):
|
|
|
297
303
|
status.increment_quest_play_count(idx)
|
|
298
304
|
|
|
299
305
|
def _handle_input(self) -> None:
|
|
300
|
-
if self.
|
|
306
|
+
if self._perk_menu.open and rl.is_key_pressed(rl.KeyboardKey.KEY_ESCAPE):
|
|
301
307
|
self._world.audio_router.play_sfx("sfx_ui_buttonclick")
|
|
302
|
-
self.
|
|
308
|
+
self._perk_menu.close()
|
|
303
309
|
return
|
|
304
310
|
|
|
305
311
|
if rl.is_key_pressed(rl.KeyboardKey.KEY_TAB):
|
|
306
312
|
self._paused = not self._paused
|
|
307
313
|
|
|
314
|
+
if debug_enabled() and (not self._perk_menu.open):
|
|
315
|
+
if rl.is_key_pressed(rl.KeyboardKey.KEY_F2):
|
|
316
|
+
self._state.debug_god_mode = not bool(self._state.debug_god_mode)
|
|
317
|
+
self._world.audio_router.play_sfx("sfx_ui_buttonclick")
|
|
318
|
+
if rl.is_key_pressed(rl.KeyboardKey.KEY_F3):
|
|
319
|
+
self._state.perk_selection.pending_count += 1
|
|
320
|
+
self._state.perk_selection.choices_dirty = True
|
|
321
|
+
self._world.audio_router.play_sfx("sfx_ui_levelup")
|
|
322
|
+
if rl.is_key_pressed(rl.KeyboardKey.KEY_LEFT_BRACKET):
|
|
323
|
+
self._debug_cycle_weapon(-1)
|
|
324
|
+
if rl.is_key_pressed(rl.KeyboardKey.KEY_RIGHT_BRACKET):
|
|
325
|
+
self._debug_cycle_weapon(1)
|
|
326
|
+
|
|
308
327
|
if rl.is_key_pressed(rl.KeyboardKey.KEY_ESCAPE):
|
|
309
328
|
self._action = "open_pause_menu"
|
|
310
329
|
return
|
|
311
330
|
|
|
331
|
+
def _debug_cycle_weapon(self, delta: int) -> None:
|
|
332
|
+
weapon_ids = _DEBUG_WEAPON_IDS
|
|
333
|
+
if not weapon_ids:
|
|
334
|
+
return
|
|
335
|
+
current = int(self._player.weapon_id)
|
|
336
|
+
try:
|
|
337
|
+
idx = weapon_ids.index(current)
|
|
338
|
+
except ValueError:
|
|
339
|
+
idx = 0
|
|
340
|
+
weapon_id = int(weapon_ids[(idx + int(delta)) % len(weapon_ids)])
|
|
341
|
+
weapon_assign_player(self._player, weapon_id, state=self._state)
|
|
342
|
+
|
|
312
343
|
def _build_input(self):
|
|
313
344
|
keybinds = config_keybinds(self._config)
|
|
314
345
|
if not keybinds:
|
|
@@ -385,139 +416,6 @@ class QuestMode(BaseGameplayMode):
|
|
|
385
416
|
y = margin
|
|
386
417
|
return rl.Rectangle(x, y, text_w, text_h)
|
|
387
418
|
|
|
388
|
-
def _open_perk_menu(self) -> None:
|
|
389
|
-
if self._perk_menu_open:
|
|
390
|
-
return
|
|
391
|
-
players = self._world.players
|
|
392
|
-
choices = perk_selection_current_choices(
|
|
393
|
-
self._state,
|
|
394
|
-
players,
|
|
395
|
-
self._state.perk_selection,
|
|
396
|
-
game_mode=int(GameMode.QUESTS),
|
|
397
|
-
player_count=len(players),
|
|
398
|
-
)
|
|
399
|
-
if not choices:
|
|
400
|
-
self._perk_menu_open = False
|
|
401
|
-
return
|
|
402
|
-
self._world.audio_router.play_sfx("sfx_ui_panelclick")
|
|
403
|
-
self._perk_menu_open = True
|
|
404
|
-
self._perk_menu_selected = 0
|
|
405
|
-
|
|
406
|
-
def _close_perk_menu(self) -> None:
|
|
407
|
-
self._perk_menu_open = False
|
|
408
|
-
if int(self._state.perk_selection.pending_count) > 0:
|
|
409
|
-
# Reset the prompt swing so each pending perk replays the intro.
|
|
410
|
-
self._perk_prompt_timer_ms = 0.0
|
|
411
|
-
self._perk_prompt_hover = False
|
|
412
|
-
self._perk_prompt_pulse = 0.0
|
|
413
|
-
|
|
414
|
-
def _perk_menu_handle_input(self, dt_frame: float, dt_ms: float) -> None:
|
|
415
|
-
if self._perk_menu_assets is None:
|
|
416
|
-
self._close_perk_menu()
|
|
417
|
-
return
|
|
418
|
-
|
|
419
|
-
perk_state = self._state.perk_selection
|
|
420
|
-
players = self._world.players
|
|
421
|
-
choices = perk_selection_current_choices(
|
|
422
|
-
self._state,
|
|
423
|
-
players,
|
|
424
|
-
perk_state,
|
|
425
|
-
game_mode=int(GameMode.QUESTS),
|
|
426
|
-
player_count=len(players),
|
|
427
|
-
)
|
|
428
|
-
if not choices:
|
|
429
|
-
self._close_perk_menu()
|
|
430
|
-
return
|
|
431
|
-
if self._perk_menu_selected >= len(choices):
|
|
432
|
-
self._perk_menu_selected = 0
|
|
433
|
-
|
|
434
|
-
if rl.is_key_pressed(rl.KeyboardKey.KEY_DOWN):
|
|
435
|
-
self._perk_menu_selected = (self._perk_menu_selected + 1) % len(choices)
|
|
436
|
-
if rl.is_key_pressed(rl.KeyboardKey.KEY_UP):
|
|
437
|
-
self._perk_menu_selected = (self._perk_menu_selected - 1) % len(choices)
|
|
438
|
-
|
|
439
|
-
screen_w = float(rl.get_screen_width())
|
|
440
|
-
screen_h = float(rl.get_screen_height())
|
|
441
|
-
scale = ui_scale(screen_w, screen_h)
|
|
442
|
-
origin_x, origin_y = ui_origin(screen_w, screen_h, scale)
|
|
443
|
-
slide_x = perk_menu_panel_slide_x(self._perk_menu_timeline_ms, width=self._perk_ui_layout.panel_w)
|
|
444
|
-
|
|
445
|
-
mouse = self._ui_mouse_pos()
|
|
446
|
-
click = rl.is_mouse_button_pressed(rl.MouseButton.MOUSE_BUTTON_LEFT)
|
|
447
|
-
|
|
448
|
-
master_owned = int(self._player.perk_counts[int(PerkId.PERK_MASTER)]) > 0
|
|
449
|
-
expert_owned = int(self._player.perk_counts[int(PerkId.PERK_EXPERT)]) > 0
|
|
450
|
-
computed = perk_menu_compute_layout(
|
|
451
|
-
self._perk_ui_layout,
|
|
452
|
-
screen_w=screen_w,
|
|
453
|
-
origin_x=origin_x,
|
|
454
|
-
origin_y=origin_y,
|
|
455
|
-
scale=scale,
|
|
456
|
-
choice_count=len(choices),
|
|
457
|
-
expert_owned=expert_owned,
|
|
458
|
-
master_owned=master_owned,
|
|
459
|
-
panel_slide_x=slide_x,
|
|
460
|
-
)
|
|
461
|
-
|
|
462
|
-
fx_toggle = int(self._config.data.get("fx_toggle", 0) or 0) if self._config is not None else 0
|
|
463
|
-
for idx, perk_id in enumerate(choices):
|
|
464
|
-
label = perk_display_name(int(perk_id), fx_toggle=fx_toggle)
|
|
465
|
-
item_x = computed.list_x
|
|
466
|
-
item_y = computed.list_y + float(idx) * computed.list_step_y
|
|
467
|
-
rect = menu_item_hit_rect(self._small, label, x=item_x, y=item_y, scale=scale)
|
|
468
|
-
if rl.check_collision_point_rec(mouse, rect):
|
|
469
|
-
self._perk_menu_selected = idx
|
|
470
|
-
if click:
|
|
471
|
-
self._world.audio_router.play_sfx("sfx_ui_buttonclick")
|
|
472
|
-
picked = perk_selection_pick(
|
|
473
|
-
self._state,
|
|
474
|
-
players,
|
|
475
|
-
perk_state,
|
|
476
|
-
idx,
|
|
477
|
-
game_mode=int(GameMode.QUESTS),
|
|
478
|
-
player_count=len(players),
|
|
479
|
-
dt=dt_frame,
|
|
480
|
-
creatures=self._creatures.entries,
|
|
481
|
-
)
|
|
482
|
-
if picked is not None:
|
|
483
|
-
self._world.audio_router.play_sfx("sfx_ui_bonus")
|
|
484
|
-
self._close_perk_menu()
|
|
485
|
-
return
|
|
486
|
-
break
|
|
487
|
-
|
|
488
|
-
cancel_w = button_width(self._small, self._perk_cancel_button.label, scale=scale, force_wide=self._perk_cancel_button.force_wide)
|
|
489
|
-
cancel_x = computed.cancel_x
|
|
490
|
-
button_y = computed.cancel_y
|
|
491
|
-
|
|
492
|
-
if button_update(
|
|
493
|
-
self._perk_cancel_button,
|
|
494
|
-
x=cancel_x,
|
|
495
|
-
y=button_y,
|
|
496
|
-
width=cancel_w,
|
|
497
|
-
dt_ms=dt_ms,
|
|
498
|
-
mouse=mouse,
|
|
499
|
-
click=click,
|
|
500
|
-
):
|
|
501
|
-
self._world.audio_router.play_sfx("sfx_ui_buttonclick")
|
|
502
|
-
self._close_perk_menu()
|
|
503
|
-
return
|
|
504
|
-
|
|
505
|
-
if rl.is_key_pressed(rl.KeyboardKey.KEY_ENTER) or rl.is_key_pressed(rl.KeyboardKey.KEY_SPACE):
|
|
506
|
-
self._world.audio_router.play_sfx("sfx_ui_buttonclick")
|
|
507
|
-
picked = perk_selection_pick(
|
|
508
|
-
self._state,
|
|
509
|
-
players,
|
|
510
|
-
perk_state,
|
|
511
|
-
self._perk_menu_selected,
|
|
512
|
-
game_mode=int(GameMode.QUESTS),
|
|
513
|
-
player_count=len(players),
|
|
514
|
-
dt=dt_frame,
|
|
515
|
-
creatures=self._creatures.entries,
|
|
516
|
-
)
|
|
517
|
-
if picked is not None:
|
|
518
|
-
self._world.audio_router.play_sfx("sfx_ui_bonus")
|
|
519
|
-
self._close_perk_menu()
|
|
520
|
-
|
|
521
419
|
def _close_failed_run(self) -> None:
|
|
522
420
|
if self._outcome is None:
|
|
523
421
|
fired = 0
|
|
@@ -555,7 +453,7 @@ class QuestMode(BaseGameplayMode):
|
|
|
555
453
|
self.close_requested = True
|
|
556
454
|
|
|
557
455
|
def _draw_perk_prompt(self) -> None:
|
|
558
|
-
if self.
|
|
456
|
+
if self._perk_menu.active:
|
|
559
457
|
return
|
|
560
458
|
if not any(player.health > 0.0 for player in self._world.players):
|
|
561
459
|
return
|
|
@@ -611,94 +509,6 @@ class QuestMode(BaseGameplayMode):
|
|
|
611
509
|
rl.draw_texture_pro(tex, src, dst, origin, rot_deg, pulse_tint)
|
|
612
510
|
rl.end_blend_mode()
|
|
613
511
|
|
|
614
|
-
def _draw_perk_menu(self) -> None:
|
|
615
|
-
menu_t = _clamp(self._perk_menu_timeline_ms / PERK_MENU_TRANSITION_MS, 0.0, 1.0)
|
|
616
|
-
if menu_t <= 1e-3:
|
|
617
|
-
return
|
|
618
|
-
if self._perk_menu_assets is None:
|
|
619
|
-
return
|
|
620
|
-
|
|
621
|
-
perk_state = self._state.perk_selection
|
|
622
|
-
players = self._world.players
|
|
623
|
-
choices = perk_selection_current_choices(
|
|
624
|
-
self._state,
|
|
625
|
-
players,
|
|
626
|
-
perk_state,
|
|
627
|
-
game_mode=int(GameMode.QUESTS),
|
|
628
|
-
player_count=len(players),
|
|
629
|
-
)
|
|
630
|
-
if not choices:
|
|
631
|
-
return
|
|
632
|
-
|
|
633
|
-
screen_w = float(rl.get_screen_width())
|
|
634
|
-
screen_h = float(rl.get_screen_height())
|
|
635
|
-
scale = ui_scale(screen_w, screen_h)
|
|
636
|
-
origin_x, origin_y = ui_origin(screen_w, screen_h, scale)
|
|
637
|
-
slide_x = perk_menu_panel_slide_x(self._perk_menu_timeline_ms, width=self._perk_ui_layout.panel_w)
|
|
638
|
-
|
|
639
|
-
master_owned = int(self._player.perk_counts[int(PerkId.PERK_MASTER)]) > 0
|
|
640
|
-
expert_owned = int(self._player.perk_counts[int(PerkId.PERK_EXPERT)]) > 0
|
|
641
|
-
computed = perk_menu_compute_layout(
|
|
642
|
-
self._perk_ui_layout,
|
|
643
|
-
screen_w=screen_w,
|
|
644
|
-
origin_x=origin_x,
|
|
645
|
-
origin_y=origin_y,
|
|
646
|
-
scale=scale,
|
|
647
|
-
choice_count=len(choices),
|
|
648
|
-
expert_owned=expert_owned,
|
|
649
|
-
master_owned=master_owned,
|
|
650
|
-
panel_slide_x=slide_x,
|
|
651
|
-
)
|
|
652
|
-
|
|
653
|
-
panel_tex = self._perk_menu_assets.menu_panel
|
|
654
|
-
if panel_tex is not None:
|
|
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)
|
|
657
|
-
|
|
658
|
-
title_tex = self._perk_menu_assets.title_pick_perk
|
|
659
|
-
if title_tex is not None:
|
|
660
|
-
src = rl.Rectangle(0.0, 0.0, float(title_tex.width), float(title_tex.height))
|
|
661
|
-
rl.draw_texture_pro(title_tex, src, computed.title, rl.Vector2(0.0, 0.0), 0.0, rl.WHITE)
|
|
662
|
-
|
|
663
|
-
sponsor = None
|
|
664
|
-
if master_owned:
|
|
665
|
-
sponsor = "extra perks sponsored by the Perk Master"
|
|
666
|
-
elif expert_owned:
|
|
667
|
-
sponsor = "extra perk sponsored by the Perk Expert"
|
|
668
|
-
if sponsor:
|
|
669
|
-
draw_ui_text(self._small, sponsor, computed.sponsor_x, computed.sponsor_y, scale=scale, color=UI_SPONSOR_COLOR)
|
|
670
|
-
|
|
671
|
-
mouse = self._ui_mouse_pos()
|
|
672
|
-
fx_toggle = int(self._config.data.get("fx_toggle", 0) or 0) if self._config is not None else 0
|
|
673
|
-
for idx, perk_id in enumerate(choices):
|
|
674
|
-
label = perk_display_name(int(perk_id), fx_toggle=fx_toggle)
|
|
675
|
-
item_x = computed.list_x
|
|
676
|
-
item_y = computed.list_y + float(idx) * computed.list_step_y
|
|
677
|
-
rect = menu_item_hit_rect(self._small, label, x=item_x, y=item_y, scale=scale)
|
|
678
|
-
hovered = rl.check_collision_point_rec(mouse, rect) or (idx == self._perk_menu_selected)
|
|
679
|
-
draw_menu_item(self._small, label, x=item_x, y=item_y, scale=scale, hovered=hovered)
|
|
680
|
-
|
|
681
|
-
selected = choices[self._perk_menu_selected]
|
|
682
|
-
desc = perk_display_description(int(selected), fx_toggle=fx_toggle)
|
|
683
|
-
desc_x = float(computed.desc.x)
|
|
684
|
-
desc_y = float(computed.desc.y)
|
|
685
|
-
desc_w = float(computed.desc.width)
|
|
686
|
-
desc_h = float(computed.desc.height)
|
|
687
|
-
desc_scale = scale * 0.85
|
|
688
|
-
desc_lines = wrap_ui_text(self._small, desc, max_width=desc_w, scale=desc_scale)
|
|
689
|
-
line_h = float(self._small.cell_size * desc_scale) if self._small is not None else float(20 * desc_scale)
|
|
690
|
-
y = desc_y
|
|
691
|
-
for line in desc_lines:
|
|
692
|
-
if y + line_h > desc_y + desc_h:
|
|
693
|
-
break
|
|
694
|
-
draw_ui_text(self._small, line, desc_x, y, scale=desc_scale, color=UI_TEXT_COLOR)
|
|
695
|
-
y += line_h
|
|
696
|
-
|
|
697
|
-
cancel_w = button_width(self._small, self._perk_cancel_button.label, scale=scale, force_wide=self._perk_cancel_button.force_wide)
|
|
698
|
-
cancel_x = computed.cancel_x
|
|
699
|
-
button_y = computed.cancel_y
|
|
700
|
-
button_draw(self._perk_menu_assets, self._small, self._perk_cancel_button, x=cancel_x, y=button_y, width=cancel_w, scale=scale)
|
|
701
|
-
|
|
702
512
|
def update(self, dt: float) -> None:
|
|
703
513
|
self._update_audio(dt)
|
|
704
514
|
|
|
@@ -714,10 +524,11 @@ class QuestMode(BaseGameplayMode):
|
|
|
714
524
|
perk_pending = int(self._state.perk_selection.pending_count) > 0 and any_alive
|
|
715
525
|
|
|
716
526
|
self._perk_prompt_hover = False
|
|
717
|
-
|
|
718
|
-
|
|
527
|
+
perk_ctx = self._perk_menu_context()
|
|
528
|
+
if self._perk_menu.open:
|
|
529
|
+
self._perk_menu.handle_input(perk_ctx, dt_frame=dt_frame, dt_ui_ms=dt_ui_ms)
|
|
719
530
|
|
|
720
|
-
perk_menu_active = self.
|
|
531
|
+
perk_menu_active = self._perk_menu.active
|
|
721
532
|
|
|
722
533
|
if (not perk_menu_active) and perk_pending and (not self._paused):
|
|
723
534
|
label = self._perk_prompt_label()
|
|
@@ -736,29 +547,26 @@ class QuestMode(BaseGameplayMode):
|
|
|
736
547
|
|
|
737
548
|
if input_code_is_pressed(pick_key) and (not input_code_is_down(fire_key)):
|
|
738
549
|
self._perk_prompt_pulse = 1000.0
|
|
739
|
-
self.
|
|
550
|
+
self._perk_menu.open_if_available(perk_ctx)
|
|
740
551
|
elif self._perk_prompt_hover and input_code_is_pressed(fire_key):
|
|
741
552
|
self._perk_prompt_pulse = 1000.0
|
|
742
|
-
self.
|
|
553
|
+
self._perk_menu.open_if_available(perk_ctx)
|
|
743
554
|
|
|
744
|
-
perk_menu_active = self.
|
|
555
|
+
perk_menu_active = self._perk_menu.active
|
|
745
556
|
|
|
746
557
|
if not self._paused:
|
|
747
558
|
pulse_delta = dt_ui_ms * (6.0 if self._perk_prompt_hover else -2.0)
|
|
748
|
-
self._perk_prompt_pulse =
|
|
559
|
+
self._perk_prompt_pulse = clamp(self._perk_prompt_pulse + pulse_delta, 0.0, 1000.0)
|
|
749
560
|
|
|
750
561
|
prompt_active = perk_pending and (not perk_menu_active) and (not self._paused)
|
|
751
562
|
if prompt_active:
|
|
752
|
-
self._perk_prompt_timer_ms =
|
|
563
|
+
self._perk_prompt_timer_ms = clamp(self._perk_prompt_timer_ms + dt_ui_ms, 0.0, PERK_PROMPT_MAX_TIMER_MS)
|
|
753
564
|
else:
|
|
754
|
-
self._perk_prompt_timer_ms =
|
|
565
|
+
self._perk_prompt_timer_ms = clamp(self._perk_prompt_timer_ms - dt_ui_ms, 0.0, PERK_PROMPT_MAX_TIMER_MS)
|
|
755
566
|
|
|
756
|
-
|
|
757
|
-
self._perk_menu_timeline_ms = _clamp(self._perk_menu_timeline_ms + dt_ui_ms, 0.0, PERK_MENU_TRANSITION_MS)
|
|
758
|
-
else:
|
|
759
|
-
self._perk_menu_timeline_ms = _clamp(self._perk_menu_timeline_ms - dt_ui_ms, 0.0, PERK_MENU_TRANSITION_MS)
|
|
567
|
+
self._perk_menu.tick_timeline(dt_ui_ms)
|
|
760
568
|
|
|
761
|
-
dt_world = 0.0 if self._paused or (not any_alive) or
|
|
569
|
+
dt_world = 0.0 if self._paused or (not any_alive) or self._perk_menu.active else dt_frame
|
|
762
570
|
if dt_world <= 0.0:
|
|
763
571
|
if not any(player.health > 0.0 for player in self._world.players):
|
|
764
572
|
self._close_failed_run()
|
|
@@ -794,6 +602,9 @@ class QuestMode(BaseGameplayMode):
|
|
|
794
602
|
self._quest.spawn_timeline_ms = float(timeline_ms)
|
|
795
603
|
self._quest.no_creatures_timer_ms = float(no_creatures_timer_ms)
|
|
796
604
|
|
|
605
|
+
detail_preset = 5
|
|
606
|
+
if self._world.config is not None:
|
|
607
|
+
detail_preset = int(self._world.config.data.get("detail_preset", 5) or 5)
|
|
797
608
|
for call in spawns:
|
|
798
609
|
self._creatures.spawn_template(
|
|
799
610
|
int(call.template_id),
|
|
@@ -801,6 +612,8 @@ class QuestMode(BaseGameplayMode):
|
|
|
801
612
|
float(call.heading),
|
|
802
613
|
self._state.rng,
|
|
803
614
|
rand=self._state.rng.rand,
|
|
615
|
+
state=self._state,
|
|
616
|
+
detail_preset=detail_preset,
|
|
804
617
|
)
|
|
805
618
|
|
|
806
619
|
completion_ms, completed = tick_quest_completion_transition(
|
|
@@ -847,7 +660,7 @@ class QuestMode(BaseGameplayMode):
|
|
|
847
660
|
self.close_requested = True
|
|
848
661
|
|
|
849
662
|
def draw(self) -> None:
|
|
850
|
-
perk_menu_active = self.
|
|
663
|
+
perk_menu_active = self._perk_menu.active
|
|
851
664
|
self._world.draw(draw_aim_indicators=not perk_menu_active)
|
|
852
665
|
self._draw_screen_fade()
|
|
853
666
|
|
|
@@ -860,6 +673,7 @@ class QuestMode(BaseGameplayMode):
|
|
|
860
673
|
self._draw_target_health_bar()
|
|
861
674
|
hud_bottom = draw_hud_overlay(
|
|
862
675
|
self._hud_assets,
|
|
676
|
+
state=self._hud_state,
|
|
863
677
|
player=self._player,
|
|
864
678
|
players=self._world.players,
|
|
865
679
|
bonus_hud=self._state.bonus_hud,
|
|
@@ -875,6 +689,12 @@ class QuestMode(BaseGameplayMode):
|
|
|
875
689
|
small_indicators=self._hud_small_indicators(),
|
|
876
690
|
)
|
|
877
691
|
|
|
692
|
+
if debug_enabled() and (not perk_menu_active):
|
|
693
|
+
x = 18.0
|
|
694
|
+
y = max(18.0, hud_bottom + 10.0)
|
|
695
|
+
god = "on" if self._state.debug_god_mode else "off"
|
|
696
|
+
self._draw_ui_text(f"debug: [/] weapon F3 perk+1 F2 god={god}", x, y, UI_HINT_COLOR, scale=0.9)
|
|
697
|
+
|
|
878
698
|
self._draw_quest_title()
|
|
879
699
|
|
|
880
700
|
warn_y = float(rl.get_screen_height()) - 28.0
|
|
@@ -887,7 +707,7 @@ class QuestMode(BaseGameplayMode):
|
|
|
887
707
|
self._draw_ui_text(warn, 24.0, warn_y, rl.Color(240, 80, 80, 255), scale=0.8)
|
|
888
708
|
|
|
889
709
|
self._draw_perk_prompt()
|
|
890
|
-
self.
|
|
710
|
+
self._perk_menu.draw(self._perk_menu_context())
|
|
891
711
|
|
|
892
712
|
if perk_menu_active:
|
|
893
713
|
self._draw_game_cursor()
|
|
@@ -895,6 +715,8 @@ class QuestMode(BaseGameplayMode):
|
|
|
895
715
|
self._draw_game_cursor()
|
|
896
716
|
x = 18.0
|
|
897
717
|
y = max(18.0, hud_bottom + 10.0)
|
|
718
|
+
if debug_enabled() and (not perk_menu_active):
|
|
719
|
+
y += float(self._ui_line_height(scale=0.9))
|
|
898
720
|
self._draw_ui_text("paused (TAB)", x, y, UI_HINT_COLOR)
|
|
899
721
|
else:
|
|
900
722
|
self._draw_aim_cursor()
|
crimson/modes/rush_mode.py
CHANGED
|
@@ -13,13 +13,13 @@ from grim.view import ViewContext
|
|
|
13
13
|
|
|
14
14
|
from ..creatures.spawn import tick_rush_mode_spawns
|
|
15
15
|
from ..game_modes import GameMode
|
|
16
|
-
from ..gameplay import PlayerInput,
|
|
16
|
+
from ..gameplay import PlayerInput, weapon_assign_player
|
|
17
17
|
from ..input_codes import config_keybinds, input_code_is_down, input_code_is_pressed, player_move_fire_binds
|
|
18
|
-
from ..persistence.highscores import HighScoreRecord
|
|
19
18
|
from ..ui.cursor import draw_aim_cursor, draw_menu_cursor
|
|
20
19
|
from ..ui.hud import draw_hud_overlay, hud_flags_for_game_mode
|
|
21
20
|
from ..ui.perk_menu import load_perk_menu_assets
|
|
22
21
|
from .base_gameplay_mode import BaseGameplayMode
|
|
22
|
+
from .components.highscore_record_builder import build_highscore_record_for_game_over
|
|
23
23
|
|
|
24
24
|
WORLD_SIZE = 1024.0
|
|
25
25
|
RUSH_WEAPON_ID = 2
|
|
@@ -146,25 +146,14 @@ class RushMode(BaseGameplayMode):
|
|
|
146
146
|
if self._game_over_active:
|
|
147
147
|
return
|
|
148
148
|
|
|
149
|
-
|
|
150
|
-
record
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
try:
|
|
158
|
-
fired = int(self._state.shots_fired[int(self._player.index)])
|
|
159
|
-
hit = int(self._state.shots_hit[int(self._player.index)])
|
|
160
|
-
except Exception:
|
|
161
|
-
fired = 0
|
|
162
|
-
hit = 0
|
|
163
|
-
fired = max(0, int(fired))
|
|
164
|
-
hit = max(0, min(int(hit), fired))
|
|
165
|
-
record.shots_fired = fired
|
|
166
|
-
record.shots_hit = hit
|
|
167
|
-
record.game_mode_id = int(self._config.data.get("game_mode", int(GameMode.RUSH))) if self._config is not None else int(GameMode.RUSH)
|
|
149
|
+
game_mode_id = int(self._config.data.get("game_mode", int(GameMode.RUSH))) if self._config is not None else int(GameMode.RUSH)
|
|
150
|
+
record = build_highscore_record_for_game_over(
|
|
151
|
+
state=self._state,
|
|
152
|
+
player=self._player,
|
|
153
|
+
survival_elapsed_ms=int(self._rush.elapsed_ms),
|
|
154
|
+
creature_kill_count=int(self._creatures.kill_count),
|
|
155
|
+
game_mode_id=game_mode_id,
|
|
156
|
+
)
|
|
168
157
|
|
|
169
158
|
self._game_over_record = record
|
|
170
159
|
self._game_over_ui.open()
|
|
@@ -179,28 +168,7 @@ class RushMode(BaseGameplayMode):
|
|
|
179
168
|
return
|
|
180
169
|
|
|
181
170
|
if self._game_over_active:
|
|
182
|
-
|
|
183
|
-
if record is None:
|
|
184
|
-
self._enter_game_over()
|
|
185
|
-
record = self._game_over_record
|
|
186
|
-
if record is not None:
|
|
187
|
-
action = self._game_over_ui.update(
|
|
188
|
-
dt,
|
|
189
|
-
record=record,
|
|
190
|
-
player_name_default=self._player_name_default(),
|
|
191
|
-
play_sfx=self._world.audio_router.play_sfx,
|
|
192
|
-
rand=self._state.rng.rand,
|
|
193
|
-
mouse=self._ui_mouse_pos(),
|
|
194
|
-
)
|
|
195
|
-
if action == "play_again":
|
|
196
|
-
self.open()
|
|
197
|
-
return
|
|
198
|
-
if action == "high_scores":
|
|
199
|
-
self._action = "open_high_scores"
|
|
200
|
-
return
|
|
201
|
-
if action == "main_menu":
|
|
202
|
-
self._action = "back_to_menu"
|
|
203
|
-
self.close_requested = True
|
|
171
|
+
self._update_game_over_ui(dt)
|
|
204
172
|
return
|
|
205
173
|
|
|
206
174
|
any_alive = any(player.health > 0.0 for player in self._world.players)
|
|
@@ -265,6 +233,7 @@ class RushMode(BaseGameplayMode):
|
|
|
265
233
|
self._draw_target_health_bar()
|
|
266
234
|
hud_bottom = draw_hud_overlay(
|
|
267
235
|
self._hud_assets,
|
|
236
|
+
state=self._hud_state,
|
|
268
237
|
player=self._player,
|
|
269
238
|
players=self._world.players,
|
|
270
239
|
bonus_hud=self._state.bonus_hud,
|