Added party to Gridania opening, fixed BattleActionx18 and made it so x18 is used for packets with more than 10 targets. Changed how death works. Added respawn time and roam modifiers. Added TryAggro functions and moved aggroing out of roaming and helpplayers. Fixed high cpu usage in zone's OnUpdate function. Fixed work value in player update

This commit is contained in:
yogurt 2017-12-08 00:58:39 -06:00
parent 520ae7a119
commit 1275c8b5da
61 changed files with 1226 additions and 223 deletions

View file

@ -511,7 +511,6 @@ namespace FFXIVClassic_Map_Server
battleNpc.SetMod((uint)Modifier.Defense, reader.GetUInt32("def"));
battleNpc.SetMod((uint)Modifier.Evasion, reader.GetUInt32("eva"));
battleNpc.dropListId = reader.GetUInt32("dropListId");
battleNpc.spellListId = reader.GetUInt32("spellListId");
battleNpc.skillListId = reader.GetUInt32("skillListId");
@ -644,12 +643,48 @@ namespace FFXIVClassic_Map_Server
battleNpc.SetMod((uint)Modifier.Defense, reader.GetUInt32("def"));
battleNpc.SetMod((uint)Modifier.Evasion, reader.GetUInt32("eva"));
if (battleNpc.poolMods != null)
{
foreach (var a in battleNpc.poolMods.mobModList)
{
battleNpc.SetMobMod(a.Value.id, (long)(a.Value.value));
}
foreach (var a in battleNpc.poolMods.modList)
{
battleNpc.SetMod(a.Key, (long)(a.Value.value));
}
}
if (battleNpc.genusMods != null)
{
foreach (var a in battleNpc.genusMods.mobModList)
{
battleNpc.SetMobMod(a.Key, (long)(a.Value.value));
}
foreach (var a in battleNpc.genusMods.modList)
{
battleNpc.SetMod(a.Key, (long)(a.Value.value));
}
}
if(battleNpc.spawnMods != null)
{
foreach (var a in battleNpc.spawnMods.mobModList)
{
battleNpc.SetMobMod(a.Key, (long)(a.Value.value));
}
foreach (var a in battleNpc.spawnMods.modList)
{
battleNpc.SetMod(a.Key, (long)(a.Value.value));
}
}
battleNpc.dropListId = reader.GetUInt32("dropListId");
battleNpc.spellListId = reader.GetUInt32("spellListId");
battleNpc.skillListId = reader.GetUInt32("skillListId");
battleNpc.SetMaxHP(1000);
battleNpc.SetHP(1000);
battleNpc.SetBattleNpcId(reader.GetUInt32("bnpcId"));
battleNpc.SetRespawnTime(reader.GetUInt32("respawnTime"));
battleNpc.CalculateBaseStats();
battleNpc.RecalculateStats();
//battleNpc.SetMod((uint)Modifier.ResistFire, )
@ -679,7 +714,7 @@ namespace FFXIVClassic_Map_Server
try
{
conn.Open();
var query = $"SELECT {primaryKey}, modId, modVal, isMobMod FROM {tableName} GROUP BY {primaryKey};";
var query = $"SELECT {primaryKey}, modId, modVal, isMobMod FROM {tableName}";
MySqlCommand cmd = new MySqlCommand(query, conn);
@ -688,9 +723,9 @@ namespace FFXIVClassic_Map_Server
while (reader.Read())
{
var id = reader.GetUInt32(primaryKey);
ModifierList modList = new ModifierList(id);
ModifierList modList = list.TryGetValue(id, out modList) ? modList : new ModifierList(id);
modList.SetModifier(reader.GetUInt16("modId"), reader.GetInt64("modVal"), reader.GetBoolean("isMobMod"));
list.Add(id, modList);
list[id] = modList;
}
}
}

View file

@ -438,9 +438,6 @@ namespace FFXIVClassic_Map_Server.Actors
updateFlags = ActorUpdateFlags.None;
zone.BroadcastPacketsAroundActor(this, packets);
SetActorPropetyPacket hpInfo = new SetActorPropetyPacket("charaWork/exp");
hpInfo.AddTarget();
}
}

View file

@ -419,6 +419,11 @@ namespace FFXIVClassic_Map_Server.Actors
public virtual List<BattleNpc> GetMonsters()
{
return GetAllActors<BattleNpc>();
}
public virtual List<Ally> GetAllies()
{
return GetAllActors<Ally>();
}
public void BroadcastPacketsAroundActor(Actor actor, List<SubPacket> packets)
@ -495,8 +500,8 @@ namespace FFXIVClassic_Map_Server.Actors
npc = new Npc(mActorList.Count + 1, actorClass, uniqueId, this, x, y, z, rot, state, animId, null);
npc.LoadEventConditions(actorClass.eventConditions);
npc.SetMaxHP(300);
npc.SetHP(300);
//npc.SetMaxHP(30000);
//npc.SetHP(30000);
AddActorToZone(npc);
@ -666,7 +671,7 @@ namespace FFXIVClassic_Map_Server.Actors
a.Update(tick);
var deltaTime = (tick - Program.LastTick).TotalMilliseconds;
LuaEngine.GetInstance().CallLuaFunction(null, this, "onUpdate", true, deltaTime, this);
//LuaEngine.GetInstance().CallLuaFunction(null, this, "onUpdate", true, this, deltaTime);
}
}

View file

