backup
main menu, roadmap, save file system
This commit is contained in:
+582
-19
@@ -2,6 +2,7 @@
|
||||
import json
|
||||
import random
|
||||
from pathlib import Path
|
||||
from datetime import datetime
|
||||
from copy import deepcopy
|
||||
import pygame
|
||||
import math
|
||||
@@ -4722,7 +4723,7 @@ def _draw_tether_chain_loop(surface, rect: pygame.Rect, light: bool, cx: float,
|
||||
|
||||
|
||||
def get_pause_menu_rects():
|
||||
items = ["Resume", "Settings", "Close Game"]
|
||||
items = ["Resume", "Save Game", "Settings", "Main Menu", "Close Game"]
|
||||
rects = []
|
||||
start_y = HEIGHT // 2 + 20
|
||||
for i, label in enumerate(items):
|
||||
@@ -4734,7 +4735,7 @@ def get_pause_menu_rects():
|
||||
|
||||
|
||||
def draw_pause_menu(surface, selected_idx: int, use_mouse: bool):
|
||||
items = ["Resume", "Settings", "Close Game"]
|
||||
items = ["Resume", "Save Game", "Settings", "Main Menu", "Close Game"]
|
||||
rects = get_pause_menu_rects()
|
||||
mouse_pos = pygame.mouse.get_pos()
|
||||
hover_idx = None
|
||||
@@ -5865,6 +5866,557 @@ def draw_stats_overlay(surface):
|
||||
ty += 18
|
||||
|
||||
|
||||
# ----------------------------
|
||||
# Save system
|
||||
# ----------------------------
|
||||
SAVES_DIR = BASE_DIR / "saves"
|
||||
|
||||
|
||||
def ensure_saves_dir():
|
||||
SAVES_DIR.mkdir(parents=True, exist_ok=True)
|
||||
|
||||
|
||||
def sanitize_save_name(name: str) -> str:
|
||||
out = []
|
||||
for ch in str(name):
|
||||
if ch.isalnum() or ch in (" ", "-", "_"):
|
||||
out.append(ch)
|
||||
return "".join(out).strip()[:40]
|
||||
|
||||
|
||||
def save_path(name: str) -> Path:
|
||||
return SAVES_DIR / f"{sanitize_save_name(name)}.json"
|
||||
|
||||
|
||||
def list_saves() -> list[dict]:
|
||||
"""Returns [{name, path, updated}] sorted newest-first."""
|
||||
ensure_saves_dir()
|
||||
out = []
|
||||
for p in sorted(SAVES_DIR.glob("*.json"), key=lambda pp: pp.stat().st_mtime, reverse=True):
|
||||
updated_str = ""
|
||||
try:
|
||||
updated_str = datetime.fromtimestamp(p.stat().st_mtime).strftime("%Y-%m-%d %H:%M")
|
||||
except OSError:
|
||||
pass
|
||||
out.append({"name": p.stem, "path": p, "updated": updated_str})
|
||||
return out
|
||||
|
||||
|
||||
def save_game(name: str = "auto") -> str:
|
||||
"""Serialize the current game state to a save file. Returns the save name used."""
|
||||
ensure_saves_dir()
|
||||
name = sanitize_save_name(name) or "auto"
|
||||
data = {
|
||||
"version": 1,
|
||||
"saved_at": datetime.now().isoformat(timespec="seconds"),
|
||||
"player": {
|
||||
"room_id": ROOM.id,
|
||||
"x": player.rect.centerx,
|
||||
"y": player.rect.bottom,
|
||||
"health": player.health,
|
||||
"charge": round(float(player.charge), 2),
|
||||
"shade": player.shade,
|
||||
"coins_light": player.coins_light,
|
||||
"coins_dark": player.coins_dark,
|
||||
"has_ranged": player.has_ranged,
|
||||
"reward_chest_opened": player.reward_chest_opened,
|
||||
"equipment": {s: it for s, it in player.equipment.items()},
|
||||
},
|
||||
"inventory": inventory.items,
|
||||
"quests": {
|
||||
"active": quest_manager.active,
|
||||
"completed_one_time": sorted(quest_manager.completed_one_time),
|
||||
"tokens": quest_manager.tokens,
|
||||
},
|
||||
"boss_diary": {"slotted": boss_diary.slotted},
|
||||
"defeated_bosses": sorted(DEFEATED_BOSSES),
|
||||
"broken_doors": sorted(BROKEN_DOORS),
|
||||
}
|
||||
path = save_path(name)
|
||||
with path.open("w", encoding="utf-8") as f:
|
||||
json.dump(data, f, indent=2)
|
||||
return name
|
||||
|
||||
|
||||
def apply_save_data(data: dict):
|
||||
"""Apply a save's state to the current live game objects (player/inventory/quests/room)."""
|
||||
pdata = data.get("player", {}) or {}
|
||||
|
||||
# persistent player fields
|
||||
player.coins_light = int(pdata.get("coins_light", 0))
|
||||
player.coins_dark = int(pdata.get("coins_dark", 0))
|
||||
player.has_ranged = bool(pdata.get("has_ranged", False))
|
||||
player.reward_chest_opened = bool(pdata.get("reward_chest_opened", False))
|
||||
|
||||
eq = pdata.get("equipment") or {}
|
||||
player.equipment = {s: (dict(it) if it else None) for s, it in eq.items()}
|
||||
player.recalc_bonuses()
|
||||
player._clamp_resources()
|
||||
|
||||
inventory.items = [dict(it) for it in (data.get("inventory") or [])]
|
||||
|
||||
qdata = data.get("quests") or {}
|
||||
quest_manager.active = {k: dict(v) for k, v in (qdata.get("active") or {}).items()}
|
||||
quest_manager.completed_one_time = set(qdata.get("completed_one_time") or [])
|
||||
quest_manager.tokens = int(qdata.get("tokens", 0))
|
||||
|
||||
bdata = data.get("boss_diary") or {}
|
||||
boss_diary.slotted = {k: int(v) for k, v in (bdata.get("slotted") or {}).items()}
|
||||
|
||||
DEFEATED_BOSSES.clear()
|
||||
DEFEATED_BOSSES.update(data.get("defeated_bosses") or [])
|
||||
BROKEN_DOORS.clear()
|
||||
BROKEN_DOORS.update(data.get("broken_doors") or [])
|
||||
|
||||
room_id = normalize_room_id(str(pdata.get("room_id") or "room_01.json"))
|
||||
room = room_manager.load_room(room_id)
|
||||
px, py = pdata.get("x"), pdata.get("y")
|
||||
if isinstance(px, (int, float)) and isinstance(py, (int, float)):
|
||||
room.player_spawn = [int(px), int(py)]
|
||||
apply_room(room, set_player_spawn=True)
|
||||
|
||||
player.respawn()
|
||||
if "health" in pdata:
|
||||
player.health = int(pdata["health"])
|
||||
if "charge" in pdata:
|
||||
player.charge = float(pdata["charge"])
|
||||
player.shade = bool(pdata.get("shade", False))
|
||||
player._clamp_resources()
|
||||
|
||||
|
||||
def fresh_save_data() -> dict:
|
||||
return {
|
||||
"version": 1,
|
||||
"player": {
|
||||
"room_id": "room_01.json",
|
||||
"x": None,
|
||||
"y": None,
|
||||
"health": BASE_MAX_HEALTH,
|
||||
"charge": BASE_MAX_CHARGE,
|
||||
"shade": False,
|
||||
"coins_light": 0,
|
||||
"coins_dark": 0,
|
||||
"has_ranged": False,
|
||||
"reward_chest_opened": False,
|
||||
"equipment": {},
|
||||
},
|
||||
"inventory": [],
|
||||
"quests": {"active": {}, "completed_one_time": [], "tokens": 0},
|
||||
"boss_diary": {"slotted": {}},
|
||||
"defeated_bosses": [],
|
||||
"broken_doors": [],
|
||||
}
|
||||
|
||||
|
||||
def load_game(name: str) -> bool:
|
||||
path = save_path(name)
|
||||
if not path.exists():
|
||||
return False
|
||||
try:
|
||||
data = load_json(path)
|
||||
except Exception as e:
|
||||
print(f"[Load failed] {path} -> {e}")
|
||||
return False
|
||||
apply_save_data(data)
|
||||
return True
|
||||
|
||||
|
||||
def start_new_game(name: str) -> str:
|
||||
"""Reset all state to a fresh game and save it. Returns the save name used."""
|
||||
room_id = "room_01.json"
|
||||
room_json = load_json_safe(room_id, [ROOMS_DIR, LEVELS_DIR, DATA_ROOT, BASE_DIR], ROOM_DEFAULT)
|
||||
room_json = normalize_room_json(room_json)
|
||||
spawn = room_json.get("player_spawn", [200, 100])
|
||||
data = fresh_save_data()
|
||||
if isinstance(spawn, (list, tuple)) and len(spawn) == 2:
|
||||
data["player"]["x"] = int(spawn[0])
|
||||
data["player"]["y"] = int(spawn[1])
|
||||
apply_save_data(data)
|
||||
|
||||
final_name = sanitize_save_name(name) or "auto"
|
||||
base = final_name
|
||||
i = 2
|
||||
while save_path(final_name).exists():
|
||||
final_name = f"{base} {i}"
|
||||
i += 1
|
||||
return save_game(final_name)
|
||||
|
||||
|
||||
def wipe_all_saves():
|
||||
ensure_saves_dir()
|
||||
for p in SAVES_DIR.glob("*.json"):
|
||||
try:
|
||||
p.unlink()
|
||||
except OSError:
|
||||
pass
|
||||
|
||||
|
||||
def _do_save_game():
|
||||
global CURRENT_SAVE_NAME, save_feedback, save_feedback_timer
|
||||
name = CURRENT_SAVE_NAME or "auto"
|
||||
CURRENT_SAVE_NAME = save_game(name)
|
||||
save_feedback = f"Saved: {CURRENT_SAVE_NAME}"
|
||||
save_feedback_timer = 2.0
|
||||
|
||||
|
||||
def _return_to_main_menu():
|
||||
global GAME_MODE, paused, pause_menu, pause_menu_index, pause_use_mouse
|
||||
global settings_wait_action
|
||||
if CURRENT_SAVE_NAME:
|
||||
_do_save_game()
|
||||
inv_ui.visible = False
|
||||
trade_ui.visible = False
|
||||
trade_ui.active_npc = None
|
||||
diary_ui.visible = False
|
||||
paused = False
|
||||
pause_menu = "main"
|
||||
pause_menu_index = 0
|
||||
pause_use_mouse = False
|
||||
settings_wait_action = None
|
||||
GAME_MODE = "menu"
|
||||
|
||||
|
||||
# ----------------------------
|
||||
# Main menu
|
||||
# ----------------------------
|
||||
GAME_MODE = "menu" # menu | select | newgame | settings | confirm_wipe | game
|
||||
|
||||
MAIN_MENU_ITEMS = ["Select Game", "New Game", "Settings", "Quit"]
|
||||
|
||||
menu_index = 0
|
||||
menu_use_mouse = False
|
||||
select_index = 0
|
||||
select_use_mouse = False
|
||||
newgame_name_input = ""
|
||||
wipe_confirm_idx = 1
|
||||
wipe_use_mouse = False
|
||||
CURRENT_SAVE_NAME: str | None = None
|
||||
save_feedback = ""
|
||||
save_feedback_timer = 0.0
|
||||
|
||||
|
||||
def get_main_menu_rects():
|
||||
rects = []
|
||||
start_y = int(HEIGHT * 0.38)
|
||||
for label in MAIN_MENU_ITEMS:
|
||||
text = font.render(label, True, (240, 240, 240))
|
||||
x = WIDTH // 2 - text.get_width() // 2
|
||||
y = start_y
|
||||
rects.append(pygame.Rect(x - 8, y - 4, text.get_width() + 16, text.get_height() + 8))
|
||||
start_y += 36
|
||||
return rects
|
||||
|
||||
|
||||
def get_wipe_button_rect() -> pygame.Rect:
|
||||
w, h = 150, 26
|
||||
return pygame.Rect(WIDTH - w - 12, HEIGHT - h - 12, w, h)
|
||||
|
||||
|
||||
def draw_main_menu(surface, selected_idx: int, use_mouse: bool, has_saves: bool):
|
||||
title_font = pygame.font.SysFont(None, 72)
|
||||
title = title_font.render("Light & Dark", True, (240, 235, 210))
|
||||
surface.blit(title, (WIDTH // 2 - title.get_width() // 2, int(HEIGHT * 0.14)))
|
||||
sub = font.render("Prototype", True, (160, 160, 160))
|
||||
surface.blit(sub, (WIDTH // 2 - sub.get_width() // 2, int(HEIGHT * 0.14) + 68))
|
||||
|
||||
rects = get_main_menu_rects()
|
||||
mouse_pos = pygame.mouse.get_pos()
|
||||
hover_idx = None
|
||||
for i, r in enumerate(rects):
|
||||
if r.collidepoint(mouse_pos):
|
||||
hover_idx = i
|
||||
break
|
||||
active_idx = hover_idx if (use_mouse and hover_idx is not None) else selected_idx
|
||||
|
||||
for i, label in enumerate(MAIN_MENU_ITEMS):
|
||||
r = rects[i]
|
||||
enabled = (i != 0) or has_saves
|
||||
active = enabled and (i == active_idx)
|
||||
fill = (60, 60, 60) if active else (40, 40, 40)
|
||||
border = (140, 140, 140) if active else (90, 90, 90)
|
||||
pygame.draw.rect(surface, fill, r, border_radius=6)
|
||||
pygame.draw.rect(surface, border, r, 2, border_radius=6)
|
||||
col = (245, 245, 245) if enabled else (95, 95, 95)
|
||||
text = font.render(label, True, col)
|
||||
surface.blit(text, (r.x + 6, r.y + 2))
|
||||
|
||||
wr = get_wipe_button_rect()
|
||||
wfill = (70, 40, 40) if has_saves else (40, 40, 40)
|
||||
wborder = (160, 80, 80) if has_saves else (90, 90, 90)
|
||||
pygame.draw.rect(surface, wfill, wr, border_radius=6)
|
||||
pygame.draw.rect(surface, wborder, wr, 2, border_radius=6)
|
||||
wtext = font.render("Wipe All Saves", True, (245, 170, 170) if has_saves else (95, 95, 95))
|
||||
surface.blit(wtext, (wr.x + 8, wr.y + 4))
|
||||
|
||||
|
||||
def get_select_rects(saves) -> list[pygame.Rect]:
|
||||
rects = []
|
||||
start_y = int(HEIGHT * 0.30)
|
||||
for i in range(len(saves)):
|
||||
rects.append(pygame.Rect(WIDTH // 2 - 170, start_y + i * 30 - 2, 340, 24))
|
||||
return rects
|
||||
|
||||
|
||||
def draw_select_menu(surface, selected_idx: int, use_mouse: bool):
|
||||
title = font.render("Select Game", True, (240, 235, 210))
|
||||
surface.blit(title, (WIDTH // 2 - title.get_width() // 2, int(HEIGHT * 0.15)))
|
||||
saves = list_saves()
|
||||
if not saves:
|
||||
msg = font.render("No save files found.", True, (170, 170, 170))
|
||||
surface.blit(msg, (WIDTH // 2 - msg.get_width() // 2, HEIGHT // 2))
|
||||
hint = font.render("Press Esc to return", True, (150, 150, 150))
|
||||
surface.blit(hint, (WIDTH // 2 - hint.get_width() // 2, HEIGHT // 2 + 30))
|
||||
return
|
||||
rects = get_select_rects(saves)
|
||||
mouse_pos = pygame.mouse.get_pos()
|
||||
hover_idx = None
|
||||
for i, r in enumerate(rects):
|
||||
if r.collidepoint(mouse_pos):
|
||||
hover_idx = i
|
||||
break
|
||||
active_idx = hover_idx if (use_mouse and hover_idx is not None) else min(selected_idx, len(saves) - 1)
|
||||
|
||||
for i, sv in enumerate(saves):
|
||||
r = rects[i]
|
||||
active = (i == active_idx)
|
||||
fill = (60, 60, 60) if active else (40, 40, 40)
|
||||
border = (140, 140, 140) if active else (90, 90, 90)
|
||||
pygame.draw.rect(surface, fill, r, border_radius=6)
|
||||
pygame.draw.rect(surface, border, r, 2, border_radius=6)
|
||||
label = f"{sv['name']} ({sv['updated']})" if sv.get("updated") else sv["name"]
|
||||
text = font.render(label, True, (240, 240, 240))
|
||||
surface.blit(text, (r.x + 8, r.y + 3))
|
||||
|
||||
hint = font.render("Up/Down = select Enter = continue Esc = back", True, (170, 170, 170))
|
||||
surface.blit(hint, (WIDTH // 2 - hint.get_width() // 2, HEIGHT - 40))
|
||||
|
||||
|
||||
def draw_newgame(surface):
|
||||
title = font.render("New Game", True, (240, 235, 210))
|
||||
surface.blit(title, (WIDTH // 2 - title.get_width() // 2, int(HEIGHT * 0.22)))
|
||||
label = font.render("Enter save name:", True, (200, 200, 200))
|
||||
surface.blit(label, (WIDTH // 2 - label.get_width() // 2, int(HEIGHT * 0.34)))
|
||||
box = pygame.Rect(WIDTH // 2 - 140, int(HEIGHT * 0.40), 280, 28)
|
||||
pygame.draw.rect(surface, (40, 40, 40), box, border_radius=6)
|
||||
pygame.draw.rect(surface, (140, 140, 140), box, 2, border_radius=6)
|
||||
display = newgame_name_input + "_"
|
||||
txt = font.render(display, True, (240, 240, 240))
|
||||
surface.blit(txt, (box.x + 8, box.y + 5))
|
||||
hint = font.render("Enter = create Esc = cancel", True, (170, 170, 170))
|
||||
surface.blit(hint, (WIDTH // 2 - hint.get_width() // 2, int(HEIGHT * 0.40) + 40))
|
||||
|
||||
|
||||
def get_confirm_rects() -> list[pygame.Rect]:
|
||||
yes = pygame.Rect(WIDTH // 2 - 130, HEIGHT // 2 + 30, 110, 30)
|
||||
no = pygame.Rect(WIDTH // 2 + 20, HEIGHT // 2 + 30, 110, 30)
|
||||
return [yes, no]
|
||||
|
||||
|
||||
def draw_confirm_wipe(surface, selected_idx: int, use_mouse: bool):
|
||||
overlay = pygame.Surface((WIDTH, HEIGHT), pygame.SRCALPHA)
|
||||
overlay.fill((0, 0, 0, 180))
|
||||
surface.blit(overlay, (0, 0))
|
||||
n = len(list_saves())
|
||||
msg = font.render(f"Delete all {n} save file(s)?", True, (240, 240, 240))
|
||||
surface.blit(msg, (WIDTH // 2 - msg.get_width() // 2, HEIGHT // 2 - 30))
|
||||
sub = font.render("This cannot be undone.", True, (200, 90, 90))
|
||||
surface.blit(sub, (WIDTH // 2 - sub.get_width() // 2, HEIGHT // 2 - 4))
|
||||
|
||||
rects = get_confirm_rects()
|
||||
mouse_pos = pygame.mouse.get_pos()
|
||||
hover_idx = None
|
||||
for i, r in enumerate(rects):
|
||||
if r.collidepoint(mouse_pos):
|
||||
hover_idx = i
|
||||
break
|
||||
active_idx = hover_idx if (use_mouse and hover_idx is not None) else selected_idx
|
||||
|
||||
for i, label in enumerate(["Yes", "No"]):
|
||||
r = rects[i]
|
||||
active = (i == active_idx)
|
||||
if active and label == "Yes":
|
||||
fill, border = (90, 40, 40), (200, 80, 80)
|
||||
elif active:
|
||||
fill, border = (60, 60, 60), (140, 140, 140)
|
||||
else:
|
||||
fill, border = (40, 40, 40), (90, 90, 90)
|
||||
pygame.draw.rect(surface, fill, r, border_radius=6)
|
||||
pygame.draw.rect(surface, border, r, 2, border_radius=6)
|
||||
t = font.render(label, True, (245, 245, 245))
|
||||
surface.blit(t, (r.x + (r.width - t.get_width()) // 2, r.y + 5))
|
||||
|
||||
|
||||
def handle_settings_keydown(key, return_mode: str):
|
||||
global settings_wait_action, settings_index, GAME_MODE, pause_menu
|
||||
if settings_wait_action:
|
||||
BINDINGS[settings_wait_action] = key
|
||||
settings_wait_action = None
|
||||
return True
|
||||
if key_is("ui_up", key):
|
||||
settings_index = (settings_index - 1) % len(SETTINGS_ACTIONS)
|
||||
elif key_is("ui_down", key):
|
||||
settings_index = (settings_index + 1) % len(SETTINGS_ACTIONS)
|
||||
elif key in (key_for("ui_confirm"), key_for("ui_confirm_alt")):
|
||||
settings_wait_action = SETTINGS_ACTIONS[settings_index][1]
|
||||
elif key_is("ui_cancel", key):
|
||||
if return_mode == "menu":
|
||||
GAME_MODE = "menu"
|
||||
else:
|
||||
pause_menu = "main"
|
||||
else:
|
||||
return False
|
||||
return True
|
||||
|
||||
|
||||
def _activate_menu_item(i: int, has_saves: bool):
|
||||
global GAME_MODE, running
|
||||
global select_index, select_use_mouse, newgame_name_input
|
||||
global settings_index, settings_wait_action
|
||||
label = MAIN_MENU_ITEMS[i]
|
||||
if label == "Select Game":
|
||||
if not has_saves:
|
||||
return
|
||||
select_index = 0
|
||||
select_use_mouse = False
|
||||
GAME_MODE = "select"
|
||||
elif label == "New Game":
|
||||
newgame_name_input = ""
|
||||
GAME_MODE = "newgame"
|
||||
elif label == "Settings":
|
||||
settings_index = 0
|
||||
settings_wait_action = None
|
||||
GAME_MODE = "settings"
|
||||
elif label == "Quit":
|
||||
running = False
|
||||
|
||||
|
||||
def _menu_handle_events():
|
||||
global running, GAME_MODE, menu_index, menu_use_mouse
|
||||
global select_index, select_use_mouse, newgame_name_input
|
||||
global wipe_confirm_idx, wipe_use_mouse, CURRENT_SAVE_NAME, paused
|
||||
has_saves = len(list_saves()) > 0
|
||||
|
||||
for e in pygame.event.get():
|
||||
if e.type == pygame.QUIT:
|
||||
running = False
|
||||
continue
|
||||
|
||||
if GAME_MODE == "settings":
|
||||
if e.type == pygame.KEYDOWN:
|
||||
handle_settings_keydown(e.key, "menu")
|
||||
|
||||
elif GAME_MODE == "confirm_wipe":
|
||||
if e.type == pygame.KEYDOWN:
|
||||
if key_is("ui_cancel", e.key):
|
||||
GAME_MODE = "menu"
|
||||
elif key_is("ui_left", e.key) or key_is("ui_right", e.key):
|
||||
wipe_confirm_idx = 1 - wipe_confirm_idx
|
||||
wipe_use_mouse = False
|
||||
elif e.key in (key_for("ui_confirm"), key_for("ui_confirm_alt")):
|
||||
if wipe_confirm_idx == 0:
|
||||
wipe_all_saves()
|
||||
GAME_MODE = "menu"
|
||||
elif e.type == pygame.MOUSEBUTTONDOWN and e.button == 1:
|
||||
for i, r in enumerate(get_confirm_rects()):
|
||||
if r.collidepoint(e.pos):
|
||||
if i == 0:
|
||||
wipe_all_saves()
|
||||
GAME_MODE = "menu"
|
||||
break
|
||||
|
||||
elif GAME_MODE == "select":
|
||||
if e.type == pygame.KEYDOWN:
|
||||
if key_is("ui_cancel", e.key):
|
||||
GAME_MODE = "menu"
|
||||
elif key_is("ui_up", e.key):
|
||||
select_index = (select_index - 1) % max(1, len(list_saves()))
|
||||
select_use_mouse = False
|
||||
elif key_is("ui_down", e.key):
|
||||
select_index = (select_index + 1) % max(1, len(list_saves()))
|
||||
select_use_mouse = False
|
||||
elif e.key in (key_for("ui_confirm"), key_for("ui_confirm_alt")):
|
||||
svs = list_saves()
|
||||
if svs:
|
||||
idx = min(select_index, len(svs) - 1)
|
||||
if load_game(svs[idx]["name"]):
|
||||
CURRENT_SAVE_NAME = svs[idx]["name"]
|
||||
GAME_MODE = "game"
|
||||
paused = False
|
||||
elif e.type == pygame.MOUSEBUTTONDOWN and e.button == 1:
|
||||
svs = list_saves()
|
||||
for i, r in enumerate(get_select_rects(svs)):
|
||||
if r.collidepoint(e.pos):
|
||||
if load_game(svs[i]["name"]):
|
||||
CURRENT_SAVE_NAME = svs[i]["name"]
|
||||
GAME_MODE = "game"
|
||||
paused = False
|
||||
break
|
||||
|
||||
elif GAME_MODE == "newgame":
|
||||
if e.type == pygame.KEYDOWN:
|
||||
if key_is("ui_cancel", e.key):
|
||||
GAME_MODE = "menu"
|
||||
elif e.key in (key_for("ui_confirm"), key_for("ui_confirm_alt")):
|
||||
name = newgame_name_input.strip() or f"Save {len(list_saves()) + 1}"
|
||||
CURRENT_SAVE_NAME = start_new_game(name)
|
||||
GAME_MODE = "game"
|
||||
paused = False
|
||||
elif e.key == pygame.K_BACKSPACE:
|
||||
newgame_name_input = newgame_name_input[:-1]
|
||||
else:
|
||||
ch = getattr(e, "unicode", "") or ""
|
||||
if ch and ch.isprintable() and len(newgame_name_input) < 24:
|
||||
newgame_name_input += ch
|
||||
|
||||
elif GAME_MODE == "menu":
|
||||
if e.type == pygame.MOUSEMOTION:
|
||||
hover = None
|
||||
for i, r in enumerate(get_main_menu_rects()):
|
||||
if r.collidepoint(e.pos):
|
||||
hover = i
|
||||
break
|
||||
if hover is not None:
|
||||
menu_use_mouse = True
|
||||
menu_index = hover
|
||||
else:
|
||||
menu_use_mouse = False
|
||||
elif e.type == pygame.MOUSEBUTTONDOWN and e.button == 1:
|
||||
if get_wipe_button_rect().collidepoint(e.pos):
|
||||
if has_saves:
|
||||
GAME_MODE = "confirm_wipe"
|
||||
wipe_confirm_idx = 1
|
||||
wipe_use_mouse = True
|
||||
continue
|
||||
for i, r in enumerate(get_main_menu_rects()):
|
||||
if r.collidepoint(e.pos):
|
||||
menu_use_mouse = True
|
||||
menu_index = i
|
||||
_activate_menu_item(i, has_saves)
|
||||
break
|
||||
elif e.type == pygame.KEYDOWN:
|
||||
if key_is("ui_up", e.key):
|
||||
menu_index = (menu_index - 1) % len(MAIN_MENU_ITEMS)
|
||||
menu_use_mouse = False
|
||||
elif key_is("ui_down", e.key):
|
||||
menu_index = (menu_index + 1) % len(MAIN_MENU_ITEMS)
|
||||
menu_use_mouse = False
|
||||
elif e.key in (key_for("ui_confirm"), key_for("ui_confirm_alt")):
|
||||
_activate_menu_item(menu_index, has_saves)
|
||||
|
||||
|
||||
def _menu_draw(surface):
|
||||
if GAME_MODE == "settings":
|
||||
draw_settings_menu(surface, settings_index, settings_wait_action)
|
||||
elif GAME_MODE == "confirm_wipe":
|
||||
draw_confirm_wipe(surface, wipe_confirm_idx, wipe_use_mouse)
|
||||
elif GAME_MODE == "select":
|
||||
draw_select_menu(surface, select_index, select_use_mouse)
|
||||
elif GAME_MODE == "newgame":
|
||||
draw_newgame(surface)
|
||||
else:
|
||||
draw_main_menu(surface, menu_index, menu_use_mouse, len(list_saves()) > 0)
|
||||
|
||||
|
||||
# ----------------------------
|
||||
# Main loop
|
||||
# ----------------------------
|
||||
@@ -5878,6 +6430,17 @@ settings_wait_action: str | None = None
|
||||
pause_use_mouse = False
|
||||
while running:
|
||||
dt = clock.tick(60) / 1000.0
|
||||
|
||||
if save_feedback_timer > 0:
|
||||
save_feedback_timer = max(0.0, save_feedback_timer - dt)
|
||||
|
||||
if GAME_MODE != "game":
|
||||
_menu_handle_events()
|
||||
screen.fill(BG)
|
||||
_menu_draw(screen)
|
||||
pygame.display.flip()
|
||||
continue
|
||||
|
||||
if not paused:
|
||||
pickup_feed.update(dt)
|
||||
town_title.update(dt)
|
||||
@@ -5907,8 +6470,12 @@ while running:
|
||||
if idx == 0:
|
||||
paused = False
|
||||
elif idx == 1:
|
||||
pause_menu = "settings"
|
||||
_do_save_game()
|
||||
elif idx == 2:
|
||||
pause_menu = "settings"
|
||||
elif idx == 3:
|
||||
_return_to_main_menu()
|
||||
elif idx == 4:
|
||||
running = False
|
||||
break
|
||||
|
||||
@@ -5918,33 +6485,26 @@ while running:
|
||||
was_diary_visible = diary_ui.visible
|
||||
|
||||
if paused and pause_menu == "settings":
|
||||
if settings_wait_action:
|
||||
BINDINGS[settings_wait_action] = e.key
|
||||
settings_wait_action = None
|
||||
else:
|
||||
if key_is("ui_up", e.key):
|
||||
settings_index = (settings_index - 1) % len(SETTINGS_ACTIONS)
|
||||
elif key_is("ui_down", e.key):
|
||||
settings_index = (settings_index + 1) % len(SETTINGS_ACTIONS)
|
||||
elif e.key in (key_for("ui_confirm"), key_for("ui_confirm_alt")):
|
||||
settings_wait_action = SETTINGS_ACTIONS[settings_index][1]
|
||||
elif key_is("ui_cancel", e.key):
|
||||
pause_menu = "main"
|
||||
handle_settings_keydown(e.key, "pause")
|
||||
continue
|
||||
|
||||
if paused and pause_menu == "main":
|
||||
if key_is("ui_up", e.key):
|
||||
pause_menu_index = (pause_menu_index - 1) % 3
|
||||
pause_menu_index = (pause_menu_index - 1) % 5
|
||||
pause_use_mouse = False
|
||||
elif key_is("ui_down", e.key):
|
||||
pause_menu_index = (pause_menu_index + 1) % 3
|
||||
pause_menu_index = (pause_menu_index + 1) % 5
|
||||
pause_use_mouse = False
|
||||
elif e.key in (key_for("ui_confirm"), key_for("ui_confirm_alt")):
|
||||
if pause_menu_index == 0:
|
||||
paused = False
|
||||
elif pause_menu_index == 1:
|
||||
pause_menu = "settings"
|
||||
_do_save_game()
|
||||
elif pause_menu_index == 2:
|
||||
pause_menu = "settings"
|
||||
elif pause_menu_index == 3:
|
||||
_return_to_main_menu()
|
||||
elif pause_menu_index == 4:
|
||||
running = False
|
||||
elif key_is("pause", e.key):
|
||||
paused = False
|
||||
@@ -6142,9 +6702,12 @@ while running:
|
||||
screen.blit(pause_text, (WIDTH // 2 - pause_text.get_width() // 2, HEIGHT // 2 - 10))
|
||||
if pause_menu == "main":
|
||||
draw_pause_menu(screen, pause_menu_index, pause_use_mouse)
|
||||
draw_pause_controls(screen, 20, HEIGHT // 2 + 90)
|
||||
draw_pause_controls(screen, 20, HEIGHT // 2 + 190)
|
||||
else:
|
||||
draw_settings_menu(screen, settings_index, settings_wait_action)
|
||||
if save_feedback_timer > 0:
|
||||
sf = font.render(save_feedback, True, (150, 220, 150))
|
||||
screen.blit(sf, (WIDTH // 2 - sf.get_width() // 2, HEIGHT // 2 + 160))
|
||||
pygame.display.flip()
|
||||
continue
|
||||
|
||||
|
||||
Reference in New Issue
Block a user