zombie-escape 1.5.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 +4 -0
- zombie_escape/__init__.py +7 -0
- zombie_escape/assets/fonts/Silkscreen-Regular.ttf +0 -0
- zombie_escape/assets/fonts/misaki_gothic.ttf +0 -0
- zombie_escape/colors.py +170 -0
- zombie_escape/config.py +58 -0
- zombie_escape/entities.py +1574 -0
- zombie_escape/font_utils.py +45 -0
- zombie_escape/gameplay/__init__.py +7 -0
- zombie_escape/gameplay/logic.py +1917 -0
- zombie_escape/gameplay_constants.py +178 -0
- zombie_escape/level_blueprints.py +179 -0
- zombie_escape/level_constants.py +20 -0
- zombie_escape/locales/ui.en.json +146 -0
- zombie_escape/locales/ui.ja.json +146 -0
- zombie_escape/localization.py +186 -0
- zombie_escape/models.py +134 -0
- zombie_escape/progress.py +48 -0
- zombie_escape/render.py +831 -0
- zombie_escape/render_assets.py +32 -0
- zombie_escape/render_constants.py +52 -0
- zombie_escape/rng.py +132 -0
- zombie_escape/screen_constants.py +21 -0
- zombie_escape/screens/__init__.py +222 -0
- zombie_escape/screens/game_over.py +183 -0
- zombie_escape/screens/gameplay.py +349 -0
- zombie_escape/screens/settings.py +440 -0
- zombie_escape/screens/title.py +462 -0
- zombie_escape/stage_constants.py +89 -0
- zombie_escape/zombie_escape.py +231 -0
- zombie_escape-1.5.4.dist-info/METADATA +160 -0
- zombie_escape-1.5.4.dist-info/RECORD +35 -0
- zombie_escape-1.5.4.dist-info/WHEEL +4 -0
- zombie_escape-1.5.4.dist-info/entry_points.txt +2 -0
- zombie_escape-1.5.4.dist-info/licenses/LICENSE.txt +9 -0
|
Binary file
|
|
Binary file
|
zombie_escape/colors.py
ADDED
|
@@ -0,0 +1,170 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from dataclasses import dataclass
|
|
4
|
+
|
|
5
|
+
# Basic palette
|
|
6
|
+
WHITE: tuple[int, int, int] = (255, 255, 255)
|
|
7
|
+
BLACK: tuple[int, int, int] = (0, 0, 0)
|
|
8
|
+
RED: tuple[int, int, int] = (255, 0, 0)
|
|
9
|
+
GREEN: tuple[int, int, int] = (0, 255, 0)
|
|
10
|
+
BLUE: tuple[int, int, int] = (0, 0, 255)
|
|
11
|
+
GRAY: tuple[int, int, int] = (100, 100, 100)
|
|
12
|
+
LIGHT_GRAY: tuple[int, int, int] = (200, 200, 200)
|
|
13
|
+
YELLOW: tuple[int, int, int] = (255, 255, 0)
|
|
14
|
+
ORANGE: tuple[int, int, int] = (255, 165, 0)
|
|
15
|
+
DARK_RED: tuple[int, int, int] = (139, 0, 0)
|
|
16
|
+
TRACKER_OUTLINE_COLOR: tuple[int, int, int] = (170, 70, 220)
|
|
17
|
+
WALL_FOLLOWER_OUTLINE_COLOR: tuple[int, int, int] = (140, 140, 140)
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
@dataclass(frozen=True)
|
|
21
|
+
class EnvironmentPalette:
|
|
22
|
+
"""Collection of colors that define the ambient environment."""
|
|
23
|
+
|
|
24
|
+
floor_primary: tuple[int, int, int]
|
|
25
|
+
floor_secondary: tuple[int, int, int]
|
|
26
|
+
outside: tuple[int, int, int]
|
|
27
|
+
inner_wall: tuple[int, int, int]
|
|
28
|
+
inner_wall_border: tuple[int, int, int]
|
|
29
|
+
outer_wall: tuple[int, int, int]
|
|
30
|
+
outer_wall_border: tuple[int, int, int]
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
def _clamp(value: float) -> int:
|
|
34
|
+
return max(0, min(255, int(value)))
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
def _adjust_color(
|
|
38
|
+
color: tuple[int, int, int], *, brightness: float = 1.0, saturation: float = 1.0
|
|
39
|
+
) -> tuple[int, int, int]:
|
|
40
|
+
"""Return color tinted by brightness/saturation multipliers."""
|
|
41
|
+
|
|
42
|
+
r, g, b = color
|
|
43
|
+
gray = 0.2126 * r + 0.7152 * g + 0.0722 * b
|
|
44
|
+
def mix(component: int) -> int:
|
|
45
|
+
value = gray + (component - gray) * saturation
|
|
46
|
+
value *= brightness
|
|
47
|
+
return _clamp(value)
|
|
48
|
+
|
|
49
|
+
return mix(r), mix(g), mix(b)
|
|
50
|
+
|
|
51
|
+
|
|
52
|
+
DEFAULT_AMBIENT_PALETTE_KEY = "default"
|
|
53
|
+
NO_FLASHLIGHT_PALETTE_KEY = "no_flashlight"
|
|
54
|
+
DAWN_AMBIENT_PALETTE_KEY = "dawn"
|
|
55
|
+
|
|
56
|
+
# Base palette used throughout gameplay (matches the previous constants).
|
|
57
|
+
_DEFAULT_ENVIRONMENT_PALETTE = EnvironmentPalette(
|
|
58
|
+
floor_primary=(43, 57, 70),
|
|
59
|
+
floor_secondary=(50, 64, 79),
|
|
60
|
+
outside=(32, 60, 40),
|
|
61
|
+
inner_wall=(125, 101, 78),
|
|
62
|
+
inner_wall_border=(136, 110, 85),
|
|
63
|
+
outer_wall=(136, 135, 128),
|
|
64
|
+
outer_wall_border=(147, 146, 138),
|
|
65
|
+
)
|
|
66
|
+
|
|
67
|
+
# Dark, desaturated palette that sells the "alone without a flashlight" vibe.
|
|
68
|
+
_GLOOM_ENVIRONMENT_PALETTE = EnvironmentPalette(
|
|
69
|
+
floor_primary=_adjust_color(
|
|
70
|
+
_DEFAULT_ENVIRONMENT_PALETTE.floor_primary, brightness=0.725, saturation=0.675
|
|
71
|
+
),
|
|
72
|
+
floor_secondary=_adjust_color(
|
|
73
|
+
_DEFAULT_ENVIRONMENT_PALETTE.floor_secondary, brightness=0.74, saturation=0.65
|
|
74
|
+
),
|
|
75
|
+
outside=_adjust_color(
|
|
76
|
+
_DEFAULT_ENVIRONMENT_PALETTE.outside, brightness=0.7, saturation=0.625
|
|
77
|
+
),
|
|
78
|
+
inner_wall=_adjust_color(
|
|
79
|
+
_DEFAULT_ENVIRONMENT_PALETTE.inner_wall, brightness=0.775, saturation=0.7
|
|
80
|
+
),
|
|
81
|
+
inner_wall_border=_adjust_color(
|
|
82
|
+
_DEFAULT_ENVIRONMENT_PALETTE.inner_wall_border, brightness=0.775, saturation=0.7
|
|
83
|
+
),
|
|
84
|
+
outer_wall=_adjust_color(
|
|
85
|
+
_DEFAULT_ENVIRONMENT_PALETTE.outer_wall, brightness=0.75, saturation=0.675
|
|
86
|
+
),
|
|
87
|
+
outer_wall_border=_adjust_color(
|
|
88
|
+
_DEFAULT_ENVIRONMENT_PALETTE.outer_wall_border, brightness=0.75, saturation=0.675
|
|
89
|
+
),
|
|
90
|
+
)
|
|
91
|
+
|
|
92
|
+
_DAWN_ENVIRONMENT_PALETTE = EnvironmentPalette(
|
|
93
|
+
floor_primary=(58, 70, 84),
|
|
94
|
+
floor_secondary=(66, 78, 92),
|
|
95
|
+
outside=(118, 140, 104),
|
|
96
|
+
inner_wall=(125, 101, 78),
|
|
97
|
+
inner_wall_border=(136, 110, 85),
|
|
98
|
+
outer_wall=(136, 135, 128),
|
|
99
|
+
outer_wall_border=(147, 146, 138),
|
|
100
|
+
)
|
|
101
|
+
|
|
102
|
+
ENVIRONMENT_PALETTES: dict[str, EnvironmentPalette] = {
|
|
103
|
+
DEFAULT_AMBIENT_PALETTE_KEY: _DEFAULT_ENVIRONMENT_PALETTE,
|
|
104
|
+
NO_FLASHLIGHT_PALETTE_KEY: _GLOOM_ENVIRONMENT_PALETTE,
|
|
105
|
+
DAWN_AMBIENT_PALETTE_KEY: _DAWN_ENVIRONMENT_PALETTE,
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
|
|
109
|
+
def get_environment_palette(key: str | None) -> EnvironmentPalette:
|
|
110
|
+
"""Return the color palette for the provided key (falls back to default)."""
|
|
111
|
+
|
|
112
|
+
if not key:
|
|
113
|
+
return ENVIRONMENT_PALETTES[DEFAULT_AMBIENT_PALETTE_KEY]
|
|
114
|
+
return ENVIRONMENT_PALETTES.get(key, ENVIRONMENT_PALETTES[DEFAULT_AMBIENT_PALETTE_KEY])
|
|
115
|
+
|
|
116
|
+
|
|
117
|
+
def ambient_palette_key_for_flashlights(count: int) -> str:
|
|
118
|
+
"""Return the palette key for the provided flashlight inventory count."""
|
|
119
|
+
|
|
120
|
+
return (
|
|
121
|
+
DEFAULT_AMBIENT_PALETTE_KEY
|
|
122
|
+
if max(0, count) > 0
|
|
123
|
+
else NO_FLASHLIGHT_PALETTE_KEY
|
|
124
|
+
)
|
|
125
|
+
|
|
126
|
+
|
|
127
|
+
# World colors (default palette versions preserved for backwards compatibility).
|
|
128
|
+
INTERNAL_WALL_COLOR: tuple[int, int, int] = _DEFAULT_ENVIRONMENT_PALETTE.inner_wall
|
|
129
|
+
INTERNAL_WALL_BORDER_COLOR: tuple[int, int, int] = _DEFAULT_ENVIRONMENT_PALETTE.inner_wall_border
|
|
130
|
+
OUTER_WALL_COLOR: tuple[int, int, int] = _DEFAULT_ENVIRONMENT_PALETTE.outer_wall
|
|
131
|
+
OUTER_WALL_BORDER_COLOR: tuple[int, int, int] = _DEFAULT_ENVIRONMENT_PALETTE.outer_wall_border
|
|
132
|
+
FLOOR_COLOR_PRIMARY: tuple[int, int, int] = _DEFAULT_ENVIRONMENT_PALETTE.floor_primary
|
|
133
|
+
FLOOR_COLOR_SECONDARY: tuple[int, int, int] = _DEFAULT_ENVIRONMENT_PALETTE.floor_secondary
|
|
134
|
+
FLOOR_COLOR_OUTSIDE: tuple[int, int, int] = _DEFAULT_ENVIRONMENT_PALETTE.outside
|
|
135
|
+
FOOTPRINT_COLOR: tuple[int, int, int] = (110, 200, 255)
|
|
136
|
+
STEEL_BEAM_COLOR: tuple[int, int, int] = (110, 50, 50)
|
|
137
|
+
STEEL_BEAM_LINE_COLOR: tuple[int, int, int] = (180, 90, 90)
|
|
138
|
+
|
|
139
|
+
|
|
140
|
+
__all__ = [
|
|
141
|
+
"WHITE",
|
|
142
|
+
"BLACK",
|
|
143
|
+
"RED",
|
|
144
|
+
"GREEN",
|
|
145
|
+
"BLUE",
|
|
146
|
+
"GRAY",
|
|
147
|
+
"LIGHT_GRAY",
|
|
148
|
+
"YELLOW",
|
|
149
|
+
"ORANGE",
|
|
150
|
+
"DARK_RED",
|
|
151
|
+
"TRACKER_OUTLINE_COLOR",
|
|
152
|
+
"WALL_FOLLOWER_OUTLINE_COLOR",
|
|
153
|
+
"DAWN_AMBIENT_PALETTE_KEY",
|
|
154
|
+
"INTERNAL_WALL_COLOR",
|
|
155
|
+
"INTERNAL_WALL_BORDER_COLOR",
|
|
156
|
+
"OUTER_WALL_COLOR",
|
|
157
|
+
"OUTER_WALL_BORDER_COLOR",
|
|
158
|
+
"FLOOR_COLOR_PRIMARY",
|
|
159
|
+
"FLOOR_COLOR_SECONDARY",
|
|
160
|
+
"FLOOR_COLOR_OUTSIDE",
|
|
161
|
+
"FOOTPRINT_COLOR",
|
|
162
|
+
"STEEL_BEAM_COLOR",
|
|
163
|
+
"STEEL_BEAM_LINE_COLOR",
|
|
164
|
+
"EnvironmentPalette",
|
|
165
|
+
"DEFAULT_AMBIENT_PALETTE_KEY",
|
|
166
|
+
"NO_FLASHLIGHT_PALETTE_KEY",
|
|
167
|
+
"ENVIRONMENT_PALETTES",
|
|
168
|
+
"get_environment_palette",
|
|
169
|
+
"ambient_palette_key_for_flashlights",
|
|
170
|
+
]
|
zombie_escape/config.py
ADDED
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
import json
|
|
2
|
+
from copy import deepcopy
|
|
3
|
+
from pathlib import Path
|
|
4
|
+
from typing import Any
|
|
5
|
+
|
|
6
|
+
from platformdirs import user_config_dir
|
|
7
|
+
|
|
8
|
+
APP_NAME = "ZombieEscape"
|
|
9
|
+
|
|
10
|
+
# Defaults for all configurable options
|
|
11
|
+
DEFAULT_CONFIG: dict[str, Any] = {
|
|
12
|
+
"language": "en",
|
|
13
|
+
"footprints": {"enabled": True},
|
|
14
|
+
"fast_zombies": {"enabled": False, "ratio": 0.1},
|
|
15
|
+
"car_hint": {"enabled": True, "delay_ms": 180_000},
|
|
16
|
+
"steel_beams": {"enabled": False, "chance": 0.05},
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
def user_config_path() -> Path:
|
|
21
|
+
"""Return the platform-specific config file path."""
|
|
22
|
+
return Path(user_config_dir(APP_NAME, APP_NAME)) / "config.json"
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
def _deep_merge(base: dict[str, Any], override: dict[str, Any]) -> dict[str, Any]:
|
|
26
|
+
"""Deep-merge dictionaries, with override winning on conflicts."""
|
|
27
|
+
merged: dict[str, Any] = deepcopy(base)
|
|
28
|
+
for key, val in override.items():
|
|
29
|
+
if isinstance(val, dict) and isinstance(merged.get(key), dict):
|
|
30
|
+
merged[key] = _deep_merge(merged[key], val)
|
|
31
|
+
else:
|
|
32
|
+
merged[key] = val
|
|
33
|
+
return merged
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
def load_config(*, path: Path | None = None) -> tuple[dict[str, Any], Path]:
|
|
37
|
+
"""Load config from disk, falling back to defaults on errors."""
|
|
38
|
+
config_path = path or user_config_path()
|
|
39
|
+
config: dict[str, Any] = deepcopy(DEFAULT_CONFIG)
|
|
40
|
+
|
|
41
|
+
try:
|
|
42
|
+
if config_path.exists():
|
|
43
|
+
loaded = json.loads(config_path.read_text(encoding="utf-8"))
|
|
44
|
+
if isinstance(loaded, dict):
|
|
45
|
+
config = _deep_merge(config, loaded)
|
|
46
|
+
except Exception as exc: # noqa: BLE001
|
|
47
|
+
print(f"Failed to load config ({config_path}): {exc}")
|
|
48
|
+
|
|
49
|
+
return config, config_path
|
|
50
|
+
|
|
51
|
+
|
|
52
|
+
def save_config(config: dict[str, Any], path: Path) -> None:
|
|
53
|
+
"""Persist config to disk, creating parent dirs as needed."""
|
|
54
|
+
try:
|
|
55
|
+
path.parent.mkdir(parents=True, exist_ok=True)
|
|
56
|
+
path.write_text(json.dumps(config, indent=2), encoding="utf-8")
|
|
57
|
+
except Exception as exc: # noqa: BLE001
|
|
58
|
+
print(f"Failed to save config ({path}): {exc}")
|