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, override
  8
  9import bascenev1 as bs
 10
 11if TYPE_CHECKING:
 12    from typing import Any, Callable
 13
 14
 15class OnScreenCountdown(bs.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 = bs.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 = bs.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: bs.getsound('announceTen'),
 54            9: bs.getsound('announceNine'),
 55            8: bs.getsound('announceEight'),
 56            7: bs.getsound('announceSeven'),
 57            6: bs.getsound('announceSix'),
 58            5: bs.getsound('announceFive'),
 59            4: bs.getsound('announceFour'),
 60            3: bs.getsound('announceThree'),
 61            2: bs.getsound('announceTwo'),
 62            1: bs.getsound('announceOne'),
 63        }
 64        self._timer: bs.Timer | None = None
 65
 66    def start(self) -> None:
 67        """Start the timer."""
 68        globalsnode = bs.getactivity().globalsnode
 69        globalsnode.connectattr('time', self.inputnode, 'time1')
 70        self.inputnode.time2 = (
 71            globalsnode.time + (self._timeremaining + 1) * 1000
 72        )
 73        self._timer = bs.Timer(1.0, self._update, repeat=True)
 74
 75    @override
 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 = bs.newnode('combine', owner=self.node, attrs={'size': 4})
 96            cmb.connectattr('output', self.node, 'color')
 97            bs.animate(cmb, 'input0', {0: 1.0, 0.15: 1.0}, loop=True)
 98            bs.animate(cmb, 'input1', {0: 1.0, 0.15: 0.5}, loop=True)
 99            bs.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            bs.getsound('tick').play()
103        if tval in self._countdownsounds:
104            self._countdownsounds[tval].play()
105        if tval <= 0 and not self._ended:
106            self._ended = True
107            if self._endcall is not None:
108                self._endcall()
class OnScreenCountdown(bascenev1._actor.Actor):
 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()

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 = 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

Duration is provided in seconds.

node
inputnode
def start(self) -> None:
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)

Start the timer.

@override
def on_expire(self) -> None:
76    @override
77    def on_expire(self) -> None:
78        super().on_expire()
79
80        # Release callbacks/refs.
81        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