diff options
| author | Meji <alvaro.megias@outlook.com> | 2025-06-28 01:38:43 +0200 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2025-06-28 01:38:43 +0200 |
| commit | 138b1b1a21ff871b54f1f2bd815de30a93b803eb (patch) | |
| tree | 2d1f454eb9173417e622d8fcfea2901f41246a67 /src/server/game/Entities | |
| parent | 60400d25f5fff2dabd4aa74bbdbc0d2370360a35 (diff) | |
Core/SpawnTracking: Allow linking a spawn to more than one quest objective (#30995)
Diffstat (limited to 'src/server/game/Entities')
7 files changed, 148 insertions, 42 deletions
diff --git a/src/server/game/Entities/Creature/Creature.cpp b/src/server/game/Entities/Creature/Creature.cpp index 8e982a78800..964c6862faf 100644 --- a/src/server/game/Entities/Creature/Creature.cpp +++ b/src/server/game/Entities/Creature/Creature.cpp @@ -629,7 +629,9 @@ bool Creature::UpdateEntry(uint32 entry, CreatureData const* data /*= nullptr*/, ReplaceAllDynamicFlags(UNIT_DYNFLAG_NONE); - SetUpdateFieldValue(m_values.ModifyValue(&Unit::m_unitData).ModifyValue(&UF::UnitData::StateWorldEffectsQuestObjectiveID), data ? data->spawnTrackingQuestObjectiveId : 0); + // Set StateWorldEffectsQuestObjectiveID if there is only one linked objective for this creature + if (data && data->spawnTrackingQuestObjectives.size() == 1) + SetUpdateFieldValue(m_values.ModifyValue(&Unit::m_unitData).ModifyValue(&UF::UnitData::StateWorldEffectsQuestObjectiveID), data->spawnTrackingQuestObjectives.front()); SetCanDualWield(cInfo->flags_extra & CREATURE_FLAG_EXTRA_USE_OFFHAND_ATTACK); @@ -3207,9 +3209,9 @@ SpawnTrackingStateData const* Creature::GetSpawnTrackingStateDataForPlayer(Playe if (CreatureData const* data = GetCreatureData()) { - if (data->spawnTrackingQuestObjectiveId && data->spawnTrackingData) + if (data->spawnTrackingData && !data->spawnTrackingQuestObjectives.empty()) { - SpawnTrackingState state = player->GetSpawnTrackingStateByObjective(data->spawnTrackingData->SpawnTrackingId, data->spawnTrackingQuestObjectiveId); + SpawnTrackingState state = player->GetSpawnTrackingStateByObjectives(data->spawnTrackingData->SpawnTrackingId, data->spawnTrackingQuestObjectives); return &data->spawnTrackingStates[AsUnderlyingType(state)]; } } diff --git a/src/server/game/Entities/GameObject/GameObject.cpp b/src/server/game/Entities/GameObject/GameObject.cpp index 731bc5c7991..7e15231016a 100644 --- a/src/server/game/Entities/GameObject/GameObject.cpp +++ b/src/server/game/Entities/GameObject/GameObject.cpp @@ -1967,7 +1967,9 @@ bool GameObject::LoadFromDB(ObjectGuid::LowType spawnId, Map* map, bool addToMap PhasingHandler::InitDbPhaseShift(GetPhaseShift(), data->phaseUseFlags, data->phaseId, data->phaseGroup); PhasingHandler::InitDbVisibleMapId(GetPhaseShift(), data->terrainSwapMap); - SetUpdateFieldValue(m_values.ModifyValue(&GameObject::m_gameObjectData).ModifyValue(&UF::GameObjectData::StateWorldEffectsQuestObjectiveID), data ? data->spawnTrackingQuestObjectiveId : 0); + // Set StateWorldEffectsQuestObjectiveID if there is only one linked objective for this gameobject + if (data && data->spawnTrackingQuestObjectives.size() == 1) + SetUpdateFieldValue(m_values.ModifyValue(&GameObject::m_gameObjectData).ModifyValue(&UF::GameObjectData::StateWorldEffectsQuestObjectiveID), data->spawnTrackingQuestObjectives.front()); if (data->spawntimesecs >= 0) { @@ -3592,9 +3594,9 @@ SpawnTrackingStateData const* GameObject::GetSpawnTrackingStateDataForPlayer(Pla if (GameObjectData const* data = GetGameObjectData()) { - if (data->spawnTrackingQuestObjectiveId && data->spawnTrackingData) + if (data->spawnTrackingData && !data->spawnTrackingQuestObjectives.empty()) { - SpawnTrackingState state = player->GetSpawnTrackingStateByObjective(data->spawnTrackingData->SpawnTrackingId, data->spawnTrackingQuestObjectiveId); + SpawnTrackingState state = player->GetSpawnTrackingStateByObjectives(data->spawnTrackingData->SpawnTrackingId, data->spawnTrackingQuestObjectives); return &data->spawnTrackingStates[AsUnderlyingType(state)]; } } diff --git a/src/server/game/Entities/Object/Updates/UpdateFields.cpp b/src/server/game/Entities/Object/Updates/UpdateFields.cpp index 7986d550120..419e2eca445 100644 --- a/src/server/game/Entities/Object/Updates/UpdateFields.cpp +++ b/src/server/game/Entities/Object/Updates/UpdateFields.cpp @@ -939,7 +939,7 @@ void UnitData::WriteCreate(ByteBuffer& data, EnumFlag<UpdateFieldFlag> fieldVisi data << uint32(ViewerDependentValue<StateAnimKitIDTag>::GetValue(this, owner, receiver)); stateWorldEffectIDs = ViewerDependentValue<StateWorldEffectIDsTag>::GetValue(this, owner, receiver); data << uint32(stateWorldEffectIDs->size()); - data << uint32(StateWorldEffectsQuestObjectiveID); + data << uint32(ViewerDependentValue<StateWorldEffectsQuestObjectiveIDTag>::GetValue(this, owner, receiver)); data << int32(SpellOverrideNameID); for (uint32 i = 0; i < stateWorldEffectIDs->size(); ++i) { @@ -1275,7 +1275,7 @@ void UnitData::WriteUpdate(ByteBuffer& data, Mask const& changesMask, bool ignor } if (changesMask[12]) { - data << uint32(StateWorldEffectsQuestObjectiveID); + data << uint32(ViewerDependentValue<StateWorldEffectsQuestObjectiveIDTag>::GetValue(this, owner, receiver)); } if (changesMask[13]) { @@ -6496,7 +6496,7 @@ void GameObjectData::WriteCreate(ByteBuffer& data, EnumFlag<UpdateFieldFlag> fie data << uint32(ViewerDependentValue<SpawnTrackingStateAnimKitIDTag>::GetValue(this, owner, receiver)); stateWorldEffectIDs = ViewerDependentValue<StateWorldEffectIDsTag>::GetValue(this, owner, receiver); data << uint32(stateWorldEffectIDs->size()); - data << uint32(StateWorldEffectsQuestObjectiveID); + data << uint32(ViewerDependentValue<StateWorldEffectsQuestObjectiveIDTag>::GetValue(this, owner, receiver)); for (uint32 i = 0; i < stateWorldEffectIDs->size(); ++i) { data << uint32((*stateWorldEffectIDs)[i]); @@ -6617,7 +6617,7 @@ void GameObjectData::WriteUpdate(ByteBuffer& data, Mask const& changesMask, bool } if (changesMask[9]) { - data << uint32(StateWorldEffectsQuestObjectiveID); + data << uint32(ViewerDependentValue<StateWorldEffectsQuestObjectiveIDTag>::GetValue(this, owner, receiver)); } if (changesMask[10]) { diff --git a/src/server/game/Entities/Object/Updates/UpdateFields.h b/src/server/game/Entities/Object/Updates/UpdateFields.h index 853fc374f76..f14700d4940 100644 --- a/src/server/game/Entities/Object/Updates/UpdateFields.h +++ b/src/server/game/Entities/Object/Updates/UpdateFields.h @@ -279,6 +279,7 @@ struct UnitData : public IsUpdateFieldStructureTag, public HasChangesMask<222> UpdateField<uint32, 0, 11> StateAnimKitID; struct StateAnimKitIDTag : ViewerDependentValueTag<uint32> {}; UpdateField<uint32, 0, 12> StateWorldEffectsQuestObjectiveID; + struct StateWorldEffectsQuestObjectiveIDTag : ViewerDependentValueTag<uint32> {}; UpdateField<int32, 0, 13> SpellOverrideNameID; UpdateField<ObjectGuid, 0, 14> Charm; UpdateField<ObjectGuid, 0, 15> Summon; @@ -1213,6 +1214,7 @@ struct GameObjectData : public IsUpdateFieldStructureTag, public HasChangesMask< UpdateField<uint32, 0, 8> SpawnTrackingStateAnimKitID; struct SpawnTrackingStateAnimKitIDTag : ViewerDependentValueTag<uint32> {}; UpdateField<uint32, 0, 9> StateWorldEffectsQuestObjectiveID; + struct StateWorldEffectsQuestObjectiveIDTag : ViewerDependentValueTag<uint32> {}; UpdateField<ObjectGuid, 0, 10> CreatedBy; UpdateField<ObjectGuid, 0, 11> GuildGUID; UpdateField<uint32, 0, 12> Flags; diff --git a/src/server/game/Entities/Object/Updates/ViewerDependentValues.h b/src/server/game/Entities/Object/Updates/ViewerDependentValues.h index 73400ed23d1..dacf78b0b3f 100644 --- a/src/server/game/Entities/Object/Updates/ViewerDependentValues.h +++ b/src/server/game/Entities/Object/Updates/ViewerDependentValues.h @@ -145,8 +145,8 @@ public: dynFlags |= GO_DYNFLAG_LO_NO_INTERACT; if (SpawnMetadata const* data = sObjectMgr->GetSpawnMetadata(SPAWN_TYPE_GAMEOBJECT, gameObject->GetSpawnId())) - if (data->spawnTrackingQuestObjectiveId && data->spawnTrackingData) - if (receiver->GetSpawnTrackingStateByObjective(data->spawnTrackingData->SpawnTrackingId, data->spawnTrackingQuestObjectiveId) != SpawnTrackingState::Active) + if (data->spawnTrackingData && !data->spawnTrackingQuestObjectives.empty()) + if (receiver->GetSpawnTrackingStateByObjectives(data->spawnTrackingData->SpawnTrackingId, data->spawnTrackingQuestObjectives) != SpawnTrackingState::Active) dynFlags &= ~GO_DYNFLAG_LO_ACTIVATE; } @@ -278,6 +278,42 @@ public: }; template<> +class ViewerDependentValue<UF::UnitData::StateWorldEffectsQuestObjectiveIDTag> +{ +public: + using value_type = UF::UnitData::StateWorldEffectsQuestObjectiveIDTag::value_type; + + static value_type GetValue(UF::UnitData const* unitData, Unit const* unit, Player const* receiver) + { + value_type stateWorldEffectsQuestObjectiveId = unitData->StateWorldEffectsQuestObjectiveID; + + if (!stateWorldEffectsQuestObjectiveId && unit->IsCreature()) + { + if (CreatureData const* data = unit->ToCreature()->GetCreatureData()) + { + auto itr = data->spawnTrackingQuestObjectives.begin(); + auto end = data->spawnTrackingQuestObjectives.end(); + if (itr != end) + { + // If there is no valid objective for player, fill UF with first objective (if any) + stateWorldEffectsQuestObjectiveId = *itr; + while (++itr != end) + { + if (receiver->GetSpawnTrackingStateByObjective(data->spawnTrackingData->SpawnTrackingId, *itr) != SpawnTrackingState::Active) + continue; + + stateWorldEffectsQuestObjectiveId = *itr; + break; + } + } + } + } + + return stateWorldEffectsQuestObjectiveId; + } +}; + +template<> class ViewerDependentValue<UF::UnitData::FactionTemplateTag> { public: @@ -539,6 +575,42 @@ public: }; template<> +class ViewerDependentValue<UF::GameObjectData::StateWorldEffectsQuestObjectiveIDTag> +{ +public: + using value_type = UF::GameObjectData::StateWorldEffectsQuestObjectiveIDTag::value_type; + + static value_type GetValue(UF::GameObjectData const* gameObjectData, GameObject const* gameObject, Player const* receiver) + { + value_type stateWorldEffectsQuestObjectiveId = gameObjectData->StateWorldEffectsQuestObjectiveID; + + if (!stateWorldEffectsQuestObjectiveId) + { + if (::GameObjectData const* data = gameObject->GetGameObjectData()) + { + auto itr = data->spawnTrackingQuestObjectives.begin(); + auto end = data->spawnTrackingQuestObjectives.end(); + if (itr != end) + { + // If there is no valid objective for player, fill UF with first objective (if any) + stateWorldEffectsQuestObjectiveId = *itr; + while (++itr != end) + { + if (receiver->GetSpawnTrackingStateByObjective(data->spawnTrackingData->SpawnTrackingId, *itr) != SpawnTrackingState::Active) + continue; + + stateWorldEffectsQuestObjectiveId = *itr; + break; + } + } + } + } + + return stateWorldEffectsQuestObjectiveId; + } +}; + +template<> class ViewerDependentValue<UF::GameObjectData::FlagsTag> { public: diff --git a/src/server/game/Entities/Player/Player.cpp b/src/server/game/Entities/Player/Player.cpp index f05a69123f1..351a4758222 100644 --- a/src/server/game/Entities/Player/Player.cpp +++ b/src/server/game/Entities/Player/Player.cpp @@ -16852,38 +16852,40 @@ void Player::UpdateQuestObjectiveProgress(QuestObjectiveType objectiveType, int3 } } - if (data && data->spawnTrackingQuestObjectiveId && data->spawnTrackingData) + if (data && data->spawnTrackingData && !data->spawnTrackingQuestObjectives.empty()) { - if (objective->ID == data->spawnTrackingQuestObjectiveId) + for (uint32 spawnTrackingQuestObjectiveId : data->spawnTrackingQuestObjectives) { - // Store spawn tracking to return correct state in Player::GetSpawnTrackingStateByObjective - QuestStatusData& questStatus = objectiveItr.second.QuestStatusItr->second; - questStatus.SpawnTrackingList.insert(std::make_pair(objective->StorageIndex, data->spawnTrackingData->SpawnTrackingId)); - - // Send QuestPOIUpdateResponse for every spawn linked to same SpawnTrackingId - for (auto const& [spawnTrackingId, data] : sObjectMgr->GetSpawnMetadataForSpawnTracking(data->spawnTrackingData->SpawnTrackingId)) + if (objective->ID == spawnTrackingQuestObjectiveId) { - SpawnData const* spawnData = data->ToSpawnData(); - if (!spawnData) - continue; + // Store spawn tracking to return correct state in Player::GetSpawnTrackingStateByObjective + QuestStatusData& questStatus = objectiveItr.second.QuestStatusItr->second; + questStatus.SpawnTrackingList.insert(std::make_pair(objective->StorageIndex, data->spawnTrackingData->SpawnTrackingId)); - WorldPackets::Quest::QuestPOIUpdateResponse response; + // Send QuestPOIUpdateResponse for every spawn linked to same SpawnTrackingId + for (auto const& [spawnTrackingId, data] : sObjectMgr->GetSpawnMetadataForSpawnTracking(data->spawnTrackingData->SpawnTrackingId)) + { + SpawnData const* spawnData = data->ToSpawnData(); + if (!spawnData) + continue; - WorldPackets::Quest::SpawnTrackingResponseInfo responseInfo; - responseInfo.SpawnTrackingID = data->spawnTrackingData->SpawnTrackingId; - responseInfo.ObjectID = spawnData->id; - responseInfo.PhaseID = spawnData->phaseId; - responseInfo.PhaseGroupID = spawnData->phaseGroup; - responseInfo.PhaseUseFlags = spawnData->phaseUseFlags; + WorldPackets::Quest::QuestPOIUpdateResponse response; - SpawnTrackingState state = GetSpawnTrackingStateByObjective(data->spawnTrackingData->SpawnTrackingId, objective->ID); - responseInfo.Visible = data->spawnTrackingStates[AsUnderlyingType(state)].Visible; + WorldPackets::Quest::SpawnTrackingResponseInfo& responseInfo = response.SpawnTrackingResponses.emplace_back(); + responseInfo.SpawnTrackingID = data->spawnTrackingData->SpawnTrackingId; + responseInfo.ObjectID = spawnData->id; + responseInfo.PhaseID = spawnData->phaseId; + responseInfo.PhaseGroupID = spawnData->phaseGroup; + responseInfo.PhaseUseFlags = spawnData->phaseUseFlags; - response.SpawnTrackingResponses.push_back(std::move(responseInfo)); - SendDirectMessage(response.Write()); - } + SpawnTrackingState state = GetSpawnTrackingStateByObjectives(data->spawnTrackingData->SpawnTrackingId, data->spawnTrackingQuestObjectives); + responseInfo.Visible = data->spawnTrackingStates[AsUnderlyingType(state)].Visible; - anyObjectiveChangedSpawnTrackingState = true; + SendDirectMessage(response.Write()); + } + + anyObjectiveChangedSpawnTrackingState = true; + } } } @@ -17508,16 +17510,38 @@ void Player::SendForceSpawnTrackingUpdate(uint32 questId) const } } -QuestObjective const* Player::GetActiveQuestObjectiveForForSpawnTracking(uint32 spawnTrackingId) const +QuestObjective const* Player::GetActiveQuestObjectiveForSpawnTracking(uint32 spawnTrackingId) const { if (std::vector<QuestObjective const*> const* questObjectiveList = sObjectMgr->GetSpawnTrackingQuestObjectiveList(spawnTrackingId)) for (QuestObjective const* questObjective : *questObjectiveList) - if (FindQuestSlot(questObjective->QuestID) < MAX_QUEST_LOG_SIZE) + if (IsQuestObjectiveCompletable(questObjective->QuestID, questObjective->ID)) return questObjective; return nullptr; } +SpawnTrackingState Player::GetSpawnTrackingStateByObjectives(uint32 spawnTrackingId, std::vector<uint32> const& questObjectives) const +{ + if (spawnTrackingId && !questObjectives.empty()) + { + bool hasAnyQuestObjectiveCompletable = false; + for (uint32 questObjectiveId : questObjectives) + { + SpawnTrackingState state = GetSpawnTrackingStateByObjective(spawnTrackingId, questObjectiveId); + if (state == SpawnTrackingState::Complete) + return SpawnTrackingState::Complete; + + if (state == SpawnTrackingState::Active) + hasAnyQuestObjectiveCompletable = true; + } + + if (hasAnyQuestObjectiveCompletable) + return SpawnTrackingState::Active; + } + + return SpawnTrackingState::None; +} + SpawnTrackingState Player::GetSpawnTrackingStateByObjective(uint32 spawnTrackingId, uint32 questObjectiveId) const { if (spawnTrackingId && questObjectiveId && sObjectMgr->IsQuestObjectiveForSpawnTracking(spawnTrackingId, questObjectiveId)) @@ -17526,7 +17550,8 @@ SpawnTrackingState Player::GetSpawnTrackingStateByObjective(uint32 spawnTracking { if (IsQuestRewarded(questObjective->QuestID) || IsQuestObjectiveComplete(questObjective->QuestID, questObjective->ID)) return SpawnTrackingState::Complete; - else if (GetQuestStatus(questObjective->QuestID) != QUEST_STATUS_NONE && IsQuestObjectiveCompletable(questObjective->QuestID, questObjective->ID)) + + if (GetQuestStatus(questObjective->QuestID) != QUEST_STATUS_NONE && IsQuestObjectiveCompletable(questObjective->QuestID, questObjective->ID)) { auto itr = m_QuestStatus.find(questObjective->QuestID); if (itr != m_QuestStatus.end() && itr->second.Slot < MAX_QUEST_LOG_SIZE) @@ -25742,13 +25767,14 @@ void Player::UpdateVisibleObjectInteractions(bool allUnits, bool onlySpellClicks UF::GameObjectData::Base goMask; SpawnMetadata const* data = sObjectMgr->GetSpawnMetadata(SPAWN_TYPE_GAMEOBJECT, gameObject->GetSpawnId()); - if (data && data->spawnTrackingQuestObjectiveId && data->spawnTrackingData) + if (data && data->spawnTrackingData && !data->spawnTrackingQuestObjectives.empty()) { objMask.MarkChanged(&UF::ObjectData::DynamicFlags); goMask.MarkChanged(&UF::GameObjectData::StateWorldEffectIDs); goMask.MarkChanged(&UF::GameObjectData::StateSpellVisualID); goMask.MarkChanged(&UF::GameObjectData::SpawnTrackingStateAnimID); goMask.MarkChanged(&UF::GameObjectData::SpawnTrackingStateAnimKitID); + goMask.MarkChanged(&UF::GameObjectData::StateWorldEffectsQuestObjectiveID); } else if (m_questObjectiveStatus.contains({ QUEST_OBJECTIVE_GAMEOBJECT, int32(gameObject->GetEntry()) }) || gameObject->GetGOInfo()->GetConditionID1()) objMask.MarkChanged(&UF::ObjectData::DynamicFlags); @@ -25789,13 +25815,14 @@ void Player::UpdateVisibleObjectInteractions(bool allUnits, bool onlySpellClicks unitMask.MarkChanged(&UF::UnitData::NpcFlags2); SpawnMetadata const* data = sObjectMgr->GetSpawnMetadata(SPAWN_TYPE_CREATURE, creature->GetSpawnId()); - if (data && data->spawnTrackingQuestObjectiveId && data->spawnTrackingData) + if (data && data->spawnTrackingData && !data->spawnTrackingQuestObjectives.empty()) { objMask.MarkChanged(&UF::ObjectData::DynamicFlags); unitMask.MarkChanged(&UF::UnitData::StateWorldEffectIDs); unitMask.MarkChanged(&UF::UnitData::StateSpellVisualID); unitMask.MarkChanged(&UF::UnitData::StateAnimID); unitMask.MarkChanged(&UF::UnitData::StateAnimKitID); + unitMask.MarkChanged(&UF::UnitData::StateWorldEffectsQuestObjectiveID); } if (objMask.GetChangesMask().IsAnySet() || unitMask.GetChangesMask().IsAnySet()) diff --git a/src/server/game/Entities/Player/Player.h b/src/server/game/Entities/Player/Player.h index 2d21399464d..cd457d24ca0 100644 --- a/src/server/game/Entities/Player/Player.h +++ b/src/server/game/Entities/Player/Player.h @@ -1797,7 +1797,8 @@ class TC_GAME_API Player final : public Unit, public GridObject<Player> bool HasPvPForcingQuest() const; void SendForceSpawnTrackingUpdate(uint32 questId) const; - QuestObjective const* GetActiveQuestObjectiveForForSpawnTracking(uint32 spawnTrackingId) const; + QuestObjective const* GetActiveQuestObjectiveForSpawnTracking(uint32 spawnTrackingId) const; + SpawnTrackingState GetSpawnTrackingStateByObjectives(uint32 spawnTrackingId, std::vector<uint32> const& questObjectives) const; SpawnTrackingState GetSpawnTrackingStateByObjective(uint32 spawnTrackingId, uint32 questObjectiveId) const; /*********************************************************/ |
