Fixed a bug with player positions being incorrect

This commit is contained in:
Eric Froemling 2020-05-21 15:43:10 -07:00
parent 90b028da18
commit d6ccf2b12d
5 changed files with 73 additions and 41 deletions

View File

@ -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]

View File

@ -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.

View File

@ -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')

View File

@ -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:

View File

@ -3730,7 +3730,7 @@ even if myactor is set to None.</p>
</p>
<h3>Attributes:</h3>
<h5><a href="#attr_ba_Player__node">node</a>, <a href="#attr_ba_Player__sessionplayer">sessionplayer</a></h5>
<h5><a href="#attr_ba_Player__node">node</a>, <a href="#attr_ba_Player__position">position</a>, <a href="#attr_ba_Player__sessionplayer">sessionplayer</a></h5>
<dl>
<dt><h4><a name="attr_ba_Player__node">node</a></h4></dt><dd>
<p><span><a href="#class_ba_Node">ba.Node</a></span></p>
@ -3738,6 +3738,14 @@ even if myactor is set to None.</p>
<p> This node can be used to get a generic player position/etc.</p>
</dd>
<dt><h4><a name="attr_ba_Player__position">position</a></h4></dt><dd>
<p><span><a href="#class_ba_Vec3">ba.Vec3</a></span></p>
<p>The position of the player, as defined by its current Actor.</p>
<p> This value should not be used when the player has no Actor, as
it is undefined in that case.</p>
</dd>
<dt><h4><a name="attr_ba_Player__sessionplayer">sessionplayer</a></h4></dt><dd>
<p><span><a href="#class_ba_SessionPlayer">ba.SessionPlayer</a></span></p>