#include #include #include #include #include #include #include namespace { Console::Setting bEnableMiscQuestSync{"Gameplay:bEnableMiscQuestSync", "(Experimental) Syncs miscellaneous quests when possible", false}; } QuestService::QuestService(World& aWorld, entt::dispatcher& aDispatcher) : m_world(aWorld) { m_questUpdateConnection = aDispatcher.sink>().connect<&QuestService::OnQuestChanges>(this); } void QuestService::OnQuestChanges(const PacketEvent& acMessage) noexcept { const auto& message = acMessage.Packet; auto* pPlayer = acMessage.pPlayer; auto& questComponent = pPlayer->GetQuestLogComponent(); auto& entries = questComponent.QuestContent.Entries; auto questIt = std::find_if(entries.begin(), entries.end(), [&message](const auto& e) { return e.Id == message.Id; }); NotifyQuestUpdate notify{}; notify.Id = message.Id; notify.Stage = message.Stage; notify.Status = message.Status; notify.ClientQuestType = message.ClientQuestType; if (notify.ClientQuestType == 0 || notify.ClientQuestType == 6) // Types None or Miscellaneous. Hard-coded to avoid client header file. { if (!bEnableMiscQuestSync) return; spdlog::info("{}: syncing type none/misc quest to party, gameId {:X} questStage {} questStatus {} questType {}", __FUNCTION__, notify.Id.LogFormat(), notify.Stage, notify.Status, notify.ClientQuestType); } if (message.Status == RequestQuestUpdate::Started || message.Status == RequestQuestUpdate::StageUpdate) { // in order to prevent bugs when a quest is in progress // and being updated we add it as a new quest record to // maintain a proper remote questlog state. if (questIt == entries.end()) { auto& newQuest = entries.emplace_back(); newQuest.Id = message.Id; newQuest.Stage = message.Stage; if (message.Status == RequestQuestUpdate::Started) { spdlog::debug("Started quest: {:X} stage: {}", message.Id.LogFormat(), message.Stage); notify.Status = NotifyQuestUpdate::Started; } else { notify.Status = NotifyQuestUpdate::StageUpdate; } } else { spdlog::debug("Updated quest: {:X}, stage: {}", message.Id.LogFormat(), message.Id.BaseId, message.Stage); auto& record = *questIt; record.Id = message.Id; record.Stage = message.Stage; notify.Status = NotifyQuestUpdate::StageUpdate; } } else if (message.Status == RequestQuestUpdate::Stopped) { spdlog::debug("Stopped quest: {:X}, stage: {}", message.Id.LogFormat(), message.Id.BaseId, message.Stage); if (questIt != entries.end()) entries.erase(questIt); else spdlog::warn("Unable to delete quest object {:X}", message.Id.LogFormat(), message.Id.BaseId); notify.Status = NotifyQuestUpdate::Stopped; } const auto& partyComponent = acMessage.pPlayer->GetParty(); if (!partyComponent.JoinedPartyId.has_value()) return; GameServer::Get()->SendToParty(notify, partyComponent, acMessage.GetSender()); }