fix(combat): escape occupied login positions

Port retail's radius-aware placement ring so a relogging player is seated beside creatures occupying the saved location, and register the local body in the shared resolved-shadow pipeline. Route new forward movement and jump through AbortAutomaticAttack so repeat combat cancels immediately on movement.

Co-Authored-By: Codex <codex@openai.com>
This commit is contained in:
Erik 2026-07-12 22:20:17 +02:00
parent e671b8a5c4
commit 00fe993f6f
11 changed files with 643 additions and 16 deletions

View file

@ -607,13 +607,14 @@ internal sealed class RemotePhysicsUpdater
/// </summary>
public void SyncRemoteShadowToBody(uint entityId, RemoteMotion rm, int liveCenterX, int liveCenterY)
{
int shLbX = (int)((rm.CellId >> 24) & 0xFFu);
int shLbY = (int)((rm.CellId >> 16) & 0xFFu);
float shOffX = (shLbX - liveCenterX) * 192f;
float shOffY = (shLbY - liveCenterY) * 192f;
_physicsEngine.ShadowObjects.UpdatePosition(
entityId, rm.Body.Position, rm.Body.Orientation,
shOffX, shOffY, rm.CellId, seedCellId: rm.CellId);
ShadowPositionSynchronizer.Sync(
_physicsEngine.ShadowObjects,
entityId,
rm.Body.Position,
rm.Body.Orientation,
rm.CellId,
liveCenterX,
liveCenterY);
rm.LastShadowSyncPos = rm.Body.Position;
}
}

View file

@ -0,0 +1,39 @@
using System.Numerics;
using AcDream.Core.Physics;
namespace AcDream.App.Physics;
/// <summary>
/// Shared host adapter for retail's moved-object shadow re-registration
/// (<c>CPhysicsObj::SetPositionInternal</c> 0x00515330). Both the local body
/// and remote dead-reckoning bodies publish their resolved position through
/// this one cell-offset calculation.
/// </summary>
internal static class ShadowPositionSynchronizer
{
public static void Sync(
ShadowObjectRegistry registry,
uint entityId,
Vector3 position,
Quaternion orientation,
uint cellId,
int liveCenterX,
int liveCenterY)
{
if (cellId == 0)
return;
int landblockX = (int)((cellId >> 24) & 0xFFu);
int landblockY = (int)((cellId >> 16) & 0xFFu);
float worldOffsetX = (landblockX - liveCenterX) * 192f;
float worldOffsetY = (landblockY - liveCenterY) * 192f;
registry.UpdatePosition(
entityId,
position,
orientation,
worldOffsetX,
worldOffsetY,
cellId,
seedCellId: cellId);
}
}