bastd.actor.playerspaz
Functionality related to player-controlled Spazzes.
1# Released under the MIT License. See LICENSE for details. 2# 3"""Functionality related to player-controlled Spazzes.""" 4 5from __future__ import annotations 6 7from typing import TYPE_CHECKING, TypeVar, overload 8 9import ba 10from bastd.actor.spaz import Spaz 11 12if TYPE_CHECKING: 13 from typing import Any, Sequence, Literal 14 15# pylint: disable=invalid-name 16PlayerType = TypeVar('PlayerType', bound=ba.Player) 17TeamType = TypeVar('TeamType', bound=ba.Team) 18# pylint: enable=invalid-name 19 20 21class PlayerSpazHurtMessage: 22 """A message saying a PlayerSpaz was hurt. 23 24 Category: **Message Classes** 25 """ 26 27 spaz: PlayerSpaz 28 """The PlayerSpaz that was hurt""" 29 30 def __init__(self, spaz: PlayerSpaz): 31 """Instantiate with the given ba.Spaz value.""" 32 self.spaz = spaz 33 34 35class PlayerSpaz(Spaz): 36 """A Spaz subclass meant to be controlled by a ba.Player. 37 38 Category: **Gameplay Classes** 39 40 When a PlayerSpaz dies, it delivers a ba.PlayerDiedMessage 41 to the current ba.Activity. (unless the death was the result of the 42 player leaving the game, in which case no message is sent) 43 44 When a PlayerSpaz is hurt, it delivers a PlayerSpazHurtMessage 45 to the current ba.Activity. 46 """ 47 48 def __init__(self, 49 player: ba.Player, 50 color: Sequence[float] = (1.0, 1.0, 1.0), 51 highlight: Sequence[float] = (0.5, 0.5, 0.5), 52 character: str = 'Spaz', 53 powerups_expire: bool = True): 54 """Create a spaz for the provided ba.Player. 55 56 Note: this does not wire up any controls; 57 you must call connect_controls_to_player() to do so. 58 """ 59 60 super().__init__(color=color, 61 highlight=highlight, 62 character=character, 63 source_player=player, 64 start_invincible=True, 65 powerups_expire=powerups_expire) 66 self.last_player_attacked_by: ba.Player | None = None 67 self.last_attacked_time = 0.0 68 self.last_attacked_type: tuple[str, str] | None = None 69 self.held_count = 0 70 self.last_player_held_by: ba.Player | None = None 71 self._player = player 72 self._drive_player_position() 73 74 # Overloads to tell the type system our return type based on doraise val. 75 76 @overload 77 def getplayer(self, 78 playertype: type[PlayerType], 79 doraise: Literal[False] = False) -> PlayerType | None: 80 ... 81 82 @overload 83 def getplayer(self, playertype: type[PlayerType], 84 doraise: Literal[True]) -> PlayerType: 85 ... 86 87 def getplayer(self, 88 playertype: type[PlayerType], 89 doraise: bool = False) -> PlayerType | None: 90 """Get the ba.Player associated with this Spaz. 91 92 By default this will return None if the Player no longer exists. 93 If you are logically certain that the Player still exists, pass 94 doraise=False to get a non-optional return type. 95 """ 96 player: Any = self._player 97 assert isinstance(player, playertype) 98 if not player.exists() and doraise: 99 raise ba.PlayerNotFoundError() 100 return player if player.exists() else None 101 102 def connect_controls_to_player(self, 103 enable_jump: bool = True, 104 enable_punch: bool = True, 105 enable_pickup: bool = True, 106 enable_bomb: bool = True, 107 enable_run: bool = True, 108 enable_fly: bool = True) -> None: 109 """Wire this spaz up to the provided ba.Player. 110 111 Full control of the character is given by default 112 but can be selectively limited by passing False 113 to specific arguments. 114 """ 115 player = self.getplayer(ba.Player) 116 assert player 117 118 # Reset any currently connected player and/or the player we're 119 # wiring up. 120 if self._connected_to_player: 121 if player != self._connected_to_player: 122 player.resetinput() 123 self.disconnect_controls_from_player() 124 else: 125 player.resetinput() 126 127 player.assigninput(ba.InputType.UP_DOWN, self.on_move_up_down) 128 player.assigninput(ba.InputType.LEFT_RIGHT, self.on_move_left_right) 129 player.assigninput(ba.InputType.HOLD_POSITION_PRESS, 130 self.on_hold_position_press) 131 player.assigninput(ba.InputType.HOLD_POSITION_RELEASE, 132 self.on_hold_position_release) 133 intp = ba.InputType 134 if enable_jump: 135 player.assigninput(intp.JUMP_PRESS, self.on_jump_press) 136 player.assigninput(intp.JUMP_RELEASE, self.on_jump_release) 137 if enable_pickup: 138 player.assigninput(intp.PICK_UP_PRESS, self.on_pickup_press) 139 player.assigninput(intp.PICK_UP_RELEASE, self.on_pickup_release) 140 if enable_punch: 141 player.assigninput(intp.PUNCH_PRESS, self.on_punch_press) 142 player.assigninput(intp.PUNCH_RELEASE, self.on_punch_release) 143 if enable_bomb: 144 player.assigninput(intp.BOMB_PRESS, self.on_bomb_press) 145 player.assigninput(intp.BOMB_RELEASE, self.on_bomb_release) 146 if enable_run: 147 player.assigninput(intp.RUN, self.on_run) 148 if enable_fly: 149 player.assigninput(intp.FLY_PRESS, self.on_fly_press) 150 player.assigninput(intp.FLY_RELEASE, self.on_fly_release) 151 152 self._connected_to_player = player 153 154 def disconnect_controls_from_player(self) -> None: 155 """ 156 Completely sever any previously connected 157 ba.Player from control of this spaz. 158 """ 159 if self._connected_to_player: 160 self._connected_to_player.resetinput() 161 self._connected_to_player = None 162 163 # Send releases for anything in case its held. 164 self.on_move_up_down(0) 165 self.on_move_left_right(0) 166 self.on_hold_position_release() 167 self.on_jump_release() 168 self.on_pickup_release() 169 self.on_punch_release() 170 self.on_bomb_release() 171 self.on_run(0.0) 172 self.on_fly_release() 173 else: 174 print('WARNING: disconnect_controls_from_player() called for' 175 ' non-connected player') 176 177 def handlemessage(self, msg: Any) -> Any: 178 # FIXME: Tidy this up. 179 # pylint: disable=too-many-branches 180 # pylint: disable=too-many-statements 181 # pylint: disable=too-many-nested-blocks 182 assert not self.expired 183 184 # Keep track of if we're being held and by who most recently. 185 if isinstance(msg, ba.PickedUpMessage): 186 # Augment standard behavior. 187 super().handlemessage(msg) 188 self.held_count += 1 189 picked_up_by = msg.node.source_player 190 if picked_up_by: 191 self.last_player_held_by = picked_up_by 192 elif isinstance(msg, ba.DroppedMessage): 193 # Augment standard behavior. 194 super().handlemessage(msg) 195 self.held_count -= 1 196 if self.held_count < 0: 197 print('ERROR: spaz held_count < 0') 198 199 # Let's count someone dropping us as an attack. 200 picked_up_by = msg.node.source_player 201 if picked_up_by: 202 self.last_player_attacked_by = picked_up_by 203 self.last_attacked_time = ba.time() 204 self.last_attacked_type = ('picked_up', 'default') 205 elif isinstance(msg, ba.StandMessage): 206 super().handlemessage(msg) # Augment standard behavior. 207 208 # Our Spaz was just moved somewhere. Explicitly update 209 # our associated player's position in case it is being used 210 # for logic (otherwise it will be out of date until next step) 211 self._drive_player_position() 212 213 elif isinstance(msg, ba.DieMessage): 214 215 # Report player deaths to the game. 216 if not self._dead: 217 218 # Immediate-mode or left-game deaths don't count as 'kills'. 219 killed = (not msg.immediate 220 and msg.how is not ba.DeathType.LEFT_GAME) 221 222 activity = self._activity() 223 224 player = self.getplayer(ba.Player, False) 225 if not killed: 226 killerplayer = None 227 else: 228 # If this player was being held at the time of death, 229 # the holder is the killer. 230 if self.held_count > 0 and self.last_player_held_by: 231 killerplayer = self.last_player_held_by 232 else: 233 # Otherwise, if they were attacked by someone in the 234 # last few seconds, that person is the killer. 235 # Otherwise it was a suicide. 236 # FIXME: Currently disabling suicides in Co-Op since 237 # all bot kills would register as suicides; need to 238 # change this from last_player_attacked_by to 239 # something like last_actor_attacked_by to fix that. 240 if (self.last_player_attacked_by 241 and ba.time() - self.last_attacked_time < 4.0): 242 killerplayer = self.last_player_attacked_by 243 else: 244 # ok, call it a suicide unless we're in co-op 245 if (activity is not None and not isinstance( 246 activity.session, ba.CoopSession)): 247 killerplayer = player 248 else: 249 killerplayer = None 250 251 # We should never wind up with a dead-reference here; 252 # we want to use None in that case. 253 assert killerplayer is None or killerplayer 254 255 # Only report if both the player and the activity still exist. 256 if killed and activity is not None and player: 257 activity.handlemessage( 258 ba.PlayerDiedMessage(player, killed, killerplayer, 259 msg.how)) 260 261 super().handlemessage(msg) # Augment standard behavior. 262 263 # Keep track of the player who last hit us for point rewarding. 264 elif isinstance(msg, ba.HitMessage): 265 source_player = msg.get_source_player(type(self._player)) 266 if source_player: 267 self.last_player_attacked_by = source_player 268 self.last_attacked_time = ba.time() 269 self.last_attacked_type = (msg.hit_type, msg.hit_subtype) 270 super().handlemessage(msg) # Augment standard behavior. 271 activity = self._activity() 272 if activity is not None and self._player.exists(): 273 activity.handlemessage(PlayerSpazHurtMessage(self)) 274 else: 275 return super().handlemessage(msg) 276 return None 277 278 def _drive_player_position(self) -> None: 279 """Drive our ba.Player's official position 280 281 If our position is changed explicitly, this should be called again 282 to instantly update the player position (otherwise it would be out 283 of date until the next sim step) 284 """ 285 player = self._player 286 if player: 287 assert self.node 288 assert player.node 289 self.node.connectattr('torso_position', player.node, 'position')
22class PlayerSpazHurtMessage: 23 """A message saying a PlayerSpaz was hurt. 24 25 Category: **Message Classes** 26 """ 27 28 spaz: PlayerSpaz 29 """The PlayerSpaz that was hurt""" 30 31 def __init__(self, spaz: PlayerSpaz): 32 """Instantiate with the given ba.Spaz value.""" 33 self.spaz = spaz
A message saying a PlayerSpaz was hurt.
Category: Message Classes
31 def __init__(self, spaz: PlayerSpaz): 32 """Instantiate with the given ba.Spaz value.""" 33 self.spaz = spaz
Instantiate with the given ba.Spaz value.
36class PlayerSpaz(Spaz): 37 """A Spaz subclass meant to be controlled by a ba.Player. 38 39 Category: **Gameplay Classes** 40 41 When a PlayerSpaz dies, it delivers a ba.PlayerDiedMessage 42 to the current ba.Activity. (unless the death was the result of the 43 player leaving the game, in which case no message is sent) 44 45 When a PlayerSpaz is hurt, it delivers a PlayerSpazHurtMessage 46 to the current ba.Activity. 47 """ 48 49 def __init__(self, 50 player: ba.Player, 51 color: Sequence[float] = (1.0, 1.0, 1.0), 52 highlight: Sequence[float] = (0.5, 0.5, 0.5), 53 character: str = 'Spaz', 54 powerups_expire: bool = True): 55 """Create a spaz for the provided ba.Player. 56 57 Note: this does not wire up any controls; 58 you must call connect_controls_to_player() to do so. 59 """ 60 61 super().__init__(color=color, 62 highlight=highlight, 63 character=character, 64 source_player=player, 65 start_invincible=True, 66 powerups_expire=powerups_expire) 67 self.last_player_attacked_by: ba.Player | None = None 68 self.last_attacked_time = 0.0 69 self.last_attacked_type: tuple[str, str] | None = None 70 self.held_count = 0 71 self.last_player_held_by: ba.Player | None = None 72 self._player = player 73 self._drive_player_position() 74 75 # Overloads to tell the type system our return type based on doraise val. 76 77 @overload 78 def getplayer(self, 79 playertype: type[PlayerType], 80 doraise: Literal[False] = False) -> PlayerType | None: 81 ... 82 83 @overload 84 def getplayer(self, playertype: type[PlayerType], 85 doraise: Literal[True]) -> PlayerType: 86 ... 87 88 def getplayer(self, 89 playertype: type[PlayerType], 90 doraise: bool = False) -> PlayerType | None: 91 """Get the ba.Player associated with this Spaz. 92 93 By default this will return None if the Player no longer exists. 94 If you are logically certain that the Player still exists, pass 95 doraise=False to get a non-optional return type. 96 """ 97 player: Any = self._player 98 assert isinstance(player, playertype) 99 if not player.exists() and doraise: 100 raise ba.PlayerNotFoundError() 101 return player if player.exists() else None 102 103 def connect_controls_to_player(self, 104 enable_jump: bool = True, 105 enable_punch: bool = True, 106 enable_pickup: bool = True, 107 enable_bomb: bool = True, 108 enable_run: bool = True, 109 enable_fly: bool = True) -> None: 110 """Wire this spaz up to the provided ba.Player. 111 112 Full control of the character is given by default 113 but can be selectively limited by passing False 114 to specific arguments. 115 """ 116 player = self.getplayer(ba.Player) 117 assert player 118 119 # Reset any currently connected player and/or the player we're 120 # wiring up. 121 if self._connected_to_player: 122 if player != self._connected_to_player: 123 player.resetinput() 124 self.disconnect_controls_from_player() 125 else: 126 player.resetinput() 127 128 player.assigninput(ba.InputType.UP_DOWN, self.on_move_up_down) 129 player.assigninput(ba.InputType.LEFT_RIGHT, self.on_move_left_right) 130 player.assigninput(ba.InputType.HOLD_POSITION_PRESS, 131 self.on_hold_position_press) 132 player.assigninput(ba.InputType.HOLD_POSITION_RELEASE, 133 self.on_hold_position_release) 134 intp = ba.InputType 135 if enable_jump: 136 player.assigninput(intp.JUMP_PRESS, self.on_jump_press) 137 player.assigninput(intp.JUMP_RELEASE, self.on_jump_release) 138 if enable_pickup: 139 player.assigninput(intp.PICK_UP_PRESS, self.on_pickup_press) 140 player.assigninput(intp.PICK_UP_RELEASE, self.on_pickup_release) 141 if enable_punch: 142 player.assigninput(intp.PUNCH_PRESS, self.on_punch_press) 143 player.assigninput(intp.PUNCH_RELEASE, self.on_punch_release) 144 if enable_bomb: 145 player.assigninput(intp.BOMB_PRESS, self.on_bomb_press) 146 player.assigninput(intp.BOMB_RELEASE, self.on_bomb_release) 147 if enable_run: 148 player.assigninput(intp.RUN, self.on_run) 149 if enable_fly: 150 player.assigninput(intp.FLY_PRESS, self.on_fly_press) 151 player.assigninput(intp.FLY_RELEASE, self.on_fly_release) 152 153 self._connected_to_player = player 154 155 def disconnect_controls_from_player(self) -> None: 156 """ 157 Completely sever any previously connected 158 ba.Player from control of this spaz. 159 """ 160 if self._connected_to_player: 161 self._connected_to_player.resetinput() 162 self._connected_to_player = None 163 164 # Send releases for anything in case its held. 165 self.on_move_up_down(0) 166 self.on_move_left_right(0) 167 self.on_hold_position_release() 168 self.on_jump_release() 169 self.on_pickup_release() 170 self.on_punch_release() 171 self.on_bomb_release() 172 self.on_run(0.0) 173 self.on_fly_release() 174 else: 175 print('WARNING: disconnect_controls_from_player() called for' 176 ' non-connected player') 177 178 def handlemessage(self, msg: Any) -> Any: 179 # FIXME: Tidy this up. 180 # pylint: disable=too-many-branches 181 # pylint: disable=too-many-statements 182 # pylint: disable=too-many-nested-blocks 183 assert not self.expired 184 185 # Keep track of if we're being held and by who most recently. 186 if isinstance(msg, ba.PickedUpMessage): 187 # Augment standard behavior. 188 super().handlemessage(msg) 189 self.held_count += 1 190 picked_up_by = msg.node.source_player 191 if picked_up_by: 192 self.last_player_held_by = picked_up_by 193 elif isinstance(msg, ba.DroppedMessage): 194 # Augment standard behavior. 195 super().handlemessage(msg) 196 self.held_count -= 1 197 if self.held_count < 0: 198 print('ERROR: spaz held_count < 0') 199 200 # Let's count someone dropping us as an attack. 201 picked_up_by = msg.node.source_player 202 if picked_up_by: 203 self.last_player_attacked_by = picked_up_by 204 self.last_attacked_time = ba.time() 205 self.last_attacked_type = ('picked_up', 'default') 206 elif isinstance(msg, ba.StandMessage): 207 super().handlemessage(msg) # Augment standard behavior. 208 209 # Our Spaz was just moved somewhere. Explicitly update 210 # our associated player's position in case it is being used 211 # for logic (otherwise it will be out of date until next step) 212 self._drive_player_position() 213 214 elif isinstance(msg, ba.DieMessage): 215 216 # Report player deaths to the game. 217 if not self._dead: 218 219 # Immediate-mode or left-game deaths don't count as 'kills'. 220 killed = (not msg.immediate 221 and msg.how is not ba.DeathType.LEFT_GAME) 222 223 activity = self._activity() 224 225 player = self.getplayer(ba.Player, False) 226 if not killed: 227 killerplayer = None 228 else: 229 # If this player was being held at the time of death, 230 # the holder is the killer. 231 if self.held_count > 0 and self.last_player_held_by: 232 killerplayer = self.last_player_held_by 233 else: 234 # Otherwise, if they were attacked by someone in the 235 # last few seconds, that person is the killer. 236 # Otherwise it was a suicide. 237 # FIXME: Currently disabling suicides in Co-Op since 238 # all bot kills would register as suicides; need to 239 # change this from last_player_attacked_by to 240 # something like last_actor_attacked_by to fix that. 241 if (self.last_player_attacked_by 242 and ba.time() - self.last_attacked_time < 4.0): 243 killerplayer = self.last_player_attacked_by 244 else: 245 # ok, call it a suicide unless we're in co-op 246 if (activity is not None and not isinstance( 247 activity.session, ba.CoopSession)): 248 killerplayer = player 249 else: 250 killerplayer = None 251 252 # We should never wind up with a dead-reference here; 253 # we want to use None in that case. 254 assert killerplayer is None or killerplayer 255 256 # Only report if both the player and the activity still exist. 257 if killed and activity is not None and player: 258 activity.handlemessage( 259 ba.PlayerDiedMessage(player, killed, killerplayer, 260 msg.how)) 261 262 super().handlemessage(msg) # Augment standard behavior. 263 264 # Keep track of the player who last hit us for point rewarding. 265 elif isinstance(msg, ba.HitMessage): 266 source_player = msg.get_source_player(type(self._player)) 267 if source_player: 268 self.last_player_attacked_by = source_player 269 self.last_attacked_time = ba.time() 270 self.last_attacked_type = (msg.hit_type, msg.hit_subtype) 271 super().handlemessage(msg) # Augment standard behavior. 272 activity = self._activity() 273 if activity is not None and self._player.exists(): 274 activity.handlemessage(PlayerSpazHurtMessage(self)) 275 else: 276 return super().handlemessage(msg) 277 return None 278 279 def _drive_player_position(self) -> None: 280 """Drive our ba.Player's official position 281 282 If our position is changed explicitly, this should be called again 283 to instantly update the player position (otherwise it would be out 284 of date until the next sim step) 285 """ 286 player = self._player 287 if player: 288 assert self.node 289 assert player.node 290 self.node.connectattr('torso_position', player.node, 'position')
A Spaz subclass meant to be controlled by a ba.Player.
Category: Gameplay Classes
When a PlayerSpaz dies, it delivers a ba.PlayerDiedMessage to the current ba.Activity. (unless the death was the result of the player leaving the game, in which case no message is sent)
When a PlayerSpaz is hurt, it delivers a PlayerSpazHurtMessage to the current ba.Activity.
49 def __init__(self, 50 player: ba.Player, 51 color: Sequence[float] = (1.0, 1.0, 1.0), 52 highlight: Sequence[float] = (0.5, 0.5, 0.5), 53 character: str = 'Spaz', 54 powerups_expire: bool = True): 55 """Create a spaz for the provided ba.Player. 56 57 Note: this does not wire up any controls; 58 you must call connect_controls_to_player() to do so. 59 """ 60 61 super().__init__(color=color, 62 highlight=highlight, 63 character=character, 64 source_player=player, 65 start_invincible=True, 66 powerups_expire=powerups_expire) 67 self.last_player_attacked_by: ba.Player | None = None 68 self.last_attacked_time = 0.0 69 self.last_attacked_type: tuple[str, str] | None = None 70 self.held_count = 0 71 self.last_player_held_by: ba.Player | None = None 72 self._player = player 73 self._drive_player_position()
Create a spaz for the provided ba.Player.
Note: this does not wire up any controls; you must call connect_controls_to_player() to do so.
88 def getplayer(self, 89 playertype: type[PlayerType], 90 doraise: bool = False) -> PlayerType | None: 91 """Get the ba.Player associated with this Spaz. 92 93 By default this will return None if the Player no longer exists. 94 If you are logically certain that the Player still exists, pass 95 doraise=False to get a non-optional return type. 96 """ 97 player: Any = self._player 98 assert isinstance(player, playertype) 99 if not player.exists() and doraise: 100 raise ba.PlayerNotFoundError() 101 return player if player.exists() else None
Get the ba.Player associated with this Spaz.
By default this will return None if the Player no longer exists. If you are logically certain that the Player still exists, pass doraise=False to get a non-optional return type.
103 def connect_controls_to_player(self, 104 enable_jump: bool = True, 105 enable_punch: bool = True, 106 enable_pickup: bool = True, 107 enable_bomb: bool = True, 108 enable_run: bool = True, 109 enable_fly: bool = True) -> None: 110 """Wire this spaz up to the provided ba.Player. 111 112 Full control of the character is given by default 113 but can be selectively limited by passing False 114 to specific arguments. 115 """ 116 player = self.getplayer(ba.Player) 117 assert player 118 119 # Reset any currently connected player and/or the player we're 120 # wiring up. 121 if self._connected_to_player: 122 if player != self._connected_to_player: 123 player.resetinput() 124 self.disconnect_controls_from_player() 125 else: 126 player.resetinput() 127 128 player.assigninput(ba.InputType.UP_DOWN, self.on_move_up_down) 129 player.assigninput(ba.InputType.LEFT_RIGHT, self.on_move_left_right) 130 player.assigninput(ba.InputType.HOLD_POSITION_PRESS, 131 self.on_hold_position_press) 132 player.assigninput(ba.InputType.HOLD_POSITION_RELEASE, 133 self.on_hold_position_release) 134 intp = ba.InputType 135 if enable_jump: 136 player.assigninput(intp.JUMP_PRESS, self.on_jump_press) 137 player.assigninput(intp.JUMP_RELEASE, self.on_jump_release) 138 if enable_pickup: 139 player.assigninput(intp.PICK_UP_PRESS, self.on_pickup_press) 140 player.assigninput(intp.PICK_UP_RELEASE, self.on_pickup_release) 141 if enable_punch: 142 player.assigninput(intp.PUNCH_PRESS, self.on_punch_press) 143 player.assigninput(intp.PUNCH_RELEASE, self.on_punch_release) 144 if enable_bomb: 145 player.assigninput(intp.BOMB_PRESS, self.on_bomb_press) 146 player.assigninput(intp.BOMB_RELEASE, self.on_bomb_release) 147 if enable_run: 148 player.assigninput(intp.RUN, self.on_run) 149 if enable_fly: 150 player.assigninput(intp.FLY_PRESS, self.on_fly_press) 151 player.assigninput(intp.FLY_RELEASE, self.on_fly_release) 152 153 self._connected_to_player = player
Wire this spaz up to the provided ba.Player.
Full control of the character is given by default but can be selectively limited by passing False to specific arguments.
155 def disconnect_controls_from_player(self) -> None: 156 """ 157 Completely sever any previously connected 158 ba.Player from control of this spaz. 159 """ 160 if self._connected_to_player: 161 self._connected_to_player.resetinput() 162 self._connected_to_player = None 163 164 # Send releases for anything in case its held. 165 self.on_move_up_down(0) 166 self.on_move_left_right(0) 167 self.on_hold_position_release() 168 self.on_jump_release() 169 self.on_pickup_release() 170 self.on_punch_release() 171 self.on_bomb_release() 172 self.on_run(0.0) 173 self.on_fly_release() 174 else: 175 print('WARNING: disconnect_controls_from_player() called for' 176 ' non-connected player')
Completely sever any previously connected ba.Player from control of this spaz.
178 def handlemessage(self, msg: Any) -> Any: 179 # FIXME: Tidy this up. 180 # pylint: disable=too-many-branches 181 # pylint: disable=too-many-statements 182 # pylint: disable=too-many-nested-blocks 183 assert not self.expired 184 185 # Keep track of if we're being held and by who most recently. 186 if isinstance(msg, ba.PickedUpMessage): 187 # Augment standard behavior. 188 super().handlemessage(msg) 189 self.held_count += 1 190 picked_up_by = msg.node.source_player 191 if picked_up_by: 192 self.last_player_held_by = picked_up_by 193 elif isinstance(msg, ba.DroppedMessage): 194 # Augment standard behavior. 195 super().handlemessage(msg) 196 self.held_count -= 1 197 if self.held_count < 0: 198 print('ERROR: spaz held_count < 0') 199 200 # Let's count someone dropping us as an attack. 201 picked_up_by = msg.node.source_player 202 if picked_up_by: 203 self.last_player_attacked_by = picked_up_by 204 self.last_attacked_time = ba.time() 205 self.last_attacked_type = ('picked_up', 'default') 206 elif isinstance(msg, ba.StandMessage): 207 super().handlemessage(msg) # Augment standard behavior. 208 209 # Our Spaz was just moved somewhere. Explicitly update 210 # our associated player's position in case it is being used 211 # for logic (otherwise it will be out of date until next step) 212 self._drive_player_position() 213 214 elif isinstance(msg, ba.DieMessage): 215 216 # Report player deaths to the game. 217 if not self._dead: 218 219 # Immediate-mode or left-game deaths don't count as 'kills'. 220 killed = (not msg.immediate 221 and msg.how is not ba.DeathType.LEFT_GAME) 222 223 activity = self._activity() 224 225 player = self.getplayer(ba.Player, False) 226 if not killed: 227 killerplayer = None 228 else: 229 # If this player was being held at the time of death, 230 # the holder is the killer. 231 if self.held_count > 0 and self.last_player_held_by: 232 killerplayer = self.last_player_held_by 233 else: 234 # Otherwise, if they were attacked by someone in the 235 # last few seconds, that person is the killer. 236 # Otherwise it was a suicide. 237 # FIXME: Currently disabling suicides in Co-Op since 238 # all bot kills would register as suicides; need to 239 # change this from last_player_attacked_by to 240 # something like last_actor_attacked_by to fix that. 241 if (self.last_player_attacked_by 242 and ba.time() - self.last_attacked_time < 4.0): 243 killerplayer = self.last_player_attacked_by 244 else: 245 # ok, call it a suicide unless we're in co-op 246 if (activity is not None and not isinstance( 247 activity.session, ba.CoopSession)): 248 killerplayer = player 249 else: 250 killerplayer = None 251 252 # We should never wind up with a dead-reference here; 253 # we want to use None in that case. 254 assert killerplayer is None or killerplayer 255 256 # Only report if both the player and the activity still exist. 257 if killed and activity is not None and player: 258 activity.handlemessage( 259 ba.PlayerDiedMessage(player, killed, killerplayer, 260 msg.how)) 261 262 super().handlemessage(msg) # Augment standard behavior. 263 264 # Keep track of the player who last hit us for point rewarding. 265 elif isinstance(msg, ba.HitMessage): 266 source_player = msg.get_source_player(type(self._player)) 267 if source_player: 268 self.last_player_attacked_by = source_player 269 self.last_attacked_time = ba.time() 270 self.last_attacked_type = (msg.hit_type, msg.hit_subtype) 271 super().handlemessage(msg) # Augment standard behavior. 272 activity = self._activity() 273 if activity is not None and self._player.exists(): 274 activity.handlemessage(PlayerSpazHurtMessage(self)) 275 else: 276 return super().handlemessage(msg) 277 return None
General message handling; can be passed any message object.
Inherited Members
- bastd.actor.spaz.Spaz
- node
- points_mult
- curse_time
- default_bomb_count
- default_bomb_type
- default_boxing_gloves
- default_shields
- exists
- on_expire
- add_dropped_bomb_callback
- is_alive
- set_score_text
- on_jump_press
- on_jump_release
- on_pickup_press
- on_pickup_release
- on_hold_position_press
- on_hold_position_release
- on_punch_press
- on_punch_release
- on_bomb_press
- on_bomb_release
- on_run
- on_fly_press
- on_fly_release
- on_move
- on_move_up_down
- on_move_left_right
- on_punched
- get_death_points
- curse
- equip_boxing_gloves
- equip_shields
- shield_decay
- drop_bomb
- set_land_mine_count
- curse_explode
- shatter
- set_bomb_count
- ba._actor.Actor
- autoretain
- expired
- activity
- getactivity