diff --git a/GUI/GUI.py b/GUI/GUI.py new file mode 100644 index 0000000..1590762 --- /dev/null +++ b/GUI/GUI.py @@ -0,0 +1,277 @@ +from GUI.GUIObjects import Button, TextBox, Toggle +import music.musicTimer as musicTimer # stop music thread in this file +import chapters + +from colorama import Fore +import random + +import pygame +pygame.init() + +class GUI: + def __init__(self): + self.bg_color = (128, 255, 0) + self.space_between_text = 60 + self.music_toggle_size = 88 + + + def set_params_no_gui(self): + self.run_gui = False + + def set_params(self, screen_width: int, screen_height: int, screen: pygame.Surface): + self.run_gui = True + + # find a font that can draw emojis + fonts = pygame.sysfont.get_fonts() + emojis = [font for font in fonts if "emoji" in font] + + if emojis == []: + print("Didn't find font with emojis") + + self.font = pygame.font.Font(None, 60) + self.button_font = pygame.font.Font(None, 40) + self.small_font = pygame.font.Font(None, 30) + else: + self.font = pygame.font.SysFont(emojis[0], 60) + self.button_font = pygame.font.SysFont(emojis[0], 40) + self.small_font = pygame.font.SysFont(emojis[0], 30) + + self.button_left = Button(screen_width * .25, screen_height * .9, 200, 60, text="LEFT", font=self.button_font, bg_color=(200, 200, 200), hover_color=(220, 220, 220)) + self.button_right = Button(screen_width * .75, screen_height * .9, 200, 60, text="RIGHT", font=self.button_font, bg_color=(200, 200, 200), hover_color=(220, 220, 220)) + self.buttons_lst = [self.button_left, self.button_right] + self.screen = screen + self.screen_width, self.screen_height = self.screen.get_size() + + # Logo + logo_width = 400 + + self.logo = pygame.image.load("assets/images/logo.png") + aspect_ratio = self.logo.get_size()[1] / self.logo.get_size()[0] + self.logo = pygame.transform.smoothscale(self.logo, (logo_width, logo_width * aspect_ratio)) + + # BG + bg_height = self.screen_height + self.background = pygame.transform.smoothscale(pygame.image.load("assets/images/landscape.png"), (bg_height* 1.778, bg_height)) + + + # private function + def __seperate_text_to_rows(self, text: str, max_width: int, font_to_use: pygame.font.Font) -> list: + """Takes in: text to split\n max_width allowed, example: screen width\n font to use: What font will the text be rendered in \n + RETURNS pygame.Surface renders of the text that are split according to max_width""" + + output_text_objs = [] + words = text.split(" ") + + # The index of 'words' where the text was longer than 'max_width' + last_overflow_index = 0 + + _index_in_words = 0 + # every loop add 1 more word to sentece + while _index_in_words < len(words) + 1: + _index_in_words += 1 + + # get words that are up to current '_index_in_words' + _text = words[last_overflow_index : _index_in_words] + _text_render = font_to_use.render(" ".join(_text), True, (0,0,0)) + + + # if the render is wider than allowed, render the things that fit in + if _text_render.get_size()[0] > max_width: + _out_text = " ".join(words[last_overflow_index : _index_in_words - 1]) + _final_text = font_to_use.render(_out_text, True, (0,0,0)) + output_text_objs.append(_final_text) + + last_overflow_index = _index_in_words - 1 + _index_in_words -= 1 + + # if last word, add rest and break from loop + if _index_in_words == len(words): + _out_text = " ".join(words[last_overflow_index : _index_in_words]) + _final_text = font_to_use.render(_out_text, True, (0,0,0)) + output_text_objs.append(_final_text) + break + + + return output_text_objs + + # private function + def __render_text_center(self, texts: list): + """Render list of pygame text renders in the center of the screen""" + # Get total height of text elements rendered. Sum all text heights and add the spaces between them + total_height = sum([x.get_size()[1] for x in texts]) + self.space_between_text * (len(texts) - 1) + + # Loop through every text element and render it + for _i, _text in enumerate(texts): + _text_width, _text_height = _text.get_size() + + # Get the position to render it in the center of screen + center_x = self.screen_width / 2 - _text_width / 2 + center_y = (self.screen_height / 2 - _text_height / 2 + 60 * _i) - total_height / 4 # make all texts centered + + self.screen.blit(_text, (center_x, center_y)) + + + def ask_question(self, question: str, left_btn_txt: str, right_btn_txt: str) -> bool: + """Ask a question with two answers.\n + RETURNS: If pressed left_button: Return True. If pressed right_button: Return False""" + if not self.run_gui: + return self.__ask_question_no_gui(question + f" ({left_btn_txt} / {right_btn_txt}) ", left_btn_txt, right_btn_txt, color_before=Fore.GREEN, color_after=Fore.LIGHTMAGENTA_EX) + + # Initalize texts and buttons + text_renders = self.__seperate_text_to_rows(question, self.screen_width - 50, self.font) + self.button_left.text = left_btn_txt + self.button_right.text = right_btn_txt + + # Basic pygame window loop + while(True): + self.screen.blit(self.background, (0, 0)) + + # Update buttons + for btn in self.buttons_lst: + btn.draw(self.screen) + btn.check_hover(pygame.mouse.get_pos()) + + for event in pygame.event.get(): + if event.type == pygame.QUIT: + self.exit_func() + + # If left mousebutton clicked, check if clicked on a button + if event.type == pygame.MOUSEBUTTONDOWN: + if pygame.mouse.get_pressed()[0]: + if self.button_left.check_click(): + return True + if self.button_right.check_click(): + return False + + # Render the text + self.__render_text_center(text_renders) + pygame.display.update() + + + def text_until_enter(self, text: str): + """Render text in the center of the screen until enter is pressed.\n + Includes a small text that says 'press enter to continue'""" + + # initialize texts + text_renders = self.__seperate_text_to_rows(text, self.screen_width - 50, self.font) + enter_text = self.small_font.render("Press Enter to continue", True, (0,0,0)) + + while(True): + self.screen.blit(self.background, (0, 0)) + + for event in pygame.event.get(): + if event.type == pygame.QUIT: + self.exit_func() + + # Return when enter pressed + if event.type == pygame.KEYDOWN: + if event.key == pygame.K_RETURN: + return + + # Render the center text + self.__render_text_center(text_renders) + + # Render 'press enter to continue' text + self.screen.blit(enter_text, (self.screen_width / 2 - enter_text.get_size()[0] / 2, + self.screen_height * .9 - enter_text.get_size()[1] / 2)) + pygame.display.update() + + + def exit_func(self): + musicTimer.musicTimerObj.cancel() # stop music thread, make sure to call these 2 lines every time program exits + musicTimer.musicTimerObj.join() + exit() + + def start_screen(self): + if not self.run_gui: + self.__start_screen_no_gui() + return + + text_box_w = 300 + text_box_h = 100 + name_text_box = TextBox((self.screen_width / 2 - text_box_w / 2, + self.screen_height * .7 - text_box_h / 2, text_box_w, text_box_h), font=self.font) + + music_toggle = Toggle(self.screen_width - self.music_toggle_size - 20, 20, "assets/images/MusicOn.png", "assets/images/MusicOff.png", (self.music_toggle_size, self.music_toggle_size)) + + text = self.font.render("Hi! What is you name?", True, (0,0,0)) + + got_name = False + + while (not got_name): + self.screen.blit(self.background, (0, 0)) + self.screen.blit(self.logo, (self.screen_width / 2 - self.logo.get_size()[0] / 2, + self.screen_height * .3 - self.logo.get_size()[1] / 2)) + + for event in pygame.event.get(): + if event.type == pygame.QUIT: + self.exit_func() + + name_text_box.get_event(event) + if event.type == pygame.MOUSEBUTTONDOWN: + if pygame.mouse.get_pressed()[0]: + music_toggle.check_click(pygame.mouse.get_pos()) + + if music_toggle.get_state() == 1: + pygame.mixer.unpause() + else: + pygame.mixer.pause() + + if event.type == pygame.KEYDOWN: + if event.key == pygame.K_RETURN: + player_name = "".join(name_text_box.buffer) + got_name = True + + self.screen.blit(text, (self.screen_width / 2 - text.get_size()[0] / 2, + self.screen_height * .85)) + music_toggle.draw(self.screen) + + name_text_box.update() + name_text_box.draw(self.screen) + pygame.display.update() + + self.text_until_enter(f"Welcome {player_name} to this adventure!") + + def __ask_question_no_gui(self, question: str, first: str, second: str, color_before: Fore=None, color_after: Fore=None) -> bool: + while(True): + q = "" + + if color_before: + q += color_before + + q += question + + if color_after: + q += color_after + + answer = input(q) + if answer.lower() == first.lower(): + return True + if answer.lower() == second.lower(): + return False + + print("Invalid answer, try again.") + + def __start_screen_no_gui(self): + name = input(Fore.YELLOW + "Type your name: " + Fore.LIGHTBLUE_EX) + print(Fore.LIGHTGREEN_EX + "Welcome", name, "to this adventure!") + + if self.__ask_question_no_gui("Do you want to play? (yes / no) ", "yes", "no", color_before=Fore.YELLOW, color_after=Fore.LIGHTBLUE_EX): + # Yes + print(Fore.LIGHTGREEN_EX + "Let's play! \U0001F3AE") + else: + # No + print("See you later! \U0001F600") + self.exit_func() + + random.choice(chapters.my_list)() + # if self.__ask_question_no_gui("Do you want music? \U0001F3B5 (yes / no) ", "yes", "no", color_before=Fore.YELLOW, color_after=Fore.LIGHTBLUE_EX): + # # Yes + # music_player.music() + # random.choice(chapters.my_list)() + # else: + # # No + # print(Fore.LIGHTGREEN_EX + "Okay \U0001F600") + +# Use this object when calling any function from GUI class +GUIInstance = GUI() \ No newline at end of file diff --git a/GUI/GUIObjects.py b/GUI/GUIObjects.py new file mode 100644 index 0000000..8a6ec13 --- /dev/null +++ b/GUI/GUIObjects.py @@ -0,0 +1,312 @@ +import pygame, os +pygame.init() + +class Button: + def __init__(self, x, y, width, height, bg_color=(120,120,120), hover_color=(150,150,150), text="Text", text_color=(0,0,0), font_size=18, center_text = True, border=0, border_color=(0,0,0), font=None): + self.x = x - width / 2 + self.y = y - height / 2 + self.width = width + self.height = height + self.pos = (self.x, self.y) + self.size = (width,height) + self.image = pygame.Surface(self.size) + self.bg_color = bg_color + self.hover_color = hover_color + self.hovering = False + + if font is None: + self.font = pygame.font.Font(None, font_size) + else: + self.font = font + + self.text = text + self.text_color = text_color + self.font_size = font_size + self.center_text = center_text + self.border = border + self.border_color = border_color + + + def draw(self, screen): + if self.hovering: + if self.border == 0: + self.image.fill(self.hover_color) + else: + self.image.fill(self.border_color) + pygame.draw.rect(self.image, self.hover_color, (self.border, self.border, self.width-self.border*2, self.height-self.border*2)) + + else: + if self.border == 0: + self.image.fill(self.bg_color) + else: + self.image.fill(self.border_color) + pygame.draw.rect(self.image, self.bg_color, (self.border, self.border, self.width-self.border*2, self.height-self.border*2)) + + #text + text = self.font.render(self.text, True, self.text_color) + text_width = text.get_width() + text_height = text.get_height() + + if self.center_text: + self.image.blit(text, (self.width//2-text_width//2,self.height//2-text_height//2)) + else: self.image.blit(text, (self.border+5,self.height//2-text_height//2)) + screen.blit(self.image, self.pos) + + + def check_hover(self, mouse_pos): + if mouse_pos[0] >= self.x and mouse_pos[0] <= self.x+self.width and mouse_pos[1] >= self.y and mouse_pos[1] <= self.y+self.height: + self.hovering = True + else: + self.hovering = False + + + def check_click(self): + if self.hovering: + return True + + return False + + +class Text_box(): + def __init__(self,x,y,width,height,bg_color=(155,155,155),active_color=(200,200,200), + text_size=24, text_color=(0,0,0), border=0, border_color=(0,0,0), only_letters=False, + only_numbers=False, placeholder_txt="Text", placeholder_color=(100,100,100), max_chars=-1): + self.x = x - width / 2 + self.y = y - height / 2 + self.width = width + self.height = height + self.pos = (self.x, self.y) + self.size = (width, height) + self.image = pygame.Surface((width, height)) + self.bg_color = bg_color + self.active_color = active_color + self.active = False + self.text = "" + self.text_size = text_size + self.text_color = text_color + self.border_color = border_color + self.font = pygame.font.Font(None, self.text_size) + self.border = border + self.numbers = [48, 49, 50, 51, 52, 53, 54, 55, 56, 57] + self.only_letters = only_letters + self.only_numbers = only_numbers + self.placeholder_txt = placeholder_txt + self.placeholder_color = placeholder_color + self.max_chars = max_chars # -1 is infinite + if self.max_chars < 0: + self.inifnite_chars = True + else: self.inifnite_chars = False + + def draw(self, screen): + if not self.active: + if self.border == 0: + self.image.fill(self.bg_color) + else: + self.image.fill(self.border_color) + pygame.draw.rect(self.image, self.bg_color, (self.border, self.border, + self.width-self.border*2, self.height-self.border*2)) + + else: + if self.border == 0: + self.image.fill(self.active_color) + else: + self.image.fill(self.border_color) + pygame.draw.rect(self.image, self.active_color, (self.border, self.border, + self.width-self.border*2, self.height-self.border*2)) + + #rendering text + if self.text == "": + placeholder_txt = self.font.render(self.placeholder_txt, True, self.placeholder_color) + placeholder_txt.set_alpha(100) + text_width = placeholder_txt.get_width() + text_height = placeholder_txt.get_height() + if text_width < self.width-self.border*2: + self.image.blit(placeholder_txt, (2+self.border*2,(self.height-text_height)//2)) + else: + self.image.blit(placeholder_txt, ((self.border*2)+(self.width-text_width-self.border*3),(self.height-text_height)//2)) + + else: + text = self.font.render(self.text, False, self.text_color) + text_width = text.get_width() + text_height = text.get_height() + if text_width < self.width-self.border*2: + self.image.blit(text, (2+self.border*2,(self.height-text_height)//2)) + else: + self.image.blit(text, ((self.border*2)+(self.width-text_width-self.border*3),(self.height-text_height)//2)) + + screen.blit(self.image, self.pos) + + def add_text(self, key): + + if not self.active: + return + + try: + # Backspace + if key == 8: + text = list(self.text) + text.pop() + self.text = "".join(text) + + if len(self.text) < self.max_chars or self.inifnite_chars: + # Adding numbers + if not self.only_letters: + if key in self.numbers: + text = list(self.text) + if key < 100: + text.append(str(key-48)) + self.text = "".join(text) + + # Spacebar + if key == 32: + text = list(self.text) + text.append(" ") + self.text = "".join(text) + # Dot + elif key == 46: + text = list(self.text) + text.append(".") + self.text = "".join(text) + + + # Add letters + if not self.only_numbers: + if chr(key).isalpha(): + text = list(self.text) + text.append(chr(key)) + self.text = "".join(text) + # Coma + elif key == 44: + text = list(self.text) + text.append(",") + self.text = "".join(text) + except: + # Invalid key + print(key, "is invalid key") + + def check_click(self, mouse_pos): + if mouse_pos[0] >= self.x and mouse_pos[0] <= self.x+self.width and mouse_pos[1] >= self.y and mouse_pos[1] <= self.y+self.height: + self.active = True + else: + self.active = False + + def return_val(self): + if self.only_letters: + return self.text + + try: + return float(self.text) + except: + return 0 + + +class Toggle: + def __init__(self, x: int, y: int, image_on_path: str, image_off_path: str, image_size: tuple, default_state: int=1): + self.x = x + self.y = y + self.w, self.h = image_size + self.image_on = pygame.transform.smoothscale(pygame.image.load(image_on_path), image_size) + self.image_off = pygame.transform.smoothscale(pygame.image.load(image_off_path), image_size) + self.default_state = default_state + self.current_state = 1 # 1 on, -1 off + + def check_click(self, mouse_pos: tuple): + if mouse_pos[0] >= self.x and mouse_pos[0] <= self.x+self.w and mouse_pos[1] >= self.y and mouse_pos[1] <= self.y+self.h: + self.current_state *= -1 + + def draw(self, screen: pygame.Surface): + if self.current_state == 1: + screen.blit(self.image_on, (self.x, self.y)) + elif self.current_state == -1: + screen.blit(self.image_off, (self.x, self.y)) + + def get_state(self) -> int: + return self.current_state + +# Taken from https://github.com/Mekire/pygame-textbox +import string +import pygame as pg + +ACCEPTED = string.ascii_letters+string.digits+string.punctuation+" " + +class TextBox(object): + def __init__(self,rect,**kwargs): + self.rect = pg.Rect(rect) + self.buffer = [] + self.final = None + self.rendered = None + self.render_rect = None + self.render_area = None + self.blink = True + self.blink_timer = 0.0 + self.process_kwargs(kwargs) + + def process_kwargs(self,kwargs): + defaults = {"id" : None, + "command" : None, + "active" : True, + "color" : pg.Color("white"), + "font_color" : pg.Color("black"), + "outline_color" : pg.Color("black"), + "outline_width" : 2, + "active_color" : pg.Color("black"), + "font" : pg.font.Font(None, self.rect.height+4), + "clear_on_enter" : False, + "inactive_on_enter" : True, + "max_length" : 999} + for kwarg in kwargs: + if kwarg in defaults: + defaults[kwarg] = kwargs[kwarg] + else: + raise KeyError("InputBox accepts no keyword {}.".format(kwarg)) + self.__dict__.update(defaults) + + def get_event(self,event): + if event.type == pg.KEYDOWN and self.active: + if event.key in (pg.K_RETURN,pg.K_KP_ENTER): + self.execute() + elif event.key == pg.K_BACKSPACE: + if self.buffer: + self.buffer.pop() + elif event.unicode in ACCEPTED: + if len(self.buffer) < self.max_length: + self.buffer.append(event.unicode) + + elif event.type == pg.MOUSEBUTTONDOWN and event.button == 1: + self.active = self.rect.collidepoint(event.pos) + + def execute(self): + if self.command: + self.command(self.id,self.final) + self.active = not self.inactive_on_enter + if self.clear_on_enter: + self.buffer = [] + + def update(self): + new = "".join(self.buffer) + if new != self.final: + self.final = new + self.rendered = self.font.render(self.final, True, self.font_color) + self.render_rect = self.rendered.get_rect(x=self.rect.x+2, + centery=self.rect.centery) + if self.render_rect.width > self.rect.width-6: + offset = self.render_rect.width-(self.rect.width-6) + self.render_area = pg.Rect(offset,0,self.rect.width-6, + self.render_rect.height) + else: + self.render_area = self.rendered.get_rect(topleft=(0,0)) + if pg.time.get_ticks()-self.blink_timer > 200: + self.blink = not self.blink + self.blink_timer = pg.time.get_ticks() + + def draw(self,surface): + outline_color = self.active_color if self.active else self.outline_color + outline = self.rect.inflate(self.outline_width*2,self.outline_width*2) + surface.fill(outline_color,outline) + surface.fill(self.color,self.rect) + if self.rendered: + surface.blit(self.rendered,self.render_rect,self.render_area) + if self.blink and self.active: + curse = self.render_area.copy() + curse.topleft = self.render_rect.topleft + surface.fill(self.font_color,(curse.right+1,curse.y,2,curse.h)) diff --git a/README.md b/README.md index f60dbbe..8e264a5 100644 --- a/README.md +++ b/README.md @@ -61,6 +61,11 @@ Further, the following songs are used (copyright free, no license required): - Snake On The Beach by Nico Staf - A Parisian Cafe by Aaron Kenny +### Other Credits + +- Background Image (copyright free) by PWL https://opengameart.org/content/seamless-hd-landscape-in-parts +- Code for Text box from https://github.com/Mekire/pygame-textbox + # Contributors Thanks to all our contributors for their active support and participation! diff --git a/assets/images/MusicOff.png b/assets/images/MusicOff.png new file mode 100644 index 0000000..3798481 Binary files /dev/null and b/assets/images/MusicOff.png differ diff --git a/assets/images/MusicOn.png b/assets/images/MusicOn.png new file mode 100644 index 0000000..e1751a7 Binary files /dev/null and b/assets/images/MusicOn.png differ diff --git a/assets/images/landscape.png b/assets/images/landscape.png new file mode 100644 index 0000000..cf4bc73 Binary files /dev/null and b/assets/images/landscape.png differ diff --git a/chapters.py b/chapters.py index 885bd35..b98644a 100644 --- a/chapters.py +++ b/chapters.py @@ -4,254 +4,147 @@ import colorama from colorama import Fore -import music.musicTimer as musicTimer # stop music thread in this file from music_player import * +from GUI.GUI import GUIInstance colorama.init(convert=True) # start the game - +# NOT CALLED ANYWHERE def start(): - answer = input( - Fore.GREEN + - "You are on a dirt road. Which way do you want to go left or right? " + - Fore.LIGHTMAGENTA_EX).lower() - # if user inputs left then - if answer == "left": + if GUIInstance.ask_question("You are on a dirt road. Which way do you want to go left or right?", "Left", "Right"): random.choice(my_list)() - - # if user inputs right then - if answer == "right": + else: random.choice(my_list)() def chapter_river(): - answer = input( - Fore.GREEN + - "You come to a river, you can walk around it or swim across." - "Type walk to walk around & swim to swim across. " + - Fore.LIGHTMAGENTA_EX).lower() - # if user inputs swim then - if answer == "swim": - game_over( - Fore.RED + - "You swam across the river and were eaten by an alligator \U0001F480" - ) - # if user inputs walk then - elif answer == "walk": - # q2 - answer = input( - Fore.GREEN + - "You walked for many miles, ran out of water and remembered " - "that there was a shop far away (10 miles/16kms) which supplies water." - "Do you want to go there (yes/no)? " + - Fore.LIGHTMAGENTA_EX).lower() - # if user inputs no then - if answer == "no": - game_over( - Fore.RED + - "You become de-hydrated and died of thirst when you were walking. \U0001F480" - ) - - # if user inputs yes then - elif answer == "yes": - print( - Fore.GREEN + - "You went 10 miles walking and bought 10 liters of drinking water. " - ) - # q3 - answer = input( - Fore.GREEN + - "You are thirsty, do you want to drink some water (yes/no)? " + - Fore.LIGHTMAGENTA_EX).lower() - # if user inputs yes then - if answer == "yes": - print(Fore.GREEN + - "You drank 5 liters of water and now you feel refreshed.") + if GUIInstance.ask_question("You come to a river, you can walk around it or swim across.", "Walk", "Swim"): + # 1. Walk + if GUIInstance.ask_question("You walked for many miles, ran out of water and remembered that there was a shop far away which supplies water. Do you want to go there?", "Yes", "No"): + # 2. Yes + if GUIInstance.ask_question("You went 10 miles walking and bought 10 liters of drinking water. Do you want to drink the water?", "Yes", "No"): + # 3. Yes + GUIInstance.text_until_enter("You drank 5 liters of water and now you feel refreshed.") + if GUIInstance.ask_question("Do you want to walk further or go back home?", "Further", "Home"): + # 4. Further + game_over("You walked 100 more miles and you WIN the game! \U0001f3c6", win=True) + else: + # 4. Home + game_over("A car crashed you and you were rushed to hospital. Although, it was too late by the time you reached the hospital, and you had already died. \U0001F480") + + else: + # 3. No + game_over("You died of thirst.\U0001F480") - # if user inputs no then - elif answer == "no": - game_over(Fore.RED + "You died of thirst.\U0001F480 ") else: - print(Fore.RED + "Not a valid answer. You die. \U0001F480") - random.choice(my_list)() - # q4 - answer = input( - Fore.GREEN + - "You drank 5 liters of water and now you feel refreshed. Do you want to walk further or go back home? (further/home) " - + Fore.LIGHTMAGENTA_EX).lower() - # if user inputs further then - if answer == "further": - game_over( - Fore.RED + - "You walked 100 more miles and you WIN the game! \U0001f3c6", - win=True, - ) - # if user inputs home then - if answer == "home": - game_over( - Fore.RED + - "A car crashed you and you were rushed to hospital. Although, it was too late by the time you reached the hospital, and you had already died. \U0001F480" - ) - else: - print(Fore.RED + "Not a valid answer. You die. \U0001F480") - game_over() + # 2. No + game_over("You were very de-hydrated and died of thirst when you were walking. \U0001F480") + + else: + # 1. Swim + game_over("You swam across the river and were eaten by an aligator \U0001F480") + def chapter_bridge(): - answer = input( - Fore.GREEN + "You come to a bridge, it looks wobbly," - "do you want to cross or do you want to head back? (cross/back) " + - Fore.LIGHTMAGENTA_EX).lower() - # if user inputs back then - if answer == "back": - answer = input( - Fore.GREEN + "You go back to the main road." - "Now you can decide to drive forward or turn left. (forward/left) " - + Fore.LIGHTMAGENTA_EX).lower() - if answer == "forward": - game_over( - Fore.RED + - "You drive forward and crashed into a tree and die.\U0001F480 ") - # if users inputs left then - if answer == "left": - chapter_lake() - # if user inputs cross then - elif answer == "cross": + if GUIInstance.ask_question("You come to a bridge, it looks wobbly. Do you want to cross it or do you want to head back?", "Cross", "Back"): + # 1. Cross chapter_stranger() + else: - print(Fore.RED + "Not a valid answer. You die. \U0001F480 ") - game_over() + # 1. Back + if GUIInstance.ask_question("You go back to the main road. Now you can decide to drive forward or turn left.", "Forward", "Left"): + # 2. Forward + game_over("You drive forward and crash into a tree and die.\U0001F480") + else: + # 2. Left + chapter_lake() def chapter_stranger(): - answer = input( - Fore.GREEN + - "You crossed the bridge and meet a stranger, do you want to talk to them? (y/n) " - + Fore.LIGHTMAGENTA_EX).lower() - if answer == "n": - game_over( - Fore.RED + - "The stranger was not pleased by you and murdered you. \U0001F480") - elif answer == "y": - answer = input(Fore.GREEN + - "You talk to a wizard and they ask you," - "do you want to be a wizard? (y/n) " + Fore.LIGHTMAGENTA_EX).lower() - if answer == "y": - game_over(Fore.RED + - "You are a wizard and you WIN the game! \U0001f3c6", - win=True) - elif answer == "n": - game_over( - Fore.RED + - "The stranger was not pleased by you and murdered you. \U0001F480" - ) - - -def chapter_lake(): - answer = input(Fore.GREEN + "You come to a lake," - "do you want to swim or go back? (swim/back) " + - Fore.LIGHTMAGENTA_EX).lower() - if answer == "swim": - game_over( - Fore.RED + - "You swam across the lake and were eaten by a shark. \U0001F480 ") - - elif answer == "back": - answer = input( - Fore.GREEN + "You go to the main road." - "Now you can decide to drive forward or turn left. (forward/left) " - + Fore.LIGHTMAGENTA_EX).lower() - if answer == "forward": - chapter_tree() - elif answer == "left": - game_over(Fore.RED + "You died. \U0001F480") + if GUIInstance.ask_question("You cross the bridge and meet a stranger, do you talk to them?", "Yes", "No"): + # 1. Yes + if GUIInstance.ask_question("You talk a wizard and he asks you, do you want to be a wizard?", "Yes", "No"): + # 2. Yes + game_over("You bacome a wizard and WIN the game! \U0001f3c6", win=True) + else: + # 2. No + game_over("The stranger was not pleased by you and murdered you. \U0001F480") else: - game_over(Fore.RED + "Not a valid answer. You die.") - - + # 1. No + game_over("The stranger was not pleased by you and murdered you. \U0001F480") + + def chapter_mountain(): - answer = input(Fore.GREEN + "\nYou reached a mountain \U000026F0. \n" - "Do you want to climb or go back? (Type \"climb/c\" to proceed or " - "\"back/b\" to return) : " + - Fore.LIGHTMAGENTA_EX).lower() - if answer == "climb" or answer == 'c': - game_over( - Fore.RED + - "You climbed to the peak \nbut due to low temperature you frozen. \U0001F976 ") + if GUIInstance.ask_question("You reached a mountain. Do you want to climb it?", "Yes", "No"): + # 1. Yes + if GUIInstance.ask_question("You start climbing the mountain. You see a rope bridge ahead. Do you want to cross it?", "Yes", "No"): + # 2. Yes + game_over("You walk on the bridge, but suddenly it collapses. You fall to the ground and die \U0001F480") + else: + # 2. No + if GUIInstance.ask_question("Do you want to continue climbing or go back down?", "Climb", "Back"): + # 3. Climb + game_over("You climb the mountain for many days, and you finally reach the top. You WIN the game! \U0001f3c6", win=True) + else: + # 3. Back + GUIInstance.text_until_enter("You climb down safely.") + random.choice(my_list)() - elif answer == "back" or answer == 'b': - answer = input( - Fore.GREEN + "You return to the main road." - "Now you can choose to drive straight ahead or turn left. (Type \"forward/f\" to proceed or " - "\"left/l\" to return.) : " - + Fore.LIGHTMAGENTA_EX).lower() - if answer == "forward" or answer == 'f': - chapter_tree() - elif answer == "left" or answer == 'l': - game_over(Fore.RED + "You died. \U0001F480") else: - game_over(Fore.RED + "I'm sorry, I don't understand the input. You Died. \U0001F480") - + # 1. No + random.choice(my_list)() -def chapter_tree(): - answer = input( - Fore.GREEN + - "You are very hungry and you see a tree with apples, do you want to eat the fruit? (y/n) " - + Fore.LIGHTMAGENTA_EX) - if answer == "y": - game_over( - Fore.RED + - "You ate the fruit but it was poisonous and you died. \U0001F480") - elif answer == "n": - answer = input(Fore.GREEN + - "You are nearly starving to death. Do you want to eat Pears instead of apples? (y/n) " - + Fore.LIGHTMAGENTA_EX).lower() - if answer == "y": - game_over( - Fore.RED + - "You ate the pears but they were poisonous and you died. \U0001F480" - ) - elif answer == "n": - game_over( - Fore.RED + - "You were so hungry that you were nearly going to die in a few seconds, but a lovely gentleman gave you some food and you WIN the game! \U0001f3c6", - win=True, - ) +def chapter_lake(): + if GUIInstance.ask_question("You turned left and you come to a lake, do you want to swim or go back?", "Swim", "Back"): + # 1. Swim + game_over("You swam across the lake and were eaten by a shark. \U0001F480 ") + else: + # 1. Back + if GUIInstance.ask_question("You go back to the main road. Now you can decide to drive forward or turn left.", "Forward", "Left"): + # 2. Forward + game_over("You died. \U0001F480") # Swapped these two answers around because there is a same question with different answer else: - print(Fore.RED + "Not a valid answer. You die. \U0001F480") - game_over() + # 2. Left + chapter_tree() -def game_over(message: str = None, *, end_game=True, win=False): - "Prints Game over message" - if message: - print(message) - if win: - print("CONGRATULATIONS on winning the game!") +def chapter_tree(): + if GUIInstance.ask_question("You are very hungry and you see a tree with apples, do you want to eat the fruit?", "Yes", "No"): + # 1. Yes + game_over("You ate the fruit but it was poisonous and you died. \U0001F480") else: - print(Fore.BLUE + "Game over") - print(Fore.LIGHTYELLOW_EX + "Thanks for playing!") - if game_over: - # Play Again - answer = input(Fore.YELLOW + "Do you want to play again? (y/n) " + - Fore.LIGHTBLUE_EX) - if answer == "y" or answer == "yes": - random.choice(my_list)() - if music == "on": - print(Fore.GREEN + "Music is on") - elif music == "off": - print(Fore.RED + "Music is off") - music.music() + # 1. No + if GUIInstance.ask_question("You are nearly starving to death. Do you want to eat Pears instead of apples?", "Yes", "No"): + # 2. Yes + game_over("You ate the pears but they were poisonous and you died. \U0001F480") + else: + # 2. No + game_over("You were super hungry and nearly died, but a lovely gentleman gave you some food and you WIN the game! \U0001f3c6", win=True) + + +def game_over(message: str = None, *, win=False): + "Shows Game over message" + if not GUIInstance.run_gui: + # No gui + if win: + print(Fore.YELLOW + message) else: - print(Fore.RED + "Thanks for playing!") - musicTimer.musicTimerObj.cancel() # stop music thread - # make sure to call these 2 lines every time program exits - musicTimer.musicTimerObj.join() - - exit() + print(Fore.RED + message) + + elif message: + # Gui and message + GUIInstance.text_until_enter(message) + + if GUIInstance.ask_question("Thanks for playing!", "Play Again", "Quit"): + # Play again + random.choice(my_list)() + else: + # Quit + GUIInstance.exit_func() -my_list = [chapter_bridge, chapter_lake, chapter_mountain] +my_list = [chapter_bridge, chapter_lake, chapter_mountain, chapter_river] diff --git a/main.py b/main.py index 04c7723..11a7f6a 100644 --- a/main.py +++ b/main.py @@ -1,16 +1,24 @@ # import modules import subprocess import sys + +# Set the taskbar icon to same as pygame window icon +import ctypes +myappid = 'KendallDoesCoding.ChooseYourAdventureGame.1.0' # arbitrary string +ctypes.windll.shell32.SetCurrentProcessExplicitAppUserModelID(myappid) + +import os +os.environ["PYGAME_HIDE_SUPPORT_PROMPT"] = "hide" + import pkg_resources -import numpy as np -import matplotlib.pyplot as plt -from PIL import Image from chapters import * -from music_player import * +import music_player + +from GUI.GUI import GUIInstance # install missing modules -required = {"playsound==1.2.2", "colorama==0.4.6"} +required = {"colorama==0.4.6", "pygame==2.2.0"} installed = {pkg.key for pkg in pkg_resources.working_set} missing = required - installed @@ -20,19 +28,12 @@ stdout=subprocess.DEVNULL) # import dependencies -import colorama +import pygame +pygame.init() -# heading text! -ImageAddress = 'assets/images/logo.png' -ImageAddress = 'assets\images/logo.png' -ImageItself = Image.open(ImageAddress) -ImageNumpyFormat = np.asarray(ImageItself) -plt.imshow(ImageNumpyFormat) -plt.draw() -plt.pause(1) # pause how many seconds -plt.close() # heading text! + heading = "Choose Your Own Adventure Game!" copyright = "\U000000A9 2023, KendallDoesCoding, All Rights Reserved" new_str = Fore.BLUE + heading.center(150) @@ -40,30 +41,29 @@ print(new_str) print(new_str2) +SCR_W = 800 +SCR_H = 600 +RUN_GUI = True + +try: + WINDOW = pygame.display.set_mode((SCR_W, SCR_H)) +except Exception as e: + RUN_GUI = False + print(f"Running without pygame") + +pygame.display.set_caption("Choose Your Own Adventure") +pygame.display.set_icon(pygame.image.load("assets/images/logo.png")) # SET PYGAME WINDOW ICON + def main(): - # welcome to the game - name = input(Fore.YELLOW + "Type your name: " + Fore.LIGHTBLUE_EX) - print(Fore.LIGHTGREEN_EX + "Welcome", name, "to this adventure!") - - # do you want to play? - answer = input(Fore.YELLOW + "Do you want to play? (y/n) " + - Fore.LIGHTBLUE_EX) - if answer == "y" or answer == "yes": - # starting the game - print(Fore.LIGHTGREEN_EX + "Let's play! \U0001F3AE") - if answer == "n" or answer == "no": - print("See you later! \U0001F600") - exit() - # do you want music? - answer = input(Fore.YELLOW + "Do you want music? \U0001F3B5 (y/n) " + - Fore.LIGHTBLUE_EX) - if answer == "y" or answer == "yes": - music() - random.choice(my_list)() - if answer == "n" or answer == "no": - print(Fore.LIGHTGREEN_EX + "Okay \U0001F600") - random.choice(my_list)() + if RUN_GUI: + GUIInstance.set_params(SCR_W, SCR_H, WINDOW) + music_player.music() + else: + GUIInstance.set_params_no_gui() + + GUIInstance.start_screen() + random.choice(my_list)() if __name__ == "__main__": diff --git a/music/__init__.py b/music/__init__.py deleted file mode 100644 index e69de29..0000000 diff --git a/music_player.py b/music_player.py index 63e852c..6c39d06 100644 --- a/music_player.py +++ b/music_player.py @@ -6,6 +6,7 @@ from colorama import Fore import music.musicTimer as musicTimer +from GUI.GUI import GUIInstance # Initialize songs @@ -172,8 +173,9 @@ def music(): pygame.init() pygame.mixer.init() - start_song() - print(Fore.BLUE + "Music has started") + start_song(print_song_name = not GUIInstance.run_gui) + if not GUIInstance.run_gui: + print(Fore.BLUE + "Music has started") # Another function to not print all the stuff when starting new song diff --git a/requirements.txt b/requirements.txt index af55d81..2f27ad6 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,4 +1,2 @@ colorama==0.4.6 -numpy==1.22.4 -matplotlib==3.7.1 pygame==2.2.0 \ No newline at end of file