zombie-escape 1.12.3__py3-none-any.whl → 1.13.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.
@@ -12,6 +12,7 @@ from ..gameplay_constants import (
12
12
  SURVIVAL_TIME_ACCEL_MAX_SUBSTEP,
13
13
  )
14
14
  from ..gameplay import (
15
+ MapGenerationError,
15
16
  apply_passenger_speed_penalty,
16
17
  check_interactions,
17
18
  cleanup_survivor_messages,
@@ -52,8 +53,10 @@ from ..progress import record_stage_clear
52
53
  from ..screens import (
53
54
  ScreenID,
54
55
  ScreenTransition,
56
+ nudge_window_scale,
55
57
  present,
56
58
  sync_window_size,
59
+ toggle_fullscreen,
57
60
  )
58
61
 
59
62
  if TYPE_CHECKING:
@@ -71,11 +74,24 @@ def gameplay_screen(
71
74
  seed: int | None,
72
75
  render_assets: "RenderAssets",
73
76
  debug_mode: bool = False,
77
+ show_fps: bool = False,
74
78
  ) -> ScreenTransition:
75
79
  """Main gameplay loop that returns the next screen transition."""
76
80
 
77
81
  screen_width = screen.get_width()
78
82
  screen_height = screen.get_height()
83
+ mouse_hidden = False
84
+
85
+ def _set_mouse_hidden(hidden: bool) -> None:
86
+ nonlocal mouse_hidden
87
+ if mouse_hidden == hidden:
88
+ return
89
+ pygame.mouse.set_visible(not hidden)
90
+ mouse_hidden = hidden
91
+
92
+ def _finalize(transition: ScreenTransition) -> ScreenTransition:
93
+ _set_mouse_hidden(False)
94
+ return transition
79
95
 
80
96
  seed_value = seed if seed is not None else generate_seed()
81
97
  applied_seed = seed_rng(seed_value)
@@ -83,6 +99,7 @@ def gameplay_screen(
83
99
  game_data = initialize_game_state(config, stage)
84
100
  game_data.state.seed = applied_seed
85
101
  game_data.state.debug_mode = debug_mode
102
+ game_data.state.show_fps = show_fps
86
103
  if debug_mode and stage.endurance_stage:
87
104
  goal_ms = max(0, stage.endurance_goal_ms)
88
105
  if goal_ms > 0:
@@ -101,8 +118,32 @@ def gameplay_screen(
101
118
  ignore_focus_loss_until = 0
102
119
  controller = init_first_controller()
103
120
  joystick = init_first_joystick() if controller is None else None
121
+ _set_mouse_hidden(pygame.mouse.get_focused())
122
+
123
+ try:
124
+ layout_data = generate_level_from_blueprint(game_data, config)
125
+ except MapGenerationError:
126
+ # If generation fails after retries, show error and back to title
127
+ draw(
128
+ render_assets,
129
+ screen,
130
+ game_data,
131
+ config=config,
132
+ hint_color=None,
133
+ fps=fps,
134
+ present_fn=present,
135
+ )
136
+ show_message(
137
+ screen,
138
+ tr("errors.map_generation_failed"),
139
+ 16,
140
+ RED,
141
+ (screen_width // 2, screen_height // 2),
142
+ )
143
+ present(screen)
144
+ pygame.time.delay(3000)
145
+ return _finalize(ScreenTransition(ScreenID.TITLE))
104
146
 
105
- layout_data = generate_level_from_blueprint(game_data, config)
106
147
  sync_ambient_palette_with_flashlights(game_data, force=True)
107
148
  initial_waiting = max(0, stage.waiting_car_target_count)
108
149
  player, waiting_cars = setup_player_and_cars(
@@ -124,7 +165,7 @@ def gameplay_screen(
124
165
  if stage.requires_fuel:
125
166
  fuel_spawn_count = stage.fuel_spawn_count
126
167
  fuel_can = place_fuel_can(
127
- layout_data["walkable_cells"],
168
+ layout_data["fuel_cells"] or layout_data["walkable_cells"],
128
169
  cell_size,
129
170
  player,
130
171
  cars=game_data.waiting_cars,
@@ -137,7 +178,7 @@ def gameplay_screen(
137
178
  occupied_centers.add(fuel_can.rect.center)
138
179
  flashlight_count = stage.initial_flashlight_count
139
180
  flashlights = place_flashlights(
140
- layout_data["walkable_cells"],
181
+ layout_data["flashlight_cells"] or layout_data["walkable_cells"],
141
182
  cell_size,
142
183
  player,
143
184
  cars=game_data.waiting_cars,
@@ -151,7 +192,7 @@ def gameplay_screen(
151
192
 
152
193
  shoes_count = stage.initial_shoes_count
153
194
  shoes_list = place_shoes(
154
- layout_data["walkable_cells"],
195
+ layout_data["shoes_cells"] or layout_data["walkable_cells"],
155
196
  cell_size,
156
197
  player,
157
198
  cars=game_data.waiting_cars,
@@ -192,16 +233,18 @@ def gameplay_screen(
192
233
  )
193
234
  present(screen)
194
235
  continue
195
- return ScreenTransition(
196
- ScreenID.GAME_OVER,
197
- stage=stage,
198
- game_data=game_data,
199
- config=config,
236
+ return _finalize(
237
+ ScreenTransition(
238
+ ScreenID.GAME_OVER,
239
+ stage=stage,
240
+ game_data=game_data,
241
+ config=config,
242
+ )
200
243
  )
201
244
 
202
245
  for event in pygame.event.get():
203
246
  if event.type == pygame.QUIT:
204
- return ScreenTransition(ScreenID.EXIT)
247
+ return _finalize(ScreenTransition(ScreenID.EXIT))
205
248
  if event.type in (pygame.WINDOWSIZECHANGED, pygame.VIDEORESIZE):
206
249
  sync_window_size(event, game_data=game_data)
207
250
  continue
@@ -230,6 +273,15 @@ def gameplay_screen(
230
273
  if joystick and not joystick.get_init():
231
274
  joystick = None
232
275
  if event.type == pygame.KEYDOWN:
276
+ if event.key == pygame.K_LEFTBRACKET:
277
+ nudge_window_scale(0.5, game_data=game_data)
278
+ continue
279
+ if event.key == pygame.K_RIGHTBRACKET:
280
+ nudge_window_scale(2.0, game_data=game_data)
281
+ continue
282
+ if event.key == pygame.K_f:
283
+ toggle_fullscreen(game_data=game_data)
284
+ continue
233
285
  if event.key == pygame.K_s and (
234
286
  pygame.key.get_mods() & pygame.KMOD_CTRL
235
287
  ):
@@ -248,7 +300,7 @@ def gameplay_screen(
248
300
  continue
249
301
  if paused_manual:
250
302
  if event.key == pygame.K_ESCAPE:
251
- return ScreenTransition(ScreenID.TITLE)
303
+ return _finalize(ScreenTransition(ScreenID.TITLE))
252
304
  if event.key == pygame.K_p:
253
305
  paused_manual = False
254
306
  continue
@@ -261,13 +313,13 @@ def gameplay_screen(
261
313
  ):
262
314
  if debug_mode:
263
315
  if is_select_event(event):
264
- return ScreenTransition(ScreenID.TITLE)
316
+ return _finalize(ScreenTransition(ScreenID.TITLE))
265
317
  if is_start_event(event):
266
318
  paused_manual = not paused_manual
267
319
  continue
268
320
  if paused_manual:
269
321
  if is_select_event(event):
270
- return ScreenTransition(ScreenID.TITLE)
322
+ return _finalize(ScreenTransition(ScreenID.TITLE))
271
323
  if is_start_event(event):
272
324
  paused_manual = False
273
325
  continue
@@ -275,6 +327,8 @@ def gameplay_screen(
275
327
  paused_manual = True
276
328
  continue
277
329
 
330
+ _set_mouse_hidden(pygame.mouse.get_focused())
331
+
278
332
  paused = paused_manual or paused_focus
279
333
  if paused:
280
334
  draw(
@@ -441,4 +495,4 @@ def gameplay_screen(
441
495
  )
442
496
 
443
497
  # Should not reach here, but return to title if it happens
444
- return ScreenTransition(ScreenID.TITLE)
498
+ return _finalize(ScreenTransition(ScreenID.TITLE))
@@ -44,14 +44,21 @@ README_URLS: dict[str, str] = {
44
44
  "en": "https://github.com/tos-kamiya/zombie-escape/blob/main/README.md",
45
45
  "ja": "https://github.com/tos-kamiya/zombie-escape/blob/main/README-ja_JP.md",
46
46
  }
47
+ STAGE6_URLS: dict[str, str] = {
48
+ "en": "https://github.com/tos-kamiya/zombie-escape/blob/main/docs/stages-6plus.md",
49
+ "ja": "https://github.com/tos-kamiya/zombie-escape/blob/main/docs/stages-6plus-ja_JP.md",
50
+ }
47
51
  UNCLEARED_STAGE_COLOR: tuple[int, int, int] = (220, 80, 80)
48
52
 
49
53
 
50
- def _open_readme_link() -> None:
51
- """Open the GitHub README for the active UI language."""
54
+ def _open_readme_link(*, use_stage6: bool = False) -> None:
55
+ """Open the GitHub README or Stage 6+ guide for the active UI language."""
52
56
 
53
57
  language = get_language()
54
- url = README_URLS.get(language, README_URLS["en"])
58
+ if use_stage6:
59
+ url = STAGE6_URLS.get(language, STAGE6_URLS["en"])
60
+ else:
61
+ url = README_URLS.get(language, README_URLS["en"])
55
62
  try:
56
63
  webbrowser.open(url, new=0, autoraise=True)
57
64
  except Exception as exc: # pragma: no cover - best effort only
@@ -295,7 +302,7 @@ def title_screen(
295
302
  seed_is_auto=current_seed_auto,
296
303
  )
297
304
  if current["type"] == "readme":
298
- _open_readme_link()
305
+ _open_readme_link(use_stage6=current_page > 0)
299
306
  continue
300
307
  if current["type"] == "quit":
301
308
  return ScreenTransition(
@@ -327,7 +334,7 @@ def title_screen(
327
334
  seed_is_auto=current_seed_auto,
328
335
  )
329
336
  if current["type"] == "readme":
330
- _open_readme_link()
337
+ _open_readme_link(use_stage6=current_page > 0)
331
338
  continue
332
339
  if current["type"] == "quit":
333
340
  return ScreenTransition(
@@ -475,7 +482,8 @@ def title_screen(
475
482
  if option["type"] == "settings":
476
483
  label = tr("menu.settings")
477
484
  elif option["type"] == "readme":
478
- label = f"> {tr('menu.readme')}"
485
+ label_key = "menu.readme_stage6" if current_page > 0 else "menu.readme"
486
+ label = f"> {tr(label_key)}"
479
487
  else:
480
488
  label = tr("menu.quit")
481
489
  color = WHITE
@@ -508,7 +516,10 @@ def title_screen(
508
516
  elif current["type"] == "quit":
509
517
  help_text = tr("menu.option_help.quit")
510
518
  elif current["type"] == "readme":
511
- help_text = tr("menu.option_help.readme")
519
+ help_key = (
520
+ "menu.option_help.readme_stage6" if current_page > 0 else "menu.option_help.readme"
521
+ )
522
+ help_text = tr(help_key)
512
523
 
513
524
  if help_text:
514
525
  _blit_wrapped_text(
@@ -85,7 +85,7 @@ STAGES: list[Stage] = [
85
85
  interior_spawn_weight=0.3,
86
86
  zombie_normal_ratio=0.4,
87
87
  zombie_tracker_ratio=0.3,
88
- zombie_wall_follower_ratio=0.3,
88
+ zombie_wall_hugging_ratio=0.3,
89
89
  zombie_aging_duration_frames=ZOMBIE_AGING_DURATION_FRAMES * 2,
90
90
  initial_interior_spawn_rate=0.01,
91
91
  ),
@@ -101,7 +101,7 @@ STAGES: list[Stage] = [
101
101
  interior_spawn_weight=0.6,
102
102
  zombie_normal_ratio=0,
103
103
  zombie_tracker_ratio=0.3,
104
- zombie_wall_follower_ratio=0.7,
104
+ zombie_wall_hugging_ratio=0.7,
105
105
  zombie_aging_duration_frames=ZOMBIE_AGING_DURATION_FRAMES * 2,
106
106
  initial_interior_spawn_rate=0.01,
107
107
  ),
@@ -118,7 +118,7 @@ STAGES: list[Stage] = [
118
118
  waiting_car_target_count=1,
119
119
  zombie_normal_ratio=0,
120
120
  zombie_tracker_ratio=0.3,
121
- zombie_wall_follower_ratio=0.7,
121
+ zombie_wall_hugging_ratio=0.7,
122
122
  survivor_spawn_rate=SURVIVOR_SPAWN_RATE,
123
123
  zombie_aging_duration_frames=ZOMBIE_AGING_DURATION_FRAMES * 2,
124
124
  initial_interior_spawn_rate=0.01,
@@ -135,7 +135,7 @@ STAGES: list[Stage] = [
135
135
  interior_spawn_weight=0.3,
136
136
  zombie_normal_ratio=0.4,
137
137
  zombie_tracker_ratio=0.4,
138
- zombie_wall_follower_ratio=0.2,
138
+ zombie_wall_hugging_ratio=0.2,
139
139
  zombie_aging_duration_frames=ZOMBIE_AGING_DURATION_FRAMES * 2,
140
140
  initial_interior_spawn_rate=0.02,
141
141
  waiting_car_target_count=1,
@@ -170,10 +170,10 @@ STAGES: list[Stage] = [
170
170
  interior_spawn_weight=0.2,
171
171
  interior_fall_spawn_weight=0.3,
172
172
  fall_spawn_zones=[
173
- (3, 3, 12, 12),
174
- (3, 17, 12, 12),
175
- (17, 3, 12, 12),
176
- (17, 17, 12, 12),
173
+ (4, 4, 10, 10),
174
+ (4, 18, 10, 10),
175
+ (18, 18, 10, 10),
176
+ (18, 18, 10, 10),
177
177
  ],
178
178
  initial_flashlight_count=5,
179
179
  zombie_aging_duration_frames=ZOMBIE_AGING_DURATION_FRAMES * 2,
@@ -192,7 +192,7 @@ STAGES: list[Stage] = [
192
192
  interior_fall_spawn_weight=0.3,
193
193
  zombie_normal_ratio=0.4,
194
194
  zombie_tracker_ratio=0.3,
195
- zombie_wall_follower_ratio=0.3,
195
+ zombie_wall_hugging_ratio=0.3,
196
196
  zombie_aging_duration_frames=ZOMBIE_AGING_DURATION_FRAMES * 2,
197
197
  initial_flashlight_count=3,
198
198
  fall_spawn_zones=[
@@ -214,6 +214,7 @@ STAGES: list[Stage] = [
214
214
  interior_spawn_weight=0.1,
215
215
  interior_fall_spawn_weight=0.7,
216
216
  fall_spawn_floor_ratio=0.05,
217
+ wall_rubble_ratio=0.35,
217
218
  initial_flashlight_count=3,
218
219
  zombie_aging_duration_frames=ZOMBIE_AGING_DURATION_FRAMES * 2,
219
220
  initial_shoes_count=1,
@@ -235,13 +236,48 @@ STAGES: list[Stage] = [
235
236
  interior_fall_spawn_weight=0.7,
236
237
  initial_flashlight_count=3,
237
238
  zombie_normal_ratio=0.5,
238
- zombie_wall_follower_ratio=0.5,
239
+ zombie_wall_hugging_ratio=0.5,
239
240
  fall_spawn_zones=[
240
241
  (33, 2, 4, 18),
241
242
  ],
242
243
  zombie_aging_duration_frames=ZOMBIE_AGING_DURATION_FRAMES * 2,
243
244
  initial_shoes_count=1,
244
245
  ),
246
+ Stage(
247
+ id="stage16",
248
+ name_key="stages.stage16.name",
249
+ description_key="stages.stage16.description",
250
+ available=True,
251
+ requires_fuel=True,
252
+ wall_algorithm="sparse_moore.25%",
253
+ grid_cols=40,
254
+ grid_rows=25,
255
+ tile_size=60,
256
+ pitfall_density=0.04,
257
+ initial_flashlight_count=1,
258
+ initial_shoes_count=1,
259
+ initial_interior_spawn_rate=0.12,
260
+ exterior_spawn_weight=0.1,
261
+ interior_spawn_weight=0.9,
262
+ zombie_aging_duration_frames=ZOMBIE_AGING_DURATION_FRAMES * 2,
263
+ ),
264
+ Stage(
265
+ id="stage17",
266
+ name_key="stages.stage17.name",
267
+ description_key="stages.stage17.description",
268
+ available=False,
269
+ requires_fuel=True,
270
+ wall_algorithm="grid_wire",
271
+ pitfall_density=0.04,
272
+ initial_flashlight_count=1,
273
+ initial_shoes_count=1,
274
+ initial_interior_spawn_rate=0.1,
275
+ exterior_spawn_weight=0.1,
276
+ interior_spawn_weight=0.9,
277
+ zombie_tracker_ratio=1.0,
278
+ wall_rubble_ratio=0.25,
279
+ zombie_aging_duration_frames=ZOMBIE_AGING_DURATION_FRAMES * 2,
280
+ ),
245
281
  ]
246
282
  DEFAULT_STAGE_ID = "stage1"
247
283
 
@@ -43,6 +43,11 @@ def _parse_cli_args(argv: list[str]) -> Tuple[argparse.Namespace, list[str]]:
43
43
  action="store_true",
44
44
  help="Enable debugging aids for Stage 5 and hide pause overlay",
45
45
  )
46
+ parser.add_argument(
47
+ "--show-fps",
48
+ action="store_true",
49
+ help="Show FPS overlay during gameplay",
50
+ )
46
51
  parser.add_argument(
47
52
  "--profile",
48
53
  action="store_true",
@@ -53,6 +58,11 @@ def _parse_cli_args(argv: list[str]) -> Tuple[argparse.Namespace, list[str]]:
53
58
  default="profile.prof",
54
59
  help="cProfile output path (default: profile.prof)",
55
60
  )
61
+ parser.add_argument(
62
+ "--export-images",
63
+ action="store_true",
64
+ help="Export documentation images to imgs/exports and exit",
65
+ )
56
66
  parser.add_argument("--seed")
57
67
  return parser.parse_known_args(argv)
58
68
 
@@ -100,10 +110,20 @@ def main() -> None:
100
110
  clock = pygame.time.Clock()
101
111
 
102
112
  debug_mode = bool(args.debug)
113
+ show_fps = bool(args.show_fps) or debug_mode
103
114
  cli_seed_text, cli_seed_is_auto = _sanitize_seed_text(args.seed)
104
115
  title_seed_text, title_seed_is_auto = cli_seed_text, cli_seed_is_auto
105
116
  last_stage_id: str | None = None
106
117
 
118
+ if args.export_images:
119
+ from .export_images import export_images
120
+
121
+ output_dir = Path.cwd() / "imgs" / "exports"
122
+ saved = export_images(output_dir, cell_size=DEFAULT_TILE_SIZE)
123
+ print(f"Exported {len(saved)} images to {output_dir}")
124
+ pygame.quit()
125
+ return
126
+
107
127
  config: dict[str, Any]
108
128
  config, config_path = load_config()
109
129
  if not config_path.exists():
@@ -121,6 +141,7 @@ def main() -> None:
121
141
  seed: int | None,
122
142
  render_assets: RenderAssets,
123
143
  debug_mode: bool,
144
+ show_fps: bool,
124
145
  ) -> ScreenTransition:
125
146
  import cProfile
126
147
  import pstats
@@ -138,6 +159,7 @@ def main() -> None:
138
159
  seed=seed,
139
160
  render_assets=render_assets,
140
161
  debug_mode=debug_mode,
162
+ show_fps=show_fps,
141
163
  )
142
164
  finally:
143
165
  output_path = Path(args.profile_output)
@@ -206,6 +228,7 @@ def main() -> None:
206
228
  seed=seed_value,
207
229
  render_assets=render_assets,
208
230
  debug_mode=debug_mode,
231
+ show_fps=show_fps,
209
232
  )
210
233
  except SystemExit:
211
234
  running = False
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: zombie-escape
3
- Version: 1.12.3
3
+ Version: 1.13.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>
@@ -57,7 +57,8 @@ This game is a simple 2D top-down action game where the player aims to escape by
57
57
  - **Pause:** `P`/Start or `ESC`/Select
58
58
  - **Quit Game:** `ESC`/Select (from pause)
59
59
  - **Restart:** `R` key (on Game Over/Clear screen)
60
- - **Window/Fullscreen (title/settings only):** `[` to shrink, `]` to enlarge, `F` to toggle fullscreen
60
+ - **Window/Fullscreen:** `[` to shrink, `]` to enlarge, `F` to toggle fullscreen
61
+ - **FPS Overlay:** Launch with `--show-fps` (implied by `--debug`)
61
62
  - **Time Acceleration:** Hold either `Shift` key or `R1` to run the entire world 4x faster; release to return to normal speed.
62
63
 
63
64
  ## Title Screen
@@ -72,6 +73,9 @@ At the title screen you can pick a stage:
72
73
  - **Stage 4: Evacuate Survivors** — start fueled, find the car, gather nearby civilians, and escape before zombies reach them. Stage 4 sprinkles extra parked cars across the map; slamming into one while already driving fully repairs your current ride and adds five more safe seats.
73
74
  - **Stage 5: Survive Until Dawn** — every car is bone-dry. Endure until the sun rises while the horde presses in from every direction. Once dawn hits, outdoor zombies carbonize and you must walk out through an existing exterior gap to win; cars remain unusable.
74
75
 
76
+ Stages 6+ unlock after clearing Stages 1–5. On the title screen, use left/right to select later stages.
77
+ Open the Stage 6+ description: [docs/stages-6plus.md](docs/stages-6plus.md)
78
+
75
79
  **Stage names are red until cleared** and turn white after at least one clear.
76
80
 
77
81
  An objective reminder is shown at the top-left during play.
@@ -93,31 +97,52 @@ Open **Settings** from the title to toggle gameplay assists:
93
97
 
94
98
  ### Characters/Items
95
99
 
96
- - **Player:** A blue circle with small hands. Controlled with the WASD or arrow keys. When carrying fuel a tiny yellow square appears near the sprite so you can immediately see whether you're ready to drive.
97
- - **Zombie:** A red circle. Will chase the player (or car) once detected.
98
- - When out of sight, the zombie's movement mode will randomly switch every certain time (moving horizontally/vertically only, side-to-side movement, random movement, etc.).
99
- - Variants with different behavior have been observed.
100
- - **Car:** A yellow rectangle. The player can enter by making contact with it.
100
+ #### Characters
101
+
102
+ | Name | Image | Notes |
103
+ | --- | --- | --- |
104
+ | Player | <img src="https://raw.githubusercontent.com/tos-kamiya/zombie-escape/main/imgs/exports/player.png" width="64"> | Blue circle with small hands. |
105
+ | Zombie (Normal) | <img src="https://raw.githubusercontent.com/tos-kamiya/zombie-escape/main/imgs/exports/zombie-normal.png" width="64"> | Chases the player once detected. |
106
+ | Car | <img src="https://raw.githubusercontent.com/tos-kamiya/zombie-escape/main/imgs/exports/car.png" width="64"> | Driveable escape vehicle with durability. |
107
+ | Buddy (Stage 3) | <img src="https://raw.githubusercontent.com/tos-kamiya/zombie-escape/main/imgs/exports/buddy.png" width="64"> | A green survivor you can rescue. |
108
+ | Survivors (Stage 4) | <img src="https://raw.githubusercontent.com/tos-kamiya/zombie-escape/main/imgs/exports/survivor.png" width="64"> | Civilians to evacuate by car. |
109
+
110
+ - **Player:** Controlled with the WASD or arrow keys. When carrying fuel a tiny yellow square appears near the sprite so you can immediately see whether you're ready to drive.
111
+ - **Zombie:** When out of sight, the zombie's movement mode will randomly switch every certain time (moving horizontally/vertically only, side-to-side movement, random movement, etc.).
112
+ - **Car:**
101
113
  - The car has durability. Durability decreases when colliding with internal walls or hitting zombies.
102
114
  - If durability reaches 0, the car is destroyed and you are dumped on foot; you must track down another parked car hidden in the level.
103
115
  - When you're already driving, ramming a parked car instantly restores your current car's health. On Stage 4 this also increases the safe passenger limit by five.
104
116
  - After roughly 5 minutes of play, a small triangle near the player points toward the objective: fuel first (Stage 2 before pickup), car after fuel is collected (Stage 2/3), or car directly (Stage 1/4).
105
- - **Walls:** Outer walls are gray; inner walls are beige.
106
- - **Outer Walls:** Walls surrounding the stage that are nearly indestructible. Each side has at least three openings (exits).
107
- - **Inner Walls:** Beige walls randomly placed inside the building. Inner wall segments each have durability. **The player can break these walls** by repeatedly colliding with a segment to reduce its durability; when it reaches 0, the segment is destroyed and disappears. Zombies can also wear down walls, but far more slowly. The car cannot break walls.
108
- - **Flashlight:** Each pickup expands your visible radius by about 20% (grab two to reach the max boost).
109
- - **Steel Beam (optional):** A square post with crossed diagonals; same collision as inner walls but with triple durability. Spawns independently of inner walls (may overlap them). If an inner wall covers a beam, the beam appears once the wall is destroyed.
110
- - **Fuel Can (Stages 2 & 3):** A yellow jerrycan that only spawns on the fuel-run stages. Pick it up before driving the car; once collected the on-player indicator appears until you refuel the car.
111
- - **Buddy (Stage 3):** A green circle survivor with small hands and a blue outline who spawns somewhere in the building and waits.
117
+ - **Buddy (Stage 3):**
112
118
  - Zombies only choose to pursue the buddy if they are on-screen; otherwise they ignore them.
113
119
  - If a zombie tags the buddy off-screen, the buddy quietly respawns somewhere else instead of ending the run.
114
120
  - Touch the buddy on foot to make them follow you (at 70% of player speed). Touch them while driving to pick them up.
115
121
  - If you bash an inner wall or steel beam, the buddy will drift toward that spot and help chip away at it.
116
- - **Survivors (Stage 4):** Pale gray civilians with a blue outline, scattered indoors.
122
+ - **Survivors (Stage 4):**
117
123
  - They stand still until you get close, then shuffle toward you at about one-third of player speed.
118
124
  - Zombies can convert them if both are on-screen; the survivor shouts a line and turns instantly.
119
125
  - They only board the car; your safe capacity starts at five but grows by five each time you sideswipe a parked car while already driving. Speed loss is based on how full the car is relative to that capacity, so extra slots mean quicker getaways.
120
126
 
127
+ #### Items
128
+
129
+ | Name | Image | Notes |
130
+ | --- | --- | --- |
131
+ | Flashlight | <img src="https://raw.githubusercontent.com/tos-kamiya/zombie-escape/main/imgs/exports/flashlight.png" width="64"> | Each pickup expands your visible radius by about 20% (grab two to reach the max boost). |
132
+ | Fuel Can (Stages 2 & 3) | <img src="https://raw.githubusercontent.com/tos-kamiya/zombie-escape/main/imgs/exports/fuel.png" width="64"> | Must be collected before driving the car in fuel-run stages. |
133
+ | Steel Beam (optional) | <img src="https://raw.githubusercontent.com/tos-kamiya/zombie-escape/main/imgs/exports/steel-beam.png" width="64"> | Same collision as inner walls but with triple durability. |
134
+
135
+ #### Environment
136
+
137
+ | Name | Image | Notes |
138
+ | --- | --- | --- |
139
+ | Outer Wall | <img src="https://raw.githubusercontent.com/tos-kamiya/zombie-escape/main/imgs/exports/wall-outer.png" width="64"> | Nearly indestructible; each side has exits. |
140
+ | Inner Wall | <img src="https://raw.githubusercontent.com/tos-kamiya/zombie-escape/main/imgs/exports/wall-inner.png" width="64"> | Breakable by the player; zombies chip away slowly. |
141
+
142
+ - **Walls:** Outer walls are gray; inner walls are beige.
143
+ - **Outer Walls:** Walls surrounding the stage that are nearly indestructible. Each side has at least three openings (exits).
144
+ - **Inner Walls:** Beige walls randomly placed inside the building. Inner wall segments each have durability. **The player can break these walls** by repeatedly colliding with a segment to reduce its durability; when it reaches 0, the segment is destroyed and disappears. Zombies can also wear down walls, but far more slowly. The car cannot break walls.
145
+
121
146
  ### Win/Lose Conditions
122
147
 
123
148
  - **Win Condition:** Escape the stage (level) boundaries while inside the car.
@@ -0,0 +1,49 @@
1
+ zombie_escape/__about__.py,sha256=PL2hra_dpIg6vOLUwwb3g7kiPnrWUuSYwSz1bnA8b98,135
2
+ zombie_escape/__init__.py,sha256=YSQnUghet8jxSvaGmKfzHfXXLlnvWh_xk10WGTDO2HM,173
3
+ zombie_escape/__main__.py,sha256=tddKDyuRF0BHYrPdMvpB-p2cQ3CdxmcwC4PORAnrmkg,108
4
+ zombie_escape/colors.py,sha256=ggXcsHQviTWO_ehCn8f3KJVYsY4dx5r5-RtKrTt7qPc,6526
5
+ zombie_escape/config.py,sha256=Ncvsz6HzBknSjecorkm7CrkrzWUIksD30ykLPueanyw,2008
6
+ zombie_escape/entities.py,sha256=qcwSqaacix6RIV4q_aNvCD6bQc_2UckkT8I92ah-maw,77077
7
+ zombie_escape/entities_constants.py,sha256=O4ti179GBBv1A3ECHJ4PdrmCZDr_GzYrEi6m2mgdn4Y,3884
8
+ zombie_escape/export_images.py,sha256=gfm4zkO_WDNwhyQPgEIE5xLrNgWFKbrUVmt30787oYw,8848
9
+ zombie_escape/font_utils.py,sha256=kkjcSlCTG3jO5zf5XUnirpJ-iL_Eg8ahzjZYGijF2JY,1206
10
+ zombie_escape/gameplay_constants.py,sha256=MdchWDi3p1xUOZuDsl5eH9KjazuApjuIYEddHkazEsw,984
11
+ zombie_escape/input_utils.py,sha256=0SHENZi5y-ybSxUX569RHihI_xbQWSI0FQ1q1ZE9U1c,5795
12
+ zombie_escape/level_blueprints.py,sha256=negu36BUHFv_7bcGYfpqiawQdSMxmxubYV6fSdOeG8E,18896
13
+ zombie_escape/level_constants.py,sha256=fSrPXfkuKHlv9XqmaRq6aR9UhjpqZK2iJJgMc-TXGXc,281
14
+ zombie_escape/localization.py,sha256=gp26FN_Od4eOeIK2aY0_QZ-9THw6yENh-cGTwglnMxw,6118
15
+ zombie_escape/models.py,sha256=Zxj57emfKQqI4dLbL6G7ciZCdytgTqYQ579_IPPmttQ,5300
16
+ zombie_escape/progress.py,sha256=WCFc7JeMY6noBjnTIFyHrXQJSM1j8PwyPA7S8ZQwjTE,1713
17
+ zombie_escape/render.py,sha256=6GmPqaJREe-hD5rQLgIW89soonQMXZNW_bEEOawYARo,57486
18
+ zombie_escape/render_assets.py,sha256=gsW-KTaR7iBxJoIo9JQ61-m9C44fjHIdgy9FUT2sMW4,26566
19
+ zombie_escape/render_constants.py,sha256=OTXpY7nzTwm2s4t6kHoSMC_4F6FrBqqI8xtQRk5lqWE,3883
20
+ zombie_escape/rng.py,sha256=gMAgpzYoNN1FxRG3aQ9fdXTDNAg48Rqz8YnB1nJ4Fpw,3787
21
+ zombie_escape/screen_constants.py,sha256=MJaTlSWfN4VtN6pMqPQ6LF34XdJm0wqYLuRwa1pQuAU,559
22
+ zombie_escape/stage_constants.py,sha256=zgs4cST94PMTaQQsBNwmFL4v8wmJoR7AQ2cahyH6Y9o,9073
23
+ zombie_escape/world_grid.py,sha256=9ZKaur2fBOXiZEg5WlaAnoeHV6Lda092rJROraMW6zk,4553
24
+ zombie_escape/zombie_escape.py,sha256=0P70hgbcb2j7lK57CoNQv8hrr2SZjMOwuNelBj6gDiw,9222
25
+ zombie_escape/assets/fonts/Silkscreen-Regular.ttf,sha256=SVZ0CGAICeJRR-kiWsTzf0EOLfRadQaWxFAnUx-2Xxs,31960
26
+ zombie_escape/assets/fonts/misaki_gothic.ttf,sha256=CWPhHonV-kCaegSKUujqLWI9dkp5mEiPikKRERYRxOE,1171204
27
+ zombie_escape/gameplay/__init__.py,sha256=QQsFVCP4FMxtztW8yOVcNAA9_16dbEcHshsqSuD679w,2433
28
+ zombie_escape/gameplay/ambient.py,sha256=hoCOz6ciyejU0nmJwdLqmVfaoo-01CrVSMRLpFMz93w,1446
29
+ zombie_escape/gameplay/constants.py,sha256=mgY9ajtG-rX__V7S7Q4R8Yh5XmMLaF_g8PsMgV8GIac,1216
30
+ zombie_escape/gameplay/footprints.py,sha256=WRN5wNIudqrQJ3vBljSbtwcdgn154f1jVdor4nEn_Ic,1995
31
+ zombie_escape/gameplay/interactions.py,sha256=D503c0PBmTF4SapBblmKHJwWFr4TDqVZ2merDNek0_k,14407
32
+ zombie_escape/gameplay/layout.py,sha256=H06ehDVD-k2Nk1hQ5fPoveGKJJDBo5C2CXKzIIUUuYs,11544
33
+ zombie_escape/gameplay/movement.py,sha256=PAGCiA2KSXRR9j0qPpCUd08IHjvQFRhGhp6j4E8dhAU,12272
34
+ zombie_escape/gameplay/spawn.py,sha256=eHoTl8_VYiGENCMVMpNUVfYEc2tvGQ1OnoA4Qv6BEFY,32349
35
+ zombie_escape/gameplay/state.py,sha256=WW6loUE1iHIW0MkawOpTDu2pSmmJHFEhIQT5IkFlJm4,4828
36
+ zombie_escape/gameplay/survivors.py,sha256=Rgn-OtiTnnu7DYuaye2oNEFGQMivTsZS0VgS6KAMOUw,12226
37
+ zombie_escape/gameplay/utils.py,sha256=NkAynBpu4VHuRGbEBDbA214WK92yquS6E_i7Y--cBV0,6439
38
+ zombie_escape/locales/ui.en.json,sha256=LIoGoILwa3x7wbEeSjvFiSCKmjaeirdWPz3WCQfQWIk,6939
39
+ zombie_escape/locales/ui.ja.json,sha256=2TFzwdaLOfSeyL1N0M8f1TP8uqWSwZRl59bBdruMYiM,7599
40
+ zombie_escape/screens/__init__.py,sha256=BFLQzXqyrAhmm6b_2wlnK7lMm_n5HvBklVrzrJRwRn0,8387
41
+ zombie_escape/screens/game_over.py,sha256=IWXiaEalfaoshRyRVAV3M3upL1Yf2uFip4wcdavIkmE,7192
42
+ zombie_escape/screens/gameplay.py,sha256=KVAQxV1ZEab2-3ii6ndCrhRN3ynEaGjb0xfXwqvyiwM,18091
43
+ zombie_escape/screens/settings.py,sha256=qgcnq8k-yeRbqweT9GsIefKWKvjOON_Gs2_SCZChY-8,21739
44
+ zombie_escape/screens/title.py,sha256=ru4PNDuIN6p7EM2xIpiJE-XZy7f9FNX4BOVALLFIeUM,23747
45
+ zombie_escape-1.13.1.dist-info/METADATA,sha256=nnn-TNYALI1yFZuYUaTchzpn-t7sCqin4xZU2uVbJDc,12124
46
+ zombie_escape-1.13.1.dist-info/WHEEL,sha256=WLgqFyCfm_KASv4WHyYy0P3pM_m7J5L9k2skdKLirC8,87
47
+ zombie_escape-1.13.1.dist-info/entry_points.txt,sha256=JprxC-vvkBJgsOp0WJnGBZRJ_ESjjmyS-nsPExeiLHU,49
48
+ zombie_escape-1.13.1.dist-info/licenses/LICENSE.txt,sha256=q-cJYG_K766eXSxQ7txWcWQ6nS2OF6c3HTVLesHbesU,1104
49
+ zombie_escape-1.13.1.dist-info/RECORD,,
@@ -1,47 +0,0 @@
1
- zombie_escape/__about__.py,sha256=FfoUj1wrayMeZzo5aMcg-nA_pRblR4kTsEVsQGtIL_M,135
2
- zombie_escape/__init__.py,sha256=YSQnUghet8jxSvaGmKfzHfXXLlnvWh_xk10WGTDO2HM,173
3
- zombie_escape/colors.py,sha256=ggXcsHQviTWO_ehCn8f3KJVYsY4dx5r5-RtKrTt7qPc,6526
4
- zombie_escape/config.py,sha256=Ncvsz6HzBknSjecorkm7CrkrzWUIksD30ykLPueanyw,2008
5
- zombie_escape/entities.py,sha256=HUANWUgIBq_AKzBYyg43QCup9-7C3JL0aOSr9uORt78,61616
6
- zombie_escape/entities_constants.py,sha256=IPDwYHe35p5EjFUyK6XCAIzm2XzvLo1AGJjQuhyCzTw,3336
7
- zombie_escape/font_utils.py,sha256=kkjcSlCTG3jO5zf5XUnirpJ-iL_Eg8ahzjZYGijF2JY,1206
8
- zombie_escape/gameplay_constants.py,sha256=MdchWDi3p1xUOZuDsl5eH9KjazuApjuIYEddHkazEsw,984
9
- zombie_escape/input_utils.py,sha256=0SHENZi5y-ybSxUX569RHihI_xbQWSI0FQ1q1ZE9U1c,5795
10
- zombie_escape/level_blueprints.py,sha256=Kv9W9VCzwalheKsNgoiJeflT06xdwVC4IN5I8iaOVMk,14469
11
- zombie_escape/level_constants.py,sha256=fSrPXfkuKHlv9XqmaRq6aR9UhjpqZK2iJJgMc-TXGXc,281
12
- zombie_escape/localization.py,sha256=gp26FN_Od4eOeIK2aY0_QZ-9THw6yENh-cGTwglnMxw,6118
13
- zombie_escape/models.py,sha256=Xlm1V8mZCrjWJDRBH22ol42TrnFKWdjCvHP8krSYBGY,5041
14
- zombie_escape/progress.py,sha256=WCFc7JeMY6noBjnTIFyHrXQJSM1j8PwyPA7S8ZQwjTE,1713
15
- zombie_escape/render.py,sha256=lLWRpyxGys2YV_ZvabLIjXxrEjcHBdKTxK0RBUxobIM,54782
16
- zombie_escape/render_assets.py,sha256=cnsSZGnDxKuXfej1cJpqos0tCdDj8CvmhJ2oXTmHN94,23596
17
- zombie_escape/render_constants.py,sha256=CEBPvzfV5yVYyZwoBXWh43Qmx4M5ZxpGFwN501_tIXM,3388
18
- zombie_escape/rng.py,sha256=gMAgpzYoNN1FxRG3aQ9fdXTDNAg48Rqz8YnB1nJ4Fpw,3787
19
- zombie_escape/screen_constants.py,sha256=MJaTlSWfN4VtN6pMqPQ6LF34XdJm0wqYLuRwa1pQuAU,559
20
- zombie_escape/stage_constants.py,sha256=d0HbQRI9Tcyqo_nWHrc7QqerucDhEMVCDCzQTBq7ti8,7903
21
- zombie_escape/world_grid.py,sha256=9ZKaur2fBOXiZEg5WlaAnoeHV6Lda092rJROraMW6zk,4553
22
- zombie_escape/zombie_escape.py,sha256=LiR2iG0bUPWFq7Fd3zCTy1V1roeXU7xnqDTDW_CZuss,8482
23
- zombie_escape/assets/fonts/Silkscreen-Regular.ttf,sha256=SVZ0CGAICeJRR-kiWsTzf0EOLfRadQaWxFAnUx-2Xxs,31960
24
- zombie_escape/assets/fonts/misaki_gothic.ttf,sha256=CWPhHonV-kCaegSKUujqLWI9dkp5mEiPikKRERYRxOE,1171204
25
- zombie_escape/gameplay/__init__.py,sha256=Q-6ifVss8Adnoo1hh9aUcN1fug6wXFVNkiFTWSHkazk,2387
26
- zombie_escape/gameplay/ambient.py,sha256=hoCOz6ciyejU0nmJwdLqmVfaoo-01CrVSMRLpFMz93w,1446
27
- zombie_escape/gameplay/constants.py,sha256=x_-b67fgvejZj00_lE-oJITcntb0Oi2ba2Zu9QyL3-0,1009
28
- zombie_escape/gameplay/footprints.py,sha256=WRN5wNIudqrQJ3vBljSbtwcdgn154f1jVdor4nEn_Ic,1995
29
- zombie_escape/gameplay/interactions.py,sha256=D503c0PBmTF4SapBblmKHJwWFr4TDqVZ2merDNek0_k,14407
30
- zombie_escape/gameplay/layout.py,sha256=5-2lYY81CUPmgPhoCppY4wAFkwnmZ-36ItZGIyigiR4,8786
31
- zombie_escape/gameplay/movement.py,sha256=6KSfTItQr8Jg5b_o5_3Dw_c9uAmjV9Cy72K87quUL6s,10330
32
- zombie_escape/gameplay/spawn.py,sha256=pGjPAXpw1zRGeRFMNla_nwRmmMxwGsKZaP1drg3mSqA,31921
33
- zombie_escape/gameplay/state.py,sha256=pu_Vh41IEC6B95cbXrSp1H1jD5r-mBGLIK7aX6GsL3U,4733
34
- zombie_escape/gameplay/survivors.py,sha256=BKWTvzMejP3sYpmVZGOLgp53DFGrMBpQ5RJDDueL8g0,12109
35
- zombie_escape/gameplay/utils.py,sha256=NkAynBpu4VHuRGbEBDbA214WK92yquS6E_i7Y--cBV0,6439
36
- zombie_escape/locales/ui.en.json,sha256=fHsEThNTKjVs3n1PEvdJLywxfk-HwZrHVAnnBPu5nZI,6505
37
- zombie_escape/locales/ui.ja.json,sha256=ryOHPyiGxn8vGeDR7Q2ueIVoP-mA6dS1yDMhejjpPiQ,7079
38
- zombie_escape/screens/__init__.py,sha256=BFLQzXqyrAhmm6b_2wlnK7lMm_n5HvBklVrzrJRwRn0,8387
39
- zombie_escape/screens/game_over.py,sha256=uxDYm_w7RTd-H2yx-Mm2M8GwqTuOFpkCis-zIEhjdUo,6716
40
- zombie_escape/screens/gameplay.py,sha256=-tzDcg9F8vNYyJhYk79LLaqNi-3X5BjoSTAc0kJ5V4U,16222
41
- zombie_escape/screens/settings.py,sha256=qgcnq8k-yeRbqweT9GsIefKWKvjOON_Gs2_SCZChY-8,21739
42
- zombie_escape/screens/title.py,sha256=1LUEMTmFLmJNL0TKTbnP2oRGWb6zUV28d8sQQo8i3Po,23118
43
- zombie_escape-1.12.3.dist-info/METADATA,sha256=HvCRIuV6kQGAP2TkI2SYDlkR37iAS1T3R2Tvr05vdMU,10864
44
- zombie_escape-1.12.3.dist-info/WHEEL,sha256=WLgqFyCfm_KASv4WHyYy0P3pM_m7J5L9k2skdKLirC8,87
45
- zombie_escape-1.12.3.dist-info/entry_points.txt,sha256=JprxC-vvkBJgsOp0WJnGBZRJ_ESjjmyS-nsPExeiLHU,49
46
- zombie_escape-1.12.3.dist-info/licenses/LICENSE.txt,sha256=q-cJYG_K766eXSxQ7txWcWQ6nS2OF6c3HTVLesHbesU,1104
47
- zombie_escape-1.12.3.dist-info/RECORD,,