zombie-escape 1.5.4__py3-none-any.whl → 1.7.1__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 (37) hide show
  1. zombie_escape/__about__.py +1 -1
  2. zombie_escape/entities.py +501 -537
  3. zombie_escape/entities_constants.py +102 -0
  4. zombie_escape/gameplay/__init__.py +75 -2
  5. zombie_escape/gameplay/ambient.py +50 -0
  6. zombie_escape/gameplay/constants.py +46 -0
  7. zombie_escape/gameplay/footprints.py +60 -0
  8. zombie_escape/gameplay/interactions.py +354 -0
  9. zombie_escape/gameplay/layout.py +190 -0
  10. zombie_escape/gameplay/movement.py +220 -0
  11. zombie_escape/gameplay/spawn.py +618 -0
  12. zombie_escape/gameplay/state.py +137 -0
  13. zombie_escape/gameplay/survivors.py +306 -0
  14. zombie_escape/gameplay/utils.py +147 -0
  15. zombie_escape/gameplay_constants.py +0 -148
  16. zombie_escape/level_blueprints.py +123 -10
  17. zombie_escape/level_constants.py +6 -13
  18. zombie_escape/locales/ui.en.json +10 -1
  19. zombie_escape/locales/ui.ja.json +10 -1
  20. zombie_escape/models.py +15 -9
  21. zombie_escape/render.py +42 -27
  22. zombie_escape/render_assets.py +533 -23
  23. zombie_escape/render_constants.py +57 -22
  24. zombie_escape/rng.py +9 -9
  25. zombie_escape/screens/__init__.py +59 -29
  26. zombie_escape/screens/game_over.py +3 -3
  27. zombie_escape/screens/gameplay.py +45 -27
  28. zombie_escape/screens/title.py +5 -2
  29. zombie_escape/stage_constants.py +34 -1
  30. zombie_escape/zombie_escape.py +30 -12
  31. {zombie_escape-1.5.4.dist-info → zombie_escape-1.7.1.dist-info}/METADATA +1 -1
  32. zombie_escape-1.7.1.dist-info/RECORD +45 -0
  33. zombie_escape/gameplay/logic.py +0 -1917
  34. zombie_escape-1.5.4.dist-info/RECORD +0 -35
  35. {zombie_escape-1.5.4.dist-info → zombie_escape-1.7.1.dist-info}/WHEEL +0 -0
  36. {zombie_escape-1.5.4.dist-info → zombie_escape-1.7.1.dist-info}/entry_points.txt +0 -0
  37. {zombie_escape-1.5.4.dist-info → zombie_escape-1.7.1.dist-info}/licenses/LICENSE.txt +0 -0
zombie_escape/rng.py CHANGED
@@ -25,10 +25,10 @@ class DeterministicRNG:
25
25
  def __init__(self, seed: int | None = None) -> None:
26
26
  self._state = [0] * self._N
27
27
  self._index = self._N
28
- self._seed_value: int | None = None
29
- self.seed(seed)
28
+ self.__seed_value: int | None = None
29
+ self._seed(seed)
30
30
 
31
- def seed(self, value: int | None) -> None:
31
+ def _seed(self, value: int | None) -> None:
32
32
  """Seed using the MT19937 initialization routine."""
33
33
  if value is None:
34
34
  value = generate_seed()
@@ -36,7 +36,7 @@ class DeterministicRNG:
36
36
  normalized = int(value)
37
37
  except (TypeError, ValueError) as exc:
38
38
  raise ValueError(f"Invalid seed value: {value}") from exc
39
- self._seed_value = normalized
39
+ self.__seed_value = normalized
40
40
  seed32 = normalized & 0xFFFFFFFF
41
41
  if seed32 == 0:
42
42
  seed32 = 5489 # default MT seed
@@ -49,8 +49,8 @@ class DeterministicRNG:
49
49
  self._index = self._N
50
50
 
51
51
  @property
52
- def seed_value(self) -> int | None:
53
- return self._seed_value
52
+ def _seed_value(self) -> int | None:
53
+ return self.__seed_value
54
54
 
55
55
  def random(self) -> float:
56
56
  """Return a float in the range [0.0, 1.0)."""
@@ -119,9 +119,9 @@ def get_rng() -> DeterministicRNG:
119
119
 
120
120
 
121
121
  def seed_rng(seed: int | None) -> int:
122
- _GLOBAL_RNG.seed(seed)
123
- assert _GLOBAL_RNG.seed_value is not None
124
- return _GLOBAL_RNG.seed_value
122
+ _GLOBAL_RNG._seed(seed)
123
+ assert _GLOBAL_RNG._seed_value is not None
124
+ return _GLOBAL_RNG._seed_value
125
125
 
126
126
 
