zombie-escape 1.8.0__py3-none-any.whl → 1.10.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.
@@ -17,10 +17,11 @@ from ..entities_constants import (
17
17
  ZOMBIE_RADIUS,
18
18
  )
19
19
  from .constants import SURVIVOR_MESSAGE_DURATION_MS, SURVIVOR_SPEED_PENALTY_PER_PASSENGER
20
- from ..localization import translate as tr, translate_dict, translate_list
20
+ from ..localization import translate_dict, translate_list
21
21
  from ..models import GameData, ProgressState
22
22
  from ..rng import get_rng
23
- from ..entities import Survivor, Zombie, spritecollideany_walls, WallIndex
23
+ from ..entities import Survivor, Zombie, spritecollideany_walls
24
+ from ..world_grid import WallIndex
24
25
  from .spawn import _create_zombie
25
26
  from .utils import find_nearby_offscreen_spawn_position, rect_visible_on_screen
26
27
 
@@ -286,8 +287,8 @@ def handle_survivor_zombie_collisions(
286
287
  config,
287
288
  start_pos=survivor.rect.center,
288
289
  stage=game_data.stage,
289
- tracker=bool(getattr(collided_zombie, "tracker", False)),
290
- wall_follower=bool(getattr(collided_zombie, "wall_follower", False)),
290
+ tracker=collided_zombie.tracker,
291
+ wall_follower=collided_zombie.wall_follower,
291
292
  )
292
293
  zombie_group.add(new_zombie)
293
294
  game_data.groups.all_sprites.add(new_zombie, layer=1)
@@ -108,7 +108,9 @@ def find_nearby_offscreen_spawn_position(
108
108
  jitter_x = RNG.uniform(-cell.width * 0.35, cell.width * 0.35)
109
109
  jitter_y = RNG.uniform(-cell.height * 0.35, cell.height * 0.35)
110
110
  candidate = (int(cell.centerx + jitter_x), int(cell.centery + jitter_y))
111
- if player is not None and (min_distance_sq is not None or max_distance_sq is not None):
111
+ if player is not None and (
112
+ min_distance_sq is not None or max_distance_sq is not None
113
+ ):
112
114
  dx = candidate[0] - player.x
113
115
  dy = candidate[1] - player.y
114
116
  dist_sq = dx * dx + dy * dy
@@ -119,6 +121,22 @@ def find_nearby_offscreen_spawn_position(
119
121
  if view_rect is not None and view_rect.collidepoint(candidate):
120
122
  continue
121
123
  return candidate
124
+ if player is not None and (min_distance_sq is not None or max_distance_sq is not None):
125
+ for _ in range(20):
126
+ cell = RNG.choice(walkable_cells)
127
+ center = (cell.centerx, cell.centery)
128
+ if view_rect is not None and view_rect.collidepoint(center):
129
+ continue
130
+ dx = center[0] - player.x
131
+ dy = center[1] - player.y
132
+ dist_sq = dx * dx + dy * dy
133
+ if min_distance_sq is not None and dist_sq < min_distance_sq:
134
+ continue
135
+ if max_distance_sq is not None and dist_sq > max_distance_sq:
136
+ continue
137
+ fallback_x = RNG.uniform(-cell.width * 0.2, cell.width * 0.2)
138
+ fallback_y = RNG.uniform(-cell.height * 0.2, cell.height * 0.2)
139
+ return (int(cell.centerx + fallback_x), int(cell.centery + fallback_y))
122
140
  fallback_cell = RNG.choice(walkable_cells)
123
141
  fallback_x = RNG.uniform(-fallback_cell.width * 0.35, fallback_cell.width * 0.35)
124
142
  fallback_y = RNG.uniform(-fallback_cell.height * 0.35, fallback_cell.height * 0.35)
@@ -107,6 +107,18 @@
107
107
  "No, no, no!",
108
108
  "Please!"
109
109
  ]
110
+ },
111
+ "stage11": {
112
+ "name": "#11 Don't Look Back!",
113
+ "description": "Left or right. Trust your instinct."
114
+ },
115
+ "stage12": {
116
+ "name": "#12 OVERHEAD HAZARD",
117
+ "description": "An open factory floor. Zombies may fall from above."
118
+ },
119
+ "stage13": {
120
+ "name": "#13 Rescue Buddy 3",
121
+ "description": "Rescue your buddy. Zombies may fall from above."
110
122
  }
