From 9ee72b14e5b2f3d7d71bcbc2ec7435f09cc60601 Mon Sep 17 00:00:00 2001 From: Aaron Date: Thu, 9 Jan 2025 21:00:57 -0800 Subject: [PATCH] Cleaned up CityDataFile. --- OpenTESArena/src/Assets/CityDataFile.cpp | 17 +++-- OpenTESArena/src/Assets/CityDataFile.h | 70 +++++++++---------- .../src/WorldMap/ArenaLocationUtils.cpp | 22 +++--- .../src/WorldMap/ArenaLocationUtils.h | 14 ++-- .../src/WorldMap/LocationDefinition.cpp | 4 +- .../src/WorldMap/LocationDefinition.h | 4 +- .../src/WorldMap/ProvinceDefinition.cpp | 26 +++---- 7 files changed, 72 insertions(+), 85 deletions(-) diff --git a/OpenTESArena/src/Assets/CityDataFile.cpp b/OpenTESArena/src/Assets/CityDataFile.cpp index cec05d42c..abb3fff6b 100644 --- a/OpenTESArena/src/Assets/CityDataFile.cpp +++ b/OpenTESArena/src/Assets/CityDataFile.cpp @@ -10,22 +10,22 @@ #include "components/utilities/String.h" #include "components/vfs/manager.hpp" -bool CityDataFile::ProvinceData::LocationData::isVisible() const +bool ArenaLocationData::isVisible() const { return (this->visibility & 0x2) != 0; } -void CityDataFile::ProvinceData::LocationData::setVisible(bool visible) +void ArenaLocationData::setVisible(bool visible) { this->visibility = visible ? 0x2 : 0; } -Rect CityDataFile::ProvinceData::getGlobalRect() const +Rect ArenaProvinceData::getGlobalRect() const { return Rect(this->globalX, this->globalY, this->globalW, this->globalH); } -const CityDataFile::ProvinceData::LocationData &CityDataFile::ProvinceData::getLocationData(int locationID) const +const ArenaLocationData &ArenaProvinceData::getLocationData(int locationID) const { if (locationID < 8) { @@ -69,13 +69,13 @@ const CityDataFile::ProvinceData::LocationData &CityDataFile::ProvinceData::getL } } -CityDataFile::ProvinceData &CityDataFile::getProvinceData(int index) +ArenaProvinceData &CityDataFile::getProvinceData(int index) { DebugAssertIndex(this->provinces, index); return this->provinces[index]; } -const CityDataFile::ProvinceData &CityDataFile::getProvinceData(int index) const +const ArenaProvinceData &CityDataFile::getProvinceData(int index) const { DebugAssertIndex(this->provinces, index); return this->provinces[index]; @@ -93,7 +93,7 @@ bool CityDataFile::init(const char *filename) const uint8_t *srcPtr = reinterpret_cast(src.begin()); // Iterate over each province and initialize the location data. - for (size_t i = 0; i < this->provinces.size(); i++) + for (size_t i = 0; i < std::size(this->provinces); i++) { auto &province = this->provinces[i]; @@ -118,8 +118,7 @@ bool CityDataFile::init(const char *filename) const uint8_t *locationPtr = provinceGlobalDimsPtr + (sizeof(uint16_t) * 4); // Lambda for initializing the given location and pushing the location pointer forward. - auto initLocation = [nameSize, &locationPtr]( - CityDataFile::ProvinceData::LocationData &locationData) + auto initLocation = [nameSize, &locationPtr](ArenaLocationData &locationData) { const char *locationNamePtr = reinterpret_cast(locationPtr); locationData.name = std::string(locationNamePtr); diff --git a/OpenTESArena/src/Assets/CityDataFile.h b/OpenTESArena/src/Assets/CityDataFile.h index d877e6075..d02ae38a1 100644 --- a/OpenTESArena/src/Assets/CityDataFile.h +++ b/OpenTESArena/src/Assets/CityDataFile.h @@ -1,12 +1,43 @@ #ifndef CITY_DATA_FILE_H #define CITY_DATA_FILE_H -#include #include #include #include "../Math/Rect.h" +// One world map location. +struct ArenaLocationData +{ + std::string name; // Up to twenty chars, null-terminated. + uint16_t x, y; // Position on screen. + uint8_t visibility; // Visibility on map. Only used for dungeons. 0x02 = visible. + + bool isVisible() const; + void setVisible(bool visible); +}; + +// Each province contains 8 city-states, 8 towns, 16 villages, 2 main quest dungeons, +// and 14 spaces for random dungeons. The center province is an exception; it has just 1 +// city (all others are zeroed out). +struct ArenaProvinceData +{ + std::string name; // Up to twenty chars, null-terminated. + uint16_t globalX, globalY, globalW, globalH; // Province-to-world-map projection. + ArenaLocationData cityStates[8]; + ArenaLocationData towns[8]; + ArenaLocationData villages[16]; + ArenaLocationData secondDungeon; // Staff dungeon. + ArenaLocationData firstDungeon; // Staff map dungeon. + ArenaLocationData randomDungeons[14]; // Random names, fixed locations. + + // Creates a rectangle from the province's global {X,Y,W,H} values. + Rect getGlobalRect() const; + + // Gets the location associated with the given location ID. + const ArenaLocationData &getLocationData(int locationID) const; +}; + // CITYDATA.00 contains basic data for locations in each province on the world map. // It has the names of each place and their XY coordinates on the screen. *.64 files are // for swapping, *.65 files are templates for new characters, and *.0x files contain @@ -14,45 +45,14 @@ class CityDataFile { public: - // Each province contains 8 city-states, 8 towns, 16 villages, 2 main quest dungeons, - // and 14 spaces for random dungeons. The center province is an exception; it has just 1 - // city (all others are zeroed out). - struct ProvinceData - { - struct LocationData - { - std::string name; // Up to twenty chars, null-terminated. - uint16_t x, y; // Position on screen. - uint8_t visibility; // Visibility on map. Only used for dungeons. 0x02 = visible. - - bool isVisible() const; - void setVisible(bool visible); - }; - - std::string name; // Up to twenty chars, null-terminated. - uint16_t globalX, globalY, globalW, globalH; // Province-to-world-map projection. - std::array cityStates; - std::array towns; - std::array villages; - LocationData secondDungeon; // Staff dungeon. - LocationData firstDungeon; // Staff map dungeon. - std::array randomDungeons; // Random names, fixed locations. - - // Creates a rectangle from the province's global {X,Y,W,H} values. - Rect getGlobalRect() const; - - // Gets the location associated with the given location ID. - const CityDataFile::ProvinceData::LocationData &getLocationData(int locationID) const; - }; - static constexpr int PROVINCE_COUNT = 9; private: // These are ordered the same as usual (read left to right, and center is last). - std::array provinces; + ArenaProvinceData provinces[PROVINCE_COUNT]; public: // Gets the province data at the given province index. - CityDataFile::ProvinceData &getProvinceData(int index); - const CityDataFile::ProvinceData &getProvinceData(int index) const; + ArenaProvinceData &getProvinceData(int index); + const ArenaProvinceData &getProvinceData(int index) const; bool init(const char *filename); }; diff --git a/OpenTESArena/src/WorldMap/ArenaLocationUtils.cpp b/OpenTESArena/src/WorldMap/ArenaLocationUtils.cpp index 3860248c3..fab63be2c 100644 --- a/OpenTESArena/src/WorldMap/ArenaLocationUtils.cpp +++ b/OpenTESArena/src/WorldMap/ArenaLocationUtils.cpp @@ -130,7 +130,7 @@ int ArenaLocationUtils::getGlobalQuarter(const Int2 &globalPoint, const CityData Rect provinceRect; for (int i = 0; i < CityDataFile::PROVINCE_COUNT; i++) { - const CityDataFile::ProvinceData &province = cityData.getProvinceData(i); + const ArenaProvinceData &province = cityData.getProvinceData(i); const Rect &curProvinceRect = province.getGlobalRect(); if (curProvinceRect.containsInclusive(globalPoint)) @@ -247,14 +247,14 @@ int ArenaLocationUtils::getTravelDays(const Int2 &startGlobalPoint, const Int2 & return travelDays; } -uint32_t ArenaLocationUtils::getCitySeed(int localCityID, const CityDataFile::ProvinceData &province) +uint32_t ArenaLocationUtils::getCitySeed(int localCityID, const ArenaProvinceData &province) { const int locationID = ArenaLocationUtils::cityToLocationID(localCityID); const auto &location = province.getLocationData(locationID); return static_cast((location.x << 16) + location.y); } -uint32_t ArenaLocationUtils::getWildernessSeed(int localCityID, const CityDataFile::ProvinceData &province) +uint32_t ArenaLocationUtils::getWildernessSeed(int localCityID, const ArenaProvinceData &province) { const auto &location = province.getLocationData(ArenaLocationUtils::cityToLocationID(localCityID)); const std::string &locationName = location.name; @@ -283,10 +283,9 @@ uint32_t ArenaLocationUtils::getSkySeed(const Int2 &localPoint, int provinceID, return seed * provinceID; } -uint32_t ArenaLocationUtils::getDungeonSeed(int localDungeonID, int provinceID, - const CityDataFile::ProvinceData &province) +uint32_t ArenaLocationUtils::getDungeonSeed(int localDungeonID, int provinceID, const ArenaProvinceData &province) { - const auto &dungeon = [localDungeonID, &province]() + const ArenaLocationData &dungeon = [localDungeonID, &province]() { if (localDungeonID == 0) { @@ -300,7 +299,9 @@ uint32_t ArenaLocationUtils::getDungeonSeed(int localDungeonID, int provinceID, } else { - return province.randomDungeons.at(localDungeonID - 2); + const int randomDungeonIndex = localDungeonID - 2; + DebugAssertIndex(province.randomDungeons, randomDungeonIndex); + return province.randomDungeons[randomDungeonIndex]; } }(); @@ -308,20 +309,19 @@ uint32_t ArenaLocationUtils::getDungeonSeed(int localDungeonID, int provinceID, return (~Bytes::rol(seed, 5)) & 0xFFFFFFFF; } -uint32_t ArenaLocationUtils::getProvinceSeed(int provinceID, const CityDataFile::ProvinceData &province) +uint32_t ArenaLocationUtils::getProvinceSeed(int provinceID, const ArenaProvinceData &province) { const uint32_t provinceSeed = ((province.globalX << 16) + province.globalY) * provinceID; return provinceSeed; } -uint32_t ArenaLocationUtils::getWildernessDungeonSeed(int provinceID, - const CityDataFile::ProvinceData &province, int wildBlockX, int wildBlockY) +uint32_t ArenaLocationUtils::getWildernessDungeonSeed(int provinceID, const ArenaProvinceData &province, int wildBlockX, int wildBlockY) { const uint32_t provinceSeed = ArenaLocationUtils::getProvinceSeed(provinceID, province); return (provinceSeed + (((wildBlockY << 6) + wildBlockX) & 0xFFFF)) & 0xFFFFFFFF; } -bool ArenaLocationUtils::isRulerMale(int localCityID, const CityDataFile::ProvinceData &province) +bool ArenaLocationUtils::isRulerMale(int localCityID, const ArenaProvinceData &province) { const auto &location = province.getLocationData(localCityID); const Int2 localPoint(location.x, location.y); diff --git a/OpenTESArena/src/WorldMap/ArenaLocationUtils.h b/OpenTESArena/src/WorldMap/ArenaLocationUtils.h index 563d67672..690b94e88 100644 --- a/OpenTESArena/src/WorldMap/ArenaLocationUtils.h +++ b/OpenTESArena/src/WorldMap/ArenaLocationUtils.h @@ -77,10 +77,10 @@ namespace ArenaLocationUtils const BinaryAssetLibrary &binaryAssetLibrary); // Gets the 32-bit seed for a city in the given province. - uint32_t getCitySeed(int localCityID, const CityDataFile::ProvinceData &province); + uint32_t getCitySeed(int localCityID, const ArenaProvinceData &province); // Gets the 32-bit seed for a city's wilderness in the given province. - uint32_t getWildernessSeed(int localCityID, const CityDataFile::ProvinceData &province); + uint32_t getWildernessSeed(int localCityID, const ArenaProvinceData &province); // Gets the 32-bit seed for a city's ruler in the given province's map. This doesn't // require actual location data -- it can just be a place on the map. @@ -90,19 +90,17 @@ namespace ArenaLocationUtils uint32_t getSkySeed(const Int2 &localPoint, int provinceID, const Rect &provinceRect); // Gets the 32-bit seed for a dungeon, given a dungeon ID and province ID. - uint32_t getDungeonSeed(int localDungeonID, int provinceID, - const CityDataFile::ProvinceData &province); + uint32_t getDungeonSeed(int localDungeonID, int provinceID, const ArenaProvinceData &province); // Gets the 32-bit seed for a province. Used with wilderness dungeons. - uint32_t getProvinceSeed(int provinceID, const CityDataFile::ProvinceData &province); + uint32_t getProvinceSeed(int provinceID, const ArenaProvinceData &province); // Gets the 32-bit seed for a wilderness dungeon, given a province ID and X and Y // wilderness block coordinates. - uint32_t getWildernessDungeonSeed(int provinceID, - const CityDataFile::ProvinceData &province, int wildBlockX, int wildBlockY); + uint32_t getWildernessDungeonSeed(int provinceID, const ArenaProvinceData &province, int wildBlockX, int wildBlockY); // Gets whether the ruler of a city in the given province should be male. - bool isRulerMale(int localCityID, const CityDataFile::ProvinceData &province); + bool isRulerMale(int localCityID, const ArenaProvinceData &province); // Gets the number of .MIF templates to choose from for a city. int getCityTemplateCount(bool isCoastal, bool isCityState); diff --git a/OpenTESArena/src/WorldMap/LocationDefinition.cpp b/OpenTESArena/src/WorldMap/LocationDefinition.cpp index c2f065706..a683a4542 100644 --- a/OpenTESArena/src/WorldMap/LocationDefinition.cpp +++ b/OpenTESArena/src/WorldMap/LocationDefinition.cpp @@ -229,9 +229,7 @@ void LocationDefinition::initCity(int localCityID, int provinceID, bool coastal, rulerIsMale, palaceIsMainQuestDungeon); } -void LocationDefinition::initDungeon(int localDungeonID, int provinceID, - const CityDataFile::ProvinceData::LocationData &locationData, - const CityDataFile::ProvinceData &provinceData) +void LocationDefinition::initDungeon(int localDungeonID, int provinceID, const ArenaLocationData &locationData, const ArenaProvinceData &provinceData) { DebugAssert((localDungeonID >= 2) && (localDungeonID < 16)); diff --git a/OpenTESArena/src/WorldMap/LocationDefinition.h b/OpenTESArena/src/WorldMap/LocationDefinition.h index b8a3c1fa2..c64f115ff 100644 --- a/OpenTESArena/src/WorldMap/LocationDefinition.h +++ b/OpenTESArena/src/WorldMap/LocationDefinition.h @@ -121,9 +121,7 @@ class LocationDefinition // Initialize from original game data. void initCity(int localCityID, int provinceID, bool coastal, bool premade, ArenaTypes::CityType type, const BinaryAssetLibrary &binaryAssetLibrary); - void initDungeon(int localDungeonID, int provinceID, - const CityDataFile::ProvinceData::LocationData &locationData, - const CityDataFile::ProvinceData &provinceData); + void initDungeon(int localDungeonID, int provinceID, const ArenaLocationData &locationData, const ArenaProvinceData &provinceData); void initMainQuestDungeon(const std::optional &optLocalDungeonID, int provinceID, LocationMainQuestDungeonDefinitionType type, const BinaryAssetLibrary &binaryAssetLibrary); // @todo: eventually have init(const char *filename) for custom locations. diff --git a/OpenTESArena/src/WorldMap/ProvinceDefinition.cpp b/OpenTESArena/src/WorldMap/ProvinceDefinition.cpp index fd2324da3..b67d19eb4 100644 --- a/OpenTESArena/src/WorldMap/ProvinceDefinition.cpp +++ b/OpenTESArena/src/WorldMap/ProvinceDefinition.cpp @@ -20,7 +20,7 @@ void ProvinceDefinition::init(int provinceID, const BinaryAssetLibrary &binaryAs this->raceID = provinceID; this->animatedDistantLand = provinceID == 3; - auto canAddLocation = [](const CityDataFile::ProvinceData::LocationData &locationData) + auto canAddLocation = [](const ArenaLocationData &locationData) { // @todo: don't think this works for dungeons because they are renamed when set visible. //return locationData.name.size() > 0; @@ -40,8 +40,7 @@ void ProvinceDefinition::init(int provinceID, const BinaryAssetLibrary &binaryAs } }; - auto tryAddDungeon = [this, &provinceData, &canAddLocation](int localDungeonID, int provinceID, - const CityDataFile::ProvinceData::LocationData &locationData) + auto tryAddDungeon = [this, &provinceData, &canAddLocation](int localDungeonID, int provinceID, const ArenaLocationData &locationData) { if (canAddLocation(locationData)) { @@ -53,8 +52,7 @@ void ProvinceDefinition::init(int provinceID, const BinaryAssetLibrary &binaryAs auto tryAddMainQuestDungeon = [this, &binaryAssetLibrary, &provinceData, &canAddLocation]( const std::optional &optLocalDungeonID, int provinceID, - LocationMainQuestDungeonDefinitionType type, - const CityDataFile::ProvinceData::LocationData &locationData) + LocationMainQuestDungeonDefinitionType type, const ArenaLocationData &locationData) { if (canAddLocation(locationData)) { @@ -77,7 +75,7 @@ void ProvinceDefinition::init(int provinceID, const BinaryAssetLibrary &binaryAs cityGen.coastalCityList.end(), globalCityID) != cityGen.coastalCityList.end(); }; - for (size_t i = 0; i < locations.size(); i++) + for (size_t i = 0; i < std::size(locations); i++) { const auto &location = locations[i]; const int localCityID = startID + static_cast(i); @@ -89,7 +87,7 @@ void ProvinceDefinition::init(int provinceID, const BinaryAssetLibrary &binaryAs auto tryAddDungeons = [provinceID, &tryAddDungeon](const auto &locations) { - for (size_t i = 0; i < locations.size(); i++) + for (size_t i = 0; i < std::size(locations); i++) { const auto &location = locations[i]; @@ -99,22 +97,18 @@ void ProvinceDefinition::init(int provinceID, const BinaryAssetLibrary &binaryAs }; tryAddCities(provinceData.cityStates, ArenaTypes::CityType::CityState, 0); - tryAddCities(provinceData.towns, ArenaTypes::CityType::Town, - static_cast(provinceData.cityStates.size())); - tryAddCities(provinceData.villages, ArenaTypes::CityType::Village, - static_cast(provinceData.cityStates.size() + provinceData.towns.size())); + tryAddCities(provinceData.towns, ArenaTypes::CityType::Town, static_cast(std::size(provinceData.cityStates))); + tryAddCities(provinceData.villages, ArenaTypes::CityType::Village, static_cast(std::size(provinceData.cityStates) + std::size(provinceData.towns))); - tryAddMainQuestDungeon(0, provinceID, - LocationMainQuestDungeonDefinitionType::Staff, provinceData.secondDungeon); - tryAddMainQuestDungeon(1, provinceID, - LocationMainQuestDungeonDefinitionType::Map, provinceData.firstDungeon); + tryAddMainQuestDungeon(0, provinceID, LocationMainQuestDungeonDefinitionType::Staff, provinceData.secondDungeon); + tryAddMainQuestDungeon(1, provinceID, LocationMainQuestDungeonDefinitionType::Map, provinceData.firstDungeon); tryAddDungeons(provinceData.randomDungeons); const bool hasStartDungeon = isCenterProvince; if (hasStartDungeon) { - CityDataFile::ProvinceData::LocationData startDungeonLocation; + ArenaLocationData startDungeonLocation; startDungeonLocation.name = std::string(); startDungeonLocation.x = 0; startDungeonLocation.y = 0;