Core/AI: Clean up charm AI handling.
Port From (https://github.com/TrinityCore/TrinityCore/commit/e4e8c1c59c8b37216814526b4d2551f23934f465)
This commit is contained in:
@@ -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);
|
||||
}
|
||||
|
||||
|
||||
@@ -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];
|
||||
|
||||
@@ -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())
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user