Overhaul redeployment of units after base-capture(s)

This commit is contained in:
Raffson 2024-06-15 23:40:16 +02:00
parent 38e62e7636
commit c22f7cbff3
No known key found for this signature in database
GPG Key ID: B0402B2C9B764D99
3 changed files with 63 additions and 19 deletions

View File

@ -159,11 +159,13 @@ class MissionResultsProcessor:
captured.control_point.capture( captured.control_point.capture(
self.game, events, captured.captured_by_player self.game, events, captured.captured_by_player
) )
logging.info(f"Will run redeploy for {captured.control_point}")
self.redeploy_units(captured.control_point)
except Exception: except Exception:
logging.exception(f"Could not process base capture {captured}") logging.exception(f"Could not process base capture {captured}")
for captured in debriefing.base_captures:
logging.info(f"Will run redeploy for {captured.control_point}")
self.redeploy_units(captured.control_point)
def record_carcasses(self, debriefing: Debriefing) -> None: def record_carcasses(self, debriefing: Debriefing) -> None:
for destroyed_unit in debriefing.state_data.destroyed_statics: for destroyed_unit in debriefing.state_data.destroyed_statics:
self.game.add_destroyed_units(destroyed_unit) self.game.add_destroyed_units(destroyed_unit)
@ -301,10 +303,6 @@ class MissionResultsProcessor:
""" " """ "
Auto redeploy units to newly captured base Auto redeploy units to newly captured base
""" """
ally_connected_cps = [
ocp for ocp in cp.connected_points if cp.captured == ocp.captured
]
enemy_connected_cps = [ enemy_connected_cps = [
ocp for ocp in cp.connected_points if cp.captured != ocp.captured ocp for ocp in cp.connected_points if cp.captured != ocp.captured
] ]
@ -314,28 +312,54 @@ class MissionResultsProcessor:
if len(enemy_connected_cps) == 0: if len(enemy_connected_cps) == 0:
return return
ally_connected_cps = [
ocp
for ocp in cp.transitive_connected_friendly_destinations()
if cp.captured == ocp.captured and ocp.base.total_armor
]
settings = cp.coalition.game.settings
factor = (
settings.frontline_reserves_factor
if cp.captured
else settings.frontline_reserves_factor_red
)
# From each ally cp, send reinforcements # From each ally cp, send reinforcements
for ally_cp in ally_connected_cps: for ally_cp in sorted(
ally_connected_cps,
key=lambda x: len(
[cp for cp in x.connected_points if x.captured != cp.captured]
),
):
self.redeploy_between(cp, ally_cp) self.redeploy_between(cp, ally_cp)
if cp.base.total_armor > factor * cp.deployable_front_line_units:
break
def redeploy_between(self, destination: ControlPoint, source: ControlPoint) -> None: def redeploy_between(self, destination: ControlPoint, source: ControlPoint) -> None:
total_units_redeployed = 0 total_units_redeployed = 0
moved_units = {} moved_units = {}
if source.has_active_frontline or not destination.captured: settings = source.coalition.game.settings
# If there are still active front lines to defend at the reserves = max(
# transferring CP we should not transfer all units. 1,
# settings.reserves_procurement_target
# Opfor also does not transfer all of their units. if source.captured
# TODO: Balance the CPs rather than moving half from everywhere. else settings.reserves_procurement_target_red,
move_factor = 0.5 )
else: total_units = source.base.total_armor
# Otherwise we can move everything. reserves_factor = (reserves - 1) / total_units # slight underestimation
move_factor = 1
source_frontline_count = len(
[cp for cp in source.connected_points if not source.is_friendly_to(cp)]
)
move_factor = max(0.0, 1 / (source_frontline_count + 1) - reserves_factor)
for frontline_unit, count in source.base.armor.items(): for frontline_unit, count in source.base.armor.items():
moved_units[frontline_unit] = int(count * move_factor) moved_count = int(count * move_factor)
total_units_redeployed = total_units_redeployed + int(count * move_factor) moved_units[frontline_unit] = moved_count
total_units_redeployed += moved_count
destination.base.commission_units(moved_units) destination.base.commission_units(moved_units)
source.base.commit_losses(moved_units) source.base.commit_losses(moved_units)

View File

@ -572,6 +572,23 @@ class ControlPoint(MissionTarget, SidcDescribable, ABC):
connected.extend(cp.transitive_friendly_shipping_destinations(seen)) connected.extend(cp.transitive_friendly_shipping_destinations(seen))
return connected return connected
def transitive_connected_friendly_destinations(
self, seen: Optional[Set[ControlPoint]] = None
) -> List[ControlPoint]:
if seen is None:
seen = {self}
connected = []
for cp in set(self.connected_points + list(self.shipping_lanes.keys())):
if cp.captured != self.captured:
continue
if cp in seen:
continue
seen.add(cp)
connected.append(cp)
connected.extend(cp.transitive_connected_friendly_destinations(seen))
return connected
@property @property
def has_factory(self) -> bool: def has_factory(self) -> bool:
for tgo in self.connected_objectives: for tgo in self.connected_objectives:

View File

@ -19,6 +19,7 @@ from game.radio.RadioFrequencyContainer import RadioFrequencyContainer
from game.radio.TacanContainer import TacanContainer from game.radio.TacanContainer import TacanContainer
from game.server import EventStream from game.server import EventStream
from game.sim import GameUpdateEvents from game.sim import GameUpdateEvents
from game.sim.missionresultsprocessor import MissionResultsProcessor
from game.theater import ( from game.theater import (
AMMO_DEPOT_FRONTLINE_UNIT_CONTRIBUTION, AMMO_DEPOT_FRONTLINE_UNIT_CONTRIBUTION,
ControlPoint, ControlPoint,
@ -181,6 +182,8 @@ class QBaseMenu2(QDialog):
def cheat_capture(self) -> None: def cheat_capture(self) -> None:
events = GameUpdateEvents() events = GameUpdateEvents()
self.cp.capture(self.game_model.game, events, for_player=not self.cp.captured) self.cp.capture(self.game_model.game, events, for_player=not self.cp.captured)
mrp = MissionResultsProcessor(self.game_model.game)
mrp.redeploy_units(self.cp)
# Reinitialized ground planners and the like. The ATO needs to be reset because # Reinitialized ground planners and the like. The ATO needs to be reset because
# missions planned against the flipped base are no longer valid. # missions planned against the flipped base are no longer valid.
self.game_model.game.initialize_turn(events) self.game_model.game.initialize_turn(events)