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.
|
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**
|
**Describe the bug**
|
||||||
A clear and concise description of what the bug is.
|
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.
|
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.**
|
**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 [...]
|
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.
|
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
|
## 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.
|
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 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 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]** 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]** 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]** 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).
|
* **[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]** 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]** 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]** 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.
|
* **[UI]** DCS loadouts are now selectable in the loadout setup menu.
|
||||||
|
|||||||
@ -8,7 +8,6 @@ DEFAULT_AVAILABLE_BUILDINGS = [
|
|||||||
"oil",
|
"oil",
|
||||||
"ware",
|
"ware",
|
||||||
"farp",
|
"farp",
|
||||||
"fob",
|
|
||||||
"power",
|
"power",
|
||||||
"derrick",
|
"derrick",
|
||||||
]
|
]
|
||||||
@ -21,7 +20,6 @@ WW2_GERMANY_BUILDINGS = [
|
|||||||
"ww2bunker",
|
"ww2bunker",
|
||||||
"allycamp",
|
"allycamp",
|
||||||
"allycamp",
|
"allycamp",
|
||||||
"fob",
|
|
||||||
]
|
]
|
||||||
WW2_ALLIES_BUILDINGS = [
|
WW2_ALLIES_BUILDINGS = [
|
||||||
"fuel",
|
"fuel",
|
||||||
@ -30,7 +28,6 @@ WW2_ALLIES_BUILDINGS = [
|
|||||||
"allycamp",
|
"allycamp",
|
||||||
"allycamp",
|
"allycamp",
|
||||||
"allycamp",
|
"allycamp",
|
||||||
"fob",
|
|
||||||
]
|
]
|
||||||
|
|
||||||
FORTIFICATION_BUILDINGS = [
|
FORTIFICATION_BUILDINGS = [
|
||||||
|
|||||||
@ -145,20 +145,26 @@ _WEAPON_FALLBACKS = [
|
|||||||
Weapons.LAU_117_with_AGM_65E___Maverick_E__Laser_ASM___Lg_Whd_,
|
Weapons.LAU_117_with_AGM_65E___Maverick_E__Laser_ASM___Lg_Whd_,
|
||||||
), # internal pylons harrier
|
), # internal pylons harrier
|
||||||
# AGM-154 JSOW
|
# 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_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_,
|
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
|
), # doesn't exist on any aircraft yet
|
||||||
(Weapons.AGM_154B___JSOW_Anti_Armour, Weapons.CBU_105___10_x_SFW__CBU_with_WCMD),
|
(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_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
|
# AGM-45 Shrike
|
||||||
(Weapons.AGM_45A_Shrike_ARM, None),
|
(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_1: 14,
|
||||||
Armor.IFV_BMP_2: 16,
|
Armor.IFV_BMP_2: 16,
|
||||||
Armor.IFV_BMP_3: 18,
|
Armor.IFV_BMP_3: 18,
|
||||||
|
Armor.LT_PT_76: 9,
|
||||||
Armor.ZBD_04A: 12,
|
Armor.ZBD_04A: 12,
|
||||||
Armor.ZTZ_96B: 30,
|
Armor.ZTZ_96B: 30,
|
||||||
Armor.Scout_Cobra: 4,
|
Armor.Scout_Cobra: 4,
|
||||||
Armor.APC_M113: 6,
|
Armor.APC_M113: 6,
|
||||||
Armor.Scout_HMMWV: 2,
|
Armor.Scout_HMMWV: 2,
|
||||||
Armor.ATGM_HMMWV: 8,
|
Armor.ATGM_HMMWV: 8,
|
||||||
|
Armor.ATGM_VAB_Mephisto: 12,
|
||||||
Armor.IFV_M2A2_Bradley: 12,
|
Armor.IFV_M2A2_Bradley: 12,
|
||||||
Armor.IFV_M1126_Stryker_ICV: 10,
|
Armor.IFV_M1126_Stryker_ICV: 10,
|
||||||
Armor.SPG_Stryker_MGS: 14,
|
Armor.SPG_Stryker_MGS: 14,
|
||||||
@ -519,6 +521,7 @@ PRICES = {
|
|||||||
Armor.MBT_Merkava_IV: 25,
|
Armor.MBT_Merkava_IV: 25,
|
||||||
Armor.APC_TPz_Fuchs: 5,
|
Armor.APC_TPz_Fuchs: 5,
|
||||||
Armor.MBT_Challenger_II: 25,
|
Armor.MBT_Challenger_II: 25,
|
||||||
|
Armor.MBT_Chieftain_Mk_3: 20,
|
||||||
Armor.IFV_Marder: 10,
|
Armor.IFV_Marder: 10,
|
||||||
Armor.IFV_Warrior: 10,
|
Armor.IFV_Warrior: 10,
|
||||||
Armor.IFV_LAV_25: 7,
|
Armor.IFV_LAV_25: 7,
|
||||||
@ -534,6 +537,7 @@ PRICES = {
|
|||||||
Artillery.Mortar_2B11_120mm: 4,
|
Artillery.Mortar_2B11_120mm: 4,
|
||||||
Artillery.SPH_Dana_vz77_152mm: 26,
|
Artillery.SPH_Dana_vz77_152mm: 26,
|
||||||
Artillery.PLZ_05: 25,
|
Artillery.PLZ_05: 25,
|
||||||
|
Artillery.SPH_T155_Firtina_155mm: 28,
|
||||||
Unarmed.LUV_UAZ_469_Jeep: 3,
|
Unarmed.LUV_UAZ_469_Jeep: 3,
|
||||||
Unarmed.Truck_Ural_375: 3,
|
Unarmed.Truck_Ural_375: 3,
|
||||||
Infantry.Infantry_M4: 1,
|
Infantry.Infantry_M4: 1,
|
||||||
@ -885,6 +889,7 @@ UNIT_BY_TASK = {
|
|||||||
Armor.IFV_BMP_3,
|
Armor.IFV_BMP_3,
|
||||||
Armor.IFV_BMP_3,
|
Armor.IFV_BMP_3,
|
||||||
Armor.IFV_BMD_1,
|
Armor.IFV_BMD_1,
|
||||||
|
Armor.LT_PT_76,
|
||||||
Armor.ZBD_04A,
|
Armor.ZBD_04A,
|
||||||
Armor.ZBD_04A,
|
Armor.ZBD_04A,
|
||||||
Armor.ZBD_04A,
|
Armor.ZBD_04A,
|
||||||
@ -913,6 +918,8 @@ UNIT_BY_TASK = {
|
|||||||
Armor.APC_TPz_Fuchs,
|
Armor.APC_TPz_Fuchs,
|
||||||
Armor.ATGM_HMMWV,
|
Armor.ATGM_HMMWV,
|
||||||
Armor.ATGM_HMMWV,
|
Armor.ATGM_HMMWV,
|
||||||
|
Armor.ATGM_VAB_Mephisto,
|
||||||
|
Armor.ATGM_VAB_Mephisto,
|
||||||
Armor.Scout_HMMWV,
|
Armor.Scout_HMMWV,
|
||||||
Armor.Scout_HMMWV,
|
Armor.Scout_HMMWV,
|
||||||
Armor.IFV_M2A2_Bradley,
|
Armor.IFV_M2A2_Bradley,
|
||||||
@ -941,6 +948,7 @@ UNIT_BY_TASK = {
|
|||||||
Armor.MBT_Leclerc,
|
Armor.MBT_Leclerc,
|
||||||
Armor.MBT_Leopard_2A6M,
|
Armor.MBT_Leopard_2A6M,
|
||||||
Armor.MBT_Challenger_II,
|
Armor.MBT_Challenger_II,
|
||||||
|
Armor.MBT_Chieftain_Mk_3,
|
||||||
Armor.MBT_Merkava_IV,
|
Armor.MBT_Merkava_IV,
|
||||||
Armor.MT_Pz_Kpfw_V_Panther_Ausf_G,
|
Armor.MT_Pz_Kpfw_V_Panther_Ausf_G,
|
||||||
Armor.Tk_PzIV_H,
|
Armor.Tk_PzIV_H,
|
||||||
@ -1002,6 +1010,7 @@ UNIT_BY_TASK = {
|
|||||||
Artillery.MLRS_9K57_Uragan_BM_27_220mm,
|
Artillery.MLRS_9K57_Uragan_BM_27_220mm,
|
||||||
Artillery.MLRS_9A52_Smerch_HE_300mm,
|
Artillery.MLRS_9A52_Smerch_HE_300mm,
|
||||||
Artillery.SPH_Dana_vz77_152mm,
|
Artillery.SPH_Dana_vz77_152mm,
|
||||||
|
Artillery.SPH_T155_Firtina_155mm,
|
||||||
Artillery.PLZ_05,
|
Artillery.PLZ_05,
|
||||||
Artillery.SPG_M12_GMC_155mm,
|
Artillery.SPG_M12_GMC_155mm,
|
||||||
Armor.SPG_Sturmpanzer_IV_Brummbar,
|
Armor.SPG_Sturmpanzer_IV_Brummbar,
|
||||||
@ -1241,7 +1250,6 @@ REWARDS = {
|
|||||||
"fuel": 2,
|
"fuel": 2,
|
||||||
"ammo": 2,
|
"ammo": 2,
|
||||||
"farp": 1,
|
"farp": 1,
|
||||||
"fob": 1,
|
|
||||||
# TODO: Should generate no cash once they generate units.
|
# TODO: Should generate no cash once they generate units.
|
||||||
"factory": 10,
|
"factory": 10,
|
||||||
"comms": 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 .profiling import logged_duration
|
||||||
from .settings import Settings
|
from .settings import Settings
|
||||||
from .theater import ConflictTheater
|
from .theater import ConflictTheater
|
||||||
|
from .theater.bullseye import Bullseye
|
||||||
from .theater.transitnetwork import TransitNetwork, TransitNetworkBuilder
|
from .theater.transitnetwork import TransitNetwork, TransitNetworkBuilder
|
||||||
from .threatzones import ThreatZones
|
from .threatzones import ThreatZones
|
||||||
from .transfers import PendingTransfers
|
from .transfers import PendingTransfers
|
||||||
@ -130,6 +131,9 @@ class Game:
|
|||||||
self.blue_ato = AirTaskingOrder()
|
self.blue_ato = AirTaskingOrder()
|
||||||
self.red_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.aircraft_inventory = GlobalAircraftInventory(self.theater.controlpoints)
|
||||||
|
|
||||||
self.transfers = PendingTransfers(self)
|
self.transfers = PendingTransfers(self)
|
||||||
@ -201,6 +205,11 @@ class Game:
|
|||||||
return self.player_faction
|
return self.player_faction
|
||||||
return self.enemy_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):
|
def _roll(self, prob, mult):
|
||||||
if self.settings.version == "dev":
|
if self.settings.version == "dev":
|
||||||
# always generate all events for dev
|
# always generate all events for dev
|
||||||
@ -337,10 +346,17 @@ class Game:
|
|||||||
|
|
||||||
return TurnState.CONTINUE
|
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:
|
def initialize_turn(self) -> None:
|
||||||
self.events = []
|
self.events = []
|
||||||
self._generate_events()
|
self._generate_events()
|
||||||
|
|
||||||
|
self.set_bullseye()
|
||||||
|
|
||||||
# Update statistics
|
# Update statistics
|
||||||
self.game_stats.update(self)
|
self.game_stats.update(self)
|
||||||
|
|
||||||
|
|||||||
@ -108,8 +108,12 @@ class Operation:
|
|||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def _setup_mission_coalitions(cls):
|
def _setup_mission_coalitions(cls):
|
||||||
cls.current_mission.coalition["blue"] = Coalition("blue")
|
cls.current_mission.coalition["blue"] = Coalition(
|
||||||
cls.current_mission.coalition["red"] = Coalition("red")
|
"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
|
p_country = cls.game.player_country
|
||||||
e_country = cls.game.enemy_country
|
e_country = cls.game.enemy_country
|
||||||
@ -171,13 +175,16 @@ class Operation:
|
|||||||
gen.add_dynamic_runway(dynamic_runway)
|
gen.add_dynamic_runway(dynamic_runway)
|
||||||
|
|
||||||
for tanker in airsupportgen.air_support.tankers:
|
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:
|
for aewc in airsupportgen.air_support.awacs:
|
||||||
gen.add_awacs(aewc)
|
if aewc.blue:
|
||||||
|
gen.add_awacs(aewc)
|
||||||
|
|
||||||
for jtac in jtacs:
|
for jtac in jtacs:
|
||||||
gen.add_jtac(jtac)
|
if jtac.blue:
|
||||||
|
gen.add_jtac(jtac)
|
||||||
|
|
||||||
for flight in airgen.flights:
|
for flight in airgen.flights:
|
||||||
gen.add_flight(flight)
|
gen.add_flight(flight)
|
||||||
@ -317,13 +324,8 @@ class Operation:
|
|||||||
|
|
||||||
# Setup combined arms parameters
|
# Setup combined arms parameters
|
||||||
cls.current_mission.groundControl.pilot_can_control_vehicles = cls.ca_slots > 0
|
cls.current_mission.groundControl.pilot_can_control_vehicles = cls.ca_slots > 0
|
||||||
if cls.game.player_country in [
|
cls.current_mission.groundControl.blue_tactical_commander = cls.ca_slots
|
||||||
country.name
|
cls.current_mission.groundControl.blue_observer = 1
|
||||||
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
|
|
||||||
|
|
||||||
# Options
|
# Options
|
||||||
forcedoptionsgen = ForcedOptionsGenerator(cls.current_mission, cls.game)
|
forcedoptionsgen = ForcedOptionsGenerator(cls.current_mission, cls.game)
|
||||||
@ -453,7 +455,7 @@ class Operation:
|
|||||||
|
|
||||||
for tanker in airsupportgen.air_support.tankers:
|
for tanker in airsupportgen.air_support.tankers:
|
||||||
luaData["Tankers"][tanker.callsign] = {
|
luaData["Tankers"][tanker.callsign] = {
|
||||||
"dcsGroupName": tanker.dcsGroupName,
|
"dcsGroupName": tanker.group_name,
|
||||||
"callsign": tanker.callsign,
|
"callsign": tanker.callsign,
|
||||||
"variant": tanker.variant,
|
"variant": tanker.variant,
|
||||||
"radio": tanker.freq.mhz,
|
"radio": tanker.freq.mhz,
|
||||||
@ -463,14 +465,14 @@ class Operation:
|
|||||||
if airsupportgen.air_support.awacs:
|
if airsupportgen.air_support.awacs:
|
||||||
for awacs in airsupportgen.air_support.awacs:
|
for awacs in airsupportgen.air_support.awacs:
|
||||||
luaData["AWACs"][awacs.callsign] = {
|
luaData["AWACs"][awacs.callsign] = {
|
||||||
"dcsGroupName": awacs.dcsGroupName,
|
"dcsGroupName": awacs.group_name,
|
||||||
"callsign": awacs.callsign,
|
"callsign": awacs.callsign,
|
||||||
"radio": awacs.freq.mhz,
|
"radio": awacs.freq.mhz,
|
||||||
}
|
}
|
||||||
|
|
||||||
for jtac in jtacs:
|
for jtac in jtacs:
|
||||||
luaData["JTACs"][jtac.callsign] = {
|
luaData["JTACs"][jtac.callsign] = {
|
||||||
"dcsGroupName": jtac.dcsGroupName,
|
"dcsGroupName": jtac.group_name,
|
||||||
"callsign": jtac.callsign,
|
"callsign": jtac.callsign,
|
||||||
"zone": jtac.region,
|
"zone": jtac.region,
|
||||||
"dcsUnit": jtac.unit_name,
|
"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 dcs.vehicles import AirDefence, Armor, MissilesSS, Unarmed
|
||||||
|
|
||||||
|
from .latlon import LatLon
|
||||||
from ..helipad import Helipad
|
from ..helipad import Helipad
|
||||||
from ..scenery_group import SceneryGroup
|
from ..scenery_group import SceneryGroup
|
||||||
from pyproj import CRS, Transformer
|
from pyproj import CRS, Transformer
|
||||||
@ -583,15 +584,6 @@ class ReferencePoint:
|
|||||||
image_coordinates: Point
|
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:
|
class ConflictTheater:
|
||||||
terrain: Terrain
|
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
|
return True
|
||||||
|
|
||||||
def generate_fob(self) -> None:
|
def generate_fob(self) -> None:
|
||||||
try:
|
category = "fob"
|
||||||
category = self.faction.building_set[self.faction.building_set.index("fob")]
|
|
||||||
except IndexError:
|
|
||||||
logging.exception("Faction has no fob buildings defined")
|
|
||||||
return
|
|
||||||
|
|
||||||
obj_name = self.control_point.name
|
obj_name = self.control_point.name
|
||||||
template = random.choice(list(self.templates[category].values()))
|
template = random.choice(list(self.templates[category].values()))
|
||||||
point = self.control_point.position
|
point = self.control_point.position
|
||||||
|
|||||||
@ -815,12 +815,13 @@ class AircraftConflictGenerator:
|
|||||||
|
|
||||||
self.air_support.awacs.append(
|
self.air_support.awacs.append(
|
||||||
AwacsInfo(
|
AwacsInfo(
|
||||||
dcsGroupName=str(group.name),
|
group_name=str(group.name),
|
||||||
callsign=callsign,
|
callsign=callsign,
|
||||||
freq=channel,
|
freq=channel,
|
||||||
depature_location=flight.departure.name,
|
depature_location=flight.departure.name,
|
||||||
end_time=flight.flight_plan.mission_departure_time,
|
end_time=flight.flight_plan.mission_departure_time,
|
||||||
start_time=flight.flight_plan.mission_start_time,
|
start_time=flight.flight_plan.mission_start_time,
|
||||||
|
blue=flight.departure.captured,
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -1275,7 +1276,7 @@ class AircraftConflictGenerator:
|
|||||||
group,
|
group,
|
||||||
react_on_threat=OptReactOnThreat.Values.EvadeFire,
|
react_on_threat=OptReactOnThreat.Values.EvadeFire,
|
||||||
roe=OptROE.Values.OpenFire,
|
roe=OptROE.Values.OpenFire,
|
||||||
rtb_winchester=OptRTBOnOutOfAmmo.Values.ASM,
|
rtb_winchester=OptRTBOnOutOfAmmo.Values.All,
|
||||||
restrict_jettison=True,
|
restrict_jettison=True,
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -1410,9 +1411,6 @@ class AircraftConflictGenerator:
|
|||||||
flight: Flight,
|
flight: Flight,
|
||||||
dynamic_runways: Dict[str, RunwayData],
|
dynamic_runways: Dict[str, RunwayData],
|
||||||
) -> None:
|
) -> 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
|
group.task = Transport.name
|
||||||
self._setup_group(group, package, flight, dynamic_runways)
|
self._setup_group(group, package, flight, dynamic_runways)
|
||||||
self.configure_behavior(
|
self.configure_behavior(
|
||||||
@ -1761,7 +1759,7 @@ class DeadIngressBuilder(PydcsWaypointBuilder):
|
|||||||
if isinstance(target_group, TheaterGroundObject):
|
if isinstance(target_group, TheaterGroundObject):
|
||||||
tgroup = self.mission.find_group(target_group.group_name)
|
tgroup = self.mission.find_group(target_group.group_name)
|
||||||
if tgroup is not None:
|
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["expend"] = "All"
|
||||||
task.params["attackQtyLimit"] = False
|
task.params["attackQtyLimit"] = False
|
||||||
task.params["directionEnabled"] = False
|
task.params["directionEnabled"] = False
|
||||||
@ -1866,12 +1864,11 @@ class StrikeIngressBuilder(PydcsWaypointBuilder):
|
|||||||
center.y += target.position.y
|
center.y += target.position.y
|
||||||
center.x /= len(targets)
|
center.x /= len(targets)
|
||||||
center.y /= len(targets)
|
center.y /= len(targets)
|
||||||
bombing = Bombing(center)
|
bombing = Bombing(center, weapon_type=WeaponType.Bombs)
|
||||||
bombing.params["expend"] = "All"
|
bombing.params["expend"] = "All"
|
||||||
bombing.params["attackQtyLimit"] = False
|
bombing.params["attackQtyLimit"] = False
|
||||||
bombing.params["directionEnabled"] = False
|
bombing.params["directionEnabled"] = False
|
||||||
bombing.params["altitudeEnabled"] = False
|
bombing.params["altitudeEnabled"] = False
|
||||||
bombing.params["weaponType"] = WeaponType.Bombs.value
|
|
||||||
bombing.params["groupAttack"] = True
|
bombing.params["groupAttack"] = True
|
||||||
waypoint.tasks.append(bombing)
|
waypoint.tasks.append(bombing)
|
||||||
return waypoint
|
return waypoint
|
||||||
@ -1879,11 +1876,10 @@ class StrikeIngressBuilder(PydcsWaypointBuilder):
|
|||||||
def build_strike(self) -> MovingPoint:
|
def build_strike(self) -> MovingPoint:
|
||||||
waypoint = super().build()
|
waypoint = super().build()
|
||||||
for target in self.waypoint.targets:
|
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 there is only one target, drop all ordnance in one pass.
|
||||||
if len(self.waypoint.targets) == 1:
|
if len(self.waypoint.targets) == 1:
|
||||||
bombing.params["expend"] = "All"
|
bombing.params["expend"] = "All"
|
||||||
bombing.params["weaponType"] = WeaponType.Auto.value
|
|
||||||
bombing.params["groupAttack"] = True
|
bombing.params["groupAttack"] = True
|
||||||
waypoint.tasks.append(bombing)
|
waypoint.tasks.append(bombing)
|
||||||
|
|
||||||
@ -1954,7 +1950,10 @@ class JoinPointBuilder(PydcsWaypointBuilder):
|
|||||||
EngageTargets(
|
EngageTargets(
|
||||||
# TODO: From doctrine.
|
# TODO: From doctrine.
|
||||||
max_distance=int(nautical_miles(30).meters),
|
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:
|
class AwacsInfo:
|
||||||
"""AWACS information for the kneeboard."""
|
"""AWACS information for the kneeboard."""
|
||||||
|
|
||||||
dcsGroupName: str
|
group_name: str
|
||||||
callsign: str
|
callsign: str
|
||||||
freq: RadioFrequency
|
freq: RadioFrequency
|
||||||
depature_location: Optional[str]
|
depature_location: Optional[str]
|
||||||
start_time: Optional[timedelta]
|
start_time: Optional[timedelta]
|
||||||
end_time: Optional[timedelta]
|
end_time: Optional[timedelta]
|
||||||
|
blue: bool
|
||||||
|
|
||||||
|
|
||||||
@dataclass
|
@dataclass
|
||||||
class TankerInfo:
|
class TankerInfo:
|
||||||
"""Tanker information for the kneeboard."""
|
"""Tanker information for the kneeboard."""
|
||||||
|
|
||||||
dcsGroupName: str
|
group_name: str
|
||||||
callsign: str
|
callsign: str
|
||||||
variant: str
|
variant: str
|
||||||
freq: RadioFrequency
|
freq: RadioFrequency
|
||||||
tacan: TacanChannel
|
tacan: TacanChannel
|
||||||
|
blue: bool
|
||||||
|
|
||||||
|
|
||||||
@dataclass
|
@dataclass
|
||||||
@ -165,7 +167,14 @@ class AirSupportConflictGenerator:
|
|||||||
tanker_group.points[0].tasks.append(SetImmortalCommand(True))
|
tanker_group.points[0].tasks.append(SetImmortalCommand(True))
|
||||||
|
|
||||||
self.air_support.tankers.append(
|
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:
|
if not self.game.settings.disable_legacy_aewc:
|
||||||
@ -196,12 +205,13 @@ class AirSupportConflictGenerator:
|
|||||||
|
|
||||||
self.air_support.awacs.append(
|
self.air_support.awacs.append(
|
||||||
AwacsInfo(
|
AwacsInfo(
|
||||||
dcsGroupName=str(awacs_flight.name),
|
group_name=str(awacs_flight.name),
|
||||||
callsign=callsign_for_support_unit(awacs_flight),
|
callsign=callsign_for_support_unit(awacs_flight),
|
||||||
freq=freq,
|
freq=freq,
|
||||||
depature_location=None,
|
depature_location=None,
|
||||||
start_time=None,
|
start_time=None,
|
||||||
end_time=None,
|
end_time=None,
|
||||||
|
blue=True,
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
else:
|
else:
|
||||||
|
|||||||
12
gen/armor.py
12
gen/armor.py
@ -68,11 +68,12 @@ INFANTRY_GROUP_SIZE = 5
|
|||||||
class JtacInfo:
|
class JtacInfo:
|
||||||
"""JTAC information."""
|
"""JTAC information."""
|
||||||
|
|
||||||
dcsGroupName: str
|
group_name: str
|
||||||
unit_name: str
|
unit_name: str
|
||||||
callsign: str
|
callsign: str
|
||||||
region: str
|
region: str
|
||||||
code: str
|
code: str
|
||||||
|
blue: bool
|
||||||
# TODO: Radio info? Type?
|
# TODO: Radio info? Type?
|
||||||
|
|
||||||
|
|
||||||
@ -196,7 +197,14 @@ class GroundConflictGenerator:
|
|||||||
# Note: Will need to change if we ever add ground based JTAC.
|
# Note: Will need to change if we ever add ground based JTAC.
|
||||||
callsign = callsign_for_support_unit(jtac)
|
callsign = callsign_for_support_unit(jtac)
|
||||||
self.jtacs.append(
|
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(
|
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,
|
ArleighBurkeGroupGenerator,
|
||||||
OliverHazardPerryGroupGenerator,
|
OliverHazardPerryGroupGenerator,
|
||||||
)
|
)
|
||||||
|
from gen.fleet.lacombattanteII import LaCombattanteIIGroupGenerator
|
||||||
from gen.fleet.lha_group import LHAGroupGenerator
|
from gen.fleet.lha_group import LHAGroupGenerator
|
||||||
from gen.fleet.ru_dd_group import (
|
from gen.fleet.ru_dd_group import (
|
||||||
RussianNavyGroupGenerator,
|
RussianNavyGroupGenerator,
|
||||||
@ -34,6 +35,7 @@ SHIP_MAP = {
|
|||||||
"KiloSubGroupGenerator": KiloSubGroupGenerator,
|
"KiloSubGroupGenerator": KiloSubGroupGenerator,
|
||||||
"TangoSubGroupGenerator": TangoSubGroupGenerator,
|
"TangoSubGroupGenerator": TangoSubGroupGenerator,
|
||||||
"Type54GroupGenerator": Type54GroupGenerator,
|
"Type54GroupGenerator": Type54GroupGenerator,
|
||||||
|
"LaCombattanteIIGroupGenerator": LaCombattanteIIGroupGenerator,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@ -132,7 +132,6 @@ CAP_CAPABLE = [
|
|||||||
MiG_29A,
|
MiG_29A,
|
||||||
F_16C_50,
|
F_16C_50,
|
||||||
FA_18C_hornet,
|
FA_18C_hornet,
|
||||||
F_15E,
|
|
||||||
F_16A,
|
F_16A,
|
||||||
F_4E,
|
F_4E,
|
||||||
JF_17,
|
JF_17,
|
||||||
@ -140,6 +139,7 @@ CAP_CAPABLE = [
|
|||||||
MiG_21Bis,
|
MiG_21Bis,
|
||||||
Mirage_2000_5,
|
Mirage_2000_5,
|
||||||
M_2000C,
|
M_2000C,
|
||||||
|
F_15E,
|
||||||
F_5E_3,
|
F_5E_3,
|
||||||
MiG_19P,
|
MiG_19P,
|
||||||
A_4E_C,
|
A_4E_C,
|
||||||
|
|||||||
@ -79,6 +79,7 @@ class FlightWaypointType(Enum):
|
|||||||
INGRESS_OCA_AIRCRAFT = 25
|
INGRESS_OCA_AIRCRAFT = 25
|
||||||
PICKUP = 26
|
PICKUP = 26
|
||||||
DROP_OFF = 27
|
DROP_OFF = 27
|
||||||
|
BULLSEYE = 28
|
||||||
|
|
||||||
|
|
||||||
class FlightWaypoint:
|
class FlightWaypoint:
|
||||||
|
|||||||
@ -426,6 +426,7 @@ class BarCapFlightPlan(PatrollingFlightPlan):
|
|||||||
takeoff: FlightWaypoint
|
takeoff: FlightWaypoint
|
||||||
land: FlightWaypoint
|
land: FlightWaypoint
|
||||||
divert: Optional[FlightWaypoint]
|
divert: Optional[FlightWaypoint]
|
||||||
|
bullseye: FlightWaypoint
|
||||||
|
|
||||||
def iter_waypoints(self) -> Iterator[FlightWaypoint]:
|
def iter_waypoints(self) -> Iterator[FlightWaypoint]:
|
||||||
yield self.takeoff
|
yield self.takeoff
|
||||||
@ -438,6 +439,7 @@ class BarCapFlightPlan(PatrollingFlightPlan):
|
|||||||
yield self.land
|
yield self.land
|
||||||
if self.divert is not None:
|
if self.divert is not None:
|
||||||
yield self.divert
|
yield self.divert
|
||||||
|
yield self.bullseye
|
||||||
|
|
||||||
|
|
||||||
@dataclass(frozen=True)
|
@dataclass(frozen=True)
|
||||||
@ -446,6 +448,7 @@ class CasFlightPlan(PatrollingFlightPlan):
|
|||||||
target: FlightWaypoint
|
target: FlightWaypoint
|
||||||
land: FlightWaypoint
|
land: FlightWaypoint
|
||||||
divert: Optional[FlightWaypoint]
|
divert: Optional[FlightWaypoint]
|
||||||
|
bullseye: FlightWaypoint
|
||||||
|
|
||||||
def iter_waypoints(self) -> Iterator[FlightWaypoint]:
|
def iter_waypoints(self) -> Iterator[FlightWaypoint]:
|
||||||
yield self.takeoff
|
yield self.takeoff
|
||||||
@ -459,6 +462,7 @@ class CasFlightPlan(PatrollingFlightPlan):
|
|||||||
yield self.land
|
yield self.land
|
||||||
if self.divert is not None:
|
if self.divert is not None:
|
||||||
yield self.divert
|
yield self.divert
|
||||||
|
yield self.bullseye
|
||||||
|
|
||||||
def request_escort_at(self) -> Optional[FlightWaypoint]:
|
def request_escort_at(self) -> Optional[FlightWaypoint]:
|
||||||
return self.patrol_start
|
return self.patrol_start
|
||||||
@ -472,6 +476,7 @@ class TarCapFlightPlan(PatrollingFlightPlan):
|
|||||||
takeoff: FlightWaypoint
|
takeoff: FlightWaypoint
|
||||||
land: FlightWaypoint
|
land: FlightWaypoint
|
||||||
divert: Optional[FlightWaypoint]
|
divert: Optional[FlightWaypoint]
|
||||||
|
bullseye: FlightWaypoint
|
||||||
lead_time: timedelta
|
lead_time: timedelta
|
||||||
|
|
||||||
def iter_waypoints(self) -> Iterator[FlightWaypoint]:
|
def iter_waypoints(self) -> Iterator[FlightWaypoint]:
|
||||||
@ -485,6 +490,7 @@ class TarCapFlightPlan(PatrollingFlightPlan):
|
|||||||
yield self.land
|
yield self.land
|
||||||
if self.divert is not None:
|
if self.divert is not None:
|
||||||
yield self.divert
|
yield self.divert
|
||||||
|
yield self.bullseye
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def tot_offset(self) -> timedelta:
|
def tot_offset(self) -> timedelta:
|
||||||
@ -523,6 +529,7 @@ class StrikeFlightPlan(FormationFlightPlan):
|
|||||||
nav_from: List[FlightWaypoint]
|
nav_from: List[FlightWaypoint]
|
||||||
land: FlightWaypoint
|
land: FlightWaypoint
|
||||||
divert: Optional[FlightWaypoint]
|
divert: Optional[FlightWaypoint]
|
||||||
|
bullseye: FlightWaypoint
|
||||||
|
|
||||||
def iter_waypoints(self) -> Iterator[FlightWaypoint]:
|
def iter_waypoints(self) -> Iterator[FlightWaypoint]:
|
||||||
yield self.takeoff
|
yield self.takeoff
|
||||||
@ -537,6 +544,7 @@ class StrikeFlightPlan(FormationFlightPlan):
|
|||||||
yield self.land
|
yield self.land
|
||||||
if self.divert is not None:
|
if self.divert is not None:
|
||||||
yield self.divert
|
yield self.divert
|
||||||
|
yield self.bullseye
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def package_speed_waypoints(self) -> Set[FlightWaypoint]:
|
def package_speed_waypoints(self) -> Set[FlightWaypoint]:
|
||||||
@ -641,6 +649,7 @@ class SweepFlightPlan(LoiterFlightPlan):
|
|||||||
nav_from: List[FlightWaypoint]
|
nav_from: List[FlightWaypoint]
|
||||||
land: FlightWaypoint
|
land: FlightWaypoint
|
||||||
divert: Optional[FlightWaypoint]
|
divert: Optional[FlightWaypoint]
|
||||||
|
bullseye: FlightWaypoint
|
||||||
lead_time: timedelta
|
lead_time: timedelta
|
||||||
|
|
||||||
def iter_waypoints(self) -> Iterator[FlightWaypoint]:
|
def iter_waypoints(self) -> Iterator[FlightWaypoint]:
|
||||||
@ -653,6 +662,7 @@ class SweepFlightPlan(LoiterFlightPlan):
|
|||||||
yield self.land
|
yield self.land
|
||||||
if self.divert is not None:
|
if self.divert is not None:
|
||||||
yield self.divert
|
yield self.divert
|
||||||
|
yield self.bullseye
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def tot_waypoint(self) -> Optional[FlightWaypoint]:
|
def tot_waypoint(self) -> Optional[FlightWaypoint]:
|
||||||
@ -704,6 +714,7 @@ class AwacsFlightPlan(LoiterFlightPlan):
|
|||||||
nav_from: List[FlightWaypoint]
|
nav_from: List[FlightWaypoint]
|
||||||
land: FlightWaypoint
|
land: FlightWaypoint
|
||||||
divert: Optional[FlightWaypoint]
|
divert: Optional[FlightWaypoint]
|
||||||
|
bullseye: FlightWaypoint
|
||||||
|
|
||||||
def iter_waypoints(self) -> Iterator[FlightWaypoint]:
|
def iter_waypoints(self) -> Iterator[FlightWaypoint]:
|
||||||
yield self.takeoff
|
yield self.takeoff
|
||||||
@ -713,6 +724,7 @@ class AwacsFlightPlan(LoiterFlightPlan):
|
|||||||
yield self.land
|
yield self.land
|
||||||
if self.divert is not None:
|
if self.divert is not None:
|
||||||
yield self.divert
|
yield self.divert
|
||||||
|
yield self.bullseye
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def mission_start_time(self) -> Optional[timedelta]:
|
def mission_start_time(self) -> Optional[timedelta]:
|
||||||
@ -746,6 +758,7 @@ class AirliftFlightPlan(FlightPlan):
|
|||||||
nav_to_home: List[FlightWaypoint]
|
nav_to_home: List[FlightWaypoint]
|
||||||
land: FlightWaypoint
|
land: FlightWaypoint
|
||||||
divert: Optional[FlightWaypoint]
|
divert: Optional[FlightWaypoint]
|
||||||
|
bullseye: FlightWaypoint
|
||||||
|
|
||||||
def iter_waypoints(self) -> Iterator[FlightWaypoint]:
|
def iter_waypoints(self) -> Iterator[FlightWaypoint]:
|
||||||
yield self.takeoff
|
yield self.takeoff
|
||||||
@ -758,6 +771,7 @@ class AirliftFlightPlan(FlightPlan):
|
|||||||
yield self.land
|
yield self.land
|
||||||
if self.divert is not None:
|
if self.divert is not None:
|
||||||
yield self.divert
|
yield self.divert
|
||||||
|
yield self.bullseye
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def tot_waypoint(self) -> Optional[FlightWaypoint]:
|
def tot_waypoint(self) -> Optional[FlightWaypoint]:
|
||||||
@ -1053,6 +1067,7 @@ class FlightPlanBuilder:
|
|||||||
),
|
),
|
||||||
land=builder.land(flight.arrival),
|
land=builder.land(flight.arrival),
|
||||||
divert=builder.divert(flight.divert),
|
divert=builder.divert(flight.divert),
|
||||||
|
bullseye=builder.bullseye(),
|
||||||
hold=start,
|
hold=start,
|
||||||
hold_duration=timedelta(hours=4),
|
hold_duration=timedelta(hours=4),
|
||||||
)
|
)
|
||||||
@ -1151,6 +1166,7 @@ class FlightPlanBuilder:
|
|||||||
patrol_end=end,
|
patrol_end=end,
|
||||||
land=builder.land(flight.arrival),
|
land=builder.land(flight.arrival),
|
||||||
divert=builder.divert(flight.divert),
|
divert=builder.divert(flight.divert),
|
||||||
|
bullseye=builder.bullseye(),
|
||||||
)
|
)
|
||||||
|
|
||||||
def generate_sweep(self, flight: Flight) -> SweepFlightPlan:
|
def generate_sweep(self, flight: Flight) -> SweepFlightPlan:
|
||||||
@ -1187,6 +1203,7 @@ class FlightPlanBuilder:
|
|||||||
sweep_end=end,
|
sweep_end=end,
|
||||||
land=builder.land(flight.arrival),
|
land=builder.land(flight.arrival),
|
||||||
divert=builder.divert(flight.divert),
|
divert=builder.divert(flight.divert),
|
||||||
|
bullseye=builder.bullseye(),
|
||||||
)
|
)
|
||||||
|
|
||||||
def generate_transport(self, flight: Flight) -> AirliftFlightPlan:
|
def generate_transport(self, flight: Flight) -> AirliftFlightPlan:
|
||||||
@ -1238,6 +1255,7 @@ class FlightPlanBuilder:
|
|||||||
),
|
),
|
||||||
land=builder.land(flight.arrival),
|
land=builder.land(flight.arrival),
|
||||||
divert=builder.divert(flight.divert),
|
divert=builder.divert(flight.divert),
|
||||||
|
bullseye=builder.bullseye(),
|
||||||
)
|
)
|
||||||
|
|
||||||
def racetrack_for_objective(
|
def racetrack_for_objective(
|
||||||
@ -1389,6 +1407,7 @@ class FlightPlanBuilder:
|
|||||||
patrol_end=end,
|
patrol_end=end,
|
||||||
land=builder.land(flight.arrival),
|
land=builder.land(flight.arrival),
|
||||||
divert=builder.divert(flight.divert),
|
divert=builder.divert(flight.divert),
|
||||||
|
bullseye=builder.bullseye(),
|
||||||
)
|
)
|
||||||
|
|
||||||
def generate_dead(
|
def generate_dead(
|
||||||
@ -1517,6 +1536,7 @@ class FlightPlanBuilder:
|
|||||||
),
|
),
|
||||||
land=builder.land(flight.arrival),
|
land=builder.land(flight.arrival),
|
||||||
divert=builder.divert(flight.divert),
|
divert=builder.divert(flight.divert),
|
||||||
|
bullseye=builder.bullseye(),
|
||||||
)
|
)
|
||||||
|
|
||||||
def generate_cas(self, flight: Flight) -> CasFlightPlan:
|
def generate_cas(self, flight: Flight) -> CasFlightPlan:
|
||||||
@ -1562,6 +1582,7 @@ class FlightPlanBuilder:
|
|||||||
patrol_end=builder.egress(egress, location),
|
patrol_end=builder.egress(egress, location),
|
||||||
land=builder.land(flight.arrival),
|
land=builder.land(flight.arrival),
|
||||||
divert=builder.divert(flight.divert),
|
divert=builder.divert(flight.divert),
|
||||||
|
bullseye=builder.bullseye(),
|
||||||
)
|
)
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
@ -1696,6 +1717,7 @@ class FlightPlanBuilder:
|
|||||||
),
|
),
|
||||||
land=builder.land(flight.arrival),
|
land=builder.land(flight.arrival),
|
||||||
divert=builder.divert(flight.divert),
|
divert=builder.divert(flight.divert),
|
||||||
|
bullseye=builder.bullseye(),
|
||||||
)
|
)
|
||||||
|
|
||||||
def _retreating_rendezvous_point(self, attack_transition: Point) -> Point:
|
def _retreating_rendezvous_point(self, attack_transition: Point) -> Point:
|
||||||
|
|||||||
@ -35,6 +35,9 @@ class Loadout:
|
|||||||
|
|
||||||
new_pylons = dict(self.pylons)
|
new_pylons = dict(self.pylons)
|
||||||
for pylon_number, weapon in self.pylons.items():
|
for pylon_number, weapon in self.pylons.items():
|
||||||
|
if weapon is None:
|
||||||
|
del new_pylons[pylon_number]
|
||||||
|
continue
|
||||||
if not weapon.available_on(date):
|
if not weapon.available_on(date):
|
||||||
pylon = Pylon.for_aircraft(unit_type, pylon_number)
|
pylon = Pylon.for_aircraft(unit_type, pylon_number)
|
||||||
for fallback in weapon.fallbacks:
|
for fallback in weapon.fallbacks:
|
||||||
@ -44,6 +47,8 @@ class Loadout:
|
|||||||
continue
|
continue
|
||||||
new_pylons[pylon_number] = fallback
|
new_pylons[pylon_number] = fallback
|
||||||
break
|
break
|
||||||
|
else:
|
||||||
|
del new_pylons[pylon_number]
|
||||||
return Loadout(f"{self.name} ({date.year})", new_pylons, date)
|
return Loadout(f"{self.name} ({date.year})", new_pylons, date)
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
|
|||||||
@ -50,6 +50,7 @@ class WaypointBuilder:
|
|||||||
self.threat_zones = game.threat_zone_for(not player)
|
self.threat_zones = game.threat_zone_for(not player)
|
||||||
self.navmesh = game.navmesh_for(player)
|
self.navmesh = game.navmesh_for(player)
|
||||||
self.targets = targets
|
self.targets = targets
|
||||||
|
self._bullseye = game.bullseye_for(player)
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def is_helo(self) -> bool:
|
def is_helo(self) -> bool:
|
||||||
@ -145,6 +146,19 @@ class WaypointBuilder:
|
|||||||
waypoint.only_for_player = True
|
waypoint.only_for_player = True
|
||||||
return waypoint
|
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:
|
def hold(self, position: Point) -> FlightWaypoint:
|
||||||
waypoint = FlightWaypoint(
|
waypoint = FlightWaypoint(
|
||||||
FlightWaypointType.LOITER,
|
FlightWaypointType.LOITER,
|
||||||
|
|||||||
@ -15,10 +15,12 @@ TYPE_TANKS = [
|
|||||||
Armor.MBT_Leopard_1A3,
|
Armor.MBT_Leopard_1A3,
|
||||||
Armor.MBT_Leclerc,
|
Armor.MBT_Leclerc,
|
||||||
Armor.MBT_Challenger_II,
|
Armor.MBT_Challenger_II,
|
||||||
|
Armor.MBT_Chieftain_Mk_3,
|
||||||
Armor.MBT_M1A2_Abrams,
|
Armor.MBT_M1A2_Abrams,
|
||||||
Armor.MBT_M60A3_Patton,
|
Armor.MBT_M60A3_Patton,
|
||||||
Armor.MBT_Merkava_IV,
|
Armor.MBT_Merkava_IV,
|
||||||
Armor.ZTZ_96B,
|
Armor.ZTZ_96B,
|
||||||
|
Armor.LT_PT_76,
|
||||||
# WW2
|
# WW2
|
||||||
Armor.MT_Pz_Kpfw_V_Panther_Ausf_G,
|
Armor.MT_Pz_Kpfw_V_Panther_Ausf_G,
|
||||||
Armor.Tk_PzIV_H,
|
Armor.Tk_PzIV_H,
|
||||||
@ -45,6 +47,7 @@ TYPE_TANKS = [
|
|||||||
|
|
||||||
TYPE_ATGM = [
|
TYPE_ATGM = [
|
||||||
Armor.ATGM_HMMWV,
|
Armor.ATGM_HMMWV,
|
||||||
|
Armor.ATGM_VAB_Mephisto,
|
||||||
Armor.ATGM_Stryker,
|
Armor.ATGM_Stryker,
|
||||||
Armor.IFV_BMP_2,
|
Armor.IFV_BMP_2,
|
||||||
# WW2 (Tank Destroyers)
|
# WW2 (Tank Destroyers)
|
||||||
@ -114,6 +117,7 @@ TYPE_ARTILLERY = [
|
|||||||
Artillery.MLRS_M270_227mm,
|
Artillery.MLRS_M270_227mm,
|
||||||
Artillery.SPM_2S9_Nona_120mm_M,
|
Artillery.SPM_2S9_Nona_120mm_M,
|
||||||
Artillery.SPH_Dana_vz77_152mm,
|
Artillery.SPH_Dana_vz77_152mm,
|
||||||
|
Artillery.SPH_T155_Firtina_155mm,
|
||||||
Artillery.PLZ_05,
|
Artillery.PLZ_05,
|
||||||
Artillery.SPH_2S19_Msta_152mm,
|
Artillery.SPH_2S19_Msta_152mm,
|
||||||
Artillery.MLRS_9A52_Smerch_CM_300mm,
|
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.
|
aircraft will be able to see the enemy's kneeboard for the same airframe.
|
||||||
"""
|
"""
|
||||||
import datetime
|
import datetime
|
||||||
|
import textwrap
|
||||||
from collections import defaultdict
|
from collections import defaultdict
|
||||||
from dataclasses import dataclass
|
from dataclasses import dataclass
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
@ -37,6 +38,7 @@ from tabulate import tabulate
|
|||||||
from game.data.alic import AlicCodes
|
from game.data.alic import AlicCodes
|
||||||
from game.db import find_unittype, unit_type_from_name
|
from game.db import find_unittype, unit_type_from_name
|
||||||
from game.theater import ConflictTheater, TheaterGroundObject, LatLon
|
from game.theater import ConflictTheater, TheaterGroundObject, LatLon
|
||||||
|
from game.theater.bullseye import Bullseye
|
||||||
from game.utils import meters
|
from game.utils import meters
|
||||||
from .aircraft import AIRCRAFT_DATA, FlightData
|
from .aircraft import AIRCRAFT_DATA, FlightData
|
||||||
from .airsupportgen import AwacsInfo, TankerInfo
|
from .airsupportgen import AwacsInfo, TankerInfo
|
||||||
@ -257,21 +259,16 @@ class BriefingPage(KneeboardPage):
|
|||||||
def __init__(
|
def __init__(
|
||||||
self,
|
self,
|
||||||
flight: FlightData,
|
flight: FlightData,
|
||||||
comms: List[CommInfo],
|
bullseye: Bullseye,
|
||||||
awacs: List[AwacsInfo],
|
theater: ConflictTheater,
|
||||||
tankers: List[TankerInfo],
|
|
||||||
jtacs: List[JtacInfo],
|
|
||||||
start_time: datetime.datetime,
|
start_time: datetime.datetime,
|
||||||
dark_kneeboard: bool,
|
dark_kneeboard: bool,
|
||||||
) -> None:
|
) -> None:
|
||||||
self.flight = flight
|
self.flight = flight
|
||||||
self.comms = list(comms)
|
self.bullseye = bullseye
|
||||||
self.awacs = awacs
|
self.theater = theater
|
||||||
self.tankers = tankers
|
|
||||||
self.jtacs = jtacs
|
|
||||||
self.start_time = start_time
|
self.start_time = start_time
|
||||||
self.dark_kneeboard = dark_kneeboard
|
self.dark_kneeboard = dark_kneeboard
|
||||||
self.comms.append(CommInfo("Flight", self.flight.intra_flight_channel))
|
|
||||||
|
|
||||||
def write(self, path: Path) -> None:
|
def write(self, path: Path) -> None:
|
||||||
writer = KneeboardPageWriter(dark_theme=self.dark_kneeboard)
|
writer = KneeboardPageWriter(dark_theme=self.dark_kneeboard)
|
||||||
@ -301,6 +298,10 @@ class BriefingPage(KneeboardPage):
|
|||||||
headers=["#", "Action", "Alt", "Dist", "GSPD", "Time", "Departure"],
|
headers=["#", "Action", "Alt", "Dist", "GSPD", "Time", "Departure"],
|
||||||
)
|
)
|
||||||
|
|
||||||
|
writer.text(
|
||||||
|
f"Bullseye: {self.format_ll(self.bullseye.to_lat_lon(self.theater))}"
|
||||||
|
)
|
||||||
|
|
||||||
writer.table(
|
writer.table(
|
||||||
[
|
[
|
||||||
[
|
[
|
||||||
@ -311,6 +312,86 @@ class BriefingPage(KneeboardPage):
|
|||||||
["Bingo", "Joker"],
|
["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
|
# AEW&C
|
||||||
writer.heading("AEW&C")
|
writer.heading("AEW&C")
|
||||||
aewc_ladder = []
|
aewc_ladder = []
|
||||||
@ -368,44 +449,6 @@ class BriefingPage(KneeboardPage):
|
|||||||
|
|
||||||
writer.write(path)
|
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:
|
def format_frequency(self, frequency: RadioFrequency) -> str:
|
||||||
channel = self.flight.channel_for(frequency)
|
channel = self.flight.channel_for(frequency)
|
||||||
if channel is None:
|
if channel is None:
|
||||||
@ -558,6 +601,13 @@ class KneeboardGenerator(MissionInfoGenerator):
|
|||||||
"""Returns a list of kneeboard pages for the given flight."""
|
"""Returns a list of kneeboard pages for the given flight."""
|
||||||
pages: List[KneeboardPage] = [
|
pages: List[KneeboardPage] = [
|
||||||
BriefingPage(
|
BriefingPage(
|
||||||
|
flight,
|
||||||
|
self.game.bullseye_for(flight.friendly),
|
||||||
|
self.game.theater,
|
||||||
|
self.mission.start_time,
|
||||||
|
self.dark_kneeboard,
|
||||||
|
),
|
||||||
|
SupportPage(
|
||||||
flight,
|
flight,
|
||||||
self.comms,
|
self.comms,
|
||||||
self.awacs,
|
self.awacs,
|
||||||
|
|||||||
@ -209,16 +209,6 @@ class TriggersGenerator:
|
|||||||
player_coalition = "blue"
|
player_coalition = "blue"
|
||||||
enemy_coalition = "red"
|
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_skill(player_coalition, enemy_coalition)
|
||||||
self._set_allegiances(player_coalition, enemy_coalition)
|
self._set_allegiances(player_coalition, enemy_coalition)
|
||||||
self._gen_markers()
|
self._gen_markers()
|
||||||
|
|||||||
2
pydcs
2
pydcs
@ -1 +1 @@
|
|||||||
Subproject commit dec648d27f74c394dd6e85e83cc09e4cd823653d
|
Subproject commit 4972988c978f2057e7aa06919c4de71ee9a06ea5
|
||||||
@ -375,6 +375,7 @@ class WaypointJs(QObject):
|
|||||||
timingChanged = Signal()
|
timingChanged = Signal()
|
||||||
isTakeoffChanged = Signal()
|
isTakeoffChanged = Signal()
|
||||||
isDivertChanged = Signal()
|
isDivertChanged = Signal()
|
||||||
|
isBullseyeChanged = Signal()
|
||||||
|
|
||||||
def __init__(
|
def __init__(
|
||||||
self,
|
self,
|
||||||
@ -439,6 +440,10 @@ class WaypointJs(QObject):
|
|||||||
def isDivert(self) -> bool:
|
def isDivert(self) -> bool:
|
||||||
return self.waypoint.waypoint_type is FlightWaypointType.DIVERT
|
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)
|
@Slot(list, result=str)
|
||||||
def setPosition(self, position: LeafletLatLon) -> str:
|
def setPosition(self, position: LeafletLatLon) -> str:
|
||||||
point = self.theater.ll_to_point(LatLon(*position))
|
point = self.theater.ll_to_point(LatLon(*position))
|
||||||
|
|||||||
@ -47,7 +47,8 @@
|
|||||||
"MBT_Challenger_II",
|
"MBT_Challenger_II",
|
||||||
"MBT_M60A3_Patton",
|
"MBT_M60A3_Patton",
|
||||||
"SPG_Stryker_MGS",
|
"SPG_Stryker_MGS",
|
||||||
"SAM_Avenger__Stinger"
|
"SAM_Avenger__Stinger",
|
||||||
|
"ATGM_VAB_Mephisto"
|
||||||
],
|
],
|
||||||
"artillery_units": [
|
"artillery_units": [
|
||||||
"MLRS_M270_227mm",
|
"MLRS_M270_227mm",
|
||||||
|
|||||||
@ -46,7 +46,8 @@
|
|||||||
"Scout_HMMWV",
|
"Scout_HMMWV",
|
||||||
"ATGM_HMMWV",
|
"ATGM_HMMWV",
|
||||||
"SAM_Linebacker___Bradley_M6",
|
"SAM_Linebacker___Bradley_M6",
|
||||||
"SAM_Avenger__Stinger"
|
"SAM_Avenger__Stinger",
|
||||||
|
"ATGM_VAB_Mephisto"
|
||||||
],
|
],
|
||||||
"artillery_units": [
|
"artillery_units": [
|
||||||
"MLRS_M270_227mm",
|
"MLRS_M270_227mm",
|
||||||
|
|||||||
@ -23,13 +23,10 @@
|
|||||||
"ERC_90",
|
"ERC_90",
|
||||||
"TRM_2000_PAMELA",
|
"TRM_2000_PAMELA",
|
||||||
"VAB__50",
|
"VAB__50",
|
||||||
"VAB_MEPHISTO",
|
"ATGM_VAB_Mephisto",
|
||||||
"VAB_T20_13",
|
|
||||||
"VAB_T20_13",
|
"VAB_T20_13",
|
||||||
"VBL__50",
|
"VBL__50",
|
||||||
"VBL_AANF1",
|
"VBL_AANF1",
|
||||||
"VBAE_CRAB",
|
|
||||||
"VBAE_CRAB_MMP",
|
|
||||||
"AMX_30B2",
|
"AMX_30B2",
|
||||||
"SAM_Roland_ADS"
|
"SAM_Roland_ADS"
|
||||||
],
|
],
|
||||||
|
|||||||
@ -21,10 +21,8 @@
|
|||||||
"MBT_Leclerc",
|
"MBT_Leclerc",
|
||||||
"APC_TPz_Fuchs",
|
"APC_TPz_Fuchs",
|
||||||
"Scout_Cobra",
|
"Scout_Cobra",
|
||||||
"ATGM_Stryker",
|
|
||||||
"IFV_LAV_25",
|
"IFV_LAV_25",
|
||||||
"Scout_HMMWV",
|
"ATGM_VAB_Mephisto",
|
||||||
"ATGM_HMMWV",
|
|
||||||
"SAM_Roland_ADS"
|
"SAM_Roland_ADS"
|
||||||
],
|
],
|
||||||
"artillery_units": [
|
"artillery_units": [
|
||||||
|
|||||||
@ -24,7 +24,6 @@
|
|||||||
"ERC_90",
|
"ERC_90",
|
||||||
"TRM_2000_PAMELA",
|
"TRM_2000_PAMELA",
|
||||||
"VAB__50",
|
"VAB__50",
|
||||||
"VAB_MEPHISTO",
|
|
||||||
"VAB_T20_13",
|
"VAB_T20_13",
|
||||||
"VAB_T20_13",
|
"VAB_T20_13",
|
||||||
"VBL__50",
|
"VBL__50",
|
||||||
@ -33,7 +32,8 @@
|
|||||||
"VBAE_CRAB_MMP",
|
"VBAE_CRAB_MMP",
|
||||||
"AMX_30B2",
|
"AMX_30B2",
|
||||||
"Leclerc_Serie_XXI",
|
"Leclerc_Serie_XXI",
|
||||||
"SAM_Roland_ADS"
|
"SAM_Roland_ADS",
|
||||||
|
"ATGM_VAB_Mephisto"
|
||||||
],
|
],
|
||||||
"artillery_units": [
|
"artillery_units": [
|
||||||
"MLRS_M270_227mm",
|
"MLRS_M270_227mm",
|
||||||
|
|||||||
@ -45,6 +45,9 @@
|
|||||||
"ZU23Generator",
|
"ZU23Generator",
|
||||||
"ZU23UralGenerator"
|
"ZU23UralGenerator"
|
||||||
],
|
],
|
||||||
|
"navy_generators": [
|
||||||
|
"LaCombattanteIIGroupGenerator"
|
||||||
|
],
|
||||||
"requirements": {},
|
"requirements": {},
|
||||||
"has_jtac": true,
|
"has_jtac": true,
|
||||||
"jtac_unit": "MQ_9_Reaper"
|
"jtac_unit": "MQ_9_Reaper"
|
||||||
|
|||||||
@ -61,7 +61,8 @@
|
|||||||
"helicopter_carrier_names": [
|
"helicopter_carrier_names": [
|
||||||
],
|
],
|
||||||
"navy_generators": [
|
"navy_generators": [
|
||||||
"OliverHazardPerryGroupGenerator"
|
"OliverHazardPerryGroupGenerator",
|
||||||
|
"LaCombattanteIIGroupGenerator"
|
||||||
],
|
],
|
||||||
"has_jtac": true,
|
"has_jtac": true,
|
||||||
"jtac_unit": "MQ_9_Reaper"
|
"jtac_unit": "MQ_9_Reaper"
|
||||||
|
|||||||
@ -46,6 +46,9 @@
|
|||||||
"HawkEwrGenerator",
|
"HawkEwrGenerator",
|
||||||
"FlatFaceGenerator"
|
"FlatFaceGenerator"
|
||||||
],
|
],
|
||||||
|
"navy_generators": [
|
||||||
|
"LaCombattanteIIGroupGenerator"
|
||||||
|
],
|
||||||
"has_jtac": true,
|
"has_jtac": true,
|
||||||
"jtac_unit": "MQ_9_Reaper"
|
"jtac_unit": "MQ_9_Reaper"
|
||||||
}
|
}
|
||||||
|
|||||||
@ -9,6 +9,7 @@
|
|||||||
"Scout_Cobra",
|
"Scout_Cobra",
|
||||||
"APC_MTLB",
|
"APC_MTLB",
|
||||||
"Scout_BRDM_2",
|
"Scout_BRDM_2",
|
||||||
|
"LT_PT_76",
|
||||||
"SPAAA_ZU_23_2_Insurgent_Mounted_Ural_375"
|
"SPAAA_ZU_23_2_Insurgent_Mounted_Ural_375"
|
||||||
],
|
],
|
||||||
"artillery_units": [
|
"artillery_units": [
|
||||||
|
|||||||
@ -11,6 +11,7 @@
|
|||||||
"Scout_BRDM_2",
|
"Scout_BRDM_2",
|
||||||
"APC_BTR_80",
|
"APC_BTR_80",
|
||||||
"APC_BTR_RD",
|
"APC_BTR_RD",
|
||||||
|
"LT_PT_76",
|
||||||
"IFV_BMP_1",
|
"IFV_BMP_1",
|
||||||
"MBT_T_55",
|
"MBT_T_55",
|
||||||
"SPAAA_ZU_23_2_Insurgent_Mounted_Ural_375",
|
"SPAAA_ZU_23_2_Insurgent_Mounted_Ural_375",
|
||||||
|
|||||||
@ -20,6 +20,7 @@
|
|||||||
"APC_M113",
|
"APC_M113",
|
||||||
"APC_BTR_80",
|
"APC_BTR_80",
|
||||||
"MBT_M60A3_Patton",
|
"MBT_M60A3_Patton",
|
||||||
|
"MBT_Chieftain_Mk_3",
|
||||||
"IFV_BMP_1",
|
"IFV_BMP_1",
|
||||||
"SPAAA_ZSU_23_4_Shilka_Gun_Dish",
|
"SPAAA_ZSU_23_4_Shilka_Gun_Dish",
|
||||||
"SPAAA_ZSU_57_2",
|
"SPAAA_ZSU_57_2",
|
||||||
@ -75,7 +76,7 @@
|
|||||||
],
|
],
|
||||||
"coastal_group_count": 2,
|
"coastal_group_count": 2,
|
||||||
"navy_generators": [
|
"navy_generators": [
|
||||||
"GrishaGroupGenerator",
|
"LaCombattanteIIGroupGenerator",
|
||||||
"MolniyaGroupGenerator"
|
"MolniyaGroupGenerator"
|
||||||
],
|
],
|
||||||
"has_jtac": true,
|
"has_jtac": true,
|
||||||
|
|||||||
@ -26,6 +26,7 @@
|
|||||||
"APC_M113",
|
"APC_M113",
|
||||||
"APC_BTR_80",
|
"APC_BTR_80",
|
||||||
"MBT_M60A3_Patton",
|
"MBT_M60A3_Patton",
|
||||||
|
"MBT_Chieftain_Mk_3",
|
||||||
"IFV_BMP_1",
|
"IFV_BMP_1",
|
||||||
"MBT_T_72B",
|
"MBT_T_72B",
|
||||||
"SPAAA_ZSU_23_4_Shilka_Gun_Dish",
|
"SPAAA_ZSU_23_4_Shilka_Gun_Dish",
|
||||||
@ -85,7 +86,8 @@
|
|||||||
"coastal_group_count": 3,
|
"coastal_group_count": 3,
|
||||||
"navy_generators": [
|
"navy_generators": [
|
||||||
"GrishaGroupGenerator",
|
"GrishaGroupGenerator",
|
||||||
"MolniyaGroupGenerator"
|
"MolniyaGroupGenerator",
|
||||||
|
"LaCombattanteIIGroupGenerator"
|
||||||
],
|
],
|
||||||
"has_jtac": true,
|
"has_jtac": true,
|
||||||
"jtac_unit": "MQ_9_Reaper"
|
"jtac_unit": "MQ_9_Reaper"
|
||||||
|
|||||||
@ -29,8 +29,10 @@
|
|||||||
"APC_MTLB",
|
"APC_MTLB",
|
||||||
"MBT_T_55",
|
"MBT_T_55",
|
||||||
"MBT_T_72B",
|
"MBT_T_72B",
|
||||||
|
"MBT_Chieftain_Mk_3",
|
||||||
"APC_BTR_80",
|
"APC_BTR_80",
|
||||||
"Scout_BRDM_2",
|
"Scout_BRDM_2",
|
||||||
|
"LT_PT_76",
|
||||||
"SPH_2S1_Gvozdika_122mm",
|
"SPH_2S1_Gvozdika_122mm",
|
||||||
"SPAAA_ZSU_57_2",
|
"SPAAA_ZSU_57_2",
|
||||||
"SPAAA_ZSU_23_4_Shilka_Gun_Dish"
|
"SPAAA_ZSU_23_4_Shilka_Gun_Dish"
|
||||||
|
|||||||
@ -21,6 +21,7 @@
|
|||||||
"Scout_BRDM_2",
|
"Scout_BRDM_2",
|
||||||
"MBT_T_72B",
|
"MBT_T_72B",
|
||||||
"MBT_T_55",
|
"MBT_T_55",
|
||||||
|
"LT_PT_76",
|
||||||
"SPAAA_ZSU_23_4_Shilka_Gun_Dish",
|
"SPAAA_ZSU_23_4_Shilka_Gun_Dish",
|
||||||
"SAM_SA_8_Osa_Gecko_TEL"
|
"SAM_SA_8_Osa_Gecko_TEL"
|
||||||
],
|
],
|
||||||
@ -71,6 +72,6 @@
|
|||||||
"carrier_names": [
|
"carrier_names": [
|
||||||
],
|
],
|
||||||
"navy_generators": [
|
"navy_generators": [
|
||||||
"GrishaGroupGenerator", "MolniyaGroupGenerator"
|
"GrishaGroupGenerator", "MolniyaGroupGenerator", "LaCombattanteIIGroupGenerator"
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|||||||
@ -25,6 +25,7 @@
|
|||||||
"MBT_T_55",
|
"MBT_T_55",
|
||||||
"MBT_T_72B",
|
"MBT_T_72B",
|
||||||
"MBT_T_80U",
|
"MBT_T_80U",
|
||||||
|
"LT_PT_76",
|
||||||
"SPAAA_ZSU_57_2",
|
"SPAAA_ZSU_57_2",
|
||||||
"SAM_SA_9_Strela_1_Gaskin_TEL"
|
"SAM_SA_9_Strela_1_Gaskin_TEL"
|
||||||
],
|
],
|
||||||
|
|||||||
@ -17,6 +17,7 @@
|
|||||||
"Grad_MRL_FDDM__FC",
|
"Grad_MRL_FDDM__FC",
|
||||||
"APC_MTLB",
|
"APC_MTLB",
|
||||||
"MBT_T_55",
|
"MBT_T_55",
|
||||||
|
"LT_PT_76",
|
||||||
"SPAAA_ZU_23_2_Mounted_Ural_375",
|
"SPAAA_ZU_23_2_Mounted_Ural_375",
|
||||||
"AAA_8_8cm_Flak_18",
|
"AAA_8_8cm_Flak_18",
|
||||||
"AAA_S_60_57mm"
|
"AAA_S_60_57mm"
|
||||||
|
|||||||
@ -22,6 +22,7 @@
|
|||||||
"APC_BTR_RD",
|
"APC_BTR_RD",
|
||||||
"IFV_BMD_1",
|
"IFV_BMD_1",
|
||||||
"IFV_BMP_1",
|
"IFV_BMP_1",
|
||||||
|
"LT_PT_76",
|
||||||
"MBT_T_55",
|
"MBT_T_55",
|
||||||
"SPAAA_ZU_23_2_Mounted_Ural_375",
|
"SPAAA_ZU_23_2_Mounted_Ural_375",
|
||||||
"SPAAA_ZSU_57_2",
|
"SPAAA_ZSU_57_2",
|
||||||
|
|||||||
@ -14,6 +14,7 @@
|
|||||||
"APC_BTR_80",
|
"APC_BTR_80",
|
||||||
"IFV_BMD_1",
|
"IFV_BMD_1",
|
||||||
"IFV_BMP_1",
|
"IFV_BMP_1",
|
||||||
|
"LT_PT_76",
|
||||||
"MBT_T_55",
|
"MBT_T_55",
|
||||||
"SPAAA_ZSU_57_2"
|
"SPAAA_ZSU_57_2"
|
||||||
],
|
],
|
||||||
|
|||||||
@ -27,6 +27,7 @@
|
|||||||
"APC_BTR_80",
|
"APC_BTR_80",
|
||||||
"IFV_BMD_1",
|
"IFV_BMD_1",
|
||||||
"IFV_BMP_1",
|
"IFV_BMP_1",
|
||||||
|
"LT_PT_76",
|
||||||
"MBT_T_55",
|
"MBT_T_55",
|
||||||
"SAM_SA_8_Osa_Gecko_TEL"
|
"SAM_SA_8_Osa_Gecko_TEL"
|
||||||
],
|
],
|
||||||
|
|||||||
@ -20,6 +20,7 @@
|
|||||||
"Scout_BRDM_2",
|
"Scout_BRDM_2",
|
||||||
"Tk_PzIV_H",
|
"Tk_PzIV_H",
|
||||||
"MBT_T_55",
|
"MBT_T_55",
|
||||||
|
"LT_PT_76",
|
||||||
"SPAAA_ZU_23_2_Mounted_Ural_375",
|
"SPAAA_ZU_23_2_Mounted_Ural_375",
|
||||||
"SPAAA_ZSU_57_2",
|
"SPAAA_ZSU_57_2",
|
||||||
"AAA_S_60_57mm"
|
"AAA_S_60_57mm"
|
||||||
|
|||||||
@ -20,6 +20,7 @@
|
|||||||
"frontline_units": [
|
"frontline_units": [
|
||||||
"Scout_BRDM_2",
|
"Scout_BRDM_2",
|
||||||
"MBT_T_55",
|
"MBT_T_55",
|
||||||
|
"LT_PT_76",
|
||||||
"Tk_PzIV_H",
|
"Tk_PzIV_H",
|
||||||
"SPG_StuG_III_Ausf__G",
|
"SPG_StuG_III_Ausf__G",
|
||||||
"SPG_Jagdpanzer_IV",
|
"SPG_Jagdpanzer_IV",
|
||||||
|
|||||||
@ -20,6 +20,7 @@
|
|||||||
"IFV_BMP_1",
|
"IFV_BMP_1",
|
||||||
"APC_MTLB",
|
"APC_MTLB",
|
||||||
"MBT_T_55",
|
"MBT_T_55",
|
||||||
|
"LT_PT_76",
|
||||||
"SPAAA_ZU_23_2_Mounted_Ural_375",
|
"SPAAA_ZU_23_2_Mounted_Ural_375",
|
||||||
"SPAAA_ZSU_57_2",
|
"SPAAA_ZSU_57_2",
|
||||||
"AAA_S_60_57mm"
|
"AAA_S_60_57mm"
|
||||||
|
|||||||
@ -21,6 +21,7 @@
|
|||||||
"IFV_BMP_1",
|
"IFV_BMP_1",
|
||||||
"APC_MTLB",
|
"APC_MTLB",
|
||||||
"MBT_T_55",
|
"MBT_T_55",
|
||||||
|
"LT_PT_76",
|
||||||
"MBT_T_72B",
|
"MBT_T_72B",
|
||||||
"SPAAA_ZU_23_2_Mounted_Ural_375",
|
"SPAAA_ZU_23_2_Mounted_Ural_375",
|
||||||
"SPAAA_ZSU_57_2",
|
"SPAAA_ZSU_57_2",
|
||||||
|
|||||||
@ -27,6 +27,7 @@
|
|||||||
"IFV_BMP_2",
|
"IFV_BMP_2",
|
||||||
"APC_BTR_80",
|
"APC_BTR_80",
|
||||||
"Scout_BRDM_2",
|
"Scout_BRDM_2",
|
||||||
|
"LT_PT_76",
|
||||||
"APC_MTLB",
|
"APC_MTLB",
|
||||||
"Scout_Cobra",
|
"Scout_Cobra",
|
||||||
"MBT_T_55",
|
"MBT_T_55",
|
||||||
|
|||||||
@ -25,7 +25,7 @@
|
|||||||
"SAM_Avenger__Stinger"
|
"SAM_Avenger__Stinger"
|
||||||
],
|
],
|
||||||
"artillery_units": [
|
"artillery_units": [
|
||||||
"SPH_M109_Paladin_155mm"
|
"SPH_T155_Firtina_155mm"
|
||||||
],
|
],
|
||||||
"logistics_units": [
|
"logistics_units": [
|
||||||
"Truck_M818_6x6"
|
"Truck_M818_6x6"
|
||||||
|
|||||||
@ -19,6 +19,7 @@
|
|||||||
],
|
],
|
||||||
"frontline_units": [
|
"frontline_units": [
|
||||||
"MBT_Challenger_II",
|
"MBT_Challenger_II",
|
||||||
|
"MBT_Chieftain_Mk_3",
|
||||||
"IFV_Warrior",
|
"IFV_Warrior",
|
||||||
"Scout_HMMWV",
|
"Scout_HMMWV",
|
||||||
"ATGM_HMMWV",
|
"ATGM_HMMWV",
|
||||||
|
|||||||
@ -570,7 +570,7 @@ class Waypoint {
|
|||||||
}
|
}
|
||||||
|
|
||||||
includeInPath() {
|
includeInPath() {
|
||||||
return !this.waypoint.isDivert;
|
return !this.waypoint.isDivert && !this.waypoint.isBullseye;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user