diff --git a/common/ruletypes.h b/common/ruletypes.h index 574b34800..0d66fd1cf 100644 --- a/common/ruletypes.h +++ b/common/ruletypes.h @@ -834,8 +834,10 @@ RULE_BOOL(Bots, AllowMagicianEpicPet, false, "If enabled, magician bots can summ RULE_INT(Bots, AllowMagicianEpicPetLevel, 50, "If AllowMagicianEpicPet is enabled, bots can start using their epic pets at this level") RULE_INT(Bots, RequiredMagicianEpicPetItemID, 28034, "If AllowMagicianEpicPet is enabled and this is set, bots will be required to have this item ID equipped to cast their epic. Takes in to account AllowMagicianEpicPetLevel as well. Set to 0 to disable requirement") RULE_STRING(Bots, EpicPetSpellName, "", "'teleport_zone' in the spell to be cast for epic pets. This must be in their spell list to cast.") -RULE_BOOL(Bots, AllowSpellPulling, true, "If enabled bots will use a spell to pull when within range. Uses PullSpellID.") +RULE_BOOL(Bots, UseSpellPulling, true, "If enabled bots will use a spell to pull when within range. Uses PullSpellID.") RULE_INT(Bots, PullSpellID, 5225, "Default 5225 - Throw Stone. Spell that will be cast to pull by bots") +RULE_BOOL(Bots, AllowRangedPulling, true, "If enabled bots will pull with their ranged items if set to ranged.") +RULE_BOOL(Bots, AllowAISpellPulling, true, "If enabled bots will rely on their detrimental AI to pull when within range.") RULE_BOOL(Bots, AllowBotEquipAnyClassGear, false, "Allows Bots to wear Equipment even if their class is not valid") RULE_BOOL(Bots, BotArcheryConsumesAmmo, true, "Set to false to disable Archery Ammo Consumption") RULE_BOOL(Bots, BotThrowingConsumesAmmo, true, "Set to false to disable Throwing Ammo Consumption") diff --git a/common/spdat.cpp b/common/spdat.cpp index e9aec626c..8a21e23b9 100644 --- a/common/spdat.cpp +++ b/common/spdat.cpp @@ -3279,6 +3279,25 @@ bool IsCommandedSpellType(uint16 spellType) { return false; } +bool IsPullingSpellType(uint16 spellType) { + switch (spellType) { + case BotSpellTypes::Nuke: + case BotSpellTypes::Lifetap: + case BotSpellTypes::Snare: + case BotSpellTypes::DOT: + case BotSpellTypes::Dispel: + case BotSpellTypes::Slow: + case BotSpellTypes::Debuff: + case BotSpellTypes::Stun: + case BotSpellTypes::HateLine: + return true; + default: + return false; + } + + return false; +} + bool BotSpellTypeRequiresLoS(uint16 spellType, uint8 cls) { switch (spellType) { case BotSpellTypes::Nuke: diff --git a/common/spdat.h b/common/spdat.h index adf9c05e4..e7a266459 100644 --- a/common/spdat.h +++ b/common/spdat.h @@ -740,7 +740,6 @@ bool IsBotSpellTypeDetrimental (uint16 spellType, uint8 cls = 0); bool IsBotSpellTypeBeneficial (uint16 spellType, uint8 cls = 0); bool IsBotSpellTypeOtherBeneficial(uint16 spellType); bool IsBotSpellTypeInnate (uint16 spellType); -bool IsBotSpellType (uint16 spellType); bool IsAEBotSpellType(uint16 spellType); bool IsGroupBotSpellType(uint16 spellType); bool IsGroupTargetOnlyBotSpellType(uint16 spellType); @@ -752,6 +751,7 @@ bool SpellTypeRequiresTarget(uint16 spellType, uint16 cls = 0); bool SpellTypeRequiresCastChecks(uint16 spellType); bool SpellTypeRequiresAEChecks(uint16 spellType); bool IsCommandedSpellType(uint16 spellType); +bool IsPullingSpellType(uint16 spellType); bool BotSpellTypeRequiresLoS(uint16 spellType, uint8 cls); // These should not be used to determine spell category.. diff --git a/zone/bot.cpp b/zone/bot.cpp index fc8e110cd..8bf13d6b4 100644 --- a/zone/bot.cpp +++ b/zone/bot.cpp @@ -101,6 +101,7 @@ Bot::Bot(NPCType *npcTypeData, Client* botOwner) : NPC(npcTypeData, nullptr, glm LoadDefaultBotSettings(); SetCastedSpellType(UINT16_MAX); SetCommandedSpell(false); + SetPullingSpell(false); //DisableBotSpellTimers(); // Do this once and only in this constructor @@ -178,6 +179,7 @@ Bot::Bot( SetBotCharmer(false); SetCastedSpellType(UINT16_MAX); SetCommandedSpell(false); + SetPullingSpell(false); bool stance_flag = false; if (!database.botdb.LoadStance(this, stance_flag) && bot_owner) { @@ -2200,36 +2202,53 @@ void Bot::AI_Process() // PULLING FLAG (ACTIONABLE RANGE) if (GetPullingFlag()) { - if (!IsBotNonSpellFighter() && !HOLDING && AI_HasSpells() && AI_EngagedCastCheck()) { - return; + if (!IsBotNonSpellFighter() && !HOLDING && AI_HasSpells()) { + SetPullingSpell(true); + + if (AI_EngagedCastCheck()) { + SetPullingSpell(false); + return; + } + + SetPullingSpell(false); } - if (RuleB(Bots, AllowSpellPulling)) { + if (RuleB(Bots, UseSpellPulling)) { uint16 pullSpell = RuleI(Bots, PullSpellID); - if (tar_distance <= (spells[pullSpell].range * spells[pullSpell].range)) { + if (tar_distance <= spells[pullSpell].range) { StopMoving(); if (!TargetValidation(tar)) { return; } CastSpell(pullSpell, tar->GetID()); + SetPullingSpell(false); return; } } else { - //TODO bot rewrite - add casting AI to this - if (atCombatRange && IsBotRanged() && ranged_timer.Check(false)) { - StopMoving(CalculateHeadingToTarget(tar->GetX(), tar->GetY())); + if (atCombatRange) { + if (RuleB(Bots, AllowRangedPulling) && IsBotRanged() && ranged_timer.Check(false)) { + StopMoving(CalculateHeadingToTarget(tar->GetX(), tar->GetY())); - if (BotRangedAttack(tar) && CheckDoubleRangedAttack()) { - BotRangedAttack(tar, true); + if (BotRangedAttack(tar) && CheckDoubleRangedAttack()) { + BotRangedAttack(tar, true); + } + + ranged_timer.Start(); + SetPullingSpell(false); + + return; } + else if (RuleB(Bots, AllowAISpellPulling) && !IsBotNonSpellFighter() && !HOLDING && AI_HasSpells() && AI_EngagedCastCheck()) { + SetPullingSpell(false); - ranged_timer.Start(); - - return; + return; + } } + + return; } } @@ -2624,7 +2643,7 @@ void Bot::DoAttackRounds(Mob* target, int hand) { NPC_FLURRY, GetCleanName(), target->GetCleanName() - ); //TODO bot rewrite - add output to others hits with flurry message + ); } } } @@ -9346,6 +9365,10 @@ bool Bot::PrecastChecks(Mob* tar, uint16 spellType) { return false; } + if (IsPullingSpell() && IsPullingSpellType(spellType)) { //Skip remaining checks for commanded + return true; + } + if (GetManaRatio() < GetSpellTypeMinManaLimit(spellType) || GetManaRatio() > GetSpellTypeMaxManaLimit(spellType)) { LogBotPreChecksDetail("{} says, 'Cancelling cast of [{}] on [{}] due to GetSpellTypeMinManaLimit or GetSpellTypeMaxManaLimit.'", GetCleanName(), GetSpellTypeNameByID(spellType), tar->GetCleanName()); //deleteme return false; diff --git a/zone/bot.h b/zone/bot.h index bf785cfc5..5a9cbf7fb 100644 --- a/zone/bot.h +++ b/zone/bot.h @@ -410,6 +410,8 @@ public: void SetPauseAI(bool pause_flag) { _pauseAI = pause_flag; } bool IsCommandedSpell() const { return _commandedSpell; } void SetCommandedSpell(bool value) { _commandedSpell = value; } + bool IsPullingSpell() const { return _pullingSpell; } + void SetPullingSpell(bool value) { _pullingSpell = value; } void SetGuardMode(); void SetHoldMode(); @@ -1083,6 +1085,7 @@ private: uint16 _castedSpellType; bool _hasLoS; bool _commandedSpell; + bool _pullingSpell; // Private "base stats" Members int32 _baseMR;