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.
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.Actor
s 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.