






init python:
    import random
    import pygame

    class PuzzleSettings:
        """
            `AudioOnClickedCell`:
                Play audio on channel `ChannelOnClickedCell` if is not None.
            `ChannelOnClickedCell`:
                Channel for play `AudioOnClickedCell`.
            `AudioOnUpValidCell`:
                Play audio on channel `ChannelOnUpValidCell` if is not None.
            `ChannelOnUpValidCell`:
                Channel for play `AudioOnUpValidCell`.
            `AudioOnUpMissedCell`:
                Play audio on channel `ChannelOnUpMissedCell` if is not None.
            `ChannelOnUpMissedCell`:
                Channel for play `AudioOnUpMissedCell`.
            `HoverEffect`:
                Visual effect for hovered cell. Type of `im.matrix`.
            `ImageZoom`:
                Scaled of Puzzle image. Const.
            `CellXSize`:
                Scalar size for cell of main picture. Const.
            `CellYSize`:
                 Scalar size for cell of main picture. Const.
            `CellXSizeOnStable`:
                Scalar size when cell in fixed position. Const.
            `CellYSizeOnStable:
                Scalar size when cell in fixed position. Const.
            `InitPositionPreset`:
                Position of random picture. Const.
            `ValidPositionPreset`:
                Position of valid picture. Const.
            `InterfaceFolder`:
                Path to interface folder.
            `PhotoFolder`:
                Path to photo folder.
            `PresetFolder`:
                Path to save preset. (Doesn't work with android.)
            `Separator`:
                Separator for naming save pictures. (Doesn't work with android.)
            `CellQuantityX`:
                Scalar quantity for cell of main picture. Const.
            `CellQuantityY`:
                Scalar quantity for cell of main picture. Const.
        """
        AudioOnClickedCell = "sound_effects/laptop/mouse_click.mp3"
        ChannelOnClickedCell = "sound"
        AudioOnUpValidCell = "sound_effects/notify_classic.mp3"
        ChannelOnUpValidCell = "sound"
        AudioOnUpMissedCell = "sound_effects/notify_negative.mp3"
        ChannelOnUpMissedCell = "sound"
        HoverEffect = im.matrix.opacity(.7)
        ImageZoom = .7
        CellXSize = 333
        CellYSize = 333
        CellXSizeOnStable = 224
        CellYSizeOnStable = 226
        InitPositionPreset = {
            0: (240, 192),
            1: (243 + int(CellXSize * ImageZoom), 192),
            2: (246 + int(2 * CellXSize * ImageZoom), 192),
            3: (240, 195 + int(CellYSize * ImageZoom)),
            4: (243 + int(CellXSize * ImageZoom), 195 + int(CellYSize * ImageZoom)),
            5: (246 + int(2 * CellXSize * ImageZoom), 195 + int(CellYSize * ImageZoom)),
            6: (240, 198 + int(2 * CellYSize * ImageZoom)),
            7: (243 + int(CellXSize * ImageZoom), 198 + int(2 * CellYSize * ImageZoom)),
            8: (246 + int(2 * CellXSize * ImageZoom), 198 + int(2 * CellYSize * ImageZoom)),
        }
        ValidPositionPreset = {
            0: {
                "x0": 1056,
                "x1": 1056 + CellXSizeOnStable,
                "y0": 186,
                "y1": 186 + CellYSizeOnStable,
            },
            1: {
                "x0": 1286,
                "x1": 1286 + CellXSizeOnStable,
                "y0": 186,
                "y1": 186 + CellYSizeOnStable,
            },
            2: {
                "x0": 1516,
                "x1": 1516 + CellXSizeOnStable,
                "y0": 186,
                "y1": 186 + CellYSizeOnStable,
            },
            3: {
                "x0": 1056,
                "x1": 1056 + CellXSizeOnStable,
                "y0": 419,
                "y1": 419 + CellYSizeOnStable,
            },
            4: {
                "x0": 1286,
                "x1": 1286 + CellXSizeOnStable,
                "y0": 419,
                "y1": 419 + CellYSizeOnStable,
            },
            5: {
                "x0": 1516,
                "x1": 1516 + CellXSizeOnStable,
                "y0": 419,
                "y1": 419 + CellYSizeOnStable,
            },
            6: {
                "x0": 1056,
                "x1": 1056 + CellXSizeOnStable,
                "y0": 652,
                "y1": 652 + CellYSizeOnStable,
            },
            7: {
                "x0": 1286,
                "x1": 1286 + CellXSizeOnStable,
                "y0": 652,
                "y1": 652 + CellYSizeOnStable,
            },
            8: {
                "x0": 1516,
                "x1": 1516 + CellXSizeOnStable,
                "y0": 652,
                "y1": 652 + CellYSizeOnStable,
            },
        }
        InterfaceFolder = "minigames/puzzle/images/interface/"
        PhotoFolder = "minigames/puzzle/images/photo/"
        PresetFolder = "minigames/puzzle/images/preset/"
        Separator = "_Into"
        CellQuantityX = 3
        CellQuantityY = 3

    class Cell(renpy.store.object):
        """

        """
        def __init__(
            self,
            main,
            texture,
            position,
            size,
            id
        ):
            self.main = main
            self.texture = texture
            self.init_position = self.position = position
            self.width, self.height = size
            self.id = id
            self.focused = False
            self.hovered = False
            self.can_move = True
            self.delay_snap = .0
            self.target_x = self.target_y = 0
        
        @property
        def xpos(self):
            return self.position[0]
        
        @property
        def ypos(self):
            return self.position[1]
        
        @property
        def concerns(self):
            xpos, ypos = renpy.get_mouse_pos()
            preset = PuzzleSettings.ValidPositionPreset[self.id]
            xobjfrom = preset["x0"]
            yobjfrom = preset["y0"]
            xobjto = preset["x1"]
            yobjto = preset["y1"]
            return ((xobjfrom <= xpos <= xobjto)
                    and (yobjfrom <= ypos <= yobjto))
        
        @property
        def get_fixed_position(self):
            x = PuzzleSettings.ValidPositionPreset[self.id]["x0"]
            y = PuzzleSettings.ValidPositionPreset[self.id]["y0"]
            return (x, y)
        
        def snap(self, position, delay=0):
            self.target_x, self.target_y = position
            self.delay_snap = delay

    class Puzzle(renpy.store.object):
        """

        """
        def __init__(
            self, folder=PuzzleSettings.PhotoFolder,
        ):
            self.folder = folder
            self.preloaded = None
            self.xsize = self.ysize = 0
            self.cells = []
            self.crop_image()
        
        def save_image(self, surf, filename):
            """
            Non worked on OS Android.
            """
            renpy.display.scale.image_save_unscaled(surf, filename)
        
        def preload_image(self, image):
            self.preloaded = renpy.easy.displayable(image)
            xsize, ysize = renpy.image_size(self.preloaded)
            self.xsize = int(xsize/PuzzleSettings.CellQuantityX)
            self.ysize = int(ysize/PuzzleSettings.CellQuantityY)
        
        def crop_image(self):
            images = [k for k in renpy.list_files() if k.startswith(self.folder) and k.endswith((".jpg", ".png", ".webp"))]
            if not images:
                raise ValueError("Don't find images for Puzzle.")
            random.shuffle(images)
            image = images.pop()
            image_rpa = image.rpartition(".")[0].split("/")[-1]
            folder_to = image_rpa + "/"
            into_preset = [k for k in renpy.list_files() if k.startswith(PuzzleSettings.PresetFolder + image_rpa)]
            self.preload_image(image=image)
            if not into_preset:
                for _ in xrange(PuzzleSettings.CellQuantityX * PuzzleSettings.CellQuantityY):
                    xpos = _ % PuzzleSettings.CellQuantityX * self.xsize
                    ypos = _ / PuzzleSettings.CellQuantityY * self.ysize
                    texture = im.Crop(self.preloaded, (xpos, ypos, self.xsize, self.ysize))
                    self.save_image(surf=texture.load(),
                                    filename=config.gamedir + "/" + PuzzleSettings.PresetFolder + image_rpa + PuzzleSettings.Separator + str(_) + ".png")
            positions = PuzzleSettings.InitPositionPreset.values()
            random.shuffle(positions)
            for _ in xrange(PuzzleSettings.CellQuantityX * PuzzleSettings.CellQuantityY):
                xpos = _ % PuzzleSettings.CellQuantityX * self.xsize
                ypos = _ / PuzzleSettings.CellQuantityY * self.ysize
                texture = Null() if renpy.android else im.Image(PuzzleSettings.PresetFolder + image_rpa + PuzzleSettings.Separator + str(_) + ".png")
                self.cells.append(
                    Cell(
                        main=image,
                        texture=texture,
                        position=positions.pop(),
                        size=(int(self.xsize * PuzzleSettings.ImageZoom),
                              int(self.ysize * PuzzleSettings.ImageZoom)),
                        id=_
                    )
                )
        
        @property
        def is_valid(self):
            return all([not cell.can_move for cell in self.cells])
        
        @property
        def get_puzzle(self):
            return PuzzleDisplayable(cells=self.cells)

    class PuzzleDisplayable(renpy.display.core.Displayable):
        """

        """
        def __init__(self, cells):
            super(PuzzleDisplayable, self).__init__()
            self.cells = cells
            self.clicked = False
            self.grab_position = None
            self.grabbed_cell = None
            self.at = 0
        
        def render(self, width, height, st, at):
            rv = renpy.display.render.Render(config.screen_width, config.screen_height)
            cells = sorted(self.cells, key=lambda cell: cell.focused)
            for cell in cells:
                if at < cell.delay_snap:
                    done = (at - self.at) / (cell.delay_snap - self.at)
                    xpos = absolute(cell.xpos + done * (cell.target_x - cell.xpos))
                    ypos = absolute(cell.ypos + done * (cell.target_y - cell.ypos))
                    position = (xpos, ypos)
                else:
                    cell.delay_snap = 0
                    position = cell.position
                cell.position = position
                if renpy.android:
                    texture = im.Scale(im.Crop(renpy.easy.displayable(cell.main),
                                               (
                                                cell.id % PuzzleSettings.CellQuantityX * PuzzleSettings.CellXSize,
                                                cell.id / PuzzleSettings.CellQuantityY * PuzzleSettings.CellYSize,
                                                PuzzleSettings.CellXSize, PuzzleSettings.CellYSize)
                                               ),
                                       width=PuzzleSettings.CellXSizeOnStable,
                                       height=PuzzleSettings.CellYSizeOnStable)
                else:
                    texture = im.Scale(cell.texture,
                                       width=PuzzleSettings.CellXSizeOnStable,
                                       height=PuzzleSettings.CellYSizeOnStable)
                if cell.hovered:
                    texture = im.MatrixColor(texture, PuzzleSettings.HoverEffect)
                texture = renpy.render(texture, width, height, st, at)
                rv.blit(texture, cell.position)
            self.at = at
            renpy.redraw(self, .0)
            return rv
        
        def concerns(self, into, obj):
            xpos, ypos = into
            xobjfrom, yobjfrom = obj.position
            xobjto = xobjfrom + obj.width
            yobjto = yobjfrom + obj.height
            return ((xobjfrom <= xpos <= xobjto)
                    and (yobjfrom <= ypos <= yobjto))
        
        def on_down(self, cell, position):
            self.clicked = True
            self.grab_position = position
            self.grabbed_cell = cell
            cell.hovered = False
            cell.focused = True
        
        def on_up(self):
            self.clicked = False
            self.grab_position = None
            self.grabbed_cell.focused = False
            if self.grabbed_cell.concerns:
                position = self.grabbed_cell.get_fixed_position
                self.grabbed_cell.can_move = False
                if PuzzleSettings.AudioOnUpValidCell is not None:
                    renpy.play(filename=PuzzleSettings.AudioOnUpValidCell,
                               channel=PuzzleSettings.ChannelOnUpValidCell)
            else:
                position = self.grabbed_cell.init_position
                if PuzzleSettings.AudioOnUpMissedCell is not None:
                    renpy.play(filename=PuzzleSettings.AudioOnUpMissedCell,
                               channel=PuzzleSettings.ChannelOnUpMissedCell)
            self.grabbed_cell.snap(position, delay=self.at + 1.)
            self.grabbed_cell = None
            renpy.restart_interaction()
        
        def on_move(self, position):
            x, y = position
            par_x = int(x - self.grab_position[0])
            par_y = int(y - self.grab_position[1])
            cell = self.grabbed_cell
            new_x = max(0, min(int(cell.xpos + par_x),
                               config.screen_width - cell.width))
            new_y = max(0, min(int(cell.ypos + par_y),
                               config.screen_height - cell.height))
            cell.position = (new_x, new_y)
            self.grab_position = (x, y)
        
        def event(self, ev, x, y, st):
            if ev.type == pygame.MOUSEBUTTONDOWN:
                if ev.button != 1:
                    if self.clicked:
                        self.on_up()
                for cell in self.cells:
                    if not cell.can_move: continue
                    if self.concerns((x, y), cell):
                        cell.delay_snap = 0
                        self.on_down(cell, (x, y))
                        if PuzzleSettings.AudioOnClickedCell is not None:
                            renpy.play(filename=PuzzleSettings.AudioOnClickedCell,
                                       channel=PuzzleSettings.ChannelOnClickedCell)
                        return
            elif ev.type == pygame.MOUSEBUTTONUP:
                if ev.button != 1: return
                if self.clicked:
                    self.on_up()
            elif ev.type == pygame.MOUSEMOTION:
                if self.clicked:
                    self.on_move((x, y))
                elif not self.clicked:
                    for cell in self.cells:
                        if not cell.can_move: continue
                        cell.hovered = self.concerns((x, y), cell)
# Decompiled by unrpyc: https://github.com/CensoredUsername/unrpyc