111
123
  },
112
124
  "status": {
@@ -122,7 +134,7 @@
122
134
  "need_fuel": "Need fuel to drive!",
123
135
  "paused": "PAUSED",
124
136
  "pause_hint": "P/Start: Resume · ESC/Select: Return to Title",
125
- "survival_timer_label": "Until dawn %{time}",
137
+ "endurance_timer_label": "Until dawn %{time}",
126
138
  "time_accel": ">> 4x",
127
139
  "time_accel_hint": "Hold Shift/R1: 4x"
128
140
  },
@@ -181,7 +193,7 @@
181
193
  "prompt": "ESC/SPACE/Select/South: Title · R: Retry",
182
194
  "scream": "AAAAHHH!!",
183
195
  "survivors_summary": "Evacuated: %{count}",
184
- "survival_duration": "Time survived %{time}"
196
+ "endurance_duration": "Time survived %{time}"
185
197
  }
186
198
  }
187
199
  }
@@ -107,6 +107,18 @@
107
107
  "やだ、やだ!",
108
108
  "誰かー!"
109
109
  ]
110
+ },
111
+ "stage11": {
112
+ "name": "#11 振り返るな!",
113
+ "description": "右か左か。直感を信じて。"
114
+ },
115
+ "stage12": {
116
+ "name": "#12 頭上注意",
117
+ "description": "吹き抜けの工場。落ちてくるゾンビあり。"
118
+ },
119
+ "stage13": {
120
+ "name": "#13 相棒を救え 3",
121
+ "description": "はぐれた相棒を救え。ゾンビの落下あり。"
110
122
  }
111
123
  },
112
124
  "status": {
@@ -122,7 +134,7 @@
122
134
  "need_fuel": "燃料が必要です!",
123
135
  "paused": "一時停止",
124
136
  "pause_hint": "P/Start: ゲーム再開 ESC/Select: タイトルに戻る",
125
- "survival_timer_label": "夜明けまであと %{time}",
137
+ "endurance_timer_label": "夜明けまであと %{time}",
126
138
  "time_accel": ">> 4x",
127
139
  "time_accel_hint": "Shift/R1押下: 4x"
128
140
  },
@@ -181,7 +193,7 @@
181
193
  "prompt": "ESC/Space/Select/South: タイトルへ R: 再挑戦",
182
194
  "scream": "ぎゃあーーー!!",
183
195
  "survivors_summary": "救出人数: %{count}",
184
- "survival_duration": "逃げ延びた時間 %{time}"
196
+ "endurance_duration": "逃げ延びた時間 %{time}"
185
197
  }
186
198
  }
187
199
  }
zombie_escape/models.py CHANGED
@@ -9,7 +9,11 @@ import pygame
9
9
  from pygame import sprite, surface
10
10
 
11
11
  from .entities_constants import ZOMBIE_AGING_DURATION_FRAMES
12
- from .gameplay_constants import SURVIVOR_SPAWN_RATE, ZOMBIE_SPAWN_DELAY_MS
12
+ from .gameplay_constants import (
13
+ DEFAULT_FLASHLIGHT_SPAWN_COUNT,
14
+ SURVIVOR_SPAWN_RATE,
15
+ ZOMBIE_SPAWN_DELAY_MS,
16
+ )
13
17
  from .level_constants import DEFAULT_GRID_COLS, DEFAULT_GRID_ROWS
14
18
  from .localization import translate as tr
15
19
 
@@ -27,9 +31,42 @@ class LevelLayout:
27
31
  walkable_cells: list[pygame.Rect]
28
32
  outer_wall_cells: set[tuple[int, int]]