@ -132,6 +132,18 @@ namespace FFXIVClassic_Map_Server.actors.area
return actor;
}
}
foreach (List<PrivateAreaContent> paList in contentAreas.Values)
{
foreach (PrivateArea pa in paList)
{
Actor actor = pa.FindActorInArea(id);
if (actor != null)
return actor;
}
}
return null;
}
else
@ -184,7 +196,7 @@ namespace FFXIVClassic_Map_Server.actors.area
{
if (this.pathCalls > 0)
{
Program.Log.Debug("Number of pathfinding calls {0} average time {1}ms", pathCalls, (float)(pathCallTime / pathCalls));
Program.Log.Debug("Number of pathfinding calls {0} average time {1}ms", pathCalls, (float)(pathCallTime / pathCalls));
}
lastUpdate = tick;
}

View file

@ -230,10 +230,10 @@ namespace FFXIVClassic_Map_Server.Actors
public void DoBattleAction(ushort commandId, uint animationId, BattleAction[] actions)
{
int currentIndex = 0;
//AoE abilities only ever hit 16 people, so we probably won't need this loop anymore
while (true)
{
if (actions.Length - currentIndex >= 18)
if (actions.Length - currentIndex >= 10)
zone.BroadcastPacketAroundActor(this, BattleActionX18Packet.BuildPacket(actorId, animationId, commandId, actions, ref currentIndex));
else if (actions.Length - currentIndex > 1)
zone.BroadcastPacketAroundActor(this, BattleActionX10Packet.BuildPacket(actorId, animationId, commandId, actions, ref currentIndex));
@ -244,7 +244,9 @@ namespace FFXIVClassic_Map_Server.Actors
}
else
break;
animationId = 0; //If more than one packet is sent out, only send the animation once to avoid double playing.
//I think aoe effects play on all hit enemies. Firaga does at least
//animationId = 0; //If more than one packet is sent out, only send the animation once to avoid double playing.
}
}
@ -313,7 +315,7 @@ namespace FFXIVClassic_Map_Server.Actors
public virtual void OnPath(Vector3 point)
{
lua.LuaEngine.CallLuaBattleFunction(this, "onPath", this, point);
//lua.LuaEngine.CallLuaBattleFunction(this, "onPath", this, point);
updateFlags |= ActorUpdateFlags.Position;
this.isAtSpawn = false;
@ -347,7 +349,7 @@ namespace FFXIVClassic_Map_Server.Actors
if ((updateFlags & ActorUpdateFlags.HpTpMp) != 0)
{
var propPacketUtil = new ActorPropertyPacketUtil("charaWork.parameterSave", this);
var propPacketUtil = new ActorPropertyPacketUtil("charaWork/stateAtQuicklyForAll", this);
propPacketUtil.AddProperty("charaWork.parameterSave.mp");
propPacketUtil.AddProperty("charaWork.parameterSave.mpMax");
@ -470,6 +472,7 @@ namespace FFXIVClassic_Map_Server.Actors
{
// todo: actual despawn timer
aiContainer.InternalDie(tick, 10);
ChangeSpeed(0.0f, 0.0f, 0.0f, 0.0f);
}
public virtual void Despawn(DateTime tick)
@ -543,6 +546,20 @@ namespace FFXIVClassic_Map_Server.Actors
updateFlags |= ActorUpdateFlags.HpTpMp;
}
public void SetMP(uint mp)
{
charaWork.parameterSave.mpMax = (short)mp;
if (mp > charaWork.parameterSave.hpMax[0])
SetMaxMP(mp);
updateFlags |= ActorUpdateFlags.HpTpMp;
}
public void SetMaxMP(uint mp)
{
charaWork.parameterSave.mp = (short)mp;
updateFlags |= ActorUpdateFlags.HpTpMp;
}
// todo: the following functions are virtuals since we want to check hidden item bonuses etc on player for certain conditions
public virtual void AddHP(int hp)
{
@ -562,7 +579,7 @@ namespace FFXIVClassic_Map_Server.Actors
}
}
public short GetJob()
public short GetClass()
{
return charaWork.parameterSave.state_mainSkill[0];
}
@ -614,8 +631,30 @@ namespace FFXIVClassic_Map_Server.Actors
public void RecalculateStats()
{
if (GetMod((uint)Modifier.Hp) != 0)
uint hpMod = (uint) GetMod((uint)Modifier.Hp);
if (hpMod != 0)
{
SetMaxHP(hpMod);
uint hpp = (uint) GetMod((uint) Modifier.HpPercent);
uint hp = hpMod;
if(hpp != 0)
{
hp = (uint) Math.Ceiling(((float)hpp / 100.0f) * hpMod);
}
SetHP(hp);
}
uint mpMod = (uint)GetMod((uint)Modifier.Mp);
if (mpMod != 0)
{
SetMaxMP(mpMod);
uint mpp = (uint)GetMod((uint)Modifier.MpPercent);
uint mp = mpMod;
if (mpp != 0)
{
mp = (uint)Math.Ceiling(((float)mpp / 100.0f) * mpMod);
}
SetMP(mp);
}
// todo: recalculate stats and crap
updateFlags |= ActorUpdateFlags.HpTpMp;
@ -650,9 +689,10 @@ namespace FFXIVClassic_Map_Server.Actors
//var packet = BattleActionX01Packet.BuildPacket(owner.actorId, owner.actorId, target.actorId, (uint)0x19001000, (uint)0x8000604, (ushort)0x765D, (ushort)BattleActionX01PacketCommand.Attack, (ushort)damage, (byte)0x1);
}
target.OnDamageTaken(this, action, DamageTakenType.Ability);
// todo: call onAttack/onDamageTaken
target.DelHP(action.amount);
BattleUtils.DamageTarget(this, target, action);
target.OnDamageTaken(this, action, DamageTakenType.Ability);
if (target is BattleNpc)
((BattleNpc)target).lastAttacker = this;
@ -664,13 +704,26 @@ namespace FFXIVClassic_Map_Server.Actors
{
var spell = ((MagicState)state).GetSpell();
// damage is handled in script
this.DelMP(spell.mpCost); // mpCost can be set in script e.g. if caster has something for free spells
var spellCost = spell.CalculateCost((uint)this.GetLevel());
this.DelMP(spellCost); // mpCost can be set in script e.g. if caster has something for free spells
foreach (var action in actions)
zone.FindActorInArea<Character>(action.targetId).OnDamageTaken(this, action, DamageTakenType.Magic);
foreach (BattleAction action in actions)
{
if (zone.FindActorInArea<Character>(action.targetId) is Character chara)
{
if (chara != null && chara is BattleNpc)
{
((BattleNpc)chara).hateContainer.UpdateHate(this, action.amount);
((BattleNpc)chara).lastAttacker = this;
}
BattleUtils.DamageTarget(this, chara, action);
}
}
if (target is BattleNpc)
((BattleNpc)target).lastAttacker = this;
lua.LuaEngine.GetInstance().OnSignal("spellUsed");
}
public virtual void OnWeaponSkill(State state, BattleAction[] actions, ref BattleAction[] errors)
@ -679,11 +732,25 @@ namespace FFXIVClassic_Map_Server.Actors
// damage is handled in script
this.DelTP(skill.tpCost);
foreach (var action in actions)
zone.FindActorInArea<BattleNpc>(action.targetId)?.OnDamageTaken(this, action, DamageTakenType.Weaponskill);
foreach (BattleAction action in actions)
{
if (zone.FindActorInArea<Character>(action.targetId) is Character chara)
{
if (chara != null && chara is BattleNpc)
{
((BattleNpc)chara).hateContainer.UpdateHate(this, action.amount);
((BattleNpc)chara).lastAttacker = this;
}
BattleUtils.DamageTarget(this, chara, action);
}
}
if (target is BattleNpc)
((BattleNpc)target).lastAttacker = this;
lua.LuaEngine.GetInstance().OnSignal("weaponskillUsed");
}
public virtual void OnAbility(State state, BattleAction[] actions, ref BattleAction[] errors)
@ -769,22 +836,22 @@ namespace FFXIVClassic_Map_Server.Actors
public bool IsDiscipleOfWar()
{
return currentJob < CLASSID_THM;
return GetClass() < CLASSID_THM;
}
public bool IsDiscipleOfMagic()
{
return currentJob >= CLASSID_THM && currentJob < CLASSID_CRP;
return GetClass() >= CLASSID_THM && currentJob < CLASSID_CRP;
}
public bool IsDiscipleOfHand()
{
return currentJob >= CLASSID_CRP && currentJob < CLASSID_MIN;
return GetClass() >= CLASSID_CRP && currentJob < CLASSID_MIN;
}
public bool IsDiscipleOfLand()
{
return currentJob >= CLASSID_MIN;
return GetClass() >= CLASSID_MIN;
}
#endregion lua helpers
#endregion ai stuff

