mirror of
https://github.com/ACEmulator/ACE
synced 2026-08-17 12:26:06 -04:00
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.
174 lines
6.5 KiB
C#
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;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|