bastd.actor.onscreencountdown

Defines Actor Type(s).

  1# Released under the MIT License. See LICENSE for details.
  2#
  3"""Defines Actor Type(s)."""
  4
  5from __future__ import annotations
  6
  7from typing import TYPE_CHECKING
  8
  9import ba
 10
 11if TYPE_CHECKING:
 12    from typing import Any, Callable
 13
 14
 15class OnScreenCountdown(ba.Actor):
 16    """A Handy On-Screen Timer.
 17
 18    category: Gameplay Classes
 19
 20    Useful for time-based games that count down to zero.
 21    """
 22
 23    def __init__(self, duration: int, endcall: Callable[[], Any] | None = None):
 24        """Duration is provided in seconds."""
 25        super().__init__()
 26        self._timeremaining = duration
 27        self._ended = False
 28        self._endcall = endcall
 29        self.node = ba.newnode(
 30            'text',
 31            attrs={
 32                'v_attach': 'top',
 33                'h_attach': 'center',
 34                'h_align': 'center',
 35                'color': (1, 1, 0.5, 1),
 36                'flatness': 0.5,
 37                'shadow': 0.5,
 38                'position': (0, -70),
 39                'scale': 1.4,
 40                'text': '',
 41            },
 42        )
 43        self.inputnode = ba.newnode(
 44            'timedisplay',
 45            attrs={
 46                'time2': duration * 1000,
 47                'timemax': duration * 1000,
 48                'timemin': 0,
 49            },
 50        )
 51        self.inputnode.connectattr('output', self.node, 'text')
 52        self._countdownsounds = {
 53            10: ba.getsound('announceTen'),
 54            9: ba.getsound('announceNine'),
 55            8: ba.getsound('announceEight'),
 56            7: ba.getsound('announceSeven'),
 57            6: ba.getsound('announceSix'),
 58            5: ba.getsound('announceFive'),
 59            4: ba.getsound('announceFour'),
 60            3: ba.getsound('announceThree'),
 61            2: ba.getsound('announceTwo'),
 62            1: ba.getsound('announceOne'),
 63        }
 64        self._timer: ba.Timer | None = None
 65
 66    def start(self) -> None:
 67        """Start the timer."""
 68        globalsnode = ba.getactivity().globalsnode
 69        globalsnode.connectattr('time', self.inputnode, 'time1')
 70        self.inputnode.time2 = (
 71            globalsnode.time + (self._timeremaining + 1) * 1000
 72        )
 73        self._timer = ba.Timer(1.0, self._update, repeat=True)
 74
 75    def on_expire(self) -> None:
 76        super().on_expire()
 77
 78        # Release callbacks/refs.
 79        self._endcall = None
 80
 81    def _update(self, forcevalue: int | None = None) -> None:
 82        if forcevalue is not None:
 83            tval = forcevalue
 84        else:
 85            self._timeremaining = max(0, self._timeremaining - 1)
 86            tval = self._timeremaining
 87
 88        # if there's a countdown sound for this time that we
 89        # haven't played yet, play it
 90        if tval == 10:
 91            assert self.node
 92            assert isinstance(self.node.scale, float)
 93            self.node.scale *= 1.2
 94            cmb = ba.newnode('combine', owner=self.node, attrs={'size': 4})
 95            cmb.connectattr('output', self.node, 'color')
 96            ba.animate(cmb, 'input0', {0: 1.0, 0.15: 1.0}, loop=True)
 97            ba.animate(cmb, 'input1', {0: 1.0, 0.15: 0.5}, loop=True)
 98            ba.animate(cmb, 'input2', {0: 0.1, 0.15: 0.0}, loop=True)
 99            cmb.input3 = 1.0