View file

@ -5,6 +5,7 @@ using System.Text;
using System.Threading.Tasks;
using FFXIVClassic_Map_Server.Actors;
using FFXIVClassic_Map_Server.actors.chara.npc;
using FFXIVClassic.Common;
namespace FFXIVClassic_Map_Server.actors.chara.ai.controllers
{
@ -17,10 +18,11 @@ namespace FFXIVClassic_Map_Server.actors.chara.ai.controllers
{
this.owner = owner;
}
// server really likes to hang whenever scripts iterate area's actorlist
protected override void DoCombatTick(DateTime tick, List<Character> contentGroupCharas = null)
protected List<Character> GetContentGroupCharas()
{
List<Character> contentGroupCharas = null;
if (owner.currentContentGroup != null)
{
contentGroupCharas = new List<Character>(owner.currentContentGroup.GetMemberCount());
@ -32,20 +34,46 @@ namespace FFXIVClassic_Map_Server.actors.chara.ai.controllers
contentGroupCharas.Add(chara);
}
}
return contentGroupCharas;
}
//Iterate over players in the group and if they are fighting, assist them
protected override void TryAggro(DateTime tick)
{
//lua.LuaEngine.CallLuaBattleFunction(owner, "tryAggro", owner, GetContentGroupCharas());
foreach(Character chara in GetContentGroupCharas())
{
if(chara.IsPlayer())
{
if(owner.aiContainer.GetTargetFind().CanTarget((Character) chara.target) && chara.target is BattleNpc && ((BattleNpc)chara.target).hateContainer.HasHateForTarget(chara))
{
owner.Engage(chara.target.actorId);
owner.hateContainer.AddBaseHate((Character) chara.target);
break;
}
}
}
//base.TryAggro(tick);
}
// server really likes to hang whenever scripts iterate area's actorlist
protected override void DoCombatTick(DateTime tick, List<Character> contentGroupCharas = null)
{
if (contentGroupCharas == null)
{
contentGroupCharas = GetContentGroupCharas();
}
base.DoCombatTick(tick, contentGroupCharas);
}
protected override void DoRoamTick(DateTime tick, List<Character> contentGroupCharas = null)
{
if (owner.currentContentGroup != null)
if (contentGroupCharas == null)
{
contentGroupCharas = new List<Character>(owner.currentContentGroup.GetMemberCount());
foreach (var charaId in owner.currentContentGroup.GetMembers())
{
var chara = owner.zone.FindActorInArea<Character>(charaId);
if (chara != null)
contentGroupCharas.Add(chara);
}
contentGroupCharas = GetContentGroupCharas();
}
base.DoRoamTick(tick, contentGroupCharas);
}

