commit fa3353c47103e96532fc1e01c0573200ca2d6dbe Author: Tucan444 Date: Wed Dec 16 20:47:09 2020 +0100 adding files cc diff --git a/Hashi.py b/Hashi.py new file mode 100644 index 0000000..ed8d1be --- /dev/null +++ b/Hashi.py @@ -0,0 +1,864 @@ +from hashi_functions import * +import hashi_generator as hg +import hashi_game +import assets.palettes.palette_manager as pm +from menu_effect import Circle_effect +from game_effect import Rectangle_effect +from widget_engine import * +from s_engine import * +import sounds +import pygame +import sys +import json +from pygame.locals import * + +# basic config +pygame.mixer.pre_init(48000, -16, 2, 512) +pygame.init() +pygame.mixer.set_num_channels(16) +available = pygame.font.get_fonts() +monitor_size = [pygame.display.Info().current_w, pygame.display.Info().current_h] + +font = pygame.font.SysFont('calibri', 60, True) +small_font = pygame.font.SysFont('calibri', 40, True) +tiny_font = pygame.font.SysFont('calibri', 20, True) + +Window_size = [600, 650] +Default_size = Window_size +screen = pygame.display.set_mode(Window_size) +display = pygame.Surface((600, 650)) +pygame.display.set_caption("Hashi") +pygame.display.set_icon(pygame.image.load("assets/images/general/hashi_logo.png").convert()) +clock = pygame.time.Clock() + +soundsX = sounds.get_sounds() + + +def main_menu(screenX, Win_size): + # PREP WORK + # getting setup + with open("assets/saves/setup.json", "r") as f: + setup = json.load(f) + # game variables + game = Game() + game.game_flow["ac"] = True + game.game_flow["custom_string"] = setup["custom_num"] + # next 3 lines for arrow buttons + game.game_flow["dfs"] = ["easy", "medium", "hard", "extreme", "custom"] + game.game_flow["df"] = setup["difficulty"] + game.game_flow["df_change"] = False + game.game_flow["dp"] = [0, 0] + game.game_flow["back_from_game"] = False + game.game_flow["Win_size"] = Win_size + game.game_flow["cut_click"] = False + game.game_flow["change_fs"] = False + + # mouse + mouse = Mouse(pygame.mouse.get_pos()) + mouse.update(Win_size, Default_size) + + # dealing with palettes + palette = pm.Palettes("assets/palettes") + palette.current_palette = setup["palette"] + palette.palette = palette.get_palette() + + menu_objects = [] + + # loading images + play_images = {"idle": pygame.image.load("assets/images/menu/play.png").convert(), + "hover": pygame.image.load("assets/images/menu/play_hover.png").convert()} + # size 200 + + palette_images = {"idle": pygame.image.load("assets/images/menu/palette.png").convert(), + "hover": pygame.image.load("assets/images/menu/palette_hover.png").convert()} + # size 140 + + new_game_images = {"idle": pygame.image.load("assets/images/menu/new_game.png").convert(), + "hover": pygame.image.load("assets/images/menu/new_game_hover.png").convert()} + # size 300 150 + + difficulty_images = {"idle": pygame.image.load("assets/images/menu/template.png").convert(), + "hover": pygame.image.load("assets/images/menu/template_hover.png").convert()} + # size 250 100 + + right_arrow_images = {"idle": pygame.image.load("assets/images/menu/arrow.png").convert(), + "hover": pygame.image.load("assets/images/menu/arrow_hover.png").convert()} + left_arrow_images = {"idle": pygame.transform.flip(pygame.image.load("assets/images/menu/arrow.png").convert(), + True, False), + "hover": pygame.transform.flip( + pygame.image.load("assets/images/menu/arrow_hover.png").convert(), + True, False)} + # size 100 100 + + custom_size_images = {"idle": pygame.image.load("assets/images/menu/custom.png").convert(), + "hover": pygame.image.load("assets/images/menu/custom_pressed.png").convert()} + # size 100 100 + + fs_images = {"idle": pygame.image.load("assets/images/menu/fs.png").convert(), + "hover": pygame.image.load("assets/images/menu/fs_hover.png").convert()} + ms_images = {"idle": pygame.image.load("assets/images/menu/ms.png").convert(), + "hover": pygame.image.load("assets/images/menu/ms_hover.png").convert()} + # size 60 60 + + menu_objects.append(CircleButton(200, [200, 220], play_images, play, [game, palette, mouse])) + menu_objects.append(CircleButton(200, [440, 20], palette_images, next_palette, [palette])) + menu_objects.append(Button([150, 450], [300, 150], new_game_images, new_game, [game, palette, mouse])) + menu_objects.append(Button([20, 20], [250, 100], difficulty_images, play_click_sound, [])) + menu_objects.append(Button([440, 270], [100, 100], right_arrow_images, right_arrow, [game])) + menu_objects.append(Button([60, 270], [100, 100], left_arrow_images, left_arrow, [game])) + menu_objects.append(Button([480, 400], [100, 100], custom_size_images, activate_edit_text, [game], False)) + menu_objects.append(Button([20, 570], [60, 60], fs_images, change_fs, [game])) + menu_objects[-1].name = "fs" + menu_objects.append(Button([20, 570], [60, 60], ms_images, change_fs, [game], False)) + menu_objects[-1].name = "ms" + difficulty_text = font.render(setup["difficulty"].capitalize(), False, palette.palette["outline-dark"]) + + # setting up custom text + + custom_text = font.render(game.game_flow["custom_string"], False, palette.palette["outline-dark"]) + custom_text_hover = font.render(game.game_flow["custom_string"], False, palette.palette["background"]) + ct_images = {"idle": create_text_sur(custom_text, [20, 20], [100, 100]), + "hover": create_text_sur(custom_text_hover, [20, 20], [100, 100])} + menu_objects.append(Button([480, 400], [100, 100], ct_images, activate_edit_text, [game], False)) + menu_objects[-1].name = "custom_text" + + custom_pointer = palette.swap_image(pygame.image.load("assets/images/menu/writing.png").convert(), + "emerald", setup["palette"]) + custom_pointer.set_colorkey((0, 0, 0)) + + # setting up custom + + if setup["difficulty"] == "custom": + for item in menu_objects: + if item.on_click == activate_edit_text: + item.active = True + + # circle effect + + cE = Circle_effect + cE.generate_circles(50, [-50, 50]) + cE.generate_circles(50, [480, 600]) + + # last config + + menu_objects, setup = set_palette(setup, palette, setup["palette"], menu_objects, True) + + palette.create_cycle() + game.game_flow["setup"] = setup + game.game_flow["screen"] = screenX + + # checking tutorial + if setup["tutorial"]: + run_tutorial(screenX, setup, game, palette, mouse) + + # game loop + while game.alive: + # checking if returned + if game.game_flow["back_from_game"]: + game.game_flow["back_from_game"] = False + Win_size = game.game_flow["Win_size"] + + with open("assets/saves/setup.json", "r") as f: + setup = json.load(f) + + game.game_flow["setup"] = setup + + # background + display.fill(palette.palette["background"]) + pygame.draw.rect(display, palette.palette["outline-shade"], pygame.Rect(180, 0, 240, 650)) + pygame.draw.rect(display, palette.palette["outline"], pygame.Rect(200, 0, 200, 650)) + + # displaying effect + + for circle in cE.circles: + circle.move(display, palette) + + if cE.timer == 0: + cE.generate_circles(4, [-50, 50]) + cE.generate_circles(4, [480, 600]) + cE.timer += 10 + else: + cE.timer -= 1 + + # displaying widgets + + for widget in menu_objects: + widget.blit(display) + + # dealing with difficulty + + if game.game_flow["df_change"]: + game.game_flow["df_change"] = False + + # dealing with json + setup["difficulty"] = game.game_flow["df"] + with open("assets/saves/setup.json", "w") as f: + json.dump(setup, f, indent=4) + + difficulty_text = font.render(setup["difficulty"].capitalize(), False, palette.palette["outline-dark"]) + + # changing custom edit text + if setup["difficulty"] == "custom": + for item in menu_objects: + if item.on_click == activate_edit_text: + item.active = True + else: + for item in menu_objects: + if item.on_click == activate_edit_text: + item.active = False + + display.blit(difficulty_text, [40, 40]) + + # checking palette + + if palette.changed: + palette.changed = False + menu_objects, setup = set_palette(setup, palette, palette.current_palette, menu_objects) + difficulty_text = font.render(setup["difficulty"].capitalize(), False, palette.palette["outline-dark"]) + custom_pointer = palette.swap_image(pygame.image.load("assets/images/menu/writing.png").convert(), + "emerald", setup["palette"]) + custom_pointer.set_colorkey((0, 0, 0)) + game.game_flow["setup"] = setup + + # custom pointer + + if game.game_flow["ac"] is False: + display.blit(custom_pointer, [480, 350]) + + # event loop + + for event in pygame.event.get(): + if event.type == QUIT: + pygame.quit() + sys.exit(0) + + # keydown + elif event.type == KEYDOWN: + if event.key == K_ESCAPE: + pygame.quit() + sys.exit(0) + elif event.key == K_f: + game.fs = not game.fs + if game.fs is False: + Win_size = Default_size + screenX = pygame.display.set_mode(Win_size) + game.game_flow["dp"] = [0, 0] + mouse.mouse_scroll = game.game_flow["dp"] + + game.game_flow["Win_size"] = Win_size + game.game_flow["screen"] = screenX + else: + screenX = pygame.display.set_mode(monitor_size, pygame.FULLSCREEN) + d = screenX + ratio = [Default_size[1] / Default_size[0], Default_size[0] / Default_size[1]] + # u chose width or height here + + if Default_size[0] > Default_size[1]: + Win_size = [d.get_width(), int(d.get_width() * ratio[0])] + d = d.get_height() + dd = Win_size[1] + game.game_flow["dp"][1] = (d - dd) / 2 + mouse.mouse_scroll = game.game_flow["dp"] + else: + Win_size = [int(d.get_height() * ratio[1]), d.get_height()] + d = pygame.display.get_surface().get_width() + dd = Win_size[0] + game.game_flow["dp"][0] = (d - dd) / 2 + mouse.mouse_scroll = game.game_flow["dp"] + + game.game_flow["Win_size"] = Win_size + game.game_flow["screen"] = screenX + + if game.game_flow["ac"]: + # mouse stuff + if event.type == MOUSEMOTION: + mouse.update(Win_size, Default_size) + for widget in menu_objects: + widget.hover_check(mouse.mouse_pos) + + # click + if event.type == MOUSEBUTTONDOWN: + for widget in menu_objects: + widget.click_check(mouse.mouse_pos) + if game.game_flow["cut_click"]: + game.game_flow["cut_click"] = False + break + else: + if event.type == MOUSEBUTTONDOWN: + mouse.update(Win_size, Default_size) + game.game_flow["ac"] = True + for widget in menu_objects: + widget.hover_check(mouse.mouse_pos) + soundsX["click"].play() + + # checking if num big enough + try: + if int(game.game_flow["custom_string"]) < 2 or len( + game.game_flow["custom_string"]) == 0: + game.game_flow["custom_string"] = "2" + except: + game.game_flow["custom_string"] = "2" + menu_objects, setup = update_text(menu_objects, setup, game, palette) + + if event.type == KEYDOWN: + if event.key == K_BACKSPACE: + try: + game.game_flow["custom_string"] = game.game_flow["custom_string"][:-1] + + menu_objects, setup = update_text(menu_objects, setup, game, palette) + except: + pass + elif event.key == K_RETURN: + mouse.update(Win_size, Default_size) + game.game_flow["ac"] = True + for widget in menu_objects: + widget.hover_check(mouse.mouse_pos) + soundsX["click"].play() + + # checking if num big enough + try: + if int(game.game_flow["custom_string"]) < 2 or len(game.game_flow["custom_string"]) == 0: + game.game_flow["custom_string"] = "2" + except: + game.game_flow["custom_string"] = "2" + menu_objects, setup = update_text(menu_objects, setup, game, palette) + else: + if len(game.game_flow["custom_string"]) < 2: + try: + int(event.unicode) + game.game_flow["custom_string"] += event.unicode + + menu_objects, setup = update_text(menu_objects, setup, game, palette) + except: + pass + + # checking fs change + if game.game_flow["change_fs"]: + game.game_flow["change_fs"] = False + + for obj in menu_objects: + if obj.name in ["fs", "ms"]: + obj.active = not obj.active + + game.fs = not game.fs + if game.fs is False: + Win_size = Default_size + screenX = pygame.display.set_mode(Win_size) + game.game_flow["dp"] = [0, 0] + mouse.mouse_scroll = game.game_flow["dp"] + + game.game_flow["Win_size"] = Win_size + game.game_flow["screen"] = screenX + else: + screenX = pygame.display.set_mode(monitor_size, pygame.FULLSCREEN) + d = screenX + ratio = [Default_size[1] / Default_size[0], Default_size[0] / Default_size[1]] + # u chose width or height here + + if Default_size[0] > Default_size[1]: + Win_size = [d.get_width(), int(d.get_width() * ratio[0])] + d = d.get_height() + dd = Win_size[1] + game.game_flow["dp"][1] = (d - dd) / 2 + mouse.mouse_scroll = game.game_flow["dp"] + else: + Win_size = [int(d.get_height() * ratio[1]), d.get_height()] + d = pygame.display.get_surface().get_width() + dd = Win_size[0] + game.game_flow["dp"][0] = (d - dd) / 2 + mouse.mouse_scroll = game.game_flow["dp"] + + game.game_flow["Win_size"] = Win_size + game.game_flow["screen"] = screenX + + # basic loop config + + screenX.blit(pygame.transform.scale(display, Win_size), game.game_flow["dp"]) + pygame.display.update() + clock.tick(60) + + +# new_game_btn +def new_game(args): + game = args[0] + palette = args[1] + mouse = args[2] + setup = game.game_flow["setup"] + screenX = game.game_flow["screen"] + setup[setup["difficulty"]] = True + + if setup["difficulty"] == "easy": + hashi = hg.Hashi(5) + elif setup["difficulty"] == "medium": + hashi = hg.Hashi(10) + elif setup["difficulty"] == "hard": + hashi = hg.Hashi(15) + elif setup["difficulty"] == "extreme": + hashi = hg.Hashi(20) + elif setup["difficulty"] == "custom": + hashi = hg.Hashi(int(setup["custom_num"])) + else: + sys.exit(55) + + hashi.generate_map() + hashi.save(f"assets/maps/{setup['difficulty']}.txt") + soundsX["click"].play() + + run_game(screenX, setup, game, palette, mouse, "new") + game.game_flow["cut_click"] = True + + +# play_btn +def play(args): + game = args[0] + palette = args[1] + mouse = args[2] + setup = game.game_flow["setup"] + screenX = game.game_flow["screen"] + + if setup[setup["difficulty"]] is False: + new_game(args) + else: + soundsX["click"].play() + run_game(screenX, setup, game, palette, mouse, "continue") + game.game_flow["cut_click"] = True + + +# at this point the whole menu is done and the game is ready to be run so here we go +def run_game(screenX, setup, gameMenu, palette, mouse, state): + game = Game() + game.game_flow["dp"] = gameMenu.game_flow["dp"] + game.fs = gameMenu.fs + hashi = None + Win_size = gameMenu.game_flow["Win_size"] + game.game_flow["scroll_length"] = (Win_size[0] - 60) / 2 + game.game_flow["quit"] = False + + scroll = Scroll([-20, -20]) + + mid_point_scroll_mouse = [game.game_flow["dp"][0] + (Win_size[0] / 2), + game.game_flow["dp"][1] + (Win_size[1] / 2)] + + back_btn = Button([200, 350], [200, 100], get_back_images(palette), go_back, [game]) + win_image = get_win_image() + + # initing rectangle effect + rE = Rectangle_effect + rE.generate_rects() + + # initializing game by state + if state == "new": + hashi = hashi_game.HashiG("file.txt", f"assets/maps/{setup['difficulty']}.txt", palette, mouse) + elif state == "continue": + hashi = hashi_game.HashiG("file.json", f"assets/saves/game_saves/{setup['difficulty']}.json", palette, mouse) + + hashi_top_lim = [(hashi.get_length() + 40) - 600, (hashi.get_length() + 40) - 650] + + # pre scrolling + scroll.move_scroll_based_on_pos(mouse.get_scrolled(scroll), [600, 650], "both", 40) + + scroll.scroll_lim(scroll, [-40, -40], hashi_top_lim) + + while game.alive: + # dealing with hashi locked + if hashi.locked: + hashi.on_locked(mouse, scroll) + + # background + display.fill(palette.palette["background"]) + + # dealing with bg effect + for item in rE.rects: + item.move(display, palette) + + if rE.timer == 0: + rE.generate_rects(5, [1, 3]) + rE.timer = 10 + else: + rE.timer -= 1 + + # dealing with scroll + # not using mouse.mouse_pos because screen size affects + if distance_indicator(pygame.mouse.get_pos(), mid_point_scroll_mouse) > game.game_flow["scroll_length"]: + scroll.move_scroll_based_on_pos(mouse.get_scrolled(scroll), [600, 650], "both", 40) + + scroll.scroll_lim(scroll, [-40, -40], hashi_top_lim) + + # game + + hashi.blit(display, palette, scroll) + + # win screen + + if hashi.win: + display.blit(win_image, [50, 50]) + + back_btn.blit(display) + + setup[setup["difficulty"]] = False + + # event loop + + for event in pygame.event.get(): + if event.type == QUIT: + # saving before quiting + with open("assets/saves/setup.json", "w") as f: + json.dump(setup, f, indent=4) + + if hashi.win is False: + hashi.save(setup) + + pygame.quit() + sys.exit(0) + + # keydown + if event.type == KEYDOWN: + if hashi.locked is False: + if event.key == K_ESCAPE: + gameMenu.fs = game.fs + gameMenu.game_flow["dp"] = game.game_flow["dp"] + gameMenu.game_flow["back_from_game"] = True + gameMenu.game_flow["Win_size"] = Win_size + game.alive = False + + with open("assets/saves/setup.json", "w") as f: + json.dump(setup, f, indent=4) + + if hashi.win is False: + hashi.save(setup) + else: + if event.key == K_ESCAPE: + hashi.locked = False + hashi.clear_temp() + hashi.clear_temp_remove() + + mouse.update(Win_size, Default_size) + hashi.hover(mouse, scroll) + + if event.key == K_f: + game.fs = not game.fs + if game.fs is False: + Win_size = Default_size + screenX = pygame.display.set_mode(Win_size) + game.game_flow["dp"] = [0, 0] + mouse.mouse_scroll = game.game_flow["dp"] + # specifics + mid_point_scroll_mouse = [game.game_flow["dp"][0] + (Win_size[0] / 2), + game.game_flow["dp"][1] + (Win_size[1] / 2)] + game.game_flow["scroll_length"] = (Win_size[0] - 100) / 2 + + else: + screenX = pygame.display.set_mode(monitor_size, pygame.FULLSCREEN) + d = screenX + ratio = [Default_size[1] / Default_size[0], Default_size[0] / Default_size[1]] + # u chose width or height here + + if Default_size[0] > Default_size[1]: + Win_size = [d.get_width(), int(d.get_width() * ratio[0])] + d = d.get_height() + dd = Win_size[1] + game.game_flow["dp"][1] = (d - dd) / 2 + else: + Win_size = [int(d.get_height() * ratio[1]), d.get_height()] + d = pygame.display.get_surface().get_width() + dd = Win_size[0] + game.game_flow["dp"][0] = (d - dd) / 2 + + mouse.mouse_scroll = game.game_flow["dp"] + # specifics + mid_point_scroll_mouse = [game.game_flow["dp"][0] + (Win_size[0] / 2), + game.game_flow["dp"][1] + (Win_size[1] / 2)] + game.game_flow["scroll_length"] = (Win_size[0] - 60) / 2 + + if hashi.win is False: + if hashi.locked is False: + # mouse stuff + if event.type == MOUSEMOTION: + mouse.update(Win_size, Default_size) + hashi.hover(mouse, scroll) + + # click + elif event.type == MOUSEBUTTONDOWN: + hashi.click(mouse, scroll) + if hashi.locked: + soundsX["click"].play() + else: + if event.type == MOUSEMOTION: + mouse.update(Win_size, Default_size) + + elif event.type == MOUSEBUTTONDOWN: + hashi.locked = False + hashi.click_on_locked(mouse, scroll) + hashi.check_win() + + mouse.update(Win_size, Default_size) + hashi.hover(mouse, scroll) + else: + if event.type == MOUSEMOTION: + mouse.update(Win_size, Default_size) + back_btn.hover_check(mouse.mouse_pos) + + elif event.type == MOUSEBUTTONDOWN: + back_btn.click_check(mouse.mouse_pos) + + # quiting on back button (same as esc) + + if game.game_flow["quit"]: + gameMenu.fs = game.fs + gameMenu.game_flow["dp"] = game.game_flow["dp"] + gameMenu.game_flow["back_from_game"] = True + gameMenu.game_flow["Win_size"] = Win_size + game.alive = False + + with open("assets/saves/setup.json", "w") as f: + json.dump(setup, f, indent=4) + + # basic loop config + + screenX.blit(pygame.transform.scale(display, Win_size), game.game_flow["dp"]) + pygame.display.update() + clock.tick(60) + + +# tutorial +def run_tutorial(screenX, setup, gameMenu, palette, mouse): + game = Game() + game.game_flow["dp"] = gameMenu.game_flow["dp"] + game.fs = gameMenu.fs + Win_size = gameMenu.game_flow["Win_size"] + game.game_flow["scroll_length"] = (Win_size[0] - 60) / 2 + game.game_flow["quit"] = False + game.game_flow["tf"] = False # tutorial forward + game.game_flow["t_step"] = 0 + + scroll = Scroll([-20, -20]) + + mid_point_scroll_mouse = [game.game_flow["dp"][0] + (Win_size[0] / 2), + game.game_flow["dp"][1] + (Win_size[1] / 2)] + + back_btn = Button([200, 350], [200, 100], get_back_images(palette), go_back, [game]) + win_image = get_win_image() + + # initing rectangle effect + rE = Rectangle_effect + rE.generate_rects() + + # initializing tutorial + hashi = hashi_game.HashiG("file.json", f"assets/tutorial/tutorial.json", palette, mouse) + + hashi_top_lim = [(hashi.get_length() + 40) - 600, (hashi.get_length() + 40) - 650] + + # preparing tutorial circle + tutorial_circle_images, size = get_tutorial_circle_images(math.sqrt(hashi.indent/hashi.indent_default)**1.5) + tutorial_circle = CircleButton(size, [120 - (size/2), 120 - (size/2)], + tutorial_circle_images, move_in_tutorial, [game]) + + # pre scrolling + scroll.move_scroll_based_on_pos(mouse.get_scrolled(scroll), [600, 650], "both", 40) + + scroll.scroll_lim(scroll, [-40, -40], hashi_top_lim) + + while game.alive: + # dealing with tutorial + if game.game_flow['tf']: + game.game_flow["tf"] = False + + if game.game_flow["t_step"] == 0: + game.game_flow["t_step"] += 1 + hashi.click(mouse, scroll) + tutorial_circle.move([0, 240]) + tutorial_circle.hover_check(mouse.get_scrolled(scroll)) + soundsX["click"].play() + + elif game.game_flow["t_step"] == 1: + game.game_flow["t_step"] += 1 + hashi.locked = False + hashi.click_on_locked(mouse, scroll) + hashi.check_win() + + mouse.update(Win_size, Default_size) + hashi.hover(mouse, scroll) + + tutorial_circle.move([240, -240]) + tutorial_circle.hover_check(mouse.get_scrolled(scroll)) + soundsX["join"].play() + + elif game.game_flow["t_step"] == 2: + game.game_flow["t_step"] += 1 + hashi.click(mouse, scroll) + tutorial_circle.move([0, -60]) + tutorial_circle.hover_check(mouse.get_scrolled(scroll)) + soundsX["click"].play() + + elif game.game_flow["t_step"] == 3: + game.game_flow["t_step"] += 1 + hashi.locked = False + hashi.click_on_locked(mouse, scroll) + hashi.check_win() + + mouse.update(Win_size, Default_size) + hashi.hover(mouse, scroll) + + tutorial_circle.move([0, -60]) + tutorial_circle.hover_check(mouse.get_scrolled(scroll)) + soundsX["join"].play() + + elif game.game_flow["t_step"] == 4: + game.game_flow["t_step"] += 1 + hashi.click(mouse, scroll) + tutorial_circle.move([-240, 0]) + tutorial_circle.hover_check(mouse.get_scrolled(scroll)) + soundsX["click"].play() + + elif game.game_flow["t_step"] == 5: + hashi.locked = False + hashi.click_on_locked(mouse, scroll) + hashi.check_win() + + mouse.update(Win_size, Default_size) + hashi.hover(mouse, scroll) + soundsX["join"].play() + + tutorial_circle.active = False + + # dealing with hashi locked + if hashi.locked: + hashi.on_locked(mouse, scroll) + + # background + display.fill(palette.palette["background"]) + + # dealing with bg effect + for item in rE.rects: + item.move(display, palette) + + if rE.timer == 0: + rE.generate_rects(5, [1, 3]) + rE.timer = 10 + else: + rE.timer -= 1 + + # dealing with scroll + # not using mouse.mouse_pos because screen size affects + if distance_indicator(pygame.mouse.get_pos(), mid_point_scroll_mouse) > game.game_flow["scroll_length"]: + scroll.move_scroll_based_on_pos(mouse.get_scrolled(scroll), [600, 650], "both", 40) + + scroll.scroll_lim(scroll, [-40, -40], hashi_top_lim) + + # game + + hashi.blit(display, palette, scroll) + + # tutorial + + tutorial_circle.blit(display, scroll) + + # win screen + + if hashi.win: + display.blit(win_image, [50, 50]) + + back_btn.blit(display) + + setup["tutorial"] = False + + # event loop + + for event in pygame.event.get(): + if event.type == QUIT: + pygame.quit() + sys.exit(0) + + # keydown + if event.type == KEYDOWN: + if event.key == K_ESCAPE: + gameMenu.fs = game.fs + gameMenu.game_flow["dp"] = game.game_flow["dp"] + gameMenu.game_flow["back_from_game"] = True + gameMenu.game_flow["Win_size"] = Win_size + game.alive = False + + with open("assets/saves/setup.json", "w") as f: + json.dump(setup, f, indent=4) + + if event.key == K_f: + game.fs = not game.fs + if game.fs is False: + Win_size = Default_size + screenX = pygame.display.set_mode(Win_size) + game.game_flow["dp"] = [0, 0] + mouse.mouse_scroll = game.game_flow["dp"] + # specifics + mid_point_scroll_mouse = [game.game_flow["dp"][0] + (Win_size[0] / 2), + game.game_flow["dp"][1] + (Win_size[1] / 2)] + game.game_flow["scroll_length"] = (Win_size[0] - 100) / 2 + + else: + screenX = pygame.display.set_mode(monitor_size, pygame.FULLSCREEN) + d = screenX + ratio = [Default_size[1] / Default_size[0], Default_size[0] / Default_size[1]] + # u chose width or height here + + if Default_size[0] > Default_size[1]: + Win_size = [d.get_width(), int(d.get_width() * ratio[0])] + d = d.get_height() + dd = Win_size[1] + game.game_flow["dp"][1] = (d - dd) / 2 + else: + Win_size = [int(d.get_height() * ratio[1]), d.get_height()] + d = pygame.display.get_surface().get_width() + dd = Win_size[0] + game.game_flow["dp"][0] = (d - dd) / 2 + + mouse.mouse_scroll = game.game_flow["dp"] + # specifics + mid_point_scroll_mouse = [game.game_flow["dp"][0] + (Win_size[0] / 2), + game.game_flow["dp"][1] + (Win_size[1] / 2)] + game.game_flow["scroll_length"] = (Win_size[0] - 60) / 2 + + if hashi.win is False: + if hashi.locked is False: + # mouse stuff + if event.type == MOUSEMOTION: + mouse.update(Win_size, Default_size) + hashi.hover(mouse, scroll) + tutorial_circle.hover_check(mouse.get_scrolled(scroll)) + + # click + elif event.type == MOUSEBUTTONDOWN: + tutorial_circle.click_check(mouse.get_scrolled(scroll)) + else: + if event.type == MOUSEMOTION: + mouse.update(Win_size, Default_size) + tutorial_circle.hover_check(mouse.get_scrolled(scroll)) + + elif event.type == MOUSEBUTTONDOWN: + tutorial_circle.click_check(mouse.get_scrolled(scroll)) + else: + if event.type == MOUSEMOTION: + mouse.update(Win_size, Default_size) + back_btn.hover_check(mouse.mouse_pos) + + elif event.type == MOUSEBUTTONDOWN: + back_btn.click_check(mouse.mouse_pos) + + # quiting on back button (same as esc) + + if game.game_flow["quit"]: + gameMenu.fs = game.fs + gameMenu.game_flow["dp"] = game.game_flow["dp"] + gameMenu.game_flow["back_from_game"] = True + gameMenu.game_flow["Win_size"] = Win_size + game.alive = False + + with open("assets/saves/setup.json", "w") as f: + json.dump(setup, f, indent=4) + + # basic loop config + + screenX.blit(pygame.transform.scale(display, Win_size), game.game_flow["dp"]) + pygame.display.update() + clock.tick(60) + + +main_menu(screen, Window_size) diff --git a/assets/images/game/back.png b/assets/images/game/back.png new file mode 100644 index 0000000..1952d7c Binary files /dev/null and b/assets/images/game/back.png differ diff --git a/assets/images/game/back_hover.png b/assets/images/game/back_hover.png new file mode 100644 index 0000000..b3db565 Binary files /dev/null and b/assets/images/game/back_hover.png differ diff --git a/assets/images/game/num_circle.png b/assets/images/game/num_circle.png new file mode 100644 index 0000000..a4f3a42 Binary files /dev/null and b/assets/images/game/num_circle.png differ diff --git a/assets/images/game/num_circle_hover.png b/assets/images/game/num_circle_hover.png new file mode 100644 index 0000000..3d653cd Binary files /dev/null and b/assets/images/game/num_circle_hover.png differ diff --git a/assets/images/game/tutorial_circle.png b/assets/images/game/tutorial_circle.png new file mode 100644 index 0000000..0b3c5d0 Binary files /dev/null and b/assets/images/game/tutorial_circle.png differ diff --git a/assets/images/game/tutorial_circle_hover.png b/assets/images/game/tutorial_circle_hover.png new file mode 100644 index 0000000..18efe23 Binary files /dev/null and b/assets/images/game/tutorial_circle_hover.png differ diff --git a/assets/images/general/hashi_logo.ico b/assets/images/general/hashi_logo.ico new file mode 100644 index 0000000..4db3b41 Binary files /dev/null and b/assets/images/general/hashi_logo.ico differ diff --git a/assets/images/general/hashi_logo.png b/assets/images/general/hashi_logo.png new file mode 100644 index 0000000..469eda7 Binary files /dev/null and b/assets/images/general/hashi_logo.png differ diff --git a/assets/images/menu/arrow.png b/assets/images/menu/arrow.png new file mode 100644 index 0000000..81ae3e7 Binary files /dev/null and b/assets/images/menu/arrow.png differ diff --git a/assets/images/menu/arrow_hover.png b/assets/images/menu/arrow_hover.png new file mode 100644 index 0000000..57bc5ca Binary files /dev/null and b/assets/images/menu/arrow_hover.png differ diff --git a/assets/images/menu/continue.png b/assets/images/menu/continue.png new file mode 100644 index 0000000..277f710 Binary files /dev/null and b/assets/images/menu/continue.png differ diff --git a/assets/images/menu/continue_hover.png b/assets/images/menu/continue_hover.png new file mode 100644 index 0000000..8e56712 Binary files /dev/null and b/assets/images/menu/continue_hover.png differ diff --git a/assets/images/menu/custom.png b/assets/images/menu/custom.png new file mode 100644 index 0000000..854ea6c Binary files /dev/null and b/assets/images/menu/custom.png differ diff --git a/assets/images/menu/custom_pressed.png b/assets/images/menu/custom_pressed.png new file mode 100644 index 0000000..c333994 Binary files /dev/null and b/assets/images/menu/custom_pressed.png differ diff --git a/assets/images/menu/fs.png b/assets/images/menu/fs.png new file mode 100644 index 0000000..451453f Binary files /dev/null and b/assets/images/menu/fs.png differ diff --git a/assets/images/menu/fs_hover.png b/assets/images/menu/fs_hover.png new file mode 100644 index 0000000..e481a37 Binary files /dev/null and b/assets/images/menu/fs_hover.png differ diff --git a/assets/images/menu/ms.png b/assets/images/menu/ms.png new file mode 100644 index 0000000..2aac8b1 Binary files /dev/null and b/assets/images/menu/ms.png differ diff --git a/assets/images/menu/ms_hover.png b/assets/images/menu/ms_hover.png new file mode 100644 index 0000000..af76062 Binary files /dev/null and b/assets/images/menu/ms_hover.png differ diff --git a/assets/images/menu/new_game.png b/assets/images/menu/new_game.png new file mode 100644 index 0000000..ec72027 Binary files /dev/null and b/assets/images/menu/new_game.png differ diff --git a/assets/images/menu/new_game_hover.png b/assets/images/menu/new_game_hover.png new file mode 100644 index 0000000..a17b483 Binary files /dev/null and b/assets/images/menu/new_game_hover.png differ diff --git a/assets/images/menu/palette.png b/assets/images/menu/palette.png new file mode 100644 index 0000000..479f338 Binary files /dev/null and b/assets/images/menu/palette.png differ diff --git a/assets/images/menu/palette_hover.png b/assets/images/menu/palette_hover.png new file mode 100644 index 0000000..99ef44f Binary files /dev/null and b/assets/images/menu/palette_hover.png differ diff --git a/assets/images/menu/play.png b/assets/images/menu/play.png new file mode 100644 index 0000000..23dd436 Binary files /dev/null and b/assets/images/menu/play.png differ diff --git a/assets/images/menu/play_hover.png b/assets/images/menu/play_hover.png new file mode 100644 index 0000000..2e65dfe Binary files /dev/null and b/assets/images/menu/play_hover.png differ diff --git a/assets/images/menu/template.png b/assets/images/menu/template.png new file mode 100644 index 0000000..43b46fe Binary files /dev/null and b/assets/images/menu/template.png differ diff --git a/assets/images/menu/template_hover.png b/assets/images/menu/template_hover.png new file mode 100644 index 0000000..20314ab Binary files /dev/null and b/assets/images/menu/template_hover.png differ diff --git a/assets/images/menu/writing.png b/assets/images/menu/writing.png new file mode 100644 index 0000000..b199bac Binary files /dev/null and b/assets/images/menu/writing.png differ diff --git a/assets/maps/custom.txt b/assets/maps/custom.txt new file mode 100644 index 0000000..e69de29 diff --git a/assets/maps/easy.txt b/assets/maps/easy.txt new file mode 100644 index 0000000..e69de29 diff --git a/assets/maps/extreme.txt b/assets/maps/extreme.txt new file mode 100644 index 0000000..e69de29 diff --git a/assets/maps/hard.txt b/assets/maps/hard.txt new file mode 100644 index 0000000..e69de29 diff --git a/assets/maps/medium.txt b/assets/maps/medium.txt new file mode 100644 index 0000000..e69de29 diff --git a/assets/maps/tutorial.txt b/assets/maps/tutorial.txt new file mode 100644 index 0000000..d4adda7 --- /dev/null +++ b/assets/maps/tutorial.txt @@ -0,0 +1,5 @@ +0 1 0 0 0 +0 0 0 1 0 +0 0 0 0 0 +0 3 0 5 1 +2 0 0 3 0 diff --git a/assets/palettes/__pycache__/palette_manager.cpython-39.pyc b/assets/palettes/__pycache__/palette_manager.cpython-39.pyc new file mode 100644 index 0000000..42a7bd6 Binary files /dev/null and b/assets/palettes/__pycache__/palette_manager.cpython-39.pyc differ diff --git a/assets/palettes/aqua.json b/assets/palettes/aqua.json new file mode 100644 index 0000000..de792e0 --- /dev/null +++ b/assets/palettes/aqua.json @@ -0,0 +1,6 @@ +{"background": "#59bdf7", + "backgroundShade": "#81ccf7", + "outline": "#268cc7", + "outline-shade": "#2ba0e3", + "addition": "#1575ad", + "outline-dark": "#09517a"} \ No newline at end of file diff --git a/assets/palettes/emerald.json b/assets/palettes/emerald.json new file mode 100644 index 0000000..f24ba84 --- /dev/null +++ b/assets/palettes/emerald.json @@ -0,0 +1,6 @@ +{"background": "#75ebc2", + "backgroundShade": "#4debb4", + "outline": "#9baba5", + "outline-shade": "#81a195", + "addition": "#07db91", + "outline-dark": "#60716a"} \ No newline at end of file diff --git a/assets/palettes/lavender.json b/assets/palettes/lavender.json new file mode 100644 index 0000000..53c616c --- /dev/null +++ b/assets/palettes/lavender.json @@ -0,0 +1,6 @@ +{"background": "#8818c9", + "backgroundShade": "#8425ba", + "outline": "#dd26e0", + "outline-shade": "#7e38a6", + "addition": "#a716fa", + "outline-dark": "#5b1485"} \ No newline at end of file diff --git a/assets/palettes/palette_manager.py b/assets/palettes/palette_manager.py new file mode 100644 index 0000000..f0797a9 --- /dev/null +++ b/assets/palettes/palette_manager.py @@ -0,0 +1,71 @@ +import os +import json +import pygame +from itertools import cycle +pygame.init() + + +class Palettes: + def __init__(self, path): + self.path = path + self.palettes = {} + self.current_palette = "emerald" # set starting palette here + self.load_palettes() + self.palette = self.get_palette() + self.cycle = None + self.changed = False + + def load_palettes(self): + current_dir = os.getcwd() + os.chdir(self.path) + files = os.listdir() + files.remove('palette_manager.py') + try: + files.remove("__pycache__") + except: + pass + + for file in files: + with open(file, "r") as f: + fileX = json.load(f) + + for color in fileX.keys(): + fileX[color] = self.rgb(fileX[color]) + + file = "".join(list(file)[:-5]) + + self.palettes[file] = fileX + + os.chdir(current_dir) + + def get_palette(self): + return self.palettes[self.current_palette] + + def swap_image(self, image, old_p, new_p): + for color in self.palettes[old_p].keys(): + image = swap_color(image, self.palettes[old_p][color], self.palettes[new_p][color]) + + return image + + def create_cycle(self): + names = self.palettes.keys() + self.cycle = cycle(names) + while next(self.cycle) != self.current_palette: + pass + + @staticmethod + def rgb(color): + color = color.strip("#") + color = [int(color[i:i+2], 16) for i in range(0, 5, 2)] + return color + + +def swap_color(imageX, old, new): + image_copy = pygame.Surface(imageX.copy().get_size()) + image_copy.fill(new) + + imageX.set_colorkey(old) + + image_copy.blit(imageX, [0, 0]) + + return image_copy diff --git a/assets/palettes/reddish.json b/assets/palettes/reddish.json new file mode 100644 index 0000000..60d6d49 --- /dev/null +++ b/assets/palettes/reddish.json @@ -0,0 +1,6 @@ +{"background": "#c9c5c5", + "backgroundShade": "#a3a0a0", + "outline": "#9e2929", + "outline-shade": "#8f6565", + "addition": "#a83939", + "outline-dark": "#610e0e"} \ No newline at end of file diff --git a/assets/palettes/sakura.json b/assets/palettes/sakura.json new file mode 100644 index 0000000..3af351b --- /dev/null +++ b/assets/palettes/sakura.json @@ -0,0 +1,6 @@ +{"background": "#ff87df", + "backgroundShade": "#f7b2e5", + "outline": "#d94db4", + "outline-shade": "#fe75db", + "addition": "#eb3bbc", + "outline-dark": "#97316b"} \ No newline at end of file diff --git a/assets/palettes/vanilla.json b/assets/palettes/vanilla.json new file mode 100644 index 0000000..9777b04 --- /dev/null +++ b/assets/palettes/vanilla.json @@ -0,0 +1,6 @@ +{"background": "#faf9de", + "backgroundShade": "#f2f2df", + "outline": "#c7c689", + "outline-shade": "#ccca5e", + "addition": "#fffd6e", + "outline-dark": "#474731"} \ No newline at end of file diff --git a/assets/saves/default.json b/assets/saves/default.json new file mode 100644 index 0000000..18014dc --- /dev/null +++ b/assets/saves/default.json @@ -0,0 +1,11 @@ +{ + "difficulty": "easy", + "palette": "emerald", + "tutorial": true, + "easy": false, + "medium": false, + "hard": false, + "extreme": false, + "custom": false, + "custom_num": "22" +} \ No newline at end of file diff --git a/assets/saves/game_saves/custom.json b/assets/saves/game_saves/custom.json new file mode 100644 index 0000000..9e26dfe --- /dev/null +++ b/assets/saves/game_saves/custom.json @@ -0,0 +1 @@ +{} \ No newline at end of file diff --git a/assets/saves/game_saves/easy.json b/assets/saves/game_saves/easy.json new file mode 100644 index 0000000..9e26dfe --- /dev/null +++ b/assets/saves/game_saves/easy.json @@ -0,0 +1 @@ +{} \ No newline at end of file diff --git a/assets/saves/game_saves/extreme.json b/assets/saves/game_saves/extreme.json new file mode 100644 index 0000000..9e26dfe --- /dev/null +++ b/assets/saves/game_saves/extreme.json @@ -0,0 +1 @@ +{} \ No newline at end of file diff --git a/assets/saves/game_saves/hard.json b/assets/saves/game_saves/hard.json new file mode 100644 index 0000000..9e26dfe --- /dev/null +++ b/assets/saves/game_saves/hard.json @@ -0,0 +1 @@ +{} \ No newline at end of file diff --git a/assets/saves/game_saves/medium.json b/assets/saves/game_saves/medium.json new file mode 100644 index 0000000..9e26dfe --- /dev/null +++ b/assets/saves/game_saves/medium.json @@ -0,0 +1 @@ +{} \ No newline at end of file diff --git a/assets/saves/setup.json b/assets/saves/setup.json new file mode 100644 index 0000000..18014dc --- /dev/null +++ b/assets/saves/setup.json @@ -0,0 +1,11 @@ +{ + "difficulty": "easy", + "palette": "emerald", + "tutorial": true, + "easy": false, + "medium": false, + "hard": false, + "extreme": false, + "custom": false, + "custom_num": "22" +} \ No newline at end of file diff --git a/assets/sounds/click.wav b/assets/sounds/click.wav new file mode 100644 index 0000000..5cd6c98 Binary files /dev/null and b/assets/sounds/click.wav differ diff --git a/assets/sounds/join.wav b/assets/sounds/join.wav new file mode 100644 index 0000000..a59537f Binary files /dev/null and b/assets/sounds/join.wav differ diff --git a/assets/sounds/remove.wav b/assets/sounds/remove.wav new file mode 100644 index 0000000..7676557 Binary files /dev/null and b/assets/sounds/remove.wav differ diff --git a/assets/tutorial/tutorial.json b/assets/tutorial/tutorial.json new file mode 100644 index 0000000..4eeff4f --- /dev/null +++ b/assets/tutorial/tutorial.json @@ -0,0 +1,239 @@ +{ + "difficulty": "tutorial", + "connections": [ + { + "pos": [ + 0, + 0 + ], + "bridges": [ + { + "bridge_num": 0, + "connection": null + }, + { + "bridge_num": 0, + "connection": null + }, + { + "bridge_num": 1, + "connection": [ + 3, + 0 + ] + }, + { + "bridge_num": 0, + "connection": null + } + ], + "bridge_occupied": 1, + "locked": true + }, + { + "pos": [ + 1, + 0 + ], + "locked": true + }, + { + "pos": [ + 1, + 1 + ], + "bridges": [ + { + "bridge_num": 0, + "connection": null + }, + { + "bridge_num": 0, + "connection": null + }, + { + "bridge_num": 2, + "connection": [ + 3, + 1 + ] + }, + { + "bridge_num": 0, + "connection": null + } + ], + "bridge_occupied": 2, + "locked": true + }, + { + "pos": [ + 1, + 4 + ], + "bridges": [ + { + "bridge_num": 0, + "connection": null + }, + { + "bridge_num": 0, + "connection": null + }, + { + "bridge_num": 0, + "connection": null + }, + { + "bridge_num": 0, + "connection": null + } + ], + "bridge_occupied": 0, + "locked": true + }, + { + "pos": [ + 2, + 0 + ], + "locked": true + }, + { + "pos": [ + 2, + 1 + ], + "locked": true + }, + { + "pos": [ + 3, + 0 + ], + "bridges": [ + { + "bridge_num": 0, + "connection": null + }, + { + "bridge_num": 0, + "connection": null + }, + { + "bridge_num": 0, + "connection": null + }, + { + "bridge_num": 1, + "connection": [ + 0, + 0 + ] + } + ], + "bridge_occupied": 1, + "locked": true + }, + { + "pos": [ + 3, + 1 + ], + "bridges": [ + { + "bridge_num": 0, + "connection": null + }, + { + "bridge_num": 1, + "connection": [ + 3, + 3 + ] + }, + { + "bridge_num": 1, + "connection": [ + 4, + 1 + ] + }, + { + "bridge_num": 2, + "connection": [ + 1, + 1 + ] + } + ], + "bridge_occupied": 4, + "locked": true + }, + { + "pos": [ + 3, + 2 + ], + "locked": true + }, + { + "pos": [ + 3, + 3 + ], + "bridges": [ + { + "bridge_num": 1, + "connection": [ + 3, + 1 + ] + }, + { + "bridge_num": 0, + "connection": null + }, + { + "bridge_num": 0, + "connection": null + }, + { + "bridge_num": 0, + "connection": null + } + ], + "bridge_occupied": 1, + "locked": true + }, + { + "pos": [ + 4, + 1 + ], + "bridges": [ + { + "bridge_num": 0, + "connection": null + }, + { + "bridge_num": 0, + "connection": null + }, + { + "bridge_num": 0, + "connection": null + }, + { + "bridge_num": 1, + "connection": [ + 3, + 1 + ] + } + ], + "bridge_occupied": 1, + "locked": true + } + ] +} \ No newline at end of file diff --git a/game_effect.py b/game_effect.py new file mode 100644 index 0000000..fe511b6 --- /dev/null +++ b/game_effect.py @@ -0,0 +1,51 @@ +import pygame +import random + +import pygame +import random + + +class Rectangle_effect: + rects = [] + timer = 0 + + def __init__(self, pos, size, speed, dirs): + self.pos = pos + self.size = size + self.speed = speed + self.dirs = dirs # True for going up False for down + + def move(self, display, palette): + sur = pygame.Surface(self.size) + sur.fill(palette.palette["backgroundShade"]) + sur.set_alpha(random.randint(100, 200)) + display.blit(sur, self.pos) + + if self.dirs[0]: + self.pos[1] -= self.speed + elif self.dirs[1]: + self.pos[1] += self.speed + elif self.dirs[2]: + self.pos[0] += self.speed + elif self.dirs[3]: + self.pos[0] -= self.speed + + if self.pos[1] < -120 or self.pos[0] > 600 or self.pos[1] > 650 or self.pos[0] < -120: + self.delete(self) + + @classmethod + def generate_rects(cls, amount=100, speed_range=None): + if speed_range is None: + speed_range = [1, 4] + + for _ in range(amount): + dirs = [0, 0, 0, 0] + dirs[random.randint(0, 3)] = 1 + cls.rects.append(Rectangle_effect([random.randint(0, 600), random.randint(0, 650)], + [random.randint(40, 120), random.randint(40, 120)], + random.randint(speed_range[0], speed_range[1]), + dirs)) + + @classmethod + def delete(cls, circle): + cls.rects.remove(circle) diff --git a/garbage.py b/garbage.py new file mode 100644 index 0000000..a4cfca7 --- /dev/null +++ b/garbage.py @@ -0,0 +1,9 @@ +def get_result(size): + size += 1 # increasing to get away from 0 + x = [1 for _ in range(size)] + for i in range(size - 1): # getting original size + x = [sum(x[:i+1]) for i in range(size)] + print(x[-1]) + + +get_result(20) diff --git a/hashi_functions.py b/hashi_functions.py new file mode 100644 index 0000000..c71a986 --- /dev/null +++ b/hashi_functions.py @@ -0,0 +1,229 @@ +import json +import pygame +import sounds + +# basic config +pygame.mixer.pre_init(48000, -16, 2, 512) +pygame.init() +pygame.mixer.set_num_channels(16) + +font = pygame.font.SysFont('calibri', 60, True) +small_font = pygame.font.SysFont('calibri', 40, True) +tiny_font = pygame.font.SysFont('calibri', 20, True) + +soundsX = sounds.get_sounds() + + +def set_palette(setup, palette, name_of_new_palette, menu_objects, first=False): + if first: + old_palette = "emerald" + else: + old_palette = setup["palette"] + palette.current_palette = name_of_new_palette + palette.palette = palette.get_palette() + + # saving new json + setup["palette"] = name_of_new_palette + with open("assets/saves/setup.json", "w") as f: + json.dump(setup, f, indent=4) + + # iterates over images and sets new palette + for obj in range(len(menu_objects)): + for img in menu_objects[obj].images.keys(): + menu_objects[obj].images[img] = palette.swap_image(menu_objects[obj].images[img], + old_palette, name_of_new_palette) + menu_objects[obj].images[img].set_colorkey((0, 0, 0)) + menu_objects[obj].display_image = palette.swap_image(menu_objects[obj].display_image, + old_palette, name_of_new_palette) + menu_objects[obj].display_image.set_colorkey((0, 0, 0)) + + return menu_objects, setup + + +# function for palette btn +def next_palette(palette): + palette = palette[0] + palette.current_palette = next(palette.cycle) + palette.changed = True + soundsX["click"].play() + + +# function for right arrow +def right_arrow(game): + game = game[0] + index = game.game_flow["dfs"].index(game.game_flow["df"]) + if index != len(game.game_flow["dfs"]) - 1: + game.game_flow["df"] = game.game_flow["dfs"][index + 1] + game.game_flow["df_change"] = True + soundsX["click"].play() + + +# function for left arrow +def left_arrow(game): + game = game[0] + index = game.game_flow["dfs"].index(game.game_flow["df"]) + if index != 0: + game.game_flow["df"] = game.game_flow["dfs"][index - 1] + game.game_flow["df_change"] = True + soundsX["click"].play() + + +# input +def activate_edit_text(game): + game[0].game_flow["ac"] = False + soundsX["click"].play() + + +def update_text(menu_objects, setup, game, palette): + custom_text = font.render(game.game_flow["custom_string"], False, + palette.palette["outline-dark"]) + custom_text_hover = font.render(game.game_flow["custom_string"], False, + palette.palette["background"]) + ct_images = {"idle": create_text_sur(custom_text, [20, 20], [100, 100]), + "hover": create_text_sur(custom_text_hover, [20, 20], [100, 100])} + for image in ct_images.keys(): + ct_images[image].set_colorkey((0, 0, 0)) + + for item in range(len(menu_objects)): + if menu_objects[item].on_click == activate_edit_text: + if menu_objects[item].name == "custom_text": + menu_objects[item].images = ct_images + menu_objects[item].display_image = ct_images["hover"] + + # saving json + setup["custom_num"] = game.game_flow["custom_string"] + with open("assets/saves/setup.json", "w") as f: + json.dump(setup, f, indent=4) + + return menu_objects, setup + + +def create_text_sur(text, pos, size): + sur = pygame.Surface(size) + sur.fill((0, 0, 0)) + sur.blit(text, pos) + return sur + + +def get_num_circles(palette, size_multiplier=1): + circle_num = pygame.image.load("assets/images/game/num_circle.png").convert() + circle_num_hover = pygame.image.load("assets/images/game/num_circle_hover.png").convert() + + circle_num = palette.swap_image(circle_num, "emerald", palette.current_palette) + circle_num_hover = palette.swap_image(circle_num_hover, "emerald", palette.current_palette) + + nums = [tiny_font.render(str(num), False, palette.palette["outline-dark"]) for num in range(1, 9)] + for i in range(len(nums)): + nums[i] = create_text_sur(nums[i], [10, 6], [30, 30]) + nums[i].set_colorkey((0, 0, 0)) + + num_dict = {} + + size = 0 + for i in range(len(nums)): + cn = circle_num.copy() + cnh = circle_num_hover.copy() + + cn.blit(nums[i], [0, 0]) + cnh.blit(nums[i], [0, 0]) + + cn.set_colorkey((0, 0, 0)) + cnh.set_colorkey((0, 0, 0)) + + size = int(30 * size_multiplier) + cn = pygame.transform.scale(cn, [size for _ in range(2)]) + cnh = pygame.transform.scale(cnh, [size for _ in range(2)]) + + num_dict[str(i + 1)] = {"idle": cn, + "hover": cnh} + + return num_dict, size + + +def get_images(palette, size): + circle_num = pygame.image.load("assets/images/game/num_circle.png").convert() + circle_num_hover = pygame.image.load("assets/images/game/num_circle_hover.png").convert() + + circle_num = palette.swap_image(circle_num, "emerald", palette.current_palette) + circle_num_hover = palette.swap_image(circle_num_hover, "emerald", palette.current_palette) + + nums = [tiny_font.render(str(num), False, palette.palette["backgroundShade"]) for num in range(1, 9)] + for i in range(len(nums)): + nums[i] = create_text_sur(nums[i], [10, 6], [30, 30]) + nums[i].set_colorkey((0, 0, 0)) + + num_dict = {} + + for i in range(len(nums)): + cn = circle_num.copy() + cnh = circle_num_hover.copy() + + cn.blit(nums[i], [0, 0]) + cnh.blit(nums[i], [0, 0]) + + cn.set_colorkey((0, 0, 0)) + cnh.set_colorkey((0, 0, 0)) + + cn = pygame.transform.scale(cn, [size for _ in range(2)]) + cnh = pygame.transform.scale(cnh, [size for _ in range(2)]) + + num_dict[str(i + 1)] = {"idle": cn, + "hover": cnh} + + return num_dict + + +def get_back_images(palette): + back_images = {"idle": pygame.image.load("assets/images/game/back.png").convert(), + "hover": pygame.image.load("assets/images/game/back_hover.png").convert()} + + for image in back_images.keys(): + back_images[image] = palette.swap_image(back_images[image], "emerald", palette.current_palette) + back_images[image].set_colorkey((0, 0, 0)) + + return back_images + + +def get_win_image(): + surf = pygame.Surface([500, 550]) + surf.fill((80, 80, 80)) + surf.set_alpha(180) + + text = font.render("Congratulations!", False, (10, 10, 10)) + surf.blit(text, [60, 120]) + + return surf + + +def go_back(args): + game = args[0] + + game.game_flow["quit"] = True + soundsX["click"].play() + + +def change_fs(args): + args[0].game_flow["change_fs"] = True + soundsX["click"].play() + + +def get_tutorial_circle_images(size_multiplier): + tutorial_circle_images = {"idle": pygame.image.load("assets/images/game/tutorial_circle.png").convert(), + "hover": pygame.image.load("assets/images/game/tutorial_circle_hover.png").convert()} + + size = int(30 * size_multiplier) + + for image in tutorial_circle_images.keys(): + tutorial_circle_images[image].set_alpha(120) + tutorial_circle_images[image].set_colorkey((0, 0, 0)) + tutorial_circle_images[image] = pygame.transform.scale(tutorial_circle_images[image], [size for _ in range(2)]) + + return tutorial_circle_images, size + + +def move_in_tutorial(args): + args[0].game_flow["tf"] = True + + +def play_click_sound(args): + soundsX["click"].play() diff --git a/hashi_game.py b/hashi_game.py new file mode 100644 index 0000000..f430df8 --- /dev/null +++ b/hashi_game.py @@ -0,0 +1,933 @@ +import json +import sounds +import widget_engine as wg +from hashi_functions import get_num_circles +from hashi_functions import get_images +import pygame +import math +import copy + + +pygame.init() + +soundsX = sounds.get_sounds() + + +class HashiG: + + def __init__(self, load_from_where, load_path, palette, mouse): + # creating variables + self.map = [] + self.size = None + self.indent = 40 + self.indent_default = 40 + self.node_count = 0 + self.win = False + + # for nodes + self.locked = False + self.c_node = None + self.temp_done = [0, 0, 0, 0] + self.temp_removed = [0, 0, 0, 0] + self.s_node = None + + # loading game + if load_from_where == "file.txt": + self.load_from_file(load_path, palette) + elif load_from_where == "file.json": + self.load_game(load_path, palette, mouse) + + self.set_size() + self.line_width = int(self.indent / 12) + 1 + + def load_from_file(self, path, palette): + with open(path, "r") as f: + file = f.read() + f.close() + + file = file.split("\n") + del file[-1] + + # init blank map + self.size = len(file) + self.map = [[NodeG([x, y]) for y in range(len(file))] for x in range(len(file))] + self.set_size() + + # getting images + images, widget_size = get_num_circles(palette, math.sqrt(self.indent/self.indent_default)**1.5) + switch_images = get_images(palette, widget_size) + + # reversing to get back into our format + file = file[::-1] + file = [x.split() for x in file] + for y in range(len(file)): + for x in range(len(file[y])): + node = self.map[x][y] + node.bridge_num = int(file[y][x]) + if int(file[y][x]) != 0: + # if its node and not blank + + node.locked = True + node.widget = wg.CircleButton(widget_size, [0, 0], + images[file[y][x]], self.lock, [node]) + node.widget.switch_images = switch_images[file[y][x]] + self.node_count += 1 + + def load_game(self, path, palette, mouse): + + with open(path, "r") as f: + save = json.load(f) + + self.load_from_file(f"assets/maps/{save['difficulty']}.txt", palette) + + for connection in save["connections"]: + # also bridge_occupied + if self.map[connection["pos"][0]][connection["pos"][1]].bridge_num != 0: + self.map[connection["pos"][0]][connection["pos"][1]].bridges = connection["bridges"] + self.map[connection["pos"][0]][connection["pos"][1]].bridge_occupied = connection["bridge_occupied"] + self.map[connection["pos"][0]][connection["pos"][1]].locked = connection["locked"] + if self.map[connection["pos"][0]][connection["pos"][1]].bridge_occupied == self.map[connection["pos"][0]][connection["pos"][1]].bridge_num: + if self.map[connection["pos"][0]][connection["pos"][1]].widget is not None: + self.map[connection["pos"][0]][connection["pos"][1]].widget.switch(mouse.mouse_pos) + else: + self.map[connection["pos"][0]][connection["pos"][1]].locked = connection["locked"] + + def blit(self, display, palette, scroll): + # grid + for lines in range(self.size): + pygame.draw.line(display, palette.palette["outline-shade"], + [(lines * self.indent) - scroll.scroll[0], + 0 - scroll.scroll[1]], + [(lines * self.indent) - scroll.scroll[0], + ((self.size - 1) * self.indent) - scroll.scroll[1]], self.line_width) + pygame.draw.line(display, palette.palette["outline-shade"], + [0 - scroll.scroll[0], + (lines * self.indent) - scroll.scroll[1]], + [((self.size - 1) * self.indent) - scroll.scroll[0], + (lines * self.indent) - scroll.scroll[1]], self.line_width) + + # connections + for x in range(self.size): + for y in range(self.size): + for connection in self.map[x][y].bridges: + if connection["connection"] is not None: + if connection["bridge_num"] == 1: + node = self.map[x][y] + target = self.map[connection["connection"][0]][connection["connection"][1]] + pygame.draw.line(display, palette.palette["addition"], + [(node.pos[0] * self.indent) - scroll.scroll[0], + (node.pos[1] * self.indent) - scroll.scroll[1]], + [(target.pos[0] * self.indent) - scroll.scroll[0], + (target.pos[1] * self.indent) - scroll.scroll[1]], + self.line_width + 1) + elif connection["bridge_num"] == 2: + node = self.map[x][y] + target = self.map[connection["connection"][0]][connection["connection"][1]] + pygame.draw.line(display, palette.palette["outline-dark"], + [(node.pos[0] * self.indent) - scroll.scroll[0], + (node.pos[1] * self.indent) - scroll.scroll[1]], + [(target.pos[0] * self.indent) - scroll.scroll[0], + (target.pos[1] * self.indent) - scroll.scroll[1]], + int((self.line_width * 1.4) + 1)) + + # dealing with nodes + for x in range(self.size): + for y in range(self.size): + if self.map[x][y].widget is not None: + node = self.map[x][y] + node.widget.pos = [(node.pos[0] * self.indent) - node.widget.radius, + (node.pos[1] * self.indent) - node.widget.radius] + node.widget.center = [node.widget.pos[0] + node.widget.radius, + node.widget.pos[1] + node.widget.radius] + node.widget.blit(display, scroll) + + def hover(self, mouse, scroll): + ms = mouse.get_scrolled(scroll) + + for y in range(self.size): + for x in range(self.size): + if self.map[x][y].widget is not None: + self.map[x][y].widget.hover_check(ms) + + def click(self, mouse, scroll): + ms = mouse.get_scrolled(scroll) + + for y in range(self.size): + for x in range(self.size): + if self.map[x][y].widget is not None: + self.map[x][y].widget.click_check(ms) + + def on_locked(self, mouse, scroll): + ms = mouse.get_scrolled(scroll) + cords = self.c_node.pos + + dists = [ms[0] - self.c_node.widget.center[0], + ms[1] - self.c_node.widget.center[1]] + da = list(map(abs, dists)) + self.clear_temp() + self.clear_temp_remove() + + # if we still can add bridges + if self.c_node.bridge_occupied < self.c_node.bridge_num: + + # if we move out mouse more on x + if da[0] > da[1]: + if dists[0] > 0: + # here we do in case we move right + + self.__right(cords) + + else: + # here we do in case we move left + + self.__left(cords) + else: + + if dists[1] > 0: + # here we do in case we move down + + self.__down(cords) + else: + # here we do in case we go up + + self.__up(cords) + # removing bridges here + else: + if da[0] > da[1]: + if dists[0] > 0: + # here we do in case we move right + + if self.c_node.bridges[2]["bridge_num"] != 2: + for i in range(1, self.size): + if cords[0] + i < self.size: + if self.map[cords[0] + i][cords[1]].locked is False: + pass + + else: + if self.map[cords[0] + i][cords[1]].pos == self.c_node.bridges[2]["connection"]: + # connect + self.s_node = self.map[cords[0] + i][cords[1]] + + self.c_node.bridges[2]["bridge_num"] -= 1 + self.s_node.bridges[3]["bridge_num"] -= 1 + + if self.c_node.bridges[2]["bridge_num"] == -1: + self.c_node.bridges[2]["bridge_num"] = 0 + self.s_node.bridges[3]["bridge_num"] = 0 + break + + self.temp_removed[2] = 1 + + if self.c_node.bridges[2]["bridge_num"] == 0: + self.c_node.bridges[2] = {"bridge_num": 0, "connection": True} + self.s_node.bridges[3] = {"bridge_num": 0, "connection": True} + + break + else: + if self.c_node.bridges[2]["connection"] is not None and self.map[cords[0] + i][cords[1]].bridge_occupied == 0: + pass + else: + break + else: + break + else: + self.__right(cords) + + else: + # here we do in case we move left + + if self.c_node.bridges[3]["bridge_num"] != 2: + for i in range(1, self.size): + if cords[0] - i > -1: + if self.map[cords[0] - i][cords[1]].locked is False: + pass + + else: + if self.map[cords[0] - i][cords[1]].pos == self.c_node.bridges[3]["connection"]: + # connect + self.s_node = self.map[cords[0] - i][cords[1]] + + self.c_node.bridges[3]["bridge_num"] -= 1 + self.s_node.bridges[2]["bridge_num"] -= 1 + + if self.c_node.bridges[3]["bridge_num"] == -1: + self.c_node.bridges[3]["bridge_num"] = 0 + self.s_node.bridges[2]["bridge_num"] = 0 + break + + self.temp_removed[3] = 1 + + if self.c_node.bridges[3]["bridge_num"] == 0: + self.c_node.bridges[3] = {"bridge_num": 0, "connection": True} + self.s_node.bridges[2] = {"bridge_num": 0, "connection": True} + + break + else: + if self.c_node.bridges[3]["connection"] is not None and self.map[cords[0] - i][cords[1]].bridge_occupied == 0: + pass + else: + break + else: + break + else: + self.__left(cords) + else: + + if dists[1] > 0: + # here we do in case we move down + + if self.c_node.bridges[1]["bridge_num"] != 2: + for i in range(1, self.size): + if cords[1] + i < self.size: + if self.map[cords[0]][cords[1] + i].locked is False: + pass + + else: + if self.map[cords[0]][cords[1] + i].pos == self.c_node.bridges[1]["connection"]: + # connect + self.s_node = self.map[cords[0]][cords[1] + i] + + self.c_node.bridges[1]["bridge_num"] -= 1 + self.s_node.bridges[0]["bridge_num"] -= 1 + + if self.c_node.bridges[1]["bridge_num"] == -1: + self.c_node.bridges[1]["bridge_num"] = 0 + self.s_node.bridges[0]["bridge_num"] = 0 + break + + self.temp_removed[1] = 1 + + if self.c_node.bridges[1]["bridge_num"] == 0: + self.c_node.bridges[1] = {"bridge_num": 0, "connection": True} + self.s_node.bridges[0] = {"bridge_num": 0, "connection": True} + + break + else: + if self.c_node.bridges[1]["connection"] is not None and self.map[cords[0]][cords[1] + i].bridge_occupied == 0: + pass + else: + break + else: + break + else: + self.__down(cords) + else: + # here we do in case we go up + + if self.c_node.bridges[0]["bridge_num"] != 2: + for i in range(1, self.size): + if cords[1] - i > -1: + if self.map[cords[0]][cords[1] - i].locked is False: + pass + + else: + if self.map[cords[0]][cords[1] - i].pos == self.c_node.bridges[0]["connection"]: + # connect + self.s_node = self.map[cords[0]][cords[1] - i] + + self.c_node.bridges[0]["bridge_num"] -= 1 + self.s_node.bridges[1]["bridge_num"] -= 1 + + if self.c_node.bridges[0]["bridge_num"] == -1: + self.c_node.bridges[0]["bridge_num"] -= 1 + self.s_node.bridges[1]["bridge_num"] -= 1 + break + + self.temp_removed[0] = 1 + + if self.c_node.bridges[0]["bridge_num"] == 0: + self.c_node.bridges[0] = {"bridge_num": 0, "connection": True} + self.s_node.bridges[1] = {"bridge_num": 0, "connection": True} + + break + else: + if self.c_node.bridges[0]["connection"] is not None and self.map[cords[0]][cords[1] - i].bridge_occupied == 0: + pass + else: + break + else: + break + else: + self.__up(cords) + + def clear_temp(self): + if self.temp_done.count(1) != 0: + i = self.temp_done.index(1) + self.temp_done[i] = 0 + + if i == 0: + self.c_node.bridges[0]["bridge_num"] -= 1 + self.s_node.bridges[1]["bridge_num"] -= 1 + + if self.c_node.bridges[0]["bridge_num"] == 0: + self.c_node.bridges[0] = {"bridge_num": 0, "connection": None} + self.s_node.bridges[1] = {"bridge_num": 0, "connection": None} + elif self.c_node.bridges[0]["bridge_num"] == -1: + self.c_node.bridges[0]["bridge_num"] = 2 + self.s_node.bridges[1]["bridge_num"] = 2 + + self.c_node.bridges[0]["connection"] = self.s_node.pos + self.s_node.bridges[1]["connection"] = self.c_node.pos + elif i == 1: + self.c_node.bridges[1]["bridge_num"] -= 1 + self.s_node.bridges[0]["bridge_num"] -= 1 + + if self.c_node.bridges[1]["bridge_num"] == 0: + self.c_node.bridges[1] = {"bridge_num": 0, "connection": None} + self.s_node.bridges[0] = {"bridge_num": 0, "connection": None} + elif self.c_node.bridges[1]["bridge_num"] == -1: + self.c_node.bridges[1]["bridge_num"] = 2 + self.s_node.bridges[0]["bridge_num"] = 2 + + self.c_node.bridges[1]["connection"] = self.s_node.pos + self.s_node.bridges[0]["connection"] = self.c_node.pos + elif i == 2: + self.c_node.bridges[2]["bridge_num"] -= 1 + self.s_node.bridges[3]["bridge_num"] -= 1 + + if self.c_node.bridges[2]["bridge_num"] == 0: + self.c_node.bridges[2] = {"bridge_num": 0, "connection": None} + self.s_node.bridges[3] = {"bridge_num": 0, "connection": None} + elif self.c_node.bridges[2]["bridge_num"] == -1: + self.c_node.bridges[2]["bridge_num"] = 2 + self.s_node.bridges[3]["bridge_num"] = 2 + + self.c_node.bridges[2]["connection"] = self.s_node.pos + self.s_node.bridges[3]["connection"] = self.c_node.pos + elif i == 3: + self.c_node.bridges[3]["bridge_num"] -= 1 + self.s_node.bridges[2]["bridge_num"] -= 1 + + if self.c_node.bridges[3]["bridge_num"] == 0: + self.c_node.bridges[3] = {"bridge_num": 0, "connection": None} + self.s_node.bridges[2] = {"bridge_num": 0, "connection": None} + elif self.c_node.bridges[3]["bridge_num"] == -1: + self.c_node.bridges[3]["bridge_num"] = 2 + self.s_node.bridges[2]["bridge_num"] = 2 + + self.c_node.bridges[3]["connection"] = self.s_node.pos + self.s_node.bridges[2]["connection"] = self.c_node.pos + + def clear_temp_remove(self): + if self.temp_removed.count(1) != 0: + i = self.temp_removed.index(1) + self.temp_removed[i] = 0 + cords = self.c_node.pos + + if i == 0: + for i in range(1, self.size): + if cords[1] - i > -1: + if self.map[cords[0]][cords[1] - i].locked is False: + pass + + else: + if self.map[cords[0]][cords[1] - i].pos == self.s_node.pos: + # connect + self.s_node = self.map[cords[0]][cords[1] - i] + + self.c_node.bridges[0]["bridge_num"] += 1 + self.s_node.bridges[1]["bridge_num"] += 1 + + if self.c_node.bridges[0]["bridge_num"] == 3: + self.c_node.bridges[0] = {"bridge_num": 0, "connection": None} + self.s_node.bridges[1] = {"bridge_num": 0, "connection": None} + else: + self.c_node.bridges[0]["connection"] = self.s_node.pos + self.s_node.bridges[1]["connection"] = self.c_node.pos + + break + else: + if self.c_node.bridges[0]["connection"] is not None and self.map[cords[0]][cords[1] - i].bridge_occupied == 0: + pass + else: + break + else: + break + elif i == 1: + for i in range(1, self.size): + if cords[1] + i < self.size: + if self.map[cords[0]][cords[1] + i].locked is False: + pass + + else: + if self.map[cords[0]][cords[1] + i].pos == self.s_node.pos: + # connect + self.s_node = self.map[cords[0]][cords[1] + i] + + self.c_node.bridges[1]["bridge_num"] += 1 + self.s_node.bridges[0]["bridge_num"] += 1 + + if self.c_node.bridges[1]["bridge_num"] == 3: + self.c_node.bridges[1] = {"bridge_num": 0, "connection": None} + self.s_node.bridges[0] = {"bridge_num": 0, "connection": None} + else: + self.c_node.bridges[1]["connection"] = self.s_node.pos + self.s_node.bridges[0]["connection"] = self.c_node.pos + + break + else: + if self.c_node.bridges[1]["connection"] is not None and self.map[cords[0]][cords[1] + i].bridge_occupied == 0: + pass + else: + break + else: + break + elif i == 2: + for i in range(1, self.size): + if cords[0] + i < self.size: + if self.map[cords[0] + i][cords[1]].locked is False: + pass + + else: + if self.map[cords[0] + i][cords[1]].pos == self.s_node.pos: + # connect + self.s_node = self.map[cords[0] + i][cords[1]] + + self.c_node.bridges[2]["bridge_num"] += 1 + self.s_node.bridges[3]["bridge_num"] += 1 + + if self.c_node.bridges[2]["bridge_num"] == 3: + self.c_node.bridges[2] = {"bridge_num": 0, "connection": None} + self.s_node.bridges[3] = {"bridge_num": 0, "connection": None} + else: + self.c_node.bridges[2]["connection"] = self.s_node.pos + self.s_node.bridges[3]["connection"] = self.c_node.pos + + break + else: + if self.c_node.bridges[2]["connection"] is not None and self.map[cords[0] + i][cords[1]].bridge_occupied == 0: + pass + else: + break + else: + break + elif i == 3: + for i in range(1, self.size): + if cords[0] - i > -1: + if self.map[cords[0] - i][cords[1]].locked is False: + pass + + else: + if self.map[cords[0] - i][cords[1]].pos == self.s_node.pos: + # connect + self.s_node = self.map[cords[0] - i][cords[1]] + + self.c_node.bridges[3]["bridge_num"] += 1 + self.s_node.bridges[2]["bridge_num"] += 1 + + if self.c_node.bridges[3]["bridge_num"] == 3: + self.c_node.bridges[3] = {"bridge_num": 0, "connection": None} + self.s_node.bridges[2] = {"bridge_num": 0, "connection": None} + else: + self.c_node.bridges[3]["connection"] = self.s_node.pos + self.s_node.bridges[2]["connection"] = self.c_node.pos + + break + else: + if self.c_node.bridges[3]["connection"] is not None and self.map[cords[0] - i][cords[1]].bridge_occupied == 0: + pass + else: + break + else: + break + + def click_on_locked(self, mouse, scroll): + c = self.c_node + s = self.s_node + temp_done = copy.copy(self.temp_done) + temp_removed = copy.copy(self.temp_removed) + other_removed = self.get_other(temp_removed) + other_done = self.get_other(temp_done) + self.clear_temp() + self.clear_temp_remove() + + if temp_done.count(1) != 0: + + # connecting + i1 = temp_done.index(1) + i2 = other_done.index(1) + + switch = {"c": False, + "s": False} + + if c.bridge_occupied == c.bridge_num: + switch["c"] = True + if s.bridge_occupied == s.bridge_num: + switch["s"] = True + + c.bridges[i1]["bridge_num"] += 1 + s.bridges[i2]["bridge_num"] += 1 + c.bridge_occupied += 1 + s.bridge_occupied += 1 + + c.bridges[i1]["connection"] = s.pos + s.bridges[i2]["connection"] = c.pos + + if c.bridges[i1]["bridge_num"] > 2: + c.bridges[i1] = {"bridge_num": 0, "connection": None} + s.bridges[i2] = {"bridge_num": 0, "connection": None} + c.bridge_occupied -= 3 + s.bridge_occupied -= 3 + + # playing sound + soundsX["remove"].play() + else: + soundsX["join"].play() + + if c.bridge_occupied == c.bridge_num or switch["c"]: + c.widget.switch(mouse.get_scrolled(scroll)) + if s.bridge_occupied == s.bridge_num or switch["s"]: + s.widget.switch(mouse.get_scrolled(scroll)) + + # locking + if temp_done[1]: + for i in range(c.pos[1] + 1, s.pos[1]): + if c.bridges[1]["connection"] is not None: + self.map[c.pos[0]][i].locked = True + else: + self.map[c.pos[0]][i].locked = False + + elif temp_done[0]: + for i in range(s.pos[1] + 1, c.pos[1]): + if c.bridges[0]["connection"] is not None: + self.map[s.pos[0]][i].locked = True + else: + self.map[s.pos[0]][i].locked = False + + elif temp_done[2]: + for i in range(c.pos[0] + 1, s.pos[0]): + if c.bridges[2]["connection"] is not None: + self.map[i][c.pos[1]].locked = True + else: + self.map[i][c.pos[1]].locked = False + + elif temp_done[3]: + for i in range(s.pos[0] + 1, c.pos[0]): + if c.bridges[3]["connection"] is not None: + self.map[i][s.pos[1]].locked = True + else: + self.map[i][s.pos[1]].locked = False + elif temp_removed.count(1) != 0: + # connecting + i1 = temp_removed.index(1) + i2 = other_removed.index(1) + + switch = {"c": False, + "s": False} + + if c.bridge_occupied == c.bridge_num: + switch["c"] = True + if s.bridge_occupied == s.bridge_num: + switch["s"] = True + + c.bridges[i1]["bridge_num"] -= 1 + s.bridges[i2]["bridge_num"] -= 1 + c.bridge_occupied -= 1 + s.bridge_occupied -= 1 + + c.bridges[i1]["connection"] = s.pos + s.bridges[i2]["connection"] = c.pos + + if c.bridges[i1]["bridge_num"] == 0: + c.bridges[i1]["connection"] = None + s.bridges[i2]["connection"] = None + + if switch["c"]: + c.widget.switch(mouse.get_scrolled(scroll)) + if switch["s"]: + s.widget.switch(mouse.get_scrolled(scroll)) + + # locking + if temp_removed[1]: + for i in range(c.pos[1] + 1, s.pos[1]): + if c.bridges[1]["connection"] is not None: + self.map[c.pos[0]][i].locked = True + else: + self.map[c.pos[0]][i].locked = False + + elif temp_removed[0]: + for i in range(s.pos[1] + 1, c.pos[1]): + if c.bridges[0]["connection"] is not None: + self.map[s.pos[0]][i].locked = True + else: + self.map[s.pos[0]][i].locked = False + + elif temp_removed[2]: + for i in range(c.pos[0] + 1, s.pos[0]): + if c.bridges[2]["connection"] is not None: + self.map[i][c.pos[1]].locked = True + else: + self.map[i][c.pos[1]].locked = False + + elif temp_removed[3]: + for i in range(s.pos[0] + 1, c.pos[0]): + if c.bridges[3]["connection"] is not None: + self.map[i][s.pos[1]].locked = True + else: + self.map[i][s.pos[1]].locked = False + + # playing sound + soundsX["remove"].play() + + def check_win(self): + node_done_count = 0 + test_node = None + for line in self.map: + for item in line: + if item.bridge_num > 0: + if item.bridge_num == item.bridge_occupied: + node_done_count += 1 + test_node = item + + if node_done_count == self.node_count: + + # checking if all connected + nodes_to_do = [test_node] + nodes_to_remove = [] + appending = [] + while len(nodes_to_do) != 0: + # making them done + for node in nodes_to_do: + node.check_for_win = True + + for node in nodes_to_do: + for connection in node.bridges: + if connection["connection"] is not None: + if self.map[connection["connection"][0]][connection["connection"][1]].check_for_win is False: + appending.append(self.map[connection["connection"][0]][connection["connection"][1]]) + nodes_to_remove.append(node) + + for node in appending: + nodes_to_do.append(node) + appending = [] + + for node in nodes_to_remove: + nodes_to_do.remove(node) + nodes_to_remove = [] + + win = True + for line in self.map: + for item in line: + if item.bridge_num > 0: + if item.check_for_win is False: + win = False + + if win: + self.win = True + else: + for line in self.map: + for item in line: + item.check_for_win = False + + def save(self, setup): + save = {"difficulty": setup["difficulty"]} + + connections = [] + + for line in self.map: + for node in line: + if node.locked is True: + if node.bridge_num != 0: + connections.append({"pos": node.pos, + "bridges": node.bridges, + "bridge_occupied": node.bridge_occupied, + "locked": node.locked}) + else: + connections.append({"pos": node.pos, + "locked": node.locked}) + save["connections"] = connections + + with open(f"assets/saves/game_saves/{setup['difficulty']}.json", "w") as f: + json.dump(save, f, indent=4) + + def print_nodes(self): + print("_" * 20) + + for line in self.map: + for item in line: + if item.bridge_num > 0: + print(f"| Node at pos {item.widget.pos}, map pos {item.pos}, bridge_num {item.bridge_num}" + f" with {item.bridge_occupied} occupied bridges. |") + + print("_" * 20) + + # technical stuff not used inside class + def set_size(self): + if self.size <= 15: + self.indent = (15 / self.size) * 40 + + def get_length(self): + return self.indent * (self.size - 1) + + def lock(self, args): + self.locked = True + self.c_node = args[0] + + # technical stuff used inside class + def __down(self, cords): + for i in range(1, self.size): + if cords[1] + i < self.size: + if self.map[cords[0]][cords[1] + i].locked is False: + pass + + else: + if self.map[cords[0]][cords[1] + i].bridge_occupied < self.map[cords[0]][ + cords[1] + i].bridge_num or ( + self.c_node.bridges[1]["bridge_num"] == 2 and self.map[cords[0]][cords[1] + i].pos == + self.c_node.bridges[1]["connection"]): + # connect + self.s_node = self.map[cords[0]][cords[1] + i] + + self.c_node.bridges[1]["bridge_num"] += 1 + self.s_node.bridges[0]["bridge_num"] += 1 + self.temp_done[1] = 1 + + if self.c_node.bridges[1]["bridge_num"] == 3: + self.c_node.bridges[1] = {"bridge_num": 0, "connection": None} + self.s_node.bridges[0] = {"bridge_num": 0, "connection": None} + else: + self.c_node.bridges[1]["connection"] = self.s_node.pos + self.s_node.bridges[0]["connection"] = self.c_node.pos + + break + else: + if self.c_node.bridges[1]["connection"] is not None and self.map[cords[0]][ + cords[1] + i].bridge_occupied == 0: + pass + else: + break + else: + break + + def __up(self, cords): + for i in range(1, self.size): + if cords[1] - i > -1: + if self.map[cords[0]][cords[1] - i].locked is False: + pass + + else: + if self.map[cords[0]][cords[1] - i].bridge_occupied < self.map[cords[0]][ + cords[1] - i].bridge_num or ( + self.c_node.bridges[0]["bridge_num"] == 2 and self.map[cords[0]][cords[1] - i].pos == + self.c_node.bridges[0]["connection"]): + # connect + self.s_node = self.map[cords[0]][cords[1] - i] + + self.c_node.bridges[0]["bridge_num"] += 1 + self.s_node.bridges[1]["bridge_num"] += 1 + self.temp_done[0] = 1 + + if self.c_node.bridges[0]["bridge_num"] == 3: + self.c_node.bridges[0] = {"bridge_num": 0, "connection": None} + self.s_node.bridges[1] = {"bridge_num": 0, "connection": None} + else: + self.c_node.bridges[0]["connection"] = self.s_node.pos + self.s_node.bridges[1]["connection"] = self.c_node.pos + + break + else: + if self.c_node.bridges[0]["connection"] is not None and self.map[cords[0]][ + cords[1] - i].bridge_occupied == 0: + pass + else: + break + else: + break + + def __right(self, cords): + for i in range(1, self.size): + if cords[0] + i < self.size: + if self.map[cords[0] + i][cords[1]].locked is False: + pass + + else: + if self.map[cords[0] + i][cords[1]].bridge_occupied < self.map[cords[0] + i][ + cords[1]].bridge_num or ( + self.c_node.bridges[2]["bridge_num"] == 2 and self.map[cords[0] + i][cords[1]].pos == + self.c_node.bridges[2]["connection"]): + # connect + self.s_node = self.map[cords[0] + i][cords[1]] + + self.c_node.bridges[2]["bridge_num"] += 1 + self.s_node.bridges[3]["bridge_num"] += 1 + self.temp_done[2] = 1 + + if self.c_node.bridges[2]["bridge_num"] == 3: + self.c_node.bridges[2] = {"bridge_num": 0, "connection": None} + self.s_node.bridges[3] = {"bridge_num": 0, "connection": None} + else: + self.c_node.bridges[2]["connection"] = self.s_node.pos + self.s_node.bridges[3]["connection"] = self.c_node.pos + + break + else: + if self.c_node.bridges[2]["connection"] is not None and self.map[cords[0] + i][ + cords[1]].bridge_occupied == 0: + pass + else: + break + else: + break + + def __left(self, cords): + for i in range(1, self.size): + if cords[0] - i > -1: + if self.map[cords[0] - i][cords[1]].locked is False: + pass + + else: + if self.map[cords[0] - i][cords[1]].bridge_occupied < self.map[cords[0] - i][ + cords[1]].bridge_num or ( + self.c_node.bridges[3]["bridge_num"] == 2 and self.map[cords[0] - i][cords[1]].pos == + self.c_node.bridges[3]["connection"]): + # connect + self.s_node = self.map[cords[0] - i][cords[1]] + + self.c_node.bridges[3]["bridge_num"] += 1 + self.s_node.bridges[2]["bridge_num"] += 1 + self.temp_done[3] = 1 + + if self.c_node.bridges[3]["bridge_num"] == 3: + self.c_node.bridges[3] = {"bridge_num": 0, "connection": None} + self.s_node.bridges[2] = {"bridge_num": 0, "connection": None} + else: + self.c_node.bridges[3]["connection"] = self.s_node.pos + self.s_node.bridges[2]["connection"] = self.c_node.pos + + break + else: + if self.c_node.bridges[3]["connection"] is not None and self.map[cords[0] - i][ + cords[1]].bridge_occupied == 0: + pass + else: + break + else: + break + + @staticmethod + def get_other(temp): + second = [0, 0, 0, 0] + + if temp[0]: + second[1] = 1 + elif temp[1]: + second[0] = 1 + elif temp[2]: + second[3] = 1 + elif temp[3]: + second[2] = 1 + + return second + + +class NodeG: + + def __init__(self, pos, bridge_num=0): + self.pos = pos + self.bridge_occupied = 0 + self.bridge_num = bridge_num + self.locked = False + self.widget = None + self.check_for_win = False + + self.bridges = [{"bridge_num": 0, "connection": None} for _ in range(4)] # down, up, right, left diff --git a/hashi_generator.py b/hashi_generator.py new file mode 100644 index 0000000..b06d194 --- /dev/null +++ b/hashi_generator.py @@ -0,0 +1,232 @@ +import random + + +class Hashi: + + def __init__(self, size): + if size < 2: + size = 2 + print("Size cant be 1 or lower.") + self.size = size + self.node_limit_lower = size # lower limit + self.node_upper_limit = int(size ** 1.4) + 1 # upper limit + self.node_count = 0 + self.low_lim = 0 + self.high_lim = size + + self.map = [[Node([x, y]) for y in range(size)] for x in range(size)] + + def generate_map(self): + # down, up, right, left + first = [random.randint(0, self.size-1), random.randint(0, self.size-1)] + self.node_count += 1 + + node = self.get_node(first) + self.write_node(node) + nodes_to_do = self.get_new_nodes(node) + while len(nodes_to_do) != 0: + for n in nodes_to_do: + node = self.get_node(n) + self.write_node(node) + nodes_to_do = nodes_to_do + self.get_new_nodes(node) + nodes_to_do.remove(n) + + def get_node(self, cords): + ways = [0, 0, 0, 0] # down, up, right, left + possibilities = 0 + + if self.node_count >= self.node_upper_limit: + node = {"ways": [0, 0, 0, 0], + "num_of_bridges": [random.randint(1, 2) for _ in range(4)], + "pos": cords} + + return node + + if cords[1] - 1 > -1 and self.map[cords[0]][cords[1] - 1].locked is False: + ways[0] += 1 + possibilities += 1 + + if cords[1] + 1 < self.size and self.map[cords[0]][cords[1] + 1].locked is False: + ways[1] += 1 + possibilities += 1 + + if cords[0] + 1 < self.size and self.map[cords[0] + 1][cords[1]].locked is False: + ways[2] += 1 + possibilities += 1 + + if cords[0] - 1 > -1 and self.map[cords[0] - 1][cords[1]].locked is False: + ways[3] += 1 + possibilities += 1 + + if possibilities: + attempts = [x+1 if random.randint(0, 1) == 0 else x for x in ways] + + # making the puzzle forcefully bigger + if self.node_count < self.node_limit_lower: + while 2 not in attempts: + attempts = [x + 1 if random.randint(0, 1) == 0 else x for x in ways] + + # getting the starting distances + distances = [1 if x > 1 else 0 for x in attempts] + + # getting possible distance up to 6 + if attempts[0] > 1: + for i in range(2, self.size): + if cords[1] - i > -1 and self.map[cords[0]][cords[1] - i].locked is False: + distances[0] += 1 + else: + break + + if attempts[1] > 1: + for i in range(2, self.size): + if cords[1]+i < self.size and self.map[cords[0]][cords[1]+i].locked is False: + distances[1] += 1 + else: + break + + if attempts[2] > 1: + for i in range(2, self.size): + if cords[0] + i < self.size and self.map[cords[0]+i][cords[1]].locked is False: + distances[2] += 1 + else: + break + + if attempts[3] > 1: + for i in range(2, self.size): + if cords[0] - i > -1 and self.map[cords[0] - i][cords[1]].locked is False: + distances[3] += 1 + else: + break + + final_decision = [random.randint(1, x)if x != 0 else 0 for x in distances] + + node = {"ways": final_decision, + "num_of_bridges": [random.randint(1, 2) for _ in range(4)], + "pos": cords} + + return node + + else: + node = {"ways": [0, 0, 0, 0], + "num_of_bridges": [random.randint(1, 2) for _ in range(4)], + "pos": cords} + + return node + + def write_node(self, node): + pos = node["pos"] + + i = 0 + sum_of_bridges = 0 + for item in node["ways"]: + if item != 0: + sum_of_bridges += node["num_of_bridges"][i] + + # adding the bridges to new nodes + if i == 0: + self.map[pos[0]][pos[1] - item].bridge_num += node["num_of_bridges"][i] + elif i == 1: + self.map[pos[0]][pos[1] + item].bridge_num += node["num_of_bridges"][i] + elif i == 2: + self.map[pos[0] + item][pos[1]].bridge_num += node["num_of_bridges"][i] + elif i == 3: + self.map[pos[0] - item][pos[1]].bridge_num += node["num_of_bridges"][i] + + i += 1 + + self.map[pos[0]][pos[1]].locked = True + self.map[pos[0]][pos[1]].bridge_num += sum_of_bridges + + def get_new_nodes(self, node): + pos = node["pos"] + # down, up, right, left + nodes_to_send = [] + + for nodeX in range(1, node["ways"][0]+1): + self.map[pos[0]][pos[1] - nodeX].locked = True + + if nodeX == node["ways"][0]: + nodes_to_send.append(self.map[pos[0]][pos[1] - nodeX].pos) + + for nodeX in range(1, node["ways"][1]+1): + self.map[pos[0]][pos[1] + nodeX].locked = True + + if nodeX == node["ways"][1]: + nodes_to_send.append(self.map[pos[0]][pos[1] + nodeX].pos) + + for nodeX in range(1, node["ways"][2]+1): + self.map[pos[0] + nodeX][pos[1]].locked = True + + if nodeX == node["ways"][2]: + nodes_to_send.append(self.map[pos[0] + nodeX][pos[1]].pos) + + for nodeX in range(1, node["ways"][3]+1): + self.map[pos[0] - nodeX][pos[1]].locked = True + + if nodeX == node["ways"][3]: + nodes_to_send.append(self.map[pos[0] - nodeX][pos[1]].pos) + + self.node_count += len(nodes_to_send) + + return nodes_to_send + + def save(self, path): + final_string = "" + + for y in range(self.size): + + for x in range(self.size): + final_string += str(self.map[x][y].bridge_num) + " " + + final_string += "\n" + + with open(path, "w") as f: + f.write(final_string) + f.close() + + def print(self, bridges=False): + final_string = "_" * ((self.size * 2) + 3) + "\n" + + for y in range(self.size): + final_string += "| " + + for x in range(self.size): + if bridges is False: + final_string += str(self.map[x][y].bridge_num) + " " + else: + final_string += f" |* {self.map[x][y].bridge_num} *,{str(self.map[x][y].locked)[0]}| " + + final_string += "|\n" + + final_string += "¯" * ((self.size * 2) + 3) + + print(final_string) + + def __len__(self): + return self.node_count + + def __str__(self): + final_string = "_" * ((self.size*2)+3) + "\n" + + for y in range(self.size): + final_string += "| " + + for x in range(self.size): + final_string += str(self.map[x][y].bridge_num) + " " + + final_string += "|\n" + + final_string += "¯" * ((self.size*2)+3) + + return final_string + + def __add__(self, other): + return self.node_count + other.node_count + + +class Node: + + def __init__(self, pos, bridge_num=0): + self.pos = pos + self.bridge_num = bridge_num + self.locked = False diff --git a/menu_effect.py b/menu_effect.py new file mode 100644 index 0000000..13257e9 --- /dev/null +++ b/menu_effect.py @@ -0,0 +1,50 @@ +import pygame +import random + + +class Circle_effect: + + circles = [] + timer = 0 + + def __init__(self, pos, radius, speed, vertical): + self.pos = pos + self.radius = radius + self.speed = speed + self.vertical = vertical # True for going up False for down + + def move(self, display, palette, horizontal=False): + sur = pygame.Surface((self.radius * 2, self.radius * 2)) + pygame.draw.circle(sur, palette.palette["addition"], [self.radius, self.radius], self.radius) + sur.set_alpha(random.randint(100, 200)) + sur.set_colorkey((0, 0, 0)) + display.blit(sur, self.pos) + + if self.vertical: + if horizontal: + self.pos[0] -= self.speed + else: + self.pos[1] -= self.speed + else: + if horizontal: + self.pos[0] += self.speed + else: + self.pos[1] += self.speed + + if self.pos[1] < -100 or self.pos[0] > 700 or self.pos[1] > 700 or self.pos[0] < -100: + self.delete(self) + + @classmethod + def generate_circles(cls, amount=100, y_lim=None): + if y_lim is None: + y_lim = [0, 100] + + for _ in range(amount): + cls.circles.append(Circle_effect([random.randint(y_lim[0], y_lim[1]), random.randint(0, 650)], + random.randint(20, 50), random.randint(1, 4), + [True if x == 1 else False for x in [random.randint(0, 1)]][0])) + + @classmethod + def delete(cls, circle): + cls.circles.remove(circle) + diff --git a/s_engine.py b/s_engine.py new file mode 100644 index 0000000..cb2ea6b --- /dev/null +++ b/s_engine.py @@ -0,0 +1,527 @@ +import pygame +import sys +import time +import random +import math +import copy +from pygame.locals import * + +pygame.init() + + +# has attributes for basic thing in game +class Game: + def __init__(self, game_maps=None): + self.alive = True + # fs = fullscreen + self.fs = False + self.custom_id_giver = 0 + + self.game_flow = {} + + self.game_maps = None + # checks if there are any maps if there are puts them in self.game_maps + if game_maps is not None: + self.game_maps = game_maps + + +# stores objects in a sorted way +class Objects: + def __init__(self): + self.game_objects = [] + self.collision_objects = [] + self.moving_objects = [] + + # dunno if next att will be useful + # u add ids of objects u want to delete there is no func to delete for now + + self.objects_to_delete = [] + + self.values = { + "pos_to_del": [] + } + + def do_collisions(self, objects): + for obj in self.collision_objects: + collision(obj, objects) + + def take_out_trash(self, ids): + for trash in self.objects_to_delete: + ids.ids_to_remove.append(trash) + self.objects_to_delete = [] + + +# Id class just stores all ids of all objects +class Id: + all_ids = [] + ids_to_remove = [] + + def remove_by_id(self, objects): + for item in self.ids_to_remove: + for obj in objects.game_objects: + if obj.object_id == item: + g_index = objects.game_objects.index(obj) + if obj.moving: + m_index = objects.moving_objects.index(obj) + if obj.move.collisions: + c_index = objects.collision_objects.index(obj) + + del objects.collision_objects[c_index] + + del objects.moving_objects[m_index] + + del objects.game_objects[g_index] + + self.ids_to_remove = [] + + +class Scroll: + def __init__(self, scroll): + self.scroll = scroll + self.fade = 20 + self.safe_fade = 20 + self.in_progress = False + self.save_scroll = self.scroll + + def move_scroll(self, player, screen, which, space=20): + if which == "y" or which == "both": + self.scroll[1] += (player.rect.y - self.scroll[1] - (screen[1] / 2) + (player.size[1] / 2)) / space + self.scroll[1] = int(self.scroll[1]) + if which == "x" or which == "both": + self.scroll[0] += (player.rect.x - self.scroll[0] - (screen[0] / 2) + (player.size[0] / 2)) / space + self.scroll[0] = int(self.scroll[0]) + + def add_scroll(self, which, how_much, fade=None): + if fade is not None: + self.safe_fade = fade + if self.in_progress is False: + self.load_safe_fade() + self.save_scroll = self.scroll + self.in_progress = True + how_much[0] /= self.fade + how_much[1] /= self.fade + self.fade += 0.01 * self.fade + + if which == "x" or which == "both": + self.scroll[0] += how_much[0] + self.scroll[0] = round(self.scroll[0]) + + if which == "y" or which == "both": + self.scroll[1] += how_much[1] + self.scroll[1] = round(self.scroll[1]) + + def load_safe_fade(self): + self.fade = self.safe_fade + self.in_progress = False + + # next function is used for mouse scrolling mainly + def move_scroll_based_on_pos(self, pos, screen, which, space=20): + # id used for mouse u can first check mouse pos from mid to create non moving bubble in middle + if which == "y" or which == "both": + self.scroll[1] += (pos[1] - self.scroll[1] - (screen[1] / 2)) / space + self.scroll[1] = int(self.scroll[1]) + if which == "x" or which == "both": + self.scroll[0] += (pos[0] - self.scroll[0] - (screen[0] / 2)) / space + self.scroll[0] = int(self.scroll[0]) + + # for limiting scroll + @staticmethod + def scroll_lim(scroll, lim_min, lim_max): + # min lim has priority + + if lim_min[0] < lim_max[0]: + + if scroll.scroll[0] < lim_min[0]: + scroll.scroll[0] = lim_min[0] + + if scroll.scroll[0] > lim_max[0]: + scroll.scroll[0] = lim_max[0] + else: + scroll.scroll[0] = lim_min[0] + + if lim_min[1] < lim_max[1]: + if scroll.scroll[1] < lim_min[1]: + scroll.scroll[1] = lim_min[1] + + if scroll.scroll[1] > lim_max[1]: + scroll.scroll[1] = lim_max[1] + else: + scroll.scroll[1] = lim_min[1] + + +class Special: + def __init__(self, object_id): + self.object_id = object_id + self.stage = "" + self.attribute = "" + self.images = [] + self.attribute_0 = 0 + self.attribute_1 = 0 + self.attribute_2 = 0 + self.color_r = [0, 0, 0] + self.boolean_attribute = False + self.height = 0 + self.power = 0 + self.flip = False + self.scroll = False + self.fixed = False + + +# collisions class take care of collision funcs +# !!!!!!!!!! holds init for Object !!!!!!!!!!!!!!!!!!! +# class purely for inheritance +class Collisions(Id): + + def __init__(self, typeX, object_id, x_y, movement, direction, moving, size, special=None): + self.type = typeX + self.object_id = object_id + self.object_pos = x_y + self.movement = movement + self.direction = direction + self.moving = moving + self.size = size + self.dir_movement = [0.0, 0.0] + self.memory = [] + self.special = special + if self.object_id != self.all_ids: + self.all_ids.append(self.object_id) + else: + print("duplicate/ linked object") + + # giving it bonus classes + + if self.moving: + self.move = Moving_Object() + if self.special: + self.specific = Special(self.object_id) + + self.rect = pygame.Rect(self.object_pos[0], self.object_pos[1], self.size[0], self.size[1]) + + # theres a function for every type of collisions + # edit collisions here (I added some basic ones just so u can see) + # self if objects that it hit and obj is the object that hit smt + + # always add both side of collisions (ask who collided with whom) + + def hit_bottom(self, obj, objects): + if self.type == "solid": + if obj.type == "player": + obj.rect.bottom = self.rect.top + obj.object_pos = [obj.rect.x, obj.rect.y] + self.memory[0] -= 1 + if self.memory[0] == 0: + objects.objects_to_delete.append(self.object_id) + + opposite = obj.move.get_final_vector() + opp = [-opposite[0] * 0.4, -opposite[1] * 0.8] + obj.move.vectors.append(opp) + + def hit_top(self, obj, objects): + if self.type == "solid": + if obj.type == "player": + obj.rect.top = self.rect.bottom + obj.object_pos = [obj.rect.x, obj.rect.y] + self.memory[0] -= 1 + if self.memory[0] == 0: + objects.objects_to_delete.append(self.object_id) + + opposite = obj.move.get_final_vector() + opp = [-opposite[0] * 0.4, -opposite[1] * 0.8] + obj.move.vectors.append(opp) + + def hit_left(self, obj, objects): + if self.type == "solid": + if obj.type == "player": + obj.rect.left = self.rect.right + obj.object_pos = [obj.rect.x, obj.rect.y] + self.memory[0] -= 1 + if self.memory[0] == 0: + objects.objects_to_delete.append(self.object_id) + + opposite = obj.move.get_final_vector() + opp = [-opposite[0] * 0.4, -opposite[1] * 0.8] + obj.move.vectors.append(opp) + + def hit_right(self, obj, objects): + if self.type == "solid": + if obj.type == "player": + obj.rect.right = self.rect.left + obj.object_pos = [obj.rect.x, obj.rect.y] + self.memory[0] -= 1 + if self.memory[0] == 0: + objects.objects_to_delete.append(self.object_id) + + opposite = obj.move.get_final_vector() + opp = [-opposite[0] * 0.4, -opposite[1] * 0.8] + obj.move.vectors.append(opp) + + +# next 2 classes are for object class +class Moving_Object: + def __init__(self): + # consts + self.degree = 0.0174533 + self.pi = math.pi + self.half_pi = round(math.pi / 2, 6) + self.two_pi = round(math.pi * 2, 6) + self.three_halves_pi = round(3 * (math.pi / 2), 6) + self.speed = 1 + self.force = 3 + self.offset = 0 + self.vectors = [] + + self.forward = False + self.backwards = False + self.left = False + self.right = False + + self.collisions = False + + def move(self, dir_movement): + + movement = dir_movement + + return movement + + # dir is the angle the player is facing + def change_dir(self, direction, angle): + + new_vector = [0, 0] + + if self.left: + direction -= angle + if direction < 0: + direction += self.two_pi + new_vector[0] = round(math.cos(direction) * self.speed, 2) + new_vector[1] = round(math.sin(direction) * self.speed, 2) + self.vectors.append(new_vector) + elif self.right: + direction += angle + if direction > self.two_pi: + direction -= self.two_pi + new_vector[0] = round(math.cos(direction) * self.speed, 2) + new_vector[1] = round(math.sin(direction) * self.speed, 2) + self.vectors.append(new_vector) + elif self.forward: + new_vector[0] = round(math.cos(direction) * self.speed, 2) + new_vector[1] = round(math.sin(direction) * self.speed, 2) + self.vectors.append(new_vector) + + return direction, self.get_final_vector() + + # used for setting things before game loop + def set_start_dir_movement(self, direction, dir_movement): + dir_movement[0] = round(math.cos(direction + (self.offset * self.degree)) * self.speed, 2) + dir_movement[1] = round(math.sin(direction + (self.offset * self.degree)) * self.speed, 2) + return dir_movement + + def get_final_vector(self): + to_remove = [] + for i in range(len(self.vectors)-1): + self.vectors[i][0] *= 0.96 + self.vectors[i][1] *= 0.96 + if abs(self.vectors[i][0]) < 0.2 and abs(self.vectors[i][1]) < 0.2: + to_remove.append(self.vectors[i]) + + for item in to_remove: + self.vectors.remove(item) + + final_vector = [0, 0] + + for vector in self.vectors: + final_vector[0] += vector[0] + final_vector[1] += vector[1] + + return final_vector + + +class Object(Collisions): + def __init__(self, typeX, object_id, x_y, movement, direction, moving, size, special=None): + super().__init__(typeX, object_id, x_y, movement, direction, moving, size, special) + + def __str__(self): + return self.type + + # changes position along with the rect + def change_pos(self, x_y): + self.object_pos = x_y + self.rect = pygame.Rect(self.object_pos[0], self.object_pos[1], self.size[0], self.size[1]) + + +class Mouse: + + points = 0 + + def __init__(self, mouse_pos): + self.mouse_pos = mouse_pos + self.mouse_scroll = [0, 0] + + def update(self, Win_size, Default_size): + self.mouse_pos = pygame.mouse.get_pos() + self.mouse_pos = [((self.mouse_pos[0] - self.mouse_scroll[0]) * (Default_size[0] / Win_size[0])), + ((self.mouse_pos[1] - self.mouse_scroll[1]) * (Default_size[1] / Win_size[1]))] + + def get_scrolled(self, scroll): + scrolled_pos = [self.mouse_pos[0] + scroll.scroll[0], + self.mouse_pos[1] + scroll.scroll[1]] + return scrolled_pos + + +def distance_indicator(coords1, coords2): + x_distance = abs(coords1[0] - coords2[0]) + y_distance = abs(coords1[1] - coords2[1]) + distance = math.sqrt((x_distance ** 2) + (y_distance ** 2)) + return round(distance, 4) + + +def load_images(path, name, number_of_images, file_type=".png"): + images = [] + for i in range(number_of_images): + images.append(pygame.image.load("{}/{}{}{}".format(path, name, i, file_type)).convert()) + return images + + +def load_map(path): + f = open(path, "r") + data = f.read() + f.close() + data = data.split('\n') + product = [] + for line in data: + product.append(list(line)) + return product + + +# next func sorts object into objects class so the objects is stored where it should be +def sort(obj, objects): + objects.game_objects.append(obj) + + if obj.moving: + objects.moving_objects.append(obj) + + if obj.move.collisions: + objects.collision_objects.append(obj) + + +def find_collisions(obj, objects): + hit_list = [] + for element in objects.game_objects: + if element.object_id != obj.object_id: + if element.rect.colliderect(obj.rect): + hit_list.append(element) + return hit_list + + +def collision(obj, objects): + # collisions for left/right + obj.change_pos([obj.object_pos[0] + obj.movement[0], obj.object_pos[1]]) + hit_list = find_collisions(obj, objects) + for item in hit_list: + if obj.movement[0] > 0: + item.hit_right(obj, objects) + elif obj.movement[0] < 0: + item.hit_left(obj, objects) + + # collisions for top/bottom + obj.change_pos([obj.object_pos[0], obj.object_pos[1] + obj.movement[1]]) + hit_list = find_collisions(obj, objects) + for item in hit_list: + if obj.movement[1] > 0: + item.hit_bottom(obj, objects) + elif obj.movement[1] < 0: + item.hit_top(obj, objects) + + +# !!!!!!!!!!! config this function for every program !!!!!!!!!! +def load_objects(game_map, width, height, objects, game): + x, y = 0, 0 + for line in game_map: + for obj in line: + # this is just to be efficient normaly u can use elif and put another obj to another num + if obj == "1": + obj = Object("solid", game.custom_id_giver, [x, y], [0, 0], 0, False, [width, height]) + obj.memory.append(3) + sort(obj, objects) + game.custom_id_giver += 1 + x += width + y += height + x = 0 + + +def load_textures(objects, dictionary, display, scroll): + for object in objects.game_objects: + if object.special: + display.blit(object.specific.images[object.specific.attribute_0], + [object.object_pos[0] - scroll.scroll[0], object.object_pos[1] - scroll.scroll[1]]) + else: + display.blit(dictionary["{}".format(object.type)], + [object.object_pos[0] - scroll.scroll[0], object.object_pos[1] - scroll.scroll[1]]) + + +def load_sp_texture(objects, type_x, image): + for object in objects.special_objects: + if object.type == type_x: + object.attributes.images.append(image) + + +def load_bg(image, y_x, width, height, display): + pos = [0, 0] + for i in range(y_x[0]): + for j in range(y_x[1]): + display.blit(image, pos) + pos[0] += width + pos[0] = 0 + pos[1] += height + + +def find_angle_between_points(center, point): + dists = distances(center, point) + try: + angle = math.atan(dists[1] / dists[0]) + + if point[0] < center[0]: + if point[1] < center[1]: + return angle + math.pi + else: + return (math.pi / 2 - angle) + (math.pi / 2) + else: + if point[1] < center[1]: + return (math.pi / 2 - angle) + 3 * (math.pi / 2) + else: + return angle + except ZeroDivisionError: + return False + + +def distances(cords1, cords2): + return [abs(cords1[0] - cords2[0]), abs(cords1[1] - cords2[1])] + + +def average(*args): + sumX = 0 + for item in args: + sumX += item + return sumX/len(args) + + +def area_intersection_of_circles(points, radius_list): + try: + dist = distance_indicator(points[0], points[1]) + + alpha_cos = (pow(radius_list[1], 2) + pow(dist, 2) - pow(radius_list[0], 2)) / (2 * radius_list[1] * dist) + + alpha = math.acos(alpha_cos) + + beta_cos = (dist - alpha_cos * radius_list[1]) / radius_list[0] + + beta = math.acos(beta_cos) + + triangles = (alpha_cos * pow(radius_list[1], 2) * math.sin(alpha)) + ( + beta_cos * pow(radius_list[0], 2) * math.sin(beta)) + + arcs = ((math.pi * pow(radius_list[0], 2) * beta * 2) / math.tau) + ( + (math.pi * pow(radius_list[1], 2) * alpha * 2) / math.tau) + + return arcs - triangles + except: + return False diff --git a/sounds.py b/sounds.py new file mode 100644 index 0000000..005263b --- /dev/null +++ b/sounds.py @@ -0,0 +1,12 @@ +import pygame +from pygame.locals import * + +pygame.init() + + +def get_sounds(): + sounds = {"click": pygame.mixer.Sound("assets/sounds/click.wav"), + "remove": pygame.mixer.Sound("assets/sounds/remove.wav"), + "join": pygame.mixer.Sound("assets/sounds/join.wav")} + + return sounds diff --git a/widget_engine.py b/widget_engine.py new file mode 100644 index 0000000..dd99293 --- /dev/null +++ b/widget_engine.py @@ -0,0 +1,99 @@ +import pygame +import math + + +class Button: + + def __init__(self, pos, size, images, onclick=None, onclick_args=None, active=True): + self.pos = pos + self.size = size + self.rect = pygame.Rect(pos[0], pos[1], size[0], size[1]) + self.images = images + self.display_image = images["idle"] + self.on_click = onclick + self.oc_args = onclick_args + self.active = active + self.switch_images = None + self.name = "" + + def hover_check(self, mouse_pos): + if self.active: + if self.rect.collidepoint(mouse_pos): + self.display_image = self.images["hover"] + else: + self.display_image = self.images["idle"] + + def click_check(self, mouse_pos): + if self.active: + if self.rect.collidepoint(mouse_pos): + if self.on_click is not None: + self.on_click(self.oc_args) + + def blit(self, display, scroll=None): + if self.active: + if scroll is None: + display.blit(self.display_image, self.pos) + else: + display.blit(self.display_image, [self.pos[0] - scroll.scroll[0], + self.pos[1] - scroll.scroll[1]]) + + def switch(self, mouse_pos): + self.switch_images, self.images = self.images, self.switch_images + self.hover_check(mouse_pos) + + def move(self, movement): + self.pos = [self.pos[0] + movement[0], + self.pos[1] + movement[1]] + self.rect = pygame.Rect(self.pos[0], self.pos[1], self.size[0], self.size[1]) + + +class CircleButton: + def __init__(self, size, pos, images, onclick=None, onclick_args=None, active=True): + self.pos = pos + self.size = size + self.center = [pos[0] + (size/2), pos[1] + (size/2)] + self.radius = size/2 + self.images = images + self.display_image = images["idle"] + self.on_click = onclick + self.oc_args = onclick_args + self.active = active + self.switch_images = None + self.name = "" + + def hover_check(self, mouse_pos): + if self.active: + if distance_indicator(self.center, mouse_pos) <= self.radius: + self.display_image = self.images["hover"] + else: + self.display_image = self.images["idle"] + + def click_check(self, mouse_pos): + if self.active: + if distance_indicator(self.center, mouse_pos) <= self.radius: + if self.on_click is not None: + self.on_click(self.oc_args) + + def blit(self, display, scroll=None): + if self.active: + if scroll is None: + display.blit(self.display_image, self.pos) + else: + display.blit(self.display_image, [self.pos[0] - scroll.scroll[0], + self.pos[1] - scroll.scroll[1]]) + + def switch(self, mouse_pos): + self.switch_images, self.images = self.images, self.switch_images + self.hover_check(mouse_pos) + + def move(self, movement): + self.pos = [self.pos[0] + movement[0], + self.pos[1] + movement[1]] + self.center = [self.pos[0] + (self.size / 2), self.pos[1] + (self.size / 2)] + + +def distance_indicator(cords1, cords2): + x_distance = abs(cords1[0] - cords2[0]) + y_distance = abs(cords1[1] - cords2[1]) + distance = round(math.sqrt((x_distance**2) + (y_distance**2))) + return distance