Retreat ground forces from captured bases.

If the captured base has no connection to other friendly objectives (the
base was encircled) then the enemy equipment will be captured and sold.
Otherwise the units will retreat toward connected bases.

Partial fix for https://github.com/Khopa/dcs_liberation/issues/693.
Still need to fix this for air units.
This commit is contained in:
Dan Albert 2021-01-01 14:41:02 -08:00
parent 6cbc2b707a
commit fcb1d8e104
3 changed files with 65 additions and 2 deletions

View File

@ -14,6 +14,7 @@ Saves from 2.3 are not compatible with 2.4.
* **[Mission Generator]** Multiple groups are created for complex SAM sites (SAMs with additional point defense or SHORADS), improving Skynet behavior.
* **[Skynet]** Point defenses are now configured to remain on to protect the site they accompany.
* **[Balance]** Opfor now gains income using the same rules as the player, significantly increasing their income relative to the player for most campaigns.
* **[Balance]** Ground units now retreat from captured bases when they are connected to a friendly base. Units with no retreat path will be captured and sold.
* **[Economy]** FOBs generate only $10M per turn (previously $20M like airbases).
* **[Economy]** Carriers and off-map spawns generate no income (previously $20M like airbases).
* **[UI]** Multi-SAM objectives now show threat and detection rings per group.

View File

@ -9,6 +9,7 @@ from dcs.unittype import FlyingType, UnitType, VehicleType
from dcs.vehicles import AirDefence, Armor
from game import db
from game.db import PRICES
from gen.ground_forces.ai_ground_planner_db import TYPE_SHORAD
STRENGTH_AA_ASSEMBLE_MIN = 0.2
@ -37,6 +38,16 @@ class Base:
def total_armor(self) -> int:
return sum(self.armor.values())
@property
def total_armor_value(self) -> int:
total = 0
for unit_type, count in self.armor.items():
try:
total += PRICES[unit_type] * count
except KeyError:
logging.exception(f"No price found for {unit_type.id}")
return total
@property
def total_frontline_aa(self) -> int:
return sum([v for k, v in self.armor.items() if k in TYPE_SHORAD])

View File

@ -1,5 +1,6 @@
from __future__ import annotations
import heapq
import itertools
import logging
import random
@ -7,7 +8,8 @@ import re
from abc import ABC, abstractmethod
from dataclasses import dataclass, field
from enum import Enum
from typing import Dict, Iterator, List, Optional, TYPE_CHECKING, Type
from functools import total_ordering
from typing import Any, Dict, Iterator, List, Optional, TYPE_CHECKING, Type
from dcs.mapping import Point
from dcs.ships import (
@ -192,6 +194,28 @@ class RunwayStatus:
return f"Runway repairing, {turns_remaining} turns remaining"
@total_ordering
class GroundUnitDestination:
def __init__(self, control_point: ControlPoint) -> None:
self.control_point = control_point
@property
def total_value(self) -> float:
return self.control_point.base.total_armor_value
def __eq__(self, other: Any) -> bool:
if not isinstance(other, GroundUnitDestination):
raise TypeError
return self.total_value == other.total_value
def __lt__(self, other: Any) -> bool:
if not isinstance(other, GroundUnitDestination):
raise TypeError
return self.total_value < other.total_value
class ControlPoint(MissionTarget, ABC):
position = None # type: Point
@ -365,9 +389,37 @@ class ControlPoint(MissionTarget, ABC):
base_defense.position)
self.base_defenses = []
def capture_equipment(self, game: Game) -> None:
total = self.base.total_armor_value
self.base.armor.clear()
game.adjust_budget(total, player=not self.captured)
game.message(
f"{self.name} is not connected to any friendly points. Ground "
f"vehicles have been captured and sold for ${total}M.")
def retreat_ground_units(self, game: Game):
# When there are multiple valid destinations, deliver units to whichever
# base is least defended first. The closest approximation of unit
# strength we have is price
destinations = [GroundUnitDestination(cp)
for cp in self.connected_points
if cp.captured == self.captured]
if not destinations:
self.capture_equipment(game)
return
heapq.heapify(destinations)
destination = heapq.heappop(destinations)
while self.base.armor:
unit_type, count = self.base.armor.popitem()
for _ in range(count):
destination.control_point.base.commision_units({unit_type: 1})
destination = heapq.heappushpop(destinations, destination)
# TODO: Should be Airbase specific.
def capture(self, game: Game, for_player: bool) -> None:
self.pending_unit_deliveries.refund_all(game)
self.retreat_ground_units(game)
if for_player:
self.captured = True
@ -377,7 +429,6 @@ class ControlPoint(MissionTarget, ABC):
self.base.set_strength_to_minimum()
self.base.aircraft = {}
self.base.armor = {}
self.clear_base_defenses()
from .start_generator import BaseDefenseGenerator