Preserve canonical live-object ownership across Hidden transitions and remote teleport placement so effects, collision, streaming, and targeting remain synchronized.
89 lines
3.3 KiB
C#
89 lines
3.3 KiB
C#
namespace AcDream.Core.Physics;
|
|
|
|
/// <summary>
|
|
/// Result of retail <c>CPhysicsObj::set_state</c> (<c>0x00514DD0</c>) and
|
|
/// the nested <c>set_hidden</c> call (<c>0x00514C60</c>).
|
|
/// </summary>
|
|
public readonly record struct RetailPhysicsStateTransition(
|
|
PhysicsStateFlags PreviousState,
|
|
PhysicsStateFlags RequestedState,
|
|
PhysicsStateFlags FinalState,
|
|
bool LightingChanged,
|
|
bool NoDrawChanged,
|
|
RetailHiddenTransition HiddenTransition)
|
|
{
|
|
public bool HasChanges => PreviousState != FinalState;
|
|
}
|
|
|
|
public enum RetailHiddenTransition
|
|
{
|
|
None,
|
|
BecameHidden,
|
|
BecameVisible,
|
|
}
|
|
|
|
/// <summary>
|
|
/// Pure port of retail's PhysicsState side-effect ordering. The server's raw
|
|
/// state is installed first; Lighting and NoDraw are processed next; Hidden is
|
|
/// processed last and may rewrite ReportCollisions/IgnoreCollisions.
|
|
/// </summary>
|
|
public static class RetailPhysicsStateTransitions
|
|
{
|
|
/// <summary>
|
|
/// Retail <c>CPhysicsObj</c> constructor state at <c>0x00512508</c>.
|
|
/// This deliberately does not contain Hidden: a normal visible
|
|
/// CreateObject must not manufacture an UnHide script.
|
|
/// </summary>
|
|
public const PhysicsStateFlags ConstructorState =
|
|
PhysicsStateFlags.EdgeSlide
|
|
| PhysicsStateFlags.Lighting
|
|
| PhysicsStateFlags.Gravity
|
|
| PhysicsStateFlags.ReportCollisions;
|
|
|
|
public static RetailPhysicsStateTransition Apply(
|
|
PhysicsStateFlags previousState,
|
|
PhysicsStateFlags requestedState)
|
|
{
|
|
// CPhysicsObj::set_state compares only the low 16 bits. Every state
|
|
// with a set_state side effect (Lighting, NoDraw, Hidden) lives there.
|
|
uint changedLow = ((uint)previousState ^ (uint)requestedState) & 0xFFFFu;
|
|
bool lightingChanged =
|
|
(changedLow & (uint)PhysicsStateFlags.Lighting) != 0;
|
|
bool noDrawChanged =
|
|
(changedLow & (uint)PhysicsStateFlags.NoDraw) != 0;
|
|
bool hiddenChanged =
|
|
(changedLow & (uint)PhysicsStateFlags.Hidden) != 0;
|
|
|
|
PhysicsStateFlags finalState = requestedState;
|
|
RetailHiddenTransition hiddenTransition = RetailHiddenTransition.None;
|
|
if (hiddenChanged)
|
|
{
|
|
if ((requestedState & PhysicsStateFlags.Hidden) != 0)
|
|
{
|
|
// set_hidden(true): report_collision_end, clear reporting,
|
|
// then enable collision-ignore before hiding from the cell.
|
|
finalState &= ~PhysicsStateFlags.ReportCollisions;
|
|
finalState |= PhysicsStateFlags.Hidden
|
|
| PhysicsStateFlags.IgnoreCollisions;
|
|
hiddenTransition = RetailHiddenTransition.BecameHidden;
|
|
}
|
|
else
|
|
{
|
|
// set_hidden(false): clear ignore and restore collision
|
|
// reporting even when the incoming raw state omitted it.
|
|
finalState &= ~(PhysicsStateFlags.Hidden
|
|
| PhysicsStateFlags.IgnoreCollisions);
|
|
finalState |= PhysicsStateFlags.ReportCollisions;
|
|
hiddenTransition = RetailHiddenTransition.BecameVisible;
|
|
}
|
|
}
|
|
|
|
return new RetailPhysicsStateTransition(
|
|
previousState,
|
|
requestedState,
|
|
finalState,
|
|
lightingChanged,
|
|
noDrawChanged,
|
|
hiddenTransition);
|
|
}
|
|
}
|