bastd.actor.image

Defines Actor(s).

  1# Released under the MIT License. See LICENSE for details.
  2#
  3"""Defines Actor(s)."""
  4
  5from __future__ import annotations
  6
  7from enum import Enum
  8from typing import TYPE_CHECKING
  9
 10import ba
 11
 12if TYPE_CHECKING:
 13    from typing import Any, Sequence
 14
 15
 16class Image(ba.Actor):
 17    """Just a wrapped up image node with a few tricks up its sleeve."""
 18
 19    class Transition(Enum):
 20        """Transition types we support."""
 21
 22        FADE_IN = 'fade_in'
 23        IN_RIGHT = 'in_right'
 24        IN_LEFT = 'in_left'
 25        IN_BOTTOM = 'in_bottom'
 26        IN_BOTTOM_SLOW = 'in_bottom_slow'
 27        IN_TOP_SLOW = 'in_top_slow'
 28
 29    class Attach(Enum):
 30        """Attach types we support."""
 31
 32        CENTER = 'center'
 33        TOP_CENTER = 'topCenter'
 34        TOP_LEFT = 'topLeft'
 35        BOTTOM_CENTER = 'bottomCenter'
 36
 37    def __init__(
 38        self,
 39        texture: ba.Texture | dict[str, Any],
 40        position: tuple[float, float] = (0, 0),
 41        transition: Transition | None = None,
 42        transition_delay: float = 0.0,
 43        attach: Attach = Attach.CENTER,
 44        color: Sequence[float] = (1.0, 1.0, 1.0, 1.0),
 45        scale: tuple[float, float] = (100.0, 100.0),
 46        transition_out_delay: float | None = None,
 47        model_opaque: ba.Model | None = None,
 48        model_transparent: ba.Model | None = None,
 49        vr_depth: float = 0.0,
 50        host_only: bool = False,
 51        front: bool = False,
 52    ):
 53        # pylint: disable=too-many-statements
 54        # pylint: disable=too-many-branches
 55        # pylint: disable=too-many-locals
 56        super().__init__()
 57
 58        # If they provided a dict as texture, assume its an icon.
 59        # otherwise its just a texture value itself.
 60        mask_texture: ba.Texture | None
 61        if isinstance(texture, dict):
 62            tint_color = texture['tint_color']
 63            tint2_color = texture['tint2_color']
 64            tint_texture = texture['tint_texture']
 65            texture = texture['texture']
 66            mask_texture = ba.gettexture('characterIconMask')
 67        else:
 68            tint_color = (1, 1, 1)
 69            tint2_color = None
 70            tint_texture = None
 71            mask_texture = None
 72
 73        self.node = ba.newnode(
 74            'image',
 75            attrs={
 76                'texture': texture,
 77                'tint_color': tint_color,
 78                'tint_texture': tint_texture,
 79                'position': position,
 80                'vr_depth': vr_depth,
 81                'scale': scale,
 82                'mask_texture': mask_texture,
 83                'color': color,
 84                'absolute_scale': True,
 85                'host_only': host_only,
 86                'front': front,
 87                'attach': attach.value,
 88            },
 89            delegate=self,
 90        )
 91
 92        if model_opaque is not None:
 93            self.node.model_opaque = model_opaque
 94        if model_transparent is not None:
 95            self.node.model_transparent = model_transparent
 96        if tint2_color is not None:
 97            self.node.tint2_color = tint2_color
 98        if transition is self.Transition.FADE_IN:
 99            keys = {transition_delay: 0, transition_delay + 0.5: color[3]}
