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, use it to wire up extended
 60        # stuff like tints and masks.
 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
 67            # Assume we're dealing with a character icon but allow
 68            # overriding.
 69            mask_tex_name = texture.get('mask_texture', 'characterIconMask')
 70            mask_texture = (
 71                None if mask_tex_name is None else bs.gettexture(mask_tex_name)
 72            )
 73            texture = texture['texture']
 74        else:
 75            tint_color = (1, 1, 1)
 76            tint2_color = None
 77            tint_texture = None
 78            mask_texture = None
 79
 80        self.node = bs.newnode(
 81            'image',
 82            attrs={
 83                'texture': texture,
 84                'tint_color': tint_color,
 85                'tint_texture': tint_texture,
 86                'position': position,
 87                'vr_depth': vr_depth,
 88                'scale': scale,
 89                'mask_texture': mask_texture,
 90                'color': color,
 91                'absolute_scale': True,
 92                'host_only': host_only,
 93                'front': front,
 94                'attach': attach.value,
 95            },
 96            delegate=self,
 97        )
 98
 99        if mesh_opaque is not None:
100            self.node.mesh_opaque = mesh_opaque
101        if mesh_transparent is not None:
102            self.node.mesh_transparent = mesh_transparent
103        if tint2_color is not None:
104            self.node.tint2_color = tint2_color
105        if transition is self.Transition.FADE_IN:
106            keys = {transition_delay: 0, transition_delay + 0.5: color[3]}
107            if transition_out_delay is not None:
108                keys[transition_delay + transition_out_delay] = color[3]
109                keys[transition_delay + transition_out_delay + 0.5] = 0
110            bs.animate(self.node, 'opacity', keys)
111        cmb = self.position_combine = bs.newnode(
112            'combine', owner=self.node, attrs={'size': 2}
113        )
114        if transition is self.Transition.IN_RIGHT:
115            keys = {
116                transition_delay: position[0] + 1200,
117                transition_delay + 0.2: position[0],
118            }
119            o_keys = {transition_delay: 0.0, transition_delay + 0.05: 1.0}
120            bs.animate(cmb, 'input0', keys)
121            cmb.input1 = position[1]
122            bs.animate(self.node, 'opacity', o_keys)
123        elif transition is self.Transition.IN_LEFT:
124            keys = {
125                transition_delay: position[0] - 1200,
126                transition_delay + 0.2: position[0],
127            }
128            o_keys = {transition_delay: 0.0, transition_delay + 0.05: 1.0}
129            if transition_out_delay is not None:
130                keys[transition_delay + transition_out_delay] = position[0]
131                keys[transition_delay + transition_out_delay + 200] = (
132                    -position[0] - 1200
133                )
134                o_keys[transition_delay + transition_out_delay + 0.15] = 1.0
135                o_keys[transition_delay + transition_out_delay + 0.2] = 0.0
136            bs.animate(cmb, 'input0', keys)
137            cmb.input1 = position[1]
138            bs.animate(self.node, 'opacity', o_keys)
139        elif transition is self.Transition.IN_BOTTOM_SLOW:
140            keys = {transition_delay: -400, transition_delay + 3.5: position[1]}
141            o_keys = {transition_delay: 0.0, transition_delay + 2.0: 1.0}
142            cmb.input0 = position[0]
143            bs.animate(cmb, 'input1', keys)
144            bs.animate(self.node, 'opacity', o_keys)
145        elif transition is self.Transition.IN_BOTTOM:
146            keys = {transition_delay: -400, transition_delay + 0.2: position[1]}
147            o_keys = {transition_delay: 0.0, transition_delay + 0.05: 1.0}
148            if transition_out_delay is not None:
149                keys[transition_delay + transition_out_delay] = position[1]
150                keys[transition_delay + transition_out_delay + 0.2] = -400
151                o_keys[transition_delay + transition_out_delay + 0.15] = 1.0
152                o_keys[transition_delay + transition_out_delay + 0.2] = 0.0
153            cmb.input0 = position[0]
154            bs.animate(cmb, 'input1', keys)
155            bs.animate(self.node, 'opacity', o_keys)
156        elif transition is self.Transition.IN_TOP_SLOW:
157            keys = {transition_delay: 400, transition_delay + 3.5: position[1]}
158            o_keys = {transition_delay: 0.0, transition_delay + 1.0: 1.0}
159            cmb.input0 = position[0]
160            bs.animate(cmb, 'input1', keys)
161            bs.animate(self.node, 'opacity', o_keys)
162        else:
163            assert transition is self.Transition.FADE_IN or transition is None
164            cmb.input0 = position[0]
165            cmb.input1 = position[1]
166        cmb.connectattr('output', self.node, 'position')
167
168        # If we're transitioning out, die at the end of it.
169        if transition_out_delay is not None:
170            bs.timer(
171                transition_delay + transition_out_delay + 1.0,
172                bs.WeakCall(self.handlemessage, bs.DieMessage()),
173            )
174
175    @override
176    def handlemessage(self, msg: Any) -> Any:
177        assert not self.expired
178        if isinstance(msg, bs.DieMessage):
179            if self.node:
180                self.node.delete()
181            return None
182        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, use it to wire up extended
 61        # stuff like tints and masks.
 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
 68            # Assume we're dealing with a character icon but allow
 69            # overriding.
 70            mask_tex_name = texture.get('mask_texture', 'characterIconMask')
 71            mask_texture = (
 72                None if mask_tex_name is None else bs.gettexture(mask_tex_name)
 73            )
 74            texture = texture['texture']
 75        else:
 76            tint_color = (1, 1, 1)
 77            tint2_color = None
 78            tint_texture = None
 79            mask_texture = None
 80
 81        self.node = bs.newnode(
 82            'image',
 83            attrs={
 84                'texture': texture,
 85                'tint_color': tint_color,
 86                'tint_texture': tint_texture,
 87                'position': position,
 88                'vr_depth': vr_depth,
 89                'scale': scale,
 90                'mask_texture': mask_texture,
 91                'color': color,
 92                'absolute_scale': True,
 93                'host_only': host_only,
 94                'front': front,
 95                'attach': attach.value,
 96            },
 97            delegate=self,
 98        )
 99
