diff --git a/README.md b/README.md index 4e9a61d..8a0cec0 100644 --- a/README.md +++ b/README.md @@ -9,7 +9,7 @@ I was bored Check out the `example.yml` file, all games are in the `YAML` format. ## How do I use this -Download/clone the repo and run the `__main__.py` file with the path to a game in `YAML` format as the first argument. +Download/clone the repo and run the `__main__.py` file. You need to have a valid game in a `games` folder. ## TODO [Here](https://github.com/hernikplays/texty/projects/1) diff --git a/__main__.py b/__main__.py index 43908dd..57a9151 100644 --- a/__main__.py +++ b/__main__.py @@ -19,7 +19,7 @@ def lang(): data = yaml.load(f,Loader=SafeLoader) return data -def main(): # TODO: Maybe a menu for available text games? +def main(): l = lang() init() if(not isdir("./games")): @@ -41,7 +41,8 @@ def main(): # TODO: Maybe a menu for available text games? print(l['no_games']) else: names = [] - for n in games: names.append(n.name) + for n in games: + names.append(n.name) m = MenuManager(names,f" TEXTY \n{l['available']}") games[m.selected].main_menu() diff --git a/games/example.yml b/games/example.yml index 59c66c9..8728446 100644 --- a/games/example.yml +++ b/games/example.yml @@ -21,24 +21,28 @@ game: # here goes all the game logic beer: description: "Order beer" text: "You order some &ebeer" - add_inventory: "Beer" # add something to inventory + add_item: "Beer" # add something to inventory + actions: + - do_something nothing: description: "Do nothing" text: "You sit and wait..." actions: - - last - last: - description: 'Continue' - text: 'You look around the tavern' + - drink + - leave + do_something: + description: "Continue" + text: "You start to feel bored." actions: - drink - - sitmore + - leave drink: - needs_item: - - 'Beer' + has_item: ["Beer"] # item names are case-sensitive description: "Drink beer" - text: 'You take a sip of your beer' - sitmore: - description: 'Sit some more' - text: 'You sit some more...' + text: "You take a sip of your cold &eBeer" + actions: + - leave + leave: + description: "Leave" + text: "You decide to leave." diff --git a/lib/ascii.py b/lib/ascii.py index 79c6d6c..d7d1cc7 100644 --- a/lib/ascii.py +++ b/lib/ascii.py @@ -21,6 +21,5 @@ class AsciiAnimation: data = yaml.load(c,Loader=yaml.SafeLoader) if(data["speed"] != None): self.speed = data["speed"] - pass with open(f"{tmpdir}/ascii/{name}/{f}",encoding="utf-8") as f: # add all frames into list self.frames.append(f.read()) \ No newline at end of file diff --git a/lib/game.py b/lib/game.py index a3172e4..04b8378 100644 --- a/lib/game.py +++ b/lib/game.py @@ -1,9 +1,10 @@ +import enum import yaml from yaml.loader import SafeLoader from colorama import Fore, Back import re -from lib.menu import MenuManager +from lib.menu import HasItemDialogue, MenuManager from .save import SaveManager from .ascii import AsciiAnimation from time import sleep @@ -70,30 +71,75 @@ class Game: # the game class keeps information about the loaded game def print_text(self): # Prints out the current prompt system("cls||clear") + if "add_item" in self.nodes[self.current].keys(): # if there is an add_inventory key in the node, + # add item to inventory + item = self.nodes[self.current]['add_item'] + if item not in self.inventory: + self.inventory.append(item) + print(self.inventory) + system("clear||cls") + print(f"{self.lang['acquire'].replace('$item',f'{Fore.CYAN}{item}{Fore.RESET}')}") + sleep(3) + system("clear||cls") animated = re.search(r"(?!{).+(?=})",self.nodes[self.current]["text"]) # find the animated text if(animated != None): self.print_animated(animated.group(0)) self.nodes[self.current]["text"] = self.nodes[self.current]["text"].replace("{"+animated.group(0)+"}","") # remove the animated text from the text prompt if("actions" in self.nodes[self.current].keys()): - actions_desc = [] + actions_desc = [] # has descriptions of text prompts, so that we don't need to find them in MenuManager + need_item = [] # helps implement a check for needing an item for option in self.nodes[self.current]["actions"]: try: actions_desc.append(self.nodes[option]["description"]) - except: + if "has_item" in self.nodes[option].keys(): + need_item.append(self.nodes[option]["has_item"]) + else: + need_item.append(None) + except Exception: print(f"{Back.RED}{Fore.WHITE}{self.lang['no_action'].replace('$action',option)}{Fore.RESET}") exit(1) - m = MenuManager(actions_desc,self.parse_colors(self.nodes[self.current]["text"])) + m = "" + actions_desc.extend([self.lang['inventory'],self.lang['quit']]) + if(all(element == None for element in need_item) is False): + need_item.extend([None, None]) + # we need to check if user has item + m = HasItemDialogue(actions_desc,self.parse_colors(self.nodes[self.current]["text"]),self.inventory,need_item) + # TODO: Remove item from inventory after using it? + while need_item[m.selected] != None and all(element not in self.inventory for element in need_item[m.selected]): # until user selects an available prompt, re-prompt again + m = HasItemDialogue(actions_desc,self.parse_colors(self.nodes[self.current]["text"]),self.inventory,need_item) + if "has_item" in self.nodes[m.selected].keys(): + for item in need_item[m.selected]: + self.inventory.remove(item) + else: + m = MenuManager(actions_desc,self.parse_colors(self.nodes[self.current]["text"])) sel = m.selected - if "add_item" in self.nodes[self.current]: # if there is an add_inventory key in the node, - # add item to inventory - self.inventory.append(self.nodes[self.current]["add_inventory"]) - self.current = self.nodes[self.current]["actions"][sel] - self.save.currentPrompt = self.current # save the current prompt - self.print_text() + if(sel == len(actions_desc)-2): # show inventory + self.show_inventory() + elif (sel == len(actions_desc)-1): # Save & quit + self.save.currentPrompt = self.current # save the current prompt + self.save.inventory = self.inventory + self.save.save() + exit(0) + else: + self.current = self.nodes[self.current]["actions"][sel] + self.print_text() else: print(self.parse_colors(self.nodes[self.current]["text"])) print("") + def show_inventory(self): + if len(self.inventory) == 0: + MenuManager([self.lang["return"]],f" YOUR INVENTORY \n") + else: + s = "" + for i,item in enumerate(self.inventory): + if(i == len(self.inventory)): # last item + s += f"- {item}" + else: + s += f"- {item}\n" + MenuManager([self.lang["return"]],f" YOUR INVENTORY \n{s}") + self.print_text() + def print_animated(self,animid): # prints the first found occurence of an ascii animation animation = AsciiAnimation() animation.load_ascii(animid) diff --git a/lib/lang/cz.yml b/lib/lang/cz.yml index 8c3d22e..f8feb64 100644 --- a/lib/lang/cz.yml +++ b/lib/lang/cz.yml @@ -9,6 +9,11 @@ quit: 'Vypnout' quitting: 'Vypínám' continue: 'Pokračovat' new_game: 'Nová hra' +inventory: 'Zobrazit inventář' +acquire: 'Získal jsi $item' + +inside_inv: "VÁŠ INVENTÁŘ" +return: "Vrátit se" lang: 'Jazyk' back: 'Zpět' diff --git a/lib/lang/en.yml b/lib/lang/en.yml index c4c8615..faa1529 100644 --- a/lib/lang/en.yml +++ b/lib/lang/en.yml @@ -9,6 +9,11 @@ quit: 'Quit' quitting: 'Quitting' continue: 'Continue' new_game: 'New game' +inventory: 'Show inventory' +acquire: 'You acquired $item' + +inside_inv: "YOUR INVENTORY" +return: "Return" lang: 'Language' back: 'Back' diff --git a/lib/menu.py b/lib/menu.py index fe224e6..f572008 100644 --- a/lib/menu.py +++ b/lib/menu.py @@ -35,9 +35,46 @@ class MenuManager: self.show_menu() def show_menu(self): + system("cls||clear") print(self.additional) for selection in self.selections: if(self.selected == self.selections.index(selection)): print(f"{Fore.RED}->{Fore.RESET} {selection}") else: print(f" {selection}") + +class HasItemDialogue(MenuManager): + ''' + Custom handler for dialogue, that requires to check if the user has an item + ''' + def __init__(self, selections: list, additional: str,inv:list,need_item:list): + system("cls||clear") + self.inventory = inv + self.need_items = need_item + super().__init__(selections, additional) + + def show_menu(self): + print(self.additional) + + for i,selection in enumerate(self.selections): + if(self.need_items[i] != None and all(element not in self.inventory for element in self.need_items[i])): + c = "" + for i,item in enumerate(self.need_items[i]): + if item not in self.inventory: + if i == len(self.need_items)-self.need_items.count(None)-1: # last item, don't add a comma + c+=f"{item} " + else: + c+=f"{item}, " + # user does not have the needed item + if(self.selected == i): + print(f"{Fore.RED}-> {Fore.CYAN}{selection}{Fore.RESET} (Need {c})") + else: + print(f" {Fore.CYAN}{selection}{Fore.RESET}") + else: + # we don't need to change color for an item user doesn't have + if(self.selected == i): + print(f"{Fore.RED}->{Fore.RESET} {selection}") + else: + print(f" {selection}") + def make_selection(self) -> int: + keyboard.remove_all_hotkeys() diff --git a/lib/save.py b/lib/save.py index 2347209..389e830 100644 --- a/lib/save.py +++ b/lib/save.py @@ -5,7 +5,6 @@ class SaveManager: # manages save and configuration files def __init__(self): self.id = "" # game ID self.currentPrompt = "" # Current prompt - self.lang = "" # Selected language self.inventory = [] # Items in inventory def load(self): @@ -18,6 +17,6 @@ class SaveManager: # manages save and configuration files return False def save(self): - data = {"id":self.id,"currentPrompt":self.currentPrompt,"inventory":self.inventory,"lang":self.lang} + data = {"id":self.id,"currentPrompt":self.currentPrompt,"inventory":self.inventory} with open(f"./saves/{self.id}.yml",mode="w",encoding="utf-8") as f: yaml.dump(data,f) \ No newline at end of file