Merge branch 'actor_instancing' into develop

# Conflicts:
#	sql/gamedata_actor_class.sql
This commit is contained in:
Filip Maj 2016-06-12 18:52:30 -04:00
commit a4ea5f024b
16 changed files with 9132 additions and 8135 deletions

View file

@ -18,7 +18,7 @@ using System.Text;
namespace FFXIVClassic_Map_Server.Actors
{
class Actor
{
{
public uint actorId;
public string actorName;
@ -38,6 +38,7 @@ namespace FFXIVClassic_Map_Server.Actors
public bool spawnedFirstTime = false;
public string classPath;
public string className;
public List<LuaParam> classParams;
@ -307,6 +308,53 @@ namespace FFXIVClassic_Map_Server.Actors
SubPacket changeSpeedPacket = SetActorSpeedPacket.buildPacket(actorId, actorId, moveSpeeds[0], moveSpeeds[1], moveSpeeds[2]);
zone.broadcastPacketAroundActor(this, changeSpeedPacket);
}
public void generateActorName(int actorNumber)
{
//Format Class Name
string className = this.className.Replace("Populace", "Ppl")
.Replace("Monster", "Mon")
.Replace("Crowd", "Crd")
.Replace("MapObj", "Map")
.Replace("Object", "Obj")
.Replace("Retainer", "Rtn")
.Replace("Standard", "Std");
className = Char.ToLowerInvariant(className[0]) + className.Substring(1);
//Format Zone Name
string zoneName = zone.zoneName.Replace("Field", "Fld")
.Replace("Dungeon", "Dgn")
.Replace("Town", "Twn")
.Replace("Battle", "Btl")
.Replace("Test", "Tes")
.Replace("Event", "Evt")
.Replace("Ship", "Shp")
.Replace("Office", "Ofc");
if (zone is PrivateArea)
{
//Check if "normal"
zoneName = zoneName.Remove(zoneName.Length - 1, 1) + "P";
}
zoneName = Char.ToLowerInvariant(zoneName[0]) + zoneName.Substring(1);
try
{
className = className.Substring(0, 20 - zoneName.Length);
}
catch (ArgumentOutOfRangeException e)
{}
//Convert actor number to base 63
string classNumber = Utils.ToStringBase63(actorNumber);
//Get stuff after @
uint zoneId = zone.actorId;
uint privLevel = 0;
if (zone is PrivateArea)
privLevel = ((PrivateArea)zone).getPrivateAreaLevel();
actorName = String.Format("{0}_{1}_{2}@{3:X3}{4:X2}", className, zoneName, classNumber, zoneId, privLevel);
}
}
}

View file

@ -1,5 +1,8 @@
using FFXIVClassic_Lobby_Server.common;
using FFXIVClassic_Lobby_Server;
using FFXIVClassic_Lobby_Server.common;
using FFXIVClassic_Lobby_Server.packets;
using FFXIVClassic_Map_Server.actors.area;
using FFXIVClassic_Map_Server.actors.chara.npc;
using FFXIVClassic_Map_Server.dataobjects;
using FFXIVClassic_Map_Server.dataobjects.chara;
using FFXIVClassic_Map_Server.lua;
@ -21,15 +24,16 @@ namespace FFXIVClassic_Map_Server.Actors
public ushort weatherNormal, weatherCommon, weatherRare;
public ushort bgmDay, bgmNight, bgmBattle;
private string classPath;
protected string classPath;
public int boundingGridSize = 50;
public int minX = -1000, minY = -1000, maxX = 1000, maxY = 1000;
private int numXBlocks, numYBlocks;
private int halfWidth, halfHeight;
protected int numXBlocks, numYBlocks;
protected int halfWidth, halfHeight;
private Dictionary<uint, Actor> mActorList = new Dictionary<uint,Actor>();
private List<Actor>[,] mActorBlock;
protected List<SpawnLocation> mSpawnLocations = new List<SpawnLocation>();
protected Dictionary<uint, Actor> mActorList = new Dictionary<uint, Actor>();
protected List<Actor>[,] mActorBlock;
Script areaScript;
@ -335,5 +339,18 @@ namespace FFXIVClassic_Map_Server.Actors
}
}
public void spawnActor(SpawnLocation location)
{
ActorClass actorClass = Server.GetWorldManager().GetActorClass(location.classId);
if (actorClass == null)
return;
Npc npc = new Npc(mActorList.Count + 1, actorClass.actorClassId, location.uniqueId, actorId, location.x, location.y, location.z, location.rot, location.state, location.animId, actorClass.displayNameId, null, actorClass.classPath);
npc.loadEventConditions(actorClass.eventConditions);
addActorToZone(npc);
}
}
}

