diff --git a/game/event.py b/game/event.py index 2eeda8f8..d1ba4463 100644 --- a/game/event.py +++ b/game/event.py @@ -6,6 +6,7 @@ DIFFICULTY_LOG_BASE = 1.5 class Event: silent = False informational = False + is_awacs_enabled = False operation = None # type: Operation difficulty = 1 # type: int BONUS_BASE = 0 @@ -24,11 +25,13 @@ class Event: return self.operation.is_successfull(debriefing) def generate(self): + self.operation.is_awacs_enabled = self.is_awacs_enabled self.operation.prepare(is_quick=False) self.operation.generate() self.operation.mission.save("build/nextturn.miz") def generate_quick(self): + self.operation.is_awacs_enabled = self.is_awacs_enabled self.operation.prepare(is_quick=True) self.operation.generate() self.operation.mission.save('build/nextturn_quick.miz') diff --git a/game/game.py b/game/game.py index 1fb3176d..5ce191ab 100644 --- a/game/game.py +++ b/game/game.py @@ -17,20 +17,22 @@ COMMISION_AMOUNTS_FACTORS = { } -ENEMY_INTERCEPT_PROBABILITY_BASE = 5 -ENEMY_INTERCEPT_GLOBAL_PROBABILITY_BASE = 1 -ENEMY_CAPTURE_PROBABILITY_BASE = 3 +ENEMY_INTERCEPT_PROBABILITY_BASE = 8 +ENEMY_INTERCEPT_GLOBAL_PROBABILITY_BASE = 3 +ENEMY_CAPTURE_PROBABILITY_BASE = 4 PLAYER_INTERCEPT_PROBABILITY_BASE = 30 PLAYER_GROUNDINTERCEPT_PROBABILITY_BASE = 30 -PLAYER_INTERCEPT_GLOBAL_PROBABILITY_BASE = 50 +PLAYER_INTERCEPT_GLOBAL_PROBABILITY_BASE = 10 PLAYER_INTERCEPT_GLOBAL_PROBABILITY_LOG = 2 PLAYER_BUDGET_INITIAL = 90 PLAYER_BUDGET_BASE = 20 PLAYER_BUDGET_IMPORTANCE_LOG = 2 +AWACS_BUDGET_COST = 8 + class Game: budget = PLAYER_BUDGET_INITIAL @@ -152,6 +154,9 @@ class Game: def _budget_player(self): self.budget += self.budget_reward_amount + def awacs_expense_commit(self): + self.budget -= AWACS_BUDGET_COST + def units_delivery_event(self, to_cp: ControlPoint) -> UnitsDeliveryEvent: event = UnitsDeliveryEvent(attacker_name=self.player, defender_name=self.player, diff --git a/game/operation.py b/game/operation.py index 5d960251..f22579c6 100644 --- a/game/operation.py +++ b/game/operation.py @@ -8,6 +8,7 @@ from gen.aaa import * from gen.shipgen import * from gen.conflictgen import * from gen.envsettingsgen import * +from gen.awacsgen import * class Operation: @@ -21,6 +22,9 @@ class Operation: extra_aagen = None # type: ExtraAAConflictGenerator shipgen = None # type: ShipGenerator envgen = None # type: EnvironmentSettingsGenerator + awacsgen = None # type: AWACSConflictGenerator + + is_awacs_enabled = False def __init__(self, game, @@ -47,6 +51,7 @@ class Operation: self.airgen = AircraftConflictGenerator(mission, conflict) self.aagen = AAConflictGenerator(mission, conflict) self.shipgen = ShipGenerator(mission, conflict) + self.awacsgen = AWACSConflictGenerator(mission, conflict, self.game) self.envgen = EnvironmentSettingsGenerator(mission, conflict, self.game) player_name = self.from_cp.captured and self.attacker_name or self.defender_name @@ -66,6 +71,8 @@ class Operation: def generate(self): self.extra_aagen.generate() self.envgen.generate(self.is_quick) + if self.is_awacs_enabled: + self.awacsgen.generate() def units_of(self, country_name: str) -> typing.Collection[UnitType]: return [] diff --git a/gen/awacsgen.py b/gen/awacsgen.py new file mode 100644 index 00000000..714a3184 --- /dev/null +++ b/gen/awacsgen.py @@ -0,0 +1,26 @@ +from game import db +from .conflictgen import * +from .naming import * + +from dcs.mission import * +from dcs.unitgroup import * +from dcs.unittype import * +from dcs.task import * +from dcs.terrain.terrain import NoParkingSlotError + + +class AWACSConflictGenerator: + def __init__(self, mission: Mission, conflict: Conflict, game): + self.mission = mission + self.conflict = conflict + self.game = game + + def generate(self): + plane = db.find_unittype(AWACS, self.conflict.attackers_side.name)[0] + + self.mission.awacs_flight( + country=self.conflict.attackers_side, + name=namegen.next_awacs_group_name(), + plane_type=plane, + airport=None, + position=self.conflict.position) diff --git a/gen/envsettingsgen.py b/gen/envsettingsgen.py index e923a62c..8d6c07c2 100644 --- a/gen/envsettingsgen.py +++ b/gen/envsettingsgen.py @@ -1,5 +1,6 @@ import typing import random +from datetime import datetime, timedelta from dcs.mission import Mission from dcs.triggers import * @@ -21,19 +22,20 @@ RANDOM_TIME = { } RANDOM_WEATHER = { - 0: 0, # thunderstorm - 1: 5, # heavy rain - 2: 15, # rain - 3: 35, # random dynamic + 0: 0, # thunderstorm + 1: 5, # heavy rain + 2: 20, # rain + 3: 40, # random dynamic } + class EnvironmentSettingsGenerator: def __init__(self, mission: Mission, conflict: Conflict, game): self.mission = mission self.conflict = conflict self.game = game - def generate(self, is_quick: bool): + def _gen_random_time(self): time_roll = random.randint(0, 100) time_period = None for k, v in RANDOM_TIME.items(): @@ -43,6 +45,26 @@ class EnvironmentSettingsGenerator: self.mission.random_daytime(time_period) + def _gen_random_time_2(self): + start_time = datetime.now(self.mission.start_time.tzinfo).date() + daytime_map = { + "day": timedelta(hours=random.randrange(9, 18)), + "night": timedelta(hours=random.randrange(-3, 6)), + "dusk": timedelta(hours=random.randrange(18, 21)), + "dawn": timedelta(hours=random.randrange(6, 9)), + } + + time_roll = random.randint(0, 100) + time_period = None + for k, v in RANDOM_TIME.items(): + if v >= time_roll: + time_period = k + break + + start_time += daytime_map[time_period] + self.mission.start_time = start_time + + def _gen_random_weather(self): weather_roll = random.randint(0, 100) weather_type = None for k, v in RANDOM_TIME.items(): @@ -60,34 +82,45 @@ class EnvironmentSettingsGenerator: elif weather_type == 3: self.mission.weather.random(self.mission.start_time, self.mission.terrain) - player_coalition = self.game.player == "USA" and "blue" or "red" - enemy_coalition = player_coalition == "blue" and "red" or "blue" + def _gen_activation_trigger(self, player_coalition: str, enemy_coalition: str): + activate_by_trigger = [] + for coalition_name, coalition in self.mission.coalition.items(): + for country in coalition.countries.values(): + if coalition_name == enemy_coalition: + for plane_group in country.plane_group: + plane_group.late_activation = True + activate_by_trigger.append(plane_group) + for vehicle_group in country.vehicle_group: + vehicle_group.late_activation = True + activate_by_trigger.append(vehicle_group) + + zone_distance_to_aircraft = self.conflict.air_attackers_location.distance_to_point(self.conflict.position) + zone_size = min(zone_distance_to_aircraft - ACTIVATION_TRIGGER_MIN_DISTANCE, ACTIVATION_TRIGGER_SIZE) + + activation_trigger_zone = self.mission.triggers.add_triggerzone(self.conflict.position, zone_size) + activation_trigger = TriggerOnce(Event.NoEvent, "Activation trigger") + activation_trigger.add_condition(PartOfCoalitionInZone(player_coalition, activation_trigger_zone.id)) + for group in activate_by_trigger: + activation_trigger.add_action(ActivateGroup(group.id)) + + self.mission.triggerrules.triggers.append(activation_trigger) + + def _set_allegiances(self, player_coalition: str, enemy_coalition: str): for cp in self.game.theater.controlpoints: if cp.is_global: continue self.mission.terrain.airport_by_id(cp.at.id).set_coalition(cp.captured and player_coalition or enemy_coalition) + def generate(self, is_quick: bool): + player_coalition = self.game.player == "USA" and "blue" or "red" + enemy_coalition = player_coalition == "blue" and "red" or "blue" + + self.mission.coalition[player_coalition].bullseye = self.conflict.position + + self._gen_random_time_2() + self._gen_random_weather() + self._set_allegiances(player_coalition, enemy_coalition) + if not is_quick: - activate_by_trigger = [] - for coalition_name, coalition in self.mission.coalition.items(): - for country in coalition.countries.values(): - if coalition_name == enemy_coalition: - for plane_group in country.plane_group: - plane_group.late_activation = True - activate_by_trigger.append(plane_group) - - for vehicle_group in country.vehicle_group: - vehicle_group.late_activation = True - activate_by_trigger.append(vehicle_group) - - zone_distance_to_aircraft = self.conflict.air_attackers_location.distance_to_point(self.conflict.position) - zone_size = min(zone_distance_to_aircraft - ACTIVATION_TRIGGER_MIN_DISTANCE, ACTIVATION_TRIGGER_SIZE) - - activation_trigger_zone = self.mission.triggers.add_triggerzone(self.conflict.position, zone_size) - activation_trigger = TriggerOnce(Event.NoEvent, "Activation trigger") - activation_trigger.add_condition(PartOfCoalitionInZone(player_coalition, activation_trigger_zone.id)) - for group in activate_by_trigger: - activation_trigger.add_action(ActivateGroup(group.id)) - - self.mission.triggerrules.triggers.append(activation_trigger) + self._gen_activation_trigger(player_coalition, enemy_coalition) diff --git a/gen/naming.py b/gen/naming.py index 96fb9bbd..42363aea 100644 --- a/gen/naming.py +++ b/gen/naming.py @@ -25,6 +25,10 @@ class NameGenerator: self.number += 1 return "Transport Unit {}".format(self.number) + def next_awacs_group_name(self): + self.number += 1 + return "AWACS Unit {}".format(self.number) + namegen = NameGenerator() diff --git a/ui/eventmenu.py b/ui/eventmenu.py index 460166e8..9467089b 100644 --- a/ui/eventmenu.py +++ b/ui/eventmenu.py @@ -8,6 +8,7 @@ class EventMenu(Menu): aircraft_scramble_entries = None # type: typing.Dict[PlaneType , Entry] aircraft_client_entries = None # type: typing.Dict[PlaneType, Entry] armor_scramble_entries = None # type: typing.Dict[VehicleType, Entry] + awacs = None # type: IntVar def __init__(self, window: Window, parent, game: Game, event: event.Event): super(EventMenu, self).__init__(window, parent, game) @@ -23,6 +24,7 @@ class EventMenu(Menu): self.base = self.event.to_cp.base self.frame = self.window.right_pane + self.awacs = IntVar() def display(self): self.window.clear_right_pane() @@ -60,11 +62,16 @@ class EventMenu(Menu): row += 1 - label("Aircraft") - label("Amount", row, 1) - label("Client slots", row, 2) + Checkbutton(self.frame, text="AWACS", var=self.awacs).grid(row=row, column=2) row += 1 + label("Aircraft") + + if self.base.aircraft: + label("Amount", row, 1) + label("Client slots", row, 2) + row += 1 + for unit_type, count in self.base.aircraft.items(): scrable_row(unit_type, count) @@ -82,14 +89,19 @@ class EventMenu(Menu): Button(self.frame, text="Back", command=self.dismiss).grid(column=2, row=row) def start(self): + if self.awacs.get() == 1: + self.event.is_awacs_enabled = True + self.game.awacs_expense_commit() + else: + self.event.is_awacs_enabled = False + scrambled_aircraft = {} scrambled_sweep = {} scrambled_cas = {} for unit_type, field in self.aircraft_scramble_entries.items(): value = field.get() if value and int(value) > 0: - #amount = min(int(value), self.base.aircraft[unit_type]) - amount = int(value) + amount = min(int(value), self.base.aircraft[unit_type]) task = db.unit_task(unit_type) scrambled_aircraft[unit_type] = amount