Implemented new options in settings:

- Pretense: Extra friendly zone connections - Add connections from each zone to this many closest friendly zones, which don't have an existing supply route defined in the campaign.
- Number of cargo planes per side
- Number of AI SEAD flights per control point / zone
- Number of AI CAS flights per control point / zone
- Number of AI BAI flights per control point / zone
- Number of AI Strike flights per control point / zone
- Number of AI BARCAP flights per control point / zone
- Number of AI aircraft per flight
- Number of player flights per aircraft type at each base
- Number of AI cargo planes per side

Implemented CAS helo mission handling for Pretense. Implemented separate pretense_air_groups container for storing/referencing Flight objects. Tweaked the supply costs of SAM sites and Command centers. Will no longer generate player air starts at roadbases either. Restored the missing DEAD flights to Pretense. Removed spawning of frontline units and moved the JTAC spawning to pretensemissiongenerator.py
This commit is contained in:
MetalStormGhost 2023-11-22 19:46:44 +02:00
parent e15aca8c54
commit f4e8e30cb1
9 changed files with 461 additions and 339 deletions

View File

@ -22,6 +22,7 @@ from game.models.game_stats import GameStats
from game.plugins import LuaPluginManager
from game.utils import Distance
from . import naming, persistency
from .ato import Flight
from .ato.flighttype import FlightType
from .campaignloader import CampaignAirWingConfig
from .coalition import Coalition
@ -155,6 +156,7 @@ class Game:
1: {},
2: {},
}
self.pretense_air_groups: dict[str, Flight] = {}
self.on_load(game_still_initializing=True)

View File

@ -290,11 +290,9 @@ class GroundObjectGenerator:
# All alive Ships
ship_units.append(unit)
if vehicle_units:
vg = self.create_vehicle_group(group.group_name, vehicle_units)
vg.hidden_on_mfd = self.ground_object.hide_on_mfd
self.create_vehicle_group(group.group_name, vehicle_units)
if ship_units:
sg = self.create_ship_group(group.group_name, ship_units)
sg.hidden_on_mfd = self.ground_object.hide_on_mfd
self.create_ship_group(group.group_name, ship_units)
def create_vehicle_group(
self, group_name: str, units: list[TheaterUnit]
@ -827,30 +825,45 @@ class HelipadGenerator:
else:
self.helipads.append(sg)
# Generate a FARP Ammo and Fuel stack for each pad
self.m.static_group(
country=country,
name=(name + "_fuel"),
_type=Fortification.FARP_Fuel_Depot,
position=pad.position.point_from_heading(helipad.heading.degrees, 35),
heading=pad.heading + 180,
)
self.m.static_group(
country=country,
name=(name + "_ammo"),
_type=Fortification.FARP_Ammo_Dump_Coating,
position=pad.position.point_from_heading(
helipad.heading.degrees, 35
).point_from_heading(helipad.heading.degrees + 90, 10),
heading=pad.heading + 90,
)
self.m.static_group(
country=country,
name=(name + "_ws"),
_type=Fortification.Windsock,
position=helipad.point_from_heading(helipad.heading.degrees + 45, 35),
heading=pad.heading,
)
if self.game.position_culled(helipad):
cull_farp_statics = True
if self.cp.coalition.player:
for package in self.cp.coalition.ato.packages:
for flight in package.flights:
if flight.squadron.location == self.cp:
cull_farp_statics = False
break
elif flight.divert and flight.divert == self.cp:
cull_farp_statics = False
break
else:
cull_farp_statics = False
if not cull_farp_statics:
# Generate a FARP Ammo and Fuel stack for each pad
self.m.static_group(
country=country,
name=(name + "_fuel"),
_type=Fortification.FARP_Fuel_Depot,
position=pad.position.point_from_heading(helipad.heading.degrees, 35),
heading=pad.heading + 180,
)
self.m.static_group(
country=country,
name=(name + "_ammo"),
_type=Fortification.FARP_Ammo_Dump_Coating,
position=pad.position.point_from_heading(
helipad.heading.degrees, 35
).point_from_heading(helipad.heading.degrees + 90, 10),
heading=pad.heading + 90,
)
self.m.static_group(
country=country,
name=(name + "_ws"),
_type=Fortification.Windsock,
position=helipad.point_from_heading(helipad.heading.degrees + 45, 35),
heading=pad.heading,
)
def append_helipad(
self,
@ -927,61 +940,76 @@ class GroundSpawnRoadbaseGenerator:
country.id
)
# Generate ammo truck/farp and fuel truck/stack for each pad
if self.game.settings.ground_start_trucks_roadbase:
self.m.vehicle_group(
country=country,
name=(name + "_fuel"),
_type=tanker_type,
position=pad.position.point_from_heading(
ground_spawn[0].heading.degrees + 90, 35
),
group_size=1,
heading=pad.heading + 315,
move_formation=PointAction.OffRoad,
)
self.m.vehicle_group(
country=country,
name=(name + "_ammo"),
_type=ammo_truck_type,
position=pad.position.point_from_heading(
ground_spawn[0].heading.degrees + 90, 35
).point_from_heading(ground_spawn[0].heading.degrees + 180, 10),
group_size=1,
heading=pad.heading + 315,
move_formation=PointAction.OffRoad,
)
if self.game.position_culled(ground_spawn[0]):
cull_farp_statics = True
if self.cp.coalition.player:
for package in self.cp.coalition.ato.packages:
for flight in package.flights:
if flight.squadron.location == self.cp:
cull_farp_statics = False
break
elif flight.divert and flight.divert == self.cp:
cull_farp_statics = False
break
else:
self.m.static_group(
country=country,
name=(name + "_fuel"),
_type=Fortification.FARP_Fuel_Depot,
position=pad.position.point_from_heading(
ground_spawn[0].heading.degrees + 90, 35
),
heading=pad.heading + 270,
)
self.m.static_group(
country=country,
name=(name + "_ammo"),
_type=Fortification.FARP_Ammo_Dump_Coating,
position=pad.position.point_from_heading(
ground_spawn[0].heading.degrees + 90, 35
).point_from_heading(ground_spawn[0].heading.degrees + 180, 10),
heading=pad.heading + 180,
)
if self.game.settings.ground_start_ground_power_trucks_roadbase:
self.m.vehicle_group(
country=country,
name=(name + "_power"),
_type=power_truck_type,
position=pad.position.point_from_heading(
ground_spawn[0].heading.degrees + 90, 35
).point_from_heading(ground_spawn[0].heading.degrees + 180, 20),
group_size=1,
heading=pad.heading + 315,
move_formation=PointAction.OffRoad,
)
cull_farp_statics = False
if not cull_farp_statics:
# Generate ammo truck/farp and fuel truck/stack for each pad
if self.game.settings.ground_start_trucks_roadbase:
self.m.vehicle_group(
country=country,
name=(name + "_fuel"),
_type=tanker_type,
position=pad.position.point_from_heading(
ground_spawn[0].heading.degrees + 90, 35
),
group_size=1,
heading=pad.heading + 315,
move_formation=PointAction.OffRoad,
)
self.m.vehicle_group(
country=country,
name=(name + "_ammo"),
_type=ammo_truck_type,
position=pad.position.point_from_heading(
ground_spawn[0].heading.degrees + 90, 35
).point_from_heading(ground_spawn[0].heading.degrees + 180, 10),
group_size=1,
heading=pad.heading + 315,
move_formation=PointAction.OffRoad,
)
else:
self.m.static_group(
country=country,
name=(name + "_fuel"),
_type=Fortification.FARP_Fuel_Depot,
position=pad.position.point_from_heading(
ground_spawn[0].heading.degrees + 90, 35
),
heading=pad.heading + 270,
)
self.m.static_group(
country=country,
name=(name + "_ammo"),
_type=Fortification.FARP_Ammo_Dump_Coating,
position=pad.position.point_from_heading(
ground_spawn[0].heading.degrees + 90, 35
).point_from_heading(ground_spawn[0].heading.degrees + 180, 10),
heading=pad.heading + 180,
)
if self.game.settings.ground_start_ground_power_trucks_roadbase:
self.m.vehicle_group(
country=country,
name=(name + "_power"),
_type=power_truck_type,
position=pad.position.point_from_heading(
ground_spawn[0].heading.degrees + 90, 35
).point_from_heading(ground_spawn[0].heading.degrees + 180, 20),
group_size=1,
heading=pad.heading + 315,
move_formation=PointAction.OffRoad,
)
def generate(self) -> None:
try:
@ -1044,61 +1072,76 @@ class GroundSpawnGenerator:
country.id
)
# Generate a FARP Ammo and Fuel stack for each pad
if self.game.settings.ground_start_trucks:
self.m.vehicle_group(
country=country,
name=(name + "_fuel"),
_type=tanker_type,
position=pad.position.point_from_heading(
vtol_pad[0].heading.degrees - 175, 35
),
group_size=1,
heading=pad.heading + 45,
move_formation=PointAction.OffRoad,
)
self.m.vehicle_group(
country=country,
name=(name + "_ammo"),
_type=ammo_truck_type,
position=pad.position.point_from_heading(
vtol_pad[0].heading.degrees - 185, 35
),
group_size=1,
heading=pad.heading + 45,
move_formation=PointAction.OffRoad,
)
if self.game.position_culled(vtol_pad[0]):
cull_farp_statics = True
if self.cp.coalition.player:
for package in self.cp.coalition.ato.packages:
for flight in package.flights:
if flight.squadron.location == self.cp:
cull_farp_statics = False
break
elif flight.divert and flight.divert == self.cp:
cull_farp_statics = False
break
else:
self.m.static_group(
country=country,
name=(name + "_fuel"),
_type=Fortification.FARP_Fuel_Depot,
position=pad.position.point_from_heading(
vtol_pad[0].heading.degrees - 180, 45
),
heading=pad.heading,
)
self.m.static_group(
country=country,
name=(name + "_ammo"),
_type=Fortification.FARP_Ammo_Dump_Coating,
position=pad.position.point_from_heading(
vtol_pad[0].heading.degrees - 180, 35
),
heading=pad.heading + 270,
)
if self.game.settings.ground_start_ground_power_trucks:
self.m.vehicle_group(
country=country,
name=(name + "_power"),
_type=power_truck_type,
position=pad.position.point_from_heading(
vtol_pad[0].heading.degrees - 185, 35
),
group_size=1,
heading=pad.heading + 45,
move_formation=PointAction.OffRoad,
)
cull_farp_statics = False
if not cull_farp_statics:
# Generate a FARP Ammo and Fuel stack for each pad
if self.game.settings.ground_start_trucks:
self.m.vehicle_group(
country=country,
name=(name + "_fuel"),
_type=tanker_type,
position=pad.position.point_from_heading(
vtol_pad[0].heading.degrees - 175, 35
),
group_size=1,
heading=pad.heading + 45,
move_formation=PointAction.OffRoad,
)
self.m.vehicle_group(
country=country,
name=(name + "_ammo"),
_type=ammo_truck_type,
position=pad.position.point_from_heading(
vtol_pad[0].heading.degrees - 185, 35
),
group_size=1,
heading=pad.heading + 45,
move_formation=PointAction.OffRoad,
)
else:
self.m.static_group(
country=country,
name=(name + "_fuel"),
_type=Fortification.FARP_Fuel_Depot,
position=pad.position.point_from_heading(
vtol_pad[0].heading.degrees - 180, 45
),
heading=pad.heading,
)
self.m.static_group(
country=country,
name=(name + "_ammo"),
_type=Fortification.FARP_Ammo_Dump_Coating,
position=pad.position.point_from_heading(
vtol_pad[0].heading.degrees - 180, 35
),
heading=pad.heading + 270,
)
if self.game.settings.ground_start_ground_power_trucks:
self.m.vehicle_group(
country=country,
name=(name + "_power"),
_type=power_truck_type,
position=pad.position.point_from_heading(
vtol_pad[0].heading.degrees - 185, 35
),
group_size=1,
heading=pad.heading + 45,
move_formation=PointAction.OffRoad,
)
def generate(self) -> None:
try:

View File