View file

@ -14,12 +14,14 @@ namespace FFXIVClassic_Map_Server.actors.area
{
private Zone parentZone;
private string privateAreaName;
private uint privateAreaLevel;
public PrivateArea(Zone parent, uint id, string className, string privateAreaName,ushort bgmDay, ushort bgmNight, ushort bgmBattle)
public PrivateArea(Zone parent, uint id, string className, string privateAreaName, uint privateAreaLevel, ushort bgmDay, ushort bgmNight, ushort bgmBattle)
: base(id, parent.zoneName, parent.regionId, className, bgmDay, bgmNight, bgmBattle, parent.isIsolated, parent.isInn, parent.canRideChocobo, parent.canStealth, true)
{
this.parentZone = parent;
this.privateAreaName = privateAreaName;
this.privateAreaLevel = privateAreaLevel;
}
public string getPrivateAreaName()
@ -27,6 +29,11 @@ namespace FFXIVClassic_Map_Server.actors.area
return privateAreaName;
}
public uint getPrivateAreaLevel()
{
return privateAreaLevel;
}
public Zone getParentZone()
{
return parentZone;
@ -45,6 +52,17 @@ namespace FFXIVClassic_Map_Server.actors.area
ActorInstantiatePacket.buildPacket(actorId, playerActorId, actorName, className, lParams).debugPrintSubPacket();
return ActorInstantiatePacket.buildPacket(actorId, playerActorId, actorName, className, lParams);
}
public void addSpawnLocation(SpawnLocation spawn)
{
mSpawnLocations.Add(spawn);
}
public void spawnAllActors()
{
foreach (SpawnLocation spawn in mSpawnLocations)
spawnActor(spawn);
}
}
}

View file

@ -0,0 +1,38 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace FFXIVClassic_Map_Server.actors.area
{
class SpawnLocation
{
public uint classId;
public string uniqueId;
public uint zoneId;
public string privAreaName;
public uint privAreaLevel;
public float x;
public float y;
public float z;
public float rot;
public ushort state;
public uint animId;
public SpawnLocation(uint classId, string uniqueId, uint zoneId, string privAreaName, uint privAreaLevel, float x, float y, float z, float rot, ushort state, uint animId)
{
this.classId = classId;
this.uniqueId = uniqueId;
this.zoneId = zoneId;
this.privAreaName = privAreaName;
this.privAreaLevel = privAreaLevel;
this.x = x;
this.y = y;
this.z = z;
this.rot = rot;
this.state = state;
this.animId = animId;
}
}
}

View file

@ -1,4 +1,7 @@
using FFXIVClassic_Lobby_Server.packets;
using FFXIVClassic_Lobby_Server;
using FFXIVClassic_Lobby_Server.common;
using FFXIVClassic_Lobby_Server.packets;
using FFXIVClassic_Map_Server.actors.chara.npc;
using FFXIVClassic_Map_Server.Actors;
using FFXIVClassic_Map_Server.lua;
using FFXIVClassic_Map_Server.packets.send.actor;
@ -11,7 +14,7 @@ using System.Threading.Tasks;
namespace FFXIVClassic_Map_Server.actors.area
{
class Zone : Area
{
{
Dictionary<string, Dictionary<uint, PrivateArea>> privateAreas = new Dictionary<string, Dictionary<uint, PrivateArea>>();
public Zone(uint id, string zoneName, ushort regionId, string className, ushort bgmDay, ushort bgmNight, ushort bgmBattle, bool isIsolated, bool isInn, bool canRideChocobo, bool canStealth, bool isInstanceRaid)
@ -54,5 +57,40 @@ namespace FFXIVClassic_Map_Server.actors.area
return ActorInstantiatePacket.buildPacket(actorId, playerActorId, actorName, className, lParams);
}
public void addSpawnLocation(SpawnLocation spawn)
{
//Is it in a private area?
if (!spawn.privAreaName.Equals(""))
{
if (privateAreas.ContainsKey(spawn.privAreaName))
{
Dictionary<uint, PrivateArea> levels = privateAreas[spawn.privAreaName];
if (levels.ContainsKey(spawn.privAreaLevel))
levels[spawn.privAreaLevel].addSpawnLocation(spawn);
else
Log.error(String.Format("Tried to add a spawn location to non-existing private area level \"{0}\" in area {1} in zone {2}", spawn.privAreaName, spawn.privAreaLevel, zoneName));
}
else
Log.error(String.Format("Tried to add a spawn location to non-existing private area \"{0}\" in zone {1}", spawn.privAreaName, zoneName));
}
else
mSpawnLocations.Add(spawn);
}
public void spawnAllActors(bool doPrivAreas)
{
foreach (SpawnLocation spawn in mSpawnLocations)
spawnActor(spawn);
if (doPrivAreas)
{
foreach (Dictionary<uint, PrivateArea> areas in privateAreas.Values)
{
foreach (PrivateArea pa in areas.Values)
pa.spawnAllActors();
}
}
}
}
}

