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
|
@@ -2,177 +2,29 @@
|
|
|
2
2
|
|
|
3
3
|
from __future__ import annotations
|
|
4
4
|
|
|
5
|
-
from .screen_constants import FPS
|
|
6
|
-
|
|
7
5
|
# --- Survival stage settings ---
|
|
8
6
|
SURVIVAL_TIME_ACCEL_SUBSTEPS = 4
|
|
9
7
|
SURVIVAL_TIME_ACCEL_MAX_SUBSTEP = 1.0 / 30.0
|
|
10
8
|
SURVIVAL_FAKE_CLOCK_RATIO = 12.0 # 20 min -> 4 hr clock
|
|
11
|
-
SURVIVAL_NEAR_SPAWN_MIN_DISTANCE = 140
|
|
12
|
-
SURVIVAL_NEAR_SPAWN_MAX_DISTANCE = 280
|
|
13
|
-
SURVIVAL_NEAR_SPAWN_CAMERA_MARGIN = 20
|
|
14
|
-
|
|
15
|
-
# --- Player and buddy settings ---
|
|
16
|
-
HUMANOID_RADIUS = 6
|
|
17
|
-
HUMANOID_OUTLINE_COLOR = (0, 80, 200)
|
|
18
|
-
HUMANOID_OUTLINE_WIDTH = 1
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
def interaction_radius(width: float, height: float) -> float:
|
|
22
|
-
"""Approximate interaction reach for a humanoid and an object."""
|
|
23
|
-
return HUMANOID_RADIUS + (width + height) / 4
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
def car_body_radius(width: float, height: float) -> float:
|
|
27
|
-
"""Approximate car collision radius using only its own dimensions."""
|
|
28
|
-
return min(width, height) / 2
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
PLAYER_RADIUS = HUMANOID_RADIUS
|
|
32
|
-
PLAYER_SPEED = 1.4
|
|
33
|
-
FOV_RADIUS = 124 # approximate legacy FOV (80) * 1.55 cap
|
|
34
|
-
BUDDY_RADIUS = HUMANOID_RADIUS
|
|
35
|
-
BUDDY_FOLLOW_SPEED = PLAYER_SPEED * 0.7
|
|
36
|
-
BUDDY_COLOR = (0, 180, 63)
|
|
37
9
|
|
|
38
10
|
# --- Survivor settings (Stage 4) ---
|
|
39
|
-
SURVIVOR_RADIUS = HUMANOID_RADIUS
|
|
40
|
-
SURVIVOR_COLOR = (198, 198, 198)
|
|
41
|
-
SURVIVOR_APPROACH_RADIUS = 48
|
|
42
|
-
SURVIVOR_APPROACH_SPEED = PLAYER_SPEED * 0.35
|
|
43
11
|
SURVIVOR_SPAWN_RATE = 0.07
|
|
44
|
-
SURVIVOR_MAX_SAFE_PASSENGERS = 5
|
|
45
|
-
SURVIVOR_SPEED_PENALTY_PER_PASSENGER = 0.08
|
|
46
|
-
SURVIVOR_MIN_SPEED_FACTOR = 0.35
|
|
47
|
-
SURVIVOR_OVERLOAD_DAMAGE_RATIO = 0.2
|
|
48
|
-
SURVIVOR_MESSAGE_DURATION_MS = 2000
|
|
49
|
-
SURVIVOR_CONVERSION_LINE_KEYS = [
|
|
50
|
-
"stages.stage4.conversion_lines.line1",
|
|
51
|
-
"stages.stage4.conversion_lines.line2",
|
|
52
|
-
"stages.stage4.conversion_lines.line3",
|
|
53
|
-
]
|
|
54
|
-
SURVIVOR_STAGE_WAITING_CAR_COUNT = 2
|
|
55
12
|
|
|
56
13
|
# --- Flashlight settings ---
|
|
57
|
-
FLASHLIGHT_WIDTH = 10
|
|
58
|
-
FLASHLIGHT_HEIGHT = 8
|
|
59
14
|
DEFAULT_FLASHLIGHT_SPAWN_COUNT = 2
|
|
60
15
|
|
|
61
|
-
# --- Footprint settings (gameplay) ---
|
|
62
|
-
FOOTPRINT_STEP_DISTANCE = 40
|
|
63
|
-
FOOTPRINT_MAX = 320
|
|
64
|
-
|
|
65
16
|
# --- Zombie settings ---
|
|
66
|
-
ZOMBIE_RADIUS = HUMANOID_RADIUS
|
|
67
|
-
ZOMBIE_SPEED = PLAYER_SPEED * 0.45
|
|
68
17
|
ZOMBIE_SPAWN_DELAY_MS = 4000
|
|
69
|
-
MAX_ZOMBIES = 400
|
|
70
|
-
ZOMBIE_SPAWN_PLAYER_BUFFER = 140
|
|
71
|
-
ZOMBIE_WANDER_INTERVAL_MS = 5000
|
|
72
|
-
ZOMBIE_SIGHT_RANGE = FOV_RADIUS * 1.2
|
|
73
|
-
ZOMBIE_TRACKER_SIGHT_RANGE = ZOMBIE_SIGHT_RANGE * 0.2
|
|
74
|
-
FAST_ZOMBIE_BASE_SPEED = PLAYER_SPEED * 0.77
|
|
75
|
-
ZOMBIE_SEPARATION_DISTANCE = ZOMBIE_RADIUS * 2.2
|
|
76
|
-
ZOMBIE_AGING_DURATION_FRAMES = FPS * 60 * 6 # ~6 minutes at target framerate
|
|
77
|
-
ZOMBIE_AGING_MIN_SPEED_RATIO = 0.3
|
|
78
|
-
ZOMBIE_TRACKER_SCENT_RADIUS = 70
|
|
79
|
-
ZOMBIE_TRACKER_WANDER_INTERVAL_MS = 2500
|
|
80
|
-
ZOMBIE_TRACKER_AGING_DURATION_FRAMES = ZOMBIE_AGING_DURATION_FRAMES
|
|
81
|
-
ZOMBIE_WALL_FOLLOW_SENSOR_DISTANCE = 24
|
|
82
|
-
ZOMBIE_WALL_FOLLOW_PROBE_ANGLE_DEG = 45
|
|
83
|
-
ZOMBIE_WALL_FOLLOW_PROBE_STEP = 2.0
|
|
84
|
-
ZOMBIE_WALL_FOLLOW_TARGET_GAP = 4.0
|
|
85
|
-
ZOMBIE_WALL_FOLLOW_LOST_WALL_MS = 2500
|
|
86
18
|
|
|
87
19
|
# --- Car and fuel settings ---
|
|
88
|
-
CAR_WIDTH = 15
|
|
89
|
-
CAR_HEIGHT = 25
|
|
90
|
-
CAR_SPEED = 2
|
|
91
|
-
CAR_HEALTH = 20
|
|
92
|
-
CAR_WALL_DAMAGE = 1
|
|
93
|
-
CAR_ZOMBIE_DAMAGE = 1
|
|
94
20
|
CAR_HINT_DELAY_MS_DEFAULT = 300000
|
|
95
|
-
FUEL_CAN_WIDTH = 11
|
|
96
|
-
FUEL_CAN_HEIGHT = 15
|
|
97
|
-
FUEL_HINT_DURATION_MS = 1600
|
|
98
|
-
|
|
99
|
-
# --- Wall and beam settings ---
|
|
100
|
-
INTERNAL_WALL_HEALTH = 40 * 100
|
|
101
|
-
INTERNAL_WALL_BEVEL_DEPTH = 14
|
|
102
|
-
OUTER_WALL_HEALTH = 999999
|
|
103
|
-
STEEL_BEAM_HEALTH = int(INTERNAL_WALL_HEALTH * 1.5)
|
|
104
|
-
PLAYER_WALL_DAMAGE = 100
|
|
105
|
-
ZOMBIE_WALL_DAMAGE = 1
|
|
106
21
|
|
|
107
22
|
__all__ = [
|
|
108
23
|
"SURVIVAL_TIME_ACCEL_SUBSTEPS",
|
|
109
24
|
"SURVIVAL_TIME_ACCEL_MAX_SUBSTEP",
|
|
110
25
|
"SURVIVAL_FAKE_CLOCK_RATIO",
|
|
111
|
-
"SURVIVAL_NEAR_SPAWN_MIN_DISTANCE",
|
|
112
|
-
"SURVIVAL_NEAR_SPAWN_MAX_DISTANCE",
|
|
113
|
-
"SURVIVAL_NEAR_SPAWN_CAMERA_MARGIN",
|
|
114
|
-
"HUMANOID_RADIUS",
|
|
115
|
-
"HUMANOID_OUTLINE_COLOR",
|
|
116
|
-
"HUMANOID_OUTLINE_WIDTH",
|
|
117
|
-
"interaction_radius",
|
|
118
|
-
"car_body_radius",
|
|
119
|
-
"PLAYER_RADIUS",
|
|
120
|
-
"PLAYER_SPEED",
|
|
121
|
-
"FOV_RADIUS",
|
|
122
|
-
"BUDDY_RADIUS",
|
|
123
|
-
"BUDDY_FOLLOW_SPEED",
|
|
124
|
-
"BUDDY_COLOR",
|
|
125
|
-
"SURVIVOR_RADIUS",
|
|
126
|
-
"SURVIVOR_COLOR",
|
|
127
|
-
"SURVIVOR_APPROACH_RADIUS",
|
|
128
|
-
"SURVIVOR_APPROACH_SPEED",
|
|
129
26
|
"SURVIVOR_SPAWN_RATE",
|
|
130
|
-
"SURVIVOR_MAX_SAFE_PASSENGERS",
|
|
131
|
-
"SURVIVOR_SPEED_PENALTY_PER_PASSENGER",
|
|
132
|
-
"SURVIVOR_MIN_SPEED_FACTOR",
|
|
133
|
-
"SURVIVOR_OVERLOAD_DAMAGE_RATIO",
|
|
134
|
-
"SURVIVOR_MESSAGE_DURATION_MS",
|
|
135
|
-
"SURVIVOR_CONVERSION_LINE_KEYS",
|
|
136
|
-
"SURVIVOR_STAGE_WAITING_CAR_COUNT",
|
|
137
|
-
"FLASHLIGHT_WIDTH",
|
|
138
|
-
"FLASHLIGHT_HEIGHT",
|
|
139
27
|
"DEFAULT_FLASHLIGHT_SPAWN_COUNT",
|
|
140
|
-
"FOOTPRINT_STEP_DISTANCE",
|
|
141
|
-
"FOOTPRINT_MAX",
|
|
142
|
-
"ZOMBIE_RADIUS",
|
|
143
|
-
"ZOMBIE_SPEED",
|
|
144
28
|
"ZOMBIE_SPAWN_DELAY_MS",
|
|
145
|
-
"MAX_ZOMBIES",
|
|
146
|
-
"ZOMBIE_SPAWN_PLAYER_BUFFER",
|
|
147
|
-
"ZOMBIE_WANDER_INTERVAL_MS",
|
|
148
|
-
"ZOMBIE_SIGHT_RANGE",
|
|
149
|
-
"ZOMBIE_TRACKER_SIGHT_RANGE",
|
|
150
|
-
"FAST_ZOMBIE_BASE_SPEED",
|
|
151
|
-
"ZOMBIE_SEPARATION_DISTANCE",
|
|
152
|
-
"ZOMBIE_AGING_DURATION_FRAMES",
|
|
153
|
-
"ZOMBIE_AGING_MIN_SPEED_RATIO",
|
|
154
|
-
"ZOMBIE_TRACKER_SCENT_RADIUS",
|
|
155
|
-
"ZOMBIE_TRACKER_WANDER_INTERVAL_MS",
|
|
156
|
-
"ZOMBIE_TRACKER_AGING_DURATION_FRAMES",
|
|
157
|
-
"ZOMBIE_WALL_FOLLOW_SENSOR_DISTANCE",
|
|
158
|
-
"ZOMBIE_WALL_FOLLOW_PROBE_ANGLE_DEG",
|
|
159
|
-
"ZOMBIE_WALL_FOLLOW_PROBE_STEP",
|
|
160
|
-
"ZOMBIE_WALL_FOLLOW_TARGET_GAP",
|
|
161
|
-
"ZOMBIE_WALL_FOLLOW_LOST_WALL_MS",
|
|
162
|
-
"CAR_WIDTH",
|
|
163
|
-
"CAR_HEIGHT",
|
|
164
|
-
"CAR_SPEED",
|
|
165
|
-
"CAR_HEALTH",
|
|
166
|
-
"CAR_WALL_DAMAGE",
|
|
167
|
-
"CAR_ZOMBIE_DAMAGE",
|
|
168
29
|
"CAR_HINT_DELAY_MS_DEFAULT",
|
|
169
|
-
"FUEL_CAN_WIDTH",
|
|
170
|
-
"FUEL_CAN_HEIGHT",
|
|
171
|
-
"FUEL_HINT_DURATION_MS",
|
|
172
|
-
"INTERNAL_WALL_HEALTH",
|
|
173
|
-
"INTERNAL_WALL_BEVEL_DEPTH",
|
|
174
|
-
"OUTER_WALL_HEALTH",
|
|
175
|
-
"STEEL_BEAM_HEALTH",
|
|
176
|
-
"PLAYER_WALL_DAMAGE",
|
|
177
|
-
"ZOMBIE_WALL_DAMAGE",
|
|
178
30
|
]
|
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
# Blueprint generator for randomized layouts.
|
|
2
2
|
|
|
3
|
-
from .level_constants import GRID_COLS, GRID_ROWS
|
|
4
3
|
from .rng import get_rng
|
|
5
4
|
|
|
6
5
|
EXITS_PER_SIDE = 1 # currently fixed to 1 per side (can be tuned)
|
|
@@ -63,7 +62,7 @@ def _place_exits(grid: list[list[str]], exits_per_side: int) -> None:
|
|
|
63
62
|
rng = RNG.randint
|
|
64
63
|
used = set()
|
|
65
64
|
|
|
66
|
-
def
|
|
65
|
+
def _pick_pos(side: str) -> tuple[int, int]:
|
|
67
66
|
if side in ("top", "bottom"):
|
|
68
67
|
x = rng(2, cols - 3)
|
|
69
68
|
y = 1 if side == "top" else rows - 2
|
|
@@ -74,17 +73,17 @@ def _place_exits(grid: list[list[str]], exits_per_side: int) -> None:
|
|
|
74
73
|
|
|
75
74
|
for side in ("top", "bottom", "left", "right"):
|
|
76
75
|
for _ in range(exits_per_side):
|
|
77
|
-
x, y =
|
|
76
|
+
x, y = _pick_pos(side)
|
|
78
77
|
# avoid duplicates; retry a few times
|
|
79
78
|
for _ in range(10):
|
|
80
79
|
if (x, y) not in used:
|
|
81
80
|
break
|
|
82
|
-
x, y =
|
|
81
|
+
x, y = _pick_pos(side)
|
|
83
82
|
used.add((x, y))
|
|
84
83
|
grid[y][x] = "E"
|
|
85
84
|
|
|
86
85
|
|
|
87
|
-
def
|
|
86
|
+
def _place_walls_default(grid: list[list[str]]) -> None:
|
|
88
87
|
cols, rows = len(grid[0]), len(grid)
|
|
89
88
|
rng = RNG.randint
|
|
90
89
|
# Avoid placing walls adjacent to exits: collect forbidden cells (exits + neighbors)
|
|
@@ -111,6 +110,104 @@ def _place_internal_walls(grid: list[list[str]]) -> None:
|
|
|
111
110
|
grid[y + i][x] = "1"
|
|
112
111
|
|
|
113
112
|
|
|
113
|
+
def _place_walls_empty(grid: list[list[str]]) -> None:
|
|
114
|
+
"""Place no internal walls (open floor plan)."""
|
|
115
|
+
pass
|
|
116
|
+
|
|
117
|
+
|
|
118
|
+
def _place_walls_grid_wire(grid: list[list[str]]) -> None:
|
|
119
|
+
"""
|
|
120
|
+
Place walls using a 2-pass approach with independent layers.
|
|
121
|
+
Vertical and horizontal walls are generated on separate grids to ensure
|
|
122
|
+
one orientation doesn't block the other during generation.
|
|
123
|
+
Finally, they are merged.
|
|
124
|
+
Strictly forbids parallel adjacency within the same orientation layer.
|
|
125
|
+
"""
|
|
126
|
+
cols, rows = len(grid[0]), len(grid)
|
|
127
|
+
rng = RNG.randint
|
|
128
|
+
forbidden = _collect_exit_adjacent_cells(grid)
|
|
129
|
+
|
|
130
|
+
# Temporary grids for independent generation
|
|
131
|
+
# They only track the internal walls ("1").
|
|
132
|
+
grid_v = [["." for _ in range(cols)] for _ in range(rows)]
|
|
133
|
+
grid_h = [["." for _ in range(cols)] for _ in range(rows)]
|
|
134
|
+
|
|
135
|
+
# Use a similar density to default.
|
|
136
|
+
lines_per_pass = int(NUM_WALL_LINES * 0.7)
|
|
137
|
+
|
|
138
|
+
# --- Pass 1: Vertical Walls (on grid_v) ---
|
|
139
|
+
for _ in range(lines_per_pass):
|
|
140
|
+
length = rng(WALL_MIN_LEN, WALL_MAX_LEN)
|
|
141
|
+
x = rng(2, cols - 3)
|
|
142
|
+
y = rng(2, rows - 2 - length)
|
|
143
|
+
|
|
144
|
+
can_place = True
|
|
145
|
+
for i in range(length):
|
|
146
|
+
cy = y + i
|
|
147
|
+
# 1. Global forbidden check (exits, outer walls in main grid)
|
|
148
|
+
if (x, cy) in forbidden:
|
|
149
|
+
can_place = False
|
|
150
|
+
break
|
|
151
|
+
if grid[cy][x] not in (".",):
|
|
152
|
+
can_place = False
|
|
153
|
+
break
|
|
154
|
+
# 2. Local self-overlap check
|
|
155
|
+
if grid_v[cy][x] != ".":
|
|
156
|
+
can_place = False
|
|
157
|
+
break
|
|
158
|
+
# 3. Parallel adjacency check (only against other vertical walls)
|
|
159
|
+
if grid_v[cy][x - 1] == "1" or grid_v[cy][x + 1] == "1":
|
|
160
|
+
can_place = False
|
|
161
|
+
break
|
|
162
|
+
|
|
163
|
+
if can_place:
|
|
164
|
+
for i in range(length):
|
|
165
|
+
grid_v[y + i][x] = "1"
|
|
166
|
+
|
|
167
|
+
# --- Pass 2: Horizontal Walls (on grid_h) ---
|
|
168
|
+
for _ in range(lines_per_pass):
|
|
169
|
+
length = rng(WALL_MIN_LEN, WALL_MAX_LEN)
|
|
170
|
+
x = rng(2, cols - 2 - length)
|
|
171
|
+
y = rng(2, rows - 3)
|
|
172
|
+
|
|
173
|
+
can_place = True
|
|
174
|
+
for i in range(length):
|
|
175
|
+
cx = x + i
|
|
176
|
+
# 1. Global forbidden check
|
|
177
|
+
if (cx, y) in forbidden:
|
|
178
|
+
can_place = False
|
|
179
|
+
break
|
|
180
|
+
if grid[y][cx] not in (".",):
|
|
181
|
+
can_place = False
|
|
182
|
+
break
|
|
183
|
+
# 2. Local self-overlap check
|
|
184
|
+
if grid_h[y][cx] != ".":
|
|
185
|
+
can_place = False
|
|
186
|
+
break
|
|
187
|
+
# 3. Parallel adjacency check (only against other horizontal walls)
|
|
188
|
+
if grid_h[y - 1][cx] == "1" or grid_h[y + 1][cx] == "1":
|
|
189
|
+
can_place = False
|
|
190
|
+
break
|
|
191
|
+
|
|
192
|
+
if can_place:
|
|
193
|
+
for i in range(length):
|
|
194
|
+
grid_h[y][x + i] = "1"
|
|
195
|
+
|
|
196
|
+
# --- Merge Phase ---
|
|
197
|
+
for y in range(rows):
|
|
198
|
+
for x in range(cols):
|
|
199
|
+
# If either layer has a wall, and the main grid is empty, place it.
|
|
200
|
+
if (grid_v[y][x] == "1" or grid_h[y][x] == "1") and grid[y][x] == ".":
|
|
201
|
+
grid[y][x] = "1"
|
|
202
|
+
|
|
203
|
+
|
|
204
|
+
WALL_ALGORITHMS = {
|
|
205
|
+
"default": _place_walls_default,
|
|
206
|
+
"empty": _place_walls_empty,
|
|
207
|
+
"grid_wire": _place_walls_grid_wire,
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
|
|
114
211
|
def _place_steel_beams(
|
|
115
212
|
grid: list[list[str]], *, chance: float = STEEL_BEAM_CHANCE
|
|
116
213
|
) -> set[tuple[int, int]]:
|
|
@@ -150,10 +247,22 @@ def _pick_empty_cell(
|
|
|
150
247
|
return cols // 2, rows // 2
|
|
151
248
|
|
|
152
249
|
|
|
153
|
-
def
|
|
154
|
-
|
|
250
|
+
def _generate_random_blueprint(
|
|
251
|
+
steel_chance: float, *, cols: int, rows: int, wall_algo: str = "default"
|
|
252
|
+
) -> dict:
|
|
253
|
+
grid = _init_grid(cols, rows)
|
|
155
254
|
_place_exits(grid, EXITS_PER_SIDE)
|
|
156
|
-
|
|
255
|
+
|
|
256
|
+
# Select and run the wall placement algorithm
|
|
257
|
+
if wall_algo not in WALL_ALGORITHMS:
|
|
258
|
+
print(
|
|
259
|
+
f"WARNING: Unknown wall algorithm '{wall_algo}'. Falling back to 'default'."
|
|
260
|
+
)
|
|
261
|
+
wall_algo = "default"
|
|
262
|
+
|
|
263
|
+
algo_func = WALL_ALGORITHMS[wall_algo]
|
|
264
|
+
algo_func(grid)
|
|
265
|
+
|
|
157
266
|
steel_beams = _place_steel_beams(grid, chance=steel_chance)
|
|
158
267
|
|
|
159
268
|
# Spawns: player, car, zombies
|
|
@@ -169,11 +278,15 @@ def generate_random_blueprint(steel_chance: float) -> dict:
|
|
|
169
278
|
return {"grid": blueprint_rows, "steel_cells": steel_beams}
|
|
170
279
|
|
|
171
280
|
|
|
172
|
-
def choose_blueprint(
|
|
281
|
+
def choose_blueprint(
|
|
282
|
+
config: dict, *, cols: int, rows: int, wall_algo: str = "default"
|
|
283
|
+
) -> dict:
|
|
173
284
|
# Currently only random generation; hook for future variants.
|
|
174
285
|
steel_conf = config.get("steel_beams", {})
|
|
175
286
|
try:
|
|
176
287
|
steel_chance = float(steel_conf.get("chance", STEEL_BEAM_CHANCE))
|
|
177
288
|
except (TypeError, ValueError):
|
|
178
289
|
steel_chance = STEEL_BEAM_CHANCE
|
|
179
|
-
return
|
|
290
|
+
return _generate_random_blueprint(
|
|
291
|
+
steel_chance=steel_chance, cols=cols, rows=rows, wall_algo=wall_algo
|
|
292
|
+
)
|
zombie_escape/level_constants.py
CHANGED
|
@@ -2,19 +2,12 @@
|
|
|
2
2
|
|
|
3
3
|
from __future__ import annotations
|
|
4
4
|
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
CELL_SIZE = TILE_SIZE
|
|
10
|
-
LEVEL_WIDTH = GRID_COLS * CELL_SIZE
|
|
11
|
-
LEVEL_HEIGHT = GRID_ROWS * CELL_SIZE
|
|
5
|
+
DEFAULT_GRID_COLS = 48
|
|
6
|
+
DEFAULT_GRID_ROWS = 30
|
|
7
|
+
DEFAULT_TILE_SIZE = 50 # world units per cell; adjust to scale the whole map
|
|
12
8
|
|
|
13
9
|
__all__ = [
|
|
14
|
-
"
|
|
15
|
-
"
|
|
16
|
-
"
|
|
17
|
-
"CELL_SIZE",
|
|
18
|
-
"LEVEL_WIDTH",
|
|
19
|
-
"LEVEL_HEIGHT",
|
|
10
|
+
"DEFAULT_GRID_COLS",
|
|
11
|
+
"DEFAULT_GRID_ROWS",
|
|
12
|
+
"DEFAULT_TILE_SIZE",
|
|
20
13
|
]
|
zombie_escape/locales/ui.en.json
CHANGED
|
@@ -9,7 +9,7 @@
|
|
|
9
9
|
"fonts": {
|
|
10
10
|
"primary": {
|
|
11
11
|
"resource": "assets/fonts/Silkscreen-Regular.ttf",
|
|
12
|
-
"scale": 0.
|
|
12
|
+
"scale": 0.75
|
|
13
13
|
}
|
|
14
14
|
},
|
|
15
15
|
"menu": {
|
|
@@ -69,6 +69,14 @@
|
|
|
69
69
|
"stage7": {
|
|
70
70
|
"name": "#7 Rescue Buddy 2",
|
|
71
71
|
"description": "Rescue your buddy. Wall-hugging zombies appear."
|
|
72
|
+
},
|
|
73
|
+
"stage8": {
|
|
74
|
+
"name": "#8 Assembly Floor",
|
|
75
|
+
"description": "Narrow corridors. Get surrounded, and there’s no escape."
|
|
76
|
+
},
|
|
77
|
+
"stage9": {
|
|
78
|
+
"name": "# Evacuate Survivors 2",
|
|
79
|
+
"description": "Evacuate like stage 4, but narrow corridors. Requires fuel."
|
|
72
80
|
}
|
|
73
81
|
},
|
|
74
82
|
"status": {
|
|
@@ -100,6 +108,7 @@
|
|
|
100
108
|
"pickup_buddy": "Pick up your buddy",
|
|
101
109
|
"find_buddy": "Find your buddy",
|
|
102
110
|
"escape_with_buddy": "Get your buddy in the car and escape",
|
|
111
|
+
"board_buddy": "Get your buddy in the car",
|
|
103
112
|
"buddy_onboard": "Buddy onboard: %{count}",
|
|
104
113
|
"escape_with_survivors": "Escape with the survivors aboard",
|
|
105
114
|
"survivors_onboard": "Survivors aboard: %{count}/%{limit}",
|
zombie_escape/locales/ui.ja.json
CHANGED
|
@@ -67,8 +67,16 @@
|
|
|
67
67
|
"description": "ステージ2と同じだが、一部のゾンビが足跡を追う。"
|
|
68
68
|
},
|
|
69
69
|
"stage7": {
|
|
70
|
-
"name": "#7 相棒を救え2",
|
|
70
|
+
"name": "#7 相棒を救え 2",
|
|
71
71
|
"description": "はぐれた相棒を救え。壁沿いゾンビが出現。"
|
|
72
|
+
},
|
|
73
|
+
"stage8": {
|
|
74
|
+
"name": "#8 組立工程",
|
|
75
|
+
"description": "狭い通路。挟まれると逃げ場なし。"
|
|
76
|
+
},
|
|
77
|
+
"stage9": {
|
|
78
|
+
"name": "#9 生存者救出 2",
|
|
79
|
+
"description": "ステージ4と同じだが、狭い通路。燃料も必要。"
|
|
72
80
|
}
|
|
73
81
|
},
|
|
74
82
|
"status": {
|
|
@@ -100,6 +108,7 @@
|
|
|
100
108
|
"pickup_buddy": "相棒を救出する",
|
|
101
109
|
"find_buddy": "相棒を探す",
|
|
102
110
|
"escape_with_buddy": "相棒を車に乗せて脱出する",
|
|
111
|
+
"board_buddy": "相棒を車に乗せる",
|
|
103
112
|
"buddy_onboard": "相棒乗車中: %{count}",
|
|
104
113
|
"escape_with_survivors": "生存者を車に乗せて脱出する",
|
|
105
114
|
"survivors_onboard": "乗車中: %{count}/%{limit}",
|
zombie_escape/models.py
CHANGED
|
@@ -8,11 +8,9 @@ from typing import TYPE_CHECKING
|
|
|
8
8
|
import pygame
|
|
9
9
|
from pygame import sprite, surface
|
|
10
10
|
|
|
11
|
-
from .
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
ZOMBIE_SPAWN_DELAY_MS,
|
|
15
|
-
)
|
|
11
|
+
from .entities_constants import ZOMBIE_AGING_DURATION_FRAMES
|
|
12
|
+
from .gameplay_constants import SURVIVOR_SPAWN_RATE, ZOMBIE_SPAWN_DELAY_MS
|
|
13
|
+
from .level_constants import DEFAULT_GRID_COLS, DEFAULT_GRID_ROWS
|
|
16
14
|
from .localization import translate as tr
|
|
17
15
|
|
|
18
16
|
if TYPE_CHECKING: # pragma: no cover - typing-only imports
|
|
@@ -20,8 +18,8 @@ if TYPE_CHECKING: # pragma: no cover - typing-only imports
|
|
|
20
18
|
|
|
21
19
|
|
|
22
20
|
@dataclass
|
|
23
|
-
class
|
|
24
|
-
"""Container for level
|
|
21
|
+
class LevelLayout:
|
|
22
|
+
"""Container for level layout rectangles and cell sets."""
|
|
25
23
|
|
|
26
24
|
outer_rect: tuple[int, int, int, int]
|
|
27
25
|
inner_rect: tuple[int, int, int, int]
|
|
@@ -83,9 +81,12 @@ class GameData:
|
|
|
83
81
|
state: ProgressState
|
|
84
82
|
groups: Groups
|
|
85
83
|
camera: Camera
|
|
86
|
-
|
|
84
|
+
layout: LevelLayout
|
|
87
85
|
fog: dict
|
|
88
86
|
stage: Stage
|
|
87
|
+
cell_size: int
|
|
88
|
+
level_width: int
|
|
89
|
+
level_height: int
|
|
89
90
|
fuel: FuelCan | None = None
|
|
90
91
|
flashlights: list[Flashlight] | None = None
|
|
91
92
|
player: Player | None = None
|
|
@@ -100,6 +101,9 @@ class Stage:
|
|
|
100
101
|
name_key: str
|
|
101
102
|
description_key: str
|
|
102
103
|
available: bool = True
|
|
104
|
+
tile_size: int = 50
|
|
105
|
+
grid_cols: int = DEFAULT_GRID_COLS
|
|
106
|
+
grid_rows: int = DEFAULT_GRID_ROWS
|
|
103
107
|
requires_fuel: bool = False
|
|
104
108
|
buddy_required_count: int = 0
|
|
105
109
|
rescue_stage: bool = False
|
|
@@ -115,6 +119,8 @@ class Stage:
|
|
|
115
119
|
zombie_wall_follower_ratio: float = 0.0
|
|
116
120
|
zombie_normal_ratio: float = 1.0
|
|
117
121
|
zombie_aging_duration_frames: int = ZOMBIE_AGING_DURATION_FRAMES
|
|
122
|
+
waiting_car_target_count: int = 1
|
|
123
|
+
wall_algorithm: str = "default"
|
|
118
124
|
|
|
119
125
|
@property
|
|
120
126
|
def name(self) -> str:
|
|
@@ -126,7 +132,7 @@ class Stage:
|
|
|
126
132
|
|
|
127
133
|
|
|
128
134
|
__all__ = [
|
|
129
|
-
"
|
|
135
|
+
"LevelLayout",
|
|
130
136
|
"ProgressState",
|
|
131
137
|
"Groups",
|
|
132
138
|
"GameData",
|