View file

@ -39,12 +39,20 @@ namespace FFXIVClassic_Map_Server.actors.chara.ai.controllers
public override void Update(DateTime tick)
{
lastUpdate = tick;
// todo: handle aggro/deaggro and other shit here
if (owner.aiContainer.IsEngaged())
if (!owner.aiContainer.IsEngaged())
{
TryAggro(tick);
}
if(owner.aiContainer.IsEngaged())
{
DoCombatTick(tick);
}
else if (!owner.IsDead())
//Only move if owner isn't dead and is either too far away from their spawn point or is meant to roam
if (!owner.IsDead() && (owner.isMovingToSpawn || owner.GetMobMod((uint) MobModifier.Roams) > 0))
{
DoRoamTick(tick);
}
@ -63,6 +71,38 @@ namespace FFXIVClassic_Map_Server.actors.chara.ai.controllers
return false;
}
//If the owner isn't moving to spawn, iterate over nearby enemies and
//aggro the first one that is within 10 levels and can be detected, then engage
protected virtual void TryAggro(DateTime tick)
{
if (tick >= neutralTime && !owner.isMovingToSpawn)
{
if (!owner.neutral && owner.IsAlive())
{
foreach (var chara in owner.zone.GetActorsAroundActor<Character>(owner, 50))
{
if (owner.allegiance == chara.allegiance)
continue;
if (owner.aiContainer.pathFind.AtPoint() && owner.detectionType != DetectionType.None)
{
uint levelDifference = (uint)Math.Abs(owner.GetLevel() - chara.GetLevel());
if (levelDifference <= 10 || (owner.detectionType & DetectionType.IgnoreLevelDifference) != 0 && CanAggroTarget(chara))
{
owner.hateContainer.AddBaseHate(chara);
break;
}
}
}
}
}
if (owner.hateContainer.GetHateList().Count > 0)
{
Engage(owner.hateContainer.GetMostHatedTarget());
}
}
public override bool Engage(Character target)
{
var canEngage = this.owner.aiContainer.InternalEngage(target);
@ -77,6 +117,7 @@ namespace FFXIVClassic_Map_Server.actors.chara.ai.controllers
owner.ChangeState(SetActorStatePacket.MAIN_STATE_ACTIVE);
owner.moveState = 2;
//owner.SetMod((uint)Modifier.Speed, 5);
lastActionTime = DateTime.Now;
battleStartTime = lastActionTime;
// todo: adjust cooldowns with modifiers
@ -129,12 +170,6 @@ namespace FFXIVClassic_Map_Server.actors.chara.ai.controllers
protected virtual void DoRoamTick(DateTime tick, List<Character> contentGroupCharas = null)
{
if (owner.hateContainer.GetHateList().Count > 0)
{
Engage(owner.hateContainer.GetMostHatedTarget());
return;
}
if (tick >= waitTime)
{
neutralTime = tick.AddSeconds(5);
@ -150,7 +185,7 @@ namespace FFXIVClassic_Map_Server.actors.chara.ai.controllers
}
}
waitTime = tick.AddSeconds(10);
waitTime = tick.AddSeconds(owner.GetMobMod((uint) MobModifier.RoamDelay));
owner.OnRoam(tick);
if (CanMoveForward(0.0f) && !owner.aiContainer.pathFind.IsFollowingPath())
@ -159,31 +194,7 @@ namespace FFXIVClassic_Map_Server.actors.chara.ai.controllers
owner.aiContainer.pathFind.SetPathFlags(PathFindFlags.None);
owner.aiContainer.pathFind.PathInRange(owner.spawnX, owner.spawnY, owner.spawnZ, 1.5f, 50.0f);
}
lua.LuaEngine.CallLuaBattleFunction(owner, "onRoam", owner, contentGroupCharas);
}
if (tick >= neutralTime)
{
if (!owner.neutral && owner.IsAlive())
{
foreach (var chara in owner.zone.GetActorsAroundActor<Character>(owner, 50))
{
if (owner.allegiance == chara.allegiance)
continue;
if (!owner.isMovingToSpawn && owner.aiContainer.pathFind.AtPoint() && owner.detectionType != DetectionType.None)
{
uint levelDifference = (uint)Math.Abs(owner.GetLevel() - chara.GetLevel());
if (levelDifference <= 10 || (owner.detectionType & DetectionType.IgnoreLevelDifference) != 0 && CanAggroTarget(chara))
{
owner.hateContainer.AddBaseHate(chara);
break;
}
}
}
}
//lua.LuaEngine.CallLuaBattleFunction(owner, "onRoam", owner, contentGroupCharas);
}
if (owner.aiContainer.pathFind.IsFollowingPath() && owner.aiContainer.CanFollowPath())
@ -202,8 +213,8 @@ namespace FFXIVClassic_Map_Server.actors.chara.ai.controllers
return;
}
Move();
lua.LuaEngine.CallLuaBattleFunction(owner, "onCombatTick", owner, owner.target, Utils.UnixTimeStampUTC(tick), contentGroupCharas);
}
protected virtual void Move()

