bascenev1lib.actor.zoomtext
Defined Actor(s).
1# Released under the MIT License. See LICENSE for details. 2# 3"""Defined Actor(s).""" 4 5from __future__ import annotations 6 7import random 8import logging 9from typing import TYPE_CHECKING, override 10 11import bascenev1 as bs 12 13if TYPE_CHECKING: 14 from typing import Any, Sequence 15 16 17class ZoomText(bs.Actor): 18 """Big Zooming Text. 19 20 Category: Gameplay Classes 21 22 Used for things such as the 'BOB WINS' victory messages. 23 """ 24 25 def __init__( 26 self, 27 text: str | bs.Lstr, 28 position: tuple[float, float] = (0.0, 0.0), 29 *, 30 shiftposition: tuple[float, float] | None = None, 31 shiftdelay: float | None = None, 32 lifespan: float | None = None, 33 flash: bool = True, 34 trail: bool = True, 35 h_align: str = 'center', 36 color: Sequence[float] = (0.9, 0.4, 0.0), 37 jitter: float = 0.0, 38 trailcolor: Sequence[float] = (1.0, 0.35, 0.1, 0.0), 39 scale: float = 1.0, 40 project_scale: float = 1.0, 41 tilt_translate: float = 0.0, 42 maxwidth: float | None = None, 43 ): 44 # pylint: disable=too-many-locals 45 super().__init__() 46 self._dying = False 47 positionadjusted = (position[0], position[1] - 100) 48 if shiftdelay is None: 49 shiftdelay = 2.500 50 if shiftdelay < 0.0: 51 logging.error('got shiftdelay < 0') 52 shiftdelay = 0.0 53 self._project_scale = project_scale 54 self.node = bs.newnode( 55 'text', 56 delegate=self, 57 attrs={ 58 'position': positionadjusted, 59 'big': True, 60 'text': text, 61 'trail': trail, 62 'vr_depth': 0, 63 'shadow': 0.0 if trail else 0.3, 64 'scale': scale, 65 'maxwidth': maxwidth if maxwidth is not None else 0.0, 66 'tilt_translate': tilt_translate, 67 'h_align': h_align, 68 'v_align': 'center', 69 }, 70 ) 71 72 # we never jitter in vr mode.. 73 if bs.app.env.vr: 74 jitter = 0.0 75 76 # if they want jitter, animate its position slightly... 77 if jitter > 0.0: 78 self._jitter(positionadjusted, jitter * scale) 79 80 # if they want shifting, move to the shift position and 81 # then resume jittering 82 if shiftposition is not None: 83 positionadjusted2 = (shiftposition[0], shiftposition[1] - 100) 84 bs.timer( 85 shiftdelay, 86 bs.WeakCall(self._shift, positionadjusted, positionadjusted2), 87 ) 88 if jitter > 0.0: 89 bs.timer( 90 shiftdelay + 0.25, 91 bs.WeakCall( 92 self._jitter, positionadjusted2, jitter * scale 93 ), 94 ) 95 color_combine = bs.newnode( 96 'combine', 97 owner=self.node, 98 attrs={'input2': color[2], 'input3': 1.0, 'size': 4}, 99 ) 100 if trail: 101 trailcolor_n = bs.newnode( 102 'combine', 103 owner=self.node, 104 attrs={ 105 'size': 3, 106 'input0': trailcolor[0], 107 'input1': trailcolor[1], 108 'input2': trailcolor[2], 109 }, 110 ) 111 trailcolor_n.connectattr('output', self.node, 'trailcolor') 112 basemult = 0.85 113 bs.animate( 114 self.node, 115 'trail_project_scale', 116 { 117 0: 0 * project_scale, 118 basemult * 0.201: 0.6 * project_scale, 119 basemult * 0.347: 0.8 * project_scale, 120 basemult * 0.478: 0.9 * project_scale, 121 basemult * 0.595: 0.93 * project_scale, 122 basemult * 0.748: 0.95 * project_scale, 123 basemult * 0.941: 0.95 * project_scale, 124 }, 125 ) 126 if flash: 127 mult = 2.0 128 tm1 = 0.15 129 tm2 = 0.3 130 bs.animate( 131 color_combine, 132 'input0', 133 {0: color[0] * mult, tm1: color[0], tm2: color[0] * mult}, 134 loop=True, 135 ) 136 bs.animate( 137 color_combine, 138 'input1', 139 {0: color[1] * mult, tm1: color[1], tm2: color[1] * mult}, 140 loop=True, 141 ) 142 bs.animate( 143 color_combine, 144 'input2', 145 {0: color[2] * mult, tm1: color[2], tm2: color[2] * mult}, 146 loop=True, 147 ) 148 else: 149 color_combine.input0 = color[0] 150 color_combine.input1 = color[1] 151 color_combine.connectattr('output', self.node, 'color') 152 bs.animate( 153 self.node, 154 'project_scale', 155 {0: 0, 0.27: 1.05 * project_scale, 0.3: 1 * project_scale}, 156 ) 157 158 # if they give us a lifespan, kill ourself down the line 159 if lifespan is not None: 160 bs.timer(lifespan, bs.WeakCall(self.handlemessage, bs.DieMessage())) 161 162 @override 163 def handlemessage(self, msg: Any) -> Any: 164 assert not self.expired 165 if isinstance(msg, bs.DieMessage): 166 if not self._dying and self.node: 167 self._dying = True 168 if msg.immediate: 169 self.node.delete() 170 else: 171 bs.animate( 172 self.node, 173 'project_scale', 174 { 175 0.0: 1 * self._project_scale, 176 0.6: 1.2 * self._project_scale, 177 }, 178 ) 179 bs.animate(self.node, 'opacity', {0.0: 1, 0.3: 0}) 180 bs.animate(self.node, 'trail_opacity', {0.0: 1, 0.6: 0}) 181 bs.timer(0.7, self.node.delete) 182 return None 183 return super().handlemessage(msg) 184 185 def _jitter( 186 self, position: tuple[float, float], jitter_amount: float 187 ) -> None: 188 if not self.node: 189 return 190 cmb = bs.newnode('combine', owner=self.node, attrs={'size': 2}) 191 for index, attr in enumerate(['input0', 'input1']): 192 keys = {} 193 timeval = 0.0 194 # gen some random keys for that stop-motion-y look 195 for _i in range(10): 196 keys[timeval] = ( 197 position[index] 198 + (random.random() - 0.5) * jitter_amount * 1.6 199 ) 200 timeval += random.random() * 0.1 201 bs.animate(cmb, attr, keys, loop=True) 202 cmb.connectattr('output', self.node, 'position') 203 204 def _shift( 205 self, position1: tuple[float, float], position2: tuple[float, float] 206 ) -> None: 207 if not self.node: 208 return 209 cmb = bs.newnode('combine', owner=self.node, attrs={'size': 2}) 210 bs.animate(cmb, 'input0', {0.0: position1[0], 0.25: position2[0]}) 211 bs.animate(cmb, 'input1', {0.0: position1[1], 0.25: position2[1]}) 212 cmb.connectattr('output', self.node, 'position')
class
ZoomText(bascenev1._actor.Actor):
18class ZoomText(bs.Actor): 19 """Big Zooming Text. 20 21 Category: Gameplay Classes 22 23 Used for things such as the 'BOB WINS' victory messages. 24 """ 25 26 def __init__( 27 self, 28 text: str | bs.Lstr, 29 position: tuple[float, float] = (0.0, 0.0), 30 *, 31 shiftposition: tuple[float, float] | None = None, 32 shiftdelay: float | None = None, 33 lifespan: float | None = None, 34 flash: bool = True, 35 trail: bool = True, 36 h_align: str = 'center', 37 color: Sequence[float] = (0.9, 0.4, 0.0), 38 jitter: float = 0.0, 39 trailcolor: Sequence[float] = (1.0, 0.35, 0.1, 0.0), 40 scale: float = 1.0, 41 project_scale: float = 1.0, 42 tilt_translate: float = 0.0, 43 maxwidth: float | None = None, 44 ): 45 # pylint: disable=too-many-locals 46 super().__init__() 47 self._dying = False 48 positionadjusted = (position[0], position[1] - 100) 49 if shiftdelay is None: 50 shiftdelay = 2.500 51 if shiftdelay < 0.0: 52 logging.error('got shiftdelay < 0') 53 shiftdelay = 0.0 54 self._project_scale = project_scale 55 self.node = bs.newnode( 56 'text', 57 delegate=self, 58 attrs={ 59 'position': positionadjusted, 60 'big': True, 61 'text': text, 62 'trail': trail, 63 'vr_depth': 0, 64 'shadow': 0.0 if trail else 0.3, 65 'scale': scale, 66 'maxwidth': maxwidth if maxwidth is not None else 0.0, 67 'tilt_translate': tilt_translate, 68 'h_align': h_align, 69 'v_align': 'center', 70 }, 71 ) 72 73 # we never jitter in vr mode.. 74 if bs.app.env.vr: 75 jitter = 0.0 76 77 # if they want jitter, animate its position slightly... 78 if jitter > 0.0: 79 self._jitter(positionadjusted, jitter * scale) 80 81 # if they want shifting, move to the shift position and 82 # then resume jittering 83 if shiftposition is not None: 84 positionadjusted2 = (shiftposition[0], shiftposition[1] - 100) 85 bs.timer( 86 shiftdelay, 87 bs.WeakCall(self._shift, positionadjusted, positionadjusted2), 88 ) 89 if jitter > 0.0: 90 bs.timer( 91 shiftdelay + 0.25, 92 bs.WeakCall( 93 self._jitter, positionadjusted2, jitter * scale 94 ), 95 ) 96 color_combine = bs.newnode( 97 'combine', 98 owner=self.node, 99 attrs={'input2': color[2], 'input3': 1.0, 'size': 4}, 100 ) 101 if trail: 102 trailcolor_n = bs.newnode( 103 'combine', 104 owner=self.node, 105 attrs={ 106 'size': 3, 107 'input0': trailcolor[0], 108 'input1': trailcolor[1], 109 'input2': trailcolor[2], 110 }, 111 ) 112 trailcolor_n.connectattr('output', self.node, 'trailcolor') 113 basemult = 0.85 114 bs.animate( 115 self.node, 116 'trail_project_scale', 117 { 118 0: 0 * project_scale, 119 basemult * 0.201: 0.6 * project_scale, 120 basemult * 0.347: 0.8 * project_scale, 121 basemult * 0.478: 0.9 * project_scale, 122 basemult * 0.595: 0.93 * project_scale, 123 basemult * 0.748: 0.95 * project_scale, 124 basemult * 0.941: 0.95 * project_scale, 125 }, 126 ) 127 if flash: 128 mult = 2.0 129 tm1 = 0.15 130 tm2 = 0.3 131 bs.animate( 132 color_combine, 133 'input0', 134 {0: color[0] * mult, tm1: color[0], tm2: color[0] * mult}, 135 loop=True, 136 ) 137 bs.animate( 138 color_combine, 139 'input1', 140 {0: color[1] * mult, tm1: color[1], tm2: color[1] * mult}, 141 loop=True, 142 ) 143 bs.animate( 144 color_combine, 145 'input2', 146 {0: color[2] * mult, tm1: color[2], tm2: color[2] * mult}, 147 loop=True, 148 ) 149 else: 150 color_combine.input0 = color[0] 151 color_combine.input1 = color[1] 152 color_combine.connectattr('output', self.node, 'color') 153 bs.animate( 154 self.node, 155 'project_scale', 156 {0: 0, 0.27: 1.05 * project_scale, 0.3: 1 * project_scale}, 157 ) 158 159 # if they give us a lifespan, kill ourself down the line 160 if lifespan is not None: 161 bs.timer(lifespan, bs.WeakCall(self.handlemessage, bs.DieMessage())) 162 163 @override 164 def handlemessage(self, msg: Any) -> Any: 165 assert not self.expired 166 if isinstance(msg, bs.DieMessage): 167 if not self._dying and self.node: 168 self._dying = True 169 if msg.immediate: 170 self.node.delete() 171 else: 172 bs.animate( 173 self.node, 174 'project_scale', 175 { 176 0.0: 1 * self._project_scale, 177 0.6: 1.2 * self._project_scale, 178 }, 179 ) 180 bs.animate(self.node, 'opacity', {0.0: 1, 0.3: 0}) 181 bs.animate(self.node, 'trail_opacity', {0.0: 1, 0.6: 0}) 182 bs.timer(0.7, self.node.delete) 183 return None 184 return super().handlemessage(msg) 185 186 def _jitter( 187 self, position: tuple[float, float], jitter_amount: float 188 ) -> None: 189 if not self.node: 190 return 191 cmb = bs.newnode('combine', owner=self.node, attrs={'size': 2}) 192 for index, attr in enumerate(['input0', 'input1']): 193 keys = {} 194 timeval = 0.0 195 # gen some random keys for that stop-motion-y look 196 for _i in range(10): 197 keys[timeval] = ( 198 position[index] 199 + (random.random() - 0.5) * jitter_amount * 1.6 200 ) 201 timeval += random.random() * 0.1 202 bs.animate(cmb, attr, keys, loop=True) 203 cmb.connectattr('output', self.node, 'position') 204 205 def _shift( 206 self, position1: tuple[float, float], position2: tuple[float, float] 207 ) -> None: 208 if not self.node: 209 return 210 cmb = bs.newnode('combine', owner=self.node, attrs={'size': 2}) 211 bs.animate(cmb, 'input0', {0.0: position1[0], 0.25: position2[0]}) 212 bs.animate(cmb, 'input1', {0.0: position1[1], 0.25: position2[1]}) 213 cmb.connectattr('output', self.node, 'position')
Big Zooming Text.
Category: Gameplay Classes
Used for things such as the 'BOB WINS' victory messages.
ZoomText( text: str | babase.Lstr, position: tuple[float, float] = (0.0, 0.0), *, shiftposition: tuple[float, float] | None = None, shiftdelay: float | None = None, lifespan: float | None = None, flash: bool = True, trail: bool = True, h_align: str = 'center', color: Sequence[float] = (0.9, 0.4, 0.0), jitter: float = 0.0, trailcolor: Sequence[float] = (1.0, 0.35, 0.1, 0.0), scale: float = 1.0, project_scale: float = 1.0, tilt_translate: float = 0.0, maxwidth: float | None = None)
26 def __init__( 27 self, 28 text: str | bs.Lstr, 29 position: tuple[float, float] = (0.0, 0.0), 30 *, 31 shiftposition: tuple[float, float] | None = None, 32 shiftdelay: float | None = None, 33 lifespan: float | None = None, 34 flash: bool = True, 35 trail: bool = True, 36 h_align: str = 'center', 37 color: Sequence[float] = (0.9, 0.4, 0.0), 38 jitter: float = 0.0, 39 trailcolor: Sequence[float] = (1.0, 0.35, 0.1, 0.0), 40 scale: float = 1.0, 41 project_scale: float = 1.0, 42 tilt_translate: float = 0.0, 43 maxwidth: float | None = None, 44 ): 45 # pylint: disable=too-many-locals 46 super().__init__() 47 self._dying = False 48 positionadjusted = (position[0], position[1] - 100) 49 if shiftdelay is None: 50 shiftdelay = 2.500 51 if shiftdelay < 0.0: 52 logging.error('got shiftdelay < 0') 53 shiftdelay = 0.0 54 self._project_scale = project_scale 55 self.node = bs.newnode( 56 'text', 57 delegate=self, 58 attrs={ 59 'position': positionadjusted, 60 'big': True, 61 'text': text, 62 'trail': trail, 63 'vr_depth': 0, 64 'shadow': 0.0 if trail else 0.3, 65 'scale': scale, 66 'maxwidth': maxwidth if maxwidth is not None else 0.0, 67 'tilt_translate': tilt_translate, 68 'h_align': h_align, 69 'v_align': 'center', 70 }, 71 ) 72 73 # we never jitter in vr mode.. 74 if bs.app.env.vr: 75 jitter = 0.0 76 77 # if they want jitter, animate its position slightly... 78 if jitter > 0.0: 79 self._jitter(positionadjusted, jitter * scale) 80 81 # if they want shifting, move to the shift position and 82 # then resume jittering 83 if shiftposition is not None: 84 positionadjusted2 = (shiftposition[0], shiftposition[1] - 100) 85 bs.timer( 86 shiftdelay, 87 bs.WeakCall(self._shift, positionadjusted, positionadjusted2), 88 ) 89 if jitter > 0.0: 90 bs.timer( 91 shiftdelay + 0.25, 92 bs.WeakCall( 93 self._jitter, positionadjusted2, jitter * scale 94 ), 95 ) 96 color_combine = bs.newnode( 97 'combine', 98 owner=self.node, 99 attrs={'input2': color[2], 'input3': 1.0, 'size': 4}, 100 ) 101 if trail: 102 trailcolor_n = bs.newnode( 103 'combine', 104 owner=self.node, 105 attrs={ 106 'size': 3, 107 'input0': trailcolor[0], 108 'input1': trailcolor[1], 109 'input2': trailcolor[2], 110 }, 111 ) 112 trailcolor_n.connectattr('output', self.node, 'trailcolor') 113 basemult = 0.85 114 bs.animate( 115 self.node, 116 'trail_project_scale', 117 { 118 0: 0 * project_scale, 119 basemult * 0.201: 0.6 * project_scale, 120 basemult * 0.347: 0.8 * project_scale, 121 basemult * 0.478: 0.9 * project_scale, 122 basemult * 0.595: 0.93 * project_scale, 123 basemult * 0.748: 0.95 * project_scale, 124 basemult * 0.941: 0.95 * project_scale, 125 }, 126 ) 127 if flash: 128 mult = 2.0 129 tm1 = 0.15 130 tm2 = 0.3 131 bs.animate( 132 color_combine, 133 'input0', 134 {0: color[0] * mult, tm1: color[0], tm2: color[0] * mult}, 135 loop=True, 136 ) 137 bs.animate( 138 color_combine, 139 'input1', 140 {0: color[1] * mult, tm1: color[1], tm2: color[1] * mult}, 141 loop=True, 142 ) 143 bs.animate( 144 color_combine, 145 'input2', 146 {0: color[2] * mult, tm1: color[2], tm2: color[2] * mult}, 147 loop=True, 148 ) 149 else: 150 color_combine.input0 = color[0] 151 color_combine.input1 = color[1] 152 color_combine.connectattr('output', self.node, 'color') 153 bs.animate( 154 self.node, 155 'project_scale', 156 {0: 0, 0.27: 1.05 * project_scale, 0.3: 1 * project_scale}, 157 ) 158 159 # if they give us a lifespan, kill ourself down the line 160 if lifespan is not None: 161 bs.timer(lifespan, bs.WeakCall(self.handlemessage, bs.DieMessage()))
Instantiates an Actor in the current bascenev1.Activity.
@override
def
handlemessage(self, msg: Any) -> Any:
163 @override 164 def handlemessage(self, msg: Any) -> Any: 165 assert not self.expired 166 if isinstance(msg, bs.DieMessage): 167 if not self._dying and self.node: 168 self._dying = True 169 if msg.immediate: 170 self.node.delete() 171 else: 172 bs.animate( 173 self.node, 174 'project_scale', 175 { 176 0.0: 1 * self._project_scale, 177 0.6: 1.2 * self._project_scale, 178 }, 179 ) 180 bs.animate(self.node, 'opacity', {0.0: 1, 0.3: 0}) 181 bs.animate(self.node, 'trail_opacity', {0.0: 1, 0.6: 0}) 182 bs.timer(0.7, self.node.delete) 183 return None 184 return super().handlemessage(msg)
General message handling; can be passed any message object.