100            if transition_out_delay is not None:
101                keys[transition_delay + transition_out_delay] = color[3]
102                keys[transition_delay + transition_out_delay + 0.5] = 0
103            ba.animate(self.node, 'opacity', keys)
104        cmb = self.position_combine = ba.newnode(
105            'combine', owner=self.node, attrs={'size': 2}
106        )
107        if transition is self.Transition.IN_RIGHT:
108            keys = {
109                transition_delay: position[0] + 1200,
110                transition_delay + 0.2: position[0],
111            }
112            o_keys = {transition_delay: 0.0, transition_delay + 0.05: 1.0}
113            ba.animate(cmb, 'input0', keys)
114            cmb.input1 = position[1]
115            ba.animate(self.node, 'opacity', o_keys)
116        elif transition is self.Transition.IN_LEFT:
117            keys = {
118                transition_delay: position[0] - 1200,
119                transition_delay + 0.2: position[0],
120            }
121            o_keys = {transition_delay: 0.0, transition_delay + 0.05: 1.0}
122            if transition_out_delay is not None:
123                keys[transition_delay + transition_out_delay] = position[0]
124                keys[transition_delay + transition_out_delay + 200] = (
125                    -position[0] - 1200
126                )
127                o_keys[transition_delay + transition_out_delay + 0.15] = 1.0
128                o_keys[transition_delay + transition_out_delay + 0.2] = 0.0
129            ba.animate(cmb, 'input0', keys)
130            cmb.input1 = position[1]
131            ba.animate(self.node, 'opacity', o_keys)
132        elif transition is self.Transition.IN_BOTTOM_SLOW:
133            keys = {transition_delay: -400, transition_delay + 3.5: position[1]}
134            o_keys = {transition_delay: 0.0, transition_delay + 2.0: 1.0}
135            cmb.input0 = position[0]
136            ba.animate(cmb, 'input1', keys)
137            ba.animate(self.node, 'opacity', o_keys)
138        elif transition is self.Transition.IN_BOTTOM:
139            keys = {transition_delay: -400, transition_delay + 0.2: position[1]}
140            o_keys = {transition_delay: 0.0, transition_delay + 0.05: 1.0}
141            if transition_out_delay is not None:
142                keys[transition_delay + transition_out_delay] = position[1]
143                keys[transition_delay + transition_out_delay + 0.2] = -400
144                o_keys[transition_delay + transition_out_delay + 0.15] = 1.0
145                o_keys[transition_delay + transition_out_delay + 0.2] = 0.0
146            cmb.input0 = position[0]
147            ba.animate(cmb, 'input1', keys)
148            ba.animate(self.node, 'opacity', o_keys)
149        elif transition is self.Transition.IN_TOP_SLOW:
150            keys = {transition_delay: 400, transition_delay + 3.5: position[1]}
151            o_keys = {transition_delay: 0.0, transition_delay + 1.0: 1.0}
152            cmb.input0 = position[0]
153            ba.animate(cmb, 'input1', keys)
154            ba.animate(self.node, 'opacity', o_keys)
155        else:
156            assert transition is self.Transition.FADE_IN or transition is None
157            cmb.input0 = position[0]
158            cmb.input1 = position[1]
159        cmb.connectattr('output', self.node, 'position')
160
161        # If we're transitioning out, die at the end of it.
162        if transition_out_delay is not None:
163            ba.timer(
164                transition_delay + transition_out_delay + 1.0,
165                ba.WeakCall(self.handlemessage, ba.DieMessage()),
166            )
167
168    def handlemessage(self, msg: Any) -> Any:
169        assert not self.expired
170        if isinstance(msg, ba.DieMessage):
171            if self.node:
172                self.node.delete()
173            return None
174        return super().handlemessage(msg)
class Image(ba._actor.Actor):
 17class Image(ba.Actor):
 18    """Just a wrapped up image node with a few tricks up its sleeve."""
 19
 20    class Transition(Enum):
 21        """Transition types we support."""
 22
 23        FADE_IN = 'fade_in'
 24        IN_RIGHT = 'in_right'
 25        IN_LEFT = 'in_left'
 26        IN_BOTTOM = 'in_bottom'
 27        IN_BOTTOM_SLOW = 'in_bottom_slow'
 28        IN_TOP_SLOW = 'in_top_slow'
 29
 30    class Attach(Enum):
 31        """Attach types we support."""
 32
 33        CENTER = 'center'
 34        TOP_CENTER = 'topCenter'
 35        TOP_LEFT = 'topLeft'
 36        BOTTOM_CENTER = 'bottomCenter'
 37
 38    def __init__(
 39        self,
 40        texture: ba.Texture | dict[str, Any],
 41        position: tuple[float, float] = (0, 0),
 42        transition: Transition | None = None,
 43        transition_delay: float = 0.0,
 44        attach: Attach = Attach.CENTER,
 45        color: Sequence[float] = (1.0, 1.0, 1.0, 1.0),
 46        scale: tuple[float, float] = (100.0, 100.0),
 47        transition_out_delay: float | None = None,
 48        model_opaque: ba.Model | None = None,
 49        model_transparent: ba.Model | None = None,
 50        vr_depth: float = 0.0,
 51        host_only: bool = False,
 52        front: bool = False,
 53    ):
 54        # pylint: disable=too-many-statements
 55        # pylint: disable=too-many-branches
 56        # pylint: disable=too-many-locals
 57        super().__init__()
 58
 59        # If they provided a dict as texture, assume its an icon.
 60        # otherwise its just a texture value itself.
 61        mask_texture: ba.Texture | None
 62        if isinstance(texture, dict):
 63            tint_color = texture['tint_color']
 64            tint2_color = texture['tint2_color']
 65            tint_texture = texture['tint_texture']
 66            texture = texture['texture']
 67            mask_texture = ba.gettexture('characterIconMask')
 68        else:
 69            tint_color = (1, 1, 1)
 70            tint2_color = None
 71            tint_texture = None
 72            mask_texture = None
 73
 74        self.node = ba.newnode(
 75            'image',
 76            attrs={
 77                'texture': texture,
 78                'tint_color': tint_color,
 79                'tint_texture': tint_texture,
 80                'position': position,
 81                'vr_depth': vr_depth,
 82                'scale': scale,
 83                'mask_texture': mask_texture,
 84                'color': color,
 85                'absolute_scale': True,
 86                'host_only': host_only,
 87                'front': front,
 88                'attach': attach.value,
 89            },
 90            delegate=self,
 91        )
 92
 93        if model_opaque is not None:
 94            self.node.model_opaque = model_opaque
 95        if model_transparent is not None:
 96            self.node.model_transparent = model_transparent
 97        if tint2_color is not None:
 98            self.node.tint2_color = tint2_color
 99        if transition is self.Transition.FADE_IN:
