mirror of
https://github.com/dcs-liberation/dcs_liberation.git
synced 2025-11-10 14:22:26 +00:00
Handle callsigns for flights.
We don't configure the callsigns that pydcs uses, so instead read those from pydcs and use them where appropriate instead of just guessing. Fixes https://github.com/Khopa/dcs_liberation/issues/113.
This commit is contained in:
parent
51bfc9a59b
commit
993bf50012
@ -199,7 +199,7 @@ class Operation:
|
||||
)
|
||||
|
||||
# Generate ground units on frontline everywhere
|
||||
self.game.jtacs = []
|
||||
jtacs: List[JtacInfo] = []
|
||||
for player_cp, enemy_cp in self.game.theater.conflicts(True):
|
||||
conflict = Conflict.frontline_cas_conflict(self.attacker_name, self.defender_name,
|
||||
self.current_mission.country(self.attacker_country),
|
||||
@ -210,6 +210,7 @@ class Operation:
|
||||
enemy_gp = self.game.ground_planners[enemy_cp.id].units_per_cp[player_cp.id]
|
||||
groundConflictGen = GroundConflictGenerator(self.current_mission, conflict, self.game, player_gp, enemy_gp, player_cp.stances[enemy_cp.id])
|
||||
groundConflictGen.generate()
|
||||
jtacs.extend(groundConflictGen.jtacs)
|
||||
|
||||
# Setup combined arms parameters
|
||||
self.current_mission.groundControl.pilot_can_control_vehicles = self.ca_slots > 0
|
||||
@ -250,8 +251,8 @@ class Operation:
|
||||
if not self.game.settings.jtac_smoke_on:
|
||||
smoke = "false"
|
||||
|
||||
for jtac in self.game.jtacs:
|
||||
script = script + "\n" + "JTACAutoLase('" + str(jtac[2]) + "', " + str(jtac[1]) + ", " + smoke + ", \"vehicle\")" + "\n"
|
||||
for jtac in jtacs:
|
||||
script += f"\nJTACAutoLase('{jtac.unit_name}', {jtac.code}, {smoke}, 'vehicle')\n"
|
||||
|
||||
load_autolase.add_action(DoScript(String(script)))
|
||||
self.current_mission.triggerrules.triggers.append(load_autolase)
|
||||
@ -282,9 +283,7 @@ class Operation:
|
||||
self.briefinggen.add_awacs(awacs)
|
||||
kneeboard_generator.add_awacs(awacs)
|
||||
|
||||
for region, code, name in self.game.jtacs:
|
||||
# TODO: Radio info? Type?
|
||||
jtac = JtacInfo(name, region, code)
|
||||
for jtac in jtacs:
|
||||
self.briefinggen.add_jtac(jtac)
|
||||
kneeboard_generator.add_jtac(jtac)
|
||||
|
||||
|
||||
@ -202,9 +202,27 @@ class FlightData:
|
||||
self.waypoints = waypoints
|
||||
self.intra_flight_channel = intra_flight_channel
|
||||
self.frequency_to_channel_map = {}
|
||||
self.callsign = self.create_group_callsign()
|
||||
|
||||
self.assign_intra_flight_channel()
|
||||
|
||||
def create_group_callsign(self) -> str:
|
||||
lead = self.units[0]
|
||||
raw_callsign = lead.callsign_as_str()
|
||||
if not lead.callsign_is_western:
|
||||
# Callsigns for non-Western countries are just a number per flight,
|
||||
# similar to tail numbers.
|
||||
return f"Flight {raw_callsign}"
|
||||
|
||||
# Callsign from pydcs is in the format `<name><group ID><unit ID>`,
|
||||
# where unit ID is guaranteed to be a single digit but the group ID may
|
||||
# be more.
|
||||
match = re.search(r"^(\D+)(\d+)(\d)$", raw_callsign)
|
||||
if match is None:
|
||||
logging.error(f"Could not parse unit callsign: {raw_callsign}")
|
||||
return f"Flight {raw_callsign}"
|
||||
return f"{match.group(1)} {match.group(2)}"
|
||||
|
||||
@property
|
||||
def client_units(self) -> List[FlyingUnit]:
|
||||
"""List of playable units in the flight."""
|
||||
@ -254,6 +272,17 @@ class FlightData:
|
||||
)
|
||||
|
||||
|
||||
def callsign_for_support_unit(group: FlyingGroup) -> str:
|
||||
# Either something like Overlord11 for Western AWACS, or else just a number.
|
||||
# Convert to either "Overlord" or "Flight 123".
|
||||
lead = group.units[0]
|
||||
raw_callsign = lead.callsign_as_str()
|
||||
try:
|
||||
return f"Flight {int(raw_callsign)}"
|
||||
except ValueError:
|
||||
return raw_callsign.rstrip("1234567890")
|
||||
|
||||
|
||||
class AircraftConflictGenerator:
|
||||
escort_targets = [] # type: typing.List[typing.Tuple[FlyingGroup, int]]
|
||||
|
||||
|
||||
@ -1,16 +1,11 @@
|
||||
from typing import List
|
||||
from dataclasses import dataclass, field
|
||||
|
||||
from .aircraft import callsign_for_support_unit
|
||||
from .conflictgen import *
|
||||
from .naming import *
|
||||
from .radios import RadioFrequency, RadioRegistry
|
||||
from .tacan import TacanBand, TacanChannel, TacanRegistry
|
||||
|
||||
from dcs.mission import *
|
||||
from dcs.unitgroup import *
|
||||
from dcs.unittype import *
|
||||
from dcs.task import *
|
||||
|
||||
TANKER_DISTANCE = 15000
|
||||
TANKER_ALT = 4572
|
||||
TANKER_HEADING_OFFSET = 45
|
||||
@ -18,27 +13,6 @@ TANKER_HEADING_OFFSET = 45
|
||||
AWACS_DISTANCE = 150000
|
||||
AWACS_ALT = 13000
|
||||
|
||||
AWACS_CALLSIGNS = [
|
||||
"Overlord",
|
||||
"Magic",
|
||||
"Wizard",
|
||||
"Focus",
|
||||
"Darkstar",
|
||||
]
|
||||
|
||||
|
||||
@dataclass
|
||||
class TankerCallsign:
|
||||
full: str
|
||||
short: str
|
||||
|
||||
|
||||
TANKER_CALLSIGNS = [
|
||||
TankerCallsign("Texaco", "TEX"),
|
||||
TankerCallsign("Arco", "ARC"),
|
||||
TankerCallsign("Shell", "SHL"),
|
||||
]
|
||||
|
||||
|
||||
@dataclass
|
||||
class AwacsInfo:
|
||||
@ -81,8 +55,9 @@ class AirSupportConflictGenerator:
|
||||
def generate(self, is_awacs_enabled):
|
||||
player_cp = self.conflict.from_cp if self.conflict.from_cp.captured else self.conflict.to_cp
|
||||
|
||||
fallback_tanker_number = 0
|
||||
|
||||
for i, tanker_unit_type in enumerate(db.find_unittype(Refueling, self.conflict.attackers_side)):
|
||||
callsign = TANKER_CALLSIGNS[i]
|
||||
variant = db.unit_type_name(tanker_unit_type)
|
||||
freq = self.radio_registry.alloc_uhf()
|
||||
tacan = self.tacan_registry.alloc_for_band(TacanBand.Y)
|
||||
@ -102,19 +77,35 @@ class AirSupportConflictGenerator:
|
||||
tacanchannel=str(tacan),
|
||||
)
|
||||
|
||||
callsign = callsign_for_support_unit(tanker_group)
|
||||
tacan_callsign = {
|
||||
"Texaco": "TEX",
|
||||
"Arco": "ARC",
|
||||
"Shell": "SHL",
|
||||
}.get(callsign)
|
||||
if tacan_callsign is None:
|
||||
# The dict above is all the callsigns currently in the game, but
|
||||
# non-Western countries don't use the callsigns and instead just
|
||||
# use numbers. It's possible that none of those nations have
|
||||
# TACAN compatible refueling aircraft, but fallback just in
|
||||
# case.
|
||||
tacan_callsign = f"TK{fallback_tanker_number}"
|
||||
fallback_tanker_number += 1
|
||||
|
||||
if tanker_unit_type != IL_78M:
|
||||
tanker_group.points[0].tasks.pop() # Override PyDCS tacan channel
|
||||
# Override PyDCS tacan channel.
|
||||
tanker_group.points[0].tasks.pop()
|
||||
tanker_group.points[0].tasks.append(ActivateBeaconCommand(
|
||||
tacan.number, tacan.band.value, callsign.short, True, tanker_group.units[0].id, True))
|
||||
tacan.number, tacan.band.value, tacan_callsign, True,
|
||||
tanker_group.units[0].id, True))
|
||||
|
||||
tanker_group.points[0].tasks.append(SetInvisibleCommand(True))
|
||||
tanker_group.points[0].tasks.append(SetImmortalCommand(True))
|
||||
|
||||
self.air_support.tankers.append(TankerInfo(callsign.full, variant, freq, tacan))
|
||||
self.air_support.tankers.append(TankerInfo(callsign, variant, freq, tacan))
|
||||
|
||||
if is_awacs_enabled:
|
||||
try:
|
||||
callsign = AWACS_CALLSIGNS[0]
|
||||
freq = self.radio_registry.alloc_uhf()
|
||||
awacs_unit = db.find_unittype(AWACS, self.conflict.attackers_side)[0]
|
||||
awacs_flight = self.mission.awacs_flight(
|
||||
@ -129,7 +120,8 @@ class AirSupportConflictGenerator:
|
||||
)
|
||||
awacs_flight.points[0].tasks.append(SetInvisibleCommand(True))
|
||||
awacs_flight.points[0].tasks.append(SetImmortalCommand(True))
|
||||
self.air_support.awacs.append(AwacsInfo(callsign, freq))
|
||||
|
||||
self.air_support.awacs.append(AwacsInfo(
|
||||
callsign_for_support_unit(awacs_flight), freq))
|
||||
except:
|
||||
print("No AWACS for faction")
|
||||
|
||||
|
||||
25
gen/armor.py
25
gen/armor.py
@ -1,10 +1,12 @@
|
||||
from dcs.action import AITaskPush, AITaskSet
|
||||
from dataclasses import dataclass
|
||||
|
||||
from dcs.action import AITaskPush
|
||||
from dcs.condition import TimeAfter, UnitDamaged, Or, GroupLifeLess
|
||||
from dcs.task import *
|
||||
from dcs.triggers import TriggerOnce, Event
|
||||
|
||||
from gen import namegen
|
||||
from gen.ground_forces.ai_ground_planner import CombatGroupRole, DISTANCE_FROM_FRONTLINE
|
||||
from .aircraft import callsign_for_support_unit
|
||||
from .conflictgen import *
|
||||
|
||||
SPREAD_DISTANCE_FACTOR = 0.1, 0.3
|
||||
@ -22,6 +24,17 @@ FIGHT_DISTANCE = 3500
|
||||
|
||||
RANDOM_OFFSET_ATTACK = 250
|
||||
|
||||
|
||||
@dataclass(frozen=True)
|
||||
class JtacInfo:
|
||||
"""JTAC information."""
|
||||
unit_name: str
|
||||
callsign: str
|
||||
region: str
|
||||
code: str
|
||||
# TODO: Radio info? Type?
|
||||
|
||||
|
||||
class GroundConflictGenerator:
|
||||
|
||||
def __init__(self, mission: Mission, conflict: Conflict, game, player_planned_combat_groups, enemy_planned_combat_groups, player_stance):
|
||||
@ -32,6 +45,7 @@ class GroundConflictGenerator:
|
||||
self.player_stance = CombatStance(player_stance)
|
||||
self.enemy_stance = random.choice([CombatStance.AGGRESIVE, CombatStance.AGGRESIVE, CombatStance.AGGRESIVE, CombatStance.ELIMINATION, CombatStance.BREAKTHROUGH]) if len(enemy_planned_combat_groups) > len(player_planned_combat_groups) else random.choice([CombatStance.DEFENSIVE, CombatStance.DEFENSIVE, CombatStance.DEFENSIVE, CombatStance.AMBUSH, CombatStance.AGGRESIVE])
|
||||
self.game = game
|
||||
self.jtacs: List[JtacInfo] = []
|
||||
|
||||
def _group_point(self, point) -> Point:
|
||||
distance = randint(
|
||||
@ -100,7 +114,7 @@ class GroundConflictGenerator:
|
||||
# Add JTAC
|
||||
if "has_jtac" in self.game.player_faction and self.game.player_faction["has_jtac"] and self.game.settings.include_jtac_if_available:
|
||||
n = "JTAC" + str(self.conflict.from_cp.id) + str(self.conflict.to_cp.id)
|
||||
code = 1688 - len(self.game.jtacs)
|
||||
code = 1688 - len(self.jtacs)
|
||||
|
||||
utype = MQ_9_Reaper
|
||||
if "jtac_unit" in self.game.player_faction:
|
||||
@ -115,7 +129,10 @@ class GroundConflictGenerator:
|
||||
jtac.points[0].tasks.append(SetInvisibleCommand(True))
|
||||
jtac.points[0].tasks.append(SetImmortalCommand(True))
|
||||
jtac.points[0].tasks.append(OrbitAction(5000, 300, OrbitAction.OrbitPattern.Circle))
|
||||
self.game.jtacs.append(("Frontline " + self.conflict.from_cp.name + "/" + self.conflict.to_cp.name, code, n))
|
||||
frontline = f"Frontline {self.conflict.from_cp.name}/{self.conflict.to_cp.name}"
|
||||
# Note: Will need to change if we ever add ground based JTAC.
|
||||
callsign = callsign_for_support_unit(jtac)
|
||||
self.jtacs.append(JtacInfo(n, callsign, frontline, str(code)))
|
||||
|
||||
def gen_infantry_group_for_group(self, group, is_player, side:Country, forward_heading):
|
||||
|
||||
|
||||
@ -2,13 +2,14 @@ import os
|
||||
from collections import defaultdict
|
||||
from dataclasses import dataclass
|
||||
import random
|
||||
from typing import List, Tuple
|
||||
from typing import List
|
||||
|
||||
from game import db
|
||||
from dcs.mission import Mission
|
||||
from .aircraft import FlightData
|
||||
from .airfields import RunwayData
|
||||
from .airsupportgen import AwacsInfo, TankerInfo
|
||||
from .armor import JtacInfo
|
||||
from .conflictgen import Conflict
|
||||
from .ground_forces.combat_stance import CombatStance
|
||||
from .radios import RadioFrequency
|
||||
@ -21,14 +22,6 @@ class CommInfo:
|
||||
freq: RadioFrequency
|
||||
|
||||
|
||||
@dataclass
|
||||
class JtacInfo:
|
||||
"""JTAC information for the kneeboard."""
|
||||
callsign: str
|
||||
region: str
|
||||
code: str
|
||||
|
||||
|
||||
class MissionInfoGenerator:
|
||||
"""Base type for generators of mission information for the player.
|
||||
|
||||
|
||||
@ -109,9 +109,7 @@ class BriefingPage(KneeboardPage):
|
||||
|
||||
def write(self, path: Path) -> None:
|
||||
writer = KneeboardPageWriter()
|
||||
# TODO: Assign callsigns to flights and include that info.
|
||||
# https://github.com/Khopa/dcs_liberation/issues/113
|
||||
writer.title(f"Mission Info")
|
||||
writer.title(f"{self.flight.callsign} Mission Info")
|
||||
|
||||
# TODO: Handle carriers.
|
||||
writer.heading("Airfield Info")
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user