diff --git a/Source/CMakeLists.txt b/Source/CMakeLists.txt index 187754ba7..5392f9ed6 100644 --- a/Source/CMakeLists.txt +++ b/Source/CMakeLists.txt @@ -152,6 +152,8 @@ set(libdevilutionx_SRCS qol/stash.cpp qol/xpbar.cpp + quests/validation.cpp + storm/storm_net.cpp storm/storm_svid.cpp diff --git a/Source/msg.cpp b/Source/msg.cpp index 9893f3c24..f462c4e4a 100644 --- a/Source/msg.cpp +++ b/Source/msg.cpp @@ -40,6 +40,7 @@ #include "pfile.h" #include "plrmsg.h" #include "portals/validation.hpp" +#include "quests/validation.hpp" #include "spells.h" #include "storm/storm_net.hpp" #include "sync.h" @@ -296,6 +297,11 @@ bool IsPortalDeltaValid(const DPortal &portal) return IsPortalDeltaValid(position, portal.level, portal.ltype, portal.setlvl); } +bool IsQuestDeltaValid(quest_id qidx, const MultiQuests &quest) +{ + return IsQuestDeltaValid(qidx, quest.qstate, quest.qlog, quest.qmsg); +} + uint8_t GetLevelForMultiplayer(uint8_t level, bool isSetLevel) { if (isSetLevel) @@ -711,6 +717,9 @@ const std::byte *DeltaImportJunk(const std::byte *src, const std::byte *end) return nullptr; } memcpy(&sgJunk.quests[q], src, sizeof(MultiQuests)); + if (!IsQuestDeltaValid(static_cast(qidx), sgJunk.quests[q])) { + sgJunk.quests[q].qstate = QUEST_INVALID; + } src += sizeof(MultiQuests); q++; } diff --git a/Source/quests/validation.cpp b/Source/quests/validation.cpp new file mode 100644 index 000000000..fff38ac01 --- /dev/null +++ b/Source/quests/validation.cpp @@ -0,0 +1,46 @@ +/** + * @file quests/validation.cpp + * + * Implementation of functions for validation of quest data. + */ + +#include "quests/validation.hpp" + +#include + +#include "objdat.h" +#include "quests.h" +#include "textdat.h" +#include "utils/is_of.hpp" + +namespace devilution { + +bool IsQuestDeltaValid(quest_id qidx, quest_state qstate, uint8_t qlog, int16_t qmsg) +{ + if (IsNoneOf(qlog, 0, 1)) + return false; + + if (qmsg < 0 || static_cast(qmsg) >= SpeechCount) + return false; + + switch (qstate) { + case QUEST_NOTAVAIL: + case QUEST_INIT: + case QUEST_ACTIVE: + case QUEST_DONE: + return true; + + case QUEST_HIVE_TEASE1: + case QUEST_HIVE_TEASE2: + case QUEST_HIVE_ACTIVE: + return qidx == Q_JERSEY; + + case QUEST_HIVE_DONE: + return IsAnyOf(qidx, Q_FARMER, Q_JERSEY); + + default: + return false; + } +} + +} // namespace devilution diff --git a/Source/quests/validation.hpp b/Source/quests/validation.hpp new file mode 100644 index 000000000..205458add --- /dev/null +++ b/Source/quests/validation.hpp @@ -0,0 +1,17 @@ +/** + * @file quests/validation.hpp + * + * Interface of functions for validation of quest data. + */ +#pragma once + +#include + +#include "objdat.h" +#include "quests.h" + +namespace devilution { + +bool IsQuestDeltaValid(quest_id qidx, quest_state qstate, uint8_t qlog, int16_t qmsg); + +} // namespace devilution diff --git a/Source/textdat.cpp b/Source/textdat.cpp index c5d2fbefd..4275bf854 100644 --- a/Source/textdat.cpp +++ b/Source/textdat.cpp @@ -916,4 +916,7 @@ const Speech Speeches[] = { 1, 5, SfxID::Adria48 }, */ }; + +const size_t SpeechCount = sizeof(Speeches) / sizeof(Speech); + } // namespace devilution diff --git a/Source/textdat.h b/Source/textdat.h index c188b61df..1a8264cf2 100644 --- a/Source/textdat.h +++ b/Source/textdat.h @@ -5,6 +5,7 @@ */ #pragma once +#include #include #include "sound_effect_enums.h" @@ -430,6 +431,7 @@ struct Speech { SfxID sfxnr; }; +extern const size_t SpeechCount; extern const Speech Speeches[]; } // namespace devilution