zombie-escape 1.13.1__py3-none-any.whl → 1.14.4__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 +7 -21
- zombie_escape/entities.py +100 -191
- zombie_escape/export_images.py +39 -33
- zombie_escape/gameplay/ambient.py +2 -6
- zombie_escape/gameplay/footprints.py +8 -11
- zombie_escape/gameplay/interactions.py +17 -58
- zombie_escape/gameplay/layout.py +20 -46
- zombie_escape/gameplay/movement.py +7 -21
- zombie_escape/gameplay/spawn.py +12 -40
- zombie_escape/gameplay/state.py +1 -0
- zombie_escape/gameplay/survivors.py +5 -16
- zombie_escape/gameplay/utils.py +4 -13
- zombie_escape/input_utils.py +8 -31
- zombie_escape/level_blueprints.py +112 -69
- zombie_escape/level_constants.py +8 -0
- zombie_escape/locales/ui.en.json +12 -0
- zombie_escape/locales/ui.ja.json +12 -0
- zombie_escape/localization.py +3 -11
- zombie_escape/models.py +26 -9
- zombie_escape/render/__init__.py +30 -0
- zombie_escape/render/core.py +992 -0
- zombie_escape/render/hud.py +444 -0
- zombie_escape/render/overview.py +218 -0
- zombie_escape/render/shadows.py +343 -0
- zombie_escape/render_assets.py +11 -33
- zombie_escape/rng.py +4 -8
- zombie_escape/screens/__init__.py +14 -30
- zombie_escape/screens/game_over.py +43 -15
- zombie_escape/screens/gameplay.py +41 -104
- zombie_escape/screens/settings.py +19 -104
- zombie_escape/screens/title.py +36 -176
- zombie_escape/stage_constants.py +192 -67
- zombie_escape/zombie_escape.py +1 -1
- {zombie_escape-1.13.1.dist-info → zombie_escape-1.14.4.dist-info}/METADATA +100 -39
- zombie_escape-1.14.4.dist-info/RECORD +53 -0
- zombie_escape/render.py +0 -1746
- zombie_escape-1.13.1.dist-info/RECORD +0 -49
- {zombie_escape-1.13.1.dist-info → zombie_escape-1.14.4.dist-info}/WHEEL +0 -0
- {zombie_escape-1.13.1.dist-info → zombie_escape-1.14.4.dist-info}/entry_points.txt +0 -0
- {zombie_escape-1.13.1.dist-info → zombie_escape-1.14.4.dist-info}/licenses/LICENSE.txt +0 -0
zombie_escape/entities.py
CHANGED
|
@@ -159,9 +159,7 @@ class Wall(pygame.sprite.Sprite):
|
|
|
159
159
|
self.draw_bottom_side = draw_bottom_side
|
|
160
160
|
self.bottom_side_ratio = max(0.0, bottom_side_ratio)
|
|
161
161
|
self.side_shade_ratio = max(0.0, min(1.0, side_shade_ratio))
|
|
162
|
-
self._local_polygon = _build_beveled_polygon(
|
|
163
|
-
safe_width, safe_height, self.bevel_depth, self.bevel_mask
|
|
164
|
-
)
|
|
162
|
+
self._local_polygon = _build_beveled_polygon(safe_width, safe_height, self.bevel_depth, self.bevel_mask)
|
|
165
163
|
self._update_color()
|
|
166
164
|
self.rect = self.image.get_rect(topleft=(x, y))
|
|
167
165
|
# Keep collision rectangular even when beveled visually.
|
|
@@ -205,18 +203,14 @@ class Wall(pygame.sprite.Sprite):
|
|
|
205
203
|
return self.rect.colliderect(rect_obj)
|
|
206
204
|
return _rect_polygon_collision(rect_obj, self._collision_polygon)
|
|
207
205
|
|
|
208
|
-
def _collides_circle(
|
|
209
|
-
self: Self, center: tuple[float, float], radius: float
|
|
210
|
-
) -> bool:
|
|
206
|
+
def _collides_circle(self: Self, center: tuple[float, float], radius: float) -> bool:
|
|
211
207
|
if not _circle_rect_collision(center, radius, self.rect):
|
|
212
208
|
return False
|
|
213
209
|
if self._collision_polygon is None:
|
|
214
210
|
return True
|
|
215
211
|
return _circle_polygon_collision(center, radius, self._collision_polygon)
|
|
216
212
|
|
|
217
|
-
def set_palette(
|
|
218
|
-
self: Self, palette: EnvironmentPalette | None, *, force: bool = False
|
|
219
|
-
) -> None:
|
|
213
|
+
def set_palette(self: Self, palette: EnvironmentPalette | None, *, force: bool = False) -> None:
|
|
220
214
|
"""Update the wall's palette to match the current ambient palette."""
|
|
221
215
|
|
|
222
216
|
if not force and self.palette is palette:
|
|
@@ -241,15 +235,9 @@ class RubbleWall(Wall):
|
|
|
241
235
|
rubble_offset_px: int | None = None,
|
|
242
236
|
on_destroy: Callable[[Self], None] | None = None,
|
|
243
237
|
) -> None:
|
|
244
|
-
self._rubble_rotation_deg =
|
|
245
|
-
RUBBLE_ROTATION_DEG if rubble_rotation_deg is None else rubble_rotation_deg
|
|
246
|
-
)
|
|
238
|
+
self._rubble_rotation_deg = RUBBLE_ROTATION_DEG if rubble_rotation_deg is None else rubble_rotation_deg
|
|
247
239
|
base_size = max(1, min(width, height))
|
|
248
|
-
self._rubble_offset_px = (
|
|
249
|
-
rubble_offset_for_size(base_size)
|
|
250
|
-
if rubble_offset_px is None
|
|
251
|
-
else rubble_offset_px
|
|
252
|
-
)
|
|
240
|
+
self._rubble_offset_px = rubble_offset_for_size(base_size) if rubble_offset_px is None else rubble_offset_px
|
|
253
241
|
super().__init__(
|
|
254
242
|
x,
|
|
255
243
|
y,
|
|
@@ -322,9 +310,7 @@ class SteelBeam(pygame.sprite.Sprite):
|
|
|
322
310
|
if self.health <= 0:
|
|
323
311
|
return
|
|
324
312
|
health_ratio = max(0, self.health / self.max_health)
|
|
325
|
-
base_color, line_color = resolve_steel_beam_colors(
|
|
326
|
-
health_ratio=health_ratio, palette=self.palette
|
|
327
|
-
)
|
|
313
|
+
base_color, line_color = resolve_steel_beam_colors(health_ratio=health_ratio, palette=self.palette)
|
|
328
314
|
paint_steel_beam_surface(
|
|
329
315
|
self.image,
|
|
330
316
|
base_color=base_color,
|
|
@@ -387,9 +373,7 @@ def _walls_for_sprite(
|
|
|
387
373
|
)
|
|
388
374
|
|
|
389
375
|
|
|
390
|
-
def _circle_rect_collision(
|
|
391
|
-
center: tuple[float, float], radius: float, rect_obj: rect.Rect
|
|
392
|
-
) -> bool:
|
|
376
|
+
def _circle_rect_collision(center: tuple[float, float], radius: float, rect_obj: rect.Rect) -> bool:
|
|
393
377
|
"""Return True if a circle overlaps the provided rectangle."""
|
|
394
378
|
cx, cy = center
|
|
395
379
|
closest_x = max(rect_obj.left, min(cx, rect_obj.right))
|
|
@@ -408,9 +392,7 @@ def _build_beveled_polygon(
|
|
|
408
392
|
return build_beveled_polygon(width, height, depth, bevels)
|
|
409
393
|
|
|
410
394
|
|
|
411
|
-
def _point_in_polygon(
|
|
412
|
-
point: tuple[float, float], polygon: Sequence[tuple[float, float]]
|
|
413
|
-
) -> bool:
|
|
395
|
+
def _point_in_polygon(point: tuple[float, float], polygon: Sequence[tuple[float, float]]) -> bool:
|
|
414
396
|
x, y = point
|
|
415
397
|
inside = False
|
|
416
398
|
count = len(polygon)
|
|
@@ -418,9 +400,7 @@ def _point_in_polygon(
|
|
|
418
400
|
for i in range(count):
|
|
419
401
|
xi, yi = polygon[i]
|
|
420
402
|
xj, yj = polygon[j]
|
|
421
|
-
intersects = (yi > y) != (yj > y) and (
|
|
422
|
-
x < (xj - xi) * (y - yi) / (yj - yi + 0.000001) + xi
|
|
423
|
-
)
|
|
403
|
+
intersects = (yi > y) != (yj > y) and (x < (xj - xi) * (y - yi) / (yj - yi + 0.000001) + xi)
|
|
424
404
|
if intersects:
|
|
425
405
|
inside = not inside
|
|
426
406
|
j = i
|
|
@@ -433,17 +413,11 @@ def _segments_intersect(
|
|
|
433
413
|
b1: tuple[float, float],
|
|
434
414
|
b2: tuple[float, float],
|
|
435
415
|
) -> bool:
|
|
436
|
-
def orient(
|
|
437
|
-
p: tuple[float, float], q: tuple[float, float], r: tuple[float, float]
|
|
438
|
-
) -> float:
|
|
416
|
+
def orient(p: tuple[float, float], q: tuple[float, float], r: tuple[float, float]) -> float:
|
|
439
417
|
return (q[0] - p[0]) * (r[1] - p[1]) - (q[1] - p[1]) * (r[0] - p[0])
|
|
440
418
|
|
|
441
|
-
def on_segment(
|
|
442
|
-
p
|
|
443
|
-
) -> bool:
|
|
444
|
-
return min(p[0], r[0]) <= q[0] <= max(p[0], r[0]) and min(p[1], r[1]) <= q[
|
|
445
|
-
1
|
|
446
|
-
] <= max(p[1], r[1])
|
|
419
|
+
def on_segment(p: tuple[float, float], q: tuple[float, float], r: tuple[float, float]) -> bool:
|
|
420
|
+
return min(p[0], r[0]) <= q[0] <= max(p[0], r[0]) and min(p[1], r[1]) <= q[1] <= max(p[1], r[1])
|
|
447
421
|
|
|
448
422
|
o1 = orient(a1, a2, b1)
|
|
449
423
|
o2 = orient(a1, a2, b2)
|
|
@@ -507,16 +481,12 @@ def _line_of_sight_clear(
|
|
|
507
481
|
return True
|
|
508
482
|
|
|
509
483
|
|
|
510
|
-
def _rect_polygon_collision(
|
|
511
|
-
rect_obj: rect.Rect, polygon: Sequence[tuple[float, float]]
|
|
512
|
-
) -> bool:
|
|
484
|
+
def _rect_polygon_collision(rect_obj: rect.Rect, polygon: Sequence[tuple[float, float]]) -> bool:
|
|
513
485
|
min_x = min(p[0] for p in polygon)
|
|
514
486
|
max_x = max(p[0] for p in polygon)
|
|
515
487
|
min_y = min(p[1] for p in polygon)
|
|
516
488
|
max_y = max(p[1] for p in polygon)
|
|
517
|
-
if not rect_obj.colliderect(
|
|
518
|
-
pygame.Rect(min_x, min_y, max_x - min_x, max_y - min_y)
|
|
519
|
-
):
|
|
489
|
+
if not rect_obj.colliderect(pygame.Rect(min_x, min_y, max_x - min_x, max_y - min_y)):
|
|
520
490
|
return False
|
|
521
491
|
|
|
522
492
|
rect_points = [
|
|
@@ -536,9 +506,7 @@ def _rect_polygon_collision(
|
|
|
536
506
|
(rect_points[2], rect_points[3]),
|
|
537
507
|
(rect_points[3], rect_points[0]),
|
|
538
508
|
]
|
|
539
|
-
poly_edges = [
|
|
540
|
-
(polygon[i], polygon[(i + 1) % len(polygon)]) for i in range(len(polygon))
|
|
541
|
-
]
|
|
509
|
+
poly_edges = [(polygon[i], polygon[(i + 1) % len(polygon)]) for i in range(len(polygon))]
|
|
542
510
|
for edge_a in rect_edges:
|
|
543
511
|
for edge_b in poly_edges:
|
|
544
512
|
if _segments_intersect(edge_a[0], edge_a[1], edge_b[0], edge_b[1]):
|
|
@@ -562,9 +530,7 @@ def _circle_polygon_collision(
|
|
|
562
530
|
return False
|
|
563
531
|
|
|
564
532
|
|
|
565
|
-
def _collide_sprite_wall(
|
|
566
|
-
sprite: pygame.sprite.Sprite, wall: pygame.sprite.Sprite
|
|
567
|
-
) -> bool:
|
|
533
|
+
def _collide_sprite_wall(sprite: pygame.sprite.Sprite, wall: pygame.sprite.Sprite) -> bool:
|
|
568
534
|
if hasattr(sprite, "radius"):
|
|
569
535
|
center = sprite.rect.center
|
|
570
536
|
radius = float(sprite.radius)
|
|
@@ -591,9 +557,7 @@ def _spritecollide_walls(
|
|
|
591
557
|
if wall_index is None:
|
|
592
558
|
return cast(
|
|
593
559
|
list[Wall],
|
|
594
|
-
pygame.sprite.spritecollide(
|
|
595
|
-
sprite, walls, dokill, collided=_collide_sprite_wall
|
|
596
|
-
),
|
|
560
|
+
pygame.sprite.spritecollide(sprite, walls, dokill, collided=_collide_sprite_wall),
|
|
597
561
|
)
|
|
598
562
|
if cell_size is None:
|
|
599
563
|
raise ValueError("cell_size is required when using wall_index")
|
|
@@ -625,9 +589,7 @@ def spritecollideany_walls(
|
|
|
625
589
|
if wall_index is None:
|
|
626
590
|
return cast(
|
|
627
591
|
Wall | None,
|
|
628
|
-
pygame.sprite.spritecollideany(
|
|
629
|
-
sprite, walls, collided=_collide_sprite_wall
|
|
630
|
-
),
|
|
592
|
+
pygame.sprite.spritecollideany(sprite, walls, collided=_collide_sprite_wall),
|
|
631
593
|
)
|
|
632
594
|
if cell_size is None:
|
|
633
595
|
raise ValueError("cell_size is required when using wall_index")
|
|
@@ -725,9 +687,7 @@ class Player(pygame.sprite.Sprite):
|
|
|
725
687
|
self.is_jumping = False
|
|
726
688
|
self._update_image_scale(1.0)
|
|
727
689
|
else:
|
|
728
|
-
self._update_image_scale(
|
|
729
|
-
_get_jump_scale(elapsed, self.jump_duration, JUMP_SCALE_MAX)
|
|
730
|
-
)
|
|
690
|
+
self._update_image_scale(_get_jump_scale(elapsed, self.jump_duration, JUMP_SCALE_MAX))
|
|
731
691
|
|
|
732
692
|
# Pre-calculate jump possibility based on actual movement vector
|
|
733
693
|
can_jump_now = (
|
|
@@ -735,9 +695,7 @@ class Player(pygame.sprite.Sprite):
|
|
|
735
695
|
and pitfall_cells
|
|
736
696
|
and cell_size
|
|
737
697
|
and walkable_cells
|
|
738
|
-
and _can_humanoid_jump(
|
|
739
|
-
self.x, self.y, dx, dy, PLAYER_JUMP_RANGE, cell_size, walkable_cells
|
|
740
|
-
)
|
|
698
|
+
and _can_humanoid_jump(self.x, self.y, dx, dy, PLAYER_JUMP_RANGE, cell_size, walkable_cells)
|
|
741
699
|
)
|
|
742
700
|
|
|
743
701
|
inner_wall_hit = False
|
|
@@ -833,9 +791,7 @@ class Player(pygame.sprite.Sprite):
|
|
|
833
791
|
self.image = base_img
|
|
834
792
|
else:
|
|
835
793
|
w, h = base_img.get_size()
|
|
836
|
-
self.image = pygame.transform.scale(
|
|
837
|
-
base_img, (int(w * scale), int(h * scale))
|
|
838
|
-
)
|
|
794
|
+
self.image = pygame.transform.scale(base_img, (int(w * scale), int(h * scale)))
|
|
839
795
|
old_center = self.rect.center
|
|
840
796
|
self.rect = self.image.get_rect(center=old_center)
|
|
841
797
|
|
|
@@ -929,8 +885,7 @@ class Survivor(pygame.sprite.Sprite):
|
|
|
929
885
|
wall_cells: set[tuple[int, int]] | None = None,
|
|
930
886
|
pitfall_cells: set[tuple[int, int]] | None = None,
|
|
931
887
|
walkable_cells: list[tuple[int, int]] | None = None,
|
|
932
|
-
bevel_corners: dict[tuple[int, int], tuple[bool, bool, bool, bool]]
|
|
933
|
-
| None = None,
|
|
888
|
+
bevel_corners: dict[tuple[int, int], tuple[bool, bool, bool, bool]] | None = None,
|
|
934
889
|
grid_cols: int | None = None,
|
|
935
890
|
grid_rows: int | None = None,
|
|
936
891
|
level_width: int | None = None,
|
|
@@ -947,9 +902,7 @@ class Survivor(pygame.sprite.Sprite):
|
|
|
947
902
|
self.is_jumping = False
|
|
948
903
|
self._update_image_scale(1.0)
|
|
949
904
|
else:
|
|
950
|
-
self._update_image_scale(
|
|
951
|
-
_get_jump_scale(elapsed, self.jump_duration, JUMP_SCALE_MAX)
|
|
952
|
-
)
|
|
905
|
+
self._update_image_scale(_get_jump_scale(elapsed, self.jump_duration, JUMP_SCALE_MAX))
|
|
953
906
|
|
|
954
907
|
if self.is_buddy:
|
|
955
908
|
if self.rescued or not self.following:
|
|
@@ -975,12 +928,7 @@ class Survivor(pygame.sprite.Sprite):
|
|
|
975
928
|
move_x = (dx / dist) * BUDDY_FOLLOW_SPEED
|
|
976
929
|
move_y = (dy / dist) * BUDDY_FOLLOW_SPEED
|
|
977
930
|
|
|
978
|
-
if
|
|
979
|
-
cell_size is not None
|
|
980
|
-
and wall_cells is not None
|
|
981
|
-
and grid_cols is not None
|
|
982
|
-
and grid_rows is not None
|
|
983
|
-
):
|
|
931
|
+
if cell_size is not None and wall_cells is not None and grid_cols is not None and grid_rows is not None:
|
|
984
932
|
move_x, move_y = apply_tile_edge_nudge(
|
|
985
933
|
self.x,
|
|
986
934
|
self.y,
|
|
@@ -1095,10 +1043,7 @@ class Survivor(pygame.sprite.Sprite):
|
|
|
1095
1043
|
dx = player_pos[0] - self.x
|
|
1096
1044
|
dy = player_pos[1] - self.y
|
|
1097
1045
|
dist_sq = dx * dx + dy * dy
|
|
1098
|
-
if
|
|
1099
|
-
dist_sq <= 0
|
|
1100
|
-
or dist_sq > SURVIVOR_APPROACH_RADIUS * SURVIVOR_APPROACH_RADIUS
|
|
1101
|
-
):
|
|
1046
|
+
if dist_sq <= 0 or dist_sq > SURVIVOR_APPROACH_RADIUS * SURVIVOR_APPROACH_RADIUS:
|
|
1102
1047
|
return
|
|
1103
1048
|
|
|
1104
1049
|
dist = math.sqrt(dist_sq)
|
|
@@ -1124,12 +1069,7 @@ class Survivor(pygame.sprite.Sprite):
|
|
|
1124
1069
|
)
|
|
1125
1070
|
)
|
|
1126
1071
|
|
|
1127
|
-
if
|
|
1128
|
-
cell_size is not None
|
|
1129
|
-
and wall_cells is not None
|
|
1130
|
-
and grid_cols is not None
|
|
1131
|
-
and grid_rows is not None
|
|
1132
|
-
):
|
|
1072
|
+
if cell_size is not None and wall_cells is not None and grid_cols is not None and grid_rows is not None:
|
|
1133
1073
|
move_x, move_y = apply_tile_edge_nudge(
|
|
1134
1074
|
self.x,
|
|
1135
1075
|
self.y,
|
|
@@ -1203,9 +1143,7 @@ class Survivor(pygame.sprite.Sprite):
|
|
|
1203
1143
|
self.image = base_img
|
|
1204
1144
|
else:
|
|
1205
1145
|
w, h = base_img.get_size()
|
|
1206
|
-
self.image = pygame.transform.scale(
|
|
1207
|
-
base_img, (int(w * scale), int(h * scale))
|
|
1208
|
-
)
|
|
1146
|
+
self.image = pygame.transform.scale(base_img, (int(w * scale), int(h * scale)))
|
|
1209
1147
|
old_center = self.rect.center
|
|
1210
1148
|
self.rect = self.image.get_rect(center=old_center)
|
|
1211
1149
|
|
|
@@ -1240,9 +1178,7 @@ class Survivor(pygame.sprite.Sprite):
|
|
|
1240
1178
|
self.rect = self.image.get_rect(center=center)
|
|
1241
1179
|
|
|
1242
1180
|
|
|
1243
|
-
def random_position_outside_building(
|
|
1244
|
-
level_width: int, level_height: int
|
|
1245
|
-
) -> tuple[int, int]:
|
|
1181
|
+
def random_position_outside_building(level_width: int, level_height: int) -> tuple[int, int]:
|
|
1246
1182
|
side = RNG.choice(["top", "bottom", "left", "right"])
|
|
1247
1183
|
margin = 0
|
|
1248
1184
|
if side == "top":
|
|
@@ -1266,6 +1202,7 @@ def _zombie_tracker_movement(
|
|
|
1266
1202
|
grid_cols: int,
|
|
1267
1203
|
grid_rows: int,
|
|
1268
1204
|
outer_wall_cells: set[tuple[int, int]] | None,
|
|
1205
|
+
pitfall_cells: set[tuple[int, int]] | None,
|
|
1269
1206
|
) -> tuple[float, float]:
|
|
1270
1207
|
is_in_sight = zombie._update_mode(player_center, ZOMBIE_TRACKER_SIGHT_RANGE)
|
|
1271
1208
|
if not is_in_sight:
|
|
@@ -1273,9 +1210,7 @@ def _zombie_tracker_movement(
|
|
|
1273
1210
|
last_target_time = zombie.tracker_target_time
|
|
1274
1211
|
if last_target_time is None:
|
|
1275
1212
|
last_target_time = pygame.time.get_ticks()
|
|
1276
|
-
zombie.tracker_relock_after_time =
|
|
1277
|
-
last_target_time + ZOMBIE_TRACKER_RELOCK_DELAY_MS
|
|
1278
|
-
)
|
|
1213
|
+
zombie.tracker_relock_after_time = last_target_time + ZOMBIE_TRACKER_RELOCK_DELAY_MS
|
|
1279
1214
|
zombie.tracker_target_pos = None
|
|
1280
1215
|
return _zombie_wander_move(
|
|
1281
1216
|
zombie,
|
|
@@ -1284,6 +1219,7 @@ def _zombie_tracker_movement(
|
|
|
1284
1219
|
grid_cols=grid_cols,
|
|
1285
1220
|
grid_rows=grid_rows,
|
|
1286
1221
|
outer_wall_cells=outer_wall_cells,
|
|
1222
|
+
pitfall_cells=pitfall_cells,
|
|
1287
1223
|
)
|
|
1288
1224
|
_zombie_update_tracker_target(zombie, footprints, walls)
|
|
1289
1225
|
if zombie.tracker_target_pos is not None:
|
|
@@ -1295,6 +1231,7 @@ def _zombie_tracker_movement(
|
|
|
1295
1231
|
grid_cols=grid_cols,
|
|
1296
1232
|
grid_rows=grid_rows,
|
|
1297
1233
|
outer_wall_cells=outer_wall_cells,
|
|
1234
|
+
pitfall_cells=pitfall_cells,
|
|
1298
1235
|
)
|
|
1299
1236
|
return _zombie_move_toward(zombie, player_center)
|
|
1300
1237
|
|
|
@@ -1309,6 +1246,7 @@ def _zombie_wander_movement(
|
|
|
1309
1246
|
grid_cols: int,
|
|
1310
1247
|
grid_rows: int,
|
|
1311
1248
|
outer_wall_cells: set[tuple[int, int]] | None,
|
|
1249
|
+
pitfall_cells: set[tuple[int, int]] | None,
|
|
1312
1250
|
) -> tuple[float, float]:
|
|
1313
1251
|
return _zombie_wander_move(
|
|
1314
1252
|
zombie,
|
|
@@ -1317,6 +1255,7 @@ def _zombie_wander_movement(
|
|
|
1317
1255
|
grid_cols=grid_cols,
|
|
1318
1256
|
grid_rows=grid_rows,
|
|
1319
1257
|
outer_wall_cells=outer_wall_cells,
|
|
1258
|
+
pitfall_cells=pitfall_cells,
|
|
1320
1259
|
)
|
|
1321
1260
|
|
|
1322
1261
|
|
|
@@ -1329,15 +1268,9 @@ def _zombie_wall_hug_has_wall(
|
|
|
1329
1268
|
check_x = zombie.x + math.cos(angle) * distance
|
|
1330
1269
|
check_y = zombie.y + math.sin(angle) * distance
|
|
1331
1270
|
candidates = [
|
|
1332
|
-
wall
|
|
1333
|
-
for wall in walls
|
|
1334
|
-
if abs(wall.rect.centerx - check_x) < 120
|
|
1335
|
-
and abs(wall.rect.centery - check_y) < 120
|
|
1271
|
+
wall for wall in walls if abs(wall.rect.centerx - check_x) < 120 and abs(wall.rect.centery - check_y) < 120
|
|
1336
1272
|
]
|
|
1337
|
-
return any(
|
|
1338
|
-
_circle_wall_collision((check_x, check_y), zombie.radius, wall)
|
|
1339
|
-
for wall in candidates
|
|
1340
|
-
)
|
|
1273
|
+
return any(_circle_wall_collision((check_x, check_y), zombie.radius, wall) for wall in candidates)
|
|
1341
1274
|
|
|
1342
1275
|
|
|
1343
1276
|
def _zombie_wall_hug_wall_distance(
|
|
@@ -1354,8 +1287,7 @@ def _zombie_wall_hug_wall_distance(
|
|
|
1354
1287
|
candidates = [
|
|
1355
1288
|
wall
|
|
1356
1289
|
for wall in walls
|
|
1357
|
-
if abs(wall.rect.centerx - zombie.x) < max_search
|
|
1358
|
-
and abs(wall.rect.centery - zombie.y) < max_search
|
|
1290
|
+
if abs(wall.rect.centerx - zombie.x) < max_search and abs(wall.rect.centery - zombie.y) < max_search
|
|
1359
1291
|
]
|
|
1360
1292
|
if not candidates:
|
|
1361
1293
|
return max_distance
|
|
@@ -1363,10 +1295,7 @@ def _zombie_wall_hug_wall_distance(
|
|
|
1363
1295
|
while distance <= max_distance:
|
|
1364
1296
|
check_x = zombie.x + direction_x * distance
|
|
1365
1297
|
check_y = zombie.y + direction_y * distance
|
|
1366
|
-
if any(
|
|
1367
|
-
_circle_wall_collision((check_x, check_y), zombie.radius, wall)
|
|
1368
|
-
for wall in candidates
|
|
1369
|
-
):
|
|
1298
|
+
if any(_circle_wall_collision((check_x, check_y), zombie.radius, wall) for wall in candidates):
|
|
1370
1299
|
return distance
|
|
1371
1300
|
distance += step
|
|
1372
1301
|
return max_distance
|
|
@@ -1382,6 +1311,7 @@ def _zombie_wall_hug_movement(
|
|
|
1382
1311
|
grid_cols: int,
|
|
1383
1312
|
grid_rows: int,
|
|
1384
1313
|
outer_wall_cells: set[tuple[int, int]] | None,
|
|
1314
|
+
pitfall_cells: set[tuple[int, int]] | None,
|
|
1385
1315
|
) -> tuple[float, float]:
|
|
1386
1316
|
is_in_sight = zombie._update_mode(player_center, ZOMBIE_TRACKER_SIGHT_RANGE)
|
|
1387
1317
|
if zombie.wall_hug_angle is None:
|
|
@@ -1392,15 +1322,9 @@ def _zombie_wall_hug_movement(
|
|
|
1392
1322
|
probe_offset = math.radians(ZOMBIE_WALL_HUG_PROBE_ANGLE_DEG)
|
|
1393
1323
|
left_angle = forward_angle + probe_offset
|
|
1394
1324
|
right_angle = forward_angle - probe_offset
|
|
1395
|
-
left_dist = _zombie_wall_hug_wall_distance(
|
|
1396
|
-
|
|
1397
|
-
)
|
|
1398
|
-
right_dist = _zombie_wall_hug_wall_distance(
|
|
1399
|
-
zombie, walls, right_angle, sensor_distance
|
|
1400
|
-
)
|
|
1401
|
-
forward_dist = _zombie_wall_hug_wall_distance(
|
|
1402
|
-
zombie, walls, forward_angle, sensor_distance
|
|
1403
|
-
)
|
|
1325
|
+
left_dist = _zombie_wall_hug_wall_distance(zombie, walls, left_angle, sensor_distance)
|
|
1326
|
+
right_dist = _zombie_wall_hug_wall_distance(zombie, walls, right_angle, sensor_distance)
|
|
1327
|
+
forward_dist = _zombie_wall_hug_wall_distance(zombie, walls, forward_angle, sensor_distance)
|
|
1404
1328
|
left_wall = left_dist < sensor_distance
|
|
1405
1329
|
right_wall = right_dist < sensor_distance
|
|
1406
1330
|
forward_wall = forward_dist < sensor_distance
|
|
@@ -1425,17 +1349,14 @@ def _zombie_wall_hug_movement(
|
|
|
1425
1349
|
grid_cols=grid_cols,
|
|
1426
1350
|
grid_rows=grid_rows,
|
|
1427
1351
|
outer_wall_cells=outer_wall_cells,
|
|
1352
|
+
pitfall_cells=pitfall_cells,
|
|
1428
1353
|
)
|
|
1429
1354
|
|
|
1430
1355
|
sensor_distance = ZOMBIE_WALL_HUG_SENSOR_DISTANCE + zombie.radius
|
|
1431
1356
|
probe_offset = math.radians(ZOMBIE_WALL_HUG_PROBE_ANGLE_DEG)
|
|
1432
1357
|
side_angle = zombie.wall_hug_angle + zombie.wall_hug_side * probe_offset
|
|
1433
|
-
side_dist = _zombie_wall_hug_wall_distance(
|
|
1434
|
-
|
|
1435
|
-
)
|
|
1436
|
-
forward_dist = _zombie_wall_hug_wall_distance(
|
|
1437
|
-
zombie, walls, zombie.wall_hug_angle, sensor_distance
|
|
1438
|
-
)
|
|
1358
|
+
side_dist = _zombie_wall_hug_wall_distance(zombie, walls, side_angle, sensor_distance)
|
|
1359
|
+
forward_dist = _zombie_wall_hug_wall_distance(zombie, walls, zombie.wall_hug_angle, sensor_distance)
|
|
1439
1360
|
side_has_wall = side_dist < sensor_distance
|
|
1440
1361
|
forward_has_wall = forward_dist < sensor_distance
|
|
1441
1362
|
now = pygame.time.get_ticks()
|
|
@@ -1487,6 +1408,7 @@ def _zombie_normal_movement(
|
|
|
1487
1408
|
grid_cols: int,
|
|
1488
1409
|
grid_rows: int,
|
|
1489
1410
|
outer_wall_cells: set[tuple[int, int]] | None,
|
|
1411
|
+
pitfall_cells: set[tuple[int, int]] | None,
|
|
1490
1412
|
) -> tuple[float, float]:
|
|
1491
1413
|
is_in_sight = zombie._update_mode(player_center, ZOMBIE_SIGHT_RANGE)
|
|
1492
1414
|
if not is_in_sight:
|
|
@@ -1497,6 +1419,7 @@ def _zombie_normal_movement(
|
|
|
1497
1419
|
grid_cols=grid_cols,
|
|
1498
1420
|
grid_rows=grid_rows,
|
|
1499
1421
|
outer_wall_cells=outer_wall_cells,
|
|
1422
|
+
pitfall_cells=pitfall_cells,
|
|
1500
1423
|
)
|
|
1501
1424
|
return _zombie_move_toward(zombie, player_center)
|
|
1502
1425
|
|
|
@@ -1525,12 +1448,9 @@ def _zombie_update_tracker_target(
|
|
|
1525
1448
|
if latest_fp_time is None or fp.time > latest_fp_time:
|
|
1526
1449
|
latest_fp_time = fp.time
|
|
1527
1450
|
use_far_scan = last_target_time is None or (
|
|
1528
|
-
latest_fp_time is not None
|
|
1529
|
-
and latest_fp_time - last_target_time >= ZOMBIE_TRACKER_NEWER_FOOTPRINT_MS
|
|
1530
|
-
)
|
|
1531
|
-
scan_radius = (
|
|
1532
|
-
ZOMBIE_TRACKER_FAR_SCENT_RADIUS if use_far_scan else ZOMBIE_TRACKER_SCENT_RADIUS
|
|
1451
|
+
latest_fp_time is not None and latest_fp_time - last_target_time >= ZOMBIE_TRACKER_NEWER_FOOTPRINT_MS
|
|
1533
1452
|
)
|
|
1453
|
+
scan_radius = ZOMBIE_TRACKER_FAR_SCENT_RADIUS if use_far_scan else ZOMBIE_TRACKER_SCENT_RADIUS
|
|
1534
1454
|
scent_radius_sq = scan_radius * scan_radius
|
|
1535
1455
|
min_target_dist_sq = (FOOTPRINT_STEP_DISTANCE * 0.5) ** 2
|
|
1536
1456
|
for fp in footprints:
|
|
@@ -1548,13 +1468,23 @@ def _zombie_update_tracker_target(
|
|
|
1548
1468
|
if not nearby:
|
|
1549
1469
|
return
|
|
1550
1470
|
|
|
1551
|
-
nearby.sort(key=lambda fp: fp.time
|
|
1471
|
+
nearby.sort(key=lambda fp: fp.time)
|
|
1552
1472
|
if last_target_time is not None:
|
|
1553
1473
|
newer = [fp for fp in nearby if fp.time > last_target_time]
|
|
1554
1474
|
else:
|
|
1555
1475
|
newer = nearby
|
|
1556
1476
|
|
|
1557
|
-
|
|
1477
|
+
if use_far_scan or last_target_time is None:
|
|
1478
|
+
candidates = list(reversed(newer))[:ZOMBIE_TRACKER_SCENT_TOP_K]
|
|
1479
|
+
else:
|
|
1480
|
+
newer_threshold = last_target_time + ZOMBIE_TRACKER_NEWER_FOOTPRINT_MS
|
|
1481
|
+
very_new = [fp for fp in newer if fp.time >= newer_threshold]
|
|
1482
|
+
if very_new:
|
|
1483
|
+
candidates = list(reversed(very_new))[:ZOMBIE_TRACKER_SCENT_TOP_K]
|
|
1484
|
+
else:
|
|
1485
|
+
candidates = newer[:ZOMBIE_TRACKER_SCENT_TOP_K]
|
|
1486
|
+
|
|
1487
|
+
for fp in candidates:
|
|
1558
1488
|
pos = fp.pos
|
|
1559
1489
|
fp_time = fp.time
|
|
1560
1490
|
if _line_of_sight_clear((zombie.x, zombie.y), pos, walls):
|
|
@@ -1566,8 +1496,7 @@ def _zombie_update_tracker_target(
|
|
|
1566
1496
|
|
|
1567
1497
|
if (
|
|
1568
1498
|
zombie.tracker_target_pos is not None
|
|
1569
|
-
and (zombie.x - zombie.tracker_target_pos[0]) ** 2
|
|
1570
|
-
+ (zombie.y - zombie.tracker_target_pos[1]) ** 2
|
|
1499
|
+
and (zombie.x - zombie.tracker_target_pos[0]) ** 2 + (zombie.y - zombie.tracker_target_pos[1]) ** 2
|
|
1571
1500
|
> min_target_dist_sq
|
|
1572
1501
|
):
|
|
1573
1502
|
return
|
|
@@ -1629,6 +1558,7 @@ def _zombie_wander_move(
|
|
|
1629
1558
|
grid_cols: int,
|
|
1630
1559
|
grid_rows: int,
|
|
1631
1560
|
outer_wall_cells: set[tuple[int, int]] | None,
|
|
1561
|
+
pitfall_cells: set[tuple[int, int]] | None,
|
|
1632
1562
|
) -> tuple[float, float]:
|
|
1633
1563
|
now = pygame.time.get_ticks()
|
|
1634
1564
|
changed_angle = False
|
|
@@ -1676,13 +1606,9 @@ def _zombie_wander_move(
|
|
|
1676
1606
|
nearby_walls = [
|
|
1677
1607
|
wall
|
|
1678
1608
|
for wall in walls
|
|
1679
|
-
if abs(wall.rect.centerx - next_x) < 120
|
|
1680
|
-
and abs(wall.rect.centery - next_y) < 120
|
|
1609
|
+
if abs(wall.rect.centerx - next_x) < 120 and abs(wall.rect.centery - next_y) < 120
|
|
1681
1610
|
]
|
|
1682
|
-
return not any(
|
|
1683
|
-
_circle_wall_collision((next_x, next_y), zombie.radius, wall)
|
|
1684
|
-
for wall in nearby_walls
|
|
1685
|
-
)
|
|
1611
|
+
return not any(_circle_wall_collision((next_x, next_y), zombie.radius, wall) for wall in nearby_walls)
|
|
1686
1612
|
|
|
1687
1613
|
if at_x_edge:
|
|
1688
1614
|
inward_dx = zombie.speed if cell_x == 0 else -zombie.speed
|
|
@@ -1695,12 +1621,30 @@ def _zombie_wander_move(
|
|
|
1695
1621
|
|
|
1696
1622
|
move_x = math.cos(zombie.wander_angle) * zombie.speed
|
|
1697
1623
|
move_y = math.sin(zombie.wander_angle) * zombie.speed
|
|
1624
|
+
if pitfall_cells is not None:
|
|
1625
|
+
avoid_x, avoid_y = zombie._avoid_pitfalls(pitfall_cells, cell_size)
|
|
1626
|
+
move_x += avoid_x
|
|
1627
|
+
move_y += avoid_y
|
|
1628
|
+
if cell_size > 0:
|
|
1629
|
+
next_x = zombie.x + move_x
|
|
1630
|
+
next_y = zombie.y + move_y
|
|
1631
|
+
next_cell = (int(next_x // cell_size), int(next_y // cell_size))
|
|
1632
|
+
if next_cell in pitfall_cells:
|
|
1633
|
+
zombie.wander_angle = (zombie.wander_angle + math.pi) % math.tau
|
|
1634
|
+
move_x = math.cos(zombie.wander_angle) * zombie.speed
|
|
1635
|
+
move_y = math.sin(zombie.wander_angle) * zombie.speed
|
|
1636
|
+
avoid_x, avoid_y = zombie._avoid_pitfalls(pitfall_cells, cell_size)
|
|
1637
|
+
move_x += avoid_x
|
|
1638
|
+
move_y += avoid_y
|
|
1639
|
+
next_x = zombie.x + move_x
|
|
1640
|
+
next_y = zombie.y + move_y
|
|
1641
|
+
next_cell = (int(next_x // cell_size), int(next_y // cell_size))
|
|
1642
|
+
if next_cell in pitfall_cells:
|
|
1643
|
+
return 0.0, 0.0
|
|
1698
1644
|
return move_x, move_y
|
|
1699
1645
|
|
|
1700
1646
|
|
|
1701
|
-
def _zombie_move_toward(
|
|
1702
|
-
zombie: Zombie, target: tuple[float, float]
|
|
1703
|
-
) -> tuple[float, float]:
|
|
1647
|
+
def _zombie_move_toward(zombie: Zombie, target: tuple[float, float]) -> tuple[float, float]:
|
|
1704
1648
|
dx = target[0] - zombie.x
|
|
1705
1649
|
dy = target[1] - zombie.y
|
|
1706
1650
|
dist = math.hypot(dx, dy)
|
|
@@ -1763,19 +1707,13 @@ class Zombie(pygame.sprite.Sprite):
|
|
|
1763
1707
|
self.wall_hug_stuck_flag = False
|
|
1764
1708
|
self.pos_history: list[tuple[float, float]] = []
|
|
1765
1709
|
self.wander_angle = RNG.uniform(0, math.tau)
|
|
1766
|
-
self.wander_interval_ms =
|
|
1767
|
-
ZOMBIE_TRACKER_WANDER_INTERVAL_MS if tracker else ZOMBIE_WANDER_INTERVAL_MS
|
|
1768
|
-
)
|
|
1710
|
+
self.wander_interval_ms = ZOMBIE_TRACKER_WANDER_INTERVAL_MS if tracker else ZOMBIE_WANDER_INTERVAL_MS
|
|
1769
1711
|
self.last_wander_change_time = pygame.time.get_ticks()
|
|
1770
|
-
self.wander_change_interval = max(
|
|
1771
|
-
0, self.wander_interval_ms + RNG.randint(-500, 500)
|
|
1772
|
-
)
|
|
1712
|
+
self.wander_change_interval = max(0, self.wander_interval_ms + RNG.randint(-500, 500))
|
|
1773
1713
|
self.last_move_dx = 0.0
|
|
1774
1714
|
self.last_move_dy = 0.0
|
|
1775
1715
|
|
|
1776
|
-
def _update_mode(
|
|
1777
|
-
self: Self, player_center: tuple[int, int], sight_range: float
|
|
1778
|
-
) -> bool:
|
|
1716
|
+
def _update_mode(self: Self, player_center: tuple[int, int], sight_range: float) -> bool:
|
|
1779
1717
|
dx_target = player_center[0] - self.x
|
|
1780
1718
|
dy_target = player_center[1] - self.y
|
|
1781
1719
|
dist_to_player_sq = dx_target * dx_target + dy_target * dy_target
|
|
@@ -1783,16 +1721,10 @@ class Zombie(pygame.sprite.Sprite):
|
|
|
1783
1721
|
self.was_in_sight = is_in_sight
|
|
1784
1722
|
return is_in_sight
|
|
1785
1723
|
|
|
1786
|
-
def _handle_wall_collision(
|
|
1787
|
-
self: Self, next_x: float, next_y: float, walls: list[Wall]
|
|
1788
|
-
) -> tuple[float, float]:
|
|
1724
|
+
def _handle_wall_collision(self: Self, next_x: float, next_y: float, walls: list[Wall]) -> tuple[float, float]:
|
|
1789
1725
|
final_x, final_y = next_x, next_y
|
|
1790
1726
|
|
|
1791
|
-
possible_walls = [
|
|
1792
|
-
w
|
|
1793
|
-
for w in walls
|
|
1794
|
-
if abs(w.rect.centerx - self.x) < 100 and abs(w.rect.centery - self.y) < 100
|
|
1795
|
-
]
|
|
1727
|
+
possible_walls = [w for w in walls if abs(w.rect.centerx - self.x) < 100 and abs(w.rect.centery - self.y) < 100]
|
|
1796
1728
|
|
|
1797
1729
|
for wall in possible_walls:
|
|
1798
1730
|
collides = _circle_wall_collision((next_x, self.y), self.radius, wall)
|
|
@@ -1831,10 +1763,7 @@ class Zombie(pygame.sprite.Sprite):
|
|
|
1831
1763
|
continue
|
|
1832
1764
|
dx = other.x - next_x
|
|
1833
1765
|
dy = other.y - next_y
|
|
1834
|
-
if (
|
|
1835
|
-
abs(dx) > ZOMBIE_SEPARATION_DISTANCE
|
|
1836
|
-
or abs(dy) > ZOMBIE_SEPARATION_DISTANCE
|
|
1837
|
-
):
|
|
1766
|
+
if abs(dx) > ZOMBIE_SEPARATION_DISTANCE or abs(dy) > ZOMBIE_SEPARATION_DISTANCE:
|
|
1838
1767
|
continue
|
|
1839
1768
|
dist_sq = dx * dx + dy * dy
|
|
1840
1769
|
if dist_sq < closest_dist_sq:
|
|
@@ -1896,9 +1825,7 @@ class Zombie(pygame.sprite.Sprite):
|
|
|
1896
1825
|
def _apply_render_overlays(self: Self) -> None:
|
|
1897
1826
|
base_surface = self.directional_images[self.facing_bin]
|
|
1898
1827
|
needs_overlay = self.tracker or (
|
|
1899
|
-
self.wall_hugging
|
|
1900
|
-
and self.wall_hug_side != 0
|
|
1901
|
-
and self.wall_hug_last_side_has_wall
|
|
1828
|
+
self.wall_hugging and self.wall_hug_side != 0 and self.wall_hug_last_side_has_wall
|
|
1902
1829
|
)
|
|
1903
1830
|
if not needs_overlay:
|
|
1904
1831
|
self.image = base_surface
|
|
@@ -1912,11 +1839,7 @@ class Zombie(pygame.sprite.Sprite):
|
|
|
1912
1839
|
angle_rad=angle_rad,
|
|
1913
1840
|
color=ZOMBIE_NOSE_COLOR,
|
|
1914
1841
|
)
|
|
1915
|
-
if
|
|
1916
|
-
self.wall_hugging
|
|
1917
|
-
and self.wall_hug_side != 0
|
|
1918
|
-
and self.wall_hug_last_side_has_wall
|
|
1919
|
-
):
|
|
1842
|
+
if self.wall_hugging and self.wall_hug_side != 0 and self.wall_hug_last_side_has_wall:
|
|
1920
1843
|
side_sign = 1.0 if self.wall_hug_side > 0 else -1.0
|
|
1921
1844
|
hand_angle = angle_rad + side_sign * (math.pi / 2.0)
|
|
1922
1845
|
draw_humanoid_hand(
|
|
@@ -1931,9 +1854,7 @@ class Zombie(pygame.sprite.Sprite):
|
|
|
1931
1854
|
history.append((self.x, self.y))
|
|
1932
1855
|
if len(history) > 20:
|
|
1933
1856
|
history.pop(0)
|
|
1934
|
-
max_dist_sq = max(
|
|
1935
|
-
(self.x - hx) ** 2 + (self.y - hy) ** 2 for hx, hy in history
|
|
1936
|
-
)
|
|
1857
|
+
max_dist_sq = max((self.x - hx) ** 2 + (self.y - hy) ** 2 for hx, hy in history)
|
|
1937
1858
|
self.wall_hug_stuck_flag = max_dist_sq < 25
|
|
1938
1859
|
if not self.wall_hug_stuck_flag:
|
|
1939
1860
|
return
|
|
@@ -1993,8 +1914,7 @@ class Zombie(pygame.sprite.Sprite):
|
|
|
1993
1914
|
outer_wall_cells: set[tuple[int, int]] | None = None,
|
|
1994
1915
|
wall_cells: set[tuple[int, int]] | None = None,
|
|
1995
1916
|
pitfall_cells: set[tuple[int, int]] | None = None,
|
|
1996
|
-
bevel_corners: dict[tuple[int, int], tuple[bool, bool, bool, bool]]
|
|
1997
|
-
| None = None,
|
|
1917
|
+
bevel_corners: dict[tuple[int, int], tuple[bool, bool, bool, bool]] | None = None,
|
|
1998
1918
|
) -> None:
|
|
1999
1919
|
if self.carbonized:
|
|
2000
1920
|
return
|
|
@@ -2015,11 +1935,8 @@ class Zombie(pygame.sprite.Sprite):
|
|
|
2015
1935
|
grid_cols,
|
|
2016
1936
|
grid_rows,
|
|
2017
1937
|
outer_wall_cells,
|
|
1938
|
+
pitfall_cells,
|
|
2018
1939
|
)
|
|
2019
|
-
if pitfall_cells is not None:
|
|
2020
|
-
avoid_x, avoid_y = self._avoid_pitfalls(pitfall_cells, cell_size)
|
|
2021
|
-
move_x += avoid_x
|
|
2022
|
-
move_y += avoid_y
|
|
2023
1940
|
if dist_to_player_sq <= avoid_radius_sq or self.wall_hugging:
|
|
2024
1941
|
move_x, move_y = self._avoid_other_zombies(move_x, move_y, nearby_zombies)
|
|
2025
1942
|
if wall_cells is not None:
|
|
@@ -2038,9 +1955,7 @@ class Zombie(pygame.sprite.Sprite):
|
|
|
2038
1955
|
self._apply_render_overlays()
|
|
2039
1956
|
self.last_move_dx = move_x
|
|
2040
1957
|
self.last_move_dy = move_y
|
|
2041
|
-
final_x, final_y = self._handle_wall_collision(
|
|
2042
|
-
self.x + move_x, self.y + move_y, walls
|
|
2043
|
-
)
|
|
1958
|
+
final_x, final_y = self._handle_wall_collision(self.x + move_x, self.y + move_y, walls)
|
|
2044
1959
|
|
|
2045
1960
|
if not (0 <= final_x < level_width and 0 <= final_y < level_height):
|
|
2046
1961
|
self.kill()
|
|
@@ -2146,10 +2061,7 @@ class Car(pygame.sprite.Sprite):
|
|
|
2146
2061
|
possible_walls = list(walls)
|
|
2147
2062
|
else:
|
|
2148
2063
|
possible_walls = [
|
|
2149
|
-
w
|
|
2150
|
-
for w in walls
|
|
2151
|
-
if abs(w.rect.centery - self.y) < 100
|
|
2152
|
-
and abs(w.rect.centerx - new_x) < 100
|
|
2064
|
+
w for w in walls if abs(w.rect.centery - self.y) < 100 and abs(w.rect.centerx - new_x) < 100
|
|
2153
2065
|
]
|
|
2154
2066
|
car_center = (new_x, new_y)
|
|
2155
2067
|
for wall in possible_walls:
|
|
@@ -2165,10 +2077,7 @@ class Car(pygame.sprite.Sprite):
|
|
|
2165
2077
|
if hit_walls or in_pitfall:
|
|
2166
2078
|
if hit_walls:
|
|
2167
2079
|
self._take_damage(CAR_WALL_DAMAGE)
|
|
2168
|
-
hit_walls.sort(
|
|
2169
|
-
key=lambda w: (w.rect.centery - self.y) ** 2
|
|
2170
|
-
+ (w.rect.centerx - self.x) ** 2
|
|
2171
|
-
)
|
|
2080
|
+
hit_walls.sort(key=lambda w: (w.rect.centery - self.y) ** 2 + (w.rect.centerx - self.x) ** 2)
|
|
2172
2081
|
nearest_wall = hit_walls[0]
|
|
2173
2082
|
new_x += (self.x - nearest_wall.rect.centerx) * 1.2
|
|
2174
2083
|
new_y += (self.y - nearest_wall.rect.centery) * 1.2
|