mirror of
https://github.com/dcs-liberation/dcs_liberation.git
synced 2025-11-10 14:22:26 +00:00
AI Flight planner generate CAS & SEAD
This commit is contained in:
parent
b7ee98dcd6
commit
827138fc6a
@ -3,17 +3,20 @@ import operator
|
|||||||
import typing
|
import typing
|
||||||
import random
|
import random
|
||||||
|
|
||||||
from gen.flights.ai_flight_planner_db import INTERCEPT_CAPABLE, CAP_CAPABLE, CAS_CAPABLE
|
from game import db
|
||||||
|
from gen.flights.ai_flight_planner_db import INTERCEPT_CAPABLE, CAP_CAPABLE, CAS_CAPABLE, SEAD_CAPABLE
|
||||||
from gen.flights.flight import Flight, FlightType
|
from gen.flights.flight import Flight, FlightType
|
||||||
|
|
||||||
|
|
||||||
# TODO : Ideally should be based on the aircraft type instead / Availability of fuel
|
# TODO : Ideally should be based on the aircraft type instead / Availability of fuel
|
||||||
STRIKE_MAX_RANGE = 30000
|
STRIKE_MAX_RANGE = 150000
|
||||||
SEAD_MAX_RANGE = 30000
|
SEAD_MAX_RANGE = 150000
|
||||||
|
|
||||||
MAX_NUMBER_OF_INTERCEPTION_GROUP = 3
|
MAX_NUMBER_OF_INTERCEPTION_GROUP = 3
|
||||||
MISSION_DURATION = 120 # in minutes
|
MISSION_DURATION = 120 # in minutes
|
||||||
CAP_EVERY_X_MINUTES = 20
|
CAP_EVERY_X_MINUTES = 20
|
||||||
|
CAS_EVERY_X_MINUTES = 30
|
||||||
|
SEAD_EVERY_X_MINUTES = 40
|
||||||
|
|
||||||
|
|
||||||
class FlightPlanner:
|
class FlightPlanner:
|
||||||
@ -28,6 +31,9 @@ class FlightPlanner:
|
|||||||
sead_flights = []
|
sead_flights = []
|
||||||
flights = []
|
flights = []
|
||||||
|
|
||||||
|
potential_sead_targets = []
|
||||||
|
potential_strike_targets = []
|
||||||
|
|
||||||
def __init__(self, from_cp, game):
|
def __init__(self, from_cp, game):
|
||||||
# TODO : have the flight planner depend on a 'stance' setting : [Defensive, Aggresive... etc] and faction doctrine
|
# TODO : have the flight planner depend on a 'stance' setting : [Defensive, Aggresive... etc] and faction doctrine
|
||||||
self.from_cp = from_cp
|
self.from_cp = from_cp
|
||||||
@ -45,10 +51,14 @@ class FlightPlanner:
|
|||||||
self.strike_flights = []
|
self.strike_flights = []
|
||||||
self.sead_flights = []
|
self.sead_flights = []
|
||||||
self.flights = []
|
self.flights = []
|
||||||
|
self.potential_sead_targets = []
|
||||||
|
self.potential_strike_targets = []
|
||||||
|
|
||||||
def plan_flights(self):
|
def plan_flights(self):
|
||||||
|
|
||||||
self.reset()
|
self.reset()
|
||||||
|
self.compute_sead_targets()
|
||||||
|
self.compute_strike_targets()
|
||||||
|
|
||||||
# The priority is to assign air-superiority fighter or interceptor to interception roles, so they can scramble if there is an attacker
|
# The priority is to assign air-superiority fighter or interceptor to interception roles, so they can scramble if there is an attacker
|
||||||
self.commision_interceptors()
|
self.commision_interceptors()
|
||||||
@ -56,9 +66,11 @@ class FlightPlanner:
|
|||||||
# Then some CAP patrol for the next 2 hours
|
# Then some CAP patrol for the next 2 hours
|
||||||
self.commision_barcap()
|
self.commision_barcap()
|
||||||
|
|
||||||
# TODO : commision CAS / BAI
|
# Then setup cas
|
||||||
|
self.commision_cas()
|
||||||
|
|
||||||
# TODO : commision SEAD
|
# Then prepare some sead flights if required
|
||||||
|
self.commision_sead()
|
||||||
|
|
||||||
# TODO : commision STRIKE / ANTISHIP
|
# TODO : commision STRIKE / ANTISHIP
|
||||||
|
|
||||||
@ -130,13 +142,95 @@ class FlightPlanner:
|
|||||||
for k, v in inventory.items():
|
for k, v in inventory.items():
|
||||||
self.aircraft_inventory[k] = v
|
self.aircraft_inventory[k] = v
|
||||||
|
|
||||||
def _get_strike_targets_in_range(self):
|
def commision_cas(self):
|
||||||
|
"""
|
||||||
|
Pick some aircraft to assign them to CAS
|
||||||
|
"""
|
||||||
|
|
||||||
|
possible_aircraft = [k for k, v in self.aircraft_inventory.items() if k in CAS_CAPABLE and v >= 2]
|
||||||
|
inventory = dict({k: v for k, v in self.aircraft_inventory.items() if k in possible_aircraft})
|
||||||
|
cas_location = self._get_cas_locations()
|
||||||
|
|
||||||
|
if len(cas_location) > 0:
|
||||||
|
|
||||||
|
offset = random.randint(0,5)
|
||||||
|
for i in range(int(MISSION_DURATION/CAS_EVERY_X_MINUTES)):
|
||||||
|
|
||||||
|
try:
|
||||||
|
unit = random.choice([k for k, v in inventory.items() if v >= 2])
|
||||||
|
except IndexError:
|
||||||
|
break
|
||||||
|
|
||||||
|
inventory[unit] = inventory[unit] - 2
|
||||||
|
flight = Flight(unit, 2, self.from_cp, FlightType.CAS)
|
||||||
|
|
||||||
|
# Flight path : fly over each ground object (TODO : improve)
|
||||||
|
flight.points = []
|
||||||
|
flight.scheduled_in = offset + i*random.randint(CAS_EVERY_X_MINUTES-5, CAS_EVERY_X_MINUTES+5)
|
||||||
|
|
||||||
|
location = random.choice(cas_location)
|
||||||
|
flight.points.append([location[0], location[1], 1000]) # TODO : Egress / Ingress points
|
||||||
|
|
||||||
|
self.cas_flights.append(flight)
|
||||||
|
self.flights.append(flight)
|
||||||
|
|
||||||
|
# Update inventory
|
||||||
|
for k, v in inventory.items():
|
||||||
|
self.aircraft_inventory[k] = v
|
||||||
|
|
||||||
|
def commision_sead(self):
|
||||||
|
"""
|
||||||
|
Pick some aircraft to assign them to SEAD tasks
|
||||||
|
"""
|
||||||
|
|
||||||
|
possible_aircraft = [k for k, v in self.aircraft_inventory.items() if k in SEAD_CAPABLE and v >= 2]
|
||||||
|
inventory = dict({k: v for k, v in self.aircraft_inventory.items() if k in possible_aircraft})
|
||||||
|
|
||||||
|
if len(self.potential_sead_targets) > 0:
|
||||||
|
|
||||||
|
offset = random.randint(0,5)
|
||||||
|
for i in range(int(MISSION_DURATION/SEAD_EVERY_X_MINUTES)):
|
||||||
|
|
||||||
|
if len(self.potential_sead_targets) <= 0:
|
||||||
|
break
|
||||||
|
|
||||||
|
try:
|
||||||
|
unit = random.choice([k for k, v in inventory.items() if v >= 2])
|
||||||
|
except IndexError:
|
||||||
|
break
|
||||||
|
|
||||||
|
inventory[unit] = inventory[unit] - 2
|
||||||
|
flight = Flight(unit, 2, self.from_cp, random.choice([FlightType.SEAD, FlightType.DEAD]))
|
||||||
|
|
||||||
|
# Flight path : fly over each ground object (TODO : improve)
|
||||||
|
flight.points = []
|
||||||
|
flight.scheduled_in = offset + i*random.randint(SEAD_EVERY_X_MINUTES-5, SEAD_EVERY_X_MINUTES+5)
|
||||||
|
|
||||||
|
location = self.potential_sead_targets[0][0]
|
||||||
|
self.potential_sead_targets.pop(0)
|
||||||
|
flight.points.append([location.position.x, location.position.y, 1000]) # TODO : Egress / Ingress points
|
||||||
|
|
||||||
|
self.cas_flights.append(flight)
|
||||||
|
self.flights.append(flight)
|
||||||
|
|
||||||
|
# Update inventory
|
||||||
|
for k, v in inventory.items():
|
||||||
|
self.aircraft_inventory[k] = v
|
||||||
|
|
||||||
|
def _get_cas_locations(self):
|
||||||
|
cas_locations = []
|
||||||
|
for cp in self.from_cp.connected_points:
|
||||||
|
if cp.captured != self.from_cp.captured:
|
||||||
|
cas_locations.append(((self.from_cp.position.x + cp.position.x)/2, (self.from_cp.position.y + cp.position.y)/2))
|
||||||
|
return cas_locations
|
||||||
|
|
||||||
|
def compute_strike_targets(self):
|
||||||
"""
|
"""
|
||||||
@return a list of potential strike targets in range
|
@return a list of potential strike targets in range
|
||||||
"""
|
"""
|
||||||
|
|
||||||
# target, distance
|
# target, distance
|
||||||
potential_targets = []
|
self.potential_strike_targets = []
|
||||||
|
|
||||||
for cp in [c for c in self.game.theater.controlpoints if c.captured != self.from_cp.captured]:
|
for cp in [c for c in self.game.theater.controlpoints if c.captured != self.from_cp.captured]:
|
||||||
|
|
||||||
@ -157,18 +251,18 @@ class FlightPlanner:
|
|||||||
cp.position.y - self.from_cp.position.y)
|
cp.position.y - self.from_cp.position.y)
|
||||||
|
|
||||||
if distance < SEAD_MAX_RANGE:
|
if distance < SEAD_MAX_RANGE:
|
||||||
potential_targets.append((g, distance))
|
self.potential_strike_targets.append((g, distance))
|
||||||
added_group.append(g)
|
added_group.append(g)
|
||||||
|
|
||||||
return potential_targets.sort(key=operator.itemgetter(1))
|
self.potential_strike_targets.sort(key=operator.itemgetter(1))
|
||||||
|
|
||||||
def _get_sead_targets_in_range(self):
|
def compute_sead_targets(self):
|
||||||
"""
|
"""
|
||||||
@return a list of potential sead targets in range
|
@return a list of potential sead targets in range
|
||||||
"""
|
"""
|
||||||
|
|
||||||
# target, distance
|
# target, distance
|
||||||
potential_targets = []
|
self.potential_sead_targets = []
|
||||||
|
|
||||||
for cp in [c for c in self.game.theater.controlpoints if c.captured != self.from_cp.captured]:
|
for cp in [c for c in self.game.theater.controlpoints if c.captured != self.from_cp.captured]:
|
||||||
|
|
||||||
@ -185,7 +279,8 @@ class FlightPlanner:
|
|||||||
if g.dcs_identifier == "AA":
|
if g.dcs_identifier == "AA":
|
||||||
|
|
||||||
# Check that there is at least one unit with a radar in the ground objects unit groups
|
# Check that there is at least one unit with a radar in the ground objects unit groups
|
||||||
number_of_units = sum([len([r for r in group.units if hasattr(r, "detection_range") and r.detection_range > 1000]) for group in g.groups])
|
number_of_units = sum([len([r for r in group.units if hasattr(db.unit_type_from_name(r.type), "detection_range")
|
||||||
|
and db.unit_type_from_name(r.type).detection_range > 1000]) for group in g.groups])
|
||||||
if number_of_units <= 0:
|
if number_of_units <= 0:
|
||||||
continue
|
continue
|
||||||
|
|
||||||
@ -194,9 +289,9 @@ class FlightPlanner:
|
|||||||
cp.position.y - self.from_cp.position.y)
|
cp.position.y - self.from_cp.position.y)
|
||||||
|
|
||||||
if distance < SEAD_MAX_RANGE:
|
if distance < SEAD_MAX_RANGE:
|
||||||
potential_targets.append((g, distance))
|
self.potential_sead_targets.append((g, distance))
|
||||||
|
|
||||||
return potential_targets.sort(key=operator.itemgetter(1))
|
self.potential_sead_targets.sort(key=operator.itemgetter(1))
|
||||||
|
|
||||||
def __repr__(self):
|
def __repr__(self):
|
||||||
return "-"*40 + "\n" + self.from_cp.name + " planned flights :\n"\
|
return "-"*40 + "\n" + self.from_cp.name + " planned flights :\n"\
|
||||||
|
|||||||
@ -69,6 +69,7 @@ CAS_CAPABLE = [
|
|||||||
Su_24MR,
|
Su_24MR,
|
||||||
Su_25,
|
Su_25,
|
||||||
Su_25T,
|
Su_25T,
|
||||||
|
Su_25TM,
|
||||||
Su_34,
|
Su_34,
|
||||||
|
|
||||||
M_2000C,
|
M_2000C,
|
||||||
|
|||||||
@ -21,6 +21,10 @@ class FlightType(Enum):
|
|||||||
LOGISTICS = 12
|
LOGISTICS = 12
|
||||||
EVAC = 13
|
EVAC = 13
|
||||||
|
|
||||||
|
ELINT = 14
|
||||||
|
RECON = 15
|
||||||
|
EWAR = 16
|
||||||
|
|
||||||
|
|
||||||
class Flight:
|
class Flight:
|
||||||
|
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user