From 55a37c48eb61f95bc2da5be3387aa6e3e2cfde62 Mon Sep 17 00:00:00 2001 From: Wishmaster117 <140754794+Wishmaster117@users.noreply.github.com> Date: Sat, 26 Jul 2025 00:15:46 +0200 Subject: [PATCH] Add /w botname "glyphs" and "glyph equip" commands --- src/factory/PlayerbotFactory.cpp | 4 + src/strategy/AiObjectContext.h | 1 + src/strategy/actions/ChangeTalentsAction.cpp | 9 + src/strategy/actions/ChatActionContext.h | 6 + src/strategy/actions/EquipGlyphsAction.cpp | 159 ++++++++++++++++++ src/strategy/actions/EquipGlyphsAction.h | 37 ++++ src/strategy/actions/TellGlyphsAction.cpp | 113 +++++++++++++ src/strategy/actions/TellGlyphsAction.h | 21 +++ .../generic/ChatCommandHandlerStrategy.cpp | 4 + src/strategy/triggers/ChatTriggerContext.h | 4 + src/strategy/values/ValueContext.h | 8 + 11 files changed, 366 insertions(+) create mode 100644 src/strategy/actions/EquipGlyphsAction.cpp create mode 100644 src/strategy/actions/EquipGlyphsAction.h create mode 100644 src/strategy/actions/TellGlyphsAction.cpp create mode 100644 src/strategy/actions/TellGlyphsAction.h diff --git a/src/factory/PlayerbotFactory.cpp b/src/factory/PlayerbotFactory.cpp index 02e47b21..b43ee055 100644 --- a/src/factory/PlayerbotFactory.cpp +++ b/src/factory/PlayerbotFactory.cpp @@ -39,6 +39,7 @@ #include "SpellAuraDefines.h" #include "StatsWeightCalculator.h" #include "World.h" +#include "AiObjectContext.h" const uint64 diveMask = (1LL << 7) | (1LL << 44) | (1LL << 37) | (1LL << 38) | (1LL << 26) | (1LL << 30) | (1LL << 27) | (1LL << 33) | (1LL << 24) | (1LL << 34); @@ -3330,6 +3331,9 @@ void PlayerbotFactory::InitReagents() void PlayerbotFactory::InitGlyphs(bool increment) { bot->InitGlyphsForLevel(); + if (!increment && + botAI->GetAiObjectContext()->GetValue("custom_glyphs")->Get()) + return; // // Added for custom Glyphs - custom glyphs flag test if (!increment) { diff --git a/src/strategy/AiObjectContext.h b/src/strategy/AiObjectContext.h index cfa7d4db..c65ad8ae 100644 --- a/src/strategy/AiObjectContext.h +++ b/src/strategy/AiObjectContext.h @@ -27,6 +27,7 @@ typedef UntypedValue* (*ValueCreator)(PlayerbotAI* botAI); class AiObjectContext : public PlayerbotAIAware { public: + static BoolCalculatedValue* custom_glyphs(PlayerbotAI* ai); // Added for cutom glyphs AiObjectContext(PlayerbotAI* botAI, SharedNamedObjectContextList& sharedStrategyContext = sharedStrategyContexts, SharedNamedObjectContextList& sharedActionContext = sharedActionContexts, diff --git a/src/strategy/actions/ChangeTalentsAction.cpp b/src/strategy/actions/ChangeTalentsAction.cpp index 33931d92..65634162 100644 --- a/src/strategy/actions/ChangeTalentsAction.cpp +++ b/src/strategy/actions/ChangeTalentsAction.cpp @@ -11,9 +11,18 @@ #include "PlayerbotAIConfig.h" #include "PlayerbotFactory.h" #include "Playerbots.h" +#include "AiObjectContext.h" +#include "Log.h" bool ChangeTalentsAction::Execute(Event event) { + auto* flag = botAI->GetAiObjectContext()->GetValue("custom_glyphs"); // Added for custom Glyphs + + if (flag->Get()) // Added for custom Glyphs + { + flag->Set(false); + LOG_INFO("playerbots", "Custom Glyph Flag set to OFF"); + } std::string param = event.getParam(); std::ostringstream out; diff --git a/src/strategy/actions/ChatActionContext.h b/src/strategy/actions/ChatActionContext.h index f0a8d3f7..bc111ac6 100644 --- a/src/strategy/actions/ChatActionContext.h +++ b/src/strategy/actions/ChatActionContext.h @@ -79,6 +79,8 @@ #include "UnlockItemAction.h" #include "UnlockTradedItemAction.h" #include "PetAction.h" +#include "TellGlyphsAction.h" +#include "EquipGlyphsAction.h" class ChatActionContext : public NamedObjectContext { @@ -189,6 +191,8 @@ public: creators["calc"] = &ChatActionContext::calc; creators["wipe"] = &ChatActionContext::wipe; creators["pet"] = &ChatActionContext::pet; + creators["glyphs"] = &ChatActionContext::glyphs; // Added for custom Glyphs + creators["glyph equip"] = &ChatActionContext::glyph_equip; // Added for custom Glyphs } private: @@ -296,6 +300,8 @@ private: static Action* calc(PlayerbotAI* ai) { return new TellCalculateItemAction(ai); } static Action* wipe(PlayerbotAI* ai) { return new WipeAction(ai); } static Action* pet(PlayerbotAI* botAI) { return new PetAction(botAI); } + static Action* glyphs(PlayerbotAI* botAI) { return new TellGlyphsAction(botAI); } // Added for custom Glyphs + static Action* glyph_equip(PlayerbotAI* ai) { return new EquipGlyphsAction(ai); } // Added for custom Glyphs }; #endif diff --git a/src/strategy/actions/EquipGlyphsAction.cpp b/src/strategy/actions/EquipGlyphsAction.cpp new file mode 100644 index 00000000..4c7ac14d --- /dev/null +++ b/src/strategy/actions/EquipGlyphsAction.cpp @@ -0,0 +1,159 @@ +/* + * Copyright (C) 2016+ AzerothCore , 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 "EquipGlyphsAction.h" + +#include "Playerbots.h" +#include "ObjectMgr.h" +#include "SpellMgr.h" +#include "DBCStores.h" +#include "AiObjectContext.h" +#include "Log.h" + +#include +#include +#include + +namespace +{ + // itemId -> GlyphInfo + std::unordered_map s_GlyphCache; +} + +void EquipGlyphsAction::BuildGlyphCache() +{ + if (!s_GlyphCache.empty()) + return; + + ItemTemplateContainer const* store = sObjectMgr->GetItemTemplateStore(); + + for (auto const& kv : *store) + { + uint32 itemId = kv.first; + ItemTemplate const* proto = &kv.second; + if (!proto || proto->Class != ITEM_CLASS_GLYPH) + continue; + + // inspect item spell + for (uint32 i = 0; i < MAX_ITEM_PROTO_SPELLS; ++i) + { + uint32 spellId = proto->Spells[i].SpellId; + if (!spellId) continue; + + SpellInfo const* si = sSpellMgr->GetSpellInfo(spellId); + if (!si) continue; + + for (uint8 eff = 0; eff <= EFFECT_2; ++eff) + { + if (si->Effects[eff].Effect != SPELL_EFFECT_APPLY_GLYPH) + continue; + + uint32 glyphId = si->Effects[eff].MiscValue; + if (!glyphId) continue; + + if (auto const* gp = sGlyphPropertiesStore.LookupEntry(glyphId)) + s_GlyphCache[itemId] = {gp, proto}; + } + } + } +} + +EquipGlyphsAction::GlyphInfo const* EquipGlyphsAction::GetGlyphInfo(uint32 itemId) +{ + BuildGlyphCache(); + auto it = s_GlyphCache.find(itemId); + return (it == s_GlyphCache.end()) ? nullptr : &it->second; +} + +/// ----------------------------------------------------------------- +/// Validation and collect +/// ----------------------------------------------------------------- +bool EquipGlyphsAction::CollectGlyphs(std::vector const& itemIds, + std::vector& out) const +{ + std::unordered_set seen; + + for (uint32 itemId : itemIds) + { + if (!seen.insert(itemId).second) + return false; // double + + auto const* info = GetGlyphInfo(itemId); + if (!info) // no good glyph + return false; + + // check class by AllowableClass + if ((info->proto->AllowableClass & bot->getClassMask()) == 0) + return false; + + out.push_back(info); + } + return out.size() <= 6 && !out.empty(); +} + +/// ----------------------------------------------------------------- +/// Action +/// ----------------------------------------------------------------- +bool EquipGlyphsAction::Execute(Event event) +{ + // 1) parse IDs + std::vector itemIds; + std::istringstream iss(event.getParam()); + for (uint32 id; iss >> id; ) itemIds.push_back(id); + + std::vector glyphs; + if (!CollectGlyphs(itemIds, glyphs)) + { + botAI->TellMaster("glyph equip : liste invalide (IDs items glyphes de ta classe, max 6)."); + return false; + } + + // 2) prepare a empty slots table ? + bool used[6] = {false,false,false,false,false,false}; + + // 3) for each glyph, find the first available and compatible socket + for (auto const* g : glyphs) + { + bool placed = false; + + for (uint8 i = 0; i < MAX_GLYPH_SLOT_INDEX; ++i) + { + if (used[i]) continue; + + uint32 slotId = bot->GetGlyphSlot(i); + auto const* gs = sGlyphSlotStore.LookupEntry(slotId); + if (!gs || gs->TypeFlags != g->prop->TypeFlags) + continue; // major/minor don't match + + // Remove aura if exist + uint32 cur = bot->GetGlyph(i); + if (cur) + if (auto* old = sGlyphPropertiesStore.LookupEntry(cur)) + bot->RemoveAurasDueToSpell(old->SpellId); + + // Apply new one + bot->CastSpell(bot, g->prop->SpellId, true); + bot->SetGlyph(i, g->prop->Id, true); + + used[i] = true; + placed = true; + break; + } + + if (!placed) + { + botAI->TellMaster("Not enought empty sockets for all glyphs."); + return false; + } + } + + botAI->TellMaster("Glyphs updated."); + + // Flag to custom glyphs + botAI->GetAiObjectContext()->GetValue("custom_glyphs")->Set(true); + LOG_INFO("playerbots", "Custom Glyph Flag set to ON"); + + return true; +} diff --git a/src/strategy/actions/EquipGlyphsAction.h b/src/strategy/actions/EquipGlyphsAction.h new file mode 100644 index 00000000..9206915e --- /dev/null +++ b/src/strategy/actions/EquipGlyphsAction.h @@ -0,0 +1,37 @@ +/* + * Copyright (C) 2016+ AzerothCore , 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. + */ + +#ifndef _PLAYERBOT_EQUIPGLYPHSACTION_H +#define _PLAYERBOT_EQUIPGLYPHSACTION_H + +#include "Action.h" + +// 1 = major, 2 = minor dans GlyphProperties.dbc +enum class GlyphKind : uint32 { MAJOR = 1, MINOR = 2 }; + +class EquipGlyphsAction : public Action +{ +public: + EquipGlyphsAction(PlayerbotAI* ai) : Action(ai, "glyph equip") {} + bool Execute(Event event) override; + + /// ---- Rendu public pour être utilisable par le cache global ---- + struct GlyphInfo + { + GlyphPropertiesEntry const* prop; ///< entrée GlyphProperties.dbc + ItemTemplate const* proto; ///< template de l’objet glyphe + }; + +private: + /// Construit la cache {itemId -> GlyphInfo} + static void BuildGlyphCache(); + static GlyphInfo const* GetGlyphInfo(uint32 itemId); + + /// Parse & valide la liste d’items glyphes + bool CollectGlyphs(std::vector const& itemIds, + std::vector& out) const; +}; + +#endif diff --git a/src/strategy/actions/TellGlyphsAction.cpp b/src/strategy/actions/TellGlyphsAction.cpp new file mode 100644 index 00000000..3f48dbd8 --- /dev/null +++ b/src/strategy/actions/TellGlyphsAction.cpp @@ -0,0 +1,113 @@ +/* + * Copyright (C) 2016+ AzerothCore , 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 "TellGlyphsAction.h" + +#include "Event.h" +#include "Playerbots.h" + +#include "ObjectMgr.h" +#include "SpellMgr.h" +#include "World.h" + +#include +#include + +namespace +{ + // ----------------------------------------------------------------- + // Cache : GlyphID (MiscValue) -> ItemTemplate* + // ----------------------------------------------------------------- + std::unordered_map s_GlyphItemCache; + + void BuildGlyphItemCache() + { + if (!s_GlyphItemCache.empty()) + return; + + ItemTemplateContainer const* store = sObjectMgr->GetItemTemplateStore(); + + for (auto const& kv : *store) // C++17 : range-for sur map + { + ItemTemplate const* proto = &kv.second; + + if (!proto || proto->Class != ITEM_CLASS_GLYPH) + continue; + + for (uint32 i = 0; i < MAX_ITEM_PROTO_SPELLS; ++i) + { + uint32 spellId = proto->Spells[i].SpellId; + if (!spellId) + continue; + + SpellInfo const* spell = sSpellMgr->GetSpellInfo(spellId); + if (!spell) + continue; + + for (uint32 eff = 0; eff <= EFFECT_2; ++eff) + { + if (spell->Effects[eff].Effect != SPELL_EFFECT_APPLY_GLYPH) + continue; + + uint32 glyphId = spell->Effects[eff].MiscValue; + if (glyphId) + s_GlyphItemCache[glyphId] = proto; + } + } + } + } +} // namespace + +// ----------------------------------------------------------------- +// Action +// ----------------------------------------------------------------- +bool TellGlyphsAction::Execute(Event event) +{ + //----------------------------------------------------------------- + // 1. who sended the wisp ? (source of event) + //----------------------------------------------------------------- + Player* sender = event.getOwner(); // API Event + if (!sender) + return false; + + //----------------------------------------------------------------- + // 2. Generate glyphId cache -> item + //----------------------------------------------------------------- + BuildGlyphItemCache(); + + //----------------------------------------------------------------- + // 3. Look at the 6 glyphs sockets + //----------------------------------------------------------------- + std::ostringstream list; + bool first = true; + + for (uint8 slot = 0; slot < MAX_GLYPH_SLOT_INDEX; ++slot) + { + uint32 glyphId = bot->GetGlyph(slot); + if (!glyphId) + continue; + + auto it = s_GlyphItemCache.find(glyphId); + if (it == s_GlyphItemCache.end()) + continue; // No glyph found (rare) + + if (!first) + list << ", "; + + // chat->FormatItem + list << chat->FormatItem(it->second); + first = false; + } + + //----------------------------------------------------------------- + // 4. Send chat messages + //----------------------------------------------------------------- + if (first) // no glyphs + botAI->TellMaster("No glyphs equipped"); + else + botAI->TellMaster(std::string("Glyphs: ") + list.str()); + + return true; +} diff --git a/src/strategy/actions/TellGlyphsAction.h b/src/strategy/actions/TellGlyphsAction.h new file mode 100644 index 00000000..b68e9fef --- /dev/null +++ b/src/strategy/actions/TellGlyphsAction.h @@ -0,0 +1,21 @@ +/* + * Copyright (C) 2016+ AzerothCore , 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. + */ + +#ifndef _PLAYERBOT_TELLGLYPHSACTION_H +#define _PLAYERBOT_TELLGLYPHSACTION_H + +#include "Action.h" + +class TellGlyphsAction : public Action +{ +public: + TellGlyphsAction(PlayerbotAI* ai, std::string const name = "glyphs") + : Action(ai, name) {} + + bool Execute(Event event) override; +}; + +#endif + diff --git a/src/strategy/generic/ChatCommandHandlerStrategy.cpp b/src/strategy/generic/ChatCommandHandlerStrategy.cpp index 20ed07b8..db44bf10 100644 --- a/src/strategy/generic/ChatCommandHandlerStrategy.cpp +++ b/src/strategy/generic/ChatCommandHandlerStrategy.cpp @@ -103,6 +103,8 @@ void ChatCommandHandlerStrategy::InitTriggers(std::vector& trigger triggers.push_back( new TriggerNode("wipe", NextAction::array(0, new NextAction("wipe", relevance), nullptr))); triggers.push_back(new TriggerNode("pet", NextAction::array(0, new NextAction("pet", relevance), nullptr))); + triggers.push_back(new TriggerNode("glyphs", NextAction::array(0, new NextAction("glyphs", relevance), nullptr))); // Added for custom Glyphs + triggers.push_back(new TriggerNode("glyph equip", NextAction::array(0, new NextAction("glyph equip", relevance), nullptr))); // Added for custom Glyphs } ChatCommandHandlerStrategy::ChatCommandHandlerStrategy(PlayerbotAI* botAI) : PassTroughStrategy(botAI) @@ -183,4 +185,6 @@ ChatCommandHandlerStrategy::ChatCommandHandlerStrategy(PlayerbotAI* botAI) : Pas supported.push_back("unlock items"); supported.push_back("unlock traded item"); supported.push_back("pet"); + supported.push_back("glyphs"); // Added for custom Glyphs + supported.push_back("glyph equip"); // Added for custom Glyphs } diff --git a/src/strategy/triggers/ChatTriggerContext.h b/src/strategy/triggers/ChatTriggerContext.h index 6979e81f..64239fe9 100644 --- a/src/strategy/triggers/ChatTriggerContext.h +++ b/src/strategy/triggers/ChatTriggerContext.h @@ -134,6 +134,8 @@ public: creators["qi"] = &ChatTriggerContext::qi; creators["wipe"] = &ChatTriggerContext::wipe; creators["pet"] = &ChatTriggerContext::pet; + creators["glyphs"] = &ChatTriggerContext::glyphs; // Added for custom Glyphs + creators["glyph equip"] = &ChatTriggerContext::glyph_equip; // Added for custom Glyphs } private: @@ -247,6 +249,8 @@ private: static Trigger* qi(PlayerbotAI* botAI) { return new ChatCommandTrigger(botAI, "qi"); } static Trigger* wipe(PlayerbotAI* botAI) { return new ChatCommandTrigger(botAI, "wipe"); } static Trigger* pet(PlayerbotAI* botAI) { return new ChatCommandTrigger(botAI, "pet"); } + static Trigger* glyphs(PlayerbotAI* botAI) { return new ChatCommandTrigger(botAI, "glyphs"); } // Added for custom Glyphs + static Trigger* glyph_equip(PlayerbotAI* ai) { return new ChatCommandTrigger(ai, "glyph equip"); } // Added for custom Glyphs }; #endif diff --git a/src/strategy/values/ValueContext.h b/src/strategy/values/ValueContext.h index c9dcdb7c..2e735fc6 100644 --- a/src/strategy/values/ValueContext.h +++ b/src/strategy/values/ValueContext.h @@ -160,6 +160,7 @@ public: creators["my attacker count"] = &ValueContext::my_attacker_count; creators["has aggro"] = &ValueContext::has_aggro; creators["mounted"] = &ValueContext::mounted; + creators["custom_glyphs"] = &ValueContext::custom_glyphs; // Added for custom glyphs creators["can loot"] = &ValueContext::can_loot; creators["loot target"] = &ValueContext::loot_target; @@ -554,6 +555,13 @@ private: static UntypedValue* last_flee_angle(PlayerbotAI* ai) { return new LastFleeAngleValue(ai); } static UntypedValue* last_flee_timestamp(PlayerbotAI* ai) { return new LastFleeTimestampValue(ai); } static UntypedValue* recently_flee_info(PlayerbotAI* ai) { return new RecentlyFleeInfo(ai); } + // ------------------------------------------------------- + // Flag for cutom glyphs : true when /w bot glyph equip … + // ------------------------------------------------------- + static UntypedValue* custom_glyphs(PlayerbotAI* ai) + { + return new ManualSetValue(ai, false, "custom_glyphs"); + } }; #endif