ace/Source/ACE.Server/Physics/ObjectInfo.cs
FlaggAC ef8374c03e
Performance improvements to Physics.Common.WeenieObject (#4212)
Entire set of fields can be marked as readonly, as it never changes outside of the constructor.

Reduces unnecessary reflection operations by pre-fetching them during constructor

Changes WeenieObject to a sealed class to guarantee immutability

Immutability allows a shared DummyObject to be used by PhysicsObjs which do not have a weenie.
2026-08-01 12:28:52 +00:00

174 lines
6.5 KiB
C#

using System;
using System.Numerics;
using ACE.Entity.Enum;
namespace ACE.Server.Physics.Animation
{
[Flags]
public enum ObjectInfoState
{
Default = 0x0,
Contact = 0x1,
OnWalkable = 0x2,
IsViewer = 0x4,
PathClipped = 0x8,
FreeRotate = 0x10,
PerfectClip = 0x40,
IsImpenetrable = 0x80,
IsPlayer = 0x100,
EdgeSlide = 0x200,
IgnoreCreatures = 0x400,
IsPK = 0x800,
IsPKLite = 0x1000
};
public class ObjectInfo
{
public PhysicsObj Object;
public ObjectInfoState State;
public float Scale;
public float StepUpHeight;
public float StepDownHeight;
public bool Ethereal;
public bool StepDown;
public uint TargetID;
public float GetWalkableZ()
{
return Object.get_walkable_z();
}
public void Init(PhysicsObj obj, ObjectInfoState state)
{
Object = obj;
State = state; // copy constructor?
Scale = Object.Scale;
StepUpHeight = Object.GetStepUpHeight();
StepDownHeight = Object.GetStepDownHeight();
Ethereal = Object.State.HasFlag(PhysicsState.Ethereal);
StepDown = !Object.State.HasFlag(PhysicsState.Missile);
var wobj = Object.WeenieObj;
if (wobj != null)
{
if (wobj.IsImpenetrable())
State |= ObjectInfoState.IsImpenetrable;
if (wobj.IsPlayer)
State |= ObjectInfoState.IsPlayer;
if (wobj.IsPK())
State |= ObjectInfoState.IsPK;
if (wobj.IsPKLite())
State |= ObjectInfoState.IsPKLite;
}
if (obj.ProjectileTarget != null)
TargetID = obj.ProjectileTarget.ID;
}
public bool IsValidWalkable(Vector3 normal)
{
return PhysicsObj.is_valid_walkable(normal);
}
public bool MissileIgnore(PhysicsObj collideObj)
{
// modified for 2-way
if (collideObj.State.HasFlag(PhysicsState.Missile))
{
if (!Object.IsPlayer)
return true;
if (collideObj.ProjectileTarget == null || collideObj.ProjectileTarget == Object)
return false;
return true;
}
if (Object.State.HasFlag(PhysicsState.Missile))
{
if (collideObj.WeenieObj != null && collideObj.ID != TargetID)
{
if (collideObj.State.HasFlag(PhysicsState.Ethereal) || TargetID != 0 && collideObj.WeenieObj.IsCreature)
return true;
}
}
return false;
}
public void StopVelocity()
{
Object.set_velocity(Vector3.Zero, false);
}
public TransitionState ValidateWalkable(Sphere checkPos, Plane contactPlane, bool isWater, float waterDepth, Transition transition, uint landCellID)
{
var path = transition.SpherePath;
var collision = transition.CollisionInfo;
if (State.HasFlag(ObjectInfoState.IsViewer))
{
var dist = Vector3.Dot(checkPos.Center, contactPlane.Normal) + contactPlane.D - checkPos.Radius;
if (dist > -PhysicsGlobals.EPSILON && path.BeginPos != null && (path.BeginPos.ObjCellID & 0xFFFF) >= 0x100)
return TransitionState.OK;
var offset = checkPos.Center - path.GlobalCurrCenter[0].Center;
var angle = dist / Vector3.Dot(offset, contactPlane.Normal);
if ((angle <= 0.0f || angle > 1.0f) && path.BeginPos != null && (path.BeginPos.ObjCellID & 0xFFFF) >= 0x100)
return TransitionState.OK;
path.AddOffsetToCheckPos(offset * -angle);
collision.SetCollisionNormal(contactPlane.Normal);
collision.CollidedWithEnvironment = true;
return TransitionState.Adjusted;
}
else
{
var dist = Vector3.Dot(checkPos.Center - new Vector3(0, 0, checkPos.Radius), contactPlane.Normal) + contactPlane.D + waterDepth;
if (dist >= -PhysicsGlobals.EPSILON)
{
if (dist > PhysicsGlobals.EPSILON)
return TransitionState.OK;
if (path.StepDown || !State.HasFlag(ObjectInfoState.OnWalkable) || PhysicsObj.is_valid_walkable(contactPlane.Normal))
{
collision.SetContactPlane(contactPlane, isWater);
collision.ContactPlaneCellID = landCellID;
}
if (!State.HasFlag(ObjectInfoState.Contact) && !path.StepDown)
{
collision.SetCollisionNormal(contactPlane.Normal);
collision.CollidedWithEnvironment = true;
}
return TransitionState.OK;
}
else
{
if (path.CheckWalkable) return TransitionState.Collided;
var zDist = dist / contactPlane.Normal.Z;
if (path.StepDown || !State.HasFlag(ObjectInfoState.OnWalkable) || PhysicsObj.is_valid_walkable(contactPlane.Normal))
{
collision.SetContactPlane(contactPlane, isWater);
collision.ContactPlaneCellID = landCellID;
if (path.StepDown)
{
var interp = (1.0f - -1.0f / (path.StepDownAmt * path.WalkInterp) * zDist) * path.WalkInterp;
if (interp >= path.WalkInterp || interp < -0.1f)
return TransitionState.Collided;
path.WalkInterp = interp;
}
var offset = new Vector3(0, 0, -zDist);
path.AddOffsetToCheckPos(offset);
}
if (!State.HasFlag(ObjectInfoState.Contact) && !path.StepDown)
{
collision.SetCollisionNormal(contactPlane.Normal);
collision.CollidedWithEnvironment = true;
}
return TransitionState.Adjusted;
}
}
}
}
}