mirror of
https://github.com/dcs-liberation/dcs_liberation.git
synced 2025-11-10 14:22:26 +00:00
Compare commits
13 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
a7824039e3 | ||
|
|
bb9247e821 | ||
|
|
4bc04ec031 | ||
|
|
8fd91e6c5c | ||
|
|
ee137d086a | ||
|
|
fcd81850cb | ||
|
|
aa4b07d024 | ||
|
|
16a096d288 | ||
|
|
b219b2a71b | ||
|
|
ce70242c35 | ||
|
|
dec01e2611 | ||
|
|
4373d89661 | ||
|
|
c73290eebb |
32
changelog.md
32
changelog.md
@@ -1,10 +1,39 @@
|
|||||||
|
#2.0 RC 7
|
||||||
|
|
||||||
|
##Features/Improvements :
|
||||||
|
* **[Units/Factions]** Added P-47D-30 for factions allies_1944
|
||||||
|
* **[Units/Factions]** Replaced S3-B Tanker by KC130 for most factions
|
||||||
|
* **[Mission Generator]** AI Flight generator has been reworked
|
||||||
|
* **[Mission Generator]** Add PP points for JF-17 on STRIKE missions
|
||||||
|
* **[Mission Generator]** Add ST point for F-14B on STRIKE missions
|
||||||
|
* **[Mission Generator]** Flights with client slots will never be delayed
|
||||||
|
* **[Mission Generator]** AI units can start from parking (Added a new setting)
|
||||||
|
* **[Mission Generator]** Tacan for carrier will only be in Mode X from now
|
||||||
|
* **[Mission Generator]** RTB waypoints for autogenerated flights
|
||||||
|
* **[Info Panel]** Added information about destroyed buildings in info panel
|
||||||
|
* **[Info Panel]** Added information about destroyed units at SAM site in info panel
|
||||||
|
* **[Info Panel]** Added information about units destroyed outside the frontline in the debriefing window
|
||||||
|
* **[Info Panel]** Added information about buildings destroyed in the debriefing window
|
||||||
|
* **[Map]** Tooltip now contains the list of building for Strike targets on the map
|
||||||
|
* **[Map]** Added "Oil derrick" building
|
||||||
|
* **[Misc]** Made it possible to setup DCS Saved Games directory and DCS installation directory manually
|
||||||
|
|
||||||
|
##Fixed issues :
|
||||||
|
* **[Mission Generator]** When playing as RED the activation trigger would not be properly generated
|
||||||
|
* **[Mission Generator]** Changed "strike" payload for Su-24M that was innefective
|
||||||
|
* **[Mission Generator]** Changed "strike" payload for JF-17 to use LS-6 bombs instead of GBU
|
||||||
|
* **[Mission Generator]** FW-190A8 is now properly considered as a flyable
|
||||||
|
* **[Maps/Campaign]** Now using Vasiani airport instead of Soganlung in North Caucasus campaign (More parking slots)
|
||||||
|
* **[Info Panel]** Message displayed on base capture event stated that the ennemy captured an airbase, while it was the player who captured it.
|
||||||
|
* **[Map]** Graphical glitch on map when one building of an objective was destroyed, but not the others
|
||||||
|
* **[Map]** Change power station template. (Buildings could end up superposed).
|
||||||
|
|
||||||
#2.0 RC 6
|
#2.0 RC 6
|
||||||
|
|
||||||
Saves file from RC5 are not compatible with the new version.
|
Saves file from RC5 are not compatible with the new version.
|
||||||
Sorry :(
|
Sorry :(
|
||||||
|
|
||||||
##Features/Improvements :
|
##Features/Improvements :
|
||||||
|
|
||||||
* **[Units/Factions]** Supercarrier support (You have to go to settings to enable it, if you have the supercarrier module)
|
* **[Units/Factions]** Supercarrier support (You have to go to settings to enable it, if you have the supercarrier module)
|
||||||
* **[Units/Factions]** Added 'Modern Bluefor' factions, containing all most popular DCS flyable units
|
* **[Units/Factions]** Added 'Modern Bluefor' factions, containing all most popular DCS flyable units
|
||||||
* **[Units/Factions]** Factions US 2005 / 1990 will now sometimes have Arleigh Burke class ships instead of Perry as carrier escorts
|
* **[Units/Factions]** Factions US 2005 / 1990 will now sometimes have Arleigh Burke class ships instead of Perry as carrier escorts
|
||||||
@@ -19,7 +48,6 @@ Sorry :(
|
|||||||
* **[UX]** : Improved flight selection behaviour in the Mission Planning Window
|
* **[UX]** : Improved flight selection behaviour in the Mission Planning Window
|
||||||
|
|
||||||
##Fixed issues :
|
##Fixed issues :
|
||||||
|
|
||||||
* **[Mission Generator]** Payloads were not correctly assigned in the release version.
|
* **[Mission Generator]** Payloads were not correctly assigned in the release version.
|
||||||
* **[Mission Generator]** Game generation does not work when "no night mission" settings was selected and the current time was "day"
|
* **[Mission Generator]** Game generation does not work when "no night mission" settings was selected and the current time was "day"
|
||||||
* **[Mission Generator]** Game generation does not work when the player selected faction has no AWACS
|
* **[Mission Generator]** Game generation does not work when the player selected faction has no AWACS
|
||||||
|
|||||||
15
game/db.py
15
game/db.py
@@ -150,6 +150,7 @@ PRICES = {
|
|||||||
S_3B_Tanker: 13,
|
S_3B_Tanker: 13,
|
||||||
IL_78M: 13,
|
IL_78M: 13,
|
||||||
KC_135: 13,
|
KC_135: 13,
|
||||||
|
KC130: 13,
|
||||||
|
|
||||||
A_50: 8,
|
A_50: 8,
|
||||||
E_3A: 8,
|
E_3A: 8,
|
||||||
@@ -158,6 +159,7 @@ PRICES = {
|
|||||||
# WW2
|
# WW2
|
||||||
P_51D_30_NA: 6,
|
P_51D_30_NA: 6,
|
||||||
P_51D: 6,
|
P_51D: 6,
|
||||||
|
P_47D_30: 6,
|
||||||
|
|
||||||
# armor
|
# armor
|
||||||
Armor.APC_MTLB: 4,
|
Armor.APC_MTLB: 4,
|
||||||
@@ -344,6 +346,7 @@ UNIT_BY_TASK = {
|
|||||||
Mi_24V,
|
Mi_24V,
|
||||||
MiG_27K,
|
MiG_27K,
|
||||||
A_20G,
|
A_20G,
|
||||||
|
P_47D_30,
|
||||||
Ju_88A4,
|
Ju_88A4,
|
||||||
],
|
],
|
||||||
Transport: [
|
Transport: [
|
||||||
@@ -357,6 +360,7 @@ UNIT_BY_TASK = {
|
|||||||
Refueling: [
|
Refueling: [
|
||||||
IL_78M,
|
IL_78M,
|
||||||
KC_135,
|
KC_135,
|
||||||
|
KC130,
|
||||||
S_3B_Tanker,
|
S_3B_Tanker,
|
||||||
],
|
],
|
||||||
AWACS: [E_3A, A_50, ],
|
AWACS: [E_3A, A_50, ],
|
||||||
@@ -793,7 +797,8 @@ TIME_PERIODS = {
|
|||||||
|
|
||||||
REWARDS = {
|
REWARDS = {
|
||||||
"power": 4, "warehouse": 2, "fuel": 2, "ammo": 2,
|
"power": 4, "warehouse": 2, "fuel": 2, "ammo": 2,
|
||||||
"farp": 1, "fob": 1, "factory": 10, "comms": 10, "oil": 10
|
"farp": 1, "fob": 1, "factory": 10, "comms": 10, "oil": 10,
|
||||||
|
"derrick": 8
|
||||||
}
|
}
|
||||||
|
|
||||||
# Base post-turn bonus value
|
# Base post-turn bonus value
|
||||||
@@ -1029,14 +1034,6 @@ def _validate_db():
|
|||||||
assert unit_type not in total_set, "{} is duplicate for task {}".format(unit_type, t)
|
assert unit_type not in total_set, "{} is duplicate for task {}".format(unit_type, t)
|
||||||
total_set.add(unit_type)
|
total_set.add(unit_type)
|
||||||
|
|
||||||
# check country allegiance
|
|
||||||
for unit_type in total_set:
|
|
||||||
did_find = False
|
|
||||||
for country_units_list in FACTIONS.values():
|
|
||||||
if unit_type in country_units_list["units"]:
|
|
||||||
did_find = True
|
|
||||||
print("WARN : {} not in country list".format(unit_type))
|
|
||||||
|
|
||||||
# check prices
|
# check prices
|
||||||
for unit_type in total_set:
|
for unit_type in total_set:
|
||||||
assert unit_type in PRICES, "{} not in prices".format(unit_type)
|
assert unit_type in PRICES, "{} not in prices".format(unit_type)
|
||||||
|
|||||||
@@ -127,17 +127,6 @@ class Event:
|
|||||||
self.operation.current_mission.save(persistency.mission_path_for("liberation_nextturn.miz"))
|
self.operation.current_mission.save(persistency.mission_path_for("liberation_nextturn.miz"))
|
||||||
self.environment_settings = self.operation.environment_settings
|
self.environment_settings = self.operation.environment_settings
|
||||||
|
|
||||||
def generate_quick(self):
|
|
||||||
pass
|
|
||||||
# TODO : This is not needed anymore. The player can start mission in flight from the flight planner if he want it to be quick.
|
|
||||||
# TODO : remove this method
|
|
||||||
#self.operation.is_awacs_enabled = self.is_awacs_enabled
|
|
||||||
#self.operation.environment_settings = self.environment_settings
|
|
||||||
#
|
|
||||||
#self.operation.prepare(self.game.theater.terrain, is_quick=True)
|
|
||||||
#self.operation.generate()
|
|
||||||
#self.operation.current_mission.save(persistency.mission_path_for("liberation_nextturn_quick.miz"))
|
|
||||||
|
|
||||||
def commit(self, debriefing: Debriefing):
|
def commit(self, debriefing: Debriefing):
|
||||||
|
|
||||||
logging.info("Commiting mission results")
|
logging.info("Commiting mission results")
|
||||||
@@ -190,16 +179,30 @@ class Event:
|
|||||||
logging.info("cp {} killing ground object {}".format(cp, ground_object.string_identifier))
|
logging.info("cp {} killing ground object {}".format(cp, ground_object.string_identifier))
|
||||||
cp.ground_objects[i].is_dead = True
|
cp.ground_objects[i].is_dead = True
|
||||||
|
|
||||||
|
info = Information("Building destroyed",
|
||||||
|
ground_object.dcs_identifier + " has been destroyed at location " + ground_object.obj_name,
|
||||||
|
self.game.turn)
|
||||||
|
self.game.informations.append(info)
|
||||||
|
|
||||||
|
|
||||||
# -- AA Site groups
|
# -- AA Site groups
|
||||||
|
destroyed_units = 0
|
||||||
|
info = Information("Units destroyed at " + ground_object.obj_name,
|
||||||
|
"",
|
||||||
|
self.game.turn)
|
||||||
for i, ground_object in enumerate(cp.ground_objects):
|
for i, ground_object in enumerate(cp.ground_objects):
|
||||||
if ground_object.dcs_identifier in ["AA", "CARRIER", "LHA"]:
|
if ground_object.dcs_identifier in ["AA", "CARRIER", "LHA"]:
|
||||||
for g in ground_object.groups:
|
for g in ground_object.groups:
|
||||||
for u in g.units:
|
for u in g.units:
|
||||||
if u.name == destroyed_ground_unit_name:
|
if u.name == destroyed_ground_unit_name:
|
||||||
g.units.remove(u)
|
g.units.remove(u)
|
||||||
|
destroyed_units = destroyed_units + 1
|
||||||
|
info.text = u.type
|
||||||
ucount = sum([len(g.units) for g in ground_object.groups])
|
ucount = sum([len(g.units) for g in ground_object.groups])
|
||||||
if ucount == 0:
|
if ucount == 0:
|
||||||
ground_object.is_dead = True
|
ground_object.is_dead = True
|
||||||
|
if destroyed_units > 0:
|
||||||
|
self.game.informations.append(info)
|
||||||
|
|
||||||
# ------------------------------
|
# ------------------------------
|
||||||
# Captured bases
|
# Captured bases
|
||||||
@@ -213,28 +216,27 @@ class Event:
|
|||||||
id = int(captured.split("||")[0])
|
id = int(captured.split("||")[0])
|
||||||
new_owner_coalition = int(captured.split("||")[1])
|
new_owner_coalition = int(captured.split("||")[1])
|
||||||
|
|
||||||
|
captured_cps = []
|
||||||
for cp in self.game.theater.controlpoints:
|
for cp in self.game.theater.controlpoints:
|
||||||
if cp.id == id:
|
if cp.id == id:
|
||||||
|
|
||||||
pname = ""
|
|
||||||
if cp.captured and new_owner_coalition != coalition:
|
if cp.captured and new_owner_coalition != coalition:
|
||||||
cp.captured = False
|
cp.captured = False
|
||||||
info = Information(cp.name + " lost !",
|
info = Information(cp.name + " lost !", "The ennemy took control of " + cp.name + "\nShame on us !", self.game.turn)
|
||||||
"The ennemy took control of " + cp.name + "\nShame on us !",
|
|
||||||
self.game.turn)
|
|
||||||
self.game.informations.append(info)
|
self.game.informations.append(info)
|
||||||
pname = self.game.enemy_name
|
pname = self.game.enemy_name
|
||||||
|
captured_cps.append(cp)
|
||||||
elif not(cp.captured) and new_owner_coalition == coalition:
|
elif not(cp.captured) and new_owner_coalition == coalition:
|
||||||
cp.captured = True
|
cp.captured = True
|
||||||
info = Information(cp.name + " captured !", "The ennemy took control of " + cp.name + "\nShame on us !", self.game.turn)
|
info = Information(cp.name + " captured !", "We took control of " + cp.name + "! Great job !", self.game.turn)
|
||||||
self.game.informations.append(info)
|
self.game.informations.append(info)
|
||||||
pname = self.game.player_name
|
pname = self.game.player_name
|
||||||
|
captured_cps.append(cp)
|
||||||
else:
|
else:
|
||||||
continue
|
continue
|
||||||
|
|
||||||
cp.base.aircraft = {}
|
cp.base.aircraft = {}
|
||||||
cp.base.armor = {}
|
cp.base.armor = {}
|
||||||
cp.base.aa = {}
|
|
||||||
|
|
||||||
airbase_def_id = 0
|
airbase_def_id = 0
|
||||||
for g in cp.ground_objects:
|
for g in cp.ground_objects:
|
||||||
@@ -243,6 +245,10 @@ class Event:
|
|||||||
generate_airbase_defense_group(airbase_def_id, g, pname, self.game, cp)
|
generate_airbase_defense_group(airbase_def_id, g, pname, self.game, cp)
|
||||||
airbase_def_id = airbase_def_id + 1
|
airbase_def_id = airbase_def_id + 1
|
||||||
|
|
||||||
|
for cp in captured_cps:
|
||||||
|
logging.info("Will run redeploy for " + cp.name)
|
||||||
|
self.redeploy_units(cp)
|
||||||
|
|
||||||
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
print(e)
|
print(e)
|
||||||
@@ -321,6 +327,47 @@ class Event:
|
|||||||
def skip(self):
|
def skip(self):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
def redeploy_units(self, cp):
|
||||||
|
""""
|
||||||
|
Auto redeploy units to newly captured base
|
||||||
|
"""
|
||||||
|
|
||||||
|
ally_connected_cps = [ocp for ocp in cp.connected_points if cp.captured == ocp.captured]
|
||||||
|
enemy_connected_cps = [ocp for ocp in cp.connected_points if cp.captured != ocp.captured]
|
||||||
|
|
||||||
|
# If the newly captured cp does not have enemy connected cp,
|
||||||
|
# then it is not necessary to redeploy frontline units there.
|
||||||
|
if len(enemy_connected_cps) == 0:
|
||||||
|
return
|
||||||
|
else:
|
||||||
|
# From each ally cp, send reinforcements
|
||||||
|
for ally_cp in ally_connected_cps:
|
||||||
|
total_units_redeployed = 0
|
||||||
|
own_enemy_cp = [ocp for ocp in ally_cp.connected_points if ally_cp.captured != ocp.captured]
|
||||||
|
|
||||||
|
moved_units = {}
|
||||||
|
|
||||||
|
# If the connected base, does not have any more enemy cp connected.
|
||||||
|
# Or if it is not the opponent redeploying forces there (enemy AI will never redeploy all their forces at once)
|
||||||
|
if len(own_enemy_cp) > 0 or not cp.captured:
|
||||||
|
for frontline_unit, count in ally_cp.base.armor.items():
|
||||||
|
moved_units[frontline_unit] = int(count/2)
|
||||||
|
total_units_redeployed = total_units_redeployed + int(count/2)
|
||||||
|
else: # So if the old base, does not have any more enemy cp connected, or if it is an enemy base
|
||||||
|
for frontline_unit, count in ally_cp.base.armor.items():
|
||||||
|
moved_units[frontline_unit] = count
|
||||||
|
total_units_redeployed = total_units_redeployed + count
|
||||||
|
|
||||||
|
cp.base.commision_units(moved_units)
|
||||||
|
ally_cp.base.commit_losses(moved_units)
|
||||||
|
|
||||||
|
if total_units_redeployed > 0:
|
||||||
|
info = Information("Units redeployed", "", self.game.turn)
|
||||||
|
info.text = str(total_units_redeployed) + " units have been redeployed from " + ally_cp.name + " to " + cp.name
|
||||||
|
self.game.informations.append(info)
|
||||||
|
logging.info(info.text)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
class UnitsDeliveryEvent(Event):
|
class UnitsDeliveryEvent(Event):
|
||||||
informational = True
|
informational = True
|
||||||
|
|||||||
@@ -24,7 +24,7 @@ BLUEFOR_MODERN = {
|
|||||||
AJS37,
|
AJS37,
|
||||||
|
|
||||||
KC_135,
|
KC_135,
|
||||||
S_3B_Tanker,
|
KC130,
|
||||||
C_130,
|
C_130,
|
||||||
E_3A,
|
E_3A,
|
||||||
|
|
||||||
|
|||||||
@@ -11,7 +11,7 @@ France_1995 = {
|
|||||||
Mirage_2000_5,
|
Mirage_2000_5,
|
||||||
|
|
||||||
KC_135,
|
KC_135,
|
||||||
S_3B_Tanker,
|
KC130,
|
||||||
C_130,
|
C_130,
|
||||||
E_3A,
|
E_3A,
|
||||||
|
|
||||||
|
|||||||
@@ -12,7 +12,7 @@ France_2005 = {
|
|||||||
FA_18C_hornet, # Standing as Rafale M
|
FA_18C_hornet, # Standing as Rafale M
|
||||||
|
|
||||||
KC_135,
|
KC_135,
|
||||||
S_3B_Tanker,
|
KC130,
|
||||||
C_130,
|
C_130,
|
||||||
E_3A,
|
E_3A,
|
||||||
|
|
||||||
|
|||||||
@@ -12,7 +12,7 @@ Germany_1990 = {
|
|||||||
F_4E,
|
F_4E,
|
||||||
|
|
||||||
KC_135,
|
KC_135,
|
||||||
S_3B_Tanker,
|
KC130,
|
||||||
C_130,
|
C_130,
|
||||||
E_3A,
|
E_3A,
|
||||||
|
|
||||||
|
|||||||
@@ -15,7 +15,7 @@ India_2010 = {
|
|||||||
Su_30,
|
Su_30,
|
||||||
|
|
||||||
KC_135,
|
KC_135,
|
||||||
S_3B_Tanker,
|
KC130,
|
||||||
C_130,
|
C_130,
|
||||||
E_3A,
|
E_3A,
|
||||||
|
|
||||||
|
|||||||
@@ -12,7 +12,7 @@ Israel_2000 = {
|
|||||||
F_4E,
|
F_4E,
|
||||||
|
|
||||||
KC_135,
|
KC_135,
|
||||||
S_3B_Tanker,
|
KC130,
|
||||||
C_130,
|
C_130,
|
||||||
E_3A,
|
E_3A,
|
||||||
|
|
||||||
|
|||||||
@@ -11,7 +11,7 @@ Netherlands_1990 = {
|
|||||||
F_5E_3,
|
F_5E_3,
|
||||||
|
|
||||||
KC_135,
|
KC_135,
|
||||||
S_3B_Tanker,
|
KC130,
|
||||||
C_130,
|
C_130,
|
||||||
E_3A,
|
E_3A,
|
||||||
|
|
||||||
|
|||||||
@@ -12,7 +12,7 @@ Spain_1990 = {
|
|||||||
C_101CC,
|
C_101CC,
|
||||||
|
|
||||||
KC_135,
|
KC_135,
|
||||||
S_3B_Tanker,
|
KC130,
|
||||||
C_130,
|
C_130,
|
||||||
E_3A,
|
E_3A,
|
||||||
|
|
||||||
|
|||||||
@@ -11,7 +11,7 @@ Turkey_2005 = {
|
|||||||
F_4E,
|
F_4E,
|
||||||
|
|
||||||
KC_135,
|
KC_135,
|
||||||
S_3B_Tanker,
|
KC130,
|
||||||
C_130,
|
C_130,
|
||||||
E_3A,
|
E_3A,
|
||||||
|
|
||||||
|
|||||||
@@ -12,7 +12,7 @@ UAE_2005 = {
|
|||||||
F_16C_50,
|
F_16C_50,
|
||||||
|
|
||||||
KC_135,
|
KC_135,
|
||||||
S_3B_Tanker,
|
KC130,
|
||||||
C_130,
|
C_130,
|
||||||
E_3A,
|
E_3A,
|
||||||
|
|
||||||
|
|||||||
@@ -12,7 +12,7 @@ UnitedKingdom_1990 = {
|
|||||||
F_4E,
|
F_4E,
|
||||||
|
|
||||||
KC_135,
|
KC_135,
|
||||||
S_3B_Tanker,
|
KC130,
|
||||||
C_130,
|
C_130,
|
||||||
E_3A,
|
E_3A,
|
||||||
|
|
||||||
|
|||||||
@@ -8,6 +8,7 @@ USA_1944 = {
|
|||||||
"units": [
|
"units": [
|
||||||
P_51D,
|
P_51D,
|
||||||
P_51D_30_NA,
|
P_51D_30_NA,
|
||||||
|
P_47D_30,
|
||||||
SpitfireLFMkIX,
|
SpitfireLFMkIX,
|
||||||
SpitfireLFMkIXCW,
|
SpitfireLFMkIXCW,
|
||||||
A_20G,
|
A_20G,
|
||||||
|
|||||||
@@ -11,7 +11,7 @@ USA_1955 = {
|
|||||||
P_51D,
|
P_51D,
|
||||||
|
|
||||||
KC_135,
|
KC_135,
|
||||||
S_3B_Tanker,
|
KC130,
|
||||||
C_130,
|
C_130,
|
||||||
E_3A,
|
E_3A,
|
||||||
|
|
||||||
|
|||||||
@@ -11,7 +11,7 @@ USA_1960 = {
|
|||||||
P_51D,
|
P_51D,
|
||||||
|
|
||||||
KC_135,
|
KC_135,
|
||||||
S_3B_Tanker,
|
KC130,
|
||||||
C_130,
|
C_130,
|
||||||
E_3A,
|
E_3A,
|
||||||
|
|
||||||
|
|||||||
@@ -12,7 +12,7 @@ USA_1965 = {
|
|||||||
F_4E,
|
F_4E,
|
||||||
|
|
||||||
KC_135,
|
KC_135,
|
||||||
S_3B_Tanker,
|
KC130,
|
||||||
C_130,
|
C_130,
|
||||||
E_3A,
|
E_3A,
|
||||||
|
|
||||||
|
|||||||
@@ -17,7 +17,7 @@ USA_1990 = {
|
|||||||
B_1B,
|
B_1B,
|
||||||
|
|
||||||
KC_135,
|
KC_135,
|
||||||
S_3B_Tanker,
|
KC130,
|
||||||
C_130,
|
C_130,
|
||||||
E_3A,
|
E_3A,
|
||||||
|
|
||||||
|
|||||||
@@ -12,12 +12,11 @@ USA_2005 = {
|
|||||||
FA_18C_hornet,
|
FA_18C_hornet,
|
||||||
F_16C_50,
|
F_16C_50,
|
||||||
JF_17,
|
JF_17,
|
||||||
|
|
||||||
A_10C,
|
A_10C,
|
||||||
AV8BNA,
|
AV8BNA,
|
||||||
|
|
||||||
KC_135,
|
KC_135,
|
||||||
S_3B_Tanker,
|
KC130,
|
||||||
C_130,
|
C_130,
|
||||||
E_3A,
|
E_3A,
|
||||||
|
|
||||||
|
|||||||
15
game/game.py
15
game/game.py
@@ -145,11 +145,8 @@ class Game:
|
|||||||
|
|
||||||
def initiate_event(self, event: Event):
|
def initiate_event(self, event: Event):
|
||||||
assert event in self.events
|
assert event in self.events
|
||||||
|
|
||||||
logging.info("Generating {} (regular)".format(event))
|
logging.info("Generating {} (regular)".format(event))
|
||||||
event.generate()
|
event.generate()
|
||||||
logging.info("Generating {} (quick)".format(event))
|
|
||||||
event.generate_quick()
|
|
||||||
|
|
||||||
def finish_event(self, event: Event, debriefing: Debriefing):
|
def finish_event(self, event: Event, debriefing: Debriefing):
|
||||||
logging.info("Finishing event {}".format(event))
|
logging.info("Finishing event {}".format(event))
|
||||||
@@ -168,6 +165,18 @@ class Game:
|
|||||||
else:
|
else:
|
||||||
return event.name == self.player_name
|
return event.name == self.player_name
|
||||||
|
|
||||||
|
def get_player_coalition_id(self):
|
||||||
|
if self.player_country in db.BLUEFOR_FACTIONS:
|
||||||
|
return 2
|
||||||
|
else:
|
||||||
|
return 1
|
||||||
|
|
||||||
|
def get_enemy_coalition_id(self):
|
||||||
|
if self.get_player_coalition_id() == 1:
|
||||||
|
return 2
|
||||||
|
else:
|
||||||
|
return 1
|
||||||
|
|
||||||
def pass_turn(self, no_action=False, ignored_cps: typing.Collection[ControlPoint] = None):
|
def pass_turn(self, no_action=False, ignored_cps: typing.Collection[ControlPoint] = None):
|
||||||
logging.info("Pass turn")
|
logging.info("Pass turn")
|
||||||
self.informations.append(Information("End of turn #" + str(self.turn), "-" * 40, 0))
|
self.informations.append(Information("End of turn #" + str(self.turn), "-" * 40, 0))
|
||||||
|
|||||||
@@ -47,11 +47,9 @@ class GameStats:
|
|||||||
for cp in game.theater.controlpoints:
|
for cp in game.theater.controlpoints:
|
||||||
if cp.captured:
|
if cp.captured:
|
||||||
turn_data.allied_units.aircraft_count += sum(cp.base.aircraft.values())
|
turn_data.allied_units.aircraft_count += sum(cp.base.aircraft.values())
|
||||||
turn_data.allied_units.sam_count += sum(cp.base.aa.values())
|
|
||||||
turn_data.allied_units.vehicles_count += sum(cp.base.armor.values())
|
turn_data.allied_units.vehicles_count += sum(cp.base.armor.values())
|
||||||
else:
|
else:
|
||||||
turn_data.enemy_units.aircraft_count += sum(cp.base.aircraft.values())
|
turn_data.enemy_units.aircraft_count += sum(cp.base.aircraft.values())
|
||||||
turn_data.enemy_units.sam_count += sum(cp.base.aa.values())
|
|
||||||
turn_data.enemy_units.vehicles_count += sum(cp.base.armor.values())
|
turn_data.enemy_units.vehicles_count += sum(cp.base.armor.values())
|
||||||
|
|
||||||
self.data_per_turn.append(turn_data)
|
self.data_per_turn.append(turn_data)
|
||||||
|
|||||||
@@ -21,5 +21,6 @@ class Settings:
|
|||||||
perf_artillery = True
|
perf_artillery = True
|
||||||
perf_moving_units = True
|
perf_moving_units = True
|
||||||
perf_infantry = True
|
perf_infantry = True
|
||||||
|
perf_ai_parking_start = True
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
277
gen/aircraft.py
277
gen/aircraft.py
@@ -1,12 +1,12 @@
|
|||||||
from dcs.action import ActivateGroup
|
from dcs.action import ActivateGroup, AITaskPush
|
||||||
from dcs.condition import TimeAfter, CoalitionHasAirdrome
|
from dcs.condition import TimeAfter, CoalitionHasAirdrome
|
||||||
from dcs.helicopters import UH_1H
|
from dcs.helicopters import UH_1H
|
||||||
from dcs.terrain.terrain import NoParkingSlotError
|
from dcs.terrain.terrain import NoParkingSlotError
|
||||||
from dcs.triggers import TriggerOnce, Event
|
from dcs.triggers import TriggerOnce, Event
|
||||||
|
|
||||||
from game.settings import Settings
|
from game.settings import Settings
|
||||||
from gen.flights.ai_flight_planner import FlightPlanner
|
from gen.flights.ai_flight_planner import FlightPlanner, CAP_DEFAULT_ENGAGE_DISTANCE, nm_to_meter
|
||||||
from gen.flights.flight import Flight, FlightType
|
from gen.flights.flight import Flight, FlightType, FlightWaypointType
|
||||||
from .conflictgen import *
|
from .conflictgen import *
|
||||||
from .naming import *
|
from .naming import *
|
||||||
from .triggergen import TRIGGER_WAYPOINT_OFFSET
|
from .triggergen import TRIGGER_WAYPOINT_OFFSET
|
||||||
@@ -51,33 +51,6 @@ class AircraftConflictGenerator:
|
|||||||
def _start_type(self) -> StartType:
|
def _start_type(self) -> StartType:
|
||||||
return self.settings.cold_start and StartType.Cold or StartType.Warm
|
return self.settings.cold_start and StartType.Cold or StartType.Warm
|
||||||
|
|
||||||
def _group_point(self, point) -> Point:
|
|
||||||
distance = randint(
|
|
||||||
int(self.conflict.size * SPREAD_DISTANCE_FACTOR[0]),
|
|
||||||
int(self.conflict.size * SPREAD_DISTANCE_FACTOR[1]),
|
|
||||||
)
|
|
||||||
return point.random_point_within(distance, self.conflict.size * SPREAD_DISTANCE_FACTOR[0])
|
|
||||||
|
|
||||||
def _split_to_groups(self, dict: db.PlaneDict, clients: db.PlaneDict = None) -> typing.Collection[typing.Tuple[FlyingType, int, int]]:
|
|
||||||
for flying_type, count in dict.items():
|
|
||||||
if clients:
|
|
||||||
client_count = clients.get(flying_type, 0)
|
|
||||||
else:
|
|
||||||
client_count = 0
|
|
||||||
|
|
||||||
if flying_type == F_14B:
|
|
||||||
# workaround since 2 and 3 tomcat collide on carrier
|
|
||||||
group_size = 2
|
|
||||||
else:
|
|
||||||
group_size = 4
|
|
||||||
|
|
||||||
while count > 0:
|
|
||||||
group_size = min(count, group_size)
|
|
||||||
client_size = max(min(client_count, group_size), 0)
|
|
||||||
|
|
||||||
yield (flying_type, group_size, client_size)
|
|
||||||
count -= group_size
|
|
||||||
client_count -= client_size
|
|
||||||
|
|
||||||
def _setup_group(self, group: FlyingGroup, for_task: typing.Type[Task], client_count: int):
|
def _setup_group(self, group: FlyingGroup, for_task: typing.Type[Task], client_count: int):
|
||||||
did_load_loadout = False
|
did_load_loadout = False
|
||||||
@@ -251,37 +224,6 @@ class AircraftConflictGenerator:
|
|||||||
else:
|
else:
|
||||||
assert False
|
assert False
|
||||||
|
|
||||||
def _generate_escort(self, side: Country, units: db.PlaneDict, clients: db.PlaneDict, at: db.StartingPosition, cp, is_quick=False, should_orbit=False):
|
|
||||||
groups = []
|
|
||||||
for flying_type, count, client_count in self._split_to_groups(units, clients):
|
|
||||||
group = self._generate_group(
|
|
||||||
name=namegen.next_unit_name(side, cp.id, flying_type),
|
|
||||||
side=side,
|
|
||||||
unit_type=flying_type,
|
|
||||||
count=count,
|
|
||||||
client_count=client_count,
|
|
||||||
at=at)
|
|
||||||
|
|
||||||
group.task = Escort.name
|
|
||||||
self._setup_group(group, CAP, client_count)
|
|
||||||
|
|
||||||
for escorted_group, waypoint_index in self.escort_targets:
|
|
||||||
waypoint_index += 1
|
|
||||||
if not is_quick:
|
|
||||||
waypoint_index += TRIGGER_WAYPOINT_OFFSET
|
|
||||||
|
|
||||||
group.points[0].tasks.append(EscortTaskAction(escorted_group.id, engagement_max_dist=ESCORT_ENGAGEMENT_MAX_DIST, lastwpt=waypoint_index))
|
|
||||||
|
|
||||||
if should_orbit:
|
|
||||||
orbit_task = ControlledTask(OrbitAction(ATTACK_CIRCLE_ALT, pattern=OrbitAction.OrbitPattern.Circle))
|
|
||||||
orbit_task.stop_after_duration(ATTACK_CIRCLE_DURATION * 60)
|
|
||||||
|
|
||||||
orbit_waypoint = self._add_radio_waypoint(group, self.conflict.position, CAS_ALTITUDE)
|
|
||||||
orbit_waypoint.tasks.append(orbit_task)
|
|
||||||
orbit_waypoint.tasks.append(EngageTargets(max_distance=DEFENCE_ENGAGEMENT_MAX_DISTANCE))
|
|
||||||
|
|
||||||
groups.append(group)
|
|
||||||
return groups
|
|
||||||
|
|
||||||
def _setup_custom_payload(self, flight, group:FlyingGroup):
|
def _setup_custom_payload(self, flight, group:FlyingGroup):
|
||||||
if flight.use_custom_loadout:
|
if flight.use_custom_loadout:
|
||||||
@@ -303,74 +245,56 @@ class AircraftConflictGenerator:
|
|||||||
|
|
||||||
def generate_flights(self, cp, country, flight_planner:FlightPlanner):
|
def generate_flights(self, cp, country, flight_planner:FlightPlanner):
|
||||||
|
|
||||||
for flight in flight_planner.interceptor_flights:
|
for flight in flight_planner.flights:
|
||||||
group = self.generate_planned_flight(cp, country, flight)
|
|
||||||
self.setup_group_as_intercept_flight(group, flight)
|
|
||||||
self._setup_custom_payload(flight, group)
|
|
||||||
self.setup_group_activation_trigger(flight, group)
|
|
||||||
|
|
||||||
for flight in flight_planner.cap_flights:
|
|
||||||
group = self.generate_planned_flight(cp, country, flight)
|
|
||||||
self.setup_group_as_cap_flight(group, flight)
|
|
||||||
self._setup_custom_payload(flight, group)
|
|
||||||
self.setup_group_activation_trigger(flight, group)
|
|
||||||
|
|
||||||
for flight in flight_planner.cas_flights:
|
|
||||||
group = self.generate_planned_flight(cp, country, flight)
|
|
||||||
self.setup_group_as_cas_flight(group, flight)
|
|
||||||
self._setup_custom_payload(flight, group)
|
|
||||||
self.setup_group_activation_trigger(flight, group)
|
|
||||||
|
|
||||||
for flight in flight_planner.sead_flights:
|
|
||||||
group = self.generate_planned_flight(cp, country, flight)
|
|
||||||
self.setup_group_as_sead_flight(group, flight)
|
|
||||||
self._setup_custom_payload(flight, group)
|
|
||||||
self.setup_group_activation_trigger(flight, group)
|
|
||||||
|
|
||||||
for flight in flight_planner.strike_flights:
|
|
||||||
group = self.generate_planned_flight(cp, country, flight)
|
|
||||||
self.setup_group_as_strike_flight(group, flight)
|
|
||||||
self._setup_custom_payload(flight, group)
|
|
||||||
self.setup_group_activation_trigger(flight, group)
|
|
||||||
|
|
||||||
for flight in flight_planner.custom_flights:
|
|
||||||
group = self.generate_planned_flight(cp, country, flight)
|
group = self.generate_planned_flight(cp, country, flight)
|
||||||
if flight.flight_type == FlightType.INTERCEPTION:
|
if flight.flight_type == FlightType.INTERCEPTION:
|
||||||
self.setup_group_as_intercept_flight(group, flight)
|
self.setup_group_as_intercept_flight(group, flight)
|
||||||
elif flight.flight_type in [FlightType.CAP, FlightType.TARCAP, FlightType.BARCAP]:
|
self._setup_custom_payload(flight, group)
|
||||||
self.setup_group_as_cap_flight(group, flight)
|
|
||||||
elif flight.flight_type in [FlightType.CAS, FlightType.BAI]:
|
|
||||||
self.setup_group_as_cas_flight(group, flight)
|
|
||||||
elif flight.flight_type in [FlightType.STRIKE]:
|
|
||||||
self.setup_group_as_strike_flight(group, flight)
|
|
||||||
elif flight.flight_type in [FlightType.ANTISHIP]:
|
|
||||||
self.setup_group_as_antiship_flight(group, flight)
|
|
||||||
elif flight.flight_type in [FlightType.SEAD, FlightType.DEAD]:
|
|
||||||
self.setup_group_as_sead_flight(group, flight)
|
|
||||||
else:
|
else:
|
||||||
self.setup_group_as_cap_flight(group, flight)
|
self.setup_flight_group(group, flight, flight.flight_type)
|
||||||
self._setup_custom_payload(flight, group)
|
|
||||||
self.setup_group_activation_trigger(flight, group)
|
self.setup_group_activation_trigger(flight, group)
|
||||||
|
|
||||||
|
|
||||||
def setup_group_activation_trigger(self, flight, group):
|
def setup_group_activation_trigger(self, flight, group):
|
||||||
if flight.scheduled_in > 0:
|
if flight.scheduled_in > 0 and flight.client_count == 0:
|
||||||
group.late_activation = True
|
|
||||||
activation_trigger = TriggerOnce(Event.NoEvent, "LiberationActivationTriggerForGroup" + str(group.id))
|
|
||||||
activation_trigger.add_condition(TimeAfter(seconds=flight.scheduled_in*60))
|
|
||||||
|
|
||||||
if(flight.from_cp.cptype == ControlPointType.AIRBASE):
|
if flight.start_type != "In Flight":
|
||||||
if not flight.from_cp.captured:
|
group.late_activation = False
|
||||||
activation_trigger.add_condition(CoalitionHasAirdrome(1, flight.from_cp.id))
|
group.uncontrolled = True
|
||||||
else:
|
|
||||||
activation_trigger.add_condition(CoalitionHasAirdrome(2, flight.from_cp.id))
|
|
||||||
|
|
||||||
|
activation_trigger = TriggerOnce(Event.NoEvent, "LiberationControlTriggerForGroup" + str(group.id))
|
||||||
|
activation_trigger.add_condition(TimeAfter(seconds=flight.scheduled_in * 60))
|
||||||
|
if (flight.from_cp.cptype == ControlPointType.AIRBASE):
|
||||||
|
if flight.from_cp.captured:
|
||||||
|
activation_trigger.add_condition(
|
||||||
|
CoalitionHasAirdrome(self.game.get_player_coalition_id(), flight.from_cp.id))
|
||||||
|
else:
|
||||||
|
activation_trigger.add_condition(
|
||||||
|
CoalitionHasAirdrome(self.game.get_enemy_coalition_id(), flight.from_cp.id))
|
||||||
|
|
||||||
activation_trigger.add_action(ActivateGroup(group.id))
|
group.add_trigger_action(StartCommand())
|
||||||
self.m.triggerrules.triggers.append(activation_trigger)
|
activation_trigger.add_action(AITaskPush(group.id, len(group.tasks)))
|
||||||
|
self.m.triggerrules.triggers.append(activation_trigger)
|
||||||
|
else:
|
||||||
|
group.late_activation = True
|
||||||
|
activation_trigger = TriggerOnce(Event.NoEvent, "LiberationActivationTriggerForGroup" + str(group.id))
|
||||||
|
activation_trigger.add_condition(TimeAfter(seconds=flight.scheduled_in*60))
|
||||||
|
|
||||||
|
if(flight.from_cp.cptype == ControlPointType.AIRBASE):
|
||||||
|
if flight.from_cp.captured:
|
||||||
|
activation_trigger.add_condition(CoalitionHasAirdrome(self.game.get_player_coalition_id(), flight.from_cp.id))
|
||||||
|
else:
|
||||||
|
activation_trigger.add_condition(CoalitionHasAirdrome(self.game.get_enemy_coalition_id(), flight.from_cp.id))
|
||||||
|
|
||||||
|
activation_trigger.add_action(ActivateGroup(group.id))
|
||||||
|
self.m.triggerrules.triggers.append(activation_trigger)
|
||||||
|
|
||||||
def generate_planned_flight(self, cp, country, flight:Flight):
|
def generate_planned_flight(self, cp, country, flight:Flight):
|
||||||
try:
|
try:
|
||||||
if flight.start_type == "In Flight" or flight.client_count == 0:
|
if flight.client_count == 0 and self.game.settings.perf_ai_parking_start:
|
||||||
|
flight.start_type = "Warm"
|
||||||
|
|
||||||
|
if flight.start_type == "In Flight":
|
||||||
group = self._generate_group(
|
group = self._generate_group(
|
||||||
name=namegen.next_unit_name(country, cp.id, flight.unit_type),
|
name=namegen.next_unit_name(country, cp.id, flight.unit_type),
|
||||||
side=country,
|
side=country,
|
||||||
@@ -379,7 +303,6 @@ class AircraftConflictGenerator:
|
|||||||
client_count=0,
|
client_count=0,
|
||||||
at=cp.position)
|
at=cp.position)
|
||||||
else:
|
else:
|
||||||
|
|
||||||
st = StartType.Runway
|
st = StartType.Runway
|
||||||
if flight.start_type == "Cold":
|
if flight.start_type == "Cold":
|
||||||
st = StartType.Cold
|
st = StartType.Cold
|
||||||
@@ -406,6 +329,7 @@ class AircraftConflictGenerator:
|
|||||||
start_type=st)
|
start_type=st)
|
||||||
except Exception:
|
except Exception:
|
||||||
# Generated when there is no place on Runway or on Parking Slots
|
# Generated when there is no place on Runway or on Parking Slots
|
||||||
|
flight.start_type = "In Flight"
|
||||||
group = self._generate_group(
|
group = self._generate_group(
|
||||||
name=namegen.next_unit_name(country, cp.id, flight.unit_type),
|
name=namegen.next_unit_name(country, cp.id, flight.unit_type),
|
||||||
side=country,
|
side=country,
|
||||||
@@ -425,65 +349,74 @@ class AircraftConflictGenerator:
|
|||||||
group.add_waypoint(Point(point.x,point.y), point.alt)
|
group.add_waypoint(Point(point.x,point.y), point.alt)
|
||||||
|
|
||||||
|
|
||||||
def setup_group_as_cap_flight(self, group, flight):
|
def setup_flight_group(self, group, flight, flight_type):
|
||||||
self._setup_group(group, CAP, flight.client_count)
|
|
||||||
for point in flight.points:
|
|
||||||
group.add_waypoint(Point(point.x,point.y), point.alt)
|
|
||||||
|
|
||||||
def setup_group_as_cas_flight(self, group, flight):
|
if flight_type in [FlightType.CAP, FlightType.BARCAP, FlightType.TARCAP]:
|
||||||
group.task = CAS.name
|
group.task = CAP.name
|
||||||
self._setup_group(group, CAS, flight.client_count)
|
self._setup_group(group, CAP, flight.client_count)
|
||||||
|
# group.points[0].tasks.clear()
|
||||||
|
# group.tasks.clear()
|
||||||
|
# group.tasks.append(EngageTargets(max_distance=40, targets=[Targets.All.Air]))
|
||||||
|
# group.tasks.append(EngageTargets(max_distance=nm_to_meter(120), targets=[Targets.All.Air]))
|
||||||
|
pass
|
||||||
|
elif flight_type in [FlightType.CAS, FlightType.BAI]:
|
||||||
|
group.task = CAS.name
|
||||||
|
self._setup_group(group, CAS, flight.client_count)
|
||||||
|
group.points[0].tasks.clear()
|
||||||
|
group.points[0].tasks.append(CASTaskAction())
|
||||||
|
group.points[0].tasks.append(OptReactOnThreat(OptReactOnThreat.Values.EvadeFire))
|
||||||
|
group.points[0].tasks.append(OptROE(OptROE.Values.OpenFireWeaponFree))
|
||||||
|
elif flight_type in [FlightType.SEAD, FlightType.DEAD]:
|
||||||
|
group.task = SEAD.name
|
||||||
|
self._setup_group(group, SEAD, flight.client_count)
|
||||||
|
group.points[0].tasks.clear()
|
||||||
|
group.points[0].tasks.append(SEADTaskAction())
|
||||||
|
group.points[0].tasks.append(OptReactOnThreat(OptReactOnThreat.Values.EvadeFire))
|
||||||
|
group.points[0].tasks.append(OptROE(OptROE.Values.OpenFireWeaponFree))
|
||||||
|
group.points[0].tasks.append(OptRestrictJettison(True))
|
||||||
|
elif flight_type in [FlightType.STRIKE]:
|
||||||
|
group.task = PinpointStrike.name
|
||||||
|
self._setup_group(group, GroundAttack, flight.client_count)
|
||||||
|
group.points[0].tasks.clear()
|
||||||
|
group.points[0].tasks.append(OptReactOnThreat(OptReactOnThreat.Values.EvadeFire))
|
||||||
|
group.points[0].tasks.append(OptROE(OptROE.Values.OpenFire))
|
||||||
|
group.points[0].tasks.append(OptRestrictJettison(True))
|
||||||
|
elif flight_type in [FlightType.ANTISHIP]:
|
||||||
|
group.task = AntishipStrike.name
|
||||||
|
self._setup_group(group, AntishipStrike, flight.client_count)
|
||||||
|
group.points[0].tasks.clear()
|
||||||
|
group.points[0].tasks.append(OptReactOnThreat(OptReactOnThreat.Values.EvadeFire))
|
||||||
|
group.points[0].tasks.append(OptROE(OptROE.Values.OpenFire))
|
||||||
|
group.points[0].tasks.append(OptRestrictJettison(True))
|
||||||
|
|
||||||
group.points[0].tasks.clear()
|
for i, point in enumerate(flight.points):
|
||||||
group.points[0].tasks.append(CASTaskAction())
|
if not point.only_for_player or (point.only_for_player and flight.client_count > 0):
|
||||||
group.points[0].tasks.append(OptReactOnThreat(OptReactOnThreat.Values.EvadeFire))
|
pt = group.add_waypoint(Point(point.x, point.y), point.alt)
|
||||||
group.points[0].tasks.append(OptROE(OptROE.Values.OpenFireWeaponFree))
|
if point.waypoint_type == FlightWaypointType.PATROL_TRACK:
|
||||||
#group.points[0].tasks.append(OptRestrictJettison(True))
|
action = OrbitAction(altitude=pt.alt, pattern=OrbitAction.OrbitPattern.RaceTrack)
|
||||||
|
pt.tasks.append(action)
|
||||||
|
#for tgt in point.targets:
|
||||||
|
# if hasattr(tgt, "position"):
|
||||||
|
# engagetgt = EngageTargetsInZone(tgt.position, radius=CAP_DEFAULT_ENGAGE_DISTANCE, targets=[Targets.All.Air])
|
||||||
|
# pt.tasks.append(engagetgt)
|
||||||
|
elif point.waypoint_type == FlightWaypointType.LANDING_POINT:
|
||||||
|
pt.type = "Land"
|
||||||
|
elif point.waypoint_type == FlightWaypointType.INGRESS_STRIKE:
|
||||||
|
print("TGTS :")
|
||||||
|
print(point.targets)
|
||||||
|
for j, t in enumerate(point.targets):
|
||||||
|
print(t.position)
|
||||||
|
pt.tasks.append(Bombing(t.position))
|
||||||
|
if group.units[0].unit_type == JF_17 and j < 4:
|
||||||
|
group.add_nav_target_point(t.position, "PP" + str(j + 1))
|
||||||
|
if group.units[0].unit_type == F_14B and j == 0:
|
||||||
|
group.add_nav_target_point(t.position, "ST")
|
||||||
|
|
||||||
for point in flight.points:
|
if pt is not None:
|
||||||
group.add_waypoint(Point(point.x,point.y), point.alt)
|
pt.alt_type = point.alt_type
|
||||||
|
pt.name = String(point.name)
|
||||||
|
|
||||||
def setup_group_as_sead_flight(self, group, flight):
|
self._setup_custom_payload(flight, group)
|
||||||
group.task = SEAD.name
|
|
||||||
self._setup_group(group, SEAD, flight.client_count)
|
|
||||||
|
|
||||||
group.points[0].tasks.clear()
|
|
||||||
group.points[0].tasks.append(SEADTaskAction())
|
|
||||||
group.points[0].tasks.append(OptReactOnThreat(OptReactOnThreat.Values.EvadeFire))
|
|
||||||
group.points[0].tasks.append(OptROE(OptROE.Values.OpenFireWeaponFree))
|
|
||||||
group.points[0].tasks.append(OptRestrictJettison(True))
|
|
||||||
|
|
||||||
i = 1
|
|
||||||
for point in flight.points:
|
|
||||||
group.add_waypoint(Point(point.x,point.y), point.alt)
|
|
||||||
group.points[i].tasks.clear()
|
|
||||||
group.points[i].tasks.append(SEADTaskAction())
|
|
||||||
i = i + 1
|
|
||||||
|
|
||||||
def setup_group_as_strike_flight(self, group, flight):
|
|
||||||
group.task = PinpointStrike.name
|
|
||||||
self._setup_group(group, GroundAttack, flight.client_count)
|
|
||||||
|
|
||||||
group.points[0].tasks.clear()
|
|
||||||
group.points[0].tasks.append(CASTaskAction())
|
|
||||||
group.points[0].tasks.append(OptReactOnThreat(OptReactOnThreat.Values.EvadeFire))
|
|
||||||
group.points[0].tasks.append(OptROE(OptROE.Values.OpenFire))
|
|
||||||
group.points[0].tasks.append(OptRestrictJettison(True))
|
|
||||||
|
|
||||||
i = 1
|
|
||||||
bombing_point_found = False
|
|
||||||
for point in flight.points:
|
|
||||||
group.add_waypoint(Point(point.x,point.y), point.alt)
|
|
||||||
if not bombing_point_found:
|
|
||||||
for t in point.targets:
|
|
||||||
if hasattr(t, "obj_name"):
|
|
||||||
buildings = self.game.theater.find_ground_objects_by_obj_name(t.obj_name)
|
|
||||||
for building in buildings:
|
|
||||||
group.points[i].tasks.append(Bombing(building.position))
|
|
||||||
else:
|
|
||||||
group.points[i].tasks.append(Bombing(t.position))
|
|
||||||
bombing_point_found = True
|
|
||||||
i = i + 1
|
|
||||||
|
|
||||||
|
|
||||||
def setup_group_as_antiship_flight(self, group, flight):
|
def setup_group_as_antiship_flight(self, group, flight):
|
||||||
|
|||||||
@@ -5,8 +5,25 @@ import random
|
|||||||
|
|
||||||
from game import db
|
from game import db
|
||||||
from gen import Conflict
|
from gen import Conflict
|
||||||
from gen.flights.ai_flight_planner_db import INTERCEPT_CAPABLE, CAP_CAPABLE, CAS_CAPABLE, SEAD_CAPABLE
|
from gen.flights.ai_flight_planner_db import INTERCEPT_CAPABLE, CAP_CAPABLE, CAS_CAPABLE, SEAD_CAPABLE, STRIKE_CAPABLE
|
||||||
from gen.flights.flight import Flight, FlightType, FlightWaypoint
|
from gen.flights.flight import Flight, FlightType, FlightWaypoint, FlightWaypointType
|
||||||
|
|
||||||
|
|
||||||
|
def meter_to_feet(value_in_meter):
|
||||||
|
return int(3.28084 * value_in_meter)
|
||||||
|
|
||||||
|
|
||||||
|
def feet_to_meter(value_in_feet):
|
||||||
|
return int(float(value_in_feet)/3.048)
|
||||||
|
|
||||||
|
|
||||||
|
def meter_to_nm(value_in_meter):
|
||||||
|
return int(float(value_in_meter)*0.000539957)
|
||||||
|
|
||||||
|
|
||||||
|
def nm_to_meter(value_in_nm):
|
||||||
|
return int(float(value_in_nm)*1852)
|
||||||
|
|
||||||
|
|
||||||
# TODO : Ideally should be based on the aircraft type instead / Availability of fuel
|
# TODO : Ideally should be based on the aircraft type instead / Availability of fuel
|
||||||
STRIKE_MAX_RANGE = 1500000
|
STRIKE_MAX_RANGE = 1500000
|
||||||
@@ -19,11 +36,15 @@ CAS_EVERY_X_MINUTES = 30
|
|||||||
SEAD_EVERY_X_MINUTES = 40
|
SEAD_EVERY_X_MINUTES = 40
|
||||||
STRIKE_EVERY_X_MINUTES = 40
|
STRIKE_EVERY_X_MINUTES = 40
|
||||||
|
|
||||||
INGRESS_EGRESS_DISTANCE = 45000
|
INGRESS_EGRESS_DISTANCE = nm_to_meter(45)
|
||||||
INGRESS_ALT = 6096 # 20k feet
|
INGRESS_ALT = feet_to_meter(20000)
|
||||||
EGRESS_ALT = 6096 # 20k feet
|
EGRESS_ALT = feet_to_meter(20000)
|
||||||
PATROL_ALT_RANGE = (3600, 9200)
|
PATROL_ALT_RANGE = (feet_to_meter(15000), feet_to_meter(33000))
|
||||||
NAV_ALT = 9144
|
NAV_ALT = 9144
|
||||||
|
PATTERN_ALTITUDE = feet_to_meter(5000)
|
||||||
|
|
||||||
|
CAP_DEFAULT_ENGAGE_DISTANCE = nm_to_meter(40)
|
||||||
|
|
||||||
|
|
||||||
class FlightPlanner:
|
class FlightPlanner:
|
||||||
|
|
||||||
@@ -69,7 +90,7 @@ class FlightPlanner:
|
|||||||
|
|
||||||
self.commision_strike()
|
self.commision_strike()
|
||||||
|
|
||||||
# TODO : commision STRIKE / ANTISHIP
|
# TODO : commision ANTISHIP
|
||||||
|
|
||||||
def remove_flight(self, index):
|
def remove_flight(self, index):
|
||||||
try:
|
try:
|
||||||
@@ -135,30 +156,64 @@ class FlightPlanner:
|
|||||||
ftype = FlightType.BARCAP if self.from_cp.is_carrier else FlightType.CAP
|
ftype = FlightType.BARCAP if self.from_cp.is_carrier else FlightType.CAP
|
||||||
flight = Flight(unit, 2, self.from_cp, ftype)
|
flight = Flight(unit, 2, self.from_cp, ftype)
|
||||||
|
|
||||||
# Flight path : fly over each ground object (TODO : improve)
|
|
||||||
flight.points = []
|
flight.points = []
|
||||||
flight.scheduled_in = offset + i*random.randint(CAP_EVERY_X_MINUTES-5, CAP_EVERY_X_MINUTES+5)
|
flight.scheduled_in = offset + i*random.randint(CAP_EVERY_X_MINUTES-5, CAP_EVERY_X_MINUTES+5)
|
||||||
|
|
||||||
patrol_alt = random.randint(PATROL_ALT_RANGE[0], PATROL_ALT_RANGE[1])
|
patrol_alt = random.randint(PATROL_ALT_RANGE[0], PATROL_ALT_RANGE[1])
|
||||||
|
|
||||||
patrolled = []
|
# Choose a location for CAP patrols (Either behind frontline if there is one, or to protect ground objects)
|
||||||
for ground_object in self.from_cp.ground_objects:
|
if len(self._get_cas_locations()) > 0:
|
||||||
if ground_object.group_id not in patrolled and not ground_object.airbase_group:
|
loc = random.choice(self._get_cas_locations())
|
||||||
point = FlightWaypoint(ground_object.position.x, ground_object.position.y, patrol_alt)
|
ingress, heading, distance = Conflict.frontline_vector(self.from_cp, loc, self.game.theater)
|
||||||
point.name = "Patrol point"
|
center = ingress.point_from_heading(heading, distance / 2)
|
||||||
point.description = "Patrol #" + str(len(flight.points))
|
orbit_center = center.point_from_heading(heading - 90, random.randint(nm_to_meter(6), nm_to_meter(15)))
|
||||||
point.pretty_name = "Patrol #" + str(len(flight.points))
|
radius = distance * 2
|
||||||
flight.points.append(point)
|
orbit0p = orbit_center.point_from_heading(heading, radius)
|
||||||
patrolled.append(ground_object.group_id)
|
orbit1p = orbit_center.point_from_heading(heading + 180, radius)
|
||||||
|
elif len(self.from_cp.ground_objects) > 0:
|
||||||
|
loc = random.choice(self.from_cp.ground_objects)
|
||||||
|
hdg = self.from_cp.position.heading_between_point(loc.position)
|
||||||
|
radius = random.randint(nm_to_meter(5), nm_to_meter(10))
|
||||||
|
orbit0p = loc.position.point_from_heading(hdg - 90, radius)
|
||||||
|
orbit1p = loc.position.point_from_heading(hdg + 90, radius)
|
||||||
|
else:
|
||||||
|
loc = self.from_cp.position.point_from_heading(random.randint(0, 360), random.randint(nm_to_meter(5), nm_to_meter(40)))
|
||||||
|
hdg = self.from_cp.position.heading_between_point(loc.position)
|
||||||
|
radius = random.randint(nm_to_meter(40), nm_to_meter(120))
|
||||||
|
orbit0p = loc.position.point_from_heading(hdg - 90, radius)
|
||||||
|
orbit1p = loc.position.point_from_heading(hdg + 90, radius)
|
||||||
|
|
||||||
if len(flight.points) == 0:
|
|
||||||
for i in range(3):
|
# Create points
|
||||||
pos = self.from_cp.position.point_from_heading(random.randint(0, 360), random.randint(30000, 80000))
|
ascend = self.generate_ascend_point(self.from_cp)
|
||||||
point = FlightWaypoint(pos.x, pos.y, patrol_alt)
|
flight.points.append(ascend)
|
||||||
point.name = "Patrol point"
|
|
||||||
point.description = "Patrol #" + str(len(flight.points))
|
orbit0 = FlightWaypoint(orbit0p.x, orbit0p.y, patrol_alt)
|
||||||
point.pretty_name = "Patrol #" + str(len(flight.points))
|
orbit0.name = "ORBIT 0"
|
||||||
flight.points.append(point)
|
orbit0.description = "Standby between this point and the next one"
|
||||||
|
orbit0.pretty_name = "Orbit race-track start"
|
||||||
|
orbit0.waypoint_type = FlightWaypointType.PATROL_TRACK
|
||||||
|
flight.points.append(orbit0)
|
||||||
|
|
||||||
|
orbit1 = FlightWaypoint(orbit1p.x, orbit1p.y, patrol_alt)
|
||||||
|
orbit1.name = "ORBIT 1"
|
||||||
|
orbit1.description = "Standby between this point and the previous one"
|
||||||
|
orbit1.pretty_name = "Orbit race-track end"
|
||||||
|
orbit1.waypoint_type = FlightWaypointType.PATROL
|
||||||
|
flight.points.append(orbit1)
|
||||||
|
|
||||||
|
orbit0.targets.append(self.from_cp)
|
||||||
|
obj_added = []
|
||||||
|
for ground_object in self.from_cp.ground_objects:
|
||||||
|
if ground_object.obj_name not in obj_added and not ground_object.airbase_group:
|
||||||
|
orbit0.targets.append(ground_object)
|
||||||
|
obj_added.append(ground_object.obj_name)
|
||||||
|
|
||||||
|
descend = self.generate_descend_point(self.from_cp)
|
||||||
|
flight.points.append(descend)
|
||||||
|
|
||||||
|
rtb = self.generate_rtb_waypoint(self.from_cp)
|
||||||
|
flight.points.append(rtb)
|
||||||
|
|
||||||
self.cap_flights.append(flight)
|
self.cap_flights.append(flight)
|
||||||
self.flights.append(flight)
|
self.flights.append(flight)
|
||||||
@@ -197,26 +252,39 @@ class FlightPlanner:
|
|||||||
center = ingress.point_from_heading(heading, distance/2)
|
center = ingress.point_from_heading(heading, distance/2)
|
||||||
egress = ingress.point_from_heading(heading, distance)
|
egress = ingress.point_from_heading(heading, distance)
|
||||||
|
|
||||||
flight.targets.append(center)
|
ascend = self.generate_ascend_point(self.from_cp)
|
||||||
|
flight.points.append(ascend)
|
||||||
|
|
||||||
ingress_point = FlightWaypoint(ingress.x, ingress.y, 1000)
|
ingress_point = FlightWaypoint(ingress.x, ingress.y, 1000)
|
||||||
|
ingress_point.alt_type = "RADIO"
|
||||||
ingress_point.name = "INGRESS"
|
ingress_point.name = "INGRESS"
|
||||||
ingress_point.pretty_name = "INGRESS"
|
ingress_point.pretty_name = "INGRESS"
|
||||||
ingress_point.description = "Ingress into CAS area"
|
ingress_point.description = "Ingress into CAS area"
|
||||||
|
ingress_point.waypoint_type = FlightWaypointType.INGRESS_CAS
|
||||||
flight.points.append(ingress_point)
|
flight.points.append(ingress_point)
|
||||||
|
|
||||||
center_point = FlightWaypoint(center.x, center.y, 1000)
|
center_point = FlightWaypoint(center.x, center.y, 1000)
|
||||||
|
center_point.alt_type = "RADIO"
|
||||||
center_point.description = "Provide CAS"
|
center_point.description = "Provide CAS"
|
||||||
center_point.name = "CAS"
|
center_point.name = "CAS"
|
||||||
center_point.pretty_name = "INGRESS"
|
center_point.pretty_name = "CAS"
|
||||||
|
center_point.waypoint_type = FlightWaypointType.CAS
|
||||||
flight.points.append(center_point)
|
flight.points.append(center_point)
|
||||||
|
|
||||||
egress_point = FlightWaypoint(egress.x, egress.y, 1000)
|
egress_point = FlightWaypoint(egress.x, egress.y, 1000)
|
||||||
|
egress_point.alt_type = "RADIO"
|
||||||
egress_point.description = "Egress from CAS area"
|
egress_point.description = "Egress from CAS area"
|
||||||
egress_point.name = "EGRESS"
|
egress_point.name = "EGRESS"
|
||||||
egress_point.pretty_name = "EGRESS"
|
egress_point.pretty_name = "EGRESS"
|
||||||
|
egress_point.waypoint_type = FlightWaypointType.EGRESS
|
||||||
flight.points.append(egress_point)
|
flight.points.append(egress_point)
|
||||||
|
|
||||||
|
descend = self.generate_descend_point(self.from_cp)
|
||||||
|
flight.points.append(descend)
|
||||||
|
|
||||||
|
rtb = self.generate_rtb_waypoint(self.from_cp)
|
||||||
|
flight.points.append(rtb)
|
||||||
|
|
||||||
self.cas_flights.append(flight)
|
self.cas_flights.append(flight)
|
||||||
self.flights.append(flight)
|
self.flights.append(flight)
|
||||||
|
|
||||||
@@ -251,6 +319,9 @@ class FlightPlanner:
|
|||||||
flight.points = []
|
flight.points = []
|
||||||
flight.scheduled_in = offset + i*random.randint(SEAD_EVERY_X_MINUTES-5, SEAD_EVERY_X_MINUTES+5)
|
flight.scheduled_in = offset + i*random.randint(SEAD_EVERY_X_MINUTES-5, SEAD_EVERY_X_MINUTES+5)
|
||||||
|
|
||||||
|
ascend = self.generate_ascend_point(self.from_cp)
|
||||||
|
flight.points.append(ascend)
|
||||||
|
|
||||||
location = self.potential_sead_targets[0][0]
|
location = self.potential_sead_targets[0][0]
|
||||||
self.potential_sead_targets.pop(0)
|
self.potential_sead_targets.pop(0)
|
||||||
|
|
||||||
@@ -262,25 +333,36 @@ class FlightPlanner:
|
|||||||
ingress_point = FlightWaypoint(ingress_pos.x, ingress_pos.y, INGRESS_ALT)
|
ingress_point = FlightWaypoint(ingress_pos.x, ingress_pos.y, INGRESS_ALT)
|
||||||
ingress_point.pretty_name = "INGRESS on " + location.obj_name
|
ingress_point.pretty_name = "INGRESS on " + location.obj_name
|
||||||
ingress_point.description = "INGRESS on " + location.obj_name
|
ingress_point.description = "INGRESS on " + location.obj_name
|
||||||
|
ingress_point.waypoint_type = FlightWaypointType.INGRESS_SEAD
|
||||||
flight.points.append(ingress_point)
|
flight.points.append(ingress_point)
|
||||||
|
|
||||||
point = FlightWaypoint(location.position.x, location.position.y, 1000)
|
point = FlightWaypoint(location.position.x, location.position.y, 0)
|
||||||
|
point.alt_type = "RADIO"
|
||||||
if flight.flight_type == FlightType.DEAD:
|
if flight.flight_type == FlightType.DEAD:
|
||||||
point.description = "SEAD on " + location.obj_name
|
point.description = "SEAD on " + location.obj_name
|
||||||
point.pretty_name = "SEAD on " + location.obj_name
|
point.pretty_name = "SEAD on " + location.obj_name
|
||||||
|
point.only_for_player = True
|
||||||
else:
|
else:
|
||||||
point.description = "DEAD on " + location.obj_name
|
point.description = "DEAD on " + location.obj_name
|
||||||
point.pretty_name = "DEAD on " + location.obj_name
|
point.pretty_name = "DEAD on " + location.obj_name
|
||||||
|
point.only_for_player = True
|
||||||
|
|
||||||
point.targets.append(location)
|
ingress_point.targets.append(location)
|
||||||
flight.points.append(point)
|
flight.points.append(point)
|
||||||
|
|
||||||
egress_pos = location.position.point_from_heading(egress_heading, INGRESS_EGRESS_DISTANCE)
|
egress_pos = location.position.point_from_heading(egress_heading, INGRESS_EGRESS_DISTANCE)
|
||||||
egress_point = FlightWaypoint(egress_pos.x, egress_pos.y, EGRESS_ALT)
|
egress_point = FlightWaypoint(egress_pos.x, egress_pos.y, EGRESS_ALT)
|
||||||
egress_point.pretty_name = "EGRESS on " + location.obj_name
|
egress_point.pretty_name = "EGRESS from " + location.obj_name
|
||||||
egress_point.description = "EGRESS on " + location.obj_name
|
egress_point.description = "EGRESS from " + location.obj_name
|
||||||
|
egress_point.waypoint_type = FlightWaypointType.EGRESS
|
||||||
flight.points.append(egress_point)
|
flight.points.append(egress_point)
|
||||||
|
|
||||||
|
descend = self.generate_descend_point(self.from_cp)
|
||||||
|
flight.points.append(descend)
|
||||||
|
|
||||||
|
rtb = self.generate_rtb_waypoint(self.from_cp)
|
||||||
|
flight.points.append(rtb)
|
||||||
|
|
||||||
self.sead_flights.append(flight)
|
self.sead_flights.append(flight)
|
||||||
self.flights.append(flight)
|
self.flights.append(flight)
|
||||||
|
|
||||||
@@ -293,7 +375,7 @@ class FlightPlanner:
|
|||||||
"""
|
"""
|
||||||
Pick some aircraft to assign them to STRIKE tasks
|
Pick some aircraft to assign them to STRIKE tasks
|
||||||
"""
|
"""
|
||||||
possible_aircraft = [k for k, v in self.aircraft_inventory.items() if k in CAS_CAPABLE and v >= 2]
|
possible_aircraft = [k for k, v in self.aircraft_inventory.items() if k in STRIKE_CAPABLE and v >= 2]
|
||||||
inventory = dict({k: v for k, v in self.aircraft_inventory.items() if k in possible_aircraft})
|
inventory = dict({k: v for k, v in self.aircraft_inventory.items() if k in possible_aircraft})
|
||||||
|
|
||||||
if len(self.potential_strike_targets) > 0:
|
if len(self.potential_strike_targets) > 0:
|
||||||
@@ -315,6 +397,9 @@ class FlightPlanner:
|
|||||||
flight.points = []
|
flight.points = []
|
||||||
flight.scheduled_in = offset + i*random.randint(SEAD_EVERY_X_MINUTES-5, SEAD_EVERY_X_MINUTES+5)
|
flight.scheduled_in = offset + i*random.randint(SEAD_EVERY_X_MINUTES-5, SEAD_EVERY_X_MINUTES+5)
|
||||||
|
|
||||||
|
ascend = self.generate_ascend_point(self.from_cp)
|
||||||
|
flight.points.append(ascend)
|
||||||
|
|
||||||
location = self.potential_strike_targets[0][0]
|
location = self.potential_strike_targets[0][0]
|
||||||
self.potential_strike_targets.pop(0)
|
self.potential_strike_targets.pop(0)
|
||||||
|
|
||||||
@@ -326,29 +411,62 @@ class FlightPlanner:
|
|||||||
ingress_point = FlightWaypoint(ingress_pos.x, ingress_pos.y, INGRESS_ALT)
|
ingress_point = FlightWaypoint(ingress_pos.x, ingress_pos.y, INGRESS_ALT)
|
||||||
ingress_point.pretty_name = "INGRESS on " + location.obj_name
|
ingress_point.pretty_name = "INGRESS on " + location.obj_name
|
||||||
ingress_point.description = "INGRESS on " + location.obj_name
|
ingress_point.description = "INGRESS on " + location.obj_name
|
||||||
|
ingress_point.name = "INGRESS"
|
||||||
|
ingress_point.waypoint_type = FlightWaypointType.INGRESS_STRIKE
|
||||||
flight.points.append(ingress_point)
|
flight.points.append(ingress_point)
|
||||||
|
|
||||||
if len(location.groups) > 0:
|
if len(location.groups) > 0 and location.dcs_identifier == "AA":
|
||||||
for g in location.groups:
|
for g in location.groups:
|
||||||
for j, u in enumerate(g.units):
|
for j, u in enumerate(g.units):
|
||||||
point = FlightWaypoint(u.position.x, u.position.y, 0)
|
point = FlightWaypoint(u.position.x, u.position.y, 0)
|
||||||
point.description = "STRIKE " + "[" + str(location.obj_name) + "] : " + u.type + " #" + str(j)
|
point.description = "STRIKE " + "[" + str(location.obj_name) + "] : " + u.type + " #" + str(j)
|
||||||
point.pretty_name = "STRIKE " + "[" + str(location.obj_name) + "] : " + u.type + " #" + str(j)
|
point.pretty_name = "STRIKE " + "[" + str(location.obj_name) + "] : " + u.type + " #" + str(j)
|
||||||
|
point.name = location.obj_name + "#" + str(j)
|
||||||
|
point.only_for_player = True
|
||||||
ingress_point.targets.append(location)
|
ingress_point.targets.append(location)
|
||||||
flight.points.append(point)
|
flight.points.append(point)
|
||||||
else:
|
else:
|
||||||
point = FlightWaypoint(location.position.x, location.position.y, 0)
|
if hasattr(location, "obj_name"):
|
||||||
point.description = "STRIKE on " + location.obj_name + " " + str(location.category)
|
buildings = self.game.theater.find_ground_objects_by_obj_name(location.obj_name)
|
||||||
point.pretty_name = "STRIKE on " + location.obj_name + " " + str(location.category)
|
print(buildings)
|
||||||
point.targets.append(location)
|
for building in buildings:
|
||||||
flight.points.append(point)
|
print("BUILDING " + str(building.is_dead) + " " + str(building.dcs_identifier))
|
||||||
|
if building.is_dead:
|
||||||
|
continue
|
||||||
|
|
||||||
|
point = FlightWaypoint(building.position.x, building.position.y, 0)
|
||||||
|
point.description = "STRIKE on " + building.obj_name + " " + str(building.category)
|
||||||
|
point.pretty_name = "STRIKE on " + building.obj_name + " " + str(building.category)
|
||||||
|
point.name = building.obj_name
|
||||||
|
point.only_for_player = True
|
||||||
|
ingress_point.targets.append(building)
|
||||||
|
flight.points.append(point)
|
||||||
|
else:
|
||||||
|
point = FlightWaypoint(location.position.x, location.position.y, 0)
|
||||||
|
point.description = "STRIKE on " + location.obj_name + " " + str(location.category)
|
||||||
|
point.pretty_name = "STRIKE on " + location.obj_name + " " + str(location.category)
|
||||||
|
point.name = location.obj_name
|
||||||
|
point.only_for_player = True
|
||||||
|
ingress_point.targets.append(location)
|
||||||
|
flight.points.append(point)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
egress_pos = location.position.point_from_heading(egress_heading, INGRESS_EGRESS_DISTANCE)
|
egress_pos = location.position.point_from_heading(egress_heading, INGRESS_EGRESS_DISTANCE)
|
||||||
egress_point = FlightWaypoint(egress_pos.x, egress_pos.y, EGRESS_ALT)
|
egress_point = FlightWaypoint(egress_pos.x, egress_pos.y, EGRESS_ALT)
|
||||||
egress_point.pretty_name = "EGRESS on " + location.obj_name
|
egress_point.name = "EGRESS"
|
||||||
egress_point.description = "EGRESS on " + location.obj_name
|
egress_point.pretty_name = "EGRESS from " + location.obj_name
|
||||||
|
egress_point.description = "EGRESS from " + location.obj_name
|
||||||
|
egress_point.waypoint_type = FlightWaypointType.EGRESS
|
||||||
flight.points.append(egress_point)
|
flight.points.append(egress_point)
|
||||||
|
|
||||||
|
descend = self.generate_descend_point(self.from_cp)
|
||||||
|
flight.points.append(descend)
|
||||||
|
|
||||||
|
rtb = self.generate_rtb_waypoint(self.from_cp)
|
||||||
|
flight.points.append(rtb)
|
||||||
|
|
||||||
self.strike_flights.append(flight)
|
self.strike_flights.append(flight)
|
||||||
self.flights.append(flight)
|
self.flights.append(flight)
|
||||||
|
|
||||||
@@ -445,3 +563,35 @@ class FlightPlanner:
|
|||||||
del base_aircraft_inventory[f.unit_type]
|
del base_aircraft_inventory[f.unit_type]
|
||||||
return base_aircraft_inventory
|
return base_aircraft_inventory
|
||||||
|
|
||||||
|
def generate_ascend_point(self, from_cp):
|
||||||
|
ascend_heading = from_cp.heading
|
||||||
|
pos_ascend = from_cp.position.point_from_heading(ascend_heading, 10000)
|
||||||
|
ascend = FlightWaypoint(pos_ascend.x, pos_ascend.y, PATTERN_ALTITUDE)
|
||||||
|
ascend.name = "ASCEND"
|
||||||
|
ascend.alt_type = "RADIO"
|
||||||
|
ascend.description = "Ascend to alt [" + str(meter_to_feet(PATTERN_ALTITUDE)) + " ft AGL], then proceed to next waypoint"
|
||||||
|
ascend.pretty_name = "Ascend to alt [" + str(meter_to_feet(PATTERN_ALTITUDE)) + " ft AGL]"
|
||||||
|
ascend.waypoint_type = FlightWaypointType.ASCEND_POINT
|
||||||
|
return ascend
|
||||||
|
|
||||||
|
def generate_descend_point(self, from_cp):
|
||||||
|
ascend_heading = from_cp.heading
|
||||||
|
descend = from_cp.position.point_from_heading(ascend_heading - 180, 30000)
|
||||||
|
descend = FlightWaypoint(descend.x, descend.y, PATTERN_ALTITUDE)
|
||||||
|
descend.name = "DESCEND"
|
||||||
|
descend.alt_type = "RADIO"
|
||||||
|
descend.description = "Descend to pattern alt [" + str(meter_to_feet(PATTERN_ALTITUDE)) + " ft AGL], contact tower, and land"
|
||||||
|
descend.pretty_name = "Descend to pattern alt [" + str(meter_to_feet(PATTERN_ALTITUDE)) + " ft AGL]"
|
||||||
|
descend.waypoint_type = FlightWaypointType.DESCENT_POINT
|
||||||
|
return descend
|
||||||
|
|
||||||
|
def generate_rtb_waypoint(self, from_cp):
|
||||||
|
rtb = from_cp.position
|
||||||
|
rtb = FlightWaypoint(rtb.x, rtb.y, 0)
|
||||||
|
rtb.name = "LANDING"
|
||||||
|
rtb.alt_type = "RADIO"
|
||||||
|
rtb.description = "RTB"
|
||||||
|
rtb.pretty_name = "RTB"
|
||||||
|
rtb.waypoint_type = FlightWaypointType.LANDING_POINT
|
||||||
|
return rtb
|
||||||
|
|
||||||
|
|||||||
@@ -48,6 +48,7 @@ CAP_CAPABLE = [
|
|||||||
|
|
||||||
P_51D_30_NA,
|
P_51D_30_NA,
|
||||||
P_51D,
|
P_51D,
|
||||||
|
P_47D_30,
|
||||||
|
|
||||||
SpitfireLFMkIXCW,
|
SpitfireLFMkIXCW,
|
||||||
SpitfireLFMkIX,
|
SpitfireLFMkIX,
|
||||||
@@ -106,6 +107,7 @@ CAS_CAPABLE = [
|
|||||||
|
|
||||||
P_51D_30_NA,
|
P_51D_30_NA,
|
||||||
P_51D,
|
P_51D,
|
||||||
|
P_47D_30,
|
||||||
A_20G,
|
A_20G,
|
||||||
|
|
||||||
SpitfireLFMkIXCW,
|
SpitfireLFMkIXCW,
|
||||||
@@ -165,10 +167,9 @@ STRIKE_CAPABLE = [
|
|||||||
L_39ZA,
|
L_39ZA,
|
||||||
AJS37,
|
AJS37,
|
||||||
|
|
||||||
M_2000C,
|
|
||||||
|
|
||||||
P_51D_30_NA,
|
P_51D_30_NA,
|
||||||
P_51D,
|
P_51D,
|
||||||
|
P_47D_30,
|
||||||
A_20G,
|
A_20G,
|
||||||
|
|
||||||
SpitfireLFMkIXCW,
|
SpitfireLFMkIXCW,
|
||||||
|
|||||||
@@ -30,17 +30,38 @@ class FlightType(Enum):
|
|||||||
EWAR = 16
|
EWAR = 16
|
||||||
|
|
||||||
|
|
||||||
class FlightWaypoint():
|
class FlightWaypointType(Enum):
|
||||||
|
TAKEOFF = 0 # Take off point
|
||||||
|
ASCEND_POINT = 1 # Ascension point after take off
|
||||||
|
PATROL = 2 # Patrol point
|
||||||
|
PATROL_TRACK = 3 # Patrol race track
|
||||||
|
NAV = 4 # Nav point
|
||||||
|
INGRESS_STRIKE = 5 # Ingress strike (For generator, means that this should have bombing on next TARGET_POINT points)
|
||||||
|
INGRESS_SEAD = 6 # Ingress sead (For generator, means that this should attack groups on TARGET_GROUP_LOC points)
|
||||||
|
INGRESS_CAS = 7 # Ingress cas (should start CAS task)
|
||||||
|
CAS = 8 # Should do CAS there
|
||||||
|
EGRESS = 9 # Should stop attack
|
||||||
|
DESCENT_POINT = 10 # Should start descending to pattern alt
|
||||||
|
LANDING_POINT = 11 # Should land there
|
||||||
|
TARGET_POINT = 12 # A target building or static object, position
|
||||||
|
TARGET_GROUP_LOC = 13 # A target group approximate location
|
||||||
|
TARGET_SHIP = 14 # A target ship known location
|
||||||
|
|
||||||
|
|
||||||
|
class FlightWaypoint:
|
||||||
|
|
||||||
def __init__(self, x: float, y: float, alt=0):
|
def __init__(self, x: float, y: float, alt=0):
|
||||||
self.x = x
|
self.x = x
|
||||||
self.y = y
|
self.y = y
|
||||||
self.alt = alt
|
self.alt = alt
|
||||||
|
self.alt_type = "BARO"
|
||||||
self.name = ""
|
self.name = ""
|
||||||
self.description = ""
|
self.description = ""
|
||||||
self.targets = []
|
self.targets = []
|
||||||
self.obj_name = ""
|
self.obj_name = ""
|
||||||
self.pretty_name = ""
|
self.pretty_name = ""
|
||||||
|
self.waypoint_type = FlightWaypointType.TAKEOFF # type: FlightWaypointType
|
||||||
|
self.only_for_player = False
|
||||||
|
|
||||||
|
|
||||||
class Flight:
|
class Flight:
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ import sys
|
|||||||
from shutil import copyfile
|
from shutil import copyfile
|
||||||
|
|
||||||
import dcs
|
import dcs
|
||||||
|
from PySide2 import QtWidgets
|
||||||
from PySide2.QtGui import QPixmap
|
from PySide2.QtGui import QPixmap
|
||||||
from PySide2.QtWidgets import QApplication, QSplashScreen
|
from PySide2.QtWidgets import QApplication, QSplashScreen
|
||||||
from dcs import installation
|
from dcs import installation
|
||||||
@@ -11,12 +12,22 @@ from dcs import installation
|
|||||||
from qt_ui import uiconstants
|
from qt_ui import uiconstants
|
||||||
from qt_ui.windows.GameUpdateSignal import GameUpdateSignal
|
from qt_ui.windows.GameUpdateSignal import GameUpdateSignal
|
||||||
from qt_ui.windows.QLiberationWindow import QLiberationWindow
|
from qt_ui.windows.QLiberationWindow import QLiberationWindow
|
||||||
from userdata import persistency, logging as logging_module
|
from qt_ui.windows.preferences.QLiberationFirstStartWindow import QLiberationFirstStartWindow
|
||||||
|
from userdata import persistency, logging as logging_module, liberation_install
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
|
|
||||||
persistency.setup(installation.get_dcs_saved_games_directory())
|
app = QApplication(sys.argv)
|
||||||
|
|
||||||
|
css = ""
|
||||||
|
with open("./resources/stylesheets/style.css") as stylesheet:
|
||||||
|
app.setStyleSheet(stylesheet.read())
|
||||||
|
|
||||||
|
# Logging setup
|
||||||
|
VERSION_STRING = "2.0RC6"
|
||||||
|
logging_module.setup_version_string(VERSION_STRING)
|
||||||
|
|
||||||
|
# Inject custom payload in pydcs framework
|
||||||
custom_payloads = os.path.join(os.path.dirname(os.path.realpath(__file__)), "..\\resources\\customized_payloads")
|
custom_payloads = os.path.join(os.path.dirname(os.path.realpath(__file__)), "..\\resources\\customized_payloads")
|
||||||
if os.path.exists(custom_payloads):
|
if os.path.exists(custom_payloads):
|
||||||
dcs.planes.FlyingType.payload_dirs.append(custom_payloads)
|
dcs.planes.FlyingType.payload_dirs.append(custom_payloads)
|
||||||
@@ -27,11 +38,14 @@ if __name__ == "__main__":
|
|||||||
if os.path.exists(custom_payloads):
|
if os.path.exists(custom_payloads):
|
||||||
dcs.planes.FlyingType.payload_dirs.append(custom_payloads)
|
dcs.planes.FlyingType.payload_dirs.append(custom_payloads)
|
||||||
|
|
||||||
VERSION_STRING = "2.0RC6"
|
|
||||||
logging_module.setup_version_string(VERSION_STRING)
|
|
||||||
logging.info("Using {} as userdata folder".format(persistency.base_path()))
|
|
||||||
|
|
||||||
app = QApplication(sys.argv)
|
first_start = liberation_install.init()
|
||||||
|
if first_start:
|
||||||
|
window = QLiberationFirstStartWindow()
|
||||||
|
window.exec_()
|
||||||
|
|
||||||
|
logging.info("Using {} as 'Saved Game Folder'".format(persistency.base_path()))
|
||||||
|
logging.info("Using {} as 'DCS installation folder'".format(liberation_install.get_dcs_install_directory()))
|
||||||
|
|
||||||
# Splash screen setup
|
# Splash screen setup
|
||||||
pixmap = QPixmap("./resources/ui/splash_screen.png")
|
pixmap = QPixmap("./resources/ui/splash_screen.png")
|
||||||
@@ -44,24 +58,28 @@ if __name__ == "__main__":
|
|||||||
uiconstants.load_aircraft_icons()
|
uiconstants.load_aircraft_icons()
|
||||||
uiconstants.load_vehicle_icons()
|
uiconstants.load_vehicle_icons()
|
||||||
|
|
||||||
|
|
||||||
css = ""
|
|
||||||
with open("./resources/stylesheets/style.css") as stylesheet:
|
|
||||||
css = stylesheet.read()
|
|
||||||
|
|
||||||
# Replace DCS Mission scripting file to allow DCS Liberation to work
|
# Replace DCS Mission scripting file to allow DCS Liberation to work
|
||||||
print("Replace : " + installation.get_dcs_install_directory() + os.path.sep + "Scripts/MissionScripting.lua")
|
try:
|
||||||
copyfile("./resources/scripts/MissionScripting.lua", installation.get_dcs_install_directory() + os.path.sep + "Scripts/MissionScripting.lua")
|
liberation_install.replace_mission_scripting_file()
|
||||||
app.processEvents()
|
except:
|
||||||
|
error_dialog = QtWidgets.QErrorMessage()
|
||||||
|
error_dialog.setWindowTitle("Wrong DCS installation directory.")
|
||||||
|
error_dialog.showMessage("Unable to modify Mission Scripting file. Possible issues with rights. Try running as admin, or please perform the modification of the MissionScripting file manually.")
|
||||||
|
error_dialog.exec_()
|
||||||
|
|
||||||
# Apply CSS (need works)
|
# Apply CSS (need works)
|
||||||
app.setStyleSheet(css)
|
|
||||||
GameUpdateSignal()
|
GameUpdateSignal()
|
||||||
|
|
||||||
# Start window
|
# Start window
|
||||||
window = QLiberationWindow()
|
window = QLiberationWindow()
|
||||||
window.showMaximized()
|
window.showMaximized()
|
||||||
|
|
||||||
splash.finish(window)
|
splash.finish(window)
|
||||||
sys.exit(app.exec_())
|
qt_execution_code = app.exec_()
|
||||||
|
|
||||||
|
# Restore Mission Scripting file
|
||||||
|
logging.info("QT App terminated with status code : " + str(qt_execution_code))
|
||||||
|
logging.info("Attempt to restore original mission scripting file")
|
||||||
|
liberation_install.restore_original_mission_scripting()
|
||||||
|
sys.exit(qt_execution_code)
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -8,12 +8,12 @@ from game.event import UnitsDeliveryEvent, FrontlineAttackEvent
|
|||||||
from theater.theatergroundobject import CATEGORY_MAP
|
from theater.theatergroundobject import CATEGORY_MAP
|
||||||
|
|
||||||
URLS : Dict[str, str] = {
|
URLS : Dict[str, str] = {
|
||||||
"Manual": "https://github.com/shdwp/dcs_liberation/wiki/Manual",
|
"Manual": "https://github.com/khopa/dcs_liberation/wiki",
|
||||||
"Troubleshooting": "https://github.com/shdwp/dcs_liberation/wiki/Troubleshooting",
|
"Troubleshooting": "https://github.com/shdwp/dcs_liberation/wiki/Troubleshooting",
|
||||||
"Modding": "https://github.com/shdwp/dcs_liberation/wiki/Modding-tutorial",
|
"Modding": "https://github.com/shdwp/dcs_liberation/wiki/Modding-tutorial",
|
||||||
"Repository": "https://github.com/shdwp/dcs_liberation",
|
"Repository": "https://github.com/khopa/dcs_liberation",
|
||||||
"ForumThread": "https://forums.eagle.ru/showthread.php?t=214834",
|
"ForumThread": "https://forums.eagle.ru/showthread.php?t=214834",
|
||||||
"Issues": "https://github.com/shdwp/dcs_liberation/issues"
|
"Issues": "https://github.com/khopa/dcs_liberation/issues"
|
||||||
}
|
}
|
||||||
|
|
||||||
LABELS_OPTIONS = ["Full", "Abbreviated", "Dot Only", "Off"]
|
LABELS_OPTIONS = ["Full", "Abbreviated", "Dot Only", "Off"]
|
||||||
|
|||||||
@@ -93,15 +93,16 @@ class QPredefinedWaypointSelectionComboBox(QComboBox):
|
|||||||
pos = Conflict.frontline_position(self.game.theater, cp, ecp)[0]
|
pos = Conflict.frontline_position(self.game.theater, cp, ecp)[0]
|
||||||
wpt = FlightWaypoint(pos.x, pos.y, 800)
|
wpt = FlightWaypoint(pos.x, pos.y, 800)
|
||||||
wpt.name = "Frontline " + cp.name + "/" + ecp.name + " [CAS]"
|
wpt.name = "Frontline " + cp.name + "/" + ecp.name + " [CAS]"
|
||||||
|
wpt.alt_type = "RADIO"
|
||||||
wpt.pretty_name = wpt.name
|
wpt.pretty_name = wpt.name
|
||||||
wpt.description = "Frontline"
|
wpt.description = "Frontline"
|
||||||
i = add_model_item(i, model, wpt.pretty_name, wpt)
|
i = add_model_item(i, model, wpt.pretty_name, wpt)
|
||||||
|
|
||||||
|
|
||||||
for cp in self.game.theater.controlpoints:
|
for cp in self.game.theater.controlpoints:
|
||||||
for ground_object in cp.ground_objects:
|
for ground_object in cp.ground_objects:
|
||||||
if not ground_object.is_dead and not ground_object.dcs_identifier == "AA":
|
if not ground_object.is_dead and not ground_object.dcs_identifier == "AA":
|
||||||
wpt = FlightWaypoint(ground_object.position.x,ground_object.position.y, 0)
|
wpt = FlightWaypoint(ground_object.position.x,ground_object.position.y, 0)
|
||||||
|
wpt.alt_type = "RADIO"
|
||||||
wpt.name = wpt.name = "[" + str(ground_object.obj_name) + "] : " + ground_object.category + " #" + str(ground_object.object_id)
|
wpt.name = wpt.name = "[" + str(ground_object.obj_name) + "] : " + ground_object.category + " #" + str(ground_object.object_id)
|
||||||
wpt.pretty_name = wpt.name
|
wpt.pretty_name = wpt.name
|
||||||
wpt.obj_name = ground_object.obj_name
|
wpt.obj_name = ground_object.obj_name
|
||||||
@@ -119,6 +120,7 @@ class QPredefinedWaypointSelectionComboBox(QComboBox):
|
|||||||
for g in ground_object.groups:
|
for g in ground_object.groups:
|
||||||
for j, u in enumerate(g.units):
|
for j, u in enumerate(g.units):
|
||||||
wpt = FlightWaypoint(u.position.x, u.position.y, 0)
|
wpt = FlightWaypoint(u.position.x, u.position.y, 0)
|
||||||
|
wpt.alt_type = "RADIO"
|
||||||
wpt.name = wpt.name = "[" + str(ground_object.obj_name) + "] : " + u.type + " #" + str(j)
|
wpt.name = wpt.name = "[" + str(ground_object.obj_name) + "] : " + u.type + " #" + str(j)
|
||||||
wpt.pretty_name = wpt.name
|
wpt.pretty_name = wpt.name
|
||||||
wpt.targets.append(u)
|
wpt.targets.append(u)
|
||||||
@@ -132,6 +134,7 @@ class QPredefinedWaypointSelectionComboBox(QComboBox):
|
|||||||
for cp in self.game.theater.controlpoints:
|
for cp in self.game.theater.controlpoints:
|
||||||
|
|
||||||
wpt = FlightWaypoint(cp.position.x, cp.position.y, 0)
|
wpt = FlightWaypoint(cp.position.x, cp.position.y, 0)
|
||||||
|
wpt.alt_type = "RADIO"
|
||||||
wpt.name = cp.name
|
wpt.name = cp.name
|
||||||
if cp.captured:
|
if cp.captured:
|
||||||
wpt.description = "Position of " + cp.name + " [Friendly Airbase]"
|
wpt.description = "Position of " + cp.name + " [Friendly Airbase]"
|
||||||
|
|||||||
@@ -83,11 +83,16 @@ class QLiberationMap(QGraphicsView):
|
|||||||
pen = QPen(brush=CONST.COLORS["red"])
|
pen = QPen(brush=CONST.COLORS["red"])
|
||||||
brush = CONST.COLORS["red_transparent"]
|
brush = CONST.COLORS["red_transparent"]
|
||||||
|
|
||||||
|
added_objects = []
|
||||||
for ground_object in cp.ground_objects:
|
for ground_object in cp.ground_objects:
|
||||||
|
if ground_object.obj_name in added_objects:
|
||||||
|
continue
|
||||||
|
|
||||||
|
|
||||||
go_pos = self._transform_point(ground_object.position)
|
go_pos = self._transform_point(ground_object.position)
|
||||||
if not ground_object.airbase_group:
|
if not ground_object.airbase_group:
|
||||||
scene.addItem(QMapGroundObject(self, go_pos[0], go_pos[1], 12, 12, cp, ground_object))
|
buildings = self.game.theater.find_ground_objects_by_obj_name(ground_object.obj_name)
|
||||||
|
scene.addItem(QMapGroundObject(self, go_pos[0], go_pos[1], 12, 12, cp, ground_object, buildings))
|
||||||
|
|
||||||
if ground_object.category == "aa" and self.get_display_rule("sam"):
|
if ground_object.category == "aa" and self.get_display_rule("sam"):
|
||||||
max_range = 0
|
max_range = 0
|
||||||
@@ -99,6 +104,7 @@ class QLiberationMap(QGraphicsView):
|
|||||||
max_range = unit.threat_range
|
max_range = unit.threat_range
|
||||||
if max_range >= 6000:
|
if max_range >= 6000:
|
||||||
scene.addEllipse(go_pos[0] - max_range/300.0 + 8, go_pos[1] - max_range/300.0 + 8, max_range/150.0, max_range/150.0, pen, brush)
|
scene.addEllipse(go_pos[0] - max_range/300.0 + 8, go_pos[1] - max_range/300.0 + 8, max_range/150.0, max_range/150.0, pen, brush)
|
||||||
|
added_objects.append(ground_object.obj_name)
|
||||||
|
|
||||||
for cp in self.game.theater.enemy_points():
|
for cp in self.game.theater.enemy_points():
|
||||||
if self.get_display_rule("lines"):
|
if self.get_display_rule("lines"):
|
||||||
|
|||||||
@@ -9,13 +9,14 @@ from theater import TheaterGroundObject, ControlPoint
|
|||||||
|
|
||||||
class QMapGroundObject(QGraphicsRectItem):
|
class QMapGroundObject(QGraphicsRectItem):
|
||||||
|
|
||||||
def __init__(self, parent, x: float, y: float, w: float, h: float, cp: ControlPoint, model: TheaterGroundObject):
|
def __init__(self, parent, x: float, y: float, w: float, h: float, cp: ControlPoint, model: TheaterGroundObject, buildings=[]):
|
||||||
super(QMapGroundObject, self).__init__(x, y, w, h)
|
super(QMapGroundObject, self).__init__(x, y, w, h)
|
||||||
self.model = model
|
self.model = model
|
||||||
self.cp = cp
|
self.cp = cp
|
||||||
self.parent = parent
|
self.parent = parent
|
||||||
self.setAcceptHoverEvents(True)
|
self.setAcceptHoverEvents(True)
|
||||||
self.setZValue(2)
|
self.setZValue(2)
|
||||||
|
self.buildings = buildings
|
||||||
#self.setFlag(QGraphicsItem.ItemIgnoresTransformations, True)
|
#self.setFlag(QGraphicsItem.ItemIgnoresTransformations, True)
|
||||||
|
|
||||||
if len(self.model.groups) > 0:
|
if len(self.model.groups) > 0:
|
||||||
@@ -32,7 +33,11 @@ class QMapGroundObject(QGraphicsRectItem):
|
|||||||
tooltip = tooltip + str(unit) + "x" + str(units[unit]) + "\n"
|
tooltip = tooltip + str(unit) + "x" + str(units[unit]) + "\n"
|
||||||
self.setToolTip(tooltip[:-1])
|
self.setToolTip(tooltip[:-1])
|
||||||
else:
|
else:
|
||||||
self.setToolTip("[" + self.model.obj_name + "] : " + self.model.category)
|
tooltip = "[" + self.model.obj_name + "]" + "\n"
|
||||||
|
for building in buildings:
|
||||||
|
if not building.is_dead:
|
||||||
|
tooltip = tooltip + str(building.dcs_identifier) + "\n"
|
||||||
|
self.setToolTip(tooltip[:-1])
|
||||||
|
|
||||||
|
|
||||||
def paint(self, painter, option, widget=None):
|
def paint(self, painter, option, widget=None):
|
||||||
|
|||||||
@@ -57,6 +57,14 @@ class QDebriefingWindow(QDialog):
|
|||||||
except:
|
except:
|
||||||
print("Issue adding " + str(unit_type) + " to debriefing information")
|
print("Issue adding " + str(unit_type) + " to debriefing information")
|
||||||
|
|
||||||
|
for building, count in self.debriefing.player_dead_buildings_dict.items():
|
||||||
|
try:
|
||||||
|
lostUnitsLayout.addWidget(QLabel(building, row, 0))
|
||||||
|
lostUnitsLayout.addWidget(QLabel("{}".format(count)), row, 1)
|
||||||
|
row += 1
|
||||||
|
except:
|
||||||
|
print("Issue adding " + str(building) + " to debriefing information")
|
||||||
|
|
||||||
self.layout.addWidget(lostUnits)
|
self.layout.addWidget(lostUnits)
|
||||||
|
|
||||||
# Enemy lost units
|
# Enemy lost units
|
||||||
@@ -87,6 +95,14 @@ class QDebriefingWindow(QDialog):
|
|||||||
enemylostUnitsLayout.addWidget(QLabel("{}".format(count)), row, 1)
|
enemylostUnitsLayout.addWidget(QLabel("{}".format(count)), row, 1)
|
||||||
row += 1
|
row += 1
|
||||||
|
|
||||||
|
for building, count in self.debriefing.enemy_dead_buildings_dict.items():
|
||||||
|
try:
|
||||||
|
enemylostUnitsLayout.addWidget(QLabel(building), row, 0)
|
||||||
|
enemylostUnitsLayout.addWidget(QLabel("{}".format(count)), row, 1)
|
||||||
|
row += 1
|
||||||
|
except:
|
||||||
|
print("Issue adding " + str(building) + " to debriefing information")
|
||||||
|
|
||||||
self.layout.addWidget(enemylostUnits)
|
self.layout.addWidget(enemylostUnits)
|
||||||
|
|
||||||
# confirm button
|
# confirm button
|
||||||
|
|||||||
@@ -1,10 +1,9 @@
|
|||||||
import sys
|
import sys
|
||||||
import webbrowser
|
import webbrowser
|
||||||
|
|
||||||
from PySide2 import QtGui
|
|
||||||
from PySide2.QtCore import Qt
|
from PySide2.QtCore import Qt
|
||||||
from PySide2.QtGui import QIcon
|
from PySide2.QtGui import QIcon
|
||||||
from PySide2.QtWidgets import QWidget, QHBoxLayout, QVBoxLayout, QMainWindow, QAction, QMessageBox, QDesktopWidget, \
|
from PySide2.QtWidgets import QWidget, QVBoxLayout, QMainWindow, QAction, QMessageBox, QDesktopWidget, \
|
||||||
QSplitter
|
QSplitter
|
||||||
|
|
||||||
import qt_ui.uiconstants as CONST
|
import qt_ui.uiconstants as CONST
|
||||||
@@ -12,10 +11,12 @@ from game import Game
|
|||||||
from qt_ui.uiconstants import URLS
|
from qt_ui.uiconstants import URLS
|
||||||
from qt_ui.widgets.QTopPanel import QTopPanel
|
from qt_ui.widgets.QTopPanel import QTopPanel
|
||||||
from qt_ui.widgets.map.QLiberationMap import QLiberationMap
|
from qt_ui.widgets.map.QLiberationMap import QLiberationMap
|
||||||
|
from qt_ui.windows.preferences import QLiberationPreferences
|
||||||
from qt_ui.windows.GameUpdateSignal import GameUpdateSignal, DebriefingSignal
|
from qt_ui.windows.GameUpdateSignal import GameUpdateSignal, DebriefingSignal
|
||||||
from qt_ui.windows.QDebriefingWindow import QDebriefingWindow
|
from qt_ui.windows.QDebriefingWindow import QDebriefingWindow
|
||||||
from qt_ui.windows.QNewGameWizard import NewGameWizard
|
from qt_ui.windows.QNewGameWizard import NewGameWizard
|
||||||
from qt_ui.windows.infos.QInfoPanel import QInfoPanel
|
from qt_ui.windows.infos.QInfoPanel import QInfoPanel
|
||||||
|
from qt_ui.windows.preferences.QLiberationPreferencesWindow import QLiberationPreferencesWindow
|
||||||
from userdata import persistency
|
from userdata import persistency
|
||||||
|
|
||||||
|
|
||||||
@@ -79,6 +80,10 @@ class QLiberationWindow(QMainWindow):
|
|||||||
self.showAboutDialogAction.setIcon(QIcon.fromTheme("help-about"))
|
self.showAboutDialogAction.setIcon(QIcon.fromTheme("help-about"))
|
||||||
self.showAboutDialogAction.triggered.connect(self.showAboutDialog)
|
self.showAboutDialogAction.triggered.connect(self.showAboutDialog)
|
||||||
|
|
||||||
|
self.showLiberationPrefDialogAction = QAction("Preferences", self)
|
||||||
|
self.showLiberationPrefDialogAction.setIcon(QIcon.fromTheme("help-about"))
|
||||||
|
self.showLiberationPrefDialogAction.triggered.connect(self.showLiberationDialog)
|
||||||
|
|
||||||
def initToolbar(self):
|
def initToolbar(self):
|
||||||
self.tool_bar = self.addToolBar("File")
|
self.tool_bar = self.addToolBar("File")
|
||||||
self.tool_bar.addAction(self.newGameAction)
|
self.tool_bar.addAction(self.newGameAction)
|
||||||
@@ -92,17 +97,21 @@ class QLiberationWindow(QMainWindow):
|
|||||||
file_menu.addAction(self.newGameAction)
|
file_menu.addAction(self.newGameAction)
|
||||||
#file_menu.addAction(QIcon(CONST.ICONS["Open"]), "Open") # TODO : implement
|
#file_menu.addAction(QIcon(CONST.ICONS["Open"]), "Open") # TODO : implement
|
||||||
file_menu.addAction(self.saveGameAction)
|
file_menu.addAction(self.saveGameAction)
|
||||||
|
file_menu.addSeparator()
|
||||||
|
file_menu.addAction(self.showLiberationPrefDialogAction)
|
||||||
|
file_menu.addSeparator()
|
||||||
#file_menu.addAction("Save As") # TODO : implement
|
#file_menu.addAction("Save As") # TODO : implement
|
||||||
#file_menu.addAction("Close Current Game", lambda: self.closeGame()) # Not working
|
#file_menu.addAction("Close Current Game", lambda: self.closeGame()) # Not working
|
||||||
file_menu.addAction("Exit" , lambda: self.exit())
|
file_menu.addAction("Exit" , lambda: self.exit())
|
||||||
|
|
||||||
|
|
||||||
help_menu = self.menu.addMenu("Help")
|
help_menu = self.menu.addMenu("Help")
|
||||||
#help_menu.addAction("Online Manual", lambda: webbrowser.open_new_tab(URLS["Manual"]))
|
help_menu.addAction("Online Manual", lambda: webbrowser.open_new_tab(URLS["Manual"]))
|
||||||
|
help_menu.addAction("Discord", lambda: webbrowser.open_new_tab("https://" + "discord.gg" + "/" + "bKrt" + "rkJ"))
|
||||||
#help_menu.addAction("Troubleshooting Guide", lambda: webbrowser.open_new_tab(URLS["Troubleshooting"]))
|
#help_menu.addAction("Troubleshooting Guide", lambda: webbrowser.open_new_tab(URLS["Troubleshooting"]))
|
||||||
#help_menu.addAction("Modding Guide", lambda: webbrowser.open_new_tab(URLS["Modding"]))
|
#help_menu.addAction("Modding Guide", lambda: webbrowser.open_new_tab(URLS["Modding"]))
|
||||||
#help_menu.addSeparator() ----> Note from Khopa : I disable these links since it's not up to date for this branch
|
#help_menu.addSeparator() ----> Note from Khopa : I disable these links since it's not up to date for this branch
|
||||||
help_menu.addAction("Contribute", lambda: webbrowser.open_new_tab(URLS["Repository"]))
|
#help_menu.addAction("Contribute", lambda: webbrowser.open_new_tab(URLS["Repository"]))
|
||||||
help_menu.addAction("Forum Thread", lambda: webbrowser.open_new_tab(URLS["ForumThread"]))
|
help_menu.addAction("Forum Thread", lambda: webbrowser.open_new_tab(URLS["ForumThread"]))
|
||||||
help_menu.addAction("Report an issue", lambda: webbrowser.open_new_tab(URLS["Issues"]))
|
help_menu.addAction("Report an issue", lambda: webbrowser.open_new_tab(URLS["Issues"]))
|
||||||
help_menu.addSeparator()
|
help_menu.addSeparator()
|
||||||
@@ -192,6 +201,10 @@ class QLiberationWindow(QMainWindow):
|
|||||||
print(about.textFormat())
|
print(about.textFormat())
|
||||||
about.exec_()
|
about.exec_()
|
||||||
|
|
||||||
|
def showLiberationDialog(self):
|
||||||
|
self.subwindow = QLiberationPreferencesWindow()
|
||||||
|
self.subwindow.show()
|
||||||
|
|
||||||
def onDebriefing(self, debrief: DebriefingSignal):
|
def onDebriefing(self, debrief: DebriefingSignal):
|
||||||
print("On Debriefing")
|
print("On Debriefing")
|
||||||
self.debriefing = QDebriefingWindow(debrief.debriefing, debrief.gameEvent, debrief.game)
|
self.debriefing = QDebriefingWindow(debrief.debriefing, debrief.gameEvent, debrief.game)
|
||||||
|
|||||||
80
qt_ui/windows/preferences/QLiberationFirstStartWindow.py
Normal file
80
qt_ui/windows/preferences/QLiberationFirstStartWindow.py
Normal file
@@ -0,0 +1,80 @@
|
|||||||
|
from PySide2.QtGui import QIcon, Qt
|
||||||
|
from PySide2.QtWidgets import QDialog, QVBoxLayout, QPushButton, QHBoxLayout, QPlainTextEdit, QTextEdit
|
||||||
|
|
||||||
|
from qt_ui.windows.preferences.QLiberationPreferences import QLiberationPreferences
|
||||||
|
|
||||||
|
|
||||||
|
class QLiberationFirstStartWindow(QDialog):
|
||||||
|
|
||||||
|
def __init__(self):
|
||||||
|
super(QLiberationFirstStartWindow, self).__init__()
|
||||||
|
|
||||||
|
self.setModal(True)
|
||||||
|
self.setWindowTitle("First start configuration")
|
||||||
|
self.setMinimumSize(500, 200)
|
||||||
|
self.setWindowIcon(QIcon("./resources/icon.png"))
|
||||||
|
self.setWindowFlags(Qt.WindowStaysOnTopHint | Qt.Dialog | Qt.WindowTitleHint)
|
||||||
|
self.setWindowModality(Qt.WindowModal)
|
||||||
|
self.preferences = QLiberationPreferences()
|
||||||
|
|
||||||
|
WARN_TEXT = """
|
||||||
|
<strong>Welcome to DCS Liberation !</strong>
|
||||||
|
<br/><br>
|
||||||
|
<strong>Please take 30 seconds to read this :</strong>
|
||||||
|
|
||||||
|
<p>DCS Liberation will modify this file in your DCS installation directory :</p>
|
||||||
|
<br/>
|
||||||
|
<strong><dcs_installation_directory>/Scripts/MissionScripting.lua</strong><br/>
|
||||||
|
|
||||||
|
<p>
|
||||||
|
This will disable some security limits of the DCS World Lua scripting environment, in order to allow communication between DCS World and DCS Liberation.
|
||||||
|
However, the modification of this file could potentially grant access to your filesystem to malicious DCS mission files.
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<p>So, you should not join untrusted servers or open untrusted mission files within DCS world while DCS Liberation is running.</p>
|
||||||
|
|
||||||
|
<p>
|
||||||
|
DCS Liberation will restore your original MissionScripting file when it close.
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<p>
|
||||||
|
However, should DCS Liberation encounter an unexpected crash (which should not happen), the MissionScripting file might not be restored.
|
||||||
|
If that occurs, you can use the backup file saved in the DCS Liberation directory there :
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<br/>
|
||||||
|
<strong>./resources/scripts/MissionScripting.original.lua</strong><br/>
|
||||||
|
|
||||||
|
<p>Then copy it in your DCS installation directory to replace this file :</p>
|
||||||
|
|
||||||
|
<br/>
|
||||||
|
<strong><dcs_installation_directory>/Scripts/MissionScripting.lua</strong><br/>
|
||||||
|
|
||||||
|
<p>As you click on the button below, the file will be replaced in your DCS installation directory.</p>
|
||||||
|
|
||||||
|
<br/><br/>
|
||||||
|
|
||||||
|
<strong>Thank you for reading !</strong>
|
||||||
|
"""
|
||||||
|
self.warning_text = QTextEdit(WARN_TEXT)
|
||||||
|
self.warning_text.setReadOnly(True)
|
||||||
|
self.apply_button = QPushButton("I have read everything and I Accept")
|
||||||
|
self.apply_button.clicked.connect(lambda : self.apply())
|
||||||
|
self.initUI()
|
||||||
|
|
||||||
|
def initUI(self):
|
||||||
|
layout = QVBoxLayout()
|
||||||
|
layout.addWidget(self.preferences)
|
||||||
|
layout.addWidget(self.warning_text)
|
||||||
|
layout.addStretch()
|
||||||
|
apply_btn_layout = QHBoxLayout()
|
||||||
|
apply_btn_layout.addStretch()
|
||||||
|
apply_btn_layout.addWidget(self.apply_button)
|
||||||
|
layout.addLayout(apply_btn_layout)
|
||||||
|
self.setLayout(layout)
|
||||||
|
|
||||||
|
def apply(self):
|
||||||
|
print("Applying changes")
|
||||||
|
if self.preferences.apply():
|
||||||
|
self.close()
|
||||||
|
|
||||||
93
qt_ui/windows/preferences/QLiberationPreferences.py
Normal file
93
qt_ui/windows/preferences/QLiberationPreferences.py
Normal file
@@ -0,0 +1,93 @@
|
|||||||
|
import os
|
||||||
|
|
||||||
|
from PySide2 import QtWidgets
|
||||||
|
from PySide2.QtGui import Qt
|
||||||
|
from PySide2.QtWidgets import QFrame, QLineEdit, QGridLayout, QVBoxLayout, QLabel, QPushButton, \
|
||||||
|
QFileDialog, QMessageBox, QDialog
|
||||||
|
|
||||||
|
from userdata import liberation_install
|
||||||
|
|
||||||
|
|
||||||
|
class QLiberationPreferences(QFrame):
|
||||||
|
|
||||||
|
def __init__(self):
|
||||||
|
super(QLiberationPreferences, self).__init__()
|
||||||
|
self.saved_game_dir = ""
|
||||||
|
self.dcs_install_dir = ""
|
||||||
|
|
||||||
|
self.dcs_install_dir = liberation_install.get_dcs_install_directory()
|
||||||
|
self.saved_game_dir = liberation_install.get_saved_game_dir()
|
||||||
|
|
||||||
|
self.edit_dcs_install_dir = QLineEdit(self.dcs_install_dir)
|
||||||
|
self.edit_saved_game_dir = QLineEdit(self.saved_game_dir)
|
||||||
|
|
||||||
|
self.edit_dcs_install_dir.setMinimumWidth(300)
|
||||||
|
self.edit_saved_game_dir.setMinimumWidth(300)
|
||||||
|
|
||||||
|
self.browse_saved_game = QPushButton("Browse...")
|
||||||
|
self.browse_saved_game.clicked.connect(self.on_browse_saved_games)
|
||||||
|
self.browse_install_dir = QPushButton("Browse...")
|
||||||
|
self.browse_install_dir.clicked.connect(self.on_browse_installation_dir)
|
||||||
|
|
||||||
|
self.initUi()
|
||||||
|
|
||||||
|
def initUi(self):
|
||||||
|
main_layout = QVBoxLayout()
|
||||||
|
layout = QGridLayout()
|
||||||
|
layout.addWidget(QLabel("<strong>DCS saved game directory:</strong>"), 0, 0, alignment=Qt.AlignLeft)
|
||||||
|
layout.addWidget(self.edit_saved_game_dir, 1, 0, alignment=Qt.AlignRight)
|
||||||
|
layout.addWidget(self.browse_saved_game, 1, 1, alignment=Qt.AlignRight)
|
||||||
|
layout.addWidget(QLabel("<strong>DCS installation directory:</strong>"), 2, 0, alignment=Qt.AlignLeft)
|
||||||
|
layout.addWidget(self.edit_dcs_install_dir, 3, 0, alignment=Qt.AlignRight)
|
||||||
|
layout.addWidget(self.browse_install_dir, 3, 1, alignment=Qt.AlignRight)
|
||||||
|
|
||||||
|
main_layout.addLayout(layout)
|
||||||
|
main_layout.addStretch()
|
||||||
|
|
||||||
|
self.setLayout(main_layout)
|
||||||
|
|
||||||
|
def on_browse_saved_games(self):
|
||||||
|
saved_game_dir = str(QFileDialog.getExistingDirectory(self, "Select DCS Saved Game Directory"))
|
||||||
|
if saved_game_dir:
|
||||||
|
self.saved_game_dir = saved_game_dir
|
||||||
|
self.edit_saved_game_dir.setText(saved_game_dir)
|
||||||
|
|
||||||
|
def on_browse_installation_dir(self):
|
||||||
|
install_dir = str(QFileDialog.getExistingDirectory(self, "Select DCS Installation Directory"))
|
||||||
|
if install_dir:
|
||||||
|
self.dcs_install_dir = install_dir
|
||||||
|
self.edit_dcs_install_dir.setText(install_dir)
|
||||||
|
|
||||||
|
def apply(self):
|
||||||
|
|
||||||
|
print("Applying changes")
|
||||||
|
self.saved_game_dir = self.edit_saved_game_dir.text()
|
||||||
|
self.dcs_install_dir = self.edit_dcs_install_dir.text()
|
||||||
|
|
||||||
|
if not os.path.isdir(self.saved_game_dir):
|
||||||
|
error_dialog = QMessageBox.critical(self, "Wrong DCS Saved Games directory.",
|
||||||
|
self.saved_game_dir + " is not a valid directory",
|
||||||
|
QMessageBox.StandardButton.Ok)
|
||||||
|
error_dialog.exec_()
|
||||||
|
return False
|
||||||
|
|
||||||
|
if not os.path.isdir(self.dcs_install_dir):
|
||||||
|
error_dialog = QMessageBox.critical(self, "Wrong DCS installation directory.",
|
||||||
|
self.dcs_install_dir + " is not a valid directory",
|
||||||
|
QMessageBox.StandardButton.Ok)
|
||||||
|
error_dialog.exec_()
|
||||||
|
return False
|
||||||
|
|
||||||
|
if not os.path.isdir(os.path.join(self.dcs_install_dir, "Scripts")) and os.path.isfile(os.path.join(self.dcs_install_dir, "bin", "DCS.exe")):
|
||||||
|
error_dialog = QMessageBox.critical(self, "Wrong DCS installation directory.",
|
||||||
|
self.dcs_install_dir + " is not a valid DCS installation directory",
|
||||||
|
QMessageBox.StandardButton.Ok)
|
||||||
|
error_dialog.exec_()
|
||||||
|
return False
|
||||||
|
|
||||||
|
liberation_install.setup(self.saved_game_dir, self.dcs_install_dir)
|
||||||
|
liberation_install.save_config()
|
||||||
|
return True
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
37
qt_ui/windows/preferences/QLiberationPreferencesWindow.py
Normal file
37
qt_ui/windows/preferences/QLiberationPreferencesWindow.py
Normal file
@@ -0,0 +1,37 @@
|
|||||||
|
from PySide2.QtGui import QIcon, Qt
|
||||||
|
from PySide2.QtWidgets import QDialog, QVBoxLayout, QPushButton, QHBoxLayout
|
||||||
|
|
||||||
|
from qt_ui.windows.preferences.QLiberationPreferences import QLiberationPreferences
|
||||||
|
|
||||||
|
|
||||||
|
class QLiberationPreferencesWindow(QDialog):
|
||||||
|
|
||||||
|
def __init__(self):
|
||||||
|
super(QLiberationPreferencesWindow, self).__init__()
|
||||||
|
|
||||||
|
self.setModal(True)
|
||||||
|
self.setWindowTitle("Preferences")
|
||||||
|
self.setMinimumSize(300, 200)
|
||||||
|
self.setWindowIcon(QIcon("./resources/icon.png"))
|
||||||
|
self.preferences = QLiberationPreferences()
|
||||||
|
self.apply_button = QPushButton("Apply")
|
||||||
|
self.apply_button.clicked.connect(lambda : self.apply())
|
||||||
|
self.initUI()
|
||||||
|
|
||||||
|
def initUI(self):
|
||||||
|
layout = QVBoxLayout()
|
||||||
|
layout.addWidget(self.preferences)
|
||||||
|
layout.addStretch()
|
||||||
|
apply_btn_layout = QHBoxLayout()
|
||||||
|
apply_btn_layout.addStretch()
|
||||||
|
apply_btn_layout.addWidget(self.apply_button)
|
||||||
|
layout.addLayout(apply_btn_layout)
|
||||||
|
self.setLayout(layout)
|
||||||
|
|
||||||
|
def apply(self):
|
||||||
|
if self.preferences.apply():
|
||||||
|
print("Closing")
|
||||||
|
self.close()
|
||||||
|
else:
|
||||||
|
print("Not Closing")
|
||||||
|
|
||||||
@@ -98,11 +98,11 @@ class QSettingsWindow(QDialog):
|
|||||||
self.enemyAASkill.currentIndexChanged.connect(self.applySettings)
|
self.enemyAASkill.currentIndexChanged.connect(self.applySettings)
|
||||||
|
|
||||||
self.difficultyLayout.addWidget(QLabel("Player coalition skill"), 0, 0)
|
self.difficultyLayout.addWidget(QLabel("Player coalition skill"), 0, 0)
|
||||||
self.difficultyLayout.addWidget(self.playerCoalitionSkill, 0, 1)
|
self.difficultyLayout.addWidget(self.playerCoalitionSkill, 0, 1, Qt.AlignRight)
|
||||||
self.difficultyLayout.addWidget(QLabel("Enemy skill"), 1, 0)
|
self.difficultyLayout.addWidget(QLabel("Enemy skill"), 1, 0)
|
||||||
self.difficultyLayout.addWidget(self.enemyCoalitionSkill, 1, 1)
|
self.difficultyLayout.addWidget(self.enemyCoalitionSkill, 1, 1, Qt.AlignRight)
|
||||||
self.difficultyLayout.addWidget(QLabel("Enemy AA and vehicles skill"), 2, 0)
|
self.difficultyLayout.addWidget(QLabel("Enemy AA and vehicles skill"), 2, 0)
|
||||||
self.difficultyLayout.addWidget(self.enemyAASkill, 2, 1)
|
self.difficultyLayout.addWidget(self.enemyAASkill, 2, 1, Qt.AlignRight)
|
||||||
|
|
||||||
self.difficultyLabel = QComboBox()
|
self.difficultyLabel = QComboBox()
|
||||||
[self.difficultyLabel.addItem(t) for t in CONST.LABELS_OPTIONS]
|
[self.difficultyLabel.addItem(t) for t in CONST.LABELS_OPTIONS]
|
||||||
@@ -110,13 +110,13 @@ class QSettingsWindow(QDialog):
|
|||||||
self.difficultyLabel.currentIndexChanged.connect(self.applySettings)
|
self.difficultyLabel.currentIndexChanged.connect(self.applySettings)
|
||||||
|
|
||||||
self.difficultyLayout.addWidget(QLabel("In Game Labels"), 3, 0)
|
self.difficultyLayout.addWidget(QLabel("In Game Labels"), 3, 0)
|
||||||
self.difficultyLayout.addWidget(self.difficultyLabel, 3, 1)
|
self.difficultyLayout.addWidget(self.difficultyLabel, 3, 1, Qt.AlignRight)
|
||||||
|
|
||||||
self.noNightMission = QCheckBox()
|
self.noNightMission = QCheckBox()
|
||||||
self.noNightMission.setChecked(self.game.settings.night_disabled)
|
self.noNightMission.setChecked(self.game.settings.night_disabled)
|
||||||
self.noNightMission.toggled.connect(self.applySettings)
|
self.noNightMission.toggled.connect(self.applySettings)
|
||||||
self.difficultyLayout.addWidget(QLabel("No night missions"), 4, 0)
|
self.difficultyLayout.addWidget(QLabel("No night missions"), 4, 0)
|
||||||
self.difficultyLayout.addWidget(self.noNightMission, 4, 1)
|
self.difficultyLayout.addWidget(self.noNightMission, 4, 1, Qt.AlignRight)
|
||||||
|
|
||||||
|
|
||||||
def initGeneratorLayout(self):
|
def initGeneratorLayout(self):
|
||||||
@@ -135,7 +135,7 @@ class QSettingsWindow(QDialog):
|
|||||||
self.supercarrier.toggled.connect(self.applySettings)
|
self.supercarrier.toggled.connect(self.applySettings)
|
||||||
|
|
||||||
self.gameplayLayout.addWidget(QLabel("Use Supercarrier Module"), 0, 0)
|
self.gameplayLayout.addWidget(QLabel("Use Supercarrier Module"), 0, 0)
|
||||||
self.gameplayLayout.addWidget(self.supercarrier, 0, 1)
|
self.gameplayLayout.addWidget(self.supercarrier, 0, 1, Qt.AlignRight)
|
||||||
|
|
||||||
self.performance = QGroupBox("Performance")
|
self.performance = QGroupBox("Performance")
|
||||||
self.performanceLayout = QGridLayout();
|
self.performanceLayout = QGridLayout();
|
||||||
@@ -162,16 +162,22 @@ class QSettingsWindow(QDialog):
|
|||||||
self.infantry.setChecked(self.game.settings.perf_infantry)
|
self.infantry.setChecked(self.game.settings.perf_infantry)
|
||||||
self.infantry.toggled.connect(self.applySettings)
|
self.infantry.toggled.connect(self.applySettings)
|
||||||
|
|
||||||
|
self.ai_parking_start = QCheckBox()
|
||||||
|
self.ai_parking_start.setChecked(self.game.settings.perf_ai_parking_start)
|
||||||
|
self.ai_parking_start.toggled.connect(self.applySettings)
|
||||||
|
|
||||||
self.performanceLayout.addWidget(QLabel("Smoke visual effect on frontline"), 0, 0)
|
self.performanceLayout.addWidget(QLabel("Smoke visual effect on frontline"), 0, 0)
|
||||||
self.performanceLayout.addWidget(self.smoke, 0, 1)
|
self.performanceLayout.addWidget(self.smoke, 0, 1, alignment=Qt.AlignRight)
|
||||||
self.performanceLayout.addWidget(QLabel("SAM starts in RED alert mode"), 1, 0)
|
self.performanceLayout.addWidget(QLabel("SAM starts in RED alert mode"), 1, 0)
|
||||||
self.performanceLayout.addWidget(self.red_alert, 1, 1)
|
self.performanceLayout.addWidget(self.red_alert, 1, 1, alignment=Qt.AlignRight)
|
||||||
self.performanceLayout.addWidget(QLabel("Artillery strikes"), 2, 0)
|
self.performanceLayout.addWidget(QLabel("Artillery strikes"), 2, 0)
|
||||||
self.performanceLayout.addWidget(self.arti, 2, 1)
|
self.performanceLayout.addWidget(self.arti, 2, 1, alignment=Qt.AlignRight)
|
||||||
self.performanceLayout.addWidget(QLabel("Moving ground units"), 3, 0)
|
self.performanceLayout.addWidget(QLabel("Moving ground units"), 3, 0)
|
||||||
self.performanceLayout.addWidget(self.moving_units, 3, 1)
|
self.performanceLayout.addWidget(self.moving_units, 3, 1, alignment=Qt.AlignRight)
|
||||||
self.performanceLayout.addWidget(QLabel("Generate infantry squads along vehicles"), 4, 0)
|
self.performanceLayout.addWidget(QLabel("Generate infantry squads along vehicles"), 4, 0)
|
||||||
self.performanceLayout.addWidget(self.infantry, 4, 1)
|
self.performanceLayout.addWidget(self.infantry, 4, 1, alignment=Qt.AlignRight)
|
||||||
|
self.performanceLayout.addWidget(QLabel("AI planes parking start (AI starts in flight if disabled)"), 5, 0)
|
||||||
|
self.performanceLayout.addWidget(self.ai_parking_start, 5, 1, alignment=Qt.AlignRight)
|
||||||
|
|
||||||
self.generatorLayout.addWidget(self.gameplay)
|
self.generatorLayout.addWidget(self.gameplay)
|
||||||
self.generatorLayout.addWidget(QLabel("Disabling settings below may improve performance, but will impact the overall quality of the experience."))
|
self.generatorLayout.addWidget(QLabel("Disabling settings below may improve performance, but will impact the overall quality of the experience."))
|
||||||
@@ -230,6 +236,7 @@ class QSettingsWindow(QDialog):
|
|||||||
self.game.settings.perf_artillery = self.arti.isChecked()
|
self.game.settings.perf_artillery = self.arti.isChecked()
|
||||||
self.game.settings.perf_moving_units = self.moving_units.isChecked()
|
self.game.settings.perf_moving_units = self.moving_units.isChecked()
|
||||||
self.game.settings.perf_infantry = self.infantry.isChecked()
|
self.game.settings.perf_infantry = self.infantry.isChecked()
|
||||||
|
self.game.settings.perf_ai_parking_start = self.ai_parking_start.isChecked()
|
||||||
|
|
||||||
GameUpdateSignal.get_instance().updateGame(self.game)
|
GameUpdateSignal.get_instance().updateGame(self.game)
|
||||||
|
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
pydcs>=0.9.4
|
pydcs>=0.9.4
|
||||||
Pyside2>=5.13.0
|
Pyside2>=5.13.0
|
||||||
pyinstaller==3.5
|
pyinstaller==3.5
|
||||||
|
pyproj==2.6.1.post1
|
||||||
|
|||||||
@@ -73,33 +73,29 @@ local unitPayloads = {
|
|||||||
["name"] = "STRIKE",
|
["name"] = "STRIKE",
|
||||||
["pylons"] = {
|
["pylons"] = {
|
||||||
[1] = {
|
[1] = {
|
||||||
["CLSID"] = "DIS_WMD7",
|
["CLSID"] = "DIS_LS_6_500",
|
||||||
["num"] = 4,
|
["num"] = 6,
|
||||||
},
|
},
|
||||||
[2] = {
|
[2] = {
|
||||||
|
["CLSID"] = "DIS_LS_6_500",
|
||||||
|
["num"] = 5,
|
||||||
|
},
|
||||||
|
[3] = {
|
||||||
|
["CLSID"] = "DIS_LS_6_500",
|
||||||
|
["num"] = 3,
|
||||||
|
},
|
||||||
|
[4] = {
|
||||||
|
["CLSID"] = "DIS_LS_6_500",
|
||||||
|
["num"] = 2,
|
||||||
|
},
|
||||||
|
[5] = {
|
||||||
["CLSID"] = "DIS_PL-5EII",
|
["CLSID"] = "DIS_PL-5EII",
|
||||||
["num"] = 1,
|
["num"] = 1,
|
||||||
},
|
},
|
||||||
[3] = {
|
[6] = {
|
||||||
["CLSID"] = "DIS_PL-5EII",
|
["CLSID"] = "DIS_PL-5EII",
|
||||||
["num"] = 7,
|
["num"] = 7,
|
||||||
},
|
},
|
||||||
[4] = {
|
|
||||||
["CLSID"] = "DIS_GBU_12_DUAL",
|
|
||||||
["num"] = 6,
|
|
||||||
},
|
|
||||||
[5] = {
|
|
||||||
["CLSID"] = "DIS_GBU_12_DUAL",
|
|
||||||
["num"] = 2,
|
|
||||||
},
|
|
||||||
[6] = {
|
|
||||||
["CLSID"] = "DIS_GBU_16",
|
|
||||||
["num"] = 3,
|
|
||||||
},
|
|
||||||
[7] = {
|
|
||||||
["CLSID"] = "DIS_GBU_16",
|
|
||||||
["num"] = 5,
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
["tasks"] = {
|
["tasks"] = {
|
||||||
[1] = 10,
|
[1] = 10,
|
||||||
|
|||||||
85
resources/customized_payloads/P-47D-30.lua
Normal file
85
resources/customized_payloads/P-47D-30.lua
Normal file
@@ -0,0 +1,85 @@
|
|||||||
|
local unitPayloads = {
|
||||||
|
["name"] = "P-47D-30",
|
||||||
|
["payloads"] = {
|
||||||
|
[1] = {
|
||||||
|
["name"] = "CAS",
|
||||||
|
["pylons"] = {
|
||||||
|
[1] = {
|
||||||
|
["CLSID"] = "{AN-M64}",
|
||||||
|
["num"] = 3,
|
||||||
|
},
|
||||||
|
[2] = {
|
||||||
|
["CLSID"] = "{AN-M64}",
|
||||||
|
["num"] = 2,
|
||||||
|
},
|
||||||
|
[3] = {
|
||||||
|
["CLSID"] = "{AN-M64}",
|
||||||
|
["num"] = 1,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
["tasks"] = {
|
||||||
|
[1] = 11,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
[2] = {
|
||||||
|
["name"] = "STRIKE",
|
||||||
|
["pylons"] = {
|
||||||
|
[1] = {
|
||||||
|
["CLSID"] = "{AN_M65}",
|
||||||
|
["num"] = 3,
|
||||||
|
},
|
||||||
|
[2] = {
|
||||||
|
["CLSID"] = "{AN_M65}",
|
||||||
|
["num"] = 2,
|
||||||
|
},
|
||||||
|
[3] = {
|
||||||
|
["CLSID"] = "{AN-M64}",
|
||||||
|
["num"] = 1,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
["tasks"] = {
|
||||||
|
[1] = 11,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
[3] = {
|
||||||
|
["name"] = "ANTISTRIKE",
|
||||||
|
["pylons"] = {
|
||||||
|
},
|
||||||
|
["tasks"] = {
|
||||||
|
[1] = 11,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
[4] = {
|
||||||
|
["name"] = "CAP",
|
||||||
|
["pylons"] = {
|
||||||
|
},
|
||||||
|
["tasks"] = {
|
||||||
|
[1] = 11,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
[5] = {
|
||||||
|
["name"] = "SEAD",
|
||||||
|
["pylons"] = {
|
||||||
|
[1] = {
|
||||||
|
["CLSID"] = "{AN_M57}",
|
||||||
|
["num"] = 3,
|
||||||
|
},
|
||||||
|
[2] = {
|
||||||
|
["CLSID"] = "{AN_M57}",
|
||||||
|
["num"] = 2,
|
||||||
|
},
|
||||||
|
[3] = {
|
||||||
|
["CLSID"] = "{AN_M57}",
|
||||||
|
["num"] = 1,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
["tasks"] = {
|
||||||
|
[1] = 11,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
["tasks"] = {
|
||||||
|
},
|
||||||
|
["unitType"] = "P-47D-30",
|
||||||
|
}
|
||||||
|
return unitPayloads
|
||||||
@@ -2,156 +2,150 @@ local unitPayloads = {
|
|||||||
["name"] = "Su-24M",
|
["name"] = "Su-24M",
|
||||||
["payloads"] = {
|
["payloads"] = {
|
||||||
[1] = {
|
[1] = {
|
||||||
["name"] = "ANTISHIP",
|
|
||||||
["pylons"] = {
|
|
||||||
[1] = {
|
|
||||||
["CLSID"] = "{6DADF342-D4BA-4D8A-B081-BA928C4AF86D}",
|
|
||||||
["num"] = 1,
|
|
||||||
},
|
|
||||||
[2] = {
|
|
||||||
["CLSID"] = "{D8F2C90B-887B-4B9E-9FE2-996BC9E9AF03}",
|
|
||||||
["num"] = 2,
|
|
||||||
},
|
|
||||||
[3] = {
|
|
||||||
["CLSID"] = "{0519A264-0AB6-11d6-9193-00A0249B6F00}",
|
|
||||||
["num"] = 5,
|
|
||||||
},
|
|
||||||
[4] = {
|
|
||||||
["CLSID"] = "{D8F2C90B-887B-4B9E-9FE2-996BC9E9AF03}",
|
|
||||||
["num"] = 7,
|
|
||||||
},
|
|
||||||
[5] = {
|
|
||||||
["CLSID"] = "{6DADF342-D4BA-4D8A-B081-BA928C4AF86D}",
|
|
||||||
["num"] = 8,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
["tasks"] = {
|
|
||||||
[1] = 29,
|
|
||||||
[2] = 30,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
[2] = {
|
|
||||||
["name"] = "CAS",
|
|
||||||
["pylons"] = {
|
|
||||||
[1] = {
|
|
||||||
["CLSID"] = "{292960BB-6518-41AC-BADA-210D65D5073C}",
|
|
||||||
["num"] = 1,
|
|
||||||
},
|
|
||||||
[2] = {
|
|
||||||
["CLSID"] = "{292960BB-6518-41AC-BADA-210D65D5073C}",
|
|
||||||
["num"] = 2,
|
|
||||||
},
|
|
||||||
[3] = {
|
|
||||||
["CLSID"] = "{292960BB-6518-41AC-BADA-210D65D5073C}",
|
|
||||||
["num"] = 7,
|
|
||||||
},
|
|
||||||
[4] = {
|
|
||||||
["CLSID"] = "{292960BB-6518-41AC-BADA-210D65D5073C}",
|
|
||||||
["num"] = 8,
|
|
||||||
},
|
|
||||||
[5] = {
|
|
||||||
["CLSID"] = "{3858707D-F5D5-4bbb-BDD8-ABB0530EBC7C}",
|
|
||||||
["num"] = 6,
|
|
||||||
},
|
|
||||||
[6] = {
|
|
||||||
["CLSID"] = "{3858707D-F5D5-4bbb-BDD8-ABB0530EBC7C}",
|
|
||||||
["num"] = 3,
|
|
||||||
},
|
|
||||||
[7] = {
|
|
||||||
["CLSID"] = "{3C612111-C7AD-476E-8A8E-2485812F4E5C}",
|
|
||||||
["num"] = 4,
|
|
||||||
},
|
|
||||||
[8] = {
|
|
||||||
["CLSID"] = "{3C612111-C7AD-476E-8A8E-2485812F4E5C}",
|
|
||||||
["num"] = 5,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
["tasks"] = {
|
|
||||||
[1] = 31,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
[3] = {
|
|
||||||
["name"] = "STRIKE",
|
|
||||||
["pylons"] = {
|
|
||||||
[1] = {
|
|
||||||
["CLSID"] = "{4203753F-8198-4E85-9924-6F8FF679F9FF}",
|
|
||||||
["num"] = 1,
|
|
||||||
},
|
|
||||||
[2] = {
|
|
||||||
["CLSID"] = "{4203753F-8198-4E85-9924-6F8FF679F9FF}",
|
|
||||||
["num"] = 2,
|
|
||||||
},
|
|
||||||
[3] = {
|
|
||||||
["CLSID"] = "{4203753F-8198-4E85-9924-6F8FF679F9FF}",
|
|
||||||
["num"] = 3,
|
|
||||||
},
|
|
||||||
[4] = {
|
|
||||||
["CLSID"] = "{4203753F-8198-4E85-9924-6F8FF679F9FF}",
|
|
||||||
["num"] = 4,
|
|
||||||
},
|
|
||||||
[5] = {
|
|
||||||
["CLSID"] = "{4203753F-8198-4E85-9924-6F8FF679F9FF}",
|
|
||||||
["num"] = 5,
|
|
||||||
},
|
|
||||||
[6] = {
|
|
||||||
["CLSID"] = "{4203753F-8198-4E85-9924-6F8FF679F9FF}",
|
|
||||||
["num"] = 6,
|
|
||||||
},
|
|
||||||
[7] = {
|
|
||||||
["CLSID"] = "{4203753F-8198-4E85-9924-6F8FF679F9FF}",
|
|
||||||
["num"] = 7,
|
|
||||||
},
|
|
||||||
[8] = {
|
|
||||||
["CLSID"] = "{4203753F-8198-4E85-9924-6F8FF679F9FF}",
|
|
||||||
["num"] = 8,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
["tasks"] = {
|
|
||||||
[1] = 31,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
[4] = {
|
|
||||||
["name"] = "SEAD",
|
["name"] = "SEAD",
|
||||||
["pylons"] = {
|
["pylons"] = {
|
||||||
[1] = {
|
[1] = {
|
||||||
["CLSID"] = "{6DADF342-D4BA-4D8A-B081-BA928C4AF86D}",
|
|
||||||
["num"] = 1,
|
|
||||||
},
|
|
||||||
[2] = {
|
|
||||||
["CLSID"] = "{FE382A68-8620-4AC0-BDF5-709BFE3977D7}",
|
|
||||||
["num"] = 2,
|
|
||||||
},
|
|
||||||
[3] = {
|
|
||||||
["CLSID"] = "{0519A264-0AB6-11d6-9193-00A0249B6F00}",
|
|
||||||
["num"] = 5,
|
|
||||||
},
|
|
||||||
[4] = {
|
|
||||||
["CLSID"] = "{FE382A68-8620-4AC0-BDF5-709BFE3977D7}",
|
["CLSID"] = "{FE382A68-8620-4AC0-BDF5-709BFE3977D7}",
|
||||||
["num"] = 7,
|
["num"] = 7,
|
||||||
},
|
},
|
||||||
[5] = {
|
[2] = {
|
||||||
["CLSID"] = "{6DADF342-D4BA-4D8A-B081-BA928C4AF86D}",
|
["CLSID"] = "{E86C5AA5-6D49-4F00-AD2E-79A62D6DDE26}",
|
||||||
["num"] = 8,
|
["num"] = 8,
|
||||||
},
|
},
|
||||||
|
[3] = {
|
||||||
|
["CLSID"] = "{E86C5AA5-6D49-4F00-AD2E-79A62D6DDE26}",
|
||||||
|
["num"] = 1,
|
||||||
|
},
|
||||||
|
[4] = {
|
||||||
|
["CLSID"] = "{FE382A68-8620-4AC0-BDF5-709BFE3977D7}",
|
||||||
|
["num"] = 2,
|
||||||
|
},
|
||||||
},
|
},
|
||||||
["tasks"] = {
|
["tasks"] = {
|
||||||
[1] = 29,
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
[5] = {
|
[2] = {
|
||||||
|
["name"] = "STRIKE",
|
||||||
|
["pylons"] = {
|
||||||
|
[1] = {
|
||||||
|
["CLSID"] = "{KAB_1500LG_LOADOUT}",
|
||||||
|
["num"] = 7,
|
||||||
|
},
|
||||||
|
[2] = {
|
||||||
|
["CLSID"] = "{3C612111-C7AD-476E-8A8E-2485812F4E5C}",
|
||||||
|
["num"] = 8,
|
||||||
|
},
|
||||||
|
[3] = {
|
||||||
|
["CLSID"] = "{3C612111-C7AD-476E-8A8E-2485812F4E5C}",
|
||||||
|
["num"] = 1,
|
||||||
|
},
|
||||||
|
[4] = {
|
||||||
|
["CLSID"] = "{KAB_1500LG_LOADOUT}",
|
||||||
|
["num"] = 2,
|
||||||
|
},
|
||||||
|
[5] = {
|
||||||
|
["CLSID"] = "{E2C426E3-8B10-4E09-B733-9CDC26520F48}",
|
||||||
|
["num"] = 3,
|
||||||
|
},
|
||||||
|
[6] = {
|
||||||
|
["CLSID"] = "{E2C426E3-8B10-4E09-B733-9CDC26520F48}",
|
||||||
|
["num"] = 6,
|
||||||
|
},
|
||||||
|
[7] = {
|
||||||
|
["CLSID"] = "{3C612111-C7AD-476E-8A8E-2485812F4E5C}",
|
||||||
|
["num"] = 4,
|
||||||
|
},
|
||||||
|
[8] = {
|
||||||
|
["CLSID"] = "{3C612111-C7AD-476E-8A8E-2485812F4E5C}",
|
||||||
|
["num"] = 5,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
["tasks"] = {
|
||||||
|
},
|
||||||
|
},
|
||||||
|
[3] = {
|
||||||
["name"] = "CAP",
|
["name"] = "CAP",
|
||||||
["pylons"] = {
|
["pylons"] = {
|
||||||
[1] = {
|
[1] = {
|
||||||
["CLSID"] = "{B0DBC591-0F52-4F7D-AD7B-51E67725FB81}",
|
["CLSID"] = "{7D7EC917-05F6-49D4-8045-61FC587DD019}",
|
||||||
["num"] = 1,
|
["num"] = 7,
|
||||||
},
|
},
|
||||||
[2] = {
|
[2] = {
|
||||||
["CLSID"] = "{275A2855-4A79-4B2D-B082-91EA2ADF4691}",
|
["CLSID"] = "{275A2855-4A79-4B2D-B082-91EA2ADF4691}",
|
||||||
["num"] = 8,
|
["num"] = 8,
|
||||||
},
|
},
|
||||||
|
[3] = {
|
||||||
|
["CLSID"] = "{B0DBC591-0F52-4F7D-AD7B-51E67725FB81}",
|
||||||
|
["num"] = 1,
|
||||||
|
},
|
||||||
|
[4] = {
|
||||||
|
["CLSID"] = "{7D7EC917-05F6-49D4-8045-61FC587DD019}",
|
||||||
|
["num"] = 2,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
["tasks"] = {
|
||||||
|
},
|
||||||
|
},
|
||||||
|
[4] = {
|
||||||
|
["name"] = "ANTISHIP",
|
||||||
|
["pylons"] = {
|
||||||
|
[1] = {
|
||||||
|
["CLSID"] = "{D8F2C90B-887B-4B9E-9FE2-996BC9E9AF03}",
|
||||||
|
["num"] = 7,
|
||||||
|
},
|
||||||
|
[2] = {
|
||||||
|
["CLSID"] = "{E86C5AA5-6D49-4F00-AD2E-79A62D6DDE26}",
|
||||||
|
["num"] = 8,
|
||||||
|
},
|
||||||
|
[3] = {
|
||||||
|
["CLSID"] = "{E86C5AA5-6D49-4F00-AD2E-79A62D6DDE26}",
|
||||||
|
["num"] = 1,
|
||||||
|
},
|
||||||
|
[4] = {
|
||||||
|
["CLSID"] = "{D8F2C90B-887B-4B9E-9FE2-996BC9E9AF03}",
|
||||||
|
["num"] = 2,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
["tasks"] = {
|
||||||
|
},
|
||||||
|
},
|
||||||
|
[5] = {
|
||||||
|
["name"] = "CAS",
|
||||||
|
["pylons"] = {
|
||||||
|
[1] = {
|
||||||
|
["CLSID"] = "{6DADF342-D4BA-4D8A-B081-BA928C4AF86D}",
|
||||||
|
["num"] = 8,
|
||||||
|
},
|
||||||
|
[2] = {
|
||||||
|
["CLSID"] = "{6DADF342-D4BA-4D8A-B081-BA928C4AF86D}",
|
||||||
|
["num"] = 1,
|
||||||
|
},
|
||||||
|
[3] = {
|
||||||
|
["CLSID"] = "{3858707D-F5D5-4bbb-BDD8-ABB0530EBC7C}",
|
||||||
|
["num"] = 2,
|
||||||
|
},
|
||||||
|
[4] = {
|
||||||
|
["CLSID"] = "{3858707D-F5D5-4bbb-BDD8-ABB0530EBC7C}",
|
||||||
|
["num"] = 7,
|
||||||
|
},
|
||||||
|
[5] = {
|
||||||
|
["CLSID"] = "{3858707D-F5D5-4bbb-BDD8-ABB0530EBC7C}",
|
||||||
|
["num"] = 6,
|
||||||
|
},
|
||||||
|
[6] = {
|
||||||
|
["CLSID"] = "{3858707D-F5D5-4bbb-BDD8-ABB0530EBC7C}",
|
||||||
|
["num"] = 3,
|
||||||
|
},
|
||||||
|
[7] = {
|
||||||
|
["CLSID"] = "{3C612111-C7AD-476E-8A8E-2485812F4E5C}",
|
||||||
|
["num"] = 5,
|
||||||
|
},
|
||||||
|
[8] = {
|
||||||
|
["CLSID"] = "{3C612111-C7AD-476E-8A8E-2485812F4E5C}",
|
||||||
|
["num"] = 4,
|
||||||
|
},
|
||||||
},
|
},
|
||||||
["tasks"] = {
|
["tasks"] = {
|
||||||
[1] = 32,
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|||||||
Binary file not shown.
Binary file not shown.
@@ -120,34 +120,13 @@ class Base:
|
|||||||
target_dict[unit_type] = target_dict.get(unit_type, 0) + unit_count
|
target_dict[unit_type] = target_dict.get(unit_type, 0) + unit_count
|
||||||
|
|
||||||
def commit_losses(self, units_lost: typing.Dict[typing.Any, int]):
|
def commit_losses(self, units_lost: typing.Dict[typing.Any, int]):
|
||||||
# advanced SAM sites have multiple units - this code was not at all set up to handle that
|
|
||||||
# to avoid having to restructure a bunch of upstream code, we track total destroyed units and
|
|
||||||
# use that to determine if a site was destroyed
|
|
||||||
# this can be thought of as the enemy re-distributing parts of SAM sites to keep as many
|
|
||||||
# operational as possible (pulling specific units from ...storage... to bring them back online
|
|
||||||
# if non-letal damage was done)
|
|
||||||
# in the future, I may add more depth to this (e.g. a base having a certain number of spares and tracking
|
|
||||||
# the number of pieces of each site), but for now this is what we get
|
|
||||||
sams_destroyed = {}
|
|
||||||
# we count complex SAM sites at the end - don't double count
|
|
||||||
aa_skip = [
|
|
||||||
AirDefence.SAM_SA_6_Kub_LN_2P25,
|
|
||||||
AirDefence.SAM_SA_3_S_125_LN_5P73,
|
|
||||||
AirDefence.SAM_SA_11_Buk_LN_9A310M1
|
|
||||||
]
|
|
||||||
for unit_type, count in units_lost.items():
|
for unit_type, count in units_lost.items():
|
||||||
if unit_type in db.SAM_CONVERT or unit_type in db.SAM_CONVERT['except']:
|
|
||||||
# unit is part of an advanced SAM site, which means it will fail the below check
|
|
||||||
try:
|
|
||||||
sams_destroyed[unit_type] += 1
|
|
||||||
except KeyError:
|
|
||||||
sams_destroyed[unit_type] = 1
|
|
||||||
if unit_type in self.aircraft:
|
if unit_type in self.aircraft:
|
||||||
target_array = self.aircraft
|
target_array = self.aircraft
|
||||||
elif unit_type in self.armor:
|
elif unit_type in self.armor:
|
||||||
target_array = self.armor
|
target_array = self.armor
|
||||||
elif unit_type in self.aa and unit_type not in aa_skip:
|
|
||||||
target_array = self.aa
|
|
||||||
else:
|
else:
|
||||||
print("Base didn't find event type {}".format(unit_type))
|
print("Base didn't find event type {}".format(unit_type))
|
||||||
continue
|
continue
|
||||||
@@ -160,22 +139,6 @@ class Base:
|
|||||||
if target_array[unit_type] == 0:
|
if target_array[unit_type] == 0:
|
||||||
del target_array[unit_type]
|
del target_array[unit_type]
|
||||||
|
|
||||||
# now that we have a complete picture of the SAM sites destroyed, determine if any were destroyed
|
|
||||||
for sam_site, count in sams_destroyed.items():
|
|
||||||
dead_count = aaa.num_sam_dead(sam_site, count)
|
|
||||||
try:
|
|
||||||
modified_sam_site = db.SAM_CONVERT[sam_site]
|
|
||||||
except KeyError:
|
|
||||||
modified_sam_site = db.SAM_CONVERT[sam_site]['except']
|
|
||||||
|
|
||||||
if modified_sam_site in self.aa:
|
|
||||||
self.aa[modified_sam_site] = max(
|
|
||||||
self.aa[modified_sam_site] - dead_count,
|
|
||||||
0
|
|
||||||
)
|
|
||||||
if self.aa[modified_sam_site] == 0:
|
|
||||||
del self.aa[modified_sam_site]
|
|
||||||
|
|
||||||
def affect_strength(self, amount):
|
def affect_strength(self, amount):
|
||||||
self.strength += amount
|
self.strength += amount
|
||||||
if self.strength > BASE_MAX_STRENGTH:
|
if self.strength > BASE_MAX_STRENGTH:
|
||||||
|
|||||||
@@ -180,7 +180,7 @@ class NorthCaucasus(ConflictTheater):
|
|||||||
super(NorthCaucasus, self).__init__()
|
super(NorthCaucasus, self).__init__()
|
||||||
|
|
||||||
self.kutaisi = ControlPoint.from_airport(caucasus.Kutaisi, LAND, SIZE_SMALL, IMPORTANCE_LOW)
|
self.kutaisi = ControlPoint.from_airport(caucasus.Kutaisi, LAND, SIZE_SMALL, IMPORTANCE_LOW)
|
||||||
self.soganlug = ControlPoint.from_airport(caucasus.Soganlug, LAND, SIZE_SMALL, IMPORTANCE_LOW)
|
self.vaziani = ControlPoint.from_airport(caucasus.Vaziani, LAND, SIZE_SMALL, IMPORTANCE_LOW)
|
||||||
self.maykop = ControlPoint.from_airport(caucasus.Maykop_Khanskaya, LAND, SIZE_LARGE, IMPORTANCE_HIGH)
|
self.maykop = ControlPoint.from_airport(caucasus.Maykop_Khanskaya, LAND, SIZE_LARGE, IMPORTANCE_HIGH)
|
||||||
self.beslan = ControlPoint.from_airport(caucasus.Beslan, LAND, SIZE_REGULAR, IMPORTANCE_LOW)
|
self.beslan = ControlPoint.from_airport(caucasus.Beslan, LAND, SIZE_REGULAR, IMPORTANCE_LOW)
|
||||||
self.nalchik = ControlPoint.from_airport(caucasus.Nalchik, LAND, SIZE_REGULAR, 1.1)
|
self.nalchik = ControlPoint.from_airport(caucasus.Nalchik, LAND, SIZE_REGULAR, 1.1)
|
||||||
@@ -188,12 +188,12 @@ class NorthCaucasus(ConflictTheater):
|
|||||||
self.mozdok = ControlPoint.from_airport(caucasus.Mozdok, LAND, SIZE_BIG, 1.1)
|
self.mozdok = ControlPoint.from_airport(caucasus.Mozdok, LAND, SIZE_BIG, 1.1)
|
||||||
self.carrier_1 = ControlPoint.carrier("Carrier", mapping.Point(-285810.6875, 496399.1875))
|
self.carrier_1 = ControlPoint.carrier("Carrier", mapping.Point(-285810.6875, 496399.1875))
|
||||||
|
|
||||||
self.soganlug.frontline_offset = 0.5
|
self.vaziani.frontline_offset = 0.5
|
||||||
self.soganlug.base.strength = 1
|
self.vaziani.base.strength = 1
|
||||||
|
|
||||||
self.add_controlpoint(self.kutaisi, connected_to=[self.soganlug])
|
self.add_controlpoint(self.kutaisi, connected_to=[self.vaziani])
|
||||||
self.add_controlpoint(self.soganlug, connected_to=[self.beslan, self.kutaisi])
|
self.add_controlpoint(self.vaziani, connected_to=[self.beslan, self.kutaisi])
|
||||||
self.add_controlpoint(self.beslan, connected_to=[self.soganlug, self.mozdok, self.nalchik])
|
self.add_controlpoint(self.beslan, connected_to=[self.vaziani, self.mozdok, self.nalchik])
|
||||||
self.add_controlpoint(self.nalchik, connected_to=[self.beslan, self.mozdok, self.mineralnye])
|
self.add_controlpoint(self.nalchik, connected_to=[self.beslan, self.mozdok, self.mineralnye])
|
||||||
self.add_controlpoint(self.mozdok, connected_to=[self.nalchik, self.beslan, self.mineralnye])
|
self.add_controlpoint(self.mozdok, connected_to=[self.nalchik, self.beslan, self.mineralnye])
|
||||||
self.add_controlpoint(self.mineralnye, connected_to=[self.nalchik, self.mozdok, self.maykop])
|
self.add_controlpoint(self.mineralnye, connected_to=[self.nalchik, self.mozdok, self.maykop])
|
||||||
@@ -201,5 +201,5 @@ class NorthCaucasus(ConflictTheater):
|
|||||||
self.add_controlpoint(self.carrier_1, connected_to=[])
|
self.add_controlpoint(self.carrier_1, connected_to=[])
|
||||||
|
|
||||||
self.carrier_1.captured = True
|
self.carrier_1.captured = True
|
||||||
self.soganlug.captured = True
|
self.vaziani.captured = True
|
||||||
self.kutaisi.captured = True
|
self.kutaisi.captured = True
|
||||||
|
|||||||
@@ -38,6 +38,7 @@ class ControlPoint:
|
|||||||
cptype: ControlPointType = None
|
cptype: ControlPointType = None
|
||||||
|
|
||||||
ICLS_counter = 1
|
ICLS_counter = 1
|
||||||
|
alt = 0
|
||||||
|
|
||||||
def __init__(self, id: int, name: str, position: Point, at, radials: typing.Collection[int], size: int, importance: float,
|
def __init__(self, id: int, name: str, position: Point, at, radials: typing.Collection[int], size: int, importance: float,
|
||||||
has_frontline=True, cptype=ControlPointType.AIRBASE):
|
has_frontline=True, cptype=ControlPointType.AIRBASE):
|
||||||
@@ -64,18 +65,21 @@ class ControlPoint:
|
|||||||
self.tacanN = None
|
self.tacanN = None
|
||||||
self.tacanI = "TAC"
|
self.tacanI = "TAC"
|
||||||
self.icls = 0
|
self.icls = 0
|
||||||
|
self.airport = None
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def from_airport(cls, airport: Airport, radials: typing.Collection[int], size: int, importance: float, has_frontline=True):
|
def from_airport(cls, airport: Airport, radials: typing.Collection[int], size: int, importance: float, has_frontline=True):
|
||||||
assert airport
|
assert airport
|
||||||
return cls(airport.id, airport.name, airport.position, airport, radials, size, importance, has_frontline, cptype=ControlPointType.AIRBASE)
|
obj = cls(airport.id, airport.name, airport.position, airport, radials, size, importance, has_frontline, cptype=ControlPointType.AIRBASE)
|
||||||
|
obj.airport = airport()
|
||||||
|
return obj
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def carrier(cls, name: str, at: Point, id: int = 1001):
|
def carrier(cls, name: str, at: Point, id: int = 1001):
|
||||||
import theater.conflicttheater
|
import theater.conflicttheater
|
||||||
cp = cls(id, name, at, at, theater.conflicttheater.LAND, theater.conflicttheater.SIZE_SMALL, 1,
|
cp = cls(id, name, at, at, theater.conflicttheater.LAND, theater.conflicttheater.SIZE_SMALL, 1,
|
||||||
has_frontline=False, cptype=ControlPointType.AIRCRAFT_CARRIER_GROUP)
|
has_frontline=False, cptype=ControlPointType.AIRCRAFT_CARRIER_GROUP)
|
||||||
cp.tacanY = random.choice([True, False])
|
cp.tacanY = False
|
||||||
cp.tacanN = random.randint(26, 49)
|
cp.tacanN = random.randint(26, 49)
|
||||||
cp.tacanI = random.choice(["STE", "CVN", "CVH", "CCV", "ACC", "ARC", "GER", "ABR", "LIN", "TRU"])
|
cp.tacanI = random.choice(["STE", "CVN", "CVH", "CCV", "ACC", "ARC", "GER", "ABR", "LIN", "TRU"])
|
||||||
ControlPoint.ICLS_counter = ControlPoint.ICLS_counter + 1
|
ControlPoint.ICLS_counter = ControlPoint.ICLS_counter + 1
|
||||||
@@ -87,11 +91,20 @@ class ControlPoint:
|
|||||||
import theater.conflicttheater
|
import theater.conflicttheater
|
||||||
cp = cls(id, name, at, at, theater.conflicttheater.LAND, theater.conflicttheater.SIZE_SMALL, 1,
|
cp = cls(id, name, at, at, theater.conflicttheater.LAND, theater.conflicttheater.SIZE_SMALL, 1,
|
||||||
has_frontline=False, cptype=ControlPointType.LHA_GROUP)
|
has_frontline=False, cptype=ControlPointType.LHA_GROUP)
|
||||||
cp.tacanY = random.choice([True, False])
|
cp.tacanY = False
|
||||||
cp.tacanN = random.randint(1,25)
|
cp.tacanN = random.randint(1,25)
|
||||||
cp.tacanI = random.choice(["LHD", "LHA", "LHB", "LHC", "LHD", "LDS"])
|
cp.tacanI = random.choice(["LHD", "LHA", "LHB", "LHC", "LHD", "LDS"])
|
||||||
return cp
|
return cp
|
||||||
|
|
||||||
|
@property
|
||||||
|
def heading(self):
|
||||||
|
if self.cptype == ControlPointType.AIRBASE:
|
||||||
|
return self.airport.runways[0].heading
|
||||||
|
elif self.cptype in [ControlPointType.AIRCRAFT_CARRIER_GROUP, ControlPointType.LHA_GROUP]:
|
||||||
|
return 0 # TODO compute heading
|
||||||
|
else:
|
||||||
|
return 0
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
return self.name
|
return self.name
|
||||||
|
|
||||||
|
|||||||
@@ -30,10 +30,12 @@ ABBREV_NAME = {
|
|||||||
}
|
}
|
||||||
|
|
||||||
CATEGORY_MAP = {
|
CATEGORY_MAP = {
|
||||||
|
# Special cases
|
||||||
"CARRIER": ["CARRIER"],
|
"CARRIER": ["CARRIER"],
|
||||||
"LHA": ["LHA"],
|
"LHA": ["LHA"],
|
||||||
"aa": ["AA"],
|
"aa": ["AA"],
|
||||||
"power": ["Workshop A", "Electric power box", "Garage small A"],
|
# Buildings
|
||||||
|
"power": ["Workshop A", "Electric power box", "Garage small A", "Farm B", "Repair workshop", "Garage B"],
|
||||||
"warehouse": ["Warehouse", "Hangar A"],
|
"warehouse": ["Warehouse", "Hangar A"],
|
||||||
"fuel": ["Tank", "Tank 2", "Tank 3", "Fuel tank"],
|
"fuel": ["Tank", "Tank 2", "Tank 3", "Fuel tank"],
|
||||||
"ammo": [".Ammunition depot", "Hangar B"],
|
"ammo": [".Ammunition depot", "Hangar B"],
|
||||||
@@ -42,6 +44,7 @@ CATEGORY_MAP = {
|
|||||||
"factory": ["Tech combine", "Tech hangar A"],
|
"factory": ["Tech combine", "Tech hangar A"],
|
||||||
"comms": ["TV tower", "Comms tower M"],
|
"comms": ["TV tower", "Comms tower M"],
|
||||||
"oil": ["Oil platform"],
|
"oil": ["Oil platform"],
|
||||||
|
"derrick": ["Oil derrick", "Pump station", "Subsidiary structure 2"],
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -1,132 +0,0 @@
|
|||||||
"""
|
|
||||||
This utility classes provides methods to check players installed DCS environment.
|
|
||||||
|
|
||||||
TODO : add method 'is_using_open_beta', 'is_using_stable'
|
|
||||||
TODO : [NICE to have] add method to check list of installed DCS modules (could be done either through window registry, or through filesystem analysis)
|
|
||||||
"""
|
|
||||||
|
|
||||||
import winreg
|
|
||||||
import os
|
|
||||||
|
|
||||||
|
|
||||||
def is_using_dcs_steam_edition():
|
|
||||||
"""
|
|
||||||
Check if DCS World : Steam Edition version is installed on this computer
|
|
||||||
:return True if DCS Steam edition is installed,
|
|
||||||
-1 if DCS Steam Edition is registered in Steam apps but not installed,
|
|
||||||
False if never installed in Steam
|
|
||||||
"""
|
|
||||||
try:
|
|
||||||
# Note : Steam App ID for DCS World is 223750
|
|
||||||
dcs_steam_app_key = winreg.OpenKey(winreg.HKEY_CURRENT_USER, "Software\\Valve\\Steam\\Apps\\223750")
|
|
||||||
installed = winreg.QueryValueEx(dcs_steam_app_key, "Installed")
|
|
||||||
winreg.CloseKey(dcs_steam_app_key)
|
|
||||||
if installed[0] == 1:
|
|
||||||
return True
|
|
||||||
else:
|
|
||||||
return False
|
|
||||||
except FileNotFoundError as fnfe:
|
|
||||||
return False
|
|
||||||
|
|
||||||
|
|
||||||
def is_using_dcs_standalone_edition():
|
|
||||||
"""
|
|
||||||
Check if DCS World standalone edition is installed on this computer
|
|
||||||
:return True if Standalone is installed, False if it is not
|
|
||||||
"""
|
|
||||||
try:
|
|
||||||
dcs_path_key = winreg.OpenKey(winreg.HKEY_CURRENT_USER, "Software\\Eagle Dynamics\\DCS World")
|
|
||||||
winreg.CloseKey(dcs_path_key)
|
|
||||||
return True
|
|
||||||
except FileNotFoundError as fnfe:
|
|
||||||
return False
|
|
||||||
|
|
||||||
|
|
||||||
def _find_steam_directory():
|
|
||||||
"""
|
|
||||||
Get the Steam install directory for this computer from registry
|
|
||||||
:return Steam installation path
|
|
||||||
"""
|
|
||||||
try:
|
|
||||||
steam_key = winreg.OpenKey(winreg.HKEY_CURRENT_USER, "Software\\Valve\\Steam")
|
|
||||||
path = winreg.QueryValueEx(steam_key, "SteamPath")[0]
|
|
||||||
winreg.CloseKey(steam_key)
|
|
||||||
return path
|
|
||||||
except FileNotFoundError as fnfe:
|
|
||||||
print(fnfe)
|
|
||||||
return ""
|
|
||||||
|
|
||||||
|
|
||||||
def _get_steam_library_folders():
|
|
||||||
"""
|
|
||||||
Get the installation directory for Steam games
|
|
||||||
:return List of Steam library folders where games can be installed
|
|
||||||
"""
|
|
||||||
try:
|
|
||||||
steam_dir = _find_steam_directory()
|
|
||||||
"""
|
|
||||||
For reference here is what the vdf file is supposed to look like :
|
|
||||||
|
|
||||||
"LibraryFolders"
|
|
||||||
{
|
|
||||||
"TimeNextStatsReport" "1561832478"
|
|
||||||
"ContentStatsID" "-158337411110787451"
|
|
||||||
"1" "D:\\Games\\Steam"
|
|
||||||
"2" "E:\\Steam"
|
|
||||||
}
|
|
||||||
"""
|
|
||||||
vdf_file_location = steam_dir + os.path.sep + "steamapps" + os.path.sep + "libraryfolders.vdf"
|
|
||||||
with open(vdf_file_location) as adf_file:
|
|
||||||
paths = [l.split("\"")[3] for l in adf_file.readlines()[1:] if ':\\\\' in l]
|
|
||||||
return paths
|
|
||||||
except Exception as e:
|
|
||||||
print(e)
|
|
||||||
return []
|
|
||||||
|
|
||||||
|
|
||||||
def _find_steam_dcs_directory():
|
|
||||||
"""
|
|
||||||
Find the DCS install directory for DCS World Steam Edition
|
|
||||||
:return: Install directory as string, empty string if not found
|
|
||||||
"""
|
|
||||||
for library_folder in _get_steam_library_folders():
|
|
||||||
folder = library_folder + os.path.sep + "steamapps" + os.path.sep + "common" + os.path.sep + "DCSWorld"
|
|
||||||
if os.path.isdir(folder):
|
|
||||||
return folder + os.path.sep
|
|
||||||
return ""
|
|
||||||
|
|
||||||
|
|
||||||
def get_dcs_install_directory():
|
|
||||||
"""
|
|
||||||
Get the DCS World install directory for this computer
|
|
||||||
:return DCS World install directory
|
|
||||||
"""
|
|
||||||
if is_using_dcs_standalone_edition():
|
|
||||||
try:
|
|
||||||
dcs_path_key = winreg.OpenKey(winreg.HKEY_CURRENT_USER, "Software\\Eagle Dynamics\\DCS World")
|
|
||||||
path = winreg.QueryValueEx(dcs_path_key, "Path")
|
|
||||||
dcs_dir = path[0] + os.path.sep
|
|
||||||
winreg.CloseKey(dcs_path_key)
|
|
||||||
return dcs_dir
|
|
||||||
except Exception as e:
|
|
||||||
print("Couldn't detect DCS World installation folder")
|
|
||||||
return ""
|
|
||||||
elif is_using_dcs_steam_edition():
|
|
||||||
return _find_steam_dcs_directory()
|
|
||||||
else:
|
|
||||||
print("Couldn't detect any installed DCS World version")
|
|
||||||
|
|
||||||
|
|
||||||
def get_dcs_saved_games_directory():
|
|
||||||
"""
|
|
||||||
Get the save game directory for DCS World
|
|
||||||
:return: Save game directory as string
|
|
||||||
"""
|
|
||||||
return os.path.join(os.path.expanduser("~"), "Saved Games", "DCS")
|
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
|
||||||
print("Using STEAM Edition : " + str(is_using_dcs_steam_edition()))
|
|
||||||
print("Using Standalone Edition : " + str(is_using_dcs_standalone_edition()))
|
|
||||||
print("DCS Installation directory : " + get_dcs_install_directory())
|
|
||||||
print("DCS saved games directory : " + get_dcs_saved_games_directory())
|
|
||||||
@@ -47,6 +47,8 @@ class Debriefing:
|
|||||||
|
|
||||||
self.dead_aircraft = []
|
self.dead_aircraft = []
|
||||||
self.dead_units = []
|
self.dead_units = []
|
||||||
|
self.dead_aaa_groups = []
|
||||||
|
self.dead_buildings = []
|
||||||
|
|
||||||
for aircraft in self.killed_aircrafts:
|
for aircraft in self.killed_aircrafts:
|
||||||
try:
|
try:
|
||||||
@@ -70,10 +72,36 @@ class Debriefing:
|
|||||||
except Exception as e:
|
except Exception as e:
|
||||||
print(e)
|
print(e)
|
||||||
|
|
||||||
|
for unit in self.killed_ground_units:
|
||||||
|
for cp in game.theater.controlpoints:
|
||||||
|
|
||||||
|
print(cp.name)
|
||||||
|
print(cp.captured)
|
||||||
|
if cp.captured:
|
||||||
|
country = self.player_country_id
|
||||||
|
else:
|
||||||
|
country = self.enemy_country_id
|
||||||
|
player_unit = (country == self.player_country_id)
|
||||||
|
|
||||||
|
for i, ground_object in enumerate(cp.ground_objects):
|
||||||
|
print(unit)
|
||||||
|
print(ground_object.string_identifier)
|
||||||
|
if ground_object.matches_string_identifier(unit):
|
||||||
|
unit = DebriefingDeadUnitInfo(country, player_unit, ground_object.dcs_identifier)
|
||||||
|
self.dead_buildings.append(unit)
|
||||||
|
elif ground_object.dcs_identifier in ["AA", "CARRIER", "LHA"]:
|
||||||
|
for g in ground_object.groups:
|
||||||
|
for u in g.units:
|
||||||
|
if u.name == unit:
|
||||||
|
unit = DebriefingDeadUnitInfo(country, player_unit, db.unit_type_from_name(u.type))
|
||||||
|
self.dead_units.append(unit)
|
||||||
|
|
||||||
self.player_dead_aircraft = [a for a in self.dead_aircraft if a.country_id == self.player_country_id]
|
self.player_dead_aircraft = [a for a in self.dead_aircraft if a.country_id == self.player_country_id]
|
||||||
self.enemy_dead_aircraft = [a for a in self.dead_aircraft if a.country_id == self.enemy_country_id]
|
self.enemy_dead_aircraft = [a for a in self.dead_aircraft if a.country_id == self.enemy_country_id]
|
||||||
self.player_dead_units = [a for a in self.dead_units if a.country_id == self.player_country_id]
|
self.player_dead_units = [a for a in self.dead_units if a.country_id == self.player_country_id]
|
||||||
self.enemy_dead_units = [a for a in self.dead_units if a.country_id == self.enemy_country_id]
|
self.enemy_dead_units = [a for a in self.dead_units if a.country_id == self.enemy_country_id]
|
||||||
|
self.player_dead_buildings = [a for a in self.dead_buildings if a.country_id == self.player_country_id]
|
||||||
|
self.enemy_dead_buildings = [a for a in self.dead_buildings if a.country_id == self.enemy_country_id]
|
||||||
|
|
||||||
print(self.player_dead_aircraft)
|
print(self.player_dead_aircraft)
|
||||||
print(self.enemy_dead_aircraft)
|
print(self.enemy_dead_aircraft)
|
||||||
@@ -108,10 +136,27 @@ class Debriefing:
|
|||||||
else:
|
else:
|
||||||
self.enemy_dead_units_dict[a.type] = 1
|
self.enemy_dead_units_dict[a.type] = 1
|
||||||
|
|
||||||
|
self.player_dead_buildings_dict = {}
|
||||||
|
for a in self.player_dead_buildings:
|
||||||
|
if a.type in self.player_dead_buildings_dict.keys():
|
||||||
|
self.player_dead_buildings_dict[a.type] = self.player_dead_buildings_dict[a.type] + 1
|
||||||
|
else:
|
||||||
|
self.player_dead_buildings_dict[a.type] = 1
|
||||||
|
|
||||||
|
self.enemy_dead_buildings_dict = {}
|
||||||
|
for a in self.enemy_dead_buildings:
|
||||||
|
if a.type in self.enemy_dead_buildings_dict.keys():
|
||||||
|
self.enemy_dead_buildings_dict[a.type] = self.enemy_dead_buildings_dict[a.type] + 1
|
||||||
|
else:
|
||||||
|
self.enemy_dead_buildings_dict[a.type] = 1
|
||||||
|
|
||||||
|
print("DEBRIEFING PRE PROCESS")
|
||||||
print(self.player_dead_aircraft_dict)
|
print(self.player_dead_aircraft_dict)
|
||||||
print(self.enemy_dead_aircraft_dict)
|
print(self.enemy_dead_aircraft_dict)
|
||||||
print(self.player_dead_units_dict)
|
print(self.player_dead_units_dict)
|
||||||
print(self.enemy_dead_units_dict)
|
print(self.enemy_dead_units_dict)
|
||||||
|
print(self.player_dead_buildings_dict)
|
||||||
|
print(self.enemy_dead_buildings_dict)
|
||||||
|
|
||||||
|
|
||||||
def _poll_new_debriefing_log(callback: typing.Callable, game):
|
def _poll_new_debriefing_log(callback: typing.Callable, game):
|
||||||
|
|||||||
99
userdata/liberation_install.py
Normal file
99
userdata/liberation_install.py
Normal file
@@ -0,0 +1,99 @@
|
|||||||
|
import json
|
||||||
|
import os
|
||||||
|
from shutil import copyfile
|
||||||
|
|
||||||
|
import dcs
|
||||||
|
|
||||||
|
from userdata import persistency
|
||||||
|
|
||||||
|
global __dcs_saved_game_directory
|
||||||
|
global __dcs_installation_directory
|
||||||
|
|
||||||
|
PREFERENCES_FILE_PATH = "liberation_preferences.json"
|
||||||
|
|
||||||
|
def init():
|
||||||
|
global __dcs_saved_game_directory
|
||||||
|
global __dcs_installation_directory
|
||||||
|
|
||||||
|
if os.path.isfile(PREFERENCES_FILE_PATH):
|
||||||
|
try:
|
||||||
|
with(open(PREFERENCES_FILE_PATH)) as prefs:
|
||||||
|
pref_data = json.loads(prefs.read())
|
||||||
|
__dcs_saved_game_directory = pref_data["saved_game_dir"]
|
||||||
|
__dcs_installation_directory = pref_data["dcs_install_dir"]
|
||||||
|
is_first_start = False
|
||||||
|
except:
|
||||||
|
__dcs_saved_game_directory = ""
|
||||||
|
__dcs_installation_directory = ""
|
||||||
|
is_first_start = True
|
||||||
|
else:
|
||||||
|
try:
|
||||||
|
__dcs_saved_game_directory = dcs.installation.get_dcs_saved_games_directory()
|
||||||
|
if os.path.exists(__dcs_saved_game_directory + ".openbeta"):
|
||||||
|
__dcs_saved_game_directory = dcs.installation.get_dcs_saved_games_directory() + ".openbeta"
|
||||||
|
except:
|
||||||
|
__dcs_saved_game_directory = ""
|
||||||
|
try:
|
||||||
|
__dcs_installation_directory = dcs.installation.get_dcs_install_directory()
|
||||||
|
except:
|
||||||
|
__dcs_installation_directory = ""
|
||||||
|
|
||||||
|
is_first_start = True
|
||||||
|
persistency.setup(__dcs_saved_game_directory)
|
||||||
|
return is_first_start
|
||||||
|
|
||||||
|
|
||||||
|
def setup(saved_game_dir, install_dir):
|
||||||
|
global __dcs_saved_game_directory
|
||||||
|
global __dcs_installation_directory
|
||||||
|
__dcs_saved_game_directory = saved_game_dir
|
||||||
|
__dcs_installation_directory = install_dir
|
||||||
|
persistency.setup(__dcs_saved_game_directory)
|
||||||
|
|
||||||
|
|
||||||
|
def save_config():
|
||||||
|
global __dcs_saved_game_directory
|
||||||
|
global __dcs_installation_directory
|
||||||
|
pref_data = {"saved_game_dir": __dcs_saved_game_directory,
|
||||||
|
"dcs_install_dir": __dcs_installation_directory}
|
||||||
|
with(open(PREFERENCES_FILE_PATH, "w")) as prefs:
|
||||||
|
prefs.write(json.dumps(pref_data))
|
||||||
|
|
||||||
|
|
||||||
|
def get_dcs_install_directory():
|
||||||
|
global __dcs_installation_directory
|
||||||
|
return __dcs_installation_directory
|
||||||
|
|
||||||
|
|
||||||
|
def get_saved_game_dir():
|
||||||
|
global __dcs_saved_game_directory
|
||||||
|
return __dcs_saved_game_directory
|
||||||
|
|
||||||
|
|
||||||
|
def replace_mission_scripting_file():
|
||||||
|
install_dir = get_dcs_install_directory()
|
||||||
|
mission_scripting_path = os.path.join(install_dir, "Scripts", "MissionScripting.lua")
|
||||||
|
liberation_scripting_path = "./resources/scripts/MissionScripting.lua"
|
||||||
|
backup_scripting_path = "./resources/scripts/MissionScripting.original.lua"
|
||||||
|
if os.path.isfile(mission_scripting_path):
|
||||||
|
with open(mission_scripting_path, "r") as ms:
|
||||||
|
current_file_content = ms.read()
|
||||||
|
with open(liberation_scripting_path, "r") as libe_ms:
|
||||||
|
liberation_file_content = libe_ms.read()
|
||||||
|
|
||||||
|
# Save original file
|
||||||
|
if current_file_content != liberation_file_content:
|
||||||
|
copyfile(mission_scripting_path, backup_scripting_path)
|
||||||
|
|
||||||
|
# Replace DCS file
|
||||||
|
copyfile(liberation_scripting_path, mission_scripting_path)
|
||||||
|
|
||||||
|
|
||||||
|
def restore_original_mission_scripting():
|
||||||
|
install_dir = get_dcs_install_directory()
|
||||||
|
mission_scripting_path = os.path.join(install_dir, "Scripts", "MissionScripting.lua")
|
||||||
|
backup_scripting_path = "./resources/scripts/MissionScripting.original.lua"
|
||||||
|
|
||||||
|
if os.path.isfile(backup_scripting_path) and os.path.isfile(mission_scripting_path):
|
||||||
|
copyfile(backup_scripting_path, mission_scripting_path)
|
||||||
|
|
||||||
@@ -2,9 +2,6 @@ import logging
|
|||||||
import os
|
import os
|
||||||
import pickle
|
import pickle
|
||||||
import shutil
|
import shutil
|
||||||
import sys
|
|
||||||
|
|
||||||
from dcs import installation
|
|
||||||
|
|
||||||
_dcs_saved_game_folder = None # type: str
|
_dcs_saved_game_folder = None # type: str
|
||||||
|
|
||||||
@@ -17,12 +14,7 @@ def setup(user_folder: str):
|
|||||||
def base_path() -> str:
|
def base_path() -> str:
|
||||||
global _dcs_saved_game_folder
|
global _dcs_saved_game_folder
|
||||||
assert _dcs_saved_game_folder
|
assert _dcs_saved_game_folder
|
||||||
|
return _dcs_saved_game_folder
|
||||||
openbeta_path = _dcs_saved_game_folder + ".openbeta"
|
|
||||||
if os.path.exists(openbeta_path):
|
|
||||||
return openbeta_path # For standalone openbeta users
|
|
||||||
else:
|
|
||||||
return _dcs_saved_game_folder # For standalone stable users & steam users (any branch)
|
|
||||||
|
|
||||||
|
|
||||||
def _save_file() -> str:
|
def _save_file() -> str:
|
||||||
|
|||||||
Reference in New Issue
Block a user