mirror of
https://github.com/ACEmulator/ACE
synced 2026-08-17 12:26:06 -04:00
* Network performance improvements, limit boxing * Polygon comment out redundant test code * Transition remove duplicate Init() * Creature_BodyPart and Monster_Melee comment out unused var
1091 lines
40 KiB
C#
1091 lines
40 KiB
C#
using System;
|
|
using System.Linq;
|
|
using System.Collections.Generic;
|
|
using System.Numerics;
|
|
using ACE.Entity.Enum;
|
|
using ACE.Server.Physics.Collision;
|
|
using ACE.Server.Physics.Common;
|
|
using ACE.Server.Physics.Extensions;
|
|
|
|
namespace ACE.Server.Physics.Animation
|
|
{
|
|
public enum TransitionState
|
|
{
|
|
Invalid = 0x0,
|
|
OK = 0x1,
|
|
Collided = 0x2,
|
|
Adjusted = 0x3,
|
|
Slid = 0x4
|
|
};
|
|
|
|
public class Transition
|
|
{
|
|
public ObjectInfo ObjectInfo;
|
|
public SpherePath SpherePath;
|
|
public CollisionInfo CollisionInfo;
|
|
public CellArray CellArray;
|
|
//public ObjCell NewCellPtr;
|
|
|
|
public Transition()
|
|
{
|
|
Init();
|
|
}
|
|
|
|
public Vector3 AdjustOffset(Vector3 _offset)
|
|
{
|
|
var offset = new Vector3(_offset.X, _offset.Y, _offset.Z);
|
|
var checkSlide = false;
|
|
|
|
var slidingAngle = Vector3.Dot(offset, CollisionInfo.SlidingNormal);
|
|
if (CollisionInfo.SlidingNormalValid)
|
|
{
|
|
if (slidingAngle < 0.0f)
|
|
checkSlide = true;
|
|
else
|
|
CollisionInfo.SlidingNormalValid = false;
|
|
}
|
|
|
|
if (!CollisionInfo.ContactPlaneValid)
|
|
{
|
|
if (checkSlide)
|
|
offset -= CollisionInfo.SlidingNormal * slidingAngle;
|
|
|
|
return offset;
|
|
}
|
|
|
|
var collisionAngle = Vector3.Dot(offset, CollisionInfo.ContactPlane.Normal);
|
|
var slideOffset = Vector3.Cross(CollisionInfo.ContactPlane.Normal, CollisionInfo.SlidingNormal);
|
|
|
|
if (checkSlide)
|
|
{
|
|
if (Vec.NormalizeCheckSmall(ref slideOffset))
|
|
offset = Vector3.Zero;
|
|
else
|
|
offset = Vector3.Dot(slideOffset, offset) * slideOffset;
|
|
}
|
|
else if (collisionAngle <= 0.0f)
|
|
offset -= CollisionInfo.ContactPlane.Normal * collisionAngle;
|
|
else
|
|
CollisionInfo.ContactPlane.SnapToPlane(ref offset);
|
|
|
|
if (CollisionInfo.ContactPlaneCellID == 0 || CollisionInfo.ContactPlaneIsWater)
|
|
return offset;
|
|
|
|
var globSphere = SpherePath.GlobalSphere[0];
|
|
var blockOffset = LandDefs.GetBlockOffset(SpherePath.CheckPos.ObjCellID, CollisionInfo.ContactPlaneCellID);
|
|
var dist = Vector3.Dot(globSphere.Center - blockOffset, CollisionInfo.ContactPlane.Normal) + CollisionInfo.ContactPlane.D;
|
|
if (dist >= globSphere.Radius - PhysicsGlobals.EPSILON)
|
|
return offset;
|
|
|
|
var zDist = (globSphere.Radius - dist) / CollisionInfo.ContactPlane.Normal.Z;
|
|
if (globSphere.Radius > Math.Abs(zDist))
|
|
{
|
|
var checkOffset = new Vector3(0, 0, zDist);
|
|
SpherePath.AddOffsetToCheckPos(checkOffset);
|
|
}
|
|
return offset;
|
|
}
|
|
|
|
public void BuildCellArray(ref ObjCell newCell)
|
|
{
|
|
SpherePath.CellArrayValid = true;
|
|
SpherePath.HitsInteriorCell = false;
|
|
|
|
ObjCell.find_cell_list(CellArray, ref newCell, SpherePath);
|
|
}
|
|
|
|
public void CalcNumSteps(ref Vector3 offset, ref Vector3 offsetPerStep, ref int numSteps)
|
|
{
|
|
if (SpherePath.BeginPos == null)
|
|
{
|
|
offset = Vector3.Zero;
|
|
offsetPerStep = Vector3.Zero;
|
|
numSteps = 1;
|
|
return;
|
|
}
|
|
offset = SpherePath.BeginPos.GetOffset(SpherePath.EndPos);
|
|
var dist = offset.Length();
|
|
var step = dist / SpherePath.LocalSphere[0].Radius;
|
|
|
|
if (!ObjectInfo.State.HasFlag(ObjectInfoState.IsViewer))
|
|
{
|
|
if (step > 1.0f)
|
|
{
|
|
numSteps = (int)Math.Ceiling(step);
|
|
offsetPerStep = offset * (1.0f / numSteps);
|
|
}
|
|
else if (!offset.Equals(Vector3.Zero))
|
|
{
|
|
offsetPerStep = offset;
|
|
numSteps = 1;
|
|
}
|
|
else
|
|
{
|
|
offsetPerStep = Vector3.Zero;
|
|
numSteps = 0;
|
|
}
|
|
return;
|
|
}
|
|
|
|
if (dist <= PhysicsGlobals.EPSILON)
|
|
{
|
|
offsetPerStep = Vector3.Zero;
|
|
numSteps = 0;
|
|
}
|
|
else
|
|
{
|
|
offsetPerStep = offset * (1.0f / step);
|
|
numSteps = (int)Math.Floor(step) + 1;
|
|
}
|
|
}
|
|
|
|
public bool CheckCollisions(PhysicsObj obj)
|
|
{
|
|
SpherePath.InsertType = InsertType.Placement;
|
|
SpherePath.SetCheckPos(SpherePath.CurPos, SpherePath.CurCell);
|
|
|
|
return obj.FindObjCollisions(this) != TransitionState.OK;
|
|
}
|
|
|
|
public TransitionState CheckOtherCells(ObjCell currCell)
|
|
{
|
|
var result = TransitionState.OK;
|
|
|
|
SpherePath.CellArrayValid = true;
|
|
SpherePath.HitsInteriorCell = false;
|
|
|
|
//ObjCell newCell = null;
|
|
var newCell = ObjCell.EmptyCell; // null check?
|
|
ObjCell.find_cell_list(CellArray, ref newCell, SpherePath);
|
|
|
|
for (var i = 0; i < CellArray.Cells.Count; i++)
|
|
{
|
|
var cell = CellArray.Cells.Values.ElementAt(i);
|
|
if (cell == null || cell.Equals(currCell)) continue;
|
|
|
|
var collides = cell.FindCollisions(this);
|
|
switch (collides)
|
|
{
|
|
case TransitionState.Slid:
|
|
CollisionInfo.ContactPlaneValid = false;
|
|
CollisionInfo.ContactPlaneIsWater = false;
|
|
return collides;
|
|
|
|
case TransitionState.Collided:
|
|
case TransitionState.Adjusted:
|
|
return collides;
|
|
}
|
|
}
|
|
SpherePath.CheckCell = newCell;
|
|
|
|
if (newCell != null)
|
|
{
|
|
SpherePath.AdjustCheckPos(newCell.ID);
|
|
return result;
|
|
}
|
|
|
|
if (SpherePath.StepDown)
|
|
return TransitionState.Collided;
|
|
|
|
var checkPos = new Position(SpherePath.CheckPos);
|
|
if ((checkPos.ObjCellID & 0xFFFF) < 0x100)
|
|
LandDefs.AdjustToOutside(checkPos);
|
|
|
|
if (checkPos.ObjCellID != 0)
|
|
{
|
|
SpherePath.AdjustCheckPos(checkPos.ObjCellID);
|
|
SpherePath.SetCheckPos(checkPos, null);
|
|
|
|
SpherePath.CellArrayValid = true;
|
|
|
|
return result;
|
|
}
|
|
return TransitionState.Collided;
|
|
}
|
|
|
|
public bool CheckWalkable(float zCheck)
|
|
{
|
|
if (!ObjectInfo.State.HasFlag(ObjectInfoState.OnWalkable) || SpherePath.CheckWalkables())
|
|
return true;
|
|
|
|
SpherePath.BackupCheckPos = new Position(SpherePath.CheckPos);
|
|
SpherePath.BackupCell = SpherePath.CheckCell;
|
|
|
|
var stepHeight = ObjectInfo.StepDownHeight;
|
|
|
|
SpherePath.WalkableAllowance = zCheck;
|
|
SpherePath.CheckWalkable = true;
|
|
|
|
var globSphere = SpherePath.GlobalSphere[0];
|
|
if (SpherePath.NumSphere < 2 && stepHeight > globSphere.Radius * 2)
|
|
stepHeight = globSphere.Radius * 0.5f;
|
|
|
|
if (stepHeight > globSphere.Radius * 2)
|
|
stepHeight *= 0.5f;
|
|
|
|
var offset = new Vector3(0, 0, -stepHeight);
|
|
SpherePath.AddOffsetToCheckPos(offset);
|
|
|
|
var transitionState = TransitionalInsert(1);
|
|
|
|
SpherePath.CheckWalkable = false;
|
|
SpherePath.SetCheckPos(SpherePath.BackupCheckPos, SpherePath.BackupCell);
|
|
|
|
return transitionState != TransitionState.OK;
|
|
}
|
|
|
|
public void CleanupTransition()
|
|
{
|
|
// removed static transitions
|
|
}
|
|
|
|
public TransitionState CliffSlide(Plane contactPlane)
|
|
{
|
|
var contactNormal = Vector3.Cross(contactPlane.Normal, CollisionInfo.LastKnownContactPlane.Normal);
|
|
contactNormal.Z = 0.0f;
|
|
|
|
var collideNormal = new Vector3(contactNormal.Z - contactNormal.Y, contactNormal.X - contactNormal.Z, 0);
|
|
if (Vec.NormalizeCheckSmall(ref collideNormal))
|
|
return TransitionState.OK;
|
|
|
|
var blockOffset = LandDefs.GetBlockOffset(SpherePath.CurPos.ObjCellID, SpherePath.CheckPos.ObjCellID);
|
|
var offset = SpherePath.GlobalSphere[0].Center - SpherePath.GlobalCurrCenter[0].Center;
|
|
|
|
var angle = Vector3.Dot(collideNormal, offset + blockOffset);
|
|
if (angle <= 0.0f)
|
|
{
|
|
SpherePath.AddOffsetToCheckPos(collideNormal * angle);
|
|
CollisionInfo.SetCollisionNormal(collideNormal);
|
|
}
|
|
else
|
|
{
|
|
SpherePath.AddOffsetToCheckPos(collideNormal * -angle);
|
|
CollisionInfo.SetCollisionNormal(-collideNormal);
|
|
}
|
|
return TransitionState.Adjusted;
|
|
}
|
|
|
|
public bool EdgeSlide(ref TransitionState transitionState, float stepDownHeight, float zval)
|
|
{
|
|
if (!ObjectInfo.State.HasFlag(ObjectInfoState.OnWalkable) || !ObjectInfo.State.HasFlag(ObjectInfoState.EdgeSlide))
|
|
{
|
|
transitionState = SetEdgeSlide(true, true, TransitionState.OK);
|
|
return true;
|
|
}
|
|
|
|
if (CollisionInfo.ContactPlaneValid && CollisionInfo.ContactPlane.Normal.Z < zval)
|
|
{
|
|
transitionState = SetEdgeSlide(true, false, CliffSlide(CollisionInfo.ContactPlane));
|
|
return false;
|
|
}
|
|
|
|
if (SpherePath.Walkable != null)
|
|
{
|
|
transitionState = SetEdgeSlide(false, false, SpherePath.PrecipiceSlide(this));
|
|
|
|
return transitionState == TransitionState.Collided;
|
|
}
|
|
|
|
if (CollisionInfo.ContactPlaneValid)
|
|
{
|
|
transitionState = SetEdgeSlide(true, true, TransitionState.OK);
|
|
return true;
|
|
}
|
|
|
|
var offset = SpherePath.GlobalCurrCenter[0].Center - SpherePath.GlobalSphere[0].Center;
|
|
SpherePath.AddOffsetToCheckPos(offset);
|
|
|
|
StepDown(stepDownHeight, zval);
|
|
|
|
CollisionInfo.ContactPlaneValid = false;
|
|
CollisionInfo.ContactPlaneIsWater = false;
|
|
SpherePath.RestoreCheckPos();
|
|
|
|
if (SpherePath.Walkable != null)
|
|
{
|
|
SpherePath.CacheLocalSpaceSphere(SpherePath.GetWalkablePos(), SpherePath.WalkableScale);
|
|
SpherePath.SetWalkableCheckPos(SpherePath.LocalSpaceSphere[0]);
|
|
|
|
transitionState = SpherePath.PrecipiceSlide(this);
|
|
return transitionState == TransitionState.Collided;
|
|
}
|
|
else
|
|
{
|
|
SpherePath.Walkable = null;
|
|
SpherePath.CellArrayValid = true;
|
|
|
|
transitionState = TransitionState.Collided;
|
|
return true;
|
|
}
|
|
}
|
|
|
|
public TransitionState SetEdgeSlide(bool unwalkable, bool validCell, TransitionState transitionState)
|
|
{
|
|
if (unwalkable) SpherePath.Walkable = null;
|
|
|
|
SpherePath.RestoreCheckPos();
|
|
|
|
CollisionInfo.ContactPlaneValid = false;
|
|
CollisionInfo.ContactPlaneIsWater = false;
|
|
|
|
if (validCell) SpherePath.CellArrayValid = true;
|
|
|
|
return transitionState;
|
|
}
|
|
|
|
public bool FindPlacementPos()
|
|
{
|
|
// refactor me
|
|
SpherePath.SetCheckPos(SpherePath.CurPos, SpherePath.CurCell);
|
|
|
|
CollisionInfo.SlidingNormalValid = false;
|
|
CollisionInfo.ContactPlaneValid = false;
|
|
CollisionInfo.ContactPlaneIsWater = false;
|
|
|
|
var transitionState = TransitionalInsert(3);
|
|
|
|
var redo = 0;
|
|
transitionState = ValidatePlacementTransition(transitionState, ref redo);
|
|
|
|
if (transitionState == TransitionState.OK)
|
|
return true;
|
|
|
|
if (!SpherePath.PlacementAllowsSliding)
|
|
return false;
|
|
|
|
var adjustDist = 4.0f;
|
|
var adjustRad = adjustDist;
|
|
var sphereRad = SpherePath.LocalSphere[0].Radius;
|
|
|
|
var fakeSphere = false;
|
|
|
|
if (sphereRad < 0.125f)
|
|
{
|
|
fakeSphere = true;
|
|
adjustRad = 2.0f;
|
|
}
|
|
else if (sphereRad < 0.48f)
|
|
{
|
|
sphereRad = 0.48f;
|
|
}
|
|
|
|
var movementDelta = sphereRad;
|
|
|
|
var fNumSteps = 4.0f / movementDelta;
|
|
|
|
if (fakeSphere)
|
|
fNumSteps *= 0.5f;
|
|
|
|
if (fNumSteps <= 1.0f)
|
|
return false;
|
|
|
|
var numSteps = (int)Math.Ceiling(fNumSteps);
|
|
var distPerStep = adjustRad / numSteps;
|
|
var radiansPerStep = (float)(Math.PI * distPerStep / sphereRad);
|
|
|
|
var totalDist = 0.0f;
|
|
var totalRad = 0.0f;
|
|
|
|
for (var i = 0; i < numSteps; i++)
|
|
{
|
|
totalDist += distPerStep;
|
|
totalRad += radiansPerStep;
|
|
|
|
var rad = (int)Math.Ceiling(totalRad);
|
|
rad *= 2;
|
|
var angle = 360.0f / rad;
|
|
|
|
var frame = new AFrame();
|
|
|
|
for (var j = 0; j < rad; j++)
|
|
{
|
|
SpherePath.SetCheckPos(SpherePath.CurPos, SpherePath.CurCell);
|
|
frame.set_heading(angle * j);
|
|
var offset = frame.get_vector_heading() * totalDist;
|
|
SpherePath.GlobalOffset = AdjustOffset(offset);
|
|
if (SpherePath.GlobalOffset.Length() >= PhysicsGlobals.EPSILON)
|
|
{
|
|
SpherePath.AddOffsetToCheckPos(SpherePath.GlobalOffset);
|
|
|
|
// possibly reset by AdjustOffset
|
|
CollisionInfo.SlidingNormalValid = false;
|
|
CollisionInfo.ContactPlaneValid = false;
|
|
CollisionInfo.ContactPlaneIsWater = false;
|
|
|
|
transitionState = TransitionalInsert(3);
|
|
transitionState = ValidatePlacementTransition(transitionState, ref redo);
|
|
|
|
if (transitionState == TransitionState.OK)
|
|
return true;
|
|
}
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
public bool FindPlacementPosition()
|
|
{
|
|
SpherePath.SetCheckPos(SpherePath.CurPos, SpherePath.CurCell);
|
|
SpherePath.InsertType = InsertType.InitialPlacement;
|
|
var transitionState = new TransitionState();
|
|
|
|
if (SpherePath.CheckCell != null)
|
|
{
|
|
transitionState = InsertIntoCell(SpherePath.CheckCell, 3);
|
|
|
|
if (transitionState == TransitionState.OK)
|
|
transitionState = CheckOtherCells(SpherePath.CheckCell);
|
|
}
|
|
else
|
|
transitionState = TransitionState.Collided;
|
|
|
|
var result = ValidatePlacement(transitionState, true);
|
|
if (result != TransitionState.OK)
|
|
return false;
|
|
|
|
SpherePath.InsertType = InsertType.Placement;
|
|
if (!FindPlacementPos()) return false;
|
|
|
|
if (!ObjectInfo.StepDown)
|
|
{
|
|
result = ValidatePlacement(TransitionState.OK, true);
|
|
return result == TransitionState.OK;
|
|
}
|
|
|
|
SpherePath.WalkableAllowance = PhysicsGlobals.LandingZ;
|
|
SpherePath.SaveCheckPos();
|
|
|
|
SpherePath.Backup = SpherePath.InsertType;
|
|
SpherePath.InsertType = InsertType.Transition;
|
|
|
|
var globSphere = SpherePath.GlobalSphere[0];
|
|
var stepDownHeight = ObjectInfo.StepDownHeight;
|
|
|
|
if (SpherePath.NumSphere < 2 && globSphere.Radius * 2 < stepDownHeight)
|
|
stepDownHeight = globSphere.Radius * 0.5f;
|
|
|
|
if (stepDownHeight <= globSphere.Radius * 2)
|
|
{
|
|
if (!StepDown(stepDownHeight, PhysicsGlobals.LandingZ))
|
|
{
|
|
SpherePath.RestoreCheckPos();
|
|
CollisionInfo.ContactPlaneValid = false;
|
|
CollisionInfo.ContactPlaneIsWater = false;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
stepDownHeight *= 0.5f;
|
|
if (!StepDown(stepDownHeight, PhysicsGlobals.LandingZ) && !StepDown(stepDownHeight, PhysicsGlobals.LandingZ))
|
|
{
|
|
SpherePath.RestoreCheckPos();
|
|
CollisionInfo.ContactPlaneValid = false;
|
|
CollisionInfo.ContactPlaneIsWater = false;
|
|
}
|
|
}
|
|
|
|
SpherePath.InsertType = SpherePath.Backup;
|
|
SpherePath.Walkable = null;
|
|
|
|
result = ValidatePlacement(TransitionState.OK, true);
|
|
return result == TransitionState.OK;
|
|
}
|
|
|
|
public bool FindTransitionalPosition()
|
|
{
|
|
if (SpherePath.BeginCell == null) return false;
|
|
|
|
var transitionState = TransitionState.OK;
|
|
var offset = Vector3.Zero;
|
|
var offsetPerStep = Vector3.Zero;
|
|
var numSteps = 0;
|
|
|
|
CalcNumSteps(ref offset, ref offsetPerStep, ref numSteps); // restructure as retval?
|
|
|
|
//var maxSteps = 30;
|
|
var maxSteps = 1000;
|
|
if (numSteps > maxSteps && !ObjectInfo.Object.IsSightObj)
|
|
{
|
|
//Console.WriteLine("NumSteps: " + numSteps);
|
|
return false;
|
|
}
|
|
|
|
if ((ObjectInfo.State & ObjectInfoState.FreeRotate) != 0)
|
|
SpherePath.CurPos.Frame.set_rotate(SpherePath.EndPos.Frame.Orientation);
|
|
|
|
SpherePath.SetCheckPos(SpherePath.CurPos, SpherePath.CurCell);
|
|
|
|
var redo = 0;
|
|
if (numSteps <= 0)
|
|
{
|
|
if ((ObjectInfo.State & ObjectInfoState.FreeRotate) == 0) // ?
|
|
SpherePath.CurPos.Frame.set_rotate(SpherePath.EndPos.Frame.Orientation);
|
|
|
|
SpherePath.CellArrayValid = true;
|
|
SpherePath.HitsInteriorCell = false;
|
|
|
|
ObjCell empty = null;
|
|
ObjCell.find_cell_list(CellArray, ref empty, SpherePath);
|
|
return true;
|
|
}
|
|
|
|
for (var step = 0; step < numSteps; step++)
|
|
{
|
|
if ((ObjectInfo.State & ObjectInfoState.IsViewer) != 0)
|
|
{
|
|
var lastStep = numSteps - 1;
|
|
|
|
if (step == lastStep)
|
|
{
|
|
var offsetLen = offset.Length();
|
|
if (offsetLen > PhysicsGlobals.EPSILON)
|
|
{
|
|
redo = lastStep;
|
|
offsetPerStep = offset * (offsetLen - SpherePath.LocalSphere[0].Radius * lastStep) / offsetLen;
|
|
}
|
|
}
|
|
}
|
|
SpherePath.GlobalOffset = AdjustOffset(offsetPerStep);
|
|
if ((ObjectInfo.State & ObjectInfoState.IsViewer) == 0)
|
|
{
|
|
if (SpherePath.GlobalOffset.LengthSquared() < PhysicsGlobals.EPSILON * PhysicsGlobals.EPSILON)
|
|
{
|
|
return (step != 0 && transitionState == TransitionState.OK);
|
|
}
|
|
}
|
|
if ((ObjectInfo.State & ObjectInfoState.FreeRotate) == 0)
|
|
{
|
|
redo = step + 1;
|
|
var delta = (float)redo / numSteps;
|
|
SpherePath.CheckPos.Frame.InterpolateRotation(SpherePath.BeginPos.Frame, SpherePath.EndPos.Frame, delta);
|
|
}
|
|
|
|
CollisionInfo.SlidingNormalValid = false;
|
|
CollisionInfo.ContactPlaneValid = false;
|
|
CollisionInfo.ContactPlaneIsWater = false;
|
|
|
|
if (SpherePath.InsertType != InsertType.Transition)
|
|
{
|
|
var insert = TransitionalInsert(3);
|
|
transitionState = ValidatePlacementTransition(insert, ref redo);
|
|
|
|
if (transitionState == TransitionState.OK)
|
|
return true;
|
|
|
|
if (!SpherePath.PlacementAllowsSliding)
|
|
return false;
|
|
|
|
SpherePath.AddOffsetToCheckPos(SpherePath.GlobalOffset);
|
|
}
|
|
else
|
|
{
|
|
SpherePath.AddOffsetToCheckPos(SpherePath.GlobalOffset);
|
|
|
|
var transitionInsert = TransitionalInsert(3);
|
|
transitionState = ValidateTransition(transitionInsert, ref redo);
|
|
|
|
if (CollisionInfo.FramesStationaryFall > 0) break;
|
|
}
|
|
|
|
if (CollisionInfo.CollisionNormalValid && (ObjectInfo.State & ObjectInfoState.PathClipped) != 0) break;
|
|
}
|
|
|
|
return transitionState == TransitionState.OK;
|
|
}
|
|
|
|
public bool FindValidPosition()
|
|
{
|
|
if (SpherePath.InsertType == InsertType.Transition)
|
|
return FindTransitionalPosition();
|
|
else
|
|
return FindPlacementPosition();
|
|
}
|
|
|
|
public void Init()
|
|
{
|
|
ObjectInfo = new ObjectInfo();
|
|
SpherePath = new SpherePath();
|
|
CollisionInfo = new CollisionInfo();
|
|
CellArray = new CellArray();
|
|
//NewCellPtr = new ObjCell();
|
|
}
|
|
|
|
public void InitContactPlane(uint cellID, Plane contactPlane, bool isWater)
|
|
{
|
|
InitLastKnownContactPlane(cellID, contactPlane, isWater);
|
|
|
|
CollisionInfo.ContactPlaneValid = true;
|
|
CollisionInfo.ContactPlane = new Plane(contactPlane.Normal, contactPlane.D);
|
|
CollisionInfo.ContactPlaneIsWater = isWater;
|
|
CollisionInfo.ContactPlaneCellID = cellID;
|
|
}
|
|
|
|
public void InitLastKnownContactPlane(uint cellID, Plane contactPlane, bool isWater)
|
|
{
|
|
CollisionInfo.LastKnownContactPlaneValid = true;
|
|
CollisionInfo.LastKnownContactPlane = new Plane(contactPlane.Normal, contactPlane.D);
|
|
CollisionInfo.LastKnownContactPlaneIsWater = isWater;
|
|
CollisionInfo.LastKnownContactPlaneCellID = cellID;
|
|
}
|
|
|
|
public void InitObject(PhysicsObj obj, ObjectInfoState objectState)
|
|
{
|
|
ObjectInfo.Init(obj, objectState);
|
|
}
|
|
|
|
public void InitPath(ObjCell beginCell, Position beginPos, Position endPos)
|
|
{
|
|
SpherePath.InitPath(beginCell, beginPos, endPos);
|
|
}
|
|
|
|
public void InitSlidingNormal(Vector3 normal)
|
|
{
|
|
CollisionInfo.SlidingNormalValid = true;
|
|
CollisionInfo.SlidingNormal = new Vector3(normal.X, normal.Y, normal.Z);
|
|
|
|
if (Vec.NormalizeCheckSmall(ref CollisionInfo.SlidingNormal))
|
|
CollisionInfo.SlidingNormal = Vector3.Zero;
|
|
}
|
|
|
|
public void InitSphere(int numSphere, List<Sphere> sphere, float scale)
|
|
{
|
|
SpherePath.InitSphere(numSphere, sphere, scale);
|
|
}
|
|
|
|
public void InitSphere(int numSphere, Sphere sphere, float scale)
|
|
{
|
|
SpherePath.InitSphere(numSphere, new List<Sphere> { sphere }, scale);
|
|
}
|
|
|
|
public TransitionState InsertIntoCell(ObjCell cell, int num_insertion_attempts)
|
|
{
|
|
if (cell == null)
|
|
return TransitionState.Collided;
|
|
|
|
var transitionState = TransitionState.OK;
|
|
|
|
for (var i = 0; i < num_insertion_attempts; i++)
|
|
{
|
|
transitionState = cell.FindCollisions(this);
|
|
|
|
switch (transitionState)
|
|
{
|
|
case TransitionState.OK:
|
|
case TransitionState.Collided:
|
|
return transitionState;
|
|
|
|
case TransitionState.Slid:
|
|
CollisionInfo.ContactPlaneValid = false;
|
|
CollisionInfo.ContactPlaneIsWater = false;
|
|
break;
|
|
}
|
|
}
|
|
return transitionState;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Initializes a new default transition
|
|
/// </summary>
|
|
/// <returns></returns>
|
|
public static Transition MakeTransition()
|
|
{
|
|
return new Transition();
|
|
}
|
|
|
|
public TransitionState PlacementInsert()
|
|
{
|
|
if (SpherePath.CheckCell == null) return TransitionState.Collided;
|
|
|
|
var checkCell = SpherePath.CheckCell;
|
|
var transitState = InsertIntoCell(checkCell, 3);
|
|
|
|
if (transitState == TransitionState.OK)
|
|
transitState = CheckOtherCells(checkCell);
|
|
|
|
return transitState;
|
|
}
|
|
|
|
public bool StepDown(float stepDownHeight, float zVal)
|
|
{
|
|
SpherePath.NegPolyHit = false;
|
|
SpherePath.StepDown = true;
|
|
|
|
SpherePath.StepDownAmt = stepDownHeight;
|
|
SpherePath.WalkInterp = 1.0f;
|
|
|
|
if (!SpherePath.StepUp)
|
|
{
|
|
SpherePath.CellArrayValid = false;
|
|
|
|
var offset = new Vector3(0, 0, -stepDownHeight);
|
|
|
|
SpherePath.CheckPos.Frame.Origin.Z -= stepDownHeight;
|
|
SpherePath.CacheGlobalSphere(offset);
|
|
}
|
|
|
|
var transitionState = TransitionalInsert(5);
|
|
SpherePath.StepDown = false;
|
|
|
|
if (transitionState == TransitionState.OK && CollisionInfo.ContactPlaneValid && CollisionInfo.ContactPlane.Normal.Z >= zVal &&
|
|
((ObjectInfo.State & ObjectInfoState.EdgeSlide) == 0 || SpherePath.StepUp || CheckWalkable(zVal)))
|
|
{
|
|
SpherePath.Backup = SpherePath.InsertType;
|
|
SpherePath.InsertType = InsertType.Placement;
|
|
|
|
transitionState = TransitionalInsert(1);
|
|
|
|
SpherePath.InsertType = SpherePath.Backup;
|
|
return (transitionState == TransitionState.OK);
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
public bool StepUp(Vector3 collisionNormal)
|
|
{
|
|
CollisionInfo.ContactPlaneValid = false;
|
|
CollisionInfo.ContactPlaneIsWater = false;
|
|
|
|
SpherePath.StepUp = true;
|
|
SpherePath.StepUpNormal = new Vector3(collisionNormal.X, collisionNormal.Y, collisionNormal.Z);
|
|
|
|
var stepDownHeight = 0.039999999f; // set global?
|
|
|
|
var zLandingValue = PhysicsGlobals.LandingZ;
|
|
|
|
if (ObjectInfo.State.HasFlag(ObjectInfoState.OnWalkable))
|
|
{
|
|
zLandingValue = ObjectInfo.GetWalkableZ();
|
|
stepDownHeight = ObjectInfo.StepUpHeight;
|
|
}
|
|
|
|
SpherePath.WalkableAllowance = zLandingValue;
|
|
SpherePath.BackupCell = SpherePath.CheckCell;
|
|
SpherePath.BackupCheckPos = new Position(SpherePath.CheckPos);
|
|
|
|
var stepDown = StepDown(stepDownHeight, zLandingValue);
|
|
|
|
SpherePath.StepUp = false;
|
|
SpherePath.Walkable = null;
|
|
|
|
if (!stepDown)
|
|
SpherePath.RestoreCheckPos();
|
|
|
|
return stepDown;
|
|
}
|
|
|
|
public TransitionState TransitionalInsert(int num_insertion_attempts)
|
|
{
|
|
if (SpherePath.CheckCell == null)
|
|
return TransitionState.OK;
|
|
|
|
if (num_insertion_attempts <= 0)
|
|
return TransitionState.Invalid;
|
|
|
|
var transitState = TransitionState.Invalid;
|
|
|
|
for (var i = 0; i < num_insertion_attempts; i++)
|
|
{
|
|
transitState = InsertIntoCell(SpherePath.CheckCell, num_insertion_attempts);
|
|
|
|
switch (transitState)
|
|
{
|
|
case TransitionState.OK:
|
|
|
|
transitState = CheckOtherCells(SpherePath.CheckCell);
|
|
|
|
if (transitState != TransitionState.OK)
|
|
SpherePath.NegPolyHit = false;
|
|
|
|
if (transitState == TransitionState.Collided)
|
|
return transitState;
|
|
|
|
break;
|
|
|
|
case TransitionState.Collided:
|
|
|
|
return transitState;
|
|
|
|
case TransitionState.Adjusted:
|
|
|
|
SpherePath.NegPolyHit = false;
|
|
break;
|
|
|
|
case TransitionState.Slid:
|
|
|
|
CollisionInfo.ContactPlaneValid = false;
|
|
CollisionInfo.ContactPlaneIsWater = false;
|
|
SpherePath.NegPolyHit = false;
|
|
break;
|
|
}
|
|
|
|
if (transitState == TransitionState.OK)
|
|
{
|
|
if (!SpherePath.Collide)
|
|
{
|
|
if (SpherePath.NegPolyHit && !SpherePath.StepDown && !SpherePath.StepUp)
|
|
{
|
|
SpherePath.NegPolyHit = false;
|
|
|
|
if (SpherePath.NegStepUp)
|
|
{
|
|
if (!StepUp(SpherePath.NegCollisionNormal))
|
|
transitState = SpherePath.StepUpSlide(this);
|
|
}
|
|
else
|
|
transitState = SpherePath.GlobalSphere[0].SlideSphere(this,
|
|
ref SpherePath.NegCollisionNormal, SpherePath.GlobalCurrCenter[0].Center);
|
|
}
|
|
else
|
|
{
|
|
if (CollisionInfo.ContactPlaneValid || (ObjectInfo.State & ObjectInfoState.Contact) == 0 ||
|
|
SpherePath.StepDown || SpherePath.CheckCell == null || !ObjectInfo.StepDown)
|
|
{
|
|
return TransitionState.OK;
|
|
}
|
|
|
|
var zVal = PhysicsGlobals.LandingZ;
|
|
var stepDownHeight = 0.039999999f; // set global
|
|
|
|
if ((ObjectInfo.State & ObjectInfoState.OnWalkable) != 0)
|
|
{
|
|
zVal = ObjectInfo.GetWalkableZ();
|
|
stepDownHeight = ObjectInfo.StepDownHeight;
|
|
}
|
|
SpherePath.WalkableAllowance = zVal;
|
|
SpherePath.SaveCheckPos();
|
|
|
|
var radsum = SpherePath.GlobalSphere[0].Radius * 2;
|
|
if (SpherePath.NumSphere < 2)
|
|
{
|
|
if (radsum < stepDownHeight)
|
|
stepDownHeight = SpherePath.GlobalSphere[0].Radius * 0.5f;
|
|
}
|
|
|
|
if (radsum >= stepDownHeight)
|
|
{
|
|
if (StepDown(stepDownHeight, zVal))
|
|
{
|
|
SpherePath.Walkable = null;
|
|
return TransitionState.OK;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// 2 half-steps
|
|
stepDownHeight *= 0.5f;
|
|
if (StepDown(stepDownHeight, zVal) || StepDown(stepDownHeight, zVal))
|
|
{
|
|
SpherePath.Walkable = null;
|
|
return TransitionState.OK;
|
|
}
|
|
}
|
|
|
|
if (EdgeSlide(ref transitState, stepDownHeight, zVal))
|
|
return transitState;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
var reset = false;
|
|
SpherePath.Collide = false;
|
|
if (CollisionInfo.ContactPlaneValid && CheckWalkable(PhysicsGlobals.LandingZ))
|
|
{
|
|
SpherePath.Backup = SpherePath.InsertType;
|
|
SpherePath.InsertType = InsertType.Placement;
|
|
|
|
transitState = TransitionalInsert(num_insertion_attempts);
|
|
|
|
SpherePath.InsertType = SpherePath.Backup;
|
|
|
|
if (transitState != TransitionState.OK)
|
|
{
|
|
transitState = TransitionState.OK;
|
|
reset = true;
|
|
}
|
|
}
|
|
else
|
|
reset = true;
|
|
|
|
SpherePath.Walkable = null;
|
|
|
|
if (!reset) return transitState;
|
|
|
|
SpherePath.RestoreCheckPos();
|
|
|
|
CollisionInfo.ContactPlaneValid = false;
|
|
CollisionInfo.ContactPlaneIsWater = false;
|
|
|
|
if (CollisionInfo.LastKnownContactPlaneValid)
|
|
{
|
|
CollisionInfo.LastKnownContactPlaneValid = false;
|
|
ObjectInfo.StopVelocity();
|
|
}
|
|
else
|
|
CollisionInfo.SetCollisionNormal(SpherePath.StepUpNormal);
|
|
|
|
return TransitionState.Collided;
|
|
}
|
|
}
|
|
}
|
|
return transitState;
|
|
}
|
|
|
|
public TransitionState ValidatePlacement(TransitionState transitionState, bool adjust)
|
|
{
|
|
if (SpherePath.CheckCell == null) return TransitionState.Collided;
|
|
|
|
switch (transitionState)
|
|
{
|
|
case TransitionState.OK:
|
|
SpherePath.CurPos = SpherePath.CheckPos;
|
|
SpherePath.CurCell = SpherePath.CheckCell;
|
|
SpherePath.CacheGlobalCurrCenter();
|
|
break;
|
|
|
|
case TransitionState.Adjusted:
|
|
case TransitionState.Slid:
|
|
if (adjust)
|
|
return ValidatePlacement(PlacementInsert(), false);
|
|
break;
|
|
}
|
|
return transitionState;
|
|
}
|
|
|
|
public TransitionState ValidatePlacementTransition(TransitionState transitionState, ref int redo)
|
|
{
|
|
redo = 0;
|
|
|
|
if (SpherePath.CheckCell == null) return TransitionState.Collided;
|
|
|
|
switch (transitionState)
|
|
{
|
|
case TransitionState.OK:
|
|
SpherePath.CurPos = SpherePath.CheckPos;
|
|
SpherePath.CurCell = SpherePath.CheckCell;
|
|
SpherePath.CacheGlobalCurrCenter();
|
|
break;
|
|
|
|
case TransitionState.Collided:
|
|
case TransitionState.Adjusted:
|
|
case TransitionState.Slid:
|
|
|
|
// added target id
|
|
if (SpherePath.PlacementAllowsSliding && ObjectInfo.TargetID == 0)
|
|
CollisionInfo.Init();
|
|
|
|
break;
|
|
}
|
|
return transitionState;
|
|
}
|
|
|
|
public TransitionState ValidateTransition(TransitionState transitionState, ref int redo)
|
|
{
|
|
redo = 0;
|
|
var _redo = 1;
|
|
Plane contactPlane = new Plane();
|
|
|
|
if (transitionState != TransitionState.OK || SpherePath.CheckPos.Equals(SpherePath.CurPos))
|
|
{
|
|
_redo = 0;
|
|
if (transitionState != TransitionState.OK)
|
|
{
|
|
if (transitionState != TransitionState.Invalid)
|
|
{
|
|
if (CollisionInfo.LastKnownContactPlaneValid)
|
|
{
|
|
ObjectInfo.StopVelocity();
|
|
var angle = Vector3.Dot(CollisionInfo.LastKnownContactPlane.Normal, SpherePath.GlobalCurrCenter[0].Center) + CollisionInfo.LastKnownContactPlane.D;
|
|
if (SpherePath.GlobalSphere[0].Radius + PhysicsGlobals.EPSILON > Math.Abs(angle))
|
|
{
|
|
CollisionInfo.SetContactPlane(CollisionInfo.LastKnownContactPlane, CollisionInfo.LastKnownContactPlaneIsWater);
|
|
CollisionInfo.ContactPlaneCellID = CollisionInfo.LastKnownContactPlaneCellID;
|
|
|
|
if (ObjectInfo.State.HasFlag(ObjectInfoState.OnWalkable)) _redo = 1;
|
|
}
|
|
}
|
|
if (!CollisionInfo.CollisionNormalValid)
|
|
{
|
|
contactPlane.Normal = Vector3.UnitZ;
|
|
CollisionInfo.SetCollisionNormal(contactPlane.Normal);
|
|
}
|
|
SpherePath.SetCheckPos(SpherePath.CurPos, SpherePath.CurCell);
|
|
ObjCell empty = null;
|
|
BuildCellArray(ref empty);
|
|
transitionState = TransitionState.OK;
|
|
}
|
|
}
|
|
else
|
|
SetCurrentCheckPos();
|
|
}
|
|
else
|
|
SetCurrentCheckPos();
|
|
|
|
if (CollisionInfo.CollisionNormalValid)
|
|
CollisionInfo.SetSlidingNormal(CollisionInfo.CollisionNormal);
|
|
|
|
if (!ObjectInfo.State.HasFlag(ObjectInfoState.IsViewer))
|
|
{
|
|
if (ObjectInfo.Object.State.HasFlag(PhysicsState.Gravity))
|
|
{
|
|
if (_redo == 0)
|
|
{
|
|
if (CollisionInfo.FramesStationaryFall > 0)
|
|
{
|
|
if (CollisionInfo.FramesStationaryFall > 1)
|
|
{
|
|
CollisionInfo.FramesStationaryFall = 3;
|
|
contactPlane.Normal = Vector3.UnitZ;
|
|
contactPlane.D = SpherePath.GlobalSphere[0].Radius - SpherePath.GlobalSphere[0].Center.Z;
|
|
|
|
CollisionInfo.SetContactPlane(contactPlane, false);
|
|
CollisionInfo.ContactPlaneCellID = SpherePath.CheckPos.ObjCellID;
|
|
|
|
if (!ObjectInfo.State.HasFlag(ObjectInfoState.Contact))
|
|
{
|
|
CollisionInfo.SetCollisionNormal(contactPlane.Normal);
|
|
CollisionInfo.CollidedWithEnvironment = true;
|
|
}
|
|
}
|
|
else
|
|
CollisionInfo.FramesStationaryFall = 2;
|
|
}
|
|
else
|
|
CollisionInfo.FramesStationaryFall = 1;
|
|
}
|
|
else
|
|
CollisionInfo.FramesStationaryFall = 0;
|
|
}
|
|
}
|
|
|
|
CollisionInfo.LastKnownContactPlaneValid = CollisionInfo.ContactPlaneValid;
|
|
|
|
if (CollisionInfo.ContactPlaneValid)
|
|
{
|
|
CollisionInfo.LastKnownContactPlane = CollisionInfo.ContactPlane;
|
|
CollisionInfo.LastKnownContactPlaneCellID = CollisionInfo.ContactPlaneCellID;
|
|
CollisionInfo.LastKnownContactPlaneIsWater = CollisionInfo.ContactPlaneIsWater;
|
|
|
|
ObjectInfo.State |= ObjectInfoState.Contact;
|
|
|
|
if (ObjectInfo.IsValidWalkable(CollisionInfo.ContactPlane.Normal))
|
|
ObjectInfo.State |= ObjectInfoState.OnWalkable;
|
|
else
|
|
ObjectInfo.State &= ~ObjectInfoState.OnWalkable;
|
|
}
|
|
else
|
|
ObjectInfo.State &= ~(ObjectInfoState.Contact | ObjectInfoState.OnWalkable);
|
|
|
|
return transitionState;
|
|
}
|
|
|
|
public void SetCurrentCheckPos()
|
|
{
|
|
SpherePath.CurPos = new Position(SpherePath.CheckPos);
|
|
SpherePath.CurCell = SpherePath.CheckCell;
|
|
SpherePath.CacheGlobalCurrCenter();
|
|
|
|
SpherePath.SetCheckPos(SpherePath.CurPos, SpherePath.CurCell);
|
|
}
|
|
}
|
|
}
|