crimsonland 0.1.0.dev15__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.
Files changed (75) hide show
  1. crimson/cli.py +61 -0
  2. crimson/creatures/damage.py +111 -36
  3. crimson/creatures/runtime.py +246 -156
  4. crimson/creatures/spawn.py +7 -3
  5. crimson/demo.py +38 -45
  6. crimson/effects.py +7 -13
  7. crimson/frontend/high_scores_layout.py +81 -0
  8. crimson/frontend/panels/base.py +4 -1
  9. crimson/frontend/panels/controls.py +0 -15
  10. crimson/frontend/panels/databases.py +291 -3
  11. crimson/frontend/panels/mods.py +0 -15
  12. crimson/frontend/panels/play_game.py +0 -16
  13. crimson/game.py +441 -1
  14. crimson/gameplay.py +905 -569
  15. crimson/modes/base_gameplay_mode.py +33 -12
  16. crimson/modes/components/__init__.py +2 -0
  17. crimson/modes/components/highscore_record_builder.py +58 -0
  18. crimson/modes/components/perk_menu_controller.py +325 -0
  19. crimson/modes/quest_mode.py +58 -273
  20. crimson/modes/rush_mode.py +12 -43
  21. crimson/modes/survival_mode.py +71 -328
  22. crimson/modes/tutorial_mode.py +46 -247
  23. crimson/modes/typo_mode.py +11 -38
  24. crimson/oracle.py +396 -0
  25. crimson/perks.py +5 -2
  26. crimson/player_damage.py +94 -37
  27. crimson/projectiles.py +539 -320
  28. crimson/render/projectile_draw_registry.py +637 -0
  29. crimson/render/projectile_render_registry.py +110 -0
  30. crimson/render/secondary_projectile_draw_registry.py +206 -0
  31. crimson/render/world_renderer.py +58 -707
  32. crimson/sim/world_state.py +118 -61
  33. crimson/typo/spawns.py +5 -12
  34. crimson/ui/demo_trial_overlay.py +3 -11
  35. crimson/ui/formatting.py +24 -0
  36. crimson/ui/game_over.py +12 -58
  37. crimson/ui/hud.py +72 -39
  38. crimson/ui/layout.py +20 -0
  39. crimson/ui/perk_menu.py +9 -34
  40. crimson/ui/quest_results.py +12 -64
  41. crimson/ui/text_input.py +20 -0
  42. crimson/views/_ui_helpers.py +27 -0
  43. crimson/views/aim_debug.py +15 -32
  44. crimson/views/animations.py +18 -28
  45. crimson/views/arsenal_debug.py +22 -32
  46. crimson/views/bonuses.py +23 -36
  47. crimson/views/camera_debug.py +16 -29
  48. crimson/views/camera_shake.py +9 -33
  49. crimson/views/corpse_stamp_debug.py +13 -21
  50. crimson/views/decals_debug.py +36 -23
  51. crimson/views/fonts.py +8 -25
  52. crimson/views/ground.py +4 -21
  53. crimson/views/lighting_debug.py +42 -45
  54. crimson/views/particles.py +33 -42
  55. crimson/views/perk_menu_debug.py +3 -10
  56. crimson/views/player.py +50 -44
  57. crimson/views/player_sprite_debug.py +24 -31
  58. crimson/views/projectile_fx.py +57 -52
  59. crimson/views/projectile_render_debug.py +24 -33
  60. crimson/views/projectiles.py +24 -37
  61. crimson/views/spawn_plan.py +13 -29
  62. crimson/views/sprites.py +14 -29
  63. crimson/views/terrain.py +6 -23
  64. crimson/views/ui.py +7 -24
  65. crimson/views/wicons.py +28 -33
  66. {crimsonland-0.1.0.dev15.dist-info → crimsonland-0.1.0.dev16.dist-info}/METADATA +1 -1
  67. {crimsonland-0.1.0.dev15.dist-info → crimsonland-0.1.0.dev16.dist-info}/RECORD +72 -64
  68. {crimsonland-0.1.0.dev15.dist-info → crimsonland-0.1.0.dev16.dist-info}/WHEEL +2 -2
  69. grim/config.py +29 -1
  70. grim/console.py +7 -10
  71. grim/math.py +12 -0
  72. crimson/.DS_Store +0 -0
  73. crimson/creatures/.DS_Store +0 -0
  74. grim/.DS_Store +0 -0
  75. {crimsonland-0.1.0.dev15.dist-info → crimsonland-0.1.0.dev16.dist-info}/entry_points.txt +0 -0
