From c9f4c87e612b29e54c95095717b977564133dad2 Mon Sep 17 00:00:00 2001 From: SylvaniaCore deploy Date: Tue, 18 Aug 2026 00:56:55 +0200 Subject: [PATCH] PlayerChoice : boutons vides (choix de specialisation chasseur de demons) Signale en jeu : le tome des secrets gangrenes ouvre le choix de specialisation, la question saffiche mais les deux boutons sont vides. Trois defauts cumules dans Player::SendPlayerChoice : 1. Le filtre exigeait une recompense avec un sort VALIDE pour quune reponse soit envoyee. Le choix 231 (Devastation / Vengeance) a SpellID=0 : les deux reponses etaient silencieusement ecartees. 2. displayPlayerChoice.Responses etait redimensionne AVANT le filtrage, laissant une entree vide par reponse ecartee - do les boutons vides. 3. Reward->SpellID etait dereference sans controle alors que Reward peut etre nul, comme le montre le test plus bas dans la meme fonction : un plantage en attente pour toute reponse sans ligne de recompense. Correctif general, valable pour tous les choix, pas seulement celui-ci. Co-Authored-By: Claude Opus 4.8 --- src/server/game/Entities/Player/Player.cpp | 17 ++++++++++++----- 1 file changed, 12 insertions(+), 5 deletions(-) diff --git a/src/server/game/Entities/Player/Player.cpp b/src/server/game/Entities/Player/Player.cpp index 9ef78ef..e063a4a 100644 --- a/src/server/game/Entities/Player/Player.cpp +++ b/src/server/game/Entities/Player/Player.cpp @@ -30187,7 +30187,6 @@ void Player::SendPlayerChoice(ObjectGuid sender, int32 choiceId) if (playerChoiceLocale) ObjectMgr::GetLocaleString(playerChoiceLocale->Question, locale, displayPlayerChoice.Question); - displayPlayerChoice.Responses.resize(playerChoice->Responses.size()); displayPlayerChoice.CloseChoiceFrame = false; displayPlayerChoice.HideWarboardHeader = playerChoice->HideWarboardHeader; @@ -30198,12 +30197,16 @@ void Player::SendPlayerChoice(ObjectGuid sender, int32 choiceId) if (!_playerChoiceResponseTemplate.ResponseId) continue; - SpellInfo const* spellInfo = sSpellMgr->GetSpellInfo(_playerChoiceResponseTemplate.Reward->SpellID); - if (!spellInfo) - continue; + // Une reponse n'a pas forcement de recompense : sans ce garde, toute + // reponse sans sort de recompense etait silencieusement ecartee, ce qui + // laissait des boutons vides (ex. choix de specialisation demoniste 231). + // Reward peut aussi etre nul : le dereferencer ici plantait le serveur. + SpellInfo const* spellInfo = _playerChoiceResponseTemplate.Reward + ? sSpellMgr->GetSpellInfo(_playerChoiceResponseTemplate.Reward->SpellID) + : nullptr; bool playerEligible = true; - for (SpellEffectInfo const* effect : spellInfo->GetEffectsForDifficulty(DIFFICULTY_NONE)) + for (SpellEffectInfo const* effect : spellInfo ? spellInfo->GetEffectsForDifficulty(DIFFICULTY_NONE) : SpellEffectInfoVector()) { Quest const* quest = sObjectMgr->GetQuestTemplate(effect->MiscValue); if (!quest) @@ -30236,6 +30239,10 @@ void Player::SendPlayerChoice(ObjectGuid sender, int32 choiceId) return; } + // Dimensionner APRES le filtrage : redimensionner avant laissait des + // entrees vides pour chaque reponse ecartee. + displayPlayerChoice.Responses.resize(playerChoiceResponses.size()); + for (std::size_t i = 0; i < playerChoiceResponses.size(); ++i) { auto const& playerChoiceResponseTemplate = playerChoiceResponses[i];