Skip to content

Commit

Permalink
Cleaned up CityDataFile.
Browse files Browse the repository at this point in the history
  • Loading branch information
afritz1 committed Jan 10, 2025
1 parent 1d0add2 commit 9ee72b1
Show file tree
Hide file tree
Showing 7 changed files with 72 additions and 85 deletions.
17 changes: 8 additions & 9 deletions OpenTESArena/src/Assets/CityDataFile.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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)
{
Expand Down Expand Up @@ -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];
Expand All @@ -93,7 +93,7 @@ bool CityDataFile::init(const char *filename)
const uint8_t *srcPtr = reinterpret_cast<const uint8_t*>(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];

Expand All @@ -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<const char*>(locationPtr);
locationData.name = std::string(locationNamePtr);
Expand Down
70 changes: 35 additions & 35 deletions OpenTESArena/src/Assets/CityDataFile.h
Original file line number Diff line number Diff line change
@@ -1,58 +1,58 @@
#ifndef CITY_DATA_FILE_H
#define CITY_DATA_FILE_H

#include <array>
#include <cstdint>
#include <string>

#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
// save-specific modifications (i.e., to save random dungeon names).
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<LocationData, 8> cityStates;
std::array<LocationData, 8> towns;
std::array<LocationData, 16> villages;
LocationData secondDungeon; // Staff dungeon.
LocationData firstDungeon; // Staff map dungeon.
std::array<LocationData, 14> 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<ProvinceData, PROVINCE_COUNT> 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);
};
Expand Down
22 changes: 11 additions & 11 deletions OpenTESArena/src/WorldMap/ArenaLocationUtils.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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))
Expand Down Expand Up @@ -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<uint32_t>((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;
Expand Down Expand Up @@ -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)
{
Expand All @@ -300,28 +299,29 @@ 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];
}
}();

const uint32_t seed = (dungeon.y << 16) + dungeon.x + 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);
Expand Down
14 changes: 6 additions & 8 deletions OpenTESArena/src/WorldMap/ArenaLocationUtils.h
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand All @@ -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);
Expand Down
4 changes: 1 addition & 3 deletions OpenTESArena/src/WorldMap/LocationDefinition.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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));

Expand Down
4 changes: 1 addition & 3 deletions OpenTESArena/src/WorldMap/LocationDefinition.h
Original file line number Diff line number Diff line change
Expand Up @@ -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<int> &optLocalDungeonID, int provinceID,
LocationMainQuestDungeonDefinitionType type, const BinaryAssetLibrary &binaryAssetLibrary);
// @todo: eventually have init(const char *filename) for custom locations.
Expand Down
26 changes: 10 additions & 16 deletions OpenTESArena/src/WorldMap/ProvinceDefinition.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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))
{
Expand All @@ -53,8 +52,7 @@ void ProvinceDefinition::init(int provinceID, const BinaryAssetLibrary &binaryAs

auto tryAddMainQuestDungeon = [this, &binaryAssetLibrary, &provinceData, &canAddLocation](
const std::optional<int> &optLocalDungeonID, int provinceID,
LocationMainQuestDungeonDefinitionType type,
const CityDataFile::ProvinceData::LocationData &locationData)
LocationMainQuestDungeonDefinitionType type, const ArenaLocationData &locationData)
{
if (canAddLocation(locationData))
{
Expand All @@ -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<int>(i);
Expand All @@ -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];

Expand All @@ -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<int>(provinceData.cityStates.size()));
tryAddCities(provinceData.villages, ArenaTypes::CityType::Village,
static_cast<int>(provinceData.cityStates.size() + provinceData.towns.size()));
tryAddCities(provinceData.towns, ArenaTypes::CityType::Town, static_cast<int>(std::size(provinceData.cityStates)));
tryAddCities(provinceData.villages, ArenaTypes::CityType::Village, static_cast<int>(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;
Expand Down

0 comments on commit 9ee72b1

Please sign in to comment.