100            keys = {transition_delay: 0, transition_delay + 0.5: color[3]}
101            if transition_out_delay is not None:
102                keys[transition_delay + transition_out_delay] = color[3]
103                keys[transition_delay + transition_out_delay + 0.5] = 0
104            ba.animate(self.node, 'opacity', keys)
105        cmb = self.position_combine = ba.newnode(
106            'combine', owner=self.node, attrs={'size': 2}
107        )
108        if transition is self.Transition.IN_RIGHT:
109            keys = {
110                transition_delay: position[0] + 1200,
111                transition_delay + 0.2: position[0],
112            }
113            o_keys = {transition_delay: 0.0, transition_delay + 0.05: 1.0}
114            ba.animate(cmb, 'input0', keys)
115            cmb.input1 = position[1]
116            ba.animate(self.node, 'opacity', o_keys)
117        elif transition is self.Transition.IN_LEFT:
118            keys = {
119                transition_delay: position[0] - 1200,
120                transition_delay + 0.2: position[0],
121            }
122            o_keys = {transition_delay: 0.0, transition_delay + 0.05: 1.0}
123            if transition_out_delay is not None:
124                keys[transition_delay + transition_out_delay] = position[0]
125                keys[transition_delay + transition_out_delay + 200] = (
126                    -position[0] - 1200
127                )
128                o_keys[transition_delay + transition_out_delay + 0.15] = 1.0
129                o_keys[transition_delay + transition_out_delay + 0.2] = 0.0
130            ba.animate(cmb, 'input0', keys)
131            cmb.input1 = position[1]
132            ba.animate(self.node, 'opacity', o_keys)
133        elif transition is self.Transition.IN_BOTTOM_SLOW:
134            keys = {transition_delay: -400, transition_delay + 3.5: position[1]}
135            o_keys = {transition_delay: 0.0, transition_delay + 2.0: 1.0}
136            cmb.input0 = position[0]
137            ba.animate(cmb, 'input1', keys)
138            ba.animate(self.node, 'opacity', o_keys)
139        elif transition is self.Transition.IN_BOTTOM:
140            keys = {transition_delay: -400, transition_delay + 0.2: position[1]}
141            o_keys = {transition_delay: 0.0, transition_delay + 0.05: 1.0}
142            if transition_out_delay is not None:
143                keys[transition_delay + transition_out_delay] = position[1]
144                keys[transition_delay + transition_out_delay + 0.2] = -400
145                o_keys[transition_delay + transition_out_delay + 0.15] = 1.0
146                o_keys[transition_delay + transition_out_delay + 0.2] = 0.0
147            cmb.input0 = position[0]
148            ba.animate(cmb, 'input1', keys)
149            ba.animate(self.node, 'opacity', o_keys)
150        elif transition is self.Transition.IN_TOP_SLOW:
151            keys = {transition_delay: 400, transition_delay + 3.5: position[1]}
152            o_keys = {transition_delay: 0.0, transition_delay + 1.0: 1.0}
153            cmb.input0 = position[0]
154            ba.animate(cmb, 'input1', keys)
155            ba.animate(self.node, 'opacity', o_keys)
156        else:
157            assert transition is self.Transition.FADE_IN or transition is None
158            cmb.input0 = position[0]
159            cmb.input1 = position[1]
160        cmb.connectattr('output', self.node, 'position')
161
162        # If we're transitioning out, die at the end of it.
163        if transition_out_delay is not None:
164            ba.timer(
165                transition_delay + transition_out_delay + 1.0,
166                ba.WeakCall(self.handlemessage, ba.DieMessage()),
167            )
168
169    def handlemessage(self, msg: Any) -> Any:
170        assert not self.expired
171        if isinstance(msg, ba.DieMessage):
172            if self.node:
173                self.node.delete()
174            return None
175        return super().handlemessage(msg)