100        if mesh_opaque is not None:
101            self.node.mesh_opaque = mesh_opaque
102        if mesh_transparent is not None:
103            self.node.mesh_transparent = mesh_transparent
104        if tint2_color is not None:
105            self.node.tint2_color = tint2_color
106        if transition is self.Transition.FADE_IN:
107            keys = {transition_delay: 0, transition_delay + 0.5: color[3]}
108            if transition_out_delay is not None:
109                keys[transition_delay + transition_out_delay] = color[3]
110                keys[transition_delay + transition_out_delay + 0.5] = 0
111            bs.animate(self.node, 'opacity', keys)
112        cmb = self.position_combine = bs.newnode(
113            'combine', owner=self.node, attrs={'size': 2}
114        )
115        if transition is self.Transition.IN_RIGHT:
116            keys = {
117                transition_delay: position[0] + 1200,
118                transition_delay + 0.2: position[0],
119            }
120            o_keys = {transition_delay: 0.0, transition_delay + 0.05: 1.0}
121            bs.animate(cmb, 'input0', keys)
122            cmb.input1 = position[1]
123            bs.animate(self.node, 'opacity', o_keys)
124        elif transition is self.Transition.IN_LEFT:
125            keys = {
126                transition_delay: position[0] - 1200,
127                transition_delay + 0.2: position[0],
128            }
129            o_keys = {transition_delay: 0.0, transition_delay + 0.05: 1.0}
130            if transition_out_delay is not None:
131                keys[transition_delay + transition_out_delay] = position[0]
132                keys[transition_delay + transition_out_delay + 200] = (
133                    -position[0] - 1200
134                )
135                o_keys[transition_delay + transition_out_delay + 0.15] = 1.0
136                o_keys[transition_delay + transition_out_delay + 0.2] = 0.0
137            bs.animate(cmb, 'input0', keys)
138            cmb.input1 = position[1]
139            bs.animate(self.node, 'opacity', o_keys)
140        elif transition is self.Transition.IN_BOTTOM_SLOW:
141            keys = {transition_delay: -400, transition_delay + 3.5: position[1]}
142            o_keys = {transition_delay: 0.0, transition_delay + 2.0: 1.0}
143            cmb.input0 = position[0]
144            bs.animate(cmb, 'input1', keys)
145            bs.animate(self.node, 'opacity', o_keys)
146        elif transition is self.Transition.IN_BOTTOM:
147            keys = {transition_delay: -400, transition_delay + 0.2: position[1]}
148            o_keys = {transition_delay: 0.0, transition_delay + 0.05: 1.0}
149            if transition_out_delay is not None:
150                keys[transition_delay + transition_out_delay] = position[1]
151                keys[transition_delay + transition_out_delay + 0.2] = -400
152                o_keys[transition_delay + transition_out_delay + 0.15] = 1.0
153                o_keys[transition_delay + transition_out_delay + 0.2] = 0.0
154            cmb.input0 = position[0]
155            bs.animate(cmb, 'input1', keys)
156            bs.animate(self.node, 'opacity', o_keys)
157        elif transition is self.Transition.IN_TOP_SLOW:
158            keys = {transition_delay: 400, transition_delay + 3.5: position[1]}
159            o_keys = {transition_delay: 0.0, transition_delay + 1.0: 1.0}
160            cmb.input0 = position[0]
161            bs.animate(cmb, 'input1', keys)
162            bs.animate(self.node, 'opacity', o_keys)
163        else:
164            assert transition is self.Transition.FADE_IN or transition is None
165            cmb.input0 = position[0]
166            cmb.input1 = position[1]
167        cmb.connectattr('output', self.node, 'position')
168
169        # If we're transitioning out, die at the end of it.
170        if transition_out_delay is not None:
171            bs.timer(
172                transition_delay + transition_out_delay + 1.0,
173                bs.WeakCall(self.handlemessage, bs.DieMessage()),
174            )
175
176    @override
177    def handlemessage(self, msg: Any) -> Any:
178        assert not self.expired
179        if isinstance(msg, bs.DieMessage):
180            if self.node:
181                self.node.delete()
182            return None
183        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, use it to wire up extended
 61        # stuff like tints and masks.
 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
 68            # Assume we're dealing with a character icon but allow
 69            # overriding.
 70            mask_tex_name = texture.get('mask_texture', 'characterIconMask')
 71            mask_texture = (
 72                None if mask_tex_name is None else bs.gettexture(mask_tex_name)
 73            )
 74            texture = texture['texture']
 75        else:
 76            tint_color = (1, 1, 1)
 77            tint2_color = None
 78            tint_texture = None
 79            mask_texture = None
 80
 81        self.node = bs.newnode(
 82            'image',
 83            attrs={
 84                'texture': texture,
 85                'tint_color': tint_color,
 86                'tint_texture': tint_texture,
 87                'position': position,
 88                'vr_depth': vr_depth,
 89                'scale': scale,
 90                'mask_texture': mask_texture,
 91                'color': color,
 92                'absolute_scale': True,
 93                'host_only': host_only,
 94                'front': front,
 95                'attach': attach.value,
 96            },
 97            delegate=self,
 98        )
 99
