diff --git a/FFXIVClassic Common Class Lib/FFXIVClassic Common Class Lib.csproj b/FFXIVClassic Common Class Lib/FFXIVClassic Common Class Lib.csproj index b5c7bcb2..6a3befdb 100644 --- a/FFXIVClassic Common Class Lib/FFXIVClassic Common Class Lib.csproj +++ b/FFXIVClassic Common Class Lib/FFXIVClassic Common Class Lib.csproj @@ -70,6 +70,7 @@ + diff --git a/FFXIVClassic Common Class Lib/Vector3.cs b/FFXIVClassic Common Class Lib/Vector3.cs new file mode 100644 index 00000000..04bf296c --- /dev/null +++ b/FFXIVClassic Common Class Lib/Vector3.cs @@ -0,0 +1,74 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace FFXIVClassic.Common +{ + public class Vector3 + { + public float X; + public float Y; + public float Z; + public static Vector3 Zero = new Vector3(); + + public Vector3(float x, float y, float z) + { + X = x; + Y = y; + Z = z; + } + + public Vector3() + { + X = 0.0f; + Y = 0.0f; + Z = 0.0f; + } + + public static Vector3 operator +(Vector3 lhs, Vector3 rhs) + { + Vector3 newVec = new Vector3(lhs.X, lhs.Y, lhs.Z); + newVec.X += rhs.X; + newVec.Y += rhs.Y; + newVec.Z += rhs.Z; + return newVec; + } + + public static Vector3 operator -(Vector3 lhs, Vector3 rhs) + { + return new Vector3(lhs.X - rhs.X, lhs.Y - rhs.Y, lhs.Z - rhs.Z); + } + + public static Vector3 operator *(Vector3 lhs, Vector3 rhs) + { + return new Vector3(lhs.X * rhs.X, lhs.Y * rhs.Y, lhs.Z * rhs.Z); + } + + public static Vector3 operator *(float scalar, Vector3 rhs) + { + return new Vector3(scalar * rhs.X, scalar * rhs.Y, scalar * rhs.Z); + } + + public static Vector3 operator /(Vector3 lhs, Vector3 rhs) + { + return new Vector3(lhs.X - rhs.X, lhs.Y - rhs.Y, lhs.Z - rhs.Z); + } + + public float Length() + { + return (float)Math.Sqrt(this.LengthSquared()); + } + + public float LengthSquared() + { + return (this.X * this.X) + (this.Y * this.Y) + (this.Z * this.Z); + } + + public static float Dot(Vector3 lhs, Vector3 rhs) + { + return (lhs.X * rhs.X) + (lhs.Y * rhs.Y) + (lhs.Z * rhs.Z); + } + } +} diff --git a/FFXIVClassic Map Server/FFXIVClassic Map Server.csproj b/FFXIVClassic Map Server/FFXIVClassic Map Server.csproj index 8e3fc9ae..f0b4d371 100644 --- a/FFXIVClassic Map Server/FFXIVClassic Map Server.csproj +++ b/FFXIVClassic Map Server/FFXIVClassic Map Server.csproj @@ -340,6 +340,7 @@ + diff --git a/FFXIVClassic Map Server/actors/Actor.cs b/FFXIVClassic Map Server/actors/Actor.cs index 4afe2ca2..8e642e82 100644 --- a/FFXIVClassic Map Server/actors/Actor.cs +++ b/FFXIVClassic Map Server/actors/Actor.cs @@ -40,7 +40,7 @@ namespace FFXIVClassic_Map_Server.Actors public string className; public List classParams; - public List positionUpdates = new List(); + public List positionUpdates = new List(); public DateTime lastMoveUpdate; public Actor target; @@ -158,12 +158,16 @@ namespace FFXIVClassic_Map_Server.Actors if (hasMoved) { var pos = positionUpdates[0]; - positionUpdates.Remove(pos); + if (this is Character) + ((Character)this).OnPath(ref pos); + positionX = pos.X; positionY = pos.Y; positionZ = pos.Z; //Program.Server.GetInstance().mLuaEngine.OnPath(actor, position, positionUpdates) + + positionUpdates.RemoveAt(0); } lastMoveUpdate = DateTime.Now; return MoveActorToPositionPacket.BuildPacket(actorId, playerActorId, positionX, positionY, positionZ, rotation, moveState); @@ -609,10 +613,10 @@ namespace FFXIVClassic_Map_Server.Actors rotation = (float)dRot; } - public void QueuePositionUpdate(utils.Vector3 pos) + public void QueuePositionUpdate(Vector3 pos) { if (positionUpdates == null) - positionUpdates = new List(); + positionUpdates = new List(); positionUpdates.Add(pos); this.hasMoved = true; @@ -620,7 +624,7 @@ namespace FFXIVClassic_Map_Server.Actors public void QueuePositionUpdate(float x, float y, float z) { - QueuePositionUpdate(new utils.Vector3(x, y, z)); + QueuePositionUpdate(new Vector3(x, y, z)); } public void ClearPositionUpdates() @@ -628,7 +632,7 @@ namespace FFXIVClassic_Map_Server.Actors positionUpdates.Clear(); } - public utils.Vector3 FindRandomPointAroundActor(float minRadius, float maxRadius) + public Vector3 FindRandomPointAroundActor(float minRadius, float maxRadius) { var angle = Program.Random.NextDouble() * Math.PI * 2; var radius = Math.Sqrt(Program.Random.NextDouble() * (maxRadius - minRadius)) + minRadius; @@ -636,7 +640,7 @@ namespace FFXIVClassic_Map_Server.Actors float x = (float)(radius * Math.Cos(angle)); float z = (float)(radius * Math.Sin(angle)); - return new utils.Vector3(positionX + x, positionY, positionZ + z); + return new Vector3(positionX + x, positionY, positionZ + z); } } } diff --git a/FFXIVClassic Map Server/actors/area/Zone.cs b/FFXIVClassic Map Server/actors/area/Zone.cs index e4aaea3e..cb75cbd2 100644 --- a/FFXIVClassic Map Server/actors/area/Zone.cs +++ b/FFXIVClassic Map Server/actors/area/Zone.cs @@ -33,23 +33,20 @@ namespace FFXIVClassic_Map_Server.actors.area public Zone(uint id, string zoneName, ushort regionId, string classPath, ushort bgmDay, ushort bgmNight, ushort bgmBattle, bool isIsolated, bool isInn, bool canRideChocobo, bool canStealth, bool isInstanceRaid, bool loadNavMesh = false) : base(id, zoneName, regionId, classPath, bgmDay, bgmNight, bgmBattle, isIsolated, isInn, canRideChocobo, canStealth, isInstanceRaid) { - var navMeshName = loadNavMesh ? zoneName + ".snb" : ""; - - if (navMeshName != "") + if (loadNavMesh) { try { - tiledNavMesh = utils.NavmeshUtils.LoadNavmesh(tiledNavMesh, navMeshName); + tiledNavMesh = utils.NavmeshUtils.LoadNavmesh(tiledNavMesh, zoneName + ".snb"); navMeshQuery = new SharpNav.NavMeshQuery(tiledNavMesh, 100); - if (tiledNavMesh != null) + if (tiledNavMesh != null && tiledNavMesh.Tiles[0].PolyCount > 0) Program.Log.Info($"Loaded navmesh for {zoneName}"); } catch (Exception e) { Program.Log.Error(e.Message); } - } } diff --git a/FFXIVClassic Map Server/actors/chara/Character.cs b/FFXIVClassic Map Server/actors/chara/Character.cs index 0c2c5e1d..0cdf17d6 100644 --- a/FFXIVClassic Map Server/actors/chara/Character.cs +++ b/FFXIVClassic Map Server/actors/chara/Character.cs @@ -119,8 +119,8 @@ namespace FFXIVClassic_Map_Server.Actors public void PathTo(float x, float y, float z, float stepSize = 0.70f, int maxPath = 40, float polyRadius = 0.0f) { - var pos = new utils.Vector3(positionX, positionY, positionZ); - var dest = new utils.Vector3(x, y, z); + var pos = new Vector3(positionX, positionY, positionZ); + var dest = new Vector3(x, y, z); var sw = new System.Diagnostics.Stopwatch(); sw.Start(); @@ -156,11 +156,11 @@ namespace FFXIVClassic_Map_Server.Actors if (path.Count == 1) Program.Log.Info($"mypos: {positionX} {positionY} {positionZ} | targetPos: {x} {y} {z} | step {stepSize} | maxPath {maxPath} | polyRadius {polyRadius}"); - //Program.Log.Error("[{0}][{1}] Created {2} points in {3} milliseconds", actorId, actorName, path.Count, sw.ElapsedMilliseconds); + Program.Log.Error("[{0}][{1}] Created {2} points in {3} milliseconds", actorId, actorName, path.Count, sw.ElapsedMilliseconds); } } - public void FollowTarget(Actor target, float stepSize = 1.2f, int maxPath = 25) + public void FollowTarget(Actor target, float stepSize = 1.2f, int maxPath = 25, float radius = 0.0f) { var player = target as Player; @@ -202,13 +202,20 @@ namespace FFXIVClassic_Map_Server.Actors this.moveState = player.moveState; this.moveSpeeds = player.moveSpeeds; - PathTo(player.positionX, player.positionY, player.positionZ, stepSize, maxPath); + PathTo(player.positionX, player.positionY, player.positionZ, stepSize, maxPath, radius); } } - public void OnPath() + public void OnPath(ref Vector3 point) { - // todo: lua function onPath in mob script + if (positionUpdates != null && positionUpdates.Count > 0) + { + if (point == positionUpdates[positionUpdates.Count - 1]) + { + var myPos = new Vector3(positionX, positionY, positionZ); + //point = NavmeshUtils.GetPath((Zone)zone, myPos, point, 0.35f, 1, 0.000001f, true)?[0]; + } + } } public void Update(double deltaTime) @@ -234,22 +241,21 @@ namespace FFXIVClassic_Map_Server.Actors if (diffTime.Milliseconds >= deltaTime) { bool foundActor = false; - bool despawnOutOfRange = false; - - var targId = target != null ? actorId : 0; // leash back to spawn if (!isMovingToSpawn && this.oldPositionX != 0.0f && this.oldPositionY != 0.0f && this.oldPositionZ != 0.0f) { - var spawnDistance = Utils.Distance(positionX, positionY, positionZ, oldPositionX, oldPositionY, oldPositionZ); + //var spawnDistanceSq = Utils.DistanceSquared(positionX, positionY, positionZ, oldPositionX, oldPositionY, oldPositionZ); // todo: actual spawn leash and modifiers read from table // set a leash to path back to spawn even if have target - if (spawnDistance >= 55) + // (50 yalms) + if (Utils.DistanceSquared(positionX, positionY, positionZ, oldPositionX, oldPositionY, oldPositionZ) >= 3025) { this.isMovingToSpawn = true; this.target = null; this.lastMoveUpdate = this.lastMoveUpdate.AddSeconds(-5); + this.hasMoved = false; ClearPositionUpdates(); } } @@ -260,7 +266,8 @@ namespace FFXIVClassic_Map_Server.Actors var player = target as Player; // deaggro if zoning/logging - if (player.playerSession.isUpdatesLocked || player.isZoneChanging || player.isZoning) + // todo: player.isZoning seems to be busted + if (player.playerSession.isUpdatesLocked) { target = null; ClearPositionUpdates(); @@ -268,7 +275,7 @@ namespace FFXIVClassic_Map_Server.Actors } Player closestPlayer = null; - float closestPlayerDistance = 1000.0f; + float closestPlayerDistanceSq = 1000.0f; // dont bother checking for any in-range players if going back to spawn if (!this.isMovingToSpawn) @@ -280,19 +287,20 @@ namespace FFXIVClassic_Map_Server.Actors var player = actor as Player; // skip if zoning/logging - if (player != null && player.isZoning || player.isZoning || player.playerSession.isUpdatesLocked) + // todo: player.isZoning seems to be busted + if (player != null && player.playerSession.isUpdatesLocked) continue; // find distance between self and target - var distance = Utils.Distance(positionX, positionY, positionZ, player.positionX, player.positionY, player.positionZ); + var distanceSq = Utils.DistanceSquared(positionX, positionY, positionZ, player.positionX, player.positionY, player.positionZ); - int maxDistance = player == target ? 27 : 10; + int maxDistanceSq = player == target ? 900 : 100; // check target isnt too far // todo: create cone thing for IsFacing - if (distance <= maxDistance && distance <= closestPlayerDistance && (IsFacing(player) || true)) + if (distanceSq <= maxDistanceSq && distanceSq <= closestPlayerDistanceSq && (IsFacing(player) || true)) { - closestPlayerDistance = distance; + closestPlayerDistanceSq = distanceSq; closestPlayer = player; foundActor = true; } @@ -306,12 +314,12 @@ namespace FFXIVClassic_Map_Server.Actors if (!hasMoved) { // todo: include model size and mob specific distance checks - if (closestPlayerDistance >= 3) + if (closestPlayerDistanceSq >= 9) { - FollowTarget(closestPlayer, 2.4f, 5); + FollowTarget(closestPlayer, 2.5f, 4); } // too close, spread out - else if (closestPlayerDistance <= 0.64f) + else if (closestPlayerDistanceSq <= 0.85f) { QueuePositionUpdate(target.FindRandomPointAroundActor(0.65f, 0.85f)); } @@ -328,14 +336,15 @@ namespace FFXIVClassic_Map_Server.Actors // time elapsed since last move update var diffMove = (DateTime.Now - lastMoveUpdate); + // todo: modifier for DelayBeforeRoamToSpawn // player disappeared - if (diffMove.Seconds >= 5 && !foundActor) + if (!foundActor && diffMove.Seconds >= 5) { // dont path if havent moved before - if (oldPositionX != 0.0f && oldPositionY != 0.0f && oldPositionZ != 0.0f) + if (!hasMoved && oldPositionX != 0.0f && oldPositionY != 0.0f && oldPositionZ != 0.0f) { // check within spawn radius - this.isAtSpawn = Utils.Distance(positionX, positionY, positionZ, oldPositionX, oldPositionY, oldPositionZ) <= 25.0f; + this.isAtSpawn = Utils.DistanceSquared(positionX, positionY, positionZ, oldPositionX, oldPositionY, oldPositionZ) <= 625.0f; // make sure we have no target if (this.target == null) @@ -346,10 +355,11 @@ namespace FFXIVClassic_Map_Server.Actors PathTo(oldPositionX, oldPositionY, oldPositionZ, 2.8f); } // within spawn range, find a random point - else if (diffMove.Seconds >= 15 && !hasMoved) + else if (diffMove.Seconds >= 15) { - // pick a random point within 10 yalms or spawn - PathTo(oldPositionX, oldPositionY, oldPositionZ, 2.5f, 7, 10.5f); + // todo: polyRadius isnt euclidean distance.. + // pick a random point within 10 yalms of spawn + PathTo(oldPositionX, oldPositionY, oldPositionZ, 2.5f, 7, 2.5f); // face destination if (positionUpdates.Count > 0) diff --git a/FFXIVClassic Map Server/navmesh/SharpNav.dll b/FFXIVClassic Map Server/navmesh/SharpNav.dll index e53d6c97..be560e8f 100644 Binary files a/FFXIVClassic Map Server/navmesh/SharpNav.dll and b/FFXIVClassic Map Server/navmesh/SharpNav.dll differ diff --git a/FFXIVClassic Map Server/navmesh/wil0Field01.snb b/FFXIVClassic Map Server/navmesh/wil0Field01.snb index 2b42f353..e8a49ee9 100644 Binary files a/FFXIVClassic Map Server/navmesh/wil0Field01.snb and b/FFXIVClassic Map Server/navmesh/wil0Field01.snb differ diff --git a/FFXIVClassic Map Server/utils/NavmeshUtils.cs b/FFXIVClassic Map Server/utils/NavmeshUtils.cs index ae1f9907..54c6e433 100644 --- a/FFXIVClassic Map Server/utils/NavmeshUtils.cs +++ b/FFXIVClassic Map Server/utils/NavmeshUtils.cs @@ -8,82 +8,10 @@ using SharpNav; using SharpNav.Pathfinding; using SharpNav.Crowds; using SharpNav.IO; +using FFXIVClassic.Common; namespace FFXIVClassic_Map_Server.utils { - public class Vector3 - { - public float X; - public float Y; - public float Z; - public static Vector3 Zero = new Vector3(); - - public Vector3(float x, float y, float z) - { - X = x; - Y = y; - Z = z; - } - - public Vector3() - { - X = 0.0f; - Y = 0.0f; - Z = 0.0f; - } - - public Vector3(SharpNav.Geometry.Vector3 vec) - { - X = vec.X; - Y = vec.Y; - Z = vec.Z; - } - - public static Vector3 operator +(Vector3 lhs, Vector3 rhs) - { - Vector3 newVec = new Vector3(lhs.X, lhs.Y, lhs.Z); - newVec.X += rhs.X; - newVec.Y += rhs.Y; - newVec.Z += rhs.Z; - return newVec; - } - - public static Vector3 operator -(Vector3 lhs, Vector3 rhs) - { - return new Vector3(lhs.X - rhs.X, lhs.Y - rhs.Y, lhs.Z - rhs.Z); - } - - public static Vector3 operator *(Vector3 lhs, Vector3 rhs) - { - return new Vector3(lhs.X * rhs.X, lhs.Y * rhs.Y, lhs.Z * rhs.Z); - } - - public static Vector3 operator *(float scalar, Vector3 rhs) - { - return new Vector3(scalar * rhs.X, scalar * rhs.Y, scalar * rhs.Z); - } - - public static Vector3 operator /(Vector3 lhs, Vector3 rhs) - { - return new Vector3(lhs.X - rhs.X, lhs.Y - rhs.Y, lhs.Z - rhs.Z); - } - - public float Length() - { - return (float)Math.Sqrt(this.LengthSquared()); - } - - public float LengthSquared() - { - return (this.X * this.X) + (this.Y * this.Y) + (this.Z * this.Z); - } - - public static float Dot(Vector3 lhs, Vector3 rhs) - { - return (lhs.X * rhs.X) + (lhs.Y * rhs.Y) + (lhs.Z * rhs.Z); - } - } - class NavmeshUtils { @@ -96,8 +24,13 @@ namespace FFXIVClassic_Map_Server.utils public static SharpNav.TiledNavMesh LoadNavmesh(TiledNavMesh navmesh, string filePath) { - var serialiser = new SharpNav.IO.Json.NavMeshJsonSerializer(); - return serialiser.Deserialize(System.IO.Path.Combine("../../navmesh/", filePath)); + SharpNav.IO.NavMeshSerializer serializer; + if (System.IO.Path.GetExtension(filePath) == ".snb") + serializer = new SharpNav.IO.Binary.NavMeshBinarySerializer(); + else + serializer = new SharpNav.IO.Json.NavMeshJsonSerializer(); + + return serializer.Deserialize(System.IO.Path.Combine("../../navmesh/", filePath)); //return navmesh = new SharpNav.IO.Json.NavMeshJsonSerializer().Deserialize(filePath); } @@ -105,11 +38,11 @@ namespace FFXIVClassic_Map_Server.utils // Copyright (c) 2013-2016 Robert Rouhani and other contributors (see CONTRIBUTORS file). // Licensed under the MIT License - https://raw.github.com/Robmaister/SharpNav/master/LICENSE - public static List GetPath(FFXIVClassic_Map_Server.actors.area.Zone zone, Vector3 startVec, Vector3 endVec, float stepSize = 0.70f, int pathSize = 45, float polyRadius = 0.0f) + public static List GetPath(FFXIVClassic_Map_Server.actors.area.Zone zone, Vector3 startVec, Vector3 endVec, float stepSize = 0.70f, int pathSize = 45, float polyRadius = 0.0f, bool skipToTarget = false) { var navMesh = zone.tiledNavMesh; var navMeshQuery = zone.navMeshQuery; - + // no navmesh loaded, run straight to player if (navMesh == null) { @@ -166,7 +99,11 @@ namespace FFXIVClassic_Map_Server.utils targetPos = randPoly.Position; } - smoothPath.Add(new Vector3(iterPos)); + if (skipToTarget) + { + return new List() { new Vector3(targetPos.X, targetPos.Y, targetPos.Z) }; + } + smoothPath.Add(new Vector3(iterPos.X, iterPos.Y, iterPos.Z)); //float STEP_SIZE = 0.70f; float SLOP = 0.15f; @@ -216,7 +153,7 @@ namespace FFXIVClassic_Map_Server.utils iterPos = targetPos; if (smoothPath.Count < smoothPath.Capacity) { - smoothPath.Add(new Vector3(iterPos)); + smoothPath.Add(new Vector3(iterPos.X, iterPos.Y, iterPos.Z)); } break; } @@ -224,7 +161,7 @@ namespace FFXIVClassic_Map_Server.utils //store results if (smoothPath.Count < smoothPath.Capacity) { - smoothPath.Add(new Vector3(iterPos)); + smoothPath.Add(new Vector3(iterPos.X, iterPos.Y, iterPos.Z)); } } }