Core/Quests: ported quest flag and visbility update changes from master branch

This commit is contained in:
Ovahlord committed 2023-07-11 15:21:09 +02:00
1 parent 833b77c05e
commit a3dd0d75cf
16 files changed
+249 -245

No files matched your search

@@ -1205,9 +1205,9 @@ bool SmartAIMgr::IsEventValid(SmartScriptHolder& e)
case SMART_ACTION_CALL_GROUPEVENTHAPPENS:
if (Quest const* qid = sObjectMgr->GetQuestTemplate(e.action.quest.quest))
{
if (!qid->HasSpecialFlag(QUEST_SPECIAL_FLAGS_EXPLORATION_OR_EVENT))
if (!qid->HasFlag(QUEST_FLAGS_COMPLETION_EVENT) && !qid->HasFlag(QUEST_FLAGS_COMPLETION_AREA_TRIGGER))
{
TC_LOG_ERROR("sql.sql", "SmartAIMgr: Entry %d SourceType %u Event %u Action %u SpecialFlags for Quest entry %u does not include FLAGS_EXPLORATION_OR_EVENT(2), skipped.", e.entryOrGuid, e.GetScriptType(), e.event_id, e.GetActionType(), e.action.quest.quest);
TC_LOG_ERROR("sql.sql", "SmartAIMgr: Entry %d SourceType %u Event %u Action %u Flags for Quest entry %u does not include QUEST_FLAGS_COMPLETION_EVENT or QUEST_FLAGS_COMPLETION_AREA_TRIGGER, skipped.", e.entryOrGuid, e.GetScriptType(), e.event_id, e.GetActionType(), e.action.quest.quest);
return false;
}
}
@@ -235,7 +235,7 @@ void PlayerMenu::SendGossipMenu(uint32 titleTextId, ObjectGuid objectGUID)
text.QuestType = itr.QuestIcon;
text.QuestFlags = quest->GetFlags();
text.QuestLevel = quest->GetQuestLevel();
text.Repeatable = quest->IsAutoComplete() && quest->IsRepeatable() && !quest->IsDailyOrWeekly() && !quest->IsMonthly();
text.Repeatable = quest->IsTurnIn() && quest->IsRepeatable() && !quest->IsDailyOrWeekly() && !quest->IsMonthly();
text.QuestTitle = quest->GetTitle();
LocaleConstant localeConstant = _session->GetSessionDbLocaleIndex();
@@ -373,7 +373,7 @@ void PlayerMenu::SendQuestGiverQuestList(QEmote const& eEmote, const std::string
if (questLevelInTitle)
Quest::AddQuestLevelToTitle(title, quest->GetQuestLevel());
bool repeatable = quest->IsAutoComplete() && quest->IsRepeatable() && !quest->IsDailyOrWeekly() && !quest->IsMonthly();
bool repeatable = quest->IsTurnIn() && quest->IsRepeatable() && !quest->IsDailyOrWeekly() && !quest->IsMonthly();
questList.QuestDataText.emplace_back(questID, questMenuItem.QuestIcon, quest->GetQuestLevel(), quest->GetFlags(), repeatable, title);
}
}
+100 -57
View File
@@ -1342,6 +1342,9 @@ void Player::setDeathState(DeathState s)
// passive spell
if (!ressSpellId)
ressSpellId = GetResurrectionSpellId();
FailQuestsWithFlag(QUEST_FLAGS_COMPLETION_NO_DEATH);
UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_DEATH_AT_MAP, 1);
UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_DEATH, 1);
UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_DEATH_IN_DUNGEON, 1);
@@ -13705,9 +13708,9 @@ void Player::PrepareQuestMenu(ObjectGuid guid)
if (!CanTakeQuest(quest, false))
continue;
if (quest->IsAutoComplete() && (!quest->IsRepeatable() || quest->IsDaily() || quest->IsWeekly() || quest->IsMonthly()))
if (quest->IsTurnIn() && (!quest->IsRepeatable() || quest->IsDaily() || quest->IsWeekly() || quest->IsMonthly()))
qm.AddMenuItem(quest_id, 0);
else if (quest->IsAutoComplete())
else if (quest->IsTurnIn())
qm.AddMenuItem(quest_id, 4);
else if (GetQuestStatus(quest_id) == QUEST_STATUS_NONE)
qm.AddMenuItem(quest_id, 2);
@@ -13745,7 +13748,7 @@ void Player::SendPreparedQuest(ObjectGuid guid)
if (quest->IsAutoAccept() && CanAddQuest(quest, true) && CanTakeQuest(quest, true))
AddQuestAndCheckCompletion(quest, object);
if (quest->IsAutoComplete() && quest->IsRepeatable() && !quest->IsDailyOrWeekly())
if (quest->IsTurnIn() && quest->IsRepeatable() && !quest->IsDailyOrWeekly())
PlayerTalkClass->SendQuestGiverRequestItems(quest, guid, CanCompleteRepeatableQuest(quest), true);
else
{
@@ -13808,48 +13811,30 @@ bool Player::IsActiveQuest(uint32 quest_id) const
return m_QuestStatus.find(quest_id) != m_QuestStatus.end();
}
Quest const* Player::GetNextQuest(ObjectGuid guid, Quest const* quest) const
Quest const* Player::GetNextQuest(Object const* questGiver, Quest const* quest) const
{
QuestRelationResult quests;
uint32 nextQuestID = quest->GetNextQuestInChain();
switch (guid.GetHigh())
{
case HighGuid::Player:
ASSERT(quest->HasFlag(QUEST_FLAGS_AUTOCOMPLETE));
return sObjectMgr->GetQuestTemplate(nextQuestID);
case HighGuid::Unit:
case HighGuid::Pet:
case HighGuid::Vehicle:
{
if (Creature* creature = ObjectAccessor::GetCreatureOrPetOrVehicle(*this, guid))
quests = sObjectMgr->GetCreatureQuestRelations(creature->GetEntry());
else
if (!nextQuestID)
return nullptr;
break;
}
case HighGuid::GameObject:
if (questGiver == this)
{
if (!quest->HasFlag(QUEST_FLAGS_AUTO_COMPLETE))
return nullptr;
return sObjectMgr->GetQuestTemplate(nextQuestID);
}
//we should obtain map pointer from GetMap() in 99% of cases. Special case
//only for quests which cast teleport spells on player
Map* _map = IsInWorld() ? GetMap() : sMapMgr->FindMap(GetMapId(), GetInstanceId());
ASSERT(_map);
if (GameObject* gameObject = _map->GetGameObject(guid))
quests = sObjectMgr->GetGOQuestRelations(gameObject->GetEntry());
else
if (WorldObject const* worldObjectQuestGiver = dynamic_cast<WorldObject const*>(questGiver))
if (!IsInMap(worldObjectQuestGiver))
return nullptr;
if (questGiver && !questGiver->hasQuest(nextQuestID))
return nullptr;
break;
}
default:
return nullptr;
}
// for unit and go state
if (nextQuestID)
if (quests.HasQuest(nextQuestID))
return sObjectMgr->GetQuestTemplate(nextQuestID);
return nullptr;
}
bool Player::CanSeeStartQuest(Quest const* quest) const
@@ -13914,7 +13899,7 @@ bool Player::CanCompleteQuest(uint32 quest_id)
return false; // not allow re-complete quest
// auto complete quest
if (qInfo->IsAutoComplete() && CanTakeQuest(qInfo, false))
if (qInfo->IsTurnIn() && CanTakeQuest(qInfo, false))
return true;
QuestStatusMap::iterator itr = m_QuestStatus.find(quest_id);
@@ -13934,7 +13919,7 @@ bool Player::CanCompleteQuest(uint32 quest_id)
}
}
if (qInfo->HasSpecialFlag(QUEST_SPECIAL_FLAGS_KILL | QUEST_SPECIAL_FLAGS_CAST | QUEST_SPECIAL_FLAGS_SPEAKTO))
if (qInfo->HasSpecialFlag(QuestSpecialFlags(QUEST_SPECIAL_FLAGS_KILL | QUEST_SPECIAL_FLAGS_CAST | QUEST_SPECIAL_FLAGS_SPEAKTO)))
{
for (uint8 i = 0; i < QUEST_OBJECTIVES_COUNT; i++)
{
@@ -13950,7 +13935,7 @@ bool Player::CanCompleteQuest(uint32 quest_id)
if (qInfo->GetPlayersSlain() != 0 && q_status.PlayerCount < qInfo->GetPlayersSlain())
return false;
if (qInfo->HasSpecialFlag(QUEST_SPECIAL_FLAGS_EXPLORATION_OR_EVENT) && !q_status.Explored)
if ((qInfo->HasFlag(QUEST_FLAGS_COMPLETION_EVENT) || qInfo->HasFlag(QUEST_FLAGS_COMPLETION_AREA_TRIGGER)) && !q_status.Explored)
return false;
if (qInfo->HasSpecialFlag(QUEST_SPECIAL_FLAGS_TIMED) && q_status.Timer == 0)
@@ -13994,7 +13979,7 @@ bool Player::CanCompleteRepeatableQuest(Quest const* quest)
bool Player::CanRewardQuest(Quest const* quest, bool msg)
{
// not auto complete quest and not completed quest (only cheating case, then ignore without message)
if (!quest->IsDFQuest() && !quest->IsAutoComplete() && GetQuestStatus(quest->GetQuestId()) != QUEST_STATUS_COMPLETE)
if (!quest->IsDFQuest() && !quest->IsTurnIn() && GetQuestStatus(quest->GetQuestId()) != QUEST_STATUS_COMPLETE)
return false;
// daily quest can't be rewarded (25 daily quest already completed)
@@ -14156,7 +14141,7 @@ void Player::AddQuest(Quest const* quest, Object* questGiver)
questStatusData.ItemCount[i] = 0;
}
if (quest->HasSpecialFlag(QUEST_SPECIAL_FLAGS_KILL | QUEST_SPECIAL_FLAGS_CAST | QUEST_SPECIAL_FLAGS_SPEAKTO))
if (quest->HasSpecialFlag(QuestSpecialFlags(QUEST_SPECIAL_FLAGS_KILL | QUEST_SPECIAL_FLAGS_CAST | QUEST_SPECIAL_FLAGS_SPEAKTO)))
{
for (uint8 i = 0; i < QUEST_OBJECTIVES_COUNT; ++i)
questStatusData.CreatureOrGOCount[i] = 0;
@@ -14198,6 +14183,17 @@ void Player::AddQuest(Quest const* quest, Object* questGiver)
UpdatePvPState();
}
if (quest->GetSrcSpell() > 0)
{
SpellInfo const* spellInfo = sSpellMgr->GetSpellInfo(quest->GetSrcSpell());
Unit* caster = this;
if (questGiver && questGiver->isType(TYPEMASK_UNIT) && !quest->HasFlag(QUEST_FLAGS_PLAYER_CAST_ACCEPT) && !spellInfo->HasTargetType(TARGET_UNIT_CASTER) && !spellInfo->HasTargetType(TARGET_DEST_CASTER_SUMMON))
if (Unit* unit = questGiver->ToUnit())
caster = unit;
caster->CastSpell(this, spellInfo->Id, CastSpellExtraArgs(TRIGGERED_FULL_MASK));
}
SetQuestSlot(log_slot, quest_id, qtime);
m_QuestStatusSave[quest_id] = QUEST_DEFAULT_SAVE_TYPE;
@@ -14235,7 +14231,7 @@ void Player::CompleteQuest(uint32 quest_id)
SetQuestSlotState(log_slot, QUEST_STATE_COMPLETE);
if (Quest const* qInfo = sObjectMgr->GetQuestTemplate(quest_id))
if (qInfo->HasFlag(QUEST_FLAGS_TRACKING))
if (qInfo->HasFlag(QUEST_FLAGS_TRACKING_EVENT))
RewardQuest(qInfo, 0, this, false);
}
@@ -14273,14 +14269,16 @@ void Player::RewardQuest(Quest const* quest, uint32 reward, Object* questGiver,
for (uint8 i = 0; i < QUEST_ITEM_OBJECTIVES_COUNT; ++i)
{
if (ItemTemplate const* itemTemplate = sObjectMgr->GetItemTemplate(quest->RequiredItemId[i]))
if (quest->RequiredItemId[i])
{
if (quest->RequiredItemCount[i] > 0 && itemTemplate->GetBonding() == BIND_QUEST && !quest->IsRepeatable() && !HasQuestForItem(quest->RequiredItemId[i], quest_id, true))
DestroyItemCount(quest->RequiredItemId[i], 9999, true);
else
DestroyItemCount(quest->RequiredItemId[i], quest->RequiredItemCount[i], true);
uint32 amountToDestroy = quest->RequiredItemCount[i];
if (quest->HasFlag(QUEST_FLAGS_REMOVE_SURPLUS_ITEMS))
amountToDestroy = std::numeric_limits<uint32>::max();
DestroyItemCount(quest->RequiredItemId[i], amountToDestroy, true);
}
}
for (uint8 i = 0; i < QUEST_REQUIRED_CURRENCY_COUNT; ++i)
if (quest->RequiredCurrencyId[i])
ModifyCurrency(quest->RequiredCurrencyId[i], -int32(quest->RequiredCurrencyCount[i]));
@@ -14445,7 +14443,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) && !spellInfo->IsSelfCast() && !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_COMPLETE))
{
if (Creature* creature = GetMap()->GetCreature(questGiver->GetGUID()))
creature->CastSpell(this, quest->GetRewSpellCast(), true);
@@ -14456,7 +14454,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) && !spellInfo->IsSelfCast() && !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_COMPLETE))
{
if (Creature* creature = GetMap()->GetCreature(questGiver->GetGUID()))
creature->CastSpell(this, quest->GetRewSpell(), true);
@@ -14481,13 +14479,43 @@ void Player::RewardQuest(Quest const* quest, uint32 reward, Object* questGiver,
SendQuestUpdate(quest_id);
bool updateVisibility = false;
if (quest->HasFlag(QUEST_FLAGS_UPDATE_PHASESHIFT))
updateVisibility = PhasingHandler::OnConditionChange(this, false);
//Update QuestGivers
SendQuestGiverStatusMultiple();
//lets remove flag for delayed teleports
SetCanDelayTeleport(false);
if (questGiver && questGiver->isType(TYPEMASK_UNIT | TYPEMASK_GAMEOBJECT))
{
//For AutoSubmition was added plr case there as it almost same exclute AI script cases.
// Send next quest
if (Quest const* nextQuest = GetNextQuest(questGiver, quest))
{
// Only send the quest to the player if the conditions are met
if (CanTakeQuest(nextQuest, false))
{
if (nextQuest->IsAutoAccept() && CanAddQuest(nextQuest, true))
AddQuestAndCheckCompletion(nextQuest, questGiver);
PlayerTalkClass->SendQuestGiverQuestDetails(nextQuest, questGiver->GetGUID(), true, false);
}
}
PlayerTalkClass->ClearMenus();
if (Creature* creatureQGiver = questGiver->ToCreature())
creatureQGiver->AI()->QuestReward(this, quest, reward);
else if (GameObject* goQGiver = questGiver->ToGameObject())
goQGiver->AI()->QuestReward(this, quest, reward);
}
sScriptMgr->OnQuestStatusChange(this, quest_id);
if (updateVisibility)
UpdateObjectVisibility();
}
void Player::SetRewardedQuest(uint32 quest_id)
@@ -14545,6 +14573,21 @@ void Player::FailQuest(uint32 questId)
}
}
void Player::FailQuestsWithFlag(QuestFlags flag)
{
for (uint16 slot = 0; slot < MAX_QUEST_LOG_SIZE; ++slot)
{
uint32 questId = GetQuestSlotQuestId(slot);
if (!questId)
continue;
if (Quest const* quest = sObjectMgr->GetQuestTemplate(questId))
if (quest->HasFlag(flag))
FailQuest(questId);
}
}
void Player::AbandonQuest(uint32 questId)
{
if (Quest const* quest = sObjectMgr->GetQuestTemplate(questId))
@@ -15133,7 +15176,7 @@ void Player::SetQuestStatus(uint32 questId, QuestStatus status, bool update /*=
{
m_QuestStatus[questId].Status = status;
if (!quest->IsAutoComplete())
if (!quest->IsTurnIn())
m_QuestStatusSave[questId] = QUEST_DEFAULT_SAVE_TYPE;
}
@@ -15230,7 +15273,6 @@ void Player::SendQuestUpdate(uint32 questId)
}
UpdateVisibleGameobjectsOrSpellClicks();
PhasingHandler::OnConditionChange(this);
}
bool Player::SatisfyFirstLFGReward(uint32 dungeonId, uint8 maxRewCount) const
@@ -15294,7 +15336,7 @@ QuestGiverStatus Player::GetQuestDialogStatus(Object* questgiver)
else if (status == QUEST_STATUS_INCOMPLETE)
result2 = DIALOG_STATUS_INCOMPLETE;
if (quest->IsAutoComplete() && CanTakeQuest(quest, false) && quest->IsRepeatable() && !quest->IsDailyOrWeekly())
if (quest->IsTurnIn() && CanTakeQuest(quest, false) && quest->IsRepeatable() && !quest->IsDailyOrWeekly())
result2 = DIALOG_STATUS_REWARD_REP;
if (result2 > result)
@@ -15335,7 +15377,7 @@ QuestGiverStatus Player::GetQuestDialogStatus(Object* questgiver)
else
result2 = DIALOG_STATUS_LOW_LEVEL_AVAILABLE;
}
else if (quest->IsAutoComplete())
else if (quest->IsTurnIn())
{
if (isNotLowLevelQuest)
result2 = DIALOG_STATUS_REWARD_REP;
@@ -15804,7 +15846,7 @@ void Player::TalkedToCreature(uint32 entry, ObjectGuid guid)
if (q_status.Status == QUEST_STATUS_INCOMPLETE)
{
if (qInfo->HasSpecialFlag(QUEST_SPECIAL_FLAGS_KILL | QUEST_SPECIAL_FLAGS_CAST | QUEST_SPECIAL_FLAGS_SPEAKTO))
if (qInfo->HasSpecialFlag(QuestSpecialFlags(QUEST_SPECIAL_FLAGS_KILL | QUEST_SPECIAL_FLAGS_CAST | QUEST_SPECIAL_FLAGS_SPEAKTO)))
{
for (uint8 j = 0; j < QUEST_OBJECTIVES_COUNT; ++j)
{
@@ -16022,9 +16064,10 @@ void Player::SendQuestReward(Quest const* quest, Creature const* questGiver, uin
if (questGiver)
{
if (questGiver->IsGossip() && quest->HasFlag(QUEST_FLAGS_LAUNCH_GOSSIP_COMPLETE))
packet.LaunchGossip = true;
else if (quest->GetNextQuestInChain() && !quest->HasFlag(QUEST_FLAGS_AUTOCOMPLETE))
if (questGiver->IsGossip())
packet.LaunchGossip = quest->HasFlag(QUEST_FLAGS_LAUNCH_GOSSIP_COMPLETE);
else if (quest->GetNextQuestInChain() && !quest->HasFlag(QUEST_FLAGS_AUTO_COMPLETE))
if (Quest const* rewardQuest = sObjectMgr->GetQuestTemplate(quest->GetNextQuestInChain()))
packet.UseQuestReward = CanTakeQuest(rewardQuest, false);
}
+2 -1
View File
@@ -1331,7 +1331,7 @@ class TC_GAME_API Player : public Unit, public GridObject<Player>
void PrepareQuestMenu(ObjectGuid guid);
void SendPreparedQuest(ObjectGuid guid);
bool IsActiveQuest(uint32 quest_id) const;
Quest const* GetNextQuest(ObjectGuid guid, Quest const* quest) const;
Quest const* GetNextQuest(Object const* questGiver, Quest const* quest) const;
bool CanSeeStartQuest(Quest const* quest) const;
bool CanTakeQuest(Quest const* quest, bool msg) const;
bool CanAddQuest(Quest const* quest, bool msg) const;
@@ -1347,6 +1347,7 @@ class TC_GAME_API Player : public Unit, public GridObject<Player>
void RewardQuest(Quest const* quest, uint32 reward, Object* questGiver, bool announce = true);
void SetRewardedQuest(uint32 quest_id);
void FailQuest(uint32 quest_id);
void FailQuestsWithFlag(QuestFlags flag);
bool SatisfyQuestSkill(Quest const* qInfo, bool msg) const;
bool SatisfyQuestLevel(Quest const* qInfo, bool msg) const;
bool SatisfyQuestLog(bool msg) const;
+12 -47
View File
@@ -4119,8 +4119,8 @@ void ObjectMgr::LoadQuests()
// additional quest integrity checks (GO, creature_template and item_template must be loaded already)
if (qinfo->GetQuestMethod() >= 3)
TC_LOG_ERROR("sql.sql", "Quest %u has `Method` = %u, expected values are 0, 1 or 2.", qinfo->GetQuestId(), qinfo->GetQuestMethod());
if (qinfo->GetQuestType() >= MAX_DB_ALLOWED_QUEST_TYPES)
TC_LOG_ERROR("sql.sql", "Quest %u has `Method` = %u, expected values are 0, 1 or 2.", qinfo->GetQuestId(), qinfo->GetQuestType());
if (qinfo->_specialFlags & ~QUEST_SPECIAL_FLAGS_DB_ALLOWED)
{
@@ -4162,7 +4162,7 @@ void ObjectMgr::LoadQuests()
}
}
if (qinfo->_flags & QUEST_FLAGS_TRACKING)
if (qinfo->_flags & QUEST_FLAGS_TRACKING_EVENT)
{
// at auto-reward can be rewarded only RewardChoiceItemId[0]
for (uint32 j = 1; j < QUEST_REWARD_CHOICES_COUNT; ++j)
@@ -4445,7 +4445,7 @@ void ObjectMgr::LoadQuests()
{
// In fact SpeakTo and Kill are quite same: either you can speak to mob:SpeakTo or you can't:Kill/Cast
qinfo->SetSpecialFlag(QUEST_SPECIAL_FLAGS_KILL | QUEST_SPECIAL_FLAGS_CAST | QUEST_SPECIAL_FLAGS_SPEAKTO);
qinfo->SetSpecialFlag(QuestSpecialFlags(QUEST_SPECIAL_FLAGS_KILL | QUEST_SPECIAL_FLAGS_CAST | QUEST_SPECIAL_FLAGS_SPEAKTO));
if (!qinfo->RequiredNpcOrGoCount[j])
{
@@ -4784,7 +4784,8 @@ void ObjectMgr::LoadQuests()
qinfo->SetSpecialFlag(QUEST_SPECIAL_FLAGS_PLAYER_KILL);
// Special flag to determine if quest is completed from the start, used to determine if we can fail timed quest if it is completed
if (!qinfo->HasSpecialFlag(QUEST_SPECIAL_FLAGS_KILL | QUEST_SPECIAL_FLAGS_CAST | QUEST_SPECIAL_FLAGS_SPEAKTO | QUEST_SPECIAL_FLAGS_EXPLORATION_OR_EVENT))
if (!qinfo->HasSpecialFlag(QuestSpecialFlags(QUEST_SPECIAL_FLAGS_KILL | QUEST_SPECIAL_FLAGS_CAST | QUEST_SPECIAL_FLAGS_SPEAKTO)) &&
!qinfo->HasFlag(QUEST_FLAGS_COMPLETION_AREA_TRIGGER | QUEST_FLAGS_COMPLETION_EVENT))
{
bool addFlag = true;
if (qinfo->HasSpecialFlag(QUEST_SPECIAL_FLAGS_DELIVER))
@@ -4836,35 +4837,7 @@ void ObjectMgr::LoadQuests()
}
}
// check QUEST_SPECIAL_FLAGS_EXPLORATION_OR_EVENT for spell with SPELL_EFFECT_QUEST_COMPLETE
for (uint32 i = 0; i < sSpellMgr->GetSpellInfoStoreSize(); ++i)
{
SpellInfo const* spellInfo = sSpellMgr->GetSpellInfo(i);
if (!spellInfo)
continue;
for (uint8 j = 0; j < MAX_SPELL_EFFECTS; ++j)
{
if (spellInfo->Effects[j].Effect != SPELL_EFFECT_QUEST_COMPLETE)
continue;
uint32 quest_id = spellInfo->Effects[j].MiscValue;
Quest const* quest = GetQuestTemplate(quest_id);
// some quest referenced in spells not exist (outdated spells)
if (!quest)
continue;
if (!quest->HasSpecialFlag(QUEST_SPECIAL_FLAGS_EXPLORATION_OR_EVENT))
{
TC_LOG_ERROR("sql.sql", "Spell (id: %u) have SPELL_EFFECT_QUEST_COMPLETE for quest %u, but quest not have flag QUEST_SPECIAL_FLAGS_EXPLORATION_OR_EVENT. Quest flags must be fixed, quest modified to enable objective.", spellInfo->Id, quest_id);
// this will prevent quest completing without objective
const_cast<Quest*>(quest)->SetSpecialFlag(QUEST_SPECIAL_FLAGS_EXPLORATION_OR_EVENT);
}
}
}
// don't check spells with SPELL_EFFECT_QUEST_COMPLETE, a lot of invalid db2 data
TC_LOG_INFO("server.loading", ">> Loaded %lu quests definitions in %u ms", (unsigned long)_questTemplates.size(), GetMSTimeDiffToNow(oldMSTime));
}
@@ -5033,15 +5006,11 @@ void ObjectMgr::LoadScripts(ScriptsType type)
continue;
}
if (!quest->HasSpecialFlag(QUEST_SPECIAL_FLAGS_EXPLORATION_OR_EVENT))
if (!quest->HasFlag(QUEST_FLAGS_COMPLETION_EVENT) && !quest->HasFlag(QUEST_FLAGS_COMPLETION_AREA_TRIGGER))
{
TC_LOG_ERROR("sql.sql", "Table `%s` has quest (ID: %u) in SCRIPT_COMMAND_QUEST_EXPLORED in `datalong` for script id %u, but quest not have flag QUEST_SPECIAL_FLAGS_EXPLORATION_OR_EVENT in quest flags. Script command or quest flags wrong. Quest modified to require objective.",
tableName.c_str(), tmp.QuestExplored.QuestID, tmp.id);
// this will prevent quest completing without objective
const_cast<Quest*>(quest)->SetSpecialFlag(QUEST_SPECIAL_FLAGS_EXPLORATION_OR_EVENT);
// continue; - quest objective requirement set and command can be allowed
continue;
}
if (float(tmp.QuestExplored.Distance) > DEFAULT_VISIBILITY_DISTANCE)
@@ -6007,14 +5976,10 @@ void ObjectMgr::LoadQuestAreaTriggers()
continue;
}
if (!quest->HasSpecialFlag(QUEST_SPECIAL_FLAGS_EXPLORATION_OR_EVENT))
if (!quest->HasFlag(QUEST_FLAGS_COMPLETION_AREA_TRIGGER))
{
TC_LOG_ERROR("sql.sql", "Table `areatrigger_involvedrelation` has record (id: %u) for not quest %u, but quest not have flag QUEST_SPECIAL_FLAGS_EXPLORATION_OR_EVENT. Trigger or quest flags must be fixed, quest modified to require objective.", trigger_ID, quest_ID);
// this will prevent quest completing without objective
const_cast<Quest*>(quest)->SetSpecialFlag(QUEST_SPECIAL_FLAGS_EXPLORATION_OR_EVENT);
// continue; - quest modified to required objective and trigger can be allowed.
TC_LOG_ERROR("sql.sql", "Table `areatrigger_involvedrelation` has record (id: %u) for not quest %u, but quest not have flag QUEST_FLAGS_COMPLETION_AREA_TRIGGER. Trigger or quest flags must be fixed, quest modified to require objective.", trigger_ID, quest_ID);
continue;
}
_questAreaTriggerStore[trigger_ID] = quest_ID;
+36 -75
View File
@@ -196,7 +196,7 @@ void WorldSession::HandleQuestgiverAcceptQuestOpcode(WorldPackets::Quest::QuestG
{
_player->AddQuestAndCheckCompletion(quest, object);
if (quest->HasFlag(QUEST_FLAGS_PARTY_ACCEPT))
if (quest->IsPushedToPartyOnAccept())
{
if (Group* group = _player->GetGroup())
{
@@ -222,6 +222,21 @@ void WorldSession::HandleQuestgiverAcceptQuestOpcode(WorldPackets::Quest::QuestG
_player->PlayerTalkClass->SendCloseGossip();
if (quest->HasFlag(QUEST_FLAGS_LAUNCH_GOSSIP_ACCEPT))
{
auto launchGossip = [&](WorldObject* worldObject)
{
_player->PlayerTalkClass->ClearMenus();
_player->PrepareGossipMenu(worldObject, _player->GetDefaultGossipMenuForSource(worldObject), true);
_player->SendPreparedGossip(worldObject);
};
if (Creature* creature = object->ToCreature())
launchGossip(creature);
else if (GameObject* go = object->ToGameObject())
launchGossip(go);
}
if (quest->GetSrcSpell() > 0)
_player->CastSpell(_player, quest->GetSrcSpell(), true);
@@ -258,7 +273,7 @@ void WorldSession::HandleQuestgiverQueryQuestOpcode(WorldPackets::Quest::QuestGi
if (quest->IsAutoAccept() && _player->CanAddQuest(quest, true))
_player->AddQuestAndCheckCompletion(quest, object);
if (quest->IsAutoComplete())
if (quest->IsTurnIn())
_player->PlayerTalkClass->SendQuestGiverRequestItems(quest, object->GetGUID(), _player->CanCompleteQuest(quest->GetQuestId()), true);
else
{
@@ -295,7 +310,7 @@ void WorldSession::HandleQuestgiverChooseRewardOpcode(WorldPackets::Quest::Quest
Object* object = _player;
if (!quest->HasFlag(QUEST_FLAGS_AUTOCOMPLETE))
if (!quest->HasFlag(QUEST_FLAGS_AUTO_COMPLETE))
{
object = ObjectAccessor::GetObjectByTypeMask(*_player, packet.QuestGiverGUID, TYPEMASK_UNIT|TYPEMASK_GAMEOBJECT);
if (!object || !object->hasInvolvedQuest(packet.QuestID))
@@ -307,70 +322,17 @@ void WorldSession::HandleQuestgiverChooseRewardOpcode(WorldPackets::Quest::Quest
}
if ((!_player->CanSeeStartQuest(quest) && _player->GetQuestStatus(packet.QuestID) == QUEST_STATUS_NONE) ||
(_player->GetQuestStatus(packet.QuestID) != QUEST_STATUS_COMPLETE && !quest->IsAutoComplete()))
(_player->GetQuestStatus(packet.QuestID) != QUEST_STATUS_COMPLETE && !quest->IsTurnIn()))
{
TC_LOG_ERROR("entities.player.cheat", "Error in QUEST_STATUS_COMPLETE: player %s (guid %u) tried to complete quest %u, but is not allowed to do so (possible packet-hacking or high latency)",
_player->GetName().c_str(), _player->GetGUID().GetCounter(), packet.QuestID);
return;
}
if (_player->CanRewardQuest(quest, true))
{
if (_player->CanRewardQuest(quest, packet.ItemChoiceID, true))
{
_player->RewardQuest(quest, packet.ItemChoiceID, object);
switch (object->GetTypeId())
{
case TYPEID_UNIT:
case TYPEID_PLAYER:
{
// Send next quest
if (Quest const* nextQuest = _player->GetNextQuest(packet.QuestGiverGUID, quest))
{
// Only send the quest to the player if the conditions are met
if (_player->CanTakeQuest(nextQuest, false))
{
if (nextQuest->IsAutoAccept() && _player->CanAddQuest(nextQuest, true))
_player->AddQuestAndCheckCompletion(nextQuest, object);
// Previous quest has been rewarded via popup. The player may now accept the follow quest from himself.
if (object->IsPlayer() && quest->HasFlag(QUEST_FLAGS_AUTOCOMPLETE) && !nextQuest->IsAutoAccept())
_player->SetPopupQuestId(nextQuest->GetQuestId());
else
_player->SetPopupQuestId(0);
_player->PlayerTalkClass->SendQuestGiverQuestDetails(nextQuest, packet.QuestGiverGUID, true, false);
}
}
_player->PlayerTalkClass->ClearMenus();
if (Creature* creatureQGiver = object->ToCreature())
creatureQGiver->AI()->QuestReward(_player, quest, packet.ItemChoiceID);
break;
}
case TYPEID_GAMEOBJECT:
{
// Send next quest
if (Quest const* nextQuest = _player->GetNextQuest(packet.QuestGiverGUID, quest))
{
// Only send the quest to the player if the conditions are met
if (_player->CanTakeQuest(nextQuest, false))
{
if (nextQuest->IsAutoAccept() && _player->CanAddQuest(nextQuest, true))
_player->AddQuestAndCheckCompletion(nextQuest, object);
_player->SetPopupQuestId(0);
_player->PlayerTalkClass->SendQuestGiverQuestDetails(nextQuest, packet.QuestGiverGUID, true, false);
}
}
_player->PlayerTalkClass->ClearMenus();
if (GameObject* questGiver = object->ToGameObject())
questGiver->AI()->QuestReward(_player, quest, packet.ItemChoiceID);
break;
}
default:
break;
}
}
else
_player->PlayerTalkClass->SendQuestGiverOfferReward(quest, packet.QuestGiverGUID, true);
@@ -473,9 +435,12 @@ void WorldSession::HandleQuestConfirmAccept(WorldPackets::Quest::QuestConfirmAcc
{
TC_LOG_DEBUG("network", "WORLD: Received CMSG_QUEST_CONFIRM_ACCEPT QuestID = %u", packet.QuestID);
if (Quest const* quest = sObjectMgr->GetQuestTemplate(packet.QuestID))
{
if (!quest->HasFlag(QUEST_FLAGS_PARTY_ACCEPT))
if (_player->GetSharedQuestID() != uint32(packet.QuestID))
return;
_player->ClearQuestSharingInfo();
Quest const* quest = sObjectMgr->GetQuestTemplate(packet.QuestID);
if (!quest)
return;
Player* originalPlayer = ObjectAccessor::FindPlayer(_player->GetPlayerSharingQuest());
@@ -491,31 +456,27 @@ void WorldSession::HandleQuestConfirmAccept(WorldPackets::Quest::QuestConfirmAcc
if (!_player->CanTakeQuest(quest, true))
return;
if (_player->CanAddQuest(quest, true))
{
_player->AddQuestAndCheckCompletion(quest, nullptr); // nullptr, this prevent DB script from duplicate running
if (!_player->CanAddQuest(quest, true))
return;
_player->AddQuestAndCheckCompletion(quest, nullptr); // NULL, this prevent DB script from duplicate running
if (quest->GetSrcSpell() > 0)
_player->CastSpell(_player, quest->GetSrcSpell(), true);
}
}
_player->ClearQuestSharingInfo();
}
void WorldSession::HandleQuestgiverCompleteQuest(WorldPackets::Quest::QuestGiverCompleteQuest& packet)
{
bool autoCompleteMode = packet.FromScript; // 0 - standart complete quest mode with npc, 1 - auto-complete mode
TC_LOG_DEBUG("network", "WORLD: Received CMSG_QUESTGIVER_COMPLETE_QUEST npc = %s, questId = %u self-complete: %u", packet.QuestGiverGUID.ToString().c_str(), packet.QuestID, packet.FromScript ? 1 : 0);
Quest const* quest = sObjectMgr->GetQuestTemplate(packet.QuestID);
if (!quest)
return;
if (packet.FromScript && !quest->HasFlag(QUEST_FLAGS_AUTOCOMPLETE))
return;
Object* object = nullptr;
if (packet.FromScript)
if (autoCompleteMode)
object = _player;
else
object = ObjectAccessor::GetObjectByTypeMask(*_player, packet.QuestGiverGUID, TYPEMASK_UNIT | TYPEMASK_GAMEOBJECT);
@@ -523,7 +484,7 @@ void WorldSession::HandleQuestgiverCompleteQuest(WorldPackets::Quest::QuestGiver
if (!object)
return;
if (!packet.FromScript)
if (!quest->HasFlag(QUEST_FLAGS_AUTO_COMPLETE))
{
if (!object->hasInvolvedQuest(packet.QuestID))
return;
@@ -641,7 +602,7 @@ void WorldSession::HandlePushQuestToParty(WorldPacket& recvPacket)
if (quest->IsAutoAccept() && receiver->CanAddQuest(quest, true) && receiver->CanTakeQuest(quest, true))
receiver->AddQuestAndCheckCompletion(quest, sender);
if (quest->IsAutoComplete() && quest->IsRepeatable() && !quest->IsDailyOrWeekly())
if (quest->IsTurnIn() && quest->IsRepeatable() && !quest->IsDailyOrWeekly())
receiver->PlayerTalkClass->SendQuestGiverRequestItems(quest, sender->GetGUID(), receiver->CanCompleteRepeatableQuest(quest), true);
else
{
+25 -13
View File
@@ -3220,20 +3220,32 @@ enum HolidayIds : uint32
HOLIDAY_ANNIVERSARY_8_YEARS = 484
};
// values based at QuestInfo.dbc
enum QuestTypes
enum QuestType
{
QUEST_TYPE_ELITE = 1,
QUEST_TYPE_LIFE = 21,
QUEST_TYPE_PVP = 41,
QUEST_TYPE_RAID = 62,
QUEST_TYPE_DUNGEON = 81,
QUEST_TYPE_WORLD_EVENT = 82,
QUEST_TYPE_LEGENDARY = 83,
QUEST_TYPE_ESCORT = 84,
QUEST_TYPE_HEROIC = 85,
QUEST_TYPE_RAID_10 = 88,
QUEST_TYPE_RAID_25 = 89
QUEST_TYPE_TURNIN = 0,
QUEST_TYPE_WITH_MAX_LEVEL = 1,
QUEST_TYPE_NORMAL = 2,
MAX_DB_ALLOWED_QUEST_TYPES = 3,
// values used in quest menu packets
QUEST_TYPE_IN_PROGRESS = 4,
QUEST_TYPE_TASK_IN_PROGRESS = 5
};
// values based at QuestInfo.dbc
enum QuestInfo
{
QUEST_INFO_GROUP = 1,
QUEST_INFO_CLASS = 21,
QUEST_INFO_PVP = 41,
QUEST_INFO_RAID = 62,
QUEST_INFO_DUNGEON = 81,
QUEST_INFO_WORLD_EVENT = 82,
QUEST_INFO_LEGENDARY = 83,
QUEST_INFO_ESCORT = 84,
QUEST_INFO_HEROIC = 85,
QUEST_INFO_RAID_10 = 88,
QUEST_INFO_RAID_25 = 89
};
// values based at QuestSort.dbc
+3 -2
View File
@@ -285,7 +285,7 @@ void PhasingHandler::OnAreaChange(WorldObject* object)
UpdateVisibilityIfNeeded(object, true, changed);
}
void PhasingHandler::OnConditionChange(WorldObject* object)
bool PhasingHandler::OnConditionChange(WorldObject* object, bool updateVisibility /*= true*/)
{
PhaseShift& phaseShift = object->GetPhaseShift();
PhaseShift& suppressedPhaseShift = object->GetSuppressedPhaseShift();
@@ -393,7 +393,8 @@ void PhasingHandler::OnConditionChange(WorldObject* object)
unit->RemoveNotOwnSingleTargetAuras(true);
}
UpdateVisibilityIfNeeded(object, true, changed);
UpdateVisibilityIfNeeded(object, updateVisibility, changed);
return changed;
}
void PhasingHandler::SendToPlayer(Player const* player, PhaseShift const& phaseShift)
+1 -1
View File
@@ -51,7 +51,7 @@ public:
static void OnMapChange(WorldObject* object);
static void OnAreaChange(WorldObject* object);
static void OnConditionChange(WorldObject* object);
static bool OnConditionChange(WorldObject* object, bool updateVisibility = true);
static void SendToPlayer(Player const* player, PhaseShift const& phaseShift);
static void SendToPlayer(Player const* player);
+14 -14
View File
@@ -29,11 +29,11 @@
Quest::Quest(Field* questRecord)
{
_id = questRecord[0].GetUInt32();
_method = questRecord[1].GetUInt8();
_type = questRecord[1].GetUInt8();
_level = questRecord[2].GetInt16();
_minLevel = questRecord[3].GetInt16();
_zoneOrSort = questRecord[4].GetInt16();
_type = questRecord[5].GetUInt16();
_questInfoId = questRecord[5].GetUInt16();
_suggestedPlayers = questRecord[6].GetUInt8();
_requiredFactionId1 = questRecord[7].GetUInt16();
_requiredFactionId2 = questRecord[8].GetUInt16();
@@ -339,7 +339,7 @@ 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_FROM_XP))
if (HasFlag(QUEST_FLAGS_NO_MONEY_FOR_XP))
return 0;
// Else, return the rewarded copper sum modified by the rate
@@ -351,26 +351,26 @@ bool Quest::IsAutoAccept() const
return !sWorld->getBoolConfig(CONFIG_QUEST_IGNORE_AUTO_ACCEPT) && HasFlag(QUEST_FLAGS_AUTO_ACCEPT);
}
bool Quest::IsAutoComplete() const
bool Quest::IsTurnIn() const
{
return !sWorld->getBoolConfig(CONFIG_QUEST_IGNORE_AUTO_COMPLETE) && _method == 0;
return !sWorld->getBoolConfig(CONFIG_QUEST_IGNORE_AUTO_COMPLETE) && _type == QUEST_TYPE_TURNIN;
}
bool Quest::IsRaidQuest(Difficulty difficulty) const
{
switch (_type)
switch (_questInfoId)
{
case QUEST_TYPE_RAID:
case QUEST_INFO_RAID:
return true;
case QUEST_TYPE_RAID_10:
return !(difficulty & RAID_DIFFICULTY_MASK_25MAN);
case QUEST_TYPE_RAID_25:
return difficulty & RAID_DIFFICULTY_MASK_25MAN;
case QUEST_INFO_RAID_10:
return difficulty == RAID_DIFFICULTY_10MAN_NORMAL || difficulty == RAID_DIFFICULTY_10MAN_HEROIC;
case QUEST_INFO_RAID_25:
return difficulty == RAID_DIFFICULTY_25MAN_NORMAL || difficulty == RAID_DIFFICULTY_25MAN_HEROIC;
default:
break;
}
if ((_flags & QUEST_FLAGS_RAID) != 0)
if ((_flags & QUEST_FLAGS_RAID_GROUP_OK) != 0)
return true;
return false;
@@ -470,11 +470,11 @@ WorldPacket Quest::BuildQueryData(LocaleConstant loc) const
ObjectMgr::GetLocaleString(localeData->ObjectiveText[i], loc, locQuestObjectiveText[i]);
}
response.Info.QuestType = GetQuestMethod();
response.Info.QuestType = GetQuestType();
response.Info.QuestLevel = GetQuestLevel();
response.Info.QuestMinLevel = GetMinLevel();
response.Info.QuestSortID = GetZoneOrSort();
response.Info.QuestInfoID = GetType();
response.Info.QuestInfoID = GetQuestInfoId();
response.Info.SuggestedGroupNum = GetSuggestedPlayers();
+32 -28
View File
@@ -131,40 +131,40 @@ enum QuestGiverStatus
};
enum QuestFlags
enum QuestFlags : uint32
{
QUEST_FLAGS_NONE = 0x00000000,
QUEST_FLAGS_STAY_ALIVE = 0x00000001, // Not used currently
QUEST_FLAGS_PARTY_ACCEPT = 0x00000002, // Not used currently. If player in party, all players that can accept this quest will receive confirmation box to accept quest CMSG_QUEST_CONFIRM_ACCEPT/SMSG_QUEST_CONFIRM_ACCEPT
QUEST_FLAGS_EXPLORATION = 0x00000004, // Not used currently
QUEST_FLAGS_COMPLETION_NO_DEATH = 0x00000001,
QUEST_FLAGS_COMPLETION_EVENT = 0x00000002,
QUEST_FLAGS_COMPLETION_AREA_TRIGGER = 0x00000004,
QUEST_FLAGS_SHARABLE = 0x00000008, // Can be shared: Player::CanShareQuest()
QUEST_FLAGS_HAS_CONDITION = 0x00000010, // Not used currently
QUEST_FLAGS_HIDE_REWARD_POI = 0x00000020, // Not used currently: Unsure of content
QUEST_FLAGS_RAID = 0x00000040, // Can be completed while in raid
QUEST_FLAGS_TBC = 0x00000080, // Not used currently: Available if TBC expansion enabled only
QUEST_FLAGS_NO_MONEY_FROM_XP = 0x00000100, // Not used currently: Experience is not converted to gold at max level
QUEST_FLAGS_HIDDEN_REWARDS = 0x00000200, // Items and money rewarded only sent in SMSG_QUESTGIVER_OFFER_REWARD (not in SMSG_QUESTGIVER_QUEST_DETAILS or in client quest log(SMSG_QUEST_QUERY_RESPONSE))
QUEST_FLAGS_TRACKING = 0x00000400, // These quests are automatically rewarded on quest complete and they will never appear in quest log client side.
QUEST_FLAGS_HIDE_REWARD_POI = 0x00000020, // Hides questgiver turn-in minimap icon
QUEST_FLAGS_RAID_GROUP_OK = 0x00000040, // Can be completed while in raid
QUEST_FLAGS_EXPANSION_ONLY = 0x00000080, // Not used currently
QUEST_FLAGS_NO_MONEY_FOR_XP = 0x00000100, // Experience is not converted to gold at max level
QUEST_FLAGS_HIDE_REWARD = 0x00000200, // Items and money rewarded only sent in SMSG_QUESTGIVER_OFFER_REWARD (not in SMSG_QUEST_GIVER_QUEST_DETAILS or in client quest log(SMSG_QUEST_QUERY_RESPONSE))
QUEST_FLAGS_TRACKING_EVENT = 0x00000400, // These quests are automatically rewarded on quest complete and they will never appear in quest log client side.
QUEST_FLAGS_DEPRECATE_REPUTATION = 0x00000800, // Not used currently
QUEST_FLAGS_DAILY = 0x00001000, // Used to know quest is Daily one
QUEST_FLAGS_FLAGS_PVP = 0x00002000, // Having this quest in log forces PvP flag
QUEST_FLAGS_UNAVAILABLE = 0x00004000, // Used on quests that are not generically available
QUEST_FLAGS_DEPRECATED = 0x00004000, // Used on quests that are not generally available
QUEST_FLAGS_WEEKLY = 0x00008000,
QUEST_FLAGS_AUTOCOMPLETE = 0x00010000, // Quests with this flag player submit automatically by special button in player gui
QUEST_FLAGS_AUTO_COMPLETE = 0x00010000, // Quests with this flag player submit automatically by special button in player gui
QUEST_FLAGS_DISPLAY_ITEM_IN_TRACKER = 0x00020000, // Displays usable item in quest tracker
QUEST_FLAGS_OBJ_TEXT = 0x00040000, // use Objective text as Complete text
QUEST_FLAGS_DISABLE_COMPLETION_TEXT = 0x00040000, // use Objective text as Complete text
QUEST_FLAGS_AUTO_ACCEPT = 0x00080000, // The client recognizes this flag as auto-accept.
QUEST_FLAGS_PLAYER_CAST_ON_ACCEPT = 0x00100000,
QUEST_FLAGS_PLAYER_CAST_ON_COMPLETE = 0x00200000,
QUEST_FLAGS_UPDATE_PHASE_SHIFT = 0x00400000,
QUEST_FLAGS_PLAYER_CAST_ACCEPT = 0x00100000,
QUEST_FLAGS_PLAYER_CAST_COMPLETE = 0x00200000,
QUEST_FLAGS_UPDATE_PHASESHIFT = 0x00400000,
QUEST_FLAGS_SOR_WHITELIST = 0x00800000,
QUEST_FLAGS_LAUNCH_GOSSIP_COMPLETE = 0x01000000,
QUEST_FLAGS_REMOVE_EXTRA_GET_ITEMS = 0x02000000,
QUEST_FLAGS_HIDE_UNTIL_DISCOVERED = 0x04000000,
QUEST_FLAGS_REMOVE_SURPLUS_ITEMS = 0x02000000, // Remove all items from inventory that have the same id as the objective, not just the amount required by quest
QUEST_FLAGS_WELL_KNOWN = 0x04000000,
QUEST_FLAGS_PORTRAIT_IN_QUEST_LOG = 0x08000000,
QUEST_FLAGS_SHOW_ITEM_WHEN_COMPLETED = 0x10000000,
QUEST_FLAGS_LAUNCH_GOSSIP_ACCEPT = 0x20000000,
QUEST_FLAGS_ITEMS_GLOW_WHEN_DONE = 0x40000000,
QUEST_FLAGS_ITEMS_GLOW_WHEN_COMPLETE = 0x40000000,
QUEST_FLAGS_FAIL_ON_LOGOUT = 0x80000000
};
@@ -173,14 +173,14 @@ enum QuestSpecialFlags
QUEST_SPECIAL_FLAGS_NONE = 0x000,
// Trinity flags for set SpecialFlags in DB if required but used only at server
QUEST_SPECIAL_FLAGS_REPEATABLE = 0x001, // Set by 1 in SpecialFlags from DB
QUEST_SPECIAL_FLAGS_EXPLORATION_OR_EVENT = 0x002, // Set by 2 in SpecialFlags from DB (if required area explore, spell SPELL_EFFECT_QUEST_COMPLETE casting, table `FECT_QUEST_COMPLETE casting, table `*_script` command SCRIPT_COMMAND_QUEST_EXPLORED use, set from script)
QUEST_SPECIAL_FLAGS_AUTO_PUSH_TO_PARTY = 0x002, // Set by 2 in SpecialFlags from DB will make quest be pushed to entire party when one member accepts it
QUEST_SPECIAL_FLAGS_AUTO_ACCEPT = 0x004, // Set by 4 in SpecialFlags in DB if the quest is to be auto-accepted.
QUEST_SPECIAL_FLAGS_DF_QUEST = 0x008, // Set by 8 in SpecialFlags in DB if the quest is used by Dungeon Finder.
QUEST_SPECIAL_FLAGS_MONTHLY = 0x010, // Set by 16 in SpecialFlags in DB if the quest is reset at the begining of the month
QUEST_SPECIAL_FLAGS_CAST = 0x020, // Set by 32 in SpecialFlags in DB if the quest requires RequiredOrNpcGo killcredit but NOT kill (a spell cast)
// room for more custom flags
QUEST_SPECIAL_FLAGS_DB_ALLOWED = QUEST_SPECIAL_FLAGS_REPEATABLE | QUEST_SPECIAL_FLAGS_EXPLORATION_OR_EVENT | QUEST_SPECIAL_FLAGS_AUTO_ACCEPT | QUEST_SPECIAL_FLAGS_DF_QUEST | QUEST_SPECIAL_FLAGS_MONTHLY | QUEST_SPECIAL_FLAGS_CAST,
QUEST_SPECIAL_FLAGS_DB_ALLOWED = QUEST_SPECIAL_FLAGS_REPEATABLE | QUEST_SPECIAL_FLAGS_AUTO_ACCEPT | QUEST_SPECIAL_FLAGS_DF_QUEST | QUEST_SPECIAL_FLAGS_MONTHLY | QUEST_SPECIAL_FLAGS_CAST,
QUEST_SPECIAL_FLAGS_DELIVER = 0x080, // Internal flag computed only
QUEST_SPECIAL_FLAGS_SPEAKTO = 0x100, // Internal flag computed only
@@ -242,20 +242,23 @@ class TC_GAME_API Quest
bool HasFlag(uint32 flag) const { return (_flags & flag) != 0; }
void SetFlag(uint32 flag) { _flags |= flag; }
bool HasSpecialFlag(uint32 flag) const { return (_specialFlags & flag) != 0; }
void SetSpecialFlag(uint32 flag) { _specialFlags |= flag; }
bool HasSpecialFlag(QuestSpecialFlags flag) const { return (_specialFlags & flag) != 0; }
void SetSpecialFlag(QuestSpecialFlags flag) { _specialFlags |= flag; }
// Possibly deprecated flag
bool IsUnavailable() const { return HasFlag(QUEST_FLAGS_DEPRECATED); }
// whether the quest is globally enabled (spawned by pool, game event active etc.)
static bool IsTakingQuestEnabled(uint32 questId);
// table data accessors:
uint32 GetQuestId() const { return _id; }
uint32 GetQuestMethod() const { return _method; }
uint32 GetQuestType() const { return _type; }
int32 GetZoneOrSort() const { return _zoneOrSort; }
uint32 GetMinLevel() const { return _minLevel; }
uint32 GetMaxLevel() const { return _maxLevel; }
int32 GetQuestLevel() const { return _level; }
uint32 GetType() const { return _type; }
uint32 GetQuestInfoId() const { return _questInfoId; }
uint32 GetAllowableClasses() const { return _allowableClasses; }
uint32 GetAllowableRaces() const { return _allowableRaces; }
uint32 GetRequiredSkill() const { return _requiredSkillId; }
@@ -314,7 +317,7 @@ class TC_GAME_API Quest
uint32 GetCompleteEmote() const { return _emoteOnComplete; }
bool IsRepeatable() const { return _specialFlags & QUEST_SPECIAL_FLAGS_REPEATABLE; }
bool IsAutoAccept() const;
bool IsAutoComplete() const;
bool IsTurnIn() const;
uint32 GetFlags() const { return _flags; }
uint32 GetSpecialFlags() const { return _specialFlags; }
uint32 GetMinimapTargetMark() const { return _minimapTargetMark; }
@@ -331,6 +334,7 @@ class TC_GAME_API Quest
bool IsRaidQuest(Difficulty difficulty) const;
bool IsAllowedInRaid(Difficulty difficulty) const;
bool IsDFQuest() const { return (_specialFlags & QUEST_SPECIAL_FLAGS_DF_QUEST) != 0; }
bool IsPushedToPartyOnAccept() const { return HasSpecialFlag(QUEST_SPECIAL_FLAGS_AUTO_PUSH_TO_PARTY); }
uint32 CalculateHonorGain(uint8 level) const;
bool CanIncreaseRewardedQuestCounters() const;
bool IsStartAtAreaTrigger() const { return _startsAtAreaTrigger; }
@@ -395,11 +399,11 @@ class TC_GAME_API Quest
// table data
uint32 _id = 0;
uint32 _method = 0;
uint32 _type = 0;
int32 _zoneOrSort = 0;
uint32 _minLevel = 0;
int32 _level = 0;
uint32 _type = 0;
uint32 _questInfoId = 0;
uint32 _requiredFactionId1 = 0;
int32 _requiredFactionValue1 = 0;
uint32 _requiredFactionId2 = 0;
@@ -316,7 +316,7 @@ WorldPacket const* WorldPackets::Quest::QueryQuestInfoResponse::Write()
_worldPacket << int32(Info.RewardNextQuest);
_worldPacket << uint32(Info.RewardXPDifficulty);
if ((Info.Flags & QUEST_FLAGS_HIDDEN_REWARDS) != 0)
if ((Info.Flags & QUEST_FLAGS_HIDE_REWARD) != 0)
_worldPacket << int32(0);
else
_worldPacket << int32(Info.RewardMoney);
@@ -339,7 +339,7 @@ WorldPacket const* WorldPackets::Quest::QueryQuestInfoResponse::Write()
_worldPacket << uint32(Info.PortraitGiver);
_worldPacket << uint32(Info.PortraitTurnIn);
if ((Info.Flags & QUEST_FLAGS_HIDDEN_REWARDS) != 0)
if ((Info.Flags & QUEST_FLAGS_HIDE_REWARD) != 0)
{
for (uint8 i = 0; i < QUEST_REWARDS_COUNT; ++i)
_worldPacket << int32(0) << uint32(0);
+2
View File
@@ -609,6 +609,8 @@ void WorldSession::LogoutPlayer(bool save)
///- Clear whisper whitelist
_player->ClearWhisperWhiteList();
_player->FailQuestsWithFlag(QUEST_FLAGS_FAIL_ON_LOGOUT);
///- empty buyback items and save the player in the database
// some save parts only correctly work in case player present in map/player_lists (pets, etc)
if (save)
+1 -1
View File
@@ -4314,7 +4314,7 @@ void Spell::EffectQuestComplete(SpellEffIndex effIndex)
uint16 logSlot = player->FindQuestSlot(questId);
if (logSlot < MAX_QUEST_LOG_SIZE)
player->AreaExploredOrEventHappens(questId);
else if (quest->HasFlag(QUEST_FLAGS_TRACKING)) // Check if the quest is used as a serverside flag.
else if (quest->HasFlag(QUEST_FLAGS_TRACKING_EVENT)) // Check if the quest is used as a serverside flag.
player->SetRewardedQuest(questId); // If so, set status to rewarded without broadcasting it to client.
}
}
+14
View File
@@ -1103,6 +1103,20 @@ bool SpellInfo::HasOnlyDamageEffects() const
return true;
}
bool SpellInfo::HasTargetType(::Targets target) const
{
for (uint8 i = 0; i < MAX_SPELL_EFFECTS; ++i)
{
if (!Effects[i].IsEffect())
continue;
if (Effects[i].TargetA.GetTarget() == target || Effects[i].TargetB.GetTarget() == target)
return true;
}
return false;
}
bool SpellInfo::CanBeInterrupted(Unit const* interruptTarget, bool ignoreImmunity /*= false*/) const
{
return HasAttribute(SPELL_ATTR7_CAN_ALWAYS_BE_INTERRUPTED)
+1
View File
@@ -474,6 +474,7 @@ class TC_GAME_API SpellInfo
bool HasAura(AuraType aura) const;
bool HasAreaAuraEffect() const;
bool HasOnlyDamageEffects() const;
bool HasTargetType(::Targets target) const;
inline bool HasAttribute(SpellAttr0 attribute) const { return !!(Attributes & attribute); }
inline bool HasAttribute(SpellAttr1 attribute) const { return !!(AttributesEx & attribute); }