mirror of
https://github.com/dcs-liberation/dcs_liberation.git
synced 2025-11-10 14:22:26 +00:00
AI flight planner now auto generate STRIKE flights.
Fix CAS point position in predefined wpt selector. When an airbase is captured, base defenses are re-generated for the new base owner.
This commit is contained in:
parent
03a1c44659
commit
ab3ea84d70
@ -13,6 +13,7 @@ from theater import *
|
|||||||
from gen.environmentgen import EnvironmentSettings
|
from gen.environmentgen import EnvironmentSettings
|
||||||
from gen.conflictgen import Conflict
|
from gen.conflictgen import Conflict
|
||||||
from game.db import assigned_units_from, unitdict_from
|
from game.db import assigned_units_from, unitdict_from
|
||||||
|
from theater.start_generator import generate_airbase_defense_group
|
||||||
|
|
||||||
from userdata.debriefing import Debriefing
|
from userdata.debriefing import Debriefing
|
||||||
from userdata import persistency
|
from userdata import persistency
|
||||||
@ -214,28 +215,35 @@ class Event:
|
|||||||
|
|
||||||
for cp in self.game.theater.controlpoints:
|
for cp in self.game.theater.controlpoints:
|
||||||
if cp.id == id:
|
if cp.id == id:
|
||||||
|
|
||||||
|
pname = ""
|
||||||
if cp.captured and new_owner_coalition != coalition:
|
if cp.captured and new_owner_coalition != coalition:
|
||||||
cp.captured = False
|
cp.captured = False
|
||||||
cp.base.aircraft = {}
|
|
||||||
cp.base.armor = {}
|
|
||||||
cp.base.aa = {}
|
|
||||||
for g in cp.ground_objects:
|
|
||||||
g.groups = []
|
|
||||||
info = Information(cp.name + " lost !",
|
info = Information(cp.name + " lost !",
|
||||||
"The ennemy took control of " + cp.name + "\nShame on us !",
|
"The ennemy took control of " + cp.name + "\nShame on us !",
|
||||||
self.game.turn)
|
self.game.turn)
|
||||||
self.game.informations.append(info)
|
self.game.informations.append(info)
|
||||||
|
pname = self.game.enemy_name
|
||||||
elif not(cp.captured) and new_owner_coalition == coalition:
|
elif not(cp.captured) and new_owner_coalition == coalition:
|
||||||
cp.captured = True
|
cp.captured = True
|
||||||
|
info = Information(cp.name + " captured !", "The ennemy took control of " + cp.name + "\nShame on us !", self.game.turn)
|
||||||
|
self.game.informations.append(info)
|
||||||
|
pname = self.game.player_name
|
||||||
|
else:
|
||||||
|
continue
|
||||||
|
|
||||||
cp.base.aircraft = {}
|
cp.base.aircraft = {}
|
||||||
cp.base.armor = {}
|
cp.base.armor = {}
|
||||||
cp.base.aa = {}
|
cp.base.aa = {}
|
||||||
|
|
||||||
|
airbase_def_id = 0
|
||||||
for g in cp.ground_objects:
|
for g in cp.ground_objects:
|
||||||
g.groups = []
|
g.groups = []
|
||||||
info = Information(cp.name + " captured !",
|
if g.airbase_group and pname != "":
|
||||||
"The ennemy took control of " + cp.name + "\nShame on us !",
|
generate_airbase_defense_group(airbase_def_id, g, pname, self.game, cp)
|
||||||
self.game.turn)
|
airbase_def_id = airbase_def_id + 1
|
||||||
self.game.informations.append(info)
|
|
||||||
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
print(e)
|
print(e)
|
||||||
|
|
||||||
|
|||||||
21
game/game.py
21
game/game.py
@ -1,24 +1,11 @@
|
|||||||
import logging
|
from datetime import datetime, timedelta
|
||||||
import typing
|
|
||||||
import random
|
|
||||||
import math
|
|
||||||
|
|
||||||
from dcs.task import *
|
|
||||||
from dcs.vehicles import *
|
|
||||||
|
|
||||||
from game.db import REWARDS, PLAYER_BUDGET_BASE
|
from game.db import REWARDS, PLAYER_BUDGET_BASE
|
||||||
from game.game_stats import GameStats
|
from game.game_stats import GameStats
|
||||||
from game.infos.information import Information
|
|
||||||
from gen.conflictgen import Conflict
|
|
||||||
from gen.flights.ai_flight_planner import FlightPlanner
|
from gen.flights.ai_flight_planner import FlightPlanner
|
||||||
from gen.ground_forces.ai_ground_planner import GroundPlanner
|
from gen.ground_forces.ai_ground_planner import GroundPlanner
|
||||||
from userdata.debriefing import Debriefing
|
|
||||||
from theater import *
|
|
||||||
|
|
||||||
from . import db
|
|
||||||
from .settings import Settings
|
|
||||||
from .event import *
|
from .event import *
|
||||||
from datetime import datetime, timedelta
|
from .settings import Settings
|
||||||
|
|
||||||
COMMISION_UNIT_VARIETY = 4
|
COMMISION_UNIT_VARIETY = 4
|
||||||
COMMISION_LIMITS_SCALE = 1.5
|
COMMISION_LIMITS_SCALE = 1.5
|
||||||
@ -230,6 +217,10 @@ class Game:
|
|||||||
self.ground_planners[cp.id] = gplanner
|
self.ground_planners[cp.id] = gplanner
|
||||||
|
|
||||||
def _enemy_reinforcement(self):
|
def _enemy_reinforcement(self):
|
||||||
|
"""
|
||||||
|
Compute and commision reinforcement for enemy bases
|
||||||
|
"""
|
||||||
|
|
||||||
MAX_ARMOR = 30 * self.settings.multiplier
|
MAX_ARMOR = 30 * self.settings.multiplier
|
||||||
MAX_AIRCRAFT = 25 * self.settings.multiplier
|
MAX_AIRCRAFT = 25 * self.settings.multiplier
|
||||||
|
|
||||||
|
|||||||
271
gen/aircraft.py
271
gen/aircraft.py
@ -327,6 +327,11 @@ class AircraftConflictGenerator:
|
|||||||
self.setup_group_as_sead_flight(group, flight)
|
self.setup_group_as_sead_flight(group, flight)
|
||||||
self._setup_custom_payload(flight, group)
|
self._setup_custom_payload(flight, group)
|
||||||
|
|
||||||
|
for flight in flight_planner.strike_flights:
|
||||||
|
group = self.generate_planned_flight(cp, country, flight)
|
||||||
|
self.setup_group_as_strike_flight(group, flight)
|
||||||
|
self._setup_custom_payload(flight, group)
|
||||||
|
|
||||||
for flight in flight_planner.custom_flights:
|
for flight in flight_planner.custom_flights:
|
||||||
group = self.generate_planned_flight(cp, country, flight)
|
group = self.generate_planned_flight(cp, country, flight)
|
||||||
if flight.flight_type == FlightType.INTERCEPTION:
|
if flight.flight_type == FlightType.INTERCEPTION:
|
||||||
@ -445,11 +450,16 @@ class AircraftConflictGenerator:
|
|||||||
group.points[0].tasks.clear()
|
group.points[0].tasks.clear()
|
||||||
group.points[0].tasks.append(CASTaskAction())
|
group.points[0].tasks.append(CASTaskAction())
|
||||||
group.points[0].tasks.append(OptReactOnThreat(OptReactOnThreat.Values.EvadeFire))
|
group.points[0].tasks.append(OptReactOnThreat(OptReactOnThreat.Values.EvadeFire))
|
||||||
group.points[0].tasks.append(OptROE(OptROE.Values.OpenFireWeaponFree))
|
group.points[0].tasks.append(OptROE(OptROE.Values.OpenFire))
|
||||||
group.points[0].tasks.append(OptRestrictJettison(True))
|
group.points[0].tasks.append(OptRestrictJettison(True))
|
||||||
|
|
||||||
|
i = 1
|
||||||
for point in flight.points:
|
for point in flight.points:
|
||||||
group.add_waypoint(Point(point.x,point.y), point.alt)
|
group.add_waypoint(Point(point.x,point.y), point.alt)
|
||||||
|
for t in point.targets:
|
||||||
|
group.points[i].tasks.append(Bombing(t.position))
|
||||||
|
i = i + 1
|
||||||
|
|
||||||
|
|
||||||
def setup_group_as_antiship_flight(self, group, flight):
|
def setup_group_as_antiship_flight(self, group, flight):
|
||||||
group.task = AntishipStrike.name
|
group.task = AntishipStrike.name
|
||||||
@ -464,263 +474,4 @@ class AircraftConflictGenerator:
|
|||||||
for point in flight.points:
|
for point in flight.points:
|
||||||
group.add_waypoint(Point(point.x,point.y), point.alt)
|
group.add_waypoint(Point(point.x,point.y), point.alt)
|
||||||
|
|
||||||
def generate_cas_strikegroup(self, attackers: db.PlaneDict, clients: db.PlaneDict, at: db.StartingPosition = None, escort=True):
|
|
||||||
assert not escort or len(self.escort_targets) == 0
|
|
||||||
|
|
||||||
for flying_type, count, client_count in self._split_to_groups(attackers, clients):
|
|
||||||
group = self._generate_group(
|
|
||||||
name=namegen.next_unit_name(self.conflict.attackers_country, self.conflict.from_cp.id, flying_type),
|
|
||||||
side=self.conflict.attackers_country,
|
|
||||||
unit_type=flying_type,
|
|
||||||
count=count,
|
|
||||||
client_count=client_count,
|
|
||||||
at=at and at or self._group_point(self.conflict.air_attackers_location))
|
|
||||||
|
|
||||||
waypoint = self._add_radio_waypoint(group, self.conflict.position, CAS_ALTITUDE, WARM_START_AIRSPEED)
|
|
||||||
if self.conflict.is_vector:
|
|
||||||
self._add_radio_waypoint(group, self.conflict.tail, CAS_ALTITUDE, WARM_START_AIRSPEED)
|
|
||||||
|
|
||||||
group.task = CAS.name
|
|
||||||
self._setup_group(group, CAS, client_count)
|
|
||||||
if escort:
|
|
||||||
self.escort_targets.append((group, group.points.index(waypoint)))
|
|
||||||
self._rtb_for(group, self.conflict.from_cp, at)
|
|
||||||
|
|
||||||
def generate_ground_attack_strikegroup(self, strikegroup: db.PlaneDict, clients: db.PlaneDict, targets: typing.List[typing.Tuple[str, Point]], at: db.StartingPosition = None, escort=True):
|
|
||||||
assert not escort or len(self.escort_targets) == 0
|
|
||||||
|
|
||||||
for flying_type, count, client_count in self._split_to_groups(strikegroup, clients):
|
|
||||||
group = self._generate_group(
|
|
||||||
name=namegen.next_unit_name(self.conflict.attackers_country, self.conflict.from_cp.id, flying_type),
|
|
||||||
side=self.conflict.attackers_country,
|
|
||||||
unit_type=flying_type,
|
|
||||||
count=count,
|
|
||||||
client_count=client_count,
|
|
||||||
at=at and at or self._group_point(self.conflict.air_attackers_location))
|
|
||||||
|
|
||||||
escort_until_waypoint = None
|
|
||||||
|
|
||||||
for name, pos in targets:
|
|
||||||
waypoint = group.add_waypoint(pos, 0, WARM_START_AIRSPEED, self.m.translation.create_string(name))
|
|
||||||
waypoint.tasks.append(Bombing(pos, attack_qty=2))
|
|
||||||
if escort_until_waypoint is None:
|
|
||||||
escort_until_waypoint = waypoint
|
|
||||||
|
|
||||||
group.task = GroundAttack.name
|
|
||||||
self._setup_group(group, GroundAttack, client_count)
|
|
||||||
if escort:
|
|
||||||
self.escort_targets.append((group, group.points.index(escort_until_waypoint)))
|
|
||||||
self._rtb_for(group, self.conflict.from_cp, at)
|
|
||||||
|
|
||||||
def generate_sead_strikegroup(self, strikegroup: db.PlaneDict, clients: db.PlaneDict, targets: typing.List[typing.Tuple[str, Point]], at: db.StartingPosition, escort=True):
|
|
||||||
assert not escort or len(self.escort_targets) == 0
|
|
||||||
|
|
||||||
for flying_type, count, client_count in self._split_to_groups(strikegroup, clients):
|
|
||||||
group = self._generate_group(
|
|
||||||
name=namegen.next_unit_name(self.conflict.attackers_country, self.conflict.from_cp.id, flying_type),
|
|
||||||
side=self.conflict.attackers_country,
|
|
||||||
unit_type=flying_type,
|
|
||||||
count=count,
|
|
||||||
client_count=client_count,
|
|
||||||
at=at and at or self._group_point(self.conflict.air_attackers_location))
|
|
||||||
|
|
||||||
escort_until_waypoint = None
|
|
||||||
|
|
||||||
for name, pos in targets:
|
|
||||||
waypoint = group.add_waypoint(pos, 0, WARM_START_AIRSPEED, self.m.translation.create_string(name))
|
|
||||||
if escort_until_waypoint is None:
|
|
||||||
escort_until_waypoint = waypoint
|
|
||||||
|
|
||||||
group.task = SEAD.name
|
|
||||||
self._setup_group(group, SEAD, client_count)
|
|
||||||
if escort:
|
|
||||||
self.escort_targets.append((group, group.points.index(escort_until_waypoint)))
|
|
||||||
|
|
||||||
self._rtb_for(group, self.conflict.from_cp, at)
|
|
||||||
|
|
||||||
def generate_defenders_cas(self, defenders: db.PlaneDict, clients: db.PlaneDict, at: db.StartingPosition = None, escort=True):
|
|
||||||
assert not escort or len(self.escort_targets) == 0
|
|
||||||
|
|
||||||
for flying_type, count, client_count in self._split_to_groups(defenders, clients):
|
|
||||||
group = self._generate_group(
|
|
||||||
name=namegen.next_unit_name(self.conflict.defenders_country, self.conflict.to_cp.id, flying_type),
|
|
||||||
side=self.conflict.defenders_country,
|
|
||||||
unit_type=flying_type,
|
|
||||||
count=count,
|
|
||||||
client_count=client_count,
|
|
||||||
at=at and at or self._group_point(self.conflict.air_defenders_location))
|
|
||||||
|
|
||||||
location = self._group_point(self.conflict.air_defenders_location)
|
|
||||||
insertion_point = self.conflict.find_insertion_point(location)
|
|
||||||
waypoint = self._add_radio_waypoint(group, insertion_point, CAS_ALTITUDE, WARM_START_AIRSPEED)
|
|
||||||
|
|
||||||
if self.conflict.is_vector:
|
|
||||||
destination_tail = self.conflict.tail.distance_to_point(insertion_point) > self.conflict.position.distance_to_point(insertion_point)
|
|
||||||
self._add_radio_waypoint(group, destination_tail and self.conflict.tail or self.conflict.position, CAS_ALTITUDE, WARM_START_AIRSPEED)
|
|
||||||
|
|
||||||
group.task = CAS.name
|
|
||||||
self._setup_group(group, CAS, client_count)
|
|
||||||
if escort:
|
|
||||||
self.escort_targets.append((group, group.points.index(waypoint)))
|
|
||||||
self._rtb_for(group, self.conflict.to_cp, at)
|
|
||||||
|
|
||||||
def generate_ship_strikegroup(self, attackers: db.PlaneDict, clients: db.PlaneDict, target_groups: typing.Collection[ShipGroup], at: db.StartingPosition = None, escort=True):
|
|
||||||
assert not escort or len(self.escort_targets) == 0
|
|
||||||
|
|
||||||
for flying_type, count, client_count in self._split_to_groups(attackers, clients):
|
|
||||||
group = self._generate_group(
|
|
||||||
name=namegen.next_unit_name(self.conflict.attackers_country, self.conflict.from_cp.id, flying_type),
|
|
||||||
side=self.conflict.attackers_country,
|
|
||||||
unit_type=flying_type,
|
|
||||||
count=count,
|
|
||||||
client_count=client_count,
|
|
||||||
at=at and at or self._group_point(self.conflict.air_attackers_location))
|
|
||||||
|
|
||||||
wayp = self._add_radio_waypoint(group, self.conflict.position, CAS_ALTITUDE, WARM_START_AIRSPEED)
|
|
||||||
for target_group in target_groups:
|
|
||||||
wayp.tasks.append(AttackGroup(target_group.id))
|
|
||||||
|
|
||||||
group.task = AntishipStrike.name
|
|
||||||
self._setup_group(group, AntishipStrike, client_count)
|
|
||||||
if escort:
|
|
||||||
self.escort_targets.append((group, group.points.index(wayp)))
|
|
||||||
self._rtb_for(group, self.conflict.from_cp, at)
|
|
||||||
|
|
||||||
def generate_attackers_escort(self, attackers: db.PlaneDict, clients: db.PlaneDict, at: db.StartingPosition = None):
|
|
||||||
for g in self._generate_escort(
|
|
||||||
side=self.conflict.attackers_country,
|
|
||||||
units=attackers,
|
|
||||||
clients=clients,
|
|
||||||
at=at and at or self._group_point(self.conflict.air_attackers_location),
|
|
||||||
is_quick=at is None,
|
|
||||||
cp=self.conflict.from_cp,
|
|
||||||
should_orbit=True):
|
|
||||||
self._rtb_for(g, self.conflict.from_cp, at)
|
|
||||||
|
|
||||||
def generate_defenders_escort(self, escort: db.PlaneDict, clients: db.PlaneDict, at: db.StartingPosition = None):
|
|
||||||
for g in self._generate_escort(
|
|
||||||
side=self.conflict.defenders_country,
|
|
||||||
units=escort,
|
|
||||||
clients=clients,
|
|
||||||
at=at and at or self._group_point(self.conflict.air_defenders_location),
|
|
||||||
is_quick=at is None,
|
|
||||||
cp=self.conflict.to_cp,
|
|
||||||
should_orbit=False):
|
|
||||||
self._rtb_for(g, self.conflict.to_cp, at)
|
|
||||||
|
|
||||||
def generate_defense(self, defenders: db.PlaneDict, clients: db.PlaneDict, at: db.StartingPosition = None):
|
|
||||||
for flying_type, count, client_count in self._split_to_groups(defenders, clients):
|
|
||||||
group = self._generate_group(
|
|
||||||
name=namegen.next_unit_name(self.conflict.attackers_country, self.conflict.to_cp.id, flying_type),
|
|
||||||
side=self.conflict.defenders_country,
|
|
||||||
unit_type=flying_type,
|
|
||||||
count=count,
|
|
||||||
client_count=client_count,
|
|
||||||
at=at and at or self._group_point(self.conflict.air_defenders_location))
|
|
||||||
|
|
||||||
group.task = CAP.name
|
|
||||||
wayp = self._add_radio_waypoint(group, self.conflict.position, CAS_ALTITUDE, WARM_START_AIRSPEED)
|
|
||||||
wayp.tasks.append(dcs.task.EngageTargets(max_distance=DEFENCE_ENGAGEMENT_MAX_DISTANCE))
|
|
||||||
wayp.tasks.append(dcs.task.OrbitAction(ATTACK_CIRCLE_ALT, pattern=OrbitAction.OrbitPattern.Circle))
|
|
||||||
self._setup_group(group, CAP, client_count)
|
|
||||||
self._rtb_for(group, self.conflict.to_cp, at)
|
|
||||||
|
|
||||||
def generate_migcap(self, patrol: db.PlaneDict, clients: db.PlaneDict, at: db.StartingPosition = None):
|
|
||||||
for flying_type, count, client_count in self._split_to_groups(patrol, clients):
|
|
||||||
group = self._generate_group(
|
|
||||||
name=namegen.next_unit_name(self.conflict.attackers_country, self.conflict.from_cp.id, flying_type),
|
|
||||||
side=self.conflict.attackers_country,
|
|
||||||
unit_type=flying_type,
|
|
||||||
count=count,
|
|
||||||
client_count=client_count,
|
|
||||||
at=at and at or self._group_point(self.conflict.air_attackers_location))
|
|
||||||
|
|
||||||
waypoint = self._add_radio_waypoint(group, self.conflict.position, WARM_START_ALTITUDE, WARM_START_AIRSPEED)
|
|
||||||
if self.conflict.is_vector:
|
|
||||||
self._add_radio_waypoint(group, self.conflict.tail, WARM_START_ALTITUDE, WARM_START_AIRSPEED)
|
|
||||||
|
|
||||||
group.task = CAP.name
|
|
||||||
self._setup_group(group, CAP, client_count)
|
|
||||||
self._rtb_for(group, self.conflict.from_cp, at)
|
|
||||||
|
|
||||||
def generate_barcap(self, patrol: db.PlaneDict, clients: db.PlaneDict, at: db.StartingPosition = None):
|
|
||||||
for flying_type, count, client_count in self._split_to_groups(patrol, clients):
|
|
||||||
group = self._generate_group(
|
|
||||||
name=namegen.next_unit_name(self.conflict.attackers_country, self.conflict.from_cp.id, flying_type),
|
|
||||||
side=self.conflict.defenders_country,
|
|
||||||
unit_type=flying_type,
|
|
||||||
count=count,
|
|
||||||
client_count=client_count,
|
|
||||||
at=at and at or self._group_point(self.conflict.air_defenders_location))
|
|
||||||
|
|
||||||
waypoint = self._add_radio_waypoint(group, self.conflict.position, WARM_START_ALTITUDE, WARM_START_AIRSPEED)
|
|
||||||
if self.conflict.is_vector:
|
|
||||||
self._add_radio_waypoint(group, self.conflict.tail, WARM_START_ALTITUDE, WARM_START_AIRSPEED)
|
|
||||||
else:
|
|
||||||
heading = group.position.heading_between_point(self.conflict.position)
|
|
||||||
waypoint = self._add_radio_waypoint(group, self.conflict.position.point_from_heading(heading, BARCAP_RACETRACK_DISTANCE),
|
|
||||||
WARM_START_ALTITUDE,
|
|
||||||
WARM_START_AIRSPEED)
|
|
||||||
waypoint.tasks.append(OrbitAction(WARM_START_ALTITUDE, WARM_START_AIRSPEED))
|
|
||||||
|
|
||||||
group.task = CAP.name
|
|
||||||
self._setup_group(group, CAP, client_count)
|
|
||||||
self._rtb_for(group, self.conflict.to_cp, at)
|
|
||||||
|
|
||||||
def generate_transport(self, transport: db.PlaneDict, destination: Airport, escort=True):
|
|
||||||
assert not escort or len(self.escort_targets) == 0
|
|
||||||
|
|
||||||
for flying_type, count, client_count in self._split_to_groups(transport):
|
|
||||||
group = self._generate_group(
|
|
||||||
name=namegen.next_unit_name(self.conflict.attackers_country, self.conflict.from_cp.id, flying_type),
|
|
||||||
side=self.conflict.defenders_country,
|
|
||||||
unit_type=flying_type,
|
|
||||||
count=count,
|
|
||||||
client_count=client_count,
|
|
||||||
at=self._group_point(self.conflict.air_defenders_location))
|
|
||||||
|
|
||||||
waypoint = self._rtb_for(group, self.conflict.to_cp)
|
|
||||||
if escort:
|
|
||||||
self.escort_targets.append((group, group.points.index(waypoint)))
|
|
||||||
|
|
||||||
self._add_radio_waypoint(group, destination.position, RTB_ALTITUDE)
|
|
||||||
group.task = Transport.name
|
|
||||||
group.land_at(destination)
|
|
||||||
|
|
||||||
def generate_interception(self, interceptors: db.PlaneDict, clients: db.PlaneDict, at: db.StartingPosition = None):
|
|
||||||
for flying_type, count, client_count in self._split_to_groups(interceptors, clients):
|
|
||||||
group = self._generate_group(
|
|
||||||
name=namegen.next_unit_name(self.conflict.attackers_country, self.conflict.from_cp.id, flying_type),
|
|
||||||
side=self.conflict.attackers_country,
|
|
||||||
unit_type=flying_type,
|
|
||||||
count=count,
|
|
||||||
client_count=client_count,
|
|
||||||
at=at and at or self._group_point(self.conflict.air_attackers_location))
|
|
||||||
|
|
||||||
group.task = CAP.name
|
|
||||||
group.points[0].tasks.append(EngageTargets(max_distance=INTERCEPT_MAX_DISTANCE))
|
|
||||||
|
|
||||||
wayp = self._add_radio_waypoint(group, self.conflict.position, WARM_START_ALTITUDE, INTERCEPTION_AIRSPEED)
|
|
||||||
wayp.tasks.append(EngageTargets(max_distance=INTERCEPT_MAX_DISTANCE))
|
|
||||||
|
|
||||||
if self.conflict.is_vector:
|
|
||||||
self._add_radio_waypoint(group, self.conflict.tail, CAS_ALTITUDE, WARM_START_ALTITUDE)
|
|
||||||
|
|
||||||
self._setup_group(group, CAP, client_count)
|
|
||||||
self._rtb_for(group, self.conflict.from_cp, at)
|
|
||||||
|
|
||||||
def generate_passenger_transport(self, helis: db.HeliDict, clients: db.HeliDict, at: db.StartingPosition):
|
|
||||||
for heli_type, count, client_count in self._split_to_groups(helis, clients):
|
|
||||||
group = self._generate_group(
|
|
||||||
name=namegen.next_unit_name(self.conflict.attackers_country, self.conflict.from_cp.id, heli_type),
|
|
||||||
side=self.conflict.attackers_country,
|
|
||||||
unit_type=heli_type,
|
|
||||||
count=count,
|
|
||||||
client_count=client_count,
|
|
||||||
at=at and at or self._group_point(self.conflict.air_attackers_location)
|
|
||||||
)
|
|
||||||
|
|
||||||
self._add_radio_waypoint(group, self.conflict.position, HELI_ALT)
|
|
||||||
self._setup_group(group, Transport, client_count)
|
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@ -172,13 +172,6 @@ class Conflict:
|
|||||||
position = middle_point.point_from_heading(attack_heading, strength_delta * attack_distance / 2 - FRONTLINE_MIN_CP_DISTANCE)
|
position = middle_point.point_from_heading(attack_heading, strength_delta * attack_distance / 2 - FRONTLINE_MIN_CP_DISTANCE)
|
||||||
return position, _opposite_heading(attack_heading)
|
return position, _opposite_heading(attack_heading)
|
||||||
|
|
||||||
ground_position = cls._find_ground_position(position, attack_distance / 2 - FRONTLINE_MIN_CP_DISTANCE, attack_heading, theater)
|
|
||||||
if ground_position:
|
|
||||||
return ground_position, _opposite_heading(attack_heading)
|
|
||||||
else:
|
|
||||||
logging.warning("Coudn't find frontline position between {} and {}!".format(from_cp, to_cp))
|
|
||||||
return position, _opposite_heading(attack_heading)
|
|
||||||
|
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def frontline_vector(cls, from_cp: ControlPoint, to_cp: ControlPoint, theater: ConflictTheater) -> typing.Optional[typing.Tuple[Point, int, int]]:
|
def frontline_vector(cls, from_cp: ControlPoint, to_cp: ControlPoint, theater: ConflictTheater) -> typing.Optional[typing.Tuple[Point, int, int]]:
|
||||||
|
|||||||
@ -17,7 +17,13 @@ MISSION_DURATION = 120 # in minutes
|
|||||||
CAP_EVERY_X_MINUTES = 20
|
CAP_EVERY_X_MINUTES = 20
|
||||||
CAS_EVERY_X_MINUTES = 30
|
CAS_EVERY_X_MINUTES = 30
|
||||||
SEAD_EVERY_X_MINUTES = 40
|
SEAD_EVERY_X_MINUTES = 40
|
||||||
|
STRIKE_EVERY_X_MINUTES = 40
|
||||||
|
|
||||||
|
INGRESS_EGRESS_DISTANCE = 45000
|
||||||
|
INGRESS_ALT = 6096 # 20k feet
|
||||||
|
EGRESS_ALT = 6096 # 20k feet
|
||||||
|
PATROL_ALT_RANGE = (3600, 9200)
|
||||||
|
NAV_ALT = 9144
|
||||||
|
|
||||||
class FlightPlanner:
|
class FlightPlanner:
|
||||||
|
|
||||||
@ -61,6 +67,8 @@ class FlightPlanner:
|
|||||||
# Then prepare some sead flights if required
|
# Then prepare some sead flights if required
|
||||||
self.commision_sead()
|
self.commision_sead()
|
||||||
|
|
||||||
|
self.commision_strike()
|
||||||
|
|
||||||
# TODO : commision STRIKE / ANTISHIP
|
# TODO : commision STRIKE / ANTISHIP
|
||||||
|
|
||||||
def remove_flight(self, index):
|
def remove_flight(self, index):
|
||||||
@ -130,7 +138,7 @@ class FlightPlanner:
|
|||||||
flight.points = []
|
flight.points = []
|
||||||
flight.scheduled_in = offset + i*random.randint(CAP_EVERY_X_MINUTES-5, CAP_EVERY_X_MINUTES+5)
|
flight.scheduled_in = offset + i*random.randint(CAP_EVERY_X_MINUTES-5, CAP_EVERY_X_MINUTES+5)
|
||||||
|
|
||||||
patrol_alt = random.randint(3600, 7000)
|
patrol_alt = random.randint(PATROL_ALT_RANGE[0], PATROL_ALT_RANGE[1])
|
||||||
|
|
||||||
patrolled = []
|
patrolled = []
|
||||||
for ground_object in self.from_cp.ground_objects:
|
for ground_object in self.from_cp.ground_objects:
|
||||||
@ -245,12 +253,33 @@ class FlightPlanner:
|
|||||||
location = self.potential_sead_targets[0][0]
|
location = self.potential_sead_targets[0][0]
|
||||||
self.potential_sead_targets.pop(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
|
||||||
|
flight.points.append(ingress_point)
|
||||||
|
|
||||||
point = FlightWaypoint(location.position.x, location.position.y, 1000)
|
point = FlightWaypoint(location.position.x, location.position.y, 1000)
|
||||||
point.description = "SEAD"
|
if flight.flight_type == FlightType.DEAD:
|
||||||
point.pretty_name = "SEAD"
|
point.description = "SEAD on " + location.obj_name
|
||||||
|
point.pretty_name = "SEAD on " + location.obj_name
|
||||||
|
else:
|
||||||
|
point.description = "DEAD on " + location.obj_name
|
||||||
|
point.pretty_name = "DEAD on " + location.obj_name
|
||||||
|
|
||||||
point.targets.append(location)
|
point.targets.append(location)
|
||||||
flight.points.append(point)
|
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 on " + location.obj_name
|
||||||
|
egress_point.description = "EGRESS on " + location.obj_name
|
||||||
|
flight.points.append(egress_point)
|
||||||
|
|
||||||
self.sead_flights.append(flight)
|
self.sead_flights.append(flight)
|
||||||
self.flights.append(flight)
|
self.flights.append(flight)
|
||||||
|
|
||||||
@ -258,6 +287,74 @@ 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 commision_strike(self):
|
||||||
|
"""
|
||||||
|
Pick some aircraft to assign them to STRIKE tasks
|
||||||
|
"""
|
||||||
|
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})
|
||||||
|
|
||||||
|
if len(self.potential_strike_targets) > 0:
|
||||||
|
|
||||||
|
offset = random.randint(0,5)
|
||||||
|
for i in range(int(MISSION_DURATION/STRIKE_EVERY_X_MINUTES)):
|
||||||
|
|
||||||
|
if len(self.potential_strike_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, FlightType.STRIKE)
|
||||||
|
|
||||||
|
flight.points = []
|
||||||
|
flight.scheduled_in = offset + i*random.randint(SEAD_EVERY_X_MINUTES-5, SEAD_EVERY_X_MINUTES+5)
|
||||||
|
|
||||||
|
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
|
||||||
|
flight.points.append(ingress_point)
|
||||||
|
|
||||||
|
if len(location.groups) > 0:
|
||||||
|
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.targets.append(location)
|
||||||
|
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.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 on " + location.obj_name
|
||||||
|
egress_point.description = "EGRESS on " + location.obj_name
|
||||||
|
flight.points.append(egress_point)
|
||||||
|
|
||||||
|
self.strike_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):
|
def _get_cas_locations(self):
|
||||||
cas_locations = []
|
cas_locations = []
|
||||||
for cp in self.from_cp.connected_points:
|
for cp in self.from_cp.connected_points:
|
||||||
@ -285,7 +382,7 @@ class FlightPlanner:
|
|||||||
|
|
||||||
added_group = []
|
added_group = []
|
||||||
for g in cp.ground_objects:
|
for g in cp.ground_objects:
|
||||||
if g.group_id in added_group: continue
|
if g.group_id in added_group or g.is_dead: continue
|
||||||
|
|
||||||
# Compute distance to current cp
|
# Compute distance to current cp
|
||||||
distance = math.hypot(cp.position.x - self.from_cp.position.x,
|
distance = math.hypot(cp.position.x - self.from_cp.position.x,
|
||||||
|
|||||||
@ -119,7 +119,7 @@ CAS_CAPABLE = [
|
|||||||
SEAD_CAPABLE = [
|
SEAD_CAPABLE = [
|
||||||
F_4E,
|
F_4E,
|
||||||
FA_18C_hornet,
|
FA_18C_hornet,
|
||||||
F_16C_50,
|
# F_16C_50, Not yet
|
||||||
AV8BNA,
|
AV8BNA,
|
||||||
JF_17,
|
JF_17,
|
||||||
|
|
||||||
@ -179,7 +179,12 @@ STRIKE_CAPABLE = [
|
|||||||
|
|
||||||
ANTISHIP_CAPABLE = [
|
ANTISHIP_CAPABLE = [
|
||||||
Su_24M,
|
Su_24M,
|
||||||
|
Su_17M4,
|
||||||
F_A_18C,
|
F_A_18C,
|
||||||
AV8BNA,
|
AV8BNA,
|
||||||
JF_17
|
JF_17,
|
||||||
|
F_16C_50,
|
||||||
|
A_10C,
|
||||||
|
A_10A,
|
||||||
|
|
||||||
]
|
]
|
||||||
@ -2,6 +2,7 @@ from PySide2.QtCore import QSortFilterProxyModel, Qt, QModelIndex
|
|||||||
from PySide2.QtGui import QStandardItem, QStandardItemModel
|
from PySide2.QtGui import QStandardItem, QStandardItemModel
|
||||||
from PySide2.QtWidgets import QComboBox, QCompleter
|
from PySide2.QtWidgets import QComboBox, QCompleter
|
||||||
from game import Game
|
from game import Game
|
||||||
|
from gen import Conflict
|
||||||
from gen.flights.flight import FlightWaypoint
|
from gen.flights.flight import FlightWaypoint
|
||||||
from theater import ControlPointType
|
from theater import ControlPointType
|
||||||
|
|
||||||
@ -89,7 +90,8 @@ class QPredefinedWaypointSelectionComboBox(QComboBox):
|
|||||||
if cp.captured:
|
if cp.captured:
|
||||||
enemy_cp = [ecp for ecp in cp.connected_points if ecp.captured != cp.captured]
|
enemy_cp = [ecp for ecp in cp.connected_points if ecp.captured != cp.captured]
|
||||||
for ecp in enemy_cp:
|
for ecp in enemy_cp:
|
||||||
wpt = FlightWaypoint((cp.position.x + ecp.position.x)/2, (cp.position.y + ecp.position.y)/2, 800)
|
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.name = "Frontline " + cp.name + "/" + ecp.name + " [CAS]"
|
||||||
wpt.pretty_name = wpt.name
|
wpt.pretty_name = wpt.name
|
||||||
wpt.description = "Frontline"
|
wpt.description = "Frontline"
|
||||||
|
|||||||
@ -112,6 +112,9 @@ def generate_groundobjects(theater: ConflictTheater, game):
|
|||||||
if "lhanames" in db.FACTIONS[faction]:
|
if "lhanames" in db.FACTIONS[faction]:
|
||||||
cp.name = random.choice(db.FACTIONS[faction]["lhanames"])
|
cp.name = random.choice(db.FACTIONS[faction]["lhanames"])
|
||||||
else:
|
else:
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
for i in range(random.randint(2,6)):
|
for i in range(random.randint(2,6)):
|
||||||
point = find_location(True, cp.position, theater, 1000, 2800, [])
|
point = find_location(True, cp.position, theater, 1000, 2800, [])
|
||||||
|
|
||||||
@ -131,18 +134,7 @@ def generate_groundobjects(theater: ConflictTheater, game):
|
|||||||
g.heading = 0
|
g.heading = 0
|
||||||
g.position = Point(point.x, point.y)
|
g.position = Point(point.x, point.y)
|
||||||
|
|
||||||
if i == 0:
|
generate_airbase_defense_group(i, g, faction, game, cp)
|
||||||
group = generate_armor_group(faction, game, g)
|
|
||||||
elif i == 1 and random.randint(0,1) == 0:
|
|
||||||
group = generate_anti_air_group(game, cp, g, faction)
|
|
||||||
elif random.randint(0, 2) == 1:
|
|
||||||
group = generate_shorad_group(game, cp, g, faction)
|
|
||||||
else:
|
|
||||||
group = generate_armor_group(faction, game, g)
|
|
||||||
|
|
||||||
g.groups = []
|
|
||||||
if group is not None:
|
|
||||||
g.groups.append(group)
|
|
||||||
cp.ground_objects.append(g)
|
cp.ground_objects.append(g)
|
||||||
|
|
||||||
print("---------------------------")
|
print("---------------------------")
|
||||||
@ -151,6 +143,22 @@ def generate_groundobjects(theater: ConflictTheater, game):
|
|||||||
print(ground_object.groups)
|
print(ground_object.groups)
|
||||||
|
|
||||||
|
|
||||||
|
def generate_airbase_defense_group(airbase_defense_group_id, ground_obj:TheaterGroundObject, faction, game, cp):
|
||||||
|
|
||||||
|
if airbase_defense_group_id == 0:
|
||||||
|
group = generate_armor_group(faction, game, ground_obj)
|
||||||
|
elif airbase_defense_group_id == 1 and random.randint(0, 1) == 0:
|
||||||
|
group = generate_anti_air_group(game, cp, ground_obj, faction)
|
||||||
|
elif random.randint(0, 2) == 1:
|
||||||
|
group = generate_shorad_group(game, cp, ground_obj, faction)
|
||||||
|
else:
|
||||||
|
group = generate_armor_group(faction, game, ground_obj)
|
||||||
|
|
||||||
|
ground_obj.groups = []
|
||||||
|
if group is not None:
|
||||||
|
ground_obj.groups.append(group)
|
||||||
|
|
||||||
|
|
||||||
def find_location(on_ground, near, theater, min, max, others) -> typing.Optional[Point]:
|
def find_location(on_ground, near, theater, min, max, others) -> typing.Optional[Point]:
|
||||||
"""
|
"""
|
||||||
Find a valid ground object location
|
Find a valid ground object location
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user