bascenev1lib.actor.respawnicon

Implements respawn icon actor.

  1# Released under the MIT License. See LICENSE for details.
  2#
  3"""Implements respawn icon actor."""
  4
  5from __future__ import annotations
  6
  7import weakref
  8
  9import bascenev1 as bs
 10
 11
 12class RespawnIcon:
 13    """An icon with a countdown that appears alongside the screen.
 14
 15    category: Gameplay Classes
 16
 17    This is used to indicate that a bascenev1.Player is waiting to respawn.
 18    """
 19
 20    _MASKTEXSTORENAME = bs.storagename('masktex')
 21    _ICONSSTORENAME = bs.storagename('icons')
 22
 23    def __init__(self, player: bs.Player, respawn_time: float):
 24        """Instantiate with a Player and respawn_time (in seconds)."""
 25        self._visible = True
 26
 27        on_right, offs_extra, respawn_icons = self._get_context(player)
 28
 29        # Cache our mask tex on the team for easy access.
 30        mask_tex = player.team.customdata.get(self._MASKTEXSTORENAME)
 31        if mask_tex is None:
 32            mask_tex = bs.gettexture('characterIconMask')
 33            player.team.customdata[self._MASKTEXSTORENAME] = mask_tex
 34        assert isinstance(mask_tex, bs.Texture)
 35
 36        # Now find the first unused slot and use that.
 37        index = 0
 38        while (
 39            index in respawn_icons
 40            and respawn_icons[index]() is not None
 41            and respawn_icons[index]().visible
 42        ):
 43            index += 1
 44        respawn_icons[index] = weakref.ref(self)
 45
 46        offs = offs_extra + index * -53
 47        icon = player.get_icon()
 48        texture = icon['texture']
 49        h_offs = -10
 50        ipos = (-40 - h_offs if on_right else 40 + h_offs, -180 + offs)
 51        self._image: bs.NodeActor | None = bs.NodeActor(
 52            bs.newnode(
 53                'image',
 54                attrs={
 55                    'texture': texture,
 56                    'tint_texture': icon['tint_texture'],
 57                    'tint_color': icon['tint_color'],
 58                    'tint2_color': icon['tint2_color'],
 59                    'mask_texture': mask_tex,
 60                    'position': ipos,
 61                    'scale': (32, 32),
 62                    'opacity': 1.0,
 63                    'absolute_scale': True,
 64                    'attach': 'topRight' if on_right else 'topLeft',
 65                },
 66            )
 67        )
 68
 69        assert self._image.node
 70        bs.animate(self._image.node, 'opacity', {0.0: 0, 0.2: 0.7})
 71
 72        npos = (-40 - h_offs if on_right else 40 + h_offs, -205 + 49 + offs)
 73        self._name: bs.NodeActor | None = bs.NodeActor(
 74            bs.newnode(
 75                'text',
 76                attrs={
 77                    'v_attach': 'top',
 78                    'h_attach': 'right' if on_right else 'left',
 79                    'text': bs.Lstr(value=player.getname()),
 80                    'maxwidth': 100,
 81                    'h_align': 'center',
 82                    'v_align': 'center',
 83                    'shadow': 1.0,
 84                    'flatness': 1.0,
 85                    'color': bs.safecolor(icon['tint_color']),
 86                    'scale': 0.5,
 87                    'position': npos,
 88                },
 89            )
 90        )
 91
 92        assert self._name.node
 93        bs.animate(self._name.node, 'scale', {0: 0, 0.1: 0.5})
 94
 95        tpos = (-60 - h_offs if on_right else 60 + h_offs, -192 + offs)
 96        self._text: bs.NodeActor | None = bs.NodeActor(
 97            bs.newnode(
 98                'text',
 99                attrs={
100                    'position': tpos,
101                    'h_attach': 'right' if on_right else 'left',
102                    'h_align': 'right' if on_right else 'left',
103                    'scale': 0.9,
104                    'shadow': 0.5,
105                    'flatness': 0.5,
106                    'v_attach': 'top',
107                    'color': bs.safecolor(icon['tint_color']),
108                    'text': '',
109                },
110            )
111        )
112
113        assert self._text.node
114        bs.animate(self._text.node, 'scale', {0: 0, 0.1: 0.9})
115
116        self._respawn_time = bs.time() + respawn_time
117        self._update()
118        self._timer: bs.Timer | None = bs.Timer(
119            1.0, bs.WeakCall(self._update), repeat=True
120        )
121
122    @property
123    def visible(self) -> bool:
124        """Is this icon still visible?"""
125        return self._visible
126
127    def _get_context(self, player: bs.Player) -> tuple[bool, float, dict]:
128        """Return info on where we should be shown and stored."""
129        activity = bs.getactivity()
130
131        if isinstance(bs.getsession(), bs.DualTeamSession):
132            on_right = player.team.id % 2 == 1
133
134            # Store a list of icons in the team.
135            icons = player.team.customdata.get(self._ICONSSTORENAME)
136            if icons is None:
137                player.team.customdata[self._ICONSSTORENAME] = icons = {}
138            assert isinstance(icons, dict)
139
140            offs_extra = -20
141        else:
142            on_right = False
143
144            # Store a list of icons in the activity.
145            icons = activity.customdata.get(self._ICONSSTORENAME)
146            if icons is None:
147                activity.customdata[self._ICONSSTORENAME] = icons = {}
148            assert isinstance(icons, dict)
149
150            if isinstance(activity.session, bs.FreeForAllSession):
151                offs_extra = -150
152            else:
153                offs_extra = -20
154        return on_right, offs_extra, icons
155
156    def _update(self) -> None:
157        remaining = int(round(self._respawn_time - bs.time()))
158        if remaining > 0:
159            assert self._text is not None
160            if self._text.node:
161                self._text.node.text = str(remaining)
162        else:
163            self._visible = False
164            self._image = self._text = self._timer = self._name = None
class RespawnIcon:
 13class RespawnIcon:
 14    """An icon with a countdown that appears alongside the screen.
 15
 16    category: Gameplay Classes
 17
 18    This is used to indicate that a bascenev1.Player is waiting to respawn.
 19    """
 20
 21    _MASKTEXSTORENAME = bs.storagename('masktex')
 22    _ICONSSTORENAME = bs.storagename('icons')
 23
 24    def __init__(self, player: bs.Player, respawn_time: float):
 25        """Instantiate with a Player and respawn_time (in seconds)."""
 26        self._visible = True
 27
 28        on_right, offs_extra, respawn_icons = self._get_context(player)
 29
 30        # Cache our mask tex on the team for easy access.
 31        mask_tex = player.team.customdata.get(self._MASKTEXSTORENAME)
 32        if mask_tex is None:
 33            mask_tex = bs.gettexture('characterIconMask')
 34            player.team.customdata[self._MASKTEXSTORENAME] = mask_tex
 35        assert isinstance(mask_tex, bs.Texture)
 36
 37        # Now find the first unused slot and use that.
 38        index = 0
 39        while (
 40            index in respawn_icons
 41            and respawn_icons[index]() is not None
 42            and respawn_icons[index]().visible
 43        ):
 44            index += 1
 45        respawn_icons[index] = weakref.ref(self)
 46
 47        offs = offs_extra + index * -53
 48        icon = player.get_icon()
 49        texture = icon['texture']
 50        h_offs = -10
 51        ipos = (-40 - h_offs if on_right else 40 + h_offs, -180 + offs)
 52        self._image: bs.NodeActor | None = bs.NodeActor(
 53            bs.newnode(
 54                'image',
 55                attrs={
 56                    'texture': texture,
 57                    'tint_texture': icon['tint_texture'],
 58                    'tint_color': icon['tint_color'],
 59                    'tint2_color': icon['tint2_color'],
 60                    'mask_texture': mask_tex,
 61                    'position': ipos,
 62                    'scale': (32, 32),
 63                    'opacity': 1.0,
 64                    'absolute_scale': True,
 65                    'attach': 'topRight' if on_right else 'topLeft',
 66                },
 67            )
 68        )
 69
 70        assert self._image.node
 71        bs.animate(self._image.node, 'opacity', {0.0: 0, 0.2: 0.7})
 72
 73        npos = (-40 - h_offs if on_right else 40 + h_offs, -205 + 49 + offs)
 74        self._name: bs.NodeActor | None = bs.NodeActor(
 75            bs.newnode(
 76                'text',
 77                attrs={
 78                    'v_attach': 'top',
 79                    'h_attach': 'right' if on_right else 'left',
 80                    'text': bs.Lstr(value=player.getname()),
 81                    'maxwidth': 100,
 82                    'h_align': 'center',
 83                    'v_align': 'center',
 84                    'shadow': 1.0,
 85                    'flatness': 1.0,
 86                    'color': bs.safecolor(icon['tint_color']),
 87                    'scale': 0.5,
 88                    'position': npos,
 89                },
 90            )
 91        )
 92
 93        assert self._name.node
 94        bs.animate(self._name.node, 'scale', {0: 0, 0.1: 0.5})
 95
 96        tpos = (-60 - h_offs if on_right else 60 + h_offs, -192 + offs)
 97        self._text: bs.NodeActor | None = bs.NodeActor(
 98            bs.newnode(
 99                'text',
100                attrs={
101                    'position': tpos,
102                    'h_attach': 'right' if on_right else 'left',
103                    'h_align': 'right' if on_right else 'left',
104                    'scale': 0.9,
105                    'shadow': 0.5,
106                    'flatness': 0.5,
107                    'v_attach': 'top',
108                    'color': bs.safecolor(icon['tint_color']),
109                    'text': '',
110                },
111            )
112        )
113
114        assert self._text.node
115        bs.animate(self._text.node, 'scale', {0: 0, 0.1: 0.9})
116
117        self._respawn_time = bs.time() + respawn_time
118        self._update()
119        self._timer: bs.Timer | None = bs.Timer(
120            1.0, bs.WeakCall(self._update), repeat=True
121        )
122
123    @property
124    def visible(self) -> bool:
125        """Is this icon still visible?"""
126        return self._visible
127
128    def _get_context(self, player: bs.Player) -> tuple[bool, float, dict]:
129        """Return info on where we should be shown and stored."""
130        activity = bs.getactivity()
131
132        if isinstance(bs.getsession(), bs.DualTeamSession):
133            on_right = player.team.id % 2 == 1
134
135            # Store a list of icons in the team.
136            icons = player.team.customdata.get(self._ICONSSTORENAME)
137            if icons is None:
138                player.team.customdata[self._ICONSSTORENAME] = icons = {}
139            assert isinstance(icons, dict)
140
141            offs_extra = -20
142        else:
143            on_right = False
144
145            # Store a list of icons in the activity.
146            icons = activity.customdata.get(self._ICONSSTORENAME)
147            if icons is None:
148                activity.customdata[self._ICONSSTORENAME] = icons = {}
149            assert isinstance(icons, dict)
150
151            if isinstance(activity.session, bs.FreeForAllSession):
152                offs_extra = -150
153            else:
154                offs_extra = -20
155        return on_right, offs_extra, icons
156
157    def _update(self) -> None:
158        remaining = int(round(self._respawn_time - bs.time()))
159        if remaining > 0:
160            assert self._text is not None
161            if self._text.node:
162                self._text.node.text = str(remaining)
163        else:
164            self._visible = False
165            self._image = self._text = self._timer = self._name = None

