Core/AI: Clean up charm AI handling.

Port From (https://github.com/TrinityCore/TrinityCore/commit/e4e8c1c59c8b37216814526b4d2551f23934f465)
This commit is contained in:
hondacrx
2021-10-31 13:55:26 -04:00
parent 7050fda482
commit 26940fa650
33 changed files with 472 additions and 443 deletions
+31 -54
View File
@@ -58,13 +58,6 @@ namespace Game.Entities
_currentWaypointNodeInfo = new();
}
public override void Dispose()
{
i_AI = null;
base.Dispose();
}
public override void AddToWorld()
{
// Register the creature for guid lookup
@@ -170,8 +163,9 @@ namespace Game.Entities
//DestroyForNearbyPlayers(); // old UpdateObjectVisibility()
loot.Clear();
uint respawnDelay = m_respawnDelay;
if (IsAIEnabled)
GetAI().CorpseRemoved(respawnDelay);
CreatureAI ai = GetAI();
if (ai != null)
ai.CorpseRemoved(respawnDelay);
if (destroyForNearbyPlayers)
DestroyForNearbyPlayers();
@@ -429,7 +423,7 @@ namespace Game.Entities
public override void Update(uint diff)
{
if (IsAIEnabled && triggerJustAppeared && m_deathState != DeathState.Dead)
if (IsAIEnabled() && triggerJustAppeared && m_deathState != DeathState.Dead)
{
if (m_respawnCompatibilityMode && VehicleKit != null)
VehicleKit.Reset();
@@ -541,24 +535,8 @@ namespace Game.Entities
m_shouldReacquireTarget = false;
}
// if creature is charmed, switch to charmed AI (and back)
if (NeedChangeAI)
{
UpdateCharmAI();
NeedChangeAI = false;
IsAIEnabled = true;
if (!IsInEvadeMode() && !LastCharmerGUID.IsEmpty())
{
Unit charmer = Global.ObjAccessor.GetUnit(this, LastCharmerGUID);
if (charmer)
EngageWithTarget(charmer);
}
LastCharmerGUID.Clear();
}
// periodic check to see if the creature has passed an evade boundary
if (IsAIEnabled && !IsInEvadeMode() && IsEngaged())
if (IsAIEnabled() && !IsInEvadeMode() && IsEngaged())
{
if (diff >= m_boundaryCheckTime)
{
@@ -593,13 +571,10 @@ namespace Game.Entities
}
}
if (!IsInEvadeMode() && IsAIEnabled)
{
// do not allow the AI to be changed during update
m_AI_locked = true;
i_AI.UpdateAI(diff);
m_AI_locked = false;
}
// do not allow the AI to be changed during update
m_AI_locked = true;
base.AIUpdateTick(diff);
m_AI_locked = false;
if (!IsAlive())
break;
@@ -633,8 +608,11 @@ namespace Game.Entities
{
m_cannotReachTimer += diff;
if (m_cannotReachTimer >= SharedConst.CreatureNoPathEvadeTime)
if (IsAIEnabled)
GetAI().EnterEvadeMode(EvadeReason.NoPath);
{
CreatureAI ai = GetAI();
if (ai != null)
ai.EnterEvadeMode(EvadeReason.NoPath);
}
}
break;
}
@@ -747,19 +725,15 @@ namespace Game.Entities
}
}
bool AIDestory()
bool DestoryAI()
{
if (m_AI_locked)
{
Log.outDebug(LogFilter.Scripts, "AIM_Destroy: failed to destroy, locked.");
Log.outDebug(LogFilter.Scripts, "DestroyAI: failed to destroy, locked.");
return false;
}
Cypher.Assert(i_disabledAI == null, "The disabled AI wasn't cleared!");
i_AI = null;
IsAIEnabled = false;
SetAI(null);
return true;
}
@@ -772,14 +746,15 @@ namespace Game.Entities
return false;
}
AIDestory();
if (ai == null)
ai = AISelector.SelectAI(this);
SetAI(ai);
InitializeMovementAI();
i_AI = ai ?? AISelector.SelectAI(this);
IsAIEnabled = true;
i_AI.InitializeAI();
// Initialize vehicle
if (GetVehicleKit() != null)
GetVehicleKit().Reset();
@@ -1108,10 +1083,11 @@ namespace Game.Entities
public bool IsEscortNPC(bool onlyIfActive = true)
{
if (!IsAIEnabled)
return false;
CreatureAI ai = GetAI();
if (ai != null)
return ai.IsEscortNPC(onlyIfActive);
return GetAI().IsEscortNPC(onlyIfActive);
return false;
}
public override bool IsMovementPreventedByCasting()
@@ -1695,7 +1671,7 @@ namespace Game.Entities
public override bool CanAlwaysSee(WorldObject obj)
{
if (IsAIEnabled && GetAI<CreatureAI>().CanSeeAlways(obj))
if (IsAIEnabled() && GetAI<CreatureAI>().CanSeeAlways(obj))
return true;
return false;
@@ -1936,8 +1912,9 @@ namespace Game.Entities
//Re-initialize reactstate that could be altered by movementgenerators
InitializeReactState();
if (IsAIEnabled) // reset the AI to be sure no dirty or uninitialized values will be used till next tick
GetAI().Reset();
UnitAI ai = GetAI();
if (ai != null) // reset the AI to be sure no dirty or uninitialized values will be used till next tick
ai.Reset();
triggerJustAppeared = true;
@@ -2299,7 +2276,7 @@ namespace Game.Entities
if (!victim.IsInAccessiblePlaceFor(this))
return false;
if (IsAIEnabled && !GetAI().CanAIAttack(victim))
if (IsAIEnabled() && !GetAI().CanAIAttack(victim))
return false;
// we cannot attack in evade mode
+2 -8
View File
@@ -390,14 +390,7 @@ namespace Game.Entities
aura.SetDuration(aura.GetSpellInfo().GetMaxDuration());
}
if (IsAIEnabled && GetAI() != null)
GetAI().UpdateAI(diff);
else if (NeedChangeAI)
{
UpdateCharmAI();
NeedChangeAI = false;
IsAIEnabled = GetAI() != null;
}
AIUpdateTick(diff);
// Update items that have just a limited lifetime
if (now > m_Last_tick)
@@ -5175,6 +5168,7 @@ namespace Game.Entities
if (guildId != 0)
{
SetUpdateFieldValue(m_values.ModifyValue(m_unitData).ModifyValue(m_unitData.GuildGUID), ObjectGuid.Create(HighGuid.Guild, guildId));
SetUpdateFieldValue(m_values.ModifyValue(m_activePlayerData).ModifyValue(m_activePlayerData.GuildClubMemberID), GetGUID().GetCounter());
AddPlayerFlag(PlayerFlags.GuildLevelEnabled);
}
else
+3 -3
View File
@@ -207,9 +207,9 @@ namespace Game.Entities
Unit owner = GetSummoner();
if (owner != null)
{
if (owner.IsTypeId(TypeId.Unit) && owner.ToCreature().IsAIEnabled)
if (owner.IsTypeId(TypeId.Unit) && owner.ToCreature().IsAIEnabled())
owner.ToCreature().GetAI().JustSummoned(this);
if (IsAIEnabled)
if (IsAIEnabled())
GetAI().IsSummonedBy(owner);
}
}
@@ -243,7 +243,7 @@ namespace Game.Entities
}
Unit owner = GetSummoner();
if (owner != null && owner.IsTypeId(TypeId.Unit) && owner.ToCreature().IsAIEnabled)
if (owner != null && owner.IsTypeId(TypeId.Unit) && owner.ToCreature().IsAIEnabled())
owner.ToCreature().GetAI().SummonedCreatureDespawn(this);
AddObjectToRemoveList();
+12 -8
View File
@@ -16,10 +16,10 @@
*/
using Framework.Constants;
using Game.AI;
using Game.BattleFields;
using Game.BattleGrounds;
using Game.Combat;
using Game.DataStorage;
using Game.Loots;
using Game.Maps;
using Game.Networking.Packets;
@@ -354,8 +354,11 @@ namespace Game.Entities
{
Creature cControlled = controlled.ToCreature();
if (cControlled != null)
if (cControlled.IsAIEnabled)
cControlled.GetAI().OwnerAttacked(victim);
{
CreatureAI controlledAI = cControlled.GetAI();
if (controlledAI != null)
controlledAI.OwnerAttacked(victim);
}
}
}
return true;
@@ -880,7 +883,7 @@ namespace Game.Entities
plrVictim.SendDurabilityLoss(plrVictim, loss);
}
// Call KilledUnit for creatures
if (attacker != null && attacker.IsCreature() && attacker.IsAIEnabled)
if (attacker != null && attacker.IsCreature() && attacker.IsAIEnabled())
attacker.ToCreature().GetAI().KilledUnit(victim);
// last damage from non duel opponent or opponent controlled creature
@@ -907,19 +910,20 @@ namespace Game.Entities
}
// Call KilledUnit for creatures, this needs to be called after the lootable flag is set
if (attacker != null && attacker.IsCreature() && attacker.IsAIEnabled)
if (attacker != null && attacker.IsCreature() && attacker.IsAIEnabled())
attacker.ToCreature().GetAI().KilledUnit(victim);
// Call creature just died function
if (creature.IsAIEnabled)
creature.GetAI().JustDied(attacker);
CreatureAI ai = creature.GetAI();
if (ai != null)
ai.JustDied(attacker);
TempSummon summon = creature.ToTempSummon();
if (summon != null)
{
Unit summoner = summon.GetSummoner();
if (summoner != null)
if (summoner.IsTypeId(TypeId.Unit) && summoner.IsAIEnabled)
if (summoner.IsTypeId(TypeId.Unit) && summoner.IsAIEnabled())
summoner.ToCreature().GetAI().SummonedCreatureDies(creature, attacker);
}
-2
View File
@@ -36,8 +36,6 @@ namespace Game.Entities
//AI
protected UnitAI i_AI;
protected UnitAI i_disabledAI;
public bool IsAIEnabled { get; set; }
public bool NeedChangeAI { get; set; }
//Movement
protected float[] m_speed_rate = new float[(int)UnitMoveType.Max];
+53 -97
View File
@@ -48,83 +48,48 @@ namespace Game.Entities
public void UpdateCharmAI()
{
switch (GetTypeId())
if (IsCharmed())
{
case TypeId.Unit:
if (i_disabledAI != null) // disabled AI must be primary AI
UnitAI newAI = null;
if (IsPlayer())
{
Unit charmer = GetCharmer();
if (charmer != null)
{
if (!IsCharmed())
// first, we check if the creature's own AI specifies an override playerai for its owned players
Creature creatureCharmer = charmer.ToCreature();
if (creatureCharmer != null)
{
i_AI = i_disabledAI;
i_disabledAI = null;
if (IsTypeId(TypeId.Unit))
ToCreature().GetAI().OnCharmed(false);
}
}
else
{
if (IsCharmed())
{
i_disabledAI = i_AI;
if (IsPossessed() || IsVehicle())
i_AI = new PossessedAI(ToCreature());
else
i_AI = new PetAI(ToCreature());
}
}
break;
case TypeId.Player:
{
if (IsCharmed()) // if we are currently being charmed, then we should apply charm AI
{
i_disabledAI = i_AI;
UnitAI newAI = null;
// first, we check if the creature's own AI specifies an override playerai for its owned players
Unit charmer = GetCharmer();
if (charmer)
{
Creature creatureCharmer = charmer.ToCreature();
if (creatureCharmer)
{
PlayerAI charmAI = creatureCharmer.IsAIEnabled ? creatureCharmer.GetAI().GetAIForCharmedPlayer(ToPlayer()) : null;
if (charmAI != null)
newAI = charmAI;
}
else
{
Log.outError(LogFilter.Misc, "Attempt to assign charm AI to player {0} who is charmed by non-creature {1}.", GetGUID().ToString(), GetCharmerGUID().ToString());
}
}
if (newAI == null) // otherwise, we default to the generic one
newAI = new SimpleCharmedPlayerAI(ToPlayer());
i_AI = newAI;
newAI.OnCharmed(true);
CreatureAI charmerAI = creatureCharmer.GetAI();
if (charmerAI != null)
newAI = charmerAI.GetAIForCharmedPlayer(ToPlayer());
}
else
{
if (i_AI != null)
{
// we allow the charmed PlayerAI to clean up
i_AI.OnCharmed(false);
}
else
{
Log.outError(LogFilter.Misc, "Attempt to remove charm AI from player {0} who doesn't currently have charm AI.", GetGUID().ToString());
}
// and restore our previous PlayerAI (if we had one)
i_AI = i_disabledAI;
i_disabledAI = null;
// IsAIEnabled gets handled in the caller
}
break;
Log.outError(LogFilter.Misc, $"Attempt to assign charm AI to player {GetGUID()} who is charmed by non-creature {GetCharmerGUID()}.");
}
default:
Log.outError(LogFilter.Misc, "Attempt to update charm AI for unit {0}, which is neither player nor creature.", GetGUID().ToString());
break;
}
if (newAI == null) // otherwise, we default to the generic one
newAI = new SimpleCharmedPlayerAI(ToPlayer());
}
else
{
Cypher.Assert(IsCreature());
if (IsPossessed() || IsVehicle())
newAI = new PossessedAI(ToCreature());
else
newAI = new PetAI(ToCreature());
}
Cypher.Assert(newAI != null);
i_AI = newAI;
newAI.OnCharmed(true);
AIUpdateTick(0, true);
}
else
{
RestoreDisabledAI();
if (i_AI != null)
i_AI.OnCharmed(true);
}
}
public void SetMinion(Minion minion, bool apply)
@@ -365,7 +330,12 @@ namespace Game.Entities
StopMoving();
ToCreature().GetAI().OnCharmed(true);
// AI will schedule its own change if appropriate
UnitAI ai = GetAI();
if (ai != null)
ai.OnCharmed(false);
else
ScheduleAIChange();
}
else
{
@@ -378,12 +348,11 @@ namespace Game.Entities
if (charmer.IsTypeId(TypeId.Unit)) // we are charmed by a creature
{
// change AI to charmed AI on next Update tick
NeedChangeAI = true;
if (IsAIEnabled)
{
IsAIEnabled = false;
player.GetAI().OnCharmed(true);
}
UnitAI ai = GetAI();
if (ai != null)
ai.OnCharmed(false);
else
player.ScheduleAIChange();
}
player.SetClientControl(this, false);
@@ -482,17 +451,9 @@ namespace Game.Entities
///@todo Handle SLOT_IDLE motion resume
GetMotionMaster().InitializeDefault();
Creature creature = ToCreature();
if (creature)
{
// Creature will restore its old AI on next update
if (creature.GetAI() != null)
creature.GetAI().OnCharmed(false);
// Vehicle should not attack its passenger after he exists the seat
if (type != CharmType.Vehicle)
LastCharmerGUID = charmer ? charmer.GetGUID() : ObjectGuid.Empty;
}
// Vehicle should not attack its passenger after he exists the seat
if (type != CharmType.Vehicle)
LastCharmerGUID = charmer.GetGUID();
// If charmer still exists
if (!charmer)
@@ -539,17 +500,12 @@ namespace Game.Entities
}
}
Player player = ToPlayer();
if (player)
{
if (charmer.IsTypeId(TypeId.Unit)) // charmed by a creature, this means we had PlayerAI
{
NeedChangeAI = true;
IsAIEnabled = false;
}
if (!IsPlayer() || charmer.IsCreature())
GetAI().OnCharmed(false); // AI will potentially schedule a charm ai update
Player player = ToPlayer();
if (player != null)
player.SetClientControl(this, true);
}
// a guardian should always have charminfo
if (playerCharmer && this != charmer.GetFirstControlled())
+9 -9
View File
@@ -17,6 +17,7 @@
using Framework.Constants;
using Framework.Dynamic;
using Game.AI;
using Game.BattleGrounds;
using Game.Networking.Packets;
using Game.Spells;
@@ -1801,14 +1802,13 @@ namespace Game.Entities
Unit victim = healInfo.GetTarget();
uint addhealth = healInfo.GetHeal();
if (healer)
{
if (victim.IsAIEnabled)
victim.GetAI().HealReceived(healer, addhealth);
UnitAI victimAI = victim.GetAI();
if (victimAI != null)
victimAI.HealReceived(healer, addhealth);
if (healer.IsAIEnabled)
healer.GetAI().HealDone(victim, addhealth);
}
UnitAI healerAI = healer != null ? healer.GetAI() : null;
if (healerAI != null)
healerAI.HealDone(victim, addhealth);
if (addhealth != 0)
gain = (int)victim.ModifyHealth(addhealth);
@@ -2424,7 +2424,7 @@ namespace Game.Entities
spell.SetReferencedFromCurrent(false);
}
if (IsCreature() && IsAIEnabled)
if (IsCreature() && IsAIEnabled())
ToCreature().GetAI().OnSpellCastInterrupt(spell.GetSpellInfo());
}
}
@@ -2595,7 +2595,7 @@ namespace Game.Entities
}
Creature creature = ToCreature();
if (creature && creature.IsAIEnabled)
if (creature && creature.IsAIEnabled())
creature.GetAI().OnSpellClick(clicker, ref spellClickHandled);
}
+67 -15
View File
@@ -172,6 +172,9 @@ namespace Game.Entities
UpdateSplineMovement(diff);
GetMotionMaster().Update(diff);
if (i_AI == null && (!IsPlayer() || IsCharmed()))
UpdateCharmAI();
}
void _UpdateSpells(uint diff)
@@ -438,8 +441,9 @@ namespace Game.Entities
if (IsInWorld)
{
m_duringRemoveFromWorld = true;
if (IsAIEnabled)
GetAI().LeavingWorld();
UnitAI ai = GetAI();
if (ai != null)
ai.LeavingWorld();
if (IsVehicle())
RemoveVehicleKit(true);
@@ -537,14 +541,14 @@ namespace Game.Entities
public void _RegisterDynObject(DynamicObject dynObj)
{
m_dynObj.Add(dynObj);
if (IsTypeId(TypeId.Unit) && IsAIEnabled)
if (IsTypeId(TypeId.Unit) && IsAIEnabled())
ToCreature().GetAI().JustRegisteredDynObject(dynObj);
}
public void _UnregisterDynObject(DynamicObject dynObj)
{
m_dynObj.Remove(dynObj);
if (IsTypeId(TypeId.Unit) && IsAIEnabled)
if (IsTypeId(TypeId.Unit) && IsAIEnabled())
ToCreature().GetAI().JustUnregisteredDynObject(dynObj);
}
@@ -611,7 +615,7 @@ namespace Game.Entities
GetSpellHistory().StartCooldown(createBySpell, 0, null, true);
}
if (IsTypeId(TypeId.Unit) && ToCreature().IsAIEnabled)
if (IsTypeId(TypeId.Unit) && ToCreature().IsAIEnabled())
ToCreature().GetAI().JustSummonedGameobject(gameObj);
}
@@ -646,7 +650,7 @@ namespace Game.Entities
m_gameObj.Remove(gameObj);
if (IsTypeId(TypeId.Unit) && ToCreature().IsAIEnabled)
if (IsTypeId(TypeId.Unit) && ToCreature().IsAIEnabled())
ToCreature().GetAI().SummonedGameobjectDespawn(gameObj);
if (del)
@@ -694,14 +698,14 @@ namespace Game.Entities
public void _RegisterAreaTrigger(AreaTrigger areaTrigger)
{
m_areaTrigger.Add(areaTrigger);
if (IsTypeId(TypeId.Unit) && IsAIEnabled)
if (IsTypeId(TypeId.Unit) && IsAIEnabled())
ToCreature().GetAI().JustRegisteredAreaTrigger(areaTrigger);
}
public void _UnregisterAreaTrigger(AreaTrigger areaTrigger)
{
m_areaTrigger.Remove(areaTrigger);
if (IsTypeId(TypeId.Unit) && IsAIEnabled)
if (IsTypeId(TypeId.Unit) && IsAIEnabled())
ToCreature().GetAI().JustUnregisteredAreaTrigger(areaTrigger);
}
@@ -1160,8 +1164,51 @@ namespace Game.Entities
return m_vehicle != null && m_vehicle == vehicle.GetVehicleKit();
}
public bool IsAIEnabled() { return i_AI != null; }
public virtual UnitAI GetAI() { return i_AI; }
public void SetAI(UnitAI newAI) { i_AI = newAI; }
public void AIUpdateTick(uint diff, bool force = false)
{
if (diff == 0) // some places call with diff = 0, which does nothing (for now), see PR #22296
return;
UnitAI ai = GetAI();
if (ai != null)
ai.UpdateAI(diff);
}
public void SetAI(UnitAI newAI)
{
if (i_AI != null)
AIUpdateTick(0, true); // old AI gets a final tick if enabled
i_AI = newAI;
AIUpdateTick(0, true); // new AI gets its initial tick
}
public void ScheduleAIChange()
{
bool charmed = IsCharmed();
// if charm is applied, we can't have disabled AI already, and vice versa
if (charmed)
Cypher.Assert(i_disabledAI == null, "Attempt to schedule charm AI change on unit that already has disabled AI");
else if (!IsPlayer())
Cypher.Assert(i_disabledAI != null, "Attempt to schedule charm ID change on unit that doesn't have disabled AI");
if (charmed)
i_disabledAI = i_AI;
else
i_AI = null;
}
void RestoreDisabledAI()
{
Cypher.Assert(IsPlayer() || i_disabledAI != null, "Attempt to restore disabled AI on creature without disabled AI");
i_AI = i_disabledAI;
AIUpdateTick(0, true);
}
public bool IsPossessing()
{
@@ -2270,11 +2317,13 @@ namespace Game.Entities
public static uint DealDamage(Unit attacker, Unit victim, uint damage, CleanDamage cleanDamage = null, DamageEffectType damagetype = DamageEffectType.Direct, SpellSchoolMask damageSchoolMask = SpellSchoolMask.Normal, SpellInfo spellProto = null, bool durabilityLoss = true)
{
if (victim.IsAIEnabled)
victim.GetAI().DamageTaken(attacker, ref damage);
UnitAI victimAI = victim.GetAI();
if (victimAI != null)
victimAI.DamageTaken(attacker, ref damage);
if (attacker != null && attacker.IsAIEnabled)
attacker.GetAI().DamageDealt(victim, ref damage, damagetype);
UnitAI attackerAI = attacker ? attacker.GetAI() : null;
if (attackerAI != null)
attackerAI.DamageDealt(victim, ref damage, damagetype);
// Hook for OnDamage Event
Global.ScriptMgr.OnDamage(attacker, victim, ref damage);
@@ -2288,8 +2337,11 @@ namespace Game.Entities
{
Creature cControlled = controlled.ToCreature();
if (cControlled != null)
if (cControlled.IsAIEnabled)
cControlled.GetAI().OwnerAttackedBy(attacker);
{
CreatureAI controlledAI = cControlled.GetAI();
if (controlledAI != null)
controlledAI.OwnerAttackedBy(attacker);
}
}
}
+5 -3
View File
@@ -17,6 +17,7 @@
using Framework.Constants;
using Framework.Dynamic;
using Game.AI;
using Game.BattleGrounds;
using Game.DataStorage;
using Game.Movement;
@@ -361,7 +362,7 @@ namespace Game.Entities
if (unit.IsFlying())
_me.CastSpell(unit, SharedConst.VehicleSpellParachute, true);
if (_me.IsTypeId(TypeId.Unit) && _me.ToCreature().IsAIEnabled)
if (_me.IsTypeId(TypeId.Unit) && _me.ToCreature().IsAIEnabled())
_me.ToCreature().GetAI().PassengerBoarded(unit, seat.Key, false);
if (GetBase().IsTypeId(TypeId.Unit))
@@ -646,8 +647,9 @@ namespace Game.Entities
Creature creature = Target.GetBase().ToCreature();
if (creature != null)
{
if (creature.IsAIEnabled)
creature.GetAI().PassengerBoarded(Passenger, Seat.Key, true);
CreatureAI ai = creature.GetAI();
if (ai != null)
ai.PassengerBoarded(Passenger, Seat.Key, true);
Global.ScriptMgr.OnAddPassenger(Target, Passenger, Seat.Key);