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__(
 25        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    ):
 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            ba.print_error('got shiftdelay < 0')
 50            shiftdelay = 0.0
 51        self._project_scale = project_scale
 52        self.node = ba.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 ba.app.vr_mode:
 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            ba.timer(
 83                shiftdelay,
 84                ba.WeakCall(self._shift, positionadjusted, positionadjusted2),
 85            )
 86            if jitter > 0.0:
 87                ba.timer(
 88                    shiftdelay + 0.25,
 89                    ba.WeakCall(
 90                        self._jitter, positionadjusted2, jitter * scale
 91                    ),
 92                )
 93        color_combine = ba.newnode(
 94            'combine',
 95            owner=self.node,
 96            attrs={'input2': color[2], 'input3': 1.0, 'size': 4},
 97        )
 98        if trail:
 99            trailcolor_n = ba.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            ba.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            ba.animate(
129                color_combine,
130                'input0',
131                {0: color[0] * mult, tm1: color[0], tm2: color[0] * mult},
132                loop=True,
133            )
134            ba.animate(
135                color_combine,
136                'input1',
137                {0: color[1] * mult, tm1: color[1], tm2: color[1] * mult},
138                loop=True,
139            )
140            ba.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        ba.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            ba.timer(lifespan, ba.WeakCall(self.handlemessage, ba.DieMessage()))
159
160    def handlemessage(self, msg: Any) -> Any:
161        assert not self.expired
162        if isinstance(msg, ba.DieMessage):
163            if not self._dying and self.node:
164                self._dying = True
165                if msg.immediate:
166                    self.node.delete()
167                else:
168                    ba.animate(
169                        self.node,
170                        'project_scale',
171                        {
172                            0.0: 1 * self._project_scale,
173                            0.6: 1.2 * self._project_scale,
174                        },
175                    )
176                    ba.animate(self.node, 'opacity', {0.0: 1, 0.3: 0})
177                    ba.animate(self.node, 'trail_opacity', {0.0: 1, 0.6: 0})
178                    ba.timer(0.7, self.node.delete)
179            return None
180        return super().handlemessage(msg)
181
182    def _jitter(
183        self, position: tuple[float, float], jitter_amount: float
184    ) -> None:
185        if not self.node:
186            return
187        cmb = ba.newnode('combine', owner=self.node, attrs={'size': 2})
188        for index, attr in enumerate(['input0', 'input1']):
189            keys = {}
190            timeval = 0.0
191            # gen some random keys for that stop-motion-y look
192            for _i in range(10):
193                keys[timeval] = (
194                    position[index]
195                    + (random.random() - 0.5) * jitter_amount * 1.6
196                )
197                timeval += random.random() * 0.1
198            ba.animate(cmb, attr, keys, loop=True)
199        cmb.connectattr('output', self.node, 'position')
200
201    def _shift(
202        self, position1: tuple[float, float], position2: tuple[float, float]
203    ) -> None:
204        if not self.node:
205            return
206        cmb = ba.newnode('combine', owner=self.node, attrs={'size': 2})
207        ba.animate(cmb, 'input0', {0.0: position1[0], 0.25: position2[0]})
208        ba.animate(cmb, 'input1', {0.0: position1[1], 0.25: position2[1]})
209        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__(
 26        self,
 27        text: str | ba.Lstr,
 28        position: tuple[float, float] = (0.0, 0.0),
 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            ba.print_error('got shiftdelay < 0')
 51            shiftdelay = 0.0
 52        self._project_scale = project_scale
 53        self.node = ba.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 ba.app.vr_mode:
 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            ba.timer(
 84                shiftdelay,
 85                ba.WeakCall(self._shift, positionadjusted, positionadjusted2),
 86            )
 87            if jitter > 0.0:
 88                ba.timer(
 89                    shiftdelay + 0.25,
 90                    ba.WeakCall(
 91                        self._jitter, positionadjusted2, jitter * scale
 92                    ),
 93                )
 94        color_combine = ba.newnode(
 95            'combine',
 96            owner=self.node,
 97            attrs={'input2': color[2], 'input3': 1.0, 'size': 4},
 98        )
 99        if trail:
100            trailcolor_n = ba.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            ba.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            ba.animate(
130                color_combine,
131                'input0',
132                {0: color[0] * mult, tm1: color[0], tm2: color[0] * mult},
133                loop=True,
134            )
135            ba.animate(
136                color_combine,
137                'input1',
138                {0: color[1] * mult, tm1: color[1], tm2: color[1] * mult},
139                loop=True,
140            )
141            ba.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        ba.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            ba.timer(lifespan, ba.WeakCall(self.handlemessage, ba.DieMessage()))
160
161    def handlemessage(self, msg: Any) -> Any:
162        assert not self.expired
163        if isinstance(msg, ba.DieMessage):
164            if not self._dying and self.node:
165                self._dying = True
166                if msg.immediate:
167                    self.node.delete()
168                else:
169                    ba.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                    ba.animate(self.node, 'opacity', {0.0: 1, 0.3: 0})
178                    ba.animate(self.node, 'trail_opacity', {0.0: 1, 0.6: 0})
179                    ba.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 = ba.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            ba.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 = ba.newnode('combine', owner=self.node, attrs={'size': 2})
208        ba.animate(cmb, 'input0', {0.0: position1[0], 0.25: position2[0]})
209        ba.animate(cmb, 'input1', {0.0: position1[1], 0.25: position2[1]})
210        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__(
 26        self,
 27        text: str | ba.Lstr,
 28        position: tuple[float, float] = (0.0, 0.0),
 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            ba.print_error('got shiftdelay < 0')
 51            shiftdelay = 0.0
 52        self._project_scale = project_scale
 53        self.node = ba.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 ba.app.vr_mode:
 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            ba.timer(
 84                shiftdelay,
 85                ba.WeakCall(self._shift, positionadjusted, positionadjusted2),
 86            )
 87            if jitter > 0.0:
 88                ba.timer(
 89                    shiftdelay + 0.25,
 90                    ba.WeakCall(
 91                        self._jitter, positionadjusted2, jitter * scale
 92                    ),
 93                )
 94        color_combine = ba.newnode(
 95            'combine',
 96            owner=self.node,
 97            attrs={'input2': color[2], 'input3': 1.0, 'size': 4},
 98        )
 99        if trail:
100            trailcolor_n = ba.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            ba.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            ba.animate(
130                color_combine,
131                'input0',
132                {0: color[0] * mult, tm1: color[0], tm2: color[0] * mult},
133                loop=True,
134            )
135            ba.animate(
136                color_combine,
137                'input1',
138                {0: color[1] * mult, tm1: color[1], tm2: color[1] * mult},
139                loop=True,
140            )
141            ba.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        ba.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            ba.timer(lifespan, ba.WeakCall(self.handlemessage, ba.DieMessage()))

Instantiates an Actor in the current ba.Activity.

def handlemessage(self, msg: Any) -> Any:
161    def handlemessage(self, msg: Any) -> Any:
162        assert not self.expired
163        if isinstance(msg, ba.DieMessage):
164            if not self._dying and self.node:
165                self._dying = True
166                if msg.immediate:
167                    self.node.delete()
168                else:
169                    ba.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                    ba.animate(self.node, 'opacity', {0.0: 1, 0.3: 0})
178                    ba.animate(self.node, 'trail_opacity', {0.0: 1, 0.6: 0})
179                    ba.timer(0.7, self.node.delete)
180            return None
181        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