From d6ccf2b12dfdd12303afecac165f9ca8de245c81 Mon Sep 17 00:00:00 2001
From: Eric Froemling
Date: Thu, 21 May 2020 15:43:10 -0700
Subject: [PATCH] Fixed a bug with player positions being incorrect
---
assets/src/ba_data/python/ba/_map.py | 9 +---
assets/src/ba_data/python/ba/_player.py | 14 +++++-
.../ba_data/python/bastd/actor/playerspaz.py | 36 ++++++++++-----
assets/src/ba_data/python/bastd/actor/spaz.py | 45 ++++++++++---------
docs/ba_module.md | 10 ++++-
5 files changed, 73 insertions(+), 41 deletions(-)
diff --git a/assets/src/ba_data/python/ba/_map.py b/assets/src/ba_data/python/ba/_map.py
index 139b4d63..9af476a8 100644
--- a/assets/src/ba_data/python/ba/_map.py
+++ b/assets/src/ba_data/python/ba/_map.py
@@ -367,13 +367,8 @@ class Map(Actor):
# Get positions for existing players.
player_pts = []
for player in players:
- try:
- if player and player.node:
- pnt = _ba.Vec3(player.node.position)
- player_pts.append(pnt)
- except Exception:
- from ba import _error
- _error.print_exception()
+ if player.is_alive():
+ player_pts.append(player.position)
def _getpt() -> Sequence[float]:
point = self.ffa_spawn_points[self._next_ffa_start_index]
diff --git a/assets/src/ba_data/python/ba/_player.py b/assets/src/ba_data/python/ba/_player.py
index be51c669..20615269 100644
--- a/assets/src/ba_data/python/ba/_player.py
+++ b/assets/src/ba_data/python/ba/_player.py
@@ -24,6 +24,8 @@ from __future__ import annotations
from typing import TYPE_CHECKING, TypeVar, Generic
+import _ba
+
if TYPE_CHECKING:
from typing import (Type, Optional, Sequence, Dict, Any, Union, Tuple,
Callable)
@@ -66,7 +68,6 @@ class Player(Generic[TeamType]):
(internal)
"""
from ba._nodeactor import NodeActor
- import _ba
# Sanity check; if a dataclass is created that inherits from us,
# it will define an equality operator by default which will break
@@ -92,6 +93,8 @@ class Player(Generic[TeamType]):
self.gamedata = sessionplayer.gamedata
# Create our player node in the current activity.
+ # Note: do we want to save a few cycles here by managing our player
+ # node manually instead of wrapping it in a NodeActor?
node = _ba.newnode('player', attrs={'playerID': sessionplayer.id})
self._nodeactor = NodeActor(node)
sessionplayer.set_node(node)
@@ -118,6 +121,15 @@ class Player(Generic[TeamType]):
raise _error.NodeNotFoundError
return self._nodeactor.node
+ @property
+ def position(self) -> ba.Vec3:
+ """The position of the player, as defined by its current Actor.
+
+ This value should not be used when the player has no Actor, as
+ it is undefined in that case.
+ """
+ return _ba.Vec3(self.node.position)
+
def exists(self) -> bool:
"""Whether the underlying player still exists.
diff --git a/assets/src/ba_data/python/bastd/actor/playerspaz.py b/assets/src/ba_data/python/bastd/actor/playerspaz.py
index 1c620736..ea346360 100644
--- a/assets/src/ba_data/python/bastd/actor/playerspaz.py
+++ b/assets/src/ba_data/python/bastd/actor/playerspaz.py
@@ -87,14 +87,7 @@ class PlayerSpaz(Spaz, Generic[PlayerType]):
self.held_count = 0
self.last_player_held_by: Optional[PlayerType] = None
self._player = player
- self.playertype = type(player)
-
- # Grab the node for this player and wire it to follow our spaz
- # (so players' controllers know where to draw their guides, etc).
- if player:
- assert self.node
- assert player.node
- self.node.connectattr('torso_position', player.node, 'position')
+ self._drive_player_position()
@property
def player(self) -> PlayerType:
@@ -200,7 +193,7 @@ class PlayerSpaz(Spaz, Generic[PlayerType]):
if isinstance(msg, ba.PickedUpMessage):
super().handlemessage(msg) # Augment standard behavior.
self.held_count += 1
- picked_up_by = ba.playercast_o(self.playertype,
+ picked_up_by = ba.playercast_o(type(self._player),
msg.node.source_player)
if picked_up_by:
self.last_player_held_by = picked_up_by
@@ -212,7 +205,7 @@ class PlayerSpaz(Spaz, Generic[PlayerType]):
# Let's count someone dropping us as an attack.
try:
- picked_up_by_2 = ba.playercast_o(self.playertype,
+ picked_up_by_2 = ba.playercast_o(type(self._player),
msg.node.source_player)
except Exception:
picked_up_by_2 = None
@@ -220,6 +213,14 @@ class PlayerSpaz(Spaz, Generic[PlayerType]):
self.last_player_attacked_by = picked_up_by_2
self.last_attacked_time = ba.time()
self.last_attacked_type = ('picked_up', 'default')
+ elif isinstance(msg, ba.StandMessage):
+ super().handlemessage(msg) # Augment standard behavior.
+
+ # Our Spaz was just moved somewhere. Explicitly update
+ # our associated player's position in case it is being used
+ # for logic (otherwise it will be out of date until next step)
+ self._drive_player_position()
+
elif isinstance(msg, ba.DieMessage):
# Report player deaths to the game.
@@ -271,7 +272,7 @@ class PlayerSpaz(Spaz, Generic[PlayerType]):
# Keep track of the player who last hit us for point rewarding.
elif isinstance(msg, ba.HitMessage):
- source_player = msg.get_source_player(self.playertype)
+ source_player = msg.get_source_player(type(self._player))
if source_player:
self.last_player_attacked_by = source_player
self.last_attacked_time = ba.time()
@@ -282,3 +283,16 @@ class PlayerSpaz(Spaz, Generic[PlayerType]):
activity.handlemessage(PlayerSpazHurtMessage(self))
else:
super().handlemessage(msg)
+
+ def _drive_player_position(self) -> None:
+ """Drive our ba.Player's official position
+
+ If our position is changed explicitly, this should be called again
+ to instantly update the player position (otherwise it would be out
+ of date until the next sim step)
+ """
+ player = self._player
+ if player:
+ assert self.node
+ assert player.node
+ self.node.connectattr('torso_position', player.node, 'position')
diff --git a/assets/src/ba_data/python/bastd/actor/spaz.py b/assets/src/ba_data/python/bastd/actor/spaz.py
index 007ffc1d..b1d64372 100644
--- a/assets/src/ba_data/python/bastd/actor/spaz.py
+++ b/assets/src/ba_data/python/bastd/actor/spaz.py
@@ -1070,9 +1070,9 @@ class Spaz(ba.Actor):
if self.hitpoints > 0:
# It's kinda crappy to die from impacts, so lets reduce
- # impact damage by a reasonable amount if it'll keep us alive
+ # impact damage by a reasonable amount *if* it'll keep us alive
if msg.hit_type == 'impact' and damage > self.hitpoints:
- # drop damage to whatever puts us at 10 hit points,
+ # Drop damage to whatever puts us at 10 hit points,
# or 200 less than it used to be whichever is greater
# (so it *can* still kill us if its high enough)
newdamage = max(damage - 200, self.hitpoints - 10)
@@ -1081,27 +1081,28 @@ class Spaz(ba.Actor):
# If we're holding something, drop it.
if damage > 0.0 and self.node.hold_node:
- # self.node.hold_node = ba.Node(None)
self.node.hold_node = None
self.hitpoints -= damage
self.node.hurt = 1.0 - float(
self.hitpoints) / self.hitpoints_max
+
# If we're cursed, *any* damage blows us up.
if self._cursed and damage > 0:
ba.timer(
0.05,
ba.WeakCall(self.curse_explode,
msg.get_source_player(ba.Player)))
- # if we're frozen, shatter.. otherwise die if we hit zero
+
+ # If we're frozen, shatter.. otherwise die if we hit zero
if self.frozen and (damage > 200 or self.hitpoints <= 0):
self.shatter()
elif self.hitpoints <= 0:
self.node.handlemessage(
ba.DieMessage(how=ba.DeathType.IMPACT))
- # if we're dead, take a look at the smoothed damage val
+ # If we're dead, take a look at the smoothed damage value
# (which gives us a smoothed average of recent damage) and shatter
- # us if its grown high enough
+ # us if its grown high enough.
if self.hitpoints <= 0:
damage_avg = self.node.damage_smoothed * damage_scale
if damage_avg > 1000:
@@ -1144,23 +1145,23 @@ class Spaz(ba.Actor):
return None
node = ba.get_collision_info('opposing_node')
- # only allow one hit per node per punch
+ # Only allow one hit per node per punch.
if node and (node not in self._punched_nodes):
punch_momentum_angular = (self.node.punch_momentum_angular *
self._punch_power_scale)
punch_power = self.node.punch_power * self._punch_power_scale
- # ok here's the deal: we pass along our base velocity for use
+ # Ok here's the deal: we pass along our base velocity for use
# in the impulse damage calculations since that is a more
# predictable value than our fist velocity, which is rather
- # erratic. ...however we want to actually apply force in the
- # direction our fist is moving so it looks better.. so we still
- # pass that along as a direction ..perhaps a time-averaged
- # fist-velocity would work too?.. should try that.
+ # erratic. However, we want to actually apply force in the
+ # direction our fist is moving so it looks better. So we still
+ # pass that along as a direction. Perhaps a time-averaged
+ # fist-velocity would work too?.. perhaps should try that.
- # if its something besides another spaz, just do a muffled
- # punch sound
+ # If its something besides another spaz, just do a muffled
+ # punch sound.
if node.getnodetype() != 'spaz':
sounds = get_factory().impact_sounds_medium
sound = sounds[random.randrange(len(sounds))]
@@ -1226,7 +1227,7 @@ class Spaz(ba.Actor):
if held and held.getnodetype() == 'flag':
return True
- # hold_body needs to be set before hold_node.
+ # Note: hold_body needs to be set before hold_node.
self.node.hold_body = opposing_body
self.node.hold_node = opposing_node
elif isinstance(msg, ba.CelebrateMessage):
@@ -1280,7 +1281,8 @@ class Spaz(ba.Actor):
def _pick_up(self, node: ba.Node) -> None:
if self.node:
- self.node.hold_body = 0 # needs to be set before hold_node
+ # Note: hold_body needs to be set before hold_node.
+ self.node.hold_body = 0
self.node.hold_node = node
def set_land_mine_count(self, count: int) -> None:
@@ -1317,7 +1319,7 @@ class Spaz(ba.Actor):
self.shattered = True
assert self.node
if self.frozen:
- # momentary flash of light
+ # Momentary flash of light.
light = ba.newnode('light',
attrs={
'position': self.node.position,
@@ -1333,7 +1335,8 @@ class Spaz(ba.Actor):
0.3: 0
})
ba.timer(0.3, light.delete)
- # emit ice chunks..
+
+ # Emit ice chunks.
ba.emitfx(position=self.node.position,
velocity=self.node.velocity,
count=int(random.random() * 10.0 + 10.0),
@@ -1399,8 +1402,8 @@ class Spaz(ba.Actor):
def set_bomb_count(self, count: int) -> None:
"""Sets the number of bombs this Spaz has."""
- # we cant just set bomb_count cuz some bombs may be laid currently
- # so we have to do a relative diff based on max
+ # We can't just set bomb_count because some bombs may be laid currently
+ # so we have to do a relative diff based on max.
diff = count - self._max_bomb_count
self._max_bomb_count += diff
self.bomb_count += diff
@@ -1413,7 +1416,7 @@ class Spaz(ba.Actor):
self.node.billboard_cross_out = True
def _gloves_wear_off(self) -> None:
- if self._demo_mode: # preserve old behavior
+ if self._demo_mode: # Preserve old behavior.
self._punch_power_scale = 1.2
self._punch_cooldown = BASE_PUNCH_COOLDOWN
else:
diff --git a/docs/ba_module.md b/docs/ba_module.md
index cfb7a5a6..1ae1ec10 100644
--- a/docs/ba_module.md
+++ b/docs/ba_module.md
@@ -3730,7 +3730,7 @@ even if myactor is set to None.
Attributes:
-
+
-
ba.Node
@@ -3738,6 +3738,14 @@ even if myactor is set to None.
This node can be used to get a generic player position/etc.
+
+-
+
ba.Vec3
+The position of the player, as defined by its current Actor.
+
+ This value should not be used when the player has no Actor, as
+ it is undefined in that case.
+
-
ba.SessionPlayer