diff --git a/game/event/event.py b/game/event/event.py index 7832444e..5aff24fb 100644 --- a/game/event/event.py +++ b/game/event/event.py @@ -320,7 +320,7 @@ class Event: enemy_cp.base.affect_strength(delta) cp.base.affect_strength(-delta) info = Information("Frontline Report", - "Our ground forces from " + cp.name + " are losing ground against the enemy forces from" + enemy_cp.name, + "Our ground forces from " + cp.name + " are losing ground against the enemy forces from " + enemy_cp.name, self.game.turn) self.game.informations.append(info) diff --git a/gen/aircraft.py b/gen/aircraft.py index ed296ec0..6ea24693 100644 --- a/gen/aircraft.py +++ b/gen/aircraft.py @@ -258,7 +258,7 @@ class AircraftConflictGenerator: def setup_group_activation_trigger(self, flight, group): if flight.scheduled_in > 0 and flight.client_count == 0: - if flight.start_type != "In Flight": + if flight.start_type != "In Flight" and flight.from_cp.cptype not in [ControlPointType.AIRCRAFT_CARRIER_GROUP, ControlPointType.LHA_GROUP]: group.late_activation = False group.uncontrolled = True @@ -402,8 +402,6 @@ class AircraftConflictGenerator: elif point.waypoint_type == FlightWaypointType.LANDING_POINT: pt.type = "Land" elif point.waypoint_type == FlightWaypointType.INGRESS_STRIKE: - print("TGTS :") - print(point.targets) for j, t in enumerate(point.targets): print(t.position) pt.tasks.append(Bombing(t.position)) @@ -411,6 +409,12 @@ class AircraftConflictGenerator: group.add_nav_target_point(t.position, "PP" + str(j + 1)) if group.units[0].unit_type == F_14B and j == 0: group.add_nav_target_point(t.position, "ST") + elif point.waypoint_type == FlightWaypointType.INGRESS_SEAD: + for j, t in enumerate(point.targets): + if group.units[0].unit_type == JF_17 and j < 4: + group.add_nav_target_point(t.position, "PP" + str(j + 1)) + if group.units[0].unit_type == F_14B and j == 0: + group.add_nav_target_point(t.position, "ST") if pt is not None: pt.alt_type = point.alt_type diff --git a/gen/flights/ai_flight_planner.py b/gen/flights/ai_flight_planner.py index 8f34a044..68ac16b6 100644 --- a/gen/flights/ai_flight_planner.py +++ b/gen/flights/ai_flight_planner.py @@ -153,67 +153,16 @@ class FlightPlanner: break inventory[unit] = inventory[unit] - 2 - ftype = FlightType.BARCAP if self.from_cp.is_carrier else FlightType.CAP - flight = Flight(unit, 2, self.from_cp, ftype) + flight = Flight(unit, 2, self.from_cp, FlightType.CAP) flight.points = [] flight.scheduled_in = offset + i*random.randint(CAP_EVERY_X_MINUTES-5, CAP_EVERY_X_MINUTES+5) - patrol_alt = random.randint(PATROL_ALT_RANGE[0], PATROL_ALT_RANGE[1]) - - # Choose a location for CAP patrols (Either behind frontline if there is one, or to protect ground objects) if len(self._get_cas_locations()) > 0: - loc = random.choice(self._get_cas_locations()) - ingress, heading, distance = Conflict.frontline_vector(self.from_cp, loc, self.game.theater) - center = ingress.point_from_heading(heading, distance / 2) - orbit_center = center.point_from_heading(heading - 90, random.randint(nm_to_meter(6), nm_to_meter(15))) - radius = distance * 2 - orbit0p = orbit_center.point_from_heading(heading, radius) - orbit1p = orbit_center.point_from_heading(heading + 180, radius) - elif len(self.from_cp.ground_objects) > 0: - loc = random.choice(self.from_cp.ground_objects) - hdg = self.from_cp.position.heading_between_point(loc.position) - radius = random.randint(nm_to_meter(5), nm_to_meter(10)) - orbit0p = loc.position.point_from_heading(hdg - 90, radius) - orbit1p = loc.position.point_from_heading(hdg + 90, radius) + enemy_cp = random.choice(self._get_cas_locations()) + self.generate_frontline_cap(flight, flight.from_cp, enemy_cp) else: - loc = self.from_cp.position.point_from_heading(random.randint(0, 360), random.randint(nm_to_meter(5), nm_to_meter(40))) - hdg = self.from_cp.position.heading_between_point(loc.position) - radius = random.randint(nm_to_meter(40), nm_to_meter(120)) - orbit0p = loc.position.point_from_heading(hdg - 90, radius) - orbit1p = loc.position.point_from_heading(hdg + 90, radius) - - - # Create points - ascend = self.generate_ascend_point(self.from_cp) - flight.points.append(ascend) - - orbit0 = FlightWaypoint(orbit0p.x, orbit0p.y, patrol_alt) - orbit0.name = "ORBIT 0" - orbit0.description = "Standby between this point and the next one" - orbit0.pretty_name = "Orbit race-track start" - orbit0.waypoint_type = FlightWaypointType.PATROL_TRACK - flight.points.append(orbit0) - - orbit1 = FlightWaypoint(orbit1p.x, orbit1p.y, patrol_alt) - orbit1.name = "ORBIT 1" - orbit1.description = "Standby between this point and the previous one" - orbit1.pretty_name = "Orbit race-track end" - orbit1.waypoint_type = FlightWaypointType.PATROL - flight.points.append(orbit1) - - orbit0.targets.append(self.from_cp) - obj_added = [] - for ground_object in self.from_cp.ground_objects: - if ground_object.obj_name not in obj_added and not ground_object.airbase_group: - orbit0.targets.append(ground_object) - obj_added.append(ground_object.obj_name) - - descend = self.generate_descend_point(self.from_cp) - flight.points.append(descend) - - rtb = self.generate_rtb_waypoint(self.from_cp) - flight.points.append(rtb) + self.generate_barcap(flight, flight.from_cp) self.cap_flights.append(flight) self.flights.append(flight) @@ -243,47 +192,11 @@ class FlightPlanner: inventory[unit] = inventory[unit] - 2 flight = Flight(unit, 2, self.from_cp, FlightType.CAS) - flight.points = [] - flight.scheduled_in = offset + i*random.randint(CAS_EVERY_X_MINUTES-5, CAS_EVERY_X_MINUTES+5) - + flight.scheduled_in = offset + i * random.randint(CAS_EVERY_X_MINUTES - 5, CAS_EVERY_X_MINUTES + 5) location = random.choice(cas_location) - ingress, heading, distance = Conflict.frontline_vector(self.from_cp, location, self.game.theater) - center = ingress.point_from_heading(heading, distance/2) - egress = ingress.point_from_heading(heading, distance) - ascend = self.generate_ascend_point(self.from_cp) - flight.points.append(ascend) - - ingress_point = FlightWaypoint(ingress.x, ingress.y, 1000) - ingress_point.alt_type = "RADIO" - ingress_point.name = "INGRESS" - ingress_point.pretty_name = "INGRESS" - ingress_point.description = "Ingress into CAS area" - ingress_point.waypoint_type = FlightWaypointType.INGRESS_CAS - flight.points.append(ingress_point) - - center_point = FlightWaypoint(center.x, center.y, 1000) - center_point.alt_type = "RADIO" - center_point.description = "Provide CAS" - center_point.name = "CAS" - center_point.pretty_name = "CAS" - center_point.waypoint_type = FlightWaypointType.CAS - flight.points.append(center_point) - - egress_point = FlightWaypoint(egress.x, egress.y, 1000) - egress_point.alt_type = "RADIO" - egress_point.description = "Egress from CAS area" - egress_point.name = "EGRESS" - egress_point.pretty_name = "EGRESS" - egress_point.waypoint_type = FlightWaypointType.EGRESS - flight.points.append(egress_point) - - descend = self.generate_descend_point(self.from_cp) - flight.points.append(descend) - - rtb = self.generate_rtb_waypoint(self.from_cp) - flight.points.append(rtb) + self.generate_cas(flight, flight.from_cp, location) self.cas_flights.append(flight) self.flights.append(flight) @@ -319,49 +232,10 @@ class FlightPlanner: flight.points = [] flight.scheduled_in = offset + i*random.randint(SEAD_EVERY_X_MINUTES-5, SEAD_EVERY_X_MINUTES+5) - ascend = self.generate_ascend_point(self.from_cp) - flight.points.append(ascend) - location = self.potential_sead_targets[0][0] self.potential_sead_targets.pop(0) - heading = self.from_cp.position.heading_between_point(location.position) - ingress_heading = heading - 180 + 25 - egress_heading = heading - 180 - 25 - - ingress_pos = location.position.point_from_heading(ingress_heading, INGRESS_EGRESS_DISTANCE) - ingress_point = FlightWaypoint(ingress_pos.x, ingress_pos.y, INGRESS_ALT) - ingress_point.pretty_name = "INGRESS on " + location.obj_name - ingress_point.description = "INGRESS on " + location.obj_name - ingress_point.waypoint_type = FlightWaypointType.INGRESS_SEAD - flight.points.append(ingress_point) - - point = FlightWaypoint(location.position.x, location.position.y, 0) - point.alt_type = "RADIO" - if flight.flight_type == FlightType.DEAD: - point.description = "SEAD on " + location.obj_name - point.pretty_name = "SEAD on " + location.obj_name - point.only_for_player = True - else: - point.description = "DEAD on " + location.obj_name - point.pretty_name = "DEAD on " + location.obj_name - point.only_for_player = True - - ingress_point.targets.append(location) - flight.points.append(point) - - egress_pos = location.position.point_from_heading(egress_heading, INGRESS_EGRESS_DISTANCE) - egress_point = FlightWaypoint(egress_pos.x, egress_pos.y, EGRESS_ALT) - egress_point.pretty_name = "EGRESS from " + location.obj_name - egress_point.description = "EGRESS from " + location.obj_name - egress_point.waypoint_type = FlightWaypointType.EGRESS - flight.points.append(egress_point) - - descend = self.generate_descend_point(self.from_cp) - flight.points.append(descend) - - rtb = self.generate_rtb_waypoint(self.from_cp) - flight.points.append(rtb) + self.generate_sead(flight, location, []) self.sead_flights.append(flight) self.flights.append(flight) @@ -397,75 +271,10 @@ class FlightPlanner: flight.points = [] flight.scheduled_in = offset + i*random.randint(SEAD_EVERY_X_MINUTES-5, SEAD_EVERY_X_MINUTES+5) - ascend = self.generate_ascend_point(self.from_cp) - flight.points.append(ascend) - location = self.potential_strike_targets[0][0] self.potential_strike_targets.pop(0) - heading = self.from_cp.position.heading_between_point(location.position) - ingress_heading = heading - 180 + 25 - egress_heading = heading - 180 - 25 - - ingress_pos = location.position.point_from_heading(ingress_heading, INGRESS_EGRESS_DISTANCE) - ingress_point = FlightWaypoint(ingress_pos.x, ingress_pos.y, INGRESS_ALT) - ingress_point.pretty_name = "INGRESS on " + location.obj_name - ingress_point.description = "INGRESS on " + location.obj_name - ingress_point.name = "INGRESS" - ingress_point.waypoint_type = FlightWaypointType.INGRESS_STRIKE - flight.points.append(ingress_point) - - if len(location.groups) > 0 and location.dcs_identifier == "AA": - for g in location.groups: - for j, u in enumerate(g.units): - point = FlightWaypoint(u.position.x, u.position.y, 0) - point.description = "STRIKE " + "[" + str(location.obj_name) + "] : " + u.type + " #" + str(j) - point.pretty_name = "STRIKE " + "[" + str(location.obj_name) + "] : " + u.type + " #" + str(j) - point.name = location.obj_name + "#" + str(j) - point.only_for_player = True - ingress_point.targets.append(location) - flight.points.append(point) - else: - if hasattr(location, "obj_name"): - buildings = self.game.theater.find_ground_objects_by_obj_name(location.obj_name) - print(buildings) - for building in buildings: - print("BUILDING " + str(building.is_dead) + " " + str(building.dcs_identifier)) - if building.is_dead: - continue - - point = FlightWaypoint(building.position.x, building.position.y, 0) - point.description = "STRIKE on " + building.obj_name + " " + str(building.category) - point.pretty_name = "STRIKE on " + building.obj_name + " " + str(building.category) - point.name = building.obj_name - point.only_for_player = True - ingress_point.targets.append(building) - flight.points.append(point) - else: - point = FlightWaypoint(location.position.x, location.position.y, 0) - point.description = "STRIKE on " + location.obj_name + " " + str(location.category) - point.pretty_name = "STRIKE on " + location.obj_name + " " + str(location.category) - point.name = location.obj_name - point.only_for_player = True - ingress_point.targets.append(location) - flight.points.append(point) - - - - - egress_pos = location.position.point_from_heading(egress_heading, INGRESS_EGRESS_DISTANCE) - egress_point = FlightWaypoint(egress_pos.x, egress_pos.y, EGRESS_ALT) - egress_point.name = "EGRESS" - egress_point.pretty_name = "EGRESS from " + location.obj_name - egress_point.description = "EGRESS from " + location.obj_name - egress_point.waypoint_type = FlightWaypointType.EGRESS - flight.points.append(egress_point) - - descend = self.generate_descend_point(self.from_cp) - flight.points.append(descend) - - rtb = self.generate_rtb_waypoint(self.from_cp) - flight.points.append(rtb) + self.generate_strike(flight, location) self.strike_flights.append(flight) self.flights.append(flight) @@ -475,9 +284,12 @@ class FlightPlanner: self.aircraft_inventory[k] = v def _get_cas_locations(self): + return self._get_cas_locations_for_cp(self.from_cp) + + def _get_cas_locations_for_cp(self, for_cp): cas_locations = [] - for cp in self.from_cp.connected_points: - if cp.captured != self.from_cp.captured: + for cp in for_cp.connected_points: + if cp.captured != for_cp.captured: cas_locations.append(cp) return cas_locations @@ -563,7 +375,300 @@ class FlightPlanner: del base_aircraft_inventory[f.unit_type] return base_aircraft_inventory + + def generate_strike(self, flight, location): + + flight.flight_type = FlightType.STRIKE + ascend = self.generate_ascend_point(flight.from_cp) + flight.points.append(ascend) + + heading = flight.from_cp.position.heading_between_point(location.position) + ingress_heading = heading - 180 + 25 + egress_heading = heading - 180 - 25 + + ingress_pos = location.position.point_from_heading(ingress_heading, INGRESS_EGRESS_DISTANCE) + ingress_point = FlightWaypoint(ingress_pos.x, ingress_pos.y, INGRESS_ALT) + ingress_point.pretty_name = "INGRESS on " + location.obj_name + ingress_point.description = "INGRESS on " + location.obj_name + ingress_point.name = "INGRESS" + ingress_point.waypoint_type = FlightWaypointType.INGRESS_STRIKE + flight.points.append(ingress_point) + + if len(location.groups) > 0 and location.dcs_identifier == "AA": + for g in location.groups: + for j, u in enumerate(g.units): + point = FlightWaypoint(u.position.x, u.position.y, 0) + point.description = "STRIKE " + "[" + str(location.obj_name) + "] : " + u.type + " #" + str(j) + point.pretty_name = "STRIKE " + "[" + str(location.obj_name) + "] : " + u.type + " #" + str(j) + point.name = location.obj_name + "#" + str(j) + point.only_for_player = True + ingress_point.targets.append(location) + flight.points.append(point) + else: + if hasattr(location, "obj_name"): + buildings = self.game.theater.find_ground_objects_by_obj_name(location.obj_name) + print(buildings) + for building in buildings: + print("BUILDING " + str(building.is_dead) + " " + str(building.dcs_identifier)) + if building.is_dead: + continue + + point = FlightWaypoint(building.position.x, building.position.y, 0) + point.description = "STRIKE on " + building.obj_name + " " + str(building.category) + point.pretty_name = "STRIKE on " + building.obj_name + " " + str(building.category) + point.name = building.obj_name + point.only_for_player = True + ingress_point.targets.append(building) + flight.points.append(point) + else: + point = FlightWaypoint(location.position.x, location.position.y, 0) + point.description = "STRIKE on " + location.obj_name + point.pretty_name = "STRIKE on " + location.obj_name + point.name = location.obj_name + point.only_for_player = True + ingress_point.targets.append(location) + flight.points.append(point) + + egress_pos = location.position.point_from_heading(egress_heading, INGRESS_EGRESS_DISTANCE) + egress_point = FlightWaypoint(egress_pos.x, egress_pos.y, EGRESS_ALT) + egress_point.name = "EGRESS" + egress_point.pretty_name = "EGRESS from " + location.obj_name + egress_point.description = "EGRESS from " + location.obj_name + egress_point.waypoint_type = FlightWaypointType.EGRESS + flight.points.append(egress_point) + + descend = self.generate_descend_point(flight.from_cp) + flight.points.append(descend) + + rtb = self.generate_rtb_waypoint(flight.from_cp) + flight.points.append(rtb) + + def generate_barcap(self, flight, for_cp): + """ + Generate a barcap flight at a given location + :param flight: Flight to setup + :param for_cp: CP to protect + :param location: Location to protect in priority + :param location: Is the location to protect a frontline + """ + flight.flight_type = FlightType.BARCAP if for_cp.is_carrier else FlightType.CAP + patrol_alt = random.randint(PATROL_ALT_RANGE[0], PATROL_ALT_RANGE[1]) + + if len(for_cp.ground_objects) > 0: + loc = random.choice(for_cp.ground_objects) + hdg = for_cp.position.heading_between_point(loc.position) + radius = nm_to_meter(random.randint(15, 40)) + orbit0p = loc.position.point_from_heading(hdg - 90, radius) + orbit1p = loc.position.point_from_heading(hdg + 90, radius) + else: + loc = for_cp.position.point_from_heading(random.randint(0, 360),random.randint(nm_to_meter(10), nm_to_meter(40))) + hdg = for_cp.position.heading_between_point(loc.position) + radius = nm_to_meter(random.randint(15, 40)) + orbit0p = loc.position.point_from_heading(hdg - 90, radius) + orbit1p = loc.position.point_from_heading(hdg + 90, radius) + + # Create points + ascend = self.generate_ascend_point(flight.from_cp) + flight.points.append(ascend) + + orbit0 = FlightWaypoint(orbit0p.x, orbit0p.y, patrol_alt) + orbit0.name = "ORBIT 0" + orbit0.description = "Standby between this point and the next one" + orbit0.pretty_name = "Race-track start" + orbit0.waypoint_type = FlightWaypointType.PATROL_TRACK + flight.points.append(orbit0) + + orbit1 = FlightWaypoint(orbit1p.x, orbit1p.y, patrol_alt) + orbit1.name = "ORBIT 1" + orbit1.description = "Standby between this point and the previous one" + orbit1.pretty_name = "Race-track end" + orbit1.waypoint_type = FlightWaypointType.PATROL + flight.points.append(orbit1) + + orbit0.targets.append(for_cp) + obj_added = [] + for ground_object in for_cp.ground_objects: + if ground_object.obj_name not in obj_added and not ground_object.airbase_group: + orbit0.targets.append(ground_object) + obj_added.append(ground_object.obj_name) + + descend = self.generate_descend_point(flight.from_cp) + flight.points.append(descend) + + rtb = self.generate_rtb_waypoint(flight.from_cp) + flight.points.append(rtb) + + + def generate_frontline_cap(self, flight, ally_cp, enemy_cp): + """ + Generate a cap flight for the frontline between ally_cp and enemy cp in order to ensure air superiority and + protect friendly CAP airbase + :param flight: Flight to setup + :param ally_cp: CP to protect + :param enemy_cp: Enemy connected cp + """ + flight.flight_type = FlightType.CAP + patrol_alt = random.randint(PATROL_ALT_RANGE[0], PATROL_ALT_RANGE[1]) + + # Find targets waypoints + ingress, heading, distance = Conflict.frontline_vector(ally_cp, enemy_cp, self.game.theater) + center = ingress.point_from_heading(heading, distance / 2) + orbit_center = center.point_from_heading(heading - 90, random.randint(nm_to_meter(6), nm_to_meter(15))) + radius = distance * 2 + orbit0p = orbit_center.point_from_heading(heading, radius) + orbit1p = orbit_center.point_from_heading(heading + 180, radius) + + # Create points + ascend = self.generate_ascend_point(flight.from_cp) + flight.points.append(ascend) + + orbit0 = FlightWaypoint(orbit0p.x, orbit0p.y, patrol_alt) + orbit0.name = "ORBIT 0" + orbit0.description = "Standby between this point and the next one" + orbit0.pretty_name = "Race-track start" + orbit0.waypoint_type = FlightWaypointType.PATROL_TRACK + flight.points.append(orbit0) + + orbit1 = FlightWaypoint(orbit1p.x, orbit1p.y, patrol_alt) + orbit1.name = "ORBIT 1" + orbit1.description = "Standby between this point and the previous one" + orbit1.pretty_name = "Race-track end" + orbit1.waypoint_type = FlightWaypointType.PATROL + flight.points.append(orbit1) + + # Note : Targets of a PATROL TRACK waypoints are the points to be defended + orbit0.targets.append(flight.from_cp) + orbit0.targets.append(center) + + descend = self.generate_descend_point(flight.from_cp) + flight.points.append(descend) + + rtb = self.generate_rtb_waypoint(flight.from_cp) + flight.points.append(rtb) + + + def generate_sead(self, flight, location, custom_targets = []): + """ + Generate a sead flight at a given location + :param flight: Flight to setup + :param location: Location of the SEAD target + :param custom_targets: Custom targets if any + """ + flight.points = [] + flight.flight_type = random.choice([FlightType.SEAD, FlightType.DEAD]) + + ascend = self.generate_ascend_point(flight.from_cp) + flight.points.append(ascend) + + heading = flight.from_cp.position.heading_between_point(location.position) + ingress_heading = heading - 180 + 25 + egress_heading = heading - 180 - 25 + + ingress_pos = location.position.point_from_heading(ingress_heading, INGRESS_EGRESS_DISTANCE) + ingress_point = FlightWaypoint(ingress_pos.x, ingress_pos.y, INGRESS_ALT) + ingress_point.name = "INGRESS" + ingress_point.pretty_name = "INGRESS on " + location.obj_name + ingress_point.description = "INGRESS on " + location.obj_name + ingress_point.waypoint_type = FlightWaypointType.INGRESS_SEAD + flight.points.append(ingress_point) + + if len(custom_targets) > 0: + for target in custom_targets: + point = FlightWaypoint(target.position.x, target.position.y, 0) + point.alt_type = "RADIO" + if flight.flight_type == FlightType.DEAD: + point.description = "SEAD on " + target.type + point.pretty_name = "SEAD on " + location.obj_name + point.only_for_player = True + else: + point.description = "DEAD on " + location.obj_name + point.pretty_name = "DEAD on " + location.obj_name + point.only_for_player = True + ingress_point.targets.append(location) + flight.points.append(point) + else: + point = FlightWaypoint(location.position.x, location.position.y, 0) + point.alt_type = "RADIO" + if flight.flight_type == FlightType.DEAD: + point.description = "SEAD on " + location.obj_name + point.pretty_name = "SEAD on " + location.obj_name + point.only_for_player = True + else: + point.description = "DEAD on " + location.obj_name + point.pretty_name = "DEAD on " + location.obj_name + point.only_for_player = True + ingress_point.targets.append(location) + flight.points.append(point) + + egress_pos = location.position.point_from_heading(egress_heading, INGRESS_EGRESS_DISTANCE) + egress_point = FlightWaypoint(egress_pos.x, egress_pos.y, EGRESS_ALT) + egress_point.name = "INGRESS" + egress_point.pretty_name = "EGRESS from " + location.obj_name + egress_point.description = "EGRESS from " + location.obj_name + egress_point.waypoint_type = FlightWaypointType.EGRESS + flight.points.append(egress_point) + + descend = self.generate_descend_point(flight.from_cp) + flight.points.append(descend) + + rtb = self.generate_rtb_waypoint(flight.from_cp) + flight.points.append(rtb) + + + def generate_cas(self, flight, from_cp, location): + """ + Generate a CAS flight at a given location + :param flight: Flight to setup + :param location: Location of the CAS targets + """ + + flight.points = [] + flight.flight_type = FlightType.CAS + + ingress, heading, distance = Conflict.frontline_vector(from_cp, location, self.game.theater) + center = ingress.point_from_heading(heading, distance / 2) + egress = ingress.point_from_heading(heading, distance) + + ascend = self.generate_ascend_point(flight.from_cp) + flight.points.append(ascend) + + ingress_point = FlightWaypoint(ingress.x, ingress.y, 1000) + ingress_point.alt_type = "RADIO" + ingress_point.name = "INGRESS" + ingress_point.pretty_name = "INGRESS" + ingress_point.description = "Ingress into CAS area" + ingress_point.waypoint_type = FlightWaypointType.INGRESS_CAS + flight.points.append(ingress_point) + + center_point = FlightWaypoint(center.x, center.y, 1000) + center_point.alt_type = "RADIO" + center_point.description = "Provide CAS" + center_point.name = "CAS" + center_point.pretty_name = "CAS" + center_point.waypoint_type = FlightWaypointType.CAS + flight.points.append(center_point) + + egress_point = FlightWaypoint(egress.x, egress.y, 1000) + egress_point.alt_type = "RADIO" + egress_point.description = "Egress from CAS area" + egress_point.name = "EGRESS" + egress_point.pretty_name = "EGRESS" + egress_point.waypoint_type = FlightWaypointType.EGRESS + flight.points.append(egress_point) + + descend = self.generate_descend_point(flight.from_cp) + flight.points.append(descend) + + rtb = self.generate_rtb_waypoint(flight.from_cp) + flight.points.append(rtb) + + def generate_ascend_point(self, from_cp): + """ + Generate ascend point + :param from_cp: Airport you're taking off from + :return: + """ ascend_heading = from_cp.heading pos_ascend = from_cp.position.point_from_heading(ascend_heading, 10000) ascend = FlightWaypoint(pos_ascend.x, pos_ascend.y, PATTERN_ALTITUDE) @@ -574,9 +679,15 @@ class FlightPlanner: ascend.waypoint_type = FlightWaypointType.ASCEND_POINT return ascend + def generate_descend_point(self, from_cp): + """ + Generate approach/descend point + :param from_cp: Airport you're landing at + :return: + """ ascend_heading = from_cp.heading - descend = from_cp.position.point_from_heading(ascend_heading - 180, 30000) + descend = from_cp.position.point_from_heading(ascend_heading - 180, 10000) descend = FlightWaypoint(descend.x, descend.y, PATTERN_ALTITUDE) descend.name = "DESCEND" descend.alt_type = "RADIO" @@ -585,7 +696,13 @@ class FlightPlanner: descend.waypoint_type = FlightWaypointType.DESCENT_POINT return descend + def generate_rtb_waypoint(self, from_cp): + """ + Generate RTB landing point + :param from_cp: Airport you're landing at + :return: + """ rtb = from_cp.position rtb = FlightWaypoint(rtb.x, rtb.y, 0) rtb.name = "LANDING" @@ -593,5 +710,4 @@ class FlightPlanner: rtb.description = "RTB" rtb.pretty_name = "RTB" rtb.waypoint_type = FlightWaypointType.LANDING_POINT - return rtb - + return rtb \ No newline at end of file diff --git a/gen/flights/flight.py b/gen/flights/flight.py index e24dd92f..a36605a0 100644 --- a/gen/flights/flight.py +++ b/gen/flights/flight.py @@ -46,6 +46,18 @@ class FlightWaypointType(Enum): TARGET_POINT = 12 # A target building or static object, position TARGET_GROUP_LOC = 13 # A target group approximate location TARGET_SHIP = 14 # A target ship known location + CUSTOM = 15 # User waypoint (no specific behaviour) + + +class PredefinedWaypointCategory(Enum): + NOT_PREDEFINED = 0 + ALLY_CP = 1 + ENEMY_CP = 2 + FRONTLINE = 3 + ENEMY_BUILDING = 4 + ENEMY_UNIT = 5 + ALLY_BUILDING = 6 + ALLY_UNIT = 7 class FlightWaypoint: @@ -61,7 +73,9 @@ class FlightWaypoint: self.obj_name = "" self.pretty_name = "" self.waypoint_type = FlightWaypointType.TAKEOFF # type: FlightWaypointType + self.category = PredefinedWaypointCategory.NOT_PREDEFINED# type: PredefinedWaypointCategory self.only_for_player = False + self.data = None class Flight: diff --git a/qt_ui/widgets/QPredefinedWaypointSelectionComboBox.py b/qt_ui/widgets/QPredefinedWaypointSelectionComboBox.py deleted file mode 100644 index 167ae8e6..00000000 --- a/qt_ui/widgets/QPredefinedWaypointSelectionComboBox.py +++ /dev/null @@ -1,153 +0,0 @@ -from PySide2.QtCore import QSortFilterProxyModel, Qt, QModelIndex -from PySide2.QtGui import QStandardItem, QStandardItemModel -from PySide2.QtWidgets import QComboBox, QCompleter -from game import Game -from gen import Conflict -from gen.flights.flight import FlightWaypoint -from theater import ControlPointType - - -class QPredefinedWaypointSelectionComboBox(QComboBox): - - def __init__(self, game: Game, parent=None): - super(QPredefinedWaypointSelectionComboBox, self).__init__(parent) - - self.game = game - self.setFocusPolicy(Qt.StrongFocus) - self.setEditable(True) - self.completer = QCompleter(self) - - # always show all completions - self.completer.setCompletionMode(QCompleter.UnfilteredPopupCompletion) - self.pFilterModel = QSortFilterProxyModel(self) - self.pFilterModel.setFilterCaseSensitivity(Qt.CaseInsensitive) - - self.completer.setPopup(self.view()) - - self.setCompleter(self.completer) - - self.lineEdit().textEdited.connect(self.pFilterModel.setFilterFixedString) - self.completer.activated.connect(self.setTextIfCompleterIsClicked) - - self.find_possible_waypoints() - - def setModel(self, model): - super(QPredefinedWaypointSelectionComboBox, self).setModel(model) - self.pFilterModel.setSourceModel(model) - self.completer.setModel(self.pFilterModel) - - def setModelColumn(self, column): - self.completer.setCompletionColumn(column) - self.pFilterModel.setFilterKeyColumn(column) - super(QPredefinedWaypointSelectionComboBox, self).setModelColumn(column) - - def view(self): - return self.completer.popup() - - def index(self): - return self.currentIndex() - - def setTextIfCompleterIsClicked(self, text): - if text: - index = self.findText(text) - self.setCurrentIndex(index) - - def get_selected_waypoints(self, include_all_from_same_location=False): - n = self.currentText() - - first_waypoint = None - for w in self.wpts: - if w.pretty_name == n: - first_waypoint = w - break - - if first_waypoint is None: - return [] - - waypoints = [first_waypoint] - if include_all_from_same_location: - for w in self.wpts: - if w is not first_waypoint and w.obj_name and w.obj_name == first_waypoint.obj_name: - waypoints.append(w) - - return waypoints - - def find_possible_waypoints(self): - - self.wpts = [] - model = QStandardItemModel() - i = 0 - - def add_model_item(i, model, name, wpt): - print(name) - item = QStandardItem(name) - model.setItem(i, 0, item) - self.wpts.append(wpt) - return i + 1 - - for cp in self.game.theater.controlpoints: - print(cp) - if cp.captured: - enemy_cp = [ecp for ecp in cp.connected_points if ecp.captured != cp.captured] - for ecp in enemy_cp: - pos = Conflict.frontline_position(self.game.theater, cp, ecp)[0] - wpt = FlightWaypoint(pos.x, pos.y, 800) - wpt.name = "Frontline " + cp.name + "/" + ecp.name + " [CAS]" - wpt.alt_type = "RADIO" - wpt.pretty_name = wpt.name - wpt.description = "Frontline" - i = add_model_item(i, model, wpt.pretty_name, wpt) - - for cp in self.game.theater.controlpoints: - for ground_object in cp.ground_objects: - if not ground_object.is_dead and not ground_object.dcs_identifier == "AA": - wpt = FlightWaypoint(ground_object.position.x,ground_object.position.y, 0) - wpt.alt_type = "RADIO" - wpt.name = wpt.name = "[" + str(ground_object.obj_name) + "] : " + ground_object.category + " #" + str(ground_object.object_id) - wpt.pretty_name = wpt.name - wpt.obj_name = ground_object.obj_name - wpt.targets.append(ground_object) - if cp.captured: - wpt.description = "Friendly Building" - else: - wpt.description = "Enemy Building" - i = add_model_item(i, model, wpt.pretty_name, wpt) - - for cp in self.game.theater.controlpoints: - - for ground_object in cp.ground_objects: - if not ground_object.is_dead and ground_object.dcs_identifier == "AA": - for g in ground_object.groups: - for j, u in enumerate(g.units): - wpt = FlightWaypoint(u.position.x, u.position.y, 0) - wpt.alt_type = "RADIO" - wpt.name = wpt.name = "[" + str(ground_object.obj_name) + "] : " + u.type + " #" + str(j) - wpt.pretty_name = wpt.name - wpt.targets.append(u) - wpt.obj_name = ground_object.obj_name - if cp.captured: - wpt.description = "Friendly unit : " + u.type - else: - wpt.description = "Enemy unit : " + u.type - i = add_model_item(i, model, wpt.pretty_name, wpt) - - for cp in self.game.theater.controlpoints: - - wpt = FlightWaypoint(cp.position.x, cp.position.y, 0) - wpt.alt_type = "RADIO" - wpt.name = cp.name - if cp.captured: - wpt.description = "Position of " + cp.name + " [Friendly Airbase]" - else: - wpt.description = "Position of " + cp.name + " [Enemy Airbase]" - - if cp.cptype == ControlPointType.AIRCRAFT_CARRIER_GROUP: - wpt.pretty_name = cp.name + " (Aircraft Carrier Group)" - elif cp.cptype == ControlPointType.LHA_GROUP: - wpt.pretty_name = cp.name + " (LHA Group)" - else: - wpt.pretty_name = cp.name + " (Airbase)" - - i = add_model_item(i, model, wpt.pretty_name, wpt) - - self.setModel(model) diff --git a/qt_ui/widgets/combos/QFilteredComboBox.py b/qt_ui/widgets/combos/QFilteredComboBox.py new file mode 100644 index 00000000..9597c42b --- /dev/null +++ b/qt_ui/widgets/combos/QFilteredComboBox.py @@ -0,0 +1,53 @@ +from PySide2.QtCore import QSortFilterProxyModel, Qt +from PySide2.QtWidgets import QComboBox, QCompleter + + +class QFilteredComboBox(QComboBox): + + def __init__(self, parent=None, include_targets=True, include_airbases=True, + include_frontlines=True, include_units=True, include_enemy=True, include_friendly=True): + super(QFilteredComboBox, self).__init__(parent) + + self.setFocusPolicy(Qt.StrongFocus) + self.setEditable(True) + self.completer = QCompleter(self) + + self.include_targets = include_targets + self.include_airbases = include_airbases + self.include_frontlines = include_frontlines + self.include_units = include_units + self.include_enemy = include_enemy + self.include_friendly = include_friendly + + # always show all completions + self.completer.setCompletionMode(QCompleter.UnfilteredPopupCompletion) + self.pFilterModel = QSortFilterProxyModel(self) + self.pFilterModel.setFilterCaseSensitivity(Qt.CaseInsensitive) + + self.completer.setPopup(self.view()) + + self.setCompleter(self.completer) + + self.lineEdit().textEdited.connect(self.pFilterModel.setFilterFixedString) + self.completer.activated.connect(self.setTextIfCompleterIsClicked) + + def setModel(self, model): + super(QFilteredComboBox, self).setModel(model) + self.pFilterModel.setSourceModel(model) + self.completer.setModel(self.pFilterModel) + + def setModelColumn(self, column): + self.completer.setCompletionColumn(column) + self.pFilterModel.setFilterKeyColumn(column) + super(QFilteredComboBox, self).setModelColumn(column) + + def view(self): + return self.completer.popup() + + def index(self): + return self.currentIndex() + + def setTextIfCompleterIsClicked(self, text): + if text: + index = self.findText(text) + self.setCurrentIndex(index) diff --git a/qt_ui/widgets/combos/QPredefinedWaypointSelectionComboBox.py b/qt_ui/widgets/combos/QPredefinedWaypointSelectionComboBox.py new file mode 100644 index 00000000..c8721a17 --- /dev/null +++ b/qt_ui/widgets/combos/QPredefinedWaypointSelectionComboBox.py @@ -0,0 +1,139 @@ +from PySide2.QtCore import QSortFilterProxyModel, Qt, QModelIndex +from PySide2.QtGui import QStandardItem, QStandardItemModel +from PySide2.QtWidgets import QComboBox, QCompleter +from game import Game +from gen import Conflict, FlightWaypointType +from gen.flights.flight import FlightWaypoint, PredefinedWaypointCategory +from qt_ui.widgets.combos.QFilteredComboBox import QFilteredComboBox +from theater import ControlPointType + + +class QPredefinedWaypointSelectionComboBox(QFilteredComboBox): + + def __init__(self, game: Game, parent=None, include_targets=True, include_airbases=True, + include_frontlines=True, include_units=True, include_enemy=True, include_friendly=True): + super(QPredefinedWaypointSelectionComboBox, self).__init__(parent) + self.game = game + self.include_targets = include_targets + self.include_airbases = include_airbases + self.include_frontlines = include_frontlines + self.include_units = include_units + self.include_enemy = include_enemy + self.include_friendly = include_friendly + self.find_possible_waypoints() + + def get_selected_waypoints(self, include_all_from_same_location=False): + n = self.currentText() + first_waypoint = None + for w in self.wpts: + if w.pretty_name == n: + first_waypoint = w + break + if first_waypoint is None: + return [] + waypoints = [first_waypoint] + if include_all_from_same_location: + for w in self.wpts: + if w is not first_waypoint and w.obj_name and w.obj_name == first_waypoint.obj_name: + waypoints.append(w) + return waypoints + + def find_possible_waypoints(self): + + self.wpts = [] + model = QStandardItemModel() + i = 0 + + def add_model_item(i, model, name, wpt): + print(name) + item = QStandardItem(name) + model.setItem(i, 0, item) + self.wpts.append(wpt) + return i + 1 + + if self.include_frontlines: + for cp in self.game.theater.controlpoints: + if cp.captured: + enemy_cp = [ecp for ecp in cp.connected_points if ecp.captured != cp.captured] + for ecp in enemy_cp: + pos = Conflict.frontline_position(self.game.theater, cp, ecp)[0] + wpt = FlightWaypoint(pos.x, pos.y, 800) + wpt.name = "Frontline " + cp.name + "/" + ecp.name + " [CAS]" + wpt.alt_type = "RADIO" + wpt.pretty_name = wpt.name + wpt.description = "Frontline" + wpt.data = [cp, ecp] + wpt.waypoint_type = FlightWaypointType.CUSTOM + wpt.category = PredefinedWaypointCategory.FRONTLINE + i = add_model_item(i, model, wpt.pretty_name, wpt) + + if self.include_targets: + for cp in self.game.theater.controlpoints: + if (self.include_enemy and not cp.captured) or (self.include_friendly and cp.captured): + for ground_object in cp.ground_objects: + if not ground_object.is_dead and not ground_object.dcs_identifier == "AA": + wpt = FlightWaypoint(ground_object.position.x,ground_object.position.y, 0) + wpt.alt_type = "RADIO" + wpt.name = wpt.name = "[" + str(ground_object.obj_name) + "] : " + ground_object.category + " #" + str(ground_object.object_id) + wpt.pretty_name = wpt.name + wpt.obj_name = ground_object.obj_name + wpt.targets.append(ground_object) + wpt.data = ground_object + wpt.waypoint_type = FlightWaypointType.CUSTOM + if cp.captured: + wpt.description = "Friendly Building" + wpt.category = PredefinedWaypointCategory.ALLY_BUILDING + else: + wpt.description = "Enemy Building" + wpt.category = PredefinedWaypointCategory.ENEMY_BUILDING + i = add_model_item(i, model, wpt.pretty_name, wpt) + + if self.include_units: + for cp in self.game.theater.controlpoints: + if (self.include_enemy and not cp.captured) or (self.include_friendly and cp.captured): + for ground_object in cp.ground_objects: + if not ground_object.is_dead and ground_object.dcs_identifier == "AA": + for g in ground_object.groups: + for j, u in enumerate(g.units): + wpt = FlightWaypoint(u.position.x, u.position.y, 0) + wpt.alt_type = "RADIO" + wpt.name = wpt.name = "[" + str(ground_object.obj_name) + "] : " + u.type + " #" + str(j) + wpt.pretty_name = wpt.name + wpt.targets.append(u) + wpt.data = u + wpt.obj_name = ground_object.obj_name + wpt.waypoint_type = FlightWaypointType.CUSTOM + if cp.captured: + wpt.description = "Friendly unit : " + u.type + wpt.category = PredefinedWaypointCategory.ALLY_UNIT + else: + wpt.description = "Enemy unit : " + u.type + wpt.category = PredefinedWaypointCategory.ENEMY_UNIT + i = add_model_item(i, model, wpt.pretty_name, wpt) + + if self.include_airbases: + for cp in self.game.theater.controlpoints: + if (self.include_enemy and not cp.captured) or (self.include_friendly and cp.captured): + wpt = FlightWaypoint(cp.position.x, cp.position.y, 0) + wpt.alt_type = "RADIO" + wpt.name = cp.name + wpt.data = cp + wpt.waypoint_type = FlightWaypointType.CUSTOM + if cp.captured: + wpt.description = "Position of " + cp.name + " [Friendly Airbase]" + wpt.category = PredefinedWaypointCategory.ALLY_CP + else: + wpt.description = "Position of " + cp.name + " [Enemy Airbase]" + wpt.category = PredefinedWaypointCategory.ENEMY_CP + + if cp.cptype == ControlPointType.AIRCRAFT_CARRIER_GROUP: + wpt.pretty_name = cp.name + " (Aircraft Carrier Group)" + elif cp.cptype == ControlPointType.LHA_GROUP: + wpt.pretty_name = cp.name + " (LHA Group)" + else: + wpt.pretty_name = cp.name + " (Airbase)" + + + i = add_model_item(i, model, wpt.pretty_name, wpt) + + self.setModel(model) diff --git a/qt_ui/widgets/combos/QSEADTargetSelectionComboBox.py b/qt_ui/widgets/combos/QSEADTargetSelectionComboBox.py new file mode 100644 index 00000000..07cda611 --- /dev/null +++ b/qt_ui/widgets/combos/QSEADTargetSelectionComboBox.py @@ -0,0 +1,76 @@ +from PySide2.QtCore import QSortFilterProxyModel, Qt, QModelIndex +from PySide2.QtGui import QStandardItem, QStandardItemModel +from PySide2.QtWidgets import QComboBox, QCompleter +from game import Game +from gen import Conflict, FlightWaypointType, db +from gen.flights.flight import FlightWaypoint, PredefinedWaypointCategory +from qt_ui.widgets.combos.QFilteredComboBox import QFilteredComboBox +from theater import ControlPointType + + +class SEADTargetInfo: + + def __init__(self): + self.name = "" + self.location = None + self.radars = [] + self.threat_range = 0 + self.detection_range = 0 + +class QSEADTargetSelectionComboBox(QFilteredComboBox): + + def __init__(self, game: Game, parent=None): + super(QSEADTargetSelectionComboBox, self).__init__(parent) + self.game = game + self.find_possible_sead_targets() + + def get_selected_target(self): + n = self.currentText() + for target in self.targets: + if target.name == n: + return target + + def find_possible_sead_targets(self): + + self.targets = [] + i = 0 + model = QStandardItemModel() + + def add_model_item(i, model, target): + item = QStandardItem(target.name) + model.setItem(i, 0, item) + self.targets.append(target) + return i + 1 + + for cp in self.game.theater.controlpoints: + if cp.captured: continue + for g in cp.ground_objects: + + radars = [] + detection_range = 0 + threat_range = 0 + if g.dcs_identifier == "AA": + for group in g.groups: + for u in group.units: + utype = db.unit_type_from_name(u.type) + if hasattr(utype, "detection_range") and utype.detection_range > 1000: + if utype.detection_range > detection_range: + detection_range = utype.detection_range + radars.append(u) + if hasattr(utype, "threat_range"): + if utype.threat_range > threat_range: + threat_range = utype.threat_range + if len(radars) > 0: + tgt_info = SEADTargetInfo() + tgt_info.name = g.obj_name + " [" + ",".join([db.unit_type_from_name(u.type).id for u in radars]) + " ]" + if len(tgt_info.name) > 25: + tgt_info.name = g.obj_name + " [" + str(len(radars)) + " units]" + tgt_info.radars = radars + tgt_info.location = g + tgt_info.threat_range = threat_range + tgt_info.detection_range = detection_range + i = add_model_item(i, model, tgt_info) + + self.setModel(model) + + diff --git a/qt_ui/widgets/combos/QStrikeTargetSelectionComboBox.py b/qt_ui/widgets/combos/QStrikeTargetSelectionComboBox.py new file mode 100644 index 00000000..c1027cb6 --- /dev/null +++ b/qt_ui/widgets/combos/QStrikeTargetSelectionComboBox.py @@ -0,0 +1,69 @@ +from PySide2.QtGui import QStandardItem, QStandardItemModel + +from game import Game +from qt_ui.widgets.combos.QFilteredComboBox import QFilteredComboBox + + +class StrikeTargetInfo: + + def __init__(self): + self.name = "" + self.location = None + self.units = [] + self.buildings = [] + + +class QStrikeTargetSelectionComboBox(QFilteredComboBox): + + def __init__(self, game: Game, parent=None): + super(QStrikeTargetSelectionComboBox, self).__init__(parent) + self.game = game + self.find_possible_strike_targets() + + def get_selected_target(self): + n = self.currentText() + for target in self.targets: + if target.name == n: + return target + + def find_possible_strike_targets(self): + + self.targets = [] + i = 0 + model = QStandardItemModel() + + def add_model_item(i, model, target): + item = QStandardItem(target.name) + model.setItem(i, 0, item) + self.targets.append(target) + return i + 1 + + for cp in self.game.theater.controlpoints: + if cp.captured: continue + + added_obj_names = [] + + for g in cp.ground_objects: + if g.obj_name in added_obj_names: continue + + target = StrikeTargetInfo() + target.location = g + target.name = g.obj_name + + if g.dcs_identifier == "AA": + target.name = g.obj_name + " [units]" + for group in g.groups: + for u in group.units: + target.units.append(u) + else: + target.name = g.obj_name + " [" + g.category + "]" + for g2 in cp.ground_objects: + if g2 is not g and g2.obj_name == g.obj_name: + target.buildings.append(g2) + + i = add_model_item(i, model, target) + added_obj_names.append(g.obj_name) + + self.setModel(model) + + diff --git a/qt_ui/windows/mission/flight/generator/QAbstractMissionGenerator.py b/qt_ui/windows/mission/flight/generator/QAbstractMissionGenerator.py new file mode 100644 index 00000000..8a69d4cd --- /dev/null +++ b/qt_ui/windows/mission/flight/generator/QAbstractMissionGenerator.py @@ -0,0 +1,43 @@ +from PySide2.QtCore import Qt +from PySide2.QtWidgets import QDialog, QPushButton + +from game import Game +from gen.flights.flight import Flight +from qt_ui.uiconstants import EVENT_ICONS +from qt_ui.windows.mission.flight.waypoints.QFlightWaypointInfoBox import QFlightWaypointInfoBox + + +class QAbstractMissionGenerator(QDialog): + + def __init__(self, game: Game, flight: Flight, flight_waypoint_list, title): + super(QAbstractMissionGenerator, self).__init__() + self.game = game + self.flight = flight + self.setWindowFlags(Qt.WindowStaysOnTopHint) + self.setMinimumSize(400, 250) + self.setModal(True) + self.setWindowTitle(title) + self.setWindowIcon(EVENT_ICONS["strike"]) + self.flight_waypoint_list = flight_waypoint_list + self.planner = self.game.planners[self.flight.from_cp.id] + + self.selected_waypoints = [] + self.wpt_info = QFlightWaypointInfoBox() + + self.ok_button = QPushButton("Ok") + self.ok_button.clicked.connect(self.apply) + + def on_select_wpt_changed(self): + self.selected_waypoints = self.wpt_selection_box.get_selected_waypoints(False) + if self.selected_waypoints is None or len(self.selected_waypoints) <= 0: + self.ok_button.setDisabled(True) + else: + self.wpt_info.set_flight_waypoint(self.selected_waypoints[0]) + self.ok_button.setDisabled(False) + + def apply(self): + raise NotImplementedError() + + + + diff --git a/qt_ui/windows/mission/flight/generator/QCAPMissionGenerator.py b/qt_ui/windows/mission/flight/generator/QCAPMissionGenerator.py new file mode 100644 index 00000000..c1f5591e --- /dev/null +++ b/qt_ui/windows/mission/flight/generator/QCAPMissionGenerator.py @@ -0,0 +1,52 @@ +from PySide2.QtWidgets import QLabel, QHBoxLayout, QVBoxLayout + +from game import Game +from gen.flights.flight import Flight, PredefinedWaypointCategory +from qt_ui.widgets.combos.QPredefinedWaypointSelectionComboBox import QPredefinedWaypointSelectionComboBox +from qt_ui.windows.mission.flight.generator.QAbstractMissionGenerator import QAbstractMissionGenerator + + +class QCAPMissionGenerator(QAbstractMissionGenerator): + + def __init__(self, game: Game, flight: Flight, flight_waypoint_list): + super(QCAPMissionGenerator, self).__init__(game, flight, flight_waypoint_list, "CAP Generator") + + self.wpt_selection_box = QPredefinedWaypointSelectionComboBox(self.game, self, False, True, True, False, False, True) + self.wpt_selection_box.setMinimumWidth(200) + self.wpt_selection_box.currentTextChanged.connect(self.on_select_wpt_changed) + + self.init_ui() + self.on_select_wpt_changed() + + def init_ui(self): + layout = QVBoxLayout() + + wpt_layout = QHBoxLayout() + wpt_layout.addWidget(QLabel("CAP mission on : ")) + wpt_layout.addWidget(self.wpt_selection_box) + wpt_layout.addStretch() + + layout.addLayout(wpt_layout) + layout.addWidget(self.wpt_info) + layout.addStretch() + layout.addWidget(self.ok_button) + + self.setLayout(layout) + + def apply(self): + self.flight.points = [] + + wpt = self.selected_waypoints[0] + if wpt.category == PredefinedWaypointCategory.FRONTLINE: + self.planner.generate_frontline_cap(self.flight, wpt.data[0], wpt.data[1]) + elif wpt.category == PredefinedWaypointCategory.ALLY_CP: + self.planner.generate_barcap(self.flight, wpt.data) + else: + return + + self.flight_waypoint_list.update_list() + self.close() + + + + diff --git a/qt_ui/windows/mission/flight/generator/QCASMissionGenerator.py b/qt_ui/windows/mission/flight/generator/QCASMissionGenerator.py new file mode 100644 index 00000000..92305af7 --- /dev/null +++ b/qt_ui/windows/mission/flight/generator/QCASMissionGenerator.py @@ -0,0 +1,65 @@ +from PySide2.QtGui import Qt +from PySide2.QtWidgets import QLabel, QHBoxLayout, QVBoxLayout +from dcs import Point + +from game import Game +from gen.flights.ai_flight_planner import meter_to_nm +from gen.flights.flight import Flight +from qt_ui.widgets.combos.QPredefinedWaypointSelectionComboBox import QPredefinedWaypointSelectionComboBox +from qt_ui.windows.mission.flight.generator.QAbstractMissionGenerator import QAbstractMissionGenerator + + +class QCASMissionGenerator(QAbstractMissionGenerator): + + def __init__(self, game: Game, flight: Flight, flight_waypoint_list): + super(QCASMissionGenerator, self).__init__(game, flight, flight_waypoint_list, "CAS Generator") + + self.wpt_selection_box = QPredefinedWaypointSelectionComboBox(self.game, self, False, False, True, False, False) + self.wpt_selection_box.setMinimumWidth(200) + self.wpt_selection_box.currentTextChanged.connect(self.on_select_wpt_changed) + + self.distanceToTargetLabel = QLabel("0 nm") + self.init_ui() + self.on_select_wpt_changed() + + def on_select_wpt_changed(self): + super(QCASMissionGenerator, self).on_select_wpt_changed() + wpts = self.wpt_selection_box.get_selected_waypoints() + + if len(wpts) > 0: + self.distanceToTargetLabel.setText("~" + str(meter_to_nm(self.flight.from_cp.position.distance_to_point(Point(wpts[0].x, wpts[0].y)))) + " nm") + else: + self.distanceToTargetLabel.setText("??? nm") + + + + def init_ui(self): + layout = QVBoxLayout() + + wpt_layout = QHBoxLayout() + wpt_layout.addWidget(QLabel("CAS : ")) + wpt_layout.addWidget(self.wpt_selection_box) + wpt_layout.addStretch() + + distToTarget = QHBoxLayout() + distToTarget.addWidget(QLabel("Distance to target : ")) + distToTarget.addStretch() + distToTarget.addWidget(self.distanceToTargetLabel, alignment=Qt.AlignRight) + + layout.addLayout(wpt_layout) + layout.addWidget(self.wpt_info) + layout.addLayout(distToTarget) + layout.addStretch() + layout.addWidget(self.ok_button) + + self.setLayout(layout) + + def apply(self): + self.flight.points = [] + self.planner.generate_cas(self.flight, self.selected_waypoints[0].data[0], self.selected_waypoints[0].data[1]) + self.flight_waypoint_list.update_list() + self.close() + + + + diff --git a/qt_ui/windows/mission/flight/generator/QSEADMissionGenerator.py b/qt_ui/windows/mission/flight/generator/QSEADMissionGenerator.py new file mode 100644 index 00000000..b61435da --- /dev/null +++ b/qt_ui/windows/mission/flight/generator/QSEADMissionGenerator.py @@ -0,0 +1,73 @@ +from PySide2.QtGui import Qt +from PySide2.QtWidgets import QLabel, QHBoxLayout, QVBoxLayout + +from game import Game +from gen.flights.ai_flight_planner import meter_to_nm +from gen.flights.flight import Flight +from qt_ui.widgets.combos.QSEADTargetSelectionComboBox import QSEADTargetSelectionComboBox +from qt_ui.windows.mission.flight.generator.QAbstractMissionGenerator import QAbstractMissionGenerator + + +class QSEADMissionGenerator(QAbstractMissionGenerator): + + def __init__(self, game: Game, flight: Flight, flight_waypoint_list): + super(QSEADMissionGenerator, self).__init__(game, flight, flight_waypoint_list, "SEAD/DEAD Generator") + + self.tgt_selection_box = QSEADTargetSelectionComboBox(self.game) + self.tgt_selection_box.setMinimumWidth(200) + self.tgt_selection_box.currentTextChanged.connect(self.on_selected_target_changed) + + self.distanceToTargetLabel = QLabel("0 nm") + self.threatRangeLabel = QLabel("0 nm") + self.detectionRangeLabel = QLabel("0 nm") + self.init_ui() + self.on_selected_target_changed() + + def on_selected_target_changed(self): + target = self.tgt_selection_box.get_selected_target() + self.distanceToTargetLabel.setText("~" + str(meter_to_nm(self.flight.from_cp.position.distance_to_point(target.location.position))) + " nm") + self.threatRangeLabel.setText(str(meter_to_nm(target.threat_range)) + " nm") + self.detectionRangeLabel.setText(str(meter_to_nm(target.detection_range)) + " nm") + + def init_ui(self): + layout = QVBoxLayout() + + wpt_layout = QHBoxLayout() + wpt_layout.addWidget(QLabel("SEAD/DEAD target : ")) + wpt_layout.addStretch() + wpt_layout.addWidget(self.tgt_selection_box, alignment=Qt.AlignRight) + + distToTarget = QHBoxLayout() + distToTarget.addWidget(QLabel("Distance to site : ")) + distToTarget.addStretch() + distToTarget.addWidget(self.distanceToTargetLabel, alignment=Qt.AlignRight) + + threatRangeLayout = QHBoxLayout() + threatRangeLayout.addWidget(QLabel("Site threat range : ")) + threatRangeLayout.addStretch() + threatRangeLayout.addWidget(self.threatRangeLabel, alignment=Qt.AlignRight) + + detectionRangeLayout = QHBoxLayout() + detectionRangeLayout.addWidget(QLabel("Site radar detection range: ")) + detectionRangeLayout.addStretch() + detectionRangeLayout.addWidget(self.detectionRangeLabel, alignment=Qt.AlignRight) + + layout.addLayout(wpt_layout) + layout.addLayout(distToTarget) + layout.addLayout(threatRangeLayout) + layout.addLayout(detectionRangeLayout) + layout.addStretch() + layout.addWidget(self.ok_button) + + self.setLayout(layout) + + def apply(self): + self.flight.points = [] + target = self.tgt_selection_box.get_selected_target() + self.planner.generate_sead(self.flight, target.location, target.radars) + self.flight_waypoint_list.update_list() + self.close() + + + + diff --git a/qt_ui/windows/mission/flight/generator/QSTRIKEMissionGenerator.py b/qt_ui/windows/mission/flight/generator/QSTRIKEMissionGenerator.py new file mode 100644 index 00000000..7d7477d3 --- /dev/null +++ b/qt_ui/windows/mission/flight/generator/QSTRIKEMissionGenerator.py @@ -0,0 +1,57 @@ +from PySide2.QtGui import Qt +from PySide2.QtWidgets import QLabel, QHBoxLayout, QVBoxLayout + +from game import Game +from gen.flights.ai_flight_planner import meter_to_nm +from gen.flights.flight import Flight +from qt_ui.widgets.combos.QStrikeTargetSelectionComboBox import QStrikeTargetSelectionComboBox +from qt_ui.windows.mission.flight.generator.QAbstractMissionGenerator import QAbstractMissionGenerator + + +class QSTRIKEMissionGenerator(QAbstractMissionGenerator): + + def __init__(self, game: Game, flight: Flight, flight_waypoint_list): + super(QSTRIKEMissionGenerator, self).__init__(game, flight, flight_waypoint_list, "SEAD/DEAD Generator") + + self.tgt_selection_box = QStrikeTargetSelectionComboBox(self.game) + self.tgt_selection_box.setMinimumWidth(200) + self.tgt_selection_box.currentTextChanged.connect(self.on_selected_target_changed) + + self.distanceToTargetLabel = QLabel("0 nm") + self.init_ui() + self.on_selected_target_changed() + + def on_selected_target_changed(self): + target = self.tgt_selection_box.get_selected_target() + self.distanceToTargetLabel.setText("~" + str(meter_to_nm(self.flight.from_cp.position.distance_to_point(target.location.position))) + " nm") + + def init_ui(self): + layout = QVBoxLayout() + + wpt_layout = QHBoxLayout() + wpt_layout.addWidget(QLabel("SEAD/DEAD target : ")) + wpt_layout.addStretch() + wpt_layout.addWidget(self.tgt_selection_box, alignment=Qt.AlignRight) + + distToTarget = QHBoxLayout() + distToTarget.addWidget(QLabel("Distance to target : ")) + distToTarget.addStretch() + distToTarget.addWidget(self.distanceToTargetLabel, alignment=Qt.AlignRight) + + layout.addLayout(wpt_layout) + layout.addLayout(distToTarget) + layout.addStretch() + layout.addWidget(self.ok_button) + + self.setLayout(layout) + + def apply(self): + self.flight.points = [] + # target = self.tgt_selection_box.get_selected_target() + # self.planner.generate_sead(self.flight, target.location, target.radars) + # self.flight_waypoint_list.update_list() + # self.close() + + + + diff --git a/qt_ui/windows/mission/flight/waypoints/QFlightWaypointTab.py b/qt_ui/windows/mission/flight/waypoints/QFlightWaypointTab.py index 1b5f31b0..d08e94d5 100644 --- a/qt_ui/windows/mission/flight/waypoints/QFlightWaypointTab.py +++ b/qt_ui/windows/mission/flight/waypoints/QFlightWaypointTab.py @@ -1,6 +1,10 @@ from PySide2.QtWidgets import QFrame, QGridLayout, QLabel, QPushButton, QVBoxLayout -from gen.flights.flight import Flight +from gen.flights.flight import Flight, FlightWaypoint, FlightWaypointType +from qt_ui.windows.mission.flight.generator.QCAPMissionGenerator import QCAPMissionGenerator +from qt_ui.windows.mission.flight.generator.QCASMissionGenerator import QCASMissionGenerator +from qt_ui.windows.mission.flight.generator.QSEADMissionGenerator import QSEADMissionGenerator +from qt_ui.windows.mission.flight.generator.QSTRIKEMissionGenerator import QSTRIKEMissionGenerator from qt_ui.windows.mission.flight.waypoints.QFlightWaypointList import QFlightWaypointList from qt_ui.windows.mission.flight.waypoints.QPredefinedWaypointSelectionWindow import QPredefinedWaypointSelectionWindow from game import Game @@ -11,6 +15,7 @@ class QFlightWaypointTab(QFrame): super(QFlightWaypointTab, self).__init__() self.flight = flight self.game = game + self.planner = self.game.planners[self.flight.from_cp.id] self.init_ui() def init_ui(self): @@ -19,10 +24,44 @@ class QFlightWaypointTab(QFrame): self.flight_waypoint_list = QFlightWaypointList(self.flight) self.open_fast_waypoint_button = QPushButton("Add Waypoint") self.open_fast_waypoint_button.clicked.connect(self.on_fast_waypoint) + + self.cas_generator = QPushButton("Gen. CAS") + self.cas_generator.clicked.connect(self.on_cas_generator) + + self.cap_generator = QPushButton("Gen. CAP") + self.cap_generator.clicked.connect(self.on_cap_generator) + + self.sead_generator = QPushButton("Gen. SEAD/DEAD") + self.sead_generator.clicked.connect(self.on_sead_generator) + + self.strike_generator = QPushButton("Gen. STRIKE") + self.strike_generator.clicked.connect(self.on_strike_generator) + + self.rtb_waypoint = QPushButton("Add RTB Waypoint") + self.rtb_waypoint.clicked.connect(self.on_rtb_waypoint) + + self.ascend_waypoint = QPushButton("Add Ascend Waypoint") + self.ascend_waypoint.clicked.connect(self.on_ascend_waypoint) + + self.descend_waypoint = QPushButton("Add Descend Waypoint") + self.descend_waypoint.clicked.connect(self.on_descend_waypoint) + self.delete_selected = QPushButton("Delete Selected") self.delete_selected.clicked.connect(self.on_delete_waypoint) - layout.addWidget(self.flight_waypoint_list,0,0) + layout.addWidget(self.flight_waypoint_list, 0, 0) + + rlayout.addWidget(QLabel("Generator :")) + rlayout.addWidget(QLabel("AI compatible")) + rlayout.addWidget(self.cas_generator) + rlayout.addWidget(self.cap_generator) + rlayout.addWidget(self.sead_generator) + rlayout.addWidget(self.strike_generator) + rlayout.addWidget(QLabel("Advanced : ")) + rlayout.addWidget(QLabel("Do not use for AI flights")) + rlayout.addWidget(self.ascend_waypoint) + rlayout.addWidget(self.descend_waypoint) + rlayout.addWidget(self.rtb_waypoint) rlayout.addWidget(self.open_fast_waypoint_button) rlayout.addWidget(self.delete_selected) rlayout.addStretch() @@ -37,4 +76,36 @@ class QFlightWaypointTab(QFrame): def on_fast_waypoint(self): self.subwindow = QPredefinedWaypointSelectionWindow(self.game, self.flight, self.flight_waypoint_list) - self.subwindow.show() \ No newline at end of file + self.subwindow.show() + + def on_ascend_waypoint(self): + ascend = self.planner.generate_ascend_point(self.flight.from_cp) + self.flight.points.append(ascend) + self.flight_waypoint_list.update_list() + + def on_rtb_waypoint(self): + rtb = self.planner.generate_rtb_waypoint(self.flight.from_cp) + self.flight.points.append(rtb) + self.flight_waypoint_list.update_list() + + def on_descend_waypoint(self): + descend = self.planner.generate_descend_point(self.flight.from_cp) + self.flight.points.append(descend) + self.flight_waypoint_list.update_list() + + def on_cas_generator(self): + self.subwindow = QCASMissionGenerator(self.game, self.flight, self.flight_waypoint_list) + self.subwindow.show() + + def on_cap_generator(self): + self.subwindow = QCAPMissionGenerator(self.game, self.flight, self.flight_waypoint_list) + self.subwindow.show() + + def on_sead_generator(self): + self.subwindow = QSEADMissionGenerator(self.game, self.flight, self.flight_waypoint_list) + self.subwindow.show() + + def on_strike_generator(self): + self.subwindow = QSTRIKEMissionGenerator(self.game, self.flight, self.flight_waypoint_list) + self.subwindow.show() + diff --git a/qt_ui/windows/mission/flight/waypoints/QPredefinedWaypointSelectionWindow.py b/qt_ui/windows/mission/flight/waypoints/QPredefinedWaypointSelectionWindow.py index f2f4323e..e7e2a90c 100644 --- a/qt_ui/windows/mission/flight/waypoints/QPredefinedWaypointSelectionWindow.py +++ b/qt_ui/windows/mission/flight/waypoints/QPredefinedWaypointSelectionWindow.py @@ -1,13 +1,11 @@ from PySide2.QtCore import Qt -from PySide2.QtWidgets import QDialog, QGridLayout, QLabel, QComboBox, QHBoxLayout, QVBoxLayout, QPushButton, QCheckBox -from dcs import Point +from PySide2.QtWidgets import QDialog, QLabel, QHBoxLayout, QVBoxLayout, QPushButton, QCheckBox from game import Game -from gen.flights.flight import Flight, FlightWaypoint +from gen.flights.flight import Flight from qt_ui.uiconstants import EVENT_ICONS -from qt_ui.widgets.QPredefinedWaypointSelectionComboBox import QPredefinedWaypointSelectionComboBox +from qt_ui.widgets.combos.QPredefinedWaypointSelectionComboBox import QPredefinedWaypointSelectionComboBox from qt_ui.windows.mission.flight.waypoints.QFlightWaypointInfoBox import QFlightWaypointInfoBox -from theater import ControlPointType PREDEFINED_WAYPOINT_CATEGORIES = [ "Frontline (CAS AREA)", diff --git a/resources/caumap.gif b/resources/caumap.gif index 56e9465e..7c2d9ea2 100644 Binary files a/resources/caumap.gif and b/resources/caumap.gif differ