feat: fire combat, spell, and cooldown events for Lua addon system

Add PLAYER_REGEN_DISABLED/ENABLED (combat enter/leave) via per-frame
edge detection, LEARNED_SPELL_IN_TAB/SPELLS_CHANGED on spell learn/remove,
SPELL_UPDATE_COOLDOWN/ACTIONBAR_UPDATE_COOLDOWN on cooldown finish, and
PLAYER_XP_UPDATE on XP field changes. Total addon events now at 34.
This commit is contained in:
Kelsi 2026-03-20 14:27:46 -07:00
parent 4c10974553
commit ae627193f8
2 changed files with 25 additions and 0 deletions

View file

@ -2797,6 +2797,7 @@ private:
float autoAttackResendTimer_ = 0.0f; // Re-send CMSG_ATTACKSWING every ~1s while attacking float autoAttackResendTimer_ = 0.0f; // Re-send CMSG_ATTACKSWING every ~1s while attacking
float autoAttackFacingSyncTimer_ = 0.0f; // Periodic facing sync while meleeing float autoAttackFacingSyncTimer_ = 0.0f; // Periodic facing sync while meleeing
std::unordered_set<uint64_t> hostileAttackers_; std::unordered_set<uint64_t> hostileAttackers_;
bool wasCombat_ = false; // Previous frame combat state for PLAYER_REGEN edge detection
std::vector<CombatTextEntry> combatText; std::vector<CombatTextEntry> combatText;
static constexpr size_t MAX_COMBAT_LOG = 500; static constexpr size_t MAX_COMBAT_LOG = 500;
struct RecentSpellstealLogEntry { struct RecentSpellstealLogEntry {

View file

@ -922,6 +922,17 @@ void GameHandler::update(float deltaTime) {
clearTarget(); clearTarget();
} }
// Detect combat state transitions → fire PLAYER_REGEN_DISABLED / PLAYER_REGEN_ENABLED
{
bool combatNow = isInCombat();
if (combatNow != wasCombat_) {
wasCombat_ = combatNow;
if (addonEventCallback_) {
addonEventCallback_(combatNow ? "PLAYER_REGEN_DISABLED" : "PLAYER_REGEN_ENABLED", {});
}
}
}
if (auctionSearchDelayTimer_ > 0.0f) { if (auctionSearchDelayTimer_ > 0.0f) {
auctionSearchDelayTimer_ -= deltaTime; auctionSearchDelayTimer_ -= deltaTime;
if (auctionSearchDelayTimer_ < 0.0f) auctionSearchDelayTimer_ = 0.0f; if (auctionSearchDelayTimer_ < 0.0f) auctionSearchDelayTimer_ = 0.0f;
@ -12453,6 +12464,8 @@ void GameHandler::applyUpdateObjectBlock(const UpdateBlock& block, bool& newItem
if (key == ufPlayerXp) { if (key == ufPlayerXp) {
playerXp_ = val; playerXp_ = val;
LOG_DEBUG("XP updated: ", val); LOG_DEBUG("XP updated: ", val);
if (addonEventCallback_)
addonEventCallback_("PLAYER_XP_UPDATE", {std::to_string(val)});
} }
else if (key == ufPlayerNextXp) { else if (key == ufPlayerNextXp) {
playerNextLevelXp_ = val; playerNextLevelXp_ = val;
@ -19203,6 +19216,10 @@ void GameHandler::handleCooldownEvent(network::Packet& packet) {
slot.cooldownRemaining = 0.0f; slot.cooldownRemaining = 0.0f;
} }
} }
if (addonEventCallback_) {
addonEventCallback_("SPELL_UPDATE_COOLDOWN", {});
addonEventCallback_("ACTIONBAR_UPDATE_COOLDOWN", {});
}
} }
void GameHandler::handleAuraUpdate(network::Packet& packet, bool isAll) { void GameHandler::handleAuraUpdate(network::Packet& packet, bool isAll) {
@ -19293,6 +19310,12 @@ void GameHandler::handleLearnedSpell(network::Packet& packet) {
} }
} }
// Fire LEARNED_SPELL_IN_TAB / SPELLS_CHANGED for Lua addons
if (!alreadyKnown && addonEventCallback_) {
addonEventCallback_("LEARNED_SPELL_IN_TAB", {std::to_string(spellId)});
addonEventCallback_("SPELLS_CHANGED", {});
}
// Show chat message for non-talent spells, but only if not already announced by // Show chat message for non-talent spells, but only if not already announced by
// SMSG_TRAINER_BUY_SUCCEEDED (which pre-inserts into knownSpells). // SMSG_TRAINER_BUY_SUCCEEDED (which pre-inserts into knownSpells).
if (!alreadyKnown) { if (!alreadyKnown) {
@ -19313,6 +19336,7 @@ void GameHandler::handleRemovedSpell(network::Packet& packet) {
uint32_t spellId = classicSpellId ? packet.readUInt16() : packet.readUInt32(); uint32_t spellId = classicSpellId ? packet.readUInt16() : packet.readUInt32();
knownSpells.erase(spellId); knownSpells.erase(spellId);
LOG_INFO("Removed spell: ", spellId); LOG_INFO("Removed spell: ", spellId);
if (addonEventCallback_) addonEventCallback_("SPELLS_CHANGED", {});
const std::string& name = getSpellName(spellId); const std::string& name = getSpellName(spellId);
if (!name.empty()) if (!name.empty())