From 4c51b4b82237f2f22d48045655e93a69acb0efee Mon Sep 17 00:00:00 2001 From: Magnus Wolffelt Date: Sat, 31 Jul 2021 12:57:23 +0200 Subject: [PATCH] Seasonal weather types per theater. Adjusts the weather conditions per theater and per season. --- game/theater/conflicttheater.py | 82 +++++-------------- game/theater/seasonalconditions/__init__.py | 1 + game/theater/seasonalconditions/caucasus.py | 36 ++++++++ .../seasonalconditions/marianaislands.py | 38 +++++++++ game/theater/seasonalconditions/nevada.py | 36 ++++++++ game/theater/seasonalconditions/normandy.py | 36 ++++++++ .../theater/seasonalconditions/persiangulf.py | 37 +++++++++ .../seasonalconditions/seasonalconditions.py | 48 +++++++++++ game/theater/seasonalconditions/syria.py | 36 ++++++++ game/theater/seasonalconditions/thechannel.py | 36 ++++++++ game/weather.py | 26 ++++-- 11 files changed, 346 insertions(+), 66 deletions(-) create mode 100644 game/theater/seasonalconditions/__init__.py create mode 100644 game/theater/seasonalconditions/caucasus.py create mode 100644 game/theater/seasonalconditions/marianaislands.py create mode 100644 game/theater/seasonalconditions/nevada.py create mode 100644 game/theater/seasonalconditions/normandy.py create mode 100644 game/theater/seasonalconditions/persiangulf.py create mode 100644 game/theater/seasonalconditions/seasonalconditions.py create mode 100644 game/theater/seasonalconditions/syria.py create mode 100644 game/theater/seasonalconditions/thechannel.py diff --git a/game/theater/conflicttheater.py b/game/theater/conflicttheater.py index 8e88bda2..bdae60e6 100644 --- a/game/theater/conflicttheater.py +++ b/game/theater/conflicttheater.py @@ -51,6 +51,7 @@ from .controlpoint import ( MissionTarget, OffMapSpawn, ) +from .seasonalconditions import SeasonalConditions from .frontline import FrontLine from .landmap import Landmap, load_landmap, poly_contains from .latlon import LatLon @@ -527,17 +528,6 @@ class ReferencePoint: image_coordinates: Point -@dataclass(frozen=True) -class SeasonalConditions: - # Units are inHg and degrees Celsius - # Future improvement: add clouds/precipitation - summer_avg_pressure: float - winter_avg_pressure: float - summer_avg_temperature: float - winter_avg_temperature: float - temperature_day_night_difference: float - - class ConflictTheater: terrain: Terrain @@ -799,13 +789,9 @@ class CaucasusTheater(ConflictTheater): @property def seasonal_conditions(self) -> SeasonalConditions: - return SeasonalConditions( - summer_avg_pressure=30.02, # TODO: More science - winter_avg_pressure=29.72, # TODO: More science - summer_avg_temperature=22.5, - winter_avg_temperature=3.0, - temperature_day_night_difference=6.0, - ) + from .seasonalconditions.caucasus import CONDITIONS + + return CONDITIONS @property def projection_parameters(self) -> TransverseMercator: @@ -831,13 +817,9 @@ class PersianGulfTheater(ConflictTheater): @property def seasonal_conditions(self) -> SeasonalConditions: - return SeasonalConditions( - summer_avg_pressure=29.98, # TODO: More science - winter_avg_pressure=29.80, # TODO: More science - summer_avg_temperature=32.5, - winter_avg_temperature=15.0, - temperature_day_night_difference=2.0, - ) + from .seasonalconditions.persiangulf import CONDITIONS + + return CONDITIONS @property def projection_parameters(self) -> TransverseMercator: @@ -863,13 +845,9 @@ class NevadaTheater(ConflictTheater): @property def seasonal_conditions(self) -> SeasonalConditions: - return SeasonalConditions( - summer_avg_pressure=30.02, # TODO: More science - winter_avg_pressure=29.72, # TODO: More science - summer_avg_temperature=31.5, - winter_avg_temperature=5.0, - temperature_day_night_difference=6.0, - ) + from .seasonalconditions.nevada import CONDITIONS + + return CONDITIONS @property def projection_parameters(self) -> TransverseMercator: @@ -895,13 +873,9 @@ class NormandyTheater(ConflictTheater): @property def seasonal_conditions(self) -> SeasonalConditions: - return SeasonalConditions( - summer_avg_pressure=30.02, # TODO: More science - winter_avg_pressure=29.72, # TODO: More science - summer_avg_temperature=20.0, - winter_avg_temperature=0.0, - temperature_day_night_difference=5.0, - ) + from .seasonalconditions.normandy import CONDITIONS + + return CONDITIONS @property def projection_parameters(self) -> TransverseMercator: @@ -927,13 +901,9 @@ class TheChannelTheater(ConflictTheater): @property def seasonal_conditions(self) -> SeasonalConditions: - return SeasonalConditions( - summer_avg_pressure=30.02, # TODO: More science - winter_avg_pressure=29.72, # TODO: More science - summer_avg_temperature=20.0, - winter_avg_temperature=0.0, - temperature_day_night_difference=5.0, - ) + from .seasonalconditions.thechannel import CONDITIONS + + return CONDITIONS @property def projection_parameters(self) -> TransverseMercator: @@ -959,13 +929,9 @@ class SyriaTheater(ConflictTheater): @property def seasonal_conditions(self) -> SeasonalConditions: - return SeasonalConditions( - summer_avg_pressure=29.98, # TODO: More science - winter_avg_pressure=29.86, # TODO: More science - summer_avg_temperature=28.5, - winter_avg_temperature=10.0, - temperature_day_night_difference=8.0, - ) + from .seasonalconditions.syria import CONDITIONS + + return CONDITIONS @property def projection_parameters(self) -> TransverseMercator: @@ -988,13 +954,9 @@ class MarianaIslandsTheater(ConflictTheater): @property def seasonal_conditions(self) -> SeasonalConditions: - return SeasonalConditions( - summer_avg_pressure=30.02, # TODO: More science - winter_avg_pressure=29.82, # TODO: More science - summer_avg_temperature=28.0, - winter_avg_temperature=27.0, - temperature_day_night_difference=1.0, - ) + from .seasonalconditions.marianaislands import CONDITIONS + + return CONDITIONS @property def projection_parameters(self) -> TransverseMercator: diff --git a/game/theater/seasonalconditions/__init__.py b/game/theater/seasonalconditions/__init__.py new file mode 100644 index 00000000..713a85f5 --- /dev/null +++ b/game/theater/seasonalconditions/__init__.py @@ -0,0 +1 @@ +from .seasonalconditions import * diff --git a/game/theater/seasonalconditions/caucasus.py b/game/theater/seasonalconditions/caucasus.py new file mode 100644 index 00000000..e605a543 --- /dev/null +++ b/game/theater/seasonalconditions/caucasus.py @@ -0,0 +1,36 @@ +from .seasonalconditions import SeasonalConditions, Season, WeatherTypeChances + +CONDITIONS = SeasonalConditions( + summer_avg_pressure=30.02, # TODO: Find real-world data + winter_avg_pressure=29.72, # TODO: Find real-world data + summer_avg_temperature=22.5, + winter_avg_temperature=3.0, + temperature_day_night_difference=6.0, + weather_type_chances={ + # TODO: Find real-world data for all these values + Season.Winter: WeatherTypeChances( + thunderstorm=1, + raining=20, + cloudy=60, + clear_skies=20, + ), + Season.Spring: WeatherTypeChances( + thunderstorm=1, + raining=20, + cloudy=40, + clear_skies=40, + ), + Season.Summer: WeatherTypeChances( + thunderstorm=1, + raining=10, + cloudy=30, + clear_skies=60, + ), + Season.Fall: WeatherTypeChances( + thunderstorm=1, + raining=30, + cloudy=50, + clear_skies=20, + ), + }, +) diff --git a/game/theater/seasonalconditions/marianaislands.py b/game/theater/seasonalconditions/marianaislands.py new file mode 100644 index 00000000..0d662908 --- /dev/null +++ b/game/theater/seasonalconditions/marianaislands.py @@ -0,0 +1,38 @@ +from .seasonalconditions import SeasonalConditions, Season, WeatherTypeChances + +CONDITIONS = SeasonalConditions( + summer_avg_pressure=30.02, # TODO: Find real-world data + winter_avg_pressure=29.82, # TODO: Find real-world data + summer_avg_temperature=28.0, + winter_avg_temperature=27.0, + temperature_day_night_difference=1.0, + weather_type_chances={ + # TODO: Find real-world data for all these values + Season.Winter: WeatherTypeChances( + thunderstorm=2, + raining=20, + cloudy=40, + clear_skies=40, + ), + Season.Spring: WeatherTypeChances( + # Spring is dry/sunny in Marianas + thunderstorm=1, + raining=10, + cloudy=30, + clear_skies=60, + ), + Season.Summer: WeatherTypeChances( + thunderstorm=2, + raining=20, + cloudy=40, + clear_skies=40, + ), + Season.Fall: WeatherTypeChances( + # Rain season + thunderstorm=5, + raining=45, + cloudy=30, + clear_skies=20, + ), + }, +) diff --git a/game/theater/seasonalconditions/nevada.py b/game/theater/seasonalconditions/nevada.py new file mode 100644 index 00000000..352ca456 --- /dev/null +++ b/game/theater/seasonalconditions/nevada.py @@ -0,0 +1,36 @@ +from .seasonalconditions import SeasonalConditions, Season, WeatherTypeChances + +CONDITIONS = SeasonalConditions( + summer_avg_pressure=30.02, # TODO: Find real-world data + winter_avg_pressure=29.72, # TODO: Find real-world data + summer_avg_temperature=31.5, + winter_avg_temperature=5.0, + temperature_day_night_difference=6.0, + weather_type_chances={ + # TODO: Find real-world data for all these values + Season.Winter: WeatherTypeChances( + thunderstorm=1, + raining=10, + cloudy=50, + clear_skies=40, + ), + Season.Spring: WeatherTypeChances( + thunderstorm=1, + raining=5, + cloudy=45, + clear_skies=50, + ), + Season.Summer: WeatherTypeChances( + thunderstorm=1, + raining=5, + cloudy=25, + clear_skies=70, + ), + Season.Fall: WeatherTypeChances( + thunderstorm=1, + raining=10, + cloudy=45, + clear_skies=45, + ), + }, +) diff --git a/game/theater/seasonalconditions/normandy.py b/game/theater/seasonalconditions/normandy.py new file mode 100644 index 00000000..109c781f --- /dev/null +++ b/game/theater/seasonalconditions/normandy.py @@ -0,0 +1,36 @@ +from .seasonalconditions import SeasonalConditions, Season, WeatherTypeChances + +CONDITIONS = SeasonalConditions( + summer_avg_pressure=30.02, # TODO: Find real-world data + winter_avg_pressure=29.72, # TODO: Find real-world data + summer_avg_temperature=20.0, + winter_avg_temperature=0.0, + temperature_day_night_difference=5.0, + weather_type_chances={ + # TODO: Find real-world data for all these values + Season.Winter: WeatherTypeChances( + thunderstorm=1, + raining=20, + cloudy=60, + clear_skies=20, + ), + Season.Spring: WeatherTypeChances( + thunderstorm=1, + raining=20, + cloudy=40, + clear_skies=40, + ), + Season.Summer: WeatherTypeChances( + thunderstorm=1, + raining=10, + cloudy=30, + clear_skies=60, + ), + Season.Fall: WeatherTypeChances( + thunderstorm=1, + raining=30, + cloudy=50, + clear_skies=20, + ), + }, +) diff --git a/game/theater/seasonalconditions/persiangulf.py b/game/theater/seasonalconditions/persiangulf.py new file mode 100644 index 00000000..467168ab --- /dev/null +++ b/game/theater/seasonalconditions/persiangulf.py @@ -0,0 +1,37 @@ +from .seasonalconditions import SeasonalConditions, Season, WeatherTypeChances + +CONDITIONS = SeasonalConditions( + summer_avg_pressure=29.98, # TODO: Find real-world data + winter_avg_pressure=29.80, # TODO: Find real-world data + summer_avg_temperature=32.5, + winter_avg_temperature=15.0, + temperature_day_night_difference=2.0, + weather_type_chances={ + # TODO: Find real-world data for all these values + Season.Winter: WeatherTypeChances( + # Winter there is some rain in PG (Dubai) + thunderstorm=1, + raining=15, + cloudy=35, + clear_skies=50, + ), + Season.Spring: WeatherTypeChances( + thunderstorm=1, + raining=2, + cloudy=18, + clear_skies=80, + ), + Season.Summer: WeatherTypeChances( + thunderstorm=1, + raining=1, + cloudy=8, + clear_skies=90, + ), + Season.Fall: WeatherTypeChances( + thunderstorm=1, + raining=2, + cloudy=18, + clear_skies=80, + ), + }, +) diff --git a/game/theater/seasonalconditions/seasonalconditions.py b/game/theater/seasonalconditions/seasonalconditions.py new file mode 100644 index 00000000..2280d15e --- /dev/null +++ b/game/theater/seasonalconditions/seasonalconditions.py @@ -0,0 +1,48 @@ +import datetime +from dataclasses import dataclass +from enum import Enum + + +class Season(Enum): + Winter = "winter" + Spring = "spring" + Summer = "summer" + Fall = "fall" + + +def determine_season(day: datetime.date) -> Season: + # Note: This logic doesn't need to be very precise + # Currently refers strictly to northern-hemisphere seasons + day_of_year = day.timetuple().tm_yday + season_length = 365.0 / 4 + winter_end_day = season_length / 2 + if day_of_year < winter_end_day: + return Season.Winter + elif day_of_year < winter_end_day + season_length: + return Season.Spring + elif day_of_year < winter_end_day + season_length * 2: + return Season.Summer + elif day_of_year < winter_end_day + season_length * 3: + return Season.Fall + else: + return Season.Winter + + +@dataclass(frozen=True) +class WeatherTypeChances: + thunderstorm: float + raining: float + cloudy: float + clear_skies: float + + +@dataclass(frozen=True) +class SeasonalConditions: + # Units are inHg and degrees Celsius + summer_avg_pressure: float + winter_avg_pressure: float + summer_avg_temperature: float + winter_avg_temperature: float + temperature_day_night_difference: float + + weather_type_chances: dict[Season, WeatherTypeChances] diff --git a/game/theater/seasonalconditions/syria.py b/game/theater/seasonalconditions/syria.py new file mode 100644 index 00000000..0a6c7ec1 --- /dev/null +++ b/game/theater/seasonalconditions/syria.py @@ -0,0 +1,36 @@ +from .seasonalconditions import SeasonalConditions, Season, WeatherTypeChances + +CONDITIONS = SeasonalConditions( + summer_avg_pressure=29.98, # TODO: Find real-world data + winter_avg_pressure=29.86, # TODO: Find real-world data + summer_avg_temperature=28.5, + winter_avg_temperature=10.0, + temperature_day_night_difference=8.0, + weather_type_chances={ + # TODO: Find real-world data for all these values + Season.Winter: WeatherTypeChances( + thunderstorm=1, + raining=25, + cloudy=25, + clear_skies=50, + ), + Season.Spring: WeatherTypeChances( + thunderstorm=1, + raining=10, + cloudy=30, + clear_skies=60, + ), + Season.Summer: WeatherTypeChances( + thunderstorm=1, + raining=3, + cloudy=20, + clear_skies=77, + ), + Season.Fall: WeatherTypeChances( + thunderstorm=1, + raining=10, + cloudy=30, + clear_skies=60, + ), + }, +) diff --git a/game/theater/seasonalconditions/thechannel.py b/game/theater/seasonalconditions/thechannel.py new file mode 100644 index 00000000..109c781f --- /dev/null +++ b/game/theater/seasonalconditions/thechannel.py @@ -0,0 +1,36 @@ +from .seasonalconditions import SeasonalConditions, Season, WeatherTypeChances + +CONDITIONS = SeasonalConditions( + summer_avg_pressure=30.02, # TODO: Find real-world data + winter_avg_pressure=29.72, # TODO: Find real-world data + summer_avg_temperature=20.0, + winter_avg_temperature=0.0, + temperature_day_night_difference=5.0, + weather_type_chances={ + # TODO: Find real-world data for all these values + Season.Winter: WeatherTypeChances( + thunderstorm=1, + raining=20, + cloudy=60, + clear_skies=20, + ), + Season.Spring: WeatherTypeChances( + thunderstorm=1, + raining=20, + cloudy=40, + clear_skies=40, + ), + Season.Summer: WeatherTypeChances( + thunderstorm=1, + raining=10, + cloudy=30, + clear_skies=60, + ), + Season.Fall: WeatherTypeChances( + thunderstorm=1, + raining=30, + cloudy=50, + clear_skies=20, + ), + }, +) diff --git a/game/weather.py b/game/weather.py index 2594ed91..fb0ea68c 100644 --- a/game/weather.py +++ b/game/weather.py @@ -14,9 +14,11 @@ from game.savecompat import has_save_compat_for from game.settings import Settings from game.utils import Distance, Heading, meters, interpolate, Pressure, inches_hg +from game.theater.seasonalconditions import determine_season + if TYPE_CHECKING: from game.theater import ConflictTheater - from game.theater.conflicttheater import SeasonalConditions + from game.theater.seasonalconditions import SeasonalConditions class TimeOfDay(Enum): @@ -119,10 +121,18 @@ class Weather: temperature -= seasonal_conditions.temperature_day_night_difference / 2 pressure += self.pressure_adjustment temperature += self.temperature_adjustment + logging.debug( + "Weather: Before random: temp {} press {}".format(temperature, pressure) + ) conditions = AtmosphericConditions( qnh=self.random_pressure(pressure), temperature_celsius=self.random_temperature(temperature), ) + logging.debug( + "Weather: After random: temp {} press {}".format( + conditions.temperature_celsius, conditions.qnh.pressure_in_inches_hg + ) + ) return conditions @property @@ -334,14 +344,18 @@ class Conditions: day: datetime.date, time_of_day: TimeOfDay, ) -> Weather: - # Future improvement: use seasonal weights for theaters + season = determine_season(day) + logging.debug("Weather: Season {}".format(season)) + weather_chances = seasonal_conditions.weather_type_chances[season] chances = { - Thunderstorm: 1, - Raining: 20, - Cloudy: 60, - ClearSkies: 20, + Thunderstorm: weather_chances.thunderstorm, + Raining: weather_chances.raining, + Cloudy: weather_chances.cloudy, + ClearSkies: weather_chances.clear_skies, } + logging.debug("Weather: Chances {}".format(weather_chances)) weather_type = random.choices( list(chances.keys()), weights=list(chances.values()) )[0] + logging.debug("Weather: Type {}".format(weather_type)) return weather_type(seasonal_conditions, day, time_of_day)