
init -1 python:

    ### Global Functions ###

    def get_character_emote(char, emote):
        return "characters/{}/emotes/{}.webp".format(char, emote) if emote else None

    def get_character_pos(char):
        global sprite_pos

        flip = getattr(renpy.store, char+"_flip", None)
        use_head = getattr(renpy.store, "use_"+char+"_head", None)

        # Resolve X position for head state
        if use_head:
            xpos = sprite_pos["x"]["far_right"] if flip == 1 else sprite_pos["x"]["far_left"]
        else:
            xpos = getattr(renpy.store, char+"_xpos", None)
        ypos = getattr(renpy.store, char+"_ypos", None)

        return (xpos, ypos)

    ### Classes ###

    class DollDisplayable(renpy.Displayable):
        def __init__(self, child, **properties):
            super(DollDisplayable, self).__init__(**properties)
            self.focusable = None
            self.child = child

        def render(self, width, height, st, at):
            rv = renpy.Render(width, height)
            c = self.child
            cr = renpy.render(c, width, height, st, at)

            # We need to find the 'true' size of the displayable
            # otherwise it may end up being the size of a null,
            # or any other irrelevant element.

            xsize, ysize = cr.get_size()
            xoffset, yoffset = c.place(rv, 0, 0, width, height, cr)

            width = max(xoffset + xsize, width)
            height = max(yoffset + ysize, height)

            rv.width = width
            rv.height = height

            return rv

        def event(self, ev, x, y, st):
            return None
            #raise renpy.IgnoreEvent() # Seems to work similar to NullAction

        def focus(self, default=False):
            raise Exception("Not Implemented")

        def unfocus(self, default=False):
            raise Exception("Not Implemented")

        def is_focused(self):
            return False

        def visit_all(self, callback, seen=None):
            return

        def visit(self):
            return []

    class DollMethods(object):
        """Container class for commonly used methods and attributes"""
        sprite = Null()
        cached = False
        threadlock = False
        icon_threadlock = False

        layers_extra = {"extra", "outline", "overlay"}
        layers_special = {"skin", "mask", "wind_mask"}
        layers_additional = {"back", "front"}

        blacklist_toggles = ("hair", "glasses", "pubes", "piercing", "makeup", "tattoo", "earrings")
        blacklist_unequip = {"hair"}
        multislots = ("makeup", "accessory", "piercing", "tattoo")

        sizes = (1010, 1200) # Default sizes used for defining rare cases

        def rebuild_image(self):
            # Defers rebuild until next time get_image is called
            self.cached = False

        def make_image(self):
            thread = DollThread(target=self.build_image)
            thread.daemon = True
            thread.start()

            sprites = thread.join()

            self.sprite = DollDisplayable(Fixed(*sprites, fit_first=True))

        def get_image(self):
            if not renpy.is_skipping():
                if not self.cached:
                    self.cached = True

                    self.make_image()
            return self.sprite
