1382 lines
54 KiB
Python
1382 lines
54 KiB
Python
import win32api
|
|
import win32con
|
|
import win32gui
|
|
import pygame
|
|
import ctypes
|
|
import json
|
|
from tkinter import Tk, filedialog
|
|
import numpy as np
|
|
import os
|
|
|
|
script_dir = os.path.dirname(os.path.abspath(__file__))
|
|
|
|
# INITIALIZATION
|
|
pygame.init()
|
|
FPS = 60
|
|
|
|
IMAGES_PATH = "images/albums/"
|
|
SIZE_PATH = "images/assets/"
|
|
|
|
DATA_FILE = "main_data.json"
|
|
PUZZLE_FILE = "main_puzzle.json"
|
|
SETTINGS_FILE = "main_settings.json"
|
|
STATS_FILE = "main_stats.json"
|
|
|
|
black = (0, 0, 0)
|
|
white = (255, 255, 255)
|
|
transparent = (255, 0, 128)
|
|
|
|
# Data loading and saving
|
|
def load_data(data):
|
|
try:
|
|
with open(data, "r") as file:
|
|
current_data = json.load(file)
|
|
except (FileNotFoundError, json.JSONDecodeError):
|
|
print("Something went wrong with file loading")
|
|
return current_data
|
|
|
|
|
|
storage = load_data(DATA_FILE)
|
|
puzzle_data = load_data(PUZZLE_FILE)
|
|
settings_data = load_data(SETTINGS_FILE)
|
|
stats_data = load_data(STATS_FILE)
|
|
|
|
gui = "gui color"
|
|
music = "music"
|
|
sound = "sound"
|
|
style = "style"
|
|
current_size = settings_data["size"]
|
|
current_style = settings_data["style"]
|
|
|
|
# Configuration
|
|
SIZE_CHOICES = ["small", "medium", "big"]
|
|
STYLE_CHOICES = ["classic", "original", "dark"]
|
|
GRID_CHOICES = ["4x4", "3x3"]
|
|
MUSIC_CHOICES = ["funky leap", "serenity", "sunny day", "epec unf"]
|
|
SOUND_CHOICES = ["puzzle", "wood", "metal pipe", "lego breaking"]
|
|
DISPLAY_CHOICES = ["time", "moves", "none"]
|
|
LANGUAGE_CHOICES = ["english", "russian", "latvian"]
|
|
FONTS = ["msreferencesansserif", "arial", "bahnschrift"]
|
|
|
|
if current_style == STYLE_CHOICES[0]:
|
|
FONT = FONTS[0]
|
|
STYLE_PATH = "1_"
|
|
text_color = (1, 35, 61)
|
|
oposite_color = white
|
|
elif current_style == STYLE_CHOICES[1]:
|
|
FONT = FONTS[1]
|
|
STYLE_PATH = "2_"
|
|
text_color = white
|
|
oposite_color = (70, 35, 0)
|
|
else:
|
|
FONT = FONTS[2]
|
|
STYLE_PATH = "3_"
|
|
text_color = white
|
|
oposite_color = text_color
|
|
|
|
if current_size == SIZE_CHOICES[0]:
|
|
WIDTH, HEIGHT = 144, 154
|
|
INT = 0
|
|
if FONT == FONTS[0]:
|
|
FONT_SIZE = 11
|
|
FONT_SIZE2 = 10
|
|
elif FONT == FONTS[1]:
|
|
FONT_SIZE = 12
|
|
FONT_SIZE2 = 11
|
|
elif FONT == FONTS[2]:
|
|
FONT_SIZE = 11
|
|
FONT_SIZE2 = 10
|
|
puzzle_size = (128, 128)
|
|
# --> puzzle_size[2] and puzzle_size[3] (128) - puzzle size (?x? px)
|
|
non_movable_area = pygame.Rect(8, 19, 128, 128)
|
|
# --> scaled_size[0] (8) - x position for image in game mode
|
|
# --> scaled_size[1] (19) - y position for image
|
|
# --> scaled_size[2] and scaled_size[3] (128) - image size (?x? px)
|
|
timer_area = pygame.Rect(22, 1, 55, 12)
|
|
# --> timer_area[0] (22) - position x for timer area in game mode
|
|
# --> timer_area[1] (1) - position y for timer area
|
|
# --> timer_area[2] (55) - width of timer area
|
|
# --> timer_area[3] (12) - heigth of timer area
|
|
win_area = pygame.Rect(74, 78, 128, 128)
|
|
# --> win_area[0] (74) - position x for "You win !!" text area in game mode
|
|
# --> win_area[1] (78) - position y for "You win !!" text area
|
|
# --> win_area[2] (128) - width of "You win !!" text area
|
|
# --> win_area[3] (128) - heigth of "You win !!" text area
|
|
size = [20, 25, 116, 15]
|
|
cons = [15, 195, 12, 3, 9]
|
|
# size and cons (constants) are too complicated, I cant remember what each element was, sorry :(
|
|
scaled_size = (18, 21, 108, 108)
|
|
# --> scaled_size[0] (18) - x position for image in select mode
|
|
# --> scaled_size[1] (21) - y position for image
|
|
# --> scaled_size[2] and scaled_size[3] (108) - image size (?x? px)
|
|
area = (72, 142)
|
|
# --> area[0] (72) - x position for text "? of ?" in select mode
|
|
# --> area[1] (142) - y position for text "? of ?"
|
|
spaces = [14, 18, 20, 30, 8]
|
|
# --> spaces[0] (14) - space between setting name and setting in settings mode, increasing
|
|
# --> spaces[1] (18) - space between non_movable_area edge, decreasing
|
|
# --> spaces[2] (20) - space between setting names, increasing
|
|
# --> spaces[3] (30) - space between slider and non_movable_area edge, decreasing
|
|
# --> spaces[4] (8) - space between setting names and non_movable_area edge, increasing
|
|
f_line = [92, 30, 80, 12]
|
|
s_line = [92, 45, 80, 12]
|
|
s_button = [175, 30, 10, 16]
|
|
# for list in f_line, s_line and s_button:
|
|
# --> list[0] (135, 135, 218) - position x for element in spritesheet
|
|
# --> list[1] (38, 53, 38) - position y for element in spritesheet
|
|
# --> list[2] (80, 80, 10) - width of element in settings mode
|
|
# --> list[3] (12, 12, 16) - heigth of element
|
|
arrow1 = (207, 0, 10, 13)
|
|
arrow2 = (220, 0, 10, 13)
|
|
spaces2 = [4, 5, 2, 5, 60, 42]
|
|
spaces3 = [10, 20, 23]
|
|
|
|
elif current_size == SIZE_CHOICES[1]:
|
|
WIDTH, HEIGHT = 202, 216
|
|
INT = 1
|
|
if FONT == FONTS[0]:
|
|
FONT_SIZE = 15
|
|
FONT_SIZE2 = 13
|
|
elif FONT == FONTS[1]:
|
|
FONT_SIZE = 17
|
|
FONT_SIZE2 = 15
|
|
elif FONT == FONTS[2]:
|
|
FONT_SIZE = 16
|
|
FONT_SIZE2 = 14
|
|
puzzle_size = (180, 180)
|
|
non_movable_area = pygame.Rect(11, 26, 180, 180)
|
|
timer_area = pygame.Rect(27, 2, 86, 16)
|
|
win_area = pygame.Rect(104, 108, 180, 180)
|
|
size = [30, 35, 161, 25]
|
|
cons = [25, 247, 18, 3, 13]
|
|
scaled_size = (22, 26, 158, 158)
|
|
area = (100, 200)
|
|
spaces = [14, 30, 30, 40, 8]
|
|
f_line = [135, 38, 100, 16]
|
|
s_line = [135, 58, 100, 16]
|
|
s_button = [238, 38, 12, 20]
|
|
arrow1 = (263, 0, 13, 17)
|
|
arrow2 = (280, 0, 13, 17)
|
|
spaces2 = [4, 3, 3, 5, 60, 42]
|
|
spaces3 = [10, 20, 23]
|
|
if current_style == STYLE_CHOICES[2]:
|
|
spaces2[1] = 2
|
|
|
|
else:
|
|
WIDTH, HEIGHT = 396, 425
|
|
INT = 2
|
|
if FONT == FONTS[0]:
|
|
FONT_SIZE = 24
|
|
FONT_SIZE2 = 21
|
|
elif FONT == FONTS[1]:
|
|
FONT_SIZE = 22
|
|
FONT_SIZE2 = 19
|
|
elif FONT == FONTS[2]:
|
|
FONT_SIZE = 26
|
|
FONT_SIZE2 = 22
|
|
puzzle_size = (360, 360)
|
|
non_movable_area = pygame.Rect(18, 48, 360, 360)
|
|
timer_area = pygame.Rect(49, 5, 86, 32)
|
|
win_area = pygame.Rect(200, 214, 360, 360)
|
|
size = [50, 58, 328, 35]
|
|
cons = [35, 455, 26, 10, 26]
|
|
scaled_size = (43, 48, 310, 310)
|
|
area = (197, 390)
|
|
spaces = [24, 34, 50, 84, 8]
|
|
f_line = [276, 70, 200, 32]
|
|
s_line = [276, 105, 200, 32]
|
|
s_button = [479, 70, 24, 40]
|
|
arrow1 = (485, 0, 21, 32)
|
|
arrow2 = (509, 0, 21, 32)
|
|
spaces2 = (4, 0, 2, 5, 60, 80)
|
|
spaces3 = [50, 40, 43]
|
|
if current_style == STYLE_CHOICES[2]:
|
|
cons[3] = 8
|
|
|
|
puzzle_version = settings_data["grid"]
|
|
|
|
# Seting screen and other stuff
|
|
programIcon = pygame.image.load('icon.png')
|
|
pygame.display.set_icon(programIcon)
|
|
pygame.display.set_caption('Picture Puzzle')
|
|
screen_info = pygame.display.Info()
|
|
window_coords = [(screen_info.current_w - WIDTH) // 2, (screen_info.current_h - HEIGHT) // 2]
|
|
screen = pygame.display.set_mode((WIDTH, HEIGHT), pygame.NOFRAME)
|
|
clock = pygame.time.Clock()
|
|
hwnd = pygame.display.get_wm_info()["window"]
|
|
|
|
# Sprites generation
|
|
class SpriteSheet:
|
|
def __init__(self, sheet_path):
|
|
self.sheet = pygame.image.load(sheet_path).convert_alpha()
|
|
|
|
def get_sprite(self, position, width, height, color_key=None):
|
|
image = pygame.Surface((width, height), pygame.SRCALPHA).convert_alpha()
|
|
image.blit(self.sheet, (0, 0), (position[0], position[1], width, height))
|
|
|
|
if color_key:
|
|
for x in range(width):
|
|
for y in range(height):
|
|
if image.get_at((x, y))[:3] == color_key:
|
|
image.set_at((x, y), (0, 0, 0, 0))
|
|
|
|
return image
|
|
|
|
def set_hue(self, hue):
|
|
for x in range(self.sheet.get_width()):
|
|
for y in range(self.sheet.get_height()):
|
|
color = self.sheet.get_at((x, y))
|
|
|
|
h, s, v, a = pygame.Color(color)
|
|
h = (h + hue) % 360
|
|
new_color = pygame.Color(0, 0, 0, 0)
|
|
new_color.hsva = (h, s, v, a)
|
|
|
|
self.sheet.set_at((x, y), new_color)
|
|
|
|
|
|
sprite_sheet = SpriteSheet(SIZE_PATH + str(INT) + STYLE_PATH + "sprites.png")
|
|
|
|
|
|
class Image:
|
|
def __init__(self, sprite_position, window_position, size):
|
|
self.sprite_sheet = sprite_sheet
|
|
self.sprite_position = sprite_position
|
|
self.window_position = window_position
|
|
self.size = size
|
|
self.update_image()
|
|
|
|
def update_image(self):
|
|
self.img = self.sprite_sheet.get_sprite(self.sprite_position, *self.size)
|
|
self.rect = self.img.get_rect(topleft=self.window_position)
|
|
|
|
def draw(self, screen):
|
|
screen.blit(self.img, self.rect)
|
|
|
|
|
|
class Button:
|
|
def __init__(self, sprite_position, window_position, size, popup_text=None):
|
|
self.sprite_sheet = sprite_sheet
|
|
self.sprite_position = sprite_position
|
|
self.window_position = window_position
|
|
self.size = size
|
|
self.popup_text = popup_text
|
|
self.hovered = False
|
|
self.disabled = False
|
|
self.popup_timer = 0
|
|
self.popup_duration = 3000
|
|
self.popup_surface = None
|
|
self.update_images()
|
|
|
|
def update_images(self):
|
|
self.img = self.sprite_sheet.get_sprite(self.sprite_position, *self.size)
|
|
hover_position = (
|
|
self.sprite_position[0],
|
|
self.sprite_position[1] + 3 + self.size[1]
|
|
)
|
|
self.hover_image = self.sprite_sheet.get_sprite(hover_position, *self.size)
|
|
|
|
self.rect = self.img.get_rect(topleft=self.window_position)
|
|
self.mask = pygame.mask.from_surface(self.img)
|
|
|
|
def is_hovered(self, mouse_pos):
|
|
return self.rect.collidepoint(mouse_pos) and self.mask.get_at(
|
|
(mouse_pos[0] - self.rect.x, mouse_pos[1] - self.rect.y)
|
|
)
|
|
|
|
def disable(self):
|
|
self.disabled = True
|
|
|
|
def enable(self):
|
|
self.disabled = False
|
|
|
|
def update(self, mouse_pos):
|
|
self.hovered = self.is_hovered(mouse_pos)
|
|
|
|
if self.popup_text is not None and self.hovered:
|
|
self.popup_timer += FPS
|
|
if self.popup_timer >= self.popup_duration:
|
|
self.show_popup(mouse_pos)
|
|
else:
|
|
self.popup_timer = 0
|
|
self.popup_surface = None
|
|
|
|
def show_popup(self, mouse_pos):
|
|
font = pygame.font.Font(None, 24)
|
|
text_surface = font.render(self.popup_text, True, (0, 0, 0), (255, 255, 225))
|
|
text_rect = text_surface.get_rect(center=mouse_pos)
|
|
|
|
popup_width = text_rect.width + 14
|
|
popup_height = text_rect.height + 14
|
|
|
|
popup_x = min(mouse_pos[0], WIDTH - popup_width - 1)
|
|
popup_y = min(mouse_pos[1] + 20, HEIGHT - popup_height - 1)
|
|
|
|
self.popup_surface = pygame.Surface((popup_width, popup_height), pygame.SRCALPHA)
|
|
pygame.draw.rect(self.popup_surface, (255, 255, 255), self.popup_surface.get_rect())
|
|
pygame.draw.rect(self.popup_surface, (0, 0, 0), self.popup_surface.get_rect(), 1)
|
|
self.popup_surface.blit(text_surface, (7, 7))
|
|
|
|
self.popup_rect = self.popup_surface.get_rect(topleft=(popup_x, popup_y))
|
|
|
|
def draw(self, screen):
|
|
if not self.disabled:
|
|
screen.blit(self.hover_image if self.hovered else self.img, self.rect)
|
|
if self.popup_surface:
|
|
screen.blit(self.popup_surface, self.popup_rect)
|
|
else:
|
|
screen.blit(self.img, self.rect)
|
|
|
|
|
|
|
|
# Ability to move window
|
|
def move_win(coordinates):
|
|
hwnd = pygame.display.get_wm_info()["window"]
|
|
w, h = pygame.display.get_surface().get_size()
|
|
win32gui.MoveWindow(hwnd, coordinates[0], coordinates[1], w, h, True)
|
|
set_shadow_style(hwnd)
|
|
|
|
# Generating shadow
|
|
def set_shadow_style(hwnd):
|
|
try:
|
|
DWMWA_NCRENDERING_POLICY = 2
|
|
DWMNCRP_ENABLED = 2
|
|
|
|
ctypes.windll.dwmapi.DwmSetWindowAttribute(
|
|
hwnd,
|
|
DWMWA_NCRENDERING_POLICY,
|
|
ctypes.byref(ctypes.c_uint(DWMNCRP_ENABLED)),
|
|
ctypes.sizeof(ctypes.c_uint),
|
|
)
|
|
MARGINS = (ctypes.c_int * 4)(-1, -1, -1, -1)
|
|
ctypes.windll.dwmapi.DwmExtendFrameIntoClientArea(hwnd, ctypes.byref(MARGINS))
|
|
|
|
except Exception as e:
|
|
print(e)
|
|
|
|
# Text render
|
|
def render_text(text, position, screen, color=text_color, centered=False):
|
|
font = pygame.font.SysFont(FONT, FONT_SIZE)
|
|
text_render = font.render(text, True, color)
|
|
|
|
if centered:
|
|
text_rect = text_render.get_rect(center=position)
|
|
screen.blit(text_render, text_rect.topleft)
|
|
else:
|
|
screen.blit(text_render, position)
|
|
|
|
def render_settings_text(text, position, screen, value=None,color=text_color):
|
|
font = pygame.font.SysFont(FONT, FONT_SIZE2-1, italic=True)
|
|
if value is not None:
|
|
text_render = font.render(f"{text}: {value}", True, color)
|
|
else:
|
|
text_render = font.render(text, True, color)
|
|
|
|
text_rect = text_render.get_rect(center=position)
|
|
screen.blit(text_render, text_rect.topleft)
|
|
|
|
# MENU MODE
|
|
def browse():
|
|
root = Tk()
|
|
root.withdraw()
|
|
file_path = filedialog.askopenfilename(filetypes=[("Image files", "*.png;*.jpg;*.jpeg;*.gif")])
|
|
root.destroy()
|
|
return file_path
|
|
|
|
|
|
quick_game_activated = False
|
|
selected_file = None
|
|
|
|
|
|
def quick_game():
|
|
global selected_file, puzzle_image, puzzle_pieces, shuffled_pieces, empty_position, quick_game_activated, original_image
|
|
quick_game_activated = True
|
|
try:
|
|
selected_file = browse()
|
|
if selected_file is not None:
|
|
puzzle_image = pygame.image.load(selected_file)
|
|
original_image = pygame.transform.scale(puzzle_image, puzzle_size)
|
|
puzzle_pieces = cut_image(original_image)
|
|
shuffled_pieces = shuffle_pieces(puzzle_pieces)
|
|
empty_position = (len(puzzle_matrix) - 1, len(puzzle_matrix[0]) - 1)
|
|
quick_game_activated = False
|
|
except FileNotFoundError:
|
|
print("No file selected")
|
|
|
|
|
|
def render_albums():
|
|
global current_mode, album_name
|
|
current_hovered_index = -1
|
|
size_to_y = {"small": 25, "medium": 35, "big": 65}
|
|
y = size_to_y[current_size]
|
|
|
|
for hover_index, album in enumerate(storage["albums"]):
|
|
render_text(album, (size[1], y), screen)
|
|
album_rect = pygame.Rect(size[0], y, size[2], size[3])
|
|
album_hover = album_rect.collidepoint(pygame.mouse.get_pos())
|
|
|
|
if album_hover:
|
|
current_hovered_index = hover_index
|
|
|
|
if album_hover and pygame.mouse.get_pressed()[0]:
|
|
album_name = album
|
|
if album_name != album:
|
|
album_name = None
|
|
|
|
if album_name is not None:
|
|
current_mode = "select"
|
|
|
|
return album
|
|
|
|
y += cons[0]
|
|
|
|
if current_hovered_index == -1:
|
|
current_hovered_index = 0
|
|
arrow_image = Image((cons[1], 0), (cons[2], size[1] + cons[0] * current_hovered_index + cons[3]), (cons[4], cons[4]))
|
|
arrow_image.draw(screen)
|
|
|
|
|
|
def album_deletion():
|
|
# Not finished yet
|
|
pass
|
|
|
|
|
|
def album_creation():
|
|
# Not finished yet
|
|
pass
|
|
|
|
# SELECT MODE
|
|
class ImageRender:
|
|
def __init__(self, album_name, initial_index=0):
|
|
self.album_name = album_name
|
|
self.index = initial_index
|
|
self.image_load()
|
|
|
|
def image_load(self):
|
|
global current_img, current_album
|
|
self.scaled_size = scaled_size
|
|
image_list = storage["albums"][self.album_name]
|
|
image_names = list(image_list.keys())
|
|
|
|
if not image_names:
|
|
return
|
|
|
|
self.current = image_names[self.index]
|
|
selected_image = pygame.image.load(IMAGES_PATH + self.album_name + "/" + self.current)
|
|
self.scaled_image = pygame.transform.scale(selected_image, (self.scaled_size[2], self.scaled_size[3]))
|
|
current_img = self.current
|
|
current_album = self.album_name
|
|
self.num_images = len(storage["albums"][self.album_name])
|
|
self.current_text = f"{self.index+1} of {self.num_images}"
|
|
|
|
def render(self, screen):
|
|
screen.blit(self.scaled_image, (self.scaled_size[0], self.scaled_size[1]))
|
|
render_text(self.current_text, (area[0], area[1]), screen, oposite_color, True)
|
|
pygame.display.flip()
|
|
|
|
def update_index(self, increment):
|
|
self.index += increment
|
|
if self.index >= self.num_images:
|
|
self.index = 0
|
|
elif self.index < 0:
|
|
self.index = self.num_images - 1
|
|
self.image_load()
|
|
|
|
@staticmethod
|
|
def image_save(selected_album, current_img):
|
|
for album in storage["albums"].values():
|
|
for image in album.values():
|
|
image[0] = 0
|
|
chosen = storage["albums"][selected_album][current_img]
|
|
chosen[0] = 1
|
|
with open(DATA_FILE, 'w') as data_file:
|
|
data_file.write(json.dumps(storage, indent=3))
|
|
image_rect = pygame.Rect(scaled_size)
|
|
return image_rect
|
|
|
|
def image_deletion(self):
|
|
# Not finished yet
|
|
pass
|
|
|
|
def image_creation(self):
|
|
# Not finished yet
|
|
pass
|
|
|
|
|
|
def reload_chosen():
|
|
for album in storage["albums"].values():
|
|
for image in album.values():
|
|
image[0] = 0
|
|
with open(DATA_FILE, 'w') as data_file:
|
|
data_file.write(json.dumps(storage, indent=3))
|
|
|
|
# GAME MODE
|
|
def timer(elapsed_time):
|
|
hours = elapsed_time // 3600
|
|
minutes = (elapsed_time % 3600) // 60
|
|
seconds = elapsed_time % 60
|
|
timer_text = f"{hours:02d}:{minutes:02d}:{seconds:02d}"
|
|
return timer_text
|
|
|
|
# Puzzle loading and running logic
|
|
def cut_image(image):
|
|
piece_width = puzzle_size[0] // len(puzzle_matrix[0])
|
|
piece_height = puzzle_size[1] // len(puzzle_matrix)
|
|
pieces = []
|
|
for row in range(len(puzzle_matrix)):
|
|
for col in range(len(puzzle_matrix[0])):
|
|
left, top = col * piece_width, row * piece_height
|
|
piece_rect = pygame.Rect(left, top, piece_width, piece_height)
|
|
piece_image = image.subsurface(piece_rect)
|
|
|
|
piece_layer = pygame.Surface((piece_width, piece_height), pygame.SRCALPHA)
|
|
|
|
if puzzle_version == GRID_CHOICES[0]:
|
|
layer_path = pygame.image.load("images/assets/piece32.png").convert_alpha()
|
|
else:
|
|
layer_path = pygame.image.load("images/assets/piece45.png" if current_size == SIZE_CHOICES[0] else "images/assets/piece60.png").convert_alpha()
|
|
layer_path = pygame.transform.scale(layer_path, (piece_width, piece_height))
|
|
layer_path.set_colorkey((0, 0, 0))
|
|
|
|
piece_layer.blit(piece_image, (0, 0))
|
|
piece_layer.blit(layer_path, (0, 0))
|
|
|
|
pieces.append((piece_layer, (left, top)))
|
|
|
|
return pieces
|
|
|
|
|
|
def load_puzzle():
|
|
global puzzle_matrix, original_matrix, empty_position, puzzle_pieces, shuffled_pieces, puzzle_size, directions, cell_size, number_of_rows, original_image
|
|
|
|
puzzle_version = settings_data["grid"]
|
|
image_path = puzzle_data[f'current {puzzle_version}']['selected_image']
|
|
path_parts = image_path.split('/')
|
|
real_image_path = os.path.abspath(os.path.join(script_dir, *path_parts))
|
|
puzzle_image = pygame.image.load(real_image_path)
|
|
|
|
directions = {"up": [4, 8, 12],
|
|
"down": [],
|
|
"left": [13, 14, 15],
|
|
"right": []}
|
|
|
|
puzzle_matrix = np.array(puzzle_data[f'current {puzzle_version}']['matrix'])
|
|
original_matrix = np.array(puzzle_data[f'puzzle {puzzle_version}'])
|
|
empty_position = np.nonzero(puzzle_matrix == 0)
|
|
number_of_rows = int(puzzle_version[0])
|
|
cell_size = puzzle_size[0] // number_of_rows
|
|
original_image = pygame.transform.scale(puzzle_image, puzzle_size)
|
|
|
|
puzzle_pieces = cut_image(original_image)
|
|
if np.max(puzzle_matrix) == 0:
|
|
shuffled_pieces = shuffle_pieces(puzzle_pieces)
|
|
empty_position = (len(puzzle_matrix) - 1, len(puzzle_matrix[0]) - 1)
|
|
|
|
|
|
load_puzzle()
|
|
|
|
|
|
def shuffle_pieces(puzzle_pieces):
|
|
global puzzle_matrix
|
|
numbers = list(range(len(puzzle_pieces)))
|
|
np.random.shuffle(numbers)
|
|
puzzle_matrix = np.array(numbers).reshape(puzzle_matrix.shape)
|
|
save_puzzle_state()
|
|
update_directions()
|
|
|
|
for i, (_, (left, top)) in enumerate(puzzle_pieces):
|
|
row, col = divmod(numbers[i], len(puzzle_matrix[0]))
|
|
puzzle_pieces[i] = (puzzle_pieces[i][0], (col * cell_size, row * cell_size))
|
|
|
|
return puzzle_pieces
|
|
|
|
|
|
def move_pieces(clicked_row, clicked_col):
|
|
global puzzle_matrix
|
|
|
|
zero_positions = np.nonzero(puzzle_matrix == 0)
|
|
zero_row, zero_col = zero_positions[0][0], zero_positions[1][0]
|
|
|
|
if clicked_row == zero_row:
|
|
if clicked_col < zero_col:
|
|
direction = "right"
|
|
else:
|
|
direction = "left"
|
|
elif clicked_col == zero_col:
|
|
if clicked_row < zero_row:
|
|
direction = "down"
|
|
else:
|
|
direction = "up"
|
|
else:
|
|
return
|
|
|
|
if direction in ["up", "down"]:
|
|
pieces_to_move = puzzle_matrix[min(clicked_row, zero_row):max(clicked_row, zero_row) + 1, clicked_col]
|
|
else:
|
|
pieces_to_move = puzzle_matrix[clicked_row, min(clicked_col, zero_col):max(clicked_col, zero_col) + 1]
|
|
|
|
if direction == "right":
|
|
puzzle_matrix[clicked_row, clicked_col:zero_col + 1] = np.roll(puzzle_matrix[clicked_row, clicked_col:zero_col + 1], 1)
|
|
elif direction == "left":
|
|
puzzle_matrix[clicked_row, zero_col:clicked_col + 1] = np.roll(puzzle_matrix[clicked_row, zero_col:clicked_col + 1], -1)
|
|
elif direction == "down":
|
|
puzzle_matrix[min(clicked_row, zero_row):max(clicked_row, zero_row) + 1, clicked_col] = np.roll(pieces_to_move, 1)
|
|
elif direction == "up":
|
|
puzzle_matrix[min(clicked_row, zero_row):max(clicked_row, zero_row) + 1, clicked_col] = np.roll(pieces_to_move, -1)[:pieces_to_move.shape[0]]
|
|
save_puzzle_state()
|
|
update_directions()
|
|
return pieces_to_move.any()
|
|
|
|
|
|
def update_directions():
|
|
global directions, puzzle_matrix
|
|
zero_positions = np.nonzero(puzzle_matrix == 0)
|
|
zero_row, zero_col = zero_positions[0][0], zero_positions[1][0]
|
|
|
|
directions = {
|
|
"up": puzzle_matrix[zero_row + 1:, zero_col].flatten().tolist() if zero_row < number_of_rows - 1 else [],
|
|
"down": puzzle_matrix[:zero_row, zero_col].flatten().tolist() if zero_row > 0 else [],
|
|
"left": puzzle_matrix[zero_row, zero_col + 1:].flatten().tolist() if zero_col < number_of_rows - 1 else [],
|
|
"right": puzzle_matrix[zero_row, :zero_col].flatten().tolist() if zero_col > 0 else [],
|
|
}
|
|
|
|
|
|
update_directions()
|
|
|
|
|
|
def save_puzzle_state():
|
|
puzzle_matrix_list = puzzle_matrix.tolist()
|
|
puzzle_data[f'current {puzzle_version}']['matrix'] = puzzle_matrix_list
|
|
with open(PUZZLE_FILE, 'w') as puzzle_file:
|
|
json.dump(puzzle_data, puzzle_file)
|
|
|
|
|
|
def update_puzzle_data(completed, sorted, current_time, current_moves, real_time):
|
|
path_components = puzzle_data[f'current {puzzle_version}']['selected_image'].split('/')[2:]
|
|
current_data = storage["albums"][path_components[0]]
|
|
for component in path_components[1:]:
|
|
if component in current_data:
|
|
current_data = current_data[component]
|
|
else:
|
|
print("Something went wrong while updating puzzle data")
|
|
return
|
|
|
|
initial_chosen, initial_completed, initial_sorted, initial_moves, initial_time, _, _ = current_data
|
|
initial_completed = int(completed)
|
|
initial_sorted = int(sorted)
|
|
initial_moves += current_moves
|
|
|
|
initial_time_seconds = sum(int(x) * 60**i for i, x in enumerate(reversed(initial_time.split(":"))))
|
|
total_time_seconds = initial_time_seconds + real_time
|
|
hours, remainder = divmod(total_time_seconds, 3600)
|
|
minutes, seconds = divmod(remainder, 60)
|
|
formatted_time = f"{hours:02d}:{minutes:02d}:{seconds:02d}"
|
|
|
|
current_data = [initial_chosen, initial_completed, initial_sorted, initial_moves, formatted_time, current_time, current_moves]
|
|
storage["albums"][path_components[0]][path_components[1]] = current_data
|
|
with open(DATA_FILE, 'w') as data_file:
|
|
data_file.write(json.dumps(storage, indent=3))
|
|
|
|
# SETTINGS MODE
|
|
scroll_offset = 0
|
|
settings_x_position = puzzle_size[0]
|
|
total_settings_height = len(settings_data) * (cons[0] + spaces[1])
|
|
max_scroll_offset = max(0, total_settings_height - non_movable_area.height)
|
|
active_sliders = {gui: False, music: False, sound: False}
|
|
gap = (s_button[2]/2)-(s_line[3]/2)-1
|
|
|
|
|
|
def slider(position, value_range, value, active_slider, setting):
|
|
second_x = value / value_range * s_line[2]
|
|
value_pos = second_x - s_button[2] / 2
|
|
first_line = Image((f_line[0], f_line[1]), (position[0], position[1]), (f_line[2], f_line[3]))
|
|
second_line = Image((s_line[0], s_line[1]), (position[0], position[1]), (second_x, s_line[3]))
|
|
slider_button = Button((s_button[0], s_button[1]), (position[0] + value_pos, position[1] + gap), (s_button[2], s_button[3]))
|
|
if active_slider[setting] and pygame.mouse.get_pressed()[0]:
|
|
mouse_pos = pygame.mouse.get_pos()
|
|
if slider_button.rect.inflate(100, 25).collidepoint(mouse_pos):
|
|
mouse_x = mouse_pos[0]
|
|
min_x = position[0] - s_button[2] / 2
|
|
max_x = position[0] + s_line[2] - s_button[2] / 2
|
|
new_value_pos = min(max(mouse_x - s_button[2] / 2, min_x), max_x)
|
|
new_value = int(((new_value_pos - min_x) / (max_x - min_x)) * value_range)
|
|
second_x = new_value_pos - position[0] + s_button[2] / 2
|
|
slider_button.rect.x = new_value_pos
|
|
value = new_value
|
|
data_set(setting, value)
|
|
|
|
return first_line, second_line, slider_button
|
|
|
|
|
|
active_selects = {"size": False, "style": False, "grid": False, "music_version": False, "sound_version": False}
|
|
def arrows(position, setting, value, choices, event_list):
|
|
index = choices.index(value)
|
|
arrow_width = arrow1[2]
|
|
|
|
font = pygame.font.SysFont(FONT, FONT_SIZE)
|
|
text_render = font.render(value, True, text_color)
|
|
text_rect = text_render.get_rect()
|
|
text_x = position[0] + (arrow_width + spaces2[0]) / 2 - text_rect.width / 2 + spaces2[5]
|
|
text_y = position[1] - spaces2[1]
|
|
|
|
value_text = screen.blit(text_render, (text_x, text_y))
|
|
|
|
first_arrow = Button((arrow1[0], arrow1[1]), (text_x - arrow_width - spaces2[0], position[1]), (arrow_width, arrow1[3]))
|
|
second_arrow_x = text_x + text_rect.width + spaces2[2]
|
|
second_arrow = Button((arrow2[0], arrow2[1]), (second_arrow_x , position[1]), (arrow2[2], arrow2[3]))
|
|
|
|
for event in event_list:
|
|
if event.type == pygame.MOUSEBUTTONDOWN:
|
|
if first_arrow.rect.collidepoint(event.pos):
|
|
index -= 1
|
|
if index < 0:
|
|
index = len(choices) - 1
|
|
value = choices[index]
|
|
data_set(setting, value)
|
|
elif second_arrow.rect.collidepoint(event.pos):
|
|
index += 1
|
|
if index >= len(choices):
|
|
index = 0
|
|
value = choices[index]
|
|
data_set(setting, value)
|
|
|
|
return first_arrow, value_text, second_arrow
|
|
|
|
|
|
def data_set(setting, value):
|
|
if setting == "size":
|
|
settings_data["size"] = value
|
|
elif setting == "style":
|
|
settings_data["style"] = value
|
|
elif setting == "grid":
|
|
settings_data["grid"] = value
|
|
elif setting == gui:
|
|
settings_data[gui] = value
|
|
elif setting == music:
|
|
if isinstance(value, str):
|
|
settings_data[music]["version"] = value
|
|
else:
|
|
settings_data[music]["volume"] = value
|
|
update_music_volume()
|
|
elif setting == sound:
|
|
if isinstance(value, str):
|
|
settings_data[sound]["version"] = value
|
|
else:
|
|
settings_data[sound]["volume"] = value
|
|
update_sound_volume()
|
|
|
|
with open(SETTINGS_FILE, "w") as settings_file:
|
|
json.dump(settings_data, settings_file, indent=2)
|
|
|
|
|
|
def play_music():
|
|
music_file = settings_data[music]["version"]
|
|
music_file = music_file.replace(" ", "_")
|
|
volume = settings_data[music]["volume"]
|
|
pygame.mixer.music.load("misc/" + music_file + ".mp3")
|
|
pygame.mixer.music.set_volume(volume / 100)
|
|
pygame.mixer.music.play(-1)
|
|
|
|
def play_sound():
|
|
sound_file = settings_data[sound]["version"]
|
|
sound_file = sound_file.replace(" ", "_")
|
|
volume = settings_data[sound]["volume"]
|
|
current_sound = pygame.mixer.Sound("misc/" + sound_file + ".ogg")
|
|
current_sound.set_volume(volume / 100)
|
|
return current_sound
|
|
|
|
current_sound = play_sound()
|
|
|
|
|
|
def update_music_volume():
|
|
volume = settings_data[music]["volume"]
|
|
pygame.mixer.music.set_volume(volume / 100)
|
|
|
|
def update_sound_volume():
|
|
volume = settings_data[sound]["volume"]
|
|
current_sound.set_volume(volume / 100)
|
|
|
|
|
|
# Not configured properly
|
|
def set_hue():
|
|
global hue_value
|
|
if settings_data["style"] == STYLE_CHOICES[0]:
|
|
x = 0
|
|
elif settings_data["style"] == STYLE_CHOICES[2]:
|
|
x = 1
|
|
|
|
hue_value = settings_data[gui][x]
|
|
keys = pygame.key.get_pressed()
|
|
if keys[pygame.K_LEFT]:
|
|
hue_value = (hue_value - 1) % 360
|
|
if keys[pygame.K_RIGHT]:
|
|
hue_value = (hue_value + 1) % 360
|
|
|
|
sprite_sheet.set_hue(hue_value)
|
|
|
|
pygame.image.save(sprite_sheet.sheet, SIZE_PATH + str(INT) + STYLE_PATH + "spritesheet.png")
|
|
|
|
settings_data[gui][x] = hue_value
|
|
with open(SETTINGS_FILE, "w") as settings_file:
|
|
json.dump(settings_data, settings_file, indent=2)
|
|
|
|
|
|
def change_hue(image, hue):
|
|
new_image = image.copy()
|
|
|
|
for x in range(new_image.get_width()):
|
|
for y in range(new_image.get_height()):
|
|
color = new_image.get_at((x, y))
|
|
|
|
h, s, v, a = pygame.Color(color)
|
|
h = (h + hue) % 360
|
|
new_color = pygame.Color(0, 0, 0, 0)
|
|
new_color.hsva = (h, s, v, a)
|
|
|
|
new_image.set_at((x, y), new_color)
|
|
|
|
return new_image
|
|
|
|
|
|
def export():
|
|
# Export data function, not finished yet
|
|
pass
|
|
|
|
|
|
def reset():
|
|
# Reset all data function, not finished yet
|
|
pass
|
|
|
|
# STATS MODE
|
|
|
|
|
|
#INFO MODE
|
|
text_size = [3, 5]
|
|
|
|
def render_text_in_rect(long_text, rect_area):
|
|
font = pygame.font.SysFont(FONT, FONT_SIZE-text_size[0])
|
|
lines = []
|
|
words = long_text.split()
|
|
current_line = ""
|
|
for word in words:
|
|
test_line = current_line + word + " "
|
|
if font.size(test_line)[0] <= rect_area.width:
|
|
current_line = test_line
|
|
else:
|
|
lines.append(current_line.strip())
|
|
current_line = word + " "
|
|
lines.append(current_line.strip())
|
|
|
|
line_height = font.get_linesize()
|
|
text_surfaces = []
|
|
text_rects = []
|
|
for i, line in enumerate(lines):
|
|
text_render = font.render(line, True, pygame.Color(*text_color))
|
|
text_rect = text_render.get_rect(topleft=(rect_area.x, rect_area.y + i * line_height))
|
|
text_surfaces.append(text_render)
|
|
text_rects.append(text_rect)
|
|
|
|
return text_surfaces, text_rects
|
|
|
|
# Example usage
|
|
rect_area = non_movable_area.copy()
|
|
rect_area.x += text_size[1]
|
|
rect_area.width -= text_size[1]
|
|
long_text = (
|
|
"Picture Puzzle, once a feature in Windows Vista and 7's 'Desktop Gadgets,' "
|
|
"faced an end in 2012 due to security concerns. This game is a remake of original "
|
|
"Picture Puzzle with modified and upgraded functionality. Music autor is SmugBurger, "
|
|
"sounds taken from open sources."
|
|
)
|
|
|
|
text_surfaces, text_rects = render_text_in_rect(long_text, rect_area)
|
|
names = ("Pause", "Menu", "Hint", "Shuffle", "Close", "Delete", "Continue", "Settings", "Quick game", "Add new", "Info", "Stats")
|
|
|
|
# Buttons (maybe optimise?)
|
|
if current_size == SIZE_CHOICES[0]:
|
|
px12 = (12, 12)
|
|
px14 = (14, 14)
|
|
timer_button = Button((0, 0), (7, 3), px12, names[0])
|
|
menu_button = Button((15, 0), (80, 3), px12, names[1])
|
|
hint_button = Button((30, 0), (95, 3), px12, names[2])
|
|
shuffle_button = Button((45, 0), (110, 3), px12, names[3])
|
|
close_button = Button((60, 0), (125, 3), px12, names[4])
|
|
finished_button = Button((75, 0), (7, 3), px12)
|
|
not_finished_button = Button((90, 0), (7, 3), px12)
|
|
delete_button = Button((105, 0), (95, 3), px12, names[5])
|
|
continue_button = Button((120, 0), (110, 3), px12, names[6])
|
|
settings_button = Button((135, 0), (7, 3), px12, names[7])
|
|
quick_add_button = Button((34, 30), (22, 3), (55, 12), names[8])
|
|
new_button = Button((150, 0), (80, 3), px12, names[9])
|
|
info_button = Button((165, 0), (7, 3), px12, names[10])
|
|
stats_button = Button((180, 0), (95, 3), px12, names[11])
|
|
to_left_button = Button((0, 30), (33, 136), px14)
|
|
to_right_button = Button((17, 30), (97, 136), px14)
|
|
BACKGROUND_IMAGE = Image((0, 64), (0, 0), (WIDTH, HEIGHT))
|
|
ver1_layer = sprite_sheet.get_sprite((147, 64), WIDTH, HEIGHT, transparent)
|
|
ver2_layer = sprite_sheet.get_sprite((294, 64), WIDTH, HEIGHT, transparent)
|
|
elif current_size == SIZE_CHOICES[1]:
|
|
px16 = (16, 16)
|
|
px20 = (20, 20)
|
|
timer_button = Button((0, 0), (8, 4), px16, names[0])
|
|
menu_button = Button((19, 0), (118, 4), px16, names[1])
|
|
hint_button = Button((38, 0), (138, 4), px16, names[2])
|
|
shuffle_button = Button((57, 0), (158, 4), px16, names[3])
|
|
close_button = Button((76, 0), (178, 4), px16, names[4])
|
|
finished_button = Button((95, 0), (8, 4), px16)
|
|
not_finished_button = Button((114, 0), (8, 4), px16)
|
|
delete_button = Button((133, 0), (138, 4), px16, names[5])
|
|
continue_button = Button((152, 0), (158, 4), px16, names[6])
|
|
settings_button = Button((171, 0), (8, 4), px16, names[7])
|
|
quick_add_button = Button((46, 38), (28, 4), (86, 16), names[8])
|
|
new_button = Button((190, 0), (118, 4), px16, names[9])
|
|
info_button = Button((209, 0), (8, 4), px16, names[10])
|
|
stats_button = Button((228, 0), (138, 4), px16, names[11])
|
|
to_left_button = Button((0, 38), (46, 191), px20)
|
|
to_right_button = Button((23, 38), (136, 191), px20)
|
|
BACKGROUND_IMAGE = Image((0, 84), (0, 0), (WIDTH, HEIGHT))
|
|
ver1_layer = sprite_sheet.get_sprite((205, 84), WIDTH, HEIGHT, transparent)
|
|
ver2_layer = sprite_sheet.get_sprite((410, 84), WIDTH, HEIGHT, transparent)
|
|
else:
|
|
px32 = (32, 32)
|
|
px40 = (40, 40)
|
|
timer_button = Button((0, 0), (12, 6), px32, names[0])
|
|
menu_button = Button((35, 0), (241, 6), px32, names[1])
|
|
hint_button = Button((72, 0), (278, 6), px32, names[2])
|
|
shuffle_button = Button((105, 0), (315, 6), px32, names[3])
|
|
close_button = Button((140, 0), (352, 6), px32, names[4])
|
|
finished_button = Button((175, 0), (12, 6), px32)
|
|
not_finished_button = Button((210, 0), (8, 6), px32)
|
|
delete_button = Button((245, 0), (278, 6), px32, names[5])
|
|
continue_button = Button((280, 0), (315, 6), px32, names[6])
|
|
settings_button = Button((315, 0), (12, 6), px32, names[7])
|
|
quick_add_button = Button((86, 70), (49, 6), (187, 32), names[8])
|
|
new_button = Button((350, 0), (241, 6), px32, names[9])
|
|
info_button = Button((385, 0), (12, 6), px32, names[10])
|
|
stats_button = Button((420, 0), (278, 6), px32, names[11])
|
|
to_left_button = Button((0, 70), (107, 372), px40)
|
|
to_right_button = Button((43, 70), (248, 372), px40)
|
|
BACKGROUND_IMAGE = Image((0, 156), (0, 0), (WIDTH, HEIGHT))
|
|
ver1_layer = sprite_sheet.get_sprite((399, 156), WIDTH, HEIGHT, transparent)
|
|
ver2_layer = sprite_sheet.get_sprite((798, 156), WIDTH, HEIGHT, transparent)
|
|
|
|
|
|
def load_data():
|
|
path_components = puzzle_data[f'current {puzzle_version}']['selected_image'].split('/')[2:]
|
|
current_data = storage["albums"][path_components[0]]
|
|
for component in path_components[1:]:
|
|
current_data = current_data[component]
|
|
|
|
completed = current_data[1]
|
|
not_shuffled = current_data[2]
|
|
timer_text = current_data[5]
|
|
current_timer_text = timer_text
|
|
displayed_current_moves = current_data[6]
|
|
current_moves = 0
|
|
|
|
return completed, not_shuffled, timer_text, current_timer_text, displayed_current_moves, current_moves
|
|
|
|
completed, not_shuffled, timer_text, current_timer_text, displayed_current_moves, current_moves = load_data()
|
|
|
|
if completed:
|
|
state_button = finished_button
|
|
else:
|
|
state_button = not_finished_button
|
|
|
|
game_buttons = [timer_button, menu_button, hint_button, shuffle_button, close_button]
|
|
menu_buttons = [settings_button, quick_add_button, new_button, delete_button, continue_button, close_button]
|
|
select_buttons = [state_button, menu_button, delete_button, continue_button, close_button, to_left_button, to_right_button]
|
|
settings_buttons = [info_button, menu_button, stats_button, continue_button, close_button]
|
|
|
|
do_not_save = False
|
|
|
|
|
|
def button_check(pressed_button, run, current_mode, timer_running):
|
|
global completed, not_shuffled, current_timer_text, current_moves, real_time
|
|
do_not_save = False
|
|
if pressed_button == close_button:
|
|
run = False
|
|
save_puzzle_state()
|
|
update_puzzle_data(completed, not_shuffled, current_timer_text, current_moves, real_time)
|
|
reload_chosen()
|
|
elif pressed_button == settings_button:
|
|
current_mode = "settings"
|
|
elif pressed_button == menu_button:
|
|
current_mode = "menu"
|
|
timer_running = False
|
|
elif pressed_button == continue_button:
|
|
current_mode = "game"
|
|
elif pressed_button == delete_button:
|
|
if current_mode == "menu":
|
|
album_deletion()
|
|
else:
|
|
# Not finished yet
|
|
pass
|
|
elif pressed_button == new_button:
|
|
album_creation()
|
|
elif pressed_button == quick_add_button:
|
|
quick_game()
|
|
current_mode = "game"
|
|
not_shuffled = 1
|
|
current_timer_text = "00:00:00"
|
|
current_moves = 0
|
|
do_not_save = True
|
|
elif pressed_button in [not_finished_button, finished_button]:
|
|
# Not finished yet
|
|
pass
|
|
elif pressed_button == info_button:
|
|
current_mode = "info"
|
|
elif pressed_button == stats_button:
|
|
current_mode = "stats"
|
|
|
|
return run, current_mode, timer_running, do_not_save
|
|
|
|
|
|
win32gui.SetWindowLong(hwnd, win32con.GWL_EXSTYLE,
|
|
win32gui.GetWindowLong(hwnd, win32con.GWL_EXSTYLE) | win32con.WS_EX_LAYERED)
|
|
|
|
win32gui.SetLayeredWindowAttributes(hwnd, win32api.RGB(*transparent), 0, win32con.LWA_COLORKEY)
|
|
set_shadow_style(hwnd)
|
|
|
|
|
|
def data_check(current_mode):
|
|
if current_mode == "select":
|
|
background_image = ver2_layer
|
|
current_buttons = select_buttons
|
|
|
|
elif current_mode == "game":
|
|
current_buttons = game_buttons
|
|
background_image = ver1_layer
|
|
|
|
elif current_mode == "menu":
|
|
current_buttons = menu_buttons
|
|
background_image = ver1_layer
|
|
|
|
elif current_mode == "settings" or current_mode == "stats" or current_mode == "info":
|
|
current_buttons = settings_buttons
|
|
background_image = ver1_layer
|
|
|
|
return background_image, current_buttons
|
|
|
|
|
|
# Game state
|
|
run = True
|
|
current_mode = "menu"
|
|
# Dragging and mouse interaction
|
|
dragging = False
|
|
can_move = False
|
|
start_pos = (0, 0)
|
|
pressed_button = None
|
|
slider_moving = False
|
|
setting = None
|
|
arrow = None
|
|
# Select mode
|
|
clicked_image = False
|
|
image_display = None
|
|
clicked = None
|
|
# Puzzle-related stuff
|
|
last_shuffle_time = 0
|
|
user_win = False
|
|
moved = None
|
|
# Timer-related stuff
|
|
timer_running = False
|
|
timer_start_time = 0
|
|
elapsed_time = 0
|
|
real_time = elapsed_time
|
|
total_paused_time = 0
|
|
timer_button.disable()
|
|
|
|
if settings_data[music]["on"] is True:
|
|
play_music()
|
|
|
|
while run:
|
|
background_image, current_buttons = data_check(current_mode)
|
|
current_time = pygame.time.get_ticks()
|
|
|
|
screen.fill((255,0,128))
|
|
screen.blit(BACKGROUND_IMAGE.img, (0, 0))
|
|
screen.blit(background_image, (0, 0))
|
|
|
|
mouse_pos = pygame.mouse.get_pos()
|
|
|
|
for button in current_buttons:
|
|
button.update(mouse_pos)
|
|
|
|
for button in current_buttons:
|
|
button.draw(screen)
|
|
|
|
clock.tick(FPS)
|
|
|
|
if current_mode == "menu":
|
|
selected_album = render_albums()
|
|
|
|
if current_mode == "select":
|
|
if image_display is None or current_album != selected_album:
|
|
selected_image = ImageRender(selected_album)
|
|
image_display = True
|
|
|
|
selected_image.render(screen)
|
|
|
|
event_list = pygame.event.get()
|
|
|
|
for event in event_list:
|
|
if event.type == pygame.QUIT or event.type == pygame.KEYDOWN and event.key == pygame.K_ESCAPE:
|
|
save_puzzle_state()
|
|
update_puzzle_data(completed, not_shuffled, current_timer_text, current_moves, real_time)
|
|
reload_chosen()
|
|
run = False
|
|
elif event.type == pygame.MOUSEBUTTONDOWN and event.button == 1:
|
|
if not non_movable_area.collidepoint(pygame.mouse.get_pos()):
|
|
can_move = True
|
|
if current_mode == "select":
|
|
clicked_image_rect = ImageRender.image_save(selected_album, current_img)
|
|
clicked_image = clicked_image_rect.collidepoint(event.pos)
|
|
if clicked_image:
|
|
clicked = True
|
|
update_puzzle_data(completed, not_shuffled, current_timer_text, current_moves, real_time)
|
|
puzzle_data[f'current {puzzle_version}']["selected_image"] = IMAGES_PATH + selected_album + "/" + current_img
|
|
with open(PUZZLE_FILE, 'w') as puzzle_file:
|
|
puzzle_file.write(json.dumps(puzzle_data))
|
|
completed, not_shuffled, timer_text, current_timer_text, displayed_current_moves, current_moves = load_data()
|
|
elapsed_time = 0
|
|
current_moves = 0
|
|
shuffle_pieces(puzzle_pieces)
|
|
load_puzzle()
|
|
current_mode = "game"
|
|
timer_running = False
|
|
timer_start_time = 0
|
|
real_time = elapsed_time
|
|
total_paused_time = 0
|
|
timer_button.disable()
|
|
clicked = None
|
|
last_click_time = pygame.time.get_ticks()
|
|
|
|
if to_left_button.is_hovered(event.pos):
|
|
selected_image.update_index(-1)
|
|
last_image_switch_time = current_time
|
|
|
|
if to_right_button.is_hovered(event.pos):
|
|
selected_image.update_index(1)
|
|
last_image_switch_time = current_time
|
|
|
|
elif current_mode == "game":
|
|
if non_movable_area.collidepoint(event.pos) and not timer_running:
|
|
timer_running = True
|
|
timer_button.enable()
|
|
timer_start_time = pygame.time.get_ticks()
|
|
elif timer_button.rect.collidepoint(event.pos) and timer_running:
|
|
timer_running = False
|
|
timer_button.disable()
|
|
total_paused_time += pygame.time.get_ticks() - timer_start_time
|
|
|
|
dragging = True
|
|
start_pos = pygame.mouse.get_pos()
|
|
for button in current_buttons:
|
|
if button.rect.collidepoint(start_pos):
|
|
pressed_button = button
|
|
run, current_mode, timer_running, do_not_save = button_check(pressed_button, run, current_mode, timer_running)
|
|
|
|
elif event.type == pygame.MOUSEMOTION and can_move:
|
|
new_pos = pygame.mouse.get_pos()
|
|
offset_x = new_pos[0] - start_pos[0]
|
|
offset_y = new_pos[1] - start_pos[1]
|
|
window_coords[0] += offset_x
|
|
window_coords[1] += offset_y
|
|
move_win(window_coords)
|
|
|
|
elif event.type == pygame.MOUSEBUTTONUP:
|
|
dragging = False
|
|
slider_moving = False
|
|
pressed_button = None
|
|
can_move = False
|
|
setting = None
|
|
|
|
elif event.type == pygame.MOUSEBUTTONDOWN and current_mode == "settings":
|
|
if event.button == 4:
|
|
scroll_offset -= 20
|
|
scroll_offset = max(0, scroll_offset)
|
|
elif event.button == 5:
|
|
scroll_offset += 20
|
|
scroll_offset = min(scroll_offset, max_scroll_offset)
|
|
|
|
if current_mode == "game":
|
|
if timer_running:
|
|
current_time = pygame.time.get_ticks() - timer_start_time
|
|
elapsed_time = (current_time + total_paused_time) // 1000
|
|
real_time = elapsed_time
|
|
hours, minutes, seconds = map(int, timer_text.split(':'))
|
|
total_seconds = hours * 3600 + minutes * 60 + seconds
|
|
elapsed_time += total_seconds
|
|
current_timer_text = timer(elapsed_time)
|
|
render_text(current_timer_text, (timer_area.x, timer_area.y), screen, oposite_color)
|
|
|
|
# Puzzle
|
|
if pressed_button == shuffle_button and current_time - last_shuffle_time > 500:
|
|
if not_shuffled:
|
|
np.copyto(puzzle_matrix, original_matrix)
|
|
user_win = False
|
|
not_shuffled = 0
|
|
else:
|
|
shuffle_pieces(puzzle_pieces)
|
|
not_shuffled = 1
|
|
save_puzzle_state()
|
|
update_directions()
|
|
last_shuffle_time = current_time
|
|
|
|
for row in range(len(puzzle_matrix)):
|
|
for col in range(len(puzzle_matrix[0])):
|
|
if puzzle_matrix[row][col]:
|
|
|
|
piece_x = col * puzzle_size[0] // len(puzzle_matrix[0]) + non_movable_area.x
|
|
piece_y = row * puzzle_size[1] // len(puzzle_matrix) + non_movable_area.y
|
|
screen.blit(puzzle_pieces[puzzle_matrix[row][col] - 1][0], (piece_x, piece_y))
|
|
|
|
for button in current_buttons:
|
|
button.draw(screen)
|
|
|
|
if event.type == pygame.MOUSEBUTTONDOWN and event.button == 1:
|
|
mouse_x, mouse_y = pygame.mouse.get_pos()
|
|
|
|
clicked_row, clicked_col = (mouse_y - non_movable_area.y) // cell_size, (mouse_x - non_movable_area.x) // cell_size
|
|
|
|
if 0 <= clicked_row < len(puzzle_matrix) and 0 <= clicked_col < len(puzzle_matrix[0]):
|
|
if puzzle_matrix[clicked_row, clicked_col] and clicked is None:
|
|
moved = move_pieces(clicked_row, clicked_col)
|
|
if settings_data[sound]["on"] and moved is not None:
|
|
current_moves += 1
|
|
user_win = True
|
|
pygame.mixer.Sound.play(current_sound)
|
|
|
|
if pressed_button == hint_button:
|
|
screen.blit(original_image, non_movable_area)
|
|
if number_of_rows == 4 and puzzle_data[f'current {puzzle_version}']['matrix'] == puzzle_data[f'puzzle {puzzle_version}'] and user_win is True:
|
|
completed = 1
|
|
not_shuffled = 1
|
|
user_win = False
|
|
timer_running = False
|
|
start_time = pygame.time.get_ticks()
|
|
fade_in_end_time = start_time + 2 * 1000
|
|
fade_out_start_time = start_time + 4 * 1000
|
|
end_time = start_time + 6 * 1000
|
|
alpha = 0
|
|
text_alpha = 0
|
|
fade_in_speed = 100 / (fade_in_end_time - start_time)
|
|
fade_out_speed = 100 / (fade_out_start_time - fade_in_end_time)
|
|
|
|
while pygame.time.get_ticks() < end_time:
|
|
|
|
surface = pygame.Surface((non_movable_area.width, non_movable_area.height), pygame.SRCALPHA)
|
|
surface.fill((0, 0, 0, alpha))
|
|
print(alpha)
|
|
screen.blit(surface, (non_movable_area.x, non_movable_area.y))
|
|
|
|
render_text("You win !!", (win_area.x, win_area.y), screen, (255, 255, 255, text_alpha), True)
|
|
|
|
if pygame.time.get_ticks() < fade_in_end_time:
|
|
alpha += fade_in_speed
|
|
text_alpha += fade_in_speed
|
|
elif fade_in_end_time <= pygame.time.get_ticks() < fade_out_start_time:
|
|
alpha = max(0, alpha)
|
|
text_alpha = max(0, text_alpha)
|
|
elif pygame.time.get_ticks() >= fade_out_start_time:
|
|
|
|
alpha -= fade_out_speed
|
|
text_alpha -= fade_out_speed
|
|
|
|
alpha = max(0, alpha)
|
|
text_alpha = max(0, text_alpha)
|
|
pygame.display.flip()
|
|
pygame.time.delay(16)
|
|
|
|
if current_mode == "settings":
|
|
clipped_rect = pygame.Rect(non_movable_area.x, non_movable_area.y, settings_x_position, non_movable_area.height)
|
|
screen.set_clip(clipped_rect)
|
|
|
|
for index, setting_name in enumerate(settings_data):
|
|
if setting_name != "keybinds":
|
|
y = f_line[3] - scroll_offset + index * (cons[0] + spaces[2]) + spaces[1]
|
|
value = settings_data[setting_name]
|
|
if setting_name in [gui, music, sound]:
|
|
if setting_name in [music, sound]:
|
|
value = settings_data[setting_name]["volume"]
|
|
value2 = settings_data[setting_name]["version"]
|
|
|
|
if setting_name == "size":
|
|
render_settings_text(setting_name.upper(),(settings_x_position // 2 + spaces[4], y), screen)
|
|
size_choosing = arrows((settings_x_position // 2 - spaces[3], y + spaces[0]), setting_name, value, SIZE_CHOICES, event_list)
|
|
size_choosing[0].update(mouse_pos)
|
|
size_choosing[2].update(mouse_pos)
|
|
size_choosing[0].draw(screen)
|
|
size_choosing[2].draw(screen)
|
|
active_selects["size"] = True
|
|
elif setting_name == "style":
|
|
render_settings_text(setting_name.upper(),(settings_x_position // 2 + spaces[4], y), screen)
|
|
style_choosing = arrows((settings_x_position // 2 - spaces[3], y + spaces[0]), setting_name, value, STYLE_CHOICES, event_list)
|
|
style_choosing[0].update(mouse_pos)
|
|
style_choosing[2].update(mouse_pos)
|
|
style_choosing[0].draw(screen)
|
|
style_choosing[2].draw(screen)
|
|
active_selects["style"] = True
|
|
elif setting_name == "grid":
|
|
render_settings_text(setting_name.upper(),(settings_x_position // 2 + spaces[4], y), screen)
|
|
grid_choosing = arrows((settings_x_position // 2 - spaces[3], y + spaces[0]), setting_name, value, GRID_CHOICES, event_list)
|
|
grid_choosing[0].update(mouse_pos)
|
|
grid_choosing[2].update(mouse_pos)
|
|
grid_choosing[0].draw(screen)
|
|
grid_choosing[2].draw(screen)
|
|
active_selects["grid"] = True
|
|
elif setting_name == gui:
|
|
render_settings_text(setting_name.upper(),(settings_x_position // 2 + spaces[4], y), screen, value)
|
|
gui_color_slider = slider((settings_x_position // 2 - spaces[3], y + spaces[0]), 360, value, active_sliders, setting_name)
|
|
for slider_part in gui_color_slider:
|
|
slider_part.draw(screen)
|
|
if dragging and gui_color_slider[2].rect.collidepoint(mouse_pos):
|
|
active_sliders[gui] = True
|
|
setting = setting_name
|
|
elif not dragging:
|
|
active_sliders[gui] = False
|
|
elif setting_name == music:
|
|
render_settings_text(setting_name.upper(),(settings_x_position // 2 + spaces[4], y), screen, value)
|
|
music_volume_slider = slider((settings_x_position // 2 - spaces[3], y + spaces[0]), 100, value, active_sliders, setting_name)
|
|
for slider_part in music_volume_slider:
|
|
slider_part.draw(screen)
|
|
if dragging and music_volume_slider[2].rect.collidepoint(mouse_pos):
|
|
active_sliders[music] = True
|
|
setting = setting_name
|
|
elif not dragging:
|
|
active_sliders[music] = False
|
|
music_choosing = arrows((settings_x_position // 2 - spaces[3], y + spaces[0]+spaces3[2]), setting_name, value2, MUSIC_CHOICES, event_list)
|
|
music_choosing[0].update(mouse_pos)
|
|
music_choosing[2].update(mouse_pos)
|
|
music_choosing[0].draw(screen)
|
|
music_choosing[2].draw(screen)
|
|
active_selects["music_version"] = True
|
|
elif setting_name == sound:
|
|
render_settings_text(setting_name.upper(),(settings_x_position // 2 + spaces[4], y+spaces3[1]), screen, value)
|
|
y += spaces2[5]
|
|
sound_volume_slider = slider((settings_x_position // 2 - spaces[3], y-spaces3[0]), 100, value, active_sliders, setting_name)
|
|
for slider_part in sound_volume_slider:
|
|
slider_part.draw(screen)
|
|
if dragging and sound_volume_slider[2].rect.collidepoint(mouse_pos):
|
|
active_sliders[sound] = True
|
|
setting = setting_name
|
|
elif not dragging:
|
|
active_sliders[sound] = False
|
|
sound_choosing = arrows((settings_x_position // 2 - spaces[3], y + spaces[0]), setting_name, value2, SOUND_CHOICES, event_list)
|
|
sound_choosing[0].update(mouse_pos)
|
|
sound_choosing[2].update(mouse_pos)
|
|
sound_choosing[0].draw(screen)
|
|
sound_choosing[2].draw(screen)
|
|
active_selects["sound_version"] = True
|
|
|
|
screen.set_clip(None)
|
|
|
|
if current_mode == "info":
|
|
for text_surface, text_rect in zip(text_surfaces, text_rects):
|
|
screen.blit(text_surface, text_rect)
|
|
if current_mode == "stats":
|
|
print("Currently under maintenance")
|
|
|
|
pygame.display.flip()
|
|
|
|
pygame.quit() |