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.
Files changed (37) hide show
  1. zombie_escape/__about__.py +1 -1
  2. zombie_escape/entities.py +501 -537
  3. zombie_escape/entities_constants.py +102 -0
  4. zombie_escape/gameplay/__init__.py +75 -2
  5. zombie_escape/gameplay/ambient.py +50 -0
  6. zombie_escape/gameplay/constants.py +46 -0
  7. zombie_escape/gameplay/footprints.py +60 -0
  8. zombie_escape/gameplay/interactions.py +354 -0
  9. zombie_escape/gameplay/layout.py +190 -0
  10. zombie_escape/gameplay/movement.py +220 -0
  11. zombie_escape/gameplay/spawn.py +618 -0
  12. zombie_escape/gameplay/state.py +137 -0
  13. zombie_escape/gameplay/survivors.py +306 -0
  14. zombie_escape/gameplay/utils.py +147 -0
  15. zombie_escape/gameplay_constants.py +0 -148
  16. zombie_escape/level_blueprints.py +123 -10
  17. zombie_escape/level_constants.py +6 -13
  18. zombie_escape/locales/ui.en.json +10 -1
  19. zombie_escape/locales/ui.ja.json +10 -1
  20. zombie_escape/models.py +15 -9
  21. zombie_escape/render.py +42 -27
  22. zombie_escape/render_assets.py +533 -23
  23. zombie_escape/render_constants.py +57 -22
  24. zombie_escape/rng.py +9 -9
  25. zombie_escape/screens/__init__.py +59 -29
  26. zombie_escape/screens/game_over.py +3 -3
  27. zombie_escape/screens/gameplay.py +45 -27
  28. zombie_escape/screens/title.py +5 -2
  29. zombie_escape/stage_constants.py +34 -1
  30. zombie_escape/zombie_escape.py +30 -12
  31. {zombie_escape-1.5.4.dist-info → zombie_escape-1.7.1.dist-info}/METADATA +1 -1
  32. zombie_escape-1.7.1.dist-info/RECORD +45 -0
  33. zombie_escape/gameplay/logic.py +0 -1917
  34. zombie_escape-1.5.4.dist-info/RECORD +0 -35
  35. {zombie_escape-1.5.4.dist-info → zombie_escape-1.7.1.dist-info}/WHEEL +0 -0
  36. {zombie_escape-1.5.4.dist-info → zombie_escape-1.7.1.dist-info}/entry_points.txt +0 -0
  37. {zombie_escape-1.5.4.dist-info → zombie_escape-1.7.1.dist-info}/licenses/LICENSE.txt +0 -0
@@ -1,32 +1,542 @@
1
- """Shared render asset dataclasses used by multiple modules."""
1
+ """Shared render asset dataclasses and helpers used by multiple modules."""
2
2
 
3
3
  from __future__ import annotations
4
4
 
5
- from dataclasses import dataclass
5
+ import math
6
6
 
7
+ import pygame
7
8
 
8
- @dataclass(frozen=True)
9
- class FogRing:
10
- radius_factor: float
11
- thickness: int
9
+ from .colors import (
10
+ BLACK,
11
+ BLUE,
12
+ DARK_RED,
13
+ RED,
14
+ TRACKER_OUTLINE_COLOR,
15
+ WALL_FOLLOWER_OUTLINE_COLOR,
16
+ YELLOW,
17
+ EnvironmentPalette,
18
+ ORANGE,
19
+ STEEL_BEAM_COLOR,
20
+ STEEL_BEAM_LINE_COLOR,
21
+ get_environment_palette,
22
+ )
23
+ from .render_constants import (
24
+ BUDDY_COLOR,
25
+ HUMANOID_OUTLINE_COLOR,
26
+ HUMANOID_OUTLINE_WIDTH,
27
+ SURVIVOR_COLOR,
28
+ FogRing,
29
+ RenderAssets,
30
+ )
12
31
 
