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