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