13
32
 
14
- @dataclass(frozen=True)
15
- class RenderAssets:
16
- screen_width: int
17
- screen_height: int
18
- status_bar_height: int
19
- player_radius: int
20
- fov_radius: int
21
- fog_radius_scale: float
22
- fog_hatch_pixel_scale: int
23
- fog_rings: list[FogRing]
24
- footprint_radius: int
25
- footprint_overview_radius: int
26
- footprint_lifetime_ms: int
27
- footprint_min_fade: float
28
- internal_wall_grid_snap: int
29
- flashlight_bonus_step: float
33
+ def _draw_outlined_circle(
34
+ surface: pygame.Surface,
35
+ center: tuple[int, int],
36
+ radius: int,
37
+ fill_color: tuple[int, int, int],
38
+ outline_color: tuple[int, int, int],
39
+ outline_width: int,
40
+ ) -> None:
41
+ pygame.draw.circle(surface, fill_color, center, radius)
42
+ if outline_width > 0:
43
+ pygame.draw.circle(surface, outline_color, center, radius, width=outline_width)
30
44
 
31
45
 
32
- __all__ = ["FogRing", "RenderAssets"]
46
+ def build_beveled_polygon(
47
+ width: int,
48
+ height: int,
49
+ depth: int,
50
+ bevels: tuple[bool, bool, bool, bool],
51
+ ) -> list[tuple[int, int]]:
52
+ d = max(0, min(depth, width // 2, height // 2))
53
+ if d == 0 or not any(bevels):
54
+ return [(0, 0), (width, 0), (width, height), (0, height)]
55
+
56
+ segments = 4
57
+ tl, tr, br, bl = bevels
58
+ points: list[tuple[int, int]] = []
59
+
60
+ def _add_point(x: float, y: float) -> None:
61
+ point = (int(round(x)), int(round(y)))
62
+ if not points or points[-1] != point:
63
+ points.append(point)
64
+
65
+ def _add_arc(
66
+ center_x: float,
67
+ center_y: float,
68
+ radius: float,
69
+ start_deg: float,
70
+ end_deg: float,
71
+ *,
72
+ skip_first: bool = False,
73
+ skip_last: bool = False,
74
+ ) -> None:
75
+ for i in range(segments + 1):
76
+ if skip_first and i == 0:
77
+ continue
78
+ if skip_last and i == segments:
79
+ continue
80
+ t = i / segments
81
+ angle = math.radians(start_deg + (end_deg - start_deg) * t)
82
+ _add_point(
83
+ center_x + radius * math.cos(angle),
84
+ center_y + radius * math.sin(angle),
85
+ )
86
+
87
+ _add_point(d if tl else 0, 0)
88
+ if tr:
89
+ _add_point(width - d, 0)
90
+ _add_arc(width - d, d, d, -90, 0, skip_first=True)
91
+ else:
92
+ _add_point(width, 0)
93
+ if br:
94
+ _add_point(width, height - d)
95
+ _add_arc(width - d, height - d, d, 0, 90, skip_first=True)
96
+ else:
97
+ _add_point(width, height)
98
+ if bl:
99
+ _add_point(d, height)
100
+ _add_arc(d, height - d, d, 90, 180, skip_first=True)
101
+ else:
102
+ _add_point(0, height)
103
+ if tl:
104
+ _add_point(0, d)
105
+ _add_arc(d, d, d, 180, 270, skip_first=True, skip_last=True)
106
+ return points
107
+
108
+
109
+ def resolve_wall_colors(
110
+ *,
111
+ health_ratio: float,
112
+ palette_category: str,
113
+ palette: EnvironmentPalette | None,
114
+ ) -> tuple[tuple[int, int, int], tuple[int, int, int]]:
115
+ if palette is None:
116
+ palette = get_environment_palette(None)
117
+ if palette_category == "outer_wall":
118
+ base_color = palette.outer_wall
119
+ border_base_color = palette.outer_wall_border
120
+ else:
121
+ base_color = palette.inner_wall
122
+ border_base_color = palette.inner_wall_border
123
+
124
+ if health_ratio <= 0:
125
+ fill_color = (40, 40, 40)
126
+ ratio = 0.0
127
+ else:
128
+ ratio = max(0.0, min(1.0, health_ratio))
129
+ mix = 0.6 + 0.4 * ratio
130
+ fill_color = (
131
+ int(base_color[0] * mix),
132
+ int(base_color[1] * mix),
133
+ int(base_color[2] * mix),
134
+ )
135
+ border_mix = 0.6 + 0.4 * ratio
136
+ border_color = (
137
+ int(border_base_color[0] * border_mix),
138
+ int(border_base_color[1] * border_mix),
139
+ int(border_base_color[2] * border_mix),
140
+ )
141
+ return fill_color, border_color
142
+
143
+
144
+ CAR_COLOR_SCHEMES: dict[str, dict[str, tuple[int, int, int]]] = {
145
+ "default": {
146
+ "healthy": YELLOW,
147
+ "damaged": ORANGE,
148
+ "critical": DARK_RED,
149
+ },
150
+ "disabled": {
151
+ "healthy": (185, 185, 185),
152
+ "damaged": (150, 150, 150),
153
+ "critical": (110, 110, 110),
154
+ },
155
+ }
156
+
157
+
158
+ def resolve_car_color(
159
+ *,
160
+ health_ratio: float,
161
+ appearance: str,
162
+ palette: EnvironmentPalette | None = None,
163
+ ) -> tuple[int, int, int]:
164
+ palette = CAR_COLOR_SCHEMES.get(appearance, CAR_COLOR_SCHEMES["default"])
165
+ color = palette["healthy"]
166
+ if health_ratio < 0.6:
167
+ color = palette["damaged"]
168
+ if health_ratio < 0.3:
169
+ color = palette["critical"]
170
+ return color
171
+
172
+
173
+ def resolve_steel_beam_colors(
174
+ *,
175
+ health_ratio: float,
176
+ palette: EnvironmentPalette | None = None,
177
+ ) -> tuple[tuple[int, int, int], tuple[int, int, int]]:
178
+ return STEEL_BEAM_COLOR, STEEL_BEAM_LINE_COLOR
179
+
180
+
181
+ def build_player_surface(radius: int) -> pygame.Surface:
182
+ surface = pygame.Surface((radius * 2 + 2, radius * 2 + 2), pygame.SRCALPHA)
183
+ _draw_outlined_circle(
184
+ surface,
185
+ (radius + 1, radius + 1),
186
+ radius,
187
+ BLUE,
188
+ HUMANOID_OUTLINE_COLOR,
189
+ HUMANOID_OUTLINE_WIDTH,
190
+ )
191
+ return surface
192
+
193
+
194
+ def build_survivor_surface(radius: int, *, is_buddy: bool) -> pygame.Surface:
195
+ surface = pygame.Surface((radius * 2, radius * 2), pygame.SRCALPHA)
196
+ fill_color = BUDDY_COLOR if is_buddy else SURVIVOR_COLOR
197
+ _draw_outlined_circle(
198
+ surface,
199
+ (radius, radius),
200
+ radius,
201
+ fill_color,
202
+ HUMANOID_OUTLINE_COLOR,
203
+ HUMANOID_OUTLINE_WIDTH,
204
+ )
205
+ return surface
206
+
207
+
208
+ def build_zombie_surface(
209
+ radius: int,
210
+ *,
211
+ tracker: bool = False,
212
+ wall_follower: bool = False,
213
+ ) -> pygame.Surface:
214
+ if tracker:
215
+ outline_color = TRACKER_OUTLINE_COLOR
216
+ elif wall_follower:
217
+ outline_color = WALL_FOLLOWER_OUTLINE_COLOR
218
+ else:
219
+ outline_color = DARK_RED
220
+ surface = pygame.Surface((radius * 2, radius * 2), pygame.SRCALPHA)
221
+ _draw_outlined_circle(
222
+ surface,
223
+ (radius, radius),
224
+ radius,
225
+ RED,
226
+ outline_color,
227
+ 1,
228
+ )
229
+ return surface
230
+
231
+
232
+ def build_car_surface(width: int, height: int) -> pygame.Surface:
233
+ return pygame.Surface((width, height), pygame.SRCALPHA)
234
+
235
+
236
+ def paint_car_surface(
237
+ surface: pygame.Surface,
238
+ *,
239
+ width: int,
240
+ height: int,
241
+ color: tuple[int, int, int],
242
+ ) -> None:
243
+ surface.fill((0, 0, 0, 0))
244
+
245
+ body_rect = pygame.Rect(1, 4, width - 2, height - 8)
246
+ front_cap_height = max(8, body_rect.height // 3)
247
+ front_cap = pygame.Rect(
248
+ body_rect.left, body_rect.top, body_rect.width, front_cap_height
249
+ )
250
+ windshield_rect = pygame.Rect(
251
+ body_rect.left + 4,
252
+ body_rect.top + 3,
253
+ body_rect.width - 8,
254
+ front_cap_height - 5,
255
+ )
256
+
257
+ trim_color = tuple(int(c * 0.55) for c in color)
258
+ front_cap_color = tuple(min(255, int(c * 1.08)) for c in color)
259
+ body_color = color
260
+ window_color = (70, 110, 150)
261
+ wheel_color = (35, 35, 35)
262
+
263
+ wheel_width = width // 3
264
+ wheel_height = 6
265
+ for y in (body_rect.top + 4, body_rect.bottom - wheel_height - 4):
266
+ left_wheel = pygame.Rect(2, y, wheel_width, wheel_height)
267
+ right_wheel = pygame.Rect(width - wheel_width - 2, y, wheel_width, wheel_height)
268
+ pygame.draw.rect(surface, wheel_color, left_wheel, border_radius=3)
269
+ pygame.draw.rect(surface, wheel_color, right_wheel, border_radius=3)
270
+
271
+ pygame.draw.rect(surface, body_color, body_rect, border_radius=4)
272
+ pygame.draw.rect(surface, trim_color, body_rect, width=2, border_radius=4)
273
+ pygame.draw.rect(surface, front_cap_color, front_cap, border_radius=10)
274
+ pygame.draw.rect(surface, trim_color, front_cap, width=2, border_radius=10)
275
+ pygame.draw.rect(surface, window_color, windshield_rect, border_radius=4)
276
+
277
+ headlight_color = (245, 245, 200)
278
+ for x in (front_cap.left + 5, front_cap.right - 5):
279
+ pygame.draw.circle(surface, headlight_color, (x, body_rect.top + 5), 2)
280
+ grille_rect = pygame.Rect(front_cap.centerx - 6, front_cap.top + 2, 12, 6)
281
+ pygame.draw.rect(surface, trim_color, grille_rect, border_radius=2)
282
+ tail_light_color = (255, 80, 50)
283
+ for x in (body_rect.left + 5, body_rect.right - 5):
284
+ pygame.draw.rect(
285
+ surface,
286
+ tail_light_color,
287
+ (x - 2, body_rect.bottom - 5, 4, 3),
288
+ border_radius=1,
289
+ )
290
+
291
+
292
+ def paint_wall_surface(
293
+ surface: pygame.Surface,
294
+ *,
295
+ fill_color: tuple[int, int, int],
296
+ border_color: tuple[int, int, int],
297
+ bevel_depth: int,
298
+ bevel_mask: tuple[bool, bool, bool, bool],
299
+ draw_bottom_side: bool,
300
+ bottom_side_ratio: float,
301
+ side_shade_ratio: float,
302
+ ) -> None:
303
+ surface.fill((0, 0, 0, 0))
304
+ rect_obj = surface.get_rect()
305
+ side_height = 0
306
+ if draw_bottom_side:
307
+ side_height = max(1, int(rect_obj.height * bottom_side_ratio))
308
+
309
+ def _draw_face(
310
+ target: pygame.Surface,
311
+ *,
312
+ face_size: tuple[int, int] | None = None,
313
+ ) -> None:
314
+ face_width, face_height = face_size or target.get_size()
315
+ if bevel_depth > 0 and any(bevel_mask):
316
+ face_polygon = build_beveled_polygon(
317
+ face_width, face_height, bevel_depth, bevel_mask
318
+ )
319
+ pygame.draw.polygon(target, border_color, face_polygon)
320
+ else:
321
+ target.fill(border_color)
322
+ border_width = 18
323
+ inner_rect = target.get_rect().inflate(-border_width, -border_width)
324
+ if inner_rect.width > 0 and inner_rect.height > 0:
325
+ inner_depth = max(0, bevel_depth - border_width)
326
+ if inner_depth > 0 and any(bevel_mask):
327
+ inner_polygon = build_beveled_polygon(
328
+ inner_rect.width, inner_rect.height, inner_depth, bevel_mask
329
+ )
330
+ inner_offset_polygon = [
331
+ (
332
+ int(point[0] + inner_rect.left),
333
+ int(point[1] + inner_rect.top),
334
+ )
335
+ for point in inner_polygon
336
+ ]
337
+ pygame.draw.polygon(target, fill_color, inner_offset_polygon)
338
+ else:
339
+ pygame.draw.rect(target, fill_color, inner_rect)
340
+
341
+ if draw_bottom_side:
342
+ extra_height = max(0, int(bevel_depth / 2))
343
+ side_draw_height = min(rect_obj.height, side_height + extra_height)
344
+ top_rect = pygame.Rect(
345
+ rect_obj.left,
346
+ rect_obj.top,
347
+ rect_obj.width,
348
+ rect_obj.height - side_height,
349
+ )
350
+ side_rect = pygame.Rect(
351
+ rect_obj.left,
352
+ rect_obj.bottom - side_draw_height,
353
+ rect_obj.width,
354
+ side_draw_height,
355
+ )
356
+ side_color = tuple(int(c * side_shade_ratio) for c in fill_color)
357
+ side_surface = pygame.Surface(rect_obj.size, pygame.SRCALPHA)
358
+ if bevel_depth > 0 and any(bevel_mask):
359
+ side_polygon = build_beveled_polygon(
360
+ rect_obj.width, rect_obj.height, bevel_depth, bevel_mask
361
+ )
362
+ pygame.draw.polygon(side_surface, side_color, side_polygon)
363
+ else:
364
+ pygame.draw.rect(side_surface, side_color, rect_obj)
365
+ surface.blit(side_surface, side_rect.topleft, area=side_rect)
366
+
367
+ top_height = max(0, rect_obj.height - side_height)
368
+ top_surface = pygame.Surface((rect_obj.width, top_height), pygame.SRCALPHA)
369
+ _draw_face(
370
+ top_surface,
371
+ face_size=(rect_obj.width, top_height),
372
+ )
373
+ if top_rect.height > 0:
374
+ surface.blit(top_surface, top_rect.topleft, area=top_rect)
375
+ else:
376
+ _draw_face(surface)
377
+
378
+
379
+ def paint_steel_beam_surface(
380
+ surface: pygame.Surface,
381
+ *,
382
+ base_color: tuple[int, int, int],
383
+ line_color: tuple[int, int, int],
384
+ health_ratio: float,
385
+ ) -> None:
386
+ surface.fill((0, 0, 0, 0))
387
+ fill_mix = 0.55 + 0.45 * health_ratio
388
+ fill_color = tuple(int(c * fill_mix) for c in base_color)
389
+ rect_obj = surface.get_rect()
390
+ side_height = max(1, int(rect_obj.height * 0.1))
391
+ top_rect = pygame.Rect(
392
+ rect_obj.left,
393
+ rect_obj.top,
394
+ rect_obj.width,
395
+ rect_obj.height - side_height,
396
+ )
397
+ side_mix = 0.45 + 0.35 * health_ratio
398
+ side_color = tuple(int(c * side_mix * 0.9) for c in base_color)
399
+ side_rect = pygame.Rect(
400
+ rect_obj.left,
401
+ rect_obj.bottom - side_height,
402
+ rect_obj.width,
403
+ side_height,
404
+ )
405
+ pygame.draw.rect(surface, side_color, side_rect)
406
+ line_mix = 0.7 + 0.3 * health_ratio
407
+ tuned_line_color = tuple(int(c * line_mix) for c in line_color)
408
+ top_surface = pygame.Surface(top_rect.size, pygame.SRCALPHA)
409
+ local_rect = top_surface.get_rect()
410
+ pygame.draw.rect(top_surface, fill_color, local_rect)
411
+ pygame.draw.rect(top_surface, tuned_line_color, local_rect, width=6)
412
+ pygame.draw.line(
413
+ top_surface,
414
+ tuned_line_color,
415
+ local_rect.topleft,
416
+ local_rect.bottomright,
417
+ width=6,
418
+ )
419
+ pygame.draw.line(
420
+ top_surface,
421
+ tuned_line_color,
422
+ local_rect.topright,
423
+ local_rect.bottomleft,
424
+ width=6,
425
+ )
426
+ surface.blit(top_surface, top_rect.topleft)
427
+
428
+
429
+ def paint_zombie_surface(
430
+ surface: pygame.Surface,
431
+ *,
432
+ radius: int,
433
+ palm_angle: float | None = None,
434
+ tracker: bool = False,
435
+ wall_follower: bool = False,
436
+ ) -> None:
437
+ if tracker:
438
+ outline_color = TRACKER_OUTLINE_COLOR
439
+ elif wall_follower:
440
+ outline_color = WALL_FOLLOWER_OUTLINE_COLOR
441
+ else:
442
+ outline_color = DARK_RED
443
+ surface.fill((0, 0, 0, 0))
444
+ _draw_outlined_circle(
445
+ surface,
446
+ (radius, radius),
447
+ radius,
448
+ RED,
449
+ outline_color,
450
+ 1,
451
+ )
452
+ if palm_angle is None:
453
+ return
454
+ palm_radius = max(1, radius // 3)
455
+ palm_offset = radius - palm_radius * 0.3
456
+ palm_x = radius + math.cos(palm_angle) * palm_offset
457
+ palm_y = radius + math.sin(palm_angle) * palm_offset
458
+ pygame.draw.circle(
459
+ surface,
460
+ outline_color,
461
+ (int(palm_x), int(palm_y)),
462
+ palm_radius,
463
+ )
464
+
465
+
466
+ def build_fuel_can_surface(width: int, height: int) -> pygame.Surface:
467
+ surface = pygame.Surface((width, height), pygame.SRCALPHA)
468
+
469
+ # Jerrycan silhouette with cut corner
470
+ body_pts = [
471
+ (1, 4),
472
+ (width - 2, 4),
473
+ (width - 2, height - 2),
474
+ (1, height - 2),
475
+ (1, 8),
476
+ (4, 4),
477
+ ]
478
+ pygame.draw.polygon(surface, YELLOW, body_pts)
479
+ pygame.draw.polygon(surface, BLACK, body_pts, width=2)
480
+
481
+ cap_size = max(2, width // 4)
482
+ cap_rect = pygame.Rect(width - cap_size - 2, 1, cap_size, 3)
483
+ pygame.draw.rect(surface, YELLOW, cap_rect, border_radius=1)
484
+ pygame.draw.rect(surface, BLACK, cap_rect, width=1, border_radius=1)
485
+
486
+ # Cross brace accent
487
+ brace_color = (240, 200, 40)
488
+ pygame.draw.line(
489
+ surface, brace_color, (3, height // 2), (width - 4, height // 2), width=2
490
+ )
491
+ pygame.draw.line(
492
+ surface, BLACK, (3, height // 2), (width - 4, height // 2), width=1
493
+ )
494
+ return surface
495
+
496
+
497
+ def build_flashlight_surface(width: int, height: int) -> pygame.Surface:
498
+ surface = pygame.Surface((width, height), pygame.SRCALPHA)
499
+
500
+ body_color = (230, 200, 70)
501
+ trim_color = (80, 70, 40)
502
+ head_color = (200, 180, 90)
503
+ beam_color = (255, 240, 180, 150)
504
+
505
+ body_rect = pygame.Rect(1, 2, width - 4, height - 4)
506
+ head_rect = pygame.Rect(
507
+ body_rect.right - 3, body_rect.top - 1, 4, body_rect.height + 2
508
+ )
509
+ beam_points = [
510
+ (head_rect.right + 4, head_rect.centery),
511
+ (head_rect.right + 2, head_rect.top),
512
+ (head_rect.right + 2, head_rect.bottom),
513
+ ]
514
+
515
+ pygame.draw.rect(surface, body_color, body_rect, border_radius=2)
516
+ pygame.draw.rect(surface, trim_color, body_rect, width=1, border_radius=2)
517
+ pygame.draw.rect(surface, head_color, head_rect, border_radius=2)
518
+ pygame.draw.rect(surface, trim_color, head_rect, width=1, border_radius=2)
519
+ pygame.draw.polygon(surface, beam_color, beam_points)
520
+ return surface
521
+
522
+
523
+ __all__ = [
524
+ "EnvironmentPalette",
525
+ "FogRing",
526
+ "RenderAssets",
527
+ "build_beveled_polygon",
528
+ "resolve_wall_colors",
529
+ "resolve_car_color",
530
+ "resolve_steel_beam_colors",
531
+ "CAR_COLOR_SCHEMES",
532
+ "build_player_surface",
533
+ "build_survivor_surface",
534
+ "build_zombie_surface",
535
+ "build_car_surface",
536
+ "paint_car_surface",
537
+ "paint_wall_surface",
538
+ "paint_steel_beam_surface",
539
+ "paint_zombie_surface",
540
+ "build_fuel_can_surface",
541
+ "build_flashlight_surface",
542
+ ]
@@ -2,11 +2,40 @@
2
2
 
3
3
  from __future__ import annotations
4
4
 
5
- from .gameplay_constants import FOV_RADIUS, PLAYER_RADIUS
6
- from .level_constants import CELL_SIZE
7
- from .render_assets import FogRing, RenderAssets
5
+ from dataclasses import dataclass
6
+
7
+ from .entities_constants import FOV_RADIUS, PLAYER_RADIUS
8
8
  from .screen_constants import SCREEN_HEIGHT, SCREEN_WIDTH, STATUS_BAR_HEIGHT
9
9
 
10
+ HUMANOID_OUTLINE_COLOR = (0, 80, 200)
11
+ HUMANOID_OUTLINE_WIDTH = 1
12
+ BUDDY_COLOR = (0, 180, 63)
13
+ SURVIVOR_COLOR = (198, 198, 198)
14
+
15
+
16
+ @dataclass(frozen=True)
17
+ class FogRing:
18
+ radius_factor: float
19
+ thickness: int
20
+
21
+
22
+ @dataclass(frozen=True)
23
+ class RenderAssets:
24
+ screen_width: int
25
+ screen_height: int
26
+ status_bar_height: int
27
+ player_radius: int
28
+ fov_radius: int
29
+ fog_radius_scale: float
30
+ fog_hatch_pixel_scale: int
31
+ fog_rings: list[FogRing]
32
+ footprint_radius: int
33
+ footprint_overview_radius: int
34
+ footprint_lifetime_ms: int
35
+ footprint_min_fade: float
36
+ internal_wall_grid_snap: int
37
+ flashlight_bonus_step: float
38
+
10
39
  FOG_RADIUS_SCALE = 1.2
11
40
  FOG_HATCH_PIXEL_SCALE = 2
12
41
 
@@ -18,8 +47,6 @@ FOOTPRINT_COLOR = (110, 200, 255)
18
47
  FOOTPRINT_LIFETIME_MS = 135000
19
48
  FOOTPRINT_MIN_FADE = 0.3
20
49
 
21
- INTERNAL_WALL_GRID_SNAP = CELL_SIZE
22
-
23
50
  FOG_RINGS = [
24
51
  FogRing(radius_factor=0.529, thickness=2),
25
52
  FogRing(radius_factor=0.639, thickness=4),
@@ -28,25 +55,33 @@ FOG_RINGS = [
28
55
  FogRing(radius_factor=0.968, thickness=12),
29
56
  ]
30
57
 
31
- RENDER_ASSETS = RenderAssets(
32
- screen_width=SCREEN_WIDTH,
33
- screen_height=SCREEN_HEIGHT,
34
- status_bar_height=STATUS_BAR_HEIGHT,
35
- player_radius=PLAYER_RADIUS,
36
- fov_radius=FOV_RADIUS,
37
- fog_radius_scale=FOG_RADIUS_SCALE,
38
- fog_hatch_pixel_scale=FOG_HATCH_PIXEL_SCALE,
39
- fog_rings=FOG_RINGS,
40
- footprint_radius=FOOTPRINT_RADIUS,
41
- footprint_overview_radius=FOOTPRINT_OVERVIEW_RADIUS,
42
- footprint_lifetime_ms=FOOTPRINT_LIFETIME_MS,
43
- footprint_min_fade=FOOTPRINT_MIN_FADE,
44
- internal_wall_grid_snap=INTERNAL_WALL_GRID_SNAP,
45
- flashlight_bonus_step=FLASHLIGHT_FOG_SCALE_STEP,
46
- )
58
+ def build_render_assets(cell_size: int) -> RenderAssets:
59
+ return RenderAssets(
60
+ screen_width=SCREEN_WIDTH,
61
+ screen_height=SCREEN_HEIGHT,
62
+ status_bar_height=STATUS_BAR_HEIGHT,
63
+ player_radius=PLAYER_RADIUS,
64
+ fov_radius=FOV_RADIUS,
65
+ fog_radius_scale=FOG_RADIUS_SCALE,
66
+ fog_hatch_pixel_scale=FOG_HATCH_PIXEL_SCALE,
67
+ fog_rings=FOG_RINGS,
68
+ footprint_radius=FOOTPRINT_RADIUS,
69
+ footprint_overview_radius=FOOTPRINT_OVERVIEW_RADIUS,
70
+ footprint_lifetime_ms=FOOTPRINT_LIFETIME_MS,
71
+ footprint_min_fade=FOOTPRINT_MIN_FADE,
72
+ internal_wall_grid_snap=cell_size,
73
+ flashlight_bonus_step=FLASHLIGHT_FOG_SCALE_STEP,
74
+ )
75
+
47
76
 
48
77
  __all__ = [
78
+ "BUDDY_COLOR",
79
+ "HUMANOID_OUTLINE_COLOR",
80
+ "HUMANOID_OUTLINE_WIDTH",
81
+ "SURVIVOR_COLOR",
82
+ "FogRing",
83
+ "RenderAssets",
49
84
  "FOG_RADIUS_SCALE",
50
85
  "FLASHLIGHT_FOG_SCALE_STEP",
51
- "RENDER_ASSETS",
86
+ "build_render_assets",
52
87
  ]