Add situational temperature and pressure variation.

Now varies by:

* Season
* Theater
* Weather
* Time of day
This commit is contained in:
Magnus Wolffelt 2021-07-16 23:08:14 +02:00 committed by GitHub
parent e5c0fc92ec
commit 04a346678c
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 192 additions and 29 deletions

View File

@ -497,6 +497,17 @@ 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
@ -719,6 +730,10 @@ class ConflictTheater:
MizCampaignLoader(directory / miz, t).populate_theater()
return t
@property
def seasonal_conditions(self) -> SeasonalConditions:
raise NotImplementedError
@property
def projection_parameters(self) -> TransverseMercator:
raise NotImplementedError
@ -748,6 +763,16 @@ class CaucasusTheater(ConflictTheater):
"night": (0, 5),
}
@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,
)
@property
def projection_parameters(self) -> TransverseMercator:
from .caucasus import PARAMETERS
@ -770,6 +795,16 @@ class PersianGulfTheater(ConflictTheater):
"night": (0, 5),
}
@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,
)
@property
def projection_parameters(self) -> TransverseMercator:
from .persiangulf import PARAMETERS
@ -792,6 +827,16 @@ class NevadaTheater(ConflictTheater):
"night": (0, 5),
}
@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,
)
@property
def projection_parameters(self) -> TransverseMercator:
from .nevada import PARAMETERS
@ -814,6 +859,16 @@ class NormandyTheater(ConflictTheater):
"night": (0, 5),
}
@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,
)
@property
def projection_parameters(self) -> TransverseMercator:
from .normandy import PARAMETERS
@ -836,6 +891,16 @@ class TheChannelTheater(ConflictTheater):
"night": (0, 5),
}
@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,
)
@property
def projection_parameters(self) -> TransverseMercator:
from .thechannel import PARAMETERS
@ -858,6 +923,16 @@ class SyriaTheater(ConflictTheater):
"night": (0, 5),
}
@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,
)
@property
def projection_parameters(self) -> TransverseMercator:
from .syria import PARAMETERS
@ -877,6 +952,16 @@ class MarianaIslandsTheater(ConflictTheater):
"night": (0, 5),
}
@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,
)
@property
def projection_parameters(self) -> TransverseMercator:
from .marianaislands import PARAMETERS

View File

@ -189,3 +189,15 @@ def pairwise(iterable: Iterable[Any]) -> Iterable[tuple[Any, Any]]:
a, b = itertools.tee(iterable)
next(b, None)
return zip(a, b)
def interpolate(value1: float, value2: float, factor: float, clamp: bool) -> float:
"""Inerpolate between two values, factor 0-1"""
interpolated = value1 + (value2 - value1) * factor
if clamp:
bigger_value = max(value1, value2)
smaller_value = min(value1, value2)
return min(bigger_value, max(smaller_value, interpolated))
else:
return interpolated

View File

