diff --git a/.gitmodules b/.gitmodules new file mode 100644 index 00000000..e0e89768 --- /dev/null +++ b/.gitmodules @@ -0,0 +1,3 @@ +[submodule "FFXIVClassic Map Server/navmesh/rcdtcs"] + path = FFXIVClassic Map Server/navmesh/rcdtcs + url = https://github.com/jlalleve/rcdtcs diff --git a/FFXIVClassic Common Class Lib/FFXIVClassic Common Class Lib.csproj b/FFXIVClassic Common Class Lib/FFXIVClassic Common Class Lib.csproj index a08e253f..b5c7bcb2 100644 --- a/FFXIVClassic Common Class Lib/FFXIVClassic Common Class Lib.csproj +++ b/FFXIVClassic Common Class Lib/FFXIVClassic Common Class Lib.csproj @@ -36,6 +36,7 @@ prompt 4 false + true @@ -51,6 +52,7 @@ + diff --git a/FFXIVClassic Common Class Lib/Utils.cs b/FFXIVClassic Common Class Lib/Utils.cs index a6479b78..a3b596ff 100644 --- a/FFXIVClassic Common Class Lib/Utils.cs +++ b/FFXIVClassic Common Class Lib/Utils.cs @@ -351,5 +351,32 @@ namespace FFXIVClassic.Common { return (value >> bits) | (value << (16 - bits)); } + + public static float Clamp(float val, float min, float max) + { + + return Math.Max(Math.Min(max, val), min); + } + + public static float Distance(float x, float y, float z, float x2, float y2, float z2) + { + if (x == x2 && y == y2 && z == z2) + return 0.0f; + + return (float)Math.Sqrt(DistanceSquared(x, y, z, x2, y2, z2)); + } + + public static float DistanceSquared(float x, float y, float z, float x2, float y2, float z2) + { + if (x == x2 && y == y2 && z == z2) + return 0.0f; + + // todo: my maths is shit + var dx = x - x2; + var dy = y - y2; + var dz = z - z2; + + return dx * dx + dy * dy + dz * dz; + } } } \ No newline at end of file diff --git a/FFXIVClassic Map Server/App.config b/FFXIVClassic Map Server/App.config index 63198b4c..46fa2bf4 100644 --- a/FFXIVClassic Map Server/App.config +++ b/FFXIVClassic Map Server/App.config @@ -1,11 +1,20 @@  - + - \ No newline at end of file + + + + + + + + + + diff --git a/FFXIVClassic Map Server/FFXIVClassic Map Server.csproj b/FFXIVClassic Map Server/FFXIVClassic Map Server.csproj index 5934570c..131d6716 100644 --- a/FFXIVClassic Map Server/FFXIVClassic Map Server.csproj +++ b/FFXIVClassic Map Server/FFXIVClassic Map Server.csproj @@ -10,9 +10,10 @@ Properties FFXIVClassic_Map_Server FFXIVClassic Map Server - v4.5 + v4.5.1 512 1d22ec4a + AnyCPU @@ -24,6 +25,7 @@ prompt 4 true + true AnyCPU @@ -58,16 +60,20 @@ ..\packages\MySql.Data.6.9.8\lib\net45\MySql.Data.dll True - - ..\packages\Newtonsoft.Json.8.0.3\lib\net45\Newtonsoft.Json.dll - True + + ..\packages\Newtonsoft.Json.10.0.2\lib\net45\Newtonsoft.Json.dll ..\packages\NLog.4.3.5\lib\net45\NLog.dll True + + False + .\SharpNav.dll + + @@ -131,6 +137,21 @@ + + + + + + + + + + + + + + + @@ -310,6 +331,7 @@ + @@ -332,6 +354,7 @@ Resources.Designer.cs + xcopy "$(SolutionDir)data\map_config.ini" "$(SolutionDir)$(ProjectName)\$(OutDir)" /d diff --git a/FFXIVClassic Map Server/Program.cs b/FFXIVClassic Map Server/Program.cs index eb24fc4f..e4105a8d 100644 --- a/FFXIVClassic Map Server/Program.cs +++ b/FFXIVClassic Map Server/Program.cs @@ -16,6 +16,8 @@ namespace FFXIVClassic_Map_Server class Program { public static Logger Log; + public static Server Server; + public static Random Random; static void Main(string[] args) { @@ -55,9 +57,10 @@ namespace FFXIVClassic_Map_Server //Start server if A-OK if (startServer) { - Server server = new Server(); + Random = new Random(); + Server = new Server(); - server.StartServer(); + Server.StartServer(); while (startServer) { diff --git a/FFXIVClassic Map Server/Properties/Resources.Designer.cs b/FFXIVClassic Map Server/Properties/Resources.Designer.cs index 75ee4efc..11a22948 100644 --- a/FFXIVClassic Map Server/Properties/Resources.Designer.cs +++ b/FFXIVClassic Map Server/Properties/Resources.Designer.cs @@ -17,7 +17,7 @@ namespace FFXIVClassic_Map_Server.Properties { /// // This class was auto-generated by the StronglyTypedResourceBuilder // class via a tool like ResGen or Visual Studio. - // To add or Remove a member, edit your .ResX file then rerun ResGen + // To add or remove a member, edit your .ResX file then rerun ResGen // with the /str option, or rebuild your VS project. [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "4.0.0.0")] [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] @@ -105,7 +105,7 @@ namespace FFXIVClassic_Map_Server.Properties { /// ///Available commands: ///Standard: mypos, music, warp - ///Server Administration: givecurrency, giveitem, givekeyitem, Removecurrency, Removekeyitem, reloaditems, reloadzones + ///Server Administration: givecurrency, giveitem, givekeyitem, removecurrency, removekeyitem, reloaditems, reloadzones ///Test: test weather. /// public static string CPhelp { @@ -176,38 +176,38 @@ namespace FFXIVClassic_Map_Server.Properties { /// /// Looks up a localized string similar to Removes the specified currency from the current player's inventory /// - ///*Syntax: Removecurrency <quantity> - /// Removecurrency <type> <quantity> + ///*Syntax: removecurrency <quantity> + /// removecurrency <type> <quantity> ///<type> is the specific type of currency desired, defaults to gil if no type specified. /// - public static string CPRemovecurrency { + public static string CPremovecurrency { get { - return ResourceManager.GetString("CPRemovecurrency", resourceCulture); + return ResourceManager.GetString("CPremovecurrency", resourceCulture); } } /// /// Looks up a localized string similar to Removes the specified items to the current player's inventory /// - ///*Syntax: Removeitem <itemid> - /// Removeitem <itemid> <quantity> + ///*Syntax: removeitem <itemid> + /// removeitem <itemid> <quantity> ///<item id> is the item's specific id as defined in the server database. /// - public static string CPRemoveitem { + public static string CPremoveitem { get { - return ResourceManager.GetString("CPRemoveitem", resourceCulture); + return ResourceManager.GetString("CPremoveitem", resourceCulture); } } /// /// Looks up a localized string similar to Removes the specified key item to the current player's inventory /// - ///*Syntax: Removekeyitem <itemid> + ///*Syntax: removekeyitem <itemid> ///<item id> is the key item's specific id as defined in the server database. /// - public static string CPRemovekeyitem { + public static string CPremovekeyitem { get { - return ResourceManager.GetString("CPRemovekeyitem", resourceCulture); + return ResourceManager.GetString("CPremovekeyitem", resourceCulture); } } diff --git a/FFXIVClassic Map Server/SharpNav.dll b/FFXIVClassic Map Server/SharpNav.dll new file mode 100644 index 00000000..3c648e5c Binary files /dev/null and b/FFXIVClassic Map Server/SharpNav.dll differ diff --git a/FFXIVClassic Map Server/actors/Actor.cs b/FFXIVClassic Map Server/actors/Actor.cs index d75077fd..72fe5f04 100644 --- a/FFXIVClassic Map Server/actors/Actor.cs +++ b/FFXIVClassic Map Server/actors/Actor.cs @@ -40,6 +40,13 @@ namespace FFXIVClassic_Map_Server.Actors public string className; public List classParams; + public List positionUpdates = new List(); + public DateTime lastAiUpdate; + public DateTime lastMoveUpdate; + public Actor target; + + public bool hasMoved = false; + public bool isAtSpawn = true; public EventList eventConditions; public Actor(uint actorId) @@ -126,7 +133,30 @@ namespace FFXIVClassic_Map_Server.Actors public SubPacket CreatePositionUpdatePacket(uint playerActorId) { - return MoveActorToPositionPacket.BuildPacket(actorId, playerActorId, positionX, positionY, positionZ, rotation, moveState); + int updateMs = 300; + var diffTime = (DateTime.Now - lastMoveUpdate); + + if (this.target != null) + { + updateMs = 150; + } + if (diffTime.Milliseconds >= updateMs && hasMoved) + { + hasMoved = (this.positionUpdates != null && this.positionUpdates.Count > 0); + if (hasMoved) + { + var pos = positionUpdates[0]; + positionUpdates.Remove(pos); + + positionX = pos.X; + positionY = pos.Y; + positionZ = pos.Z; + //Program.Server.GetInstance().mLuaEngine.OnPath(actor, position, positionUpdates) + } + lastMoveUpdate = DateTime.Now; + return MoveActorToPositionPacket.BuildPacket(actorId, playerActorId, positionX, positionY, positionZ, rotation, moveState); + } + return null; } public SubPacket CreateStatePacket(uint playerActorID) @@ -323,7 +353,78 @@ namespace FFXIVClassic_Map_Server.Actors } public void Update(double deltaTime) - { + { + // todo: this is retarded + if (this is Zone || this is Area || this is Player) + return; + + var diffTime = (DateTime.Now - lastAiUpdate); + + // todo: this too + if (diffTime.Milliseconds >= deltaTime) + { + bool foundActor = false; + foreach (var actor in ((Area)zone).GetActorsAroundActor(this, 50)) + { + if (actor is Player && actor != this) + { + var player = actor as Player; + + var distance = Utils.Distance(positionX, positionY, positionZ, player.positionX, player.positionY, player.positionZ); + + int maxDistance = player == target ? 25 : 15; + + if (distance <= maxDistance) + { + foundActor = true; + + if (!hasMoved) + { + if (distance >= 3) + { + FollowTarget(player, 2.0f); + } + // too close, spread out + else if (distance <= 0.64f) + { + var minRadius = 0.65f; + var maxRadius = 0.85f; + + var angle = Program.Random.NextDouble() * Math.PI * 2; + var radius = Math.Sqrt(Program.Random.NextDouble() * (maxRadius - minRadius)) + minRadius; + + float x = (float)(radius * Math.Cos(angle)); + float z = (float)(radius * Math.Sin(angle)); + + positionUpdates.Add(new utils.Vector3(positionX + x, positionY, positionZ + z)); + + hasMoved = true; + } + else if (target != null) + { + // todo: actually make IsFacing work + if(!IsFacing(target.positionX, target.positionY)) + LookAt(target); + } + } + } + break; + } + } + // 5 seconds before back to spawn + if ((DateTime.Now - lastMoveUpdate).Seconds >= 5 && !foundActor) + { + // 10 yalms spawn radius just because + this.isAtSpawn = Utils.Distance(positionX, positionY, positionZ, oldPositionX, oldPositionY, oldPositionZ) <= 18.0f; + + if (this.isAtSpawn != true && this.target == null && oldPositionX != 0.0f && oldPositionY != 0.0f && oldPositionZ != 0.0f) + PathTo(oldPositionX, oldPositionY, oldPositionZ, 3.0f); + + lastMoveUpdate = DateTime.Now; + this.target = null; + } + lastAiUpdate = DateTime.Now; + } } public void GenerateActorName(int actorNumber) @@ -492,6 +593,110 @@ namespace FFXIVClassic_Map_Server.Actors { return zoneId; } + + public bool IsFacing(float x, float y, byte checkAngle = 45) + { + var rot1 = this.rotation; + + var dX = this.positionX - x; + var dY = this.positionY - y; + + var rot2 = Math.Atan2(dY, dX); + + var dRot = Math.PI - rot2 + Math.PI / 2; + + Program.Log.Error("IsFacing Rotation {0} Rotation2 {1}", rot1, rot2); + return rot1 == rot2; + } + + public void LookAt(Actor actor) + { + if (actor != null) + { + LookAt(actor.positionX, actor.positionY); + } + else + { + Program.Log.Error("Actor.LookAt() unable to find actor!"); + } + } + + public void LookAt(float x, float y) + { + var rot1 = this.rotation; + + var dX = this.positionX - x; + var dY = this.positionY - y; + + var rot2 = Math.Atan2(dY, dX); + + var dRot = Math.PI - rot2 + Math.PI / 2; + + Program.Log.Error("LookAt Rotation {0} Rotation2 {1}", rot1, rot2); + + this.hasMoved = rot2 != rot1; + this.rotation = (float)dRot; + } + + public void PathTo(float x, float y, float z, float stepSize = 0.70f, int maxPath = 40) + { + var pos = new utils.Vector3(positionX, positionY, positionZ); + var dest = new utils.Vector3(x, y, z); + + var sw = new System.Diagnostics.Stopwatch(); + sw.Start(); + + var path = utils.NavmeshUtils.GetPath(((Zone)GetZone()), pos, dest, stepSize, maxPath); + + if (path != null) + { + if (oldPositionX == 0.0f && oldPositionY == 0.0f && oldPositionZ == 0.0f) + { + oldPositionX = positionX; + oldPositionY = positionY; + oldPositionZ = positionZ; + } + + // todo: something went wrong + if (path.Count == 0) + { + positionX = oldPositionX; + positionY = oldPositionY; + positionZ = oldPositionZ; + } + + positionUpdates = path; + + this.hasMoved = true; + this.isAtSpawn = false; + + sw.Stop(); + ((Zone)zone).pathCalls++; + ((Zone)zone).pathCallTime += 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) + { + if (target != null) + { + var player = target as Player; + + if (this.target != target) + this.target = target; + + this.moveState = player.moveState; + this.moveSpeeds = player.moveSpeeds; + + PathTo(player.positionX, player.positionY, player.positionZ, stepSize, maxPath); + } + } + + public void OnPath() + { + // todo: lua function onPath in mob script + } } } diff --git a/FFXIVClassic Map Server/actors/area/Zone.cs b/FFXIVClassic Map Server/actors/area/Zone.cs index 0f26abfb..a55165c8 100644 --- a/FFXIVClassic Map Server/actors/area/Zone.cs +++ b/FFXIVClassic Map Server/actors/area/Zone.cs @@ -10,6 +10,8 @@ using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; +using System.IO; + using FFXIVClassic_Map_Server.actors.director; namespace FFXIVClassic_Map_Server.actors.area @@ -20,10 +22,31 @@ namespace FFXIVClassic_Map_Server.actors.area Dictionary> contentAreas = new Dictionary>(); Object contentAreasLock = new Object(); + public Detour.dtNavMesh navMesh; + public SharpNav.TiledNavMesh tiledNavMesh; + public SharpNav.NavMeshQuery navMeshQuery; + public Int64 pathCalls; + public Int64 pathCallTime; + 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) : base(id, zoneName, regionId, classPath, bgmDay, bgmNight, bgmBattle, isIsolated, isInn, canRideChocobo, canStealth, isInstanceRaid) { - + // central thanalan navmesh + if (id == 170) + { + try + { + //navMesh = utils.NavmeshUtils.LoadNavmesh("wil_w0_fld01.bin"); + tiledNavMesh = utils.NavmeshUtils.LoadNavmesh(tiledNavMesh, "wil_w0_fld01.snb"); + navMeshQuery = new SharpNav.NavMeshQuery(tiledNavMesh, 100); + GC.Collect(2); + } + catch(Exception e) + { + Program.Log.Error(e.Message); + } + + } } public void AddPrivateArea(PrivateArea pa) diff --git a/FFXIVClassic Map Server/actors/chara/npc/Npc.cs b/FFXIVClassic Map Server/actors/chara/npc/Npc.cs index 05b694b0..d7a94eee 100644 --- a/FFXIVClassic Map Server/actors/chara/npc/Npc.cs +++ b/FFXIVClassic Map Server/actors/chara/npc/Npc.cs @@ -151,8 +151,8 @@ namespace FFXIVClassic_Map_Server.Actors isStatic = true; else { - // charaWork.property[2] = 1; - // npcWork.hateType = 1; + charaWork.property[2] = 1; + npcWork.hateType = 1; } if (lParams == null) diff --git a/FFXIVClassic Map Server/bin/Debug/scripts/commands/gm/yolo.lua b/FFXIVClassic Map Server/bin/Debug/scripts/commands/gm/yolo.lua new file mode 100644 index 00000000..9d2c364f --- /dev/null +++ b/FFXIVClassic Map Server/bin/Debug/scripts/commands/gm/yolo.lua @@ -0,0 +1,169 @@ +require("global"); + +properties = { + permissions = 0, + parameters = "ssss", + description = +[[ +yolo +]], +} + +local quests = +{ + [111807] = { level = 25, weight = 4, rewardexp = 1080 }, + [110868] = { level = 50, weight = 4, rewardexp = 4400 }, + [111603] = { level = 22, weight = 5, rewardexp = 1100 }, + [111602] = { level = 22, weight = 5, rewardexp = 1100 }, + [111420] = { level = 45, weight = 5, rewardexp = 4450 }, + [110811] = { level = 18, weight = 6, rewardexp = 780 }, + [110814] = { level = 18, weight = 6, rewardexp = 780 }, + [110707] = { level = 25, weight = 6, rewardexp = 1620 }, + [110682] = { level = 34, weight = 6, rewardexp = 3180 }, + [111202] = { level = 35, weight = 6, rewardexp = 3360 }, + [111222] = { level = 35, weight = 6, rewardexp = 3360 }, + [111302] = { level = 35, weight = 6, rewardexp = 3360 }, + [111223] = { level = 40, weight = 6, rewardexp = 4260 }, + [110819] = { level = 45, weight = 6, rewardexp = 5340 }, + [111224] = { level = 45, weight = 6, rewardexp = 5340 }, + [111225] = { level = 45, weight = 6, rewardexp = 5340 }, + [110867] = { level = 45, weight = 6, rewardexp = 5340 }, + [110869] = { level = 45, weight = 6, rewardexp = 5340 }, + [110708] = { level = 45, weight = 6, rewardexp = 5340 }, + [110627] = { level = 45, weight = 6, rewardexp = 5340 }, + [111434] = { level = 50, weight = 6, rewardexp = 6600 }, + [110850] = { level = 1, weight = 7, rewardexp = 40 }, + [110851] = { level = 1, weight = 7, rewardexp = 40 }, + [110841] = { level = 20, weight = 7, rewardexp = 1120 }, + [110642] = { level = 20, weight = 7, rewardexp = 1120 }, + [110840] = { level = 20, weight = 7, rewardexp = 1120 }, + [110727] = { level = 21, weight = 7, rewardexp = 1401 }, + [111221] = { level = 30, weight = 7, rewardexp = 2661 }, + [111241] = { level = 30, weight = 7, rewardexp = 2661 }, + [110687] = { level = 28, weight = 9, rewardexp = 2970 }, + [110016] = { level = 34, weight = 50, rewardexp = 26500 }, + [110017] = { level = 38, weight = 50, rewardexp = 32500 }, + [110019] = { level = 46, weight = 50, rewardexp = 46000 } +}; + +local expTable = { + 570, -- 1 + 700, + 880, + 1100, + 1500, + 1800, + 2300, + 3200, + 4300, + 5000, -- 10 + 5900, + 6800, + 7700, + 8700, + 9700, + 11000, + 12000, + 13000, + 15000, + 16000, -- 20 + 20000, + 22000, + 23000, + 25000, + 27000, + 29000, + 31000, + 33000, + 35000, + 38000, -- 30 + 45000, + 47000, + 50000, + 53000, + 56000, + 59000, + 62000, + 65000, + 68000, + 71000, -- 40 + 74000, + 78000, + 81000, + 85000, + 89000, + 92000, + 96000, + 100000, + 100000, + 110000 -- 50 +}; + +local commandCost = { + ["raise"] = 150, + ["cure"] = 40, + ["cura"] = 100, + ["curaga"] = 150, +}; +-- stone: (1, 9) (5, 12) (10, ) +-- cure: (1, 5) (5, 6) (10, ) +-- aero: (1, 9) (5, 12) (10, ) +-- protect: (1, 9) (5, 12) (10, ) +--[[ +function onTrigger(player, argc, id, level, weight) + id = tonumber(id) or 111807; + level = tonumber(level) or quests[id].level; + weight = tonumber(weight) or quests[id].weight; + local messageId = MESSAGE_TYPE_SYSTEM_ERROR; + local sender = "yolo"; + + if id == 1 then + return + end + local message = calcSkillPoint(player, level, weight); + if player then + player.SendMessage(messageId, sender, string.format("calculated %s | expected %s", message, quests[id].rewardexp)); + end; + printf("calculated %s | expected %s", message, quests[id].rewardexp); +end; +]] + +function onTrigger(player, argc, skillName, level) + local messageId = MESSAGE_TYPE_SYSTEM_ERROR; + local sender = "yolo"; + if player then + local pos = player:GetPos(); + local x = tonumber(pos[0]); + local y = tonumber(pos[1]); + local z = tonumber(pos[2]); + local rot = tonumber(pos[3]); + local zone = pos[4]; + + printf("%f %f %f", x, y, z); + --local x, y, z = player.GetPos(); + for i = 1, 1 do + + local actor = player.GetZone().SpawnActor(1000070, 'ass', x-(13 + 2*i), y, z); + + actor.FollowTarget(player, 3.2); + end; + return; + end + level = tonumber(level) or 1; + if player then + player.SendMessage(messageId, sender, string.format("name %s | cost %d | level %u", skillName, calculateCommandCost(player, skillName, level), level)); + end; +end; + +function calculateCommandCost(player, skillName, level) + if skillName and level and commandCost[skillName] then + return math.ceil((8000 + (level - 70) * 500) * (commandCost[skillName] * 0.001)); + end; + return 1; +end + +function calcSkillPoint(player, lvl, weight) + weight = weight / 100 + + return math.ceil(expTable[lvl] * weight) +end \ No newline at end of file diff --git a/FFXIVClassic Map Server/dataobjects/Session.cs b/FFXIVClassic Map Server/dataobjects/Session.cs index bdfcc561..fd7f69d1 100644 --- a/FFXIVClassic Map Server/dataobjects/Session.cs +++ b/FFXIVClassic Map Server/dataobjects/Session.cs @@ -120,6 +120,7 @@ namespace FFXIVClassic_Map_Server.dataobjects } + bool checkedThisTick = false; //Add new actors or move for (int i = 0; i < list.Count; i++) { @@ -131,10 +132,25 @@ namespace FFXIVClassic_Map_Server.dataobjects if (actorInstanceList.Contains(actor)) { //Don't send for static characters (npcs) - if (actor is Character && ((Character)actor).isStatic) + // todo: this is retarded, need actual mob class + if (actor is Character && !actor.hasMoved && ((Character)actor).isStatic) continue; - GetActor().QueuePacket(actor.CreatePositionUpdatePacket(playerActor.actorId)); + // todo: again, this is retarded but debug stuff + var zone = (actors.area.Zone)actor.zone; + if(zone != null && !checkedThisTick) + { + if (zone.pathCalls > 0) + { + checkedThisTick = true; + Program.Log.Error("Number of pathfinding calls {0} average time {1}", zone.pathCalls, zone.pathCallTime / zone.pathCalls); + } + } + + var packet = actor.CreatePositionUpdatePacket(playerActor.actorId); + + if (packet != null) + GetActor().QueuePacket(packet); } else { diff --git a/FFXIVClassic Map Server/lua/LuaEngine.cs b/FFXIVClassic Map Server/lua/LuaEngine.cs index 65f146d3..c3c77720 100644 --- a/FFXIVClassic Map Server/lua/LuaEngine.cs +++ b/FFXIVClassic Map Server/lua/LuaEngine.cs @@ -533,11 +533,11 @@ namespace FFXIVClassic_Map_Server.lua LuaParam.Insert(1, i - (playerNull ? 2 : 0)); // run the script - //script.Call(script.Globals["onTrigger"], LuaParam.ToArray()); + script.Call(script.Globals["onTrigger"], LuaParam.ToArray()); - Coroutine coroutine = script.CreateCoroutine(script.Globals["onTrigger"]).Coroutine; - DynValue value = coroutine.Resume(LuaParam.ToArray()); - GetInstance().ResolveResume(player, coroutine, value); + //Coroutine coroutine = script.CreateCoroutine(script.Globals["onTrigger"]).Coroutine; + //DynValue value = coroutine.Resume(LuaParam.ToArray()); + //GetInstance().ResolveResume(player, coroutine, value); return; } } diff --git a/FFXIVClassic Map Server/navmesh/rcdtcs b/FFXIVClassic Map Server/navmesh/rcdtcs new file mode 160000 index 00000000..8eea2772 --- /dev/null +++ b/FFXIVClassic Map Server/navmesh/rcdtcs @@ -0,0 +1 @@ +Subproject commit 8eea27727df5132c5f0e6732d1bd36238ce300ea diff --git a/FFXIVClassic Map Server/packages.config b/FFXIVClassic Map Server/packages.config index 4e137685..affb0e30 100644 --- a/FFXIVClassic Map Server/packages.config +++ b/FFXIVClassic Map Server/packages.config @@ -5,7 +5,7 @@ - + diff --git a/FFXIVClassic Map Server/utils/NavmeshUtils.cs b/FFXIVClassic Map Server/utils/NavmeshUtils.cs new file mode 100644 index 00000000..a2a00145 --- /dev/null +++ b/FFXIVClassic Map Server/utils/NavmeshUtils.cs @@ -0,0 +1,313 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using System.IO; +using SharpNav; +using SharpNav.Pathfinding; +using SharpNav.Crowds; +using SharpNav.IO; + +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 + { + + // navmesh + public static bool CanSee(float x1, float y1, float z1, float x2, float y2, float z2) + { + + return false; + } + + public static Detour.dtNavMesh LoadNavmesh(string path) + { + var bytes = File.ReadAllBytes(path); + var start = 0; + + var navmesh = Detour.NavMeshSetBuild.FromBytes(bytes, ref start); + return navmesh; + } + + public static SharpNav.TiledNavMesh LoadNavmesh(TiledNavMesh navmesh, string filePath) + { + var serialiser = new SharpNav.IO.Json.NavMeshJsonSerializer(); + return serialiser.Deserialize(filePath); + //return navmesh = new SharpNav.IO.Json.NavMeshJsonSerializer().Deserialize(filePath); + } + + public static List GetPath(Detour.dtNavMesh navmesh, Vector3 start, Vector3 end) + { + var path = new Vector3[] { }; + + + return path.ToList(); + } + + #region sharpnav stuff + // 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 = 2048) + { + var navMesh = zone.tiledNavMesh; + var navMeshQuery = zone.navMeshQuery; + + if (navMesh == null || (startVec.X == endVec.X && startVec.Y == endVec.Y && startVec.Z == endVec.Z)) + { + return null; + } + + float distanceSquared = FFXIVClassic.Common.Utils.DistanceSquared(startVec.X, startVec.Y, startVec.Z, endVec.X, endVec.Y, endVec.Z); + + // no point pathing if in range + if (distanceSquared < 4 && Math.Abs(startVec.Y - endVec.Y) < 1.1f) + { + return null; + } + + var smoothPath = new List(pathSize) { }; + + NavQueryFilter filter = new NavQueryFilter(); + + NavPoint startPt, endPt; + RaycastHit hit; + PathCorridor corridor = new PathCorridor(); + + try + { + SharpNav.Geometry.Vector3 c = new SharpNav.Geometry.Vector3(startVec.X, startVec.Y, startVec.Z); + SharpNav.Geometry.Vector3 ep = new SharpNav.Geometry.Vector3(endVec.X, endVec.Y, endVec.Z); + + SharpNav.Geometry.Vector3 e = new SharpNav.Geometry.Vector3(5, 5, 5); + navMeshQuery.FindNearestPoly(ref c, ref e, out startPt); + navMeshQuery.FindNearestPoly(ref ep, ref e, out endPt); + + //calculate the overall path, which contains an array of polygon references + int MAX_POLYS = 256; + var path = new SharpNav.Pathfinding.Path(); + + navMeshQuery.FindPath(ref startPt, ref endPt, filter, path); + + //find a smooth path over the mesh surface + int npolys = path.Count; + SharpNav.Geometry.Vector3 iterPos = new SharpNav.Geometry.Vector3(); + SharpNav.Geometry.Vector3 targetPos = new SharpNav.Geometry.Vector3(); + navMeshQuery.ClosestPointOnPoly(startPt.Polygon, startPt.Position, ref iterPos); + navMeshQuery.ClosestPointOnPoly(path[npolys - 1], endPt.Position, ref targetPos); + + smoothPath.Add(new Vector3(iterPos)); + + //float STEP_SIZE = 0.70f; + float SLOP = 0.15f; + while (npolys > 0 && smoothPath.Count < smoothPath.Capacity) + { + //find location to steer towards + SharpNav.Geometry.Vector3 steerPos = new SharpNav.Geometry.Vector3(); + StraightPathFlags steerPosFlag = 0; + NavPolyId steerPosRef = NavPolyId.Null; + + if (!GetSteerTarget(navMeshQuery, iterPos, targetPos, SLOP, path, ref steerPos, ref steerPosFlag, ref steerPosRef)) + break; + + bool endOfPath = (steerPosFlag & StraightPathFlags.End) != 0 ? true : false; + bool offMeshConnection = (steerPosFlag & StraightPathFlags.OffMeshConnection) != 0 ? true : false; + + //find movement delta + SharpNav.Geometry.Vector3 delta = steerPos - iterPos; + float len = (float)Math.Sqrt(SharpNav.Geometry.Vector3.Dot(delta, delta)); + + //if steer target is at end of path or off-mesh link + //don't move past location + if ((endOfPath || offMeshConnection) && len < stepSize) + len = 1; + else + len = stepSize / len; + + SharpNav.Geometry.Vector3 moveTgt = new SharpNav.Geometry.Vector3(); + VMad(ref moveTgt, iterPos, delta, len); + + //move + SharpNav.Geometry.Vector3 result = new SharpNav.Geometry.Vector3(); + List visited = new List(pathSize); + NavPoint startPoint = new NavPoint(path[0], iterPos); + navMeshQuery.MoveAlongSurface(ref startPoint, ref moveTgt, out result, visited); + path.FixupCorridor(visited); + npolys = path.Count; + float h = 0; + navMeshQuery.GetPolyHeight(path[0], result, ref h); + result.Y = h; + iterPos = result; + + //handle end of path when close enough + if (endOfPath && InRange(iterPos, steerPos, SLOP, 1000.0f)) + { + //reached end of path + iterPos = targetPos; + if (smoothPath.Count < smoothPath.Capacity) + { + smoothPath.Add(new Vector3(iterPos)); + } + break; + } + + //store results + if (smoothPath.Count < smoothPath.Capacity) + { + smoothPath.Add(new Vector3(iterPos)); + } + } + } + catch(Exception e) + { + Program.Log.Error(e.Message); + Program.Log.Error("Start pos {0} {1} {2} end pos {3} {4} {5}", startVec.X, startVec.Y, startVec.Z, endVec.X, endVec.Y, endVec.Z); + // todo: probably log this + return new List() { }; + } + return smoothPath; + } + + /// + /// Scaled vector addition + /// + /// Result + /// Vector 1 + /// Vector 2 + /// Scalar + private static void VMad(ref SharpNav.Geometry.Vector3 dest, SharpNav.Geometry.Vector3 v1, SharpNav.Geometry.Vector3 v2, float s) + { + dest.X = v1.X + v2.X * s; + dest.Y = v1.Y + v2.Y * s; + dest.Z = v1.Z + v2.Z * s; + } + + private static bool GetSteerTarget(NavMeshQuery navMeshQuery, SharpNav.Geometry.Vector3 startPos, SharpNav.Geometry.Vector3 endPos, float minTargetDist, SharpNav.Pathfinding.Path path, + ref SharpNav.Geometry.Vector3 steerPos, ref StraightPathFlags steerPosFlag, ref NavPolyId steerPosRef) + { + StraightPath steerPath = new StraightPath(); + navMeshQuery.FindStraightPath(startPos, endPos, path, steerPath, 0); + int nsteerPath = steerPath.Count; + if (nsteerPath == 0) + return false; + + //find vertex far enough to steer to + int ns = 0; + while (ns < nsteerPath) + { + if ((steerPath[ns].Flags & StraightPathFlags.OffMeshConnection) != 0 || + !InRange(steerPath[ns].Point.Position, startPos, minTargetDist, 1000.0f)) + break; + + ns++; + } + + //failed to find good point to steer to + if (ns >= nsteerPath) + return false; + + steerPos = steerPath[ns].Point.Position; + steerPos.Y = startPos.Y; + steerPosFlag = steerPath[ns].Flags; + if (steerPosFlag == StraightPathFlags.None && ns == (nsteerPath - 1)) + steerPosFlag = StraightPathFlags.End; // otherwise seeks path infinitely!!! + steerPosRef = steerPath[ns].Point.Polygon; + + return true; + } + + private static bool InRange(SharpNav.Geometry.Vector3 v1, SharpNav.Geometry.Vector3 v2, float r, float h) + { + float dx = v2.X - v1.X; + float dy = v2.Y - v1.Y; + float dz = v2.Z - v1.Z; + return (dx * dx + dz * dz) < (r * r) && Math.Abs(dy) < h; + } + #endregion + + public static Vector3 GamePosToNavmeshPos(float x, float y, float z) + { + return new Vector3(x, -z, y); + } + + public static Vector3 NavmeshPosToGamePos(float x, float y, float z) + { + return new Vector3(x, z, -y); + } + + } +} diff --git a/sql/gamedata_actor_class.sql b/sql/gamedata_actor_class.sql index 2941e324..6b10502f 100644 --- a/sql/gamedata_actor_class.sql +++ b/sql/gamedata_actor_class.sql @@ -8,6 +8,7 @@ Date: 6/19/2017 10:23:48 PM */ SET FOREIGN_KEY_CHECKS=0; +SET autocommit=0; -- ---------------------------- -- Table structure for gamedata_actor_class -- ---------------------------- @@ -8007,3 +8008,5 @@ INSERT INTO `gamedata_actor_class` VALUES ('9220405', '', '2', '0', null); INSERT INTO `gamedata_actor_class` VALUES ('9220406', '', '2', '0', null); INSERT INTO `gamedata_actor_class` VALUES ('9220407', '', '2', '0', null); INSERT INTO `gamedata_actor_class` VALUES ('9220408', '', '2', '0', null); + +COMMIT; \ No newline at end of file diff --git a/sql/server_spawn_locations.sql b/sql/server_spawn_locations.sql index 82e195f9..63bde218 100644 --- a/sql/server_spawn_locations.sql +++ b/sql/server_spawn_locations.sql @@ -6,6 +6,7 @@ Target Host: localhost Target Database: ffxiv_server Date: 6/19/2017 10:24:01 PM */ +DROP TABLE IF EXISTS `server_spawn_locations`; SET FOREIGN_KEY_CHECKS=0; -- ----------------------------