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.

node
@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.