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

@@ -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)