View file

@ -159,6 +159,7 @@ namespace FFXIVClassic_Map_Server.actors.chara.ai
/// </summary>
public void FindWithinArea(Character target, ValidTarget flags, TargetFindAOETarget aoeTarget)
{
targets.Clear();
validTarget = flags;
// are we creating aoe circles around target or self
if (aoeTarget == TargetFindAOETarget.Self)
@ -174,59 +175,63 @@ namespace FFXIVClassic_Map_Server.actors.chara.ai
if (masterTarget != null)
targets.Add(masterTarget);
if (IsPlayer(owner))
if (aoeType != TargetFindAOEType.None)
{
if (masterTarget is Player)
if (IsPlayer(owner))
{
findType = TargetFindCharacterType.PlayerToPlayer;
if (masterTarget.currentParty != null)
if (masterTarget is Player)
{
if ((validTarget & (ValidTarget.Ally | ValidTarget.PartyMember)) != 0)
AddAllInAlliance(masterTarget, withPet);
findType = TargetFindCharacterType.PlayerToPlayer;
if (masterTarget.currentParty != null)
{
if ((validTarget & (ValidTarget.Ally | ValidTarget.PartyMember)) != 0)
AddAllInAlliance(masterTarget, withPet);
else
AddAllInParty(masterTarget, withPet);
}
else
AddAllInParty(masterTarget, withPet);
{
AddTarget(masterTarget, withPet);
}
}
else
{
AddTarget(masterTarget, withPet);
findType = TargetFindCharacterType.PlayerToBattleNpc;
AddAllBattleNpcs(masterTarget, false);
}
}
else
{
findType = TargetFindCharacterType.PlayerToBattleNpc;
AddAllBattleNpcs(masterTarget, false);
}
}
else
{
// todo: this needs checking..
if (masterTarget is Player || owner.allegiance == CharacterTargetingAllegiance.Player)
findType = TargetFindCharacterType.BattleNpcToPlayer;
else
findType = TargetFindCharacterType.BattleNpcToBattleNpc;
// todo: configurable pet aoe buff
if (findType == TargetFindCharacterType.BattleNpcToBattleNpc && TryGetMasterTarget(target) != null)
withPet = true;
// todo: does ffxiv have call for help flag?
//if ((findFlags & ValidTarget.HitAll) != 0)
//{
// AddAllInZone(masterTarget, withPet);
//}
AddAllInAlliance(target, withPet);
if (findType == TargetFindCharacterType.BattleNpcToPlayer)
{
if (owner.allegiance == CharacterTargetingAllegiance.Player)
AddAllInZone(masterTarget, withPet);
// todo: this needs checking..
if (masterTarget is Player || owner.allegiance == CharacterTargetingAllegiance.Player)
findType = TargetFindCharacterType.BattleNpcToPlayer;
else
AddAllInHateList();
findType = TargetFindCharacterType.BattleNpcToBattleNpc;
// todo: configurable pet aoe buff
if (findType == TargetFindCharacterType.BattleNpcToBattleNpc && TryGetMasterTarget(target) != null)
withPet = true;
// todo: does ffxiv have call for help flag?
//if ((findFlags & ValidTarget.HitAll) != 0)
//{
// AddAllInZone(masterTarget, withPet);
//}
AddAllInAlliance(target, withPet);
if (findType == TargetFindCharacterType.BattleNpcToPlayer)
{
if (owner.allegiance == CharacterTargetingAllegiance.Player)
AddAllInZone(masterTarget, withPet);
else
AddAllInHateList();
}
}
}
if(targets.Count > 16)
targets.RemoveRange(16, targets.Count - 16);
}
/// <summary>
@ -288,7 +293,8 @@ namespace FFXIVClassic_Map_Server.actors.chara.ai
private void AddAllBattleNpcs(Character target, bool withPet)
{
var actors = owner.zone.GetActorsAroundActor<BattleNpc>(owner, 50);
int dist = (int)maxDistance;
var actors = owner.zone.GetActorsAroundActor<BattleNpc>(target, dist);
foreach (BattleNpc actor in actors)
{
@ -375,12 +381,13 @@ namespace FFXIVClassic_Map_Server.actors.chara.ai
private bool IsWithinCircle(Character target, float maxDistance)
{
// todo: make y diff modifiable?
if (Math.Abs(owner.positionX - target.positionY) > 6.0f)
return false;
//if (Math.Abs(owner.positionX - target.positionY) > 6.0f)
// return false;
if (this.targetPosition == null)
this.targetPosition = aoeTarget == TargetFindAOETarget.Self ? owner.GetPosAsVector3() : masterTarget.GetPosAsVector3();
return target.GetPosAsVector3().IsWithinCircle(targetPosition, param);
return target.GetPosAsVector3().IsWithinCircle(targetPosition, maxDistance);
}
private bool IsPlayer(Character target)

View file

@ -15,7 +15,9 @@ namespace FFXIVClassic_Map_Server.actors.chara.ai.state
: base(owner, null)
{
owner.Disengage();
owner.ChangeState(SetActorStatePacket.MAIN_STATE_DEAD2);
//owner.ChangeState(SetActorStatePacket.MAIN_STATE_DEAD2);
var deathStatePacket = SetActorStatePacket.BuildPacket(owner.actorId, SetActorStatePacket.MAIN_STATE_DEAD, owner.currentSubState);
owner.zone.BroadcastPacketAroundActor(owner, deathStatePacket);
canInterrupt = false;
startTime = tick;
despawnTime = startTime.AddSeconds(timeToFadeOut);

View file

@ -110,16 +110,16 @@ namespace FFXIVClassic_Map_Server.actors.chara.ai.state
var i = 0;
foreach (var chara in targets)
{
var action = new BattleAction(target.actorId, spell.worldMasterTextId, spell.battleAnimation, 0, (byte)HitDirection.None, 1);
var action = new BattleAction(chara.actorId, spell.worldMasterTextId, spell.battleAnimation, 0, (byte)HitDirection.None, 1);
action.amount = (ushort)lua.LuaEngine.CallLuaBattleCommandFunction(owner, spell, "magic", "onMagicFinish", owner, chara, spell, action);
actions[i++] = action;
}
// todo: this is fuckin stupid, probably only need *one* error packet, not an error for each action
var errors = (BattleAction[])actions.Clone();
owner.OnCast(this, actions, ref errors);
owner.DoBattleAction(spell.id, spell.battleAnimation, actions);
}
public override void TryInterrupt()

View file

@ -88,7 +88,6 @@ namespace FFXIVClassic_Map_Server.actors.chara.ai.state
{
skill.targetFind.FindWithinArea(target, skill.validTarget, skill.aoeTarget);
isCompleted = true;
var targets = skill.targetFind.GetTargets();
BattleAction[] actions = new BattleAction[targets.Count];
@ -100,12 +99,12 @@ namespace FFXIVClassic_Map_Server.actors.chara.ai.state
// evasion, miss, dodge, etc to be handled in script, calling helpers from scripts/weaponskills.lua
action.amount = (ushort)lua.LuaEngine.CallLuaBattleCommandFunction(owner, skill, "weaponskill", "onSkillFinish", owner, target, skill, action);
actions[i++] = action;
chara.Engage(chara.actorId, 1);
}
// todo: this is fuckin stupid, probably only need *one* error packet, not an error for each action
var errors = (BattleAction[])actions.Clone();
owner.OnWeaponSkill(this, actions, ref errors);
owner.OnWeaponSkill(this, actions, ref errors);
owner.DoBattleAction(skill.id, skill.battleAnimation, actions);
}

View file

@ -78,16 +78,19 @@ namespace FFXIVClassic_Map_Server.actors.chara.ai.utils
public static void DamageTarget(Character attacker, Character defender, BattleAction action)
{
// todo: other stuff too
if (defender is BattleNpc)
if (defender != null)
{
if (!((BattleNpc)defender).hateContainer.HasHateForTarget(attacker))
// todo: other stuff too
if (defender is BattleNpc)
{
((BattleNpc)defender).hateContainer.AddBaseHate(attacker);
if (!((BattleNpc)defender).hateContainer.HasHateForTarget(attacker))
{
((BattleNpc)defender).hateContainer.AddBaseHate(attacker);
}
((BattleNpc)defender).hateContainer.UpdateHate(attacker, action.amount);
}
((BattleNpc)defender).hateContainer.UpdateHate(attacker, action.amount);
defender.DelHP((short)action.amount);
}
defender.DelHP((short)action.amount);
}
public static int CalculateSpellDamage(Character attacker, Character defender, BattleCommand spell)

View file

@ -177,7 +177,7 @@ namespace FFXIVClassic_Map_Server.Actors
packets = new List<SubPacket>();
if ((updateFlags & ActorUpdateFlags.HpTpMp) != 0)
{
var propPacketUtil = new ActorPropertyPacketUtil("charaWork.parameterSave", this);
var propPacketUtil = new ActorPropertyPacketUtil("charaWork/stateAtQuicklyForAll", this);
propPacketUtil.AddProperty("charaWork.parameterSave.hp[0]");
propPacketUtil.AddProperty("charaWork.parameterSave.hpMax[0]");
@ -290,6 +290,10 @@ namespace FFXIVClassic_Map_Server.Actors
if (lastAttacker is Player)
{
//I think this is, or should be odne in DoBattleAction. Packet capture had the message in the same packet as an attack
// <actor> defeat/defeats <target>
//((Player)lastAttacker).QueuePacket(BattleActionX01Packet.BuildPacket(lastAttacker.actorId, 0, 0, new BattleAction(actorId, 30108, 0)));
if (lastAttacker.currentParty != null && lastAttacker.currentParty is Party)
{
foreach (var memberId in ((Party)lastAttacker.currentParty).members)
@ -297,14 +301,9 @@ namespace FFXIVClassic_Map_Server.Actors
var partyMember = zone.FindActorInArea<Player>(memberId);
// onDeath(monster, player, killer)
lua.LuaEngine.CallLuaBattleFunction(this, "onDeath", this, partyMember, lastAttacker);
// <actor> defeat/defeats <target>
if (lastAttacker is Player)
((Player)lastAttacker).QueuePacket(BattleActionX01Packet.BuildPacket(lastAttacker.actorId, 0, 0, new BattleAction(actorId, 30108, 0)));
if(partyMember is Player)
((Player)partyMember).AddExp(1500, (byte)partyMember.GetJob(), 5);
if(partyMember is Player)
((Player)partyMember).AddExp(1500, (byte)partyMember.GetClass(), 5);
}
}
else
@ -317,7 +316,6 @@ namespace FFXIVClassic_Map_Server.Actors
positionUpdates?.Clear();
aiContainer.InternalDie(tick, despawnTime);
this.ResetMoveSpeeds();
// todo: reset cooldowns
lua.LuaEngine.GetInstance().OnSignal("mobkill");
@ -381,6 +379,9 @@ namespace FFXIVClassic_Map_Server.Actors
if (GetMobMod((uint)MobModifier.AttackScript) != 0)
lua.LuaEngine.CallLuaBattleFunction(this, "onAttack", this, state.GetTarget(), action.amount);
if (target is BattleNpc)
((BattleNpc)target).hateContainer.UpdateHate(this, action.amount);
}
public override void OnCast(State state, BattleAction[] actions, ref BattleAction[] errors)
@ -390,6 +391,19 @@ namespace FFXIVClassic_Map_Server.Actors
if (GetMobMod((uint)MobModifier.SpellScript) != 0)
foreach (var action in actions)
lua.LuaEngine.CallLuaBattleFunction(this, "onCast", this, zone.FindActorInArea<Character>(action.targetId), ((MagicState)state).GetSpell(), action);
foreach (BattleAction action in actions)
{
if (zone.FindActorInArea<Character>(action.targetId) is Character chara)
{
if (chara is BattleNpc)
{
((BattleNpc)chara).hateContainer.UpdateHate(this, action.amount);
((BattleNpc)chara).lastAttacker = this;
}
BattleUtils.DamageTarget(this, chara, action);
}
}
}
public override void OnAbility(State state, BattleAction[] actions, ref BattleAction[] errors)

