GroundForcePainter for applying custom liveries to ground units and ships (#26)

See #26 for more info
This commit is contained in:
MetalStormGhost 2022-12-03 17:11:22 +02:00 committed by GitHub
parent 0c30ce3bb3
commit f5c7935993
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 123 additions and 3 deletions

View File

@ -4,6 +4,7 @@
* **[Engine]** Support for DCS v2.8.0.32066. * **[Engine]** Support for DCS v2.8.0.32066.
* **[Briefing]** Add tanker info to mission briefing * **[Briefing]** Add tanker info to mission briefing
* **[Campaign]** Add 3 new campaigns by Oscar Juliet from WRL * **[Campaign]** Add 3 new campaigns by Oscar Juliet from WRL
* **[Campaign]** Add ability to define livery overrides also for ground/naval units
* **[Data]** Added data to support C-47 Skytrain. * **[Data]** Added data to support C-47 Skytrain.
* **[Data]** Added data to support KS-19 & SON-9, including support for "AAA Site" layout. * **[Data]** Added data to support KS-19 & SON-9, including support for "AAA Site" layout.
* **[Mission Generation]** Add option to configure the maximum front-line length in settings * **[Mission Generation]** Add option to configure the maximum front-line length in settings

View File

@ -116,6 +116,9 @@ class Faction:
# List of default livery overrides # List of default livery overrides
liveries_overrides: Dict[AircraftType, List[str]] = field(default_factory=dict) liveries_overrides: Dict[AircraftType, List[str]] = field(default_factory=dict)
# List of default livery overrides for ground vehicles
liveries_overrides_ground_forces: Dict[str, List[str]] = field(default_factory=dict)
#: Set to True if the faction should force the "Unrestricted satnav" option #: Set to True if the faction should force the "Unrestricted satnav" option
#: for the mission. This option enables GPS for capable aircraft regardless #: for the mission. This option enables GPS for capable aircraft regardless
#: of the time period or operator. For example, the CJTF "countries" don't #: of the time period or operator. For example, the CJTF "countries" don't
@ -284,6 +287,16 @@ class Faction:
aircraft = AircraftType.named(name) aircraft = AircraftType.named(name)
faction.liveries_overrides[aircraft] = [s.lower() for s in livery] faction.liveries_overrides[aircraft] = [s.lower() for s in livery]
# Load liveries override for ground forces
faction.liveries_overrides_ground_forces = {}
liveries_overrides_ground_forces = json.get(
"liveries_overrides_ground_forces", {}
)
for vehicle_type, livery in liveries_overrides_ground_forces.items():
faction.liveries_overrides_ground_forces[vehicle_type] = [
s.lower() for s in livery
]
faction.unrestricted_satnav = json.get("unrestricted_satnav", False) faction.unrestricted_satnav = json.get("unrestricted_satnav", False)
return faction return faction

View File

@ -10,6 +10,7 @@ from dcs.unit import Vehicle
from dcs.unitgroup import VehicleGroup from dcs.unitgroup import VehicleGroup
from game.dcs.groundunittype import GroundUnitType from game.dcs.groundunittype import GroundUnitType
from game.missiongenerator.groundforcepainter import GroundForcePainter
from game.transfers import Convoy from game.transfers import Convoy
from game.unitmap import UnitMap from game.unitmap import UnitMap
from game.utils import kph from game.utils import kph
@ -72,6 +73,7 @@ class ConvoyGenerator:
for_player: bool, for_player: bool,
) -> VehicleGroup: ) -> VehicleGroup:
country = self.mission.country(self.game.coalition_for(for_player).country_name) country = self.mission.country(self.game.coalition_for(for_player).country_name)
faction = self.game.faction_for(for_player)
unit_types = list(units.items()) unit_types = list(units.items())
main_unit_type, main_unit_count = unit_types[0] main_unit_type, main_unit_count = unit_types[0]
@ -97,6 +99,7 @@ class ConvoyGenerator:
v.position.x = position.x v.position.x = position.x
v.position.y = next(y) v.position.y = next(y)
v.heading = 0 v.heading = 0
GroundForcePainter(faction, v).apply_livery()
group.add_unit(v) group.add_unit(v)
return group return group

View File