100        if tval <= 10 and not self._ended:
101            ba.playsound(ba.getsound('tick'))
102        if tval in self._countdownsounds:
103            ba.playsound(self._countdownsounds[tval])
104        if tval <= 0 and not self._ended:
105            self._ended = True
106            if self._endcall is not None:
107                self._endcall()
class OnScreenCountdown(ba._actor.Actor):
 16class OnScreenCountdown(ba.Actor):
 17    """A Handy On-Screen Timer.
 18
 19    category: Gameplay Classes
 20
 21    Useful for time-based games that count down to zero.
 22    """
 23
 24    def __init__(self, duration: int, endcall: Callable[[], Any] | None = None):
 25        """Duration is provided in seconds."""
 26        super().__init__()
 27        self._timeremaining = duration
 28        self._ended = False
 29        self._endcall = endcall
 30        self.node = ba.newnode(
 31            'text',
 32            attrs={
 33                'v_attach': 'top',
 34                'h_attach': 'center',
 35                'h_align': 'center',
 36                'color': (1, 1, 0.5, 1),
 37                'flatness': 0.5,
 38                'shadow': 0.5,
 39                'position': (0, -70),
 40                'scale': 1.4,
 41                'text': '',
 42            },
 43        )
 44        self.inputnode = ba.newnode(
 45            'timedisplay',
 46            attrs={
 47                'time2': duration * 1000,
 48                'timemax': duration * 1000,
 49                'timemin': 0,
 50            },
 51        )
 52        self.inputnode.connectattr('output', self.node, 'text')
 53        self._countdownsounds = {
 54            10: ba.getsound('announceTen'),
 55            9: ba.getsound('announceNine'),
 56            8: ba.getsound('announceEight'),
 57            7: ba.getsound('announceSeven'),
 58            6: ba.getsound('announceSix'),
 59            5: ba.getsound('announceFive'),
 60            4: ba.getsound('announceFour'),
 61            3: ba.getsound('announceThree'),
 62            2: ba.getsound('announceTwo'),
 63            1: ba.getsound('announceOne'),
 64        }
 65        self._timer: ba.Timer | None = None
 66
 67    def start(self) -> None:
 68        """Start the timer."""
 69        globalsnode = ba.getactivity().globalsnode
 70        globalsnode.connectattr('time', self.inputnode, 'time1')
 71        self.inputnode.time2 = (
 72            globalsnode.time + (self._timeremaining + 1) * 1000
 73        )
 74        self._timer = ba.Timer(1.0, self._update, repeat=True)
 75
 76    def on_expire(self) -> None:
 77        super().on_expire()
 78
 79        # Release callbacks/refs.
 80        self._endcall = None
 81
 82    def _update(self, forcevalue: int | None = None) -> None:
 83        if forcevalue is not None:
 84            tval = forcevalue
 85        else:
 86            self._timeremaining = max(0, self._timeremaining - 1)
 87            tval = self._timeremaining
 88
 89        # if there's a countdown sound for this time that we
 90        # haven't played yet, play it
 91        if tval == 10:
 92            assert self.node
 93            assert isinstance(self.node.scale, float)
 94            self.node.scale *= 1.2
 95            cmb = ba.newnode('combine', owner=self.node, attrs={'size': 4})
 96            cmb.connectattr('output', self.node, 'color')
 97            ba.animate(cmb, 'input0', {0: 1.0, 0.15: 1.0}, loop=True)
 98            ba.animate(cmb, 'input1', {0: 1.0, 0.15: 0.5}, loop=True)
 99            ba.animate(cmb, 'input2', {0: 0.1, 0.15: 0.0}, loop=True)
100            cmb.input3 = 1.0
101        if tval <= 10 and not self._ended:
102            ba.playsound(ba.getsound('tick'))
103        if tval in self._countdownsounds:
104            ba.playsound(self._countdownsounds[tval])
105        if tval <= 0 and not self._ended:
106            self._ended = True
107            if self._endcall is not None:
108                self._endcall()

A Handy On-Screen Timer.

category: Gameplay Classes

Useful for time-based games that count down to zero.

OnScreenCountdown(duration: int, endcall: Optional[Callable[[], Any]] = None)
24    def __init__(self, duration: int, endcall: Callable[[], Any] | None = None):
25        """Duration is provided in seconds."""
26        super().__init__()
27        self._timeremaining = duration
28        self._ended = False
29        self._endcall = endcall
30        self.node = ba.newnode(
31            'text',
32            attrs={
33                'v_attach': 'top',
34                'h_attach': 'center',
35                'h_align': 'center',
36                'color': (1, 1, 0.5, 1),
37                'flatness': 0.5,
38                'shadow': 0.5,
39                'position': (0, -70),
40                'scale': 1.4,
41                'text': '',
42            },
43        )
44        self.inputnode = ba.newnode(
45            'timedisplay',
46            attrs={
47                'time2': duration * 1000,
48                'timemax': duration * 1000,
49                'timemin': 0,
50            },
51        )
52        self.inputnode.connectattr('output', self.node, 'text')
53        self._countdownsounds = {
54            10: ba.getsound('announceTen'),
55            9: ba.getsound('announceNine'),
56            8: ba.getsound('announceEight'),
57            7: ba.getsound('announceSeven'),
58            6: ba.getsound('announceSix'),
59            5: ba.getsound('announceFive'),
60            4: ba.getsound('announceFour'),
61            3: ba.getsound('announceThree'),
62            2: ba.getsound('announceTwo'),
63            1: ba.getsound('announceOne'),
64        }
65        self._timer: ba.Timer | None = None

Duration is provided in seconds.

def start(self) -> None:
67    def start(self) -> None:
68        """Start the timer."""
69        globalsnode = ba.getactivity().globalsnode
70        globalsnode.connectattr('time', self.inputnode, 'time1')
71        self.inputnode.time2 = (
72            globalsnode.time + (self._timeremaining + 1) * 1000
73        )
74        self._timer = ba.Timer(1.0, self._update, repeat=True)

Start the timer.

def on_expire(self) -> None:
76    def on_expire(self) -> None:
77        super().on_expire()
78
79        # Release callbacks/refs.
80        self._endcall = None

Called for remaining ba.Actors when their ba.Activity shuts down.

Actors can use this opportunity to clear callbacks or other references which have the potential of keeping the ba.Activity alive inadvertently (Activities can not exit cleanly while any Python references to them remain.)

Once an actor is expired (see ba.Actor.is_expired()) it should no longer perform any game-affecting operations (creating, modifying, or deleting nodes, media, timers, etc.) Attempts to do so will likely result in errors.

Inherited Members
ba._actor.Actor
handlemessage
autoretain
expired
exists
is_alive
activity
getactivity