Compare commits

...

2126 Commits

Author SHA1 Message Date
Dan Albert
34aca41461 More Python 3.9 compat.
(cherry picked from commit 1c31cffe4b)
2021-05-16 20:47:35 -07:00
Dan Albert
7d83131173 Python 3.9 compatibility.
This argument was removed and wasn't needed anyway since the file itself
is already opened UTF-8.

(cherry picked from commit b9822cd5d1)
2021-05-16 20:47:33 -07:00
Dan Albert
4555a4968d Update changelog for 2.5.1. 2021-05-02 13:17:35 -07:00
SnappyComebacks
ae34e4749b Move base EWRs into their own category.
Without this we're sometimes spawning base EWRs at points far outside the base perimeter.
2021-04-28 21:07:22 -07:00
Khopa
635eee9590 Fixed ai_flight_planner for maps lacking frontlines (such as battle of britain on The Channel map) 2021-04-24 00:11:53 +02:00
Dan Albert
637ca8fbca Stop projecting threat zones from front lines.
This is an interim improvement since we should probably be pushing the
BARCAPs into TARCAP roles when the front line is so close. This does
regress flight pathing for anything that should route around the front
(to avoid getting shot at by SHORADS and TARCAPs), but for now it's one
or the other and this is the one everyone's complaining about.

(cherry picked from commit e474748f4d)
2021-04-22 18:23:12 -07:00
Dan Albert
e4e65df976 Generalize commit range display for all patrols.
Fixes https://github.com/Khopa/dcs_liberation/issues/890

(cherry picked from commit 132ba905c7)
2021-04-22 17:55:49 -07:00
Dan Albert
29579a2aec Remove missed merge conflict marker. 2021-04-22 17:49:34 -07:00
Dan Albert
e32b43cffb Show BARCAP commit ranges by default.
BARCAP placement confuses a lot of people but this should make it more
clear.

(cherry picked from commit 208d1b82b5)
2021-04-22 17:46:29 -07:00
Khopa
b27a7fc71b Fixed Lint issue 2021-04-21 22:54:48 +02:00
Khopa
5861ce6146 Fixed error with Ramat David frequency (typo) 2021-04-21 22:38:08 +02:00
Khopa
c732ed556f Fixed airfields frequency on Persian Gulf 2021-04-21 22:30:08 +02:00
Khopa
be1a75e520 Fixed airfields frequency on Syria 2021-04-21 22:14:18 +02:00
Khopa
c41d10c581 Pydcs update to latest version 2021-04-21 12:57:19 +02:00
Dan Albert
157a59e3c4 Fix UI crash when unchecking default loadout.
This was throwing because it was being called with the wrong number of
arguments, preventing the UI from actually updating back to the default.
2021-04-18 13:05:22 -07:00
Khopa
d24c65c3aa Fixed airfield data airport name for Persian Gulf map 2021-04-18 20:10:52 +02:00
Khopa
d4d441ff9b Fixed some factions errors that weren't caught yet. 2021-04-18 18:11:00 +02:00
Khopa
f43fb1223f Fix : Fixed duplicate units on cold war flak site. 2021-04-18 15:16:17 +02:00
Khopa
3db275414d Allow 0 income multiplier in game settings windows (this was already possible in new game wizard) 2021-04-18 01:20:32 +02:00
Khopa
6e0ff6c805 pydcs update 2021-04-18 01:13:50 +02:00
Dan Albert
9c359efbff Note Litening -> ATFLIR change. 2021-04-17 16:03:56 -07:00
Dan Albert
c5cc1ea8e8 Make the F/A-18C strike loadout less silly.
Instead of 4xMk83 and 4xGBU-38, 2 bags and 2 GBU-31. ATFLIR added for
TOO/BDA.
2021-04-17 15:51:46 -07:00
Dan Albert
afb6a33131 Replace Litening II with ATFLIR in Honet loadouts.
https://github.com/pydcs/dcs/pull/120
2021-04-17 15:43:49 -07:00
Khopa
539a11f54d Added icons for new units 2021-04-18 00:15:10 +02:00
Khopa
9324e549e6 Updated changelog 2021-04-18 00:13:43 +02:00
Khopa
c8f6b6df87 Fixed lint issue 2021-04-18 00:11:06 +02:00
Dan Albert
38f632097e Add support for DCS 2.7 weather generation.
https://github.com/Khopa/dcs_liberation/issues/981
2021-04-17 15:06:17 -07:00
Khopa
e63743f537 Improved FOB support : new custom banner for FOB menu and do not display aircrafts menu on first page. 2021-04-17 23:49:49 +02:00
Khopa
ce13295cf0 pydcs repo now pointing on temporary branch 2-7-temp on https://github.com/Khopa/dcs for new weather development 2021-04-17 23:06:48 +02:00
Khopa
23c02a3510 Updated airfields data for the Channel map 2021-04-17 17:50:41 +02:00
Khopa
01ea7b9ee1 Updated airfields metadata for Syria 2021-04-17 17:37:15 +02:00
Khopa
6fed1284a1 Updated airfields metadata for Syria 2021-04-17 17:35:40 +02:00
Khopa
5574d849bd Unit support : S-60 added to Syria faction 2021-04-17 13:11:58 +02:00
Khopa
c2ce3a6992 Fixed Lint issue 2021-04-17 13:11:26 +02:00
Khopa
b61d15fdf4 Unit support : Added support for the PLZ-05, new artillery unit from the Chinese Asset Pack 2021-04-17 11:28:36 +02:00
Khopa
ad5cc83fb3 Unit support : now using the new unit S-60 57mm AA Gun units. 2021-04-17 11:23:00 +02:00
Ronny Röhricht
2f53edd775 Add plugin for exporting RED and BLUE threat circles to LotATC.
Implemented as a plugin because LotATC needs actual lat/lon, and the only APIs for those are in lua.

Fixes https://github.com/Khopa/dcs_liberation/issues/956.
2021-04-17 00:55:06 -07:00
Khopa
923459c88b Pydcs update to the good commit reference 2021-04-17 02:35:34 +02:00
Khopa
1192d26448 Fixed lint issue 2021-04-17 02:27:42 +02:00
Khopa
2d5e827417 Pydcs update to master repo 2021-04-17 02:26:31 +02:00
Khopa
a30d9276b8 Merge remote-tracking branch 'khopa/develop' into develop 2021-04-17 02:22:56 +02:00
Khopa
b963c2272f More naming fixes 2021-04-17 02:21:19 +02:00
Khopa
221cb8709b Ran black formatter 2021-04-17 02:15:49 +02:00
Khopa
648857fc44 Removed deprecated faction 2021-04-17 02:15:02 +02:00
Khopa
8091051bb4 Fixed weapons names in pdcs extensions, removed deprecated rafale mod, fixed many other compilation issues with pydcs 2.7+ 2021-04-17 02:13:52 +02:00
Khopa
1e468cd3e0 Fixed weapons fallback db names with new pydcs version 2021-04-17 01:23:08 +02:00
Khopa
15d2a5bb2b Updated units name in liberation 2021-04-16 23:33:22 +02:00
Khopa
5c76229ee5 Referencing pydcs new version 2021-04-16 23:31:03 +02:00
Dan Albert
0cd088122e Remove WIP status of AEW&C missions. 2021-04-15 21:35:25 -07:00
Dan Albert
b6f3467a89 Update changelog. 2021-04-15 21:34:31 -07:00
SnappyComebacks
52ce1a5959 Add support for additional EWR sites in campaigns.
* A Bluefor EWR 55GS in the campaign miz defines an optional EWR site. There is no distinction between how close or far it is to a base, so it's possible that there will be many EWRs within an airbase.
* A Redfor EWR 1L13 in the campaign miz defines a required EWR site.

It would be a good future idea to limit the amount of EWRs within a certain distance from an airbase. That way there's no chance of 5 EWRs all at the same airbase. Even better if there were something preventing any two EWRs from being right next to each other.

No campaigns take advantage of this yet.

Fixes https://github.com/Khopa/dcs_liberation/issues/524
2021-04-15 21:23:27 -07:00
Khopa
7ce05762f5 Possible to add additional helipad to any control point in campaign file. (WIP) 2021-04-14 00:00:25 +02:00
Dan Albert
cce736bc16 Note the font crash fix in the changelog. 2021-04-11 13:27:39 -07:00
Hanninho
2a1127e637 Force the basic layout engine when generating the kneeboard.
The libraqm backed layout engine causes crashes on some machines.

Fixes #531.
2021-04-11 13:24:31 -07:00
Dan Albert
8ca68b3d7a Set win/loss status for functioning airfields.
"Fixes" https://github.com/Khopa/dcs_liberation/issues/833. The crash is
still present, but we're at least telling the player that the game is
over so they shouldn't try to play. The UX for this sucks
(https://github.com/Khopa/dcs_liberation/issues/978), but it's the same
as other end-game states.
2021-04-10 15:44:57 -07:00
Dan Albert
0f76d893b8 Note date fix in the changelog. 2021-04-10 15:22:27 -07:00
Dan Albert
ab746b5195 Fix date given to the conditions generator.
`Game.date` is actually the start date, not the current date. Not renaming to
avoid breaking save compat.

This fix won't have any effect on existing saves until they pass the turn
because this is encoded into the conditions generated at the start of the turn,
but it will fix on the next turn.

Fixes https://github.com/Khopa/dcs_liberation/issues/973
2021-04-10 15:20:54 -07:00
Khopa
828c87df39 Game settings / new game wizard : Allowed a 0% income multiplier. 2021-04-07 19:33:23 +02:00
C. Perreau
888aeb621d Merge pull request #955 from Hornet2041/Integrate-splash-damage-script-plugin
Integrate splash damage plugin script
2021-04-05 20:33:17 +02:00
Khopa
ac2fddf87e Changelog update 2021-03-31 00:28:52 +02:00
Khopa
614304cc81 Added F86 Sabre loadout by Starfire. 2021-03-31 00:20:19 +02:00
Khopa
f363d66aac F_86F Sabre payloads can now be customized. 2021-03-31 00:17:21 +02:00
Khopa
1706c42695 Fix : Added Mig-19P to CAS capable aircraft list 2021-03-31 00:00:44 +02:00
Khopa
2b44b2fc0b Ran formatter to fix lint issue 2021-03-29 23:53:09 +02:00
C. Perreau
25986aa15c Merge pull request #928 from Mustang-25/patch-1
Add GAR-8 restriction date and fallback info
2021-03-29 23:51:21 +02:00
C. Perreau
264eb01afc Merge pull request #947 from SnappyComebacks/add-e2c-to-more-factions
Add E-2C to more factions
2021-03-29 23:49:27 +02:00
Khopa
6db3c3f9f1 Updated changelog 2021-03-29 23:46:44 +02:00
SnappyComebacks
714992bdcb ARMADILLLO to ARMADILLO. 2021-03-27 13:02:16 -07:00
SnappyComebacks
ca7a86b6d7 Merge branch 'develop' into add-e2c-to-more-factions 2021-03-27 11:04:13 -06:00
GvonH
49e729e9ec Add dark kneeboard option for night missions (#951) 2021-03-22 19:41:54 -07:00
Hornet2041
7f0a690c7b Add files via upload
referencing the original upload in Discord here: https://discord.com/channels/595702951800995872/768226890158702654/809571449979142174

and wheelyjoe's github repository here: https://github.com/wheelyjoe/DCS-Scripts

"Improves splash damage modelling by pulling weapon warhead info (where available) and using this to create explosions (only way to apply damage) to units in more sensible range." Makes using non-precision weaponry actually viable for ground targets.
2021-03-22 09:43:42 -04:00
C. Perreau
d07afc603b Merge pull request #950 from Khopa/dependabot/pip/pillow-8.1.1
Bump pillow from 7.2.0 to 8.1.1
2021-03-21 19:09:21 +01:00
Khopa
5bd4c00257 Merge branch 'develop_2_4_x' into develop
# Conflicts:
#	changelog.md
#	game/db.py
#	game/navmesh.py
#	game/operation/operation.py
#	game/theater/conflicttheater.py
#	game/theater/controlpoint.py
#	game/theater/start_generator.py
#	game/theater/theatergroundobject.py
#	game/threatzones.py
#	game/version.py
#	gen/aircraft.py
#	gen/airsupportgen.py
#	gen/fleet/carrier_group.py
#	gen/flights/ai_flight_planner.py
#	gen/flights/ai_flight_planner_db.py
#	gen/flights/flightplan.py
#	gen/flights/waypointbuilder.py
#	gen/groundobjectsgen.py
#	gen/kneeboard.py
#	pydcs
#	pydcs_extensions/f22a/f22a.py
#	qt_ui/uiconstants.py
#	qt_ui/widgets/combos/QAircraftTypeSelector.py
#	qt_ui/widgets/map/QLiberationMap.py
#	qt_ui/windows/QUnitInfoWindow.py
#	qt_ui/windows/mission/flight/payload/QPylonEditor.py
#	qt_ui/windows/settings/QSettingsWindow.py
2021-03-21 18:50:50 +01:00
dependabot[bot]
13272aa280 Bump pillow from 7.2.0 to 8.1.1
Bumps [pillow](https://github.com/python-pillow/Pillow) from 7.2.0 to 8.1.1.
- [Release notes](https://github.com/python-pillow/Pillow/releases)
- [Changelog](https://github.com/python-pillow/Pillow/blob/master/CHANGES.rst)
- [Commits](https://github.com/python-pillow/Pillow/compare/7.2.0...8.1.1)

Signed-off-by: dependabot[bot] <support@github.com>
2021-03-19 15:06:06 +00:00
Simon Krüger
260358c5fb AEW&C kneeboard + actually do AEW&C (#922)
* AEW&C will now do AEW&C
* AEW&C gets a frequency
* AEW&C is added to kneeboard (Frequency, Depature, Depature Time, Arrival Time)
2021-03-13 14:07:19 -08:00
Dan Albert
0f07b2c095 Increase size of navmesh to avoid planning issues.
The tradeoff is that any flights that might have previously routed
_around_ a threat near the edge of the map may no longer do so as the
zones at the edge are significantly larger now.

Fixes https://github.com/Khopa/dcs_liberation/issues/903
2021-03-13 13:41:57 -08:00
Dan Albert
b2fafc22dd Fix flight path debugging. 2021-03-13 13:34:49 -08:00
Dan Albert
6200ec8e0e Don't plan BAI targets at dead subgroups.
Fixes https://github.com/Khopa/dcs_liberation/issues/900
2021-03-13 13:16:10 -08:00
Dan Albert
831516c5f5 Ask for state.json in the bug template. 2021-03-13 12:56:33 -08:00
Dan Albert
fb49d9e6ae Project threat zone from front lines.
Fixes https://github.com/Khopa/dcs_liberation/issues/940
2021-03-13 12:53:10 -08:00
SnappyComebacks
e660726828 Added E-2C elements for UI. 2021-03-12 14:19:50 -07:00
SnappyComebacks
4519f47b19 Added US Aggressors. 2021-03-12 11:16:27 -07:00
SnappyComebacks
47174bbb4d Added E-2C to USA, France, Bluefor. 2021-03-12 11:12:29 -07:00
Mustang-25
de18ce3e7b Add GAR-8 restriction date and fallback info 2021-02-27 15:22:26 -08:00
Dan Albert
b5934633fa Improve wording of "never delay players" option. 2021-02-27 14:16:41 -08:00
Khopa
f314c08216 Improved map scale display 2021-02-27 00:29:23 +01:00
Khopa
6704cded2d Fixed unit info windows crashing when banner not found (variable referenced before assignment error) 2021-02-27 00:10:57 +01:00
Khopa
f11918fc41 Fixed F-22A invalid radio frequency issues for player flights (F-22 mod only allow 100-156Mhz frequency range)
Added F-22A icon and banner.
2021-02-27 00:10:06 +01:00
Khopa
58d5aa9944 Fixes : Missing weapons names would cause flight edition window to crash while setting up default loadout UI, preventing the user from editing flights. 2021-02-26 23:30:23 +01:00
Khopa
60af2ad0a4 Preparing 2.4.4 2021-02-26 23:12:59 +01:00
Dan Albert
4ec8994c38 Avoid warning in GitHub workflow. 2021-02-25 17:32:33 -08:00
Simon Krüger
49d6cece50 Merge pull request #896 from siKruger/aewc_no_threat_zone
if aewc is in threat zone move it further away
2021-02-24 22:14:22 +01:00
sikruger
03251d5bd5 some improvements 2021-02-24 22:06:42 +01:00
BenBenBeartrax
a6e03184bc fictional Korea factions 2021-02-22 16:52:53 -08:00
Khopa
54f2a6f1b5 Release 2.4.3 2021-02-22 21:58:56 +01:00
Khopa
7eed7ae6ba Release 2.4.3 2021-02-22 21:44:11 +01:00
C. Perreau
b3d642fdf5 Merge pull request #913 from Khopa/develop_2_4_x
Release 2.4.3    (fixed)
2021-02-22 21:41:48 +01:00
Khopa
35dd9427a5 Added possibility to set up custom date in new game wizard. 2021-02-22 21:34:14 +01:00
C. Perreau
bf1087df3c Merge pull request #912 from Khopa/develop_2_4_x
2.4.3 Release
2021-02-22 21:04:23 +01:00
Khopa
523ef08697 Ran black reformater on 2.4x branch 2021-02-22 20:55:51 +01:00
Khopa
c2310453d8 Updated version String to 2.4.3 2021-02-22 20:51:53 +01:00
Khopa
b4a6a5dc26 Changelog update for 4.2.3 2021-02-22 20:51:24 +01:00
Khopa
20ceb440fa black 2021-02-22 20:28:54 +01:00
Khopa
4ee9c66524 Updated Hercules cargo script to latest version. 2021-02-22 20:28:52 +01:00
Khopa
9d04abd3af Updated C130J Hercules pydcs data 2021-02-22 20:28:51 +01:00
Khopa
a562345876 Updated credits in about dialog. 2021-02-22 20:28:34 +01:00
Dan Albert
2e7daceb3c Increase file log level to debug.
Fixes https://github.com/Khopa/dcs_liberation/issues/907
2021-02-22 20:27:57 +01:00
Khopa
a9aba3484a F-22 default loadout replaced weapons in internal bay by their "no-drag" versions 2021-02-22 20:24:27 +01:00
Khopa
1b9bbb8eb6 Updated F22 mod 2021-02-22 20:24:19 +01:00
BenBenBeartrax
25c44723a9 customized_payloads: M-2000C add Eclair counter meassures pod to all loadouts 2021-02-22 20:18:24 +01:00
Khopa
688a20c312 Changelog update 2021-02-21 23:01:05 +01:00
Khopa
4c1b34461e Merge remote-tracking branch 'khopa/develop' into develop 2021-02-21 22:57:32 +01:00
Khopa
c6939e7194 Added possibility to set up custom date in new game wizard. 2021-02-21 22:57:21 +01:00
BenBenBeartrax
cd9432e395 kneeboard: custom flight name in title (#911)
If a flight has a custom flight name set it gets appended to the title in the kneeboard.
Partially addresses #862 .
2021-02-21 13:29:31 -08:00
Khopa
7d5244a5bc black 2021-02-21 17:47:51 +01:00
Khopa
1aa5d4f7de Updated Hercules cargo script to latest version. 2021-02-21 17:33:34 +01:00
Khopa
ad74204fe4 Updated C130J Hercules pydcs data 2021-02-21 17:31:58 +01:00
Khopa
5cb1a47ed3 Updated credits in about dialog. 2021-02-21 17:24:15 +01:00
Khopa
fff22d3cd1 F-22 default loadout replaced weapons in internal bay by their "no-drag" versions 2021-02-21 17:20:51 +01:00
Khopa
665cd7b996 Merge remote-tracking branch 'khopa/develop' into develop 2021-02-21 17:15:54 +01:00
Khopa
61173196d2 Updated F22 mod 2021-02-21 17:15:29 +01:00
Dan Albert
03e98ba562 Increase file log level to debug.
Fixes https://github.com/Khopa/dcs_liberation/issues/907
2021-02-20 15:38:52 -08:00
BenBenBeartrax
6195290adf customized_payloads: M-2000C add Eclair counter meassures pod to all loadouts 2021-02-20 12:49:08 -08:00
sikruger
4f1b0055e1 new point generation 2021-02-19 20:40:58 +01:00
sikruger
dd9fe87ff4 new point generation 2021-02-18 16:08:23 +01:00
sikruger
5bda4abfce if aewc is in threat zone move it further away 2021-02-17 16:47:24 +01:00
Dan Albert
24f98aede5 Undo unintentional change.
Not sure how the extra whitespace got there, but this gets overwritten
on every launch.

(cherry picked from commit 2ffe3bf722)
2021-02-13 13:09:47 -08:00
Dan Albert
f8ae1e9076 Merge pull request #885 from DanAlbert/black
Set up black.
2021-02-12 20:21:41 -08:00
Dan Albert
16b0dcad71 Add black workflow. 2021-02-12 20:13:47 -08:00
Dan Albert
9c1265d50d Add pre-commit configuration for black.
To set up, run `pre-commit install`.
2021-02-12 20:11:41 -08:00
Dan Albert
8c0e781c94 Ignore reformating in blame. 2021-02-12 20:11:36 -08:00
Dan Albert
a47bef1f13 Blacken. 2021-02-12 20:10:45 -08:00
Dan Albert
053663bd76 Merge branch 'develop_2_4_x' into develop 2021-02-12 19:23:13 -08:00
Dan Albert
e40b916b07 Merge pull request #884 from Khopa/develop_2_4_x
Release 2.4.2.
2021-02-12 19:05:44 -08:00
Dan Albert
2ffe3bf722 Undo unintentional change.
Not sure how the extra whitespace got there, but this gets overwritten
on every launch.
2021-02-12 18:59:48 -08:00
Dan Albert
1bc994c102 Fix version number of the release. 2021-02-12 16:18:01 -08:00
Dan Albert
21be4d38e1 Mention new start dates in the changelog.
(cherry picked from commit f7889b785d)
2021-02-12 16:16:36 -08:00
Mustang-25
3b58c571b3 Add a mid-90s campaign date option
1995 is a good date to pick if you want to date restrict all GPS weapons but still have all the laser guided options.

(cherry picked from commit 27829a024a)
2021-02-12 16:16:35 -08:00
Mustang-25
e0430cf607 Adjust HARM Weapon Restriction Date
Official Navy docs have the 88A's IOC date in 1983. Also left a note on the B and C IOC dates if DCS ever adds the older models.

(cherry picked from commit a0fda2552f)
2021-02-12 16:16:31 -08:00
Dan Albert
f7889b785d Mention new start dates in the changelog. 2021-02-12 16:16:13 -08:00
Mustang-25
27829a024a Add a mid-90s campaign date option
1995 is a good date to pick if you want to date restrict all GPS weapons but still have all the laser guided options.
2021-02-12 16:15:20 -08:00
Mustang-25
a0fda2552f Adjust HARM Weapon Restriction Date
Official Navy docs have the 88A's IOC date in 1983. Also left a note on the B and C IOC dates if DCS ever adds the older models.
2021-02-12 16:14:54 -08:00
Simon Krüger
65c185ebd2 Add an option for disabling the legacy AEW&C aircraft.
Using the legacy AEW&C aircraft is still the default until
https://github.com/Khopa/dcs_liberation/issues/844 is fixed.
2021-02-12 14:20:26 -08:00
Dan Albert
fb425d3524 Fix rounding of budget in recruitment menu.
Fixes https://github.com/Khopa/dcs_liberation/issues/861.

(cherry picked from commit 5792eb354c)
2021-02-12 14:01:34 -08:00
Dan Albert
5792eb354c Fix rounding of budget in recruitment menu.
Fixes https://github.com/Khopa/dcs_liberation/issues/861.
2021-02-12 14:00:58 -08:00
Dan Albert
45300b64c5 Mention weapon data in changelog.
(cherry picked from commit dce7d91511)
2021-02-12 13:54:51 -08:00
Dan Albert
dce7d91511 Mention weapon data in changelog. 2021-02-12 13:54:30 -08:00
Brandon Danyluk
4b7ef46f82 Add weapon era restrictions for USA/Russia/UK/France (#860)
(cherry picked from commit 61f1e11a48)
2021-02-12 13:49:40 -08:00
Dan Albert
b67e6d20f1 Move 2.4.2 fix to the correct section.
(cherry picked from commit 3d1afa74d4)
2021-02-12 13:48:02 -08:00
Dan Albert
3d1afa74d4 Move 2.4.2 fix to the correct section. 2021-02-12 13:47:14 -08:00
Dan Albert
0ae6575087 Transfer pending purchases forward along capture.
Fixes https://github.com/Khopa/dcs_liberation/issues/828.

(cherry picked from commit d8c94f5ece)
2021-02-12 13:46:08 -08:00
Dan Albert
d8c94f5ece Transfer pending purchases forward along capture.
Fixes https://github.com/Khopa/dcs_liberation/issues/828.
2021-02-12 13:45:01 -08:00
Brandon Danyluk
61f1e11a48 Add weapon era restrictions for USA/Russia/UK/France (#860) 2021-02-10 22:30:56 -08:00
Simon Krüger
98249b1aca Bugfix: Blue AEW&C above Red CV (#872) 2021-02-10 11:49:49 -08:00
Simon Krüger
8e51b7fc1d Carrier strike group (#863)
Generate a Carrier Group which comes close the the real Carrier Strike Group 8.

Under carrier_names in the faction simply add "Carrier Strike Group 8" as the first and only entry and enable super carrier.

* TRU as TACAN name
* Harry S. Truman CV
* 4x Arleigh Burke
* 1x Ticonderoga
* CV in the middle, Ticonderoga in a radius of 2 miles, Arleigh Burkes forming a rectangle
2021-02-09 14:17:46 -08:00
Simon Krüger
71914b8a8b Aew&c ai planning.
AI will generate AWE&C

* Only one flight per turn
* Takes the airfield farthest away from the frontline
* Prefers CV over any airfield
2021-02-09 12:35:47 -08:00
Khopa
7a077a0d21 Merge remote-tracking branch 'khopa/develop' into develop 2021-02-07 21:16:04 +01:00
Khopa
d23e4665e7 Fixed possible cheat by selling SA-10 SAMs site for more money than what they were bought for.
+ Fixed SA-10 sites having the same name in game UI.
2021-02-07 21:15:48 +01:00
C. Perreau
df12b54856 Merge pull request #858 from benedikt-wegmann/develop_kneeboard_briefing_cleanup
kneeboard: slight cleanup in Jinja
2021-02-07 18:48:47 +01:00
Khopa
ae12053d74 Pydcs update 2021-02-07 18:34:48 +01:00
Khopa
0e7695f2d6 pydcs update repo 2021-02-07 17:49:01 +01:00
BenBenBeartrax
38cee87ee9 kneeboard: slight cleanup in Jinja by not rendering sections that are empty anyway (JTAC, Carriers, AWACS etc.) 2021-02-07 16:44:41 +01:00
Simon Clark
c64a6083e2 Merge branch 'develop' of https://github.com/Khopa/dcs_liberation into develop 2021-02-07 11:41:04 +00:00
Simon Krüger
e0501e46e3 Initial implementation of AEW&C missions.
Still a work in progress (the missions don't actually perform their task, just orbit). Currently:

* AEW&C aircraft can be bought.
* AEW&C missions can be planned at any control point and at front lines.
* AEW&C will return after 4H or Bingo.
2021-02-07 11:39:22 +00:00
Khopa
4a0ccc4c2f Fixed error detected by mypy 2021-02-07 11:39:21 +00:00
Khopa
c92b7240eb Increased number of launchers on Silkworms sites 2021-02-07 11:39:21 +00:00
Khopa
6a74c3faeb Added coastal defenses sites generator for Iran and China. 2021-02-07 11:39:21 +00:00
BenBenBeartrax
c5ae872787 waypointbuilder: low altitude AGL for helos 2021-02-07 11:39:21 +00:00
Dan Albert
2e9ab0a9d7 Move develop to 2.5. 2021-02-07 11:39:20 +00:00
Malakhit
08f67860be Merge pull request #854 from Khopa/develop_2_4_x
2.4.1 Release
2021-02-07 11:22:38 +00:00
Simon Clark
3fab1d92b7 Fixed some areas where the non-pretty name for a unit was displayed. 2021-02-07 11:15:25 +00:00
Simon Clark
6573157112 Changelog. 2021-02-07 10:49:34 +00:00
Simon Clark
e83841eb0b Fix syntax error with SH-60B payload. 2021-02-07 10:47:36 +00:00
Simon Clark
6a0e18c0e9 Change the logic for culling missile sites.
Missile sites now generate a 2.5km culling circle around themselves, rather than using the standard full culling zone size.

Fixes #850.
2021-02-06 22:20:02 +00:00
Simon Clark
b71b6473e3 Start 2.4.1, fix #852. 2021-02-06 19:56:17 +00:00
Simon Krüger
a004f62fe8 Initial implementation of AEW&C missions.
Still a work in progress (the missions don't actually perform their task, just orbit). Currently:

* AEW&C aircraft can be bought.
* AEW&C missions can be planned at any control point and at front lines.
* AEW&C will return after 4H or Bingo.
2021-02-05 17:18:50 -08:00
Malakhit
c69e5e05a3 Merge pull request #841 from Khopa/develop_2_4_x
2.4.0 Release
2021-02-05 18:46:23 +00:00
Khopa
07ce20fd29 Fixed error detected by mypy 2021-02-05 00:48:35 +01:00
Khopa
df74f7c6c7 Increased number of launchers on Silkworms sites 2021-02-05 00:25:42 +01:00
Khopa
2a6e2d470d Added coastal defenses sites generator for Iran and China. 2021-02-05 00:15:06 +01:00
BenBenBeartrax
643dd65113 waypointbuilder: low altitude AGL for helos 2021-02-02 14:30:28 -08:00
Dan Albert
cee0532b85 Move develop to 2.5. 2021-01-31 15:42:13 -08:00
Dan Albert
845e7fb956 Highlight major areas of change in the changelog.
The list is getting too long to be easily scannable for big changes :)
2021-01-31 14:52:16 -08:00
Khopa
f5cc2c3a37 Removed Western georgia campaign 2021-01-31 23:44:49 +01:00
Dan Albert
d56c2f7a50 Allow editing the arrival/diver airfield.
Fixes https://github.com/Khopa/dcs_liberation/issues/810
2021-01-31 14:26:14 -08:00
Dan Albert
5c47a8f7e1 Fix flyover waypoint generation.
Fixes https://github.com/Khopa/dcs_liberation/issues/820
2021-01-31 13:56:48 -08:00
Simon Clark
fb724e0150 Missed the bofors.
Also removes cargo carrier from factions.
2021-01-31 21:46:26 +00:00
Dan Albert
94ef47c89d Remove incomplete feature from changelog.
https://github.com/Khopa/dcs_liberation/issues/511 isn't done yet, and
as-is the system does barely anything, so not worth mentioning.
2021-01-31 13:26:29 -08:00
Dan Albert
7e415b3fd7 Fix carrier packages at beginning of campaign.
Fixes https://github.com/Khopa/dcs_liberation/issues/819
2021-01-31 13:21:34 -08:00
Simon Clark
fee497219e FInal set of vehicles, I think? 2021-01-31 20:23:58 +00:00
Simon Clark
7624f09f98 A-20G is good at anti-ship. 2021-01-31 17:09:19 +00:00
Simon Clark
946fe0a94c Added a lot of tanks to the unit info list. A lot of tanks. 2021-01-31 16:55:17 +00:00
Khopa
c7e1699546 SEAD/DEAD loadout for Harrier is using 4 AGM65-F + 2 Sidearm 2021-01-31 17:14:40 +01:00
Khopa
de4a617743 Fixed AV8B Harrier SEAD loadout contains invalid ordnance (AGM-65G) Replace by AGM-65F 2021-01-31 17:11:24 +01:00
Simon Clark
228d62dd32 Duplicates. 2021-01-31 12:15:51 +00:00
Simon Clark
51f8a60096 Add strategic bombers to DEAD/Strike lists. 2021-01-31 12:15:17 +00:00
Simon Clark
83f1a95966 Tweak the offset heading to 2 degrees, one degree doesn't always seem to be enough. 2021-01-31 10:54:29 +00:00
Dan Albert
646ba94d10 Make UI update faster on TOT change.
Fixes https://github.com/Khopa/dcs_liberation/issues/815
2021-01-30 20:25:34 -08:00
Dan Albert
8c5c08d678 Don't show income for dead buildings on the map. 2021-01-30 18:27:07 -08:00
Dan Albert
3d0b47a181 Fix exception in building debrief.
There are always a bunch of integer dead ground units. Not sure what
they are.
2021-01-30 18:24:25 -08:00
Dan Albert
ea7bece3b8 Display the TGO's income, not the CPs. 2021-01-30 17:23:18 -08:00
Dan Albert
944a8e9cd6 Allow selecting start type when creating flights. 2021-01-30 16:16:04 -08:00
Simon Clark
88f7d1d572 Changelog. 2021-01-31 00:14:53 +00:00
Simon Clark
6d78c1e302 Round budget display income figure to 2DP.
Fixes #809
2021-01-31 00:13:10 +00:00
Simon Clark
791aa8b6d1 Fix the powerplant object template.
For some reason, the large building with the tank on is rotated 180 degrees when put into an actual mission. Therefore, I've just spun it around to counter this.
2021-01-30 23:46:15 +00:00
Dan Albert
a078b67b36 Show arrival airfield in the flight list.
Fixes https://github.com/Khopa/dcs_liberation/issues/798.
2021-01-30 15:14:05 -08:00
Dan Albert
34d4ecd4e6 Add an option for default start type.
Changing this completely breaks OCA/Aircraft missions, but if the player
doesn't care about those this can reduce airfield congestion. The UI
warns about this.

This also makes the AI start type selectable in the flight UI.

Fixes https://github.com/Khopa/dcs_liberation/issues/387
Fixes https://github.com/Khopa/dcs_liberation/issues/729
2021-01-30 15:04:23 -08:00
Dan Albert
5047b535c4 Make ASAP a checkbox and maintain ASAP on changes.
Fixes https://github.com/Khopa/dcs_liberation/issues/642
2021-01-30 13:33:39 -08:00
Simon Clark
768d239840 Adds missile sites to the do-not-cull list.
Contributes-to: #700
2021-01-30 18:34:57 +00:00
Simon Clark
89392553bd Adds a new icon for AA sites with no threat.
Also adds the logic to check for this state.

Contributes-to: #239
2021-01-30 18:08:17 +00:00
Simon Clark
9a41217a59 Removes control point name dash->space replace.
This was breaking runway destruction. This seems to have been a legacy change from 1c67a2e4cf (diff-4c34a7844594e282145481e501ff81d198e869aad558b3589fbc1e140625ea03R20)

Fixes #741
2021-01-30 15:50:13 +00:00
Simon Clark
f6557e4980 Changelog. 2021-01-30 15:03:17 +00:00
Simon Clark
cc3cd95e2d Test for unit_name + " object" in the debrief.
This is because all building objects have " object" appended to the end in the unit map, but not in the state.json. Therefore, we should now find destroyed building objects in the unit map.

Should fix #793.
2021-01-30 15:00:40 +00:00
Simon Clark
8fb02136bb Changelog. 2021-01-30 13:38:55 +00:00
Simon Clark
1c86585f03 Make certain stances use slightly offset headings.
There's a DCS bug where vehicles sometimes don't move if they don't have to change heading. This is a bit of a hacky workaround for that.

Should fix #797.
2021-01-30 13:35:22 +00:00
Simon Clark
242085966b Changelog. 2021-01-30 01:14:11 +00:00
Simon Clark
6217075adc Add mission planning docs link to TOT field.
Completes: #696
2021-01-30 01:12:59 +00:00
Simon Clark
883f233c09 Adds WIP handling for default payload display with restricted date feature enabled. 2021-01-30 01:00:40 +00:00
Khopa
e4cc749180 Changelog update 2021-01-30 01:33:36 +01:00
Khopa
9780798b75 Merge remote-tracking branch 'khopa/develop' into develop 2021-01-30 01:30:00 +01:00
Khopa
225325cc29 High Digit SAM mod support added with an example factions (Russia 2010 High Digit SAM) 2021-01-30 01:28:47 +01:00
Simon Clark
2e3307065c Changelog. 2021-01-30 00:22:37 +00:00
Simon Clark
f6a4316093 Merge branch 'develop' of https://github.com/Khopa/dcs_liberation into develop 2021-01-30 00:19:38 +00:00
Simon Clark
b80aad7449 Link custom theater/faction/loadout docs in UI.
Contributes-to: #614
2021-01-30 00:19:29 +00:00
Khopa
5dbd1d093b High digit sam pydcs export update 2021-01-30 00:33:26 +01:00
Simon Clark
64e2e1109e Add the two Redfor factions from @RobertPeary.
Contributes-to: #488
2021-01-29 21:42:34 +00:00
Simon Clark
9c60140cec Changelog and Hercules radio fix.
Contributes-to: #782
2021-01-29 20:43:19 +00:00
Simon Clark
6099b664ac Updates Hercules Cargo file.
Fixes #782.
2021-01-29 20:36:05 +00:00
Simon Clark
33d084eff0 Add required mod link for USA C-130 faction.
Fixes #803.
2021-01-29 20:32:59 +00:00
Simon Clark
07eb14eaa6 Final aircraft banner until I download some mods. 2021-01-29 20:25:24 +00:00
Simon Clark
b2710fafd4 More banners.
Fixed a few typos.
2021-01-29 20:16:10 +00:00
Simon Clark
8f44b4571a Another batch of banners.
Also removes unarmed trainers.
2021-01-29 19:17:27 +00:00
Simon Clark
169f010fae Bunch more banner images. 2021-01-29 17:33:19 +00:00
Khopa
3e1547e0da Harrier default CAS loadout has APKWS 2021-01-27 22:29:43 +01:00
Khopa
825b9935ee Merge remote-tracking branch 'khopa/develop' into develop 2021-01-27 22:26:14 +01:00
Khopa
2f2e086fbb Using new pydcs export 2021-01-27 22:25:26 +01:00
Simon Clark
2f6e0c15fe Unit detail updates 2021-01-26 16:14:58 +00:00
Simon Clark
09e0c3dd63 Made the description text message a bit clearer. 2021-01-24 17:18:45 +00:00
Simon Clark
4529ac9b92 More banner images. 2021-01-24 16:43:08 +00:00
Dan Albert
8dac4eca55 Don't plan strikes against non-strike targets.
FOB strucutres can't be repaired and the player can't target them.

SAMs are targeted by DEAD already, so considering them for strike double
plans the mission.

Fixes https://github.com/Khopa/dcs_liberation/issues/686
2021-01-23 14:29:10 -08:00
Dan Albert
bf56091dc7 Fix inverted condition for building waypoints.
Fixes https://github.com/Khopa/dcs_liberation/issues/548
2021-01-23 14:10:25 -08:00
Khopa
f10f580f1c Fixed factions descriptions not being updated after auto-selection of factions. 2021-01-23 00:33:07 +01:00
Khopa
c09861d1ca Changelog update 2021-01-22 23:51:17 +01:00
Khopa
8e8df2b846 Changelog update 2021-01-22 23:51:03 +01:00
Khopa
d4a1d5bb9e Added campaign "Exercise Vegas Nerve" by Starfire 2021-01-22 23:49:03 +01:00
Khopa
84145aa7a7 Added Black Sea Lite campaign by Starfire. 2021-01-22 23:42:48 +01:00
Khopa
25f32a4776 New game wizard : Added default factions in description 2021-01-22 23:41:12 +01:00
Khopa
097c42d1dd New game wizard : Added performance information about the selected campaigns. 2021-01-22 23:07:17 +01:00
Khopa
91ac368a19 Changelog update 2021-01-22 22:11:49 +01:00
Khopa
f959dd0519 Campaign have a recommended defaults faction setup to ease campaign setup 2021-01-22 22:09:55 +01:00
Simon Clark
444605920f Adds F-14 to the DEAD list (TALD pods are cool) 2021-01-22 15:41:42 +00:00
Simon Clark
a102d8b39f Change case and add CR2. 2021-01-21 18:47:26 +00:00
Simon Clark
22eb861d28 More banner images. 2021-01-21 18:41:19 +00:00
Simon Clark
779b36bf7b The M163 Vulcan is now American again. 2021-01-20 18:07:16 +00:00
Simon Clark
f36336403b A few more ground units. The list goes on forever.
Next one to do is the TPz Fuchs.
2021-01-19 21:32:11 +00:00
Simon Clark
b7fbade968 More work on the unit info screen. 2021-01-19 17:53:55 +00:00
Simon Clark
0535b20db7 Another batch of screenshots. 2021-01-19 12:05:08 +00:00
Simon Clark
ddd91e3078 _24ed again. 2021-01-18 22:47:06 +00:00
Simon Clark
97f734b8fc Rename the B-1 image. 2021-01-18 22:46:12 +00:00
Simon Clark
38941f02a8 Add some initial images for the unit info pages.
This was pretty fun! DCS is very photogenic given the right conditions...
2021-01-18 22:44:34 +00:00
Simon Clark
84e09be199 Add aircraft manufacturer and role to info window. 2021-01-18 20:13:51 +00:00
Simon Clark
995a89d370 Add initial version of the unit info window.
DCS features a massive range of aircraft and land vehicles, and not all of them make their role(s) clear just from the name alone. What this commit does is add an "information" button (and resultant window) to the recruitment section. This should allow new players to understand what each unit is/does.

Current state - every aircraft has a country of origin and an introduction date for that variant. Some also have a small placeholder description, taken from ED's store page for that aircraft. There is also a placeholder picture (taken from a rejected image from my own personal photography) that will, in time, show a banner image of each unit.

Todo - add appropriate screenshots for each aircraft's banner, replace the placeholder text for each aircraft (this will take a while...) and add more data points for each unit type, such as a unit role (i.e. "air-superiority fighter", "multirole fighter", etc) or perhaps a list of weapons carried. I also haven't made a start on the huge number of ground units yet.
2021-01-18 19:27:54 +00:00
Simon Clark
6f11a269bc Add a few more pretty names. 2021-01-18 12:27:55 +00:00
Simon Clark
24a212a987 Make the C-130 work. For real this time.
Also separate out SEAD and DEAD taskings. Some aircraft can DEAD but not SEAD.

Also make the recruitment menu use the pretty names in the alphabetical sort func.
2021-01-17 21:41:02 +00:00
Simon Clark
3282ba0302 Hercules mod has AC-130s, so CAS makes sense too. 2021-01-17 21:10:14 +00:00
Simon Clark
a4db443f93 Add the Hercules to strike tasks.
It's not strike capable per se, but can transport objects for a strike.
2021-01-17 14:56:36 +00:00
Dan Albert
f8276f7e59 Don't defend against air threats from FOBs.
FOBs (and broken runways) do not need to be considered as sources of air
threats.

Fixes https://github.com/Khopa/dcs_liberation/issues/778
2021-01-17 01:17:33 -08:00
Dan Albert
b545634d87 Revert accidental display options default change. 2021-01-16 15:30:54 -08:00
Dan Albert
5da4cace94 Don't plan BARCAPs so aggressively.
Limit the commit range of a BARCAP to halfway to the closest enemy
airbase so that they don't become offensive missions.

This has the side effect of largely reducing long retreats to hold
points from front line airfields, since the package can get much closer
without being at risk of engagement by an enemy BARCAP.

Fixes https://github.com/Khopa/dcs_liberation/issues/742.
2021-01-16 15:26:25 -08:00
Dan Albert
1a2475dc25 Add display option for BARCAP commit range. 2021-01-16 15:15:40 -08:00
Dan Albert
2374239238 Fix doctrine inversion in threat zones.
The threat zone is the zone project by the given coaltition, not against
it.
2021-01-16 14:01:53 -08:00
Simon Clark
f84d77d334 Fix startup faction error. 2021-01-14 15:30:08 +00:00
Simon Clark
5a275e6153 Missed some subs. 2021-01-13 20:45:59 +00:00
Simon Clark
5f07069f1d Remove submarines for the time being.
They definitely feel too much like placeholders at this point.
They should be removed until they're in a better state.

https://forums.eagle.ru/topic/251238-submarines is a good read on the topic.
2021-01-13 19:52:35 +00:00
Simon Clark
1e1cebc3fc Display a "No aircraft available" message.
If in the create flight dialog, there are no suitable aircraft for a task, or no aircraft left at all, a suitable message is now shown that prevents the user from creating a flight.

Also adds in a quick "remember what plane the user had selected last" feature.
2021-01-13 12:11:53 +00:00
Simon Clark
c40ad75fa2 Add DEAD to the selectable aircraft list. 2021-01-13 10:37:05 +00:00
Simon Clark
c80db72bf7 Changelog. 2021-01-12 19:03:02 +00:00
Simon Clark
727ec6bc28 Filter mission types by aircraft.
First stab at implementing #392.
You can now only select aircraft types that can do the selected task.
2021-01-12 18:58:29 +00:00
Simon Clark
9ebad734a9 Changelog. 2021-01-12 17:30:15 +00:00
Simon Clark
0094628f6b Add the S-3B Viking into the game.
It's a carrier-based strike/anti-ship platform. What's not to love?

Completes #759.
2021-01-12 14:57:29 +00:00
Simon Clark
abbb046566 Add the SH-60B Seahawk into the game.
It was requested in #759, and also carries the Penguin AShM, which could be fun.
2021-01-12 12:27:31 +00:00
Dan Albert
a1136953d0 Fix custom waypoints. 2021-01-11 20:16:23 -08:00
Dan Albert
3298a5c6ad Improve front line flight plans.
Fixes https://github.com/Khopa/dcs_liberation/issues/462
2021-01-10 15:03:42 -08:00
Simon Clark
5e24fe9bb1 Poland is now Poland again.
The Mission Editor can be tricked into giving factions any unit.
Therefore, Poland can be Poland again, and not CJTFB.
2021-01-10 13:16:40 +00:00
Simon Clark
bc5b32ddef Changelog. 2021-01-10 13:09:28 +00:00
Simon Clark
7269dbb79d Edit Flight window is now dynamically sized.
This means that the waypoint names no longer get cut off.

Contributes-to: Khopa/dcs_liberation#754
2021-01-10 13:07:02 +00:00
Simon Clark
6f5bb6ffa2 Fix HQ7 generation error. 2021-01-10 11:46:58 +00:00
root0fall
7d0b738918 fix 588 - long waypoint descriptor in kneeboard generation 2021-01-09 23:35:47 -08:00
Dan Albert
1539d9c7ed Add a 1985 East German faction. 2021-01-09 16:02:51 -08:00
Dan Albert
4ae95e06ef Add a 1985 French faction. 2021-01-09 16:02:51 -08:00
Dan Albert
fa1166d014 Limit reserve ground units to 10.
Allowing these to grow infinitely leads to some really weird behaviors
when the enemy has been buying reserves long enough, where capturing a
base might result in 80 enemy vehicles suddenly at the gates.

This is just an interim fix. Ideally these units would be reinforcing
the front line as needed
(https://github.com/Khopa/dcs_liberation/issues/382), and a CP could be
lost without needing to completely destroy the defender.
2021-01-09 13:10:21 -08:00
Dan Albert
2d9e5fe984 Fix handling of destroyed buildings. 2021-01-08 19:21:06 -08:00
Simon Clark
7ae934e940 Make new flight comboboxes auto-adjust their size.
Content was previously being cut off when the first airport selected had a shorter name than the one the player wanted.
2021-01-08 22:52:21 +00:00
Simon Clark
64b2eeface Stop loadout resetting each time the editor opens. 2021-01-08 10:22:56 +00:00
Simon Clark
558dc591a3 Made the payload editor more user-friendly.
A user can now modify the existing default loadout, rather than starting from scratch.

Also adds WIP handling for removed pylons, which looks like it may need some PyDCS work.

Also fixes the F-14B default loadouts for everything OTHER than fighter sweep again.
2021-01-08 09:42:09 +00:00
Simon Clark
0bfd766a0b Make the base capture cheat also toggleable.
Also changelog.
2021-01-07 23:04:33 +00:00
Simon Clark
7741713a7c Makes the base intel window scrollable.
Contributes-to: Khopa/dcs_liberation#691
2021-01-07 22:56:04 +00:00
Khopa
454b540bce 2.3.4 changelog hadn't been added on develop branch. 2021-01-07 21:56:07 +01:00
Simon Clark
591c62b6d5 Make frontline advance/retreat cheats optional.
This can now be toggled on and off in the cheats menu.
2021-01-07 17:46:06 +00:00
Simon Clark
fdb4a7b055 Try to prevent objectives getting the same name.
Fixes Khopa/dcs_liberation#745
2021-01-07 17:22:43 +00:00
Simon Clark
f845ad9b31 Move the OCA payload type from runway->aircraft. 2021-01-07 15:56:00 +00:00
Simon Clark
f5f33ec865 Fixes the F-14B fighter sweep loadout.
The default one just picked two external tanks, so using the CAP one.

Fixes Khopa/dcs_liberation#663
2021-01-07 15:52:24 +00:00
Simon Clark
d35faf15d7 Tuples require a comma after a single item. 2021-01-07 13:42:21 +00:00
Simon Clark
7085bce6d4 Add support for some extra loadout types.
Also fixes the bug that @Starfire13 spotted in Khopa/dcs_liberation#744.
2021-01-07 13:37:16 +00:00
Simon Clark
a81890e844 Changelog. 2021-01-07 12:35:08 +00:00
Simon Clark
8dbec21b02 Adds Iran 1988 Faction.
Also adds the SA342 Gazelle to Iraq, as they used them.

Adds associated pretty names.
2021-01-07 12:34:35 +00:00
Simon Clark
a654c8229a Make the default loadout code more resilient.
It can now cope with the payload overrides defined in the db.
2021-01-07 11:31:46 +00:00
Simon Clark
f2e35c185b Displays the default loadout in the payload UI.
The default loadout is now displayed whenever the custom loadout checkbox is unchecked.

The custom loadout is reset when the custom loadout is checked, to force the user to be prescriptive about the loadout.

Contributes-to: Khopa/dcs_liberation#725
2021-01-07 01:45:45 -08:00
Simon Clark
062c2643ad Adds rockets/hellfires to Apaches where sensible. 2021-01-06 17:18:13 -08:00
Simon Clark
088c7b35ba Adds more strategic & maritime bombers for Russia.
Also adds the prettified name for the Tu-142.
2021-01-06 17:16:03 -08:00
walterroach
ef439a6c42 Merge pull request #736 from SimonC6R/fix-carrier-sam-rings
Carrier group threat rings move with the carrier.
2021-01-06 17:24:15 -06:00
Dan Albert
40956a4042 Merge pull request #738 from SimonC6R/remove-pyotr-velikiy
Remove Pyotr Velikiy from the generator for now.
2021-01-06 12:43:59 -08:00
Simon Clark
8680e90e3b Changelog. 2021-01-06 20:42:10 +00:00
Simon Clark
fac770424c Remove Pyotr Velikiy from the generator for now.
It's just too strong to be killed by a reasonable number of Harpoons. DCS needs a fixed ship damage model first.

Contributes-to: Khopa/dcs_liberation#567
2021-01-06 20:39:50 +00:00
Simon Clark
042be9da6d Carrier group threat rings move with the carrier.
Previously, the individual units in the group were moved, but the ground_unit object was not.

Fixes: Khopa/dcs_liberation#735
2021-01-06 18:58:20 +00:00
Khopa
81b0ea1eef Updated AIM-7 config to account for the Belly and Shoulder versions of the AIM-7 used on the F-14. Also Downgrade AIM-54C to AIM-54A before 86 on the Tomcat. 2021-01-06 01:27:53 +01:00
Khopa
52289d1283 More fallbacks weapons for popular precision air to ground weapons 2021-01-06 00:56:20 +01:00
Khopa
1843d23203 Merge remote-tracking branch 'khopa/develop' into develop 2021-01-06 00:54:42 +01:00
Khopa
1a32fef987 More fallbacks weapons for popular precision air to ground weapons 2021-01-06 00:53:55 +01:00
Simon Clark
c740c8304b Adds prettier user-facing aircraft names. (#726)
This makes the names of the aircraft displayed to the player in the UI more verbose and readable.

It allows allows specific countries to display an aircraft's name differently. An example of this would be the JF-17 Thunder, which is known in China as the FC-1 Fierce Dragon - this now displays correctly in the Liberation UI.
2021-01-05 13:21:38 -08:00
Khopa
c3401d478b Added R-73 -> R60 to weapons fallback data 2021-01-05 21:40:20 +01:00
Khopa
cf583bcd55 Merge branch 'master' into develop
# Conflicts:
#	changelog.md
#	game/db.py
#	game/game.py
#	game/income.py
#	game/theater/theatergroundobject.py
#	game/version.py
#	qt_ui/windows/finances/QFinancesMenu.py
2021-01-05 19:50:29 +01:00
Simon Clark
ef143a7ebb Changelog update. 2021-01-05 03:34:10 -08:00
SimonC6R
7aec483e73 Also add the E-2C to Israel and Japan.
Israel only got it in 1981 but it's that or the E-3A they never used.

Japan never used the E-3A, but did buy the E-2.
2021-01-05 02:36:08 -08:00
SimonC6R
db6a3b9849 Use E-2C Hawkeye for US faction between 1964-1977. 2021-01-05 02:36:08 -08:00
Dan Albert
657c5e1f52 Merge pull request #712 from SimonC6R/add-greece-2005
Adds a 2005-ish Greece faction.
2021-01-05 02:34:52 -08:00
Simon Clark
3b0466d7cb Adds a 2010-ish Poland faction. (#713)
The SU-17M4 represents the remaining Su-22s that Poland still flies.
I've used the Stryker as a replacement for the KTO Rosomak, which was in service as far back as 2007.
There were a few Molniyas still in service as of 2010, namely the Metalowiec and Rolnik, as well as a singular Kilo class.
2021-01-05 02:31:49 -08:00
Dan Albert
8a9177b459 Merge pull request #716 from SimonC6R/sold-aircraft-state
Adds a buffer for sold aircraft/vehicles.
2021-01-05 02:12:49 -08:00
Simon Clark
a0e63511d6 Update Greece faction with comments. 2021-01-05 10:12:46 +00:00
Simon Clark
7c3f7d4b8e Addresses review comments. 2021-01-05 09:42:05 +00:00
Dan Albert
be1062c373 Update era-specific loadout release notes. 2021-01-05 00:54:28 -08:00
Dan Albert
2bd5ab06a7 More weapon data.
https://github.com/Khopa/dcs_liberation/issues/490
2021-01-05 00:43:59 -08:00
Dan Albert
e174c1b147 Fix bug in weapon downgrading.
We want the *first* valid replacement, not the last one :)

https://github.com/Khopa/dcs_liberation/issues/490
2021-01-05 00:26:19 -08:00
Dan Albert
7ef191be2a Add more weapon data.
Finishes AIM-120 and AIM-9X.

https://github.com/Khopa/dcs_liberation/issues/490
2021-01-05 00:25:37 -08:00
SimonC6R
03a29aeedf Type annotations. 2021-01-05 00:31:54 +00:00
SimonC6R
d10b4c1e13 Re-add whitespace. 2021-01-05 00:27:26 +00:00
SimonC6R
ab2046a2c2 Refactor the sell unit changes as requested.
It works more simply now, and also doesn't immediately sell the unit.

Also adds a matching UI dialog popup for selling too many ground units.
2021-01-05 00:26:40 +00:00
Dan Albert
bc6b2e0f3e Add warning for missing weapon data.
These are disabled by default because it would otherwise warn about
nearly every piece of equipment in the game currently.
2021-01-04 15:57:37 -08:00
Dan Albert
746c99ebd6 Update Skynet to 2.0.1.
Fixes https://github.com/Khopa/dcs_liberation/issues/717
2021-01-04 15:36:18 -08:00
Dan Albert
34945e7eba Add date-based loadout restriction.
Follow up work:

* Data entry. I plan to do the air-to-air missiles in the near term. I
  covered some variants of the AIM-120, AIM-7, and AIM-9 here, but there
  are variants of those weapons for each mounting rack that need to be
  done still, as well as all the non-US weapons.
* Arbitrary start dates.

https://github.com/Khopa/dcs_liberation/issues/490
2021-01-04 15:13:40 -08:00
Dan Albert
507b217065 Clean up custom loadout interface.
Wraps the pydcs data in a real type so we don't need to spread the
reflection all over.
2021-01-04 15:13:40 -08:00
Simon Clark
e222f17199 Adds payload and EWRS info for J-11A. (#715)
No loadout had been specified for the J-11A, so I've added one. The J-11A carries a similar loadout to the Su-27, but with R-77s instead of R-27ERs.

Also looks like it was missing from the EWRS details, so I've added it there too.

Addresses Khopa/dcs_liberation#610
Addresses Khopa/dcs_liberation#671
2021-01-04 13:07:07 -08:00
Simon Clark
144cfecc0f Adds the E-2C Hawkeye to the game. (#714)
* Adds the E-2C Hawkeye to the game.

It wasn't being imported from pydcs, and thus wasn't in the list of AWACS aircraft or prices.

Also adds it to the 1985 US Navy list, as that makes sense to do.

Updates .gitignore to ignore my VS Code settings file.

Addresses Khopa/dcs_liberation#709
2021-01-04 13:04:11 -08:00
Simon Clark
64066bfc90 Merge branch 'develop' of https://github.com/Khopa/dcs_liberation into develop 2021-01-04 19:27:36 +00:00
Simon Clark
366ac4ee14 Adds a buffer for sold aircraft/vehicles.
This feature allows you to cancel the sales of aircraft or ground vehicles if needed.

Upon clicking the minus button, a count of sold units will be appended to the unit count. This count responds to both further presses of the minus button, and also to presses of the plus button. No further units will be requested for the next turn until all sold units have been re-bought.

I've tested a bunch of different scenarios with this:

- Selling and rebuying a unit - the budget increases and decreases as expected.

- Selling one unit, buying a unit worth the new player budget, and then trying to rebuy the old unit - the old unit cannot be rebought until the budget has been freed up for it.

- Closing the base window and re-opening it - the sold unit count is retained.

- Ending the turn - the sold unit count is reset back to 0 as expected.

Contributes to Khopa/dcs_liberation#365
2021-01-04 19:27:29 +00:00
walterroach
851984ee66 Revert TGO ID reset - fixes #708 2021-01-04 11:36:40 -06:00
Dan Albert
34bdc0e80b Fix new game creation. 2021-01-01 16:15:43 -08:00
Dan Albert
61d7d5e041 Update minimum version of pyside2.
Fixes https://github.com/Khopa/dcs_liberation/issues/416
2021-01-01 15:50:01 -08:00
Dan Albert
b5278550e7 Retreat air units from captured bases.
If there are not airbases withing ferry range with available parking for
the aircraft then the aircraft will be captured and sold. Otherwise the
aircraft will retreat to the closest available airbase.

Fixes https://github.com/Khopa/dcs_liberation/issues/693
2021-01-01 15:19:16 -08:00
Dan Albert
461635c001 Note pending unit refund in changelog. 2021-01-01 14:46:51 -08:00
Dan Albert
fcb1d8e104 Retreat ground forces from captured bases.
If the captured base has no connection to other friendly objectives (the
base was encircled) then the enemy equipment will be captured and sold.
Otherwise the units will retreat toward connected bases.

Partial fix for https://github.com/Khopa/dcs_liberation/issues/693.
Still need to fix this for air units.
2021-01-01 14:45:43 -08:00
Dan Albert
6cbc2b707a Fix type of Game.budget.
This *is* a float, since the income multiplier is a float. The type
annotations were wrong.
2021-01-01 13:56:33 -08:00
Dan Albert
9671542bdf Decouple unit deliveries and conflict events.
Fixes https://github.com/Khopa/dcs_liberation/issues/692 and lets us
clean up the interface quite a bit.
2021-01-01 13:48:23 -08:00
Dan Albert
de325c1208 Refunding unfulfilled orders on capture.
Fixes https://github.com/Khopa/dcs_liberation/issues/682
2021-01-01 13:23:58 -08:00
Dan Albert
802eff1faa Fix some airfield data in Persian Gulf.
Not sure if this changed or has just always been wrong.
2021-01-01 13:22:33 -08:00
C. Perreau
068f9e42d7 Merge pull request #698 from Khopa/develop_2_3_x
Release 2.3.4
2020-12-31 14:07:30 +01:00
Khopa
4a483c3b27 Changelog 2020-12-31 13:52:02 +01:00
Khopa
e3bd958069 Fixed possible AttributeError when generating missile site fire tasks 2020-12-31 13:28:21 +01:00
Khopa
900cf0a9d0 Update preview version number 2020-12-31 13:16:09 +01:00
C. Perreau
64c424b9a6 Merge pull request #649 from Khopa/develop_2_3_x
Release 2.3.3
2020-12-31 01:26:19 +01:00
Dan Albert
5734c29312 Remove wrong precondition in aircraft procurement.
Those task types aren't correct here (that whole dict probably serves
little purpose now), and the actual unit pool is handled in
_affordable_aircraft_of_types.
2020-12-28 16:06:54 -08:00
walterroach
362caa6ac1 Merge pull request #689 from walterroach/naming
Naming
2020-12-28 17:22:42 -06:00
Dan Albert
b620976b70 Flesh out Desert War IADS, add carriers. 2020-12-28 13:16:39 -08:00
walterroach
daba4ef09e Fix aircraft group IDs not being reproducible. 2020-12-28 14:04:19 -06:00
walterroach
c697a34239 Correctly reset ANIMALS 2020-12-28 11:31:04 -06:00
walterroach
09b7cb3d85 Change naming to static class
This ensures all generators are using the same ID set.
2020-12-28 11:22:28 -06:00
walterroach
d7e48662e0 Add custom flight names
Mildly breaks save compat with 2.3; All existing flight dialogs will be
broken, passing the turn or recreating all the flights in the UI will
allow you to continue
2020-12-28 10:52:25 -06:00
walterroach
9fd5c6f230 Add package info to aircraft names
Makes it easier to identify aircraft for client flights
2020-12-28 09:51:37 -06:00
walterroach
aa7825d4aa Reproducible unit naming
Splits infantry and other unit IDs.

Resets IDs to start from zero at each press of "Takeoff"

Direct access to the the `Game` class IDs is done on the `Operation` class to
preserve save compat
2020-12-28 09:27:33 -06:00
Dan Albert
436725b38e Don't award base capture bonuses.
If anything we should be granting the loser extra income so they can
recover. For now just remove.
2020-12-27 19:52:42 -08:00
Dan Albert
922d935bc1 Tweak default budget and turn 0 allocation.
AI wasn't buying many ground vehicles. Keep the turn 0 air budget about
where it was but bump up the ground budget from ~120M to ~600M.
2020-12-27 19:48:03 -08:00
Dan Albert
3716395453 Don't show FOB structure as a target.
This isn't perfect because the auto planner might still target it. We
need a larger refactoring for target iteration so we don't need to
remember all the special rules at each call site. For now, this restores
the 2.3.2 behavior.

Fixes https://github.com/Khopa/dcs_liberation/issues/681

(cherry picked from commit 69833f66e3)
2020-12-27 19:35:45 -08:00
Dan Albert
69833f66e3 Don't show FOB structure as a target.
This isn't perfect because the auto planner might still target it. We
need a larger refactoring for target iteration so we don't need to
remember all the special rules at each call site. For now, this restores
the 2.3.2 behavior.

Fixes https://github.com/Khopa/dcs_liberation/issues/681
2020-12-27 19:34:10 -08:00
Dan Albert
ec787b913c Use exact name matching when picking targets.
Inexact name matching targets the first group that partially matches the
given group name. aa|71 will match aa|7.

The inexact match that was here was only needed for an early attempt to
use skynet where group names were not used consistently. That's no
longer a problem so we don't need this workaround.

Fixes https://github.com/Khopa/dcs_liberation/issues/676

(cherry picked from commit 89f313295e)
2020-12-27 13:50:55 -08:00
Dan Albert
89f313295e Use exact name matching when picking targets.
Inexact name matching targets the first group that partially matches the
given group name. aa|71 will match aa|7.

The inexact match that was here was only needed for an early attempt to
use skynet where group names were not used consistently. That's no
longer a problem so we don't need this workaround.

Fixes https://github.com/Khopa/dcs_liberation/issues/676
2020-12-27 13:45:53 -08:00
Dan Albert
7bc7a44c72 Allow managing disbanded sites in CPs.
Fixes https://github.com/Khopa/dcs_liberation/issues/679

(cherry picked from commit 317a882386)
2020-12-27 13:09:15 -08:00
Dan Albert
317a882386 Allow managing disbanded sites in CPs.
Fixes https://github.com/Khopa/dcs_liberation/issues/679
2020-12-27 13:08:59 -08:00
Dan Albert
3a9f585b6b Plan multiple CAP rounds per turn.
On station time for CAP is only 30 minutes, so plan three cycles to give
~90 minutes of CAP coverage.

Default starting budget has increased significantly to account for the
greatly increased aircraft needs on turn 1.

Fixes https://github.com/Khopa/dcs_liberation/issues/673
2020-12-26 17:26:07 -08:00
Dan Albert
7bbb1c0822 Don't show repaired TGOs as dead.
We were never resetting the dead state for repaired SAMs. Rather than
tracking that manually, determine liveness from the number of units left
alive.

For building objectives where the group is not assigned to the TGO until
*mission* generation time we still need manual tracking.

(cherry picked from commit d3b1f6110f)
2020-12-26 16:21:31 -08:00
Dan Albert
d3b1f6110f Don't show repaired TGOs as dead.
We were never resetting the dead state for repaired SAMs. Rather than
tracking that manually, determine liveness from the number of units left
alive.

For building objectives where the group is not assigned to the TGO until
*mission* generation time we still need manual tracking.
2020-12-26 16:19:41 -08:00
Dan Albert
a6dc3d2aff Handle threat/detection per group.
Some SAMs have multiple groups (such as an SA-10 group with its
accompanying SA-15 and SA-19 groups). This shows each group's threat and
detection separately on the map, and also makes it so that an SA-10 with
dead radars will no longer contribute to the threat zone just because
the shilka next to it still has a functioning radar.

https://github.com/Khopa/dcs_liberation/issues/647
Fixes https://github.com/Khopa/dcs_liberation/issues/672
2020-12-26 15:52:36 -08:00
Dan Albert
d946a9e526 Don't count blind SAMs as threats.
https://github.com/Khopa/dcs_liberation/issues/647
2020-12-26 15:40:38 -08:00
Dan Albert
17dd1b193e Remove aa_ranges in favor of using the TGO data. 2020-12-26 15:25:23 -08:00
Dan Albert
d634fd3236 Adjust income based on control point type.
* Navies and off map spawns generate no income
* FOBs generate 10 instead of 20

Fixes https://github.com/Khopa/dcs_liberation/issues/662
2020-12-26 15:08:57 -08:00
Dan Albert
e861e5b3d6 Even out player and opfor income rules.
Should help fix the anemic enemy forces after the first few turns.
2020-12-26 14:35:57 -08:00
Dan Albert
6045f4dd91 Fix New Package for naval control points.
Also reordered the tasks so ship-specific tasks appear first.

Fixes https://github.com/Khopa/dcs_liberation/issues/628

(cherry picked from commit 8be2841bdf)
2020-12-26 14:30:34 -08:00
Dan Albert
8be2841bdf Fix New Package for naval control points.
Also reordered the tasks so ship-specific tasks appear first.

Fixes https://github.com/Khopa/dcs_liberation/issues/628
2020-12-26 14:30:26 -08:00
Dan Albert
b6e37b9e67 Exclude non-AA groups from SAM threat zones.
Fixes https://github.com/Khopa/dcs_liberation/issues/666
2020-12-26 14:00:31 -08:00
walterroach
0d0d582bd8 Add F-14A-135-GR Icon 2020-12-26 13:24:45 -06:00
walterroach
0c42227e5e Add F-14A-135-GR Icon 2020-12-26 13:23:45 -06:00
Dan Albert
98ac4bd5c8 Fix generator -> group conversion when buying SAM.
Fixes https://github.com/Khopa/dcs_liberation/issues/664
Fixes https://github.com/Khopa/dcs_liberation/issues/665
2020-12-26 02:23:15 -08:00
Dan Albert
a43b100781 Purchase reserves for CAP/CAS.
Next turn's defenses should be planned in preference to expanding
offensive capabilities.

Fixes https://github.com/Khopa/dcs_liberation/issues/511
2020-12-25 19:15:31 -08:00
Dan Albert
c7f9bfbb43 Sell off incomplete opfor squadrons.
Short term fix for https://github.com/Khopa/dcs_liberation/issues/41.
2020-12-25 18:45:58 -08:00
Dan Albert
b5f8e6925b Rank aircraft purchase preferences.
Rather than randomly selecting compatible aircraft for missions, perfer
the *best* aircraft for the job. This removes the "preferred" lists in
favor of sorting the capable lists in priority order. To maintain some
amount of variety the procurer has a 50/50 chance of buying when it
finds a match.

Fixes https://github.com/Khopa/dcs_liberation/issues/510
2020-12-25 18:22:50 -08:00
Dan Albert
993e59413a Add pretty-print for AircraftProcurementRequest. 2020-12-25 17:31:26 -08:00
Dan Albert
9f2fab78a1 Flesh out intel displays.
* Add enemy air and ground unit reports.
* Changes the summary to be a comparison of relative strengths rather
  than raw enemy numbers.

Fixes https://github.com/Khopa/dcs_liberation/issues/658

(cherry picked from commit 3bdf1377c0)
2020-12-25 16:08:10 -08:00
Dan Albert
3bdf1377c0 Flesh out intel displays.
* Add enemy air and ground unit reports.
* Changes the summary to be a comparison of relative strengths rather
  than raw enemy numbers.

Fixes https://github.com/Khopa/dcs_liberation/issues/658
2020-12-25 16:08:02 -08:00
Dan Albert
8f24cf07be Improve layout of intel window.
(cherry picked from commit 1f4516b954)
2020-12-25 16:05:04 -08:00
Dan Albert
17c40234e9 Add basic intel window.
Currently only shows the enemy's economic information.

https://github.com/Khopa/dcs_liberation/issues/658
(cherry picked from commit 1d76ee4871)
2020-12-25 16:04:54 -08:00
Dan Albert
4cecddcdd0 Add basic enemy intel widget.
https://github.com/Khopa/dcs_liberation/issues/658
(cherry picked from commit b53cac4c7a)
2020-12-25 16:04:17 -08:00
Dan Albert
1f4516b954 Improve layout of intel window. 2020-12-25 14:42:04 -08:00
Dan Albert
1d76ee4871 Add basic intel window.
Currently only shows the enemy's economic information.

https://github.com/Khopa/dcs_liberation/issues/658
2020-12-25 03:24:33 -08:00
Dan Albert
b53cac4c7a Add basic enemy intel widget.
https://github.com/Khopa/dcs_liberation/issues/658
2020-12-25 01:38:14 -08:00
Dan Albert
29a0644719 Never generate empty ship groups.
Fixes https://github.com/Khopa/dcs_liberation/issues/391

(cherry picked from commit c833078e71)
2020-12-25 01:35:24 -08:00
Dan Albert
c833078e71 Never generate empty ship groups.
Fixes https://github.com/Khopa/dcs_liberation/issues/391
2020-12-25 01:32:54 -08:00
Dan Albert
e4cba8d19f Mention Skynet PD in changelog. 2020-12-24 19:32:31 -08:00
Dan Albert
cd6620712f Configure skynet point defenses.
Fixes https://github.com/Khopa/dcs_liberation/issues/470
2020-12-24 17:10:32 -08:00
Dan Albert
85619b156d Support groups for SAM templates.
It's only possible to control emissions for the group as a whole, so
Skynet needs PDs to be in separate groups from the main part of the SAM
for PD to operate correctly.

https://github.com/Khopa/dcs_liberation/issues/429
https://github.com/Khopa/dcs_liberation/issues/470
2020-12-24 16:09:42 -08:00
Dan Albert
10debbc286 Constrain front lines better.
Holes in the inclusion zone are defined by exclusion zones, not by holes
in the inclusion zone. Add a cached property for the inclusion zone that
is not also sea or exclusion zone and use that boundary instead.
2020-12-24 13:50:29 -08:00
Khopa
dcac5b488a Changelog update 2020-12-24 16:11:12 +01:00
Khopa
e1009bdafa Fixed ships group that could be replaced by sam site. Removed the possibility to disband ship groups for now. 2020-12-24 16:09:13 +01:00
Khopa
38ce842ca8 Merge remote-tracking branch 'khopa/master' into develop_2_3_x
# Conflicts:
#	changelog.md
#	game/version.py
#	gen/ground_forces/ai_ground_planner.py
#	pydcs
#	resources/factions/iraq_1991.json
#	resources/factions/russia_2010.json
2020-12-24 13:32:14 +01:00
Dan Albert
aafd09569c Fix mypy error. 2020-12-24 02:20:49 -08:00
Dan Albert
67a9df686e Add fast path for NavPoint equality.
Hot method and the FFI costs for comparing the points are not cheap.
2020-12-24 02:06:08 -08:00
Dan Albert
9a374711fd Don't access point coordinates when hashing.
For some reason this is crazy expensive. Turn time goes from 1.7 seconds
to 1 second with this change.
2020-12-24 01:49:07 -08:00
Dan Albert
b9138acbc8 Use shapely projection instead of brute force.
Converts the landmap to use MultiPolygon instead of a collection of
polygons, since Shapely has explicit support for this.

Because we've done that, we can use a single projection from a line
instead of brute forcing the extent of the front line.

This makes turn processing ~66% faster (3 seconds to 1.8).

There are probably other places this should be used.
2020-12-24 01:20:15 -08:00
Dan Albert
2a65916f7c Log time taken in turn processing. 2020-12-24 00:53:36 -08:00
Dan Albert
6aa1f1cca0 Prefer buying aircraft at safe airbases.
Fixes https://github.com/Khopa/dcs_liberation/issues/652
2020-12-23 22:25:47 -08:00
Dan Albert
8c1ebfda02 Show ASAP TOT for debug view flights. 2020-12-23 21:42:13 -08:00
Dan Albert
81af5d7497 Use navmesh to plan strike-like flight plans.
The cases where the target is extremely close to the origin point still
use the old flight plan pattern. This is probably fine.

https://github.com/Khopa/dcs_liberation/issues/292
2020-12-23 21:30:36 -08:00
Khopa
368bf08ade Fixed mypy error 2020-12-24 03:18:53 +01:00
Dan Albert
d95f623ca9 Use navmesh to plan sweep missions.
https://github.com/Khopa/dcs_liberation/issues/292
2020-12-23 18:08:58 -08:00
Dan Albert
2856fbc42b Improve display of nav points in kneeboard. 2020-12-23 17:52:14 -08:00
Dan Albert
ac59e15bd9 Use navmesh to plan CAS and BARCAP.
https://github.com/Khopa/dcs_liberation/issues/292
2020-12-23 17:40:59 -08:00
Dan Albert
91d9bbdc97 Add visual debugging for other fligth plans. 2020-12-23 17:31:04 -08:00
Dan Albert
575f4e1786 Clean up debug display options. 2020-12-23 17:19:57 -08:00
Dan Albert
bff905fae5 Use navmeshes to improve TARCAP flight plans.
Started with TARCAP because they're simple, but will follow and extend
this to the other flight plans next.

This works by building navigation meshes (navmeshes) of the theater
based on the threat regions. A navmesh is created for each faction to
allow the unique pathing around each side's threats. Navmeshes are built
such that there are nav edges around threat zones to allow the planner
to pick waypoints that (slightly) route around threats before
approaching the target.

Using the navmesh, routes are found using A*. Performance appears
adequate, and could probably be improved with a cache if needed since
the small number of origin points means many flights will share portions
of their flight paths.

This adds a few visual debugging tools to the map. They're disabled by
default, but changing the local `debug` variable in `DisplayOptions` to
`True` will make them appear in the display options menu. These are:

* Display navmeshes (red and blue). Displaying either navmesh will draw
  each navmesh polygon on the map view and highlight the mesh that
  contains the cursor. Neighbors are indicated by a small yellow line
  pointing from the center of the polygon's edge/vertext that is shared
  with its neighbor toward the centroid of the zone.
* Shortest path from control point to mouse location. The first control
  point for the selected faction is arbitrarily selected, and the
  shortest path from that control point to the mouse cursor will be
  drawn on the map.
* TARCAP plan near mouse location. A TARCAP will be planned from the
  faction's first control point to the target nearest the mouse cursor.

https://github.com/Khopa/dcs_liberation/issues/292
2020-12-23 17:09:34 -08:00
Khopa
c0fa135bf6 Artillery groups would retreat in the wrong direction - fixed (parameters of the find_retreat_point function are a bit confusing 😕 ) 2020-12-24 02:03:12 +01:00
Khopa
86394d8f19 Artillery groups would retreat in the wrong direction - fixed (parameters of the retreat point function are a bit confusing 😕 ) 2020-12-24 02:02:17 +01:00
Khopa
72c233cb0d Fixed possible assertion error when redeploying units which would lead to ground units not being redeployed. 2020-12-24 01:47:44 +01:00
Khopa
04e2c02eff SCUD missile sites will fire on nearest enemy airport by default 2020-12-24 01:26:00 +01:00
Khopa
7362744df2 Changelog update 2020-12-23 22:15:56 +01:00
Khopa
01951b5c32 Reworked emirates campaign 2020-12-23 21:58:39 +01:00
Khopa
f2f52771bd Removed "broken" midgame setting 2020-12-23 21:37:59 +01:00
Khopa
b59167d3ca Changelog update, WW2 factions can recruit AA/AT guns for frontlines. 2020-12-23 18:21:13 +01:00
Khopa
88e466562c Infantry squads can contain a mortar. 2020-12-23 17:53:52 +01:00
Khopa
1f85e5d7f8 Changelog update 2020-12-23 17:25:15 +01:00
Khopa
50471d510e Fixed and added many ground units icons 2020-12-23 17:24:20 +01:00
Khopa
8b7cf2f725 Changelog update 2020-12-23 01:35:19 +01:00
Khopa
282a5109ba Infantry group are always made of 5 units instead of a random amount. 2020-12-23 01:33:49 +01:00
Khopa
3d3b4738d9 Insurgent hard faction name fixed 2020-12-23 01:31:07 +01:00
Khopa
66149bb591 Fixed error in merge 2020-12-22 23:34:08 +01:00
Khopa
b0ad664ece Merge branch 'develop_2_3_x' into develop
# Conflicts:
#	changelog.md
#	game/procurement.py
#	resources/factions/iraq_1991.json
2020-12-22 23:32:06 +01:00
Khopa
7c29ea836c Infantry is only generated for IFV and APC groups 2020-12-22 23:24:27 +01:00
Khopa
92e9e8c56a Merge remote-tracking branch 'khopa/develop_2_3_x' into develop_2_3_x 2020-12-22 23:23:52 +01:00
Khopa
12bf26223d Added shorad units on frontline 2020-12-22 23:23:32 +01:00
Dan Albert
56d7993c8f Improve threat zone display options. 2020-12-22 13:57:05 -08:00
Dan Albert
52b63927b4 Prune escorts from packages that don't need them.
If the package is not flying into the threat zones of significant air
defenses there's no need for SEAD, and packages not near enemy airbases
do not need escorts. Prune these flights from the package to save
aircraft.
2020-12-22 13:12:04 -08:00
Dan Albert
86558bdef6 Add threat zone modeling.
Creates threat zones around airfields and non-trivial air defenses (it's
not worth dodging anything with a threat range under 3nm). These threat
zones can be used to aid mission planning and waypoint placement.

https://github.com/Khopa/dcs_liberation/issues/292
2020-12-22 13:12:04 -08:00
Dan Albert
e46262b021 Move has_radar into the TGO. 2020-12-22 12:59:29 -08:00
Dan Albert
c53feb5ccb Specify CAP engagement range in the doctrine. 2020-12-22 12:42:36 -08:00
Dan Albert
fc6d4f0990 Add EWRS plugin.
Fixes https://github.com/Khopa/dcs_liberation/issues/323
2020-12-21 21:28:27 +01:00
C. Perreau
df948bde9d Update CONTRIBUTING.md 2020-12-21 14:14:42 +01:00
C. Perreau
203a720ae1 Create CONTRIBUTING.md 2020-12-21 14:14:39 +01:00
C. Perreau
3410f08cfb Create CODE_OF_CONDUCT.md 2020-12-21 14:14:37 +01:00
C. Perreau
a553914ef4 Merge pull request #625 from Khopa/contributing
Create CONTRIBUTING.md
2020-12-21 14:13:29 +01:00
Khopa
21220141f2 pydcs submodule version update 2020-12-21 14:08:21 +01:00
Khopa
caf2d8436b Changelog update for 2.3.3 2020-12-21 13:56:00 +01:00
Khopa
4cc305fa81 Syrian civil war description updated 2020-12-21 13:55:33 +01:00
Khopa
60f837d0b9 Fixed : AI wouldn't buy artillery units 2020-12-21 13:34:56 +01:00
C. Perreau
05bd7f8e6b Update CONTRIBUTING.md 2020-12-21 13:04:16 +01:00
Emanuele Garofalo
e58ab34a15 Update NATO_Desert_Storm.json 2020-12-21 13:02:01 +01:00
Emanuele Garofalo
d960758ef3 Update iraq_1991.json 2020-12-21 13:01:58 +01:00
Emanuele Garofalo
a36ccdcc39 Update NATO_Desert_Storm.json 2020-12-21 13:01:57 +01:00
Emanuele Garofalo
d582948377 Update NATO_Desert_Storm.json 2020-12-21 03:31:19 -08:00
Emanuele Garofalo
d806e0b1c3 Update iraq_1991.json 2020-12-21 03:31:19 -08:00
Emanuele Garofalo
b9e110a7e3 Update NATO_Desert_Storm.json 2020-12-21 03:31:19 -08:00
Dan Albert
a2f218d56d Add EWRS plugin.
Fixes https://github.com/Khopa/dcs_liberation/issues/323
2020-12-21 01:19:54 -08:00
Khopa
2c475011a1 Syria terrain update + syrian civil war reworked in miz format 2020-12-21 03:19:17 +01:00
walterroach
2d7fc33726 Fix Distance being passed to pydcs methods 2020-12-20 17:19:49 -06:00
Dan Albert
0c8d1e1dc4 Remove checkbox from feature request form. 2020-12-20 14:48:54 -08:00
Khopa
bb04ce2abb Golan heights battle scenario fully migrated to miz format 2020-12-20 22:39:15 +01:00
Khopa
9850b22c0a Improved "Golan Heights Campaign Lite" for the Syria map. 2020-12-20 14:56:55 +01:00
C. Perreau
a2f65666a5 Create CONTRIBUTING.md 2020-12-20 14:20:18 +01:00
C. Perreau
7730809dbb Merge pull request #619 from Khopa/add-code-of-conduct-1
Add code of conduct
2020-12-20 13:45:25 +01:00
Dan Albert
2ac818dcdd Convert to new unit APIs, remove old APIs.
There are probably plenty of raw ints around that never used the old
conversion APIs, but we'll just need to fix those when we see them.

Fixes https://github.com/Khopa/dcs_liberation/issues/558
2020-12-19 22:08:57 -08:00
Dan Albert
113947b9f0 Add types for distance and speed.
Not converting all at once so I can prove the concept. After that we'll
want to cover all the cases where an int distance or speed is a part of
the save game (I've done one of them here with `Flight.alt`) so further
cleanups don't break save compat.

https://github.com/Khopa/dcs_liberation/issues/558
2020-12-19 21:07:55 -08:00
walterroach
44bc2d769b Merge branch 'develop_2_3_x' into develop 2020-12-19 20:27:31 -06:00
Khopa
02ecfebb85 Merge remote-tracking branch 'khopa/develop_2_3_x' into develop_2_3_x 2020-12-19 23:54:28 +01:00
Khopa
a1fed62591 Added "Golan Heights Campaign Lite" for the Syria map. 2020-12-19 23:54:02 +01:00
Dan Albert
778ed6ad91 Update 2.3 branch to 2.3.3 preview. 2020-12-19 13:48:44 -08:00
Dan Albert
7d539f5810 Remove pyproj from requirements.txt.
Not actually used.
2020-12-19 12:44:12 -08:00
Dan Albert
b407acbc07 Add section for 2.4 changelog. 2020-12-19 12:36:32 -08:00
Dan Albert
3260260dce Move more SAMs off runways in Syria Full.
(cherry picked from commit e5bca224e9)
2020-12-19 12:30:38 -08:00
Dan Albert
70c1290993 Note fixed SAM placement in changelog. 2020-12-19 12:30:38 -08:00
Dan Albert
6bae60c51e Note Iraq 1991 faction in changelog.
(cherry picked from commit 8447c563ea)
2020-12-19 12:30:38 -08:00
Emanuele Garofalo
a45adb6b3a New Faction Iraq 1991
(cherry picked from commit fd61a4b23a)
2020-12-19 12:30:38 -08:00
Dan Albert
476aaf5d3e Update changelog for #616.
(cherry picked from commit 3e4bb88089)
2020-12-19 12:30:38 -08:00
Dan Albert
58187b6969 Put back code to reserve beacon frequencies.
Fixes https://github.com/Khopa/dcs_liberation/issues/616

(cherry picked from commit 2f3f53a978)
2020-12-19 12:30:38 -08:00
Dan Albert
f3a3d81d96 Update beacon data.
(cherry picked from commit 89755b1005)
2020-12-19 12:30:38 -08:00
Dan Albert
7a40b54153 Update beacon importer to handle TheChannel.
TheChannel doesn't have message catalogs for English.

(cherry picked from commit a7203ea90a)
2020-12-19 12:30:38 -08:00
Dan Albert
9dd62d3538 Improve TOT planning.
Moves all TOT planning into the FlightPlan to clean up specialized
behavior and improve planning characteristics.

The important part of this change is that flights are now planning to
the mission time for their flight rather than the package as a whole.
For example, a TARCAP is planned based on the time from startup to the
patrol point, a sweep is planned based on the time from startup to the
sween end point, and a strike flight is planned based on the time from
startup to the target location. TOT offsets can be handled within the
flight plan.

As another benefit of theis cleanup, flights without hold points no
longer account for the hold time in their planning, so those flights are
planned to reach their targets sooner.

As a follow up TotEstimator can be removed, but I want to keep this low
impact for 2.3.2.

Fixes https://github.com/Khopa/dcs_liberation/issues/593

(cherry picked from commit 745dfc71bc)
2020-12-19 12:30:38 -08:00
walterroach
76e4a6ed83 Fix bad air defense location #617 2020-12-19 12:30:38 -08:00
Dan Albert
7a9eb06677 Add missing 2.3.2 change to changelog.
(cherry picked from commit 4eac743812)
2020-12-19 12:30:38 -08:00
Dan Albert
26f54e7619 Fix adding custom waypoints.
Fixes https://github.com/Khopa/dcs_liberation/issues/604

(cherry picked from commit 296e6e8e8f)
2020-12-19 12:30:38 -08:00
Khopa
117b7ae414 Changelog update 2020-12-19 12:30:38 -08:00
Khopa
baeac324d6 Updated version string 2020-12-19 12:30:38 -08:00
Khopa
0db0f003dc Added ZSU-57 sites 2020-12-19 12:30:38 -08:00
Khopa
2d4f341710 T72B3 and BTR-82A support 2020-12-19 12:30:38 -08:00
Khopa
b8a41dc937 pydcs version update to include new data export 2020-12-19 12:30:38 -08:00
walterroach
2f2bb0de4f Fix Ground units ... don't move forward #601 2020-12-19 12:30:38 -08:00
Dan Albert
3b76d7f47e Fail gracefully when out of radio channels.
Fixes https://github.com/Khopa/dcs_liberation/issues/598

(cherry picked from commit 498af28efb)
2020-12-19 12:30:38 -08:00
Dan Albert
10b74e507f Don't exclude BARCAP targets from culling.
Fixes https://github.com/Khopa/dcs_liberation/issues/597

(cherry picked from commit b9ade2295e)
2020-12-19 12:30:38 -08:00
Dan Albert
8a03a9462b Move more SAMs off runways in Syria Full.
(cherry picked from commit e5bca224e9)
2020-12-19 12:22:27 -08:00
Dan Albert
e5bca224e9 Move more SAMs off runways in Syria Full. 2020-12-19 12:22:08 -08:00
Dan Albert
197bf5d0cf Mark develop branch as 2.4 preview. 2020-12-19 12:07:35 -08:00
Dan Albert
d8b15ebcdb Merge branch 'develop_2_3_x' into develop 2020-12-19 12:03:21 -08:00
Dan Albert
078466241f Note fixed SAM placement in changelog. 2020-12-19 12:02:34 -08:00
Dan Albert
57c3eb5d2c Remove checkboxes from bug template.
Prevents all our bugs from looking complete or in progress because they have partially completed "tasks".
2020-12-19 11:59:28 -08:00
C. Perreau
a4876167c4 Create CODE_OF_CONDUCT.md 2020-12-19 20:57:55 +01:00
Dan Albert
a38a5654a9 Document current license.
Copied from the original shdwp repository.

https://github.com/Khopa/dcs_liberation/issues/243
2020-12-19 11:56:13 -08:00
Dan Albert
69a41879bb Note Iraq 1991 faction in changelog.
(cherry picked from commit 8447c563ea)
2020-12-19 11:53:49 -08:00
Emanuele Garofalo
e3524a506b New Faction Iraq 1991
(cherry picked from commit fd61a4b23a)
2020-12-19 11:53:46 -08:00
Dan Albert
8447c563ea Note Iraq 1991 faction in changelog. 2020-12-19 11:53:25 -08:00
Emanuele Garofalo
fd61a4b23a New Faction Iraq 1991 2020-12-19 11:52:54 -08:00
Dan Albert
9257311896 Update changelog for #616.
(cherry picked from commit 3e4bb88089)
2020-12-19 11:47:02 -08:00
Dan Albert
23e870e416 Put back code to reserve beacon frequencies.
Fixes https://github.com/Khopa/dcs_liberation/issues/616

(cherry picked from commit 2f3f53a978)
2020-12-19 11:47:01 -08:00
Dan Albert
8270b28d85 Update beacon data.
(cherry picked from commit 89755b1005)
2020-12-19 11:47:00 -08:00
Dan Albert
1a0889d3d9 Update beacon importer to handle TheChannel.
TheChannel doesn't have message catalogs for English.

(cherry picked from commit a7203ea90a)
2020-12-19 11:47:00 -08:00
Dan Albert
5382d99a94 Improve TOT planning.
Moves all TOT planning into the FlightPlan to clean up specialized
behavior and improve planning characteristics.

The important part of this change is that flights are now planning to
the mission time for their flight rather than the package as a whole.
For example, a TARCAP is planned based on the time from startup to the
patrol point, a sweep is planned based on the time from startup to the
sween end point, and a strike flight is planned based on the time from
startup to the target location. TOT offsets can be handled within the
flight plan.

As another benefit of theis cleanup, flights without hold points no
longer account for the hold time in their planning, so those flights are
planned to reach their targets sooner.

As a follow up TotEstimator can be removed, but I want to keep this low
impact for 2.3.2.

Fixes https://github.com/Khopa/dcs_liberation/issues/593

(cherry picked from commit 745dfc71bc)
2020-12-19 11:46:58 -08:00
Dan Albert
3e4bb88089 Update changelog for #616. 2020-12-19 11:46:19 -08:00
Dan Albert
2f3f53a978 Put back code to reserve beacon frequencies.
Fixes https://github.com/Khopa/dcs_liberation/issues/616
2020-12-19 11:45:33 -08:00
Dan Albert
89755b1005 Update beacon data. 2020-12-19 11:44:12 -08:00
Dan Albert
a7203ea90a Update beacon importer to handle TheChannel.
TheChannel doesn't have message catalogs for English.
2020-12-19 11:44:02 -08:00
walterroach
afb0ac14c4 Fix bad air defense location #617 2020-12-19 13:31:31 -06:00
Dan Albert
745dfc71bc Improve TOT planning.
Moves all TOT planning into the FlightPlan to clean up specialized
behavior and improve planning characteristics.

The important part of this change is that flights are now planning to
the mission time for their flight rather than the package as a whole.
For example, a TARCAP is planned based on the time from startup to the
patrol point, a sweep is planned based on the time from startup to the
sween end point, and a strike flight is planned based on the time from
startup to the target location. TOT offsets can be handled within the
flight plan.

As another benefit of theis cleanup, flights without hold points no
longer account for the hold time in their planning, so those flights are
planned to reach their targets sooner.

As a follow up TotEstimator can be removed, but I want to keep this low
impact for 2.3.2.

Fixes https://github.com/Khopa/dcs_liberation/issues/593
2020-12-19 11:20:16 -08:00
Dan Albert
82d9689d1b Remove links from bug report template.
Links don't render here.
2020-12-18 13:39:48 -08:00
Dan Albert
6afaef1654 Add issue templates. 2020-12-18 13:38:07 -08:00
Dan Albert
bb42d86012 Add more information to the readme.
Adds information about filing bugs/feature requests, finding the roadmap, and finding the preview builds.
2020-12-18 13:10:05 -08:00
Dan Albert
5b44580061 Add missing 2.3.2 change to changelog.
(cherry picked from commit 4eac743812)
2020-12-17 16:28:17 -08:00
Dan Albert
4eac743812 Add missing 2.3.2 change to changelog. 2020-12-17 16:27:54 -08:00
Dan Albert
ed8ab37bd5 Fix adding custom waypoints.
Fixes https://github.com/Khopa/dcs_liberation/issues/604

(cherry picked from commit 296e6e8e8f)
2020-12-17 16:26:19 -08:00
Dan Albert
563c3f0f1b Merge branch 'develop_2_3_x' into develop 2020-12-17 16:25:18 -08:00
Dan Albert
296e6e8e8f Fix adding custom waypoints.
Fixes https://github.com/Khopa/dcs_liberation/issues/604
2020-12-17 16:21:31 -08:00
Khopa
334aab2755 Changelog update 2020-12-18 01:09:14 +01:00
Khopa
419f4f3156 Updated version string 2020-12-18 01:08:34 +01:00
Khopa
ec5a26e8dd Added ZSU-57 sites 2020-12-18 01:05:06 +01:00
Khopa
2b7cd36eea T72B3 and BTR-82A support 2020-12-18 00:42:44 +01:00
Khopa
2f11731052 pydcs version update to include new data export 2020-12-18 00:25:50 +01:00
Khopa
23a0846533 Merge branch 'develop_2_3_x' into develop
# Conflicts:
#	changelog.md
#	qt_ui/windows/QLiberationWindow.py
#	resources/campaigns/syria_full_map_remastered.json
2020-12-18 00:23:53 +01:00
walterroach
666858f8e2 Fix Ground units ... don't move forward #601 2020-12-17 02:03:02 -06:00
Dan Albert
2288b7f7b2 Fail gracefully when out of radio channels.
Fixes https://github.com/Khopa/dcs_liberation/issues/598

(cherry picked from commit 498af28efb)
2020-12-16 22:24:07 -08:00
Dan Albert
498af28efb Fail gracefully when out of radio channels.
Fixes https://github.com/Khopa/dcs_liberation/issues/598
2020-12-16 19:12:18 -08:00
Khopa
3902ab3375 Fixed : BMD_1 IFV missing from db. Fixed error preventing mission generation when the role of an unit can not be determined.
(cherry picked from commit 4112a86fe9)
2020-12-16 19:08:42 -08:00
Dan Albert
6bb0bdf66e Don't exclude BARCAP targets from culling.
Fixes https://github.com/Khopa/dcs_liberation/issues/597

(cherry picked from commit b9ade2295e)
2020-12-16 18:53:17 -08:00
Dan Albert
b9ade2295e Don't exclude BARCAP targets from culling.
Fixes https://github.com/Khopa/dcs_liberation/issues/597
2020-12-16 18:52:12 -08:00
C. Perreau
44b5f5a919 Merge pull request #596 from Khopa/develop_2_3_x
Release 2.3.1
2020-12-16 21:45:30 +01:00
Khopa
17d37494c2 2.3.1 changelog 2020-12-16 21:14:25 +01:00
Khopa
f0d81e98a0 About dialog update 2020-12-16 21:12:01 +01:00
Khopa
e3b13f7b4a UX : Display a warning message when attempting to buy more aircraft at an already full airfield. 2020-12-16 21:08:48 +01:00
Khopa
ba2686630a Updated version number 2020-12-16 20:51:36 +01:00
Emanuele Garofalo
e195cfa6a0 Replaced previous Syria full map by the new version by Hawkmoon 2020-12-16 20:49:03 +01:00
Emanuele Garofalo
b9fbd1906f Add files via upload
Fixed NATO DESERT STORM FACTIO
2020-12-16 20:47:04 +01:00
Emanuele Garofalo
1f611bafef Add files via upload
New Syria Full map Remastered
2020-12-16 20:46:54 +01:00
C. Perreau
af7faa59dc Merge pull request #595 from ITAHawkmoon/develop
Syria Full map Remastered & NATO Desert Storm fixed
2020-12-16 20:43:34 +01:00
Emanuele Garofalo
0b21ee46ea Delete NATO Desert Storm.json
old faction
2020-12-16 16:23:40 +01:00
Emanuele Garofalo
f64996a350 Add files via upload
Fixed NATO DESERT STORM FACTIO
2020-12-16 16:22:37 +01:00
Emanuele Garofalo
d7cccd1980 Add files via upload
New Syria Full map Remastered
2020-12-16 16:20:45 +01:00
walterroach
b9467d9236 Fix broken Full Caucasus Map frontline 2020-12-16 09:08:13 -06:00
walterroach
69096b15ae Fix broken Full Caucasus Map frontline 2020-12-16 09:06:51 -06:00
Dan Albert
a075e62bad Fix easy going CAPs.
Fixes https://github.com/Khopa/dcs_liberation/issues/592

(cherry picked from commit 1ebe367e07)
2020-12-15 22:48:21 -08:00
Dan Albert
1ebe367e07 Fix easy going CAPs.
Fixes https://github.com/Khopa/dcs_liberation/issues/592
2020-12-15 22:48:05 -08:00
Khopa
db229f25bf Changelog update 2020-12-16 00:28:59 +01:00
Khopa
787c93b9d4 Another yaml release update 2020-12-15 23:55:13 +01:00
Khopa
63953992a9 Fix release yml syntax error 2020-12-15 23:46:01 +01:00
Khopa
567cb0c0b6 Removed exe installer from release process (To avoid having people installing it in C:/Program Files 2020-12-15 23:38:59 +01:00
C. Perreau
f37999a3ef Merge pull request #590 from Khopa/develop_2_3_x
Release 2.3.0
2020-12-15 23:36:08 +01:00
Khopa
545761e3d4 2.3.0 changelog update 2020-12-15 23:22:21 +01:00
C. Perreau
97ea67d01d Merge pull request #533 from walterroach/changelog_audit
WIP: 2.3.0 Changelog Audit
2020-12-15 23:19:13 +01:00
Khopa
561d679a62 Merge remote-tracking branch 'khopa/develop_2_3_x' into develop_2_3_x 2020-12-15 23:14:51 +01:00
Khopa
991cd91dd4 Possible to setup Runway attack payload, default to strike payload otherwise; 2020-12-15 23:14:36 +01:00
walterroach
9e51ff0253 Merge pull request #589 from walterroach/sead_flyover
SEAD Tasking
2020-12-15 15:25:31 -06:00
Khopa
1a1e55e16c About dialog update 2020-12-15 21:01:13 +01:00
Khopa
2fe4a39784 Changed default payload for C-130 mod 2020-12-15 21:00:56 +01:00
Khopa
7d81b9ef5c C-130 Plugin by Plob 2020-12-15 20:19:42 +01:00
Khopa
b355b6dc60 Updated Persian Gulf Full Map in new miz format by Plob. 2020-12-15 20:19:36 +01:00
Khopa
819148762b Hercules 130 pydcs extensions updated to version 6.0 (MOAB 💣 support) 2020-12-15 20:19:35 +01:00
Khopa
4112a86fe9 Fixed : BMD_1 IFV missing from db. Fixed error preventing mission generation when the role of an unit can not be determined. 2020-12-15 20:09:41 +01:00
walterroach
7838c9b49b Revert DEAD flyover 2020-12-15 12:58:55 -06:00
Khopa
ca5a70e3bc pydcs version update 2020-12-15 19:57:32 +01:00
Khopa
d6376c3a91 pydcs version update 2020-12-15 19:57:04 +01:00
Khopa
e134143f16 Fixed possible crash in mission generation when generating a mission with a faction that doesn't have mandpad and with the disabled infantry option checked.
(cherry picked from commit 31fdd24c3e)
2020-12-15 10:20:41 -08:00
Dan Albert
0f1577d314 Fix crash in new game generation.
Fixes https://github.com/Khopa/dcs_liberation/issues/583

(cherry picked from commit 793b356c01)
2020-12-15 10:20:38 -08:00
Dan Albert
793b356c01 Fix crash in new game generation.
Fixes https://github.com/Khopa/dcs_liberation/issues/583
2020-12-15 10:16:21 -08:00
walterroach
a36858f3ea Change SEAD target waypoint to flyover 2020-12-15 11:53:12 -06:00
Dan Albert
aaa6637435 Don't cull objects near package targets.
https://github.com/Khopa/dcs_liberation/issues/578
Fixes https://github.com/Khopa/dcs_liberation/issues/262

(cherry picked from commit 25efdd3d4f)
2020-12-14 23:55:59 -08:00
Dan Albert
f0f6739cf8 Don't cull flights.
This was made largely pointless in 2.2, since the AI won't plan a dozen
CAPs 300nm from the front line any more. Culling flights mostly just
confuses players and breaks the planning AI.

If we do want to limit flight counts, we need to do that by limiting
flight counts. The AI will try to put their aircraft as close to the
mission as possible, so culling will do very little to stop them from
spawning 50 flights at the front-line attached airfield.

https://github.com/Khopa/dcs_liberation/issues/578
(cherry picked from commit 0b2483ea15)
2020-12-14 23:55:58 -08:00
Dan Albert
25efdd3d4f Don't cull objects near package targets.
https://github.com/Khopa/dcs_liberation/issues/578
Fixes https://github.com/Khopa/dcs_liberation/issues/262
2020-12-14 23:47:31 -08:00
Dan Albert
0b2483ea15 Don't cull flights.
This was made largely pointless in 2.2, since the AI won't plan a dozen
CAPs 300nm from the front line any more. Culling flights mostly just
confuses players and breaks the planning AI.

If we do want to limit flight counts, we need to do that by limiting
flight counts. The AI will try to put their aircraft as close to the
mission as possible, so culling will do very little to stop them from
spawning 50 flights at the front-line attached airfield.

https://github.com/Khopa/dcs_liberation/issues/578
2020-12-14 23:35:30 -08:00
Dan Albert
2c38ce910c Add off-map spawns to the Black Sea campaign.
Fixes https://github.com/Khopa/dcs_liberation/issues/561

(cherry picked from commit 4d26ec0789)
2020-12-14 22:36:57 -08:00
Dan Albert
4d26ec0789 Add off-map spawns to the Black Sea campaign.
Fixes https://github.com/Khopa/dcs_liberation/issues/561
2020-12-14 22:36:39 -08:00
Dan Albert
edba923f2f Fix budget update for non-base SAMs.
Just emit the signal to update the budget rather than trying to figure
out the heirarchy of the UI.

Fixes https://github.com/Khopa/dcs_liberation/issues/581

(cherry picked from commit 7d907aac0f)
2020-12-14 17:41:14 -08:00
Dan Albert
7d907aac0f Fix budget update for non-base SAMs.
Just emit the signal to update the budget rather than trying to figure
out the heirarchy of the UI.

Fixes https://github.com/Khopa/dcs_liberation/issues/581
2020-12-14 17:00:56 -08:00
Khopa
d6981550a8 C-130 Plugin by Plob 2020-12-14 22:23:21 +01:00
Khopa
8b0636367b Updated Persian Gulf Full Map in new miz format by Plob. 2020-12-14 22:09:37 +01:00
Khopa
a6c9d0f9bc Hercules 130 pydcs extensions updated to version 6.0 (MOAB 💣 support) 2020-12-14 21:41:30 +01:00
walterroach
4b0d2f7abc Merge pull request #576 from walterroach/explict_resources
Explicit resources
2020-12-13 18:04:58 -06:00
Dan Albert
dd28781b69 Fix kneeboard ILS for many airfields.
Fixes https://github.com/Khopa/dcs_liberation/issues/564

(cherry picked from commit 61ebe9780e)
2020-12-13 15:12:39 -08:00
Dan Albert
ece56032f1 Fix missing import for new package on enemy CVs.
Fixes https://github.com/Khopa/dcs_liberation/issues/570

(cherry picked from commit e887082501)
2020-12-13 15:12:39 -08:00
Dan Albert
61ebe9780e Fix kneeboard ILS for many airfields.
Fixes https://github.com/Khopa/dcs_liberation/issues/564
2020-12-13 13:48:08 -08:00
Dan Albert
e887082501 Fix missing import for new package on enemy CVs.
Fixes https://github.com/Khopa/dcs_liberation/issues/570
2020-12-13 13:44:10 -08:00
walterroach
8e3039dd37 Fix Zero Fuel Start #559 2020-12-13 15:41:13 -06:00
walterroach
1d1c130d19 Add missing WW2 units to db
Fixes #571
2020-12-13 15:03:19 -06:00
Khopa
1fd3f70eec Fix usa C-130 faction name being the same as usa_2005 2020-12-13 12:41:49 -06:00
walterroach
d9ea33cbb9 Fix diversified frontline distance 2020-12-13 12:21:09 -06:00
Khopa
80778aa267 Fix usa C-130 faction name being the same as usa_2005 2020-12-13 17:21:13 +01:00
C. Perreau
445cb4f146 Merge pull request #569 from ITAHawkmoon/ITAHawkmoon-update-db
Update db and new faction.py
2020-12-13 16:43:09 +01:00
C. Perreau
95db2aa14f Merge branch 'develop' into ITAHawkmoon-update-db 2020-12-13 16:25:52 +01:00
Emanuele Garofalo
4b0fc637eb Update NATO Desert Storm.json
syntax error
2020-12-13 12:43:47 +01:00
Emanuele Garofalo
48d6b4cfa1 Add files via upload
New faction NATO desert Storm
2020-12-13 12:33:21 +01:00
Emanuele Garofalo
fc11182bbe Update db.py
added some plane missing from the db
2020-12-13 12:29:58 +01:00
Dan Albert
25b72e1af4 Fix UI exception for custom flight plans.
(cherry picked from commit 1848338ef7)
2020-12-12 18:27:55 -08:00
Dan Albert
1848338ef7 Fix UI exception for custom flight plans. 2020-12-12 18:27:30 -08:00
Dan Albert
ff0446cc12 Fix division by zero for very close waypoints.
Fixes https://github.com/Khopa/dcs_liberation/issues/557

(cherry picked from commit 08ceb57c31)
2020-12-12 14:51:32 -08:00
Dan Albert
08ceb57c31 Fix division by zero for very close waypoints.
Fixes https://github.com/Khopa/dcs_liberation/issues/557
2020-12-12 14:51:03 -08:00
Dan Albert
affb332eb9 Fix duplication of pydcs translation keys.
We shouldn't be constructing these by hand, and the first argument is
not the value of the string. I'm not really sure why the current code
works at all. We probably do this in other places and should clean that
up, but for now this should fix Tauntaun.

Fixes https://github.com/Khopa/dcs_liberation/issues/528
2020-12-12 14:24:30 -08:00
Dan Albert
6455c38ff4 Fix duplication of pydcs translation keys.
We shouldn't be constructing these by hand, and the first argument is
not the value of the string. I'm not really sure why the current code
works at all. We probably do this in other places and should clean that
up, but for now this should fix Tauntaun.

Fixes https://github.com/Khopa/dcs_liberation/issues/528

(cherry picked from commit affb332eb9)
2020-12-12 14:24:09 -08:00
Dan Albert
f62c2fbabb Show player waypoint timing in the briefing.
Fixes https://github.com/Khopa/dcs_liberation/issues/536

(cherry picked from commit d5276c9d4a)
2020-12-12 14:18:36 -08:00
Dan Albert
d5276c9d4a Show player waypoint timing in the briefing.
Fixes https://github.com/Khopa/dcs_liberation/issues/536
2020-12-12 14:17:48 -08:00
walterroach
4c0fc5a407 Adjust tanker speed and altitudes #444 2020-12-12 16:08:37 -06:00
walterroach
f608cd5aef fix mypy issues 2020-12-12 15:45:55 -06:00
walterroach
0371b62acb Merge branch 'ground_tasking' into develop_2_3_x 2020-12-12 15:41:00 -06:00
walterroach
eddd66b5c4 West coast Caucasus Landmap improvements 2020-12-12 15:35:12 -06:00
walterroach
c8e71d269b Improve Ground Unit Behavior
* Prevent spinning units
* Prevent units spawning in exclusion zone
* Prevent units from moving into exclusion zone
2020-12-12 15:33:26 -06:00
Dan Albert
ae034d5387 Show takeoff time in waypoint list.
https://github.com/Khopa/dcs_liberation/issues/536
(cherry picked from commit 07e5c568c4)
2020-12-12 13:22:57 -08:00
Dan Albert
07e5c568c4 Show takeoff time in waypoint list.
https://github.com/Khopa/dcs_liberation/issues/536
2020-12-12 13:22:23 -08:00
walterroach
817d6a0e15 Merge remote-tracking branch 'upstream/develop_2_3_x' into ground_tasking 2020-12-12 12:44:47 -06:00
Khopa
31fdd24c3e Fixed possible crash in mission generation when generating a mission with a faction that doesn't have mandpad and with the disabled infantry option checked. 2020-12-12 15:27:36 +01:00
Khopa
bfa0e4ba49 Made normandy campaign less cluttered 2020-12-12 15:22:51 +01:00
Khopa
dff98f0b53 Made normandy campaign less cluttered 2020-12-12 15:22:35 +01:00
Khopa
d0856ff279 pydcs update 2020-12-12 14:36:57 +01:00
Khopa
c89ff2c3d6 pydcs update 2020-12-12 14:36:40 +01:00
Khopa
4ec88d524a Fixed invasion_from_turkey inverted setup. 2020-12-12 14:25:30 +01:00
Khopa
d6b762efa7 Fixed invasion_from_turkey inverted setup. 2020-12-12 14:25:12 +01:00
Khopa
b476a26759 Fix russia_small campaign not being invertable 2020-12-12 13:23:32 +01:00
Khopa
48c218b430 Fix russia_small campaign not being invertable 2020-12-12 13:22:01 +01:00
Dan Albert
1a062e2170 Set carrier speed for recovery.
https://github.com/Khopa/dcs_liberation/issues/543
(cherry picked from commit d316836e90)
2020-12-11 19:19:48 -08:00
Dan Albert
fe658eb877 Always aim the carrier into the wind.
https://github.com/Khopa/dcs_liberation/issues/543
(cherry picked from commit 8443f61f0a)
2020-12-11 19:19:47 -08:00
Dan Albert
d316836e90 Set carrier speed for recovery.
https://github.com/Khopa/dcs_liberation/issues/543
2020-12-11 19:19:08 -08:00
Dan Albert
8443f61f0a Always aim the carrier into the wind.
https://github.com/Khopa/dcs_liberation/issues/543
2020-12-11 19:01:35 -08:00
Khopa
feed55186f Migrated "polygon" code to shapely 2020-12-12 02:31:43 +01:00
walterroach
56591b8655 broken waypoint finder 2020-12-11 18:17:44 -06:00
Khopa
7c52ca15f3 C-130 example faction 2020-12-12 00:54:19 +01:00
Khopa
06c751f214 Migrated invasion from turkey campaign to new miz format. 2020-12-12 00:51:46 +01:00
Khopa
9d774eaad8 Fixed culling display distance. Allowed smaller distance for culling (useful for WW2 maps) 2020-12-11 22:39:09 +01:00
Khopa
babfd4abda Merge remote-tracking branch 'khopa/develop' into develop 2020-12-11 22:04:50 +01:00
Khopa
a029c165a0 Normandy campaigns and terrain update 2020-12-11 22:04:33 +01:00
walterroach
9b51533d96 Caucasus Terrain and frontline tweaks 2020-12-11 14:35:06 -06:00
Khopa
e1b7e0eb00 Merge remote-tracking branch 'khopa/develop' into develop 2020-12-11 20:56:30 +01:00
Khopa
508a5693c9 Fixed duplicated tgo issue with chinese and russian navy generators. 2020-12-11 20:55:59 +01:00
walterroach
67806f3d76 Dec 10 review 2020-12-11 11:53:25 -06:00
walterroach
f687a30c7e Merge pull request #525 from root0fall/fix_221
refresh budget after purchase from sub-dialog
2020-12-11 11:43:16 -06:00
Dan Albert
15615a1077 Add the Black Sea campaign.
Similar to Western Georgia, but extended further up the coast, and
without the initial battle in Georgia itself.
2020-12-11 01:15:28 -08:00
Dan Albert
905175c210 Add --invert to the game generator. 2020-12-11 01:15:28 -08:00
Dan Albert
409e070887 Generate required IADS near FOBs. 2020-12-11 01:09:14 -08:00
Dan Albert
f659dc1f76 Reference point rework.
* Introduce a real type.
* Rewrite _transform_point to make use of Point.
* Add shift modifier for large (10 pixel) adjustements to reference
  points. Unmodified behavior is now single pixel.
* Use WASD for moving the second point (shift modified numpad keys don't
  seem to work).
* Add a debug option to draw transformed reference points to check for
  errors. If they don't overlap, something is wrong.
* Cleaned up all the existing reference points. Caucasus in particular
  is now *much* better.

As an added bonus, the cleanup for carrier movement projection now also
shows an invalid destination when the destination is on land.
2020-12-10 21:08:38 -08:00
root0fall
b8922b39fd add type hinting to update_dialog_budget() 2020-12-11 09:24:21 +08:00
Khopa
8137d57cdf Dev : Improved Reference point setup mode, made it easier to setup point with polygon map appearing as transparent overlay, and displaying reference points on the map [Press Shift+R to toggle the mode]) 2020-12-11 00:08:08 +01:00
Khopa
bf290ac1a9 Improved Normandy reference points 2020-12-10 23:34:06 +01:00
Khopa
77fda00233 Invasion of iran lite campaign update. 2020-12-10 23:19:51 +01:00
Khopa
0255088e30 Fixed buildings showing in airbase defense menu for FOB points. 2020-12-10 23:19:26 +01:00
Khopa
b74d8b12d0 Fixed duplicated unit names in german WW2 Flak site groups. (Which would cause duplicated unit error in unitmap.py when trying to start the mission) 2020-12-10 22:32:24 +01:00
Khopa
2834f2982c Fix error on first start because of new last_save_file parameter. 2020-12-10 21:11:24 +01:00
Khopa
7d07faa5fb Fix last commit error 2020-12-10 21:09:19 +01:00
Khopa
fd70f0fc4a Pydcs extensions and db info added for C-130 Hercules mod ✈️ 2020-12-10 21:07:04 +01:00
walterroach
7744f84e85 Changelog audit 2020-12-10 14:04:48 -06:00
walterroach
441ef79aa4 Merge pull request #529 from walterroach/you_won
You Won Message
2020-12-09 18:30:47 -06:00
walterroach
c54f6ba4d2 Use set instead of dict 2020-12-09 18:25:24 -06:00
walterroach
61ecdfc48c Merge branch 'develop' into you_won 2020-12-09 18:12:18 -06:00
Khopa
5e2b259af1 Updated changelog 2020-12-10 00:20:22 +01:00
Khopa
1258f3e17c Carrier and Tarawa are now fully moveable 2020-12-10 00:13:34 +01:00
Khopa
44ed895277 Merge remote-tracking branch 'khopa/develop' into develop 2020-12-09 22:55:24 +01:00
Khopa
c74e18e449 Target positions for ship movement are now properly projected on DCS game X,Y plane 2020-12-09 22:55:11 +01:00
walterroach
2ea3f914f0 You Won message #419 2020-12-09 14:37:17 -06:00
Khopa
d6e4a50064 QOL : On launch, auto load the latest saved game. 2020-12-09 21:01:32 +01:00
walterroach
6296896471 Add special fuel case for C101 #492 2020-12-08 18:26:37 -06:00
walterroach
473cda971a Fix typing mistake 2020-12-08 17:16:09 -06:00
walterroach
cf570adabe Refactor armor
Break `plan_action_for_groups` into smaller methods

Add type hinting and cleanup formatting
2020-12-08 16:55:04 -06:00
walterroach
02196f2883 Change ground unit waypoints to offroad Ground Unit Waypoint
"On-Road" #495
2020-12-08 15:11:47 -06:00
root0fall
8b49752401 refresh budget after purchase from subwindow
fixes https://github.com/Khopa/dcs_liberation/issues/221
2020-12-08 20:47:02 +08:00
Dan Albert
8c64867918 Fix duplicate names of AAA units.
Fixes https://github.com/Khopa/dcs_liberation/issues/517
2020-12-06 22:14:56 -08:00
Dan Albert
e544063c40 Update Skynet and Mist. 2020-12-06 20:20:08 -08:00
Dan Albert
edfaaacd04 Determine player vs enemy early in debriefing.
This way the results of committing the debriefing can't alter the view
of the debriefing. It looks like it was probably that case that
debriefing information displays (but not the committed results) would be
incorrect after a base capture because the results might be shown after
the results were committed.

Maybe fixes https://github.com/Khopa/dcs_liberation/issues/513
2020-12-06 20:20:08 -08:00
Dan Albert
84b8613cf5 Cleanup debriefing signals.
We don't need most of this information.
2020-12-06 20:20:08 -08:00
walterroach
aea82e2266 Add FOB capture 2020-12-06 21:45:21 -06:00
Dan Albert
b0b9c1c8e6 Fix deletion of waypoints.
`FlightPlan.waypoints` returns a list created from the iterator now, so
removing items from that list does not actually alter the flight plan.

Fixes https://github.com/Khopa/dcs_liberation/issues/489
2020-12-06 15:32:28 -08:00
Dan Albert
b5ff32c5b6 Show patrol end time on the kneeboard.
Fixed https://github.com/Khopa/dcs_liberation/issues/421
2020-12-06 15:01:21 -08:00
Dan Albert
e0223ded54 Fix display of AA ranges for most ship types.
Fixes https://github.com/Khopa/dcs_liberation/issues/390
2020-12-06 14:22:33 -08:00
Khopa
2012ad0aa3 Fixed cursor / single click behaviour on map. 2020-12-06 21:12:44 +01:00
Dan Albert
15d72a8dcb Fix turn number off by one.
Fixes https://github.com/Khopa/dcs_liberation/issues/482
2020-12-06 01:08:23 -08:00
Dan Albert
e525b11695 Remove scary label from automation options. 2020-12-06 01:06:12 -08:00
Dan Albert
8f30e60e1b Use unplanned missions to guide aircraft purchase.
AI aircraft purchase decisions are now driven by the missions that the
flight planner was unable to fulfill. This way we're buying the aircraft
we actually need instead of buying them at random, in the locations we
need them, in the order they're needed.

There's a bit more that could be improved here:

* Unused aircraft could be ferried to where they would be more useful.
* Partial squadrons could be completed rather than buying a whole flight
  at a time.
* Aircraft could be ranked by their usefulness so we're not buying so
  many Hueys when a Hornet would do better.
* Purchase a buffer of CAP capable aircraft in case too many are shot
  down and they are not available next turn.

https://github.com/Khopa/dcs_liberation/issues/361
2020-12-06 00:59:04 -08:00
Dan Albert
ce977ac937 Remove the enemy forces multiplier option.
This didn't do what it claimed to (it actually just determines the
threshold for whether a control point shoudl be a *preferred* canidate
for purchasing ground units), and the income multipliers offer the
intended behavior.
2020-12-05 23:55:33 -08:00
Dan Albert
aa9ffa0855 Obey parking limits when auto-buying aircraft.
Fixes https://github.com/Khopa/dcs_liberation/issues/137
2020-12-05 23:36:51 -08:00
Dan Albert
6a8ca810ff Add auto-procure option to the CLI generator. 2020-12-05 23:20:58 -08:00
Dan Albert
f2d2fd7014 Add automation options to the new game wizard.
To have an effect on turn zero these need to be enabled in the wizard.
Since the last page was getting quite full I've split it into two pages:
one for the objective generation options and a second for the difficulty
and player assist options.

I also added an option to set the inital budget for opfor.
2020-12-05 23:16:17 -08:00
Dan Albert
ddd06b3162 Move mid-game option to the map options page. 2020-12-05 22:47:26 -08:00
Dan Albert
d519aa1dad Move the AI to the normal procurement system.
The procurement AI now uses the same system as the players. Orders are
placed and take a turn to fulfill.

This has a few advantages:

* We no longer need special case purchase logic for the turn 0
  population of opfor airbases.
* Players using auto-purchase can cancel orders they don't like.
2020-12-05 22:41:05 -08:00
Dan Albert
7226359e64 Avoid negative AI produrement budgets.
This also fixes the double charging that the AI was suffering from, so
they have a much better chance of actually affording planes now.

Fixes https://github.com/Khopa/dcs_liberation/issues/504
2020-12-05 21:50:30 -08:00
Dan Albert
f396ff7f12 Add procurement automation for players.
The allows the players to use the same auto-purchase mechanics that the
AI uses. The behavior is very bad, but it's no worse than what OPFOR
deals with.

There's a lot that needs to be improved before this is really a good
choice for the player:

* Option to adjust budget balance between front lines and airbases.
* Disallow negative budgets (which incidentally will cause more aircraft
  to be purchased, since the armor purchases currently accidentally
  spend the aircraft budget).
* Buy less randomly: https://github.com/Khopa/dcs_liberation/issues/361.
* Obey parking limits.
* Use the delivery events rather than instant delivery (also allows the
  player to cancel orders they don't want).

Fixes https://github.com/Khopa/dcs_liberation/issues/362
2020-12-05 21:18:23 -08:00
Dan Albert
1adee0af17 Factor AI purchases out of Game.
For now this is mostly behavior preserving. I slightly improved the
ability to buy units when multiple front lines exist by removing full
bases as candidates, but it should be a minor change at best. A larger
improvement will come later.

This is also written such that it will work for the player as well. The
procurer currently runs for the player but with all the options off, so
it does nothing. The next patch allows adds options for the player to
use auto-procurement.
2020-12-05 20:42:02 -08:00
Dan Albert
bac47dad83 Add settings for scaling income.
This adds both player and enemy income multiplier options. Note that
previously the AI was only getting 75% of their income. I've changed
that to give them their full income by default since the player can now
influence it.
2020-12-05 18:06:57 -08:00
Dan Albert
f1a2602cfd Move generator only settings out of Settings. 2020-12-05 16:56:14 -08:00
Dan Albert
b8e64d4369 Revert "Revert "Migrate buildings and TGOs to unit map.""
With fixed tracking for TGO groups and fortification "buildings".

Fixes https://github.com/Khopa/dcs_liberation/issues/485

This reverts commit 72ac8ca872.
2020-12-05 14:26:16 -08:00
Dan Albert
72ac8ca872 Revert "Migrate buildings and TGOs to unit map."
Not registering kills correctly. It was in my limited testing, so need
to dig deeper.

https://github.com/Khopa/dcs_liberation/issues/494

This reverts commit 90697194a1.
2020-12-04 23:57:58 -08:00
Dan Albert
ccb41829c9 Mark an optional property as optional. 2020-12-04 01:10:23 -08:00
Dan Albert
90697194a1 Migrate buildings and TGOs to unit map.
Fixes https://github.com/Khopa/dcs_liberation/issues/485.
2020-12-04 01:10:18 -08:00
Dan Albert
13f4baa34e Fix duplicate name of Patriot unit.
Caught by the `UnitMap`. This does break saves that have live Patriot
STRs.
2020-12-04 01:03:57 -08:00
Khopa
76840ff5c2 Added display settings to the toolbar. 2020-12-03 22:48:28 +01:00
Khopa
72ac806cb8 Possible to plan ships movements on the map (UI only) 2020-12-03 01:01:15 +01:00
Khopa
bf275fe564 Added FOB on Iran Invasion lite campaign. 2020-12-02 00:37:14 +01:00
Khopa
68818ae50d Syria 2011 country changed to combined joint task force red, as some units are not available to Syria in DCS. 2020-12-01 23:45:28 +01:00
Khopa
7315d097c2 Regorganized difficulty page of settings window 2020-12-01 23:42:34 +01:00
Khopa
cdf28700cf Possible to spawn manpads on frontline even if infantry squads are disabled. 2020-12-01 23:14:07 +01:00
Khopa
948c1d0bb0 Fixed unused aircraft not using skins setup in factions files. 2020-12-01 22:50:11 +01:00
Khopa
de0a3f929c Made WW2 Flak sites more compact, so it's easier to fit them in fields on the Normandy map. 2020-12-01 13:49:09 +01:00
Khopa
c3023a9f99 Normandy terrain data update 2020-12-01 13:39:54 +01:00
Dan Albert
4f37610dfb Convert front line units to UnitMap.
https://github.com/Khopa/dcs_liberation/issues/485
2020-12-01 01:25:51 -08:00
walterroach
aef4316f72 Merge pull request #487 from walterroach/fob
FOB Control Points
2020-11-30 23:29:15 -06:00
walterroach
378dbf254a Remove commented code 2020-11-30 23:28:39 -06:00
Khopa
f0b6a37ce2 Migrated normandy small to new miz format. 2020-12-01 00:36:49 +01:00
Khopa
ff12a120e6 Migrated invasion of iran campaign to miz format. 2020-11-30 23:12:01 +01:00
walterroach
d04be4d71b Merge branch 'develop' into fob 2020-11-30 12:18:38 -06:00
Dan Albert
581aaaad28 Merge develop 2020-11-30 12:18:31 -06:00
walterroach
453eb9feb4 revert unrelated changes 2020-11-30 12:10:03 -06:00
walterroach
4059ee44b8 pg campaign 2020-11-29 22:47:18 -06:00
walterroach
7a222ecfa0 gulf landmap update 2020-11-29 22:47:03 -06:00
Dan Albert
be15e9adf2 Adjust IADS for Inherent Resolve.
Had too many long range SAMs and not enough medium range SAMs. Balanced
that out a bit and added many of the fixed SAM positions on the map as
possible target locations.
2020-11-29 17:52:57 -08:00
Dan Albert
2fd097c613 Remove SA-15 from Syria 2011.
It looks like the SA-15s were something delivered a few years later than
this. The SA-10s are also more recent, but they have been using SA-5s
for quite some time and until those are available in DCS the SA-10 makes
a reasonable approximation.
2020-11-29 16:33:30 -08:00
Ignacio Muñoz Fernandez
66ee5f5392 Bingo & Joker Fuel for Flight Plans (#480)
Add bingo and joker fuel information to the kneeboard.
2020-11-29 14:53:15 -08:00
walterroach
3bb08f8d30 FOB names
Remove icon
2020-11-29 16:26:08 -06:00
walterroach
d7787adddc Generators for FOBs and their defenses 2020-11-29 14:21:02 -06:00
walterroach
1f37b879b1 Add FOB ControlPoint type 2020-11-29 12:16:39 -06:00
Khopa
45ce28f9bf Another big Persian gulf terrain update (Iran) 2020-11-29 15:19:28 +01:00
Khopa
4e87bed4e5 Migrated invasion of iran lite campaign to new miz format. 2020-11-29 15:00:56 +01:00
Khopa
a7421fc670 Updated Persian Gulf terrain data to include more exclusions zones. 2020-11-29 15:00:03 +01:00
Khopa
208a7550ef Added C101CC to list of antiship capable aircrafts. 2020-11-29 13:16:35 +01:00
Khopa
37e23f70d6 Put manpads in modern faction's infantry squads. And added a setting to disable it. 2020-11-29 13:11:49 +01:00
Dan Albert
7daefa8ae5 Fix off by one in briefing waypoint numbers.
Fixes https://github.com/Khopa/dcs_liberation/issues/441
2020-11-28 20:28:35 -08:00
walterroach
292ac42003 Fix campaigns without frontline.
* Missions will now generate without a frontline conflict
* Bulls is now defined as the nearest opposing airfield for each side
2020-11-28 19:53:01 -06:00
walterroach
c501c45c52 Update channel landmap
Ensures a valid frontline location can be found on the Dunkirk campaign
between Saint Omer and Dunkirk
2020-11-28 19:42:10 -06:00
walterroach
29b894f8b0 Fix odd frontline unit spawns
* Modfied frontline vector to ensure start point stays outside of
  exclusion zone.  (Previously it could be up to 100m inside)

* Change randomization in offset distance from frontline to be based
  on a percentage of the unit type's fixed offset instead of the
  width of frontline.
  (Prevents units from being far from their expected distance from
  frontline)

* Change visualgen to use the same frontline vector calculation as the
  unit spawns
2020-11-28 18:43:32 -06:00
Dan Albert
55573bf40a Clear loaded scripts before generation.
Every mission generated after the first each time Liberation was
skipping all of the plugins (including the base plugin) because they'd
already been loaded on a previous generation and the list wasn't
cleared.
2020-11-28 15:39:33 -08:00
Khopa
f2c2ef82c5 Fixed unused aircraft using the same slots as used aircrafts. 2020-11-28 22:29:09 +01:00
Khopa
d6b33d353c Now possible to get CVN-75 Harry S Truman supercarrier generated for US faction 2020-11-28 21:57:58 +01:00
Khopa
2ed1c36c54 Fixed infantry for red player being the same units as blue faction. Fixed skill for enemy vehicles being the same as player's one. 2020-11-28 21:34:21 +01:00
Khopa
7cbcbc1171 Migrated desert war to new miz format 2020-11-28 19:19:33 +01:00
Khopa
b74dcfa053 Battle of britain campaign migrated to new miz format. 2020-11-28 18:03:55 +01:00
Khopa
62bf7eb227 Dunkirk campaign migrated to the new miz format. 2020-11-28 17:41:20 +01:00
Dan Albert
07bfe8e29a Move a SAM that's occluding Palmyra. 2020-11-27 20:11:21 -08:00
Dan Albert
0b258997dd Update faction air defenses. 2020-11-27 19:56:49 -08:00
Dan Albert
69421ad7a1 Fix faction info for air defense changes. 2020-11-27 18:12:41 -08:00
Dan Albert
fdf571c016 Clean up faction air defenses vs shorads vs SAMs. 2020-11-27 18:10:13 -08:00
Dan Albert
bd60760f9d Merge faction sams and shorads into air_defenses.
Fixes https://github.com/Khopa/dcs_liberation/issues/473. Air defenses
for bases, strike locations, and fixed IADS will now all downgrade to
lower tier systems as needed. Strike locations will still be spawned as
an equally weighted random generator from either the medium or long
range groups, but will use a short range system if none are available to
the faction.

I've made the change in a way that leaves factions compatible, but will
follow up to clean up our built-in factions.
2020-11-27 17:45:40 -08:00
Dan Albert
e8aa9839b0 Fall back to lower range SAMs when needed.
Mostly fixes https://github.com/Khopa/dcs_liberation/issues/473. The
last part of the fix is to migrate the `shorads` property of the faction
to just be in `sams` and just use the property to decide its use.
Currently factions like USA 2005 that have long range SAMs and SHORADs
only will still not spawn anything at medium sites because they have no
other SAMs declared.
2020-11-27 17:45:40 -08:00
Dan Albert
fcdb22db5b Add range property to all air defense generators. 2020-11-27 17:45:40 -08:00
walterroach
e73cf68def Merge pull request #474 from walterroach/frontline_vector
Frontline vector
2020-11-27 18:49:50 -06:00
Dan Albert
fa5b842cc7 Strengthen SA-10, add point defense.
Fixes https://github.com/Khopa/dcs_liberation/issues/417, though the
notes on that bug about this being non-optimal for skynet are still
true. This doesn't make skynet behavior any worse though, and does
improve it some compared to not having PD.

Adds two new SA-10 generator variants:

* Tier 2, with SA-15 for point defense
* Tier 3, with SA-15 for point defense and the Shilka upgraded to a
  Tunguska.

Updated factions that are capable of those systems, added missing SAMs
to those factions, and removed use of SA-19 as an independent SAM from
those factions. Will do a larger audit of faction SAMs later.
2020-11-27 16:25:10 -08:00
walterroach
43a21cb341 Fix bug #353 2020-11-27 17:53:56 -06:00
walterroach
45361b57a7 fixed bad import 2020-11-27 16:42:11 -06:00
walterroach
046c7a662a DisplayOption string change 2020-11-27 16:33:31 -06:00
walterroach
a9f1de13b1 Fix armor groups spawning bugs
* Prevent common cases where ground units do not spawn due to
  frontline position being in exclusion zone

* Fix case where ground units will spawn inside exclusion zone due to
  random offset from frontline center being fixed

* Remove dead code from `conflictgen.py`

* Start cleanup of `GroundConflictGenerator`
2020-11-27 16:31:52 -06:00
walterroach
edbe2d86f2 Merge branch 'develop' into frontline_vector 2020-11-27 13:47:10 -06:00
walterroach
4e12a1cdad Rework frontline vector
Ensures frontline stays outside of exclusion zones by adjusting its
position and width

Adds a DisplayOption for viewing the frontline vector on the map
2020-11-27 13:46:53 -06:00
Dan Albert
d24c7ea93e Remove the Hawk from USA 2005.
According to Wikipedia the last user of these (USMC) stopped using them
in 2002.
2020-11-26 22:55:44 -08:00
Dan Albert
484f1e8d51 Generate required IADS even at unconnected bases.
If the campaign designer doesn't want SAMs at unconnected bases they can
just not put them there. If they *do* put them there, generate them.
2020-11-26 22:54:50 -08:00
Dan Albert
8d5abb877c Improve IADS on Inherent Resolve. 2020-11-26 22:54:30 -08:00
Dan Albert
fd454dce74 Load campaign data lazily.
Error checking comes later, but the new game wizard opens much faster by
not spending time creating theaters it doesn't need.

Fixes https://github.com/Khopa/dcs_liberation/issues/469
2020-11-26 22:14:23 -08:00
Dan Albert
5d4fccd438 Fix mypy regressions. 2020-11-26 20:08:24 -08:00
Dan Albert
9f078e1483 Don't generate empty groups. 2020-11-26 20:03:16 -08:00
Dan Albert
0e807d84c2 Differentiate required long and medium range SAMs.
To improve IADS design in campaigns, this differentiates required long
and medium range SAMs. SAMs that must be long range SAMs are defined by
SA-10 or Patriot launchers, while medium range SAMs are defined by SA-2,
SA-3, or Hawk launchers.

Long range SAMs positions will only be populated by long range SAMs
(Patriots and SA-10s), and not all factions have those available. Medium
range SAMs currently comprise all air defenses that are not long range
SAMs, so if the faction includes flak guns in their `sams` property then
flak guns may be spawned at medium range SAM locations.

Base defenses and random SAM locations continue to use either type of
SAM.
2020-11-26 19:48:14 -08:00
Ignacio Muñoz Fernandez
3ad57d995b feat: added timestamp to information widget log items 2020-11-26 15:48:16 -08:00
Ignacio Muñoz Fernandez
28cf42aeb8 fix: Unable to find MLRS_BM21_Grad in pydcs 2020-11-26 15:43:54 -08:00
Ignacio Muñoz Fernandez
7fcf74a8ed added none check on budget setGame 2020-11-26 15:43:36 -08:00
Ignacio Muñoz Fernandez
a0d38f7465 fix: disable topbar buttons when game is None 2020-11-26 15:43:36 -08:00
Dan Albert
cd97526d2b Mark the fishbed as capable of strike missions. 2020-11-26 13:46:33 -08:00
Khopa
87fdc16f9b Update to support the newest version of the Rafale mod. 2020-11-26 21:38:46 +01:00
Khopa
b69eb02766 Added base defenses for russia small campaign 2020-11-26 13:38:59 +01:00
Dan Albert
7636234649 Fix mission generation when labels are changed.
Fixes https://github.com/Khopa/dcs_liberation/issues/457
2020-11-25 22:17:15 -08:00
Dan Albert
2bd673a531 Don't allow operating on broken runways.
Doesn't allow helos or harriers to do it either even though they should
be able to because we don't currently support ground spawns, which would
be needed to prevent those aircraft from using the runway. Even then, I
don't know if they can be forced to *land* vertically.

Fixes https://github.com/Khopa/dcs_liberation/issues/432
2020-11-25 18:50:00 -08:00
Ignacio Muñoz Fernandez
a1b64bc72d Cleanup QWeatherInfoWindow and rjust wind bearings. (#456) 2020-11-25 18:10:46 -08:00
Dan Albert
80bc9d6b23 Support reparing damaged runways.
Repairing a damaged runway costs $100M and takes 4 turns (one day). The
AI will always repair runways if they can afford it. if a runway is
damaged again during the repair the process must begin again.

Runways are still operational despite what the UI says. Preventing the
player and AI from using damaged runways (except for with helicopters
and harriers) is next.
2020-11-25 18:07:51 -08:00
Dan Albert
ee768b9147 Display damaged runways. 2020-11-25 18:07:51 -08:00
Khopa
7dfb0c67e5 Migrated campaign "Russia Small" to the new campaign format. 2020-11-26 00:10:11 +01:00
Dan Albert
75ea5cc462 Fix None dereferences in weather UI.
Also normalizes line endings.
2020-11-25 14:39:33 -08:00
Khopa
afabf6fd00 Fixed Control Point being set as neutral in some case. 2020-11-25 23:35:01 +01:00
Khopa
0eb4519797 Fixed IndexError preventing mission generation when a faction does not have any awacs aircraft available. 2020-11-25 23:11:55 +01:00
Dan Albert
611f04ab5a Resurrect force multiplier option.
Fixes https://github.com/Khopa/dcs_liberation/issues/440
2020-11-25 14:10:34 -08:00
Dan Albert
a9ba2deafa Update pydcs to get access to satnav options.
Fixes https://github.com/Khopa/dcs_liberation/issues/426
2020-11-25 13:16:51 -08:00
Dan Albert
0c4e920af3 Handle runway damage in the debrief.
Apparently we were already getting this info because it's a unit like
any other according to the event system, so if runways were actually
sufficiently damaged we'd emit a ton of exceptions.

This doesn't do anything yet, but tracks the damage state. Will add the
ability to repair them next, and then finally make the damage render the
runway inoperable.
2020-11-25 13:12:46 -08:00
Ignacio Muñoz Fernandez
ef0e565337 chore: standarized parameters in game/utils.py and added docblocks 2020-11-25 13:04:03 -08:00
Ignacio Muñoz Fernandez
02e7ab41b4 feat: MVP done for Weather Information Display 2020-11-25 13:04:03 -08:00
Ignacio Muñoz Fernandez
59bd4541c4 Removed old daytime icons 2020-11-25 13:04:03 -08:00
Ignacio Muñoz Fernandez
ca30af4238 wip: finished work on the TopPanel Widget, added weather icons, changed timeofday icons 2020-11-25 13:04:03 -08:00
Ignacio Muñoz Fernandez
718b3f2623 wip: fixed qt cosmetic issue, added forecast text generation, initial for weather window 2020-11-25 13:04:03 -08:00
Ignacio Muñoz Fernandez
6e153c6451 wip: initial work on issue #406 2020-11-25 13:04:03 -08:00
Khopa
4a1809d56e Added name of F-22 mod author in USA 2005 faction. 2020-11-25 12:13:19 +01:00
Khopa
a2bf0c1bea Added the wrong F-22A.lua file last night 😵 2020-11-25 12:12:07 +01:00
Dan Albert
f0d9dae33b Remove M-2000 from runway strike preferred list. 2020-11-24 17:32:09 -08:00
Dan Albert
b99462b628 Rename OCA strike tasks. 2020-11-24 17:28:48 -08:00
Dan Albert
65ac30acda Merge ground strike and SAM location presets.
Locations that should always be SAMs should be done with fixed IADs
locations, so we don't need the separate type.

The generic "ground strike location" needs to be eventually split up
into more specific types, so eventually all of the non-base defense SAMs
will go away.
2020-11-24 17:15:24 -08:00
walterroach
2072b6fa63 Merge pull request #447 from walterroach/caucasus_miz
Caucasus full single miz campaign
2020-11-24 18:37:04 -06:00
walterroach
4f604ba687 replace mizdata 2020-11-24 18:30:56 -06:00
Khopa
b9fe559b42 Forgot to commit F-22A mod payload 2020-11-25 01:17:27 +01:00
Dan Albert
efcdbebda5 Handle additional preset location types.
Missile sites were accidentally excluded, and coastal defenses aren't
being generated yet.
2020-11-24 15:50:51 -08:00
Khopa
8886850c60 Added fictional factions from Discord user Starfire 2020-11-25 00:49:53 +01:00
Khopa
20276e5230 Removed an unused property in controlpoint.py 2020-11-25 00:46:23 +01:00
Khopa
ed96bc83b4 Added support for the F-22A mod. 2020-11-25 00:45:40 +01:00
walterroach
c0147f5eb7 Caucasus full single miz campaign 2020-11-24 17:06:59 -06:00
Khopa
5bf5f024cb More adjustments to ai_flight_planner_db 2020-11-24 23:34:22 +01:00
Khopa
4628e8320a Integrated and reviewed changes to flight planner db from @foxwxl 2020-11-24 23:03:23 +01:00
Khopa
9c1d36d18a Removed radials from control point data. (Not used anymore) 2020-11-24 22:48:40 +01:00
Khopa
789b618e37 Changelog update 2020-11-24 13:10:28 +01:00
Khopa
d0804a6f9e Added missile icons for missiles sites 2020-11-24 13:08:59 +01:00
walterroach
17fe977b06 Merge pull request #437 from walterroach/operation_refactor
Operation refactor
2020-11-23 22:36:46 -06:00
walterroach
60783ca390 remove mypy ignore 2020-11-23 22:28:53 -06:00
walterroach
34a7a37409 Operation refactor cleanup
Fix bug in closest cp algorithm
2020-11-23 22:27:12 -06:00
Dan Albert
e68d2b5deb Plan OCA strikes at heavily populated airfields.
https://github.com/Khopa/dcs_liberation/issues/349
2020-11-23 18:09:05 -08:00
Dan Albert
b0317055e7 Implement OCA strike missions.
https://github.com/Khopa/dcs_liberation/issues/349
2020-11-23 17:58:53 -08:00
Dan Albert
6e0af7c144 Fix names of tasks to not use the enum name. 2020-11-23 17:15:42 -08:00
Dan Albert
9394ed663a Add runway bombing missions.
This allows planning the missions and the missions are functional, but
they will have no effect on future turns yet.
2020-11-23 16:47:58 -08:00
walterroach
967574820f Remove unused conflictgen globals
Remove unused  `Conflict` properties

mypy fixes

Cleanup
2020-11-23 18:14:25 -06:00
walterroach
da17d1e5d1 Change Operation to a static class
Removed always True "event successful"

Add `AirWarEvent` as the primary game `Event` applied to every miz

Cleanup of `FrontLineAttackEvent`

Change `Operation.is_awacs_enabled` to two bools for each side red/blue
Currently controlled by whether an AWACs is available for the faction
(and only ever true for Blue)
2020-11-23 18:14:25 -06:00
Dan Albert
63bdbebcaa Refactor control points into individual classes. 2020-11-23 14:34:58 -08:00
Dan Albert
c67263662d Fix arrival box changing when changing aircraft. 2020-11-23 00:23:57 -08:00
Dan Albert
2484457183 Spawn unused aircraft at airports.
The aircraft that have not been fragged will now be spawned in parking
to provide more targets for OCA strikes. We do this only at airports,
not carriers. The AI has enough trouble taxiing around uncrowded carrier
decks that we probably shouldn't make it harder for them, plus most of
the aircraft will be stored below the flight deck (we allow 90 aircraft
to be stored at the carrier, which certainly will not fit on the flight
deck).

The aircraft are spawned in an uncontrolled state and nothing will
activate them, so aside from the cost of rendering them they shouldn't
affect performance.

Fixes https://github.com/Khopa/dcs_liberation/issues/148
2020-11-23 00:12:42 -08:00
Dan Albert
d7b328b887 Consider trasnfers when counting parking. 2020-11-22 18:44:16 -08:00
Dan Albert
493e53c28f Perform aircraft transfers at the end of the turn. 2020-11-22 18:44:16 -08:00
walterroach
7438c30885 Merge pull request #430 from walterroach/develop_test
Update pydcs submodule
2020-11-22 18:59:35 -06:00
walterroach
fac43ba20b update submodule 2020-11-22 18:46:18 -06:00
walterroach
c2eb243026 Fix bug #400
arg position
2020-11-22 17:43:19 -06:00
Khopa
2adaee8671 Added Soviet Union 1943 2020-11-23 00:15:44 +01:00
walterroach
57edc5678c Merge branch 'develop' of https://github.com/Khopa/dcs_liberation into develop 2020-11-22 16:41:59 -06:00
walterroach
730130b19e Set AGL altitude on target waypoints 2020-11-22 16:41:53 -06:00
Dan Albert
17b0cee507 Add unrestricted SATNAV support to factions.
Enabled for bluefor modern, since they ought to have GPS but seemingly
don't.

This change does nothing until https://github.com/pydcs/dcs/pull/102
lands and we update to it.
2020-11-22 13:28:05 -08:00
Dan Albert
2557383946 Fix type of default map visibility setting.
The UI sets these to the proper enum types; only the default is wrong.
Fix the default and clean up the associated code.

Note that this does minorly break save compatibility and alters default
behavior, since previously we were ignoring the default option. Ignoring
the default looks unintentional since there is no explicit "don't force
this option" setting in the UI.

Existing saves can be fixed simply by changing this option to something
else and then back.
2020-11-22 13:28:05 -08:00
walterroach
29d3b5dfc6 Add missing P-47 icons 2020-11-22 02:12:41 -06:00
Dan Albert
f6fad30852 Add unit name -> Liberation object map.
Generated units are added to this during mission generation so we can
map destroyed units back to the data that generated them. Currently only
implemented for aircraft as a proof of concept.
2020-11-21 21:01:46 -08:00
walterroach
d5a081a15f Merge pull request #422 from walterroach/groundwar
Operation refactor and conflictgen cleanup
2020-11-21 21:00:51 -06:00
walterroach
6147e9ac96 mypy cleanup 2020-11-21 19:31:30 -06:00
walterroach
b32ca4f92f merge 2020-11-21 18:57:15 -06:00
walterroach
b57dd51f86 Merge branch 'develop' into frontline 2020-11-21 18:55:56 -06:00
walterroach
fc6ca162af Merge branch 'develop_2_2_x' into develop 2020-11-21 18:24:36 -06:00
walterroach
58ba9e9d1d bad arg 2020-11-21 18:17:35 -06:00
walterroach
939b6c468d refactoring 2020-11-21 17:19:54 -06:00
Dan Albert
bf7df6721a Fix turn 0.
`Game.initialize_turn` doesn't run on turn zero, so we have to set up
the unit order events when we create the `Game`.
2020-11-21 15:00:49 -08:00
walterroach
f6e0dbbb6a operation refactoring 2020-11-21 17:00:22 -06:00
Dan Albert
851c2d88a9 Factor out Lua generation. 2020-11-21 14:55:00 -08:00
Dan Albert
200c13dc31 Remove dead code. 2020-11-21 13:27:19 -08:00
walterroach
8889e35f9e Merge branch 'develop' into frontline 2020-11-21 12:50:44 -06:00
Khopa
398fa1e73d Fixed warehouse building not generating any rewards 2020-11-21 18:31:12 +01:00
walterroach
316f73138c Merge branch 'develop' into frontline 2020-11-21 11:19:31 -06:00
walterroach
708b615ad7 Merge conflict 2020-11-21 11:18:30 -06:00
walterroach
866ff78518 Merge branch 'develop_2_2_x' into frontline 2020-11-21 11:15:36 -06:00
Khopa
f0480b033f Removed debug print 2020-11-21 17:30:31 +01:00
Khopa
4d19548736 Changelog update / consistency with 2.X branch. 2020-11-21 17:05:33 +01:00
Khopa
fcf45554ef Fixed culling display and added setting to include/exclude carriers from culling area. 2020-11-21 17:01:50 +01:00
Khopa
799b0fae94 Merge branch 'develop' of https://github.com/khopa/dcs_liberation into develop 2020-11-21 14:46:01 +01:00
Dan Albert
0d95716545 Correct type annotations. 2020-11-20 21:40:31 -08:00
walterroach
8c5b808eba inherent resolve frontlines 2020-11-20 20:28:59 -08:00
walterroach
007dcf548e remove dead code 2020-11-20 21:02:11 -06:00
walterroach
9a640bf7eb inherent resolve frontlines 2020-11-20 20:38:32 -06:00
walterroach
edd02d9dd6 Merge branch 'develop' into frontline 2020-11-20 20:32:50 -06:00
Dan Albert
75edbb62f1 Clean up available parking code a bit.
Moved more into the `ControlPoint`.
2020-11-20 18:31:19 -08:00
walterroach
2b6227f3b1 remove dead code 2020-11-20 20:29:30 -06:00
Dan Albert
c4b8a41742 Move calculation of aircraft amounts into game.
The planner needs to know how much space is still expected to be
available next turn.
2020-11-20 18:25:03 -08:00
Dan Albert
f8b2dbe283 Move unit delivery ownership out of the UI.
Breaks save compat, but we need to have this knowledge outside the UI so
we can know whether or not we can ferry aircraft to the airfield without
overflowing parking.
2020-11-20 18:04:27 -08:00
Dan Albert
20091292f4 Remove dead code. 2020-11-20 17:37:20 -08:00
Dan Albert
a594f45aae Pick divert airfields when planning.
https://github.com/Khopa/dcs_liberation/issues/342
2020-11-20 17:29:58 -08:00
Ignacio Muñoz Fernandez
d394d01ea8 fix: conditional value at passTurn on QTopPanel 2020-11-20 17:12:13 -08:00
Ignacio Muñoz Fernandez
70d982b0ed fix: Pass Turn and Take Off Buttons when no game is loaded 2020-11-20 17:12:13 -08:00
Dan Albert
ae68a35a1a Remove save compat since it's breaking anyway.
Removal of old paths/names for things that no longer exist.
2020-11-20 17:06:01 -08:00
Dan Albert
a9fcfe60f4 Add arrival/divert airfield selection.
Breaks save compat because it adds new fields to `Flight` that have no
constant default. Removing all of our other save compat at the same
time.

Note that player flights with a divert point will have a nav point for
their actual landing point. This is because we place the divert point
last, and DCS won't let us have a land point anywhere but the final
waypoint. It would allow a LandingReFuAr point, but they're only
generated for player flights anyway so it doesn't really matter.

Fixes https://github.com/Khopa/dcs_liberation/issues/342
2020-11-20 16:16:00 -08:00
Ignacio Muñoz Fernandez
c3b028ef4b fix: fixes #325 for version 2.2.x 2020-11-20 03:50:58 -08:00
C. Perreau
833399f068 Merge pull request #409 from kavinsky/fix/issue-325
fix: fixes #325 Budget calculation for both factions
2020-11-20 12:44:16 +01:00
Ignacio Muñoz Fernandez
5695cf4ac5 fix: fixes #325 Budget calculation and enemy reinforcement calculation now takes into account the destroyed buildings 2020-11-20 12:38:06 +01:00
Ignacio Muñoz Fernandez
1553e5efd5 chore: syntax cleaning and redundant variable cleanup 2020-11-20 02:57:23 -08:00
Ignacio Muñoz Fernandez
0f1b396dd2 chore: variable name changes 2020-11-20 02:57:23 -08:00
Ignacio Muñoz Fernandez
976ee51bf5 feat: Added Financial Income to GroundObjectMenu building window 2020-11-20 02:57:23 -08:00
Dan Albert
7c22f6e83b Change mach function to take altitude in meters.
All of the callers are passing altitude in meters because that's what
pydcs uses. This still returns knots which makes it extra weird, but
that's what almost all of the callers expect.

It's probably a good idea to introduce some explicit types for the
various distance and speed units to avoid these sorts of mistakes.
2020-11-20 02:19:38 -08:00
Dan Albert
18b6f7b84c Add off-map spawn locations.
The AI isn't making use of these yet, but it's not smart enough to do so
anyway.

Would benefit from an icon to differentiate it on the map.

I'm stretching the definition of "control point" quite a bit. We might
want to put a class above `ControlPoint` for `AirSpawnLocation` to
represent types of spawn locations that can't be captured and don't have
ground objectives.

Fixes https://github.com/Khopa/dcs_liberation/issues/274
2020-11-20 02:19:03 -08:00
Dan Albert
206d09f7f8 Maybe correct fishbed radios.
Maybe fixes https://github.com/Khopa/dcs_liberation/issues/377
2020-11-20 00:20:01 -08:00
Dan Albert
4e910c4b09 Maybe correct fishbed radios.
Maybe fixes https://github.com/Khopa/dcs_liberation/issues/377
2020-11-20 00:18:00 -08:00
Dan Albert
bc3cd50a6c Add support for required SAMs in campaigns.
"Required" SAMs (designative by redfor long range SAM launchers in the
ME) will always be spawned during campaign generation. This makes it
possible to build a semi-guaranteed IADS (the exact type of SAM is
dependent) on the choice of faction.

Requierd SAMs will consume the slots of random SAMs during generation.
Later we should differentiate between strategic SAMs like SA-10s and
tactical SAMs like SA-11s so we can fill in the medium range SAMs at
random locations among the fixed long range SAMs.
2020-11-19 23:47:07 -08:00
Dan Albert
cecf611f91 Add docs for PresetLocations. 2020-11-19 21:59:36 -08:00
Dan Albert
5e4802f05e Return base defense locations to pool on capture.
When a base is captured we clear its defenses. Those locations need to
be returned to the preset location pool so they can be used for the new
base defenses.
2020-11-19 21:50:29 -08:00
Dan Albert
1e70e654ed Don't attempt loading miz files as JSON. 2020-11-19 21:32:44 -08:00
Dan Albert
20054b9825 Update Inherent Resolve campaign presets.
None of these are placed precisely yet, but they're useful for testing.
2020-11-19 21:29:21 -08:00
Dan Albert
5fb6a53cbd Add preset configuration for offshore types. 2020-11-19 21:28:57 -08:00
Dan Albert
ff751c30f9 Add preset configuration for strike targets. 2020-11-19 21:20:40 -08:00
Dan Albert
c1614ad5a7 Add TODOs for carrier CP names. 2020-11-19 21:19:53 -08:00
Dan Albert
13e372159a Change default settings to match UI defaults.
Doesn't affect the thing players see, but corrects the defaults when
using the command line mission generator.
2020-11-19 21:19:12 -08:00
Dan Albert
1ee0aafd9a Unify TGO location selection.
We currently have three methods of choosing locations for TGOs:

1. From the campaign miz
2. From the per-CP mizdata files
3. Randomly

Move the selection among these sources into a single place and use it
everywhere that we search for a TGO location.

Longer term methods 2 and 3 will be removed.
2020-11-19 21:09:33 -08:00
Dan Albert
df80ec635f Add a new miz file based campaign generator.
Defining a campaign using a miz file instead of as JSON has a number of
advantages:

* Much easier for players to mod their campaigns.
* Easier to see the big picture of how objective locations will be laid
  out, since every control point can be seen at once.
* No need to associate objective locations to control points explicitly;
  the campaign generator can claim objectives for control points based
  on distance.
* Easier to create an IADS that performs well.
* Non-random campaigns are easier to make.

The downside is duplication across campaigns, and a less structured data
format for complex objects. The former is annoying if we have to fix a
bug that appears in a dozen campaigns. It's less an annoyance for
needing to start from scratch since the easiest way to create a campaign
will be to copy the "full" campaign for the given theater and prune it.

So far I've implemented control points, base defenses, and front lines.
Still need to add support for non-base defense TGOs.

This currently doesn't do anything for the `radials` property of the
`ControlPoint` because I'm not sure what those are.
2020-11-19 16:55:21 -08:00
C. Perreau
6524286f04 Merge pull request #403 from Khopa/develop_2_2_x
Release 2.2.1
2020-11-20 00:29:15 +01:00
Khopa
87248fec53 Changelog update 2020-11-19 22:00:56 +01:00
Khopa
6470d25d18 About dialog update 2020-11-19 21:59:01 +01:00
Khopa
b7634a8ac3 About dialog update 2020-11-19 21:54:33 +01:00
walterroach
434755a620 Update README.md 2020-11-19 14:23:43 -06:00
walterroach
3eb2529b0b Fix #402 2020-11-19 13:20:05 -06:00
Khopa
e6e4cca076 Made it possible to setup custom liveries in faction files. 2020-11-19 20:18:26 +01:00
Khopa
8d57bbc777 Changelog update 2.2.1 2020-11-19 20:17:11 +01:00
Khopa
483db564f9 Added F-14A support 2020-11-19 20:14:21 +01:00
Khopa
63d5862319 Pydcs module update 2020-11-19 20:13:15 +01:00
Dan Albert
365b379798 Update changelog for 2.2.1. 2020-11-19 00:45:03 -08:00
Dan Albert
20f97e48a9 Update changelog for 2.2.1. 2020-11-19 00:42:54 -08:00
Dan Albert
fd473f0a46 Fix custom waypoints.
Like with deleting waypoints, these will degrade the flight plan to the
2.1 behavior.

Ascend/descend points aren't in use any more, so I removed those.
2020-11-19 00:36:43 -08:00
Dan Albert
94c5ed8bdc Fix custom waypoints.
Like with deleting waypoints, these will degrade the flight plan to the
2.1 behavior.

Ascend/descend points aren't in use any more, so I removed those.
2020-11-19 00:29:05 -08:00
Dan Albert
4b7b4bf110 Allow deleting waypoints.
In almost every case this leaves us with a flight plan we can't reason
about, so it gets degraded to `CustomFlightPlan`. The exception is when
deleting a target point when there are other target points remaining.
This probably gets people using this feature back to what they want
though, which is essentially the 2.1 behavior.

Fixes https://github.com/Khopa/dcs_liberation/issues/393
2020-11-18 23:44:16 -08:00
Dan Albert
a816877d08 Allow deleting waypoints.
In almost every case this leaves us with a flight plan we can't reason
about, so it gets degraded to `CustomFlightPlan`. The exception is when
deleting a target point when there are other target points remaining.
This probably gets people using this feature back to what they want
though, which is essentially the 2.1 behavior.

Fixes https://github.com/Khopa/dcs_liberation/issues/393
2020-11-18 23:43:01 -08:00
Dan Albert
2d56ae1cb6 Avoid cases where empty flights could be created.
Fixes https://github.com/Khopa/dcs_liberation/issues/373
2020-11-18 22:04:24 -08:00
Dan Albert
a223da8f99 Avoid cases where empty flights could be created.
Fixes https://github.com/Khopa/dcs_liberation/issues/373
2020-11-18 22:03:33 -08:00
Dan Albert
216adcc35a Add infor about delayed flights to the start page.
Fixes https://github.com/Khopa/dcs_liberation/issues/398
2020-11-18 21:27:19 -08:00
Dan Albert
8d485d5fa2 Delay player CV flight when their settings say so.
Fixes https://github.com/Khopa/dcs_liberation/issues/375

This also fixes a problem where we're spawning non-cold start planes in
an uncontrolled state. The ME won't let us do this, so we probably
shouldn't be doing that.
2020-11-18 21:27:18 -08:00
Dan Albert
fa549fcf94 Don't delay player flights with short delays.
Not much point in delaying humans 8 seconds.

Fixes https://github.com/Khopa/dcs_liberation/issues/397
2020-11-18 21:27:18 -08:00
Dan Albert
98fd707aea Add infor about delayed flights to the start page.
Fixes https://github.com/Khopa/dcs_liberation/issues/398
2020-11-18 21:26:14 -08:00
Dan Albert
5928f29f11 Delay player CV flight when their settings say so.
Fixes https://github.com/Khopa/dcs_liberation/issues/375

This also fixes a problem where we're spawning non-cold start planes in
an uncontrolled state. The ME won't let us do this, so we probably
shouldn't be doing that.
2020-11-18 19:34:38 -08:00
Dan Albert
c3ebabbe44 Don't delay player flights with short delays.
Not much point in delaying humans 8 seconds.

Fixes https://github.com/Khopa/dcs_liberation/issues/397
2020-11-18 19:27:03 -08:00
Khopa
29fd094dd0 Added F-14A support 2020-11-19 00:34:24 +01:00
walterroach
f7966b8d8c fix bug #394 2020-11-18 16:37:30 -06:00
Khopa
ee2f4ecbc8 Pydcs submodule update 2020-11-18 21:51:47 +01:00
Khopa
b08f6cad1d French translations for jinja templates, might be useful later. 2020-11-18 21:50:58 +01:00
Dan Albert
e851223733 Fix mypy. 2020-11-18 01:58:35 -08:00
Dan Albert
1f12546ff4 Add command line option to generate a new game.
Saves us a ton of clicks while developing the campaign generator.
2020-11-17 18:56:35 -08:00
Dan Albert
8345063e84 Move theater into game. 2020-11-17 18:11:33 -08:00
walterroach
482bedd739 Merge pull request #381 from foxwxl/patch-1
Create briefingtemplate_CN.j2
2020-11-17 15:47:27 -06:00
Dan Albert
7d7a334418 Fix mypy. 2020-11-17 02:04:38 -08:00
Dan Albert
69dbe62b70 Plan anti-ship missions automatically. 2020-11-17 01:08:06 -08:00
foxwxl
f61167cedf Create briefingtemplate_CN.j2
Briefing template CN version
2020-11-17 16:52:52 +08:00
Dan Albert
2ac92a75a4 Plan BAI missions automatically. 2020-11-17 00:36:41 -08:00
Dan Albert
fe80a9fd08 Make garrisons not SAMs.
The AI planner was planning DEAD against tanks because technically they
were SAMs.
2020-11-17 00:25:45 -08:00
Dan Albert
505af7635f Include EWRs in DEAD targets. 2020-11-16 23:53:25 -08:00
Dan Albert
426dc69e1d Allow DEAD on EWRs. 2020-11-16 23:53:25 -08:00
Dan Albert
082e8c062c Add anti-ship missions.
The only practical difference between this and BAI is that the target is
floating, so this mostly shares its implementation with BAI.

Fixes https://github.com/Khopa/dcs_liberation/issues/350
2020-11-16 23:29:19 -08:00
Dan Albert
9f2409bb9e Make sweep flights actually sweep flights.
DCS group task doesn't alter behavior, but it does alter which tasks are
available to the group.
2020-11-16 22:47:32 -08:00
Dan Albert
14dd8e43a4 Improve waypoint names for BAI targets. 2020-11-16 22:37:19 -08:00
Dan Albert
9fb33526a7 Add BAI missions.
BAI is used for attacking ground vehicles as opposed to buildings like
strike does, and not air defenses like DEAD does. Unlike strike, BAI is
tolerant of moving targets.

Fixes https://github.com/Khopa/dcs_liberation/issues/216
2020-11-16 21:11:18 -08:00
Dan Albert
8bd00bf450 Move mission type compatibility to the target.
This was also needed in other parts of the UI and is easier to implement
in the target class anyway.

Note that DEAD is now properly restricted to air defense targets.

Also added error boxes to the UI for when planning fails on an invalid
target.
2020-11-16 21:11:14 -08:00
walterroach
f3553ced78 Merge pull request #366 from walterroach/new_frontline
New frontline paths.  First step in #287
2020-11-16 22:13:33 -06:00
Khopa
a52dc43c9e MAde it possible to setup liveries in faction files. 2020-11-16 22:02:30 -06:00
Khopa
0b6b40a358 Pydcs repository update : Fixed issue with clipped wing variant of the spitfire not being seen as a flyable module 2020-11-16 22:02:27 -06:00
Dan Albert
f6371d2ef1 Further improve split/join positioning. 2020-11-16 22:02:22 -06:00
walterroach
ecd073e31d typing and comment cleanup 2020-11-16 22:01:49 -06:00
Dan Albert
b7d160631a Further improve split/join positioning.
(cherry picked from commit dc235f36c8)
2020-11-16 19:15:41 -08:00
Dan Albert
dc235f36c8 Further improve split/join positioning. 2020-11-16 19:15:11 -08:00
Khopa
7503c1e1e9 Pydcs repository update : Fixed issue with clipped wing variant of the spitfire not being seen as a flyable module 2020-11-17 00:06:10 +01:00
walterroach
253e8a209c fix return statement 2020-11-16 17:02:04 -06:00
Khopa
e26b692631 MAde it possible to setup liveries in faction files. 2020-11-16 23:57:12 +01:00
walterroach
658d808524 Merge branch 'develop' into new_frontline 2020-11-16 16:25:21 -06:00
Dan Albert
28e00055ab Differentiate BARCAP and TARCAP.
Previously the only difference between these was the objective type:
TARCAP was for front lines and BARCAP was for everything else.

Now BARCAP is for friendly areas and TARCAP is for enemy areas. The
practical difference between the two is that a TARCAP package is like
the old front line CAP in that it will adjust its patrol time to match
the package if it can, and it will also arrive two minutes ahead of the
rest of the package to clear the area if needed.
2020-11-16 00:32:50 -08:00
Dan Albert
8eef1eaa7c Fix mypy issue. 2020-11-15 23:59:20 -08:00
Dan Albert
e361a857a4 Only engage fighters with sweep. 2020-11-15 23:53:12 -08:00
Dan Albert
d369ce8847 Add fighter sweep tasks.
Fighter sweeps arrive at the target ahead of the rest of the package
(currently a fixed 5 minute lead) to clear out enemy fighters and then
RTB.

Fixes https://github.com/Khopa/dcs_liberation/issues/348
2020-11-15 23:49:14 -08:00
walterroach
d05897edcb Change default CAS loadout for Viggen
Reported that AI can't hit the broad side of a barn with the rockets.
2020-11-15 22:59:46 -06:00
walterroach
1d98432c57 Briefing tweak
Fixes frontline info repeating when player has no vehicles.
2020-11-15 22:59:40 -06:00
walterroach
e60166dc89 Change default CAS loadout for Viggen
Reported that AI can't hit the broad side of a barn with the rockets.
2020-11-15 22:50:14 -06:00
walterroach
87afc2fcef bezier frontline display
Start on refactoring and cleanup of QLiberationMap.py
2020-11-15 22:08:30 -06:00
walterroach
bd1457c435 Complete Caucasus Full Map frontline data. 2020-11-15 22:08:30 -06:00
walterroach
c1f88b4a5f frontline refactoring
`FrontLine` is tightly coupled with `ConflictTheater`.
  Moved into the same module to prevent circular imports.

Moved `ConflictTheater.frontline_data` from class var
to instance var to allow save games to have different
versions of frontlines.
2020-11-15 22:08:18 -06:00
walterroach
a080d4b692 Briefing tweak
Fixes frontline info repeating when player has no vehicles.
2020-11-15 22:06:05 -06:00
walterroach
c20e9e19cb Add cheat for more easily debugging frontline 2020-11-15 20:56:09 -06:00
Dan Albert
d9056acc6d Improve hold/split/join point positioning.
This also removes ascend/descend waypoints. They don't seem to be
helping at all. The AI already gets an implicit ascend waypoint (they
won't go to waypoint one until they've climbed sufficiently), and
forcing unnecessary sharp turns toward the possibly mispredicted ascent
direction can mess with the AI. It's also yet another variable to
contend with when planning hold points, and hold points do essentially
the same thing.

Fixes https://github.com/Khopa/dcs_liberation/issues/352.

(cherry picked from commit 21cd764f66)
2020-11-15 18:53:42 -08:00
Dan Albert
21cd764f66 Improve hold/split/join point positioning.
This also removes ascend/descend waypoints. They don't seem to be
helping at all. The AI already gets an implicit ascend waypoint (they
won't go to waypoint one until they've climbed sufficiently), and
forcing unnecessary sharp turns toward the possibly mispredicted ascent
direction can mess with the AI. It's also yet another variable to
contend with when planning hold points, and hold points do essentially
the same thing.

Fixes https://github.com/Khopa/dcs_liberation/issues/352.
2020-11-15 18:53:17 -08:00
Khopa
dfc31dfd5c Changelog update
(cherry picked from commit 8ffbf32677)
2020-11-15 18:53:13 -08:00
Khopa
8ffbf32677 Changelog update 2020-11-15 16:14:18 +01:00
Khopa
6e2124252c Added full persian gulf map by Plob 2020-11-15 15:58:00 +01:00
Khopa
3dd07b8c23 Added factions made by Discord user HerrTom 2020-11-15 15:57:59 +01:00
Khopa
2e067aada6 Added full persian gulf map by Plob 2020-11-15 15:57:36 +01:00
Khopa
78cd60f3df Added factions made by Discord user HerrTom 2020-11-15 15:50:29 +01:00
Dan Albert
2891649531 Fix pyinstaller spec for release.
final and buildnumber are optional files. Move them into resources to
avoid naming them explicitly.

(cherry picked from commit fae9650f56)
2020-11-14 13:13:18 -08:00
Dan Albert
4b40739918 Fix versioning for release builds.
(cherry picked from commit 9019cbfd2b)
2020-11-14 13:13:18 -08:00
Dan Albert
96c401e1b9 Fix pyinstaller spec for release.
final and buildnumber are optional files. Move them into resources to
avoid naming them explicitly.

(cherry picked from commit fae9650f56)
2020-11-14 13:06:59 -08:00
Dan Albert
fad132dcca Fix versioning for release builds.
(cherry picked from commit 9019cbfd2b)
2020-11-14 13:06:01 -08:00
Dan Albert
fae9650f56 Fix pyinstaller spec for release.
final and buildnumber are optional files. Move them into resources to
avoid naming them explicitly.
2020-11-14 13:01:11 -08:00
C. Perreau
e26e7f53c5 Merge pull request #367 from Khopa/develop_2_2_x
Release 2.2.0
2020-11-14 21:46:59 +01:00
Khopa
696710bf41 Merge remote-tracking branch 'khopa/master' into develop_2_2_x
# Conflicts:
#	changelog.md
#	game/operation/operation.py
#	gen/aircraft.py
#	gen/groundobjectsgen.py
#	qt_ui/uiconstants.py
2020-11-14 21:40:07 +01:00
Dan Albert
9019cbfd2b Fix versioning for release builds. 2020-11-14 12:39:50 -08:00
Khopa
f8735927bf Version string 2.2.0 2020-11-14 21:32:27 +01:00
Khopa
16cfc4e945 Changelog update for 2.2.0 2020-11-14 21:32:05 +01:00
Khopa
85f931316a Changelog update for 2.2.0 2020-11-14 21:29:03 +01:00
Khopa
717ea05d38 Pulled latest pydcs version 2020-11-14 21:24:45 +01:00
Khopa
3987274764 Pulled latest pydcs version 2020-11-14 21:24:15 +01:00
Khopa
73a97f9c2a Added High Digit Sams mods pydcs export extensions, maybe for later use. 2020-11-14 20:27:38 +01:00
walterroach
80612ba97d Merge branch 'develop_2_2_x' into develop 2020-11-14 12:24:26 -06:00
walterroach
c4d2b92e34 Merge branch 'develop_2_2_x' into new_frontline 2020-11-14 12:18:40 -06:00
walterroach
169fba9ab8 fixes viggen client waypoints
Only one target waypoint created when flight is Viggen client flight.

    M Waypoints not explicitly set anymore
      (they don't need to be when waypoint has a TOT)
2020-11-14 12:17:15 -06:00
Khopa
0b902e19ee Adding Persian gulf started mizdata files before release 2020-11-14 14:48:48 +01:00
Khopa
ab26a76789 A-20G won't level bomb unit groups 2020-11-14 14:19:34 +01:00
Khopa
1f43fbe16e A-20G won't level bomb unit groups 2020-11-14 14:17:07 +01:00
Dan Albert
f29cb99530 Update the README with a more recent screenshot.
(cherry picked from commit ef84703da9)
2020-11-14 01:39:47 -08:00
Dan Albert
ef84703da9 Update the README with a more recent screenshot. 2020-11-14 01:39:20 -08:00
Dan Albert
9e32ea7413 Handle inventory when selling aircraft.
This still leaves a bit to be desired, namely that selling aircraft
happens immediately but buying aircraft takes a turn. However, that's
how this behaved before, so this restores the 2.1 behavior. Worth
investigating further in the future.

(cherry picked from commit 75769df8e2)
2020-11-14 00:12:02 -08:00
Dan Albert
75769df8e2 Handle inventory when selling aircraft.
This still leaves a bit to be desired, namely that selling aircraft
happens immediately but buying aircraft takes a turn. However, that's
how this behaved before, so this restores the 2.1 behavior. Worth
investigating further in the future.
2020-11-14 00:11:19 -08:00
Dan Albert
95fd4cab05 Fix error box in flight creation.
(cherry picked from commit a81254cd18)
2020-11-13 21:01:30 -08:00
Dan Albert
a81254cd18 Fix error box in flight creation. 2020-11-13 21:00:55 -08:00
Dan Albert
4cff838de0 Develop is now 2.3. 2020-11-13 18:46:59 -08:00
walterroach
3838b3ca4f More frontlines 2020-11-13 17:03:46 -06:00
walterroach
7dc3e041c8 Merge branch 'new_frontline' of https://github.com/walterroach/dcs_liberation into new_frontline 2020-11-13 09:12:40 -06:00
walterroach
33b92423d8 cleanup comments
remove unnecessary method call
2020-11-13 09:12:08 -06:00
walterroach
6237fffa5a cleanup comments 2020-11-13 09:03:02 -06:00
walterroach
0b4e2d3b6b Merge branch 'develop' into new_frontline 2020-11-13 08:58:43 -06:00
Dan Albert
c4d08fa7b7 Fix handling of non-AA units in AA groups.
Some units in pydcs have detection_range and threat_range
defined, but explicitly set to None.
2020-11-13 02:57:51 -08:00
Dan Albert
cec28351e7 Remove dead code. 2020-11-13 01:20:59 -08:00
walterroach
398630d51e initial tooling 2020-11-12 21:51:54 -06:00
walterroach
61400ba726 caucasus test data 2020-11-12 21:47:54 -06:00
walterroach
33885e2216 initial multi segment frontline implementation 2020-11-12 21:47:13 -06:00
walterroach
5719b136fe sanity check 2020-11-12 19:16:01 -06:00
walterroach
ede5ee60c3 frontline 2020-11-12 18:21:37 -06:00
Khopa
9620ac7e7e Updated mizdata on Caucasus map 2020-11-12 21:08:44 +01:00
Dan Albert
c0bfdbf4bb Update client counts when packages are changed.
Fixes https://github.com/Khopa/dcs_liberation/issues/345
2020-11-11 15:06:57 -08:00
Dan Albert
efb544a303 Fix bad argument type. 2020-11-11 15:05:58 -08:00
Dan Albert
adfc4b7244 Don't plan DEAD missions against scuds.
Fixes https://github.com/Khopa/dcs_liberation/issues/355
2020-11-11 14:57:39 -08:00
Khopa
7a5ce98569 Added mizdata preset locations for the channel map. 2020-11-11 14:27:13 +01:00
Khopa
1fcceb0901 Mizdata for the channel map 2020-11-11 02:53:12 +01:00
Khopa
22c552053f Made Patriot and Sa-10 sites more compact, so it's easier to find valid position to generate them on the map. 2020-11-11 01:03:57 +01:00
Khopa
818c679d4f Merge branch 'develop' of https://github.com/khopa/dcs_liberation into develop 2020-11-10 23:48:45 +01:00
Khopa
3ff36c45aa Ground Object and Sam sites locations are chosen from a set of preset location when possible. 2020-11-10 23:48:27 +01:00
Dan Albert
623d461b06 Fix delays of AI flights.
Fixes https://github.com/Khopa/dcs_liberation/issues/354
2020-11-10 13:18:06 -08:00
Dan Albert
7535013848 Fix #85: update the max aircraft available.
https://github.com/Khopa/dcs_liberation/issues/85
2020-11-10 00:52:19 -08:00
Dan Albert
a63bac8826 Don't display scud sites as SAM threat range. 2020-11-09 21:51:55 -08:00
Dan Albert
d2c831c4ee Handle inventory for changing flight size. 2020-11-09 21:29:47 -08:00
walterroach
840b5ce071 Change CAS ROE 2020-11-09 20:36:07 -06:00
walterroach
be6abc0025 Don't display attack button for dead base groups 2020-11-09 15:48:34 -06:00
walterroach
ef585c59dd Merge pull request #343 from walterroach/missed_events
Missed Debrief Events
2020-11-09 15:36:09 -06:00
walterroach
16d9c1ccad Add EWR to destruction events in debrief
Also impacted reinforcement generation on turn after EWR destroyed.
2020-11-09 14:49:44 -06:00
walterroach
9a9ef78583 Fix statics not being killed in debrief 2020-11-09 13:34:56 -06:00
Dan Albert
fe7ee5b610 Add more to the changelog. 2020-11-09 00:55:57 -08:00
walterroach
680804040a Update changelog. 2020-11-09 00:43:53 -08:00
Dan Albert
0d4fe73daa Add ships to skynet EWR. 2020-11-08 23:51:28 -08:00
Dan Albert
407190c6c5 Use EWRs for EWR. 2020-11-08 22:17:06 -08:00
Dan Albert
73998dbde0 Show EWRs as detection range, not threat range. 2020-11-08 19:03:47 -08:00
Dan Albert
8827f7df34 Cleanup Lua plugin implementation.
* Move the UI code out of the plugin logic.
* Add types where needed.
* Move into game package.
* Improve error handling.
* Simplify settings behavior.
* Don't load disabled plugins.
* Remove knowledge of non-base plugins from game generation.

Fixes https://github.com/Khopa/dcs_liberation/issues/311
2020-11-08 17:53:52 -08:00
Khopa
4c394a9e2d Merge branch 'develop' of https://github.com/khopa/dcs_liberation into develop 2020-11-08 23:24:24 +01:00
Khopa
5946fc7404 Improved campaign selection screen in new game wizard. 2020-11-08 23:24:10 +01:00
Dan Albert
5b8ecb2c14 Add radio data for the MiG-21.
Fixes https://github.com/Khopa/dcs_liberation/issues/49
2020-11-08 13:25:58 -08:00
Khopa
61253e4d4d Show name instead of Id of ground units in faction selection screen. 2020-11-08 22:02:16 +01:00
Khopa
2c0ca5803f Improved the faction selection screen in the new game wizard. 2020-11-08 21:57:26 +01:00
Khopa
690f3d0f13 B-52 default strike loadout use Mk-84 2020-11-08 13:52:43 +01:00
walterroach
42c259bc58 Fix briefing whitespace 2020-11-08 01:28:28 -08:00
walterroach
11426a0713 Revert "more explicity waypoint namespace"
This reverts commit 314be482f9.
2020-11-07 21:16:10 -08:00
walterroach
e6af1b8645 more explicity waypoint namespace 2020-11-07 21:16:10 -08:00
walterroach
103f18191d Move viggen tot check down to pydcs waypoint only. 2020-11-07 21:16:10 -08:00
walterroach
fb312236a2 rework special waypoints 2020-11-07 21:16:10 -08:00
Dan Albert
c850c0095d Add DEAD to the flight recreate types. 2020-11-07 17:07:45 -08:00
Dan Albert
58481268f7 Fix mypy issue. 2020-11-07 16:33:00 -08:00
Dan Albert
11604671f8 Fix bad stagger interval calculation.
Was using the interval from mission start to latest rather than from
earliest to latest, so this could sometimes be off by a bit and cause
us to not generate enough start times.
2020-11-07 16:25:56 -08:00
Dan Albert
e8feded4c3 Add EWR generation.
Fixes https://github.com/Khopa/dcs_liberation/issues/66
2020-11-07 16:20:58 -08:00
walterroach
18f9b38d25 Keep only last base capture per base in debrief 2020-11-07 15:14:02 -08:00
Dan Albert
676eea3ccc Don't plan missions against dead targets.
Fixes https://github.com/Khopa/dcs_liberation/issues/314
2020-11-07 13:59:52 -08:00
Dan Albert
ee113d080e Merge branch 'theater-refactor' into develop 2020-11-07 13:39:35 -08:00
Dan Albert
8bc69415a7 Clean up base defense generation. 2020-11-07 13:38:58 -08:00
Dan Albert
3979ee57ff Cleanup oddities of CP generation.
* Don't spawn missile sites or ground locations for CVs/LHAs.
* Don't spawn ground locations around CVs/LHAs.
* Spawn AA sites even if the faction has no buildings defined.
2020-11-07 13:38:58 -08:00
Dan Albert
c2ee169d16 Fix regeneration of base defenses on capture.
I messed up the counting here and was counting *every* object rather
than just the base defenses, so it was very unlikely that we'd hit
base defenses on the second object, which is the only index valid for
SAM generation. That logic maybe needs to be different, but this
restores the previous behavior.
2020-11-07 13:38:58 -08:00
Dan Albert
56b51c85bb Fix targeting SAMs with strike missions.
The changes for Skynet unfortunately broke this because the names used
by the TGO and the name of the group itself were no longer in sync.
This deserves a larger cleanup where we decouple that naming
requirement (TGOs don't need a lot of the data they currently have),
but this works until we have the time to do that.

Fixes https://github.com/Khopa/dcs_liberation/issues/298
2020-11-07 13:37:52 -08:00
Dan Albert
853ee5aac4 Cleanup Theater Ground Object.
A whole bunch of this data is redundant.
2020-11-07 13:10:06 -08:00
Dan Albert
4cf406aefa Defer game load to after UI initialization.
This removes both the double load that happens on startup and also
makes it possible to get the UI to create a new game if the existing
default.liberation is not compatible.
2020-11-07 13:03:25 -08:00
Khopa
1abb341cb6 Added SCUD missiles sites 2020-11-07 19:20:08 +01:00
Khopa
44dce9598c Added support for Tetrarch and Daimler Armoured car. (New WW2 units) 2020-11-07 17:43:30 +01:00
Khopa
aefc8685a1 Added new FLAK units to WW2 allies factions 2020-11-07 17:40:26 +01:00
Khopa
26761342f5 Changelog update 2020-11-07 16:25:23 +01:00
Khopa
ca777bcebb Tuned base defense location generator for big airbases. (Some would end up without any base defenses before) 2020-11-07 16:23:46 +01:00
Khopa
5742075ff2 Syria terrain update for Incirlik airbase exlusions zones. 2020-11-07 16:22:59 +01:00
Khopa
c7a6ec9691 Changelog update 2020-11-07 03:27:03 +01:00
Khopa
e0153cfa6a Added Freya EWR sites to WW2 german factions 2020-11-07 03:21:53 +01:00
Khopa
3fd5e1bae7 Added newest P-47 variants to WW2 factions 2020-11-07 02:31:29 +01:00
Khopa
d9511a7edd Improved Strike mission generation.
- B52, A-20, and Tu-22 will level bomb targets
- When there is an unit group as target, all the units are now engaged instead of only the first unit of the group
2020-11-07 02:13:17 +01:00
C. Perreau
040db055fd Merge pull request #309 from DanAlbert/theater-refactor
Refactor game and ground object generation.
2020-11-06 20:33:19 +01:00
C. Perreau
b9f8cfd10d Merge pull request #319 from walterroach/fix_flight_edit
Fixes #312
2020-11-06 20:18:33 +01:00
Khopa
ff46556927 Fixed russia 1990 faction. Added Tu_22M3. 2020-11-06 18:56:25 +01:00
walterroach
d1815a3d6e Fixes #312 2020-11-06 10:38:29 -06:00
Dan Albert
fdfa4827ab Remove unused and broken scripts. 2020-11-05 21:39:49 -08:00
Dan Albert
5d579ccef9 Add hints for creating packages. 2020-11-05 17:08:34 -08:00
Dan Albert
4145d5578e Refactor game and ground object generation.
No real functional improvements yet, just reorganizing to make
improvements easier.
2020-11-05 16:09:34 -08:00
Dan Albert
43eb041bb8 Fix escort planning for racetracks. 2020-11-05 15:17:45 -08:00
Dan Albert
0b8ac8fc47 Move to hold points more slowly.
This should give the AI a better chance to climb before reaching the
waypoint.
2020-11-05 15:17:45 -08:00
Dan Albert
de3ba5908f Round waypoint TOTs in the UI. 2020-11-05 15:17:45 -08:00
C. Perreau
bbb6251aa9 Merge pull request #308 from walterroach/briefing_jinja
Briefing jinja
2020-11-05 23:31:58 +01:00
walterroach
6f71d92a7b Merge branch 'develop' into briefing_jinja 2020-11-05 16:08:16 -06:00
walterroach
a8b59cc567 Move briefing string literals to template 2020-11-05 16:07:44 -06:00
walterroach
f4d3660eac split sead and dead
change SEAD to engage in zone
2020-11-05 13:42:44 -08:00
walterroach
1f165835c6 type hinting and comment cleanup 2020-11-05 13:20:54 -08:00
walterroach
9087f3487d Single ShipGroupGenerator added 2020-11-05 13:20:54 -08:00
Walter
4ca92ea22d Fix ships being wrong unit type 2020-11-05 13:20:54 -08:00
Khopa
e2682d633f Added payloads for bombers. 2020-11-05 20:43:49 +01:00
Khopa
e6cb1b5970 Updated pydcs submodule commit reference 2020-11-05 19:59:21 +01:00
Khopa
de2d548139 Added Flak guns to most coldwar factions 2020-11-05 00:34:57 +01:00
Khopa
4d1a0b85e4 Added free version of WW2 factions that will not require the WW2 Asset Pack. 2020-11-04 22:25:24 +01:00
Khopa
5cfbd8c3ad Added building set for WW2 units that does not require WW2 asset pack. 2020-11-04 22:01:28 +01:00
Khopa
95f72be8eb Fixed issue with duplicated WW2 germany faction IDs 2020-11-04 21:51:09 +01:00
Khopa
95c4dfa52f Added new Flak site configuration (now that flak 18 and bofors guns do not require WW2 asset pack) 2020-11-04 21:50:25 +01:00
Khopa
b72a2f4a5f Fixed error in new game wizard with units and mods required not being displayed correctly in some cases. 2020-11-04 21:09:48 +01:00
Khopa
844f8595d1 Fixed ID of some units to match new pydcs data-export. 2020-11-04 21:07:28 +01:00
Khopa
1c9d9be667 Using latest pydcs data-export 2020-11-04 21:02:01 +01:00
Khopa
2a02a743a4 Do not display manage button on base defense group for enemy cp. 2020-11-04 18:53:18 +01:00
Khopa
968d9365d6 Re-added F-15E to bluefor modern. 2020-11-03 20:38:04 +01:00
Walter
cdb16cc591 Fixes #268
Changes red base info to show defenses first, and adds an attack option
2020-11-02 18:06:10 -08:00
Walter
86bc41c15c Add base menu to new package dialog for red cp 2020-11-02 18:06:10 -08:00
Dan Albert
ac05c7cfaa Fix recreate as CAP for ground targets. 2020-11-02 02:33:09 -08:00
Dan Albert
9c07fe5963 Remove source of error in package waypoint timing.
We were calculating the TOT based on travel time to the *flight's*
target, but the ingress point based on the travel time to the target
area. If the difference in travel time between the center of the
target area and the first target were different then we'd calculate
the start time incorrectly even for single flight packages.

Seems to fix https://github.com/Khopa/dcs_liberation/issues/295
2020-11-02 02:26:07 -08:00
Dan Albert
ed05f995b5 Refactor strike formation timing calculations. 2020-11-02 02:25:33 -08:00
Dan Albert
85491dca20 Fix front line CAP patrol end time. 2020-11-02 01:34:35 -08:00
Dan Albert
465399f803 Add radio configuration for the UH-1H and Ka-50. 2020-11-01 17:12:42 -08:00
Dan Albert
3550c8a8f6 Remove afterburner restrictions.
The AI often needs afterburner to recover from high AoA.

Fixes https://github.com/Khopa/dcs_liberation/issues/205
2020-11-01 15:12:05 -08:00
Dan Albert
739c0f8f52 Add radio data for MiG-15bis and MiG-19P.
Fixes https://github.com/Khopa/dcs_liberation/issues/233.
2020-11-01 14:50:23 -08:00
Dan Albert
49aa79c612 Fix mypy issues. 2020-11-01 14:49:50 -08:00
Dan Albert
cdde75b517 Add option to avoid delaying player flights.
Fixes https://github.com/Khopa/dcs_liberation/issues/227
2020-11-01 14:13:06 -08:00
Dan Albert
dde74af6b5 Fix client/player detection.
Client needs to be used if there are other player slots in *any*
flight, not just the same group.

Fixes https://github.com/Khopa/dcs_liberation/issues/297
2020-11-01 14:02:32 -08:00
Dan Albert
eff9c77c9a Fix departure time in the kneeboard.
We don't have the departure time set until after we create the initial
FlightData object. Populate the value after it is determined.

Fixes https://github.com/Khopa/dcs_liberation/issues/290
2020-11-01 13:31:56 -08:00
Dan Albert
5ba633c8a1 Round TOT/start time as needed.
The increased precision that we had everywhere except the UI and the
interface with DCS was causing issues with ASAP creating barely
negative start times. The main cause of this was that we'd compute the
earliest possible TOT, it would result in, for example, 23:10.002.
When we then set the QTimeEdit for the TOT, we have to round because
it does not support (nor do we really want to display) sub-second
values, which then caused the previously 0 start time to be -0.002.

Instead, since the sub-second values aren't really interesting anyway,
we now just round TOTs up and start times down. This should prevent
negative start times from occurring (except when they've been manually
planned as such), and also prevents start times of 00:00:01.

Also rounds the package waypoint times to avoid the same issues, but
it's not really important which direction we round these.

Fixes https://github.com/Khopa/dcs_liberation/issues/295
2020-11-01 13:31:10 -08:00
Dan Albert
ab67a38ca5 Remove rounding from waypoint timing in the UI.
This is behaving strangely on some machines. Stop hiding the details
in the UI while we debug.
2020-11-01 01:42:58 -08:00
Dan Albert
08f0c9d30a Fix timedelta -> timedelta conversion.
Fixes https://github.com/Khopa/dcs_liberation/issues/293
2020-11-01 00:58:35 -07:00
Dan Albert
9d747a9f9b Fix kneeboard ground speeds. 2020-10-31 20:28:07 -07:00
walterroach
31ca121498 Add github build number to title bar for preview
builds
2020-10-31 20:10:58 -07:00
walterroach
44b5f5acf1 added notify call back 2020-10-31 20:09:20 -07:00
walterroach
eb4878dfc4 remove useless props 2020-10-31 20:09:20 -07:00
walterroach
3dc7dc3d1a Dan'ts notes
commit 35ab9103cebf823bab85fbb1c9ff4bc2b9c5701a
Author: walterroach <37820425+walterroach@users.noreply.github.com>
Date:   Fri Oct 30 22:25:42 2020 -0500

    more cleanup

commit d3d008bc6b29f328ad48063bd4b0520e1b819d68
Author: walterroach <37820425+walterroach@users.noreply.github.com>
Date:   Fri Oct 30 22:17:59 2020 -0500

    More briefinggen cleanup

commit b2033f091d7191aecefb86fecb5cb060c074706e
Author: walterroach <37820425+walterroach@users.noreply.github.com>
Date:   Fri Oct 30 22:08:48 2020 -0500

    briefinggen cleanup

commit 72ea0196c22d9493df078765800f3fafb9c054dc
Author: walterroach <37820425+walterroach@users.noreply.github.com>
Date:   Fri Oct 30 21:57:52 2020 -0500

    Add notifier method back to Operation

commit efd39a9e03d02b9d581637d0d1c289af68e749c3
Author: walterroach <37820425+walterroach@users.noreply.github.com>
Date:   Fri Oct 30 21:34:37 2020 -0500

    Revert "Move kneeboard and briefing gen to unified"
    Removes properties added to Operation
    This reverts commit 941f2af770.
2020-10-31 20:09:20 -07:00
Walter
6a6133e5cd template cleanup 2020-10-31 20:09:20 -07:00
Walter
65c85d7f0b init args change 2020-10-31 20:09:20 -07:00
Walter
d519dfa5da docstrings and cleanup 2020-10-31 20:09:20 -07:00
Walter
73ea83bbdd Move kneeboard and briefing gen to unified
interface
2020-10-31 20:09:20 -07:00
Walter
235a5ec538 cleanup 2020-10-31 20:09:20 -07:00
walterroach
f81a3d03c0 working briefinggen refactor 2020-10-31 20:09:20 -07:00
walterroach
6878b57fba refactor frontline brief generation 2020-10-31 20:09:20 -07:00
walterroach
0143e5641f var change 2020-10-31 20:09:20 -07:00
walterroach
5adc92c601 Properly get enum instead of value on strategy
selecter
2020-10-31 20:09:20 -07:00
walterroach
0b2fbddbc5 refactor frontline situation briefing 2020-10-31 20:09:20 -07:00
Walter
28035bf02b linting and formatting 2020-10-31 20:09:20 -07:00
Walter
6c9a9de3f3 parent f03121af5a
first pass briefing refactor

briefing fixes

briefing fixes

Stop briefing generate being called twice

Stop frontline advantage string being appended
when there are no units.

jinja template

always return enum instance in Strategy Selector

For some reason on DEFENSE, enum is appended to control point stance,
but on all other the enum.value is added instead.

I don't see any case where the value is used, but there are many
cases that the enum instance is evaluated against.

type issue

junja's not a thing

swap mapping with dict

jinja template

always return enum instance in Strategy Selector

For some reason on DEFENSE, enum is appended to control point stance,
but on all other the enum.value is added instead.

I don't see any case where the value is used, but there are many
cases that the enum instance is evaluated against.

type issue

Update build.yml

junja's not a thing

swap mapping with dict

Restore build job
2020-10-31 20:09:20 -07:00
Walter
62139fc4eb Fix Nevada landmap not loading
logging on open try except block
2020-10-31 19:55:26 -07:00
Dan Albert
88b9ed29ba Reorganize flight planning.
Previously we were trying to make every potential flight plan look
just like a strike mission's flight plan. This led to a lot of special
case behavior in several places that was causing us to misplan TOTs.

I've reorganized this such that there's now an explicit `FlightPlan`
class, and any specialized behavior is handled by the subclasses.

I've also taken the opportunity to alter the behavior of CAS and
front-line CAP missions. These no longer involve the usual formation
waypoints. Instead the CAP will aim to be on station at the time that
the CAS mission reaches its ingress point, and leave at its egress
time. Both flights fly directly to the point with a start time
configured for a rendezvous.

It might be worth adding hold points back to every flight plan just to
ensure that non-formation flights don't end up with a very low speed
enroute to the target if they perform ground ops quicker than
expected.
2020-10-31 19:29:24 -07:00
Dan Albert
d94c57afd6 Fix error displaying flight edit dialog.
If no airfield was selected (as sometimes happens, usually when there
are no aircraft available anywhere), report that no aircraft are
available.
2020-10-31 18:39:06 -07:00
Dan Albert
b6421646ff Name the set TOT ASAP button better. 2020-10-30 17:28:04 -07:00
Dan Albert
611b6fc272 Fix mypy. 2020-10-30 16:43:19 -07:00
Dan Albert
9cdbef9faf Revert "Run tests in GitHub actions."
Was pushed by accident.

This reverts commit f5047fc0cc.
2020-10-30 16:14:38 -07:00
Dan Albert
b34de70fc7 Fix logging.
Same problem as last time: we were logging during initialization
before the log handlers could be initialized.
2020-10-30 16:13:34 -07:00
Dan Albert
f5047fc0cc Run tests in GitHub actions. 2020-10-30 15:09:20 -07:00
Dan Albert
258c34e61d Make test runnable from command line.
`pytest tests` works now. I can't explain why `pytest` alone does not,
but it could have something to do with us not being a real Python
package.

With just `pytest` I get:

    E   ModuleNotFoundError: No module named 'tests.test_factions'

But `python -c "import tests.test_factions"` works fine.
2020-10-30 15:05:44 -07:00
Dan Albert
f365487fd6 Remove user-specific config file. 2020-10-30 13:51:18 -07:00
Khopa
8f65b7ee7c Fixed and added many ground unit icons 2020-10-30 16:59:13 +01:00
Khopa
9397f1f39c Fixed issue with Russian 1955/65/75 factions 2020-10-30 16:16:46 +01:00
Walter
f03121af5a fixed wrong conditionals 2020-10-28 16:27:18 -07:00
Dan Albert
239b9f8234 Use python, not py.
py is a shortcut that launches the *latest* version of Python on the
machine. https://stackoverflow.com/a/50896577/632035

The build machines were updated to include python 3.9, so we were
doing everything with 3.9 instead of 3.8. pyproj doesn't have a binary
wheel for 3.9 on pypi yet, so we were falling back to building it from
source, which we aren't able to do, breaking the build.
2020-10-28 00:06:12 -07:00
Dan Albert
1620c602cf Fix mypy issue. 2020-10-27 18:12:13 -07:00
Walter
fa01303460 #257 CasIngressBuilder changes
* Calculate radius from frontline length global
* Determine CAS waypoint by waypoint type rather than name
* Change logging from error to exception
2020-10-27 09:48:36 -05:00
walterroach
1e5bd916d9 CasIngressBuilder proper inheritence 2020-10-26 23:33:56 -05:00
walterroach
62d89239fc merge 2020-10-26 23:20:05 -05:00
walterroach
91bee4e6c2 Changed CAS engage task to zone 2020-10-26 23:10:53 -05:00
Walter
a465dde32f Changed task to add AAA and infantry properly 2020-10-26 16:42:40 -07:00
Walter
254dd5f70f Add Air Defenses to engage task 2020-10-26 16:42:40 -07:00
Walter
1a70ed5121 CAS Weapons Hold until Ingress 2020-10-26 16:42:40 -07:00
Walter
16d5a550ce Move Engage targets to ingress point for CAS 2020-10-26 16:42:40 -07:00
Khopa
794cc43a41 Fix pmc mb339 modded faction id 2020-10-26 23:56:52 +01:00
Walter
8583bbf893 Changed task to add AAA and infantry properly 2020-10-26 17:19:52 -05:00
Walter
63d510f2ea Add Air Defenses to engage task 2020-10-26 15:48:22 -05:00
David Pierron
2c6b26003b Merge pull request #237 from VEAF/skynet-iads-plugin
First version of the Skynet IADS plugin
2020-10-26 21:39:58 +01:00
Walter
fdaf3bc30f CAS Weapons Hold until Ingress 2020-10-26 15:26:51 -05:00
Walter
816d9696b5 Move Engage targets to ingress point for CAS 2020-10-26 15:26:47 -05:00
David Pierron
bdaa6a294a Merge branch 'skynet-iads-plugin' of https://github.com/VEAF/dcs_liberation into skynet-iads-plugin 2020-10-26 21:17:26 +01:00
David Pierron
a6b15b9529 Merge remote-tracking branch 'upstream/develop' into skynet-iads-plugin 2020-10-26 21:17:16 +01:00
David Pierron
878529e8f8 Merge branch 'develop' into skynet-iads-plugin 2020-10-26 21:14:24 +01:00
David Pierron
2cb37b5bd8 added typing 2020-10-26 21:06:25 +01:00
David Pierron
de95cfc981 base plugin needs to be loaded first,
before skynetiads
2020-10-26 21:06:08 +01:00
David Pierron
0477247cf2 correction :
skipping plugin work orders did not work
2020-10-26 21:05:34 +01:00
David Pierron
e0319a4047 Merge remote-tracking branch 'upstream/develop'
into skynet-iads-plugin
2020-10-26 18:05:07 +01:00
Khopa
839f163ac5 Fixed issue with base defense that could not be repaired 2020-10-26 00:32:05 +01:00
Khopa
ba9ad4c371 Merge branch 'develop' of https://github.com/khopa/dcs_liberation into develop 2020-10-26 00:24:30 +01:00
Khopa
bdcf7c1828 Fixed mig 21 wrong identifier in some files. 2020-10-26 00:24:03 +01:00
Dan Albert
821e9fc114 Merge branch 'fix-mypy-actions' into develop 2020-10-25 16:19:57 -07:00
Dan Albert
334d21897b Fix mypy actions.
GitHub actions fail based on the exit code of the *last* command in
the step, so we were failing mypy but passing the action.
2020-10-25 16:19:46 -07:00
Dan Albert
b405c3ab32 Fix mypy issues in faction code. 2020-10-25 16:19:46 -07:00
Khopa
3cabb1e02d Inverted usa 1990 and 2005 factions names 2020-10-26 00:16:37 +01:00
Khopa
ec7f8f5710 Completed basic faction parameters loading test. 2020-10-25 14:38:10 +01:00
Khopa
93f0627c5e Fixed errors in faction code 2020-10-25 14:21:25 +01:00
Khopa
3d8c2d689e Added unit tests for faction. (To be completed) 2020-10-25 14:20:43 +01:00
Khopa
e1572c09ff Improved faction & faction loader typing. Fixed error with netherlands faction. 2020-10-25 14:08:11 +01:00
Khopa
910af12fb9 Default faction for bluefor is USA 2005. 2020-10-25 12:19:51 +01:00
C. Perreau
acbd45341f Merge pull request #249 from Khopa/faction_refactor
Moddable factions
2020-10-25 02:19:22 +02:00
Khopa
901c89371c Fixed syntax errors in json files 2020-10-24 23:40:05 +02:00
Khopa
743534bdda Migrated latest factions to new faction json format. 2020-10-24 23:35:39 +02:00
Walter
98bb1a9f65 Explicit message for confirm_no_client_launch 2020-10-24 13:28:02 -07:00
Dan Albert
bfc602f22f Add cheat option to capture bases.
Capturing bases is sometimes really annoying because of the DCS unit
AI and our non-optimal ground victory heuristics. Add a cheat option
to allow the player to move on without the tedium.
2020-10-24 13:06:37 -07:00
Dan Albert
dd2b61edf3 Use FrontLine in ConflictTheater.conflicts. 2020-10-24 12:24:29 -07:00
Khopa
0fd58135fd Migrated more factions to new faction json format. 2020-10-24 18:02:41 +02:00
Khopa
4672252242 Added description field to both campaign and faction json file. 2020-10-24 17:40:04 +02:00
Khopa
a0c61bf73a Authors informations are loaded in Campaign object (to be displayed in UI later) 2020-10-24 17:23:09 +02:00
Khopa
d6c19a8aff Added author field to campaign json file 2020-10-24 17:18:55 +02:00
Khopa
b6a933e264 Readded Full Map - Caucasus, remade by Discord user "george" 2020-10-24 17:16:56 +02:00
Khopa
0191eca9dc Keeping new factions up to date with develop branch. 2020-10-24 17:03:39 +02:00
Khopa
f962fd55bc Merge branch 'develop' into faction_refactor
# Conflicts:
#	game/factions/bluefor_coldwar.py
#	game/factions/bluefor_coldwar_a4.py
#	game/factions/bluefor_coldwar_mods.py
#	game/factions/bluefor_modern.py
2020-10-24 16:57:28 +02:00
David Pierron
33fa719e8d Merge branch 'skynet-iads-plugin' of https://github.com/VEAF/dcs_liberation into skynet-iads-plugin 2020-10-24 11:03:03 +02:00
David Pierron
adb9352905 removed mist.lua from skynet plugin
It's already included in the base plugin, which is mandatory
2020-10-24 11:02:46 +02:00
David Pierron
58574c67df Merge branch 'develop' into skynet-iads-plugin 2020-10-24 11:01:18 +02:00
Dan Albert
04d3ba4c47 Add mypy to github actions so we stop regressing. 2020-10-24 01:25:47 -07:00
Dan Albert
0f1d2b8685 Remove a plugin we don't include. 2020-10-24 01:10:15 -07:00
Dan Albert
c06a855113 Fix mypy regressions.
Mostly in the plugin system, which needed a handful of asserts that
shouldn't be necessary, but fixing them requires a refactor.
2020-10-24 01:10:11 -07:00
Dan Albert
e9bfd58ee1 Shorten strike waypoint descriptions.
We don't really need this much detail in these, and it was overflowing
the kneeboard.
2020-10-23 22:52:32 -07:00
Dan Albert
5f02febb6c Fix kneeboard crash for small strike missions.
When we don't coalesce target points on the kneeboard we call
add_waypoint_row multiple times, so calls after the first were using
the travel time from one strike target to the next. Since they were so
close that rounded down to zero and caused a divide by zero error.

Update the last waypoint in add_waypoint instead.
2020-10-23 22:41:28 -07:00
Dan Albert
15db12fb21 Fix TOT/start for BARCAPs in other packages.
We were only getting BARCAP results right in BARCAP packages. This
fixes calculations of TOTs and start times for BARCAPs in strike
packages.

The probably needs some refactoring. BARCAP is just the symptomatic
example at the moment, but the real problem is that different mission
profiles exist and we currently only handle one. Making profiles
explicit in mission planning will clean this up, will be needed for
other future mission types, and makes it easier for us to alter
behavior for waypoint and timing decisions based on the aircraft or
mission type.
2020-10-23 22:28:51 -07:00
Dan Albert
c3fca6696d Fix waypoint altitudes for helicopters.
Fixes https://github.com/Khopa/dcs_liberation/issues/234
2020-10-23 14:52:14 -07:00
Dan Albert
dd4c37cde3 Pick runways and ascent/descent based on headwind. 2020-10-23 14:41:42 -07:00
Dan Albert
aa7ffdabb0 Fix planned flights view, stop hiding bugs. 2020-10-23 14:19:45 -07:00
Dan Albert
85f6616185 Add bombers to coalitions.
* B-1
* B-52
* F-117
* Tu-160
* Tu-22
* Tu-95

Also alters the default loadouts for the F-15E.
2020-10-23 13:50:03 -07:00
David Pierron
c8955bdca7 activate the SAM at mission start,
to haste their response
2020-10-23 07:49:26 +02:00
Khopa
669a8c9e64 Merge branch 'develop' into faction_refactor 2020-10-23 01:39:48 +02:00
Khopa
b4d0eb0b99 Started cleaning old factions, added back most factions as json. Fixed LHA not spawning. 2020-10-23 01:38:08 +02:00
C. Perreau
035bebaab8 Merge pull request #238 from walterroach/ter_fix
Fixes issue Khopa#203 TER weapons not selectable in custom payload
2020-10-23 01:07:37 +02:00
Walter
b20200318b ter fix 2020-10-22 16:29:46 -05:00
Walter
9caf83cda9 Fixes issue #203
Conditional in comprehension appears to be intended to exclude
dunders, but was also excluding many TER weapons from Weapon class args
beginning with '_'
2020-10-22 16:28:38 -05:00
David Pierron
493f9df4e2 Merge remote-tracking branch 'upstream/develop' into skynet-iads-plugin 2020-10-22 21:36:49 +02:00
Dan Albert
cc61893bca Fix more fallout from double/right click change. 2020-10-22 12:12:28 -07:00
David Pierron
3ba2fb76aa removed VEAF from the default plugins 2020-10-22 18:40:40 +02:00
David Pierron
e024da277b first working version of the skynetiads plugin 2020-10-22 18:39:54 +02:00
Khopa
bc1e793ce6 Fixed faction loader so it works with known modded units 2020-10-22 13:55:37 +02:00
Khopa
aa7eacc043 Added list of modded units in pydcs_extensions module 2020-10-22 13:51:24 +02:00
Khopa
dd7b9f1790 Fix faction shorad units loader 2020-10-22 13:50:24 +02:00
Khopa
d6b94345d9 Merge branch 'develop' into faction_refactor 2020-10-22 13:40:14 +02:00
Khopa
aa1ac56ec3 Faction rework, working :) 2020-10-22 13:33:18 +02:00
Dan Albert
fd969020af Add escort tasks for the AI. 2020-10-22 01:29:19 -07:00
Dan Albert
95f486870d Correct AI startup time.
It doesn't look like the AI is subject to much startup time. I see
B-1, F-15, F-16, and M-2000 all start up in about 2 minutes.
2020-10-22 00:46:53 -07:00
C. Perreau
f6d049da3c Merge pull request #232 from VEAF/develop
changes to the export of state.json :
2020-10-22 09:46:10 +02:00
Dan Albert
69f15824ca Fix breakage in package list UI. 2020-10-22 00:45:10 -07:00
Dan Albert
8c70d1ab79 Fix foot to meter conversion.
Somehow this constant was wrong so all of our foot-to-meter
conversions were coming out ~7% too large. We're still introducing
some error because we're rounding early rather than only when we need
an integer, but it's much more accurate now.
2020-10-21 23:58:23 -07:00
Dan Albert
58fd651a0b Confirm exit to avoid losing progress.
Fixes https://github.com/Khopa/dcs_liberation/issues/218
2020-10-21 18:35:14 -07:00
Dan Albert
177b505cb7 Update client slots UI on end turn.
Fixes https://github.com/Khopa/dcs_liberation/issues/222
2020-10-21 17:54:58 -07:00
Dan Albert
8ac5dbe22a Add double- and right-click actions for ATO lists.
Fixes https://github.com/Khopa/dcs_liberation/issues/230
2020-10-21 17:52:21 -07:00
Khopa
b744238fb8 Factions are properly loaded, now need to refactor whole code. 2020-10-22 01:32:33 +02:00
David Pierron
57b7402753 changes to the export of state.json :
- dcsLiberation.installPath does not include "state.json" anymore
- corrected behavior :
  - try LIBERATION_EXPORT_DIR
  - then try dcsLiberation.installPath
  - then try TEMP
  - then try working directory
- corrected multiple bugs in dcs_liberation.lua
- corrected bad string.format causing DCS crashes in
  jtacautolase-config.lua
2020-10-21 23:44:36 +02:00
Khopa
dcaa390d24 Added support for Su-57 mod by Cubanace 2020-10-21 22:21:36 +02:00
Khopa
59010f6949 Added support for Su-57 mod by Cubanace 2020-10-21 21:40:31 +02:00
Khopa
6a91fad10a Merge branch 'develop' into faction_refactor 2020-10-21 19:51:25 +02:00
C. Perreau
f03029417d Merge pull request #229 from VEAF/develop
Removed VEAF submodule
2020-10-21 11:39:18 +02:00
David Pierron
f5aa342602 Removed VEAF submodule 2020-10-21 11:32:39 +02:00
C. Perreau
53582ba539 Merge pull request #228 from VEAF/new-plugin-system
New plugin system
2020-10-20 23:16:19 +02:00
David Pierron
44c976948d bug correction in the JTACautolase LUA config 2020-10-20 23:02:46 +02:00
David Pierron
41d5020467 bug correction - when running a new campaing
without accessing the plugin setup screen
2020-10-20 22:25:39 +02:00
David Pierron
1bd26005f2 Merge remote-tracking branch 'upstream/develop' into new-plugin-system 2020-10-20 22:10:19 +02:00
David Pierron
b1840ce2ca Merge 'upstream/develop' into new-plugin-system 2020-10-20 22:10:08 +02:00
C. Perreau
769246c55d Merge pull request #224 from justin-lovell/feature/remote-hosting-flexibility
Flexible Dedicated Hosting Options for Mission Files
2020-10-20 22:04:37 +02:00
Khopa
24394d4d00 Faction rework wip. 2020-10-20 20:58:39 +02:00
Justin Lovell
cab5825b72 Flexible Dedicated Hosting Options
* Fixed minor errors on the original LUA scripting
* Refactored code to be self-contained to a function
* Changed the search logic to use an environment variable first, then
  fallback into other search options
2020-10-20 21:49:32 +11:00
Dan Albert
84beb2dfe5 Remove dead code. 2020-10-20 00:15:22 -07:00
Dan Albert
f8ac39fb82 Fix min/max inversion in wind setting. 2020-10-20 00:10:37 -07:00
Dan Albert
eb69d01067 Add distance and ground speed to the kneeboard. 2020-10-19 23:25:46 -07:00
Dan Albert
1c4f255c7f Add waypoint departure time to the kneeboard. 2020-10-19 22:44:54 -07:00
Dan Albert
4125f6ec06 Don't scale waypoint info text size when zooming. 2020-10-19 22:26:31 -07:00
Dan Albert
5023e0d30f Fix disabled delete button in package UI.
The selection changed handler isn't called for the initial selection.
2020-10-19 21:34:17 -07:00
Dan Albert
f65595c626 Automatically select newly created packages. 2020-10-19 21:17:24 -07:00
Dan Albert
916d1eec96 Limit flight size to available aircraft. 2020-10-19 20:46:18 -07:00
Dan Albert
c2d615315e Add client slot selection to new flight window. 2020-10-19 20:29:55 -07:00
Dan Albert
aa96ce7134 Fix cancel of new package. 2020-10-19 01:21:49 -07:00
Dan Albert
01f83e8451 Place CAP racetracks more defensively.
Ensure that we're never putting a CAP race track within 20 nmi of an
enemy airfield (aside from the target airfield, if the target is
hostile).
2020-10-19 00:12:36 -07:00
David Pierron
ed92e9afb9 changed the system to make use of JSON files 2020-10-18 18:23:31 +02:00
Dan Albert
064890c0a2 Mark the F-16 as SEAD capable.
Latest OB update has HARMs for the F-16.
2020-10-17 16:47:41 -07:00
Dan Albert
8617f48fc2 Sort air unit purchases properly, and by name.
Previously we were sorting by task first and price second. Much easier
to find things if they're sorted by name (although longer term we
should make the sort option selectable).
2020-10-17 16:46:54 -07:00
Dan Albert
2269cf0f08 Show objective value per turn in the tooltip. 2020-10-17 16:11:28 -07:00
Dan Albert
3d41eb1ab4 Clean up CAP types.
Stop using "CAP". Use BARCAP or TARCAP instead.

TARCAP no longer allowed anywhere but front lines, since that's all we
have mission planning for right now. Later will add TARCAP and BARCAP
for all objective types with different timing profiles.

Part two of the fix for
https://github.com/Khopa/dcs_liberation/issues/210.
2020-10-17 14:32:09 -07:00
Dan Albert
cace523aa8 Avoid crash for custom/empty flight plans.
Fixes https://github.com/Khopa/dcs_liberation/issues/210
2020-10-17 14:10:54 -07:00
Dan Albert
002f55dc04 Update the map from the main window.
This guarantees that we update the map *after* updating the model that
the map uses to draw flight plans. Without this, after creating a new
game we'd redraw the previous game's flight plans because the model
hadn't been updated by the time the map was.

Fixes https://github.com/Khopa/dcs_liberation/issues/212
2020-10-17 13:33:28 -07:00
Dan Albert
49b6951ac3 Generate weather conditions at turn start.
Weather and exact time of day information is helpful during mission
planning, so generate it at the start of the turn rather than at
takeoff time.

Another advantage aside from planning is that we can now use the wind
information to set carrier headings and takeoff runways appropriately.
2020-10-16 18:25:25 -07:00
Dan Albert
7aa17e5ad6 Fix package/flight selection signals.
Qt helpfully converts None to 0 for us, so use -1 instead of None.
2020-10-16 14:12:27 -07:00
Dan Albert
9db41270f3 Add red ATO cheat option, show red flight plans. 2020-10-16 13:43:01 -07:00
Dan Albert
e4852c74ab Remove completed TODOs/dead code. 2020-10-16 13:00:59 -07:00
Dan Albert
613f84aa3c Add aircraft-specific speed estimates. 2020-10-16 03:10:14 -07:00
Dan Albert
2814876976 Fix A-10C II radio data.
These are two different planes.
2020-10-16 03:09:21 -07:00
Dan Albert
69bf3999aa Fix new package double flight delete.
This button was connected twice.
2020-10-16 03:08:48 -07:00
Dan Albert
2fa3b26119 Improve speed estimations.
Reasonable ground speed depends a lot on altitude, so plumb that
information through to the speed estimator.

Also adds calculations for ground speed based on desired mach. I don't
know if DCS is using the same formulas, but we should at least be
pretty close.
2020-10-16 02:11:50 -07:00
Dan Albert
5a027c552e Point players to the kneeboard from the briefing. 2020-10-15 21:43:41 -07:00
Dan Albert
9efecf9514 Fix inventory handling for new packages. 2020-10-15 21:33:04 -07:00
Dan Albert
8b87c43869 Warn the player about misconfigured TOTs. 2020-10-15 21:04:22 -07:00
Dan Albert
f7fec834e6 Add a button to automatically set the package TOT. 2020-10-15 20:23:57 -07:00
Dan Albert
de43a1215c Update departure time when TOT is changed.
Fixes https://github.com/Khopa/dcs_liberation/issues/207
2020-10-15 18:03:03 -07:00
Dan Albert
8dc531bb7f Make the departure time non-editable.
Fixes part 2 of https://github.com/Khopa/dcs_liberation/issues/207.
2020-10-15 17:57:25 -07:00
Khopa
da2584d7ee Updated pydcs version 2020-10-15 20:58:06 +02:00
David Pierron
373924a959 small changes to veaf plugin 2020-10-15 16:21:39 +02:00
Khopa
f75032bd79 Put 2.2.0-preview as version string 2020-10-15 12:17:33 +02:00
Khopa
4203dc5d41 Prepared changelog first draft for version 2.2.0 2020-10-14 23:34:09 +02:00
Khopa
9fe1f8ff90 Now use the new sea map to place boats and offshore buildings. 2020-10-14 23:30:33 +02:00
Khopa
bc825f760d Added sea zones for each map, and display it in polygon map mode. 2020-10-14 23:21:16 +02:00
David Pierron
191199d9de Merge remote-tracking branch 'upstream/develop' into new-plugin-system 2020-10-14 09:01:37 +02:00
Khopa
883a66a792 Fix : Added missing channel.json file 2020-10-13 00:35:27 +02:00
Khopa
411e71b9a2 Added setting to display culling distance 2020-10-12 20:57:32 +02:00
David Pierron
5463505787 added documentation for plugins system 2020-10-12 20:01:27 +02:00
David Pierron
ec6fc076de multiple changes
- load plugins when loading a game
- moved plugins scripts to resources/plugins (for pyinstaller)
- removed vanilla JTAC and JTAC_smoke options and settings GUI
- call JtacAutolasePlugin in armor.py
- made a dictionary of INSTALLED_PLUGINS
- removed NIOD from the VEAF plugin
2020-10-12 19:49:39 +02:00
Khopa
5a245bf362 Fixed crash in polygon display mode for Nevada map. 2020-10-12 18:53:15 +02:00
Khopa
d95912322c Added Polygon drawing mode for map background 2020-10-12 18:48:50 +02:00
Khopa
9c58e73b39 Added more display options for SAMS 2020-10-12 18:12:45 +02:00
David Pierron
3c4ccd7d57 Merge remote-tracking branch 'upstream/develop' into new-plugin-system 2020-10-12 17:29:54 +02:00
David Pierron
d22943d755 added a customizable plugin system
- the base LUA functionality has been implemented as a mandatory plugin
- the jtacautolase functionality has been implemented as a plugin
- added a VEAF framework plugin

The plugins have GUI elements in the Settings window.
2020-10-12 17:27:13 +02:00
Dan Albert
974b6590d8 Estimate TOTs for packages.
We estimate the longest possible time from mission start to TOT for
all flights in a package and use that to set the TOT (plus any delay
used to stagger flights). This both cuts down on loiter time for
shorter flights and ensures that long flights will make it to the
target in time.

This is also used to compute the start time for the AI, so the
explicit delay option is no longer needed.
2020-10-12 01:00:47 -07:00
Dan Albert
d414c00b74 Make waypoint info more readable. 2020-10-10 16:52:59 -07:00
Khopa
7b79d183eb Changelog 2.1.5 did not make it on this branch somehow 2020-10-11 00:48:13 +02:00
Dan Albert
edd56cb407 Fix bad import of THEMES. 2020-10-10 14:11:44 -07:00
Dan Albert
c777204f50 Draw waypoint information on the map. 2020-10-10 14:09:10 -07:00
Dan Albert
55f12f20c1 Fix missing default parameter. 2020-10-10 14:05:25 -07:00
Dan Albert
1ac062653d Make the starting budget text editable. 2020-10-10 13:00:59 -07:00
Dan Albert
b0a176a22c Make "all" the default flight path display option. 2020-10-10 12:43:51 -07:00
Khopa
f4b07cb518 New game wizard : added to slider to choose starting money 2020-10-10 16:21:41 +02:00
Khopa
a0ff78a810 Merge branch 'develop' of https://github.com/khopa/dcs_liberation into develop
 Conflicts:
	qt_ui/widgets/map/QLiberationMap.py
2020-10-10 15:46:23 +02:00
Khopa
f22391855b Fixed SAM threat radius scale, added detection range, changed UI colors for SAMS range. 2020-10-10 15:43:58 +02:00
Dan Albert
1fa18447e1 Show player slots in the overview. 2020-10-09 20:07:54 -07:00
Dan Albert
2d8c8c63c9 Improve flight path display options.
Adds an option show only selected flight, and also changes the show
all option to highlight the selected flight.
2020-10-09 16:49:32 -07:00
Dan Albert
31d5e3151b Refactor display rules. 2020-10-09 14:54:58 -07:00
David Pierron
c77bfe9da2 plugin base : inject mission configuration data 2020-10-09 21:25:21 +02:00
David Pierron
9a7dfc55e3 Merge remote-tracking branch 'upstream/develop' into develop 2020-10-09 21:03:12 +02:00
C. Perreau
63bc3bd46e Merge pull request #193 from DanAlbert/tot
Plan waypoint TOTs.
2020-10-09 13:25:08 +02:00
Dan Albert
b5e5a3b2da Plan waypoint TOTs.
Also fixes the CAP racetracks so the AI actually stays on station.

Waypoint TOT assignment happens at mission generation time for the
sake of the UI. It's a bit messy since we have the late-initialized
field in FlightWaypoint, but on the other hand we don't have to reset
every extant waypoint whenever the player adjusts the mission's TOT.

If we want to clean this up a bit more, we could have two distinct
types for waypoints: one for the planning stage and one with the
resolved TOTs. We already do some thing like this with Flight vs
FlightData.

Future improvements:

* Estimate the group's ground speed so we don't need such wide margins
  of error.
* Delay takeoff to cut loiter fuel cost.
* Plan mission TOT based on the aircraft in the package and their
  travel times to the objective.
* Tune target area time prediction. Flights often don't need to travel
  all the way to the target point, and probably won't be doing it
  slowly, so the current planning causes a lot of extra time spent in
  enemy territory.
* Per-flight TOT offsets from the package to allow a sweep to arrive
  before the rest, etc.
2020-10-09 01:08:34 -07:00
Dan Albert
7abe32be5c Fix name of decent waypoint. 2020-10-09 00:26:57 -07:00
Dan Albert
f0279a6866 Ignore the entire logs directory.
We use a rotating log handler, so we generate more than just the one
file.
2020-10-08 22:49:04 -07:00
Dan Albert
5ce942c9a0 Confirm mission start when no client slots exist.
Especially considering the button in this position used to be how
players added client slots, confirm that they in fact want to launch
an AI-only mission before launching, and guide them toward the new UI.
2020-10-07 17:09:57 -07:00
David Pierron
59d6cc7625 Merge remote-tracking branch 'upstream/develop' into develop 2020-10-07 09:21:41 +02:00
Dan Albert
4abf806837 Fix initial frequencies for support aircraft.
Vaicom (a mod that adds voice control for the communications menus)
isn't able to follow the waypoint frequency change that normally sets
the radio channel for the AWACS/tanker flights. Set the group's
frequency correctly to start so it works.
2020-10-06 23:44:11 -07:00
Dan Albert
6bfb8cf2fd Fix double logging.
Calling logging.basicConfig creates a stream handler for the root
logger, and then we were adding our own with a different formatter.
Pass the format string to basicConfig so we don't need to add our own
duplicate stream handler.
2020-10-06 23:31:40 -07:00
Dan Albert
1d7f1082ea Fix logging by deferring campaign data loading.
Logging before we've made it to the logging setup was causing the root
logger to be permanently configured to the default (warning) log
level, so we weren't getting any info or debug logs any more.

Defer the campaign data load until it is needed rather than doing it
at import time. I've also cleaned up a bit so we only load each
campaign once, rather than re-loading the campaign to create the
theater again after the wizard is finished.
2020-10-06 23:26:18 -07:00
Dan Albert
1c4aec83cb Clean up start-up logging.
Most of this wasn't helpful. What was is now logging instead of print
so it can be configured.
2020-10-06 22:24:47 -07:00
Dan Albert
e537396fec Stagger package start times.
Avoids crowding the taxiways, and adds some life to the end of the
mission.

Later on, this will happen more naturally because we can delay
takeoffs to align with the package's DTOT.
2020-10-06 21:51:23 -07:00
Dan Albert
944748a0ac Don't throw away exception info on save/load.
Makes it much easier to determine what we did that broke save game
compatibility.
2020-10-06 21:50:44 -07:00
Dan Albert
023925d741 Set preferred mission types for aircraft. 2020-10-06 17:30:20 -07:00
Dan Albert
93db1254ec Improve insufficient aircraft message.
Specify only the tasks which were unfulfilled so the player knows what
to buy.
2020-10-06 17:29:42 -07:00
Dan Albert
e0725ff139 Add escort mission planning to the UI. 2020-10-06 17:29:19 -07:00
Dan Albert
9e96aee89f Add default escort loadouts. 2020-10-06 17:29:11 -07:00
Dan Albert
db6b660270 Fix mypy issues in all modules except qt_ui. 2020-10-06 17:24:08 -07:00
Dan Albert
1808e5bccf Add initial mypy.ini. 2020-10-06 17:01:37 -07:00
Dan Albert
5f1601a2da Remove the userdata package. 2020-10-06 17:01:37 -07:00
Khopa
60ce6658ad Campaigns are sorted by terrain in new game wizard 2020-10-07 01:12:16 +02:00
Khopa
e664652cc5 Now properly merged operation.py 2020-10-07 01:07:56 +02:00
Khopa
ca48a42701 Revert "Reverted automerge errors."
This reverts commit 00ea8ac4
2020-10-07 00:59:18 +02:00
Khopa
00ea8ac4e1 Reverted automerge errors. 2020-10-07 00:56:42 +02:00
Khopa
de5238e89a Fixed issue in Normandy small campaign 2020-10-07 00:27:43 +02:00
Khopa
3bb1327a65 Manuall reintroduced inverted campaign config in json campaign files 2020-10-07 00:25:02 +02:00
Khopa
71f77dd8fb Added moddable campaigns through json files. 2020-10-07 00:09:11 +02:00
C. Perreau
9101dae38a Merge pull request #184 from DanAlbert/waypoint-planning
Improve automated mission planning.
2020-10-06 22:04:58 +02:00
C. Perreau
1f18bf2bd8 Merge branch 'develop' into waypoint-planning 2020-10-06 22:01:44 +02:00
C. Perreau
41445c3092 Merge pull request #187 from Khopa/develop_2_1_x
2.1.5
2020-10-05 19:45:30 +02:00
Khopa
2a3bf9821b Fixed EPLRS cherry pick merge. 2020-10-05 19:33:48 +02:00
Khopa
819d775282 EPLRS for 2.1.5 2020-10-05 19:30:43 +02:00
Khopa
efbc6fe3ae Enable EPLRS for ground units that can use it. 2020-10-05 19:29:34 +02:00
Khopa
262ba6c113 Chery picked another fix for this release, so updated changelog 2020-10-05 19:28:55 +02:00
Khopa
c41ecb6735 Fix aircrafts landing point 2020-10-05 19:27:03 +02:00
Khopa
f5c32c6b98 Version 2.1.5 2020-10-05 19:26:07 +02:00
Dan Albert
a1886e37f8 Fix save issues after aborting mission.
When the mission is aborted the pending mission is still in the event
list, which is part of the game option. That event has a reference to
the operation, which in turn contains all the mission generator
objects. Two of these objects are the radio/TACAN allocators, which
use a generator to track the next free channel. Generators cannot be
picked, so because these are transitively part of the game object the
game cannot be saved.

Aside from the briefing generator, none of those objects are
actually needed outside the generation function itself, so just make
them locals instead.

This probably needs a larger refactor at some point. It doesn't look
like we need so many calls into the operation type (it has an
initialize, a prepare, and a generate, and it doesn't seem to need
anything but the last one). The only reason breifinggen needs to
remain a part of the class is because the briefing title and
description are filled in from the derived class, where title and
description should probably be overridden properties instead. I'm also
not sure if we need to make the event list a part of game at all, and
also don't think that the mission needs to be one of these events.
2020-10-05 19:20:57 +02:00
Dan Albert
f040804d02 Fix save issues after aborting mission.
When the mission is aborted the pending mission is still in the event
list, which is part of the game option. That event has a reference to
the operation, which in turn contains all the mission generator
objects. Two of these objects are the radio/TACAN allocators, which
use a generator to track the next free channel. Generators cannot be
picked, so because these are transitively part of the game object the
game cannot be saved.

Aside from the briefing generator, none of those objects are
actually needed outside the generation function itself, so just make
them locals instead.

This probably needs a larger refactor at some point. It doesn't look
like we need so many calls into the operation type (it has an
initialize, a prepare, and a generate, and it doesn't seem to need
anything but the last one). The only reason breifinggen needs to
remain a part of the class is because the briefing title and
description are filled in from the derived class, where title and
description should probably be overridden properties instead. I'm also
not sure if we need to make the event list a part of game at all, and
also don't think that the mission needs to be one of these events.
2020-10-05 02:17:14 -07:00
Khopa
e27625556c Exported existing campaigns to json objects. 2020-10-04 22:09:57 +02:00
Dan Albert
b13711ddef Update the waypoint builder UI.
Changing targets doesn't make sense now that flights belong to a
package. Change all the "generate" dialogs to simply confirm dialogs
to make sure the user is okay with us clobbering the flight plan.
2020-10-04 12:24:31 -07:00
Dan Albert
6ce82be46b Set up split/join points. 2020-10-04 12:24:31 -07:00
Dan Albert
56a5864600 Generate common ingress/egress points.
This still isn't very good because it doesn't work well for anything
but the automatically planned package.

Instead, should be a part of the Package itself, generated the first
time it is needed, and resettable by the user.
2020-10-04 12:24:31 -07:00
Dan Albert
07cbaa3e70 Plan escort flights.
TODO: UI
2020-10-04 12:24:31 -07:00
Dan Albert
2aecea88b0 Orient CAP tracks toward the enemy.
Pointing the race track 90 degrees away from where the enemy is
expected means the radar can't see much. CAP flights normally fly
*toward* the expected direction of contact and alternate approaching
and retreating legs with their wingman.
2020-10-04 12:24:31 -07:00
Dan Albert
582c43fb6c Generate CAP missions in useful locations.
CAP missions should be between the protected location and the nearest
threat. Find the closest enemy airfield and ensure that the CAP race
track is between it and the protected location.
2020-10-04 12:24:26 -07:00
Dan Albert
cc7c2cc707 Refactor flight plan generation. 2020-10-04 12:24:26 -07:00
Dan Albert
8b717c4f4c Replace doctrine dict with a real type. 2020-10-04 12:24:26 -07:00
Dan Albert
aa309af015 Redraw flight plans when they change. 2020-10-04 12:24:26 -07:00
Dan Albert
1e041b6249 Perform coalition-wide mission planning.
Mission planning on a per-control point basis lacked the context it
needed to make good decisions, and the ability to make larger missions
that pulled aircraft from multiple airfields.

The per-CP planners have been replaced in favor of a global planner
per coalition. The planner generates a list of potential missions in
order of priority and then allocates aircraft to the proposed flights
until no missions remain.

Mission planning behavior has changed:

* CAP flights will now only be generated for airfields within a
  predefined threat range of an enemy airfield.
* CAS, SEAD, and strike missions get escorts. Strike missions get a
  SEAD flight.
* CAS, SEAD, and strike missions will not be planned unless
  they have an escort available.
* Missions may originate from multiple airfields.

There's more to do:

* The range limitations imposed on the mission planner should take
  aircraft range limitations into account.
* Air superiority aircraft like the F-15 should be preferred for CAP
  over multi-role aircraft like the F/A-18 since otherwise we run the
  risk of running out of ground attack capable aircraft even though
  there are still unused aircraft.
* Mission priorities may need tuning.
* Target areas could be analyzed for potential threats, allowing
  escort flights to be optional or omitted if there is no threat to
  defend against. For example, late game a SEAD flight for a strike
  mission probably is not necessary.
* SAM threat should be judged by how close the extent of the SAM's
  range is to friendly locations, not the distance to the site itself.
  An SA-10 30 nm away is more threatening than an SA-6 25 nm away.
* Much of the planning behavior should be factored out into the
  coalition's doctrine.

But as-is this is an improvement over the existing behavior, so those
things can be follow ups.

The potential regression in behavior here is that we're no longer
planning multiple cycles of missions. Each objective will get one CAP.
I think this fits better with the turn cycle of the game, as a CAP
flight should be able to remain on station for the duration of the
turn (especially with refueling).

Note that this does break save compatibility as the old planner was a
part of the game object, and since that class is now gone it can't be
unpickled.
2020-10-04 12:24:26 -07:00
Khopa
1f240b02f4 Fix aircrafts landing point 2020-10-04 14:27:13 +02:00
Khopa
6317f376b7 EPLRS typo fixed 2020-10-04 14:11:28 +02:00
Khopa
5ecf9aeed8 EPLRS implemented for base defense unit. 2020-10-03 18:32:11 +02:00
Khopa
db36a76c2c Fixed eplrs for frontline ground units 2020-10-03 17:35:06 +02:00
Khopa
3df8fb5fe9 Merge branch 'develop' into develop_mission_planner 2020-10-03 17:06:07 +02:00
Khopa
7dd3367203 Version number for release 2.1.4 2020-10-03 16:50:56 +02:00
Khopa
e72c82521a Forgot the changelog for 2.1.4 2020-10-03 16:45:21 +02:00
C. Perreau
aca415db23 Merge pull request #179 from Khopa/develop
Release 2.1.4
2020-10-03 16:33:26 +02:00
Khopa
5ba2e8a7a1 Fixed error after merge 2020-10-03 16:18:32 +02:00
Khopa
07d4b126f5 Enable EPLRS for ground units that can use it. 2020-10-03 16:18:12 +02:00
David Pierron
98b2d8b3b9 typo in the generate_initial_units function name 2020-10-02 13:15:40 -07:00
David Pierron
f8ef5db5a3 bug when continuing an old campaign save 2020-10-02 13:15:40 -07:00
C. Perreau
6e14ec3227 Merge pull request #147 from DanAlbert/ato
Replace mission planning UI.
2020-10-02 13:31:45 +02:00
C. Perreau
028292a023 Merge branch 'develop_mission_planner' into ato 2020-10-02 13:26:21 +02:00
David Pierron
a38f2e36a2 Merge remote-tracking branch 'upstream/develop' into develop 2020-10-02 09:25:18 +02:00
Dan Albert
a44cbe5972 Tone down failure message for missing plugin file.
This looked an awful lot like an error, but it's the common case.
2020-10-01 23:57:53 -07:00
C. Perreau
2066a2e9bc Merge pull request #167 from Khopa/develop
Release 2.1.3
2020-10-01 23:23:06 +02:00
Khopa
f381bf85a4 Fixed JTAC script not working after changes made to lua files 2020-10-01 23:17:32 +02:00
Khopa
44dcdcc8bb Changelog update 2020-10-01 23:17:08 +02:00
Khopa
01220800f3 Fixed Viggen icon 2020-10-01 22:47:58 +02:00
Khopa
8ecb4cdcf4 Added more ground vehicles icons. 2020-10-01 21:09:05 +02:00
Khopa
8402d108c0 Pydcs location 2020-10-01 20:22:11 +02:00
Khopa
75af2d468e Fix A-10C_II icon 2020-10-01 19:38:43 +02:00
Khopa
72ce37f008 Added custom payloads for A-10C II 2020-10-01 19:33:46 +02:00
Khopa
e48e884286 Added A-10C_2. Changed bluefor factions' country to "Combined Joint Task Forces Blue" instead of "USA" (support more units) 2020-10-01 19:20:25 +02:00
Khopa
f9d5c1f8de Update pydcs location 2020-10-01 19:07:06 +02:00
Khopa
0873dcab0a Gitmodule points to pydcs 2020-10-01 19:06:38 +02:00
C. Perreau
a1343c2849 Merge pull request #166 from DanAlbert/build-canaries
Build and archive binaries on push/PR.
2020-10-01 12:53:35 +02:00
Dan Albert
f732cc54d0 Build and archive binaries on push/PR.
Not building a full release, but this makes it easier to test someone
else's PR, or for players to get at pre-release builds.
2020-09-30 21:03:12 -07:00
David Pierron
473a7d5fa4 added a 'mkrelease' config for VS.Code 2020-09-30 19:47:02 -07:00
David Pierron
8054a0b62f removed useless link.cmd.sample file 2020-09-30 19:47:02 -07:00
Dan Albert
7f9cba5d37 Fix more None ATC bugs.
Fixes https://github.com/Khopa/dcs_liberation/issues/164
2020-09-30 19:20:50 -07:00
David Pierron
5807fbf896 added a 'mkrelease' config for VS.Code 2020-09-30 13:35:09 +02:00
David Pierron
2c1dc6a18d removed useless link.cmd.sample file 2020-09-30 13:34:41 +02:00
C. Perreau
f68e6387e6 Merge pull request #161 from VEAF/make-mission-portable
Make mission portable
2020-09-29 23:50:34 +02:00
Khopa
f032001bee Limit number of aircraft that can be bought at a Control Point. 2020-09-29 23:47:57 +02:00
David Pierron
3bae591c04 corrected and enhanced mission portability 2020-09-29 20:46:22 +02:00
Khopa
18a1f0af94 Added credits to new contributor to about dialog. 2020-09-29 20:03:07 +02:00
David Pierron
afbd4a4716 Make mission portable
use inline json.lua and write to %LIBERATION_EXPORT_DIR%, %TEMP%
or the DCS working directory
2020-09-29 17:27:35 +02:00
David Pierron
a98da14c6f Merge pull request #1 from Khopa/master
Merge from base
2020-09-29 15:14:55 +02:00
Khopa
a2a70213a7 Update pydcs location 2020-09-28 00:55:02 +02:00
Khopa
8b0f877041 Verrsion string updated to 2.1.2 2020-09-28 00:42:27 +02:00
Khopa
bf7ad4cad2 Merge remote-tracking branch 'khopa/master' into develop 2020-09-28 00:27:25 +02:00
C. Perreau
66b659c0af Merge pull request #152 from VEAF/introduced-scripts-plugins
Introduced LUA scripts plugins
2020-09-28 00:26:26 +02:00
Khopa
8709ea948f Merge remote-tracking branch 'khopa/master' into develop 2020-09-28 00:21:33 +02:00
Khopa
7236c10403 Changelog update 2020-09-28 00:21:12 +02:00
C. Perreau
dde703ec41 Merge pull request #154 from VEAF/add-tanker-type-to-tanker-name
add tanker type to tanker name
2020-09-28 00:06:49 +02:00
Khopa
aa2e9b123c Fix : AI is not planning flights for Tornado. 2020-09-28 00:03:01 +02:00
Dan Albert
a3c06ce6e0 Limit task type combo box to valid mission types. 2020-09-27 13:44:58 -07:00
Dan Albert
0e1dfb8ccb Implement CAP and CAS for front lines. 2020-09-27 13:44:58 -07:00
Dan Albert
8a4a81a008 Remove unused frontline code. 2020-09-27 13:44:58 -07:00
Dan Albert
ff083942e8 Replace mission planning UI.
Mission planning has been completely redone. Missions are now planned
by right clicking the target area and choosing "New package".

A package can include multiple flights for the same objective. Right
now the automatic flight planner is only fragging single-flight
packages in the same manner that it used to, but that can be improved
now.

The air tasking order (ATO) is now the left bar of the main UI. This
shows every fragged package, and the flights in the selected package.
The info bar that was previously on the left is now a smaller bar at
the bottom of the screen. The old "Mission Planning" button is now
just the "Take Off" button.

The flight plan display no longer shows enemy flight plans. That could
be re-added if needed, probably with a difficulty/cheat option.

Aircraft inventories have been disassociated from the Planner class.
Aircraft inventories are now stored globally in the Game object.

Save games made prior to this update will not be compatible do to the
changes in how aircraft inventories and planned flights are stored.
2020-09-27 13:44:58 -07:00
Khopa
737e04d09e Merge branch 'master' into develop 2020-09-27 19:16:34 +02:00
C. Perreau
0fe59efd72 Merge pull request #157 from DanAlbert/fix-none
Fix None dereference.
2020-09-27 19:10:31 +02:00
C. Perreau
ddb50e6254 Merge pull request #151 from VEAF/UHF-intraflight-frequency-for-Player-and-Clients-A-10C
UHF Intraflight Frequency for Player and Clients A-10C
2020-09-27 19:09:33 +02:00
Dan Albert
72e6ae4186 Fix None dereference. 2020-09-26 16:32:02 -07:00
David Pierron
4d510f643a add tanker type to tanker name 2020-09-25 17:11:17 +02:00
David Pierron
66f607b5e6 added a comment that links to my forum post 2020-09-25 11:33:07 +02:00
David Pierron
1a125c62e7 added sample __plugins.lst file 2020-09-25 11:21:23 +02:00
David Pierron
84da44a27b Introduced LUA scripts plugins
In order to be able to customize the scripts that can be injected in the
mission, a __plugin.lst file is read and the scripts mentionned in this
file are injected (through DoScriptFile and not DoScript).

A mechanism checks if a standard script (Mist, JTACAutolase) has
already been loaded, to avoid loading them twice.
2020-09-25 11:06:25 +02:00
David Pierron
5e35efcaef UHF Intraflight Frequency for Player and Clients A-10C
In the mission editor, using a VHF frequency for a Player or
Client A-10C results in an error. Changed the radio definitions
to use AN/ARC-164 for intraflight comms.
2020-09-25 10:36:18 +02:00
Khopa
83f221c5e3 Use latest data-export 2020-09-25 01:23:05 +02:00
Khopa
e86a10e9ba JF-17 radio frequency fix. 2020-09-25 01:13:59 +02:00
C. Perreau
7eea328706 Merge pull request #149 from Khopa/develop
2.1.1
2020-09-25 01:06:09 +02:00
Khopa
aa9dcec0ad Version number 2020-09-25 00:58:11 +02:00
Khopa
e9bad2c7eb Version number update and changelog update 2020-09-25 00:56:12 +02:00
Khopa
ce257a31bb Fixed UI bugs when buying new units in base defense menu. 2020-09-25 00:36:13 +02:00
Khopa
c96b5cf4d7 Fixed bug when buying armor at base 2020-09-25 00:25:20 +02:00
Khopa
fb40e9273d Added newest contributors to about dialog. 2020-09-24 20:00:57 +02:00
Khopa
42c9af102b Added KJ 2000 to China as AWACS 2020-09-24 19:56:56 +02:00
Khopa
b7dff59542 P_47 variants added to db 2020-09-24 18:03:10 +02:00
Khopa
266927aa9a Added new payloads for F-16C for SEAD and CAS, added defaults payload for new P-47 variants. 2020-09-24 18:01:20 +02:00
Dan Albert
0eee5747af Clean up flight path drawing code. 2020-09-24 01:01:26 -07:00
Dan Albert
80f2b7a1db Cleanups in map object UI.
* Fix the context menu
* Remove unnecessary overrides
* Clean up formatting/naming
* Factor out base class for shared behavior
2020-09-24 01:01:26 -07:00
C. Perreau
dcaa8d4e96 Merge pull request #142 from DanAlbert/update-pydcs
Update pydcs.
2020-09-23 00:39:17 +02:00
Khopa
0e2a449553 Fixes to Buy/Sell sam UI 2020-09-23 00:39:01 +02:00
Dan Albert
a70e035e02 Update pydcs.
This fixes the issue where delayed AI flights would not start if the
mission was not first saved via the DCS mission editor.
2020-09-20 15:07:32 -07:00
Khopa
4019da8ba9 Base defense armor unit icon style fix. 2020-09-20 17:15:43 +02:00
Khopa
5042ac1789 P-51/P-47 radios support. 2020-09-20 17:07:09 +02:00
Khopa
4031c9b978 Possible to sell/buy units at SAM location and in airports. 2020-09-19 17:11:43 +02:00
Khopa
65dd9bc286 Fix typo. 2020-09-19 17:10:56 +02:00
Khopa
e210dcb4df Added disband menu in ground object menu. 2020-09-19 14:02:53 +02:00
C. Perreau
52a379229e Merge pull request #134 from pedromagueija/master
Fix typo in CombatStance enumeration
2020-09-17 10:12:27 +02:00
Pedro Magueija
93b2e91c10 Fix usage of CombatStance typo 2020-09-15 15:03:41 +02:00
Pedro Magueija
b8afb01c46 Fix typo in CombatStance enumeration 2020-09-15 14:58:17 +02:00
C. Perreau
59e7665b65 Merge pull request #133 from DanAlbert/radio-naming
Name radios appropriately for each aircraft.
2020-09-12 22:35:49 +02:00
C. Perreau
8a8a835a32 Merge pull request #132 from DanAlbert/fix-radio-reservation
Ensure that allocated channels are reserved.
2020-09-12 22:35:00 +02:00
Dan Albert
6ceab69656 Name radios appropriately for each aircraft. 2020-09-12 12:30:52 -07:00
Dan Albert
9e98f05be0 Ensure that allocated channels are reserved.
This was previously mostly working because the allocator itself was
moving forward, but since each radio has its own allocator, aircraft
with different radios would often get overlapping intra-flight
frequencies.
2020-09-12 12:28:59 -07:00
Khopa
5da1db91fd Normandy airfields data export. 2020-09-12 15:49:30 +02:00
C. Perreau
f0d58acd62 Merge pull request #131 from DanAlbert/more-aircraft-radios
Add radio information for more aircraft.
2020-09-12 11:36:43 +02:00
Dan Albert
722ec00076 Add radio information for more aircraft.
Adds the following:

* AJS37
* AV-8B
* JF-17

This does move the preset channel allocation logic into its own class,
since we need to customize that behavior for the AJS37 since it has a
rather unique preset channel layout (see the comments in
`ViggenRadioChannelAllocator` for details).
2020-09-11 18:45:19 -07:00
Khopa
2db740e1ad Finished airfield export for PG map. 2020-09-11 22:00:22 +02:00
C. Perreau
70cd0e8c31 Merge pull request #130 from DanAlbert/callsigns
Handle callsigns for flights.
2020-09-11 20:53:44 +02:00
C. Perreau
848c92ec25 Merge pull request #129 from DanAlbert/fix-kneeboard-tacan
Fix TACAN/ILS info for airfields.
2020-09-11 20:52:15 +02:00
C. Perreau
98946d0a63 Merge pull request #128 from DanAlbert/coalesce-target-points
Improve target waypoint behavior in the kneeboard.
2020-09-11 20:51:52 +02:00
C. Perreau
e0a39104b1 Merge pull request #127 from DanAlbert/first-waypoint
Add first waypoint to FlightData.
2020-09-11 20:48:04 +02:00
C. Perreau
a4f66298a4 Merge pull request #126 from DanAlbert/syria-map-data
Add map data for Syria.
2020-09-11 20:47:32 +02:00
Dan Albert
993bf50012 Handle callsigns for flights.
We don't configure the callsigns that pydcs uses, so instead read
those from pydcs and use them where appropriate instead of just
guessing.

Fixes https://github.com/Khopa/dcs_liberation/issues/113.
2020-09-11 01:47:13 -07:00
Dan Albert
51bfc9a59b Update pydcs. 2020-09-11 01:47:13 -07:00
Dan Albert
837795e87a Fix TACAN/ILS info for airfields. 2020-09-10 17:05:32 -07:00
Dan Albert
d4820b2435 Coalesce large runs of target waypoints.
Since we create a target waypoint for every target in a
strike/SEAD/DEAD objective area (including every ground vehicle), the
kneeboard can quickly be overrun with target waypoints. When there are
many target waypoints, collapse them all into a single row for
brevity.
2020-09-10 01:38:27 -07:00
Dan Albert
180537cd48 Fix SEAD/DEAD reversal. 2020-09-10 01:38:27 -07:00
Dan Albert
8bc77bbf18 Make waypoint types less error prone.
Make the type of the waypoint a non-optional part of the constructor.
Every waypoint needs a type, and there's no good default (the previous
default, `TAKEOFF`, is actually unused). All of the target waypoints
were mistakenly being set as `TAKEOFF`, so I've fixed that in the
process.

Also, fix the bug where only the last custom target of a SEAD
objective was being added to the waypoint list because the append was
scoped incorrectly.
2020-09-10 01:38:25 -07:00
Dan Albert
474b606524 Add map data for Syria. 2020-09-08 16:45:08 -07:00
Dan Albert
8a7e43ef42 Also dedup ATC frequencies.
Some airports on the Syria map share ATC frequencies.
2020-09-08 16:45:08 -07:00
C. Perreau
7b5b486f0e Merge pull request #123 from DanAlbert/fix-radios
Fix radio information.
2020-09-07 23:50:36 +02:00
Dan Albert
ebedc02a0a Add first waypoint to FlightData.
The first waypoint is automatically added by pydcs, so it's not
actually in our waypoint list from the flight planner. Import is from
the group so it shows up in the kneeboard.
2020-09-06 22:07:10 -07:00
Dan Albert
ad42a3d956 Fix radio information.
Not every aircraft has a pydcs radio index, so we can't use that to
index into a list. Any mission with an A-10C crashes, since it would
try to use `None - 1` to index into the list of radios to find the
intra-flight radio.

Also fix the radio ranges for the newly added radios. The current
implementation can't model gaps, so extending the radio ranges across
those gaps means that we might allocate channels that aren't tunable
by those radios. Additionally, the end frequency is exclusive rather
than inclusive, so fix the ranges to include that last tunable
frequency.
2020-09-06 16:44:46 -07:00
Khopa
98d75bc721 Tomcat and M2000 radios support 2020-09-06 15:43:44 +02:00
Khopa
14fe68eb54 Added Mirage 2000 radios 2020-09-06 15:10:10 +02:00
Khopa
b6938c14ca Added southern airfields of PG map 2020-09-06 15:09:40 +02:00
Khopa
3c96c1d5b1 Removed incorrect imports causing pydcs being imported twice; 2020-09-06 12:33:40 +02:00
Khopa
2969ce2d56 Removed unused file 2020-09-06 12:11:03 +02:00
C. Perreau
bca92f5ee7 Merge pull request #122 from DanAlbert/fix-pydcs-path
Fix incorrect pydcs import paths.
2020-09-06 11:49:58 +02:00
C. Perreau
1c1982952e Merge pull request #121 from DanAlbert/preferred-runway
Pick ILS runways if possible.
2020-09-06 11:48:47 +02:00
Dan Albert
4b74b5a13d Fix incorrect pydcs import paths.
I've been wrongly importing these from `pydcs.dcs` instead of just
`dcs`, because that was what PyCharm thought they were. These will all
be broken when we get back to using a real pydcs instead of relying on
its directory being in our tree.

This page in the wiki should be updated:
https://github.com/Khopa/dcs_liberation/wiki/Developer's-Guide

Instead of recommending that `PYTHONPATH` be updated in the run
configuration, it should instead recommend that Settings -> Project:
dcs_liberation -> Project Structure be set to exclude the pydcs
directory from the dcs_liberation content root, and add the pydcs
directory as a *separate* content root.

Alternatively, we could recommend that configure a virtualenv (good
advice anyway, and pycharm knows how to set them up) that have people
run `pip install -e pydcs`.

I think even easier would be switching from the virtualenv-style
requirements.txt to pipenv, which can actually encode the `-e` style
pip install into its equivalent of requirements.txt.
2020-09-06 01:16:57 -07:00
Dan Albert
0fc00fac38 Pick ILS runways if possible. 2020-09-04 15:58:43 -07:00
C. Perreau
4446a7f060 Merge pull request #119 from DanAlbert/radio-setup
Allocate per-flight radio channels, set up preset channels.
2020-09-04 12:50:44 +02:00
Dan Albert
9d31c478d3 Fix briefing generation.
I removed the nav target info from the briefing because that doesn't
seem to have been doing what it was intended to do. It didn't give any
actual target information, all it would show was (example is a  JF-17
strike mission):

    PP1
    PP2
    PP3
    PP4

Without any additional context that doesn't seem too helpful to me.
I'll be following up (hopefully) shortly by adding target information
(type, coordinates, STPT/PP, etc) to both the briefing and the
kneeboard that will cover that.

Refactor a bunch to share some code with the kneeboard generator as
well.
2020-09-04 01:06:31 -07:00
Dan Albert
b31e186d1d Fix inconsistent runway numbering.
pydcs gives us a 3-digit runway, but most of our data is 2-digit
runway numbers, so we weren't finding any runways for those airfields.
2020-09-04 00:56:26 -07:00
Dan Albert
d02a3a0d3f Add carrier support to kneeboards. 2020-09-04 00:56:26 -07:00
Dan Albert
a9e65cc83d Setup default radio channels for player flights. 2020-09-03 15:02:59 -07:00
Dan Albert
010d505f04 Reserve frequencies used by beacons. 2020-09-03 15:02:59 -07:00
Dan Albert
95b9a3e1aa Check in beacon lists for most maps. 2020-09-03 15:02:59 -07:00
Dan Albert
d051859371 Add beacon list importer. 2020-09-03 15:02:59 -07:00
Dan Albert
af596c58c3 Control radio/TACAN allocation, set flight radios.
Add central registries for allocating TACAN/radio channels to the
Operation. These ensure that each channel is allocated uniquely, and
removes the caller's need to think about which frequency to use.

The registry allocates frequencies based on the radio it is given,
which ensures that the allocated frequency will be compatible with the
radio that needs it. A mapping from aircraft to the radio used by that
aircraft for intra-flight comms (i.e. the F-16 uses the AN/ARC-222)
exists for creating infra-flight channels appropriate for the
aircraft. Inter-flight channels are allocated by a generic UHF radio.

I've moved the inter-flight radio channels from the VHF to UHF range,
since that's the most easily allocated band, and inter-flight will be
in the highest demand.

Intra-flight radios are now generally not shared. For aircraft where
the radio type is not known we will still fall back to the shared
channel, but that will stop being the case as we gain more data.

Tankers have been moved to the Y TACAN band. Not completely needed,
but seems typical for most missions and deconflicts the tankers from
any unknown airfields (which always use the X band in DCS).
2020-09-03 15:02:59 -07:00
Dan Albert
b4e3067718 Update pydcs. 2020-09-03 15:02:59 -07:00
C. Perreau
40f8ae1cde Merge pull request #120 from DanAlbert/sort-objectives
Sort objective name combo boxes.
2020-09-03 21:36:06 +02:00
Dan Albert
f5f45a098e Sort objective name combo boxes. 2020-09-03 00:42:20 -07:00
Khopa
d429804c1f Airfields data for Nevada 2020-09-01 20:58:27 +02:00
Khopa
f5f770f401 Added airfields data for The Channel map. 2020-09-01 20:32:32 +02:00
Khopa
91d6ed0ee7 Completed Caucasus airfield data. 2020-09-01 20:23:52 +02:00
Khopa
06858b26c7 Added additional informations to the AirfieldData class 2020-09-01 13:47:41 +02:00
C. Perreau
7e60a43f53 Merge pull request #114 from DanAlbert/kneeboard
Generate kneeboards for player flights.
2020-09-01 12:51:04 +02:00
Dan Albert
e7e82dcd0b Build mission kneeboards.
This includes most of the briefing information in the kneeboard:

* Airfield info
* Waypoint info
* Comm info
* AWACS
* Tankers
* JTAC

There's more that could be done:

* Restrict tankers to the type compatible with the current aircraft
* Support for carriers
* Merge all relevant comm info (tankers, AWACS, JTAC, other flights)
  into the comm ladder

This gives us a good start and a framework to build on. Very likely
that we'll want to split part of this (probably the comm ladder) off
onto a separate page once we start adding more to this, since it's a
pretty full page currently.

Also missing is any checking that the contents do not go beyond the
bounds of the page. We could add this if needed. For now the page has
enough room for about a dozen waypoints, which is quite a bit more
than most missions need.
2020-08-31 13:01:05 -07:00
Dan Albert
66af6be063 Update pydcs. 2020-08-31 13:01:05 -07:00
C. Perreau
69e1d2779d Merge pull request #112 from DanAlbert/gitignore
Update gitignore.
2020-08-31 12:04:37 +02:00
Dan Albert
001752a81e Update gitignore. 2020-08-29 19:58:03 -07:00
C. Perreau
1000041bce Merge pull request #109 from DanAlbert/non-modal-mission-planning
Make the mission planning window non-modal.
2020-08-29 18:06:57 +02:00
Dan Albert
d50e791c30 Make the mission planning window non-modal.
Doesn't appear to be any need for this to be modal. Making it
non-modal allows interacting with the map during planning.
2020-08-28 13:42:38 -07:00
Khopa
7817d59989 Fixed campaign sometimes not starting when the user does not explicitly re-select a campaign and just kee p the default one.. 2020-08-27 23:47:00 +02:00
Khopa
139c4c1dd8 Fixed crash on mission generation when clearing slots. 2020-08-27 23:46:10 +02:00
Khopa
75bb6941d3 Added version string in the window title 2020-08-24 22:49:55 +02:00
Khopa
e92fb38271 Fixed Sweden 1990 faction not working 2020-08-24 22:32:37 +02:00
C. Perreau
e4eeef8f99 Merge pull request #102 from parithon/menukbsupport
Add keyboard support to the menu system
2020-08-24 21:42:27 +02:00
Khopa
21c355bc9f Fix small issue with ground object menu when the ground object is empty. 2020-08-24 21:34:24 +02:00
Anthony Conrad
04c878f57c Added keyboard support to the menu system 2020-08-23 22:25:16 -07:00
Anthony Conrad
ef23ce58d1 Added keyboard support for the menu system 2020-08-23 21:54:12 -07:00
C. Perreau
d213fa1b91 Merge pull request #98 from Khopa/develop
2.1.1-alpha-2
2020-08-23 18:26:14 +02:00
Khopa
4b2804427e Possible to replace SAM, possible to see building recon images on ground site. 2020-08-23 18:25:23 +02:00
Khopa
d707a59a71 Added thumbnail for strike targets / buildings. 2020-08-23 18:02:36 +02:00
Khopa
923358b364 Cheat menu : added buttons to remove money 2020-08-23 15:29:11 +02:00
C. Perreau
1d0c0ac19c Merge pull request #96 from parithon/develop
Add a Github Action and Installer
2020-08-23 14:14:30 +02:00
C. Perreau
7c97ecddac Merge branch 'develop' into develop 2020-08-23 14:08:09 +02:00
Khopa
bcae51cc92 Added possibility to repair SAM sites (WIP) 2020-08-23 13:43:33 +02:00
Anthony Conrad
18896a69cf Added PyDCS as a submodule 2020-08-22 20:00:07 -07:00
Anthony Conrad
4c310d268d Added an installer script using Inno Setup and Github Action to create releases when a tag is pushed. 2020-08-22 18:44:11 -07:00
Khopa
70babd9c32 Added Incirlik airbase in the Inherent Resolve campaign 2020-08-22 03:53:51 +02:00
Khopa
15d0edce2e Added MQ_9 Reaper as a recruitable CAS unit to usa 2005 2020-08-22 03:52:54 +02:00
Khopa
d7ab1774ac AI was not using AH_1W 2020-08-22 03:51:53 +02:00
Khopa
42d9607b0d Syria 2011 update 2020-08-22 03:51:18 +02:00
Khopa
bf82474fd7 Fixed Viper TGP bug. 2020-08-22 02:59:24 +02:00
Khopa
64211275cc Changelog update 2020-08-21 13:14:36 +02:00
Khopa
cf8060f7ff AH-64 better payloads management. 2020-08-21 13:14:03 +02:00
Khopa
cee1d01d9a Payloads fix for AH-64A. And AH-64D has payloads for all missions type. 2020-08-21 12:50:37 +02:00
Khopa
536d763a8e usa 1990 tweaks 2020-08-21 12:09:59 +02:00
Khopa
6d27b6ce41 Fixed position of Tarawa on Syrian Civil War campaign.(A new save is required though) 2020-08-21 12:03:50 +02:00
Khopa
0d05655e94 Merge branches 'develop' and 'master' of https://github.com/khopa/dcs_liberation into develop 2020-08-21 11:49:39 +02:00
C. Perreau
9cf697d72c Merge pull request #88 from Steveveepee/Steveveepee-patch-1
Update QWaitingForMissionResultWindow.py
2020-08-21 11:34:49 +02:00
C. Perreau
f0f818c524 Merge pull request #89 from Steveveepee/Steveveepee-patch-2
Update aircraft.py
2020-08-21 11:34:30 +02:00
Steve Pole
d2a3448819 Update aircraft.py
corrected spelling of aircrafts to aircraft
2020-08-21 17:07:10 +10:00
Steve Pole
1a25b15bce Update QWaitingForMissionResultWindow.py
corrected spelling of aircrafts to aircraft
2020-08-21 17:05:50 +10:00
Khopa
83808a63ed Fix inverted Syria map starting point 2020-08-21 03:06:19 +02:00
C. Perreau
515d1d0dee Merge pull request #83 from Khopa/develop
Version 2.1.0
2020-08-21 02:12:07 +02:00
Khopa
2d47df96b6 Better performances for Golan heights startup with only one frontline. 2020-08-21 01:13:20 +02:00
Khopa
ad4d71316f Israel 2000 update 2020-08-21 01:13:00 +02:00
Khopa
40932c9e84 More Performance for Golan heights startup with only one frontline. 2020-08-21 01:07:25 +02:00
Khopa
59bee5f23e Changelog update 2020-08-21 01:06:38 +02:00
Khopa
e6f00febea Credits 2020-08-21 01:06:00 +02:00
Khopa
6ea694bc7e Added more Syria campaigns 2020-08-21 00:41:34 +02:00
Khopa
8f7a8edde4 Added custom payloads for AH-1W 2020-08-21 00:39:31 +02:00
Khopa
11c7107152 Fixed assymetric default F16 payload for CAS 2020-08-21 00:37:04 +02:00
Khopa
d18a9f376f Added Syria map with GolanHeights setup.
Added a setting to forbid navy units on some control points.
2020-08-20 22:15:54 +02:00
Khopa
6ba95d8c70 Syria terrain update 2020-08-20 22:14:59 +02:00
Khopa
1c00418d64 Syria polygon, terrain and background image added. 2020-08-20 21:04:20 +02:00
Khopa
602e7fb530 Added syria map icon 2020-08-20 19:12:30 +02:00
Khopa
e6a0a1d4a4 Added payloads for drones. Possibility to setup a different JTAC unit for some factions. China using Wingloong instead of Reaper as JTAC unit. 2020-08-20 19:11:51 +02:00
Khopa
136cf143bd Added payloads for drones. Possibility to setup a different JTAC unit for some factions. China using Wingloong instead of Reaper as JTAC unit. 2020-08-20 16:08:52 +02:00
Khopa
5afa2a23f6 Version String managed in a single place. And shown in about Window 2020-08-20 16:02:16 +02:00
Khopa
513c81b508 Changelog update 2020-08-20 14:30:52 +02:00
Khopa
45af66e000 Fixed T+ not updating in the list of flight shown in the mission planner. 2020-08-20 14:22:10 +02:00
Khopa
b38f6c39e4 Changelog update with TODO 2020-08-20 14:21:06 +02:00
Khopa
036874fbf0 Preparing version 2.1.0 2020-08-20 13:12:26 +02:00
Khopa
9f3b49a7f5 Changelog update 2020-08-20 13:11:35 +02:00
Khopa
9c35156db9 Fix : First unit of base defenses group was not controllable with Combined Arms. 2020-08-20 12:54:03 +02:00
Khopa
ab2edc6e59 Added Syrian faction 1982. + Mig-25PD better support. 2020-08-20 12:53:34 +02:00
Khopa
9af278217c More start date. Some small factions changes, F-16A support. 2020-08-20 12:39:18 +02:00
Khopa
13dbc9c0fe Added error message in mission when state file can not be written. 2020-08-20 01:09:10 +02:00
Khopa
14f7fbe794 Added new exclusions zones in caucasus map 2020-08-20 00:18:07 +02:00
Khopa
a60e6aa860 Reworked campaign selection wizard. Added two small scale camapaigns on PG map 2020-08-20 00:17:10 +02:00
Khopa
220e72322c Added new factions in anticipation for new syria map. 2020-08-18 21:08:26 +02:00
Khopa
1a100dc54b Merge branches 'develop' and 'master' of https://github.com/khopa/dcs_liberation into develop 2020-08-18 17:53:27 +02:00
C. Perreau
04b95c986d Update README.md 2020-08-17 15:22:39 +02:00
C. Perreau
8f6bb90945 Update README.md 2020-08-17 15:21:57 +02:00
C. Perreau
50e78bd069 Update README.md 2020-08-17 15:20:14 +02:00
C. Perreau
ce60e1a8f8 Update README.md 2020-08-17 15:19:48 +02:00
C. Perreau
fb7ed19f7a Merge pull request #72 from root0fall/add_budget_update
add budget preview to purchase dialogue
2020-08-17 11:46:23 +02:00
root0fall
970888b5ca add budget preview to purchase dialogue 2020-08-17 12:37:02 +08:00
C. Perreau
8c934654ba Update README.md 2020-08-16 16:44:38 +02:00
C. Perreau
ad3dc70cfd Merge pull request #62 from Khopa/develop
Develop
2020-08-16 16:42:21 +02:00
Khopa
ae5949bc7f Added Ka-50 to Russia 1990 2020-08-16 16:32:59 +02:00
Khopa
c7c563e68c Requirements update to new pydcs version 2020-08-16 16:31:06 +02:00
C. Perreau
3dac0af6c4 Merge pull request #61 from Khopa/develop
2.0.11
2020-08-16 15:27:25 +02:00
Khopa
f9ce6966bb Changelog update 2020-08-16 15:26:20 +02:00
C. Perreau
f61e23153b Merge pull request #60 from Khopa/develop
Develop
2020-08-16 15:22:10 +02:00
Khopa
ff4f008a63 Merge branches 'develop' and 'master' of https://github.com/khopa/dcs_liberation into develop 2020-08-16 15:21:22 +02:00
C. Perreau
cbcf8a0a90 Merge pull request #59 from bwRavencl/fix_uk_typo
Fixed typo "United Kingdown" -> "United Kingdom"
2020-08-16 15:20:59 +02:00
Khopa
647e62059f Changelog update 2020-08-16 15:10:55 +02:00
Khopa
6e3ef24e3a Changelog update 2020-08-16 15:09:44 +02:00
Khopa
2559b27a6f Restrict afterburner for AI units. 2020-08-16 15:08:27 +02:00
Khopa
421e2508d4 JTAC invisble and immortal commands update. 2020-08-16 15:01:34 +02:00
Matteo Hausner
c9f8a93813 Fixed typo "United Kingdown" -> "United Kingdom" 2020-08-16 14:07:37 +02:00
Khopa
126849cf9a JTAC message will be displayed for 25 seconds instead of just 10. 2020-08-16 13:52:42 +02:00
Khopa
c08768f648 Cleaned up dead code 2020-08-16 13:31:12 +02:00
Khopa
2c07257bf6 Small update to nevada exclusion polygons around nellis AFB. 2020-08-16 13:30:36 +02:00
Khopa
f797bbb97f Fixed JTAC using code above than 1688 that cannot be used in game. 2020-08-16 13:11:28 +02:00
Khopa
a7f3b6e0dc F-15E added for USA 2005 and USA 1990 factions. 2020-08-16 13:09:31 +02:00
Khopa
60732c33c0 Libya mispell issue 2020-08-16 12:53:40 +02:00
Khopa
65b77e241f Changelog update 2020-08-16 02:41:56 +02:00
Khopa
a167b95cec Destroyed units will not remain on airfields. 2020-08-16 02:25:42 +02:00
Khopa
283cfd1ce9 Removed some dead code. 2020-08-16 02:19:52 +02:00
Khopa
e16db60d0f Fix error with JTAC compatibility with old saves 2020-08-16 02:18:30 +02:00
Khopa
6a3b5bbe1d Empty neutral airports from supply 2020-08-16 02:17:43 +02:00
Khopa
339c3f506c changelog update 2020-08-16 01:02:12 +02:00
Khopa
9fade70092 Fixed Tankers TACAN being the same and being different from the one in briefing. 2020-08-16 01:01:01 +02:00
Khopa
e18d84ae5e Fixed issues with libya 2011 faction. 2020-08-15 01:10:30 +02:00
Khopa
fa76e31640 JTAC smoke markers can be disabled 2020-08-14 22:59:45 +02:00
Khopa
2fd4fa25f7 Added JTAC smoke parameter. 2020-08-14 22:39:45 +02:00
Khopa
c27d8e3b16 Updated readme.md file. 2020-08-14 19:45:02 +02:00
Khopa
0841c52a75 Readme update link 2020-08-14 19:28:46 +02:00
Khopa
669bff13c7 Updated Github readme. 2020-08-14 19:24:35 +02:00
Khopa
01ea4fa7a6 Fixed big performance issues in release executable. 2020-08-13 16:53:56 +02:00
Khopa
ef024b5118 Removed unused part of release script 2020-08-13 16:10:37 +02:00
Khopa
a96a107ef9 Added Mi24, Mi28, Mig31, Su30 to Russia. 2020-08-13 14:51:30 +02:00
Khopa
8d3ab2be5d Payloads for Mi-24V && Mi-28 2020-08-13 14:50:36 +02:00
Khopa
6ed407f656 Fixed OH-58D not being used by AI 2020-08-13 14:47:59 +02:00
Khopa
6b3625f0ea Forgot to put jtac in changelog. 2020-08-13 00:36:28 +02:00
C. Perreau
db8e7f0474 Merge pull request #46 from Khopa/develop
Merge develop 2.0.10
2020-08-13 00:02:21 +02:00
Khopa
d6398630df Version number 2020-08-12 23:58:23 +02:00
Khopa
464bfccfb6 C-101 picture fix.
Removed UH60 from PMCs.
2020-08-12 23:57:25 +02:00
Khopa
aa16da837d Fixed error 2020-08-12 22:59:00 +02:00
Khopa
ce4478803a Added new factions, and OH-58D payloads. 2020-08-12 22:57:50 +02:00
Khopa
114239fe8e Default size for info panel & map changed 2020-08-12 18:27:13 +02:00
Khopa
67d96061da Fixed JTAC infos in briefing 2020-08-12 18:26:49 +02:00
Khopa
2d5ad16399 More WW2 factions 2020-08-12 18:26:26 +02:00
Khopa
4c7f79c6f8 Fix bug in mission result window. 2020-08-12 03:31:56 +02:00
Khopa
2a50768db1 JTAC support 2020-08-12 03:13:51 +02:00
Khopa
bff33e6992 Added LHA to Caucasus campaigns. 2020-08-10 18:41:41 +02:00
Khopa
448057a0b9 Disabled interceptors for upcoming release 2020-08-10 18:17:00 +02:00
Khopa
0a47669b14 Possible fix for debriefing updates. 2020-08-10 18:10:10 +02:00
Khopa
b5893ce521 Fix potential error in mission waiting for results window. 2020-08-10 17:56:30 +02:00
Khopa
23537211b0 Fallback point is further away 2020-08-10 17:55:32 +02:00
Khopa
39f25db439 Replaced Sa342L by SA342M in uk 90 factions. (L version doesn't want to attack ennemy.) 2020-08-10 17:49:22 +02:00
Khopa
a551067c72 Restrict Jettison for CAS aircrafts 2020-08-10 17:04:46 +02:00
Khopa
23f4df766c Now possible to open and save game under different names. 2020-08-09 17:31:53 +02:00
Khopa
7354a34f1a Misc UI changes to new game wizard and mission result window. 2020-08-09 16:37:10 +02:00
Khopa
2de48b3918 Fix no lha for some factions 2020-08-09 16:36:21 +02:00
Khopa
e823a7a7b0 Replaced new game wizard picture by my own screenshoots (not sure of the source for the previous ones) 2020-08-09 16:36:00 +02:00
Khopa
ffc1f9d48e New harrier payload, tarawa compatible. 2020-08-09 14:43:53 +02:00
Khopa
bb19bfb925 Improved special case for Su-33 carrier takeoff payload 2020-08-07 23:06:54 +02:00
Khopa
b3a2464249 Fixed issue with IL-78M tanker 2020-08-07 23:03:15 +02:00
Khopa
09718e73a3 Changed campaign names in new game window 2020-08-06 00:37:39 +02:00
Khopa
d5f20377ea Remove weapons fired from debriefing window (not computed anymore) 2020-08-06 00:36:49 +02:00
Khopa
f703d620c2 Changed the Nevada campaign setup 2020-08-06 00:35:55 +02:00
Khopa
5ac680b37d Slight change to base defense position calculations 2020-08-06 00:35:24 +02:00
Khopa
9bf57e99fb Removed Mig-29 from Strike mission 2020-08-06 00:34:13 +02:00
Khopa
b787f7cb11 RTB rules for CAS aircraft changed. Added PP M points for AJS 37 2020-08-06 00:33:37 +02:00
Khopa
7b35965dbf Fix : Awacs frequency in briefing 2020-08-06 00:32:47 +02:00
Khopa
1618c1e677 Awacs use 233Mhz instead of 133Mhz 2020-08-06 00:32:17 +02:00
Khopa
0a5ce108b7 Added preplanned point to briefing 2020-08-06 00:31:50 +02:00
Khopa
99c180441d Updated terrain data for all maps. 2020-08-05 22:01:32 +02:00
Khopa
79a7b0557c Improved Caucasus landmap collision data, added exclusions zones for airports. 2020-08-05 20:13:42 +02:00
Khopa
91cf192d2e Fixed AI aircraft do not start datalink. 2020-08-05 01:04:44 +02:00
Khopa
66d7435ed6 Minor balances changes 2020-08-05 00:58:14 +02:00
Khopa
19ea75b281 Fixed all aircraft spawning in the air after a few missions have been played without restarting DCS Liberation. 2020-08-05 00:58:02 +02:00
Khopa
f0350b7045 New Caucasus campaign in Russia 2020-08-04 01:17:26 +02:00
Khopa
9d4d3d0523 Avoid duplicate groups id. 2020-08-04 01:08:27 +02:00
Khopa
a76962e206 Added new campaign generation settings. Added Helicopter Carrier to China faction. 2020-08-04 00:14:42 +02:00
Khopa
4060039440 Changed tanker frequency range to avoid overlap with Persian Gulf TACAN frequencies. 2020-08-03 21:47:22 +02:00
Khopa
c3a3a428a4 Fix tanker speed, tacan and increased race track length. 2020-08-03 20:34:28 +02:00
Khopa
171e23bd09 Fixed S-300 (SA-10) sites missing a tracking radar. 2020-08-03 19:47:08 +02:00
Khopa
6f4b7e0f1a WIP 2020-08-03 19:43:12 +02:00
Khopa
ccf2cd3425 Factions small changes 2020-08-03 19:42:09 +02:00
Khopa
6ee444efd7 Aircraft should RTB when Bingo.
CAP should RTB when winchester.
2020-08-02 13:59:55 +02:00
Khopa
3e5be909a2 Lua script injected in mission will not listen to weapon fired event anymore. 2020-08-02 02:46:00 +02:00
Khopa
dac78f8f09 changelog update 2020-08-02 02:45:19 +02:00
Khopa
6b91e1b03c Fixed FW-190A8 spawning with bomb rack even when configured for CAP 2020-08-02 02:31:40 +02:00
Khopa
772295fc04 Avoid having Su-33 crashing when taking off from their carrier with a too big payload by removing some fuel. 2020-08-01 18:07:49 +02:00
Khopa
a4e93276b8 Possible to mix factions side. Player will always be blue. 2020-08-01 14:21:34 +02:00
Khopa
456a82acaa Multiple factions fixes 2020-08-01 14:20:37 +02:00
Khopa
f2fb2cb363 Fixed label in strike mission generator 2020-08-01 14:19:46 +02:00
C. Perreau
2a7e5eecd7 Merge pull request #30 from DeusEx010101/add-usa-aggressors-faction
Added USA aggressors aircraft.
2020-07-31 20:35:18 +02:00
Khopa
79502f56a0 Added Australia / Canada 2020-07-31 20:34:27 +02:00
Khopa
12dacfe2d2 Fixed A-20G payloads. 2020-07-30 19:31:38 +02:00
Donnie
d46337d694 Added USA aggressors. TODO. Decide on final aggressors unit list. 2020-07-26 11:27:18 -04:00
Khopa
f897cf745f Multiple WIP changes on UI / Submit manually debriefing. 2020-07-26 12:59:16 +02:00
Khopa
cfa4f7da2e Take off alt waypoint was editable in flight waypoint table. 2020-07-26 00:57:45 +02:00
Khopa
b21272cfab Made waypoint altitude column not editable 2020-07-25 23:07:23 +02:00
Khopa
8ce0520101 QSettings Windows can edit new performance setting to disable destroyed units 2020-07-25 23:05:13 +02:00
Khopa
4c17e1fd33 Possible to disable destroyed units. 2020-07-25 23:04:00 +02:00
Khopa
d5fb1f62f5 Previously destroyed units are added to the mission. 2020-07-25 18:46:10 +02:00
Khopa
b34ede3795 LHA carrier group ships are more spread out. 2020-07-25 16:21:03 +02:00
Khopa
4989d84693 Carrier group ships are more spread out. 2020-07-25 16:18:13 +02:00
Khopa
2f210ab59f Fixed error in carrier group generator if faction has no destroyers 2020-07-25 16:17:40 +02:00
Khopa
3ffea901f1 Added japan_2005 faction. 2020-07-25 16:15:23 +02:00
Khopa
5e2e6520ce Fixed issues with coldwar factions. 2020-07-25 16:14:53 +02:00
Khopa
a8679c8eef Fixed strike mission generator window was named "sead mission generator" 2020-07-25 15:14:48 +02:00
Khopa
469e842e96 Fixed wrong radio frequency for german WW2 warbirds. 2020-07-24 01:37:33 +02:00
Khopa
cd945c625f Base defense units can be controlled with CA. 2020-07-24 00:23:11 +02:00
Khopa
1346192b75 Fix : Carrier on Persian Gulf full map were sharing the same id. 2020-07-24 00:11:55 +02:00
Khopa
45bebdd94e Updated pydcs version in requirements.txt 2020-07-24 00:02:13 +02:00
Khopa
d0bbb025d3 Added carrier frequency to briefing. 2020-07-23 21:30:50 +02:00
Khopa
8d0c53ef69 Remove Oliver Hazard Perry from Cold War factions. 2020-07-23 21:12:46 +02:00
Khopa
a59c2dfb10 Changed F-15C default payload. Replaced AIM-9P by AIM-9M. Replaced AIM-120B by AIM-120C. 2020-07-22 23:59:25 +02:00
Khopa
9c7689f9b5 Increased offset for attack points. 2020-07-20 21:32:49 +02:00
Khopa
c580979fee Open base menu by single mouse click. 2020-07-20 20:36:57 +02:00
Khopa
e8e7bc95ea Culling is a bit less aggresive. 2020-07-20 00:29:37 +02:00
Khopa
839e3e0833 Removed JF-17 from usa 2005 faction. 2020-07-19 17:11:34 +02:00
Khopa
4f451fab2f Show required mods in new gam wizard 2020-07-19 17:11:05 +02:00
Khopa
7d0413f41d Remove Type 93 from radar db and cc fleet gen 2020-07-17 01:45:46 +02:00
Khopa
bbac78195d Credits 2020-07-17 01:08:47 +02:00
Khopa
20fef86b84 Fixed aircraft carrier cold start 2020-07-17 01:02:21 +02:00
Khopa
9581a8f1f4 Support for frenchpack mod & Rafale Mod 2020-07-09 00:39:33 +02:00
Khopa
58b4c36b6c MB-339PAN payload fix. 2020-07-05 18:10:54 +02:00
Khopa
15aaa5d9a1 MB-339PAN support. 2020-07-05 17:15:43 +02:00
Khopa
b38332d061 Requirements update, new pydcs version 2020-07-04 16:43:01 +02:00
Khopa
4cfbbb6756 A-4E-C is now considered carrier capable; 2020-07-04 15:59:50 +02:00
Khopa
dec7db9e69 Cleaning up dead code 2020-07-04 15:59:23 +02:00
Khopa
274be3bcfc Fix : Carrier sail into the wind. Not in the same direction. 2020-07-04 15:59:00 +02:00
Khopa
d8a668ce60 Fix : Channel map, removed town names 2020-07-04 02:04:53 +02:00
Khopa
a845ed1998 Fix : Code that should be commented for custom ref point. 2020-07-04 01:37:04 +02:00
Khopa
53bd147de2 Community A-4E-C support 2020-07-04 01:35:48 +02:00
Khopa
4248b518a2 Merge branch 'develop' of https://github.com/khopa/dcs_liberation 2020-07-03 02:55:50 +02:00
Khopa
8f65a88d8b New satelitte background for all maps. 2020-07-03 02:54:58 +02:00
C. Perreau
4d4a640f34 Merge pull request #11 from DeusEx010101/UI-fixes-new-themes
UI Update!
2020-07-01 15:39:31 +02:00
Donnie
af9ead5937 lots of UI enhancements for better feedback and state 2020-06-30 22:19:45 -04:00
Donnie
ae3518f450 Map cp lines and flight paths are now the color of your coalition (red or blue) 2020-06-30 16:44:04 -04:00
Donnie
04e77a97f2 Fix: map icons now match player side color (red or blue) 2020-06-29 17:32:29 -04:00
Donnie
0ff3ce98e0 new map graphic test 2020-06-29 17:31:47 -04:00
Donnie
df2659787c adjust map colors 2020-06-29 17:31:35 -04:00
Donnie
d994673e91 blurred map background to soften edges 2020-06-29 10:00:12 -04:00
Donnie
dce781ef0e New splash screen 2020-06-29 09:59:20 -04:00
Donnie
b0e2c73024 Made SAM range circles easier to see 2020-06-28 22:56:48 -04:00
Donnie
bda28f81cc Added disable button style 2020-06-28 22:45:30 -04:00
Donnie
d8dc3d48b1 made dcs theme default 2020-06-28 01:12:47 -04:00
Donnie
5fdfa6339d front line thinner 2020-06-28 01:10:38 -04:00
Donnie
9a799190ef Map ui design updated 2020-06-28 01:06:02 -04:00
Donnie
8d32ba5b8e Map red icons darker 2020-06-28 01:05:37 -04:00
Donnie
85a7f89ba9 Css updates to dcs theme 2020-06-27 23:55:56 -04:00
Donnie
17f5378326 Modified recruit button layout 2020-06-27 23:54:27 -04:00
Donnie
9d67741310 made font references constants 2020-06-27 23:53:56 -04:00
Donnie
ac6a106c6a Merge branch 'master' into DCS-UI-Theme 2020-06-26 16:18:01 -04:00
Donnie
d775b8baa0 Some clean up and refactoring 2020-06-26 11:39:28 -04:00
Donnie
2dc1b3ec43 Icons change based on theme. wip 2020-06-26 00:57:00 -04:00
Donnie
d3d5160861 UI Theme switcher working 2020-06-26 00:39:04 -04:00
Donnie
cdf8c3b6e5 menu bar and info panel spacing 2020-06-24 22:09:31 -04:00
Donnie
29b1bbab9d combobox styles. what a pain. WIP 2020-06-24 18:13:20 -04:00
Khopa
c5d055c19b Prepared version RC9 2020-06-24 23:54:23 +02:00
Khopa
8f7b51a3df Fixed an issue with MolniyaGroup generator causing an error on campaign start. 2020-06-24 23:51:23 +02:00
Khopa
44e8cc810f Fixed an issue with MolniyaGroup generator causing an error on campaign start. 2020-06-24 23:49:27 +02:00
Donnie
b994878465 checkboxes 2020-06-24 17:33:36 -04:00
Khopa
8d5d703cbe Merge branches 'develop' and 'master' of https://github.com/khopa/dcs_liberation into develop 2020-06-24 23:22:34 +02:00
Khopa
1c1936d8f8 Fix : Carrier TACAN was wrongfully set up as an A/A TACAN 2020-06-24 23:22:07 +02:00
Donnie
7890fd5cc7 Base theme applied. WIP. 2020-06-24 16:40:19 -04:00
C. Perreau
4bdf11eb79 Merge pull request #9 from DeusEx010101/New-Icons
changed AA icon
2020-06-24 22:29:56 +02:00
Donnie
81f4c7303f changed AA icon 2020-06-24 15:39:08 -04:00
C. Perreau
0641907ea0 Merge pull request #8 from DeusEx010101/New-Icons
Icons updated to be more consistent
2020-06-24 21:31:32 +02:00
Donnie
ff0f32fcf5 Icons updated to be more consistent 2020-06-24 14:52:06 -04:00
Khopa
768049ca36 Changelog update 2020-06-23 12:57:46 +02:00
Khopa
88c8fe7cca Fixed zoom on map. 2020-06-23 12:56:01 +02:00
Khopa
5e7facfeb6 Fixed issue with WW2 LST group (boats superposed) 2020-06-23 12:06:19 +02:00
Khopa
6e43467ef6 Fix : Reduced maximum number of uboat 2020-06-23 12:05:15 +02:00
Khopa
cd4ef8ae32 Fix frequency for P-47 2020-06-23 11:46:57 +02:00
Khopa
93a4463f22 Fixed logging issues and SEAD flights departing without waiting. 2020-06-22 23:20:14 +02:00
Khopa
56cf6bdaa4 Mission planning polishing. 2020-06-22 21:31:25 +02:00
Khopa
fe02df27a2 Do not allow selection of non existing carrier in mission planner 2020-06-22 12:44:49 +02:00
Khopa
a60ab68287 Aircraft Carrier will try to sail in the wind when possible. 2020-06-21 18:15:58 +02:00
Khopa
83e46ddc97 SEAD AI will only target the designated target group. 2020-06-21 17:50:06 +02:00
Khopa
9c89ad7c72 Reduced maximum wind in generated missions. 2020-06-21 15:05:57 +02:00
Khopa
0518abdedc Balance for WW2 aircraft. WW2 B_17 bombing mode is now much more effective. 2020-06-20 15:28:42 +02:00
Khopa
93bcd07c7f Added aircraft icons + fixed ww2 empty aircraft loadout not being loaded. 2020-06-20 13:44:11 +02:00
Khopa
5f1f4f8d81 Culling optimizations + fixed some aircraft icons 2020-06-19 14:31:25 +02:00
Khopa
e78120d9c6 Fix : Egress waypoint for SEAD mission is wrongfully named INGRESS 2020-06-17 23:15:44 +02:00
Khopa
cc5986d435 Updated readme 2020-06-17 13:48:33 +02:00
Khopa
5192306b06 Generate fleet and missiles sites 2020-06-17 13:04:55 +02:00
Khopa
826935eb7d Ship icons for ship groups 2020-06-14 17:31:45 +02:00
Khopa
cd41bcf45c Generate WW2 Ship groups, added B17 to allies. Implemented modifiable doctrine to setup flight generator. 2020-06-13 18:53:43 +02:00
Khopa
601375d06f Channel map support. 2020-06-12 19:10:58 +02:00
Khopa
c708abafc8 Started refactoring on ground objects. WW2 factions will have WW2 style buildings. Added ground objects templates for : "village", "ww2bunker", "allycamp" 2020-06-11 21:50:09 +02:00
Khopa
90d588353c Icon and background image for channel map. Updated Normandy map background. 2020-06-11 21:48:16 +02:00
Khopa
2d4287df2a Polished mission generator, fixed sam bug on map view. 2020-06-10 22:34:31 +02:00
Khopa
fde3a988b7 Added missions generators in flight planner. + refactoring 2020-06-09 02:13:46 +02:00
Khopa
397164e667 Added multiple settings (external views, map views) and added marker generators. 2020-06-07 19:55:38 +02:00
Khopa
a7824039e3 Added revenue for derrick building 2020-06-07 15:54:15 +02:00
Khopa
bb9247e821 Fix info panel messages. 2020-06-06 16:57:20 +02:00
Khopa
4bc04ec031 Auto redeploy frontline units. 2020-06-06 16:50:19 +02:00
Khopa
8fd91e6c5c Added possible ground target : Oil derrick 2020-06-06 15:45:15 +02:00
Khopa
ee137d086a Change power station template. (Some buildings could superpose in older version) 2020-06-06 15:10:07 +02:00
Khopa
fcd81850cb Debriefing and info on AA site and buildings destroyed. KC130 replace S3B 2020-06-06 04:10:22 +02:00
Khopa
aa4b07d024 Add more waypoints to generated flights 2020-06-05 21:24:23 +02:00
Khopa
16a096d288 Possible to setup whether AI should starts from parking or not. 2020-06-05 14:23:38 +02:00
Khopa
b219b2a71b Possible to setup custom saved game and installation directory. 2020-06-05 14:21:42 +02:00
Khopa
ce70242c35 Added 947 to allies and default payload for P47 2020-06-04 13:30:38 +02:00
Khopa
dec01e2611 Added P-47 to db 2020-06-04 13:25:14 +02:00
Khopa
4373d89661 WIP on CAP, and AI units can starts from ground uncontrolled instead of using late activation. 2020-06-03 00:45:44 +02:00
Khopa
c73290eebb Nav Target point for JF-17 and F-14.
Fix activation trigger using wrong coalition when playing REDFOR side.
2020-06-02 13:00:10 +02:00
Khopa
a022f9c2e1 Changelog update 2020-06-01 15:27:45 +02:00
Khopa
5adb25a695 Using late activation trigger. Planned flight will not spawn if their home base has been captured or is contested. 2020-06-01 15:11:38 +02:00
Khopa
08c2972cf9 Added map theater : North Caucasus 2020-06-01 14:20:43 +02:00
Khopa
8132c7e676 Fix : CAP aircraft will jettison fuel tanks. 2020-06-01 13:03:35 +02:00
Khopa
8c68c9f703 Fix for payload for release versions. 2020-06-01 12:51:59 +02:00
Khopa
000b6142fd Added Ka-50 to bluefor modern. 2020-06-01 12:42:02 +02:00
Khopa
c203ded1cd Improved Mission Planning flight selection behaviour. 2020-06-01 12:36:10 +02:00
Khopa
64c5c39b2a BARCAP renamed CAP for regular airbases. 2020-06-01 11:14:30 +02:00
Khopa
a8d2a1e371 Removed Ju-88 from CAS (only has torpedo in DCS) 2020-06-01 01:49:20 +02:00
Khopa
9e5846b24a Improved WW2 support. 2020-06-01 01:27:16 +02:00
Khopa
836ff9122c Fix base defense units not being generated when other ground objects were too close. 2020-06-01 00:35:26 +02:00
Khopa
75d836358b Revert an error on sam site generation. (Disabled the wrong code.) 2020-06-01 00:05:32 +02:00
Khopa
bb11e7f90c Removed FARP AA that does not respect the factions config. 2020-05-31 23:58:43 +02:00
Khopa
cf6a71ab86 Fix issue when "no night mission" is enabled. 2020-05-31 23:50:13 +02:00
Khopa
7f7288937d Handle error when there is no AWACS for a faction. 2020-05-31 23:26:03 +02:00
Khopa
a38f9c2183 Added performance settings. 2020-05-31 23:07:54 +02:00
Khopa
7ee880cadc Generate ICLS command for carriers 2020-05-31 20:00:07 +02:00
Khopa
9ae34d474b Added Bluefor modern faction 2020-05-31 19:44:53 +02:00
Khopa
2817e2f2c8 Update for new pyDCS version. Supercarrier support added. 2020-05-31 19:19:03 +02:00
Khopa
02886a09d3 AI Strike flight with user generated STRIKE, will now perform their bombing task correctly. 2020-05-31 18:02:00 +02:00
Khopa
0b9d827ad6 AI Strike flight will bomb all their targets correctly 2020-05-31 15:43:56 +02:00
Khopa
ab3ea84d70 AI flight planner now auto generate STRIKE flights.
Fix CAS point position in predefined wpt selector.
When an airbase is captured, base defenses are re-generated for the new base owner.
2020-05-30 02:32:45 +02:00
Khopa
03a1c44659 README update for QT UI branch 2020-05-30 00:03:42 +02:00
Khopa
53364444fd Possible fix for crash when loading old save files. 2020-05-30 00:03:11 +02:00
Khopa
94040e8551 Fix new campaign wizard crashing and made the campaign generator slightly faster. 2020-05-29 23:47:13 +02:00
Khopa
34d46ee28e Fix objective name error when no more objective name are availables. 2020-05-29 23:43:26 +02:00
Khopa
59986d74f4 Generate random TACAN frequency for ships at start.
TACAN infos are displayed in briefing.
2020-05-29 03:28:09 +02:00
Khopa
8afdf5ef65 Polishing, Bug Fixes, Slight improvements to flight generator for BARCAP. 2020-05-29 03:05:27 +02:00
Khopa
fc64e57495 Ground objects have 'Objective' name for easier search. Predefined Waypoint generator entirely reworked.
Added finances menu, and info panel.
2020-05-29 00:40:06 +02:00
Khopa
6dec5ea8f8 Reworked payloads, factions, map display, carrier. Re-added Tarawa support, many minor bug fixes. UI changes. 2020-05-27 21:45:58 +02:00
Khopa
3f2aafcd28 Now possible to create/delete flights. 2020-05-25 14:54:02 +02:00
Khopa
7a3ee6b1a9 Frontline progression is now based on combat results. 2020-05-25 04:09:51 +02:00
Khopa
f7d9c62afb Updated mission launch instructions. 2020-05-25 01:22:20 +02:00
Khopa
1a26a7f346 Fix an issue on Nevada map, cp not well connected. 2020-05-25 00:52:46 +02:00
Khopa
f6ff4405f8 Merge branch 'qt_ui' of https://github.com/shdwp/dcs_liberation into qt_ui 2020-05-24 23:53:51 +02:00
Khopa
14ceb0abc6 Fix : Interceptor groups can have custom waypoints. 2020-05-24 23:50:30 +02:00
Khopa
c9a3af6065 Flight waypoint editor to select targets 2020-05-24 23:44:06 +02:00
Khopa
4e4a7fe84e Budget amount per turn, now depends on factory owned and such 2020-05-24 21:30:05 +02:00
Khopa
989d25e2e2 Updated UI ground objects icons 2020-05-24 20:53:41 +02:00
Khopa
f57e453d8d Ground war rework 2020-05-24 20:32:45 +02:00
Wrycu
fcfa785ae9 fix(save): correct openbeta detection 2020-01-18 11:29:16 -08:00
Wrycu
3de688da29 fix(payloads): fix for payloads when compiled into an exe 2020-01-18 11:28:26 -08:00
Khopa
5884d9d120 Made it possible to toggle 'flight paths' visibility. 2019-12-14 20:32:39 +01:00
Khopa
074ea5c719 JF-17 Support (Require pydcs own data export) 2019-12-09 02:09:50 +01:00
Khopa
e794446a54 Fix briefing text being wrong 2019-12-04 19:42:46 +01:00
Khopa
1320a67e1f Fix briefing generation, missing line break 2019-12-04 19:39:04 +01:00
Khopa
7c45177650 Fix issue with bullseye missing causing a crash when launching the mission in MP on a server. 2019-12-04 19:18:20 +01:00
Khopa
6b2d6ab57f Updated pydcs version in requirements.txt 2019-11-21 22:22:39 +01:00
Khopa
04add8ebb5 Fix weird import issue in operation.py. 2019-11-21 22:08:05 +01:00
Khopa
ad8138fb04 Fix an issue with player UH-1H radio 2019-11-21 19:58:28 +01:00
Khopa
6598e034c1 Briefing generation changed to match new flight. Include info for all flight containing client slots; 2019-11-21 19:56:26 +01:00
Khopa
bba51c6a23 Changed position of the carrier on Caucasus small map 2019-11-21 19:55:29 +01:00
Khopa
7d524300e5 Start window maximized 2019-11-21 19:55:03 +01:00
Khopa
a25a0031ff Improved CAS flight waypoints generated by ai flight planner. 2019-11-11 02:41:17 +01:00
Khopa
98b899c9c7 Fix for SEAD flights not being correctly configured. 2019-11-10 23:41:47 +01:00
Khopa
4424425dd4 Changed default map when creating new game. (Added 'recommended' label to the maps i really tested the most) 2019-11-03 17:32:34 +01:00
Khopa
85de3a09ea Remove triggers from generated mission for now. Do not generate the 'quick' mission
Fix range for ai flight planner so they are more likely to plan CAS flights
(It's up to the player to setup flight on the runway or in flight)
2019-11-03 17:27:30 +01:00
Khopa
e82db1fecd Only allow recruiting carrier units in aircraft carrier groups. Generate one group on carrier when possible. 2019-10-26 23:54:33 +02:00
Khopa
76638b549f Generate ship groups for carriers. Carriers can be destroyed. Faction need to have carrier available. 2019-10-26 22:59:59 +02:00
Khopa
2936df6a02 Fix error (unable to start DCS liberation if no save file exists) 2019-10-26 20:58:28 +02:00
Khopa
27e3cf8ac5 Added possibility to setup parking slot type for players' flight. 2019-10-23 17:01:41 +02:00
Khopa
adf1f8db8c Print weapons with their pretty name in loadout editor 2019-10-20 01:54:33 +02:00
Khopa
9f319ab99a Added loadout editor to mission preparation screen. 2019-10-19 16:14:40 +02:00
Khopa
48a40f2511 Stats view now has a tab for vehicles forces. Display waypoint coordinates in waypoint tab. 2019-10-19 13:12:21 +02:00
Khopa
7fbc75b375 New mission briefing menu, work in progress. 2019-10-19 00:07:37 +02:00
Khopa
65a54acd4f Generate ground forces on all active frontlines. 2019-10-17 01:45:46 +02:00
Khopa
de96552f78 Actually generate the planned flights from the flight planner. 2019-10-16 23:20:11 +02:00
Khopa
fee959940a Base menu layout improved. 2019-10-16 22:22:16 +02:00
Khopa
48748bbc39 Improved base menu to show planned AI flights and base defence. (WIP) 2019-10-16 00:41:02 +02:00
Khopa
827138fc6a AI Flight planner generate CAS & SEAD 2019-10-15 23:52:43 +02:00
Khopa
b7ee98dcd6 Flight Planner first version (just print planned flight in log but does not generate them yet) 2019-10-15 23:06:21 +02:00
Khopa
fa99df3ce7 Normandy map image added to repo (was missing in last commit). 2019-10-15 23:05:43 +02:00
Khopa
d2ea6ed2fd Don't show airbase armor & air defense. 2019-10-15 19:19:44 +02:00
Khopa
d8d17e5c18 F_14B client slots are now by default using pre-alignement. 2019-10-14 18:53:43 +02:00
Khopa
0519438292 Improved german WW2 flak group generator. 2019-10-14 01:49:30 +02:00
Khopa
09b8ff6b93 Generate AA & armor to defend airbase. Destruction status correctly tracked. 2019-10-14 01:25:48 +02:00
Khopa
bd66dcb39e Added support for Normandy WW2 map 2019-10-13 23:00:26 +02:00
Khopa
93504eaf7a Added & generated Normandy theater geometry data 2019-10-13 21:24:13 +02:00
Khopa
9e116f481e Fixed issue with P51D liveries that caused errors. 2019-10-13 17:57:38 +02:00
Khopa
bd95258176 Fixed issue with lost helicopters causing errors in debriefing.
Mission generated now configure bluefor and redfor coalition properly, so it is possible to use country that are not in the default coalitions.
Added insurgent faction.
2019-10-13 17:17:33 +02:00
Khopa
a7e202bbc8 Made it possible to capture airbase ingame; 2019-10-13 05:16:34 +02:00
Khopa
3f5f4f4bb1 New automatic de-briefing system implemented 2019-10-13 03:40:22 +02:00
Khopa
4365db0fae Fix : Create default blank liveries for the F_16C_50 to avoid errors at mission generation; 2019-10-12 11:17:00 +02:00
Khopa
82bb608fd3 Mission units destroyed are stored in a json file being written mission runtime. (First step that will remove the need to save the debriefing manually after mission)
Using Mist framework to do this in the mission script env.
2019-10-12 03:21:33 +02:00
Khopa
ba0b3adf71 Added some factions, refactor bluefor/redfor distinction code. 2019-10-12 00:02:07 +02:00
Khopa
707e1f8b67 Refactored factions in separates files to declutter db.py. 2019-10-11 23:08:00 +02:00
Khopa
7f97e894a3 Fix issue with Zu23 Ural aa site generator 2019-10-11 02:07:08 +02:00
Khopa
19118f7b84 Update factions. 2019-10-11 02:05:18 +02:00
Khopa
63c46d1b21 Removed placeholder text. Polished UI when no saved game is found. 2019-10-11 01:27:29 +02:00
Khopa
4c3ff906ff Improved layout for airbase window layout.
Fixed missing icons for some windows.
2019-10-11 01:14:43 +02:00
Khopa
6b77e1cce5 Added possibility to not display SAM ranges in display options. 2019-10-11 00:27:10 +02:00
Khopa
fdd8f102e6 Generate battle environment : SEAD + CAS flight.
Added more factions.
2019-10-11 00:18:25 +02:00
Khopa
2d0c195e46 Added SAM HQ-7, tweaks in db 2019-10-08 22:35:08 +02:00
Khopa
f5a5fb765b Show SAM range on map. 2019-10-08 21:42:48 +02:00
Khopa
f698cb66b8 Ally SAM are shown with blue icon on map. 2019-10-08 21:07:06 +02:00
Khopa
fc51b16e4a Added playable F16C_Bl50 support (For now, you need to update pydcs to the version on my own fork at https://github.com/Khopa/dcs for it to work)
+ Payload overrides for F/A-18C and M-2000-5
2019-10-08 20:45:45 +02:00
Khopa
0365d7a900 Fix issue with payloads loading. 2019-10-08 00:44:57 +02:00
Khopa
7a14376765 Generate CAP on whole map, for 2 hours. Added China. 2019-10-07 01:57:36 +02:00
Khopa
2167953b87 Ground objects are always generated and destroyable, even when it's not the current mission objective.
Fix : SAM site destruction status is saved correctly.
Added most SAM site to generator.
2019-10-06 16:14:13 +02:00
Khopa
17352bfcf7 Fix : Not possible anymore to start a mission by clicking the PendingDelivery event icon. 2019-10-06 01:02:49 +02:00
Khopa
d4577aa7a6 Briefing Window : Only show "clients slot" spinbox if the aircraft is flyable. 2019-10-06 00:54:02 +02:00
Khopa
e955c10170 Added smaller Caucasus map for dev test and smaller campaign 2019-10-05 13:13:43 +02:00
Khopa
9d4b6183b0 Added Iranian faction, SAM SA-2 & SAM HAWK site generation added 2019-10-05 04:36:40 +02:00
Khopa
3c9bffb557 Persian Gulf : Added Liwa, Ras Al Khaimah & Al Ain airbases 2019-10-05 03:47:39 +02:00
Khopa
d4d7b546e1 Fixed debriefing success validation for others events. 2019-10-05 02:03:51 +02:00
Khopa
9f0c17115e Fixed debriefing for Naval Intercept 2019-10-05 02:01:12 +02:00
Khopa
f6fdd3d12a Fixed issue with FrontlineAttack debriefing validation 2019-10-05 01:52:18 +02:00
Khopa
2401da2b24 Persian Gulf Theather : Disconnect Lar from Qeshm 2019-10-04 23:34:01 +02:00
Khopa
50ef8fef24 Fixed Persian Gulf Theater control points in Iran being blue when midgame is selected. 2019-10-04 23:12:27 +02:00
Khopa
079248bfeb Fixed a few issue with the briefing view. 2019-10-04 22:18:20 +02:00
Khopa
650ee9666d Fix turn counter, so that turn 0 is turn 1 2019-07-19 19:52:26 +02:00
Khopa
18bcc1bce7 Implemented stats view. 2019-07-13 14:08:44 +02:00
Khopa
73b4ec47b9 Apply CSS to whole app, but disabled for now 2019-07-13 00:53:11 +02:00
Khopa
70331d913d UI : Daytime effect on map background 2019-07-13 00:35:35 +02:00
Khopa
9c72c9a063 Put correct cursor style when mouse hover a map item. 2019-07-13 00:05:59 +02:00
Khopa
af8ae09434 Draw control point as Arc with life gauge representing power 2019-07-12 23:37:44 +02:00
Khopa
e8a8364ac2 Added back the settings menu in the new UI 2019-07-12 23:13:03 +02:00
Khopa
0e7d49488c Put CSS in resources folder, so that it is properly embedded in the release. Updated requirement and release build for new UI. 2019-07-12 19:35:30 +02:00
Khopa
405c6659b9 Cleaned up some 'print'. 2019-07-12 19:14:44 +02:00
Khopa
7ca435337f Merge 2019-07-12 00:35:54 +02:00
Khopa
024b665dd9 Commit before merge 2019-07-12 00:32:47 +02:00
Khopa
a397296624 Fix : Only possible to open event menu if event is actually displayed. 2019-07-11 21:38:59 +02:00
Khopa
adb9e38ff4 Waiting and Debriefing Window, QT UI is now functionnal. 2019-07-11 21:31:16 +02:00
Khopa
c4dc432be1 Fix issue when the faction lack units in a category of task 2019-07-11 12:16:47 +02:00
Wrycu
49795993f1 refactor(release): clean up release build process
* handle case where a dist has never been created
* ignore requirements.txt
* include map resources instead of pulling from submodule
2019-07-10 23:17:52 -07:00
Wrycu
ea6b2ab2dc chore(ignore): removed unused/personal files 2019-07-10 23:14:46 -07:00
Wrycu
44d04d3ba6 chore(requirements): add release requirement 2019-07-10 23:12:49 -07:00
Wrycu
577c171d48 chore(ignore): removed unused/personal files 2019-07-10 23:11:45 -07:00
Vasyl Horbachenko
c5edf7a581 Update README.md 2019-07-09 18:04:50 +03:00
Vasyl Horbachenko
ac67ec86b1 Delete a.py 2019-07-09 18:04:19 +03:00
Wrycu
291f538645 chore(requirements): add requirements file 2019-07-07 18:03:51 -07:00
Khopa
817a3a5e15 Briefing has start function and departure airbase selection 2019-07-07 13:57:04 +02:00
Khopa
0b87e192ce Briefing windows layout done 2019-07-07 13:36:40 +02:00
Khopa
fc83ca0de6 Added credits to pydcs in about window 2019-07-07 12:16:04 +02:00
Khopa
c621e822dc Fix artifact on map when drawing events & improved base menu 2019-07-07 12:11:12 +02:00
Khopa
1776452964 Show events on map 2019-07-06 13:54:45 +02:00
Khopa
4e5e0a4a7a ix : Possible to buy units in player base, not ennemy bases 2019-07-06 13:22:09 +02:00
Khopa
bb32f47b8c Implemented basic base menu to buy units 2019-07-06 13:21:03 +02:00
Khopa
6b5f77c415 Added classes for sub windows. (WIP) 2019-07-05 22:01:16 +02:00
Khopa
9a73c78705 Using a singleton QObject to propagate game model update across whole app 2019-07-05 21:43:14 +02:00
Khopa
8246c8e94b Top bar layout, added some icons and possible to pass turn 2019-07-05 21:02:18 +02:00
Khopa
89e8ef65ea Added about dialog & credits + icon in pass turn button 2019-07-05 20:44:01 +02:00
Khopa
2428d39308 Implemented top bar with turn counter and budget info. Added CSS stylesheet. 2019-07-05 20:05:27 +02:00
Khopa
dc91f5004e New Game Wizard with Qt UI 2019-07-05 01:58:23 +02:00
Khopa
b66bf4cc36 Qt Map drawn with line and frontline 2019-07-04 19:04:31 +02:00
Khopa
67910bce61 Map implementation 2019-07-04 09:33:19 +02:00
Khopa
2adacdba02 icon as png for qt 2019-07-03 23:35:22 +02:00
Khopa
4652e7d188 Basics for QT UI 2019-07-03 23:33:47 +02:00
Khopa
a30d4a4514 Fix in 'unit_task' method 2019-07-03 21:06:03 +02:00
Khopa
a17a9399ef Merge remote-tracking branch 'wrycu/develop' into develop_khopa
# Conflicts:
#	gen/aaa.py
2019-07-03 19:45:03 +02:00
Khopa
ba6d10cd9e Small fix and database changes. 2019-07-03 18:50:16 +02:00
Wrycu
b1576e8f15 fix(SAMs): correctly track when a SAM site is destroyed 2019-07-01 21:49:25 -07:00
Wrycu
55de28105e fix(farp): remove unsupported units from spawning in farps 2019-07-01 21:45:58 -07:00
Wrycu
40e3d59432 fix(cap): cap now properly spawns at a randomized interval after take-off 2019-06-30 12:59:01 -07:00
Khopa
665f022111 Fix Ship Gen 2019-06-30 19:15:46 +02:00
Khopa
309c10c4cb Selectable factions wip 2019-06-30 14:05:26 +02:00
Khopa
658120b8d9 Tweaked ground object generator 2019-06-30 05:48:19 +02:00
Khopa
fbd01fbfdb WIP for possibility to select player and ennemy faction 2019-06-30 05:31:22 +02:00
Khopa
09135adadc Allow selection of a time period, for campaign start 2019-06-30 02:58:04 +02:00
Khopa
0c7a36cef6 Start time generation now more correct. 2019-06-30 01:56:40 +02:00
Khopa
abf6fe69bd Generate start time of the mission according to settings. 2019-06-30 01:38:12 +02:00
Khopa
b3318583ed Added a filter so that map displayed change color according to the time of the day. 2019-06-30 01:32:47 +02:00
Khopa
288fe20def Doubled size of map image for Caucasus 2019-06-30 01:12:15 +02:00
Khopa
96ee14bf08 Icon order in topbar 2019-06-30 01:11:46 +02:00
Khopa
00b9ba0f32 Display current day time icon in top bar and improved overall look and feel. 2019-06-30 01:02:50 +02:00
Khopa
2a63f7b187 Drew and added misc icons 2019-06-30 00:42:07 +02:00
Khopa
c655d97109 Drew a few placeholder icons for daytime 2019-06-30 00:25:18 +02:00
Khopa
fd26700867 Show current turn, date and time of the day in overview. 2019-06-30 00:14:33 +02:00
Khopa
df5d9782e7 Added turn counter, top bar refresh correctly. 2019-06-29 21:06:05 +02:00
Khopa
61af07bd79 Fixed pydoc 2019-06-27 22:47:01 +02:00
Khopa
0e008ac87b Added method to get DCS saved games directory 2019-06-27 22:41:58 +02:00
Khopa
28eab870d4 Added methods to retrieve the DCS installation directory (with Steam/Standalone version abstraction) 2019-06-27 22:38:05 +02:00
Khopa
86aedaf631 Added a module to check DCS environment, to be completed. 2019-06-27 21:58:12 +02:00
Khopa
e242851260 Optimized imports in debriefing.py 2019-06-27 21:43:09 +02:00
Khopa
c884fb7262 Merge remote-tracking branch 'wrycu/develop' into develop_khopa
# Conflicts:
#	ui/overviewcanvas.py
2019-06-27 21:24:01 +02:00
Khopa
b6104fbdaf Removed idea files from version control 2019-06-27 21:03:14 +02:00
Khopa
9305f1d483 Added xml idea files in gitignore 2019-06-27 21:00:02 +02:00
Khopa
048cd22d85 Removed idea files from version control 2019-06-27 20:56:23 +02:00
Khopa
e1ea9664dc Added idea files to gitignore 2019-06-27 20:54:36 +02:00
Wrycu
72d3863f8a feat(intercept): add random delay to enemy intercept 2019-06-25 22:21:12 -07:00
Wrycu
49de53f1c9 feat(strike): add chance of AAA/tanks to strike targets 2019-06-24 20:44:28 -07:00
Wrycu
b17e34351c fix(strike): spawn air defense SAM sites on strike 2019-06-24 19:01:56 -07:00
Wrycu
84b1df75c2 feat(attack): air defense spawns further from the thing they are defending 2019-06-23 14:27:07 -07:00
Wrycu
89f122c325 feat(attack): add multi-unit SAM site support 2019-06-23 13:43:16 -07:00
Wrycu
e9103acb07 feat(attack): base attack - now spawn all CAP aircraft when a base is attacked 2019-06-22 14:30:27 -07:00
Wrycu
c7eae7e97a fix(base): correct AA spawn 2019-06-22 14:17:28 -07:00
Wrycu
f5851d09f5 fix(overview): correct negative radius 2019-06-22 14:15:24 -07:00
Vasyl Horbachenko
ba8112280d Merge pull request #66 from shdwp/develop
Develop
2019-05-31 16:49:03 +03:00
Vasyl Horbachenko
4cc373595c Update README.md 2019-05-31 16:39:18 +03:00
Wrycu
36aa4edb05 refactor(units): add SAMs to air defense 2019-05-30 21:32:39 -07:00
Wrycu
26840373ed refactor(turns): modify turn settings
* raise air defense limits
* enemy gains more units even if no action is taken on a turn
* player gains money even if no action was taken on a turn
2019-05-30 21:31:50 -07:00
Vasyl Horbachenko
4c5a2650d1 Merge pull request #58 from JohanAberg/patch-1
ValueError: negative radius
2019-05-21 04:50:30 +03:00
Johan Aberg
b99cdfb5c3 ValueError: negative radius
When cp.base.strength is zero 0, radius_m becomes negative and an ValueError is raised.

INFO:root:Commision Al Maktoum: {<class 'dcs.vehicles.Armor.APC_BTR_80'>: 3}
ERROR:root:<class 'ValueError'>
Traceback (most recent call last):
  File "tkinter\__init__.py", line 1702, in __call__
  File "ui\window.py", line 156, in dismiss
  File "ui\mainmenu.py", line 29, in display
  File "ui\overviewcanvas.py", line 590, in update
  File "ui\overviewcanvas.py", line 252, in draw
  File "ui\overviewcanvas.py", line 312, in draw_map
  File "ui\overviewcanvas.py", line 331, in draw_bases
ValueError: negative radius

Proposed fix:
radius_m = max(radius * cp.base.strength - 2, 0)
2019-04-04 19:10:59 +13:00
Vasyl Horbachenko
6ec14e744e fixed starting bat; updated escort options for Strikes 2019-03-31 19:55:27 +03:00
Vasyl Horbachenko
310db66c22 Merge branch 'develop' of https://github.com/shdwp/dcs_liberation into develop 2019-03-27 11:53:14 +02:00
Vasyl Horbachenko
c7c2b9a248 special flight for AI to SEAD; AI bomb task 2019-03-27 11:53:00 +02:00
Vasyl Horbachenko
b697a8b40a cleanups & minor fixesh 2019-03-27 10:41:40 +02:00
Vasyl Horbachenko
c152b49b88 minor fixes; F-14B 2019-03-27 08:47:29 +02:00
Vasyl Horbachenko
2356fc2bbf Merge pull request #54 from Khopa/develop_khopa
Re-added missing png icons
2019-01-23 23:27:34 +02:00
Vasyl Horbachenko
f7e2c8921c integration tests for operation generation; adjusted waypoint altitude for AI to not fly too low; removed C101 from list of generated AI aircrafts 2019-01-16 01:07:24 +02:00
Khopa
604352f8df Re-added missing icons (they were somehow removed from the repository, and the software couldn't be launched) 2018-12-03 21:06:47 +01:00
Vasyl Horbachenko
fbbe56f954 fixes to convoy strikes; UI updates; don't generate helis for enemy 2018-11-06 04:06:59 +02:00
Vasyl Horbachenko
7842c69ebb fixed incompatible missions being available from carriers 2018-11-06 02:59:24 +02:00
Vasyl Horbachenko
e1d50f1f27 added missing assets; convoy strike event 2018-11-06 02:33:38 +02:00
Vasyl Horbachenko
9d0997624b fixes for selectable departure; generate statics for used units; raised RTB alt 2018-11-05 03:17:06 +02:00
Vasyl Horbachenko
355cd3e0e4 display events on map 2018-11-04 04:50:51 +02:00
Vasyl Horbachenko
af5cd57094 WIP: display events on the map 2018-11-04 04:06:44 +02:00
Vasyl Horbachenko
8f85101cec WIP: display events on the map; start events from adjacent CPs 2018-11-04 02:38:14 +02:00
Vasyl Horbachenko
97be483624 Merge pull request #43 from Khopa/sdl_map
Scrollable & Zoomable Map view
2018-11-04 00:58:14 +02:00
Khopa
1ff5721912 Possible to toggle map size 2018-11-03 23:56:10 +01:00
Vasyl Horbachenko
80cbc663bf Merge pull request #45 from calvinmorrow/labels_and_coalition_view
Labels and coalition view
2018-11-04 00:54:21 +02:00
Vasyl Horbachenko
b3e729af0d Merge pull request #44 from calvinmorrow/bugfix_37
Add initiatorMissionID key to multiplayer debriefing table
2018-11-03 20:45:56 +02:00
Calvin Morrow
3ed864021d Move log button and minimize padding to fit existing window size 2018-10-31 15:24:34 -07:00
Calvin Morrow
8bc269fa99 Add labels game setting and map visibility setting to mission generation forced options 2018-10-31 15:11:12 -07:00
Khopa
c656c0f7e4 Confirm dialog to start a new game 2018-10-31 17:18:08 +01:00
Khopa
858e5d2d04 Do redraw the map when declutter options are toggled 2018-10-31 14:35:17 +01:00
Khopa
903a8db46f Commented debug mouse cursos position rect 2018-10-31 14:26:23 +01:00
Khopa
5aa731853d The zoom is a bit less annoying to use 2018-10-31 14:24:37 +01:00
Khopa
540096178c Display zoom level on pygame overlay in bottom right corner 2018-10-31 14:22:49 +01:00
Khopa
ed2a611197 Removed unnecessary print 2018-10-31 14:14:17 +01:00
Khopa
1c61b0b5a2 Refactored the way icons are loaded + reformat 2018-10-31 13:10:07 +01:00
Calvin Morrow
82f7e5d0c4 Add initiatorMissionID key to multiplayer debriefing table for dead unit detection 2018-10-30 19:40:59 -07:00
Khopa
ecb2c86dc4 Change order of map view options 2018-10-31 01:29:47 +01:00
Khopa
5cbbc3b1ab Improved base intel view 2018-10-31 01:22:08 +01:00
Khopa
e0b2e178f9 Display detailled info about base when mouse hover 2018-10-31 00:59:02 +01:00
Khopa
390974ba0f Icons for all kind of targets 2018-10-31 00:20:29 +01:00
Khopa
91379ff7d9 Instructions for map + tweaks (enable AA for text) 2018-10-30 23:32:27 +01:00
Khopa
1fe9e56997 Added missing resources. Bigger maps for nevada & caucasus 2018-10-30 20:30:10 +01:00
Khopa
e10b853712 Much better performance for the pygame viewport. 2018-10-30 20:00:14 +01:00
Khopa
7d70862e72 Work in progress : features restored. 2018-10-30 17:12:35 +01:00
Khopa
90bdcec7ff Render map on a pygame SDL layer. WIP 2018-10-30 14:47:31 +01:00
Vasyl Horbachenko
63da350223 armor balance tweaks 2018-10-23 04:04:30 +03:00
Vasyl Horbachenko
854f31cb7a armor balance improvements; fixed trigger for farps 2018-10-23 03:40:12 +03:00
Vasyl Horbachenko
eba6daf6c8 Merge remote-tracking branch 'origin/master' 2018-10-23 03:08:13 +03:00
Vasyl Horbachenko
911d57b415 fixed FOB generation 2018-10-23 03:08:01 +03:00
Vasyl Horbachenko
d91e0344a7 Update README.md 2018-10-23 00:54:55 +03:00
Vasyl Horbachenko
e9d7ee51f3 readme upd 2018-10-22 02:29:42 +03:00
Vasyl Horbachenko
9053408e13 Merge branch 'develop' 2018-10-22 02:18:26 +03:00
Vasyl Horbachenko
8f4094ee98 number of fixes 2018-10-22 02:13:38 +03:00
Vasyl Horbachenko
933e064079 fixed debriefing crash 2018-10-14 04:30:46 +03:00
Vasyl Horbachenko
274e08dd8b quick mission debriefing fixed; adjusted constants 2018-10-14 04:09:05 +03:00
Vasyl Horbachenko
05c968edc2 added Gazelle as CAS aircraft + minor fixes 2018-10-13 23:38:26 +03:00
Vasyl Horbachenko
270820de0b added Gazelle as CAS aircraft 2018-10-13 22:50:26 +03:00
Vasyl Horbachenko
e049a97bec minor text update 2018-10-13 22:30:43 +03:00
Vasyl Horbachenko
e2306ba0f3 new weather, strike objectives placement fixes & tarawa for av8b 2018-10-13 22:28:30 +03:00
Vasyl Horbachenko
6d0f488672 updated version compatibility check 2018-10-13 04:44:59 +03:00
Vasyl Horbachenko
397f9a58cb fixed naval intercept crash; fixed wrong targets order; fixed initial waypoint being WP #1; m2k a2g ccip; fixed time being time zone offset ahead; lowered rain weather chance 2018-10-13 04:41:18 +03:00
Vasyl Horbachenko
4fc766a524 trigger fixes; strike waypoint fixes; m2k strike payload update 2018-10-13 02:36:25 +03:00
Vasyl Horbachenko
3f8b5c6c00 minor UI fix 2018-10-12 23:33:10 +03:00
Vasyl Horbachenko
ce43be0d67 display strike objectives on map; minor fixes 2018-10-12 23:31:00 +03:00
Vasyl Horbachenko
ff08888385 minor adjustment in strike objectives generation 2018-10-12 22:38:26 +03:00
Vasyl Horbachenko
6b96410ea4 updated config menu 2018-10-12 22:29:37 +03:00
Vasyl Horbachenko
f21bd10f09 number of minor bugfixes and UI improvements 2018-10-12 21:32:43 +03:00
Vasyl Horbachenko
07b35f8ee1 specify tanker callsign in briefing 2018-10-12 05:45:28 +03:00
Vasyl Horbachenko
251435ae0b debriefing results based only on IDs; fixes in strike ops 2018-10-12 05:41:52 +03:00
Vasyl Horbachenko
b81bf90319 few minor fixes 2018-10-12 03:46:53 +03:00
Vasyl Horbachenko
d6b1b8665d minor fixes 2018-10-12 03:37:32 +03:00
Vasyl Horbachenko
64bd3e6a52 removed debugging code 2018-10-12 03:17:41 +03:00
Vasyl Horbachenko
520a0f91fd UI update; enemy vehicle difficulty settings; minor adjustments 2018-10-12 03:13:33 +03:00
Vasyl Horbachenko
0015667829 new frontline position finding method; AA for strikes; other minor fixes and adjustments 2018-10-12 00:12:25 +03:00
Vasyl Horbachenko
35a7da2816 oil strike objectives correct placement; updated starting point for gulf 2018-10-11 04:42:38 +03:00
Vasyl Horbachenko
5bbf3fc49f fixed land map for gulf; ability to get logs from settings; minor trigger updates 2018-10-11 04:12:02 +03:00
Vasyl Horbachenko
7a8dfeb819 Merge remote-tracking branch 'origin/develop' into develop 2018-10-11 03:45:31 +03:00
Vasyl Horbachenko
e28a24c875 randomized strike objects with templates; forbid ground objects and vehicles placement on mountains and in forests; updated push trigger so it include player group; adjacent CP missions could be initiated from carriers 2018-10-11 03:45:20 +03:00
Vasyl Horbachenko
823c6a6137 Merge pull request #27 from Khopa/combined_arms_slots
Combined arms slots
2018-10-03 02:17:21 +03:00
Khopa
2cbe63f162 Fixed potential exception in start if user enter invalid value inside the combined arms slots entry. 2018-09-29 11:55:42 +02:00
Khopa
93d0746d3e Replaced with player faction check by coalition check 2018-09-29 11:46:15 +02:00
Khopa
8cb7c7378f Type for ca_slot_entry 2018-09-29 11:34:22 +02:00
Khopa
3500c85e8d UI for CA slot selection (align with unit count rows) 2018-09-22 13:42:51 +02:00
Khopa
c699567c73 Only generate Combined Arms "Tactical Commander" slots for player side. 2018-09-22 13:33:31 +02:00
Khopa
056c397e68 [Cherry Picked] Added possibility to add 'DCS: Combined Arms' slots to the generated mission. 2018-09-22 11:46:40 +02:00
Vasyl Horbachenko
8431c7745d fixed base atttack op 2018-09-15 00:30:40 +03:00
Vasyl Horbachenko
8df4607e50 Update README.md 2018-09-14 23:49:50 +03:00
Vasyl Horbachenko
edf9efddf9 update to ground objects parser; waypoints in briefings & general briefings update; minor fixes 2018-09-13 05:09:57 +03:00
Vasyl Horbachenko
03fc17fae6 new ground objects format & parser; place dead objects instead of removing them completely 2018-09-12 05:19:21 +03:00
Vasyl Horbachenko
6fb342a42c updated location argument; updated ground units placement during attack operation 2018-09-12 00:20:35 +03:00
Vasyl Horbachenko
262347f8c8 tweaked caucasus start times 2018-09-11 22:46:26 +03:00
Vasyl Horbachenko
1176b92073 capture armor placement tweaks 2018-09-11 17:39:23 +03:00
Vasyl Horbachenko
afb084ebf8 minor bugfixes 2018-09-11 05:55:05 +03:00
Vasyl Horbachenko
12853feec3 updated version check 2018-09-11 03:35:52 +03:00
Vasyl Horbachenko
d31876b65e FARP spawn for helis on frontline cas; updated ground object placements 2018-09-11 03:12:33 +03:00
Vasyl Horbachenko
ca521e7e51 FARPs for heli flights WIP 2018-09-10 23:12:04 +03:00
Vasyl Horbachenko
f21c515d5c convert resource files to gif 2018-09-10 21:46:34 +03:00
Vasyl Horbachenko
fa5259d1f2 minor fixes before rc 2018-09-10 01:48:38 +03:00
Vasyl Horbachenko
7a5361c057 minor updates and strike mission objects expand 2018-09-10 01:38:50 +03:00
Vasyl Horbachenko
40bfb6fa88 strike operations fixes 2018-09-09 23:56:30 +03:00
Vasyl Horbachenko
61a237d1ae proper settings for barcap 2018-09-09 20:57:32 +03:00
Vasyl Horbachenko
cf7276b528 updated AA units 2018-09-09 20:39:49 +03:00
Vasyl Horbachenko
67f69d0a7f updated AA units 2018-09-09 20:27:53 +03:00
Vasyl Horbachenko
a918914431 dont limit aircraft to predefined role; better scrambling screen 2018-09-09 20:21:07 +03:00
Vasyl Horbachenko
49f2c00d76 Update README.md 2018-09-09 04:39:53 +03:00
Vasyl Horbachenko
d284305323 Update README.md 2018-09-09 04:39:03 +03:00
Vasyl Horbachenko
c32ac8577c Update README.md 2018-09-09 04:38:47 +03:00
Vasyl Horbachenko
4ba1dd87e8 updates to strike missions; frontline operations invalid units placement fixed; minor UI updates 2018-09-09 04:15:44 +03:00
Vasyl Horbachenko
e0d82da6cb save game version check; mission description in briefings; minor fixes and improvements; predefined ground objects (statics) support; strike mission WIP 2018-09-09 01:21:32 +03:00
Vasyl Horbachenko
2179e4af47 Merge pull request #12 from Khopa/ui_dev
Modified UI Style
2018-09-08 19:15:52 +03:00
Vasyl Horbachenko
0d5530f5ea Update README.md 2018-08-15 15:14:19 +03:00
Vasyl Horbachenko
a528249062 Update README.md 2018-08-15 15:13:49 +03:00
Khopa
75d734d6e4 Typo in UI 2018-08-09 13:08:07 +02:00
Khopa
1d73affa08 New style for event result menu 2018-08-08 23:40:10 +02:00
Khopa
834ee30c86 Style for event result menu 2018-08-08 23:18:49 +02:00
Khopa
d236b8a94d UI alignement in base menu 2018-08-08 22:38:09 +02:00
Khopa
a132cba7ef Changed the color of text on map (easier to read this way for now imo) 2018-08-08 22:30:33 +02:00
Khopa
86706231e0 New style for event/mission menu 2018-08-08 22:30:12 +02:00
Khopa
5eb921948a Display the units x/x/x value of the base in the base menu. 2018-08-08 21:52:09 +02:00
Khopa
fd54a5f86c Display the name of the base in base menu 2018-08-08 21:41:16 +02:00
Khopa
1b2ad5b419 Improved style of main menu, configuration menu, and base menu 2018-08-08 21:22:00 +02:00
Khopa
14cd54668e Improved style of new game menu 2018-08-08 18:45:25 +02:00
Khopa
2047089b64 Added icon to ui windows. 2018-08-08 16:27:42 +02:00
Khopa
ee924ef36e Added title name to UI window 2018-08-08 16:24:20 +02:00
Vasyl Horbachenko
f9c1dd980b spamram for fa18; non-randomized groups vertical separation on spawn; argument to affect DCS folder selection 2018-08-04 04:35:27 +03:00
Vasyl Horbachenko
74c1861240 build archieve tool 2018-07-31 23:40:35 +03:00
Vasyl Horbachenko
e8226782c1 Merge pull request #8 from shdwp/develop
minor balance improvements
2018-07-31 04:17:48 +03:00
Vasyl Horbachenko
a592cf3a05 minor balance improvements 2018-07-31 04:16:17 +03:00
Vasyl Horbachenko
0b1cb0d770 Merge pull request #7 from shdwp/develop
Develop
2018-07-31 04:00:18 +03:00
Vasyl Horbachenko
a4aa1cff3a widened spawn windows for aircraft 2018-07-31 03:59:55 +03:00
Vasyl Horbachenko
6a02d2ffb6 few minor fixes; added F5 2018-07-31 03:47:50 +03:00
Vasyl Horbachenko
7458181e90 success rate calculation division by zero fix 2018-07-29 04:41:32 +03:00
Vasyl Horbachenko
ec28cdc936 version bump 2018-07-29 04:20:30 +03:00
Vasyl Horbachenko
9dbc9a8a56 prompt window with logs on raised exception; minor UI updates; minor fixes 2018-07-29 04:16:39 +03:00
Vasyl Horbachenko
73d4a2d414 moved viggen to CAS; tanker position fix on enemy attacks; more logs 2018-07-28 04:58:28 +03:00
Vasyl Horbachenko
e93ad8b800 adjusted budget amounts 2018-07-20 04:08:04 +03:00
Vasyl Horbachenko
d48985ca6d new airports for PG; TACANs and ICLS; list frequencies in mission briefing; carrier ops improvements; cold start option 2018-07-20 04:06:13 +03:00
Vasyl Horbachenko
5f7d717b63 fixed sir abu having a frontline 2018-07-19 01:07:24 +03:00
Vasyl Horbachenko
e266698e68 fixed start.bat for usernames w/ spaces; fixed triggers for player defending; minor fixes 2018-07-19 00:57:15 +03:00
Vasyl Horbachenko
e8098e795c added symlink to pydcs to resources/tools 2018-07-18 03:20:18 +03:00
Vasyl Horbachenko
8d69724272 env gen minor update 2018-07-18 03:14:25 +03:00
Vasyl Horbachenko
683114f916 updates to CAP op 2018-07-18 00:45:55 +03:00
Vasyl Horbachenko
3b454470f9 Merge branch 'improved_cas' of https://github.com/shdwp/dcs_pmcliberation into improved_cas 2018-07-17 23:31:58 +03:00
Vasyl Horbachenko
f40f83bb09 fixes and improvements for fronline CAP 2018-07-17 14:22:45 +03:00
Vasyl Horbachenko
932bec2f84 fixes to frontline attack; frontline CAP WIP 2018-07-17 14:22:45 +03:00
Vasyl Horbachenko
820820eb92 frontline attack ops 2018-07-17 14:22:45 +03:00
Vasyl Horbachenko
6f5835a2b8 Improved Frontline CAS 2018-07-17 14:21:50 +03:00
Vasiliy Horbachenko
07128bb5e6 fixed capture issue when two or more points were attacking the base 2018-07-17 05:53:55 +03:00
Vasyl Horbachenko
b302372d4c fixes and improvements for fronline CAP 2018-07-17 05:22:41 +03:00
Vasyl Horbachenko
cad7d2c735 fixes to frontline attack; frontline CAP WIP 2018-07-17 02:14:46 +03:00
Vasyl Horbachenko
e4c3f8bce2 frontline attack ops 2018-07-16 23:58:01 +03:00
Vasyl Horbachenko
1fbf4e292a Merge branch 'improved_cas' of https://github.com/shdwp/dcs_pmcliberation into improved_cas 2018-07-16 23:03:47 +03:00
Vasyl Horbachenko
62f5b2d06d Improved Frontline CAS 2018-07-16 23:02:08 +03:00
Vasyl Horbachenko
3831658162 more base recovery after attack 2018-07-16 22:55:29 +03:00
Vasyl Horbachenko
b2545e4de0 Improved Frontline CAS 2018-07-16 22:19:19 +03:00
Vasyl Horbachenko
26e43f5e54 Merge branch 'master' of https://github.com/shdwp/dcs_liberation 2018-07-16 22:12:31 +03:00
Vasyl Horbachenko
44ba5c32c6 use correct join char for paths 2018-07-16 22:11:47 +03:00
Vasyl Horbachenko
ce7d3a89c0 fixed user path for regular DCS 2018-07-16 22:11:03 +03:00
Vasyl Horbachenko
c74b1205df fixed user path for regular DCS 2018-07-16 22:05:42 +03:00
Vasyl Horbachenko
58dd16219b removed observer slots 2018-07-15 08:55:20 +03:00
Vasyl Horbachenko
e0e1d0238f nevada debugging code removal 2018-07-15 05:55:38 +03:00
Vasyl Horbachenko
71e22bdb21 Merge remote-tracking branch 'origin/master' 2018-07-15 04:21:07 +03:00
Vasyl Horbachenko
3c2025ab44 minor hotfixes in mp debriefing (WIP) 2018-07-15 04:20:55 +03:00
Vasyl Horbachenko
d314e22970 Update start.bat 2018-07-13 04:59:59 +03:00
Vasyl Horbachenko
ff7181b648 updated .gitignore to ignore payload resource files 2018-07-13 04:34:56 +03:00
Vasyl Horbachenko
4cbd30fdbc debriefing based on events, not world state; tweaked visualgen; vehicles will not group in single location during capture op; fixed triggers for carrier ops; fixed naval ops; correct speed for inflight heli spawns; awacs will not change it's freq 2018-07-13 04:33:07 +03:00
Vasiliy Horbachenko
4ce7480df8 debriefing based on events, not world state WIP 2018-07-12 05:12:01 +03:00
Vasyl Horbachenko
d2aede593b added files missing from git 2018-07-05 03:13:30 +03:00
Vasyl Horbachenko
32fb5ad0e2 fixed a number of issues: user directory on different drive, carrier ops, multiplayer debriefing parser, multiplayer mission generation; added su25 2018-07-05 02:42:46 +03:00
Vasyl Horbachenko
fa55ae1fcc fixed multiplayer mission start 2018-07-04 05:20:35 +03:00
Vasyl Horbachenko
90fbe77682 fixed unplayable F-5 2018-07-04 04:51:31 +03:00
1296 changed files with 88462 additions and 37048 deletions

2
.git-blame-ignore-revs Normal file
View File

@@ -0,0 +1,2 @@
# Black
a47bef1f1336fd264d0b175f4421758339a30acb

38
.github/ISSUE_TEMPLATE/bug_report.md vendored Normal file
View File

@@ -0,0 +1,38 @@
---
name: Bug report
about: Create a report to help us improve
title: ''
labels: bug
assignees: ''
---
Before filing, please search the issue tracker to see if the issue has already been reported.
**Describe the bug**
A clear and concise description of what the bug is.
**To Reproduce**
Steps to reproduce the behavior:
1. Go to '...'
2. Click on '....'
3. Scroll down to '....'
4. See error
**Expected behavior**
A clear and concise description of what you expected to happen.
**Additional information**
We will usually need more information for debugging. Include as much of the following as you are able:
- DCS Liberation save file (the `.liberation` file you save from the DCS Liberation window). By default these are located in your DCS saved games directory (`%USERPROFILE%/Saved Games/DCS`).
- The generated mission file (the `.miz` file that you load in DCS to play the turn). By default these are located in your missions directory (`%USERPROFILE%/Saved Games/DCS/Missions`).
- A tacview track file, especially when demonstrating an issue with AI behavior. By default these are located in your Tacview tracks directory (`%USERPROFILE%/Documents/Tacview`).
- The state.json file from the finished mission when the problem is related to results processing. By default these are located in your Liberation install directory.
**Version information (please complete the following information):**
- DCS Liberation [e.g. 2.3.1]:
**Additional context**
Add any other context about the problem here.

View File

@@ -0,0 +1,19 @@
---
name: Feature request
about: Suggest an idea for this project
title: ''
labels: enhancement
assignees: ''
---
Before filing, please search the issue tracker to see if this feature has already been requested.
**Is your feature request related to a problem? Please describe.**
A clear and concise description of what the problem is. Ex. I'm always frustrated when [...]
**Describe the solution you'd like**
A clear and concise description of what you want to happen.
**Additional context**
Add any other context or screenshots about the feature request here.

13
.github/workflows/black.yml vendored Normal file
View File

@@ -0,0 +1,13 @@
name: Lint
on: [push, pull_request]
jobs:
lint:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- uses: actions/setup-python@v2
- uses: psf/black@stable
with:
args: ". --check"

53
.github/workflows/build.yml vendored Normal file
View File

@@ -0,0 +1,53 @@
name: Build
on: [push, pull_request]
jobs:
build:
runs-on: windows-latest
steps:
- uses: actions/checkout@v2
with:
submodules: true
- name: Set up Python 3.8
uses: actions/setup-python@v2
with:
python-version: 3.8
- name: Install environment
run: |
python -m venv ./venv
- name: Install dependencies
run: |
./venv/scripts/activate
python -m pip install -r requirements.txt
# For some reason the shiboken2.abi3.dll is not found properly, so I copy it instead
Copy-Item .\venv\Lib\site-packages\shiboken2\shiboken2.abi3.dll .\venv\Lib\site-packages\PySide2\ -Force
- name: mypy game
run: |
./venv/scripts/activate
mypy game
- name: mypy gen
run: |
./venv/scripts/activate
mypy gen
- name: update build number
run: |
[IO.File]::WriteAllLines($pwd.path + "\resources\buildnumber", $env:GITHUB_RUN_NUMBER)
- name: Build binaries
run: |
./venv/scripts/activate
$env:PYTHONPATH=".;./pydcs"
pyinstaller pyinstaller.spec
- uses: actions/upload-artifact@v2
with:
name: dcs_liberation
path: dist/

108
.github/workflows/release.yml vendored Normal file
View File

@@ -0,0 +1,108 @@
name: Release Pipeline
on:
push:
tags: [ '*' ]
jobs:
build:
runs-on: windows-latest
steps:
- uses: actions/checkout@v2
with:
submodules: true
- name: Set up Python 3.8
uses: actions/setup-python@v2
with:
python-version: 3.8
- name: Install environment
run: |
python -m venv ./venv
- name: Install dependencies
run: |
./venv/scripts/activate
python -m pip install -r requirements.txt
# For some reason the shiboken2.abi3.dll is not found properly, so I copy it instead
Copy-Item .\venv\Lib\site-packages\shiboken2\shiboken2.abi3.dll .\venv\Lib\site-packages\PySide2\ -Force
- name: Finalize version
run: |
New-Item -ItemType file resources\final
- name: mypy game
run: |
./venv/scripts/activate
mypy game
- name: mypy gen
run: |
./venv/scripts/activate
mypy gen
- name: Build binaries
run: |
./venv/scripts/activate
$env:PYTHONPATH=".;./pydcs"
pyinstaller pyinstaller.spec
- name: Create Installer
env:
TAG_NAME: ${{ github.ref }}
run: |
Copy-Item .\changelog.md .\dist
- uses: actions/upload-artifact@v2
with:
name: dcs_liberation
path: dist/
release:
needs: [ build ]
runs-on: windows-latest
steps:
- uses: actions/download-artifact@v2
with:
name: dcs_liberation
- name: "Get Version"
id: version
env:
TAG_NAME: ${{ github.ref }}
run: |
Get-ChildItem -Recurse -Depth 1
$version = ($env:TAG_NAME -split "/") | Select-Object -Last 1
$prerelease = ("2.1.1-alpha3" -match '[^\.\d]').ToString().ToLower()
Write-Host $version
Write-Host $prerelease
Write-Output "::set-output name=number::$version"
Write-Output "::set-output name=prerelease::$prerelease"
$changelog = Get-Content .\changelog.md
$last_change = ($changelog | Select-String -Pattern "^#\s" | Select-Object -Skip 1 -First 1).LineNumber - 2
($changelog | Select-Object -First $last_change) -join "`n" | Out-File .\releasenotes.md
Compress-Archive -Path .\dcs_liberation -DestinationPath "dcs_liberation.$version.zip" -Compression Optimal
- uses: actions/create-release@v1
id: create_release
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with:
tag_name: ${{ github.ref }}
release_name: ${{ github.ref }}
body_path: releasenotes.md
draft: false
prerelease: ${{ steps.version.outputs.prerelease }}
- uses: actions/upload-release-asset@v1
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with:
upload_url: ${{ steps.create_release.outputs.upload_url }}
asset_path: ./dcs_liberation.${{ steps.version.outputs.number }}.zip
asset_name: dcs_liberation.${{ steps.version.outputs.number }}.zip
asset_content_type: application/zip

26
.gitignore vendored
View File

@@ -1,12 +1,24 @@
*.pyc
__pycache__
build/*
build/**
resources/payloads/*.lua
venv
logs.txt
.DS_Store
.vscode/settings.json
dist/**
a.py
resources/tools/a.miz
# User-specific stuff
.idea/**/workspace.xml
.idea/**/tasks.xml
.idea/**/dictionaries
.idea/**/shelf
.idea/misc.xml
.idea/*.iml
.idea/
.env
/kneeboards
/liberation_preferences.json
/state.json
logs/
qt_ui/logs/liberation.log
*.psd

5
.gitmodules vendored
View File

@@ -1,3 +1,4 @@
[submodule "submodules/dcs"]
path = submodules/dcs
[submodule "pydcs"]
path = pydcs
url = https://github.com/pydcs/dcs
branch = master

View File

@@ -1,10 +0,0 @@
<component name="InspectionProjectProfileManager">
<profile version="1.0">
<option name="myName" value="Project Default" />
<inspection_tool class="SpellCheckingInspection" enabled="false" level="TYPO" enabled_by_default="false">
<option name="processCode" value="true" />
<option name="processLiterals" value="true" />
<option name="processComments" value="true" />
</inspection_tool>
</profile>
</component>

8
.idea/modules.xml generated
View File

@@ -1,8 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="ProjectModuleManager">
<modules>
<module fileurl="file://$PROJECT_DIR$/.idea/dcs_pmcliberation.iml" filepath="$PROJECT_DIR$/.idea/dcs_pmcliberation.iml" />
</modules>
</component>
</project>

11
.idea/vcs.xml generated
View File

@@ -1,11 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="GitSharedSettings">
<option name="FORCE_PUSH_PROHIBITED_PATTERNS">
<list />
</option>
</component>
<component name="VcsDirectoryMappings">
<mapping directory="$PROJECT_DIR$" vcs="Git" />
</component>
</project>

6
.pre-commit-config.yaml Normal file
View File

@@ -0,0 +1,6 @@
repos:
- repo: https://github.com/psf/black
rev: 20.8b1
hooks:
- id: black
language_version: python3

30
.vscode/launch.json vendored Normal file
View File

@@ -0,0 +1,30 @@
{
// Use IntelliSense to learn about possible attributes.
// Hover to view descriptions of existing attributes.
// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
"version": "0.2.0",
"configurations": [
{
"name": "Python: Main",
"type": "python",
"request": "launch",
"program": "qt_ui\\main.py",
"console": "integratedTerminal",
"env": {
"PYTHONPATH": ".;./pydcs"
},
"preLaunchTask": "Prepare Environment"
},
{
"name": "Python: Make Release",
"type": "python",
"request": "launch",
"program": "resources\\tools\\mkrelease.py",
"console": "integratedTerminal",
"env": {
"PYTHONPATH": ".;./pydcs"
},
"preLaunchTask": "Prepare Environment"
}
]
}

35
.vscode/tasks.json vendored Normal file
View File

@@ -0,0 +1,35 @@
{
// See https://go.microsoft.com/fwlink/?LinkId=733558
// for the documentation about the tasks.json format
"version": "2.0.0",
"tasks": [
{
"label": "Prepare Environment",
"type": "shell",
"isBackground": false,
"problemMatcher": [],
"command": "powershell",
"args": [
"-Command",
"& {if (-not (Test-Path ${workspaceFolder}\\venv)) { python -m venv ${workspaceFolder}\\venv; . ${workspaceFolder}\\venv\\scripts\\activate.ps1; pip install -r requirements.txt; Copy-Item ${workspaceFolder}\\venv\\Lib\\site-packages\\shiboken2\\shiboken2.abi3.dll ${workspaceFolder}\\venv\\Lib\\site-packages\\PySide2 } }",
],
"group": "build",
"options": {
"env": {
"PYTHONPATH": ".;./pydcs"
}
},
"presentation": {
"echo": true,
"reveal": "never",
"focus": false,
"panel": "shared",
"showReuseMessage": true,
"clear": false
},
"runOptions": {
"runOn": "folderOpen"
}
}
]
}

76
CODE_OF_CONDUCT.md Normal file
View File

@@ -0,0 +1,76 @@
# Contributor Covenant Code of Conduct
## Our Pledge
In the interest of fostering an open and welcoming environment, we as
contributors and maintainers pledge to making participation in our project and
our community a harassment-free experience for everyone, regardless of age, body
size, disability, ethnicity, sex characteristics, gender identity and expression,
level of experience, education, socio-economic status, nationality, personal
appearance, race, religion, or sexual identity and orientation.
## Our Standards
Examples of behavior that contributes to creating a positive environment
include:
* Using welcoming and inclusive language
* Being respectful of differing viewpoints and experiences
* Gracefully accepting constructive criticism
* Focusing on what is best for the community
* Showing empathy towards other community members
Examples of unacceptable behavior by participants include:
* The use of sexualized language or imagery and unwelcome sexual attention or
advances
* Trolling, insulting/derogatory comments, and personal or political attacks
* Public or private harassment
* Publishing others' private information, such as a physical or electronic
address, without explicit permission
* Other conduct which could reasonably be considered inappropriate in a
professional setting
## Our Responsibilities
Project maintainers are responsible for clarifying the standards of acceptable
behavior and are expected to take appropriate and fair corrective action in
response to any instances of unacceptable behavior.
Project maintainers have the right and responsibility to remove, edit, or
reject comments, commits, code, wiki edits, issues, and other contributions
that are not aligned to this Code of Conduct, or to ban temporarily or
permanently any contributor for other behaviors that they deem inappropriate,
threatening, offensive, or harmful.
## Scope
This Code of Conduct applies both within project spaces and in public spaces
when an individual is representing the project or its community. Examples of
representing a project or community include using an official project e-mail
address, posting via an official social media account, or acting as an appointed
representative at an online or offline event. Representation of a project may be
further defined and clarified by project maintainers.
## Enforcement
Instances of abusive, harassing, or otherwise unacceptable behavior may be
reported by contacting the project team at khopa.studio@gmail.com. All
complaints will be reviewed and investigated and will result in a response that
is deemed necessary and appropriate to the circumstances. The project team is
obligated to maintain confidentiality with regard to the reporter of an incident.
Further details of specific enforcement policies may be posted separately.
Project maintainers who do not follow or enforce the Code of Conduct in good
faith may face temporary or permanent repercussions as determined by other
members of the project's leadership.
## Attribution
This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4,
available at https://www.contributor-covenant.org/version/1/4/code-of-conduct.html
[homepage]: https://www.contributor-covenant.org
For answers to common questions about this code of conduct, see
https://www.contributor-covenant.org/faq

26
CONTRIBUTING.md Normal file
View File

@@ -0,0 +1,26 @@
First, note that we have a code of conduct, please follow it in all your interactions with the project.
## Contributing as a non-developer
* Report bugs by opening issues here on Github.
* Help others users on Discord by answering their questions.
* Raise awareness about the project, by making a video and/or a tutorial.
Should you report a bug, please use the search bar at the top of the page to see if it has already been reported.
Note that you may need to remove the filter for open bugs if it's something we've recently fixed.
## Making content for Liberation
You can create new campaigns : See [campaign creation wiki](https://github.com/Khopa/dcs_liberation/wiki/Custom-Campaigns).
You can also improve existing campaigns.
You can then submit new campaigns on the "campaigns" channel on Discord, or by making a pull request if you are comfortable with git.
## Develop new features
If you want to develop a new feature, we recommend you first open an issue describing the new feature and discuss it with us on Discord before starting development.
However, feel free to work on any existing issue.
## Pull requests
Please submit your pull requests on the **develop** branch. We expect a description of its content, and when applicable, a reference to the issue(s) it is resolving.

165
LICENSE Normal file
View File

@@ -0,0 +1,165 @@
GNU LESSER GENERAL PUBLIC LICENSE
Version 3, 29 June 2007
Copyright (C) 2007 Free Software Foundation, Inc. <http://fsf.org/>
Everyone is permitted to copy and distribute verbatim copies
of this license document, but changing it is not allowed.
This version of the GNU Lesser General Public License incorporates
the terms and conditions of version 3 of the GNU General Public
License, supplemented by the additional permissions listed below.
0. Additional Definitions.
As used herein, "this License" refers to version 3 of the GNU Lesser
General Public License, and the "GNU GPL" refers to version 3 of the GNU
General Public License.
"The Library" refers to a covered work governed by this License,
other than an Application or a Combined Work as defined below.
An "Application" is any work that makes use of an interface provided
by the Library, but which is not otherwise based on the Library.
Defining a subclass of a class defined by the Library is deemed a mode
of using an interface provided by the Library.
A "Combined Work" is a work produced by combining or linking an
Application with the Library. The particular version of the Library
with which the Combined Work was made is also called the "Linked
Version".
The "Minimal Corresponding Source" for a Combined Work means the
Corresponding Source for the Combined Work, excluding any source code
for portions of the Combined Work that, considered in isolation, are
based on the Application, and not on the Linked Version.
The "Corresponding Application Code" for a Combined Work means the
object code and/or source code for the Application, including any data
and utility programs needed for reproducing the Combined Work from the
Application, but excluding the System Libraries of the Combined Work.
1. Exception to Section 3 of the GNU GPL.
You may convey a covered work under sections 3 and 4 of this License
without being bound by section 3 of the GNU GPL.
2. Conveying Modified Versions.
If you modify a copy of the Library, and, in your modifications, a
facility refers to a function or data to be supplied by an Application
that uses the facility (other than as an argument passed when the
facility is invoked), then you may convey a copy of the modified
version:
a) under this License, provided that you make a good faith effort to
ensure that, in the event an Application does not supply the
function or data, the facility still operates, and performs
whatever part of its purpose remains meaningful, or
b) under the GNU GPL, with none of the additional permissions of
this License applicable to that copy.
3. Object Code Incorporating Material from Library Header Files.
The object code form of an Application may incorporate material from
a header file that is part of the Library. You may convey such object
code under terms of your choice, provided that, if the incorporated
material is not limited to numerical parameters, data structure
layouts and accessors, or small macros, inline functions and templates
(ten or fewer lines in length), you do both of the following:
a) Give prominent notice with each copy of the object code that the
Library is used in it and that the Library and its use are
covered by this License.
b) Accompany the object code with a copy of the GNU GPL and this license
document.
4. Combined Works.
You may convey a Combined Work under terms of your choice that,
taken together, effectively do not restrict modification of the
portions of the Library contained in the Combined Work and reverse
engineering for debugging such modifications, if you also do each of
the following:
a) Give prominent notice with each copy of the Combined Work that
the Library is used in it and that the Library and its use are
covered by this License.
b) Accompany the Combined Work with a copy of the GNU GPL and this license
document.
c) For a Combined Work that displays copyright notices during
execution, include the copyright notice for the Library among
these notices, as well as a reference directing the user to the
copies of the GNU GPL and this license document.
d) Do one of the following:
0) Convey the Minimal Corresponding Source under the terms of this
License, and the Corresponding Application Code in a form
suitable for, and under terms that permit, the user to
recombine or relink the Application with a modified version of
the Linked Version to produce a modified Combined Work, in the
manner specified by section 6 of the GNU GPL for conveying
Corresponding Source.
1) Use a suitable shared library mechanism for linking with the
Library. A suitable mechanism is one that (a) uses at run time
a copy of the Library already present on the user's computer
system, and (b) will operate properly with a modified version
of the Library that is interface-compatible with the Linked
Version.
e) Provide Installation Information, but only if you would otherwise
be required to provide such information under section 6 of the
GNU GPL, and only to the extent that such information is
necessary to install and execute a modified version of the
Combined Work produced by recombining or relinking the
Application with a modified version of the Linked Version. (If
you use option 4d0, the Installation Information must accompany
the Minimal Corresponding Source and Corresponding Application
Code. If you use option 4d1, you must provide the Installation
Information in the manner specified by section 6 of the GNU GPL
for conveying Corresponding Source.)
5. Combined Libraries.
You may place library facilities that are a work based on the
Library side by side in a single library together with other library
facilities that are not Applications and are not covered by this
License, and convey such a combined library under terms of your
choice, if you do both of the following:
a) Accompany the combined library with a copy of the same work based
on the Library, uncombined with any other library facilities,
conveyed under the terms of this License.
b) Give prominent notice with the combined library that part of it
is a work based on the Library, and explaining where to find the
accompanying uncombined form of the same work.
6. Revised Versions of the GNU Lesser General Public License.
The Free Software Foundation may publish revised and/or new versions
of the GNU Lesser General Public License from time to time. Such new
versions will be similar in spirit to the present version, but may
differ in detail to address new problems or concerns.
Each version is given a distinguishing version number. If the
Library as you received it specifies that a certain numbered version
of the GNU Lesser General Public License "or any later version"
applies to it, you have the option of following the terms and
conditions either of that published version or of any later version
published by the Free Software Foundation. If the Library as you
received it does not specify a version number of the GNU Lesser
General Public License, you may choose any version of the GNU Lesser
General Public License ever published by the Free Software Foundation.
If the Library as you received it specifies that a proxy can decide
whether future versions of the GNU Lesser General Public License shall
apply, that proxy's public statement of acceptance of any version is
permanent authorization for you to choose that version for the
Library.

View File

@@ -1,8 +1,50 @@
[DCS World](https://www.digitalcombatsimulator.com/en/products/world/) single-player liberation dynamic campaign.
![Logo](https://i.imgur.com/c2k18E1.png)
[Installation instructions/Manual](https://github.com/shdwp/dcs_liberation/wiki)
[![Donate](https://img.shields.io/badge/Donate-PayPal-green.svg?logo=paypal)](https://www.paypal.com/paypalme/KhopaDCSL)
[![Patreon](https://img.shields.io/badge/patreon-become%20a%20patron-orange?logo=patreon)](https://patreon.com/khopa)
Inspired by *ARMA Liberation* mission.
[![Download](https://img.shields.io/github/downloads/khopa/dcs_liberation/total?label=Download)](https://github.com/Khopa/dcs_liberation/releases)
Uses [pydcs](http://github.com/pydcs/dcs) for mission generation.
[![Discord](https://img.shields.io/discord/595702951800995872?label=Discord&logo=discord)](https://discord.gg/bKrtrkJ)
[![GitHub pull requests](https://img.shields.io/github/issues-pr/khopa/dcs_liberation)](https://github.com/Khopa/dcs_liberation)
[![GitHub issues](https://img.shields.io/github/issues/khopa/dcs_liberation)](https://github.com/Khopa/dcs_liberation/issues)
![GitHub stars](https://img.shields.io/github/stars/khopa/dcs_liberation?style=social)
## About DCS Liberation
DCS Liberation is a [DCS World](https://www.digitalcombatsimulator.com/en/products/world/) turn based single-player or co-op dynamic campaign.
It is an external program that generates full and complex DCS missions and manage a persistent combat environment.
![Logo](https://i.imgur.com/4hq0rLq.png)
## Downloads
Latest release is available here : https://github.com/Khopa/dcs_liberation/releases
To download preview builds of the next version of DCS Liberation, see https://github.com/Khopa/dcs_liberation/wiki/Preview-builds.
## Bugs and feature requests
If you need to report a bug or want to suggest a new feature, you can do this on our [bug tracker](https://github.com/Khopa/dcs_liberation/issues). In either case, please use the search bar at the top of the page to see if it has already been reported. Note that you may need to remove the filter for open bugs if it's something we've recently fixed.
## Roadmap
Our plans for future releases can be found on our [Projects page](https://github.com/Khopa/dcs_liberation/projects). Each planned release has a Project, and the page for that project has columns for to do, in progress, and done. Items in the Done column are in the [preview build](https://github.com/Khopa/dcs_liberation/wiki/Preview-builds) for that release. Items in the To do column are planned to be added to that release.
## Resources
Tutorials, contributors and developer's guides are available in the project's [Wiki](https://github.com/Khopa/dcs_liberation/wiki/)
## Special Thanks
First, a big thanks to shdwp, for starting the original DCS Liberation project.
Then, DCS Liberation uses [pydcs](http://github.com/pydcs/dcs) for mission generation, and nothing would be possible without this.
It also uses the popular [Mist](https://github.com/mrSkortch/MissionScriptingTools) lua framework for mission scripting.
Excellent lua scripts DCS Liberation uses as plugins:
* For the JTAC feature, DCS Liberation embeds Ciribob's JTAC Autolase [script](https://github.com/ciribob/DCS-JTACAutoLaze).
* Walder's [Skynet-IADS](https://github.com/walder/Skynet-IADS) is used for Integrated Air Defense System.
Please also show some support to these projects !

View File

@@ -1,65 +0,0 @@
#!/usr/bin/env python3
import os
import dcs
import theater.caucasus
import theater.persiangulf
import theater.nevada
import ui.window
import ui.mainmenu
import ui.newgamemenu
import ui.corruptedsavemenu
from game.game import Game
from theater import start_generator
from userdata import persistency
dcs.planes.FlyingType.payload_dirs.append(os.path.join(os.path.dirname(os.path.realpath(__file__)), "resources\\payloads"))
def proceed_to_main_menu(game: Game):
m = ui.mainmenu.MainMenu(w, None, game)
m.display()
w = ui.window.Window()
try:
game = persistency.restore_game()
if not game:
new_game_menu = None # type: NewGameMenu
def start_new_game(player_name: str, enemy_name: str, terrain: str, sams: bool, midgame: bool, multiplier: float):
if terrain == "persiangulf":
conflicttheater = theater.persiangulf.PersianGulfTheater()
elif terrain == "nevada":
conflicttheater = theater.nevada.NevadaTheater()
else:
conflicttheater = theater.caucasus.CaucasusTheater()
if midgame:
for i in range(0, int(len(conflicttheater.controlpoints) / 2)):
conflicttheater.controlpoints[i].captured = True
start_generator.generate_initial(conflicttheater, enemy_name, sams, multiplier)
game = Game(player_name=player_name,
enemy_name=enemy_name,
theater=conflicttheater)
game.budget = int(game.budget * multiplier)
game.settings.multiplier = multiplier
game.settings.sams = sams
if midgame:
game.budget = game.budget * 4 * len(list(conflicttheater.conflicts()))
proceed_to_main_menu(game)
new_game_menu = ui.newgamemenu.NewGameMenu(w, start_new_game)
new_game_menu.display()
else:
proceed_to_main_menu(game)
except Exception as e:
ui.corruptedsavemenu.CorruptedSaveMenu(w).display()
w.run()

599
changelog.md Normal file
View File

@@ -0,0 +1,599 @@
# 2.5.1
## Features/Improvements
* **[UI]** Engagement ranges are now displayed by default.
* **[UI]** Engagement range display generalized to work for all patrolling flight plans (BARCAP, TARCAP, and CAS).
* **[Flight Planner]** Front lines no longer project threat zones to avoid pushing BARCAPs back so much. TARCAPs will be forcibly planned but strike packages will not route around front lines even if it is reasonable to do so.
## Fixes
* **[Campaigns]** EWRs associated with a base will now only be generated near the base.
* **[Flight Planner]** Fixed error when generating AEW&C flight plans in campaigns with no front lines.
# 2.5.0
Saves from 2.4 are not compatible with 2.5.
## Features/Improvements
* **[Engine]** DCS 2.7 Support
* **[UI]** Improved FOB menu, added a custom banner, and do not display aircraft recruitment menu
* **[Flight Planner]** Added AEW&C missions. (by siKruger)
* **[Kneeboard]** Added dark kneeboard option (by GvonH)
* **[Campaigns]** Multiple EWR sites may now be generated, and EWR sites may be generated outside bases (by SnappyComebacks)
* **[Mission Generation]** Cloudy and rainy (but not thunderstorm) weather will use the cloud presets from DCS 2.7.
* **[Plugins]** Added LotATC export plugin (by drsoran)
* **[Plugins]** Added Splash Damage Plugin (by Wheelijoe)
* **[Loadouts]** Replaced Litening with ATFLIR for all default F/A-18C loadouts.
## Fixes
* **[Flight Planner]** Front lines now project threat zones, so TARCAP/escorts will not be pruned for flights near the front. Packages may also route around the front line when practical.
* **[Flight Planner]** Fixed error when planning BAI at SAMs with dead subgroups.
* **[Flight Planner]** Mig-19 was not allowed for CAS roles fixed
* **[Flight Planner]** Increased size of navigation planning area to avoid plannign failures with distant waypoints.
* **[Flight Planner]** Fixed UI refresh when unchecking the "default loadout" box in the loadout editor.
* **[Objective names]** Fixed typos in objective name : ARMADILLLO -> ARMADILLO (by SnappyComebacks)
* **[Payloads]** F-86 Sabre was missing a custom payload
* **[Payloads]** Added GAR-8 period restrictions (by Mustang-25)
* **[Campaign]** Date now progresses.
* **[Campaign]** Added game over message when a coalition runs out of functioning airbases.
* **[Mission Generation]** Fixed "invalid face handle" error in kneeboard generation that occurred on some machines.
## Regressions
* **[Mod Support]** Stopped support for 2.5.5 Rafale Mode, and removed factions that were using it
* **[Mod Support]** Su-57 mod support might be out of date
# 2.4.3
## Features/Improvements
* **[New Game Wizard]** Added the possibility to setup custom start date
## Fixes
* **[Mods]** Updated C-130J mod data to version 6.4
* **[Mods]** Updated F-22A mod to latest version
# 2.4.2
## Features/Improvements
* **[Factions]** Introduction dates and fallback weapons added for US, Russian, UK, and French weapons. Huge thanks to @TheCandianVendingMachine for the massive amount of data entry!
* **[Campaigns]** Added 1995 start dates.
## Fixes
* **[Economy]** Pending ground unit purchases will also be transferred when a connected base is captured.
* **[UI]** Fixed rounding of budget in recruitment menu.
# 2.4.1
## Fixes
* **[Units]** Fixed syntax error with the SH-60B payload file.
* **[Culling]** Missile sites generate reasonably sized non-cull zones rather than 100km ones.
* **[UI]** Budget display is also now rounded to 2 decimal places.
* **[UI]** Fixed some areas where the old, non-pretty name was displayed to users.
# 2.4.0
Saves from 2.3 are not compatible with 2.4.
## Highlights
* Improved flight plan generation to avoid loitering in or traveling through threatened areas when practical.
* Improved AI aircraft purchasing behavior.
* Era-restricted weapons (work in progress).
* Tons of UI polish.
* Rebalanced economy to keep opfor competitive over the course of the game.
## Features/Improvements
* **[Flight Planner]** Air-to-air and SEAD escorts will no longer be automatically planned for packages that are not in range of threats.
* **[Flight Planner]** Non-custom flight plans will now navigate around threat areas en route to the target area when practical.
* **[Flight Planner]** Flight plans along front lines now ensure that the race track start is closer to the departure airfield than the race track end.
* **[Campaign AI]** Auto-purchase now prefers airfields that are not within range of the enemy.
* **[Campaign AI]** Auto-purchase now prefers the best aircraft for the task, but will attempt to maintain some variety.
* **[Campaign AI]** Opfor now sells off odd aircraft since they're unlikely to be used.
* **[Campaign AI]** Multiple rounds of CAP will be planned (roughly 90 minutes of coverage). Default starting budget has increased to account for the increased need for aircraft.
* **[Mission Generator]** Multiple groups are created for complex SAM sites (SAMs with additional point defense or SHORADS), improving Skynet behavior.
* **[Mission Generator]** Default start type can now be chosen in the settings. This replaces the non-functional "AI Parking Start" option. **Selecting any type other than cold will break OCA/Aircraft missions.**
* **[Cheat Menu]** Added ability to toggle base capture and frontline advance/retreat cheats.
* **[Skynet]** Updated to 2.0.1.
* **[Skynet]** Point defenses are now configured to remain on to protect the site they accompany.
* **[Hercules]** Updated the Hercules Cargo list file.
* **[Balance]** Opfor now gains income using the same rules as the player, significantly increasing their income relative to the player for most campaigns.
* **[Balance]** Units now retreat from captured bases when able. Units with no retreat path will be captured and sold.
* **[Economy]** FOBs generate only $10M per turn (previously $20M like airbases).
* **[Economy]** Carriers and off-map spawns generate no income (previously $20M like airbases).
* **[Economy]** Sales of aircraft and ground vehicles can now be cancelled before the next turn begins.
* **[UI]** Multi-SAM objectives now show threat and detection rings per group.
* **[UI]** New icon for AA sites with no active threat.
* **[UI]** Unit names are now prettier and more accurate, and can now be set per-country for added historical flavour.
* **[UI]** Default loadout is now shown for flights with no custom loadout selected.
* **[UI]** Aircraft for a new flight are now only selectable if they match the task type for that flight.
* **[UI]** WIP - There is now a unit info button for each unit in the recruitment list, that should help newer players learn what each unit does.
* **[UI]** Docs for time-on-target and creating new theaters/factions/loadouts are now linked in the UI at the appropriate places.
* **[UI]** ASAP is now a checkbox rather than a button. Enabling this will disable the TOT selector but changes to the package structure will automatically re-ASAP the package.
* **[UI]** Arrival airfield is now shown in the flight list if it differs from the departure airfield.
* **[UI]** Start type can now be selected when creating a flight.
* **[UI]** Arrival and divert airfields can be edited after the flight is created.
* **[Factions]** Added option for date-based loadout restriction. Active radar homing missiles are handled, patches welcome for the other thousand weapons.
* **[Factions]** Added Poland 2010 faction.
* **[Factions]** Added Greece 2005 faction.
* **[Factions]** Added Iran 1988 faction.
* **[Units]** Support for E-2 Hawkeye, SH-60B Seahawk, S-3B Viking (thanks to awinterquest) and SpGH Dana - these are now being used by appropriate factions.
* **[Culling]** Missile sites are no longer culled.
* **[Campaigns]** Added campaign "Black Sea Lite" by Starfire
* **[Campaigns]** Added campaign "Exercise Vegas Nerve" by Starfire
* **[New game Wizard]** The theater page is now the first page of the campaign wizard, recommended factions will be selected automatically on the faction selection page
* **[New game Wizard]** Added information text about the selected campaign performance.
* **[Mod Support]** Added support for High Digit SAMs mod 1.4.0
* **[Mod Support]** Added SAMs sites generator : KS19Generator, SA10BGenerator, SA12Generator, SA17Generator, SA20Generator, SA20BGenerator, SA23Generator
## Fixes
* **[Hercules]** Updated the default Hercules radio frequency.
* **[Economy]** Pending unit orders at captured bases will be refunded.
* **[UI]** Carrier group SAM threat rings now move with the carrier.
* **[UI]** Base intel menu no longer compresses text, and is now scrollable.
* **[UI]** Edit Flight window is now dynamically sized to adapt to the width of waypoint names, so they no longer get truncated.
* **[UI]** Budget income display is now rounded to 2 decimal places.
* **[UI]** Fixed incorrect income per turn displayed for strike target tooltip.
* **[Factions]** USA with C-130 faction now links to the required mod.
* **[Campaign]** Fixed issue where destroyed buildings would sometimes not count as destroyed and thus respawn.
* **[Campaign]** Fixed issue where destroyed runways were not registered.
* **[Units]** J-11A is no longer spawned with empty loadout.
* **[Units]** F-14B is no longer spawned with empty loadout for fighter sweep tasks.
* **[Units]** Pyotr Velikiy cruiser has been removed for now as it's nearly unkillable.
* **[Units]** Submarines have been removed for now as they aren't wholly functional.
* **[Units]** Fixed "FACTION ERROR : Unable to find OliverHazardPerryGroupGenerator in pydcs" error at startup.
* **[Mission Generator]** Fixed a bug where units set to Aggressive stance sometimes did not move.
* **[Mission Generator]** Flyover points for OCA/Aircraft missions are now generated correctly.
* **[Flight Planner]** Fixed not being able to create custom waypoints for buildings.
* **[Flight Planner]** Strike missions will no longer be automatically planned against SAMs.
* **[Flight Planner]** Strike missions will no longer be automatically planned against FOB structures.
# 2.3.4
## Fixes:
[Mission Generator] Mission generator would crash when generating fire missions for destroyed SCUD sites - fixed
# 2.3.3
## Features/Improvements
* **[Campaigns]** Reworked Golan Heights campaign on Syria, (Added FOB and preset locations for SAMS)
* **[Campaigns]** Added a lite version of the Golan Heights campaign
* **[Campaigns]** Reworked Syrian Civil War campaign (Added FOB and preset locations for SAMS)
* **[Campaigns]** Reworked Emirates campaign
* **[Campaigns]** AA units added to frontlines and updated all factions to include some frontline AA units.
* **[Mission Generator]** Infantry will only be generated for APC and IFV groups
* **[Mission Generator]** Infantry squads size is not randomized anymore
* **[Mission Generator]** Infantry squads can have a mortar.
* **[Mission Generator]** SCUD missiles sites will now fire on enemy controls points in range when possible
* **[Factions]** Updated Nato Desert Storm to include F-14A
* **[Factions]** Updated Iraq 1991 factions to include Zsu-57 and Mig-29A
* **[Factions]** Germany 1944, added Stug III and Stug IV
* **[Factions]** Added factions Insurgents (Hard) with better and more weapons
* **[Plugins]** [The EWRS plugin](https://github.com/Bob7heBuilder/EWRS) is now included.
* **[UI]** Added enemy intelligence summary and details window.
## Fixes:
* **[Factions]** AI would never buy artillery units for the frontline - fixed
* **[Factions]** Removed the F-111 unit from the NATO desert storm faction. (Recruiting it would cause crashes in DCS, since it is not a valid unit)
* **[Campaign]** Automatic redeployment of ground units would sometimes fail - fixed
* **[Mission Generator]** Artillery groups would retreat in the wrong direction - fixed
* **[Units]** Fixed SPG_Stryker_M1128_MGS not being in db
* **[UI]** Fixed and added many missing ground units icons
* **[UI]** Ship groups could be replaced by SAM sites in the UI, which would lead to broken mission being generated - fixed
* **[New Game Wizard]** Removed the "mid game" campaign generator option which is currently broken
* **[Mission Generator]** Empty navy groups will no longer be generated
* **[Mission Generator]** Fixed BAI, SEAD, and DEAD flights ocassionally being assigned the wrong targets.
* **[Flight Planner]** Fixed not being able to plan packages against opfor carriers
* **[UI]** Repaired SAMs no longer show as dead.
* **[UI]** Fixed not being able to manage a disbanded site after disbanding and closing the base menu.
# 2.3.2
## Features/Improvements
* **[Units]** Support for newly added BTR-82A, T-72B3
* **[Units]** Added ZSU-57 AAA sites
* **[Culling]** BARCAP missions no longer create culling exclusion zones.
* **[Flight Planner]** Improved TOT planning. Negative start times no longer occur with TARCAPs and hold times no longer affect planning for flight plans without hold points.
* **[Factions]** Added Iraq 1991 faction (thanks again to Hawkmoon!)
## Fixes:
* **[Mission Generator]** Fix mission generation error when there are too many radio frequency to setup for the Mig-21
* **[Mission Generator]** Fix ground units not moving forward
* **[Mission Generator]** Fixed assigned radio channels overlapping with beacons.
* **[Flight Planner]** Fix creation of custom waypoints.
* **[Campaigns]** Fixed many cases of SAMs spawning on the runways/taxiways in Syria Full.
# 2.3.1
## Features/Improvements
* **[UX]** Added a warning message when the player is attempting to buy more planes at an already full airbase.
* **[Campaigns]** Migrated Syria full map to new format. (Thanks to Hawkmoon)
* **[Faction]** Added NATO desert Storm faction (Thanks to Hawkmoon)
## Fixes:
* **[AI]** CAP flights will engage enemies again.
* **[Campaigns]** Fixed a missing path on the Caucasus Full Map campaign
# 2.3.0
## Features/Improvements
* **[Campaign Map]** Overhauled the campaign model
* **[Campaign Map]** Possible to add FOB as control points
* **[Campaign Map]** Added off-map spawn locations
* **[Campaign AI]** Overhauled AI recruiting behaviour
* **[Campaign AI]** Added AI procurement for Blue
* **[Campaign]** New Campaign: "Black Sea"
* **[Mission Planner]** Possible to move carrier and tarawa on the campaign map
* **[Mission Generator]** Infantry squads on frontline can have manpads
* **[Mission Generator]** Unused aircraft now spawned to allow for OCA strikes
* **[Mission Generator]** Opfor now obeys parking limits
* **[Mission Generator]** Support for Anubis C-130 Hercules mod
* **[Flight Planner]** Added fighter sweep missions.
* **[Flight Planner]** Added BAI missions.
* **[Flight Planner]** Added anti-ship missions.
* **[Flight Planner]** Differentiated BARCAP and TARCAP. TARCAP is now for hostile areas and will arrive before the package.
* **[Flight Planner]** Added OCA missions
* **[Flight Planner]** Added Alternate/divert airfields
* **[Culling]** Added possibility to include/exclude carriers from culling zones
* **[QOL]** On liberation startup, your latest save game is loaded automatically
* **[Units]** Reduced starting fuel load for C101
* **[UI]** Inform the user of the weather
* **[UI]** Added toolbar buttons to change map display settings
* **[Game]** Added new Economy options for adjusting income multipliers and starting budgets.
## Fixes :
* **[Map]** Missiles sites now have a proper icon and will not re-use the SAM sites icon
* **[Mission Generator]** Ground unit waypoints improperly set to "On Road" - fixed
* **[Mission Generator]** Target waypoints not at ground level - fixed
* **[Mission Generator]** Selected skill not applied to Helicopters - fixed
* **[Mission Generator]** Ground units do not always spawn - fixed
* **[Kneeboard]** Briefing waypoints off by one - fixed
* **[Game]** Destroyed buildings still granting budget - fixed
# 2.2.1
## Features/Improvements
* **[Factions]** Added factions : Georgia 2008, USN 1985, France 2005 Frenchpack by HerrTom
* **[Factions]** Added map Persian Gulf full by Plob
* **[Flight Planner]** Player flights with start delays under ten minutes will spawn immediately.
* **[UI]** Mission start screen now informs players about delayed flights.
* **[Units]** Added support for F-14A-135-GR
* **[Modding]** Possible to setup liveries overrides in factions definition files
## Fixes :
* **[Flight Planner]** Hold, join, and split points are planned cautiously near enemy airfields. Ascend/descend points are no longer planned.
* **[Flight Planner]** Custom waypoints are usable again. Not that in most cases custom flight plans will revert to the 2.1 flight planning behavior.
* **[Flight Planner]** Fixed UI bug that made it possible to create empty flights which would throw an error.
* **[Flight Planner]** Player flights from carriers will now be delayed correctly according to the player's settings.
* **[Misc]** Spitfire variant with clipped wings was not seen as flyable by DCS Liberation (hence could not be setup as client/player slot)
* **[Misc]** Updated Syria terrain parking slots database, the out-of-date database could end up generating aircraft in wrong slots (We are still experiencing issues with somes airbases, such as Khalkhalah though)
# 2.2.0
## Features/Improvements :
* **[Campaign Generator]** Added early warning radar generation
* **[Campaign Generator]** Added scud launcher sites
* **[Cheat Menu]** Added ability to capture base from mission planner
* **[Cheat Menu]** Added ability to show red ATO
* **[Factions]** Added WW2 factions that do not depend on WW2 asset pack
* **[Factions]** Cold War / Middle eastern factions will use Flak sites
* **[Flight Planner]** Flight planner overhaul, with package and TOT system
* **[Flight Planner]** Pick runways and ascent/descent based on headwind
* **[Map]** Added polygon debug mode display
* **[Map]** Highlight the selected flight path on the map
* **[Map]** Improved SAM display settings
* **[Map]** Improved flight plan display settings
* **[Map]** Caucasus and The Channel map use a new system to generate SAM and strike target location to reduce probability of targets generated in the middle of a forests
* **[Misc]** Flexible Dedicated Hosting Options for Mission Files via environment variables
* **[Moddability]** Custom campaigns can be designed through json files
* **[Moddability]** LUA plugins can now be injected into Liberation missions.
* **[Moddability]** Optional Skynet IADS lua plugin now included
* **[New Game]** Starting budget can be freely selected
* **[New Game]** Exanded information for faction and campaign selection in the new game wizard
* **[UI]** Add double and right click actions to many UI elements.
* **[UI]** Add polygon drawing mode for map background
* **[UI]** Added a warning if you press takeoff with no player enabled flights
* **[UI]** Packages and flights now visible in the main window sidebar
* **[Units/Factions]** Added bombers to some coalitions
* **[Units/Factions]** Added support for SU-57 mod by Cubanace
* **[Units]** Added Freya EWR sites to german WW2 factions
* **[Units]** Added support for many bombers (B-52H, B-1B, Tu-22, Tu-142)
* **[Units]** Added support for new P-47 variants
## Fixes :
* **[Campaign Generator]** Big airbases could end up without any airbase defense.
* **[Campaign generator]** Ship group and offshore buildings should not be generated on land anymore
* **[Flight Planner]** Fix waypoint alitudes for helicopters
* **[Flight Planner]** Fixed CAS aircraft wandering away from frontline
* **[Maps]** Incirlik airbase was missing exclusions zones, so SAMS could end up being generated on the runway
* **[Mission Generator]** Fixed player/client confusion when a flight had only one player slot.
* **[Radios]** Fix A-10C radio
* **[UI]** Many missing unit icons were added
* **[UI]** Missing TER weapons in custom payload now selectable.
# 2.1.5
## Features/Improvements :
* **[Units/Factions]** Enabled EPLRS for ground units that supports it (so they appear on A-10C II TAD and Helmet)
## Fixes :
* **[UI]** Fixed an issue that prevent saving after aborting a mission
* **[Mission Generator]** Fixed aircraft landing point type being wrong
# 2.1.4
## Fixes :
* **[UI]** Fixed an issue that prevented generating the mission (take off button no working) on old savegames.
## Features/Improvements :
* **[Units/Factions]** Added A-10C_2 to USA 2005 and Bluefor modern factions
* **[UI]** Limit number of aircraft that can be bought to the number of available parking slots.
* **[Mission Generator]** Use inline loading of the JSON.lua library, and save to either %LIBERATION_EXPORT_DIR%, or to DCS working directory
## Changes :
* **[Units/Factions]** Bluefor generic factions will now use the new "Combined Joint Task Forces Blue" country in the generated mission instead of "USA"
## Fixes :
* **[UI]** Fixed icon for Viggen
* **[UI]** Added icons for some ground units
* **[Misc]** Fixed issue with Chinese characters in pydcs preventing generating the mission. (Take Off button not working) (thanks to spark135246)
* **[Misc]** Fixed an error causing with ATC frequency preventing generating the mission. (Take Off button not working) (thanks to danalbert)
# 2.1.2
## Fixes :
* **[Mission Generator]** Fix mission generation issues with radio frequencies (Thanks to contributors davidp57 and danalbert)
* **[Mission Generator]** AI should now properly plan flights for Tornados
# 2.1.1
## Features/Improvements :
* **[Other]** Added an installer option (thanks to contributor parithon)
* **[Kneeboards]** Generate mission kneeboards for player flights. Kneeboards include
airfield/carrier information (ATC frequencies, ILS, TACAN, and runway
assignments), assigned radio channels, waypoint lists, and AWACS/JTAC/tanker
information. (Thanks to contributor danalbert)
* **[Radios]** Allocate separate intra-flight channels for most aircraft to reduce global
chatter. (Thanks to contributor danalbert)
* **[Radios]** Configure radio channel presets for most aircraft. Currently supported are:
* AJS37
* AV-8B
* F-14B
* F-16C
* F/A-18C
* JF-17
* M-2000C (Thanks to contributor danalbert)
* **[Base Menu]** Added possibility to repair destroyed SAM and base defenses units for the player (Click on a SAM site to fix it)
* **[Base Menu]** Added possibility to buy/sell/replace SAM units
* **[Map]** Added recon images for buildings on strike targets, click on a Strike target to get detailled informations
* **[Units/Factions]** Added F-16C to USA 1990
* **[Units/Factions]** Added MQ-9 Reaper as CAS unit for USA 2005
* **[Units/Factions]** Added Mig-21, Mig-23, SA-342L to Syria 2011
* **[Cheat Menu]** Added buttons to remove money
## Fixed issues :
* **[UI/UX]** Spelling issues (Thanks to contributor steveveepee)
* **[Campaign Generator]** LHA was placed on land in Syrian Civil War campaign
* **[Campaign Generator]** Fixed inverted configuration for Syria full map
* **[Campaign Generator]** Syria "Inherent Resolve" campaign, added Incirlik Air Base
* **[Mission Generator]** AH-1W was not used by AI to generate CAS mission by default
* **[Mission Generator]** Fixed F-16C targeting pod not being added to payload
* **[Mission Generator]** AH-64A and AH-64D payloads fix.
* **[Units/Factions]** China will use KJ-2000 as awacs instead of A-50
# 2.1.0
## Features/Improvements :
* **[Campaign Generator]** Added Syria map
* **[Campaign Generator]** Added 5 campaigns for the Syria map
* **[Campaign Generator]** Added 2 small scale campaign for Persian Gulf map
* **[Units/Factions]** Added factions for Syria map : Syria 2011, Arab Armies 1982, 1973, 1968, 1948, Israel 1982, 1973, 1948
* **[Base Menu]** Budget is visible in recruitment menu. (Thanks to Github contributor root0fall)
* **[Misc]** Added error message in mission when the state file can not be written
* **[Units/Factions]** China, Pakistan, UAE will now use the new WingLoong drone as JTAC instead of the MQ-9 Reaper
* **[Units/Factions]** Minor changes to Syria 2011 and Turkey 2005 factions
* **[UI]** Version number is shown in about dialog
## Fixed issues :
* **[Mission Generator]** Caucasus terrain improvement on exclusions zone (added forests between Vaziani and Beslan to exclusion zones)
* **[Mission Generator]** The first unit of every base defenses group could not be controlled with Combined Arms.
* **[Mission Generator]** Reduced generated helicopter altitude for CAS missions
* **[Mission Generator]** F-16C default CAS payload was asymmetric, fixed.
* **[Mission Generator]** AH-1W couldn't be bought, and added default payloads.
* **[UI/UX]** Fixed Mi-28N missing thumbnail
* **[UI/UX]** Fixed list of flights not refreshing when changing the mission departure (T+).
# 2.0.11
## Features/Improvements :
* **[Units/Factions]** Added Mig-31, Su-30, Mi-24V, Mi-28N to Russia 2010 faction.
* **[Units/Factions]** Added F-15E to USA 2005 and USA 1990 factions.
* **[Mission Generator]** Added a parameter to choose whether the JTACs should use smoke markers or not
## Fixed issues :
* **[Units/Factions]** Fixed big performance issue in new release UI that occurred only when running the .exe
* **[Units/Factions]** Fixed mission generation not working with Libya faction
* **[Units/Factions]** Fixed OH-58D not being used by AI
* **[Units/Factions]** Typo in UK 1990 name (fixed by bwRavencl)
* **[Units/Factions]** Fixed Tanker Tacan channel not being the same as the briefing one. (Sorry)
* **[Mission Generator]** Neutral airbases services will now be disabled. (Not possible to refuel or re-arm there)
* **[Mission Generator]** AI will be configured to limit afterburner usage
* **[Mission Generator]** JTAC will not use laser codes above 1688 anymore
* **[Mission Generator]** JTAC units were misconfigured and would not be invisible/immortal.
* **[Mission Generator]** Increased JTAC status message duration to 25s, so you have more time to enter coordinates;
* **[Mission Generator]** Destroyed units carcass will not appear on airfields to avoid having a destroyed vehicle blocking a runway or taxiway.
# 2.0.10
## Features/Improvements :
* **[Misc]** Now possible to save game in a different file, and to open DCS Liberation savegame files. (You are not restricted to a single save file anymore)
* **[UI/UX]** New dark UI Theme and default theme improvement by Deus
* **[UI/UX]** New "satellite" map backgrounds
* **[UX]** Base menu is opened with a single mouse click
* **[Units/Factions/Mods]** Added Community A-4E-C support for faction Bluefor Cold War
* **[Units/Factions/Mods]** Added MB-339PAN support for faction Bluefor Cold War
* **[Units/Factions/Mods]** Added Rafale AI mod support
* **[Units/Factions/Mods]** Added faction "France Modded" with units from frenchpack v3.5 mod
* **[Units/Factions/Mods]** Added faction "Insurgent modded" with Insurgent units from frenchpack v3.5 mod (Toyota truck)
* **[Units/Factions/Mods]** Added factions Canada 2005, Australia 2005, Japan 2005, USA Aggressors, PMC
* **[New Game Wizard]** Added the list of required mods for modded factions.
* **[New Game Wizard]** No more RED vs BLUE opposing faction restrictions.
* **[New Game Wizard]** New campaign generation settings added : No aircraft carrier, no lha, no navy, invert map starting positions.
* **[Mission Generator]** Artillery units will start firing mission after a random delay. It should reduces lag spikes induced by artillery strikes by spreading them out.
* **[Mission Generator]** Ground units will retreat after taking too much casualties. Artillery units will retreat if engaged.
* **[Mission Generator]** The briefing will now contain the carrier ATC frequency
* **[Mission Generator]** The briefing contains a small situation update.
* **[Mission Generator]** Previously destroyed units are visible in the mission. (And added a performance settings to disable this behaviour)
* **[Mission Generator]*c* Basic JTAC on Frontlines
* **[Campaign Generator]** Added Tarawa in caucasus campaigns
* **[Campaign Generator]** Tuned the various existing campaign parameters
* **[Campaign Generator]** Added small campaign : "Russia" on Caucasus Theater
## Fixed issues :
* **[Mission Generator]** Carrier will sail into the wind, not in the same direction
* **[Mission Generator]** Carrier cold start was not working (flight was starting warm even when cold was selected)
* **[Mission Generator]** Carrier group ships are more spread out
* **[Mission Generator]** Fixed wrong radio frequency for german WW2 warbirds
* **[Mission Generator]** Fixed FW-190A8 spawning with bomb rack for CAP missions
* **[Mission Generator]** Fixed A-20G spawning with no payload
* **[Mission Generator]** Fixed Su-33 spawning too heavy to take off from carrier
* **[Mission Generator]** Fixed Harrier AV-8B spawning too heavy to take off from tarawa
* **[Mission Generator]** Base defense units were not controllable with Combined Arms
* **[Mission Generator]** Tanker speed was too low
* **[Mission Generator]** Tanker TACAN settings were wrong
* **[Mission Generator]** AI aircraft should start datalink ON (EPLRS)
* **[Mission Generator]** Base defense units should not spawn on runway and or taxyway. (The chance for this to happen should now be really really low)
* **[Mission Generator]** Fixed all flights starting "In flight" after playing a few missions (parking slot reset issue)
* **[Mission Script/Performance]** Mission lua script will not listen to weapons fired event anymore and register every fired weapons. This should improve performance especially in WW2 scenarios or when rocket artillery is firing.
* **[Campaign Generator]** Carrier name will now not appear for faction who do not have carriers
* **[Campaign Generator]** SA-10 sites will now have a tracking radar.
* **[Units/Factions]** Remove JF-17 from USA 2005 faction
* **[Units/Factions]** Remove AJS-37 from Russia 2010
* **[Units/Factions]** Removed Oliver Hazard Perry from cold war factions (too powerful sam system for the era)
* **[Bug]** On the persian gulf full map campaign, the two carriers were sharing the same id, this was causing a lot of bugs
* **[Performance]** Tuned the culling setting so that you cannot run into situation where no friendly or enemy AI flights are generated
* **[Other]** Application doesn't gracefully exit.
* **[Other]** Other minor fixes, and multiples factions small changes
# 2.0 RC 9
## Features/Improvements :
* **[UI/UX]** New icons from contributor Deus
## Fixed issues :
* **[Mission Generator]** Carrier TACAN was wrongfully set up as an A/A TACAN
* **[Campaign Generator]** Fixed issue with Russian navy group generator causing a random crash on campaign creation.
# 2.0 RC 8
## Fixed issues :
* **[Mission Generator]** Frequency for P-47D-30 changed to 124Mhz (Generated mission with 251Mhz would not work)
* **[Mission Generator]** Reduced the maximum number of uboat per generated group
* **[Mission Generator]** Fixed an issue with the WW2 LST groups (superposed units).
* **[UI]** Fixed issue with the zoom
# 2.0 RC 7
## Features/Improvements :
* **[Units/Factions]** Added P-47D-30 for factions allies_1944
* **[Units/Factions]** Added factions : Bluefor Coldwar, Germany 1944 Easy
* **[Campaign/Map]** Added a campaign in the Channel map
* **[Campaign/Map]** Changed the Normandy campaign map
* **[Campaign/Map]** Added new campaign Normandy Small
* **[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 (With a new setting in Settings Window to disable it)
* **[Mission Generator]** Tacan for carrier will only be in Mode X from now
* **[Mission Generator]** RTB waypoints for autogenerated flights
* **[Flight Planner]** Added CAS mission generator
* **[Flight Planner]** Added CAP mission generator
* **[Flight Planner]** Added SEAD mission generator
* **[Flight Planner]** Added STRIKE mission generator
* **[Flight Planner]** Added buttons to add autogenerated waypoints (ASCEND, DESCEND, RTB)
* **[Flight Planner]** Improved waypoint list
* **[Flight Planner]** WW2 factions uses different parameters for flight planning.
* **[Settings]** Added settings to disallow external views
* **[Settings]** Added settings to choose F10 Map mode (All, Allies only, Player only, Fog of War, Map Only)
* **[Settings]** Added settings to choose whether to auto-generate objective marks on the F10 map
* **[Info Panel]** Added information about destroyed buildings in info panel
* **[Info Panel]** Added information about destroyed units at SAM site in info panel
* **[Debriefing]** Added information about units destroyed outside the frontline in the debriefing window
* **[Debriefing]** Added destroyed buildings in the debriefing window
* **[Map]** Tooltip now contains the list of building for Strike targets on the map
* **[Map]** Added "Oil derrick" building
* **[Map]** Added "ww2 bunker" building (WW2)
* **[Map]** Added "ally camp" building (WW2)
* **[Map]** Added "V1 Site" (WW2)
* **[Misc]** Made it possible to setup DCS Saved Games directory and DCS installation directory manually at first start
* **[Misc]** Added culling performance settings
## Fixed issues :
* **[Units/Factions]** Replaced S3-B Tanker by KC130 for most factions (More fuel)
* **[Units/Factions]** WW2 factions will not have offshore oil station and other modern buildings generated. No more third-reich operated offshore stations will spawn on normandy's coast.
* **[Units/Factions]** Aircraft carrier will try to move in the wind direction
* **[Units/Factions]** Missing icons added for some aircraft
* **[Mission Generator]** When playing as RED the activation trigger would not be properly generated
* **[Mission Generator]** FW-190A8 is now properly considered as a flyable aircraft
* **[Mission Generator]** Changed "strike" payload for Su-24M that was ineffective
* **[Mission Generator]** Changed "strike" payload for JF-17 to use LS-6 bombs instead of GBU
* **[Mission Generator]** Change power station template. (Buildings could end up superposed).
* **[Maps/Campaign]** Now using Vasiani airbase instead of Soganlung airport in Caucasus campaigns (more parking slot)
* **[Info Panel]** Message displayed on base capture event stated that the enemy captured an airbase, while it was the player who captured it.
* **[Map View]** Graphical glitch on map when one building of an objective was destroyed, but not the others
* **[Mission Planner]** The list of flights was not updated on departure time change.
# 2.0 RC 6
Saves file from RC5 are not compatible with the new version.
Sorry :(
## Features/Improvements :
* **[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]** Factions US 2005 / 1990 will now sometimes have Arleigh Burke class ships instead of Perry as carrier escorts
* **[Units/Factions]** Added support for newest WW2 Units
* **[Campaign logic]** When a base is captured, refill the "base defenses" group with units for the new owner.
* **[Mission Generator]** Carrier ICLS channel will now be configured (check your briefing)
* **[Mission Generator]** SAM units will spawn on RED Alarm state
* **[Mission Generator]** AI Flight planner now creates its own STRIKE flights
* **[Mission Generator]** AI units assigned to Strike flight will now actually engage the buildings they have been assigned.
* **[Mission Generator]** Added performance settings to allow disabling : smoke, artillery strike, moving units, infantry, SAM Red alert mode.
* **[Mission Generator]** Using Late Activation & Trigger in attempt to improve performance & reduce stutter (Previously they were spawned through 'ETA' feature)
* **[UX]** : Improved flight selection behaviour in the Mission Planning Window
## Fixed issues :
* **[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 the player selected faction has no AWACS
* **[Mission Generator]** Planned flights will spawn even if their home base has been captured or is being contested by enemy ground units.
* **[Campaign Generator]** Base defenses would not be generated on Normandy map and in some rare cases on others maps as well
* **[Mission Planning]** CAS waypoints created from the "Predefined waypoint selector" would not be at the exact location of the frontline
* **[Naming]** CAP mission flown from airbase are not named BARCAP anymore (CAP from carrier is still named BARCAP)

1
dcs
View File

@@ -1 +0,0 @@
submodules/dcs/dcs

View File

@@ -1,2 +1,3 @@
from .game import Game
from . import db
from . import db
from .version import VERSION

22
game/data/aaa_db.py Normal file
View File

@@ -0,0 +1,22 @@
from dcs.vehicles import AirDefence
AAA_UNITS = [
AirDefence.SPAAA_Gepard,
AirDefence.SPAAA_ZSU_23_4_Shilka_Gun_Dish,
AirDefence.SPAAA_Vulcan_M163,
AirDefence.AAA_ZU_23_Closed_Emplacement,
AirDefence.AAA_ZU_23_Emplacement,
AirDefence.SPAAA_ZU_23_2_Mounted_Ural_375,
AirDefence.AAA_ZU_23_Closed_Emplacement_Insurgent,
AirDefence.SPAAA_ZU_23_2_Insurgent_Mounted_Ural_375,
AirDefence.AAA_ZU_23_Insurgent,
AirDefence.AAA_8_8cm_Flak_18,
AirDefence.AAA_Flak_38_20mm,
AirDefence.AAA_8_8cm_Flak_36,
AirDefence.AAA_8_8cm_Flak_37,
AirDefence.AAA_Flak_Vierling_38_Quad_20mm,
AirDefence.AAA_SP_Kdo_G_40,
AirDefence.AAA_8_8cm_Flak_41,
AirDefence.AAA_40mm_Bofors,
AirDefence.AAA_S_60_57mm,
]

View File

@@ -0,0 +1,70 @@
import inspect
import dcs
DEFAULT_AVAILABLE_BUILDINGS = [
"fuel",
"ammo",
"comms",
"oil",
"ware",
"farp",
"fob",
"power",
"factory",
"derrick",
]
WW2_FREE = ["fuel", "factory", "ware", "fob"]
WW2_GERMANY_BUILDINGS = [
"fuel",
"factory",
"ww2bunker",
"ww2bunker",
"ww2bunker",
"allycamp",
"allycamp",
"fob",
]
WW2_ALLIES_BUILDINGS = [
"fuel",
"factory",
"allycamp",
"allycamp",
"allycamp",
"allycamp",
"allycamp",
"fob",
]
FORTIFICATION_BUILDINGS = [
"Siegfried Line",
"Concertina wire",
"Concertina Wire",
"Czech hedgehogs 1",
"Czech hedgehogs 2",
"Dragonteeth 1",
"Dragonteeth 2",
"Dragonteeth 3",
"Dragonteeth 4",
"Dragonteeth 5",
"Haystack 1",
"Haystack 2",
"Haystack 3",
"Haystack 4",
"Hemmkurvenvenhindernis",
"Log posts 1",
"Log posts 2",
"Log posts 3",
"Log ramps 1",
"Log ramps 2",
"Log ramps 3",
"Belgian Gate",
"Container white",
]
FORTIFICATION_UNITS = [
c for c in vars(dcs.vehicles.Fortification).values() if inspect.isclass(c)
]
FORTIFICATION_UNITS_ID = [
c.id for c in vars(dcs.vehicles.Fortification).values() if inspect.isclass(c)
]

View File

@@ -0,0 +1,51 @@
from dcs.planes import (
Bf_109K_4,
C_101CC,
FW_190A8,
FW_190D9,
F_5E_3,
F_86F_Sabre,
I_16,
L_39ZA,
MiG_15bis,
MiG_19P,
MiG_21Bis,
P_47D_30,
P_47D_30bl1,
P_47D_40,
P_51D,
P_51D_30_NA,
SpitfireLFMkIX,
SpitfireLFMkIXCW,
)
from pydcs_extensions.a4ec.a4ec import A_4E_C
"""
This list contains the aircraft that do not use the guns as the last resort weapons, but as a main weapon
They'll RTB when they don't have gun ammo left
"""
GUNFIGHTERS = [
# Cold War
MiG_15bis,
MiG_19P,
MiG_21Bis,
F_86F_Sabre,
A_4E_C,
F_5E_3,
# Trainers
C_101CC,
L_39ZA,
# WW2
P_51D_30_NA,
P_51D,
P_47D_30,
P_47D_30bl1,
P_47D_40,
SpitfireLFMkIXCW,
SpitfireLFMkIX,
Bf_109K_4,
FW_190D9,
FW_190A8,
I_16,
]

133
game/data/doctrine.py Normal file
View File

@@ -0,0 +1,133 @@
from dataclasses import dataclass
from datetime import timedelta
from game.utils import Distance, feet, nautical_miles
@dataclass(frozen=True)
class Doctrine:
cas: bool
cap: bool
sead: bool
strike: bool
antiship: bool
rendezvous_altitude: Distance
hold_distance: Distance
push_distance: Distance
join_distance: Distance
split_distance: Distance
ingress_egress_distance: Distance
ingress_altitude: Distance
egress_altitude: Distance
min_patrol_altitude: Distance
max_patrol_altitude: Distance
pattern_altitude: Distance
#: The duration that CAP flights will remain on-station.
cap_duration: timedelta
#: The minimum length of the CAP race track.
cap_min_track_length: Distance
#: The maximum length of the CAP race track.
cap_max_track_length: Distance
#: The minimum distance between the defended position and the *end* of the
#: CAP race track.
cap_min_distance_from_cp: Distance
#: The maximum distance between the defended position and the *end* of the
#: CAP race track.
cap_max_distance_from_cp: Distance
#: The engagement range of CAP flights. Any enemy aircraft within this range
#: of the CAP's current position will be engaged by the CAP.
cap_engagement_range: Distance
cas_duration: timedelta
sweep_distance: Distance
MODERN_DOCTRINE = Doctrine(
cap=True,
cas=True,
sead=True,
strike=True,
antiship=True,
rendezvous_altitude=feet(25000),
hold_distance=nautical_miles(15),
push_distance=nautical_miles(20),
join_distance=nautical_miles(20),
split_distance=nautical_miles(20),
ingress_egress_distance=nautical_miles(45),
ingress_altitude=feet(20000),
egress_altitude=feet(20000),
min_patrol_altitude=feet(15000),
max_patrol_altitude=feet(33000),
pattern_altitude=feet(5000),
cap_duration=timedelta(minutes=30),
cap_min_track_length=nautical_miles(15),
cap_max_track_length=nautical_miles(40),
cap_min_distance_from_cp=nautical_miles(10),
cap_max_distance_from_cp=nautical_miles(40),
cap_engagement_range=nautical_miles(50),
cas_duration=timedelta(minutes=30),
sweep_distance=nautical_miles(60),
)
COLDWAR_DOCTRINE = Doctrine(
cap=True,
cas=True,
sead=True,
strike=True,
antiship=True,
rendezvous_altitude=feet(22000),
hold_distance=nautical_miles(10),
push_distance=nautical_miles(10),
join_distance=nautical_miles(10),
split_distance=nautical_miles(10),
ingress_egress_distance=nautical_miles(30),
ingress_altitude=feet(18000),
egress_altitude=feet(18000),
min_patrol_altitude=feet(10000),
max_patrol_altitude=feet(24000),
pattern_altitude=feet(5000),
cap_duration=timedelta(minutes=30),
cap_min_track_length=nautical_miles(12),
cap_max_track_length=nautical_miles(24),
cap_min_distance_from_cp=nautical_miles(8),
cap_max_distance_from_cp=nautical_miles(25),
cap_engagement_range=nautical_miles(35),
cas_duration=timedelta(minutes=30),
sweep_distance=nautical_miles(40),
)
WWII_DOCTRINE = Doctrine(
cap=True,
cas=True,
sead=False,
strike=True,
antiship=True,
hold_distance=nautical_miles(5),
push_distance=nautical_miles(5),
join_distance=nautical_miles(5),
split_distance=nautical_miles(5),
rendezvous_altitude=feet(10000),
ingress_egress_distance=nautical_miles(7),
ingress_altitude=feet(8000),
egress_altitude=feet(8000),
min_patrol_altitude=feet(4000),
max_patrol_altitude=feet(15000),
pattern_altitude=feet(5000),
cap_duration=timedelta(minutes=30),
cap_min_track_length=nautical_miles(8),
cap_max_track_length=nautical_miles(18),
cap_min_distance_from_cp=nautical_miles(0),
cap_max_distance_from_cp=nautical_miles(5),
cap_engagement_range=nautical_miles(20),
cas_duration=timedelta(minutes=30),
sweep_distance=nautical_miles(10),
)

72
game/data/radar_db.py Normal file
View File

@@ -0,0 +1,72 @@
from dcs.ships import (
Battlecruiser_1144_2_Pyotr_Velikiy,
Cruiser_1164_Moskva,
CVN_70_Carl_Vinson,
CVN_71_Theodore_Roosevelt,
CVN_72_Abraham_Lincoln,
CVN_73_George_Washington,
CVN_74_John_C__Stennis,
CV_1143_5_Admiral_Kuznetsov,
CV_1143_5_Admiral_Kuznetsov_2017,
Frigate_11540_Neustrashimy,
Corvette_1124_4_Grisha,
Frigate_1135M_Rezky,
Corvette_1241_1_Molniya,
LHA_1_Tarawa,
FFG_Oliver_Hazzard_Perry,
CG_Ticonderoga,
Type_052B_Destroyer,
Type_052C_Destroyer,
Type_054A_Frigate,
DDG_Arleigh_Burke_IIa,
)
from dcs.vehicles import AirDefence
UNITS_WITH_RADAR = [
# Radars
AirDefence.SAM_SA_15_Tor_Gauntlet,
AirDefence.SAM_SA_11_Buk_Gadfly_C2,
AirDefence.SAM_Patriot_CR__AMG_AN_MRC_137,
AirDefence.SAM_Patriot_ECS,
AirDefence.SPAAA_Gepard,
AirDefence.SPAAA_Vulcan_M163,
AirDefence.SPAAA_ZSU_23_4_Shilka_Gun_Dish,
AirDefence.EWR_1L13,
AirDefence.SAM_SA_6_Kub_Long_Track_STR,
AirDefence.SAM_SA_10_S_300_Grumble_Flap_Lid_TR,
AirDefence.SAM_SA_10_S_300_Grumble_Clam_Shell_SR,
AirDefence.EWR_55G6,
AirDefence.SAM_SA_10_S_300_Grumble_Big_Bird_SR,
AirDefence.SAM_SA_11_Buk_Gadfly_Snow_Drift_SR,
AirDefence.MCC_SR_Sborka_Dog_Ear_SR,
AirDefence.SAM_Hawk_TR__AN_MPQ_46,
AirDefence.SAM_Hawk_SR__AN_MPQ_50,
AirDefence.SAM_Patriot_STR,
AirDefence.SAM_Hawk_CWAR_AN_MPQ_55,
AirDefence.SAM_P19_Flat_Face_SR__SA_2_3,
AirDefence.SAM_Roland_EWR,
AirDefence.SAM_SA_3_S_125_Low_Blow_TR,
AirDefence.SAM_SA_2_S_75_Fan_Song_TR,
AirDefence.HQ_7_Self_Propelled_STR,
# Ships
CVN_70_Carl_Vinson,
FFG_Oliver_Hazzard_Perry,
CG_Ticonderoga,
Corvette_1124_4_Grisha,
CV_1143_5_Admiral_Kuznetsov,
Corvette_1241_1_Molniya,
Cruiser_1164_Moskva,
Frigate_11540_Neustrashimy,
Battlecruiser_1144_2_Pyotr_Velikiy,
Frigate_1135M_Rezky,
CV_1143_5_Admiral_Kuznetsov_2017,
CVN_74_John_C__Stennis,
CVN_71_Theodore_Roosevelt,
CVN_72_Abraham_Lincoln,
CVN_73_George_Washington,
DDG_Arleigh_Burke_IIa,
LHA_1_Tarawa,
Type_052B_Destroyer,
Type_054A_Frigate,
Type_052C_Destroyer,
]

1169
game/data/weapons.py Normal file

File diff suppressed because it is too large Load Diff

1773
game/db.py

File diff suppressed because it is too large Load Diff

282
game/debriefing.py Normal file
View File

@@ -0,0 +1,282 @@
from __future__ import annotations
import itertools
import json
import logging
import os
import threading
import time
from collections import defaultdict
from dataclasses import dataclass, field
from typing import (
Any,
Callable,
Dict,
Iterator,
List,
Type,
TYPE_CHECKING,
)
from dcs.unittype import FlyingType, UnitType
from game import db
from game.theater import Airfield, ControlPoint
from game.unitmap import Building, FrontLineUnit, GroundObjectUnit, UnitMap
from gen.flights.flight import Flight
if TYPE_CHECKING:
from game import Game
DEBRIEFING_LOG_EXTENSION = "log"
@dataclass(frozen=True)
class AirLosses:
player: List[Flight]
enemy: List[Flight]
@property
def losses(self) -> Iterator[Flight]:
return itertools.chain(self.player, self.enemy)
def by_type(self, player: bool) -> Dict[Type[FlyingType], int]:
losses_by_type: Dict[Type[FlyingType], int] = defaultdict(int)
losses = self.player if player else self.enemy
for loss in losses:
losses_by_type[loss.unit_type] += 1
return losses_by_type
def surviving_flight_members(self, flight: Flight) -> int:
losses = 0
for loss in self.losses:
if loss == flight:
losses += 1
return flight.count - losses
@dataclass
class GroundLosses:
player_front_line: List[FrontLineUnit] = field(default_factory=list)
enemy_front_line: List[FrontLineUnit] = field(default_factory=list)
player_ground_objects: List[GroundObjectUnit] = field(default_factory=list)
enemy_ground_objects: List[GroundObjectUnit] = field(default_factory=list)
player_buildings: List[Building] = field(default_factory=list)
enemy_buildings: List[Building] = field(default_factory=list)
player_airfields: List[Airfield] = field(default_factory=list)
enemy_airfields: List[Airfield] = field(default_factory=list)
@dataclass(frozen=True)
class StateData:
#: True if the mission ended. If False, the mission exited abnormally.
mission_ended: bool
#: Names of aircraft units that were killed during the mission.
killed_aircraft: List[str]
#: Names of vehicle (and ship) units that were killed during the mission.
killed_ground_units: List[str]
#: Names of static units that were destroyed during the mission.
destroyed_statics: List[str]
#: Mangled names of bases that were captured during the mission.
base_capture_events: List[str]
@classmethod
def from_json(cls, data: Dict[str, Any]) -> StateData:
return cls(
mission_ended=data["mission_ended"],
killed_aircraft=data["killed_aircrafts"],
# Airfields emit a new "dead" event every time a bomb is dropped on
# them when they've already dead. Dedup.
killed_ground_units=list(set(data["killed_ground_units"])),
destroyed_statics=data["destroyed_objects_positions"],
base_capture_events=data["base_capture_events"],
)
class Debriefing:
def __init__(
self, state_data: Dict[str, Any], game: Game, unit_map: UnitMap
) -> None:
self.state_data = StateData.from_json(state_data)
self.unit_map = unit_map
self.player_country = game.player_country
self.enemy_country = game.enemy_country
self.player_country_id = db.country_id_from_name(game.player_country)
self.enemy_country_id = db.country_id_from_name(game.enemy_country)
self.air_losses = self.dead_aircraft()
self.ground_losses = self.dead_ground_units()
@property
def front_line_losses(self) -> Iterator[FrontLineUnit]:
yield from self.ground_losses.player_front_line
yield from self.ground_losses.enemy_front_line
@property
def ground_object_losses(self) -> Iterator[GroundObjectUnit]:
yield from self.ground_losses.player_ground_objects
yield from self.ground_losses.enemy_ground_objects
@property
def building_losses(self) -> Iterator[Building]:
yield from self.ground_losses.player_buildings
yield from self.ground_losses.enemy_buildings
@property
def damaged_runways(self) -> Iterator[Airfield]:
yield from self.ground_losses.player_airfields
yield from self.ground_losses.enemy_airfields
def casualty_count(self, control_point: ControlPoint) -> int:
return len([x for x in self.front_line_losses if x.origin == control_point])
def front_line_losses_by_type(self, player: bool) -> Dict[Type[UnitType], int]:
losses_by_type: Dict[Type[UnitType], int] = defaultdict(int)
if player:
losses = self.ground_losses.player_front_line
else:
losses = self.ground_losses.enemy_front_line
for loss in losses:
losses_by_type[loss.unit_type] += 1
return losses_by_type
def building_losses_by_type(self, player: bool) -> Dict[str, int]:
losses_by_type: Dict[str, int] = defaultdict(int)
if player:
losses = self.ground_losses.player_buildings
else:
losses = self.ground_losses.enemy_buildings
for loss in losses:
if loss.ground_object.control_point.captured != player:
continue
losses_by_type[loss.ground_object.dcs_identifier] += 1
return losses_by_type
def dead_aircraft(self) -> AirLosses:
player_losses = []
enemy_losses = []
for unit_name in self.state_data.killed_aircraft:
flight = self.unit_map.flight(unit_name)
if flight is None:
logging.error(f"Could not find Flight matching {unit_name}")
continue
if flight.departure.captured:
player_losses.append(flight)
else:
enemy_losses.append(flight)
return AirLosses(player_losses, enemy_losses)
def dead_ground_units(self) -> GroundLosses:
losses = GroundLosses()
for unit_name in self.state_data.killed_ground_units:
front_line_unit = self.unit_map.front_line_unit(unit_name)
if front_line_unit is not None:
if front_line_unit.origin.captured:
losses.player_front_line.append(front_line_unit)
else:
losses.enemy_front_line.append(front_line_unit)
continue
ground_object_unit = self.unit_map.ground_object_unit(unit_name)
if ground_object_unit is not None:
if ground_object_unit.ground_object.control_point.captured:
losses.player_ground_objects.append(ground_object_unit)
else:
losses.enemy_ground_objects.append(ground_object_unit)
continue
building = self.unit_map.building_or_fortification(unit_name)
# Try appending object to the name, because we do this for building statics.
if building is None:
building = self.unit_map.building_or_fortification(
f"{unit_name} object"
)
if building is not None:
if building.ground_object.control_point.captured:
losses.player_buildings.append(building)
else:
losses.enemy_buildings.append(building)
continue
airfield = self.unit_map.airfield(unit_name)
if airfield is not None:
if airfield.captured:
losses.player_airfields.append(airfield)
else:
losses.enemy_airfields.append(airfield)
continue
# Only logging as debug because we don't currently track infantry
# deaths, so we expect to see quite a few unclaimed dead ground
# units. We should start tracking those and covert this to a
# warning.
logging.debug(
f"Death of untracked ground unit {unit_name} will "
"have no effect. This may be normal behavior."
)
return losses
@property
def base_capture_events(self):
"""Keeps only the last instance of a base capture event for each base ID."""
reversed_captures = list(reversed(self.state_data.base_capture_events))
last_base_cap_indexes = []
for idx, base in enumerate(i.split("||")[0] for i in reversed_captures):
if base not in [x[1] for x in last_base_cap_indexes]:
last_base_cap_indexes.append((idx, base))
return [reversed_captures[idx[0]] for idx in last_base_cap_indexes]
class PollDebriefingFileThread(threading.Thread):
"""Thread class with a stop() method. The thread itself has to check
regularly for the stopped() condition."""
def __init__(
self, callback: Callable[[Debriefing], None], game: Game, unit_map: UnitMap
) -> None:
super().__init__()
self._stop_event = threading.Event()
self.callback = callback
self.game = game
self.unit_map = unit_map
def stop(self):
self._stop_event.set()
def stopped(self):
return self._stop_event.is_set()
def run(self):
if os.path.isfile("state.json"):
last_modified = os.path.getmtime("state.json")
else:
last_modified = 0
while not self.stopped():
if (
os.path.isfile("state.json")
and os.path.getmtime("state.json") > last_modified
):
with open("state.json", "r") as json_file:
json_data = json.load(json_file)
debriefing = Debriefing(json_data, self.game, self.unit_map)
self.callback(debriefing)
break
time.sleep(5)
def wait_for_debriefing(
callback: Callable[[Debriefing], None], game: Game, unit_map
) -> PollDebriefingFileThread:
thread = PollDebriefingFileThread(callback, game, unit_map)
thread.start()
return thread

View File

@@ -1,8 +1,2 @@
from .event import *
from .groundintercept import *
from .intercept import *
from .capture import *
from .navalintercept import *
from .antiaastrike import *
from .groundattack import *
from .infantrytransport import *
from .frontlineattack import *

14
game/event/airwar.py Normal file
View File

@@ -0,0 +1,14 @@
from __future__ import annotations
from typing import TYPE_CHECKING
from .event import Event
if TYPE_CHECKING:
from game.theater import ConflictTheater
class AirWarEvent(Event):
"""Event handler for the air battle"""
def __str__(self):
return "AirWar"

View File

@@ -1,86 +0,0 @@
import math
import random
from dcs.task import *
from game import *
from game.event import *
from game.operation.antiaastrike import AntiAAStrikeOperation
from userdata.debriefing import Debriefing
class AntiAAStrikeEvent(Event):
TARGET_AMOUNT_MAX = 2
STRENGTH_INFLUENCE = 0.3
SUCCESS_TARGETS_HIT_PERCENTAGE = 0.5
targets = None # type: db.ArmorDict
def __str__(self):
return "Anti-AA strike from {} at {}".format(self.from_cp, self.to_cp)
def is_successfull(self, debriefing: Debriefing):
total_targets = sum(self.targets.values())
destroyed_targets = 0
for unit, count in debriefing.destroyed_units[self.defender_name].items():
if unit in self.targets:
destroyed_targets += count
if self.from_cp.captured:
return math.ceil(float(destroyed_targets) / total_targets) >= self.SUCCESS_TARGETS_HIT_PERCENTAGE
else:
return math.ceil(float(destroyed_targets) / total_targets) < self.SUCCESS_TARGETS_HIT_PERCENTAGE
def commit(self, debriefing: Debriefing):
super(AntiAAStrikeEvent, self).commit(debriefing)
if self.from_cp.captured:
if self.is_successfull(debriefing):
self.to_cp.base.affect_strength(-self.STRENGTH_INFLUENCE)
else:
self.to_cp.base.affect_strength(+self.STRENGTH_INFLUENCE)
else:
if self.is_successfull(debriefing):
self.from_cp.base.affect_strength(-self.STRENGTH_INFLUENCE)
else:
self.to_cp.base.affect_strength(-self.STRENGTH_INFLUENCE)
def skip(self):
if self.to_cp.captured:
self.to_cp.base.affect_strength(-0.1)
def player_attacking(self, strikegroup: db.PlaneDict, clients: db.PlaneDict):
self.targets = self.to_cp.base.assemble_aa(count=self.to_cp.base.total_aa)
op = AntiAAStrikeOperation(game=self.game,
attacker_name=self.attacker_name,
defender_name=self.defender_name,
attacker_clients=clients,
defender_clients={},
from_cp=self.from_cp,
to_cp=self.to_cp)
op.setup(target=self.targets,
strikegroup=strikegroup,
interceptors={})
self.operation = op
def player_defending(self, interceptors: db.PlaneDict, clients: db.PlaneDict):
self.targets = self.to_cp.base.assemble_aa()
op = AntiAAStrikeOperation(
self.game,
attacker_name=self.attacker_name,
defender_name=self.defender_name,
attacker_clients={},
defender_clients=clients,
from_cp=self.from_cp,
to_cp=self.to_cp
)
strikegroup = self.from_cp.base.scramble_cas(self.game.settings.multiplier)
op.setup(target=self.targets,
strikegroup=strikegroup,
interceptors=interceptors)
self.operation = op

View File

@@ -1,89 +0,0 @@
import math
import random
from dcs.task import *
from game import db
from game.operation.capture import CaptureOperation
from userdata.debriefing import Debriefing
from .event import Event
class CaptureEvent(Event):
silent = True
BONUS_BASE = 15
STRENGTH_RECOVERY = 0.35
def __str__(self):
return "Attack from {} to {}".format(self.from_cp, self.to_cp)
def is_successfull(self, debriefing: Debriefing):
alive_attackers = sum([v for k, v in debriefing.alive_units[self.attacker_name].items() if db.unit_task(k) == PinpointStrike])
alive_defenders = sum([v for k, v in debriefing.alive_units[self.defender_name].items() if db.unit_task(k) == PinpointStrike])
attackers_success = alive_attackers >= alive_defenders
if self.from_cp.captured:
return attackers_success
else:
return not attackers_success
def commit(self, debriefing: Debriefing):
super(CaptureEvent, self).commit(debriefing)
if self.is_successfull(debriefing):
if self.from_cp.captured:
self.to_cp.captured = True
self.to_cp.base.filter_units(db.UNIT_BY_COUNTRY[self.attacker_name])
self.to_cp.base.affect_strength(+self.STRENGTH_RECOVERY)
else:
if not self.from_cp.captured:
self.to_cp.captured = False
self.to_cp.base.affect_strength(+self.STRENGTH_RECOVERY)
def skip(self):
if self.to_cp.captured:
self.to_cp.captured = False
def player_defending(self, interceptors: db.PlaneDict, clients: db.PlaneDict):
cas = self.from_cp.base.scramble_cas(self.game.settings.multiplier)
escort = self.from_cp.base.scramble_sweep(self.game.settings.multiplier)
attackers = self.from_cp.base.armor
op = CaptureOperation(game=self.game,
attacker_name=self.attacker_name,
defender_name=self.defender_name,
attacker_clients={},
defender_clients=clients,
from_cp=self.from_cp,
to_cp=self.to_cp)
op.setup(cas=cas,
escort=escort,
attack=attackers,
intercept=interceptors,
defense=self.to_cp.base.armor,
aa=self.to_cp.base.aa)
self.operation = op
def player_attacking(self, cas: db.PlaneDict, escort: db.PlaneDict, armor: db.ArmorDict, clients: db.PlaneDict):
op = CaptureOperation(game=self.game,
attacker_name=self.attacker_name,
defender_name=self.defender_name,
attacker_clients=clients,
defender_clients={},
from_cp=self.from_cp,
to_cp=self.to_cp)
defenders = self.to_cp.base.scramble_sweep(self.game.settings.multiplier)
defenders.update(self.to_cp.base.scramble_cas(self.game.settings.multiplier))
op.setup(cas=cas,
escort=escort,
attack=armor,
intercept=defenders,
defense=self.to_cp.base.armor,
aa=self.to_cp.base.assemble_aa())
self.operation = op

View File

@@ -1,101 +1,480 @@
from dcs.unittype import UnitType
from __future__ import annotations
from game import *
from theater import *
from gen.environmentgen import EnvironmentSettings
import logging
import math
from typing import Dict, Iterator, List, TYPE_CHECKING, Tuple, Type
from userdata.debriefing import Debriefing
from userdata import persistency
from dcs.mapping import Point
from dcs.task import Task
from dcs.unittype import UnitType, VehicleType
DIFFICULTY_LOG_BASE = 1.1
from game import persistency
from game.debriefing import AirLosses, Debriefing
from game.infos.information import Information
from game.operation.operation import Operation
from game.theater import ControlPoint
from gen import AirTaskingOrder
from gen.ground_forces.combat_stance import CombatStance
from ..db import PRICES
from ..unitmap import UnitMap
if TYPE_CHECKING:
from ..game import Game
MINOR_DEFEAT_INFLUENCE = 0.1
DEFEAT_INFLUENCE = 0.3
STRONG_DEFEAT_INFLUENCE = 0.5
class Event:
silent = False
informational = False
is_awacs_enabled = False
operation = None # type: Operation
difficulty = 1 # type: int
game = None # type: Game
environment_settings = None # type: EnvironmentSettings
BONUS_BASE = 5
def __init__(self, attacker_name: str, defender_name: str, from_cp: ControlPoint, to_cp: ControlPoint, game):
game = None # type: Game
location = None # type: Point
from_cp = None # type: ControlPoint
to_cp = None # type: ControlPoint
difficulty = 1 # type: int
def __init__(
self,
game,
from_cp: ControlPoint,
target_cp: ControlPoint,
location: Point,
attacker_name: str,
defender_name: str,
):
self.game = game
self.from_cp = from_cp
self.to_cp = target_cp
self.location = location
self.attacker_name = attacker_name
self.defender_name = defender_name
self.to_cp = to_cp
self.from_cp = from_cp
self.game = game
@property
def is_player_attacking(self) -> bool:
return self.attacker_name == self.game.player
return self.attacker_name == self.game.player_name
@property
def enemy_cp(self) -> ControlPoint:
if self.attacker_name == self.game.player:
return self.to_cp
else:
return self.from_cp
def tasks(self) -> List[Type[Task]]:
return []
@property
def threat_description(self) -> str:
return ""
def generate(self) -> UnitMap:
Operation.prepare(self.game)
unit_map = Operation.generate()
Operation.current_mission.save(
persistency.mission_path_for("liberation_nextturn.miz")
)
return unit_map
def bonus(self) -> int:
return int(math.log(self.to_cp.importance + 1, DIFFICULTY_LOG_BASE) * self.BONUS_BASE)
@staticmethod
def _transfer_aircraft(
ato: AirTaskingOrder, losses: AirLosses, for_player: bool
) -> None:
for package in ato.packages:
for flight in package.flights:
# No need to transfer to the same location.
if flight.departure == flight.arrival:
continue
def is_successfull(self, debriefing: Debriefing) -> bool:
return self.operation.is_successfull(debriefing)
# Don't transfer to bases that were captured. Note that if the
# airfield was back-filling transfers it may overflow. We could
# attempt to be smarter in the future by performing transfers in
# order up a graph to prevent transfers to full airports and
# send overflow off-map, but overflow is fine for now.
if flight.arrival.captured != for_player:
logging.info(
f"Not transferring {flight} because {flight.arrival} "
"was captured"
)
continue
def generate(self):
self.operation.is_awacs_enabled = self.is_awacs_enabled
transfer_count = losses.surviving_flight_members(flight)
if transfer_count < 0:
logging.error(
f"{flight} had {flight.count} aircraft but "
f"{transfer_count} losses were recorded."
)
continue
self.operation.prepare(self.game.theater.terrain, is_quick=False)
self.operation.generate()
self.operation.mission.save(persistency.mission_path_for("liberation_nextturn.miz"))
self.environment_settings = self.operation.environment_settings
aircraft = flight.unit_type
available = flight.departure.base.total_units_of_type(aircraft)
if available < transfer_count:
logging.error(
f"Found killed {aircraft} from {flight.departure} but "
f"that airbase has only {available} available."
)
continue
def generate_quick(self):
self.operation.is_awacs_enabled = self.is_awacs_enabled
self.operation.environment_settings = self.environment_settings
flight.departure.base.aircraft[aircraft] -= transfer_count
if aircraft not in flight.arrival.base.aircraft:
# TODO: Should use defaultdict.
flight.arrival.base.aircraft[aircraft] = 0
flight.arrival.base.aircraft[aircraft] += transfer_count
self.operation.prepare(self.game.theater.terrain, is_quick=True)
self.operation.generate()
self.operation.mission.save(persistency.mission_path_for("liberation_nextturn_quick.miz"))
def complete_aircraft_transfers(self, debriefing: Debriefing) -> None:
self._transfer_aircraft(
self.game.blue_ato, debriefing.air_losses, for_player=True
)
self._transfer_aircraft(
self.game.red_ato, debriefing.air_losses, for_player=False
)
@staticmethod
def commit_air_losses(debriefing: Debriefing) -> None:
for loss in debriefing.air_losses.losses:
aircraft = loss.unit_type
cp = loss.departure
available = cp.base.total_units_of_type(aircraft)
if available <= 0:
logging.error(
f"Found killed {aircraft} from {cp} but that airbase has "
"none available."
)
continue
logging.info(f"{aircraft} destroyed from {cp}")
cp.base.aircraft[aircraft] -= 1
@staticmethod
def commit_front_line_losses(debriefing: Debriefing) -> None:
for loss in debriefing.front_line_losses:
unit_type = loss.unit_type
control_point = loss.origin
available = control_point.base.total_units_of_type(unit_type)
if available <= 0:
logging.error(
f"Found killed {unit_type} from {control_point} but that "
"airbase has none available."
)
continue
logging.info(f"{unit_type} destroyed from {control_point}")
control_point.base.armor[unit_type] -= 1
@staticmethod
def commit_ground_object_losses(debriefing: Debriefing) -> None:
for loss in debriefing.ground_object_losses:
# TODO: This should be stored in the TGO, not in the pydcs Group.
if not hasattr(loss.group, "units_losts"):
loss.group.units_losts = []
loss.group.units.remove(loss.unit)
loss.group.units_losts.append(loss.unit)
def commit_building_losses(self, debriefing: Debriefing) -> None:
for loss in debriefing.building_losses:
loss.ground_object.kill()
self.game.informations.append(
Information(
"Building destroyed",
f"{loss.ground_object.dcs_identifier} has been destroyed at "
f"location {loss.ground_object.obj_name}",
self.game.turn,
)
)
@staticmethod
def commit_damaged_runways(debriefing: Debriefing) -> None:
for damaged_runway in debriefing.damaged_runways:
damaged_runway.damage_runway()
def commit(self, debriefing: Debriefing):
for country, losses in debriefing.destroyed_units.items():
if country == self.attacker_name:
cp = self.from_cp
else:
cp = self.to_cp
logging.info("Committing mission results")
cp.base.commit_losses(losses)
self.commit_air_losses(debriefing)
self.commit_front_line_losses(debriefing)
self.commit_ground_object_losses(debriefing)
self.commit_building_losses(debriefing)
self.commit_damaged_runways(debriefing)
def skip(self):
pass
# ------------------------------
# Captured bases
# if self.game.player_country in db.BLUEFOR_FACTIONS:
coalition = 2 # Value in DCS mission event for BLUE
# else:
# coalition = 1 # Value in DCS mission event for RED
for captured in debriefing.base_capture_events:
try:
id = int(captured.split("||")[0])
new_owner_coalition = int(captured.split("||")[1])
captured_cps = []
for cp in self.game.theater.controlpoints:
if cp.id == id:
if cp.captured and new_owner_coalition != coalition:
for_player = False
info = Information(
cp.name + " lost !",
"The ennemy took control of "
+ cp.name
+ "\nShame on us !",
self.game.turn,
)
self.game.informations.append(info)
captured_cps.append(cp)
elif not (cp.captured) and new_owner_coalition == coalition:
for_player = True
info = Information(
cp.name + " captured !",
"We took control of " + cp.name + "! Great job !",
self.game.turn,
)
self.game.informations.append(info)
captured_cps.append(cp)
else:
continue
cp.capture(self.game, for_player)
for cp in captured_cps:
logging.info("Will run redeploy for " + cp.name)
self.redeploy_units(cp)
except Exception:
logging.exception(f"Could not process base capture {captured}")
self.complete_aircraft_transfers(debriefing)
# Destroyed units carcass
# -------------------------
for destroyed_unit in debriefing.state_data.destroyed_statics:
self.game.add_destroyed_units(destroyed_unit)
# -----------------------------------
# Compute damage to bases
for cp in self.game.theater.player_points():
enemy_cps = [e for e in cp.connected_points if not e.captured]
for enemy_cp in enemy_cps:
print(
"Compute frontline progression for : "
+ cp.name
+ " to "
+ enemy_cp.name
)
delta = 0.0
player_won = True
ally_casualties = debriefing.casualty_count(cp)
enemy_casualties = debriefing.casualty_count(enemy_cp)
ally_units_alive = cp.base.total_armor
enemy_units_alive = enemy_cp.base.total_armor
print(ally_units_alive)
print(enemy_units_alive)
print(ally_casualties)
print(enemy_casualties)
ratio = (1.0 + enemy_casualties) / (1.0 + ally_casualties)
player_aggresive = cp.stances[enemy_cp.id] in [
CombatStance.AGGRESSIVE,
CombatStance.ELIMINATION,
CombatStance.BREAKTHROUGH,
]
if ally_units_alive == 0:
player_won = False
delta = STRONG_DEFEAT_INFLUENCE
elif enemy_units_alive == 0:
player_won = True
delta = STRONG_DEFEAT_INFLUENCE
elif cp.stances[enemy_cp.id] == CombatStance.RETREAT:
player_won = False
delta = STRONG_DEFEAT_INFLUENCE
else:
if enemy_casualties > ally_casualties:
player_won = True
if cp.stances[enemy_cp.id] == CombatStance.BREAKTHROUGH:
delta = STRONG_DEFEAT_INFLUENCE
else:
if ratio > 3:
delta = STRONG_DEFEAT_INFLUENCE
elif ratio < 1.5:
delta = MINOR_DEFEAT_INFLUENCE
else:
delta = DEFEAT_INFLUENCE
elif ally_casualties > enemy_casualties:
if (
ally_units_alive > 2 * enemy_units_alive
and player_aggresive
):
# Even with casualties if the enemy is overwhelmed, they are going to lose ground
player_won = True
delta = MINOR_DEFEAT_INFLUENCE
elif (
ally_units_alive > 3 * enemy_units_alive
and player_aggresive
):
player_won = True
delta = STRONG_DEFEAT_INFLUENCE
else:
# But is the enemy is not outnumbered, we lose
player_won = False
if cp.stances[enemy_cp.id] == CombatStance.BREAKTHROUGH:
delta = STRONG_DEFEAT_INFLUENCE
else:
delta = STRONG_DEFEAT_INFLUENCE
# No progress with defensive strategies
if player_won and cp.stances[enemy_cp.id] in [
CombatStance.DEFENSIVE,
CombatStance.AMBUSH,
]:
print("Defensive stance, progress is limited")
delta = MINOR_DEFEAT_INFLUENCE
if player_won:
print(cp.name + " won ! factor > " + str(delta))
cp.base.affect_strength(delta)
enemy_cp.base.affect_strength(-delta)
info = Information(
"Frontline Report",
"Our ground forces from "
+ cp.name
+ " are making progress toward "
+ enemy_cp.name,
self.game.turn,
)
self.game.informations.append(info)
else:
print(cp.name + " lost ! factor > " + str(delta))
enemy_cp.base.affect_strength(delta)
cp.base.affect_strength(-delta)
info = Information(
"Frontline Report",
"Our ground forces from "
+ cp.name
+ " are losing ground against the enemy forces from "
+ enemy_cp.name,
self.game.turn,
)
self.game.informations.append(info)
def redeploy_units(self, cp: ControlPoint) -> None:
""" "
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
# From each ally cp, send reinforcements
for ally_cp in ally_connected_cps:
self.redeploy_between(cp, ally_cp)
def redeploy_between(self, destination: ControlPoint, source: ControlPoint) -> None:
total_units_redeployed = 0
moved_units = {}
if source.has_active_frontline or not destination.captured:
# If there are still active front lines to defend at the
# transferring CP we should not transfer all units.
#
# Opfor also does not transfer all of their units.
# TODO: Balance the CPs rather than moving half from everywhere.
move_factor = 0.5
else:
# Otherwise we can move everything.
move_factor = 1
for frontline_unit, count in source.base.armor.items():
moved_units[frontline_unit] = int(count * move_factor)
total_units_redeployed = total_units_redeployed + int(count * move_factor)
destination.base.commision_units(moved_units)
source.base.commit_losses(moved_units)
# Also transfer pending deliveries.
for unit_type, count in source.pending_unit_deliveries.units.items():
if not issubclass(unit_type, VehicleType):
continue
if count <= 0:
# Don't transfer *sales*...
continue
move_count = int(count * move_factor)
source.pending_unit_deliveries.sell({unit_type: move_count})
destination.pending_unit_deliveries.order({unit_type: move_count})
total_units_redeployed += move_count
if total_units_redeployed > 0:
text = (
f"{total_units_redeployed} units have been redeployed from "
f"{source.name} to {destination.name}"
)
info = Information("Units redeployed", text, self.game.turn)
self.game.informations.append(info)
logging.info(text)
class UnitsDeliveryEvent(Event):
informational = True
units = None # type: typing.Dict[UnitType, int]
class UnitsDeliveryEvent:
def __init__(self, control_point: ControlPoint) -> None:
self.to_cp = control_point
self.units: Dict[Type[UnitType], int] = {}
def __init__(self, attacker_name: str, defender_name: str, from_cp: ControlPoint, to_cp: ControlPoint, game):
super(UnitsDeliveryEvent, self).__init__(attacker_name=attacker_name,
defender_name=defender_name,
from_cp=from_cp,
to_cp=to_cp,
game=game)
self.units = {}
def __str__(self):
def __str__(self) -> str:
return "Pending delivery to {}".format(self.to_cp)
def deliver(self, units: typing.Dict[UnitType, int]):
def order(self, units: Dict[Type[UnitType], int]) -> None:
for k, v in units.items():
self.units[k] = self.units.get(k, 0) + v
def skip(self):
self.to_cp.base.commision_units(self.units)
def sell(self, units: Dict[Type[UnitType], int]) -> None:
for k, v in units.items():
self.units[k] = self.units.get(k, 0) - v
def consume_each_order(self) -> Iterator[Tuple[Type[UnitType], int]]:
while self.units:
yield self.units.popitem()
def refund_all(self, game: Game) -> None:
for unit_type, count in self.consume_each_order():
try:
price = PRICES[unit_type]
except KeyError:
logging.error(f"Could not refund {unit_type.id}, price unknown")
continue
logging.info(f"Refunding {count} {unit_type.id} at {self.to_cp.name}")
game.adjust_budget(price * count, player=self.to_cp.captured)
def available_next_turn(self, unit_type: Type[UnitType]) -> int:
pending_units = self.units.get(unit_type)
if pending_units is None:
pending_units = 0
current_units = self.to_cp.base.total_units_of_type(unit_type)
return pending_units + current_units
def process(self, game: Game) -> None:
bought_units: Dict[Type[UnitType], int] = {}
sold_units: Dict[Type[UnitType], int] = {}
for unit_type, count in self.units.items():
coalition = "Ally" if self.to_cp.captured else "Enemy"
aircraft = unit_type.id
name = self.to_cp.name
if count >= 0:
bought_units[unit_type] = count
game.message(
f"{coalition} reinforcements: {aircraft} x {count} at {name}"
)
else:
sold_units[unit_type] = -count
game.message(f"{coalition} sold: {aircraft} x {-count} at {name}")
self.to_cp.base.commision_units(bought_units)
self.to_cp.base.commit_losses(sold_units)
self.units = {}
bought_units = {}
sold_units = {}

View File

@@ -0,0 +1,12 @@
from .event import Event
class FrontlineAttackEvent(Event):
"""
An event centered on a FrontLine Conflict.
Currently the same as its parent, but here for legacy compatibility as well as to allow for
future unique Event handling
"""
def __str__(self):
return "Frontline attack"

View File

@@ -1,40 +0,0 @@
import math
import random
from dcs.task import *
from game import *
from game.event import *
from game.event.groundintercept import GroundInterceptEvent
from game.operation.groundattack import GroundAttackOperation
class GroundAttackEvent(GroundInterceptEvent):
def __str__(self):
return "Destroy insurgents at {}".format(self.to_cp)
@property
def threat_description(self):
return ""
def player_defending(self, strikegroup: db.PlaneDict, clients: db.PlaneDict):
suitable_unittypes = db.find_unittype(Reconnaissance, self.attacker_name)
random.shuffle(suitable_unittypes)
unittypes = suitable_unittypes[:self.TARGET_VARIETY]
typecount = max(math.floor(self.difficulty * self.TARGET_AMOUNT_FACTOR), 1)
self.targets = {unittype: typecount for unittype in unittypes}
op = GroundAttackOperation(game=self.game,
attacker_name=self.attacker_name,
defender_name=self.defender_name,
attacker_clients={},
defender_clients=clients,
from_cp=self.from_cp,
to_cp=self.to_cp)
op.setup(target=self.targets,
strikegroup=strikegroup)
self.operation = op
def player_attacking(self, interceptors: db.PlaneDict, clients: db.PlaneDict):
assert False

View File

@@ -1,106 +0,0 @@
import math
import random
from dcs.task import *
from dcs.vehicles import AirDefence
from game import *
from game.event import *
from game.operation.groundintercept import GroundInterceptOperation
from userdata.debriefing import Debriefing
class GroundInterceptEvent(Event):
TARGET_AMOUNT_FACTOR = 2
TARGET_VARIETY = 2
STRENGTH_INFLUENCE = 0.3
SUCCESS_TARGETS_HIT_PERCENTAGE = 0.5
targets = None # type: db.ArmorDict
@property
def threat_description(self):
if not self.game.is_player_attack(self):
return "{} aicraft".format(self.from_cp.base.scramble_count(self.game.settings.multiplier, CAS))
else:
return super(GroundInterceptEvent, self).threat_description
def __str__(self):
return "Fontline CAS from {} at {}".format(self.from_cp, self.to_cp)
def is_successfull(self, debriefing: Debriefing):
total_targets = sum(self.targets.values())
destroyed_targets = 0
for unit, count in debriefing.destroyed_units[self.defender_name].items():
if unit in self.targets:
destroyed_targets += count
if self.from_cp.captured:
return float(destroyed_targets) / total_targets >= self.SUCCESS_TARGETS_HIT_PERCENTAGE
else:
return float(destroyed_targets) / total_targets < self.SUCCESS_TARGETS_HIT_PERCENTAGE
def commit(self, debriefing: Debriefing):
super(GroundInterceptEvent, self).commit(debriefing)
if self.from_cp.captured:
if self.is_successfull(debriefing):
self.to_cp.base.affect_strength(-self.STRENGTH_INFLUENCE)
else:
self.to_cp.base.affect_strength(+self.STRENGTH_INFLUENCE)
else:
if self.is_successfull(debriefing):
self.from_cp.base.affect_strength(-self.STRENGTH_INFLUENCE)
else:
self.to_cp.base.affect_strength(-self.STRENGTH_INFLUENCE)
def skip(self):
if self.to_cp.captured:
self.to_cp.base.affect_strength(-0.1)
def player_attacking(self, strikegroup: db.PlaneDict, clients: db.PlaneDict):
suitable_unittypes = db.find_unittype(PinpointStrike, self.defender_name)
random.shuffle(suitable_unittypes)
unittypes = suitable_unittypes[:self.TARGET_VARIETY]
typecount = max(math.floor(self.difficulty * self.TARGET_AMOUNT_FACTOR), 1)
self.targets = {unittype: typecount for unittype in unittypes}
defense_aa_unit = random.choice(self.game.commision_unit_types(self.to_cp, AirDefence))
self.targets[defense_aa_unit] = 1
op = GroundInterceptOperation(game=self.game,
attacker_name=self.attacker_name,
defender_name=self.defender_name,
attacker_clients=clients,
defender_clients={},
from_cp=self.from_cp,
to_cp=self.to_cp)
op.setup(target=self.targets,
strikegroup=strikegroup,
interceptors={})
self.operation = op
def player_defending(self, interceptors: db.PlaneDict, clients: db.PlaneDict):
suitable_unittypes = db.find_unittype(PinpointStrike, self.defender_name)
random.shuffle(suitable_unittypes)
unittypes = suitable_unittypes[:self.TARGET_VARIETY]
typecount = max(math.floor(self.difficulty * self.TARGET_AMOUNT_FACTOR), 1)
self.targets = {unittype: typecount for unittype in unittypes}
op = GroundInterceptOperation(
self.game,
attacker_name=self.attacker_name,
defender_name=self.defender_name,
attacker_clients={},
defender_clients=clients,
from_cp=self.from_cp,
to_cp=self.to_cp
)
strikegroup = self.from_cp.base.scramble_cas(self.game.settings.multiplier)
op.setup(target=self.targets,
strikegroup=strikegroup,
interceptors=interceptors)
self.operation = op

View File

@@ -1,47 +0,0 @@
import math
import random
from dcs.task import *
from dcs.vehicles import *
from game import db
from game.operation.infantrytransport import InfantryTransportOperation
from theater.conflicttheater import *
from userdata.debriefing import Debriefing
from .event import Event
class InfantryTransportEvent(Event):
STRENGTH_INFLUENCE = 0.3
def __str__(self):
return "Frontline transport troops to {}".format(self.to_cp)
def is_successfull(self, debriefing: Debriefing):
return True
def commit(self, debriefing: Debriefing):
super(InfantryTransportEvent, self).commit(debriefing)
if self.is_successfull(debriefing):
self.to_cp.base.affect_strength(-self.STRENGTH_INFLUENCE)
else:
self.from_cp.base.affect_strength(-self.STRENGTH_INFLUENCE)
def player_attacking(self, transport: db.HeliDict, clients: db.HeliDict):
op = InfantryTransportOperation(
game=self.game,
attacker_name=self.attacker_name,
defender_name=self.defender_name,
attacker_clients=clients,
defender_clients={},
from_cp=self.from_cp,
to_cp=self.to_cp
)
air_defense = db.find_unittype(AirDefence, self.defender_name)[0]
op.setup(transport=transport,
aa={air_defense: 2})
self.operation = op

View File

@@ -1,100 +0,0 @@
import math
import random
from dcs.task import *
from dcs.vehicles import *
from game import db
from game.operation.intercept import InterceptOperation
from theater.conflicttheater import *
from userdata.debriefing import Debriefing
from .event import Event
class InterceptEvent(Event):
STRENGTH_INFLUENCE = 0.3
GLOBAL_STRENGTH_INFLUENCE = 0.3
AIRDEFENSE_COUNT = 3
transport_unit = None # type: FlyingType
def __str__(self):
return "Intercept from {} at {}".format(self.from_cp, self.to_cp)
@property
def threat_description(self):
return "{} aircraft".format(self.enemy_cp.base.scramble_count(self.game.settings.multiplier, CAP))
def is_successfull(self, debriefing: Debriefing):
units_destroyed = debriefing.destroyed_units[self.defender_name].get(self.transport_unit, 0)
if self.from_cp.captured:
return units_destroyed > 0
else:
return units_destroyed == 0
def commit(self, debriefing: Debriefing):
super(InterceptEvent, self).commit(debriefing)
if self.attacker_name == self.game.player:
if self.is_successfull(debriefing):
for _, cp in self.game.theater.conflicts(True):
cp.base.affect_strength(-self.STRENGTH_INFLUENCE)
else:
self.from_cp.base.affect_strength(-self.STRENGTH_INFLUENCE)
else:
# enemy attacking
if self.is_successfull(debriefing):
self.from_cp.base.affect_strength(-self.STRENGTH_INFLUENCE)
else:
self.to_cp.base.affect_strength(-self.STRENGTH_INFLUENCE)
def skip(self):
if self.to_cp.captured:
self.to_cp.base.affect_strength(-self.STRENGTH_INFLUENCE)
def player_attacking(self, interceptors: db.PlaneDict, clients: db.PlaneDict):
escort = self.to_cp.base.scramble_sweep(self.game.settings.multiplier)
self.transport_unit = random.choice(db.find_unittype(Transport, self.defender_name))
assert self.transport_unit is not None
airdefense_unit = db.find_unittype(AirDefence, self.defender_name)[-1]
op = InterceptOperation(game=self.game,
attacker_name=self.attacker_name,
defender_name=self.defender_name,
attacker_clients=clients,
defender_clients={},
from_cp=self.from_cp,
to_cp=self.to_cp)
op.setup(escort=escort,
transport={self.transport_unit: 1},
airdefense={airdefense_unit: self.AIRDEFENSE_COUNT},
interceptors=interceptors)
self.operation = op
def player_defending(self, escort: db.PlaneDict, clients: db.PlaneDict):
# TODO: even not quick mission is too quick
interceptors = self.from_cp.base.scramble_interceptors(self.game.settings.multiplier)
self.transport_unit = random.choice(db.find_unittype(Transport, self.defender_name))
assert self.transport_unit is not None
op = InterceptOperation(game=self.game,
attacker_name=self.attacker_name,
defender_name=self.defender_name,
attacker_clients={},
defender_clients=clients,
from_cp=self.from_cp,
to_cp=self.to_cp)
op.setup(escort=escort,
transport={self.transport_unit: 1},
interceptors=interceptors,
airdefense={})
self.operation = op

View File

@@ -1,108 +0,0 @@
import typing
import math
import random
from dcs.task import *
from dcs.vehicles import *
from game import db
from game.operation.navalintercept import NavalInterceptionOperation
from userdata.debriefing import Debriefing
from .event import Event
class NavalInterceptEvent(Event):
STRENGTH_INFLUENCE = 0.3
SUCCESS_RATE = 0.5
targets = None # type: db.ShipDict
def _targets_count(self) -> int:
from gen.conflictgen import IMPORTANCE_LOW, IMPORTANCE_HIGH
factor = (self.to_cp.importance - IMPORTANCE_LOW) * 10
return max(int(factor), 1)
def __str__(self) -> str:
return "Naval intercept at {}".format(self.to_cp)
@property
def threat_description(self):
s = "{} ship(s)".format(self._targets_count())
if not self.from_cp.captured:
s += ", {} aircraft".format(self.from_cp.base.scramble_count(self.game.settings.multiplier))
return s
def is_successfull(self, debriefing: Debriefing):
total_targets = sum(self.targets.values())
destroyed_targets = 0
for unit, count in debriefing.destroyed_units[self.defender_name].items():
if unit in self.targets:
destroyed_targets += count
if self.from_cp.captured:
return math.ceil(float(destroyed_targets) / total_targets) > self.SUCCESS_RATE
else:
return math.ceil(float(destroyed_targets) / total_targets) < self.SUCCESS_RATE
def commit(self, debriefing: Debriefing):
super(NavalInterceptEvent, self).commit(debriefing)
if self.attacker_name == self.game.player:
if self.is_successfull(debriefing):
self.to_cp.base.affect_strength(-self.STRENGTH_INFLUENCE)
else:
self.from_cp.base.affect_strength(-self.STRENGTH_INFLUENCE)
else:
# enemy attacking
if self.is_successfull(debriefing):
self.from_cp.base.affect_strength(-self.STRENGTH_INFLUENCE)
else:
self.to_cp.base.affect_strength(-self.STRENGTH_INFLUENCE)
def skip(self):
if self.to_cp.captured:
self.to_cp.base.affect_strength(-self.STRENGTH_INFLUENCE)
def player_attacking(self, strikegroup: db.PlaneDict, clients: db.PlaneDict):
self.targets = {
random.choice(db.find_unittype(CargoTransportation, self.defender_name)): self._targets_count(),
}
op = NavalInterceptionOperation(
self.game,
attacker_name=self.attacker_name,
defender_name=self.defender_name,
attacker_clients=clients,
defender_clients={},
from_cp=self.from_cp,
to_cp=self.to_cp
)
op.setup(strikegroup=strikegroup,
interceptors={},
targets=self.targets)
self.operation = op
def player_defending(self, interceptors: db.PlaneDict, clients: db.PlaneDict):
self.targets = {
random.choice(db.find_unittype(CargoTransportation, self.defender_name)): self._targets_count(),
}
op = NavalInterceptionOperation(
self.game,
attacker_name=self.attacker_name,
defender_name=self.defender_name,
attacker_clients={},
defender_clients=clients,
from_cp=self.from_cp,
to_cp=self.to_cp
)
strikegroup = self.from_cp.base.scramble_cas(self.game.settings.multiplier)
op.setup(strikegroup=strikegroup,
interceptors=interceptors,
targets=self.targets)
self.operation = op

307
game/factions/faction.py Normal file
View File

@@ -0,0 +1,307 @@
from __future__ import annotations
import logging
from dataclasses import dataclass, field
from typing import Optional, Dict, Type, List, Any, cast
import dcs
from dcs.countries import country_dict
from dcs.planes import plane_map
from dcs.unittype import FlyingType, ShipType, VehicleType, UnitType
from dcs.vehicles import Armor, Unarmed, Infantry, Artillery, AirDefence
from game.data.building_data import (
WW2_ALLIES_BUILDINGS,
DEFAULT_AVAILABLE_BUILDINGS,
WW2_GERMANY_BUILDINGS,
WW2_FREE,
)
from game.data.doctrine import (
Doctrine,
MODERN_DOCTRINE,
COLDWAR_DOCTRINE,
WWII_DOCTRINE,
)
from pydcs_extensions.mod_units import MODDED_VEHICLES, MODDED_AIRPLANES
@dataclass
class Faction:
# Country used by this faction
country: str = field(default="")
# Nice name of the faction
name: str = field(default="")
# List of faction file authors
authors: str = field(default="")
# A description of the faction
description: str = field(default="")
# Available aircraft
aircrafts: List[Type[FlyingType]] = field(default_factory=list)
# Available awacs aircraft
awacs: List[Type[FlyingType]] = field(default_factory=list)
# Available tanker aircraft
tankers: List[Type[FlyingType]] = field(default_factory=list)
# Available frontline units
frontline_units: List[Type[VehicleType]] = field(default_factory=list)
# Available artillery units
artillery_units: List[Type[VehicleType]] = field(default_factory=list)
# Infantry units used
infantry_units: List[Type[VehicleType]] = field(default_factory=list)
# Logistics units used
logistics_units: List[Type[VehicleType]] = field(default_factory=list)
# Possible SAMS site generators for this faction
air_defenses: List[str] = field(default_factory=list)
# Possible EWR generators for this faction.
ewrs: List[str] = field(default_factory=list)
# Possible Missile site generators for this faction
missiles: List[str] = field(default_factory=list)
# Possible costal site generators for this faction
coastal_defenses: List[str] = field(default_factory=list)
# Required mods or asset packs
requirements: Dict[str, str] = field(default_factory=dict)
# possible aircraft carrier units
aircraft_carrier: List[Type[UnitType]] = field(default_factory=list)
# possible helicopter carrier units
helicopter_carrier: List[Type[UnitType]] = field(default_factory=list)
# Possible carrier names
carrier_names: List[str] = field(default_factory=list)
# Possible helicopter carrier names
helicopter_carrier_names: List[str] = field(default_factory=list)
# Navy group generators
navy_generators: List[str] = field(default_factory=list)
# Available destroyers
destroyers: List[Type[ShipType]] = field(default_factory=list)
# Available cruisers
cruisers: List[Type[ShipType]] = field(default_factory=list)
# How many navy group should we try to generate per CP on startup for this faction
navy_group_count: int = field(default=1)
# How many missiles group should we try to generate per CP on startup for this faction
missiles_group_count: int = field(default=1)
# How many coastal group should we try to generate per CP on startup for this faction
coastal_group_count: int = field(default=1)
# Whether this faction has JTAC access
has_jtac: bool = field(default=False)
# Unit to use as JTAC for this faction
jtac_unit: Optional[Type[FlyingType]] = field(default=None)
# doctrine
doctrine: Doctrine = field(default=MODERN_DOCTRINE)
# List of available buildings for this faction
building_set: List[str] = field(default_factory=list)
# List of default livery overrides
liveries_overrides: Dict[Type[UnitType], List[str]] = field(default_factory=dict)
#: Set to True if the faction should force the "Unrestricted satnav" option
#: for the mission. This option enables GPS for capable aircraft regardless
#: of the time period or operator. For example, the CJTF "countries" don't
#: appear to have GPS capability, so they need this.
#:
#: Note that this option cannot be set per-side. If either faction needs it,
#: both will use it.
unrestricted_satnav: bool = False
@classmethod
def from_json(cls: Type[Faction], json: Dict[str, Any]) -> Faction:
faction = Faction()
faction.country = json.get("country", "/")
if faction.country not in [c.name for c in country_dict.values()]:
raise AssertionError(
'Faction\'s country ("{}") is not a valid DCS country ID'.format(
faction.country
)
)
faction.name = json.get("name", "")
if not faction.name:
raise AssertionError("Faction has no valid name")
faction.authors = json.get("authors", "")
faction.description = json.get("description", "")
faction.aircrafts = load_all_aircraft(json.get("aircrafts", []))
faction.awacs = load_all_aircraft(json.get("awacs", []))
faction.tankers = load_all_aircraft(json.get("tankers", []))
faction.frontline_units = load_all_vehicles(json.get("frontline_units", []))
faction.artillery_units = load_all_vehicles(json.get("artillery_units", []))
faction.infantry_units = load_all_vehicles(json.get("infantry_units", []))
faction.logistics_units = load_all_vehicles(json.get("logistics_units", []))
faction.ewrs = json.get("ewrs", [])
faction.air_defenses = json.get("air_defenses", [])
# Compatibility for older factions. All air defenses now belong to a
# single group and the generator decides what belongs where.
faction.air_defenses.extend(json.get("sams", []))
faction.air_defenses.extend(json.get("shorads", []))
faction.missiles = json.get("missiles", [])
faction.coastal_defenses = json.get("coastal_defenses", [])
faction.requirements = json.get("requirements", {})
faction.carrier_names = json.get("carrier_names", [])
faction.helicopter_carrier_names = json.get("helicopter_carrier_names", [])
faction.navy_generators = json.get("navy_generators", [])
faction.aircraft_carrier = load_all_ships(json.get("aircraft_carrier", []))
faction.helicopter_carrier = load_all_ships(json.get("helicopter_carrier", []))
faction.destroyers = load_all_ships(json.get("destroyers", []))
faction.cruisers = load_all_ships(json.get("cruisers", []))
faction.has_jtac = json.get("has_jtac", False)
jtac_name = json.get("jtac_unit", None)
if jtac_name is not None:
faction.jtac_unit = load_aircraft(jtac_name)
else:
faction.jtac_unit = None
faction.navy_group_count = int(json.get("navy_group_count", 1))
faction.missiles_group_count = int(json.get("missiles_group_count", 0))
faction.coastal_group_count = int(json.get("coastal_group_count", 0))
# Load doctrine
doctrine = json.get("doctrine", "modern")
if doctrine == "modern":
faction.doctrine = MODERN_DOCTRINE
elif doctrine == "coldwar":
faction.doctrine = COLDWAR_DOCTRINE
elif doctrine == "ww2":
faction.doctrine = WWII_DOCTRINE
else:
faction.doctrine = MODERN_DOCTRINE
# Load the building set
building_set = json.get("building_set", "default")
if building_set == "default":
faction.building_set = DEFAULT_AVAILABLE_BUILDINGS
elif building_set == "ww2free":
faction.building_set = WW2_FREE
elif building_set == "ww2ally":
faction.building_set = WW2_ALLIES_BUILDINGS
elif building_set == "ww2germany":
faction.building_set = WW2_GERMANY_BUILDINGS
else:
faction.building_set = DEFAULT_AVAILABLE_BUILDINGS
# Load liveries override
faction.liveries_overrides = {}
liveries_overrides = json.get("liveries_overrides", {})
for k, v in liveries_overrides.items():
k = load_aircraft(k)
if k is not None:
faction.liveries_overrides[k] = [s.lower() for s in v]
faction.unrestricted_satnav = json.get("unrestricted_satnav", False)
return faction
@property
def units(self) -> List[Type[UnitType]]:
return (
self.infantry_units
+ self.aircrafts
+ self.awacs
+ self.artillery_units
+ self.frontline_units
+ self.tankers
+ self.logistics_units
)
def unit_loader(unit: str, class_repository: List[Any]) -> Optional[Type[UnitType]]:
"""
Find unit by name
:param unit: Unit name as string
:param class_repository: Repository of classes (Either a module, a class, or a list of classes)
:return: The unit as a PyDCS type
"""
if unit is None:
return None
elif unit in plane_map.keys():
return plane_map[unit]
else:
for mother_class in class_repository:
if getattr(mother_class, unit, None) is not None:
return getattr(mother_class, unit)
if type(mother_class) is list:
for m in mother_class:
if m.__name__ == unit:
return m
logging.error(f"FACTION ERROR : Unable to find {unit} in pydcs")
return None
def load_aircraft(name: str) -> Optional[Type[FlyingType]]:
return cast(
Optional[FlyingType],
unit_loader(name, [dcs.planes, dcs.helicopters, MODDED_AIRPLANES]),
)
def load_all_aircraft(data) -> List[Type[FlyingType]]:
items = []
for name in data:
item = load_aircraft(name)
if item is not None:
items.append(item)
return items
def load_vehicle(name: str) -> Optional[Type[VehicleType]]:
return cast(
Optional[FlyingType],
unit_loader(
name, [Infantry, Unarmed, Armor, AirDefence, Artillery, MODDED_VEHICLES]
),
)
def load_all_vehicles(data) -> List[Type[VehicleType]]:
items = []
for name in data:
item = load_vehicle(name)
if item is not None:
items.append(item)
return items
def load_ship(name: str) -> Optional[Type[ShipType]]:
return cast(Optional[FlyingType], unit_loader(name, [dcs.ships]))
def load_all_ships(data) -> List[Type[ShipType]]:
items = []
for name in data:
item = load_ship(name)
if item is not None:
items.append(item)
return items

View File

@@ -0,0 +1,46 @@
from __future__ import annotations
import json
import logging
from pathlib import Path
from typing import Dict, Iterator, Optional, Type
from game.factions.faction import Faction
FACTION_DIRECTORY = Path("./resources/factions/")
class FactionLoader:
def __init__(self) -> None:
self._factions: Optional[Dict[str, Faction]] = None
@property
def factions(self) -> Dict[str, Faction]:
self.initialize()
assert self._factions is not None
return self._factions
def initialize(self) -> None:
if self._factions is None:
self._factions = self.load_factions()
@classmethod
def load_factions(cls: Type[FactionLoader]) -> Dict[str, Faction]:
files = [f for f in FACTION_DIRECTORY.glob("*.json") if f.is_file()]
factions = {}
for f in files:
try:
with f.open("r", encoding="utf-8") as fdata:
data = json.load(fdata)
factions[data["name"]] = Faction.from_json(data)
logging.info("Loaded faction : " + str(f))
except Exception:
logging.exception(f"Unable to load faction : {f}")
return factions
def __getitem__(self, name: str) -> Faction:
return self.factions[name]
def __iter__(self) -> Iterator[str]:
return iter(self.factions.keys())

View File

@@ -1,16 +1,41 @@
import typing
import itertools
import logging
import random
import math
import sys
from datetime import date, datetime, timedelta
from enum import Enum
from typing import Any, Dict, List
from dcs.task import *
from dcs.vehicles import *
from dcs.action import Coalition
from dcs.mapping import Point
from dcs.task import CAP, CAS, PinpointStrike
from dcs.vehicles import AirDefence
from userdata.debriefing import Debriefing
from theater import *
from . import db
from game import db
from game.inventory import GlobalAircraftInventory
from game.models.game_stats import GameStats
from game.plugins import LuaPluginManager
from gen.ato import AirTaskingOrder
from gen.conflictgen import Conflict
from gen.flights.ai_flight_planner import CoalitionMissionPlanner
from gen.flights.closestairfields import ObjectiveDistanceCache
from gen.flights.flight import FlightType
from gen.ground_forces.ai_ground_planner import GroundPlanner
from . import persistency
from .debriefing import Debriefing
from .event.event import Event, UnitsDeliveryEvent
from .event.frontlineattack import FrontlineAttackEvent
from .factions.faction import Faction
from .income import Income
from .infos.information import Information
from .navmesh import NavMesh
from .procurement import ProcurementAi
from .settings import Settings
from .event import *
from .theater import ConflictTheater, ControlPoint, TheaterGroundObject
from game.theater.theatergroundobject import MissileSiteGroundObject
from .threatzones import ThreatZones
from .unitmap import UnitMap
from .weather import Conditions, TimeOfDay
COMMISION_UNIT_VARIETY = 4
COMMISION_LIMITS_SCALE = 1.5
@@ -18,42 +43,20 @@ COMMISION_LIMITS_FACTORS = {
PinpointStrike: 10,
CAS: 5,
CAP: 8,
AirDefence: 1,
AirDefence: 8,
}
COMMISION_AMOUNTS_SCALE = 1.5
COMMISION_AMOUNTS_FACTORS = {
PinpointStrike: 2,
PinpointStrike: 3,
CAS: 1,
CAP: 2,
AirDefence: 0.3,
AirDefence: 0.8,
}
PLAYER_INTERCEPT_GLOBAL_PROBABILITY_BASE = 25
PLAYER_INTERCEPT_GLOBAL_PROBABILITY_BASE = 30
PLAYER_INTERCEPT_GLOBAL_PROBABILITY_LOG = 2
"""
Various events probabilities. First key is player probabilty, second is enemy probability.
For the enemy events, only 1 event of each type could be generated for a turn.
Events:
* CaptureEvent - capture base
* InterceptEvent - air intercept
* GroundInterceptEvent - frontline CAS
* GroundAttackEvent - destroy insurgents
* NavalInterceptEvent - naval intercept
* AntiAAStrikeEvent - anti-AA strike
* InfantryTransportEvent - helicopter infantry transport
"""
EVENT_PROBABILITIES = {
CaptureEvent: [100, 10],
InterceptEvent: [25, 10],
GroundInterceptEvent: [25, 10],
GroundAttackEvent: [0, 10],
NavalInterceptEvent: [25, 10],
AntiAAStrikeEvent: [25, 10],
InfantryTransportEvent: [25, 0],
}
PLAYER_BASEATTACK_THRESHOLD = 0.4
# amount of strength player bases recover for the turn
PLAYER_BASE_STRENGTH_RECOVERY = 0.2
@@ -64,170 +67,485 @@ ENEMY_BASE_STRENGTH_RECOVERY = 0.05
# cost of AWACS for single operation
AWACS_BUDGET_COST = 4
# Initial budget value
PLAYER_BUDGET_INITIAL = 120
# Base post-turn bonus value
PLAYER_BUDGET_BASE = 10
# Bonus multiplier logarithm base
PLAYER_BUDGET_IMPORTANCE_LOG = 2
class TurnState(Enum):
WIN = 0
LOSS = 1
CONTINUE = 2
class Game:
settings = None # type: Settings
budget = PLAYER_BUDGET_INITIAL
events = None # type: typing.List[Event]
pending_transfers = None # type: typing.Dict[]
ignored_cps = None # type: typing.Collection[ControlPoint]
def __init__(self, player_name: str, enemy_name: str, theater: ConflictTheater):
self.settings = Settings()
self.events = []
def __init__(
self,
player_name: str,
enemy_name: str,
theater: ConflictTheater,
start_date: datetime,
settings: Settings,
player_budget: float,
enemy_budget: float,
) -> None:
self.settings = settings
self.events: List[Event] = []
self.theater = theater
self.player = player_name
self.enemy = enemy_name
self.player_name = player_name
self.player_country = db.FACTIONS[player_name].country
self.enemy_name = enemy_name
self.enemy_country = db.FACTIONS[enemy_name].country
self.turn = 0
# NB: This is the *start* date. It is never updated.
self.date = date(start_date.year, start_date.month, start_date.day)
self.game_stats = GameStats()
self.game_stats.update(self)
self.ground_planners: Dict[int, GroundPlanner] = {}
self.informations = []
self.informations.append(Information("Game Start", "-" * 40, 0))
# Culling Zones are for areas around points of interest that contain things we may not wish to cull.
self.__culling_zones: List[Point] = []
# Culling Points are for individual theater ground objects that we don't wish to cull.
self.__culling_points: List[Point] = []
self.__destroyed_units: List[str] = []
self.savepath = ""
self.budget = player_budget
self.enemy_budget = enemy_budget
self.current_unit_id = 0
self.current_group_id = 0
def _roll(self, prob, mult):
return random.randint(1, 100) <= prob * mult
self.conditions = self.generate_conditions()
def _generate_globalinterceptions(self):
global_count = len([x for x in self.theater.player_points() if x.is_global])
for from_cp in [x for x in self.theater.player_points() if x.is_global]:
probability_base = max(PLAYER_INTERCEPT_GLOBAL_PROBABILITY_BASE / global_count, 1)
probability = probability_base * math.log(len(self.theater.player_points()) + 1, PLAYER_INTERCEPT_GLOBAL_PROBABILITY_LOG)
if self._roll(probability, from_cp.base.strength):
to_cp = random.choice([x for x in self.theater.enemy_points() if x not in self.theater.conflicts()])
self.events.append(InterceptEvent(attacker_name=self.player,
defender_name=self.enemy,
from_cp=from_cp,
to_cp=to_cp,
game=self))
break
self.blue_ato = AirTaskingOrder()
self.red_ato = AirTaskingOrder()
def _generate_events(self):
enemy_cap_generated = False
enemy_generated_types = []
self.aircraft_inventory = GlobalAircraftInventory(self.theater.controlpoints)
for player_cp, enemy_cp in self.theater.conflicts(True):
if player_cp.is_global or enemy_cp.is_global:
continue
self.sanitize_sides()
for event_class, (player_probability, enemy_probability) in EVENT_PROBABILITIES.items():
if self._roll(player_probability, player_cp.base.strength):
if event_class == NavalInterceptEvent and enemy_cp.radials == LAND:
pass
else:
self.events.append(event_class(self.player, self.enemy, player_cp, enemy_cp, self))
elif self._roll(enemy_probability, enemy_cp.base.strength):
if event_class in enemy_generated_types:
continue
self.on_load()
if player_cp in self.ignored_cps:
continue
# Turn 0 procurement. We don't actually have any missions to plan, but
# the planner will tell us what it would like to plan so we can use that
# to drive purchase decisions.
blue_planner = CoalitionMissionPlanner(self, is_player=True)
blue_planner.plan_missions()
if enemy_cp.base.total_planes == 0:
continue
red_planner = CoalitionMissionPlanner(self, is_player=False)
red_planner.plan_missions()
if event_class == NavalInterceptEvent:
if player_cp.radials == LAND:
continue
elif event_class == CaptureEvent:
if enemy_cap_generated:
continue
if enemy_cp.base.total_armor == 0:
continue
enemy_cap_generated = True
elif event_class == AntiAAStrikeEvent:
if player_cp.base.total_aa == 0:
continue
self.plan_procurement(blue_planner, red_planner)
enemy_generated_types.append(event_class)
self.events.append(event_class(self.enemy, self.player, enemy_cp, player_cp, self))
def __getstate__(self) -> Dict[str, Any]:
state = self.__dict__.copy()
# Avoid persisting any volatile types that can be deterministically
# recomputed on load for the sake of save compatibility.
del state["blue_threat_zone"]
del state["red_threat_zone"]
del state["blue_navmesh"]
del state["red_navmesh"]
return state
def commision_unit_types(self, cp: ControlPoint, for_task: Task) -> typing.Collection[UnitType]:
importance_factor = (cp.importance - IMPORTANCE_LOW) / (IMPORTANCE_HIGH - IMPORTANCE_LOW)
def __setstate__(self, state: Dict[str, Any]) -> None:
self.__dict__.update(state)
# Regenerate any state that was not persisted.
self.on_load()
if for_task == AirDefence and not self.settings.sams:
return [x for x in db.find_unittype(AirDefence, self.enemy) if x not in db.SAM_BAN]
else:
return db.choose_units(for_task, importance_factor, COMMISION_UNIT_VARIETY, self.enemy)
def generate_conditions(self) -> Conditions:
return Conditions.generate(
self.theater, self.current_day, self.current_turn_time_of_day, self.settings
)
def _commision_units(self, cp: ControlPoint):
for for_task in [PinpointStrike, CAS, CAP, AirDefence]:
limit = COMMISION_LIMITS_FACTORS[for_task] * math.pow(cp.importance, COMMISION_LIMITS_SCALE) * self.settings.multiplier
missing_units = limit - cp.base.total_units(for_task)
if missing_units > 0:
awarded_points = COMMISION_AMOUNTS_FACTORS[for_task] * math.pow(cp.importance, COMMISION_AMOUNTS_SCALE) * self.settings.multiplier
points_to_spend = cp.base.append_commision_points(for_task, awarded_points)
if points_to_spend > 0:
unittypes = self.commision_unit_types(cp, for_task)
d = {random.choice(unittypes): points_to_spend}
print("Commision {}: {}".format(cp, d))
cp.base.commision_units(d)
def sanitize_sides(self):
"""
Make sure the opposing factions are using different countries
:return:
"""
if self.player_country == self.enemy_country:
if self.player_country == "USA":
self.enemy_country = "USAF Aggressors"
elif self.player_country == "Russia":
self.enemy_country = "USSR"
else:
self.enemy_country = "Russia"
@property
def budget_reward_amount(self):
if len(self.theater.player_points()) > 0:
total_importance = sum([x.importance * x.base.strength for x in self.theater.player_points()])
return math.ceil(math.log(total_importance + 1, PLAYER_BUDGET_IMPORTANCE_LOG) * PLAYER_BUDGET_BASE * self.settings.multiplier)
def player_faction(self) -> Faction:
return db.FACTIONS[self.player_name]
@property
def enemy_faction(self) -> Faction:
return db.FACTIONS[self.enemy_name]
def faction_for(self, player: bool) -> Faction:
if player:
return self.player_faction
return self.enemy_faction
def _roll(self, prob, mult):
if self.settings.version == "dev":
# always generate all events for dev
return 100
else:
return 0
return random.randint(1, 100) <= prob * mult
def _budget_player(self):
self.budget += self.budget_reward_amount
def _generate_player_event(self, event_class, player_cp, enemy_cp):
self.events.append(
event_class(
self,
player_cp,
enemy_cp,
enemy_cp.position,
self.player_name,
self.enemy_name,
)
)
def awacs_expense_commit(self):
self.budget -= AWACS_BUDGET_COST
def _generate_events(self):
for front_line in self.theater.conflicts(True):
self._generate_player_event(
FrontlineAttackEvent,
front_line.control_point_a,
front_line.control_point_b,
)
def units_delivery_event(self, to_cp: ControlPoint) -> UnitsDeliveryEvent:
event = UnitsDeliveryEvent(attacker_name=self.player,
defender_name=self.player,
from_cp=to_cp,
to_cp=to_cp,
game=self)
self.events.append(event)
return event
def adjust_budget(self, amount: float, player: bool) -> None:
if player:
self.budget += amount
else:
self.enemy_budget += amount
def units_delivery_remove(self, event: Event):
if event in self.events:
self.events.remove(event)
def process_player_income(self):
self.budget += Income(self, player=True).total
def initiate_event(self, event: Event):
assert event in self.events
def process_enemy_income(self):
# TODO: Clean up save compat.
if not hasattr(self, "enemy_budget"):
self.enemy_budget = 0
self.enemy_budget += Income(self, player=False).total
event.generate()
event.generate_quick()
def initiate_event(self, event: Event) -> UnitMap:
# assert event in self.events
logging.info("Generating {} (regular)".format(event))
return event.generate()
def finish_event(self, event: Event, debriefing: Debriefing):
logging.info("Finishing event {}".format(event))
event.commit(debriefing)
if event.is_successfull(debriefing):
self.budget += event.bonus()
if event in self.events:
self.events.remove(event)
else:
print("finish_event: event not in the events!")
logging.info("finish_event: event not in the events!")
def is_player_attack(self, event: Event):
return event.attacker_name == self.player
def is_player_attack(self, event):
if isinstance(event, Event):
return (
event
and event.attacker_name
and event.attacker_name == self.player_name
)
else:
raise RuntimeError(f"{event} was passed when an Event type was expected")
def pass_turn(self, no_action=False, ignored_cps: typing.Collection[ControlPoint]=None):
for event in self.events:
event.skip()
def on_load(self) -> None:
LuaPluginManager.load_settings(self.settings)
ObjectiveDistanceCache.set_theater(self.theater)
self.compute_conflicts_position()
self.compute_threat_zones()
if not no_action:
self._budget_player()
def pass_turn(self, no_action: bool = False) -> None:
logging.info("Pass turn")
self.informations.append(
Information("End of turn #" + str(self.turn), "-" * 40, 0)
)
self.turn += 1
for cp in self.theater.enemy_points():
self._commision_units(cp)
for control_point in self.theater.controlpoints:
control_point.process_turn(self)
self.process_enemy_income()
self.process_player_income()
if not no_action and self.turn > 1:
for cp in self.theater.player_points():
cp.base.affect_strength(+PLAYER_BASE_STRENGTH_RECOVERY)
else:
for cp in self.theater.player_points():
if not cp.is_carrier and not cp.is_lha:
cp.base.affect_strength(-PLAYER_BASE_STRENGTH_RECOVERY)
self.ignored_cps = []
if ignored_cps:
self.ignored_cps = ignored_cps
self.conditions = self.generate_conditions()
self.events = [] # type: typing.List[Event]
self.initialize_turn()
# Autosave progress
persistency.autosave(self)
def check_win_loss(self):
player_airbases = {
cp for cp in self.theater.player_points() if cp.runway_is_operational()
}
if not player_airbases:
return TurnState.LOSS
enemy_airbases = {
cp for cp in self.theater.enemy_points() if cp.runway_is_operational()
}
if not enemy_airbases:
return TurnState.WIN
return TurnState.CONTINUE
def initialize_turn(self) -> None:
self.events = []
self._generate_events()
self._generate_globalinterceptions()
# Update statistics
self.game_stats.update(self)
self.aircraft_inventory.reset()
for cp in self.theater.controlpoints:
self.aircraft_inventory.set_from_control_point(cp)
# Check for win or loss condition
turn_state = self.check_win_loss()
if turn_state in (TurnState.LOSS, TurnState.WIN):
return self.process_win_loss(turn_state)
# Plan flights & combat for next turn
self.compute_conflicts_position()
self.compute_threat_zones()
self.ground_planners = {}
self.blue_ato.clear()
self.red_ato.clear()
blue_planner = CoalitionMissionPlanner(self, is_player=True)
blue_planner.plan_missions()
red_planner = CoalitionMissionPlanner(self, is_player=False)
red_planner.plan_missions()
for cp in self.theater.controlpoints:
if cp.has_frontline:
gplanner = GroundPlanner(cp, self)
gplanner.plan_groundwar()
self.ground_planners[cp.id] = gplanner
self.plan_procurement(blue_planner, red_planner)
def plan_procurement(
self,
blue_planner: CoalitionMissionPlanner,
red_planner: CoalitionMissionPlanner,
) -> None:
# The first turn needs to buy a *lot* of aircraft to fill CAPs, so it
# gets much more of the budget that turn. Otherwise budget (after
# repairs) is split evenly between air and ground. For the default
# starting budget of 2000 this gives 600 to ground forces and 1400 to
# aircraft.
ground_portion = 0.3 if self.turn == 0 else 0.5
self.budget = ProcurementAi(
self,
for_player=True,
faction=self.player_faction,
manage_runways=self.settings.automate_runway_repair,
manage_front_line=self.settings.automate_front_line_reinforcements,
manage_aircraft=self.settings.automate_aircraft_reinforcements,
front_line_budget_share=ground_portion,
).spend_budget(self.budget, blue_planner.procurement_requests)
self.enemy_budget = ProcurementAi(
self,
for_player=False,
faction=self.enemy_faction,
manage_runways=True,
manage_front_line=True,
manage_aircraft=True,
front_line_budget_share=ground_portion,
).spend_budget(self.enemy_budget, red_planner.procurement_requests)
def message(self, text: str) -> None:
self.informations.append(Information(text, turn=self.turn))
@property
def current_turn_time_of_day(self) -> TimeOfDay:
return list(TimeOfDay)[self.turn % 4]
@property
def current_day(self) -> date:
return self.date + timedelta(days=self.turn // 4)
def next_unit_id(self):
"""
Next unit id for pre-generated units
"""
self.current_unit_id += 1
return self.current_unit_id
def next_group_id(self):
"""
Next unit id for pre-generated units
"""
self.current_group_id += 1
return self.current_group_id
def compute_threat_zones(self) -> None:
self.blue_threat_zone = ThreatZones.for_faction(self, player=True)
self.red_threat_zone = ThreatZones.for_faction(self, player=False)
self.blue_navmesh = NavMesh.from_threat_zones(
self.red_threat_zone, self.theater
)
self.red_navmesh = NavMesh.from_threat_zones(
self.blue_threat_zone, self.theater
)
def threat_zone_for(self, player: bool) -> ThreatZones:
if player:
return self.blue_threat_zone
return self.red_threat_zone
def navmesh_for(self, player: bool) -> NavMesh:
if player:
return self.blue_navmesh
return self.red_navmesh
def compute_conflicts_position(self):
"""
Compute the current conflict center position(s), mainly used for culling calculation
:return: List of points of interests
"""
zones = []
points = []
# By default, use the existing frontline conflict position
for front_line in self.theater.conflicts():
position = Conflict.frontline_position(
front_line.control_point_a, front_line.control_point_b, self.theater
)
zones.append(position[0])
zones.append(front_line.control_point_a.position)
zones.append(front_line.control_point_b.position)
for cp in self.theater.controlpoints:
# Don't cull missile sites - their range is long enough to make them
# easily culled despite being a threat.
for tgo in cp.ground_objects:
if isinstance(tgo, MissileSiteGroundObject):
points.append(tgo.position)
# If do_not_cull_carrier is enabled, add carriers as culling point
if self.settings.perf_do_not_cull_carrier:
if cp.is_carrier or cp.is_lha:
zones.append(cp.position)
# If there is no conflict take the center point between the two nearest opposing bases
if len(zones) == 0:
cpoint = None
min_distance = sys.maxsize
for cp in self.theater.player_points():
for cp2 in self.theater.enemy_points():
d = cp.position.distance_to_point(cp2.position)
if d < min_distance:
min_distance = d
cpoint = Point(
(cp.position.x + cp2.position.x) / 2,
(cp.position.y + cp2.position.y) / 2,
)
zones.append(cp.position)
zones.append(cp2.position)
break
if cpoint is not None:
break
if cpoint is not None:
zones.append(cpoint)
packages = itertools.chain(self.blue_ato.packages, self.red_ato.packages)
for package in packages:
if package.primary_task is FlightType.BARCAP:
# BARCAPs will be planned at most locations on smaller theaters,
# rendering culling fairly useless. BARCAP packages don't really
# need the ground detail since they're defensive. SAMs nearby
# are only interesting if there are enemies in the area, and if
# there are they won't be culled because of the enemy's mission.
continue
zones.append(package.target.position)
# Else 0,0, since we need a default value
# (in this case this means the whole map is owned by the same player, so it is not an issue)
if len(zones) == 0:
zones.append(Point(0, 0))
self.__culling_zones = zones
self.__culling_points = points
def add_destroyed_units(self, data):
pos = Point(data["x"], data["z"])
if self.theater.is_on_land(pos):
self.__destroyed_units.append(data)
def get_destroyed_units(self):
return self.__destroyed_units
def position_culled(self, pos):
"""
Check if unit can be generated at given position depending on culling performance settings
:param pos: Position you are tryng to spawn stuff at
:return: True if units can not be added at given position
"""
if self.settings.perf_culling == False:
return False
else:
for z in self.__culling_zones:
if (
z.distance_to_point(pos)
< self.settings.perf_culling_distance * 1000
):
return False
for p in self.__culling_points:
if p.distance_to_point(pos) < 2500:
return False
return True
def get_culling_zones(self):
"""
Check culling points
:return: List of culling zones
"""
return self.__culling_zones
def get_culling_points(self):
"""
Check culling points
:return: List of culling points
"""
return self.__culling_points
# 1 = red, 2 = blue
def get_player_coalition_id(self):
return 2
def get_enemy_coalition_id(self):
return 1
def get_player_coalition(self):
return Coalition.Blue
def get_enemy_coalition(self):
return Coalition.Red
def get_player_color(self):
return "blue"
def get_enemy_color(self):
return "red"
def process_win_loss(self, turn_state: TurnState):
if turn_state is TurnState.WIN:
return self.message(
"Congratulations, you are victorious! Start a new campaign to continue."
)
elif turn_state is TurnState.LOSS:
return self.message(
"Game Over, you lose. Start a new campaign to continue."
)

55
game/income.py Normal file
View File

@@ -0,0 +1,55 @@
from __future__ import annotations
from dataclasses import dataclass
from typing import TYPE_CHECKING
from game.db import REWARDS
if TYPE_CHECKING:
from game import Game
@dataclass(frozen=True)
class BuildingIncome:
name: str
category: str
number: int
income_per_building: int
@property
def income(self) -> int:
return self.number * self.income_per_building
class Income:
def __init__(self, game: Game, player: bool) -> None:
if player:
self.multiplier = game.settings.player_income_multiplier
else:
self.multiplier = game.settings.enemy_income_multiplier
self.control_points = []
self.buildings = []
names = set()
for cp in game.theater.control_points_for(player):
if cp.income_per_turn:
self.control_points.append(cp)
for tgo in cp.ground_objects:
names.add(tgo.obj_name)
for name in names:
count = 0
tgos = game.theater.find_ground_objects_by_obj_name(name)
category = tgos[0].category
if category not in REWARDS:
continue
for tgo in tgos:
if not tgo.is_dead:
count += 1
self.buildings.append(
BuildingIncome(name, category, count, REWARDS[category])
)
self.from_bases = sum(cp.income_per_turn for cp in self.control_points)
self.total_buildings = sum(b.income for b in self.buildings)
self.total = (self.total_buildings + self.from_bases) * self.multiplier

19
game/infos/information.py Normal file
View File

@@ -0,0 +1,19 @@
import datetime
class Information:
def __init__(self, title="", text="", turn=0):
self.title = title
self.text = text
self.turn = turn
self.timestamp = datetime.datetime.now()
def __str__(self):
return "[{}][{}] {} {}".format(
self.timestamp.strftime("%Y-%m-%d %H:%M:%S")
if self.timestamp is not None
else "",
self.turn,
self.title,
self.text,
)

130
game/inventory.py Normal file
View File

@@ -0,0 +1,130 @@
"""Inventory management APIs."""
from __future__ import annotations
from collections import defaultdict
from typing import Dict, Iterable, Iterator, Set, Tuple, TYPE_CHECKING, Type
from dcs.unittype import FlyingType
from gen.flights.flight import Flight
if TYPE_CHECKING:
from game.theater import ControlPoint
class ControlPointAircraftInventory:
"""Aircraft inventory for a single control point."""
def __init__(self, control_point: ControlPoint) -> None:
self.control_point = control_point
self.inventory: Dict[Type[FlyingType], int] = defaultdict(int)
def add_aircraft(self, aircraft: Type[FlyingType], count: int) -> None:
"""Adds aircraft to the inventory.
Args:
aircraft: The type of aircraft to add.
count: The number of aircraft to add.
"""
self.inventory[aircraft] += count
def remove_aircraft(self, aircraft: Type[FlyingType], count: int) -> None:
"""Removes aircraft from the inventory.
Args:
aircraft: The type of aircraft to remove.
count: The number of aircraft to remove.
Raises:
ValueError: The control point cannot fulfill the requested number of
aircraft.
"""
available = self.inventory[aircraft]
if available < count:
raise ValueError(
f"Cannot remove {count} {aircraft.id} from "
f"{self.control_point.name}. Only have {available}."
)
self.inventory[aircraft] -= count
def available(self, aircraft: Type[FlyingType]) -> int:
"""Returns the number of available aircraft of the given type.
Args:
aircraft: The type of aircraft to query.
"""
try:
return self.inventory[aircraft]
except KeyError:
return 0
@property
def types_available(self) -> Iterator[Type[FlyingType]]:
"""Iterates over all available aircraft types."""
for aircraft, count in self.inventory.items():
if count > 0:
yield aircraft
@property
def all_aircraft(self) -> Iterator[Tuple[Type[FlyingType], int]]:
"""Iterates over all available aircraft types, including amounts."""
for aircraft, count in self.inventory.items():
if count > 0:
yield aircraft, count
def clear(self) -> None:
"""Clears all aircraft from the inventory."""
self.inventory.clear()
class GlobalAircraftInventory:
"""Game-wide aircraft inventory."""
def __init__(self, control_points: Iterable[ControlPoint]) -> None:
self.inventories: Dict[ControlPoint, ControlPointAircraftInventory] = {
cp: ControlPointAircraftInventory(cp) for cp in control_points
}
def reset(self) -> None:
"""Clears all control points and their inventories."""
for inventory in self.inventories.values():
inventory.clear()
def set_from_control_point(self, control_point: ControlPoint) -> None:
"""Set the control point's aircraft inventory.
If the inventory for the given control point has already been set for
the turn, it will be overwritten.
"""
inventory = self.inventories[control_point]
for aircraft, count in control_point.base.aircraft.items():
inventory.add_aircraft(aircraft, count)
def for_control_point(
self, control_point: ControlPoint
) -> ControlPointAircraftInventory:
"""Returns the inventory specific to the given control point."""
return self.inventories[control_point]
@property
def available_types_for_player(self) -> Iterator[Type[FlyingType]]:
"""Iterates over all aircraft types available to the player."""
seen: Set[Type[FlyingType]] = set()
for control_point, inventory in self.inventories.items():
if control_point.captured:
for aircraft in inventory.types_available:
if not control_point.can_operate(aircraft):
continue
if aircraft not in seen:
seen.add(aircraft)
yield aircraft
def claim_for_flight(self, flight: Flight) -> None:
"""Removes aircraft from the inventory for the given flight."""
inventory = self.for_control_point(flight.from_cp)
inventory.remove_aircraft(flight.unit_type, flight.count)
def return_from_flight(self, flight: Flight) -> None:
"""Returns a flight's aircraft to the inventory."""
inventory = self.for_control_point(flight.from_cp)
inventory.add_aircraft(flight.unit_type, flight.count)

View File

@@ -0,0 +1,13 @@
class DestroyedUnit:
"""
Store info about a destroyed unit
"""
x: int
y: int
name: str
def __init__(self, x, y, name):
self.x = x
self.y = y
self.name = name

View File

@@ -0,0 +1,13 @@
from game.theater import ControlPoint
class FrontlineData:
"""
This Data structure will store information about an existing frontline
"""
def __init__(self, from_cp: ControlPoint, to_cp: ControlPoint):
self.to_cp = to_cp
self.from_cp = from_cp
self.enemy_units_position = []
self.blue_units_position = []

56
game/models/game_stats.py Normal file
View File

@@ -0,0 +1,56 @@
from typing import List
class FactionTurnMetadata:
"""
Store metadata about a faction
"""
aircraft_count: int = 0
vehicles_count: int = 0
sam_count: int = 0
def __init__(self):
self.aircraft_count = 0
self.vehicles_count = 0
self.sam_count = 0
class GameTurnMetadata:
"""
Store metadata about a game turn
"""
allied_units: FactionTurnMetadata
enemy_units: FactionTurnMetadata
def __init__(self):
self.allied_units = FactionTurnMetadata()
self.enemy_units = FactionTurnMetadata()
class GameStats:
"""
Store statistics for the current game
"""
def __init__(self):
self.data_per_turn: List[GameTurnMetadata] = []
def update(self, game):
"""
Save data for current turn
:param game: Game we want to save the data about
"""
turn_data = GameTurnMetadata()
for cp in game.theater.controlpoints:
if cp.captured:
turn_data.allied_units.aircraft_count += sum(cp.base.aircraft.values())
turn_data.allied_units.vehicles_count += sum(cp.base.armor.values())
else:
turn_data.enemy_units.aircraft_count += sum(cp.base.aircraft.values())
turn_data.enemy_units.vehicles_count += sum(cp.base.armor.values())
self.data_per_turn.append(turn_data)

273
game/navmesh.py Normal file
View File

@@ -0,0 +1,273 @@
from __future__ import annotations
import heapq
import math
from collections import defaultdict
from dataclasses import dataclass, field
from typing import Dict, List, Optional, Set, Tuple, Union
from dcs.mapping import Point
from shapely.geometry import (
LineString,
MultiPolygon,
Point as ShapelyPoint,
Polygon,
box,
)
from shapely.ops import nearest_points, triangulate
from game.theater import ConflictTheater
from game.threatzones import ThreatZones
from game.utils import nautical_miles
class NavMeshError(RuntimeError):
pass
class NavMeshPoly:
def __init__(self, ident: int, poly: Polygon, threatened: bool) -> None:
self.ident = ident
self.poly = poly
self.threatened = threatened
self.neighbors: Dict[NavMeshPoly, Union[LineString, ShapelyPoint]] = {}
def __eq__(self, other: object) -> bool:
if not isinstance(other, NavMeshPoly):
return False
return self.ident == other.ident
def __hash__(self) -> int:
return self.ident
@dataclass(frozen=True)
class NavPoint:
point: ShapelyPoint
poly: NavMeshPoly
@property
def world_point(self) -> Point:
return Point(self.point.x, self.point.y)
def __hash__(self) -> int:
return hash(self.poly.ident)
def __eq__(self, other: object) -> bool:
if id(self) == id(other):
return True
if not isinstance(other, NavPoint):
return False
if not self.point.almost_equals(other.point):
return False
return self.poly == other.poly
def __str__(self) -> str:
return f"{self.point} in {self.poly.ident}"
@dataclass(frozen=True, order=True)
class FrontierNode:
cost: float
point: NavPoint = field(compare=False)
class NavFrontier:
def __init__(self) -> None:
self.nodes: List[FrontierNode] = []
def push(self, poly: NavPoint, cost: float) -> None:
heapq.heappush(self.nodes, FrontierNode(cost, poly))
def pop(self) -> Optional[NavPoint]:
try:
return heapq.heappop(self.nodes).point
except IndexError:
return None
class NavMesh:
def __init__(self, polys: List[NavMeshPoly]) -> None:
self.polys = polys
def localize(self, point: Point) -> Optional[NavMeshPoly]:
# This is a naive implementation but it's O(n). Runs at about 10k
# lookups a second on a 5950X. Flights usually have 5-10 waypoints, so
# that's 1k-2k flights before we lose a full second to localization as a
# part of flight plan creation.
#
# Can improve the algorithm later if needed, but that seems unnecessary
# currently.
p = ShapelyPoint(point.x, point.y)
for navpoly in self.polys:
if navpoly.poly.contains(p):
return navpoly
return None
@staticmethod
def travel_cost(a: NavPoint, b: NavPoint) -> float:
modifier = 1.0
if a.poly.threatened:
modifier = 3.0
return a.point.distance(b.point) * modifier
def travel_heuristic(self, a: NavPoint, b: NavPoint) -> float:
return self.travel_cost(a, b)
@staticmethod
def reconstruct_path(
came_from: Dict[NavPoint, Optional[NavPoint]],
origin: NavPoint,
destination: NavPoint,
) -> List[Point]:
current = destination
path: List[Point] = []
while current != origin:
path.append(current.world_point)
previous = came_from[current]
if previous is None:
raise NavMeshError(
f"Could not reconstruct path to {destination} from {origin}"
)
current = previous
path.append(origin.world_point)
path.reverse()
return path
@staticmethod
def dcs_to_shapely_point(point: Point) -> ShapelyPoint:
return ShapelyPoint(point.x, point.y)
def shortest_path(self, origin: Point, destination: Point) -> List[Point]:
origin_poly = self.localize(origin)
if origin_poly is None:
raise NavMeshError(f"Origin point {origin} is outside the navmesh")
destination_poly = self.localize(destination)
if destination_poly is None:
raise NavMeshError(
f"Destination point {destination} is outside the navmesh"
)
return self._shortest_path(
NavPoint(self.dcs_to_shapely_point(origin), origin_poly),
NavPoint(self.dcs_to_shapely_point(destination), destination_poly),
)
def _shortest_path(self, origin: NavPoint, destination: NavPoint) -> List[Point]:
# Adapted from
# https://www.redblobgames.com/pathfinding/a-star/implementation.py.
frontier = NavFrontier()
frontier.push(origin, 0.0)
came_from: Dict[NavPoint, Optional[NavPoint]] = {origin: None}
best_known: Dict[NavPoint, float] = defaultdict(lambda: math.inf)
best_known[origin] = 0.0
while (current := frontier.pop()) is not None:
if current == destination:
break
if current.poly == destination.poly:
# Made it to the correct nav poly. Add the leg from the border
# to the target.
cost = best_known[current] + self.travel_cost(current, destination)
if cost < best_known[destination]:
best_known[destination] = cost
estimated = cost
frontier.push(destination, estimated)
came_from[destination] = current
for neighbor, boundary in current.poly.neighbors.items():
previous = came_from[current]
if previous is not None and previous.poly == neighbor:
# Don't backtrack.
continue
if previous is None and current != origin:
raise RuntimeError
_, neighbor_point = nearest_points(current.point, boundary)
neighbor_nav = NavPoint(neighbor_point, neighbor)
cost = best_known[current] + self.travel_cost(current, neighbor_nav)
if cost < best_known[neighbor_nav]:
best_known[neighbor_nav] = cost
estimated = cost + self.travel_heuristic(neighbor_nav, destination)
frontier.push(neighbor_nav, estimated)
came_from[neighbor_nav] = current
return self.reconstruct_path(came_from, origin, destination)
@staticmethod
def map_bounds(theater: ConflictTheater) -> Polygon:
points = []
for cp in theater.controlpoints:
points.append(ShapelyPoint(cp.position.x, cp.position.y))
for tgo in cp.ground_objects:
points.append(ShapelyPoint(tgo.position.x, tgo.position.y))
# Needs to be a large enough boundary beyond the known points so that
# threatened airbases at the map edges have room to retreat from the
# threat without running off the navmesh.
return box(*LineString(points).bounds).buffer(
nautical_miles(200).meters, resolution=1
)
@staticmethod
def create_navpolys(
polys: List[Polygon], threat_zones: ThreatZones
) -> List[NavMeshPoly]:
return [
NavMeshPoly(i, p, threat_zones.threatened(p)) for i, p in enumerate(polys)
]
@staticmethod
def associate_neighbors(polys: List[NavMeshPoly]) -> None:
# Maps (rounded) points to polygons that have a vertex at that point.
# The points are rounded to the nearest int so we can use them as dict
# keys. This allows us to perform approximate neighbor lookups more
# efficiently than comparing each poly to every other poly by finding
# approximate neighbors before checking if the polys actually touch.
points_map: Dict[Tuple[int, int], Set[NavMeshPoly]] = defaultdict(set)
for navpoly in polys:
# The coordinates of the polygon's boundary are a sequence of
# coordinates that define the polygon. The first point is repeated
# at the end, so skip the last vertex.
for x, y in navpoly.poly.boundary.coords[:-1]:
point = (int(x), int(y))
neighbors = {}
for potential_neighbor in points_map[point]:
intersection = navpoly.poly.intersection(potential_neighbor.poly)
if not intersection.is_empty:
potential_neighbor.neighbors[navpoly] = intersection
neighbors[potential_neighbor] = intersection
navpoly.neighbors.update(neighbors)
points_map[point].add(navpoly)
@classmethod
def from_threat_zones(
cls, threat_zones: ThreatZones, theater: ConflictTheater
) -> NavMesh:
# Simplify the threat poly to reduce the number of nav zones. Increase
# the size of the zone and then simplify it with the buffer size as the
# error margin. This will create a simpler poly around the threat zone.
buffer = nautical_miles(10).meters
threat_poly = threat_zones.all.buffer(buffer).simplify(buffer)
# Threat zones can be disconnected. Create a list of threat zones.
if isinstance(threat_poly, MultiPolygon):
polys = list(threat_poly.geoms)
else:
polys = [threat_poly]
# Subtract the threat zones from the whole-map poly to build a navmesh
# for the *safe* areas. Navigation within threatened regions is always
# a straight line to the target or out of the threatened region.
bounds = cls.map_bounds(theater)
for poly in polys:
bounds = bounds.difference(poly)
# Triangulate the safe-region to build the navmesh.
navpolys = cls.create_navpolys(triangulate(bounds), threat_zones)
cls.associate_neighbors(navpolys)
return cls(navpolys)

View File

@@ -1,53 +0,0 @@
from dcs.terrain import Terrain
from game import db
from gen.armor import *
from gen.aircraft import *
from gen.aaa import *
from gen.shipgen import *
from gen.triggergen import *
from gen.awacsgen import *
from gen.visualgen import *
from gen.conflictgen import Conflict
from .operation import Operation
class AntiAAStrikeOperation(Operation):
strikegroup = None # type: db.PlaneDict
interceptors = None # type: db.PlaneDict
target = None # type: db.ArmorDict
def setup(self,
target: db.ArmorDict,
strikegroup: db.PlaneDict,
interceptors: db.PlaneDict):
self.strikegroup = strikegroup
self.interceptors = interceptors
self.target = target
def prepare(self, terrain: Terrain, is_quick: bool):
super(AntiAAStrikeOperation, self).prepare(terrain, is_quick)
if self.defender_name == self.game.player:
self.attackers_starting_position = None
self.defenders_starting_position = None
conflict = Conflict.ground_base_attack(
attacker=self.mission.country(self.attacker_name),
defender=self.mission.country(self.defender_name),
from_cp=self.from_cp,
to_cp=self.to_cp,
theater=self.game.theater
)
self.initialize(mission=self.mission,
conflict=conflict)
def generate(self):
self.airgen.generate_cas_strikegroup(self.strikegroup, clients=self.attacker_clients, at=self.attackers_starting_position)
if self.interceptors:
self.airgen.generate_defense(self.interceptors, clients=self.defender_clients, at=self.defenders_starting_position)
self.armorgen.generate({}, self.target)
super(AntiAAStrikeOperation, self).generate()

View File

@@ -1,67 +0,0 @@
from game import db
from gen.conflictgen import Conflict
from gen.armor import *
from gen.aircraft import *
from gen.aaa import *
from gen.shipgen import *
from gen.triggergen import *
from gen.awacsgen import *
from gen.visualgen import *
from .operation import Operation
class CaptureOperation(Operation):
cas = None # type: db.PlaneDict
escort = None # type: db.PlaneDict
intercept = None # type: db.PlaneDict
attack = None # type: db.ArmorDict
defense = None # type: db.ArmorDict
aa = None # type: db.AirDefenseDict
trigger_radius = TRIGGER_RADIUS_SMALL
def setup(self,
cas: db.PlaneDict,
escort: db.PlaneDict,
attack: db.ArmorDict,
intercept: db.PlaneDict,
defense: db.ArmorDict,
aa: db.AirDefenseDict):
self.cas = cas
self.escort = escort
self.intercept = intercept
self.attack = attack
self.defense = defense
self.aa = aa
def prepare(self, terrain: dcs.terrain.Terrain, is_quick: bool):
super(CaptureOperation, self).prepare(terrain, is_quick)
self.defenders_starting_position = None
if self.game.player == self.defender_name:
self.attackers_starting_position = None
conflict = Conflict.capture_conflict(
attacker=self.mission.country(self.attacker_name),
defender=self.mission.country(self.defender_name),
from_cp=self.from_cp,
to_cp=self.to_cp,
theater=self.game.theater
)
self.initialize(mission=self.mission,
conflict=conflict)
def generate(self):
self.armorgen.generate(self.attack, self.defense)
self.aagen.generate(self.aa)
self.airgen.generate_defense(self.intercept, clients=self.defender_clients, at=self.defenders_starting_position)
self.airgen.generate_cas_strikegroup(self.cas, clients=self.attacker_clients, at=self.attackers_starting_position)
self.airgen.generate_strikegroup_escort(self.escort, clients=self.attacker_clients, at=self.attackers_starting_position)
self.visualgen.generate_target_smokes(self.to_cp)
super(CaptureOperation, self).generate()

View File

@@ -1,44 +0,0 @@
from dcs.terrain import Terrain
from game import db
from gen.armor import *
from gen.aircraft import *
from gen.aaa import *
from gen.shipgen import *
from gen.triggergen import *
from gen.awacsgen import *
from gen.visualgen import *
from gen.conflictgen import Conflict
from .operation import Operation
class GroundAttackOperation(Operation):
strikegroup = None # type: db.PlaneDict
target = None # type: db.ArmorDict
def setup(self,
target: db.ArmorDict,
strikegroup: db.PlaneDict):
self.strikegroup = strikegroup
self.target = target
def prepare(self, terrain: Terrain, is_quick: bool):
super(GroundAttackOperation, self).prepare(terrain, is_quick)
conflict = Conflict.ground_attack_conflict(
attacker=self.mission.country(self.attacker_name),
defender=self.mission.country(self.defender_name),
from_cp=self.from_cp,
to_cp=self.to_cp,
theater=self.game.theater
)
self.initialize(mission=self.mission,
conflict=conflict)
def generate(self):
self.airgen.generate_defense(self.strikegroup, self.defender_clients, self.defenders_starting_position)
self.armorgen.generate(self.target, {})
super(GroundAttackOperation, self).generate()

View File

@@ -1,53 +0,0 @@
from dcs.terrain import Terrain
from game import db
from gen.armor import *
from gen.aircraft import *
from gen.aaa import *
from gen.shipgen import *
from gen.triggergen import *
from gen.awacsgen import *
from gen.visualgen import *
from gen.conflictgen import Conflict
from .operation import Operation
class GroundInterceptOperation(Operation):
strikegroup = None # type: db.PlaneDict
interceptors = None # type: db.PlaneDict
target = None # type: db.ArmorDict
def setup(self,
target: db.ArmorDict,
strikegroup: db.PlaneDict,
interceptors: db.PlaneDict):
self.strikegroup = strikegroup
self.interceptors = interceptors
self.target = target
def prepare(self, terrain: Terrain, is_quick: bool):
super(GroundInterceptOperation, self).prepare(terrain, is_quick)
if self.defender_name == self.game.player:
self.attackers_starting_position = None
self.defenders_starting_position = None
conflict = Conflict.ground_intercept_conflict(
attacker=self.mission.country(self.attacker_name),
defender=self.mission.country(self.defender_name),
from_cp=self.from_cp,
to_cp=self.to_cp,
theater=self.game.theater
)
self.initialize(mission=self.mission,
conflict=conflict)
def generate(self):
self.airgen.generate_cas_strikegroup(self.strikegroup, clients=self.attacker_clients, at=self.attackers_starting_position)
if self.interceptors:
self.airgen.generate_defense(self.interceptors, clients=self.defender_clients, at=self.defenders_starting_position)
self.armorgen.generate({}, self.target)
super(GroundInterceptOperation, self).generate()

View File

@@ -1,56 +0,0 @@
from dcs.terrain import Terrain
from game import db
from gen.armor import *
from gen.aircraft import *
from gen.aaa import *
from gen.shipgen import *
from gen.triggergen import *
from gen.awacsgen import *
from gen.visualgen import *
from gen.conflictgen import Conflict
from .operation import Operation
class InfantryTransportOperation(Operation):
transport = None # type: db.HeliDict
aa = None # type: db.AirDefenseDict
def setup(self, transport: db.HeliDict, aa: db.AirDefenseDict):
self.transport = transport
self.aa = aa
def prepare(self, terrain: Terrain, is_quick: bool):
super(InfantryTransportOperation, self).prepare(terrain, is_quick)
conflict = Conflict.transport_conflict(
attacker=self.mission.country(self.attacker_name),
defender=self.mission.country(self.defender_name),
from_cp=self.from_cp,
to_cp=self.to_cp,
theater=self.game.theater
)
self.initialize(mission=self.mission,
conflict=conflict)
def generate(self):
self.airgen.generate_passenger_transport(
helis=self.transport,
clients=self.attacker_clients,
at=self.attackers_starting_position
)
self.armorgen.generate_passengers(count=6)
self.aagen.generate_at_defenders_location(self.aa)
self.visualgen.generate_transportation_marker(self.conflict.ground_attackers_location)
self.visualgen.generate_transportation_destination(self.conflict.position)
# TODO: horrible, horrible hack
# this will disable vehicle activation triggers,
# which aren't needed on this type of missions
self.is_quick = True
super(InfantryTransportOperation, self).generate()
self.is_quick = False

View File

@@ -1,52 +0,0 @@
from dcs.terrain import Terrain
from gen import *
from .operation import Operation
class InterceptOperation(Operation):
escort = None # type: db.PlaneDict
transport = None # type: db.PlaneDict
interceptors = None # type: db.PlaneDict
airdefense = None # type: db.AirDefenseDict
trigger_radius = TRIGGER_RADIUS_LARGE
def setup(self,
escort: db.PlaneDict,
transport: db.PlaneDict,
airdefense: db.AirDefenseDict,
interceptors: db.PlaneDict):
self.escort = escort
self.transport = transport
self.airdefense = airdefense
self.interceptors = interceptors
def prepare(self, terrain: Terrain, is_quick: bool):
super(InterceptOperation, self).prepare(terrain, is_quick)
self.defenders_starting_position = None
if self.defender_name == self.game.player:
self.attackers_starting_position = None
conflict = Conflict.intercept_conflict(
attacker=self.mission.country(self.attacker_name),
defender=self.mission.country(self.defender_name),
from_cp=self.from_cp,
to_cp=self.to_cp,
theater=self.game.theater
)
self.initialize(mission=self.mission,
conflict=conflict)
def generate(self):
self.airgen.generate_transport(self.transport, self.to_cp.at)
self.airgen.generate_transport_escort(self.escort, clients=self.defender_clients)
if self.from_cp.is_global:
super(InterceptOperation, self).generate()
self.airgen.generate_interception(self.interceptors, clients=self.attacker_clients, at=self.attackers_starting_position)
else:
self.airgen.generate_interception(self.interceptors, clients=self.attacker_clients, at=self.attackers_starting_position)
super(InterceptOperation, self).generate()

View File

@@ -1,53 +0,0 @@
from dcs.terrain import Terrain
from gen import *
from .operation import Operation
class NavalInterceptionOperation(Operation):
strikegroup = None # type: db.PlaneDict
interceptors = None # type: db.PlaneDict
targets = None # type: db.ShipDict
trigger_radius = TRIGGER_RADIUS_LARGE
def setup(self,
strikegroup: db.PlaneDict,
interceptors: db.PlaneDict,
targets: db.ShipDict):
self.strikegroup = strikegroup
self.interceptors = interceptors
self.targets = targets
def prepare(self, terrain: Terrain, is_quick: bool):
super(NavalInterceptionOperation, self).prepare(terrain, is_quick)
if self.defender_name == self.game.player:
self.attackers_starting_position = None
conflict = Conflict.naval_intercept_conflict(
attacker=self.mission.country(self.attacker_name),
defender=self.mission.country(self.defender_name),
from_cp=self.from_cp,
to_cp=self.to_cp,
theater=self.game.theater
)
self.initialize(self.mission, conflict)
def generate(self):
super(NavalInterceptionOperation, self).generate()
target_groups = self.shipgen.generate_cargo(units=self.targets)
self.airgen.generate_ship_strikegroup(
attackers=self.strikegroup,
clients=self.attacker_clients,
target_groups=target_groups,
at=self.attackers_starting_position
)
if self.interceptors:
self.airgen.generate_defense(
defenders=self.interceptors,
clients=self.defender_clients,
at=self.defenders_starting_position
)

View File

@@ -1,103 +1,652 @@
from dcs.terrain import Terrain
from __future__ import annotations
from game.theater.theatergroundobject import TheaterGroundObject
from userdata.debriefing import *
import logging
import os
from pathlib import Path
from typing import TYPE_CHECKING, Iterable, List, Optional, Set
from theater import *
from gen import *
from dcs import Mission
from dcs.action import DoScript, DoScriptFile
from dcs.coalition import Coalition
from dcs.countries import country_dict
from dcs.lua.parse import loads
from dcs.mapping import Point
from dcs.translation import String
from dcs.triggers import TriggerStart
from game.plugins import LuaPluginManager
from gen import Conflict, FlightType, VisualGenerator
from gen.aircraft import AIRCRAFT_DATA, AircraftConflictGenerator, FlightData
from gen.airfields import AIRFIELD_DATA
from gen.airsupportgen import AirSupport, AirSupportConflictGenerator
from gen.armor import GroundConflictGenerator, JtacInfo
from gen.beacons import load_beacons_for_terrain
from gen.briefinggen import BriefingGenerator, MissionInfoGenerator
from gen.environmentgen import EnvironmentGenerator
from gen.forcedoptionsgen import ForcedOptionsGenerator
from gen.groundobjectsgen import GroundObjectsGenerator
from gen.kneeboard import KneeboardGenerator
from gen.naming import namegen
from gen.radios import RadioFrequency, RadioRegistry
from gen.tacan import TacanRegistry
from gen.triggergen import TRIGGER_RADIUS_MEDIUM, TriggersGenerator
from .. import db
from ..theater import Airfield
from ..unitmap import UnitMap
if TYPE_CHECKING:
from game import Game
class Operation:
attackers_starting_position = None # type: db.StartingPosition
defenders_starting_position = None # type: db.StartingPosition
mission = None # type: dcs.Mission
conflict = None # type: Conflict
armorgen = None # type: ArmorConflictGenerator
airgen = None # type: AircraftConflictGenerator
aagen = None # type: AAConflictGenerator
extra_aagen = None # type: ExtraAAConflictGenerator
shipgen = None # type: ShipGenerator
triggersgen = None # type: TriggersGenerator
awacsgen = None # type: AWACSConflictGenerator
visualgen = None # type: VisualGenerator
envgen = None # type: EnvironmentGenerator
"""Static class for managing the final Mission generation"""
current_mission = None # type: Mission
airgen = None # type: AircraftConflictGenerator
triggersgen = None # type: TriggersGenerator
airsupportgen = None # type: AirSupportConflictGenerator
visualgen = None # type: VisualGenerator
groundobjectgen = None # type: GroundObjectsGenerator
briefinggen = None # type: BriefingGenerator
forcedoptionsgen = None # type: ForcedOptionsGenerator
radio_registry: Optional[RadioRegistry] = None
tacan_registry: Optional[TacanRegistry] = None
game = None # type: Game
environment_settings = None
trigger_radius = TRIGGER_RADIUS_MEDIUM
is_quick = None
is_awacs_enabled = False
player_awacs_enabled = True
# TODO: #436 Generate Air Support for red
enemy_awacs_enabled = True
ca_slots = 1
unit_map: UnitMap
jtacs: List[JtacInfo] = []
plugin_scripts: List[str] = []
def __init__(self,
game,
attacker_name: str,
defender_name: str,
attacker_clients: db.PlaneDict,
defender_clients: db.PlaneDict,
from_cp: ControlPoint,
to_cp: ControlPoint = None):
self.game = game
self.attacker_name = attacker_name
self.defender_name = defender_name
self.attacker_clients = attacker_clients
self.defender_clients = defender_clients
self.from_cp = from_cp
self.to_cp = to_cp
self.is_quick = False
@classmethod
def prepare(cls, game: Game):
with open("resources/default_options.lua", "r") as f:
options_dict = loads(f.read())["options"]
cls._set_mission(Mission(game.theater.terrain))
cls.game = game
cls._setup_mission_coalitions()
cls.current_mission.options.load_from_dict(options_dict)
def initialize(self, mission: Mission, conflict: Conflict):
self.mission = mission
self.conflict = conflict
@classmethod
def conflicts(cls) -> Iterable[Conflict]:
assert cls.game
for frontline in cls.game.theater.conflicts():
yield Conflict(
cls.game.theater,
frontline.control_point_a,
frontline.control_point_b,
cls.game.player_name,
cls.game.enemy_name,
cls.game.player_country,
cls.game.enemy_country,
frontline.position,
)
self.armorgen = ArmorConflictGenerator(mission, conflict)
self.airgen = AircraftConflictGenerator(mission, conflict, self.game.settings)
self.aagen = AAConflictGenerator(mission, conflict)
self.shipgen = ShipGenerator(mission, conflict)
self.awacsgen = AWACSConflictGenerator(mission, conflict, self.game)
self.triggersgen = TriggersGenerator(mission, conflict, self.game)
self.visualgen = VisualGenerator(mission, conflict, self.game)
self.envgen = EnviromentGenerator(mission, conflict, self.game)
@classmethod
def air_conflict(cls) -> Conflict:
assert cls.game
player_cp, enemy_cp = cls.game.theater.closest_opposing_control_points()
mid_point = player_cp.position.point_from_heading(
player_cp.position.heading_between_point(enemy_cp.position),
player_cp.position.distance_to_point(enemy_cp.position) / 2,
)
return Conflict(
cls.game.theater,
player_cp,
enemy_cp,
cls.game.player_name,
cls.game.enemy_name,
cls.game.player_country,
cls.game.enemy_country,
mid_point,
)
player_name = self.from_cp.captured and self.attacker_name or self.defender_name
enemy_name = self.from_cp.captured and self.defender_name or self.attacker_name
self.extra_aagen = ExtraAAConflictGenerator(mission, conflict, self.game, player_name, enemy_name)
@classmethod
def _set_mission(cls, mission: Mission) -> None:
cls.current_mission = mission
def prepare(self, terrain: Terrain, is_quick: bool):
self.mission = dcs.Mission(terrain)
self.is_quick = is_quick
@classmethod
def _setup_mission_coalitions(cls):
cls.current_mission.coalition["blue"] = Coalition("blue")
cls.current_mission.coalition["red"] = Coalition("red")
if is_quick:
self.attackers_starting_position = None
self.defenders_starting_position = None
p_country = cls.game.player_country
e_country = cls.game.enemy_country
cls.current_mission.coalition["blue"].add_country(
country_dict[db.country_id_from_name(p_country)]()
)
cls.current_mission.coalition["red"].add_country(
country_dict[db.country_id_from_name(e_country)]()
)
@classmethod
def inject_lua_trigger(cls, contents: str, comment: str) -> None:
trigger = TriggerStart(comment=comment)
trigger.add_action(DoScript(String(contents)))
cls.current_mission.triggerrules.triggers.append(trigger)
@classmethod
def bypass_plugin_script(cls, mnemonic: str) -> None:
cls.plugin_scripts.append(mnemonic)
@classmethod
def inject_plugin_script(
cls, plugin_mnemonic: str, script: str, script_mnemonic: str
) -> None:
if script_mnemonic in cls.plugin_scripts:
logging.debug(f"Skipping already loaded {script} for {plugin_mnemonic}")
else:
self.attackers_starting_position = self.from_cp.at
self.defenders_starting_position = self.to_cp.at
cls.plugin_scripts.append(script_mnemonic)
def generate(self):
self.visualgen.generate()
plugin_path = Path("./resources/plugins", plugin_mnemonic)
if self.is_awacs_enabled:
self.awacsgen.generate()
script_path = Path(plugin_path, script)
if not script_path.exists():
logging.error(f"Cannot find {script_path} for plugin {plugin_mnemonic}")
return
self.extra_aagen.generate()
self.triggersgen.generate(self.is_quick, self.trigger_radius)
trigger = TriggerStart(comment=f"Load {script_mnemonic}")
filename = script_path.resolve()
fileref = cls.current_mission.map_resource.add_resource_file(filename)
trigger.add_action(DoScriptFile(fileref))
cls.current_mission.triggerrules.triggers.append(trigger)
if self.environment_settings is None:
self.environment_settings = self.envgen.generate()
else:
self.envgen.load(self.environment_settings)
@classmethod
def notify_info_generators(
cls,
groundobjectgen: GroundObjectsGenerator,
airsupportgen: AirSupportConflictGenerator,
jtacs: List[JtacInfo],
airgen: AircraftConflictGenerator,
):
"""Generates subscribed MissionInfoGenerator objects (currently kneeboards and briefings)"""
for global_cp in self.game.theater.controlpoints:
if not global_cp.is_global:
gens: List[MissionInfoGenerator] = [
KneeboardGenerator(cls.current_mission, cls.game),
BriefingGenerator(cls.current_mission, cls.game),
]
for gen in gens:
for dynamic_runway in groundobjectgen.runways.values():
gen.add_dynamic_runway(dynamic_runway)
for tanker in airsupportgen.air_support.tankers:
gen.add_tanker(tanker)
for aewc in airsupportgen.air_support.awacs:
gen.add_awacs(aewc)
for jtac in jtacs:
gen.add_jtac(jtac)
for flight in airgen.flights:
gen.add_flight(flight)
gen.generate()
@classmethod
def create_unit_map(cls) -> None:
cls.unit_map = UnitMap()
for control_point in cls.game.theater.controlpoints:
if isinstance(control_point, Airfield):
cls.unit_map.add_airfield(control_point)
@classmethod
def create_radio_registries(cls) -> None:
unique_map_frequencies: Set[RadioFrequency] = set()
cls._create_tacan_registry(unique_map_frequencies)
cls._create_radio_registry(unique_map_frequencies)
assert cls.radio_registry is not None
for frequency in unique_map_frequencies:
cls.radio_registry.reserve(frequency)
@classmethod
def assign_channels_to_flights(
cls, flights: List[FlightData], air_support: AirSupport
) -> None:
"""Assigns preset radio channels for client flights."""
for flight in flights:
if not flight.client_units:
continue
cls.assign_channels_to_flight(flight, air_support)
@staticmethod
def assign_channels_to_flight(flight: FlightData, air_support: AirSupport) -> None:
"""Assigns preset radio channels for a client flight."""
airframe = flight.aircraft_type
try:
aircraft_data = AIRCRAFT_DATA[airframe.id]
except KeyError:
logging.warning(f"No aircraft data for {airframe.id}")
return
if aircraft_data.channel_allocator is not None:
aircraft_data.channel_allocator.assign_channels_for_flight(
flight, air_support
)
@classmethod
def _create_tacan_registry(
cls, unique_map_frequencies: Set[RadioFrequency]
) -> None:
"""
Dedup beacon/radio frequencies, since some maps have some frequencies
used multiple times.
"""
cls.tacan_registry = TacanRegistry()
beacons = load_beacons_for_terrain(cls.game.theater.terrain.name)
for beacon in beacons:
unique_map_frequencies.add(beacon.frequency)
if beacon.is_tacan:
if beacon.channel is None:
logging.error(f"TACAN beacon has no channel: {beacon.callsign}")
else:
cls.tacan_registry.reserve(beacon.tacan_channel)
@classmethod
def _create_radio_registry(
cls, unique_map_frequencies: Set[RadioFrequency]
) -> None:
cls.radio_registry = RadioRegistry()
for data in AIRFIELD_DATA.values():
if data.theater == cls.game.theater.terrain.name and data.atc:
unique_map_frequencies.add(data.atc.hf)
unique_map_frequencies.add(data.atc.vhf_fm)
unique_map_frequencies.add(data.atc.vhf_am)
unique_map_frequencies.add(data.atc.uhf)
# No need to reserve ILS or TACAN because those are in the
# beacon list.
@classmethod
def _generate_ground_units(cls):
cls.groundobjectgen = GroundObjectsGenerator(
cls.current_mission,
cls.game,
cls.radio_registry,
cls.tacan_registry,
cls.unit_map,
)
cls.groundobjectgen.generate()
@classmethod
def _generate_destroyed_units(cls) -> None:
"""Add destroyed units to the Mission"""
for d in cls.game.get_destroyed_units():
try:
utype = db.unit_type_from_name(d["type"])
except KeyError:
continue
ship = self.shipgen.generate_carrier(type=db.find_unittype(Carriage, self.game.player)[0],
country=self.game.player,
at=global_cp.at)
pos = Point(d["x"], d["z"])
if (
utype is not None
and not cls.game.position_culled(pos)
and cls.game.settings.perf_destroyed_units
):
cls.current_mission.static_group(
country=cls.current_mission.country(cls.game.player_country),
name="",
_type=utype,
hidden=True,
position=pos,
heading=d["orientation"],
dead=True,
)
if global_cp == self.from_cp and not self.is_quick:
self.attackers_starting_position = ship
@classmethod
def generate(cls) -> UnitMap:
"""Build the final Mission to be exported"""
cls.create_unit_map()
cls.create_radio_registries()
# Set mission time and weather conditions.
EnvironmentGenerator(cls.current_mission, cls.game.conditions).generate()
cls._generate_ground_units()
cls._generate_destroyed_units()
cls._generate_air_units()
cls.assign_channels_to_flights(
cls.airgen.flights, cls.airsupportgen.air_support
)
cls._generate_ground_conflicts()
def units_of(self, country_name: str) -> typing.Collection[UnitType]:
return []
# Triggers
triggersgen = TriggersGenerator(cls.current_mission, cls.game)
triggersgen.generate()
def is_successfull(self, debriefing: Debriefing) -> bool:
return True
# Setup combined arms parameters
cls.current_mission.groundControl.pilot_can_control_vehicles = cls.ca_slots > 0
if cls.game.player_country in [
country.name
for country in cls.current_mission.coalition["blue"].countries.values()
]:
cls.current_mission.groundControl.blue_tactical_commander = cls.ca_slots
else:
cls.current_mission.groundControl.red_tactical_commander = cls.ca_slots
# Options
forcedoptionsgen = ForcedOptionsGenerator(cls.current_mission, cls.game)
forcedoptionsgen.generate()
# Generate Visuals Smoke Effects
visualgen = VisualGenerator(cls.current_mission, cls.game)
if cls.game.settings.perf_smoke_gen:
visualgen.generate()
cls.generate_lua(cls.airgen, cls.airsupportgen, cls.jtacs)
# Inject Plugins Lua Scripts and data
cls.plugin_scripts.clear()
for plugin in LuaPluginManager.plugins():
if plugin.enabled:
plugin.inject_scripts(cls)
plugin.inject_configuration(cls)
cls.assign_channels_to_flights(
cls.airgen.flights, cls.airsupportgen.air_support
)
cls.notify_info_generators(
cls.groundobjectgen, cls.airsupportgen, cls.jtacs, cls.airgen
)
cls.reset_naming_ids()
return cls.unit_map
@classmethod
def _generate_air_units(cls) -> None:
"""Generate the air units for the Operation"""
# Air Support (Tanker & Awacs)
assert cls.radio_registry and cls.tacan_registry
cls.airsupportgen = AirSupportConflictGenerator(
cls.current_mission,
cls.air_conflict(),
cls.game,
cls.radio_registry,
cls.tacan_registry,
)
cls.airsupportgen.generate()
# Generate Aircraft Activity on the map
cls.airgen = AircraftConflictGenerator(
cls.current_mission,
cls.game.settings,
cls.game,
cls.radio_registry,
cls.unit_map,
air_support=cls.airsupportgen.air_support,
)
cls.airgen.clear_parking_slots()
cls.airgen.generate_flights(
cls.current_mission.country(cls.game.player_country),
cls.game.blue_ato,
cls.groundobjectgen.runways,
)
cls.airgen.generate_flights(
cls.current_mission.country(cls.game.enemy_country),
cls.game.red_ato,
cls.groundobjectgen.runways,
)
cls.airgen.spawn_unused_aircraft(
cls.current_mission.country(cls.game.player_country),
cls.current_mission.country(cls.game.enemy_country),
)
@classmethod
def _generate_ground_conflicts(cls) -> None:
"""For each frontline in the Operation, generate the ground conflicts and JTACs"""
for front_line in cls.game.theater.conflicts(True):
player_cp = front_line.control_point_a
enemy_cp = front_line.control_point_b
conflict = Conflict.frontline_cas_conflict(
cls.game.player_name,
cls.game.enemy_name,
cls.current_mission.country(cls.game.player_country),
cls.current_mission.country(cls.game.enemy_country),
player_cp,
enemy_cp,
cls.game.theater,
)
# Generate frontline ops
player_gp = cls.game.ground_planners[player_cp.id].units_per_cp[enemy_cp.id]
enemy_gp = cls.game.ground_planners[enemy_cp.id].units_per_cp[player_cp.id]
ground_conflict_gen = GroundConflictGenerator(
cls.current_mission,
conflict,
cls.game,
player_gp,
enemy_gp,
player_cp.stances[enemy_cp.id],
cls.unit_map,
)
ground_conflict_gen.generate()
cls.jtacs.extend(ground_conflict_gen.jtacs)
@classmethod
def reset_naming_ids(cls):
namegen.reset_numbers()
@classmethod
def generate_lua(
cls,
airgen: AircraftConflictGenerator,
airsupportgen: AirSupportConflictGenerator,
jtacs: List[JtacInfo],
) -> None:
# TODO: Refactor this
luaData = {
"AircraftCarriers": {},
"Tankers": {},
"AWACs": {},
"JTACs": {},
"TargetPoints": {},
"RedAA": {},
"BlueAA": {},
} # type: ignore
for tanker in airsupportgen.air_support.tankers:
luaData["Tankers"][tanker.callsign] = {
"dcsGroupName": tanker.dcsGroupName,
"callsign": tanker.callsign,
"variant": tanker.variant,
"radio": tanker.freq.mhz,
"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.dcsGroupName,
"callsign": awacs.callsign,
"radio": awacs.freq.mhz,
}
for jtac in jtacs:
luaData["JTACs"][jtac.callsign] = {
"dcsGroupName": jtac.dcsGroupName,
"callsign": jtac.callsign,
"zone": jtac.region,
"dcsUnit": jtac.unit_name,
"laserCode": jtac.code,
}
for flight in airgen.flights:
if flight.friendly and flight.flight_type in [
FlightType.ANTISHIP,
FlightType.DEAD,
FlightType.SEAD,
FlightType.STRIKE,
]:
flightType = str(flight.flight_type)
flightTarget = flight.package.target
if flightTarget:
flightTargetName = None
flightTargetType = None
if isinstance(flightTarget, TheaterGroundObject):
flightTargetName = flightTarget.obj_name
flightTargetType = (
flightType + f" TGT ({flightTarget.category})"
)
elif hasattr(flightTarget, "name"):
flightTargetName = flightTarget.name
flightTargetType = flightType + " TGT (Airbase)"
luaData["TargetPoints"][flightTargetName] = {
"name": flightTargetName,
"type": flightTargetType,
"position": {
"x": flightTarget.position.x,
"y": flightTarget.position.y,
},
}
for cp in cls.game.theater.controlpoints:
for ground_object in cp.ground_objects:
if ground_object.might_have_aa and not ground_object.is_dead:
for g in ground_object.groups:
threat_range = ground_object.threat_range(g)
if not threat_range:
continue
faction = "BlueAA" if cp.captured else "RedAA"
luaData[faction][g.name] = {
"name": ground_object.name,
"range": threat_range.meters,
"position": {
"x": ground_object.position.x,
"y": ground_object.position.y,
},
}
# set a LUA table with data from Liberation that we want to set
# at the moment it contains Liberation's install path, and an overridable definition for the JTACAutoLase function
# later, we'll add data about the units and points having been generated, in order to facilitate the configuration of the plugin lua scripts
state_location = "[[" + os.path.abspath(".") + "]]"
lua = (
"""
-- setting configuration table
env.info("DCSLiberation|: setting configuration table")
-- all data in this table is overridable.
dcsLiberation = {}
-- the base location for state.json; if non-existent, it'll be replaced with LIBERATION_EXPORT_DIR, TEMP, or DCS working directory
dcsLiberation.installPath="""
+ state_location
+ """
"""
)
# Process the tankers
lua += """
-- list the tankers generated by Liberation
dcsLiberation.Tankers = {
"""
for key in luaData["Tankers"]:
data = luaData["Tankers"][key]
dcsGroupName = data["dcsGroupName"]
callsign = data["callsign"]
variant = data["variant"]
tacan = data["tacan"]
radio = data["radio"]
lua += f" {{dcsGroupName='{dcsGroupName}', callsign='{callsign}', variant='{variant}', tacan='{tacan}', radio='{radio}' }}, \n"
# lua += f" {{name='{dcsGroupName}', description='{callsign} ({variant})', information='Tacan:{tacan} Radio:{radio}' }}, \n"
lua += "}"
# Process the AWACSes
lua += """
-- list the AWACs generated by Liberation
dcsLiberation.AWACs = {
"""
for key in luaData["AWACs"]:
data = luaData["AWACs"][key]
dcsGroupName = data["dcsGroupName"]
callsign = data["callsign"]
radio = data["radio"]
lua += f" {{dcsGroupName='{dcsGroupName}', callsign='{callsign}', radio='{radio}' }}, \n"
# lua += f" {{name='{dcsGroupName}', description='{callsign} (AWACS)', information='Radio:{radio}' }}, \n"
lua += "}"
# Process the JTACs
lua += """
-- list the JTACs generated by Liberation
dcsLiberation.JTACs = {
"""
for key in luaData["JTACs"]:
data = luaData["JTACs"][key]
dcsGroupName = data["dcsGroupName"]
callsign = data["callsign"]
zone = data["zone"]
laserCode = data["laserCode"]
dcsUnit = data["dcsUnit"]
lua += f" {{dcsGroupName='{dcsGroupName}', callsign='{callsign}', zone='{zone}', laserCode='{laserCode}', dcsUnit='{dcsUnit}' }}, \n"
# lua += f" {{name='{dcsGroupName}', description='JTAC {callsign} ', information='Laser:{laserCode}', jtac={laserCode} }}, \n"
lua += "}"
# Process the Target Points
lua += """
-- list the target points generated by Liberation
dcsLiberation.TargetPoints = {
"""
for key in luaData["TargetPoints"]:
data = luaData["TargetPoints"][key]
name = data["name"]
pointType = data["type"]
positionX = data["position"]["x"]
positionY = data["position"]["y"]
lua += f" {{name='{name}', pointType='{pointType}', positionX='{positionX}', positionY='{positionY}' }}, \n"
# lua += f" {{name='{pointType} {name}', point{{x={positionX}, z={positionY} }} }}, \n"
lua += "}"
lua += """
-- list the airbases generated by Liberation
-- dcsLiberation.Airbases = {}
-- list the aircraft carriers generated by Liberation
-- dcsLiberation.Carriers = {}
-- list the Red AA generated by Liberation
dcsLiberation.RedAA = {
"""
for key in luaData["RedAA"]:
data = luaData["RedAA"][key]
name = data["name"]
radius = data["range"]
positionX = data["position"]["x"]
positionY = data["position"]["y"]
lua += f" {{dcsGroupName='{key}', name='{name}', range='{radius}', positionX='{positionX}', positionY='{positionY}' }}, \n"
lua += "}"
lua += """
-- list the Blue AA generated by Liberation
dcsLiberation.BlueAA = {
"""
for key in luaData["BlueAA"]:
data = luaData["BlueAA"][key]
name = data["name"]
radius = data["range"]
positionX = data["position"]["x"]
positionY = data["position"]["y"]
lua += f" {{dcsGroupName='{key}', name='{name}', range='{radius}', positionX='{positionX}', positionY='{positionY}' }}, \n"
lua += "}"
lua += """
"""
trigger = TriggerStart(comment="Set DCS Liberation data")
trigger.add_action(DoScript(String(lua)))
Operation.current_mission.triggerrules.triggers.append(trigger)

69
game/persistency.py Normal file
View File

@@ -0,0 +1,69 @@
import logging
import os
import pickle
import shutil
from typing import Optional
_dcs_saved_game_folder: Optional[str] = None
_file_abs_path = None
def setup(user_folder: str):
global _dcs_saved_game_folder
_dcs_saved_game_folder = user_folder
_file_abs_path = os.path.join(base_path(), "default.liberation")
def base_path() -> str:
global _dcs_saved_game_folder
assert _dcs_saved_game_folder
return _dcs_saved_game_folder
def _temporary_save_file() -> str:
return os.path.join(base_path(), "tmpsave.liberation")
def _autosave_path() -> str:
return os.path.join(base_path(), "autosave.liberation")
def mission_path_for(name: str) -> str:
return os.path.join(base_path(), "Missions", "{}".format(name))
def load_game(path):
with open(path, "rb") as f:
try:
save = pickle.load(f)
save.savepath = path
return save
except Exception:
logging.exception("Invalid Save game")
return None
def save_game(game) -> bool:
try:
with open(_temporary_save_file(), "wb") as f:
pickle.dump(game, f)
shutil.copy(_temporary_save_file(), game.savepath)
return True
except Exception:
logging.exception("Could not save game")
return False
def autosave(game) -> bool:
"""
Autosave to the autosave location
:param game: Game to save
:return: True if saved succesfully
"""
try:
with open(_autosave_path(), "wb") as f:
pickle.dump(game, f)
return True
except Exception:
logging.exception("Could not save game")
return False

2
game/plugins/__init__.py Normal file
View File

@@ -0,0 +1,2 @@
from .luaplugin import LuaPlugin
from .manager import LuaPluginManager

187
game/plugins/luaplugin.py Normal file
View File

@@ -0,0 +1,187 @@
from __future__ import annotations
import json
import logging
import textwrap
from dataclasses import dataclass
from pathlib import Path
from typing import List, Optional, TYPE_CHECKING, Type
from game.settings import Settings
if TYPE_CHECKING:
from game.operation.operation import Operation
class LuaPluginWorkOrder:
def __init__(
self, parent_mnemonic: str, filename: str, mnemonic: str, disable: bool
) -> None:
self.parent_mnemonic = parent_mnemonic
self.filename = filename
self.mnemonic = mnemonic
self.disable = disable
def work(self, operation: Type[Operation]) -> None:
if self.disable:
operation.bypass_plugin_script(self.mnemonic)
else:
operation.inject_plugin_script(
self.parent_mnemonic, self.filename, self.mnemonic
)
class PluginSettings:
def __init__(self, identifier: str, enabled_by_default: bool) -> None:
self.identifier = identifier
self.enabled_by_default = enabled_by_default
self.settings = Settings()
self.initialize_settings()
def set_settings(self, settings: Settings):
self.settings = settings
self.initialize_settings()
def initialize_settings(self) -> None:
# Plugin options are saved in the game's Settings, but it's possible for
# plugins to change across loads. If new plugins are added or new
# options added to those plugins, initialize the new settings.
self.settings.initialize_plugin_option(self.identifier, self.enabled_by_default)
@property
def enabled(self) -> bool:
return self.settings.plugin_option(self.identifier)
def set_enabled(self, enabled: bool) -> None:
self.settings.set_plugin_option(self.identifier, enabled)
class LuaPluginOption(PluginSettings):
def __init__(self, identifier: str, name: str, enabled_by_default: bool) -> None:
super().__init__(identifier, enabled_by_default)
self.name = name
@dataclass(frozen=True)
class LuaPluginDefinition:
identifier: str
name: str
present_in_ui: bool
enabled_by_default: bool
options: List[LuaPluginOption]
work_orders: List[LuaPluginWorkOrder]
config_work_orders: List[LuaPluginWorkOrder]
@classmethod
def from_json(cls, name: str, path: Path) -> LuaPluginDefinition:
data = json.loads(path.read_text())
options = []
for option in data.get("specificOptions"):
option_id = option["mnemonic"]
options.append(
LuaPluginOption(
identifier=f"{name}.{option_id}",
name=option.get("nameInUI", name),
enabled_by_default=option.get("defaultValue"),
)
)
work_orders = []
for work_order in data.get("scriptsWorkOrders"):
work_orders.append(
LuaPluginWorkOrder(
name,
work_order.get("file"),
work_order["mnemonic"],
work_order.get("disable", False),
)
)
config_work_orders = []
for work_order in data.get("configurationWorkOrders"):
config_work_orders.append(
LuaPluginWorkOrder(
name,
work_order.get("file"),
work_order["mnemonic"],
work_order.get("disable", False),
)
)
return cls(
identifier=name,
name=data["nameInUI"],
present_in_ui=not data.get("skipUI", False),
enabled_by_default=data.get("defaultValue", False),
options=options,
work_orders=work_orders,
config_work_orders=config_work_orders,
)
class LuaPlugin(PluginSettings):
def __init__(self, definition: LuaPluginDefinition) -> None:
self.definition = definition
super().__init__(self.definition.identifier, self.definition.enabled_by_default)
@property
def name(self) -> str:
return self.definition.name
@property
def show_in_ui(self) -> bool:
return self.definition.present_in_ui
@property
def options(self) -> List[LuaPluginOption]:
return self.definition.options
@classmethod
def from_json(cls, name: str, path: Path) -> Optional[LuaPlugin]:
try:
definition = LuaPluginDefinition.from_json(name, path)
except KeyError:
logging.exception("Required plugin configuration value missing")
return None
return cls(definition)
def set_settings(self, settings: Settings):
super().set_settings(settings)
for option in self.definition.options:
option.set_settings(self.settings)
def inject_scripts(self, operation: Type[Operation]) -> None:
for work_order in self.definition.work_orders:
work_order.work(operation)
def inject_configuration(self, operation: Type[Operation]) -> None:
# inject the plugin options
if self.options:
option_decls = []
for option in self.options:
enabled = str(option.enabled).lower()
name = option.identifier
option_decls.append(f" dcsLiberation.plugins.{name} = {enabled}")
joined_options = "\n".join(option_decls)
lua = textwrap.dedent(
f"""\
-- {self.identifier} plugin configuration.
if dcsLiberation then
if not dcsLiberation.plugins then
dcsLiberation.plugins = {{}}
end
dcsLiberation.plugins.{self.identifier} = {{}}
{joined_options}
end
"""
)
operation.inject_lua_trigger(lua, f"{self.identifier} plugin configuration")
for work_order in self.definition.config_work_orders:
work_order.work(operation)

51
game/plugins/manager.py Normal file
View File

@@ -0,0 +1,51 @@
import json
import logging
from pathlib import Path
from typing import Dict, List, Optional
from game.settings import Settings
from game.plugins.luaplugin import LuaPlugin
class LuaPluginManager:
_plugins_loaded = False
_plugins: Dict[str, LuaPlugin] = {}
@classmethod
def _load_plugins(cls) -> None:
plugins_path = Path("resources/plugins")
path = plugins_path / "plugins.json"
if not path.exists():
raise RuntimeError(f"{path} does not exist. Cannot continue.")
logging.info(f"Reading plugins list from {path}")
data = json.loads(path.read_text())
for name in data:
plugin_path = plugins_path / name / "plugin.json"
if not plugin_path.exists():
raise RuntimeError(
f"Invalid plugin configuration: required plugin {name} "
f"does not exist at {plugin_path}"
)
logging.info(f"Loading plugin {name} from {plugin_path}")
plugin = LuaPlugin.from_json(name, plugin_path)
if plugin is not None:
cls._plugins[name] = plugin
cls._plugins_loaded = True
@classmethod
def _get_plugins(cls) -> Dict[str, LuaPlugin]:
if not cls._plugins_loaded:
cls._load_plugins()
return cls._plugins
@classmethod
def plugins(cls) -> List[LuaPlugin]:
return list(cls._get_plugins().values())
@classmethod
def load_settings(cls, settings: Settings) -> None:
for plugin in cls.plugins():
plugin.set_settings(settings)

View File

@@ -0,0 +1,15 @@
from dcs import Point
class PointWithHeading(Point):
def __init__(self):
super(PointWithHeading, self).__init__(0, 0)
self.heading = 0
@staticmethod
def from_point(point: Point, heading: int):
p = PointWithHeading()
p.x = point.x
p.y = point.y
p.heading = heading
return p

269
game/procurement.py Normal file
View File

@@ -0,0 +1,269 @@
from __future__ import annotations
import math
import random
from dataclasses import dataclass
from typing import Iterator, List, Optional, TYPE_CHECKING, Type
from dcs.unittype import FlyingType, VehicleType
from game import db
from game.factions.faction import Faction
from game.theater import ControlPoint, MissionTarget
from game.utils import Distance
from gen.flights.ai_flight_planner_db import aircraft_for_task
from gen.flights.closestairfields import ObjectiveDistanceCache
from gen.flights.flight import FlightType
from gen.ground_forces.ai_ground_planner_db import TYPE_SHORAD
if TYPE_CHECKING:
from game import Game
@dataclass(frozen=True)
class AircraftProcurementRequest:
near: MissionTarget
range: Distance
task_capability: FlightType
number: int
def __str__(self) -> str:
task = self.task_capability.value
distance = self.range.nautical_miles
target = self.near.name
return f"{self.number} ship {task} within {distance} nm of {target}"
class ProcurementAi:
def __init__(
self,
game: Game,
for_player: bool,
faction: Faction,
manage_runways: bool,
manage_front_line: bool,
manage_aircraft: bool,
front_line_budget_share: float,
) -> None:
if front_line_budget_share > 1.0:
raise ValueError
self.game = game
self.is_player = for_player
self.faction = faction
self.manage_runways = manage_runways
self.manage_front_line = manage_front_line
self.manage_aircraft = manage_aircraft
self.front_line_budget_share = front_line_budget_share
self.threat_zones = self.game.threat_zone_for(not self.is_player)
def spend_budget(
self, budget: float, aircraft_requests: List[AircraftProcurementRequest]
) -> float:
if self.manage_runways:
budget = self.repair_runways(budget)
if self.manage_front_line:
armor_budget = math.ceil(budget * self.front_line_budget_share)
budget -= armor_budget
budget += self.reinforce_front_line(armor_budget)
# Don't sell overstock aircraft until after we've bought runways and
# front lines. Any budget we free up should be earmarked for aircraft.
if not self.is_player:
budget += self.sell_incomplete_squadrons()
if self.manage_aircraft:
budget = self.purchase_aircraft(budget, aircraft_requests)
return budget
def sell_incomplete_squadrons(self) -> float:
# Selling incomplete squadrons gives us more money to spend on the next
# turn. This serves as a short term fix for
# https://github.com/Khopa/dcs_liberation/issues/41.
#
# Only incomplete squadrons which are unlikely to get used will be sold
# rather than all unused aircraft because the unused aircraft are what
# make OCA strikes worthwhile.
#
# This option is only used by the AI since players cannot cancel sales
# (https://github.com/Khopa/dcs_liberation/issues/365).
total = 0.0
for cp in self.game.theater.control_points_for(self.is_player):
inventory = self.game.aircraft_inventory.for_control_point(cp)
for aircraft, available in inventory.all_aircraft:
# We only ever plan even groups, so the odd aircraft is unlikely
# to get used.
if available % 2 == 0:
continue
inventory.remove_aircraft(aircraft, 1)
total += db.PRICES[aircraft]
return total
def repair_runways(self, budget: float) -> float:
for control_point in self.owned_points:
if budget < db.RUNWAY_REPAIR_COST:
break
if control_point.runway_can_be_repaired:
control_point.begin_runway_repair()
budget -= db.RUNWAY_REPAIR_COST
if self.is_player:
self.game.message(
"OPFOR has begun repairing the runway at " f"{control_point}"
)
else:
self.game.message(
"We have begun repairing the runway at " f"{control_point}"
)
return budget
def random_affordable_ground_unit(
self, budget: float, cp: ControlPoint
) -> Optional[Type[VehicleType]]:
affordable_units = [
u
for u in self.faction.frontline_units + self.faction.artillery_units
if db.PRICES[u] <= budget
]
total_number_aa = (
cp.base.total_frontline_aa + cp.pending_frontline_aa_deliveries_count
)
total_non_aa = (
cp.base.total_armor + cp.pending_deliveries_count - total_number_aa
)
max_aa = math.ceil(total_non_aa / 8)
# Limit the number of AA units the AI will buy
if not total_number_aa < max_aa:
for unit in [u for u in affordable_units if u in TYPE_SHORAD]:
affordable_units.remove(unit)
if not affordable_units:
return None
return random.choice(affordable_units)
def reinforce_front_line(self, budget: float) -> float:
if not self.faction.frontline_units and not self.faction.artillery_units:
return budget
while budget > 0:
candidates = self.front_line_candidates()
if not candidates:
break
cp = random.choice(candidates)
unit = self.random_affordable_ground_unit(budget, cp)
if unit is None:
# Can't afford any more units.
break
budget -= db.PRICES[unit]
cp.pending_unit_deliveries.order({unit: 1})
return budget
def _affordable_aircraft_of_types(
self,
types: List[Type[FlyingType]],
airbase: ControlPoint,
number: int,
max_price: float,
) -> Optional[Type[FlyingType]]:
best_choice: Optional[Type[FlyingType]] = None
for unit in [u for u in self.faction.aircrafts if u in types]:
if db.PRICES[unit] * number > max_price:
continue
if not airbase.can_operate(unit):
continue
# Affordable and compatible. To keep some variety, skip with a 50/50
# chance. Might be a good idea to have the chance to skip based on
# the price compared to the rest of the choices.
best_choice = unit
if random.choice([True, False]):
break
return best_choice
def affordable_aircraft_for(
self, request: AircraftProcurementRequest, airbase: ControlPoint, budget: float
) -> Optional[Type[FlyingType]]:
return self._affordable_aircraft_of_types(
aircraft_for_task(request.task_capability), airbase, request.number, budget
)
def purchase_aircraft(
self, budget: float, aircraft_requests: List[AircraftProcurementRequest]
) -> float:
for request in aircraft_requests:
for airbase in self.best_airbases_for(request):
unit = self.affordable_aircraft_for(request, airbase, budget)
if unit is None:
# Can't afford any aircraft capable of performing the
# required mission that can operate from this airbase. We
# might be able to afford aircraft at other airbases though,
# in the case where the airbase we attempted to use is only
# able to operate expensive aircraft.
continue
budget -= db.PRICES[unit] * request.number
airbase.pending_unit_deliveries.order({unit: request.number})
return budget
@property
def owned_points(self) -> List[ControlPoint]:
if self.is_player:
return self.game.theater.player_points()
else:
return self.game.theater.enemy_points()
def best_airbases_for(
self, request: AircraftProcurementRequest
) -> Iterator[ControlPoint]:
distance_cache = ObjectiveDistanceCache.get_closest_airfields(request.near)
threatened = []
for cp in distance_cache.airfields_within(request.range):
if not cp.is_friendly(self.is_player):
continue
if not cp.runway_is_operational():
continue
if cp.unclaimed_parking(self.game) < request.number:
continue
if self.threat_zones.threatened(cp.position):
threatened.append(cp)
yield cp
yield from threatened
def front_line_candidates(self) -> List[ControlPoint]:
candidates = []
# Prefer to buy front line units at active front lines that are not
# already overloaded.
for cp in self.owned_points:
if cp.expected_ground_units_next_turn.total >= 30:
# Control point is already sufficiently defended.
continue
for connected in cp.connected_points:
if not connected.is_friendly(to_player=self.is_player):
candidates.append(cp)
if not candidates:
# Otherwise buy reserves, but don't exceed 10 reserve units per CP.
# These units do not exist in the world until the CP becomes
# connected to an active front line, at which point all these units
# will suddenly appear at the gates of the newly captured CP.
#
# To avoid sudden overwhelming numbers of units we avoid buying
# many.
#
# Also, do not bother buying units at bases that will never connect
# to a front line.
for cp in self.owned_points:
if not cp.can_deploy_ground_units:
continue
if cp.expected_ground_units_next_turn.total >= 10:
continue
if cp.is_global:
continue
candidates.append(cp)
return candidates

View File

@@ -1,8 +1,83 @@
from dataclasses import dataclass, field
from typing import Dict, Optional
from dcs.forcedoptions import ForcedOptions
@dataclass
class Settings:
player_skill = "Good"
enemy_skill = "Average"
only_player_takeoff = False
night_disabled = False
multiplier = 1
sams = True
# Difficulty settings
player_skill: str = "Good"
enemy_skill: str = "Average"
enemy_vehicle_skill: str = "Average"
map_coalition_visibility: ForcedOptions.Views = ForcedOptions.Views.All
labels: str = "Full"
only_player_takeoff: bool = True # Legacy parameter do not use
night_disabled: bool = False
external_views_allowed: bool = True
supercarrier: bool = False
generate_marks: bool = True
manpads: bool = True
version: Optional[str] = None
player_income_multiplier: float = 1.0
enemy_income_multiplier: float = 1.0
default_start_type: str = "Cold"
# Campaign management
automate_runway_repair: bool = False
automate_front_line_reinforcements: bool = False
automate_aircraft_reinforcements: bool = False
restrict_weapons_by_date: bool = False
disable_legacy_aewc: bool = False
generate_dark_kneeboard: bool = False
# Performance oriented
perf_red_alert_state: bool = True
perf_smoke_gen: bool = True
perf_artillery: bool = True
perf_moving_units: bool = True
perf_infantry: bool = True
perf_destroyed_units: bool = True
# Performance culling
perf_culling: bool = False
perf_culling_distance: int = 100
perf_do_not_cull_carrier = True
# LUA Plugins system
plugins: Dict[str, bool] = field(default_factory=dict)
# Cheating
show_red_ato: bool = False
enable_frontline_cheats: bool = False
enable_base_capture_cheat: bool = False
never_delay_player_flights: bool = False
@staticmethod
def plugin_settings_key(identifier: str) -> str:
return f"plugins.{identifier}"
def initialize_plugin_option(self, identifier: str, default_value: bool) -> None:
try:
self.plugin_option(identifier)
except KeyError:
self.set_plugin_option(identifier, default_value)
def plugin_option(self, identifier: str) -> bool:
return self.plugins[self.plugin_settings_key(identifier)]
def set_plugin_option(self, identifier: str, enabled: bool) -> None:
self.plugins[self.plugin_settings_key(identifier)] = enabled
def __setstate__(self, state) -> None:
# __setstate__ is called with the dict of the object being unpickled. We
# can provide save compatibility for new settings options (which
# normally would not be present in the unpickled object) by creating a
# new settings object, updating it with the unpickled state, and
# updating our dict with that.
new_state = Settings().__dict__
new_state.update(state)
self.__dict__.update(new_state)

5
game/theater/__init__.py Normal file
View File

@@ -0,0 +1,5 @@
from .base import *
from .conflicttheater import *
from .controlpoint import *
from .missiontarget import MissionTarget
from .theatergroundobject import SamGroundObject

247
game/theater/base.py Normal file
View File

@@ -0,0 +1,247 @@
import itertools
import logging
import math
import typing
from typing import Dict, Type
from dcs.task import AWACS, CAP, CAS, Embarking, PinpointStrike, Task
from dcs.unittype import FlyingType, UnitType, VehicleType
from dcs.vehicles import AirDefence, Armor
from game import db
from game.db import PRICES
from gen.ground_forces.ai_ground_planner_db import TYPE_SHORAD
STRENGTH_AA_ASSEMBLE_MIN = 0.2
PLANES_SCRAMBLE_MIN_BASE = 2
PLANES_SCRAMBLE_MAX_BASE = 8
PLANES_SCRAMBLE_FACTOR = 0.3
BASE_MAX_STRENGTH = 1
BASE_MIN_STRENGTH = 0
class Base:
def __init__(self):
self.aircraft: Dict[Type[FlyingType], int] = {}
self.armor: Dict[Type[VehicleType], int] = {}
self.aa: Dict[AirDefence, int] = {}
self.commision_points: Dict[Type, float] = {}
self.strength = 1
@property
def total_aircraft(self) -> int:
return sum(self.aircraft.values())
@property
def total_armor(self) -> int:
return sum(self.armor.values())
@property
def total_armor_value(self) -> int:
total = 0
for unit_type, count in self.armor.items():
try:
total += PRICES[unit_type] * count
except KeyError:
logging.exception(f"No price found for {unit_type.id}")
return total
@property
def total_frontline_aa(self) -> int:
return sum([v for k, v in self.armor.items() if k in TYPE_SHORAD])
@property
def total_aa(self) -> int:
return sum(self.aa.values())
def total_units(self, task: Task) -> int:
return sum(
[
c
for t, c in itertools.chain(
self.aircraft.items(), self.armor.items(), self.aa.items()
)
if t in db.UNIT_BY_TASK[task]
]
)
def total_units_of_type(self, unit_type) -> int:
return sum(
[
c
for t, c in itertools.chain(
self.aircraft.items(), self.armor.items(), self.aa.items()
)
if t == unit_type
]
)
@property
def all_units(self):
return itertools.chain(
self.aircraft.items(), self.armor.items(), self.aa.items()
)
def _find_best_unit(
self, available_units: Dict[UnitType, int], for_type: Task, count: int
) -> Dict[UnitType, int]:
if count <= 0:
logging.warning("{}: no units for {}".format(self, for_type))
return {}
sorted_units = [
key for key in available_units if key in db.UNIT_BY_TASK[for_type]
]
sorted_units.sort(key=lambda x: db.PRICES[x], reverse=True)
result: Dict[UnitType, int] = {}
for unit_type in sorted_units:
existing_count = available_units[unit_type] # type: int
if not existing_count:
continue
if count <= 0:
break
result_unit_count = min(count, existing_count)
count -= result_unit_count
assert result_unit_count > 0
result[unit_type] = result.get(unit_type, 0) + result_unit_count
logging.info("{} for {} ({}): {}".format(self, for_type, count, result))
return result
def _find_best_planes(
self, for_type: Task, count: int
) -> typing.Dict[FlyingType, int]:
return self._find_best_unit(self.aircraft, for_type, count)
def _find_best_armor(self, for_type: Task, count: int) -> typing.Dict[Armor, int]:
return self._find_best_unit(self.armor, for_type, count)
def append_commision_points(self, for_type, points: float) -> int:
self.commision_points[for_type] = (
self.commision_points.get(for_type, 0) + points
)
points = self.commision_points[for_type]
if points >= 1:
self.commision_points[for_type] = points - math.floor(points)
return int(math.floor(points))
return 0
def filter_units(self, applicable_units: typing.Collection):
self.aircraft = {
k: v for k, v in self.aircraft.items() if k in applicable_units
}
self.armor = {k: v for k, v in self.armor.items() if k in applicable_units}
def commision_units(self, units: typing.Dict[typing.Any, int]):
for unit_type, unit_count in units.items():
if unit_count <= 0:
continue
for_task = db.unit_task(unit_type)
target_dict = None
if (
for_task == AWACS
or for_task == CAS
or for_task == CAP
or for_task == Embarking
):
target_dict = self.aircraft
elif for_task == PinpointStrike:
target_dict = self.armor
elif for_task == AirDefence:
target_dict = self.aa
if target_dict is not None:
target_dict[unit_type] = target_dict.get(unit_type, 0) + unit_count
else:
logging.error("Unable to determine target dict for " + str(unit_type))
def commit_losses(self, units_lost: typing.Dict[typing.Any, int]):
for unit_type, count in units_lost.items():
if unit_type in self.aircraft:
target_array = self.aircraft
elif unit_type in self.armor:
target_array = self.armor
else:
print("Base didn't find event type {}".format(unit_type))
continue
if unit_type not in target_array:
print("Base didn't find event type {}".format(unit_type))
continue
target_array[unit_type] = max(target_array[unit_type] - count, 0)
if target_array[unit_type] == 0:
del target_array[unit_type]
def affect_strength(self, amount):
self.strength += amount
if self.strength > BASE_MAX_STRENGTH:
self.strength = BASE_MAX_STRENGTH
elif self.strength <= 0:
self.strength = BASE_MIN_STRENGTH
def set_strength_to_minimum(self) -> None:
self.strength = BASE_MIN_STRENGTH
def scramble_count(self, multiplier: float, task: Task = None) -> int:
if task:
count = sum(
[v for k, v in self.aircraft.items() if db.unit_task(k) == task]
)
else:
count = self.total_aircraft
count = int(math.ceil(count * PLANES_SCRAMBLE_FACTOR * self.strength))
return min(
min(
max(count, PLANES_SCRAMBLE_MIN_BASE),
int(PLANES_SCRAMBLE_MAX_BASE * multiplier),
),
count,
)
def assemble_count(self):
return int(self.total_armor * 0.5)
def assemble_aa_count(self) -> int:
# previous logic removed because we always want the full air defense capabilities.
return self.total_aa
def scramble_sweep(self, multiplier: float) -> typing.Dict[FlyingType, int]:
return self._find_best_planes(CAP, self.scramble_count(multiplier, CAP))
def scramble_last_defense(self):
# return as many CAP-capable aircraft as we can since this is the last defense of the base
# (but not more than 20 - that's just nuts)
return self._find_best_planes(CAP, min(self.total_aircraft, 20))
def scramble_cas(self, multiplier: float) -> typing.Dict[FlyingType, int]:
return self._find_best_planes(CAS, self.scramble_count(multiplier, CAS))
def scramble_interceptors(self, multiplier: float) -> typing.Dict[FlyingType, int]:
return self._find_best_planes(CAP, self.scramble_count(multiplier, CAP))
def assemble_attack(self) -> typing.Dict[Armor, int]:
return self._find_best_armor(PinpointStrike, self.assemble_count())
def assemble_defense(self) -> typing.Dict[Armor, int]:
count = int(self.total_armor * min(self.strength + 0.5, 1))
return self._find_best_armor(PinpointStrike, count)
def assemble_aa(self, count=None) -> typing.Dict[AirDefence, int]:
return self._find_best_unit(
self.aa,
AirDefence,
count and min(count, self.total_aa) or self.assemble_aa_count(),
)

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,973 @@
from __future__ import annotations
import heapq
import itertools
import logging
import random
from abc import ABC, abstractmethod
from dataclasses import dataclass, field
from enum import Enum
from functools import total_ordering
from typing import Any, Dict, Iterator, List, Optional, TYPE_CHECKING, Type
from dcs.mapping import Point
from dcs.ships import (
CVN_74_John_C__Stennis,
CV_1143_5_Admiral_Kuznetsov,
LHA_1_Tarawa,
Type_071_Amphibious_Transport_Dock,
)
from dcs.terrain.terrain import Airport, ParkingSlot
from dcs.unittype import FlyingType
from game import db
from gen.flights.closestairfields import ObjectiveDistanceCache
from gen.ground_forces.ai_ground_planner_db import TYPE_SHORAD
from gen.ground_forces.combat_stance import CombatStance
from gen.runways import RunwayAssigner, RunwayData
from .base import Base
from .missiontarget import MissionTarget
from game.point_with_heading import PointWithHeading
from .theatergroundobject import (
BaseDefenseGroundObject,
EwrGroundObject,
GenericCarrierGroundObject,
SamGroundObject,
TheaterGroundObject,
VehicleGroupGroundObject,
)
from ..db import PRICES
from ..utils import nautical_miles
from ..weather import Conditions
if TYPE_CHECKING:
from game import Game
from gen.flights.flight import FlightType
class ControlPointType(Enum):
#: An airbase with slots for everything.
AIRBASE = 0
#: A group with a Stennis type carrier (F/A-18, F-14 compatible).
AIRCRAFT_CARRIER_GROUP = 1
#: A group with a Tarawa carrier (Helicopters & Harrier).
LHA_GROUP = 2
#: A FARP, with slots for helicopters
FARP = 4
#: A FOB (ground units only)
FOB = 5
OFF_MAP = 6
class LocationType(Enum):
BaseAirDefense = "base air defense"
Coastal = "coastal defense"
Ewr = "EWR"
BaseEwr = "Base EWR"
Garrison = "garrison"
MissileSite = "missile site"
OffshoreStrikeTarget = "offshore strike target"
Sam = "SAM"
Ship = "ship"
Shorad = "SHORAD"
StrikeTarget = "strike target"
@dataclass
class PresetLocations:
"""Defines the preset locations loaded from the campaign mission file."""
#: Locations used for spawning ground defenses for bases.
base_garrisons: List[PointWithHeading] = field(default_factory=list)
#: Locations used for spawning air defenses for bases. Used by SAMs, AAA,
#: and SHORADs.
base_air_defense: List[PointWithHeading] = field(default_factory=list)
#: Locations used by EWRs.
ewrs: List[PointWithHeading] = field(default_factory=list)
#: Locations used by Base EWRs.
base_ewrs: List[PointWithHeading] = field(default_factory=list)
#: Locations used by non-carrier ships. Carriers and LHAs are not random.
ships: List[PointWithHeading] = field(default_factory=list)
#: Locations used by coastal defenses.
coastal_defenses: List[PointWithHeading] = field(default_factory=list)
#: Locations used by ground based strike objectives.
strike_locations: List[PointWithHeading] = field(default_factory=list)
#: Locations used by offshore strike objectives.
offshore_strike_locations: List[PointWithHeading] = field(default_factory=list)
#: Locations used by missile sites like scuds and V-2s.
missile_sites: List[PointWithHeading] = field(default_factory=list)
#: Locations of long range SAMs which should always be spawned.
required_long_range_sams: List[PointWithHeading] = field(default_factory=list)
#: Locations of medium range SAMs which should always be spawned.
required_medium_range_sams: List[PointWithHeading] = field(default_factory=list)
#: Locations of EWRs which should always be spawned.
required_ewrs: List[PointWithHeading] = field(default_factory=list)
@staticmethod
def _random_from(points: List[PointWithHeading]) -> Optional[PointWithHeading]:
"""Finds, removes, and returns a random position from the given list."""
if not points:
return None
point = random.choice(points)
points.remove(point)
return point
def random_for(self, location_type: LocationType) -> Optional[PointWithHeading]:
"""Returns a position suitable for the given location type.
The location, if found, will be claimed by the caller and not available
to subsequent calls.
"""
if location_type == LocationType.BaseAirDefense:
return self._random_from(self.base_air_defense)
if location_type == LocationType.Coastal:
return self._random_from(self.coastal_defenses)
if location_type == LocationType.Ewr:
return self._random_from(self.ewrs)
if location_type == LocationType.BaseEwr:
return self._random_from(self.base_ewrs)
if location_type == LocationType.Garrison:
return self._random_from(self.base_garrisons)
if location_type == LocationType.MissileSite:
return self._random_from(self.missile_sites)
if location_type == LocationType.OffshoreStrikeTarget:
return self._random_from(self.offshore_strike_locations)
if location_type == LocationType.Sam:
return self._random_from(self.strike_locations)
if location_type == LocationType.Ship:
return self._random_from(self.ships)
if location_type == LocationType.Shorad:
return self._random_from(self.base_garrisons)
if location_type == LocationType.StrikeTarget:
return self._random_from(self.strike_locations)
logging.error(f"Unknown location type: {location_type}")
return None
@dataclass(frozen=True)
class PendingOccupancy:
present: int
ordered: int
transferring: int
@property
def total(self) -> int:
return self.present + self.ordered + self.transferring
@dataclass
class RunwayStatus:
damaged: bool = False
repair_turns_remaining: Optional[int] = None
def damage(self) -> None:
self.damaged = True
# If the runway is already under repair and is damaged again, progress
# is reset.
self.repair_turns_remaining = None
def begin_repair(self) -> None:
if self.repair_turns_remaining is not None:
logging.error("Runway already under repair. Restarting.")
self.repair_turns_remaining = 4
def process_turn(self) -> None:
if self.repair_turns_remaining is not None:
if self.repair_turns_remaining == 1:
self.repair_turns_remaining = None
self.damaged = False
else:
self.repair_turns_remaining -= 1
@property
def needs_repair(self) -> bool:
return self.damaged and self.repair_turns_remaining is None
def __str__(self) -> str:
if not self.damaged:
return "Runway operational"
turns_remaining = self.repair_turns_remaining
if turns_remaining is None:
return "Runway damaged"
return f"Runway repairing, {turns_remaining} turns remaining"
@total_ordering
class GroundUnitDestination:
def __init__(self, control_point: ControlPoint) -> None:
self.control_point = control_point
@property
def total_value(self) -> float:
return self.control_point.base.total_armor_value
def __eq__(self, other: Any) -> bool:
if not isinstance(other, GroundUnitDestination):
raise TypeError
return self.total_value == other.total_value
def __lt__(self, other: Any) -> bool:
if not isinstance(other, GroundUnitDestination):
raise TypeError
return self.total_value < other.total_value
class ControlPoint(MissionTarget, ABC):
position = None # type: Point
name = None # type: str
captured = False
has_frontline = True
alt = 0
# TODO: Only airbases have IDs.
# TODO: has_frontline is only reasonable for airbases.
# TODO: cptype is obsolete.
def __init__(
self,
cp_id: int,
name: str,
position: Point,
at: db.StartingPosition,
size: int,
importance: float,
has_frontline=True,
cptype=ControlPointType.AIRBASE,
):
super().__init__(name, position)
# TODO: Should be Airbase specific.
self.id = cp_id
self.full_name = name
self.at = at
self.connected_objectives: List[TheaterGroundObject] = []
self.base_defenses: List[BaseDefenseGroundObject] = []
self.preset_locations = PresetLocations()
self.helipads: List[PointWithHeading] = []
# TODO: Should be Airbase specific.
self.size = size
self.importance = importance
self.captured = False
self.captured_invert = False
# TODO: Should be Airbase specific.
self.has_frontline = has_frontline
self.connected_points: List[ControlPoint] = []
self.base: Base = Base()
self.cptype = cptype
# TODO: Should be Airbase specific.
self.stances: Dict[int, CombatStance] = {}
from ..event import UnitsDeliveryEvent
self.pending_unit_deliveries = UnitsDeliveryEvent(self)
self.target_position: Optional[Point] = None
def __repr__(self):
return f"<{__class__}: {self.name}>"
@property
def ground_objects(self) -> List[TheaterGroundObject]:
return list(itertools.chain(self.connected_objectives, self.base_defenses))
@property
@abstractmethod
def heading(self) -> int:
...
def __str__(self):
return self.name
@property
def is_global(self):
return not self.connected_points
@property
def is_carrier(self):
"""
:return: Whether this control point is an aircraft carrier
"""
return False
@property
def is_fleet(self):
"""
:return: Whether this control point is a boat (mobile)
"""
return False
@property
def is_lha(self):
"""
:return: Whether this control point is an LHA
"""
return False
@property
def moveable(self) -> bool:
"""
:return: Whether this control point can be moved around
"""
return False
@property
@abstractmethod
def can_deploy_ground_units(self) -> bool:
...
@property
@abstractmethod
def total_aircraft_parking(self):
"""
:return: The maximum number of aircraft that can be stored in this
control point
"""
...
# TODO: Should be Airbase specific.
def connect(self, to: ControlPoint) -> None:
self.connected_points.append(to)
self.stances[to.id] = CombatStance.DEFENSIVE
@abstractmethod
def runway_is_operational(self) -> bool:
"""
Check whether this control point supports taking offs and landings.
:return:
"""
...
# TODO: Should be naval specific.
def get_carrier_group_name(self):
"""
Get the carrier group name if the airbase is a carrier
:return: Carrier group name
"""
if self.cptype in [
ControlPointType.AIRCRAFT_CARRIER_GROUP,
ControlPointType.LHA_GROUP,
]:
for g in self.ground_objects:
if g.dcs_identifier == "CARRIER":
for group in g.groups:
for u in group.units:
if db.unit_type_from_name(u.type) in [
CVN_74_John_C__Stennis,
CV_1143_5_Admiral_Kuznetsov,
]:
return group.name
elif g.dcs_identifier == "LHA":
for group in g.groups:
for u in group.units:
if db.unit_type_from_name(u.type) in [LHA_1_Tarawa]:
return group.name
return None
# TODO: Should be Airbase specific.
def is_connected(self, to) -> bool:
return to in self.connected_points
def find_ground_objects_by_obj_name(self, obj_name):
found = []
for g in self.ground_objects:
if g.obj_name == obj_name:
found.append(g)
return found
def is_friendly(self, to_player: bool) -> bool:
return self.captured == to_player
# TODO: Should be Airbase specific.
def clear_base_defenses(self) -> None:
for base_defense in self.base_defenses:
p = PointWithHeading.from_point(base_defense.position, base_defense.heading)
if isinstance(base_defense, EwrGroundObject):
self.preset_locations.base_ewrs.append(p)
elif isinstance(base_defense, SamGroundObject):
self.preset_locations.base_air_defense.append(p)
elif isinstance(base_defense, VehicleGroupGroundObject):
self.preset_locations.base_garrisons.append(p)
else:
logging.error(
"Could not determine preset location type for "
f"{base_defense}. Assuming garrison type."
)
self.preset_locations.base_garrisons.append(p)
self.base_defenses = []
def capture_equipment(self, game: Game) -> None:
total = self.base.total_armor_value
self.base.armor.clear()
game.adjust_budget(total, player=not self.captured)
game.message(
f"{self.name} is not connected to any friendly points. Ground "
f"vehicles have been captured and sold for ${total}M."
)
def retreat_ground_units(self, game: Game):
# When there are multiple valid destinations, deliver units to whichever
# base is least defended first. The closest approximation of unit
# strength we have is price
destinations = [
GroundUnitDestination(cp)
for cp in self.connected_points
if cp.captured == self.captured
]
if not destinations:
self.capture_equipment(game)
return
heapq.heapify(destinations)
destination = heapq.heappop(destinations)
while self.base.armor:
unit_type, count = self.base.armor.popitem()
for _ in range(count):
destination.control_point.base.commision_units({unit_type: 1})
destination = heapq.heappushpop(destinations, destination)
def capture_aircraft(
self, game: Game, airframe: Type[FlyingType], count: int
) -> None:
try:
value = PRICES[airframe] * count
except KeyError:
logging.exception(f"Unknown price for {airframe.id}")
return
game.adjust_budget(value, player=not self.captured)
game.message(
f"No valid retreat destination in range of {self.name} for "
f"{airframe.id}. {count} aircraft have been captured and sold for "
f"${value}M."
)
def aircraft_retreat_destination(
self, game: Game, airframe: Type[FlyingType]
) -> Optional[ControlPoint]:
closest = ObjectiveDistanceCache.get_closest_airfields(self)
# TODO: Should be airframe dependent.
max_retreat_distance = nautical_miles(200)
# Skip the first airbase because that's the airbase we're retreating
# from.
airfields = list(closest.airfields_within(max_retreat_distance))[1:]
for airbase in airfields:
if not airbase.can_operate(airframe):
continue
if airbase.captured != self.captured:
continue
if airbase.unclaimed_parking(game) > 0:
return airbase
return None
def _retreat_air_units(
self, game: Game, airframe: Type[FlyingType], count: int
) -> None:
while count:
logging.debug(f"Retreating {count} {airframe.id} from {self.name}")
destination = self.aircraft_retreat_destination(game, airframe)
if destination is None:
self.capture_aircraft(game, airframe, count)
return
parking = destination.unclaimed_parking(game)
transfer_amount = min([parking, count])
destination.base.commision_units({airframe: transfer_amount})
count -= transfer_amount
def retreat_air_units(self, game: Game) -> None:
# TODO: Capture in order of price to retain maximum value?
while self.base.aircraft:
airframe, count = self.base.aircraft.popitem()
self._retreat_air_units(game, airframe, count)
# TODO: Should be Airbase specific.
def capture(self, game: Game, for_player: bool) -> None:
self.pending_unit_deliveries.refund_all(game)
self.retreat_ground_units(game)
self.retreat_air_units(game)
if for_player:
self.captured = True
else:
self.captured = False
self.base.set_strength_to_minimum()
self.clear_base_defenses()
from .start_generator import BaseDefenseGenerator
BaseDefenseGenerator(game, self).generate()
@abstractmethod
def can_operate(self, aircraft: Type[FlyingType]) -> bool:
...
def aircraft_transferring(self, game: Game) -> int:
if self.captured:
ato = game.blue_ato
else:
ato = game.red_ato
total = 0
for package in ato.packages:
for flight in package.flights:
if flight.departure == flight.arrival:
continue
if flight.departure == self:
total -= flight.count
elif flight.arrival == self:
total += flight.count
return total
def expected_aircraft_next_turn(self, game: Game) -> PendingOccupancy:
on_order = 0
for unit_bought in self.pending_unit_deliveries.units:
if issubclass(unit_bought, FlyingType):
on_order += self.pending_unit_deliveries.units[unit_bought]
return PendingOccupancy(
self.base.total_aircraft, on_order, self.aircraft_transferring(game)
)
def unclaimed_parking(self, game: Game) -> int:
return (
self.total_aircraft_parking - self.expected_aircraft_next_turn(game).total
)
@abstractmethod
def active_runway(
self, conditions: Conditions, dynamic_runways: Dict[str, RunwayData]
) -> RunwayData:
...
@property
def parking_slots(self) -> Iterator[ParkingSlot]:
yield from []
@property
@abstractmethod
def runway_status(self) -> RunwayStatus:
...
@property
def runway_can_be_repaired(self) -> bool:
return self.runway_status.needs_repair
def begin_runway_repair(self) -> None:
if not self.runway_can_be_repaired:
logging.error(f"Cannot repair runway at {self}")
return
self.runway_status.begin_repair()
def process_turn(self, game: Game) -> None:
self.pending_unit_deliveries.process(game)
runway_status = self.runway_status
if runway_status is not None:
runway_status.process_turn()
# Process movements for ships control points group
if self.target_position is not None:
delta = self.target_position - self.position
self.position = self.target_position
self.target_position = None
# Move the linked unit groups
for ground_object in self.ground_objects:
if isinstance(ground_object, GenericCarrierGroundObject):
ground_object.position.x = ground_object.position.x + delta.x
ground_object.position.y = ground_object.position.y + delta.y
for group in ground_object.groups:
for u in group.units:
u.position.x = u.position.x + delta.x
u.position.y = u.position.y + delta.y
@property
def pending_frontline_aa_deliveries_count(self):
"""
Get number of pending frontline aa units
"""
if self.pending_unit_deliveries:
return sum(
[
v
for k, v in self.pending_unit_deliveries.units.items()
if k in TYPE_SHORAD
]
)
else:
return 0
@property
def pending_deliveries_count(self):
"""
Get number of pending units
"""
if self.pending_unit_deliveries:
return sum([v for k, v in self.pending_unit_deliveries.units.items()])
else:
return 0
@property
def expected_ground_units_next_turn(self) -> PendingOccupancy:
on_order = 0
for unit_bought in self.pending_unit_deliveries.units:
if issubclass(unit_bought, FlyingType):
continue
if unit_bought in TYPE_SHORAD:
continue
on_order += self.pending_unit_deliveries.units[unit_bought]
return PendingOccupancy(
self.base.total_armor,
on_order,
# Ground unit transfers not yet implemented.
transferring=0,
)
@property
def income_per_turn(self) -> int:
return 0
def mission_types(self, for_player: bool) -> Iterator[FlightType]:
from gen.flights.flight import FlightType
if self.is_friendly(for_player):
yield from [
FlightType.AEWC,
]
yield from super().mission_types(for_player)
@property
def has_active_frontline(self) -> bool:
return any(not c.is_friendly(self.captured) for c in self.connected_points)
class Airfield(ControlPoint):
def __init__(
self, airport: Airport, size: int, importance: float, has_frontline=True
):
super().__init__(
airport.id,
airport.name,
airport.position,
airport,
size,
importance,
has_frontline,
cptype=ControlPointType.AIRBASE,
)
self.airport = airport
self._runway_status = RunwayStatus()
def can_operate(self, aircraft: FlyingType) -> bool:
# TODO: Allow helicopters.
# Need to implement ground spawns so the helos don't use the runway.
# TODO: Allow harrier.
# Needs ground spawns just like helos do, but also need to be able to
# limit takeoff weight to ~20500 lbs or it won't be able to take off.
return self.runway_is_operational()
def mission_types(self, for_player: bool) -> Iterator[FlightType]:
from gen.flights.flight import FlightType
if self.is_friendly(for_player):
yield from [
# TODO: FlightType.INTERCEPTION
# TODO: FlightType.LOGISTICS
]
else:
yield from [
FlightType.OCA_AIRCRAFT,
FlightType.OCA_RUNWAY,
]
yield from super().mission_types(for_player)
@property
def total_aircraft_parking(self) -> int:
return len(self.airport.parking_slots)
@property
def heading(self) -> int:
return self.airport.runways[0].heading
def runway_is_operational(self) -> bool:
return not self.runway_status.damaged
@property
def runway_status(self) -> RunwayStatus:
return self._runway_status
def damage_runway(self) -> None:
self.runway_status.damage()
def active_runway(
self, conditions: Conditions, dynamic_runways: Dict[str, RunwayData]
) -> RunwayData:
assigner = RunwayAssigner(conditions)
return assigner.get_preferred_runway(self.airport)
@property
def parking_slots(self) -> Iterator[ParkingSlot]:
yield from self.airport.parking_slots
@property
def can_deploy_ground_units(self) -> bool:
return True
@property
def income_per_turn(self) -> int:
return 20
class NavalControlPoint(ControlPoint, ABC):
@property
def is_fleet(self) -> bool:
return True
def mission_types(self, for_player: bool) -> Iterator[FlightType]:
from gen.flights.flight import FlightType
if self.is_friendly(for_player):
yield from [
# TODO: FlightType.INTERCEPTION
# TODO: Buddy tanking for the A-4?
# TODO: Rescue chopper?
# TODO: Inter-ship logistics?
]
else:
yield FlightType.ANTISHIP
yield from super().mission_types(for_player)
@property
def heading(self) -> int:
return 0 # TODO compute heading
def runway_is_operational(self) -> bool:
# Necessary because it's possible for the carrier itself to have sunk
# while its escorts are still alive.
for g in self.ground_objects:
if g.dcs_identifier in ["CARRIER", "LHA"]:
for group in g.groups:
for u in group.units:
if db.unit_type_from_name(u.type) in [
CVN_74_John_C__Stennis,
LHA_1_Tarawa,
CV_1143_5_Admiral_Kuznetsov,
Type_071_Amphibious_Transport_Dock,
]:
return True
return False
def active_runway(
self, conditions: Conditions, dynamic_runways: Dict[str, RunwayData]
) -> RunwayData:
# TODO: Assign TACAN and ICLS earlier so we don't need this.
fallback = RunwayData(self.full_name, runway_heading=0, runway_name="")
return dynamic_runways.get(self.name, fallback)
@property
def runway_status(self) -> RunwayStatus:
return RunwayStatus(damaged=not self.runway_is_operational())
@property
def runway_can_be_repaired(self) -> bool:
return False
@property
def moveable(self) -> bool:
return True
@property
def can_deploy_ground_units(self) -> bool:
return False
class Carrier(NavalControlPoint):
def __init__(self, name: str, at: Point, cp_id: int):
import game.theater.conflicttheater
super().__init__(
cp_id,
name,
at,
at,
game.theater.conflicttheater.SIZE_SMALL,
1,
has_frontline=False,
cptype=ControlPointType.AIRCRAFT_CARRIER_GROUP,
)
def capture(self, game: Game, for_player: bool) -> None:
raise RuntimeError("Carriers cannot be captured")
@property
def is_carrier(self):
return True
def can_operate(self, aircraft: FlyingType) -> bool:
return aircraft in db.CARRIER_CAPABLE
@property
def total_aircraft_parking(self) -> int:
return 90
class Lha(NavalControlPoint):
def __init__(self, name: str, at: Point, cp_id: int):
import game.theater.conflicttheater
super().__init__(
cp_id,
name,
at,
at,
game.theater.conflicttheater.SIZE_SMALL,
1,
has_frontline=False,
cptype=ControlPointType.LHA_GROUP,
)
def capture(self, game: Game, for_player: bool) -> None:
raise RuntimeError("LHAs cannot be captured")
@property
def is_lha(self) -> bool:
return True
def can_operate(self, aircraft: FlyingType) -> bool:
return aircraft in db.LHA_CAPABLE
@property
def total_aircraft_parking(self) -> int:
return 20
class OffMapSpawn(ControlPoint):
def runway_is_operational(self) -> bool:
return True
def __init__(self, cp_id: int, name: str, position: Point):
from . import IMPORTANCE_MEDIUM, SIZE_REGULAR
super().__init__(
cp_id,
name,
position,
at=position,
size=SIZE_REGULAR,
importance=IMPORTANCE_MEDIUM,
has_frontline=False,
cptype=ControlPointType.OFF_MAP,
)
def capture(self, game: Game, for_player: bool) -> None:
raise RuntimeError("Off map control points cannot be captured")
def mission_types(self, for_player: bool) -> Iterator[FlightType]:
yield from []
@property
def total_aircraft_parking(self) -> int:
return 1000
def can_operate(self, aircraft: FlyingType) -> bool:
return True
@property
def heading(self) -> int:
return 0
def active_runway(
self, conditions: Conditions, dynamic_runways: Dict[str, RunwayData]
) -> RunwayData:
logging.warning("TODO: Off map spawns have no runways.")
return RunwayData(self.full_name, runway_heading=0, runway_name="")
@property
def runway_status(self) -> RunwayStatus:
return RunwayStatus()
@property
def can_deploy_ground_units(self) -> bool:
return False
class Fob(ControlPoint):
def __init__(self, name: str, at: Point, cp_id: int):
import game.theater.conflicttheater
super().__init__(
cp_id,
name,
at,
at,
game.theater.conflicttheater.SIZE_SMALL,
1,
has_frontline=True,
cptype=ControlPointType.FOB,
)
self.name = name
def runway_is_operational(self) -> bool:
return False
def active_runway(
self, conditions: Conditions, dynamic_runways: Dict[str, RunwayData]
) -> RunwayData:
logging.warning("TODO: FOBs have no runways.")
return RunwayData(self.full_name, runway_heading=0, runway_name="")
@property
def runway_status(self) -> RunwayStatus:
return RunwayStatus()
def mission_types(self, for_player: bool) -> Iterator[FlightType]:
from gen.flights.flight import FlightType
if self.is_friendly(for_player):
yield from [
FlightType.BARCAP,
# TODO: FlightType.LOGISTICS
]
else:
yield from [
FlightType.STRIKE,
FlightType.SWEEP,
FlightType.ESCORT,
FlightType.SEAD,
]
@property
def total_aircraft_parking(self) -> int:
return 0
def can_operate(self, aircraft: FlyingType) -> bool:
return False
@property
def heading(self) -> int:
return 0
@property
def can_deploy_ground_units(self) -> bool:
return True
@property
def income_per_turn(self) -> int:
return 10

48
game/theater/landmap.py Normal file
View File

@@ -0,0 +1,48 @@
from dataclasses import dataclass
import pickle
from functools import cached_property
from typing import Optional, Tuple, Union
import logging
from shapely import geometry
from shapely.geometry import MultiPolygon, Polygon
@dataclass(frozen=True)
class Landmap:
inclusion_zones: MultiPolygon
exclusion_zones: MultiPolygon
sea_zones: MultiPolygon
def __post_init__(self):
if not self.inclusion_zones.is_valid:
raise RuntimeError("Inclusion zones not valid")
if not self.exclusion_zones.is_valid:
raise RuntimeError("Exclusion zones not valid")
if not self.sea_zones.is_valid:
raise RuntimeError("Sea zones not valid")
@cached_property
def inclusion_zone_only(self) -> MultiPolygon:
return self.inclusion_zones - self.exclusion_zones - self.sea_zones
def load_landmap(filename: str) -> Optional[Landmap]:
try:
with open(filename, "rb") as f:
return pickle.load(f)
except:
logging.exception(f"Failed to load landmap {filename}")
return None
def poly_contains(x, y, poly: Union[MultiPolygon, Polygon]):
return poly.contains(geometry.Point(x, y))
def poly_centroid(poly) -> Tuple[float, float]:
x_list = [vertex[0] for vertex in poly]
y_list = [vertex[1] for vertex in poly]
x = sum(x_list) / len(poly)
y = sum(y_list) / len(poly)
return (x, y)

View File

@@ -0,0 +1,44 @@
from __future__ import annotations
from typing import Iterator, TYPE_CHECKING
from dcs.mapping import Point
if TYPE_CHECKING:
from gen.flights.flight import FlightType
class MissionTarget:
def __init__(self, name: str, position: Point) -> None:
"""Initializes a mission target.
Args:
name: The name of the mission target.
position: The location of the mission target.
"""
self.name = name
self.position = position
def distance_to(self, other: MissionTarget) -> int:
"""Computes the distance to the given mission target."""
return self.position.distance_to_point(other.position)
def is_friendly(self, to_player: bool) -> bool:
"""Returns True if the objective is in friendly territory."""
raise NotImplementedError
def mission_types(self, for_player: bool) -> Iterator[FlightType]:
from gen.flights.flight import FlightType
if self.is_friendly(for_player):
yield FlightType.BARCAP
else:
yield from [
FlightType.ESCORT,
FlightType.TARCAP,
FlightType.SEAD,
FlightType.SWEEP,
# TODO: FlightType.ELINT,
# TODO: FlightType.EWAR,
# TODO: FlightType.RECON,
]

View File

@@ -0,0 +1,907 @@
from __future__ import annotations
import logging
import pickle
import random
from dataclasses import dataclass
from datetime import datetime
from typing import Any, Dict, Iterable, List, Optional, Set
from dcs.mapping import Point
from dcs.task import CAP, CAS, PinpointStrike
from dcs.vehicles import AirDefence
from game import Game, db
from game.factions.faction import Faction
from game.theater import Carrier, Lha, LocationType, PointWithHeading
from game.theater.theatergroundobject import (
BuildingGroundObject,
CarrierGroundObject,
EwrGroundObject,
LhaGroundObject,
MissileSiteGroundObject,
SamGroundObject,
ShipGroundObject,
VehicleGroupGroundObject,
CoastalSiteGroundObject,
)
from game.version import VERSION
from gen import namegen
from gen.coastal.coastal_group_generator import generate_coastal_group
from gen.defenses.armor_group_generator import generate_armor_group
from gen.fleet.ship_group_generator import (
generate_carrier_group,
generate_lha_group,
generate_ship_group,
)
from gen.locations.preset_location_finder import MizDataLocationFinder
from gen.missiles.missiles_group_generator import generate_missile_group
from gen.sam.airdefensegroupgenerator import AirDefenseRange
from gen.sam.sam_group_generator import generate_anti_air_group
from gen.sam.ewr_group_generator import generate_ewr_group
from . import (
ConflictTheater,
ControlPoint,
ControlPointType,
Fob,
OffMapSpawn,
)
from ..settings import Settings
GroundObjectTemplates = Dict[str, Dict[str, Any]]
UNIT_VARIETY = 6
UNIT_AMOUNT_FACTOR = 16
UNIT_COUNT_IMPORTANCE_LOG = 1.3
COUNT_BY_TASK = {
PinpointStrike: 12,
CAP: 8,
CAS: 4,
AirDefence: 1,
}
@dataclass(frozen=True)
class GeneratorSettings:
start_date: datetime
player_budget: int
enemy_budget: int
midgame: bool
inverted: bool
no_carrier: bool
no_lha: bool
no_player_navy: bool
no_enemy_navy: bool
class GameGenerator:
def __init__(
self,
player: str,
enemy: str,
theater: ConflictTheater,
settings: Settings,
generator_settings: GeneratorSettings,
) -> None:
self.player = player
self.enemy = enemy
self.theater = theater
self.settings = settings
self.generator_settings = generator_settings
def generate(self) -> Game:
# Reset name generator
namegen.reset()
self.prepare_theater()
game = Game(
player_name=self.player,
enemy_name=self.enemy,
theater=self.theater,
start_date=self.generator_settings.start_date,
settings=self.settings,
player_budget=self.generator_settings.player_budget,
enemy_budget=self.generator_settings.enemy_budget,
)
GroundObjectGenerator(game, self.generator_settings).generate()
game.settings.version = VERSION
return game
def prepare_theater(self) -> None:
to_remove: List[ControlPoint] = []
# Auto-capture half the bases if midgame.
if self.generator_settings.midgame:
control_points = self.theater.controlpoints
for control_point in control_points[: len(control_points) // 2]:
control_point.captured = True
# Remove carrier and lha, invert situation if needed
for cp in self.theater.controlpoints:
if isinstance(cp, Carrier) and self.generator_settings.no_carrier:
to_remove.append(cp)
elif isinstance(cp, Lha) and self.generator_settings.no_lha:
to_remove.append(cp)
if self.generator_settings.inverted:
cp.captured = cp.captured_invert
# do remove
for cp in to_remove:
self.theater.controlpoints.remove(cp)
# TODO: Fix this. This captures all bases for blue.
# reapply midgame inverted if needed
if self.generator_settings.midgame and self.generator_settings.inverted:
for i, cp in enumerate(reversed(self.theater.controlpoints)):
if i > len(self.theater.controlpoints):
break
else:
cp.captured = True
class LocationFinder:
def __init__(self, game: Game, control_point: ControlPoint) -> None:
self.game = game
self.control_point = control_point
self.miz_data = MizDataLocationFinder.compute_possible_locations(
game.theater.terrain.name, control_point.full_name
)
def location_for(self, location_type: LocationType) -> Optional[PointWithHeading]:
position = self.control_point.preset_locations.random_for(location_type)
if position is not None:
return position
logging.warning(
f"No campaign location for %s Mat %s",
location_type.value,
self.control_point,
)
position = self.random_from_miz_data(
location_type == LocationType.OffshoreStrikeTarget
)
if position is not None:
return position
logging.debug(
f"No mizdata location for %s at %s", location_type.value, self.control_point
)
position = self.random_position(location_type)
if position is not None:
return position
logging.error(
f"Could not find position for %s at %s",
location_type.value,
self.control_point,
)
return None
def random_from_miz_data(self, offshore: bool) -> Optional[PointWithHeading]:
if offshore:
locations = self.miz_data.offshore_locations
else:
locations = self.miz_data.ashore_locations
if self.miz_data.offshore_locations:
preset = random.choice(locations)
locations.remove(preset)
return PointWithHeading.from_point(preset.position, preset.heading)
return None
def random_position(
self, location_type: LocationType
) -> Optional[PointWithHeading]:
# TODO: Flesh out preset locations so we never hit this case.
if location_type == LocationType.Coastal:
# No coastal locations generated randomly
return None
logging.warning(
"Falling back to random location for %s at %s",
location_type.value,
self.control_point,
)
is_base_defense = location_type in {
LocationType.BaseAirDefense,
LocationType.Garrison,
LocationType.Shorad,
}
on_land = location_type not in {
LocationType.OffshoreStrikeTarget,
LocationType.Ship,
}
avoid_others = location_type not in {
LocationType.Garrison,
LocationType.MissileSite,
LocationType.Sam,
LocationType.Ship,
LocationType.Shorad,
}
if is_base_defense:
min_range = 400
max_range = 3200
elif location_type == LocationType.Ship:
min_range = 5000
max_range = 40000
elif location_type == LocationType.MissileSite:
min_range = 2500
max_range = 40000
else:
min_range = 10000
max_range = 40000
position = self._find_random_position(
min_range, max_range, on_land, is_base_defense, avoid_others
)
# Retry once, searching a bit further (On some big airbases, 3200 is too
# short (Ex : Incirlik)), but searching farther on every base would be
# problematic, as some base defense units would end up very far away
# from small airfields.
if position is None and is_base_defense:
position = self._find_random_position(
3200, 4800, on_land, is_base_defense, avoid_others
)
return position
def _find_random_position(
self,
min_range: int,
max_range: int,
on_ground: bool,
is_base_defense: bool,
avoid_others: bool,
) -> Optional[PointWithHeading]:
"""
Find a valid ground object location
:param on_ground: Whether it should be on ground or on sea (True = on
ground)
:param min_range: Minimal range from point
:param max_range: Max range from point
:param is_base_defense: True if the location is for base defense.
:return:
"""
near = self.control_point.position
others = self.control_point.ground_objects
def is_valid(point: Optional[PointWithHeading]) -> bool:
if point is None:
return False
if on_ground and not self.game.theater.is_on_land(point):
return False
elif not on_ground and not self.game.theater.is_in_sea(point):
return False
if avoid_others:
for other in others:
if other.position.distance_to_point(point) < 10000:
return False
if is_base_defense:
# If it's a base defense we don't care how close it is to other
# points.
return True
# Else verify that it's not too close to another control point.
for control_point in self.game.theater.controlpoints:
if control_point != self.control_point:
if control_point.position.distance_to_point(point) < 30000:
return False
for ground_obj in control_point.ground_objects:
if ground_obj.position.distance_to_point(point) < 10000:
return False
return True
for _ in range(300):
# Check if on land or sea
p = PointWithHeading.from_point(
near.random_point_within(max_range, min_range), random.randint(0, 360)
)
if is_valid(p):
return p
return None
class ControlPointGroundObjectGenerator:
def __init__(
self,
game: Game,
generator_settings: GeneratorSettings,
control_point: ControlPoint,
) -> None:
self.game = game
self.generator_settings = generator_settings
self.control_point = control_point
self.location_finder = LocationFinder(game, control_point)
@property
def faction_name(self) -> str:
if self.control_point.captured:
return self.game.player_name
else:
return self.game.enemy_name
@property
def faction(self) -> Faction:
return db.FACTIONS[self.faction_name]
def generate(self) -> bool:
self.control_point.connected_objectives = []
if self.faction.navy_generators:
# Even airbases can generate navies if they are close enough to the
# water. This is not controlled by the control point definition, but
# rather by whether or not the generator can find a valid position
# for the ship.
self.generate_navy()
return True
def generate_navy(self) -> None:
skip_player_navy = self.generator_settings.no_player_navy
if self.control_point.captured and skip_player_navy:
return
skip_enemy_navy = self.generator_settings.no_enemy_navy
if not self.control_point.captured and skip_enemy_navy:
return
for _ in range(self.faction.navy_group_count):
self.generate_ship()
def generate_ship(self) -> None:
point = self.location_finder.location_for(LocationType.OffshoreStrikeTarget)
if point is None:
return
group_id = self.game.next_group_id()
g = ShipGroundObject(
namegen.random_objective_name(), group_id, point, self.control_point
)
group = generate_ship_group(self.game, g, self.faction_name)
g.groups = []
if group is not None:
g.groups.append(group)
self.control_point.connected_objectives.append(g)
class NoOpGroundObjectGenerator(ControlPointGroundObjectGenerator):
def generate(self) -> bool:
return True
class CarrierGroundObjectGenerator(ControlPointGroundObjectGenerator):
def generate(self) -> bool:
if not super().generate():
return False
carrier_names = self.faction.carrier_names
if not carrier_names:
logging.info(
f"Skipping generation of {self.control_point.name} because "
f"{self.faction_name} has no carriers"
)
return False
# Create ground object group
group_id = self.game.next_group_id()
g = CarrierGroundObject(
namegen.random_objective_name(), group_id, self.control_point
)
group = generate_carrier_group(self.faction_name, self.game, g)
g.groups = []
if group is not None:
g.groups.append(group)
self.control_point.connected_objectives.append(g)
self.control_point.name = random.choice(carrier_names)
return True
class LhaGroundObjectGenerator(ControlPointGroundObjectGenerator):
def generate(self) -> bool:
if not super().generate():
return False
lha_names = self.faction.helicopter_carrier_names
if not lha_names:
logging.info(
f"Skipping generation of {self.control_point.name} because "
f"{self.faction_name} has no LHAs"
)
return False
# Create ground object group
group_id = self.game.next_group_id()
g = LhaGroundObject(
namegen.random_objective_name(), group_id, self.control_point
)
group = generate_lha_group(self.faction_name, self.game, g)
g.groups = []
if group is not None:
g.groups.append(group)
self.control_point.connected_objectives.append(g)
self.control_point.name = random.choice(lha_names)
return True
class BaseDefenseGenerator:
def __init__(self, game: Game, control_point: ControlPoint) -> None:
self.game = game
self.control_point = control_point
self.location_finder = LocationFinder(game, control_point)
@property
def faction_name(self) -> str:
if self.control_point.captured:
return self.game.player_name
else:
return self.game.enemy_name
@property
def faction(self) -> Faction:
return db.FACTIONS[self.faction_name]
def generate(self) -> None:
self.generate_ewr()
self.generate_garrison()
self.generate_base_defenses()
def generate_ewr(self) -> None:
position = self.location_finder.location_for(LocationType.BaseEwr)
if position is None:
return
group_id = self.game.next_group_id()
g = EwrGroundObject(
namegen.random_objective_name(),
group_id,
position,
self.control_point,
True,
)
group = generate_ewr_group(self.game, g, self.faction)
if group is None:
logging.error(f"Could not generate EWR at {self.control_point}")
return
g.groups = [group]
self.control_point.base_defenses.append(g)
def generate_base_defenses(self) -> None:
# First group has a 1/2 chance of being a SAM, 1/6 chance of SHORAD,
# and a 1/6 chance of a garrison.
#
# Further groups have a 1/3 chance of being SHORAD and 2/3 chance of
# being a garrison.
for i in range(random.randint(2, 5)):
if i == 0 and random.randint(0, 1) == 0:
self.generate_sam()
elif random.randint(0, 2) == 1:
self.generate_shorad()
else:
self.generate_garrison()
def generate_garrison(self) -> None:
position = self.location_finder.location_for(LocationType.Garrison)
if position is None:
return
group_id = self.game.next_group_id()
g = VehicleGroupGroundObject(
namegen.random_objective_name(),
group_id,
position,
self.control_point,
for_airbase=True,
)
group = generate_armor_group(self.faction_name, self.game, g)
if group is None:
logging.error(f"Could not generate garrison at {self.control_point}")
return
g.groups.append(group)
self.control_point.base_defenses.append(g)
def generate_sam(self) -> None:
position = self.location_finder.location_for(LocationType.BaseAirDefense)
if position is None:
return
group_id = self.game.next_group_id()
g = SamGroundObject(
namegen.random_objective_name(),
group_id,
position,
self.control_point,
for_airbase=True,
)
groups = generate_anti_air_group(self.game, g, self.faction)
if not groups:
logging.error(f"Could not generate SAM at {self.control_point}")
return
g.groups = groups
self.control_point.base_defenses.append(g)
def generate_shorad(self) -> None:
position = self.location_finder.location_for(LocationType.BaseAirDefense)
if position is None:
return
group_id = self.game.next_group_id()
g = SamGroundObject(
namegen.random_objective_name(),
group_id,
position,
self.control_point,
for_airbase=True,
)
groups = generate_anti_air_group(
self.game, g, self.faction, ranges=[{AirDefenseRange.Short}]
)
if not groups:
logging.error(f"Could not generate SHORAD group at {self.control_point}")
return
g.groups = groups
self.control_point.base_defenses.append(g)
class FobDefenseGenerator(BaseDefenseGenerator):
def generate(self) -> None:
self.generate_garrison()
self.generate_fob_defenses()
def generate_fob_defenses(self):
# First group has a 1/2 chance of being a SHORAD,
# and a 1/2 chance of a garrison.
#
# Further groups have a 1/3 chance of being SHORAD and 2/3 chance of
# being a garrison.
for i in range(random.randint(2, 5)):
if i == 0 and random.randint(0, 1) == 0:
self.generate_shorad()
elif i == 0 and random.randint(0, 1) == 0:
self.generate_garrison()
elif random.randint(0, 2) == 1:
self.generate_shorad()
else:
self.generate_garrison()
class AirbaseGroundObjectGenerator(ControlPointGroundObjectGenerator):
def __init__(
self,
game: Game,
generator_settings: GeneratorSettings,
control_point: ControlPoint,
templates: GroundObjectTemplates,
) -> None:
super().__init__(game, generator_settings, control_point)
self.templates = templates
def generate(self) -> bool:
if not super().generate():
return False
BaseDefenseGenerator(self.game, self.control_point).generate()
self.generate_ground_points()
if self.faction.missiles:
self.generate_missile_sites()
if self.faction.coastal_defenses:
self.generate_coastal_sites()
return True
def generate_ground_points(self) -> None:
"""Generate ground objects and AA sites for the control point."""
skip_sams = self.generate_required_aa()
skip_ewrs = self.generate_required_ewr()
if self.control_point.is_global:
return
# Always generate at least one AA point.
self.generate_aa_site()
# And between 2 and 7 other objectives.
amount = random.randrange(2, 7)
for i in range(amount):
# 1 in 4 additional objectives are AA.
if random.randint(0, 3) == 0:
if skip_sams > 0:
skip_sams -= 1
else:
self.generate_aa_site()
# 1 in 4 additional objectives are EWR.
elif random.randint(0, 3) == 0:
if skip_ewrs > 0:
skip_ewrs -= 1
else:
self.generate_ewr_site()
else:
self.generate_ground_point()
def generate_required_aa(self) -> int:
"""Generates the AA sites that are required by the campaign.
Returns:
The number of AA sites that were generated.
"""
presets = self.control_point.preset_locations
for position in presets.required_long_range_sams:
self.generate_aa_at(
position,
ranges=[
{AirDefenseRange.Long},
{AirDefenseRange.Medium},
{AirDefenseRange.Short},
],
)
for position in presets.required_medium_range_sams:
self.generate_aa_at(
position,
ranges=[
{AirDefenseRange.Medium},
{AirDefenseRange.Short},
],
)
return len(presets.required_long_range_sams) + len(
presets.required_medium_range_sams
)
def generate_required_ewr(self) -> int:
"""Generates the EWR sites that are required by the campaign.
Returns:
The number of EWR sites that were generated.
"""
presets = self.control_point.preset_locations
for position in presets.required_ewrs:
self.generate_ewr_at(position)
return len(presets.required_ewrs)
def generate_ground_point(self) -> None:
try:
category = random.choice(self.faction.building_set)
except IndexError:
logging.exception("Faction has no buildings defined")
return
obj_name = namegen.random_objective_name()
template = random.choice(list(self.templates[category].values()))
if category == "oil":
location_type = LocationType.OffshoreStrikeTarget
else:
location_type = LocationType.StrikeTarget
# Pick from preset locations
point = self.location_finder.location_for(location_type)
if point is None:
return
object_id = 0
group_id = self.game.next_group_id()
# TODO: Create only one TGO per objective, each with multiple units.
for unit in template:
object_id += 1
template_point = Point(unit["offset"].x, unit["offset"].y)
g = BuildingGroundObject(
obj_name,
category,
group_id,
object_id,
point + template_point,
unit["heading"],
self.control_point,
unit["type"],
)
self.control_point.connected_objectives.append(g)
def generate_aa_site(self) -> None:
position = self.location_finder.location_for(LocationType.Sam)
if position is None:
return
self.generate_aa_at(
position,
ranges=[
# Prefer to use proper SAMs, but fall back to SHORADs if needed.
{AirDefenseRange.Long, AirDefenseRange.Medium},
{AirDefenseRange.Short},
],
)
def generate_aa_at(
self, position: Point, ranges: Iterable[Set[AirDefenseRange]]
) -> None:
group_id = self.game.next_group_id()
g = SamGroundObject(
namegen.random_objective_name(),
group_id,
position,
self.control_point,
for_airbase=False,
)
groups = generate_anti_air_group(self.game, g, self.faction, ranges)
if not groups:
logging.error(
"Could not generate air defense group for %s at %s",
g.name,
self.control_point,
)
return
g.groups = groups
self.control_point.connected_objectives.append(g)
def generate_ewr_site(self) -> None:
position = self.location_finder.location_for(LocationType.Ewr)
if position is None:
return
self.generate_ewr_at(position)
def generate_ewr_at(self, position: Point) -> None:
group_id = self.game.next_group_id()
g = EwrGroundObject(
namegen.random_objective_name(),
group_id,
position,
self.control_point,
for_airbase=False,
)
group = generate_ewr_group(self.game, g, self.faction)
if group is None:
logging.error(
"Could not generate ewr group for %s at %s",
g.name,
self.control_point,
)
return
g.groups = [group]
self.control_point.connected_objectives.append(g)
def generate_missile_sites(self) -> None:
for i in range(self.faction.missiles_group_count):
self.generate_missile_site()
def generate_missile_site(self) -> None:
position = self.location_finder.location_for(LocationType.MissileSite)
if position is None:
return
group_id = self.game.next_group_id()
g = MissileSiteGroundObject(
namegen.random_objective_name(), group_id, position, self.control_point
)
group = generate_missile_group(self.game, g, self.faction_name)
g.groups = []
if group is not None:
g.groups.append(group)
self.control_point.connected_objectives.append(g)
return
def generate_coastal_sites(self) -> None:
for i in range(self.faction.coastal_group_count):
self.generate_coastal_site()
def generate_coastal_site(self) -> None:
position = self.location_finder.location_for(LocationType.Coastal)
if position is None:
return
group_id = self.game.next_group_id()
g = CoastalSiteGroundObject(
namegen.random_objective_name(),
group_id,
position,
self.control_point,
position.heading,
)
group = generate_coastal_group(self.game, g, self.faction_name)
g.groups = []
if group is not None:
g.groups.append(group)
self.control_point.connected_objectives.append(g)
return
class FobGroundObjectGenerator(AirbaseGroundObjectGenerator):
def generate(self) -> bool:
self.generate_fob()
FobDefenseGenerator(self.game, self.control_point).generate()
self.generate_required_aa()
return True
def generate_fob(self) -> None:
try:
category = self.faction.building_set[self.faction.building_set.index("fob")]
except IndexError:
logging.exception("Faction has no fob buildings defined")
return
obj_name = self.control_point.name
template = random.choice(list(self.templates[category].values()))
point = self.control_point.position
# Pick from preset locations
object_id = 0
group_id = self.game.next_group_id()
# TODO: Create only one TGO per objective, each with multiple units.
for unit in template:
object_id += 1
template_point = Point(unit["offset"].x, unit["offset"].y)
g = BuildingGroundObject(
obj_name,
category,
group_id,
object_id,
point + template_point,
unit["heading"],
self.control_point,
unit["type"],
airbase_group=True,
)
self.control_point.connected_objectives.append(g)
class GroundObjectGenerator:
def __init__(self, game: Game, generator_settings: GeneratorSettings) -> None:
self.game = game
self.generator_settings = generator_settings
with open("resources/groundobject_templates.p", "rb") as f:
self.templates: GroundObjectTemplates = pickle.load(f)
def generate(self) -> None:
# Copied so we can remove items from the original list without breaking
# the iterator.
control_points = list(self.game.theater.controlpoints)
for control_point in control_points:
if not self.generate_for_control_point(control_point):
self.game.theater.controlpoints.remove(control_point)
def generate_for_control_point(self, control_point: ControlPoint) -> bool:
generator: ControlPointGroundObjectGenerator
if control_point.cptype == ControlPointType.AIRCRAFT_CARRIER_GROUP:
generator = CarrierGroundObjectGenerator(
self.game, self.generator_settings, control_point
)
elif control_point.cptype == ControlPointType.LHA_GROUP:
generator = LhaGroundObjectGenerator(
self.game, self.generator_settings, control_point
)
elif isinstance(control_point, OffMapSpawn):
generator = NoOpGroundObjectGenerator(
self.game, self.generator_settings, control_point
)
elif isinstance(control_point, Fob):
generator = FobGroundObjectGenerator(
self.game, self.generator_settings, control_point, self.templates
)
else:
generator = AirbaseGroundObjectGenerator(
self.game, self.generator_settings, control_point, self.templates
)
return generator.generate()

View File

@@ -0,0 +1,501 @@
from __future__ import annotations
import itertools
import logging
from typing import Iterator, List, TYPE_CHECKING
from dcs.mapping import Point
from dcs.unit import Unit
from dcs.unitgroup import Group
from .. import db
from ..data.radar_db import UNITS_WITH_RADAR
from ..utils import Distance, meters
if TYPE_CHECKING:
from .controlpoint import ControlPoint
from gen.flights.flight import FlightType
from .missiontarget import MissionTarget
NAME_BY_CATEGORY = {
"power": "Power plant",
"ammo": "Ammo depot",
"fuel": "Fuel depot",
"aa": "AA Defense Site",
"ware": "Warehouse",
"farp": "FARP",
"fob": "FOB",
"factory": "Factory",
"comms": "Comms. tower",
"oil": "Oil platform",
"derrick": "Derrick",
"ww2bunker": "Bunker",
"village": "Village",
"allycamp": "Camp",
"EWR": "EWR",
}
ABBREV_NAME = {
"power": "PLANT",
"ammo": "AMMO",
"fuel": "FUEL",
"aa": "AA",
"ware": "WARE",
"farp": "FARP",
"fob": "FOB",
"factory": "FACTORY",
"comms": "COMMST",
"oil": "OILP",
"derrick": "DERK",
"ww2bunker": "BUNK",
"village": "VLG",
"allycamp": "CMP",
}
CATEGORY_MAP = {
# Special cases
"CARRIER": ["CARRIER"],
"LHA": ["LHA"],
"aa": ["AA"],
# Buildings
"power": [
"Workshop A",
"Electric power box",
"Garage small A",
"Farm B",
"Repair workshop",
"Garage B",
],
"ware": ["Warehouse", "Hangar A"],
"fuel": ["Tank", "Tank 2", "Tank 3", "Fuel tank"],
"ammo": [".Ammunition depot", "Hangar B"],
"farp": [
"FARP Tent",
"FARP Ammo Dump Coating",
"FARP Fuel Depot",
"FARP Command Post",
"FARP CP Blindage",
],
"fob": ["Bunker 2", "Bunker 1", "Garage small B", ".Command Center", "Barracks 2"],
"factory": ["Tech combine", "Tech hangar A"],
"comms": ["TV tower", "Comms tower M"],
"oil": ["Oil platform"],
"derrick": ["Oil derrick", "Pump station", "Subsidiary structure 2"],
"ww2bunker": [
"Siegfried Line",
"Fire Control Bunker",
"SK_C_28_naval_gun",
"Concertina Wire",
"Czech hedgehogs 1",
],
"village": ["Small house 1B", "Small House 1A", "Small warehouse 1"],
"allycamp": [],
}
class TheaterGroundObject(MissionTarget):
def __init__(
self,
name: str,
category: str,
group_id: int,
position: Point,
heading: int,
control_point: ControlPoint,
dcs_identifier: str,
airbase_group: bool,
sea_object: bool,
) -> None:
super().__init__(name, position)
self.category = category
self.group_id = group_id
self.heading = heading
self.control_point = control_point
self.dcs_identifier = dcs_identifier
self.airbase_group = airbase_group
self.sea_object = sea_object
self.groups: List[Group] = []
@property
def is_dead(self) -> bool:
return self.alive_unit_count == 0
@property
def units(self) -> List[Unit]:
"""
:return: all the units at this location
"""
return list(itertools.chain.from_iterable([g.units for g in self.groups]))
@property
def group_name(self) -> str:
"""The name of the unit group."""
return f"{self.category}|{self.group_id}"
@property
def waypoint_name(self) -> str:
return f"[{self.name}] {self.category}"
def __str__(self) -> str:
return NAME_BY_CATEGORY[self.category]
def is_same_group(self, identifier: str) -> bool:
return self.group_id == identifier
@property
def obj_name(self) -> str:
return self.name
@property
def faction_color(self) -> str:
return "BLUE" if self.control_point.captured else "RED"
def is_friendly(self, to_player: bool) -> bool:
return self.control_point.is_friendly(to_player)
def mission_types(self, for_player: bool) -> Iterator[FlightType]:
from gen.flights.flight import FlightType
if self.is_friendly(for_player):
yield from [
# TODO: FlightType.LOGISTICS
# TODO: FlightType.TROOP_TRANSPORT
]
else:
yield from [
FlightType.STRIKE,
FlightType.BAI,
]
yield from super().mission_types(for_player)
@property
def alive_unit_count(self) -> int:
return sum(len(g.units) for g in self.groups)
@property
def might_have_aa(self) -> bool:
return False
@property
def has_radar(self) -> bool:
"""Returns True if the ground object contains a unit with radar."""
for group in self.groups:
for unit in group.units:
if db.unit_type_from_name(unit.type) in UNITS_WITH_RADAR:
return True
return False
def _max_range_of_type(self, group: Group, range_type: str) -> Distance:
if not self.might_have_aa:
return meters(0)
max_range = meters(0)
for u in group.units:
unit = db.unit_type_from_name(u.type)
if unit is None:
logging.error(f"Unknown unit type {u.type}")
continue
# Some units in pydcs have detection_range/threat_range defined,
# but explicitly set to None.
unit_range = getattr(unit, range_type, None)
if unit_range is not None:
max_range = max(max_range, meters(unit_range))
return max_range
def detection_range(self, group: Group) -> Distance:
return self._max_range_of_type(group, "detection_range")
def threat_range(self, group: Group) -> Distance:
if not self.detection_range(group):
# For simple SAMs like shilkas, the unit has both a threat and
# detection range. For complex sites like SA-2s, the launcher has a
# threat range and the search/track radars have detection ranges. If
# the site has no detection range it has no radars and can't fire,
# so it's not actually a threat even if it still has launchers.
return meters(0)
return self._max_range_of_type(group, "threat_range")
class BuildingGroundObject(TheaterGroundObject):
def __init__(
self,
name: str,
category: str,
group_id: int,
object_id: int,
position: Point,
heading: int,
control_point: ControlPoint,
dcs_identifier: str,
airbase_group=False,
) -> None:
super().__init__(
name=name,
category=category,
group_id=group_id,
position=position,
heading=heading,
control_point=control_point,
dcs_identifier=dcs_identifier,
airbase_group=airbase_group,
sea_object=False,
)
self.object_id = object_id
# Other TGOs track deadness based on the number of alive units, but
# buildings don't have groups assigned to the TGO.
self._dead = False
@property
def group_name(self) -> str:
"""The name of the unit group."""
return f"{self.category}|{self.group_id}|{self.object_id}"
@property
def waypoint_name(self) -> str:
return f"{super().waypoint_name} #{self.object_id}"
@property
def is_dead(self) -> bool:
if not hasattr(self, "_dead"):
self._dead = False
return self._dead
def kill(self) -> None:
self._dead = True
class NavalGroundObject(TheaterGroundObject):
def mission_types(self, for_player: bool) -> Iterator[FlightType]:
from gen.flights.flight import FlightType
if not self.is_friendly(for_player):
yield FlightType.ANTISHIP
yield from super().mission_types(for_player)
@property
def might_have_aa(self) -> bool:
return True
class GenericCarrierGroundObject(NavalGroundObject):
pass
# TODO: Why is this both a CP and a TGO?
class CarrierGroundObject(GenericCarrierGroundObject):
def __init__(self, name: str, group_id: int, control_point: ControlPoint) -> None:
super().__init__(
name=name,
category="CARRIER",
group_id=group_id,
position=control_point.position,
heading=0,
control_point=control_point,
dcs_identifier="CARRIER",
airbase_group=True,
sea_object=True,
)
@property
def group_name(self) -> str:
# Prefix the group names with the side color so Skynet can find them,
# add to EWR.
return f"{self.faction_color}|EWR|{super().group_name}"
# TODO: Why is this both a CP and a TGO?
class LhaGroundObject(GenericCarrierGroundObject):
def __init__(self, name: str, group_id: int, control_point: ControlPoint) -> None:
super().__init__(
name=name,
category="LHA",
group_id=group_id,
position=control_point.position,
heading=0,
control_point=control_point,
dcs_identifier="LHA",
airbase_group=True,
sea_object=True,
)
@property
def group_name(self) -> str:
# Prefix the group names with the side color so Skynet can find them,
# add to EWR.
return f"{self.faction_color}|EWR|{super().group_name}"
class MissileSiteGroundObject(TheaterGroundObject):
def __init__(
self, name: str, group_id: int, position: Point, control_point: ControlPoint
) -> None:
super().__init__(
name=name,
category="aa",
group_id=group_id,
position=position,
heading=0,
control_point=control_point,
dcs_identifier="AA",
airbase_group=False,
sea_object=False,
)
class CoastalSiteGroundObject(TheaterGroundObject):
def __init__(
self,
name: str,
group_id: int,
position: Point,
control_point: ControlPoint,
heading,
) -> None:
super().__init__(
name=name,
category="aa",
group_id=group_id,
position=position,
heading=heading,
control_point=control_point,
dcs_identifier="AA",
airbase_group=False,
sea_object=False,
)
class BaseDefenseGroundObject(TheaterGroundObject):
"""Base type for all base defenses."""
# TODO: Differentiate types.
# This type gets used both for AA sites (SAM, AAA, or SHORAD). These should each
# be split into their own types.
class SamGroundObject(BaseDefenseGroundObject):
def __init__(
self,
name: str,
group_id: int,
position: Point,
control_point: ControlPoint,
for_airbase: bool,
) -> None:
super().__init__(
name=name,
category="aa",
group_id=group_id,
position=position,
heading=0,
control_point=control_point,
dcs_identifier="AA",
airbase_group=for_airbase,
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
if not self.is_friendly(for_player):
yield FlightType.DEAD
yield from super().mission_types(for_player)
@property
def might_have_aa(self) -> bool:
return True
class VehicleGroupGroundObject(BaseDefenseGroundObject):
def __init__(
self,
name: str,
group_id: int,
position: Point,
control_point: ControlPoint,
for_airbase: bool,
) -> None:
super().__init__(
name=name,
category="aa",
group_id=group_id,
position=position,
heading=0,
control_point=control_point,
dcs_identifier="AA",
airbase_group=for_airbase,
sea_object=False,
)
class EwrGroundObject(BaseDefenseGroundObject):
def __init__(
self,
name: str,
group_id: int,
position: Point,
control_point: ControlPoint,
for_airbase: bool,
) -> None:
super().__init__(
name=name,
category="EWR",
group_id=group_id,
position=position,
heading=0,
control_point=control_point,
dcs_identifier="EWR",
airbase_group=for_airbase,
sea_object=False,
)
@property
def group_name(self) -> str:
# Prefix the group names with the side color so Skynet can find them.
return f"{self.faction_color}|{super().group_name}"
def mission_types(self, for_player: bool) -> Iterator[FlightType]:
from gen.flights.flight import FlightType
if not self.is_friendly(for_player):
yield FlightType.DEAD
yield from super().mission_types(for_player)
@property
def might_have_aa(self) -> bool:
return True
class ShipGroundObject(NavalGroundObject):
def __init__(
self, name: str, group_id: int, position: Point, control_point: ControlPoint
) -> None:
super().__init__(
name=name,
category="aa",
group_id=group_id,
position=position,
heading=0,
control_point=control_point,
dcs_identifier="AA",
airbase_group=False,
sea_object=True,
)
@property
def group_name(self) -> str:
# Prefix the group names with the side color so Skynet can find them,
# add to EWR.
return f"{self.faction_color}|EWR|{super().group_name}"

161
game/threatzones.py Normal file
View File

@@ -0,0 +1,161 @@
from __future__ import annotations
from functools import singledispatchmethod
from typing import Optional, TYPE_CHECKING, Union
from dcs.mapping import Point as DcsPoint
from shapely.geometry import (
LineString,
MultiPolygon,
Point as ShapelyPoint,
Polygon,
)
from shapely.geometry.base import BaseGeometry
from shapely.ops import nearest_points, unary_union
from game.theater import ControlPoint
from game.utils import Distance, meters, nautical_miles
from gen import Conflict
from gen.flights.closestairfields import ObjectiveDistanceCache
from gen.flights.flight import Flight
if TYPE_CHECKING:
from game import Game
ThreatPoly = Union[MultiPolygon, Polygon]
class ThreatZones:
def __init__(self, airbases: ThreatPoly, air_defenses: ThreatPoly) -> None:
self.airbases = airbases
self.air_defenses = air_defenses
self.all = unary_union([airbases, air_defenses])
def closest_boundary(self, point: DcsPoint) -> DcsPoint:
boundary, _ = nearest_points(
self.all.boundary, self.dcs_to_shapely_point(point)
)
return DcsPoint(boundary.x, boundary.y)
@singledispatchmethod
def threatened(self, position) -> bool:
raise NotImplementedError
@threatened.register
def _threatened_geometry(self, position: BaseGeometry) -> bool:
return self.all.intersects(position)
@threatened.register
def _threatened_dcs_point(self, position: DcsPoint) -> bool:
return self.all.intersects(self.dcs_to_shapely_point(position))
def path_threatened(self, a: DcsPoint, b: DcsPoint) -> bool:
return self.threatened(
LineString([self.dcs_to_shapely_point(a), self.dcs_to_shapely_point(b)])
)
@singledispatchmethod
def threatened_by_aircraft(self, target) -> bool:
raise NotImplementedError
@threatened_by_aircraft.register
def _threatened_by_aircraft_geom(self, position: BaseGeometry) -> bool:
return self.airbases.intersects(position)
@threatened_by_aircraft.register
def _threatened_by_aircraft_flight(self, flight: Flight) -> bool:
return self.threatened_by_aircraft(
LineString((self.dcs_to_shapely_point(p.position) for p in flight.points))
)
@singledispatchmethod
def threatened_by_air_defense(self, target) -> bool:
raise NotImplementedError
@threatened_by_air_defense.register
def _threatened_by_air_defense_geom(self, position: BaseGeometry) -> bool:
return self.air_defenses.intersects(position)
@threatened_by_air_defense.register
def _threatened_by_air_defense_flight(self, flight: Flight) -> bool:
return self.threatened_by_air_defense(
LineString((self.dcs_to_shapely_point(p.position) for p in flight.points))
)
@classmethod
def closest_enemy_airbase(
cls, location: ControlPoint, max_distance: Distance
) -> Optional[ControlPoint]:
airfields = ObjectiveDistanceCache.get_closest_airfields(location)
for airfield in airfields.airfields_within(max_distance):
if airfield.captured != location.captured:
return airfield
return None
@classmethod
def barcap_threat_range(cls, game: Game, control_point: ControlPoint) -> Distance:
doctrine = game.faction_for(control_point.captured).doctrine
cap_threat_range = (
doctrine.cap_max_distance_from_cp + doctrine.cap_engagement_range
)
opposing_airfield = cls.closest_enemy_airbase(
control_point, cap_threat_range * 2
)
if opposing_airfield is None:
return cap_threat_range
airfield_distance = meters(
opposing_airfield.position.distance_to_point(control_point.position)
)
# BARCAPs should not commit further than halfway to the closest enemy
# airfield (with some breathing room) to avoid those missions becoming
# offensive. For dissimilar doctrines we could weight this so that, as
# an example, modern US goes no closer than 70% of the way to the WW2
# German base, and the Germans go no closer than 30% of the way to the
# US base, but for now equal weighting is fine.
max_distance = airfield_distance * 0.45
return min(cap_threat_range, max_distance)
@classmethod
def for_faction(cls, game: Game, player: bool) -> ThreatZones:
"""Generates the threat zones projected by the given coalition.
Args:
game: The game to generate the threat zone for.
player: True if the coalition projecting the threat zone belongs to
the player.
Returns:
The threat zones projected by the given coalition. If the threat
zone belongs to the player, it is the zone that will be avoided by
the enemy and vice versa.
"""
air_threats = []
air_defenses = []
for control_point in game.theater.controlpoints:
if control_point.captured != player:
continue
if control_point.runway_is_operational():
point = ShapelyPoint(control_point.position.x, control_point.position.y)
cap_threat_range = cls.barcap_threat_range(game, control_point)
air_threats.append(point.buffer(cap_threat_range.meters))
for tgo in control_point.ground_objects:
for group in tgo.groups:
threat_range = tgo.threat_range(group)
# Any system with a shorter range than this is not worth
# even avoiding.
if threat_range > nautical_miles(3):
point = ShapelyPoint(tgo.position.x, tgo.position.y)
threat_zone = point.buffer(threat_range.meters)
air_defenses.append(threat_zone)
return cls(
airbases=unary_union(air_threats), air_defenses=unary_union(air_defenses)
)
@staticmethod
def dcs_to_shapely_point(point: DcsPoint) -> ShapelyPoint:
return ShapelyPoint(point.x, point.y)

140
game/unitmap.py Normal file
View File

@@ -0,0 +1,140 @@
"""Maps generated units back to their Liberation types."""
from dataclasses import dataclass
from typing import Dict, Optional, Type
from dcs.unit import Unit
from dcs.unitgroup import FlyingGroup, Group, VehicleGroup
from dcs.unittype import VehicleType
from game import db
from game.theater import Airfield, ControlPoint, TheaterGroundObject
from game.theater.theatergroundobject import BuildingGroundObject
from gen.flights.flight import Flight
@dataclass(frozen=True)
class FrontLineUnit:
unit_type: Type[VehicleType]
origin: ControlPoint
@dataclass(frozen=True)
class GroundObjectUnit:
ground_object: TheaterGroundObject
group: Group
unit: Unit
@dataclass(frozen=True)
class Building:
ground_object: BuildingGroundObject
class UnitMap:
def __init__(self) -> None:
self.aircraft: Dict[str, Flight] = {}
self.airfields: Dict[str, Airfield] = {}
self.front_line_units: Dict[str, FrontLineUnit] = {}
self.ground_object_units: Dict[str, GroundObjectUnit] = {}
self.buildings: Dict[str, Building] = {}
def add_aircraft(self, group: FlyingGroup, flight: Flight) -> None:
for unit in group.units:
# The actual name is a String (the pydcs translatable string), which
# doesn't define __eq__.
name = str(unit.name)
if name in self.aircraft:
raise RuntimeError(f"Duplicate unit name: {name}")
self.aircraft[name] = flight
def flight(self, unit_name: str) -> Optional[Flight]:
return self.aircraft.get(unit_name, None)
def add_airfield(self, airfield: Airfield) -> None:
if airfield.name in self.airfields:
raise RuntimeError(f"Duplicate airfield: {airfield.name}")
self.airfields[airfield.name] = airfield
def airfield(self, name: str) -> Optional[Airfield]:
return self.airfields.get(name, None)
def add_front_line_units(self, group: Group, origin: ControlPoint) -> None:
for unit in group.units:
# The actual name is a String (the pydcs translatable string), which
# doesn't define __eq__.
name = str(unit.name)
if name in self.front_line_units:
raise RuntimeError(f"Duplicate front line unit: {name}")
unit_type = db.unit_type_from_name(unit.type)
if unit_type is None:
raise RuntimeError(f"Unknown unit type: {unit.type}")
if not issubclass(unit_type, VehicleType):
raise RuntimeError(
f"{name} is a {unit_type.__name__}, expected a VehicleType"
)
self.front_line_units[name] = FrontLineUnit(unit_type, origin)
def front_line_unit(self, name: str) -> Optional[FrontLineUnit]:
return self.front_line_units.get(name, None)
def add_ground_object_units(
self,
ground_object: TheaterGroundObject,
persistence_group: Group,
miz_group: Group,
) -> None:
"""Adds a group associated with a TGO to the unit map.
Args:
ground_object: The TGO the group is associated with.
persistence_group: The Group tracked by the TGO itself.
miz_group: The Group spawned for the miz to match persistence_group.
"""
# Deaths for units at TGOs are recorded in the Group that is contained
# by the TGO, but when groundobjectsgen populates the miz it creates new
# groups based on that template, so the units and groups in the miz are
# not a direct match for the units and groups that persist in the TGO.
#
# This means that we need to map the spawned unit names back to the
# original TGO units, not the ones in the miz.
if len(persistence_group.units) != len(miz_group.units):
raise ValueError("Persistent group does not match generated group")
unit_pairs = zip(persistence_group.units, miz_group.units)
for persistent_unit, miz_unit in unit_pairs:
# The actual name is a String (the pydcs translatable string), which
# doesn't define __eq__.
name = str(miz_unit.name)
if name in self.ground_object_units:
raise RuntimeError(f"Duplicate TGO unit: {name}")
self.ground_object_units[name] = GroundObjectUnit(
ground_object, persistence_group, persistent_unit
)
def ground_object_unit(self, name: str) -> Optional[GroundObjectUnit]:
return self.ground_object_units.get(name, None)
def add_building(self, ground_object: BuildingGroundObject, group: Group) -> None:
# The actual name is a String (the pydcs translatable string), which
# doesn't define __eq__.
# The name of the initiator in the DCS dead event will have " object"
# appended for statics.
name = f"{group.name} object"
if name in self.buildings:
raise RuntimeError(f"Duplicate TGO unit: {name}")
self.buildings[name] = Building(ground_object)
def add_fortification(
self, ground_object: BuildingGroundObject, group: VehicleGroup
) -> None:
if len(group.units) != 1:
raise ValueError("Fortification groups must have exactly one unit.")
unit = group.units[0]
# The actual name is a String (the pydcs translatable string), which
# doesn't define __eq__.
name = str(unit.name)
if name in self.buildings:
raise RuntimeError(f"Duplicate TGO unit: {name}")
self.buildings[name] = Building(ground_object)
def building_or_fortification(self, name: str) -> Optional[Building]:
return self.buildings.get(name, None)

180
game/utils.py Normal file
View File

@@ -0,0 +1,180 @@
from __future__ import annotations
import math
from dataclasses import dataclass
from typing import Union
METERS_TO_FEET = 3.28084
FEET_TO_METERS = 1 / METERS_TO_FEET
NM_TO_METERS = 1852
METERS_TO_NM = 1 / NM_TO_METERS
KNOTS_TO_KPH = 1.852
KPH_TO_KNOTS = 1 / KNOTS_TO_KPH
MS_TO_KPH = 3.6
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
def opposite_heading(h):
return heading_sum(h, 180)
@dataclass(frozen=True, order=True)
class Distance:
distance_in_meters: float
@property
def feet(self) -> float:
return self.distance_in_meters * METERS_TO_FEET
@property
def meters(self) -> float:
return self.distance_in_meters
@property
def nautical_miles(self) -> float:
return self.distance_in_meters * METERS_TO_NM
@classmethod
def from_feet(cls, value: float) -> Distance:
return cls(value * FEET_TO_METERS)
@classmethod
def from_meters(cls, value: float) -> Distance:
return cls(value)
@classmethod
def from_nautical_miles(cls, value: float) -> Distance:
return cls(value * NM_TO_METERS)
def __add__(self, other: Distance) -> Distance:
return meters(self.meters + other.meters)
def __sub__(self, other: Distance) -> Distance:
return meters(self.meters - other.meters)
def __mul__(self, other: Union[float, int]) -> Distance:
return meters(self.meters * other)
def __truediv__(self, other: Union[float, int]) -> Distance:
return meters(self.meters / other)
def __floordiv__(self, other: Union[float, int]) -> Distance:
return meters(self.meters // other)
def __bool__(self) -> bool:
return not math.isclose(self.meters, 0.0)
def feet(value: float) -> Distance:
return Distance.from_feet(value)
def meters(value: float) -> Distance:
return Distance.from_meters(value)
def nautical_miles(value: float) -> Distance:
return Distance.from_nautical_miles(value)
@dataclass(frozen=True, order=True)
class Speed:
speed_in_kph: float
@property
def knots(self) -> float:
return self.speed_in_kph * KPH_TO_KNOTS
@property
def kph(self) -> float:
return self.speed_in_kph
@property
def meters_per_second(self) -> float:
return self.speed_in_kph * KPH_TO_MS
def mach(self, altitude: Distance = meters(0)) -> float:
c_sound = mach(1, altitude)
return self.speed_in_kph / c_sound.kph
@classmethod
def from_knots(cls, value: float) -> Speed:
return cls(value * KNOTS_TO_KPH)
@classmethod
def from_kph(cls, value: float) -> Speed:
return cls(value)
@classmethod
def from_meters_per_second(cls, value: float) -> Speed:
return cls(value * MS_TO_KPH)
@classmethod
def from_mach(cls, value: float, altitude: Distance) -> Speed:
# https://www.grc.nasa.gov/WWW/K-12/airplane/atmos.html
if altitude <= feet(36152):
temperature_f = 59 - 0.00356 * altitude.feet
else:
# There's another formula for altitudes over 82k feet, but we better
# not be planning waypoints that high...
temperature_f = -70
temperature_k = (temperature_f + 459.67) * (5 / 9)
# https://www.engineeringtoolbox.com/specific-heat-ratio-d_602.html
# Dependent on temperature, but varies very little (+/-0.001)
# between -40F and 180F.
heat_capacity_ratio = 1.4
# https://www.grc.nasa.gov/WWW/K-12/airplane/sound.html
gas_constant = 286 # m^2/s^2/K
c_sound = math.sqrt(heat_capacity_ratio * gas_constant * temperature_k)
return mps(c_sound) * value
def __add__(self, other: Speed) -> Speed:
return kph(self.kph + other.kph)
def __sub__(self, other: Speed) -> Speed:
return kph(self.kph - other.kph)
def __mul__(self, other: Union[float, int]) -> Speed:
return kph(self.kph * other)
def __truediv__(self, other: Union[float, int]) -> Speed:
return kph(self.kph / other)
def __floordiv__(self, other: Union[float, int]) -> Speed:
return kph(self.kph // other)
def __bool__(self) -> bool:
return not math.isclose(self.kph, 0.0)
def knots(value: float) -> Speed:
return Speed.from_knots(value)
def kph(value: float) -> Speed:
return Speed.from_kph(value)
def mps(value: float) -> Speed:
return Speed.from_meters_per_second(value)
def mach(value: float, altitude: Distance) -> Speed:
return Speed.from_mach(value, altitude)
SPEED_OF_SOUND_AT_SEA_LEVEL = knots(661.5)

18
game/version.py Normal file
View File

@@ -0,0 +1,18 @@
from pathlib import Path
def _build_version_string() -> str:
components = ["2.5"]
build_number_path = Path("resources/buildnumber")
if build_number_path.exists():
with build_number_path.open("r") as build_number_file:
components.append(build_number_file.readline())
if not Path("resources/final").exists():
components.append("preview")
return "-".join(components)
#: Current version of Liberation.
VERSION = _build_version_string()

212
game/weather.py Normal file
View File

@@ -0,0 +1,212 @@
from __future__ import annotations
import datetime
import logging
import random
from dataclasses import dataclass, field
from enum import Enum
from typing import Optional, TYPE_CHECKING
from dcs.cloud_presets import Clouds as PydcsClouds
from dcs.weather import CloudPreset, Weather as PydcsWeather, Wind
from game.settings import Settings
from game.utils import Distance, meters
if TYPE_CHECKING:
from game.theater import ConflictTheater
class TimeOfDay(Enum):
Dawn = "dawn"
Day = "day"
Dusk = "dusk"
Night = "night"
@dataclass(frozen=True)
class WindConditions:
at_0m: Wind
at_2000m: Wind
at_8000m: Wind
@dataclass(frozen=True)
class Clouds:
base: int
density: int
thickness: int
precipitation: PydcsWeather.Preceptions
preset: Optional[CloudPreset] = field(default=None)
@classmethod
def random_preset(cls, rain: bool) -> Clouds:
clouds = (p.value for p in PydcsClouds)
if rain:
presets = [p for p in clouds if "Rain" in p.name]
else:
presets = [p for p in clouds if "Rain" not in p.name]
preset = random.choice(presets)
return Clouds(
base=random.randint(preset.min_base, preset.max_base),
density=0,
thickness=0,
precipitation=PydcsWeather.Preceptions.None_,
preset=preset,
)
@dataclass(frozen=True)
class Fog:
visibility: Distance
thickness: int
class Weather:
def __init__(self) -> None:
self.clouds = self.generate_clouds()
self.fog = self.generate_fog()
self.wind = self.generate_wind()
def generate_clouds(self) -> Optional[Clouds]:
raise NotImplementedError
def generate_fog(self) -> Optional[Fog]:
if random.randrange(5) != 0:
return None
return Fog(
visibility=meters(random.randint(2500, 5000)),
thickness=random.randint(100, 500),
)
def generate_wind(self) -> WindConditions:
raise NotImplementedError
@staticmethod
def random_wind(minimum: int, maximum) -> WindConditions:
wind_direction = random.randint(0, 360)
at_0m_factor = 1
at_2000m_factor = 2
at_8000m_factor = 3
base_wind = random.randint(minimum, maximum)
return WindConditions(
# Always some wind to make the smoke move a bit.
at_0m=Wind(wind_direction, max(1, base_wind * at_0m_factor)),
at_2000m=Wind(wind_direction, base_wind * at_2000m_factor),
at_8000m=Wind(wind_direction, base_wind * at_8000m_factor),
)
@staticmethod
def random_cloud_base() -> int:
return random.randint(2000, 3000)
@staticmethod
def random_cloud_thickness() -> int:
return random.randint(100, 400)
class ClearSkies(Weather):
def generate_clouds(self) -> Optional[Clouds]:
return None
def generate_fog(self) -> Optional[Fog]:
return None
def generate_wind(self) -> WindConditions:
return self.random_wind(0, 0)
class Cloudy(Weather):
def generate_clouds(self) -> Optional[Clouds]:
return Clouds.random_preset(rain=False)
def generate_fog(self) -> Optional[Fog]:
# DCS 2.7 says to not use fog with the cloud presets.
return None
def generate_wind(self) -> WindConditions:
return self.random_wind(0, 4)
class Raining(Weather):
def generate_clouds(self) -> Optional[Clouds]:
return Clouds.random_preset(rain=True)
def generate_fog(self) -> Optional[Fog]:
# DCS 2.7 says to not use fog with the cloud presets.
return None
def generate_wind(self) -> WindConditions:
return self.random_wind(0, 6)
class Thunderstorm(Weather):
def generate_clouds(self) -> Optional[Clouds]:
return Clouds(
base=self.random_cloud_base(),
density=random.randint(9, 10),
thickness=self.random_cloud_thickness(),
precipitation=PydcsWeather.Preceptions.Thunderstorm,
)
def generate_wind(self) -> WindConditions:
return self.random_wind(0, 8)
@dataclass
class Conditions:
time_of_day: TimeOfDay
start_time: datetime.datetime
weather: Weather
@classmethod
def generate(
cls,
theater: ConflictTheater,
day: datetime.date,
time_of_day: TimeOfDay,
settings: Settings,
) -> Conditions:
return cls(
time_of_day=time_of_day,
start_time=cls.generate_start_time(
theater, day, time_of_day, settings.night_disabled
),
weather=cls.generate_weather(),
)
@classmethod
def generate_start_time(
cls,
theater: ConflictTheater,
day: datetime.date,
time_of_day: TimeOfDay,
night_disabled: bool,
) -> datetime.datetime:
if night_disabled:
logging.info("Skip Night mission due to user settings")
time_range = {
TimeOfDay.Dawn: (8, 9),
TimeOfDay.Day: (10, 12),
TimeOfDay.Dusk: (12, 14),
TimeOfDay.Night: (14, 17),
}[time_of_day]
else:
time_range = theater.daytime_map[time_of_day.value]
time = datetime.time(hour=random.randint(*time_range))
return datetime.datetime.combine(day, time)
@classmethod
def generate_weather(cls) -> Weather:
chances = {
Thunderstorm: 1,
Raining: 20,
Cloudy: 60,
ClearSkies: 20,
}
weather_type = random.choices(
list(chances.keys()), weights=list(chances.values())
)[0]
return weather_type()

View File

@@ -1,12 +1,13 @@
from .aaa import *
from .aircraft import *
from .armor import *
from .awacsgen import *
from .airsupportgen import *
from .conflictgen import *
from .shipgen import *
from .visualgen import *
from .triggergen import *
from .environmentgen import *
from .groundobjectsgen import *
from .briefinggen import *
from .forcedoptionsgen import *
from .kneeboard import *
from . import naming

View File

@@ -1,74 +0,0 @@
from game import *
from theater.conflicttheater import ConflictTheater
from .conflictgen import *
from .naming import *
from dcs.mission import *
DISTANCE_FACTOR = 0.5, 1
EXTRA_AA_MIN_DISTANCE = 35000
EXTRA_AA_POSITION_FROM_CP = 550
class AAConflictGenerator:
def __init__(self, mission: Mission, conflict: Conflict):
self.m = mission
self.conflict = conflict
def generate_at_defenders_location(self, units: db.AirDefenseDict):
for unit_type, count in units.items():
for _ in range(count):
self.m.vehicle_group(
country=self.conflict.defenders_side,
name=namegen.next_ground_group_name(),
_type=unit_type,
position=self.conflict.ground_defenders_location.random_point_within(100, 100),
group_size=1)
def generate(self, units: db.AirDefenseDict):
for type, count in units.items():
for _, radial in zip(range(count), self.conflict.radials):
distance = randint(self.conflict.size * DISTANCE_FACTOR[0], self.conflict.size * DISTANCE_FACTOR[1])
p = self.conflict.position.point_from_heading(radial, distance)
self.m.vehicle_group(
country=self.conflict.defenders_side,
name=namegen.next_ground_group_name(),
_type=type,
position=p,
group_size=1)
class ExtraAAConflictGenerator:
def __init__(self, mission: Mission, conflict: Conflict, game, player_name: Country, enemy_name: Country):
self.mission = mission
self.game = game
self.conflict = conflict
self.player_name = player_name
self.enemy_name = enemy_name
def generate(self):
from theater.conflicttheater import ControlPoint
for cp in self.game.theater.controlpoints:
if cp.is_global:
continue
if cp.position.distance_to_point(self.conflict.position) < EXTRA_AA_MIN_DISTANCE:
continue
if cp.position.distance_to_point(self.conflict.from_cp.position) < EXTRA_AA_MIN_DISTANCE:
continue
country_name = cp.captured and self.player_name or self.enemy_name
position = cp.position.point_from_heading(0, EXTRA_AA_POSITION_FROM_CP)
self.mission.vehicle_group(
country=self.mission.country(country_name),
name=namegen.next_ground_group_name(),
_type=db.EXTRA_AA[country_name],
position=position,
group_size=2
)

File diff suppressed because it is too large Load Diff

1435
gen/airfields.py Normal file

File diff suppressed because it is too large Load Diff

208
gen/airsupportgen.py Normal file
View File

@@ -0,0 +1,208 @@
import logging
from dataclasses import dataclass, field
from datetime import timedelta
from typing import List, Type, Tuple, Optional
from dcs.mission import Mission, StartType
from dcs.planes import IL_78M, KC130, KC135MPRS, KC_135
from dcs.unittype import UnitType
from dcs.task import (
AWACS,
ActivateBeaconCommand,
MainTask,
Refueling,
SetImmortalCommand,
SetInvisibleCommand,
)
from game import db
from .naming import namegen
from .callsigns import callsign_for_support_unit
from .conflictgen import Conflict
from .radios import RadioFrequency, RadioRegistry
from .tacan import TacanBand, TacanChannel, TacanRegistry
TANKER_DISTANCE = 15000
TANKER_ALT = 4572
TANKER_HEADING_OFFSET = 45
AWACS_DISTANCE = 150000
AWACS_ALT = 13000
@dataclass
class AwacsInfo:
"""AWACS information for the kneeboard."""
dcsGroupName: str
callsign: str
freq: RadioFrequency
depature_location: Optional[str]
start_time: Optional[timedelta]
end_time: Optional[timedelta]
@dataclass
class TankerInfo:
"""Tanker information for the kneeboard."""
dcsGroupName: str
callsign: str
variant: str
freq: RadioFrequency
tacan: TacanChannel
@dataclass
class AirSupport:
awacs: List[AwacsInfo] = field(default_factory=list)
tankers: List[TankerInfo] = field(default_factory=list)
class AirSupportConflictGenerator:
def __init__(
self,
mission: Mission,
conflict: Conflict,
game,
radio_registry: RadioRegistry,
tacan_registry: TacanRegistry,
) -> None:
self.mission = mission
self.conflict = conflict
self.game = game
self.air_support = AirSupport()
self.radio_registry = radio_registry
self.tacan_registry = tacan_registry
@classmethod
def support_tasks(cls) -> List[Type[MainTask]]:
return [Refueling, AWACS]
@staticmethod
def _get_tanker_params(unit_type: Type[UnitType]) -> Tuple[int, int]:
if unit_type is KC130:
return (TANKER_ALT - 500, 596)
elif unit_type is KC_135:
return (TANKER_ALT, 770)
elif unit_type is KC135MPRS:
return (TANKER_ALT + 500, 596)
return (TANKER_ALT, 574)
def generate(self):
player_cp = (
self.conflict.from_cp
if self.conflict.from_cp.captured
else self.conflict.to_cp
)
fallback_tanker_number = 0
for i, tanker_unit_type in enumerate(
db.find_unittype(Refueling, self.conflict.attackers_side)
):
alt, airspeed = self._get_tanker_params(tanker_unit_type)
variant = db.unit_type_name(tanker_unit_type)
freq = self.radio_registry.alloc_uhf()
tacan = self.tacan_registry.alloc_for_band(TacanBand.Y)
tanker_heading = (
self.conflict.to_cp.position.heading_between_point(
self.conflict.from_cp.position
)
+ TANKER_HEADING_OFFSET * i
)
tanker_position = player_cp.position.point_from_heading(
tanker_heading, TANKER_DISTANCE
)
tanker_group = self.mission.refuel_flight(
country=self.mission.country(self.game.player_country),
name=namegen.next_tanker_name(
self.mission.country(self.game.player_country), tanker_unit_type
),
airport=None,
plane_type=tanker_unit_type,
position=tanker_position,
altitude=alt,
race_distance=58000,
frequency=freq.mhz,
start_type=StartType.Warm,
speed=airspeed,
tacanchannel=str(tacan),
)
tanker_group.set_frequency(freq.mhz)
callsign = callsign_for_support_unit(tanker_group)
tacan_callsign = {
"Texaco": "TEX",
"Arco": "ARC",
"Shell": "SHL",
}.get(callsign)
if tacan_callsign is None:
# The dict above is all the callsigns currently in the game, but
# non-Western countries don't use the callsigns and instead just
# use numbers. It's possible that none of those nations have
# TACAN compatible refueling aircraft, but fallback just in
# case.
tacan_callsign = f"TK{fallback_tanker_number}"
fallback_tanker_number += 1
if tanker_unit_type != IL_78M:
# Override PyDCS tacan channel.
tanker_group.points[0].tasks.pop()
tanker_group.points[0].tasks.append(
ActivateBeaconCommand(
tacan.number,
tacan.band.value,
tacan_callsign,
True,
tanker_group.units[0].id,
True,
)
)
tanker_group.points[0].tasks.append(SetInvisibleCommand(True))
tanker_group.points[0].tasks.append(SetImmortalCommand(True))
self.air_support.tankers.append(
TankerInfo(str(tanker_group.name), callsign, variant, freq, tacan)
)
if not self.game.settings.disable_legacy_aewc:
possible_awacs = db.find_unittype(AWACS, self.conflict.attackers_side)
if len(possible_awacs) > 0:
awacs_unit = possible_awacs[0]
freq = self.radio_registry.alloc_uhf()
awacs_flight = self.mission.awacs_flight(
country=self.mission.country(self.game.player_country),
name=namegen.next_awacs_name(
self.mission.country(self.game.player_country)
),
plane_type=awacs_unit,
altitude=AWACS_ALT,
airport=None,
position=self.conflict.position.random_point_within(
AWACS_DISTANCE, AWACS_DISTANCE
),
frequency=freq.mhz,
start_type=StartType.Warm,
)
awacs_flight.set_frequency(freq.mhz)
awacs_flight.points[0].tasks.append(SetInvisibleCommand(True))
awacs_flight.points[0].tasks.append(SetImmortalCommand(True))
self.air_support.awacs.append(
AwacsInfo(
dcsGroupName=str(awacs_flight.name),
callsign=callsign_for_support_unit(awacs_flight),
freq=freq,
depature_location=None,
start_time=None,
end_time=None,
)
)
else:
logging.warning("No AWACS for faction")

View File

@@ -1,64 +1,806 @@
from game import db
from .conflictgen import *
from .naming import *
from __future__ import annotations
from dcs.mission import *
from dcs.unittype import *
from dcs.point import *
from dcs.task import *
from dcs.country import *
import logging
import random
from dataclasses import dataclass
from typing import TYPE_CHECKING, List, Optional, Tuple
from dcs import Mission
from dcs.action import AITaskPush
from dcs.condition import GroupLifeLess, Or, TimeAfter, UnitDamaged
from dcs.country import Country
from dcs.mapping import Point
from dcs.planes import MQ_9_Reaper
from dcs.point import PointAction
from dcs.task import (
EPLRS,
AttackGroup,
ControlledTask,
FireAtPoint,
GoToWaypoint,
Hold,
OrbitAction,
SetImmortalCommand,
SetInvisibleCommand,
)
from dcs.triggers import Event, TriggerOnce
from dcs.unit import Vehicle
from dcs.unitgroup import VehicleGroup
from dcs.unittype import VehicleType
from game import db
from game.unitmap import UnitMap
from game.utils import heading_sum, opposite_heading
from game.theater.controlpoint import ControlPoint
from gen.ground_forces.ai_ground_planner import (
DISTANCE_FROM_FRONTLINE,
CombatGroup,
CombatGroupRole,
)
from .callsigns import callsign_for_support_unit
from .conflictgen import Conflict
from .ground_forces.combat_stance import CombatStance
from .naming import namegen
if TYPE_CHECKING:
from game import Game
SPREAD_DISTANCE_FACTOR = 0.1, 0.3
SPREAD_DISTANCE_SIZE_FACTOR = 0.1
FRONTLINE_CAS_FIGHTS_COUNT = 16, 24
FRONTLINE_CAS_GROUP_MIN = 1, 2
FRONTLINE_CAS_PADDING = 12000
class ArmorConflictGenerator:
def __init__(self, mission: Mission, conflict: Conflict):
self.m = mission
RETREAT_DISTANCE = 20000
BREAKTHROUGH_OFFENSIVE_DISTANCE = 35000
AGGRESIVE_MOVE_DISTANCE = 16000
FIGHT_DISTANCE = 3500
RANDOM_OFFSET_ATTACK = 250
INFANTRY_GROUP_SIZE = 5
@dataclass(frozen=True)
class JtacInfo:
"""JTAC information."""
dcsGroupName: str
unit_name: str
callsign: str
region: str
code: str
# TODO: Radio info? Type?
class GroundConflictGenerator:
def __init__(
self,
mission: Mission,
conflict: Conflict,
game: Game,
player_planned_combat_groups: List[CombatGroup],
enemy_planned_combat_groups: List[CombatGroup],
player_stance: CombatStance,
unit_map: UnitMap,
) -> None:
self.mission = mission
self.conflict = conflict
self.enemy_planned_combat_groups = enemy_planned_combat_groups
self.player_planned_combat_groups = player_planned_combat_groups
self.player_stance = CombatStance(player_stance)
self.enemy_stance = self._enemy_stance()
self.game = game
self.unit_map = unit_map
self.jtacs: List[JtacInfo] = []
def _group_point(self, point) -> Point:
distance = randint(
int(self.conflict.size * SPREAD_DISTANCE_FACTOR[0]),
int(self.conflict.size * SPREAD_DISTANCE_FACTOR[1]),
def _enemy_stance(self):
"""Picks the enemy stance according to the number of planned groups on the frontline for each side"""
if len(self.enemy_planned_combat_groups) > len(
self.player_planned_combat_groups
):
return random.choice(
[
CombatStance.AGGRESSIVE,
CombatStance.AGGRESSIVE,
CombatStance.AGGRESSIVE,
CombatStance.ELIMINATION,
CombatStance.BREAKTHROUGH,
]
)
else:
return random.choice(
[
CombatStance.DEFENSIVE,
CombatStance.DEFENSIVE,
CombatStance.DEFENSIVE,
CombatStance.AMBUSH,
CombatStance.AGGRESSIVE,
]
)
@staticmethod
def _group_point(point: Point, base_distance) -> Point:
distance = random.randint(
int(base_distance * SPREAD_DISTANCE_FACTOR[0]),
int(base_distance * SPREAD_DISTANCE_FACTOR[1]),
)
return point.random_point_within(
distance, base_distance * SPREAD_DISTANCE_SIZE_FACTOR
)
def generate(self):
position = Conflict.frontline_position(
self.conflict.from_cp, self.conflict.to_cp, self.game.theater
)
frontline_vector = Conflict.frontline_vector(
self.conflict.from_cp, self.conflict.to_cp, self.game.theater
)
# Create player groups at random position
player_groups = self._generate_groups(
self.player_planned_combat_groups, frontline_vector, True
)
# Create enemy groups at random position
enemy_groups = self._generate_groups(
self.enemy_planned_combat_groups, frontline_vector, False
)
# Plan combat actions for groups
self.plan_action_for_groups(
self.player_stance,
player_groups,
enemy_groups,
self.conflict.heading + 90,
self.conflict.from_cp,
self.conflict.to_cp,
)
self.plan_action_for_groups(
self.enemy_stance,
enemy_groups,
player_groups,
self.conflict.heading - 90,
self.conflict.to_cp,
self.conflict.from_cp,
)
# Add JTAC
if self.game.player_faction.has_jtac:
n = "JTAC" + str(self.conflict.from_cp.id) + str(self.conflict.to_cp.id)
code = 1688 - len(self.jtacs)
utype = MQ_9_Reaper
if self.game.player_faction.jtac_unit is not None:
utype = self.game.player_faction.jtac_unit
jtac = self.mission.flight_group(
country=self.mission.country(self.game.player_country),
name=n,
aircraft_type=utype,
position=position[0],
airport=None,
altitude=5000,
)
jtac.points[0].tasks.append(SetInvisibleCommand(True))
jtac.points[0].tasks.append(SetImmortalCommand(True))
jtac.points[0].tasks.append(
OrbitAction(5000, 300, OrbitAction.OrbitPattern.Circle)
)
frontline = (
f"Frontline {self.conflict.from_cp.name}/{self.conflict.to_cp.name}"
)
# Note: Will need to change if we ever add ground based JTAC.
callsign = callsign_for_support_unit(jtac)
self.jtacs.append(
JtacInfo(str(jtac.name), n, callsign, frontline, str(code))
)
def gen_infantry_group_for_group(
self, group: VehicleGroup, is_player: bool, side: Country, forward_heading: int
) -> None:
infantry_position = self.conflict.find_ground_position(
group.points[0].position.random_point_within(250, 50),
500,
forward_heading,
self.conflict.theater,
)
if not infantry_position:
logging.warning("Could not find infantry position")
return
if side == self.conflict.attackers_country:
cp = self.conflict.from_cp
else:
cp = self.conflict.to_cp
if is_player:
faction = self.game.player_name
else:
faction = self.game.enemy_name
# Disable infantry unit gen if disabled
if not self.game.settings.perf_infantry:
if self.game.settings.manpads:
# 50% of armored units protected by manpad
if random.choice([True, False]):
manpads = db.find_manpad(faction)
if len(manpads) > 0:
u = random.choice(manpads)
self.mission.vehicle_group(
side,
namegen.next_infantry_name(side, cp.id, u),
u,
position=infantry_position,
group_size=1,
heading=forward_heading,
move_formation=PointAction.OffRoad,
)
return
possible_infantry_units = db.find_infantry(
faction, allow_manpad=self.game.settings.manpads
)
if len(possible_infantry_units) == 0:
return
u = random.choice(possible_infantry_units)
self.mission.vehicle_group(
side,
namegen.next_infantry_name(side, cp.id, u),
u,
position=infantry_position,
group_size=1,
heading=forward_heading,
move_formation=PointAction.OffRoad,
)
for i in range(INFANTRY_GROUP_SIZE):
u = random.choice(possible_infantry_units)
position = infantry_position.random_point_within(55, 5)
self.mission.vehicle_group(
side,
namegen.next_infantry_name(side, cp.id, u),
u,
position=position,
group_size=1,
heading=forward_heading,
move_formation=PointAction.OffRoad,
)
def _set_reform_waypoint(
self, dcs_group: VehicleGroup, forward_heading: int
) -> None:
"""Setting a waypoint close to the spawn position allows the group to reform gracefully
rather than spin
"""
reform_point = dcs_group.position.point_from_heading(forward_heading, 50)
dcs_group.add_waypoint(reform_point)
def _plan_artillery_action(
self,
stance: CombatStance,
gen_group: CombatGroup,
dcs_group: VehicleGroup,
forward_heading: int,
target: Point,
) -> bool:
"""
Handles adding the DCS tasks for artillery groups for all combat stances.
Returns True if tasking was added, returns False if the stance was not a combat stance.
"""
self._set_reform_waypoint(dcs_group, forward_heading)
if stance != CombatStance.RETREAT:
hold_task = Hold()
hold_task.number = 1
dcs_group.add_trigger_action(hold_task)
# Artillery strike random start
artillery_trigger = TriggerOnce(
Event.NoEvent, "ArtilleryFireTask #" + str(dcs_group.id)
)
artillery_trigger.add_condition(TimeAfter(seconds=random.randint(1, 45) * 60))
# TODO: Update to fire at group instead of point
fire_task = FireAtPoint(target, len(gen_group.units) * 10, 100)
fire_task.number = 2 if stance != CombatStance.RETREAT else 1
dcs_group.add_trigger_action(fire_task)
artillery_trigger.add_action(AITaskPush(dcs_group.id, len(dcs_group.tasks)))
self.mission.triggerrules.triggers.append(artillery_trigger)
# Artillery will fall back when under attack
if stance != CombatStance.RETREAT:
# Hold position
dcs_group.points[1].tasks.append(Hold())
retreat = self.find_retreat_point(
dcs_group, forward_heading, (int)(RETREAT_DISTANCE / 3)
)
dcs_group.add_waypoint(
dcs_group.position.point_from_heading(forward_heading, 1),
PointAction.OffRoad,
)
dcs_group.points[2].tasks.append(Hold())
dcs_group.add_waypoint(retreat, PointAction.OffRoad)
artillery_fallback = TriggerOnce(
Event.NoEvent, "ArtilleryRetreat #" + str(dcs_group.id)
)
for i, u in enumerate(dcs_group.units):
artillery_fallback.add_condition(UnitDamaged(u.id))
if i < len(dcs_group.units) - 1:
artillery_fallback.add_condition(Or())
hold_2 = Hold()
hold_2.number = 3
dcs_group.add_trigger_action(hold_2)
retreat_task = GoToWaypoint(to_index=3)
retreat_task.number = 4
dcs_group.add_trigger_action(retreat_task)
artillery_fallback.add_action(
AITaskPush(dcs_group.id, len(dcs_group.tasks))
)
self.mission.triggerrules.triggers.append(artillery_fallback)
for u in dcs_group.units:
u.initial = True
u.heading = forward_heading + random.randint(-5, 5)
return True
return False
def _plan_tank_ifv_action(
self,
stance: CombatStance,
enemy_groups: List[Tuple[VehicleGroup, CombatGroup]],
dcs_group: VehicleGroup,
forward_heading: int,
to_cp: ControlPoint,
) -> bool:
"""
Handles adding the DCS tasks for tank and IFV groups for all combat stances.
Returns True if tasking was added, returns False if the stance was not a combat stance.
"""
self._set_reform_waypoint(dcs_group, forward_heading)
if stance == CombatStance.AGGRESSIVE:
# Attack nearest enemy if any
# Then move forward OR Attack enemy base if it is not too far away
target = self.find_nearest_enemy_group(dcs_group, enemy_groups)
if target is not None:
rand_offset = Point(
random.randint(-RANDOM_OFFSET_ATTACK, RANDOM_OFFSET_ATTACK),
random.randint(-RANDOM_OFFSET_ATTACK, RANDOM_OFFSET_ATTACK),
)
target_point = self.conflict.theater.nearest_land_pos(
target.points[0].position + rand_offset
)
dcs_group.add_waypoint(target_point)
dcs_group.points[2].tasks.append(AttackGroup(target.id))
if (
to_cp.position.distance_to_point(dcs_group.points[0].position)
<= AGGRESIVE_MOVE_DISTANCE
):
attack_point = self.conflict.theater.nearest_land_pos(
to_cp.position.random_point_within(500, 0)
)
else:
# We use an offset heading here because DCS doesn't always
# force vehicles to move if there's no heading change.
offset_heading = forward_heading - 2
if offset_heading < 0:
offset_heading = 358
attack_point = self.find_offensive_point(
dcs_group, offset_heading, AGGRESIVE_MOVE_DISTANCE
)
dcs_group.add_waypoint(attack_point, PointAction.OffRoad)
elif stance == CombatStance.BREAKTHROUGH:
# In breakthrough mode, the units will move forward
# If the enemy base is close enough, the units will attack the base
if (
to_cp.position.distance_to_point(dcs_group.points[0].position)
<= BREAKTHROUGH_OFFENSIVE_DISTANCE
):
attack_point = self.conflict.theater.nearest_land_pos(
to_cp.position.random_point_within(500, 0)
)
else:
# We use an offset heading here because DCS doesn't always
# force vehicles to move if there's no heading change.
offset_heading = forward_heading - 1
if offset_heading < 0:
offset_heading = 359
attack_point = self.find_offensive_point(
dcs_group, offset_heading, BREAKTHROUGH_OFFENSIVE_DISTANCE
)
dcs_group.add_waypoint(attack_point, PointAction.OffRoad)
elif stance == CombatStance.ELIMINATION:
# In elimination mode, the units focus on destroying as much enemy groups as possible
targets = self.find_n_nearest_enemy_groups(dcs_group, enemy_groups, 3)
for i, target in enumerate(targets, start=1):
rand_offset = Point(
random.randint(-RANDOM_OFFSET_ATTACK, RANDOM_OFFSET_ATTACK),
random.randint(-RANDOM_OFFSET_ATTACK, RANDOM_OFFSET_ATTACK),
)
target_point = self.conflict.theater.nearest_land_pos(
target.points[0].position + rand_offset
)
dcs_group.add_waypoint(target_point, PointAction.OffRoad)
dcs_group.points[i + 1].tasks.append(AttackGroup(target.id))
if (
to_cp.position.distance_to_point(dcs_group.points[0].position)
<= AGGRESIVE_MOVE_DISTANCE
):
attack_point = self.conflict.theater.nearest_land_pos(
to_cp.position.random_point_within(500, 0)
)
dcs_group.add_waypoint(attack_point)
if stance != CombatStance.RETREAT:
self.add_morale_trigger(dcs_group, forward_heading)
return True
return False
def _plan_apc_atgm_action(
self,
stance: CombatStance,
dcs_group: VehicleGroup,
forward_heading: int,
to_cp: ControlPoint,
) -> bool:
"""
Handles adding the DCS tasks for APC and ATGM groups for all combat stances.
Returns True if tasking was added, returns False if the stance was not a combat stance.
"""
self._set_reform_waypoint(dcs_group, forward_heading)
if stance in [
CombatStance.AGGRESSIVE,
CombatStance.BREAKTHROUGH,
CombatStance.ELIMINATION,
]:
# APC & ATGM will never move too much forward, but will follow along any offensive
if (
to_cp.position.distance_to_point(dcs_group.points[0].position)
<= AGGRESIVE_MOVE_DISTANCE
):
attack_point = self.conflict.theater.nearest_land_pos(
to_cp.position.random_point_within(500, 0)
)
else:
attack_point = self.find_offensive_point(
dcs_group, forward_heading, AGGRESIVE_MOVE_DISTANCE
)
dcs_group.add_waypoint(attack_point, PointAction.OffRoad)
if stance != CombatStance.RETREAT:
self.add_morale_trigger(dcs_group, forward_heading)
return True
return False
def plan_action_for_groups(
self,
stance: CombatStance,
ally_groups: List[Tuple[VehicleGroup, CombatGroup]],
enemy_groups: List[Tuple[VehicleGroup, CombatGroup]],
forward_heading: int,
from_cp: ControlPoint,
to_cp: ControlPoint,
) -> None:
if not self.game.settings.perf_moving_units:
return
for dcs_group, group in ally_groups:
if hasattr(group.units[0], "eplrs") and group.units[0].eplrs:
dcs_group.points[0].tasks.append(EPLRS(dcs_group.id))
if group.role == CombatGroupRole.ARTILLERY:
if self.game.settings.perf_artillery:
target = self.get_artillery_target_in_range(
dcs_group, group, enemy_groups
)
if target is not None:
self._plan_artillery_action(
stance, group, dcs_group, forward_heading, target
)
elif group.role in [CombatGroupRole.TANK, CombatGroupRole.IFV]:
self._plan_tank_ifv_action(
stance, enemy_groups, dcs_group, forward_heading, to_cp
)
return point.random_point_within(distance, self.conflict.size * SPREAD_DISTANCE_SIZE_FACTOR)
elif group.role in [CombatGroupRole.APC, CombatGroupRole.ATGM]:
self._plan_apc_atgm_action(stance, dcs_group, forward_heading, to_cp)
def _generate_group(self, side: Country, unit: VehicleType, count: int, at: Point):
for c in range(count):
group = self.m.vehicle_group(
side,
namegen.next_armor_group_name(),
unit,
position=self._group_point(at),
group_size=1,
move_formation=PointAction.OffRoad)
wayp = group.add_waypoint(self.conflict.position.point_from_heading(0, 500))
wayp.tasks = []
if stance == CombatStance.RETREAT:
# In retreat mode, the units will fall back
# If the ally base is close enough, the units will even regroup there
if (
from_cp.position.distance_to_point(dcs_group.points[0].position)
<= RETREAT_DISTANCE
):
retreat_point = from_cp.position.random_point_within(500, 250)
else:
retreat_point = self.find_retreat_point(dcs_group, forward_heading)
reposition_point = retreat_point.point_from_heading(
forward_heading, 10
) # Another point to make the unit face the enemy
dcs_group.add_waypoint(retreat_point, PointAction.OffRoad)
dcs_group.add_waypoint(reposition_point, PointAction.OffRoad)
def generate(self, attackers: db.ArmorDict, defenders: db.ArmorDict):
for type, count in attackers.items():
self._generate_group(
side=self.conflict.attackers_side,
unit=type,
count=count,
at=self.conflict.ground_attackers_location)
def add_morale_trigger(self, dcs_group: VehicleGroup, forward_heading: int) -> None:
"""
This add a trigger to manage units fleeing whenever their group is hit hard, or being engaged by CAS
"""
for type, count in defenders.items():
self._generate_group(
side=self.conflict.defenders_side,
unit=type,
count=count,
at=self.conflict.ground_defenders_location)
if len(dcs_group.units) == 1:
return
def generate_passengers(self, count: int):
unit_type = random.choice(db.find_unittype(Nothing, self.conflict.attackers_side.name))
# Units should hold position on last waypoint
dcs_group.points[len(dcs_group.points) - 1].tasks.append(Hold())
self.m.vehicle_group(
country=self.conflict.attackers_side,
name=namegen.next_passenger_group_name(),
_type=unit_type,
position=self.conflict.ground_attackers_location,
group_size=count
# Force unit heading
for unit in dcs_group.units:
unit.heading = forward_heading
dcs_group.manualHeading = True
# We add a new retreat waypoint
dcs_group.add_waypoint(
self.find_retreat_point(
dcs_group, forward_heading, (int)(RETREAT_DISTANCE / 8)
),
PointAction.OffRoad,
)
# Fallback task
fallback = ControlledTask(GoToWaypoint(to_index=len(dcs_group.points)))
fallback.enabled = False
dcs_group.add_trigger_action(Hold())
dcs_group.add_trigger_action(fallback)
# Create trigger
fallback = TriggerOnce(Event.NoEvent, "Morale manager #" + str(dcs_group.id))
# Usually more than 50% casualties = RETREAT
fallback.add_condition(GroupLifeLess(dcs_group.id, random.randint(51, 76)))
# Do retreat to the configured retreat waypoint
fallback.add_action(AITaskPush(dcs_group.id, len(dcs_group.tasks)))
self.mission.triggerrules.triggers.append(fallback)
def find_retreat_point(
self,
dcs_group: VehicleGroup,
frontline_heading: int,
distance: int = RETREAT_DISTANCE,
) -> Point:
"""
Find a point to retreat to
:param dcs_group: DCS mission group we are searching a retreat point for
:param frontline_heading: Heading of the frontline
:return: dcs.mapping.Point object with the desired position
"""
desired_point = dcs_group.points[0].position.point_from_heading(
heading_sum(frontline_heading, +180), distance
)
if self.conflict.theater.is_on_land(desired_point):
return desired_point
return self.conflict.theater.nearest_land_pos(desired_point)
def find_offensive_point(
self, dcs_group: VehicleGroup, frontline_heading: int, distance: int
) -> Point:
"""
Find a point to attack
:param dcs_group: DCS mission group we are searching an attack point for
:param frontline_heading: Heading of the frontline
:param distance: Distance of the offensive (how far unit should move)
:return: dcs.mapping.Point object with the desired position
"""
desired_point = dcs_group.points[0].position.point_from_heading(
frontline_heading, distance
)
if self.conflict.theater.is_on_land(desired_point):
return desired_point
return self.conflict.theater.nearest_land_pos(desired_point)
@staticmethod
def find_n_nearest_enemy_groups(
player_group: VehicleGroup,
enemy_groups: List[Tuple[VehicleGroup, CombatGroup]],
n: int,
) -> List[VehicleGroup]:
"""
Return the nearest enemy group for the player group
@param group Group for which we should find the nearest ennemies
@param enemy_groups Potential enemy groups
@param n number of nearby groups to take
"""
targets = [] # type: List[Optional[VehicleGroup]]
sorted_list = sorted(
enemy_groups,
key=lambda group: player_group.points[0].position.distance_to_point(
group[0].points[0].position
),
)
for i in range(n):
# TODO: Is this supposed to return no groups if enemy_groups is less than n?
if len(sorted_list) <= i:
break
else:
targets.append(sorted_list[i][0])
return targets
@staticmethod
def find_nearest_enemy_group(
player_group: VehicleGroup, enemy_groups: List[Tuple[VehicleGroup, CombatGroup]]
) -> Optional[VehicleGroup]:
"""
Search the enemy groups for a potential target suitable to armored assault
@param group Group for which we should find the nearest ennemy
@param enemy_groups Potential enemy groups
"""
min_distance = 99999999
target = None
for dcs_group, _ in enemy_groups:
dist = player_group.points[0].position.distance_to_point(
dcs_group.points[0].position
)
if dist < min_distance:
min_distance = dist
target = dcs_group
return target
@staticmethod
def get_artillery_target_in_range(
dcs_group: VehicleGroup,
group: CombatGroup,
enemy_groups: List[Tuple[VehicleGroup, CombatGroup]],
) -> Optional[Point]:
"""
Search the enemy groups for a potential target suitable to an artillery unit
"""
# TODO: Update to return a list of groups instead of a single point
rng = group.units[0].threat_range
if not enemy_groups:
return None
for _ in range(10):
potential_target = random.choice(enemy_groups)[0]
distance_to_target = dcs_group.points[0].position.distance_to_point(
potential_target.points[0].position
)
if distance_to_target < rng:
return potential_target.points[0].position
return None
@staticmethod
def get_artilery_group_distance_from_frontline(group: CombatGroup) -> int:
"""
For artilery group, decide the distance from frontline with the range of the unit
"""
rg = group.units[0].threat_range - 7500
if rg > DISTANCE_FROM_FRONTLINE[CombatGroupRole.ARTILLERY][1]:
rg = random.randint(
DISTANCE_FROM_FRONTLINE[CombatGroupRole.ARTILLERY][0],
DISTANCE_FROM_FRONTLINE[CombatGroupRole.ARTILLERY][1],
)
elif rg < DISTANCE_FROM_FRONTLINE[CombatGroupRole.ARTILLERY][1]:
rg = random.randint(
DISTANCE_FROM_FRONTLINE[CombatGroupRole.TANK][0],
DISTANCE_FROM_FRONTLINE[CombatGroupRole.TANK][1],
)
return rg
def get_valid_position_for_group(
self,
conflict_position: Point,
combat_width: int,
distance_from_frontline: int,
heading: int,
spawn_heading: int,
):
shifted = conflict_position.point_from_heading(
heading, random.randint(0, combat_width)
)
desired_point = shifted.point_from_heading(
spawn_heading, distance_from_frontline
)
return Conflict.find_ground_position(
desired_point, combat_width, heading, self.conflict.theater
)
def _generate_groups(
self,
groups: List[CombatGroup],
frontline_vector: Tuple[Point, int, int],
is_player: bool,
) -> List[Tuple[VehicleGroup, CombatGroup]]:
"""Finds valid positions for planned groups and generates a pydcs group for them"""
positioned_groups = []
position, heading, combat_width = frontline_vector
spawn_heading = (
int(heading_sum(heading, -90))
if is_player
else int(heading_sum(heading, 90))
)
country = self.game.player_country if is_player else self.game.enemy_country
for group in groups:
if group.role == CombatGroupRole.ARTILLERY:
distance_from_frontline = (
self.get_artilery_group_distance_from_frontline(group)
)
else:
distance_from_frontline = random.randint(
DISTANCE_FROM_FRONTLINE[group.role][0],
DISTANCE_FROM_FRONTLINE[group.role][1],
)
final_position = self.get_valid_position_for_group(
position, combat_width, distance_from_frontline, heading, spawn_heading
)
if final_position is not None:
g = self._generate_group(
self.mission.country(country),
group.units[0],
len(group.units),
final_position,
distance_from_frontline,
heading=opposite_heading(spawn_heading),
)
if is_player:
g.set_skill(self.game.settings.player_skill)
else:
g.set_skill(self.game.settings.enemy_vehicle_skill)
positioned_groups.append((g, group))
if group.role in [CombatGroupRole.APC, CombatGroupRole.IFV]:
self.gen_infantry_group_for_group(
g,
is_player,
self.mission.country(country),
opposite_heading(spawn_heading),
)
else:
logging.warning(f"Unable to get valid position for {group}")
return positioned_groups
def _generate_group(
self,
side: Country,
unit: VehicleType,
count: int,
at: Point,
distance_from_frontline,
move_formation: PointAction = PointAction.OffRoad,
heading=0,
) -> VehicleGroup:
if side == self.conflict.attackers_country:
cp = self.conflict.from_cp
else:
cp = self.conflict.to_cp
logging.info("armorgen: {} for {}".format(unit, side.id))
group = self.mission.vehicle_group(
side,
namegen.next_unit_name(side, cp.id, unit),
unit,
position=at,
group_size=count,
heading=heading,
move_formation=move_formation,
)
self.unit_map.add_front_line_units(group, cp)
for c in range(count):
vehicle: Vehicle = group.units[c]
vehicle.player_can_drive = True
return group

225
gen/ato.py Normal file
View File

@@ -0,0 +1,225 @@
"""Air Tasking Orders.
The classes of the Air Tasking Order (ATO) define all of the missions that have
been planned, and which aircraft have been assigned to them. Each planned
mission, or "package" is composed of individual flights. The package may contain
dissimilar aircraft performing different roles, but all for the same goal. For
example, the package to strike an enemy airfield may contain an escort flight,
a SEAD flight, and the strike aircraft themselves. CAP packages may contain only
the single CAP flight.
"""
import logging
from collections import defaultdict
from dataclasses import dataclass, field
from datetime import timedelta
from typing import Dict, List, Optional
from dcs.mapping import Point
from game.theater.missiontarget import MissionTarget
from game.utils import Speed
from .flights.flight import Flight, FlightType
from .flights.flightplan import FormationFlightPlan
from .flights.traveltime import TotEstimator
@dataclass(frozen=True)
class Task:
"""The main task of a flight or package."""
#: The type of task.
task_type: FlightType
#: The location of the objective.
location: str
@dataclass(frozen=True)
class PackageWaypoints:
join: Point
ingress: Point
egress: Point
split: Point
@dataclass
class Package:
"""A mission package."""
#: The mission target. Currently can be either a ControlPoint or a
#: TheaterGroundObject (non-ControlPoint map objectives).
target: MissionTarget
#: The set of flights in the package.
flights: List[Flight] = field(default_factory=list)
delay: int = field(default=0)
#: True if the package ToT should be reset to ASAP whenever the player makes
#: a change. This is really a UI property rather than a game property, but
#: we want it to persist in the save.
auto_asap: bool = field(default=False)
#: Desired TOT as an offset from mission start.
time_over_target: timedelta = field(default=timedelta())
waypoints: Optional[PackageWaypoints] = field(default=None)
@property
def formation_speed(self) -> Optional[Speed]:
"""The speed of the package when in formation.
If none of the flights in the package will join a formation, this
returns None. This is nto uncommon, since only strike-like (strike,
DEAD, anti-ship, BAI, etc.) flights and their escorts fly in formation.
Others (CAP and CAS, currently) will coordinate in target timing but
fly their own path to the target.
"""
speeds = []
for flight in self.flights:
if isinstance(flight.flight_plan, FormationFlightPlan):
speeds.append(flight.flight_plan.best_flight_formation_speed)
if not speeds:
return None
return min(speeds)
# TODO: Should depend on the type of escort.
# SEAD might be able to leave before CAP.
@property
def escort_start_time(self) -> Optional[timedelta]:
times = []
for flight in self.flights:
waypoint = flight.flight_plan.request_escort_at()
if waypoint is None:
continue
tot = flight.flight_plan.tot_for_waypoint(waypoint)
if tot is None:
logging.error(
f"{flight} requested escort at {waypoint} but that "
"waypoint has no TOT. It may not be escorted."
)
continue
times.append(tot)
if times:
return min(times)
return None
@property
def escort_end_time(self) -> Optional[timedelta]:
times = []
for flight in self.flights:
waypoint = flight.flight_plan.dismiss_escort_at()
if waypoint is None:
continue
tot = flight.flight_plan.tot_for_waypoint(waypoint)
if tot is None:
tot = flight.flight_plan.depart_time_for_waypoint(waypoint)
if tot is None:
logging.error(
f"{flight} dismissed escort at {waypoint} but that "
"waypoint has no TOT or departure time. It may not be "
"escorted."
)
continue
times.append(tot)
if times:
return max(times)
return None
@property
def mission_departure_time(self) -> Optional[timedelta]:
times = []
for flight in self.flights:
times.append(flight.flight_plan.mission_departure_time)
if times:
return max(times)
return None
def set_tot_asap(self) -> None:
self.time_over_target = TotEstimator(self).earliest_tot()
def add_flight(self, flight: Flight) -> None:
"""Adds a flight to the package."""
self.flights.append(flight)
def remove_flight(self, flight: Flight) -> None:
"""Removes a flight from the package."""
self.flights.remove(flight)
if not self.flights:
self.waypoints = None
@property
def primary_task(self) -> Optional[FlightType]:
if not self.flights:
return None
flight_counts: Dict[FlightType, int] = defaultdict(lambda: 0)
for flight in self.flights:
flight_counts[flight.flight_type] += 1
# The package will contain a mix of mission types, but in general we can
# determine the goal of the mission because some mission types are more
# likely to be the main task than others. For example, a package with
# only CAP flights is a CAP package, a flight with CAP and strike is a
# strike package, a flight with CAP and DEAD is a DEAD package, and a
# flight with strike and SEAD is an OCA/Strike package. The type of
# package is determined by the highest priority flight in the package.
task_priorities = [
FlightType.CAS,
FlightType.STRIKE,
FlightType.ANTISHIP,
FlightType.OCA_AIRCRAFT,
FlightType.OCA_RUNWAY,
FlightType.BAI,
FlightType.DEAD,
FlightType.SEAD,
FlightType.TARCAP,
FlightType.BARCAP,
FlightType.AEWC,
FlightType.SWEEP,
FlightType.ESCORT,
]
for task in task_priorities:
if flight_counts[task]:
return task
# If we get here, our task_priorities list above is incomplete. Log the
# issue and return the type of *any* flight in the package.
some_mission = next(iter(self.flights)).flight_type
logging.warning(f"Unhandled mission type: {some_mission}")
return some_mission
@property
def package_description(self) -> str:
"""Generates a package description based on flight composition."""
task = self.primary_task
if task is None:
return "No mission"
oca_strike_types = {FlightType.OCA_AIRCRAFT, FlightType.OCA_RUNWAY}
if task in oca_strike_types:
return "OCA Strike"
return str(task)
def __hash__(self) -> int:
# TODO: Far from perfect. Number packages?
return hash(self.target.name)
@dataclass
class AirTaskingOrder:
"""The entire ATO for one coalition."""
#: The set of all planned packages in the ATO.
packages: List[Package] = field(default_factory=list)
def add_package(self, package: Package) -> None:
"""Adds a package to the ATO."""
self.packages.append(package)
def remove_package(self, package: Package) -> None:
"""Removes a package from the ATO."""
self.packages.remove(package)
def clear(self) -> None:
"""Removes all packages from the ATO."""
self.packages.clear()

View File

@@ -1,30 +0,0 @@
from game import db
from .conflictgen import *
from .naming import *
from dcs.mission import *
from dcs.unitgroup import *
from dcs.unittype import *
from dcs.task import *
from dcs.terrain.terrain import NoParkingSlotError
AWACS_DISTANCE = 150000
AWACS_ALT = 10000
class AWACSConflictGenerator:
def __init__(self, mission: Mission, conflict: Conflict, game):
self.mission = mission
self.conflict = conflict
self.game = game
def generate(self):
plane = db.find_unittype(AWACS, self.conflict.attackers_side.name)[0]
self.mission.awacs_flight(
country=self.mission.country(self.game.player),
name=namegen.next_awacs_group_name(),
plane_type=plane,
altitude=AWACS_ALT,
airport=None,
position=self.conflict.position.random_point_within(AWACS_DISTANCE, AWACS_DISTANCE))

74
gen/beacons.py Normal file
View File

@@ -0,0 +1,74 @@
from dataclasses import dataclass
from enum import auto, IntEnum
import json
from pathlib import Path
from typing import Iterable, Optional
from gen.radios import RadioFrequency
from gen.tacan import TacanBand, TacanChannel
BEACONS_RESOURCE_PATH = Path("resources/dcs/beacons")
class BeaconType(IntEnum):
BEACON_TYPE_NULL = auto()
BEACON_TYPE_VOR = auto()
BEACON_TYPE_DME = auto()
BEACON_TYPE_VOR_DME = auto()
BEACON_TYPE_TACAN = auto()
BEACON_TYPE_VORTAC = auto()
BEACON_TYPE_RSBN = auto()
BEACON_TYPE_BROADCAST_STATION = auto()
BEACON_TYPE_HOMER = auto()
BEACON_TYPE_AIRPORT_HOMER = auto()
BEACON_TYPE_AIRPORT_HOMER_WITH_MARKER = auto()
BEACON_TYPE_ILS_FAR_HOMER = auto()
BEACON_TYPE_ILS_NEAR_HOMER = auto()
BEACON_TYPE_ILS_LOCALIZER = auto()
BEACON_TYPE_ILS_GLIDESLOPE = auto()
BEACON_TYPE_PRMG_LOCALIZER = auto()
BEACON_TYPE_PRMG_GLIDESLOPE = auto()
BEACON_TYPE_ICLS_LOCALIZER = auto()
BEACON_TYPE_ICLS_GLIDESLOPE = auto()
BEACON_TYPE_NAUTICAL_HOMER = auto()
@dataclass(frozen=True)
class Beacon:
name: str
callsign: str
beacon_type: BeaconType
hertz: int
channel: Optional[int]
@property
def frequency(self) -> RadioFrequency:
return RadioFrequency(self.hertz)
@property
def is_tacan(self) -> bool:
return self.beacon_type in (
BeaconType.BEACON_TYPE_VORTAC,
BeaconType.BEACON_TYPE_TACAN,
)
@property
def tacan_channel(self) -> TacanChannel:
assert self.is_tacan
assert self.channel is not None
return TacanChannel(self.channel, TacanBand.X)
def load_beacons_for_terrain(name: str) -> Iterable[Beacon]:
beacons_file = BEACONS_RESOURCE_PATH / f"{name.lower()}.json"
if not beacons_file.exists():
raise RuntimeError(f"Beacon file {beacons_file.resolve()} is missing")
for beacon in json.loads(beacons_file.read_text()):
yield Beacon(**beacon)

181
gen/briefinggen.py Normal file
View File

@@ -0,0 +1,181 @@
"""
Briefing generation logic
"""
from __future__ import annotations
import os
from dataclasses import dataclass
from datetime import timedelta
from typing import Dict, List, TYPE_CHECKING
from dcs.mission import Mission
from jinja2 import Environment, FileSystemLoader, select_autoescape
from game.theater import ControlPoint, FrontLine
from .aircraft import FlightData
from .airsupportgen import AwacsInfo, TankerInfo
from .armor import JtacInfo
from .flights.flight import FlightWaypoint
from .ground_forces.combat_stance import CombatStance
from .radios import RadioFrequency
from .runways import RunwayData
if TYPE_CHECKING:
from game import Game
@dataclass
class CommInfo:
"""Communications information for the kneeboard."""
name: str
freq: RadioFrequency
class FrontLineInfo:
def __init__(self, front_line: FrontLine):
self.front_line: FrontLine = front_line
self.player_base: ControlPoint = front_line.control_point_a
self.enemy_base: ControlPoint = front_line.control_point_b
self.player_zero: bool = self.player_base.base.total_armor == 0
self.enemy_zero: bool = self.enemy_base.base.total_armor == 0
self.advantage: bool = (
self.player_base.base.total_armor > self.enemy_base.base.total_armor
)
self.stance: CombatStance = self.player_base.stances[self.enemy_base.id]
self.combat_stances = CombatStance
class MissionInfoGenerator:
"""Base type for generators of mission information for the player.
Examples of subtypes include briefing generators, kneeboard generators, etc.
"""
def __init__(self, mission: Mission, game: Game) -> None:
self.mission = mission
self.game = game
self.awacs: List[AwacsInfo] = []
self.comms: List[CommInfo] = []
self.flights: List[FlightData] = []
self.jtacs: List[JtacInfo] = []
self.tankers: List[TankerInfo] = []
self.frontlines: List[FrontLineInfo] = []
self.dynamic_runways: List[RunwayData] = []
def add_awacs(self, awacs: AwacsInfo) -> None:
"""Adds an AWACS/GCI to the mission.
Args:
awacs: AWACS information.
"""
self.awacs.append(awacs)
def add_comm(self, name: str, freq: RadioFrequency) -> None:
"""Adds communications info to the mission.
Args:
name: Name of the radio channel.
freq: Frequency of the radio channel.
"""
self.comms.append(CommInfo(name, freq))
def add_flight(self, flight: FlightData) -> None:
"""Adds flight info to the mission.
Args:
flight: Flight information.
"""
self.flights.append(flight)
def add_jtac(self, jtac: JtacInfo) -> None:
"""Adds a JTAC to the mission.
Args:
jtac: JTAC information.
"""
self.jtacs.append(jtac)
def add_tanker(self, tanker: TankerInfo) -> None:
"""Adds a tanker to the mission.
Args:
tanker: Tanker information.
"""
self.tankers.append(tanker)
def add_frontline(self, frontline: FrontLineInfo) -> None:
"""Adds a frontline to the briefing
Arguments:
frontline: Frontline conflict information
"""
self.frontlines.append(frontline)
def add_dynamic_runway(self, runway: RunwayData) -> None:
"""Adds a dynamically generated runway to the briefing.
Dynamic runways are any valid landing point that is a unit rather than a
map feature. These include carriers, ships with a helipad, and FARPs.
"""
self.dynamic_runways.append(runway)
def generate(self) -> None:
"""Generates the mission information."""
raise NotImplementedError
def format_waypoint_time(waypoint: FlightWaypoint, depart_prefix: str) -> str:
if waypoint.tot is not None:
time = timedelta(seconds=int(waypoint.tot.total_seconds()))
return f"T+{time} "
elif waypoint.departure_time is not None:
time = timedelta(seconds=int(waypoint.departure_time.total_seconds()))
return f"{depart_prefix} T+{time} "
return ""
class BriefingGenerator(MissionInfoGenerator):
def __init__(self, mission: Mission, game: Game):
super().__init__(mission, game)
self.allied_flights_by_departure: Dict[str, List[FlightData]] = {}
env = Environment(
loader=FileSystemLoader("resources/briefing/templates"),
autoescape=select_autoescape(
disabled_extensions=("",),
default_for_string=True,
default=True,
),
trim_blocks=True,
lstrip_blocks=True,
)
env.filters["waypoint_timing"] = format_waypoint_time
self.template = env.get_template("briefingtemplate_EN.j2")
def generate(self) -> None:
"""Generate the mission briefing"""
self._generate_frontline_info()
self.generate_allied_flights_by_departure()
self.mission.set_description_text(self.template.render(vars(self)))
self.mission.add_picture_blue(
os.path.abspath("./resources/ui/splash_screen.png")
)
def _generate_frontline_info(self) -> None:
"""Build FrontLineInfo objects from FrontLine type and append to briefing."""
for front_line in self.game.theater.conflicts(from_player=True):
self.add_frontline(FrontLineInfo(front_line))
# TODO: This should determine if runway is friendly through a method more robust than the existing string match
def generate_allied_flights_by_departure(self) -> None:
"""Create iterable to display allied flights grouped by departure airfield."""
for flight in self.flights:
if not flight.client_units and flight.friendly:
name = flight.departure.airfield_name
if (
name in self.allied_flights_by_departure
): # where else can we get this?
self.allied_flights_by_departure[name].append(flight)
else:
self.allied_flights_by_departure[name] = [flight]

34
gen/callsigns.py Normal file
View File

@@ -0,0 +1,34 @@
"""Support for working with DCS group callsigns."""
import logging
import re
from dcs.unitgroup import FlyingGroup
from dcs.flyingunit import FlyingUnit
def callsign_for_support_unit(group: FlyingGroup) -> str:
# Either something like Overlord11 for Western AWACS, or else just a number.
# Convert to either "Overlord" or "Flight 123".
lead = group.units[0]
raw_callsign = lead.callsign_as_str()
try:
return f"Flight {int(raw_callsign)}"
except ValueError:
return raw_callsign.rstrip("1234567890")
def create_group_callsign_from_unit(lead: FlyingUnit) -> str:
raw_callsign = lead.callsign_as_str()
if not lead.callsign_is_western:
# Callsigns for non-Western countries are just a number per flight,
# similar to tail numbers.
return f"Flight {raw_callsign}"
# Callsign from pydcs is in the format `<name><group ID><unit ID>`,
# where unit ID is guaranteed to be a single digit but the group ID may
# be more.
match = re.search(r"^(\D+)(\d+)(\d)$", raw_callsign)
if match is None:
logging.error(f"Could not parse unit callsign: {raw_callsign}")
return f"Flight {raw_callsign}"
return f"{match.group(1)} {match.group(2)}"

View File

@@ -0,0 +1,31 @@
import logging
import random
from game import db
from gen.coastal.silkworm import SilkwormGenerator
COASTAL_MAP = {
"SilkwormGenerator": SilkwormGenerator,
}
def generate_coastal_group(game, ground_object, faction_name: str):
"""
This generate a coastal defenses group
:return: Nothing, but put the group reference inside the ground object
"""
faction = db.FACTIONS[faction_name]
if len(faction.coastal_defenses) > 0:
generators = faction.coastal_defenses
if len(generators) > 0:
gen = random.choice(generators)
if gen in COASTAL_MAP.keys():
generator = COASTAL_MAP[gen](game, ground_object, faction)
generator.generate()
return generator.get_generated_group()
else:
logging.info(
"Unable to generate missile group, generator : "
+ str(gen)
+ "does not exists"
)
return None

58
gen/coastal/silkworm.py Normal file
View File

@@ -0,0 +1,58 @@
from dcs.vehicles import MissilesSS, Unarmed, AirDefence
from gen.sam.group_generator import GroupGenerator
class SilkwormGenerator(GroupGenerator):
def __init__(self, game, ground_object, faction):
super(SilkwormGenerator, self).__init__(game, ground_object)
self.faction = faction
def generate(self):
positions = self.get_circular_position(5, launcher_distance=120, coverage=180)
self.add_unit(
MissilesSS.AShM_Silkworm_SR,
"SR#0",
self.position.x,
self.position.y,
self.heading,
)
# Launchers
for i, p in enumerate(positions):
self.add_unit(
MissilesSS.AShM_SS_N_2_Silkworm,
"Missile#" + str(i),
p[0],
p[1],
self.heading,
)
# Commander
self.add_unit(
Unarmed.Truck_KAMAZ_43101,
"KAMAZ#0",
self.position.x - 35,
self.position.y - 20,
self.heading,
)
# Shorad
self.add_unit(
AirDefence.SPAAA_ZSU_23_4_Shilka_Gun_Dish,
"SHILKA#0",
self.position.x - 55,
self.position.y - 38,
self.heading,
)
# Shorad 2
self.add_unit(
AirDefence.SAM_SA_9_Strela_1_Gaskin_TEL,
"STRELA#0",
self.position.x + 200,
self.position.y + 15,
90,
)

View File

@@ -1,264 +1,157 @@
import typing
import pdb
import dcs
import logging
from typing import Tuple, Optional
from random import randint
from dcs import Mission
from dcs.country import Country
from dcs.mapping import Point
from shapely.geometry import LineString, Point as ShapelyPoint
from dcs.mission import *
from dcs.vehicles import *
from dcs.unitgroup import *
from dcs.unittype import *
from dcs.mapping import *
from dcs.point import *
from dcs.task import *
from dcs.country import *
from theater import *
AIR_DISTANCE = 40000
from game.theater.conflicttheater import ConflictTheater, FrontLine
from game.theater.controlpoint import ControlPoint
from game.utils import heading_sum, opposite_heading
GROUND_DISTANCE_FACTOR = 1
GROUNDINTERCEPT_DISTANCE_FACTOR = 6
AIR_DISTANCE = 32000
INTERCEPT_ATTACKERS_HEADING = -45, 45
INTERCEPT_DEFENDERS_HEADING = -10, 10
INTERCEPT_CONFLICT_DISTANCE = 50000
INTERCEPT_ATTACKERS_DISTANCE = 100000
INTERCEPT_MAX_DISTANCE = 160000
INTERCEPT_MIN_DISTANCE = 100000
NAVAL_INTERCEPT_DISTANCE_FACTOR = 0.4
NAVAL_INTERCEPT_DISTANCE_MAX = 40000
NAVAL_INTERCEPT_STEP = 5000
def _opposite_heading(h):
return h+180
def _heading_sum(h, a) -> int:
h += a
if h > 360:
return h - 360
elif h < 0:
return 360 + h
else:
return h
FRONTLINE_LENGTH = 80000
class Conflict:
attackers_side = None # type: Country
defenders_side = None # type: Country
from_cp = None # type: ControlPoint
to_cp = None # type: ControlPoint
position = None # type: Point
size = None # type: int
radials = None # type: typing.List[int]
def __init__(
self,
theater: ConflictTheater,
from_cp: ControlPoint,
to_cp: ControlPoint,
attackers_side: str,
defenders_side: str,
attackers_country: Country,
defenders_country: Country,
position: Point,
heading: Optional[int] = None,
size: Optional[int] = None,
):
ground_attackers_location = None # type: Point
ground_defenders_location = None # type: Point
air_attackers_location = None # type: Point
air_defenders_location = None # type: Point
def __init__(self,
position: Point,
theater: ConflictTheater,
from_cp: ControlPoint,
to_cp: ControlPoint,
attackers_side: Country,
defenders_side: Country,
ground_attackers_location: Point,
ground_defenders_location: Point,
air_attackers_location: Point,
air_defenders_location: Point):
self.attackers_side = attackers_side
self.defenders_side = defenders_side
self.attackers_country = attackers_country
self.defenders_country = defenders_country
self.from_cp = from_cp
self.to_cp = to_cp
self.theater = theater
self.position = position
self.size = to_cp.size
self.radials = to_cp.radials
self.ground_attackers_location = ground_attackers_location
self.ground_defenders_location = ground_defenders_location
self.air_attackers_location = air_attackers_location
self.air_defenders_location = air_defenders_location
self.heading = heading
self.size = size
@classmethod
def _find_ground_location(cls, initial: Point, max_distance: int, heading: int, theater: ConflictTheater) -> Point:
for _ in range(0, int(max_distance), 800):
for _ in range(3):
if theater.is_on_land(initial):
return initial
def has_frontline_between(cls, from_cp: ControlPoint, to_cp: ControlPoint) -> bool:
return from_cp.has_frontline and to_cp.has_frontline
initial = initial.random_point_within(1000, 1000)
@classmethod
def frontline_position(
cls, from_cp: ControlPoint, to_cp: ControlPoint, theater: ConflictTheater
) -> Tuple[Point, int]:
frontline = FrontLine(from_cp, to_cp, theater)
attack_heading = frontline.attack_heading
position = cls.find_ground_position(
frontline.position,
FRONTLINE_LENGTH,
heading_sum(attack_heading, 90),
theater,
)
return position, opposite_heading(attack_heading)
initial = initial.point_from_heading(heading, 800)
@classmethod
def frontline_vector(
cls, from_cp: ControlPoint, to_cp: ControlPoint, theater: ConflictTheater
) -> Tuple[Point, int, int]:
"""
Returns a vector for a valid frontline location avoiding exclusion zones.
"""
center_position, heading = cls.frontline_position(from_cp, to_cp, theater)
left_heading = heading_sum(heading, -90)
right_heading = heading_sum(heading, 90)
left_position = cls.extend_ground_position(
center_position, int(FRONTLINE_LENGTH / 2), left_heading, theater
)
right_position = cls.extend_ground_position(
center_position, int(FRONTLINE_LENGTH / 2), right_heading, theater
)
distance = int(left_position.distance_to_point(right_position))
return left_position, right_heading, distance
print("Didn't find ground position!")
@classmethod
def frontline_cas_conflict(
cls,
attacker_name: str,
defender_name: str,
attacker: Country,
defender: Country,
from_cp: ControlPoint,
to_cp: ControlPoint,
theater: ConflictTheater,
):
assert cls.has_frontline_between(from_cp, to_cp)
position, heading, distance = cls.frontline_vector(from_cp, to_cp, theater)
conflict = cls(
position=position,
heading=heading,
theater=theater,
from_cp=from_cp,
to_cp=to_cp,
attackers_side=attacker_name,
defenders_side=defender_name,
attackers_country=attacker,
defenders_country=defender,
size=distance,
)
return conflict
@classmethod
def extend_ground_position(
cls, initial: Point, max_distance: int, heading: int, theater: ConflictTheater
) -> Point:
"""Finds the first intersection with an exclusion zone in one heading from an initial point up to max_distance"""
extended = initial.point_from_heading(heading, max_distance)
if theater.landmap is None:
# TODO: Why is this possible?
return extended
p0 = ShapelyPoint(initial.x, initial.y)
p1 = ShapelyPoint(extended.x, extended.y)
line = LineString([p0, p1])
intersection = line.intersection(theater.landmap.inclusion_zone_only.boundary)
if intersection.is_empty:
# Max extent does not intersect with the boundary of the inclusion
# zone, so the full front line is usable. This does assume that the
# front line was centered on a valid location.
return extended
# Otherwise extend the front line only up to the intersection.
return initial.point_from_heading(heading, p0.distance(intersection))
@classmethod
def find_ground_position(
cls,
initial: Point,
max_distance: int,
heading: int,
theater: ConflictTheater,
coerce=True,
) -> Optional[Point]:
"""
Finds the nearest valid ground position along a provided heading and it's inverse up to max_distance.
`coerce=True` will return the closest land position to `initial` regardless of heading or distance
`coerce=False` will return None if a point isn't found
"""
pos = initial
if theater.is_on_land(pos):
return pos
for distance in range(0, int(max_distance), 100):
pos = initial.point_from_heading(heading, distance)
if theater.is_on_land(pos):
return pos
pos = initial.point_from_heading(opposite_heading(heading), distance)
if coerce:
pos = theater.nearest_land_pos(initial)
return pos
logging.error("Didn't find ground position ({})!".format(initial))
return None
@classmethod
def capture_conflict(cls, attacker: Country, defender: Country, from_cp: ControlPoint, to_cp: ControlPoint, theater: ConflictTheater):
position = to_cp.position
attack_raw_heading = to_cp.position.heading_between_point(from_cp.position)
attack_heading = to_cp.find_radial(attack_raw_heading)
defense_heading = to_cp.find_radial(from_cp.position.heading_between_point(to_cp.position), ignored_radial=attack_heading)
distance = to_cp.size * GROUND_DISTANCE_FACTOR
attackers_location = position.point_from_heading(attack_heading, distance)
attackers_location = Conflict._find_ground_location(attackers_location, distance * 2, _heading_sum(attack_heading, 180), theater)
defenders_location = position.point_from_heading(defense_heading, distance)
defenders_location = Conflict._find_ground_location(defenders_location, distance * 2, _heading_sum(defense_heading, 180), theater)
return cls(
position=position,
theater=theater,
from_cp=from_cp,
to_cp=to_cp,
attackers_side=attacker,
defenders_side=defender,
ground_attackers_location=attackers_location,
ground_defenders_location=defenders_location,
air_attackers_location=position.point_from_heading(attack_raw_heading, CAPTURE_AIR_ATTACKERS_DISTANCE),
air_defenders_location=position.point_from_heading(_opposite_heading(attack_raw_heading), CAPTURE_AIR_DEFENDERS_DISTANCE)
)
@classmethod
def intercept_conflict(cls, attacker: Country, defender: Country, from_cp: ControlPoint, to_cp: ControlPoint, theater: ConflictTheater):
raw_distance = from_cp.position.distance_to_point(to_cp.position) * 1.5
distance = max(min(raw_distance, INTERCEPT_MAX_DISTANCE), INTERCEPT_MIN_DISTANCE)
heading = _heading_sum(from_cp.position.heading_between_point(to_cp.position), random.choice([-1, 1]) * random.randint(60, 100))
position = from_cp.position.point_from_heading(heading, distance)
return cls(
position=position.point_from_heading(position.heading_between_point(to_cp.position), INTERCEPT_CONFLICT_DISTANCE),
theater=theater,
from_cp=from_cp,
to_cp=to_cp,
attackers_side=attacker,
defenders_side=defender,
ground_attackers_location=None,
ground_defenders_location=None,
air_attackers_location=position.point_from_heading(random.randint(*INTERCEPT_ATTACKERS_HEADING) + heading, INTERCEPT_ATTACKERS_DISTANCE),
air_defenders_location=position
)
@classmethod
def ground_attack_conflict(cls, attacker: Country, defender: Country, from_cp: ControlPoint, to_cp: ControlPoint, theater: ConflictTheater):
heading = random.choice(to_cp.radials)
initial_location = to_cp.position.random_point_within(*GROUND_ATTACK_DISTANCE)
position = Conflict._find_ground_location(initial_location, GROUND_INTERCEPT_SPREAD, _heading_sum(heading, 180), theater)
if not position:
heading = to_cp.find_radial(to_cp.position.heading_between_point(from_cp.position))
position = to_cp.position.point_from_heading(heading, to_cp.size * GROUND_DISTANCE_FACTOR)
return cls(
position=position,
theater=theater,
from_cp=from_cp,
to_cp=to_cp,
attackers_side=attacker,
defenders_side=defender,
ground_attackers_location=position,
ground_defenders_location=None,
air_attackers_location=None,
air_defenders_location=position.point_from_heading(heading, AIR_DISTANCE),
)
@classmethod
def ground_intercept_conflict(cls, attacker: Country, defender: Country, heading: int, from_cp: ControlPoint, to_cp: ControlPoint, theater: ConflictTheater):
heading = random.choice(to_cp.radials)
initial_location = to_cp.position.point_from_heading(heading, to_cp.size * GROUNDINTERCEPT_DISTANCE_FACTOR)
max_distance = to_cp.size * GROUNDINTERCEPT_DISTANCE_FACTOR
ground_location = Conflict._find_ground_location(initial_location, max_distance, _heading_sum(heading, 180), theater)
return cls(
position=position,
theater=theater,
from_cp=from_cp,
to_cp=to_cp,
attackers_side=attacker,
defenders_side=defender,
ground_attackers_location=None,
ground_defenders_location=position,
air_attackers_location=position.point_from_heading(random.randint(*INTERCEPT_ATTACKERS_HEADING) + heading, AIR_DISTANCE),
air_defenders_location=position.point_from_heading(random.randint(*INTERCEPT_ATTACKERS_HEADING) + _opposite_heading(heading), AIR_DISTANCE)
)
@classmethod
def ground_base_attack(cls, attacker: Country, defender: Country, from_cp: ControlPoint, to_cp: ControlPoint, theater: ConflictTheater):
position = to_cp.position
attack_heading = to_cp.find_radial(to_cp.position.heading_between_point(from_cp.position))
defense_heading = to_cp.find_radial(from_cp.position.heading_between_point(to_cp.position), ignored_radial=attack_heading)
distance = to_cp.size * GROUND_DISTANCE_FACTOR
defenders_location = position.point_from_heading(defense_heading, distance)
defenders_location = Conflict._find_ground_location(defenders_location, distance * 2, _heading_sum(defense_heading, 180), theater)
return cls(
position=position,
theater=theater,
from_cp=from_cp,
to_cp=to_cp,
attackers_side=attacker,
defenders_side=defender,
ground_attackers_location=None,
ground_defenders_location=defenders_location,
air_attackers_location=position.point_from_heading(attack_heading, AIR_DISTANCE),
air_defenders_location=position
)
@classmethod
def naval_intercept_conflict(cls, attacker: Country, defender: Country, from_cp: ControlPoint, to_cp: ControlPoint, theater: ConflictTheater):
radial = random.choice(to_cp.sea_radials)
initial_distance = min(int(from_cp.position.distance_to_point(to_cp.position) * NAVAL_INTERCEPT_DISTANCE_FACTOR), NAVAL_INTERCEPT_DISTANCE_MAX)
position = to_cp.position.point_from_heading(radial, initial_distance)
for offset in range(0, initial_distance, NAVAL_INTERCEPT_STEP):
position = to_cp.position.point_from_heading(_opposite_heading(radial), initial_distance - offset)
if not theater.is_on_land(position):
break
attacker_heading = from_cp.position.heading_between_point(to_cp.position)
return cls(
position=position,
theater=theater,
from_cp=from_cp,
to_cp=to_cp,
attackers_side=attacker,
defenders_side=defender,
ground_attackers_location=None,
ground_defenders_location=position,
air_attackers_location=position.point_from_heading(attacker_heading, AIR_DISTANCE),
air_defenders_location=position.point_from_heading(_opposite_heading(attacker_heading), AIR_DISTANCE)
)
@classmethod
def transport_conflict(cls, attacker: Country, defender: Country, from_cp: ControlPoint, to_cp: ControlPoint, theater: ConflictTheater):
frontline_position = cls._frontline_position(from_cp, to_cp)
heading = to_cp.position.heading_between_point(from_cp.position)
initial_dest = frontline_position.point_from_heading(heading, TRANSPORT_FRONTLINE_DIST)
dest = cls._find_ground_location(initial_dest, from_cp.position.distance_to_point(to_cp.position) / 3, heading, theater)
if not dest:
radial = to_cp.find_radial(to_cp.position.heading_between_point(from_cp.position))
dest = to_cp.position.point_from_heading(radial, to_cp.size * GROUND_DISTANCE_FACTOR)
return cls(
position=dest,
theater=theater,
from_cp=from_cp,
to_cp=to_cp,
attackers_side=attacker,
defenders_side=defender,
ground_attackers_location=from_cp.position,
ground_defenders_location=frontline_position,
air_attackers_location=from_cp.position.point_from_heading(0, 100),
air_defenders_location=frontline_position
)

View File

@@ -0,0 +1,43 @@
import random
from dcs.vehicles import Armor
from game import db
from gen.defenses.armored_group_generator import (
ArmoredGroupGenerator,
FixedSizeArmorGroupGenerator,
)
def generate_armor_group(faction: str, game, ground_object):
"""
This generate a group of ground units
:return: Generated group
"""
possible_unit = [
u for u in db.FACTIONS[faction].frontline_units if u in Armor.__dict__.values()
]
if len(possible_unit) > 0:
unit_type = random.choice(possible_unit)
return generate_armor_group_of_type(game, ground_object, unit_type)
return None
def generate_armor_group_of_type(game, ground_object, unit_type):
"""
This generate a group of ground units of given type
:return: Generated group
"""
generator = ArmoredGroupGenerator(game, ground_object, unit_type)
generator.generate()
return generator.get_generated_group()
def generate_armor_group_of_type_and_size(game, ground_object, unit_type, size: int):
"""
This generate a group of ground units of given type and size
:return: Generated group
"""
generator = FixedSizeArmorGroupGenerator(game, ground_object, unit_type, size)
generator.generate()
return generator.get_generated_group()

View File

@@ -0,0 +1,49 @@
import random
from gen.sam.group_generator import GroupGenerator
class ArmoredGroupGenerator(GroupGenerator):
def __init__(self, game, ground_object, unit_type):
super(ArmoredGroupGenerator, self).__init__(game, ground_object)
self.unit_type = unit_type
def generate(self):
grid_x = random.randint(2, 3)
grid_y = random.randint(1, 2)
spacing = random.randint(30, 80)
index = 0
for i in range(grid_x):
for j in range(grid_y):
index = index + 1
self.add_unit(
self.unit_type,
"Armor#" + str(index),
self.position.x + spacing * i,
self.position.y + spacing * j,
self.heading,
)
class FixedSizeArmorGroupGenerator(GroupGenerator):
def __init__(self, game, ground_object, unit_type, size):
super(FixedSizeArmorGroupGenerator, self).__init__(game, ground_object)
self.unit_type = unit_type
self.size = size
def generate(self):
spacing = random.randint(20, 70)
index = 0
for i in range(self.size):
index = index + 1
self.add_unit(
self.unit_type,
"Armor#" + str(index),
self.position.x + spacing * i,
self.position.y,
self.heading,
)

View File

@@ -1,107 +1,37 @@
import typing
import random
from datetime import datetime, timedelta, time
from typing import Optional
from dcs.mission import Mission
from dcs.triggers import *
from dcs.condition import *
from dcs.action import *
from dcs.unit import Skill
from dcs.point import MovingPoint, PointProperties
from dcs.action import *
from dcs.weather import *
from game import db
from theater import *
from gen import *
WEATHER_CLOUD_BASE = 2000, 3000
WEATHER_CLOUD_DENSITY = 1, 8
WEATHER_CLOUD_THICKNESS = 100, 400
WEATHER_CLOUD_BASE_MIN = 1200
RANDOM_TIME = {
"night": 5,
"dusk": 30,
"dawn": 30,
"day": 100,
}
RANDOM_WEATHER = {
1: 5, # heavy rain
2: 15, # rain
3: 25, # dynamic
4: 35, # clear
5: 100, # random
}
from game.weather import Clouds, Fog, Conditions, WindConditions
class EnvironmentSettings:
weather_dict = None
start_time = None
class EnviromentGenerator:
def __init__(self, mission: Mission, conflict: Conflict, game):
class EnvironmentGenerator:
def __init__(self, mission: Mission, conditions: Conditions) -> None:
self.mission = mission
self.conflict = conflict
self.game = game
self.conditions = conditions
def _gen_random_time(self):
start_time = datetime.combine(datetime.today(), time())
time_range = None
for k, v in RANDOM_TIME.items():
if self.game.settings.night_disabled and k == "night":
continue
def set_clouds(self, clouds: Optional[Clouds]) -> None:
if clouds is None:
return
self.mission.weather.clouds_base = clouds.base
self.mission.weather.clouds_thickness = clouds.thickness
self.mission.weather.clouds_density = clouds.density
self.mission.weather.clouds_iprecptns = clouds.precipitation
self.mission.weather.clouds_preset = clouds.preset
if random.randint(0, 100) <= v:
time_range = self.game.theater.daytime_map[k]
break
def set_fog(self, fog: Optional[Fog]) -> None:
if fog is None:
return
self.mission.weather.fog_visibility = fog.visibility.meters
self.mission.weather.fog_thickness = fog.thickness
start_time += timedelta(hours=random.randint(*time_range))
self.mission.start_time = start_time
def _gen_random_weather(self):
weather_type = None
for k, v in RANDOM_WEATHER.items():
if random.randint(0, 100) <= v:
weather_type = k
break
print("generated weather {}".format(weather_type))
if weather_type == 1:
self.mission.weather.heavy_rain()
elif weather_type == 2:
self.mission.weather.heavy_rain()
self.mission.weather.enable_fog = False
elif weather_type == 3:
self.mission.weather.random(self.mission.start_time, self.conflict.theater.terrain)
elif weather_type == 4:
pass
elif weather_type == 5:
self.mission.weather.clouds_base = random.randint(*WEATHER_CLOUD_BASE)
self.mission.weather.clouds_density = random.randint(*WEATHER_CLOUD_DENSITY)
self.mission.weather.clouds_thickness = random.randint(*WEATHER_CLOUD_THICKNESS)
wind_direction = random.randint(0, 360)
wind_speed = random.randint(0, 13)
self.mission.weather.wind_at_ground = Wind(wind_direction, wind_speed)
self.mission.weather.wind_at_2000 = Wind(wind_direction, wind_speed * 2)
self.mission.weather.wind_at_8000 = Wind(wind_direction, wind_speed * 3)
if self.mission.weather.clouds_density > 0:
self.mission.weather.clouds_base = max(self.mission.weather.clouds_base, WEATHER_CLOUD_BASE_MIN)
def generate(self) -> EnvironmentSettings:
self._gen_random_time()
self._gen_random_weather()
settings = EnvironmentSettings()
settings.start_time = self.mission.start_time
settings.weather_dict = self.mission.weather.dict()
return settings
def load(self, settings: EnvironmentSettings):
self.mission.start_time = settings.start_time
self.mission.weather.load_from_dict(settings.weather_dict)
def set_wind(self, wind: WindConditions) -> None:
self.mission.weather.wind_at_ground = wind.at_0m
self.mission.weather.wind_at_2000 = wind.at_2000m
self.mission.weather.wind_at_8000 = wind.at_8000m
def generate(self):
self.mission.start_time = self.conditions.start_time
self.set_clouds(self.conditions.weather.clouds)
self.set_fog(self.conditions.weather.fog)
self.set_wind(self.conditions.weather.wind)

123
gen/fleet/carrier_group.py Normal file
View File

@@ -0,0 +1,123 @@
import random
from gen.sam.group_generator import ShipGroupGenerator
from dcs.ships import DDG_Arleigh_Burke_IIa, CG_Ticonderoga
class CarrierGroupGenerator(ShipGroupGenerator):
def generate(self):
# Carrier Strike Group 8
if self.faction.carrier_names[0] == "Carrier Strike Group 8":
carrier_type = random.choice(self.faction.aircraft_carrier)
self.add_unit(
carrier_type,
"CVN-75 Harry S. Truman",
self.position.x,
self.position.y,
self.heading,
)
# Add Arleigh Burke escort
self.add_unit(
DDG_Arleigh_Burke_IIa,
"USS Ramage",
self.position.x + 6482,
self.position.y + 6667,
self.heading,
)
self.add_unit(
DDG_Arleigh_Burke_IIa,
"USS Mitscher",
self.position.x - 7963,
self.position.y + 7037,
self.heading,
)
self.add_unit(
DDG_Arleigh_Burke_IIa,
"USS Forrest Sherman",
self.position.x - 7408,
self.position.y - 7408,
self.heading,
)
self.add_unit(
DDG_Arleigh_Burke_IIa,
"USS Lassen",
self.position.x + 8704,
self.position.y - 6296,
self.heading,
)
# Add Ticonderoga escort
if self.heading >= 180:
self.add_unit(
CG_Ticonderoga,
"USS Hué City",
self.position.x + 2222,
self.position.y - 3333,
self.heading,
)
else:
self.add_unit(
CG_Ticonderoga,
"USS Hué City",
self.position.x - 3333,
self.position.y + 2222,
self.heading,
)
self.get_generated_group().points[0].speed = 20
##################################################################################################
# Add carrier for normal generation
else:
if len(self.faction.aircraft_carrier) > 0:
carrier_type = random.choice(self.faction.aircraft_carrier)
self.add_unit(
carrier_type,
"Carrier",
self.position.x,
self.position.y,
self.heading,
)
else:
return
# Add destroyers escort
if len(self.faction.destroyers) > 0:
dd_type = random.choice(self.faction.destroyers)
self.add_unit(
dd_type,
"DD1",
self.position.x + 2500,
self.position.y + 4500,
self.heading,
)
self.add_unit(
dd_type,
"DD2",
self.position.x + 2500,
self.position.y - 4500,
self.heading,
)
self.add_unit(
dd_type,
"DD3",
self.position.x + 4500,
self.position.y + 8500,
self.heading,
)
self.add_unit(
dd_type,
"DD4",
self.position.x + 4500,
self.position.y - 8500,
self.heading,
)
self.get_generated_group().points[0].speed = 20

73
gen/fleet/cn_dd_group.py Normal file
View File

@@ -0,0 +1,73 @@
from __future__ import annotations
import random
from typing import TYPE_CHECKING
from dcs.ships import (
Type_052C_Destroyer,
Type_052B_Destroyer,
Type_054A_Frigate,
)
from game.factions.faction import Faction
from gen.fleet.dd_group import DDGroupGenerator
from gen.sam.group_generator import ShipGroupGenerator
from game.theater.theatergroundobject import TheaterGroundObject
if TYPE_CHECKING:
from game.game import Game
class ChineseNavyGroupGenerator(ShipGroupGenerator):
def generate(self):
include_frigate = random.choice([True, True, False])
include_dd = random.choice([True, False])
if not any([include_frigate, include_dd]):
include_frigate = True
if include_frigate:
self.add_unit(
Type_054A_Frigate,
"FF1",
self.position.x + 1200,
self.position.y + 900,
self.heading,
)
self.add_unit(
Type_054A_Frigate,
"FF2",
self.position.x + 1200,
self.position.y - 900,
self.heading,
)
if include_dd:
dd_type = random.choice([Type_052C_Destroyer, Type_052B_Destroyer])
self.add_unit(
dd_type,
"DD1",
self.position.x + 2400,
self.position.y + 900,
self.heading,
)
self.add_unit(
dd_type,
"DD2",
self.position.x + 2400,
self.position.y - 900,
self.heading,
)
self.get_generated_group().points[0].speed = 20
class Type54GroupGenerator(DDGroupGenerator):
def __init__(
self, game: Game, ground_object: TheaterGroundObject, faction: Faction
):
super(Type54GroupGenerator, self).__init__(
game, ground_object, faction, Type_054A_Frigate
)

59
gen/fleet/dd_group.py Normal file
View File

@@ -0,0 +1,59 @@
from __future__ import annotations
from typing import TYPE_CHECKING
from game.factions.faction import Faction
from game.theater.theatergroundobject import TheaterGroundObject
from gen.sam.group_generator import ShipGroupGenerator
from dcs.unittype import ShipType
from dcs.ships import FFG_Oliver_Hazzard_Perry, DDG_Arleigh_Burke_IIa
if TYPE_CHECKING:
from game.game import Game
class DDGroupGenerator(ShipGroupGenerator):
def __init__(
self,
game: Game,
ground_object: TheaterGroundObject,
faction: Faction,
ddtype: ShipType,
):
super(DDGroupGenerator, self).__init__(game, ground_object, faction)
self.ddtype = ddtype
def generate(self):
self.add_unit(
self.ddtype,
"DD1",
self.position.x + 500,
self.position.y + 900,
self.heading,
)
self.add_unit(
self.ddtype,
"DD2",
self.position.x + 500,
self.position.y - 900,
self.heading,
)
self.get_generated_group().points[0].speed = 20
class OliverHazardPerryGroupGenerator(DDGroupGenerator):
def __init__(
self, game: Game, ground_object: TheaterGroundObject, faction: Faction
):
super(OliverHazardPerryGroupGenerator, self).__init__(
game, ground_object, faction, FFG_Oliver_Hazzard_Perry
)
class ArleighBurkeGroupGenerator(DDGroupGenerator):
def __init__(
self, game: Game, ground_object: TheaterGroundObject, faction: Faction
):
super(ArleighBurkeGroupGenerator, self).__init__(
game, ground_object, faction, DDG_Arleigh_Burke_IIa
)

34
gen/fleet/lha_group.py Normal file
View File

@@ -0,0 +1,34 @@
import random
from gen.sam.group_generator import ShipGroupGenerator
class LHAGroupGenerator(ShipGroupGenerator):
def generate(self):
# Add carrier
if len(self.faction.helicopter_carrier) > 0:
carrier_type = random.choice(self.faction.helicopter_carrier)
self.add_unit(
carrier_type, "LHA", self.position.x, self.position.y, self.heading
)
# Add destroyers escort
if len(self.faction.destroyers) > 0:
dd_type = random.choice(self.faction.destroyers)
self.add_unit(
dd_type,
"DD1",
self.position.x + 1250,
self.position.y + 1450,
self.heading,
)
self.add_unit(
dd_type,
"DD2",
self.position.x + 1250,
self.position.y - 1450,
self.heading,
)
self.get_generated_group().points[0].speed = 20

Some files were not shown because too many files have changed in this diff Show More