crimson/demo.py CHANGED
@@ -12,8 +12,10 @@ from grim.assets import PaqTextureCache, load_paq_entries
12
12
  from grim.config import CrimsonConfig
13
13
  from grim.fonts.grim_mono import GrimMonoFont, draw_grim_mono_text, load_grim_mono_font
14
14
  from grim.fonts.small import SmallFontData, draw_small_text, load_small_font, measure_small_text_width
15
+ from grim.math import clamp, distance_sq
15
16
 
16
17
  from grim.rand import Crand
18
+ from .creatures.spawn import RANDOM_HEADING_SENTINEL
17
19
  from .game_world import GameWorld
18
20
  from .gameplay import PlayerInput, PlayerState, weapon_assign_player
19
21
  from .ui.cursor import draw_menu_cursor
@@ -65,20 +67,6 @@ def _weapon_name(weapon_id: int) -> str:
65
67
  return f"weapon_{weapon_id}"
66
68
 
67
69
 
68
- def _clamp(value: float, lo: float, hi: float) -> float:
69
- if value < lo:
70
- return lo
71
- if value > hi:
72
- return hi
73
- return value
74
-
75
-
76
- def _distance_sq(x0: float, y0: float, x1: float, y1: float) -> float:
77
- dx = x1 - x0
78
- dy = y1 - y0
79
- return dx * dx + dy * dy
80
-
81
-
82
70
  def _normalize(dx: float, dy: float) -> tuple[float, float, float]:
83
71
  d = math.hypot(dx, dy)
84
72
  if d <= 1e-6:
@@ -335,7 +323,7 @@ class DemoView:
335
323
  # - per-corner color slots, with a sin^2 pulse at bottom-right
336
324
 
337
325
  def _to_u8(value: float) -> int:
338
- return int(_clamp(value, 0.0, 1.0) * 255.0 + 0.5)
326
+ return int(clamp(value, 0.0, 1.0) * 255.0 + 0.5)
339
327
 
340
328
  c0 = rl.Color(_to_u8(0.0), _to_u8(0.0), _to_u8(0.0), _to_u8(1.0))
341
329
  c1 = rl.Color(_to_u8(0.0), _to_u8(0.0), _to_u8(0.3), _to_u8(1.0))
@@ -551,12 +539,17 @@ class DemoView:
551
539
 
552
540
  def _spawn(self, spawn_id: int, x: float, y: float, *, heading: float = 0.0) -> None:
553
541
  x, y = self._wrap_pos(x, y)
542
+ detail_preset = 5
543
+ if self._world.config is not None:
544
+ detail_preset = int(self._world.config.data.get("detail_preset", 5) or 5)
554
545
  self._world.creatures.spawn_template(
555
546
  int(spawn_id),
556
547
  (x, y),
557
548
  float(heading),
558
549
  self._spawn_rng,
559
550
  rand=self._spawn_rng.rand,
551
+ state=self._world.state,
552
+ detail_preset=detail_preset,
560
553
  )
561
554
 
562
555
  def _setup_variant_0(self) -> None:
@@ -573,8 +566,8 @@ class DemoView:
573
566
  i = 0
574
567
  while y < 1696:
575
568
  col = i % 2
576
- self._spawn(0x38, float((col + 2) * 64), float(y), heading=-100.0)
577
- self._spawn(0x38, float(col * 64 + 798), float(y), heading=-100.0)
569
+ self._spawn(0x38, float((col + 2) * 64), float(y), heading=RANDOM_HEADING_SENTINEL)
570
+ self._spawn(0x38, float(col * 64 + 798), float(y), heading=RANDOM_HEADING_SENTINEL)
578
571
  y += 80