View file

@ -0,0 +1,24 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace FFXIVClassic_Map_Server.actors.chara.npc
{
class ActorClass
{
public readonly uint actorClassId;
public readonly string classPath;
public readonly uint displayNameId;
public readonly string eventConditions;
public ActorClass(uint id, string classPath, uint nameId, string eventConditions)
{
this.actorClassId = id;
this.classPath = classPath;
this.displayNameId = nameId;
this.eventConditions = eventConditions;
}
}
}

View file

@ -5,12 +5,15 @@ using FFXIVClassic_Map_Server.actors;
using FFXIVClassic_Map_Server.Actors.Chara;
using FFXIVClassic_Map_Server.dataobjects;
using FFXIVClassic_Map_Server.lua;
using FFXIVClassic_Map_Server.packets.receive.events;
using FFXIVClassic_Map_Server.packets.send.actor;
using FFXIVClassic_Map_Server.utils;
using MoonSharp.Interpreter;
using MySql.Data.MySqlClient;
using Newtonsoft.Json;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
@ -20,27 +23,33 @@ namespace FFXIVClassic_Map_Server.Actors
class Npc : Character
{
private uint actorClassId;
private string uniqueIdentifier;
public NpcWork npcWork = new NpcWork();
public Npc(uint id, string actorName, uint zoneId, float posX, float posY, float posZ, float rot, ushort actorState, uint animationId, uint displayNameId, string customDisplayName, string className)
: base(id)
public Npc(int actorNumber, uint classId, string uniqueId, uint zoneId, float posX, float posY, float posZ, float rot, ushort actorState, uint animationId, uint displayNameId, string customDisplayName, string classPath)
: base((4 << 28 | zoneId << 19 | (uint)actorNumber))
{
this.actorName = actorName;
this.actorClassId = id;
this.positionX = posX;
this.positionY = posY;
this.positionZ = posZ;
this.rotation = rot;
this.animationId = animationId;
this.className = className;
this.displayNameId = displayNameId;
this.customDisplayName = customDisplayName;
this.zoneId = zoneId;
this.uniqueIdentifier = uniqueId;
loadNpcTemplate(id);
this.zoneId = zoneId;
this.zone = Server.GetWorldManager().GetZone(zoneId);
this.actorClassId = classId;
loadNpcAppearance(classId);
this.classPath = classPath;
className = classPath.Substring(classPath.LastIndexOf("/")+1);
charaWork.battleSave.potencial = 1.0f;
@ -61,6 +70,11 @@ namespace FFXIVClassic_Map_Server.Actors
charaWork.property[3] = 1;
charaWork.property[4] = 1;
npcWork.pushCommand = 0x271D;
npcWork.pushCommandPriority = 1;
generateActorName((int)actorNumber);
}
public SubPacket createAddActorPacket(uint playerActorId)
@ -68,19 +82,34 @@ namespace FFXIVClassic_Map_Server.Actors
return AddActorPacket.buildPacket(actorId, playerActorId, 8);
}
// actorClassId, [], [], numBattleCommon, [battleCommon], numEventCommon, [eventCommon], args for either initForBattle/initForEvent
public override SubPacket createScriptBindPacket(uint playerActorId)
{
List<LuaParam> lParams;
Player player = Server.GetWorldManager().GetPCInWorld(playerActorId);
lParams = LuaEngine.doActorInstantiate(player, this);
lParams = DoActorInit(player);
if (lParams == null)
{
className = "PopulaceStandard";
lParams = LuaUtils.createLuaParamList("/Chara/Npc/Populace/PopulaceStandard", false, false, false, false, false, 0xF47F6, false, false, 0, 1, "TEST");
string classPathFake = "/Chara/Npc/Populace/PopulaceStandard";
string classNameFake = "PopulaceStandard";
lParams = LuaUtils.createLuaParamList(classPathFake, false, false, false, false, false, 0xF47F6, false, false, 0, 0);
ActorInstantiatePacket.buildPacket(actorId, playerActorId, actorName, classNameFake, lParams).debugPrintSubPacket();
return ActorInstantiatePacket.buildPacket(actorId, playerActorId, actorName, classNameFake, lParams);
}
else
{
lParams.Insert(0, new LuaParam(2, classPath));
lParams.Insert(1, new LuaParam(4, 4));
lParams.Insert(2, new LuaParam(4, 4));
lParams.Insert(3, new LuaParam(4, 4));
lParams.Insert(4, new LuaParam(4, 4));
lParams.Insert(5, new LuaParam(4, 4));
lParams.Insert(6, new LuaParam(0, (int)actorClassId));
}
ActorInstantiatePacket.buildPacket(actorId, playerActorId, actorName, className, lParams).debugPrintSubPacket();
return ActorInstantiatePacket.buildPacket(actorId, playerActorId, actorName, className, lParams);
}
@ -147,6 +176,8 @@ namespace FFXIVClassic_Map_Server.Actors
}
propPacketUtil.addProperty("npcWork.hateType");
propPacketUtil.addProperty("npcWork.pushCommand");
propPacketUtil.addProperty("npcWork.pushCommandPriority");
return BasePacket.createPacket(propPacketUtil.done(), true, false);
}
@ -156,7 +187,7 @@ namespace FFXIVClassic_Map_Server.Actors
return actorClassId;
}
public void loadNpcTemplate(uint id)
public void loadNpcAppearance(uint id)
{
using (MySqlConnection conn = new MySqlConnection(String.Format("Server={0}; Port={1}; Database={2}; UID={3}; Password={4}", ConfigConstants.DATABASE_HOST, ConfigConstants.DATABASE_PORT, ConfigConstants.DATABASE_NAME, ConfigConstants.DATABASE_USERNAME, ConfigConstants.DATABASE_PASSWORD)))
{
@ -171,7 +202,7 @@ namespace FFXIVClassic_Map_Server.Actors
hairStyle,
hairHighlightColor,
hairVariation,
faceType,
faceType,
characteristics,
characteristicsColor,
faceEyebrows,
@ -225,17 +256,25 @@ namespace FFXIVClassic_Map_Server.Actors
appearanceIds[Character.HIGHLIGHT_HAIR] = (uint)(reader.GetUInt32(3) | reader.GetUInt32(2) << 10); //5- Hair Highlight, 4 - Hair Style
appearanceIds[Character.VOICE] = reader.GetUInt32(17);
appearanceIds[Character.MAINHAND] = reader.GetUInt32(19);
//appearanceIds[Character.WEAPON2] = reader.GetUInt32(22);
appearanceIds[Character.OFFHAND] = reader.GetUInt32(20);
appearanceIds[Character.SPMAINHAND] = reader.GetUInt32(21);
appearanceIds[Character.SPOFFHAND] = reader.GetUInt32(22);
appearanceIds[Character.THROWING] = reader.GetUInt32(23);
appearanceIds[Character.PACK] = reader.GetUInt32(24);
appearanceIds[Character.POUCH] = reader.GetUInt32(25);
appearanceIds[Character.HEADGEAR] = reader.GetUInt32(26);
appearanceIds[Character.BODYGEAR] = reader.GetUInt32(27);
appearanceIds[Character.LEGSGEAR] = reader.GetUInt32(28);
appearanceIds[Character.HANDSGEAR] = reader.GetUInt32(29);
appearanceIds[Character.FEETGEAR] = reader.GetUInt32(30);
appearanceIds[Character.WAISTGEAR] = reader.GetUInt32(31);
appearanceIds[Character.R_EAR] = reader.GetUInt32(32);
appearanceIds[Character.L_EAR] = reader.GetUInt32(33);
appearanceIds[Character.R_RINGFINGER] = reader.GetUInt32(36);
appearanceIds[Character.L_RINGFINGER] = reader.GetUInt32(37);
appearanceIds[Character.NECKGEAR] = reader.GetUInt32(32);
appearanceIds[Character.R_EAR] = reader.GetUInt32(33);
appearanceIds[Character.L_EAR] = reader.GetUInt32(34);
appearanceIds[Character.R_INDEXFINGER] = reader.GetUInt32(35);
appearanceIds[Character.L_INDEXFINGER] = reader.GetUInt32(36);
appearanceIds[Character.R_RINGFINGER] = reader.GetUInt32(37);
appearanceIds[Character.L_RINGFINGER] = reader.GetUInt32(38);
}
}
@ -255,5 +294,127 @@ namespace FFXIVClassic_Map_Server.Actors
EventList conditions = JsonConvert.DeserializeObject<EventList>(eventConditions);
this.eventConditions = conditions;
}
public List<LuaParam> DoActorInit(Player player)
{
Script parent = null, child = null;
if (File.Exists("./scripts/base/" + classPath + ".lua"))
parent = LuaEngine.loadScript("./scripts/base/" + classPath + ".lua");
if (File.Exists(String.Format("./scripts/unique/{0}/{1}/{2}.lua", zone.zoneName, className, uniqueIdentifier)))
child = LuaEngine.loadScript(String.Format("./scripts/unique/{0}/{1}/{2}.lua", zone.zoneName, className, uniqueIdentifier));
if (parent == null && child == null)
{
LuaEngine.SendError(player, String.Format("ERROR: Could not find script for actor {0}.", getName()));
return null;
}
DynValue result;
if (child != null && child.Globals["init"] != null)
result = child.Call(child.Globals["init"], this);
else if (parent.Globals["init"] != null)
result = parent.Call(parent.Globals["init"], this);
else
return null;
List<LuaParam> lparams = LuaUtils.createLuaParamList(result);
return lparams;
}
public void DoEventStart(Player player, EventStartPacket eventStart)
{
Script parent = null, child = null;
if (File.Exists("./scripts/base/" + classPath + ".lua"))
parent = LuaEngine.loadScript("./scripts/base/" + classPath + ".lua");
if (File.Exists(String.Format("./scripts/unique/{0}/{1}/{2}.lua", zone.zoneName, className, uniqueIdentifier)))
child = LuaEngine.loadScript(String.Format("./scripts/unique/{0}/{1}/{2}.lua", zone.zoneName, className, uniqueIdentifier));
if (parent == null)
{
LuaEngine.SendError(player, String.Format("ERROR: Could not find script for actor {0}.", getName()));
return;
}
//Have to do this to combine LuaParams
List<Object> objects = new List<Object>();
objects.Add(player);
objects.Add(this);
objects.Add(eventStart.triggerName);
if (eventStart.luaParams != null)
objects.AddRange(LuaUtils.createLuaParamObjectList(eventStart.luaParams));
//Run Script
DynValue result;
if (child != null && !child.Globals.Get("onEventStarted").IsNil())
result = child.Call(child.Globals["onEventStarted"], objects.ToArray());
else if (!parent.Globals.Get("onEventStarted").IsNil())
result = parent.Call(parent.Globals["onEventStarted"], objects.ToArray());
else
return;
}
public void DoEventUpdate(Player player, EventUpdatePacket eventUpdate)
{
Script parent = null, child = null;
if (File.Exists("./scripts/base/" + classPath + ".lua"))
parent = LuaEngine.loadScript("./scripts/base/" + classPath + ".lua");
if (File.Exists(String.Format("./scripts/unique/{0}/{1}/{2}.lua", zone.zoneName, className, uniqueIdentifier)))
child = LuaEngine.loadScript(String.Format("./scripts/unique/{0}/{1}/{2}.lua", zone.zoneName, className, uniqueIdentifier));
if (parent == null)
{
LuaEngine.SendError(player, String.Format("ERROR: Could not find script for actor {0}.", getName()));
return;
}
//Have to do this to combine LuaParams
List<Object> objects = new List<Object>();
objects.Add(player);
objects.Add(this);
objects.Add(eventUpdate.val2);
objects.AddRange(LuaUtils.createLuaParamObjectList(eventUpdate.luaParams));
//Run Script
DynValue result;
if (child != null && !child.Globals.Get("onEventUpdate").IsNil())
result = child.Call(child.Globals["onEventUpdate"], objects.ToArray());
else if (!parent.Globals.Get("onEventUpdate").IsNil())
result = parent.Call(parent.Globals["onEventUpdate"], objects.ToArray());
else
return;
}
internal void DoOnActorSpawn(Player player)
{
Script parent = null, child = null;
if (File.Exists("./scripts/base/" + classPath + ".lua"))
parent = LuaEngine.loadScript("./scripts/base/" + classPath + ".lua");
if (File.Exists(String.Format("./scripts/unique/{0}/{1}/{2}.lua", zone.zoneName, className, uniqueIdentifier)))
child = LuaEngine.loadScript(String.Format("./scripts/unique/{0}/{1}/{2}.lua", zone.zoneName, className, uniqueIdentifier));
if (parent == null)
{
LuaEngine.SendError(player, String.Format("ERROR: Could not find script for actor {0}.", getName()));
return;
}
//Run Script
if (child != null && !child.Globals.Get("onSpawn").IsNil())
child.Call(child.Globals["onSpawn"], player, this);
else if (!parent.Globals.Get("onSpawn").IsNil())
parent.Call(parent.Globals["onSpawn"], player, this);
else
return;
}
}
}