diff --git a/src/openrct2-ui/windows/Scenery.cpp b/src/openrct2-ui/windows/Scenery.cpp index 562f209607..1756e3830e 100644 --- a/src/openrct2-ui/windows/Scenery.cpp +++ b/src/openrct2-ui/windows/Scenery.cpp @@ -187,7 +187,7 @@ static void init_scenery_entry( { Guard::ArgumentInRange(selection.EntryIndex, 0, OBJECT_ENTRY_INDEX_NULL); - if (gCheatsIgnoreResearchStatus || scenery_is_invented(selection)) + if (IsSceneryAvailableToBuild(selection)) { // Check if in any other groups for (const auto& otherTab : _tabEntries) @@ -296,7 +296,7 @@ static void window_scenery_init(rct_window* w) for (size_t i = 0; i < sceneryGroupEntry->entry_count; i++) { auto sceneryEntry = sceneryGroupEntry->scenery_entries[i]; - if (scenery_is_invented(sceneryEntry) || gCheatsIgnoreResearchStatus) + if (IsSceneryAvailableToBuild(sceneryEntry)) { tabInfo.Entries.push_back(sceneryEntry); } diff --git a/src/openrct2/GameState.cpp b/src/openrct2/GameState.cpp index 1812e82eae..43163e6abd 100644 --- a/src/openrct2/GameState.cpp +++ b/src/openrct2/GameState.cpp @@ -78,6 +78,9 @@ void GameState::InitAll(int32_t mapSize) context_broadcast_intent(&intent); load_palette(); + + CheatsReset(); + ClearRestrictedScenery(); } /** diff --git a/src/openrct2/ParkFile.cpp b/src/openrct2/ParkFile.cpp index 6f32203b60..b97099c2af 100644 --- a/src/openrct2/ParkFile.cpp +++ b/src/openrct2/ParkFile.cpp @@ -84,6 +84,7 @@ namespace OpenRCT2 constexpr uint32_t BANNERS = 0x33; // constexpr uint32_t STAFF = 0x35; constexpr uint32_t CHEATS = 0x36; + constexpr uint32_t RESTRICTED_OBJECTS = 0x37; constexpr uint32_t PACKED_OBJECTS = 0x80; // clang-format on }; // namespace ParkFileChunkType @@ -128,6 +129,7 @@ namespace OpenRCT2 ReadWriteNotificationsChunk(os); ReadWriteInterfaceChunk(os); ReadWriteCheatsChunk(os); + ReadWriteRestrictedObjectsChunk(os); // Initial cash will eventually be removed gInitialCash = gCash; @@ -156,6 +158,7 @@ namespace OpenRCT2 ReadWriteNotificationsChunk(os); ReadWriteInterfaceChunk(os); ReadWriteCheatsChunk(os); + ReadWriteRestrictedObjectsChunk(os); ReadWritePackedObjectsChunk(os); } @@ -438,6 +441,28 @@ namespace OpenRCT2 }); } + void ReadWriteRestrictedObjectsChunk(OrcaStream& os) + { + os.ReadWriteChunk(ParkFileChunkType::RESTRICTED_OBJECTS, [](OrcaStream::ChunkStream& cs) { + auto& restrictedScenery = GetRestrictedScenery(); + + // We are want to support all object types in the future, so convert scenery type + // to object type when we write the list + cs.ReadWriteVector(restrictedScenery, [&cs](ScenerySelection& item) { + if (cs.GetMode() == OrcaStream::Mode::READING) + { + item.SceneryType = GetSceneryTypeFromObjectType(static_cast(cs.Read())); + item.EntryIndex = cs.Read(); + } + else + { + cs.Write(static_cast(GetObjectTypeFromSceneryType(item.SceneryType))); + cs.Write(item.EntryIndex); + } + }); + }); + } + void ReadWritePackedObjectsChunk(OrcaStream& os) { static constexpr uint8_t DESCRIPTOR_DAT = 0; diff --git a/src/openrct2/rct1/S4Importer.cpp b/src/openrct2/rct1/S4Importer.cpp index 89fbc1f75e..5be2ea7452 100644 --- a/src/openrct2/rct1/S4Importer.cpp +++ b/src/openrct2/rct1/S4Importer.cpp @@ -219,6 +219,8 @@ public: research_determine_first_of_type(); CheatsReset(); + ClearRestrictedScenery(); + RestrictAllMiscScenery(); } bool GetDetails(scenario_index_entry* dst) override diff --git a/src/openrct2/rct2/S6Importer.cpp b/src/openrct2/rct2/S6Importer.cpp index 05decd245f..18e46fc53e 100644 --- a/src/openrct2/rct2/S6Importer.cpp +++ b/src/openrct2/rct2/S6Importer.cpp @@ -480,6 +480,7 @@ public: research_determine_first_of_type(); CheatsReset(); + ClearRestrictedScenery(); } void ImportRides() diff --git a/src/openrct2/world/Scenery.cpp b/src/openrct2/world/Scenery.cpp index 9d898ea5e1..8aa9cca09d 100644 --- a/src/openrct2/world/Scenery.cpp +++ b/src/openrct2/world/Scenery.cpp @@ -26,6 +26,7 @@ #include "Climate.h" #include "Footpath.h" #include "Fountain.h" +#include "LargeScenery.h" #include "Map.h" #include "Park.h" #include "SmallScenery.h" @@ -52,6 +53,8 @@ int16_t gSceneryCtrlPressZ; money32 gClearSceneryCost; +static std::vector _restrictedScenery; + // rct2: 0x009A3E74 const CoordsXY SceneryQuadrantOffsets[] = { { 7, 7 }, { 7, 23 }, { 23, 23 }, { 23, 7 } }; @@ -281,3 +284,149 @@ int32_t wall_entry_get_door_sound(const rct_scenery_entry* wallEntry) { return (wallEntry->wall.flags2 & WALL_SCENERY_2_DOOR_SOUND_MASK) >> WALL_SCENERY_2_DOOR_SOUND_SHIFT; } + +bool IsSceneryAvailableToBuild(ScenerySelection item) +{ + if (!gCheatsIgnoreResearchStatus) + { + if (!scenery_is_invented(item)) + { + return false; + } + } + + if (!gCheatsSandboxMode) + { + if (IsSceneryItemRestricted(item)) + { + return false; + } + } + + return true; +} + +static ObjectEntryIndex GetMaxObjectsForSceneryType(uint8_t sceneryType) +{ + switch (sceneryType) + { + case SCENERY_TYPE_SMALL: + return MAX_SMALL_SCENERY_OBJECTS; + case SCENERY_TYPE_PATH_ITEM: + return MAX_PATH_ADDITION_OBJECTS; + case SCENERY_TYPE_WALL: + return MAX_WALL_SCENERY_OBJECTS; + case SCENERY_TYPE_LARGE: + return MAX_LARGE_SCENERY_OBJECTS; + case SCENERY_TYPE_BANNER: + return MAX_BANNER_OBJECTS; + default: + return 0; + } +} + +static rct_scenery_entry* GetSceneryEntry(ScenerySelection item) +{ + switch (item.SceneryType) + { + case SCENERY_TYPE_SMALL: + return get_small_scenery_entry(item.EntryIndex); + case SCENERY_TYPE_PATH_ITEM: + return get_footpath_item_entry(item.EntryIndex); + case SCENERY_TYPE_WALL: + return get_wall_entry(item.EntryIndex); + case SCENERY_TYPE_LARGE: + return get_large_scenery_entry(item.EntryIndex); + case SCENERY_TYPE_BANNER: + return get_banner_entry(item.EntryIndex); + default: + return nullptr; + } +} + +bool IsSceneryItemRestricted(ScenerySelection item) +{ + auto it = std::find(_restrictedScenery.begin(), _restrictedScenery.end(), item); + return it != _restrictedScenery.end(); +} + +void ClearRestrictedScenery() +{ + _restrictedScenery.clear(); +} + +std::vector& GetRestrictedScenery() +{ + return _restrictedScenery; +} + +void RestrictAllMiscScenery() +{ + std::vector nonMiscScenery; + for (ObjectEntryIndex i = 0; i < MAX_SCENERY_GROUP_OBJECTS; i++) + { + auto sgEntry = get_scenery_group_entry(i); + if (sgEntry != nullptr) + { + for (size_t j = 0; j < sgEntry->entry_count; j++) + { + nonMiscScenery.push_back(sgEntry->scenery_entries[j]); + } + } + } + for (uint8_t sceneryType = SCENERY_TYPE_SMALL; sceneryType < SCENERY_TYPE_COUNT; sceneryType++) + { + auto maxObjects = GetMaxObjectsForSceneryType(sceneryType); + for (ObjectEntryIndex i = 0; i < maxObjects; i++) + { + ScenerySelection sceneryItem = { sceneryType, i }; + auto sceneryEntry = GetSceneryEntry(sceneryItem); + if (sceneryEntry != nullptr) + { + auto it = std::find(nonMiscScenery.begin(), nonMiscScenery.end(), sceneryItem); + if (it == nonMiscScenery.end()) + { + _restrictedScenery.push_back(sceneryItem); + } + } + } + } +} + +ObjectType GetObjectTypeFromSceneryType(uint8_t type) +{ + switch (type) + { + case SCENERY_TYPE_SMALL: + return ObjectType::SmallScenery; + case SCENERY_TYPE_PATH_ITEM: + return ObjectType::PathBits; + case SCENERY_TYPE_WALL: + return ObjectType::Walls; + case SCENERY_TYPE_LARGE: + return ObjectType::LargeScenery; + case SCENERY_TYPE_BANNER: + return ObjectType::Banners; + default: + throw std::runtime_error("Invalid scenery type"); + } +} + +uint8_t GetSceneryTypeFromObjectType(ObjectType type) +{ + switch (type) + { + case ObjectType::SmallScenery: + return SCENERY_TYPE_SMALL; + case ObjectType::PathBits: + return SCENERY_TYPE_PATH_ITEM; + case ObjectType::Walls: + return SCENERY_TYPE_WALL; + case ObjectType::LargeScenery: + return SCENERY_TYPE_LARGE; + case ObjectType::Banners: + return SCENERY_TYPE_BANNER; + default: + throw std::runtime_error("Invalid object type"); + } +} diff --git a/src/openrct2/world/Scenery.h b/src/openrct2/world/Scenery.h index 410d9022cf..94ead1534b 100644 --- a/src/openrct2/world/Scenery.h +++ b/src/openrct2/world/Scenery.h @@ -264,4 +264,14 @@ rct_scenery_group_entry* get_scenery_group_entry(ObjectEntryIndex entryIndex); int32_t wall_entry_get_door_sound(const rct_scenery_entry* wallEntry); +bool IsSceneryAvailableToBuild(ScenerySelection item); + +bool IsSceneryItemRestricted(ScenerySelection item); +void ClearRestrictedScenery(); +void RestrictAllMiscScenery(); +std::vector& GetRestrictedScenery(); + +ObjectType GetObjectTypeFromSceneryType(uint8_t type); +uint8_t GetSceneryTypeFromObjectType(ObjectType type); + #endif