mirror of
https://github.com/Kelsidavis/WoWee.git
synced 2026-03-22 23:30:14 +00:00
game/ui: generalize cast tracking to per-GUID map; add boss cast bars
Previously the target cast bar tracked a single target using 4 private fields. This replaces that with unitCastStates_ (unordered_map<uint64_t, UnitCastState>), tracking cast state for every non-player unit whose SMSG_SPELL_START we receive. Changes: - GameHandler::UnitCastState struct: casting, spellId, timeRemaining, timeTotal - getUnitCastState(guid) → returns cast state for any tracked unit - isTargetCasting(), getTargetCastSpellId(), getTargetCastProgress(), getTargetCastTimeRemaining() now delegate to getUnitCastState(targetGuid) - handleSpellStart: tracks all non-player casters (not just the target) - handleSpellGo: erases caster from map when spell lands - update loop: ticks down all unit cast states, erasing expired entries - unitCastStates_ cleared on world reset - renderBossFrames: shows red cast progress bar per boss slot with spell name + remaining seconds — critical for instance interrupt play
This commit is contained in:
parent
1c85b7a46d
commit
07d0485a31
3 changed files with 68 additions and 36 deletions
|
|
@ -513,15 +513,33 @@ public:
|
|||
float getCastProgress() const { return castTimeTotal > 0 ? (castTimeTotal - castTimeRemaining) / castTimeTotal : 0.0f; }
|
||||
float getCastTimeRemaining() const { return castTimeRemaining; }
|
||||
|
||||
// Target cast bar (shows when the current target is casting)
|
||||
bool isTargetCasting() const { return targetCasting_; }
|
||||
uint32_t getTargetCastSpellId() const { return targetCastSpellId_; }
|
||||
float getTargetCastProgress() const {
|
||||
return targetCastTimeTotal_ > 0.0f
|
||||
? (targetCastTimeTotal_ - targetCastTimeRemaining_) / targetCastTimeTotal_
|
||||
: 0.0f;
|
||||
// Unit cast state (tracked per GUID for target frame + boss frames)
|
||||
struct UnitCastState {
|
||||
bool casting = false;
|
||||
uint32_t spellId = 0;
|
||||
float timeRemaining = 0.0f;
|
||||
float timeTotal = 0.0f;
|
||||
};
|
||||
// Returns cast state for any unit by GUID (empty/non-casting if not found)
|
||||
const UnitCastState* getUnitCastState(uint64_t guid) const {
|
||||
auto it = unitCastStates_.find(guid);
|
||||
return (it != unitCastStates_.end() && it->second.casting) ? &it->second : nullptr;
|
||||
}
|
||||
// Convenience helpers for the current target
|
||||
bool isTargetCasting() const { return getUnitCastState(targetGuid) != nullptr; }
|
||||
uint32_t getTargetCastSpellId() const {
|
||||
auto* s = getUnitCastState(targetGuid);
|
||||
return s ? s->spellId : 0;
|
||||
}
|
||||
float getTargetCastProgress() const {
|
||||
auto* s = getUnitCastState(targetGuid);
|
||||
return (s && s->timeTotal > 0.0f)
|
||||
? (s->timeTotal - s->timeRemaining) / s->timeTotal : 0.0f;
|
||||
}
|
||||
float getTargetCastTimeRemaining() const {
|
||||
auto* s = getUnitCastState(targetGuid);
|
||||
return s ? s->timeRemaining : 0.0f;
|
||||
}
|
||||
float getTargetCastTimeRemaining() const { return targetCastTimeRemaining_; }
|
||||
|
||||
// Talents
|
||||
uint8_t getActiveTalentSpec() const { return activeTalentSpec_; }
|
||||
|
|
@ -1764,11 +1782,8 @@ private:
|
|||
bool casting = false;
|
||||
uint32_t currentCastSpellId = 0;
|
||||
float castTimeRemaining = 0.0f;
|
||||
// Target cast bar state (populated from SMSG_SPELL_START for the current target)
|
||||
bool targetCasting_ = false;
|
||||
uint32_t targetCastSpellId_ = 0;
|
||||
float targetCastTimeRemaining_= 0.0f;
|
||||
float targetCastTimeTotal_ = 0.0f;
|
||||
// Per-unit cast state (keyed by GUID, populated from SMSG_SPELL_START)
|
||||
std::unordered_map<uint64_t, UnitCastState> unitCastStates_;
|
||||
uint64_t pendingGameObjectInteractGuid_ = 0;
|
||||
|
||||
// Talents (dual-spec support)
|
||||
|
|
|
|||
|
|
@ -759,14 +759,17 @@ void GameHandler::update(float deltaTime) {
|
|||
}
|
||||
}
|
||||
|
||||
// Tick down target cast bar
|
||||
if (targetCasting_ && targetCastTimeRemaining_ > 0.0f) {
|
||||
targetCastTimeRemaining_ -= deltaTime;
|
||||
if (targetCastTimeRemaining_ <= 0.0f) {
|
||||
targetCasting_ = false;
|
||||
targetCastSpellId_ = 0;
|
||||
targetCastTimeRemaining_ = 0.0f;
|
||||
// Tick down all tracked unit cast bars
|
||||
for (auto it = unitCastStates_.begin(); it != unitCastStates_.end(); ) {
|
||||
auto& s = it->second;
|
||||
if (s.casting && s.timeRemaining > 0.0f) {
|
||||
s.timeRemaining -= deltaTime;
|
||||
if (s.timeRemaining <= 0.0f) {
|
||||
it = unitCastStates_.erase(it);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
++it;
|
||||
}
|
||||
|
||||
// Update spell cooldowns (Phase 3)
|
||||
|
|
@ -5700,6 +5703,7 @@ void GameHandler::selectCharacter(uint64_t characterGuid) {
|
|||
actionBar = {};
|
||||
playerAuras.clear();
|
||||
targetAuras.clear();
|
||||
unitCastStates_.clear();
|
||||
petGuid_ = 0;
|
||||
playerXp_ = 0;
|
||||
playerNextLevelXp_ = 0;
|
||||
|
|
@ -8556,10 +8560,8 @@ void GameHandler::setTarget(uint64_t guid) {
|
|||
|
||||
targetGuid = guid;
|
||||
|
||||
// Clear target cast bar when target changes
|
||||
targetCasting_ = false;
|
||||
targetCastSpellId_ = 0;
|
||||
targetCastTimeRemaining_ = 0.0f;
|
||||
// Clear previous target's cast bar on target change
|
||||
// (the new target's cast state is naturally fetched from unitCastStates_ by GUID)
|
||||
|
||||
// Inform server of target selection (Phase 1)
|
||||
if (state == WorldState::IN_WORLD && socket) {
|
||||
|
|
@ -12656,12 +12658,13 @@ void GameHandler::handleSpellStart(network::Packet& packet) {
|
|||
SpellStartData data;
|
||||
if (!packetParsers_->parseSpellStart(packet, data)) return;
|
||||
|
||||
// Track cast bar for the current target (for interrupt awareness)
|
||||
if (data.casterUnit == targetGuid && data.castTime > 0) {
|
||||
targetCasting_ = true;
|
||||
targetCastSpellId_ = data.spellId;
|
||||
targetCastTimeTotal_ = data.castTime / 1000.0f;
|
||||
targetCastTimeRemaining_ = targetCastTimeTotal_;
|
||||
// Track cast bar for any non-player caster (target frame + boss frames)
|
||||
if (data.casterUnit != playerGuid && data.castTime > 0) {
|
||||
auto& s = unitCastStates_[data.casterUnit];
|
||||
s.casting = true;
|
||||
s.spellId = data.spellId;
|
||||
s.timeTotal = data.castTime / 1000.0f;
|
||||
s.timeRemaining = s.timeTotal;
|
||||
}
|
||||
|
||||
// If this is the player's own cast, start cast bar
|
||||
|
|
@ -12736,12 +12739,8 @@ void GameHandler::handleSpellGo(network::Packet& packet) {
|
|||
castTimeRemaining = 0.0f;
|
||||
}
|
||||
|
||||
// Clear target cast bar when the target's spell lands
|
||||
if (data.casterUnit == targetGuid) {
|
||||
targetCasting_ = false;
|
||||
targetCastSpellId_ = 0;
|
||||
targetCastTimeRemaining_ = 0.0f;
|
||||
}
|
||||
// Clear unit cast bar when the spell lands (for any tracked unit)
|
||||
unitCastStates_.erase(data.casterUnit);
|
||||
|
||||
// Show miss/dodge/parry/etc combat text when player's spells miss targets
|
||||
if (data.casterUnit == playerGuid && !data.missTargets.empty()) {
|
||||
|
|
|
|||
|
|
@ -5010,6 +5010,24 @@ void GameScreen::renderBossFrames(game::GameHandler& gameHandler) {
|
|||
ImGui::PopStyleColor();
|
||||
}
|
||||
|
||||
// Boss cast bar — shown when the boss is casting (critical for interrupt)
|
||||
if (auto* cs = gameHandler.getUnitCastState(bs.guid)) {
|
||||
float castPct = (cs->timeTotal > 0.0f)
|
||||
? (cs->timeTotal - cs->timeRemaining) / cs->timeTotal : 0.0f;
|
||||
uint32_t bspell = cs->spellId;
|
||||
const std::string& bcastName = (bspell != 0)
|
||||
? gameHandler.getSpellName(bspell) : "";
|
||||
ImGui::PushStyleColor(ImGuiCol_PlotHistogram, ImVec4(0.9f, 0.3f, 0.2f, 1.0f));
|
||||
char bcastLabel[72];
|
||||
if (!bcastName.empty())
|
||||
snprintf(bcastLabel, sizeof(bcastLabel), "%s (%.1fs)",
|
||||
bcastName.c_str(), cs->timeRemaining);
|
||||
else
|
||||
snprintf(bcastLabel, sizeof(bcastLabel), "Casting... (%.1fs)", cs->timeRemaining);
|
||||
ImGui::ProgressBar(castPct, ImVec2(-1, 12), bcastLabel);
|
||||
ImGui::PopStyleColor();
|
||||
}
|
||||
|
||||
ImGui::PopID();
|
||||
ImGui::Spacing();
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue