diff --git a/data.json b/data.json index 4e83e88..2ded604 100644 --- a/data.json +++ b/data.json @@ -4,7 +4,7 @@ "name": "Streichholzpackung", "description": "Eine Packung mit Streichhoelzern, leider ist aber nur noch ein einziges uebrig." }, - "key": { + "laundry_key": { "name": "Ein rostiger, alter Schluessel", "description": "Wo der wohl passen koennte?" } @@ -12,7 +12,9 @@ "rooms": { "entrance": { "name": "Haupthalle", + "identifier": [ "haupthalle", "eingang", "eingangsbereich" ], "is_start": true, + "requires_key": false, "enter_text": "Du betrittst die Haupthalle. Nebst Spinnenweben und Staub findet sich jede Menge kaputter Moebel.", "interactables": [ { @@ -26,7 +28,7 @@ { "failed": { - "action": null, + "actions": null, "text": "Vielleicht sollte ich sie erstmal mitnehmen, bevor ich versuche damit irgendwas zu tun?" } }, @@ -34,7 +36,7 @@ { "success": { - "action": "add_item matches", + "actions": "add_item matches", "text": "Die nehme ich mal mit ... vielleicht will ich ja spontan was abfackeln" } }, @@ -42,7 +44,7 @@ { "success": { - "action": null, + "actions": null, "text": "Streichhoelzer. Man streicht sie und sie sind aus Holz. Macht Sinn, oder?" } } @@ -62,24 +64,27 @@ "interactions": { "use": { "success": { - "action": "set_room_flag entrance lit_up", + "actions": [ + "set_room_flag entrance lit_up", + "remove_item matches" + ], "text": "Du hast die Kerze mit einem Streichholz entzuendet, es ist nun ein wenig heller im Raum" }, "failed": { - "action": null, + "actions": null, "text": "Du kannst die Kerze nicht einfach mit deinen Haenden anzuenden." } }, "take": { "failed": { - "action": null, + "actions": null, "text": "DU NICHT NEHMEN KERZE!" } }, "look": { "success": { - "action": null, + "actions": null, "text": "Eine dicke Kerze, wie man sie in einer Kirche findet." } } @@ -93,20 +98,20 @@ "interactions": { "use": { "failed": { - "action": null, + "actions": null, "text": "Vielleicht sollte ich diesen eventuell wichtigen Gegenstand ja erstmal mitnehmen ..." } }, "take": { "success": { - "action": "add_item key", + "actions": "add_item laundry_key", "text": "Den nehme ich mal lieber mit ..." } }, "look": { "success": { - "action": null, + "actions": null, "text": "Ein rostiger, alter Schluessel - wo der wohl passt?" } } @@ -119,6 +124,8 @@ }, "laundry": { "name": "Waschkueche", + "identifier": [ "waschkueche", "waschraum" ], + "requires_key": "laundry_key", "enter_text": "Dies hier scheint die Waschkueche zu sein ... zumindest deutet die uralte, benutzte Unterwaesche darauf hin.", "interactables": [], "doors": [ diff --git a/src/game.py b/src/game.py index 7142e81..475a8e5 100644 --- a/src/game.py +++ b/src/game.py @@ -12,6 +12,9 @@ class Game: self.game_data : dict = None self.rooms : list[Room] = [] self.current_room : Room = None + + def tiges(mett): + pass def load_game(self, file: str) -> None: try: @@ -27,9 +30,12 @@ class Game: room = self.game_data['rooms'][room_id] g_room = Room(room_id, room['name']) + g_room.identifier = room['identifier'] g_room.enter_text = room['enter_text'] g_room.doors = room['doors'] g_room.interactables = [] + g_room.requires_key = room['requires_key'] if 'requires_key' in room else False + g_room.unlocked = g_room.requires_key == False g_room.is_start = room['is_start'] if 'is_start' in room else False # Check if we had a starting room already @@ -62,6 +68,26 @@ class Game: return room return None + + def get_matching_rooms(self, query : str) -> list[Room]: + rooms : list[Room] = [] + query : str = query.lower().strip() + + for room in self.rooms: + if room.id == id: + rooms.append(room) + else: + for identifier in room.identifier: + if identifier.lower().strip().startswith(query): + already_there = False + for r in rooms: + if r.id == room.id: + already_there = True + + if not already_there: + rooms.append(room) + + return rooms def get_start_room(self) -> Room | None: for room in self.rooms: @@ -70,22 +96,29 @@ class Game: return None - def run_action(self, action : str) -> bool: - print(f"[DEBUG] run_action('{action}')") + def run_actions(self, actions : str|list[str]) -> bool: + if isinstance(actions, list) == False: + actions = [ actions ] - action_parts = action.split(' ') - action_cmd = action_parts[0] - action_args = action_parts[1:] if len(action_parts) >= 2 else [] + for action in actions: + print(f"[DEBUG] run_action('{action}')") + + action_parts = action.lower().split(' ') + action_cmd = action_parts[0] + action_args = list(filter(None, action_parts[1:] if len(action_parts) >= 2 else [])) + + if action_cmd == "set_room_flag": + room = self.get_room(action_args[0]) + if room is not None: + room.set_flag(action_args[1]) + else: + print(f"[DEBUG] Error: {action} (Room not found)") + + elif action_cmd == "add_item": + self.inventory.add_item(action_args[0]) + + elif action_cmd == "remove_item": + self.inventory.remove_item(action_args[0]) - if action_cmd == "set_room_flag": - room = self.get_room(action_args[0]) - if room is not None: - room.set_flag(action_args[1]) else: - print(f"[DEBUG] Error: {action} (Room not found)") - - elif action_cmd == "add_item": - self.inventory.add_item(action_args[0]) - - else: - return False \ No newline at end of file + return False \ No newline at end of file diff --git a/src/interactable.py b/src/interactable.py index b3444dc..fd63ed1 100644 --- a/src/interactable.py +++ b/src/interactable.py @@ -45,7 +45,7 @@ class Interactable: continue if requirement.type == "item": - if requirement.id not in self.game.inventory.get_items(): + if self.game.inventory.has_item(requirement.id) == False: all_met = False return all_met @@ -53,8 +53,8 @@ class Interactable: def can_see(self): return self.visible == True or self.room.has_flag(self.visible) - def run(self, action : str): - if not self.can_see(): + def run(self, action : str) -> bool: + if self.can_see() == False: print(f"[{self.room.name}] Du kannst nicht genug sehen, um damit etwas zu tun") return @@ -64,36 +64,42 @@ class Interactable: if "success" in self.data["interactions"][action]: print(f"[{self.room.name}] {self.data['interactions'][action]['success']['text']}") - if "action" in self.data["interactions"][action]["success"] and self.data["interactions"][action]["success"]["action"] != None: - self.game.run_action(self.data["interactions"][action]["success"]["action"]) - pass + if "actions" in self.data["interactions"][action]["success"] and self.data["interactions"][action]["success"]["actions"] != None: + self.game.run_actions(self.data["interactions"][action]["success"]["actions"]) + return True elif "failed" in self.data["interactions"][action]: print(f"[{self.room.name}] {self.data['interactions'][action]['failed']['text']}") - if "action" in self.data["interactions"][action]["failed"] and self.data["interactions"][action]["failed"]["action"] != None: - self.game.run_action(self.data["interactions"][action]["failed"]["action"]) - pass + if "actions" in self.data["interactions"][action]["failed"] and self.data["interactions"][action]["failed"]["actions"] != None: + self.game.run_actions(self.data["interactions"][action]["failed"]["actions"]) + return False + + else: + return False else: all_met = self.check_action_requirements(action, requirements) if not all_met: print(f"[{self.room.name}] {self.data['interactions'][action]['failed']['text']}") - if self.data["interactions"][action]["failed"]["action"] is not None: - self.game.run_action(self.data["interactions"][action]["failed"]["action"]) + if self.data["interactions"][action]["failed"]["actions"] is not None: + self.game.run_actions(self.data["interactions"][action]["failed"]["actions"]) + return False else: print(f"[{self.room.name}] {self.data['interactions'][action]['success']['text']}") - if self.data["interactions"][action]["success"]["action"] is not None: - self.game.run_action(self.data["interactions"][action]["success"]["action"]) + if self.data["interactions"][action]["success"]["actions"] is not None: + self.game.run_actions(self.data["interactions"][action]["success"]["actions"]) + return True def use(self): self.run("use") def take(self): - self.run("take") + if self.run("take"): + self.room.interactables.remove(self) def look(self): self.run("look") diff --git a/src/inventory.py b/src/inventory.py index 4381921..7867ae8 100644 --- a/src/inventory.py +++ b/src/inventory.py @@ -1,17 +1,41 @@ +from item import Item + class Inventory: def __init__(self, item_db : dict): - self.items = [] - self.item_db = item_db + self.items : list[str] = [] + self.item_db : dict[str,Item] = {} + + for id in item_db: + item = item_db[id] + self.item_db[id] = Item(id, item['name'], item['description']) def add_item(self, item): self.items.append(item) - - def get_items(self): - return self.items - def get_item(self, name : str) -> None|dict: - lname = name.lower().strip() - if lname in self.item_db and lname in self.items: - return self.item_db[lname] + def has_item(self, id: str) -> bool: + return id in self.items + + def get_items(self) -> list[Item]: + items = [] + for id in self.items: + items.append(self.item_db[id]) + + return items + + def remove_item(self, item : str): + if item.lower().strip() in self.items: + self.items.remove(item.lower().strip()) + + def get_item(self, id : str) -> None|Item: + if id in self.item_db and id in self.items: + return self.item_db[id] else: - return None \ No newline at end of file + return None + + def get_item_by_name(self, name : str) -> Item|None: + for id in self.item_db: + item = self.item_db[id] + if name.lower().strip() in item.name.lower().strip(): + return item + + return None \ No newline at end of file diff --git a/src/item.py b/src/item.py index 7e6df88..afea42c 100644 --- a/src/item.py +++ b/src/item.py @@ -1,8 +1,8 @@ class Item: - def __init__(self, name, description): - self.name = name - self.description = description - self.is_key = False + def __init__(self, id : str, name : str, description : str): + self.id : str = id + self.name : str = name + self.description : str = description def __str__(self): return f"Item: {self.name}" diff --git a/src/main.py b/src/main.py index 537e0fa..63bb1f1 100644 --- a/src/main.py +++ b/src/main.py @@ -3,15 +3,19 @@ from game import Game def main(): game = Game() game.load_game('data.json') - print() + + has_changed_room = True while game.is_running: - print(f"[{game.current_room.name}] {game.current_room.enter_text}") + + if has_changed_room: + print(f"[{game.current_room.name}] {game.current_room.enter_text}") + has_changed_room = False cmd = input("[Player] ") cmd_parts = cmd.split(' ') - cmd_name = cmd_parts[0] - cmd_args = cmd_parts[1:] if len(cmd_parts) >= 2 else [] + cmd_name = cmd_parts[0] + cmd_args = list(filter(None, cmd_parts[1:] if len(cmd_parts) >= 2 else [])) if cmd_name.lower().strip() == "quit": game.is_running = False @@ -28,8 +32,8 @@ def main(): print(f"[Player] In diesem Raum befindet sich eine Tuer zu {room.name}.") else: rooms = [] - for room_id in game.current_room.doors: - rooms.append(game.get_room(room_id).name) + for query in game.current_room.doors: + rooms.append(game.get_room(query).name) print(f"[Player] In diesem Raum befinden sich {door_count} Tueren zu {', '.join(rooms)}.") @@ -66,7 +70,7 @@ def main(): what = cmd_args[0] interactable = game.current_room.get_interactable(what) - if what is None: + if what is None or interactable is None: print(f"[Player] Ich kann {what} nicht nehmen, da es nicht da ist!") else: interactable.take() @@ -78,7 +82,7 @@ def main(): what = cmd_args[0] interactable = game.current_room.get_interactable(what) - if what is None: + if what is None or interactable is None: print(f"[Player] Ich kann {what} nicht benutzen, da es nicht da ist!") else: interactable.use() @@ -88,27 +92,50 @@ def main(): if cmd_name.lower().strip() == "inspect": if len(cmd_args) == 1: item_name = cmd_args[0].lower().strip() - item = game.inventory.get_item(item_name) + item = game.inventory.get_item_by_name(item_name) if item is not None: - print(f"[Player] {item['name']}: {item['description']}") + print(f"[Player] {item.name}: {item.description}") else: print(f"[Player] {item_name} habe ich nicht bei mir") + elif len(cmd_args) == 0: + item_count = len(game.inventory.get_items()) + if item_count == 0: + print("[Player] Ich habe nichts bei mir.") + elif item_count == 1: + item = game.inventory.get_items()[0] + print(f"[Player] Ich habe gerade nur {item.name}") + else: + print("[Player] Ein paar Sachen habe ich schon dabei ...") + for item in game.inventory.get_items(): + print(f"[Player] {item.name}") else: - print(f"[Player] Was soll ich mir denn anschauen?") + print("[Player] Was soll ich mir denn anschauen?") if cmd_name.lower().strip() == "enter": if len(cmd_args) == 1: - room_id = cmd_args[0].lower().strip() - room = game.get_room(room_id) + query = cmd_args[0].lower().strip() + rooms = game.get_matching_rooms(query) - if room is not None and room_id in game.current_room.doors: - print(f"[{game.current_room.name}] Du gehst durch die Tuer zum {room.name}") - game.current_room = room + if len(rooms) == 0: + print(f"[Player] Wenn ich nicht weiss, wo ich rein soll, dann bleibe ich lieber einfach, wo ich bin.") + elif len(rooms) == 1: + room = rooms[0] + if game.current_room.is_adjacent(room): + if room.unlocked or (type(room.requires_key) is str and game.inventory.has_item(room.requires_key)): + print(f"[{game.current_room.name}] Du gehst durch die Tuer zum {room.name}") + game.current_room = room + room.unlocked = True + has_changed_room = True + + if type(room.requires_key) is str and game.inventory.has_item(room.requires_key): + game.inventory.remove_item(room.requires_key) + else: + print("[Player] Es scheint so, als brauche ich hier einen passenden Schluessel ...") + else: + print(f"[Player] Hier gibt es keine Tuer dort hin ... vielleicht sollte ich ja einfach mit dem Kopf durch die Wand?") else: - print(f"[Player] Hier gibt es keine Tuer dort hin ... vielleicht sollte ich ja einfach mit dem Kopf durch die Wand?") - else: - print(f"[Player] Wenn ich nicht weiss, wo ich rein soll, dann bleibe ich lieber einfach, wo ich bin.") + print("[Player] Ich bin mir nicht ganz sicher, in welchen Raum ich denn nun gehen soll ...") print() diff --git a/src/room.py b/src/room.py index 5b1d702..e6edd9e 100644 --- a/src/room.py +++ b/src/room.py @@ -4,11 +4,14 @@ class Room: def __init__(self, id : str, name : str): self.id : str = id self.name : str = name + self.identifier : list[str] = [] self.enter_text : str = "" self.is_start : bool = False self.doors : list[str] = [] self.interactables : list[Interactable] = [] self.flags : list = [] + self.requires_key : str|bool = False + self.unlocked : bool = True def set_flag(self, flag : str) -> None: if flag not in self.flags: @@ -24,6 +27,13 @@ class Room: return i return None + + def is_adjacent(self, room) -> bool: + if type(room) is not Room: + print("[WARN] room is not of type Room") + return False + + return room.id in self.doors def __str__(self): r = f"[Room] ID: {self.id} | Name: {self.name} | IsStart: {self.is_start}"