An icon with a countdown that appears alongside the screen.

category: Gameplay Classes

This is used to indicate that a bascenev1.Player is waiting to respawn.

RespawnIcon(player: bascenev1._player.Player, respawn_time: float)
 24    def __init__(self, player: bs.Player, respawn_time: float):
 25        """Instantiate with a Player and respawn_time (in seconds)."""
 26        self._visible = True
 27
 28        on_right, offs_extra, respawn_icons = self._get_context(player)
 29
 30        # Cache our mask tex on the team for easy access.
 31        mask_tex = player.team.customdata.get(self._MASKTEXSTORENAME)
 32        if mask_tex is None:
 33            mask_tex = bs.gettexture('characterIconMask')
 34            player.team.customdata[self._MASKTEXSTORENAME] = mask_tex
 35        assert isinstance(mask_tex, bs.Texture)
 36
 37        # Now find the first unused slot and use that.
 38        index = 0
 39        while (
 40            index in respawn_icons
 41            and respawn_icons[index]() is not None
 42            and respawn_icons[index]().visible
 43        ):
 44            index += 1
 45        respawn_icons[index] = weakref.ref(self)
 46
 47        offs = offs_extra + index * -53
 48        icon = player.get_icon()
 49        texture = icon['texture']
 50        h_offs = -10
 51        ipos = (-40 - h_offs if on_right else 40 + h_offs, -180 + offs)
 52        self._image: bs.NodeActor | None = bs.NodeActor(
 53            bs.newnode(
 54                'image',
 55                attrs={
 56                    'texture': texture,
 57                    'tint_texture': icon['tint_texture'],
 58                    'tint_color': icon['tint_color'],
 59                    'tint2_color': icon['tint2_color'],
 60                    'mask_texture': mask_tex,
 61                    'position': ipos,
 62                    'scale': (32, 32),
 63                    'opacity': 1.0,
 64                    'absolute_scale': True,
 65                    'attach': 'topRight' if on_right else 'topLeft',
 66                },
 67            )
 68        )
 69
 70        assert self._image.node
 71        bs.animate(self._image.node, 'opacity', {0.0: 0, 0.2: 0.7})
 72
 73        npos = (-40 - h_offs if on_right else 40 + h_offs, -205 + 49 + offs)
 74        self._name: bs.NodeActor | None = bs.NodeActor(
 75            bs.newnode(
 76                'text',
 77                attrs={
 78                    'v_attach': 'top',
 79                    'h_attach': 'right' if on_right else 'left',
 80                    'text': bs.Lstr(value=player.getname()),
 81                    'maxwidth': 100,
 82                    'h_align': 'center',
 83                    'v_align': 'center',
 84                    'shadow': 1.0,
 85                    'flatness': 1.0,
 86                    'color': bs.safecolor(icon['tint_color']),
 87                    'scale': 0.5,
 88                    'position': npos,
 89                },
 90            )
 91        )
 92
 93        assert self._name.node
 94        bs.animate(self._name.node, 'scale', {0: 0, 0.1: 0.5})
 95
 96        tpos = (-60 - h_offs if on_right else 60 + h_offs, -192 + offs)
 97        self._text: bs.NodeActor | None = bs.NodeActor(
 98            bs.newnode(
 99                'text',
100                attrs={
101                    'position': tpos,
102                    'h_attach': 'right' if on_right else 'left',
103                    'h_align': 'right' if on_right else 'left',
104                    'scale': 0.9,
105                    'shadow': 0.5,
106                    'flatness': 0.5,
107                    'v_attach': 'top',
108                    'color': bs.safecolor(icon['tint_color']),
109                    'text': '',
110                },
111            )
112        )
113
114        assert self._text.node
115        bs.animate(self._text.node, 'scale', {0: 0, 0.1: 0.9})
116
117        self._respawn_time = bs.time() + respawn_time
118        self._update()
119        self._timer: bs.Timer | None = bs.Timer(
120            1.0, bs.WeakCall(self._update), repeat=True
121        )

Instantiate with a Player and respawn_time (in seconds).

visible: bool
123    @property
124    def visible(self) -> bool:
125        """Is this icon still visible?"""
126        return self._visible

Is this icon still visible?