View file

@ -32,5 +32,7 @@ namespace FFXIVClassic_Map_Server.actors.chara.npc
AbilityScript = 21, // call my script's onAbility whenever i finish using an ability
CallForHelp = 22, // actor with this id outside of target's party with this can attack me
FreeForAll = 23, // any actor can attack me
Roams = 24, // Do I walk around?
RoamDelay = 25 // What is the delay between roam ticks
}
}

View file

@ -557,6 +557,9 @@ namespace FFXIVClassic_Map_Server.Actors
if (currentContentGroup != null)
currentContentGroup.SendGroupPackets(playerSession);
if (currentParty != null)
currentParty.SendGroupPackets(playerSession);
}
private void SendRemoveInventoryPackets(List<ushort> slots)
@ -1625,7 +1628,7 @@ namespace FFXIVClassic_Map_Server.Actors
//Update Instance
List<Actor> aroundMe = new List<Actor>();
if (zone != null)
if (zone != null)
aroundMe.AddRange(zone.GetActorsAroundActor(this, 50));
if (zone2 != null)
aroundMe.AddRange(zone2.GetActorsAroundActor(this, 50));
@ -1714,6 +1717,7 @@ namespace FFXIVClassic_Map_Server.Actors
//currentParty.members.Remove(this);
if (partyGroup.members.Count == 0)
Server.GetWorldManager().NoMembersInParty((Party)currentParty);
currentParty = null;
}
@ -1755,7 +1759,7 @@ namespace FFXIVClassic_Map_Server.Actors
if ((updateFlags & ActorUpdateFlags.HpTpMp) != 0)
{
var propPacketUtil = new ActorPropertyPacketUtil("charaWork.parameterSave", this);
var propPacketUtil = new ActorPropertyPacketUtil("charaWork/stateAtQuicklyForAll", this);
// todo: should this be using job as index?
propPacketUtil.AddProperty($"charaWork.parameterSave.hp[{0}]");
@ -1768,9 +1772,6 @@ namespace FFXIVClassic_Map_Server.Actors
}
base.PostUpdate(tick, packets);
SetActorPropetyPacket hpInfo = new SetActorPropetyPacket("charaWork/exp");
hpInfo.AddTarget();
QueuePacket(hpInfo.BuildPacket(actorId));
}
public override void Die(DateTime tick)
@ -1801,7 +1802,6 @@ namespace FFXIVClassic_Map_Server.Actors
public void UpdateHotbarCommands(List<ushort> slotsToUpdate)
{
ActorPropertyPacketUtil propPacketUtil = new ActorPropertyPacketUtil("charaWork/command", this);
ActorPropertyPacketUtil compatibiltyUtil = new ActorPropertyPacketUtil("charaWork/commandDetailForSelf", this);
foreach (ushort slot in slotsToUpdate)
{
propPacketUtil.AddProperty($"charaWork.command[{slot}]");
@ -2184,9 +2184,9 @@ namespace FFXIVClassic_Map_Server.Actors
{
// todo: update hotbar timers to skill's recast time (also needs to be done on class change or equip crap)
base.OnCast(state, actions, ref errors);
var spell = ((MagicState)state).GetSpell();
// todo: should just make a thing that updates the one slot cause this is dumb as hell
// todo: should just make a thing that updates the one slot cause this is dumb as hell
UpdateHotbarTimer(spell.id, spell.recastTimeSeconds);
}
@ -2300,27 +2300,23 @@ namespace FFXIVClassic_Map_Server.Actors
Database.LoadHotbar(this);
}
//Gets the id of the player's current job. If they aren't a job, gets the id of their class
public byte GetCurrentClassOrJob()
{
if (currentJob != 0)
return (byte) currentJob;
return charaWork.parameterSave.state_mainSkill[0];
}
public void hpstuff(uint hp)
{
SetMaxHP(hp);
SetHP(hp);
SetHP(hp);
mpMaxBase = (ushort)hp;
charaWork.parameterSave.mpMax = (short)hp;
charaWork.parameterSave.mp = (short)hp;
AddTP(0);
//SendCharaExpInfo();
//ActorPropertyPacketUtil exp = new ActorPropertyPacketUtil("charaWork/exp", this);
SetActorPropetyPacket hpInfo = new SetActorPropetyPacket("charaWork/exp");
hpInfo.AddTarget();
QueuePacket(hpInfo.BuildPacket(actorId));
AddTP(3000);
updateFlags |= ActorUpdateFlags.HpTpMp;
}
}

