diff --git a/src/openrct2/world/Climate.cpp b/src/openrct2/world/Climate.cpp index c4e522bcc2..e997a2d137 100644 --- a/src/openrct2/world/Climate.cpp +++ b/src/openrct2/world/Climate.cpp @@ -33,24 +33,34 @@ using namespace OpenRCT2; using namespace OpenRCT2::Audio; -constexpr int32_t MAX_THUNDER_INSTANCES = 2; +constexpr int32_t kMaxThunderInstances = 2; -enum class THUNDER_STATUS +enum class ThunderStatus { - NONE, - PLAYING, + none, + playing, }; -struct WeatherTransition +struct WeatherPattern { - int8_t BaseTemperature; - int8_t DistributionSize; - WeatherType Distribution[24]; + int8_t baseTemperature; + int8_t randomBias; + WeatherType distribution[24]; }; -extern const WeatherTransition* ClimateTransitions[4]; -extern const WeatherState ClimateWeatherData[EnumValue(WeatherType::Count)]; -extern const FilterPaletteID ClimateWeatherGloomColours[4]; +struct WeatherTrait +{ + int8_t temperatureDelta; + WeatherEffectType effectLevel; + int8_t gloomLevel; + WeatherLevel level; + uint32_t spriteId; +}; + +// TODO: no need for these to be declared extern, just move the definitions up +extern const WeatherPattern* kClimatePatterns[4]; +extern const WeatherTrait kClimateWeatherTraits[EnumValue(WeatherType::Count)]; +extern const FilterPaletteID kClimateWeatherGloomColours[4]; // Climate data uint16_t gClimateLightningFlash; @@ -59,10 +69,10 @@ uint16_t gClimateLightningFlash; static int32_t _weatherVolume = 1; static uint32_t _lightningTimer; static uint32_t _thunderTimer; -static std::shared_ptr _thunderSoundChannels[MAX_THUNDER_INSTANCES]; -static THUNDER_STATUS _thunderStatus[MAX_THUNDER_INSTANCES] = { - THUNDER_STATUS::NONE, - THUNDER_STATUS::NONE, +static std::shared_ptr _thunderSoundChannels[kMaxThunderInstances]; +static ThunderStatus _thunderStatus[kMaxThunderInstances] = { + ThunderStatus::none, + ThunderStatus::none, }; static OpenRCT2::Audio::SoundId _thunderSoundId; static int32_t _thunderVolume; @@ -70,7 +80,7 @@ static int32_t _thunderStereoEcho = 0; static std::shared_ptr _weatherSoundChannel; static int8_t ClimateStepWeatherLevel(int8_t currentWeatherLevel, int8_t nextWeatherLevel); -static void ClimateDetermineFutureWeather(int32_t randomDistribution); +static void ClimateDetermineFutureWeather(int32_t randomValue); static void ClimateUpdateWeatherSound(); static void ClimateUpdateThunderSound(); static void ClimateUpdateLightning(); @@ -87,18 +97,19 @@ int32_t ClimateCelsiusToFahrenheit(int32_t celsius) */ void ClimateReset(ClimateType climate) { - auto& gameState = GetGameState(); auto weather = WeatherType::PartiallyCloudy; int32_t month = GetDate().GetMonth(); - const WeatherTransition* transition = &ClimateTransitions[EnumValue(climate)][month]; - const WeatherState* weatherState = &ClimateWeatherData[EnumValue(weather)]; + const WeatherPattern& pattern = kClimatePatterns[EnumValue(climate)][month]; + const WeatherTrait& trait = kClimateWeatherTraits[EnumValue(weather)]; + + auto& gameState = GetGameState(); gameState.Climate = climate; gameState.ClimateCurrent.Weather = weather; - gameState.ClimateCurrent.Temperature = transition->BaseTemperature + weatherState->TemperatureDelta; - gameState.ClimateCurrent.WeatherEffect = weatherState->EffectLevel; - gameState.ClimateCurrent.WeatherGloom = weatherState->GloomLevel; - gameState.ClimateCurrent.Level = weatherState->Level; + gameState.ClimateCurrent.Temperature = pattern.baseTemperature + trait.temperatureDelta; + gameState.ClimateCurrent.WeatherEffect = trait.effectLevel; + gameState.ClimateCurrent.WeatherGloom = trait.gloomLevel; + gameState.ClimateCurrent.Level = trait.level; _lightningTimer = 0; _thunderTimer = 0; @@ -201,14 +212,15 @@ void ClimateForceWeather(WeatherType weather) { auto& gameState = GetGameState(); int32_t month = GetDate().GetMonth(); - const WeatherTransition* transition = &ClimateTransitions[EnumValue(gameState.Climate)][month]; - const auto weatherState = &ClimateWeatherData[EnumValue(weather)]; + + const auto& pattern = kClimatePatterns[EnumValue(gameState.Climate)][month]; + const auto& trait = kClimateWeatherTraits[EnumValue(weather)]; gameState.ClimateCurrent.Weather = weather; - gameState.ClimateCurrent.WeatherGloom = weatherState->GloomLevel; - gameState.ClimateCurrent.Level = weatherState->Level; - gameState.ClimateCurrent.WeatherEffect = weatherState->EffectLevel; - gameState.ClimateCurrent.Temperature = transition->BaseTemperature + weatherState->TemperatureDelta; + gameState.ClimateCurrent.WeatherGloom = trait.gloomLevel; + gameState.ClimateCurrent.Level = trait.level; + gameState.ClimateCurrent.WeatherEffect = trait.effectLevel; + gameState.ClimateCurrent.Temperature = pattern.baseTemperature + trait.temperatureDelta; gameState.ClimateUpdateTimer = 1920; ClimateUpdate(); @@ -258,9 +270,9 @@ FilterPaletteID ClimateGetWeatherGloomPaletteId(const ClimateState& state) { auto paletteId = FilterPaletteID::PaletteNull; auto gloom = state.WeatherGloom; - if (gloom < std::size(ClimateWeatherGloomColours)) + if (gloom < std::size(kClimateWeatherGloomColours)) { - paletteId = ClimateWeatherGloomColours[gloom]; + paletteId = kClimateWeatherGloomColours[gloom]; } return paletteId; } @@ -268,9 +280,9 @@ FilterPaletteID ClimateGetWeatherGloomPaletteId(const ClimateState& state) uint32_t ClimateGetWeatherSpriteId(const ClimateState& state) { uint32_t spriteId = SPR_WEATHER_SUN; - if (EnumValue(state.Weather) < std::size(ClimateWeatherData)) + if (EnumValue(state.Weather) < std::size(kClimateWeatherTraits)) { - spriteId = ClimateWeatherData[EnumValue(state.Weather)].SpriteId; + spriteId = kClimateWeatherTraits[EnumValue(state.Weather)].spriteId; } return spriteId; } @@ -291,22 +303,23 @@ static int8_t ClimateStepWeatherLevel(int8_t currentWeatherLevel, int8_t nextWea * for nextWeather. The other weather parameters are then looked up depending only on the * next weather. */ -static void ClimateDetermineFutureWeather(int32_t randomDistribution) +static void ClimateDetermineFutureWeather(int32_t randomValue) { int32_t month = GetDate().GetMonth(); auto& gameState = GetGameState(); - // Generate a random variable with values 0 up to DistributionSize-1 and chose weather from the distribution table - // accordingly - const WeatherTransition* transition = &ClimateTransitions[EnumValue(gameState.Climate)][month]; - WeatherType nextWeather = (transition->Distribution[((randomDistribution & 0xFF) * transition->DistributionSize) >> 8]); + // Generate a random index with values 0 up to randomBias-1 + // and choose weather from the distribution table accordingly + const auto& pattern = kClimatePatterns[EnumValue(gameState.Climate)][month]; + auto randomIndex = ((randomValue % 256) * pattern.randomBias) / 256; + auto nextWeather = pattern.distribution[randomIndex]; gameState.ClimateNext.Weather = nextWeather; - const auto nextWeatherState = &ClimateWeatherData[EnumValue(nextWeather)]; - gameState.ClimateNext.Temperature = transition->BaseTemperature + nextWeatherState->TemperatureDelta; - gameState.ClimateNext.WeatherEffect = nextWeatherState->EffectLevel; - gameState.ClimateNext.WeatherGloom = nextWeatherState->GloomLevel; - gameState.ClimateNext.Level = nextWeatherState->Level; + const auto& nextWeatherTrait = kClimateWeatherTraits[EnumValue(nextWeather)]; + gameState.ClimateNext.Temperature = pattern.baseTemperature + nextWeatherTrait.temperatureDelta; + gameState.ClimateNext.WeatherEffect = nextWeatherTrait.effectLevel; + gameState.ClimateNext.WeatherGloom = nextWeatherTrait.gloomLevel; + gameState.ClimateNext.Level = nextWeatherTrait.level; gameState.ClimateUpdateTimer = 1920; } @@ -373,15 +386,15 @@ static void ClimateUpdateThunderSound() } // Stop thunder sounds if they have finished - for (int32_t i = 0; i < MAX_THUNDER_INSTANCES; i++) + for (int32_t i = 0; i < kMaxThunderInstances; i++) { - if (_thunderStatus[i] != THUNDER_STATUS::NONE) + if (_thunderStatus[i] != ThunderStatus::none) { auto& channel = _thunderSoundChannels[i]; if (!channel->IsPlaying()) { channel->Stop(); - _thunderStatus[i] = THUNDER_STATUS::NONE; + _thunderStatus[i] = ThunderStatus::none; } } } @@ -414,7 +427,7 @@ static void ClimateUpdateThunder() uint32_t randomNumber = UtilRand(); if (randomNumber & 0x10000) { - if (_thunderStatus[0] == THUNDER_STATUS::NONE && _thunderStatus[1] == THUNDER_STATUS::NONE) + if (_thunderStatus[0] == ThunderStatus::none && _thunderStatus[1] == ThunderStatus::none) { // Play thunder on left side _thunderSoundId = (randomNumber & 0x20000) ? OpenRCT2::Audio::SoundId::Thunder1 @@ -428,7 +441,7 @@ static void ClimateUpdateThunder() } else { - if (_thunderStatus[0] == THUNDER_STATUS::NONE) + if (_thunderStatus[0] == ThunderStatus::none) { _thunderSoundId = (randomNumber & 0x20000) ? OpenRCT2::Audio::SoundId::Thunder1 : OpenRCT2::Audio::SoundId::Thunder2; @@ -444,31 +457,32 @@ static void ClimatePlayThunder(int32_t instanceIndex, OpenRCT2::Audio::SoundId s _thunderSoundChannels[instanceIndex] = CreateAudioChannel(soundId, false, DStoMixerVolume(volume), DStoMixerPan(pan)); if (_thunderSoundChannels[instanceIndex] != nullptr) { - _thunderStatus[instanceIndex] = THUNDER_STATUS::PLAYING; + _thunderStatus[instanceIndex] = ThunderStatus::playing; } } #pragma region Climate / Weather data tables -const FilterPaletteID ClimateWeatherGloomColours[4] = { +const FilterPaletteID kClimateWeatherGloomColours[4] = { FilterPaletteID::PaletteNull, FilterPaletteID::PaletteDarken1, FilterPaletteID::PaletteDarken2, FilterPaletteID::PaletteDarken3, }; -// There is actually a sprite at 0x5A9C for snow but only these weather types seem to be fully implemented -const WeatherState ClimateWeatherData[EnumValue(WeatherType::Count)] = { - { 10, WeatherEffectType::None, 0, WeatherLevel::None, SPR_WEATHER_SUN }, // Sunny - { 5, WeatherEffectType::None, 0, WeatherLevel::None, SPR_WEATHER_SUN_CLOUD }, // Partially Cloudy - { 0, WeatherEffectType::None, 0, WeatherLevel::None, SPR_WEATHER_CLOUD }, // Cloudy - { -2, WeatherEffectType::Rain, 1, WeatherLevel::Light, SPR_WEATHER_LIGHT_RAIN }, // Rain - { -4, WeatherEffectType::Rain, 2, WeatherLevel::Heavy, SPR_WEATHER_HEAVY_RAIN }, // Heavy Rain - { 2, WeatherEffectType::Storm, 2, WeatherLevel::Heavy, SPR_WEATHER_STORM }, // Thunderstorm - { -10, WeatherEffectType::Snow, 1, WeatherLevel::Light, SPR_WEATHER_SNOW }, // Snow - { -15, WeatherEffectType::Snow, 2, WeatherLevel::Heavy, SPR_WEATHER_SNOW }, // Heavy Snow - { -20, WeatherEffectType::Blizzard, 2, WeatherLevel::Heavy, SPR_WEATHER_SNOW }, // Blizzard +// clang-format off +const WeatherTrait kClimateWeatherTraits[EnumValue(WeatherType::Count)] = { + { 10, WeatherEffectType::None, 0, WeatherLevel::None, SPR_WEATHER_SUN }, // Sunny + { 5, WeatherEffectType::None, 0, WeatherLevel::None, SPR_WEATHER_SUN_CLOUD }, // Partially Cloudy + { 0, WeatherEffectType::None, 0, WeatherLevel::None, SPR_WEATHER_CLOUD }, // Cloudy + { -2, WeatherEffectType::Rain, 1, WeatherLevel::Light, SPR_WEATHER_LIGHT_RAIN }, // Rain + { -4, WeatherEffectType::Rain, 2, WeatherLevel::Heavy, SPR_WEATHER_HEAVY_RAIN }, // Heavy Rain + { 2, WeatherEffectType::Storm, 2, WeatherLevel::Heavy, SPR_WEATHER_STORM }, // Thunderstorm + { -10, WeatherEffectType::Snow, 1, WeatherLevel::Light, SPR_WEATHER_SNOW }, // Snow + { -15, WeatherEffectType::Snow, 2, WeatherLevel::Heavy, SPR_WEATHER_SNOW }, // Heavy Snow + { -20, WeatherEffectType::Blizzard, 2, WeatherLevel::Heavy, SPR_WEATHER_SNOW }, // Blizzard }; +// clang-format on constexpr auto S = WeatherType::Sunny; constexpr auto P = WeatherType::PartiallyCloudy; @@ -477,7 +491,7 @@ constexpr auto R = WeatherType::Rain; constexpr auto H = WeatherType::HeavyRain; constexpr auto T = WeatherType::Thunder; -static constexpr WeatherTransition ClimateTransitionsCoolAndWet[] = { +static constexpr WeatherPattern kClimatePatternsCoolAndWet[] = { { 8, 18, { S, P, P, P, P, P, C, C, C, C, C, C, C, R, R, R, H, H, S, S, S, S, S } }, { 10, 21, { P, P, P, P, P, C, C, C, C, C, C, C, C, C, R, R, R, H, H, H, T, S, S } }, { 14, 17, { S, S, S, P, P, P, P, P, P, C, C, C, C, R, R, R, H, S, S, S, S, S, S } }, @@ -487,7 +501,7 @@ static constexpr WeatherTransition ClimateTransitionsCoolAndWet[] = { { 16, 19, { S, S, S, P, P, P, P, P, C, C, C, C, C, C, R, R, H, H, T, S, S, S, S } }, { 13, 16, { S, S, P, P, P, P, C, C, C, C, C, C, R, R, H, T, S, S, S, S, S, S, S } }, }; -static constexpr WeatherTransition ClimateTransitionsWarm[] = { +static constexpr WeatherPattern kClimatePatternsWarm[] = { { 12, 21, { S, S, S, S, S, P, P, P, P, P, P, P, P, C, C, C, C, C, C, C, H, S, S } }, { 13, 22, { S, S, S, S, S, P, P, P, P, P, P, C, C, C, C, C, C, C, C, C, R, T, S } }, { 16, 17, { S, S, S, S, S, S, P, P, P, P, P, P, C, C, C, C, R, S, S, S, S, S, S } }, @@ -497,7 +511,7 @@ static constexpr WeatherTransition ClimateTransitionsWarm[] = { { 19, 17, { S, S, S, S, S, P, P, P, P, P, C, C, C, C, C, C, R, S, S, S, S, S, S } }, { 16, 17, { S, S, P, P, P, P, P, C, C, C, C, C, C, C, C, C, H, S, S, S, S, S, S } }, }; -static constexpr WeatherTransition ClimateTransitionsHotAndDry[] = { +static constexpr WeatherPattern kClimatePatternsHotAndDry[] = { { 12, 15, { S, S, S, S, P, P, P, P, P, P, P, P, C, C, R, S, S, S, S, S, S, S, S } }, { 14, 12, { S, S, S, S, S, P, P, P, P, P, C, C, S, S, S, S, S, S, S, S, S, S, S } }, { 16, 11, { S, S, S, S, S, S, P, P, P, P, C, S, S, S, S, S, S, S, S, S, S, S, S } }, @@ -507,7 +521,7 @@ static constexpr WeatherTransition ClimateTransitionsHotAndDry[] = { { 21, 12, { S, S, S, S, S, S, S, P, P, P, C, T, S, S, S, S, S, S, S, S, S, S, S } }, { 16, 13, { S, S, S, S, S, S, S, S, P, P, P, C, R, S, S, S, S, S, S, S, S, S, S } }, }; -static constexpr WeatherTransition ClimateTransitionsCold[] = { +static constexpr WeatherPattern kClimatePatternsCold[] = { { 4, 18, { S, S, S, S, P, P, P, P, P, C, C, C, C, C, C, C, R, H, S, S, S, S, S } }, { 5, 21, { S, S, S, S, P, P, P, P, P, C, C, C, C, C, C, C, C, C, R, H, T, S, S } }, { 7, 17, { S, S, S, S, P, P, P, P, P, P, P, C, C, C, C, R, H, S, S, S, S, S, S } }, @@ -518,11 +532,11 @@ static constexpr WeatherTransition ClimateTransitionsCold[] = { { 6, 16, { S, S, P, P, P, P, C, C, C, C, C, C, R, R, H, T, S, S, S, S, S, S, S } }, }; -const WeatherTransition* ClimateTransitions[] = { - ClimateTransitionsCoolAndWet, - ClimateTransitionsWarm, - ClimateTransitionsHotAndDry, - ClimateTransitionsCold, +const WeatherPattern* kClimatePatterns[] = { + kClimatePatternsCoolAndWet, + kClimatePatternsWarm, + kClimatePatternsHotAndDry, + kClimatePatternsCold, }; #pragma endregion diff --git a/src/openrct2/world/Climate.h b/src/openrct2/world/Climate.h index 04540673bd..c31e6ad0bf 100644 --- a/src/openrct2/world/Climate.h +++ b/src/openrct2/world/Climate.h @@ -50,15 +50,6 @@ enum class WeatherLevel Heavy, }; -struct WeatherState -{ - int8_t TemperatureDelta; - WeatherEffectType EffectLevel; - int8_t GloomLevel; - WeatherLevel Level; - uint32_t SpriteId; -}; - struct ClimateState { WeatherType Weather;