Core/Spells: Implement using different difficulty data from all spell related db2s, not just SpellEffect and SpellPower

Port From (https://github.com/TrinityCore/TrinityCore/commit/c7306439e7004288fb85890d6a5f730cf1761d71)
This commit is contained in:
hondacrx
2020-06-18 12:39:39 -04:00
parent f0942a257e
commit d7954f4fc7
89 changed files with 1738 additions and 1893 deletions
+27 -17
View File
@@ -28,7 +28,7 @@ namespace Game.AI
public override void InitializeAI()
{
for (var i = 0; i < SharedConst.MaxCreatureSpells; ++i)
if (me.m_spells[i] != 0 && Global.SpellMgr.GetSpellInfo(me.m_spells[i]) != null)
if (me.m_spells[i] != 0 && Global.SpellMgr.HasSpellInfo(me.m_spells[i], me.GetMap().GetDifficultyID()))
spells.Add(me.m_spells[i]);
base.InitializeAI();
@@ -42,18 +42,22 @@ namespace Game.AI
public override void JustDied(Unit killer)
{
foreach (var id in spells)
if (AISpellInfo[id].condition == AICondition.Die)
{
AISpellInfoType info = GetAISpellInfo(id, me.GetMap().GetDifficultyID());
if (info.condition == AICondition.Die)
me.CastSpell(killer, id, true);
}
}
public override void EnterCombat(Unit victim)
{
foreach (var id in spells)
{
if (AISpellInfo[id].condition == AICondition.Aggro)
AISpellInfoType info = GetAISpellInfo(id, me.GetMap().GetDifficultyID());
if (info.condition == AICondition.Aggro)
me.CastSpell(victim, id, false);
else if (AISpellInfo[id].condition == AICondition.Combat)
_events.ScheduleEvent(id, AISpellInfo[id].cooldown + RandomHelper.Rand32() % AISpellInfo[id].cooldown);
else if (info.condition == AICondition.Combat)
_events.ScheduleEvent(id, info.cooldown + RandomHelper.Rand32() % info.cooldown);
}
}
@@ -71,10 +75,11 @@ namespace Game.AI
if (spellId != 0)
{
DoCast(spellId);
_events.ScheduleEvent(spellId, AISpellInfo[spellId].cooldown + RandomHelper.Rand32() % AISpellInfo[spellId].cooldown);
AISpellInfoType info = GetAISpellInfo(spellId, me.GetMap().GetDifficultyID());
_events.ScheduleEvent(spellId, info.cooldown + RandomHelper.Rand32() % info.cooldown);
}
DoMeleeAttackIfReady();
else
DoMeleeAttackIfReady();
}
public override void SpellInterrupted(uint spellId, uint unTimeMs)
@@ -112,8 +117,12 @@ namespace Game.AI
m_attackDist = 30.0f;
foreach (var id in spells)
if (AISpellInfo[id].condition == AICondition.Combat && m_attackDist > AISpellInfo[id].maxRange)
m_attackDist = AISpellInfo[id].maxRange;
{
AISpellInfoType info = GetAISpellInfo(id, me.GetMap().GetDifficultyID());
if (info.condition == AICondition.Combat && m_attackDist > info.maxRange)
m_attackDist = info.maxRange;
}
if (m_attackDist == 30.0f)
m_attackDist = SharedConst.MeleeRange;
}
@@ -132,12 +141,12 @@ namespace Game.AI
uint count = 0;
foreach (var id in spells)
{
if (AISpellInfo[id].condition == AICondition.Aggro)
AISpellInfoType info = GetAISpellInfo(id, me.GetMap().GetDifficultyID());
if (info.condition == AICondition.Aggro)
me.CastSpell(victim, id, false);
else if (AISpellInfo[id].condition == AICondition.Combat)
else if (info.condition == AICondition.Combat)
{
uint cooldown = AISpellInfo[id].realCooldown;
uint cooldown = info.realCooldown;
if (count == spell)
{
DoCast(spells[spell]);
@@ -169,7 +178,8 @@ namespace Game.AI
{
DoCast(spellId);
uint casttime = (uint)me.GetCurrentSpellCastTime(spellId);
_events.ScheduleEvent(spellId, (casttime != 0 ? casttime : 500) + AISpellInfo[spellId].realCooldown);
AISpellInfoType info = GetAISpellInfo(spellId, me.GetMap().GetDifficultyID());
_events.ScheduleEvent(spellId, (casttime != 0 ? casttime : 500) + info.realCooldown);
}
}
@@ -184,7 +194,7 @@ namespace Game.AI
if (me.m_spells[0] == 0)
Log.outError(LogFilter.ScriptsAi, "ArcherAI set for creature (entry = {0}) with spell1=0. AI will do nothing", me.GetEntry());
var spellInfo = Global.SpellMgr.GetSpellInfo(me.m_spells[0]);
var spellInfo = Global.SpellMgr.GetSpellInfo(me.m_spells[0], me.GetMap().GetDifficultyID());
m_minRange = spellInfo != null ? spellInfo.GetMinRange(false) : 0;
if (m_minRange == 0)
@@ -234,7 +244,7 @@ namespace Game.AI
if (me.m_spells[0] == 0)
Log.outError(LogFilter.Server, "TurretAI set for creature (entry = {0}) with spell1=0. AI will do nothing", me.GetEntry());
var spellInfo = Global.SpellMgr.GetSpellInfo(me.m_spells[0]);
var spellInfo = Global.SpellMgr.GetSpellInfo(me.m_spells[0], me.GetMap().GetDifficultyID());
m_minRange = spellInfo != null ? spellInfo.GetMinRange(false) : 0;
me.m_CombatDistance = spellInfo != null ? spellInfo.GetMaxRange(false) : 0;
me.m_SightDistance = me.m_CombatDistance;
+3
View File
@@ -492,6 +492,9 @@ namespace Game.AI
public uint cooldown;
public uint realCooldown;
public float maxRange;
public byte Targets; // set of enum SelectTarget
public byte Effects; // set of enum SelectEffect
}
public enum AITarget
+1 -1
View File
@@ -138,7 +138,7 @@ namespace Game.AI
if (spellID == 0)
continue;
SpellInfo spellInfo = Global.SpellMgr.GetSpellInfo(spellID);
SpellInfo spellInfo = Global.SpellMgr.GetSpellInfo(spellID, me.GetMap().GetDifficultyID());
if (spellInfo == null)
continue;
+1 -1
View File
@@ -42,7 +42,7 @@ namespace Game.AI
return;
// Search spell
var spellInfo = Global.SpellMgr.GetSpellInfo(me.ToTotem().GetSpell());
var spellInfo = Global.SpellMgr.GetSpellInfo(me.ToTotem().GetSpell(), me.GetMap().GetDifficultyID());
if (spellInfo == null)
return;
+105 -25
View File
@@ -31,7 +31,7 @@ namespace Game.AI
{
me = _unit;
}
public virtual void AttackStart(Unit victim)
{
if (victim != null && me.Attack(victim, true))
@@ -99,7 +99,7 @@ namespace Game.AI
if (me.HasUnitState(UnitState.Casting) || !me.IsAttackReady())
return true;
var spellInfo = Global.SpellMgr.GetSpellInfo(spell);
var spellInfo = Global.SpellMgr.GetSpellInfo(spell, me.GetMap().GetDifficultyID());
if (spellInfo != null)
{
if (me.IsWithinCombatRange(me.GetVictim(), spellInfo.GetMaxRange(false)))
@@ -156,18 +156,18 @@ namespace Game.AI
{
case SelectAggroTarget.Nearest:
case SelectAggroTarget.TopAggro:
{
return targetList.First();
}
{
return targetList.First();
}
case SelectAggroTarget.Farthest:
case SelectAggroTarget.BottomAggro:
{
return targetList.Last();
}
{
return targetList.Last();
}
case SelectAggroTarget.Random:
{
return targetList.SelectRandom();
}
{
return targetList.SelectRandom();
}
default:
break;
}
@@ -179,7 +179,7 @@ namespace Game.AI
{
return SelectTargetList(new DefaultTargetSelector(me, dist, playerOnly, aura), num, targetType);
}
// Select the targets satifying the predicate.
// predicate shall extend std.unary_function<Unit*, bool>
public List<Unit> SelectTargetList(ISelector selector, uint maxTargets, SelectAggroTarget targetType)
@@ -215,7 +215,8 @@ namespace Game.AI
{
Unit target = null;
switch (AISpellInfo[spellId].target)
AISpellInfoType info = GetAISpellInfo(spellId, me.GetMap().GetDifficultyID());
switch (info.target)
{
default:
case AITarget.Self:
@@ -226,7 +227,7 @@ namespace Game.AI
break;
case AITarget.Enemy:
{
var spellInfo = Global.SpellMgr.GetSpellInfo(spellId);
var spellInfo = Global.SpellMgr.GetSpellInfo(spellId, me.GetMap().GetDifficultyID());
if (spellInfo != null)
{
bool playerOnly = spellInfo.HasAttribute(SpellAttr3.OnlyTargetPlayers);
@@ -240,7 +241,7 @@ namespace Game.AI
break;
case AITarget.Debuff:
{
var spellInfo = Global.SpellMgr.GetSpellInfo(spellId);
var spellInfo = Global.SpellMgr.GetSpellInfo(spellId, me.GetMap().GetDifficultyID());
if (spellInfo != null)
{
bool playerOnly = spellInfo.HasAttribute(SpellAttr3.OnlyTargetPlayers);
@@ -289,12 +290,11 @@ namespace Game.AI
public static void FillAISpellInfo()
{
var spellStorage = Global.SpellMgr.GetSpellInfoStorage();
AISpellInfo = new AISpellInfoType[spellStorage.Keys.Max() + 1];
//AISpellInfo = new AISpellInfoType[spellStorage.Keys.Max() + 1];
foreach (var spellInfo in spellStorage.Values)
Global.SpellMgr.ForEachSpellInfo(spellInfo =>
{
AISpellInfoType AIInfo = AISpellInfo[spellInfo.Id];
AISpellInfoType AIInfo = new AISpellInfoType();
if (spellInfo.HasAttribute(SpellAttr0.CastableWhileDead))
AIInfo.condition = AICondition.Die;
else if (spellInfo.IsPassive() || spellInfo.GetDuration() == -1)
@@ -312,14 +312,14 @@ namespace Game.AI
}
else
{
foreach (SpellEffectInfo effect in spellInfo.GetEffectsForDifficulty(Difficulty.None))
foreach (SpellEffectInfo effect in spellInfo.GetEffects())
{
if (effect == null)
continue;
var targetType = effect.TargetA.GetTarget();
if (targetType == Targets.UnitEnemy || targetType == Targets.DestEnemy)
if (targetType == Targets.UnitTargetEnemy || targetType == Targets.DestTargetEnemy)
{
if (AIInfo.target < AITarget.Victim)
AIInfo.target = AITarget.Victim;
@@ -332,7 +332,7 @@ namespace Game.AI
if (effect.Effect == SpellEffectName.ApplyAura)
{
if (targetType == Targets.UnitEnemy)
if (targetType == Targets.UnitTargetEnemy)
{
if (AIInfo.target < AITarget.Debuff)
AIInfo.target = AITarget.Debuff;
@@ -347,7 +347,82 @@ namespace Game.AI
}
AIInfo.realCooldown = spellInfo.RecoveryTime + spellInfo.StartRecoveryTime;
AIInfo.maxRange = spellInfo.GetMaxRange(false) * 3 / 4;
}
AIInfo.Effects = 0;
AIInfo.Targets = 0;
foreach (SpellEffectInfo effect in spellInfo.GetEffects())
{
if (effect == null)
continue;
// Spell targets self.
if (effect.TargetA.GetTarget() == Targets.UnitCaster)
AIInfo.Targets |= 1 << ((int)SelectTargetType.Self - 1);
// Spell targets a single enemy.
if (effect.TargetA.GetTarget() == Targets.UnitTargetEnemy ||
effect.TargetA.GetTarget() == Targets.DestTargetEnemy)
AIInfo.Targets |= 1 << ((int)SelectTargetType.SingleEnemy - 1);
// Spell targets AoE at enemy.
if (effect.TargetA.GetTarget() == Targets.UnitSrcAreaEnemy ||
effect.TargetA.GetTarget() == Targets.UnitDestAreaEnemy ||
effect.TargetA.GetTarget() == Targets.SrcCaster ||
effect.TargetA.GetTarget() == Targets.DestDynobjEnemy)
AIInfo.Targets |= 1 << ((int)SelectTargetType.AoeEnemy - 1);
// Spell targets an enemy.
if (effect.TargetA.GetTarget() == Targets.UnitTargetEnemy ||
effect.TargetA.GetTarget() == Targets.DestTargetEnemy ||
effect.TargetA.GetTarget() == Targets.UnitSrcAreaEnemy ||
effect.TargetA.GetTarget() == Targets.UnitDestAreaEnemy ||
effect.TargetA.GetTarget() == Targets.SrcCaster ||
effect.TargetA.GetTarget() == Targets.DestDynobjEnemy)
AIInfo.Targets |= 1 << ((int)SelectTargetType.AnyEnemy - 1);
// Spell targets a single friend (or self).
if (effect.TargetA.GetTarget() == Targets.UnitCaster ||
effect.TargetA.GetTarget() == Targets.UnitTargetAlly ||
effect.TargetA.GetTarget() == Targets.UnitTargetParty)
AIInfo.Targets |= 1 << ((int)SelectTargetType.SingleFriend - 1);
// Spell targets AoE friends.
if (effect.TargetA.GetTarget() == Targets.UnitCasterAreaParty ||
effect.TargetA.GetTarget() == Targets.UnitLastTargetAreaParty ||
effect.TargetA.GetTarget() == Targets.SrcCaster)
AIInfo.Targets |= 1 << ((int)SelectTargetType.AoeFriend - 1);
// Spell targets any friend (or self).
if (effect.TargetA.GetTarget() == Targets.UnitCaster ||
effect.TargetA.GetTarget() == Targets.UnitTargetAlly ||
effect.TargetA.GetTarget() == Targets.UnitTargetParty ||
effect.TargetA.GetTarget() == Targets.UnitCasterAreaParty ||
effect.TargetA.GetTarget() == Targets.UnitLastTargetAreaParty ||
effect.TargetA.GetTarget() == Targets.SrcCaster)
AIInfo.Targets |= 1 << ((int)SelectTargetType.AnyFriend - 1);
// Make sure that this spell includes a damage effect.
if (effect.Effect == SpellEffectName.SchoolDamage ||
effect.Effect == SpellEffectName.Instakill ||
effect.Effect == SpellEffectName.EnvironmentalDamage ||
effect.Effect == SpellEffectName.HealthLeech)
AIInfo.Effects |= 1 << ((int)SelectEffect.Damage - 1);
// Make sure that this spell includes a healing effect (or an apply aura with a periodic heal).
if (effect.Effect == SpellEffectName.Heal ||
effect.Effect == SpellEffectName.HealMaxHealth ||
effect.Effect == SpellEffectName.HealMechanical ||
(effect.Effect == SpellEffectName.ApplyAura && effect.ApplyAuraName == AuraType.PeriodicHeal))
AIInfo.Effects |= 1 << ((int)SelectEffect.Healing - 1);
// Make sure that this spell applies an aura.
if (effect.Effect == SpellEffectName.ApplyAura)
AIInfo.Effects |= 1 << ((int)SelectEffect.Aura - 1);
}
AISpellInfo[(spellInfo.Id, spellInfo.Difficulty)] = AIInfo;
});
}
public virtual bool CanAIAttack(Unit victim) { return true; }
@@ -411,7 +486,12 @@ namespace Game.AI
// Called when the dialog status between a player and the creature is requested.
public virtual QuestGiverStatus GetDialogStatus(Player player) { return QuestGiverStatus.ScriptedNoStatus; }
public static AISpellInfoType[] AISpellInfo;
public AISpellInfoType GetAISpellInfo(uint spellId, Difficulty difficulty)
{
return AISpellInfo.LookupByKey((spellId, difficulty));
}
public static Dictionary<(uint id, Difficulty difficulty), AISpellInfoType> AISpellInfo = new Dictionary<(uint id, Difficulty difficulty), AISpellInfoType>();
protected Unit me { get; private set; }
}
@@ -493,7 +573,7 @@ namespace Game.AI
public SpellTargetSelector(Unit caster, uint spellId)
{
_caster = caster;
_spellInfo = Global.SpellMgr.GetSpellInfo(spellId);
_spellInfo = Global.SpellMgr.GetSpellInfo(spellId, caster.GetMap().GetDifficultyID());
Cypher.Assert(_spellInfo != null);
}
+1 -1
View File
@@ -487,7 +487,7 @@ namespace Game.AI
if (knownRank == 0)
return null;
SpellInfo spellInfo = Global.SpellMgr.GetSpellInfo(knownRank);
SpellInfo spellInfo = Global.SpellMgr.GetSpellInfo(knownRank, me.GetMap().GetDifficultyID());
if (spellInfo == null)
return null;
+4 -3
View File
@@ -136,7 +136,8 @@ namespace Game.AI
//Check if each spell is viable(set it to null if not)
for (uint i = 0; i < SharedConst.MaxCreatureSpells; i++)
{
SpellInfo tempSpell = Global.SpellMgr.GetSpellInfo(me.m_spells[i]);
SpellInfo tempSpell = Global.SpellMgr.GetSpellInfo(me.m_spells[i], me.GetMap().GetDifficultyID());
AISpellInfoType aiSpell = GetAISpellInfo(me.m_spells[i], me.GetMap().GetDifficultyID());
//This spell doesn't exist
if (tempSpell == null)
@@ -144,11 +145,11 @@ namespace Game.AI
// Targets and Effects checked first as most used restrictions
//Check the spell targets if specified
if (targets != 0 && !Convert.ToBoolean(Global.ScriptMgr.spellSummaryStorage[me.m_spells[i]].Targets & (1 << ((int)targets - 1))))
if (targets != 0 && !Convert.ToBoolean(aiSpell.Targets & (1 << ((int)targets - 1))))
continue;
//Check the type of spell if we are looking for a specific spell type
if (effect != 0 && !Convert.ToBoolean(Global.ScriptMgr.spellSummaryStorage[me.m_spells[i]].Effects & (1 << ((int)effect - 1))))
if (effect != 0 && !Convert.ToBoolean(aiSpell.Effects & (1 << ((int)effect - 1))))
continue;
//Check for school if specified
+1 -1
View File
@@ -656,7 +656,7 @@ namespace Game.AI
public override void SpellHitTarget(Unit target, SpellInfo spell)
{
GetScript().ProcessEventsFor(SmartEvents.SpellhitTarget, target, 0, 0, false, spell);
GetScript().ProcessEventsFor(SmartEvents.SpellHitTarget, target, 0, 0, false, spell);
}
public override void DamageTaken(Unit attacker, ref uint damage)
+16 -7
View File
@@ -102,7 +102,7 @@ namespace Game.AI
}
case SmartScriptType.Spell:
{
if (!Global.SpellMgr.HasSpellInfo((uint)temp.entryOrGuid))
if (!Global.SpellMgr.HasSpellInfo((uint)temp.entryOrGuid, Difficulty.None))
{
Log.outError(LogFilter.Sql, "SmartAIMgr.LoadSmartAIFromDB: Scene id ({0}) does not exist, skipped loading.", temp.entryOrGuid);
continue;
@@ -505,10 +505,10 @@ namespace Game.AI
return false;
break;
case SmartEvents.SpellHit:
case SmartEvents.SpellhitTarget:
case SmartEvents.SpellHitTarget:
if (e.Event.spellHit.spell != 0)
{
SpellInfo spellInfo = Global.SpellMgr.GetSpellInfo(e.Event.spellHit.spell);
SpellInfo spellInfo = Global.SpellMgr.GetSpellInfo(e.Event.spellHit.spell, Difficulty.None);
if (spellInfo == null)
{
Log.outError(LogFilter.ScriptsAi, "SmartAIMgr: Entry {0} SourceType {1} Event {2} Action {3} uses non-existent Spell entry {4}, skipped.", e.entryOrGuid, e.GetScriptType(), e.event_id, e.GetActionType(), e.Event.spellHit.spell);
@@ -571,6 +571,15 @@ namespace Game.AI
return false;
break;
case SmartEvents.VictimCasting:
if (e.Event.targetCasting.spellId > 0 && !Global.SpellMgr.HasSpellInfo(e.Event.targetCasting.spellId, Difficulty.None))
{
Log.outError(LogFilter.Sql, $"SmartAIMgr: Entry {e.entryOrGuid} SourceType {e.GetScriptType()} Event {e.event_id} Action {e.GetActionType()} uses non-existent Spell entry {e.Event.spellHit.spell}, skipped.");
return false;
}
if (!IsMinMaxValid(e, e.Event.minMax.repeatMin, e.Event.minMax.repeatMax))
return false;
break;
case SmartEvents.PassengerBoarded:
case SmartEvents.PassengerRemoved:
if (!IsMinMaxValid(e, e.Event.minMax.repeatMin, e.Event.minMax.repeatMax))
@@ -912,8 +921,8 @@ namespace Game.AI
if (!IsSpellValid(e, e.Action.cast.spell))
return false;
SpellInfo spellInfo = Global.SpellMgr.GetSpellInfo(e.Action.cast.spell);
foreach (SpellEffectInfo effect in spellInfo.GetEffectsForDifficulty(Difficulty.None))
SpellInfo spellInfo = Global.SpellMgr.GetSpellInfo(e.Action.cast.spell, Difficulty.None);
foreach (SpellEffectInfo effect in spellInfo.GetEffects())
{
if (effect != null && (effect.IsEffect(SpellEffectName.KillCredit) || effect.IsEffect(SpellEffectName.KillCredit2)))
{
@@ -1437,7 +1446,7 @@ namespace Game.AI
}
bool IsSpellValid(SmartScriptHolder e, uint entry)
{
if (!Global.SpellMgr.HasSpellInfo(entry))
if (!Global.SpellMgr.HasSpellInfo(entry, Difficulty.None))
{
Log.outError(LogFilter.ScriptsAi, "SmartAIMgr: Entry {0} SourceType {1} Event {2} Action {3} uses non-existent Spell entry {4}, skipped.", e.entryOrGuid, e.GetScriptType(), e.event_id, e.GetActionType(), entry);
return false;
@@ -1599,7 +1608,7 @@ namespace Game.AI
{ SmartEvents.PassengerRemoved, SmartScriptTypeMaskId.Creature },
{ SmartEvents.Charmed, SmartScriptTypeMaskId.Creature },
{ SmartEvents.CharmedTarget, SmartScriptTypeMaskId.Creature },
{ SmartEvents.SpellhitTarget, SmartScriptTypeMaskId.Creature },
{ SmartEvents.SpellHitTarget, SmartScriptTypeMaskId.Creature },
{ SmartEvents.Damaged, SmartScriptTypeMaskId.Creature },
{ SmartEvents.DamagedTarget, SmartScriptTypeMaskId.Creature },
{ SmartEvents.Movementinform, SmartScriptTypeMaskId.Creature },
+2 -2
View File
@@ -501,7 +501,7 @@ namespace Game.AI
// unless target is outside spell range, out of mana, or LOS.
bool _allowMove = false;
SpellInfo spellInfo = Global.SpellMgr.GetSpellInfo(e.Action.cast.spell);
SpellInfo spellInfo = Global.SpellMgr.GetSpellInfo(e.Action.cast.spell, me.GetMap().GetDifficultyID());
var costs = spellInfo.CalcPowerCost(me, spellInfo.GetSchoolMask());
bool hasPower = true;
foreach (var cost in costs)
@@ -3438,7 +3438,7 @@ namespace Game.AI
RecalcTimer(e, e.Event.kill.cooldownMin, e.Event.kill.cooldownMax);
break;
}
case SmartEvents.SpellhitTarget:
case SmartEvents.SpellHitTarget:
case SmartEvents.SpellHit:
{
if (spell == null)