29
33
  wall_cells: set[tuple[int, int]]
34
+ fall_spawn_cells: set[tuple[int, int]]
30
35
  bevel_corners: dict[tuple[int, int], tuple[bool, bool, bool, bool]]
31
36
 
32
37
 
38
+ @dataclass
39
+ class FallingZombie:
40
+ """Represents a zombie falling toward a target position."""
41
+
42
+ start_pos: tuple[int, int]
43
+ target_pos: tuple[int, int]
44
+ started_at_ms: int
45
+ pre_fx_ms: int
46
+ fall_duration_ms: int
47
+ dust_duration_ms: int
48
+ tracker: bool
49
+ wall_follower: bool
50
+ dust_started: bool = False
51
+
52
+
53
+ @dataclass
54
+ class DustRing:
55
+ """Short-lived dust ring spawned on impact."""
56
+
57
+ pos: tuple[int, int]
58
+ started_at_ms: int
59
+ duration_ms: int
60
+
61
+
62
+ @dataclass(frozen=True)
63
+ class Footprint:
64
+ """Tracked player footprint."""
65
+
66
+ pos: tuple[float, float]
67
+ time: int
68
+
69
+
33
70
  @dataclass
34
71
  class ProgressState:
35
72
  """Game progress/state flags."""
@@ -40,8 +77,8 @@ class ProgressState:
40
77
  game_over_at: int | None
41
78
  scaled_overview: surface.Surface | None
42
79
  overview_created: bool
43
- footprints: list
44
- last_footprint_pos: tuple | None
80
+ footprints: list[Footprint]
81
+ last_footprint_pos: tuple[float, float] | None
45
82
  elapsed_play_ms: int
46
83
  has_fuel: bool
47
84
  flashlight_count: int
@@ -56,14 +93,17 @@ class ProgressState:
56
93
  survivor_messages: list
57
94
  survivor_capacity: int
58
95
  seed: int | None
59
- survival_elapsed_ms: int
60
- survival_goal_ms: int
96
+ endurance_elapsed_ms: int
97
+ endurance_goal_ms: int
61
98
  dawn_ready: bool
62
99
  dawn_prompt_at: int | None
63
100
  time_accel_active: bool
64
101
  last_zombie_spawn_time: int
65
102
  dawn_carbonized: bool
66
103
  debug_mode: bool
104
+ falling_zombies: list[FallingZombie]
105
+ falling_spawn_carry: int
106
+ dust_rings: list[DustRing]
67
107
 
68
108
 
69
109
  @dataclass
@@ -109,14 +149,17 @@ class Stage:
109
149
  requires_fuel: bool = False
110
150
  buddy_required_count: int = 0
111
151
  rescue_stage: bool = False
112
- survival_stage: bool = False
113
- survival_goal_ms: int = 0
152
+ endurance_stage: bool = False
153
+ endurance_goal_ms: int = 0
114
154
  fuel_spawn_count: int = 1
155
+ initial_flashlight_count: int = DEFAULT_FLASHLIGHT_SPAWN_COUNT
115
156
  survivor_spawn_rate: float = SURVIVOR_SPAWN_RATE
116
157
  spawn_interval_ms: int = ZOMBIE_SPAWN_DELAY_MS
117
158
  initial_interior_spawn_rate: float = 0.015
118
159
  exterior_spawn_weight: float = 1.0
119
160
  interior_spawn_weight: float = 0.0
161
+ interior_fall_spawn_weight: float = 0.0
162
+ fall_spawn_zones: list[tuple[int, int, int, int]] = field(default_factory=list)
120
163
  zombie_tracker_ratio: float = 0.0
121
164
  zombie_wall_follower_ratio: float = 0.0
122
165
  zombie_normal_ratio: float = 1.0
@@ -135,6 +178,8 @@ class Stage:
135
178
 
136
179
  __all__ = [
137
180
  "LevelLayout",
181
+ "FallingZombie",
182
+ "DustRing",
138
183
  "ProgressState",
139
184
  "Groups",
140
185
  "GameData",