Just a wrapped up image node with a few tricks up its sleeve.

Image( texture: _ba.Texture | dict[str, typing.Any], position: tuple[float, float] = (0, 0), transition: bastd.actor.image.Image.Transition | None = None, transition_delay: float = 0.0, attach: bastd.actor.image.Image.Attach = <Attach.CENTER: 'center'>, color: Sequence[float] = (1.0, 1.0, 1.0, 1.0), scale: tuple[float, float] = (100.0, 100.0), transition_out_delay: float | None = None, model_opaque: _ba.Model | None = None, model_transparent: _ba.Model | None = None, vr_depth: float = 0.0, host_only: bool = False, front: bool = False)
 38    def __init__(
 39        self,
 40        texture: ba.Texture | dict[str, Any],
 41        position: tuple[float, float] = (0, 0),
 42        transition: Transition | None = None,
 43        transition_delay: float = 0.0,
 44        attach: Attach = Attach.CENTER,
 45        color: Sequence[float] = (1.0, 1.0, 1.0, 1.0),
 46        scale: tuple[float, float] = (100.0, 100.0),
 47        transition_out_delay: float | None = None,
 48        model_opaque: ba.Model | None = None,
 49        model_transparent: ba.Model | None = None,
 50        vr_depth: float = 0.0,
 51        host_only: bool = False,
 52        front: bool = False,
 53    ):
 54        # pylint: disable=too-many-statements
 55        # pylint: disable=too-many-branches
 56        # pylint: disable=too-many-locals
 57        super().__init__()
 58
 59        # If they provided a dict as texture, assume its an icon.
 60        # otherwise its just a texture value itself.
 61        mask_texture: ba.Texture | None
 62        if isinstance(texture, dict):
 63            tint_color = texture['tint_color']
 64            tint2_color = texture['tint2_color']
 65            tint_texture = texture['tint_texture']
 66            texture = texture['texture']
 67            mask_texture = ba.gettexture('characterIconMask')
 68        else:
 69            tint_color = (1, 1, 1)
 70            tint2_color = None
 71            tint_texture = None
 72            mask_texture = None
 73
 74        self.node = ba.newnode(
 75            'image',
 76            attrs={
 77                'texture': texture,
 78                'tint_color': tint_color,
 79                'tint_texture': tint_texture,
 80                'position': position,
 81                'vr_depth': vr_depth,
 82                'scale': scale,
 83                'mask_texture': mask_texture,
 84                'color': color,
 85                'absolute_scale': True,
 86                'host_only': host_only,
 87                'front': front,
 88                'attach': attach.value,
 89            },
 90            delegate=self,
 91        )
 92
 93        if model_opaque is not None:
 94            self.node.model_opaque = model_opaque
 95        if model_transparent is not None:
 96            self.node.model_transparent = model_transparent
 97        if tint2_color is not None:
 98            self.node.tint2_color = tint2_color
 99        if transition is self.Transition.FADE_IN:
