init 5 python:
    # def pairwise(iterable):
    #     a = iter(iterable)
    #     return zip(a, a)

    class DollChibi(renpy.Displayable):
        def __init__(self, name, doll, pose="stand", layer="screens", zorder=12, zoom=0.28, *args, **properties):

            super(DollChibi, self).__init__(**properties)

            self.name = name
            self.doll = doll
            self.pose = pose
            self.layer = layer
            self.zorder = zorder
            self.zoom = zoom

            self.pos = (0,0)
            self.xzoom = 1

            # Animation
            self.anim_speed = 1.0
            self.anim_fps = 8.0
            self.anim_trans = None
            self.anim_interval = None
            self.anim_interval_total = None
            self.anim_path = None

            self.anim_constructor()

            # ATL
            self.atl_time = 0
            self.atl_time_total = 0
            self.atl_partial = None

        def anim_constructor(self):
            """This function is responsible for creating an animation out of raw image files."""

            path = "characters/{}/chibi/{}/".format(self.name, self.pose)
            images = []

            # Base model
            for fn in renpy.list_files():
                if not fn.startswith(path):
                    continue

                basename = os.path.basename(fn)
                base, ext = os.path.splitext(basename)

                if not ext.lower() in [ ".jpg", ".jpeg", ".png", ".webp" ]:
                    continue

                images.append(renpy.displayable(fn))

            if not images:
                raise IndexError("Animation Constructor could not find any images inside directory:\n\n\"{}\"".format(path))

            # For singular images it's optimal to halt the animation,
            # instead of replaying the same frame over and over again.
            frames = len(images)
            if frames > 1:
                interval = self.anim_speed / self.anim_fps
            else:
                interval = (365.25 * 86400.0) # About one year

            self.anim_path = path
            self.anim_frames = frames
            self.anim_interval = interval
            self.anim_interval_total = (frames * interval)
            self.anim_images = images
            self.anim_prev_images = [ images[-1] ] + images[:-1]

        def render(self, width, height, st, at):

            # Animation Renderer
            t = st % self.anim_interval_total

            trans = self.anim_trans
            interval = self.anim_interval

            # Trigger event after animation time elapses
            if st > self.atl_time_total:
                renpy.timeout(0)

            for image, prev in zip(self.anim_images, self.anim_prev_images):
                if t < interval:
                    if not renpy.game.less_updates:
                        renpy.redraw(self, interval - t)

                    if trans and st >= interval:
                        image = trans(old_widget=prev, new_widget=image)

                    im = renpy.render(image, width, height, t, at)
                    width, height = im.get_size()
                    rv = renpy.Render(width, height)
                    rv.blit(im, (0, 0))

                    return rv
                else:
                    t = t - interval

        def event(self, ev, x, y, st):
            # Determine pose change if show time exceeds animation time.
            # Looping animations ignore this check because they are rebuilt every loop.

            if st > self.atl_time_total:
                self.set_pose("stand")

        def set_pose(self, pose):
            if self.pose == pose:
                return

            self.pose = pose
            self.anim_constructor()

        def move(self, path, speed=1.0, pause=False, loop=False, warper="linear", at_list=[], pose="walk"):
            """Makes chibi move"""
            if isinstance(path, tuple):
                path = [path]

            # If 'A' position is not supplied for A -> B movement, use last known position instead.
            if len(path) < 2:
                path = [self.pos] + path

            self.set_pose(pose)

            # Note: Warper names and their count can change over time,
            # so it's easier to just evaluate the input.
            # List of available warpers:
            # https://www.renpy.org/doc/html/atl.html?#warpers
            warper = eval("_warper.{}".format(warper))

            distances = []
            times = []

            if loop:
                # Append first position as last to create a looped path.
                path.append(path[0])

            # Calculate distances and timings using euclidean distance algorithm.
            for xy1, xy2 in zip(path, path[1:]):
                x1, y1 = xy1
                x2, y2 = xy2

                distance = math.hypot(x2 - x1, y2 - y1)
                time = distance / (100.0 * speed)

                distances.append(distance)
                times.append(time)

            # Calculate total ATL time required to reach the destination
            total_time = sum(times)
            self.atl_time_total = total_time

            # Recalculate animation intervals when necessary, including speed factors.
            frames = self.anim_frames
            if frames > 1:
                interval = (self.anim_speed / self.anim_fps) / speed
                interval_total = (frames * interval)

                self.anim_interval = interval
                self.anim_interval_total = interval_total

            # renpy.partial allows us to pass arguments into a transform function.
            partial = renpy.partial(self.move_atl, path, times, loop, warper)
            self.atl_partial = partial

            self.restart_atl()

            if pause:
                renpy.pause(total_time)

            return (distances, times)

        def move_atl(self, path, times, loop, warper, trans, st, at):
            """Animations are time based, so each segment will happen at a specific frame time."""
            if loop:
                timer = st % self.atl_time_total
            else:
                timer = st

                if timer > self.atl_time_total:
                    return None

            internal_time = 0
            current_segment = 0

            # TODO: This loop feels unnecessary, need to find a better way.
            for i, t in enumerate(times):
                if (internal_time + t) > timer:
                    current_segment = i
                    break

                internal_time += t

            segment_time = (timer - internal_time) / times[current_segment]
            next_segment = current_segment + 1

            # Adjust XY position
            trans.pos = renpy.atl.interpolate(warper(segment_time), path[current_segment], path[next_segment], renpy.atl.PROPERTIES["pos"])
            self.pos = trans.pos

            # Adjust X zoom based on target X position
            # 1 = Facing Right, -1 = Facing Left
            trans.xzoom = -1 if (path[current_segment][0] > path[next_segment][0]) else 1
            self.xzoom = trans.xzoom

            # Adjust zoom
            trans.zoom = self.zoom

            # Adjust Z position based on Y axis
            # TODO: Add room support with bottom, middle, and top vanishing points.
            # room_scale = 0.5
            # zpos1 = ((path[current_segment][1] / 600.0) * 1000.0) * room_scale
            # zpos2 = ((path[next_segment][1] / 600.0) * 1000.0) * room_scale
            # trans.zpos = renpy.atl.interpolate(warper(segment_time), zpos1, zpos2, renpy.atl.PROPERTIES["zpos"])
            # self.zpos = trans.zpos

            # TODO: Using zorders is suboptimal and expensive, using 3D staging would be preferable.
            zpos = self.zorder + self.pos[1] / config.screen_height
            renpy.change_zorder(self.layer, self.name, zpos)
            return 0

        def restart_atl(self):
            # The safest way to restart the transform is to rebuild it.
            # Other methods proved to be too finicky...

            transform = Transform(function=self.atl_partial, anchor=(0.5, 1.0))
            image = At(self, transform) # IMPORTANT: Enable perspective and gl_depth for 3D staging

            if not renpy.is_init_phase():
                renpy.show(self.name, at_list=[], layer=self.layer, what=image, zorder=self.zorder)

        # def dynamic(self, st, at):
        #     return self.image, None

init offset = 5

default hooch_chibi = DollChibi(name="hooch", doll=hooch)