@ -11,10 +11,11 @@ from dcs.cloud_presets import Clouds as PydcsClouds
from dcs.weather import CloudPreset, Weather as PydcsWeather, Wind
from game.settings import Settings
from game.utils import Distance, meters
from game.utils import Distance, meters, interpolate
if TYPE_CHECKING:
from game.theater import ConflictTheater
from game.theater.conflicttheater import SeasonalConditions
class TimeOfDay(Enum):
@ -71,15 +72,56 @@ class Fog:
class Weather:
def __init__(self) -> None:
def __init__(
self,
seasonal_conditions: SeasonalConditions,
day: datetime.date,
time_of_day: TimeOfDay,
) -> None:
# Future improvement: Use theater, day and time of day
# to get a more realistic conditions
self.atmospheric = self.generate_atmospheric()
self.atmospheric = self.generate_atmospheric(
seasonal_conditions, day, time_of_day
)
self.clouds = self.generate_clouds()
self.fog = self.generate_fog()
self.wind = self.generate_wind()
def generate_atmospheric(self) -> AtmosphericConditions:
def generate_atmospheric(
self,
seasonal_conditions: SeasonalConditions,
day: datetime.date,
time_of_day: TimeOfDay,
) -> AtmosphericConditions:
pressure = self.interpolate_summer_winter(
seasonal_conditions.summer_avg_pressure,
seasonal_conditions.winter_avg_pressure,
day,
)
temperature = self.interpolate_summer_winter(
seasonal_conditions.summer_avg_temperature,
seasonal_conditions.winter_avg_temperature,
day,
)
if time_of_day == TimeOfDay.Day:
temperature += seasonal_conditions.temperature_day_night_difference / 2
if time_of_day == TimeOfDay.Night:
temperature -= seasonal_conditions.temperature_day_night_difference / 2
pressure += self.pressure_adjustment
temperature += self.temperature_adjustment
conditions = AtmosphericConditions(
qnh_inches_mercury=self.random_pressure(pressure),
temperature_celsius=self.random_temperature(temperature),
)
return conditions
@property
def pressure_adjustment(self) -> float:
raise NotImplementedError
@property
def temperature_adjustment(self) -> float:
raise NotImplementedError
def generate_clouds(self) -> Optional[Clouds]:
@ -126,7 +168,7 @@ class Weather:
SAFE_MIN = 28.4
SAFE_MAX = 30.9
# Use normalvariate to get normal distribution, more realistic than uniform
pressure = random.normalvariate(average_pressure, 0.2)
pressure = random.normalvariate(average_pressure, 0.1)
return max(SAFE_MIN, min(SAFE_MAX, pressure))
@staticmethod
@ -136,17 +178,29 @@ class Weather:
SAFE_MIN = -12
SAFE_MAX = 49
# Use normalvariate to get normal distribution, more realistic than uniform
temperature = random.normalvariate(average_temperature, 4)
temperature = random.normalvariate(average_temperature, 2)
temperature = round(temperature)
return max(SAFE_MIN, min(SAFE_MAX, temperature))
@staticmethod
def interpolate_summer_winter(
summer_value: float, winter_value: float, day: datetime.date
) -> float:
day_of_year = day.timetuple().tm_yday
day_of_year_peak_summer = 183
distance_from_peak_summer = abs(-day_of_year_peak_summer + day_of_year)
winter_factor = distance_from_peak_summer / day_of_year_peak_summer
return interpolate(summer_value, winter_value, winter_factor, clamp=True)
class ClearSkies(Weather):
def generate_atmospheric(self) -> AtmosphericConditions:
return AtmosphericConditions(
qnh_inches_mercury=self.random_pressure(29.96),
temperature_celsius=self.random_temperature(22),
)
@property
def pressure_adjustment(self) -> float:
return 0.22
@property
def temperature_adjustment(self) -> float:
return 3.0
def generate_clouds(self) -> Optional[Clouds]:
return None
@ -159,11 +213,13 @@ class ClearSkies(Weather):
class Cloudy(Weather):
def generate_atmospheric(self) -> AtmosphericConditions:
return AtmosphericConditions(
qnh_inches_mercury=self.random_pressure(29.90),
temperature_celsius=self.random_temperature(20),
)
@property
def pressure_adjustment(self) -> float:
return 0.0
@property
def temperature_adjustment(self) -> float:
return 0.0
def generate_clouds(self) -> Optional[Clouds]:
return Clouds.random_preset(rain=False)
@ -177,11 +233,13 @@ class Cloudy(Weather):
class Raining(Weather):
def generate_atmospheric(self) -> AtmosphericConditions:
return AtmosphericConditions(
qnh_inches_mercury=self.random_pressure(29.70),
temperature_celsius=self.random_temperature(16),
)
@property
def pressure_adjustment(self) -> float:
return -0.22
@property
def temperature_adjustment(self) -> float:
return -3.0
def generate_clouds(self) -> Optional[Clouds]:
return Clouds.random_preset(rain=True)
@ -195,11 +253,13 @@ class Raining(Weather):
class Thunderstorm(Weather):
def generate_atmospheric(self) -> AtmosphericConditions:
return AtmosphericConditions(
qnh_inches_mercury=self.random_pressure(29.60),
temperature_celsius=self.random_temperature(15),
)
@property
def pressure_adjustment(self) -> float:
return 0.1
@property
def temperature_adjustment(self) -> float:
return -3.0
def generate_clouds(self) -> Optional[Clouds]:
return Clouds(
@ -233,7 +293,7 @@ class Conditions:
return cls(
time_of_day=time_of_day,
start_time=_start_time,
weather=cls.generate_weather(),
weather=cls.generate_weather(theater.seasonal_conditions, day, time_of_day),
)
@classmethod
@ -259,7 +319,13 @@ class Conditions:
return datetime.datetime.combine(day, time)
@classmethod
def generate_weather(cls) -> Weather:
def generate_weather(
cls,
seasonal_conditions: SeasonalConditions,
day: datetime.date,
time_of_day: TimeOfDay,
) -> Weather:
# Future improvement: use seasonal weights for theaters
chances = {
Thunderstorm: 1,
Raining: 20,
@ -269,4 +335,4 @@ class Conditions:
weather_type = random.choices(
list(chances.keys()), weights=list(chances.values())
)[0]
return weather_type()
return weather_type(seasonal_conditions, day, time_of_day)