mirror of
https://github.com/dcs-retribution/dcs-retribution.git
synced 2025-11-10 15:41:24 +00:00
number of fixes and improvements to mission generation & debriefings
This commit is contained in:
parent
a4dfde157d
commit
d78b2dd238
@ -1,6 +1,4 @@
|
|||||||
#!/usr/bin/env python3
|
#!/usr/bin/env python3
|
||||||
import sys
|
|
||||||
|
|
||||||
import theater.caucasus
|
import theater.caucasus
|
||||||
import ui.window
|
import ui.window
|
||||||
import ui.mainmenu
|
import ui.mainmenu
|
||||||
|
|||||||
22
game/db.py
22
game/db.py
@ -23,6 +23,7 @@ PRICES = {
|
|||||||
F_15C: 30,
|
F_15C: 30,
|
||||||
M_2000C: 11,
|
M_2000C: 11,
|
||||||
|
|
||||||
|
MiG_15bis: 8,
|
||||||
MiG_21Bis: 13,
|
MiG_21Bis: 13,
|
||||||
MiG_29A: 23,
|
MiG_29A: 23,
|
||||||
|
|
||||||
@ -49,13 +50,14 @@ PRICES = {
|
|||||||
}
|
}
|
||||||
|
|
||||||
UNIT_BY_TASK = {
|
UNIT_BY_TASK = {
|
||||||
FighterSweep: [Su_27, Su_33, FA_18C_hornet, F_15C, MiG_21Bis, MiG_29A, F_A_18C, AV8BNA],
|
FighterSweep: [Su_27, Su_33, FA_18C_hornet, F_15C, MiG_21Bis, MiG_29A, F_A_18C, AV8BNA, ],
|
||||||
CAS: [Su_25T, A_10A, A_10C, ],
|
CAS: [Su_25T, A_10A, A_10C, ],
|
||||||
|
Transport: [IL_76MD, S_3B_Tanker, ],
|
||||||
|
AWACS: [E_3A, A_50, ],
|
||||||
|
|
||||||
CAP: [Armor.MBT_T_90, Armor.MBT_T_80U, Armor.MBT_T_55, Armor.MBT_M1A2_Abrams, Armor.MBT_M60A3_Patton, Armor.ATGM_M1134_Stryker, Armor.APC_BTR_80, ],
|
CAP: [Armor.MBT_T_90, Armor.MBT_T_80U, Armor.MBT_T_55, Armor.MBT_M1A2_Abrams, Armor.MBT_M60A3_Patton, Armor.ATGM_M1134_Stryker, Armor.APC_BTR_80, ],
|
||||||
AirDefence: [AirDefence.AAA_ZU_23_on_Ural_375, AirDefence.SAM_Avenger_M1097 ],
|
AirDefence: [AirDefence.AAA_ZU_23_on_Ural_375, AirDefence.SAM_Avenger_M1097 ],
|
||||||
Transport: [IL_76MD, S_3B_Tanker, ],
|
|
||||||
Carriage: [CVN_74_John_C__Stennis, CV_1143_5_Admiral_Kuznetsov, ],
|
Carriage: [CVN_74_John_C__Stennis, CV_1143_5_Admiral_Kuznetsov, ],
|
||||||
AWACS: [E_3A, A_50, ],
|
|
||||||
}
|
}
|
||||||
|
|
||||||
UNIT_BY_COUNTRY = {
|
UNIT_BY_COUNTRY = {
|
||||||
@ -88,6 +90,20 @@ UNIT_BY_COUNTRY = {
|
|||||||
CVN_74_John_C__Stennis],
|
CVN_74_John_C__Stennis],
|
||||||
}
|
}
|
||||||
|
|
||||||
|
PLANE_PAYLOAD_OVERRIDES = {
|
||||||
|
FA_18C_hornet: {
|
||||||
|
"*": "AIM-9M*6, AIM-7M*2, FUEL*3",
|
||||||
|
},
|
||||||
|
|
||||||
|
MiG_21Bis: {
|
||||||
|
"*": "Patrol, medium range",
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
PLANE_LIVERY_OVERRIDES = {
|
||||||
|
FA_18C_hornet: "VFA-34",
|
||||||
|
}
|
||||||
|
|
||||||
UnitsDict = typing.Dict[UnitType, int]
|
UnitsDict = typing.Dict[UnitType, int]
|
||||||
PlaneDict = typing.Dict[FlyingType, int]
|
PlaneDict = typing.Dict[FlyingType, int]
|
||||||
ArmorDict = typing.Dict[VehicleType, int]
|
ArmorDict = typing.Dict[VehicleType, int]
|
||||||
|
|||||||
@ -49,10 +49,10 @@ class Event:
|
|||||||
|
|
||||||
class GroundInterceptEvent(Event):
|
class GroundInterceptEvent(Event):
|
||||||
BONUS_BASE = 3
|
BONUS_BASE = 3
|
||||||
TARGET_AMOUNT_FACTOR = 3
|
TARGET_AMOUNT_FACTOR = 2
|
||||||
TARGET_VARIETY = 3
|
TARGET_VARIETY = 2
|
||||||
STRENGTH_INFLUENCE = 0.1
|
STRENGTH_INFLUENCE = 0.1
|
||||||
SUCCESS_TARGETS_HIT_PERCENTAGE = 0.7
|
SUCCESS_TARGETS_HIT_PERCENTAGE = 0.5
|
||||||
|
|
||||||
targets = None # type: db.ArmorDict
|
targets = None # type: db.ArmorDict
|
||||||
|
|
||||||
|
|||||||
@ -83,12 +83,6 @@ class Game:
|
|||||||
enemy_interception = True
|
enemy_interception = True
|
||||||
break
|
break
|
||||||
|
|
||||||
for to_cp in self.theater.player_points():
|
|
||||||
if enemy_interception:
|
|
||||||
break
|
|
||||||
|
|
||||||
if to_cp.is_global:
|
|
||||||
continue
|
|
||||||
|
|
||||||
if to_cp in self.theater.conflicts(False):
|
if to_cp in self.theater.conflicts(False):
|
||||||
continue
|
continue
|
||||||
|
|||||||
@ -37,6 +37,7 @@ class Operation:
|
|||||||
self.defender_clients = defender_clients
|
self.defender_clients = defender_clients
|
||||||
self.from_cp = from_cp
|
self.from_cp = from_cp
|
||||||
self.to_cp = to_cp
|
self.to_cp = to_cp
|
||||||
|
self.is_quick = False
|
||||||
|
|
||||||
def initialize(self, mission: Mission, conflict: Conflict):
|
def initialize(self, mission: Mission, conflict: Conflict):
|
||||||
self.mission = mission
|
self.mission = mission
|
||||||
@ -46,13 +47,15 @@ class Operation:
|
|||||||
self.airgen = AircraftConflictGenerator(mission, conflict)
|
self.airgen = AircraftConflictGenerator(mission, conflict)
|
||||||
self.aagen = AAConflictGenerator(mission, conflict)
|
self.aagen = AAConflictGenerator(mission, conflict)
|
||||||
self.shipgen = ShipGenerator(mission, conflict)
|
self.shipgen = ShipGenerator(mission, conflict)
|
||||||
self.envgen = EnvironmentSettingsGenerator(mission, self.game)
|
self.envgen = EnvironmentSettingsGenerator(mission, conflict, self.game)
|
||||||
|
|
||||||
player_name = self.from_cp.captured and self.attacker_name or self.defender_name
|
player_name = self.from_cp.captured and self.attacker_name or self.defender_name
|
||||||
enemy_name = self.from_cp.captured and self.defender_name or self.attacker_name
|
enemy_name = self.from_cp.captured and self.defender_name or self.attacker_name
|
||||||
self.extra_aagen = ExtraAAConflictGenerator(mission, conflict, self.game, player_name, enemy_name)
|
self.extra_aagen = ExtraAAConflictGenerator(mission, conflict, self.game, player_name, enemy_name)
|
||||||
|
|
||||||
def prepare(self, is_quick: bool):
|
def prepare(self, is_quick: bool):
|
||||||
|
self.is_quick = is_quick
|
||||||
|
|
||||||
if is_quick:
|
if is_quick:
|
||||||
self.attackers_starting_position = None
|
self.attackers_starting_position = None
|
||||||
self.defenders_starting_position = None
|
self.defenders_starting_position = None
|
||||||
@ -62,7 +65,7 @@ class Operation:
|
|||||||
|
|
||||||
def generate(self):
|
def generate(self):
|
||||||
self.extra_aagen.generate()
|
self.extra_aagen.generate()
|
||||||
self.envgen.generate()
|
self.envgen.generate(self.is_quick)
|
||||||
|
|
||||||
def units_of(self, country_name: str) -> typing.Collection[UnitType]:
|
def units_of(self, country_name: str) -> typing.Collection[UnitType]:
|
||||||
return []
|
return []
|
||||||
@ -103,7 +106,6 @@ class CaptureOperation(Operation):
|
|||||||
mission.country(self.defender_name)))
|
mission.country(self.defender_name)))
|
||||||
|
|
||||||
def generate(self):
|
def generate(self):
|
||||||
self.envgen.generate()
|
|
||||||
self.armorgen.generate(self.attack, self.defense)
|
self.armorgen.generate(self.attack, self.defense)
|
||||||
self.aagen.generate(self.aa)
|
self.aagen.generate(self.aa)
|
||||||
|
|
||||||
@ -112,6 +114,8 @@ class CaptureOperation(Operation):
|
|||||||
self.airgen.generate_cas(self.cas, clients=self.attacker_clients, at=self.attackers_starting_position)
|
self.airgen.generate_cas(self.cas, clients=self.attacker_clients, at=self.attackers_starting_position)
|
||||||
self.airgen.generate_cas_escort(self.escort, clients=self.attacker_clients, at=self.attackers_starting_position)
|
self.airgen.generate_cas_escort(self.escort, clients=self.attacker_clients, at=self.attackers_starting_position)
|
||||||
|
|
||||||
|
super(CaptureOperation, self).generate()
|
||||||
|
|
||||||
|
|
||||||
class InterceptOperation(Operation):
|
class InterceptOperation(Operation):
|
||||||
escort = None # type: db.PlaneDict
|
escort = None # type: db.PlaneDict
|
||||||
@ -144,17 +148,22 @@ class InterceptOperation(Operation):
|
|||||||
conflict=conflict)
|
conflict=conflict)
|
||||||
|
|
||||||
def generate(self):
|
def generate(self):
|
||||||
super(InterceptOperation, self).generate()
|
|
||||||
self.airgen.generate_transport(self.transport, self.to_cp.at)
|
self.airgen.generate_transport(self.transport, self.to_cp.at)
|
||||||
self.airgen.generate_transport_escort(self.escort, clients=self.defender_clients)
|
self.airgen.generate_transport_escort(self.escort, clients=self.defender_clients)
|
||||||
|
|
||||||
if self.from_cp.is_global:
|
if self.from_cp.is_global:
|
||||||
ship = self.shipgen.generate(type=db.find_unittype(Carriage, self.attacker_name)[0],
|
starting_ship = self.shipgen.generate(type=db.find_unittype(Carriage, self.attacker_name)[0],
|
||||||
at=self.from_cp.at)
|
at=self.from_cp.at)
|
||||||
self.airgen.generate_interception(self.interceptors, clients=self.attacker_clients, at=ship)
|
|
||||||
|
if self.is_quick:
|
||||||
|
starting_ship = None
|
||||||
|
|
||||||
|
self.airgen.generate_interception(self.interceptors, clients=self.attacker_clients, at=starting_ship)
|
||||||
else:
|
else:
|
||||||
self.airgen.generate_interception(self.interceptors, clients=self.attacker_clients, at=self.attackers_starting_position)
|
self.airgen.generate_interception(self.interceptors, clients=self.attacker_clients, at=self.attackers_starting_position)
|
||||||
|
|
||||||
|
super(InterceptOperation, self).generate()
|
||||||
|
|
||||||
|
|
||||||
class GroundInterceptOperation(Operation):
|
class GroundInterceptOperation(Operation):
|
||||||
def setup(self,
|
def setup(self,
|
||||||
@ -180,6 +189,7 @@ class GroundInterceptOperation(Operation):
|
|||||||
conflict=conflict)
|
conflict=conflict)
|
||||||
|
|
||||||
def generate(self):
|
def generate(self):
|
||||||
super(GroundInterceptOperation, self).generate()
|
|
||||||
self.airgen.generate_cas(self.strikegroup, clients=self.attacker_clients, at=self.attackers_starting_position)
|
self.airgen.generate_cas(self.strikegroup, clients=self.attacker_clients, at=self.attackers_starting_position)
|
||||||
self.armorgen.generate({}, self.target)
|
self.armorgen.generate({}, self.target)
|
||||||
|
|
||||||
|
super(GroundInterceptOperation, self).generate()
|
||||||
|
|||||||
@ -8,6 +8,7 @@ from dcs.mission import *
|
|||||||
|
|
||||||
DISTANCE_FACTOR = 4, 5
|
DISTANCE_FACTOR = 4, 5
|
||||||
EXTRA_AA_MIN_DISTANCE = 70000
|
EXTRA_AA_MIN_DISTANCE = 70000
|
||||||
|
EXTRA_AA_POSITION_FROM_CP = 10000
|
||||||
|
|
||||||
class AAConflictGenerator:
|
class AAConflictGenerator:
|
||||||
def __init__(self, mission: Mission, conflict: Conflict):
|
def __init__(self, mission: Mission, conflict: Conflict):
|
||||||
@ -37,18 +38,21 @@ class ExtraAAConflictGenerator:
|
|||||||
self.enemy_name = enemy_name
|
self.enemy_name = enemy_name
|
||||||
|
|
||||||
def generate(self):
|
def generate(self):
|
||||||
|
from theater.conflicttheater import ControlPoint
|
||||||
|
|
||||||
for cp in self.game.theater.controlpoints:
|
for cp in self.game.theater.controlpoints:
|
||||||
if cp.is_global:
|
if cp.is_global:
|
||||||
continue
|
continue
|
||||||
|
|
||||||
if cp.position.distance_to_point(self.conflict.position) > EXTRA_AA_MIN_DISTANCE:
|
if cp.position.distance_to_point(self.conflict.position) > EXTRA_AA_MIN_DISTANCE:
|
||||||
country_name = cp.captured and self.player_name or self.enemy_name
|
country_name = cp.captured and self.player_name or self.enemy_name
|
||||||
|
position = cp.position.point_from_heading(0, EXTRA_AA_POSITION_FROM_CP)
|
||||||
|
|
||||||
self.mission.vehicle_group(
|
self.mission.vehicle_group(
|
||||||
country=self.mission.country(country_name),
|
country=self.mission.country(country_name),
|
||||||
name=namegen.next_ground_group_name(),
|
name=namegen.next_ground_group_name(),
|
||||||
_type=random.choice(db.find_unittype(AirDefence, country_name)),
|
_type=random.choice(db.find_unittype(AirDefence, country_name)),
|
||||||
position=cp.position,
|
position=position,
|
||||||
group_size=2
|
group_size=2
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|||||||
@ -6,19 +6,24 @@ from dcs.mission import *
|
|||||||
from dcs.unitgroup import *
|
from dcs.unitgroup import *
|
||||||
from dcs.unittype import *
|
from dcs.unittype import *
|
||||||
from dcs.task import *
|
from dcs.task import *
|
||||||
|
from dcs.terrain.terrain import NoParkingSlotError
|
||||||
|
|
||||||
SPREAD_DISTANCE_FACTOR = 1, 2
|
SPREAD_DISTANCE_FACTOR = 1, 2
|
||||||
ESCORT_MAX_DIST = 30000
|
ESCORT_MAX_DIST = 30000
|
||||||
WORKAROUND_WAYP_DIST = 1000
|
WORKAROUND_WAYP_DIST = 1000
|
||||||
|
|
||||||
WARM_START_ALTITUDE = 6000
|
WARM_START_ALTITUDE = 3600
|
||||||
WARM_START_AIRSPEED = 300
|
WARM_START_AIRSPEED = 600
|
||||||
|
INTERCEPTION_AIRSPEED = 1200
|
||||||
|
|
||||||
INTERCEPT_ALT = 15000
|
TRANSPORT_LANDING_ALT = 500
|
||||||
CAS_ALTITUDE = 3000
|
|
||||||
|
INTERCEPTION_ALT = 3600
|
||||||
|
CAS_ALTITUDE = 1000
|
||||||
|
|
||||||
INTERCEPT_MAX_DISTANCE_FACTOR = 15
|
INTERCEPT_MAX_DISTANCE_FACTOR = 15
|
||||||
|
|
||||||
|
|
||||||
class AircraftConflictGenerator:
|
class AircraftConflictGenerator:
|
||||||
escort_targets = [] # type: typing.List[PlaneGroup]
|
escort_targets = [] # type: typing.List[PlaneGroup]
|
||||||
|
|
||||||
@ -34,6 +39,39 @@ class AircraftConflictGenerator:
|
|||||||
)
|
)
|
||||||
return point.random_point_within(distance, self.conflict.size * SPREAD_DISTANCE_FACTOR[0])
|
return point.random_point_within(distance, self.conflict.size * SPREAD_DISTANCE_FACTOR[0])
|
||||||
|
|
||||||
|
def _split_to_groups(self, dict: db.PlaneDict, clients: db.PlaneDict = None) -> typing.Collection[typing.Tuple[FlyingType, int, int]]:
|
||||||
|
for flying_type, count in dict.items():
|
||||||
|
if clients:
|
||||||
|
client_count = clients.get(flying_type, 0)
|
||||||
|
else:
|
||||||
|
client_count = 0
|
||||||
|
|
||||||
|
while count > 0:
|
||||||
|
group_size = min(count, 4)
|
||||||
|
client_size = max(min(client_count, 4), 0)
|
||||||
|
|
||||||
|
yield (flying_type, group_size, client_size)
|
||||||
|
count -= group_size
|
||||||
|
client_count -= client_size
|
||||||
|
|
||||||
|
def _setup_group(self, group: FlyingGroup, for_task: Task):
|
||||||
|
did_load_loadout = False
|
||||||
|
unit_type = group.units[0].unit_type
|
||||||
|
if unit_type in db.PLANE_PAYLOAD_OVERRIDES:
|
||||||
|
if for_task in db.PLANE_PAYLOAD_OVERRIDES[unit_type]:
|
||||||
|
group.load_loadout(db.PLANE_PAYLOAD_OVERRIDES[unit_type][for_task])
|
||||||
|
did_load_loadout = True
|
||||||
|
elif "*" in db.PLANE_PAYLOAD_OVERRIDES[unit_type]:
|
||||||
|
group.load_loadout(db.PLANE_PAYLOAD_OVERRIDES[unit_type]["*"])
|
||||||
|
did_load_loadout = True
|
||||||
|
|
||||||
|
if not did_load_loadout:
|
||||||
|
group.load_task_default_loadout(for_task)
|
||||||
|
|
||||||
|
if unit_type in db.PLANE_LIVERY_OVERRIDES:
|
||||||
|
for unit_instance in group.units:
|
||||||
|
unit_instance.livery_id = db.PLANE_LIVERY_OVERRIDES[unit_type]
|
||||||
|
|
||||||
def _generate_at_airport(self, name: str, side: Country, unit_type: FlyingType, count: int, client_count: int, airport: Airport = None) -> FlyingGroup:
|
def _generate_at_airport(self, name: str, side: Country, unit_type: FlyingType, count: int, client_count: int, airport: Airport = None) -> FlyingGroup:
|
||||||
assert count > 0
|
assert count > 0
|
||||||
assert unit is not None
|
assert unit is not None
|
||||||
@ -98,7 +136,10 @@ class AircraftConflictGenerator:
|
|||||||
elif isinstance(at, ShipGroup):
|
elif isinstance(at, ShipGroup):
|
||||||
return self._generate_at_carrier(name, side, unit_type, count, client_count, at)
|
return self._generate_at_carrier(name, side, unit_type, count, client_count, at)
|
||||||
elif issubclass(at, Airport):
|
elif issubclass(at, Airport):
|
||||||
return self._generate_at_airport(name, side, unit_type, count, client_count, at)
|
try:
|
||||||
|
return self._generate_at_airport(name, side, unit_type, count, client_count, at)
|
||||||
|
except NoParkingSlotError:
|
||||||
|
return self._generate_inflight(name, side, unit_type, count, client_count, at.position)
|
||||||
else:
|
else:
|
||||||
assert False
|
assert False
|
||||||
|
|
||||||
@ -106,41 +147,42 @@ class AircraftConflictGenerator:
|
|||||||
if len(self.escort_targets) == 0:
|
if len(self.escort_targets) == 0:
|
||||||
return
|
return
|
||||||
|
|
||||||
for type, count in units.items():
|
for flying_type, count, client_count in self._split_to_groups(units, clients):
|
||||||
group = self._generate_group(
|
group = self._generate_group(
|
||||||
name=namegen.next_escort_group_name(),
|
name=namegen.next_escort_group_name(),
|
||||||
side=side,
|
side=side,
|
||||||
unit_type=type,
|
unit_type=flying_type,
|
||||||
count=count,
|
count=count,
|
||||||
client_count=clients.get(type, 0),
|
client_count=client_count,
|
||||||
at=at)
|
at=at)
|
||||||
|
|
||||||
group.task = Escort.name
|
group.task = Escort.name
|
||||||
group.load_task_default_loadout(dcs.task.Escort)
|
|
||||||
|
|
||||||
heading = group.position.heading_between_point(self.conflict.position)
|
heading = group.position.heading_between_point(self.conflict.position)
|
||||||
position = group.position # type: Point
|
position = group.position # type: Point
|
||||||
wayp = group.add_waypoint(position.point_from_heading(heading, WORKAROUND_WAYP_DIST), CAS_ALTITUDE)
|
wayp = group.add_waypoint(position.point_from_heading(heading, WORKAROUND_WAYP_DIST), CAS_ALTITUDE, WARM_START_AIRSPEED)
|
||||||
|
|
||||||
for group in self.escort_targets:
|
for group in self.escort_targets:
|
||||||
wayp.tasks.append(EscortTaskAction(group.id, engagement_max_dist=ESCORT_MAX_DIST))
|
wayp.tasks.append(EscortTaskAction(group.id, engagement_max_dist=ESCORT_MAX_DIST))
|
||||||
|
|
||||||
|
self._setup_group(group, dcs.task.Escort)
|
||||||
|
|
||||||
def generate_cas(self, attackers: db.PlaneDict, clients: db.PlaneDict, at: db.StartingPosition = None):
|
def generate_cas(self, attackers: db.PlaneDict, clients: db.PlaneDict, at: db.StartingPosition = None):
|
||||||
assert len(self.escort_targets) == 0
|
assert len(self.escort_targets) == 0
|
||||||
|
|
||||||
for type, count in attackers.items():
|
for flying_type, count, client_count in self._split_to_groups(attackers, clients):
|
||||||
group = self._generate_group(
|
group = self._generate_group(
|
||||||
name=namegen.next_cas_group_name(),
|
name=namegen.next_cas_group_name(),
|
||||||
side=self.conflict.attackers_side,
|
side=self.conflict.attackers_side,
|
||||||
unit_type=type,
|
unit_type=flying_type,
|
||||||
count=count,
|
count=count,
|
||||||
client_count=clients.get(type, 0),
|
client_count=client_count,
|
||||||
at=at and at or self._group_point(self.conflict.air_attackers_location))
|
at=at and at or self._group_point(self.conflict.air_attackers_location))
|
||||||
self.escort_targets.append(group)
|
self.escort_targets.append(group)
|
||||||
|
|
||||||
group.add_waypoint(self.conflict.position, CAS_ALTITUDE)
|
group.add_waypoint(self.conflict.position, CAS_ALTITUDE, WARM_START_AIRSPEED)
|
||||||
group.task = CAS.name
|
group.task = CAS.name
|
||||||
group.load_task_default_loadout(CAS)
|
self._setup_group(group, CAS)
|
||||||
|
|
||||||
def generate_cas_escort(self, attackers: db.PlaneDict, clients: db.PlaneDict, at: db.StartingPosition = None):
|
def generate_cas_escort(self, attackers: db.PlaneDict, clients: db.PlaneDict, at: db.StartingPosition = None):
|
||||||
self._generate_escort(
|
self._generate_escort(
|
||||||
@ -157,54 +199,55 @@ class AircraftConflictGenerator:
|
|||||||
at=at and at or self._group_point(self.conflict.air_defenders_location))
|
at=at and at or self._group_point(self.conflict.air_defenders_location))
|
||||||
|
|
||||||
def generate_defense(self, defenders: db.PlaneDict, clients: db.PlaneDict, at: db.StartingPosition = None):
|
def generate_defense(self, defenders: db.PlaneDict, clients: db.PlaneDict, at: db.StartingPosition = None):
|
||||||
for type, count in defenders.items():
|
for flying_type, count, client_count in self._split_to_groups(defenders, clients):
|
||||||
group = self._generate_group(
|
group = self._generate_group(
|
||||||
name=namegen.next_intercept_group_name(),
|
name=namegen.next_intercept_group_name(),
|
||||||
side=self.conflict.defenders_side,
|
side=self.conflict.defenders_side,
|
||||||
unit_type=type,
|
unit_type=flying_type,
|
||||||
count=count,
|
count=count,
|
||||||
client_count=clients.get(type, 0),
|
client_count=client_count,
|
||||||
at=at and at or self._group_point(self.conflict.air_defenders_location))
|
at=at and at or self._group_point(self.conflict.air_defenders_location))
|
||||||
|
|
||||||
group.task = FighterSweep.name
|
group.task = FighterSweep.name
|
||||||
group.load_task_default_loadout(FighterSweep)
|
wayp = group.add_waypoint(self.conflict.position, CAS_ALTITUDE, WARM_START_AIRSPEED)
|
||||||
wayp = group.add_waypoint(self.conflict.position, CAS_ALTITUDE)
|
|
||||||
wayp.tasks.append(dcs.task.EngageTargets(max_distance=self.conflict.size * INTERCEPT_MAX_DISTANCE_FACTOR))
|
wayp.tasks.append(dcs.task.EngageTargets(max_distance=self.conflict.size * INTERCEPT_MAX_DISTANCE_FACTOR))
|
||||||
wayp.tasks.append(dcs.task.OrbitAction())
|
wayp.tasks.append(dcs.task.OrbitAction())
|
||||||
|
self._setup_group(group, FighterSweep)
|
||||||
|
|
||||||
def generate_transport(self, transport: db.PlaneDict, destination: Airport):
|
def generate_transport(self, transport: db.PlaneDict, destination: Airport):
|
||||||
assert len(self.escort_targets) == 0
|
assert len(self.escort_targets) == 0
|
||||||
|
|
||||||
for type, count in transport.items():
|
for flying_type, count, client_count in self._split_to_groups(transport):
|
||||||
group = self._generate_group(
|
group = self._generate_group(
|
||||||
name=namegen.next_transport_group_name(),
|
name=namegen.next_transport_group_name(),
|
||||||
side=self.conflict.defenders_side,
|
side=self.conflict.defenders_side,
|
||||||
unit_type=type,
|
unit_type=flying_type,
|
||||||
count=count,
|
count=count,
|
||||||
client_count=0,
|
client_count=client_count,
|
||||||
at=self._group_point(self.conflict.air_defenders_location))
|
at=self._group_point(self.conflict.air_defenders_location))
|
||||||
|
|
||||||
group.task = Transport.name
|
group.task = Transport.name
|
||||||
|
|
||||||
self.escort_targets.append(group)
|
self.escort_targets.append(group)
|
||||||
|
group.add_waypoint(destination.position.random_point_within(0, 0), TRANSPORT_LANDING_ALT)
|
||||||
group.land_at(destination)
|
group.land_at(destination)
|
||||||
|
|
||||||
def generate_interception(self, interceptors: db.PlaneDict, clients: db.PlaneDict, at: db.StartingPosition = None):
|
def generate_interception(self, interceptors: db.PlaneDict, clients: db.PlaneDict, at: db.StartingPosition = None):
|
||||||
for type, count in interceptors.items():
|
for flying_type, count, client_count in self._split_to_groups(interceptors, clients):
|
||||||
group = self._generate_group(
|
group = self._generate_group(
|
||||||
name=namegen.next_intercept_group_name(),
|
name=namegen.next_intercept_group_name(),
|
||||||
side=self.conflict.attackers_side,
|
side=self.conflict.attackers_side,
|
||||||
unit_type=type,
|
unit_type=flying_type,
|
||||||
count=count,
|
count=count,
|
||||||
client_count=clients.get(type, 0),
|
client_count=client_count,
|
||||||
at=at and at or self._group_point(self.conflict.air_attackers_location))
|
at=at and at or self._group_point(self.conflict.air_attackers_location))
|
||||||
|
|
||||||
group.task = FighterSweep.name
|
group.task = FighterSweep.name
|
||||||
group.load_task_default_loadout(FighterSweep)
|
|
||||||
|
|
||||||
heading = group.position.heading_between_point(self.conflict.position)
|
heading = group.position.heading_between_point(self.conflict.position)
|
||||||
initial_wayp = group.add_waypoint(group.position.point_from_heading(heading, WORKAROUND_WAYP_DIST), INTERCEPT_ALT)
|
initial_wayp = group.add_waypoint(group.position.point_from_heading(heading, WORKAROUND_WAYP_DIST), INTERCEPTION_ALT, INTERCEPTION_AIRSPEED)
|
||||||
initial_wayp.tasks.append(EngageTargets())
|
initial_wayp.tasks.append(EngageTargets())
|
||||||
|
|
||||||
wayp = group.add_waypoint(self.conflict.position, 0)
|
wayp = group.add_waypoint(self.conflict.position, 0)
|
||||||
wayp.tasks.append(EngageTargets())
|
wayp.tasks.append(EngageTargets())
|
||||||
|
self._setup_group(group, FighterSweep)
|
||||||
|
|||||||
@ -18,13 +18,14 @@ def _opposite_heading(h):
|
|||||||
return h+180
|
return h+180
|
||||||
|
|
||||||
GROUND_DISTANCE_FACTOR = 2
|
GROUND_DISTANCE_FACTOR = 2
|
||||||
AIR_DISTANCE = 8000
|
AIR_DISTANCE = 32000
|
||||||
|
|
||||||
INTERCEPT_ATTACKERS_HEADING = -45, 45
|
INTERCEPT_ATTACKERS_HEADING = -45, 45
|
||||||
INTERCEPT_DEFENDERS_HEADING = -10, 10
|
INTERCEPT_DEFENDERS_HEADING = -10, 10
|
||||||
INTERCEPT_ATTACKERS_DISTANCE = 60000
|
INTERCEPT_ATTACKERS_DISTANCE = 60000
|
||||||
INTERCEPT_DEFENDERS_DISTANCE = 30000
|
INTERCEPT_DEFENDERS_DISTANCE = 30000
|
||||||
INTERCEPT_MAX_DISTANCE = 45000
|
INTERCEPT_MAX_DISTANCE = 80000
|
||||||
|
INTERCEPT_MIN_DISTANCE = 45000
|
||||||
|
|
||||||
|
|
||||||
class Conflict:
|
class Conflict:
|
||||||
@ -62,7 +63,8 @@ class Conflict:
|
|||||||
from theater.conflicttheater import ALL_RADIALS
|
from theater.conflicttheater import ALL_RADIALS
|
||||||
|
|
||||||
heading = from_cp.position.heading_between_point(to_cp.position)
|
heading = from_cp.position.heading_between_point(to_cp.position)
|
||||||
distance = min(from_cp.position.distance_to_point(to_cp.position) / 2, INTERCEPT_MAX_DISTANCE)
|
raw_distance = from_cp.position.distance_to_point(to_cp.position) / 2
|
||||||
|
distance = max(min(raw_distance, INTERCEPT_MAX_DISTANCE), INTERCEPT_MIN_DISTANCE)
|
||||||
position = from_cp.position.point_from_heading(heading, distance)
|
position = from_cp.position.point_from_heading(heading, distance)
|
||||||
|
|
||||||
instance = self()
|
instance = self()
|
||||||
@ -74,7 +76,7 @@ class Conflict:
|
|||||||
instance.radials = ALL_RADIALS
|
instance.radials = ALL_RADIALS
|
||||||
|
|
||||||
instance.air_attackers_location = instance.position.point_from_heading(random.randint(*INTERCEPT_ATTACKERS_HEADING) + heading, INTERCEPT_ATTACKERS_DISTANCE)
|
instance.air_attackers_location = instance.position.point_from_heading(random.randint(*INTERCEPT_ATTACKERS_HEADING) + heading, INTERCEPT_ATTACKERS_DISTANCE)
|
||||||
instance.air_defenders_location = instance.position.point_from_heading(random.randint(*INTERCEPT_DEFENDERS_HEADING) + heading, INTERCEPT_DEFENDERS_DISTANCE)
|
instance.air_defenders_location = instance.position
|
||||||
|
|
||||||
return instance
|
return instance
|
||||||
|
|
||||||
|
|||||||
@ -2,9 +2,15 @@ import typing
|
|||||||
import random
|
import random
|
||||||
|
|
||||||
from dcs.mission import Mission
|
from dcs.mission import Mission
|
||||||
|
from dcs.triggers import *
|
||||||
|
from dcs.condition import *
|
||||||
|
from dcs.action import *
|
||||||
|
|
||||||
from theater.weatherforecast import WeatherForecast
|
from theater.weatherforecast import WeatherForecast
|
||||||
|
from theater.conflicttheater import Conflict
|
||||||
|
|
||||||
|
ACTIVATION_TRIGGER_SIZE = 80000
|
||||||
|
ACTIVATION_TRIGGER_MIN_DISTANCE = 5000
|
||||||
|
|
||||||
RANDOM_TIME = {
|
RANDOM_TIME = {
|
||||||
"night": 0,
|
"night": 0,
|
||||||
@ -22,11 +28,12 @@ RANDOM_WEATHER = {
|
|||||||
}
|
}
|
||||||
|
|
||||||
class EnvironmentSettingsGenerator:
|
class EnvironmentSettingsGenerator:
|
||||||
def __init__(self, mission: Mission, game):
|
def __init__(self, mission: Mission, conflict: Conflict, game):
|
||||||
self.mission = mission
|
self.mission = mission
|
||||||
|
self.conflict = conflict
|
||||||
self.game = game
|
self.game = game
|
||||||
|
|
||||||
def generate(self):
|
def generate(self, is_quick: bool):
|
||||||
time_roll = random.randint(0, 100)
|
time_roll = random.randint(0, 100)
|
||||||
time_period = None
|
time_period = None
|
||||||
for k, v in RANDOM_TIME.items():
|
for k, v in RANDOM_TIME.items():
|
||||||
@ -53,10 +60,34 @@ class EnvironmentSettingsGenerator:
|
|||||||
elif weather_type == 3:
|
elif weather_type == 3:
|
||||||
self.mission.weather.random(self.mission.start_time, self.mission.terrain)
|
self.mission.weather.random(self.mission.start_time, self.mission.terrain)
|
||||||
|
|
||||||
|
player_coalition = self.game.player == "USA" and "blue" or "red"
|
||||||
|
enemy_coalition = player_coalition == "blue" and "red" or "blue"
|
||||||
|
|
||||||
for cp in self.game.theater.controlpoints:
|
for cp in self.game.theater.controlpoints:
|
||||||
if cp.is_global:
|
if cp.is_global:
|
||||||
continue
|
continue
|
||||||
|
|
||||||
player_coalition = self.game.player == "USA" and "blue" or "red"
|
|
||||||
enemy_coalition = player_coalition == "blue" and "red" or "blue"
|
|
||||||
self.mission.terrain.airport_by_id(cp.at.id).set_coalition(cp.captured and player_coalition or enemy_coalition)
|
self.mission.terrain.airport_by_id(cp.at.id).set_coalition(cp.captured and player_coalition or enemy_coalition)
|
||||||
|
|
||||||
|
if not is_quick:
|
||||||
|
activate_by_trigger = []
|
||||||
|
for coalition_name, coalition in self.mission.coalition.items():
|
||||||
|
for country in coalition.countries.values():
|
||||||
|
if coalition_name == enemy_coalition:
|
||||||
|
for plane_group in country.plane_group:
|
||||||
|
plane_group.late_activation = True
|
||||||
|
activate_by_trigger.append(plane_group)
|
||||||
|
|
||||||
|
for vehicle_group in country.vehicle_group:
|
||||||
|
vehicle_group.late_activation = True
|
||||||
|
activate_by_trigger.append(vehicle_group)
|
||||||
|
|
||||||
|
zone_distance_to_aircraft = self.conflict.air_attackers_location.distance_to_point(self.conflict.position)
|
||||||
|
zone_size = min(zone_distance_to_aircraft - ACTIVATION_TRIGGER_MIN_DISTANCE, ACTIVATION_TRIGGER_SIZE)
|
||||||
|
|
||||||
|
activation_trigger_zone = self.mission.triggers.add_triggerzone(self.conflict.position, zone_size)
|
||||||
|
activation_trigger = TriggerOnce(Event.NoEvent, "Activation trigger")
|
||||||
|
activation_trigger.add_condition(PartOfCoalitionInZone(player_coalition, activation_trigger_zone.id))
|
||||||
|
for group in activate_by_trigger:
|
||||||
|
activation_trigger.add_action(ActivateGroup(group.id))
|
||||||
|
|
||||||
|
self.mission.triggerrules.triggers.append(activation_trigger)
|
||||||
|
|||||||
@ -62,10 +62,8 @@ class ControlPoint:
|
|||||||
return closest_radial
|
return closest_radial
|
||||||
|
|
||||||
def conflict_attack(self, from_cp, attacker: Country, defender: Country) -> Conflict:
|
def conflict_attack(self, from_cp, attacker: Country, defender: Country) -> Conflict:
|
||||||
cp = from_cp # type: ControlPoint
|
attack_radial = self.find_radial(self.position.heading_between_point(from_cp.position))
|
||||||
|
defense_radial = self.find_radial(from_cp.position.heading_between_point(self.position), ignored_radial=attack_radial)
|
||||||
attack_radial = self.find_radial(cp.position.heading_between_point(self.position))
|
|
||||||
defense_radial = self.find_radial(self.position.heading_between_point(cp.position), ignored_radial=attack_radial)
|
|
||||||
|
|
||||||
return Conflict.capture_conflict(attacker=attacker,
|
return Conflict.capture_conflict(attacker=attacker,
|
||||||
attack_heading=attack_radial,
|
attack_heading=attack_radial,
|
||||||
|
|||||||
@ -17,6 +17,11 @@ class EventMenu(Menu):
|
|||||||
self.armor_scramble_entries = {}
|
self.armor_scramble_entries = {}
|
||||||
self.aircraft_client_entries = {}
|
self.aircraft_client_entries = {}
|
||||||
|
|
||||||
|
if self.event.attacker_name == self.game.player:
|
||||||
|
self.base = self.event.from_cp.base
|
||||||
|
else:
|
||||||
|
self.base = self.event.to_cp.base
|
||||||
|
|
||||||
self.frame = self.window.right_pane
|
self.frame = self.window.right_pane
|
||||||
|
|
||||||
def display(self):
|
def display(self):
|
||||||
@ -55,28 +60,22 @@ class EventMenu(Menu):
|
|||||||
|
|
||||||
row += 1
|
row += 1
|
||||||
|
|
||||||
base = None # type: Base
|
|
||||||
if self.event.attacker_name == self.game.player:
|
|
||||||
base = self.event.from_cp.base
|
|
||||||
else:
|
|
||||||
base = self.event.to_cp.base
|
|
||||||
|
|
||||||
label("Aircraft")
|
label("Aircraft")
|
||||||
label("Amount", row, 1)
|
label("Amount", row, 1)
|
||||||
label("Client slots", row, 2)
|
label("Client slots", row, 2)
|
||||||
row += 1
|
row += 1
|
||||||
|
|
||||||
for unit_type, count in base.aircraft.items():
|
for unit_type, count in self.base.aircraft.items():
|
||||||
scrable_row(unit_type, count)
|
scrable_row(unit_type, count)
|
||||||
|
|
||||||
if not base.total_planes:
|
if not self.base.total_planes:
|
||||||
label("None")
|
label("None")
|
||||||
|
|
||||||
label("Armor")
|
label("Armor")
|
||||||
for unit_type, count in base.armor.items():
|
for unit_type, count in self.base.armor.items():
|
||||||
scramble_armor_row(unit_type, count)
|
scramble_armor_row(unit_type, count)
|
||||||
|
|
||||||
if not base.total_armor:
|
if not self.base.total_armor:
|
||||||
label("None")
|
label("None")
|
||||||
|
|
||||||
Button(self.frame, text="Commit", command=self.start).grid(column=0, row=row)
|
Button(self.frame, text="Commit", command=self.start).grid(column=0, row=row)
|
||||||
@ -89,6 +88,7 @@ class EventMenu(Menu):
|
|||||||
for unit_type, field in self.aircraft_scramble_entries.items():
|
for unit_type, field in self.aircraft_scramble_entries.items():
|
||||||
value = field.get()
|
value = field.get()
|
||||||
if value and int(value) > 0:
|
if value and int(value) > 0:
|
||||||
|
#amount = min(int(value), self.base.aircraft[unit_type])
|
||||||
amount = int(value)
|
amount = int(value)
|
||||||
task = db.unit_task(unit_type)
|
task = db.unit_task(unit_type)
|
||||||
|
|
||||||
|
|||||||
@ -72,6 +72,11 @@ class Debriefing:
|
|||||||
enemy.name: calculate_losses(enemy_units, self.alive_units.get(enemy.id, {})),
|
enemy.name: calculate_losses(enemy_units, self.alive_units.get(enemy.id, {})),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
self.alive_units = {
|
||||||
|
player.name: self.alive_units.get(player.id, {}),
|
||||||
|
enemy.name: self.alive_units.get(enemy.id, {}),
|
||||||
|
}
|
||||||
|
|
||||||
def debriefing_directory_location() -> str:
|
def debriefing_directory_location() -> str:
|
||||||
return "build/debriefing"
|
return "build/debriefing"
|
||||||
|
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user