|
|
|
|
@@ -76,6 +76,7 @@
|
|
|
|
|
#include "OutdoorPvPMgr.h"
|
|
|
|
|
#include "Pet.h"
|
|
|
|
|
#include "PhasingHandler.h"
|
|
|
|
|
#include "PoolMgr.h"
|
|
|
|
|
#include "QueryCallback.h"
|
|
|
|
|
#include "QueryHolder.h"
|
|
|
|
|
#include "QuestDef.h"
|
|
|
|
|
@@ -14721,7 +14722,7 @@ bool Player::CanCompleteQuest(uint32 quest_id)
|
|
|
|
|
if (!qInfo)
|
|
|
|
|
return false;
|
|
|
|
|
|
|
|
|
|
if (!qInfo->IsRepeatable() && m_RewardedQuests.find(quest_id) != m_RewardedQuests.end())
|
|
|
|
|
if (!qInfo->IsRepeatable() && GetQuestRewardStatus(quest_id))
|
|
|
|
|
return false; // not allow re-complete quest
|
|
|
|
|
|
|
|
|
|
// auto complete quest
|
|
|
|
|
@@ -15122,12 +15123,13 @@ void Player::RewardQuest(Quest const* quest, uint32 reward, Object* questGiver,
|
|
|
|
|
SendNewItem(item, quest->RewardItemIdCount[i], true, false, false, false);
|
|
|
|
|
}
|
|
|
|
|
else if (quest->IsDFQuest())
|
|
|
|
|
SendItemRetrievalMail(quest->RewardItemId[i], quest->RewardItemIdCount[i]);
|
|
|
|
|
SendItemRetrievalMail(itemId, quest->RewardItemIdCount[i]);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
for (uint8 i = 0; i < QUEST_REWARD_CURRENCY_COUNT; ++i)
|
|
|
|
|
{
|
|
|
|
|
if (quest->RewardCurrencyId[i])
|
|
|
|
|
{
|
|
|
|
|
uint32 rewardCurrencyCount = quest->RewardCurrencyCount[i];
|
|
|
|
|
@@ -15138,6 +15140,7 @@ void Player::RewardQuest(Quest const* quest, uint32 reward, Object* questGiver,
|
|
|
|
|
|
|
|
|
|
ModifyCurrency(quest->RewardCurrencyId[i], rewardCurrencyCount, !quest->IsDFQuest());
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (uint32 skill = quest->GetRewardSkillId())
|
|
|
|
|
UpdateSkillPro(skill, 1000, quest->GetRewardSkillPoints());
|
|
|
|
|
@@ -15159,10 +15162,10 @@ void Player::RewardQuest(Quest const* quest, uint32 reward, Object* questGiver,
|
|
|
|
|
if (log_slot < MAX_QUEST_LOG_SIZE)
|
|
|
|
|
SetQuestSlot(log_slot, 0);
|
|
|
|
|
|
|
|
|
|
bool rewarded = (m_RewardedQuests.find(quest_id) != m_RewardedQuests.end());
|
|
|
|
|
bool rewarded = IsQuestRewarded(quest_id) && !quest->IsDFQuest();
|
|
|
|
|
|
|
|
|
|
// Not give XP in case already completed once repeatable quest
|
|
|
|
|
uint32 XP = rewarded && !quest->IsDFQuest() ? 0 : uint32(quest->GetXPReward(this) * sWorld->getRate(RATE_XP_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);
|
|
|
|
|
@@ -15245,12 +15248,6 @@ void Player::RewardQuest(Quest const* quest, uint32 reward, Object* questGiver,
|
|
|
|
|
if (quest->CanIncreaseRewardedQuestCounters())
|
|
|
|
|
SetRewardedQuest(quest_id);
|
|
|
|
|
|
|
|
|
|
// StoreNewItem, mail reward, etc. save data directly to the database
|
|
|
|
|
// to prevent exploitable data desynchronisation we save the quest status to the database too
|
|
|
|
|
// (to prevent rewarding this quest another time while rewards were already given out)
|
|
|
|
|
SQLTransaction trans = SQLTransaction(nullptr);
|
|
|
|
|
_SaveQuestStatus(trans);
|
|
|
|
|
|
|
|
|
|
if (announce)
|
|
|
|
|
SendQuestReward(quest, questGiver ? questGiver->ToCreature() : nullptr, XP);
|
|
|
|
|
|
|
|
|
|
@@ -15260,7 +15257,7 @@ void Player::RewardQuest(Quest const* quest, uint32 reward, Object* questGiver,
|
|
|
|
|
if (quest->GetRewSpellCast() > 0)
|
|
|
|
|
{
|
|
|
|
|
SpellInfo const* spellInfo = ASSERT_NOTNULL(sSpellMgr->GetSpellInfo(quest->GetRewSpellCast()));
|
|
|
|
|
if (questGiver->isType(TYPEMASK_UNIT) && !spellInfo->HasEffect(SPELL_EFFECT_LEARN_SPELL) && !spellInfo->HasEffect(SPELL_EFFECT_CREATE_ITEM) && !quest->HasFlag(QUEST_FLAGS_PLAYER_CAST_ON_COMPLETE))
|
|
|
|
|
if (questGiver->isType(TYPEMASK_UNIT) && !spellInfo->HasEffect(SPELL_EFFECT_LEARN_SPELL) && !spellInfo->HasEffect(SPELL_EFFECT_CREATE_ITEM) && !spellInfo->IsSelfCast() && !quest->HasFlag(QUEST_FLAGS_PLAYER_CAST_ON_COMPLETE))
|
|
|
|
|
{
|
|
|
|
|
if (Creature* creature = GetMap()->GetCreature(questGiver->GetGUID()))
|
|
|
|
|
creature->CastSpell(this, quest->GetRewSpellCast(), true);
|
|
|
|
|
@@ -15271,7 +15268,7 @@ void Player::RewardQuest(Quest const* quest, uint32 reward, Object* questGiver,
|
|
|
|
|
else if (quest->GetRewSpell() > 0)
|
|
|
|
|
{
|
|
|
|
|
SpellInfo const* spellInfo = sSpellMgr->GetSpellInfo(quest->GetRewSpell());
|
|
|
|
|
if (questGiver->isType(TYPEMASK_UNIT) && !spellInfo->HasEffect(SPELL_EFFECT_LEARN_SPELL) && !spellInfo->HasEffect(SPELL_EFFECT_CREATE_ITEM) && !quest->HasFlag(QUEST_FLAGS_PLAYER_CAST_ON_COMPLETE))
|
|
|
|
|
if (questGiver->isType(TYPEMASK_UNIT) && !spellInfo->HasEffect(SPELL_EFFECT_LEARN_SPELL) && !spellInfo->HasEffect(SPELL_EFFECT_CREATE_ITEM) && !spellInfo->IsSelfCast() && !quest->HasFlag(QUEST_FLAGS_PLAYER_CAST_ON_COMPLETE))
|
|
|
|
|
{
|
|
|
|
|
if (Creature* creature = GetMap()->GetCreature(questGiver->GetGUID()))
|
|
|
|
|
creature->CastSpell(this, quest->GetRewSpell(), true);
|
|
|
|
|
@@ -15285,6 +15282,9 @@ void Player::RewardQuest(Quest const* quest, uint32 reward, Object* questGiver,
|
|
|
|
|
UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_COMPLETE_QUEST_COUNT);
|
|
|
|
|
UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_COMPLETE_QUEST, quest->GetQuestId());
|
|
|
|
|
|
|
|
|
|
// make full db save
|
|
|
|
|
SaveToDB(false);
|
|
|
|
|
|
|
|
|
|
if (quest->HasFlag(QUEST_FLAGS_FLAGS_PVP))
|
|
|
|
|
{
|
|
|
|
|
pvpInfo.IsHostile = pvpInfo.IsInHostileArea || HasPvPForcingQuest();
|
|
|
|
|
@@ -15312,13 +15312,15 @@ void Player::FailQuest(uint32 questId)
|
|
|
|
|
{
|
|
|
|
|
if (Quest const* quest = sObjectMgr->GetQuestTemplate(questId))
|
|
|
|
|
{
|
|
|
|
|
// Already complete quests shouldn't turn failed.
|
|
|
|
|
if (GetQuestStatus(questId) == QUEST_STATUS_COMPLETE && !quest->HasSpecialFlag(QUEST_SPECIAL_FLAGS_TIMED))
|
|
|
|
|
return;
|
|
|
|
|
QuestStatus qStatus = GetQuestStatus(questId);
|
|
|
|
|
|
|
|
|
|
// You can't fail a quest if you don't have it, or if it's already rewarded.
|
|
|
|
|
if (GetQuestStatus(questId) == QUEST_STATUS_NONE || GetQuestStatus(questId) == QUEST_STATUS_REWARDED)
|
|
|
|
|
return;
|
|
|
|
|
// we can only fail incomplete quest or...
|
|
|
|
|
if (qStatus != QUEST_STATUS_INCOMPLETE)
|
|
|
|
|
{
|
|
|
|
|
// completed timed quest with no requirements
|
|
|
|
|
if (qStatus != QUEST_STATUS_COMPLETE || !quest->HasSpecialFlag(QUEST_SPECIAL_FLAGS_TIMED) || !quest->HasSpecialFlag(QUEST_SPECIAL_FLAGS_COMPLETED_AT_START))
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
SetQuestStatus(questId, QUEST_STATUS_FAILED);
|
|
|
|
|
|
|
|
|
|
@@ -15698,7 +15700,7 @@ bool Player::SatisfyQuestExclusiveGroup(Quest const* qInfo, bool msg) const
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// alternative quest already started or completed - but don't check rewarded states if both are repeatable
|
|
|
|
|
if (GetQuestStatus(exclude_Id) != QUEST_STATUS_NONE || (!(qInfo->IsRepeatable() && Nquest->IsRepeatable()) && (m_RewardedQuests.find(exclude_Id) != m_RewardedQuests.end())))
|
|
|
|
|
if (GetQuestStatus(exclude_Id) != QUEST_STATUS_NONE || (!(qInfo->IsRepeatable() && Nquest->IsRepeatable()) && GetQuestRewardStatus(exclude_Id)))
|
|
|
|
|
{
|
|
|
|
|
if (msg)
|
|
|
|
|
{
|
|
|
|
|
@@ -15873,7 +15875,7 @@ bool Player::GetQuestRewardStatus(uint32 quest_id) const
|
|
|
|
|
|
|
|
|
|
// for repeatable quests: rewarded field is set after first reward only to prevent getting XP more than once
|
|
|
|
|
if (!qInfo->IsRepeatable())
|
|
|
|
|
return m_RewardedQuests.find(quest_id) != m_RewardedQuests.end();
|
|
|
|
|
return IsQuestRewarded(quest_id);
|
|
|
|
|
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
@@ -15888,14 +15890,8 @@ QuestStatus Player::GetQuestStatus(uint32 quest_id) const
|
|
|
|
|
if (itr != m_QuestStatus.end())
|
|
|
|
|
return itr->second.Status;
|
|
|
|
|
|
|
|
|
|
if (Quest const* qInfo = sObjectMgr->GetQuestTemplate(quest_id))
|
|
|
|
|
{
|
|
|
|
|
if (qInfo->IsSeasonal() && !qInfo->IsRepeatable())
|
|
|
|
|
return SatisfyQuestSeasonal(qInfo, false) ? QUEST_STATUS_NONE : QUEST_STATUS_REWARDED;
|
|
|
|
|
|
|
|
|
|
if (!qInfo->IsRepeatable() && IsQuestRewarded(quest_id))
|
|
|
|
|
return QUEST_STATUS_REWARDED;
|
|
|
|
|
}
|
|
|
|
|
if (GetQuestRewardStatus(quest_id))
|
|
|
|
|
return QUEST_STATUS_REWARDED;
|
|
|
|
|
}
|
|
|
|
|
return QUEST_STATUS_NONE;
|
|
|
|
|
}
|
|
|
|
|
@@ -15903,7 +15899,22 @@ QuestStatus Player::GetQuestStatus(uint32 quest_id) const
|
|
|
|
|
bool Player::CanShareQuest(uint32 quest_id) const
|
|
|
|
|
{
|
|
|
|
|
Quest const* qInfo = sObjectMgr->GetQuestTemplate(quest_id);
|
|
|
|
|
return qInfo && qInfo->HasFlag(QUEST_FLAGS_SHARABLE) && IsActiveQuest(quest_id);
|
|
|
|
|
if (qInfo && qInfo->HasFlag(QUEST_FLAGS_SHARABLE))
|
|
|
|
|
{
|
|
|
|
|
QuestStatusMap::const_iterator itr = m_QuestStatus.find(quest_id);
|
|
|
|
|
if (itr != m_QuestStatus.end())
|
|
|
|
|
{
|
|
|
|
|
if (itr->second.Status != QUEST_STATUS_INCOMPLETE)
|
|
|
|
|
return false;
|
|
|
|
|
|
|
|
|
|
// in pool and not currently available (wintergrasp weekly, dalaran weekly) - can't share
|
|
|
|
|
if (sPoolMgr->IsPartOfAPool<Quest>(quest_id) && !sPoolMgr->IsSpawnedObject<Quest>(quest_id))
|
|
|
|
|
return false;
|
|
|
|
|
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void Player::SetQuestStatus(uint32 questId, QuestStatus status, bool update /*= true*/)
|
|
|
|
|
@@ -15944,6 +15955,18 @@ void Player::RemoveRewardedQuest(uint32 questId, bool update /*= true*/)
|
|
|
|
|
m_RewardedQuestsSave[questId] = QUEST_FORCE_DELETE_SAVE_TYPE;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Remove seasonal quest also
|
|
|
|
|
Quest const* qInfo = sObjectMgr->GetQuestTemplate(questId);
|
|
|
|
|
if (qInfo->IsSeasonal())
|
|
|
|
|
{
|
|
|
|
|
uint16 eventId = qInfo->GetEventIdForQuest();
|
|
|
|
|
if (m_seasonalquests.find(eventId) != m_seasonalquests.end())
|
|
|
|
|
{
|
|
|
|
|
m_seasonalquests[eventId].erase(questId);
|
|
|
|
|
m_SeasonalQuestChanged = true;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (update)
|
|
|
|
|
SendQuestUpdate(questId);
|
|
|
|
|
}
|
|
|
|
|
@@ -16228,12 +16251,15 @@ void Player::AreaExploredOrEventHappens(uint32 questId)
|
|
|
|
|
{
|
|
|
|
|
QuestStatusData& q_status = m_QuestStatus[questId];
|
|
|
|
|
|
|
|
|
|
if (!q_status.Explored)
|
|
|
|
|
// Dont complete failed quest
|
|
|
|
|
if (!q_status.Explored && q_status.Status != QUEST_STATUS_FAILED)
|
|
|
|
|
{
|
|
|
|
|
q_status.Explored = true;
|
|
|
|
|
m_QuestStatusSave[questId] = QUEST_DEFAULT_SAVE_TYPE;
|
|
|
|
|
SetQuestSlotState(log_slot, QUEST_STATE_COMPLETE);
|
|
|
|
|
SendQuestComplete(qInfo);
|
|
|
|
|
|
|
|
|
|
// if we cannot complete quest send exploration succeded (to mark exploration on client)
|
|
|
|
|
if (!CanCompleteQuest(questId))
|
|
|
|
|
SendQuestComplete(qInfo);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
if (CanCompleteQuest(questId))
|
|
|
|
|
@@ -16433,7 +16459,8 @@ void Player::KilledPlayerCredit(uint16 count)
|
|
|
|
|
QuestStatusData& q_status = m_QuestStatus[questid];
|
|
|
|
|
if (q_status.Status == QUEST_STATUS_INCOMPLETE && (!GetGroup() || !GetGroup()->isRaidGroup() || qInfo->IsAllowedInRaid(GetMap()->GetDifficulty())))
|
|
|
|
|
{
|
|
|
|
|
if (qInfo->HasSpecialFlag(QUEST_SPECIAL_FLAGS_PLAYER_KILL))
|
|
|
|
|
// PvP Killing quest require player to be in same zone as quest zone (only 2 quests so no doubt)
|
|
|
|
|
if (qInfo->HasSpecialFlag(QUEST_SPECIAL_FLAGS_PLAYER_KILL) && GetZoneId() == static_cast<uint32>(qInfo->GetZoneOrSort()))
|
|
|
|
|
{
|
|
|
|
|
KilledPlayerCreditForQuest(count, qInfo);
|
|
|
|
|
break; // there is only one quest per zone
|
|
|
|
|
@@ -20478,7 +20505,7 @@ void Player::_SaveWeeklyQuestStatus(SQLTransaction& trans)
|
|
|
|
|
|
|
|
|
|
void Player::_SaveSeasonalQuestStatus(SQLTransaction& trans)
|
|
|
|
|
{
|
|
|
|
|
if (!m_SeasonalQuestChanged || m_seasonalquests.empty())
|
|
|
|
|
if (!m_SeasonalQuestChanged)
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
// we don't need transactions here.
|
|
|
|
|
@@ -20486,6 +20513,11 @@ void Player::_SaveSeasonalQuestStatus(SQLTransaction& trans)
|
|
|
|
|
stmt->setUInt32(0, GetGUID().GetCounter());
|
|
|
|
|
trans->Append(stmt);
|
|
|
|
|
|
|
|
|
|
m_SeasonalQuestChanged = false;
|
|
|
|
|
|
|
|
|
|
if (m_seasonalquests.empty())
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
for (SeasonalEventQuestMap::const_iterator iter = m_seasonalquests.begin(); iter != m_seasonalquests.end(); ++iter)
|
|
|
|
|
{
|
|
|
|
|
uint16 eventId = iter->first;
|
|
|
|
|
@@ -20501,8 +20533,6 @@ void Player::_SaveSeasonalQuestStatus(SQLTransaction& trans)
|
|
|
|
|
trans->Append(stmt);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
m_SeasonalQuestChanged = false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void Player::_SaveMonthlyQuestStatus(SQLTransaction& trans)
|
|
|
|
|
|