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
|
@@ -0,0 +1,190 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from typing import Any
|
|
4
|
+
|
|
5
|
+
import pygame
|
|
6
|
+
|
|
7
|
+
from ..colors import get_environment_palette
|
|
8
|
+
from ..entities import SteelBeam, Wall
|
|
9
|
+
from ..entities_constants import INTERNAL_WALL_HEALTH, STEEL_BEAM_HEALTH
|
|
10
|
+
from .constants import OUTER_WALL_HEALTH
|
|
11
|
+
from ..level_blueprints import choose_blueprint
|
|
12
|
+
from ..models import GameData
|
|
13
|
+
|
|
14
|
+
__all__ = ["generate_level_from_blueprint"]
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
def _rect_for_cell(x_idx: int, y_idx: int, cell_size: int) -> pygame.Rect:
|
|
18
|
+
return pygame.Rect(
|
|
19
|
+
x_idx * cell_size,
|
|
20
|
+
y_idx * cell_size,
|
|
21
|
+
cell_size,
|
|
22
|
+
cell_size,
|
|
23
|
+
)
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
def generate_level_from_blueprint(
|
|
27
|
+
game_data: GameData, config: dict[str, Any]
|
|
28
|
+
) -> dict[str, list[pygame.Rect]]:
|
|
29
|
+
"""Build walls/spawn candidates/outside area from a blueprint grid."""
|
|
30
|
+
wall_group = game_data.groups.wall_group
|
|
31
|
+
all_sprites = game_data.groups.all_sprites
|
|
32
|
+
stage = game_data.stage
|
|
33
|
+
|
|
34
|
+
steel_conf = config.get("steel_beams", {})
|
|
35
|
+
steel_enabled = steel_conf.get("enabled", False)
|
|
36
|
+
|
|
37
|
+
blueprint_data = choose_blueprint(
|
|
38
|
+
config,
|
|
39
|
+
cols=stage.grid_cols,
|
|
40
|
+
rows=stage.grid_rows,
|
|
41
|
+
wall_algo=stage.wall_algorithm,
|
|
42
|
+
)
|
|
43
|
+
if isinstance(blueprint_data, dict):
|
|
44
|
+
blueprint = blueprint_data.get("grid", [])
|
|
45
|
+
steel_cells_raw = blueprint_data.get("steel_cells", set())
|
|
46
|
+
else:
|
|
47
|
+
blueprint = blueprint_data
|
|
48
|
+
steel_cells_raw = set()
|
|
49
|
+
|
|
50
|
+
steel_cells = (
|
|
51
|
+
{(int(x), int(y)) for x, y in steel_cells_raw} if steel_enabled else set()
|
|
52
|
+
)
|
|
53
|
+
cell_size = game_data.cell_size
|
|
54
|
+
outer_wall_cells = {
|
|
55
|
+
(x, y)
|
|
56
|
+
for y, row in enumerate(blueprint)
|
|
57
|
+
for x, ch in enumerate(row)
|
|
58
|
+
if ch == "B"
|
|
59
|
+
}
|
|
60
|
+
wall_cells = {
|
|
61
|
+
(x, y)
|
|
62
|
+
for y, row in enumerate(blueprint)
|
|
63
|
+
for x, ch in enumerate(row)
|
|
64
|
+
if ch in {"B", "1"}
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
def _has_wall(nx: int, ny: int) -> bool:
|
|
68
|
+
if nx < 0 or ny < 0 or nx >= stage.grid_cols or ny >= stage.grid_rows:
|
|
69
|
+
return True
|
|
70
|
+
return (nx, ny) in wall_cells
|
|
71
|
+
|
|
72
|
+
outside_rects: list[pygame.Rect] = []
|
|
73
|
+
walkable_cells: list[pygame.Rect] = []
|
|
74
|
+
player_cells: list[pygame.Rect] = []
|
|
75
|
+
car_cells: list[pygame.Rect] = []
|
|
76
|
+
zombie_cells: list[pygame.Rect] = []
|
|
77
|
+
palette = get_environment_palette(game_data.state.ambient_palette_key)
|
|
78
|
+
|
|
79
|
+
def add_beam_to_groups(beam: SteelBeam) -> None:
|
|
80
|
+
if getattr(beam, "_added_to_groups", False):
|
|
81
|
+
return
|
|
82
|
+
wall_group.add(beam)
|
|
83
|
+
all_sprites.add(beam, layer=0)
|
|
84
|
+
beam._added_to_groups = True
|
|
85
|
+
|
|
86
|
+
for y, row in enumerate(blueprint):
|
|
87
|
+
if len(row) != stage.grid_cols:
|
|
88
|
+
raise ValueError(
|
|
89
|
+
"Blueprint width mismatch at row "
|
|
90
|
+
f"{y}: {len(row)} != {stage.grid_cols}"
|
|
91
|
+
)
|
|
92
|
+
for x, ch in enumerate(row):
|
|
93
|
+
cell_rect = _rect_for_cell(x, y, cell_size)
|
|
94
|
+
cell_has_beam = steel_enabled and (x, y) in steel_cells
|
|
95
|
+
if ch == "O":
|
|
96
|
+
outside_rects.append(cell_rect)
|
|
97
|
+
continue
|
|
98
|
+
if ch == "B":
|
|
99
|
+
draw_bottom_side = not _has_wall(x, y + 1)
|
|
100
|
+
wall = Wall(
|
|
101
|
+
cell_rect.x,
|
|
102
|
+
cell_rect.y,
|
|
103
|
+
cell_rect.width,
|
|
104
|
+
cell_rect.height,
|
|
105
|
+
health=OUTER_WALL_HEALTH,
|
|
106
|
+
palette=palette,
|
|
107
|
+
palette_category="outer_wall",
|
|
108
|
+
bevel_depth=0,
|
|
109
|
+
draw_bottom_side=draw_bottom_side,
|
|
110
|
+
)
|
|
111
|
+
wall_group.add(wall)
|
|
112
|
+
all_sprites.add(wall, layer=0)
|
|
113
|
+
continue
|
|
114
|
+
if ch == "E":
|
|
115
|
+
if not cell_has_beam:
|
|
116
|
+
walkable_cells.append(cell_rect)
|
|
117
|
+
elif ch == "1":
|
|
118
|
+
beam = None
|
|
119
|
+
if cell_has_beam:
|
|
120
|
+
beam = SteelBeam(
|
|
121
|
+
cell_rect.x,
|
|
122
|
+
cell_rect.y,
|
|
123
|
+
cell_rect.width,
|
|
124
|
+
health=STEEL_BEAM_HEALTH,
|
|
125
|
+
palette=palette,
|
|
126
|
+
)
|
|
127
|
+
draw_bottom_side = not _has_wall(x, y + 1)
|
|
128
|
+
bevel_mask = (
|
|
129
|
+
not _has_wall(x, y - 1)
|
|
130
|
+
and not _has_wall(x - 1, y)
|
|
131
|
+
and not _has_wall(x - 1, y - 1),
|
|
132
|
+
not _has_wall(x, y - 1)
|
|
133
|
+
and not _has_wall(x + 1, y)
|
|
134
|
+
and not _has_wall(x + 1, y - 1),
|
|
135
|
+
not _has_wall(x, y + 1)
|
|
136
|
+
and not _has_wall(x + 1, y)
|
|
137
|
+
and not _has_wall(x + 1, y + 1),
|
|
138
|
+
not _has_wall(x, y + 1)
|
|
139
|
+
and not _has_wall(x - 1, y)
|
|
140
|
+
and not _has_wall(x - 1, y + 1),
|
|
141
|
+
)
|
|
142
|
+
wall = Wall(
|
|
143
|
+
cell_rect.x,
|
|
144
|
+
cell_rect.y,
|
|
145
|
+
cell_rect.width,
|
|
146
|
+
cell_rect.height,
|
|
147
|
+
health=INTERNAL_WALL_HEALTH,
|
|
148
|
+
palette=palette,
|
|
149
|
+
palette_category="inner_wall",
|
|
150
|
+
bevel_mask=bevel_mask,
|
|
151
|
+
draw_bottom_side=draw_bottom_side,
|
|
152
|
+
on_destroy=(lambda _w, b=beam: add_beam_to_groups(b))
|
|
153
|
+
if beam
|
|
154
|
+
else None,
|
|
155
|
+
)
|
|
156
|
+
wall_group.add(wall)
|
|
157
|
+
all_sprites.add(wall, layer=0)
|
|
158
|
+
else:
|
|
159
|
+
if not cell_has_beam:
|
|
160
|
+
walkable_cells.append(cell_rect)
|
|
161
|
+
|
|
162
|
+
if ch == "P":
|
|
163
|
+
player_cells.append(cell_rect)
|
|
164
|
+
if ch == "C":
|
|
165
|
+
car_cells.append(cell_rect)
|
|
166
|
+
if ch == "Z":
|
|
167
|
+
zombie_cells.append(cell_rect)
|
|
168
|
+
|
|
169
|
+
if cell_has_beam and ch != "1":
|
|
170
|
+
beam = SteelBeam(
|
|
171
|
+
cell_rect.x,
|
|
172
|
+
cell_rect.y,
|
|
173
|
+
cell_rect.width,
|
|
174
|
+
health=STEEL_BEAM_HEALTH,
|
|
175
|
+
palette=palette,
|
|
176
|
+
)
|
|
177
|
+
add_beam_to_groups(beam)
|
|
178
|
+
|
|
179
|
+
game_data.layout.outer_rect = (0, 0, game_data.level_width, game_data.level_height)
|
|
180
|
+
game_data.layout.inner_rect = (0, 0, game_data.level_width, game_data.level_height)
|
|
181
|
+
game_data.layout.outside_rects = outside_rects
|
|
182
|
+
game_data.layout.walkable_cells = walkable_cells
|
|
183
|
+
game_data.layout.outer_wall_cells = outer_wall_cells
|
|
184
|
+
|
|
185
|
+
return {
|
|
186
|
+
"player_cells": player_cells,
|
|
187
|
+
"car_cells": car_cells,
|
|
188
|
+
"zombie_cells": zombie_cells,
|
|
189
|
+
"walkable_cells": walkable_cells,
|
|
190
|
+
}
|
|
@@ -0,0 +1,220 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
import math
|
|
4
|
+
from typing import Any, Sequence
|
|
5
|
+
|
|
6
|
+
import pygame
|
|
7
|
+
|
|
8
|
+
from ..entities import Car, Player, Survivor, Wall, WallIndex, Zombie, walls_for_radius
|
|
9
|
+
from ..entities_constants import (
|
|
10
|
+
CAR_SPEED,
|
|
11
|
+
PLAYER_SPEED,
|
|
12
|
+
ZOMBIE_SEPARATION_DISTANCE,
|
|
13
|
+
ZOMBIE_WALL_FOLLOW_SENSOR_DISTANCE,
|
|
14
|
+
)
|
|
15
|
+
from ..models import GameData
|
|
16
|
+
from .constants import MAX_ZOMBIES
|
|
17
|
+
from .spawn import spawn_weighted_zombie
|
|
18
|
+
from .survivors import update_survivors
|
|
19
|
+
from .utils import rect_visible_on_screen
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
def process_player_input(
|
|
23
|
+
keys: Sequence[bool], player: Player, car: Car | None
|
|
24
|
+
) -> tuple[float, float, float, float]:
|
|
25
|
+
"""Process keyboard input and return movement deltas."""
|
|
26
|
+
dx_input, dy_input = 0, 0
|
|
27
|
+
if keys[pygame.K_w] or keys[pygame.K_UP]:
|
|
28
|
+
dy_input -= 1
|
|
29
|
+
if keys[pygame.K_s] or keys[pygame.K_DOWN]:
|
|
30
|
+
dy_input += 1
|
|
31
|
+
if keys[pygame.K_a] or keys[pygame.K_LEFT]:
|
|
32
|
+
dx_input -= 1
|
|
33
|
+
if keys[pygame.K_d] or keys[pygame.K_RIGHT]:
|
|
34
|
+
dx_input += 1
|
|
35
|
+
|
|
36
|
+
player_dx, player_dy, car_dx, car_dy = 0, 0, 0, 0
|
|
37
|
+
|
|
38
|
+
if player.in_car and car and car.alive():
|
|
39
|
+
target_speed = getattr(car, "speed", CAR_SPEED)
|
|
40
|
+
move_len = math.hypot(dx_input, dy_input)
|
|
41
|
+
if move_len > 0:
|
|
42
|
+
car_dx, car_dy = (
|
|
43
|
+
(dx_input / move_len) * target_speed,
|
|
44
|
+
(dy_input / move_len) * target_speed,
|
|
45
|
+
)
|
|
46
|
+
elif not player.in_car:
|
|
47
|
+
target_speed = PLAYER_SPEED
|
|
48
|
+
move_len = math.hypot(dx_input, dy_input)
|
|
49
|
+
if move_len > 0:
|
|
50
|
+
player_dx, player_dy = (
|
|
51
|
+
(dx_input / move_len) * target_speed,
|
|
52
|
+
(dy_input / move_len) * target_speed,
|
|
53
|
+
)
|
|
54
|
+
|
|
55
|
+
return player_dx, player_dy, car_dx, car_dy
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
def update_entities(
|
|
59
|
+
game_data: GameData,
|
|
60
|
+
player_dx: float,
|
|
61
|
+
player_dy: float,
|
|
62
|
+
car_dx: float,
|
|
63
|
+
car_dy: float,
|
|
64
|
+
config: dict[str, Any],
|
|
65
|
+
wall_index: WallIndex | None = None,
|
|
66
|
+
) -> None:
|
|
67
|
+
"""Update positions and states of game entities."""
|
|
68
|
+
player = game_data.player
|
|
69
|
+
assert player is not None
|
|
70
|
+
car = game_data.car
|
|
71
|
+
wall_group = game_data.groups.wall_group
|
|
72
|
+
all_sprites = game_data.groups.all_sprites
|
|
73
|
+
zombie_group = game_data.groups.zombie_group
|
|
74
|
+
survivor_group = game_data.groups.survivor_group
|
|
75
|
+
camera = game_data.camera
|
|
76
|
+
stage = game_data.stage
|
|
77
|
+
active_car = car if car and car.alive() else None
|
|
78
|
+
|
|
79
|
+
all_walls = list(wall_group) if wall_index is None else None
|
|
80
|
+
|
|
81
|
+
def _walls_near(center: tuple[float, float], radius: float) -> list[Wall]:
|
|
82
|
+
if wall_index is None:
|
|
83
|
+
return all_walls or []
|
|
84
|
+
return walls_for_radius(
|
|
85
|
+
wall_index,
|
|
86
|
+
center,
|
|
87
|
+
radius,
|
|
88
|
+
cell_size=game_data.cell_size,
|
|
89
|
+
grid_cols=stage.grid_cols,
|
|
90
|
+
grid_rows=stage.grid_rows,
|
|
91
|
+
)
|
|
92
|
+
|
|
93
|
+
# Update player/car movement
|
|
94
|
+
if player.in_car and active_car:
|
|
95
|
+
car_walls = _walls_near((active_car.x, active_car.y), 150.0)
|
|
96
|
+
active_car.move(car_dx, car_dy, car_walls)
|
|
97
|
+
player.rect.center = active_car.rect.center
|
|
98
|
+
player.x, player.y = active_car.x, active_car.y
|
|
99
|
+
elif not player.in_car:
|
|
100
|
+
# Ensure player is in all_sprites if not in car
|
|
101
|
+
if player not in all_sprites:
|
|
102
|
+
all_sprites.add(player, layer=2)
|
|
103
|
+
player.move(
|
|
104
|
+
player_dx,
|
|
105
|
+
player_dy,
|
|
106
|
+
wall_group,
|
|
107
|
+
wall_index=wall_index,
|
|
108
|
+
cell_size=game_data.cell_size,
|
|
109
|
+
level_width=game_data.level_width,
|
|
110
|
+
level_height=game_data.level_height,
|
|
111
|
+
)
|
|
112
|
+
else:
|
|
113
|
+
# Player flagged as in-car but car is gone; drop them back to foot control
|
|
114
|
+
player.in_car = False
|
|
115
|
+
|
|
116
|
+
# Update camera
|
|
117
|
+
target_for_camera = active_car if player.in_car and active_car else player
|
|
118
|
+
camera.update(target_for_camera)
|
|
119
|
+
|
|
120
|
+
update_survivors(game_data, wall_index=wall_index)
|
|
121
|
+
|
|
122
|
+
# Spawn new zombies if needed
|
|
123
|
+
current_time = pygame.time.get_ticks()
|
|
124
|
+
spawn_interval = max(1, stage.spawn_interval_ms)
|
|
125
|
+
spawn_blocked = stage.survival_stage and game_data.state.dawn_ready
|
|
126
|
+
if (
|
|
127
|
+
len(zombie_group) < MAX_ZOMBIES
|
|
128
|
+
and not spawn_blocked
|
|
129
|
+
and current_time - game_data.state.last_zombie_spawn_time > spawn_interval
|
|
130
|
+
):
|
|
131
|
+
if spawn_weighted_zombie(game_data, config):
|
|
132
|
+
game_data.state.last_zombie_spawn_time = current_time
|
|
133
|
+
|
|
134
|
+
# Update zombies
|
|
135
|
+
target_center = (
|
|
136
|
+
active_car.rect.center if player.in_car and active_car else player.rect.center
|
|
137
|
+
)
|
|
138
|
+
buddies = [
|
|
139
|
+
survivor
|
|
140
|
+
for survivor in survivor_group
|
|
141
|
+
if survivor.alive() and survivor.is_buddy and not survivor.rescued
|
|
142
|
+
]
|
|
143
|
+
buddies_on_screen = [
|
|
144
|
+
buddy for buddy in buddies if rect_visible_on_screen(camera, buddy.rect)
|
|
145
|
+
]
|
|
146
|
+
|
|
147
|
+
survivors_on_screen: list[Survivor] = []
|
|
148
|
+
if stage.rescue_stage:
|
|
149
|
+
for survivor in survivor_group:
|
|
150
|
+
if survivor.alive():
|
|
151
|
+
if rect_visible_on_screen(camera, survivor.rect):
|
|
152
|
+
survivors_on_screen.append(survivor)
|
|
153
|
+
|
|
154
|
+
zombies_sorted: list[Zombie] = sorted(list(zombie_group), key=lambda z: z.x)
|
|
155
|
+
|
|
156
|
+
def _nearby_zombies(index: int) -> list[Zombie]:
|
|
157
|
+
center = zombies_sorted[index]
|
|
158
|
+
neighbors: list[Zombie] = []
|
|
159
|
+
search_radius = ZOMBIE_SEPARATION_DISTANCE + PLAYER_SPEED
|
|
160
|
+
for left in range(index - 1, -1, -1):
|
|
161
|
+
other = zombies_sorted[left]
|
|
162
|
+
if center.x - other.x > search_radius:
|
|
163
|
+
break
|
|
164
|
+
if other.alive():
|
|
165
|
+
neighbors.append(other)
|
|
166
|
+
for right in range(index + 1, len(zombies_sorted)):
|
|
167
|
+
other = zombies_sorted[right]
|
|
168
|
+
if other.x - center.x > search_radius:
|
|
169
|
+
break
|
|
170
|
+
if other.alive():
|
|
171
|
+
neighbors.append(other)
|
|
172
|
+
return neighbors
|
|
173
|
+
|
|
174
|
+
for idx, zombie in enumerate(zombies_sorted):
|
|
175
|
+
target = target_center
|
|
176
|
+
if buddies_on_screen:
|
|
177
|
+
dist_to_target_sq = (target_center[0] - zombie.x) ** 2 + (
|
|
178
|
+
target_center[1] - zombie.y
|
|
179
|
+
) ** 2
|
|
180
|
+
nearest_buddy = min(
|
|
181
|
+
buddies_on_screen,
|
|
182
|
+
key=lambda buddy: (buddy.rect.centerx - zombie.x) ** 2
|
|
183
|
+
+ (buddy.rect.centery - zombie.y) ** 2,
|
|
184
|
+
)
|
|
185
|
+
dist_to_buddy_sq = (nearest_buddy.rect.centerx - zombie.x) ** 2 + (
|
|
186
|
+
nearest_buddy.rect.centery - zombie.y
|
|
187
|
+
) ** 2
|
|
188
|
+
if dist_to_buddy_sq < dist_to_target_sq:
|
|
189
|
+
target = nearest_buddy.rect.center
|
|
190
|
+
|
|
191
|
+
if stage.rescue_stage:
|
|
192
|
+
zombie_on_screen = rect_visible_on_screen(camera, zombie.rect)
|
|
193
|
+
if zombie_on_screen:
|
|
194
|
+
candidate_positions: list[tuple[int, int]] = []
|
|
195
|
+
for survivor in survivors_on_screen:
|
|
196
|
+
candidate_positions.append(survivor.rect.center)
|
|
197
|
+
for buddy in buddies_on_screen:
|
|
198
|
+
candidate_positions.append(buddy.rect.center)
|
|
199
|
+
candidate_positions.append(player.rect.center)
|
|
200
|
+
if candidate_positions:
|
|
201
|
+
target = min(
|
|
202
|
+
candidate_positions,
|
|
203
|
+
key=lambda pos: (pos[0] - zombie.x) ** 2
|
|
204
|
+
+ (pos[1] - zombie.y) ** 2,
|
|
205
|
+
)
|
|
206
|
+
nearby_candidates = _nearby_zombies(idx)
|
|
207
|
+
zombie_search_radius = ZOMBIE_WALL_FOLLOW_SENSOR_DISTANCE + zombie.radius + 120
|
|
208
|
+
nearby_walls = _walls_near((zombie.x, zombie.y), zombie_search_radius)
|
|
209
|
+
zombie.update(
|
|
210
|
+
target,
|
|
211
|
+
nearby_walls,
|
|
212
|
+
nearby_candidates,
|
|
213
|
+
footprints=game_data.state.footprints,
|
|
214
|
+
cell_size=game_data.cell_size,
|
|
215
|
+
grid_cols=stage.grid_cols,
|
|
216
|
+
grid_rows=stage.grid_rows,
|
|
217
|
+
level_width=game_data.level_width,
|
|
218
|
+
level_height=game_data.level_height,
|
|
219
|
+
outer_wall_cells=game_data.layout.outer_wall_cells,
|
|
220
|
+
)
|