Core/Quests: ported quest level scaling and reward money calculation from Classic

This commit is contained in:
Ovahlord committed 2025-06-18 16:53:29 +02:00
1 parent 1dfee7e072
commit 44f466030b
10 files changed
+212 -158

No files matched your search

@@ -0,0 +1,11 @@
ALTER TABLE `quest_template_addon`
ADD `QuestMaxScalingLevel` INT UNSIGNED NOT NULL DEFAULT 0,
ADD `RewardMoneyDifficulty` INT UNSIGNED NOT NULL DEFAULT 0,
ADD `RewardMoneyMultiplier` FLOAT NOT NULL DEFAULT 1,
ADD `RequiredMoney` INT NOT NULL DEFAULT 0;
-- Convert negative reward money values to RequiredMoney
UPDATE `quest_template_addon` AS qta
INNER JOIN `quest_template` AS qt
ON qt.`ID` = qta.`ID`
SET qta.`RequiredMoney` = qt.`RewardMoney` WHERE qt.`RewardMoney` < 0;
@@ -455,7 +455,7 @@ void PlayerMenu::SendQuestQueryResponse(Quest const* quest) const
_session->SendPacket(&quest->QueryData[static_cast<uint32>(_session->GetSessionDbLocaleIndex())]);
else
{
WorldPacket queryPacket = quest->BuildQueryData(_session->GetSessionDbLocaleIndex());
WorldPacket queryPacket = quest->BuildQueryData(_session->GetSessionDbLocaleIndex(), _session->GetPlayer());
_session->SendPacket(&queryPacket);
}
@@ -546,7 +546,10 @@ void PlayerMenu::SendQuestGiverRequestItems(Quest const* quest, ObjectGuid npcGU
packet.AutoLaunched = autoLaunched;
packet.QuestFlags = quest->GetFlags();
packet.SuggestPartyMembers = quest->GetSuggestedPlayers();
packet.MoneyToGet = quest->GetRewOrReqMoney() < 0 ? -quest->GetRewOrReqMoney() : 0;
int32 moneyReward = quest->GetMoneyReward(_session->GetPlayer());
if (moneyReward < 0)
packet.MoneyToGet = std::abs(moneyReward);
QuestStatusData data;
QuestStatusMap statusMap = _session->GetPlayer()->getQuestStatusMap();
+44 -29
View File
@@ -13876,11 +13876,10 @@ bool Player::CanCompleteQuest(uint32 quest_id)
if (qInfo->HasSpecialFlag(QUEST_SPECIAL_FLAGS_TIMED) && q_status.Timer == 0)
return false;
if (qInfo->GetRewOrReqMoney() < 0)
{
if (!HasEnoughMoney(-int64(qInfo->GetRewOrReqMoney())))
int32 rewardMoney = qInfo->GetMoneyReward(this);
if (rewardMoney < 0)
if (!HasEnoughMoney(-int64(rewardMoney)))
return false;
}
uint32 repFacId = qInfo->GetRepObjectiveFaction();
if (repFacId && GetReputationMgr().GetReputation(repFacId) < qInfo->GetRepObjectiveValue())
@@ -13945,8 +13944,10 @@ bool Player::CanRewardQuest(Quest const* quest, bool msg)
return false;
// prevent receive reward with low money and GetRewOrReqMoney() < 0
if (quest->GetRewOrReqMoney() < 0 && !HasEnoughMoney(-int64(quest->GetRewOrReqMoney())))
return false;
int32 rewardMoney = quest->GetMoneyReward(this);
if (rewardMoney < 0)
if (!HasEnoughMoney(-int64(rewardMoney)))
return false;
// dungeon finder quests cannot be rewarded when hit weekly currency limit
if (quest->IsDFQuest())
@@ -14189,10 +14190,35 @@ void Player::IncompleteQuest(uint32 quest_id)
SetQuestStatus(quest_id, QUEST_STATUS_INCOMPLETE);
uint16 log_slot = FindQuestSlot(quest_id);
if (log_slot < MAX_QUEST_LOG_SIZE)
RemoveQuestSlotState(log_slot, QUEST_STATE_COMPLETE);
if (log_slot < MAX_QUEST_LOG_SIZE) RemoveQuestSlotState(log_slot, QUEST_STATE_COMPLETE);
}
}
uint32 Player::GetQuestMoneyReward(Quest const* quest) const
{
uint32 moneyReward = quest->GetMoneyReward(this) * sWorld->getRate(RATE_MONEY_QUEST);
if (IsMaxLevel())
moneyReward += quest->GetRewMoneyMaxLevel(this) * sWorld->getRate(RATE_MONEY_MAX_LEVEL_QUEST);
return moneyReward;
}
uint32 Player::GetQuestXPReward(Quest const* quest)
{
bool rewarded = IsQuestRewarded(quest->GetQuestId()) && !quest->IsDFQuest();
// Not give XP in case already completed once repeatable quest
if (rewarded)
return 0;
uint32 XP = quest->XPValue(this) * sWorld->getRate(RATE_XP_QUEST);
// handle SPELL_AURA_MOD_XP_QUEST_PCT auras
Unit::AuraEffectList const& ModXPPctAuras = GetAuraEffectsByType(SPELL_AURA_MOD_XP_QUEST_PCT);
for (Unit::AuraEffectList::const_iterator i = ModXPPctAuras.begin(); i != ModXPPctAuras.end(); ++i)
AddPct(XP, (*i)->GetAmount());
return XP;
}
void Player::RewardQuest(Quest const* quest, uint32 reward, Object* questGiver, bool announce)
{
@@ -14283,30 +14309,22 @@ void Player::RewardQuest(Quest const* quest, uint32 reward, Object* questGiver,
if (log_slot < MAX_QUEST_LOG_SIZE)
SetQuestSlot(log_slot, 0);
bool rewarded = IsQuestRewarded(quest_id) && !quest->IsDFQuest();
// Not give XP in case already completed once repeatable quest
uint32 XP = rewarded ? 0 : uint32(quest->GetXPReward(this) * sWorld->getRate(RATE_XP_QUEST));
// handle SPELL_AURA_MOD_XP_QUEST_PCT auras
Unit::AuraEffectList const& ModXPPctAuras = GetAuraEffectsByType(SPELL_AURA_MOD_XP_QUEST_PCT);
for (Unit::AuraEffectList::const_iterator i = ModXPPctAuras.begin(); i != ModXPPctAuras.end(); ++i)
AddPct(XP, (*i)->GetAmount());
uint32 XP = GetQuestXPReward(quest);
if (!IsMaxLevel())
GiveXP(XP, nullptr);
if (Guild* guild = GetGuild())
{
uint32 _xp = quest->GetXPReward(this);
uint32 _xp = XP;
uint32 guildRep = std::max(uint32(1), uint32(_xp / 450));
guild->GiveXP(uint32(_xp * sWorld->getRate(RATE_XP_QUEST) * sWorld->getRate(RATE_XP_QUEST_GUILD_MODIFIER)), this);
guild->GiveReputation(guildRep, this);
guild->UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_COMPLETE_QUESTS_GUILD, 0, 0, 0, nullptr, this);
}
// Give player extra money if GetRewOrReqMoney > 0 and get ReqMoney if negative
if (int32 moneyRew = quest->GetRewOrReqMoney(this))
int32 moneyRew = GetQuestMoneyReward(quest);
if (moneyRew)
{
ModifyMoney(moneyRew);
@@ -15840,13 +15858,14 @@ void Player::MoneyChanged(uint64 value)
continue;
Quest const* qInfo = sObjectMgr->GetQuestTemplate(questid);
if (qInfo && qInfo->GetRewOrReqMoney() < 0)
int32 requiredMoney = qInfo ? qInfo->GetMoneyReward(this) : 0;
if (qInfo && requiredMoney < 0)
{
QuestStatusData& q_status = m_QuestStatus[questid];
if (q_status.Status == QUEST_STATUS_INCOMPLETE)
{
if (int64(value) >= -int64(qInfo->GetRewOrReqMoney()))
if (int64(value) >= -int64(requiredMoney))
{
if (CanCompleteQuest(questid))
CompleteQuest(questid);
@@ -15854,7 +15873,7 @@ void Player::MoneyChanged(uint64 value)
}
else if (q_status.Status == QUEST_STATUS_COMPLETE)
{
if (int64(value) < -int64(qInfo->GetRewOrReqMoney()))
if (int64(value) < -int64(requiredMoney))
IncompleteQuest(questid);
}
}
@@ -15991,14 +16010,10 @@ void Player::SendQuestReward(Quest const* quest, Creature const* questGiver, uin
uint32 questId = quest->GetQuestId();
sGameEventMgr->HandleQuestComplete(questId);
uint32 XP = 0;
if (!IsMaxLevel())
XP = xp;
WorldPackets::Quest::QuestGiverQuestComplete packet;
packet.QuestID = questId;
packet.XPReward = XP;
packet.MoneyReward = quest->GetRewOrReqMoney(this);
packet.MoneyReward = GetQuestMoneyReward(quest);
packet.XPReward = IsMaxLevel() ? 0 : xp;
packet.NumSkillUpsReward = quest->GetRewardSkillPoints();
packet.TalentReward = quest->GetBonusTalents();
packet.SkillLineIDReward = quest->GetRewardSkillId();
+2
View File
@@ -1314,6 +1314,8 @@ class TC_GAME_API Player : public Unit, public GridObject<Player>
void AbandonQuest(uint32 quest_id);
void CompleteQuest(uint32 quest_id);
void IncompleteQuest(uint32 quest_id);
uint32 GetQuestMoneyReward(Quest const* quest) const;
uint32 GetQuestXPReward(Quest const* quest);
void RewardQuest(Quest const* quest, uint32 reward, Object* questGiver, bool announce = true);
void SetRewardedQuest(uint32 quest_id);
void FailQuest(uint32 quest_id);
+19 -18
View File
@@ -4067,37 +4067,38 @@ void ObjectMgr::LoadQuests()
_exclusiveQuestGroups.clear();
QueryResult result = WorldDatabase.Query("SELECT "
//0 1 2 3 4 5 6
// 0 1 2 3 4 5 6
"ID, QuestType, QuestLevel, MinLevel, QuestSortID, QuestInfoID, SuggestedGroupNum, "
//7 8 9 10
// 7 8 9 10
"RequiredFactionId1, RequiredFactionId2, RequiredFactionValue1, RequiredFactionValue2, "
//11 12 13 14 15 16 17 18
"RewardNextQuest, RewardXPDifficulty, RewardMoney, RewardBonusMoney, RewardDisplaySpell, RewardSpell, RewardHonor, RewardKillHonor, "
//19 20 21 22 23 24
// 11 12 13 14 15 16
"RewardNextQuest, RewardXPDifficulty, RewardDisplaySpell, RewardSpell, RewardHonor, RewardKillHonor, "
// 17 18 19 20 21 22
"StartItem, Flags, MinimapTargetMark, RewardTitle, RequiredPlayerKills, RewardTalents, "
//25 26 27 28 29 30
// 23 24 25 26 27 28
"RewardArenaPoints, RewardSkillId, RewardSkillPoints, RewardReputationMask, QuestGiverPortrait, QuestTurnInPortrait, "
//31 32 33 34 35 36 37 38
// 29 30 31 32 33 34 35 36
"RewardItem1, RewardAmount1, RewardItem2, RewardAmount2, RewardItem3, RewardAmount3, RewardItem4, RewardAmount4, "
//39 40 41 42 43 44 45 46 47 48 49 50
// 37 38 39 40 41 42 43 44 45 46 47 48
"RewardChoiceItemID1, RewardChoiceItemQuantity1, RewardChoiceItemID2, RewardChoiceItemQuantity2, RewardChoiceItemID3, RewardChoiceItemQuantity3, RewardChoiceItemID4, RewardChoiceItemQuantity4, RewardChoiceItemID5, RewardChoiceItemQuantity5, RewardChoiceItemID6, RewardChoiceItemQuantity6, "
//51 52 53 54 55 56 57 58 59 60 61 62 63 64 65
// 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63
"RewardFactionID1, RewardFactionValue1, RewardFactionOverride1, RewardFactionID2, RewardFactionValue2, RewardFactionOverride2, RewardFactionID3, RewardFactionValue3, RewardFactionOverride3, RewardFactionID4, RewardFactionValue4, RewardFactionOverride4, RewardFactionID5, RewardFactionValue5, RewardFactionOverride5, "
//66 67 68 69 70 71 72 73 74
// 64 65 66 67 68 69 70 71 72
"POIContinent, POIx, POIy, POIPriority, LogTitle, LogDescription, QuestDescription, AreaDescription, QuestCompletionLog, "
//75 76 77 78 79 80 81 82
// 73 74 75 76 77 78 79 80
"RequiredNpcOrGo1, RequiredNpcOrGo2, RequiredNpcOrGo3, RequiredNpcOrGo4, RequiredNpcOrGoCount1, RequiredNpcOrGoCount2, RequiredNpcOrGoCount3, RequiredNpcOrGoCount4, "
//83 84 85 86 87 88 89 90
// 81 82 83 84 85 86 87 88
"ItemDrop1, ItemDrop2, ItemDrop3, ItemDrop4, ItemDropQuantity1, ItemDropQuantity2, ItemDropQuantity3, ItemDropQuantity4, "
//91 92 93 94 95 96 97 98 99 100 101 102
// 89 90 91 92 93 94 95 96 97 98 99 100
"RequiredItemId1, RequiredItemId2, RequiredItemId3, RequiredItemId4, RequiredItemId5, RequiredItemId6, RequiredItemCount1, RequiredItemCount2, RequiredItemCount3, RequiredItemCount4, RequiredItemCount5, RequiredItemCount6, "
//103 104 105 106 107 108 109 110 111 112 113 114 115
// 101 102 103 104 105 106 107 108 109 110 111 112 113
"RequiredSpell, ObjectiveText1, ObjectiveText2, ObjectiveText3, ObjectiveText4, RewardCurrencyId1, RewardCurrencyId2, RewardCurrencyId3, RewardCurrencyId4, RewardCurrencyCount1, RewardCurrencyCount2, RewardCurrencyCount3, RewardCurrencyCount4, "
//116 117 118 119 120 121 122 123
// 114 115 116 117 118 119 120 121
"RequiredCurrencyId1, RequiredCurrencyId2, RequiredCurrencyId3, RequiredCurrencyId4, RequiredCurrencyCount1, RequiredCurrencyCount2, RequiredCurrencyCount3, RequiredCurrencyCount4, "
//124 125 126 127 128 129
// 122 123 124 125 126 127
"QuestGiverTextWindow, QuestGiverTargetName, QuestTurnTextWindow, QuestTurnTargetName, SoundAccept, SoundTurnIn"
" FROM quest_template");
if (!result)
{
TC_LOG_ERROR("server.loading", ">> Loaded 0 quests definitions. DB table `quest_template` is empty.");
@@ -4142,8 +4143,8 @@ void ObjectMgr::LoadQuests()
// 0 1 2 3 4 5 6 7 8 9
{ "ID, MaxLevel, AllowableClasses, SourceSpellID, PrevQuestID, NextQuestID, ExclusiveGroup, BreadcrumbForQuestId, RewardMailTemplateID, RewardMailDelay,"
// 10 11 12 13 14 15 16 17 18 19 20
" RequiredSkillID, RequiredSkillPoints, RequiredMinRepFaction, RequiredMaxRepFaction, RequiredMinRepValue, RequiredMaxRepValue, ProvidedItemCount, SpecialFlags, AllowableRaces, TimeAllowed, AbandonSpellID", "quest_template_addon", "template addons", &Quest::LoadQuestTemplateAddon },
// 10 11 12 13 14 15 16 17 18 19 20
" RequiredSkillID, RequiredSkillPoints, RequiredMinRepFaction, RequiredMaxRepFaction, RequiredMinRepValue, RequiredMaxRepValue, ProvidedItemCount, SpecialFlags, AllowableRaces, TimeAllowed, AbandonSpellID, QuestMaxScalingLevel, RewardMoneyDifficulty, RewardMoneyMultiplier, RequiredMoney", "quest_template_addon", "template addons", &Quest::LoadQuestTemplateAddon },
// 0 1
{ "QuestId, RewardMailSenderEntry", "quest_mail_sender", "mail sender entries", &Quest::LoadQuestMailSender }
+15 -11
View File
@@ -193,8 +193,10 @@ void WorldSession::SendLfgPlayerLockInfo()
if (currentQuest)
{
playerDungeonInfo.Rewards.RewardMoney = currentQuest->GetRewOrReqMoney(_player);
playerDungeonInfo.Rewards.RewardXP = _player->getLevel() < sWorld->getIntConfig(CONFIG_MAX_PLAYER_LEVEL) ? currentQuest->GetXPReward(_player) : 0;
playerDungeonInfo.Rewards.RewardMoney = _player->GetQuestMoneyReward(currentQuest);
if (!_player->IsMaxLevel())
playerDungeonInfo.Rewards.RewardXP = _player->GetQuestXPReward(currentQuest);
for (uint8 i = 0; i < QUEST_REWARDS_COUNT; ++i)
if (uint32 itemId = currentQuest->RewardItemId[i])
playerDungeonInfo.Rewards.Item.emplace_back(itemId, currentQuest->RewardItemIdCount[i]);
@@ -213,8 +215,10 @@ void WorldSession::SendLfgPlayerLockInfo()
if (shortageQuest)
{
playerDungeonInfo.ShortageEligible = true;
playerDungeonInfo.ShortageReward.RewardMoney = shortageQuest->GetRewOrReqMoney(_player);;
playerDungeonInfo.ShortageReward.RewardXP = _player->getLevel() < sWorld->getIntConfig(CONFIG_MAX_PLAYER_LEVEL) ? shortageQuest->GetXPReward(_player) : 0;
playerDungeonInfo.ShortageReward.RewardMoney = _player->GetQuestMoneyReward(currentQuest);
if (!_player->IsMaxLevel())
playerDungeonInfo.ShortageReward.RewardXP = _player->GetQuestXPReward(currentQuest);
for (uint8 i = 0; i < QUEST_REWARDS_COUNT; ++i)
if (uint32 itemId = shortageQuest->RewardItemId[i])
playerDungeonInfo.ShortageReward.Item.emplace_back(itemId, shortageQuest->RewardItemIdCount[i]);
@@ -493,18 +497,18 @@ void WorldSession::SendLfgPlayerReward(lfg::LfgPlayerRewardData const& rewardDat
WorldPackets::LFG::LFGPlayerReward lfgPlayerReward;
lfgPlayerReward.QueuedSlot = rewardData.rdungeonEntry;
lfgPlayerReward.ActualSlot = rewardData.sdungeonEntry;
uint32 rewardMoney = rewardData.quest->GetRewOrReqMoney(_player);
uint32 rewardExp = _player->getLevel() < sWorld->getIntConfig(CONFIG_MAX_PLAYER_LEVEL) ? rewardData.quest->GetXPReward(_player) : 0;
lfgPlayerReward.RewardMoney = _player->GetQuestMoneyReward(rewardData.quest);
if (!_player->IsMaxLevel())
lfgPlayerReward.AddedXP = _player->GetQuestXPReward(rewardData.quest);
if (rewardData.shortageQuest)
{
rewardMoney += rewardData.shortageQuest->GetRewOrReqMoney(_player);
rewardExp += _player->getLevel() < sWorld->getIntConfig(CONFIG_MAX_PLAYER_LEVEL) ? rewardData.shortageQuest->GetXPReward(_player) : 0;
lfgPlayerReward.RewardMoney += _player->GetQuestMoneyReward(rewardData.shortageQuest);
if (!_player->IsMaxLevel())
lfgPlayerReward.AddedXP += _player->GetQuestXPReward(rewardData.shortageQuest);
}
lfgPlayerReward.RewardMoney = rewardMoney;
lfgPlayerReward.AddedXP = rewardExp;
for (uint8 i = 0; i < QUEST_REWARDS_COUNT; ++i)
if (uint32 itemId = rewardData.quest->RewardItemId[i])
lfgPlayerReward.Rewards.emplace_back(itemId, rewardData.quest->RewardItemIdCount[i], false);
+101 -89
View File
@@ -16,6 +16,7 @@
*/
#include "QuestDef.h"
#include "DBCEnums.h"
#include "DBCStores.h"
#include "Field.h"
#include "Log.h"
@@ -40,29 +41,27 @@ Quest::Quest(Field* questRecord)
_requiredFactionValue2 = questRecord[10].GetInt32();
_rewardNextQuest = questRecord[11].GetUInt32();
_rewardXPDifficulty = questRecord[12].GetUInt8();
_rewardMoney = questRecord[13].GetInt32();
_rewardBonusMoney = questRecord[14].GetUInt32();
_rewardDisplaySpell = questRecord[15].GetUInt32();
_rewardSpell = questRecord[16].GetInt32();
_rewardHonor = questRecord[17].GetUInt32();
_rewardKillHonor = questRecord[18].GetFloat();
_startItem = questRecord[19].GetUInt32();
_flags = questRecord[20].GetUInt32();
_minimapTargetMark = questRecord[21].GetUInt8();
_rewardTitleId = questRecord[22].GetUInt8();
_requiredPlayerKills = questRecord[23].GetUInt8();
_rewardTalents = questRecord[24].GetUInt8();
_rewardArenaPoints = questRecord[25].GetUInt16();
_rewardSkillId = questRecord[26].GetUInt16();
_rewardSkillPoints = questRecord[27].GetUInt8();
_rewardReputationMask = questRecord[28].GetUInt8();
_questGiverPortrait = questRecord[29].GetUInt32();
_questTurnInPortrait = questRecord[30].GetUInt32();
_rewardDisplaySpell = questRecord[13].GetUInt32();
_rewardSpell = questRecord[14].GetInt32();
_rewardHonor = questRecord[15].GetUInt32();
_rewardKillHonor = questRecord[16].GetFloat();
_startItem = questRecord[17].GetUInt32();
_flags = questRecord[18].GetUInt32();
_minimapTargetMark = questRecord[19].GetUInt8();
_rewardTitleId = questRecord[20].GetUInt8();
_requiredPlayerKills = questRecord[21].GetUInt8();
_rewardTalents = questRecord[22].GetUInt8();
_rewardArenaPoints = questRecord[23].GetUInt16();
_rewardSkillId = questRecord[24].GetUInt16();
_rewardSkillPoints = questRecord[25].GetUInt8();
_rewardReputationMask = questRecord[26].GetUInt8();
_questGiverPortrait = questRecord[27].GetUInt32();
_questTurnInPortrait = questRecord[28].GetUInt32();
for (uint32 i = 0; i < QUEST_REWARDS_COUNT; ++i)
{
RewardItemId[i] = questRecord[31 + i * 2].GetUInt32();
RewardItemIdCount[i] = questRecord[32 + i * 2].GetUInt16();
RewardItemId[i] = questRecord[29 + i * 2].GetUInt32();
RewardItemIdCount[i] = questRecord[30 + i * 2].GetUInt16();
if (RewardItemId[i])
++_rewItemsCount;
@@ -70,8 +69,8 @@ Quest::Quest(Field* questRecord)
for (uint32 i = 0; i < QUEST_REWARD_CHOICES_COUNT; ++i)
{
RewardChoiceItemId[i] = questRecord[39 + i * 2].GetUInt32();
RewardChoiceItemCount[i] = questRecord[40 + i * 2].GetUInt16();
RewardChoiceItemId[i] = questRecord[37 + i * 2].GetUInt32();
RewardChoiceItemCount[i] = questRecord[38 + i * 2].GetUInt16();
if (RewardChoiceItemId[i])
++_rewChoiceItemsCount;
@@ -79,26 +78,26 @@ Quest::Quest(Field* questRecord)
for (uint32 i = 0; i < QUEST_REPUTATIONS_COUNT; ++i)
{
RewardFactionId[i] = questRecord[51 + i * 3].GetUInt16();
RewardFactionValueId[i] = questRecord[52 + i * 3].GetInt32();
RewardFactionValueIdOverride[i] = questRecord[53 + i * 3].GetInt32();
RewardFactionId[i] = questRecord[49 + i * 3].GetUInt16();
RewardFactionValueId[i] = questRecord[50 + i * 3].GetInt32();
RewardFactionValueIdOverride[i] = questRecord[51 + i * 3].GetInt32();
}
_poiContinent = questRecord[66].GetUInt16();
_poiX = questRecord[67].GetFloat();
_poiY = questRecord[68].GetFloat();
_poiPriority = questRecord[69].GetUInt32();
_title = questRecord[70].GetString();
_objectives = questRecord[71].GetString();
_details = questRecord[72].GetString();
_areaDescription = questRecord[73].GetString();
_completedText = questRecord[74].GetString();
_poiContinent = questRecord[64].GetUInt16();
_poiX = questRecord[65].GetFloat();
_poiY = questRecord[66].GetFloat();
_poiPriority = questRecord[67].GetUInt32();
_title = questRecord[68].GetString();
_objectives = questRecord[69].GetString();
_details = questRecord[70].GetString();
_areaDescription = questRecord[71].GetString();
_completedText = questRecord[72].GetString();
for (uint32 i = 0; i < QUEST_OBJECTIVES_COUNT; ++i)
{
RequiredNpcOrGo[i] = questRecord[75 + i].GetInt32();
RequiredNpcOrGoCount[i] = questRecord[79 + i].GetUInt16();
ObjectiveText[i] = questRecord[104 + i].GetString();
RequiredNpcOrGo[i] = questRecord[73 + i].GetInt32();
RequiredNpcOrGoCount[i] = questRecord[77 + i].GetUInt16();
ObjectiveText[i] = questRecord[102 + i].GetString();
if (RequiredNpcOrGo[i])
++_reqNpcOrGoCount;
@@ -106,14 +105,14 @@ Quest::Quest(Field* questRecord)
for (uint32 i = 0; i < QUEST_SOURCE_ITEM_IDS_COUNT; ++i)
{
ItemDrop[i] = questRecord[83 + i].GetUInt32();
ItemDropQuantity[i] = questRecord[87 + i].GetUInt16();
ItemDrop[i] = questRecord[81 + i].GetUInt32();
ItemDropQuantity[i] = questRecord[85 + i].GetUInt16();
}
for (uint32 i = 0; i < QUEST_ITEM_OBJECTIVES_COUNT; ++i)
{
RequiredItemId[i] = questRecord[91 + i].GetUInt32();
RequiredItemCount[i] = questRecord[97 + i].GetUInt16();
RequiredItemId[i] = questRecord[89 + i].GetUInt32();
RequiredItemCount[i] = questRecord[95 + i].GetUInt16();
if (RequiredItemId[i])
++_reqItemsCount;
@@ -129,12 +128,12 @@ Quest::Quest(Field* questRecord)
OfferRewardEmoteDelay[i] = 0;
}
_requiredSpell = questRecord[103].GetUInt32();
_requiredSpell = questRecord[101].GetUInt32();
for (uint32 i = 0; i < QUEST_REWARD_CURRENCY_COUNT; ++i)
{
RewardCurrencyId[i] = questRecord[108 + i].GetUInt16();
RewardCurrencyCount[i] = questRecord[112 + i].GetUInt32();
RewardCurrencyId[i] = questRecord[106 + i].GetUInt16();
RewardCurrencyCount[i] = questRecord[110 + i].GetUInt32();
if (RewardCurrencyId[i])
++_rewCurrencyCount;
@@ -142,19 +141,19 @@ Quest::Quest(Field* questRecord)
for (uint32 i = 0; i < QUEST_REQUIRED_CURRENCY_COUNT; ++i)
{
RequiredCurrencyId[i] = questRecord[116 + i].GetUInt16();
RequiredCurrencyCount[i] = questRecord[120 + i].GetUInt32();
RequiredCurrencyId[i] = questRecord[114 + i].GetUInt16();
RequiredCurrencyCount[i] = questRecord[118 + i].GetUInt32();
if (RequiredCurrencyId[i])
++_reqCurrencyCount;
}
_questGiverTextWindow = questRecord[124].GetString();
_questGiverTargetName = questRecord[125].GetString();
_questTurnTextWindow = questRecord[126].GetString();
_questTurnTargetName = questRecord[127].GetString();
_soundAccept = questRecord[128].GetUInt16();
_soundTurnIn = questRecord[129].GetUInt16();
_questGiverTextWindow = questRecord[122].GetString();
_questGiverTargetName = questRecord[123].GetString();
_questTurnTextWindow = questRecord[124].GetString();
_questTurnTargetName = questRecord[125].GetString();
_soundAccept = questRecord[126].GetUInt16();
_soundTurnIn = questRecord[127].GetUInt16();
}
void Quest::LoadQuestDetails(Field* fields)
@@ -233,6 +232,10 @@ void Quest::LoadQuestTemplateAddon(Field* fields)
_allowableRaces = fields[18].GetUInt32();
_timeAllowed = fields[19].GetInt32();
_questAbandonSpell = fields[20].GetUInt32();
_maxScalingLevel = fields[21].GetUInt32();
_rewardMoneyDifficulty = fields[22].GetUInt32();
_rewardMoneyMultiplier = fields[23].GetFloat();
_requiredMoney = fields[24].GetInt32();
}
void Quest::LoadQuestMailSender(Field* fields)
@@ -240,37 +243,42 @@ void Quest::LoadQuestMailSender(Field* fields)
_rewardMailSenderEntry = fields[1].GetUInt32();
}
uint32 Quest::GetXPReward(Player const* player) const
uint32 Quest::XPValue(Player const* player) const
{
if (!player)
return 0;
return Quest::CalcXPReward(player->getLevel(), _level, _rewardXPDifficulty);
return XPValue(player ? player->getLevel() : 0, GetQuestLevelForPlayer(player), _level, _rewardXPDifficulty);
}
/*static*/ uint32 Quest::CalcXPReward(uint8 playerLevel, int32 targetLevel, uint8 xpDifficulty)
/*static*/ uint32 Quest::XPValue(uint8 playerLevel, uint32 questLevel, int32 unscaledQuestLevel, uint32 xpDifficulty, float xpMultiplier /*= 1.0f*/)
{
int32 effectiveLevel = (targetLevel == -1 ? playerLevel : targetLevel);
const QuestXPEntry* xpentry = sQuestXPStore.LookupEntry(effectiveLevel);
if (!xpentry)
QuestXPEntry const* questXp = sQuestXPStore.LookupEntry(questLevel);
if (!questXp || xpDifficulty >= MAX_QUEST_DIFFICULTY)
return 0;
int32 diffFactor = 2 * (effectiveLevel - playerLevel) + 20;
int32 diffFactor = 2 * (questLevel - (unscaledQuestLevel == -1 ? 0 : -5) - playerLevel) + 10;
if (diffFactor < 1)
diffFactor = 1;
else if (diffFactor > 10)
diffFactor = 10;
uint32 xp = RoundXPValue(diffFactor * xpentry->Difficulty[xpDifficulty] / 10);
uint32 xp = RoundXPValue(diffFactor * questXp->Difficulty[xpDifficulty] * xpMultiplier / 10);
if (sWorld->getIntConfig(CONFIG_MIN_QUEST_SCALED_XP_RATIO))
{
uint32 minScaledXP = RoundXPValue(xpentry->Difficulty[xpDifficulty]) * sWorld->getIntConfig(CONFIG_MIN_QUEST_SCALED_XP_RATIO) / 100;
uint32 minScaledXP = RoundXPValue(questXp->Difficulty[xpDifficulty] * xpMultiplier) * sWorld->getIntConfig(CONFIG_MIN_QUEST_SCALED_XP_RATIO) / 100;
xp = std::max(minScaledXP, xp);
}
return xp;
}
uint32 Quest::GetQuestLevelForPlayer(Player const* player) const
{
if (_level != -1)
return static_cast<uint32>(_level);
return std::clamp<int32>(player->getLevel(), _minLevel, _maxScalingLevel);
}
/*static*/ bool Quest::IsTakingQuestEnabled(uint32 questId)
{
if (!sQuestPoolMgr->IsQuestActive(questId))
@@ -279,25 +287,39 @@ uint32 Quest::GetXPReward(Player const* player) const
return true;
}
int32 Quest::GetRewOrReqMoney(Player const* player) const
int32 Quest::GetMoneyReward(Player const* player) const
{
// RequiredMoney: the amount is the negative copper sum.
if (_rewardMoney < 0)
return _rewardMoney;
// Some quests cost money instead of rewarding it
if (_requiredMoney < 0)
return _requiredMoney;
// RewardMoney: the positive amount
if (!player || !player->IsMaxLevel())
return int32(_rewardMoney * sWorld->getRate(RATE_MONEY_QUEST));
else // At level cap, the money reward is the maximum amount between normal and bonus money reward
return std::max(int32(GetRewMoneyMaxLevel()), int32(_rewardMoney * sWorld->getRate(RATE_MONEY_QUEST)));
if (std::array<uint32, MAX_QUEST_DIFFICULTY> const* money = sObjectMgr->GetQuestMoneyRewards(player ? GetQuestLevelForPlayer(player) : _level))
return money->at(GetRewMoneyDifficulty()) * GetRewMoneyMultiplier();
return 0;
}
uint32 Quest::GetRewMoneyMaxLevel(Player const* player /*= nullptr*/) const
{
// If Quest has flag to not give money on max level, it's 0
if (HasFlag(QUEST_FLAGS_NO_MONEY_FOR_XP))
return 0;
uint32 maxQuestLevel = _level == -1 ? _maxScalingLevel : _level;
// Query initialization during startup does not provide a valid player so we just go for the expansion's max level
uint8 playerLevel = player ? player->getLevel() : DBCManager::GetMaxLevelForExpansion(CURRENT_EXPANSION);
// The conversion rate is XP * 6 copper
return XPValue(playerLevel, maxQuestLevel, _level, _rewardXPDifficulty) * 6;
}
void Quest::BuildQuestRewards(WorldPackets::Quest::QuestRewards& rewards, Player* player) const
{
rewards.ChoiceItemCount = GetRewChoiceItemsCount();
rewards.ItemCount = GetReqItemsCount();
rewards.Money = GetRewOrReqMoney(player);
rewards.XP = GetXPReward(player) * sWorld->getRate(RATE_XP_QUEST);
rewards.Money = player->GetQuestMoneyReward(this);
rewards.XP = player->GetQuestXPReward(this);
rewards.Title = GetCharTitleId();
rewards.FactionFlags = GetRewardReputationMask();
rewards.SpellCompletionDisplayID = GetRewSpell();
@@ -336,16 +358,6 @@ void Quest::BuildQuestRewards(WorldPackets::Quest::QuestRewards& rewards, Player
}
}
uint32 Quest::GetRewMoneyMaxLevel() const
{
// If Quest has flag to not give money on max level, it's 0
if (HasFlag(QUEST_FLAGS_NO_MONEY_FOR_XP))
return 0;
// Else, return the rewarded copper sum modified by the rate
return uint32(_rewardBonusMoney * sWorld->getRate(RATE_MONEY_MAX_LEVEL_QUEST));
}
bool Quest::IsAutoAccept() const
{
return !sWorld->getBoolConfig(CONFIG_QUEST_IGNORE_AUTO_ACCEPT) && HasFlag(QUEST_FLAGS_AUTO_ACCEPT);
@@ -429,12 +441,12 @@ void Quest::InitializeQueryData()
WorldPacket queryTemp;
for (uint8 loc = LOCALE_enUS; loc < TOTAL_LOCALES; ++loc)
{
queryTemp = BuildQueryData(static_cast<LocaleConstant>(loc));
queryTemp = BuildQueryData(static_cast<LocaleConstant>(loc), nullptr);
QueryData[loc] = queryTemp;
}
}
WorldPacket Quest::BuildQueryData(LocaleConstant loc) const
WorldPacket Quest::BuildQueryData(LocaleConstant loc, Player const* player) const
{
WorldPackets::Quest::QueryQuestInfoResponse response;
@@ -487,8 +499,8 @@ WorldPacket Quest::BuildQueryData(LocaleConstant loc) const
response.Info.RewardNextQuest = GetNextQuestInChain();
response.Info.RewardXPDifficulty = GetXPId();
response.Info.RewardMoney = GetRewOrReqMoney();
response.Info.RewardBonusMoney = GetRewMoneyMaxLevel();
response.Info.RewardMoney = GetMoneyReward(player);
response.Info.RewardBonusMoney = GetRewMoneyMaxLevel(player);
response.Info.RewardDisplaySpell = GetRewSpell();
response.Info.RewardSpell = GetRewSpellCast();
+13 -7
View File
@@ -237,8 +237,11 @@ class TC_GAME_API Quest
void LoadQuestTemplateAddon(Field* fields);
void LoadQuestMailSender(Field* fields);
uint32 GetXPReward(Player const* player) const;
static uint32 CalcXPReward(uint8 playerLevel, int32 targetLevel, uint8 xpDifficulty);
uint32 XPValue(Player const* player) const;
static uint32 XPValue(uint8 playerLevel, uint32 questLevel, int32 unscaledQuestLevel, uint32 xpDifficulty, float xpMultiplier = 1.0f);
int32 GetMoneyReward(Player const* player) const;
uint32 GetRewMoneyMaxLevel(Player const* player = nullptr) const;
uint32 GetQuestLevelForPlayer(Player const* player) const;
bool HasFlag(uint32 flag) const { return (_flags & flag) != 0; }
void SetFlag(uint32 flag) { _flags |= flag; }
@@ -298,10 +301,8 @@ class TC_GAME_API Quest
std::string const& GetQuestGiverTargetName() const { return _questGiverTargetName; }
std::string const& GetQuestTurnTextWindow() const { return _questTurnTextWindow; }
std::string const& GetQuestTurnTargetName() const { return _questTurnTargetName; }
int32 GetRewOrReqMoney(Player const* player = nullptr) const;
uint32 GetRewHonorAddition() const { return _rewardHonor; }
float GetRewHonorMultiplier() const { return _rewardKillHonor; }
uint32 GetRewMoneyMaxLevel() const; // use in XP calculation at client
uint32 GetRewSpell() const { return _rewardDisplaySpell; }
int32 GetRewSpellCast() const { return _rewardSpell; }
uint32 GetRewMailTemplateId() const { return _rewardMailTemplateId; }
@@ -373,6 +374,9 @@ class TC_GAME_API Quest
uint32 GetRewCurrencyCount() const { return _rewCurrencyCount; }
uint32 GetReqCurrencyCount() const { return _reqCurrencyCount; }
uint32 GetQuestAbandonSpell() const { return _questAbandonSpell; }
uint32 GetQuestMaxScalingLevel() const { return _maxScalingLevel; }
uint32 GetRewMoneyDifficulty() const { return _rewardMoneyDifficulty; }
float GetRewMoneyMultiplier() const { return _rewardMoneyMultiplier; }
void SetEventIdForQuest(uint16 eventId) { _eventIdForQuest = eventId; }
uint16 GetEventIdForQuest() const { return _eventIdForQuest; }
@@ -383,7 +387,7 @@ class TC_GAME_API Quest
static void AddQuestLevelToTitle(std::string& title, int32 level);
void InitializeQueryData();
WorldPacket BuildQueryData(LocaleConstant loc) const;
WorldPacket BuildQueryData(LocaleConstant loc, Player const* player) const;
std::vector<uint32> DependentPreviousQuests;
std::vector<uint32> DependentBreadcrumbQuests;
@@ -428,8 +432,6 @@ class TC_GAME_API Quest
std::string _completedText;
uint32 _rewardHonor = 0;
float _rewardKillHonor = 0.f;
int32 _rewardMoney = 0;
uint32 _rewardBonusMoney = 0;
uint32 _rewardDisplaySpell = 0;
int32 _rewardSpell = 0;
uint32 _poiContinent = 0;
@@ -478,6 +480,10 @@ class TC_GAME_API Quest
uint32 _allowableRaces = 0;
uint32 _timeAllowed = 0;
uint32 _questAbandonSpell = 0;
uint32 _maxScalingLevel = 0;
uint32 _rewardMoneyDifficulty = 0;
float _rewardMoneyMultiplier = 0.0f;
int32 _requiredMoney = 0; // Static value which is not calculated during runtime
};
struct QuestStatusData
+1 -1
View File
@@ -1773,7 +1773,7 @@ void Spell::EffectOpenLock(SpellEffIndex effIndex)
if (uint8 xpDifficulty = goInfo->chest.xpDifficulty)
{
int32 xpLevel = goInfo->chest.xpMinLevel;
player->GiveXP(Quest::CalcXPReward(player->getLevel(), xpLevel, xpDifficulty), nullptr);
player->GiveXP(Quest::XPValue(player->getLevel(), xpLevel, xpLevel, xpDifficulty), nullptr);
}
}
}
+1 -1
View File
@@ -256,7 +256,7 @@ public:
}
// If the quest requires money
int32 ReqOrRewMoney = quest->GetRewOrReqMoney(player);
int32 ReqOrRewMoney = quest->GetMoneyReward(player);
if (ReqOrRewMoney < 0)
player->ModifyMoney(-int64(ReqOrRewMoney));