100        if mesh_opaque is not None:
101            self.node.mesh_opaque = mesh_opaque
102        if mesh_transparent is not None:
103            self.node.mesh_transparent = mesh_transparent
104        if tint2_color is not None:
105            self.node.tint2_color = tint2_color
106        if transition is self.Transition.FADE_IN:
107            keys = {transition_delay: 0, transition_delay + 0.5: color[3]}
108            if transition_out_delay is not None:
109                keys[transition_delay + transition_out_delay] = color[3]
110                keys[transition_delay + transition_out_delay + 0.5] = 0
111            bs.animate(self.node, 'opacity', keys)
112        cmb = self.position_combine = bs.newnode(
113            'combine', owner=self.node, attrs={'size': 2}
114        )
115        if transition is self.Transition.IN_RIGHT:
116            keys = {
117                transition_delay: position[0] + 1200,
118                transition_delay + 0.2: position[0],
119            }
120            o_keys = {transition_delay: 0.0, transition_delay + 0.05: 1.0}
121            bs.animate(cmb, 'input0', keys)
122            cmb.input1 = position[1]
123            bs.animate(self.node, 'opacity', o_keys)
124        elif transition is self.Transition.IN_LEFT:
125            keys = {
126                transition_delay: position[0] - 1200,
127                transition_delay + 0.2: position[0],
128            }
129            o_keys = {transition_delay: 0.0, transition_delay + 0.05: 1.0}
130            if transition_out_delay is not None:
131                keys[transition_delay + transition_out_delay] = position[0]
132                keys[transition_delay + transition_out_delay + 200] = (
133                    -position[0] - 1200
134                )
135                o_keys[transition_delay + transition_out_delay + 0.15] = 1.0
136                o_keys[transition_delay + transition_out_delay + 0.2] = 0.0
137            bs.animate(cmb, 'input0', keys)
138            cmb.input1 = position[1]
139            bs.animate(self.node, 'opacity', o_keys)
140        elif transition is self.Transition.IN_BOTTOM_SLOW:
141            keys = {transition_delay: -400, transition_delay + 3.5: position[1]}
142            o_keys = {transition_delay: 0.0, transition_delay + 2.0: 1.0}
143            cmb.input0 = position[0]
144            bs.animate(cmb, 'input1', keys)
145            bs.animate(self.node, 'opacity', o_keys)
146        elif transition is self.Transition.IN_BOTTOM:
147            keys = {transition_delay: -400, transition_delay + 0.2: position[1]}
148            o_keys = {transition_delay: 0.0, transition_delay + 0.05: 1.0}
149            if transition_out_delay is not None:
150                keys[transition_delay + transition_out_delay] = position[1]
151                keys[transition_delay + transition_out_delay + 0.2] = -400
152                o_keys[transition_delay + transition_out_delay + 0.15] = 1.0
153                o_keys[transition_delay + transition_out_delay + 0.2] = 0.0
154            cmb.input0 = position[0]
155            bs.animate(cmb, 'input1', keys)
156            bs.animate(self.node, 'opacity', o_keys)
157        elif transition is self.Transition.IN_TOP_SLOW:
158            keys = {transition_delay: 400, transition_delay + 3.5: position[1]}
159            o_keys = {transition_delay: 0.0, transition_delay + 1.0: 1.0}
160            cmb.input0 = position[0]
161            bs.animate(cmb, 'input1', keys)
162            bs.animate(self.node, 'opacity', o_keys)
163        else:
164            assert transition is self.Transition.FADE_IN or transition is None
165            cmb.input0 = position[0]
166            cmb.input1 = position[1]
167        cmb.connectattr('output', self.node, 'position')
168
169        # If we're transitioning out, die at the end of it.
170        if transition_out_delay is not None:
171            bs.timer(
172                transition_delay + transition_out_delay + 1.0,
173                bs.WeakCall(self.handlemessage, bs.DieMessage()),
174            )

Instantiates an Actor in the current bascenev1.Activity.

node
@override
def handlemessage(self, msg: Any) -> Any:
176    @override
177    def handlemessage(self, msg: Any) -> Any:
178        assert not self.expired
179        if isinstance(msg, bs.DieMessage):
180            if self.node:
181                self.node.delete()
182            return None
183        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'>