diff --git a/game/db.py b/game/db.py index e0e67e08..3b155721 100644 --- a/game/db.py +++ b/game/db.py @@ -380,7 +380,7 @@ PLANE_PAYLOAD_OVERRIDES = { CAP: "AIM-54A-MK47*4, AIM-7M*2, AIM-9M*2, XT*2", Escort: "AIM-54A-MK47*4, AIM-7M*2, AIM-9M*2, XT*2", CAS: "AIM-54A-MK60*1, AIM-7M*1, AIM-9M*2, XT*2, Mk-82*2, LANTIRN", - GroundAttack: "AIM-54A-MK60*1, AIM-7M*1, AIM-9M*2, XT*2, Mk-82*2, LANTIRN", + GroundAttack: "AIM54, AIM-9M*2, XT*2, GBU-12*4, LANTIRN", }, Su_25T: { diff --git a/game/event/event.py b/game/event/event.py index 9f7ac89f..d0475472 100644 --- a/game/event/event.py +++ b/game/event/event.py @@ -71,6 +71,10 @@ class Event: def ai_banned_tasks(self) -> typing.Collection[typing.Type[Task]]: return [] + @property + def player_banned_tasks(self) -> typing.Collection[typing.Type[Task]]: + return [] + @property def global_cp_available(self) -> bool: return False diff --git a/game/event/strike.py b/game/event/strike.py index 949cbdb9..9fc35bcc 100644 --- a/game/event/strike.py +++ b/game/event/strike.py @@ -20,7 +20,7 @@ class StrikeEvent(Event): @property def tasks(self): if self.is_player_attacking: - return [CAP, CAS] + return [CAP, CAS, SEAD] else: return [CAP] @@ -28,6 +28,10 @@ class StrikeEvent(Event): def ai_banned_tasks(self): return [CAS] + @property + def player_banned_tasks(self): + return [SEAD] + @property def global_cp_available(self) -> bool: return True @@ -38,6 +42,8 @@ class StrikeEvent(Event): return "Escort flight" else: return "CAP flight" + elif for_task == SEAD: + return "SEAD flight" elif for_task == CAS: return "Strike flight" @@ -47,7 +53,7 @@ class StrikeEvent(Event): self.to_cp.base.affect_strength(-self.SINGLE_OBJECT_STRENGTH_INFLUENCE * len(debriefing.destroyed_objects)) def player_attacking(self, flights: db.TaskForceDict): - assert CAP in flights and CAS in flights and len(flights) == 2, "Invalid flights" + assert CAP in flights and CAS in flights and SEAD in flights and len(flights) == 3, "Invalid flights" op = StrikeOperation( self.game, @@ -60,6 +66,7 @@ class StrikeEvent(Event): interceptors = self.to_cp.base.scramble_interceptors(self.game.settings.multiplier) op.setup(strikegroup=flights[CAS], + sead=flights[SEAD], escort=flights[CAP], interceptors=assigned_units_from(interceptors)) diff --git a/game/operation/operation.py b/game/operation/operation.py index 8d32d94d..b0851309 100644 --- a/game/operation/operation.py +++ b/game/operation/operation.py @@ -99,19 +99,18 @@ class Operation: self.defenders_starting_position = self.to_cp.at def prepare_carriers(self, for_units: db.UnitsDict): - for global_cp in self.game.theater.controlpoints: - if not global_cp.is_global: - continue + if not self.departure_cp.is_global: + return - ship = self.shipgen.generate_carrier(for_units=[t for t, c in for_units.items() if c > 0], - country=self.game.player, - at=global_cp.at) + ship = self.shipgen.generate_carrier(for_units=[t for t, c in for_units.items() if c > 0], + country=self.game.player, + at=self.departure_cp.at) - if global_cp == self.departure_cp and not self.is_quick: - if not self.to_cp.captured: - self.attackers_starting_position = ship - else: - self.defenders_starting_position = ship + if not self.is_quick: + if not self.to_cp.captured: + self.attackers_starting_position = ship + else: + self.defenders_starting_position = ship def generate(self): # air support diff --git a/game/operation/strike.py b/game/operation/strike.py index 9c158f13..07db70b0 100644 --- a/game/operation/strike.py +++ b/game/operation/strike.py @@ -5,6 +5,7 @@ from .operation import * class StrikeOperation(Operation): strikegroup = None # type: db.AssignedUnitsDict + sead = None # type: db.AssignedUnitsDict escort = None # type: db.AssignedUnitsDict interceptors = None # type: db.AssignedUnitsDict @@ -12,9 +13,11 @@ class StrikeOperation(Operation): def setup(self, strikegroup: db.AssignedUnitsDict, + sead: db.AssignedUnitsDict, escort: db.AssignedUnitsDict, interceptors: db.AssignedUnitsDict): self.strikegroup = strikegroup + self.sead = sead self.escort = escort self.interceptors = interceptors @@ -40,8 +43,10 @@ class StrikeOperation(Operation): self.prepare_carriers(db.unitdict_merge(db.unitdict_from(self.strikegroup), db.unitdict_from(self.escort))) targets = [] # type: typing.List[typing.Tuple[str, str, Point]] + sead_targets = [] # type: typing.List[typing.Tuple[str, str, Point]] category_counters = {} # type: typing.Dict[str, int] processed_groups = [] + for object in self.to_cp.ground_objects: if object.group_identifier in processed_groups: continue @@ -49,6 +54,10 @@ class StrikeOperation(Operation): processed_groups.append(object.group_identifier) category_counters[object.category] = category_counters.get(object.category, 0) + 1 markpoint_name = "{}{}".format(object.name_abbrev, category_counters[object.category]) + + if object.category == "aa": + sead_targets.append((str(object), markpoint_name, object.position)) + targets.append((str(object), markpoint_name, object.position)) targets.sort(key=lambda x: self.from_cp.position.distance_to_point(x[2])) @@ -59,7 +68,13 @@ class StrikeOperation(Operation): planes_flights = {k: v for k, v in self.strikegroup.items() if k in plane_map.values()} self.airgen.generate_ground_attack_strikegroup(*assigned_units_split(planes_flights), targets=[(mp, pos) for (n, mp, pos) in targets], - at=self.attackers_starting_position) + at=self.attackers_starting_position, + escort=True) + + self.airgen.generate_sead_strikegroup(*assigned_units_split(self.sead), + targets=[(mp, pos) for (n, mp, pos) in sead_targets], + at=self.attackers_starting_position, + escort=False) heli_flights = {k: v for k, v in self.strikegroup.items() if k in helicopters.helicopter_map.values()} if heli_flights: diff --git a/gen/aircraft.py b/gen/aircraft.py index f1357eb8..33fd2aff 100644 --- a/gen/aircraft.py +++ b/gen/aircraft.py @@ -309,6 +309,7 @@ class AircraftConflictGenerator: for name, pos in targets: waypoint = group.add_waypoint(pos, 0, WARM_START_AIRSPEED, self.m.translation.create_string(name)) + waypoint.tasks.append(Bombing(pos, attack_qty=2)) if escort_until_waypoint is None: escort_until_waypoint = waypoint @@ -318,6 +319,32 @@ class AircraftConflictGenerator: self.escort_targets.append((group, group.points.index(escort_until_waypoint))) self._rtb_for(group, self.conflict.from_cp, at) + def generate_sead_strikegroup(self, strikegroup: db.PlaneDict, clients: db.PlaneDict, targets: typing.List[typing.Tuple[str, Point]], at: db.StartingPosition, escort=True): + assert not escort or len(self.escort_targets) == 0 + + for flying_type, count, client_count in self._split_to_groups(strikegroup, clients): + group = self._generate_group( + name=namegen.next_unit_name(self.conflict.attackers_side, flying_type), + side=self.conflict.attackers_side, + unit_type=flying_type, + count=count, + client_count=client_count, + at=at and at or self._group_point(self.conflict.air_attackers_location)) + + escort_until_waypoint = None + + for name, pos in targets: + waypoint = group.add_waypoint(pos, 0, WARM_START_AIRSPEED, self.m.translation.create_string(name)) + if escort_until_waypoint is None: + escort_until_waypoint = waypoint + + group.task = SEAD.name + self._setup_group(group, SEAD, client_count) + if escort: + self.escort_targets.append((group, group.points.index(escort_until_waypoint))) + + self._rtb_for(group, self.conflict.from_cp, at) + def generate_defenders_cas(self, defenders: db.PlaneDict, clients: db.PlaneDict, at: db.StartingPosition = None, escort=True): assert not escort or len(self.escort_targets) == 0 diff --git a/gen/environmentgen.py b/gen/environmentgen.py index fb0600ef..98c1c3b4 100644 --- a/gen/environmentgen.py +++ b/gen/environmentgen.py @@ -26,9 +26,9 @@ WEATHER_FOG_VISIBILITY = 2500, 5000 WEATHER_FOG_THICKNESS = 100, 500 RANDOM_TIME = { - "night": 5, - "dusk": 30, - "dawn": 30, + "night": 7, + "dusk": 40, + "dawn": 40, "day": 100, } diff --git a/theater/start_generator.py b/theater/start_generator.py index 47ddbf86..6a95f705 100644 --- a/theater/start_generator.py +++ b/theater/start_generator.py @@ -79,7 +79,7 @@ def generate_groundobjects(theater: ConflictTheater): if not cp.has_frontline: continue - amount = random.randrange(5, 6) + amount = random.randrange(5, 7) for i in range(0, amount): available_categories = list(tpls) if i >= amount - 1: diff --git a/ui/eventmenu.py b/ui/eventmenu.py index e4c29e27..24aa7e70 100644 --- a/ui/eventmenu.py +++ b/ui/eventmenu.py @@ -194,6 +194,11 @@ class EventMenu(Menu): self.error_label["text"] = "Need at least one player in flight {}".format(self.event.flight_name(task)) return + for task in self.event.player_banned_tasks: + if tasks_clients_counts.get(task, 0) != 0: + self.error_label["text"] = "Players are not allowed on flight {}".format(self.event.flight_name(task)) + return + if self.game.is_player_attack(self.event): if isinstance(self.event, FrontlineAttackEvent) or isinstance(self.event, FrontlinePatrolEvent): if self.event.from_cp.base.total_armor == 0: