add support for neutral FOBs

This commit is contained in:
Eclipse/Druss99 2025-09-23 19:05:38 -04:00 committed by Raffson
parent 9f10ecc884
commit c0748e2a3e
7 changed files with 67 additions and 12 deletions

View File

@ -57,6 +57,7 @@ class MizCampaignLoader:
FOB_UNIT_TYPE = Unarmed.SKP_11.id FOB_UNIT_TYPE = Unarmed.SKP_11.id
FARP_HELIPADS_TYPE = ["Invisible FARP", "SINGLE_HELIPAD", "FARP"] FARP_HELIPADS_TYPE = ["Invisible FARP", "SINGLE_HELIPAD", "FARP"]
INVISIBLE_FOB_UNIT_TYPE = Unarmed.M_818.id INVISIBLE_FOB_UNIT_TYPE = Unarmed.M_818.id
NEUTRAL_FOB_UNIT_TYPE = Unarmed.KrAZ6322.id
OFFSHORE_STRIKE_TARGET_UNIT_TYPE = Fortification.Oil_platform.id OFFSHORE_STRIKE_TARGET_UNIT_TYPE = Fortification.Oil_platform.id
SHIP_UNIT_TYPE = USS_Arleigh_Burke_IIa.id SHIP_UNIT_TYPE = USS_Arleigh_Burke_IIa.id
@ -180,6 +181,12 @@ class MizCampaignLoader:
if group.units[0].type == self.INVISIBLE_FOB_UNIT_TYPE: if group.units[0].type == self.INVISIBLE_FOB_UNIT_TYPE:
yield group yield group
@property
def neutral_fobs(self) -> Iterator[VehicleGroup]:
for group in self.red.vehicle_group:
if group.units[0].type == self.NEUTRAL_FOB_UNIT_TYPE:
yield group
@property @property
def ships(self) -> Iterator[ShipGroup]: def ships(self) -> Iterator[ShipGroup]:
for group in self.red.ship_group: for group in self.red.ship_group:
@ -345,6 +352,18 @@ class MizCampaignLoader:
control_point.captured_invert = fob.late_activation control_point.captured_invert = fob.late_activation
control_points[control_point.id] = control_point control_points[control_point.id] = control_point
for fob in self.neutral_fobs:
ctld_zones = self.get_ctld_zones(fob.name)
control_point = Fob(
str(fob.name),
fob.position,
self.theater,
starts_blue=Player.NEUTRAL,
ctld_zones=ctld_zones,
)
control_point.captured_invert = fob.late_activation
control_points[control_point.id] = control_point
if self.cp_influence_zones: if self.cp_influence_zones:
for cp in control_points.values(): for cp in control_points.values():
for influence_radius in self.cp_influence_zones: for influence_radius in self.cp_influence_zones:

View File

@ -1,5 +1,6 @@
from __future__ import annotations from __future__ import annotations
import itertools
import math import math
import operator import operator
from collections.abc import Iterable, Iterator from collections.abc import Iterable, Iterator
@ -278,6 +279,29 @@ class ObjectiveFinder:
prioritized.extend(self._targets_by_range(isolated)) prioritized.extend(self._targets_by_range(isolated))
return prioritized return prioritized
def air_assault_targets(self) -> list[ControlPoint]:
"""Returns control points suitable for air assault missions, including neutral bases."""
prioritized = []
capturable_later = []
isolated = []
combined_control_points = itertools.chain(
self.game.theater.control_points_for(self.is_player.opponent),
self.game.theater.control_points_for(Player.NEUTRAL),
)
for cp in combined_control_points:
if cp.is_isolated:
isolated.append(cp)
continue
if cp.has_active_frontline:
prioritized.append(cp)
else:
capturable_later.append(cp)
prioritized.extend(self._targets_by_range(capturable_later))
prioritized.extend(self._targets_by_range(isolated))
return prioritized
@staticmethod @staticmethod
def closest_airfields_to(location: MissionTarget) -> ClosestAirfields: def closest_airfields_to(location: MissionTarget) -> ClosestAirfields:
"""Returns the closest airfields to the given location.""" """Returns the closest airfields to the given location."""

View File

