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

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

Instantiate with a ba.Player and respawn_time (in seconds).

visible: bool

Is this icon still visible?