Add a wrapper type for ground unit info.

This commit is contained in:
Dan Albert
2021-06-17 21:48:02 -07:00
parent 8a0824880e
commit 09704b6f37
32 changed files with 469 additions and 1145 deletions

View File

@@ -110,7 +110,6 @@ class AirSupportConflictGenerator:
):
# TODO: Make loiter altitude a property of the unit type.
alt, airspeed = self._get_tanker_params(tanker_unit_type.dcs_unit_type)
variant = db.unit_type_name(tanker_unit_type)
freq = self.radio_registry.alloc_uhf()
tacan = self.tacan_registry.alloc_for_band(TacanBand.Y)
tanker_heading = (
@@ -175,7 +174,7 @@ class AirSupportConflictGenerator:
TankerInfo(
str(tanker_group.name),
callsign,
variant,
tanker_unit_type.name,
freq,
tacan,
blue=True,

View File

@@ -10,7 +10,6 @@ from dcs.action import AITaskPush
from dcs.condition import GroupLifeLess, Or, TimeAfter, UnitDamaged
from dcs.country import Country
from dcs.mapping import Point
from dcs.planes import MQ_9_Reaper
from dcs.point import PointAction
from dcs.task import (
EPLRS,
@@ -26,19 +25,18 @@ from dcs.task import (
from dcs.triggers import Event, TriggerOnce
from dcs.unit import Vehicle
from dcs.unitgroup import VehicleGroup
from dcs.unittype import VehicleType
from game import db
from game.data.groundunitclass import GroundUnitClass
from game.dcs.aircrafttype import AircraftType
from game.dcs.groundunittype import GroundUnitType
from game.theater.controlpoint import ControlPoint
from game.unitmap import UnitMap
from game.utils import heading_sum, opposite_heading
from game.theater.controlpoint import ControlPoint
from gen.ground_forces.ai_ground_planner import (
DISTANCE_FROM_FRONTLINE,
CombatGroup,
CombatGroupRole,
)
from .callsigns import callsign_for_support_unit
from .conflictgen import Conflict
from .ground_forces.combat_stance import CombatStance
@@ -226,19 +224,18 @@ class GroundConflictGenerator:
else:
cp = self.conflict.red_cp
if is_player:
faction = self.game.player_name
else:
faction = self.game.enemy_name
faction = self.game.faction_for(is_player)
# Disable infantry unit gen if disabled
if not self.game.settings.perf_infantry:
if self.game.settings.manpads:
# 50% of armored units protected by manpad
if random.choice([True, False]):
manpads = db.find_manpad(faction)
if len(manpads) > 0:
u = random.choice(manpads)
manpads = list(faction.infantry_with_class(GroundUnitClass.Manpads))
if manpads:
u = random.choices(
manpads, weights=[m.spawn_weight for m in manpads]
)[0]
self.mission.vehicle_group(
side,
namegen.next_infantry_name(side, cp.id, u),
@@ -250,30 +247,38 @@ class GroundConflictGenerator:
)
return
possible_infantry_units = db.find_infantry(
faction, allow_manpad=self.game.settings.manpads
possible_infantry_units = set(
faction.infantry_with_class(GroundUnitClass.Infantry)
)
if len(possible_infantry_units) == 0:
if self.game.settings.manpads:
possible_infantry_units |= set(
faction.infantry_with_class(GroundUnitClass.Manpads)
)
if not possible_infantry_units:
return
u = random.choice(possible_infantry_units)
infantry_choices = list(possible_infantry_units)
units = random.choices(
infantry_choices,
weights=[u.spawn_weight for u in infantry_choices],
k=INFANTRY_GROUP_SIZE,
)
self.mission.vehicle_group(
side,
namegen.next_infantry_name(side, cp.id, u),
u,
namegen.next_infantry_name(side, cp.id, units[0]),
units[0].dcs_unit_type,
position=infantry_position,
group_size=1,
heading=forward_heading,
move_formation=PointAction.OffRoad,
)
for i in range(INFANTRY_GROUP_SIZE):
u = random.choice(possible_infantry_units)
for unit in units[1:]:
position = infantry_position.random_point_within(55, 5)
self.mission.vehicle_group(
side,
namegen.next_infantry_name(side, cp.id, u),
u,
namegen.next_infantry_name(side, cp.id, unit),
unit.dcs_unit_type,
position=position,
group_size=1,
heading=forward_heading,
@@ -313,7 +318,7 @@ class GroundConflictGenerator:
)
artillery_trigger.add_condition(TimeAfter(seconds=random.randint(1, 45) * 60))
# TODO: Update to fire at group instead of point
fire_task = FireAtPoint(target, len(gen_group.units) * 10, 100)
fire_task = FireAtPoint(target, gen_group.size * 10, 100)
fire_task.number = 2 if stance != CombatStance.RETREAT else 1
dcs_group.add_trigger_action(fire_task)
artillery_trigger.add_action(AITaskPush(dcs_group.id, len(dcs_group.tasks)))
@@ -503,7 +508,7 @@ class GroundConflictGenerator:
return
for dcs_group, group in ally_groups:
if hasattr(group.units[0], "eplrs") and group.units[0].eplrs:
if getattr(group.unit_type.dcs_unit_type, "eplrs", False):
dcs_group.points[0].tasks.append(EPLRS(dcs_group.id))
if group.role == CombatGroupRole.ARTILLERY:
@@ -674,7 +679,7 @@ class GroundConflictGenerator:
Search the enemy groups for a potential target suitable to an artillery unit
"""
# TODO: Update to return a list of groups instead of a single point
rng = group.units[0].threat_range
rng = getattr(group.unit_type.dcs_unit_type, "threat_range", 0)
if not enemy_groups:
return None
for _ in range(10):
@@ -691,7 +696,7 @@ class GroundConflictGenerator:
"""
For artilery group, decide the distance from frontline with the range of the unit
"""
rg = group.units[0].threat_range - 7500
rg = getattr(group.unit_type.dcs_unit_type, "threat_range", 0) - 7500
if rg > DISTANCE_FROM_FRONTLINE[CombatGroupRole.ARTILLERY][1]:
rg = random.randint(
DISTANCE_FROM_FRONTLINE[CombatGroupRole.ARTILLERY][0],
@@ -724,7 +729,7 @@ class GroundConflictGenerator:
def _generate_groups(
self,
groups: List[CombatGroup],
groups: list[CombatGroup],
frontline_vector: Tuple[Point, int, int],
is_player: bool,
) -> List[Tuple[VehicleGroup, CombatGroup]]:
@@ -755,10 +760,9 @@ class GroundConflictGenerator:
if final_position is not None:
g = self._generate_group(
self.mission.country(country),
group.units[0],
len(group.units),
group.unit_type,
group.size,
final_position,
distance_from_frontline,
heading=opposite_heading(spawn_heading),
)
if is_player:
@@ -782,10 +786,9 @@ class GroundConflictGenerator:
def _generate_group(
self,
side: Country,
unit: VehicleType,
unit_type: GroundUnitType,
count: int,
at: Point,
distance_from_frontline,
move_formation: PointAction = PointAction.OffRoad,
heading=0,
) -> VehicleGroup:
@@ -795,18 +798,17 @@ class GroundConflictGenerator:
else:
cp = self.conflict.red_cp
logging.info("armorgen: {} for {}".format(unit, side.id))
group = self.mission.vehicle_group(
side,
namegen.next_unit_name(side, cp.id, unit),
unit,
namegen.next_unit_name(side, cp.id, unit_type),
unit_type.dcs_unit_type,
position=at,
group_size=count,
heading=heading,
move_formation=move_formation,
)
self.unit_map.add_front_line_units(group, cp)
self.unit_map.add_front_line_units(group, cp, unit_type)
for c in range(count):
vehicle: Vehicle = group.units[c]

View File

@@ -1,15 +1,15 @@
from __future__ import annotations
import itertools
from typing import Dict, TYPE_CHECKING, Type
from typing import TYPE_CHECKING
from dcs import Mission
from dcs.mapping import Point
from dcs.point import PointAction
from dcs.unit import Vehicle
from dcs.unitgroup import VehicleGroup
from dcs.unittype import VehicleType
from game.dcs.groundunittype import GroundUnitType
from game.transfers import Convoy
from game.unitmap import UnitMap
from game.utils import kph
@@ -50,7 +50,7 @@ class ConvoyGenerator:
self,
name: str,
position: Point,
units: Dict[Type[VehicleType], int],
units: dict[GroundUnitType, int],
for_player: bool,
) -> VehicleGroup:
country = self.mission.country(
@@ -63,7 +63,7 @@ class ConvoyGenerator:
group = self.mission.vehicle_group(
country,
name,
main_unit_type,
main_unit_type.dcs_unit_type,
position=position,
group_size=main_unit_count,
move_formation=PointAction.OnRoad,
@@ -76,7 +76,7 @@ class ConvoyGenerator:
for unit_type, count in unit_types[1:]:
for i in range(count):
v = self.mission.vehicle(
f"{name} Unit #{next(unit_name_counter)}", unit_type
f"{name} Unit #{next(unit_name_counter)}", unit_type.dcs_unit_type
)
v.position.x = position.x
v.position.y = next(y)

View File

@@ -1,8 +1,11 @@
import random
from dcs.vehicles import Armor
from dcs.unitgroup import VehicleGroup
from game import db
from game import db, Game
from game.data.groundunitclass import GroundUnitClass
from game.dcs.groundunittype import GroundUnitType
from game.theater.theatergroundobject import VehicleGroupGroundObject
from gen.defenses.armored_group_generator import (
ArmoredGroupGenerator,
FixedSizeArmorGroupGenerator,
@@ -14,8 +17,14 @@ def generate_armor_group(faction: str, game, ground_object):
This generate a group of ground units
:return: Generated group
"""
armor_types = (
GroundUnitClass.Apc,
GroundUnitClass.Atgm,
GroundUnitClass.Ifv,
GroundUnitClass.Tank,
)
possible_unit = [
u for u in db.FACTIONS[faction].frontline_units if u in Armor.__dict__.values()
u for u in db.FACTIONS[faction].frontline_units if u.unit_class in armor_types
]
if len(possible_unit) > 0:
unit_type = random.choice(possible_unit)
@@ -23,7 +32,9 @@ def generate_armor_group(faction: str, game, ground_object):
return None
def generate_armor_group_of_type(game, ground_object, unit_type):
def generate_armor_group_of_type(
game: Game, ground_object: VehicleGroupGroundObject, unit_type: GroundUnitType
) -> VehicleGroup:
"""
This generate a group of ground units of given type
:return: Generated group
@@ -33,7 +44,12 @@ def generate_armor_group_of_type(game, ground_object, unit_type):
return generator.get_generated_group()
def generate_armor_group_of_type_and_size(game, ground_object, unit_type, size: int):
def generate_armor_group_of_type_and_size(
game: Game,
ground_object: VehicleGroupGroundObject,
unit_type: GroundUnitType,
size: int,
) -> VehicleGroup:
"""
This generate a group of ground units of given type and size
:return: Generated group

View File

@@ -1,15 +1,22 @@
import random
from game import Game
from game.dcs.groundunittype import GroundUnitType
from game.theater.theatergroundobject import VehicleGroupGroundObject
from gen.sam.group_generator import GroupGenerator
class ArmoredGroupGenerator(GroupGenerator):
def __init__(self, game, ground_object, unit_type):
super(ArmoredGroupGenerator, self).__init__(game, ground_object)
def __init__(
self,
game: Game,
ground_object: VehicleGroupGroundObject,
unit_type: GroundUnitType,
) -> None:
super().__init__(game, ground_object)
self.unit_type = unit_type
def generate(self):
def generate(self) -> None:
grid_x = random.randint(2, 3)
grid_y = random.randint(1, 2)
@@ -20,7 +27,7 @@ class ArmoredGroupGenerator(GroupGenerator):
for j in range(grid_y):
index = index + 1
self.add_unit(
self.unit_type,
self.unit_type.dcs_unit_type,
"Armor#" + str(index),
self.position.x + spacing * i,
self.position.y + spacing * j,
@@ -29,8 +36,14 @@ class ArmoredGroupGenerator(GroupGenerator):
class FixedSizeArmorGroupGenerator(GroupGenerator):
def __init__(self, game, ground_object, unit_type, size):
super(FixedSizeArmorGroupGenerator, self).__init__(game, ground_object)
def __init__(
self,
game: Game,
ground_object: VehicleGroupGroundObject,
unit_type: GroundUnitType,
size: int,
) -> None:
super().__init__(game, ground_object)
self.unit_type = unit_type
self.size = size
@@ -41,7 +54,7 @@ class FixedSizeArmorGroupGenerator(GroupGenerator):
for i in range(self.size):
index = index + 1
self.add_unit(
self.unit_type,
self.unit_type.dcs_unit_type,
"Armor#" + str(index),
self.position.x + spacing * i,
self.position.y,

View File

@@ -3,11 +3,9 @@ import random
from enum import Enum
from typing import Dict, List
from dcs.unittype import VehicleType
from game.theater import ControlPoint
from game.data.groundunitclass import GroundUnitClass
from game.dcs.groundunittype import GroundUnitType
from game.theater import ControlPoint
from gen.ground_forces.combat_stance import CombatStance
MAX_COMBAT_GROUP_PER_CP = 10
@@ -48,17 +46,19 @@ GROUP_SIZES_BY_COMBAT_STANCE = {
class CombatGroup:
def __init__(self, role: CombatGroupRole):
self.units: List[VehicleType] = []
def __init__(
self, role: CombatGroupRole, unit_type: GroundUnitType, size: int
) -> None:
self.unit_type = unit_type
self.size = size
self.role = role
self.assigned_enemy_cp = None
self.start_position = None
def __str__(self):
s = ""
s += "ROLE : " + str(self.role) + "\n"
if len(self.units) > 0:
s += "UNITS " + self.units[0].name + " * " + str(len(self.units))
s = f"ROLE : {self.role}\n"
if self.size:
s += f"UNITS {self.unit_type} * {self.size}"
return s
@@ -97,28 +97,29 @@ class GroundPlanner:
# Create combat groups and assign them randomly to each enemy CP
for unit_type in self.cp.base.armor:
if unit_type in GroundUnitClass.Tank:
unit_class = unit_type.unit_class
if unit_class is GroundUnitClass.Tank:
collection = self.tank_groups
role = CombatGroupRole.TANK
elif unit_type in GroundUnitClass.Apc:
elif unit_class is GroundUnitClass.Apc:
collection = self.apc_group
role = CombatGroupRole.APC
elif unit_type in GroundUnitClass.Artillery:
elif unit_class is GroundUnitClass.Artillery:
collection = self.art_group
role = CombatGroupRole.ARTILLERY
elif unit_type in GroundUnitClass.Ifv:
elif unit_class is GroundUnitClass.Ifv:
collection = self.ifv_group
role = CombatGroupRole.IFV
elif unit_type in GroundUnitClass.Logistics:
elif unit_class is GroundUnitClass.Logistics:
collection = self.logi_groups
role = CombatGroupRole.LOGI
elif unit_type in GroundUnitClass.Atgm:
elif unit_class is GroundUnitClass.Atgm:
collection = self.atgm_group
role = CombatGroupRole.ATGM
elif unit_type in GroundUnitClass.Shorads:
elif unit_class is GroundUnitClass.Shorads:
collection = self.shorad_groups
role = CombatGroupRole.SHORAD
elif unit_type in GroundUnitClass.Recon:
elif unit_class is GroundUnitClass.Recon:
collection = self.recon_groups
role = CombatGroupRole.RECON
else:
@@ -137,17 +138,17 @@ class GroundPlanner:
while available > 0:
if role == CombatGroupRole.SHORAD:
n = 1
count = 1
else:
n = random.choice(group_size_choice)
if n > available:
count = random.choice(group_size_choice)
if count > available:
if available >= 2:
n = 2
count = 2
else:
n = 1
available -= n
count = 1
available -= count
group = CombatGroup(role)
group = CombatGroup(role, unit_type, count)
if len(self.connected_enemy_cp) > 0:
enemy_cp = random.choice(self.connected_enemy_cp).id
self.units_per_cp[enemy_cp].append(group)
@@ -155,9 +156,6 @@ class GroundPlanner:
else:
self.reserve.append(group)
group.assigned_enemy_cp = "__reserve__"
for i in range(n):
group.units.append(unit_type)
collection.append(group)
if remaining_available_frontline_units == 0:

View File

@@ -3,11 +3,9 @@ import time
from typing import List
from dcs.country import Country
from dcs.unittype import UnitType
from game import db
from game.dcs.aircrafttype import AircraftType
from game.dcs.unittype import UnitType
from gen.flights.flight import Flight
ALPHA_MILITARY = [
@@ -298,7 +296,7 @@ class NameGenerator:
def next_unit_name(cls, country: Country, parent_base_id: int, unit_type: UnitType):
cls.number += 1
return "unit|{}|{}|{}|{}|".format(
country.id, cls.number, parent_base_id, db.unit_type_name(unit_type)
country.id, cls.number, parent_base_id, unit_type.name
)
@classmethod
@@ -310,7 +308,7 @@ class NameGenerator:
country.id,
cls.infantry_number,
parent_base_id,
db.unit_type_name(unit_type),
unit_type.name,
)
@classmethod