Inventory stored keycards under their full item_id (keycard_red) while Room.locked_by/has_keycard checked against the plain color (red), so the Waffentresor always reported itself as locked even with the keycard in hand. Also fixes "rede Keycard" -> "rote Keycard".
113 lines
4.1 KiB
Python
113 lines
4.1 KiB
Python
#!/usr/bin/env python3
|
|
# -*- coding: utf-8 -*-
|
|
"""Room-Klasse und Hub-Navigation: verbundene Raeume statt linearer Szenen."""
|
|
|
|
from doom_matrix.engine.ui import Colors, slow_print, menu, section_title
|
|
from doom_matrix.engine import cheats
|
|
|
|
KEYCARD_COLOR_DE = {"red": "rote", "blue": "blaue", "yellow": "gelbe"}
|
|
|
|
|
|
class Room:
|
|
def __init__(self, room_id, name, description, exits=None, item_ids=None,
|
|
locked_by=None, on_enter=None, one_time_event=True):
|
|
self.room_id = room_id
|
|
self.name = name
|
|
self.description = description
|
|
self.exits = exits or {} # direction_label -> room_id
|
|
self.item_ids = item_ids or [] # Item-IDs, die hier liegen
|
|
self.locked_by = locked_by # Keycard-Farbe oder None
|
|
self.on_enter = on_enter # Funktion(player, game_ctx) -> optionales Signal ("leave", room_id) oder None
|
|
self.one_time_event = one_time_event
|
|
self._event_triggered = False
|
|
self.visited = False
|
|
|
|
|
|
class Hub:
|
|
"""Eine Sammlung verbundener Raeume fuer einen Layer."""
|
|
|
|
def __init__(self, rooms, start_room_id):
|
|
self.rooms = {r.room_id: r for r in rooms}
|
|
self.current_id = start_room_id
|
|
|
|
def current(self):
|
|
return self.rooms[self.current_id]
|
|
|
|
def explore(self, player, item_registry, game_ctx=None):
|
|
"""
|
|
Haupt-Loop fuer die Raum-Navigation.
|
|
item_registry: dict item_id -> Item-Objekt (aus data/items.json geladen)
|
|
game_ctx: beliebiges Kontext-Objekt, das an on_enter-Callbacks weitergereicht wird.
|
|
Gibt zurueck, sobald ein on_enter-Callback ein Signal-Tuple ("leave", next_scene) liefert.
|
|
"""
|
|
while True:
|
|
room = self.current()
|
|
self._describe(room)
|
|
|
|
if not room.visited:
|
|
room.visited = True
|
|
self._collect_items(room, player, item_registry)
|
|
|
|
if room.on_enter and (not room.one_time_event or not room._event_triggered):
|
|
room._event_triggered = True
|
|
result = room.on_enter(player, game_ctx)
|
|
if result is not None:
|
|
return result
|
|
|
|
signal = self._prompt(player, room)
|
|
if signal is not None:
|
|
return signal
|
|
|
|
def _describe(self, room):
|
|
section_title(room.name, Colors.CYAN)
|
|
slow_print(room.description)
|
|
|
|
def _collect_items(self, room, player, item_registry):
|
|
for item_id in room.item_ids:
|
|
item = item_registry.get(item_id)
|
|
if item:
|
|
player.inventory.add(item)
|
|
slow_print(f"{Colors.GREEN}Gefunden: {item.name}{Colors.END} - {item.description}")
|
|
|
|
def _prompt(self, player, room):
|
|
options = []
|
|
exit_keys = list(room.exits.items())
|
|
for i, (label, _) in enumerate(exit_keys):
|
|
options.append((str(i + 1), label))
|
|
options.append(("s", "Status anzeigen"))
|
|
options.append(("i", "Inventar anzeigen"))
|
|
|
|
choice = menu(options, "Wohin")
|
|
|
|
if cheats.try_cheat(choice, player):
|
|
return None
|
|
|
|
if choice == "s":
|
|
player.show_status()
|
|
return None
|
|
if choice == "i":
|
|
lines = player.inventory.list_display()
|
|
if lines:
|
|
for line in lines:
|
|
print(f" {Colors.YELLOW}{line}{Colors.END}")
|
|
else:
|
|
print(f" {Colors.DIM}Inventar leer.{Colors.END}")
|
|
return None
|
|
|
|
try:
|
|
idx = int(choice) - 1
|
|
if 0 <= idx < len(exit_keys):
|
|
_, target_id = exit_keys[idx]
|
|
target = self.rooms.get(target_id)
|
|
if target and target.locked_by and not player.inventory.has_keycard(target.locked_by):
|
|
color_de = KEYCARD_COLOR_DE.get(target.locked_by, target.locked_by)
|
|
slow_print(f"\n{Colors.RED}Diese Tuer benoetigt eine {color_de} Keycard.{Colors.END}")
|
|
return None
|
|
self.current_id = target_id
|
|
return None
|
|
except ValueError:
|
|
pass
|
|
|
|
slow_print(f"{Colors.YELLOW}Das geht hier nicht.{Colors.END}")
|
|
return None
|