-AAZQFh~P4l&vQv|=e3LF^yQ~)$Y
z)a?OVHIPe4eqZ
zbUSh{!3
z4~)TovHRjuTw+SL&Yb%U4=(T?(^)A#G$_1y%zM;S!XbuouXqh9_K7Z>^`2lGL|mDZ
zQ&P+cFDu05w&Hf3`yIz^Tz07Uf|dh78;UnOLqUm
z4_xMX{`LjA>=s$Fg6Qt|d&56&BGx>sflKCxiGO?cr9vhm{=Jq1N^jutc&v7}mJhgI
duZQ>j4gkk;MXM#IRGt6;002ovPDHLkV1h4r1)~4}
literal 0
HcmV?d00001
diff --git a/resources/ui/misc/light/notes.png b/resources/ui/misc/light/notes.png
new file mode 100644
index 0000000000000000000000000000000000000000..9f1300fc28f618acc6ef055af7654dc17f94ff72
GIT binary patch
literal 3536
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
literal 0
HcmV?d00001
diff --git a/resources/ui/misc/notes.png b/resources/ui/misc/notes.png
new file mode 100644
index 0000000000000000000000000000000000000000..9f1300fc28f618acc6ef055af7654dc17f94ff72
GIT binary patch
literal 3536
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
literal 0
HcmV?d00001
diff --git a/resources/ui/misc/original/notes.png b/resources/ui/misc/original/notes.png
new file mode 100644
index 0000000000000000000000000000000000000000..1223307a4646c32499d70fa92fce61e03c0a6fe9
GIT binary patch
literal 3435
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
Date: Sun, 27 Jun 2021 19:46:07 -0700
Subject: [PATCH 006/167] Update TGP Restriction Dates
TGP dates to more accurately reflect IRL IOC dates.
---
game/data/weapons.py | 12 ++++++------
1 file changed, 6 insertions(+), 6 deletions(-)
diff --git a/game/data/weapons.py b/game/data/weapons.py
index 22aa53b9..6b9164a7 100644
--- a/game/data/weapons.py
+++ b/game/data/weapons.py
@@ -888,16 +888,16 @@ WEAPON_INTRODUCTION_YEARS = {
Weapon.from_pydcs(Weapons.ALQ_184): 1989,
Weapon.from_pydcs(Weapons.AN_ALQ_164_DECM_Pod): 1984,
# TGP Pods
- Weapon.from_pydcs(Weapons.AN_AAQ_28_LITENING___Targeting_Pod): 1995,
- Weapon.from_pydcs(Weapons.AN_AAQ_28_LITENING___Targeting_Pod_): 1995,
- Weapon.from_pydcs(Weapons.AN_ASQ_228_ATFLIR___Targeting_Pod): 1993,
+ Weapon.from_pydcs(Weapons.AN_AAQ_28_LITENING___Targeting_Pod): 1999,
+ Weapon.from_pydcs(Weapons.AN_AAQ_28_LITENING___Targeting_Pod_): 1999,
+ Weapon.from_pydcs(Weapons.AN_ASQ_228_ATFLIR___Targeting_Pod): 2003,
Weapon.from_pydcs(
Weapons.AN_ASQ_173_Laser_Spot_Tracker_Strike_CAMera__LST_SCAM_
): 1993,
Weapon.from_pydcs(Weapons.AWW_13_DATALINK_POD): 1967,
- Weapon.from_pydcs(Weapons.LANTIRN_Targeting_Pod): 1985,
- Weapon.from_pydcs(Weapons.Lantirn_F_16): 1985,
- Weapon.from_pydcs(Weapons.Lantirn_Target_Pod): 1985,
+ Weapon.from_pydcs(Weapons.LANTIRN_Targeting_Pod): 1990,
+ Weapon.from_pydcs(Weapons.Lantirn_F_16): 1990,
+ Weapon.from_pydcs(Weapons.Lantirn_Target_Pod): 1990,
Weapon.from_pydcs(Weapons.Pavetack_F_111): 1982,
# BLU-107
Weapon.from_pydcs(Weapons.BLU_107___440lb_Anti_Runway_Penetrator_Bomb): 1983,
From 7ba4077f9f83822468d1b3c191b470634a71dbd6 Mon Sep 17 00:00:00 2001
From: bgreman <47828384+bgreman@users.noreply.github.com>
Date: Wed, 30 Jun 2021 23:50:02 -0400
Subject: [PATCH 007/167] Fixes #240 by making statistics windows axis labels
integers (#1370)
---
changelog.md | 1 +
qt_ui/windows/stats/QAircraftChart.py | 6 ++++++
qt_ui/windows/stats/QArmorChart.py | 6 ++++++
qt_ui/windows/stats/QStatsWindow.py | 2 +-
4 files changed, 14 insertions(+), 1 deletion(-)
diff --git a/changelog.md b/changelog.md
index fd71f412..53c138c3 100644
--- a/changelog.md
+++ b/changelog.md
@@ -17,6 +17,7 @@ Saves from 4.0.0 are compatible with 4.0.1.
* **[UI]** Google search link added to unit information when there is no information provided.
## Fixes
+* **[UI]** Statistics window tick marks are now always integers.
# 4.0.0
diff --git a/qt_ui/windows/stats/QAircraftChart.py b/qt_ui/windows/stats/QAircraftChart.py
index 6c8d1db9..6516ec58 100644
--- a/qt_ui/windows/stats/QAircraftChart.py
+++ b/qt_ui/windows/stats/QAircraftChart.py
@@ -42,10 +42,16 @@ class QAircraftChart(QFrame):
self.chart.setTitle("Aircraft forces over time")
self.chart.createDefaultAxes()
+ self.chart.axisX().setTitleText("Turn")
+ self.chart.axisX().setLabelFormat("%i")
self.chart.axisX().setRange(0, len(self.alliedAircraft))
+ self.chart.axisX().applyNiceNumbers()
+
+ self.chart.axisY().setLabelFormat("%i")
self.chart.axisY().setRange(
0, max(max(self.alliedAircraft), max(self.enemyAircraft)) + 10
)
+ self.chart.axisY().applyNiceNumbers()
self.chartView = QtCharts.QChartView(self.chart)
self.chartView.setRenderHint(QPainter.Antialiasing)
diff --git a/qt_ui/windows/stats/QArmorChart.py b/qt_ui/windows/stats/QArmorChart.py
index 09c272fa..e952c717 100644
--- a/qt_ui/windows/stats/QArmorChart.py
+++ b/qt_ui/windows/stats/QArmorChart.py
@@ -42,10 +42,16 @@ class QArmorChart(QFrame):
self.chart.setTitle("Combat vehicles over time")
self.chart.createDefaultAxes()
+ self.chart.axisX().setTitleText("Turn")
+ self.chart.axisX().setLabelFormat("%i")
self.chart.axisX().setRange(0, len(self.alliedArmor))
+ self.chart.axisX().applyNiceNumbers()
+
+ self.chart.axisY().setLabelFormat("%i")
self.chart.axisY().setRange(
0, max(max(self.alliedArmor), max(self.enemyArmor)) + 10
)
+ self.chart.axisY().applyNiceNumbers()
self.chartView = QtCharts.QChartView(self.chart)
self.chartView.setRenderHint(QPainter.Antialiasing)
diff --git a/qt_ui/windows/stats/QStatsWindow.py b/qt_ui/windows/stats/QStatsWindow.py
index 7d4fda07..14817d18 100644
--- a/qt_ui/windows/stats/QStatsWindow.py
+++ b/qt_ui/windows/stats/QStatsWindow.py
@@ -14,7 +14,7 @@ class QStatsWindow(QDialog):
self.setModal(True)
self.setWindowTitle("Stats")
self.setWindowIcon(CONST.ICONS["Statistics"])
- self.setMinimumSize(600, 250)
+ self.setMinimumSize(600, 300)
self.layout = QGridLayout()
self.aircraft_charts = QAircraftChart(self.game)
From c8e5cefd3675040d0cec83b23e1306a56fca2e65 Mon Sep 17 00:00:00 2001
From: bgreman <47828384+bgreman@users.noreply.github.com>
Date: Wed, 30 Jun 2021 23:55:37 -0400
Subject: [PATCH 008/167] Increasing time JTAC radio messages stay on the UI.
(#1369)
- Target lost or killed: 10s -> 20s
- New target : 10s -> 30s
- Request JTAC Status: 25s -> 60s
---
changelog.md | 1 +
resources/plugins/jtacautolase/JTACAutoLase.lua | 8 ++++----
2 files changed, 5 insertions(+), 4 deletions(-)
diff --git a/changelog.md b/changelog.md
index 53c138c3..b75fab07 100644
--- a/changelog.md
+++ b/changelog.md
@@ -11,6 +11,7 @@ Saves from 3.x are not compatible with 5.0.
Saves from 4.0.0 are compatible with 4.0.1.
## Features/Improvements
+* **[Plugins]** Increased time JTAC Autolase messages stay visible on the UI.
* **[UI]** Added ability to take notes and have those notes appear as a kneeboard page.
* **[UI]** Hovering over the weather information now dispalys the cloud base (meters and feet).
diff --git a/resources/plugins/jtacautolase/JTACAutoLase.lua b/resources/plugins/jtacautolase/JTACAutoLase.lua
index 05315574..01cd8649 100644
--- a/resources/plugins/jtacautolase/JTACAutoLase.lua
+++ b/resources/plugins/jtacautolase/JTACAutoLase.lua
@@ -161,9 +161,9 @@ function JTACAutoLase(jtacGroupName, laserCode,smoke,lock,colour)
local tempUnit = Unit.getByName(tempUnitInfo.name)
if tempUnit ~= nil and tempUnit:getLife() > 0 and tempUnit:isActive() == true then
- notify(jtacGroupName .. " target " .. tempUnitInfo.unitType .. " lost. Scanning for Targets. ", 10)
+ notify(jtacGroupName .. " target " .. tempUnitInfo.unitType .. " lost. Scanning for Targets. ", 20)
else
- notify(jtacGroupName .. " target " .. tempUnitInfo.unitType .. " KIA. Good Job! Scanning for Targets. ", 10)
+ notify(jtacGroupName .. " target " .. tempUnitInfo.unitType .. " KIA. Good Job! Scanning for Targets. ", 20)
end
--remove from smoke list
@@ -186,7 +186,7 @@ function JTACAutoLase(jtacGroupName, laserCode,smoke,lock,colour)
-- store current target for easy lookup
GLOBAL_JTAC_CURRENT_TARGETS[jtacGroupName] = { name = enemyUnit:getName(), unitType = enemyUnit:getTypeName(), unitId = enemyUnit:getID() }
- notify(jtacGroupName .. " lasing new target " .. enemyUnit:getTypeName() .. '. CODE: ' .. laserCode ..getPositionString(enemyUnit) , 10)
+ notify(jtacGroupName .. " lasing new target " .. enemyUnit:getTypeName() .. '. CODE: ' .. laserCode ..getPositionString(enemyUnit) , 30)
-- create smoke
if smoke == true then
@@ -554,7 +554,7 @@ function getJTACStatus()
end
end
- notify(message, 25)
+ notify(message, 60)
end
From 9bd6f9ef47475e936d2abe052bb56fd30d0d2fbb Mon Sep 17 00:00:00 2001
From: bgreman <47828384+bgreman@users.noreply.github.com>
Date: Wed, 30 Jun 2021 23:58:20 -0400
Subject: [PATCH 009/167] Addresses #478 to clean up the angle summing
functionality. (#1386)
---
changelog.md | 2 ++
game/utils.py | 7 +------
2 files changed, 3 insertions(+), 6 deletions(-)
diff --git a/changelog.md b/changelog.md
index b75fab07..09058d82 100644
--- a/changelog.md
+++ b/changelog.md
@@ -20,6 +20,8 @@ Saves from 4.0.0 are compatible with 4.0.1.
## Fixes
* **[UI]** Statistics window tick marks are now always integers.
+* **[Flight Planning]** Fixed potential issue with angles > 360° or < 0° being generated when summing two angles.
+
# 4.0.0
Saves from 3.x are not compatible with 4.0.
diff --git a/game/utils.py b/game/utils.py
index 0bd1f79c..68e38d31 100644
--- a/game/utils.py
+++ b/game/utils.py
@@ -18,12 +18,7 @@ KPH_TO_MS = 1 / MS_TO_KPH
def heading_sum(h, a) -> int:
h += a
- if h > 360:
- return h - 360
- elif h < 0:
- return 360 + h
- else:
- return h
+ return h % 360
def opposite_heading(h):
From 568655d503ce5d017f9be3d1d239b6d7d487de99 Mon Sep 17 00:00:00 2001
From: Schneefl0cke <60181177+Schneefl0cke@users.noreply.github.com>
Date: Fri, 2 Jul 2021 02:04:03 +0200
Subject: [PATCH 010/167] Add incomes for WW2 villages and camps.
---
game/db.py | 2 ++
game/income.py | 4 ++--
2 files changed, 4 insertions(+), 2 deletions(-)
diff --git a/game/db.py b/game/db.py
index 61426d12..0a63c056 100644
--- a/game/db.py
+++ b/game/db.py
@@ -317,6 +317,8 @@ REWARDS = {
"comms": 10,
"oil": 10,
"derrick": 8,
+ "village": 0.25,
+ "allycamp": 0.5,
}
"""
diff --git a/game/income.py b/game/income.py
index dd2be887..f9a74eb6 100644
--- a/game/income.py
+++ b/game/income.py
@@ -14,10 +14,10 @@ class BuildingIncome:
name: str
category: str
number: int
- income_per_building: int
+ income_per_building: float
@property
- def income(self) -> int:
+ def income(self) -> float:
return self.number * self.income_per_building
From 2ef2eafdd3d90b330825e206b4638c50c1b29b2c Mon Sep 17 00:00:00 2001
From: Dan Albert
Date: Thu, 1 Jul 2021 19:37:08 -0700
Subject: [PATCH 011/167] Remove debug cruft.
We don't need to print the description of every unit on startup.
---
game/dcs/groundunittype.py | 1 -
1 file changed, 1 deletion(-)
diff --git a/game/dcs/groundunittype.py b/game/dcs/groundunittype.py
index c2b6f2ab..908e0e18 100644
--- a/game/dcs/groundunittype.py
+++ b/game/dcs/groundunittype.py
@@ -57,7 +57,6 @@ class GroundUnitType(UnitType[VehicleType]):
def _load_all(cls) -> None:
for unit_type in cls._each_unit_type():
for data in cls._each_variant_of(unit_type):
- print(data.name, data.description)
cls.register(data)
cls._loaded = True
From b2dd8c68e1aa7cb9e009777cc6e6a06272496dca Mon Sep 17 00:00:00 2001
From: Dan Albert
Date: Thu, 1 Jul 2021 20:00:08 -0700
Subject: [PATCH 012/167] Revert "Add Cloud Base Altitude to Weather Display
(#1371)"
Reverting until
https://github.com/dcs-liberation/dcs_liberation/issues/1394 is
resovled.
This reverts commit f80696b7244d2eace20111dacdcb149cb4747055.
---
changelog.md | 2 --
qt_ui/widgets/QTopPanel.py | 6 ------
2 files changed, 8 deletions(-)
diff --git a/changelog.md b/changelog.md
index 09058d82..69f5dd8d 100644
--- a/changelog.md
+++ b/changelog.md
@@ -12,9 +12,7 @@ Saves from 4.0.0 are compatible with 4.0.1.
## Features/Improvements
* **[Plugins]** Increased time JTAC Autolase messages stay visible on the UI.
-
* **[UI]** Added ability to take notes and have those notes appear as a kneeboard page.
-* **[UI]** Hovering over the weather information now dispalys the cloud base (meters and feet).
* **[UI]** Google search link added to unit information when there is no information provided.
## Fixes
diff --git a/qt_ui/widgets/QTopPanel.py b/qt_ui/widgets/QTopPanel.py
index 83959b12..a00b6044 100644
--- a/qt_ui/widgets/QTopPanel.py
+++ b/qt_ui/widgets/QTopPanel.py
@@ -13,7 +13,6 @@ import qt_ui.uiconstants as CONST
from game import Game
from game.event.airwar import AirWarEvent
from game.profiling import logged_duration
-from game.utils import meters
from gen.ato import Package
from gen.flights.traveltime import TotEstimator
from qt_ui.models import GameModel
@@ -113,11 +112,6 @@ class QTopPanel(QFrame):
self.transfers.setEnabled(True)
self.conditionsWidget.setCurrentTurn(game.turn, game.conditions)
-
- base_m = game.conditions.weather.clouds.base
- base_ft = int(meters(base_m).feet)
- self.conditionsWidget.setToolTip(f"Cloud Base: {base_m}m / {base_ft}ft")
-
self.intel_box.set_game(game)
self.budgetBox.setGame(game)
self.factionsInfos.setGame(game)
From c89416702d7f9153212e4af45f79d3c735c09805 Mon Sep 17 00:00:00 2001
From: Dan Albert
Date: Thu, 1 Jul 2021 20:02:29 -0700
Subject: [PATCH 013/167] Revert "Revert "Add Cloud Base Altitude to Weather
Display (#1371)""
This reverts commit b2dd8c68e1aa7cb9e009777cc6e6a06272496dca.
---
changelog.md | 2 ++
qt_ui/widgets/QTopPanel.py | 6 ++++++
2 files changed, 8 insertions(+)
diff --git a/changelog.md b/changelog.md
index 69f5dd8d..09058d82 100644
--- a/changelog.md
+++ b/changelog.md
@@ -12,7 +12,9 @@ Saves from 4.0.0 are compatible with 4.0.1.
## Features/Improvements
* **[Plugins]** Increased time JTAC Autolase messages stay visible on the UI.
+
* **[UI]** Added ability to take notes and have those notes appear as a kneeboard page.
+* **[UI]** Hovering over the weather information now dispalys the cloud base (meters and feet).
* **[UI]** Google search link added to unit information when there is no information provided.
## Fixes
diff --git a/qt_ui/widgets/QTopPanel.py b/qt_ui/widgets/QTopPanel.py
index a00b6044..83959b12 100644
--- a/qt_ui/widgets/QTopPanel.py
+++ b/qt_ui/widgets/QTopPanel.py
@@ -13,6 +13,7 @@ import qt_ui.uiconstants as CONST
from game import Game
from game.event.airwar import AirWarEvent
from game.profiling import logged_duration
+from game.utils import meters
from gen.ato import Package
from gen.flights.traveltime import TotEstimator
from qt_ui.models import GameModel
@@ -112,6 +113,11 @@ class QTopPanel(QFrame):
self.transfers.setEnabled(True)
self.conditionsWidget.setCurrentTurn(game.turn, game.conditions)
+
+ base_m = game.conditions.weather.clouds.base
+ base_ft = int(meters(base_m).feet)
+ self.conditionsWidget.setToolTip(f"Cloud Base: {base_m}m / {base_ft}ft")
+
self.intel_box.set_game(game)
self.budgetBox.setGame(game)
self.factionsInfos.setGame(game)
From bc2539b566a9849d3c3997729128087f3f21e557 Mon Sep 17 00:00:00 2001
From: Chris Seagraves
Date: Thu, 1 Jul 2021 20:04:14 -0700
Subject: [PATCH 014/167] Fix for crash when clear weather.
Fixes https://github.com/dcs-liberation/dcs_liberation/issues/1394
---
qt_ui/widgets/QTopPanel.py | 9 ++++++---
1 file changed, 6 insertions(+), 3 deletions(-)
diff --git a/qt_ui/widgets/QTopPanel.py b/qt_ui/widgets/QTopPanel.py
index 83959b12..5f295a6a 100644
--- a/qt_ui/widgets/QTopPanel.py
+++ b/qt_ui/widgets/QTopPanel.py
@@ -114,9 +114,12 @@ class QTopPanel(QFrame):
self.conditionsWidget.setCurrentTurn(game.turn, game.conditions)
- base_m = game.conditions.weather.clouds.base
- base_ft = int(meters(base_m).feet)
- self.conditionsWidget.setToolTip(f"Cloud Base: {base_m}m / {base_ft}ft")
+ if game.conditions.weather.clouds:
+ base_m = game.conditions.weather.clouds.base
+ base_ft = int(meters(base_m).feet)
+ self.conditionsWidget.setToolTip(f"Cloud Base: {base_m}m / {base_ft}ft")
+ else:
+ self.conditionsWidget.setToolTip("")
self.intel_box.set_game(game)
self.budgetBox.setGame(game)
From 20839853b7bfac7c3907fc62ba821f05bd8efda9 Mon Sep 17 00:00:00 2001
From: Dan Albert
Date: Thu, 1 Jul 2021 20:07:28 -0700
Subject: [PATCH 015/167] Minor formatting fix for the changelog.
---
changelog.md | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/changelog.md b/changelog.md
index 09058d82..abfac327 100644
--- a/changelog.md
+++ b/changelog.md
@@ -11,8 +11,8 @@ Saves from 3.x are not compatible with 5.0.
Saves from 4.0.0 are compatible with 4.0.1.
## Features/Improvements
-* **[Plugins]** Increased time JTAC Autolase messages stay visible on the UI.
+* **[Plugins]** Increased time JTAC Autolase messages stay visible on the UI.
* **[UI]** Added ability to take notes and have those notes appear as a kneeboard page.
* **[UI]** Hovering over the weather information now dispalys the cloud base (meters and feet).
* **[UI]** Google search link added to unit information when there is no information provided.
From de443fa3f0ac7ab2d450ec097cf7ac94f17f3506 Mon Sep 17 00:00:00 2001
From: RndName
Date: Thu, 1 Jul 2021 16:00:04 +0200
Subject: [PATCH 016/167] reworked the skynet group name generation
- added information about the role of the aa site
- moved handling of ground name from tgo to the sam generator to make the tgo cleaner
- adjusted the skynet-config lua to the changes
---
changelog.md | 3 +-
game/game.py | 2 +-
game/theater/theatergroundobject.py | 18 +-----
gen/sam/airdefensegroupgenerator.py | 58 ++++++++++++++++---
gen/sam/sam_hawk.py | 3 +-
gen/sam/sam_hq7.py | 3 +-
gen/sam/sam_patriot.py | 3 +-
gen/sam/sam_rapier.py | 5 ++
gen/sam/sam_roland.py | 5 ++
gen/sam/sam_sa10.py | 9 +--
.../plugins/skynetiads/skynetiads-config.lua | 23 +++++++-
11 files changed, 97 insertions(+), 35 deletions(-)
diff --git a/changelog.md b/changelog.md
index abfac327..bd9a6626 100644
--- a/changelog.md
+++ b/changelog.md
@@ -13,13 +13,14 @@ Saves from 4.0.0 are compatible with 4.0.1.
## Features/Improvements
* **[Plugins]** Increased time JTAC Autolase messages stay visible on the UI.
+* **[Mission Generation]** Improvements for better support of the Skynet Plugin and long range SAMs are now acting as EWR
* **[UI]** Added ability to take notes and have those notes appear as a kneeboard page.
* **[UI]** Hovering over the weather information now dispalys the cloud base (meters and feet).
* **[UI]** Google search link added to unit information when there is no information provided.
## Fixes
* **[UI]** Statistics window tick marks are now always integers.
-
+* **[Mission Generation]** The lua data for other plugins is now generated correctly
* **[Flight Planning]** Fixed potential issue with angles > 360° or < 0° being generated when summing two angles.
# 4.0.0
diff --git a/game/game.py b/game/game.py
index 3a783e40..6d2aa329 100644
--- a/game/game.py
+++ b/game/game.py
@@ -483,7 +483,7 @@ class Game:
self.current_unit_id += 1
return self.current_unit_id
- def next_group_id(self):
+ def next_group_id(self) -> int:
"""
Next unit id for pre-generated units
"""
diff --git a/game/theater/theatergroundobject.py b/game/theater/theatergroundobject.py
index df637cbc..49fb8fd9 100644
--- a/game/theater/theatergroundobject.py
+++ b/game/theater/theatergroundobject.py
@@ -460,9 +460,9 @@ class CoastalSiteGroundObject(TheaterGroundObject):
return False
-# TODO: Differentiate types.
-# This type gets used both for AA sites (SAM, AAA, or SHORAD). These should each
-# be split into their own types.
+# The SamGroundObject represents all type of AA
+# The TGO can have multiple types of units (AAA,SAM,Support...)
+# Differentiation can be made during generation with the airdefensegroupgenerator
class SamGroundObject(TheaterGroundObject):
def __init__(
self,
@@ -481,18 +481,6 @@ class SamGroundObject(TheaterGroundObject):
dcs_identifier="AA",
sea_object=False,
)
- # Set by the SAM unit generator if the generated group is compatible
- # with Skynet.
- self.skynet_capable = False
-
- @property
- def group_name(self) -> str:
- if self.skynet_capable:
- # Prefix the group names of SAM sites with the side color so Skynet
- # can find them.
- return f"{self.faction_color}|SAM|{self.group_id}"
- else:
- return super().group_name
def mission_types(self, for_player: bool) -> Iterator[FlightType]:
from gen.flights.flight import FlightType
diff --git a/gen/sam/airdefensegroupgenerator.py b/gen/sam/airdefensegroupgenerator.py
index a62a5f11..7d269ece 100644
--- a/gen/sam/airdefensegroupgenerator.py
+++ b/gen/sam/airdefensegroupgenerator.py
@@ -1,3 +1,5 @@
+from __future__ import annotations
+
from abc import ABC, abstractmethod
from enum import Enum
from typing import Iterator, List
@@ -9,11 +11,31 @@ from game.theater.theatergroundobject import SamGroundObject
from gen.sam.group_generator import GroupGenerator
+class SkynetRole(Enum):
+ #: A radar SAM that should be controlled by Skynet.
+ Sam = "Sam"
+
+ #: A radar SAM that should be controlled and used as an EWR by Skynet.
+ SamAsEwr = "SamAsEwr"
+
+ #: An air defense unit that should be used as point defense by Skynet.
+ PointDefense = "PD"
+
+ #: All other types of groups that might be present in a SAM TGO. This includes
+ #: SHORADS, AAA, supply trucks, etc. Anything that shouldn't be controlled by Skynet
+ #: should use this role.
+ NoSkynetBehavior = "NoSkynetBehavior"
+
+
class AirDefenseRange(Enum):
- AAA = "AAA"
- Short = "short"
- Medium = "medium"
- Long = "long"
+ AAA = ("AAA", SkynetRole.NoSkynetBehavior)
+ Short = ("short", SkynetRole.NoSkynetBehavior)
+ Medium = ("medium", SkynetRole.Sam)
+ Long = ("long", SkynetRole.SamAsEwr)
+
+ def __init__(self, description: str, default_role: SkynetRole) -> None:
+ self.range_name = description
+ self.default_role = default_role
class AirDefenseGroupGenerator(GroupGenerator, ABC):
@@ -24,18 +46,32 @@ class AirDefenseGroupGenerator(GroupGenerator, ABC):
price: int
def __init__(self, game: Game, ground_object: SamGroundObject) -> None:
- ground_object.skynet_capable = True
super().__init__(game, ground_object)
+ self.vg.name = self.group_name_for_role(self.vg.id, self.primary_group_role())
self.auxiliary_groups: List[VehicleGroup] = []
- def add_auxiliary_group(self, name_suffix: str) -> VehicleGroup:
- group = VehicleGroup(
- self.game.next_group_id(), "|".join([self.go.group_name, name_suffix])
- )
+ def add_auxiliary_group(self, role: SkynetRole) -> VehicleGroup:
+ gid = self.game.next_group_id()
+ group = VehicleGroup(gid, self.group_name_for_role(gid, role))
self.auxiliary_groups.append(group)
return group
+ def group_name_for_role(self, gid: int, role: SkynetRole) -> str:
+ if role is SkynetRole.NoSkynetBehavior:
+ # No special naming needed for air defense groups that don't participate in
+ # Skynet.
+ return f"{self.go.group_name}|{gid}"
+
+ # For those that do, we need a prefix of `$COLOR|SAM| so our Skynet config picks
+ # the group up at all. To support PDs we need to append the ID of the TGO so
+ # that the PD will know which group it's protecting. We then append the role so
+ # our config knows what to do with the group, and finally the GID of *this*
+ # group to ensure no conflicts.
+ return "|".join(
+ [self.go.faction_color, "SAM", str(self.go.group_id), role.value, str(gid)]
+ )
+
def get_generated_group(self) -> VehicleGroup:
raise RuntimeError(
"Deprecated call to AirDefenseGroupGenerator.get_generated_group "
@@ -52,3 +88,7 @@ class AirDefenseGroupGenerator(GroupGenerator, ABC):
@abstractmethod
def range(cls) -> AirDefenseRange:
...
+
+ @classmethod
+ def primary_group_role(cls) -> SkynetRole:
+ return cls.range().default_role
diff --git a/gen/sam/sam_hawk.py b/gen/sam/sam_hawk.py
index 01e463e1..ea05f726 100644
--- a/gen/sam/sam_hawk.py
+++ b/gen/sam/sam_hawk.py
@@ -6,6 +6,7 @@ from dcs.vehicles import AirDefence
from gen.sam.airdefensegroupgenerator import (
AirDefenseRange,
AirDefenseGroupGenerator,
+ SkynetRole,
)
@@ -41,7 +42,7 @@ class HawkGenerator(AirDefenseGroupGenerator):
)
# Triple A for close range defense
- aa_group = self.add_auxiliary_group("AA")
+ aa_group = self.add_auxiliary_group(SkynetRole.NoSkynetBehavior)
self.add_unit_to_group(
aa_group,
AirDefence.Vulcan,
diff --git a/gen/sam/sam_hq7.py b/gen/sam/sam_hq7.py
index d05aecd8..be5eeb6a 100644
--- a/gen/sam/sam_hq7.py
+++ b/gen/sam/sam_hq7.py
@@ -6,6 +6,7 @@ from dcs.vehicles import AirDefence
from gen.sam.airdefensegroupgenerator import (
AirDefenseRange,
AirDefenseGroupGenerator,
+ SkynetRole,
)
@@ -34,7 +35,7 @@ class HQ7Generator(AirDefenseGroupGenerator):
)
# Triple A for close range defense
- aa_group = self.add_auxiliary_group("AA")
+ aa_group = self.add_auxiliary_group(SkynetRole.NoSkynetBehavior)
self.add_unit_to_group(
aa_group,
AirDefence.Ural_375_ZU_23,
diff --git a/gen/sam/sam_patriot.py b/gen/sam/sam_patriot.py
index 21f6cd18..aafeb79c 100644
--- a/gen/sam/sam_patriot.py
+++ b/gen/sam/sam_patriot.py
@@ -6,6 +6,7 @@ from dcs.vehicles import AirDefence
from gen.sam.airdefensegroupgenerator import (
AirDefenseRange,
AirDefenseGroupGenerator,
+ SkynetRole,
)
@@ -69,7 +70,7 @@ class PatriotGenerator(AirDefenseGroupGenerator):
)
# Short range protection for high value site
- aa_group = self.add_auxiliary_group("AA")
+ aa_group = self.add_auxiliary_group(SkynetRole.NoSkynetBehavior)
num_launchers = random.randint(3, 4)
positions = self.get_circular_position(
num_launchers, launcher_distance=200, coverage=360
diff --git a/gen/sam/sam_rapier.py b/gen/sam/sam_rapier.py
index 0e361459..af3965e4 100644
--- a/gen/sam/sam_rapier.py
+++ b/gen/sam/sam_rapier.py
@@ -5,6 +5,7 @@ from dcs.vehicles import AirDefence
from gen.sam.airdefensegroupgenerator import (
AirDefenseRange,
AirDefenseGroupGenerator,
+ SkynetRole,
)
@@ -49,3 +50,7 @@ class RapierGenerator(AirDefenseGroupGenerator):
@classmethod
def range(cls) -> AirDefenseRange:
return AirDefenseRange.Short
+
+ @classmethod
+ def primary_group_role(cls) -> SkynetRole:
+ return SkynetRole.Sam
diff --git a/gen/sam/sam_roland.py b/gen/sam/sam_roland.py
index 4a88cfd4..e2e704af 100644
--- a/gen/sam/sam_roland.py
+++ b/gen/sam/sam_roland.py
@@ -3,6 +3,7 @@ from dcs.vehicles import AirDefence, Unarmed
from gen.sam.airdefensegroupgenerator import (
AirDefenseRange,
AirDefenseGroupGenerator,
+ SkynetRole,
)
@@ -40,3 +41,7 @@ class RolandGenerator(AirDefenseGroupGenerator):
@classmethod
def range(cls) -> AirDefenseRange:
return AirDefenseRange.Short
+
+ @classmethod
+ def primary_group_role(cls) -> SkynetRole:
+ return SkynetRole.Sam
diff --git a/gen/sam/sam_sa10.py b/gen/sam/sam_sa10.py
index 6daf8bfb..35611e83 100644
--- a/gen/sam/sam_sa10.py
+++ b/gen/sam/sam_sa10.py
@@ -8,6 +8,7 @@ from game.theater import SamGroundObject
from gen.sam.airdefensegroupgenerator import (
AirDefenseRange,
AirDefenseGroupGenerator,
+ SkynetRole,
)
from pydcs_extensions.highdigitsams import highdigitsams
@@ -76,7 +77,7 @@ class SA10Generator(AirDefenseGroupGenerator):
def generate_defensive_groups(self) -> None:
# AAA for defending against close targets.
- aa_group = self.add_auxiliary_group("AA")
+ aa_group = self.add_auxiliary_group(SkynetRole.NoSkynetBehavior)
num_launchers = random.randint(6, 8)
positions = self.get_circular_position(
num_launchers, launcher_distance=210, coverage=360
@@ -101,7 +102,7 @@ class Tier2SA10Generator(SA10Generator):
super().generate_defensive_groups()
# SA-15 for both shorter range targets and point defense.
- pd_group = self.add_auxiliary_group("PD")
+ pd_group = self.add_auxiliary_group(SkynetRole.PointDefense)
num_launchers = random.randint(2, 4)
positions = self.get_circular_position(
num_launchers, launcher_distance=140, coverage=360
@@ -123,7 +124,7 @@ class Tier3SA10Generator(SA10Generator):
def generate_defensive_groups(self) -> None:
# AAA for defending against close targets.
- aa_group = self.add_auxiliary_group("AA")
+ aa_group = self.add_auxiliary_group(SkynetRole.NoSkynetBehavior)
num_launchers = random.randint(6, 8)
positions = self.get_circular_position(
num_launchers, launcher_distance=210, coverage=360
@@ -138,7 +139,7 @@ class Tier3SA10Generator(SA10Generator):
)
# SA-15 for both shorter range targets and point defense.
- pd_group = self.add_auxiliary_group("PD")
+ pd_group = self.add_auxiliary_group(SkynetRole.PointDefense)
num_launchers = random.randint(2, 4)
positions = self.get_circular_position(
num_launchers, launcher_distance=140, coverage=360
diff --git a/resources/plugins/skynetiads/skynetiads-config.lua b/resources/plugins/skynetiads/skynetiads-config.lua
index aa0ce992..f083c6f9 100644
--- a/resources/plugins/skynetiads/skynetiads-config.lua
+++ b/resources/plugins/skynetiads/skynetiads-config.lua
@@ -93,9 +93,28 @@ if dcsLiberation and SkynetIADS then
for i = 1, #sites do
local site = sites[i]
local name = site:getDCSName()
+
+ if string.match(name, "|SamAsEwr|") then
+ env.info(string.format("DCSLiberation|Skynet-IADS plugin - %s now acting as EWR", name))
+ site:setActAsEW(true)
+ end
+
if not string.match(name, "|PD") then
- env.info(string.format("DCSLiberation|Skynet-IADS plugin - Checking %s for PD", name))
- local pds = iads:getSAMSitesByPrefix(name .. "|PD")
+ -- Name is prefixed with `$color|SAM|$tgoid`. For pre-4.1 generated
+ -- campaigns that's the full name of the primary SAM and any PD are just
+ -- that name suffixed with |PD.
+ --
+ -- For 4.1+ generated campaigns the name will be
+ -- `$color|SAM|$tgoid|$role|$gid`, so we need to replace the content
+ -- beginning with the third pipe with `|PD` to find our PDs.
+ local first_pipe = string.find(name, "|")
+ local second_pipe = string.find(name, "|", first_pipe + 1)
+ local third_pipe = string.find(name, "|", second_pipe + 1)
+ local pd_prefix = name .. "|PD"
+ if third_pipe ~= nil then
+ pd_prefix = string.sub(name, 1, third_pipe) .. "PD"
+ end
+ local pds = iads:getSAMSitesByPrefix(pd_prefix)
for j = 1, #pds do
pd = pds[j]
env.info(string.format("DCSLiberation|Skynet-IADS plugin - Adding %s as PD for %s", pd:getDCSName(), name))
From 357361de3deabc8894c46207e784c8ba6c47e482 Mon Sep 17 00:00:00 2001
From: RndName
Date: Sat, 19 Jun 2021 23:29:44 +0200
Subject: [PATCH 017/167] fixed lua data generation
---
game/operation/operation.py | 26 +++++++++++++-------------
1 file changed, 13 insertions(+), 13 deletions(-)
diff --git a/game/operation/operation.py b/game/operation/operation.py
index da5239cb..a44dd5aa 100644
--- a/game/operation/operation.py
+++ b/game/operation/operation.py
@@ -439,8 +439,8 @@ class Operation:
"BlueAA": {},
} # type: ignore
- for tanker in airsupportgen.air_support.tankers:
- luaData["Tankers"][tanker.callsign] = {
+ for i, tanker in enumerate(airsupportgen.air_support.tankers):
+ luaData["Tankers"][i] = {
"dcsGroupName": tanker.group_name,
"callsign": tanker.callsign,
"variant": tanker.variant,
@@ -448,23 +448,22 @@ class Operation:
"tacan": str(tanker.tacan.number) + tanker.tacan.band.name,
}
- if airsupportgen.air_support.awacs:
- for awacs in airsupportgen.air_support.awacs:
- luaData["AWACs"][awacs.callsign] = {
- "dcsGroupName": awacs.group_name,
- "callsign": awacs.callsign,
- "radio": awacs.freq.mhz,
- }
+ for i, awacs in enumerate(airsupportgen.air_support.awacs):
+ luaData["AWACs"][i] = {
+ "dcsGroupName": awacs.group_name,
+ "callsign": awacs.callsign,
+ "radio": awacs.freq.mhz,
+ }
- for jtac in jtacs:
- luaData["JTACs"][jtac.callsign] = {
+ for i, jtac in enumerate(jtacs):
+ luaData["JTACs"][i] = {
"dcsGroupName": jtac.group_name,
"callsign": jtac.callsign,
"zone": jtac.region,
"dcsUnit": jtac.unit_name,
"laserCode": jtac.code,
}
-
+ flight_count = 0
for flight in airgen.flights:
if flight.friendly and flight.flight_type in [
FlightType.ANTISHIP,
@@ -485,7 +484,7 @@ class Operation:
elif hasattr(flightTarget, "name"):
flightTargetName = flightTarget.name
flightTargetType = flightType + " TGT (Airbase)"
- luaData["TargetPoints"][flightTargetName] = {
+ luaData["TargetPoints"][flight_count] = {
"name": flightTargetName,
"type": flightTargetType,
"position": {
@@ -493,6 +492,7 @@ class Operation:
"y": flightTarget.position.y,
},
}
+ flight_count += 1
for cp in cls.game.theater.controlpoints:
for ground_object in cp.ground_objects:
From 9e22d4b5dfa1a5848fc707dea831ea66930a7049 Mon Sep 17 00:00:00 2001
From: Chris Seagraves <47610393+nosv1@users.noreply.github.com>
Date: Fri, 2 Jul 2021 03:26:55 -0500
Subject: [PATCH 018/167] Note TGO tooltip improvement in the changelog.
---
changelog.md | 1 +
1 file changed, 1 insertion(+)
diff --git a/changelog.md b/changelog.md
index bd9a6626..7e2ae0a8 100644
--- a/changelog.md
+++ b/changelog.md
@@ -17,6 +17,7 @@ Saves from 4.0.0 are compatible with 4.0.1.
* **[UI]** Added ability to take notes and have those notes appear as a kneeboard page.
* **[UI]** Hovering over the weather information now dispalys the cloud base (meters and feet).
* **[UI]** Google search link added to unit information when there is no information provided.
+* **[UI]** Control point name displayed with ground object group name on map.
## Fixes
* **[UI]** Statistics window tick marks are now always integers.
From 4e6659e7e8099c371251c9df8e193607f9386f7e Mon Sep 17 00:00:00 2001
From: Dan Albert
Date: Fri, 2 Jul 2021 01:27:51 -0700
Subject: [PATCH 019/167] 4.0.1 -> 4.1.0
This includes new features now.
---
changelog.md | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/changelog.md b/changelog.md
index 7e2ae0a8..6ed5616c 100644
--- a/changelog.md
+++ b/changelog.md
@@ -6,9 +6,9 @@ Saves from 3.x are not compatible with 5.0.
## Fixes
-# 4.0.1
+# 4.1.0
-Saves from 4.0.0 are compatible with 4.0.1.
+Saves from 4.0.0 are compatible with 4.1.0.
## Features/Improvements
From 3f65928e9d1012cff95029ce03bec08db682ea92 Mon Sep 17 00:00:00 2001
From: Florian
Date: Thu, 24 Jun 2021 00:42:13 +0200
Subject: [PATCH 020/167] Remove the randomness from SAM group size.
---
changelog.md | 3 ++-
gen/sam/aaa_bofors.py | 25 ++++++++++-----------
gen/sam/aaa_flak.py | 34 +++++++++++++----------------
gen/sam/aaa_ks19.py | 24 ++++++++++----------
gen/sam/aaa_zsu57.py | 2 +-
gen/sam/aaa_zu23_insurgent.py | 26 ++++++++++------------
gen/sam/sam_avenger.py | 2 +-
gen/sam/sam_chaparral.py | 2 +-
gen/sam/sam_gepard.py | 20 ++++++++---------
gen/sam/sam_hawk.py | 2 +-
gen/sam/sam_hq7.py | 9 +-------
gen/sam/sam_linebacker.py | 2 +-
gen/sam/sam_patriot.py | 21 +++++++++---------
gen/sam/sam_rapier.py | 2 +-
gen/sam/sam_roland.py | 18 ++++++++++-----
gen/sam/sam_sa10.py | 18 +++++----------
gen/sam/sam_sa11.py | 2 +-
gen/sam/sam_sa13.py | 2 +-
gen/sam/sam_sa15.py | 17 ++++++++++-----
gen/sam/sam_sa19.py | 2 +-
gen/sam/sam_sa2.py | 2 +-
gen/sam/sam_sa3.py | 2 +-
gen/sam/sam_sa6.py | 2 +-
gen/sam/sam_sa8.py | 18 ++++++++++-----
gen/sam/sam_sa9.py | 2 +-
gen/sam/sam_vulcan.py | 20 ++++++++---------
gen/sam/sam_zsu23.py | 11 ++++++++--
gen/sam/sam_zu23.py | 35 ++++++++++++++++--------------
gen/sam/sam_zu23_ural.py | 2 +-
gen/sam/sam_zu23_ural_insurgent.py | 2 +-
30 files changed, 164 insertions(+), 165 deletions(-)
diff --git a/changelog.md b/changelog.md
index 6ed5616c..4864dfd3 100644
--- a/changelog.md
+++ b/changelog.md
@@ -12,8 +12,9 @@ Saves from 4.0.0 are compatible with 4.1.0.
## Features/Improvements
-* **[Plugins]** Increased time JTAC Autolase messages stay visible on the UI.
+* **[Campaign]** Air defense sites now generate a fixed number of launchers per type.
* **[Mission Generation]** Improvements for better support of the Skynet Plugin and long range SAMs are now acting as EWR
+* **[Plugins]** Increased time JTAC Autolase messages stay visible on the UI.
* **[UI]** Added ability to take notes and have those notes appear as a kneeboard page.
* **[UI]** Hovering over the weather information now dispalys the cloud base (meters and feet).
* **[UI]** Google search link added to unit information when there is no information provided.
diff --git a/gen/sam/aaa_bofors.py b/gen/sam/aaa_bofors.py
index 8c76f7f4..283c1d59 100644
--- a/gen/sam/aaa_bofors.py
+++ b/gen/sam/aaa_bofors.py
@@ -17,22 +17,19 @@ class BoforsGenerator(AirDefenseGroupGenerator):
price = 75
def generate(self):
- grid_x = random.randint(2, 3)
- grid_y = random.randint(2, 3)
-
- spacing = random.randint(10, 40)
index = 0
- for i in range(grid_x):
- for j in range(grid_y):
- index = index + 1
- self.add_unit(
- AirDefence.Bofors40,
- "AAA#" + str(index),
- self.position.x + spacing * i,
- self.position.y + spacing * j,
- self.heading,
- )
+ for i in range(4):
+ spacing_x = random.randint(10, 40)
+ spacing_y = random.randint(10, 40)
+ index = index + 1
+ self.add_unit(
+ AirDefence.Bofors40,
+ "AAA#" + str(index),
+ self.position.x + spacing_x * i,
+ self.position.y + spacing_y * i,
+ self.heading,
+ )
@classmethod
def range(cls) -> AirDefenseRange:
diff --git a/gen/sam/aaa_flak.py b/gen/sam/aaa_flak.py
index f918e48a..141e5b7d 100644
--- a/gen/sam/aaa_flak.py
+++ b/gen/sam/aaa_flak.py
@@ -26,28 +26,24 @@ class FlakGenerator(AirDefenseGroupGenerator):
price = 135
def generate(self):
- grid_x = random.randint(2, 3)
- grid_y = random.randint(2, 3)
-
- spacing = random.randint(20, 35)
-
index = 0
mixed = random.choice([True, False])
unit_type = random.choice(GFLAK)
- for i in range(grid_x):
- for j in range(grid_y):
- index = index + 1
- self.add_unit(
- unit_type,
- "AAA#" + str(index),
- self.position.x + spacing * i + random.randint(1, 5),
- self.position.y + spacing * j + random.randint(1, 5),
- self.heading,
- )
+ for i in range(4):
+ index = index + 1
+ spacing_x = random.randint(10, 40)
+ spacing_y = random.randint(10, 40)
+ self.add_unit(
+ unit_type,
+ "AAA#" + str(index),
+ self.position.x + spacing_x * i + random.randint(1, 5),
+ self.position.y + spacing_y * i + random.randint(1, 5),
+ self.heading,
+ )
- if mixed:
- unit_type = random.choice(GFLAK)
+ if mixed:
+ unit_type = random.choice(GFLAK)
# Search lights
search_pos = self.get_circular_position(random.randint(2, 3), 80)
@@ -86,8 +82,8 @@ class FlakGenerator(AirDefenseGroupGenerator):
)
# Some Opel Blitz trucks
- for i in range(int(max(1, grid_x / 2))):
- for j in range(int(max(1, grid_x / 2))):
+ for i in range(int(max(1, 2))):
+ for j in range(int(max(1, 2))):
self.add_unit(
Unarmed.Blitz_36_6700A,
"BLITZ#" + str(index),
diff --git a/gen/sam/aaa_ks19.py b/gen/sam/aaa_ks19.py
index 1e3de4ca..bb51e92a 100644
--- a/gen/sam/aaa_ks19.py
+++ b/gen/sam/aaa_ks19.py
@@ -16,9 +16,6 @@ class KS19Generator(AirDefenseGroupGenerator):
price = 98
def generate(self):
-
- spacing = random.randint(10, 40)
-
self.add_unit(
highdigitsams.AAA_SON_9_Fire_Can,
"TR",
@@ -28,16 +25,17 @@ class KS19Generator(AirDefenseGroupGenerator):
)
index = 0
- for i in range(3):
- for j in range(3):
- index = index + 1
- self.add_unit(
- highdigitsams.AAA_100mm_KS_19,
- "AAA#" + str(index),
- self.position.x + spacing * i,
- self.position.y + spacing * j,
- self.heading,
- )
+ for i in range(4):
+ spacing_x = random.randint(10, 40)
+ spacing_y = random.randint(10, 40)
+ index = index + 1
+ self.add_unit(
+ highdigitsams.AAA_100mm_KS_19,
+ "AAA#" + str(index),
+ self.position.x + spacing_x * i,
+ self.position.y + spacing_y * i,
+ self.heading,
+ )
@classmethod
def range(cls) -> AirDefenseRange:
diff --git a/gen/sam/aaa_zsu57.py b/gen/sam/aaa_zsu57.py
index 4648e90b..422693d5 100644
--- a/gen/sam/aaa_zsu57.py
+++ b/gen/sam/aaa_zsu57.py
@@ -15,7 +15,7 @@ class ZSU57Generator(AirDefenseGroupGenerator):
price = 60
def generate(self):
- num_launchers = 5
+ num_launchers = 4
positions = self.get_circular_position(
num_launchers, launcher_distance=110, coverage=360
)
diff --git a/gen/sam/aaa_zu23_insurgent.py b/gen/sam/aaa_zu23_insurgent.py
index 5ca97638..49113668 100644
--- a/gen/sam/aaa_zu23_insurgent.py
+++ b/gen/sam/aaa_zu23_insurgent.py
@@ -17,22 +17,18 @@ class ZU23InsurgentGenerator(AirDefenseGroupGenerator):
price = 56
def generate(self):
- grid_x = random.randint(2, 3)
- grid_y = random.randint(2, 3)
-
- spacing = random.randint(10, 40)
-
index = 0
- for i in range(grid_x):
- for j in range(grid_y):
- index = index + 1
- self.add_unit(
- AirDefence.ZU_23_Closed_Insurgent,
- "AAA#" + str(index),
- self.position.x + spacing * i,
- self.position.y + spacing * j,
- self.heading,
- )
+ for i in range(4):
+ index = index + 1
+ spacing_x = random.randint(10, 40)
+ spacing_y = random.randint(10, 40)
+ self.add_unit(
+ AirDefence.ZU_23_Closed_Insurgent,
+ "AAA#" + str(index),
+ self.position.x + spacing_x * i,
+ self.position.y + spacing_y * i,
+ self.heading,
+ )
@classmethod
def range(cls) -> AirDefenseRange:
diff --git a/gen/sam/sam_avenger.py b/gen/sam/sam_avenger.py
index b778cc62..b3f63354 100644
--- a/gen/sam/sam_avenger.py
+++ b/gen/sam/sam_avenger.py
@@ -17,7 +17,7 @@ class AvengerGenerator(AirDefenseGroupGenerator):
price = 62
def generate(self):
- num_launchers = random.randint(2, 3)
+ num_launchers = 2
self.add_unit(
Unarmed.M_818,
diff --git a/gen/sam/sam_chaparral.py b/gen/sam/sam_chaparral.py
index 465ba0bd..ea239746 100644
--- a/gen/sam/sam_chaparral.py
+++ b/gen/sam/sam_chaparral.py
@@ -17,7 +17,7 @@ class ChaparralGenerator(AirDefenseGroupGenerator):
price = 66
def generate(self):
- num_launchers = random.randint(2, 4)
+ num_launchers = 2
self.add_unit(
Unarmed.M_818,
diff --git a/gen/sam/sam_gepard.py b/gen/sam/sam_gepard.py
index 669781df..6128efab 100644
--- a/gen/sam/sam_gepard.py
+++ b/gen/sam/sam_gepard.py
@@ -17,20 +17,18 @@ class GepardGenerator(AirDefenseGroupGenerator):
price = 50
def generate(self):
- self.add_unit(
- AirDefence.Gepard,
- "SPAAA",
- self.position.x,
- self.position.y,
- self.heading,
+ num_launchers = 2
+
+ positions = self.get_circular_position(
+ num_launchers, launcher_distance=120, coverage=180
)
- if random.randint(0, 1) == 1:
+ for i, position in enumerate(positions):
self.add_unit(
AirDefence.Gepard,
- "SPAAA2",
- self.position.x,
- self.position.y,
- self.heading,
+ "SPAA#" + str(i),
+ position[0],
+ position[1],
+ position[2],
)
self.add_unit(
Unarmed.M_818,
diff --git a/gen/sam/sam_hawk.py b/gen/sam/sam_hawk.py
index ea05f726..efec60a2 100644
--- a/gen/sam/sam_hawk.py
+++ b/gen/sam/sam_hawk.py
@@ -51,7 +51,7 @@ class HawkGenerator(AirDefenseGroupGenerator):
self.heading,
)
- num_launchers = random.randint(3, 6)
+ num_launchers = 6
positions = self.get_circular_position(
num_launchers, launcher_distance=120, coverage=180
)
diff --git a/gen/sam/sam_hq7.py b/gen/sam/sam_hq7.py
index be5eeb6a..0143fc63 100644
--- a/gen/sam/sam_hq7.py
+++ b/gen/sam/sam_hq7.py
@@ -26,13 +26,6 @@ class HQ7Generator(AirDefenseGroupGenerator):
self.position.y,
self.heading,
)
- self.add_unit(
- AirDefence.HQ_7_LN_SP,
- "LN",
- self.position.x + 20,
- self.position.y,
- self.heading,
- )
# Triple A for close range defense
aa_group = self.add_auxiliary_group(SkynetRole.NoSkynetBehavior)
@@ -51,7 +44,7 @@ class HQ7Generator(AirDefenseGroupGenerator):
self.heading,
)
- num_launchers = random.randint(0, 3)
+ num_launchers = 2
if num_launchers > 0:
positions = self.get_circular_position(
num_launchers, launcher_distance=120, coverage=360
diff --git a/gen/sam/sam_linebacker.py b/gen/sam/sam_linebacker.py
index b140b138..09c57117 100644
--- a/gen/sam/sam_linebacker.py
+++ b/gen/sam/sam_linebacker.py
@@ -17,7 +17,7 @@ class LinebackerGenerator(AirDefenseGroupGenerator):
price = 75
def generate(self):
- num_launchers = random.randint(2, 4)
+ num_launchers = 2
self.add_unit(
Unarmed.M_818,
diff --git a/gen/sam/sam_patriot.py b/gen/sam/sam_patriot.py
index aafeb79c..fcd82417 100644
--- a/gen/sam/sam_patriot.py
+++ b/gen/sam/sam_patriot.py
@@ -1,5 +1,3 @@
-import random
-
from dcs.mapping import Point
from dcs.vehicles import AirDefence
@@ -56,10 +54,7 @@ class PatriotGenerator(AirDefenseGroupGenerator):
self.heading,
)
- num_launchers = random.randint(3, 4)
- positions = self.get_circular_position(
- num_launchers, launcher_distance=120, coverage=360
- )
+ positions = self.get_circular_position(8, launcher_distance=120, coverage=360)
for i, position in enumerate(positions):
self.add_unit(
AirDefence.Patriot_ln,
@@ -71,10 +66,7 @@ class PatriotGenerator(AirDefenseGroupGenerator):
# Short range protection for high value site
aa_group = self.add_auxiliary_group(SkynetRole.NoSkynetBehavior)
- num_launchers = random.randint(3, 4)
- positions = self.get_circular_position(
- num_launchers, launcher_distance=200, coverage=360
- )
+ positions = self.get_circular_position(2, launcher_distance=200, coverage=360)
for i, (x, y, heading) in enumerate(positions):
self.add_unit_to_group(
aa_group,
@@ -83,6 +75,15 @@ class PatriotGenerator(AirDefenseGroupGenerator):
Point(x, y),
heading,
)
+ positions = self.get_circular_position(2, launcher_distance=300, coverage=360)
+ for i, (x, y, heading) in enumerate(positions):
+ self.add_unit_to_group(
+ aa_group,
+ AirDefence.M1097_Avenger,
+ f"Avenger#{i}",
+ Point(x, y),
+ heading,
+ )
@classmethod
def range(cls) -> AirDefenseRange:
diff --git a/gen/sam/sam_rapier.py b/gen/sam/sam_rapier.py
index af3965e4..538dd7c4 100644
--- a/gen/sam/sam_rapier.py
+++ b/gen/sam/sam_rapier.py
@@ -33,7 +33,7 @@ class RapierGenerator(AirDefenseGroupGenerator):
self.heading,
)
- num_launchers = random.randint(3, 6)
+ num_launchers = 2
positions = self.get_circular_position(
num_launchers, launcher_distance=80, coverage=240
)
diff --git a/gen/sam/sam_roland.py b/gen/sam/sam_roland.py
index e2e704af..64ee154d 100644
--- a/gen/sam/sam_roland.py
+++ b/gen/sam/sam_roland.py
@@ -16,6 +16,7 @@ class RolandGenerator(AirDefenseGroupGenerator):
price = 40
def generate(self):
+ num_launchers = 2
self.add_unit(
AirDefence.Roland_Radar,
"EWR",
@@ -23,13 +24,18 @@ class RolandGenerator(AirDefenseGroupGenerator):
self.position.y,
self.heading,
)
- self.add_unit(
- AirDefence.Roland_ADS,
- "ADS",
- self.position.x,
- self.position.y,
- self.heading,
+ positions = self.get_circular_position(
+ num_launchers, launcher_distance=80, coverage=240
)
+
+ for i, position in enumerate(positions):
+ self.add_unit(
+ AirDefence.Roland_ADS,
+ "ADS#" + str(i),
+ position[0],
+ position[1],
+ position[2],
+ )
self.add_unit(
Unarmed.M_818,
"TRUCK",
diff --git a/gen/sam/sam_sa10.py b/gen/sam/sam_sa10.py
index 35611e83..8d4d4e2c 100644
--- a/gen/sam/sam_sa10.py
+++ b/gen/sam/sam_sa10.py
@@ -1,5 +1,3 @@
-import random
-
from dcs.mapping import Point
from dcs.vehicles import AirDefence
@@ -45,17 +43,13 @@ class SA10Generator(AirDefenseGroupGenerator):
# Command Post
self.add_unit(self.cp, "CP", self.position.x, self.position.y, self.heading)
- # 2 Tracking radars
+ # 1 Tracking radar
self.add_unit(
self.tr1, "TR1", self.position.x - 40, self.position.y - 40, self.heading
)
- self.add_unit(
- self.tr2, "TR2", self.position.x + 40, self.position.y - 40, self.heading
- )
-
# 2 different launcher type (C & D)
- num_launchers = random.randint(6, 8)
+ num_launchers = 6
positions = self.get_circular_position(
num_launchers, launcher_distance=100, coverage=360
)
@@ -78,7 +72,7 @@ class SA10Generator(AirDefenseGroupGenerator):
def generate_defensive_groups(self) -> None:
# AAA for defending against close targets.
aa_group = self.add_auxiliary_group(SkynetRole.NoSkynetBehavior)
- num_launchers = random.randint(6, 8)
+ num_launchers = 2
positions = self.get_circular_position(
num_launchers, launcher_distance=210, coverage=360
)
@@ -103,7 +97,7 @@ class Tier2SA10Generator(SA10Generator):
# SA-15 for both shorter range targets and point defense.
pd_group = self.add_auxiliary_group(SkynetRole.PointDefense)
- num_launchers = random.randint(2, 4)
+ num_launchers = 2
positions = self.get_circular_position(
num_launchers, launcher_distance=140, coverage=360
)
@@ -125,7 +119,7 @@ class Tier3SA10Generator(SA10Generator):
def generate_defensive_groups(self) -> None:
# AAA for defending against close targets.
aa_group = self.add_auxiliary_group(SkynetRole.NoSkynetBehavior)
- num_launchers = random.randint(6, 8)
+ num_launchers = 2
positions = self.get_circular_position(
num_launchers, launcher_distance=210, coverage=360
)
@@ -140,7 +134,7 @@ class Tier3SA10Generator(SA10Generator):
# SA-15 for both shorter range targets and point defense.
pd_group = self.add_auxiliary_group(SkynetRole.PointDefense)
- num_launchers = random.randint(2, 4)
+ num_launchers = 2
positions = self.get_circular_position(
num_launchers, launcher_distance=140, coverage=360
)
diff --git a/gen/sam/sam_sa11.py b/gen/sam/sam_sa11.py
index 7fec37c2..3611aff5 100644
--- a/gen/sam/sam_sa11.py
+++ b/gen/sam/sam_sa11.py
@@ -32,7 +32,7 @@ class SA11Generator(AirDefenseGroupGenerator):
self.heading,
)
- num_launchers = random.randint(2, 4)
+ num_launchers = 4
positions = self.get_circular_position(
num_launchers, launcher_distance=140, coverage=180
)
diff --git a/gen/sam/sam_sa13.py b/gen/sam/sam_sa13.py
index 0fbe1af0..24cd75a0 100644
--- a/gen/sam/sam_sa13.py
+++ b/gen/sam/sam_sa13.py
@@ -32,7 +32,7 @@ class SA13Generator(AirDefenseGroupGenerator):
self.heading,
)
- num_launchers = random.randint(2, 3)
+ num_launchers = 2
positions = self.get_circular_position(
num_launchers, launcher_distance=120, coverage=360
)
diff --git a/gen/sam/sam_sa15.py b/gen/sam/sam_sa15.py
index 3dcb881a..d5d74b9c 100644
--- a/gen/sam/sam_sa15.py
+++ b/gen/sam/sam_sa15.py
@@ -15,13 +15,18 @@ class SA15Generator(AirDefenseGroupGenerator):
price = 55
def generate(self):
- self.add_unit(
- AirDefence.Tor_9A331,
- "ADS",
- self.position.x,
- self.position.y,
- self.heading,
+ num_launchers = 2
+ positions = self.get_circular_position(
+ num_launchers, launcher_distance=120, coverage=360
)
+ for i, position in enumerate(positions):
+ self.add_unit(
+ AirDefence.Tor_9A331,
+ "ADS#" + str(i),
+ position[0],
+ position[1],
+ position[2],
+ )
self.add_unit(
Unarmed.UAZ_469,
"EWR",
diff --git a/gen/sam/sam_sa19.py b/gen/sam/sam_sa19.py
index caac1f7c..0ff23158 100644
--- a/gen/sam/sam_sa19.py
+++ b/gen/sam/sam_sa19.py
@@ -17,7 +17,7 @@ class SA19Generator(AirDefenseGroupGenerator):
price = 90
def generate(self):
- num_launchers = random.randint(1, 3)
+ num_launchers = 2
if num_launchers == 1:
self.add_unit(
diff --git a/gen/sam/sam_sa2.py b/gen/sam/sam_sa2.py
index 4b7341df..3cfae81e 100644
--- a/gen/sam/sam_sa2.py
+++ b/gen/sam/sam_sa2.py
@@ -32,7 +32,7 @@ class SA2Generator(AirDefenseGroupGenerator):
self.heading,
)
- num_launchers = random.randint(3, 6)
+ num_launchers = 6
positions = self.get_circular_position(
num_launchers, launcher_distance=120, coverage=180
)
diff --git a/gen/sam/sam_sa3.py b/gen/sam/sam_sa3.py
index 1a95de12..400bda82 100644
--- a/gen/sam/sam_sa3.py
+++ b/gen/sam/sam_sa3.py
@@ -32,7 +32,7 @@ class SA3Generator(AirDefenseGroupGenerator):
self.heading,
)
- num_launchers = random.randint(3, 6)
+ num_launchers = 4
positions = self.get_circular_position(
num_launchers, launcher_distance=120, coverage=180
)
diff --git a/gen/sam/sam_sa6.py b/gen/sam/sam_sa6.py
index fa72b24a..6706f250 100644
--- a/gen/sam/sam_sa6.py
+++ b/gen/sam/sam_sa6.py
@@ -25,7 +25,7 @@ class SA6Generator(AirDefenseGroupGenerator):
self.heading,
)
- num_launchers = random.randint(2, 4)
+ num_launchers = 4
positions = self.get_circular_position(
num_launchers, launcher_distance=120, coverage=360
)
diff --git a/gen/sam/sam_sa8.py b/gen/sam/sam_sa8.py
index 3ab28dfc..7f3f72b3 100644
--- a/gen/sam/sam_sa8.py
+++ b/gen/sam/sam_sa8.py
@@ -15,13 +15,19 @@ class SA8Generator(AirDefenseGroupGenerator):
price = 55
def generate(self):
- self.add_unit(
- AirDefence.Osa_9A33_ln,
- "OSA",
- self.position.x,
- self.position.y,
- self.heading,
+ num_launchers = 2
+ positions = self.get_circular_position(
+ num_launchers, launcher_distance=120, coverage=180
)
+
+ for i, position in enumerate(positions):
+ self.add_unit(
+ AirDefence.Osa_9A33_ln,
+ "OSA" + str(i),
+ position[0],
+ position[1],
+ position[2],
+ )
self.add_unit(
AirDefence.SA_8_Osa_LD_9T217,
"LD",
diff --git a/gen/sam/sam_sa9.py b/gen/sam/sam_sa9.py
index fccc7973..ed7883b5 100644
--- a/gen/sam/sam_sa9.py
+++ b/gen/sam/sam_sa9.py
@@ -32,7 +32,7 @@ class SA9Generator(AirDefenseGroupGenerator):
self.heading,
)
- num_launchers = random.randint(2, 3)
+ num_launchers = 2
positions = self.get_circular_position(
num_launchers, launcher_distance=120, coverage=360
)
diff --git a/gen/sam/sam_vulcan.py b/gen/sam/sam_vulcan.py
index 2d057dc0..0c869afc 100644
--- a/gen/sam/sam_vulcan.py
+++ b/gen/sam/sam_vulcan.py
@@ -17,20 +17,18 @@ class VulcanGenerator(AirDefenseGroupGenerator):
price = 25
def generate(self):
- self.add_unit(
- AirDefence.Vulcan,
- "SPAAA",
- self.position.x,
- self.position.y,
- self.heading,
+ num_launchers = 2
+
+ positions = self.get_circular_position(
+ num_launchers, launcher_distance=120, coverage=180
)
- if random.randint(0, 1) == 1:
+ for i, position in enumerate(positions):
self.add_unit(
AirDefence.Vulcan,
- "SPAAA2",
- self.position.x,
- self.position.y,
- self.heading,
+ "SPAA#" + str(i),
+ position[0],
+ position[1],
+ position[2],
)
self.add_unit(
Unarmed.M_818,
diff --git a/gen/sam/sam_zsu23.py b/gen/sam/sam_zsu23.py
index 708ae5c6..0e638b62 100644
--- a/gen/sam/sam_zsu23.py
+++ b/gen/sam/sam_zsu23.py
@@ -1,6 +1,6 @@
import random
-from dcs.vehicles import AirDefence
+from dcs.vehicles import AirDefence, Unarmed
from gen.sam.airdefensegroupgenerator import (
AirDefenseRange,
@@ -17,7 +17,7 @@ class ZSU23Generator(AirDefenseGroupGenerator):
price = 50
def generate(self):
- num_launchers = random.randint(4, 5)
+ num_launchers = 4
positions = self.get_circular_position(
num_launchers, launcher_distance=120, coverage=180
@@ -30,6 +30,13 @@ class ZSU23Generator(AirDefenseGroupGenerator):
position[1],
position[2],
)
+ self.add_unit(
+ Unarmed.M_818,
+ "TRUCK",
+ self.position.x + 80,
+ self.position.y,
+ self.heading,
+ )
@classmethod
def range(cls) -> AirDefenseRange:
diff --git a/gen/sam/sam_zu23.py b/gen/sam/sam_zu23.py
index 6a1b41cb..58c55ad5 100644
--- a/gen/sam/sam_zu23.py
+++ b/gen/sam/sam_zu23.py
@@ -1,6 +1,6 @@
import random
-from dcs.vehicles import AirDefence
+from dcs.vehicles import AirDefence, Unarmed
from gen.sam.airdefensegroupgenerator import (
AirDefenseRange,
@@ -17,22 +17,25 @@ class ZU23Generator(AirDefenseGroupGenerator):
price = 54
def generate(self):
- grid_x = random.randint(2, 3)
- grid_y = random.randint(2, 3)
-
- spacing = random.randint(10, 40)
-
index = 0
- for i in range(grid_x):
- for j in range(grid_y):
- index = index + 1
- self.add_unit(
- AirDefence.ZU_23_Emplacement_Closed,
- "AAA#" + str(index),
- self.position.x + spacing * i,
- self.position.y + spacing * j,
- self.heading,
- )
+ for i in range(4):
+ index = index + 1
+ spacing_x = random.randint(10, 40)
+ spacing_y = random.randint(10, 40)
+ self.add_unit(
+ AirDefence.ZU_23_Emplacement_Closed,
+ "AAA#" + str(index),
+ self.position.x + spacing_x * i,
+ self.position.y + spacing_y * i,
+ self.heading,
+ )
+ self.add_unit(
+ Unarmed.M_818,
+ "TRUCK",
+ self.position.x + 80,
+ self.position.y,
+ self.heading,
+ )
@classmethod
def range(cls) -> AirDefenseRange:
diff --git a/gen/sam/sam_zu23_ural.py b/gen/sam/sam_zu23_ural.py
index 4f97d6f3..47b7ac6a 100644
--- a/gen/sam/sam_zu23_ural.py
+++ b/gen/sam/sam_zu23_ural.py
@@ -17,7 +17,7 @@ class ZU23UralGenerator(AirDefenseGroupGenerator):
price = 64
def generate(self):
- num_launchers = random.randint(2, 8)
+ num_launchers = 4
positions = self.get_circular_position(
num_launchers, launcher_distance=80, coverage=360
diff --git a/gen/sam/sam_zu23_ural_insurgent.py b/gen/sam/sam_zu23_ural_insurgent.py
index d0ab8405..0e277599 100644
--- a/gen/sam/sam_zu23_ural_insurgent.py
+++ b/gen/sam/sam_zu23_ural_insurgent.py
@@ -17,7 +17,7 @@ class ZU23UralInsurgentGenerator(AirDefenseGroupGenerator):
price = 64
def generate(self):
- num_launchers = random.randint(2, 8)
+ num_launchers = 4
positions = self.get_circular_position(
num_launchers, launcher_distance=80, coverage=360
From ceb77c990b5e15c4a7d8313f2f3b041d88811b47 Mon Sep 17 00:00:00 2001
From: Mustang-25 <72566076+Mustang-25@users.noreply.github.com>
Date: Fri, 2 Jul 2021 11:45:01 -0700
Subject: [PATCH 021/167] Corrected Silkworm launcher name
---
gen/coastal/silkworm.py | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/gen/coastal/silkworm.py b/gen/coastal/silkworm.py
index 4198e004..1ffe6b04 100644
--- a/gen/coastal/silkworm.py
+++ b/gen/coastal/silkworm.py
@@ -23,7 +23,7 @@ class SilkwormGenerator(GroupGenerator):
# Launchers
for i, p in enumerate(positions):
self.add_unit(
- MissilesSS.Silkworm_SR,
+ MissilesSS.Hy_launcher,
"Missile#" + str(i),
p[0],
p[1],
From bab8384803eb0bcf16f1a3e599c6555a060ac304 Mon Sep 17 00:00:00 2001
From: Mustang-25 <72566076+Mustang-25@users.noreply.github.com>
Date: Fri, 2 Jul 2021 11:45:01 -0700
Subject: [PATCH 022/167] Corrected Silkworm launcher name
---
gen/coastal/silkworm.py | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/gen/coastal/silkworm.py b/gen/coastal/silkworm.py
index 4198e004..1ffe6b04 100644
--- a/gen/coastal/silkworm.py
+++ b/gen/coastal/silkworm.py
@@ -23,7 +23,7 @@ class SilkwormGenerator(GroupGenerator):
# Launchers
for i, p in enumerate(positions):
self.add_unit(
- MissilesSS.Silkworm_SR,
+ MissilesSS.Hy_launcher,
"Missile#" + str(i),
p[0],
p[1],
From 3f42f1281d60b941b0b269c50c3aebc0ed5338fd Mon Sep 17 00:00:00 2001
From: Dan Albert
Date: Fri, 2 Jul 2021 16:28:06 -0700
Subject: [PATCH 023/167] Note the silkworm fix in the changelog.
---
changelog.md | 6 ++++--
1 file changed, 4 insertions(+), 2 deletions(-)
diff --git a/changelog.md b/changelog.md
index 4864dfd3..00f2ca94 100644
--- a/changelog.md
+++ b/changelog.md
@@ -21,9 +21,11 @@ Saves from 4.0.0 are compatible with 4.1.0.
* **[UI]** Control point name displayed with ground object group name on map.
## Fixes
-* **[UI]** Statistics window tick marks are now always integers.
-* **[Mission Generation]** The lua data for other plugins is now generated correctly
+
+* **[Campaign]** Fixed the Silkworm generator to include launchers and not all radars.
* **[Flight Planning]** Fixed potential issue with angles > 360° or < 0° being generated when summing two angles.
+* **[Mission Generation]** The lua data for other plugins is now generated correctly
+* **[UI]** Statistics window tick marks are now always integers.
# 4.0.0
From 96be6c0efe4b6b0f3af38bf5c43a9818e1eed4e6 Mon Sep 17 00:00:00 2001
From: RndName
Date: Thu, 24 Jun 2021 21:50:02 +0200
Subject: [PATCH 024/167] correct prices for ewr and sams
prices will now be calculated for the whole group by the generator by
looking up the price using the GroundUnitType wrapper
fixes #1163
---
changelog.md | 2 ++
gen/sam/airdefensegroupgenerator.py | 2 --
gen/sam/ewrs.py | 5 ----
gen/sam/group_generator.py | 14 +++++++++++
.../windows/groundobject/QGroundObjectMenu.py | 24 ++++++++++---------
5 files changed, 29 insertions(+), 18 deletions(-)
diff --git a/changelog.md b/changelog.md
index 00f2ca94..9bd0a301 100644
--- a/changelog.md
+++ b/changelog.md
@@ -19,10 +19,12 @@ Saves from 4.0.0 are compatible with 4.1.0.
* **[UI]** Hovering over the weather information now dispalys the cloud base (meters and feet).
* **[UI]** Google search link added to unit information when there is no information provided.
* **[UI]** Control point name displayed with ground object group name on map.
+* **[UI]** Buy or Replace will now show the correct price for generated ground objects like sams.
## Fixes
* **[Campaign]** Fixed the Silkworm generator to include launchers and not all radars.
+* **[Economy]** EWRs can now be bought and sold for the correct price and can no longer be used to generate money
* **[Flight Planning]** Fixed potential issue with angles > 360° or < 0° being generated when summing two angles.
* **[Mission Generation]** The lua data for other plugins is now generated correctly
* **[UI]** Statistics window tick marks are now always integers.
diff --git a/gen/sam/airdefensegroupgenerator.py b/gen/sam/airdefensegroupgenerator.py
index 7d269ece..bc192691 100644
--- a/gen/sam/airdefensegroupgenerator.py
+++ b/gen/sam/airdefensegroupgenerator.py
@@ -43,8 +43,6 @@ class AirDefenseGroupGenerator(GroupGenerator, ABC):
This is the base for all SAM group generators
"""
- price: int
-
def __init__(self, game: Game, ground_object: SamGroundObject) -> None:
super().__init__(game, ground_object)
diff --git a/gen/sam/ewrs.py b/gen/sam/ewrs.py
index df27e6ad..3678fe79 100644
--- a/gen/sam/ewrs.py
+++ b/gen/sam/ewrs.py
@@ -13,11 +13,6 @@ class EwrGenerator(GroupGenerator):
def name(cls) -> str:
return cls.unit_type.name
- @staticmethod
- def price() -> int:
- # TODO: Differentiate sites.
- return 20
-
def generate(self) -> None:
self.add_unit(
self.unit_type, "EWR", self.position.x, self.position.y, self.heading
diff --git a/gen/sam/group_generator.py b/gen/sam/group_generator.py
index 65eb0b50..f41b9543 100644
--- a/gen/sam/group_generator.py
+++ b/gen/sam/group_generator.py
@@ -1,5 +1,6 @@
from __future__ import annotations
+import logging
import math
import random
from typing import TYPE_CHECKING, Type
@@ -10,6 +11,7 @@ from dcs.point import PointAction
from dcs.unit import Ship, Vehicle
from dcs.unittype import VehicleType
+from game.dcs.groundunittype import GroundUnitType
from game.factions.faction import Faction
from game.theater.theatergroundobject import TheaterGroundObject
@@ -23,11 +25,15 @@ if TYPE_CHECKING:
# care about in the format we want if we just generate our own group description
# types rather than pydcs groups.
class GroupGenerator:
+
+ price: int
+
def __init__(self, game: Game, ground_object: TheaterGroundObject) -> None:
self.game = game
self.go = ground_object
self.position = ground_object.position
self.heading = random.randint(0, 359)
+ self.price = 0
self.vg = unitgroup.VehicleGroup(self.game.next_group_id(), self.go.group_name)
wp = self.vg.add_waypoint(self.position, PointAction.OffRoad, 0)
wp.ETA_locked = True
@@ -62,6 +68,14 @@ class GroupGenerator:
unit.position = position
unit.heading = heading
group.add_unit(unit)
+
+ # get price of unit to calculate the real price of the whole group
+ try:
+ ground_unit_type = next(GroundUnitType.for_dcs_type(unit_type))
+ self.price += ground_unit_type.price
+ except StopIteration:
+ logging.error(f"Cannot get price for unit {unit_type.name}")
+
return unit
def get_circular_position(self, num_units, launcher_distance, coverage=90):
diff --git a/qt_ui/windows/groundobject/QGroundObjectMenu.py b/qt_ui/windows/groundobject/QGroundObjectMenu.py
index 96debe14..7f955f3d 100644
--- a/qt_ui/windows/groundobject/QGroundObjectMenu.py
+++ b/qt_ui/windows/groundobject/QGroundObjectMenu.py
@@ -305,8 +305,11 @@ class QBuyGroupForGroundObjectDialog(QDialog):
possible_sams = get_faction_possible_sams_generator(faction)
for sam in possible_sams:
+ # Pre Generate SAM to get the real price
+ generator = sam(self.game, self.ground_object)
+ generator.generate()
self.samCombo.addItem(
- sam.name + " [$" + str(sam.price) + "M]", userData=sam
+ generator.name + " [$" + str(generator.price) + "M]", userData=generator
)
self.samCombo.currentIndexChanged.connect(self.samComboChanged)
@@ -331,8 +334,12 @@ class QBuyGroupForGroundObjectDialog(QDialog):
buy_ewr_layout.addWidget(self.ewr_selector, 0, 1, alignment=Qt.AlignRight)
ewr_types = get_faction_possible_ewrs_generator(faction)
for ewr_type in ewr_types:
+ # Pre Generate to get the real price
+ generator = ewr_type(self.game, self.ground_object)
+ generator.generate()
self.ewr_selector.addItem(
- f"{ewr_type.name()} [${ewr_type.price()}M]", ewr_type
+ generator.name() + " [$" + str(generator.price) + "M]",
+ userData=generator,
)
self.ewr_selector.currentIndexChanged.connect(self.on_ewr_selection_changed)
@@ -402,7 +409,7 @@ class QBuyGroupForGroundObjectDialog(QDialog):
def on_ewr_selection_changed(self, index):
ewr = self.ewr_selector.itemData(index)
self.buy_ewr_button.setText(
- f"Buy [${ewr.price()}M][-${self.current_group_value}M]"
+ f"Buy [${ewr.price}M][-${self.current_group_value}M]"
)
def armorComboChanged(self, index):
@@ -443,25 +450,20 @@ class QBuyGroupForGroundObjectDialog(QDialog):
else:
self.game.budget -= price
- # Generate SAM
- generator = sam_generator(self.game, self.ground_object)
- generator.generate()
- self.ground_object.groups = list(generator.groups)
+ self.ground_object.groups = list(sam_generator.groups)
GameUpdateSignal.get_instance().updateGame(self.game)
def buy_ewr(self):
ewr_generator = self.ewr_selector.itemData(self.ewr_selector.currentIndex())
- price = ewr_generator.price() - self.current_group_value
+ price = ewr_generator.price - self.current_group_value
if price > self.game.budget:
self.error_money()
return
else:
self.game.budget -= price
- generator = ewr_generator(self.game, self.ground_object)
- generator.generate()
- self.ground_object.groups = [generator.vg]
+ self.ground_object.groups = [ewr_generator.vg]
GameUpdateSignal.get_instance().updateGame(self.game)
From b2db27f9aa06ddbeb6c9e87686abb04cd2f2bb16 Mon Sep 17 00:00:00 2001
From: RndName
Date: Sun, 27 Jun 2021 12:35:02 +0200
Subject: [PATCH 025/167] remove prices from sam generators
The prices are only estimations due to randomization. the real price will be only known when the generator was used and the final units are known
---
gen/sam/aaa_bofors.py | 1 -
gen/sam/aaa_flak.py | 1 -
gen/sam/aaa_flak18.py | 1 -
gen/sam/aaa_ks19.py | 1 -
gen/sam/aaa_ww2_ally_flak.py | 1 -
gen/sam/aaa_zsu57.py | 1 -
gen/sam/aaa_zu23_insurgent.py | 1 -
gen/sam/cold_war_flak.py | 2 --
gen/sam/freya_ewr.py | 1 -
gen/sam/sam_avenger.py | 1 -
gen/sam/sam_chaparral.py | 1 -
gen/sam/sam_gepard.py | 1 -
gen/sam/sam_hawk.py | 1 -
gen/sam/sam_hq7.py | 1 -
gen/sam/sam_linebacker.py | 1 -
gen/sam/sam_patriot.py | 1 -
gen/sam/sam_rapier.py | 1 -
gen/sam/sam_roland.py | 1 -
gen/sam/sam_sa10.py | 8 --------
gen/sam/sam_sa11.py | 1 -
gen/sam/sam_sa13.py | 1 -
gen/sam/sam_sa15.py | 1 -
gen/sam/sam_sa17.py | 1 -
gen/sam/sam_sa19.py | 1 -
gen/sam/sam_sa2.py | 1 -
gen/sam/sam_sa3.py | 1 -
gen/sam/sam_sa6.py | 1 -
gen/sam/sam_sa8.py | 1 -
gen/sam/sam_sa9.py | 1 -
gen/sam/sam_vulcan.py | 1 -
gen/sam/sam_zsu23.py | 1 -
gen/sam/sam_zu23.py | 1 -
gen/sam/sam_zu23_ural.py | 1 -
gen/sam/sam_zu23_ural_insurgent.py | 1 -
34 files changed, 42 deletions(-)
diff --git a/gen/sam/aaa_bofors.py b/gen/sam/aaa_bofors.py
index 283c1d59..b4c87e34 100644
--- a/gen/sam/aaa_bofors.py
+++ b/gen/sam/aaa_bofors.py
@@ -14,7 +14,6 @@ class BoforsGenerator(AirDefenseGroupGenerator):
"""
name = "Bofors AAA"
- price = 75
def generate(self):
diff --git a/gen/sam/aaa_flak.py b/gen/sam/aaa_flak.py
index 141e5b7d..882e5ad3 100644
--- a/gen/sam/aaa_flak.py
+++ b/gen/sam/aaa_flak.py
@@ -23,7 +23,6 @@ class FlakGenerator(AirDefenseGroupGenerator):
"""
name = "Flak Site"
- price = 135
def generate(self):
index = 0
diff --git a/gen/sam/aaa_flak18.py b/gen/sam/aaa_flak18.py
index 91f81f15..60f1d389 100644
--- a/gen/sam/aaa_flak18.py
+++ b/gen/sam/aaa_flak18.py
@@ -14,7 +14,6 @@ class Flak18Generator(AirDefenseGroupGenerator):
"""
name = "WW2 Flak Site"
- price = 40
def generate(self):
diff --git a/gen/sam/aaa_ks19.py b/gen/sam/aaa_ks19.py
index bb51e92a..f173dab2 100644
--- a/gen/sam/aaa_ks19.py
+++ b/gen/sam/aaa_ks19.py
@@ -13,7 +13,6 @@ class KS19Generator(AirDefenseGroupGenerator):
"""
name = "KS-19 AAA Site"
- price = 98
def generate(self):
self.add_unit(
diff --git a/gen/sam/aaa_ww2_ally_flak.py b/gen/sam/aaa_ww2_ally_flak.py
index 415bdab3..7e58718f 100644
--- a/gen/sam/aaa_ww2_ally_flak.py
+++ b/gen/sam/aaa_ww2_ally_flak.py
@@ -14,7 +14,6 @@ class AllyWW2FlakGenerator(AirDefenseGroupGenerator):
"""
name = "WW2 Ally Flak Site"
- price = 140
def generate(self):
diff --git a/gen/sam/aaa_zsu57.py b/gen/sam/aaa_zsu57.py
index 422693d5..5da161c8 100644
--- a/gen/sam/aaa_zsu57.py
+++ b/gen/sam/aaa_zsu57.py
@@ -12,7 +12,6 @@ class ZSU57Generator(AirDefenseGroupGenerator):
"""
name = "ZSU-57-2 Group"
- price = 60
def generate(self):
num_launchers = 4
diff --git a/gen/sam/aaa_zu23_insurgent.py b/gen/sam/aaa_zu23_insurgent.py
index 49113668..a91d143e 100644
--- a/gen/sam/aaa_zu23_insurgent.py
+++ b/gen/sam/aaa_zu23_insurgent.py
@@ -14,7 +14,6 @@ class ZU23InsurgentGenerator(AirDefenseGroupGenerator):
"""
name = "Zu-23 Site"
- price = 56
def generate(self):
index = 0
diff --git a/gen/sam/cold_war_flak.py b/gen/sam/cold_war_flak.py
index 6c0bdf40..4030321b 100644
--- a/gen/sam/cold_war_flak.py
+++ b/gen/sam/cold_war_flak.py
@@ -17,7 +17,6 @@ class EarlyColdWarFlakGenerator(AirDefenseGroupGenerator):
"""
name = "Early Cold War Flak Site"
- price = 74
def generate(self):
@@ -90,7 +89,6 @@ class ColdWarFlakGenerator(AirDefenseGroupGenerator):
"""
name = "Cold War Flak Site"
- price = 72
def generate(self):
diff --git a/gen/sam/freya_ewr.py b/gen/sam/freya_ewr.py
index 917767fb..fbd8dbfb 100644
--- a/gen/sam/freya_ewr.py
+++ b/gen/sam/freya_ewr.py
@@ -12,7 +12,6 @@ class FreyaGenerator(AirDefenseGroupGenerator):
"""
name = "Freya EWR Site"
- price = 60
def generate(self):
diff --git a/gen/sam/sam_avenger.py b/gen/sam/sam_avenger.py
index b3f63354..75df690f 100644
--- a/gen/sam/sam_avenger.py
+++ b/gen/sam/sam_avenger.py
@@ -14,7 +14,6 @@ class AvengerGenerator(AirDefenseGroupGenerator):
"""
name = "Avenger Group"
- price = 62
def generate(self):
num_launchers = 2
diff --git a/gen/sam/sam_chaparral.py b/gen/sam/sam_chaparral.py
index ea239746..3489fd8b 100644
--- a/gen/sam/sam_chaparral.py
+++ b/gen/sam/sam_chaparral.py
@@ -14,7 +14,6 @@ class ChaparralGenerator(AirDefenseGroupGenerator):
"""
name = "Chaparral Group"
- price = 66
def generate(self):
num_launchers = 2
diff --git a/gen/sam/sam_gepard.py b/gen/sam/sam_gepard.py
index 6128efab..cb752f90 100644
--- a/gen/sam/sam_gepard.py
+++ b/gen/sam/sam_gepard.py
@@ -14,7 +14,6 @@ class GepardGenerator(AirDefenseGroupGenerator):
"""
name = "Gepard Group"
- price = 50
def generate(self):
num_launchers = 2
diff --git a/gen/sam/sam_hawk.py b/gen/sam/sam_hawk.py
index efec60a2..007b0b99 100644
--- a/gen/sam/sam_hawk.py
+++ b/gen/sam/sam_hawk.py
@@ -16,7 +16,6 @@ class HawkGenerator(AirDefenseGroupGenerator):
"""
name = "Hawk Site"
- price = 115
def generate(self):
self.add_unit(
diff --git a/gen/sam/sam_hq7.py b/gen/sam/sam_hq7.py
index 0143fc63..8c6f5d2a 100644
--- a/gen/sam/sam_hq7.py
+++ b/gen/sam/sam_hq7.py
@@ -16,7 +16,6 @@ class HQ7Generator(AirDefenseGroupGenerator):
"""
name = "HQ-7 Site"
- price = 120
def generate(self):
self.add_unit(
diff --git a/gen/sam/sam_linebacker.py b/gen/sam/sam_linebacker.py
index 09c57117..224e09bf 100644
--- a/gen/sam/sam_linebacker.py
+++ b/gen/sam/sam_linebacker.py
@@ -14,7 +14,6 @@ class LinebackerGenerator(AirDefenseGroupGenerator):
"""
name = "Linebacker Group"
- price = 75
def generate(self):
num_launchers = 2
diff --git a/gen/sam/sam_patriot.py b/gen/sam/sam_patriot.py
index fcd82417..3fb8a995 100644
--- a/gen/sam/sam_patriot.py
+++ b/gen/sam/sam_patriot.py
@@ -14,7 +14,6 @@ class PatriotGenerator(AirDefenseGroupGenerator):
"""
name = "Patriot Battery"
- price = 240
def generate(self):
# Command Post
diff --git a/gen/sam/sam_rapier.py b/gen/sam/sam_rapier.py
index 538dd7c4..a2c1d07b 100644
--- a/gen/sam/sam_rapier.py
+++ b/gen/sam/sam_rapier.py
@@ -15,7 +15,6 @@ class RapierGenerator(AirDefenseGroupGenerator):
"""
name = "Rapier AA Site"
- price = 50
def generate(self):
self.add_unit(
diff --git a/gen/sam/sam_roland.py b/gen/sam/sam_roland.py
index 64ee154d..30d51aa5 100644
--- a/gen/sam/sam_roland.py
+++ b/gen/sam/sam_roland.py
@@ -13,7 +13,6 @@ class RolandGenerator(AirDefenseGroupGenerator):
"""
name = "Roland Site"
- price = 40
def generate(self):
num_launchers = 2
diff --git a/gen/sam/sam_sa10.py b/gen/sam/sam_sa10.py
index 8d4d4e2c..282515ab 100644
--- a/gen/sam/sam_sa10.py
+++ b/gen/sam/sam_sa10.py
@@ -17,7 +17,6 @@ class SA10Generator(AirDefenseGroupGenerator):
"""
name = "SA-10/S-300PS Battery - With ZSU-23"
- price = 550
def __init__(self, game: Game, ground_object: SamGroundObject):
super().__init__(game, ground_object)
@@ -89,7 +88,6 @@ class SA10Generator(AirDefenseGroupGenerator):
class Tier2SA10Generator(SA10Generator):
name = "SA-10/S-300PS Battery - With SA-15 PD"
- price = 650
def generate_defensive_groups(self) -> None:
# Create AAA the way the main group does.
@@ -114,7 +112,6 @@ class Tier2SA10Generator(SA10Generator):
class Tier3SA10Generator(SA10Generator):
name = "SA-10/S-300PS Battery - With SA-15 PD & SA-19 SHORAD"
- price = 750
def generate_defensive_groups(self) -> None:
# AAA for defending against close targets.
@@ -150,7 +147,6 @@ class Tier3SA10Generator(SA10Generator):
class SA10BGenerator(Tier3SA10Generator):
- price = 700
name = "SA-10B/S-300PS Battery"
def __init__(self, game: Game, ground_object: SamGroundObject):
@@ -166,7 +162,6 @@ class SA10BGenerator(Tier3SA10Generator):
class SA12Generator(Tier3SA10Generator):
- price = 750
name = "SA-12/S-300V Battery"
def __init__(self, game: Game, ground_object: SamGroundObject):
@@ -182,7 +177,6 @@ class SA12Generator(Tier3SA10Generator):
class SA20Generator(Tier3SA10Generator):
- price = 800
name = "SA-20/S-300PMU-1 Battery"
def __init__(self, game: Game, ground_object: SamGroundObject):
@@ -198,7 +192,6 @@ class SA20Generator(Tier3SA10Generator):
class SA20BGenerator(Tier3SA10Generator):
- price = 850
name = "SA-20B/S-300PMU-2 Battery"
def __init__(self, game: Game, ground_object: SamGroundObject):
@@ -214,7 +207,6 @@ class SA20BGenerator(Tier3SA10Generator):
class SA23Generator(Tier3SA10Generator):
- price = 950
name = "SA-23/S-300VM Battery"
def __init__(self, game: Game, ground_object: SamGroundObject):
diff --git a/gen/sam/sam_sa11.py b/gen/sam/sam_sa11.py
index 3611aff5..8e53e6b5 100644
--- a/gen/sam/sam_sa11.py
+++ b/gen/sam/sam_sa11.py
@@ -14,7 +14,6 @@ class SA11Generator(AirDefenseGroupGenerator):
"""
name = "SA-11 Buk Battery"
- price = 180
def generate(self):
self.add_unit(
diff --git a/gen/sam/sam_sa13.py b/gen/sam/sam_sa13.py
index 24cd75a0..d364edf3 100644
--- a/gen/sam/sam_sa13.py
+++ b/gen/sam/sam_sa13.py
@@ -14,7 +14,6 @@ class SA13Generator(AirDefenseGroupGenerator):
"""
name = "SA-13 Strela Group"
- price = 50
def generate(self):
self.add_unit(
diff --git a/gen/sam/sam_sa15.py b/gen/sam/sam_sa15.py
index d5d74b9c..ca0d4b22 100644
--- a/gen/sam/sam_sa15.py
+++ b/gen/sam/sam_sa15.py
@@ -12,7 +12,6 @@ class SA15Generator(AirDefenseGroupGenerator):
"""
name = "SA-15 Tor Group"
- price = 55
def generate(self):
num_launchers = 2
diff --git a/gen/sam/sam_sa17.py b/gen/sam/sam_sa17.py
index 093044b8..c59eb263 100644
--- a/gen/sam/sam_sa17.py
+++ b/gen/sam/sam_sa17.py
@@ -13,7 +13,6 @@ class SA17Generator(AirDefenseGroupGenerator):
"""
name = "SA-17 Grizzly Battery"
- price = 180
def generate(self):
self.add_unit(
diff --git a/gen/sam/sam_sa19.py b/gen/sam/sam_sa19.py
index 0ff23158..fb0fabe8 100644
--- a/gen/sam/sam_sa19.py
+++ b/gen/sam/sam_sa19.py
@@ -14,7 +14,6 @@ class SA19Generator(AirDefenseGroupGenerator):
"""
name = "SA-19 Tunguska Group"
- price = 90
def generate(self):
num_launchers = 2
diff --git a/gen/sam/sam_sa2.py b/gen/sam/sam_sa2.py
index 3cfae81e..70d57f2d 100644
--- a/gen/sam/sam_sa2.py
+++ b/gen/sam/sam_sa2.py
@@ -14,7 +14,6 @@ class SA2Generator(AirDefenseGroupGenerator):
"""
name = "SA-2/S-75 Site"
- price = 74
def generate(self):
self.add_unit(
diff --git a/gen/sam/sam_sa3.py b/gen/sam/sam_sa3.py
index 400bda82..802a28b6 100644
--- a/gen/sam/sam_sa3.py
+++ b/gen/sam/sam_sa3.py
@@ -14,7 +14,6 @@ class SA3Generator(AirDefenseGroupGenerator):
"""
name = "SA-3/S-125 Site"
- price = 80
def generate(self):
self.add_unit(
diff --git a/gen/sam/sam_sa6.py b/gen/sam/sam_sa6.py
index 6706f250..67975914 100644
--- a/gen/sam/sam_sa6.py
+++ b/gen/sam/sam_sa6.py
@@ -14,7 +14,6 @@ class SA6Generator(AirDefenseGroupGenerator):
"""
name = "SA-6 Kub Site"
- price = 102
def generate(self):
self.add_unit(
diff --git a/gen/sam/sam_sa8.py b/gen/sam/sam_sa8.py
index 7f3f72b3..dc6184c2 100644
--- a/gen/sam/sam_sa8.py
+++ b/gen/sam/sam_sa8.py
@@ -12,7 +12,6 @@ class SA8Generator(AirDefenseGroupGenerator):
"""
name = "SA-8 OSA Site"
- price = 55
def generate(self):
num_launchers = 2
diff --git a/gen/sam/sam_sa9.py b/gen/sam/sam_sa9.py
index ed7883b5..add7358c 100644
--- a/gen/sam/sam_sa9.py
+++ b/gen/sam/sam_sa9.py
@@ -14,7 +14,6 @@ class SA9Generator(AirDefenseGroupGenerator):
"""
name = "SA-9 Group"
- price = 40
def generate(self):
self.add_unit(
diff --git a/gen/sam/sam_vulcan.py b/gen/sam/sam_vulcan.py
index 0c869afc..ea0b8834 100644
--- a/gen/sam/sam_vulcan.py
+++ b/gen/sam/sam_vulcan.py
@@ -14,7 +14,6 @@ class VulcanGenerator(AirDefenseGroupGenerator):
"""
name = "Vulcan Group"
- price = 25
def generate(self):
num_launchers = 2
diff --git a/gen/sam/sam_zsu23.py b/gen/sam/sam_zsu23.py
index 0e638b62..8f9d0529 100644
--- a/gen/sam/sam_zsu23.py
+++ b/gen/sam/sam_zsu23.py
@@ -14,7 +14,6 @@ class ZSU23Generator(AirDefenseGroupGenerator):
"""
name = "ZSU-23 Group"
- price = 50
def generate(self):
num_launchers = 4
diff --git a/gen/sam/sam_zu23.py b/gen/sam/sam_zu23.py
index 58c55ad5..7c73da0f 100644
--- a/gen/sam/sam_zu23.py
+++ b/gen/sam/sam_zu23.py
@@ -14,7 +14,6 @@ class ZU23Generator(AirDefenseGroupGenerator):
"""
name = "ZU-23 Group"
- price = 54
def generate(self):
index = 0
diff --git a/gen/sam/sam_zu23_ural.py b/gen/sam/sam_zu23_ural.py
index 47b7ac6a..fe2f38fa 100644
--- a/gen/sam/sam_zu23_ural.py
+++ b/gen/sam/sam_zu23_ural.py
@@ -14,7 +14,6 @@ class ZU23UralGenerator(AirDefenseGroupGenerator):
"""
name = "ZU-23 Ural Group"
- price = 64
def generate(self):
num_launchers = 4
diff --git a/gen/sam/sam_zu23_ural_insurgent.py b/gen/sam/sam_zu23_ural_insurgent.py
index 0e277599..aea7c92b 100644
--- a/gen/sam/sam_zu23_ural_insurgent.py
+++ b/gen/sam/sam_zu23_ural_insurgent.py
@@ -14,7 +14,6 @@ class ZU23UralInsurgentGenerator(AirDefenseGroupGenerator):
"""
name = "ZU-23 Ural Insurgent Group"
- price = 64
def generate(self):
num_launchers = 4
From 4add8534735c75865955ec7a00a45c43bcf78cde Mon Sep 17 00:00:00 2001
From: Dan Albert
Date: Fri, 2 Jul 2021 17:17:25 -0700
Subject: [PATCH 026/167] Fix the legacy tanker.
Fixes https://github.com/dcs-liberation/dcs_liberation/issues/1379
---
changelog.md | 1 +
gen/airsupportgen.py | 4 +++-
2 files changed, 4 insertions(+), 1 deletion(-)
diff --git a/changelog.md b/changelog.md
index 9bd0a301..d85f0b7e 100644
--- a/changelog.md
+++ b/changelog.md
@@ -27,6 +27,7 @@ Saves from 4.0.0 are compatible with 4.1.0.
* **[Economy]** EWRs can now be bought and sold for the correct price and can no longer be used to generate money
* **[Flight Planning]** Fixed potential issue with angles > 360° or < 0° being generated when summing two angles.
* **[Mission Generation]** The lua data for other plugins is now generated correctly
+* **[Mission Generation]** The legacy always-available tanker option no longer prevents mission creation.
* **[UI]** Statistics window tick marks are now always integers.
# 4.0.0
diff --git a/gen/airsupportgen.py b/gen/airsupportgen.py
index 875a0e58..7ed159e2 100644
--- a/gen/airsupportgen.py
+++ b/gen/airsupportgen.py
@@ -127,7 +127,7 @@ class AirSupportConflictGenerator:
self.mission.country(self.game.player_country), tanker_unit_type
),
airport=None,
- plane_type=tanker_unit_type,
+ plane_type=tanker_unit_type.dcs_unit_type,
position=tanker_position,
altitude=alt,
race_distance=58000,
@@ -177,6 +177,8 @@ class AirSupportConflictGenerator:
tanker_unit_type.name,
freq,
tacan,
+ start_time=None,
+ end_time=None,
blue=True,
)
)
From 727facfb90dac06c3eb74b90e0296a097fae79a3 Mon Sep 17 00:00:00 2001
From: Dan Albert
Date: Fri, 2 Jul 2021 17:30:37 -0700
Subject: [PATCH 027/167] Fixup None loadouts for aircraft with no loadouts.
Aircraft that have no loadouts at all (such as the IL-78M) will have no
loadouts and thus no values in the dropdown menu. If the player toggles
the custom layout box we reset the flight's loadout to the selected
loadout, and with no loadouts in the combo box that is None, and
`Flight.loadout` isn't supposed to be optional.
Check for that case in the loadout selector and replace with an empty
loadout if that happens.
Fixes https://github.com/dcs-liberation/dcs_liberation/issues/1402
---
changelog.md | 1 +
gen/flights/loadouts.py | 4 ++++
.../mission/flight/payload/QFlightPayloadTab.py | 16 ++++++++++++++--
3 files changed, 19 insertions(+), 2 deletions(-)
diff --git a/changelog.md b/changelog.md
index d85f0b7e..06e98544 100644
--- a/changelog.md
+++ b/changelog.md
@@ -29,6 +29,7 @@ Saves from 4.0.0 are compatible with 4.1.0.
* **[Mission Generation]** The lua data for other plugins is now generated correctly
* **[Mission Generation]** The legacy always-available tanker option no longer prevents mission creation.
* **[UI]** Statistics window tick marks are now always integers.
+* **[UI]** Toggling custom loadout for an aircraft with no preset loadouts no longer breaks the flight.
# 4.0.0
diff --git a/gen/flights/loadouts.py b/gen/flights/loadouts.py
index 0a51245a..5906315e 100644
--- a/gen/flights/loadouts.py
+++ b/gen/flights/loadouts.py
@@ -133,4 +133,8 @@ class Loadout:
)
# TODO: Try group.load_task_default_loadout(loadout_for_task)
+ return cls.empty_loadout()
+
+ @classmethod
+ def empty_loadout(cls) -> Loadout:
return Loadout("Empty", {}, date=None)
diff --git a/qt_ui/windows/mission/flight/payload/QFlightPayloadTab.py b/qt_ui/windows/mission/flight/payload/QFlightPayloadTab.py
index 5cf5b370..b1eb809e 100644
--- a/qt_ui/windows/mission/flight/payload/QFlightPayloadTab.py
+++ b/qt_ui/windows/mission/flight/payload/QFlightPayloadTab.py
@@ -47,8 +47,20 @@ class QFlightPayloadTab(QFrame):
def reload_from_flight(self) -> None:
self.loadout_selector.setCurrentText(self.flight.loadout.name)
+ def loadout_at(self, index: int) -> Loadout:
+ loadout = self.loadout_selector.itemData(index)
+ if loadout is None:
+ return Loadout.empty_loadout()
+ return loadout
+
+ def current_loadout(self) -> Loadout:
+ loadout = self.loadout_selector.currentData()
+ if loadout is None:
+ return Loadout.empty_loadout()
+ return loadout
+
def on_new_loadout(self, index: int) -> None:
- self.flight.loadout = self.loadout_selector.itemData(index)
+ self.flight.loadout = self.loadout_at(index)
self.payload_editor.reset_pylons()
def on_custom_toggled(self, use_custom: bool) -> None:
@@ -56,5 +68,5 @@ class QFlightPayloadTab(QFrame):
if use_custom:
self.flight.loadout = self.flight.loadout.derive_custom("Custom")
else:
- self.flight.loadout = self.loadout_selector.currentData()
+ self.flight.loadout = self.current_loadout()
self.payload_editor.reset_pylons()
From aa328d3ef715bd86180b29d8784ae9040d51aec6 Mon Sep 17 00:00:00 2001
From: bgreman <47828384+bgreman@users.noreply.github.com>
Date: Sat, 3 Jul 2021 14:51:26 -0400
Subject: [PATCH 028/167] Adds Marianas Islands support (#1406)
* Implements #1399
* Reverting accidental change in generate_landmap.py
* Changelog update
* Import beacon data for Marianas.
Co-authored-by: Dan Albert
---
changelog.md | 1 +
game/theater/conflicttheater.py | 20 +++
game/theater/marianaislands.py | 8 ++
game/version.py | 5 +-
gen/airfields.py | 43 +++++++
qt_ui/uiconstants.py | 1 +
requirements.txt | 2 +-
resources/dcs/beacons/marianaislands.json | 135 +++++++++++++++++++++
resources/marianaislandslandmap.p | Bin 0 -> 47753 bytes
resources/marianasislands.gif | Bin 0 -> 13754 bytes
resources/tools/export_coordinates.py | 3 +
resources/tools/generate_frontlines.py | 14 ++-
resources/tools/generate_landmap.py | 2 +-
resources/tools/marianaislands_terrain.miz | Bin 0 -> 147341 bytes
resources/ui/terrain_marianas.gif | Bin 0 -> 1091 bytes
15 files changed, 229 insertions(+), 5 deletions(-)
create mode 100644 game/theater/marianaislands.py
create mode 100644 resources/dcs/beacons/marianaislands.json
create mode 100644 resources/marianaislandslandmap.p
create mode 100644 resources/marianasislands.gif
create mode 100644 resources/tools/marianaislands_terrain.miz
create mode 100644 resources/ui/terrain_marianas.gif
diff --git a/changelog.md b/changelog.md
index 06e98544..5aab0539 100644
--- a/changelog.md
+++ b/changelog.md
@@ -13,6 +13,7 @@ Saves from 4.0.0 are compatible with 4.1.0.
## Features/Improvements
* **[Campaign]** Air defense sites now generate a fixed number of launchers per type.
+* **[Campaign]** Added support for Mariana Islands map.
* **[Mission Generation]** Improvements for better support of the Skynet Plugin and long range SAMs are now acting as EWR
* **[Plugins]** Increased time JTAC Autolase messages stay visible on the UI.
* **[UI]** Added ability to take notes and have those notes appear as a kneeboard page.
diff --git a/game/theater/conflicttheater.py b/game/theater/conflicttheater.py
index 0d49b1e6..1db5cee5 100644
--- a/game/theater/conflicttheater.py
+++ b/game/theater/conflicttheater.py
@@ -29,6 +29,7 @@ from dcs.terrain import (
persiangulf,
syria,
thechannel,
+ marianaislands,
)
from dcs.terrain.terrain import Airport, Terrain
from dcs.unitgroup import (
@@ -856,3 +857,22 @@ class SyriaTheater(ConflictTheater):
from .syria import PARAMETERS
return PARAMETERS
+
+
+class MarianaIslandsTheater(ConflictTheater):
+ terrain = marianaislands.MarianaIslands()
+ overview_image = "marianaislands.gif"
+
+ landmap = load_landmap("resources\\marianaislandslandmap.p")
+ daytime_map = {
+ "dawn": (6, 8),
+ "day": (8, 16),
+ "dusk": (16, 18),
+ "night": (0, 5),
+ }
+
+ @property
+ def projection_parameters(self) -> TransverseMercator:
+ from .marianaislands import PARAMETERS
+
+ return PARAMETERS
diff --git a/game/theater/marianaislands.py b/game/theater/marianaislands.py
new file mode 100644
index 00000000..b5ddd5e1
--- /dev/null
+++ b/game/theater/marianaislands.py
@@ -0,0 +1,8 @@
+from game.theater.projections import TransverseMercator
+
+PARAMETERS = TransverseMercator(
+ central_meridian=147,
+ false_easting=238417.99999989968,
+ false_northing=-1491840.000000048,
+ scale_factor=0.9996,
+)
diff --git a/game/version.py b/game/version.py
index bcb8b8cf..c25490e0 100644
--- a/game/version.py
+++ b/game/version.py
@@ -96,4 +96,7 @@ VERSION = _build_version_string()
#: mission using map buildings as strike targets must check and potentially recreate
#: all those objectives. This definitely affects all Syria campaigns, other maps are
#: not yet verified.
-CAMPAIGN_FORMAT_VERSION = (7, 0)
+#:
+#: Version 7.1
+#: * Support for Mariana Islands terrain
+CAMPAIGN_FORMAT_VERSION = (7, 1)
diff --git a/gen/airfields.py b/gen/airfields.py
index 7d499cf1..7998cbf4 100644
--- a/gen/airfields.py
+++ b/gen/airfields.py
@@ -1521,4 +1521,47 @@ AIRFIELD_DATA = {
runway_length=3953,
atc=AtcData(MHz(3, 850), MHz(118, 200), MHz(38, 600), MHz(250, 200)),
),
+ "Antonio B. Won Pat Intl": AirfieldData(
+ theater="MarianaIslands",
+ icao="PGUM",
+ elevation=255,
+ runway_length=9359,
+ atc=AtcData(MHz(3, 825), MHz(118, 100), MHz(38, 550), MHz(340, 200)),
+ ils={
+ "06": ("IGUM", MHz(110, 30)),
+ },
+ ),
+ "Andersen AFB": AirfieldData(
+ theater="MarianaIslands",
+ icao="PGUA",
+ elevation=545,
+ runway_length=10490,
+ tacan=TacanChannel(54, TacanBand.X),
+ tacan_callsign="UAM",
+ atc=AtcData(MHz(3, 850), MHz(126, 200), MHz(38, 600), MHz(250, 100)),
+ ),
+ "Rota Intl": AirfieldData(
+ theater="MarianaIslands",
+ icao="PGRO",
+ elevation=568,
+ runway_length=6105,
+ atc=AtcData(MHz(3, 750), MHz(123, 600), MHz(38, 400), MHz(250, 0)),
+ ),
+ "Tinian Intl": AirfieldData(
+ theater="MarianaIslands",
+ icao="PGWT",
+ elevation=240,
+ runway_length=7777,
+ atc=AtcData(MHz(3, 800), MHz(123, 650), MHz(38, 500), MHz(250, 50)),
+ ),
+ "Saipan Intl": AirfieldData(
+ theater="MarianaIslands",
+ icao="PGSN",
+ elevation=213,
+ runway_length=7790,
+ atc=AtcData(MHz(3, 775), MHz(125, 700), MHz(38, 450), MHz(256, 900)),
+ ils={
+ "07": ("IGSN", MHz(109, 90)),
+ },
+ ),
}
diff --git a/qt_ui/uiconstants.py b/qt_ui/uiconstants.py
index e36ffb7e..ad4f26e4 100644
--- a/qt_ui/uiconstants.py
+++ b/qt_ui/uiconstants.py
@@ -68,6 +68,7 @@ def load_icons():
ICONS["Terrain_Normandy"] = QPixmap("./resources/ui/terrain_normandy.gif")
ICONS["Terrain_TheChannel"] = QPixmap("./resources/ui/terrain_channel.gif")
ICONS["Terrain_Syria"] = QPixmap("./resources/ui/terrain_syria.gif")
+ ICONS["Terrain_Mariana"] = QPixmap("./resources/ui/terrain_mariana.gif")
ICONS["Dawn"] = QPixmap("./resources/ui/conditions/timeofday/dawn.png")
ICONS["Day"] = QPixmap("./resources/ui/conditions/timeofday/day.png")
diff --git a/requirements.txt b/requirements.txt
index 75bf846a..eeed5fc3 100644
--- a/requirements.txt
+++ b/requirements.txt
@@ -19,7 +19,7 @@ pathspec==0.8.1
pefile==2019.4.18
Pillow==8.2.0
pre-commit==2.10.1
--e git://github.com/pydcs/dcs@7dea4f516d943c1f48454a46043b5f38d42a35f0#egg=pydcs
+-e git://github.com/pydcs/dcs@75a8dd35331e8fd337ba05fe950732077433f378#egg=pydcs
pyinstaller==4.3
pyinstaller-hooks-contrib==2021.1
pyparsing==2.4.7
diff --git a/resources/dcs/beacons/marianaislands.json b/resources/dcs/beacons/marianaislands.json
new file mode 100644
index 00000000..da67641b
--- /dev/null
+++ b/resources/dcs/beacons/marianaislands.json
@@ -0,0 +1,135 @@
+[
+ {
+ "name": "MTMACAJNA",
+ "callsign": "AJA",
+ "beacon_type": 9,
+ "hertz": 385000,
+ "channel": null
+ },
+ {
+ "name": "Nimitz",
+ "callsign": "UNZ",
+ "beacon_type": 6,
+ "hertz": 115800000,
+ "channel": 105
+ },
+ {
+ "name": "SAIPAN",
+ "callsign": "SN",
+ "beacon_type": 9,
+ "hertz": 312000,
+ "channel": null
+ },
+ {
+ "name": "ANDERSEN",
+ "callsign": "UAM",
+ "beacon_type": 5,
+ "hertz": null,
+ "channel": 54
+ },
+ {
+ "name": "",
+ "callsign": "IPMY",
+ "beacon_type": 15,
+ "hertz": 110150000,
+ "channel": null
+ },
+ {
+ "name": "",
+ "callsign": "IUAM",
+ "beacon_type": 15,
+ "hertz": 110100000,
+ "channel": null
+ },
+ {
+ "name": "",
+ "callsign": "IYIG",
+ "beacon_type": 15,
+ "hertz": 109350000,
+ "channel": null
+ },
+ {
+ "name": "",
+ "callsign": "IAND",
+ "beacon_type": 15,
+ "hertz": 109300000,
+ "channel": null
+ },
+ {
+ "name": "",
+ "callsign": "IUAM",
+ "beacon_type": 14,
+ "hertz": 110100000,
+ "channel": null
+ },
+ {
+ "name": "",
+ "callsign": "IAND",
+ "beacon_type": 14,
+ "hertz": 109300000,
+ "channel": null
+ },
+ {
+ "name": "",
+ "callsign": "IYIG",
+ "beacon_type": 14,
+ "hertz": 109350000,
+ "channel": null
+ },
+ {
+ "name": "",
+ "callsign": "IPMY",
+ "beacon_type": 14,
+ "hertz": 110150000,
+ "channel": null
+ },
+ {
+ "name": "",
+ "callsign": "IGUM",
+ "beacon_type": 14,
+ "hertz": 110300000,
+ "channel": null
+ },
+ {
+ "name": "",
+ "callsign": "PGUM",
+ "beacon_type": 15,
+ "hertz": 110300000,
+ "channel": null
+ },
+ {
+ "name": "",
+ "callsign": "IAWD",
+ "beacon_type": 14,
+ "hertz": 110900000,
+ "channel": null
+ },
+ {
+ "name": "",
+ "callsign": "PGUM",
+ "beacon_type": 15,
+ "hertz": 110900000,
+ "channel": null
+ },
+ {
+ "name": "ROTA",
+ "callsign": "GRO",
+ "beacon_type": 9,
+ "hertz": 332000,
+ "channel": null
+ },
+ {
+ "name": "",
+ "callsign": "IGSN",
+ "beacon_type": 14,
+ "hertz": 109900000,
+ "channel": null
+ },
+ {
+ "name": "",
+ "callsign": "PGSN",
+ "beacon_type": 15,
+ "hertz": 109900000,
+ "channel": null
+ }
+]
\ No newline at end of file
diff --git a/resources/marianaislandslandmap.p b/resources/marianaislandslandmap.p
new file mode 100644
index 0000000000000000000000000000000000000000..d11e03684b2693e4a41d5734cbad7d15065a220c
GIT binary patch
literal 47753
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