View file

@ -161,6 +161,9 @@ namespace FFXIVClassic_Map_Server.actors.director
{
members.Add(actor);
if (actor is Player)
((Player)actor).AddDirector(this);
if (contentGroup != null)
contentGroup.AddMember(actor);
}

View file

@ -42,6 +42,7 @@ namespace FFXIVClassic_Map_Server.actors.group
public void Start()
{
isStarted = true;
SendGroupPacketsAll(members);
}
@ -50,7 +51,8 @@ namespace FFXIVClassic_Map_Server.actors.group
if (actor == null)
return;
members.Add(actor.actorId);
if(!members.Contains(actor.actorId))
members.Add(actor.actorId);
if (actor is Character)
((Character)actor).SetCurrentContentGroup(this);
@ -121,7 +123,6 @@ namespace FFXIVClassic_Map_Server.actors.group
}
session.QueuePacket(GroupMembersEndPacket.buildPacket(session.id, session.GetActor().zoneId, time, this));
}
public override uint GetTypeId()

View file

@ -64,11 +64,23 @@ namespace FFXIVClassic_Map_Server.actors.group
groupMembers.Add(new GroupMember(id, -1, 0, false, true, Server.GetWorldManager().GetActorInWorld(id).customDisplayName));
foreach (uint charaId in members)
{
if (charaId != id)
groupMembers.Add(new GroupMember(charaId, -1, 0, false, true, Server.GetWorldManager().GetActorInWorld(charaId).customDisplayName));
var chara = Server.GetWorldManager().GetActorInWorld(charaId);
if (charaId != id && chara != null)
groupMembers.Add(new GroupMember(charaId, -1, 0, false, true, chara.customDisplayName));
}
return groupMembers;
}
public void AddMember(uint memberId)
{
members.Add(memberId);
SendGroupPacketsAll(members);
}
public void RemoveMember(uint memberId)
{
members.Remove(memberId);
SendGroupPacketsAll(members);
}
}
}

