diff --git a/FFXIVClassic Map Server/FFXIVClassic Map Server.csproj b/FFXIVClassic Map Server/FFXIVClassic Map Server.csproj
index 8a1c780b..5d2f424b 100644
--- a/FFXIVClassic Map Server/FFXIVClassic Map Server.csproj
+++ b/FFXIVClassic Map Server/FFXIVClassic Map Server.csproj
@@ -182,6 +182,7 @@
+
diff --git a/FFXIVClassic Map Server/actors/area/Area.cs b/FFXIVClassic Map Server/actors/area/Area.cs
index 25350da7..a6ab5aa0 100644
--- a/FFXIVClassic Map Server/actors/area/Area.cs
+++ b/FFXIVClassic Map Server/actors/area/Area.cs
@@ -407,6 +407,34 @@ namespace FFXIVClassic_Map_Server.Actors
return npc;
}
+ public Npc SpawnActor(uint classId, string uniqueId, float x, float y, float z, uint regionId, uint layoutId)
+ {
+ ActorClass actorClass = Server.GetWorldManager().GetActorClass(classId);
+
+ if (actorClass == null)
+ return null;
+
+ uint zoneId;
+
+ if (this is PrivateArea)
+ zoneId = ((PrivateArea)this).GetParentZone().actorId;
+ else
+ zoneId = actorId;
+
+ Npc npc = new Npc(mActorList.Count + 1, actorClass, uniqueId, this, x, y, z, 0, regionId, layoutId);
+
+ npc.LoadEventConditions(actorClass.eventConditions);
+
+ AddActorToZone(npc);
+
+ return npc;
+ }
+
+ public void DespawnActor(string uniqueId)
+ {
+ RemoveActorFromZone(FindActorInZoneByUniqueID(uniqueId));
+ }
+
public Director GetWeatherDirector()
{
return mWeatherDirector;
diff --git a/FFXIVClassic Map Server/actors/chara/npc/Npc.cs b/FFXIVClassic Map Server/actors/chara/npc/Npc.cs
index 905921ce..148b586a 100644
--- a/FFXIVClassic Map Server/actors/chara/npc/Npc.cs
+++ b/FFXIVClassic Map Server/actors/chara/npc/Npc.cs
@@ -24,6 +24,7 @@ namespace FFXIVClassic_Map_Server.Actors
{
private uint actorClassId;
private string uniqueIdentifier;
+ private uint regionId, layoutId;
public NpcWork npcWork = new NpcWork();
@@ -71,6 +72,43 @@ namespace FFXIVClassic_Map_Server.Actors
GenerateActorName((int)actorNumber);
}
+ public Npc(int actorNumber, ActorClass actorClass, string uniqueId, Area spawnedArea, float posX, float posY, float posZ, float rot, uint region, uint layout)
+ : base((4 << 28 | spawnedArea.actorId << 19 | (uint)actorNumber))
+ {
+ this.positionX = posX;
+ this.positionY = posY;
+ this.positionZ = posZ;
+ this.rotation = rot;
+ this.currentMainState = 0;
+ this.animationId = 0;
+
+ this.displayNameId = actorClass.displayNameId;
+
+ this.uniqueIdentifier = uniqueId;
+
+ this.zoneId = spawnedArea.actorId;
+ this.zone = spawnedArea;
+
+ this.actorClassId = actorClass.actorClassId;
+
+ LoadNpcAppearance(actorClass.actorClassId);
+
+ this.classPath = actorClass.classPath;
+ className = classPath.Substring(classPath.LastIndexOf("/") + 1);
+
+ for (int i = 0; i < 32; i++)
+ charaWork.property[i] = (byte)(((int)actorClass.propertyFlags >> i) & 1);
+
+ npcWork.pushCommand = actorClass.pushCommand;
+ npcWork.pushCommandSub = actorClass.pushCommandSub;
+ npcWork.pushCommandPriority = actorClass.pushCommandPriority;
+
+ this.regionId = region;
+ this.layoutId = layout;
+
+ GenerateActorName((int)actorNumber);
+ }
+
public SubPacket CreateAddActorPacket(uint playerActorId)
{
return AddActorPacket.BuildPacket(actorId, playerActorId, 8);
@@ -100,7 +138,16 @@ namespace FFXIVClassic_Map_Server.Actors
// npcWork.hateType = 1;
}
- if (lParams == null)
+ if (regionId != 0 && layoutId != 0)
+ {
+ string classPathFake = "/Chara/Npc/MapObj/MapObjStandard";
+ string classNameFake = "MapObjStandard";
+ lParams = LuaUtils.CreateLuaParamList(classPathFake, false, false, false, false, false, actorClassId, false, false, 0, 0, regionId, layoutId);
+ isStatic = true;
+ //ActorInstantiatePacket.BuildPacket(actorId, playerActorId, actorName, classNameFake, lParams).DebugPrintSubPacket();
+ return ActorInstantiatePacket.BuildPacket(actorId, playerActorId, actorName, classNameFake, lParams);
+ }
+ else if (lParams == null)
{
string classPathFake = "/Chara/Npc/Populace/PopulaceStandard";
string classNameFake = "PopulaceStandard";
@@ -132,7 +179,11 @@ namespace FFXIVClassic_Map_Server.Actors
subpackets.Add(CreateSpeedPacket(playerActorId));
subpackets.Add(CreateSpawnPositonPacket(playerActorId, 0x0));
- if (uniqueIdentifier.Equals("door1"))
+ if (regionId != 0 && layoutId != 0)
+ {
+ subpackets.Add(_0xD8Packet.BuildPacket(actorId, playerActorId, layoutId, regionId));
+ }
+ else if (uniqueIdentifier.Equals("door1"))
{
subpackets.Add(_0xD8Packet.BuildPacket(actorId, playerActorId, 0xB0D, 0x1af));
}
@@ -384,6 +435,11 @@ namespace FFXIVClassic_Map_Server.Actors
LuaEngine.GetInstance().CallLuaFunction(player, this, "onSpawn", true);
}
+ public void PlayMapObjAnimation(Player player, string animationName)
+ {
+ player.QueuePacket(PlayBGAnimation.BuildPacket(actorId, player.actorId, animationName));
+ }
+
public void Update(double deltaTime)
{
LuaEngine.GetInstance().CallLuaFunction(null, this, "onUpdate", true, deltaTime);
diff --git a/FFXIVClassic Map Server/packets/send/Actor/PlayBGAnimation.cs b/FFXIVClassic Map Server/packets/send/Actor/PlayBGAnimation.cs
new file mode 100644
index 00000000..7ffc20f7
--- /dev/null
+++ b/FFXIVClassic Map Server/packets/send/Actor/PlayBGAnimation.cs
@@ -0,0 +1,31 @@
+using FFXIVClassic.Common;
+using System;
+using System.IO;
+
+using FFXIVClassic.Common;
+using System.Text;
+
+namespace FFXIVClassic_Map_Server.packets.send.actor
+{
+ class PlayBGAnimation
+ {
+ public const ushort OPCODE = 0x00D9;
+ public const uint PACKET_SIZE = 0x28;
+
+ public static SubPacket BuildPacket(uint playerActorID, uint targetActorID, string animName)
+ {
+ byte[] data = new byte[PACKET_SIZE - 0x20];
+
+ using (MemoryStream mem = new MemoryStream(data))
+ {
+ using (BinaryWriter binWriter = new BinaryWriter(mem))
+ {
+ binWriter.Write(Encoding.ASCII.GetBytes(animName), 0, Encoding.ASCII.GetByteCount(animName) > 0x8 ? 0x8 : Encoding.ASCII.GetByteCount(animName));
+ }
+ }
+
+ return new SubPacket(OPCODE, playerActorID, targetActorID, data);
+ }
+
+ }
+}
diff --git a/data/scripts/aetheryte.lua b/data/scripts/aetheryte.lua
index c14f7ae9..ea61626a 100644
--- a/data/scripts/aetheryte.lua
+++ b/data/scripts/aetheryte.lua
@@ -109,22 +109,97 @@ aetheryteChildLinks = {
}
aetheryteTeleportPositions = {
- [1280001] = {230, -407, 42.5, 337},
- [1280002] = {128, 29.97, 45.83, -35.47},
- [1280003] = {129, -991.88, 61.71, -1120.79},
- [1280004] = {129, -1883.47, 53.77, -1372.68},
- [1280005] = {130, 1123.29, 45.7, -928.69},
- [1280006] = {135, -278.181, 77.63, -2260.79},
+ --La Noscea
+ [1280001] = {230, -407, 42.5, 337}, -- CAP
+ [1280002] = {128, 29.97, 45.83, -35.47}, -- CAP
+ [1280003] = {129, -991.88, 61.71, -1120.79}, -- CAP
+ [1280004] = {129, -1883.47, 53.77, -1372.68}, -- CAP
+ [1280005] = {130, 1123.29, 45.7, -928.69}, -- CAP
+ [1280006] = {135, -278.181, 77.63, -2260.79}, -- CAP
[1280007] = {128, 582.47, 54.52, -1.2},
- [1280020] = {132, 1343.5, -54.38, -870.84},
-
- [1280031] = {175, -235, 185, -3.9},
- [1280033] = {171, 1250.9, 264, -544.2},
-
- [1280061] = {206, -120, 16, -1332},
- [1280062] = {150, 288, 4, -543.928},
- [1280063] = {151, 1702, 20, -862},
- [1280064] = {152, -1052, 20, -1760},
- [1280065] = {153, -1566.035, -11.89, -550.51},
- [1280066] = {154, 734, -12, 1126}
-}
\ No newline at end of file
+ [1280008] = {128, 962.836, 46.507, 832.206}, -- Widow Cliffs http://ic.pics.livejournal.com/eijih/14054410/1355/1355_original.jpg
+ [1280009] = {128, 318, 24.5, 581}, -- Moraby Bay http://ic.pics.livejournal.com/eijih/14054410/1092/1092_original.jpg
+ [1280010] = {129, -636, 48.8, -1287}, -- Woad Whisper Canyon
+ [1280011] = {129, -2016.72, 60.055, -766.962}, -- Isles of Umbra http://ic.pics.livejournal.com/eijih/14054410/2243/2243_original.jpg
+ [1280012] = {130, 1628, 60.3, -449}, -- Tiger Helm Island http://ic.pics.livejournal.com/eijih/14054410/2032/2032_original.jpg
+ [1280013] = {130, 1522, 1.7, -669},-- Bloodshore http://ic.pics.livejournal.com/eijih/14054410/1607/1607_original.jpg
+ [1280014] = {130, 1410, 53.3, -1650}, -- Agelyss Wise
+ [1280015] = {135, -123.315, 60.061, -1438.8}, -- Zelma's Run https://youtu.be/97Ju0Xv-aaQ?t=102
+ [1280016] = {135, -320.322, 52.835, -1823.68}, -- Bronze Lake http://ic.pics.livejournal.com/eijih/14054410/2503/2503_original.jpg
+ [1280017] = {135, -894, 41.2, -2188}, -- Oakwood
+ [1280018] = {131, -1694.5, -19.9, -1534.}, -- Mistbeard Cove
+ [1280020] = {132, 1343.5, -54.38, -870.84}, -- CAP
+ --Thanalan
+ [1280031] = {175, -235, 185, -3.9}, -- CAP
+ [1280032] = {170, 33, 200.1, -482}, -- Camp Black Brush
+ [1280033] = {171, 1250.9, 264, -544.2}, -- CAP
+ [1280034] = {172, -1313.91, 56.023, -145.597}, -- Camp Horizon https://www.youtube.com/watch?v=mQAK4QVnx3c
+ [1280035] = {173, -165.816, 280.002, -1698.45}, -- Camp Bluefog
+ [1280036] = {174, 1687.64, 296.002, 992.283}, -- Camp Brokenwater https://www.youtube.com/watch?v=YyBYHg9h2AM
+ [1280037] = {170, 639, 183.9, 122}, -- Cactus Basin
+ [1280038] = {170, 539, 215.8, -14}, -- Four Sisters
+ [1280039] = {171, 1599, 256.7, -233}, -- Halatali
+ [1280040] = {171, 2010, 280.3, -768}, -- Burning Wall
+ [1280041] = {171, 2015, 247.8, 64}, -- Sandgate
+ [1280042] = {172, -864.991, 88.84, 375.18}, -- Nophica's Wells https://www.youtube.com/watch?v=pk4POCDQ9QE
+ [1280043] = {172, -1653, 24.5, -469}, -- Footfalls
+ [1280044] = {172, -1220.38, 69.854, 194.365}, -- Scorpion Keep
+ [1280045] = {173, -635, 280, -1797}, -- Hidden Gorge
+ [1280046] = {173, 447, 259.1, -2158}, -- Sea of Spires
+ [1280047] = {173, -710, 280.4, -2212}, -- Cutters Pass
+ [1280048] = {174, 1797, 248, 1856}, -- Red Labyrinth
+ [1280049] = {174, 1185, 279.8, 1407}, -- Burnt Lizard Creek
+ [1280050] = {174, 2416, 248.3, 1535}, -- Zanr'ak
+ [1280052] = {176, 80.056, 167.929, -1267.94}, -- Nanawa Mines https://www.youtube.com/watch?v=9H-NveJx9EI
+ [1280054] = {178, -620.374, 110.429, -113.903}, -- Copperbell Mines
+ -- Black Shroud
+ [1280061] = {206, -120, 16, -1332}, -- CAP
+ [1280062] = {150, 288, 4, -543.928}, -- CAP
+ [1280063] = {151, 1702, 20, -862}, -- CAP
+ [1280064] = {152, -1052, 20, -1760}, -- CAP
+ [1280065] = {153, -1566.035, -11.89, -550.51}, -- CAP
+ [1280066] = {154, 734, -12, 1126}, -- CAP
+ [1280067] = {150, -94.07, 4, -543.16}, -- Humblehearth
+ [1280068] = {150, -285, -21.8, -46}, -- Sorrel Haven
+ [1280069] = {150, 636, 16.2, -324}, -- Five Hangs
+ [1280070] = {151, 1529.83, 26.991, -1140.15}, -- Verdant Drop
+ [1280071] = {151, 1296, 47.2, -1534}, -- Lynxpelt Patch
+ [1280072] = {151, 2297.02, 31.546, -697.828}, -- Larkscall http://www.gwcdn.com/albums/images/4f7ce3a389118b43470005b1.jpg
+ [1280073] = {152, -883.769, 34.688, -2187.45}, -- Treespeak
+ [1280074] = {152, -1567, 16.1, -2593}, -- Aldersprings
+ [1280075] = {152, -800.277, 32, -2785.4}, -- Lasthold
+ [1280076] = {153, -1908, 0.3, -1042}, -- Lichenweed
+ [1280077] = {153, -2158, -46.1, -166}, -- Murmur Rills
+ [1280078] = {153, -1333, -14.2, 324}, -- Turning Leaf
+ [1280079] = {154, 991, -11.8, 600}, -- Silent Arbor
+ [1280080] = {154, 1126, -0.1, 1440}, -- Longroot
+ [1280081] = {154, 189, 0.1, 1337}, -- Snakemolt
+ [1280082] = {157, -687.916, -15.308, -2063.94}, -- Mun-Tuy Cellars https://www.youtube.com/watch?v=ty6f9Gy0uws
+ [1280083] = {158, 314.801, -36.2, -167.843}, -- Tam-Tara Deepcroft https://www.youtube.com/watch?v=eLJPTUG-dE0
+ -- Coerthas
+ [1280092] = {143, 216, 302.1, -258}, -- Camp Dragonhead
+ [1280093] = {144, 1122.21, 270.004, -1149.29}, -- Camp Crooked Fork https://www.youtube.com/watch?v=Q7-0r6ELCAU
+ [1280094] = {145, 1500.78, 206.036, 767.546}, -- Camp Glory
+ [1280095] = {147, -159.828, 222.037, 1154.81}, -- Camp Ever Lakes https://youtu.be/3wKNidix0Ls?t=274
+ [1280096] = {148, -1760.36, 270.059, -194.713}, -- Camp Riversmeet https://www.youtube.com/watch?v=gt7Tc9gbTpk
+ [1280097] = {143, -517, 207.9, 543}, -- Boulder Downs
+ [1280098] = {143, 190, 367.4, -662}, -- Prominence Point
+ [1280099] = {143, 960, 287.4, -22}, -- Feathergorge
+ [1280100] = {144, 1737, 176.5, -1250}, -- Maiden Glen
+ [1280101] = {144, 1390, 222.6, -736}, -- Hushed Boughs
+ [1280102] = {144, 1788, 164.8, -829}, -- Scarwing Fall
+ [1280103] = {145, 1383, 231.8, 422}, -- Weeping Vale
+ [1280104] = {145, 2160, 142.7, 622}, -- Clearwater
+ [1280105] = {147, -1, 144.1, 1373}, -- Teriggans Stand
+ [1280106] = {147, -64, 185.1, 1924}, -- Shepherd Peak
+ [1280107] = {147, -908, 191.7, 2162}, -- Fellwood
+ [1280108] = {148, -1734.82, 285.069, -839.63}, -- Wyrmkings Perch
+ [1280109] = {148, -2366.07, 336.041, -1054.75}, -- The Lance
+ [1280110] = {148, -2821, 256.1, -290}, -- Twinpools
+ -- Mor Dhona
+ [1280121] = {190, 487.445, 18.531, 672.244}, -- Camp Brittlebark https://youtu.be/mkbYeaUqcr4 - I can barely make the ground out
+ [1280122] = {190, -215.76, 18.54, -668.703}, -- Camp Revenant's Toll
+ [1280123] = {190, -458, -40.9, -318}, -- Fogfens
+ [1280124] = {190, 580, 58.2, 206}, -- Singing Shards
+ [1280125] = {190, -365.724, -18.591, -25.448} -- Jagged Crest Cave
+}
diff --git a/data/scripts/commands/gm/despawn.lua b/data/scripts/commands/gm/despawn.lua
new file mode 100644
index 00000000..0d502ffd
--- /dev/null
+++ b/data/scripts/commands/gm/despawn.lua
@@ -0,0 +1,16 @@
+require("global");
+
+properties = {
+ permissions = 0,
+ parameters = "d",
+ description = "Spawns a actor",
+}
+
+function onTrigger(player, argc, actorName)
+
+ if (actorName ~= nil) then
+ zone = player:GetZone();
+ actor = zone:DespawnActor(actorName);
+ end
+
+end;
\ No newline at end of file
diff --git a/data/scripts/commands/gm/testmapobj.lua b/data/scripts/commands/gm/testmapobj.lua
new file mode 100644
index 00000000..739eab53
--- /dev/null
+++ b/data/scripts/commands/gm/testmapobj.lua
@@ -0,0 +1,53 @@
+require("global");
+
+properties = {
+ permissions = 0,
+ parameters = "ssss",
+ description = ""
+}
+
+function onTrigger(player, argc, actorClassId, regionId, layoutId, maxLayoutId)
+ layoutId = tonumber(layoutId);
+
+ if (maxLayoutId ~= nil) then
+ maxLayoutId = tonumber(maxLayoutId);
+ else
+ maxLayoutId = layoutId;
+ end
+
+ while (layoutId <= maxLayoutId) do
+ if (actorClassId == nil) then
+ player:SendMessage(0x20, "", "No actor class id provided.");
+ return;
+ end
+
+ local pos = player:GetPos();
+ local x = pos[0];
+ local y = pos[1];
+ local z = pos[2];
+ local zone = pos[4];
+
+ actorClassId = tonumber(actorClassId);
+
+ if (actorClassId ~= nil) then
+ zone = player:GetZone();
+ actor = zone:SpawnActor(actorClassId, "mapobj", pos[0], pos[1], pos[2], tonumber(regionId), tonumber(layoutId));
+ wait(0.8);
+ actor:PlayMapObjAnimation(player, "open");
+ zone:DespawnActor("mapobj");
+ wait(0.5);
+ player:SendMessage(0x20, "", "Layout ID: "..layoutId);
+ end
+ layoutId=layoutId+1;
+ end
+end;
+--!testmapobj 5900001 421 2810 2820
+
+--dun4 (0x19e) - Copperbell Mines
+--dun1 - Mun Tuy
+--dun2 - Tam Tara
+
+--Ferry (Thanalan, Z=-130): 5145, 252
+--Ferry (La Noscea, Z=+130): 5144, 201
+--Ferry (5142, Z=-130) Doors: 323, 326,
+--Ferry (5143, Z=+130) Doors: 323, 326,
\ No newline at end of file