mirror of
https://github.com/dcs-retribution/dcs-retribution.git
synced 2025-11-10 15:41:24 +00:00
Overhaul redeployment of units after base-capture(s)
This commit is contained in:
parent
38e62e7636
commit
c22f7cbff3
@ -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)
|
||||||
|
|||||||
@ -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:
|
||||||
|
|||||||
@ -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)
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user