diff --git a/game/event/event.py b/game/event/event.py index 15bbc58b..21c880c7 100644 --- a/game/event/event.py +++ b/game/event/event.py @@ -2,6 +2,7 @@ from dcs.unittype import UnitType from game import * from theater import * +from gen.environmentgen import EnvironmentSettings from userdata.debriefing import Debriefing @@ -15,6 +16,7 @@ class Event: operation = None # type: Operation difficulty = 1 # type: int game = None # type: Game + environment_settings = None # type: EnvironmentSettings BONUS_BASE = 3 def __init__(self, attacker_name: str, defender_name: str, from_cp: ControlPoint, to_cp: ControlPoint, game): @@ -47,12 +49,16 @@ class Event: def generate(self): self.operation.is_awacs_enabled = self.is_awacs_enabled + self.operation.prepare(self.game.theater.terrain, is_quick=False) self.operation.generate() self.operation.mission.save("build/nextturn.miz") + self.environment_settings = self.operation.environment_settings def generate_quick(self): self.operation.is_awacs_enabled = self.is_awacs_enabled + self.operation.environment_settings = self.environment_settings + self.operation.prepare(self.game.theater.terrain, is_quick=True) self.operation.generate() self.operation.mission.save('build/nextturn_quick.miz') diff --git a/game/event/groundintercept.py b/game/event/groundintercept.py index 95a2b7c7..5e4f5e50 100644 --- a/game/event/groundintercept.py +++ b/game/event/groundintercept.py @@ -2,6 +2,7 @@ import math import random from dcs.task import * +from dcs.vehicles import AirDefence from game import * from game.event import * @@ -57,6 +58,9 @@ class GroundInterceptEvent(Event): typecount = max(math.floor(self.difficulty * self.TARGET_AMOUNT_FACTOR), 1) self.targets = {unittype: typecount for unittype in unittypes} + defense_aa_unit = random.choice(self.game.commision_unit_types(self.to_cp, AirDefence)) + self.targets[defense_aa_unit] = 1 + op = GroundInterceptOperation(game=self.game, attacker_name=self.attacker_name, defender_name=self.defender_name, diff --git a/game/game.py b/game/game.py index 1dae70a6..d2c07db8 100644 --- a/game/game.py +++ b/game/game.py @@ -141,6 +141,14 @@ class Game: enemy_generated_types.append(event_class) self.events.append(event_class(self.enemy, self.player, enemy_cp, player_cp, self)) + def commision_unit_types(self, cp: ControlPoint, for_task: Task) -> typing.Collection[UnitType]: + importance_factor = (cp.importance - IMPORTANCE_LOW) / (IMPORTANCE_HIGH - IMPORTANCE_LOW) + + if for_task == AirDefence and not self.settings.sams: + return [x for x in db.find_unittype(AirDefence, self.enemy) if x not in db.SAM_BAN] + else: + return db.choose_units(for_task, importance_factor, COMMISION_UNIT_VARIETY, self.enemy) + def _commision_units(self, cp: ControlPoint): for for_task in [PinpointStrike, CAS, CAP, AirDefence]: limit = COMMISION_LIMITS_FACTORS[for_task] * math.pow(cp.importance, COMMISION_LIMITS_SCALE) * self.settings.multiplier @@ -149,13 +157,7 @@ class Game: awarded_points = COMMISION_AMOUNTS_FACTORS[for_task] * math.pow(cp.importance, COMMISION_AMOUNTS_SCALE) * self.settings.multiplier points_to_spend = cp.base.append_commision_points(for_task, awarded_points) if points_to_spend > 0: - importance_factor = (cp.importance - IMPORTANCE_LOW) / (IMPORTANCE_HIGH - IMPORTANCE_LOW) - - if for_task == AirDefence and not self.settings.sams: - unittypes = [x for x in db.find_unittype(AirDefence, self.enemy) if x not in db.SAM_BAN] - else: - unittypes = db.choose_units(for_task, importance_factor, COMMISION_UNIT_VARIETY, self.enemy) - + unittypes = self._commision_unit_types(cp, for_task) d = {random.choice(unittypes): points_to_spend} print("Commision {}: {}".format(cp, d)) cp.base.commision_units(d) @@ -208,7 +210,8 @@ class Game: def pass_turn(self, no_action=False, ignored_cps: typing.Collection[ControlPoint]=None): for event in self.events: - event.skip() + if isinstance(event, UnitsDeliveryEvent): + event.skip() if not no_action: self._budget_player() diff --git a/game/operation/antiaastrike.py b/game/operation/antiaastrike.py index a9b619a8..4403f504 100644 --- a/game/operation/antiaastrike.py +++ b/game/operation/antiaastrike.py @@ -5,7 +5,7 @@ from gen.armor import * from gen.aircraft import * from gen.aaa import * from gen.shipgen import * -from gen.settingsgen import * +from gen.triggergen import * from gen.awacsgen import * from gen.visualgen import * from gen.conflictgen import Conflict diff --git a/game/operation/capture.py b/game/operation/capture.py index 9991065f..4ec3f163 100644 --- a/game/operation/capture.py +++ b/game/operation/capture.py @@ -5,7 +5,7 @@ from gen.armor import * from gen.aircraft import * from gen.aaa import * from gen.shipgen import * -from gen.settingsgen import * +from gen.triggergen import * from gen.awacsgen import * from gen.visualgen import * diff --git a/game/operation/groundattack.py b/game/operation/groundattack.py index 9eb34ea6..7b12ad04 100644 --- a/game/operation/groundattack.py +++ b/game/operation/groundattack.py @@ -5,7 +5,7 @@ from gen.armor import * from gen.aircraft import * from gen.aaa import * from gen.shipgen import * -from gen.settingsgen import * +from gen.triggergen import * from gen.awacsgen import * from gen.visualgen import * from gen.conflictgen import Conflict diff --git a/game/operation/groundintercept.py b/game/operation/groundintercept.py index f623cba4..715b1b9e 100644 --- a/game/operation/groundintercept.py +++ b/game/operation/groundintercept.py @@ -5,7 +5,7 @@ from gen.armor import * from gen.aircraft import * from gen.aaa import * from gen.shipgen import * -from gen.settingsgen import * +from gen.triggergen import * from gen.awacsgen import * from gen.visualgen import * from gen.conflictgen import Conflict diff --git a/game/operation/infantrytransport.py b/game/operation/infantrytransport.py index 38550c9d..1c980137 100644 --- a/game/operation/infantrytransport.py +++ b/game/operation/infantrytransport.py @@ -5,7 +5,7 @@ from gen.armor import * from gen.aircraft import * from gen.aaa import * from gen.shipgen import * -from gen.settingsgen import * +from gen.triggergen import * from gen.awacsgen import * from gen.visualgen import * from gen.conflictgen import Conflict diff --git a/game/operation/operation.py b/game/operation/operation.py index d6c84bb2..caab84e6 100644 --- a/game/operation/operation.py +++ b/game/operation/operation.py @@ -16,10 +16,13 @@ class Operation: aagen = None # type: AAConflictGenerator extra_aagen = None # type: ExtraAAConflictGenerator shipgen = None # type: ShipGenerator - envgen = None # type: SettingsGenerator + triggersgen = None # type: TriggersGenerator awacsgen = None # type: AWACSConflictGenerator visualgen = None # type: VisualGenerator + envgen = None # type: EnvironmentGenerator + environment_settings = None + is_quick = None is_awacs_enabled = False def __init__(self, @@ -48,8 +51,9 @@ class Operation: self.aagen = AAConflictGenerator(mission, conflict) self.shipgen = ShipGenerator(mission, conflict) self.awacsgen = AWACSConflictGenerator(mission, conflict, self.game) - self.envgen = SettingsGenerator(mission, conflict, self.game) + self.triggersgen = TriggersGenerator(mission, conflict, self.game) self.visualgen = VisualGenerator(mission, conflict, self.game) + self.envgen = EnviromentGenerator(mission, conflict, self.game) player_name = self.from_cp.captured and self.attacker_name or self.defender_name enemy_name = self.from_cp.captured and self.defender_name or self.attacker_name @@ -73,7 +77,12 @@ class Operation: self.awacsgen.generate() self.extra_aagen.generate() - self.envgen.generate(self.is_quick) + self.triggersgen.generate(self.is_quick) + + if self.environment_settings is None: + self.environment_settings = self.envgen.generate() + else: + self.envgen.load(self.environment_settings) for global_cp in self.game.theater.controlpoints: if not global_cp.is_global: diff --git a/gen/__init__.py b/gen/__init__.py index 5a0ac6a4..0e4e80ab 100644 --- a/gen/__init__.py +++ b/gen/__init__.py @@ -5,7 +5,8 @@ from .awacsgen import * from .conflictgen import * from .shipgen import * from .visualgen import * -from .settingsgen import * +from .triggergen import * +from .environmentgen import * from . import naming diff --git a/gen/environmentgen.py b/gen/environmentgen.py new file mode 100644 index 00000000..eb0d0259 --- /dev/null +++ b/gen/environmentgen.py @@ -0,0 +1,106 @@ +import typing +import random +from datetime import datetime, timedelta, time + +from dcs.mission import Mission +from dcs.triggers import * +from dcs.condition import * +from dcs.action import * +from dcs.unit import Skill +from dcs.point import MovingPoint, PointProperties +from dcs.action import * + +from game import db +from theater import * +from gen import * + +WEATHER_CLOUD_BASE = 2000, 3000 +WEATHER_CLOUD_DENSITY = 1, 8 +WEATHER_CLOUD_THICKNESS = 100, 400 +WEATHER_CLOUD_BASE_MIN = 1200 + +RANDOM_TIME = { + "night": 5, + "dusk": 30, + "dawn": 30, + "day": 100, +} + +RANDOM_WEATHER = { + 1: 5, # heavy rain + 2: 20, # rain + 3: 30, # dynamic + 4: 50, # clear + 5: 100, # random +} + + +class EnvironmentSettings: + weather_dict = None + start_time = None + + +class EnviromentGenerator: + def __init__(self, mission: Mission, conflict: Conflict, game): + self.mission = mission + self.conflict = conflict + self.game = game + + def _gen_random_time(self): + start_time = datetime.combine(datetime.today(), time()) + time_range = None + for k, v in RANDOM_TIME.items(): + if self.game.settings.night_disabled and k == "night": + continue + + if random.randint(0, 100) <= v: + time_range = self.game.theater.daytime_map[k] + break + + start_time += timedelta(hours=random.randint(*time_range)) + self.mission.start_time = start_time + + def _gen_random_weather(self): + weather_type = None + for k, v in RANDOM_WEATHER.items(): + if random.randint(0, 100) <= v: + weather_type = k + break + + print("generated weather {}".format(weather_type)) + if weather_type == 0: + self.mission.weather.random_thunderstorm() + elif weather_type == 1: + self.mission.weather.heavy_rain() + elif weather_type == 2: + self.mission.weather.heavy_rain() + self.mission.weather.enable_fog = False + elif weather_type == 3: + self.mission.weather.random(self.mission.start_time, self.conflict.theater.terrain) + elif weather_type == 4: + pass + elif weather_type == 5: + self.mission.weather.clouds_base = random.randint(*WEATHER_CLOUD_BASE) + self.mission.weather.clouds_density = random.randint(*WEATHER_CLOUD_DENSITY) + self.mission.weather.clouds_thickness = random.randint(*WEATHER_CLOUD_THICKNESS) + + self.mission.weather.random(self.mission.start_time, self.conflict.theater.terrain) + + if self.mission.weather.clouds_density > 0: + self.mission.weather.clouds_base = max(self.mission.weather.clouds_base, WEATHER_CLOUD_BASE_MIN) + + self.mission.random_weather = False + + def generate(self) -> EnvironmentSettings: + self._gen_random_time() + self._gen_random_weather() + + settings = EnvironmentSettings() + settings.start_time = self.mission.start_time + settings.weather_dict = self.mission.weather.dict() + return settings + + def load(self, settings: EnvironmentSettings): + self.mission.start_time = settings.start_time + self.mission.weather.load_from_dict(settings.weather_dict) + diff --git a/gen/settingsgen.py b/gen/triggergen.py similarity index 79% rename from gen/settingsgen.py rename to gen/triggergen.py index a96e6fd5..a0720fd7 100644 --- a/gen/settingsgen.py +++ b/gen/triggergen.py @@ -22,67 +22,17 @@ PUSH_TRIGGER_SIZE = 3000 REGROUP_ZONE_DISTANCE = 12000 REGROUP_ALT = 5000 -CLOUDS_BASE_MIN = 4000 - -RANDOM_TIME = { - "night": 5, - "dusk": 30, - "dawn": 30, - "day": 100, -} - -RANDOM_WEATHER = { - 1: 5, # heavy rain - 2: 15, # rain - 3: 100, # random dynamic -} - class Silence(Option): Key = 7 -class SettingsGenerator: +class TriggersGenerator: def __init__(self, mission: Mission, conflict: Conflict, game): self.mission = mission self.conflict = conflict self.game = game - def _gen_random_time(self): - start_time = datetime.combine(datetime.today(), time()) - time_range = None - for k, v in RANDOM_TIME.items(): - if self.game.settings.night_disabled and k == "night": - continue - - if random.randint(0, 100) <= v: - time_range = self.game.theater.daytime_map[k] - break - - start_time += timedelta(hours=random.randint(*time_range)) - self.mission.start_time = start_time - - def _gen_random_weather(self): - weather_type = None - for k, v in RANDOM_WEATHER.items(): - if random.randint(0, 100) <= v: - weather_type = k - break - - print("generated weather {}".format(weather_type)) - if weather_type == 0: - self.mission.weather.random_thunderstorm() - elif weather_type == 1: - self.mission.weather.heavy_rain() - elif weather_type == 2: - self.mission.weather.heavy_rain() - self.mission.weather.enable_fog = False - elif weather_type == 3: - self.mission.weather.random(self.mission.start_time, self.conflict.theater.terrain) - - if self.mission.weather.clouds_density > 0: - self.mission.weather.clouds_base = max(self.mission.weather.clouds_base, CLOUDS_BASE_MIN) - def _gen_activation_trigger(self, player_coalition: str, enemy_coalition: str): activate_by_trigger = [] for coalition_name, coalition in self.mission.coalition.items(): @@ -178,8 +128,6 @@ class SettingsGenerator: self.mission.coalition[player_coalition].bullseye = {"x": self.conflict.position.x, "y": self.conflict.position.y} - self._gen_random_time() - self._gen_random_weather() self._set_skill(player_coalition, enemy_coalition) self._set_allegiances(player_coalition, enemy_coalition)