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

Instantiates an Actor in the current ba.Activity.

def handlemessage(self, msg: Any) -> Any:
156    def handlemessage(self, msg: Any) -> Any:
157        assert not self.expired
158        if isinstance(msg, ba.DieMessage):
159            if not self._dying and self.node:
160                self._dying = True
161                if msg.immediate:
162                    self.node.delete()
163                else:
164                    ba.animate(
165                        self.node, 'project_scale', {
166                            0.0: 1 * self._project_scale,
167                            0.6: 1.2 * self._project_scale
168                        })
169                    ba.animate(self.node, 'opacity', {0.0: 1, 0.3: 0})
170                    ba.animate(self.node, 'trail_opacity', {0.0: 1, 0.6: 0})
171                    ba.timer(0.7, self.node.delete)
172            return None
173        return super().handlemessage(msg)

General message handling; can be passed any message object.

Inherited Members
ba._actor.Actor
autoretain
on_expire
expired
exists
is_alive
activity
getactivity