@ -1,5 +1,6 @@
from __future__ import annotations from __future__ import annotations
import logging
import math import math
import random import random
from typing import List, Optional, TYPE_CHECKING, Tuple from typing import List, Optional, TYPE_CHECKING, Tuple
@ -43,6 +44,7 @@ from game.theater.controlpoint import ControlPoint
from game.unitmap import UnitMap from game.unitmap import UnitMap
from game.utils import Heading from game.utils import Heading
from .frontlineconflictdescription import FrontLineConflictDescription from .frontlineconflictdescription import FrontLineConflictDescription
from .groundforcepainter import GroundForcePainter
from .lasercoderegistry import LaserCodeRegistry from .lasercoderegistry import LaserCodeRegistry
from .missiondata import JtacInfo, MissionData from .missiondata import JtacInfo, MissionData
@ -221,7 +223,7 @@ class FlotGenerator:
u = random.choices( u = random.choices(
manpads, weights=[m.spawn_weight for m in manpads] manpads, weights=[m.spawn_weight for m in manpads]
)[0] )[0]
self.mission.vehicle_group( vg = self.mission.vehicle_group(
side, side,
namegen.next_infantry_name(side, u), namegen.next_infantry_name(side, u),
u.dcs_unit_type, u.dcs_unit_type,
@ -230,6 +232,8 @@ class FlotGenerator:
heading=forward_heading.degrees, heading=forward_heading.degrees,
move_formation=PointAction.OffRoad, move_formation=PointAction.OffRoad,
) )
vehicle = vg.units[0]
GroundForcePainter(faction, vehicle).apply_livery()
return return
possible_infantry_units = set(faction.infantry_with_class(UnitClass.INFANTRY)) possible_infantry_units = set(faction.infantry_with_class(UnitClass.INFANTRY))
@ -246,7 +250,7 @@ class FlotGenerator:
weights=[u.spawn_weight for u in infantry_choices], weights=[u.spawn_weight for u in infantry_choices],
k=INFANTRY_GROUP_SIZE, k=INFANTRY_GROUP_SIZE,
) )
self.mission.vehicle_group( vg = self.mission.vehicle_group(
side, side,
namegen.next_infantry_name(side, units[0]), namegen.next_infantry_name(side, units[0]),
units[0].dcs_unit_type, units[0].dcs_unit_type,
@ -255,10 +259,12 @@ class FlotGenerator:
heading=forward_heading.degrees, heading=forward_heading.degrees,
move_formation=PointAction.OffRoad, move_formation=PointAction.OffRoad,
) )
vehicle = vg.units[0]
GroundForcePainter(faction, vehicle).apply_livery()
for unit in units[1:]: for unit in units[1:]:
position = infantry_position.random_point_within(55, 5) position = infantry_position.random_point_within(55, 5)
self.mission.vehicle_group( vg = self.mission.vehicle_group(
side, side,
namegen.next_infantry_name(side, unit), namegen.next_infantry_name(side, unit),
unit.dcs_unit_type, unit.dcs_unit_type,
@ -267,6 +273,8 @@ class FlotGenerator:
heading=forward_heading.degrees, heading=forward_heading.degrees,
move_formation=PointAction.OffRoad, move_formation=PointAction.OffRoad,
) )
vehicle = vg.units[0]
GroundForcePainter(faction, vehicle).apply_livery()
def _set_reform_waypoint( def _set_reform_waypoint(
self, dcs_group: VehicleGroup, forward_heading: Heading self, dcs_group: VehicleGroup, forward_heading: Heading
@ -769,6 +777,8 @@ class FlotGenerator:
heading: Heading, heading: Heading,
) -> VehicleGroup: ) -> VehicleGroup:
cp = self.conflict.front_line.control_point_friendly_to(player) cp = self.conflict.front_line.control_point_friendly_to(player)
faction = self.game.faction_for(player)
group = self.mission.vehicle_group( group = self.mission.vehicle_group(
side, side,
namegen.next_unit_name(side, unit_type), namegen.next_unit_name(side, unit_type),
@ -783,5 +793,6 @@ class FlotGenerator:
for c in range(count): for c in range(count):
vehicle: Vehicle = group.units[c] vehicle: Vehicle = group.units[c]
vehicle.player_can_drive = True vehicle.player_can_drive = True
GroundForcePainter(faction, vehicle).apply_livery()
return group return group

View File

@ -0,0 +1,82 @@
from __future__ import annotations
import logging
import random
from typing import Any, Optional
from dcs.unit import Ship
from dcs.unitgroup import Vehicle
from game.factions.faction import Faction
class GroundForcePainter:
def __init__(self, faction: Faction, vehicle: Vehicle) -> None:
self.faction = faction
self.vehicle = vehicle
def livery_from_faction(self) -> Optional[str]:
faction = self.faction
try:
if (
choices := faction.liveries_overrides_ground_forces.get(
self.vehicle.type
)
) is not None:
return random.choice(choices)
except AttributeError:
logging.warning(
f"Faction {self.faction.name} is missing livery for ground unit {self.vehicle.type}"
)
return None
logging.warning(
f"Faction {self.faction.name} is missing livery for ground unit {self.vehicle.type}"
)
return None
def determine_livery(self) -> Optional[str]:
if (livery := self.livery_from_faction()) is not None:
return livery
return None
def apply_livery(self) -> None:
livery = self.determine_livery()
if livery is None:
return
self.vehicle.livery_id = livery
class NavalForcePainter:
def __init__(self, faction: Faction, vessel: Ship) -> None:
self.faction = faction
self.vessel = vessel
def livery_from_faction(self) -> Optional[str]:
faction = self.faction
try:
if (
choices := faction.liveries_overrides_ground_forces.get(
self.vessel.type
)
) is not None:
return random.choice(choices)
except AttributeError:
logging.warning(
f"Faction {self.faction.name} is missing livery for naval unit {self.vessel.type}"
)
return None
logging.warning(
f"Faction {self.faction.name} is missing livery for naval unit {self.vessel.type}"
)
return None
def determine_livery(self) -> Optional[str]:
if (livery := self.livery_from_faction()) is not None:
return livery
return None
def apply_livery(self) -> None:
livery = self.determine_livery()
if livery is None:
return
self.vessel.livery_id = livery

View File

@ -40,6 +40,10 @@ from dcs.unitgroup import MovingGroup, ShipGroup, StaticGroup, VehicleGroup
from dcs.unittype import ShipType, VehicleType from dcs.unittype import ShipType, VehicleType
from dcs.vehicles import vehicle_map from dcs.vehicles import vehicle_map
from game.missiongenerator.groundforcepainter import (
NavalForcePainter,
GroundForcePainter,
)
from game.missiongenerator.missiondata import CarrierInfo, MissionData from game.missiongenerator.missiondata import CarrierInfo, MissionData
from game.radio.radios import RadioFrequency, RadioRegistry from game.radio.radios import RadioFrequency, RadioRegistry
from game.radio.tacan import TacanBand, TacanChannel, TacanRegistry, TacanUsage from game.radio.tacan import TacanBand, TacanChannel, TacanRegistry, TacanUsage
@ -122,6 +126,7 @@ class GroundObjectGenerator:
vehicle_group: Optional[VehicleGroup] = None vehicle_group: Optional[VehicleGroup] = None
for unit in units: for unit in units:
assert issubclass(unit.type, VehicleType) assert issubclass(unit.type, VehicleType)
faction = unit.ground_object.control_point.coalition.faction
if vehicle_group is None: if vehicle_group is None:
vehicle_group = self.m.vehicle_group( vehicle_group = self.m.vehicle_group(
self.country, self.country,
@ -134,11 +139,13 @@ class GroundObjectGenerator:
self.enable_eplrs(vehicle_group, unit.type) self.enable_eplrs(vehicle_group, unit.type)
vehicle_group.units[0].name = unit.unit_name vehicle_group.units[0].name = unit.unit_name
self.set_alarm_state(vehicle_group) self.set_alarm_state(vehicle_group)
GroundForcePainter(faction, vehicle_group.units[0]).apply_livery()
else: else:
vehicle_unit = self.m.vehicle(unit.unit_name, unit.type) vehicle_unit = self.m.vehicle(unit.unit_name, unit.type)
vehicle_unit.player_can_drive = True vehicle_unit.player_can_drive = True
vehicle_unit.position = unit.position vehicle_unit.position = unit.position
vehicle_unit.heading = unit.position.heading.degrees vehicle_unit.heading = unit.position.heading.degrees
GroundForcePainter(faction, vehicle_unit).apply_livery()
vehicle_group.add_unit(vehicle_unit) vehicle_group.add_unit(vehicle_unit)
self._register_theater_unit(unit, vehicle_group.units[-1]) self._register_theater_unit(unit, vehicle_group.units[-1])
if vehicle_group is None: if vehicle_group is None:
@ -154,6 +161,7 @@ class GroundObjectGenerator:
ship_group: Optional[ShipGroup] = None ship_group: Optional[ShipGroup] = None
for unit in units: for unit in units:
assert issubclass(unit.type, ShipType) assert issubclass(unit.type, ShipType)
faction = unit.ground_object.control_point.coalition.faction
if ship_group is None: if ship_group is None:
ship_group = self.m.ship_group( ship_group = self.m.ship_group(
self.country, self.country,
@ -166,12 +174,14 @@ class GroundObjectGenerator:
ship_group.set_frequency(frequency.hertz) ship_group.set_frequency(frequency.hertz)
ship_group.units[0].name = unit.unit_name ship_group.units[0].name = unit.unit_name
self.set_alarm_state(ship_group) self.set_alarm_state(ship_group)
NavalForcePainter(faction, ship_group.units[0]).apply_livery()
else: else:
ship_unit = self.m.ship(unit.unit_name, unit.type) ship_unit = self.m.ship(unit.unit_name, unit.type)
if frequency: if frequency:
ship_unit.set_frequency(frequency.hertz) ship_unit.set_frequency(frequency.hertz)
ship_unit.position = unit.position ship_unit.position = unit.position
ship_unit.heading = unit.position.heading.degrees ship_unit.heading = unit.position.heading.degrees
NavalForcePainter(faction, ship_unit).apply_livery()
ship_group.add_unit(ship_unit) ship_group.add_unit(ship_unit)
self._register_theater_unit(unit, ship_group.units[-1]) self._register_theater_unit(unit, ship_group.units[-1])
if ship_group is None: if ship_group is None: