Core/Players: PlayerChoice improvements

* Add missing choice properties to database (InfiniteRange, ShowChoicesAsList)
* Allow limiiting the number of responses sent at the same time
* Fixed duration sent in SMSG_DISPLAY_PLAYER_CHOICE
* Remove dynamically generated response identifiers from database
* Remove auto rewarding choice responses
* Change response scripts to be bound to scriptname
This commit is contained in:
Shauren
2025-06-22 21:56:58 +02:00
parent 7ca6b226a7
commit e59059e1bd
14 changed files with 421 additions and 278 deletions

View File

@@ -24,12 +24,14 @@
#include "DB2Stores.h"
#include "GameObject.h"
#include "GameObjectAI.h"
#include "GameTime.h"
#include "GossipDef.h"
#include "Group.h"
#include "Log.h"
#include "ObjectAccessor.h"
#include "ObjectMgr.h"
#include "Player.h"
#include "PlayerChoice.h"
#include "PoolMgr.h"
#include "QuestDef.h"
#include "QuestPackets.h"
@@ -768,12 +770,35 @@ void WorldSession::HandleRequestWorldQuestUpdate(WorldPackets::Quest::RequestWor
SendPacket(response.Write());
}
void WorldSession::HandlePlayerChoiceResponse(WorldPackets::Quest::ChoiceResponse& choiceResponse)
void WorldSession::HandlePlayerChoiceResponse(WorldPackets::Quest::ChoiceResponse const& choiceResponse)
{
if (_player->PlayerTalkClass->GetInteractionData().GetPlayerChoiceId() != uint32(choiceResponse.ChoiceID))
PlayerChoiceData const* playerChoiceData = _player->PlayerTalkClass->GetInteractionData().GetPlayerChoice();
if (!playerChoiceData)
{
TC_LOG_ERROR("entities.player.cheat", "Error in CMSG_CHOICE_RESPONSE: {} tried to respond to invalid player choice {} (allowed {}) (possible packet-hacking detected)",
GetPlayerInfo(), choiceResponse.ChoiceID, _player->PlayerTalkClass->GetInteractionData().GetPlayerChoiceId());
TC_LOG_ERROR("entities.player.cheat", "Error in CMSG_CHOICE_RESPONSE: {} tried to respond to invalid player choice {} (none allowed)",
GetPlayerInfo(), choiceResponse.ChoiceID);
return;
}
if (playerChoiceData->GetChoiceId() != uint32(choiceResponse.ChoiceID))
{
TC_LOG_ERROR("entities.player.cheat", "Error in CMSG_CHOICE_RESPONSE: {} tried to respond to invalid player choice {} ({} allowed)",
GetPlayerInfo(), choiceResponse.ChoiceID, playerChoiceData->GetChoiceId());
return;
}
if (playerChoiceData->GetExpireTime() && playerChoiceData->GetExpireTime() < GameTime::GetSystemTime())
{
TC_LOG_ERROR("entities.player.cheat", "Error in CMSG_CHOICE_RESPONSE: {} tried to respond to expired player choice {})",
GetPlayerInfo(), choiceResponse.ChoiceID);
return;
}
Optional<uint32> responseId = playerChoiceData->FindIdByClientIdentifier(choiceResponse.ResponseIdentifier);
if (!responseId)
{
TC_LOG_ERROR("entities.player.cheat", "Error in CMSG_CHOICE_RESPONSE: {} tried to select invalid player choice response identifier {}",
GetPlayerInfo(), choiceResponse.ResponseIdentifier);
return;
}
@@ -781,52 +806,23 @@ void WorldSession::HandlePlayerChoiceResponse(WorldPackets::Quest::ChoiceRespons
if (!playerChoice)
return;
PlayerChoiceResponse const* playerChoiceResponse = playerChoice->GetResponseByIdentifier(choiceResponse.ResponseIdentifier);
PlayerChoiceResponse const* playerChoiceResponse = playerChoice->GetResponse(*responseId);
if (!playerChoiceResponse)
{
TC_LOG_ERROR("entities.player.cheat", "Error in CMSG_CHOICE_RESPONSE: {} tried to select invalid player choice response {} (possible packet-hacking detected)",
GetPlayerInfo(), choiceResponse.ResponseIdentifier);
TC_LOG_ERROR("entities.player.cheat", "Error in CMSG_CHOICE_RESPONSE: {} tried to select invalid player choice response {}",
GetPlayerInfo(), *responseId);
return;
}
sScriptMgr->OnPlayerChoiceResponse(GetPlayer(), choiceResponse.ChoiceID, choiceResponse.ResponseIdentifier);
if (playerChoiceResponse->Reward)
if (playerChoiceResponse->Flags.HasFlag(PlayerChoiceResponseFlags::DisabledButton | PlayerChoiceResponseFlags::DisabledOption | PlayerChoiceResponseFlags::HideButtonShowText))
{
if (playerChoiceResponse->Reward->TitleId)
_player->SetTitle(sCharTitlesStore.AssertEntry(playerChoiceResponse->Reward->TitleId), false);
if (playerChoiceResponse->Reward->PackageId)
_player->RewardQuestPackage(playerChoiceResponse->Reward->PackageId, ItemContext::NONE);
if (playerChoiceResponse->Reward->SkillLineId && _player->HasSkill(playerChoiceResponse->Reward->SkillLineId))
_player->UpdateSkillPro(playerChoiceResponse->Reward->SkillLineId, 1000, playerChoiceResponse->Reward->SkillPointCount);
if (playerChoiceResponse->Reward->HonorPointCount)
_player->AddHonorXP(playerChoiceResponse->Reward->HonorPointCount);
if (playerChoiceResponse->Reward->Money)
_player->ModifyMoney(playerChoiceResponse->Reward->Money, false);
if (playerChoiceResponse->Reward->Xp)
_player->GiveXP(playerChoiceResponse->Reward->Xp, nullptr, 0.0f);
for (PlayerChoiceResponseRewardItem const& item : playerChoiceResponse->Reward->Items)
{
ItemPosCountVec dest;
if (_player->CanStoreNewItem(NULL_BAG, NULL_SLOT, dest, item.Id, item.Quantity) == EQUIP_ERR_OK)
{
Item* newItem = _player->StoreNewItem(dest, item.Id, true, GenerateItemRandomBonusListId(item.Id), {}, ItemContext::Quest_Reward, &item.BonusListIDs);
_player->SendNewItem(newItem, item.Quantity, true, false);
}
}
for (PlayerChoiceResponseRewardEntry const& currency : playerChoiceResponse->Reward->Currency)
_player->ModifyCurrency(currency.Id, currency.Quantity);
for (PlayerChoiceResponseRewardEntry const& faction : playerChoiceResponse->Reward->Faction)
_player->GetReputationMgr().ModifyReputation(sFactionStore.AssertEntry(faction.Id), faction.Quantity);
TC_LOG_ERROR("entities.player.cheat", "Error in CMSG_CHOICE_RESPONSE: {} tried to select disabled player choice response {}",
GetPlayerInfo(), *responseId);
return;
}
sScriptMgr->OnPlayerChoiceResponse(ObjectAccessor::GetWorldObject(*_player, _player->PlayerTalkClass->GetInteractionData().SourceGuid), _player,
playerChoice, playerChoiceResponse, choiceResponse.ResponseIdentifier);
}
void WorldSession::HandleUiMapQuestLinesRequest(WorldPackets::Quest::UiMapQuestLinesRequest& uiMapQuestLinesRequest)