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;
-- ----------------------------