mirror of
https://github.com/Kelsidavis/WoWee.git
synced 2026-05-03 00:03:50 +00:00
game/ui: add target cast bar to target frame (SMSG_SPELL_START tracking)
SMSG_SPELL_START fires for all units, not just the player. Previously only the player's own cast was tracked; now we also track when the current target is casting, enabling interrupt decisions. - GameHandler: track targetCasting_/targetCastSpellId_/targetCastTimeTotal_ /targetCastTimeRemaining_ — updated by SMSG_SPELL_START for the current target and ticked down in the update loop each frame - Target cast cleared when: target changes (setTarget), target's spell lands (SMSG_SPELL_GO), or cast timer expires naturally - game_screen: renderTargetFrame shows a red cast progress bar between the power bar and distance line when the target is casting, with spell name + remaining seconds - Public accessors: isTargetCasting(), getTargetCastSpellId(), getTargetCastProgress(), getTargetCastTimeRemaining()
This commit is contained in:
parent
6951b7803d
commit
4d39736d29
3 changed files with 61 additions and 0 deletions
|
|
@ -513,6 +513,16 @@ public:
|
||||||
float getCastProgress() const { return castTimeTotal > 0 ? (castTimeTotal - castTimeRemaining) / castTimeTotal : 0.0f; }
|
float getCastProgress() const { return castTimeTotal > 0 ? (castTimeTotal - castTimeRemaining) / castTimeTotal : 0.0f; }
|
||||||
float getCastTimeRemaining() const { return castTimeRemaining; }
|
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;
|
||||||
|
}
|
||||||
|
float getTargetCastTimeRemaining() const { return targetCastTimeRemaining_; }
|
||||||
|
|
||||||
// Talents
|
// Talents
|
||||||
uint8_t getActiveTalentSpec() const { return activeTalentSpec_; }
|
uint8_t getActiveTalentSpec() const { return activeTalentSpec_; }
|
||||||
uint8_t getUnspentTalentPoints() const { return unspentTalentPoints_[activeTalentSpec_]; }
|
uint8_t getUnspentTalentPoints() const { return unspentTalentPoints_[activeTalentSpec_]; }
|
||||||
|
|
@ -1754,6 +1764,11 @@ private:
|
||||||
bool casting = false;
|
bool casting = false;
|
||||||
uint32_t currentCastSpellId = 0;
|
uint32_t currentCastSpellId = 0;
|
||||||
float castTimeRemaining = 0.0f;
|
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;
|
||||||
uint64_t pendingGameObjectInteractGuid_ = 0;
|
uint64_t pendingGameObjectInteractGuid_ = 0;
|
||||||
|
|
||||||
// Talents (dual-spec support)
|
// Talents (dual-spec support)
|
||||||
|
|
|
||||||
|
|
@ -759,6 +759,16 @@ 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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Update spell cooldowns (Phase 3)
|
// Update spell cooldowns (Phase 3)
|
||||||
for (auto it = spellCooldowns.begin(); it != spellCooldowns.end(); ) {
|
for (auto it = spellCooldowns.begin(); it != spellCooldowns.end(); ) {
|
||||||
it->second -= deltaTime;
|
it->second -= deltaTime;
|
||||||
|
|
@ -8546,6 +8556,11 @@ void GameHandler::setTarget(uint64_t guid) {
|
||||||
|
|
||||||
targetGuid = guid;
|
targetGuid = guid;
|
||||||
|
|
||||||
|
// Clear target cast bar when target changes
|
||||||
|
targetCasting_ = false;
|
||||||
|
targetCastSpellId_ = 0;
|
||||||
|
targetCastTimeRemaining_ = 0.0f;
|
||||||
|
|
||||||
// Inform server of target selection (Phase 1)
|
// Inform server of target selection (Phase 1)
|
||||||
if (state == WorldState::IN_WORLD && socket) {
|
if (state == WorldState::IN_WORLD && socket) {
|
||||||
auto packet = SetSelectionPacket::build(guid);
|
auto packet = SetSelectionPacket::build(guid);
|
||||||
|
|
@ -12641,6 +12656,14 @@ void GameHandler::handleSpellStart(network::Packet& packet) {
|
||||||
SpellStartData data;
|
SpellStartData data;
|
||||||
if (!packetParsers_->parseSpellStart(packet, data)) return;
|
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_;
|
||||||
|
}
|
||||||
|
|
||||||
// If this is the player's own cast, start cast bar
|
// If this is the player's own cast, start cast bar
|
||||||
if (data.casterUnit == playerGuid && data.castTime > 0) {
|
if (data.casterUnit == playerGuid && data.castTime > 0) {
|
||||||
casting = true;
|
casting = true;
|
||||||
|
|
@ -12713,6 +12736,13 @@ void GameHandler::handleSpellGo(network::Packet& packet) {
|
||||||
castTimeRemaining = 0.0f;
|
castTimeRemaining = 0.0f;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Clear target cast bar when the target's spell lands
|
||||||
|
if (data.casterUnit == targetGuid) {
|
||||||
|
targetCasting_ = false;
|
||||||
|
targetCastSpellId_ = 0;
|
||||||
|
targetCastTimeRemaining_ = 0.0f;
|
||||||
|
}
|
||||||
|
|
||||||
// Show miss/dodge/parry/etc combat text when player's spells miss targets
|
// Show miss/dodge/parry/etc combat text when player's spells miss targets
|
||||||
if (data.casterUnit == playerGuid && !data.missTargets.empty()) {
|
if (data.casterUnit == playerGuid && !data.missTargets.empty()) {
|
||||||
static const CombatTextEntry::Type missTypes[] = {
|
static const CombatTextEntry::Type missTypes[] = {
|
||||||
|
|
|
||||||
|
|
@ -2093,6 +2093,22 @@ void GameScreen::renderTargetFrame(game::GameHandler& gameHandler) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Target cast bar — shown when the target is casting
|
||||||
|
if (gameHandler.isTargetCasting()) {
|
||||||
|
float castPct = gameHandler.getTargetCastProgress();
|
||||||
|
float castLeft = gameHandler.getTargetCastTimeRemaining();
|
||||||
|
uint32_t tspell = gameHandler.getTargetCastSpellId();
|
||||||
|
const std::string& castName = (tspell != 0) ? gameHandler.getSpellName(tspell) : "";
|
||||||
|
ImGui::PushStyleColor(ImGuiCol_PlotHistogram, ImVec4(0.9f, 0.3f, 0.2f, 1.0f));
|
||||||
|
char castLabel[72];
|
||||||
|
if (!castName.empty())
|
||||||
|
snprintf(castLabel, sizeof(castLabel), "%s (%.1fs)", castName.c_str(), castLeft);
|
||||||
|
else
|
||||||
|
snprintf(castLabel, sizeof(castLabel), "Casting... (%.1fs)", castLeft);
|
||||||
|
ImGui::ProgressBar(castPct, ImVec2(-1, 14), castLabel);
|
||||||
|
ImGui::PopStyleColor();
|
||||||
|
}
|
||||||
|
|
||||||
// Distance
|
// Distance
|
||||||
const auto& movement = gameHandler.getMovementInfo();
|
const auto& movement = gameHandler.getMovementInfo();
|
||||||
float dx = target->getX() - movement.x;
|
float dx = target->getX() - movement.x;
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue