zombie-escape 1.7.1__py3-none-any.whl → 1.8.0__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/entities.py +172 -65
- zombie_escape/gameplay/constants.py +0 -6
- zombie_escape/gameplay/layout.py +5 -0
- zombie_escape/gameplay/movement.py +43 -3
- zombie_escape/gameplay/spawn.py +38 -31
- zombie_escape/gameplay/state.py +2 -0
- zombie_escape/gameplay/survivors.py +46 -15
- zombie_escape/input_utils.py +167 -0
- zombie_escape/level_blueprints.py +28 -0
- zombie_escape/locales/ui.en.json +41 -9
- zombie_escape/locales/ui.ja.json +40 -8
- zombie_escape/localization.py +28 -0
- zombie_escape/models.py +2 -0
- zombie_escape/screens/game_over.py +4 -0
- zombie_escape/screens/gameplay.py +78 -17
- zombie_escape/screens/settings.py +124 -13
- zombie_escape/screens/title.py +111 -0
- zombie_escape/stage_constants.py +26 -1
- zombie_escape/zombie_escape.py +3 -0
- {zombie_escape-1.7.1.dist-info → zombie_escape-1.8.0.dist-info}/METADATA +4 -3
- zombie_escape-1.8.0.dist-info/RECORD +46 -0
- zombie_escape-1.7.1.dist-info/RECORD +0 -45
- {zombie_escape-1.7.1.dist-info → zombie_escape-1.8.0.dist-info}/WHEEL +0 -0
- {zombie_escape-1.7.1.dist-info → zombie_escape-1.8.0.dist-info}/entry_points.txt +0 -0
- {zombie_escape-1.7.1.dist-info → zombie_escape-1.8.0.dist-info}/licenses/LICENSE.txt +0 -0
|
@@ -31,6 +31,17 @@ from ..gameplay import (
|
|
|
31
31
|
update_footprints,
|
|
32
32
|
update_survival_timer,
|
|
33
33
|
)
|
|
34
|
+
from ..input_utils import (
|
|
35
|
+
CONTROLLER_BUTTON_DOWN,
|
|
36
|
+
CONTROLLER_DEVICE_ADDED,
|
|
37
|
+
CONTROLLER_DEVICE_REMOVED,
|
|
38
|
+
init_first_controller,
|
|
39
|
+
init_first_joystick,
|
|
40
|
+
is_accel_active,
|
|
41
|
+
is_select_event,
|
|
42
|
+
is_start_event,
|
|
43
|
+
read_gamepad_move,
|
|
44
|
+
)
|
|
34
45
|
from ..gameplay.spawn import _alive_waiting_cars
|
|
35
46
|
from ..entities import build_wall_index
|
|
36
47
|
from ..localization import translate as tr
|
|
@@ -89,6 +100,8 @@ def gameplay_screen(
|
|
|
89
100
|
paused_focus = False
|
|
90
101
|
ignore_focus_loss_until = 0
|
|
91
102
|
last_fov_target = None
|
|
103
|
+
controller = init_first_controller()
|
|
104
|
+
joystick = init_first_joystick() if controller is None else None
|
|
92
105
|
|
|
93
106
|
layout_data = generate_level_from_blueprint(game_data, config)
|
|
94
107
|
sync_ambient_palette_with_flashlights(game_data, force=True)
|
|
@@ -180,7 +193,22 @@ def gameplay_screen(
|
|
|
180
193
|
paused_focus = False
|
|
181
194
|
if event.type == pygame.MOUSEBUTTONDOWN:
|
|
182
195
|
paused_focus = False
|
|
183
|
-
|
|
196
|
+
if event.type == pygame.JOYDEVICEADDED or (
|
|
197
|
+
CONTROLLER_DEVICE_ADDED is not None
|
|
198
|
+
and event.type == CONTROLLER_DEVICE_ADDED
|
|
199
|
+
):
|
|
200
|
+
if controller is None:
|
|
201
|
+
controller = init_first_controller()
|
|
202
|
+
if controller is None:
|
|
203
|
+
joystick = init_first_joystick()
|
|
204
|
+
if event.type == pygame.JOYDEVICEREMOVED or (
|
|
205
|
+
CONTROLLER_DEVICE_REMOVED is not None
|
|
206
|
+
and event.type == CONTROLLER_DEVICE_REMOVED
|
|
207
|
+
):
|
|
208
|
+
if controller and not controller.get_init():
|
|
209
|
+
controller = None
|
|
210
|
+
if joystick and not joystick.get_init():
|
|
211
|
+
joystick = None
|
|
184
212
|
if event.type == pygame.KEYDOWN:
|
|
185
213
|
if event.key == pygame.K_s and (
|
|
186
214
|
pygame.key.get_mods() & pygame.KMOD_CTRL
|
|
@@ -192,10 +220,40 @@ def gameplay_screen(
|
|
|
192
220
|
}
|
|
193
221
|
print("STATE DEBUG:", state_snapshot)
|
|
194
222
|
continue
|
|
195
|
-
if
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
223
|
+
if debug_mode:
|
|
224
|
+
if event.key == pygame.K_ESCAPE:
|
|
225
|
+
return ScreenTransition(ScreenID.TITLE)
|
|
226
|
+
if event.key == pygame.K_p:
|
|
227
|
+
paused_manual = not paused_manual
|
|
228
|
+
continue
|
|
229
|
+
if paused_manual:
|
|
230
|
+
if event.key == pygame.K_ESCAPE:
|
|
231
|
+
return ScreenTransition(ScreenID.TITLE)
|
|
232
|
+
if event.key == pygame.K_p:
|
|
233
|
+
paused_manual = False
|
|
234
|
+
continue
|
|
235
|
+
if event.key in (pygame.K_ESCAPE, pygame.K_p):
|
|
236
|
+
paused_manual = True
|
|
237
|
+
continue
|
|
238
|
+
if event.type == pygame.JOYBUTTONDOWN or (
|
|
239
|
+
CONTROLLER_BUTTON_DOWN is not None
|
|
240
|
+
and event.type == CONTROLLER_BUTTON_DOWN
|
|
241
|
+
):
|
|
242
|
+
if debug_mode:
|
|
243
|
+
if is_select_event(event):
|
|
244
|
+
return ScreenTransition(ScreenID.TITLE)
|
|
245
|
+
if is_start_event(event):
|
|
246
|
+
paused_manual = not paused_manual
|
|
247
|
+
continue
|
|
248
|
+
if paused_manual:
|
|
249
|
+
if is_select_event(event):
|
|
250
|
+
return ScreenTransition(ScreenID.TITLE)
|
|
251
|
+
if is_start_event(event):
|
|
252
|
+
paused_manual = False
|
|
253
|
+
continue
|
|
254
|
+
if is_select_event(event) or is_start_event(event):
|
|
255
|
+
paused_manual = True
|
|
256
|
+
continue
|
|
199
257
|
|
|
200
258
|
paused = paused_manual or paused_focus
|
|
201
259
|
if paused:
|
|
@@ -211,17 +269,19 @@ def gameplay_screen(
|
|
|
211
269
|
if show_pause_overlay:
|
|
212
270
|
overlay = pygame.Surface((screen_width, screen_height), pygame.SRCALPHA)
|
|
213
271
|
overlay.fill((0, 0, 0, 150))
|
|
272
|
+
pause_radius = 53
|
|
273
|
+
cx = screen_width // 2
|
|
274
|
+
cy = screen_height // 2 - 18
|
|
214
275
|
pygame.draw.circle(
|
|
215
276
|
overlay,
|
|
216
277
|
LIGHT_GRAY,
|
|
217
|
-
(
|
|
218
|
-
|
|
278
|
+
(cx, cy),
|
|
279
|
+
pause_radius,
|
|
219
280
|
width=3,
|
|
220
281
|
)
|
|
221
|
-
bar_width =
|
|
222
|
-
bar_height =
|
|
223
|
-
gap =
|
|
224
|
-
cx, cy = screen_width // 2, screen_height // 2
|
|
282
|
+
bar_width = 10
|
|
283
|
+
bar_height = 38
|
|
284
|
+
gap = 12
|
|
225
285
|
pygame.draw.rect(
|
|
226
286
|
overlay,
|
|
227
287
|
LIGHT_GRAY,
|
|
@@ -238,12 +298,12 @@ def gameplay_screen(
|
|
|
238
298
|
tr("hud.paused"),
|
|
239
299
|
18,
|
|
240
300
|
WHITE,
|
|
241
|
-
(screen_width // 2,
|
|
301
|
+
(screen_width // 2, 28),
|
|
242
302
|
)
|
|
243
303
|
show_message(
|
|
244
304
|
screen,
|
|
245
|
-
tr("hud.
|
|
246
|
-
|
|
305
|
+
tr("hud.pause_hint"),
|
|
306
|
+
16,
|
|
247
307
|
LIGHT_GRAY,
|
|
248
308
|
(screen_width // 2, screen_height // 2 + 70),
|
|
249
309
|
)
|
|
@@ -254,8 +314,8 @@ def gameplay_screen(
|
|
|
254
314
|
accel_allowed = not (
|
|
255
315
|
game_data.state.game_over or game_data.state.game_won
|
|
256
316
|
)
|
|
257
|
-
accel_active = accel_allowed and (
|
|
258
|
-
keys
|
|
317
|
+
accel_active = accel_allowed and is_accel_active(
|
|
318
|
+
keys, controller, joystick
|
|
259
319
|
)
|
|
260
320
|
game_data.state.time_accel_active = accel_active
|
|
261
321
|
substeps = SURVIVAL_TIME_ACCEL_SUBSTEPS if accel_active else 1
|
|
@@ -271,8 +331,9 @@ def gameplay_screen(
|
|
|
271
331
|
if player_ref is None:
|
|
272
332
|
break
|
|
273
333
|
car_ref = game_data.car
|
|
334
|
+
pad_vector = read_gamepad_move(controller, joystick)
|
|
274
335
|
player_dx, player_dy, car_dx, car_dy = process_player_input(
|
|
275
|
-
keys, player_ref, car_ref
|
|
336
|
+
keys, player_ref, car_ref, pad_input=pad_vector
|
|
276
337
|
)
|
|
277
338
|
update_entities(
|
|
278
339
|
game_data,
|
|
@@ -10,6 +10,19 @@ from pygame import surface, time
|
|
|
10
10
|
from ..colors import BLACK, GREEN, LIGHT_GRAY, WHITE
|
|
11
11
|
from ..config import DEFAULT_CONFIG, save_config
|
|
12
12
|
from ..font_utils import load_font
|
|
13
|
+
from ..input_utils import (
|
|
14
|
+
CONTROLLER_BUTTON_DOWN,
|
|
15
|
+
CONTROLLER_BUTTON_DPAD_DOWN,
|
|
16
|
+
CONTROLLER_BUTTON_DPAD_LEFT,
|
|
17
|
+
CONTROLLER_BUTTON_DPAD_RIGHT,
|
|
18
|
+
CONTROLLER_BUTTON_DPAD_UP,
|
|
19
|
+
CONTROLLER_DEVICE_ADDED,
|
|
20
|
+
CONTROLLER_DEVICE_REMOVED,
|
|
21
|
+
init_first_controller,
|
|
22
|
+
init_first_joystick,
|
|
23
|
+
is_confirm_event,
|
|
24
|
+
is_select_event,
|
|
25
|
+
)
|
|
13
26
|
from ..localization import (
|
|
14
27
|
get_font_settings,
|
|
15
28
|
get_language,
|
|
@@ -95,6 +108,8 @@ def settings_screen(
|
|
|
95
108
|
selected = 0
|
|
96
109
|
languages = language_options()
|
|
97
110
|
language_codes = [lang.code for lang in languages]
|
|
111
|
+
controller = init_first_controller()
|
|
112
|
+
joystick = init_first_joystick() if controller is None else None
|
|
98
113
|
|
|
99
114
|
def _ensure_parent(path: tuple[str, ...]) -> tuple[dict, str]:
|
|
100
115
|
node = working
|
|
@@ -143,6 +158,15 @@ def settings_screen(
|
|
|
143
158
|
|
|
144
159
|
def build_sections() -> list[dict]:
|
|
145
160
|
return [
|
|
161
|
+
{
|
|
162
|
+
"label": tr("settings.sections.menu"),
|
|
163
|
+
"rows": [
|
|
164
|
+
{
|
|
165
|
+
"type": "action",
|
|
166
|
+
"label": tr("settings.rows.return_to_title"),
|
|
167
|
+
}
|
|
168
|
+
],
|
|
169
|
+
},
|
|
146
170
|
{
|
|
147
171
|
"label": tr("settings.sections.localization"),
|
|
148
172
|
"rows": [
|
|
@@ -210,6 +234,10 @@ def settings_screen(
|
|
|
210
234
|
row_count = len(rows)
|
|
211
235
|
last_language = get_language()
|
|
212
236
|
|
|
237
|
+
def _exit_settings() -> dict[str, Any]:
|
|
238
|
+
save_config(working, config_path)
|
|
239
|
+
return working
|
|
240
|
+
|
|
213
241
|
while True:
|
|
214
242
|
for event in pygame.event.get():
|
|
215
243
|
if event.type == pygame.QUIT:
|
|
@@ -217,6 +245,24 @@ def settings_screen(
|
|
|
217
245
|
if event.type in (pygame.WINDOWSIZECHANGED, pygame.VIDEORESIZE):
|
|
218
246
|
sync_window_size(event)
|
|
219
247
|
continue
|
|
248
|
+
if event.type == pygame.JOYDEVICEADDED or (
|
|
249
|
+
CONTROLLER_DEVICE_ADDED is not None
|
|
250
|
+
and event.type == CONTROLLER_DEVICE_ADDED
|
|
251
|
+
):
|
|
252
|
+
if controller is None:
|
|
253
|
+
controller = init_first_controller()
|
|
254
|
+
if controller is None:
|
|
255
|
+
joystick = init_first_joystick()
|
|
256
|
+
if event.type == pygame.JOYDEVICEREMOVED or (
|
|
257
|
+
CONTROLLER_DEVICE_REMOVED is not None
|
|
258
|
+
and event.type == CONTROLLER_DEVICE_REMOVED
|
|
259
|
+
):
|
|
260
|
+
if controller and not controller.get_init():
|
|
261
|
+
controller = None
|
|
262
|
+
if joystick and not joystick.get_init():
|
|
263
|
+
joystick = None
|
|
264
|
+
if is_select_event(event):
|
|
265
|
+
return _exit_settings()
|
|
220
266
|
if event.type == pygame.KEYDOWN:
|
|
221
267
|
if event.key == pygame.K_LEFTBRACKET:
|
|
222
268
|
nudge_window_scale(0.5)
|
|
@@ -228,8 +274,7 @@ def settings_screen(
|
|
|
228
274
|
toggle_fullscreen()
|
|
229
275
|
continue
|
|
230
276
|
if event.key in (pygame.K_ESCAPE, pygame.K_BACKSPACE):
|
|
231
|
-
|
|
232
|
-
return working
|
|
277
|
+
return _exit_settings()
|
|
233
278
|
if event.key in (pygame.K_UP, pygame.K_w):
|
|
234
279
|
selected = (selected - 1) % row_count
|
|
235
280
|
if event.key in (pygame.K_DOWN, pygame.K_s):
|
|
@@ -237,16 +282,18 @@ def settings_screen(
|
|
|
237
282
|
current_row = rows[selected]
|
|
238
283
|
row_type = current_row.get("type", "toggle")
|
|
239
284
|
if event.key in (pygame.K_SPACE, pygame.K_RETURN):
|
|
285
|
+
if row_type == "action":
|
|
286
|
+
return _exit_settings()
|
|
240
287
|
if row_type == "toggle":
|
|
241
288
|
toggle_row(current_row)
|
|
242
289
|
elif row_type == "choice":
|
|
243
290
|
cycle_choice(current_row, 1)
|
|
244
|
-
if event.key == pygame.K_LEFT:
|
|
291
|
+
if event.key == pygame.K_LEFT and row_type != "action":
|
|
245
292
|
if row_type == "toggle":
|
|
246
293
|
set_easy_value(current_row, True)
|
|
247
294
|
elif row_type == "choice":
|
|
248
295
|
cycle_choice(current_row, -1)
|
|
249
|
-
if event.key == pygame.K_RIGHT:
|
|
296
|
+
if event.key == pygame.K_RIGHT and row_type != "action":
|
|
250
297
|
if row_type == "toggle":
|
|
251
298
|
set_easy_value(current_row, False)
|
|
252
299
|
elif row_type == "choice":
|
|
@@ -254,6 +301,66 @@ def settings_screen(
|
|
|
254
301
|
if event.key == pygame.K_r:
|
|
255
302
|
working = copy.deepcopy(DEFAULT_CONFIG)
|
|
256
303
|
set_language(working.get("language"))
|
|
304
|
+
if event.type == pygame.JOYBUTTONDOWN or (
|
|
305
|
+
CONTROLLER_BUTTON_DOWN is not None
|
|
306
|
+
and event.type == CONTROLLER_BUTTON_DOWN
|
|
307
|
+
):
|
|
308
|
+
current_row = rows[selected]
|
|
309
|
+
row_type = current_row.get("type", "toggle")
|
|
310
|
+
if is_confirm_event(event):
|
|
311
|
+
if row_type == "action":
|
|
312
|
+
return _exit_settings()
|
|
313
|
+
if row_type == "toggle":
|
|
314
|
+
toggle_row(current_row)
|
|
315
|
+
elif row_type == "choice":
|
|
316
|
+
cycle_choice(current_row, 1)
|
|
317
|
+
if CONTROLLER_BUTTON_DOWN is not None and event.type == CONTROLLER_BUTTON_DOWN:
|
|
318
|
+
if (
|
|
319
|
+
CONTROLLER_BUTTON_DPAD_UP is not None
|
|
320
|
+
and event.button == CONTROLLER_BUTTON_DPAD_UP
|
|
321
|
+
):
|
|
322
|
+
selected = (selected - 1) % row_count
|
|
323
|
+
if (
|
|
324
|
+
CONTROLLER_BUTTON_DPAD_DOWN is not None
|
|
325
|
+
and event.button == CONTROLLER_BUTTON_DPAD_DOWN
|
|
326
|
+
):
|
|
327
|
+
selected = (selected + 1) % row_count
|
|
328
|
+
if (
|
|
329
|
+
CONTROLLER_BUTTON_DPAD_LEFT is not None
|
|
330
|
+
and event.button == CONTROLLER_BUTTON_DPAD_LEFT
|
|
331
|
+
and row_type != "action"
|
|
332
|
+
):
|
|
333
|
+
if row_type == "toggle":
|
|
334
|
+
set_easy_value(current_row, True)
|
|
335
|
+
elif row_type == "choice":
|
|
336
|
+
cycle_choice(current_row, -1)
|
|
337
|
+
if (
|
|
338
|
+
CONTROLLER_BUTTON_DPAD_RIGHT is not None
|
|
339
|
+
and event.button == CONTROLLER_BUTTON_DPAD_RIGHT
|
|
340
|
+
and row_type != "action"
|
|
341
|
+
):
|
|
342
|
+
if row_type == "toggle":
|
|
343
|
+
set_easy_value(current_row, False)
|
|
344
|
+
elif row_type == "choice":
|
|
345
|
+
cycle_choice(current_row, 1)
|
|
346
|
+
if event.type == pygame.JOYHATMOTION:
|
|
347
|
+
current_row = rows[selected]
|
|
348
|
+
row_type = current_row.get("type", "toggle")
|
|
349
|
+
hat_x, hat_y = event.value
|
|
350
|
+
if hat_y == 1:
|
|
351
|
+
selected = (selected - 1) % row_count
|
|
352
|
+
elif hat_y == -1:
|
|
353
|
+
selected = (selected + 1) % row_count
|
|
354
|
+
if hat_x == -1 and row_type != "action":
|
|
355
|
+
if row_type == "toggle":
|
|
356
|
+
set_easy_value(current_row, True)
|
|
357
|
+
elif row_type == "choice":
|
|
358
|
+
cycle_choice(current_row, -1)
|
|
359
|
+
elif hat_x == 1 and row_type != "action":
|
|
360
|
+
if row_type == "toggle":
|
|
361
|
+
set_easy_value(current_row, False)
|
|
362
|
+
elif row_type == "choice":
|
|
363
|
+
cycle_choice(current_row, 1)
|
|
257
364
|
|
|
258
365
|
current_language = get_language()
|
|
259
366
|
if current_language != last_language:
|
|
@@ -284,8 +391,8 @@ def settings_screen(
|
|
|
284
391
|
)
|
|
285
392
|
highlight_color = (70, 70, 70)
|
|
286
393
|
|
|
287
|
-
row_height =
|
|
288
|
-
start_y =
|
|
394
|
+
row_height = 20
|
|
395
|
+
start_y = 46
|
|
289
396
|
|
|
290
397
|
segment_width = 30
|
|
291
398
|
segment_height = 18
|
|
@@ -294,7 +401,7 @@ def settings_screen(
|
|
|
294
401
|
|
|
295
402
|
column_margin = 24
|
|
296
403
|
column_width = screen_width // 2 - column_margin * 2
|
|
297
|
-
section_spacing =
|
|
404
|
+
section_spacing = 4
|
|
298
405
|
row_indent = 12
|
|
299
406
|
value_padding = 20
|
|
300
407
|
|
|
@@ -305,7 +412,7 @@ def settings_screen(
|
|
|
305
412
|
section["label"], False, LIGHT_GRAY
|
|
306
413
|
)
|
|
307
414
|
section_states[section["label"]] = {
|
|
308
|
-
"next_y": y_cursor + header_surface.get_height() +
|
|
415
|
+
"next_y": y_cursor + header_surface.get_height() + 4,
|
|
309
416
|
"header_surface": header_surface,
|
|
310
417
|
"header_pos": (column_margin, y_cursor),
|
|
311
418
|
}
|
|
@@ -324,9 +431,13 @@ def settings_screen(
|
|
|
324
431
|
state = section_states[section_label]
|
|
325
432
|
col_x = column_margin + row_indent
|
|
326
433
|
row_width = column_width - row_indent + value_padding
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
434
|
+
row_type = row.get("type", "toggle")
|
|
435
|
+
value = None
|
|
436
|
+
if row_type != "action":
|
|
437
|
+
value = _get_value(
|
|
438
|
+
row["path"],
|
|
439
|
+
row.get("easy_value", row.get("choices", [None])[0]),
|
|
440
|
+
)
|
|
330
441
|
row_y_current = state["next_y"]
|
|
331
442
|
state["next_y"] += row_height
|
|
332
443
|
|
|
@@ -344,7 +455,7 @@ def settings_screen(
|
|
|
344
455
|
)
|
|
345
456
|
)
|
|
346
457
|
screen.blit(label_surface, label_rect)
|
|
347
|
-
if
|
|
458
|
+
if row_type == "choice":
|
|
348
459
|
display_fn = row.get("get_display")
|
|
349
460
|
display_text = (
|
|
350
461
|
display_fn(value)
|
|
@@ -359,7 +470,7 @@ def settings_screen(
|
|
|
359
470
|
)
|
|
360
471
|
)
|
|
361
472
|
screen.blit(value_surface, value_rect)
|
|
362
|
-
|
|
473
|
+
elif row_type == "toggle":
|
|
363
474
|
slider_y = row_y_current + (row_height - segment_height) // 2 - 2
|
|
364
475
|
slider_x = col_x + row_width - segment_total_width
|
|
365
476
|
left_rect = pygame.Rect(
|
zombie_escape/screens/title.py
CHANGED
|
@@ -14,6 +14,18 @@ from ..models import Stage
|
|
|
14
14
|
from ..progress import load_progress
|
|
15
15
|
from ..render import show_message
|
|
16
16
|
from ..rng import generate_seed
|
|
17
|
+
from ..input_utils import (
|
|
18
|
+
CONTROLLER_BUTTON_DOWN,
|
|
19
|
+
CONTROLLER_BUTTON_DPAD_DOWN,
|
|
20
|
+
CONTROLLER_BUTTON_DPAD_LEFT,
|
|
21
|
+
CONTROLLER_BUTTON_DPAD_RIGHT,
|
|
22
|
+
CONTROLLER_BUTTON_DPAD_UP,
|
|
23
|
+
CONTROLLER_DEVICE_ADDED,
|
|
24
|
+
CONTROLLER_DEVICE_REMOVED,
|
|
25
|
+
init_first_controller,
|
|
26
|
+
init_first_joystick,
|
|
27
|
+
is_confirm_event,
|
|
28
|
+
)
|
|
17
29
|
from ..screens import (
|
|
18
30
|
ScreenID,
|
|
19
31
|
ScreenTransition,
|
|
@@ -193,6 +205,8 @@ def title_screen(
|
|
|
193
205
|
0,
|
|
194
206
|
)
|
|
195
207
|
selected = min(selected_stage_index, len(options) - 1)
|
|
208
|
+
controller = init_first_controller()
|
|
209
|
+
joystick = init_first_joystick() if controller is None else None
|
|
196
210
|
|
|
197
211
|
while True:
|
|
198
212
|
for event in pygame.event.get():
|
|
@@ -205,6 +219,22 @@ def title_screen(
|
|
|
205
219
|
if event.type in (pygame.WINDOWSIZECHANGED, pygame.VIDEORESIZE):
|
|
206
220
|
sync_window_size(event)
|
|
207
221
|
continue
|
|
222
|
+
if event.type == pygame.JOYDEVICEADDED or (
|
|
223
|
+
CONTROLLER_DEVICE_ADDED is not None
|
|
224
|
+
and event.type == CONTROLLER_DEVICE_ADDED
|
|
225
|
+
):
|
|
226
|
+
if controller is None:
|
|
227
|
+
controller = init_first_controller()
|
|
228
|
+
if controller is None:
|
|
229
|
+
joystick = init_first_joystick()
|
|
230
|
+
if event.type == pygame.JOYDEVICEREMOVED or (
|
|
231
|
+
CONTROLLER_DEVICE_REMOVED is not None
|
|
232
|
+
and event.type == CONTROLLER_DEVICE_REMOVED
|
|
233
|
+
):
|
|
234
|
+
if controller and not controller.get_init():
|
|
235
|
+
controller = None
|
|
236
|
+
if joystick and not joystick.get_init():
|
|
237
|
+
joystick = None
|
|
208
238
|
if event.type == pygame.KEYDOWN:
|
|
209
239
|
if event.key == pygame.K_BACKSPACE:
|
|
210
240
|
current_seed_text = _generate_auto_seed_text()
|
|
@@ -273,6 +303,87 @@ def title_screen(
|
|
|
273
303
|
seed_text=current_seed_text,
|
|
274
304
|
seed_is_auto=current_seed_auto,
|
|
275
305
|
)
|
|
306
|
+
if event.type == pygame.JOYBUTTONDOWN or (
|
|
307
|
+
CONTROLLER_BUTTON_DOWN is not None
|
|
308
|
+
and event.type == CONTROLLER_BUTTON_DOWN
|
|
309
|
+
):
|
|
310
|
+
if is_confirm_event(event):
|
|
311
|
+
current = options[selected]
|
|
312
|
+
if current["type"] == "stage" and current.get("available"):
|
|
313
|
+
seed_value = (
|
|
314
|
+
int(current_seed_text) if current_seed_text else None
|
|
315
|
+
)
|
|
316
|
+
return ScreenTransition(
|
|
317
|
+
ScreenID.GAMEPLAY,
|
|
318
|
+
stage=current["stage"],
|
|
319
|
+
seed=seed_value,
|
|
320
|
+
seed_text=current_seed_text,
|
|
321
|
+
seed_is_auto=current_seed_auto,
|
|
322
|
+
)
|
|
323
|
+
if current["type"] == "settings":
|
|
324
|
+
return ScreenTransition(
|
|
325
|
+
ScreenID.SETTINGS,
|
|
326
|
+
seed_text=current_seed_text,
|
|
327
|
+
seed_is_auto=current_seed_auto,
|
|
328
|
+
)
|
|
329
|
+
if current["type"] == "readme":
|
|
330
|
+
_open_readme_link()
|
|
331
|
+
continue
|
|
332
|
+
if current["type"] == "quit":
|
|
333
|
+
return ScreenTransition(
|
|
334
|
+
ScreenID.EXIT,
|
|
335
|
+
seed_text=current_seed_text,
|
|
336
|
+
seed_is_auto=current_seed_auto,
|
|
337
|
+
)
|
|
338
|
+
if CONTROLLER_BUTTON_DOWN is not None and event.type == CONTROLLER_BUTTON_DOWN:
|
|
339
|
+
if (
|
|
340
|
+
CONTROLLER_BUTTON_DPAD_UP is not None
|
|
341
|
+
and event.button == CONTROLLER_BUTTON_DPAD_UP
|
|
342
|
+
):
|
|
343
|
+
selected = (selected - 1) % len(options)
|
|
344
|
+
if (
|
|
345
|
+
CONTROLLER_BUTTON_DPAD_DOWN is not None
|
|
346
|
+
and event.button == CONTROLLER_BUTTON_DPAD_DOWN
|
|
347
|
+
):
|
|
348
|
+
selected = (selected + 1) % len(options)
|
|
349
|
+
if (
|
|
350
|
+
CONTROLLER_BUTTON_DPAD_LEFT is not None
|
|
351
|
+
and event.button == CONTROLLER_BUTTON_DPAD_LEFT
|
|
352
|
+
):
|
|
353
|
+
if current_page > 0:
|
|
354
|
+
current_page -= 1
|
|
355
|
+
options, stage_options = _build_options(current_page)
|
|
356
|
+
selected = 0
|
|
357
|
+
if (
|
|
358
|
+
CONTROLLER_BUTTON_DPAD_RIGHT is not None
|
|
359
|
+
and event.button == CONTROLLER_BUTTON_DPAD_RIGHT
|
|
360
|
+
):
|
|
361
|
+
if (
|
|
362
|
+
current_page < len(stage_pages) - 1
|
|
363
|
+
and _page_available(current_page + 1)
|
|
364
|
+
):
|
|
365
|
+
current_page += 1
|
|
366
|
+
options, stage_options = _build_options(current_page)
|
|
367
|
+
selected = 0
|
|
368
|
+
if event.type == pygame.JOYHATMOTION:
|
|
369
|
+
hat_x, hat_y = event.value
|
|
370
|
+
if hat_y == 1:
|
|
371
|
+
selected = (selected - 1) % len(options)
|
|
372
|
+
elif hat_y == -1:
|
|
373
|
+
selected = (selected + 1) % len(options)
|
|
374
|
+
if hat_x == -1:
|
|
375
|
+
if current_page > 0:
|
|
376
|
+
current_page -= 1
|
|
377
|
+
options, stage_options = _build_options(current_page)
|
|
378
|
+
selected = 0
|
|
379
|
+
elif hat_x == 1:
|
|
380
|
+
if (
|
|
381
|
+
current_page < len(stage_pages) - 1
|
|
382
|
+
and _page_available(current_page + 1)
|
|
383
|
+
):
|
|
384
|
+
current_page += 1
|
|
385
|
+
options, stage_options = _build_options(current_page)
|
|
386
|
+
selected = 0
|
|
276
387
|
|
|
277
388
|
screen.fill(BLACK)
|
|
278
389
|
title_text = tr("game.title")
|
zombie_escape/stage_constants.py
CHANGED
|
@@ -67,6 +67,7 @@ STAGES: list[Stage] = [
|
|
|
67
67
|
zombie_normal_ratio=0.4,
|
|
68
68
|
zombie_tracker_ratio=0.6,
|
|
69
69
|
zombie_aging_duration_frames=ZOMBIE_AGING_DURATION_FRAMES * 2,
|
|
70
|
+
initial_interior_spawn_rate=0.01,
|
|
70
71
|
),
|
|
71
72
|
Stage(
|
|
72
73
|
id="stage7",
|
|
@@ -82,6 +83,7 @@ STAGES: list[Stage] = [
|
|
|
82
83
|
zombie_tracker_ratio=0.3,
|
|
83
84
|
zombie_wall_follower_ratio=0.3,
|
|
84
85
|
zombie_aging_duration_frames=ZOMBIE_AGING_DURATION_FRAMES * 2,
|
|
86
|
+
initial_interior_spawn_rate=0.01,
|
|
85
87
|
),
|
|
86
88
|
Stage(
|
|
87
89
|
id="stage8",
|
|
@@ -97,20 +99,43 @@ STAGES: list[Stage] = [
|
|
|
97
99
|
zombie_tracker_ratio=0.3,
|
|
98
100
|
zombie_wall_follower_ratio=0.7,
|
|
99
101
|
zombie_aging_duration_frames=ZOMBIE_AGING_DURATION_FRAMES * 2,
|
|
102
|
+
initial_interior_spawn_rate=0.01,
|
|
100
103
|
),
|
|
101
104
|
Stage(
|
|
102
105
|
id="stage9",
|
|
103
106
|
name_key="stages.stage9.name",
|
|
104
107
|
description_key="stages.stage9.description",
|
|
105
|
-
available=
|
|
108
|
+
available=True,
|
|
106
109
|
rescue_stage=True,
|
|
107
110
|
tile_size=35,
|
|
108
111
|
requires_fuel=True,
|
|
109
112
|
exterior_spawn_weight=0.4,
|
|
110
113
|
interior_spawn_weight=0.6,
|
|
111
114
|
waiting_car_target_count=1,
|
|
115
|
+
zombie_normal_ratio=0,
|
|
116
|
+
zombie_tracker_ratio=0.3,
|
|
117
|
+
zombie_wall_follower_ratio=0.7,
|
|
112
118
|
survivor_spawn_rate=SURVIVOR_SPAWN_RATE,
|
|
113
119
|
zombie_aging_duration_frames=ZOMBIE_AGING_DURATION_FRAMES * 2,
|
|
120
|
+
initial_interior_spawn_rate=0.01,
|
|
121
|
+
),
|
|
122
|
+
Stage(
|
|
123
|
+
id="stage10",
|
|
124
|
+
name_key="stages.stage10.name",
|
|
125
|
+
description_key="stages.stage10.description",
|
|
126
|
+
available=True,
|
|
127
|
+
rescue_stage=True,
|
|
128
|
+
tile_size=40,
|
|
129
|
+
wall_algorithm="sparse",
|
|
130
|
+
exterior_spawn_weight=0.7,
|
|
131
|
+
interior_spawn_weight=0.3,
|
|
132
|
+
zombie_normal_ratio=0.4,
|
|
133
|
+
zombie_tracker_ratio=0.4,
|
|
134
|
+
zombie_wall_follower_ratio=0.2,
|
|
135
|
+
zombie_aging_duration_frames=ZOMBIE_AGING_DURATION_FRAMES * 2,
|
|
136
|
+
initial_interior_spawn_rate=0.02,
|
|
137
|
+
waiting_car_target_count=1,
|
|
138
|
+
survivor_spawn_rate=0.35,
|
|
114
139
|
),
|
|
115
140
|
]
|
|
116
141
|
DEFAULT_STAGE_ID = "stage1"
|
zombie_escape/zombie_escape.py
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: zombie-escape
|
|
3
|
-
Version: 1.
|
|
3
|
+
Version: 1.8.0
|
|
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>
|
|
@@ -46,10 +46,11 @@ This game is a simple 2D top-down action game where the player aims to escape by
|
|
|
46
46
|
|
|
47
47
|
- **Player/Car Movement:** `W` / `↑` (Up), `A` / `←` (Left), `S` / `↓` (Down), `D` / `→` (Right)
|
|
48
48
|
- **Enter Car:** Overlap the player with the car.
|
|
49
|
-
- **
|
|
49
|
+
- **Pause:** `P`/Start or `ESC`/Select
|
|
50
|
+
- **Quit Game:** `ESC`/Select (from pause)
|
|
50
51
|
- **Restart:** `R` key (on Game Over/Clear screen)
|
|
51
52
|
- **Window/Fullscreen (title/settings only):** `[` to shrink, `]` to enlarge, `F` to toggle fullscreen
|
|
52
|
-
- **Time Acceleration:** Hold either `Shift` key to run the entire world 4x faster; release to return to normal speed.
|
|
53
|
+
- **Time Acceleration:** Hold either `Shift` key or `R1` to run the entire world 4x faster; release to return to normal speed.
|
|
53
54
|
|
|
54
55
|
## Title Screen
|
|
55
56
|
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
zombie_escape/__about__.py,sha256=LlsY4VV4kb_yglExHx0yDj65RPKm5mVRb6gDT_MXY-U,134
|
|
2
|
+
zombie_escape/__init__.py,sha256=YSQnUghet8jxSvaGmKfzHfXXLlnvWh_xk10WGTDO2HM,173
|
|
3
|
+
zombie_escape/colors.py,sha256=Cmu3wwSQgMqu75bTtsD3G85daQd96zNErnvGfXi3iVM,5771
|
|
4
|
+
zombie_escape/config.py,sha256=Ncvsz6HzBknSjecorkm7CrkrzWUIksD30ykLPueanyw,2008
|
|
5
|
+
zombie_escape/entities.py,sha256=nQCy0NXaGBTvwajoBzJvKE3WpP3C_a9ClXGmhzF9fyU,55604
|
|
6
|
+
zombie_escape/entities_constants.py,sha256=WDQslNovvstSSavZCT0vrD4PESn0rVdKc1L4BqxLY0g,2851
|
|
7
|
+
zombie_escape/font_utils.py,sha256=kkjcSlCTG3jO5zf5XUnirpJ-iL_Eg8ahzjZYGijF2JY,1206
|
|
8
|
+
zombie_escape/gameplay_constants.py,sha256=I5g2xsd4Rck4d5tbWae2bm6Yfwp4ZAgujDLnDMsHxgM,758
|
|
9
|
+
zombie_escape/input_utils.py,sha256=0SHENZi5y-ybSxUX569RHihI_xbQWSI0FQ1q1ZE9U1c,5795
|
|
10
|
+
zombie_escape/level_blueprints.py,sha256=VZieyhG4kWd1l6u7_Owy2FxhYt63pWpvsmLx5yB8GOs,10609
|
|
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=r8EvhF800Cpk9_87B1ar6RIQeECDB9m7AKvIZRspZ4Y,3757
|
|
14
|
+
zombie_escape/progress.py,sha256=WCFc7JeMY6noBjnTIFyHrXQJSM1j8PwyPA7S8ZQwjTE,1713
|
|
15
|
+
zombie_escape/render.py,sha256=VHiz4woL-qWbeNPX0q7ZPZnaPB5BPqIBzL7AKxx7Us0,30862
|
|
16
|
+
zombie_escape/render_assets.py,sha256=66lRKQZ9fII6930WeQchIJRFeLLu7J3Cr155svpDrUM,16489
|
|
17
|
+
zombie_escape/render_constants.py,sha256=yJ26KA7DF6ndYG3kAFaDkL-ljN1MgWbSEhR6nmWzzWI,2315
|
|
18
|
+
zombie_escape/rng.py,sha256=gMAgpzYoNN1FxRG3aQ9fdXTDNAg48Rqz8YnB1nJ4Fpw,3787
|
|
19
|
+
zombie_escape/screen_constants.py,sha256=MJaTlSWfN4VtN6pMqPQ6LF34XdJm0wqYLuRwa1pQuAU,559
|
|
20
|
+
zombie_escape/stage_constants.py,sha256=0SE8EfzQL6qMQUFs3cygAronkHaNiAK2zSoxKJ8OPYA,4455
|
|
21
|
+
zombie_escape/zombie_escape.py,sha256=lyW-ZF2CTAO7s2yToQlGDGwBxW1WEI6qeOV-ylOxQU4,8939
|
|
22
|
+
zombie_escape/assets/fonts/Silkscreen-Regular.ttf,sha256=SVZ0CGAICeJRR-kiWsTzf0EOLfRadQaWxFAnUx-2Xxs,31960
|
|
23
|
+
zombie_escape/assets/fonts/misaki_gothic.ttf,sha256=CWPhHonV-kCaegSKUujqLWI9dkp5mEiPikKRERYRxOE,1171204
|
|
24
|
+
zombie_escape/gameplay/__init__.py,sha256=hv-37H7R7cQrK0Qr7FkcakwYME26pi31LC4r25s2Dxk,2349
|
|
25
|
+
zombie_escape/gameplay/ambient.py,sha256=hoCOz6ciyejU0nmJwdLqmVfaoo-01CrVSMRLpFMz93w,1446
|
|
26
|
+
zombie_escape/gameplay/constants.py,sha256=ZEBCiuDmGvaky8U34u1nD5rt0Ph2kDMIaYgNQBsLZDw,1009
|
|
27
|
+
zombie_escape/gameplay/footprints.py,sha256=LQ3wZNovzgO54kaGncZT4yrhnmoZVfROkTQ6wL_TVSY,1781
|
|
28
|
+
zombie_escape/gameplay/interactions.py,sha256=QFtjv-5t2G6ek4AC3y5XWl15XQl5q9oK80ODNvd_Zt8,13476
|
|
29
|
+
zombie_escape/gameplay/layout.py,sha256=8hQwELQ35-y-SkcIGuDsmNJEuvbPRhWIZbu1LSxoClI,6868
|
|
30
|
+
zombie_escape/gameplay/movement.py,sha256=Vi1Q6NTvn3HYPyBy_JEUl-nXGPULiamjDZT9seTPymo,9086
|
|
31
|
+
zombie_escape/gameplay/spawn.py,sha256=IH_r5VIq0_7Us1W6WX2zgOUIpRm62i1dXf8paXz8nr8,20079
|
|
32
|
+
zombie_escape/gameplay/state.py,sha256=UngfxpPxUppZKLeuynEuxJBFCH0Qv9mxFoFjq9789c8,4549
|
|
33
|
+
zombie_escape/gameplay/survivors.py,sha256=JRjnlrcuXhHlTaWm-3npnatlLAtoMWIS84-ZJMjLRCM,11920
|
|
34
|
+
zombie_escape/gameplay/utils.py,sha256=2IwsQ1Fv8qVUWMP9JZ77tCV1slEJrokkiXAvQi7u81k,4921
|
|
35
|
+
zombie_escape/locales/ui.en.json,sha256=T7w4CGzbMlVR3NAlGZM23vi3uDruUyBdNL3CJkPOtnw,5805
|
|
36
|
+
zombie_escape/locales/ui.ja.json,sha256=OzN4yz3LVWkuFtWe9U-VaO8T786w10kE2vD0hEZresI,6340
|
|
37
|
+
zombie_escape/screens/__init__.py,sha256=P5aDyqJ3ZXdTVi0AAsxK9scc0lPuydc-XHplNxpDbu0,8335
|
|
38
|
+
zombie_escape/screens/game_over.py,sha256=P33aqm-6lgGMs5kOlGOXMEgPEpJIh4IC1oUxKtRFiwc,6689
|
|
39
|
+
zombie_escape/screens/gameplay.py,sha256=XhIG9oC5iY4mS2Ul04aAuFxDDo21r-b7pbAYifv65zo,15652
|
|
40
|
+
zombie_escape/screens/settings.py,sha256=qgcnq8k-yeRbqweT9GsIefKWKvjOON_Gs2_SCZChY-8,21739
|
|
41
|
+
zombie_escape/screens/title.py,sha256=1LUEMTmFLmJNL0TKTbnP2oRGWb6zUV28d8sQQo8i3Po,23118
|
|
42
|
+
zombie_escape-1.8.0.dist-info/METADATA,sha256=WdW0kIC3rxFHzs5kIrA1GIvOu-wAqWuDUP3Fqa-2e6k,10181
|
|
43
|
+
zombie_escape-1.8.0.dist-info/WHEEL,sha256=WLgqFyCfm_KASv4WHyYy0P3pM_m7J5L9k2skdKLirC8,87
|
|
44
|
+
zombie_escape-1.8.0.dist-info/entry_points.txt,sha256=JprxC-vvkBJgsOp0WJnGBZRJ_ESjjmyS-nsPExeiLHU,49
|
|
45
|
+
zombie_escape-1.8.0.dist-info/licenses/LICENSE.txt,sha256=q-cJYG_K766eXSxQ7txWcWQ6nS2OF6c3HTVLesHbesU,1104
|
|
46
|
+
zombie_escape-1.8.0.dist-info/RECORD,,
|