diff --git a/common/ruletypes.h b/common/ruletypes.h index 11eba8bee..9f5508fac 100644 --- a/common/ruletypes.h +++ b/common/ruletypes.h @@ -835,7 +835,7 @@ RULE_INT(Bots, MinGroupCureTargets, 3, "Minimum number of targets in valid range RULE_INT(Bots, MinTargetsForAESpell, 3, "Minimum number of targets in valid range that are required for an AE spell to cast. Default 3.") RULE_INT(Bots, MinTargetsForGroupSpell, 3, "Minimum number of targets in valid range that are required for an group spell to cast. Default 3.") RULE_BOOL(Bots, AllowBuffingHealingFamiliars, false, "Determines if bots are allowed to buff and heal familiars. Default false.") -RULE_BOOL(Bots, RunSpellTypeChecksOnSpawn, false, "This will run a serious of checks on spell types and output errors to LogBotSpellTypeChecks") +RULE_BOOL(Bots, RunSpellTypeChecksOnBoot, false, "This will run a series of checks to find potential errors in your bot_spells_entries table on boot and output to LogBotSpellTypeChecks") RULE_BOOL(Bots, UseParentSpellTypeForChecks, true, "This will check only the parent instead of AE/Group/Pet types (ex: AENukes/AERains/PBAENukes fall under Nukes or PetBuffs fall under buffs) when RunSpellTypeChecksOnSpawn fires") RULE_BOOL(Bots, AllowForcedCastsBySpellID, true, "If enabled, players can use ^cast spellid # to cast a specific spell by ID that is in their spell list") RULE_BOOL(Bots, AllowCastAAs, true, "If enabled, players can use ^cast aa to cast a clickable AA") diff --git a/common/spdat.cpp b/common/spdat.cpp index e7706dbff..8a6512dfa 100644 --- a/common/spdat.cpp +++ b/common/spdat.cpp @@ -1456,41 +1456,42 @@ bool IsCompleteHealSpell(uint16 spell_id) } bool IsFastHealSpell(uint16 spell_id) { - spell_id = ( - IsEffectInSpell(spell_id, SE_CurrentHP) ? - spell_id : - GetSpellTriggerSpellID(spell_id, SE_CurrentHP) - ); + spell_id = ( + IsEffectInSpell(spell_id, SE_CurrentHP) ? + spell_id : + GetSpellTriggerSpellID(spell_id, SE_CurrentHP) + ); - if (!spell_id) { - spell_id = ( - IsEffectInSpell(spell_id, SE_CurrentHPOnce) ? - spell_id : - GetSpellTriggerSpellID(spell_id, SE_CurrentHPOnce) - ); - } + if (!spell_id) { + spell_id = ( + IsEffectInSpell(spell_id, SE_CurrentHPOnce) ? + spell_id : + GetSpellTriggerSpellID(spell_id, SE_CurrentHPOnce) + ); + } - if (spell_id && IsValidSpell(spell_id)) { - if ( - spells[spell_id].cast_time <= MAX_FAST_HEAL_CASTING_TIME && - spells[spell_id].good_effect && - !IsGroupSpell(spell_id) - ) { - for (int i = 0; i < EFFECT_COUNT; i++) { - if ( - spells[spell_id].base_value[i] > 0 && - ( - spells[spell_id].effect_id[i] == SE_CurrentHP || - spells[spell_id].effect_id[i] == SE_CurrentHPOnce - ) - ) { - return true; - } - } - } - } + if (IsValidSpell(spell_id)) { + if ( + spell_id != SPELL_MINOR_HEALING && + (spells[spell_id].cast_time > MAX_VERY_FAST_HEAL_CASTING_TIME && spells[spell_id].cast_time <= MAX_FAST_HEAL_CASTING_TIME) && + spells[spell_id].good_effect && + !IsGroupSpell(spell_id) + ) { + for (int i = 0; i < EFFECT_COUNT; i++) { + if ( + spells[spell_id].base_value[i] > 0 && + ( + spells[spell_id].effect_id[i] == SE_CurrentHP || + spells[spell_id].effect_id[i] == SE_CurrentHPOnce + ) + ) { + return true; + } + } + } + } - return false; + return false; } bool IsVeryFastHealSpell(uint16 spell_id) @@ -1509,8 +1510,9 @@ bool IsVeryFastHealSpell(uint16 spell_id) ); } - if (spell_id) { + if (IsValidSpell(spell_id)) { if ( + spell_id != SPELL_MINOR_HEALING && spells[spell_id].cast_time <= MAX_VERY_FAST_HEAL_CASTING_TIME && spells[spell_id].good_effect && !IsGroupSpell(spell_id) @@ -1548,8 +1550,13 @@ bool IsRegularSingleTargetHealSpell(uint16 spell_id) ); } - if (spell_id) { + if (IsValidSpell(spell_id)) { + if (spell_id == SPELL_MINOR_HEALING) { + return true; + } + if ( + spells[spell_id].cast_time > MAX_FAST_HEAL_CASTING_TIME && spells[spell_id].target_type == ST_Target && !IsCompleteHealSpell(spell_id) && !IsHealOverTimeSpell(spell_id) && @@ -1589,9 +1596,14 @@ bool IsRegularPetHealSpell(uint16 spell_id) ); } - if (spell_id && IsValidSpell(spell_id)) { + if (IsValidSpell(spell_id)) { + if (spell_id == SPELL_MINOR_HEALING) { + return true; + } + if ( - (spells[spell_id].target_type == ST_Pet || spells[spell_id].target_type == ST_Undead) && + spells[spell_id].cast_time > MAX_FAST_HEAL_CASTING_TIME && + (spells[spell_id].target_type == ST_Pet || spells[spell_id].target_type == ST_SummonedPet) && !IsCompleteHealSpell(spell_id) && !IsHealOverTimeSpell(spell_id) && !IsGroupSpell(spell_id) @@ -1630,7 +1642,7 @@ bool IsRegularGroupHealSpell(uint16 spell_id) ); } - if (spell_id) { + if (IsValidSpell(spell_id)) { if ( IsGroupSpell(spell_id) && !IsCompleteHealSpell(spell_id) && diff --git a/common/spdat.h b/common/spdat.h index ceb316a6a..9b42e2bd4 100644 --- a/common/spdat.h +++ b/common/spdat.h @@ -215,6 +215,7 @@ #define SPELL_AMPLIFICATION 2603 #define SPELL_DIVINE_REZ 2738 #define SPELL_NATURES_RECOVERY 2520 +#define SPELL_MINOR_HEALING 200 #define SPELL_ADRENALINE_SWELL 14445 #define SPELL_ADRENALINE_SWELL_RK2 14446 #define SPELL_ADRENALINE_SWELL_RK3 14447 diff --git a/common/spdat_bot.cpp b/common/spdat_bot.cpp index 04eb5cff8..413a5e00c 100644 --- a/common/spdat_bot.cpp +++ b/common/spdat_bot.cpp @@ -1,4 +1,5 @@ #include "spdat.h" +#include "../zone/bot.h" bool IsBotSpellTypeDetrimental(uint16 spell_type) { switch (spell_type) { @@ -417,264 +418,23 @@ uint16 GetCorrectBotSpellType(uint16 spell_type, uint16 spell_id) { return UINT16_MAX; } - uint16 correct_type = UINT16_MAX; - SPDat_Spell_Struct spell = spells[spell_id]; - std::string teleport_zone = spell.teleport_zone; + uint16 correct_type = spell_type; - if (IsCharmSpell(spell_id)) { - correct_type = BotSpellTypes::Charm; - } - else if (IsFearSpell(spell_id)) { - correct_type = BotSpellTypes::Fear; - } - else if (IsEffectInSpell(spell_id, SE_Revive)) { - correct_type = BotSpellTypes::Resurrect; - } - else if (IsHarmonySpell(spell_id)) { - correct_type = BotSpellTypes::Lull; - } - else if ( - teleport_zone.compare("") && - !IsEffectInSpell(spell_id, SE_GateToHomeCity) && - IsBeneficialSpell(spell_id) && - (IsEffectInSpell(spell_id, SE_Teleport) || IsEffectInSpell(spell_id, SE_Translocate)) - ) { - correct_type = BotSpellTypes::Teleport; - } - else if ( - IsBeneficialSpell(spell_id) && - IsEffectInSpell(spell_id, SE_Succor) - ) { - correct_type = BotSpellTypes::Succor; - } - else if (IsEffectInSpell(spell_id, SE_BindAffinity)) { - correct_type = BotSpellTypes::BindAffinity; - } - else if (IsEffectInSpell(spell_id, SE_Identify)) { - correct_type = BotSpellTypes::Identify; - } - else if ( - spell_type == BotSpellTypes::Levitate && - IsBeneficialSpell(spell_id) && - IsEffectInSpell(spell_id, SE_Levitate) - ) { - correct_type = BotSpellTypes::Levitate; - } - else if ( - spell_type == BotSpellTypes::Rune && - IsBeneficialSpell(spell_id) && - (IsEffectInSpell(spell_id, SE_AbsorbMagicAtt) || IsEffectInSpell(spell_id, SE_Rune)) - ) { - correct_type = BotSpellTypes::Rune; - } - else if ( - spell_type == BotSpellTypes::WaterBreathing && - IsBeneficialSpell(spell_id) && - IsEffectInSpell(spell_id, SE_WaterBreathing) - ) { - correct_type = BotSpellTypes::WaterBreathing; - } - else if ( - spell_type == BotSpellTypes::Size && - IsBeneficialSpell(spell_id) && - (IsEffectInSpell(spell_id, SE_ModelSize) || IsEffectInSpell(spell_id, SE_ChangeHeight)) - ) { - correct_type = BotSpellTypes::Size; - } - else if ( - spell_type == BotSpellTypes::Invisibility && - IsBeneficialSpell(spell_id) && - (IsEffectInSpell(spell_id, SE_SeeInvis) || IsInvisibleSpell(spell_id)) - ) { - correct_type = BotSpellTypes::Invisibility; - } - else if ( - spell_type == BotSpellTypes::MovementSpeed && - IsBeneficialSpell(spell_id) && - IsEffectInSpell(spell_id, SE_MovementSpeed) - ) { - correct_type = BotSpellTypes::MovementSpeed; - } - else if ( - !teleport_zone.compare("") && - IsBeneficialSpell(spell_id) && - (IsEffectInSpell(spell_id, SE_Translocate) || IsEffectInSpell(spell_id, SE_GateToHomeCity)) - ) { - correct_type = BotSpellTypes::SendHome; - } - else if (IsEffectInSpell(spell_id, SE_SummonCorpse)) { - correct_type = BotSpellTypes::SummonCorpse; - } + if (!Bot::IsValidSpellTypeBySpellID(spell_type, spell_id)) { + correct_type = UINT16_MAX; - if (correct_type == UINT16_MAX) { - if ( - IsSummonPetSpell(spell_id) || - IsEffectInSpell(spell_id, SE_TemporaryPets) - ) { - correct_type = BotSpellTypes::Pet; - } - else if (IsMesmerizeSpell(spell_id)) { - correct_type = BotSpellTypes::Mez; - } - else if (IsEscapeSpell(spell_id)) { - correct_type = BotSpellTypes::Escape; - } - else if ( - IsDetrimentalSpell(spell_id) && - IsEffectInSpell(spell_id, SE_Root) - ) { - if (IsAnyAESpell(spell_id)) { - correct_type = BotSpellTypes::AERoot; - } - else { - correct_type = BotSpellTypes::Root; - } - } - else if ( - IsDetrimentalSpell(spell_id) && - IsLifetapSpell(spell_id) - ) { - correct_type = BotSpellTypes::Lifetap; - } - else if ( - IsDetrimentalSpell(spell_id) && - IsEffectInSpell(spell_id, SE_MovementSpeed) - ) { - correct_type = BotSpellTypes::Snare; - } - else if ( - IsDetrimentalSpell(spell_id) && - (IsStackableDOT(spell_id) || IsDamageOverTimeSpell(spell_id)) - ) { - correct_type = BotSpellTypes::DOT; - } - else if (IsDispelSpell(spell_id)) { - correct_type = BotSpellTypes::Dispel; - } - else if ( - IsDetrimentalSpell(spell_id) && - IsSlowSpell(spell_id) - ) { - correct_type = BotSpellTypes::Slow; - } - else if ( - IsDebuffSpell(spell_id) && - !IsHateReduxSpell(spell_id) && - !IsHateSpell(spell_id) - ) { - correct_type = BotSpellTypes::Debuff; - } - else if (IsHateReduxSpell(spell_id)) { - correct_type = BotSpellTypes::HateRedux; - } - else if ( - IsDetrimentalSpell(spell_id) && - IsHateSpell(spell_id) - ) { - correct_type = BotSpellTypes::HateLine; - } - else if ( - IsBuffSpell(spell_id) && - IsBeneficialSpell(spell_id) && - IsBardSong(spell_id) - ) { - if ( - spell_type == BotSpellTypes::InCombatBuffSong || - spell_type == BotSpellTypes::OutOfCombatBuffSong || - spell_type == BotSpellTypes::PreCombatBuffSong - ) { - correct_type = spell_type; - } - else { - correct_type = BotSpellTypes::OutOfCombatBuffSong; - } - } - else if ( - !IsBardSong(spell_id) && - ( - (IsSelfConversionSpell(spell_id) && spell.buff_duration < 1) || - (spell_type == BotSpellTypes::InCombatBuff && IsAnyBuffSpell(spell_id)) - ) - ) { - correct_type = BotSpellTypes::InCombatBuff; - } - else if ( - spell_type == BotSpellTypes::PreCombatBuff && - IsAnyBuffSpell(spell_id) && - !IsBardSong(spell_id) - ) { - correct_type = BotSpellTypes::PreCombatBuff; - } - else if ( - (IsCureSpell(spell_id) && spell_type == BotSpellTypes::Cure) || - (IsCureSpell(spell_id) && !IsAnyHealSpell(spell_id)) - ) { - correct_type = BotSpellTypes::Cure; - } - else if (IsAnyNukeOrStunSpell(spell_id)) { - if (IsAnyAESpell(spell_id)) { - if (IsAERainSpell(spell_id)) { - correct_type = BotSpellTypes::AERains; - } - else if (IsPBAENukeSpell(spell_id)) { - correct_type = BotSpellTypes::PBAENuke; - } - else if (IsStunSpell(spell_id)) { - correct_type = BotSpellTypes::AEStun; - } - else { - correct_type = BotSpellTypes::AENukes; - } - } - else if (IsStunSpell(spell_id)) { - correct_type = BotSpellTypes::Stun; - } - else { - correct_type = BotSpellTypes::Nuke; - } - } - else if (IsAnyHealSpell(spell_id)) { - if (IsGroupSpell(spell_id)) { - if (IsGroupCompleteHealSpell(spell_id)) { - correct_type = BotSpellTypes::GroupCompleteHeals; - } - else if (IsGroupHealOverTimeSpell(spell_id)) { - correct_type = BotSpellTypes::GroupHoTHeals; - } - else if (IsRegularGroupHealSpell(spell_id)) { - correct_type = BotSpellTypes::GroupHeals; - } + auto start = std::min({ BotSpellTypes::START, BotSpellTypes::COMMANDED_START, BotSpellTypes::DISCIPLINE_START }); + auto end = std::max({ BotSpellTypes::END, BotSpellTypes::COMMANDED_END, BotSpellTypes::DISCIPLINE_END }); - return correct_type; + for (int i = end; i >= start; --i) { + if (!Bot::IsValidBotSpellType(i) || i == BotSpellTypes::InCombatBuff) { + continue; } - if (IsVeryFastHealSpell(spell_id)) { - correct_type = BotSpellTypes::VeryFastHeals; - } - else if (IsFastHealSpell(spell_id)) { - correct_type = BotSpellTypes::FastHeals; - } - else if (IsCompleteHealSpell(spell_id)) { - correct_type = BotSpellTypes::CompleteHeal; - } - else if (IsHealOverTimeSpell(spell_id)) { - correct_type = BotSpellTypes::HoTHeals; - } - else if (IsRegularSingleTargetHealSpell(spell_id)) { - correct_type = BotSpellTypes::RegularHeal; - } - else if (IsRegularPetHealSpell(spell_id)) { - correct_type = BotSpellTypes::RegularHeal; - } - } - else if (IsAnyBuffSpell(spell_id)) { - correct_type = BotSpellTypes::Buff; + if (Bot::IsValidSpellTypeBySpellID(i, spell_id)) { + correct_type = i; - if (IsResistanceOnlySpell(spell_id)) { - correct_type = BotSpellTypes::ResistBuffs; - } - else if (IsDamageShieldOnlySpell(spell_id)) { - correct_type = BotSpellTypes::DamageShields; + break; } } } diff --git a/zone/bot.cpp b/zone/bot.cpp index c2304518b..c7bdd38ea 100644 --- a/zone/bot.cpp +++ b/zone/bot.cpp @@ -3710,11 +3710,6 @@ bool Bot::Spawn(Client* botCharacterOwner) { } } - if (RuleB(Bots, RunSpellTypeChecksOnSpawn)) { - OwnerMessage("Running SpellType checks. There may be some spells that are mislabeled as incorrect. Use this as a loose guideline."); - CheckBotSpells(); //This runs through a series of checks and outputs any spells that are set to the wrong spell type in the database - } - if (IsBotRanged()) { ChangeBotRangedWeapons(true); } @@ -9798,6 +9793,7 @@ bool Bot::CanCastSpellType(uint16 spell_type, uint16 spell_id, Mob* tar) { } uint8 bot_class = GetClass(); + auto spell = spells[spell_id]; switch (spell_type) { case BotSpellTypes::Buff: @@ -9821,7 +9817,7 @@ bool Bot::CanCastSpellType(uint16 spell_type, uint16 spell_id, Mob* tar) { case BotSpellTypes::SendHome: if ( tar == this && - spells[spell_id].target_type == ST_TargetsTarget + spell.target_type == ST_TargetsTarget ) { LogBotSpellChecksDetail("{} says, 'Cancelling cast of {} on {} due to target_type checks. Using {}'", GetCleanName(), GetSpellName(spell_id), tar->GetCleanName(), GetSpellTargetType(spell_id)); return false; @@ -9851,7 +9847,7 @@ bool Bot::CanCastSpellType(uint16 spell_type, uint16 spell_id, Mob* tar) { } if ( - spells[spell_id].target_type == ST_Pet && + (spell.target_type == ST_Pet || spell.target_type == ST_SummonedPet) && ( !tar->IsPet() || ( @@ -11574,18 +11570,265 @@ bool Bot::IsValidSpellTypeBySpellID(uint16 spell_type, uint16 spell_id) { return false; } + if (IsBotSpellTypeDetrimental(spell_type) && !IsDetrimentalSpell(spell_id)) { + return false; + } + + if (IsBotSpellTypeBeneficial(spell_type) && !IsBeneficialSpell(spell_id)) { + return false; + } + + auto spell = spells[spell_id]; + std::string teleport_zone = spell.teleport_zone; + switch (spell_type) { - case BotSpellTypes::Buff: - case BotSpellTypes::PetBuffs: - if ( - IsResistanceOnlySpell(spell_id) || - IsDamageShieldOnlySpell(spell_id) || - IsDamageShieldAndResistSpell(spell_id) - ) { - return false; + case BotSpellTypes::Nuke: + if (IsAnyNukeOrStunSpell(spell_id) && !IsStunSpell(spell_id)) { + return true; } - return true; + return false; + case BotSpellTypes::RegularHeal: + case BotSpellTypes::PetRegularHeals: + if ( + IsAnyHealSpell(spell_id) && + !IsVeryFastHealSpell(spell_id) && + !IsFastHealSpell(spell_id) && + !IsCompleteHealSpell(spell_id) && + !IsHealOverTimeSpell(spell_id) && + !IsBuffSpell(spell_id) + ) { + return true; + } + + return false; + case BotSpellTypes::Root: + case BotSpellTypes::AERoot: + if (IsDetrimentalSpell(spell_id) && IsEffectInSpell(spell_id, SE_Root)) { + return true; + } + + return false; + case BotSpellTypes::Buff: + case BotSpellTypes::PreCombatBuff: + case BotSpellTypes::PetBuffs: + if ( + IsBuffSpell(spell_id) && + IsBeneficialSpell(spell_id) && + !IsBardSong(spell_id) && + !IsResistanceOnlySpell(spell_id) && + !IsDamageShieldOnlySpell(spell_id) && + !IsDamageShieldAndResistSpell(spell_id) + ) { + if ( + spell_type != BotSpellTypes::PetBuffs && + (spell.target_type == ST_Pet || spell.target_type == ST_SummonedPet) + ) { + return false; + } + + return true; + } + + return false; + case BotSpellTypes::Escape: + if (IsEscapeSpell(spell_id)) { + return true; + } + + return false; + case BotSpellTypes::Pet: + if (IsSummonPetSpell(spell_id) || IsEffectInSpell(spell_id, SE_TemporaryPets)) { + return true; + } + + return false; + case BotSpellTypes::Lifetap: + case BotSpellTypes::AELifetap: + if (IsDetrimentalSpell(spell_id) && IsLifetapSpell(spell_id)) { + return true; + } + + return false; + case BotSpellTypes::Snare: + case BotSpellTypes::AESnare: + if (IsDetrimentalSpell(spell_id) && IsEffectInSpell(spell_id, SE_MovementSpeed)) { + return true; + } + + return false; + case BotSpellTypes::DOT: + case BotSpellTypes::AEDoT: + if ( + IsDetrimentalSpell(spell_id) && + (IsStackableDOT(spell_id) || IsDamageOverTimeSpell(spell_id)) + ) { + return true; + } + + return false; + case BotSpellTypes::Dispel: + case BotSpellTypes::AEDispel: + if (IsDispelSpell(spell_id)) { + return true; + } + + return false; + case BotSpellTypes::InCombatBuff: + if ( + !IsBardSong(spell_id) && + ( + (IsSelfConversionSpell(spell_id) && spell.buff_duration < 1) || + (spell_type == BotSpellTypes::InCombatBuff && IsAnyBuffSpell(spell_id)) + ) + ) { + return true; + } + + return false; + case BotSpellTypes::Mez: + case BotSpellTypes::AEMez: + if (IsMesmerizeSpell(spell_id)) { + return true; + } + + return false; + case BotSpellTypes::Charm: + if (IsCharmSpell(spell_id)) { + return true; + } + + return false; + case BotSpellTypes::Slow: + case BotSpellTypes::AESlow: + if (IsDetrimentalSpell(spell_id) && IsSlowSpell(spell_id)) { + return true; + } + + return false; + case BotSpellTypes::Debuff: + case BotSpellTypes::AEDebuff: + if ( + IsDebuffSpell(spell_id) && + !IsHateReduxSpell(spell_id) && + !IsHateSpell(spell_id) + ) { + return true; + } + + return false; + case BotSpellTypes::Cure: + case BotSpellTypes::GroupCures: + case BotSpellTypes::PetCures: + if (IsCureSpell(spell_id)) { + return true; + } + + return false; + case BotSpellTypes::Resurrect: + if (IsEffectInSpell(spell_id, SE_Revive)) { + return true; + } + + return false; + case BotSpellTypes::HateRedux: + if (IsHateReduxSpell(spell_id)) { + return true; + } + + return false; + case BotSpellTypes::InCombatBuffSong: + case BotSpellTypes::OutOfCombatBuffSong: + case BotSpellTypes::PreCombatBuffSong: + if ( + IsBuffSpell(spell_id) && + IsBeneficialSpell(spell_id) && + IsBardSong(spell_id) + ) { + return true; + } + + return false; + case BotSpellTypes::Fear: + case BotSpellTypes::AEFear: + if (IsFearSpell(spell_id)) { + return true; + } + + return false; + case BotSpellTypes::Stun: + case BotSpellTypes::AEStun: + if (IsDetrimentalSpell(spell_id) && IsStunSpell(spell_id)) { + return true; + } + + return false; + case BotSpellTypes::HateLine: + case BotSpellTypes::AEHateLine: + if (IsDetrimentalSpell(spell_id) && IsHateSpell(spell_id)) { + return true; + } + + return false; + case BotSpellTypes::CompleteHeal: + case BotSpellTypes::GroupCompleteHeals: + case BotSpellTypes::PetCompleteHeals: + if (IsCompleteHealSpell(spell_id) || IsGroupCompleteHealSpell(spell_id)) { + return true; + } + + return false; + case BotSpellTypes::FastHeals: + case BotSpellTypes::PetFastHeals: + if (IsFastHealSpell(spell_id)) { + return true; + } + + return false; + case BotSpellTypes::VeryFastHeals: + case BotSpellTypes::PetVeryFastHeals: + if (IsVeryFastHealSpell(spell_id)) { + return true; + } + + return false; + case BotSpellTypes::GroupHeals: + if (IsRegularGroupHealSpell(spell_id)) { + return true; + } + + return false; + case BotSpellTypes::HoTHeals: + case BotSpellTypes::GroupHoTHeals: + case BotSpellTypes::PetHoTHeals: + if (IsHealOverTimeSpell(spell_id) || IsGroupHealOverTimeSpell(spell_id)) { + return true; + } + + return false; + case BotSpellTypes::AENukes: + if ( + IsDetrimentalSpell(spell_id) && + !IsAERainSpell(spell_id) && + !IsPBAENukeSpell(spell_id) && + !IsStunSpell(spell_id) + ) { + return true; + } + + return false; + case BotSpellTypes::AERains: + if (IsAERainNukeSpell(spell_id) && !IsStunSpell(spell_id)) { + return true; + } + + return false; + case BotSpellTypes::PBAENuke: + if (IsPBAENukeSpell(spell_id) && !IsStunSpell(spell_id)) { + return true; + } + + return false; case BotSpellTypes::ResistBuffs: case BotSpellTypes::PetResistBuffs: if (IsResistanceBuffSpell(spell_id)) { @@ -11595,44 +11838,7 @@ bool Bot::IsValidSpellTypeBySpellID(uint16 spell_type, uint16 spell_id) { return false; case BotSpellTypes::DamageShields: case BotSpellTypes::PetDamageShields: - if (IsEffectInSpell(spell_id, SE_DamageShield)) { - return true; - } - - return false; - case BotSpellTypes::PBAENuke: - if ( - IsPBAENukeSpell(spell_id) && - !IsStunSpell(spell_id) - ) { - return true; - } - - return false; - case BotSpellTypes::AERains: - if ( - IsAERainNukeSpell(spell_id) && - !IsStunSpell(spell_id) - ) { - return true; - } - return false; - case BotSpellTypes::AEStun: - case BotSpellTypes::Stun: - if (IsStunSpell(spell_id)) { - return true; - } - - return false; - case BotSpellTypes::AENukes: - case BotSpellTypes::Nuke: - if (!IsStunSpell(spell_id)) { - return true; - } - - return false; - case BotSpellTypes::Lull: - if (IsHarmonySpell(spell_id)) { + if (IsBeneficialSpell(spell_id) && IsEffectInSpell(spell_id, SE_DamageShield)) { return true; } @@ -11640,13 +11846,18 @@ bool Bot::IsValidSpellTypeBySpellID(uint16 spell_type, uint16 spell_id) { case BotSpellTypes::Teleport: if ( IsBeneficialSpell(spell_id) && - ( - IsEffectInSpell(spell_id, SE_Teleport) || IsEffectInSpell(spell_id, SE_Translocate) - ) + (IsEffectInSpell(spell_id, SE_Teleport) || IsEffectInSpell(spell_id, SE_Translocate)) ) { return true; } + return false; + case BotSpellTypes::Lull: + case BotSpellTypes::AELull: + if (IsHarmonySpell(spell_id)) { + return true; + } + return false; case BotSpellTypes::Succor: if ( @@ -11670,25 +11881,21 @@ bool Bot::IsValidSpellTypeBySpellID(uint16 spell_type, uint16 spell_id) { return false; case BotSpellTypes::Levitate: - if ( - IsBeneficialSpell(spell_id) && - IsEffectInSpell(spell_id, SE_Levitate) - ) { + if (IsBeneficialSpell(spell_id) && IsEffectInSpell(spell_id, SE_Levitate)) { return true; } return false; case BotSpellTypes::Rune: - if ( - IsEffectInSpell(spell_id, SE_AbsorbMagicAtt) || - IsEffectInSpell(spell_id, SE_Rune) + if (IsBeneficialSpell(spell_id) && + (IsEffectInSpell(spell_id, SE_AbsorbMagicAtt) || IsEffectInSpell(spell_id, SE_Rune)) ) { return true; } return false; case BotSpellTypes::WaterBreathing: - if (IsEffectInSpell(spell_id, SE_WaterBreathing)) { + if (IsBeneficialSpell(spell_id) && IsEffectInSpell(spell_id, SE_WaterBreathing)) { return true; } @@ -11696,29 +11903,22 @@ bool Bot::IsValidSpellTypeBySpellID(uint16 spell_type, uint16 spell_id) { case BotSpellTypes::Size: if ( IsBeneficialSpell(spell_id) && - ( - IsEffectInSpell(spell_id, SE_ModelSize) || - IsEffectInSpell(spell_id, SE_ChangeHeight) - ) + (IsEffectInSpell(spell_id, SE_ModelSize) || IsEffectInSpell(spell_id, SE_ChangeHeight)) ) { return true; } return false; case BotSpellTypes::Invisibility: - if ( - IsEffectInSpell(spell_id, SE_SeeInvis) || - IsInvisibleSpell(spell_id) + if (IsBeneficialSpell(spell_id) && + (IsEffectInSpell(spell_id, SE_SeeInvis) ||IsInvisibleSpell(spell_id)) ) { return true; } return false; case BotSpellTypes::MovementSpeed: - if ( - IsBeneficialSpell(spell_id) && - IsEffectInSpell(spell_id, SE_MovementSpeed) - ) { + if (IsBeneficialSpell(spell_id) && IsEffectInSpell(spell_id, SE_MovementSpeed)) { return true; } @@ -11726,7 +11926,13 @@ bool Bot::IsValidSpellTypeBySpellID(uint16 spell_type, uint16 spell_id) { case BotSpellTypes::SendHome: if ( IsBeneficialSpell(spell_id) && - IsEffectInSpell(spell_id, SE_GateToHomeCity) + ( + IsEffectInSpell(spell_id, SE_GateToHomeCity) || + ( + teleport_zone.compare("") && + (IsEffectInSpell(spell_id, SE_Teleport) || IsEffectInSpell(spell_id, SE_Translocate)) + ) + ) ) { return true; } diff --git a/zone/bot.h b/zone/bot.h index a2278c302..504eafe70 100644 --- a/zone/bot.h +++ b/zone/bot.h @@ -683,7 +683,6 @@ public: void SetSitManaPct(uint8 value) { _SitManaPct = value; } // Spell lists - void CheckBotSpells(); std::list GetSpellTypesPrioritized(uint8 priority_type); static uint16 GetParentSpellType(uint16 spell_type); static bool IsValidSpellTypeBySpellID(uint16 spell_type, uint16 spell_id); diff --git a/zone/bot_command.cpp b/zone/bot_command.cpp index c8a216f8b..0fcaef114 100644 --- a/zone/bot_command.cpp +++ b/zone/bot_command.cpp @@ -220,6 +220,11 @@ int bot_command_init(void) std::vector> injected_bot_command_settings; std::vector orphaned_bot_command_settings; + if (RuleB(Bots, RunSpellTypeChecksOnBoot)) { + LogBotSpellTypeChecks("Running SpellType checks. There may be some spells that are mislabeled as incorrect. Use this as a loose guideline."); + database.botdb.CheckBotSpells(); + } + database.botdb.MapCommandedSpellTypeMinLevels(); for (auto bcs_iter : bot_command_settings) { @@ -813,7 +818,7 @@ void helper_send_usage_required_bots(Client *bot_owner, uint16 spell_type) auto spell_type_itr = spell_map.find(spell_type); auto class_itr = spell_type_itr->second.find(i); const auto& spell_info = class_itr->second; - + if (spell_info.min_level < UINT8_MAX) { found = true; diff --git a/zone/bot_database.cpp b/zone/bot_database.cpp index 96309be9a..088b8bf35 100644 --- a/zone/bot_database.cpp +++ b/zone/bot_database.cpp @@ -2523,6 +2523,142 @@ bool BotDatabase::DeleteBotBlockedBuffs(const uint32 bot_id) return true; } +void BotDatabase::CheckBotSpells() { + auto spell_list = BotSpellsEntriesRepository::All(content_db); + uint16 spell_id; + SPDat_Spell_Struct spell; + + for (const auto& s : spell_list) { + if (!IsValidSpell(s.spell_id)) { + LogBotSpellTypeChecks("{} is an invalid spell", s.spell_id); + continue; + } + + spell = spells[s.spell_id]; + spell_id = spell.id; + + if (spell.classes[s.npc_spells_id - (BOT_CLASS_BASE_ID_PREFIX + 1)] >= 255) { + LogBotSpellTypeChecks("{} [#{}] is not usable by a {} [#{}].", GetSpellName(spell_id), spell_id, GetClassIDName(s.npc_spells_id - BOT_CLASS_BASE_ID_PREFIX), s.npc_spells_id); + } + else { + if (spell.classes[s.npc_spells_id - (BOT_CLASS_BASE_ID_PREFIX + 1)] > s.minlevel) { + LogBotSpellTypeChecks("{} [#{}] is not usable until level {} for a {} [#{}] and the min level is currently set to {}.", + GetSpellName(spell_id), + spell_id, + spell.classes[s.npc_spells_id - (BOT_CLASS_BASE_ID_PREFIX + 1)], + GetClassIDName(s.npc_spells_id - BOT_CLASS_BASE_ID_PREFIX), + s.npc_spells_id, + s.minlevel + ); + + LogBotSpellTypeChecksDetail("UPDATE bot_spells_entries SET `minlevel` = {} WHERE `spellid` = {} AND `npc_spells_id` = {}; -- {} [#{}] from minlevel {} to {} for {} [#{}]", + spell.classes[s.npc_spells_id - (BOT_CLASS_BASE_ID_PREFIX + 1)], + spell_id, + s.npc_spells_id, + GetSpellName(spell_id), + spell_id, + s.minlevel, + spell.classes[s.npc_spells_id - (BOT_CLASS_BASE_ID_PREFIX + 1)], + GetClassIDName(s.npc_spells_id - BOT_CLASS_BASE_ID_PREFIX), + s.npc_spells_id + ); + } + + if (spell.classes[s.npc_spells_id - (BOT_CLASS_BASE_ID_PREFIX + 1)] < s.minlevel) { + LogBotSpellTypeChecks("{} [#{}] could be used starting at level {} for a {} [#{}] instead of the current min level of {}.", + GetSpellName(spell_id), + spell_id, + spell.classes[s.npc_spells_id - (BOT_CLASS_BASE_ID_PREFIX + 1)], + GetClassIDName(s.npc_spells_id - BOT_CLASS_BASE_ID_PREFIX), + s.npc_spells_id, + s.minlevel + ); + + LogBotSpellTypeChecksDetail("UPDATE bot_spells_entries SET `minlevel` = {} WHERE `spellid` = {} AND `npc_spells_id` = {}; -- {} [#{}] from minlevel {} to {} for {} [#{}]", + spell.classes[s.npc_spells_id - (BOT_CLASS_BASE_ID_PREFIX + 1)], + spell_id, + s.npc_spells_id, + GetSpellName(spell_id), + spell_id, + s.minlevel, + spell.classes[s.npc_spells_id - (BOT_CLASS_BASE_ID_PREFIX + 1)], + GetClassIDName(s.npc_spells_id - BOT_CLASS_BASE_ID_PREFIX), + s.npc_spells_id + ); + } + + + if (spell.classes[s.npc_spells_id - (BOT_CLASS_BASE_ID_PREFIX + 1)] > s.maxlevel) { + LogBotSpellTypeChecks("{} [#{}] is not usable until level {} for a {} [#{}] and the max level is currently set to {}.", + GetSpellName(spell_id), + spell_id, + spell.classes[s.npc_spells_id - (BOT_CLASS_BASE_ID_PREFIX + 1)], + GetClassIDName(s.npc_spells_id - BOT_CLASS_BASE_ID_PREFIX), + s.npc_spells_id, + s.maxlevel + ); + } + } + + uint16 correct_type = GetCorrectBotSpellType(s.type, spell_id); + + if (RuleB(Bots, UseParentSpellTypeForChecks)) { + uint16 parent_type = Bot::GetParentSpellType(correct_type); + + if (s.type == parent_type || s.type == correct_type) { + continue; + } + + if (correct_type != parent_type) { + correct_type = parent_type; + } + } + else { + if (IsPetBotSpellType(s.type)) { + correct_type = GetPetBotSpellType(correct_type); + } + } + + if (IsPetBotSpellType(correct_type) && (spell.target_type != ST_Pet && spell.target_type != ST_SummonedPet)) { + correct_type = Bot::GetParentSpellType(correct_type); + } + + if (correct_type == s.type) { + continue; + } + + if (correct_type == UINT16_MAX) { + LogBotSpellTypeChecks( + "{} [#{}] is incorrect. It is currently set as {} [#{}] but the correct type is unknown.", + GetSpellName(spell_id), + spell_id, + Bot::GetSpellTypeNameByID(s.type), + s.type + ); + } + else { + LogBotSpellTypeChecks("{} [#{}] is incorrect. It is currently set as {} [#{}] and should be {} [#{}]", + GetSpellName(spell_id), + spell_id, + Bot::GetSpellTypeNameByID(s.type), + s.type, + Bot::GetSpellTypeNameByID(correct_type), + correct_type + ); + LogBotSpellTypeChecksDetail("UPDATE bot_spells_entries SET `type` = {} WHERE `spell_id` = {}; -- {} [#{}] from {} [#{}] to {} [#{}]", + correct_type, + spell_id, + GetSpellName(spell_id), + spell_id, + Bot::GetSpellTypeNameByID(s.type), + s.type, + Bot::GetSpellTypeNameByID(correct_type), + correct_type + ); + } + } +} + void BotDatabase::MapCommandedSpellTypeMinLevels() { commanded_spell_type_min_levels.clear(); diff --git a/zone/bot_database.h b/zone/bot_database.h index 12e429540..407050079 100644 --- a/zone/bot_database.h +++ b/zone/bot_database.h @@ -130,6 +130,7 @@ public: bool SaveBotSettings(Mob* m); bool DeleteBotSettings(const uint32 bot_id); + void CheckBotSpells(); void MapCommandedSpellTypeMinLevels(); std::map> GetCommandedSpellTypesMinLevels() { return commanded_spell_type_min_levels; } diff --git a/zone/botspellsai.cpp b/zone/botspellsai.cpp index b25ddc6a5..bb0f6b8e5 100644 --- a/zone/botspellsai.cpp +++ b/zone/botspellsai.cpp @@ -2865,132 +2865,3 @@ BotSpell Bot::GetBestBotSpellForCharm(Bot* caster, Mob* target, uint16 spell_typ return result; } - - -void Bot::CheckBotSpells() { - auto spell_list = BotSpellsEntriesRepository::All(content_db); - uint16 spell_id; - SPDat_Spell_Struct spell; - uint16 correct_type; - uint16 parent_type; - - for (const auto& s : spell_list) { - if (!IsValidSpell(s.spell_id)) { - LogBotSpellTypeChecks("{} is an invalid spell", s.spell_id); - continue; - } - - spell = spells[s.spell_id]; - spell_id = spell.id; - - if (spell.classes[s.npc_spells_id - (BOT_CLASS_BASE_ID_PREFIX + 1)] >= 255) { - LogBotSpellTypeChecks("{} [#{}] is not usable by a {} [#{}].", GetSpellName(spell_id), spell_id, GetClassIDName(s.npc_spells_id - BOT_CLASS_BASE_ID_PREFIX), s.npc_spells_id); - } - else { - if (spell.classes[s.npc_spells_id - (BOT_CLASS_BASE_ID_PREFIX + 1)] > s.minlevel) { - LogBotSpellTypeChecks("{} [#{}] is not usable until level {} for a {} [#{}] and the min level is currently set to {}." - , GetSpellName(spell_id) - , spell_id - , spell.classes[s.npc_spells_id - (BOT_CLASS_BASE_ID_PREFIX + 1)] - , GetClassIDName(s.npc_spells_id - BOT_CLASS_BASE_ID_PREFIX) - , s.npc_spells_id - , s.minlevel - ); - - LogBotSpellTypeChecksDetail("UPDATE bot_spells_entries SET `minlevel` = {} WHERE `spellid` = {} AND `npc_spells_id` = {}; -- {} [#{}] from minlevel {} to {} for {} [#{}]" - , spell.classes[s.npc_spells_id - (BOT_CLASS_BASE_ID_PREFIX + 1)] - , spell_id - , s.npc_spells_id - , GetSpellName(spell_id) - , spell_id - , s.minlevel - , spell.classes[s.npc_spells_id - (BOT_CLASS_BASE_ID_PREFIX + 1)] - , GetClassIDName(s.npc_spells_id - BOT_CLASS_BASE_ID_PREFIX) - , s.npc_spells_id - ); - } - - if (spell.classes[s.npc_spells_id - (BOT_CLASS_BASE_ID_PREFIX + 1)] < s.minlevel) { - LogBotSpellTypeChecks("{} [#{}] could be used starting at level {} for a {} [#{}] instead of the current min level of {}." - , GetSpellName(spell_id) - , spell_id - , spell.classes[s.npc_spells_id - (BOT_CLASS_BASE_ID_PREFIX + 1)] - , GetClassIDName(s.npc_spells_id - BOT_CLASS_BASE_ID_PREFIX) - , s.npc_spells_id - , s.minlevel - ); - - LogBotSpellTypeChecksDetail("UPDATE bot_spells_entries SET `minlevel` = {} WHERE `spellid` = {} AND `npc_spells_id` = {}; -- {} [#{}] from minlevel {} to {} for {} [#{}]" - , spell.classes[s.npc_spells_id - (BOT_CLASS_BASE_ID_PREFIX + 1)] - , spell_id - , s.npc_spells_id - , GetSpellName(spell_id) - , spell_id - , s.minlevel - , spell.classes[s.npc_spells_id - (BOT_CLASS_BASE_ID_PREFIX + 1)] - , GetClassIDName(s.npc_spells_id - BOT_CLASS_BASE_ID_PREFIX) - , s.npc_spells_id - ); - } - - - if (spell.classes[s.npc_spells_id - (BOT_CLASS_BASE_ID_PREFIX + 1)] > s.maxlevel) { - LogBotSpellTypeChecks("{} [#{}] is not usable until level {} for a {} [#{}] and the max level is currently set to {}." - , GetSpellName(spell_id) - , spell_id - , spell.classes[s.npc_spells_id - (BOT_CLASS_BASE_ID_PREFIX + 1)] - , GetClassIDName(s.npc_spells_id - BOT_CLASS_BASE_ID_PREFIX) - , s.npc_spells_id - , s.maxlevel - ); - } - } - - correct_type = GetCorrectBotSpellType(s.type, spell_id); - parent_type = GetParentSpellType(correct_type); - - if (RuleB(Bots, UseParentSpellTypeForChecks)) { - if (s.type == parent_type || s.type == correct_type) { - continue; - } - } - else { - if (IsPetBotSpellType(s.type)) { - correct_type = GetPetBotSpellType(correct_type); - } - } - - if (correct_type == s.type) { - continue; - } - - if (correct_type == UINT16_MAX) { - LogBotSpellTypeChecks("{} [#{}] is incorrect. It is currently set as {} [#{}] but the correct type is unknown." - , GetSpellName(spell_id) - , spell_id - , GetSpellTypeNameByID(s.type) - , s.type - ); - } - else { - LogBotSpellTypeChecks("{} [#{}] is incorrect. It is currently set as {} [#{}] and should be {} [#{}]" - , GetSpellName(spell_id) - , spell_id - , GetSpellTypeNameByID(s.type) - , s.type - , GetSpellTypeNameByID(correct_type) - , correct_type - ); - LogBotSpellTypeChecksDetail("UPDATE bot_spells_entries SET `type` = {} WHERE `spell_id` = {}; -- {} [#{}] from {} [#{}] to {} [#{}]" - , correct_type - , spell_id - , GetSpellName(spell_id) - , spell_id - , GetSpellTypeNameByID(s.type) - , s.type - , GetSpellTypeNameByID(correct_type) - , correct_type - ); - } - } -}