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.
- zombie_escape/__about__.py +1 -1
- zombie_escape/entities.py +501 -537
- zombie_escape/entities_constants.py +102 -0
- zombie_escape/gameplay/__init__.py +75 -2
- zombie_escape/gameplay/ambient.py +50 -0
- zombie_escape/gameplay/constants.py +46 -0
- zombie_escape/gameplay/footprints.py +60 -0
- zombie_escape/gameplay/interactions.py +354 -0
- zombie_escape/gameplay/layout.py +190 -0
- zombie_escape/gameplay/movement.py +220 -0
- zombie_escape/gameplay/spawn.py +618 -0
- zombie_escape/gameplay/state.py +137 -0
- zombie_escape/gameplay/survivors.py +306 -0
- zombie_escape/gameplay/utils.py +147 -0
- zombie_escape/gameplay_constants.py +0 -148
- zombie_escape/level_blueprints.py +123 -10
- zombie_escape/level_constants.py +6 -13
- zombie_escape/locales/ui.en.json +10 -1
- zombie_escape/locales/ui.ja.json +10 -1
- zombie_escape/models.py +15 -9
- zombie_escape/render.py +42 -27
- zombie_escape/render_assets.py +533 -23
- zombie_escape/render_constants.py +57 -22
- zombie_escape/rng.py +9 -9
- zombie_escape/screens/__init__.py +59 -29
- zombie_escape/screens/game_over.py +3 -3
- zombie_escape/screens/gameplay.py +45 -27
- zombie_escape/screens/title.py +5 -2
- zombie_escape/stage_constants.py +34 -1
- zombie_escape/zombie_escape.py +30 -12
- {zombie_escape-1.5.4.dist-info → zombie_escape-1.7.1.dist-info}/METADATA +1 -1
- zombie_escape-1.7.1.dist-info/RECORD +45 -0
- zombie_escape/gameplay/logic.py +0 -1917
- zombie_escape-1.5.4.dist-info/RECORD +0 -35
- {zombie_escape-1.5.4.dist-info → zombie_escape-1.7.1.dist-info}/WHEEL +0 -0
- {zombie_escape-1.5.4.dist-info → zombie_escape-1.7.1.dist-info}/entry_points.txt +0 -0
- {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.
|
|
29
|
-
self.
|
|
28
|
+
self.__seed_value: int | None = None
|
|
29
|
+
self._seed(seed)
|
|
30
30
|
|
|
31
|
-
def
|
|
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.
|
|
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
|
|
53
|
-
return self.
|
|
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.
|
|
123
|
-
assert _GLOBAL_RNG.
|
|
124
|
-
return _GLOBAL_RNG.
|
|
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
|
-
|
|
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
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
)
|
|
91
|
-
|
|
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
|
|
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,
|
|
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
|
-
|
|
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(
|
|
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
|
|
148
|
-
global
|
|
149
|
-
if
|
|
150
|
-
|
|
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(
|
|
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
|
-
|
|
159
|
-
window = pygame.display.set_mode((
|
|
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
|
|
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
|
-
|
|
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.
|
|
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
|
-
|
|
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
|
|
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 =
|
|
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 =
|
|
76
|
-
|
|
77
|
-
initial_waiting = (
|
|
78
|
-
|
|
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
|
-
|
|
88
|
-
game_data, minimum=
|
|
103
|
+
maintain_waiting_car_supply(
|
|
104
|
+
game_data, minimum=stage.waiting_car_target_count
|
|
89
105
|
)
|
|
90
|
-
|
|
106
|
+
apply_passenger_speed_penalty(game_data)
|
|
91
107
|
|
|
92
|
-
|
|
108
|
+
spawn_survivors(game_data, layout_data)
|
|
93
109
|
|
|
94
110
|
if stage.requires_fuel:
|
|
95
|
-
fuel_spawn_count =
|
|
96
|
-
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 =
|
|
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
|
-
|
|
115
|
-
|
|
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(
|
|
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 =
|
|
274
|
+
player_dx, player_dy, car_dx, car_dy = process_player_input(
|
|
257
275
|
keys, player_ref, car_ref
|
|
258
276
|
)
|
|
259
|
-
|
|
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
|
-
|
|
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
|
-
|
|
274
|
-
|
|
275
|
-
sub_fov_target =
|
|
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
|
|
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 =
|
|
349
|
+
waiting_target = nearest_waiting_car(
|
|
332
350
|
game_data, (player.x, player.y)
|
|
333
351
|
)
|
|
334
352
|
if waiting_target:
|
zombie_escape/screens/title.py
CHANGED
|
@@ -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,
|
|
431
|
-
|
|
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")
|
zombie_escape/stage_constants.py
CHANGED
|
@@ -2,7 +2,8 @@
|
|
|
2
2
|
|
|
3
3
|
from __future__ import annotations
|
|
4
4
|
|
|
5
|
-
from .
|
|
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
|
|
zombie_escape/zombie_escape.py
CHANGED
|
@@ -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 .
|
|
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 .
|
|
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 .
|
|
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(
|
|
41
|
-
|
|
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=
|
|
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=
|
|
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=
|
|
225
|
+
render_assets=render_assets,
|
|
208
226
|
)
|
|
209
227
|
elif next_screen == ScreenID.EXIT:
|
|
210
228
|
break
|
|
@@ -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,,
|