@ -158,6 +158,7 @@ class TheaterState(WorldState["TheaterState"]):
coalition = game.coalition_for(player) coalition = game.coalition_for(player)
finder = ObjectiveFinder(game, player) finder = ObjectiveFinder(game, player)
ordered_capturable_points = finder.prioritized_points() ordered_capturable_points = finder.prioritized_points()
air_assault_capturable_points = finder.air_assault_targets()
context = PersistentContext( context = PersistentContext(
game.db, game.db,
@ -177,7 +178,7 @@ class TheaterState(WorldState["TheaterState"]):
battle_postitions: Dict[ControlPoint, BattlePositions] = { battle_postitions: Dict[ControlPoint, BattlePositions] = {
cp: BattlePositions.for_control_point(cp) cp: BattlePositions.for_control_point(cp)
for cp in ordered_capturable_points for cp in air_assault_capturable_points
} }
vulnerable_control_points = [ vulnerable_control_points = [

View File

@ -225,9 +225,9 @@ class Game:
def neutral_country(self) -> Country: def neutral_country(self) -> Country:
"""Return the best fitting country that can be used as neutral faction in the generated mission""" """Return the best fitting country that can be used as neutral faction in the generated mission"""
countries_in_use = {self.red.faction.country, self.blue.faction.country} countries_in_use = {self.red.faction.country, self.blue.faction.country}
if UnitedNationsPeacekeepers not in countries_in_use: if UnitedNationsPeacekeepers() not in countries_in_use:
return UnitedNationsPeacekeepers() return UnitedNationsPeacekeepers()
elif Switzerland.name not in countries_in_use: elif Switzerland() not in countries_in_use:
return Switzerland() return Switzerland()
else: else:
return USAFAggressors() return USAFAggressors()

View File

@ -101,13 +101,15 @@ class LuaGenerator:
for logistic_info in self.mission_data.logistics: for logistic_info in self.mission_data.logistics:
if logistic_info.transport not in transports: if logistic_info.transport not in transports:
transports.append(logistic_info.transport) transports.append(logistic_info.transport)
coalition_color = "blue" if logistic_info.blue else "red" coalition_color = "blue" if logistic_info.blue.is_blue else "red"
logistics_item = logistics_flights.add_item() logistics_item = logistics_flights.add_item()
logistics_item.add_data_array("pilot_names", logistic_info.pilot_names) logistics_item.add_data_array("pilot_names", logistic_info.pilot_names)
logistics_item.add_key_value("pickup_zone", logistic_info.pickup_zone) logistics_item.add_key_value("pickup_zone", logistic_info.pickup_zone)
logistics_item.add_key_value("drop_off_zone", logistic_info.drop_off_zone) logistics_item.add_key_value("drop_off_zone", logistic_info.drop_off_zone)
logistics_item.add_key_value("target_zone", logistic_info.target_zone) logistics_item.add_key_value("target_zone", logistic_info.target_zone)
logistics_item.add_key_value("side", str(2 if logistic_info.blue else 1)) logistics_item.add_key_value(
"side", str(2 if logistic_info.blue.is_blue else 1)
)
logistics_item.add_key_value("logistic_unit", logistic_info.logistic_unit) logistics_item.add_key_value("logistic_unit", logistic_info.logistic_unit)
logistics_item.add_key_value( logistics_item.add_key_value(
"aircraft_type", logistic_info.transport.dcs_id "aircraft_type", logistic_info.transport.dcs_id
@ -201,7 +203,9 @@ class LuaGenerator:
# Should probably do the same with all the roles... but the script is already # Should probably do the same with all the roles... but the script is already
# tolerant of those being empty. # tolerant of those being empty.
for node in self.game.theater.iads_network.skynet_nodes(self.game): for node in self.game.theater.iads_network.skynet_nodes(self.game):
coalition = iads_object.get_or_create_item("BLUE" if node.player else "RED") coalition = iads_object.get_or_create_item(
"BLUE" if node.player.is_blue else "RED"
)
iads_type = coalition.get_or_create_item(node.iads_role.value) iads_type = coalition.get_or_create_item(node.iads_role.value)
iads_element = iads_type.add_item() iads_element = iads_type.add_item()
iads_element.add_key_value("dcsGroupName", node.dcs_name) iads_element.add_key_value("dcsGroupName", node.dcs_name)

View File

@ -810,6 +810,9 @@ class HelipadGenerator:
# capture triggers # capture triggers
pad: BaseFARP pad: BaseFARP
neutral_country = self.m.country(self.game.neutral_country.name) neutral_country = self.m.country(self.game.neutral_country.name)
if self.cp.captured is Player.NEUTRAL:
country = neutral_country
else:
country = self.m.country( country = self.m.country(
self.game.coalition_for(self.cp.captured).faction.country.name self.game.coalition_for(self.cp.captured).faction.country.name
) )
@ -1403,6 +1406,10 @@ class TgoGenerator:
def generate(self) -> None: def generate(self) -> None:
for cp in self.game.theater.controlpoints: for cp in self.game.theater.controlpoints:
# Use neutral country for neutral control points
if cp.captured is Player.NEUTRAL:
country = self.m.country(self.game.neutral_country.name)
else:
country = self.m.country(cp.coalition.faction.country.name) country = self.m.country(cp.coalition.faction.country.name)
# Generate helipads # Generate helipads

View File

@ -305,12 +305,12 @@ class QGroundObjectMenu(QDialog):
self.game.theater.iads_network.update_tgo(self.ground_object, events) self.game.theater.iads_network.update_tgo(self.ground_object, events)
if any( if any(
package.target == self.ground_object package.target == self.ground_object
for package in self.game.ato_for(player=False).packages for package in self.game.ato_for(player=Player.RED).packages
): ):
# Replan if the tgo was a target of the redfor # Replan if the tgo was a target of the redfor
coalition = self.ground_object.coalition coalition = self.ground_object.coalition
self.game.initialize_turn( self.game.initialize_turn(
events, for_red=coalition.player, for_blue=not coalition.player events, for_red=coalition.player, for_blue=coalition.player.opponent
) )
EventStream.put_nowait(events) EventStream.put_nowait(events)
GameUpdateSignal.get_instance().updateGame(self.game) GameUpdateSignal.get_instance().updateGame(self.game)