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

Duration is provided in seconds.

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

Start the timer.

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

Called for remaining bascenev1.Actors when their activity dies.

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

Once an actor is expired (see bascenev1.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
bascenev1._actor.Actor
handlemessage
autoretain
expired
exists
is_alive
activity
getactivity