87 lines
3.3 KiB
C#
87 lines
3.3 KiB
C#
using AcDream.App.Physics;
|
|
using AcDream.App.Rendering.Vfx;
|
|
using AcDream.App.World;
|
|
using AcDream.Core.Physics;
|
|
using AcDream.Core.World;
|
|
|
|
namespace AcDream.App.Rendering;
|
|
|
|
/// <summary>
|
|
/// Commits a changed root produced by retail's
|
|
/// <c>CPhysicsObj::animate_static_object</c> to the retained effect pose and
|
|
/// collision-shadow projections of the same live object incarnation.
|
|
/// </summary>
|
|
internal sealed class StaticLiveRootCommitter
|
|
{
|
|
private readonly ILiveEntityRuntimeSource _runtime;
|
|
private readonly ShadowObjectRegistry _shadows;
|
|
private readonly LiveWorldOriginState _origin;
|
|
private readonly EntityEffectPoseRegistry _effectPoses;
|
|
|
|
public StaticLiveRootCommitter(
|
|
ILiveEntityRuntimeSource runtime,
|
|
ShadowObjectRegistry shadows,
|
|
LiveWorldOriginState origin,
|
|
EntityEffectPoseRegistry effectPoses)
|
|
{
|
|
_runtime = runtime ?? throw new ArgumentNullException(nameof(runtime));
|
|
_shadows = shadows ?? throw new ArgumentNullException(nameof(shadows));
|
|
_origin = origin ?? throw new ArgumentNullException(nameof(origin));
|
|
_effectPoses = effectPoses ?? throw new ArgumentNullException(nameof(effectPoses));
|
|
}
|
|
|
|
public bool Commit(WorldEntity entity, PhysicsBody body)
|
|
{
|
|
ArgumentNullException.ThrowIfNull(entity);
|
|
ArgumentNullException.ThrowIfNull(body);
|
|
LiveEntityRuntime? runtime = _runtime.Current;
|
|
if (runtime is null
|
|
|| !runtime.TryGetRecord(entity.ServerGuid, out LiveEntityRecord record)
|
|
|| !ReferenceEquals(record.WorldEntity, entity)
|
|
|| !ReferenceEquals(record.PhysicsBody, body))
|
|
{
|
|
return false;
|
|
}
|
|
|
|
// The retained pose owner follows the CPhysicsObj root even while its
|
|
// mesh is hidden. Hidden is presentation state, not logical teardown.
|
|
_effectPoses.UpdateRoot(entity);
|
|
|
|
// EffectPoseChanged observers run synchronously and may delete this
|
|
// incarnation (or replace the GUID) before returning. Do not let the
|
|
// stale root restore collision shadows for an owner which no longer
|
|
// exists. This is the same callback-boundary lifetime rule used by the
|
|
// live schedulers and movement controllers.
|
|
if (!runtime.TryGetRecord(entity.ServerGuid, out LiveEntityRecord current)
|
|
|| !ReferenceEquals(current, record)
|
|
|| !ReferenceEquals(current.WorldEntity, entity)
|
|
|| !ReferenceEquals(current.PhysicsBody, body))
|
|
{
|
|
return false;
|
|
}
|
|
|
|
// CObjCell::hide_object removes shadows. Never let a later static
|
|
// animation tick re-register collision for a hidden, withdrawn, or
|
|
// pending projection; UnHide/re-entry owns restoration.
|
|
if (record.ProjectionKind is not LiveEntityProjectionKind.World
|
|
|| !record.IsSpatiallyProjected
|
|
|| !record.IsSpatiallyVisible
|
|
|| runtime.IsHidden(entity.ServerGuid))
|
|
{
|
|
return true;
|
|
}
|
|
|
|
uint cellId = body.CellPosition.ObjCellId != 0
|
|
? body.CellPosition.ObjCellId
|
|
: record.FullCellId;
|
|
ShadowPositionSynchronizer.Sync(
|
|
_shadows,
|
|
entity.Id,
|
|
body.Position,
|
|
body.Orientation,
|
|
cellId,
|
|
_origin.CenterX,
|
|
_origin.CenterY);
|
|
return true;
|
|
}
|
|
}
|