Flight Planner first version (just print planned flight in log but does not generate them yet)

This commit is contained in:
Khopa 2019-10-15 23:06:21 +02:00
parent fa99df3ce7
commit b7ee98dcd6
7 changed files with 472 additions and 20 deletions

View File

@ -8,6 +8,7 @@ from dcs.vehicles import *
from game.game_stats import GameStats
from gen.conflictgen import Conflict
from gen.flights.ai_flight_planner import FlightPlanner
from userdata.debriefing import Debriefing
from theater import *
@ -111,6 +112,7 @@ class Game:
self.date = datetime(start_date.year, start_date.month, start_date.day)
self.game_stats = GameStats()
self.game_stats.update(self)
self.planners = {}
def _roll(self, prob, mult):
if self.settings.version == "dev":
@ -310,6 +312,15 @@ class Game:
# Update statistics
self.game_stats.update(self)
# Plan flights for next turn
self.planners = {}
for cp in self.theater.controlpoints:
planner = FlightPlanner(cp, self)
planner.plan_flights()
self.planners[cp.id] = planner
print(planner)
@property
def current_turn_daytime(self):
return ["dawn", "day", "dusk", "night"][self.turn % 4]

View File

@ -135,10 +135,9 @@ class Operation:
# Generate ground object first
self.groundobjectgen.generate()
# air support
# Air Support (Tanker & Awacs)
self.airsupportgen.generate(self.is_awacs_enabled)
# Generate Activity on the map
for cp in self.game.theater.controlpoints:
side = cp.captured
@ -157,20 +156,15 @@ class Operation:
self.airgen.generate_dead_sead(cp, country)
for i, tanker_type in enumerate(self.airsupportgen.generated_tankers):
self.briefinggen.append_frequency("Tanker {} ({})".format(TANKER_CALLSIGNS[i], tanker_type), "{}X/{} MHz AM".format(97+i, 130+i))
if self.is_awacs_enabled:
self.briefinggen.append_frequency("AWACS", "133 MHz AM")
# combined arms
#Setup combined arms parameters
self.current_mission.groundControl.pilot_can_control_vehicles = self.ca_slots > 0
if self.game.player_country in [country.name for country in self.current_mission.coalition["blue"].countries.values()]:
self.current_mission.groundControl.blue_tactical_commander = self.ca_slots
else:
self.current_mission.groundControl.red_tactical_commander = self.ca_slots
#self.extra_aagen.generate()
# triggers
if self.game.is_player_attack(self.conflict.attackers_country):
@ -192,18 +186,10 @@ class Operation:
# options
self.forcedoptionsgen.generate()
# main frequencies
self.briefinggen.append_frequency("Flight", "251 MHz AM")
if self.departure_cp.is_global or self.conflict.to_cp.is_global:
self.briefinggen.append_frequency("Carrier", "20X/ICLS CHAN1")
# briefing
self.briefinggen.generate()
# visuals
# Generate Visuals Smoke Effects
self.visualgen.generate()
# Scripts
# Inject Lua Scripts
load_mist = TriggerStart(comment="Load Mist Lua Framework")
with open(os.path.abspath("./resources/scripts/mist_4_3_74.lua")) as f:
load_mist.add_action(DoScript(String(f.read())))
@ -219,5 +205,19 @@ class Operation:
load_dcs_libe.add_action(DoScript(String(script)))
self.current_mission.triggerrules.triggers.append(load_dcs_libe)
# Briefing Generation
for i, tanker_type in enumerate(self.airsupportgen.generated_tankers):
self.briefinggen.append_frequency("Tanker {} ({})".format(TANKER_CALLSIGNS[i], tanker_type), "{}X/{} MHz AM".format(97+i, 130+i))
if self.is_awacs_enabled:
self.briefinggen.append_frequency("AWACS", "133 MHz AM")
self.briefinggen.append_frequency("Flight", "251 MHz AM")
if self.departure_cp.is_global or self.conflict.to_cp.is_global:
self.briefinggen.append_frequency("Carrier", "20X/ICLS CHAN1")
# Generate the briefing
self.briefinggen.generate()

View File

