diff --git a/src/AiFactory.cpp b/src/AiFactory.cpp index 7c18070b..722e46b5 100644 --- a/src/AiFactory.cpp +++ b/src/AiFactory.cpp @@ -88,6 +88,9 @@ uint8 AiFactory::GetPlayerSpecTab(Player* bot) case CLASS_PRIEST: tab = PRIEST_TAB_HOLY; break; + case CLASS_WARLOCK: + tab = WARLOCK_TAB_DEMONOLOGY; + break; } return tab; diff --git a/src/strategy/warlock/GenericWarlockNonCombatStrategy.cpp b/src/strategy/warlock/GenericWarlockNonCombatStrategy.cpp index 1e2b89af..2fd3c78c 100644 --- a/src/strategy/warlock/GenericWarlockNonCombatStrategy.cpp +++ b/src/strategy/warlock/GenericWarlockNonCombatStrategy.cpp @@ -98,6 +98,7 @@ SummonImpStrategy::SummonImpStrategy(PlayerbotAI* ai) : NonCombatStrategy(ai) {} void SummonImpStrategy::InitTriggers(std::vector& triggers) { triggers.push_back(new TriggerNode("no pet", NextAction::array(0, new NextAction("summon imp", 29.0f), NULL))); + triggers.push_back(new TriggerNode("wrong pet", NextAction::array(0, new NextAction("summon imp", 29.0f), NULL))); } // Non-combat strategy for summoning a Voidwalker @@ -109,6 +110,7 @@ SummonVoidwalkerStrategy::SummonVoidwalkerStrategy(PlayerbotAI* ai) : NonCombatS void SummonVoidwalkerStrategy::InitTriggers(std::vector& triggers) { triggers.push_back(new TriggerNode("no pet", NextAction::array(0, new NextAction("summon voidwalker", 29.0f), NULL))); + triggers.push_back(new TriggerNode("wrong pet", NextAction::array(0, new NextAction("summon voidwalker", 29.0f), NULL))); } // Non-combat strategy for summoning a Succubus @@ -120,6 +122,7 @@ SummonSuccubusStrategy::SummonSuccubusStrategy(PlayerbotAI* ai) : NonCombatStrat void SummonSuccubusStrategy::InitTriggers(std::vector& triggers) { triggers.push_back(new TriggerNode("no pet", NextAction::array(0, new NextAction("summon succubus", 29.0f), NULL))); + triggers.push_back(new TriggerNode("wrong pet", NextAction::array(0, new NextAction("summon succubus", 29.0f), NULL))); } // Non-combat strategy for summoning a Felhunter @@ -131,6 +134,7 @@ SummonFelhunterStrategy::SummonFelhunterStrategy(PlayerbotAI* ai) : NonCombatStr void SummonFelhunterStrategy::InitTriggers(std::vector& triggers) { triggers.push_back(new TriggerNode("no pet", NextAction::array(0, new NextAction("summon felhunter", 29.0f), NULL))); + triggers.push_back(new TriggerNode("wrong pet", NextAction::array(0, new NextAction("summon felhunter", 29.0f), NULL))); } // Non-combat strategy for summoning a Felguard @@ -142,6 +146,7 @@ SummonFelguardStrategy::SummonFelguardStrategy(PlayerbotAI* ai) : NonCombatStrat void SummonFelguardStrategy::InitTriggers(std::vector& triggers) { triggers.push_back(new TriggerNode("no pet", NextAction::array(0, new NextAction("summon felguard", 29.0f), NULL))); + triggers.push_back(new TriggerNode("wrong pet", NextAction::array(0, new NextAction("summon felguard", 29.0f), NULL))); } // Non-combat strategy for selecting themselves to receive soulstone diff --git a/src/strategy/warlock/WarlockAiObjectContext.cpp b/src/strategy/warlock/WarlockAiObjectContext.cpp index 7603a5e2..8a87a823 100644 --- a/src/strategy/warlock/WarlockAiObjectContext.cpp +++ b/src/strategy/warlock/WarlockAiObjectContext.cpp @@ -171,6 +171,7 @@ public: creators["curse of exhaustion"] = &WarlockTriggerFactoryInternal::curse_of_exhaustion; creators["curse of tongues"] = &WarlockTriggerFactoryInternal::curse_of_tongues; creators["curse of weakness"] = &WarlockTriggerFactoryInternal::curse_of_weakness; + creators["wrong pet"] = &WarlockTriggerFactoryInternal::wrong_pet; } private: @@ -197,10 +198,7 @@ private: static Trigger* immolate(PlayerbotAI* botAI) { return new ImmolateTrigger(botAI); } static Trigger* immolate_on_attacker(PlayerbotAI* ai) { return new ImmolateOnAttackerTrigger(ai); } static Trigger* unstable_affliction(PlayerbotAI* ai) { return new UnstableAfflictionTrigger(ai); } - static Trigger* unstable_affliction_on_attacker(PlayerbotAI* ai) - { - return new UnstableAfflictionOnAttackerTrigger(ai); - } + static Trigger* unstable_affliction_on_attacker(PlayerbotAI* ai) { return new UnstableAfflictionOnAttackerTrigger(ai); } static Trigger* haunt(PlayerbotAI* ai) { return new HauntTrigger(ai); } static Trigger* decimation(PlayerbotAI* ai) { return new DecimationTrigger(ai); } static Trigger* life_tap(PlayerbotAI* ai) { return new LifeTapTrigger(ai); } @@ -218,6 +216,7 @@ private: static Trigger* curse_of_exhaustion(PlayerbotAI* ai) { return new CurseOfExhaustionTrigger(ai); } static Trigger* curse_of_tongues(PlayerbotAI* ai) { return new CurseOfTonguesTrigger(ai); } static Trigger* curse_of_weakness(PlayerbotAI* ai) { return new CurseOfWeaknessTrigger(ai); } + static Trigger* wrong_pet(PlayerbotAI* ai) { return new WrongPetTrigger(ai); } }; class WarlockAiObjectContextInternal : public NamedObjectContext @@ -413,4 +412,4 @@ void WarlockAiObjectContext::BuildSharedTriggerContexts(SharedNamedObjectContext void WarlockAiObjectContext::BuildSharedValueContexts(SharedNamedObjectContextList& valueContexts) { AiObjectContext::BuildSharedValueContexts(valueContexts); -} \ No newline at end of file +} diff --git a/src/strategy/warlock/WarlockTriggers.cpp b/src/strategy/warlock/WarlockTriggers.cpp index b9794935..e8198c1a 100644 --- a/src/strategy/warlock/WarlockTriggers.cpp +++ b/src/strategy/warlock/WarlockTriggers.cpp @@ -165,3 +165,66 @@ bool CurseOfWeaknessTrigger::IsActive() // Use default BuffTrigger logic for the rest (only trigger if debuff is missing or expiring) return BuffTrigger::IsActive(); } + +struct WarlockPetDef +{ + const char* strategy; // The strategy string as recognized by the AI (e.g., "imp", "voidwalker", etc.) + uint32 spellId; // The spell ID required to summon this pet + uint32 npcEntry; // The NPC entry ID for the summoned pet creature +}; + +// Static array with all relevant Warlock pets and their data +static const WarlockPetDef pets[] = {{"imp", 688, 416}, + {"voidwalker", 697, 1860}, + {"succubus", 712, 1863}, + {"felhunter", 691, 417}, + {"felguard", 30146, 17252}}; + +bool WrongPetTrigger::IsActive() +{ + // Retrieve the bot player and its current pet (if any) + Player* bot = botAI->GetBot(); + Pet* pet = bot->GetPet(); + + // Step 1: Count how many pet strategies are currently enabled for this bot. + // While doing so, also remember which pet strategy is the only enabled one (if that's the case). + int enabledCount = 0; + const WarlockPetDef* enabledPet = + nullptr; // Pointer to the pet definition of the enabled strategy, if only one is enabled + for (const WarlockPetDef& pd : pets) + { + if (botAI->HasStrategy(pd.strategy, BOT_STATE_NON_COMBAT)) + { + enabledCount++; + enabledPet = &pd; // Save the pointer to last enabled pet + } + } + + // Step 2: If not exactly one pet strategy is enabled, we should not trigger. + // This prevents ambiguous or conflicting situations. + if (enabledCount != 1) + return false; + + // Step 3: At this point, we know only one pet strategy is enabled. + // We check if the currently summoned pet matches the enabled strategy. + bool correctPet = false; + if (pet) + { + CreatureTemplate const* ct = pet->GetCreatureTemplate(); + // Check if the pet's NPC entry matches the expected one for the enabled strategy + if (ct && ct->Entry == enabledPet->npcEntry) + correctPet = true; + } + + // Step 4: If the correct pet is already summoned, the trigger should not activate. + if (correctPet) + return false; + + // Step 5: Finally, check if the bot actually knows the spell to summon the desired pet. + // If so, the trigger is active (bot should summon the correct pet). + if (bot->HasSpell(enabledPet->spellId)) + return true; + + // Step 6: If we get here, the bot doesn't know the spell required to support the active pet strategy + return false; +} diff --git a/src/strategy/warlock/WarlockTriggers.h b/src/strategy/warlock/WarlockTriggers.h index e5ce9ad1..e886635a 100644 --- a/src/strategy/warlock/WarlockTriggers.h +++ b/src/strategy/warlock/WarlockTriggers.h @@ -112,6 +112,14 @@ public: HasHealthstoneTrigger(PlayerbotAI* botAI) : WarlockConjuredItemTrigger(botAI, "healthstone") {} }; +class WrongPetTrigger : public Trigger +{ +public: + WrongPetTrigger(PlayerbotAI* botAI) : Trigger(botAI, "wrong pet") {} + bool IsActive() override; +}; + + // CC and Pet Triggers class BanishTrigger : public HasCcTargetTrigger