diff --git a/ACE b/ACE
index 05462b4..c50b222 160000
--- a/ACE
+++ b/ACE
@@ -1 +1 @@
-Subproject commit 05462b4c8b5e4c8cf48d5d711b37902101130ba2
+Subproject commit c50b22209369d71dd00b25f70c038b3df5b43cbc
diff --git a/ACViewer/ACViewer.csproj b/ACViewer/ACViewer.csproj
index 7f34520..ea743d1 100644
--- a/ACViewer/ACViewer.csproj
+++ b/ACViewer/ACViewer.csproj
@@ -162,6 +162,7 @@
+
@@ -245,6 +246,12 @@
+
+
+
+
+
+
@@ -263,6 +270,7 @@
+
diff --git a/ACViewer/Extensions/MathExtensions.cs b/ACViewer/Extensions/MathExtensions.cs
new file mode 100644
index 0000000..c8632e9
--- /dev/null
+++ b/ACViewer/Extensions/MathExtensions.cs
@@ -0,0 +1,15 @@
+namespace ACViewer.Extensions
+{
+ public static class MathExtensions
+ {
+ public static float Clamp(float val, float min, float max)
+ {
+ if (val < min)
+ val = min;
+ else if (val > max)
+ val = max;
+
+ return val;
+ }
+ }
+}
diff --git a/ACViewer/Physics/Animation/AFrame.cs b/ACViewer/Physics/Animation/AFrame.cs
index ba01ce3..7168970 100644
--- a/ACViewer/Physics/Animation/AFrame.cs
+++ b/ACViewer/Physics/Animation/AFrame.cs
@@ -24,19 +24,19 @@ public AFrame(Vector3 origin, Quaternion orientation)
public AFrame(AFrame frame)
{
- Origin = new Vector3(frame.Origin.X, frame.Origin.Y, frame.Origin.Z);
+ Origin = frame.Origin;
Orientation = new Quaternion(frame.Orientation.X, frame.Orientation.Y, frame.Orientation.Z, frame.Orientation.W);
}
public AFrame(DatLoader.Entity.Frame frame)
{
- Origin = new Vector3(frame.Origin.X, frame.Origin.Y, frame.Origin.Z);
+ Origin = frame.Origin;
Orientation = new Quaternion(frame.Orientation.X, frame.Orientation.Y, frame.Orientation.Z, frame.Orientation.W);
}
public AFrame(ACE.Entity.Frame frame)
{
- Origin = new Vector3(frame.Origin.X, frame.Origin.Y, frame.Origin.Z);
+ Origin = frame.Origin;
Orientation = new Quaternion(frame.Orientation.X, frame.Orientation.Y, frame.Orientation.Z, frame.Orientation.W);
}
@@ -203,7 +203,7 @@ public void set_rotate(Quaternion orientation)
public void set_vector_heading(Vector3 heading)
{
- var normal = new Vector3(heading.X, heading.Y, heading.Z);
+ var normal = heading;
if (Vec.NormalizeCheckSmall(ref normal)) return;
var zDeg = 450.0f - ((float)Math.Atan2(normal.Y, normal.X)).ToDegrees();
@@ -217,7 +217,7 @@ public void set_vector_heading(Vector3 heading)
public override string ToString()
{
- return Origin + " " + Orientation;
+ return $"[{Origin.X} {Origin.Y} {Origin.Z}] {Orientation.W} {Orientation.X} {Orientation.Y} {Orientation.Z}";
}
public bool Equals(AFrame frame)
diff --git a/ACViewer/Physics/Animation/AnimData.cs b/ACViewer/Physics/Animation/AnimData.cs
index 2a747c0..9cb94cc 100644
--- a/ACViewer/Physics/Animation/AnimData.cs
+++ b/ACViewer/Physics/Animation/AnimData.cs
@@ -1,4 +1,4 @@
-namespace ACE.Server.Physics.Animation
+namespace ACE.Server.Physics.Animation
{
public class AnimData
{
diff --git a/ACViewer/Physics/Animation/AnimSequenceNode.cs b/ACViewer/Physics/Animation/AnimSequenceNode.cs
index 1bf9fb2..d147635 100644
--- a/ACViewer/Physics/Animation/AnimSequenceNode.cs
+++ b/ACViewer/Physics/Animation/AnimSequenceNode.cs
@@ -30,7 +30,7 @@ public AnimSequenceNode(AnimData animData)
public float get_ending_frame()
{
- if (Framerate > 0.0f)
+ if (Framerate >= 0.0f)
return HighFrame + 1 - PhysicsGlobals.EPSILON;
else
return LowFrame;
diff --git a/ACViewer/Physics/Animation/InterpretedMotionState.cs b/ACViewer/Physics/Animation/InterpretedMotionState.cs
index 3fa7425..431d626 100644
--- a/ACViewer/Physics/Animation/InterpretedMotionState.cs
+++ b/ACViewer/Physics/Animation/InterpretedMotionState.cs
@@ -124,5 +124,11 @@ public void RemoveMotion(uint motion)
break;
}
}
+
+ public bool HasCommands()
+ {
+ //return ForwardCommand != 0 && ForwardCommand != (uint)MotionCommand.Ready || SideStepCommand != 0 || TurnCommand != 0;
+ return SideStepCommand != 0 || TurnCommand != 0;
+ }
}
}
diff --git a/ACViewer/Physics/Animation/MotionInterp.cs b/ACViewer/Physics/Animation/MotionInterp.cs
index bc41e22..c6435c7 100644
--- a/ACViewer/Physics/Animation/MotionInterp.cs
+++ b/ACViewer/Physics/Animation/MotionInterp.cs
@@ -3,7 +3,6 @@
using System.Numerics;
using ACE.Entity.Enum;
using ACE.Server.Physics.Common;
-using ACE.Server.Physics.Extensions;
namespace ACE.Server.Physics.Animation
{
@@ -170,6 +169,7 @@ public void HandleExitWorld()
}
}
PendingMotions.Clear();
+ if (PhysicsObj != null) PhysicsObj.IsAnimating = false;
}
public void HitGround()
@@ -226,7 +226,10 @@ public void MotionDone(bool success)
motionData = PendingMotions.First;
if (motionData != null)
+ {
PendingMotions.Remove(motionData);
+ PhysicsObj.IsAnimating = PendingMotions.Count > 0;
+ }
}
}
@@ -385,6 +388,7 @@ public WeenieError StopMotion(uint motion, MovementParameters movementParams)
public void add_to_queue(int contextID, uint motion, WeenieError jumpErrorCode)
{
PendingMotions.AddLast(new MotionNode(contextID, motion, jumpErrorCode));
+ PhysicsObj.IsAnimating = true;
}
public void adjust_motion(ref uint motion, ref float speed, HoldKey holdKey)
@@ -641,7 +645,7 @@ public float get_jump_v_z()
return 10.0f;
float vz = extent;
- if (WeenieObj.InqJumpVelocity(extent, ref vz))
+ if (WeenieObj.InqJumpVelocity(extent, out vz))
return vz;
return 0.0f;
@@ -652,8 +656,8 @@ public Vector3 get_leave_ground_velocity()
var velocity = get_state_velocity();
velocity.Z = get_jump_v_z();
- if (!velocity.Equals(Vector3.Zero))
- velocity = PhysicsObj.Position.GlobalToLocalVec(velocity);
+ if (Vec.IsZero(velocity))
+ velocity = PhysicsObj.Position.GlobalToLocalVec(PhysicsObj.Velocity);
return velocity;
}
@@ -689,7 +693,7 @@ public Vector3 get_state_velocity()
var maxSpeed = RunAnimSpeed * rate;
if (velocity.Length() > maxSpeed)
{
- velocity = velocity.Normalize();
+ velocity = Vector3.Normalize(velocity);
velocity *= maxSpeed;
}
return velocity;
@@ -774,6 +778,9 @@ public WeenieError motion_allows_jump(uint substate)
return WeenieError.None;
}
+ ///
+ /// Alternatively, you can use PhysicsObj.IsAnimating for better performance.
+ ///
public bool motions_pending()
{
return PendingMotions.Count > 0;
diff --git a/ACViewer/Physics/Animation/MotionTable.cs b/ACViewer/Physics/Animation/MotionTable.cs
index abd8172..ff68732 100644
--- a/ACViewer/Physics/Animation/MotionTable.cs
+++ b/ACViewer/Physics/Animation/MotionTable.cs
@@ -1,8 +1,11 @@
using System;
+using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Numerics;
+
using ACE.DatLoader;
using ACE.DatLoader.Entity;
+using ACE.DatLoader.Entity.AnimationHooks;
using ACE.Entity.Enum;
using ACE.Server.Physics.Animation.Internal;
@@ -17,15 +20,15 @@ public class MotionTable
public Dictionary> Links;
public uint DefaultStyle;
- public static Dictionary WalkSpeed;
- public static Dictionary RunSpeed;
- public static Dictionary TurnSpeed;
+ public static ConcurrentDictionary WalkSpeed { get; set; }
+ public static ConcurrentDictionary RunSpeed { get; set; }
+ public static ConcurrentDictionary TurnSpeed { get; set; }
static MotionTable()
{
- WalkSpeed = new Dictionary();
- RunSpeed = new Dictionary();
- TurnSpeed = new Dictionary();
+ WalkSpeed = new ConcurrentDictionary();
+ RunSpeed = new ConcurrentDictionary();
+ TurnSpeed = new ConcurrentDictionary();
}
public MotionTable()
@@ -454,14 +457,28 @@ public void re_modify(Sequence sequence, MotionState pstate)
}
}
+ private static readonly List<(float, AttackHook)> emptyList = new List<(float, AttackHook)>();
+
+ public static List<(float time, AttackHook attackHook)> GetAttackFrames(uint motionTableId, MotionStance stance, MotionCommand motion)
+ {
+ if (motionTableId == 0) return emptyList;
+
+ var motionTable = DatManager.PortalDat.ReadFromDat(motionTableId);
+ return motionTable.GetAttackFrames(motionTableId, stance, motion);
+ }
+
public static float GetAnimationLength(uint motionTableId, MotionStance stance, MotionCommand motion, float speed = 1.0f)
{
+ if (motionTableId == 0) return 0;
+
var motionTable = DatManager.PortalDat.ReadFromDat(motionTableId);
return motionTable.GetAnimationLength(stance, motion, null) / speed;
}
public static float GetAnimationLength(uint motionTableId, MotionStance stance, MotionCommand currentMotion, MotionCommand motion, float speed = 1.0f)
{
+ if (motionTableId == 0) return 0;
+
var motionTable = DatManager.PortalDat.ReadFromDat(motionTableId);
var animLength = 0.0f;
@@ -477,6 +494,8 @@ public static float GetAnimationLength(uint motionTableId, MotionStance stance,
public static float GetCycleLength(uint motionTableId, MotionStance stance, MotionCommand motion, float speed = 1.0f)
{
+ if (motionTableId == 0) return 0;
+
var motionTable = DatManager.PortalDat.ReadFromDat(motionTableId);
return motionTable.GetCycleLength(stance, motion) / speed;
}
@@ -495,7 +514,7 @@ public static float GetRunSpeed(uint motionTableID)
return 0.0f;
var speed = GetAnimDist(motionData);
- RunSpeed.Add(motionTableID, speed);
+ RunSpeed[motionTableID] = speed;
return speed;
}
@@ -513,7 +532,7 @@ public static float GetTurnSpeed(uint motionTableID)
return 0.0f;
var speed = Math.Abs(motionData.Omega.Z);
- TurnSpeed.Add(motionTableID, speed);
+ TurnSpeed[motionTableID] = speed;
return speed;
}
@@ -522,6 +541,8 @@ public static float GetTurnSpeed(uint motionTableID)
///
public static MotionData GetMotionData(uint motionTableID, uint motion, uint? currentStyle = null)
{
+ if (motionTableID == 0) return null;
+
var motionTable = DatManager.PortalDat.ReadFromDat(motionTableID);
if (currentStyle == null)
currentStyle = motionTable.DefaultStyle;
@@ -533,6 +554,8 @@ public static MotionData GetMotionData(uint motionTableID, uint motion, uint? cu
public static MotionData GetLinkData(uint motionTableID, uint motion, uint? currentStyle = null)
{
+ if (motionTableID == 0) return null;
+
var motionTable = DatManager.PortalDat.ReadFromDat(motionTableID);
if (currentStyle == null)
currentStyle = motionTable.DefaultStyle;
diff --git a/ACViewer/Physics/Animation/MovementSystem.cs b/ACViewer/Physics/Animation/MovementSystem.cs
index 8653c2e..e9d1868 100644
--- a/ACViewer/Physics/Animation/MovementSystem.cs
+++ b/ACViewer/Physics/Animation/MovementSystem.cs
@@ -1,16 +1,16 @@
using System;
using ACE.Server.Physics.Common;
+using ACViewer.Extensions;
namespace ACE.Server.Physics.Animation
{
public class MovementSystem
{
- public static float GetJumpHeight(float burden, int jumpSkill, float power, float scaling)
+ public static float GetJumpHeight(float burden, uint jumpSkill, float power, float scaling)
{
- if (power < 0.0f) power = 0.0f;
- if (power > 1.0f) power = 1.0f;
+ power = MathExtensions.Clamp(power, 0.0f, 1.0f);
- var result = EncumbranceSystem.GetBurdenMod(burden) * (jumpSkill / (jumpSkill + 1300) * 22.200001f + 0.050000001f) * power / scaling;
+ var result = EncumbranceSystem.GetBurdenMod(burden) * (jumpSkill / (jumpSkill + 1300.0f) * 22.2f + 0.05f) * power / scaling;
if (result < 0.35f)
result = 0.35f;
@@ -35,5 +35,13 @@ public static int JumpStaminaCost(float power, float burden, bool pk)
else
return (int)Math.Ceiling((burden + 0.5f) * power * 8.0f + 2.0f);
}
+
+ public static float GetJumpPower(uint stamina, float burden, bool pk)
+ {
+ if (pk)
+ return stamina / 100.0f - 1.0f;
+ else
+ return (stamina - 2.0f) / (burden * 8.0f + 4.0f);
+ }
}
}
diff --git a/ACViewer/Physics/Animation/Sequence.cs b/ACViewer/Physics/Animation/Sequence.cs
index e31f014..4ec5c0a 100644
--- a/ACViewer/Physics/Animation/Sequence.cs
+++ b/ACViewer/Physics/Animation/Sequence.cs
@@ -22,6 +22,35 @@ public class Sequence
public int PlacementFrameID;
public bool IsTrivial;
+ public static HashSet PlayerIdleAnims;
+
+ static Sequence()
+ {
+ PlayerIdleAnims = new HashSet();
+ PlayerIdleAnims.Add(0x03000001); // NonCombat
+ PlayerIdleAnims.Add(0x0300049E); // AtlatlCombat
+ PlayerIdleAnims.Add(0x0300045A); // BowCombat
+ PlayerIdleAnims.Add(0x03000474); // CrossbowCombat
+ PlayerIdleAnims.Add(0x03000CA8); // DualWieldCombat
+ PlayerIdleAnims.Add(0x03000448); // HandCombat
+ PlayerIdleAnims.Add(0x0300076C); // Magic
+ PlayerIdleAnims.Add(0x0300043D); // SwordCombat
+ PlayerIdleAnims.Add(0x03000426); // SwordShieldCombat
+ PlayerIdleAnims.Add(0x030008DF); // ThrownShieldCombat
+ PlayerIdleAnims.Add(0x0300049E); // ThrownWeaponCombat
+ PlayerIdleAnims.Add(0x03000B05); // TwoHandedSwordCombat
+ }
+
+ public bool is_idle_anim()
+ {
+ return CurrAnim == null || PlayerIdleAnims.Contains(CurrAnim.Value.Anim.ID);
+ }
+
+ public bool is_first_cyclic()
+ {
+ return CurrAnim == null || CurrAnim.Equals(FirstCyclic);
+ }
+
public Sequence()
{
Init();
diff --git a/ACViewer/Physics/BSP/BSPNode.cs b/ACViewer/Physics/BSP/BSPNode.cs
index aef74cd..b6484e3 100644
--- a/ACViewer/Physics/BSP/BSPNode.cs
+++ b/ACViewer/Physics/BSP/BSPNode.cs
@@ -57,7 +57,7 @@ public BSPNode(DatLoader.Entity.BSPNode node, Dictionary();
+ Polygons = new List(node.InPolys.Count);
foreach (var poly in node.InPolys)
Polygons.Add(PolygonCache.Get(polys[poly], vertexArray));
}
diff --git a/ACViewer/Physics/BSP/BSPTree.cs b/ACViewer/Physics/BSP/BSPTree.cs
index 1be1779..a18d689 100644
--- a/ACViewer/Physics/BSP/BSPTree.cs
+++ b/ACViewer/Physics/BSP/BSPTree.cs
@@ -136,6 +136,8 @@ public TransitionState find_collisions(Transition transition, float scale)
var center = path.LocalSpaceCurrCenter[0].Center;
var localSphere = path.LocalSpaceSphere[0];
+ var localSphere_ = path.NumSphere > 1 ? path.LocalSpaceSphere[1] : null;
+
var movement = localSphere.Center - center;
if (path.InsertType == InsertType.Placement || path.ObstructionEthereal)
@@ -144,7 +146,7 @@ public TransitionState find_collisions(Transition transition, float scale)
if (path.BuildingCheck)
clearCell = !path.HitsInteriorCell;
- if (RootNode.sphere_intersects_solid(localSphere, clearCell))
+ if (RootNode.sphere_intersects_solid(localSphere, clearCell) || path.NumSphere > 1 && RootNode.sphere_intersects_solid(localSphere_, clearCell))
return TransitionState.Collided;
else
return TransitionState.OK;
@@ -195,7 +197,6 @@ public TransitionState find_collisions(Transition transition, float scale)
if (path.NumSphere > 1)
{
Polygon hitPoly_ = null;
- var localSphere_ = path.LocalSpaceSphere[1];
if (RootNode.sphere_intersects_poly(localSphere_, movement, ref hitPoly_, ref contactPoint))
return slide_sphere(transition, hitPoly_.Plane.Normal);
@@ -219,7 +220,6 @@ public TransitionState find_collisions(Transition transition, float scale)
}
else if (path.NumSphere > 1)
{
- var localSphere_ = path.LocalSpaceSphere[1];
if (RootNode.sphere_intersects_poly(localSphere_, movement, ref hitPoly, ref contactPoint) || hitPoly != null)
{
var collisionNormal = path.LocalSpacePos.LocalToGlobalVec(hitPoly.Plane.Normal);
diff --git a/ACViewer/Physics/Collision/BBox.cs b/ACViewer/Physics/Collision/BBox.cs
index 190ad8c..b88895b 100644
--- a/ACViewer/Physics/Collision/BBox.cs
+++ b/ACViewer/Physics/Collision/BBox.cs
@@ -67,7 +67,7 @@ public bool Contains(Vector3 point)
public void ConvertToGlobal(Position pos)
{
- var transform = Matrix4x4.CreateFromQuaternion(pos.Frame.Orientation) * Matrix4x4.CreateTranslation(pos.Frame.Origin); ;
+ var transform = Matrix4x4.CreateFromQuaternion(pos.Frame.Orientation) * Matrix4x4.CreateTranslation(pos.Frame.Origin);
Min = Vector3.Transform(Min, transform);
Max = Vector3.Transform(Max, transform);
// adjust?
@@ -104,12 +104,26 @@ public void LocalToGlobal(BBox fromBox, Position fromPos, Position toPos)
{
Min = toPos.LocalToGlobal(fromPos, fromBox.Min);
Max = toPos.LocalToGlobal(fromPos, fromBox.Max);
+
+ AdjustBBox(toPos.LocalToGlobal(fromPos, new Vector3(fromBox.Max.X, fromBox.Max.Y, fromBox.Min.Z)));
+ AdjustBBox(toPos.LocalToGlobal(fromPos, new Vector3(fromBox.Max.X, fromBox.Min.Y, fromBox.Max.Z)));
+ AdjustBBox(toPos.LocalToGlobal(fromPos, new Vector3(fromBox.Min.X, fromBox.Max.Y, fromBox.Max.Z)));
+ AdjustBBox(toPos.LocalToGlobal(fromPos, new Vector3(fromBox.Min.X, fromBox.Max.Y, fromBox.Min.Z)));
+ AdjustBBox(toPos.LocalToGlobal(fromPos, new Vector3(fromBox.Max.X, fromBox.Min.Y, fromBox.Min.Z)));
+ AdjustBBox(toPos.LocalToGlobal(fromPos, new Vector3(fromBox.Min.X, fromBox.Min.Y, fromBox.Max.Z)));
}
public void LocalToLocal(BBox fromBox, Position fromPos, Position toPos)
{
Min = toPos.LocalToLocal(fromPos, fromBox.Min);
Max = toPos.LocalToLocal(fromPos, fromBox.Max);
+
+ AdjustBBox(toPos.LocalToLocal(fromPos, new Vector3(fromBox.Max.X, fromBox.Max.Y, fromBox.Min.Z)));
+ AdjustBBox(toPos.LocalToLocal(fromPos, new Vector3(fromBox.Max.X, fromBox.Min.Y, fromBox.Max.Z)));
+ AdjustBBox(toPos.LocalToLocal(fromPos, new Vector3(fromBox.Min.X, fromBox.Max.Y, fromBox.Max.Z)));
+ AdjustBBox(toPos.LocalToLocal(fromPos, new Vector3(fromBox.Min.X, fromBox.Max.Y, fromBox.Min.Z)));
+ AdjustBBox(toPos.LocalToLocal(fromPos, new Vector3(fromBox.Max.X, fromBox.Min.Y, fromBox.Min.Z)));
+ AdjustBBox(toPos.LocalToLocal(fromPos, new Vector3(fromBox.Min.X, fromBox.Min.Y, fromBox.Max.Z)));
}
}
}
diff --git a/ACViewer/Physics/Collision/CollisionInfo.cs b/ACViewer/Physics/Collision/CollisionInfo.cs
index 356d018..50ef12c 100644
--- a/ACViewer/Physics/Collision/CollisionInfo.cs
+++ b/ACViewer/Physics/Collision/CollisionInfo.cs
@@ -27,6 +27,9 @@ public class CollisionInfo
public bool CollidedWithEnvironment;
public int FramesStationaryFall;
+ // custom for server
+ public bool VerifiedRestrictions;
+
public CollisionInfo()
{
Init();
diff --git a/ACViewer/Physics/Collision/CollisionRecord.cs b/ACViewer/Physics/Collision/CollisionRecord.cs
index 1e6429b..114161e 100644
--- a/ACViewer/Physics/Collision/CollisionRecord.cs
+++ b/ACViewer/Physics/Collision/CollisionRecord.cs
@@ -4,5 +4,11 @@ public class CollisionRecord
{
public double TouchedTime;
public bool Ethereal;
+
+ public CollisionRecord(double touchedTime, bool ethereal)
+ {
+ TouchedTime = touchedTime;
+ Ethereal = ethereal;
+ }
}
}
diff --git a/ACViewer/Physics/Collision/ObjCollisionProfile.cs b/ACViewer/Physics/Collision/ObjCollisionProfile.cs
index 66ae600..7f541ad 100644
--- a/ACViewer/Physics/Collision/ObjCollisionProfile.cs
+++ b/ACViewer/Physics/Collision/ObjCollisionProfile.cs
@@ -1,28 +1,44 @@
using System;
using System.Numerics;
+using ACE.Entity.Enum;
namespace ACE.Server.Physics.Collision
{
[Flags]
public enum ObjCollisionProfileFlags
{
- Undefined = 0x0,
- Creature = 0x1,
- Player = 0x2,
+ Undefined = 0x0,
+ Creature = 0x1,
+ Player = 0x2,
Attackable = 0x4,
- Missile = 0x8,
- Contact = 0x10,
- MyContact = 0x20,
- Door = 0x40,
- Cloaked = 0x80,
+ Missile = 0x8,
+ Contact = 0x10,
+ MyContact = 0x20,
+ Door = 0x40,
+ Cloaked = 0x80,
};
public class ObjCollisionProfile
{
public uint ID;
public Vector3 Velocity;
- public int wcid;
- public int ItemType;
+ public uint WCID;
+ public ItemType ItemType;
public ObjCollisionProfileFlags Flags;
+
+ public ObjCollisionProfile() { }
+
+ public ObjCollisionProfile(uint id, Vector3 velocity, bool missile, bool contact, bool myContact)
+ {
+ ID = id;
+ Velocity = velocity;
+
+ if (missile)
+ Flags |= ObjCollisionProfileFlags.Missile;
+ if (contact)
+ Flags |= ObjCollisionProfileFlags.Contact;
+ if (myContact)
+ Flags |= ObjCollisionProfileFlags.MyContact;
+ }
}
}
diff --git a/ACViewer/Physics/Combat/AtkCollisionProfile.cs b/ACViewer/Physics/Combat/AtkCollisionProfile.cs
index 89cc11c..b3885c9 100644
--- a/ACViewer/Physics/Combat/AtkCollisionProfile.cs
+++ b/ACViewer/Physics/Combat/AtkCollisionProfile.cs
@@ -1,11 +1,20 @@
+using ACE.Entity.Enum;
using ACE.Server.Physics.Collision;
namespace ACE.Server.Physics.Combat
{
- public class AtkCollisionProfile: ObjCollisionProfile
+ public class AtkCollisionProfile : ObjCollisionProfile
{
public int Part;
- //public int ID;
- public int Location;
+ public Quadrant Location;
+
+ public AtkCollisionProfile() { }
+
+ public AtkCollisionProfile(uint id, int part, Quadrant location)
+ {
+ ID = id;
+ Part = part;
+ Location = location;
+ }
}
}
diff --git a/ACViewer/Physics/Combat/AtkObjInfo.cs b/ACViewer/Physics/Combat/AtkObjInfo.cs
index 6740b42..429d8b7 100644
--- a/ACViewer/Physics/Combat/AtkObjInfo.cs
+++ b/ACViewer/Physics/Combat/AtkObjInfo.cs
@@ -1,12 +1,10 @@
-using System;
-using System.Collections.Generic;
-using System.Text;
+using ACE.Entity.Enum;
namespace ACE.Server.Physics.Combat
{
public class AtkObjInfo
{
public uint ObjectID;
- public int HitLocation;
+ public Quadrant HitLocation;
}
}
diff --git a/ACViewer/Physics/Combat/AttackInfo.cs b/ACViewer/Physics/Combat/AttackInfo.cs
index 2613519..624446c 100644
--- a/ACViewer/Physics/Combat/AttackInfo.cs
+++ b/ACViewer/Physics/Combat/AttackInfo.cs
@@ -1,4 +1,5 @@
using System.Collections.Generic;
+using ACE.Entity.Enum;
namespace ACE.Server.Physics.Combat
{
@@ -11,7 +12,7 @@ public class AttackInfo
public int NumObjects;
public List ObjectList;
- public void AddObject(uint objectID, int hitLocation)
+ public void AddObject(uint objectID, Quadrant hitLocation)
{
}
diff --git a/ACViewer/Physics/Combat/TargetInfo.cs b/ACViewer/Physics/Combat/TargetInfo.cs
index 60edd66..d587852 100644
--- a/ACViewer/Physics/Combat/TargetInfo.cs
+++ b/ACViewer/Physics/Combat/TargetInfo.cs
@@ -49,8 +49,8 @@ public TargetInfo(TargetInfo info)
Quantum = info.Quantum;
TargetPosition = new Position(info.TargetPosition);
InterpolatedPosition = new Position(info.InterpolatedPosition);
- InterpolatedHeading = new Vector3(info.InterpolatedHeading.X, info.InterpolatedHeading.Y, info.InterpolatedHeading.Z);
- Velocity = new Vector3(info.Velocity.X, info.Velocity.Y, info.Velocity.Z);
+ InterpolatedHeading = info.InterpolatedHeading;
+ Velocity = info.Velocity;
Status = info.Status;
LastUpdateTime = info.LastUpdateTime;
}
diff --git a/ACViewer/Physics/Command/ACCmdInterp.cs b/ACViewer/Physics/Command/ACCmdInterp.cs
new file mode 100644
index 0000000..b1a10fa
--- /dev/null
+++ b/ACViewer/Physics/Command/ACCmdInterp.cs
@@ -0,0 +1,118 @@
+using System;
+using System.Collections.Generic;
+using System.Text;
+using ACE.Entity.Enum;
+
+namespace ACE.Server.Physics.Command
+{
+ public class ACCmdInterp : CommandInterpreter
+ {
+ public void CommenceJump()
+ {
+ /*var combatSystem = ClientCombatSystem.GetCombatSystem();
+ if (combatSystem != null)
+ combatSystem.CommenceJump();*/
+ }
+
+ public void DoJump()
+ {
+ /*var combatSystem = ClientCombatSystem.DoJump();
+ if (combatSystem != null)
+ combatSystem.DoJump();*/
+ }
+
+ public override void HandleNewForwardMovement()
+ {
+ /*var combatSystem = ClientCombatSystem.GetCombatSystem();
+ if (combatSystem != null)
+ combatSystem.AbortAutomaticAttack();*/
+
+ base.HandleNewForwardMovement();
+ }
+
+ public override void TakeControlFromServer()
+ {
+ /*var combatSystem = ClientCombatSystem.GetCombatSystem();
+ if (combatSystem != null)
+ combatSystem.AbortAutomaticAttack();*/
+
+ base.TakeControlFromServer();
+ }
+
+ public bool OnAction(InputEvent evt)
+ {
+ // vfptr[12] - IsActive
+ if (!IsActive())
+ return true;
+
+ switch (evt.InputAction)
+ {
+ case 0x32:
+ // vfptr[2].OnLoseFocus
+ return true;
+
+ case 0x30:
+ SetMotion(MotionCommand.AutoRun, true);
+ return true;
+
+ case 0x29:
+ SetMotion(MotionCommand.WalkForward, evt.Start);
+ return true;
+
+ case 0x2A:
+ SetMotion(MotionCommand.WalkBackwards, evt.Start);
+ return true;
+
+ case 0x2B:
+ SetMotion(MotionCommand.Ready, true);
+ return true;
+
+ case 0x2E:
+ SetMotion(MotionCommand.TurnRight, evt.Start);
+ return true;
+
+ case 0x2F:
+ SetMotion(MotionCommand.TurnLeft, evt.Start);
+ return true;
+
+ case 0x2C:
+ SetMotion(MotionCommand.SideStepRight, evt.Start);
+ return true;
+
+ case 0x2D:
+ SetMotion(MotionCommand.SideStepLeft, evt.Start);
+ return true;
+
+ case 0x31:
+ if (evt.Start)
+ CommenceJump(); // vfptr[5].OnAction - CommenceJump
+ else
+ DoJump(); // vfptr[5].OnLoseFocus - DoJump
+
+ return true;
+
+ default:
+ //var result = HashEmoteInputActionsToCommands.TryGetValue(evt.InputAction, out var emoteCommand);
+ var result = false;
+ //if (result)
+ //SetMotion(emoteCommand, true);
+ return result;
+ }
+ }
+
+ public void SetMotion(MotionCommand motion, bool start)
+ {
+ if (Player == null)
+ return;
+
+ var cmdStruct = new CmdStruct();
+ cmdStruct.Args[0] = 0;
+ cmdStruct.Args[1] = (uint)motion;
+ cmdStruct.Args[2] = Convert.ToUInt32(start);
+ cmdStruct.Args[3] = 4;
+
+ // vfptr[12].OnLoseFocus - HandleKeyboardCommand
+ HandleKeyboardCommand(cmdStruct, motion);
+ }
+ }
+}
diff --git a/ACViewer/Physics/Command/CmdStruct.cs b/ACViewer/Physics/Command/CmdStruct.cs
new file mode 100644
index 0000000..e44ef34
--- /dev/null
+++ b/ACViewer/Physics/Command/CmdStruct.cs
@@ -0,0 +1,12 @@
+using ACE.Entity.Enum;
+
+namespace ACE.Server.Physics.Command
+{
+ public class CmdStruct
+ {
+ public uint[] Args = new uint[64];
+ public uint Size;
+ public uint Curr;
+ public MotionCommand Command;
+ }
+}
diff --git a/ACViewer/Physics/Command/CommandInterpreter.cs b/ACViewer/Physics/Command/CommandInterpreter.cs
new file mode 100644
index 0000000..1484958
--- /dev/null
+++ b/ACViewer/Physics/Command/CommandInterpreter.cs
@@ -0,0 +1,885 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Numerics;
+using ACE.Entity.Enum;
+using ACE.Server.Physics.Animation;
+using ACE.Server.Physics.Common;
+
+namespace ACE.Server.Physics.Command
+{
+ public class CommandInterpreter
+ {
+ public PhysicsObj Player;
+ public SmartBox SmartBox;
+ public List SubstateList;
+ public List TurnList;
+ public List SidestepList;
+ public uint AutonomyLevel;
+ public bool ControlledByServer;
+ public bool HoldRun;
+ public bool HoldSidestep;
+ public bool TransientState { get; set; }
+ public bool Enabled;
+ public bool AutoRun;
+ public bool MouseLookActive;
+ public bool MouseLeftDown;
+ public float AutoRunSpeed;
+ public uint ActionStamp;
+ public DateTime LastSentPositionTime;
+ public Position LastSentPosition;
+ public Plane LastSentContactPlane;
+ public const double TimeBetweenPositionEvents = 1.875f;
+
+ public CommandInterpreter()
+ {
+ SubstateList = new List();
+ TurnList = new List();
+ SidestepList = new List();
+
+ AutonomyLevel = 2;
+ AutoRunSpeed = 1.0f;
+ ControlledByServer = true;
+ Enabled = true;
+ ActionStamp = 1;
+
+ LastSentPositionTime = DateTime.UtcNow;
+ LastSentPosition = new Position();
+ LastSentContactPlane = new Plane();
+ }
+
+ public void AddCommand(MotionCommand command, float speed, bool mouse, bool newHoldRun)
+ {
+ // vfptr[1](command) - whichlist
+ var list = WhichList(command);
+ if (list != null)
+ {
+ // CommandList.AddCommand
+ list.Add(new CommandListElement(command, speed, newHoldRun));
+ if (((uint)command & (uint)CommandMask.SubState) != 0)
+ {
+ if (list == SubstateList)
+ {
+ // vfptr[6] - ACCmdInterp::HandleNewForwardMovement
+ HandleNewForwardMovement();
+ }
+
+ TransientState = false;
+ }
+ }
+ else if (((uint)command & (uint)CommandMask.SubState) != 0)
+ {
+ if (((uint)command & (uint)CommandMask.Toggle) == 0)
+ {
+ // vfptr[6] - ACCmdInterp::HandleNewForwardMovement
+ HandleNewForwardMovement();
+ if (command != MotionCommand.Ready)
+ TransientState = true;
+ }
+ }
+ }
+
+ public void ApplyCurrentMovement(int a2)
+ {
+ if (Player == null)
+ return;
+
+ if (AutoRun)
+ {
+ // vfptr[13].OnAction - MovePlayer
+ MovePlayer(MotionCommand.WalkForward, true, AutoRunSpeed, true, true);
+ // goto LABEL_9
+ }
+ else
+ {
+ if (SubstateList.Count != 0)
+ {
+ // vfptr[3].OnLoseFocus - ApplyListHeadMovement
+ ApplyListHeadMovement(SubstateList);
+ }
+ else if (!TransientState)
+ {
+ MovePlayer(MotionCommand.Ready, true, AutoRunSpeed, false, true);
+ }
+ }
+
+ // LABEL_9
+ if (TurnList.Count != 0)
+ {
+ // vfptr[3].OnLoseFocus - ApplyListHeadMovement w/ extra param?
+ //ApplyListHeadMovement(TurnList, a2);
+ ApplyListHeadMovement(TurnList);
+ }
+ else
+ {
+ //MovePlayer(MotionCommand.SideStepRight, false, 1.0f /* ? */, false, 0, a2);
+ MovePlayer(MotionCommand.SideStepRight, false, 1.0f /* ? */, false, false);
+ MovePlayer(MotionCommand.TurnRight, false, 1.0f /* /? */, false, false);
+ }
+
+ if (SidestepList.Count != 0)
+ {
+ ApplyListHeadMovement(SidestepList);
+ }
+ else
+ {
+ MovePlayer(MotionCommand.SideStepRight, false, 1.0f, false, false);
+ }
+ }
+
+ public void ApplyHoldKeysToCommand(ref MotionCommand command, float speed)
+ {
+ if (!HoldSidestep)
+ return;
+
+ if (command == MotionCommand.TurnRight)
+ {
+ command = MotionCommand.SideStepRight;
+ }
+ else if (command == MotionCommand.TurnLeft)
+ {
+ command = MotionCommand.SideStepLeft;
+ }
+ }
+
+ public void ApplyListHeadMovement(List list)
+ {
+ var head = list.LastOrDefault();
+ if (head == null)
+ return;
+
+ // if head is mouse
+ //MovePlayer(head.Command, true, head.Speed, true, head.HoldRun);
+
+ // else
+ MovePlayer(head.Command, true, head.Speed, false, false); // always false?
+ }
+
+ public bool BookkeepCommandAndModifyIfNecessary(MotionCommand command, bool start, float speed, bool mouse, bool newHoldRun)
+ {
+ if (command == MotionCommand.Jump)
+ return true;
+
+ if (start)
+ {
+ // vfptr[1].OnAction - AddCommand
+ AddCommand(command, speed, mouse, newHoldRun);
+ return true;
+ }
+ else
+ {
+ // vfptr[1].OnLoseFocus - MovePlayer?
+ MovePlayer(command, start, speed, mouse, newHoldRun);
+ }
+ return false;
+ }
+
+ public void ClearAllCommands()
+ {
+ SubstateList.Clear();
+ TurnList.Clear();
+ SidestepList.Clear();
+ }
+
+ public void Disable()
+ {
+ // vfptr[3].OnAction()
+ // vfptr[2].OnLoseFocus(0);
+ var autonomy = AutonomyLevel != 0;
+ HoldSidestep = false;
+ if (AutonomyLevel != 0 && ControlledByServer)
+ {
+ // vfptr[2].OnAction(this)
+ // vfptr[6].OnAction(this)
+ }
+ }
+
+ public void Enable()
+ {
+ Enabled = true;
+ // vfptr[2].OnLoseFocus(HoldRun);
+ }
+
+ public bool GetMouseLeftDown()
+ {
+ return MouseLeftDown;
+ }
+
+ public bool GetMouseLookActive()
+ {
+ return MouseLookActive;
+ }
+
+ public void HandleExhaustion()
+ {
+ if (Player != null)
+ Player.report_exhaustion();
+ }
+
+ public void HandleKeyboardCommand(CmdStruct cmdStruct, MotionCommand command)
+ {
+ // vfptr[12] - IsActive
+ if (!IsActive()) return;
+
+ bool start;
+
+ if (cmdStruct.Command == MotionCommand.AutoRun)
+ {
+ start = Convert.ToBoolean(cmdStruct.Args[cmdStruct.Curr]);
+ cmdStruct.Curr++;
+ if (cmdStruct.Curr >= cmdStruct.Size)
+ {
+ AutoRunSpeed = 1.0f;
+ }
+ else
+ {
+ AutoRunSpeed = BitConverter.ToSingle(BitConverter.GetBytes(cmdStruct.Args[cmdStruct.Curr]), 0);
+ cmdStruct.Curr++;
+ }
+ // vfptr[16].OnLoseFocus - ToggleAutoRun
+ ToggleAutoRun();
+ // vfptr[6].OnAction - SendMovementEvent
+ SendMovementEvent();
+ return;
+ }
+
+ if (((uint)cmdStruct.Command & (uint)CommandMask.UI) != 0)
+ return;
+
+ start = Convert.ToBoolean(cmdStruct.Args[cmdStruct.Curr]);
+ cmdStruct.Curr++;
+
+ var speed = 1.0f;
+ if (cmdStruct.Curr < cmdStruct.Size)
+ {
+ speed = BitConverter.ToSingle(BitConverter.GetBytes(cmdStruct.Args[cmdStruct.Curr]), 0);
+ cmdStruct.Curr++;
+ }
+
+ if (ControlledByServer && !start)
+ {
+ // vfptr[1].OnLoseFocus - MovePlayer?
+ MovePlayer((MotionCommand)cmdStruct.Command, start, speed, false, false);
+ return;
+ }
+
+ // vfptr[8].OnLoseFocus(a2) - ACCmdInterp::TakeControlFromServer?
+ TakeControlFromServer();
+
+ if (cmdStruct.Command == MotionCommand.HoldRun)
+ {
+ // vfptr[2].OnLoseFocus
+
+ if (!IsStandingStill())
+ SendMovementEvent();
+
+ return;
+ }
+
+ if (cmdStruct.Command == MotionCommand.HoldSidestep)
+ {
+ // vfptr[3]
+
+ if (!IsStandingStill())
+ SendMovementEvent();
+
+ return;
+ }
+
+ // vfptr[2] - Bookkeep
+ if (!BookkeepCommandAndModifyIfNecessary(cmdStruct.Command, start, speed, false, false))
+ {
+ SendMovementEvent();
+ return;
+ }
+
+ // vfptr[4].OnAction - ApplyHoldKeysToCommand
+ ApplyHoldKeysToCommand(ref cmdStruct.Command, speed);
+
+ // vfptr[13].OnAction - MovePlayer
+ MovePlayer(cmdStruct.Command, start, speed, false, false);
+
+ // vfptr[6].OnAction - SendMovementEvent
+ if (cmdStruct.Command != MotionCommand.Jump)
+ SendMovementEvent();
+ }
+
+ public void HandleLogOff()
+ {
+ // vfptr[11]
+ Disable();
+ }
+
+ public void HandleMouseMovementCommand(CmdStruct cmdStruct)
+ {
+
+ }
+
+ public virtual void HandleNewForwardMovement()
+ {
+ // vfptr[17](0, 1) - SetAutoRun
+ SetAutoRun(false, true);
+ }
+
+ public bool HandleSelectLeft(bool start)
+ {
+ MouseLeftDown = true;
+
+ // CInputManager : ICIDM
+ return false;
+ }
+
+ public bool IsActive()
+ {
+ return Enabled && Player != null;
+ }
+
+ public bool IsEnabled()
+ {
+ return Enabled;
+ }
+
+ public bool IsStandingStill()
+ {
+ if (Player == null)
+ return true;
+
+ var minterp = Player.get_minterp();
+
+ return minterp.is_standing_still();
+ }
+
+ public void LoseControlToServer()
+ {
+ if (AutonomyLevel == 0)
+ return;
+
+ ControlledByServer = true;
+ // vfptr[17](0, 0)
+ SetAutoRun(false, false);
+ // vfptr[6].OnLoseFocus(this)
+ }
+
+ public void LoseKeyboardFocus()
+ {
+ ClearAllCommands();
+ // vfptr[2].OnLoseFocus(this, 0) - SetHoldRun
+ SetHoldRun(false);
+
+ HoldSidestep = false;
+ // vfptr[6].OnLoseFocus(this) - ACCmdInterp::FinishJump
+
+ if (AutonomyLevel != 0)
+ {
+ if (!ControlledByServer)
+ {
+ // vfptr[2].OnAction
+ // vfptr[6].OnAction
+ }
+ }
+ }
+
+ public bool MaybeStopCompletely()
+ {
+ if (ControlledByServer)
+ return true;
+
+ // vfptr[15].OnLoseFocus
+ return false;
+ }
+
+ public void MovePlayer(MotionCommand command, bool start, float speed, bool mouse, bool newHoldRun)
+ {
+ if (Player == null || Player.InqInterpretedMotionState() == null)
+ return;
+
+ // if vfptr[10] - PlayerIsDead
+ if (PlayerIsDead())
+ {
+ // vfptr[9].OnAction - LoseKeyboardFocus
+ LoseKeyboardFocus();
+ // vfptr[17](0, 0) - SetAutoRun
+ SetAutoRun(false, false);
+ return;
+ }
+
+ // if !ICIDM::s_cidm->m_UseMouseTurning
+ // - goto LABEL_55
+
+ var mvp = new MovementParameters();
+
+ if (mouse)
+ {
+ // someFlags &= 0xFFFFF7FF;
+ // unset bit 11
+ mvp.SetHoldKey = false;
+ var holdRun = Convert.ToInt32(newHoldRun) + 1;
+ }
+
+ var turn = (MotionCommand)MotionStance.Invalid;
+ var sidestep = (MotionCommand)MotionStance.Invalid;
+
+ if (TurnList.Count != 0)
+ turn = TurnList.FirstOrDefault().Command;
+
+ if (SidestepList.Count != 0)
+ sidestep = SidestepList.FirstOrDefault().Command;
+
+ // vfptr[17].OnLoseFocus - GetMouseLookActive
+ var mouselook = GetMouseLookActive();
+
+ bool start_turn_left = false;
+ bool start_turn_right = false;
+ bool start_sidestep_left = false;
+ bool start_sidestep_right = false;
+
+ bool cancel_turn_left = false;
+ bool cancel_turn_right = false;
+ bool cancel_sidestep_left = false;
+ bool cancel_sidestep_right = false;
+
+ MotionCommand cmd1;
+
+ if (!mouse)
+ {
+ if (!mouselook)
+ {
+ cmd1 = command;
+ // goto LABEL_59
+ }
+ if (command != MotionCommand.TurnRight)
+ {
+ if (command != MotionCommand.TurnLeft)
+ {
+ if (start)
+ {
+ cancel_turn_left = true;
+ start_sidestep_left = true;
+ }
+ else
+ {
+ cancel_sidestep_left = true;
+ }
+ }
+ else
+ {
+ cancel_turn_right = true;
+ cancel_turn_left = true;
+ }
+ // goto LABEL_38
+ }
+ if (!start)
+ {
+ cancel_sidestep_right = true;
+ // goto LABEL_38
+ }
+ // LABEL_31:
+ cancel_turn_right = true;
+ start_sidestep_right = true;
+ // goto LABEL_38
+ }
+
+ if (!mouselook)
+ {
+ if (turn == MotionCommand.TurnRight)
+ {
+ cancel_sidestep_right = true;
+ start_turn_right = true;
+ }
+ else if (turn == MotionCommand.TurnLeft)
+ {
+ cancel_sidestep_left = true;
+ start_turn_left = true;
+ }
+ // goto LABEL_38
+ }
+
+ if (command != MotionCommand.MouseLook)
+ {
+ // goto LABEL_38
+ }
+
+ if (turn == MotionCommand.TurnRight)
+ {
+ cancel_turn_right = true;
+
+ if (sidestep == MotionCommand.SideStepLeft)
+ start_sidestep_left = true;
+ else
+ start_sidestep_right = true;
+
+ // goto LABEL_38
+ }
+
+ if (turn == MotionCommand.TurnLeft)
+ {
+ if (sidestep != MotionCommand.SideStepRight)
+ {
+ cancel_turn_left = true;
+ start_sidestep_left = true;
+
+ // goto LABEL_38
+ }
+ // goto LABEL_31
+ }
+
+ if (MouseLeftDown)
+ {
+ start = true;
+ command = MotionCommand.WalkForward;
+ }
+
+ // ============
+ // LABEL 38:
+
+ // vfptr[8].OnLoseFocus - TakeControlFromServer
+ TakeControlFromServer();
+
+ if (cancel_sidestep_right)
+ Player.StopMotion((uint)MotionCommand.SideStepRight, mvp, true);
+
+ if (cancel_sidestep_left)
+ Player.StopMotion((uint)MotionCommand.SideStepLeft, mvp, true);
+
+ if (cancel_turn_right)
+ Player.StopMotion((uint)MotionCommand.TurnRight, mvp, true);
+
+ if (cancel_turn_left)
+ Player.StopMotion((uint)MotionCommand.TurnLeft, mvp, true);
+
+ if (start_turn_right)
+ {
+ start = true;
+ cmd1 = MotionCommand.TurnRight;
+ }
+ else
+ cmd1 = command;
+
+ if (start_turn_left)
+ {
+ start = true;
+ cmd1 = MotionCommand.TurnLeft;
+ }
+
+ if (start_sidestep_right)
+ {
+ start = true;
+ cmd1 = MotionCommand.SideStepRight;
+ speed = 1.0f;
+ }
+
+ if (start_sidestep_left)
+ {
+ start = true;
+ command = MotionCommand.SideStepLeft;
+ speed = 1.0f;
+
+ // LABEL_55:
+ cmd1 = command;
+ }
+
+ var holdRunRel1 = 0;
+ if (mouse)
+ {
+ holdRunRel1 = Convert.ToInt32(newHoldRun) + 1;
+ // goto LABEL_60
+ }
+
+ // LABEL_59:
+ holdRunRel1 = 0;
+
+ // LABEL_60:
+ if (AutonomyLevel != 0)
+ {
+ if (start)
+ {
+ if (cmd1 != MotionCommand.Jump)
+ {
+ mvp = new MovementParameters();
+ // set 12th flag
+ mvp.Autonomous = true;
+ if (mouse)
+ mvp.SetHoldKey = false; // unset 11th flag
+ if (((uint)cmd1 & (uint)CommandMask.Action) != 0)
+ {
+ // vfptr[15].OnLoseFocus(this)
+ }
+ var werror = Player.DoMotion((uint)cmd1, mvp);
+ switch (werror)
+ {
+ case WeenieError.None:
+ if (((uint)cmd1 & (uint)CommandMask.Action) != 0)
+ ActionStamp++;
+ return;
+
+ case WeenieError.CantCrouchInCombat:
+ break; // 72
+
+ case WeenieError.CantSitInCombat:
+ break; // 73
+
+ case WeenieError.CantLieDownInCombat:
+ break; // 73
+
+ case WeenieError.YouAreTooTiredToDoThat:
+ break; // 73
+
+ case WeenieError.CantChatEmoteInCombat:
+ break; // 73
+
+ case WeenieError.CantChatEmoteNotStanding:
+ break;
+
+ default:
+ return;
+ }
+ }
+ }
+ else if (cmd1 != MotionCommand.Jump)
+ {
+ mvp = new MovementParameters();
+ var holdRunRel = 0;
+ if (mouse)
+ {
+ mvp.SetHoldKey = false;
+ holdRunRel = Convert.ToInt32(newHoldRun) + 1;
+ }
+ Player.StopMotion((uint)cmd1, mvp, true);
+ }
+ }
+ else
+ {
+ // vfptr[4].OnLoseFocus - NonAutonomous?
+ MovePlayer_NonAutonomous(cmd1, start, speed, (HoldKey)holdRunRel1);
+ }
+ }
+
+ public void MovePlayer_NonAutonomous(MotionCommand command, bool start, float speed, HoldKey holdKey)
+ {
+ if (start)
+ {
+ if (command == MotionCommand.Jump)
+ {
+ // vfptr[5].OnAction
+ }
+ else
+ {
+ // vfptr[19].OnAction
+ }
+ }
+ else if (command == MotionCommand.Jump)
+ {
+ // vfptr[5].OnLoseFocus
+ }
+ else
+ {
+ // vfptr[19].OnLoseFocus
+ }
+ }
+
+ public void NewPlayer(PhysicsObj player, bool autonomous_movement)
+ {
+ Player = player;
+ if (autonomous_movement)
+ {
+ // vfptr[2].OnAction
+ }
+ else
+ {
+ // vfptr[8].OnAction
+ }
+ }
+
+ public bool NukeCommand(MotionCommand command, bool start, float speed, bool mouse, bool newHoldRun)
+ {
+ return false;
+ }
+
+ public bool PlayerIsDead()
+ {
+ if (Player != null)
+ {
+ var motionState = Player.InqInterpretedMotionState();
+ if (motionState != null)
+ return motionState.ForwardCommand == (uint)MotionCommand.Dead;
+ }
+ return false;
+ }
+
+ public void PlayerTeleported()
+ {
+ // vfptr[17](0, 1) - SetAutoRun?
+ SetAutoRun(false, true);
+
+ // vfptr[6].OnAction - SendMovementEvent?
+ SendMovementEvent();
+ }
+
+ public void SendMovementEvent()
+ {
+
+ }
+
+ public void SendPositionEvent()
+ {
+
+ }
+
+ public void SetAutoRun(bool val, bool apply_movement)
+ {
+ if (AutoRun != val)
+ {
+ AutoRun = val;
+ TransientState = false;
+ if (val)
+ {
+ //vfptr[8].OnLoseFocus(this)
+ // display string: AutoRun ON
+ }
+ else
+ {
+ // display string: AutoRun OFF
+ }
+ }
+ if (apply_movement)
+ {
+ // vfptr[2].OnAction(this) - ApplyCurrentMovement
+ //ApplyCurrentMovement();
+ }
+ }
+
+ public bool SetAutonomyLevel(uint newLevel)
+ {
+ if (newLevel <= 2)
+ {
+ AutonomyLevel = newLevel;
+ // vfptr[19](newLevel)
+ return true;
+ }
+ return false;
+ }
+
+ public void SetHoldRun(bool newVal)
+ {
+ // vfptr[5] - ACCmdInterp::UITogglesRun()
+ }
+
+ public void SetHoldSidestep(bool newVal)
+ {
+ // vfptr[4](TurnList)
+ HoldSidestep = newVal;
+ // vfptr[2].OnAction
+ }
+
+ public void SetMouseLeftDown(bool active)
+ {
+ MouseLeftDown = active;
+ }
+
+ public void SetMouseLookActive(bool active)
+ {
+ MouseLookActive = active;
+ }
+
+ public void SetSmartBox(SmartBox smartBox)
+ {
+ SmartBox = smartBox;
+ if (smartBox != null)
+ Player = smartBox.Player;
+ else
+ Player = null;
+ }
+
+ public bool ShouldSendPositionEvent()
+ {
+ return true;
+ }
+
+ public bool StopCompletely()
+ {
+ return false;
+ }
+
+ public void StopDrift()
+ {
+
+ }
+
+ public void StopListHeadMovement(List list)
+ {
+
+ }
+
+ public virtual void TakeControlFromServer()
+ {
+ // vrptr[10]
+ if (ControlledByServer && AutonomyLevel != 0 && true)
+ {
+ ControlledByServer = false;
+
+ if (Player != null)
+ {
+ Player.LastMoveWasAutonomous = true;
+ Player.StopCompletely(true);
+ Player.StopInterpolating();
+ }
+
+ // vfptr[2].OnLoseFocus(HoldRun)
+ // vfptr[2].OnAction
+ }
+ }
+
+ public void ToggleAutoRun()
+ {
+ var toggle = !AutoRun;
+ // vfptr[17] - SetAutoRun
+ SetAutoRun(toggle, true);
+ }
+
+ public bool TurnToHeading(float newHeading, bool run)
+ {
+ return false;
+ }
+
+ public void UpdateToggleRun()
+ {
+ // vfptr[2].OnLoseFocus(HoldRun)
+ // vfptr[6].OnAction
+ }
+
+ public bool UsePositionFromServer()
+ {
+ return AutonomyLevel != 2;
+ }
+
+ public void UseTime()
+ {
+
+ }
+
+ public List WhichList(MotionCommand command)
+ {
+ switch (command)
+ {
+ case MotionCommand.TurnLeft:
+ case MotionCommand.TurnRight:
+ return TurnList;
+
+ case MotionCommand.SideStepLeft:
+ case MotionCommand.SideStepRight:
+ return SidestepList;
+
+ default:
+ if (((int)command & 0x40000000) != 0)
+ {
+ if (((int)command & 0x4000000) != 0)
+ {
+ return SubstateList;
+ }
+ }
+ break;
+ }
+ return null;
+ }
+ }
+}
diff --git a/ACViewer/Physics/Command/CommandList.cs b/ACViewer/Physics/Command/CommandList.cs
new file mode 100644
index 0000000..848464d
--- /dev/null
+++ b/ACViewer/Physics/Command/CommandList.cs
@@ -0,0 +1,127 @@
+using ACE.Entity.Enum;
+
+namespace ACE.Server.Physics.Command
+{
+ public class CommandList
+ {
+ public CommandListElement Head;
+ public CommandListElement MouseCommand;
+ public CommandListElement Current;
+
+ public void AddCommand(MotionCommand command, float speed, bool mouse, bool holdRun)
+ {
+ var element = new CommandListElement(command, speed, holdRun);
+
+ CommandListElement mouse_cmd = null;
+
+ if (mouse)
+ {
+ mouse_cmd = MouseCommand;
+
+ if (mouse_cmd == null)
+ {
+ SetMouseCommand(element);
+ return;
+ }
+
+ var prevNext = mouse_cmd.Prev.Next;
+ if (prevNext != null)
+ {
+ prevNext = mouse_cmd.Next;
+ }
+ else
+ {
+ var next = mouse_cmd.Next;
+ var nextNull = next == null;
+ Head = mouse_cmd.Next;
+ if (nextNull)
+ {
+ // label_12
+ mouse_cmd.Next = null;
+ mouse_cmd.Prev = null;
+
+ SetMouseCommand(element);
+ return;
+ }
+ next.Prev = null;
+ }
+ if (mouse_cmd.Next != null)
+ mouse_cmd.Next.Prev = mouse_cmd.Prev;
+ // goto label_12
+ }
+ }
+
+ public void ClearAllCommands()
+ {
+ if (Head != null)
+ {
+ CommandListElement headPrevNext = null;
+
+ while (true)
+ {
+ var head = Head;
+ headPrevNext = Head.Prev.Next;
+ if (headPrevNext != null)
+ break;
+
+ var headNext = head.Next;
+ var headNextNull = head.Next == null;
+ Head = head.Next;
+
+ if (!headNextNull)
+ {
+ headNext.Prev = null;
+
+ // label_6
+ if (head.Next != null)
+ head.Next.Prev = head.Prev;
+ }
+
+ head.Next = null;
+ head.Prev = null;
+
+ if (Head == null)
+ {
+ MouseCommand = null;
+ return;
+ }
+ }
+ headPrevNext = Head.Next;
+ // goto label_6
+ }
+ MouseCommand = null;
+ }
+
+ public void ClearKeyboardCommands()
+ {
+
+ }
+
+ public CommandListElement GetHead()
+ {
+ return Head;
+ }
+
+ public bool HeadIsMouse()
+ {
+ if (Head == null)
+ return false;
+
+ return Head == MouseCommand;
+ }
+
+ public bool RemoveCommand(MotionCommand command, float speed, bool mouse)
+ {
+ return false;
+ }
+
+ public void SetMouseCommand(CommandListElement element)
+ {
+ MouseCommand = element;
+ element.Next = Head;
+ if (Head != null)
+ Head.Prev = element;
+ Head = element;
+ }
+ }
+}
diff --git a/ACViewer/Physics/Command/CommandListElement.cs b/ACViewer/Physics/Command/CommandListElement.cs
new file mode 100644
index 0000000..8230765
--- /dev/null
+++ b/ACViewer/Physics/Command/CommandListElement.cs
@@ -0,0 +1,26 @@
+using ACE.Entity.Enum;
+
+namespace ACE.Server.Physics.Command
+{
+ public class CommandListElement
+ {
+ public CommandListElement Next;
+ public CommandListElement Prev;
+
+ public MotionCommand Command;
+ public float Speed;
+ public bool HoldRun;
+
+ public CommandListElement()
+ {
+ Speed = 1.0f;
+ }
+
+ public CommandListElement(MotionCommand command, float speed, bool holdRun)
+ {
+ Command = command;
+ Speed = speed;
+ HoldRun = holdRun;
+ }
+ }
+}
diff --git a/ACViewer/Physics/Command/InputEvent.cs b/ACViewer/Physics/Command/InputEvent.cs
new file mode 100644
index 0000000..bbfce9a
--- /dev/null
+++ b/ACViewer/Physics/Command/InputEvent.cs
@@ -0,0 +1,12 @@
+using System;
+using System.Collections.Generic;
+using System.Text;
+
+namespace ACE.Server.Physics.Command
+{
+ public class InputEvent
+ {
+ public uint InputAction;
+ public bool Start;
+ }
+}
diff --git a/ACViewer/Physics/Common/BldPortal.cs b/ACViewer/Physics/Common/BldPortal.cs
index 9be2b1c..ae1746d 100644
--- a/ACViewer/Physics/Common/BldPortal.cs
+++ b/ACViewer/Physics/Common/BldPortal.cs
@@ -1,4 +1,4 @@
-using System.Collections.Generic;
+using System.Collections.Generic;
using ACE.Entity.Enum;
namespace ACE.Server.Physics.Common
diff --git a/ACViewer/Physics/Common/BuildingObj.cs b/ACViewer/Physics/Common/BuildingObj.cs
index eb84e98..ebed371 100644
--- a/ACViewer/Physics/Common/BuildingObj.cs
+++ b/ACViewer/Physics/Common/BuildingObj.cs
@@ -1,4 +1,6 @@
using System.Collections.Generic;
+using System.Linq;
+
using ACE.DatLoader.Entity;
using ACE.Server.Physics.Animation;
@@ -125,6 +127,26 @@ public static BuildingObj makeBuilding(uint buildingID, List portals
return building;
}
+ public float GetMinZ()
+ {
+ get_building_cells();
+
+ var minZ = float.MaxValue;
+
+ foreach (var buildingCell in BuildingCells.Where(i => i.Environment != null))
+ {
+ if (buildingCell.Environment == null) continue;
+
+ foreach (var cellStruct in buildingCell.Environment.Cells.Values)
+ {
+ foreach (var vertex in cellStruct.VertexArray.Vertices.Values)
+ if (vertex.Origin.Z < minZ)
+ minZ = vertex.Origin.Z;
+ }
+ }
+ return minZ;
+ }
+
public void remove()
{
var sortCell = (SortCell)CurCell;
diff --git a/ACViewer/Physics/Common/EnvCell.cs b/ACViewer/Physics/Common/EnvCell.cs
index a19acf9..621a308 100644
--- a/ACViewer/Physics/Common/EnvCell.cs
+++ b/ACViewer/Physics/Common/EnvCell.cs
@@ -62,10 +62,14 @@ public EnvCell(DatLoader.FileTypes.EnvCell envCell): base()
SeenOutside = envCell.SeenOutside;
EnvironmentID = envCell.EnvironmentId;
- Environment = DBObj.GetEnvironment(EnvironmentID);
+
+ if (EnvironmentID != 0)
+ Environment = DBObj.GetEnvironment(EnvironmentID);
+
CellStructureID = envCell.CellStructure; // environment can contain multiple?
- if (Environment.Cells != null && Environment.Cells.ContainsKey(CellStructureID))
- CellStructure = new CellStruct(Environment.Cells[CellStructureID]);
+
+ if (Environment?.Cells != null && Environment.Cells.TryGetValue(CellStructureID, out var cellStruct))
+ CellStructure = new CellStruct(cellStruct);
NumSurfaces = envCell.Surfaces.Count;
}
@@ -123,7 +127,9 @@ public void build_visible_cells()
public void check_building_transit(ushort portalId, Position pos, int numSphere, List spheres, CellArray cellArray, SpherePath path)
{
- if (portalId == 0) return;
+ //if (portalId == 0) return;
+ if (portalId == ushort.MaxValue) return;
+
foreach (var sphere in spheres)
{
var globSphere = new Sphere(Pos.Frame.GlobalToLocal(sphere.Center), sphere.Radius);
@@ -139,7 +145,8 @@ public void check_building_transit(ushort portalId, Position pos, int numSphere,
public void check_building_transit(int portalId, int numParts, List parts, CellArray cellArray)
{
- if (portalId == 0) return;
+ //if (portalId == 0) return;
+ if (portalId == ushort.MaxValue) return;
var portal = Portals[portalId];
var portalPoly = CellStructure.Portals[portalId];
@@ -195,7 +202,7 @@ public ObjCell find_visible_child_cell(Vector3 origin, bool searchCells)
{
if (visibleCell == null) continue;
- var envCell = GetVisible(visibleCell.ID);
+ var envCell = GetVisible(visibleCell.ID & 0xFFFF);
if (envCell != null && envCell.point_in_cell(origin))
return envCell;
}
@@ -289,7 +296,7 @@ public override void find_transit_cells(int numParts, List parts, C
}
var cellBox = new BBox();
- cellBox.LocalToLocal(bbox, Pos, otherCell.Pos);
+ cellBox.LocalToLocal(bbox, part.Pos, otherCell.Pos);
if (otherCell.CellStructure.box_intersects_cell(cellBox))
{
cellArray.add_cell(otherCell.ID, otherCell);
@@ -298,7 +305,7 @@ public override void find_transit_cells(int numParts, List parts, C
}
}
if (checkOutside)
- LandCell.add_all_outside_cells(numParts, parts, cellArray);
+ LandCell.add_all_outside_cells(numParts, parts, cellArray, ID);
}
public override void find_transit_cells(Position position, int numSphere, List spheres, CellArray cellArray, SpherePath path)
@@ -382,6 +389,12 @@ public void init_static_objects()
var staticObj = PhysicsObj.makeObject(StaticObjectIDs[i], 0, false);
staticObj.DatObject = true;
staticObj.add_obj_to_cell(this, StaticObjectFrames[i]);
+ if (staticObj.CurCell == null)
+ {
+ //Console.WriteLine($"EnvCell {ID:X8}: failed to add {staticObj.ID:X8}");
+ staticObj.DestroyObject();
+ continue;
+ }
StaticObjects.Add(staticObj);
}
@@ -426,5 +439,25 @@ public override int GetHashCode()
{
return ID.GetHashCode();
}
+
+ public bool IsVisibleIndoors(ObjCell cell)
+ {
+ var blockDist = PhysicsObj.GetBlockDist(ID, cell.ID);
+
+ // if landblocks equal
+ if (blockDist == 0)
+ {
+ // check env VisibleCells
+ var cellID = cell.ID & 0xFFFF;
+ if (VisibleCells.ContainsKey(cellID))
+ return true;
+ }
+ return SeenOutside && blockDist <= 1;
+ }
+
+ public override bool handle_move_restriction(Transition transition)
+ {
+ return true;
+ }
}
}
diff --git a/ACViewer/Physics/Common/GfxObj.cs b/ACViewer/Physics/Common/GfxObj.cs
index 2ea9c0e..6da5289 100644
--- a/ACViewer/Physics/Common/GfxObj.cs
+++ b/ACViewer/Physics/Common/GfxObj.cs
@@ -10,13 +10,22 @@ namespace ACE.Server.Physics.Collision
{
public class GfxObj
{
- public DatLoader.FileTypes.GfxObj _dat;
public uint ID;
+ public DatLoader.FileTypes.GfxObj _dat;
public CVertexArray VertexArray;
+ ///
+ /// Only populated if !PhysicsEngine.Instance.Server
+ ///
public Dictionary Polygons;
+ ///
+ /// Only populated if !PhysicsEngine.Instance.Server
+ ///
public Dictionary PhysicsPolygons;
public Sphere PhysicsSphere;
public BSP.BSPTree PhysicsBSP;
+ ///
+ /// Only populated if !PhysicsEngine.Instance.Server
+ ///
public Vector3 SortCenter;
public Sphere DrawingSphere;
public BSP.BSPTree DrawingBSP;
@@ -36,21 +45,28 @@ public GfxObj(DatLoader.FileTypes.GfxObj gfxObj)
ID = gfxObj.Id;
VertexArray = gfxObj.VertexArray;
- Polygons = new Dictionary();
- foreach (var kvp in gfxObj.Polygons)
- Polygons.Add(kvp.Key, PolygonCache.Get(kvp.Value, gfxObj.VertexArray));
+ if (!PhysicsEngine.Instance.Server)
+ {
+ Polygons = new Dictionary();
+ foreach (var kvp in gfxObj.Polygons)
+ Polygons.Add(kvp.Key, PolygonCache.Get(kvp.Value, gfxObj.VertexArray));
+ }
if (gfxObj.PhysicsPolygons.Count > 0)
{
- PhysicsPolygons = new Dictionary();
- foreach (var kvp in gfxObj.PhysicsPolygons)
- PhysicsPolygons.Add(kvp.Key, PolygonCache.Get(kvp.Value, gfxObj.VertexArray));
+ if (!PhysicsEngine.Instance.Server)
+ {
+ PhysicsPolygons = new Dictionary();
+ foreach (var kvp in gfxObj.PhysicsPolygons)
+ PhysicsPolygons.Add(kvp.Key, PolygonCache.Get(kvp.Value, gfxObj.VertexArray));
+ }
PhysicsBSP = BSPCache.Get(gfxObj.PhysicsBSP, gfxObj.PhysicsPolygons, gfxObj.VertexArray);
PhysicsSphere = PhysicsBSP.GetSphere();
}
- SortCenter = gfxObj.SortCenter;
+ if (!PhysicsEngine.Instance.Server)
+ SortCenter = gfxObj.SortCenter;
DrawingBSP = BSPCache.Get(gfxObj.DrawingBSP, gfxObj.Polygons, gfxObj.VertexArray);
DrawingSphere = DrawingBSP.GetSphere();
diff --git a/ACViewer/Physics/Common/ImgTex.cs b/ACViewer/Physics/Common/ImgTex.cs
index f2c604e..091a1fe 100644
--- a/ACViewer/Physics/Common/ImgTex.cs
+++ b/ACViewer/Physics/Common/ImgTex.cs
@@ -18,7 +18,7 @@ public class ImgTex
public uint TextureCode;
public bool IsLocked;
- public DatLoader.FileTypes.Texture _renderSurface;
+ public Texture _texture;
public SurfaceTexture _surfaceTexture;
public static ImageScaleType LandTextureScale;
@@ -41,9 +41,9 @@ public ImgTex()
}
- public ImgTex(DatLoader.FileTypes.Texture renderSurface)
+ public ImgTex(Texture texture)
{
- _renderSurface = renderSurface;
+ _texture = texture;
}
public ImgTex(SurfaceTexture surfaceTexture)
@@ -59,7 +59,7 @@ public ImgTex(SurfaceTexture surfaceTexture)
ID = _surfaceTexture.Id;
var textureID = TextureCode = surfaceTexture.Textures[0]; // use texturecode here?
//Console.WriteLine($"Loading texture {textureID:X8}");
- var renderSurface = DatManager.PortalDat.ReadFromDat(textureID);
+ var renderSurface = DatManager.PortalDat.ReadFromDat(textureID);
ImageData = new RenderSurface(renderSurface);
}
diff --git a/ACViewer/Physics/Common/LScape.cs b/ACViewer/Physics/Common/LScape.cs
index 77e0bc5..e3fef06 100644
--- a/ACViewer/Physics/Common/LScape.cs
+++ b/ACViewer/Physics/Common/LScape.cs
@@ -3,6 +3,9 @@
using System.Collections.Generic;
using System.Numerics;
+using ACE.Entity;
+using ACE.Server.Physics.Util;
+
namespace ACE.Server.Physics.Common
{
public static class LScape
@@ -11,6 +14,9 @@ public static class LScape
public static int MidWidth = 11;
private static readonly object landblockMutex = new object();
+ ///
+ /// This is not used if PhysicsEngine.Instance.Server is true
+ ///
public static ConcurrentDictionary Landblocks = new ConcurrentDictionary();
public static Dictionary BlockDrawList = new Dictionary();
@@ -37,6 +43,8 @@ public static bool SetMidRadius(int radius)
return true;
}
+ public static int LandblocksCount => Landblocks.Count;
+
///
/// Loads the backing store landblock structure
/// This function is thread safe
@@ -44,6 +52,16 @@ public static bool SetMidRadius(int radius)
/// Any landblock + cell ID within the landblock
public static Landblock get_landblock(uint blockCellID)
{
+ var landblockID = blockCellID | 0xFFFF;
+
+ /*if (PhysicsEngine.Instance.Server)
+ {
+ var lbid = new LandblockId(landblockID);
+ var lbmLandblock = LandblockManager.GetLandblock(lbid, false, false);
+
+ return lbmLandblock.PhysicsLandblock;
+ }*/
+
// client implementation
/*if (Landblocks == null || Landblocks.Count == 0)
return null;
@@ -62,8 +80,6 @@ public static Landblock get_landblock(uint blockCellID)
return Landblocks[yDiff + xDiff * MidWidth];*/
- var landblockID = blockCellID | 0xFFFF;
-
// check if landblock is already cached
if (Landblocks.TryGetValue(landblockID, out var landblock))
return landblock;
@@ -87,14 +103,31 @@ public static Landblock get_landblock(uint blockCellID)
public static bool unload_landblock(uint landblockID)
{
- return Landblocks.TryRemove(landblockID, out _);
+ if (PhysicsEngine.Instance.Server)
+ {
+ // todo: Instead of ACE.Server.Entity.Landblock.Unload() calling this function, it should be calling PhysicsLandblock.Unload()
+ // todo: which would then call AdjustCell.AdjustCells.Remove()
+
+ AdjustCell.AdjustCells.TryRemove(landblockID >> 16, out _);
+ return true;
+ }
+
+ var result = Landblocks.TryRemove(landblockID, out _);
+ // todo: Like mentioned above, the following function should be moved to ACE.Server.Physics.Common.Landblock.Unload()
+ AdjustCell.AdjustCells.TryRemove(landblockID >> 16, out _);
+ return result;
}
public static void unload_landblocks_all()
{
+ // ServerObjectManager?
Landblocks.Clear();
}
-
+
+ ///
+ /// Gets the landcell from a landblock. If the cell is an indoor cell and hasn't been loaded, it will be loaded.
+ /// This function is thread safe
+ ///
public static ObjCell get_landcell(uint blockCellID)
{
//Console.WriteLine($"get_landcell({blockCellID:X8}");
@@ -117,12 +150,19 @@ public static ObjCell get_landcell(uint blockCellID)
// indoor cells
else
{
- landblock.LandCells.TryGetValue((int)cellID, out cell);
- if (cell != null) return cell;
- cell = DBObj.GetEnvCell(blockCellID);
- landblock.LandCells.Add((int)cellID, cell);
- var envCell = cell as EnvCell;
- envCell.PostInit();
+ if (landblock.LandCells.TryGetValue((int)cellID, out cell))
+ return cell;
+
+ lock (landblock.LandCellMutex)
+ {
+ if (landblock.LandCells.TryGetValue((int)cellID, out cell))
+ return cell;
+
+ cell = DBObj.GetEnvCell(blockCellID);
+ landblock.LandCells.TryAdd((int)cellID, cell);
+ var envCell = (EnvCell)cell;
+ envCell.PostInit();
+ }
}
return cell;
}
diff --git a/ACViewer/Physics/Common/LandCell.cs b/ACViewer/Physics/Common/LandCell.cs
index f3bfb4e..4f50237 100644
--- a/ACViewer/Physics/Common/LandCell.cs
+++ b/ACViewer/Physics/Common/LandCell.cs
@@ -3,6 +3,7 @@
using System.Numerics;
using ACE.Entity.Enum;
using ACE.Server.Physics.Animation;
+using ACE.Server.Physics.Collision;
namespace ACE.Server.Physics.Common
{
@@ -116,9 +117,92 @@ public static void add_all_outside_cells(Position position, int numSphere, List<
}
}
- public static void add_all_outside_cells(int numParts, List parts, CellArray cellArray)
+ public static void add_all_outside_cells(int numParts, List parts, CellArray cellArray, uint id)
{
- // not implemented yet
+ if (cellArray.AddedOutside)
+ return;
+
+ cellArray.AddedOutside = true;
+
+ if (numParts == 0)
+ return;
+
+ var min_x = 0;
+ var min_y = 0;
+ var max_x = 0;
+ var max_y = 0;
+
+ for (var i = 0; i < numParts; i++)
+ {
+ var curPart = parts[i];
+
+ var loc = new Position(curPart.Pos);
+
+ if (!LandDefs.AdjustToOutside(loc))
+ continue;
+
+ var _lcoord = LandDefs.gid_to_lcoord(loc.ObjCellID);
+
+ if (_lcoord == null)
+ continue;
+
+ var lcoord = _lcoord.Value;
+
+ var lx = (int)(((loc.ObjCellID & 0xFFFF) - 1) / 8);
+ var ly = (int)(((loc.ObjCellID & 0xFFFF) - 1) % 8);
+
+ for (var j = 0; j < numParts; j++)
+ {
+ var otherPart = parts[j];
+
+ if (otherPart == null)
+ continue;
+
+ // add if missing: otherPart.Always2D, checks degrades.degradeMode != 1
+
+ var bbox = new BBox();
+ bbox.LocalToGlobal(otherPart.GetBoundingBox(), otherPart.Pos, loc);
+
+ var min_cx = (int)Math.Floor(bbox.Min.X / 24.0f);
+ var min_cy = (int)Math.Floor(bbox.Min.Y / 24.0f);
+
+ var max_cx = (int)Math.Floor(bbox.Max.X / 24.0f);
+ var max_cy = (int)Math.Floor(bbox.Max.Y / 24.0f);
+
+ min_x = Math.Min(min_cx - lx, min_x);
+ min_y = Math.Min(min_cy - ly, min_y);
+
+ max_x = Math.Max(max_cx - lx, max_x);
+ max_y = Math.Max(max_cy - ly, max_y);
+ }
+
+ add_cell_block(min_x + (int)lcoord.X, min_y + (int)lcoord.Y, max_x + (int)lcoord.X, max_y + (int)lcoord.Y, cellArray, id);
+ }
+ }
+
+ public static void add_cell_block(int min_x, int min_y, int max_x, int max_y, CellArray cellArray, uint id)
+ {
+ for (var i = min_x; i <= max_x; i++)
+ {
+ for (var j = min_y; j <= max_y; j++)
+ {
+ if (i < 0 || j < 0 || i >= LandDefs.LandLength || j >= LandDefs.LandLength)
+ continue;
+
+ var ui = (uint)i;
+ var uj = (uint)j;
+
+ var cellID = (((uj >> 3) | 32 * (ui & 0xFFFFFFF8)) << 16) | ((uj & 7) + 8 * (ui & 7) + 1);
+
+ // FIXME!
+ if (id >> 16 != cellID >> 16)
+ continue;
+
+ var cell = LScape.get_landcell(cellID);
+
+ cellArray.add_cell(cellID, cell);
+ }
+ }
}
public static void add_outside_cell(CellArray cellArray, float _x, float _y)
@@ -186,7 +270,7 @@ public bool find_terrain_poly(Vector3 origin, ref Polygon walkable)
public override void find_transit_cells(int numParts, List parts, CellArray cellArray)
{
- add_all_outside_cells(numParts, parts, cellArray);
+ add_all_outside_cells(numParts, parts, cellArray, ID);
base.find_transit_cells(numParts, parts, cellArray);
}
@@ -201,5 +285,37 @@ public override bool point_in_cell(Vector3 point)
Polygon poly = null;
return find_terrain_poly(point, ref poly);
}
+
+ public override bool handle_move_restriction(Transition transition)
+ {
+ var offset = Pos.GetOffset(transition.SpherePath.CurPos);
+
+ if (offset.Y >= -LandDefs.HalfSquareLength)
+ {
+ if (offset.Y <= LandDefs.HalfSquareLength)
+ {
+ offset.Y = 0;
+ if (offset.X < -LandDefs.HalfSquareLength)
+ offset.X = -1.0f;
+ else
+ offset.X = 1.0f;
+ }
+ else
+ {
+ offset.X = 0;
+ offset.Y = 1.0f;
+ }
+ }
+ else
+ {
+ offset.X = 0;
+ offset.Y = -1.0f;
+ }
+ var normal = new Vector3(offset.X, offset.Y, 0);
+
+ transition.CollisionInfo.SetCollisionNormal(normal);
+
+ return true;
+ }
}
}
diff --git a/ACViewer/Physics/Common/Landblock.cs b/ACViewer/Physics/Common/Landblock.cs
index 91df1a1..6af487d 100644
--- a/ACViewer/Physics/Common/Landblock.cs
+++ b/ACViewer/Physics/Common/Landblock.cs
@@ -67,6 +67,8 @@ public Landblock(CellLandblock landblock)
public void PostInit()
{
+ init_landcell();
+
init_buildings();
init_static_objs();
}
@@ -264,7 +266,12 @@ public void get_land_scenes()
var physicsObj = PhysicsObj.makeObject(obj.ObjId, 0, false);
physicsObj.DatObject = true;
physicsObj.set_initial_frame(pos.Frame);
- if (!physicsObj.obj_within_block()) continue;
+ if (!physicsObj.obj_within_block())
+ {
+ //Console.WriteLine($"Landblock {ID:X8} scenery: failed to spawn {obj.ObjId:X8}");
+ physicsObj.DestroyObject();
+ continue;
+ }
physicsObj.add_obj_to_cell(cell, pos.Frame);
var scale = ObjectDesc.ScaleObj(obj, globalCellX, globalCellY, j);
@@ -487,10 +494,29 @@ public void init_static_objs()
var position = new Position(ID, new AFrame(info.Frame));
var outside = LandDefs.AdjustToOutside(position);
var cell = get_landcell(position.ObjCellID);
- if (cell == null) continue;
+ if (cell == null)
+ {
+ //Console.WriteLine($"Landblock {ID:X8} - failed to spawn static object {info.Id:X8}");
+ obj.DestroyObject();
+ continue;
+ }
obj.add_obj_to_cell(cell, position.Frame);
add_static_object(obj);
}
+
+ if (Info.RestrictionTables != null)
+ {
+ foreach (var kvp in Info.RestrictionTables)
+ {
+ var lcoord = LandDefs.gid_to_lcoord(kvp.Key);
+
+ if (lcoord == null) continue;
+
+ var idx = ((int)lcoord.Value.Y & 7) + ((int)lcoord.Value.X & 7) * SideCellCount;
+
+ LandCells[idx].RestrictionObj = kvp.Value;
+ }
+ }
}
if (UseSceneFiles)
get_land_scenes();
@@ -524,6 +550,15 @@ public void release_objs()
DynObjsInitDone = false;
}
+ ///
+ /// Release shadow objects pointing to cells in this landblock
+ ///
+ public void release_shadow_objs()
+ {
+ foreach (var cell in LandCells.Values)
+ cell.release_shadow_objs();
+ }
+
public void release_visible_cells()
{
// legacy method
@@ -544,14 +579,12 @@ public bool IsDungeon
if (isDungeon != null)
return isDungeon.Value;
- var lbx = ID >> 24;
- var lby = (ID >> 16) & 0xFF;
-
// hack for NW island
// did a worldwide analysis for adding watercells into the formula,
// but they are inconsistently defined for some of the edges of map unfortunately
- if (lbx < 0x08 && lby > 0xF8)
+ if (BlockCoord.X < 64 && BlockCoord.Y > 1976)
{
+ //Console.WriteLine($"Allowing {ID:X8}");
isDungeon = false;
return isDungeon.Value;
}
@@ -597,58 +630,6 @@ public bool HasDungeon
}
}
- private List adjacents;
-
- ///
- /// Returns the list of adjacent landblocks
- ///
- public List get_adjacents(bool reload = false)
- {
- if (adjacents != null && !reload) return adjacents;
-
- var lbx = ID >> 24;
- var lby = ID >> 16 & 0xFF;
-
- //var _adjacents = LandblockManager.GetAdjacents(new LandblockId((byte)lbx, (byte)lby));
-
- adjacents = new List();
-
- // dungeons have no adjacents
- if (IsDungeon /*|| _adjacents == null*/) return adjacents;
-
- var startX = lbx > 0 ? lbx - 1 : lbx;
- var startY = lby > 0 ? lby - 1 : lby;
-
- var endX = lbx < 254 ? lbx + 1 : lbx;
- var endY = lby < 254 ? lby + 1 : lby;
-
- // get adjacents for outdoor landblocks
- for (var curX = startX; curX <= endX; curX++)
- {
- for (var curY = startY; curY <= endY; curY++)
- {
- // exclude current landblock
- if (curX == lbx && curY == lby) continue;
-
- var id = curX << 24 | curY << 16 | 0xFFFF;
-
- // ensure adjacent is loaded in ace landblock manager
- //if (!IsAdjacentLoaded(_adjacents, id))
- //continue;
-
- var landblock = LScape.get_landblock(id);
- if (landblock != null)
- adjacents.Add(landblock);
- }
- }
- return adjacents;
- }
-
- /*public bool IsAdjacentLoaded(List adjacents, uint landblockID)
- {
- return adjacents.Any(l => (l.Id.Raw | 0xFFFF) == landblockID);
- }*/
-
private List envcells;
public List get_envcells()
@@ -671,5 +652,10 @@ public List get_envcells()
}
return envcells;
}
+
+ public void SortObjects()
+ {
+ ServerObjects = ServerObjects.OrderBy(i => i.Order).ToList();
+ }
}
}
diff --git a/ACViewer/Physics/Common/LandblockStruct.cs b/ACViewer/Physics/Common/LandblockStruct.cs
index 9cbc4f5..54ef528 100644
--- a/ACViewer/Physics/Common/LandblockStruct.cs
+++ b/ACViewer/Physics/Common/LandblockStruct.cs
@@ -1,7 +1,8 @@
using System;
+using System.Collections.Concurrent;
using System.Collections.Generic;
-using System.Linq;
using System.Numerics;
+
using ACE.Entity.Enum;
using ACE.DatLoader;
using ACE.DatLoader.FileTypes;
@@ -24,7 +25,8 @@ public class LandblockStruct
public List Polygons;
//public List SurfaceStrips; // SurfaceTriStrips
//public int BlockSurfaceIndex;
- public Dictionary LandCells;
+ public readonly object LandCellMutex = new object();
+ public ConcurrentDictionary LandCells;
public List SWtoNEcut { get; set; }
// client-only
@@ -492,8 +494,8 @@ public void Init()
//BlockSurfaceIndex = -1;
// init for landcell
- LandCells = new Dictionary();
- for (uint i = 1; i <= 64; i++) LandCells.Add((int)i, new LandCell((i)));
+ LandCells = new ConcurrentDictionary();
+ for (uint i = 1; i <= 64; i++) LandCells.TryAdd((int)i, new LandCell((i)));
}
///
@@ -518,9 +520,9 @@ public void InitPVArrays()
for (var i = 0; i < numSquares; i++)
SWtoNEcut.Add(false);
- LandCells = new Dictionary(numCells);
+ LandCells = new ConcurrentDictionary(1, numCells);
for (uint i = 0; i < numCells; i++)
- LandCells.Add((int)i, new LandCell((ID & LandDefs.BlockMask) + i));
+ LandCells.TryAdd((int)i, new LandCell((ID & LandDefs.BlockMask) + i));
}
///
diff --git a/ACViewer/Physics/Common/ObjCell.cs b/ACViewer/Physics/Common/ObjCell.cs
index 37f92ff..d11ceb8 100644
--- a/ACViewer/Physics/Common/ObjCell.cs
+++ b/ACViewer/Physics/Common/ObjCell.cs
@@ -2,15 +2,21 @@
using System.Collections.Generic;
using System.Linq;
using System.Numerics;
+using System.Threading;
using ACE.Entity.Enum;
using ACE.Server.Physics.Animation;
using ACE.Server.Physics.Combat;
+using ACE.Server.Physics.Managers;
+
+using log4net;
namespace ACE.Server.Physics.Common
{
public class ObjCell: PartCell, IEquatable
{
+ private static readonly ILog log = LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType);
+
public uint ID;
public LandDefs.WaterType WaterType;
public Position Pos;
@@ -27,8 +33,26 @@ public class ObjCell: PartCell, IEquatable
public List VisibleCells;
public bool SeenOutside;
public List VoyeurTable;
+
public Landblock CurLandblock;
+ ///
+ /// Returns TRUE if this is a house cell that can be protected by a housing barrier
+ ///
+ public bool IsCellRestricted => RestrictionObj != 0;
+
+ ///
+ /// TODO: This is a temporary locking mechanism, Mag-nus 2019-10-20
+ /// TODO: The objective here is to allow multi-threading of physics, divided by landblock groups
+ /// TODO: This solves the issue of a player leaving one landblock group and trying to insert itself a target landblock group while that target landblock group is also in processing
+ /// TODO: In the future, the object should be removed from the landblock group and added to a queue of items that need to be inserted into a target
+ /// TODO: That list should then be processed in a single thread.
+ /// TODO: The above solution should remove the need for ObjCell access locking, and also increase performance
+ ///
+ private readonly ReaderWriterLockSlim readerWriterLockSlim = new ReaderWriterLockSlim(LockRecursionPolicy.SupportsRecursion);
+
+ public static readonly ObjCell EmptyCell = new ObjCell();
+
public ObjCell(): base()
{
Init();
@@ -42,41 +66,66 @@ public ObjCell(uint cellID): base()
public void AddObject(PhysicsObj obj)
{
- // check for existing obj?
- ObjectList.Add(obj);
- NumObjects++;
- if (obj.ID == 0 || obj.Parent != null || obj.State.HasFlag(PhysicsState.Hidden) || VoyeurTable == null)
- return;
-
- foreach (var voyeur_id in VoyeurTable)
+ readerWriterLockSlim.EnterWriteLock();
+ try
{
- if (voyeur_id != obj.ID && voyeur_id != 0)
+ // check for existing obj?
+ ObjectList.Add(obj);
+ NumObjects++;
+ if (obj.ID == 0 || obj.Parent != null || obj.State.HasFlag(PhysicsState.Hidden) || VoyeurTable == null)
+ return;
+
+ foreach (var voyeur_id in VoyeurTable)
{
- var voyeur = obj.GetObjectA(voyeur_id);
- if (voyeur == null) continue;
+ if (voyeur_id != obj.ID && voyeur_id != 0)
+ {
+ var voyeur = obj.GetObjectA(voyeur_id);
+ if (voyeur == null) continue;
- var info = new DetectionInfo(obj.ID, DetectionType.EnteredDetection);
- voyeur.receive_detection_update(info);
+ var info = new DetectionInfo(obj.ID, DetectionType.EnteredDetection);
+ voyeur.receive_detection_update(info);
+ }
}
}
+ finally
+ {
+ readerWriterLockSlim.ExitWriteLock();
+ }
}
+
public void AddShadowObject(ShadowObj shadowObj)
{
- ShadowObjectList.Add(shadowObj);
- NumShadowObjects++; // can probably replace with .Count
- shadowObj.Cell = this;
+ readerWriterLockSlim.EnterWriteLock();
+ try
+ {
+ ShadowObjectList.Add(shadowObj);
+ NumShadowObjects++; // can probably replace with .Count
+ shadowObj.Cell = this;
+ }
+ finally
+ {
+ readerWriterLockSlim.ExitWriteLock();
+ }
}
public void CheckAttack(uint attackerID, Position attackerPos, float attackerScale, AttackCone attackCone, AttackInfo attackInfo)
{
- foreach (var shadowObj in ShadowObjectList)
+ readerWriterLockSlim.EnterReadLock();
+ try
{
- var pObj = shadowObj.PhysicsObj;
- if (pObj.ID == attackerID || pObj.State.HasFlag(PhysicsState.Static)) continue;
+ foreach (var shadowObj in ShadowObjectList)
+ {
+ var pObj = shadowObj.PhysicsObj;
+ if (pObj.ID == attackerID || pObj.State.HasFlag(PhysicsState.Static)) continue;
- var hitLocation = pObj.check_attack(attackerPos, attackerScale, attackCone, attackInfo.AttackRadius);
- if (hitLocation != 0)
- attackInfo.AddObject(pObj.ID, hitLocation);
+ var hitLocation = pObj.check_attack(attackerPos, attackerScale, attackCone, attackInfo.AttackRadius);
+ if (hitLocation != 0)
+ attackInfo.AddObject(pObj.ID, hitLocation);
+ }
+ }
+ finally
+ {
+ readerWriterLockSlim.ExitReadLock();
}
}
@@ -100,37 +149,53 @@ public virtual TransitionState FindEnvCollisions(Transition transition)
public TransitionState FindObjCollisions(Transition transition)
{
- var path = transition.SpherePath;
-
- if (path.InsertType == InsertType.InitialPlacement)
- return TransitionState.OK;
-
- var target = transition.ObjectInfo.Object.ProjectileTarget;
-
- // TODO: find out what is causing the exception when .ToList() is not used.
- foreach (var shadowObj in ShadowObjectList.ToList())
+ readerWriterLockSlim.EnterReadLock();
+ try
{
- var obj = shadowObj.PhysicsObj;
+ var path = transition.SpherePath;
- if (obj.Parent != null || obj.Equals(transition.ObjectInfo.Object))
- continue;
+ if (path.InsertType == InsertType.InitialPlacement)
+ return TransitionState.OK;
- // clip through dynamic non-target objects
- if (target != null && !obj.Equals(target) && !obj.State.HasFlag(PhysicsState.Static))
- continue;
+ var target = transition.ObjectInfo.Object.ProjectileTarget;
- var state = obj.FindObjCollisions(transition);
- if (state != TransitionState.OK)
+ // If we use the following: foreach (var shadowObj in ShadowObjectList), an InvalidOperationException is thrown.
+ // Very rarely though, as we iterate through it, the collection will change.
+ // To avoid the InvalidOperationException, we use a for loop.
+ // We do not yet know why the collection changes.
+ for (int i = ShadowObjectList.Count - 1; i >= 0; i--)
{
- // custom: fix hellfire spawn colliding with volcano heat, and possibly other placements
- if (path.InsertType == InsertType.Placement && obj.State.HasFlag(PhysicsState.Ethereal))
+ var shadowObj = ShadowObjectList[i];
+
+ var obj = shadowObj.PhysicsObj;
+
+ if (obj.Parent != null || obj.Equals(transition.ObjectInfo.Object))
continue;
- return state;
+ // clip through dynamic non-target objects
+ // now uses ObjectInfo.TargetId in FindObjCollisions / MissileIgnore
+ //if (target != null && !obj.Equals(target) && /*!obj.State.HasFlag(PhysicsState.Static)*/
+ //obj.WeenieObj.IsCreature())
+ //continue;
+
+ var state = obj.FindObjCollisions(transition);
+ if (state != TransitionState.OK)
+ {
+ // custom: fix hellfire spawn colliding with volcano heat, and possibly other placements
+ if (path.InsertType == InsertType.Placement && (obj.State & PhysicsState.Ethereal) != 0)
+ continue;
+
+ return state;
+ }
+
}
-
+
+ return TransitionState.OK;
+ }
+ finally
+ {
+ readerWriterLockSlim.ExitReadLock();
}
- return TransitionState.OK;
}
public static ObjCell Get(uint cellID)
@@ -146,12 +211,21 @@ public static ObjCell Get(uint cellID)
public PhysicsObj GetObject(int id)
{
- foreach (var obj in ObjectList)
+ readerWriterLockSlim.EnterReadLock();
+ try
+ {
+ foreach (var obj in ObjectList)
+ {
+ if (obj != null && obj.ID == id)
+ return obj;
+ }
+
+ return null;
+ }
+ finally
{
- if (obj != null && obj.ID == id)
- return obj;
+ readerWriterLockSlim.ExitReadLock();
}
- return null;
}
public static ObjCell GetVisible(uint cellID)
@@ -176,33 +250,88 @@ public void Init()
public void RemoveObject(PhysicsObj obj)
{
- ObjectList.Remove(obj);
- NumObjects--;
- update_all_voyeur(obj, DetectionType.LeftDetection);
+ readerWriterLockSlim.EnterWriteLock();
+ try
+ {
+ ObjectList.Remove(obj);
+ NumObjects--;
+ update_all_voyeur(obj, DetectionType.LeftDetection);
+ }
+ finally
+ {
+ readerWriterLockSlim.ExitWriteLock();
+ }
}
public bool check_collisions(PhysicsObj obj)
{
- foreach (var shadowObj in ShadowObjectList)
+ readerWriterLockSlim.EnterReadLock();
+ try
+ {
+ foreach (var shadowObj in ShadowObjectList)
+ {
+ var pObj = shadowObj.PhysicsObj;
+ if (pObj.Parent == null && !pObj.Equals(obj) && pObj.check_collision(obj))
+ return true;
+ }
+
+ return false;
+ }
+ finally
{
- var pObj = shadowObj.PhysicsObj;
- if (pObj.Parent == null && !pObj.Equals(obj) && pObj.check_collision(obj))
- return true;
+ readerWriterLockSlim.ExitReadLock();
}
- return false;
}
public TransitionState check_entry_restrictions(Transition transition)
{
- var objInfo = transition.ObjectInfo;
+ // custom - acclient checks for entry restrictions (housing barriers)
+ // for each tick in the transition, regardless if there is a cell change
- if (objInfo.Object == null) return TransitionState.Collided;
- if (objInfo.Object.WeenieObj == null) return TransitionState.OK;
+ // optimizing for server here, to only check unverified cell changes
+
+ if (!transition.ObjectInfo.Object.IsPlayer || transition.CollisionInfo.VerifiedRestrictions || transition.SpherePath.BeginCell?.ID == ID)
+ {
+ return TransitionState.OK;
+ }
- // check against world object
+ if (transition.ObjectInfo.Object == null)
+ return TransitionState.Collided;
+
+ var weenieObj = transition.ObjectInfo.Object.WeenieObj;
+
+ // TODO: handle DatObject
+ if (weenieObj != null)
+ {
+ //if (transition.ObjectInfo.State.HasFlag(ObjectInfoState.IsPlayer))
+ if (transition.ObjectInfo.Object.IsPlayer)
+ {
+ if (RestrictionObj != 0 && !weenieObj.CanBypassMoveRestrictions())
+ {
+ var restrictionObj = ServerObjectManager.GetObjectA(RestrictionObj);
+
+ if (restrictionObj?.WeenieObj == null)
+ return TransitionState.Collided;
+
+ if (!restrictionObj.WeenieObj.CanMoveInto(weenieObj))
+ {
+ handle_move_restriction(transition);
+ return TransitionState.Collided;
+ }
+ else
+ transition.CollisionInfo.VerifiedRestrictions = true;
+ }
+ }
+ }
return TransitionState.OK;
}
+ public virtual bool handle_move_restriction(Transition transition)
+ {
+ // empty base?
+ return false;
+ }
+
public static void find_cell_list(Position position, int numSphere, List sphere, CellArray cellArray, ref ObjCell currCell, SpherePath path)
{
cellArray.NumCells = 0;
@@ -269,6 +398,9 @@ public static void find_cell_list(Position position, int numSphere, List
foreach (var stab in ((EnvCell)visibleCell).VisibleCells.Values)
{
+ if (stab == null)
+ continue;
+
if (cell.ID == stab.ID)
{
found = true;
@@ -334,8 +466,8 @@ public LandDefs.WaterType get_block_water_type()
{
if (CurLandblock != null)
return CurLandblock.WaterType;
- else
- return LandDefs.WaterType.NotWater;
+
+ return LandDefs.WaterType.NotWater;
}
public float get_water_depth(Vector3 point)
@@ -348,8 +480,8 @@ public float get_water_depth(Vector3 point)
if (CurLandblock != null)
return CurLandblock.calc_water_depth(ID, point);
- else
- return 0.1f;
+
+ return 0.1f;
}
public void hide_object(PhysicsObj obj)
@@ -359,9 +491,17 @@ public void hide_object(PhysicsObj obj)
public void init_objects()
{
- foreach (var obj in ObjectList)
- if (!obj.State.HasFlag(PhysicsState.Static) && !obj.is_completely_visible())
- obj.recalc_cross_cells();
+ readerWriterLockSlim.EnterReadLock();
+ try
+ {
+ foreach (var obj in ObjectList)
+ if (!obj.State.HasFlag(PhysicsState.Static) && !obj.is_completely_visible())
+ obj.recalc_cross_cells();
+ }
+ finally
+ {
+ readerWriterLockSlim.ExitReadLock();
+ }
}
public virtual bool point_in_cell(Vector3 point)
@@ -369,25 +509,48 @@ public virtual bool point_in_cell(Vector3 point)
return false;
}
+ public void release_shadow_objs()
+ {
+ foreach (var shadowObj in ShadowObjectList)
+ shadowObj.PhysicsObj.ShadowObjects.Remove(ID);
+ }
+
public void release_objects()
{
- while (NumShadowObjects > 0)
+ readerWriterLockSlim.EnterWriteLock();
+ try
{
- var shadowObj = ShadowObjectList[0];
- remove_shadow_object(shadowObj);
+ while (NumShadowObjects > 0)
+ {
+ var shadowObj = ShadowObjectList[0];
+ remove_shadow_object(shadowObj);
- shadowObj.PhysicsObj.remove_parts(this);
- }
- //if (NumObjects > 0 && ObjMaint != null)
+ shadowObj.PhysicsObj.remove_parts(this);
+ }
+
+ //if (NumObjects > 0 && ObjMaint != null)
//ObjMaint.ReleaseObjCell(this);
+ }
+ finally
+ {
+ readerWriterLockSlim.ExitWriteLock();
+ }
}
public void remove_shadow_object(ShadowObj shadowObj)
{
- // multiple shadows?
- ShadowObjectList.Remove(shadowObj);
- shadowObj.Cell = null;
- NumShadowObjects--;
+ readerWriterLockSlim.EnterWriteLock();
+ try
+ {
+ // multiple shadows?
+ ShadowObjectList.Remove(shadowObj);
+ shadowObj.Cell = null;
+ NumShadowObjects--;
+ }
+ finally
+ {
+ readerWriterLockSlim.ExitWriteLock();
+ }
}
public void unhide_object(PhysicsObj obj)
@@ -415,5 +578,53 @@ public void update_all_voyeur(PhysicsObj obj, DetectionType type, bool checkDete
}
}
}
+
+ public bool IsVisible(ObjCell cell)
+ {
+ if (ID == cell.ID) return true;
+
+ if ((ID & 0xFFFF) >= 0x100)
+ {
+ if (!(this is EnvCell envCell))
+ {
+ Console.WriteLine($"{ID:X8}.IsVisible({cell.ID:X8}): {ID:X8} not detected as EnvCell");
+ return false;
+ }
+ return envCell.IsVisibleIndoors(cell);
+ }
+ else if ((cell.ID & 0xFFFF) >= 0x100)
+ {
+ if (!(cell is EnvCell envCell))
+ {
+ Console.WriteLine($"{ID:X8}.IsVisible({cell.ID:X8}): {cell.ID:X8} not detected as EnvCell");
+ return false;
+ }
+ return envCell.IsVisibleIndoors(this);
+ }
+ else
+ {
+ // outdoors
+ return IsVisibleOutdoors(cell);
+ }
+ }
+
+ public bool IsVisibleOutdoors(ObjCell cell)
+ {
+ var blockDist = PhysicsObj.GetBlockDist(ID, cell.ID);
+ return blockDist <= 1;
+ }
+
+ public void AddObjectListTo(List target)
+ {
+ readerWriterLockSlim.EnterReadLock();
+ try
+ {
+ target.AddRange(ObjectList);
+ }
+ finally
+ {
+ readerWriterLockSlim.ExitReadLock();
+ }
+ }
}
}
diff --git a/ACViewer/Physics/Common/ObjectDesc.cs b/ACViewer/Physics/Common/ObjectDesc.cs
index cff36b7..0b91c74 100644
--- a/ACViewer/Physics/Common/ObjectDesc.cs
+++ b/ACViewer/Physics/Common/ObjectDesc.cs
@@ -1,4 +1,4 @@
-using System;
+using System;
using System.Numerics;
using ACE.Server.Physics.Animation;
using ACE.Server.Physics.Extensions;
@@ -75,7 +75,7 @@ public static float ScaleObj(DatLoader.Entity.ObjectDesc obj, uint x, uint y, ui
public static AFrame RotateObj(DatLoader.Entity.ObjectDesc obj, uint x, uint y, uint k, Vector3 loc)
{
var frame = new AFrame(obj.BaseLoc);
- frame.Origin = new Vector3(loc.X, loc.Y, loc.Z);
+ frame.Origin = loc;
if (obj.MaxRotation > 0.0f)
{
var degrees = (float)((1813693831 * y - (k + 63127) * (1360117743 * y * x + 1888038839) - 1109124029 * x) * 2.3283064e-10 * obj.MaxRotation);
@@ -90,7 +90,7 @@ public static AFrame RotateObj(DatLoader.Entity.ObjectDesc obj, uint x, uint y,
public static AFrame ObjAlign(DatLoader.Entity.ObjectDesc obj, Plane plane, Vector3 loc)
{
var frame = new AFrame(obj.BaseLoc);
- frame.Origin = new Vector3(loc.X, loc.Y, loc.Z);
+ frame.Origin = loc;
var negNormal = -plane.Normal;
var degrees = negNormal.get_heading();
frame.set_heading(degrees);
diff --git a/ACViewer/Physics/Common/ObjectMaint.cs b/ACViewer/Physics/Common/ObjectMaint.cs
index 916a085..504cef8 100644
--- a/ACViewer/Physics/Common/ObjectMaint.cs
+++ b/ACViewer/Physics/Common/ObjectMaint.cs
@@ -1,7 +1,11 @@
using System;
using System.Collections.Generic;
using System.Linq;
-using System.Numerics;
+using System.Threading;
+
+using ACE.Entity.Enum;
+using ACE.Server.Physics.Managers;
+using ACE.Server.WorldObjects;
namespace ACE.Server.Physics.Common
{
@@ -12,30 +16,20 @@ namespace ACE.Server.Physics.Common
public class ObjectMaint
{
///
- /// Objects are removed from the client after this amount of time
+ /// The client automatically removes known objects if they remain outside visibility for this amount of time
///
public static readonly float DestructionTime = 25.0f;
- ///
- /// The distance the server sends objects to players
- /// for non-dungeon landblocks
- ///
- public static readonly float RadiusOutside = 192.0f;
-
- ///
- /// The cell radius to send objects to players
- /// for non-dungeon landblocks (8 cell radius by default)
- ///
- public static readonly float CellRadiusOutside = RadiusOutside / LandDefs.CellLength;
+ private static readonly ReaderWriterLockSlim rwLock = new ReaderWriterLockSlim(LockRecursionPolicy.SupportsRecursion);
///
/// The owner of this ObjectMaint instance
/// This is who we are tracking object visibility for, ie. a Player
///
- public PhysicsObj PhysicsObj;
+ public PhysicsObj PhysicsObj { get; }
///
- /// This list of objects that are known to the client
+ /// This list of objects that are known to a player
///
///
///
@@ -46,30 +40,44 @@ public class ObjectMaint
/// it is removed from the VisibleObject list, and added to the destruction queue.
/// if it remains outside PVS for DestructionTime, the client automatically culls the object,
/// and it is removed from this list of known objects.
- ///
+ ///
+ /// - This is only maintained for players.
///
- public Dictionary ObjectTable;
+ private Dictionary KnownObjects { get; } = new Dictionary();
///
/// This list of objects that are currently within PVS / VisibleCell range
+ /// only maintained for players
///
- public Dictionary VisibleObjectTable;
+ private Dictionary VisibleObjects { get; } = new Dictionary();
///
- /// A list of objects that currently know about this object
+ /// Objects that were previously visible to the client,
+ /// but have been outside the PVS for less than 25 seconds
+ /// only maintained for players
///
- public Dictionary VoyeurTable;
+ private Dictionary DestructionQueue { get; } = new Dictionary();
///
- /// Objects that were previously visible to the client,
- /// but have been outside the PVS for less than 25 seconds
+ /// A list of players that currently know about this object
+ /// This is maintained for all server-spawned WorldObjects, and is used for broadcasting
+ ///
+ private Dictionary KnownPlayers { get; } = new Dictionary();
+
+ ///
+ /// For monster and CombatPet FindNextTarget
+ /// - for monsters, contains players and combat pets
+ /// - for combat pets, contains monsters
///
- public Dictionary DestructionQueue;
+ private Dictionary VisibleTargets { get; } = new Dictionary();
///
- /// Custom lookup table of PhysicsObjs for the server
+ /// Handles monsters targeting things they would not normally target
+ /// - For faction mobs, retaliate against same-faction players and combat pets
+ /// - For regular monsters, retaliate against faction mobs
+ /// - For regular monsters that do *not* have a FoeType, retaliate against monsters that are foes with this creature
///
- public static Dictionary ServerObjects;
+ private Dictionary RetaliateTargets { get; } = new Dictionary();
// Client structures -
// When client unloads a cell/landblock, but still knows about objects in those cells?
@@ -78,383 +86,1036 @@ public class ObjectMaint
//public Dictionary WeenieObjectTable;
//public List NullWeenieObjectTable;
- static ObjectMaint()
- {
- ServerObjects = new Dictionary();
- }
-
public ObjectMaint() { }
public ObjectMaint(PhysicsObj obj)
{
PhysicsObj = obj;
- ObjectTable = new Dictionary();
- VisibleObjectTable = new Dictionary();
- VoyeurTable = new Dictionary();
- DestructionQueue = new Dictionary();
+ }
+
+
+ public PhysicsObj GetKnownObject(uint objectGuid)
+ {
+ rwLock.EnterReadLock();
+ try
+ {
+ KnownObjects.TryGetValue(objectGuid, out var obj);
+ return obj;
+ }
+ finally
+ {
+ rwLock.ExitReadLock();
+ }
+ }
+
+ public int GetKnownObjectsCount()
+ {
+ rwLock.EnterReadLock();
+ try
+ {
+ return KnownObjects.Count;
+ }
+ finally
+ {
+ rwLock.ExitReadLock();
+ }
+ }
+
+ public bool KnownObjectsContainsKey(uint guid)
+ {
+ rwLock.EnterReadLock();
+ try
+ {
+ return KnownObjects.ContainsKey(guid);
+ }
+ finally
+ {
+ rwLock.ExitReadLock();
+ }
+ }
+
+ public bool KnownObjectsContainsValue(PhysicsObj value)
+ {
+ rwLock.EnterReadLock();
+ try
+ {
+ return KnownObjects.Values.Contains(value);
+ }
+ finally
+ {
+ rwLock.ExitReadLock();
+ }
+ }
+
+ public List> GetKnownObjectsWhere(Func, bool> predicate)
+ {
+ rwLock.EnterReadLock();
+ try
+ {
+ return KnownObjects.Where(predicate).ToList();
+ }
+ finally
+ {
+ rwLock.ExitReadLock();
+ }
+ }
+
+ public List GetKnownObjectsValues()
+ {
+ rwLock.EnterReadLock();
+ try
+ {
+ return KnownObjects.Values.ToList();
+ }
+ finally
+ {
+ rwLock.ExitReadLock();
+ }
+ }
+
+ public List GetKnownObjectsValuesWhere(Func predicate)
+ {
+ rwLock.EnterReadLock();
+ try
+ {
+ return KnownObjects.Values.Where(predicate).ToList();
+ }
+ finally
+ {
+ rwLock.ExitReadLock();
+ }
}
///
/// Adds an object to the list of known objects
+ /// only maintained for players
///
/// true if previously an unknown object
- public bool AddObject(PhysicsObj obj)
+ public bool AddKnownObject(PhysicsObj obj)
{
- if (!ObjectTable.ContainsKey(obj.ID))
+ rwLock.EnterWriteLock();
+ try
{
- ObjectTable.Add(obj.ID, obj);
+ if (KnownObjects.ContainsKey(obj.ID))
+ return false;
+
+ KnownObjects.Add(obj.ID, obj);
+
+ // maintain KnownPlayers for both parties
+ if (obj.IsPlayer)
+ AddKnownPlayer(obj);
- // add to target object's voyeurs
- obj.ObjMaint.AddVoyeur(PhysicsObj);
+ obj.ObjMaint.AddKnownPlayer(PhysicsObj);
return true;
}
- return false;
+ finally
+ {
+ rwLock.ExitWriteLock();
+ }
}
///
/// Adds a list of objects to the known objects list
+ /// only maintained for players
///
- /// A list of newly visible objects
+ /// A list of currently visible objects
/// The list of visible objects that were previously unknown
- public List AddObjects(List objs)
+ private List AddKnownObjects(IEnumerable objs)
{
var newObjs = new List();
foreach (var obj in objs)
- if (AddObject(obj)) newObjs.Add(obj);
+ {
+ if (AddKnownObject(obj))
+ newObjs.Add(obj);
+ }
return newObjs;
}
- ///
- /// Adds an object to the list of visible objects
- ///
- public bool AddVisibleObject(PhysicsObj obj)
+ public bool RemoveKnownObject(PhysicsObj obj, bool inversePlayer = true)
{
- if (!VisibleObjectTable.ContainsKey(obj.ID))
+ rwLock.EnterWriteLock();
+ try
{
- VisibleObjectTable.Add(obj.ID, obj);
- return true;
+ var result = KnownObjects.Remove(obj.ID);
+
+ if (inversePlayer && PhysicsObj.IsPlayer)
+ obj.ObjMaint.RemoveKnownPlayer(PhysicsObj);
+
+ return result;
+ }
+ finally
+ {
+ rwLock.ExitWriteLock();
}
- return false;
}
- ///
- /// Add a list of visible objects - maintains both known and visible objects
- ///
- public List AddVisibleObjects(List objs)
+
+ public int GetVisibleObjectsCount()
{
- foreach (var obj in objs)
- AddVisibleObject(obj);
+ rwLock.EnterReadLock();
+ try
+ {
+ return VisibleObjects.Count;
+ }
+ finally
+ {
+ rwLock.ExitReadLock();
+ }
+ }
- RemoveObjectsToBeDestroyed(objs);
+ public bool VisibleObjectsContainsKey(uint key)
+ {
+ rwLock.EnterReadLock();
+ try
+ {
+ return VisibleObjects.ContainsKey(key);
+ }
+ finally
+ {
+ rwLock.ExitReadLock();
+ }
+ }
- return AddObjects(objs);
+ public List> GetVisibleObjectsWhere(Func, bool> predicate)
+ {
+ rwLock.EnterReadLock();
+ try
+ {
+ return VisibleObjects.Where(predicate).ToList();
+ }
+ finally
+ {
+ rwLock.ExitReadLock();
+ }
}
- ///
- /// Adds an object to the destruction queue
- /// Called when an object exits the PVS range
- ///
- public bool AddObjectToBeDestroyed(PhysicsObj obj)
+ public List GetVisibleObjectsValues()
{
- var time = PhysicsTimer.CurrentTime + DestructionTime;
- if (!DestructionQueue.ContainsKey(obj))
+ rwLock.EnterReadLock();
+ try
{
- DestructionQueue.Add(obj, time);
- return true;
+ return VisibleObjects.Values.ToList();
+ }
+ finally
+ {
+ rwLock.ExitReadLock();
+ }
+ }
+
+ public List GetVisibleObjectsValuesWhere(Func predicate)
+ {
+ rwLock.EnterReadLock();
+ try
+ {
+ return VisibleObjects.Values.Where(predicate).ToList();
+ }
+ finally
+ {
+ rwLock.ExitReadLock();
}
- return false;
}
+ /*public List GetVisibleObjectsValuesOfTypeCreature()
+ {
+ rwLock.EnterReadLock();
+ try
+ {
+ return VisibleObjects.Values.Select(v => v.WeenieObj.WorldObject).OfType().ToList();
+ }
+ finally
+ {
+ rwLock.ExitReadLock();
+ }
+ }*/
+
+ private static readonly List emptyList = new List();
+
///
- /// Adds a list of objects to the destruction queue
+ /// Returns a list of objects that are currently visible from a cell
///
- public List AddObjectsToBeDestroyed(List objs)
+ public List GetVisibleObjects(ObjCell cell, VisibleObjectType type = VisibleObjectType.All)
{
- var queued = new List();
- foreach (var obj in objs)
+ rwLock.EnterReadLock();
+ try
{
- if (AddObjectToBeDestroyed(obj))
- queued.Add(obj);
+ if (PhysicsObj.CurLandblock == null || cell == null)
+ return new List();
+
+ // use PVS / VisibleCells for EnvCells not seen outside
+ // (mostly dungeons, also some large indoor areas ie. caves)
+ if (cell is EnvCell envCell)
+ return GetVisibleObjects(envCell, type);
+
+ // use current landblock + adjacents for outdoors,
+ // and envcells seen from outside (all buildings)
+ //var visibleObjs = PhysicsObj.CurLandblock.GetServerObjects(true);
+ var visibleObjs = emptyList;
- VisibleObjectTable.Remove(obj.ID);
+ return ApplyFilter(visibleObjs, type).Where(i => i.ID != PhysicsObj.ID && (!(i.CurCell is EnvCell indoors) || indoors.SeenOutside)).ToList();
+ }
+ finally
+ {
+ rwLock.ExitReadLock();
}
- return queued;
}
///
- /// Returns a list of objects that have been in the destruction queue
- /// for less than 25 seconds
+ /// Returns a list of objects that are currently visible from a dungeon cell
///
- public List GetCulledObjects(List visibleObjects)
+ private List GetVisibleObjects(EnvCell cell, VisibleObjectType type)
{
- var culledObjects = DestructionQueue.Where(kvp => kvp.Value > PhysicsTimer.CurrentTime).ToDictionary(kvp => kvp.Key, kvp => kvp.Value).Keys.ToList();
- return culledObjects;
+ var visibleObjs = new List();
+
+ // add objects from current cell
+ cell.AddObjectListTo(visibleObjs);
+
+ // add objects from visible cells
+ foreach (var envCell in cell.VisibleCells.Values)
+ {
+ if (envCell != null)
+ envCell.AddObjectListTo(visibleObjs);
+ }
+
+ // if SeenOutside, add objects from outdoor landblock
+ if (cell.SeenOutside)
+ {
+ //var outsideObjs = PhysicsObj.CurLandblock.GetServerObjects(true).Where(i => !(i.CurCell is EnvCell indoors) || indoors.SeenOutside);
+ var outsideObjs = emptyList;
+
+ visibleObjs.AddRange(outsideObjs);
+ }
+
+ return ApplyFilter(visibleObjs, type).Where(i => !i.DatObject && i.ID != PhysicsObj.ID).Distinct().ToList();
}
- ///
- /// Returns a list of objects that have been in the destruction queue
- /// for more than 25 seconds
- ///
- public List GetDestroyedObjects()
+ public enum VisibleObjectType
{
- var destroyedObjects = DestructionQueue.Where(kvp => kvp.Value <= PhysicsTimer.CurrentTime).ToDictionary(kvp => kvp.Key, kvp => kvp.Value).Keys.ToList();
- return destroyedObjects;
+ All,
+ Players,
+ AttackTargets
+ };
+
+ private IEnumerable ApplyFilter(List objs, VisibleObjectType type)
+ {
+ IEnumerable results = objs;
+
+ if (type == VisibleObjectType.Players)
+ {
+ results = objs.Where(i => i.IsPlayer);
+ }
+ else if (type == VisibleObjectType.AttackTargets)
+ {
+ if (PhysicsObj.WeenieObj.IsCombatPet)
+ results = objs.Where(i => i.WeenieObj.IsMonster && i.WeenieObj.PlayerKillerStatus != PlayerKillerStatus.PK); // combat pets cannot attack pk-only creatures (ie. faction banners)
+ else if (PhysicsObj.WeenieObj.IsFactionMob)
+ results = objs.Where(i => i.IsPlayer || i.WeenieObj.IsCombatPet || i.WeenieObj.IsMonster && !i.WeenieObj.SameFaction(PhysicsObj));
+ else
+ {
+ // adding faction mobs here, even though they are retaliate-only, for inverse visible targets
+ results = objs.Where(i => i.IsPlayer || i.WeenieObj.IsCombatPet && PhysicsObj.WeenieObj.PlayerKillerStatus != PlayerKillerStatus.PK || i.WeenieObj.IsFactionMob || i.WeenieObj.PotentialFoe(PhysicsObj));
+ }
+ }
+ return results;
}
+ public static bool InitialClamp = true;
+
+ public static float InitialClamp_Dist = 112.5f;
+ public static float InitialClamp_DistSq = InitialClamp_Dist * InitialClamp_Dist;
+
///
- /// Returns a PhysicsObj for an object ID
+ /// Adds an object to the list of visible objects
+ /// only maintained for players
///
- public static PhysicsObj GetObjectA(uint objectID)
+ /// TRUE if object was previously not visible, and added to the visible list
+ public bool AddVisibleObject(PhysicsObj obj)
{
- ServerObjects.TryGetValue(objectID, out var obj);
- return obj;
+ rwLock.EnterWriteLock();
+ try
+ {
+ if (VisibleObjects.ContainsKey(obj.ID))
+ return false;
+
+ if (InitialClamp && !KnownObjects.ContainsKey(obj.ID))
+ {
+ var distSq = PhysicsObj.Position.Distance2DSquared(obj.Position);
+
+ if (distSq > InitialClamp_DistSq)
+ return false;
+ }
+
+ //Console.WriteLine($"{PhysicsObj.Name}.AddVisibleObject({obj.Name})");
+ VisibleObjects.Add(obj.ID, obj);
+
+ if (obj.WeenieObj.IsMonster)
+ obj.ObjMaint.AddVisibleTarget(PhysicsObj, false);
+
+ return true;
+ }
+ finally
+ {
+ rwLock.ExitWriteLock();
+ }
}
///
- /// Returns a list of outdoor cells within visible range of player
+ /// Add a list of visible objects - maintains both known and visible objects
+ /// only maintained for players
///
- public List GetOutdoorCells(ObjCell cell)
+ public List AddVisibleObjects(ICollection objs)
{
- // get cell x/y global offset
- var lcoord = LandDefs.get_outside_lcoord(cell.ID, PhysicsObj.Position.Frame.Origin.X, PhysicsObj.Position.Frame.Origin.Y).Value;
+ rwLock.EnterWriteLock();
+ try
+ {
+ var visibleAdded = new List();
- // includes the origin cell
- var blockLength = (int)CellRadiusOutside * 2 + 1;
- var cells = new List(/*blockLength * blockLength*/);
+ foreach (var obj in objs)
+ {
+ if (AddVisibleObject(obj))
+ visibleAdded.Add(obj);
+ }
- var start = new Vector2(lcoord.X - CellRadiusOutside, lcoord.Y - CellRadiusOutside);
- var end = new Vector2(lcoord.X + CellRadiusOutside, lcoord.Y + CellRadiusOutside);
+ RemoveObjectsToBeDestroyed(objs);
- for (var cellX = start.X; cellX <= end.X; cellX++)
+ return AddKnownObjects(visibleAdded);
+ }
+ finally
{
- for (var cellY = start.Y; cellY <= end.Y; cellY++)
- {
- var blockCellID = LandDefs.lcoord_to_gid(cellX, cellY);
- var _cell = LScape.get_landcell((uint)blockCellID);
- if (_cell == null)
- continue;
- cells.Add(_cell);
-
- // does this outdoor cell contain a building?
- // if so, add all of its cells
- var landCell = _cell as LandCell;
- if (landCell.has_building())
- {
- //Console.WriteLine($"Found building in cell {landCell.ID:X8}");
- var buildingCells = landCell.Building.get_building_cells();
- //Console.WriteLine("# cells: " + buildingCells.Count);
- cells.AddRange(buildingCells);
- }
- }
+ rwLock.ExitWriteLock();
}
- return cells;
}
///
- /// Returns a list of objects that are currently visible from a cell
- /// in an outdoor landblock
+ /// Removes an object from the visible objects list
+ /// only run for players
+ /// and also removes the player from the object's visible targets list
///
- public List GetVisibleObjects(ObjCell cell)
+ public bool RemoveVisibleObject(PhysicsObj obj, bool inverseTarget = true)
{
- if (PhysicsObj.CurLandblock == null || cell == null) return new List();
+ rwLock.EnterWriteLock();
+ try
+ {
+ var removed = VisibleObjects.Remove(obj.ID);
- // use PVS / VisibleCells for EnvCells not seen outside
- // (mostly dungeons, also some large indoor areas ie. caves)
- var envCell = cell as EnvCell;
- if (envCell != null && !envCell.SeenOutside)
- return GetVisibleObjects(envCell);
+ if (inverseTarget)
+ obj.ObjMaint.RemoveVisibleTarget(PhysicsObj);
+
+ return removed;
+ }
+ finally
+ {
+ rwLock.ExitWriteLock();
+ }
+ }
- // use current landblock + adjacents for outdoors,
- // and envcells seen from outside (all buildings)
- var visibleObjs = new List(PhysicsObj.CurLandblock.ServerObjects);
- var adjacents = PhysicsObj.CurLandblock.get_adjacents();
+ public int GetDestructionQueueCount()
+ {
+ rwLock.EnterReadLock();
+ try
+ {
+ return DestructionQueue.Count;
+ }
+ finally
+ {
+ rwLock.ExitReadLock();
+ }
+ }
- foreach (var adjacent in adjacents)
- visibleObjs.AddRange(adjacent.ServerObjects);
+ public Dictionary GetDestructionQueueCopy()
+ {
+ rwLock.EnterReadLock();
+ try
+ {
+ var result = new Dictionary();
- return visibleObjs.Where(i => i.ID != PhysicsObj.ID).ToList();
+ foreach (var kvp in DestructionQueue)
+ result[kvp.Key] = kvp.Value;
- /*var cells = GetOutdoorCells(cell);
+ return result;
+ }
+ finally
+ {
+ rwLock.ExitReadLock();
+ }
+ }
- var visibleObjs = new List();
+ ///
+ /// Adds an object to the destruction queue
+ /// Called when an object exits the PVS range
+ /// only maintained for players
+ ///
+ public bool AddObjectToBeDestroyed(PhysicsObj obj)
+ {
+ rwLock.EnterWriteLock();
+ try
+ {
+ RemoveVisibleObject(obj);
- foreach (var _cell in cells)
- visibleObjs.AddRange(_cell.ObjectList);
+ if (DestructionQueue.ContainsKey(obj))
+ return false;
- return visibleObjs.Where(obj => !obj.DatObject).Distinct().ToList();*/
+ DestructionQueue.Add(obj, PhysicsTimer.CurrentTime + DestructionTime);
+ return true;
+ }
+ finally
+ {
+ rwLock.ExitWriteLock();
+ }
}
///
- /// Returns a list of objects that are currently visible from a dungeon cell
+ /// Adds a list of objects to the destruction queue
+ /// only maintained for players
///
- public List GetVisibleObjects(EnvCell cell)
+ public List AddObjectsToBeDestroyed(List objs)
{
- var visibleObjs = new List();
-
- // add objects from current cell
- visibleObjs.AddRange(cell.ObjectList);
-
- // add objects from visible cells
- foreach (var envCell in cell.VisibleCells.Values)
+ rwLock.EnterWriteLock();
+ try
{
- if (envCell == null) continue;
+ var queued = new List();
+ foreach (var obj in objs)
+ {
+ if (AddObjectToBeDestroyed(obj))
+ queued.Add(obj);
+ }
- visibleObjs.AddRange(envCell.ObjectList);
+ return queued;
+ }
+ finally
+ {
+ rwLock.ExitWriteLock();
}
-
- return visibleObjs.Where(i => !i.DatObject && i.ID != PhysicsObj.ID).Distinct().ToList();
}
///
- /// Removes an object from the destruction queue
- /// if it has been invisible for less than 25s
+ /// Removes an object from the destruction queue if it has been invisible for less than 25s
+ /// this is only used for players
///
public bool RemoveObjectToBeDestroyed(PhysicsObj obj)
{
- double time = -1;
- DestructionQueue.TryGetValue(obj, out time);
- if (time != -1 && time > PhysicsTimer.CurrentTime)
+ rwLock.EnterWriteLock();
+ try
{
- DestructionQueue.Remove(obj);
- return true;
+ double time = -1;
+ DestructionQueue.TryGetValue(obj, out time);
+ if (time != -1 && time > PhysicsTimer.CurrentTime)
+ {
+ DestructionQueue.Remove(obj);
+ return true;
+ }
+
+ return false;
+ }
+ finally
+ {
+ rwLock.ExitWriteLock();
}
- return false;
}
///
- /// Removes objects from the destruction queue
- /// when they re-enter visibility within 25s
+ /// Removes objects from the destruction queue when they re-enter visibility within 25s
+ /// this is only used for players
///
- public void RemoveObjectsToBeDestroyed(List objs)
+ private void RemoveObjectsToBeDestroyed(IEnumerable objs)
{
foreach (var obj in objs)
RemoveObjectToBeDestroyed(obj);
}
///
- /// Removes any objects that have been in the destruction queue
- /// for more than 25s
+ /// Removes any objects that have been in the destruction queue for more than 25s
+ /// this is only used for players
///
- ///
public List DestroyObjects()
{
- // find the list of objects that have been in the destruction queue > 25s
- var expiredObjs = DestructionQueue.Where(kvp => kvp.Value <= PhysicsTimer.CurrentTime).ToDictionary(kvp => kvp.Key, kvp => kvp.Value).Keys.ToList();
+ rwLock.EnterWriteLock();
+ try
+ {
+ // find the list of objects that have been in the destruction queue > 25s
+ var expiredObjs = DestructionQueue.Where(kvp => kvp.Value <= PhysicsTimer.CurrentTime)
+ .Select(kvp => kvp.Key).ToList();
- // remove expired objects from all lists
- foreach (var expiredObj in expiredObjs)
- RemoveObject(expiredObj);
+ // remove expired objects from all lists
+ foreach (var expiredObj in expiredObjs)
+ RemoveObject(expiredObj);
- return expiredObjs;
+ return expiredObjs;
+ }
+ finally
+ {
+ rwLock.ExitWriteLock();
+ }
}
///
- /// Removes an object from all of the tables
+ /// Removes an object after it has expired from the destruction queue, or it has been destroyed
///
- public void RemoveObject(PhysicsObj obj)
+ public void RemoveObject(PhysicsObj obj, bool inverse = true)
{
- if (obj == null) return;
+ rwLock.EnterWriteLock();
+ try
+ {
+ if (obj == null) return;
+
+ RemoveKnownObject(obj, inverse);
+ RemoveVisibleObject(obj, inverse);
+ DestructionQueue.Remove(obj);
- ObjectTable.Remove(obj.ID);
- VisibleObjectTable.Remove(obj.ID);
- DestructionQueue.Remove(obj);
- VoyeurTable.Remove(obj.ID);
+ if (obj.IsPlayer)
+ RemoveKnownPlayer(obj);
- obj.ObjMaint.RemoveVoyeur(PhysicsObj);
+ RemoveVisibleTarget(obj);
+ RemoveRetaliateTarget(obj);
+ }
+ finally
+ {
+ rwLock.ExitWriteLock();
+ }
}
+
+ public int GetKnownPlayersCount()
+ {
+ rwLock.EnterReadLock();
+ try
+ {
+ return KnownPlayers.Count;
+ }
+ finally
+ {
+ rwLock.ExitReadLock();
+ }
+ }
+
+ public List> GetKnownPlayersWhere(Func, bool> predicate)
+ {
+ rwLock.EnterReadLock();
+ try
+ {
+ return KnownPlayers.Where(predicate).ToList();
+ }
+ finally
+ {
+ rwLock.ExitReadLock();
+ }
+ }
+
+ public List GetKnownPlayersValues()
+ {
+ rwLock.EnterReadLock();
+ try
+ {
+ return KnownPlayers.Values.ToList();
+ }
+ finally
+ {
+ rwLock.ExitReadLock();
+ }
+ }
+
+ /*public List GetKnownPlayersValuesAsPlayer()
+ {
+ rwLock.EnterReadLock();
+ try
+ {
+ return KnownPlayers.Values.Select(v => v.WeenieObj.WorldObject).OfType().ToList();
+ }
+ finally
+ {
+ rwLock.ExitReadLock();
+ }
+ }
+
+ public List GetKnownObjectsValuesAsCreature()
+ {
+ rwLock.EnterReadLock();
+ try
+ {
+ return KnownObjects.Values.Select(v => v.WeenieObj.WorldObject).OfType().ToList();
+ }
+ finally
+ {
+ rwLock.ExitReadLock();
+ }
+ }*/
+
///
- /// Clears all of the ObjMaint tables for an object
+ /// Adds a player who currently knows about this object
+ /// - this is maintained for all server-spawned objects
+ ///
+ /// when an object broadcasts, it sends network messages
+ /// to all players who currently know about this object
///
- public void RemoveAllObjects()
+ /// true if previously an unknown object
+ private bool AddKnownPlayer(PhysicsObj obj)
{
- foreach (var obj in ObjectTable.Values)
- obj.ObjMaint.RemoveVoyeur(PhysicsObj);
+ // only tracking players who know about this object
+ if (!obj.IsPlayer)
+ {
+ Console.WriteLine($"{PhysicsObj.Name}.ObjectMaint.AddKnownPlayer({obj.Name}): tried to add a non-player");
+ return false;
+ }
+ if (PhysicsObj.DatObject)
+ {
+ Console.WriteLine($"{PhysicsObj.Name}.ObjectMaint.AddKnownPlayer({obj.Name}): tried to add player for dat object");
+ return false;
+ }
- ObjectTable.Clear();
- VisibleObjectTable.Clear();
- DestructionQueue.Clear();
+ //Console.WriteLine($"{PhysicsObj.Name} ({PhysicsObj.ID:X8}).ObjectMaint.AddKnownPlayer({obj.Name})");
+
+ // TryAdd for existing keys still modifies collection?
+ if (KnownPlayers.ContainsKey(obj.ID))
+ return false;
+
+ KnownPlayers.Add(obj.ID, obj);
+ return true;
}
///
- /// Adds a PhysicsObj to the static list of server-wide objects
+ /// Adds a list of players known to this object
///
- public static void AddServerObject(PhysicsObj obj)
+ public List AddKnownPlayers(IEnumerable objs)
{
- if (obj == null) return;
+ rwLock.EnterWriteLock();
+ try
+ {
+ var newObjs = new List();
- if (ServerObjects.ContainsKey(obj.ID))
- ServerObjects[obj.ID] = obj;
- else
- ServerObjects.Add(obj.ID, obj);
+ foreach (var obj in objs)
+ {
+ if (AddKnownPlayer(obj))
+ newObjs.Add(obj);
+ }
+
+ return newObjs;
+ }
+ finally
+ {
+ rwLock.ExitWriteLock();
+ }
}
///
- /// Removes a PhysicsObj from the static list of server-wide objects
+ /// Removes a known player for this object
///
- public static void RemoveServerObject(PhysicsObj obj)
+ public bool RemoveKnownPlayer(PhysicsObj obj)
{
- if (obj == null) return;
+ //Console.WriteLine($"{PhysicsObj.Name} ({PhysicsObj.ID:X8}).ObjectMaint.RemoveKnownPlayer({obj.Name})");
- if (ServerObjects.ContainsKey(obj.ID))
- ServerObjects.Remove(obj.ID);
+ rwLock.EnterReadLock();
+ try
+ {
+ return KnownPlayers.Remove(obj.ID);
+ }
+ finally
+ {
+ rwLock.ExitReadLock();
+ }
+ }
+
+
+ public int GetVisibleTargetsCount()
+ {
+ rwLock.EnterReadLock();
+ try
+ {
+ return VisibleTargets.Count;
+ }
+ finally
+ {
+ rwLock.ExitReadLock();
+ }
}
+ public int GetRetaliateTargetsCount()
+ {
+ rwLock.EnterReadLock();
+ try
+ {
+ return RetaliateTargets.Count;
+ }
+ finally
+ {
+ rwLock.ExitReadLock();
+ }
+ }
+
+ public bool VisibleTargetsContainsKey(uint key)
+ {
+ rwLock.EnterReadLock();
+ try
+ {
+ return VisibleTargets.ContainsKey(key);
+ }
+ finally
+ {
+ rwLock.ExitReadLock();
+ }
+ }
+
+ public bool RetaliateTargetsContainsKey(uint key)
+ {
+ rwLock.EnterReadLock();
+ try
+ {
+ return RetaliateTargets.ContainsKey(key);
+ }
+ finally
+ {
+ rwLock.ExitReadLock();
+ }
+ }
+
+ public List GetVisibleTargetsValues()
+ {
+ rwLock.EnterReadLock();
+ try
+ {
+ return VisibleTargets.Values.ToList();
+ }
+ finally
+ {
+ rwLock.ExitReadLock();
+ }
+ }
+
+ public List GetRetaliateTargetsValues()
+ {
+ rwLock.EnterReadLock();
+ try
+ {
+ return RetaliateTargets.Values.ToList();
+ }
+ finally
+ {
+ rwLock.ExitReadLock();
+ }
+ }
+
+ /*public List GetVisibleTargetsValuesOfTypeCreature()
+ {
+ rwLock.EnterReadLock();
+ try
+ {
+ return VisibleTargets.Values.Select(v => v.WeenieObj.WorldObject).OfType().ToList();
+ }
+ finally
+ {
+ rwLock.ExitReadLock();
+ }
+ }*/
+
///
- /// Adds an object to the list of voyeurs
+ /// For monster and CombatPet FindNextTarget
+ /// - for monsters, contains players and combat pets
+ /// - for combat pets, contains monsters
///
- /// true if previously an unknown object
- public bool AddVoyeur(PhysicsObj obj)
+ private bool AddVisibleTarget(PhysicsObj obj, bool clamp = true, bool foeType = false)
{
- // only tracking players who know about each object
- if (!obj.IsPlayer)
+ if (PhysicsObj.WeenieObj.IsCombatPet)
+ {
+ // only tracking monsters
+ if (!obj.WeenieObj.IsMonster)
+ {
+ Console.WriteLine($"{PhysicsObj.Name}.ObjectMaint.AddVisibleTarget({obj.Name}): tried to add a non-monster");
+ return false;
+ }
+ }
+ else if (PhysicsObj.WeenieObj.IsFactionMob)
+ {
+ // only tracking players, combat pets, and monsters of differing faction
+ if (!obj.IsPlayer && !obj.WeenieObj.IsCombatPet && (!obj.WeenieObj.IsMonster || PhysicsObj.WeenieObj.SameFaction(obj)))
+ {
+ Console.WriteLine($"{PhysicsObj.Name}.ObjectMaint.AddVisibleTarget({obj.Name}): tried to add a non-player / non-combat pet / non-opposing faction mob");
+ return false;
+ }
+ }
+ else
+ {
+ // handle special case:
+ // we want to select faction mobs for monsters inverse targets,
+ // but not add to the original monster
+ if (obj.WeenieObj.IsFactionMob)
+ {
+ obj.ObjMaint.AddVisibleTarget(PhysicsObj);
+ return false;
+ }
+
+ // handle special case:
+ // if obj has a FoeType of this creature, and this creature doesn't have a FoeType for obj,
+ // we only want to perform the inverse
+ /*if (obj.WeenieObj.FoeType != null && obj.WeenieObj.FoeType == PhysicsObj.WeenieObj.WorldObject?.CreatureType &&
+ (PhysicsObj.WeenieObj.FoeType == null || obj.WeenieObj.WorldObject != null && PhysicsObj.WeenieObj.FoeType != obj.WeenieObj.WorldObject.CreatureType))
+ {
+ obj.ObjMaint.AddVisibleTarget(PhysicsObj);
+ return false;
+ }*/
+
+ // only tracking players and combat pets
+ if (!obj.IsPlayer && !obj.WeenieObj.IsCombatPet && PhysicsObj.WeenieObj.FoeType == null)
+ {
+ Console.WriteLine($"{PhysicsObj.Name}.ObjectMaint.AddVisibleTarget({obj.Name}): tried to add a non-player / non-combat pet");
+ return false;
+ }
+ }
+ if (PhysicsObj.DatObject)
+ {
+ Console.WriteLine($"{PhysicsObj.Name}.ObjectMaint.AddVisibleTarget({obj.Name}): tried to add player for dat object");
return false;
+ }
- if (!VoyeurTable.ContainsKey(obj.ID))
+ if (clamp && InitialClamp && obj.IsPlayer && !obj.ObjMaint.KnownObjects.ContainsKey(obj.ID))
{
- VoyeurTable.Add(obj.ID, obj);
- return true;
+ var distSq = PhysicsObj.Position.Distance2DSquared(obj.Position);
+
+ if (distSq > InitialClamp_DistSq)
+ return false;
}
- return false;
+
+ // TryAdd for existing keys still modifies collection?
+ if (VisibleTargets.ContainsKey(obj.ID))
+ return false;
+
+ //Console.WriteLine($"{PhysicsObj.Name} ({PhysicsObj.ID:X8}).ObjectMaint.AddVisibleTarget({obj.Name})");
+
+ VisibleTargets.Add(obj.ID, obj);
+
+ // maintain inverse for monsters / combat pets
+ if (!obj.IsPlayer)
+ obj.ObjMaint.AddVisibleTarget(PhysicsObj);
+
+ return true;
}
///
- /// Adds a list of objects watching this object
+ /// For monster and CombatPet FindNextTarget
+ /// - for monsters, contains players and combat pets
+ /// - for combat pets, contains monsters
///
- public List AddVoyeurs(List objs)
+ public List AddVisibleTargets(IEnumerable objs)
{
- var newVoyeurs = new List();
+ rwLock.EnterWriteLock();
+ try
+ {
+ var visibleAdded = new List();
- foreach (var obj in objs)
- if (AddVoyeur(obj)) newVoyeurs.Add(obj);
+ foreach (var obj in objs)
+ {
+ if (AddVisibleTarget(obj))
+ visibleAdded.Add(obj);
+ }
+
+ return AddKnownPlayers(visibleAdded.Where(o => o.IsPlayer));
+ }
+ finally
+ {
+ rwLock.ExitWriteLock();
+ }
+ }
- return newVoyeurs;
+ private bool RemoveVisibleTarget(PhysicsObj obj)
+ {
+ //Console.WriteLine($"{PhysicsObj.Name} ({PhysicsObj.ID:X8}).ObjectMaint.RemoveVisibleTarget({obj.Name})");
+ return VisibleTargets.Remove(obj.ID);
+ }
+
+ public List GetVisibleObjectsDist(ObjCell cell, VisibleObjectType type)
+ {
+ rwLock.EnterReadLock();
+ try
+ {
+ var visibleObjs = GetVisibleObjects(cell, type);
+
+ var dist = new List();
+ foreach (var obj in visibleObjs)
+ {
+ var distSq = PhysicsObj.Position.Distance2DSquared(obj.Position);
+
+ if (distSq <= InitialClamp_DistSq)
+ dist.Add(obj);
+ }
+
+ return dist;
+ }
+ finally
+ {
+ rwLock.ExitReadLock();
+ }
}
///
- /// Removes an object from the voyeurs table
+ /// Adds a retaliate target for a monster that it would not normally attack
///
- public bool RemoveVoyeur(PhysicsObj obj)
+ public void AddRetaliateTarget(PhysicsObj obj)
{
- return VoyeurTable.Remove(obj.ID);
+ rwLock.EnterWriteLock();
+ try
+ {
+ if (RetaliateTargets.ContainsKey(obj.ID))
+ {
+ //Console.WriteLine($"{PhysicsObj.Name}.AddRetaliateTarget({obj.Name}) - retaliate target already exists");
+ return;
+ }
+ //Console.WriteLine($"{PhysicsObj.Name}.AddRetaliateTarget({obj.Name})");
+ RetaliateTargets.Add(obj.ID, obj);
+
+ // we're going to add retaliate targets to the list of visible targets as well,
+ // so that we don't have to traverse both VisibleTargets and RetaliateTargets
+ // in all of the logic based on VisibleTargets
+ if (VisibleTargets.ContainsKey(obj.ID))
+ {
+ //Console.WriteLine($"{PhysicsObj.Name}.AddRetaliateTarget({obj.Name}) - visible target already exists");
+ return;
+ }
+ VisibleTargets.Add(obj.ID, obj);
+ }
+ finally
+ {
+ rwLock.ExitWriteLock();
+ }
}
///
- /// Called when a new PhysicsObj is first instantiated
- /// Gets the list of visible players to this PhysicsObj,
- /// and adds them to the voyeurs list
+ /// Called when a monster goes back to sleep
///
- public void get_voyeurs()
+ public void ClearRetaliateTargets()
{
- if (PhysicsObj.DatObject) return;
+ rwLock.EnterWriteLock();
+ try
+ {
+ // remove retaliate targets from visible targets
+ foreach (var retaliateTarget in RetaliateTargets)
+ VisibleTargets.Remove(retaliateTarget.Key);
+
+ RetaliateTargets.Clear();
+ }
+ finally
+ {
+ rwLock.ExitWriteLock();
+ }
+ }
- var visiblePlayers = GetVisibleObjects(PhysicsObj.CurCell).Where(o => o.IsPlayer).ToList();
- AddVoyeurs(visiblePlayers);
+ private bool RemoveRetaliateTarget(PhysicsObj obj)
+ {
+ return RetaliateTargets.Remove(obj.ID);
+ }
+
+ ///
+ /// Clears all of the ObjMaint tables for an object
+ ///
+ private void RemoveAllObjects()
+ {
+ KnownObjects.Clear();
+ VisibleObjects.Clear();
+ DestructionQueue.Clear();
+ KnownPlayers.Clear();
+ VisibleTargets.Clear();
+ RetaliateTargets.Clear();
}
///
@@ -463,10 +1124,31 @@ public void get_voyeurs()
///
public void DestroyObject()
{
- foreach (var obj in ObjectTable.Values)
- obj.ObjMaint.RemoveObject(PhysicsObj);
+ rwLock.EnterWriteLock();
+ try
+ {
+ foreach (var obj in KnownObjects.Values)
+ obj.ObjMaint.RemoveObject(PhysicsObj);
+
+ // we are maintaining the inverses here,
+ // so passing false to iterate with modifying these collections
+ foreach (var obj in KnownPlayers.Values)
+ obj.ObjMaint.RemoveObject(PhysicsObj, false);
- RemoveServerObject(PhysicsObj);
+ foreach (var obj in VisibleTargets.Values)
+ obj.ObjMaint.RemoveObject(PhysicsObj, false);
+
+ foreach (var obj in RetaliateTargets.Values)
+ obj.ObjMaint.RemoveObject(PhysicsObj, false);
+
+ RemoveAllObjects();
+
+ ServerObjectManager.RemoveServerObject(PhysicsObj);
+ }
+ finally
+ {
+ rwLock.ExitWriteLock();
+ }
}
}
}
diff --git a/ACViewer/Physics/Common/Position.cs b/ACViewer/Physics/Common/Position.cs
index d707b95..a6b7fb3 100644
--- a/ACViewer/Physics/Common/Position.cs
+++ b/ACViewer/Physics/Common/Position.cs
@@ -1,5 +1,8 @@
using System;
using System.Numerics;
+
+using ACE.Entity.Enum;
+
using ACE.Server.Physics.Animation;
using ACE.Server.Physics.Extensions;
using ACE.Server.Physics.Util;
@@ -11,6 +14,12 @@ public class Position: IEquatable
public uint ObjCellID;
public AFrame Frame;
+ public uint Landblock => ObjCellID >> 16;
+
+ public uint LandblockX => ObjCellID >> 24;
+
+ public uint LandblockY => (ObjCellID >> 16) & 0xFF;
+
public Position()
{
Init();
@@ -111,25 +120,46 @@ public static double CylinderDistance(float radius, float height, Position pos,
return reach;
}
+ // custom, based on above
+ public static double CylinderDistanceSq(float radius, float height, Position pos, float otherRadius, float otherHeight, Position otherPos)
+ {
+ var offset = pos.GetOffset(otherPos);
+ var reach = offset.Length() - (radius + otherRadius);
+
+ var diffZ = pos.Frame.Origin.Z <= otherPos.Frame.Origin.Z ? otherPos.Frame.Origin.Z - (pos.Frame.Origin.Z + height) :
+ pos.Frame.Origin.Z - (otherPos.Frame.Origin.Z + otherHeight);
+
+ if (diffZ > 0 && reach > 0)
+ return diffZ * diffZ + reach * reach;
+ else if (diffZ < 0 && reach < 0)
+ return -(diffZ * diffZ + reach * reach);
+ else
+ return reach * reach;
+ }
+
public static float CylinderDistanceNoZ(float radius, Position pos, float otherRadius, Position otherPos)
{
var offset = pos.GetOffset(otherPos);
return offset.Length() - (radius + otherRadius);
}
- public int DetermineQuadrant(float height, Position position)
+ public static readonly float ThresholdMed = 1.0f / 3.0f;
+ public static readonly float ThresholdHigh = 2.0f / 3.0f;
+
+ public Quadrant DetermineQuadrant(float height, Position position)
{
- var hitLocation = LocalToLocal(position, Vector3.Zero);
+ var hitpoint = LocalToLocal(position, Vector3.Zero);
+
+ var quadrant = hitpoint.X < 0.0f ? Quadrant.Left : Quadrant.Right;
- var quadrant = hitLocation.X < 0.0f ? 0x8 : 0x10;
- quadrant |= hitLocation.Y >= 0.0f ? 0x20 : 0x40;
+ quadrant |= hitpoint.Y >= 0.0f ? Quadrant.Front : Quadrant.Back;
- if (height * 0.333333333f > hitLocation.Z)
- quadrant |= 4; // low
- else if (height * 0.66666667f > hitLocation.Z)
- quadrant |= 2; // medium
+ if (hitpoint.Z < height * ThresholdMed)
+ quadrant |= Quadrant.Low;
+ else if (hitpoint.Z < height * ThresholdHigh)
+ quadrant |= Quadrant.Medium;
else
- quadrant |= 1; // high
+ quadrant |= Quadrant.High;
return quadrant;
}
@@ -235,6 +265,25 @@ public uint GetIndoorCell(uint blockCellID)
return newCell.Value;
}
+ ///
+ /// Returns the squared 2D distance between 2 positions
+ ///
+ public float Distance2DSquared(Position p)
+ {
+ if (Landblock == p.Landblock)
+ {
+ var dx = Frame.Origin.X - p.Frame.Origin.X;
+ var dy = Frame.Origin.Y - p.Frame.Origin.Y;
+ return dx * dx + dy * dy;
+ }
+ else
+ {
+ var dx = ((int)LandblockX - p.LandblockX) * 192 + Frame.Origin.X - p.Frame.Origin.X;
+ var dy = ((int)LandblockY - p.LandblockY) * 192 + Frame.Origin.Y - p.Frame.Origin.Y;
+ return dx * dx + dy * dy;
+ }
+ }
+
public bool Equals(Position pos)
{
return ObjCellID == pos.ObjCellID && Frame.Equals(pos.Frame);
@@ -242,7 +291,12 @@ public bool Equals(Position pos)
public override string ToString()
{
- return $"{ObjCellID:X8} [{Frame.Origin}] {Frame.Orientation}";
+ return $"0x{ObjCellID:X8} {Frame}";
+ }
+
+ public string ShortLoc()
+ {
+ return $"0x{ObjCellID:X8} [{Frame.Origin.X} {Frame.Origin.Y} {Frame.Origin.Z}]";
}
}
}
diff --git a/ACViewer/Physics/Common/RenderSurface.cs b/ACViewer/Physics/Common/RenderSurface.cs
index 4293d63..8e51970 100644
--- a/ACViewer/Physics/Common/RenderSurface.cs
+++ b/ACViewer/Physics/Common/RenderSurface.cs
@@ -1,10 +1,11 @@
+using ACE.DatLoader.FileTypes;
using ACE.Entity.Enum;
namespace ACE.Server.Physics.Common
{
public class RenderSurface
{
- public DatLoader.FileTypes.Texture _renderSurface;
+ public Texture _texture;
public int Width;
public int Height;
@@ -17,16 +18,16 @@ public RenderSurface()
{
}
- public RenderSurface(DatLoader.FileTypes.Texture renderSurface)
+ public RenderSurface(Texture texture)
{
- _renderSurface = renderSurface;
+ _texture = texture;
- Width = renderSurface.Width;
- Height = renderSurface.Height;
- Format = renderSurface.Format;
- Length = renderSurface.Length;
- Data = renderSurface.SourceData;
- DefaultPaletteID = renderSurface.DefaultPaletteId;
+ Width = texture.Width;
+ Height = texture.Height;
+ Format = texture.Format;
+ Length = texture.Length;
+ Data = texture.SourceData;
+ DefaultPaletteID = texture.DefaultPaletteId;
}
// RenderSurfaceD3D.Create()
diff --git a/ACViewer/Physics/Common/SetPosition.cs b/ACViewer/Physics/Common/SetPosition.cs
index c3f250c..b222e41 100644
--- a/ACViewer/Physics/Common/SetPosition.cs
+++ b/ACViewer/Physics/Common/SetPosition.cs
@@ -21,8 +21,8 @@ public enum SetPositionFlags
Restore = 0x4,
Slide = 0x10,
DontCreateCells = 0x20,
- Scatter = 0x100,
- RandomScatter = 0x200,
+ Scatter = 0x100, // try to spawn in original position, if that fails, try to spawn in scatter pos
+ RandomScatter = 0x200, // try to spawn in scatter pos
Line = 0x400,
SendPositionEvent = 0x1000,
};
@@ -36,8 +36,19 @@ public class SetPosition
public float RadY;
public int NumTries;
+ public static int Default_NumTries = 20;
+
public SetPosition() { }
+ public SetPosition(Position pos, SetPositionFlags flags, float radius)
+ {
+ Pos = pos;
+ Flags = flags;
+ RadX = radius;
+ RadY = radius;
+ NumTries = Default_NumTries;
+ }
+
public SetPosition(Position pos, SetPositionFlags flags)
{
Pos = pos;
diff --git a/ACViewer/Physics/Common/Vector.cs b/ACViewer/Physics/Common/Vector.cs
index 0f0f621..577f594 100644
--- a/ACViewer/Physics/Common/Vector.cs
+++ b/ACViewer/Physics/Common/Vector.cs
@@ -1,4 +1,5 @@
-using System.Numerics;
+using System;
+using System.Numerics;
namespace ACE.Server.Physics.Common
{
@@ -13,5 +14,12 @@ public static bool NormalizeCheckSmall(ref Vector3 v)
v *= 1.0f / dist;
return false;
}
+
+ public static bool IsZero(Vector3 v)
+ {
+ return Math.Abs(v.X) < PhysicsGlobals.EPSILON &&
+ Math.Abs(v.Y) < PhysicsGlobals.EPSILON &&
+ Math.Abs(v.Z) < PhysicsGlobals.EPSILON;
+ }
}
}
diff --git a/ACViewer/Physics/Common/WeenieObject.cs b/ACViewer/Physics/Common/WeenieObject.cs
index 69089c7..63485f0 100644
--- a/ACViewer/Physics/Common/WeenieObject.cs
+++ b/ACViewer/Physics/Common/WeenieObject.cs
@@ -1,8 +1,8 @@
using System;
+
using ACE.Entity;
using ACE.Entity.Enum;
using ACE.Server.Physics.Animation;
-using ACE.Server.Physics.Combat;
using ACE.Server.Physics.Collision;
using ACE.Server.WorldObjects;
@@ -14,11 +14,50 @@ public class WeenieObject
public double UpdateTime;
public WorldObject WorldObject;
+ public bool IsMonster { get; set; }
+
+ public bool IsCombatPet { get; set; }
+
+ public bool IsFactionMob { get; set; }
+
+ public FactionBits Faction1Bits { get; set; }
+
+ public CreatureType? FoeType { get; set; }
+
+ public PlayerKillerStatus PlayerKillerStatus { get; set; }
+
public WeenieObject() { }
public WeenieObject(WorldObject worldObject)
{
WorldObject = worldObject;
+
+ /*if (!(worldObject is Creature creature))
+ return;
+
+ IsCombatPet = worldObject is CombatPet;
+
+ IsMonster = creature.IsMonster && !IsCombatPet;
+
+ Faction1Bits = creature.Faction1Bits ?? FactionBits.None;
+
+ IsFactionMob = IsMonster && Faction1Bits != FactionBits.None;
+
+ FoeType = creature.FoeType;
+
+ PlayerKillerStatus = creature.PlayerKillerStatus;*/
+ }
+
+ public bool SameFaction(PhysicsObj obj)
+ {
+ return (Faction1Bits & obj.WeenieObj.Faction1Bits) != 0;
+ }
+
+ public bool PotentialFoe(PhysicsObj obj)
+ {
+ /*return FoeType != null && FoeType == obj.WeenieObj.WorldObject?.CreatureType ||
+ obj.WeenieObj.FoeType != null && obj.WeenieObj.FoeType == WorldObject?.CreatureType;*/
+ return false;
}
public bool CanJump(float extent)
@@ -26,19 +65,69 @@ public bool CanJump(float extent)
return true;
}
- public bool InqJumpVelocity(float extent, ref float velocityZ)
+ public bool InqJumpVelocity(float extent, out float velocity_z)
{
- velocityZ = MovementSystem.GetJumpHeight(1.0f, 100, 1.0f, 1.0f) * 19.6f;
- return true;
+ velocity_z = 0.0f;
+
+ /*var player = WorldObject as Player;
+
+ if (player == null)
+ return false;*/
+
+ /*if (!WorldObject.IsPlayer) return false;
+
+ var burden = InqBurden();
+ if (burden == null)
+ return false;
+
+ var stamina = player.Stamina.Current;
+
+ var jumpSkill = player.GetCreatureSkill(Skill.Jump).Current;
+
+ if (stamina == 0)
+ jumpSkill = 0;
+
+ var height = MovementSystem.GetJumpHeight((float)burden, jumpSkill, extent, 1.0f);
+
+ velocity_z = (float)Math.Sqrt(height * 19.6);
+
+ return true;*/
+ return false;
+ }
+
+ ///
+ /// Returns the player's load / burden as a percentage,
+ /// usually in the range 0.0 - 3.0 (max 300% typically)
+ ///
+ public float? InqBurden()
+ {
+ /*var player = WorldObject as Player;
+
+ if (player == null)
+ return null;
+
+ var strength = (int)player.Strength.Current;
+
+ var numAugs = player.AugmentationIncreasedCarryingCapacity;
+
+ var capacity = EncumbranceSystem.EncumbranceCapacity(strength, numAugs);
+
+ var encumbrance = player.EncumbranceVal ?? 0;
+
+ var burden = EncumbranceSystem.GetBurden(capacity, encumbrance);
+
+ return burden;*/
+ return null;
}
public bool InqRunRate(ref float rate)
{
// get run skill from WorldObject
- uint runSkill = WorldObject.RunSkill;
- /*var creature = WorldObject as Creature;
- if (creature != null)
+ uint runSkill = 0;
+ /*if (WorldObject is Creature creature)
runSkill = creature.GetCreatureSkill(Skill.Run).Current;*/
+ if (WorldObject.IsCreature)
+ runSkill = WorldObject.RunSkill;
//rate = (float)MovementSystem.GetRunRate(0.0f, 300, 1.0f);
rate = (float)MovementSystem.GetRunRate(0.0f, (int)runSkill, 1.0f);
@@ -48,41 +137,43 @@ public bool InqRunRate(ref float rate)
public bool IsCorpse()
{
+ //return WorldObject is Corpse;
return false;
}
- public bool IsImpenetable()
+ public bool IsImpenetrable()
{
+ //return WorldObject is Player player && player.PlayerKillerStatus == PlayerKillerStatus.Free;
return false;
}
public bool IsPK()
{
+ //return WorldObject is Player player && player.IsPK;
return false;
}
public bool IsPKLite()
{
+ //return WorldObject is Player player && player.IsPKL;
return false;
}
public bool IsPlayer()
{
- return true;
+ //return WorldObject is Player;
+ return WorldObject.IsPlayer;
}
public bool IsCreature()
{
- //if (WorldObject == null) return false;
- //var creature = WorldObject as Creature;
- //return creature != null;
+ //return WorldObject is Creature;
return WorldObject.IsCreature;
-
- //return true;
}
public bool IsStorage()
{
+ //return WorldObject is Storage;
return false;
}
@@ -91,18 +182,51 @@ public float JumpStaminaCost(float extent, int staminaCost)
return 0;
}
- public int DoCollision(AtkCollisionProfile prof, ObjectGuid guid, PhysicsObj target)
+ public void InqCollisionProfile(ObjCollisionProfile prof)
{
+ if (WorldObject == null)
+ return;
+
+ prof.WCID = ID;
+ //prof.ItemType = WorldObject.ItemType;
+
+ //if (WorldObject is Creature)
+ if (WorldObject.IsCreature)
+ prof.Flags |= ObjCollisionProfileFlags.Creature;
+
+ //if (WorldObject is Player)
+ if (WorldObject.IsPlayer)
+ prof.Flags |= ObjCollisionProfileFlags.Player;
+
+ //if (WorldObject.Attackable)
+ if (WorldObject.IsCreature)
+ prof.Flags |= ObjCollisionProfileFlags.Attackable;
+
+ //if (WorldObject is Door)
+ //prof.Flags |= ObjCollisionProfileFlags.Door;
+ }
+
+ public int DoCollision(ObjCollisionProfile prof, ObjectGuid guid, PhysicsObj target)
+ {
+ var wo = WorldObject;
+
+ if (wo == null)
+ return -1;
+
+ var targetWO = target.WeenieObj.WorldObject;
+
+ if (targetWO == null)
+ return -1;
+
// no collision with self
- if (WorldObject.Guid.Equals(target.WeenieObj.WorldObject.Guid))
+ if (wo.Guid.Equals(targetWO.Guid))
return -1;
- /*Console.WriteLine("AtkCollisionProfile");
+ /*Console.WriteLine("ObjCollisionProfile");
Console.WriteLine("Source: " + WorldObject.Name);
Console.WriteLine("Target: " + obj.WeenieObj.WorldObject.Name);*/
- //if (WorldObject != null)
- //WorldObject.OnCollideObject(target.WeenieObj.WorldObject);
+ //wo.OnCollideObject(targetWO);
return 0;
}
@@ -111,25 +235,76 @@ public int DoCollision(EnvCollisionProfile prof, ObjectGuid guid, PhysicsObj tar
{
/*Console.WriteLine("EnvCollisionProfile");
Console.WriteLine("Source: " + WorldObject.Name);
- Console.WriteLine("Target: " + obj.WeenieObj.WorldObject.Name);*/
+ Console.WriteLine("Target: " + target.WeenieObj.WorldObject.Name);
+ Console.WriteLine("Velocity: " + prof.Velocity);*/
+
+ var wo = WorldObject;
- //if (WorldObject != null)
- //WorldObject.OnCollideEnvironment();
+ if (wo == null)
+ return 0;
+
+ /*if (wo is Player player)
+ player.HandleFallingDamage(prof);
+ else
+ wo.OnCollideEnvironment();*/
return 0;
}
public void DoCollisionEnd(ObjectGuid targetGuid)
{
- /*var target = WorldObject.CurrentLandblock?.GetObject(targetGuid);
+ var wo = WorldObject;
+
+ if (wo == null)
+ return;
- if (WorldObject != null && target != null)
- WorldObject.OnCollideObjectEnd(target);*/
+ /*var target = wo.CurrentLandblock?.GetObject(targetGuid);
+
+ if (target != null)
+ wo.OnCollideObjectEnd(target);*/
}
public void OnMotionDone(uint motionID, bool success)
{
+ //WorldObject.HandleMotionDone(motionID, success);
+ }
+
+ public void OnMoveComplete(WeenieError status)
+ {
+ //WorldObject.OnMoveComplete(status);
+ }
+ public bool CanBypassMoveRestrictions()
+ {
+ // acclient checks both of these here
+ //return WorldObject.IgnoreHouseBarriers/* && WorldObject is Admin*/;
+ return true;
+ }
+
+ public bool CanMoveInto(WeenieObject mover)
+ {
+ /*var house = WorldObject as House;
+ if (house == null)
+ {
+ log.Error($"{WorldObject?.Name} ({WorldObject?.Guid}).CanMoveInto({mover.WorldObject?.Name} ({mover.WorldObject?.Guid}) - couldn't find house");
+ return true;
+ }
+ var rootHouse = house.RootHouse;
+ if (rootHouse == null)
+ {
+ log.Error($"{WorldObject?.Name} ({WorldObject?.Guid}).CanMoveInto({mover.WorldObject?.Name} ({mover.WorldObject?.Guid}) - couldn't find root house");
+ return true;
+ }
+ var player = mover?.WorldObject as Player;
+ if (player == null)
+ {
+ log.Error($"{WorldObject?.Name} ({WorldObject?.Guid}).CanMoveInto({mover.WorldObject?.Name} ({mover.WorldObject?.Guid}) - couldn't find player");
+ return true;
+ }
+ var result = rootHouse.HouseOwner == null || rootHouse.OpenStatus || rootHouse.HasPermission(player);
+ //Console.WriteLine($"{player.Name} can move into {rootHouse.Name} ({rootHouse.Guid}): {result}");
+ return result;*/
+ return true;
}
}
}
diff --git a/ACViewer/Physics/CylSphere.cs b/ACViewer/Physics/CylSphere.cs
index f9e89b2..274f3d4 100644
--- a/ACViewer/Physics/CylSphere.cs
+++ b/ACViewer/Physics/CylSphere.cs
@@ -468,7 +468,7 @@ public bool CollisionNormal(Transition transition, Sphere checkPos, Vector3 _dis
public bool Equals(CylSphere cylSphere)
{
- return cylSphere != null && Height == cylSphere.Height && Radius == cylSphere.Radius && LowPoint.IsEqual(cylSphere.LowPoint);
+ return cylSphere != null && Height == cylSphere.Height && Radius == cylSphere.Radius && LowPoint == cylSphere.LowPoint;
}
public override int GetHashCode()
diff --git a/ACViewer/Physics/Entity/BSPCache.cs b/ACViewer/Physics/Entity/BSPCache.cs
index 34732e5..9a30079 100644
--- a/ACViewer/Physics/Entity/BSPCache.cs
+++ b/ACViewer/Physics/Entity/BSPCache.cs
@@ -14,6 +14,13 @@ public static class BSPCache
public static int Requests;
public static int Hits;
+ public static int Count => BSPTrees.Count;
+
+ public static void Clear()
+ {
+ BSPTrees.Clear();
+ }
+
public static BSPTree Get(BSPTree bspTree)
{
if (!Enabled)
diff --git a/ACViewer/Physics/Entity/GfxObjCache.cs b/ACViewer/Physics/Entity/GfxObjCache.cs
index 90b07a0..a7123e0 100644
--- a/ACViewer/Physics/Entity/GfxObjCache.cs
+++ b/ACViewer/Physics/Entity/GfxObjCache.cs
@@ -1,3 +1,6 @@
+// Uncomment this if you want to use weak reference cache. It could save 10's of MB and might be useful on a micro-server
+//#define USE_WEAK_REFERENCE_CACHE
+
using System;
using System.Collections.Concurrent;
@@ -8,11 +11,22 @@ namespace ACE.Server.Physics.Entity
{
public static class GfxObjCache
{
+ #if !USE_WEAK_REFERENCE_CACHE
public static readonly ConcurrentDictionary GfxObjs = new ConcurrentDictionary();
+ #else
+ public static readonly ConcurrentDictionary> GfxObjs = new ConcurrentDictionary>();
+ #endif
public static int Requests;
public static int Hits;
+ public static int Count => GfxObjs.Count;
+
+ public static void Clear()
+ {
+ GfxObjs.Clear();
+ }
+
public static GfxObj Get(uint gfxObjID)
{
Requests++;
@@ -22,15 +36,29 @@ public static GfxObj Get(uint gfxObjID)
if (GfxObjs.TryGetValue(gfxObjID, out var result))
{
+ #if !USE_WEAK_REFERENCE_CACHE
Hits++;
return result;
+ #else
+ if (result.TryGetTarget(out var target))
+ {
+ Hits++;
+ return target;
+ }
+ #endif
}
var _gfxObj = DBObj.GetGfxObj(gfxObjID);
// not cached, add it
var gfxObj = new GfxObj(_gfxObj);
+
+ #if !USE_WEAK_REFERENCE_CACHE
gfxObj = GfxObjs.GetOrAdd(_gfxObj.Id, gfxObj);
+ #else
+ GfxObjs[_gfxObj.Id] = new WeakReference(gfxObj);
+ #endif
+
return gfxObj;
}
}
diff --git a/ACViewer/Physics/Entity/PolygonCache.cs b/ACViewer/Physics/Entity/PolygonCache.cs
index 5c960f8..39af68e 100644
--- a/ACViewer/Physics/Entity/PolygonCache.cs
+++ b/ACViewer/Physics/Entity/PolygonCache.cs
@@ -14,6 +14,13 @@ public static class PolygonCache
public static int Requests;
public static int Hits;
+ public static int Count => Polygons.Count;
+
+ public static void Clear()
+ {
+ Polygons.Clear();
+ }
+
public static Polygon Get(Polygon p)
{
if (!Enabled)
diff --git a/ACViewer/Physics/Entity/VertexCache.cs b/ACViewer/Physics/Entity/VertexCache.cs
index 7418c3d..52b5ac8 100644
--- a/ACViewer/Physics/Entity/VertexCache.cs
+++ b/ACViewer/Physics/Entity/VertexCache.cs
@@ -14,6 +14,13 @@ public static class VertexCache
public static int Requests;
public static int Hits;
+ public static int Count => Vertices.Count;
+
+ public static void Clear()
+ {
+ Vertices.Clear();
+ }
+
public static Vertex Get(Vertex v)
{
if (!Enabled) return v;
diff --git a/ACViewer/Physics/Extensions/QuaternionExtensions.cs b/ACViewer/Physics/Extensions/QuaternionExtensions.cs
index 5f17d20..f9bbe2a 100644
--- a/ACViewer/Physics/Extensions/QuaternionExtensions.cs
+++ b/ACViewer/Physics/Extensions/QuaternionExtensions.cs
@@ -7,11 +7,11 @@ public static class QuaternionExtensions
{
public static bool IsValid(this Quaternion q)
{
- if (q.X == float.NaN || q.Y == float.NaN || q.Z == float.NaN || q.W == float.NaN)
+ if (float.IsNaN(q.X) || float.IsNaN(q.Y) || float.IsNaN(q.Z) || float.IsNaN(q.W))
return false;
var length = q.Length();
- if (length == float.NaN)
+ if (float.IsNaN(length))
return false;
if (Math.Abs(length - 1.0f) > PhysicsGlobals.EPSILON * 5.0f)
diff --git a/ACViewer/Physics/Extensions/Vector3Extensions.cs b/ACViewer/Physics/Extensions/Vector3Extensions.cs
index 8aca316..8d037ac 100644
--- a/ACViewer/Physics/Extensions/Vector3Extensions.cs
+++ b/ACViewer/Physics/Extensions/Vector3Extensions.cs
@@ -13,7 +13,7 @@ public static float Dot2D(this Vector3 a, Vector3 b)
public static bool IsValid(this Vector3 v)
{
- return v.X != float.NaN && v.Y != float.NaN && v.Z != float.NaN;
+ return !float.IsNaN(v.X) && !float.IsNaN(v.Y) && !float.IsNaN(v.Z);
}
public static double Length2D(this Vector3 v)
@@ -23,22 +23,7 @@ public static double Length2D(this Vector3 v)
public static double LengthSquared2D(this Vector3 v)
{
- return new Vector2(v.X, v.Y).LengthSquared();
- }
-
- public static Vector3 Normalize(this Vector3 v)
- {
- return v / v.Length();
- }
-
- public static bool IsEqual(this Vector3 v1, Vector3 v2)
- {
- return v1.X == v2.X && v1.Y == v2.Y && v1.Z == v2.Z; // epsilon?
- }
-
- public static bool IsMoved(this Vector3 a, Vector3 b)
- {
- return (a.X != b.X || a.Y != b.Y || a.Z != b.Z);
+ return v.X * v.X + v.Y * v.Y;
}
public static float get_heading(this Vector3 v)
diff --git a/ACViewer/Physics/Hooks/FPHook.cs b/ACViewer/Physics/Hooks/FPHook.cs
index dd03fe7..81d49f7 100644
--- a/ACViewer/Physics/Hooks/FPHook.cs
+++ b/ACViewer/Physics/Hooks/FPHook.cs
@@ -27,7 +27,7 @@ public override bool Execute(PhysicsObj obj)
scale = 1.0;
}
var current = (EndValue - StartValue) * scale + StartValue;
- obj.process_fp_hook((int)HookType, (float)current, UserData);
+ obj.process_fp_hook(HookType, (float)current, UserData);
return scale == 1.0;
}
}
diff --git a/ACViewer/Physics/Hooks/PhysicsHookType.cs b/ACViewer/Physics/Hooks/PhysicsHookType.cs
index 11239b7..0576624 100644
--- a/ACViewer/Physics/Hooks/PhysicsHookType.cs
+++ b/ACViewer/Physics/Hooks/PhysicsHookType.cs
@@ -1,28 +1,14 @@
-using System;
-
namespace ACE.Server.Physics.Hooks
{
- [Flags]
public enum PhysicsHookType
{
- Setup = 0x1,
- MotionTable = 0x2,
- Velocity = 0x4,
- Acceleration = 0x8,
- Omega = 0x10,
- Parent = 0x20,
- Children = 0x40,
- ObjScale = 0x80,
- Friction = 0x100,
- Elasticity = 0x200,
- Timestamps = 0x400,
- Stable = 0x800,
- PETable = 0x1000,
- DefaultScript = 0x2000,
- DefaultScriptIntensity = 0x4000,
- Position = 0x8000,
- Movement = 0x10000,
- AnimFrameID = 0x20000,
- Translucency = 0x40000,
+ Scale = 0,
+ Translucency = 1,
+ PartTranslucency = 2,
+ Luminosity = 3,
+ Diffusion = 4,
+ PartLuminosity = 5,
+ PartDiffusion = 6,
+ CallPES = 7,
}
}
diff --git a/ACViewer/Physics/Managers/ConstraintManager.cs b/ACViewer/Physics/Managers/ConstraintManager.cs
index ce5050e..7188b79 100644
--- a/ACViewer/Physics/Managers/ConstraintManager.cs
+++ b/ACViewer/Physics/Managers/ConstraintManager.cs
@@ -28,7 +28,7 @@ public void ConstrainTo(Position position, float startDistance, float maxDistanc
{
IsConstrained = true;
- ConstraintPos = position;
+ ConstraintPos = new Position(position);
ConstraintDistanceStart = startDistance;
ConstraintDistanceMax = maxDistance;
ConstraintPosOffset = position.Distance(PhysicsObj.Position);
diff --git a/ACViewer/Physics/Managers/InterpolationManager.cs b/ACViewer/Physics/Managers/InterpolationManager.cs
index 1c503ea..6d00b95 100644
--- a/ACViewer/Physics/Managers/InterpolationManager.cs
+++ b/ACViewer/Physics/Managers/InterpolationManager.cs
@@ -6,7 +6,7 @@ namespace ACE.Server.Physics.Animation
{
public class InterpolationManager
{
- public Queue PositionQueue;
+ public LinkedList PositionQueue;
public PhysicsObj PhysicsObj;
public bool KeepHeading;
public int FrameCounter;
@@ -38,8 +38,8 @@ public void InterpolateTo(Position position, bool keepHeading)
if (PhysicsObj == null)
return;
- var dest = PositionQueue.Count > 0 && PositionQueue.Last().Type == InterpolationNodeType.PositionType ?
- PositionQueue.Last().Position : PhysicsObj.Position;
+ var dest = PositionQueue.Count > 0 && PositionQueue.Last.Value.Type == InterpolationNodeType.PositionType ?
+ PositionQueue.Last.Value.Position : PhysicsObj.Position;
var dist = dest.Distance(position);
@@ -49,20 +49,20 @@ public void InterpolateTo(Position position, bool keepHeading)
{
while (PositionQueue.Count > 0)
{
- var lastNode = PositionQueue.Last();
+ var lastNode = PositionQueue.Last.Value;
if (lastNode.Type != InterpolationNodeType.PositionType || lastNode.Position.Distance(position) >= 0.05f)
break;
- PositionQueue.DequeueLast();
+ PositionQueue.RemoveLast();
}
while (PositionQueue.Count >= 20)
- PositionQueue.Dequeue();
+ PositionQueue.RemoveFirst();
var interpolationNode = new InterpolationNode(InterpolationNodeType.PositionType, position);
if (keepHeading)
interpolationNode.Position.Frame.set_heading(PhysicsObj.get_heading());
- PositionQueue.Enqueue(interpolationNode);
+ PositionQueue.AddLast(interpolationNode);
}
else
{
@@ -78,7 +78,7 @@ public void InterpolateTo(Position position, bool keepHeading)
if (keepHeading)
interpolationNode.Position.Frame.set_heading(PhysicsObj.get_heading());
- PositionQueue.Enqueue(interpolationNode);
+ PositionQueue.AddLast(interpolationNode);
NodeFailCounter = 4;
}
}
@@ -96,7 +96,7 @@ public void NodeCompleted(bool success)
FrameCounter = 0;
ProgressQuantum = 0.0f;
- var head = PositionQueue.Count == 0 ? null : PositionQueue.First();
+ var head = PositionQueue.Count == 0 ? null : PositionQueue.First.Value;
var next = PositionQueue.Count <= 1 ? null : PositionQueue.ElementAt(1);
if (PositionQueue.Count > 1)
@@ -122,7 +122,7 @@ public void NodeCompleted(bool success)
StopInterpolating();
}
if (PositionQueue.Count > 0)
- PositionQueue.Dequeue();
+ PositionQueue.RemoveFirst();
}
public void SetPhysicsObject(PhysicsObj obj)
@@ -148,7 +148,7 @@ public void UseTime()
{
if (NodeFailCounter <= 0) return;
- var last = PositionQueue.Last();
+ var last = PositionQueue.Last.Value;
if (last.Type != InterpolationNodeType.JumpType && last.Type != InterpolationNodeType.VelocityType)
{
if (PhysicsObj.SetPositionSimple(last.Position, true) != SetPositionError.OK)
@@ -182,7 +182,7 @@ public void UseTime()
return;
}
- var first = PositionQueue.FirstOrDefault();
+ var first = PositionQueue.First.Value;
switch (first.Type)
{
case InterpolationNodeType.JumpType:
@@ -201,7 +201,7 @@ public void adjust_offset(AFrame frame, double quantum)
if (PositionQueue.Count == 0 || PhysicsObj == null || !PhysicsObj.TransientState.HasFlag(TransientStateFlags.Contact))
return;
- var first = PositionQueue.First();
+ var first = PositionQueue.First.Value;
if (first.Type == InterpolationNodeType.JumpType || first.Type == InterpolationNodeType.VelocityType)
return;
diff --git a/ACViewer/Physics/Managers/MotionTableManager.cs b/ACViewer/Physics/Managers/MotionTableManager.cs
index 1465221..43c2031 100644
--- a/ACViewer/Physics/Managers/MotionTableManager.cs
+++ b/ACViewer/Physics/Managers/MotionTableManager.cs
@@ -1,8 +1,6 @@
-using System;
using System.Collections.Generic;
-using System.Linq;
+
using ACE.Entity.Enum;
-using ACE.Server.Physics.Common;
namespace ACE.Server.Physics.Animation
{
diff --git a/ACViewer/Physics/Managers/MoveToManager.cs b/ACViewer/Physics/Managers/MoveToManager.cs
index 26cd9e6..cb01e7e 100644
--- a/ACViewer/Physics/Managers/MoveToManager.cs
+++ b/ACViewer/Physics/Managers/MoveToManager.cs
@@ -32,7 +32,7 @@ public class MoveToManager
public List PendingActions;
public PhysicsObj PhysicsObj;
public WeenieObject WeenieObj;
- public List Callbacks;
+ public bool AlwaysTurn;
public MoveToManager()
{
@@ -59,7 +59,6 @@ public void Init()
MovementParams = new MovementParameters();
PendingActions = new List();
- Callbacks = new List();
}
public void InitializeLocalVars()
@@ -346,12 +345,7 @@ public void BeginNextNode()
}
else
CleanUpAndCallWeenie(WeenieError.None);
-
}
-
- if (PendingActions.Count == 0)
- foreach (var callback in Callbacks.ToList())
- callback();
}
public void BeginMoveForward()
@@ -425,7 +419,7 @@ public void HandleMoveToPosition()
movementParams.Speed = MovementParams.Speed;
movementParams.HoldKeyToApply = MovementParams.HoldKeyToApply;
- if (!PhysicsObj.motions_pending())
+ if (!PhysicsObj.IsAnimating)
{
var heading = MovementParams.get_desired_heading(CurrentCommand, MovingAway) + curPos.heading(CurrentTargetPosition);
if (heading >= 360.0f) heading -= 360.0f;
@@ -446,7 +440,13 @@ public void HandleMoveToPosition()
}
}
else
+ {
+ // custom: sync for server ticrate
+ if (AuxCommand != 0)
+ PhysicsObj.set_heading(heading, true);
+
stop_aux_command(movementParams);
+ }
}
else
stop_aux_command(movementParams);
@@ -455,13 +455,25 @@ public void HandleMoveToPosition()
if (!CheckProgressMade(dist))
{
- if (!PhysicsObj.IsInterpolating() && !PhysicsObj.motions_pending())
+ if (!PhysicsObj.IsInterpolating() && !PhysicsObj.IsAnimating)
FailProgressCount++;
}
else
{
+ // custom for low monster update rate
+ var inRange = false;
+
+ if (!MovementParams.UseSpheres)
+ {
+ if (dist < 1.0f && PreviousDistance < dist)
+ inRange = true;
+
+ PreviousDistance = dist;
+ PreviousDistanceTime = PhysicsTimer.CurrentTime;
+ }
+
FailProgressCount = 0;
- if (MovingAway && dist >= MovementParams.MinDistance || !MovingAway && dist <= MovementParams.DistanceToObject)
+ if (MovingAway && dist >= MovementParams.MinDistance || !MovingAway && dist <= MovementParams.DistanceToObject || inRange)
{
PendingActions.RemoveAt(0);
_StopMotion(CurrentCommand, movementParams);
@@ -505,8 +517,7 @@ public void BeginTurnToHeading()
return;
}
- if (PendingActions.Count == 0)
- return;
+ if (PhysicsObj.IsAnimating && !AlwaysTurn) return;
var pendingAction = PendingActions[0];
var headingDiff = heading_diff(pendingAction.Heading, PhysicsObj.get_heading(), (uint)MotionCommand.TurnRight);
@@ -574,6 +585,7 @@ public void HandleTurnToHeading()
var pendingAction = PendingActions[0];
var heading = PhysicsObj.get_heading();
+
if (heading_greater(heading, pendingAction.Heading, CurrentCommand))
{
FailProgressCount = 0;
@@ -603,7 +615,7 @@ public void HandleTurnToHeading()
{
PreviousHeading = heading;
- if (!PhysicsObj.IsInterpolating() && !PhysicsObj.motions_pending())
+ if (!PhysicsObj.IsInterpolating() && !PhysicsObj.IsAnimating)
FailProgressCount++;
}
}
@@ -677,6 +689,8 @@ public bool CheckProgressMade(float currDistance)
public void CancelMoveTo(WeenieError retval)
{
+ //Console.WriteLine($"CancelMoveTo({retval})");
+
if (MovementType == MovementType.Invalid)
return;
@@ -710,7 +724,8 @@ public void CleanUpAndCallWeenie(WeenieError status)
if (PhysicsObj != null)
PhysicsObj.StopCompletely(false);
- // server - handle move to done?
+ // server custom
+ WeenieObj.OnMoveComplete(status);
}
public float GetCurrentDistance()
@@ -855,15 +870,5 @@ public void stop_aux_command(MovementParameters movementParams)
AuxCommand = 0;
}
}
-
- public void add_listener(Action listener)
- {
- Callbacks.Add(listener);
- }
-
- public void remove_listener(Action listener)
- {
- Callbacks.Remove(listener);
- }
}
}
diff --git a/ACViewer/Physics/Managers/MovementManager.cs b/ACViewer/Physics/Managers/MovementManager.cs
index 916785d..f075850 100644
--- a/ACViewer/Physics/Managers/MovementManager.cs
+++ b/ACViewer/Physics/Managers/MovementManager.cs
@@ -124,6 +124,7 @@ public void MotionDone(uint motion, bool success)
public WeenieError PerformMovement(MovementStruct mvs)
{
PhysicsObj.set_active(true);
+
switch (mvs.Type)
{
case MovementType.RawCommand:
@@ -188,6 +189,9 @@ public MotionInterp get_minterp()
return MotionInterpreter;
}
+ ///
+ /// Alternatively, you can use PhysicsObj.IsAnimating for better performance.
+ ///
public bool motions_pending()
{
if (MotionInterpreter == null) return false;
diff --git a/ACViewer/Physics/Managers/ServerObjectManager.cs b/ACViewer/Physics/Managers/ServerObjectManager.cs
new file mode 100644
index 0000000..5c6d641
--- /dev/null
+++ b/ACViewer/Physics/Managers/ServerObjectManager.cs
@@ -0,0 +1,41 @@
+using System;
+using System.Collections.Concurrent;
+
+namespace ACE.Server.Physics.Managers
+{
+ public static class ServerObjectManager
+ {
+ ///
+ /// Custom lookup table of PhysicsObjs for the server
+ ///
+ public static ConcurrentDictionary ServerObjects { get; } = new ConcurrentDictionary();
+
+ ///
+ /// Adds a PhysicsObj to the static list of server-wide objects
+ ///
+ public static void AddServerObject(PhysicsObj obj)
+ {
+ if (obj != null)
+ ServerObjects[obj.ID] = obj;
+ }
+
+ ///
+ /// Removes a PhysicsObj from the static list of server-wide objects
+ ///
+ public static void RemoveServerObject(PhysicsObj obj)
+ {
+ if (obj != null)
+ ServerObjects.TryRemove(obj.ID, out _);
+ }
+
+ ///
+ /// Returns a PhysicsObj for an object ID
+ ///
+ public static PhysicsObj GetObjectA(uint objectID)
+ {
+ ServerObjects.TryGetValue(objectID, out var obj);
+
+ return obj;
+ }
+ }
+}
diff --git a/ACViewer/Physics/Managers/StickyManager.cs b/ACViewer/Physics/Managers/StickyManager.cs
index 3ee08a2..d4742d6 100644
--- a/ACViewer/Physics/Managers/StickyManager.cs
+++ b/ACViewer/Physics/Managers/StickyManager.cs
@@ -17,30 +17,19 @@ public class StickyManager
public bool Initialized;
public double StickyTimeoutTime;
- public List StickyCallbacks;
- public List UnstickyCallbacks;
-
public static readonly float StickyRadius = 0.3f;
public static readonly float StickyTime = 1.0f;
public StickyManager()
{
- Init();
}
public StickyManager(PhysicsObj obj)
{
- Init();
SetPhysicsObject(obj);
}
- public void Init()
- {
- StickyCallbacks = new List();
- UnstickyCallbacks = new List();
- }
-
public void ClearTarget()
{
if (TargetID == 0) return;
@@ -50,9 +39,6 @@ public void ClearTarget()
PhysicsObj.clear_target();
PhysicsObj.cancel_moveto();
-
- foreach (var unstickyCallback in UnstickyCallbacks.ToList())
- unstickyCallback();
}
public static StickyManager Create(PhysicsObj obj)
@@ -94,9 +80,6 @@ public void StickTo(uint objectID, float targetRadius, float targetHeight)
TargetRadius = targetRadius;
StickyTimeoutTime = PhysicsTimer.CurrentTime + StickyTime;
PhysicsObj.set_target(0, objectID, 0.5f, 0.5f);
-
- foreach (var stickyCallback in StickyCallbacks.ToList())
- stickyCallback();
}
public void UseTime()
@@ -148,25 +131,5 @@ public void adjust_offset(AFrame offset, double quantum)
//Console.WriteLine($"StickyManager.AdjustOffset(targetHeading={targetHeading}, curHeading={curHeading}, setHeading={heading})");
offset.set_heading(heading);
}
-
- public void add_sticky_listener(Action listener)
- {
- StickyCallbacks.Add(listener);
- }
-
- public void remove_sticky_listener(Action listener)
- {
- StickyCallbacks.Remove(listener);
- }
-
- public void add_unsticky_listener(Action listener)
- {
- UnstickyCallbacks.Add(listener);
- }
-
- public void remove_unsticky_listener(Action listener)
- {
- UnstickyCallbacks.Remove(listener);
- }
}
}
diff --git a/ACViewer/Physics/ObjectInfo.cs b/ACViewer/Physics/ObjectInfo.cs
index 89916fb..aba39d3 100644
--- a/ACViewer/Physics/ObjectInfo.cs
+++ b/ACViewer/Physics/ObjectInfo.cs
@@ -7,19 +7,19 @@ 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,
+ 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
+ IsPK = 0x800,
+ IsPKLite = 0x1000
};
public class ObjectInfo
@@ -31,7 +31,7 @@ public class ObjectInfo
public float StepDownHeight;
public bool Ethereal;
public bool StepDown;
- public int TargetID;
+ public uint TargetID;
public float GetWalkableZ()
{
@@ -46,11 +46,11 @@ public void Init(PhysicsObj obj, ObjectInfoState state)
StepUpHeight = Object.GetStepUpHeight();
StepDownHeight = Object.GetStepDownHeight();
Ethereal = Object.State.HasFlag(PhysicsState.Ethereal);
- StepDown = Object.State.HasFlag(PhysicsState.Missile);
+ StepDown = !Object.State.HasFlag(PhysicsState.Missile);
var wobj = Object.WeenieObj;
if (wobj != null)
{
- if (wobj.IsImpenetable())
+ if (wobj.IsImpenetrable())
State |= ObjectInfoState.IsImpenetrable;
if (wobj.IsPlayer())
State |= ObjectInfoState.IsPlayer;
@@ -59,17 +59,28 @@ public void Init(PhysicsObj obj, ObjectInfoState state)
if (wobj.IsPKLite())
State |= ObjectInfoState.IsPKLite;
}
+ if (obj.ProjectileTarget != null)
+ TargetID = obj.ProjectileTarget.ID;
}
public bool IsValidWalkable(Vector3 normal)
{
- return Object.is_valid_walkable(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))
{
@@ -116,7 +127,7 @@ public TransitionState ValidateWalkable(Sphere checkPos, Plane contactPlane, boo
if (dist > PhysicsGlobals.EPSILON)
return TransitionState.OK;
- if (path.StepDown || !State.HasFlag(ObjectInfoState.OnWalkable) || Object.is_valid_walkable(contactPlane.Normal))
+ if (path.StepDown || !State.HasFlag(ObjectInfoState.OnWalkable) || PhysicsObj.is_valid_walkable(contactPlane.Normal))
{
collision.SetContactPlane(contactPlane, isWater);
collision.ContactPlaneCellID = landCellID;
@@ -133,7 +144,7 @@ public TransitionState ValidateWalkable(Sphere checkPos, Plane contactPlane, boo
if (path.CheckWalkable) return TransitionState.Collided;
var zDist = dist / contactPlane.Normal.Z;
- if (path.StepDown || !State.HasFlag(ObjectInfoState.OnWalkable) || Object.is_valid_walkable(contactPlane.Normal))
+ if (path.StepDown || !State.HasFlag(ObjectInfoState.OnWalkable) || PhysicsObj.is_valid_walkable(contactPlane.Normal))
{
collision.SetContactPlane(contactPlane, isWater);
collision.ContactPlaneCellID = landCellID;
diff --git a/ACViewer/Physics/PartArray.cs b/ACViewer/Physics/PartArray.cs
index 30c9349..9769590 100644
--- a/ACViewer/Physics/PartArray.cs
+++ b/ACViewer/Physics/PartArray.cs
@@ -278,8 +278,7 @@ public void InitDefaults()
var animData = new Animation.AnimData();
animData.AnimID = Setup._dat.DefaultAnimation;
animData.LowFrame = 0;
- animData.HighFrame = -1;
- animData.Framerate = 30.0f;
+ animData.HighFrame = Int32.MaxValue;
Sequence.append_animation(animData);
WeenieDesc.Destroy(animData);
}
@@ -595,11 +594,13 @@ public void Update(double quantum, ref AFrame offsetFrame)
public void UpdateParts(AFrame frame)
{
var curFrame = Sequence.GetCurrAnimFrame();
-
- if (curFrame == null) return;
-
+ if (curFrame == null)
+ {
+ /*if (Parts.Count == 1) // ?? - not in acclient, why is this in server?
+ Parts[0].Pos = Owner.Position;*/
+ return;
+ }
var numParts = Math.Min(NumParts, curFrame.Frames.Count);
-
for (var i = 0; i < numParts; i++)
Parts[i].Pos.Frame.Combine(frame, new AFrame(curFrame.Frames[i]), Scale);
}
diff --git a/ACViewer/Physics/Particles/Particle.cs b/ACViewer/Physics/Particles/Particle.cs
index 8297d5b..600eba9 100644
--- a/ACViewer/Physics/Particles/Particle.cs
+++ b/ACViewer/Physics/Particles/Particle.cs
@@ -1,9 +1,10 @@
using System;
using System.Numerics;
+
+using ACE.Common;
using ACE.Entity.Enum;
using ACE.Server.Physics.Animation;
using ACE.Server.Physics.Common;
-using ACE.Server.Physics.Extensions;
namespace ACE.Server.Physics
{
@@ -64,8 +65,8 @@ public bool Init(ParticleEmitterInfo info, PhysicsObj parent, int partIdx, AFram
A = a;
B = b;
- var ra = Common.Random.RollDice(-(float)Math.PI, (float)Math.PI);
- var po = Common.Random.RollDice(-(float)Math.PI, (float)Math.PI);
+ var ra = ThreadSafeRandom.Next(-(float)Math.PI, (float)Math.PI);
+ var po = ThreadSafeRandom.Next(-(float)Math.PI, (float)Math.PI);
var rb = Math.Cos(po);
C.X = (float)(Math.Cos(ra) * c.X * rb);
diff --git a/ACViewer/Physics/Particles/ParticleEmitterInfo.cs b/ACViewer/Physics/Particles/ParticleEmitterInfo.cs
index c5d23d3..7db7093 100644
--- a/ACViewer/Physics/Particles/ParticleEmitterInfo.cs
+++ b/ACViewer/Physics/Particles/ParticleEmitterInfo.cs
@@ -1,5 +1,7 @@
using System;
using System.Numerics;
+
+using ACE.Common;
using ACE.Entity.Enum;
using ACE.Server.Physics.Common;
using ACE.Server.Physics.Extensions;
@@ -96,7 +98,7 @@ public ParticleEmitterInfo(DatLoader.FileTypes.ParticleEmitterInfo info)
public double GetRandomStartScale()
{
- var result = Common.Random.RollDice(-1.0f, 1.0f) * ScaleRand + StartScale;
+ var result = (float)ThreadSafeRandom.Next(-1.0f, 1.0f) * ScaleRand + StartScale;
result = result.Clamp(0.1f, 10.0f);
return result;
@@ -104,7 +106,7 @@ public double GetRandomStartScale()
public double GetRandomFinalScale()
{
- var result = Common.Random.RollDice(-1.0f, 1.0f) * ScaleRand * FinalScale;
+ var result = (float)ThreadSafeRandom.Next(-1.0f, 1.0f) * ScaleRand * FinalScale;
result = result.Clamp(0.1f, 10.0f);
return result;
@@ -112,7 +114,7 @@ public double GetRandomFinalScale()
public double GetRandomStartTrans()
{
- var result = Common.Random.RollDice(-1.0f, 1.0f) * TransRand * StartTrans;
+ var result = (float)ThreadSafeRandom.Next(-1.0f, 1.0f) * TransRand * StartTrans;
result = result.Clamp(0.0f, 1.0f);
return result;
@@ -120,7 +122,7 @@ public double GetRandomStartTrans()
public double GetRandomFinalTrans()
{
- var result = Common.Random.RollDice(-1.0f, 1.0f) * TransRand * FinalTrans;
+ var result = (float)ThreadSafeRandom.Next(-1.0f, 1.0f) * TransRand * FinalTrans;
result = result.Clamp(0.0f, 1.0f);
return result;
@@ -128,7 +130,7 @@ public double GetRandomFinalTrans()
public double GetRandomLifespan()
{
- var result = Common.Random.RollDice(-1.0f, 1.0f) * LifeSpanRand + LifeSpan;
+ var result = ThreadSafeRandom.Next(-1.0f, 1.0f) * LifeSpanRand + LifeSpan;
result = Math.Max(0.0f, result);
return result;
@@ -171,41 +173,41 @@ public bool ShouldEmitParticle(int numParticles, int totalEmitted, Vector3 emitt
public Vector3 GetRandomOffset()
{
var rng = new Vector3(
- Common.Random.RollDice(-1.0f, 1.0f),
- Common.Random.RollDice(-1.0f, 1.0f),
- Common.Random.RollDice(-1.0f, 1.0f));
+ (float)ThreadSafeRandom.Next(-1.0f, 1.0f),
+ (float)ThreadSafeRandom.Next(-1.0f, 1.0f),
+ (float)ThreadSafeRandom.Next(-1.0f, 1.0f));
var randomAngle = rng - OffsetDir * Vector3.Dot(OffsetDir, rng);
if (Vec.NormalizeCheckSmall(ref randomAngle))
return Vector3.Zero;
- var scaled = randomAngle * ((MaxOffset - MinOffset) + MinOffset) * Common.Random.RollDice(0.0f, 1.0f);
+ var scaled = randomAngle * ((MaxOffset - MinOffset) + MinOffset) * (float)ThreadSafeRandom.Next(0.0f, 1.0f);
return scaled;
}
public Vector3 GetRandomA()
{
- var rng = Common.Random.RollDice(0.0f, 1.0f);
+ var rng = ThreadSafeRandom.Next(0.0f, 1.0f);
var magnitude = (MaxA - MinA) * rng + MinA;
- return A * magnitude;
+ return A * (float)magnitude;
}
public Vector3 GetRandomB()
{
- var rng = Common.Random.RollDice(0.0f, 1.0f);
+ var rng = ThreadSafeRandom.Next(0.0f, 1.0f);
var magnitude = (MaxB - MinB) * rng + MinB;
- return B * magnitude;
+ return B * (float)magnitude;
}
public Vector3 GetRandomC()
{
- var rng = Common.Random.RollDice(0.0f, 1.0f);
+ var rng = ThreadSafeRandom.Next(0.0f, 1.0f);
var magnitude = (MaxC - MinC) * rng + MinC;
- return C * magnitude;
+ return C * (float)magnitude;
}
}
}
diff --git a/ACViewer/Physics/PhysicsEngine.cs b/ACViewer/Physics/PhysicsEngine.cs
index 09b4737..c8b55e9 100644
--- a/ACViewer/Physics/PhysicsEngine.cs
+++ b/ACViewer/Physics/PhysicsEngine.cs
@@ -43,12 +43,15 @@ public class PhysicsEngine
public static PhysicsEngine Instance;
public bool Server;
- public static List StaticAnimatingObjects;
+ //public static List StaticAnimatingObjects; // This is not used
public static double LastUpdate;
static PhysicsEngine()
{
- StaticAnimatingObjects = new List();
+ //StaticAnimatingObjects = new List();
+
+ Instance = new PhysicsEngine(new ObjectMaint(), new SmartBox());
+ Instance.Server = false;
}
public PhysicsEngine(ObjectMaint objMaint, SmartBox smartBox)
@@ -60,15 +63,15 @@ public PhysicsEngine(ObjectMaint objMaint, SmartBox smartBox)
Instance = this;
}
- public static void AddStaticAnimatingObject(PhysicsObj obj)
+ /*public static void AddStaticAnimatingObject(PhysicsObj obj) // Was used in PhysicsObj.InitDefaults
{
StaticAnimatingObjects.Add(obj);
- }
+ }*/
- public static void RemoveStaticAnimatingObject(PhysicsObj obj)
+ /*public static void RemoveStaticAnimatingObject(PhysicsObj obj) // Was used in PhysicsObj.Destroy
{
StaticAnimatingObjects.Remove(obj);
- }
+ }*/
public static bool SetObjectMovement(PhysicsObj obj, object buffer, int size, int movementTimestamp, int serverControlTimestamp, bool autonomous)
{
@@ -123,8 +126,8 @@ public void UseTime()
if (Player.Equals(obj))
SmartBox.PlayerPhysicsUpdatedCallback();
}
- foreach (var obj in StaticAnimatingObjects)
- obj.animate_static_object();
+ //foreach (var obj in StaticAnimatingObjects)
+ // obj.animate_static_object();
LastUpdate = PhysicsTimer.CurrentTime;
}
diff --git a/ACViewer/Physics/PhysicsGlobals.cs b/ACViewer/Physics/PhysicsGlobals.cs
index bbee338..befbda3 100644
--- a/ACViewer/Physics/PhysicsGlobals.cs
+++ b/ACViewer/Physics/PhysicsGlobals.cs
@@ -6,13 +6,15 @@ namespace ACE.Server.Physics
{
public class PhysicsGlobals
{
- public static readonly float EPSILON = 0.00019999999f;
+ public static readonly float EPSILON = 0.0002f;
- public static readonly float Gravity = -9.8000002f;
+ public static readonly float EpsilonSq = EPSILON * EPSILON;
- public static readonly float DefaultFriction = 0.94999999f;
+ public static readonly float Gravity = -9.8f;
- public static readonly float DefaultElasticity = 0.050000001f;
+ public static readonly float DefaultFriction = 0.95f;
+
+ public static readonly float DefaultElasticity = 0.05f;
public static readonly float DefaultTranslucency = 0.0f;
@@ -48,13 +50,12 @@ public class PhysicsGlobals
public static readonly float FloorZ = 0.66417414618662751f;
- //public static readonly float DummySphereRadius = 0.1f;
- public static readonly float DummySphereRadius = 0.0f; // ??
+ public static readonly float DummySphereRadius = 0.1f;
- public static readonly Sphere DummySphere = new Sphere(Vector3.Zero, DummySphereRadius);
+ public static readonly Sphere DummySphere = new Sphere(new Vector3(0, 0, DummySphereRadius), DummySphereRadius);
public static readonly Sphere DefaultSortingSphere;
- public static readonly float DefaultStepHeight = 0.0099999998f;
+ public static readonly float DefaultStepHeight = 0.01f;
}
}
diff --git a/ACViewer/Physics/PhysicsObj.cs b/ACViewer/Physics/PhysicsObj.cs
index 5e34d2f..a1810cf 100644
--- a/ACViewer/Physics/PhysicsObj.cs
+++ b/ACViewer/Physics/PhysicsObj.cs
@@ -3,16 +3,17 @@
using System.Linq;
using System.Numerics;
+using ACE.Common;
using ACE.Entity.Enum;
+using ACE.Entity.Enum.Properties;
using ACE.Server.Physics.Animation;
using ACE.Server.Physics.Collision;
using ACE.Server.Physics.Combat;
using ACE.Server.Physics.Common;
-using ACE.Server.Physics.Extensions;
using ACE.Server.Physics.Hooks;
-using ACE.Server.WorldObjects;
+using ACE.Server.Physics.Managers;
-using log4net;
+using ACViewer.Extensions;
using Landblock = ACE.Server.Physics.Common.Landblock;
using ObjectGuid = ACE.Entity.ObjectGuid;
@@ -24,8 +25,6 @@ namespace ACE.Server.Physics
///
public class PhysicsObj
{
- private static readonly ILog log = LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType);
-
public uint ID;
public ObjectGuid ObjID;
public PartArray PartArray;
@@ -81,6 +80,16 @@ public class PhysicsObj
public PhysicsObj ProjectileTarget;
public double PhysicsTimer_CurrentTime;
public bool DatObject = false;
+ public int Order = 1;
+
+ ///
+ /// This is managed by MovementManager.MotionInterpreter, and should not be updated anywhere else.
+ ///
+ public bool IsAnimating;
+
+ // this is used by the 1991 branch to determine when physics updates need to be run
+ public bool IsMovingOrAnimating => IsAnimating || !PartArray.Sequence.is_first_cyclic() || CachedVelocity != Vector3.Zero || Velocity != Vector3.Zero ||
+ MovementManager.MotionInterpreter.InterpretedState.HasCommands() || MovementManager.MoveToManager.Initialized;
// server
public Position RequestPos;
@@ -98,15 +107,11 @@ public string Name
public CellArray CellArray;
public ObjectMaint ObjMaint;
- public static List Players;
- public bool IsPlayer;
+ public bool IsPlayer => ID >= 0x50000001 && ID <= 0x5FFFFFFF;
public static readonly int UpdateTimeLength = 9;
- static PhysicsObj()
- {
- Players = new List();
- }
+ public bool IsSticky => PositionManager?.StickyManager != null && PositionManager.StickyManager.TargetID != 0;
public PhysicsObj()
{
@@ -135,6 +140,9 @@ public PhysicsObj()
UpdateTime = PhysicsTimer.CurrentTime;
UpdateTimes = new int[UpdateTimeLength];
PhysicsTimer_CurrentTime = PhysicsTimer.CurrentTime;
+
+ // todo: only allocate these for server objects
+ // get rid of 'DatObject', use the existing WeenieObj == null
WeenieObj = new WeenieObject();
ObjMaint = new ObjectMaint(this);
@@ -144,6 +152,8 @@ public PhysicsObj()
}
}
+ // calling DestroyObject() twice can be bad - especially if a new object has been re-created for it,
+ // such as the player already relogging back in
~PhysicsObj()
{
DestroyObject();
@@ -157,8 +167,8 @@ public void Destroy()
ScriptManager = null;
Hooks = null;
- if (State.HasFlag(PhysicsState.Static) && (State.HasFlag(PhysicsState.HasDefaultAnim) || State.HasFlag(PhysicsState.HasDefaultScript)))
- PhysicsEngine.RemoveStaticAnimatingObject(this);
+ //if (State.HasFlag(PhysicsState.Static) && (State.HasFlag(PhysicsState.HasDefaultAnim) || State.HasFlag(PhysicsState.HasDefaultScript)))
+ //PhysicsEngine.RemoveStaticAnimatingObject(this);
if (PhysicsScriptTable != null)
PhysicsScriptTable.Release();
@@ -227,7 +237,7 @@ public ObjCell AdjustPosition(Position position, Vector3 low_pt, bool dontCreate
return ObjCell.GetVisible(position.ObjCellID);
}
- var visibleCell = (Common.EnvCell)ObjCell.GetVisible(position.ObjCellID);
+ var visibleCell = (EnvCell)ObjCell.GetVisible(position.ObjCellID);
if (visibleCell == null) return null;
var point = position.LocalToGlobal(low_pt);
@@ -267,8 +277,8 @@ public void CallPES(uint pes, double delta) // long double?
return;
}
var upperBound = (float)delta;
- var randp = Common.Random.RollDice(0.0f, upperBound);
- var hook = new FPHook(PhysicsHookType.Velocity | PhysicsHookType.MotionTable | PhysicsHookType.Setup, PhysicsTimer_CurrentTime, randp, 0.0f, 1.0f, pes);
+ var randp = ThreadSafeRandom.Next(0.0f, upperBound);
+ var hook = new FPHook(PhysicsHookType.CallPES, PhysicsTimer_CurrentTime, randp, 0.0f, 1.0f, pes);
Hooks.Add(hook);
}
@@ -382,11 +392,15 @@ public TransitionState FindObjCollisions(Transition transition)
transition.SpherePath.ObstructionEthereal = ethereal;
var state = transition.ObjectInfo.State;
- var exemption = (WeenieObj == null || !WeenieObj.IsPlayer() || !state.HasFlag(ObjectInfoState.IsPlayer) ||
- state.HasFlag(ObjectInfoState.IsImpenetrable) || WeenieObj.IsImpenetable() ||
+
+ // TODO: reverse this check to make it more readable
+ // TODO: investigate not initting WeenieObj for DatObjects
+ var exemption = !( /*WeenieObj == null*/ DatObject || !WeenieObj.IsPlayer() || !state.HasFlag(ObjectInfoState.IsPlayer) ||
+ state.HasFlag(ObjectInfoState.IsImpenetrable) || WeenieObj.IsImpenetrable() ||
state.HasFlag(ObjectInfoState.IsPK) && WeenieObj.IsPK() || state.HasFlag(ObjectInfoState.IsPKLite) && WeenieObj.IsPKLite());
var missileIgnore = transition.ObjectInfo.MissileIgnore(this);
+
var isCreature = State.HasFlag(PhysicsState.Missile) || WeenieObj != null && WeenieObj.IsCreature();
//isCreature = false; // hack?
@@ -456,11 +470,66 @@ public TransitionState FindObjCollisions_Inner(Transition transition, Transition
return result;
}
+ public bool is_touching(PhysicsObj obj)
+ {
+ // custom for hotspots
+
+ // possible collision detection object types:
+ // - bsp
+ // - sphere
+ // - cylsphere
+
+ // player has 2 spheres
+ // hotspots appear to sphere or cylsphere?
+
+ // ensure same landblock
+ // no cross-landblock collision detection here,
+ // although it could be added if needed
+
+ if (CurLandblock != obj.CurLandblock)
+ return false;
+
+ var pSpheres = PartArray.GetSphere();
+
+ var spheres = obj.PartArray.GetSphere();
+ var cylspheres = obj.PartArray.GetCylSphere();
+
+ if (pSpheres.Count == 0 || (spheres.Count == 0 && cylspheres.Count == 0))
+ return false;
+
+ foreach (var pSphere in pSpheres)
+ {
+ foreach (var sphere in spheres)
+ {
+ // convert to landblock coordinates
+ var playerSphere = new Sphere(Position.Frame.LocalToGlobal(pSphere.Center), pSphere.Radius);
+ var globSphere = new Sphere(obj.Position.Frame.LocalToGlobal(sphere.Center), sphere.Radius);
+
+ if (playerSphere.Intersects(globSphere))
+ return true;
+ }
+
+ foreach (var cylsphere in cylspheres)
+ {
+ // convert to landblock coordinates
+ var center = Position.Frame.LocalToGlobal(pSphere.Center);
+ var lowpoint = obj.Position.Frame.LocalToGlobal(cylsphere.LowPoint);
+
+ var disp = center - lowpoint;
+ var radsum = pSphere.Radius + cylsphere.Radius - PhysicsGlobals.EPSILON;
+
+ if (cylsphere.CollidesWithSphere(pSphere, disp, radsum))
+ return true;
+ }
+ }
+ return false;
+ }
+
public SetPositionError ForceIntoCell(ObjCell newCell, Position pos)
{
if (newCell == null) return SetPositionError.NoCell;
set_frame(pos.Frame);
- if (!CurCell.Equals(newCell))
+ if (CurCell != newCell)
{
change_cell(newCell);
calc_cross_cells();
@@ -472,7 +541,7 @@ public double GetAutonomyBlipDistance()
{
if ((Position.ObjCellID & 0xFFFF) < 0x100) return 100.0f;
- return Players.Contains(this) ? 25.0f : 20.0f;
+ return IsPlayer ? 25.0f : 20.0f;
}
public BBox GetBoundingBox()
@@ -500,7 +569,7 @@ public double GetMaxConstraintDistance()
public PhysicsObj GetObjectA(uint objectID)
{
- return ObjectMaint.GetObjectA(objectID);
+ return ServerObjectManager.GetObjectA(objectID);
}
public float GetRadius()
@@ -906,6 +975,15 @@ public void MoveToObject_Internal(PhysicsObj obj, uint topLevelID, float objRadi
MovementManager.PerformMovement(mvs);
}
+ public void MoveToPosition(Position pos, MovementParameters movementParams)
+ {
+ var mvs = new MovementStruct();
+ mvs.Position = new Position(pos);
+ mvs.Type = MovementType.MoveToPosition;
+ mvs.Params = movementParams;
+ MovementManager.PerformMovement(mvs);
+ }
+
public void RemoveLinkAnimations()
{
if (PartArray != null)
@@ -955,7 +1033,7 @@ public void SetDiffusion(float start, float end, double delta)
return;
}
var hook = new FPHook(HookType.Velocity, PhysicsTimer.CurrentTime, delta, start, end, 0);
- Hooks.Add(hook);*/
+ Hooks.AddLast(hook);*/
}
@@ -973,7 +1051,7 @@ public void SetLuminosity(float start, float end, double delta)
PartArray.SetLuminosityInternal(end);
return;
}
- var hook = new FPHook(PhysicsHookType.MotionTable | PhysicsHookType.Setup, PhysicsTimer_CurrentTime, delta, start, end, 0);
+ var hook = new FPHook(PhysicsHookType.Luminosity, PhysicsTimer_CurrentTime, delta, start, end, 0);
Hooks.Add(hook);
}
@@ -1007,7 +1085,7 @@ public void SetPartDiffusion(int part, float start, float end, double delta)
PartArray.SetPartDiffusionInternal(part, end);
return;
}
- var hook = new FPHook(PhysicsHookType.Velocity | PhysicsHookType.MotionTable, PhysicsTimer_CurrentTime, delta, start, end, part);
+ var hook = new FPHook(PhysicsHookType.PartDiffusion, PhysicsTimer_CurrentTime, delta, start, end, part);
Hooks.Add(hook);
}
@@ -1027,7 +1105,7 @@ public void SetPartLuminosity(int part, float start, float end, double delta)
PartArray.SetPartLuminosityInternal(part, end);
return;
}
- var hook = new FPHook(PhysicsHookType.Velocity | PhysicsHookType.Setup, PhysicsTimer_CurrentTime, delta, start, end, part);
+ var hook = new FPHook(PhysicsHookType.PartLuminosity, PhysicsTimer_CurrentTime, delta, start, end, part);
Hooks.Add(hook);
}
@@ -1045,7 +1123,7 @@ public void SetPartTranslucency(int partIdx, float startTrans, float endTrans, d
PartArray.SetPartTranslucencyInternal(partIdx, endTrans);
return;
}
- var hook = new FPHook(PhysicsHookType.MotionTable, PhysicsTimer_CurrentTime, delta, startTrans, endTrans, partIdx);
+ var hook = new FPHook(PhysicsHookType.PartTranslucency, PhysicsTimer_CurrentTime, delta, startTrans, endTrans, partIdx);
Hooks.Add(hook);
}
@@ -1066,14 +1144,6 @@ public bool SetPlacementFrameInternal(int frameID)
return result;
}
- public void SetPlayer()
- {
- if (Players.Contains(this))
- Players.Add(this);
-
- IsPlayer = true;
- }
-
public SetPositionError SetPosition(SetPosition setPos)
{
var transition = Transition.MakeTransition();
@@ -1111,6 +1181,9 @@ public bool SetPositionInternal(Transition transition)
return true;
}
+ // modified: maintain consistency for Position.Frame in change_cell
+ set_frame(curPos.Frame);
+
if (transitCell.Equals(CurCell))
{
Position.ObjCellID = curPos.ObjCellID;
@@ -1132,7 +1205,7 @@ public bool SetPositionInternal(Transition transition)
change_cell(transitCell);
}
- set_frame(curPos.Frame);
+ //set_frame(curPos.Frame);
var collisions = transition.CollisionInfo;
@@ -1223,16 +1296,54 @@ public SetPositionError SetPositionInternal(Position pos, SetPosition setPos, Tr
if (transition.SpherePath.CurCell == null) return SetPositionError.NoCell;
+ // custom:
+ // test for non-ethereal spell projectile collision on world entry
+ /*var spellCollide = WeenieObj.WorldObject is SpellProjectile && transition.CollisionInfo.CollideObject.Count > 0 && !PropertyManager.GetBool("spell_projectile_ethereal").Item;
+
+ if (spellCollide)
+ {
+ // send initial CO as ethereal
+ WeenieObj.WorldObject.SetProperty(PropertyBool.Ethereal, true);
+ }*/
+
+ if (entering_world && transition.SpherePath.CurPos.Landblock != pos.Landblock)
+ {
+ // AdjustToOutside and find_cell_list can inconsistently result in 2 different cells for edges
+ // if something directly on a landblock edge has resulted in a different landblock from find_cell_list, discard completely
+
+ // this can also (more legitimately) happen even if the object isn't directly on landblock edge, but is near it
+ // an object trying to spawn on a hillside near a landblock edge might get pushed slightly during spawning,
+ // resulting in a successful spawn in a neighboring landblock. we don't handle adjustments to the actual landblock reference in here
+
+ // ideally CellArray.LoadCells = false would be passed to find_cell_list to prevent it from even attempting to load an unloaded neighboring landblock
+
+ Console.WriteLine($"{Name} ({ID:X8}) AddPhysicsObj() - {pos.ShortLoc()} resulted in {transition.SpherePath.CurPos.ShortLoc()}, discarding");
+ return SetPositionError.NoValidPosition;
+ }
+
if (!SetPositionInternal(transition))
return SetPositionError.GeneralFailure;
+ //if (spellCollide)
+ //handle_all_collisions(transition.CollisionInfo, false, false);
+
return SetPositionError.OK;
}
public SetPositionError SetPositionInternal(SetPosition setPos, Transition transition)
{
- if (setPos.Flags.HasFlag(SetPositionFlags.RandomScatter))
- return SetScatterPositionInternal(setPos, transition);
+ var wo = WeenieObj.WorldObject;
+
+ if (wo == null)
+ return SetPositionError.GeneralFailure;
+
+ //if (setPos.Flags.HasFlag(SetPositionFlags.RandomScatter))
+ //return SetScatterPositionInternal(setPos, transition);
+ /*if (wo.ScatterPos != null)
+ {
+ wo.ScatterPos.Flags |= setPos.Flags;
+ return SetScatterPositionInternal(wo.ScatterPos, transition);
+ }*/
// frame ref?
var result = SetPositionInternal(setPos.Pos, setPos, transition);
@@ -1245,10 +1356,12 @@ public SetPositionError SetPositionInternal(SetPosition setPos, Transition trans
public SetPositionError SetPositionSimple(Position pos, bool sliding)
{
- var flags = sliding ? 4114 : 4098; // ??
var setPos = new SetPosition();
setPos.Pos = pos;
- setPos.Flags = (SetPositionFlags)flags;
+ setPos.Flags = SetPositionFlags.Teleport | SetPositionFlags.SendPositionEvent;
+
+ if (sliding)
+ setPos.Flags |= SetPositionFlags.Slide;
return SetPosition(setPos);
}
@@ -1261,7 +1374,7 @@ public void SetScale(float scale, double delta = 0.0)
PartArray.SetScaleInternal(new Vector3(scale, scale, scale));
return;
}
- var hook = new FPHook((PhysicsHookType)0, PhysicsTimer_CurrentTime, delta, Scale, scale, 0);
+ var hook = new FPHook(PhysicsHookType.Scale, PhysicsTimer_CurrentTime, delta, Scale, scale, 0);
Hooks.Add(hook);
}
@@ -1272,22 +1385,97 @@ public void SetScaleStatic(float scale)
PartArray.SetScaleInternal(new Vector3(scale, scale, scale));
}
+ public static float ScatterThreshold_Z = 10.0f;
+
public SetPositionError SetScatterPositionInternal(SetPosition setPos, Transition transition)
{
var result = SetPositionError.GeneralFailure;
for (var i = 0; i < setPos.NumTries; i++)
{
- Position newPos = null;
- var origin = newPos.Frame.Origin;
- newPos = setPos.Pos; // ??
+ var newPos = new Position(setPos.Pos);
+
+ newPos.Frame.Origin.X += (float)ThreadSafeRandom.Next(-1.0f, 1.0f) * setPos.RadX;
+ newPos.Frame.Origin.Y += (float)ThreadSafeRandom.Next(-1.0f, 1.0f) * setPos.RadY;
+
+ // customized
+ if ((newPos.ObjCellID & 0xFFFF) < 0x100)
+ {
+ newPos.Frame.Origin.X = MathExtensions.Clamp(newPos.Frame.Origin.X, 0.5f, 191.5f);
+ newPos.Frame.Origin.Y = MathExtensions.Clamp(newPos.Frame.Origin.Y, 0.5f, 191.5f);
+ }
+
+ // get cell for this position
+ var indoors = (newPos.ObjCellID & 0xFFFF) >= 0x100;
+
+ if (!indoors)
+ {
+ LandDefs.AdjustToOutside(newPos);
+
+ // ensure walkable slope
+ var landcell = (LandCell)LScape.get_landcell(newPos.ObjCellID);
- newPos.Frame.Origin.X += Common.Random.RollDice(-1.0f, 1.0f) * setPos.RadX;
- newPos.Frame.Origin.Y += Common.Random.RollDice(-1.0f, 1.0f) * setPos.RadY;
+ Polygon walkable = null;
+ var terrainPoly = landcell.find_terrain_poly(newPos.Frame.Origin, ref walkable);
+ if (walkable == null || !is_valid_walkable(walkable.Plane.Normal)) continue;
+
+ // account for buildings
+ // if original position was outside, and scatter position is in a building, should we even try to spawn?
+ // compare: rabbits occasionally spawning in buildings in yaraq,
+ // vs. lich tower @ 3D31FFFF
+
+ var sortCell = LScape.get_landcell(newPos.ObjCellID) as SortCell;
+ if (sortCell == null || !sortCell.has_building())
+ {
+ // set to ground pos
+ var landblock = LScape.get_landblock(newPos.ObjCellID);
+ var groundZ = landblock.GetZ(newPos.Frame.Origin) + 0.05f;
+
+ if (Math.Abs(newPos.Frame.Origin.Z - groundZ) > ScatterThreshold_Z)
+ Console.WriteLine($"{Name} ({ID:X8}).SetScatterPositionInternal() - tried to spawn outdoor object @ {newPos} ground Z {groundZ} (diff: {newPos.Frame.Origin.Z - groundZ}), investigate ScatterThreshold_Z");
+ else
+ newPos.Frame.Origin.Z = groundZ;
+
+ }
+ //else
+ //indoors = true;
+
+ /*if (sortCell != null && sortCell.has_building())
+ {
+ var building = sortCell.Building;
+
+ var minZ = building.GetMinZ();
+
+ if (minZ > 0 && minZ < float.MaxValue)
+ newPos.Frame.Origin.Z += minZ;
+
+ //indoors = true;
+ }*/
+ }
+ else
+ {
+ var landblock = LScape.get_landblock(newPos.ObjCellID);
+ var envcells = landblock.get_envcells();
+ var found = false;
+ foreach (var envCell in envcells)
+ {
+ if (envCell.point_in_cell(newPos.Frame.Origin))
+ {
+ newPos.ObjCellID = envCell.ID;
+ found = true;
+ break;
+ }
+ }
+ if (!found) continue;
+ }
result = SetPositionInternal(newPos, setPos, transition);
if (result == SetPositionError.OK) break;
}
+
+ //if (result != SetPositionError.OK)
+ //Console.WriteLine($"Couldn't spawn {Name} after {setPos.NumTries} retries @ {setPos.Pos}");
+
return result;
}
@@ -1307,7 +1495,7 @@ public void SetTranslucency(float translucency, double delta)
if (PartArray != null) PartArray.SetTranslucencyInternal(Translucency);
return;
}
- var hook = new FPHook(PhysicsHookType.Setup, PhysicsTimer_CurrentTime, delta, 0.0f, translucency, 0);
+ var hook = new FPHook(PhysicsHookType.Translucency, PhysicsTimer_CurrentTime, delta, 0.0f, translucency, 0);
Hooks.Add(hook);
}
@@ -1320,7 +1508,7 @@ public void SetTranslucency2(float startTrans, float endTrans, double delta)
if (PartArray != null) PartArray.SetTranslucencyInternal(startTrans);
return;
}
- var hook = new FPHook(PhysicsHookType.Setup, PhysicsTimer_CurrentTime, delta, startTrans, endTrans, 0);
+ var hook = new FPHook(PhysicsHookType.Translucency, PhysicsTimer_CurrentTime, delta, startTrans, endTrans, 0);
Hooks.Add(hook);
}
@@ -1406,14 +1594,15 @@ public void TurnToHeading(MovementParameters movementParams)
MovementManager.PerformMovement(mvs);
}
- public void TurnToObject(uint objectID, MovementParameters movementParams)
+ public bool TurnToObject(PhysicsObj obj, MovementParameters movementParams)
{
- if (ObjMaint == null) return;
- var obj = ObjectMaint.GetObjectA(objectID);
- if (obj == null) return;
+ if (obj == null) return false;
+
var parent = obj.Parent != null ? obj.Parent : obj;
- TurnToObject_Internal(objectID, parent.ID, movementParams);
+ TurnToObject_Internal(obj.ID, parent.ID, movementParams);
+
+ return true;
}
public void TurnToObject_Internal(uint objectID, uint topLevelID, MovementParameters movementParams)
@@ -1440,7 +1629,7 @@ public void TurnToObject_Internal(uint objectID, uint topLevelID, MovementParame
public void UpdateChild(PhysicsObj childObj, int partIdx, AFrame childFrame)
{
- var frame = partIdx == -1 || partIdx >= PartArray.NumParts ?
+ var frame = partIdx == -1 ?
AFrame.Combine(Position.Frame, childFrame) : AFrame.Combine(PartArray.Parts[partIdx].Pos.Frame, childFrame);
childObj.set_frame(frame);
@@ -1457,12 +1646,14 @@ public void UpdateChildrenInternal()
UpdateChild(Children.Objects[i], Children.PartNumbers[i], Children.Frames[i]);
}
+ public int InitialUpdates;
+
public void UpdateObjectInternal(double quantum)
{
- if (!TransientState.HasFlag(TransientStateFlags.Active) || CurCell == null)
+ if ((TransientState & TransientStateFlags.Active) == 0 || CurCell == null)
return;
- if (TransientState.HasFlag(TransientStateFlags.CheckEthereal))
+ if ((TransientState & TransientStateFlags.CheckEthereal) != 0)
set_ethereal(false, false);
JumpedThisFrame = false;
@@ -1475,21 +1666,30 @@ public void UpdateObjectInternal(double quantum)
{
CachedVelocity = Vector3.Zero;
set_frame(newPos.Frame);
+ InitialUpdates++;
}
else
{
- if (State.HasFlag(PhysicsState.AlignPath))
+ if ((State & PhysicsState.AlignPath) != 0)
{
var diff = newPos.Frame.Origin - Position.Frame.Origin;
- newPos.Frame.set_vector_heading(diff.Normalize());
+ newPos.Frame.set_vector_heading(Vector3.Normalize(diff));
}
- else if (State.HasFlag(PhysicsState.Sledding) && Velocity != Vector3.Zero)
- newPos.Frame.set_vector_heading(Velocity.Normalize());
+ else if ((State & PhysicsState.Sledding) != 0 && Velocity != Vector3.Zero)
+ newPos.Frame.set_vector_heading(Vector3.Normalize(Velocity));
+ }
+
+ if (GetBlockDist(Position, newPos) > 1)
+ {
+ Console.WriteLine($"WARNING: failed transition for {Name} from {Position} to {newPos}");
+ return;
}
var transit = transition(Position, newPos, false);
- if (transit != null)
+
+ // temporarily modified while debug path is examined
+ if (transit != null && transit.SpherePath.CurCell != null)
{
CachedVelocity = Position.GetOffset(transit.SpherePath.CurPos) / (float)quantum;
@@ -1497,6 +1697,11 @@ public void UpdateObjectInternal(double quantum)
}
else
{
+ if (IsPlayer)
+ Console.WriteLine($"{Name} ({ID:X8}).UpdateObjectInternal({quantum}) - failed transition from {Position} to {newPos}");
+ else if (transit != null && transit.SpherePath.CurCell == null)
+ Console.WriteLine($"{Name} ({ID:X8}).UpdateObjectInternal({quantum}) - avoided CurCell=null from {Position} to {newPos}");
+
newPos.Frame.Origin = Position.Frame.Origin;
set_initial_frame(newPos.Frame);
CachedVelocity = Vector3.Zero;
@@ -1504,12 +1709,13 @@ public void UpdateObjectInternal(double quantum)
}
else
{
- if (MovementManager == null && TransientState.HasFlag(TransientStateFlags.OnWalkable))
+ if (MovementManager == null && (TransientState & TransientStateFlags.OnWalkable) != 0)
TransientState &= ~TransientStateFlags.Active;
newPos.Frame.Origin = Position.Frame.Origin;
set_frame(newPos.Frame);
CachedVelocity = Vector3.Zero;
+ InitialUpdates++;
}
if (DetectionManager != null) DetectionManager.CheckDetection();
@@ -1527,14 +1733,52 @@ public void UpdateObjectInternal(double quantum)
if (ScriptManager != null) ScriptManager.UpdateScripts();
}
- public void UpdateObjectInternalServer(double quantum)
+ public static int GetBlockDist(Position a, Position b)
+ {
+ // protection, figure out FastTeleport state
+ if (a == null || b == null)
+ return 0;
+
+ return GetBlockDist(a.ObjCellID, b.ObjCellID);
+ }
+
+ public static int GetBlockDist(uint a, uint b)
+ {
+ var lbx_a = a >> 24;
+ var lby_a = (a >> 16) & 0xFF;
+
+ var lbx_b = b >> 24;
+ var lby_b = (b >> 16) & 0xFF;
+
+ var dx = (int)Math.Abs((int)lbx_a - lbx_b);
+ var dy = (int)Math.Abs((int)lby_a - lby_b);
+
+ return Math.Max(dx, dy);
+ }
+
+ ///
+ /// This is for legacy movement system
+ ///
+ public bool UpdateObjectInternalServer(double quantum)
{
+ //var offsetFrame = new AFrame();
+ //UpdatePhysicsInternal((float)quantum, ref offsetFrame);
+ if (GetBlockDist(Position, RequestPos) > 1)
+ {
+ Console.WriteLine($"WARNING: failed transition for {Name} from {Position} to {RequestPos}");
+ return false;
+ }
+
+ var requestCell = RequestPos.ObjCellID;
+
var transit = transition(Position, RequestPos, false);
if (transit != null)
{
CachedVelocity = Position.GetOffset(transit.SpherePath.CurPos) / (float)quantum;
SetPositionInternal(transit);
}
+ else
+ Console.WriteLine($"{Name}.UpdateObjectInternalServer({quantum}) - failed transition from {Position} to {RequestPos}");
if (DetectionManager != null) DetectionManager.CheckDetection();
@@ -1549,6 +1793,8 @@ public void UpdateObjectInternalServer(double quantum)
if (ParticleManager != null) ParticleManager.UpdateParticles();
if (ScriptManager != null) ScriptManager.UpdateScripts();
+
+ return requestCell >> 16 != 0x18A || CurCell?.ID >> 16 == requestCell >> 16;
}
public void UpdateAnimationInternal(double quantum)
@@ -1594,7 +1840,7 @@ public void UpdatePhysicsInternal(float quantum, ref AFrame frameOffset)
{
if (velocity_mag2 > PhysicsGlobals.MaxVelocitySquared)
{
- Velocity = Velocity.Normalize() * PhysicsGlobals.MaxVelocity;
+ Velocity = Vector3.Normalize(Velocity) * PhysicsGlobals.MaxVelocity;
velocity_mag2 = PhysicsGlobals.MaxVelocitySquared;
}
// todo: collision normals
@@ -1635,11 +1881,6 @@ public void UpdatePositionInternal(double quantum, ref AFrame newFrame)
process_hooks();
}
- public void UpdatePositionInternalServer(double quantum, ref AFrame offsetFrame)
- {
- UpdatePositionInternal(quantum, ref offsetFrame);
- }
-
public void UpdateViewerDistance(float cypt, Vector3 heading)
{
if (PartArray != null) PartArray.UpdateViewerDistance(cypt, heading);
@@ -1827,37 +2068,6 @@ public void attack(AttackCone attackCone)
report_attacks(attackInfo);
}
- public AtkCollisionProfile build_collision_profile(PhysicsObj obj, bool prev_has_contact, Vector3 velocityCollide)
- {
- AtkCollisionProfile profile = null;
-
- if (!State.HasFlag(PhysicsState.Missile))
- profile = (AtkCollisionProfile)build_collision_profile(obj, velocityCollide, prev_has_contact,
- obj.State.HasFlag(PhysicsState.Missile), obj.TransientState.HasFlag(TransientStateFlags.Contact));
- else
- {
- profile = new AtkCollisionProfile();
- profile.ID = obj.ID;
- profile.Part = -1;
- profile.Location = obj.Position.DetermineQuadrant(obj.GetHeight(), Position);
- }
- return profile;
- }
-
- public static ObjCollisionProfile build_collision_profile(PhysicsObj obj, Vector3 velocity, bool amIInContact, bool objIsMissile, bool objHasContact)
- {
- if (obj.WeenieObj != null /* && vfptr */) return null;
- var prof = new ObjCollisionProfile();
- prof.Velocity = velocity;
- if (objIsMissile)
- prof.Flags |= ObjCollisionProfileFlags.Missile;
- if (objHasContact)
- prof.Flags |= ObjCollisionProfileFlags.Contact;
- if (amIInContact)
- prof.Flags |= ObjCollisionProfileFlags.MyContact;
- return prof;
- }
-
public void calc_acceleration()
{
if (TransientState.HasFlag(TransientStateFlags.Contact) && TransientState.HasFlag(TransientStateFlags.OnWalkable))
@@ -1970,7 +2180,7 @@ public void change_cell_server(ObjCell newCell)
enter_cell_server(newCell);
}
- public int check_attack(Position attackerPos, float attackerScale, AttackCone attackCone, float attackerAttackRadius)
+ public Quadrant check_attack(Position attackerPos, float attackerScale, AttackCone attackCone, float attackerAttackRadius)
{
if (Parent != null || State.HasFlag(PhysicsState.IgnoreCollisions) || State.HasFlag(PhysicsState.ReportCollisionsAsEnvironment))
return 0;
@@ -2059,13 +2269,84 @@ public void destroy_particle_manager()
ParticleManager = null;
}
+ ///
+ /// This is to mitigate possible decal crashes w/ CO messages being sent
+ /// for objects when the client landblock is very early in the loading state
+ ///
+ public static TimeSpan TeleportCreateObjectDelay = TimeSpan.FromSeconds(1);
+
public void enqueue_objs(IEnumerable newlyVisible)
{
- /*var player = WeenieObj.WorldObject as Player;
- if (player == null) return;
+ /*if (!IsPlayer || !(WeenieObj.WorldObject is Player player))
+ return;
- foreach (var obj in newlyVisible)
- player.TrackObject(obj.WeenieObj.WorldObject);*/
+ if (DateTime.UtcNow - player.LastTeleportTime < TeleportCreateObjectDelay)
+ {
+ var actionChain = new ActionChain();
+ actionChain.AddDelaySeconds(TeleportCreateObjectDelay.TotalSeconds);
+ actionChain.AddAction(player, () =>
+ {
+ foreach (var obj in newlyVisible)
+ {
+ var wo = obj.WeenieObj.WorldObject;
+ if (wo != null)
+ player.TrackObject(wo, true);
+ }
+ });
+ actionChain.EnqueueChain();
+ }
+ else
+ {
+ foreach (var obj in newlyVisible)
+ {
+ var wo = obj.WeenieObj.WorldObject;
+
+ if (wo == null)
+ continue;
+
+ if (wo.Teleporting)
+ {
+ // ensure post-teleport position is sent
+ var actionChain = new ActionChain();
+ actionChain.AddDelayForOneTick();
+ actionChain.AddAction(player, () => player.TrackObject(wo));
+ actionChain.EnqueueChain();
+ }
+ else
+ player.TrackObject(wo);
+ }
+ }*/
+ }
+
+ public void enqueue_obj(PhysicsObj newlyVisible)
+ {
+ /*if (!IsPlayer || !(WeenieObj.WorldObject is Player player))
+ return;
+
+ var wo = newlyVisible.WeenieObj.WorldObject;
+ if (wo == null)
+ return;
+
+ if (DateTime.UtcNow - player.LastTeleportTime < TeleportCreateObjectDelay)
+ {
+ var actionChain = new ActionChain();
+ actionChain.AddDelaySeconds(TeleportCreateObjectDelay.TotalSeconds);
+ actionChain.AddAction(player, () => player.TrackObject(wo, true));
+ actionChain.EnqueueChain();
+ }
+ else
+ {
+ if (wo.Teleporting)
+ {
+ // ensure post-teleport position is sent
+ var actionChain = new ActionChain();
+ actionChain.AddDelayForOneTick();
+ actionChain.AddAction(player, () => player.TrackObject(wo));
+ actionChain.EnqueueChain();
+ }
+ else
+ player.TrackObject(wo);
+ }*/
}
public void enter_cell(ObjCell newCell)
@@ -2076,7 +2357,7 @@ public void enter_cell(ObjCell newCell)
child.enter_cell(newCell);
CurCell = newCell;
- Position.ObjCellID = newCell.ID;
+ Position.ObjCellID = newCell.ID; // warning: Position will be in an inconsistent state here, until set_frame() is run!
if (PartArray != null && !State.HasFlag(PhysicsState.ParticleEmitter))
PartArray.SetCellID(newCell.ID);
@@ -2090,46 +2371,48 @@ public void enter_cell(ObjCell newCell)
public void enter_cell_server(ObjCell newCell)
{
+ //Console.WriteLine($"{Name}.enter_cell_server({newCell.ID:X8})");
+
enter_cell(newCell);
- RequestPos.ObjCellID = newCell.ID;
+ RequestPos.ObjCellID = newCell.ID; // document this control flow better
- // handle indoor cell visibility
- //if ((newCell.ID & 0xFFFF) >= 0x100)
- //{
- if (IsPlayer)
- {
- // player entering new indoor cell
- var newlyVisible = handle_visible_cells();
- enqueue_objs(newlyVisible);
- }
+ // sync location for initial CO
+ //if (entering_world)
+ //WeenieObj.WorldObject.SyncLocation();
- foreach (var player in Players)
- {
- // is other player in same indoor landblock?
- if (player.CurCell != null && (player.CurCell.ID & 0xFFFF) >= 0x100 && player.CurCell.ID >> 16 == newCell.ID >> 16)
- {
- var envCell = player.CurCell as EnvCell;
- if (envCell != null)
- {
- if (envCell.VisibleCells.ContainsKey(newCell.ID & 0xFFFF))
- {
- //Console.WriteLine($"Informing {player.WeenieObj.WorldObject.Name} about {WeenieObj.WorldObject.Name}");
+ // handle self
+ if (IsPlayer)
+ {
+ var newlyVisible = handle_visible_cells();
+ enqueue_objs(newlyVisible);
+ }
+ else
+ {
+ handle_visible_cells_non_player();
+ }
- // inform other player about this object
- var newlyVisible = player.handle_visible_cells();
- player.enqueue_objs(newlyVisible);
- }
- }
- }
- }
- //}
- //Console.WriteLine("Cell: " + newCell.ID.ToString("X8") + " (" + newCell.ShadowObjectList.Count + ")");
+ // handle known players
+ foreach (var player in ObjMaint.GetKnownPlayersValues())
+ {
+ var added = player.handle_visible_obj(this);
+
+ if (added)
+ player.enqueue_obj(this);
+ }
}
+ public bool entering_world;
+
public bool enter_world(Position pos)
{
+ entering_world = true;
+
store_position(pos);
- return enter_world(true);
+ bool slide = ProjectileTarget == null /*|| WeenieObj.WorldObject is SpellProjectile*/;
+ var result = enter_world(slide);
+
+ entering_world = false;
+ return result;
}
public bool enter_world(bool slide)
@@ -2165,7 +2448,7 @@ public bool ethereal_check_for_collisions()
{
foreach (var shadowObj in ShadowObjects.Values)
{
- if (shadowObj.Cell.check_collisions(this))
+ if (shadowObj.Cell != null && shadowObj.Cell.check_collisions(this))
return true;
}
return false;
@@ -2230,6 +2513,21 @@ public double get_distance_to_object(PhysicsObj obj, bool use_cyls)
return Position.CylinderDistance(curRadius, curHeight, Position, radius, height, obj.Position);
}
+ // custom, based on above
+ public double get_distance_sq_to_object(PhysicsObj obj, bool use_cyls)
+ {
+ if (!use_cyls)
+ return Position.DistanceSquared(obj.Position);
+
+ var height = obj.PartArray != null ? obj.PartArray.GetHeight() : 0.0f;
+ var radius = obj.PartArray != null ? obj.PartArray.GetRadius() : 0.0f;
+
+ var curHeight = PartArray != null ? PartArray.GetHeight() : 0.0f;
+ var curRadius = PartArray != null ? PartArray.GetRadius() : 0.0f;
+
+ return Position.CylinderDistanceSq(curRadius, curHeight, Position, radius, height, obj.Position);
+ }
+
public AFrame get_frame()
{
return Position.Frame;
@@ -2401,7 +2699,7 @@ public bool handle_all_collisions(CollisionInfo collisions, bool prev_has_contac
}
else
{
- Velocity = Vector3.Zero;
+ Velocity = Vector3.Zero; // gets objects stuck in falling state?
if (collisions.FramesStationaryFall == 3)
{
TransientState &= ~TransientStateFlags.StationaryComplete;
@@ -2445,8 +2743,8 @@ public List handle_visible_cells()
//Console.WriteLine(visibleObject.Name);
// get the difference between current and previous visible
- var newlyVisible = visibleObjects.Except(ObjMaint.VisibleObjectTable.Values).ToList();
- var newlyOccluded = ObjMaint.VisibleObjectTable.Values.Except(visibleObjects).ToList();
+ //var newlyVisible = visibleObjects.Except(ObjMaint.VisibleObjects.Values).ToList();
+ var newlyOccluded = ObjMaint.GetVisibleObjectsValues().Except(visibleObjects).ToList();
//Console.WriteLine("Newly visible objects: " + newlyVisible.Count);
//Console.WriteLine("Newly occluded objects: " + newlyOccluded.Count);
//foreach (var obj in newlyOccluded)
@@ -2454,6 +2752,7 @@ public List handle_visible_cells()
// add newly visible objects, and get the previously unknowns
var createObjs = ObjMaint.AddVisibleObjects(visibleObjects);
+ //Console.WriteLine("Create objects: " + createObjs.Count);
/*if (createObjs.Count != newlyVisible.Count)
{
Console.WriteLine($"Create objs differs from newly visible ({createObjs.Count} vs. {newlyVisible.Count})");
@@ -2471,6 +2770,72 @@ public List handle_visible_cells()
return createObjs;
}
+ public void handle_visible_cells_non_player()
+ {
+ if (WeenieObj.IsMonster)
+ {
+ // players and combat pets
+ var visibleTargets = ObjMaint.GetVisibleObjects(CurCell, ObjectMaint.VisibleObjectType.AttackTargets);
+
+ var newTargets = ObjMaint.AddVisibleTargets(visibleTargets);
+ }
+ else
+ {
+ // everything except monsters
+ // usually these are server objects whose position never changes
+ var knownPlayers = ObjectMaint.InitialClamp ? ObjMaint.GetVisibleObjectsDist(CurCell, ObjectMaint.VisibleObjectType.Players)
+ : ObjMaint.GetVisibleObjects(CurCell, ObjectMaint.VisibleObjectType.Players);
+
+ ObjMaint.AddKnownPlayers(knownPlayers);
+ }
+
+ if (WeenieObj.IsCombatPet)
+ {
+ var visibleMonsters = ObjMaint.GetVisibleObjects(CurCell, ObjectMaint.VisibleObjectType.AttackTargets);
+
+ var newTargets = ObjMaint.AddVisibleTargets(visibleMonsters);
+ }
+ }
+
+ public bool handle_visible_obj(PhysicsObj obj)
+ {
+ if (CurCell == null || obj.CurCell == null)
+ {
+ if (CurCell == null)
+ Console.WriteLine($"{Name}.handle_visible_obj({obj.Name}): CurCell null");
+ else
+ Console.WriteLine($"{Name}.handle_visible_obj({obj.Name}): obj.CurCell null");
+
+ return false;
+ }
+
+ var isVisible = CurCell.IsVisible(obj.CurCell);
+
+ if (isVisible)
+ {
+ var prevKnown = ObjMaint.KnownObjectsContainsKey(obj.ID);
+
+ var newlyVisible = ObjMaint.AddVisibleObject(obj);
+
+ if (newlyVisible)
+ {
+ ObjMaint.AddKnownObject(obj);
+ ObjMaint.RemoveObjectToBeDestroyed(obj);
+ }
+
+ return !prevKnown && newlyVisible;
+ }
+ else
+ {
+ var newlyOccluded = ObjMaint.VisibleObjectsContainsKey(obj.ID);
+
+ if (newlyOccluded)
+ ObjMaint.AddObjectToBeDestroyed(obj);
+
+ return false;
+ }
+ }
+
public bool is_completely_visible()
{
if (CurCell == null || NumShadowObjects == 0)
@@ -2492,7 +2857,7 @@ public bool is_newer(int timestamp, int newTime)
return timestamp < newTime;
}
- public bool is_valid_walkable(Vector3 normal)
+ public static bool is_valid_walkable(Vector3 normal)
{
return normal.Z >= PhysicsGlobals.FloorZ;
}
@@ -2579,28 +2944,29 @@ public static PhysicsObj makeObject(PhysicsObj template)
return obj;
}
- public static PhysicsObj makeObject(uint dataDID, uint objectIID, bool dynamic)
+ public bool IsSightObj;
+
+ public static PhysicsObj makeObject(uint dataDID, uint objectIID, bool dynamic, bool sightObj = false)
{
var obj = new PhysicsObj();
obj.InitObjectBegin(objectIID, dynamic);
obj.InitPartArrayObject(dataDID, true);
obj.InitObjectEnd();
+
+ // for direct visibility testing
+ obj.IsSightObj = sightObj;
+
return obj;
}
public static PhysicsObj makeParticleObject(int numParts, Sphere sortingSphere)
{
var particle = new PhysicsObj();
- particle.State = PhysicsState.Static | PhysicsState.ParticleEmitter;
+ particle.State = PhysicsState.Static | PhysicsState.ReportCollisions;
particle.PartArray = PartArray.CreateParticle(particle, numParts, sortingSphere);
return particle;
}
- public bool motions_pending()
- {
- return MovementManager != null && MovementManager.motions_pending();
- }
-
public bool movement_is_autonomous()
{
return LastMoveWasAutonomous;
@@ -2758,32 +3124,32 @@ public bool prepare_to_leave_visibility()
return true;
}
- public void process_fp_hook(int type, float curr_value, Object userData)
+ public void process_fp_hook(PhysicsHookType type, float curr_value, Object userData)
{
switch (type)
{
- case 0:
+ case PhysicsHookType.Scale:
SetScaleStatic(curr_value);
break;
- case 1:
+ case PhysicsHookType.Translucency:
SetTranslucencyInternal(curr_value);
break;
- case 2:
+ case PhysicsHookType.PartTranslucency:
if (PartArray != null) PartArray.SetPartTranslucencyInternal((int)userData, curr_value);
break;
- case 3:
+ case PhysicsHookType.Luminosity:
if (PartArray != null) PartArray.SetLuminosityInternal(curr_value);
break;
- case 5: // combined types?
+ case PhysicsHookType.PartLuminosity:
if (PartArray != null) PartArray.SetPartLuminosityInternal((int)userData, curr_value);
break;
- case 4:
+ case PhysicsHookType.Diffusion:
if (PartArray != null) PartArray.SetDiffusionInternal(curr_value);
break;
- case 6:
+ case PhysicsHookType.PartDiffusion:
if (PartArray != null) PartArray.SetPartDiffusionInternal((int)userData, curr_value);
break;
- case 7:
+ case PhysicsHookType.CallPES:
CallPESInternal((uint)userData, curr_value);
break;
}
@@ -2860,7 +3226,7 @@ public void remove_parts(ObjCell objCell)
public void remove_shadows_from_cells()
{
- foreach (var shadowObj in ShadowObjects.Values.ToList())
+ foreach (var shadowObj in ShadowObjects.Values)
{
if (shadowObj.Cell == null) continue;
var cell = shadowObj.Cell;
@@ -2940,7 +3306,7 @@ public void report_collision_start()
foreach (var objectID in CollisionTable.Keys)
{
- var obj = ObjectMaint.GetObjectA(objectID);
+ var obj = ServerObjectManager.GetObjectA(objectID);
if (obj != null)
report_object_collision(obj, TransientState.HasFlag(TransientStateFlags.Contact));
}
@@ -2977,35 +3343,36 @@ public bool report_object_collision(PhysicsObj obj, bool prev_has_contact)
var velocityCollide = Velocity - obj.Velocity;
bool collided = false;
+
if (!obj.State.HasFlag(PhysicsState.IgnoreCollisions))
{
if (State.HasFlag(PhysicsState.ReportCollisions) && WeenieObj != null)
{
var profile = build_collision_profile(obj, prev_has_contact, velocityCollide);
+
WeenieObj.DoCollision(profile, ObjID, obj);
- collided = true;
- if (!CollisionTable.ContainsKey(obj.ID))
- CollisionTable.Add(obj.ID, new CollisionRecord() { TouchedTime = PhysicsTimer.CurrentTime });
- else
- CollisionTable[obj.ID] = new CollisionRecord() { TouchedTime = PhysicsTimer.CurrentTime };
+ collided = true;
}
if (State.HasFlag(PhysicsState.Missile))
State &= ~(PhysicsState.Missile | PhysicsState.AlignPath | PhysicsState.PathClipped);
}
- if (obj.State.HasFlag(PhysicsState.ReportCollisions) && !State.HasFlag(PhysicsState.IgnoreCollisions) && WeenieObj != null)
+ if (obj.State.HasFlag(PhysicsState.ReportCollisions) && !State.HasFlag(PhysicsState.IgnoreCollisions) && obj.WeenieObj != null)
{
- var profile = obj.build_collision_profile(this, prev_has_contact, velocityCollide);
+ // acclient might have a bug here,
+ // prev_has_contact and missile state params swapped?
+ var profile = obj.build_collision_profile(this, obj.TransientState.HasFlag(TransientStateFlags.Contact), velocityCollide);
+
+ // ObjID and obj are custom parameters added by ace
+ // if obj. and obj) are the same, all of these calls seem to effectively get dropped
+ // is this intended for 1-way collisions??
obj.WeenieObj.DoCollision(profile, ObjID, obj);
- collided = true;
- if (!CollisionTable.ContainsKey(obj.ID))
- CollisionTable.Add(obj.ID, new CollisionRecord() { TouchedTime = PhysicsTimer.CurrentTime });
- else
- CollisionTable[obj.ID] = new CollisionRecord() { TouchedTime = PhysicsTimer.CurrentTime };
+ collided = true;
}
+
return collided;
}
@@ -3013,7 +3380,7 @@ public bool report_object_collision_end(uint objectID)
{
if (ObjMaint != null)
{
- var collision = ObjectMaint.GetObjectA(objectID);
+ var collision = ServerObjectManager.GetObjectA(objectID);
if (collision != null)
{
if (!collision.State.HasFlag(PhysicsState.ReportCollisionsAsEnvironment))
@@ -3033,6 +3400,30 @@ public bool report_object_collision_end(uint objectID)
return false;
}
+ public ObjCollisionProfile build_collision_profile(PhysicsObj obj, bool amIInContact, Vector3 velocityCollide)
+ {
+ if (State.HasFlag(PhysicsState.Missile))
+ {
+ return new AtkCollisionProfile(obj.ID, -1, obj.Position.DetermineQuadrant(obj.GetHeight(), Position));
+ }
+ else
+ {
+ return build_collision_profile(obj, velocityCollide, amIInContact, obj.State.HasFlag(PhysicsState.Missile), obj.TransientState.HasFlag(TransientStateFlags.Contact));
+ }
+ }
+
+ public ObjCollisionProfile build_collision_profile(PhysicsObj obj, Vector3 velocity, bool amIInContact, bool objIsMissile, bool objHasContact)
+ {
+ if (WeenieObj == null)
+ return null;
+
+ var profile = new ObjCollisionProfile(obj.ID, velocity, objIsMissile, objHasContact, amIInContact);
+
+ WeenieObj.InqCollisionProfile(profile);
+
+ return profile;
+ }
+
public void rotate(Vector3 offset)
{
var offsetFrame = new AFrame();
@@ -3052,7 +3443,7 @@ public bool set_active(bool active)
if (State.HasFlag(PhysicsState.Static))
return false;
- if (TransientState.HasFlag(TransientStateFlags.Active))
+ if (!TransientState.HasFlag(TransientStateFlags.Active))
UpdateTime = PhysicsTimer.CurrentTime;
TransientState |= TransientStateFlags.Active;
@@ -3067,7 +3458,7 @@ public bool set_active(bool active)
public bool is_active()
{
- return TransientState.HasFlag(TransientStateFlags.Active);
+ return (TransientState & TransientStateFlags.Active) != 0;
}
public void set_current_pos(Position newPos)
@@ -3078,8 +3469,20 @@ public void set_current_pos(Position newPos)
if (CurCell == null || CurCell.ID != Position.ObjCellID)
{
var newCell = LScape.get_landcell(newPos.ObjCellID);
+
+ /*if (WeenieObj.WorldObject.IsPlayer && player.LastContact && newCell is LandCell landCell)
+ {
+ Polygon walkable = null;
+ if (landCell.find_terrain_poly(newPos.Frame.Origin, ref walkable))
+ {
+ ContactPlaneCellID = newPos.ObjCellID;
+ ContactPlane = walkable.Plane;
+ }
+ }*/
change_cell_server(newCell);
}
+
+ CachedVelocity = requestCachedVelocity;
}
///
@@ -3209,14 +3612,16 @@ public bool set_ethereal(bool ethereal, bool sendEvent)
State &= ~PhysicsState.Ethereal;
- if (Parent != null || CurCell != null || ethereal_check_for_collisions())
+ if (Parent != null || CurCell == null || !ethereal_check_for_collisions())
{
TransientState &= ~TransientStateFlags.CheckEthereal;
return true;
}
- TransientState |= TransientStateFlags.CheckEthereal;
+ // error path - go back to ethereal, start loop in CheckEthereal state
State |= PhysicsState.Ethereal;
+ TransientState |= TransientStateFlags.CheckEthereal;
+
return false;
}
@@ -3366,7 +3771,7 @@ public void set_object_guid(ObjectGuid guid)
ObjID = guid;
ID = guid.Full;
- ObjectMaint.AddServerObject(this);
+ ServerObjectManager.AddServerObject(this);
}
///
@@ -3473,6 +3878,8 @@ public bool set_parent(PhysicsObj obj, int partIdx, AFrame frame)
return true;
}
+ private Vector3 requestCachedVelocity;
+
///
/// Sets the requested position to the AutonomousPosition
/// received from the client
@@ -3493,6 +3900,8 @@ public void set_request_pos(Vector3 pos, Quaternion rotation, ObjCell cell, uint
RequestPos.ObjCellID = RequestPos.GetCell(CurCell.ID);
else
RequestPos.ObjCellID = cell.ID;
+
+ requestCachedVelocity = CachedVelocity;
}
public void set_sequence_animation(int animID, bool interrupt, int startFrame, float framerate)
@@ -3591,7 +4000,7 @@ public void stick_to_object(uint objectID)
MakePositionManager();
if (ObjMaint == null) return;
- var objectA = ObjectMaint.GetObjectA(objectID);
+ var objectA = ServerObjectManager.GetObjectA(objectID);
if (objectA == null) return;
if (objectA.Parent != null)
objectA = Parent;
@@ -3648,11 +4057,8 @@ public bool track_object_collision(PhysicsObj obj, bool prev_has_contact)
if (CollisionTable == null)
CollisionTable = new Dictionary();
- //if (!CollisionTable.ContainsKey(obj.ID))
- // CollisionTable.Add(obj.ID, null);
+ CollisionTable[obj.ID] = new CollisionRecord(PhysicsTimer.CurrentTime, obj.State.HasFlag(PhysicsState.Ethereal));
- //if (!CollisionTable.ContainsKey(obj.ID)) return false;
- //CollisionTable.Remove(obj.ID);
return report_object_collision(obj, prev_has_contact);
}
@@ -3677,11 +4083,11 @@ public Transition transition(Position oldPos, Position newPos, bool adminMove)
trans.InitPath(CurCell, oldPos, newPos);
- if (TransientState.HasFlag(TransientStateFlags.StationaryStuck))
+ if ((TransientState & TransientStateFlags.StationaryStuck) != 0)
trans.CollisionInfo.FramesStationaryFall = 3;
- else if (TransientState.HasFlag(TransientStateFlags.StationaryStop))
+ else if ((TransientState & TransientStateFlags.StationaryStop) != 0)
trans.CollisionInfo.FramesStationaryFall = 2;
- else if (TransientState.HasFlag(TransientStateFlags.StationaryFall))
+ else if ((TransientState & TransientStateFlags.StationaryFall) != 0)
trans.CollisionInfo.FramesStationaryFall = 1;
var validPos = trans.FindValidPosition();
@@ -3834,18 +4240,142 @@ public void ShowPendingMotions()
Console.WriteLine($"{(MotionCommand)motion.Motion}");
}
- public void update_object_server(bool forcePos = true)
+ public bool motions_pending()
+ {
+ return IsAnimating;
+ }
+
+ ///
+ /// This is for legacy movement system
+ ///
+ public bool update_object_server(bool forcePos = true)
{
var deltaTime = PhysicsTimer.CurrentTime - UpdateTime;
- UpdateObjectInternalServer(deltaTime);
- if (forcePos)
+ var wo = WeenieObj.WorldObject;
+ var success = true;
+ //if (wo != null && !wo.Teleporting)
+ success = UpdateObjectInternalServer(deltaTime);
+
+ if (forcePos && success)
set_current_pos(RequestPos);
// temp for players
- CachedVelocity = Vector3.Zero;
+ if ((TransientState & TransientStateFlags.Contact) != 0)
+ CachedVelocity = Vector3.Zero;
+
+ /*if (wo != null && wo.Teleporting)
+ {
+ //Console.WriteLine($"*** SETTING TELEPORT ***");
+
+ var setPosition = new SetPosition();
+ setPosition.Pos = RequestPos;
+ setPosition.Flags = SetPositionFlags.SendPositionEvent | SetPositionFlags.Slide | SetPositionFlags.Placement | SetPositionFlags.Teleport;
+
+ SetPosition(setPosition);
+
+ // hack...
+ if (!TransientState.HasFlag(TransientStateFlags.OnWalkable))
+ {
+ //Console.WriteLine($"Setting velocity");
+ Velocity = new Vector3(0, 0, -PhysicsGlobals.EPSILON);
+ }
+ }*/
UpdateTime = PhysicsTimer.CurrentTime;
+
+ return success;
+ }
+
+ ///
+ /// This is for full / updated movement system
+ ///
+ public bool update_object_server_new(bool forcePos = true)
+ {
+ if (Parent != null || CurCell == null || State.HasFlag(PhysicsState.Frozen))
+ {
+ TransientState &= ~TransientStateFlags.Active;
+ return false;
+ }
+
+ PhysicsTimer_CurrentTime = UpdateTime;
+
+ var deltaTime = PhysicsTimer.CurrentTime - UpdateTime;
+
+ //Console.WriteLine($"{Name}.update_object_server({forcePos}) - deltaTime: {deltaTime}");
+
+ //var isTeleport = WeenieObj.WorldObject?.Teleporting ?? false;
+ var isTeleport = false;
+
+ // commented out for debugging
+ if (deltaTime > PhysicsGlobals.HugeQuantum && !isTeleport)
+ {
+ UpdateTime = PhysicsTimer.CurrentTime; // consume time?
+ return false;
+ }
+
+ var requestCell = RequestPos.ObjCellID;
+
+ var success = true;
+
+ if (!isTeleport)
+ {
+ if (GetBlockDist(Position, RequestPos) > 1)
+ {
+ Console.WriteLine($"WARNING: failed transition for {Name} from {Position} to {RequestPos}");
+ success = false;
+ }
+
+ while (deltaTime > PhysicsGlobals.MaxQuantum)
+ {
+ PhysicsTimer_CurrentTime += PhysicsGlobals.MaxQuantum;
+ UpdateObjectInternal(PhysicsGlobals.MaxQuantum);
+ deltaTime -= PhysicsGlobals.MaxQuantum;
+ }
+
+ if (deltaTime > PhysicsGlobals.MinQuantum)
+ {
+ PhysicsTimer_CurrentTime += deltaTime;
+ UpdateObjectInternal(deltaTime);
+ }
+
+ success &= requestCell >> 16 != 0x18A || CurCell?.ID >> 16 == requestCell >> 16;
+ }
+
+ RequestPos.ObjCellID = requestCell;
+
+ if (forcePos && success)
+ {
+ // attempt transition to request pos,
+ // to trigger any collision detection
+ var transit = transition(Position, RequestPos, false);
+
+ if (transit != null)
+ {
+ var prevContact = (TransientState & TransientStateFlags.Contact) != 0;
+
+ foreach (var collideObject in transit.CollisionInfo.CollideObject)
+ track_object_collision(collideObject, prevContact);
+ }
+
+ set_current_pos(RequestPos);
+ }
+
+ // for teleport, use SetPosition?
+ if (isTeleport)
+ {
+ //Console.WriteLine($"*** SETTING TELEPORT ***");
+
+ var setPosition = new SetPosition();
+ setPosition.Pos = RequestPos;
+ setPosition.Flags = SetPositionFlags.SendPositionEvent | SetPositionFlags.Slide | SetPositionFlags.Placement | SetPositionFlags.Teleport;
+
+ SetPosition(setPosition);
+ }
+
+ UpdateTime = PhysicsTimer_CurrentTime;
+
+ return success;
}
public void update_position()
@@ -3874,44 +4404,6 @@ public void update_position()
UpdateTime = PhysicsTimer.CurrentTime;
}
- public void get_voyeurs()
- {
- ObjMaint.get_voyeurs();
- }
-
- public void add_moveto_listener(Action listener)
- {
- MovementManager.MoveToManager.add_listener(listener);
- }
-
- public void remove_moveto_listener(Action listener)
- {
- MovementManager.MoveToManager.remove_listener(listener);
- }
-
- public void add_sticky_listener(Action listener)
- {
- MakePositionManager();
- PositionManager.MakeStickyManager();
-
- PositionManager.StickyManager.add_sticky_listener(listener);
- }
-
- public void remove_sticky_listener(Action listener)
- {
- PositionManager.StickyManager.remove_sticky_listener(listener);
- }
-
- public void add_unsticky_listener(Action listener)
- {
- PositionManager.StickyManager.add_unsticky_listener(listener);
- }
-
- public void remove_unsticky_listener(Action listener)
- {
- PositionManager.StickyManager.remove_unsticky_listener(listener);
- }
-
public bool IsGrounded { get => TransientState.HasFlag(TransientStateFlags.OnWalkable) && CachedVelocity.Equals(Vector3.Zero); }
/*public bool Equals(PhysicsObj obj)
@@ -3924,5 +4416,10 @@ public override int GetHashCode()
{
return ID.GetHashCode();
}*/
+
+ public override string ToString()
+ {
+ return $"{Name} ({ID:X8})";
+ }
}
}
diff --git a/ACViewer/Physics/Polygon.cs b/ACViewer/Physics/Polygon.cs
index 0678d10..67b4405 100644
--- a/ACViewer/Physics/Polygon.cs
+++ b/ACViewer/Physics/Polygon.cs
@@ -14,6 +14,7 @@ public class Polygon: IEquatable
public List VertexIDs;
//public List Screen;
public int PolyID; // not directly in this DAT structure
+ public int NumPoints;
public StipplingType Stippling;
public CullMode SidesType;
public List PosUVIndices; // texture coordinates unused by server
@@ -30,6 +31,7 @@ public Polygon(int idx, int numPoints, CullMode cullMode)
Init();
PolyID = idx;
+ NumPoints = numPoints;
SidesType = cullMode;
VertexIDs = new List(numPoints);
@@ -48,6 +50,7 @@ public Polygon(DatLoader.Entity.Polygon polygon, DatLoader.Entity.CVertexArray v
{
NegSurface = polygon.NegSurface;
//NegUVIndices = polygon.NegUVIndices;
+ NumPoints = polygon.NumPts;
PosSurface = polygon.PosSurface;
//PosUVIndices = polygon.PosUVIndices;
SidesType = polygon.SidesType;
@@ -138,7 +141,7 @@ public bool check_walkable(Sphere sphere, Vector3 up, bool small = false)
var radsum = sphere.Radius * sphere.Radius;
if (small) radsum *= 0.25f;
- var prevIdx = Vertices.Count - 1;
+ var prevIdx = NumPoints - 1;
for (var i = 0; i < Vertices.Count; i++)
{
var vertex = Vertices[i];
@@ -178,7 +181,7 @@ public bool find_crossed_edge(Sphere sphere, Vector3 up, ref Vector3 normal)
var angle = (Vector3.Dot(Plane.Normal, sphere.Center) + Plane.D) / angleUp;
var center = sphere.Center - up * angle;
- var prevIdx = Vertices.Count - 1;
+ var prevIdx = NumPoints - 1;
for (var i = 0; i < Vertices.Count; i++)
{
var vertex = Vertices[i];
@@ -207,24 +210,23 @@ public bool hits_sphere(Sphere sphere)
public void make_plane()
{
var normal = Vector3.Zero;
- var numPoints = Vertices.Count;
// calculate plane normal
- for (int i = numPoints - 2, spreadIdx = 1; i > 0; i--)
+ for (int i = NumPoints - 2, spreadIdx = 1; i > 0; i--)
{
var v1 = Vertices[spreadIdx++] - Vertices[0];
var v2 = Vertices[spreadIdx] - Vertices[0];
normal += Vector3.Cross(v1, v2);
}
- normal = normal.Normalize();
+ normal = Vector3.Normalize(normal);
// calculate distance
var distSum = 0.0f;
- for (int i = numPoints, spread = 0; i > 0; i--, spread++)
+ for (int i = NumPoints, spread = 0; i > 0; i--, spread++)
distSum += Vector3.Dot(normal, Vertices[spread].Origin);
- var dist = -(distSum / numPoints);
+ var dist = -(distSum / NumPoints);
Plane = new Plane(normal, dist);
}
@@ -232,7 +234,7 @@ public void make_plane()
public bool point_in_poly2D(Vector3 point, Sidedness side)
{
var prevIdx = 0;
- for (var i = Vertices.Count - 1; i >= 0; i--)
+ for (var i = NumPoints - 1; i >= 0; i--)
{
var prevVertex = Vertices[prevIdx];
var vertex = Vertices[i];
@@ -259,7 +261,7 @@ public bool point_in_poly2D(Vector3 point, Sidedness side)
public bool point_in_polygon(Vector3 point)
{
- var lastVertex = Vertices[Vertices.Count - 1];
+ var lastVertex = Vertices[NumPoints - 1];
foreach (var vertex in Vertices)
{
@@ -296,7 +298,7 @@ public bool polygon_hits_sphere(Sphere sphere, ref Vector3 contactPoint)
contactPoint = sphere.Center - Plane.Normal * dpPos;
- var prevIdx = Vertices.Count - 1;
+ var prevIdx = NumPoints - 1;
for (var i = 0; i < Vertices.Count; i++)
{
var vertex = Vertices[i];
@@ -328,8 +330,7 @@ public bool polygon_hits_sphere(Sphere sphere, ref Vector3 contactPoint)
public bool polygon_hits_sphere_precise(Sphere sphere, ref Vector3 contactPoint)
{
- var numPoints = Vertices.Count;
- if (numPoints == 0) return true;
+ if (NumPoints == 0) return true;
var dpPos = Vector3.Dot(Plane.Normal, sphere.Center) + Plane.D;
var rad = sphere.Radius - PhysicsGlobals.EPSILON;
@@ -338,7 +339,7 @@ public bool polygon_hits_sphere_precise(Sphere sphere, ref Vector3 contactPoint)
var diff = rad * rad - dpPos * dpPos;
contactPoint = sphere.Center - Plane.Normal * dpPos;
- var prevIdx = numPoints - 1;
+ var prevIdx = NumPoints - 1;
for (var i = 0; i < Vertices.Count; i++)
{
var vertex = Vertices[i];
@@ -352,7 +353,7 @@ public bool polygon_hits_sphere_precise(Sphere sphere, ref Vector3 contactPoint)
if (Vector3.Dot(disp, cross) >= 0.0f) continue;
// inner loop
- prevIdx = numPoints - 1; // alt idx?
+ prevIdx = NumPoints - 1; // alt idx?
for (var j = 0; j < Vertices.Count; j++)
{
vertex = Vertices[j];
@@ -412,12 +413,10 @@ public bool walkable_hits_sphere(SpherePath path, Sphere sphere, Vector3 up)
public bool Equals(Polygon p)
{
- var numPoints = Vertices.Count;
-
- if (PolyID != p.PolyID || numPoints != p.Vertices.Count || Stippling != p.Stippling || SidesType != p.SidesType ||
+ if (PolyID != p.PolyID || NumPoints != p.NumPoints || Stippling != p.Stippling || SidesType != p.SidesType ||
PosSurface != p.PosSurface || NegSurface != p.NegSurface) return false;
- for (var i = 0; i < numPoints; i++)
+ for (var i = 0; i < NumPoints; i++)
{
if (!p.Vertices[i].Equals(Vertices[i]))
return false;
@@ -433,12 +432,13 @@ public override int GetHashCode()
int hash = 0;
hash = (hash * 397) ^ PolyID.GetHashCode();
+ hash = (hash * 397) ^ NumPoints.GetHashCode();
hash = (hash * 397) ^ Stippling.GetHashCode();
hash = (hash * 397) ^ SidesType.GetHashCode();
hash = (hash * 397) ^ PosSurface.GetHashCode();
hash = (hash * 397) ^ NegSurface.GetHashCode();
- for (var i = 0; i < Vertices.Count; i++)
+ for (var i = 0; i < NumPoints; i++)
{
hash = (hash * 397) ^ Vertices[i].GetHashCode();
hash = (hash * 397) ^ VertexIDs[i].GetHashCode();
diff --git a/ACViewer/Physics/Sphere.cs b/ACViewer/Physics/Sphere.cs
index d10af3c..1d680cf 100644
--- a/ACViewer/Physics/Sphere.cs
+++ b/ACViewer/Physics/Sphere.cs
@@ -1,7 +1,9 @@
using System;
using System.Numerics;
+
+using ACE.Entity.Enum;
+
using ACE.Server.Physics.Animation;
-using ACE.Server.Physics.Collision;
using ACE.Server.Physics.Common;
using ACE.Server.Physics.Extensions;
@@ -34,7 +36,7 @@ public Sphere()
///
public Sphere(Sphere sphere)
{
- Center = new Vector3(sphere.Center.X, sphere.Center.Y, sphere.Center.Z);
+ Center = sphere.Center;
Radius = sphere.Radius;
}
@@ -58,65 +60,78 @@ public Sphere(DatLoader.Entity.Sphere sphere)
Radius = sphere.Radius;
}
- public static int Attack(Position targetPos, float targetRadius, float targetHeight, Position attackPos, Vector2 left, Vector2 right, float attackRadius, float attackHeight)
+ public static readonly float ThresholdMed = 1.0f / 3.0f;
+ public static readonly float ThresholdHigh = 2.0f / 3.0f;
+
+ public static Quadrant Attack(Position targetPos, float targetRadius, float targetHeight, Position attackPos, Vector2 left, Vector2 right, float attackRadius, float attackHeight)
{
var center = attackPos.LocalToLocal(targetPos, Vector3.Zero);
+
if (attackHeight < 0.0f || attackHeight > targetHeight)
- return 0;
+ return Quadrant.None;
+
var radsum = targetRadius + attackRadius;
+
var distSq = center.LengthSquared2D();
if (distSq > radsum * radsum)
- return 0;
+ return Quadrant.None;
var hitLoc = targetPos.LocalToLocal(attackPos, Vector3.Zero);
- var quadrant = 8;
- int quadmod, quadbit;
- if (hitLoc.X > 0.0f)
- quadrant = 16;
- if (hitLoc.Y > 0.0f)
- quadmod = quadrant | 0x20;
- else
- quadmod = quadrant | 0x40;
- if (targetHeight * 0.333333f <= attackHeight)
- {
- if (targetHeight * 0.666667f <= attackHeight)
- quadbit = quadmod | 1;
- else
- quadbit = quadmod | 2;
- }
+
+ var quadrant = hitLoc.X <= 0.0f ? Quadrant.Left : Quadrant.Right;
+
+ quadrant |= hitLoc.Y > 0.0f ? Quadrant.Front : Quadrant.Back;
+
+ if (attackHeight < targetHeight * ThresholdMed)
+ quadrant |= Quadrant.Low;
+ else if (attackHeight < targetHeight * ThresholdHigh)
+ quadrant |= Quadrant.Medium;
else
- quadbit = quadmod | 4;
- var attackhta = center.Y * left.X - center.X * left.Y;
- var rightDist = center.X * right.Y - center.Y * right.X;
- if (attackhta <= 0.0f && rightDist <= 0.0f)
- return quadbit;
+ quadrant |= Quadrant.High;
+
+ // 2d cross product?
+ var attack_ht = center.Y * left.X - center.X * left.Y;
+ var right_dist = center.X * right.Y - center.Y * right.X;
+
+ if (attack_ht <= 0.0f && right_dist <= 0.0f)
+ return quadrant;
+
if (left.X * right.Y - left.Y * right.X >= 0.0f)
{
- if (rightDist * attackhta <= 0.0f || attackhta <= targetRadius)
- return quadbit;
+ if (right_dist * attack_ht <= 0.0f || attack_ht <= targetRadius || right_dist <= targetRadius)
+ return quadrant;
+ else
+ return Quadrant.None;
}
- else if (attackhta <= 0.0f)
+
+ if (attack_ht < 0.0f)
{
- // drop down
+ if (right_dist <= targetRadius)
+ return quadrant;
+ else
+ return Quadrant.None;
}
- else if (rightDist >= 0.0f)
+
+ if (right_dist >= 0.0f)
{
- if (targetRadius * targetRadius >= distSq)
- return quadbit;
+ if (distSq <= targetRadius * targetRadius)
+ return quadrant;
else
- return 0;
+ return Quadrant.None;
}
- else if (attackhta >= 0.0f)
+
+ if (attack_ht < 0.0f)
{
- if (attackhta <= targetRadius)
- return quadbit;
+ if (right_dist <= targetRadius)
+ return quadrant;
else
- return 0;
+ return Quadrant.None;
}
- if (rightDist >= targetRadius)
- return 0;
+
+ if (attack_ht <= targetRadius)
+ return quadrant;
else
- return quadbit;
+ return Quadrant.None;
}
///
@@ -220,7 +235,8 @@ public bool Intersects(Sphere sphere)
public TransitionState IntersectsSphere(Position position, float scale, Transition transition, bool isCreature)
{
var globPos = transition.SpherePath.CheckPos.LocalToGlobal(position, Center * scale);
- return new Sphere(globPos, Radius * scale).IntersectsSphere(transition, isCreature);
+ var sphere = new Sphere(globPos, Radius * scale);
+ return sphere.IntersectsSphere(transition, isCreature);
}
///
@@ -247,7 +263,7 @@ public TransitionState IntersectsSphere(Transition transition, bool isCreature)
if (transition.SpherePath.ObstructionEthereal || transition.SpherePath.InsertType == InsertType.Placement)
{
- if (disp.LengthSquared() <= radsum)
+ if (disp.LengthSquared() <= radsum * radsum)
return TransitionState.Collided;
if (transition.SpherePath.NumSphere > 1)
@@ -338,7 +354,7 @@ public TransitionState IntersectsSphere(Transition transition, bool isCreature)
t = diff * 2 - t;
var time = (float)t / lenSq;
var timecheck = (1 - time) * transition.SpherePath.WalkInterp;
- if (timecheck < transition.SpherePath.WalkableAllowance && timecheck < -0.1f)
+ if (timecheck >= transition.SpherePath.WalkInterp || timecheck < -0.1f)
return TransitionState.Collided;
movement *= time;
@@ -395,10 +411,11 @@ public TransitionState SlideSphere(Transition transition, Vector3 disp, float ra
var contactPlane = collisions.ContactPlaneValid ? collisions.ContactPlane : collisions.LastKnownContactPlane;
var skid_dir = contactPlane.Normal;
- var direction = Vector3.Cross(skid_dir, collisionNormal);
+ //var direction = Vector3.Cross(skid_dir, collisionNormal);
+ var direction = Vector3.Cross(collisionNormal, skid_dir);
var blockOffset = LandDefs.GetBlockOffset(path.CurPos.ObjCellID, path.CheckPos.ObjCellID);
- var globOffset = globSphere.Center - Center + blockOffset;
+ var globOffset = globSphere.Center - path.GlobalCurrCenter[sphereNum].Center + blockOffset;
var dirLenSq = direction.LengthSquared();
if (dirLenSq >= PhysicsGlobals.EPSILON)
{
@@ -411,7 +428,8 @@ public TransitionState SlideSphere(Transition transition, Vector3 disp, float ra
direction = skid_dir;
// only x?
- if (direction.X * direction.X < PhysicsGlobals.EPSILON)
+ //if (direction.X * direction.X < PhysicsGlobals.EPSILON)
+ if (direction.LengthSquared() < PhysicsGlobals.EPSILON)
return TransitionState.Collided;
direction -= globOffset;
diff --git a/ACViewer/Physics/SpherePath.cs b/ACViewer/Physics/SpherePath.cs
index 7d8976e..a15ddc7 100644
--- a/ACViewer/Physics/SpherePath.cs
+++ b/ACViewer/Physics/SpherePath.cs
@@ -296,7 +296,7 @@ public void SetWalkable(Sphere sphere, Polygon poly, Vector3 zAxis, Position loc
{
WalkableCheckPos = new Sphere(sphere);
Walkable = poly;
- WalkableUp = new Vector3(zAxis.X, zAxis.Y, zAxis.Z);
+ WalkableUp = zAxis;
WalkablePos = new Position(localPos);
WalkableScale = scale;
}
diff --git a/ACViewer/Physics/Trajectory.cs b/ACViewer/Physics/Trajectory.cs
index 421ff17..ec93f44 100644
--- a/ACViewer/Physics/Trajectory.cs
+++ b/ACViewer/Physics/Trajectory.cs
@@ -28,8 +28,6 @@
// int SolveQuartic(double c0, double c1, double c2, double c3, double c4, out double s0, out double s1, out double s2, out double s3);
-using ACE.Server.Physics.Extensions;
-
using System;
using System.Diagnostics;
using System.Numerics;
@@ -262,7 +260,9 @@ public static int SolveQuartic(double c0, double c1, double c2, double c3, doubl
public static float ballistic_range(float speed, float gravity, float initial_height)
{
// Handling these cases is up to your project's coding standards
- Debug.Assert(speed > 0 && gravity > 0 && initial_height >= 0, "fts.ballistic_range called with invalid data");
+ //Debug.Assert(speed > 0 && gravity > 0 && initial_height >= 0, "fts.ballistic_range called with invalid data");
+ if (speed <= 0 || gravity <= 0 || initial_height < 0)
+ return 0.0f;
// Derivation
// (1) x = speed * time * cos O
@@ -290,9 +290,8 @@ public static float ballistic_range(float speed, float gravity, float initial_he
// return (int): number of unique solutions found: 0, 1, or 2.
public static int solve_ballistic_arc(Vector3 proj_pos, float proj_speed, Vector3 target, float gravity, out Vector3 s0, out Vector3 s1, out float t0, out float t1)
{
-
// Handling these cases is up to your project's coding standards
- Debug.Assert(proj_pos != target && proj_speed > 0 && gravity > 0, "fts.solve_ballistic_arc called with invalid data");
+ //Debug.Assert(proj_pos != target && proj_speed > 0 && gravity > 0, "fts.solve_ballistic_arc called with invalid data");
// C# requires out variables be set
s0 = Vector3.Zero;
@@ -300,6 +299,9 @@ public static int solve_ballistic_arc(Vector3 proj_pos, float proj_speed, Vector
t0 = float.PositiveInfinity;
t1 = float.PositiveInfinity;
+ if (proj_pos == target || proj_speed <= 0 || gravity <= 0)
+ return 0;
+
// Derivation
// (1) x = v*t*cos O
// (2) z = v*t*sin O - .5*g*t^2
@@ -338,7 +340,7 @@ public static int solve_ballistic_arc(Vector3 proj_pos, float proj_speed, Vector
var highAng = Math.Atan2(speed2 + root, gx);
int numSolutions = lowAng != highAng ? 2 : 1;
- Vector3 groundDir = diffXY.Normalize();
+ Vector3 groundDir = Vector3.Normalize(diffXY);
s0 = groundDir * (float)Math.Cos(lowAng) * proj_speed + Vector3.UnitZ * (float)Math.Sin(lowAng) * proj_speed;
if (numSolutions > 1)
s1 = groundDir * (float)Math.Cos(highAng) * proj_speed + Vector3.UnitZ * (float)Math.Sin(highAng) * proj_speed;
@@ -465,13 +467,14 @@ public static int solve_ballistic_arc(Vector3 proj_pos, float proj_speed, Vector
///
public static bool SolveBallisticArc(Vector3 projectilePosition, float lateralSpeed, Vector3 targetPosition, out Vector3 velocityVector, out float time)
{
-
// Handling these cases is up to your project's coding standards
- Debug.Assert(projectilePosition != targetPosition && lateralSpeed > 0, "fts.solve_ballistic_arc called with invalid data");
-
+ //Debug.Assert(projectilePosition != targetPosition && lateralSpeed > 0, "fts.solve_ballistic_arc called with invalid data");
velocityVector = Vector3.Zero;
time = float.NaN;
+ if (projectilePosition == targetPosition || lateralSpeed <= 0)
+ return false;
+
Vector3 diff = targetPosition - projectilePosition;
Vector3 diffXY = new Vector3(diff.X, diff.Y, 0f);
float lateralDist = diffXY.Length();
@@ -481,7 +484,7 @@ public static bool SolveBallisticArc(Vector3 projectilePosition, float lateralSp
time = lateralDist / lateralSpeed;
- velocityVector = diffXY.Normalize() * lateralSpeed;
+ velocityVector = Vector3.Normalize(diffXY) * lateralSpeed;
// System of equations. Hit max_height at t=.5*time. Hit target at t=time.
//
@@ -514,13 +517,15 @@ public static bool SolveBallisticArc(Vector3 projectilePosition, float lateralSp
// return (bool): true if a valid solution was found
public static bool solve_ballistic_arc_lateral(Vector3 proj_pos, float lateral_speed, Vector3 target_pos, float max_height, out Vector3 fire_velocity, out float gravity)
{
-
// Handling these cases is up to your project's coding standards
Debug.Assert(proj_pos != target_pos && lateral_speed > 0 && max_height > proj_pos.Z, "fts.solve_ballistic_arc called with invalid data");
fire_velocity = Vector3.Zero;
gravity = float.NaN;
+ if (proj_pos == target_pos || lateral_speed <= 0 || max_height <= proj_pos.Z)
+ return false;
+
Vector3 diff = target_pos - proj_pos;
Vector3 diffXY = new Vector3(diff.X, diff.Y, 0f);
float lateralDist = diffXY.Length();
@@ -530,7 +535,7 @@ public static bool solve_ballistic_arc_lateral(Vector3 proj_pos, float lateral_s
float time = lateralDist / lateral_speed;
- fire_velocity = diffXY.Normalize() * lateral_speed;
+ fire_velocity = Vector3.Normalize(diffXY) * lateral_speed;
// System of equations. Hit max_height at t=.5*time. Hit target at t=time.
//
@@ -562,15 +567,17 @@ public static bool solve_ballistic_arc_lateral(Vector3 proj_pos, float lateral_s
// return (bool): true if a valid solution was found
public static bool solve_ballistic_arc_lateral(Vector3 proj_pos, float lateral_speed, Vector3 target, Vector3 target_velocity, float gravity, out Vector3 fire_velocity, out float time, out Vector3 impact_point)
{
-
// Handling these cases is up to your project's coding standards
- Debug.Assert(proj_pos != target && lateral_speed > 0, "fts.solve_ballistic_arc_lateral called with invalid data");
+ //Debug.Assert(proj_pos != target && lateral_speed > 0, "fts.solve_ballistic_arc_lateral called with invalid data");
// Initialize output variables
fire_velocity = Vector3.Zero;
time = 0.0f;
impact_point = Vector3.Zero;
+ if (proj_pos == target || lateral_speed <= 0)
+ return false;
+
// Ground plane terms
Vector3 targetVelXY = new Vector3(target_velocity.X, target_velocity.Y, 0f);
Vector3 diffXY = target - proj_pos;
@@ -604,7 +611,7 @@ public static bool solve_ballistic_arc_lateral(Vector3 proj_pos, float lateral_s
// Calculate fire velocity along XZ plane
Vector3 dir = impact_point - proj_pos;
- fire_velocity = new Vector3(dir.X, dir.Y, 0f).Normalize() * lateral_speed;
+ fire_velocity = Vector3.Normalize(new Vector3(dir.X, dir.Y, 0f)) * lateral_speed;
// Solve system of equations. Hit max_height at t=.5*time. Hit target at t=time.
//
diff --git a/ACViewer/Physics/Trajectory2.cs b/ACViewer/Physics/Trajectory2.cs
new file mode 100644
index 0000000..4a6b72b
--- /dev/null
+++ b/ACViewer/Physics/Trajectory2.cs
@@ -0,0 +1,64 @@
+using System;
+using System.Numerics;
+
+using ACE.Server.Physics.Common;
+
+namespace ACE.Server.Physics
+{
+ public static class Trajectory2
+ {
+ public static Vector3 CalculateTrajectory(Vector3 startPos, Vector3 endPos, Vector3 targetVelocity, float speed, bool gravity)
+ {
+ var targetOffset = endPos - startPos;
+
+ Vector3 result;
+ float t = 0.0f;
+
+ if (targetVelocity == Vector3.Zero)
+ {
+ var targetDist = targetOffset.Length();
+
+ t = targetDist / speed;
+ result = targetOffset / t;
+ }
+ else
+ {
+ var p0 = targetOffset;
+ var p1 = Vector3.Zero;
+
+ var v0 = targetVelocity;
+ if (Vec.NormalizeCheckSmall(ref v0))
+ v0 = Vector3.Zero;
+
+ var s1 = speed;
+
+ var a = (v0.X * v0.X) + (v0.Y * v0.Y) - (s1 * s1);
+ var b = 2 * ((p0.X * v0.X) + (p0.Y * v0.Y) - (p1.X * v0.X) - (p1.Y * v0.Y));
+ var c = (p0.X * p0.X) + (p0.Y * p0.Y) + (p1.X * p1.X) + (p1.Y * p1.Y) - (2 * p1.X * p0.X) - (2 * p1.Y * p0.Y);
+
+ var t0 = Math.Sqrt((b * b) - (4 * a * c));
+ var t1 = (-b + t0) / (2 * a);
+ var t2 = (-b - t0) / (2 * a);
+
+ if (t1 < 0)
+ t1 = float.MaxValue;
+ if (t2 < 0)
+ t2 = float.MaxValue;
+
+ t = (float)Math.Min(t1, t2);
+
+ if (t >= 100)
+ return CalculateTrajectory(startPos, endPos, Vector3.Zero, speed, gravity);
+
+ var s0 = targetVelocity.Length();
+
+ result = (p0 + t * s0 * v0) / t;
+ }
+
+ if (gravity)
+ result.Z -= PhysicsGlobals.Gravity * t * 0.5f;
+
+ return result;
+ }
+ }
+}
diff --git a/ACViewer/Physics/Transition.cs b/ACViewer/Physics/Transition.cs
index 9986292..e577cbb 100644
--- a/ACViewer/Physics/Transition.cs
+++ b/ACViewer/Physics/Transition.cs
@@ -24,7 +24,7 @@ public class Transition
public SpherePath SpherePath;
public CollisionInfo CollisionInfo;
public CellArray CellArray;
- public ObjCell NewCellPtr;
+ //public ObjCell NewCellPtr;
public Transition()
{
@@ -155,7 +155,7 @@ public TransitionState CheckOtherCells(ObjCell currCell)
SpherePath.HitsInteriorCell = false;
//ObjCell newCell = null;
- var newCell = new ObjCell(); // null check?
+ var newCell = ObjCell.EmptyCell; // null check?
ObjCell.find_cell_list(CellArray, ref newCell, SpherePath);
for (var i = 0; i < CellArray.Cells.Count; i++)
@@ -260,7 +260,7 @@ public TransitionState CliffSlide(Plane contactPlane)
else
{
SpherePath.AddOffsetToCheckPos(collideNormal * -angle);
- CollisionInfo.SetCollisionNormal(collideNormal * angle); // verify
+ CollisionInfo.SetCollisionNormal(-collideNormal);
}
return TransitionState.Adjusted;
}
@@ -503,14 +503,14 @@ public bool FindTransitionalPosition()
CalcNumSteps(ref offset, ref offsetPerStep, ref numSteps); // restructure as retval?
//var maxSteps = 30;
- var maxSteps = 200; // for debugging
- if (numSteps > maxSteps)
+ var maxSteps = 1000;
+ if (numSteps > maxSteps && !ObjectInfo.Object.IsSightObj)
{
//Console.WriteLine("NumSteps: " + numSteps);
return false;
}
- if (ObjectInfo.State.HasFlag(ObjectInfoState.FreeRotate))
+ if ((ObjectInfo.State & ObjectInfoState.FreeRotate) != 0)
SpherePath.CurPos.Frame.set_rotate(SpherePath.EndPos.Frame.Orientation);
SpherePath.SetCheckPos(SpherePath.CurPos, SpherePath.CurCell);
@@ -518,7 +518,7 @@ public bool FindTransitionalPosition()
var redo = 0;
if (numSteps <= 0)
{
- if (!ObjectInfo.State.HasFlag(ObjectInfoState.FreeRotate)) // ?
+ if ((ObjectInfo.State & ObjectInfoState.FreeRotate) == 0) // ?
SpherePath.CurPos.Frame.set_rotate(SpherePath.EndPos.Frame.Orientation);
SpherePath.CellArrayValid = true;
@@ -531,7 +531,7 @@ public bool FindTransitionalPosition()
for (var step = 0; step < numSteps; step++)
{
- if (ObjectInfo.State.HasFlag(ObjectInfoState.IsViewer))
+ if ((ObjectInfo.State & ObjectInfoState.IsViewer) != 0)
{
var lastStep = numSteps - 1;
@@ -546,14 +546,14 @@ public bool FindTransitionalPosition()
}
}
SpherePath.GlobalOffset = AdjustOffset(offsetPerStep);
- if (!ObjectInfo.State.HasFlag(ObjectInfoState.IsViewer))
+ if ((ObjectInfo.State & ObjectInfoState.IsViewer) == 0)
{
if (SpherePath.GlobalOffset.LengthSquared() < PhysicsGlobals.EPSILON * PhysicsGlobals.EPSILON)
{
return (step != 0 && transitionState == TransitionState.OK);
}
}
- if (!ObjectInfo.State.HasFlag(ObjectInfoState.FreeRotate))
+ if ((ObjectInfo.State & ObjectInfoState.FreeRotate) == 0)
{
redo = step + 1;
var delta = (float)redo / numSteps;
@@ -587,7 +587,7 @@ public bool FindTransitionalPosition()
if (CollisionInfo.FramesStationaryFall > 0) break;
}
- if (CollisionInfo.CollisionNormalValid && ObjectInfo.State.HasFlag(ObjectInfoState.PathClipped)) break;
+ if (CollisionInfo.CollisionNormalValid && (ObjectInfo.State & ObjectInfoState.PathClipped) != 0) break;
}
return transitionState == TransitionState.OK;
@@ -607,7 +607,7 @@ public void Init()
SpherePath = new SpherePath();
CollisionInfo = new CollisionInfo();
CellArray = new CellArray();
- NewCellPtr = new ObjCell();
+ //NewCellPtr = new ObjCell();
}
public void InitContactPlane(uint cellID, Plane contactPlane, bool isWater)
@@ -729,7 +729,7 @@ public bool StepDown(float stepDownHeight, float zVal)
SpherePath.StepDown = false;
if (transitionState == TransitionState.OK && CollisionInfo.ContactPlaneValid && CollisionInfo.ContactPlane.Normal.Z >= zVal &&
- (!ObjectInfo.State.HasFlag(ObjectInfoState.EdgeSlide) || SpherePath.StepUp || CheckWalkable(zVal)))
+ ((ObjectInfo.State & ObjectInfoState.EdgeSlide) == 0 || SpherePath.StepUp || CheckWalkable(zVal)))
{
SpherePath.Backup = SpherePath.InsertType;
SpherePath.InsertType = InsertType.Placement;
@@ -840,8 +840,8 @@ public TransitionState TransitionalInsert(int num_insertion_attempts)
}
else
{
- if (CollisionInfo.ContactPlaneValid || !ObjectInfo.State.HasFlag(ObjectInfoState.Contact) ||
- SpherePath.StepDown || SpherePath.CheckCell == null || ObjectInfo.StepDown)
+ if (CollisionInfo.ContactPlaneValid || (ObjectInfo.State & ObjectInfoState.Contact) == 0 ||
+ SpherePath.StepDown || SpherePath.CheckCell == null || !ObjectInfo.StepDown)
{
return TransitionState.OK;
}
@@ -849,7 +849,7 @@ public TransitionState TransitionalInsert(int num_insertion_attempts)
var zVal = PhysicsGlobals.LandingZ;
var stepDownHeight = 0.039999999f; // set global
- if (ObjectInfo.State.HasFlag(ObjectInfoState.OnWalkable))
+ if ((ObjectInfo.State & ObjectInfoState.OnWalkable) != 0)
{
zVal = ObjectInfo.GetWalkableZ();
stepDownHeight = ObjectInfo.StepDownHeight;
@@ -864,21 +864,23 @@ public TransitionState TransitionalInsert(int num_insertion_attempts)
stepDownHeight = SpherePath.GlobalSphere[0].Radius * 0.5f;
}
- if (radsum < stepDownHeight)
+ if (radsum >= stepDownHeight)
{
- // bad path
- stepDownHeight *= 0.5f;
- if (StepDown(stepDownHeight, zVal) || StepDown(stepDownHeight, zVal)) // double step..
+ if (StepDown(stepDownHeight, zVal))
{
SpherePath.Walkable = null;
return TransitionState.OK;
}
}
-
- if (StepDown(stepDownHeight, zVal)) // triple step?
+ else
{
- SpherePath.Walkable = null;
- return TransitionState.OK;
+ // 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))
@@ -969,7 +971,11 @@ public TransitionState ValidatePlacementTransition(TransitionState transitionSta
case TransitionState.Collided:
case TransitionState.Adjusted:
case TransitionState.Slid:
- if (SpherePath.PlacementAllowsSliding) CollisionInfo.Init();
+
+ // added target id
+ if (SpherePath.PlacementAllowsSliding && ObjectInfo.TargetID == 0)
+ CollisionInfo.Init();
+
break;
}
return transitionState;
diff --git a/ACViewer/Physics/Util/AdjustCell.cs b/ACViewer/Physics/Util/AdjustCell.cs
index f3f771e..2ee1c43 100644
--- a/ACViewer/Physics/Util/AdjustCell.cs
+++ b/ACViewer/Physics/Util/AdjustCell.cs
@@ -1,4 +1,5 @@
using System.Collections.Generic;
+using System.Collections.Concurrent;
using System.Numerics;
using ACE.DatLoader;
using ACE.DatLoader.FileTypes;
@@ -7,9 +8,8 @@ namespace ACE.Server.Physics.Util
{
public class AdjustCell
{
- public List EnvCells;
- public List NewEnvCells;
- public static Dictionary AdjustCells = new Dictionary();
+ public List EnvCells;
+ public static ConcurrentDictionary AdjustCells = new ConcurrentDictionary();
public AdjustCell(uint dungeonID)
{
@@ -22,31 +22,23 @@ public AdjustCell(uint dungeonID)
public void BuildEnv(uint dungeonID, uint numCells)
{
- EnvCells = new List();
- NewEnvCells = new List();
+ EnvCells = new List();
uint firstCellID = 0x100;
for (uint i = 0; i < numCells; i++)
{
uint cellID = firstCellID + i;
uint blockCell = dungeonID << 16 | cellID;
- var cell = DatManager.CellDat.ReadFromDat(blockCell);
- EnvCells.Add(new Environment(cell));
var objCell = Common.LScape.get_landcell(blockCell);
var envCell = objCell as Common.EnvCell;
if (envCell != null)
- NewEnvCells.Add(envCell);
+ EnvCells.Add(envCell);
}
}
public uint? GetCell(Vector3 point)
{
- /*foreach (var envCell in EnvCells)
- if (envCell.BBox.Contains(point))
- return envCell.EnvCell.Id;
- return null;*/
-
- foreach (var envCell in NewEnvCells)
+ foreach (var envCell in EnvCells)
if (envCell.point_in_cell(point))
return envCell.ID;
return null;
@@ -59,7 +51,7 @@ public static AdjustCell Get(uint dungeonID)
if (adjustCell == null)
{
adjustCell = new AdjustCell(dungeonID);
- AdjustCells.Add(dungeonID, adjustCell);
+ AdjustCells.TryAdd(dungeonID, adjustCell);
}
return adjustCell;
}
diff --git a/ACViewer/Physics/Util/AdjustPos.cs b/ACViewer/Physics/Util/AdjustPos.cs
index 3fa9d18..45417a9 100644
--- a/ACViewer/Physics/Util/AdjustPos.cs
+++ b/ACViewer/Physics/Util/AdjustPos.cs
@@ -15,32 +15,32 @@ static AdjustPos()
{
DungeonProfiles = new Dictionary();
- // Burial Temple
- var burialTemple = new AdjustPosProfile();
- burialTemple.BadPosition = new Vector3(30.389999f, -37.439999f, 0.000000f);
- burialTemple.BadRotation = new Quaternion(-0f, 0, 0, -1f);
- burialTemple.GoodPosition = new Vector3(30f, -146.30799865723f, 0.0049999998882413f);
- burialTemple.GoodRotation = new Quaternion(1, 0, 0, 0);
-
- DungeonProfiles.Add(0x13e, burialTemple);
-
- // North Glenden Prison
- var glendenPrison = new AdjustPosProfile();
- glendenPrison.BadPosition = new Vector3(38.400002f, -18.600000f, 6.000000f);
- glendenPrison.BadRotation = new Quaternion(-0.782608f, 0, 0, -0.622514f);
- glendenPrison.GoodPosition = new Vector3(61, -20, -17.995000839233f);
- glendenPrison.GoodRotation = new Quaternion(-0.70710700750351f, 0, 0, -0.70710700750351f);
-
- DungeonProfiles.Add(0x1e4, glendenPrison);
-
- // Nuhmudira's Dungeon
- var nuhmudirasDungeon = new AdjustPosProfile();
- nuhmudirasDungeon.BadPosition = new Vector3(149.242996f, -49.946301f, -5.995000f);
- nuhmudirasDungeon.BadRotation = new Quaternion(-0.707107f, 0, 0, -0.707107f);
- nuhmudirasDungeon.GoodPosition = new Vector3(149.24299621582f, -129.94599914551f, -5.9949998855591f);
- nuhmudirasDungeon.GoodRotation = new Quaternion(0.6967059969902f, 0, 0, 0.71735697984695f);
-
- DungeonProfiles.Add(0x536d, nuhmudirasDungeon);
+ // Burial Temple // No longer needed as of 11/24/19
+ //var burialTemple = new AdjustPosProfile();
+ //burialTemple.BadPosition = new Vector3(30.389999f, -37.439999f, 0.000000f);
+ //burialTemple.BadRotation = new Quaternion(-0f, 0, 0, -1f);
+ //burialTemple.GoodPosition = new Vector3(30f, -146.30799865723f, 0.0049999998882413f);
+ //burialTemple.GoodRotation = new Quaternion(1, 0, 0, 0);
+
+ //DungeonProfiles.Add(0x13e, burialTemple); // No longer needed as of 11/24/19
+
+ // North Glenden Prison // No longer needed as of 09/22/19
+ //var glendenPrison = new AdjustPosProfile();
+ //glendenPrison.BadPosition = new Vector3(38.400002f, -18.600000f, 6.000000f);
+ //glendenPrison.BadRotation = new Quaternion(-0.782608f, 0, 0, -0.622514f);
+ //glendenPrison.GoodPosition = new Vector3(61, -20, -17.995000839233f);
+ //glendenPrison.GoodRotation = new Quaternion(-0.70710700750351f, 0, 0, -0.70710700750351f);
+
+ //DungeonProfiles.Add(0x1e4, glendenPrison); // No longer needed as of 09/22/19
+
+ // Nuhmudira's Dungeon // No longer needed as of 11/24/19
+ //var nuhmudirasDungeon = new AdjustPosProfile();
+ //nuhmudirasDungeon.BadPosition = new Vector3(149.242996f, -49.946301f, -5.995000f);
+ //nuhmudirasDungeon.BadRotation = new Quaternion(-0.707107f, 0, 0, -0.707107f);
+ //nuhmudirasDungeon.GoodPosition = new Vector3(149.24299621582f, -129.94599914551f, -5.9949998855591f);
+ //nuhmudirasDungeon.GoodRotation = new Quaternion(0.6967059969902f, 0, 0, 0.71735697984695f);
+
+ //DungeonProfiles.Add(0x536d, nuhmudirasDungeon); // No longer needed as of 11/24/19
}
public static bool Adjust(uint dungeonID, Position pos)
diff --git a/ACViewer/Physics/Util/Environment.cs b/ACViewer/Physics/Util/Environment.cs
index b7ed403..2306731 100644
--- a/ACViewer/Physics/Util/Environment.cs
+++ b/ACViewer/Physics/Util/Environment.cs
@@ -49,7 +49,7 @@ public void BuildBBox()
{
var origin = EnvCell.Position.Origin;
var orientation = EnvCell.Position.Orientation;
- var translate = Matrix4x4.CreateTranslation(new Vector3(origin.X, origin.Y, origin.Z));
+ var translate = Matrix4x4.CreateTranslation(origin);
var rotate = Matrix4x4.CreateFromQuaternion(new Quaternion(orientation.X, orientation.Y, orientation.Z, orientation.W));
BBox = new BBox(Polygons, rotate * translate);
}
diff --git a/ACViewer/Physics/WorldObject.cs b/ACViewer/Physics/WorldObject.cs
index 92b5164..46162f7 100644
--- a/ACViewer/Physics/WorldObject.cs
+++ b/ACViewer/Physics/WorldObject.cs
@@ -11,6 +11,7 @@ public class WorldObject
{
public ObjectGuid Guid;
public string Name;
+ public bool IsPlayer;
public bool IsCreature;
public uint RunSkill;
}
diff --git a/ACViewer/WorldViewer.cs b/ACViewer/WorldViewer.cs
index 615deaa..5c223f8 100644
--- a/ACViewer/WorldViewer.cs
+++ b/ACViewer/WorldViewer.cs
@@ -47,10 +47,6 @@ public KeyboardState PrevKeyboardState
public WorldViewer()
{
Instance = this;
-
- Physics = new PhysicsEngine(new ObjectMaint(), new SmartBox());
-
- Physics.Server = false;
}
public void LoadLandblock(uint landblockID, uint radius = 1)