100            keys = {transition_delay: 0, transition_delay + 0.5: color[3]}
101            if transition_out_delay is not None:
102                keys[transition_delay + transition_out_delay] = color[3]
103                keys[transition_delay + transition_out_delay + 0.5] = 0
104            ba.animate(self.node, 'opacity', keys)
105        cmb = self.position_combine = ba.newnode(
106            'combine', owner=self.node, attrs={'size': 2}
107        )
108        if transition is self.Transition.IN_RIGHT:
109            keys = {
110                transition_delay: position[0] + 1200,
111                transition_delay + 0.2: position[0],
112            }
113            o_keys = {transition_delay: 0.0, transition_delay + 0.05: 1.0}
114            ba.animate(cmb, 'input0', keys)
115            cmb.input1 = position[1]
116            ba.animate(self.node, 'opacity', o_keys)
117        elif transition is self.Transition.IN_LEFT:
118            keys = {
119                transition_delay: position[0] - 1200,
120                transition_delay + 0.2: position[0],
121            }
122            o_keys = {transition_delay: 0.0, transition_delay + 0.05: 1.0}
123            if transition_out_delay is not None:
124                keys[transition_delay + transition_out_delay] = position[0]
125                keys[transition_delay + transition_out_delay + 200] = (
126                    -position[0] - 1200
127                )
128                o_keys[transition_delay + transition_out_delay + 0.15] = 1.0
129                o_keys[transition_delay + transition_out_delay + 0.2] = 0.0
130            ba.animate(cmb, 'input0', keys)
131            cmb.input1 = position[1]
132            ba.animate(self.node, 'opacity', o_keys)
133        elif transition is self.Transition.IN_BOTTOM_SLOW:
134            keys = {transition_delay: -400, transition_delay + 3.5: position[1]}
135            o_keys = {transition_delay: 0.0, transition_delay + 2.0: 1.0}
136            cmb.input0 = position[0]
137            ba.animate(cmb, 'input1', keys)
138            ba.animate(self.node, 'opacity', o_keys)
139        elif transition is self.Transition.IN_BOTTOM:
140            keys = {transition_delay: -400, transition_delay + 0.2: position[1]}
141            o_keys = {transition_delay: 0.0, transition_delay + 0.05: 1.0}
142            if transition_out_delay is not None:
143                keys[transition_delay + transition_out_delay] = position[1]
144                keys[transition_delay + transition_out_delay + 0.2] = -400
145                o_keys[transition_delay + transition_out_delay + 0.15] = 1.0
146                o_keys[transition_delay + transition_out_delay + 0.2] = 0.0
147            cmb.input0 = position[0]
148            ba.animate(cmb, 'input1', keys)
149            ba.animate(self.node, 'opacity', o_keys)
150        elif transition is self.Transition.IN_TOP_SLOW:
151            keys = {transition_delay: 400, transition_delay + 3.5: position[1]}
152            o_keys = {transition_delay: 0.0, transition_delay + 1.0: 1.0}
153            cmb.input0 = position[0]
154            ba.animate(cmb, 'input1', keys)
155            ba.animate(self.node, 'opacity', o_keys)
156        else:
157            assert transition is self.Transition.FADE_IN or transition is None
158            cmb.input0 = position[0]
159            cmb.input1 = position[1]
160        cmb.connectattr('output', self.node, 'position')
161
162        # If we're transitioning out, die at the end of it.
163        if transition_out_delay is not None:
164            ba.timer(
165                transition_delay + transition_out_delay + 1.0,
166                ba.WeakCall(self.handlemessage, ba.DieMessage()),
167            )

Instantiates an Actor in the current ba.Activity.

def handlemessage(self, msg: Any) -> Any:
169    def handlemessage(self, msg: Any) -> Any:
170        assert not self.expired
171        if isinstance(msg, ba.DieMessage):
172            if self.node:
173                self.node.delete()
174            return None
175        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
class Image.Transition(enum.Enum):
20    class Transition(Enum):
21        """Transition types we support."""
22
23        FADE_IN = 'fade_in'
24        IN_RIGHT = 'in_right'
25        IN_LEFT = 'in_left'
26        IN_BOTTOM = 'in_bottom'
27        IN_BOTTOM_SLOW = 'in_bottom_slow'
28        IN_TOP_SLOW = 'in_top_slow'

Transition types we support.

FADE_IN = <Transition.FADE_IN: 'fade_in'>
IN_RIGHT = <Transition.IN_RIGHT: 'in_right'>
IN_LEFT = <Transition.IN_LEFT: 'in_left'>
IN_BOTTOM = <Transition.IN_BOTTOM: 'in_bottom'>
IN_BOTTOM_SLOW = <Transition.IN_BOTTOM_SLOW: 'in_bottom_slow'>
IN_TOP_SLOW = <Transition.IN_TOP_SLOW: 'in_top_slow'>
Inherited Members
enum.Enum
name
value
class Image.Attach(enum.Enum):
30    class Attach(Enum):
31        """Attach types we support."""
32
33        CENTER = 'center'
34        TOP_CENTER = 'topCenter'
35        TOP_LEFT = 'topLeft'
36        BOTTOM_CENTER = 'bottomCenter'

Attach types we support.

CENTER = <Attach.CENTER: 'center'>
TOP_CENTER = <Attach.TOP_CENTER: 'topCenter'>
TOP_LEFT = <Attach.TOP_LEFT: 'topLeft'>
BOTTOM_CENTER = <Attach.BOTTOM_CENTER: 'bottomCenter'>
Inherited Members
enum.Enum
name
value