diff --git a/.vscode/launch.json b/.vscode/launch.json index 9302a15..7a38d34 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -5,12 +5,12 @@ "version": "0.2.0", "configurations": [ { - "name": "Start with example game", + "name": "Python: Start main", "type": "python", "request": "launch", - "program": "${workspaceRoot}/__main__.py", - "args": ["./example.yml"], - "console": "integratedTerminal" + "program": "${workspaceFolder}/__main__.py", + "console": "integratedTerminal", + "justMyCode": true } ] } \ No newline at end of file diff --git a/__main__.py b/__main__.py index b75d148..43908dd 100644 --- a/__main__.py +++ b/__main__.py @@ -1,7 +1,7 @@ from genericpath import isdir from lib.game import * from colorama import init, Back, Fore -from os import mkdir, listdir +from os import mkdir, listdir, path def lang(): lang = "en" @@ -35,16 +35,15 @@ def main(): # TODO: Maybe a menu for available text games? except Exception as e: print(f"{Back.RED}{Fore.RED}{l['error_loading']} {file}:") print(e) + # PRINT OUT GAME SELECT - # TODO SWITCH TO MENU MANAGER - print(" TEXTY ") - print(l['available']) if len(games) < 1: print(l['no_games']) else: - for i,g in enumerate(games): - print(f"{i} - {g.name}") - print(f"{len(games)} - {l['quit']}") + names = [] + for n in games: names.append(n.name) + m = MenuManager(names,f" TEXTY \n{l['available']}") + games[m.selected].main_menu() if __name__ == "__main__": diff --git a/example.yml b/games/example.yml similarity index 100% rename from example.yml rename to games/example.yml diff --git a/lib/game.py b/lib/game.py index d06e803..a3172e4 100644 --- a/lib/game.py +++ b/lib/game.py @@ -1,11 +1,13 @@ import yaml from yaml.loader import SafeLoader -from colorama import Fore, Back, Style +from colorama import Fore, Back import re + +from lib.menu import MenuManager from .save import SaveManager from .ascii import AsciiAnimation from time import sleep -from os import system, path, mkdir +from os import system class Game: # the game class keeps information about the loaded game def __init__(self,data:dict): @@ -23,85 +25,48 @@ class Game: # the game class keeps information about the loaded game l = self.save.load() if not l: # New game - print(self.name) - print(self.lang['game_by'].replace("$author",self.author)) - print("") - print(f"1 - {self.lang['start']}") - print(f"2 - {self.lang['options']}") - print(f"0 - {self.lang['quit']}") - selection = self.make_selection(3) + m = MenuManager([self.lang['start'],self.lang['options'],self.lang['quit']],f"{self.name}\n{self.lang['game_by'].replace('$author',self.author)}") + selection = m.selected system("cls||clear") - if(selection == 1): # start new game + if(selection == 0): # start new game self.print_text() - elif(selection == 2): + elif(selection == 1): self.settings_menu() - elif(selection == 0): + elif(selection == 2): print(self.lang['quitting']) exit() else: # Display continue - print(self.name) - print(self.lang['game_by'].replace("$author",self.author)) - print("") - print(f"1 - {self.lang['continue']}") - print(f"2 - {self.lang['new_name']}") - print(f"3 - {self.lang['settings']}") - print(f"0 - {self.lang['quit']}") - selection = self.make_selection(3) + m = MenuManager([self.lang['continue'],self.lang['new_name'],self.lang['options'],self.lang['quit']],f"{self.name}\n{self.lang['game_by'].replace('$author',self.author)}") + selection = m.selected system("cls||clear") - if(selection == 1): + if(selection == 0): self.current = self.save.currentPrompt self.inventory = self.save.inventory self.print_text() - elif(selection == 2): + elif(selection == 1): self.print_text() - elif(selection == 3): + elif(selection == 2): self.settings_menu() - elif(selection == 0): - print("Quitting") + elif(selection == 3): + print(self.lang['quitting']) exit() def settings_menu(self): # displays the settings menu - print("Options") - print("") - print(f"1 - {self.lang['lang']}") - print("0 - Back") - selection = self.make_selection(1) - if(selection == 1): - print(self.lang['lang']) - print("") - print("1 - English") - print("2 - Česky") - print(f"0 - {self.lang['back']}") - selection = self.make_selection(2) + m = MenuManager([self.lang['lang'],self.lang['back']],self.lang['options']) + selection = m.selected + if(selection == 0): + m = MenuManager(["English","Česky",self.lang['back']],self.lang['lang']) + selection = m.selected system("cls||clear") - if(selection == 1): + if(selection == 0): with open("./saves/lang","w") as f: f.write("en") - elif(selection == 2): + elif(selection == 1): with open("./saves/lang","w") as f: f.write("cz") self.settings_menu() else: self.main_menu() - - def make_selection(self, length=0) -> int: # this method makes sure a valid selection is made and returns the selection as a number - # TODO: replace with selection by keyboard(?) - l = length # sets the length - if(l == 0): # if no length was set, we get it from nodes - l = len(self.nodes[self.current]["actions"])-1 - y = False - selection = 0 - # TODO: Check for "has_item" - while y == False: # while the selection is not correct - try: - selection = int(input(self.lang['selection'])) # ask for selection - except ValueError: # handle wrong input type - print(self.lang['not_number']) - if(selection > l or selection < 0): # if the number is bigger than the length or smaller than 0, print error - print(self.lang['invalid']) - else: - y = True # else return the selection - return selection def print_text(self): # Prints out the current prompt system("cls||clear") @@ -109,22 +74,27 @@ class Game: # the game class keeps information about the loaded game 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 - print(self.parse_colors(self.nodes[self.current]["text"])) - print("") - ostring = "" if("actions" in self.nodes[self.current].keys()): - for i,option in enumerate(self.nodes[self.current]["actions"]): - ostring+=f"{i} - {self.nodes[option]['description']}\n" - print(ostring) - sel = self.make_selection() + actions_desc = [] + for option in self.nodes[self.current]["actions"]: + try: + actions_desc.append(self.nodes[option]["description"]) + except: + 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"])) + 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() + else: + print(self.parse_colors(self.nodes[self.current]["text"])) + print("") - def print_animated(self,animid): # prinst the first found occurence of an ascii animation + def print_animated(self,animid): # prints the first found occurence of an ascii animation animation = AsciiAnimation() animation.load_ascii(animid) for frame in animation.frames: diff --git a/lib/lang/cz.yml b/lib/lang/cz.yml index 093797e..8c3d22e 100644 --- a/lib/lang/cz.yml +++ b/lib/lang/cz.yml @@ -20,3 +20,4 @@ not_number: 'Nezadali jste číslo' invalid: 'Neplatný výběr' error_loading: 'Při načítání YAML souboru nastala chyba:' +no_action: 'Chyba: žádná akce "$action" nenalezena v souboru hry' diff --git a/lib/lang/en.yml b/lib/lang/en.yml index 2fa98b2..c4c8615 100644 --- a/lib/lang/en.yml +++ b/lib/lang/en.yml @@ -20,3 +20,4 @@ not_number: 'Not a number selection' invalid: 'Invalid selection' error_loading: 'An exception has occured while loading the game from the YAML file' +no_action: 'Error: No action "$action" found in the game file.' \ No newline at end of file diff --git a/lib/menu.py b/lib/menu.py index ea41d82..fe224e6 100644 --- a/lib/menu.py +++ b/lib/menu.py @@ -1,6 +1,43 @@ +from os import system import keyboard - -class MenuManager: # TODO - def __init__(self,selections:list): +from colorama import Fore +class MenuManager: + ''' + Creates text menus controllable with arrow keys + ''' + def __init__(self,selections:list,additional:str): self.selected = 0 # current selection self.selections = selections # available selections + self.additional = additional # additional text to display above the menu + keyboard.add_hotkey("up",self.up) + keyboard.add_hotkey("down",self.down) + keyboard.add_hotkey("enter",self.make_selection) + self.show_menu() + input() + + def make_selection(self) -> int: + keyboard.remove_all_hotkeys() + + def up(self): + if self.selected == 0: + self.selected = len(self.selections)-1 + else: + self.selected -= 1 + system("cls||clear") + self.show_menu() + + def down(self): + if self.selected == len(self.selections)-1: + self.selected = 0 + else: + self.selected += 1 + system("cls||clear") + self.show_menu() + + def show_menu(self): + 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}")