@ -49,17 +49,9 @@ if TYPE_CHECKING:
PRETENSE_SQUADRON_DEF_RETRIES = 100
PRETENSE_SEAD_FLIGHTS_PER_CP = 2
PRETENSE_CAS_FLIGHTS_PER_CP = 2
PRETENSE_BAI_FLIGHTS_PER_CP = 2
PRETENSE_STRIKE_FLIGHTS_PER_CP = 2
PRETENSE_BARCAP_FLIGHTS_PER_CP = 2
PRETENSE_AI_AIRCRAFT_PER_FLIGHT = 2
PRETENSE_AI_AWACS_PER_FLIGHT = 1
PRETENSE_AI_TANKERS_PER_FLIGHT = 1
PRETENSE_AI_CARGO_PLANES_PER_SIDE = 2
PRETENSE_PLAYER_AIRCRAFT_PER_FLIGHT = 1
PRETENSE_PLAYER_FLIGHTS_PER_TYPE = 2
class PretenseAircraftGenerator:
@ -300,8 +292,12 @@ class PretenseAircraftGenerator:
if cp.coalition != squadron.coalition:
continue
squadron.owned_aircraft += PRETENSE_AI_AIRCRAFT_PER_FLIGHT
squadron.untasked_aircraft += PRETENSE_AI_AIRCRAFT_PER_FLIGHT
squadron.owned_aircraft += (
self.game.settings.pretense_ai_aircraft_per_flight
)
squadron.untasked_aircraft += (
self.game.settings.pretense_ai_aircraft_per_flight
)
squadron.populate_for_turn_0(False)
package = Package(cp, squadron.flight_db, auto_asap=False)
mission_types = squadron.auto_assignable_mission_types
@ -320,46 +316,46 @@ class PretenseAircraftGenerator:
FlightType.SEAD in mission_types
or FlightType.SEAD_SWEEP in mission_types
or FlightType.SEAD_ESCORT in mission_types
) and num_of_sead < PRETENSE_SEAD_FLIGHTS_PER_CP:
) and num_of_sead < self.game.settings.pretense_sead_flights_per_cp:
flight_type = FlightType.SEAD
num_of_sead += 1
aircraft_per_flight = PRETENSE_AI_AIRCRAFT_PER_FLIGHT
aircraft_per_flight = self.game.settings.pretense_ai_aircraft_per_flight
elif (
FlightType.DEAD in mission_types
and num_of_sead < PRETENSE_SEAD_FLIGHTS_PER_CP
and num_of_sead < self.game.settings.pretense_sead_flights_per_cp
):
flight_type = FlightType.DEAD
num_of_sead += 1
aircraft_per_flight = PRETENSE_AI_AIRCRAFT_PER_FLIGHT
aircraft_per_flight = self.game.settings.pretense_ai_aircraft_per_flight
elif (
FlightType.CAS in mission_types
) and num_of_cas < PRETENSE_CAS_FLIGHTS_PER_CP:
) and num_of_cas < self.game.settings.pretense_cas_flights_per_cp:
flight_type = FlightType.CAS
num_of_cas += 1
aircraft_per_flight = PRETENSE_AI_AIRCRAFT_PER_FLIGHT
aircraft_per_flight = self.game.settings.pretense_ai_aircraft_per_flight
elif (
FlightType.BAI in mission_types
) and num_of_bai < PRETENSE_BAI_FLIGHTS_PER_CP:
) and num_of_bai < self.game.settings.pretense_bai_flights_per_cp:
flight_type = FlightType.BAI
num_of_bai += 1
aircraft_per_flight = PRETENSE_AI_AIRCRAFT_PER_FLIGHT
aircraft_per_flight = self.game.settings.pretense_ai_aircraft_per_flight
elif (
FlightType.STRIKE in mission_types
or FlightType.OCA_RUNWAY in mission_types
or FlightType.OCA_AIRCRAFT in mission_types
) and num_of_strike < PRETENSE_STRIKE_FLIGHTS_PER_CP:
) and num_of_strike < self.game.settings.pretense_strike_flights_per_cp:
flight_type = FlightType.STRIKE
num_of_strike += 1
aircraft_per_flight = PRETENSE_AI_AIRCRAFT_PER_FLIGHT
aircraft_per_flight = self.game.settings.pretense_ai_aircraft_per_flight
elif (
FlightType.BARCAP in mission_types
or FlightType.TARCAP in mission_types
or FlightType.ESCORT in mission_types
or FlightType.INTERCEPTION in mission_types
) and num_of_cap < PRETENSE_BARCAP_FLIGHTS_PER_CP:
) and num_of_cap < self.game.settings.pretense_barcap_flights_per_cp:
flight_type = FlightType.BARCAP
num_of_cap += 1
aircraft_per_flight = PRETENSE_AI_AIRCRAFT_PER_FLIGHT
aircraft_per_flight = self.game.settings.pretense_ai_aircraft_per_flight
elif FlightType.AEWC in mission_types:
flight_type = FlightType.AEWC
aircraft_per_flight = PRETENSE_AI_AWACS_PER_FLIGHT
@ -429,8 +425,12 @@ class PretenseAircraftGenerator:
PRETENSE_SQUADRON_DEF_RETRIES,
)
if squadron is not None:
squadron.owned_aircraft += PRETENSE_AI_AIRCRAFT_PER_FLIGHT
squadron.untasked_aircraft += PRETENSE_AI_AIRCRAFT_PER_FLIGHT
squadron.owned_aircraft += (
self.game.settings.pretense_ai_aircraft_per_flight
)
squadron.untasked_aircraft += (
self.game.settings.pretense_ai_aircraft_per_flight
)
squadron.populate_for_turn_0(False)
package = Package(cp, squadron.flight_db, auto_asap=False)
flight = Flight(
@ -453,7 +453,7 @@ class PretenseAircraftGenerator:
if isinstance(cp, Airfield):
# Generate SEAD flight
flight_type = FlightType.SEAD
aircraft_per_flight = PRETENSE_AI_AIRCRAFT_PER_FLIGHT
aircraft_per_flight = self.game.settings.pretense_ai_aircraft_per_flight
squadron = self.generate_pretense_squadron(
cp,
coalition,
@ -470,8 +470,12 @@ class PretenseAircraftGenerator:
PRETENSE_SQUADRON_DEF_RETRIES,
)
if squadron is not None:
squadron.owned_aircraft += PRETENSE_AI_AIRCRAFT_PER_FLIGHT
squadron.untasked_aircraft += PRETENSE_AI_AIRCRAFT_PER_FLIGHT
squadron.owned_aircraft += (
self.game.settings.pretense_ai_aircraft_per_flight
)
squadron.untasked_aircraft += (
self.game.settings.pretense_ai_aircraft_per_flight
)
squadron.populate_for_turn_0(False)
package = Package(cp, squadron.flight_db, auto_asap=False)
flight = Flight(
@ -494,7 +498,7 @@ class PretenseAircraftGenerator:
# Generate CAS flight
flight_type = FlightType.CAS
aircraft_per_flight = PRETENSE_AI_AIRCRAFT_PER_FLIGHT
aircraft_per_flight = self.game.settings.pretense_ai_aircraft_per_flight
squadron = self.generate_pretense_squadron(
cp,
coalition,
@ -511,8 +515,12 @@ class PretenseAircraftGenerator:
PRETENSE_SQUADRON_DEF_RETRIES,
)
if squadron is not None:
squadron.owned_aircraft += PRETENSE_AI_AIRCRAFT_PER_FLIGHT
squadron.untasked_aircraft += PRETENSE_AI_AIRCRAFT_PER_FLIGHT
squadron.owned_aircraft += (
self.game.settings.pretense_ai_aircraft_per_flight
)
squadron.untasked_aircraft += (
self.game.settings.pretense_ai_aircraft_per_flight
)
squadron.populate_for_turn_0(False)
package = Package(cp, squadron.flight_db, auto_asap=False)
flight = Flight(
@ -561,7 +569,7 @@ class PretenseAircraftGenerator:
if not cp.can_operate(aircraft_type):
continue
for i in range(PRETENSE_PLAYER_FLIGHTS_PER_TYPE):
for i in range(self.game.settings.pretense_player_flights_per_type):
squadron = self.generate_pretense_squadron_for(
aircraft_type,
cp,
@ -607,7 +615,7 @@ class PretenseAircraftGenerator:
cp: Control point to generate aircraft for.
flight: The current flight being generated.
"""
cp_name_trimmed = "".join([i for i in cp.name.lower() if i.isalnum()])
cp_name_trimmed = "".join([i for i in cp.name.lower() if i.isalpha()])
for side in range(1, 3):
if cp_name_trimmed not in cp.coalition.game.pretense_air[side]:
@ -627,7 +635,7 @@ class PretenseAircraftGenerator:
flight: The current flight being generated.
"""
flight_type = flight.flight_type
cp_name_trimmed = "".join([i for i in cp.name.lower() if i.isalnum()])
cp_name_trimmed = "".join([i for i in cp.name.lower() if i.isalpha()])
for side in range(1, 3):
if cp_name_trimmed not in flight.coalition.game.pretense_air[side]:
@ -675,7 +683,7 @@ class PretenseAircraftGenerator:
PRETENSE_SQUADRON_DEF_RETRIES,
)
num_of_cargo_sq_to_generate = (
PRETENSE_AI_CARGO_PLANES_PER_SIDE
self.game.settings.pretense_ai_cargo_planes_per_side
- self.number_of_pretense_cargo_plane_sq_for(cp.coalition.air_wing)
)
for i in range(num_of_cargo_sq_to_generate):
@ -795,7 +803,8 @@ class PretenseAircraftGenerator:
if flight.package.target != flight.departure:
break
for mission_target in cp.ground_objects:
flight.package.target = mission_target
if mission_target.alive_unit_count > 0:
flight.package.target = mission_target
break
elif (
flight.flight_type == FlightType.OCA_RUNWAY
@ -837,23 +846,30 @@ class PretenseAircraftGenerator:
break
now = self.game.conditions.start_time
flight.package.set_tot_asap(now)
try:
flight.package.set_tot_asap(now)
except:
raise RuntimeError(
f"Pretense flight group {group.name} {flight.squadron.aircraft} {flight.flight_type} for target {flight.package.target} configuration failed. Please check if your Retribution campaign is compatible with Pretense."
)
logging.info(
f"Configuring flight {group.name} {flight.squadron.aircraft} {flight.flight_type}, number of players: {flight.client_count}"
)
PretenseFlightGroupConfigurator(
flight,
group,
self.game,
self.mission,
self.time,
self.radio_registry,
self.tacan_registy,
self.mission_data,
dynamic_runways,
self.use_client,
).configure()
self.mission_data.flights.append(
PretenseFlightGroupConfigurator(
flight,
group,
self.game,
self.mission,
self.time,
self.radio_registry,
self.tacan_registy,
self.mission_data,
dynamic_runways,
self.use_client,
).configure()
)
if self.ewrj:
self._track_ewrj_flight(flight, group)

View File

@ -30,7 +30,7 @@ class PretenseNameGenerator(NameGenerator):
@classmethod
def next_pretense_aircraft_name(cls, cp: ControlPoint, flight: Flight) -> str:
cls.aircraft_number += 1
cp_name_trimmed = "".join([i for i in cp.name.lower() if i.isalnum()])
cp_name_trimmed = "".join([i for i in cp.name.lower() if i.isalpha()])
return "{}-{}-{}".format(
cp_name_trimmed, str(flight.flight_type).lower(), cls.aircraft_number
)
@ -77,12 +77,17 @@ class PretenseFlightGroupSpawner(FlightGroupSpawner):
== self.flight.coalition.game.coalition_for(is_player)
else 1
)
cp_name_trimmed = "".join([i for i in cp.name.lower() if i.isalnum()])
cp_name_trimmed = "".join([i for i in cp.name.lower() if i.isalpha()])
if self.flight.client_count == 0:
self.flight.coalition.game.pretense_air[cp_side][cp_name_trimmed][
self.flight.flight_type
].append(name)
try:
self.flight.coalition.game.pretense_air_groups[name] = self.flight
except AttributeError:
self.flight.coalition.game.pretense_air_groups = {}
self.flight.coalition.game.pretense_air_groups[name] = self.flight
def generate_flight_at_departure(self) -> FlyingGroup[Any]:
cp = self.flight.departure
@ -94,7 +99,7 @@ class PretenseFlightGroupSpawner(FlightGroupSpawner):
== self.flight.coalition.game.coalition_for(is_player)
else 1
)
cp_name_trimmed = "".join([i for i in cp.name.lower() if i.isalnum()])
cp_name_trimmed = "".join([i for i in cp.name.lower() if i.isalpha()])
try:
if self.start_type is StartType.IN_FLIGHT:
@ -139,8 +144,7 @@ class PretenseFlightGroupSpawner(FlightGroupSpawner):
pad_group = self._generate_at_cp_ground_spawn(name, cp)
if pad_group is not None:
return pad_group
self.insert_into_pretense(name)
return self._generate_over_departure(name, cp)
raise NoParkingSlotError
elif isinstance(cp, Airfield):
is_heli = self.flight.squadron.aircraft.helicopter
if cp.has_helipads and is_heli:

View File

@ -259,7 +259,7 @@ class PretenseLuaGenerator(LuaGenerator):
def generate_pretense_land_upgrade_supply(self, cp_name: str, cp_side: int) -> str:
lua_string_zones = ""
cp_name_trimmed = "".join([i for i in cp_name.lower() if i.isalnum()])
cp_name_trimmed = "".join([i for i in cp_name.lower() if i.isalpha()])
cp_side_str = "blue" if cp_side == PRETENSE_BLUE_SIDE else "red"
cp = self.game.theater.controlpoints[0]
for loop_cp in self.game.theater.controlpoints:
@ -398,7 +398,7 @@ class PretenseLuaGenerator(LuaGenerator):
)
lua_string_zones += " products = {\n"
for mission_type in self.game.pretense_air[cp_side][cp_name_trimmed]:
if mission_type == FlightType.SEAD:
if mission_type in (FlightType.SEAD, FlightType.DEAD):
mission_name = "attack.sead"
for air_group in self.game.pretense_air[cp_side][cp_name_trimmed][
mission_type
@ -414,6 +414,9 @@ class PretenseLuaGenerator(LuaGenerator):
for air_group in self.game.pretense_air[cp_side][cp_name_trimmed][
mission_type
]:
flight = self.game.pretense_air_groups[air_group]
if flight.is_helo:
mission_name = "attack.helo"
lua_string_zones += (
f" presets.missions.{mission_name}:extend"
+ "({name='"
@ -503,7 +506,7 @@ class PretenseLuaGenerator(LuaGenerator):
def generate_pretense_sea_upgrade_supply(self, cp_name: str, cp_side: int) -> str:
lua_string_zones = ""
cp_name_trimmed = "".join([i for i in cp_name.lower() if i.isalnum()])
cp_name_trimmed = "".join([i for i in cp_name.lower() if i.isalpha()])
cp_side_str = "blue" if cp_side == PRETENSE_BLUE_SIDE else "red"
if cp_side == PRETENSE_BLUE_SIDE:
@ -608,6 +611,9 @@ class PretenseLuaGenerator(LuaGenerator):
for air_group in self.game.pretense_air[cp_side][cp_name_trimmed][
mission_type
]:
flight = self.game.pretense_air_groups[air_group]
if flight.is_helo:
mission_name = "attack.helo"
lua_string_zones += (
f" presets.missions.{mission_name}:extend"
+ "({name='"
@ -697,7 +703,7 @@ class PretenseLuaGenerator(LuaGenerator):
def generate_pretense_zone_land(self, cp_name: str) -> str:
lua_string_zones = ""
cp_name_trimmed = "".join([i for i in cp_name.lower() if i.isalnum()])
cp_name_trimmed = "".join([i for i in cp_name.lower() if i.isalpha()])
lua_string_zones += f"zones.{cp_name_trimmed}:defineUpgrades(" + "{\n"
lua_string_zones += " [1] = { --red side\n"
@ -771,7 +777,7 @@ class PretenseLuaGenerator(LuaGenerator):
def generate_pretense_zone_sea(self, cp_name: str) -> str:
lua_string_zones = ""
cp_name_trimmed = "".join([i for i in cp_name.lower() if i.isalnum()])
cp_name_trimmed = "".join([i for i in cp_name.lower() if i.isalpha()])
lua_string_zones += f"zones.{cp_name_trimmed}:defineUpgrades(" + "{\n"
lua_string_zones += " [1] = { --red side\n"
@ -823,29 +829,29 @@ class PretenseLuaGenerator(LuaGenerator):
def generate_pretense_plugin_data(self) -> None:
self.inject_plugin_script("base", "mist_4_5_107.lua", "mist_4_5_107")
self.inject_plugin_script(
"pretense", "pretense_compiled.lua", "pretense_compiled"
)
trigger = TriggerStart(comment="Pretense init")
lua_string_config = ""
lua_string_config = "Config = Config or {}\n"
lua_string_config += (
f"Config.maxDistFromFront = "
+ str(self.game.settings.pretense_maxdistfromfront_distance * 1000)
+ "\n"
)
lua_string_config += (
f"Config.closeOverride = "
+ str(self.game.settings.pretense_closeoverride_distance * 1000)
+ "\n"
)
if self.game.settings.pretense_do_not_generate_sead_missions:
lua_string_config += "Config.disablePlayerSead = true\n"
else:
lua_string_config += "Config.disablePlayerSead = false\n"
trigger = TriggerStart(comment="Pretense config")
trigger.add_action(DoScript(String(lua_string_config)))
self.mission.triggerrules.triggers.append(trigger)
self.inject_plugin_script(
"pretense", "pretense_compiled.lua", "pretense_compiled"
)
trigger = TriggerStart(comment="Pretense init")
init_header_file = open("./resources/plugins/pretense/init_header.lua", "r")
init_header = init_header_file.read()
@ -855,7 +861,7 @@ class PretenseLuaGenerator(LuaGenerator):
if isinstance(cp, OffMapSpawn):
continue
cp_name_trimmed = "".join([i for i in cp.name.lower() if i.isalnum()])
cp_name_trimmed = "".join([i for i in cp.name.lower() if i.isalpha()])
cp_side = 2 if cp.captured else 1
for side in range(1, 3):
if cp_name_trimmed not in self.game.pretense_air[cp_side]:
@ -947,12 +953,17 @@ class PretenseLuaGenerator(LuaGenerator):
else:
# Finally, connect remaining non-connected points
closest_cps = self.game.theater.closest_friendly_control_points_to(cp)
lua_string_connman += self.generate_pretense_zone_connection(
connected_points, cp.name, closest_cps[0].name
)
lua_string_connman += self.generate_pretense_zone_connection(
connected_points, cp.name, closest_cps[1].name
)
for extra_connection in range(
self.game.settings.pretense_extra_zone_connections
):
if len(closest_cps) > extra_connection:
lua_string_connman += self.generate_pretense_zone_connection(
connected_points,
cp.name,
closest_cps[extra_connection].name,
)
else:
break
lua_string_supply = "local redSupply = {\n"
# Generate supply
@ -963,7 +974,7 @@ class PretenseLuaGenerator(LuaGenerator):
cp_side_captured = cp_side == 2
if cp_side_captured != cp.captured:
continue
cp_name_trimmed = "".join([i for i in cp.name.lower() if i.isalnum()])
cp_name_trimmed = "".join([i for i in cp.name.lower() if i.isalpha()])
for mission_type in self.game.pretense_air[cp_side][cp_name_trimmed]:
if mission_type == FlightType.PRETENSE_CARGO:
for air_group in self.game.pretense_air[cp_side][
@ -976,7 +987,7 @@ class PretenseLuaGenerator(LuaGenerator):
lua_string_supply += "local offmapZones = {\n"
for cp in self.game.theater.controlpoints:
if isinstance(cp, Airfield):
cp_name_trimmed = "".join([i for i in cp.name.lower() if i.isalnum()])
cp_name_trimmed = "".join([i for i in cp.name.lower() if i.isalpha()])
lua_string_supply += f" zones.{cp_name_trimmed},\n"
lua_string_supply += "}\n"
@ -997,8 +1008,7 @@ class PretenseLuaGenerator(LuaGenerator):
init_footer = init_footer_file.read()
lua_string = (
lua_string_config
+ init_header
init_header
+ lua_string_zones
+ lua_string_connman
+ init_body_1

View File

@ -12,6 +12,7 @@ from dcs.countries import (
CombinedJointTaskForcesBlue,
CombinedJointTaskForcesRed,
)
from dcs.task import AFAC, FAC, SetInvisibleCommand, SetImmortalCommand, OrbitAction
from game.lasercodes.lasercoderegistry import LaserCodeRegistry
from game.missiongenerator.convoygenerator import ConvoyGenerator
@ -21,7 +22,7 @@ from game.missiongenerator.forcedoptionsgenerator import ForcedOptionsGenerator
from game.missiongenerator.frontlineconflictdescription import (
FrontLineConflictDescription,
)
from game.missiongenerator.missiondata import MissionData
from game.missiongenerator.missiondata import MissionData, JtacInfo
from game.missiongenerator.tgogenerator import TgoGenerator
from game.missiongenerator.visualsgenerator import VisualsGenerator
from game.naming import namegen
@ -34,6 +35,8 @@ from .pretenseluagenerator import PretenseLuaGenerator
from .pretensetgogenerator import PretenseTgoGenerator
from .pretensetriggergenerator import PretenseTriggerGenerator
from ..ato.airtaaskingorder import AirTaskingOrder
from ..callsigns import callsign_for_support_unit
from ..dcs.aircrafttype import AircraftType
from ..missiongenerator import MissionGenerator
if TYPE_CHECKING:
@ -148,27 +151,61 @@ class PretenseMissionGenerator(MissionGenerator):
for front_line in self.game.theater.conflicts():
player_cp = front_line.blue_cp
enemy_cp = front_line.red_cp
conflict = FrontLineConflictDescription.frontline_cas_conflict(
front_line, self.game.theater
)
# Generate frontline ops
player_gp = self.game.ground_planners[player_cp.id].units_per_cp[
enemy_cp.id
]
enemy_gp = self.game.ground_planners[enemy_cp.id].units_per_cp[player_cp.id]
ground_conflict_gen = FlotGenerator(
self.mission,
conflict,
self.game,
player_gp,
enemy_gp,
player_cp.stances[enemy_cp.id],
enemy_cp.stances[player_cp.id],
self.unit_map,
self.radio_registry,
self.mission_data,
)
ground_conflict_gen.generate()
# Add JTAC
if self.game.blue.faction.has_jtac:
freq = self.radio_registry.alloc_uhf()
# If the option fc3LaserCode is enabled, force all JTAC
# laser codes to 1113 to allow lasing for Su-25 Frogfoots and A-10A Warthogs.
# Otherwise use 1688 for the first JTAC, 1687 for the second etc.
if self.game.settings.plugins.get("ctld.fc3LaserCode"):
code = self.game.laser_code_registry.fc3_code
else:
code = front_line.laser_code
utype = self.game.blue.faction.jtac_unit
if utype is None:
utype = AircraftType.named("MQ-9 Reaper")
country = self.mission.country(self.game.blue.faction.country.name)
position = FrontLineConflictDescription.frontline_position(
front_line, self.game.theater, self.game.settings
)
jtac = self.mission.flight_group(
country=country,
name=namegen.next_jtac_name(),
aircraft_type=utype.dcs_unit_type,
position=position[0],
airport=None,
altitude=5000,
maintask=AFAC,
)
jtac.points[0].tasks.append(
FAC(
callsign=len(self.mission_data.jtacs) + 1,
frequency=int(freq.mhz),
modulation=freq.modulation,
)
)
jtac.points[0].tasks.append(SetInvisibleCommand(True))
jtac.points[0].tasks.append(SetImmortalCommand(True))
jtac.points[0].tasks.append(
OrbitAction(5000, 300, OrbitAction.OrbitPattern.Circle)
)
frontline = f"Frontline {player_cp.name}/{enemy_cp.name}"
# Note: Will need to change if we ever add ground based JTAC.
callsign = callsign_for_support_unit(jtac)
self.mission_data.jtacs.append(
JtacInfo(
group_name=jtac.name,
unit_name=jtac.units[0].name,
callsign=callsign,
region=frontline,
code=str(code),
blue=True,
freq=freq,
)
)
def generate_air_units(self, tgo_generator: TgoGenerator) -> None:
"""Generate the air units for the Operation"""

View File

@ -988,21 +988,102 @@ class Settings:
default=130,
min=10,
max=10000,
detail=(
"Zones farther away than this from the front line are switched "
"into low activity state, but will still be there as functional "
"parts of the economy. Use this to adjust performance."
),
)
pretense_closeoverride_distance: int = bounded_int_option(
"Close override distance (km)",
pretense_extra_zone_connections: int = bounded_int_option(
"Extra friendly zone connections",
page=PRETENSE_PAGE,
section=GENERAL_SECTION,
default=28,
min=5,
max=10000,
default=2,
min=0,
max=10,
detail=(
"Add connections from each zone to this many closest friendly zones,"
"which don't have an existing supply route defined in the campaign."
),
)
pretense_do_not_generate_sead_missions: bool = boolean_option(
"Do not generate player SEAD missions",
page=PRETENSE_PAGE,
section=PERFORMANCE_SECTION,
section=GENERAL_SECTION,
default=False,
)
pretense_num_of_cargo_planes: int = bounded_int_option(
"Number of cargo planes per side",
page=PRETENSE_PAGE,
section=GENERAL_SECTION,
default=2,
min=1,
max=100,
)
pretense_sead_flights_per_cp: int = bounded_int_option(
"Number of AI SEAD flights per control point / zone",
page=PRETENSE_PAGE,
section=GENERAL_SECTION,
default=2,
min=1,
max=10,
)
pretense_cas_flights_per_cp: int = bounded_int_option(
"Number of AI CAS flights per control point / zone",
page=PRETENSE_PAGE,
section=GENERAL_SECTION,
default=2,
min=1,
max=10,
)
pretense_bai_flights_per_cp: int = bounded_int_option(
"Number of AI BAI flights per control point / zone",
page=PRETENSE_PAGE,
section=GENERAL_SECTION,
default=2,
min=1,
max=10,
)
pretense_strike_flights_per_cp: int = bounded_int_option(
"Number of AI Strike flights per control point / zone",
page=PRETENSE_PAGE,
section=GENERAL_SECTION,
default=2,
min=1,
max=10,
)
pretense_barcap_flights_per_cp: int = bounded_int_option(
"Number of AI BARCAP flights per control point / zone",
page=PRETENSE_PAGE,
section=GENERAL_SECTION,
default=2,
min=1,
max=10,
)
pretense_ai_aircraft_per_flight: int = bounded_int_option(
"Number of AI aircraft per flight",
page=PRETENSE_PAGE,
section=GENERAL_SECTION,
default=2,
min=1,
max=4,
)
pretense_player_flights_per_type: int = bounded_int_option(
"Number of player flights per aircraft type at each base",
page=PRETENSE_PAGE,
section=GENERAL_SECTION,
default=2,
min=1,
max=10,
)
pretense_ai_cargo_planes_per_side: int = bounded_int_option(
"Number of AI cargo planes per side",
page=PRETENSE_PAGE,
section=GENERAL_SECTION,
default=2,
min=1,
max=20,
)
# Cheating. Not using auto settings because the same page also has buttons which do
# not alter settings.

View File

@ -494,7 +494,7 @@ presets = {
}),
comCenter = Preset:new({
display = 'Command Center',
cost = 2500,
cost = 12500,
type = 'upgrade',
template = "command-center"
})
@ -522,43 +522,43 @@ presets = {
}),
sa10 = Preset:new({
display = 'SAM',
cost=3000,
cost=30000,
type='defense',
template='sa10',
}),
sa5 = Preset:new({
display = 'SAM',
cost=3000,
cost=20000,
type='defense',
template='sa5',
}),
sa3 = Preset:new({
display = 'SAM',
cost=3000,
cost=4000,
type='defense',
template='sa3',
}),
sa6 = Preset:new({
display = 'SAM',
cost=3000,
cost=6000,
type='defense',
template='sa6',
}),
sa11 = Preset:new({
display = 'SAM',
cost=3000,
cost=10000,
type='defense',
template='sa11',
}),
hawk = Preset:new({
display = 'SAM',
cost=3000,
cost=6000,
type='defense',
template='hawk',
}),
patriot = Preset:new({
display = 'SAM',
cost=3000,
cost=30000,
type='defense',
template='patriot',
}),
@ -596,43 +596,43 @@ presets = {
}),
sa10 = Preset:new({
display = 'SAM',
cost=3000,
cost=30000,
type='defense',
template='sa10',
}),
sa5 = Preset:new({
display = 'SAM',
cost=3000,
cost=20000,
type='defense',
template='sa5',
}),
sa3 = Preset:new({
display = 'SAM',
cost=3000,
cost=4000,
type='defense',
template='sa3',
}),
sa6 = Preset:new({
display = 'SAM',
cost=3000,
cost=6000,
type='defense',
template='sa6',
}),
sa11 = Preset:new({
display = 'SAM',
cost=3000,
cost=10000,
type='defense',
template='sa11',
}),
hawk = Preset:new({
display = 'SAM',
cost=3000,
cost=6000,
type='defense',
template='hawk',
}),
patriot = Preset:new({
display = 'SAM',
cost=3000,
cost=30000,
type='defense',
template='patriot',
}),

View File

@ -32,7 +32,6 @@ Config.buildSpeed = Config.buildSpeed or 10 -- structure and defense build speed
Config.supplyBuildSpeed = Config.supplyBuildSpeed or 85 -- supply helicopters and convoys build speed
Config.missionBuildSpeedReduction = Config.missionBuildSpeedReduction or 0.12 -- reduction of build speed in case of ai missions
Config.maxDistFromFront = Config.maxDistFromFront or 129640 -- max distance in meters from front after which zone is forced into low activity state (export mode)
Config.closeOverride = Config.closeOverride or 27780 -- close override distance in meters from front within which zone is never forced into low activity state
Config.disablePlayerSead = Config.disablePlayerSead or false
Config.missions = Config.missions or {}
@ -505,8 +504,6 @@ end
GroupMonitor = {}
do
GroupMonitor.blockedDespawnTime = 10*60 --used to despawn aircraft that are stuck taxiing for some reason
GroupMonitor.blockedDespawnTimeGround = 30*60 --used to despawn ground units that are stuck en route for some reason
GroupMonitor.blockedDespawnTimeGroundAssault = 90*60 --used to despawn assault units that are stuck en route for some reason
GroupMonitor.landedDespawnTime = 10
GroupMonitor.atDestinationDespawnTime = 2*60
GroupMonitor.recoveryReduction = 0.8 -- reduce recovered resource from landed missions by this amount to account for maintenance
@ -642,13 +639,7 @@ do
group.state = 'enroute'
group.lastStateTime = timer.getAbsTime()
MissionTargetRegistry.addBaiTarget(group)
elseif group.product.missionType == 'assault' and timer.getAbsTime() - group.lastStateTime > GroupMonitor.blockedDespawnTimeGroundAssault then
env.info('GroupMonitor: processSurface ['..group.name..'] despawned due to blockage')
gr:destroy()
local todeliver = math.floor(group.product.cost)
z:addResource(todeliver)
return true
elseif timer.getAbsTime() - group.lastStateTime > GroupMonitor.blockedDespawnTimeGround then
elseif timer.getAbsTime() - group.lastStateTime > GroupMonitor.blockedDespawnTime then
env.info('GroupMonitor: processSurface ['..group.name..'] despawned due to blockage')
gr:destroy()
local todeliver = math.floor(group.product.cost)
@ -744,7 +735,7 @@ do
y = group.target.zone.point.z
}
TaskExtensions.moveOffRoadToPointAndAssault(gr, tp, group.target.built)
TaskExtensions.moveOnRoadToPointAndAssault(gr, tp, group.target.built)
group.isstopped = false
end
end
@ -1231,7 +1222,7 @@ do
if v.type == 'defense' and v.side ~= group:getCoalition() then
local gr = Group.getByName(v.name)
for _,unit in ipairs(gr:getUnits()) do
if unit:hasAttribute('SAM SR') or unit:hasAttribute('SAM TR') or unit:hasAttribute('AAA') or unit:hasAttribute('IR Guided SAM') or unit:hasAttribute('SAM LL') then
if unit:hasAttribute('SAM SR') or unit:hasAttribute('SAM TR') then
table.insert(viable, unit:getName())
end
end
@ -1245,7 +1236,7 @@ do
{
id = 'EngageTargets',
params = {
targetTypes = {'SAM SR', 'SAM TR', 'AAA', 'IR Guided SAM', 'SAM LL'}
targetTypes = {'SAM SR', 'SAM TR'}
}
}
}
@ -2145,68 +2136,7 @@ do
}
group:getController():setTask(mis)
end
function TaskExtensions.moveOffRoadToPointAndAssault(group, point, targets)
if not group or not point then return end
if not group:isExist() or group:getSize()==0 then return end
local startPos = group:getUnit(1):getPoint()
local srx, sry = land.getClosestPointOnRoads('roads', startPos.x, startPos.z)
local erx, ery = land.getClosestPointOnRoads('roads', point.x, point.y)
local mis = {
id='Mission',
params = {
route = {
points = {
[1] = {
type= AI.Task.WaypointType.TURNING_POINT,
x = srx,
y = sry,
speed = 1000,
action = AI.Task.VehicleFormation.DIAMOND
},
[2] = {
type= AI.Task.WaypointType.TURNING_POINT,
x = erx,
y = ery,
speed = 1000,
action = AI.Task.VehicleFormation.DIAMOND
},
[3] = {
type= AI.Task.WaypointType.TURNING_POINT,
x = point.x,
y = point.y,
speed = 1000,
action = AI.Task.VehicleFormation.DIAMOND
}
}
}
}
}
for i,v in pairs(targets) do
if v.type == 'defense' then
local group = Group.getByName(v.name)
if group then
for i,v in ipairs(group:getUnits()) do
local unpos = v:getPoint()
local pnt = {x=unpos.x, y = unpos.z}
table.insert(mis.params.route.points, {
type= AI.Task.WaypointType.TURNING_POINT,
x = pnt.x,
y = pnt.y,
speed = 10,
action = AI.Task.VehicleFormation.DIAMOND
})
end
end
end
end
group:getController():setTask(mis)
end
function TaskExtensions.landAtPointFromAir(group, point, alt)
if not group or not point then return end
if not group:isExist() or group:getSize()==0 then return end
@ -4882,7 +4812,7 @@ do
product.lastMission = {zoneName = zone.name}
timer.scheduleFunction(function(param)
local gr = Group.getByName(param.name)
TaskExtensions.moveOffRoadToPointAndAssault(gr, param.point, param.targets)
TaskExtensions.moveOnRoadToPointAndAssault(gr, param.point, param.targets)
end, {name=product.name, point={ x=tgtPoint.point.x, y = tgtPoint.point.z}, targets=zone.built}, timer.getTime()+1)
end
end
@ -5413,7 +5343,7 @@ do
product.lastMission = {zoneName = v.name}
timer.scheduleFunction(function(param)
local gr = Group.getByName(param.name)
TaskExtensions.moveOffRoadToPointAndAssault(gr, param.point, param.targets)
TaskExtensions.moveOnRoadToPointAndAssault(gr, param.point, param.targets)
end, {name=product.name, point={ x=tgtPoint.point.x, y = tgtPoint.point.z}, targets=v.built}, timer.getTime()+1)
env.info("ZoneCommand - "..product.name.." targeting "..v.name)
@ -5975,7 +5905,7 @@ end
BattlefieldManager = {}
do
BattlefieldManager.closeOverride = Config.closeOverride -- default 15nm
BattlefieldManager.closeOverride = 27780 -- 15nm
BattlefieldManager.farOverride = Config.maxDistFromFront -- default 100nm
BattlefieldManager.boostScale = {[0] = 1.0, [1]=1.0, [2]=1.0}
BattlefieldManager.noRedZones = false
@ -10554,7 +10484,6 @@ do
end
end
end
return false
end
function SEAD:getMissionName()
@ -12208,7 +12137,7 @@ do
if toGen > 0 then
local validMissions = {}
for _,v in pairs(Mission.types) do
if timer.getAbsTime() - timer.getTime0() > 120 and self:canCreateMission(v) then
if self:canCreateMission(v) then
table.insert(validMissions,v)
end
end