diff --git a/Source/CMakeLists.txt b/Source/CMakeLists.txt index a76a8f125..187754ba7 100644 --- a/Source/CMakeLists.txt +++ b/Source/CMakeLists.txt @@ -142,6 +142,8 @@ set(libdevilutionx_SRCS platform/locale.cpp + portals/validation.cpp + qol/autopickup.cpp qol/chatlog.cpp qol/floatingnumbers.cpp diff --git a/Source/control.cpp b/Source/control.cpp index ca861e65d..07b21caf4 100644 --- a/Source/control.cpp +++ b/Source/control.cpp @@ -422,12 +422,6 @@ void AppendArenaOverview(std::string &ret) } } -const dungeon_type DungeonTypeForArena[] = { - dungeon_type::DTYPE_CATHEDRAL, // SL_ARENA_CHURCH - dungeon_type::DTYPE_HELL, // SL_ARENA_HELL - dungeon_type::DTYPE_HELL, // SL_ARENA_CIRCLE_OF_LIFE -}; - std::string TextCmdArena(const std::string_view parameter) { std::string ret; @@ -455,7 +449,7 @@ std::string TextCmdArena(const std::string_view parameter) return ret; } - setlvltype = DungeonTypeForArena[arenaLevel - SL_FIRST_ARENA]; + setlvltype = GetArenaLevelType(arenaLevel); StartNewLvl(*MyPlayer, WM_DIABSETLVL, arenaLevel); return ret; } diff --git a/Source/levels/setmaps.h b/Source/levels/setmaps.h index 0d2a8fe51..59cc0a93a 100644 --- a/Source/levels/setmaps.h +++ b/Source/levels/setmaps.h @@ -5,8 +5,26 @@ */ #pragma once +#include "levels/gendung.h" + namespace devilution { +/** + * @brief Get the tile type used to render the given arena level + */ +inline dungeon_type GetArenaLevelType(_setlevels arenaLevel) +{ + constexpr dungeon_type DungeonTypeForArena[] = { + dungeon_type::DTYPE_CATHEDRAL, // SL_ARENA_CHURCH + dungeon_type::DTYPE_HELL, // SL_ARENA_HELL + dungeon_type::DTYPE_HELL, // SL_ARENA_CIRCLE_OF_LIFE + }; + + constexpr size_t arenaCount = sizeof(DungeonTypeForArena) / sizeof(dungeon_type); + const size_t index = arenaLevel - SL_FIRST_ARENA; + return index < arenaCount ? DungeonTypeForArena[index] : DTYPE_NONE; +} + /** * @brief Load a quest map, the given map is specified via the global setlvlnum */ diff --git a/Source/msg.cpp b/Source/msg.cpp index c64524a02..9893f3c24 100644 --- a/Source/msg.cpp +++ b/Source/msg.cpp @@ -39,6 +39,7 @@ #include "pack.h" #include "pfile.h" #include "plrmsg.h" +#include "portals/validation.hpp" #include "spells.h" #include "storm/storm_net.hpp" #include "sync.h" @@ -289,6 +290,12 @@ Item ItemLimbo; /** @brief Last sent player command for the local player. */ TCmdLocParam5 lastSentPlayerCmd; +bool IsPortalDeltaValid(const DPortal &portal) +{ + const WorldTilePosition position { portal.x, portal.y }; + return IsPortalDeltaValid(position, portal.level, portal.ltype, portal.setlvl); +} + uint8_t GetLevelForMultiplayer(uint8_t level, bool isSetLevel) { if (isSetLevel) @@ -689,6 +696,8 @@ const std::byte *DeltaImportJunk(const std::byte *src, const std::byte *end) if (src + sizeof(DPortal) > end) return nullptr; memcpy(&sgJunk.portal[i], src, sizeof(DPortal)); + if (!IsPortalDeltaValid(sgJunk.portal[i])) + memset(&sgJunk.portal[i], 0xFF, sizeof(DPortal)); src += sizeof(DPortal); } } diff --git a/Source/portals/validation.cpp b/Source/portals/validation.cpp new file mode 100644 index 000000000..0557fb2aa --- /dev/null +++ b/Source/portals/validation.cpp @@ -0,0 +1,49 @@ +/** + * @file portals/validation.cpp + * + * Implementation of functions for validation of portal data. + */ + +#include "portals/validation.hpp" + +#include + +#include "engine/world_tile.hpp" +#include "levels/gendung.h" +#include "levels/setmaps.h" +#include "quests.h" + +namespace devilution { + +namespace { + +dungeon_type GetQuestLevelType(_setlevels questLevel) +{ + for (const Quest &quest : Quests) { + if (quest._qslvl == questLevel) + return quest._qlvltype; + } + return DTYPE_NONE; +} + +dungeon_type GetSetLevelType(_setlevels setLevel) +{ + bool isArenaLevel = setLevel >= SL_FIRST_ARENA && setLevel <= SL_LAST; + return isArenaLevel ? GetArenaLevelType(setLevel) : GetQuestLevelType(setLevel); +} + +} // namespace + +bool IsPortalDeltaValid(WorldTilePosition location, uint8_t level, uint8_t ltype, bool isOnSetLevel) +{ + if (!InDungeonBounds(location)) + return false; + dungeon_type levelType = static_cast(ltype); + if (levelType == DTYPE_NONE) + return false; + if (isOnSetLevel) + return levelType == GetSetLevelType(static_cast<_setlevels>(level)); + return levelType == GetLevelType(level); +} + +} // namespace devilution diff --git a/Source/portals/validation.hpp b/Source/portals/validation.hpp new file mode 100644 index 000000000..c0e2c3c30 --- /dev/null +++ b/Source/portals/validation.hpp @@ -0,0 +1,16 @@ +/** + * @file portals/validation.hpp + * + * Interface of functions for validation of portal data. + */ +#pragma once + +#include + +#include "engine/world_tile.hpp" + +namespace devilution { + +bool IsPortalDeltaValid(WorldTilePosition location, uint8_t level, uint8_t levelType, bool isOnSetLevel); + +} // namespace devilution