Quellcode für miniworlds.tools.timer

import miniworlds.base.app as app
import miniworlds.tools.method_caller as method_caller


class Timed():
    """Base class for all timer objects.

    Registered timers are ticked once per frame by the world's main loop.
    You normally use `ActionTimer` or `LoopActionTimer` (or their decorator
    shortcuts `@timer` and `@loop`) instead of subclassing `Timed` directly.
    """

    def __init__(self):
        self.world = app.App.get_running_world()
        self.world._timed_objects.append(self)
        self.running = True

    def tick(self):
        """Advances the timer by one frame.

        Subclasses override this method when they need special timing logic.
        """
        self.time = self.time - 1

    def unregister(self):
        """Removes the timer from the current world.

        Use this to stop a running timer before it fires again.
        """
        if self in self.world._timed_objects:
            self.world._timed_objects.remove(self)
        del (self)


[Doku] class Timer(Timed): """Timer that calls `act()` repeatedly after a frame interval. Subclass this and override `act()` when you need custom timer logic. For most use-cases prefer `ActionTimer` or the `@timer` decorator. Args: time: Frame interval. Examples: :: class BlinkTimer(Timer): def act(self): actor.visible = not actor.visible """ @staticmethod def _validate_interval(time: int) -> None: if not isinstance(time, int): raise TypeError(f"Timer interval must be int, got {type(time)}") if time <= 0: raise ValueError(f"Timer interval must be > 0, got {time}") def __init__(self, time: int): self._validate_interval(time) super().__init__() self.time = time self.actual_time = 0
[Doku] def tick(self): """Count frames and call `act()` when the interval is reached. This method is called automatically once per frame by the world. """ self.actual_time += 1 if self.actual_time % self.time == 0: self.act()
[Doku] def act(self): """Hook method that is executed when the timer interval is reached. Subclasses override this method with the action that should happen after the configured number of frames. """ pass
[Doku] class ActionTimer(Timer): """Calls a method after `time` frames. Args: time: Frames to wait before calling the method. method: Method to call. arguments: Optional single argument passed to the method. Examples: :: ActionTimer(48, player.move, 2) @timer(frames=24) def moving(): player.move() """
[Doku] def __init__(self, time: int, method: callable, arguments=None): """Create a one-shot action timer. Args: time: Frames to wait before calling the method. method: Method to call. arguments: Optional single argument passed to the method. """ super().__init__(time) self.method: callable = method if arguments or arguments == 0: self.arguments = [arguments] else: self.arguments = None
[Doku] def act(self): """Calls the stored method once and then unregisters the timer. This is the behavior behind `@timer` and one-shot delayed actions. """ self._call_method() self.unregister()
def _call_method(self): method_caller.call_method(self.method, self.arguments, allow_none=False)
[Doku] class LoopActionTimer(ActionTimer): """Calls a method after `time` frames repeatedly until the timer is unregistered. Examples: :: LoopActionTimer(48, player.move, 2) @loop(frames=24) def moving(): player.move() """
[Doku] def act(self): self._call_method()
[Doku] def timer(*args, **kwargs): """Decorate a function to run once after a number of frames. Args: frames: Frames to wait before the function runs. Examples: :: @timer(frames=24) def moving(): player.move() """ def inner(method): timer = ActionTimer(kwargs["frames"], method) return timer return inner
[Doku] def loop(*args, **kwargs): """Decorate a function to run repeatedly after a frame interval. Args: frames: Frames between function calls. Examples: :: @loop(frames=24) def moving(): player.move() """ def inner(method): timer = LoopActionTimer(kwargs["frames"], method) return timer return inner