feat(vfx): port retail hidden and teleport presentation
Preserve canonical live-object ownership across Hidden transitions and remote teleport placement so effects, collision, streaming, and targeting remain synchronized.
This commit is contained in:
parent
a51ebc66e9
commit
1e98d81448
46 changed files with 4883 additions and 127 deletions
|
|
@ -251,7 +251,7 @@ internal sealed class ProjectileController
|
|||
entity.MeshRefs.Count > 0);
|
||||
_liveEntities.SetProjectileRuntime(record.ServerGuid, runtime);
|
||||
|
||||
if (HasVisibleCell(record))
|
||||
if (HasVisibleCell(record) && !IsHidden(record))
|
||||
{
|
||||
ShadowPositionSynchronizer.Sync(
|
||||
_shadows,
|
||||
|
|
@ -262,6 +262,15 @@ internal sealed class ProjectileController
|
|||
liveCenterX,
|
||||
liveCenterY);
|
||||
}
|
||||
else if (HasVisibleCell(record))
|
||||
{
|
||||
// Hidden is a retained in-world CPhysicsObj, not leave_world.
|
||||
// Keep Active/identity but remove collision rows and consume the
|
||||
// hidden clock so UnHide cannot replay a time backlog.
|
||||
body.InWorld = true;
|
||||
body.LastUpdateTime = currentTime;
|
||||
_shadows.Suspend(entity.Id);
|
||||
}
|
||||
else
|
||||
{
|
||||
SuspendOutsideWorld(runtime, entity.Id);
|
||||
|
|
@ -300,7 +309,8 @@ internal sealed class ProjectileController
|
|||
|
||||
SetVelocity(runtime.Body, velocity, currentTime);
|
||||
runtime.Body.Omega = angularVelocity;
|
||||
if (!_liveEntities.ShouldAdvanceRootRuntime(serverGuid))
|
||||
if (!_liveEntities.ShouldAdvanceRootRuntime(serverGuid)
|
||||
&& !IsHidden(record))
|
||||
Deactivate(runtime.Body);
|
||||
return true;
|
||||
}
|
||||
|
|
@ -425,7 +435,7 @@ internal sealed class ProjectileController
|
|||
entity.Rotation = orientation;
|
||||
entity.ParentCellId = canonicalCellId;
|
||||
_liveEntities.RebucketLiveEntity(serverGuid, canonicalCellId);
|
||||
if (HasVisibleCell(record))
|
||||
if (HasVisibleCell(record) && !IsHidden(record))
|
||||
{
|
||||
if (!wasInWorld)
|
||||
Activate(body, currentTime);
|
||||
|
|
@ -439,6 +449,12 @@ internal sealed class ProjectileController
|
|||
liveCenterX,
|
||||
liveCenterY);
|
||||
}
|
||||
else if (HasVisibleCell(record))
|
||||
{
|
||||
body.InWorld = true;
|
||||
body.LastUpdateTime = currentTime;
|
||||
_shadows.Suspend(entity.Id);
|
||||
}
|
||||
else
|
||||
{
|
||||
SuspendOutsideWorld(runtime, entity.Id);
|
||||
|
|
@ -496,6 +512,27 @@ internal sealed class ProjectileController
|
|||
|| record.WorldEntity is not { } entity)
|
||||
continue;
|
||||
|
||||
runtime.Body.State = record.FinalPhysicsState;
|
||||
if (IsHidden(record))
|
||||
{
|
||||
if (!HasVisibleCell(record))
|
||||
{
|
||||
SuspendOutsideWorld(runtime, entity.Id);
|
||||
}
|
||||
else
|
||||
{
|
||||
// UpdatePositionInternal skips root physics/PartArray
|
||||
// while Hidden, but update_object still advances its
|
||||
// clock and does not clear Active.
|
||||
runtime.Body.InWorld = true;
|
||||
runtime.Body.LastUpdateTime = currentTime;
|
||||
if ((record.FinalPhysicsState & PhysicsStateFlags.Frozen) != 0)
|
||||
Deactivate(runtime.Body);
|
||||
_shadows.Suspend(entity.Id);
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
bool isMissile =
|
||||
(record.FinalPhysicsState & PhysicsStateFlags.Missile) != 0;
|
||||
if (!isMissile)
|
||||
|
|
@ -530,7 +567,6 @@ internal sealed class ProjectileController
|
|||
continue;
|
||||
}
|
||||
|
||||
runtime.Body.State = record.FinalPhysicsState;
|
||||
if (!_liveEntities.ShouldAdvanceRootRuntime(record.ServerGuid))
|
||||
{
|
||||
if (!HasVisibleCell(record))
|
||||
|
|
@ -637,6 +673,9 @@ internal sealed class ProjectileController
|
|||
&& record.IsSpatiallyVisible
|
||||
&& record.FullCellId != 0;
|
||||
|
||||
private static bool IsHidden(LiveEntityRecord record) =>
|
||||
(record.FinalPhysicsState & PhysicsStateFlags.Hidden) != 0;
|
||||
|
||||
private void SuspendOutsideWorld(Runtime runtime, uint localEntityId)
|
||||
{
|
||||
runtime.Body.InWorld = false;
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue