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

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

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

Instantiates an Actor in the current bascenev1.Activity.

node
@override
def handlemessage(self, msg: Any) -> Any:
170    @override
171    def handlemessage(self, msg: Any) -> Any:
172        assert not self.expired
173        if isinstance(msg, bs.DieMessage):
174            if self.node:
175                self.node.delete()
176            return None
177        return super().handlemessage(msg)

General message handling; can be passed any message object.

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'>
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'>