127
127
  __all__ = [
@@ -53,7 +53,7 @@ class ScreenTransition:
53
53
 
54
54
 
55
55
  current_window_scale = DEFAULT_WINDOW_SCALE # Applied to the OS window only
56
- current_fullscreen = False
56
+ current_maximized = False
57
57
  last_window_scale = DEFAULT_WINDOW_SCALE
58
58
  current_window_size = (
59
59
  int(SCREEN_WIDTH * DEFAULT_WINDOW_SCALE),
@@ -80,18 +80,17 @@ def present(logical_surface: surface.Surface) -> None:
80
80
  window_size = _fetch_window_size(window)
81
81
  _update_window_size(window_size, source="frame")
82
82
  logical_size = logical_surface.get_size()
83
- if current_fullscreen:
84
- scale = max(
85
- 1,
86
- min(
87
- window_size[0] // logical_size[0],
88
- window_size[1] // logical_size[1],
89
- ),
90
- )
91
- scaled_width = logical_size[0] * scale
92
- scaled_height = logical_size[1] * scale
83
+ if window_size == logical_size:
84
+ window.blit(logical_surface, (0, 0))
85
+ else:
86
+ # Preserve aspect ratio with letterboxing.
87
+ scale_x = window_size[0] / max(1, logical_size[0])
88
+ scale_y = window_size[1] / max(1, logical_size[1])
89
+ scale = min(scale_x, scale_y)
90
+ scaled_width = max(1, int(logical_size[0] * scale))
91
+ scaled_height = max(1, int(logical_size[1] * scale))
93
92
  window.fill((0, 0, 0))
94
- if scale == 1 and (scaled_width, scaled_height) == logical_size:
93
+ if (scaled_width, scaled_height) == logical_size:
95
94
  scaled_surface = logical_surface
96
95
  else:
97
96
  scaled_surface = pygame.transform.scale(
@@ -100,12 +99,6 @@ def present(logical_surface: surface.Surface) -> None:
100
99
  offset_x = (window_size[0] - scaled_width) // 2
101
100
  offset_y = (window_size[1] - scaled_height) // 2
102
101
  window.blit(scaled_surface, (offset_x, offset_y))
103
- pygame.display.flip()
104
- return
105
- if window_size == logical_size:
106
- window.blit(logical_surface, (0, 0))
107
- else:
108
- pygame.transform.scale(logical_surface, window_size, window)
109
102
  pygame.display.flip()
110
103
 
111
104
 
@@ -113,17 +106,19 @@ def apply_window_scale(
113
106
  scale: float, *, game_data: "GameData | None" = None
114
107
  ) -> surface.Surface:
115
108
  """Resize the OS window; logical render surface stays constant."""
116
- global current_window_scale, current_fullscreen, last_window_scale
109
+ global current_window_scale, current_maximized, last_window_scale
117
110
 
118
111
  clamped_scale = max(WINDOW_SCALE_MIN, min(WINDOW_SCALE_MAX, scale))
119
112
  current_window_scale = clamped_scale
120
113
  last_window_scale = clamped_scale
121
- current_fullscreen = False
114
+ current_maximized = False
122
115
 
123
116
  window_width = max(1, int(SCREEN_WIDTH * current_window_scale))
124
117
  window_height = max(1, int(SCREEN_HEIGHT * current_window_scale))
125
118
 
126
- new_window = pygame.display.set_mode((window_width, window_height))
119
+ new_window = pygame.display.set_mode(
120
+ (window_width, window_height), pygame.RESIZABLE
121
+ )
127
122
  _update_window_size((window_width, window_height), source="apply_scale")
128
123
  _update_window_caption(window_width, window_height)
129
124
 
@@ -144,19 +139,23 @@ def nudge_window_scale(
144
139
  def toggle_fullscreen(
145
140
  *, game_data: "GameData | None" = None
146
141
  ) -> surface.Surface | None:
147
- """Toggle fullscreen without persisting the setting."""
148
- global current_fullscreen, last_window_scale
149
- if current_fullscreen:
150
- current_fullscreen = False
142
+ """Toggle a maximized window without persisting the setting."""
143
+ global current_maximized, last_window_scale
144
+ if current_maximized:
145
+ current_maximized = False
151
146
  window_width = max(1, int(SCREEN_WIDTH * last_window_scale))
152
147
  window_height = max(1, int(SCREEN_HEIGHT * last_window_scale))
153
- window = pygame.display.set_mode((window_width, window_height))
148
+ window = pygame.display.set_mode(
149
+ (window_width, window_height), pygame.RESIZABLE
150
+ )
151
+ _restore_window()
154
152
  _update_window_caption(window_width, window_height)
155
153
  _update_window_size((window_width, window_height), source="toggle_windowed")
156
154
  else:
157
155
  last_window_scale = current_window_scale
158
- current_fullscreen = True
159
- window = pygame.display.set_mode((0, 0), pygame.FULLSCREEN)
156
+ current_maximized = True
157
+ window = pygame.display.set_mode(_fetch_window_size(None), pygame.RESIZABLE)
158
+ _maximize_window()
160
159
  window_width, window_height = _fetch_window_size(window)
161
160
  _update_window_caption(window_width, window_height)
162
161
  _update_window_size((window_width, window_height), source="toggle_fullscreen")
@@ -182,7 +181,7 @@ def sync_window_size(
182
181
  _update_window_size(
183
182
  (window_width, window_height), source="window_event"
184
183
  )
185
- if not current_fullscreen:
184
+ if not current_maximized:
186
185
  scale_x = window_width / max(1, SCREEN_WIDTH)
187
186
  scale_y = window_height / max(1, SCREEN_HEIGHT)
188
187
  scale = max(WINDOW_SCALE_MIN, min(WINDOW_SCALE_MAX, min(scale_x, scale_y)))
@@ -204,6 +203,7 @@ def _fetch_window_size(window: surface.Surface | None) -> tuple[int, int]:
204
203
  window_height = max(1, int(SCREEN_HEIGHT * last_window_scale))
205
204
  return window_width, window_height
206
205
 
206
+
207
207
  def _normalize_window_size(size: tuple[int, int]) -> tuple[int, int]:
208
208
  width = max(1, int(size[0]))
209
209
  height = max(1, int(size[1]))
@@ -220,3 +220,33 @@ def _update_window_size(size: tuple[int, int], *, source: str) -> None:
220
220
 
221
221
  def _update_window_caption(window_width: int, window_height: int) -> None:
222
222
  pygame.display.set_caption(f"Zombie Escape ({window_width}x{window_height})")
223
+
224
+
225
+ def _maximize_window() -> None:
226
+ try:
227
+ from pygame import _sdl2 as sdl2 # type: ignore[import-not-found]
228
+ except Exception:
229
+ return
230
+ try:
231
+ window = sdl2.Window.from_display_module()
232
+ except Exception:
233
+ return
234
+ try:
235
+ window.maximize()
236
+ except Exception:
237
+ return
238
+
239
+
240
+ def _restore_window() -> None:
241
+ try:
242
+ from pygame import _sdl2 as sdl2 # type: ignore[import-not-found]
243
+ except Exception:
244
+ return
245
+ try:
246
+ window = sdl2.Window.from_display_module()
247
+ except Exception:
248
+ return
249
+ try:
250
+ window.restore()
251
+ except Exception:
252
+ return
@@ -11,7 +11,7 @@ from ..models import GameData, Stage
11
11
  from ..render import (
12
12
  RenderAssets,
13
13
  draw_level_overview,
14
- draw_status_bar,
14
+ _draw_status_bar,
15
15
  show_message,
16
16
  )
17
17
  from ..screens import (
@@ -46,7 +46,7 @@ def game_over_screen(
46
46
 
47
47
  while True:
48
48
  if not state.overview_created:
49
- level_rect = game_data.areas.outer_rect
49
+ level_rect = game_data.layout.outer_rect
50
50
  level_width = level_rect[2]
51
51
  level_height = level_rect[3]
52
52
  overview_surface = pygame.Surface((level_width, level_height))
@@ -154,7 +154,7 @@ def game_over_screen(
154
154
  WHITE,
155
155
  (screen_width // 2, screen_height // 2 + 24),
156
156
  )
157
- draw_status_bar(
157
+ _draw_status_bar(
158
158
  screen,
159
159
  render_assets,
160
160
  config,
@@ -9,11 +9,29 @@ from ..colors import LIGHT_GRAY, RED, WHITE, YELLOW
9
9
  from ..gameplay_constants import (
10
10
  CAR_HINT_DELAY_MS_DEFAULT,
11
11
  DEFAULT_FLASHLIGHT_SPAWN_COUNT,
12
- SURVIVOR_STAGE_WAITING_CAR_COUNT,
13
12
  SURVIVAL_TIME_ACCEL_SUBSTEPS,
14
13
  SURVIVAL_TIME_ACCEL_MAX_SUBSTEP,
15
14
  )
16
- from ..gameplay import logic
15
+ from ..gameplay import (
16
+ apply_passenger_speed_penalty,
17
+ check_interactions,
18
+ cleanup_survivor_messages,
19
+ generate_level_from_blueprint,
20
+ initialize_game_state,
21
+ maintain_waiting_car_supply,
22
+ nearest_waiting_car,
23
+ place_flashlights,
24
+ place_fuel_can,
25
+ process_player_input,
26
+ setup_player_and_cars,
27
+ spawn_initial_zombies,
28
+ spawn_survivors,
29
+ sync_ambient_palette_with_flashlights,
30
+ update_entities,
31
+ update_footprints,
32
+ update_survival_timer,
33
+ )
34
+ from ..gameplay.spawn import _alive_waiting_cars
17
35
  from ..entities import build_wall_index
18
36
  from ..localization import translate as tr
19
37
  from ..models import Stage
@@ -51,7 +69,7 @@ def gameplay_screen(
51
69
  seed_value = seed if seed is not None else generate_seed()
52
70
  applied_seed = seed_rng(seed_value)
53
71
 
54
- game_data = logic.initialize_game_state(config, stage)
72
+ game_data = initialize_game_state(config, stage)
55
73
  game_data.state.seed = applied_seed
56
74
  game_data.state.debug_mode = debug_mode
57
75
  if debug_mode and stage.survival_stage:
@@ -72,28 +90,26 @@ def gameplay_screen(
72
90
  ignore_focus_loss_until = 0
73
91
  last_fov_target = None
74
92
 
75
- layout_data = logic.generate_level_from_blueprint(game_data, config)
76
- logic.sync_ambient_palette_with_flashlights(game_data, force=True)
77
- initial_waiting = (
78
- SURVIVOR_STAGE_WAITING_CAR_COUNT if stage.rescue_stage else 1
79
- )
80
- player, waiting_cars = logic.setup_player_and_cars(
93
+ layout_data = generate_level_from_blueprint(game_data, config)
94
+ sync_ambient_palette_with_flashlights(game_data, force=True)
95
+ initial_waiting = max(0, stage.waiting_car_target_count)
96
+ player, waiting_cars = setup_player_and_cars(
81
97
  game_data, layout_data, car_count=initial_waiting
82
98
  )
83
99
  game_data.player = player
84
100
  game_data.waiting_cars = waiting_cars
85
101
  game_data.car = None
86
102
  # Only top up if initial placement spawned fewer than the intended baseline (shouldn't happen)
87
- logic.maintain_waiting_car_supply(
88
- game_data, minimum=logic.waiting_car_target_count(stage)
103
+ maintain_waiting_car_supply(
104
+ game_data, minimum=stage.waiting_car_target_count
89
105
  )
90
- logic.apply_passenger_speed_penalty(game_data)
106
+ apply_passenger_speed_penalty(game_data)
91
107
 
92
- logic.spawn_survivors(game_data, layout_data)
108
+ spawn_survivors(game_data, layout_data)
93
109
 
94
110
  if stage.requires_fuel:
95
- fuel_spawn_count = getattr(stage, "fuel_spawn_count", 1)
96
- fuel_can = logic.place_fuel_can(
111
+ fuel_spawn_count = stage.fuel_spawn_count
112
+ fuel_can = place_fuel_can(
97
113
  layout_data["walkable_cells"],
98
114
  player,
99
115
  cars=game_data.waiting_cars,
@@ -102,7 +118,7 @@ def gameplay_screen(
102
118
  if fuel_can:
103
119
  game_data.fuel = fuel_can
104
120
  game_data.groups.all_sprites.add(fuel_can, layer=1)
105
- flashlights = logic.place_flashlights(
121
+ flashlights = place_flashlights(
106
122
  layout_data["walkable_cells"],
107
123
  player,
108
124
  cars=game_data.waiting_cars,
@@ -111,8 +127,8 @@ def gameplay_screen(
111
127
  game_data.flashlights = flashlights
112
128
  game_data.groups.all_sprites.add(flashlights, layer=1)
113
129
 
114
- logic.spawn_initial_zombies(game_data, player, layout_data, config)
115
- logic.update_footprints(game_data, config)
130
+ spawn_initial_zombies(game_data, player, layout_data, config)
131
+ update_footprints(game_data, config)
116
132
  last_fov_target = player
117
133
 
118
134
  while True:
@@ -246,17 +262,19 @@ def gameplay_screen(
246
262
  sub_dt = (
247
263
  min(dt, SURVIVAL_TIME_ACCEL_MAX_SUBSTEP) if accel_active else dt
248
264
  )
249
- wall_index = build_wall_index(game_data.groups.wall_group)
265
+ wall_index = build_wall_index(
266
+ game_data.groups.wall_group, cell_size=game_data.cell_size
267
+ )
250
268
  frame_fov_target = None
251
269
  for _ in range(substeps):
252
270
  player_ref = game_data.player
253
271
  if player_ref is None:
254
272
  break
255
273
  car_ref = game_data.car
256
- player_dx, player_dy, car_dx, car_dy = logic.process_player_input(
274
+ player_dx, player_dy, car_dx, car_dy = process_player_input(
257
275
  keys, player_ref, car_ref
258
276
  )
259
- logic.update_entities(
277
+ update_entities(
260
278
  game_data,
261
279
  player_dx,
262
280
  player_dy,
@@ -265,14 +283,14 @@ def gameplay_screen(
265
283
  config,
266
284
  wall_index=wall_index,
267
285
  )
268
- logic.update_footprints(game_data, config)
286
+ update_footprints(game_data, config)
269
287
  step_ms = int(sub_dt * 1000)
270
288
  if accel_active:
271
289
  step_ms = max(1, step_ms)
272
290
  game_data.state.elapsed_play_ms += step_ms
273
- logic.update_survival_timer(game_data, step_ms)
274
- logic.cleanup_survivor_messages(game_data.state)
275
- sub_fov_target = logic.check_interactions(game_data, config)
291
+ update_survival_timer(game_data, step_ms)
292
+ cleanup_survivor_messages(game_data.state)
293
+ sub_fov_target = check_interactions(game_data, config)
276
294
  if sub_fov_target:
277
295
  frame_fov_target = sub_fov_target
278
296
  if game_data.state.game_over or game_data.state.game_won:
@@ -302,7 +320,7 @@ def gameplay_screen(
302
320
  if not has_fuel and game_data.fuel and game_data.fuel.alive():
303
321
  target_type = "fuel"
304
322
  elif not player.in_car and (
305
- active_car or logic.alive_waiting_cars(game_data)
323
+ active_car or _alive_waiting_cars(game_data)
306
324
  ):
307
325
  target_type = "car"
308
326
  else:
@@ -328,7 +346,7 @@ def gameplay_screen(
328
346
  if active_car:
329
347
  hint_target = active_car.rect.center
330
348
  else:
331
- waiting_target = logic.nearest_waiting_car(
349
+ waiting_target = nearest_waiting_car(
332
350
  game_data, (player.x, player.y)
333
351
  )
334
352
  if waiting_target:
@@ -427,8 +427,11 @@ def title_screen(
427
427
  current_seed_text if current_seed_text else tr("menu.seed_empty")
428
428
  )
429
429
  seed_label = tr("menu.seed_label", value=seed_value_display)
430
- seed_surface = hint_font.render(seed_label, False, WHITE)
431
- seed_rect = seed_surface.get_rect(bottomleft=(info_column_x, height - 30))
430
+ seed_surface = hint_font.render(seed_label, False, LIGHT_GRAY)
431
+ seed_offset_y = hint_line_height
432
+ seed_rect = seed_surface.get_rect(
433
+ bottomleft=(info_column_x, height - 30 + seed_offset_y)
434
+ )
432
435
  screen.blit(seed_surface, seed_rect)
433
436
 
434
437
  seed_hint = tr("menu.seed_hint")
@@ -2,7 +2,8 @@
2
2
 
3
3
  from __future__ import annotations
4
4
 
5
- from .gameplay_constants import SURVIVOR_SPAWN_RATE, ZOMBIE_AGING_DURATION_FRAMES
5
+ from .entities_constants import ZOMBIE_AGING_DURATION_FRAMES
6
+ from .gameplay_constants import SURVIVOR_SPAWN_RATE
6
7
  from .models import Stage
7
8
 
8
9
  STAGES: list[Stage] = [
@@ -39,6 +40,7 @@ STAGES: list[Stage] = [
39
40
  description_key="stages.stage4.description",
40
41
  available=True,
41
42
  rescue_stage=True,
43
+ waiting_car_target_count=2,
42
44
  survivor_spawn_rate=SURVIVOR_SPAWN_RATE,
43
45
  ),
44
46
  Stage(
@@ -52,6 +54,7 @@ STAGES: list[Stage] = [
52
54
  fuel_spawn_count=0,
53
55
  exterior_spawn_weight=0.4,
54
56
  interior_spawn_weight=0.6,
57
+ initial_interior_spawn_rate=0.04,
55
58
  ),
56
59
  Stage(
57
60
  id="stage6",
@@ -70,6 +73,7 @@ STAGES: list[Stage] = [
70
73
  name_key="stages.stage7.name",
71
74
  description_key="stages.stage7.description",
72
75
  available=True,
76
+ wall_algorithm="grid_wire",
73
77
  buddy_required_count=1,
74
78
  requires_fuel=True,
75
79
  exterior_spawn_weight=0.7,
@@ -79,6 +83,35 @@ STAGES: list[Stage] = [
79
83
  zombie_wall_follower_ratio=0.3,
80
84
  zombie_aging_duration_frames=ZOMBIE_AGING_DURATION_FRAMES * 2,
81
85
  ),
86
+ Stage(
87
+ id="stage8",
88
+ name_key="stages.stage8.name",
89
+ description_key="stages.stage8.description",
90
+ available=True,
91
+ tile_size=35,
92
+ wall_algorithm="grid_wire",
93
+ requires_fuel=True,
94
+ exterior_spawn_weight=0.4,
95
+ interior_spawn_weight=0.6,
96
+ zombie_normal_ratio=0,
97
+ zombie_tracker_ratio=0.3,
98
+ zombie_wall_follower_ratio=0.7,
99
+ zombie_aging_duration_frames=ZOMBIE_AGING_DURATION_FRAMES * 2,
100
+ ),
101
+ Stage(
102
+ id="stage9",
103
+ name_key="stages.stage9.name",
104
+ description_key="stages.stage9.description",
105
+ available=False,
106
+ rescue_stage=True,
107
+ tile_size=35,
108
+ requires_fuel=True,
109
+ exterior_spawn_weight=0.4,
110
+ interior_spawn_weight=0.6,
111
+ waiting_car_target_count=1,
112
+ survivor_spawn_rate=SURVIVOR_SPAWN_RATE,
113
+ zombie_aging_duration_frames=ZOMBIE_AGING_DURATION_FRAMES * 2,
114
+ ),
82
115
  ]
83
116
  DEFAULT_STAGE_ID = "stage1"
84
117
 
@@ -1,9 +1,9 @@
1
1
  from __future__ import annotations
2
2
 
3
3
  import argparse
4
- from pathlib import Path
5
4
  import sys
6
5
  import traceback # For error reporting
6
+ from pathlib import Path
7
7
  from typing import Any, Tuple
8
8
 
9
9
  import pygame
@@ -13,32 +13,41 @@ try:
13
13
  except Exception: # pragma: no cover - fallback version
14
14
  __version__ = "0.0.0-unknown"
15
15
  from .config import load_config, save_config
16
- from .gameplay_constants import (
16
+ from .entities_constants import (
17
17
  CAR_SPEED,
18
18
  SURVIVOR_MAX_SAFE_PASSENGERS,
19
19
  SURVIVOR_MIN_SPEED_FACTOR,
20
20
  )
21
- from .render_constants import RENDER_ASSETS
21
+ from .gameplay import calculate_car_speed_for_passengers
22
+ from .level_constants import DEFAULT_TILE_SIZE
23
+ from .localization import set_language
24
+ from .models import GameData, Stage
25
+ from .render_constants import build_render_assets
22
26
  from .screen_constants import (
23
27
  DEFAULT_WINDOW_SCALE,
24
28
  FPS,
25
29
  SCREEN_HEIGHT,
26
30
  SCREEN_WIDTH,
27
31
  )
28
- from .localization import set_language
29
- from .models import GameData, Stage
30
- from .stage_constants import DEFAULT_STAGE_ID, STAGES
31
32
  from .screens import ScreenID, ScreenTransition, apply_window_scale
32
33
  from .screens.game_over import game_over_screen
33
34
  from .screens.settings import settings_screen
34
35
  from .screens.title import MAX_SEED_DIGITS, title_screen
35
- from .gameplay.logic import calculate_car_speed_for_passengers
36
+ from .stage_constants import DEFAULT_STAGE_ID, STAGES
36
37
 
37
38
 
38
39
  def _parse_cli_args(argv: list[str]) -> Tuple[argparse.Namespace, list[str]]:
39
40
  parser = argparse.ArgumentParser(add_help=False)
40
- parser.add_argument("--debug", action="store_true", help="Enable debugging aids for Stage 5 and hide pause overlay")
41
- parser.add_argument("--profile", action="store_true", help="Profile gameplay and write cProfile output to disk")
41
+ parser.add_argument(
42
+ "--debug",
43
+ action="store_true",
44
+ help="Enable debugging aids for Stage 5 and hide pause overlay",
45
+ )
46
+ parser.add_argument(
47
+ "--profile",
48
+ action="store_true",
49
+ help="Profile gameplay and write cProfile output to disk",
50
+ )
42
51
  parser.add_argument(
43
52
  "--profile-output",
44
53
  default="profile.prof",
@@ -57,6 +66,7 @@ def _sanitize_seed_text(raw: str | None) -> tuple[str | None, bool]:
57
66
  return None, True
58
67
  return stripped[:MAX_SEED_DIGITS], False
59
68
 
69
+
60
70
  # Re-export the gameplay helpers constants for external callers/tests.
61
71
  __all__ = [
62
72
  "main",
@@ -149,6 +159,7 @@ def main() -> None:
149
159
 
150
160
  profiler = cProfile.Profile()
151
161
  try:
162
+ render_assets = build_render_assets(stage.tile_size)
152
163
  transition = profiler.runcall(
153
164
  gameplay_screen,
154
165
  screen,
@@ -158,7 +169,7 @@ def main() -> None:
158
169
  stage,
159
170
  show_pause_overlay=not debug_mode,
160
171
  seed=seed_value,
161
- render_assets=RENDER_ASSETS,
172
+ render_assets=render_assets,
162
173
  debug_mode=debug_mode,
163
174
  )
164
175
  finally:
@@ -171,6 +182,7 @@ def main() -> None:
171
182
  stats.print_stats(50)
172
183
  print(f"Profile saved to {output_path} and {summary_path}")
173
184
  else:
185
+ render_assets = build_render_assets(stage.tile_size)
174
186
  transition = gameplay_screen(
175
187
  screen,
176
188
  clock,
@@ -179,7 +191,7 @@ def main() -> None:
179
191
  stage,
180
192
  show_pause_overlay=not debug_mode,
181
193
  seed=seed_value,
182
- render_assets=RENDER_ASSETS,
194
+ render_assets=render_assets,
183
195
  debug_mode=debug_mode,
184
196
  )
185
197
  except SystemExit:
@@ -197,6 +209,12 @@ def main() -> None:
197
209
  pending_game_data = None
198
210
  pending_stage = None
199
211
  pending_config = None
212
+ if game_data is not None:
213
+ render_assets = build_render_assets(game_data.cell_size)
214
+ elif stage is not None:
215
+ render_assets = build_render_assets(stage.tile_size)
216
+ else:
217
+ render_assets = build_render_assets(DEFAULT_TILE_SIZE)
200
218
  transition = game_over_screen(
201
219
  screen,
202
220
  clock,
@@ -204,7 +222,7 @@ def main() -> None:
204
222
  FPS,
205
223
  game_data=game_data,
206
224
  stage=stage,
207
- render_assets=RENDER_ASSETS,
225
+ render_assets=render_assets,
208
226
  )
209
227
  elif next_screen == ScreenID.EXIT:
210
228
  break
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: zombie-escape
3
- Version: 1.5.4
3
+ Version: 1.7.1
4
4
  Summary: Top-down zombie survival game built with pygame.
5
5
  Project-URL: Homepage, https://github.com/tos-kamiya/zombie-escape
6
6
  Author-email: Toshihiro Kamiya <kamiya@mbj.nifty.com>
@@ -0,0 +1,45 @@
1
+ zombie_escape/__about__.py,sha256=lIj6_cutstDqHoeRxShnR7KSs9SmMDnDcdAglP59kh0,134
2
+ zombie_escape/__init__.py,sha256=YSQnUghet8jxSvaGmKfzHfXXLlnvWh_xk10WGTDO2HM,173
3
+ zombie_escape/colors.py,sha256=Cmu3wwSQgMqu75bTtsD3G85daQd96zNErnvGfXi3iVM,5771
4
+ zombie_escape/config.py,sha256=Ncvsz6HzBknSjecorkm7CrkrzWUIksD30ykLPueanyw,2008
5
+ zombie_escape/entities.py,sha256=50KJi3QkbTdcrTs-NOCEh_h9oLb9o7uU4K-pvZjfsH4,52101
6
+ zombie_escape/entities_constants.py,sha256=WDQslNovvstSSavZCT0vrD4PESn0rVdKc1L4BqxLY0g,2851
7
+ zombie_escape/font_utils.py,sha256=kkjcSlCTG3jO5zf5XUnirpJ-iL_Eg8ahzjZYGijF2JY,1206
8
+ zombie_escape/gameplay_constants.py,sha256=I5g2xsd4Rck4d5tbWae2bm6Yfwp4ZAgujDLnDMsHxgM,758
9
+ zombie_escape/level_blueprints.py,sha256=l7Xhg9bUp7Y5REpJKZtHcoWBO4r1sv0R34n77FLT5cE,9629
10
+ zombie_escape/level_constants.py,sha256=fSrPXfkuKHlv9XqmaRq6aR9UhjpqZK2iJJgMc-TXGXc,281
11
+ zombie_escape/localization.py,sha256=PK_lbKJjpzvLQr6xGvSiomXjKOxcbePtMbQkuX8DTF4,5322
12
+ zombie_escape/models.py,sha256=noFWxsfudlzthFQF0L_KGOSs7IKp5UzkiIJ82mCFdGI,3648
13
+ zombie_escape/progress.py,sha256=WCFc7JeMY6noBjnTIFyHrXQJSM1j8PwyPA7S8ZQwjTE,1713
14
+ zombie_escape/render.py,sha256=VHiz4woL-qWbeNPX0q7ZPZnaPB5BPqIBzL7AKxx7Us0,30862
15
+ zombie_escape/render_assets.py,sha256=66lRKQZ9fII6930WeQchIJRFeLLu7J3Cr155svpDrUM,16489
16
+ zombie_escape/render_constants.py,sha256=yJ26KA7DF6ndYG3kAFaDkL-ljN1MgWbSEhR6nmWzzWI,2315
17
+ zombie_escape/rng.py,sha256=gMAgpzYoNN1FxRG3aQ9fdXTDNAg48Rqz8YnB1nJ4Fpw,3787
18
+ zombie_escape/screen_constants.py,sha256=MJaTlSWfN4VtN6pMqPQ6LF34XdJm0wqYLuRwa1pQuAU,559
19
+ zombie_escape/stage_constants.py,sha256=ED-T_pXUJpbvChmjmzEQgjlR_WIFQXl_xw9bmACwAos,3583
20
+ zombie_escape/zombie_escape.py,sha256=8RGn5bDX5kCFXgaM5JYE7_hmS7IHObyAJ9APdF7xTRw,8841
21
+ zombie_escape/assets/fonts/Silkscreen-Regular.ttf,sha256=SVZ0CGAICeJRR-kiWsTzf0EOLfRadQaWxFAnUx-2Xxs,31960
22
+ zombie_escape/assets/fonts/misaki_gothic.ttf,sha256=CWPhHonV-kCaegSKUujqLWI9dkp5mEiPikKRERYRxOE,1171204
23
+ zombie_escape/gameplay/__init__.py,sha256=hv-37H7R7cQrK0Qr7FkcakwYME26pi31LC4r25s2Dxk,2349
24
+ zombie_escape/gameplay/ambient.py,sha256=hoCOz6ciyejU0nmJwdLqmVfaoo-01CrVSMRLpFMz93w,1446
25
+ zombie_escape/gameplay/constants.py,sha256=5n6IctC4HlquG0k8SJI3xv0wBTiw0-j33ALwBaRZJrw,1214
26
+ zombie_escape/gameplay/footprints.py,sha256=LQ3wZNovzgO54kaGncZT4yrhnmoZVfROkTQ6wL_TVSY,1781
27
+ zombie_escape/gameplay/interactions.py,sha256=QFtjv-5t2G6ek4AC3y5XWl15XQl5q9oK80ODNvd_Zt8,13476
28
+ zombie_escape/gameplay/layout.py,sha256=m3QRbICDia9g2jEIiH_nQIW_27VeJyO__v1tFcSXbzI,6604
29
+ zombie_escape/gameplay/movement.py,sha256=hZuHIwdmJAjXX7B7Qt_5vSEcjY5q3PH9UfsbzjBBfaE,7969
30
+ zombie_escape/gameplay/spawn.py,sha256=qQnW5ZwvzLpqwzB9wss51pOjz-_XAfUVuwMogjXj5wQ,19774
31
+ zombie_escape/gameplay/state.py,sha256=Yq2qIs7R9wt00svjK4CrEWm2jOyNGqTTGGRh8Gto2xo,4489
32
+ zombie_escape/gameplay/survivors.py,sha256=bKUtwCS1qpIJUOVxh9413YIN1ZIV_VOfJJ3eqeKUetg,10469
33
+ zombie_escape/gameplay/utils.py,sha256=2IwsQ1Fv8qVUWMP9JZ77tCV1slEJrokkiXAvQi7u81k,4921
34
+ zombie_escape/locales/ui.en.json,sha256=zs3kffyaMdWRHdky8u7WFpqWujxzinSf5iHw1kNn7A0,4904
35
+ zombie_escape/locales/ui.ja.json,sha256=_KOpdVJAbQJg-Z_q8oFeNYnSDB-YdAJKoTvmc6gQbB0,5336
36
+ zombie_escape/screens/__init__.py,sha256=P5aDyqJ3ZXdTVi0AAsxK9scc0lPuydc-XHplNxpDbu0,8335
37
+ zombie_escape/screens/game_over.py,sha256=_INwL2RYca4brIBErs9rAUPB9DtXYG0mYcSuT3RIA-4,6417
38
+ zombie_escape/screens/gameplay.py,sha256=2tZl4lbFetlbFZOAqSNn3omv2p4H34pLD7rIqy7wZiM,13141
39
+ zombie_escape/screens/settings.py,sha256=_KKdnnVdC-8KhMGVsQWWe0t00xa-ZQkatVQ7h7Vxs4c,16792
40
+ zombie_escape/screens/title.py,sha256=rrXHWjag9j1khKvnU4NQgFg4x0V6FhiSdR0oTMOBmnc,17998
41
+ zombie_escape-1.7.1.dist-info/METADATA,sha256=iCCHXxeJ2kVJHkLlSC_1qvyB5mdLh_SktRAG_-_vK5w,10116
42
+ zombie_escape-1.7.1.dist-info/WHEEL,sha256=WLgqFyCfm_KASv4WHyYy0P3pM_m7J5L9k2skdKLirC8,87
43
+ zombie_escape-1.7.1.dist-info/entry_points.txt,sha256=JprxC-vvkBJgsOp0WJnGBZRJ_ESjjmyS-nsPExeiLHU,49
44
+ zombie_escape-1.7.1.dist-info/licenses/LICENSE.txt,sha256=q-cJYG_K766eXSxQ7txWcWQ6nS2OF6c3HTVLesHbesU,1104
45
+ zombie_escape-1.7.1.dist-info/RECORD,,