From 41b07b89043b2309805527a327ec2b3d5435c173 Mon Sep 17 00:00:00 2001 From: Aaron van Geffen Date: Mon, 3 Feb 2025 22:33:41 +0100 Subject: [PATCH] Implement climate object loading functions --- src/openrct2/object/ClimateObject.cpp | 120 ++++++++++++++++++++++++++ src/openrct2/object/ClimateObject.h | 4 + src/openrct2/world/Climate.h | 16 ++++ 3 files changed, 140 insertions(+) diff --git a/src/openrct2/object/ClimateObject.cpp b/src/openrct2/object/ClimateObject.cpp index 8622b03d7b..a4e521f246 100644 --- a/src/openrct2/object/ClimateObject.cpp +++ b/src/openrct2/object/ClimateObject.cpp @@ -9,12 +9,33 @@ #include "ClimateObject.h" +#include "../Diagnostic.h" #include "../core/Guard.hpp" #include "../core/IStream.hpp" #include "../core/Json.hpp" using namespace OpenRCT2; +struct RawClimateMonth +{ + int8_t baseTemperature; + int8_t randomBias; + uint8_t distribution[kNumWeatherTypes]{}; + int16_t distributionSum{}; +}; + +using RawClimate = std::array; + +static constexpr const char* kWeatherTypes[] = { + "sunny", "partiallyCloudy", "cloudy", "rain", "heavyRain", "thunder", "snow", "heavySnow", "blizzard", +}; +static_assert(std::size(kWeatherTypes) == kNumWeatherTypes); + +static constexpr const char* kMonthKeys[] = { + "march", "april", "may", "june", "july", "august", "september", "october", +}; +static_assert(std::size(kMonthKeys) == kNumClimateMonths); + void ClimateObject::Load() { } @@ -23,8 +44,107 @@ void ClimateObject::Unload() { } +static RawClimate readWeatherTable(json_t& weather); +static Climate convertRawClimate(const RawClimate& rawClimate); + void ClimateObject::ReadJson(IReadObjectContext* context, json_t& root) { Guard::Assert(root.is_object(), "ClimateObject::ReadJson expects parameter root to be an object"); PopulateTablesFromJson(context, root); + + Guard::Assert(root["weather"].is_object(), "ClimateObject::ReadJson expects weather key to be an object"); + auto rawClimate = readWeatherTable(root["weather"]); + _climate = convertRawClimate(rawClimate); +} + +static Climate convertRawClimate(const RawClimate& rawClimate) +{ + Climate climate{}; + + for (auto m = 0; m < kNumClimateMonths; m++) + { + auto& srcMonth = rawClimate[m]; + auto& dstMonth = climate[m]; + + dstMonth.baseTemperature = srcMonth.baseTemperature; + dstMonth.randomBias = srcMonth.randomBias; + + auto i = 0; + for (auto w = 0u; w < kNumWeatherTypes; w++) + { + if (i > kWeatherDistSize) + break; + + for (auto k = 0u; k < srcMonth.distribution[w]; k++) + { + dstMonth.distribution[i] = WeatherType(w); + i++; + } + } + } + + return climate; +} + +static RawClimate readWeatherTable(json_t& weather) +{ + // First read raw climate distribution from JSON + RawClimate rawClimate{}; + for (auto i = 0u; i < kNumClimateMonths; i++) + { + auto& monthKey = kMonthKeys[i]; + if (!weather.contains(monthKey)) + { + LOG_ERROR("Climate month not defined: %s", monthKey); + continue; + } + + auto& month = weather[monthKey]; + for (auto j = 0u; j < kNumWeatherTypes; j++) + { + Guard::Assert( + month["baseTemperature"].is_number(), + "ClimateObject::ReadJson expects weather months to contain baseTemperature"); + Guard::Assert( + month["randomBias"].is_number(), "ClimateObject::ReadJson expects weather months to contain randomBias"); + Guard::Assert( + month["distribution"].is_object(), + "ClimateObject::ReadJson expects weather months to contain distribution object"); + + auto& weatherKey = kWeatherTypes[j]; + if (!month["distribution"].contains(weatherKey)) + continue; + + auto weight = Json::GetNumber(month["distribution"][weatherKey]); + if (weight <= 0) + continue; + + rawClimate[i].baseTemperature = Json::GetNumber(month["baseTemperature"]); + rawClimate[i].randomBias = Json::GetNumber(month["randomBias"]); + rawClimate[i].distribution[j] = weight; + rawClimate[i].distributionSum += weight; + } + + Guard::Assert(rawClimate[i].distributionSum != 0, "Month %s has no weather defined!", monthKey); + } + + // Adjust distribution to fit internal format + for (auto& climateMonth : rawClimate) + { + auto adjustedDistSum = 0u; + for (auto i = 0u; i < kNumWeatherTypes; i++) + { + auto rawWeight = climateMonth.distribution[i]; + auto adjustedWeight = rawWeight * kWeatherDistSize / climateMonth.distributionSum; + + climateMonth.distribution[i] = adjustedWeight; + adjustedDistSum += adjustedWeight; + } + + Guard::Assert( + adjustedDistSum == kWeatherDistSize, "Weather distribution size mismatches: %d != %d", adjustedDistSum, + kWeatherDistSize); + } + + return rawClimate; } diff --git a/src/openrct2/object/ClimateObject.h b/src/openrct2/object/ClimateObject.h index bc2ebe9295..1e07f6fff2 100644 --- a/src/openrct2/object/ClimateObject.h +++ b/src/openrct2/object/ClimateObject.h @@ -9,12 +9,16 @@ #pragma once +#include "../world/Climate.h" #include "Object.h" struct IReadObjectContext; class ClimateObject final : public Object { +private: + Climate _climate; + public: static constexpr ObjectType kObjectType = ObjectType::climate; diff --git a/src/openrct2/world/Climate.h b/src/openrct2/world/Climate.h index f82cb0f747..292e7f348e 100644 --- a/src/openrct2/world/Climate.h +++ b/src/openrct2/world/Climate.h @@ -9,6 +9,9 @@ #pragma once +#include "../core/EnumUtils.hpp" + +#include #include // TODO: refactor usage and remove @@ -35,6 +38,19 @@ enum class WeatherType : uint8_t Count }; +static constexpr auto kNumWeatherTypes = EnumValue(WeatherType::Count); +static constexpr auto kNumClimateMonths = 8; +static constexpr auto kWeatherDistSize = 23; + +struct ClimateMonth +{ + int8_t baseTemperature; + int8_t randomBias; + WeatherType distribution[kWeatherDistSize]{}; +}; + +using Climate = std::array; + enum class WeatherEffectType : uint8_t { None,