1
Fork 0
mirror of https://bitbucket.org/Ioncannon/project-meteor-server.git synced 2025-04-24 21:57:45 +00:00
project-meteor-server/FFXIVClassic Map Server/actors/Actor.cs
yogurt c5ce2ec771 Combat additions
Added formulas for base EXP gain and chain experience
Added basic scripts for most player abilities and effects
Added stat gains for some abilities
Changed status flags
Fixed bug with player death
Fixed bug where auto attacks didnt work when not locked on
Added traits
2018-04-18 16:06:41 -05:00

733 lines
27 KiB
C#

using FFXIVClassic_Map_Server.actors;
using FFXIVClassic_Map_Server.lua;
using FFXIVClassic_Map_Server.packets.send.actor;
using FFXIVClassic_Map_Server.packets.send.actor.events;
using FFXIVClassic.Common;
using System;
using System.Collections.Generic;
using FFXIVClassic_Map_Server.actors.area;
using System.Reflection;
using System.ComponentModel;
using FFXIVClassic_Map_Server.packets.send.actor.battle;
using FFXIVClassic_Map_Server.packets.send;
namespace FFXIVClassic_Map_Server.Actors
{
[Flags]
enum ActorUpdateFlags
{
None = 0x00,
Position = 0x01,
HpTpMp = 0x02,
State = 0x04,
Combat = 0x07,
Name = 0x08,
Appearance = 0x10,
Speed = 0x20,
Work = 0x40,
Stats = 0x80,
Status = 0x100,
StatusTime = 0x200,
AllNpc = 0x6F,
AllPlayer = 0x9F
}
class Actor
{
public static uint INVALID_ACTORID = 0xC0000000;
public uint actorId;
public string actorName;
public uint displayNameId = 0xFFFFFFFF;
public string customDisplayName;
public ushort currentMainState = SetActorStatePacket.MAIN_STATE_PASSIVE;
public ushort currentSubState = SetActorStatePacket.SUB_STATE_NONE;
public float positionX, positionY, positionZ, rotation;
public float oldPositionX, oldPositionY, oldPositionZ, oldRotation;
public ushort moveState, oldMoveState;
public float[] moveSpeeds = new float[4];
public uint zoneId, zoneId2;
public string privateArea;
public uint privateAreaType;
public Area zone = null;
public Area zone2 = null;
public bool isZoning = false;
public bool spawnedFirstTime = false;
public string classPath;
public string className;
public List<LuaParam> classParams;
public List<Vector3> positionUpdates;
protected DateTime lastUpdateScript;
protected DateTime lastUpdate;
public Actor target;
public bool isAtSpawn = true;
public ActorUpdateFlags updateFlags;
public EventList eventConditions;
public Actor(uint actorId)
{
this.actorId = actorId;
}
public Actor(uint actorId, string actorName, string className, List<LuaParam> classParams)
{
this.actorId = actorId;
this.actorName = actorName;
this.className = className;
this.classParams = classParams;
this.moveSpeeds[0] = SetActorSpeedPacket.DEFAULT_STOP;
this.moveSpeeds[1] = SetActorSpeedPacket.DEFAULT_WALK;
this.moveSpeeds[2] = SetActorSpeedPacket.DEFAULT_RUN;
this.moveSpeeds[3] = SetActorSpeedPacket.DEFAULT_ACTIVE;
}
public void SetPushCircleRange(string triggerName, float size)
{
if (eventConditions == null || eventConditions.pushWithCircleEventConditions == null)
return;
foreach (EventList.PushCircleEventCondition condition in eventConditions.pushWithCircleEventConditions)
{
if (condition.conditionName.Equals(triggerName))
{
condition.radius = size;
break;
}
}
}
public virtual void ResetMoveSpeeds()
{
this.moveSpeeds[0] = SetActorSpeedPacket.DEFAULT_STOP;
this.moveSpeeds[1] = SetActorSpeedPacket.DEFAULT_WALK;
this.moveSpeeds[2] = SetActorSpeedPacket.DEFAULT_RUN;
this.moveSpeeds[3] = SetActorSpeedPacket.DEFAULT_ACTIVE;
this.moveState = this.oldMoveState;
this.updateFlags |= ActorUpdateFlags.Speed;
}
public SubPacket CreateAddActorPacket(byte val)
{
return AddActorPacket.BuildPacket(actorId, val);
}
public SubPacket CreateNamePacket()
{
return SetActorNamePacket.BuildPacket(actorId, displayNameId, displayNameId == 0xFFFFFFFF | displayNameId == 0x0 ? customDisplayName : "");
}
public SubPacket CreateSpeedPacket()
{
return SetActorSpeedPacket.BuildPacket(actorId, moveSpeeds[0], moveSpeeds[1], moveSpeeds[2], moveSpeeds[3]);
}
public SubPacket CreateSpawnPositonPacket(ushort spawnType)
{
return CreateSpawnPositonPacket(null, spawnType);
}
public SubPacket CreateSpawnPositonPacket(Player player, ushort spawnType)
{
//TODO: FIX THIS IF
uint playerActorId = player == null ? 0 : player.actorId; //Get Rid
SubPacket spawnPacket;
if (!spawnedFirstTime && playerActorId == actorId)
spawnPacket = SetActorPositionPacket.BuildPacket(actorId, 0, positionX, positionY, positionZ, rotation, 0x1, false);
else if (playerActorId == actorId)
spawnPacket = SetActorPositionPacket.BuildPacket(actorId, 0xFFFFFFFF, positionX, positionY, positionZ, rotation, spawnType, true);
else
{
if (this is Player)
spawnPacket = SetActorPositionPacket.BuildPacket(actorId, 0, positionX, positionY, positionZ, rotation, spawnType, false);
else
spawnPacket = SetActorPositionPacket.BuildPacket(actorId, actorId, positionX, positionY, positionZ, rotation, spawnType, false);
}
//return SetActorPositionPacket.BuildPacket(actorId, -211.895477f, 190.000000f, 29.651011f, 2.674819f, SetActorPositionPacket.SPAWNTYPE_PLAYERWAKE);
spawnedFirstTime = true;
return spawnPacket;
}
public SubPacket CreateSpawnTeleportPacket(ushort spawnType)
{
SubPacket spawnPacket;
spawnPacket = SetActorPositionPacket.BuildPacket(actorId, 0xFFFFFFFF, positionX, positionY, positionZ, rotation, spawnType, false);
//return SetActorPositionPacket.BuildPacket(actorId, -211.895477f, 190.000000f, 29.651011f, 2.674819f, SetActorPositionPacket.SPAWNTYPE_PLAYERWAKE);
//spawnPacket.DebugPrintSubPacket();
return spawnPacket;
}
public SubPacket CreatePositionUpdatePacket()
{
return MoveActorToPositionPacket.BuildPacket(actorId, positionX, positionY, positionZ, rotation, moveState);
}
public SubPacket CreateStatePacket()
{
return SetActorStatePacket.BuildPacket(actorId, currentMainState, currentSubState);
}
public List<SubPacket> GetEventConditionPackets()
{
List<SubPacket> subpackets = new List<SubPacket>();
//Return empty list
if (eventConditions == null)
return subpackets;
if (eventConditions.talkEventConditions != null)
{
foreach (EventList.TalkEventCondition condition in eventConditions.talkEventConditions)
subpackets.Add(SetTalkEventCondition.BuildPacket(actorId, condition));
}
if (eventConditions.noticeEventConditions != null)
{
foreach (EventList.NoticeEventCondition condition in eventConditions.noticeEventConditions)
subpackets.Add(SetNoticeEventCondition.BuildPacket(actorId, condition));
}
if (eventConditions.emoteEventConditions != null)
{
foreach (EventList.EmoteEventCondition condition in eventConditions.emoteEventConditions)
subpackets.Add(SetEmoteEventCondition.BuildPacket(actorId, condition));
}
if (eventConditions.pushWithCircleEventConditions != null)
{
foreach (EventList.PushCircleEventCondition condition in eventConditions.pushWithCircleEventConditions)
subpackets.Add(SetPushEventConditionWithCircle.BuildPacket(actorId, condition));
}
if (eventConditions.pushWithFanEventConditions != null)
{
foreach (EventList.PushFanEventCondition condition in eventConditions.pushWithFanEventConditions)
subpackets.Add(SetPushEventConditionWithFan.BuildPacket(actorId, condition));
}
if (eventConditions.pushWithBoxEventConditions != null)
{
foreach (EventList.PushBoxEventCondition condition in eventConditions.pushWithBoxEventConditions)
subpackets.Add(SetPushEventConditionWithTriggerBox.BuildPacket(actorId, condition));
}
return subpackets;
}
public List<SubPacket> GetSetEventStatusPackets()
{
List<SubPacket> subpackets = new List<SubPacket>();
//Return empty list
if (eventConditions == null)
return subpackets;
if (eventConditions.talkEventConditions != null)
{
foreach (EventList.TalkEventCondition condition in eventConditions.talkEventConditions)
subpackets.Add(SetEventStatus.BuildPacket(actorId, true, 1, condition.conditionName));
}
if (eventConditions.noticeEventConditions != null)
{
foreach (EventList.NoticeEventCondition condition in eventConditions.noticeEventConditions)
subpackets.Add(SetEventStatus.BuildPacket(actorId, true, 1, condition.conditionName));
}
if (eventConditions.emoteEventConditions != null)
{
foreach (EventList.EmoteEventCondition condition in eventConditions.emoteEventConditions)
subpackets.Add(SetEventStatus.BuildPacket(actorId, true, 3, condition.conditionName));
}
if (eventConditions.pushWithCircleEventConditions != null)
{
foreach (EventList.PushCircleEventCondition condition in eventConditions.pushWithCircleEventConditions)
subpackets.Add(SetEventStatus.BuildPacket(actorId, true, 2, condition.conditionName));
}
if (eventConditions.pushWithFanEventConditions != null)
{
foreach (EventList.PushFanEventCondition condition in eventConditions.pushWithFanEventConditions)
subpackets.Add(SetEventStatus.BuildPacket(actorId, true, 2, condition.conditionName));
}
if (eventConditions.pushWithBoxEventConditions != null)
{
foreach (EventList.PushBoxEventCondition condition in eventConditions.pushWithBoxEventConditions)
subpackets.Add(SetEventStatus.BuildPacket(actorId, true, 2, condition.conditionName));
}
return subpackets;
}
public SubPacket CreateIsZoneingPacket()
{
return SetActorIsZoningPacket.BuildPacket(actorId, false);
}
public virtual SubPacket CreateScriptBindPacket(Player player)
{
return ActorInstantiatePacket.BuildPacket(actorId, actorName, className, classParams);
}
public virtual SubPacket CreateScriptBindPacket()
{
return ActorInstantiatePacket.BuildPacket(actorId, actorName, className, classParams);
}
public virtual List<SubPacket> GetSpawnPackets(Player player, ushort spawnType)
{
List<SubPacket> subpackets = new List<SubPacket>();
subpackets.Add(CreateAddActorPacket(8));
subpackets.AddRange(GetEventConditionPackets());
subpackets.Add(CreateSpeedPacket());
subpackets.Add(CreateSpawnPositonPacket(player, spawnType));
subpackets.Add(CreateNamePacket());
subpackets.Add(CreateStatePacket());
subpackets.Add(CreateIsZoneingPacket());
subpackets.Add(CreateScriptBindPacket(player));
return subpackets;
}
public virtual List<SubPacket> GetSpawnPackets()
{
return GetSpawnPackets(0x1);
}
public virtual List<SubPacket> GetSpawnPackets(ushort spawnType)
{
List<SubPacket> subpackets = new List<SubPacket>();
subpackets.Add(CreateAddActorPacket(8));
subpackets.AddRange(GetEventConditionPackets());
subpackets.Add(CreateSpeedPacket());
subpackets.Add(CreateSpawnPositonPacket(null, spawnType));
subpackets.Add(CreateNamePacket());
subpackets.Add(CreateStatePacket());
subpackets.Add(CreateIsZoneingPacket());
subpackets.Add(CreateScriptBindPacket());
return subpackets;
}
public virtual List<SubPacket> GetInitPackets()
{
List<SubPacket> packets = new List<SubPacket>();
SetActorPropetyPacket initProperties = new SetActorPropetyPacket("/_init");
initProperties.AddByte(0xE14B0CA8, 1);
initProperties.AddByte(0x2138FD71, 1);
initProperties.AddByte(0xFBFBCFB1, 1);
initProperties.AddTarget();
packets.Add(initProperties.BuildPacket(actorId));
return packets;
}
public override bool Equals(Object obj)
{
Actor actorObj = obj as Actor;
if (actorObj == null)
return false;
else
return actorId == actorObj.actorId;
}
public string GetName()
{
return actorName;
}
public string GetClassName()
{
return className;
}
public ushort GetState()
{
return currentMainState;
}
public List<LuaParam> GetLuaParams()
{
return classParams;
}
//character's newMainState kind of messes with this
public void ChangeState(ushort newState)
{
if (newState != currentMainState)
{
currentMainState = newState;
updateFlags |= (ActorUpdateFlags.State | ActorUpdateFlags.Position);
}
}
public void ModifySpeed(float mod)
{
for (int i = 0; i < 4; i++)
{
moveSpeeds[i] *= mod;
}
updateFlags |= ActorUpdateFlags.Speed;
}
public void ChangeSpeed(int type, float value)
{
moveSpeeds[type] = value;
updateFlags |= ActorUpdateFlags.Speed;
}
public void ChangeSpeed(float speedStop, float speedWalk, float speedRun, float speedActive)
{
moveSpeeds[0] = speedStop;
moveSpeeds[1] = speedWalk;
moveSpeeds[2] = speedRun;
moveSpeeds[3] = speedActive;
updateFlags |= ActorUpdateFlags.Speed;
}
public virtual void Update(DateTime tick)
{
}
public virtual void PostUpdate(DateTime tick, List<SubPacket> packets = null)
{
if (updateFlags != ActorUpdateFlags.None)
{
packets = packets ?? new List<SubPacket>();
if ((updateFlags & ActorUpdateFlags.Position) != 0)
{
if (positionUpdates != null && positionUpdates.Count > 0)
{
var pos = positionUpdates[0];
if (pos != null)
{
oldPositionX = positionX;
oldPositionY = positionY;
oldPositionZ = positionZ;
oldRotation = rotation;
positionX = pos.X;
positionY = pos.Y;
positionZ = pos.Z;
zone.UpdateActorPosition(this);
//Program.Server.GetInstance().mLuaEngine.OnPath(actor, position, positionUpdates)
}
positionUpdates.Remove(pos);
}
packets.Add(CreatePositionUpdatePacket());
}
if ((updateFlags & ActorUpdateFlags.Speed) != 0)
{
packets.Add(SetActorSpeedPacket.BuildPacket(actorId, moveSpeeds[0], moveSpeeds[1], moveSpeeds[2], moveSpeeds[3]));
}
if ((updateFlags & ActorUpdateFlags.Name) != 0)
{
packets.Add(SetActorNamePacket.BuildPacket(actorId, displayNameId, customDisplayName));
}
if ((updateFlags & ActorUpdateFlags.State) != 0)
{
packets.Add(SetActorStatePacket.BuildPacket(actorId, currentMainState, currentSubState));
}
updateFlags = ActorUpdateFlags.None;
zone.BroadcastPacketsAroundActor(this, packets);
}
}
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).GetPrivateAreaType();
actorName = String.Format("{0}_{1}_{2}@{3:X3}{4:X2}", className, zoneName, classNumber, zoneId, privLevel);
}
public bool SetWorkValue(Player player, string name, string uiFunc, object value)
{
string[] split = name.Split('.');
int arrayIndex = 0;
if (!(split[0].Equals("work") || split[0].Equals("charaWork") || split[0].Equals("playerWork") || split[0].Equals("npcWork")))
return false;
Object parentObj = null;
Object curObj = this;
for (int i = 0; i < split.Length; i++)
{
//For arrays
if (split[i].Contains("["))
{
if (split[i].LastIndexOf(']') - split[i].IndexOf('[') <= 0)
return false;
arrayIndex = Convert.ToInt32(split[i].Substring(split[i].IndexOf('[') + 1, split[i].LastIndexOf(']') - split[i].LastIndexOf('[') - 1));
split[i] = split[i].Substring(0, split[i].IndexOf('['));
}
FieldInfo field = curObj.GetType().GetField(split[i]);
if (field == null)
return false;
if (i == split.Length - 1)
parentObj = curObj;
curObj = field.GetValue(curObj);
if (curObj == null)
return false;
}
if (curObj == null)
return false;
else
{
//Array, we actually care whats inside
if (curObj.GetType().IsArray)
{
if (((Array)curObj).Length <= arrayIndex)
return false;
if (value.GetType() == ((Array)curObj).GetType().GetElementType() || TypeDescriptor.GetConverter(value.GetType()).CanConvertTo(((Array)curObj).GetType().GetElementType()))
{
if (value.GetType() == ((Array)curObj).GetType().GetElementType())
((Array)curObj).SetValue(value, arrayIndex);
else
((Array)curObj).SetValue(TypeDescriptor.GetConverter(value.GetType()).ConvertTo(value, curObj.GetType().GetElementType()), arrayIndex);
SetActorPropetyPacket changeProperty = new SetActorPropetyPacket(uiFunc);
changeProperty.AddProperty(this, name);
changeProperty.AddTarget();
SubPacket subpacket = changeProperty.BuildPacket(player.actorId);
player.playerSession.QueuePacket(subpacket);
subpacket.DebugPrintSubPacket();
return true;
}
}
else
{
if (value.GetType() == curObj.GetType() || TypeDescriptor.GetConverter(value.GetType()).CanConvertTo(curObj.GetType()))
{
if (value.GetType() == curObj.GetType())
parentObj.GetType().GetField(split[split.Length - 1]).SetValue(parentObj, value);
else
parentObj.GetType().GetField(split[split.Length - 1]).SetValue(parentObj, TypeDescriptor.GetConverter(value.GetType()).ConvertTo(value, curObj.GetType()));
SetActorPropetyPacket changeProperty = new SetActorPropetyPacket(uiFunc);
changeProperty.AddProperty(this, name);
changeProperty.AddTarget();
SubPacket subpacket = changeProperty.BuildPacket(player.actorId);
player.playerSession.QueuePacket(subpacket);
subpacket.DebugPrintSubPacket();
return true;
}
}
return false;
}
}
#region positioning
public List<float> GetPos()
{
List<float> pos = new List<float>();
pos.Add(positionX);
pos.Add(positionY);
pos.Add(positionZ);
pos.Add(rotation);
pos.Add(zoneId);
return pos;
}
public Vector3 GetPosAsVector3()
{
return new Vector3(positionX, positionY, positionZ);
}
public void SetPos(float x, float y, float z, float rot = 0, uint zoneId = 0)
{
oldPositionX = positionX;
oldPositionY = positionY;
oldPositionZ = positionZ;
oldRotation = rotation;
positionX = x;
positionY = y;
positionZ = z;
rotation = rot;
// todo: handle zone?
zone.BroadcastPacketAroundActor(this, MoveActorToPositionPacket.BuildPacket(actorId, x, y, z, rot, moveState));
}
public Area GetZone()
{
return zone;
}
public uint GetZoneID()
{
return zoneId;
}
public void LookAt(Actor actor)
{
if (actor != null)
{
LookAt(actor.positionX, actor.positionZ);
}
else
{
Program.Log.Error("[{0}][{1}] Actor.LookAt() unable to find actor!", actorId, actorName);
}
}
public void LookAt(Vector3 pos)
{
if (pos != null)
{
LookAt(pos.X, pos.Z);
}
}
public void LookAt(float x, float z)
{
var rot1 = this.rotation;
var dX = this.positionX - x;
var dY = this.positionZ - z;
var rot2 = Math.Atan2(dY, dX);
var dRot = Math.PI - rot2 + Math.PI / 2;
// pending move, dont need to unset it
this.updateFlags |= ActorUpdateFlags.Position;
rotation = (float)dRot;
}
// todo: is this legit?
public bool IsFacing(float x, float z, float angle = 40.0f)
{
angle = (float)(Math.PI * angle / 180);
return Math.Abs(Vector3.GetAngle(positionX, positionZ, x, z) - rotation) < angle;
}
public bool IsFacing(Actor target, float angle = 40.0f)
{
if (target == null)
{
Program.Log.Error("[{0}][{1}] IsFacing no target!", actorId, actorName);
return false;
}
return IsFacing(target.positionX, target.positionZ, angle);
}
public void QueuePositionUpdate(Vector3 pos)
{
if (positionUpdates == null)
positionUpdates = new List<Vector3>();
positionUpdates.Add(pos);
this.updateFlags |= ActorUpdateFlags.Position;
}
public void QueuePositionUpdate(float x, float y, float z)
{
QueuePositionUpdate(new Vector3(x, y, z));
}
public void ClearPositionUpdates()
{
positionUpdates.Clear();
}
public Vector3 FindRandomPoint(float x, float y, float z, float minRadius, float maxRadius)
{
var angle = Program.Random.NextDouble() * Math.PI * 2;
var radius = Math.Sqrt(Program.Random.NextDouble() * (maxRadius - minRadius)) + minRadius;
return new Vector3(x + (float)(radius * Math.Cos(angle)), y, z + (float)(radius * Math.Sin(angle)));
}
public Vector3 FindRandomPointAroundTarget(Actor target, float minRadius, float maxRadius)
{
if (target == null)
{
Program.Log.Error($"[{this.actorId}][{this.customDisplayName}] FindRandomPointAroundTarget: no target found!");
return GetPosAsVector3();
}
return FindRandomPoint(target.positionX, target.positionY, target.positionZ, minRadius, maxRadius);
}
public Vector3 FindRandomPointAroundActor(float minRadius, float maxRadius)
{
return FindRandomPoint(positionX, positionY, positionZ, minRadius, maxRadius);
}
#endregion
}
}