fix: Tracer direction now matches bot facing

Root cause: startFiring() sent ChangeFireStateMessage_Start before
updating orientation. Client caches orientation at fire-start for
tracer rendering, so tracers went in spawn direction.

Fix: In startFiring(), calculate target yaw, update orientation,
broadcast PlayerState with correct facing, THEN send fire state.

Also fixed facingYawUpper to be 0 (relative to body) instead of
absolute yaw value.
This commit is contained in:
Claude 2025-11-23 08:34:18 +00:00
parent d3eb0f4d9e
commit dab40e2262
No known key found for this signature in database

View file

@ -362,16 +362,16 @@ class BotManager(zone: Zone) extends Actor {
player.GUID, player.GUID,
player.Position, player.Position,
player.Velocity, player.Velocity,
player.Orientation.z, player.Orientation.z, // facingYaw - body direction
0f, 0f, // facingPitch
player.Orientation.z, 0f, // facingYawUpper - relative to body, 0 = looking straight ahead
timestamp, timestamp,
is_crouching = false, is_crouching = false,
is_jumping = false, is_jumping = false,
jump_thrust = false, jump_thrust = false,
is_cloaked = false, is_cloaked = false,
spectator = false, spectator = false,
weaponInHand = true // Weapon is always drawn (set at spawn) weaponInHand = true // Weapon is always drawn (set at spawn)
) )
) )
} }
@ -538,7 +538,7 @@ class BotManager(zone: Zone) extends Actor {
if (ticksSinceAcquired >= recognitionTime) { if (ticksSinceAcquired >= recognitionTime) {
// Start firing if not already // Start firing if not already
if (!combat.isFiring) { if (!combat.isFiring) {
startFiring(botState, targetPlayer.Name) startFiring(botState, targetPlayer) // Pass target for orientation calculation
combat = combat.copy(isFiring = true, burstStartTick = tickCount, shotsFired = 0) combat = combat.copy(isFiring = true, burstStartTick = tickCount, shotsFired = 0)
} }
@ -578,11 +578,47 @@ class BotManager(zone: Zone) extends Actor {
combat combat
} }
/** Broadcast that bot started firing - uses LocalEvents like turrets for proper client rendering */ /**
private def startFiring(botState: BotState, targetName: String): Unit = { * Broadcast that bot started firing.
getWeapon(botState.player).foreach { weapon => * CRITICAL: Must update orientation and broadcast PlayerState BEFORE sending fire state,
log.info(s"${botState.name} is attacking $targetName") * otherwise client will use stale orientation for tracer direction.
// Use LocalEvents like turrets do for proper tracer rendering */
private def startFiring(botState: BotState, target: Player): Unit = {
val player = botState.player
// Calculate yaw to face target
val dx = target.Position.x - player.Position.x
val dy = target.Position.y - player.Position.y
val targetYaw = math.toDegrees(math.atan2(dx, dy)).toFloat
// Update bot's orientation to face target
player.Orientation = Vector3(0, 0, targetYaw)
// Broadcast PlayerState with correct orientation BEFORE fire state
// This ensures client has correct facing when fire state arrives
val timestamp = (System.currentTimeMillis() % 65536).toInt
zone.AvatarEvents ! AvatarServiceMessage(
zone.id,
AvatarAction.PlayerState(
player.GUID,
player.Position,
player.Velocity,
targetYaw, // facingYaw - body direction
0f, // facingPitch
0f, // facingYawUpper - relative to body, 0 = looking straight
timestamp,
is_crouching = false,
is_jumping = false,
jump_thrust = false,
is_cloaked = false,
spectator = false,
weaponInHand = true
)
)
// NOW send fire state - client should have correct orientation
getWeapon(player).foreach { weapon =>
log.info(s"${botState.name} is attacking ${target.Name}")
zone.LocalEvents ! LocalServiceMessage( zone.LocalEvents ! LocalServiceMessage(
zone.id, zone.id,
LocalAction.SendResponse(ChangeFireStateMessage_Start(weapon.GUID)) LocalAction.SendResponse(ChangeFireStateMessage_Start(weapon.GUID))