zombie-escape 1.10.1__py3-none-any.whl → 1.12.3__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/colors.py +22 -14
- zombie_escape/entities.py +247 -47
- zombie_escape/entities_constants.py +15 -5
- zombie_escape/gameplay/__init__.py +2 -0
- zombie_escape/gameplay/footprints.py +4 -0
- zombie_escape/gameplay/interactions.py +38 -7
- zombie_escape/gameplay/layout.py +40 -15
- zombie_escape/gameplay/movement.py +38 -2
- zombie_escape/gameplay/spawn.py +174 -41
- zombie_escape/gameplay/state.py +17 -9
- zombie_escape/gameplay/survivors.py +9 -1
- zombie_escape/gameplay/utils.py +40 -21
- zombie_escape/gameplay_constants.py +8 -0
- zombie_escape/level_blueprints.py +139 -24
- zombie_escape/locales/ui.en.json +9 -1
- zombie_escape/locales/ui.ja.json +8 -0
- zombie_escape/models.py +11 -5
- zombie_escape/render.py +390 -43
- zombie_escape/render_assets.py +427 -174
- zombie_escape/render_constants.py +25 -4
- zombie_escape/screens/game_over.py +4 -4
- zombie_escape/screens/gameplay.py +31 -1
- zombie_escape/stage_constants.py +33 -16
- zombie_escape/zombie_escape.py +1 -1
- {zombie_escape-1.10.1.dist-info → zombie_escape-1.12.3.dist-info}/METADATA +7 -4
- zombie_escape-1.12.3.dist-info/RECORD +47 -0
- zombie_escape-1.10.1.dist-info/RECORD +0 -47
- {zombie_escape-1.10.1.dist-info → zombie_escape-1.12.3.dist-info}/WHEEL +0 -0
- {zombie_escape-1.10.1.dist-info → zombie_escape-1.12.3.dist-info}/entry_points.txt +0 -0
- {zombie_escape-1.10.1.dist-info → zombie_escape-1.12.3.dist-info}/licenses/LICENSE.txt +0 -0
|
@@ -14,6 +14,7 @@ from .spawn import (
|
|
|
14
14
|
place_flashlights,
|
|
15
15
|
place_fuel_can,
|
|
16
16
|
place_new_car,
|
|
17
|
+
place_shoes,
|
|
17
18
|
setup_player_and_cars,
|
|
18
19
|
spawn_exterior_zombie,
|
|
19
20
|
spawn_initial_zombies,
|
|
@@ -46,6 +47,7 @@ __all__ = [
|
|
|
46
47
|
"place_new_car",
|
|
47
48
|
"place_fuel_can",
|
|
48
49
|
"place_flashlights",
|
|
50
|
+
"place_shoes",
|
|
49
51
|
"place_buddies",
|
|
50
52
|
"find_interior_spawn_positions",
|
|
51
53
|
"find_nearby_offscreen_spawn_position",
|
|
@@ -26,6 +26,10 @@ def get_shrunk_sprite(
|
|
|
26
26
|
|
|
27
27
|
new_sprite = pygame.sprite.Sprite()
|
|
28
28
|
new_sprite.rect = rect
|
|
29
|
+
if hasattr(sprite_obj, "radius"):
|
|
30
|
+
base_radius = getattr(sprite_obj, "radius", None)
|
|
31
|
+
if base_radius is not None:
|
|
32
|
+
new_sprite.radius = base_radius * min(scale_x, scale_y)
|
|
29
33
|
|
|
30
34
|
return new_sprite
|
|
31
35
|
|
|
@@ -12,6 +12,8 @@ from ..entities_constants import (
|
|
|
12
12
|
FUEL_CAN_HEIGHT,
|
|
13
13
|
FUEL_CAN_WIDTH,
|
|
14
14
|
HUMANOID_RADIUS,
|
|
15
|
+
SHOES_HEIGHT,
|
|
16
|
+
SHOES_WIDTH,
|
|
15
17
|
SURVIVOR_APPROACH_RADIUS,
|
|
16
18
|
SURVIVOR_MAX_SAFE_PASSENGERS,
|
|
17
19
|
)
|
|
@@ -55,11 +57,13 @@ def check_interactions(game_data: GameData, config: dict[str, Any]) -> None:
|
|
|
55
57
|
survivor_group = game_data.groups.survivor_group
|
|
56
58
|
state = game_data.state
|
|
57
59
|
walkable_cells = game_data.layout.walkable_cells
|
|
58
|
-
|
|
60
|
+
outside_cells = game_data.layout.outside_cells
|
|
59
61
|
fuel = game_data.fuel
|
|
60
62
|
flashlights = game_data.flashlights or []
|
|
63
|
+
shoes_list = game_data.shoes or []
|
|
61
64
|
camera = game_data.camera
|
|
62
65
|
stage = game_data.stage
|
|
66
|
+
cell_size = game_data.cell_size
|
|
63
67
|
maintain_waiting_car_supply(game_data)
|
|
64
68
|
active_car = car if car and car.alive() else None
|
|
65
69
|
waiting_cars = game_data.waiting_cars
|
|
@@ -70,6 +74,18 @@ def check_interactions(game_data: GameData, config: dict[str, Any]) -> None:
|
|
|
70
74
|
flashlight_interaction_radius = _interaction_radius(
|
|
71
75
|
FLASHLIGHT_WIDTH, FLASHLIGHT_HEIGHT
|
|
72
76
|
)
|
|
77
|
+
shoes_interaction_radius = _interaction_radius(SHOES_WIDTH, SHOES_HEIGHT)
|
|
78
|
+
|
|
79
|
+
def _rect_center_cell(rect: pygame.Rect) -> tuple[int, int] | None:
|
|
80
|
+
if cell_size <= 0:
|
|
81
|
+
return None
|
|
82
|
+
return (int(rect.centerx // cell_size), int(rect.centery // cell_size))
|
|
83
|
+
|
|
84
|
+
def _cell_center(cell: tuple[int, int]) -> tuple[int, int]:
|
|
85
|
+
return (
|
|
86
|
+
int((cell[0] * cell_size) + (cell_size / 2)),
|
|
87
|
+
int((cell[1] * cell_size) + (cell_size / 2)),
|
|
88
|
+
)
|
|
73
89
|
|
|
74
90
|
def _player_near_point(point: tuple[float, float], radius: float) -> bool:
|
|
75
91
|
dx = point[0] - player.x
|
|
@@ -118,6 +134,21 @@ def check_interactions(game_data: GameData, config: dict[str, Any]) -> None:
|
|
|
118
134
|
print("Flashlight acquired!")
|
|
119
135
|
break
|
|
120
136
|
|
|
137
|
+
for shoes in list(shoes_list):
|
|
138
|
+
if not shoes.alive():
|
|
139
|
+
continue
|
|
140
|
+
if _player_near_point(shoes.rect.center, shoes_interaction_radius):
|
|
141
|
+
state.shoes_count += 1
|
|
142
|
+
state.hint_expires_at = 0
|
|
143
|
+
state.hint_target_type = None
|
|
144
|
+
shoes.kill()
|
|
145
|
+
try:
|
|
146
|
+
shoes_list.remove(shoes)
|
|
147
|
+
except ValueError:
|
|
148
|
+
pass
|
|
149
|
+
print("Shoes acquired!")
|
|
150
|
+
break
|
|
151
|
+
|
|
121
152
|
sync_ambient_palette_with_flashlights(game_data)
|
|
122
153
|
|
|
123
154
|
buddies = [
|
|
@@ -170,7 +201,7 @@ def check_interactions(game_data: GameData, config: dict[str, Any]) -> None:
|
|
|
170
201
|
else:
|
|
171
202
|
if walkable_cells:
|
|
172
203
|
new_cell = RNG.choice(walkable_cells)
|
|
173
|
-
buddy.teleport(new_cell
|
|
204
|
+
buddy.teleport(_cell_center(new_cell))
|
|
174
205
|
else:
|
|
175
206
|
buddy.teleport(
|
|
176
207
|
(game_data.level_width // 2, game_data.level_height // 2)
|
|
@@ -322,8 +353,9 @@ def check_interactions(game_data: GameData, config: dict[str, Any]) -> None:
|
|
|
322
353
|
stage.endurance_stage
|
|
323
354
|
and state.dawn_ready
|
|
324
355
|
and not player.in_car
|
|
325
|
-
and
|
|
326
|
-
and
|
|
356
|
+
and outside_cells
|
|
357
|
+
and (player_cell := _rect_center_cell(player.rect)) is not None
|
|
358
|
+
and player_cell in outside_cells
|
|
327
359
|
):
|
|
328
360
|
state.game_won = True
|
|
329
361
|
|
|
@@ -332,9 +364,8 @@ def check_interactions(game_data: GameData, config: dict[str, Any]) -> None:
|
|
|
332
364
|
buddy_ready = True
|
|
333
365
|
if stage.buddy_required_count > 0:
|
|
334
366
|
buddy_ready = state.buddy_onboard >= stage.buddy_required_count
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
):
|
|
367
|
+
car_cell = _rect_center_cell(car.rect)
|
|
368
|
+
if buddy_ready and car_cell is not None and car_cell in outside_cells:
|
|
338
369
|
if stage.buddy_required_count > 0:
|
|
339
370
|
state.buddy_rescued = min(
|
|
340
371
|
stage.buddy_required_count, state.buddy_onboard
|
zombie_escape/gameplay/layout.py
CHANGED
|
@@ -10,9 +10,12 @@ from ..entities_constants import INTERNAL_WALL_HEALTH, STEEL_BEAM_HEALTH
|
|
|
10
10
|
from .constants import OUTER_WALL_HEALTH
|
|
11
11
|
from ..level_blueprints import choose_blueprint
|
|
12
12
|
from ..models import GameData
|
|
13
|
+
from ..rng import get_rng
|
|
13
14
|
|
|
14
15
|
__all__ = ["generate_level_from_blueprint"]
|
|
15
16
|
|
|
17
|
+
RNG = get_rng()
|
|
18
|
+
|
|
16
19
|
|
|
17
20
|
def _rect_for_cell(x_idx: int, y_idx: int, cell_size: int) -> pygame.Rect:
|
|
18
21
|
return pygame.Rect(
|
|
@@ -89,11 +92,15 @@ def generate_level_from_blueprint(
|
|
|
89
92
|
return True
|
|
90
93
|
return (nx, ny) in wall_cells
|
|
91
94
|
|
|
92
|
-
|
|
93
|
-
walkable_cells: list[
|
|
94
|
-
player_cells: list[
|
|
95
|
-
car_cells: list[
|
|
96
|
-
zombie_cells: list[
|
|
95
|
+
outside_cells: set[tuple[int, int]] = set()
|
|
96
|
+
walkable_cells: list[tuple[int, int]] = []
|
|
97
|
+
player_cells: list[tuple[int, int]] = []
|
|
98
|
+
car_cells: list[tuple[int, int]] = []
|
|
99
|
+
zombie_cells: list[tuple[int, int]] = []
|
|
100
|
+
interior_min_x = 2
|
|
101
|
+
interior_max_x = stage.grid_cols - 3
|
|
102
|
+
interior_min_y = 2
|
|
103
|
+
interior_max_y = stage.grid_rows - 3
|
|
97
104
|
bevel_corners: dict[tuple[int, int], tuple[bool, bool, bool, bool]] = {}
|
|
98
105
|
palette = get_environment_palette(game_data.state.ambient_palette_key)
|
|
99
106
|
|
|
@@ -118,7 +125,7 @@ def generate_level_from_blueprint(
|
|
|
118
125
|
cell_rect = _rect_for_cell(x, y, cell_size)
|
|
119
126
|
cell_has_beam = steel_enabled and (x, y) in steel_cells
|
|
120
127
|
if ch == "O":
|
|
121
|
-
|
|
128
|
+
outside_cells.add((x, y))
|
|
122
129
|
continue
|
|
123
130
|
if ch == "B":
|
|
124
131
|
draw_bottom_side = not _has_wall(x, y + 1)
|
|
@@ -140,7 +147,7 @@ def generate_level_from_blueprint(
|
|
|
140
147
|
continue
|
|
141
148
|
if ch == "E":
|
|
142
149
|
if not cell_has_beam:
|
|
143
|
-
walkable_cells.append(
|
|
150
|
+
walkable_cells.append((x, y))
|
|
144
151
|
elif ch == "1":
|
|
145
152
|
beam = None
|
|
146
153
|
if cell_has_beam:
|
|
@@ -189,14 +196,14 @@ def generate_level_from_blueprint(
|
|
|
189
196
|
all_sprites.add(wall, layer=0)
|
|
190
197
|
else:
|
|
191
198
|
if not cell_has_beam:
|
|
192
|
-
walkable_cells.append(
|
|
199
|
+
walkable_cells.append((x, y))
|
|
193
200
|
|
|
194
201
|
if ch == "P":
|
|
195
|
-
player_cells.append(
|
|
202
|
+
player_cells.append((x, y))
|
|
196
203
|
if ch == "C":
|
|
197
|
-
car_cells.append(
|
|
204
|
+
car_cells.append((x, y))
|
|
198
205
|
if ch == "Z":
|
|
199
|
-
zombie_cells.append(
|
|
206
|
+
zombie_cells.append((x, y))
|
|
200
207
|
|
|
201
208
|
if cell_has_beam and ch != "1":
|
|
202
209
|
beam = SteelBeam(
|
|
@@ -208,17 +215,35 @@ def generate_level_from_blueprint(
|
|
|
208
215
|
)
|
|
209
216
|
add_beam_to_groups(beam)
|
|
210
217
|
|
|
211
|
-
game_data.layout.
|
|
212
|
-
|
|
213
|
-
|
|
218
|
+
game_data.layout.field_rect = pygame.Rect(
|
|
219
|
+
0,
|
|
220
|
+
0,
|
|
221
|
+
game_data.level_width,
|
|
222
|
+
game_data.level_height,
|
|
223
|
+
)
|
|
224
|
+
game_data.layout.outside_cells = outside_cells
|
|
214
225
|
game_data.layout.walkable_cells = walkable_cells
|
|
215
226
|
game_data.layout.outer_wall_cells = outer_wall_cells
|
|
216
227
|
game_data.layout.wall_cells = wall_cells
|
|
217
|
-
|
|
228
|
+
fall_spawn_cells = _expand_zone_cells(
|
|
218
229
|
stage.fall_spawn_zones,
|
|
219
230
|
grid_cols=stage.grid_cols,
|
|
220
231
|
grid_rows=stage.grid_rows,
|
|
221
232
|
)
|
|
233
|
+
floor_ratio = max(0.0, min(1.0, stage.fall_spawn_floor_ratio))
|
|
234
|
+
if floor_ratio > 0.0 and interior_min_x <= interior_max_x:
|
|
235
|
+
for y in range(interior_min_y, interior_max_y + 1):
|
|
236
|
+
for x in range(interior_min_x, interior_max_x + 1):
|
|
237
|
+
if RNG.random() < floor_ratio:
|
|
238
|
+
fall_spawn_cells.add((x, y))
|
|
239
|
+
if not fall_spawn_cells:
|
|
240
|
+
fall_spawn_cells.add(
|
|
241
|
+
(
|
|
242
|
+
RNG.randint(interior_min_x, interior_max_x),
|
|
243
|
+
RNG.randint(interior_min_y, interior_max_y),
|
|
244
|
+
)
|
|
245
|
+
)
|
|
246
|
+
game_data.layout.fall_spawn_cells = fall_spawn_cells
|
|
222
247
|
game_data.layout.bevel_corners = bevel_corners
|
|
223
248
|
|
|
224
249
|
return {
|
|
@@ -13,10 +13,15 @@ from ..entities import (
|
|
|
13
13
|
Zombie,
|
|
14
14
|
)
|
|
15
15
|
from ..entities_constants import (
|
|
16
|
+
HUMANOID_WALL_BUMP_FRAMES,
|
|
16
17
|
PLAYER_SPEED,
|
|
17
18
|
ZOMBIE_SEPARATION_DISTANCE,
|
|
18
19
|
ZOMBIE_WALL_FOLLOW_SENSOR_DISTANCE,
|
|
19
20
|
)
|
|
21
|
+
from ..gameplay_constants import (
|
|
22
|
+
SHOES_SPEED_MULTIPLIER_ONE,
|
|
23
|
+
SHOES_SPEED_MULTIPLIER_TWO,
|
|
24
|
+
)
|
|
20
25
|
from ..models import GameData
|
|
21
26
|
from ..world_grid import WallIndex, apply_tile_edge_nudge, walls_for_radius
|
|
22
27
|
from .constants import MAX_ZOMBIES
|
|
@@ -29,6 +34,7 @@ def process_player_input(
|
|
|
29
34
|
keys: Sequence[bool],
|
|
30
35
|
player: Player,
|
|
31
36
|
car: Car | None,
|
|
37
|
+
shoes_count: int = 0,
|
|
32
38
|
pad_input: tuple[float, float] = (0.0, 0.0),
|
|
33
39
|
) -> tuple[float, float, float, float]:
|
|
34
40
|
"""Process keyboard input and return movement deltas."""
|
|
@@ -44,9 +50,12 @@ def process_player_input(
|
|
|
44
50
|
dx_input += pad_input[0]
|
|
45
51
|
dy_input += pad_input[1]
|
|
46
52
|
|
|
53
|
+
player.update_facing_from_input(dx_input, dy_input)
|
|
54
|
+
|
|
47
55
|
player_dx, player_dy, car_dx, car_dy = 0, 0, 0, 0
|
|
48
56
|
|
|
49
57
|
if player.in_car and car and car.alive():
|
|
58
|
+
car.update_facing_from_input(dx_input, dy_input)
|
|
50
59
|
target_speed = car.speed
|
|
51
60
|
move_len = math.hypot(dx_input, dy_input)
|
|
52
61
|
if move_len > 0:
|
|
@@ -55,7 +64,7 @@ def process_player_input(
|
|
|
55
64
|
(dy_input / move_len) * target_speed,
|
|
56
65
|
)
|
|
57
66
|
elif not player.in_car:
|
|
58
|
-
target_speed = PLAYER_SPEED
|
|
67
|
+
target_speed = PLAYER_SPEED * _shoes_speed_multiplier(shoes_count)
|
|
59
68
|
move_len = math.hypot(dx_input, dy_input)
|
|
60
69
|
if move_len > 0:
|
|
61
70
|
player_dx, player_dy = (
|
|
@@ -66,6 +75,15 @@ def process_player_input(
|
|
|
66
75
|
return player_dx, player_dy, car_dx, car_dy
|
|
67
76
|
|
|
68
77
|
|
|
78
|
+
def _shoes_speed_multiplier(shoes_count: int) -> float:
|
|
79
|
+
count = max(0, int(shoes_count))
|
|
80
|
+
if count >= 2:
|
|
81
|
+
return SHOES_SPEED_MULTIPLIER_TWO
|
|
82
|
+
if count == 1:
|
|
83
|
+
return SHOES_SPEED_MULTIPLIER_ONE
|
|
84
|
+
return 1.0
|
|
85
|
+
|
|
86
|
+
|
|
69
87
|
def update_entities(
|
|
70
88
|
game_data: GameData,
|
|
71
89
|
player_dx: float,
|
|
@@ -152,7 +170,25 @@ def update_entities(
|
|
|
152
170
|
target_for_camera = active_car if player.in_car and active_car else player
|
|
153
171
|
camera.update(target_for_camera)
|
|
154
172
|
|
|
155
|
-
|
|
173
|
+
if player.inner_wall_hit and player.inner_wall_cell is not None:
|
|
174
|
+
game_data.state.player_wall_target_cell = player.inner_wall_cell
|
|
175
|
+
game_data.state.player_wall_target_ttl = HUMANOID_WALL_BUMP_FRAMES
|
|
176
|
+
elif game_data.state.player_wall_target_ttl > 0:
|
|
177
|
+
game_data.state.player_wall_target_ttl -= 1
|
|
178
|
+
if game_data.state.player_wall_target_ttl <= 0:
|
|
179
|
+
game_data.state.player_wall_target_cell = None
|
|
180
|
+
|
|
181
|
+
wall_target_cell = (
|
|
182
|
+
game_data.state.player_wall_target_cell
|
|
183
|
+
if game_data.state.player_wall_target_ttl > 0
|
|
184
|
+
else None
|
|
185
|
+
)
|
|
186
|
+
|
|
187
|
+
update_survivors(
|
|
188
|
+
game_data,
|
|
189
|
+
wall_index=wall_index,
|
|
190
|
+
wall_target_cell=wall_target_cell,
|
|
191
|
+
)
|
|
156
192
|
update_falling_zombies(game_data, config)
|
|
157
193
|
|
|
158
194
|
# Spawn new zombies if needed
|