@ -0,0 +1,209 @@
import math
import operator
import typing
import random
from gen.flights.ai_flight_planner_db import INTERCEPT_CAPABLE, CAP_CAPABLE, CAS_CAPABLE
from gen.flights.flight import Flight, FlightType
# TODO : Ideally should be based on the aircraft type instead / Availability of fuel
STRIKE_MAX_RANGE = 30000
SEAD_MAX_RANGE = 30000
MAX_NUMBER_OF_INTERCEPTION_GROUP = 3
MISSION_DURATION = 120 # in minutes
CAP_EVERY_X_MINUTES = 20
class FlightPlanner:
from_cp = None
game = None
interceptor_flights = []
cap_flights = []
cas_flights = []
strike_flights = []
sead_flights = []
flights = []
def __init__(self, from_cp, game):
# TODO : have the flight planner depend on a 'stance' setting : [Defensive, Aggresive... etc] and faction doctrine
self.from_cp = from_cp
self.game = game
self.aircraft_inventory = {} # local copy of the airbase inventory
def reset(self):
"""
Reset the planned flights and avaialble units
"""
self.aircraft_inventory = dict({k: v for k, v in self.from_cp.base.aircraft.items()})
self.interceptor_flights = []
self.cap_flights = []
self.cas_flights = []
self.strike_flights = []
self.sead_flights = []
self.flights = []
def plan_flights(self):
self.reset()
# 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()
# Then some CAP patrol for the next 2 hours
self.commision_barcap()
# TODO : commision CAS / BAI
# TODO : commision SEAD
# TODO : commision STRIKE / ANTISHIP
def commision_interceptors(self):
"""
Pick some aircraft to assign them to interception roles
"""
# At least try to generate one interceptor group
number_of_interceptor_groups = min(max(sum([v for k, v in self.aircraft_inventory.items()]) / 4, MAX_NUMBER_OF_INTERCEPTION_GROUP), 1)
possible_interceptors = [k for k in self.aircraft_inventory.keys() if k in INTERCEPT_CAPABLE]
if len(possible_interceptors) <= 0:
possible_interceptors = [k for k,v in self.aircraft_inventory.items() if k in CAP_CAPABLE and v >= 2]
if number_of_interceptor_groups > 0:
inventory = dict({k: v for k, v in self.aircraft_inventory.items() if k in possible_interceptors})
for i in range(number_of_interceptor_groups):
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.INTERCEPTION)
flight.points = []
self.interceptor_flights.append(flight)
self.flights.append(flight)
# Update inventory
for k, v in inventory.items():
self.aircraft_inventory[k] = v
def commision_barcap(self):
"""
Pick some aircraft to assign them to defensive CAP roles (BARCAP)
"""
possible_aircraft = [k for k, v in self.aircraft_inventory.items() if k in CAP_CAPABLE and v >= 2]
inventory = dict({k: v for k, v in self.aircraft_inventory.items() if k in possible_aircraft})
offset = random.randint(0,5)
for i in range(int(MISSION_DURATION/CAP_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.BARCAP)
# Flight path : fly over each ground object (TODO : improve)
flight.points = []
flight.scheduled_in = offset + i*random.randint(CAP_EVERY_X_MINUTES-5, CAP_EVERY_X_MINUTES+5)
patrol_alt = random.randint(3600, 7000)
patrolled = []
for ground_object in self.from_cp.ground_objects:
if ground_object.group_id not in patrolled and not ground_object.airbase_group:
flight.points.append([ground_object.position.x, ground_object.position.y, patrol_alt])
patrolled.append(ground_object.group_id)
self.cap_flights.append(flight)
self.flights.append(flight)
# Update inventory
for k, v in inventory.items():
self.aircraft_inventory[k] = v
def _get_strike_targets_in_range(self):
"""
@return a list of potential strike targets in range
"""
# target, distance
potential_targets = []
for cp in [c for c in self.game.theater.controlpoints if c.captured != self.from_cp.captured]:
# Compute distance to current cp
distance = math.hypot(cp.position.x - self.from_cp.position.x,
cp.position.y - self.from_cp.position.y)
if distance > 2*STRIKE_MAX_RANGE:
# Then it's unlikely any child ground object is in range
return
added_group = []
for g in cp.ground_objects:
if g.group_id in added_group: continue
# Compute distance to current cp
distance = math.hypot(cp.position.x - self.from_cp.position.x,
cp.position.y - self.from_cp.position.y)
if distance < SEAD_MAX_RANGE:
potential_targets.append((g, distance))
added_group.append(g)
return potential_targets.sort(key=operator.itemgetter(1))
def _get_sead_targets_in_range(self):
"""
@return a list of potential sead targets in range
"""
# target, distance
potential_targets = []
for cp in [c for c in self.game.theater.controlpoints if c.captured != self.from_cp.captured]:
# Compute distance to current cp
distance = math.hypot(cp.position.x - self.from_cp.position.x,
cp.position.y - self.from_cp.position.y)
# Then it's unlikely any ground object is range
if distance > 2*SEAD_MAX_RANGE:
return
for g in cp.ground_objects:
if g.dcs_identifier == "AA":
# 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])
if number_of_units <= 0:
continue
# Compute distance to current cp
distance = math.hypot(cp.position.x - self.from_cp.position.x,
cp.position.y - self.from_cp.position.y)
if distance < SEAD_MAX_RANGE:
potential_targets.append((g, distance))
return potential_targets.sort(key=operator.itemgetter(1))
def __repr__(self):
return "-"*40 + "\n" + self.from_cp.name + " planned flights :\n"\
+ "-"*40 + "\n" + "\n".join([repr(f) for f in self.flights]) + "\n" + "-"*40

View File

@ -0,0 +1,177 @@
from dcs.planes import *
from dcs.helicopters import *
# Interceptor are the aircraft prioritized for interception tasks
# If none is available, the AI will use regular CAP-capable aircraft instead
INTERCEPT_CAPABLE = [
MiG_21Bis,
MiG_25PD,
MiG_31,
M_2000C,
Mirage_2000_5,
F_14B,
F_15C,
]
# Used for CAP, Escort, and intercept if there is not a specialised aircraft available
CAP_CAPABLE = [
MiG_15bis,
MiG_19P,
MiG_21Bis,
MiG_23MLD,
MiG_29A,
MiG_29G,
MiG_29S,
Su_27,
J_11A,
Su_30,
Su_33,
M_2000C,
Mirage_2000_5,
F_86F_Sabre,
F_4E,
F_5E_3,
F_14B,
F_15C,
F_16C_50,
FA_18C_hornet,
C_101CC,
L_39ZA,
P_51D_30_NA,
P_51D,
SpitfireLFMkIXCW,
SpitfireLFMkIX,
Bf_109K_4,
FW_190D9,
FW_190A8,
]
# USed for CAS (Close air support) and BAI (Battlefield Interdiction)
CAS_CAPABLE = [
MiG_15bis,
MiG_29A,
MiG_27K,
MiG_29S,
Su_17M4,
Su_24M,
Su_24MR,
Su_25,
Su_25T,
Su_34,
M_2000C,
A_10A,
A_10C,
AV8BNA,
F_86F_Sabre,
F_5E_3,
F_14B,
F_16C_50,
FA_18C_hornet,
C_101CC,
L_39ZA,
AJS37,
SA342M,
SA342L,
AH_64A,
AH_64D,
UH_1H,
Mi_8MT,
Mi_28N,
Mi_24V,
Ka_50,
P_51D_30_NA,
P_51D,
SpitfireLFMkIXCW,
SpitfireLFMkIX,
Bf_109K_4,
FW_190D9,
FW_190A8,
]
# Aircraft used for SEAD / DEAD tasks
SEAD_CAPABLE = [
F_4E,
FA_18C_hornet,
F_16C_50,
AV8BNA,
Su_24M,
Su_25T,
Su_25TM,
Su_17M4,
Su_30,
Su_34,
MiG_27K,
]
# Aircraft used for Strike mission
STRIKE_CAPABLE = [
MiG_15bis,
MiG_29A,
MiG_27K,
MiG_29S,
Su_17M4,
Su_24M,
Su_24MR,
Su_25,
Su_25T,
Su_34,
M_2000C,
A_10A,
A_10C,
AV8BNA,
F_86F_Sabre,
F_5E_3,
F_14B,
F_16C_50,
FA_18C_hornet,
C_101CC,
L_39ZA,
AJS37,
M_2000C,
P_51D_30_NA,
P_51D,
SpitfireLFMkIXCW,
SpitfireLFMkIX,
Bf_109K_4,
FW_190D9,
FW_190A8,
]
ANTISHIP_CAPABLE = [
Su_24M,
F_A_18C,
AV8BNA,
]

56
gen/flights/flight.py Normal file
View File

@ -0,0 +1,56 @@
from dcs.unittype import UnitType
from enum import Enum
from game import db
class FlightType(Enum):
CAP = 0
TARCAP = 1
BARCAP = 2
CAS = 3
INTERCEPTION = 4
STRIKE = 5
ANTISHIP = 6
SEAD = 7
DEAD = 8
ESCORT = 9
BAI = 10
# Helos
TROOP_TRANSPORT = 11
LOGISTICS = 12
EVAC = 13
class Flight:
unit_type: UnitType
from_cp = None
points = []
type = ""
count = 0
client_count = 0
# How long before this flight should take off
scheduled_in = 0
def __init__(self, unit_type: UnitType, count: int, from_cp, flight_type: FlightType):
self.unit_type = unit_type
self.count = count
self.from_cp = from_cp
self.flight_type = flight_type
def __repr__(self):
return self.flight_type.name + " | " + str(self.count) + "x" + db.unit_type_name(self.unit_type)\
+ " in " + str(self.scheduled_in) + " minutes (" + str(len(self.points)) + " wpt)"
# Test
if __name__ == '__main__':
from dcs.planes import A_10C
from theater import ControlPoint, Point
from_cp = ControlPoint(0, "AA", Point(0,0), None, [], 0, 0)
f = Flight(A_10C, 4, from_cp, FlightType.CAS)
f.scheduled_in = 50
print(f)

BIN
resources/normandy.gif Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 369 KiB

View File

@ -1,8 +1,7 @@
import typing
import re
import typing
from dcs.mapping import *
from dcs.country import *
from dcs.terrain import Airport
from .theatergroundobject import TheaterGroundObject