579
572
  i += 1
580
573
 
@@ -592,11 +585,11 @@ class DemoView:
592
585
  for idx in range(20):
593
586
  x = float(self._crand_mod(200) + 32)
594
587
  y = float(self._crand_mod(899) + 64)
595
- self._spawn(0x34, x, y, heading=-100.0)
588
+ self._spawn(0x34, x, y, heading=RANDOM_HEADING_SENTINEL)
596
589
  if idx % 3 != 0:
597
590
  x2 = float(self._crand_mod(30) + 32)
598
591
  y2 = float(self._crand_mod(899) + 64)
599
- self._spawn(0x35, x2, y2, heading=-100.0)
592
+ self._spawn(0x35, x2, y2, heading=RANDOM_HEADING_SENTINEL)
600
593
 
601
594
  def _setup_variant_2(self) -> None:
602
595
  self._demo_time_limit_ms = 5000
@@ -607,10 +600,10 @@ class DemoView:
607
600
  i = 0
608
601
  while y < 848:
609
602
  col = i % 2
610
- self._spawn(0x41, float(col * 64 + 32), float(y), heading=-100.0)
611
- self._spawn(0x41, float((col + 2) * 64), float(y), heading=-100.0)
612
- self._spawn(0x41, float(col * 64 - 64), float(y), heading=-100.0)
613
- self._spawn(0x41, float((col + 12) * 64), float(y), heading=-100.0)
603
+ self._spawn(0x41, float(col * 64 + 32), float(y), heading=RANDOM_HEADING_SENTINEL)
604
+ self._spawn(0x41, float((col + 2) * 64), float(y), heading=RANDOM_HEADING_SENTINEL)
605
+ self._spawn(0x41, float(col * 64 - 64), float(y), heading=RANDOM_HEADING_SENTINEL)
606
+ self._spawn(0x41, float((col + 12) * 64), float(y), heading=RANDOM_HEADING_SENTINEL)
614
607
  y += 60
615
608
  i += 1
616
609
 
@@ -706,7 +699,7 @@ class DemoView:
706
699
  rl.draw_circle(int(sx), int(sy), radius, rl.Color(200, 120, 255, 255))
707
700
  continue
708
701
  if proj.type_id == 3:
709
- t = _clamp(proj.vel_x, 0.0, 1.0)
702
+ t = clamp(proj.vel_x, 0.0, 1.0)
710
703
  radius = proj.vel_y * t * 80.0
711
704
  alpha = int((1.0 - t) * 180.0)
712
705
  color = rl.Color(200, 120, 255, alpha)
@@ -715,15 +708,15 @@ class DemoView:
715
708
  for beam in self._beams:
716
709
  x0, y0 = self._world_to_screen(beam.x0, beam.y0)
717
710
  x1, y1 = self._world_to_screen(beam.x1, beam.y1)
718
- alpha = int(_clamp(beam.life / 0.08, 0.0, 1.0) * 255.0)
711
+ alpha = int(clamp(beam.life / 0.08, 0.0, 1.0) * 255.0)
719
712
  color = rl.Color(120, 220, 255, alpha)
720
713
  rl.draw_line_ex(rl.Vector2(x0, y0), rl.Vector2(x1, y1), 2.0 * scale, color)
721
714
 
722
715
  for fx in self._explosions:
723
716
  t = fx.elapsed / fx.duration if fx.duration > 0 else 1.0
724
- radius = fx.max_radius * _clamp(t, 0.0, 1.0)
717
+ radius = fx.max_radius * clamp(t, 0.0, 1.0)
725
718
  sx, sy = self._world_to_screen(fx.x, fx.y)
726
- alpha = int((1.0 - _clamp(t, 0.0, 1.0)) * 180.0)
719
+ alpha = int((1.0 - clamp(t, 0.0, 1.0)) * 180.0)
727
720
  color = rl.Color(255, 180, 100, alpha) if fx.kind == "rocket" else rl.Color(200, 120, 255, alpha)
