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