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

Duration is provided in seconds.

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

Start the timer.

def on_expire(self) -> None:
73    def on_expire(self) -> None:
74        super().on_expire()
75
76        # Release callbacks/refs.
77        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