View file

@ -24,7 +24,7 @@ namespace FFXIVClassic_Map_Server.dataobjects
public void FlushQueuedSendPackets()
{
if (!socket.Connected)
if (socket == null || !socket.Connected)
return;
while (SendPacketQueue.Count > 0)

View file

@ -478,10 +478,18 @@ namespace FFXIVClassic_Map_Server.lua
{
if (!script.Globals.Get(funcName).IsNil())
{
Coroutine coroutine = script.CreateCoroutine(script.Globals[funcName]).Coroutine;
DynValue value = coroutine.Resume(args2);
ResolveResume(player, coroutine, value);
try
{
Coroutine coroutine = script.CreateCoroutine(script.Globals[funcName]).Coroutine;
DynValue value = coroutine.Resume(args2);
ResolveResume(player, coroutine, value);
}
catch(Exception e)
{
player.SendMessage(0x20, "", e.Message);
player.EndEvent();
}
}
else
{

View file

@ -112,7 +112,6 @@ namespace FFXIVClassic_Map_Server.packets.send.actor
{
string[] split = name.Split('.');
int arrayIndex = 0;
if (!(split[0].Equals("work") || split[0].Equals("charaWork") || split[0].Equals("playerWork") || split[0].Equals("npcWork") || split[0].Equals("guildleveWork")))
return false;

View file

@ -35,33 +35,33 @@ namespace FFXIVClassic_Map_Server.packets.send.actor.battle
binWriter.Write((UInt16)commandId);
binWriter.Write((UInt16)0x810); //?
binWriter.Seek(0x58, SeekOrigin.Begin);
binWriter.Seek(0x28, SeekOrigin.Begin);
for (int i = 0; i < max; i++)
binWriter.Write((UInt32)actionList[listOffset + i].targetId);
binWriter.Seek(0xA0, SeekOrigin.Begin);
binWriter.Seek(0x70, SeekOrigin.Begin);
for (int i = 0; i < max; i++)
binWriter.Write((UInt16)actionList[listOffset + i].amount);
binWriter.Seek(0xC4, SeekOrigin.Begin);
binWriter.Seek(0x94, SeekOrigin.Begin);
for (int i = 0; i < max; i++)
binWriter.Write((UInt16)actionList[listOffset + i].worldMasterTextId);
binWriter.Seek(0xE8, SeekOrigin.Begin);
binWriter.Seek(0xB8, SeekOrigin.Begin);
for (int i = 0; i < max; i++)
binWriter.Write((UInt32)actionList[listOffset + i].effectId);
binWriter.Seek(0x130, SeekOrigin.Begin);
binWriter.Seek(0x100, SeekOrigin.Begin);
for (int i = 0; i < max; i++)
binWriter.Write((Byte)actionList[listOffset + i].param);
binWriter.Seek(0x142, SeekOrigin.Begin);
binWriter.Seek(0x112, SeekOrigin.Begin);
for (int i = 0; i < max; i++)
binWriter.Write((Byte)actionList[listOffset + i].unknown);
listOffset += max;
}
}
}
return new SubPacket(OPCODE, sourceActorId, data);
}