728
721
  rl.draw_circle_lines(int(sx), int(sy), max(1.0, radius * scale), color)
729
722
 
@@ -765,7 +758,7 @@ class DemoView:
765
758
  flags = creature.flags
766
759
 
767
760
  def _to_u8(value: float) -> int:
768
- return int(_clamp(value, 0.0, 1.0) * 255.0 + 0.5)
761
+ return int(clamp(value, 0.0, 1.0) * 255.0 + 0.5)
769
762
 
770
763
  tint = rl.WHITE
771
764
  if creature.tint is not None and any(v is not None for v in creature.tint):
@@ -786,7 +779,7 @@ class DemoView:
786
779
  scale_x,
787
780
  scale_y,
788
781
  tint=tint,
789
- size_scale=_clamp(creature.size / 64.0, 0.25, 2.0),
782
+ size_scale=clamp(creature.size / 64.0, 0.25, 2.0),
790
783
  )
791
784
 
792
785
  def _draw_sprite(
@@ -863,7 +856,7 @@ class DemoView:
863
856
  alpha = var_2c * 0.05
864
857
  if timeline_ms > limit_ms - 500:
865
858
  alpha = float(limit_ms - timeline_ms) * 0.002
866
- alpha = _clamp(alpha, 0.0, 1.0)
859
+ alpha = clamp(alpha, 0.0, 1.0)
867
860
 
868
861
  scale = 0.8
869
862
  text_w = float(len(msg)) * 12.8
@@ -875,9 +868,9 @@ class DemoView:
875
868
  bar_x = 64.0
876
869
  bar_y = var_2c + 72.0
877
870
 
878
- bg_alpha = int(round(_clamp(alpha * 0.5, 0.0, 1.0) * 255.0))
879
- bar_alpha = int(round(_clamp(alpha * 0.8, 0.0, 1.0) * 255.0))
880
- txt_alpha = int(round(_clamp(alpha, 0.0, 1.0) * 255.0))
871
+ bg_alpha = int(round(clamp(alpha * 0.5, 0.0, 1.0) * 255.0))
872
+ bar_alpha = int(round(clamp(alpha * 0.8, 0.0, 1.0) * 255.0))
873
+ txt_alpha = int(round(clamp(alpha, 0.0, 1.0) * 255.0))
881
874
 
882
875
  rl.draw_rectangle_rec(
883
876
  rl.Rectangle(bg_x, bg_y, text_w + 12.0, 30.0),
@@ -886,7 +879,7 @@ class DemoView:
886
879
 
887
880
  progress = 0.0
888
881
  if limit_ms > 0:
889
- progress = _clamp(float(timeline_ms) / float(limit_ms), 0.0, 1.0)
882
+ progress = clamp(float(timeline_ms) / float(limit_ms), 0.0, 1.0)
890
883
  rl.draw_rectangle_rec(
891
884
  rl.Rectangle(bar_x, bar_y, text_w * progress, 3.0),
892
885
  rl.Color(128, 26, 26, bar_alpha),
@@ -1000,7 +993,7 @@ class DemoView:
1000
993
  for idx, creature in enumerate(self._world.creatures.entries):
1001
994
  if not (creature.active and creature.hp > 0.0):
1002
995
  continue
1003
- d = _distance_sq(x, y, creature.x, creature.y)
996
+ d = distance_sq(x, y, creature.x, creature.y)
1004
997
  if best_idx is None or d < best_dist:
1005
998
  best_idx = idx
1006
999
  best_dist = d
@@ -1044,7 +1037,7 @@ class DemoView:
1044
1037
  best_idx = None
1045
1038
  best_dist = 0.0
1046
1039
  for idx, player in enumerate(self._players):
1047
- d = _distance_sq(x, y, player.x, player.y)
1040
+ d = distance_sq(x, y, player.x, player.y)
1048
1041
  if best_idx is None or d < best_dist:
1049
1042
  best_idx = idx
1050
1043
  best_dist = d
@@ -1056,7 +1049,7 @@ class DemoView:
1056
1049
  for idx, creature in enumerate(self._creatures):
1057
1050
  if creature.hp <= 0.0:
1058
1051
  continue
1059
- d = _distance_sq(x, y, creature.x, creature.y)
1052
+ d = distance_sq(x, y, creature.x, creature.y)
1060
1053
  if best_idx is None or d < best_dist:
1061
1054
  best_idx = idx
1062
1055
  best_dist = d
@@ -1082,7 +1075,7 @@ class DemoView:
1082
1075
  spawn_events.append((child_template_id, owner.x, owner.y))
1083
1076
 
1084
1077
  for child_template_id, x, y in spawn_events:
1085
- self._spawn(child_template_id, x, y, heading=-100.0)
1078
+ self._spawn(child_template_id, x, y, heading=RANDOM_HEADING_SENTINEL)
1086
1079
 
1087
1080
  def _update_creatures(self, dt: float, dt_ms: int) -> None:
1088
1081
  if not self._creatures or not self._players:
@@ -1136,8 +1129,8 @@ class DemoView:
1136
1129
  creature.vy = direction_y * dt * creature.move_scale * speed
1137
1130
 
1138
1131
  radius = max(0.0, creature.size)
1139
- creature.x = _clamp(creature.x + creature.vx, radius, WORLD_SIZE - radius)
1140
- creature.y = _clamp(creature.y + creature.vy, radius, WORLD_SIZE - radius)
1132
+ creature.x = clamp(creature.x + creature.vx, radius, WORLD_SIZE - radius)
1133
+ creature.y = clamp(creature.y + creature.vy, radius, WORLD_SIZE - radius)
1141
1134
 
1142
1135
  def _select_player_target(self, player: DemoPlayer) -> int | None:
1143
1136
  candidate = self._nearest_creature_index(player.x, player.y)
@@ -1218,8 +1211,8 @@ class DemoView:
1218
1211
  speed = 150.0
1219
1212
  player.vx = mnx * speed
1220
1213
  player.vy = mny * speed
1221
- player.x = _clamp(player.x + player.vx * dt, 0.0, WORLD_SIZE)
1222
- player.y = _clamp(player.y + player.vy * dt, 0.0, WORLD_SIZE)
1214
+ player.x = clamp(player.x + player.vx * dt, 0.0, WORLD_SIZE)
1215
+ player.y = clamp(player.y + player.vy * dt, 0.0, WORLD_SIZE)
1223
1216
 
1224
1217
  self._player_fire(player, target)
1225
1218
 
@@ -1367,12 +1360,12 @@ class DemoView:
1367
1360
 
1368
1361
  def _apply_explosion_damage(self, fx: DemoExplosion) -> None:
1369
1362
  t = fx.elapsed / fx.duration if fx.duration > 0 else 1.0
1370
- radius = fx.max_radius * _clamp(t, 0.0, 1.0)
1363
+ radius = fx.max_radius * clamp(t, 0.0, 1.0)
1371
1364
  rsq = radius * radius
1372
1365
  for creature in self._creatures:
1373
1366
  if creature.hp <= 0.0:
1374
1367
  continue
1375
- if _distance_sq(fx.x, fx.y, creature.x, creature.y) <= rsq:
1368
+ if distance_sq(fx.x, fx.y, creature.x, creature.y) <= rsq:
1376
1369
  creature.hp -= fx.damage_per_tick
1377
1370
 
1378
1371
  def _update_camera(self, dt: float) -> None:
@@ -1406,6 +1399,6 @@ class DemoView:
1406
1399
  if desired_y < min_y:
1407
1400
  desired_y = min_y
1408
1401
 
1409
- t = _clamp(dt * 6.0, 0.0, 1.0)
1402
+ t = clamp(dt * 6.0, 0.0, 1.0)
1410
1403
  self._camera_x = _lerp(self._camera_x, desired_x, t)
1411
1404
  self._camera_y = _lerp(self._camera_y, desired_y, t)
crimson/effects.py CHANGED
@@ -4,6 +4,8 @@ from dataclasses import dataclass
4
4
  import math
5
5
  from typing import Callable, Protocol
6
6
 
7
+ from grim.math import clamp
8
+
7
9
  __all__ = [
8
10
  "FX_QUEUE_CAPACITY",
9
11
  "FX_QUEUE_MAX_COUNT",
@@ -35,14 +37,6 @@ FX_QUEUE_ROTATED_CAPACITY = 0x40
35
37
  FX_QUEUE_ROTATED_MAX_COUNT = 0x3F
36
38
 
37
39
 
38
- def _clamp(value: float, lo: float, hi: float) -> float:
39
- if value < lo:
40
- return lo
41
- if value > hi:
42
- return hi
43
- return value
44
-
45
-
46
40
  def _default_rand() -> int:
47
41
  return 0
48
42
 
@@ -256,7 +250,7 @@ class ParticlePool:
256
250
  entry.vel_x = math.cos(entry.angle) * speed
257
251
  entry.vel_y = math.sin(entry.angle) * speed
258
252
 
259
- alpha = _clamp(entry.intensity, 0.0, 1.0)
253
+ alpha = clamp(entry.intensity, 0.0, 1.0)
260
254
  shade = 1.0 - max(entry.intensity, 0.0) * 0.95
261
255
  entry.age = alpha
262
256
  entry.scale_x = shade
@@ -305,10 +299,10 @@ class ParticlePool:
305
299
  tint_sum = float(getattr(creature, "tint_r", 1.0)) + float(getattr(creature, "tint_g", 1.0)) + float(getattr(creature, "tint_b", 1.0))
306
300
  if tint_sum > 1.6:
307
301
  factor = 1.0 - max(entry.intensity, 0.0) * 0.01
308
- creature.tint_r = _clamp(float(getattr(creature, "tint_r", 1.0)) * factor, 0.0, 1.0)
309
- creature.tint_g = _clamp(float(getattr(creature, "tint_g", 1.0)) * factor, 0.0, 1.0)
310
- creature.tint_b = _clamp(float(getattr(creature, "tint_b", 1.0)) * factor, 0.0, 1.0)
311
- creature.tint_a = _clamp(float(getattr(creature, "tint_a", 1.0)) * factor, 0.0, 1.0)
302
+ creature.tint_r = clamp(float(getattr(creature, "tint_r", 1.0)) * factor, 0.0, 1.0)
303
+ creature.tint_g = clamp(float(getattr(creature, "tint_g", 1.0)) * factor, 0.0, 1.0)
304
+ creature.tint_b = clamp(float(getattr(creature, "tint_b", 1.0)) * factor, 0.0, 1.0)
305
+ creature.tint_a = clamp(float(getattr(creature, "tint_a", 1.0)) * factor, 0.0, 1.0)
312
306
 
313
307
  return expired
314
308
 
@@ -24,3 +24,84 @@ HS_BUTTON_STEP_Y = 33.0
24
24
 
25
25
  HS_BACK_BUTTON_X = 400.0 # x0=302
26
26
  HS_BACK_BUTTON_Y = 301.0 # y0=495
27
+
28
+ # Quest-mode high score selector arrow (left panel).
29
+ # state_14:High scores - Quests: ui_arrow.jaz bbox [351,256]..[383,272]
30
+ # left panel top-left at 1024x768 is (-98,194).
31
+ HS_QUEST_ARROW_X = 449.0
32
+ HS_QUEST_ARROW_Y = 62.0
33
+
34
+ # Right panel (Quests): options + dropdown widgets.
35
+ # right panel top-left at 1024x768 is (630,209).
36
+ HS_RIGHT_CHECK_X = 44.0 # ui_checkOn bbox [674,253]..[690,269]
37
+ HS_RIGHT_CHECK_Y = 44.0
38
+ HS_RIGHT_SHOW_INTERNET_X = 66.0 # "Show internet scores" at (696,254)
39
+ HS_RIGHT_SHOW_INTERNET_Y = 45.0
40
+
41
+ HS_RIGHT_NUMBER_PLAYERS_X = 46.0 # "Number of players" at (676,273)
42
+ HS_RIGHT_NUMBER_PLAYERS_Y = 64.0
43
+ HS_RIGHT_GAME_MODE_X = 174.0 # "Game mode" at (804,273)
44
+ HS_RIGHT_GAME_MODE_Y = 64.0
45
+ HS_RIGHT_SHOW_SCORES_X = 44.0 # "Show scores:" at (674,315)
46
+ HS_RIGHT_SHOW_SCORES_Y = 106.0
47
+ HS_RIGHT_SCORE_LIST_X = 44.0 # "Selected score list:" at (674,359)
48
+ HS_RIGHT_SCORE_LIST_Y = 150.0
49
+
50
+ # Closed dropdown widgets (borders + value text + arrow icon).
51
+ HS_RIGHT_PLAYER_COUNT_WIDGET_X = 46.0 # x=676
52
+ HS_RIGHT_PLAYER_COUNT_WIDGET_Y = 78.0 # y=287
53
+ HS_RIGHT_PLAYER_COUNT_WIDGET_W = 102.0
54
+ HS_RIGHT_PLAYER_COUNT_VALUE_X = 50.0 # "1 player" at (680,288)
55
+ HS_RIGHT_PLAYER_COUNT_VALUE_Y = 79.0
56
+ HS_RIGHT_PLAYER_COUNT_DROP_X = 131.0 # ui_dropOff bbox [761,287]..[777,303]
57
+ HS_RIGHT_PLAYER_COUNT_DROP_Y = 78.0
58
+
59
+ HS_RIGHT_GAME_MODE_WIDGET_X = 174.0 # x=804
60
+ HS_RIGHT_GAME_MODE_WIDGET_Y = 78.0 # y=287
61
+ HS_RIGHT_GAME_MODE_WIDGET_W = 95.0
62
+ HS_RIGHT_GAME_MODE_VALUE_X = 178.0 # "Quests" at (808,288)
63
+ HS_RIGHT_GAME_MODE_VALUE_Y = 79.0
64
+ HS_RIGHT_GAME_MODE_DROP_X = 252.0 # ui_dropOff bbox [882,287]..[898,303]
65
+ HS_RIGHT_GAME_MODE_DROP_Y = 78.0
66
+
67
+ HS_RIGHT_SHOW_SCORES_WIDGET_X = 44.0 # x=674
68
+ HS_RIGHT_SHOW_SCORES_WIDGET_Y = 120.0 # y=329
69
+ HS_RIGHT_SHOW_SCORES_WIDGET_W = 134.0
70
+ HS_RIGHT_SHOW_SCORES_VALUE_X = 48.0 # "Best of all time" at (678,330)
71
+ HS_RIGHT_SHOW_SCORES_VALUE_Y = 121.0
72
+ HS_RIGHT_SHOW_SCORES_DROP_X = 161.0 # ui_dropOff bbox [791,329]..[807,345]
73
+ HS_RIGHT_SHOW_SCORES_DROP_Y = 120.0
74
+
75
+ HS_RIGHT_SCORE_LIST_WIDGET_X = 44.0 # x=674
76
+ HS_RIGHT_SCORE_LIST_WIDGET_Y = 164.0 # y=373
77
+ HS_RIGHT_SCORE_LIST_WIDGET_W = 174.0
78
+ HS_RIGHT_SCORE_LIST_VALUE_X = 48.0 # "default" at (678,374)
79
+ HS_RIGHT_SCORE_LIST_VALUE_Y = 165.0
80
+ HS_RIGHT_SCORE_LIST_DROP_X = 201.0 # ui_dropOff bbox [831,373]..[847,389]
81
+ HS_RIGHT_SCORE_LIST_DROP_Y = 164.0
82
+
83
+ # Right panel (Survival): local score details + weapon icon.
84
+ HS_LOCAL_NAME_X = 78.0 # "banteg" at (708,253)
85
+ HS_LOCAL_NAME_Y = 44.0
86
+ HS_LOCAL_LABEL_X = 78.0 # "Local score" at (708,267)
87
+ HS_LOCAL_LABEL_Y = 58.0
88
+ HS_LOCAL_DATE_X = 193.0 # "31. Jan 2026" at (823,281)
89
+ HS_LOCAL_DATE_Y = 72.0
90
+ HS_LOCAL_SCORE_LABEL_X = 105.0 # "Score" at (735,299)
91
+ HS_LOCAL_SCORE_LABEL_Y = 90.0
92
+ HS_LOCAL_TIME_LABEL_X = 192.0 # "Game time" at (822,299)
93
+ HS_LOCAL_TIME_LABEL_Y = 90.0
94
+ HS_LOCAL_SCORE_VALUE_X = 105.0 # "27874" at (735,314)
95
+ HS_LOCAL_SCORE_VALUE_Y = 105.0
96
+ HS_LOCAL_TIME_VALUE_X = 226.0 # "3:28" at (856,318)
97
+ HS_LOCAL_TIME_VALUE_Y = 109.0
98
+ HS_LOCAL_RANK_X = 94.0 # "Rank: 2nd" at (724,329)
99
+ HS_LOCAL_RANK_Y = 120.0
100
+ HS_LOCAL_WICON_X = 90.0 # ui_wicons bbox [720,355]..[784,387]
101
+ HS_LOCAL_WICON_Y = 146.0
102
+ HS_LOCAL_FRAGS_X = 200.0 # "Frags: 441" at (830,356)
103
+ HS_LOCAL_FRAGS_Y = 147.0
104
+ HS_LOCAL_HIT_X = 200.0 # "Hit %: 64%" at (830,370)
105
+ HS_LOCAL_HIT_Y = 161.0
106
+ HS_LOCAL_WEAPON_X = 90.0 # "Sawed-off Shotgun" at (720,387)
107
+ HS_LOCAL_WEAPON_Y = 178.0
@@ -179,7 +179,7 @@ class PanelMenuView:
179
179
  self._draw_panel()
180
180
  self._draw_entry(entry)
181
181
  self._draw_sign()
182
- self._draw_title_text()
182
+ self._draw_contents()
183
183
  _draw_menu_cursor(self._state, pulse_time=self._cursor_pulse_time)
184
184
 
185
185
  def take_action(self) -> str | None:
@@ -187,6 +187,9 @@ class PanelMenuView:
187
187
  self._pending_action = None
188
188
  return action
189
189
 
190
+ def _draw_contents(self) -> None:
191
+ self._draw_title_text()
192
+
190
193
  def _draw_title_text(self) -> None:
191
194
  x = 32
192
195
  y = 140
@@ -12,9 +12,7 @@ from ..menu import (
12
12
  MENU_PANEL_HEIGHT,
13
13
  MENU_PANEL_WIDTH,
14
14
  MenuView,
15
- _draw_menu_cursor,
16
15
  )
17
- from ..transitions import _draw_screen_fade
18
16
  from ...ui.menu_panel import draw_classic_menu_panel
19
17
  from .base import PANEL_TIMELINE_END_MS, PANEL_TIMELINE_START_MS, PanelMenuView
20
18
  from ...input_codes import config_keybinds, input_code_name, player_move_fire_binds
@@ -61,19 +59,6 @@ class ControlsMenuView(PanelMenuView):
61
59
  self._check_on = cache.get_or_load("ui_checkOn", "ui/ui_checkOn.jaz").texture
62
60
  self._check_off = cache.get_or_load("ui_checkOff", "ui/ui_checkOff.jaz").texture
63
61
 
64
- def draw(self) -> None:
65
- self._draw_background()
66
- _draw_screen_fade(self._state)
67
- assets = self._assets
68
- entry = self._entry
69
- if assets is None or entry is None:
70
- return
71
- self._draw_panel()
72
- self._draw_entry(entry)
73
- self._draw_sign()
74
- self._draw_contents()
75
- _draw_menu_cursor(self._state, pulse_time=self._cursor_pulse_time)
76
-
77
62
  def _ensure_small_font(self) -> SmallFontData:
78
63
  if self._small_font is not None:
79
64
  return self._small_font