diff --git a/src/strategy/paladin/BlessingManager.cpp b/src/strategy/paladin/BlessingManager.cpp new file mode 100644 index 00000000..d27c0c38 --- /dev/null +++ b/src/strategy/paladin/BlessingManager.cpp @@ -0,0 +1,332 @@ +#include "PlayerbotAI.h" +#include "Group.h" +#include "Player.h" +#include +#include +#include +#include + +// Minimal Blessing Type enum +enum GreaterBlessingType +{ + GREATER_BLESSING_OF_WISDOM, + GREATER_BLESSING_OF_MIGHT, + GREATER_BLESSING_OF_KINGS, + GREATER_BLESSING_OF_SANCTUARY +}; + +// A simple structure to hold which blessings each class should get, +// depending on how many Paladins are in the group. +static std::map>> BlessingTemplates = +{ + // 1 Paladin: everyone just gets Kings + { + 1, + { + { CLASS_WARRIOR, { GREATER_BLESSING_OF_KINGS } }, + { CLASS_PALADIN, { GREATER_BLESSING_OF_KINGS } }, + { CLASS_HUNTER, { GREATER_BLESSING_OF_KINGS } }, + { CLASS_ROGUE, { GREATER_BLESSING_OF_KINGS } }, + { CLASS_PRIEST, { GREATER_BLESSING_OF_KINGS } }, + { CLASS_DEATH_KNIGHT, { GREATER_BLESSING_OF_KINGS } }, + { CLASS_SHAMAN, { GREATER_BLESSING_OF_KINGS } }, + { CLASS_MAGE, { GREATER_BLESSING_OF_KINGS } }, + { CLASS_WARLOCK, { GREATER_BLESSING_OF_KINGS } }, + { CLASS_DRUID, { GREATER_BLESSING_OF_KINGS } } + } + }, + // 2 Paladins: physical classes prefer Might, casters prefer Wisdom, all get Kings + { + 2, + { + { CLASS_WARRIOR, { GREATER_BLESSING_OF_MIGHT, GREATER_BLESSING_OF_KINGS } }, + { CLASS_PALADIN, { GREATER_BLESSING_OF_WISDOM, GREATER_BLESSING_OF_KINGS } }, + { CLASS_HUNTER, { GREATER_BLESSING_OF_MIGHT, GREATER_BLESSING_OF_KINGS } }, + { CLASS_ROGUE, { GREATER_BLESSING_OF_MIGHT, GREATER_BLESSING_OF_KINGS } }, + { CLASS_PRIEST, { GREATER_BLESSING_OF_WISDOM, GREATER_BLESSING_OF_KINGS } }, + { CLASS_DEATH_KNIGHT, { GREATER_BLESSING_OF_MIGHT, GREATER_BLESSING_OF_KINGS } }, + { CLASS_SHAMAN, { GREATER_BLESSING_OF_WISDOM, GREATER_BLESSING_OF_KINGS } }, + { CLASS_MAGE, { GREATER_BLESSING_OF_WISDOM, GREATER_BLESSING_OF_KINGS } }, + { CLASS_WARLOCK, { GREATER_BLESSING_OF_WISDOM, GREATER_BLESSING_OF_KINGS } }, + { CLASS_DRUID, { GREATER_BLESSING_OF_WISDOM, GREATER_BLESSING_OF_KINGS } }, + } + }, + // 3 Paladins: might see some Sanctuary usage as well + { + 3, + { + { CLASS_WARRIOR, { GREATER_BLESSING_OF_MIGHT, GREATER_BLESSING_OF_SANCTUARY, GREATER_BLESSING_OF_KINGS } }, + { CLASS_PALADIN, { GREATER_BLESSING_OF_WISDOM, GREATER_BLESSING_OF_MIGHT, GREATER_BLESSING_OF_KINGS } }, + { CLASS_HUNTER, { GREATER_BLESSING_OF_MIGHT, GREATER_BLESSING_OF_WISDOM, GREATER_BLESSING_OF_KINGS } }, + { CLASS_ROGUE, { GREATER_BLESSING_OF_MIGHT, GREATER_BLESSING_OF_SANCTUARY, GREATER_BLESSING_OF_KINGS } }, + { CLASS_PRIEST, { GREATER_BLESSING_OF_WISDOM, GREATER_BLESSING_OF_SANCTUARY, GREATER_BLESSING_OF_KINGS } }, + { CLASS_DEATH_KNIGHT, { GREATER_BLESSING_OF_MIGHT, GREATER_BLESSING_OF_SANCTUARY, GREATER_BLESSING_OF_KINGS } }, + { CLASS_SHAMAN, { GREATER_BLESSING_OF_WISDOM, GREATER_BLESSING_OF_MIGHT, GREATER_BLESSING_OF_KINGS } }, + { CLASS_MAGE, { GREATER_BLESSING_OF_WISDOM, GREATER_BLESSING_OF_SANCTUARY, GREATER_BLESSING_OF_KINGS } }, + { CLASS_WARLOCK, { GREATER_BLESSING_OF_WISDOM, GREATER_BLESSING_OF_SANCTUARY, GREATER_BLESSING_OF_KINGS } }, + { CLASS_DRUID, { GREATER_BLESSING_OF_WISDOM, GREATER_BLESSING_OF_MIGHT, GREATER_BLESSING_OF_KINGS } }, + } + }, + // 4 Paladins: basically everything is on the table + { + 4, + { + { CLASS_WARRIOR, { GREATER_BLESSING_OF_WISDOM, GREATER_BLESSING_OF_MIGHT, GREATER_BLESSING_OF_SANCTUARY, GREATER_BLESSING_OF_KINGS } }, + { CLASS_PALADIN, { GREATER_BLESSING_OF_WISDOM, GREATER_BLESSING_OF_MIGHT, GREATER_BLESSING_OF_SANCTUARY, GREATER_BLESSING_OF_KINGS } }, + { CLASS_HUNTER, { GREATER_BLESSING_OF_WISDOM, GREATER_BLESSING_OF_MIGHT, GREATER_BLESSING_OF_SANCTUARY, GREATER_BLESSING_OF_KINGS } }, + { CLASS_ROGUE, { GREATER_BLESSING_OF_WISDOM, GREATER_BLESSING_OF_MIGHT, GREATER_BLESSING_OF_SANCTUARY, GREATER_BLESSING_OF_KINGS } }, + { CLASS_PRIEST, { GREATER_BLESSING_OF_WISDOM, GREATER_BLESSING_OF_MIGHT, GREATER_BLESSING_OF_SANCTUARY, GREATER_BLESSING_OF_KINGS } }, + { CLASS_DEATH_KNIGHT, { GREATER_BLESSING_OF_WISDOM, GREATER_BLESSING_OF_MIGHT, GREATER_BLESSING_OF_SANCTUARY, GREATER_BLESSING_OF_KINGS } }, + { CLASS_SHAMAN, { GREATER_BLESSING_OF_WISDOM, GREATER_BLESSING_OF_MIGHT, GREATER_BLESSING_OF_SANCTUARY, GREATER_BLESSING_OF_KINGS } }, + { CLASS_MAGE, { GREATER_BLESSING_OF_WISDOM, GREATER_BLESSING_OF_MIGHT, GREATER_BLESSING_OF_SANCTUARY, GREATER_BLESSING_OF_KINGS } }, + { CLASS_WARLOCK, { GREATER_BLESSING_OF_WISDOM, GREATER_BLESSING_OF_MIGHT, GREATER_BLESSING_OF_SANCTUARY, GREATER_BLESSING_OF_KINGS } }, + { CLASS_DRUID, { GREATER_BLESSING_OF_WISDOM, GREATER_BLESSING_OF_MIGHT, GREATER_BLESSING_OF_SANCTUARY, GREATER_BLESSING_OF_KINGS } } + } + } +}; + +// ------------------------------------------------------------------------- +// Simple helper to check if a Paladin has the required talent for the blessing +// (In your environment, replace HasTalent(SpellID, Spec) with the actual logic.) +// ------------------------------------------------------------------------- +static bool PaladinHasTalentForBlessing(Player* paladin, GreaterBlessingType blessing) +{ + switch (blessing) + { + case GREATER_BLESSING_OF_WISDOM: + // Improved Blessing of Wisdom (e.g., talent ID 20245) + return paladin->HasTalent(20245, paladin->GetActiveSpec()); + case GREATER_BLESSING_OF_MIGHT: + // Improved Blessing of Might (e.g., talent ID 20045) + return paladin->HasTalent(20045, paladin->GetActiveSpec()); + case GREATER_BLESSING_OF_SANCTUARY: + // Must have the Sanctuary talent (e.g., talent ID 20911) + return paladin->HasTalent(20911, paladin->GetActiveSpec()); + case GREATER_BLESSING_OF_KINGS: + // No talent required + return true; + default: + return false; + } +} + +// ------------------------------------------------------------------------- +// Gather all Paladins in the group/raid +// ------------------------------------------------------------------------- +static std::vector GetPaladinsInGroup(PlayerbotAI* botAI) +{ + std::vector paladins; + Player* bot = botAI->GetBot(); + + if (!bot) + return paladins; + + Group* group = bot->GetGroup(); + if (!group) + return paladins; + + for (GroupReference* ref = group->GetFirstMember(); ref; ref = ref->next()) + { + Player* member = ref->GetSource(); + if (member && member->IsInWorld() && member->getClass() == CLASS_PALADIN) + paladins.push_back(member); + } + + return paladins; +} + +// ------------------------------------------------------------------------- +// Core function: AssignBlessingsForGroup +// Returns a map from (Paladin Player*) -> (set of (classId -> assigned blessing)). +// The logic is “one greater blessing per class per paladin,” assigned in order +// of talent preference. +// ------------------------------------------------------------------------- +std::map> AssignBlessingsForGroup(PlayerbotAI* botAI) +{ + std::map> results; + + // Get relevant Paladins + std::vector paladins = GetPaladinsInGroup(botAI); + int numPaladins = std::min(static_cast(paladins.size()), 4); + if (numPaladins == 0) + { + // No paladins -> empty map + return results; + } + + // Find which template we’ll use + auto templIt = BlessingTemplates.find(numPaladins); + if (templIt == BlessingTemplates.end()) + { + // If no valid template, return empty + return results; + } + + auto& classToBlessings = templIt->second; + + // Categorize Paladins by which improved blessings they can cast + std::vector paladinsWithSanctuary; + std::vector paladinsWithMight; + std::vector paladinsWithWisdom; + std::vector paladinsWithoutImprovements; // can only cast Kings or unimproved Might/Wisdom + + for (Player* pal : paladins) + { + bool canSanctuary = PaladinHasTalentForBlessing(pal, GREATER_BLESSING_OF_SANCTUARY); + bool canMight = PaladinHasTalentForBlessing(pal, GREATER_BLESSING_OF_MIGHT); + bool canWisdom = PaladinHasTalentForBlessing(pal, GREATER_BLESSING_OF_WISDOM); + + if (canSanctuary) paladinsWithSanctuary.push_back(pal); + if (canMight) paladinsWithMight.push_back(pal); + if (canWisdom) paladinsWithWisdom.push_back(pal); + + if (!canSanctuary && !canMight && !canWisdom) + paladinsWithoutImprovements.push_back(pal); + } + + // Keep track of which class each Paladin has already assigned + // so we don't assign multiple blessings from the same Paladin to the same class. + std::map> paladinAssignedClasses; + + // Go through each class in the template, then each desired blessing, in order + for (auto& [classId, blessingsVec] : classToBlessings) + { + for (GreaterBlessingType b : blessingsVec) + { + Player* assignedPaladin = nullptr; + + // Attempt assignment depending on talent priority + switch (b) + { + case GREATER_BLESSING_OF_SANCTUARY: + { + // Try paladins with the Sanctuary talent + for (Player* pal : paladinsWithSanctuary) + { + if (!paladinAssignedClasses[pal].count(classId)) + { + assignedPaladin = pal; + paladinAssignedClasses[pal].insert(classId); + break; + } + } + break; + } + case GREATER_BLESSING_OF_MIGHT: + { + // First try paladins with Improved Might + for (Player* pal : paladinsWithMight) + { + if (!paladinAssignedClasses[pal].count(classId)) + { + assignedPaladin = pal; + paladinAssignedClasses[pal].insert(classId); + break; + } + } + // Otherwise, try “no improvement” paladins + if (!assignedPaladin) + { + for (Player* pal : paladinsWithoutImprovements) + { + if (!paladinAssignedClasses[pal].count(classId)) + { + assignedPaladin = pal; + paladinAssignedClasses[pal].insert(classId); + break; + } + } + } + // Lastly, if still unassigned, pick any Paladin + if (!assignedPaladin) + { + for (Player* pal : paladins) + { + if (!paladinAssignedClasses[pal].count(classId)) + { + assignedPaladin = pal; + paladinAssignedClasses[pal].insert(classId); + break; + } + } + } + break; + } + case GREATER_BLESSING_OF_WISDOM: + { + // First try paladins with Improved Wisdom + for (Player* pal : paladinsWithWisdom) + { + if (!paladinAssignedClasses[pal].count(classId)) + { + assignedPaladin = pal; + paladinAssignedClasses[pal].insert(classId); + break; + } + } + // Otherwise, try “no improvement” paladins + if (!assignedPaladin) + { + for (Player* pal : paladinsWithoutImprovements) + { + if (!paladinAssignedClasses[pal].count(classId)) + { + assignedPaladin = pal; + paladinAssignedClasses[pal].insert(classId); + break; + } + } + } + // Lastly, if still unassigned, pick any Paladin + if (!assignedPaladin) + { + for (Player* pal : paladins) + { + if (!paladinAssignedClasses[pal].count(classId)) + { + assignedPaladin = pal; + paladinAssignedClasses[pal].insert(classId); + break; + } + } + } + break; + } + case GREATER_BLESSING_OF_KINGS: + { + // Anyone can cast Kings, so pick a paladin who hasn’t assigned that class yet + // Try “no improvement” paladins first if you want them to focus on Kings + for (Player* pal : paladinsWithoutImprovements) + { + if (!paladinAssignedClasses[pal].count(classId)) + { + assignedPaladin = pal; + paladinAssignedClasses[pal].insert(classId); + break; + } + } + // If not found, pick any paladin + if (!assignedPaladin) + { + for (Player* pal : paladins) + { + if (!paladinAssignedClasses[pal].count(classId)) + { + assignedPaladin = pal; + paladinAssignedClasses[pal].insert(classId); + break; + } + } + } + break; + } + } + + // If we found a Paladin, record it in the results + if (assignedPaladin) + results[assignedPaladin][classId] = b; + } + } + + return results; // (Paladin -> (classId -> Blessing)) for the entire group +}