Add factory placement to the campaign files.

This also removes the "factory" type from the normal strike target
(money generating) generators to avoid confusion. Later only control
points with factories will be able to spawn ground units, at which point
these will no longer generate income.

https://github.com/Khopa/dcs_liberation/issues/986
This commit is contained in:
Dan Albert 2021-04-18 20:24:36 -07:00
parent 707323ca12
commit eff5b94db7
11 changed files with 104 additions and 9 deletions

View File

@ -7,6 +7,7 @@ Saves from 2.5 are not compatible with 2.6.
* **[Campaign]** Ground units can now be transferred by road. See https://github.com/Khopa/dcs_liberation/wiki/Unit-Transfers for more information.
* **[Campaign]** Ground units can no longer be sold. To move units to a new location, transfer them.
* **[UI]** Campaigns generated for an older or newer version of the game will now be marked as incompatible. They can still be played, but bugs may be present.
* **[Modding]** Campaigns now choose locations for factories to spawn.
## Fixes

View File

@ -10,14 +10,12 @@ DEFAULT_AVAILABLE_BUILDINGS = [
"farp",
"fob",
"power",
"factory",
"derrick",
]
WW2_FREE = ["fuel", "factory", "ware", "fob"]
WW2_FREE = ["fuel", "ware", "fob"]
WW2_GERMANY_BUILDINGS = [
"fuel",
"factory",
"ww2bunker",
"ww2bunker",
"ww2bunker",
@ -27,7 +25,6 @@ WW2_GERMANY_BUILDINGS = [
]
WW2_ALLIES_BUILDINGS = [
"fuel",
"factory",
"allycamp",
"allycamp",
"allycamp",

View File

@ -1350,6 +1350,7 @@ REWARDS = {
"ammo": 2,
"farp": 1,
"fob": 1,
# TODO: Should generate no cash once they generate units.
"factory": 10,
"comms": 10,
"oil": 10,

View File

@ -120,6 +120,8 @@ class MizCampaignLoader:
REQUIRED_EWR_UNIT_TYPE = AirDefence.EWR_1L13.id
FACTORY_UNIT_TYPE = Fortification.Workshop_A.id
BASE_DEFENSE_RADIUS = nautical_miles(2)
def __init__(self, miz: Path, theater: ConflictTheater) -> None:
@ -261,6 +263,12 @@ class MizCampaignLoader:
if group.units[0].type == self.FARP_HELIPAD:
yield group
@property
def factories(self) -> Iterator[StaticGroup]:
for group in self.blue.static_group:
if group.units[0].type in self.FACTORY_UNIT_TYPE:
yield group
@cached_property
def control_points(self) -> Dict[int, ControlPoint]:
control_points = {}
@ -421,6 +429,12 @@ class MizCampaignLoader:
PointWithHeading.from_point(group.position, group.units[0].heading)
)
for group in self.factories:
closest, distance = self.objective_info(group)
closest.preset_locations.factories.append(
PointWithHeading.from_point(group.position, group.units[0].heading)
)
def populate_theater(self) -> None:
for control_point in self.control_points.values():
self.theater.add_controlpoint(control_point)

View File

@ -110,6 +110,9 @@ class PresetLocations:
#: Locations of EWRs which should always be spawned.
required_ewrs: List[PointWithHeading] = field(default_factory=list)
#: Locations of factories for producing ground units. These will always be spawned.
factories: List[PointWithHeading] = field(default_factory=list)
@staticmethod
def _random_from(points: List[PointWithHeading]) -> Optional[PointWithHeading]:
"""Finds, removes, and returns a random position from the given list."""

View File

@ -18,6 +18,7 @@ from game.theater.theatergroundobject import (
BuildingGroundObject,
CarrierGroundObject,
EwrGroundObject,
FactoryGroundObject,
LhaGroundObject,
MissileSiteGroundObject,
SamGroundObject,
@ -612,6 +613,7 @@ class AirbaseGroundObjectGenerator(ControlPointGroundObjectGenerator):
"""Generate ground objects and AA sites for the control point."""
skip_sams = self.generate_required_aa()
skip_ewrs = self.generate_required_ewr()
self.generate_factories()
if self.control_point.is_global:
return
@ -717,6 +719,25 @@ class AirbaseGroundObjectGenerator(ControlPointGroundObjectGenerator):
self.control_point.connected_objectives.append(g)
def generate_factories(self) -> None:
"""Generates the factories that are required by the campaign."""
for position in self.control_point.preset_locations.factories:
self.generate_factory_at(position)
def generate_factory_at(self, point: PointWithHeading) -> None:
obj_name = namegen.random_objective_name()
group_id = self.game.next_group_id()
g = FactoryGroundObject(
obj_name,
group_id,
point,
point.heading,
self.control_point,
)
self.control_point.connected_objectives.append(g)
def generate_aa_site(self) -> None:
position = self.location_finder.location_for(LocationType.Sam)
if position is None:

View File

@ -266,6 +266,28 @@ class BuildingGroundObject(TheaterGroundObject):
self._dead = True
class FactoryGroundObject(BuildingGroundObject):
def __init__(
self,
name: str,
group_id: int,
position: Point,
heading: int,
control_point: ControlPoint,
) -> None:
super().__init__(
name=name,
category="factory",
group_id=group_id,
object_id=0,
position=position,
heading=heading,
control_point=control_point,
dcs_identifier="Workshop A",
airbase_group=False,
)
class NavalGroundObject(TheaterGroundObject):
def mission_types(self, for_player: bool) -> Iterator[FlightType]:
from gen.flights.flight import FlightType

View File

@ -24,4 +24,19 @@ VERSION = _build_version_string()
#:
#: There is no verification that the campaign author updated their campaign correctly
#: this is just a UI hint.
#:
#: Version history:
#:
#: Version 0
#: * Unknown compatibility.
#:
#: Version 1
#: * Compatible with Liberation 2.5.
#:
#: Version 2
#: * Front line endpoints now define convoy origin/destination waypoints. They should be
#: placed on or near roads.
#: * Factories (Warehouse_A) define factory objectives. Only control points with
#: factories will be able to recruit ground units, so they should exist in sufficient
#: number and be protected by IADS.
CAMPAIGN_FORMAT_VERSION = 1

View File

@ -14,7 +14,7 @@ from typing import Dict, Iterator, Optional, TYPE_CHECKING, Type, List
from dcs import Mission, Point, unitgroup
from dcs.country import Country
from dcs.point import StaticPoint
from dcs.statics import fortification_map, warehouse_map, Warehouse
from dcs.statics import Fortification, fortification_map, warehouse_map, Warehouse
from dcs.task import (
ActivateBeaconCommand,
ActivateICLSCommand,
@ -34,6 +34,7 @@ from game.theater import ControlPoint, TheaterGroundObject
from game.theater.theatergroundobject import (
BuildingGroundObject,
CarrierGroundObject,
FactoryGroundObject,
GenericCarrierGroundObject,
LhaGroundObject,
ShipGroundObject,
@ -213,7 +214,7 @@ class BuildingSiteGenerator(GenericGroundObjectGenerator):
f"{self.ground_object.dcs_identifier} not found in static maps"
)
def generate_vehicle_group(self, unit_type: UnitType) -> None:
def generate_vehicle_group(self, unit_type: Type[UnitType]) -> None:
if not self.ground_object.is_dead:
group = self.m.vehicle_group(
country=self.country,
@ -224,7 +225,7 @@ class BuildingSiteGenerator(GenericGroundObjectGenerator):
)
self._register_fortification(group)
def generate_static(self, static_type: StaticType) -> None:
def generate_static(self, static_type: Type[StaticType]) -> None:
group = self.m.static_group(
country=self.country,
name=self.ground_object.group_name,
@ -244,6 +245,22 @@ class BuildingSiteGenerator(GenericGroundObjectGenerator):
self.unit_map.add_building(self.ground_object, building)
class FactoryGenerator(BuildingSiteGenerator):
"""Generator for factory sites.
Factory sites are the buildings that allow the recruitment of ground units.
Destroying these prevents the owner from recruiting ground units at the connected
control point.
"""
def generate(self) -> None:
if self.game.position_culled(self.ground_object.position):
return
# TODO: Faction specific?
self.generate_static(Fortification.Workshop_A)
class GenericCarrierGenerator(GenericGroundObjectGenerator):
"""Base type for carrier group generation.
@ -557,7 +574,11 @@ class GroundObjectsGenerator:
).generate()
for ground_object in cp.ground_objects:
if isinstance(ground_object, BuildingGroundObject):
if isinstance(ground_object, FactoryGroundObject):
generator = FactoryGenerator(
ground_object, country, self.game, self.m, self.unit_map
)
elif isinstance(ground_object, BuildingGroundObject):
generator = BuildingSiteGenerator(
ground_object, country, self.game, self.m, self.unit_map
)

View File

@ -5,7 +5,7 @@
"recommended_player_faction": "USA 2005",
"recommended_enemy_faction": "Insurgents (Hard)",
"description": "<p>In this scenario, you start from Jordan, and have to fight your way through eastern Syria.</p>",
"version": 1,
"version": 2,
"miz": "inherent_resolve.miz",
"performance": 1
}