mirror of
https://github.com/mod-playerbots/mod-playerbots
synced 2025-11-29 15:58:20 +08:00
Paladin buff logic: Sanctuary+Kings synergy, role-aware targeting, safer Greater buffs (#1603)
* Paladin buff logic: Sanctuary+Kings synergy, role-aware targeting, safer Greater buffs * Update PaladinActions.cpp * Update PaladinActions.cpp * All configs should be implement into PlayerbotAIConfig and sPlayerbotAIConfig used in code * added: prayer of fortitude * Magic number removed * Update PaladinActions.cpp * Update PaladinActions.cpp * Update PaladinActions.cpp * Update PaladinActions.cpp * Update PaladinActions.cpp * Add patch for solo paladin in group * Correction review * Update PaladinActions.cpp * Add harcoded text to DB
This commit is contained in:
@@ -21,6 +21,7 @@
|
|||||||
# THRESHOLDS
|
# THRESHOLDS
|
||||||
# QUESTS
|
# QUESTS
|
||||||
# COMBAT
|
# COMBAT
|
||||||
|
# PALADIN BUFFS STRATEGIES
|
||||||
# CHEATS
|
# CHEATS
|
||||||
# SPELLS
|
# SPELLS
|
||||||
# FLIGHTPATH
|
# FLIGHTPATH
|
||||||
@@ -457,6 +458,24 @@ AiPlayerbot.FleeingEnabled = 1
|
|||||||
#
|
#
|
||||||
####################################################################################################
|
####################################################################################################
|
||||||
|
|
||||||
|
####################################################################################################
|
||||||
|
# PALADIN BUFFS STRATEGIES
|
||||||
|
#
|
||||||
|
#
|
||||||
|
|
||||||
|
# Min group size to use Greater buffs (Paladin, Mage, Druid)
|
||||||
|
# Default: 3
|
||||||
|
AiPlayerbot.MinBotsForGreaterBuff = 3
|
||||||
|
|
||||||
|
# Cooldown (seconds) between reagent-missing RP warnings, per bot & per buff
|
||||||
|
# Default: 30
|
||||||
|
AiPlayerbot.RPWarningCooldown = 30
|
||||||
|
|
||||||
|
#
|
||||||
|
#
|
||||||
|
#
|
||||||
|
####################################################################################################
|
||||||
|
|
||||||
####################################################################################################
|
####################################################################################################
|
||||||
# CHEATS
|
# CHEATS
|
||||||
#
|
#
|
||||||
|
|||||||
@@ -0,0 +1,35 @@
|
|||||||
|
DELETE FROM ai_playerbot_texts
|
||||||
|
WHERE name IN (
|
||||||
|
'rp_missing_reagent_greater_blessing',
|
||||||
|
'rp_missing_reagent_gift_of_the_wild',
|
||||||
|
'rp_missing_reagent_arcane_brilliance',
|
||||||
|
'rp_missing_reagent_generic'
|
||||||
|
);
|
||||||
|
|
||||||
|
DELETE FROM ai_playerbot_texts_chance
|
||||||
|
WHERE name IN (
|
||||||
|
'rp_missing_reagent_greater_blessing',
|
||||||
|
'rp_missing_reagent_gift_of_the_wild',
|
||||||
|
'rp_missing_reagent_arcane_brilliance',
|
||||||
|
'rp_missing_reagent_generic'
|
||||||
|
);
|
||||||
|
|
||||||
|
INSERT INTO ai_playerbot_texts (name, text, say_type, reply_type, text_loc2) VALUES
|
||||||
|
('rp_missing_reagent_greater_blessing',
|
||||||
|
'By the Light... I forgot my Symbols of Kings. We’ll make do with %base_spell!', 0, 0,
|
||||||
|
'Par la Lumière... J''ai oublié mes Symboles du roi. On se contentera de %base_spell !'),
|
||||||
|
('rp_missing_reagent_gift_of_the_wild',
|
||||||
|
'Nature is generous, my bags are not... out of herbs for %group_spell. Take %base_spell for now!', 0, 0,
|
||||||
|
'La nature est généreuse, pas mes sacs... plus d''herbes pour %group_spell. Prenez %base_spell pour l''instant !'),
|
||||||
|
('rp_missing_reagent_arcane_brilliance',
|
||||||
|
'Out of Arcane Powder... %group_spell will have to wait. Casting %base_spell!', 0, 0,
|
||||||
|
'Plus de poudre des arcanes... %group_spell attendra. Je lance %base_spell !'),
|
||||||
|
('rp_missing_reagent_generic',
|
||||||
|
'Oops, I’m out of components for %group_spell. We’ll go with %base_spell!', 0, 0,
|
||||||
|
'Oups, je n''ai plus de composants pour %group_spell. On fera avec %base_spell !');
|
||||||
|
|
||||||
|
INSERT INTO ai_playerbot_texts_chance (name, probability) VALUES
|
||||||
|
('rp_missing_reagent_greater_blessing', 100),
|
||||||
|
('rp_missing_reagent_gift_of_the_wild', 100),
|
||||||
|
('rp_missing_reagent_arcane_brilliance', 100),
|
||||||
|
('rp_missing_reagent_generic', 100);
|
||||||
@@ -81,6 +81,9 @@ bool PlayerbotAIConfig::Initialize()
|
|||||||
sitDelay = sConfigMgr->GetOption<int32>("AiPlayerbot.SitDelay", 30000);
|
sitDelay = sConfigMgr->GetOption<int32>("AiPlayerbot.SitDelay", 30000);
|
||||||
returnDelay = sConfigMgr->GetOption<int32>("AiPlayerbot.ReturnDelay", 7000);
|
returnDelay = sConfigMgr->GetOption<int32>("AiPlayerbot.ReturnDelay", 7000);
|
||||||
lootDelay = sConfigMgr->GetOption<int32>("AiPlayerbot.LootDelay", 1000);
|
lootDelay = sConfigMgr->GetOption<int32>("AiPlayerbot.LootDelay", 1000);
|
||||||
|
// Buff system
|
||||||
|
minBotsForGreaterBuff = sConfigMgr->GetOption<int32>("AiPlayerbot.MinBotsForGreaterBuff", 3);
|
||||||
|
rpWarningCooldown = sConfigMgr->GetOption<int32>("AiPlayerbot.RPWarningCooldown", 30);
|
||||||
disabledWithoutRealPlayerLoginDelay = sConfigMgr->GetOption<int32>("AiPlayerbot.DisabledWithoutRealPlayerLoginDelay", 30);
|
disabledWithoutRealPlayerLoginDelay = sConfigMgr->GetOption<int32>("AiPlayerbot.DisabledWithoutRealPlayerLoginDelay", 30);
|
||||||
disabledWithoutRealPlayerLogoutDelay = sConfigMgr->GetOption<int32>("AiPlayerbot.DisabledWithoutRealPlayerLogoutDelay", 300);
|
disabledWithoutRealPlayerLogoutDelay = sConfigMgr->GetOption<int32>("AiPlayerbot.DisabledWithoutRealPlayerLogoutDelay", 300);
|
||||||
|
|
||||||
|
|||||||
@@ -139,6 +139,12 @@ public:
|
|||||||
uint32 disabledWithoutRealPlayerLoginDelay, disabledWithoutRealPlayerLogoutDelay;
|
uint32 disabledWithoutRealPlayerLoginDelay, disabledWithoutRealPlayerLogoutDelay;
|
||||||
bool randomBotJoinLfg;
|
bool randomBotJoinLfg;
|
||||||
|
|
||||||
|
// Buff system
|
||||||
|
// Min group size to use Greater buffs (Paladin, Mage, Druid). Default: 3
|
||||||
|
int32 minBotsForGreaterBuff;
|
||||||
|
// Cooldown (seconds) between reagent-missing RP warnings, per bot & per buff. Default: 30
|
||||||
|
int32 rpWarningCooldown;
|
||||||
|
|
||||||
// chat
|
// chat
|
||||||
bool randomBotTalk;
|
bool randomBotTalk;
|
||||||
bool randomBotEmote;
|
bool randomBotEmote;
|
||||||
|
|||||||
153
src/strategy/actions/GenericBuffUtils.cpp
Normal file
153
src/strategy/actions/GenericBuffUtils.cpp
Normal file
@@ -0,0 +1,153 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2016+ AzerothCore <www.azerothcore.org>, released under GNU GPL v2 license, you may redistribute it
|
||||||
|
* and/or modify it under version 2 of the License, or (at your option), any later version.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "GenericBuffUtils.h"
|
||||||
|
#include "PlayerbotAIConfig.h"
|
||||||
|
|
||||||
|
#include <map>
|
||||||
|
|
||||||
|
#include "Player.h"
|
||||||
|
#include "Group.h"
|
||||||
|
#include "SpellMgr.h"
|
||||||
|
#include "Chat.h"
|
||||||
|
#include "PlayerbotAI.h"
|
||||||
|
#include "ServerFacade.h"
|
||||||
|
#include "AiObjectContext.h"
|
||||||
|
#include "Value.h"
|
||||||
|
#include "Config.h"
|
||||||
|
#include "PlayerbotTextMgr.h"
|
||||||
|
|
||||||
|
namespace ai::buff
|
||||||
|
{
|
||||||
|
std::string MakeAuraQualifierForBuff(std::string const& name)
|
||||||
|
{
|
||||||
|
// Paladin
|
||||||
|
if (name == "blessing of kings") return "blessing of kings,greater blessing of kings";
|
||||||
|
if (name == "blessing of might") return "blessing of might,greater blessing of might";
|
||||||
|
if (name == "blessing of wisdom") return "blessing of wisdom,greater blessing of wisdom";
|
||||||
|
if (name == "blessing of sanctuary") return "blessing of sanctuary,greater blessing of sanctuary";
|
||||||
|
// Druid
|
||||||
|
if (name == "mark of the wild") return "mark of the wild,gift of the wild";
|
||||||
|
// Mage
|
||||||
|
if (name == "arcane intellect") return "arcane intellect,arcane brilliance";
|
||||||
|
// Priest
|
||||||
|
if (name == "power word: fortitude") return "power word: fortitude,prayer of fortitude";
|
||||||
|
return name;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string GroupVariantFor(std::string const& name)
|
||||||
|
{
|
||||||
|
// Paladin
|
||||||
|
if (name == "blessing of kings") return "greater blessing of kings";
|
||||||
|
if (name == "blessing of might") return "greater blessing of might";
|
||||||
|
if (name == "blessing of wisdom") return "greater blessing of wisdom";
|
||||||
|
if (name == "blessing of sanctuary") return "greater blessing of sanctuary";
|
||||||
|
// Druid
|
||||||
|
if (name == "mark of the wild") return "gift of the wild";
|
||||||
|
// Mage
|
||||||
|
if (name == "arcane intellect") return "arcane brilliance";
|
||||||
|
// Priest
|
||||||
|
if (name == "power word: fortitude") return "prayer of fortitude";
|
||||||
|
|
||||||
|
return std::string();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool HasRequiredReagents(Player* bot, uint32 spellId)
|
||||||
|
{
|
||||||
|
if (!spellId)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if (SpellInfo const* info = sSpellMgr->GetSpellInfo(spellId))
|
||||||
|
{ for (int i = 0; i < MAX_SPELL_REAGENTS; ++i)
|
||||||
|
{
|
||||||
|
if (info->Reagent[i] > 0)
|
||||||
|
{
|
||||||
|
uint32 const itemId = info->Reagent[i];
|
||||||
|
int32 const need = info->ReagentCount[i];
|
||||||
|
if ((int32)bot->GetItemCount(itemId, false) < need)
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// No reagent required
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string UpgradeToGroupIfAppropriate(
|
||||||
|
Player* bot,
|
||||||
|
PlayerbotAI* botAI,
|
||||||
|
std::string const& baseName,
|
||||||
|
bool announceOnMissing,
|
||||||
|
std::function<void(std::string const&)> announce)
|
||||||
|
{
|
||||||
|
std::string castName = baseName; Group* g = bot->GetGroup();
|
||||||
|
if (!g || g->GetMembersCount() < static_cast<uint32>(sPlayerbotAIConfig->minBotsForGreaterBuff))
|
||||||
|
return castName; // Group too small: stay in solo mode
|
||||||
|
|
||||||
|
if (std::string const groupName = GroupVariantFor(baseName); !groupName.empty())
|
||||||
|
{
|
||||||
|
uint32 const groupVariantSpellId = botAI->GetAiObjectContext()
|
||||||
|
->GetValue<uint32>("spell id", groupName)->Get();
|
||||||
|
|
||||||
|
// We check usefulness on the **basic** buff (not the greater version),
|
||||||
|
// because "spell cast useful" may return false for the greater variant.
|
||||||
|
bool const usefulBase = botAI->GetAiObjectContext()
|
||||||
|
->GetValue<bool>("spell cast useful", baseName)->Get();
|
||||||
|
|
||||||
|
if (groupVariantSpellId && HasRequiredReagents(bot, groupVariantSpellId))
|
||||||
|
{
|
||||||
|
// Learned + reagents OK -> switch to greater
|
||||||
|
return groupName;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Missing reagents -> announce if (a) greater is known, (b) base buff is useful,
|
||||||
|
// (c) announce was requested, (d) a callback is provided.
|
||||||
|
if (announceOnMissing && groupVariantSpellId && usefulBase && announce)
|
||||||
|
{
|
||||||
|
static std::map<std::pair<uint32, std::string>, time_t> s_lastWarn; // par bot & par buff
|
||||||
|
time_t now = std::time(nullptr);
|
||||||
|
uint32 botLow = static_cast<uint32>(bot->GetGUID().GetCounter());
|
||||||
|
time_t& last = s_lastWarn[ std::make_pair(botLow, groupName) ];
|
||||||
|
if (!last || now - last >= sPlayerbotAIConfig->rpWarningCooldown) // Configurable anti-spam
|
||||||
|
{
|
||||||
|
// DB Key choice in regard of the buff
|
||||||
|
std::string key;
|
||||||
|
if (groupName.find("greater blessing") != std::string::npos)
|
||||||
|
key = "rp_missing_reagent_greater_blessing";
|
||||||
|
else if (groupName == "gift of the wild")
|
||||||
|
key = "rp_missing_reagent_gift_of_the_wild";
|
||||||
|
else if (groupName == "arcane brilliance")
|
||||||
|
key = "rp_missing_reagent_arcane_brilliance";
|
||||||
|
else
|
||||||
|
key = "rp_missing_reagent_generic";
|
||||||
|
|
||||||
|
// Placeholders
|
||||||
|
std::map<std::string, std::string> ph;
|
||||||
|
ph["%group_spell"] = groupName;
|
||||||
|
ph["%base_spell"] = baseName;
|
||||||
|
|
||||||
|
// Respecte ai_playerbot_texts_chance if present
|
||||||
|
std::string rp;
|
||||||
|
bool got = sPlayerbotTextMgr->GetBotText(key, rp, ph);
|
||||||
|
if (got && !rp.empty())
|
||||||
|
{
|
||||||
|
announce(rp);
|
||||||
|
last = now;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// Minimal Fallback
|
||||||
|
std::ostringstream oss;
|
||||||
|
oss << "Out of components for " << groupName << ". Using " << baseName << "!";
|
||||||
|
announce(oss.str());
|
||||||
|
last = now;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return castName;
|
||||||
|
}
|
||||||
|
}
|
||||||
64
src/strategy/actions/GenericBuffUtils.h
Normal file
64
src/strategy/actions/GenericBuffUtils.h
Normal file
@@ -0,0 +1,64 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2016+ AzerothCore <www.azerothcore.org>, released under GNU GPL v2 license, you may redistribute it
|
||||||
|
* and/or modify it under version 2 of the License, or (at your option), any later version.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
#include <functional>
|
||||||
|
#include "Common.h"
|
||||||
|
#include "Group.h"
|
||||||
|
#include "Chat.h"
|
||||||
|
#include "Language.h"
|
||||||
|
|
||||||
|
class Player;
|
||||||
|
class PlayerbotAI;
|
||||||
|
|
||||||
|
namespace ai::buff
|
||||||
|
{
|
||||||
|
|
||||||
|
// Build an aura qualifier "single + greater" to avoid double-buffing
|
||||||
|
std::string MakeAuraQualifierForBuff(std::string const& name);
|
||||||
|
|
||||||
|
// Returns the group spell name for a given single-target buff.
|
||||||
|
// If no group equivalent exists, returns "".
|
||||||
|
std::string GroupVariantFor(std::string const& name);
|
||||||
|
|
||||||
|
// Checks if the bot has the required reagents to cast a spell (by its spellId).
|
||||||
|
// Returns false if the spellId is invalid.
|
||||||
|
bool HasRequiredReagents(Player* bot, uint32 spellId);
|
||||||
|
|
||||||
|
|
||||||
|
// Applies the "switch to group buff" policy if: the bot is in a group of size x+,
|
||||||
|
// the group variant is known/useful, and reagents are available. Otherwise, returns baseName.
|
||||||
|
// If announceOnMissing == true and reagents are missing, calls the 'announce' callback
|
||||||
|
// (if provided) to notify the party/raid.
|
||||||
|
std::string UpgradeToGroupIfAppropriate(
|
||||||
|
Player* bot,
|
||||||
|
PlayerbotAI* botAI,
|
||||||
|
std::string const& baseName,
|
||||||
|
bool announceOnMissing = false,
|
||||||
|
std::function<void(std::string const&)> announce = {}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
namespace ai::chat {
|
||||||
|
inline std::function<void(std::string const&)> MakeGroupAnnouncer(Player* me)
|
||||||
|
{
|
||||||
|
return [me](std::string const& msg)
|
||||||
|
{
|
||||||
|
if (Group* g = me->GetGroup())
|
||||||
|
{
|
||||||
|
WorldPacket data;
|
||||||
|
ChatMsg type = g->isRaidGroup() ? CHAT_MSG_RAID : CHAT_MSG_PARTY;
|
||||||
|
ChatHandler::BuildChatPacket(data, type, LANG_UNIVERSAL, me, /*receiver=*/nullptr, msg.c_str());
|
||||||
|
g->BroadcastPacket(&data, true, -1, me->GetGUID());
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
me->Say(msg, LANG_UNIVERSAL);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -5,6 +5,8 @@
|
|||||||
|
|
||||||
#include "GenericSpellActions.h"
|
#include "GenericSpellActions.h"
|
||||||
|
|
||||||
|
#include <ctime>
|
||||||
|
|
||||||
#include "Event.h"
|
#include "Event.h"
|
||||||
#include "ItemTemplate.h"
|
#include "ItemTemplate.h"
|
||||||
#include "ObjectDefines.h"
|
#include "ObjectDefines.h"
|
||||||
@@ -13,6 +15,14 @@
|
|||||||
#include "Playerbots.h"
|
#include "Playerbots.h"
|
||||||
#include "ServerFacade.h"
|
#include "ServerFacade.h"
|
||||||
#include "WorldPacket.h"
|
#include "WorldPacket.h"
|
||||||
|
#include "Group.h"
|
||||||
|
#include "Chat.h"
|
||||||
|
#include "Language.h"
|
||||||
|
#include "GenericBuffUtils.h"
|
||||||
|
#include "PlayerbotAI.h"
|
||||||
|
|
||||||
|
using ai::buff::MakeAuraQualifierForBuff;
|
||||||
|
using ai::buff::UpgradeToGroupIfAppropriate;
|
||||||
|
|
||||||
CastSpellAction::CastSpellAction(PlayerbotAI* botAI, std::string const spell)
|
CastSpellAction::CastSpellAction(PlayerbotAI* botAI, std::string const spell)
|
||||||
: Action(botAI, spell), range(botAI->GetRange("spell")), spell(spell)
|
: Action(botAI, spell), range(botAI->GetRange("spell")), spell(spell)
|
||||||
@@ -216,11 +226,24 @@ Value<Unit*>* CurePartyMemberAction::GetTargetValue()
|
|||||||
return context->GetValue<Unit*>("party member to dispel", dispelType);
|
return context->GetValue<Unit*>("party member to dispel", dispelType);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Make Bots Paladin, druid, mage use the greater buff rank spell
|
||||||
|
// TODO Priest doen't verify il he have components
|
||||||
Value<Unit*>* BuffOnPartyAction::GetTargetValue()
|
Value<Unit*>* BuffOnPartyAction::GetTargetValue()
|
||||||
{
|
{
|
||||||
return context->GetValue<Unit*>("party member without aura", spell);
|
return context->GetValue<Unit*>("party member without aura", MakeAuraQualifierForBuff(spell));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool BuffOnPartyAction::Execute(Event event)
|
||||||
|
{
|
||||||
|
std::string castName = spell; // default = mono
|
||||||
|
|
||||||
|
auto SendGroupRP = ai::chat::MakeGroupAnnouncer(bot);
|
||||||
|
castName = ai::buff::UpgradeToGroupIfAppropriate(bot, botAI, castName, /*announceOnMissing=*/true, SendGroupRP);
|
||||||
|
|
||||||
|
return botAI->CastSpell(castName, GetTarget());
|
||||||
|
}
|
||||||
|
// End greater buff fix
|
||||||
|
|
||||||
CastShootAction::CastShootAction(PlayerbotAI* botAI) : CastSpellAction(botAI, "shoot")
|
CastShootAction::CastShootAction(PlayerbotAI* botAI) : CastSpellAction(botAI, "shoot")
|
||||||
{
|
{
|
||||||
if (Item* const pItem = bot->GetItemByPos(INVENTORY_SLOT_BAG_0, EQUIPMENT_SLOT_RANGED))
|
if (Item* const pItem = bot->GetItemByPos(INVENTORY_SLOT_BAG_0, EQUIPMENT_SLOT_RANGED))
|
||||||
|
|||||||
@@ -215,17 +215,18 @@ protected:
|
|||||||
uint32 dispelType;
|
uint32 dispelType;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// Make Bots Paladin, druid, mage use the greater buff rank spell
|
||||||
class BuffOnPartyAction : public CastBuffSpellAction, public PartyMemberActionNameSupport
|
class BuffOnPartyAction : public CastBuffSpellAction, public PartyMemberActionNameSupport
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
BuffOnPartyAction(PlayerbotAI* botAI, std::string const spell)
|
BuffOnPartyAction(PlayerbotAI* botAI, std::string const spell)
|
||||||
: CastBuffSpellAction(botAI, spell), PartyMemberActionNameSupport(spell)
|
: CastBuffSpellAction(botAI, spell), PartyMemberActionNameSupport(spell) { }
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
Value<Unit*>* GetTargetValue() override;
|
Value<Unit*>* GetTargetValue() override;
|
||||||
|
bool Execute(Event event) override;
|
||||||
std::string const getName() override { return PartyMemberActionNameSupport::getName(); }
|
std::string const getName() override { return PartyMemberActionNameSupport::getName(); }
|
||||||
};
|
};
|
||||||
|
// End Fix
|
||||||
|
|
||||||
class CastShootAction : public CastSpellAction
|
class CastShootAction : public CastSpellAction
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -13,6 +13,53 @@
|
|||||||
#include "Playerbots.h"
|
#include "Playerbots.h"
|
||||||
#include "SharedDefines.h"
|
#include "SharedDefines.h"
|
||||||
#include "../../../../../src/server/scripts/Spells/spell_generic.cpp"
|
#include "../../../../../src/server/scripts/Spells/spell_generic.cpp"
|
||||||
|
#include "GenericBuffUtils.h"
|
||||||
|
#include "Config.h"
|
||||||
|
#include "Group.h"
|
||||||
|
#include "ObjectAccessor.h"
|
||||||
|
|
||||||
|
using ai::buff::MakeAuraQualifierForBuff;
|
||||||
|
using ai::buff::UpgradeToGroupIfAppropriate;
|
||||||
|
|
||||||
|
// Helper : detect tank role on the target (player bot or not) return true if spec is tank or if the bot have tank strategies (bear/tank/tank face).
|
||||||
|
static inline bool IsTankRole(Player* p)
|
||||||
|
{
|
||||||
|
if (!p) return false;
|
||||||
|
if (p->HasTankSpec())
|
||||||
|
return true;
|
||||||
|
if (PlayerbotAI* otherAI = GET_PLAYERBOT_AI(p))
|
||||||
|
{
|
||||||
|
if (otherAI->HasStrategy("tank", BOT_STATE_NON_COMBAT) ||
|
||||||
|
otherAI->HasStrategy("tank", BOT_STATE_COMBAT) ||
|
||||||
|
otherAI->HasStrategy("tank face", BOT_STATE_NON_COMBAT) ||
|
||||||
|
otherAI->HasStrategy("tank face", BOT_STATE_COMBAT) ||
|
||||||
|
otherAI->HasStrategy("bear", BOT_STATE_NON_COMBAT) ||
|
||||||
|
otherAI->HasStrategy("bear", BOT_STATE_COMBAT))
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Added for solo paladin patch : determine if he's the only paladin on party
|
||||||
|
static inline bool IsOnlyPaladinInGroup(Player* bot)
|
||||||
|
{
|
||||||
|
if (!bot) return false;
|
||||||
|
Group* g = bot->GetGroup();
|
||||||
|
if (!g) return true; // solo
|
||||||
|
uint32 pals = 0u;
|
||||||
|
for (GroupReference* r = g->GetFirstMember(); r; r = r->next())
|
||||||
|
{
|
||||||
|
Player* p = r->GetSource();
|
||||||
|
if (!p || !p->IsInWorld()) continue;
|
||||||
|
if (p->getClass() == CLASS_PALADIN) ++pals;
|
||||||
|
}
|
||||||
|
return pals == 1u;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline bool GroupHasTankOfClass(Group* g, uint8 classId)
|
||||||
|
{
|
||||||
|
return GroupHasTankOfClass(g, static_cast<Classes>(classId));
|
||||||
|
}
|
||||||
|
|
||||||
inline std::string const GetActualBlessingOfMight(Unit* target)
|
inline std::string const GetActualBlessingOfMight(Unit* target)
|
||||||
{
|
{
|
||||||
@@ -92,24 +139,32 @@ inline std::string const GetActualBlessingOfWisdom(Unit* target)
|
|||||||
|
|
||||||
inline std::string const GetActualBlessingOfSanctuary(Unit* target, Player* bot)
|
inline std::string const GetActualBlessingOfSanctuary(Unit* target, Player* bot)
|
||||||
{
|
{
|
||||||
Player* targetPlayer = target->ToPlayer();
|
if (!bot->HasSpell(SPELL_BLESSING_OF_SANCTUARY))
|
||||||
|
return "";
|
||||||
|
|
||||||
if (!targetPlayer)
|
Player* tp = target->ToPlayer();
|
||||||
|
if (!tp)
|
||||||
|
return "";
|
||||||
|
|
||||||
|
if (auto* ai = GET_PLAYERBOT_AI(bot))
|
||||||
{
|
{
|
||||||
return "blessing of sanctuary";
|
if (Unit* mt = ai->GetAiObjectContext()->GetValue<Unit*>("main tank")->Get())
|
||||||
|
{
|
||||||
|
if (mt == target)
|
||||||
|
return "blessing of sanctuary";
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (targetPlayer->HasTankSpec() && bot->HasSpell(SPELL_BLESSING_OF_SANCTUARY))
|
if (tp->HasTankSpec())
|
||||||
{
|
|
||||||
return "blessing of sanctuary";
|
return "blessing of sanctuary";
|
||||||
}
|
|
||||||
|
|
||||||
return "blessing of kings";
|
return "";
|
||||||
}
|
}
|
||||||
|
|
||||||
Value<Unit*>* CastBlessingOnPartyAction::GetTargetValue()
|
Value<Unit*>* CastBlessingOnPartyAction::GetTargetValue()
|
||||||
{
|
{
|
||||||
return context->GetValue<Unit*>("party member without aura", name);
|
|
||||||
|
return context->GetValue<Unit*>("party member without aura", MakeAuraQualifierForBuff(spell));
|
||||||
}
|
}
|
||||||
|
|
||||||
bool CastBlessingOfMightAction::Execute(Event event)
|
bool CastBlessingOfMightAction::Execute(Event event)
|
||||||
@@ -118,12 +173,19 @@ bool CastBlessingOfMightAction::Execute(Event event)
|
|||||||
if (!target)
|
if (!target)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
return botAI->CastSpell(GetActualBlessingOfMight(target), target);
|
std::string castName = GetActualBlessingOfMight(target);
|
||||||
|
auto RP = ai::chat::MakeGroupAnnouncer(bot);
|
||||||
|
|
||||||
|
castName = ai::buff::UpgradeToGroupIfAppropriate(bot, botAI, castName, /*announceOnMissing=*/true, RP);
|
||||||
|
return botAI->CastSpell(castName, target);
|
||||||
}
|
}
|
||||||
|
|
||||||
Value<Unit*>* CastBlessingOfMightOnPartyAction::GetTargetValue()
|
Value<Unit*>* CastBlessingOfMightOnPartyAction::GetTargetValue()
|
||||||
{
|
{
|
||||||
return context->GetValue<Unit*>("party member without aura", "blessing of might,blessing of wisdom");
|
return context->GetValue<Unit*>(
|
||||||
|
"party member without aura",
|
||||||
|
"blessing of might,greater blessing of might,blessing of wisdom,greater blessing of wisdom,blessing of sanctuary,greater blessing of sanctuary"
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool CastBlessingOfMightOnPartyAction::Execute(Event event)
|
bool CastBlessingOfMightOnPartyAction::Execute(Event event)
|
||||||
@@ -132,7 +194,11 @@ bool CastBlessingOfMightOnPartyAction::Execute(Event event)
|
|||||||
if (!target)
|
if (!target)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
return botAI->CastSpell(GetActualBlessingOfMight(target), target);
|
std::string castName = GetActualBlessingOfMight(target);
|
||||||
|
auto RP = ai::chat::MakeGroupAnnouncer(bot);
|
||||||
|
|
||||||
|
castName = ai::buff::UpgradeToGroupIfAppropriate(bot, botAI, castName, /*announceOnMissing=*/true, RP);
|
||||||
|
return botAI->CastSpell(castName, target);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool CastBlessingOfWisdomAction::Execute(Event event)
|
bool CastBlessingOfWisdomAction::Execute(Event event)
|
||||||
@@ -141,12 +207,19 @@ bool CastBlessingOfWisdomAction::Execute(Event event)
|
|||||||
if (!target)
|
if (!target)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
return botAI->CastSpell(GetActualBlessingOfWisdom(target), target);
|
std::string castName = GetActualBlessingOfWisdom(target);
|
||||||
|
auto RP = ai::chat::MakeGroupAnnouncer(bot);
|
||||||
|
|
||||||
|
castName = ai::buff::UpgradeToGroupIfAppropriate(bot, botAI, castName, /*announceOnMissing=*/true, RP);
|
||||||
|
return botAI->CastSpell(castName, target);
|
||||||
}
|
}
|
||||||
|
|
||||||
Value<Unit*>* CastBlessingOfWisdomOnPartyAction::GetTargetValue()
|
Value<Unit*>* CastBlessingOfWisdomOnPartyAction::GetTargetValue()
|
||||||
{
|
{
|
||||||
return context->GetValue<Unit*>("party member without aura", "blessing of might,blessing of wisdom");
|
return context->GetValue<Unit*>(
|
||||||
|
"party member without aura",
|
||||||
|
"blessing of wisdom,greater blessing of wisdom,blessing of might,greater blessing of might,blessing of sanctuary,greater blessing of sanctuary"
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool CastBlessingOfWisdomOnPartyAction::Execute(Event event)
|
bool CastBlessingOfWisdomOnPartyAction::Execute(Event event)
|
||||||
@@ -155,21 +228,250 @@ bool CastBlessingOfWisdomOnPartyAction::Execute(Event event)
|
|||||||
if (!target)
|
if (!target)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
return botAI->CastSpell(GetActualBlessingOfWisdom(target), target);
|
Player* targetPlayer = target->ToPlayer();
|
||||||
|
|
||||||
|
if (Group* g = bot->GetGroup())
|
||||||
|
if (targetPlayer && !g->IsMember(targetPlayer->GetGUID()))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if (botAI->HasStrategy("bmana", BOT_STATE_NON_COMBAT) &&
|
||||||
|
targetPlayer && IsTankRole(targetPlayer))
|
||||||
|
{
|
||||||
|
LOG_DEBUG("playerbots", "[Wisdom/bmana] Skip tank {} (Kings only)", target->GetName());
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string castName = GetActualBlessingOfWisdom(target);
|
||||||
|
if (castName.empty())
|
||||||
|
return false;
|
||||||
|
|
||||||
|
auto RP = ai::chat::MakeGroupAnnouncer(bot);
|
||||||
|
castName = ai::buff::UpgradeToGroupIfAppropriate(bot, botAI, castName, /*announceOnMissing=*/true, RP);
|
||||||
|
return botAI->CastSpell(castName, target);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
Value<Unit*>* CastBlessingOfSanctuaryOnPartyAction::GetTargetValue()
|
Value<Unit*>* CastBlessingOfSanctuaryOnPartyAction::GetTargetValue()
|
||||||
{
|
{
|
||||||
return context->GetValue<Unit*>("party member without aura", "blessing of sanctuary,blessing of kings");
|
return context->GetValue<Unit*>(
|
||||||
|
"party member without aura",
|
||||||
|
"blessing of sanctuary,greater blessing of sanctuary"
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool CastBlessingOfSanctuaryOnPartyAction::Execute(Event event)
|
bool CastBlessingOfSanctuaryOnPartyAction::Execute(Event event)
|
||||||
|
{
|
||||||
|
if (!bot->HasSpell(SPELL_BLESSING_OF_SANCTUARY))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
Unit* target = GetTarget();
|
||||||
|
if (!target)
|
||||||
|
{
|
||||||
|
// Fallback: GetTarget() can be null if no one needs a buff.
|
||||||
|
// Keep a valid pointer for the checks/logs that follow.
|
||||||
|
target = bot;
|
||||||
|
}
|
||||||
|
|
||||||
|
Player* targetPlayer = target ? target->ToPlayer() : nullptr;
|
||||||
|
|
||||||
|
// Small helpers to check relevant auras
|
||||||
|
const auto HasKingsAura = [&](Unit* u) -> bool {
|
||||||
|
return botAI->HasAura("blessing of kings", u) || botAI->HasAura("greater blessing of kings", u);
|
||||||
|
};
|
||||||
|
const auto HasSanctAura = [&](Unit* u) -> bool {
|
||||||
|
return botAI->HasAura("blessing of sanctuary", u) || botAI->HasAura("greater blessing of sanctuary", u);
|
||||||
|
};
|
||||||
|
|
||||||
|
if (Group* g = bot->GetGroup())
|
||||||
|
{
|
||||||
|
if (targetPlayer && !g->IsMember(targetPlayer->GetGUID()))
|
||||||
|
{
|
||||||
|
LOG_DEBUG("playerbots", "[Sanct] Initial target not in group, ignoring");
|
||||||
|
target = bot;
|
||||||
|
targetPlayer = bot->ToPlayer();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (Player* self = bot->ToPlayer())
|
||||||
|
{
|
||||||
|
bool selfHasSanct = HasSanctAura(self);
|
||||||
|
bool needSelf = IsTankRole(self) && !selfHasSanct;
|
||||||
|
|
||||||
|
LOG_DEBUG("playerbots", "[Sanct] {} isTank={} selfHasSanct={} needSelf={}",
|
||||||
|
bot->GetName(), IsTankRole(self), selfHasSanct, needSelf);
|
||||||
|
|
||||||
|
if (needSelf)
|
||||||
|
{
|
||||||
|
target = self;
|
||||||
|
targetPlayer = self;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Try to re-target a valid tank in group if needed
|
||||||
|
bool targetOk = false;
|
||||||
|
if (targetPlayer)
|
||||||
|
{
|
||||||
|
bool hasSanct = HasSanctAura(targetPlayer);
|
||||||
|
targetOk = IsTankRole(targetPlayer) && !hasSanct;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!targetOk)
|
||||||
|
{
|
||||||
|
if (Group* g = bot->GetGroup())
|
||||||
|
{
|
||||||
|
for (GroupReference* gref = g->GetFirstMember(); gref; gref = gref->next())
|
||||||
|
{
|
||||||
|
Player* p = gref->GetSource();
|
||||||
|
if (!p) continue;
|
||||||
|
if (!p->IsInWorld() || !p->IsAlive()) continue;
|
||||||
|
if (!IsTankRole(p)) continue;
|
||||||
|
|
||||||
|
bool hasSanct = HasSanctAura(p);
|
||||||
|
if (!hasSanct)
|
||||||
|
{
|
||||||
|
target = p; // prioritize this tank
|
||||||
|
targetPlayer = p;
|
||||||
|
targetOk = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
bool hasKings = HasKingsAura(target);
|
||||||
|
bool hasSanct = HasSanctAura(target);
|
||||||
|
bool knowSanct = bot->HasSpell(SPELL_BLESSING_OF_SANCTUARY);
|
||||||
|
LOG_DEBUG("playerbots", "[Sanct] Final target={} hasKings={} hasSanct={} knowSanct={}",
|
||||||
|
target->GetName(), hasKings, hasSanct, knowSanct);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string castName = GetActualBlessingOfSanctuary(target, bot);
|
||||||
|
// If internal logic didn't recognize the tank (e.g., bear druid), force single-target Sanctuary
|
||||||
|
if (castName.empty())
|
||||||
|
{
|
||||||
|
if (targetPlayer)
|
||||||
|
{
|
||||||
|
if (IsTankRole(targetPlayer))
|
||||||
|
castName = "blessing of sanctuary"; // force single-target
|
||||||
|
else
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (targetPlayer && !IsTankRole(targetPlayer))
|
||||||
|
{
|
||||||
|
auto RP = ai::chat::MakeGroupAnnouncer(bot);
|
||||||
|
castName = ai::buff::UpgradeToGroupIfAppropriate(bot, botAI, castName, /*announceOnMissing=*/true, RP);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
castName = "blessing of sanctuary";
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ok = botAI->CastSpell(castName, target);
|
||||||
|
LOG_DEBUG("playerbots", "[Sanct] Cast {} on {} result={}", castName, target->GetName(), ok);
|
||||||
|
return ok;
|
||||||
|
}
|
||||||
|
|
||||||
|
Value<Unit*>* CastBlessingOfKingsOnPartyAction::GetTargetValue()
|
||||||
|
{
|
||||||
|
return context->GetValue<Unit*>(
|
||||||
|
"party member without aura",
|
||||||
|
"blessing of kings,greater blessing of kings,blessing of sanctuary,greater blessing of sanctuary"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool CastBlessingOfKingsOnPartyAction::Execute(Event event)
|
||||||
{
|
{
|
||||||
Unit* target = GetTarget();
|
Unit* target = GetTarget();
|
||||||
if (!target)
|
if (!target)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
return botAI->CastSpell(GetActualBlessingOfSanctuary(target, bot), target);
|
Group* g = bot->GetGroup();
|
||||||
|
if (!g)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
// Added for patch solo paladin, never buff itself to not remove his sanctuary buff
|
||||||
|
if (botAI->HasStrategy("bstats", BOT_STATE_NON_COMBAT) && IsOnlyPaladinInGroup(bot))
|
||||||
|
{
|
||||||
|
if (target->GetGUID() == bot->GetGUID())
|
||||||
|
{
|
||||||
|
LOG_DEBUG("playerbots", "[Kings/bstats-solo] Skip self to keep Sanctuary on {}", bot->GetName());
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// End solo paladin patch
|
||||||
|
|
||||||
|
Player* targetPlayer = target->ToPlayer();
|
||||||
|
if (targetPlayer && !g->IsMember(targetPlayer->GetGUID()))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
const bool hasBmana = botAI->HasStrategy("bmana", BOT_STATE_NON_COMBAT);
|
||||||
|
const bool hasBstats = botAI->HasStrategy("bstats", BOT_STATE_NON_COMBAT);
|
||||||
|
|
||||||
|
if (hasBmana)
|
||||||
|
{
|
||||||
|
if (!targetPlayer || !IsTankRole(targetPlayer))
|
||||||
|
{
|
||||||
|
LOG_DEBUG("playerbots", "[Kings/bmana] Skip non-tank {}", target->GetName());
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (targetPlayer)
|
||||||
|
{
|
||||||
|
const bool isTank = IsTankRole(targetPlayer);
|
||||||
|
const bool hasSanctFromMe =
|
||||||
|
target->HasAura(SPELL_BLESSING_OF_SANCTUARY, bot->GetGUID()) ||
|
||||||
|
target->HasAura(SPELL_GREATER_BLESSING_OF_SANCTUARY, bot->GetGUID());
|
||||||
|
const bool hasSanctAny =
|
||||||
|
botAI->HasAura("blessing of sanctuary", target) ||
|
||||||
|
botAI->HasAura("greater blessing of sanctuary", target);
|
||||||
|
|
||||||
|
if (isTank && hasSanctFromMe)
|
||||||
|
{
|
||||||
|
LOG_DEBUG("playerbots", "[Kings] Skip: {} has my Sanctuary and is a tank", target->GetName());
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (hasBstats && isTank && hasSanctAny)
|
||||||
|
{
|
||||||
|
LOG_DEBUG("playerbots", "[Kings] Skip (bstats): {} already has Sanctuary and is a tank", target->GetName());
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string castName = "blessing of kings";
|
||||||
|
|
||||||
|
bool allowGreater = true;
|
||||||
|
|
||||||
|
if (hasBmana)
|
||||||
|
allowGreater = false;
|
||||||
|
|
||||||
|
if (allowGreater && hasBstats && targetPlayer)
|
||||||
|
{
|
||||||
|
switch (targetPlayer->getClass())
|
||||||
|
{
|
||||||
|
case CLASS_WARRIOR:
|
||||||
|
case CLASS_PALADIN:
|
||||||
|
case CLASS_DRUID:
|
||||||
|
case CLASS_DEATH_KNIGHT:
|
||||||
|
allowGreater = false;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (allowGreater)
|
||||||
|
{
|
||||||
|
auto RP = ai::chat::MakeGroupAnnouncer(bot);
|
||||||
|
castName = ai::buff::UpgradeToGroupIfAppropriate(bot, botAI, castName, /*announceOnMissing=*/true, RP);
|
||||||
|
}
|
||||||
|
|
||||||
|
return botAI->CastSpell(castName, target);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool CastSealSpellAction::isUseful() { return AI_VALUE2(bool, "combat", "self target"); }
|
bool CastSealSpellAction::isUseful() { return AI_VALUE2(bool, "combat", "self target"); }
|
||||||
|
|||||||
@@ -141,6 +141,8 @@ public:
|
|||||||
CastBlessingOfKingsOnPartyAction(PlayerbotAI* botAI) : CastBlessingOnPartyAction(botAI, "blessing of kings") {}
|
CastBlessingOfKingsOnPartyAction(PlayerbotAI* botAI) : CastBlessingOnPartyAction(botAI, "blessing of kings") {}
|
||||||
|
|
||||||
std::string const getName() override { return "blessing of kings on party"; }
|
std::string const getName() override { return "blessing of kings on party"; }
|
||||||
|
Value<Unit*>* GetTargetValue() override; // added for Sanctuary priority
|
||||||
|
bool Execute(Event event) override; // added for 2 paladins logic
|
||||||
};
|
};
|
||||||
|
|
||||||
class CastBlessingOfSanctuaryAction : public CastBuffSpellAction
|
class CastBlessingOfSanctuaryAction : public CastBuffSpellAction
|
||||||
|
|||||||
@@ -9,11 +9,11 @@
|
|||||||
|
|
||||||
void PaladinBuffManaStrategy::InitTriggers(std::vector<TriggerNode*>& triggers)
|
void PaladinBuffManaStrategy::InitTriggers(std::vector<TriggerNode*>& triggers)
|
||||||
{
|
{
|
||||||
triggers.push_back(
|
triggers.push_back(new TriggerNode("blessing of wisdom on party",
|
||||||
new TriggerNode("blessing of wisdom on party",
|
NextAction::array(0, new NextAction("blessing of wisdom on party", 11.0f), NULL)));
|
||||||
NextAction::array(0, new NextAction("blessing of wisdom on party", 11.0f), nullptr)));
|
|
||||||
// triggers.push_back(new TriggerNode("blessing", NextAction::array(0, new NextAction("blessing of wisdom",
|
triggers.push_back(new TriggerNode("blessing of kings on party",
|
||||||
// ACTION_HIGH + 8), nullptr)));
|
NextAction::array(0, new NextAction("blessing of kings on party", 10.5f), NULL)));
|
||||||
}
|
}
|
||||||
|
|
||||||
void PaladinBuffHealthStrategy::InitTriggers(std::vector<TriggerNode*>& triggers)
|
void PaladinBuffHealthStrategy::InitTriggers(std::vector<TriggerNode*>& triggers)
|
||||||
@@ -85,10 +85,14 @@ void PaladinBuffThreatStrategy::InitTriggers(std::vector<TriggerNode*>& triggers
|
|||||||
}
|
}
|
||||||
|
|
||||||
void PaladinBuffStatsStrategy::InitTriggers(std::vector<TriggerNode*>& triggers)
|
void PaladinBuffStatsStrategy::InitTriggers(std::vector<TriggerNode*>& triggers)
|
||||||
{
|
{
|
||||||
|
// First Sanctuary (prio > Kings)
|
||||||
|
triggers.push_back(
|
||||||
|
new TriggerNode("blessing of sanctuary on party",
|
||||||
|
NextAction::array(0, new NextAction("blessing of sanctuary on party", 12.0f), nullptr)));
|
||||||
|
|
||||||
|
// After Kings
|
||||||
triggers.push_back(
|
triggers.push_back(
|
||||||
new TriggerNode("blessing of kings on party",
|
new TriggerNode("blessing of kings on party",
|
||||||
NextAction::array(0, new NextAction("blessing of kings on party", 11.0f), nullptr)));
|
NextAction::array(0, new NextAction("blessing of kings on party", 11.0f), nullptr)));
|
||||||
// triggers.push_back(new TriggerNode("blessing", NextAction::array(0, new NextAction("blessing of kings",
|
|
||||||
// ACTION_HIGH + 8), nullptr)));
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -238,7 +238,7 @@ class BlessingOfSanctuaryOnPartyTrigger : public BuffOnPartyTrigger
|
|||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
BlessingOfSanctuaryOnPartyTrigger(PlayerbotAI* botAI)
|
BlessingOfSanctuaryOnPartyTrigger(PlayerbotAI* botAI)
|
||||||
: BuffOnPartyTrigger(botAI, "blessing of sanctuary,blessing of kings", 2 * 2000)
|
: BuffOnPartyTrigger(botAI, "blessing of sanctuary", 2 * 2000)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|||||||
Reference in New Issue
Block a user