mirror of
https://github.com/dcs-liberation/dcs_liberation.git
synced 2025-11-10 14:22:26 +00:00
Merge branch 'develop' into helipads
# Conflicts: # game/data/weapons.py # game/db.py # game/theater/conflicttheater.py # resources/factions/france_1995.json # resources/factions/insurgents.json # resources/factions/iraq_1991.json # resources/factions/syria_1967_with_ww2_weapons.json # resources/factions/syria_2011.json
This commit is contained in:
commit
f31861441b
2
.github/ISSUE_TEMPLATE/bug_report.md
vendored
2
.github/ISSUE_TEMPLATE/bug_report.md
vendored
@ -9,6 +9,8 @@ assignees: ''
|
||||
|
||||
Before filing, please search the issue tracker to see if the issue has already been reported.
|
||||
|
||||
If reporting a DCS AI bug, check https://github.com/dcs-liberation/dcs_liberation#dcs-bugs.
|
||||
|
||||
**Describe the bug**
|
||||
A clear and concise description of what the bug is.
|
||||
|
||||
|
||||
2
.github/ISSUE_TEMPLATE/feature_request.md
vendored
2
.github/ISSUE_TEMPLATE/feature_request.md
vendored
@ -9,6 +9,8 @@ assignees: ''
|
||||
|
||||
Before filing, please search the issue tracker to see if this feature has already been requested.
|
||||
|
||||
If requesting a DCS AI feature, check If reporting a DCS AI bug, check https://github.com/dcs-liberation/dcs_liberation#dcs-bugs.
|
||||
|
||||
**Is your feature request related to a problem? Please describe.**
|
||||
A clear and concise description of what the problem is. Ex. I'm always frustrated when [...]
|
||||
|
||||
|
||||
@ -22,6 +22,14 @@ Latest release is available here : https://github.com/dcs-liberation/dcs_liberat
|
||||
|
||||
To download preview builds of the next version of DCS Liberation, see https://github.com/dcs-liberation/dcs_liberation/wiki/Preview-builds.
|
||||
|
||||
## DCS bugs
|
||||
|
||||
These DCS bugs prevent us from improving AI behavior. Please upvote them! (But please
|
||||
_don't_ spam them with comments):
|
||||
|
||||
* [A2A and SEAD escorts don't escort](https://forums.eagle.ru/topic/251798-options-for-alternate-ai-escort-behavior/?tab=comments#comment-4668033)
|
||||
* [DEAD can't use mixed loadouts effectively](https://forums.eagle.ru/topic/271941-ai-rtbs-after-firing-decoys-despite-full-load-of-bombs/)
|
||||
|
||||
## Bugs and feature requests
|
||||
|
||||
If you need to report a bug or want to suggest a new feature, you can do this on our [bug tracker](https://github.com/dcs-liberation/dcs_liberation/issues). In either case, please use the search bar at the top of the page to see if it has already been reported. Note that you may need to remove the filter for open bugs if it's something we've recently fixed.
|
||||
|
||||
@ -7,10 +7,13 @@ Saves from 2.5 are not compatible with 3.0.
|
||||
* **[Campaign]** Ground units can now be transferred by road, airlift, and cargo ship. See https://github.com/dcs-liberation/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.
|
||||
* **[Campaign]** Ground units must now be recruited at a base with a factory and transferred to their destination. When buying units in the UI, the purchase will automatically be fulfilled at the closest factory, and a transfer will be created on the next turn.
|
||||
* **[Campaign]** Non-control point FOBs will no longer spawn.
|
||||
* **[Campaign AI]** Every 30 minutes the AI will plan a CAP, so players can customize their mission better.
|
||||
* **[Campaign AI]** AI now considers Ju-88s for CAS, strike, and DEAD missions.
|
||||
* **[Campaign AI]** Fix purchase of aircraft by priority (the faction's list was being used as the priority list rather than the game's).
|
||||
* **[Flight Planner]** AI strike flight plans now include the correct target actions for building groups.
|
||||
* **[Flight Planner]** Flight plans now include bullseye waypoints.
|
||||
* **[Kneeboard]** ATC table overflow alleviated by wrapping long airfield names and splitting ATC frequency and channel into separate rows.
|
||||
* **[UI]** Added new web based map UI. This is mostly functional but many of the old display options are a WIP. Revert to the old map with --old-map.
|
||||
* **[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.
|
||||
* **[UI]** DCS loadouts are now selectable in the loadout setup menu.
|
||||
|
||||
@ -8,7 +8,6 @@ DEFAULT_AVAILABLE_BUILDINGS = [
|
||||
"oil",
|
||||
"ware",
|
||||
"farp",
|
||||
"fob",
|
||||
"power",
|
||||
"derrick",
|
||||
]
|
||||
@ -21,7 +20,6 @@ WW2_GERMANY_BUILDINGS = [
|
||||
"ww2bunker",
|
||||
"allycamp",
|
||||
"allycamp",
|
||||
"fob",
|
||||
]
|
||||
WW2_ALLIES_BUILDINGS = [
|
||||
"fuel",
|
||||
@ -30,7 +28,6 @@ WW2_ALLIES_BUILDINGS = [
|
||||
"allycamp",
|
||||
"allycamp",
|
||||
"allycamp",
|
||||
"fob",
|
||||
]
|
||||
|
||||
FORTIFICATION_BUILDINGS = [
|
||||
|
||||
@ -145,20 +145,26 @@ _WEAPON_FALLBACKS = [
|
||||
Weapons.LAU_117_with_AGM_65E___Maverick_E__Laser_ASM___Lg_Whd_,
|
||||
), # internal pylons harrier
|
||||
# AGM-154 JSOW
|
||||
(Weapons.AGM_154A___JSOW_CEB__CBU_type_, Weapons.GBU_12),
|
||||
(
|
||||
Weapons.AGM_154A___JSOW_CEB__CBU_type_,
|
||||
Weapons.AGM_62_Walleye_II___Guided_Weapon_Mk_5__TV_Guided_,
|
||||
),
|
||||
(
|
||||
Weapons.BRU_55_with_2_x_AGM_154A___JSOW_CEB__CBU_type_,
|
||||
Weapons.BRU_33_with_2_x_GBU_12___500lb_Laser_Guided_Bomb,
|
||||
Weapons.AGM_62_Walleye_II___Guided_Weapon_Mk_5__TV_Guided_,
|
||||
),
|
||||
(
|
||||
Weapons.BRU_57_with_2_x_AGM_154A___JSOW_CEB__CBU_type_,
|
||||
None,
|
||||
Weapons.AGM_62_Walleye_II___Guided_Weapon_Mk_5__TV_Guided_,
|
||||
), # doesn't exist on any aircraft yet
|
||||
(Weapons.AGM_154B___JSOW_Anti_Armour, Weapons.CBU_105___10_x_SFW__CBU_with_WCMD),
|
||||
(Weapons.AGM_154C___JSOW_Unitary_BROACH, Weapons.GBU_12),
|
||||
(
|
||||
Weapons.AGM_154C___JSOW_Unitary_BROACH,
|
||||
Weapons.AGM_62_Walleye_II___Guided_Weapon_Mk_5__TV_Guided_,
|
||||
),
|
||||
(
|
||||
Weapons.BRU_55_with_2_x_AGM_154C___JSOW_Unitary_BROACH,
|
||||
Weapons.BRU_33_with_2_x_GBU_12___500lb_Laser_Guided_Bomb,
|
||||
Weapons.AGM_62_Walleye_II___Guided_Weapon_Mk_5__TV_Guided_,
|
||||
),
|
||||
# AGM-45 Shrike
|
||||
(Weapons.AGM_45A_Shrike_ARM, None),
|
||||
|
||||
10
game/db.py
10
game/db.py
@ -498,12 +498,14 @@ PRICES = {
|
||||
Armor.IFV_BMP_1: 14,
|
||||
Armor.IFV_BMP_2: 16,
|
||||
Armor.IFV_BMP_3: 18,
|
||||
Armor.LT_PT_76: 9,
|
||||
Armor.ZBD_04A: 12,
|
||||
Armor.ZTZ_96B: 30,
|
||||
Armor.Scout_Cobra: 4,
|
||||
Armor.APC_M113: 6,
|
||||
Armor.Scout_HMMWV: 2,
|
||||
Armor.ATGM_HMMWV: 8,
|
||||
Armor.ATGM_VAB_Mephisto: 12,
|
||||
Armor.IFV_M2A2_Bradley: 12,
|
||||
Armor.IFV_M1126_Stryker_ICV: 10,
|
||||
Armor.SPG_Stryker_MGS: 14,
|
||||
@ -519,6 +521,7 @@ PRICES = {
|
||||
Armor.MBT_Merkava_IV: 25,
|
||||
Armor.APC_TPz_Fuchs: 5,
|
||||
Armor.MBT_Challenger_II: 25,
|
||||
Armor.MBT_Chieftain_Mk_3: 20,
|
||||
Armor.IFV_Marder: 10,
|
||||
Armor.IFV_Warrior: 10,
|
||||
Armor.IFV_LAV_25: 7,
|
||||
@ -534,6 +537,7 @@ PRICES = {
|
||||
Artillery.Mortar_2B11_120mm: 4,
|
||||
Artillery.SPH_Dana_vz77_152mm: 26,
|
||||
Artillery.PLZ_05: 25,
|
||||
Artillery.SPH_T155_Firtina_155mm: 28,
|
||||
Unarmed.LUV_UAZ_469_Jeep: 3,
|
||||
Unarmed.Truck_Ural_375: 3,
|
||||
Infantry.Infantry_M4: 1,
|
||||
@ -885,6 +889,7 @@ UNIT_BY_TASK = {
|
||||
Armor.IFV_BMP_3,
|
||||
Armor.IFV_BMP_3,
|
||||
Armor.IFV_BMD_1,
|
||||
Armor.LT_PT_76,
|
||||
Armor.ZBD_04A,
|
||||
Armor.ZBD_04A,
|
||||
Armor.ZBD_04A,
|
||||
@ -913,6 +918,8 @@ UNIT_BY_TASK = {
|
||||
Armor.APC_TPz_Fuchs,
|
||||
Armor.ATGM_HMMWV,
|
||||
Armor.ATGM_HMMWV,
|
||||
Armor.ATGM_VAB_Mephisto,
|
||||
Armor.ATGM_VAB_Mephisto,
|
||||
Armor.Scout_HMMWV,
|
||||
Armor.Scout_HMMWV,
|
||||
Armor.IFV_M2A2_Bradley,
|
||||
@ -941,6 +948,7 @@ UNIT_BY_TASK = {
|
||||
Armor.MBT_Leclerc,
|
||||
Armor.MBT_Leopard_2A6M,
|
||||
Armor.MBT_Challenger_II,
|
||||
Armor.MBT_Chieftain_Mk_3,
|
||||
Armor.MBT_Merkava_IV,
|
||||
Armor.MT_Pz_Kpfw_V_Panther_Ausf_G,
|
||||
Armor.Tk_PzIV_H,
|
||||
@ -1002,6 +1010,7 @@ UNIT_BY_TASK = {
|
||||
Artillery.MLRS_9K57_Uragan_BM_27_220mm,
|
||||
Artillery.MLRS_9A52_Smerch_HE_300mm,
|
||||
Artillery.SPH_Dana_vz77_152mm,
|
||||
Artillery.SPH_T155_Firtina_155mm,
|
||||
Artillery.PLZ_05,
|
||||
Artillery.SPG_M12_GMC_155mm,
|
||||
Armor.SPG_Sturmpanzer_IV_Brummbar,
|
||||
@ -1241,7 +1250,6 @@ REWARDS = {
|
||||
"fuel": 2,
|
||||
"ammo": 2,
|
||||
"farp": 1,
|
||||
"fob": 1,
|
||||
# TODO: Should generate no cash once they generate units.
|
||||
"factory": 10,
|
||||
"comms": 10,
|
||||
|
||||
16
game/game.py
16
game/game.py
@ -34,6 +34,7 @@ from .procurement import AircraftProcurementRequest, ProcurementAi
|
||||
from .profiling import logged_duration
|
||||
from .settings import Settings
|
||||
from .theater import ConflictTheater
|
||||
from .theater.bullseye import Bullseye
|
||||
from .theater.transitnetwork import TransitNetwork, TransitNetworkBuilder
|
||||
from .threatzones import ThreatZones
|
||||
from .transfers import PendingTransfers
|
||||
@ -130,6 +131,9 @@ class Game:
|
||||
self.blue_ato = AirTaskingOrder()
|
||||
self.red_ato = AirTaskingOrder()
|
||||
|
||||
self.blue_bullseye = Bullseye(Point(0, 0))
|
||||
self.red_bullseye = Bullseye(Point(0, 0))
|
||||
|
||||
self.aircraft_inventory = GlobalAircraftInventory(self.theater.controlpoints)
|
||||
|
||||
self.transfers = PendingTransfers(self)
|
||||
@ -201,6 +205,11 @@ class Game:
|
||||
return self.player_faction
|
||||
return self.enemy_faction
|
||||
|
||||
def bullseye_for(self, player: bool) -> Bullseye:
|
||||
if player:
|
||||
return self.blue_bullseye
|
||||
return self.red_bullseye
|
||||
|
||||
def _roll(self, prob, mult):
|
||||
if self.settings.version == "dev":
|
||||
# always generate all events for dev
|
||||
@ -337,10 +346,17 @@ class Game:
|
||||
|
||||
return TurnState.CONTINUE
|
||||
|
||||
def set_bullseye(self) -> None:
|
||||
player_cp, enemy_cp = self.theater.closest_opposing_control_points()
|
||||
self.blue_bullseye = Bullseye(enemy_cp.position)
|
||||
self.red_bullseye = Bullseye(player_cp.position)
|
||||
|
||||
def initialize_turn(self) -> None:
|
||||
self.events = []
|
||||
self._generate_events()
|
||||
|
||||
self.set_bullseye()
|
||||
|
||||
# Update statistics
|
||||
self.game_stats.update(self)
|
||||
|
||||
|
||||
@ -108,8 +108,12 @@ class Operation:
|
||||
|
||||
@classmethod
|
||||
def _setup_mission_coalitions(cls):
|
||||
cls.current_mission.coalition["blue"] = Coalition("blue")
|
||||
cls.current_mission.coalition["red"] = Coalition("red")
|
||||
cls.current_mission.coalition["blue"] = Coalition(
|
||||
"blue", bullseye=cls.game.blue_bullseye.to_pydcs()
|
||||
)
|
||||
cls.current_mission.coalition["red"] = Coalition(
|
||||
"red", bullseye=cls.game.red_bullseye.to_pydcs()
|
||||
)
|
||||
|
||||
p_country = cls.game.player_country
|
||||
e_country = cls.game.enemy_country
|
||||
@ -171,13 +175,16 @@ class Operation:
|
||||
gen.add_dynamic_runway(dynamic_runway)
|
||||
|
||||
for tanker in airsupportgen.air_support.tankers:
|
||||
gen.add_tanker(tanker)
|
||||
if tanker.blue:
|
||||
gen.add_tanker(tanker)
|
||||
|
||||
for aewc in airsupportgen.air_support.awacs:
|
||||
gen.add_awacs(aewc)
|
||||
if aewc.blue:
|
||||
gen.add_awacs(aewc)
|
||||
|
||||
for jtac in jtacs:
|
||||
gen.add_jtac(jtac)
|
||||
if jtac.blue:
|
||||
gen.add_jtac(jtac)
|
||||
|
||||
for flight in airgen.flights:
|
||||
gen.add_flight(flight)
|
||||
@ -317,13 +324,8 @@ class Operation:
|
||||
|
||||
# Setup combined arms parameters
|
||||
cls.current_mission.groundControl.pilot_can_control_vehicles = cls.ca_slots > 0
|
||||
if cls.game.player_country in [
|
||||
country.name
|
||||
for country in cls.current_mission.coalition["blue"].countries.values()
|
||||
]:
|
||||
cls.current_mission.groundControl.blue_tactical_commander = cls.ca_slots
|
||||
else:
|
||||
cls.current_mission.groundControl.red_tactical_commander = cls.ca_slots
|
||||
cls.current_mission.groundControl.blue_tactical_commander = cls.ca_slots
|
||||
cls.current_mission.groundControl.blue_observer = 1
|
||||
|
||||
# Options
|
||||
forcedoptionsgen = ForcedOptionsGenerator(cls.current_mission, cls.game)
|
||||
@ -453,7 +455,7 @@ class Operation:
|
||||
|
||||
for tanker in airsupportgen.air_support.tankers:
|
||||
luaData["Tankers"][tanker.callsign] = {
|
||||
"dcsGroupName": tanker.dcsGroupName,
|
||||
"dcsGroupName": tanker.group_name,
|
||||
"callsign": tanker.callsign,
|
||||
"variant": tanker.variant,
|
||||
"radio": tanker.freq.mhz,
|
||||
@ -463,14 +465,14 @@ class Operation:
|
||||
if airsupportgen.air_support.awacs:
|
||||
for awacs in airsupportgen.air_support.awacs:
|
||||
luaData["AWACs"][awacs.callsign] = {
|
||||
"dcsGroupName": awacs.dcsGroupName,
|
||||
"dcsGroupName": awacs.group_name,
|
||||
"callsign": awacs.callsign,
|
||||
"radio": awacs.freq.mhz,
|
||||
}
|
||||
|
||||
for jtac in jtacs:
|
||||
luaData["JTACs"][jtac.callsign] = {
|
||||
"dcsGroupName": jtac.dcsGroupName,
|
||||
"dcsGroupName": jtac.group_name,
|
||||
"callsign": jtac.callsign,
|
||||
"zone": jtac.region,
|
||||
"dcsUnit": jtac.unit_name,
|
||||
|
||||
26
game/theater/bullseye.py
Normal file
26
game/theater/bullseye.py
Normal file
@ -0,0 +1,26 @@
|
||||
from __future__ import annotations
|
||||
|
||||
from dataclasses import dataclass
|
||||
from typing import Dict, TYPE_CHECKING
|
||||
|
||||
from dcs import Point
|
||||
|
||||
from game.theater import LatLon
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from game.theater import ConflictTheater
|
||||
|
||||
|
||||
@dataclass
|
||||
class Bullseye:
|
||||
position: Point
|
||||
|
||||
@classmethod
|
||||
def from_pydcs(cls, bulls: Dict[str, float]) -> Bullseye:
|
||||
return cls(Point(bulls["x"], bulls["y"]))
|
||||
|
||||
def to_pydcs(self) -> Dict[str, float]:
|
||||
return {"x": self.position.x, "y": self.position.y}
|
||||
|
||||
def to_lat_lon(self, theater: ConflictTheater) -> LatLon:
|
||||
return theater.point_to_ll(self.position)
|
||||
@ -41,6 +41,7 @@ from dcs.unitgroup import (
|
||||
)
|
||||
from dcs.vehicles import AirDefence, Armor, MissilesSS, Unarmed
|
||||
|
||||
from .latlon import LatLon
|
||||
from ..helipad import Helipad
|
||||
from ..scenery_group import SceneryGroup
|
||||
from pyproj import CRS, Transformer
|
||||
@ -583,15 +584,6 @@ class ReferencePoint:
|
||||
image_coordinates: Point
|
||||
|
||||
|
||||
@dataclass(frozen=True)
|
||||
class LatLon:
|
||||
latitude: float
|
||||
longitude: float
|
||||
|
||||
def as_list(self) -> List[float]:
|
||||
return [self.latitude, self.longitude]
|
||||
|
||||
|
||||
class ConflictTheater:
|
||||
terrain: Terrain
|
||||
|
||||
|
||||
11
game/theater/latlon.py
Normal file
11
game/theater/latlon.py
Normal file
@ -0,0 +1,11 @@
|
||||
from dataclasses import dataclass
|
||||
from typing import List
|
||||
|
||||
|
||||
@dataclass(frozen=True)
|
||||
class LatLon:
|
||||
latitude: float
|
||||
longitude: float
|
||||
|
||||
def as_list(self) -> List[float]:
|
||||
return [self.latitude, self.longitude]
|
||||
@ -845,12 +845,7 @@ class FobGroundObjectGenerator(AirbaseGroundObjectGenerator):
|
||||
return True
|
||||
|
||||
def generate_fob(self) -> None:
|
||||
try:
|
||||
category = self.faction.building_set[self.faction.building_set.index("fob")]
|
||||
except IndexError:
|
||||
logging.exception("Faction has no fob buildings defined")
|
||||
return
|
||||
|
||||
category = "fob"
|
||||
obj_name = self.control_point.name
|
||||
template = random.choice(list(self.templates[category].values()))
|
||||
point = self.control_point.position
|
||||
|
||||
@ -815,12 +815,13 @@ class AircraftConflictGenerator:
|
||||
|
||||
self.air_support.awacs.append(
|
||||
AwacsInfo(
|
||||
dcsGroupName=str(group.name),
|
||||
group_name=str(group.name),
|
||||
callsign=callsign,
|
||||
freq=channel,
|
||||
depature_location=flight.departure.name,
|
||||
end_time=flight.flight_plan.mission_departure_time,
|
||||
start_time=flight.flight_plan.mission_start_time,
|
||||
blue=flight.departure.captured,
|
||||
)
|
||||
)
|
||||
|
||||
@ -1275,7 +1276,7 @@ class AircraftConflictGenerator:
|
||||
group,
|
||||
react_on_threat=OptReactOnThreat.Values.EvadeFire,
|
||||
roe=OptROE.Values.OpenFire,
|
||||
rtb_winchester=OptRTBOnOutOfAmmo.Values.ASM,
|
||||
rtb_winchester=OptRTBOnOutOfAmmo.Values.All,
|
||||
restrict_jettison=True,
|
||||
)
|
||||
|
||||
@ -1410,9 +1411,6 @@ class AircraftConflictGenerator:
|
||||
flight: Flight,
|
||||
dynamic_runways: Dict[str, RunwayData],
|
||||
) -> None:
|
||||
# Escort groups are actually given the CAP task so they can perform the
|
||||
# Search Then Engage task, which we have to use instead of the Escort
|
||||
# task for the reasons explained in JoinPointBuilder.
|
||||
group.task = Transport.name
|
||||
self._setup_group(group, package, flight, dynamic_runways)
|
||||
self.configure_behavior(
|
||||
@ -1761,7 +1759,7 @@ class DeadIngressBuilder(PydcsWaypointBuilder):
|
||||
if isinstance(target_group, TheaterGroundObject):
|
||||
tgroup = self.mission.find_group(target_group.group_name)
|
||||
if tgroup is not None:
|
||||
task = AttackGroup(tgroup.id, weapon_type=WeaponType.Guided)
|
||||
task = AttackGroup(tgroup.id, weapon_type=WeaponType.Auto)
|
||||
task.params["expend"] = "All"
|
||||
task.params["attackQtyLimit"] = False
|
||||
task.params["directionEnabled"] = False
|
||||
@ -1866,12 +1864,11 @@ class StrikeIngressBuilder(PydcsWaypointBuilder):
|
||||
center.y += target.position.y
|
||||
center.x /= len(targets)
|
||||
center.y /= len(targets)
|
||||
bombing = Bombing(center)
|
||||
bombing = Bombing(center, weapon_type=WeaponType.Bombs)
|
||||
bombing.params["expend"] = "All"
|
||||
bombing.params["attackQtyLimit"] = False
|
||||
bombing.params["directionEnabled"] = False
|
||||
bombing.params["altitudeEnabled"] = False
|
||||
bombing.params["weaponType"] = WeaponType.Bombs.value
|
||||
bombing.params["groupAttack"] = True
|
||||
waypoint.tasks.append(bombing)
|
||||
return waypoint
|
||||
@ -1879,11 +1876,10 @@ class StrikeIngressBuilder(PydcsWaypointBuilder):
|
||||
def build_strike(self) -> MovingPoint:
|
||||
waypoint = super().build()
|
||||
for target in self.waypoint.targets:
|
||||
bombing = Bombing(target.position)
|
||||
bombing = Bombing(target.position, weapon_type=WeaponType.Auto)
|
||||
# If there is only one target, drop all ordnance in one pass.
|
||||
if len(self.waypoint.targets) == 1:
|
||||
bombing.params["expend"] = "All"
|
||||
bombing.params["weaponType"] = WeaponType.Auto.value
|
||||
bombing.params["groupAttack"] = True
|
||||
waypoint.tasks.append(bombing)
|
||||
|
||||
@ -1954,7 +1950,10 @@ class JoinPointBuilder(PydcsWaypointBuilder):
|
||||
EngageTargets(
|
||||
# TODO: From doctrine.
|
||||
max_distance=int(nautical_miles(30).meters),
|
||||
targets=[Targets.All.Air.Planes.Fighters],
|
||||
targets=[
|
||||
Targets.All.Air.Planes.Fighters,
|
||||
Targets.All.Air.Planes.MultiroleFighters,
|
||||
],
|
||||
)
|
||||
)
|
||||
)
|
||||
|
||||
@ -35,23 +35,25 @@ AWACS_ALT = 13000
|
||||
class AwacsInfo:
|
||||
"""AWACS information for the kneeboard."""
|
||||
|
||||
dcsGroupName: str
|
||||
group_name: str
|
||||
callsign: str
|
||||
freq: RadioFrequency
|
||||
depature_location: Optional[str]
|
||||
start_time: Optional[timedelta]
|
||||
end_time: Optional[timedelta]
|
||||
blue: bool
|
||||
|
||||
|
||||
@dataclass
|
||||
class TankerInfo:
|
||||
"""Tanker information for the kneeboard."""
|
||||
|
||||
dcsGroupName: str
|
||||
group_name: str
|
||||
callsign: str
|
||||
variant: str
|
||||
freq: RadioFrequency
|
||||
tacan: TacanChannel
|
||||
blue: bool
|
||||
|
||||
|
||||
@dataclass
|
||||
@ -165,7 +167,14 @@ class AirSupportConflictGenerator:
|
||||
tanker_group.points[0].tasks.append(SetImmortalCommand(True))
|
||||
|
||||
self.air_support.tankers.append(
|
||||
TankerInfo(str(tanker_group.name), callsign, variant, freq, tacan)
|
||||
TankerInfo(
|
||||
str(tanker_group.name),
|
||||
callsign,
|
||||
variant,
|
||||
freq,
|
||||
tacan,
|
||||
blue=True,
|
||||
)
|
||||
)
|
||||
|
||||
if not self.game.settings.disable_legacy_aewc:
|
||||
@ -196,12 +205,13 @@ class AirSupportConflictGenerator:
|
||||
|
||||
self.air_support.awacs.append(
|
||||
AwacsInfo(
|
||||
dcsGroupName=str(awacs_flight.name),
|
||||
group_name=str(awacs_flight.name),
|
||||
callsign=callsign_for_support_unit(awacs_flight),
|
||||
freq=freq,
|
||||
depature_location=None,
|
||||
start_time=None,
|
||||
end_time=None,
|
||||
blue=True,
|
||||
)
|
||||
)
|
||||
else:
|
||||
|
||||
12
gen/armor.py
12
gen/armor.py
@ -68,11 +68,12 @@ INFANTRY_GROUP_SIZE = 5
|
||||
class JtacInfo:
|
||||
"""JTAC information."""
|
||||
|
||||
dcsGroupName: str
|
||||
group_name: str
|
||||
unit_name: str
|
||||
callsign: str
|
||||
region: str
|
||||
code: str
|
||||
blue: bool
|
||||
# TODO: Radio info? Type?
|
||||
|
||||
|
||||
@ -196,7 +197,14 @@ class GroundConflictGenerator:
|
||||
# Note: Will need to change if we ever add ground based JTAC.
|
||||
callsign = callsign_for_support_unit(jtac)
|
||||
self.jtacs.append(
|
||||
JtacInfo(str(jtac.name), n, callsign, frontline, str(code))
|
||||
JtacInfo(
|
||||
str(jtac.name),
|
||||
n,
|
||||
callsign,
|
||||
frontline,
|
||||
str(code),
|
||||
blue=True,
|
||||
)
|
||||
)
|
||||
|
||||
def gen_infantry_group_for_group(
|
||||
|
||||
12
gen/fleet/lacombattanteII.py
Normal file
12
gen/fleet/lacombattanteII.py
Normal file
@ -0,0 +1,12 @@
|
||||
from dcs.ships import FAC_La_Combattante_IIa
|
||||
|
||||
from game.factions.faction import Faction
|
||||
from game.theater import TheaterGroundObject
|
||||
from gen.fleet.dd_group import DDGroupGenerator
|
||||
|
||||
|
||||
class LaCombattanteIIGroupGenerator(DDGroupGenerator):
|
||||
def __init__(self, game, ground_object: TheaterGroundObject, faction: Faction):
|
||||
super(LaCombattanteIIGroupGenerator, self).__init__(
|
||||
game, ground_object, faction, FAC_La_Combattante_IIa
|
||||
)
|
||||
@ -8,6 +8,7 @@ from gen.fleet.dd_group import (
|
||||
ArleighBurkeGroupGenerator,
|
||||
OliverHazardPerryGroupGenerator,
|
||||
)
|
||||
from gen.fleet.lacombattanteII import LaCombattanteIIGroupGenerator
|
||||
from gen.fleet.lha_group import LHAGroupGenerator
|
||||
from gen.fleet.ru_dd_group import (
|
||||
RussianNavyGroupGenerator,
|
||||
@ -34,6 +35,7 @@ SHIP_MAP = {
|
||||
"KiloSubGroupGenerator": KiloSubGroupGenerator,
|
||||
"TangoSubGroupGenerator": TangoSubGroupGenerator,
|
||||
"Type54GroupGenerator": Type54GroupGenerator,
|
||||
"LaCombattanteIIGroupGenerator": LaCombattanteIIGroupGenerator,
|
||||
}
|
||||
|
||||
|
||||
|
||||
@ -132,7 +132,6 @@ CAP_CAPABLE = [
|
||||
MiG_29A,
|
||||
F_16C_50,
|
||||
FA_18C_hornet,
|
||||
F_15E,
|
||||
F_16A,
|
||||
F_4E,
|
||||
JF_17,
|
||||
@ -140,6 +139,7 @@ CAP_CAPABLE = [
|
||||
MiG_21Bis,
|
||||
Mirage_2000_5,
|
||||
M_2000C,
|
||||
F_15E,
|
||||
F_5E_3,
|
||||
MiG_19P,
|
||||
A_4E_C,
|
||||
|
||||
@ -79,6 +79,7 @@ class FlightWaypointType(Enum):
|
||||
INGRESS_OCA_AIRCRAFT = 25
|
||||
PICKUP = 26
|
||||
DROP_OFF = 27
|
||||
BULLSEYE = 28
|
||||
|
||||
|
||||
class FlightWaypoint:
|
||||
|
||||
@ -426,6 +426,7 @@ class BarCapFlightPlan(PatrollingFlightPlan):
|
||||
takeoff: FlightWaypoint
|
||||
land: FlightWaypoint
|
||||
divert: Optional[FlightWaypoint]
|
||||
bullseye: FlightWaypoint
|
||||
|
||||
def iter_waypoints(self) -> Iterator[FlightWaypoint]:
|
||||
yield self.takeoff
|
||||
@ -438,6 +439,7 @@ class BarCapFlightPlan(PatrollingFlightPlan):
|
||||
yield self.land
|
||||
if self.divert is not None:
|
||||
yield self.divert
|
||||
yield self.bullseye
|
||||
|
||||
|
||||
@dataclass(frozen=True)
|
||||
@ -446,6 +448,7 @@ class CasFlightPlan(PatrollingFlightPlan):
|
||||
target: FlightWaypoint
|
||||
land: FlightWaypoint
|
||||
divert: Optional[FlightWaypoint]
|
||||
bullseye: FlightWaypoint
|
||||
|
||||
def iter_waypoints(self) -> Iterator[FlightWaypoint]:
|
||||
yield self.takeoff
|
||||
@ -459,6 +462,7 @@ class CasFlightPlan(PatrollingFlightPlan):
|
||||
yield self.land
|
||||
if self.divert is not None:
|
||||
yield self.divert
|
||||
yield self.bullseye
|
||||
|
||||
def request_escort_at(self) -> Optional[FlightWaypoint]:
|
||||
return self.patrol_start
|
||||
@ -472,6 +476,7 @@ class TarCapFlightPlan(PatrollingFlightPlan):
|
||||
takeoff: FlightWaypoint
|
||||
land: FlightWaypoint
|
||||
divert: Optional[FlightWaypoint]
|
||||
bullseye: FlightWaypoint
|
||||
lead_time: timedelta
|
||||
|
||||
def iter_waypoints(self) -> Iterator[FlightWaypoint]:
|
||||
@ -485,6 +490,7 @@ class TarCapFlightPlan(PatrollingFlightPlan):
|
||||
yield self.land
|
||||
if self.divert is not None:
|
||||
yield self.divert
|
||||
yield self.bullseye
|
||||
|
||||
@property
|
||||
def tot_offset(self) -> timedelta:
|
||||
@ -523,6 +529,7 @@ class StrikeFlightPlan(FormationFlightPlan):
|
||||
nav_from: List[FlightWaypoint]
|
||||
land: FlightWaypoint
|
||||
divert: Optional[FlightWaypoint]
|
||||
bullseye: FlightWaypoint
|
||||
|
||||
def iter_waypoints(self) -> Iterator[FlightWaypoint]:
|
||||
yield self.takeoff
|
||||
@ -537,6 +544,7 @@ class StrikeFlightPlan(FormationFlightPlan):
|
||||
yield self.land
|
||||
if self.divert is not None:
|
||||
yield self.divert
|
||||
yield self.bullseye
|
||||
|
||||
@property
|
||||
def package_speed_waypoints(self) -> Set[FlightWaypoint]:
|
||||
@ -641,6 +649,7 @@ class SweepFlightPlan(LoiterFlightPlan):
|
||||
nav_from: List[FlightWaypoint]
|
||||
land: FlightWaypoint
|
||||
divert: Optional[FlightWaypoint]
|
||||
bullseye: FlightWaypoint
|
||||
lead_time: timedelta
|
||||
|
||||
def iter_waypoints(self) -> Iterator[FlightWaypoint]:
|
||||
@ -653,6 +662,7 @@ class SweepFlightPlan(LoiterFlightPlan):
|
||||
yield self.land
|
||||
if self.divert is not None:
|
||||
yield self.divert
|
||||
yield self.bullseye
|
||||
|
||||
@property
|
||||
def tot_waypoint(self) -> Optional[FlightWaypoint]:
|
||||
@ -704,6 +714,7 @@ class AwacsFlightPlan(LoiterFlightPlan):
|
||||
nav_from: List[FlightWaypoint]
|
||||
land: FlightWaypoint
|
||||
divert: Optional[FlightWaypoint]
|
||||
bullseye: FlightWaypoint
|
||||
|
||||
def iter_waypoints(self) -> Iterator[FlightWaypoint]:
|
||||
yield self.takeoff
|
||||
@ -713,6 +724,7 @@ class AwacsFlightPlan(LoiterFlightPlan):
|
||||
yield self.land
|
||||
if self.divert is not None:
|
||||
yield self.divert
|
||||
yield self.bullseye
|
||||
|
||||
@property
|
||||
def mission_start_time(self) -> Optional[timedelta]:
|
||||
@ -746,6 +758,7 @@ class AirliftFlightPlan(FlightPlan):
|
||||
nav_to_home: List[FlightWaypoint]
|
||||
land: FlightWaypoint
|
||||
divert: Optional[FlightWaypoint]
|
||||
bullseye: FlightWaypoint
|
||||
|
||||
def iter_waypoints(self) -> Iterator[FlightWaypoint]:
|
||||
yield self.takeoff
|
||||
@ -758,6 +771,7 @@ class AirliftFlightPlan(FlightPlan):
|
||||
yield self.land
|
||||
if self.divert is not None:
|
||||
yield self.divert
|
||||
yield self.bullseye
|
||||
|
||||
@property
|
||||
def tot_waypoint(self) -> Optional[FlightWaypoint]:
|
||||
@ -1053,6 +1067,7 @@ class FlightPlanBuilder:
|
||||
),
|
||||
land=builder.land(flight.arrival),
|
||||
divert=builder.divert(flight.divert),
|
||||
bullseye=builder.bullseye(),
|
||||
hold=start,
|
||||
hold_duration=timedelta(hours=4),
|
||||
)
|
||||
@ -1151,6 +1166,7 @@ class FlightPlanBuilder:
|
||||
patrol_end=end,
|
||||
land=builder.land(flight.arrival),
|
||||
divert=builder.divert(flight.divert),
|
||||
bullseye=builder.bullseye(),
|
||||
)
|
||||
|
||||
def generate_sweep(self, flight: Flight) -> SweepFlightPlan:
|
||||
@ -1187,6 +1203,7 @@ class FlightPlanBuilder:
|
||||
sweep_end=end,
|
||||
land=builder.land(flight.arrival),
|
||||
divert=builder.divert(flight.divert),
|
||||
bullseye=builder.bullseye(),
|
||||
)
|
||||
|
||||
def generate_transport(self, flight: Flight) -> AirliftFlightPlan:
|
||||
@ -1238,6 +1255,7 @@ class FlightPlanBuilder:
|
||||
),
|
||||
land=builder.land(flight.arrival),
|
||||
divert=builder.divert(flight.divert),
|
||||
bullseye=builder.bullseye(),
|
||||
)
|
||||
|
||||
def racetrack_for_objective(
|
||||
@ -1389,6 +1407,7 @@ class FlightPlanBuilder:
|
||||
patrol_end=end,
|
||||
land=builder.land(flight.arrival),
|
||||
divert=builder.divert(flight.divert),
|
||||
bullseye=builder.bullseye(),
|
||||
)
|
||||
|
||||
def generate_dead(
|
||||
@ -1517,6 +1536,7 @@ class FlightPlanBuilder:
|
||||
),
|
||||
land=builder.land(flight.arrival),
|
||||
divert=builder.divert(flight.divert),
|
||||
bullseye=builder.bullseye(),
|
||||
)
|
||||
|
||||
def generate_cas(self, flight: Flight) -> CasFlightPlan:
|
||||
@ -1562,6 +1582,7 @@ class FlightPlanBuilder:
|
||||
patrol_end=builder.egress(egress, location),
|
||||
land=builder.land(flight.arrival),
|
||||
divert=builder.divert(flight.divert),
|
||||
bullseye=builder.bullseye(),
|
||||
)
|
||||
|
||||
@staticmethod
|
||||
@ -1696,6 +1717,7 @@ class FlightPlanBuilder:
|
||||
),
|
||||
land=builder.land(flight.arrival),
|
||||
divert=builder.divert(flight.divert),
|
||||
bullseye=builder.bullseye(),
|
||||
)
|
||||
|
||||
def _retreating_rendezvous_point(self, attack_transition: Point) -> Point:
|
||||
|
||||
@ -35,6 +35,9 @@ class Loadout:
|
||||
|
||||
new_pylons = dict(self.pylons)
|
||||
for pylon_number, weapon in self.pylons.items():
|
||||
if weapon is None:
|
||||
del new_pylons[pylon_number]
|
||||
continue
|
||||
if not weapon.available_on(date):
|
||||
pylon = Pylon.for_aircraft(unit_type, pylon_number)
|
||||
for fallback in weapon.fallbacks:
|
||||
@ -44,6 +47,8 @@ class Loadout:
|
||||
continue
|
||||
new_pylons[pylon_number] = fallback
|
||||
break
|
||||
else:
|
||||
del new_pylons[pylon_number]
|
||||
return Loadout(f"{self.name} ({date.year})", new_pylons, date)
|
||||
|
||||
@classmethod
|
||||
|
||||
@ -50,6 +50,7 @@ class WaypointBuilder:
|
||||
self.threat_zones = game.threat_zone_for(not player)
|
||||
self.navmesh = game.navmesh_for(player)
|
||||
self.targets = targets
|
||||
self._bullseye = game.bullseye_for(player)
|
||||
|
||||
@property
|
||||
def is_helo(self) -> bool:
|
||||
@ -145,6 +146,19 @@ class WaypointBuilder:
|
||||
waypoint.only_for_player = True
|
||||
return waypoint
|
||||
|
||||
def bullseye(self) -> FlightWaypoint:
|
||||
waypoint = FlightWaypoint(
|
||||
FlightWaypointType.BULLSEYE,
|
||||
self._bullseye.position.x,
|
||||
self._bullseye.position.y,
|
||||
meters(0),
|
||||
)
|
||||
waypoint.pretty_name = "Bullseye"
|
||||
waypoint.description = "Bullseye"
|
||||
waypoint.name = "BULLSEYE"
|
||||
waypoint.only_for_player = True
|
||||
return waypoint
|
||||
|
||||
def hold(self, position: Point) -> FlightWaypoint:
|
||||
waypoint = FlightWaypoint(
|
||||
FlightWaypointType.LOITER,
|
||||
|
||||
@ -15,10 +15,12 @@ TYPE_TANKS = [
|
||||
Armor.MBT_Leopard_1A3,
|
||||
Armor.MBT_Leclerc,
|
||||
Armor.MBT_Challenger_II,
|
||||
Armor.MBT_Chieftain_Mk_3,
|
||||
Armor.MBT_M1A2_Abrams,
|
||||
Armor.MBT_M60A3_Patton,
|
||||
Armor.MBT_Merkava_IV,
|
||||
Armor.ZTZ_96B,
|
||||
Armor.LT_PT_76,
|
||||
# WW2
|
||||
Armor.MT_Pz_Kpfw_V_Panther_Ausf_G,
|
||||
Armor.Tk_PzIV_H,
|
||||
@ -45,6 +47,7 @@ TYPE_TANKS = [
|
||||
|
||||
TYPE_ATGM = [
|
||||
Armor.ATGM_HMMWV,
|
||||
Armor.ATGM_VAB_Mephisto,
|
||||
Armor.ATGM_Stryker,
|
||||
Armor.IFV_BMP_2,
|
||||
# WW2 (Tank Destroyers)
|
||||
@ -114,6 +117,7 @@ TYPE_ARTILLERY = [
|
||||
Artillery.MLRS_M270_227mm,
|
||||
Artillery.SPM_2S9_Nona_120mm_M,
|
||||
Artillery.SPH_Dana_vz77_152mm,
|
||||
Artillery.SPH_T155_Firtina_155mm,
|
||||
Artillery.PLZ_05,
|
||||
Artillery.SPH_2S19_Msta_152mm,
|
||||
Artillery.MLRS_9A52_Smerch_CM_300mm,
|
||||
|
||||
144
gen/kneeboard.py
144
gen/kneeboard.py
@ -23,6 +23,7 @@ only be added per airframe, so PvP missions where each side have the same
|
||||
aircraft will be able to see the enemy's kneeboard for the same airframe.
|
||||
"""
|
||||
import datetime
|
||||
import textwrap
|
||||
from collections import defaultdict
|
||||
from dataclasses import dataclass
|
||||
from pathlib import Path
|
||||
@ -37,6 +38,7 @@ from tabulate import tabulate
|
||||
from game.data.alic import AlicCodes
|
||||
from game.db import find_unittype, unit_type_from_name
|
||||
from game.theater import ConflictTheater, TheaterGroundObject, LatLon
|
||||
from game.theater.bullseye import Bullseye
|
||||
from game.utils import meters
|
||||
from .aircraft import AIRCRAFT_DATA, FlightData
|
||||
from .airsupportgen import AwacsInfo, TankerInfo
|
||||
@ -257,21 +259,16 @@ class BriefingPage(KneeboardPage):
|
||||
def __init__(
|
||||
self,
|
||||
flight: FlightData,
|
||||
comms: List[CommInfo],
|
||||
awacs: List[AwacsInfo],
|
||||
tankers: List[TankerInfo],
|
||||
jtacs: List[JtacInfo],
|
||||
bullseye: Bullseye,
|
||||
theater: ConflictTheater,
|
||||
start_time: datetime.datetime,
|
||||
dark_kneeboard: bool,
|
||||
) -> None:
|
||||
self.flight = flight
|
||||
self.comms = list(comms)
|
||||
self.awacs = awacs
|
||||
self.tankers = tankers
|
||||
self.jtacs = jtacs
|
||||
self.bullseye = bullseye
|
||||
self.theater = theater
|
||||
self.start_time = start_time
|
||||
self.dark_kneeboard = dark_kneeboard
|
||||
self.comms.append(CommInfo("Flight", self.flight.intra_flight_channel))
|
||||
|
||||
def write(self, path: Path) -> None:
|
||||
writer = KneeboardPageWriter(dark_theme=self.dark_kneeboard)
|
||||
@ -301,6 +298,10 @@ class BriefingPage(KneeboardPage):
|
||||
headers=["#", "Action", "Alt", "Dist", "GSPD", "Time", "Departure"],
|
||||
)
|
||||
|
||||
writer.text(
|
||||
f"Bullseye: {self.format_ll(self.bullseye.to_lat_lon(self.theater))}"
|
||||
)
|
||||
|
||||
writer.table(
|
||||
[
|
||||
[
|
||||
@ -311,6 +312,86 @@ class BriefingPage(KneeboardPage):
|
||||
["Bingo", "Joker"],
|
||||
)
|
||||
|
||||
writer.write(path)
|
||||
|
||||
def airfield_info_row(
|
||||
self, row_title: str, runway: Optional[RunwayData]
|
||||
) -> List[str]:
|
||||
"""Creates a table row for a given airfield.
|
||||
|
||||
Args:
|
||||
row_title: Purpose of the airfield. e.g. "Departure", "Arrival" or
|
||||
"Divert".
|
||||
runway: The runway described by this row.
|
||||
|
||||
Returns:
|
||||
A list of strings to be used as a row of the airfield table.
|
||||
"""
|
||||
if runway is None:
|
||||
return [row_title, "", "", "", "", ""]
|
||||
|
||||
atc = ""
|
||||
if runway.atc is not None:
|
||||
atc = self.format_frequency(runway.atc)
|
||||
if runway.tacan is None:
|
||||
tacan = ""
|
||||
else:
|
||||
tacan = str(runway.tacan)
|
||||
if runway.ils is not None:
|
||||
ils = str(runway.ils)
|
||||
elif runway.icls is not None:
|
||||
ils = str(runway.icls)
|
||||
else:
|
||||
ils = ""
|
||||
return [
|
||||
row_title,
|
||||
"\n".join(textwrap.wrap(runway.airfield_name, width=24)),
|
||||
atc,
|
||||
tacan,
|
||||
ils,
|
||||
runway.runway_name,
|
||||
]
|
||||
|
||||
def format_frequency(self, frequency: RadioFrequency) -> str:
|
||||
channel = self.flight.channel_for(frequency)
|
||||
if channel is None:
|
||||
return str(frequency)
|
||||
|
||||
namer = AIRCRAFT_DATA[self.flight.aircraft_type.id].channel_namer
|
||||
channel_name = namer.channel_name(channel.radio_id, channel.channel)
|
||||
return f"{channel_name}\n{frequency}"
|
||||
|
||||
|
||||
class SupportPage(KneeboardPage):
|
||||
"""A kneeboard page containing information about support units."""
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
flight: FlightData,
|
||||
comms: List[CommInfo],
|
||||
awacs: List[AwacsInfo],
|
||||
tankers: List[TankerInfo],
|
||||
jtacs: List[JtacInfo],
|
||||
start_time: datetime.datetime,
|
||||
dark_kneeboard: bool,
|
||||
) -> None:
|
||||
self.flight = flight
|
||||
self.comms = list(comms)
|
||||
self.awacs = awacs
|
||||
self.tankers = tankers
|
||||
self.jtacs = jtacs
|
||||
self.start_time = start_time
|
||||
self.dark_kneeboard = dark_kneeboard
|
||||
self.comms.append(CommInfo("Flight", self.flight.intra_flight_channel))
|
||||
|
||||
def write(self, path: Path) -> None:
|
||||
writer = KneeboardPageWriter(dark_theme=self.dark_kneeboard)
|
||||
if self.flight.custom_name is not None:
|
||||
custom_name_title = ' ("{}")'.format(self.flight.custom_name)
|
||||
else:
|
||||
custom_name_title = ""
|
||||
writer.title(f"{self.flight.callsign} Support Info{custom_name_title}")
|
||||
|
||||
# AEW&C
|
||||
writer.heading("AEW&C")
|
||||
aewc_ladder = []
|
||||
@ -368,44 +449,6 @@ class BriefingPage(KneeboardPage):
|
||||
|
||||
writer.write(path)
|
||||
|
||||
def airfield_info_row(
|
||||
self, row_title: str, runway: Optional[RunwayData]
|
||||
) -> List[str]:
|
||||
"""Creates a table row for a given airfield.
|
||||
|
||||
Args:
|
||||
row_title: Purpose of the airfield. e.g. "Departure", "Arrival" or
|
||||
"Divert".
|
||||
runway: The runway described by this row.
|
||||
|
||||
Returns:
|
||||
A list of strings to be used as a row of the airfield table.
|
||||
"""
|
||||
if runway is None:
|
||||
return [row_title, "", "", "", "", ""]
|
||||
|
||||
atc = ""
|
||||
if runway.atc is not None:
|
||||
atc = self.format_frequency(runway.atc)
|
||||
if runway.tacan is None:
|
||||
tacan = ""
|
||||
else:
|
||||
tacan = str(runway.tacan)
|
||||
if runway.ils is not None:
|
||||
ils = str(runway.ils)
|
||||
elif runway.icls is not None:
|
||||
ils = str(runway.icls)
|
||||
else:
|
||||
ils = ""
|
||||
return [
|
||||
row_title,
|
||||
runway.airfield_name,
|
||||
atc,
|
||||
tacan,
|
||||
ils,
|
||||
runway.runway_name,
|
||||
]
|
||||
|
||||
def format_frequency(self, frequency: RadioFrequency) -> str:
|
||||
channel = self.flight.channel_for(frequency)
|
||||
if channel is None:
|
||||
@ -558,6 +601,13 @@ class KneeboardGenerator(MissionInfoGenerator):
|
||||
"""Returns a list of kneeboard pages for the given flight."""
|
||||
pages: List[KneeboardPage] = [
|
||||
BriefingPage(
|
||||
flight,
|
||||
self.game.bullseye_for(flight.friendly),
|
||||
self.game.theater,
|
||||
self.mission.start_time,
|
||||
self.dark_kneeboard,
|
||||
),
|
||||
SupportPage(
|
||||
flight,
|
||||
self.comms,
|
||||
self.awacs,
|
||||
|
||||
@ -209,16 +209,6 @@ class TriggersGenerator:
|
||||
player_coalition = "blue"
|
||||
enemy_coalition = "red"
|
||||
|
||||
player_cp, enemy_cp = self.game.theater.closest_opposing_control_points()
|
||||
self.mission.coalition["blue"].bullseye = {
|
||||
"x": enemy_cp.position.x,
|
||||
"y": enemy_cp.position.y,
|
||||
}
|
||||
self.mission.coalition["red"].bullseye = {
|
||||
"x": player_cp.position.x,
|
||||
"y": player_cp.position.y,
|
||||
}
|
||||
|
||||
self._set_skill(player_coalition, enemy_coalition)
|
||||
self._set_allegiances(player_coalition, enemy_coalition)
|
||||
self._gen_markers()
|
||||
|
||||
2
pydcs
2
pydcs
@ -1 +1 @@
|
||||
Subproject commit dec648d27f74c394dd6e85e83cc09e4cd823653d
|
||||
Subproject commit 4972988c978f2057e7aa06919c4de71ee9a06ea5
|
||||
@ -375,6 +375,7 @@ class WaypointJs(QObject):
|
||||
timingChanged = Signal()
|
||||
isTakeoffChanged = Signal()
|
||||
isDivertChanged = Signal()
|
||||
isBullseyeChanged = Signal()
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
@ -439,6 +440,10 @@ class WaypointJs(QObject):
|
||||
def isDivert(self) -> bool:
|
||||
return self.waypoint.waypoint_type is FlightWaypointType.DIVERT
|
||||
|
||||
@Property(bool, notify=isBullseyeChanged)
|
||||
def isBullseye(self) -> bool:
|
||||
return self.waypoint.waypoint_type is FlightWaypointType.BULLSEYE
|
||||
|
||||
@Slot(list, result=str)
|
||||
def setPosition(self, position: LeafletLatLon) -> str:
|
||||
point = self.theater.ll_to_point(LatLon(*position))
|
||||
|
||||
@ -47,7 +47,8 @@
|
||||
"MBT_Challenger_II",
|
||||
"MBT_M60A3_Patton",
|
||||
"SPG_Stryker_MGS",
|
||||
"SAM_Avenger__Stinger"
|
||||
"SAM_Avenger__Stinger",
|
||||
"ATGM_VAB_Mephisto"
|
||||
],
|
||||
"artillery_units": [
|
||||
"MLRS_M270_227mm",
|
||||
|
||||
@ -46,7 +46,8 @@
|
||||
"Scout_HMMWV",
|
||||
"ATGM_HMMWV",
|
||||
"SAM_Linebacker___Bradley_M6",
|
||||
"SAM_Avenger__Stinger"
|
||||
"SAM_Avenger__Stinger",
|
||||
"ATGM_VAB_Mephisto"
|
||||
],
|
||||
"artillery_units": [
|
||||
"MLRS_M270_227mm",
|
||||
|
||||
@ -23,13 +23,10 @@
|
||||
"ERC_90",
|
||||
"TRM_2000_PAMELA",
|
||||
"VAB__50",
|
||||
"VAB_MEPHISTO",
|
||||
"VAB_T20_13",
|
||||
"ATGM_VAB_Mephisto",
|
||||
"VAB_T20_13",
|
||||
"VBL__50",
|
||||
"VBL_AANF1",
|
||||
"VBAE_CRAB",
|
||||
"VBAE_CRAB_MMP",
|
||||
"AMX_30B2",
|
||||
"SAM_Roland_ADS"
|
||||
],
|
||||
|
||||
@ -21,10 +21,8 @@
|
||||
"MBT_Leclerc",
|
||||
"APC_TPz_Fuchs",
|
||||
"Scout_Cobra",
|
||||
"ATGM_Stryker",
|
||||
"IFV_LAV_25",
|
||||
"Scout_HMMWV",
|
||||
"ATGM_HMMWV",
|
||||
"ATGM_VAB_Mephisto",
|
||||
"SAM_Roland_ADS"
|
||||
],
|
||||
"artillery_units": [
|
||||
|
||||
@ -24,7 +24,6 @@
|
||||
"ERC_90",
|
||||
"TRM_2000_PAMELA",
|
||||
"VAB__50",
|
||||
"VAB_MEPHISTO",
|
||||
"VAB_T20_13",
|
||||
"VAB_T20_13",
|
||||
"VBL__50",
|
||||
@ -33,7 +32,8 @@
|
||||
"VBAE_CRAB_MMP",
|
||||
"AMX_30B2",
|
||||
"Leclerc_Serie_XXI",
|
||||
"SAM_Roland_ADS"
|
||||
"SAM_Roland_ADS",
|
||||
"ATGM_VAB_Mephisto"
|
||||
],
|
||||
"artillery_units": [
|
||||
"MLRS_M270_227mm",
|
||||
|
||||
@ -45,6 +45,9 @@
|
||||
"ZU23Generator",
|
||||
"ZU23UralGenerator"
|
||||
],
|
||||
"navy_generators": [
|
||||
"LaCombattanteIIGroupGenerator"
|
||||
],
|
||||
"requirements": {},
|
||||
"has_jtac": true,
|
||||
"jtac_unit": "MQ_9_Reaper"
|
||||
|
||||
@ -61,7 +61,8 @@
|
||||
"helicopter_carrier_names": [
|
||||
],
|
||||
"navy_generators": [
|
||||
"OliverHazardPerryGroupGenerator"
|
||||
"OliverHazardPerryGroupGenerator",
|
||||
"LaCombattanteIIGroupGenerator"
|
||||
],
|
||||
"has_jtac": true,
|
||||
"jtac_unit": "MQ_9_Reaper"
|
||||
|
||||
@ -46,6 +46,9 @@
|
||||
"HawkEwrGenerator",
|
||||
"FlatFaceGenerator"
|
||||
],
|
||||
"navy_generators": [
|
||||
"LaCombattanteIIGroupGenerator"
|
||||
],
|
||||
"has_jtac": true,
|
||||
"jtac_unit": "MQ_9_Reaper"
|
||||
}
|
||||
|
||||
@ -9,6 +9,7 @@
|
||||
"Scout_Cobra",
|
||||
"APC_MTLB",
|
||||
"Scout_BRDM_2",
|
||||
"LT_PT_76",
|
||||
"SPAAA_ZU_23_2_Insurgent_Mounted_Ural_375"
|
||||
],
|
||||
"artillery_units": [
|
||||
|
||||
@ -11,6 +11,7 @@
|
||||
"Scout_BRDM_2",
|
||||
"APC_BTR_80",
|
||||
"APC_BTR_RD",
|
||||
"LT_PT_76",
|
||||
"IFV_BMP_1",
|
||||
"MBT_T_55",
|
||||
"SPAAA_ZU_23_2_Insurgent_Mounted_Ural_375",
|
||||
|
||||
@ -20,6 +20,7 @@
|
||||
"APC_M113",
|
||||
"APC_BTR_80",
|
||||
"MBT_M60A3_Patton",
|
||||
"MBT_Chieftain_Mk_3",
|
||||
"IFV_BMP_1",
|
||||
"SPAAA_ZSU_23_4_Shilka_Gun_Dish",
|
||||
"SPAAA_ZSU_57_2",
|
||||
@ -75,7 +76,7 @@
|
||||
],
|
||||
"coastal_group_count": 2,
|
||||
"navy_generators": [
|
||||
"GrishaGroupGenerator",
|
||||
"LaCombattanteIIGroupGenerator",
|
||||
"MolniyaGroupGenerator"
|
||||
],
|
||||
"has_jtac": true,
|
||||
|
||||
@ -26,6 +26,7 @@
|
||||
"APC_M113",
|
||||
"APC_BTR_80",
|
||||
"MBT_M60A3_Patton",
|
||||
"MBT_Chieftain_Mk_3",
|
||||
"IFV_BMP_1",
|
||||
"MBT_T_72B",
|
||||
"SPAAA_ZSU_23_4_Shilka_Gun_Dish",
|
||||
@ -85,7 +86,8 @@
|
||||
"coastal_group_count": 3,
|
||||
"navy_generators": [
|
||||
"GrishaGroupGenerator",
|
||||
"MolniyaGroupGenerator"
|
||||
"MolniyaGroupGenerator",
|
||||
"LaCombattanteIIGroupGenerator"
|
||||
],
|
||||
"has_jtac": true,
|
||||
"jtac_unit": "MQ_9_Reaper"
|
||||
|
||||
@ -29,8 +29,10 @@
|
||||
"APC_MTLB",
|
||||
"MBT_T_55",
|
||||
"MBT_T_72B",
|
||||
"MBT_Chieftain_Mk_3",
|
||||
"APC_BTR_80",
|
||||
"Scout_BRDM_2",
|
||||
"LT_PT_76",
|
||||
"SPH_2S1_Gvozdika_122mm",
|
||||
"SPAAA_ZSU_57_2",
|
||||
"SPAAA_ZSU_23_4_Shilka_Gun_Dish"
|
||||
|
||||
@ -21,6 +21,7 @@
|
||||
"Scout_BRDM_2",
|
||||
"MBT_T_72B",
|
||||
"MBT_T_55",
|
||||
"LT_PT_76",
|
||||
"SPAAA_ZSU_23_4_Shilka_Gun_Dish",
|
||||
"SAM_SA_8_Osa_Gecko_TEL"
|
||||
],
|
||||
@ -71,6 +72,6 @@
|
||||
"carrier_names": [
|
||||
],
|
||||
"navy_generators": [
|
||||
"GrishaGroupGenerator", "MolniyaGroupGenerator"
|
||||
"GrishaGroupGenerator", "MolniyaGroupGenerator", "LaCombattanteIIGroupGenerator"
|
||||
]
|
||||
}
|
||||
|
||||
@ -25,6 +25,7 @@
|
||||
"MBT_T_55",
|
||||
"MBT_T_72B",
|
||||
"MBT_T_80U",
|
||||
"LT_PT_76",
|
||||
"SPAAA_ZSU_57_2",
|
||||
"SAM_SA_9_Strela_1_Gaskin_TEL"
|
||||
],
|
||||
|
||||
@ -17,6 +17,7 @@
|
||||
"Grad_MRL_FDDM__FC",
|
||||
"APC_MTLB",
|
||||
"MBT_T_55",
|
||||
"LT_PT_76",
|
||||
"SPAAA_ZU_23_2_Mounted_Ural_375",
|
||||
"AAA_8_8cm_Flak_18",
|
||||
"AAA_S_60_57mm"
|
||||
|
||||
@ -22,6 +22,7 @@
|
||||
"APC_BTR_RD",
|
||||
"IFV_BMD_1",
|
||||
"IFV_BMP_1",
|
||||
"LT_PT_76",
|
||||
"MBT_T_55",
|
||||
"SPAAA_ZU_23_2_Mounted_Ural_375",
|
||||
"SPAAA_ZSU_57_2",
|
||||
|
||||
@ -14,6 +14,7 @@
|
||||
"APC_BTR_80",
|
||||
"IFV_BMD_1",
|
||||
"IFV_BMP_1",
|
||||
"LT_PT_76",
|
||||
"MBT_T_55",
|
||||
"SPAAA_ZSU_57_2"
|
||||
],
|
||||
|
||||
@ -27,6 +27,7 @@
|
||||
"APC_BTR_80",
|
||||
"IFV_BMD_1",
|
||||
"IFV_BMP_1",
|
||||
"LT_PT_76",
|
||||
"MBT_T_55",
|
||||
"SAM_SA_8_Osa_Gecko_TEL"
|
||||
],
|
||||
|
||||
@ -20,6 +20,7 @@
|
||||
"Scout_BRDM_2",
|
||||
"Tk_PzIV_H",
|
||||
"MBT_T_55",
|
||||
"LT_PT_76",
|
||||
"SPAAA_ZU_23_2_Mounted_Ural_375",
|
||||
"SPAAA_ZSU_57_2",
|
||||
"AAA_S_60_57mm"
|
||||
|
||||
@ -20,6 +20,7 @@
|
||||
"frontline_units": [
|
||||
"Scout_BRDM_2",
|
||||
"MBT_T_55",
|
||||
"LT_PT_76",
|
||||
"Tk_PzIV_H",
|
||||
"SPG_StuG_III_Ausf__G",
|
||||
"SPG_Jagdpanzer_IV",
|
||||
|
||||
@ -20,6 +20,7 @@
|
||||
"IFV_BMP_1",
|
||||
"APC_MTLB",
|
||||
"MBT_T_55",
|
||||
"LT_PT_76",
|
||||
"SPAAA_ZU_23_2_Mounted_Ural_375",
|
||||
"SPAAA_ZSU_57_2",
|
||||
"AAA_S_60_57mm"
|
||||
|
||||
@ -21,6 +21,7 @@
|
||||
"IFV_BMP_1",
|
||||
"APC_MTLB",
|
||||
"MBT_T_55",
|
||||
"LT_PT_76",
|
||||
"MBT_T_72B",
|
||||
"SPAAA_ZU_23_2_Mounted_Ural_375",
|
||||
"SPAAA_ZSU_57_2",
|
||||
|
||||
@ -27,6 +27,7 @@
|
||||
"IFV_BMP_2",
|
||||
"APC_BTR_80",
|
||||
"Scout_BRDM_2",
|
||||
"LT_PT_76",
|
||||
"APC_MTLB",
|
||||
"Scout_Cobra",
|
||||
"MBT_T_55",
|
||||
|
||||
@ -25,7 +25,7 @@
|
||||
"SAM_Avenger__Stinger"
|
||||
],
|
||||
"artillery_units": [
|
||||
"SPH_M109_Paladin_155mm"
|
||||
"SPH_T155_Firtina_155mm"
|
||||
],
|
||||
"logistics_units": [
|
||||
"Truck_M818_6x6"
|
||||
|
||||
@ -19,6 +19,7 @@
|
||||
],
|
||||
"frontline_units": [
|
||||
"MBT_Challenger_II",
|
||||
"MBT_Chieftain_Mk_3",
|
||||
"IFV_Warrior",
|
||||
"Scout_HMMWV",
|
||||
"ATGM_HMMWV",
|
||||
|
||||
@ -570,7 +570,7 @@ class Waypoint {
|
||||
}
|
||||
|
||||
includeInPath() {
|
||||
return !this.waypoint.isDivert;
|
||||
return !this.waypoint.isDivert && !this.waypoint.isBullseye;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user