Compare commits
99 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
ba8112280d | ||
|
|
4cc373595c | ||
|
|
4c5a2650d1 | ||
|
|
b99cdfb5c3 | ||
|
|
6ec14e744e | ||
|
|
310db66c22 | ||
|
|
c7c2b9a248 | ||
|
|
b697a8b40a | ||
|
|
c152b49b88 | ||
|
|
2356fc2bbf | ||
|
|
f7e2c8921c | ||
|
|
604352f8df | ||
|
|
fbbe56f954 | ||
|
|
7842c69ebb | ||
|
|
e1d50f1f27 | ||
|
|
9d0997624b | ||
|
|
355cd3e0e4 | ||
|
|
af5cd57094 | ||
|
|
8f85101cec | ||
|
|
97be483624 | ||
|
|
1ff5721912 | ||
|
|
80cbc663bf | ||
|
|
b3e729af0d | ||
|
|
3ed864021d | ||
|
|
8bc269fa99 | ||
|
|
c656c0f7e4 | ||
|
|
858e5d2d04 | ||
|
|
903a8db46f | ||
|
|
5aa731853d | ||
|
|
540096178c | ||
|
|
ed2a611197 | ||
|
|
1c61b0b5a2 | ||
|
|
82f7e5d0c4 | ||
|
|
ecb2c86dc4 | ||
|
|
5cbbc3b1ab | ||
|
|
e0b2e178f9 | ||
|
|
390974ba0f | ||
|
|
91379ff7d9 | ||
|
|
1fe9e56997 | ||
|
|
e10b853712 | ||
|
|
7d70862e72 | ||
|
|
90bdcec7ff | ||
|
|
63da350223 | ||
|
|
854f31cb7a | ||
|
|
eba6daf6c8 | ||
|
|
911d57b415 | ||
|
|
d91e0344a7 | ||
|
|
e9d7ee51f3 | ||
|
|
9053408e13 | ||
|
|
8f4094ee98 | ||
|
|
933e064079 | ||
|
|
274e08dd8b | ||
|
|
05c968edc2 | ||
|
|
270820de0b | ||
|
|
e049a97bec | ||
|
|
e2306ba0f3 | ||
|
|
6d0f488672 | ||
|
|
397f9a58cb | ||
|
|
4fc766a524 | ||
|
|
3f8b5c6c00 | ||
|
|
ce43be0d67 | ||
|
|
ff08888385 | ||
|
|
6b96410ea4 | ||
|
|
f21bd10f09 | ||
|
|
07b35f8ee1 | ||
|
|
251435ae0b | ||
|
|
b81bf90319 | ||
|
|
d6b1b8665d | ||
|
|
64bd3e6a52 | ||
|
|
520a0f91fd | ||
|
|
0015667829 | ||
|
|
35a7da2816 | ||
|
|
5bbf3fc49f | ||
|
|
7a8dfeb819 | ||
|
|
e28a24c875 | ||
|
|
823c6a6137 | ||
|
|
2cbe63f162 | ||
|
|
93d0746d3e | ||
|
|
8cb7c7378f | ||
|
|
3500c85e8d | ||
|
|
c699567c73 | ||
|
|
056c397e68 | ||
|
|
8431c7745d | ||
|
|
8df4607e50 | ||
|
|
edf9efddf9 | ||
|
|
03fc17fae6 | ||
|
|
6fb342a42c | ||
|
|
262347f8c8 | ||
|
|
1176b92073 | ||
|
|
afb084ebf8 | ||
|
|
12853feec3 | ||
|
|
d31876b65e | ||
|
|
ca521e7e51 | ||
|
|
f21c515d5c | ||
|
|
49f2c00d76 | ||
|
|
d284305323 | ||
|
|
c32ac8577c | ||
|
|
0d5530f5ea | ||
|
|
a528249062 |
6
.gitignore
vendored
@@ -1,10 +1,14 @@
|
|||||||
*.pyc
|
*.pyc
|
||||||
__pycache__
|
__pycache__
|
||||||
build/*
|
build/**
|
||||||
resources/payloads/*.lua
|
resources/payloads/*.lua
|
||||||
venv
|
venv
|
||||||
logs.txt
|
logs.txt
|
||||||
.DS_Store
|
.DS_Store
|
||||||
|
dist/**
|
||||||
|
a.py
|
||||||
|
resources/tools/a.miz
|
||||||
|
tests/**
|
||||||
# User-specific stuff
|
# User-specific stuff
|
||||||
.idea/**/workspace.xml
|
.idea/**/workspace.xml
|
||||||
.idea/**/tasks.xml
|
.idea/**/tasks.xml
|
||||||
|
|||||||
1
.idea/modules.xml
generated
@@ -3,7 +3,6 @@
|
|||||||
<component name="ProjectModuleManager">
|
<component name="ProjectModuleManager">
|
||||||
<modules>
|
<modules>
|
||||||
<module fileurl="file://$PROJECT_DIR$/.idea/dcs_pmcliberation.iml" filepath="$PROJECT_DIR$/.idea/dcs_pmcliberation.iml" />
|
<module fileurl="file://$PROJECT_DIR$/.idea/dcs_pmcliberation.iml" filepath="$PROJECT_DIR$/.idea/dcs_pmcliberation.iml" />
|
||||||
<module fileurl="file://$PROJECT_DIR$/.idea/dcs_pmcliberation.iml" filepath="$PROJECT_DIR$/.idea/dcs_pmcliberation.iml" />
|
|
||||||
</modules>
|
</modules>
|
||||||
</component>
|
</component>
|
||||||
</project>
|
</project>
|
||||||
26
README.md
@@ -1,8 +1,26 @@
|
|||||||
[DCS World](https://www.digitalcombatsimulator.com/en/products/world/) single-player liberation dynamic campaign.
|

|
||||||
|
|
||||||
[Installation instructions/Manual](https://github.com/shdwp/dcs_liberation/wiki)
|
[DCS World](https://www.digitalcombatsimulator.com/en/products/world/) single-player dynamic campaign.
|
||||||
|
|
||||||
Inspired by *ARMA Liberation* mission.
|
|
||||||
|
|
||||||
Uses [pydcs](http://github.com/pydcs/dcs) for mission generation.
|
Uses [pydcs](http://github.com/pydcs/dcs) for mission generation.
|
||||||
|
|
||||||
|
## Tutorials
|
||||||
|
* [Manual](https://github.com/shdwp/dcs_liberation/wiki/Manual)
|
||||||
|
|
||||||
|
You should start with the manual, it covers everything you need to know before playing the campaign.
|
||||||
|
|
||||||
|
* [Strike objectives reference images](https://imgur.com/a/vCSHa9f)
|
||||||
|
|
||||||
|
If you can't find the strike objective you can see here how it's supposed to look.
|
||||||
|
|
||||||
|
* [Troubleshooting](https://github.com/shdwp/dcs_liberation/wiki/Troubleshooting)
|
||||||
|
|
||||||
|
You could also briefly check the troubleshooting page to get familiar with the known issues that you could probably fix by yourself.
|
||||||
|
|
||||||
|
* [Modding tutorial](https://github.com/shdwp/dcs_liberation/wiki/Modding-tutorial)
|
||||||
|
|
||||||
|
Modding tutorial will cover how to change default loadouts, configure which planes are present in the campaign (or add new altogether) and more. Check this out if you find that something is not going for your liking, there could be a tutorial for changing that. Although be aware that it would require changing source files and could easily result in non functioning application.
|
||||||
|
|
||||||
|
* [Development guide](https://github.com/shdwp/dcs_liberation/wiki/Development-guide)
|
||||||
|
|
||||||
|
If you want to contribute to the project, this will give you a brief overview and on how to actually run it from source files.
|
||||||
|
|||||||
63
__init__.py
@@ -1,20 +1,16 @@
|
|||||||
#!/usr/bin/env python3
|
#!/usr/bin/env python3
|
||||||
import os
|
|
||||||
import sys
|
|
||||||
import dcs
|
|
||||||
import logging
|
import logging
|
||||||
|
import os
|
||||||
|
import re
|
||||||
|
import sys
|
||||||
|
|
||||||
import theater.caucasus
|
import dcs
|
||||||
import theater.persiangulf
|
|
||||||
import theater.nevada
|
|
||||||
|
|
||||||
import ui.window
|
import ui.corruptedsavemenu
|
||||||
import ui.mainmenu
|
import ui.mainmenu
|
||||||
import ui.newgamemenu
|
import ui.newgamemenu
|
||||||
import ui.corruptedsavemenu
|
import ui.window
|
||||||
|
|
||||||
from game.game import Game
|
from game.game import Game
|
||||||
from theater import start_generator
|
|
||||||
from userdata import persistency, logging as logging_module
|
from userdata import persistency, logging as logging_module
|
||||||
|
|
||||||
assert len(sys.argv) >= 3, "__init__.py should be started with two mandatory arguments: %UserProfile% location and application version"
|
assert len(sys.argv) >= 3, "__init__.py should be started with two mandatory arguments: %UserProfile% location and application version"
|
||||||
@@ -33,56 +29,35 @@ def proceed_to_main_menu(game: Game):
|
|||||||
|
|
||||||
|
|
||||||
def is_version_compatible(save_version):
|
def is_version_compatible(save_version):
|
||||||
current_version = VERSION_STRING.split(".")
|
current_version_components = re.split(r"[\._]", VERSION_STRING)
|
||||||
save_version = save_version.split(".")
|
save_version_components = re.split(r"[\._]", save_version)
|
||||||
|
|
||||||
if "--ignore-save" in sys.argv:
|
if "--ignore-save" in sys.argv:
|
||||||
return False
|
return False
|
||||||
|
|
||||||
if current_version[:2] == save_version[:2]:
|
if current_version_components == save_version_components:
|
||||||
|
return True
|
||||||
|
|
||||||
|
if save_version in ["1.4_rc1", "1.4_rc2", "1.4_rc3", "1.4_rc4", "1.4_rc5", "1.4_rc6"]:
|
||||||
|
return False
|
||||||
|
|
||||||
|
if current_version_components[:2] == save_version_components[:2]:
|
||||||
return True
|
return True
|
||||||
|
|
||||||
return False
|
return False
|
||||||
|
|
||||||
|
|
||||||
w = ui.window.Window()
|
w = ui.window.Window()
|
||||||
|
|
||||||
try:
|
try:
|
||||||
game = persistency.restore_game()
|
game = persistency.restore_game()
|
||||||
if not game or not is_version_compatible(game.settings.version):
|
if not game or not is_version_compatible(game.settings.version):
|
||||||
new_game_menu = None # type: NewGameMenu
|
ui.newgamemenu.NewGameMenu(w, w.start_new_game).display()
|
||||||
|
|
||||||
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
|
|
||||||
game.settings.version = VERSION_STRING
|
|
||||||
|
|
||||||
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:
|
else:
|
||||||
|
game.settings.version = VERSION_STRING
|
||||||
proceed_to_main_menu(game)
|
proceed_to_main_menu(game)
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
print(e)
|
logging.exception(e)
|
||||||
ui.corruptedsavemenu.CorruptedSaveMenu(w).display()
|
ui.corruptedsavemenu.CorruptedSaveMenu(w).display()
|
||||||
|
|
||||||
w.run()
|
w.run()
|
||||||
|
|||||||
38
a.py
Normal file
@@ -0,0 +1,38 @@
|
|||||||
|
from theater.caucasus import *
|
||||||
|
from gen.conflictgen import Conflict
|
||||||
|
|
||||||
|
from matplotlib import pyplot
|
||||||
|
from matplotlib import lines
|
||||||
|
from shapely import geometry
|
||||||
|
from shapely.geometry import Polygon
|
||||||
|
from descartes.patch import PolygonPatch
|
||||||
|
|
||||||
|
def put_lines(ls, ax):
|
||||||
|
for g in ls.geoms:
|
||||||
|
ax.plot([g.xy[0][0], g.xy[0][1]], [g.xy[1][0], g.xy[1][1]])
|
||||||
|
|
||||||
|
cau = CaucasusTheater()
|
||||||
|
#left, heading, dist = Conflict.frontline_vector(cau.soganlug, cau.kutaisi, cau)
|
||||||
|
#right = left.point_from_heading(heading, dist)
|
||||||
|
|
||||||
|
left, heading = Conflict.frontline_position(cau, cau.soganlug, cau.kutaisi)
|
||||||
|
right = left.point_from_heading(heading+90, 80000)
|
||||||
|
left = left.point_from_heading(heading-90, 80000)
|
||||||
|
|
||||||
|
line = geometry.LineString([(left.x, left.y), (right.x, right.y)])
|
||||||
|
line = line.intersection(cau.land_poly)
|
||||||
|
|
||||||
|
fig = pyplot.figure(1, figsize=(20, 20), dpi=90)
|
||||||
|
ax = fig.add_subplot(121)
|
||||||
|
ax.set_ylim([0, 1500000])
|
||||||
|
ax.set_xlim([-600000, 400000])
|
||||||
|
|
||||||
|
patch = PolygonPatch(cau.land_poly, facecolor=(0, 0, 0), edgecolor=(0, 0, 0), alpha=0.5, zorder=2)
|
||||||
|
ax.add_patch(patch)
|
||||||
|
ax.plot([left.x, right.x], [left.y, right.y], 'k-', lw=2)
|
||||||
|
|
||||||
|
ax.plot([cau.soganlug.position.x, cau.soganlug.position.x+1000], [cau.soganlug.position.y, cau.soganlug.position.y+1000], lw=5)
|
||||||
|
ax.plot([cau.kutaisi.position.x, cau.kutaisi.position.x+1000], [cau.kutaisi.position.y, cau.kutaisi.position.y+1000], lw=5)
|
||||||
|
put_lines(line, ax)
|
||||||
|
pyplot.show()
|
||||||
|
|
||||||
162
game/db.py
@@ -2,12 +2,14 @@ import typing
|
|||||||
import enum
|
import enum
|
||||||
|
|
||||||
from dcs.vehicles import *
|
from dcs.vehicles import *
|
||||||
from dcs.unitgroup import *
|
|
||||||
from dcs.ships import *
|
from dcs.ships import *
|
||||||
from dcs.planes import *
|
from dcs.planes import *
|
||||||
from dcs.helicopters import *
|
from dcs.helicopters import *
|
||||||
|
|
||||||
from dcs.task import *
|
from dcs.task import *
|
||||||
|
from dcs.unit import *
|
||||||
from dcs.unittype import *
|
from dcs.unittype import *
|
||||||
|
from dcs.unitgroup import *
|
||||||
|
|
||||||
"""
|
"""
|
||||||
---------- BEGINNING OF CONFIGURATION SECTION
|
---------- BEGINNING OF CONFIGURATION SECTION
|
||||||
@@ -37,12 +39,11 @@ and prioritization for the enemy (i.e. less important bases will receive units w
|
|||||||
"""
|
"""
|
||||||
PRICES = {
|
PRICES = {
|
||||||
# fighter
|
# fighter
|
||||||
C_101CC: 8,
|
MiG_23MLD: 13,
|
||||||
MiG_23MLD: 18,
|
Su_27: 18,
|
||||||
Su_27: 24,
|
Su_33: 22,
|
||||||
Su_33: 25,
|
MiG_29A: 18,
|
||||||
MiG_29A: 24,
|
MiG_29S: 20,
|
||||||
MiG_29S: 26,
|
|
||||||
|
|
||||||
F_5E_3: 6,
|
F_5E_3: 6,
|
||||||
MiG_15bis: 5,
|
MiG_15bis: 5,
|
||||||
@@ -52,7 +53,8 @@ PRICES = {
|
|||||||
AV8BNA: 13,
|
AV8BNA: 13,
|
||||||
M_2000C: 13,
|
M_2000C: 13,
|
||||||
FA_18C_hornet: 18,
|
FA_18C_hornet: 18,
|
||||||
F_15C: 24,
|
F_15C: 20,
|
||||||
|
F_14B: 14,
|
||||||
|
|
||||||
# bomber
|
# bomber
|
||||||
Su_25: 15,
|
Su_25: 15,
|
||||||
@@ -65,7 +67,8 @@ PRICES = {
|
|||||||
|
|
||||||
# heli
|
# heli
|
||||||
Ka_50: 13,
|
Ka_50: 13,
|
||||||
UH_1H: 5,
|
SA342M: 8,
|
||||||
|
UH_1H: 4,
|
||||||
Mi_8MT: 5,
|
Mi_8MT: 5,
|
||||||
|
|
||||||
# special
|
# special
|
||||||
@@ -82,15 +85,14 @@ PRICES = {
|
|||||||
C_130: 8,
|
C_130: 8,
|
||||||
|
|
||||||
# armor
|
# armor
|
||||||
Armor.MBT_T_55: 4,
|
Armor.APC_BTR_80: 16,
|
||||||
Armor.MBT_T_80U: 8,
|
Armor.MBT_T_55: 22,
|
||||||
Armor.MBT_T_90: 10,
|
Armor.MBT_T_80U: 28,
|
||||||
|
Armor.MBT_T_90: 35,
|
||||||
|
|
||||||
Armor.MBT_M60A3_Patton: 6,
|
Armor.ATGM_M1134_Stryker: 18,
|
||||||
Armor.MBT_M1A2_Abrams: 9,
|
Armor.MBT_M60A3_Patton: 24,
|
||||||
|
Armor.MBT_M1A2_Abrams: 35,
|
||||||
Armor.ATGM_M1134_Stryker: 6,
|
|
||||||
Armor.APC_BTR_80: 6,
|
|
||||||
|
|
||||||
Unarmed.Transport_UAZ_469: 3,
|
Unarmed.Transport_UAZ_469: 3,
|
||||||
Unarmed.Transport_Ural_375: 3,
|
Unarmed.Transport_Ural_375: 3,
|
||||||
@@ -109,9 +111,10 @@ PRICES = {
|
|||||||
# ship
|
# ship
|
||||||
CV_1143_5_Admiral_Kuznetsov: 100,
|
CV_1143_5_Admiral_Kuznetsov: 100,
|
||||||
CVN_74_John_C__Stennis: 100,
|
CVN_74_John_C__Stennis: 100,
|
||||||
|
LHA_1_Tarawa: 50,
|
||||||
|
|
||||||
LHA_1_Tarawa: 30,
|
|
||||||
Bulk_cargo_ship_Yakushev: 10,
|
Bulk_cargo_ship_Yakushev: 10,
|
||||||
|
Armed_speedboat: 10,
|
||||||
Dry_cargo_ship_Ivanov: 10,
|
Dry_cargo_ship_Ivanov: 10,
|
||||||
Tanker_Elnya_160: 10,
|
Tanker_Elnya_160: 10,
|
||||||
}
|
}
|
||||||
@@ -134,7 +137,6 @@ Following tasks are present:
|
|||||||
"""
|
"""
|
||||||
UNIT_BY_TASK = {
|
UNIT_BY_TASK = {
|
||||||
CAP: [
|
CAP: [
|
||||||
C_101CC,
|
|
||||||
F_5E_3,
|
F_5E_3,
|
||||||
MiG_23MLD,
|
MiG_23MLD,
|
||||||
Su_27,
|
Su_27,
|
||||||
@@ -144,6 +146,7 @@ UNIT_BY_TASK = {
|
|||||||
MiG_29S,
|
MiG_29S,
|
||||||
FA_18C_hornet,
|
FA_18C_hornet,
|
||||||
F_15C,
|
F_15C,
|
||||||
|
F_14B,
|
||||||
M_2000C,
|
M_2000C,
|
||||||
],
|
],
|
||||||
CAS: [
|
CAS: [
|
||||||
@@ -157,6 +160,7 @@ UNIT_BY_TASK = {
|
|||||||
Su_25T,
|
Su_25T,
|
||||||
Su_34,
|
Su_34,
|
||||||
Ka_50,
|
Ka_50,
|
||||||
|
SA342M,
|
||||||
],
|
],
|
||||||
|
|
||||||
Transport: [
|
Transport: [
|
||||||
@@ -165,18 +169,35 @@ UNIT_BY_TASK = {
|
|||||||
An_30M,
|
An_30M,
|
||||||
Yak_40,
|
Yak_40,
|
||||||
|
|
||||||
S_3B_Tanker,
|
|
||||||
C_130,
|
C_130,
|
||||||
],
|
],
|
||||||
|
|
||||||
Refueling: [
|
Refueling: [
|
||||||
IL_78M,
|
IL_78M,
|
||||||
KC_135,
|
KC_135,
|
||||||
|
S_3B_Tanker,
|
||||||
],
|
],
|
||||||
|
|
||||||
AWACS: [E_3A, A_50, ],
|
AWACS: [E_3A, A_50, ],
|
||||||
|
|
||||||
PinpointStrike: [Armor.MBT_T_90, Armor.MBT_T_80U, Armor.MBT_T_55, Armor.MBT_M1A2_Abrams, Armor.MBT_M60A3_Patton, Armor.ATGM_M1134_Stryker, Armor.APC_BTR_80, ],
|
PinpointStrike: [
|
||||||
|
Armor.APC_BTR_80,
|
||||||
|
Armor.APC_BTR_80,
|
||||||
|
Armor.APC_BTR_80,
|
||||||
|
Armor.MBT_T_55,
|
||||||
|
Armor.MBT_T_55,
|
||||||
|
Armor.MBT_T_55,
|
||||||
|
Armor.MBT_T_80U,
|
||||||
|
Armor.MBT_T_80U,
|
||||||
|
Armor.MBT_T_90,
|
||||||
|
|
||||||
|
Armor.ATGM_M1134_Stryker,
|
||||||
|
Armor.ATGM_M1134_Stryker,
|
||||||
|
Armor.MBT_M60A3_Patton,
|
||||||
|
Armor.MBT_M60A3_Patton,
|
||||||
|
Armor.MBT_M60A3_Patton,
|
||||||
|
Armor.MBT_M1A2_Abrams,
|
||||||
|
],
|
||||||
AirDefence: [
|
AirDefence: [
|
||||||
# those are listed multiple times here to balance prioritization more into lower tier AAs
|
# those are listed multiple times here to balance prioritization more into lower tier AAs
|
||||||
AirDefence.AAA_Vulcan_M163,
|
AirDefence.AAA_Vulcan_M163,
|
||||||
@@ -196,8 +217,8 @@ UNIT_BY_TASK = {
|
|||||||
Nothing: [Infantry.Infantry_M4, Infantry.Soldier_AK, ],
|
Nothing: [Infantry.Infantry_M4, Infantry.Soldier_AK, ],
|
||||||
Embarking: [UH_1H, Mi_8MT, ],
|
Embarking: [UH_1H, Mi_8MT, ],
|
||||||
|
|
||||||
Carriage: [CVN_74_John_C__Stennis, CV_1143_5_Admiral_Kuznetsov, ],
|
Carriage: [CVN_74_John_C__Stennis, LHA_1_Tarawa, CV_1143_5_Admiral_Kuznetsov, ],
|
||||||
CargoTransportation: [Dry_cargo_ship_Ivanov, Bulk_cargo_ship_Yakushev, Tanker_Elnya_160, LHA_1_Tarawa],
|
CargoTransportation: [Dry_cargo_ship_Ivanov, Bulk_cargo_ship_Yakushev, Tanker_Elnya_160, Armed_speedboat, ],
|
||||||
}
|
}
|
||||||
|
|
||||||
"""
|
"""
|
||||||
@@ -214,7 +235,6 @@ SAM_BAN = [
|
|||||||
Units that will always be spawned in the air
|
Units that will always be spawned in the air
|
||||||
"""
|
"""
|
||||||
TAKEOFF_BAN = [
|
TAKEOFF_BAN = [
|
||||||
AV8BNA, # AI takeoff currently bugged attempting VTOL with no regards for the total weight
|
|
||||||
]
|
]
|
||||||
|
|
||||||
"""
|
"""
|
||||||
@@ -238,7 +258,6 @@ Be advised that putting unit to the country that have not access to the unit in
|
|||||||
"""
|
"""
|
||||||
UNIT_BY_COUNTRY = {
|
UNIT_BY_COUNTRY = {
|
||||||
"Russia": [
|
"Russia": [
|
||||||
C_101CC,
|
|
||||||
AJS37,
|
AJS37,
|
||||||
MiG_23MLD,
|
MiG_23MLD,
|
||||||
F_5E_3,
|
F_5E_3,
|
||||||
@@ -263,6 +282,7 @@ UNIT_BY_COUNTRY = {
|
|||||||
A_50,
|
A_50,
|
||||||
|
|
||||||
Ka_50,
|
Ka_50,
|
||||||
|
SA342M,
|
||||||
UH_1H,
|
UH_1H,
|
||||||
Mi_8MT,
|
Mi_8MT,
|
||||||
|
|
||||||
@@ -286,6 +306,7 @@ UNIT_BY_COUNTRY = {
|
|||||||
"USA": [
|
"USA": [
|
||||||
F_5E_3,
|
F_5E_3,
|
||||||
F_15C,
|
F_15C,
|
||||||
|
F_14B,
|
||||||
FA_18C_hornet,
|
FA_18C_hornet,
|
||||||
AJS37,
|
AJS37,
|
||||||
M_2000C,
|
M_2000C,
|
||||||
@@ -302,6 +323,7 @@ UNIT_BY_COUNTRY = {
|
|||||||
E_3A,
|
E_3A,
|
||||||
|
|
||||||
Ka_50,
|
Ka_50,
|
||||||
|
SA342M,
|
||||||
UH_1H,
|
UH_1H,
|
||||||
Mi_8MT,
|
Mi_8MT,
|
||||||
|
|
||||||
@@ -316,9 +338,20 @@ UNIT_BY_COUNTRY = {
|
|||||||
|
|
||||||
CVN_74_John_C__Stennis,
|
CVN_74_John_C__Stennis,
|
||||||
LHA_1_Tarawa,
|
LHA_1_Tarawa,
|
||||||
|
Armed_speedboat,
|
||||||
],
|
],
|
||||||
}
|
}
|
||||||
|
|
||||||
|
CARRIER_TYPE_BY_PLANE = {
|
||||||
|
FA_18C_hornet: CVN_74_John_C__Stennis,
|
||||||
|
F_14B: CVN_74_John_C__Stennis,
|
||||||
|
Ka_50: LHA_1_Tarawa,
|
||||||
|
SA342M: LHA_1_Tarawa,
|
||||||
|
UH_1H: LHA_1_Tarawa,
|
||||||
|
Mi_8MT: LHA_1_Tarawa,
|
||||||
|
AV8BNA: LHA_1_Tarawa,
|
||||||
|
}
|
||||||
|
|
||||||
"""
|
"""
|
||||||
Aircraft payload overrides. Usually default loadout for the task is loaded during the mission generation.
|
Aircraft payload overrides. Usually default loadout for the task is loaded during the mission generation.
|
||||||
Syntax goes as follows:
|
Syntax goes as follows:
|
||||||
@@ -338,10 +371,25 @@ Payload will be used for operation of following type, "*" category will be used
|
|||||||
PLANE_PAYLOAD_OVERRIDES = {
|
PLANE_PAYLOAD_OVERRIDES = {
|
||||||
FA_18C_hornet: {
|
FA_18C_hornet: {
|
||||||
CAP: "AIM-120*4,AIM-9*2,AIM-7*2,Fuel",
|
CAP: "AIM-120*4,AIM-9*2,AIM-7*2,Fuel",
|
||||||
|
Escort: "AIM-120*4,AIM-9*2,AIM-7*2,Fuel",
|
||||||
|
PinpointStrike: "MK-82*8,AIM-9*2,AIM-7,FLIR Pod,Fuel",
|
||||||
|
AntishipStrike: "MK-82*8,AIM-9*2,AIM-7,FLIR Pod,Fuel",
|
||||||
|
},
|
||||||
|
|
||||||
|
F_14B: {
|
||||||
|
CAP: "AIM-54A-MK47*4, AIM-7M*2, AIM-9M*2, XT*2",
|
||||||
|
Escort: "AIM-54A-MK47*4, AIM-7M*2, AIM-9M*2, XT*2",
|
||||||
|
CAS: "AIM-54A-MK60*1, AIM-7M*1, AIM-9M*2, XT*2, Mk-82*2, LANTIRN",
|
||||||
|
GroundAttack: "AIM54, AIM-9M*2, XT*2, GBU-12*4, LANTIRN",
|
||||||
|
},
|
||||||
|
|
||||||
|
Su_25T: {
|
||||||
|
CAS: "APU-8 Vikhr-M*2,Kh-25ML,R-73*2,SPPU-22*2,Mercury LLTV Pod,MPS-410",
|
||||||
},
|
},
|
||||||
|
|
||||||
Su_33: {
|
Su_33: {
|
||||||
CAP: "R-73*4,R-27R*2,R-27ER*6",
|
CAP: "R-73*4,R-27R*2,R-27ER*6",
|
||||||
|
Escort: "R-73*4,R-27R*2,R-27ER*6",
|
||||||
},
|
},
|
||||||
|
|
||||||
AJS37: {
|
AJS37: {
|
||||||
@@ -358,15 +406,18 @@ PLANE_PAYLOAD_OVERRIDES = {
|
|||||||
},
|
},
|
||||||
|
|
||||||
Ka_50: {
|
Ka_50: {
|
||||||
"*": "12x9A4172, 40xS-8",
|
CAS: "12x9A4172, 40xS-8",
|
||||||
|
GroundAttack: "12x9A4172, 40xS-8",
|
||||||
},
|
},
|
||||||
|
|
||||||
M_2000C: {
|
M_2000C: {
|
||||||
CAP: "Combat Air Patrol",
|
CAP: "Combat Air Patrol",
|
||||||
|
Escort: "Combat Air Patrol",
|
||||||
|
GroundAttack: "MK-82S Heavy Strike",
|
||||||
},
|
},
|
||||||
|
|
||||||
MiG_21Bis: {
|
MiG_21Bis: {
|
||||||
"*": "Patrol, medium range",
|
CAP: "Patrol, medium range",
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -392,7 +443,11 @@ HeliDict = typing.Dict[HelicopterType, int]
|
|||||||
ArmorDict = typing.Dict[VehicleType, int]
|
ArmorDict = typing.Dict[VehicleType, int]
|
||||||
ShipDict = typing.Dict[ShipType, int]
|
ShipDict = typing.Dict[ShipType, int]
|
||||||
AirDefenseDict = typing.Dict[AirDefence, int]
|
AirDefenseDict = typing.Dict[AirDefence, int]
|
||||||
StartingPosition = typing.Optional[typing.Union[ShipGroup, Airport, Point]]
|
|
||||||
|
AssignedUnitsDict = typing.Dict[typing.Type[UnitType], typing.Tuple[int, int]]
|
||||||
|
TaskForceDict = typing.Dict[typing.Type[Task], AssignedUnitsDict]
|
||||||
|
|
||||||
|
StartingPosition = typing.Optional[typing.Union[ShipGroup, StaticGroup, Airport, Point]]
|
||||||
|
|
||||||
|
|
||||||
def unit_task(unit: UnitType) -> Task:
|
def unit_task(unit: UnitType) -> Task:
|
||||||
@@ -422,6 +477,15 @@ def unit_type_from_name(name: str) -> UnitType:
|
|||||||
return None
|
return None
|
||||||
|
|
||||||
|
|
||||||
|
def unit_type_of(unit: Unit) -> UnitType:
|
||||||
|
if isinstance(unit, Vehicle):
|
||||||
|
return vehicle_map[unit.type]
|
||||||
|
elif isinstance(unit, Ship):
|
||||||
|
return ship_map[unit.type]
|
||||||
|
else:
|
||||||
|
return unit.unit_type
|
||||||
|
|
||||||
|
|
||||||
def task_name(task) -> str:
|
def task_name(task) -> str:
|
||||||
if task == AirDefence:
|
if task == AirDefence:
|
||||||
return "AirDefence"
|
return "AirDefence"
|
||||||
@@ -433,6 +497,7 @@ def task_name(task) -> str:
|
|||||||
|
|
||||||
def choose_units(for_task: Task, factor: float, count: int, country: str) -> typing.Collection[UnitType]:
|
def choose_units(for_task: Task, factor: float, count: int, country: str) -> typing.Collection[UnitType]:
|
||||||
suitable_unittypes = find_unittype(for_task, country)
|
suitable_unittypes = find_unittype(for_task, country)
|
||||||
|
suitable_unittypes = [x for x in suitable_unittypes if x not in helicopter_map.values()]
|
||||||
suitable_unittypes.sort(key=lambda x: PRICES[x])
|
suitable_unittypes.sort(key=lambda x: PRICES[x])
|
||||||
|
|
||||||
idx = int(len(suitable_unittypes) * factor)
|
idx = int(len(suitable_unittypes) * factor)
|
||||||
@@ -447,6 +512,14 @@ def unitdict_append(unit_dict: UnitsDict, unit_type: UnitType, count: int):
|
|||||||
unit_dict[unit_type] = unit_dict.get(unit_type, 0) + 1
|
unit_dict[unit_type] = unit_dict.get(unit_type, 0) + 1
|
||||||
|
|
||||||
|
|
||||||
|
def unitdict_merge(a: UnitsDict, b: UnitsDict) -> UnitsDict:
|
||||||
|
b = b.copy()
|
||||||
|
for k, v in a.items():
|
||||||
|
b[k] = b.get(k, 0) + v
|
||||||
|
|
||||||
|
return b
|
||||||
|
|
||||||
|
|
||||||
def unitdict_split(unit_dict: UnitsDict, count: int):
|
def unitdict_split(unit_dict: UnitsDict, count: int):
|
||||||
buffer_dict = {}
|
buffer_dict = {}
|
||||||
for unit_type, unit_count in unit_dict.items():
|
for unit_type, unit_count in unit_dict.items():
|
||||||
@@ -471,6 +544,39 @@ def unitdict_restrict_count(unit_dict: UnitsDict, total_count: int) -> UnitsDict
|
|||||||
return {}
|
return {}
|
||||||
|
|
||||||
|
|
||||||
|
def assigned_units_split(fd: AssignedUnitsDict) -> typing.Tuple[PlaneDict, PlaneDict]:
|
||||||
|
return {k: v1 for k, (v1, v2) in fd.items()}, {k: v2 for k, (v1, v2) in fd.items()},
|
||||||
|
|
||||||
|
|
||||||
|
def assigned_units_from(d: PlaneDict) -> AssignedUnitsDict:
|
||||||
|
return {k: (v, 0) for k, v in d.items()}
|
||||||
|
|
||||||
|
|
||||||
|
def assignedunits_split_to_count(dict: AssignedUnitsDict, count: int):
|
||||||
|
buffer_dict = {}
|
||||||
|
for unit_type, (unit_count, client_count) in dict.items():
|
||||||
|
for _ in range(unit_count):
|
||||||
|
new_count, new_client_count = buffer_dict.get(unit_type, (0, 0))
|
||||||
|
|
||||||
|
new_count += 1
|
||||||
|
|
||||||
|
if client_count > 0:
|
||||||
|
new_client_count += 1
|
||||||
|
client_count -= 1
|
||||||
|
|
||||||
|
buffer_dict[unit_type] = new_count, new_client_count
|
||||||
|
if new_count >= count:
|
||||||
|
yield buffer_dict
|
||||||
|
buffer_dict = {}
|
||||||
|
|
||||||
|
if len(buffer_dict):
|
||||||
|
yield buffer_dict
|
||||||
|
|
||||||
|
|
||||||
|
def unitdict_from(fd: AssignedUnitsDict) -> Dict:
|
||||||
|
return {k: v1 for k, (v1, v2) in fd.items()}
|
||||||
|
|
||||||
|
|
||||||
def _validate_db():
|
def _validate_db():
|
||||||
# check unit by task uniquity
|
# check unit by task uniquity
|
||||||
total_set = set()
|
total_set = set()
|
||||||
|
|||||||
@@ -5,5 +5,6 @@ from .intercept import *
|
|||||||
from .baseattack import *
|
from .baseattack import *
|
||||||
from .navalintercept import *
|
from .navalintercept import *
|
||||||
from .insurgentattack import *
|
from .insurgentattack import *
|
||||||
|
from .convoystrike import *
|
||||||
from .infantrytransport import *
|
from .infantrytransport import *
|
||||||
from .strike import *
|
from .strike import *
|
||||||
|
|||||||
@@ -1,16 +1,7 @@
|
|||||||
import typing
|
|
||||||
import math
|
|
||||||
import random
|
|
||||||
|
|
||||||
from dcs.task import *
|
|
||||||
from dcs.unittype import UnitType
|
|
||||||
|
|
||||||
from game import db
|
|
||||||
from game.operation.baseattack import BaseAttackOperation
|
from game.operation.baseattack import BaseAttackOperation
|
||||||
from userdata.debriefing import Debriefing
|
|
||||||
|
|
||||||
from .event import *
|
from .event import *
|
||||||
from ..operation.operation import flight_dict_from
|
from game.db import assigned_units_from
|
||||||
|
|
||||||
|
|
||||||
class BaseAttackEvent(Event):
|
class BaseAttackEvent(Event):
|
||||||
@@ -34,10 +25,10 @@ class BaseAttackEvent(Event):
|
|||||||
return "Ground attack"
|
return "Ground attack"
|
||||||
|
|
||||||
def is_successfull(self, debriefing: Debriefing):
|
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_attackers = sum([v for k, v in debriefing.alive_units.get(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])
|
alive_defenders = sum([v for k, v in debriefing.alive_units.get(self.defender_name, {}).items() if db.unit_task(k) == PinpointStrike])
|
||||||
attackers_success = alive_attackers >= alive_defenders
|
attackers_success = alive_attackers >= alive_defenders
|
||||||
if self.from_cp.captured:
|
if self.departure_cp.captured:
|
||||||
return attackers_success
|
return attackers_success
|
||||||
else:
|
else:
|
||||||
return not attackers_success
|
return not attackers_success
|
||||||
@@ -45,14 +36,14 @@ class BaseAttackEvent(Event):
|
|||||||
def commit(self, debriefing: Debriefing):
|
def commit(self, debriefing: Debriefing):
|
||||||
super(BaseAttackEvent, self).commit(debriefing)
|
super(BaseAttackEvent, self).commit(debriefing)
|
||||||
if self.is_successfull(debriefing):
|
if self.is_successfull(debriefing):
|
||||||
if self.from_cp.captured:
|
if self.departure_cp.captured:
|
||||||
self.to_cp.captured = True
|
self.to_cp.captured = True
|
||||||
self.to_cp.ground_objects = []
|
self.to_cp.ground_objects = []
|
||||||
self.to_cp.base.filter_units(db.UNIT_BY_COUNTRY[self.attacker_name])
|
self.to_cp.base.filter_units(db.UNIT_BY_COUNTRY[self.attacker_name])
|
||||||
|
|
||||||
self.to_cp.base.affect_strength(+self.STRENGTH_RECOVERY)
|
self.to_cp.base.affect_strength(+self.STRENGTH_RECOVERY)
|
||||||
else:
|
else:
|
||||||
if not self.from_cp.captured:
|
if not self.departure_cp.captured:
|
||||||
self.to_cp.captured = False
|
self.to_cp.captured = False
|
||||||
self.to_cp.base.affect_strength(+self.STRENGTH_RECOVERY)
|
self.to_cp.base.affect_strength(+self.STRENGTH_RECOVERY)
|
||||||
|
|
||||||
@@ -60,21 +51,22 @@ class BaseAttackEvent(Event):
|
|||||||
if not self.is_player_attacking and self.to_cp.captured:
|
if not self.is_player_attacking and self.to_cp.captured:
|
||||||
self.to_cp.captured = False
|
self.to_cp.captured = False
|
||||||
|
|
||||||
def player_defending(self, flights: ScrambledFlightsDict):
|
def player_defending(self, flights: db.TaskForceDict):
|
||||||
assert CAP in flights and len(flights) == 1, "Invalid scrambled flights"
|
assert CAP in flights and len(flights) == 1, "Invalid scrambled flights"
|
||||||
|
|
||||||
cas = self.from_cp.base.scramble_cas(self.game.settings.multiplier)
|
cas = self.departure_cp.base.scramble_cas(self.game.settings.multiplier)
|
||||||
escort = self.from_cp.base.scramble_sweep(self.game.settings.multiplier)
|
escort = self.departure_cp.base.scramble_sweep(self.game.settings.multiplier)
|
||||||
attackers = self.from_cp.base.armor
|
attackers = self.departure_cp.base.armor
|
||||||
|
|
||||||
op = BaseAttackOperation(game=self.game,
|
op = BaseAttackOperation(game=self.game,
|
||||||
attacker_name=self.attacker_name,
|
attacker_name=self.attacker_name,
|
||||||
defender_name=self.defender_name,
|
defender_name=self.defender_name,
|
||||||
from_cp=self.from_cp,
|
from_cp=self.from_cp,
|
||||||
|
departure_cp=self.departure_cp,
|
||||||
to_cp=self.to_cp)
|
to_cp=self.to_cp)
|
||||||
|
|
||||||
op.setup(cas=flight_dict_from(cas),
|
op.setup(cas=assigned_units_from(cas),
|
||||||
escort=flight_dict_from(escort),
|
escort=assigned_units_from(escort),
|
||||||
intercept=flights[CAP],
|
intercept=flights[CAP],
|
||||||
attack=attackers,
|
attack=attackers,
|
||||||
defense=self.to_cp.base.armor,
|
defense=self.to_cp.base.armor,
|
||||||
@@ -82,13 +74,14 @@ class BaseAttackEvent(Event):
|
|||||||
|
|
||||||
self.operation = op
|
self.operation = op
|
||||||
|
|
||||||
def player_attacking(self, flights: ScrambledFlightsDict):
|
def player_attacking(self, flights: db.TaskForceDict):
|
||||||
assert CAP in flights and CAS in flights and PinpointStrike in flights and len(flights) == 3, "Invalid flights"
|
assert CAP in flights and CAS in flights and PinpointStrike in flights and len(flights) == 3, "Invalid flights"
|
||||||
|
|
||||||
op = BaseAttackOperation(game=self.game,
|
op = BaseAttackOperation(game=self.game,
|
||||||
attacker_name=self.attacker_name,
|
attacker_name=self.attacker_name,
|
||||||
defender_name=self.defender_name,
|
defender_name=self.defender_name,
|
||||||
from_cp=self.from_cp,
|
from_cp=self.from_cp,
|
||||||
|
departure_cp=self.departure_cp,
|
||||||
to_cp=self.to_cp)
|
to_cp=self.to_cp)
|
||||||
|
|
||||||
defenders = self.to_cp.base.scramble_sweep(self.game.settings.multiplier)
|
defenders = self.to_cp.base.scramble_sweep(self.game.settings.multiplier)
|
||||||
@@ -96,8 +89,8 @@ class BaseAttackEvent(Event):
|
|||||||
|
|
||||||
op.setup(cas=flights[CAS],
|
op.setup(cas=flights[CAS],
|
||||||
escort=flights[CAP],
|
escort=flights[CAP],
|
||||||
attack=flights[PinpointStrike],
|
attack=unitdict_from(flights[PinpointStrike]),
|
||||||
intercept=flight_dict_from(defenders),
|
intercept=assigned_units_from(defenders),
|
||||||
defense=self.to_cp.base.armor,
|
defense=self.to_cp.base.armor,
|
||||||
aa=self.to_cp.base.assemble_aa())
|
aa=self.to_cp.base.assemble_aa())
|
||||||
|
|
||||||
|
|||||||
83
game/event/convoystrike.py
Normal file
@@ -0,0 +1,83 @@
|
|||||||
|
import math
|
||||||
|
import random
|
||||||
|
|
||||||
|
from dcs.task import *
|
||||||
|
|
||||||
|
from game import *
|
||||||
|
from game.event import *
|
||||||
|
from game.event.frontlineattack import FrontlineAttackEvent
|
||||||
|
|
||||||
|
from .event import *
|
||||||
|
from game.operation.convoystrike import ConvoyStrikeOperation
|
||||||
|
|
||||||
|
TRANSPORT_COUNT = 4, 6
|
||||||
|
DEFENDERS_AMOUNT_FACTOR = 4
|
||||||
|
|
||||||
|
|
||||||
|
class ConvoyStrikeEvent(Event):
|
||||||
|
SUCCESS_FACTOR = 0.6
|
||||||
|
STRENGTH_INFLUENCE = 0.25
|
||||||
|
|
||||||
|
targets = None # type: db.ArmorDict
|
||||||
|
|
||||||
|
@property
|
||||||
|
def threat_description(self):
|
||||||
|
return ""
|
||||||
|
|
||||||
|
@property
|
||||||
|
def tasks(self):
|
||||||
|
return [CAS]
|
||||||
|
|
||||||
|
@property
|
||||||
|
def global_cp_available(self) -> bool:
|
||||||
|
return True
|
||||||
|
|
||||||
|
def flight_name(self, for_task: typing.Type[Task]) -> str:
|
||||||
|
if for_task == CAS:
|
||||||
|
return "Strike flight"
|
||||||
|
|
||||||
|
def __str__(self):
|
||||||
|
return "Convoy Strike"
|
||||||
|
|
||||||
|
def skip(self):
|
||||||
|
self.to_cp.base.affect_strength(-self.STRENGTH_INFLUENCE)
|
||||||
|
|
||||||
|
def commit(self, debriefing: Debriefing):
|
||||||
|
super(ConvoyStrikeEvent, self).commit(debriefing)
|
||||||
|
|
||||||
|
if self.from_cp.captured:
|
||||||
|
if self.is_successfull(debriefing):
|
||||||
|
self.to_cp.base.affect_strength(-self.STRENGTH_INFLUENCE)
|
||||||
|
else:
|
||||||
|
if self.is_successfull(debriefing):
|
||||||
|
self.from_cp.base.affect_strength(-self.STRENGTH_INFLUENCE)
|
||||||
|
|
||||||
|
def is_successfull(self, debriefing: Debriefing):
|
||||||
|
killed_units = sum([v for k, v in debriefing.destroyed_units.get(self.defender_name, {}).items() if db.unit_task(k) in [PinpointStrike, Reconnaissance]])
|
||||||
|
all_units = sum(self.targets.values())
|
||||||
|
attackers_success = (float(killed_units) / (all_units + 0.01)) > self.SUCCESS_FACTOR
|
||||||
|
if self.from_cp.captured:
|
||||||
|
return attackers_success
|
||||||
|
else:
|
||||||
|
return not attackers_success
|
||||||
|
|
||||||
|
def player_attacking(self, flights: db.TaskForceDict):
|
||||||
|
assert CAS in flights and len(flights) == 1, "Invalid flights"
|
||||||
|
|
||||||
|
convoy_unittype = db.find_unittype(Reconnaissance, self.defender_name)[0]
|
||||||
|
defense_unittype = db.find_unittype(PinpointStrike, self.defender_name)[0]
|
||||||
|
|
||||||
|
defenders_count = int(math.ceil(self.from_cp.base.strength * self.from_cp.importance * DEFENDERS_AMOUNT_FACTOR))
|
||||||
|
self.targets = {convoy_unittype: random.randrange(*TRANSPORT_COUNT),
|
||||||
|
defense_unittype: defenders_count, }
|
||||||
|
|
||||||
|
op = ConvoyStrikeOperation(game=self.game,
|
||||||
|
attacker_name=self.attacker_name,
|
||||||
|
defender_name=self.defender_name,
|
||||||
|
from_cp=self.from_cp,
|
||||||
|
departure_cp=self.departure_cp,
|
||||||
|
to_cp=self.to_cp)
|
||||||
|
op.setup(target=self.targets,
|
||||||
|
strikegroup=flights[CAS])
|
||||||
|
|
||||||
|
self.operation = op
|
||||||
@@ -2,38 +2,48 @@ import typing
|
|||||||
import logging
|
import logging
|
||||||
|
|
||||||
from dcs.unittype import UnitType
|
from dcs.unittype import UnitType
|
||||||
from dcs.task import Task
|
from dcs.task import *
|
||||||
|
from dcs.vehicles import AirDefence
|
||||||
from dcs.unittype import UnitType
|
from dcs.unittype import UnitType
|
||||||
|
|
||||||
from game import *
|
from game import *
|
||||||
from theater import *
|
from theater import *
|
||||||
from gen.environmentgen import EnvironmentSettings
|
from gen.environmentgen import EnvironmentSettings
|
||||||
from game.operation.operation import flight_dict_from, dict_from_flight
|
from gen.conflictgen import Conflict
|
||||||
|
from game.db import assigned_units_from, unitdict_from
|
||||||
|
|
||||||
from userdata.debriefing import Debriefing
|
from userdata.debriefing import Debriefing
|
||||||
from userdata import persistency
|
from userdata import persistency
|
||||||
|
|
||||||
DIFFICULTY_LOG_BASE = 1.1
|
DIFFICULTY_LOG_BASE = 1.1
|
||||||
|
EVENT_DEPARTURE_MAX_DISTANCE = 340000
|
||||||
ScrambledFlightsDict = typing.Dict[typing.Type[Task], typing.Dict[typing.Type[UnitType], typing.Tuple[int, int]]]
|
|
||||||
|
|
||||||
|
|
||||||
class Event:
|
class Event:
|
||||||
silent = False
|
silent = False
|
||||||
informational = False
|
informational = False
|
||||||
is_awacs_enabled = False
|
is_awacs_enabled = False
|
||||||
|
ca_slots = 0
|
||||||
|
|
||||||
|
game = None # type: Game
|
||||||
|
location = None # type: Point
|
||||||
|
from_cp = None # type: ControlPoint
|
||||||
|
departure_cp = None # type: ControlPoint
|
||||||
|
to_cp = None # type: ControlPoint
|
||||||
|
|
||||||
operation = None # type: Operation
|
operation = None # type: Operation
|
||||||
difficulty = 1 # type: int
|
difficulty = 1 # type: int
|
||||||
game = None # type: Game
|
|
||||||
environment_settings = None # type: EnvironmentSettings
|
environment_settings = None # type: EnvironmentSettings
|
||||||
BONUS_BASE = 5
|
BONUS_BASE = 5
|
||||||
|
|
||||||
def __init__(self, attacker_name: str, defender_name: str, from_cp: ControlPoint, to_cp: ControlPoint, game):
|
def __init__(self, game, from_cp: ControlPoint, target_cp: ControlPoint, location: Point, attacker_name: str, defender_name: str):
|
||||||
|
self.game = game
|
||||||
|
self.departure_cp = None
|
||||||
|
self.from_cp = from_cp
|
||||||
|
self.to_cp = target_cp
|
||||||
|
self.location = location
|
||||||
self.attacker_name = attacker_name
|
self.attacker_name = attacker_name
|
||||||
self.defender_name = defender_name
|
self.defender_name = defender_name
|
||||||
self.to_cp = to_cp
|
|
||||||
self.from_cp = from_cp
|
|
||||||
self.game = game
|
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def is_player_attacking(self) -> bool:
|
def is_player_attacking(self) -> bool:
|
||||||
@@ -44,7 +54,7 @@ class Event:
|
|||||||
if self.attacker_name == self.game.player:
|
if self.attacker_name == self.game.player:
|
||||||
return self.to_cp
|
return self.to_cp
|
||||||
else:
|
else:
|
||||||
return self.from_cp
|
return self.departure_cp
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def threat_description(self) -> str:
|
def threat_description(self) -> str:
|
||||||
@@ -61,24 +71,51 @@ class Event:
|
|||||||
def ai_banned_tasks(self) -> typing.Collection[typing.Type[Task]]:
|
def ai_banned_tasks(self) -> typing.Collection[typing.Type[Task]]:
|
||||||
return []
|
return []
|
||||||
|
|
||||||
|
@property
|
||||||
|
def player_banned_tasks(self) -> typing.Collection[typing.Type[Task]]:
|
||||||
|
return []
|
||||||
|
|
||||||
|
@property
|
||||||
|
def global_cp_available(self) -> bool:
|
||||||
|
return False
|
||||||
|
|
||||||
|
def is_departure_available_from(self, cp: ControlPoint) -> bool:
|
||||||
|
if not cp.captured:
|
||||||
|
return False
|
||||||
|
|
||||||
|
if self.location.distance_to_point(cp.position) > EVENT_DEPARTURE_MAX_DISTANCE:
|
||||||
|
return False
|
||||||
|
|
||||||
|
if cp.is_global and not self.global_cp_available:
|
||||||
|
return False
|
||||||
|
|
||||||
|
return True
|
||||||
|
|
||||||
def bonus(self) -> int:
|
def bonus(self) -> int:
|
||||||
return int(math.log(self.to_cp.importance + 1, DIFFICULTY_LOG_BASE) * self.BONUS_BASE)
|
return int(math.log(self.to_cp.importance + 1, DIFFICULTY_LOG_BASE) * self.BONUS_BASE)
|
||||||
|
|
||||||
def is_successfull(self, debriefing: Debriefing) -> bool:
|
def is_successfull(self, debriefing: Debriefing) -> bool:
|
||||||
return self.operation.is_successfull(debriefing)
|
return self.operation.is_successfull(debriefing)
|
||||||
|
|
||||||
def player_attacking(self, flights: ScrambledFlightsDict):
|
def player_attacking(self, cp: ControlPoint, flights: db.TaskForceDict):
|
||||||
assert False
|
if self.is_player_attacking:
|
||||||
|
self.departure_cp = cp
|
||||||
|
else:
|
||||||
|
self.to_cp = cp
|
||||||
|
|
||||||
def player_defending(self, flights: ScrambledFlightsDict):
|
def player_defending(self, cp: ControlPoint, flights: db.TaskForceDict):
|
||||||
assert False
|
if self.is_player_attacking:
|
||||||
|
self.departure_cp = cp
|
||||||
|
else:
|
||||||
|
self.to_cp = cp
|
||||||
|
|
||||||
def generate(self):
|
def generate(self):
|
||||||
self.operation.is_awacs_enabled = self.is_awacs_enabled
|
self.operation.is_awacs_enabled = self.is_awacs_enabled
|
||||||
|
self.operation.ca_slots = self.ca_slots
|
||||||
|
|
||||||
self.operation.prepare(self.game.theater.terrain, is_quick=False)
|
self.operation.prepare(self.game.theater.terrain, is_quick=False)
|
||||||
self.operation.generate()
|
self.operation.generate()
|
||||||
self.operation.mission.save(persistency.mission_path_for("liberation_nextturn.miz"))
|
self.operation.current_mission.save(persistency.mission_path_for("liberation_nextturn.miz"))
|
||||||
self.environment_settings = self.operation.environment_settings
|
self.environment_settings = self.operation.environment_settings
|
||||||
|
|
||||||
def generate_quick(self):
|
def generate_quick(self):
|
||||||
@@ -87,12 +124,12 @@ class Event:
|
|||||||
|
|
||||||
self.operation.prepare(self.game.theater.terrain, is_quick=True)
|
self.operation.prepare(self.game.theater.terrain, is_quick=True)
|
||||||
self.operation.generate()
|
self.operation.generate()
|
||||||
self.operation.mission.save(persistency.mission_path_for("liberation_nextturn_quick.miz"))
|
self.operation.current_mission.save(persistency.mission_path_for("liberation_nextturn_quick.miz"))
|
||||||
|
|
||||||
def commit(self, debriefing: Debriefing):
|
def commit(self, debriefing: Debriefing):
|
||||||
for country, losses in debriefing.destroyed_units.items():
|
for country, losses in debriefing.destroyed_units.items():
|
||||||
if country == self.attacker_name:
|
if country == self.attacker_name:
|
||||||
cp = self.from_cp
|
cp = self.departure_cp
|
||||||
else:
|
else:
|
||||||
cp = self.to_cp
|
cp = self.to_cp
|
||||||
|
|
||||||
@@ -101,18 +138,16 @@ class Event:
|
|||||||
|
|
||||||
for object_identifier in debriefing.destroyed_objects:
|
for object_identifier in debriefing.destroyed_objects:
|
||||||
for cp in self.game.theater.controlpoints:
|
for cp in self.game.theater.controlpoints:
|
||||||
remove_ids = []
|
|
||||||
if not cp.ground_objects:
|
if not cp.ground_objects:
|
||||||
continue
|
continue
|
||||||
|
|
||||||
for i, ground_object in enumerate(cp.ground_objects):
|
for i, ground_object in enumerate(cp.ground_objects):
|
||||||
if ground_object.matches_string_identifier(object_identifier):
|
if ground_object.is_dead:
|
||||||
logging.info("cp {} removing ground object {}".format(cp, ground_object.string_identifier))
|
continue
|
||||||
remove_ids.append(i)
|
|
||||||
|
|
||||||
remove_ids.reverse()
|
if ground_object.matches_string_identifier(object_identifier):
|
||||||
for i in remove_ids:
|
logging.info("cp {} killing ground object {}".format(cp, ground_object.string_identifier))
|
||||||
del cp.ground_objects[i]
|
cp.ground_objects[i].is_dead = True
|
||||||
|
|
||||||
def skip(self):
|
def skip(self):
|
||||||
pass
|
pass
|
||||||
@@ -123,11 +158,12 @@ class UnitsDeliveryEvent(Event):
|
|||||||
units = None # type: typing.Dict[UnitType, int]
|
units = None # type: typing.Dict[UnitType, int]
|
||||||
|
|
||||||
def __init__(self, attacker_name: str, defender_name: str, from_cp: ControlPoint, to_cp: ControlPoint, game):
|
def __init__(self, attacker_name: str, defender_name: str, from_cp: ControlPoint, to_cp: ControlPoint, game):
|
||||||
super(UnitsDeliveryEvent, self).__init__(attacker_name=attacker_name,
|
super(UnitsDeliveryEvent, self).__init__(game=game,
|
||||||
defender_name=defender_name,
|
location=to_cp.position,
|
||||||
from_cp=from_cp,
|
from_cp=from_cp,
|
||||||
to_cp=to_cp,
|
target_cp=to_cp,
|
||||||
game=game)
|
attacker_name=attacker_name,
|
||||||
|
defender_name=defender_name)
|
||||||
|
|
||||||
self.units = {}
|
self.units = {}
|
||||||
|
|
||||||
|
|||||||
@@ -1,10 +1,3 @@
|
|||||||
import math
|
|
||||||
import random
|
|
||||||
|
|
||||||
from dcs.task import *
|
|
||||||
from dcs.vehicles import AirDefence
|
|
||||||
|
|
||||||
from game import *
|
|
||||||
from game.event import *
|
from game.event import *
|
||||||
from game.operation.frontlineattack import FrontlineAttackOperation
|
from game.operation.frontlineattack import FrontlineAttackOperation
|
||||||
from userdata.debriefing import Debriefing
|
from userdata.debriefing import Debriefing
|
||||||
@@ -15,11 +8,9 @@ class FrontlineAttackEvent(Event):
|
|||||||
TARGET_AMOUNT_FACTOR = 0.5
|
TARGET_AMOUNT_FACTOR = 0.5
|
||||||
ATTACKER_AMOUNT_FACTOR = 0.4
|
ATTACKER_AMOUNT_FACTOR = 0.4
|
||||||
ATTACKER_DEFENDER_FACTOR = 0.7
|
ATTACKER_DEFENDER_FACTOR = 0.7
|
||||||
STRENGTH_INFLUENCE = 0.2
|
STRENGTH_INFLUENCE = 0.3
|
||||||
SUCCESS_FACTOR = 1.5
|
SUCCESS_FACTOR = 1.5
|
||||||
|
|
||||||
defenders = None # type: db.ArmorDict
|
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def threat_description(self):
|
def threat_description(self):
|
||||||
return "{} vehicles".format(self.to_cp.base.assemble_count())
|
return "{} vehicles".format(self.to_cp.base.assemble_count())
|
||||||
@@ -27,9 +18,13 @@ class FrontlineAttackEvent(Event):
|
|||||||
@property
|
@property
|
||||||
def tasks(self) -> typing.Collection[typing.Type[Task]]:
|
def tasks(self) -> typing.Collection[typing.Type[Task]]:
|
||||||
if self.is_player_attacking:
|
if self.is_player_attacking:
|
||||||
return [CAS, PinpointStrike]
|
return [CAS, CAP]
|
||||||
else:
|
else:
|
||||||
return [CAP, PinpointStrike]
|
return [CAP]
|
||||||
|
|
||||||
|
@property
|
||||||
|
def global_cp_available(self) -> bool:
|
||||||
|
return True
|
||||||
|
|
||||||
def flight_name(self, for_task: typing.Type[Task]) -> str:
|
def flight_name(self, for_task: typing.Type[Task]) -> str:
|
||||||
if for_task == CAS:
|
if for_task == CAS:
|
||||||
@@ -43,8 +38,8 @@ class FrontlineAttackEvent(Event):
|
|||||||
return "Frontline attack"
|
return "Frontline attack"
|
||||||
|
|
||||||
def is_successfull(self, debriefing: Debriefing):
|
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_attackers = sum([v for k, v in debriefing.alive_units.get(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])
|
alive_defenders = sum([v for k, v in debriefing.alive_units.get(self.defender_name, {}).items() if db.unit_task(k) == PinpointStrike])
|
||||||
attackers_success = (float(alive_attackers) / (alive_defenders + 0.01)) > self.SUCCESS_FACTOR
|
attackers_success = (float(alive_attackers) / (alive_defenders + 0.01)) > self.SUCCESS_FACTOR
|
||||||
if self.from_cp.captured:
|
if self.from_cp.captured:
|
||||||
return attackers_success
|
return attackers_success
|
||||||
@@ -69,21 +64,47 @@ class FrontlineAttackEvent(Event):
|
|||||||
if self.to_cp.captured:
|
if self.to_cp.captured:
|
||||||
self.to_cp.base.affect_strength(-0.1)
|
self.to_cp.base.affect_strength(-0.1)
|
||||||
|
|
||||||
def player_attacking(self, flights: ScrambledFlightsDict):
|
def player_attacking(self, flights: db.TaskForceDict):
|
||||||
assert CAS in flights and PinpointStrike in flights and len(flights) == 2, "Invalid flights"
|
assert CAS in flights and CAP in flights and len(flights) == 2, "Invalid flights"
|
||||||
|
|
||||||
self.defenders = self.to_cp.base.assemble_attack()
|
|
||||||
|
|
||||||
op = FrontlineAttackOperation(game=self.game,
|
op = FrontlineAttackOperation(game=self.game,
|
||||||
attacker_name=self.attacker_name,
|
attacker_name=self.attacker_name,
|
||||||
defender_name=self.defender_name,
|
defender_name=self.defender_name,
|
||||||
from_cp=self.from_cp,
|
from_cp=self.from_cp,
|
||||||
|
departure_cp=self.departure_cp,
|
||||||
to_cp=self.to_cp)
|
to_cp=self.to_cp)
|
||||||
|
|
||||||
armor = dict_from_flight(flights[PinpointStrike])
|
defenders = self.to_cp.base.assemble_attack()
|
||||||
op.setup(target=self.defenders,
|
max_attackers = int(math.ceil(sum(defenders.values()) * self.ATTACKER_DEFENDER_FACTOR))
|
||||||
attackers=db.unitdict_restrict_count(armor, sum(self.defenders.values())),
|
attackers = db.unitdict_restrict_count(self.from_cp.base.assemble_attack(), max_attackers)
|
||||||
strikegroup=flights[CAS])
|
op.setup(defenders=defenders,
|
||||||
|
attackers=attackers,
|
||||||
|
strikegroup=flights[CAS],
|
||||||
|
escort=flights[CAP],
|
||||||
|
interceptors=assigned_units_from(self.to_cp.base.scramble_interceptors(1)))
|
||||||
|
|
||||||
|
self.operation = op
|
||||||
|
|
||||||
|
def player_defending(self, flights: db.TaskForceDict):
|
||||||
|
assert CAP in flights and len(flights) == 1, "Invalid flights"
|
||||||
|
|
||||||
|
op = FrontlineAttackOperation(game=self.game,
|
||||||
|
attacker_name=self.attacker_name,
|
||||||
|
defender_name=self.defender_name,
|
||||||
|
from_cp=self.from_cp,
|
||||||
|
departure_cp=self.departure_cp,
|
||||||
|
to_cp=self.to_cp)
|
||||||
|
|
||||||
|
defenders = self.to_cp.base.assemble_attack()
|
||||||
|
|
||||||
|
max_attackers = int(math.ceil(sum(defenders.values())))
|
||||||
|
attackers = db.unitdict_restrict_count(self.from_cp.base.assemble_attack(), max_attackers)
|
||||||
|
|
||||||
|
op.setup(defenders=defenders,
|
||||||
|
attackers=attackers,
|
||||||
|
strikegroup=assigned_units_from(self.from_cp.base.scramble_cas(1)),
|
||||||
|
escort=assigned_units_from(self.from_cp.base.scramble_sweep(1)),
|
||||||
|
interceptors=flights[CAP])
|
||||||
|
|
||||||
self.operation = op
|
self.operation = op
|
||||||
|
|
||||||
|
|||||||
@@ -1,10 +1,3 @@
|
|||||||
import math
|
|
||||||
import random
|
|
||||||
|
|
||||||
from dcs.task import *
|
|
||||||
from dcs.vehicles import AirDefence
|
|
||||||
|
|
||||||
from game import *
|
|
||||||
from game.event import *
|
from game.event import *
|
||||||
from game.operation.frontlinepatrol import FrontlinePatrolOperation
|
from game.operation.frontlinepatrol import FrontlinePatrolOperation
|
||||||
from userdata.debriefing import Debriefing
|
from userdata.debriefing import Debriefing
|
||||||
@@ -12,7 +5,7 @@ from userdata.debriefing import Debriefing
|
|||||||
|
|
||||||
class FrontlinePatrolEvent(Event):
|
class FrontlinePatrolEvent(Event):
|
||||||
ESCORT_FACTOR = 0.5
|
ESCORT_FACTOR = 0.5
|
||||||
STRENGTH_INFLUENCE = 0.2
|
STRENGTH_INFLUENCE = 0.3
|
||||||
SUCCESS_FACTOR = 0.8
|
SUCCESS_FACTOR = 0.8
|
||||||
|
|
||||||
cas = None # type: db.PlaneDict
|
cas = None # type: db.PlaneDict
|
||||||
@@ -24,7 +17,7 @@ class FrontlinePatrolEvent(Event):
|
|||||||
|
|
||||||
@property
|
@property
|
||||||
def tasks(self):
|
def tasks(self):
|
||||||
return [CAP, PinpointStrike]
|
return [CAP]
|
||||||
|
|
||||||
def flight_name(self, for_task: typing.Type[Task]) -> str:
|
def flight_name(self, for_task: typing.Type[Task]) -> str:
|
||||||
if for_task == CAP:
|
if for_task == CAP:
|
||||||
@@ -61,8 +54,8 @@ class FrontlinePatrolEvent(Event):
|
|||||||
def skip(self):
|
def skip(self):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
def player_attacking(self, flights: ScrambledFlightsDict):
|
def player_attacking(self, flights: db.TaskForceDict):
|
||||||
assert CAP in flights and PinpointStrike in flights and len(flights) == 2, "Invalid flights"
|
assert CAP in flights and len(flights) == 1, "Invalid flights"
|
||||||
|
|
||||||
self.cas = self.to_cp.base.scramble_cas(self.game.settings.multiplier)
|
self.cas = self.to_cp.base.scramble_cas(self.game.settings.multiplier)
|
||||||
self.escort = self.to_cp.base.scramble_sweep(self.game.settings.multiplier * self.ESCORT_FACTOR)
|
self.escort = self.to_cp.base.scramble_sweep(self.game.settings.multiplier * self.ESCORT_FACTOR)
|
||||||
@@ -71,13 +64,15 @@ class FrontlinePatrolEvent(Event):
|
|||||||
attacker_name=self.attacker_name,
|
attacker_name=self.attacker_name,
|
||||||
defender_name=self.defender_name,
|
defender_name=self.defender_name,
|
||||||
from_cp=self.from_cp,
|
from_cp=self.from_cp,
|
||||||
|
departure_cp=self.departure_cp,
|
||||||
to_cp=self.to_cp)
|
to_cp=self.to_cp)
|
||||||
|
|
||||||
defenders = self.to_cp.base.assemble_attack()
|
defenders = self.to_cp.base.assemble_attack()
|
||||||
op.setup(cas=flight_dict_from(self.cas),
|
attackers = db.unitdict_restrict_count(self.from_cp.base.assemble_attack(), sum(defenders.values()))
|
||||||
escort=flight_dict_from(self.escort),
|
op.setup(cas=assigned_units_from(self.cas),
|
||||||
|
escort=assigned_units_from(self.escort),
|
||||||
interceptors=flights[CAP],
|
interceptors=flights[CAP],
|
||||||
armor_attackers=db.unitdict_restrict_count(dict_from_flight(flights[PinpointStrike]), sum(defenders.values())),
|
armor_attackers=attackers,
|
||||||
armor_defenders=defenders)
|
armor_defenders=defenders)
|
||||||
|
|
||||||
self.operation = op
|
self.operation = op
|
||||||
|
|||||||
@@ -35,9 +35,9 @@ class InfantryTransportEvent(Event):
|
|||||||
if self.is_successfull(debriefing):
|
if self.is_successfull(debriefing):
|
||||||
self.to_cp.base.affect_strength(-self.STRENGTH_INFLUENCE)
|
self.to_cp.base.affect_strength(-self.STRENGTH_INFLUENCE)
|
||||||
else:
|
else:
|
||||||
self.from_cp.base.affect_strength(-self.STRENGTH_INFLUENCE)
|
self.departure_cp.base.affect_strength(-self.STRENGTH_INFLUENCE)
|
||||||
|
|
||||||
def player_attacking(self, flights: ScrambledFlightsDict):
|
def player_attacking(self, flights: db.TaskForceDict):
|
||||||
assert Embarking in flights and len(flights) == 1, "Invalid flights"
|
assert Embarking in flights and len(flights) == 1, "Invalid flights"
|
||||||
|
|
||||||
op = InfantryTransportOperation(
|
op = InfantryTransportOperation(
|
||||||
@@ -45,6 +45,7 @@ class InfantryTransportEvent(Event):
|
|||||||
attacker_name=self.attacker_name,
|
attacker_name=self.attacker_name,
|
||||||
defender_name=self.defender_name,
|
defender_name=self.defender_name,
|
||||||
from_cp=self.from_cp,
|
from_cp=self.from_cp,
|
||||||
|
departure_cp=self.departure_cp,
|
||||||
to_cp=self.to_cp
|
to_cp=self.to_cp
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|||||||
@@ -15,6 +15,7 @@ class InsurgentAttackEvent(Event):
|
|||||||
SUCCESS_FACTOR = 0.7
|
SUCCESS_FACTOR = 0.7
|
||||||
TARGET_VARIETY = 2
|
TARGET_VARIETY = 2
|
||||||
TARGET_AMOUNT_FACTOR = 0.5
|
TARGET_AMOUNT_FACTOR = 0.5
|
||||||
|
STRENGTH_INFLUENCE = 0.1
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def threat_description(self):
|
def threat_description(self):
|
||||||
@@ -31,6 +32,9 @@ class InsurgentAttackEvent(Event):
|
|||||||
def __str__(self):
|
def __str__(self):
|
||||||
return "Destroy insurgents"
|
return "Destroy insurgents"
|
||||||
|
|
||||||
|
def skip(self):
|
||||||
|
self.to_cp.base.affect_strength(-self.STRENGTH_INFLUENCE)
|
||||||
|
|
||||||
def is_successfull(self, debriefing: Debriefing):
|
def is_successfull(self, debriefing: Debriefing):
|
||||||
killed_units = sum([v for k, v in debriefing.destroyed_units[self.attacker_name].items() if db.unit_task(k) == PinpointStrike])
|
killed_units = sum([v for k, v in debriefing.destroyed_units[self.attacker_name].items() if db.unit_task(k) == PinpointStrike])
|
||||||
all_units = sum(self.targets.values())
|
all_units = sum(self.targets.values())
|
||||||
@@ -40,7 +44,7 @@ class InsurgentAttackEvent(Event):
|
|||||||
else:
|
else:
|
||||||
return not attackers_success
|
return not attackers_success
|
||||||
|
|
||||||
def player_defending(self, flights: ScrambledFlightsDict):
|
def player_defending(self, flights: db.TaskForceDict):
|
||||||
assert CAS in flights and len(flights) == 1, "Invalid flights"
|
assert CAS in flights and len(flights) == 1, "Invalid flights"
|
||||||
|
|
||||||
suitable_unittypes = db.find_unittype(Reconnaissance, self.attacker_name)
|
suitable_unittypes = db.find_unittype(Reconnaissance, self.attacker_name)
|
||||||
@@ -53,6 +57,7 @@ class InsurgentAttackEvent(Event):
|
|||||||
attacker_name=self.attacker_name,
|
attacker_name=self.attacker_name,
|
||||||
defender_name=self.defender_name,
|
defender_name=self.defender_name,
|
||||||
from_cp=self.from_cp,
|
from_cp=self.from_cp,
|
||||||
|
departure_cp=self.departure_cp,
|
||||||
to_cp=self.to_cp)
|
to_cp=self.to_cp)
|
||||||
op.setup(target=self.targets,
|
op.setup(target=self.targets,
|
||||||
strikegroup=flights[CAS])
|
strikegroup=flights[CAS])
|
||||||
|
|||||||
@@ -1,13 +1,4 @@
|
|||||||
import math
|
|
||||||
import random
|
|
||||||
|
|
||||||
from dcs.task import *
|
|
||||||
from dcs.vehicles import *
|
|
||||||
|
|
||||||
from game import db
|
|
||||||
from game.operation.intercept import InterceptOperation
|
from game.operation.intercept import InterceptOperation
|
||||||
from theater.conflicttheater import *
|
|
||||||
from userdata.debriefing import Debriefing
|
|
||||||
|
|
||||||
from .event import *
|
from .event import *
|
||||||
|
|
||||||
@@ -19,6 +10,11 @@ class InterceptEvent(Event):
|
|||||||
|
|
||||||
transport_unit = None # type: FlyingType
|
transport_unit = None # type: FlyingType
|
||||||
|
|
||||||
|
def __init__(self, game, from_cp: ControlPoint, target_cp: ControlPoint, location: Point, attacker_name: str,
|
||||||
|
defender_name: str):
|
||||||
|
super().__init__(game, from_cp, target_cp, location, attacker_name, defender_name)
|
||||||
|
self.location = Conflict.intercept_position(self.from_cp, self.to_cp)
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
return "Air Intercept"
|
return "Air Intercept"
|
||||||
|
|
||||||
@@ -34,15 +30,19 @@ class InterceptEvent(Event):
|
|||||||
return "Escort flight"
|
return "Escort flight"
|
||||||
|
|
||||||
def _enemy_scramble_multiplier(self) -> float:
|
def _enemy_scramble_multiplier(self) -> float:
|
||||||
is_global = self.from_cp.is_global or self.to_cp.is_global
|
is_global = self.departure_cp.is_global or self.to_cp.is_global
|
||||||
return self.game.settings.multiplier * is_global and 0.5 or 1
|
return self.game.settings.multiplier * is_global and 0.5 or 1
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def threat_description(self):
|
def threat_description(self):
|
||||||
return "{} aircraft".format(self.enemy_cp.base.scramble_count(self._enemy_scramble_multiplier(), CAP))
|
return "{} aircraft".format(self.enemy_cp.base.scramble_count(self._enemy_scramble_multiplier(), CAP))
|
||||||
|
|
||||||
|
@property
|
||||||
|
def global_cp_available(self) -> bool:
|
||||||
|
return True
|
||||||
|
|
||||||
def is_successfull(self, debriefing: Debriefing):
|
def is_successfull(self, debriefing: Debriefing):
|
||||||
units_destroyed = debriefing.destroyed_units[self.defender_name].get(self.transport_unit, 0)
|
units_destroyed = debriefing.destroyed_units.get(self.defender_name, {}).get(self.transport_unit, 0)
|
||||||
if self.from_cp.captured:
|
if self.from_cp.captured:
|
||||||
return units_destroyed > 0
|
return units_destroyed > 0
|
||||||
else:
|
else:
|
||||||
@@ -68,7 +68,7 @@ class InterceptEvent(Event):
|
|||||||
if self.to_cp.captured:
|
if self.to_cp.captured:
|
||||||
self.to_cp.base.affect_strength(-self.STRENGTH_INFLUENCE)
|
self.to_cp.base.affect_strength(-self.STRENGTH_INFLUENCE)
|
||||||
|
|
||||||
def player_attacking(self, flights: ScrambledFlightsDict):
|
def player_attacking(self, flights: db.TaskForceDict):
|
||||||
assert CAP in flights and len(flights) == 1, "Invalid flights"
|
assert CAP in flights and len(flights) == 1, "Invalid flights"
|
||||||
|
|
||||||
escort = self.to_cp.base.scramble_sweep(self._enemy_scramble_multiplier())
|
escort = self.to_cp.base.scramble_sweep(self._enemy_scramble_multiplier())
|
||||||
@@ -81,16 +81,18 @@ class InterceptEvent(Event):
|
|||||||
attacker_name=self.attacker_name,
|
attacker_name=self.attacker_name,
|
||||||
defender_name=self.defender_name,
|
defender_name=self.defender_name,
|
||||||
from_cp=self.from_cp,
|
from_cp=self.from_cp,
|
||||||
|
departure_cp=self.departure_cp,
|
||||||
to_cp=self.to_cp)
|
to_cp=self.to_cp)
|
||||||
|
|
||||||
op.setup(escort=flight_dict_from(escort),
|
op.setup(location=self.location,
|
||||||
|
escort=assigned_units_from(escort),
|
||||||
transport={self.transport_unit: 1},
|
transport={self.transport_unit: 1},
|
||||||
airdefense={airdefense_unit: self.AIRDEFENSE_COUNT},
|
airdefense={airdefense_unit: self.AIRDEFENSE_COUNT},
|
||||||
interceptors=flights[CAP])
|
interceptors=flights[CAP])
|
||||||
|
|
||||||
self.operation = op
|
self.operation = op
|
||||||
|
|
||||||
def player_defending(self, flights: ScrambledFlightsDict):
|
def player_defending(self, flights: db.TaskForceDict):
|
||||||
assert CAP in flights and len(flights) == 1, "Invalid flights"
|
assert CAP in flights and len(flights) == 1, "Invalid flights"
|
||||||
|
|
||||||
interceptors = self.from_cp.base.scramble_interceptors(self.game.settings.multiplier)
|
interceptors = self.from_cp.base.scramble_interceptors(self.game.settings.multiplier)
|
||||||
@@ -102,11 +104,13 @@ class InterceptEvent(Event):
|
|||||||
attacker_name=self.attacker_name,
|
attacker_name=self.attacker_name,
|
||||||
defender_name=self.defender_name,
|
defender_name=self.defender_name,
|
||||||
from_cp=self.from_cp,
|
from_cp=self.from_cp,
|
||||||
|
departure_cp=self.departure_cp,
|
||||||
to_cp=self.to_cp)
|
to_cp=self.to_cp)
|
||||||
|
|
||||||
op.setup(escort=flights[CAP],
|
op.setup(location=self.location,
|
||||||
|
escort=flights[CAP],
|
||||||
transport={self.transport_unit: 1},
|
transport={self.transport_unit: 1},
|
||||||
interceptors=flight_dict_from(interceptors),
|
interceptors=assigned_units_from(interceptors),
|
||||||
airdefense={})
|
airdefense={})
|
||||||
|
|
||||||
self.operation = op
|
self.operation = op
|
||||||
|
|||||||
@@ -1,13 +1,4 @@
|
|||||||
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 game.operation.navalintercept import NavalInterceptionOperation
|
||||||
from userdata.debriefing import Debriefing
|
|
||||||
|
|
||||||
from .event import *
|
from .event import *
|
||||||
|
|
||||||
@@ -18,9 +9,14 @@ class NavalInterceptEvent(Event):
|
|||||||
|
|
||||||
targets = None # type: db.ShipDict
|
targets = None # type: db.ShipDict
|
||||||
|
|
||||||
|
def __init__(self, game, from_cp: ControlPoint, target_cp: ControlPoint, location: Point, attacker_name: str,
|
||||||
|
defender_name: str):
|
||||||
|
super().__init__(game, from_cp, target_cp, location, attacker_name, defender_name)
|
||||||
|
self.location = Conflict.naval_intercept_position(from_cp, target_cp, game.theater)
|
||||||
|
|
||||||
def _targets_count(self) -> int:
|
def _targets_count(self) -> int:
|
||||||
from gen.conflictgen import IMPORTANCE_LOW, IMPORTANCE_HIGH
|
from gen.conflictgen import IMPORTANCE_LOW
|
||||||
factor = (self.to_cp.importance - IMPORTANCE_LOW) * 10
|
factor = (self.to_cp.importance - IMPORTANCE_LOW + 0.1) * 20
|
||||||
return max(int(factor), 1)
|
return max(int(factor), 1)
|
||||||
|
|
||||||
def __str__(self) -> str:
|
def __str__(self) -> str:
|
||||||
@@ -42,18 +38,22 @@ class NavalInterceptEvent(Event):
|
|||||||
@property
|
@property
|
||||||
def threat_description(self):
|
def threat_description(self):
|
||||||
s = "{} ship(s)".format(self._targets_count())
|
s = "{} ship(s)".format(self._targets_count())
|
||||||
if not self.from_cp.captured:
|
if not self.departure_cp.captured:
|
||||||
s += ", {} aircraft".format(self.from_cp.base.scramble_count(self.game.settings.multiplier))
|
s += ", {} aircraft".format(self.departure_cp.base.scramble_count(self.game.settings.multiplier))
|
||||||
return s
|
return s
|
||||||
|
|
||||||
|
@property
|
||||||
|
def global_cp_available(self) -> bool:
|
||||||
|
return True
|
||||||
|
|
||||||
def is_successfull(self, debriefing: Debriefing):
|
def is_successfull(self, debriefing: Debriefing):
|
||||||
total_targets = sum(self.targets.values())
|
total_targets = sum(self.targets.values())
|
||||||
destroyed_targets = 0
|
destroyed_targets = 0
|
||||||
for unit, count in debriefing.destroyed_units[self.defender_name].items():
|
for unit, count in debriefing.destroyed_units.get(self.defender_name, {}).items():
|
||||||
if unit in self.targets:
|
if unit in self.targets:
|
||||||
destroyed_targets += count
|
destroyed_targets += count
|
||||||
|
|
||||||
if self.from_cp.captured:
|
if self.departure_cp.captured:
|
||||||
return math.ceil(float(destroyed_targets) / total_targets) > self.SUCCESS_RATE
|
return math.ceil(float(destroyed_targets) / total_targets) > self.SUCCESS_RATE
|
||||||
else:
|
else:
|
||||||
return math.ceil(float(destroyed_targets) / total_targets) < self.SUCCESS_RATE
|
return math.ceil(float(destroyed_targets) / total_targets) < self.SUCCESS_RATE
|
||||||
@@ -65,11 +65,11 @@ class NavalInterceptEvent(Event):
|
|||||||
if self.is_successfull(debriefing):
|
if self.is_successfull(debriefing):
|
||||||
self.to_cp.base.affect_strength(-self.STRENGTH_INFLUENCE)
|
self.to_cp.base.affect_strength(-self.STRENGTH_INFLUENCE)
|
||||||
else:
|
else:
|
||||||
self.from_cp.base.affect_strength(-self.STRENGTH_INFLUENCE)
|
self.departure_cp.base.affect_strength(-self.STRENGTH_INFLUENCE)
|
||||||
else:
|
else:
|
||||||
# enemy attacking
|
# enemy attacking
|
||||||
if self.is_successfull(debriefing):
|
if self.is_successfull(debriefing):
|
||||||
self.from_cp.base.affect_strength(-self.STRENGTH_INFLUENCE)
|
self.departure_cp.base.affect_strength(-self.STRENGTH_INFLUENCE)
|
||||||
else:
|
else:
|
||||||
self.to_cp.base.affect_strength(-self.STRENGTH_INFLUENCE)
|
self.to_cp.base.affect_strength(-self.STRENGTH_INFLUENCE)
|
||||||
|
|
||||||
@@ -77,7 +77,7 @@ class NavalInterceptEvent(Event):
|
|||||||
if self.to_cp.captured:
|
if self.to_cp.captured:
|
||||||
self.to_cp.base.affect_strength(-self.STRENGTH_INFLUENCE)
|
self.to_cp.base.affect_strength(-self.STRENGTH_INFLUENCE)
|
||||||
|
|
||||||
def player_attacking(self, flights: ScrambledFlightsDict):
|
def player_attacking(self, flights: db.TaskForceDict):
|
||||||
assert CAS in flights and len(flights) == 1, "Invalid flights"
|
assert CAS in flights and len(flights) == 1, "Invalid flights"
|
||||||
|
|
||||||
self.targets = {
|
self.targets = {
|
||||||
@@ -89,16 +89,18 @@ class NavalInterceptEvent(Event):
|
|||||||
attacker_name=self.attacker_name,
|
attacker_name=self.attacker_name,
|
||||||
defender_name=self.defender_name,
|
defender_name=self.defender_name,
|
||||||
from_cp=self.from_cp,
|
from_cp=self.from_cp,
|
||||||
|
departure_cp=self.departure_cp,
|
||||||
to_cp=self.to_cp
|
to_cp=self.to_cp
|
||||||
)
|
)
|
||||||
|
|
||||||
op.setup(strikegroup=flights[CAS],
|
op.setup(location=self.location,
|
||||||
|
strikegroup=flights[CAS],
|
||||||
interceptors={},
|
interceptors={},
|
||||||
targets=self.targets)
|
targets=self.targets)
|
||||||
|
|
||||||
self.operation = op
|
self.operation = op
|
||||||
|
|
||||||
def player_defending(self, flights: ScrambledFlightsDict):
|
def player_defending(self, flights: db.TaskForceDict):
|
||||||
assert CAP in flights and len(flights) == 1, "Invalid flights"
|
assert CAP in flights and len(flights) == 1, "Invalid flights"
|
||||||
|
|
||||||
self.targets = {
|
self.targets = {
|
||||||
@@ -110,11 +112,12 @@ class NavalInterceptEvent(Event):
|
|||||||
attacker_name=self.attacker_name,
|
attacker_name=self.attacker_name,
|
||||||
defender_name=self.defender_name,
|
defender_name=self.defender_name,
|
||||||
from_cp=self.from_cp,
|
from_cp=self.from_cp,
|
||||||
|
departure_cp=self.departure_cp,
|
||||||
to_cp=self.to_cp
|
to_cp=self.to_cp
|
||||||
)
|
)
|
||||||
|
|
||||||
strikegroup = self.from_cp.base.scramble_cas(self.game.settings.multiplier)
|
strikegroup = self.departure_cp.base.scramble_cas(self.game.settings.multiplier)
|
||||||
op.setup(strikegroup=flight_dict_from(strikegroup),
|
op.setup(strikegroup=assigned_units_from(strikegroup),
|
||||||
interceptors=flights[CAP],
|
interceptors=flights[CAP],
|
||||||
targets=self.targets)
|
targets=self.targets)
|
||||||
|
|
||||||
|
|||||||
@@ -1,23 +1,14 @@
|
|||||||
import math
|
|
||||||
import random
|
|
||||||
|
|
||||||
from dcs.task import *
|
|
||||||
from dcs.vehicles import *
|
|
||||||
|
|
||||||
from game import db
|
|
||||||
from game.operation.strike import StrikeOperation
|
from game.operation.strike import StrikeOperation
|
||||||
from theater.conflicttheater import *
|
|
||||||
from userdata.debriefing import Debriefing
|
|
||||||
|
|
||||||
from .event import *
|
from .event import *
|
||||||
|
|
||||||
|
|
||||||
class StrikeEvent(Event):
|
class StrikeEvent(Event):
|
||||||
STRENGTH_INFLUENCE = 0.0
|
STRENGTH_INFLUENCE = 0.0
|
||||||
SINGLE_OBJECT_STRENGTH_INFLUENCE = 0.03
|
SINGLE_OBJECT_STRENGTH_INFLUENCE = 0.05
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
return "Strike"
|
return "Strike / SEAD"
|
||||||
|
|
||||||
def is_successfull(self, debriefing: Debriefing):
|
def is_successfull(self, debriefing: Debriefing):
|
||||||
return True
|
return True
|
||||||
@@ -29,7 +20,7 @@ class StrikeEvent(Event):
|
|||||||
@property
|
@property
|
||||||
def tasks(self):
|
def tasks(self):
|
||||||
if self.is_player_attacking:
|
if self.is_player_attacking:
|
||||||
return [CAP, CAS]
|
return [CAP, CAS, SEAD]
|
||||||
else:
|
else:
|
||||||
return [CAP]
|
return [CAP]
|
||||||
|
|
||||||
@@ -37,33 +28,46 @@ class StrikeEvent(Event):
|
|||||||
def ai_banned_tasks(self):
|
def ai_banned_tasks(self):
|
||||||
return [CAS]
|
return [CAS]
|
||||||
|
|
||||||
|
@property
|
||||||
|
def player_banned_tasks(self):
|
||||||
|
return [SEAD]
|
||||||
|
|
||||||
|
@property
|
||||||
|
def global_cp_available(self) -> bool:
|
||||||
|
return True
|
||||||
|
|
||||||
def flight_name(self, for_task: typing.Type[Task]) -> str:
|
def flight_name(self, for_task: typing.Type[Task]) -> str:
|
||||||
if for_task == CAP:
|
if for_task == CAP:
|
||||||
if self.is_player_attacking:
|
if self.is_player_attacking:
|
||||||
return "Escort flight"
|
return "Escort flight"
|
||||||
else:
|
else:
|
||||||
return "CAP flight"
|
return "CAP flight"
|
||||||
|
elif for_task == SEAD:
|
||||||
|
return "SEAD flight"
|
||||||
elif for_task == CAS:
|
elif for_task == CAS:
|
||||||
return "Strike flight"
|
return "Strike flight"
|
||||||
|
|
||||||
def commit(self, debriefing: Debriefing):
|
def commit(self, debriefing: Debriefing):
|
||||||
super(StrikeEvent, self).commit(debriefing)
|
super(StrikeEvent, self).commit(debriefing)
|
||||||
|
|
||||||
self.to_cp.base.affect_strength(-self.SINGLE_OBJECT_STRENGTH_INFLUENCE * len(debriefing.destroyed_objects))
|
self.to_cp.base.affect_strength(-self.SINGLE_OBJECT_STRENGTH_INFLUENCE * len(debriefing.destroyed_objects))
|
||||||
|
|
||||||
def player_attacking(self, flights: ScrambledFlightsDict):
|
def player_attacking(self, flights: db.TaskForceDict):
|
||||||
assert CAP in flights and CAS in flights and len(flights) == 2, "Invalid flights"
|
assert CAP in flights and CAS in flights and SEAD in flights and len(flights) == 3, "Invalid flights"
|
||||||
|
|
||||||
op = StrikeOperation(
|
op = StrikeOperation(
|
||||||
self.game,
|
self.game,
|
||||||
attacker_name=self.attacker_name,
|
attacker_name=self.attacker_name,
|
||||||
defender_name=self.defender_name,
|
defender_name=self.defender_name,
|
||||||
from_cp=self.from_cp,
|
from_cp=self.from_cp,
|
||||||
|
departure_cp=self.departure_cp,
|
||||||
to_cp=self.to_cp
|
to_cp=self.to_cp
|
||||||
)
|
)
|
||||||
|
|
||||||
interceptors = self.to_cp.base.scramble_interceptors(self.game.settings.multiplier)
|
interceptors = self.to_cp.base.scramble_interceptors(self.game.settings.multiplier)
|
||||||
op.setup(strikegroup=flights[CAS],
|
op.setup(strikegroup=flights[CAS],
|
||||||
|
sead=flights[SEAD],
|
||||||
escort=flights[CAP],
|
escort=flights[CAP],
|
||||||
interceptors=flight_dict_from(interceptors))
|
interceptors=assigned_units_from(interceptors))
|
||||||
|
|
||||||
self.operation = op
|
self.operation = op
|
||||||
|
|||||||
176
game/game.py
@@ -25,7 +25,7 @@ COMMISION_LIMITS_FACTORS = {
|
|||||||
|
|
||||||
COMMISION_AMOUNTS_SCALE = 1.5
|
COMMISION_AMOUNTS_SCALE = 1.5
|
||||||
COMMISION_AMOUNTS_FACTORS = {
|
COMMISION_AMOUNTS_FACTORS = {
|
||||||
PinpointStrike: 6,
|
PinpointStrike: 3,
|
||||||
CAS: 1,
|
CAS: 1,
|
||||||
CAP: 2,
|
CAP: 2,
|
||||||
AirDefence: 0.3,
|
AirDefence: 0.3,
|
||||||
@@ -33,30 +33,39 @@ COMMISION_AMOUNTS_FACTORS = {
|
|||||||
|
|
||||||
PLAYER_INTERCEPT_GLOBAL_PROBABILITY_BASE = 30
|
PLAYER_INTERCEPT_GLOBAL_PROBABILITY_BASE = 30
|
||||||
PLAYER_INTERCEPT_GLOBAL_PROBABILITY_LOG = 2
|
PLAYER_INTERCEPT_GLOBAL_PROBABILITY_LOG = 2
|
||||||
PLAYER_BASEATTACK_THRESHOLD = 0.2
|
PLAYER_BASEATTACK_THRESHOLD = 0.4
|
||||||
|
|
||||||
"""
|
"""
|
||||||
Various events probabilities. First key is player probabilty, second is enemy probability.
|
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.
|
For the enemy events, only 1 event of each type could be generated for a turn.
|
||||||
|
|
||||||
Events:
|
Events:
|
||||||
* CaptureEvent - capture base
|
* BaseAttackEvent - capture base
|
||||||
* InterceptEvent - air intercept
|
* InterceptEvent - air intercept
|
||||||
* FrontlineAttack - frontline attack
|
* FrontlineAttackEvent - frontline attack
|
||||||
* GroundAttackEvent - destroy insurgents
|
|
||||||
* NavalInterceptEvent - naval intercept
|
* NavalInterceptEvent - naval intercept
|
||||||
* AntiAAStrikeEvent - anti-AA strike
|
* StrikeEvent - strike event
|
||||||
* InfantryTransportEvent - helicopter infantry transport
|
* InfantryTransportEvent - helicopter infantry transport
|
||||||
"""
|
"""
|
||||||
EVENT_PROBABILITIES = {
|
EVENT_PROBABILITIES = {
|
||||||
BaseAttackEvent: [100, 10],
|
# events always present; only for the player
|
||||||
FrontlineAttackEvent: [100, 0],
|
FrontlineAttackEvent: [100, 9],
|
||||||
FrontlinePatrolEvent: [100, 0],
|
#FrontlinePatrolEvent: [100, 0],
|
||||||
StrikeEvent: [100, 0],
|
StrikeEvent: [100, 0],
|
||||||
InterceptEvent: [25, 10],
|
|
||||||
InsurgentAttackEvent: [0, 10],
|
# events randomly present; only for the player
|
||||||
NavalInterceptEvent: [25, 10],
|
#InfantryTransportEvent: [25, 0],
|
||||||
InfantryTransportEvent: [25, 0],
|
ConvoyStrikeEvent: [25, 0],
|
||||||
|
|
||||||
|
# events conditionally present; for both enemy and player
|
||||||
|
BaseAttackEvent: [100, 9],
|
||||||
|
|
||||||
|
# events randomly present; for both enemy and player
|
||||||
|
InterceptEvent: [25, 9],
|
||||||
|
NavalInterceptEvent: [25, 9],
|
||||||
|
|
||||||
|
# events randomly present; only for the enemy
|
||||||
|
InsurgentAttackEvent: [0, 6],
|
||||||
}
|
}
|
||||||
|
|
||||||
# amount of strength player bases recover for the turn
|
# amount of strength player bases recover for the turn
|
||||||
@@ -71,7 +80,7 @@ AWACS_BUDGET_COST = 4
|
|||||||
# Initial budget value
|
# Initial budget value
|
||||||
PLAYER_BUDGET_INITIAL = 170
|
PLAYER_BUDGET_INITIAL = 170
|
||||||
# Base post-turn bonus value
|
# Base post-turn bonus value
|
||||||
PLAYER_BUDGET_BASE = 17
|
PLAYER_BUDGET_BASE = 14
|
||||||
# Bonus multiplier logarithm base
|
# Bonus multiplier logarithm base
|
||||||
PLAYER_BUDGET_IMPORTANCE_LOG = 2
|
PLAYER_BUDGET_IMPORTANCE_LOG = 2
|
||||||
|
|
||||||
@@ -91,71 +100,95 @@ class Game:
|
|||||||
self.enemy = enemy_name
|
self.enemy = enemy_name
|
||||||
|
|
||||||
def _roll(self, prob, mult):
|
def _roll(self, prob, mult):
|
||||||
return random.randint(1, 100) <= prob * mult
|
if self.settings.version == "dev":
|
||||||
|
# always generate all events for dev
|
||||||
|
return 100
|
||||||
|
else:
|
||||||
|
return random.randint(1, 100) <= prob * mult
|
||||||
|
|
||||||
def _generate_globalinterceptions(self):
|
def _generate_player_event(self, event_class, player_cp, enemy_cp):
|
||||||
global_count = len([x for x in self.theater.player_points() if x.is_global])
|
if event_class == NavalInterceptEvent and enemy_cp.radials == LAND:
|
||||||
for from_cp in [x for x in self.theater.player_points() if x.is_global]:
|
# skip naval events for non-coastal CPs
|
||||||
probability_base = max(PLAYER_INTERCEPT_GLOBAL_PROBABILITY_BASE / global_count, 1)
|
return
|
||||||
probability = probability_base * math.log(len(self.theater.player_points()) + 1, PLAYER_INTERCEPT_GLOBAL_PROBABILITY_LOG)
|
|
||||||
if self._roll(probability, from_cp.base.strength):
|
if event_class == BaseAttackEvent and enemy_cp.base.strength > PLAYER_BASEATTACK_THRESHOLD and self.settings.version != "dev":
|
||||||
to_cp = random.choice([x for x in self.theater.enemy_points() if x not in self.theater.conflicts()])
|
# skip base attack events for CPs yet too strong
|
||||||
self.events.append(InterceptEvent(attacker_name=self.player,
|
return
|
||||||
defender_name=self.enemy,
|
|
||||||
from_cp=from_cp,
|
if event_class == StrikeEvent and not enemy_cp.ground_objects:
|
||||||
to_cp=to_cp,
|
# skip strikes in case of no targets
|
||||||
game=self))
|
return
|
||||||
break
|
|
||||||
|
self.events.append(event_class(self, player_cp, enemy_cp, enemy_cp.position, self.player, self.enemy))
|
||||||
|
|
||||||
|
def _generate_enemy_event(self, event_class, player_cp, enemy_cp):
|
||||||
|
if event_class in [type(x) for x in self.events if not self.is_player_attack(x)]:
|
||||||
|
# skip already generated enemy event types
|
||||||
|
return
|
||||||
|
|
||||||
|
if player_cp in self.ignored_cps:
|
||||||
|
# skip attacks against ignored CPs (for example just captured ones)
|
||||||
|
return
|
||||||
|
|
||||||
|
if enemy_cp.base.total_planes == 0:
|
||||||
|
# skip event if there's no planes on the base
|
||||||
|
return
|
||||||
|
|
||||||
|
if player_cp.is_global:
|
||||||
|
# skip carriers
|
||||||
|
return
|
||||||
|
|
||||||
|
if event_class == NavalInterceptEvent:
|
||||||
|
if player_cp.radials == LAND:
|
||||||
|
# skip naval events for non-coastal CPs
|
||||||
|
return
|
||||||
|
elif event_class == StrikeEvent:
|
||||||
|
if not player_cp.ground_objects:
|
||||||
|
# skip strikes if there's no ground objects
|
||||||
|
return
|
||||||
|
elif event_class == BaseAttackEvent:
|
||||||
|
if BaseAttackEvent in [type(x) for x in self.events]:
|
||||||
|
# skip base attack event if there's another one going on
|
||||||
|
return
|
||||||
|
|
||||||
|
if enemy_cp.base.total_armor == 0:
|
||||||
|
# skip base attack if there's no armor
|
||||||
|
return
|
||||||
|
|
||||||
|
if player_cp.base.strength > PLAYER_BASEATTACK_THRESHOLD:
|
||||||
|
# skip base attack if strength is too high
|
||||||
|
return
|
||||||
|
|
||||||
|
self.events.append(event_class(self, enemy_cp, player_cp, player_cp.position, self.enemy, self.player))
|
||||||
|
|
||||||
def _generate_events(self):
|
def _generate_events(self):
|
||||||
enemy_cap_generated = False
|
strikes_generated_for = set()
|
||||||
enemy_generated_types = []
|
base_attack_generated_for = set()
|
||||||
|
|
||||||
for player_cp, enemy_cp in self.theater.conflicts(True):
|
for player_cp, enemy_cp in self.theater.conflicts(True):
|
||||||
if player_cp.is_global or enemy_cp.is_global:
|
|
||||||
continue
|
|
||||||
|
|
||||||
for event_class, (player_probability, enemy_probability) in EVENT_PROBABILITIES.items():
|
for event_class, (player_probability, enemy_probability) in EVENT_PROBABILITIES.items():
|
||||||
if event_class == FrontlineAttackEvent or event_class == InfantryTransportEvent or event_class == FrontlinePatrolEvent:
|
if event_class in [FrontlineAttackEvent, FrontlinePatrolEvent, InfantryTransportEvent, ConvoyStrikeEvent]:
|
||||||
|
# skip events requiring frontline
|
||||||
if not Conflict.has_frontline_between(player_cp, enemy_cp):
|
if not Conflict.has_frontline_between(player_cp, enemy_cp):
|
||||||
continue
|
continue
|
||||||
|
|
||||||
if player_probability == 100 or self._roll(player_probability, player_cp.base.strength):
|
# don't generate multiple 100% events from each attack direction
|
||||||
if event_class == NavalInterceptEvent and enemy_cp.radials == LAND:
|
if event_class is StrikeEvent:
|
||||||
pass
|
if enemy_cp in strikes_generated_for:
|
||||||
else:
|
continue
|
||||||
if event_class == BaseAttackEvent and enemy_cp.base.strength > PLAYER_BASEATTACK_THRESHOLD:
|
if event_class is BaseAttackEvent:
|
||||||
pass
|
if enemy_cp in base_attack_generated_for:
|
||||||
else:
|
|
||||||
if event_class == StrikeEvent and not enemy_cp.ground_objects:
|
|
||||||
pass
|
|
||||||
else:
|
|
||||||
self.events.append(event_class(self.player, self.enemy, player_cp, enemy_cp, self))
|
|
||||||
elif enemy_probability == 100 or self._roll(enemy_probability, enemy_cp.base.strength):
|
|
||||||
if event_class in enemy_generated_types:
|
|
||||||
continue
|
continue
|
||||||
|
|
||||||
if player_cp in self.ignored_cps:
|
if player_probability == 100 or player_probability > 0 and self._roll(player_probability, player_cp.base.strength):
|
||||||
continue
|
self._generate_player_event(event_class, player_cp, enemy_cp)
|
||||||
|
if event_class is StrikeEvent:
|
||||||
|
strikes_generated_for.add(enemy_cp)
|
||||||
|
if event_class is BaseAttackEvent:
|
||||||
|
base_attack_generated_for.add(enemy_cp)
|
||||||
|
|
||||||
if enemy_cp.base.total_planes == 0:
|
if enemy_probability == 100 or enemy_probability > 0 and self._roll(enemy_probability, enemy_cp.base.strength):
|
||||||
continue
|
self._generate_enemy_event(event_class, player_cp, enemy_cp)
|
||||||
|
|
||||||
if event_class == NavalInterceptEvent:
|
|
||||||
if player_cp.radials == LAND:
|
|
||||||
continue
|
|
||||||
elif event_class == StrikeEvent:
|
|
||||||
if not player_cp.ground_objects:
|
|
||||||
continue
|
|
||||||
elif event_class == BaseAttackEvent:
|
|
||||||
if enemy_cap_generated:
|
|
||||||
continue
|
|
||||||
if enemy_cp.base.total_armor == 0:
|
|
||||||
continue
|
|
||||||
enemy_cap_generated = True
|
|
||||||
|
|
||||||
enemy_generated_types.append(event_class)
|
|
||||||
self.events.append(event_class(self.enemy, self.player, enemy_cp, player_cp, self))
|
|
||||||
|
|
||||||
def commision_unit_types(self, cp: ControlPoint, for_task: Task) -> typing.Collection[UnitType]:
|
def commision_unit_types(self, cp: ControlPoint, for_task: Task) -> typing.Collection[UnitType]:
|
||||||
importance_factor = (cp.importance - IMPORTANCE_LOW) / (IMPORTANCE_HIGH - IMPORTANCE_LOW)
|
importance_factor = (cp.importance - IMPORTANCE_LOW) / (IMPORTANCE_HIGH - IMPORTANCE_LOW)
|
||||||
@@ -233,7 +266,12 @@ class Game:
|
|||||||
def pass_turn(self, no_action=False, ignored_cps: typing.Collection[ControlPoint]=None):
|
def pass_turn(self, no_action=False, ignored_cps: typing.Collection[ControlPoint]=None):
|
||||||
logging.info("Pass turn")
|
logging.info("Pass turn")
|
||||||
for event in self.events:
|
for event in self.events:
|
||||||
event.skip()
|
if self.settings.version == "dev":
|
||||||
|
# don't damage player CPs in by skipping in dev mode
|
||||||
|
if isinstance(event, UnitsDeliveryEvent):
|
||||||
|
event.skip()
|
||||||
|
else:
|
||||||
|
event.skip()
|
||||||
|
|
||||||
if not no_action:
|
if not no_action:
|
||||||
self._budget_player()
|
self._budget_player()
|
||||||
@@ -250,5 +288,5 @@ class Game:
|
|||||||
|
|
||||||
self.events = [] # type: typing.List[Event]
|
self.events = [] # type: typing.List[Event]
|
||||||
self._generate_events()
|
self._generate_events()
|
||||||
self._generate_globalinterceptions()
|
#self._generate_globalinterceptions()
|
||||||
|
|
||||||
|
|||||||
@@ -1,21 +1,14 @@
|
|||||||
from game import db
|
from game.db import assigned_units_split
|
||||||
|
|
||||||
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.triggergen import *
|
||||||
from gen.airsupportgen import *
|
|
||||||
from gen.visualgen import *
|
|
||||||
|
|
||||||
from .operation import *
|
from .operation import *
|
||||||
|
|
||||||
|
|
||||||
class BaseAttackOperation(Operation):
|
class BaseAttackOperation(Operation):
|
||||||
cas = None # type: FlightDict
|
cas = None # type: db.AssignedUnitsDict
|
||||||
escort = None # type: FlightDict
|
escort = None # type: db.AssignedUnitsDict
|
||||||
intercept = None # type: FlightDict
|
intercept = None # type: db.AssignedUnitsDict
|
||||||
attack = None # type: db.ArmorDict
|
attack = None # type: db.ArmorDict
|
||||||
defense = None # type: db.ArmorDict
|
defense = None # type: db.ArmorDict
|
||||||
aa = None # type: db.AirDefenseDict
|
aa = None # type: db.AirDefenseDict
|
||||||
@@ -23,10 +16,10 @@ class BaseAttackOperation(Operation):
|
|||||||
trigger_radius = TRIGGER_RADIUS_SMALL
|
trigger_radius = TRIGGER_RADIUS_SMALL
|
||||||
|
|
||||||
def setup(self,
|
def setup(self,
|
||||||
cas: FlightDict,
|
cas: db.AssignedUnitsDict,
|
||||||
escort: FlightDict,
|
escort: db.AssignedUnitsDict,
|
||||||
attack: FlightDict,
|
attack: db.AssignedUnitsDict,
|
||||||
intercept: FlightDict,
|
intercept: db.AssignedUnitsDict,
|
||||||
defense: db.ArmorDict,
|
defense: db.ArmorDict,
|
||||||
aa: db.AirDefenseDict):
|
aa: db.AirDefenseDict):
|
||||||
self.cas = cas
|
self.cas = cas
|
||||||
@@ -44,27 +37,33 @@ class BaseAttackOperation(Operation):
|
|||||||
self.attackers_starting_position = None
|
self.attackers_starting_position = None
|
||||||
|
|
||||||
conflict = Conflict.capture_conflict(
|
conflict = Conflict.capture_conflict(
|
||||||
attacker=self.mission.country(self.attacker_name),
|
attacker=self.current_mission.country(self.attacker_name),
|
||||||
defender=self.mission.country(self.defender_name),
|
defender=self.current_mission.country(self.defender_name),
|
||||||
from_cp=self.from_cp,
|
from_cp=self.from_cp,
|
||||||
to_cp=self.to_cp,
|
to_cp=self.to_cp,
|
||||||
theater=self.game.theater
|
theater=self.game.theater
|
||||||
)
|
)
|
||||||
self.initialize(mission=self.mission,
|
self.initialize(mission=self.current_mission,
|
||||||
conflict=conflict)
|
conflict=conflict)
|
||||||
|
|
||||||
def generate(self):
|
def generate(self):
|
||||||
self.armorgen.generate(self.attack, self.defense)
|
self.armorgen.generate(self.attack, self.defense)
|
||||||
self.aagen.generate(self.aa)
|
self.aagen.generate(self.aa)
|
||||||
|
|
||||||
self.airgen.generate_defense(*flight_arguments(self.intercept), at=self.defenders_starting_position)
|
self.airgen.generate_defense(*assigned_units_split(self.intercept), at=self.defenders_starting_position)
|
||||||
|
|
||||||
self.airgen.generate_cas_strikegroup(*flight_arguments(self.cas), at=self.attackers_starting_position)
|
self.airgen.generate_cas_strikegroup(*assigned_units_split(self.cas), at=self.attackers_starting_position)
|
||||||
self.airgen.generate_attackers_escort(*flight_arguments(self.escort), at=self.attackers_starting_position)
|
self.airgen.generate_attackers_escort(*assigned_units_split(self.escort), at=self.attackers_starting_position)
|
||||||
|
|
||||||
self.visualgen.generate_target_smokes(self.to_cp)
|
self.visualgen.generate_target_smokes(self.to_cp)
|
||||||
|
|
||||||
self.briefinggen.title = "Base attack"
|
self.briefinggen.title = "Base attack"
|
||||||
self.briefinggen.description = "The goal of an attacker is to lower defender presence by destroying their armor and aircraft. Base will be considered captured if attackers on the ground overrun the defenders. Be advised that your flight will not attack anything until you explicitly tell them so by comms menu."
|
self.briefinggen.description = "The goal of an attacker is to lower defender presence by destroying their armor and aircraft. Base will be considered captured if attackers on the ground overrun the defenders. Be advised that your flight will not attack anything until you explicitly tell them so by comms menu."
|
||||||
|
|
||||||
|
if self.game.player == self.attacker_name:
|
||||||
|
self.briefinggen.append_waypoint("TARGET")
|
||||||
|
else:
|
||||||
|
pass
|
||||||
|
|
||||||
super(BaseAttackOperation, self).generate()
|
super(BaseAttackOperation, self).generate()
|
||||||
|
|
||||||
|
|||||||
49
game/operation/convoystrike.py
Normal file
@@ -0,0 +1,49 @@
|
|||||||
|
from game.db import assigned_units_split
|
||||||
|
|
||||||
|
from .operation import *
|
||||||
|
|
||||||
|
|
||||||
|
class ConvoyStrikeOperation(Operation):
|
||||||
|
strikegroup = None # type: db.AssignedUnitsDict
|
||||||
|
target = None # type: db.ArmorDict
|
||||||
|
|
||||||
|
def setup(self,
|
||||||
|
target: db.ArmorDict,
|
||||||
|
strikegroup: db.AssignedUnitsDict):
|
||||||
|
self.strikegroup = strikegroup
|
||||||
|
self.target = target
|
||||||
|
|
||||||
|
def prepare(self, terrain: Terrain, is_quick: bool):
|
||||||
|
super(ConvoyStrikeOperation, self).prepare(terrain, is_quick)
|
||||||
|
|
||||||
|
conflict = Conflict.convoy_strike_conflict(
|
||||||
|
attacker=self.current_mission.country(self.attacker_name),
|
||||||
|
defender=self.current_mission.country(self.defender_name),
|
||||||
|
from_cp=self.from_cp,
|
||||||
|
to_cp=self.to_cp,
|
||||||
|
theater=self.game.theater
|
||||||
|
)
|
||||||
|
|
||||||
|
self.initialize(mission=self.current_mission,
|
||||||
|
conflict=conflict)
|
||||||
|
|
||||||
|
def generate(self):
|
||||||
|
if self.is_player_attack:
|
||||||
|
self.prepare_carriers(db.unitdict_from(self.strikegroup))
|
||||||
|
|
||||||
|
planes_flights = {k: v for k, v in self.strikegroup.items() if k in plane_map.values()}
|
||||||
|
self.airgen.generate_cas_strikegroup(*assigned_units_split(planes_flights), at=self.attackers_starting_position)
|
||||||
|
|
||||||
|
heli_flights = {k: v for k, v in self.strikegroup.items() if k in helicopters.helicopter_map.values()}
|
||||||
|
if heli_flights:
|
||||||
|
self.briefinggen.append_frequency("FARP + Heli flights", "127.5 MHz AM")
|
||||||
|
for farp, dict in zip(self.groundobjectgen.generate_farps(sum([x[0] for x in heli_flights.values()])),
|
||||||
|
db.assignedunits_split_to_count(heli_flights, self.groundobjectgen.FARP_CAPACITY)):
|
||||||
|
self.airgen.generate_cas_strikegroup(*assigned_units_split(dict),
|
||||||
|
at=farp,
|
||||||
|
escort=len(planes_flights) == 0)
|
||||||
|
|
||||||
|
self.armorgen.generate_convoy(self.target)
|
||||||
|
|
||||||
|
self.briefinggen.append_waypoint("TARGET")
|
||||||
|
super(ConvoyStrikeOperation, self).generate()
|
||||||
@@ -1,16 +1,4 @@
|
|||||||
from itertools import zip_longest
|
from game.db import assigned_units_split
|
||||||
|
|
||||||
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.airsupportgen import *
|
|
||||||
from gen.visualgen import *
|
|
||||||
from gen.conflictgen import Conflict
|
|
||||||
|
|
||||||
from .operation import *
|
from .operation import *
|
||||||
|
|
||||||
@@ -19,16 +7,24 @@ MAX_DISTANCE_BETWEEN_GROUPS = 12000
|
|||||||
|
|
||||||
|
|
||||||
class FrontlineAttackOperation(Operation):
|
class FrontlineAttackOperation(Operation):
|
||||||
strikegroup = None # type: FlightDict
|
interceptors = None # type: db.AssignedUnitsDict
|
||||||
|
escort = None # type: db.AssignedUnitsDict
|
||||||
|
strikegroup = None # type: db.AssignedUnitsDict
|
||||||
|
|
||||||
attackers = None # type: db.ArmorDict
|
attackers = None # type: db.ArmorDict
|
||||||
target = None # type: db.ArmorDict
|
defenders = None # type: db.ArmorDict
|
||||||
|
|
||||||
def setup(self,
|
def setup(self,
|
||||||
target: db.ArmorDict,
|
defenders: db.ArmorDict,
|
||||||
attackers: db.ArmorDict,
|
attackers: db.ArmorDict,
|
||||||
strikegroup: FlightDict):
|
strikegroup: db.AssignedUnitsDict,
|
||||||
|
escort: db.AssignedUnitsDict,
|
||||||
|
interceptors: db.AssignedUnitsDict):
|
||||||
self.strikegroup = strikegroup
|
self.strikegroup = strikegroup
|
||||||
self.target = target
|
self.escort = escort
|
||||||
|
self.interceptors = interceptors
|
||||||
|
|
||||||
|
self.defenders = defenders
|
||||||
self.attackers = attackers
|
self.attackers = attackers
|
||||||
|
|
||||||
def prepare(self, terrain: Terrain, is_quick: bool):
|
def prepare(self, terrain: Terrain, is_quick: bool):
|
||||||
@@ -38,20 +34,42 @@ class FrontlineAttackOperation(Operation):
|
|||||||
self.defenders_starting_position = None
|
self.defenders_starting_position = None
|
||||||
|
|
||||||
conflict = Conflict.frontline_cas_conflict(
|
conflict = Conflict.frontline_cas_conflict(
|
||||||
attacker=self.mission.country(self.attacker_name),
|
attacker=self.current_mission.country(self.attacker_name),
|
||||||
defender=self.mission.country(self.defender_name),
|
defender=self.current_mission.country(self.defender_name),
|
||||||
from_cp=self.from_cp,
|
from_cp=self.from_cp,
|
||||||
to_cp=self.to_cp,
|
to_cp=self.to_cp,
|
||||||
theater=self.game.theater
|
theater=self.game.theater
|
||||||
)
|
)
|
||||||
|
|
||||||
self.initialize(mission=self.mission,
|
self.initialize(mission=self.current_mission,
|
||||||
conflict=conflict)
|
conflict=conflict)
|
||||||
|
|
||||||
def generate(self):
|
def generate(self):
|
||||||
self.armorgen.generate_vec(self.attackers, self.target)
|
if self.is_player_attack:
|
||||||
self.airgen.generate_cas_strikegroup(*flight_arguments(self.strikegroup), at=self.attackers_starting_position)
|
self.prepare_carriers(db.unitdict_from(self.strikegroup))
|
||||||
|
|
||||||
|
# ground units
|
||||||
|
self.armorgen.generate_vec(self.attackers, self.defenders)
|
||||||
|
|
||||||
|
# strike group w/ heli support
|
||||||
|
planes_flights = {k: v for k, v in self.strikegroup.items() if k in plane_map.values()}
|
||||||
|
self.airgen.generate_cas_strikegroup(*assigned_units_split(planes_flights), at=self.attackers_starting_position)
|
||||||
|
|
||||||
|
heli_flights = {k: v for k, v in self.strikegroup.items() if k in helicopters.helicopter_map.values()}
|
||||||
|
if heli_flights:
|
||||||
|
self.briefinggen.append_frequency("FARP + Heli flights", "127.5 MHz AM")
|
||||||
|
for farp, dict in zip(self.groundobjectgen.generate_farps(sum([x[0] for x in heli_flights.values()])),
|
||||||
|
db.assignedunits_split_to_count(heli_flights, self.groundobjectgen.FARP_CAPACITY)):
|
||||||
|
self.airgen.generate_cas_strikegroup(*assigned_units_split(dict),
|
||||||
|
at=farp,
|
||||||
|
escort=len(planes_flights) == 0)
|
||||||
|
|
||||||
|
self.airgen.generate_attackers_escort(*assigned_units_split(self.escort), at=self.attackers_starting_position)
|
||||||
|
|
||||||
|
self.airgen.generate_defense(*assigned_units_split(self.interceptors), at=self.defenders_starting_position)
|
||||||
|
|
||||||
self.briefinggen.title = "Frontline CAS"
|
self.briefinggen.title = "Frontline CAS"
|
||||||
self.briefinggen.description = "Provide CAS for the ground forces attacking enemy lines. Operation will be considered successful if total number of enemy units will be lower than your own by a factor of 1.5 (i.e. with 12 units from both sides, enemy forces need to be reduced to at least 8), meaning that you (and, probably, your wingmans) should concentrate on destroying the enemy units. Target base strength will be lowered as a result. Be advised that your flight will not attack anything until you explicitly tell them so by comms menu."
|
self.briefinggen.description = "Provide CAS for the ground forces attacking enemy lines. Operation will be considered successful if total number of enemy units will be lower than your own by a factor of 1.5 (i.e. with 12 units from both sides, enemy forces need to be reduced to at least 8), meaning that you (and, probably, your wingmans) should concentrate on destroying the enemy units. Target base strength will be lowered as a result. Be advised that your flight will not attack anything until you explicitly tell them so by comms menu."
|
||||||
|
self.briefinggen.append_waypoint("CAS AREA IP")
|
||||||
|
self.briefinggen.append_waypoint("CAS AREA EGRESS")
|
||||||
super(FrontlineAttackOperation, self).generate()
|
super(FrontlineAttackOperation, self).generate()
|
||||||
|
|||||||
@@ -1,16 +1,4 @@
|
|||||||
from itertools import zip_longest
|
from game.db import assigned_units_split
|
||||||
|
|
||||||
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.airsupportgen import *
|
|
||||||
from gen.visualgen import *
|
|
||||||
from gen.conflictgen import Conflict
|
|
||||||
|
|
||||||
from .operation import *
|
from .operation import *
|
||||||
|
|
||||||
@@ -19,17 +7,17 @@ MAX_DISTANCE_BETWEEN_GROUPS = 12000
|
|||||||
|
|
||||||
|
|
||||||
class FrontlinePatrolOperation(Operation):
|
class FrontlinePatrolOperation(Operation):
|
||||||
cas = None # type: FlightDict
|
cas = None # type: db.AssignedUnitsDict
|
||||||
escort = None # type: FlightDict
|
escort = None # type: db.AssignedUnitsDict
|
||||||
interceptors = None # type: FlightDict
|
interceptors = None # type: db.AssignedUnitsDict
|
||||||
|
|
||||||
armor_attackers = None # type: db.ArmorDict
|
armor_attackers = None # type: db.ArmorDict
|
||||||
armor_defenders = None # type: db.ArmorDict
|
armor_defenders = None # type: db.ArmorDict
|
||||||
|
|
||||||
def setup(self,
|
def setup(self,
|
||||||
cas: FlightDict,
|
cas: db.AssignedUnitsDict,
|
||||||
escort: FlightDict,
|
escort: db.AssignedUnitsDict,
|
||||||
interceptors: FlightDict,
|
interceptors: db.AssignedUnitsDict,
|
||||||
armor_attackers: db.ArmorDict,
|
armor_attackers: db.ArmorDict,
|
||||||
armor_defenders: db.ArmorDict):
|
armor_defenders: db.ArmorDict):
|
||||||
self.cas = cas
|
self.cas = cas
|
||||||
@@ -44,23 +32,28 @@ class FrontlinePatrolOperation(Operation):
|
|||||||
self.defenders_starting_position = None
|
self.defenders_starting_position = None
|
||||||
|
|
||||||
conflict = Conflict.frontline_cap_conflict(
|
conflict = Conflict.frontline_cap_conflict(
|
||||||
attacker=self.mission.country(self.attacker_name),
|
attacker=self.current_mission.country(self.attacker_name),
|
||||||
defender=self.mission.country(self.defender_name),
|
defender=self.current_mission.country(self.defender_name),
|
||||||
from_cp=self.from_cp,
|
from_cp=self.from_cp,
|
||||||
to_cp=self.to_cp,
|
to_cp=self.to_cp,
|
||||||
theater=self.game.theater
|
theater=self.game.theater
|
||||||
)
|
)
|
||||||
|
|
||||||
self.initialize(mission=self.mission,
|
self.initialize(mission=self.current_mission,
|
||||||
conflict=conflict)
|
conflict=conflict)
|
||||||
|
|
||||||
def generate(self):
|
def generate(self):
|
||||||
self.airgen.generate_defenders_cas(*flight_arguments(self.cas), at=self.defenders_starting_position)
|
if self.is_player_attack:
|
||||||
self.airgen.generate_defenders_escort(*flight_arguments(self.escort), at=self.defenders_starting_position)
|
self.prepare_carriers(db.unitdict_from(self.interceptors))
|
||||||
self.airgen.generate_migcap(*flight_arguments(self.interceptors), at=self.attackers_starting_position)
|
|
||||||
|
self.airgen.generate_defenders_cas(*assigned_units_split(self.cas), at=self.defenders_starting_position)
|
||||||
|
self.airgen.generate_defenders_escort(*assigned_units_split(self.escort), at=self.defenders_starting_position)
|
||||||
|
self.airgen.generate_migcap(*assigned_units_split(self.interceptors), at=self.attackers_starting_position)
|
||||||
|
|
||||||
self.armorgen.generate_vec(self.armor_attackers, self.armor_defenders)
|
self.armorgen.generate_vec(self.armor_attackers, self.armor_defenders)
|
||||||
|
|
||||||
self.briefinggen.title = "Frontline CAP"
|
self.briefinggen.title = "Frontline CAP"
|
||||||
self.briefinggen.description = "Providing CAP support for ground units attacking enemy lines. Enemy will scramble its CAS and your task is to intercept it. Operation will be considered successful if total number of friendly units will be lower than enemy by at least a factor of 0.8 (i.e. with 12 units from both sides, there should be at least 8 friendly units alive), lowering targets strength as a result."
|
self.briefinggen.description = "Providing CAP support for ground units attacking enemy lines. Enemy will scramble its CAS and your task is to intercept it. Operation will be considered successful if total number of friendly units will be lower than enemy by at least a factor of 0.8 (i.e. with 12 units from both sides, there should be at least 8 friendly units alive), lowering targets strength as a result."
|
||||||
|
self.briefinggen.append_waypoint("CAP AREA IP")
|
||||||
|
self.briefinggen.append_waypoint("CAP AREA EGRESS")
|
||||||
super(FrontlinePatrolOperation, self).generate()
|
super(FrontlinePatrolOperation, self).generate()
|
||||||
|
|||||||
@@ -1,23 +1,13 @@
|
|||||||
from dcs.terrain import Terrain
|
from game.db import assigned_units_split
|
||||||
|
|
||||||
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.airsupportgen import *
|
|
||||||
from gen.visualgen import *
|
|
||||||
from gen.conflictgen import Conflict
|
|
||||||
|
|
||||||
from .operation import *
|
from .operation import *
|
||||||
|
|
||||||
|
|
||||||
class InfantryTransportOperation(Operation):
|
class InfantryTransportOperation(Operation):
|
||||||
transport = None # type: FlightDict
|
transport = None # type: db.AssignedUnitsDict
|
||||||
aa = None # type: db.AirDefenseDict
|
aa = None # type: db.AirDefenseDict
|
||||||
|
|
||||||
def setup(self, transport: FlightDict, aa: db.AirDefenseDict):
|
def setup(self, transport: db.AssignedUnitsDict, aa: db.AirDefenseDict):
|
||||||
self.transport = transport
|
self.transport = transport
|
||||||
self.aa = aa
|
self.aa = aa
|
||||||
|
|
||||||
@@ -25,18 +15,18 @@ class InfantryTransportOperation(Operation):
|
|||||||
super(InfantryTransportOperation, self).prepare(terrain, is_quick)
|
super(InfantryTransportOperation, self).prepare(terrain, is_quick)
|
||||||
|
|
||||||
conflict = Conflict.transport_conflict(
|
conflict = Conflict.transport_conflict(
|
||||||
attacker=self.mission.country(self.attacker_name),
|
attacker=self.current_mission.country(self.attacker_name),
|
||||||
defender=self.mission.country(self.defender_name),
|
defender=self.current_mission.country(self.defender_name),
|
||||||
from_cp=self.from_cp,
|
from_cp=self.from_cp,
|
||||||
to_cp=self.to_cp,
|
to_cp=self.to_cp,
|
||||||
theater=self.game.theater
|
theater=self.game.theater
|
||||||
)
|
)
|
||||||
|
|
||||||
self.initialize(mission=self.mission,
|
self.initialize(mission=self.current_mission,
|
||||||
conflict=conflict)
|
conflict=conflict)
|
||||||
|
|
||||||
def generate(self):
|
def generate(self):
|
||||||
self.airgen.generate_passenger_transport(*flight_arguments(self.transport), at=self.attackers_starting_position)
|
self.airgen.generate_passenger_transport(*assigned_units_split(self.transport), at=self.attackers_starting_position)
|
||||||
|
|
||||||
self.armorgen.generate_passengers(count=6)
|
self.armorgen.generate_passengers(count=6)
|
||||||
self.aagen.generate_at_defenders_location(self.aa)
|
self.aagen.generate_at_defenders_location(self.aa)
|
||||||
@@ -46,6 +36,7 @@ class InfantryTransportOperation(Operation):
|
|||||||
|
|
||||||
self.briefinggen.title = "Infantry transport"
|
self.briefinggen.title = "Infantry transport"
|
||||||
self.briefinggen.description = "Helicopter operation to transport infantry troops from the base to the front line. Lowers target strength"
|
self.briefinggen.description = "Helicopter operation to transport infantry troops from the base to the front line. Lowers target strength"
|
||||||
|
self.briefinggen.append_waypoint("DROP POINT")
|
||||||
|
|
||||||
# TODO: horrible, horrible hack
|
# TODO: horrible, horrible hack
|
||||||
# this will disable vehicle activation triggers,
|
# this will disable vehicle activation triggers,
|
||||||
|
|||||||
@@ -1,25 +1,15 @@
|
|||||||
from dcs.terrain import Terrain
|
from game.db import assigned_units_split
|
||||||
|
|
||||||
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.airsupportgen import *
|
|
||||||
from gen.visualgen import *
|
|
||||||
from gen.conflictgen import Conflict
|
|
||||||
|
|
||||||
from .operation import *
|
from .operation import *
|
||||||
|
|
||||||
|
|
||||||
class InsurgentAttackOperation(Operation):
|
class InsurgentAttackOperation(Operation):
|
||||||
strikegroup = None # type: FlightDict
|
strikegroup = None # type: db.AssignedUnitsDict
|
||||||
target = None # type: db.ArmorDict
|
target = None # type: db.ArmorDict
|
||||||
|
|
||||||
def setup(self,
|
def setup(self,
|
||||||
target: db.ArmorDict,
|
target: db.ArmorDict,
|
||||||
strikegroup: FlightDict):
|
strikegroup: db.AssignedUnitsDict):
|
||||||
self.strikegroup = strikegroup
|
self.strikegroup = strikegroup
|
||||||
self.target = target
|
self.target = target
|
||||||
|
|
||||||
@@ -27,21 +17,22 @@ class InsurgentAttackOperation(Operation):
|
|||||||
super(InsurgentAttackOperation, self).prepare(terrain, is_quick)
|
super(InsurgentAttackOperation, self).prepare(terrain, is_quick)
|
||||||
|
|
||||||
conflict = Conflict.ground_attack_conflict(
|
conflict = Conflict.ground_attack_conflict(
|
||||||
attacker=self.mission.country(self.attacker_name),
|
attacker=self.current_mission.country(self.attacker_name),
|
||||||
defender=self.mission.country(self.defender_name),
|
defender=self.current_mission.country(self.defender_name),
|
||||||
from_cp=self.from_cp,
|
from_cp=self.from_cp,
|
||||||
to_cp=self.to_cp,
|
to_cp=self.to_cp,
|
||||||
theater=self.game.theater
|
theater=self.game.theater
|
||||||
)
|
)
|
||||||
|
|
||||||
self.initialize(mission=self.mission,
|
self.initialize(mission=self.current_mission,
|
||||||
conflict=conflict)
|
conflict=conflict)
|
||||||
|
|
||||||
def generate(self):
|
def generate(self):
|
||||||
self.airgen.generate_defense(*flight_arguments(self.strikegroup), at=self.defenders_starting_position)
|
self.airgen.generate_defenders_cas(*assigned_units_split(self.strikegroup), at=self.defenders_starting_position)
|
||||||
self.armorgen.generate(self.target, {})
|
self.armorgen.generate(self.target, {})
|
||||||
|
|
||||||
self.briefinggen.title = "Destroy insurgents"
|
self.briefinggen.title = "Destroy insurgents"
|
||||||
self.briefinggen.description = "Destroy vehicles of insurgents in close proximity of the friendly base. Be advised that your flight will not attack anything until you explicitly tell them so by comms menu."
|
self.briefinggen.description = "Destroy vehicles of insurgents in close proximity of the friendly base. Be advised that your flight will not attack anything until you explicitly tell them so by comms menu."
|
||||||
|
self.briefinggen.append_waypoint("TARGET")
|
||||||
|
|
||||||
super(InsurgentAttackOperation, self).generate()
|
super(InsurgentAttackOperation, self).generate()
|
||||||
|
|||||||
@@ -1,22 +1,24 @@
|
|||||||
from dcs.terrain import Terrain
|
from game.db import assigned_units_split
|
||||||
|
|
||||||
from gen import *
|
|
||||||
from .operation import *
|
from .operation import *
|
||||||
|
|
||||||
|
|
||||||
class InterceptOperation(Operation):
|
class InterceptOperation(Operation):
|
||||||
escort = None # type: FlightDict
|
location = None # type: Point
|
||||||
|
escort = None # type: db.AssignedUnitsDict
|
||||||
transport = None # type: db.PlaneDict
|
transport = None # type: db.PlaneDict
|
||||||
interceptors = None # type: FlightDict
|
interceptors = None # type: db.AssignedUnitsDict
|
||||||
airdefense = None # type: db.AirDefenseDict
|
airdefense = None # type: db.AirDefenseDict
|
||||||
|
|
||||||
trigger_radius = TRIGGER_RADIUS_LARGE
|
trigger_radius = TRIGGER_RADIUS_LARGE
|
||||||
|
|
||||||
def setup(self,
|
def setup(self,
|
||||||
escort: FlightDict,
|
location: Point,
|
||||||
|
escort: db.AssignedUnitsDict,
|
||||||
transport: db.PlaneDict,
|
transport: db.PlaneDict,
|
||||||
airdefense: db.AirDefenseDict,
|
airdefense: db.AirDefenseDict,
|
||||||
interceptors: FlightDict):
|
interceptors: db.AssignedUnitsDict):
|
||||||
|
self.location = location
|
||||||
self.escort = escort
|
self.escort = escort
|
||||||
self.transport = transport
|
self.transport = transport
|
||||||
self.airdefense = airdefense
|
self.airdefense = airdefense
|
||||||
@@ -29,35 +31,35 @@ class InterceptOperation(Operation):
|
|||||||
self.attackers_starting_position = None
|
self.attackers_starting_position = None
|
||||||
|
|
||||||
conflict = Conflict.intercept_conflict(
|
conflict = Conflict.intercept_conflict(
|
||||||
attacker=self.mission.country(self.attacker_name),
|
attacker=self.current_mission.country(self.attacker_name),
|
||||||
defender=self.mission.country(self.defender_name),
|
defender=self.current_mission.country(self.defender_name),
|
||||||
|
position=self.location,
|
||||||
from_cp=self.from_cp,
|
from_cp=self.from_cp,
|
||||||
to_cp=self.to_cp,
|
to_cp=self.to_cp,
|
||||||
theater=self.game.theater
|
theater=self.game.theater
|
||||||
)
|
)
|
||||||
|
|
||||||
self.initialize(mission=self.mission,
|
self.initialize(mission=self.current_mission,
|
||||||
conflict=conflict)
|
conflict=conflict)
|
||||||
|
|
||||||
def generate(self):
|
def generate(self):
|
||||||
for global_cp in self.game.theater.controlpoints:
|
if self.is_player_attack:
|
||||||
if not global_cp.is_global:
|
self.prepare_carriers(db.unitdict_from(self.interceptors))
|
||||||
continue
|
|
||||||
|
|
||||||
ship = self.shipgen.generate_carrier(type=db.find_unittype(Carriage, self.game.player)[0],
|
|
||||||
country=self.game.player,
|
|
||||||
at=global_cp.at)
|
|
||||||
|
|
||||||
if global_cp == self.from_cp and not self.is_quick:
|
|
||||||
self.attackers_starting_position = ship
|
|
||||||
|
|
||||||
self.airgen.generate_transport(self.transport, self.to_cp.at)
|
self.airgen.generate_transport(self.transport, self.to_cp.at)
|
||||||
self.airgen.generate_defenders_escort(*flight_arguments(self.escort), at=self.defenders_starting_position)
|
self.airgen.generate_defenders_escort(*assigned_units_split(self.escort), at=self.defenders_starting_position)
|
||||||
|
|
||||||
self.airgen.generate_interception(*flight_arguments(self.interceptors), at=self.attackers_starting_position)
|
self.airgen.generate_interception(*assigned_units_split(self.interceptors), at=self.attackers_starting_position)
|
||||||
|
|
||||||
self.briefinggen.title = "Air Intercept"
|
self.briefinggen.title = "Air Intercept"
|
||||||
self.briefinggen.description = "Intercept enemy supply transport aircraft. Escort will also be present if there are available planes on the base. Operation will be considered successful if most of the targets are destroyed, lowering targets strength as a result"
|
|
||||||
|
if self.game.player == self.attacker_name:
|
||||||
|
self.briefinggen.description = "Intercept enemy supply transport aircraft. Escort will also be present if there are available planes on the base. Operation will be considered successful if most of the targets are destroyed, lowering targets strength as a result"
|
||||||
|
self.briefinggen.append_waypoint("TARGET")
|
||||||
|
for unit_type, count in self.transport.items():
|
||||||
|
self.briefinggen.append_target("{} ({})".format(db.unit_type_name(unit_type), count))
|
||||||
|
else:
|
||||||
|
self.briefinggen.description = "Escort friendly supply transport aircraft. Operation will be considered failed if most of the targets are destroyed, lowering CP strength as a result"
|
||||||
|
|
||||||
super(InterceptOperation, self).generate()
|
super(InterceptOperation, self).generate()
|
||||||
|
|
||||||
|
|||||||
@@ -1,19 +1,21 @@
|
|||||||
from dcs.terrain import Terrain
|
from game.db import assigned_units_split
|
||||||
|
|
||||||
from gen import *
|
|
||||||
from .operation import *
|
from .operation import *
|
||||||
|
|
||||||
|
|
||||||
class NavalInterceptionOperation(Operation):
|
class NavalInterceptionOperation(Operation):
|
||||||
strikegroup = None # type: FlightDict
|
location = None # type: Point
|
||||||
interceptors = None # type: FlightDict
|
strikegroup = None # type: db.AssignedUnitsDict
|
||||||
|
interceptors = None # type: db.AssignedUnitsDict
|
||||||
targets = None # type: db.ShipDict
|
targets = None # type: db.ShipDict
|
||||||
trigger_radius = TRIGGER_RADIUS_LARGE
|
trigger_radius = TRIGGER_RADIUS_LARGE
|
||||||
|
|
||||||
def setup(self,
|
def setup(self,
|
||||||
strikegroup: FlightDict,
|
location: Point,
|
||||||
interceptors: FlightDict,
|
strikegroup: db.AssignedUnitsDict,
|
||||||
|
interceptors: db.AssignedUnitsDict,
|
||||||
targets: db.ShipDict):
|
targets: db.ShipDict):
|
||||||
|
self.location = location
|
||||||
self.strikegroup = strikegroup
|
self.strikegroup = strikegroup
|
||||||
self.interceptors = interceptors
|
self.interceptors = interceptors
|
||||||
self.targets = targets
|
self.targets = targets
|
||||||
@@ -24,32 +26,42 @@ class NavalInterceptionOperation(Operation):
|
|||||||
self.attackers_starting_position = None
|
self.attackers_starting_position = None
|
||||||
|
|
||||||
conflict = Conflict.naval_intercept_conflict(
|
conflict = Conflict.naval_intercept_conflict(
|
||||||
attacker=self.mission.country(self.attacker_name),
|
attacker=self.current_mission.country(self.attacker_name),
|
||||||
defender=self.mission.country(self.defender_name),
|
defender=self.current_mission.country(self.defender_name),
|
||||||
|
position=self.location,
|
||||||
from_cp=self.from_cp,
|
from_cp=self.from_cp,
|
||||||
to_cp=self.to_cp,
|
to_cp=self.to_cp,
|
||||||
theater=self.game.theater
|
theater=self.game.theater
|
||||||
)
|
)
|
||||||
|
|
||||||
self.initialize(self.mission, conflict)
|
self.initialize(self.current_mission, conflict)
|
||||||
|
|
||||||
def generate(self):
|
def generate(self):
|
||||||
|
if self.is_player_attack:
|
||||||
|
self.prepare_carriers(db.unitdict_from(self.strikegroup))
|
||||||
|
|
||||||
target_groups = self.shipgen.generate_cargo(units=self.targets)
|
target_groups = self.shipgen.generate_cargo(units=self.targets)
|
||||||
|
|
||||||
self.airgen.generate_ship_strikegroup(
|
self.airgen.generate_ship_strikegroup(
|
||||||
*flight_arguments(self.strikegroup),
|
*assigned_units_split(self.strikegroup),
|
||||||
target_groups=target_groups,
|
target_groups=target_groups,
|
||||||
at=self.attackers_starting_position
|
at=self.attackers_starting_position
|
||||||
)
|
)
|
||||||
|
|
||||||
if self.interceptors:
|
if self.interceptors:
|
||||||
self.airgen.generate_defense(
|
self.airgen.generate_defense(
|
||||||
*flight_arguments(self.interceptors),
|
*assigned_units_split(self.interceptors),
|
||||||
at=self.defenders_starting_position
|
at=self.defenders_starting_position
|
||||||
)
|
)
|
||||||
|
|
||||||
self.briefinggen.title = "Naval Intercept"
|
self.briefinggen.title = "Naval Intercept"
|
||||||
self.briefinggen.description = "Destroy supply transport ships. Lowers target strength. Be advised that your flight will not attack anything until you explicitly tell them so by comms menu."
|
if self.game.player == self.attacker_name:
|
||||||
|
self.briefinggen.description = "Destroy supply transport ships. Lowers target strength. Be advised that your flight will not attack anything until you explicitly tell them so by comms menu."
|
||||||
|
for unit_type, count in self.targets.items():
|
||||||
|
self.briefinggen.append_target("{} ({})".format(db.unit_type_name(unit_type), count))
|
||||||
|
else:
|
||||||
|
self.briefinggen.description = "Protect supply transport ships."
|
||||||
|
self.briefinggen.append_waypoint("TARGET")
|
||||||
|
|
||||||
super(NavalInterceptionOperation, self).generate()
|
super(NavalInterceptionOperation, self).generate()
|
||||||
|
|
||||||
|
|||||||
@@ -1,32 +1,19 @@
|
|||||||
import typing
|
|
||||||
|
|
||||||
from dcs.lua.parse import loads
|
from dcs.lua.parse import loads
|
||||||
from dcs.unittype import UnitType
|
|
||||||
|
|
||||||
from userdata.debriefing import *
|
from userdata.debriefing import *
|
||||||
|
|
||||||
from theater import *
|
|
||||||
from gen import *
|
from gen import *
|
||||||
|
|
||||||
FlightDict = typing.Dict[typing.Type[UnitType], typing.Tuple[int, int]]
|
TANKER_CALLSIGNS = ["Texaco", "Arco", "Shell"]
|
||||||
|
|
||||||
|
|
||||||
def flight_arguments(fd: FlightDict) -> typing.Tuple[db.PlaneDict, db.PlaneDict]:
|
|
||||||
return {k: v1 for k, (v1, v2) in fd.items()}, {k: v2 for k, (v1, v2) in fd.items()},
|
|
||||||
|
|
||||||
|
|
||||||
def flight_dict_from(d: db.PlaneDict) -> FlightDict:
|
|
||||||
return {k: (v, 0) for k, v in d.items()}
|
|
||||||
|
|
||||||
|
|
||||||
def dict_from_flight(fd: FlightDict) -> db.Dict:
|
|
||||||
return {k: v1 for k, (v1, v2) in fd.items()}
|
|
||||||
|
|
||||||
|
|
||||||
class Operation:
|
class Operation:
|
||||||
attackers_starting_position = None # type: db.StartingPosition
|
attackers_starting_position = None # type: db.StartingPosition
|
||||||
defenders_starting_position = None # type: db.StartingPosition
|
defenders_starting_position = None # type: db.StartingPosition
|
||||||
mission = None # type: dcs.Mission
|
|
||||||
|
current_mission = None # type: dcs.Mission
|
||||||
|
regular_mission = None # type: dcs.Mission
|
||||||
|
quick_mission = None # type: dcs.Mission
|
||||||
conflict = None # type: Conflict
|
conflict = None # type: Conflict
|
||||||
armorgen = None # type: ArmorConflictGenerator
|
armorgen = None # type: ArmorConflictGenerator
|
||||||
airgen = None # type: AircraftConflictGenerator
|
airgen = None # type: AircraftConflictGenerator
|
||||||
@@ -39,22 +26,26 @@ class Operation:
|
|||||||
envgen = None # type: EnvironmentGenerator
|
envgen = None # type: EnvironmentGenerator
|
||||||
groundobjectgen = None # type: GroundObjectsGenerator
|
groundobjectgen = None # type: GroundObjectsGenerator
|
||||||
briefinggen = None # type: BriefingGenerator
|
briefinggen = None # type: BriefingGenerator
|
||||||
|
forcedoptionsgen = None # type: ForcedOptionsGenerator
|
||||||
|
|
||||||
environment_settings = None
|
environment_settings = None
|
||||||
trigger_radius = TRIGGER_RADIUS_MEDIUM
|
trigger_radius = TRIGGER_RADIUS_MEDIUM
|
||||||
is_quick = None
|
is_quick = None
|
||||||
is_awacs_enabled = False
|
is_awacs_enabled = False
|
||||||
|
ca_slots = 0
|
||||||
|
|
||||||
def __init__(self,
|
def __init__(self,
|
||||||
game,
|
game,
|
||||||
attacker_name: str,
|
attacker_name: str,
|
||||||
defender_name: str,
|
defender_name: str,
|
||||||
from_cp: ControlPoint,
|
from_cp: ControlPoint,
|
||||||
|
departure_cp: ControlPoint,
|
||||||
to_cp: ControlPoint = None):
|
to_cp: ControlPoint = None):
|
||||||
self.game = game
|
self.game = game
|
||||||
self.attacker_name = attacker_name
|
self.attacker_name = attacker_name
|
||||||
self.defender_name = defender_name
|
self.defender_name = defender_name
|
||||||
self.from_cp = from_cp
|
self.from_cp = from_cp
|
||||||
|
self.departure_cp = departure_cp
|
||||||
self.to_cp = to_cp
|
self.to_cp = to_cp
|
||||||
self.is_quick = False
|
self.is_quick = False
|
||||||
|
|
||||||
@@ -64,10 +55,13 @@ class Operation:
|
|||||||
def is_successfull(self, debriefing: Debriefing) -> bool:
|
def is_successfull(self, debriefing: Debriefing) -> bool:
|
||||||
return True
|
return True
|
||||||
|
|
||||||
def initialize(self, mission: Mission, conflict: Conflict):
|
@property
|
||||||
self.mission = mission
|
def is_player_attack(self) -> bool:
|
||||||
self.conflict = conflict
|
return self.from_cp.captured
|
||||||
|
|
||||||
|
def initialize(self, mission: Mission, conflict: Conflict):
|
||||||
|
self.current_mission = mission
|
||||||
|
self.conflict = conflict
|
||||||
self.armorgen = ArmorConflictGenerator(mission, conflict)
|
self.armorgen = ArmorConflictGenerator(mission, conflict)
|
||||||
self.airgen = AircraftConflictGenerator(mission, conflict, self.game.settings)
|
self.airgen = AircraftConflictGenerator(mission, conflict, self.game.settings)
|
||||||
self.aagen = AAConflictGenerator(mission, conflict)
|
self.aagen = AAConflictGenerator(mission, conflict)
|
||||||
@@ -76,6 +70,7 @@ class Operation:
|
|||||||
self.triggersgen = TriggersGenerator(mission, conflict, self.game)
|
self.triggersgen = TriggersGenerator(mission, conflict, self.game)
|
||||||
self.visualgen = VisualGenerator(mission, conflict, self.game)
|
self.visualgen = VisualGenerator(mission, conflict, self.game)
|
||||||
self.envgen = EnviromentGenerator(mission, conflict, self.game)
|
self.envgen = EnviromentGenerator(mission, conflict, self.game)
|
||||||
|
self.forcedoptionsgen = ForcedOptionsGenerator(mission, conflict, self.game)
|
||||||
self.groundobjectgen = GroundObjectsGenerator(mission, conflict, self.game)
|
self.groundobjectgen = GroundObjectsGenerator(mission, conflict, self.game)
|
||||||
self.briefinggen = BriefingGenerator(mission, conflict, self.game)
|
self.briefinggen = BriefingGenerator(mission, conflict, self.game)
|
||||||
|
|
||||||
@@ -87,25 +82,51 @@ class Operation:
|
|||||||
with open("resources/default_options.lua", "r") as f:
|
with open("resources/default_options.lua", "r") as f:
|
||||||
options_dict = loads(f.read())["options"]
|
options_dict = loads(f.read())["options"]
|
||||||
|
|
||||||
self.mission = dcs.Mission(terrain)
|
self.current_mission = dcs.Mission(terrain)
|
||||||
self.mission.options.load_from_dict(options_dict)
|
if is_quick:
|
||||||
|
self.quick_mission = self.current_mission
|
||||||
|
else:
|
||||||
|
self.regular_mission = self.current_mission
|
||||||
|
|
||||||
|
self.current_mission.options.load_from_dict(options_dict)
|
||||||
self.is_quick = is_quick
|
self.is_quick = is_quick
|
||||||
|
|
||||||
if is_quick:
|
if is_quick:
|
||||||
self.attackers_starting_position = None
|
self.attackers_starting_position = None
|
||||||
self.defenders_starting_position = None
|
self.defenders_starting_position = None
|
||||||
else:
|
else:
|
||||||
self.attackers_starting_position = self.from_cp.at
|
self.attackers_starting_position = self.departure_cp.at
|
||||||
self.defenders_starting_position = self.to_cp.at
|
self.defenders_starting_position = self.to_cp.at
|
||||||
|
|
||||||
def generate(self):
|
def prepare_carriers(self, for_units: db.UnitsDict):
|
||||||
self.visualgen.generate()
|
if not self.departure_cp.is_global:
|
||||||
|
return
|
||||||
|
|
||||||
|
ship = self.shipgen.generate_carrier(for_units=[t for t, c in for_units.items() if c > 0],
|
||||||
|
country=self.game.player,
|
||||||
|
at=self.departure_cp.at)
|
||||||
|
|
||||||
|
if not self.is_quick:
|
||||||
|
if not self.to_cp.captured:
|
||||||
|
self.attackers_starting_position = ship
|
||||||
|
else:
|
||||||
|
self.defenders_starting_position = ship
|
||||||
|
|
||||||
|
def generate(self):
|
||||||
# air support
|
# air support
|
||||||
self.airsupportgen.generate(self.is_awacs_enabled)
|
self.airsupportgen.generate(self.is_awacs_enabled)
|
||||||
self.briefinggen.append_frequency("Tanker", "10X/240 MHz FM")
|
for i, tanker_type in enumerate(self.airsupportgen.generated_tankers):
|
||||||
|
self.briefinggen.append_frequency("Tanker {} ({})".format(TANKER_CALLSIGNS[i], tanker_type), "{}X/{} MHz AM".format(97+i, 130+i))
|
||||||
|
|
||||||
if self.is_awacs_enabled:
|
if self.is_awacs_enabled:
|
||||||
self.briefinggen.append_frequency("AWACS", "244 MHz FM")
|
self.briefinggen.append_frequency("AWACS", "133 MHz AM")
|
||||||
|
|
||||||
|
# combined arms
|
||||||
|
self.current_mission.groundControl.pilot_can_control_vehicles = self.ca_slots > 0
|
||||||
|
if self.game.player in [country.name for country in self.current_mission.coalition["blue"].countries.values()]:
|
||||||
|
self.current_mission.groundControl.blue_tactical_commander = self.ca_slots
|
||||||
|
else:
|
||||||
|
self.current_mission.groundControl.red_tactical_commander = self.ca_slots
|
||||||
|
|
||||||
# ground infrastructure
|
# ground infrastructure
|
||||||
self.groundobjectgen.generate()
|
self.groundobjectgen.generate()
|
||||||
@@ -128,10 +149,17 @@ class Operation:
|
|||||||
else:
|
else:
|
||||||
self.envgen.load(self.environment_settings)
|
self.envgen.load(self.environment_settings)
|
||||||
|
|
||||||
|
# options
|
||||||
|
self.forcedoptionsgen.generate()
|
||||||
|
|
||||||
# main frequencies
|
# main frequencies
|
||||||
self.briefinggen.append_frequency("Flight", "251 MHz FM")
|
self.briefinggen.append_frequency("Flight", "251 MHz AM")
|
||||||
if self.conflict.from_cp.is_global or self.conflict.to_cp.is_global:
|
if self.departure_cp.is_global or self.conflict.to_cp.is_global:
|
||||||
self.briefinggen.append_frequency("Carrier", "20X/ICLS CHAN1")
|
self.briefinggen.append_frequency("Carrier", "20X/ICLS CHAN1")
|
||||||
|
|
||||||
# briefing
|
# briefing
|
||||||
self.briefinggen.generate()
|
self.briefinggen.generate()
|
||||||
|
|
||||||
|
# visuals
|
||||||
|
self.visualgen.generate()
|
||||||
|
|
||||||
|
|||||||
@@ -1,28 +1,23 @@
|
|||||||
from dcs.terrain import Terrain
|
from game.db import assigned_units_split
|
||||||
|
|
||||||
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.airsupportgen import *
|
|
||||||
from gen.visualgen import *
|
|
||||||
from gen.conflictgen import Conflict
|
|
||||||
|
|
||||||
from .operation import *
|
from .operation import *
|
||||||
|
|
||||||
|
|
||||||
class StrikeOperation(Operation):
|
class StrikeOperation(Operation):
|
||||||
strikegroup = None # type: FlightDict
|
strikegroup = None # type: db.AssignedUnitsDict
|
||||||
escort = None # type: FlightDict
|
sead = None # type: db.AssignedUnitsDict
|
||||||
interceptors = None # type: FlightDict
|
escort = None # type: db.AssignedUnitsDict
|
||||||
|
interceptors = None # type: db.AssignedUnitsDict
|
||||||
|
|
||||||
|
trigger_radius = TRIGGER_RADIUS_ALL_MAP
|
||||||
|
|
||||||
def setup(self,
|
def setup(self,
|
||||||
strikegroup: FlightDict,
|
strikegroup: db.AssignedUnitsDict,
|
||||||
escort: FlightDict,
|
sead: db.AssignedUnitsDict,
|
||||||
interceptors: FlightDict):
|
escort: db.AssignedUnitsDict,
|
||||||
|
interceptors: db.AssignedUnitsDict):
|
||||||
self.strikegroup = strikegroup
|
self.strikegroup = strikegroup
|
||||||
|
self.sead = sead
|
||||||
self.escort = escort
|
self.escort = escort
|
||||||
self.interceptors = interceptors
|
self.interceptors = interceptors
|
||||||
|
|
||||||
@@ -34,40 +29,65 @@ class StrikeOperation(Operation):
|
|||||||
self.attackers_starting_position = None
|
self.attackers_starting_position = None
|
||||||
|
|
||||||
conflict = Conflict.strike_conflict(
|
conflict = Conflict.strike_conflict(
|
||||||
attacker=self.mission.country(self.attacker_name),
|
attacker=self.current_mission.country(self.attacker_name),
|
||||||
defender=self.mission.country(self.defender_name),
|
defender=self.current_mission.country(self.defender_name),
|
||||||
from_cp=self.from_cp,
|
from_cp=self.from_cp,
|
||||||
to_cp=self.to_cp,
|
to_cp=self.to_cp,
|
||||||
theater=self.game.theater
|
theater=self.game.theater
|
||||||
)
|
)
|
||||||
|
|
||||||
self.initialize(mission=self.mission,
|
self.initialize(mission=self.current_mission,
|
||||||
conflict=conflict)
|
conflict=conflict)
|
||||||
|
|
||||||
def generate(self):
|
def generate(self):
|
||||||
targets = [] # type: typing.List[typing.Tuple[str, Point]]
|
self.prepare_carriers(db.unitdict_merge(db.unitdict_from(self.strikegroup), db.unitdict_from(self.escort)))
|
||||||
|
|
||||||
|
targets = [] # type: typing.List[typing.Tuple[str, str, Point]]
|
||||||
|
sead_targets = [] # type: typing.List[typing.Tuple[str, str, Point]]
|
||||||
category_counters = {} # type: typing.Dict[str, int]
|
category_counters = {} # type: typing.Dict[str, int]
|
||||||
processed_groups = []
|
processed_groups = []
|
||||||
|
|
||||||
for object in self.to_cp.ground_objects:
|
for object in self.to_cp.ground_objects:
|
||||||
if object.group_identifier in processed_groups:
|
if object.group_identifier in processed_groups:
|
||||||
continue
|
continue
|
||||||
|
|
||||||
processed_groups.append(object.group_identifier)
|
processed_groups.append(object.group_identifier)
|
||||||
|
|
||||||
category_counters[object.category] = category_counters.get(object.category, 0) + 1
|
category_counters[object.category] = category_counters.get(object.category, 0) + 1
|
||||||
markpoint_name = "{}{}".format(object.name_abbrev, category_counters[object.category])
|
markpoint_name = "{}{}".format(object.name_abbrev, category_counters[object.category])
|
||||||
targets.append((markpoint_name, object.position))
|
|
||||||
self.briefinggen.append_target(str(object), markpoint_name)
|
|
||||||
|
|
||||||
targets.sort(key=lambda x: self.from_cp.position.distance_to_point(x[1]))
|
if object.category == "aa":
|
||||||
|
sead_targets.append((str(object), markpoint_name, object.position))
|
||||||
|
|
||||||
self.airgen.generate_ground_attack_strikegroup(*flight_arguments(self.strikegroup),
|
targets.append((str(object), markpoint_name, object.position))
|
||||||
targets=targets,
|
|
||||||
at=self.attackers_starting_position)
|
|
||||||
|
|
||||||
self.airgen.generate_attackers_escort(*flight_arguments(self.escort), at=self.attackers_starting_position)
|
targets.sort(key=lambda x: self.from_cp.position.distance_to_point(x[2]))
|
||||||
|
|
||||||
self.airgen.generate_barcap(*flight_arguments(self.interceptors), at=self.defenders_starting_position)
|
for (name, markpoint_name, _) in targets:
|
||||||
|
self.briefinggen.append_waypoint("TARGET {} (TP {})".format(str(name), markpoint_name))
|
||||||
|
|
||||||
|
planes_flights = {k: v for k, v in self.strikegroup.items() if k in plane_map.values()}
|
||||||
|
self.airgen.generate_ground_attack_strikegroup(*assigned_units_split(planes_flights),
|
||||||
|
targets=[(mp, pos) for (n, mp, pos) in targets],
|
||||||
|
at=self.attackers_starting_position,
|
||||||
|
escort=len(self.sead) == 0)
|
||||||
|
|
||||||
|
self.airgen.generate_sead_strikegroup(*assigned_units_split(self.sead),
|
||||||
|
targets=[(mp, pos) for (n, mp, pos) in sead_targets],
|
||||||
|
at=self.attackers_starting_position,
|
||||||
|
escort=len(self.sead) > 0)
|
||||||
|
|
||||||
|
heli_flights = {k: v for k, v in self.strikegroup.items() if k in helicopters.helicopter_map.values()}
|
||||||
|
if heli_flights:
|
||||||
|
self.briefinggen.append_frequency("FARP", "127.5 MHz AM")
|
||||||
|
for farp, dict in zip(self.groundobjectgen.generate_farps(sum([x[0] for x in heli_flights.values()])),
|
||||||
|
db.assignedunits_split_to_count(heli_flights, self.groundobjectgen.FARP_CAPACITY)):
|
||||||
|
self.airgen.generate_ground_attack_strikegroup(*assigned_units_split(dict),
|
||||||
|
targets=[(mp, pos) for (n, mp, pos) in targets],
|
||||||
|
at=farp,
|
||||||
|
escort=len(planes_flights) == 0)
|
||||||
|
|
||||||
|
self.airgen.generate_attackers_escort(*assigned_units_split(self.escort), at=self.attackers_starting_position)
|
||||||
|
self.airgen.generate_barcap(*assigned_units_split(self.interceptors), at=self.defenders_starting_position)
|
||||||
|
|
||||||
self.briefinggen.title = "Strike"
|
self.briefinggen.title = "Strike"
|
||||||
self.briefinggen.description = "Destroy infrastructure assets and military supplies in the region. Each building destroyed will lower targets strength."
|
self.briefinggen.description = "Destroy infrastructure assets and military supplies in the region. Each building destroyed will lower targets strength."
|
||||||
|
|||||||
@@ -2,8 +2,12 @@
|
|||||||
class Settings:
|
class Settings:
|
||||||
player_skill = "Good"
|
player_skill = "Good"
|
||||||
enemy_skill = "Average"
|
enemy_skill = "Average"
|
||||||
|
enemy_vehicle_skill = "Average"
|
||||||
|
map_coalition_visibility = "All Units"
|
||||||
|
labels = "Full"
|
||||||
only_player_takeoff = True
|
only_player_takeoff = True
|
||||||
night_disabled = False
|
night_disabled = False
|
||||||
|
|
||||||
multiplier = 1
|
multiplier = 1
|
||||||
sams = True
|
sams = True
|
||||||
cold_start = False
|
cold_start = False
|
||||||
|
|||||||
@@ -9,6 +9,7 @@ from .triggergen import *
|
|||||||
from .environmentgen import *
|
from .environmentgen import *
|
||||||
from .groundobjectsgen import *
|
from .groundobjectsgen import *
|
||||||
from .briefinggen import *
|
from .briefinggen import *
|
||||||
|
from .forcedoptionsgen import *
|
||||||
|
|
||||||
from . import naming
|
from . import naming
|
||||||
|
|
||||||
|
|||||||
10
gen/aaa.py
@@ -4,7 +4,8 @@ from .naming import *
|
|||||||
from dcs.mission import *
|
from dcs.mission import *
|
||||||
|
|
||||||
DISTANCE_FACTOR = 0.5, 1
|
DISTANCE_FACTOR = 0.5, 1
|
||||||
EXTRA_AA_MIN_DISTANCE = 35000
|
EXTRA_AA_MIN_DISTANCE = 50000
|
||||||
|
EXTRA_AA_MAX_DISTANCE = 150000
|
||||||
EXTRA_AA_POSITION_FROM_CP = 550
|
EXTRA_AA_POSITION_FROM_CP = 550
|
||||||
|
|
||||||
|
|
||||||
@@ -58,7 +59,12 @@ class ExtraAAConflictGenerator:
|
|||||||
if cp.position.distance_to_point(self.conflict.from_cp.position) < EXTRA_AA_MIN_DISTANCE:
|
if cp.position.distance_to_point(self.conflict.from_cp.position) < EXTRA_AA_MIN_DISTANCE:
|
||||||
continue
|
continue
|
||||||
|
|
||||||
print("generated extra aa for {}".format(cp))
|
if cp.position.distance_to_point(self.conflict.to_cp.position) < EXTRA_AA_MIN_DISTANCE:
|
||||||
|
continue
|
||||||
|
|
||||||
|
if cp.position.distance_to_point(self.conflict.position) > EXTRA_AA_MAX_DISTANCE:
|
||||||
|
continue
|
||||||
|
|
||||||
country_name = cp.captured and self.player_name or self.enemy_name
|
country_name = cp.captured and self.player_name or self.enemy_name
|
||||||
position = cp.position.point_from_heading(0, EXTRA_AA_POSITION_FROM_CP)
|
position = cp.position.point_from_heading(0, EXTRA_AA_POSITION_FROM_CP)
|
||||||
|
|
||||||
|
|||||||
190
gen/aircraft.py
@@ -17,23 +17,23 @@ ESCORT_ENGAGEMENT_MAX_DIST = 100000
|
|||||||
WORKAROUND_WAYP_DIST = 1000
|
WORKAROUND_WAYP_DIST = 1000
|
||||||
|
|
||||||
WARM_START_HELI_AIRSPEED = 120
|
WARM_START_HELI_AIRSPEED = 120
|
||||||
WARM_START_HELI_ALT = 1000
|
WARM_START_HELI_ALT = 500
|
||||||
|
|
||||||
WARM_START_ALTITUDE = 3000
|
WARM_START_ALTITUDE = 3000
|
||||||
WARM_START_AIRSPEED = 550
|
WARM_START_AIRSPEED = 550
|
||||||
|
|
||||||
INTERCEPTION_ALT = 3000
|
|
||||||
INTERCEPTION_AIRSPEED = 1000
|
INTERCEPTION_AIRSPEED = 1000
|
||||||
BARCAP_RACETRACK_DISTANCE = 20000
|
BARCAP_RACETRACK_DISTANCE = 20000
|
||||||
|
|
||||||
ATTACK_CIRCLE_ALT = 5000
|
ATTACK_CIRCLE_ALT = 1000
|
||||||
ATTACK_CIRCLE_DURATION = 15
|
ATTACK_CIRCLE_DURATION = 15
|
||||||
|
|
||||||
CAS_ALTITUDE = 1000
|
CAS_ALTITUDE = 800
|
||||||
RTB_ALTITUDE = 1000
|
RTB_ALTITUDE = 800
|
||||||
HELI_ALT = 900
|
RTB_DISTANCE = 5000
|
||||||
|
HELI_ALT = 500
|
||||||
|
|
||||||
TRANSPORT_LANDING_ALT = 1000
|
TRANSPORT_LANDING_ALT = 2000
|
||||||
|
|
||||||
DEFENCE_ENGAGEMENT_MAX_DISTANCE = 60000
|
DEFENCE_ENGAGEMENT_MAX_DISTANCE = 60000
|
||||||
INTERCEPT_MAX_DISTANCE = 200000
|
INTERCEPT_MAX_DISTANCE = 200000
|
||||||
@@ -42,7 +42,7 @@ GROUP_VERTICAL_OFFSET = 300
|
|||||||
|
|
||||||
|
|
||||||
class AircraftConflictGenerator:
|
class AircraftConflictGenerator:
|
||||||
escort_targets = [] # type: typing.List[typing.Tuple[PlaneGroup, int]]
|
escort_targets = [] # type: typing.List[typing.Tuple[FlyingGroup, int]]
|
||||||
vertical_offset = None # type: int
|
vertical_offset = None # type: int
|
||||||
|
|
||||||
def __init__(self, mission: Mission, conflict: Conflict, settings: Settings):
|
def __init__(self, mission: Mission, conflict: Conflict, settings: Settings):
|
||||||
@@ -69,15 +69,21 @@ class AircraftConflictGenerator:
|
|||||||
else:
|
else:
|
||||||
client_count = 0
|
client_count = 0
|
||||||
|
|
||||||
|
if flying_type == F_14B:
|
||||||
|
# workaround since 2 and 3 tomcat collide on carrier
|
||||||
|
group_size = 2
|
||||||
|
else:
|
||||||
|
group_size = 4
|
||||||
|
|
||||||
while count > 0:
|
while count > 0:
|
||||||
group_size = min(count, 4)
|
group_size = min(count, group_size)
|
||||||
client_size = max(min(client_count, 4), 0)
|
client_size = max(min(client_count, group_size), 0)
|
||||||
|
|
||||||
yield (flying_type, group_size, client_size)
|
yield (flying_type, group_size, client_size)
|
||||||
count -= group_size
|
count -= group_size
|
||||||
client_count -= client_size
|
client_count -= client_size
|
||||||
|
|
||||||
def _setup_group(self, group: FlyingGroup, for_task: Task, client_count: int):
|
def _setup_group(self, group: FlyingGroup, for_task: typing.Type[Task], client_count: int):
|
||||||
did_load_loadout = False
|
did_load_loadout = False
|
||||||
unit_type = group.units[0].unit_type
|
unit_type = group.units[0].unit_type
|
||||||
if unit_type in db.PLANE_PAYLOAD_OVERRIDES:
|
if unit_type in db.PLANE_PAYLOAD_OVERRIDES:
|
||||||
@@ -113,6 +119,12 @@ class AircraftConflictGenerator:
|
|||||||
|
|
||||||
group.points[0].tasks.append(OptReactOnThreat(OptReactOnThreat.Values.EvadeFire))
|
group.points[0].tasks.append(OptReactOnThreat(OptReactOnThreat.Values.EvadeFire))
|
||||||
|
|
||||||
|
if unit_type in helicopters.helicopter_map.values():
|
||||||
|
print(unit_type)
|
||||||
|
group.set_frequency(127.5)
|
||||||
|
else:
|
||||||
|
group.set_frequency(251.0)
|
||||||
|
|
||||||
def _generate_at_airport(self, name: str, side: Country, unit_type: FlyingType, count: int, client_count: int, airport: Airport = None) -> FlyingGroup:
|
def _generate_at_airport(self, name: str, side: Country, unit_type: FlyingType, count: int, client_count: int, airport: Airport = None) -> FlyingGroup:
|
||||||
assert count > 0
|
assert count > 0
|
||||||
assert unit is not None
|
assert unit is not None
|
||||||
@@ -143,7 +155,7 @@ class AircraftConflictGenerator:
|
|||||||
pos = Point(at.x + random.randint(100, 1000), at.y + random.randint(100, 1000))
|
pos = Point(at.x + random.randint(100, 1000), at.y + random.randint(100, 1000))
|
||||||
|
|
||||||
logging.info("airgen: {} for {} at {} at {}".format(unit_type, side.id, alt, speed))
|
logging.info("airgen: {} for {} at {} at {}".format(unit_type, side.id, alt, speed))
|
||||||
return self.m.flight_group(
|
group = self.m.flight_group(
|
||||||
country=side,
|
country=side,
|
||||||
name=name,
|
name=name,
|
||||||
aircraft_type=unit_type,
|
aircraft_type=unit_type,
|
||||||
@@ -155,11 +167,14 @@ class AircraftConflictGenerator:
|
|||||||
start_type=self._start_type(),
|
start_type=self._start_type(),
|
||||||
group_size=count)
|
group_size=count)
|
||||||
|
|
||||||
def _generate_at_carrier(self, name: str, side: Country, unit_type: FlyingType, count: int, client_count: int, at: ShipGroup) -> FlyingGroup:
|
group.points[0].alt_type = "RADIO"
|
||||||
|
return group
|
||||||
|
|
||||||
|
def _generate_at_group(self, name: str, side: Country, unit_type: FlyingType, count: int, client_count: int, at: typing.Union[ShipGroup, StaticGroup]) -> FlyingGroup:
|
||||||
assert count > 0
|
assert count > 0
|
||||||
assert unit is not None
|
assert unit is not None
|
||||||
|
|
||||||
logging.info("airgen: {} for {} at carrier {}".format(unit_type, side.id, at))
|
logging.info("airgen: {} for {} at unit {}".format(unit_type, side.id, at))
|
||||||
return self.m.flight_group_from_unit(
|
return self.m.flight_group_from_unit(
|
||||||
country=side,
|
country=side,
|
||||||
name=name,
|
name=name,
|
||||||
@@ -172,10 +187,12 @@ class AircraftConflictGenerator:
|
|||||||
def _generate_group(self, name: str, side: Country, unit_type: FlyingType, count: int, client_count: int, at: db.StartingPosition):
|
def _generate_group(self, name: str, side: Country, unit_type: FlyingType, count: int, client_count: int, at: db.StartingPosition):
|
||||||
if isinstance(at, Point):
|
if isinstance(at, Point):
|
||||||
return self._generate_inflight(name, side, unit_type, count, client_count, at)
|
return self._generate_inflight(name, side, unit_type, count, client_count, at)
|
||||||
elif isinstance(at, ShipGroup):
|
elif isinstance(at, Group):
|
||||||
takeoff_ban = unit_type in db.CARRIER_TAKEOFF_BAN
|
takeoff_ban = unit_type in db.CARRIER_TAKEOFF_BAN
|
||||||
if not takeoff_ban:
|
ai_ban = client_count == 0 and self.settings.only_player_takeoff
|
||||||
return self._generate_at_carrier(name, side, unit_type, count, client_count, at)
|
|
||||||
|
if not takeoff_ban and not ai_ban:
|
||||||
|
return self._generate_at_group(name, side, unit_type, count, client_count, at)
|
||||||
else:
|
else:
|
||||||
return self._generate_inflight(name, side, unit_type, count, client_count, at.position)
|
return self._generate_inflight(name, side, unit_type, count, client_count, at.position)
|
||||||
elif issubclass(at, Airport):
|
elif issubclass(at, Airport):
|
||||||
@@ -191,15 +208,26 @@ class AircraftConflictGenerator:
|
|||||||
else:
|
else:
|
||||||
assert False
|
assert False
|
||||||
|
|
||||||
def _rtb_for(self, group: FlyingGroup, cp: ControlPoint, at: db.StartingPosition = None):
|
def _add_radio_waypoint(self, group: FlyingGroup, position, altitude: int, airspeed: int = 600):
|
||||||
group.add_waypoint(cp.position, RTB_ALTITUDE)
|
point = group.add_waypoint(position, altitude, airspeed)
|
||||||
|
point.alt_type = "RADIO"
|
||||||
|
return point
|
||||||
|
|
||||||
if isinstance(cp.at, Point):
|
def _rtb_for(self, group: FlyingGroup, cp: ControlPoint, at: db.StartingPosition = None):
|
||||||
pass
|
if not at:
|
||||||
elif isinstance(cp.at, ShipGroup):
|
at = cp.at
|
||||||
pass
|
position = at if isinstance(at, Point) else at.position
|
||||||
elif issubclass(cp.at, Airport):
|
|
||||||
group.land_at(cp.at)
|
last_waypoint = group.points[-1]
|
||||||
|
if last_waypoint is not None:
|
||||||
|
heading = position.heading_between_point(last_waypoint.position)
|
||||||
|
tod_location = position.point_from_heading(heading, RTB_DISTANCE)
|
||||||
|
self._add_radio_waypoint(group, tod_location, last_waypoint.alt)
|
||||||
|
|
||||||
|
destination_waypoint = self._add_radio_waypoint(group, position, RTB_ALTITUDE)
|
||||||
|
if isinstance(at, Airport):
|
||||||
|
group.land_at(at)
|
||||||
|
return destination_waypoint
|
||||||
|
|
||||||
def _at_position(self, at) -> Point:
|
def _at_position(self, at) -> Point:
|
||||||
if isinstance(at, Point):
|
if isinstance(at, Point):
|
||||||
@@ -236,15 +264,15 @@ class AircraftConflictGenerator:
|
|||||||
orbit_task = ControlledTask(OrbitAction(ATTACK_CIRCLE_ALT, pattern=OrbitAction.OrbitPattern.Circle))
|
orbit_task = ControlledTask(OrbitAction(ATTACK_CIRCLE_ALT, pattern=OrbitAction.OrbitPattern.Circle))
|
||||||
orbit_task.stop_after_duration(ATTACK_CIRCLE_DURATION * 60)
|
orbit_task.stop_after_duration(ATTACK_CIRCLE_DURATION * 60)
|
||||||
|
|
||||||
orbit_waypoint = group.add_waypoint(self.conflict.position, CAS_ALTITUDE)
|
orbit_waypoint = self._add_radio_waypoint(group, self.conflict.position, CAS_ALTITUDE)
|
||||||
orbit_waypoint.tasks.append(orbit_task)
|
orbit_waypoint.tasks.append(orbit_task)
|
||||||
orbit_waypoint.tasks.append(EngageTargets(max_distance=DEFENCE_ENGAGEMENT_MAX_DISTANCE))
|
orbit_waypoint.tasks.append(EngageTargets(max_distance=DEFENCE_ENGAGEMENT_MAX_DISTANCE))
|
||||||
|
|
||||||
groups.append(group)
|
groups.append(group)
|
||||||
return groups
|
return groups
|
||||||
|
|
||||||
def generate_cas_strikegroup(self, attackers: db.PlaneDict, clients: db.PlaneDict, at: db.StartingPosition = None):
|
def generate_cas_strikegroup(self, attackers: db.PlaneDict, clients: db.PlaneDict, at: db.StartingPosition = None, escort=True):
|
||||||
assert len(self.escort_targets) == 0
|
assert not escort or len(self.escort_targets) == 0
|
||||||
|
|
||||||
for flying_type, count, client_count in self._split_to_groups(attackers, clients):
|
for flying_type, count, client_count in self._split_to_groups(attackers, clients):
|
||||||
group = self._generate_group(
|
group = self._generate_group(
|
||||||
@@ -255,17 +283,44 @@ class AircraftConflictGenerator:
|
|||||||
client_count=client_count,
|
client_count=client_count,
|
||||||
at=at and at or self._group_point(self.conflict.air_attackers_location))
|
at=at and at or self._group_point(self.conflict.air_attackers_location))
|
||||||
|
|
||||||
waypoint = group.add_waypoint(self.conflict.position, CAS_ALTITUDE, WARM_START_AIRSPEED)
|
waypoint = self._add_radio_waypoint(group, self.conflict.position, CAS_ALTITUDE, WARM_START_AIRSPEED)
|
||||||
if self.conflict.is_vector:
|
if self.conflict.is_vector:
|
||||||
group.add_waypoint(self.conflict.tail, CAS_ALTITUDE, WARM_START_AIRSPEED)
|
self._add_radio_waypoint(group, self.conflict.tail, CAS_ALTITUDE, WARM_START_AIRSPEED)
|
||||||
|
|
||||||
group.task = CAS.name
|
group.task = CAS.name
|
||||||
self._setup_group(group, CAS, client_count)
|
self._setup_group(group, CAS, client_count)
|
||||||
self.escort_targets.append((group, group.points.index(waypoint)))
|
if escort:
|
||||||
|
self.escort_targets.append((group, group.points.index(waypoint)))
|
||||||
self._rtb_for(group, self.conflict.from_cp, at)
|
self._rtb_for(group, self.conflict.from_cp, at)
|
||||||
|
|
||||||
def generate_ground_attack_strikegroup(self, strikegroup: db.PlaneDict, clients: db.PlaneDict, targets: typing.List[typing.Tuple[str, Point]], at: db.StartingPosition = None):
|
def generate_ground_attack_strikegroup(self, strikegroup: db.PlaneDict, clients: db.PlaneDict, targets: typing.List[typing.Tuple[str, Point]], at: db.StartingPosition = None, escort=True):
|
||||||
assert len(self.escort_targets) == 0
|
assert not escort or len(self.escort_targets) == 0
|
||||||
|
|
||||||
|
for flying_type, count, client_count in self._split_to_groups(strikegroup, clients):
|
||||||
|
group = self._generate_group(
|
||||||
|
name=namegen.next_unit_name(self.conflict.attackers_side, flying_type),
|
||||||
|
side=self.conflict.attackers_side,
|
||||||
|
unit_type=flying_type,
|
||||||
|
count=count,
|
||||||
|
client_count=client_count,
|
||||||
|
at=at and at or self._group_point(self.conflict.air_attackers_location))
|
||||||
|
|
||||||
|
escort_until_waypoint = None
|
||||||
|
|
||||||
|
for name, pos in targets:
|
||||||
|
waypoint = group.add_waypoint(pos, 0, WARM_START_AIRSPEED, self.m.translation.create_string(name))
|
||||||
|
waypoint.tasks.append(Bombing(pos, attack_qty=2))
|
||||||
|
if escort_until_waypoint is None:
|
||||||
|
escort_until_waypoint = waypoint
|
||||||
|
|
||||||
|
group.task = GroundAttack.name
|
||||||
|
self._setup_group(group, GroundAttack, client_count)
|
||||||
|
if escort:
|
||||||
|
self.escort_targets.append((group, group.points.index(escort_until_waypoint)))
|
||||||
|
self._rtb_for(group, self.conflict.from_cp, at)
|
||||||
|
|
||||||
|
def generate_sead_strikegroup(self, strikegroup: db.PlaneDict, clients: db.PlaneDict, targets: typing.List[typing.Tuple[str, Point]], at: db.StartingPosition, escort=True):
|
||||||
|
assert not escort or len(self.escort_targets) == 0
|
||||||
|
|
||||||
for flying_type, count, client_count in self._split_to_groups(strikegroup, clients):
|
for flying_type, count, client_count in self._split_to_groups(strikegroup, clients):
|
||||||
group = self._generate_group(
|
group = self._generate_group(
|
||||||
@@ -283,13 +338,15 @@ class AircraftConflictGenerator:
|
|||||||
if escort_until_waypoint is None:
|
if escort_until_waypoint is None:
|
||||||
escort_until_waypoint = waypoint
|
escort_until_waypoint = waypoint
|
||||||
|
|
||||||
group.task = GroundAttack.name
|
group.task = SEAD.name
|
||||||
self._setup_group(group, GroundAttack, client_count)
|
self._setup_group(group, SEAD, client_count)
|
||||||
self.escort_targets.append((group, group.points.index(escort_until_waypoint)))
|
if escort:
|
||||||
|
self.escort_targets.append((group, group.points.index(escort_until_waypoint)))
|
||||||
|
|
||||||
self._rtb_for(group, self.conflict.from_cp, at)
|
self._rtb_for(group, self.conflict.from_cp, at)
|
||||||
|
|
||||||
def generate_defenders_cas(self, defenders: db.PlaneDict, clients: db.PlaneDict, at: db.StartingPosition = None):
|
def generate_defenders_cas(self, defenders: db.PlaneDict, clients: db.PlaneDict, at: db.StartingPosition = None, escort=True):
|
||||||
assert len(self.escort_targets) == 0
|
assert not escort or len(self.escort_targets) == 0
|
||||||
|
|
||||||
for flying_type, count, client_count in self._split_to_groups(defenders, clients):
|
for flying_type, count, client_count in self._split_to_groups(defenders, clients):
|
||||||
group = self._generate_group(
|
group = self._generate_group(
|
||||||
@@ -302,19 +359,20 @@ class AircraftConflictGenerator:
|
|||||||
|
|
||||||
location = self._group_point(self.conflict.air_defenders_location)
|
location = self._group_point(self.conflict.air_defenders_location)
|
||||||
insertion_point = self.conflict.find_insertion_point(location)
|
insertion_point = self.conflict.find_insertion_point(location)
|
||||||
waypoint = group.add_waypoint(insertion_point, CAS_ALTITUDE, WARM_START_AIRSPEED)
|
waypoint = self._add_radio_waypoint(group, insertion_point, CAS_ALTITUDE, WARM_START_AIRSPEED)
|
||||||
|
|
||||||
if self.conflict.is_vector:
|
if self.conflict.is_vector:
|
||||||
destination_tail = self.conflict.tail.distance_to_point(insertion_point) > self.conflict.position.distance_to_point(insertion_point)
|
destination_tail = self.conflict.tail.distance_to_point(insertion_point) > self.conflict.position.distance_to_point(insertion_point)
|
||||||
group.add_waypoint(destination_tail and self.conflict.tail or self.conflict.position, CAS_ALTITUDE, WARM_START_AIRSPEED)
|
self._add_radio_waypoint(group, destination_tail and self.conflict.tail or self.conflict.position, CAS_ALTITUDE, WARM_START_AIRSPEED)
|
||||||
|
|
||||||
group.task = CAS.name
|
group.task = CAS.name
|
||||||
self._setup_group(group, CAS, client_count)
|
self._setup_group(group, CAS, client_count)
|
||||||
self.escort_targets.append((group, group.points.index(waypoint)))
|
if escort:
|
||||||
|
self.escort_targets.append((group, group.points.index(waypoint)))
|
||||||
self._rtb_for(group, self.conflict.to_cp, at)
|
self._rtb_for(group, self.conflict.to_cp, at)
|
||||||
|
|
||||||
def generate_ship_strikegroup(self, attackers: db.PlaneDict, clients: db.PlaneDict, target_groups: typing.Collection[ShipGroup], at: db.StartingPosition = None):
|
def generate_ship_strikegroup(self, attackers: db.PlaneDict, clients: db.PlaneDict, target_groups: typing.Collection[ShipGroup], at: db.StartingPosition = None, escort=True):
|
||||||
assert len(self.escort_targets) == 0
|
assert not escort or len(self.escort_targets) == 0
|
||||||
|
|
||||||
for flying_type, count, client_count in self._split_to_groups(attackers, clients):
|
for flying_type, count, client_count in self._split_to_groups(attackers, clients):
|
||||||
group = self._generate_group(
|
group = self._generate_group(
|
||||||
@@ -325,13 +383,14 @@ class AircraftConflictGenerator:
|
|||||||
client_count=client_count,
|
client_count=client_count,
|
||||||
at=at and at or self._group_point(self.conflict.air_attackers_location))
|
at=at and at or self._group_point(self.conflict.air_attackers_location))
|
||||||
|
|
||||||
wayp = group.add_waypoint(self.conflict.position, CAS_ALTITUDE, WARM_START_AIRSPEED)
|
wayp = self._add_radio_waypoint(group, self.conflict.position, CAS_ALTITUDE, WARM_START_AIRSPEED)
|
||||||
for target_group in target_groups:
|
for target_group in target_groups:
|
||||||
wayp.tasks.append(AttackGroup(target_group.id))
|
wayp.tasks.append(AttackGroup(target_group.id))
|
||||||
|
|
||||||
group.task = AntishipStrike.name
|
group.task = AntishipStrike.name
|
||||||
self._setup_group(group, AntishipStrike, client_count)
|
self._setup_group(group, AntishipStrike, client_count)
|
||||||
self.escort_targets.append((group, group.points.index(wayp)))
|
if escort:
|
||||||
|
self.escort_targets.append((group, group.points.index(wayp)))
|
||||||
self._rtb_for(group, self.conflict.from_cp, at)
|
self._rtb_for(group, self.conflict.from_cp, at)
|
||||||
|
|
||||||
def generate_attackers_escort(self, attackers: db.PlaneDict, clients: db.PlaneDict, at: db.StartingPosition = None):
|
def generate_attackers_escort(self, attackers: db.PlaneDict, clients: db.PlaneDict, at: db.StartingPosition = None):
|
||||||
@@ -365,7 +424,7 @@ class AircraftConflictGenerator:
|
|||||||
at=at and at or self._group_point(self.conflict.air_defenders_location))
|
at=at and at or self._group_point(self.conflict.air_defenders_location))
|
||||||
|
|
||||||
group.task = CAP.name
|
group.task = CAP.name
|
||||||
wayp = group.add_waypoint(self.conflict.position, CAS_ALTITUDE, WARM_START_AIRSPEED)
|
wayp = self._add_radio_waypoint(group, self.conflict.position, CAS_ALTITUDE, WARM_START_AIRSPEED)
|
||||||
wayp.tasks.append(dcs.task.EngageTargets(max_distance=DEFENCE_ENGAGEMENT_MAX_DISTANCE))
|
wayp.tasks.append(dcs.task.EngageTargets(max_distance=DEFENCE_ENGAGEMENT_MAX_DISTANCE))
|
||||||
wayp.tasks.append(dcs.task.OrbitAction(ATTACK_CIRCLE_ALT, pattern=OrbitAction.OrbitPattern.Circle))
|
wayp.tasks.append(dcs.task.OrbitAction(ATTACK_CIRCLE_ALT, pattern=OrbitAction.OrbitPattern.Circle))
|
||||||
self._setup_group(group, CAP, client_count)
|
self._setup_group(group, CAP, client_count)
|
||||||
@@ -381,9 +440,9 @@ class AircraftConflictGenerator:
|
|||||||
client_count=client_count,
|
client_count=client_count,
|
||||||
at=at and at or self._group_point(self.conflict.air_attackers_location))
|
at=at and at or self._group_point(self.conflict.air_attackers_location))
|
||||||
|
|
||||||
waypoint = group.add_waypoint(self.conflict.position, WARM_START_ALTITUDE, WARM_START_AIRSPEED)
|
waypoint = self._add_radio_waypoint(group, self.conflict.position, WARM_START_ALTITUDE, WARM_START_AIRSPEED)
|
||||||
if self.conflict.is_vector:
|
if self.conflict.is_vector:
|
||||||
group.add_waypoint(self.conflict.tail, WARM_START_ALTITUDE, WARM_START_AIRSPEED)
|
self._add_radio_waypoint(group, self.conflict.tail, WARM_START_ALTITUDE, WARM_START_AIRSPEED)
|
||||||
|
|
||||||
group.task = CAP.name
|
group.task = CAP.name
|
||||||
self._setup_group(group, CAP, client_count)
|
self._setup_group(group, CAP, client_count)
|
||||||
@@ -399,22 +458,22 @@ class AircraftConflictGenerator:
|
|||||||
client_count=client_count,
|
client_count=client_count,
|
||||||
at=at and at or self._group_point(self.conflict.air_defenders_location))
|
at=at and at or self._group_point(self.conflict.air_defenders_location))
|
||||||
|
|
||||||
waypoint = group.add_waypoint(self.conflict.position, WARM_START_ALTITUDE, WARM_START_AIRSPEED)
|
waypoint = self._add_radio_waypoint(group, self.conflict.position, WARM_START_ALTITUDE, WARM_START_AIRSPEED)
|
||||||
if self.conflict.is_vector:
|
if self.conflict.is_vector:
|
||||||
group.add_waypoint(self.conflict.tail, WARM_START_ALTITUDE, WARM_START_AIRSPEED)
|
self._add_radio_waypoint(group, self.conflict.tail, WARM_START_ALTITUDE, WARM_START_AIRSPEED)
|
||||||
else:
|
else:
|
||||||
heading = group.position.heading_between_point(self.conflict.position)
|
heading = group.position.heading_between_point(self.conflict.position)
|
||||||
waypoint = group.add_waypoint(self.conflict.position.point_from_heading(heading, BARCAP_RACETRACK_DISTANCE),
|
waypoint = self._add_radio_waypoint(group, self.conflict.position.point_from_heading(heading, BARCAP_RACETRACK_DISTANCE),
|
||||||
WARM_START_ALTITUDE,
|
WARM_START_ALTITUDE,
|
||||||
WARM_START_AIRSPEED)
|
WARM_START_AIRSPEED)
|
||||||
waypoint.tasks.append(OrbitAction(WARM_START_ALTITUDE, WARM_START_AIRSPEED))
|
waypoint.tasks.append(OrbitAction(WARM_START_ALTITUDE, WARM_START_AIRSPEED))
|
||||||
|
|
||||||
group.task = CAP.name
|
group.task = CAP.name
|
||||||
self._setup_group(group, CAP, client_count)
|
self._setup_group(group, CAP, client_count)
|
||||||
self._rtb_for(group, self.conflict.to_cp, at)
|
self._rtb_for(group, self.conflict.to_cp, at)
|
||||||
|
|
||||||
def generate_transport(self, transport: db.PlaneDict, destination: Airport):
|
def generate_transport(self, transport: db.PlaneDict, destination: Airport, escort=True):
|
||||||
assert len(self.escort_targets) == 0
|
assert not escort or len(self.escort_targets) == 0
|
||||||
|
|
||||||
for flying_type, count, client_count in self._split_to_groups(transport):
|
for flying_type, count, client_count in self._split_to_groups(transport):
|
||||||
group = self._generate_group(
|
group = self._generate_group(
|
||||||
@@ -425,9 +484,11 @@ class AircraftConflictGenerator:
|
|||||||
client_count=client_count,
|
client_count=client_count,
|
||||||
at=self._group_point(self.conflict.air_defenders_location))
|
at=self._group_point(self.conflict.air_defenders_location))
|
||||||
|
|
||||||
waypoint = group.add_waypoint(destination.position.random_point_within(0, 0), TRANSPORT_LANDING_ALT)
|
waypoint = self._rtb_for(group, self.conflict.to_cp)
|
||||||
self.escort_targets.append((group, group.points.index(waypoint)))
|
if escort:
|
||||||
|
self.escort_targets.append((group, group.points.index(waypoint)))
|
||||||
|
|
||||||
|
self._add_radio_waypoint(group, destination.position, RTB_ALTITUDE)
|
||||||
group.task = Transport.name
|
group.task = Transport.name
|
||||||
group.land_at(destination)
|
group.land_at(destination)
|
||||||
|
|
||||||
@@ -442,16 +503,13 @@ class AircraftConflictGenerator:
|
|||||||
at=at and at or self._group_point(self.conflict.air_attackers_location))
|
at=at and at or self._group_point(self.conflict.air_attackers_location))
|
||||||
|
|
||||||
group.task = CAP.name
|
group.task = CAP.name
|
||||||
|
group.points[0].tasks.append(EngageTargets(max_distance=INTERCEPT_MAX_DISTANCE))
|
||||||
|
|
||||||
heading = group.position.heading_between_point(self.conflict.position)
|
wayp = self._add_radio_waypoint(group, self.conflict.position, WARM_START_ALTITUDE, INTERCEPTION_AIRSPEED)
|
||||||
initial_wayp = group.add_waypoint(group.position.point_from_heading(heading, WORKAROUND_WAYP_DIST), INTERCEPTION_ALT, INTERCEPTION_AIRSPEED)
|
|
||||||
initial_wayp.tasks.append(EngageTargets(max_distance=INTERCEPT_MAX_DISTANCE))
|
|
||||||
|
|
||||||
wayp = group.add_waypoint(self.conflict.position, WARM_START_ALTITUDE, INTERCEPTION_AIRSPEED)
|
|
||||||
wayp.tasks.append(EngageTargets(max_distance=INTERCEPT_MAX_DISTANCE))
|
wayp.tasks.append(EngageTargets(max_distance=INTERCEPT_MAX_DISTANCE))
|
||||||
|
|
||||||
if self.conflict.is_vector:
|
if self.conflict.is_vector:
|
||||||
group.add_waypoint(self.conflict.tail, CAS_ALTITUDE, WARM_START_ALTITUDE)
|
self._add_radio_waypoint(group, self.conflict.tail, CAS_ALTITUDE, WARM_START_ALTITUDE)
|
||||||
|
|
||||||
self._setup_group(group, CAP, client_count)
|
self._setup_group(group, CAP, client_count)
|
||||||
self._rtb_for(group, self.conflict.from_cp, at)
|
self._rtb_for(group, self.conflict.from_cp, at)
|
||||||
@@ -467,9 +525,5 @@ class AircraftConflictGenerator:
|
|||||||
at=at and at or self._group_point(self.conflict.air_attackers_location)
|
at=at and at or self._group_point(self.conflict.air_attackers_location)
|
||||||
)
|
)
|
||||||
|
|
||||||
group.add_waypoint(
|
self._add_radio_waypoint(group, self.conflict.position, HELI_ALT)
|
||||||
pos=self.conflict.position,
|
|
||||||
altitude=HELI_ALT,
|
|
||||||
)
|
|
||||||
|
|
||||||
self._setup_group(group, Transport, client_count)
|
self._setup_group(group, Transport, client_count)
|
||||||
|
|||||||
@@ -9,46 +9,61 @@ from dcs.task import *
|
|||||||
from dcs.terrain.terrain import NoParkingSlotError
|
from dcs.terrain.terrain import NoParkingSlotError
|
||||||
|
|
||||||
TANKER_DISTANCE = 15000
|
TANKER_DISTANCE = 15000
|
||||||
TANKER_ALT = 10000
|
TANKER_ALT = 4572
|
||||||
|
TANKER_HEADING_OFFSET = 45
|
||||||
|
|
||||||
AWACS_DISTANCE = 150000
|
AWACS_DISTANCE = 150000
|
||||||
AWACS_ALT = 10000
|
AWACS_ALT = 13000
|
||||||
|
|
||||||
|
|
||||||
class AirSupportConflictGenerator:
|
class AirSupportConflictGenerator:
|
||||||
|
generated_tankers = None # type: typing.List[str]
|
||||||
|
|
||||||
def __init__(self, mission: Mission, conflict: Conflict, game):
|
def __init__(self, mission: Mission, conflict: Conflict, game):
|
||||||
self.mission = mission
|
self.mission = mission
|
||||||
self.conflict = conflict
|
self.conflict = conflict
|
||||||
self.game = game
|
self.game = game
|
||||||
|
self.generated_tankers = []
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def support_tasks(cls) -> typing.Collection[typing.Type[MainTask]]:
|
||||||
|
return [Refueling, AWACS]
|
||||||
|
|
||||||
def generate(self, is_awacs_enabled):
|
def generate(self, is_awacs_enabled):
|
||||||
player_cp = self.conflict.from_cp if self.conflict.from_cp.captured else self.conflict.to_cp
|
player_cp = self.conflict.from_cp if self.conflict.from_cp.captured else self.conflict.to_cp
|
||||||
tanker_unit = db.find_unittype(Refueling, self.conflict.attackers_side.name)[0]
|
|
||||||
tanker_heading = self.conflict.to_cp.position.heading_between_point(self.conflict.from_cp.position)
|
|
||||||
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),
|
|
||||||
name=namegen.next_tanker_name(self.mission.country(self.game.player)),
|
|
||||||
airport=None,
|
|
||||||
plane_type=tanker_unit,
|
|
||||||
position=tanker_position,
|
|
||||||
altitude=TANKER_ALT,
|
|
||||||
frequency=240,
|
|
||||||
start_type=StartType.Warm,
|
|
||||||
tacanchannel="99X",
|
|
||||||
)
|
|
||||||
|
|
||||||
tanker_group.points[0].tasks.append(ActivateBeaconCommand(channel=10, unit_id=tanker_group.id, aa=False))
|
for i, tanker_unit_type in enumerate(db.find_unittype(Refueling, self.conflict.attackers_side.name)):
|
||||||
|
self.generated_tankers.append(db.unit_type_name(tanker_unit_type))
|
||||||
|
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),
|
||||||
|
name=namegen.next_tanker_name(self.mission.country(self.game.player)),
|
||||||
|
airport=None,
|
||||||
|
plane_type=tanker_unit_type,
|
||||||
|
position=tanker_position,
|
||||||
|
altitude=TANKER_ALT,
|
||||||
|
frequency=130 + i,
|
||||||
|
start_type=StartType.Warm,
|
||||||
|
tacanchannel="{}X".format(97 + i),
|
||||||
|
)
|
||||||
|
|
||||||
|
tanker_group.points[0].tasks.append(ActivateBeaconCommand(channel=97 + i, unit_id=tanker_group.id, aa=False))
|
||||||
|
tanker_group.points[0].tasks.append(SetInvisibleCommand(True))
|
||||||
|
tanker_group.points[0].tasks.append(SetImmortalCommand(True))
|
||||||
|
|
||||||
if is_awacs_enabled:
|
if is_awacs_enabled:
|
||||||
awacs_unit = db.find_unittype(AWACS, self.conflict.attackers_side.name)[0]
|
awacs_unit = db.find_unittype(AWACS, self.conflict.attackers_side.name)[0]
|
||||||
self.mission.awacs_flight(
|
awacs_flight = self.mission.awacs_flight(
|
||||||
country=self.mission.country(self.game.player),
|
country=self.mission.country(self.game.player),
|
||||||
name=namegen.next_awacs_name(self.mission.country(self.game.player),),
|
name=namegen.next_awacs_name(self.mission.country(self.game.player)),
|
||||||
plane_type=awacs_unit,
|
plane_type=awacs_unit,
|
||||||
altitude=AWACS_ALT,
|
altitude=AWACS_ALT,
|
||||||
airport=None,
|
airport=None,
|
||||||
position=self.conflict.position.random_point_within(AWACS_DISTANCE, AWACS_DISTANCE),
|
position=self.conflict.position.random_point_within(AWACS_DISTANCE, AWACS_DISTANCE),
|
||||||
frequency=244,
|
frequency=133,
|
||||||
start_type=StartType.Warm,
|
start_type=StartType.Warm,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
awacs_flight.points[0].tasks.append(SetInvisibleCommand(True))
|
||||||
|
awacs_flight.points[0].tasks.append(SetImmortalCommand(True))
|
||||||
|
|||||||
33
gen/armor.py
@@ -20,6 +20,8 @@ FRONTLINE_CAS_FIGHTS_COUNT = 4, 8
|
|||||||
FRONTLINE_CAS_GROUP_MIN = 1, 2
|
FRONTLINE_CAS_GROUP_MIN = 1, 2
|
||||||
FRONTLINE_CAS_PADDING = 12000
|
FRONTLINE_CAS_PADDING = 12000
|
||||||
|
|
||||||
|
FIGHT_DISTANCE = 3500
|
||||||
|
|
||||||
|
|
||||||
class ArmorConflictGenerator:
|
class ArmorConflictGenerator:
|
||||||
def __init__(self, mission: Mission, conflict: Conflict):
|
def __init__(self, mission: Mission, conflict: Conflict):
|
||||||
@@ -34,7 +36,7 @@ class ArmorConflictGenerator:
|
|||||||
|
|
||||||
return point.random_point_within(distance, self.conflict.size * SPREAD_DISTANCE_SIZE_FACTOR)
|
return point.random_point_within(distance, self.conflict.size * SPREAD_DISTANCE_SIZE_FACTOR)
|
||||||
|
|
||||||
def _generate_group(self, side: Country, unit: VehicleType, count: int, at: Point, to: Point = None):
|
def _generate_group(self, side: Country, unit: VehicleType, count: int, at: Point, to: Point = None, move_formation: PointAction = PointAction.OffRoad):
|
||||||
for c in range(count):
|
for c in range(count):
|
||||||
logging.info("armorgen: {} for {}".format(unit, side.id))
|
logging.info("armorgen: {} for {}".format(unit, side.id))
|
||||||
group = self.m.vehicle_group(
|
group = self.m.vehicle_group(
|
||||||
@@ -43,18 +45,21 @@ class ArmorConflictGenerator:
|
|||||||
unit,
|
unit,
|
||||||
position=self._group_point(at),
|
position=self._group_point(at),
|
||||||
group_size=1,
|
group_size=1,
|
||||||
move_formation=PointAction.OffRoad)
|
move_formation=move_formation)
|
||||||
|
|
||||||
|
vehicle: Vehicle = group.units[0]
|
||||||
|
vehicle.player_can_drive = True
|
||||||
|
|
||||||
if not to:
|
if not to:
|
||||||
to = self.conflict.position.point_from_heading(0, 500)
|
to = self.conflict.position.point_from_heading(0, 500)
|
||||||
|
|
||||||
wayp = group.add_waypoint(self._group_point(to))
|
wayp = group.add_waypoint(self._group_point(to), move_formation=move_formation)
|
||||||
wayp.tasks = []
|
wayp.tasks = []
|
||||||
|
|
||||||
def _generate_fight_at(self, attackers: db.ArmorDict, defenders: db.ArmorDict, position: Point):
|
def _generate_fight_at(self, attackers: db.ArmorDict, defenders: db.ArmorDict, position: Point):
|
||||||
if attackers:
|
if attackers:
|
||||||
attack_pos = position.point_from_heading(self.conflict.heading - 90, 8000)
|
attack_pos = position.point_from_heading(self.conflict.heading - 90, FIGHT_DISTANCE)
|
||||||
attack_dest = position.point_from_heading(self.conflict.heading + 90, 25000)
|
attack_dest = position.point_from_heading(self.conflict.heading + 90, FIGHT_DISTANCE * 2)
|
||||||
for type, count in attackers.items():
|
for type, count in attackers.items():
|
||||||
self._generate_group(
|
self._generate_group(
|
||||||
side=self.conflict.attackers_side,
|
side=self.conflict.attackers_side,
|
||||||
@@ -65,8 +70,8 @@ class ArmorConflictGenerator:
|
|||||||
)
|
)
|
||||||
|
|
||||||
if defenders:
|
if defenders:
|
||||||
def_pos = position.point_from_heading(self.conflict.heading + 90, 4000)
|
def_pos = position.point_from_heading(self.conflict.heading + 90, FIGHT_DISTANCE)
|
||||||
def_dest = position.point_from_heading(self.conflict.heading - 90, 25000)
|
def_dest = position.point_from_heading(self.conflict.heading - 90, FIGHT_DISTANCE * 2)
|
||||||
for type, count in defenders.items():
|
for type, count in defenders.items():
|
||||||
self._generate_group(
|
self._generate_group(
|
||||||
side=self.conflict.defenders_side,
|
side=self.conflict.defenders_side,
|
||||||
@@ -100,12 +105,20 @@ class ArmorConflictGenerator:
|
|||||||
attacker_groups = list(db.unitdict_split(attackers, single_fight_attackers_count))
|
attacker_groups = list(db.unitdict_split(attackers, single_fight_attackers_count))
|
||||||
|
|
||||||
for attacker_group_dict, target_group_dict in zip_longest(attacker_groups, defender_groups):
|
for attacker_group_dict, target_group_dict in zip_longest(attacker_groups, defender_groups):
|
||||||
padding = FRONTLINE_CAS_PADDING if FRONTLINE_CAS_PADDING < self.conflict.distance else 0
|
|
||||||
|
|
||||||
position = self.conflict.position.point_from_heading(self.conflict.heading,
|
position = self.conflict.position.point_from_heading(self.conflict.heading,
|
||||||
random.randint(padding, int(self.conflict.distance - padding)))
|
random.randint(0, self.conflict.distance))
|
||||||
self._generate_fight_at(attacker_group_dict, target_group_dict, position)
|
self._generate_fight_at(attacker_group_dict, target_group_dict, position)
|
||||||
|
|
||||||
|
def generate_convoy(self, units: db.ArmorDict):
|
||||||
|
for type, count in units.items():
|
||||||
|
self._generate_group(
|
||||||
|
side=self.conflict.defenders_side,
|
||||||
|
unit=type,
|
||||||
|
count=count,
|
||||||
|
at=self.conflict.ground_defenders_location,
|
||||||
|
to=self.conflict.position,
|
||||||
|
move_formation=PointAction.OnRoad)
|
||||||
|
|
||||||
def generate_passengers(self, count: int):
|
def generate_passengers(self, count: int):
|
||||||
unit_type = random.choice(db.find_unittype(Nothing, self.conflict.attackers_side.name))
|
unit_type = random.choice(db.find_unittype(Nothing, self.conflict.attackers_side.name))
|
||||||
|
|
||||||
|
|||||||
@@ -12,6 +12,7 @@ class BriefingGenerator:
|
|||||||
title = "" # type: str
|
title = "" # type: str
|
||||||
description = "" # type: str
|
description = "" # type: str
|
||||||
targets = None # type: typing.List[typing.Tuple[str, str]]
|
targets = None # type: typing.List[typing.Tuple[str, str]]
|
||||||
|
waypoints = None # type: typing.List[str]
|
||||||
|
|
||||||
def __init__(self, mission: Mission, conflict: Conflict, game):
|
def __init__(self, mission: Mission, conflict: Conflict, game):
|
||||||
self.m = mission
|
self.m = mission
|
||||||
@@ -20,6 +21,7 @@ class BriefingGenerator:
|
|||||||
|
|
||||||
self.freqs = []
|
self.freqs = []
|
||||||
self.targets = []
|
self.targets = []
|
||||||
|
self.waypoints = []
|
||||||
|
|
||||||
def append_frequency(self, name: str, frequency: str):
|
def append_frequency(self, name: str, frequency: str):
|
||||||
self.freqs.append((name, frequency))
|
self.freqs.append((name, frequency))
|
||||||
@@ -27,7 +29,14 @@ class BriefingGenerator:
|
|||||||
def append_target(self, description: str, markpoint: str = None):
|
def append_target(self, description: str, markpoint: str = None):
|
||||||
self.targets.append((description, markpoint))
|
self.targets.append((description, markpoint))
|
||||||
|
|
||||||
|
def append_waypoint(self, description: str):
|
||||||
|
self.waypoints.append(description)
|
||||||
|
|
||||||
def generate(self):
|
def generate(self):
|
||||||
|
self.waypoints.insert(0, "INITIAL")
|
||||||
|
self.waypoints.append("RTB")
|
||||||
|
self.waypoints.append("RTB Landing")
|
||||||
|
|
||||||
description = ""
|
description = ""
|
||||||
|
|
||||||
if self.title:
|
if self.title:
|
||||||
@@ -43,7 +52,12 @@ class BriefingGenerator:
|
|||||||
|
|
||||||
if self.targets:
|
if self.targets:
|
||||||
description += "\n\nTARGETS:"
|
description += "\n\nTARGETS:"
|
||||||
for name, tp in self.targets:
|
for i, (name, tp) in enumerate(self.targets):
|
||||||
description += "\n{} {}".format(name, "(TP {})".format(tp) if tp else "")
|
description += "\n#{} {} {}".format(i+1, name, "(TP {})".format(tp) if tp else "")
|
||||||
|
|
||||||
|
if self.waypoints:
|
||||||
|
description += "\n\nWAYPOINTS:"
|
||||||
|
for i, descr in enumerate(self.waypoints):
|
||||||
|
description += "\n#{}: {}".format(i, descr)
|
||||||
|
|
||||||
self.m.set_description_text(description)
|
self.m.set_description_text(description)
|
||||||
|
|||||||
@@ -28,6 +28,8 @@ CAP_CAS_DISTANCE = 10000, 120000
|
|||||||
|
|
||||||
GROUND_INTERCEPT_SPREAD = 5000
|
GROUND_INTERCEPT_SPREAD = 5000
|
||||||
GROUND_DISTANCE_FACTOR = 1
|
GROUND_DISTANCE_FACTOR = 1
|
||||||
|
GROUND_DISTANCE = 2000
|
||||||
|
|
||||||
GROUND_ATTACK_DISTANCE = 25000, 13000
|
GROUND_ATTACK_DISTANCE = 25000, 13000
|
||||||
|
|
||||||
TRANSPORT_FRONTLINE_DIST = 1800
|
TRANSPORT_FRONTLINE_DIST = 1800
|
||||||
@@ -128,33 +130,72 @@ class Conflict:
|
|||||||
return self.to_cp.size * GROUND_DISTANCE_FACTOR
|
return self.to_cp.size * GROUND_DISTANCE_FACTOR
|
||||||
|
|
||||||
def find_insertion_point(self, other_point: Point) -> Point:
|
def find_insertion_point(self, other_point: Point) -> Point:
|
||||||
dx = self.position.x - self.tail.x
|
if self.is_vector:
|
||||||
dy = self.position.y - self.tail.y
|
dx = self.position.x - self.tail.x
|
||||||
dr2 = float(dx ** 2 + dy ** 2)
|
dy = self.position.y - self.tail.y
|
||||||
|
dr2 = float(dx ** 2 + dy ** 2)
|
||||||
|
|
||||||
lerp = ((other_point.x - self.tail.x) * dx + (other_point.y - self.tail.y) * dy) / dr2
|
lerp = ((other_point.x - self.tail.x) * dx + (other_point.y - self.tail.y) * dy) / dr2
|
||||||
if lerp < 0:
|
if lerp < 0:
|
||||||
lerp = 0
|
lerp = 0
|
||||||
elif lerp > 1:
|
elif lerp > 1:
|
||||||
lerp = 1
|
lerp = 1
|
||||||
|
|
||||||
x = lerp * dx + self.tail.x
|
x = lerp * dx + self.tail.x
|
||||||
y = lerp * dy + self.tail.y
|
y = lerp * dy + self.tail.y
|
||||||
return Point(x, y)
|
return Point(x, y)
|
||||||
|
else:
|
||||||
|
return self.position
|
||||||
|
|
||||||
|
def find_ground_position(self, at: Point, heading: int, max_distance: int = 40000) -> typing.Optional[Point]:
|
||||||
|
return Conflict._find_ground_position(at, max_distance, heading, self.theater)
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def has_frontline_between(cls, from_cp: ControlPoint, to_cp: ControlPoint) -> bool:
|
def has_frontline_between(cls, from_cp: ControlPoint, to_cp: ControlPoint) -> bool:
|
||||||
return from_cp.has_frontline and to_cp.has_frontline
|
return from_cp.has_frontline and to_cp.has_frontline
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def frontline_position(cls, from_cp: ControlPoint, to_cp: ControlPoint) -> typing.Tuple[Point, int]:
|
def frontline_position(cls, theater: ConflictTheater, from_cp: ControlPoint, to_cp: ControlPoint) -> typing.Optional[typing.Tuple[Point, int]]:
|
||||||
distance = max(from_cp.position.distance_to_point(to_cp.position) * FRONTLINE_DISTANCE_STRENGTH_FACTOR * to_cp.base.strength, FRONTLINE_MIN_CP_DISTANCE)
|
attack_heading = from_cp.position.heading_between_point(to_cp.position)
|
||||||
heading = to_cp.position.heading_between_point(from_cp.position)
|
attack_distance = from_cp.position.distance_to_point(to_cp.position)
|
||||||
return to_cp.position.point_from_heading(heading, distance), heading
|
middle_point = from_cp.position.point_from_heading(attack_heading, attack_distance / 2)
|
||||||
|
|
||||||
|
strength_delta = (from_cp.base.strength - to_cp.base.strength) / 1.0
|
||||||
|
position = middle_point.point_from_heading(attack_heading, strength_delta * attack_distance / 2 - FRONTLINE_MIN_CP_DISTANCE)
|
||||||
|
return position, _opposite_heading(attack_heading)
|
||||||
|
|
||||||
|
ground_position = cls._find_ground_position(position, attack_distance / 2 - FRONTLINE_MIN_CP_DISTANCE, attack_heading, theater)
|
||||||
|
if ground_position:
|
||||||
|
return ground_position, _opposite_heading(attack_heading)
|
||||||
|
else:
|
||||||
|
logging.warning("Coudn't find frontline position between {} and {}!".format(from_cp, to_cp))
|
||||||
|
return position, _opposite_heading(attack_heading)
|
||||||
|
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def frontline_vector(cls, from_cp: ControlPoint, to_cp: ControlPoint, theater: ConflictTheater) -> typing.Tuple[Point, int, int]:
|
def frontline_vector(cls, from_cp: ControlPoint, to_cp: ControlPoint, theater: ConflictTheater) -> typing.Optional[typing.Tuple[Point, int, int]]:
|
||||||
center_position, heading = cls.frontline_position(from_cp, to_cp)
|
initial, heading = cls.frontline_position(theater, from_cp, to_cp)
|
||||||
|
|
||||||
|
"""
|
||||||
|
probe_end_point = initial.point_from_heading(heading, FRONTLINE_LENGTH)
|
||||||
|
probe = geometry.LineString([(initial.x, initial.y), (probe_end_point.x, probe_end_point.y) ])
|
||||||
|
intersection = probe.intersection(theater.land_poly)
|
||||||
|
|
||||||
|
if isinstance(intersection, geometry.LineString):
|
||||||
|
intersection = intersection
|
||||||
|
elif isinstance(intersection, geometry.MultiLineString):
|
||||||
|
intersection = intersection.geoms[0]
|
||||||
|
else:
|
||||||
|
print(intersection)
|
||||||
|
return None
|
||||||
|
|
||||||
|
return Point(*intersection.xy[0]), _heading_sum(heading, 90), intersection.length
|
||||||
|
"""
|
||||||
|
frontline = cls.frontline_position(theater, from_cp, to_cp)
|
||||||
|
if not frontline:
|
||||||
|
return None
|
||||||
|
|
||||||
|
center_position, heading = frontline
|
||||||
left_position, right_position = None, None
|
left_position, right_position = None, None
|
||||||
|
|
||||||
if not theater.is_on_land(center_position):
|
if not theater.is_on_land(center_position):
|
||||||
@@ -167,7 +208,6 @@ class Conflict:
|
|||||||
if pos:
|
if pos:
|
||||||
left_position = pos
|
left_position = pos
|
||||||
center_position = pos
|
center_position = pos
|
||||||
print("{} - {} {}".format(from_cp, to_cp, center_position))
|
|
||||||
|
|
||||||
if left_position is None:
|
if left_position is None:
|
||||||
left_position = cls._extend_ground_position(center_position, int(FRONTLINE_LENGTH/2), _heading_sum(heading, -90), theater)
|
left_position = cls._extend_ground_position(center_position, int(FRONTLINE_LENGTH/2), _heading_sum(heading, -90), theater)
|
||||||
@@ -186,20 +226,42 @@ class Conflict:
|
|||||||
pos = new_pos
|
pos = new_pos
|
||||||
else:
|
else:
|
||||||
return pos
|
return pos
|
||||||
|
|
||||||
return pos
|
return pos
|
||||||
|
|
||||||
|
"""
|
||||||
|
probe_end_point = initial.point_from_heading(heading, max_distance)
|
||||||
|
probe = geometry.LineString([(initial.x, initial.y), (probe_end_point.x, probe_end_point.y)])
|
||||||
|
|
||||||
|
intersection = probe.intersection(theater.land_poly)
|
||||||
|
if intersection is geometry.LineString:
|
||||||
|
return Point(*intersection.xy[1])
|
||||||
|
elif intersection is geometry.MultiLineString:
|
||||||
|
return Point(*intersection.geoms[0].xy[1])
|
||||||
|
|
||||||
|
return None
|
||||||
|
"""
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def _find_ground_position(cls, initial: Point, max_distance: int, heading: int, theater: ConflictTheater) -> Point:
|
def _find_ground_position(cls, initial: Point, max_distance: int, heading: int, theater: ConflictTheater) -> typing.Optional[Point]:
|
||||||
pos = initial
|
pos = initial
|
||||||
for _ in range(0, int(max_distance), 500):
|
for _ in range(0, int(max_distance), 500):
|
||||||
if theater.is_on_land(pos):
|
if theater.is_on_land(pos):
|
||||||
return pos
|
return pos
|
||||||
|
|
||||||
pos = pos.point_from_heading(heading, 500)
|
pos = pos.point_from_heading(heading, 500)
|
||||||
|
"""
|
||||||
|
probe_end_point = initial.point_from_heading(heading, max_distance)
|
||||||
|
probe = geometry.LineString([(initial.x, initial.y), (probe_end_point.x, probe_end_point.y) ])
|
||||||
|
|
||||||
logging.info("Didn't find ground position!")
|
intersection = probe.intersection(theater.land_poly)
|
||||||
return None
|
if isinstance(intersection, geometry.LineString):
|
||||||
|
return Point(*intersection.xy[1])
|
||||||
|
elif isinstance(intersection, geometry.MultiLineString):
|
||||||
|
return Point(*intersection.geoms[0].xy[1])
|
||||||
|
"""
|
||||||
|
|
||||||
|
logging.error("Didn't find ground position ({})!".format(initial))
|
||||||
|
return initial
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def capture_conflict(cls, attacker: Country, defender: Country, from_cp: ControlPoint, to_cp: ControlPoint, theater: ConflictTheater):
|
def capture_conflict(cls, attacker: Country, defender: Country, from_cp: ControlPoint, to_cp: ControlPoint, theater: ConflictTheater):
|
||||||
@@ -208,12 +270,12 @@ class Conflict:
|
|||||||
attack_heading = to_cp.find_radial(attack_raw_heading)
|
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)
|
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
|
distance = GROUND_DISTANCE
|
||||||
attackers_location = position.point_from_heading(attack_heading, distance)
|
attackers_location = position.point_from_heading(attack_heading, distance)
|
||||||
attackers_location = Conflict._find_ground_position(attackers_location, distance * 2, _heading_sum(attack_heading, 180), theater)
|
attackers_location = Conflict._find_ground_position(attackers_location, distance * 2, attack_heading, theater)
|
||||||
|
|
||||||
defenders_location = position.point_from_heading(defense_heading, distance)
|
defenders_location = position.point_from_heading(defense_heading, 0)
|
||||||
defenders_location = Conflict._find_ground_position(defenders_location, distance * 2, _heading_sum(defense_heading, 180), theater)
|
defenders_location = Conflict._find_ground_position(defenders_location, distance * 2, defense_heading, theater)
|
||||||
|
|
||||||
return cls(
|
return cls(
|
||||||
position=position,
|
position=position,
|
||||||
@@ -256,13 +318,15 @@ class Conflict:
|
|||||||
)
|
)
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def intercept_conflict(cls, attacker: Country, defender: Country, from_cp: ControlPoint, to_cp: ControlPoint, theater: ConflictTheater):
|
def intercept_position(cls, from_cp: ControlPoint, to_cp: ControlPoint) -> Point:
|
||||||
raw_distance = from_cp.position.distance_to_point(to_cp.position) * 1.5
|
raw_distance = from_cp.position.distance_to_point(to_cp.position) * 1.5
|
||||||
distance = max(min(raw_distance, INTERCEPT_MAX_DISTANCE), INTERCEPT_MIN_DISTANCE)
|
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))
|
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 from_cp.position.point_from_heading(heading, distance)
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def intercept_conflict(cls, attacker: Country, defender: Country, position: Point, from_cp: ControlPoint, to_cp: ControlPoint, theater: ConflictTheater):
|
||||||
|
heading = from_cp.position.heading_between_point(position)
|
||||||
return cls(
|
return cls(
|
||||||
position=position.point_from_heading(position.heading_between_point(to_cp.position), INTERCEPT_CONFLICT_DISTANCE),
|
position=position.point_from_heading(position.heading_between_point(to_cp.position), INTERCEPT_CONFLICT_DISTANCE),
|
||||||
theater=theater,
|
theater=theater,
|
||||||
@@ -298,6 +362,35 @@ class Conflict:
|
|||||||
air_defenders_location=position.point_from_heading(heading, AIR_DISTANCE),
|
air_defenders_location=position.point_from_heading(heading, AIR_DISTANCE),
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def convoy_strike_conflict(cls, attacker: Country, defender: Country, from_cp: ControlPoint, to_cp: ControlPoint, theater: ConflictTheater):
|
||||||
|
frontline_position, frontline_heading, frontline_length = Conflict.frontline_vector(from_cp, to_cp, theater)
|
||||||
|
if not frontline_position:
|
||||||
|
assert False
|
||||||
|
|
||||||
|
heading = frontline_heading
|
||||||
|
starting_position = Conflict._find_ground_position(frontline_position.point_from_heading(heading, 7000),
|
||||||
|
GROUND_INTERCEPT_SPREAD,
|
||||||
|
_opposite_heading(heading), theater)
|
||||||
|
if not starting_position:
|
||||||
|
starting_position = frontline_position
|
||||||
|
destination_position = frontline_position
|
||||||
|
else:
|
||||||
|
destination_position = frontline_position
|
||||||
|
|
||||||
|
return cls(
|
||||||
|
position=destination_position,
|
||||||
|
theater=theater,
|
||||||
|
from_cp=from_cp,
|
||||||
|
to_cp=to_cp,
|
||||||
|
attackers_side=attacker,
|
||||||
|
defenders_side=defender,
|
||||||
|
ground_attackers_location=None,
|
||||||
|
ground_defenders_location=starting_position,
|
||||||
|
air_attackers_location=starting_position.point_from_heading(_opposite_heading(heading), AIR_DISTANCE),
|
||||||
|
air_defenders_location=starting_position.point_from_heading(heading, AIR_DISTANCE),
|
||||||
|
)
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def frontline_cas_conflict(cls, attacker: Country, defender: Country, from_cp: ControlPoint, to_cp: ControlPoint, theater: ConflictTheater):
|
def frontline_cas_conflict(cls, attacker: Country, defender: Country, from_cp: ControlPoint, to_cp: ControlPoint, theater: ConflictTheater):
|
||||||
assert cls.has_frontline_between(from_cp, to_cp)
|
assert cls.has_frontline_between(from_cp, to_cp)
|
||||||
@@ -364,7 +457,7 @@ class Conflict:
|
|||||||
)
|
)
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def naval_intercept_conflict(cls, attacker: Country, defender: Country, from_cp: ControlPoint, to_cp: ControlPoint, theater: ConflictTheater):
|
def naval_intercept_position(cls, from_cp: ControlPoint, to_cp: ControlPoint, theater: ConflictTheater):
|
||||||
radial = random.choice(to_cp.sea_radials)
|
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)
|
initial_distance = min(int(from_cp.position.distance_to_point(to_cp.position) * NAVAL_INTERCEPT_DISTANCE_FACTOR), NAVAL_INTERCEPT_DISTANCE_MAX)
|
||||||
@@ -374,7 +467,10 @@ class Conflict:
|
|||||||
|
|
||||||
if not theater.is_on_land(position):
|
if not theater.is_on_land(position):
|
||||||
break
|
break
|
||||||
|
return position
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def naval_intercept_conflict(cls, attacker: Country, defender: Country, position: Point, from_cp: ControlPoint, to_cp: ControlPoint, theater: ConflictTheater):
|
||||||
attacker_heading = from_cp.position.heading_between_point(to_cp.position)
|
attacker_heading = from_cp.position.heading_between_point(to_cp.position)
|
||||||
return cls(
|
return cls(
|
||||||
position=position,
|
position=position,
|
||||||
@@ -391,7 +487,7 @@ class Conflict:
|
|||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def transport_conflict(cls, attacker: Country, defender: Country, from_cp: ControlPoint, to_cp: ControlPoint, theater: ConflictTheater):
|
def transport_conflict(cls, attacker: Country, defender: Country, from_cp: ControlPoint, to_cp: ControlPoint, theater: ConflictTheater):
|
||||||
frontline_position, heading = cls.frontline_position(from_cp, to_cp)
|
frontline_position, heading = cls.frontline_position(theater, from_cp, to_cp)
|
||||||
initial_dest = frontline_position.point_from_heading(heading, TRANSPORT_FRONTLINE_DIST)
|
initial_dest = frontline_position.point_from_heading(heading, TRANSPORT_FRONTLINE_DIST)
|
||||||
dest = cls._find_ground_position(initial_dest, from_cp.position.distance_to_point(to_cp.position) / 3, heading, theater)
|
dest = cls._find_ground_position(initial_dest, from_cp.position.distance_to_point(to_cp.position) / 3, heading, theater)
|
||||||
if not dest:
|
if not dest:
|
||||||
|
|||||||
@@ -21,19 +21,22 @@ WEATHER_CLOUD_DENSITY = 1, 8
|
|||||||
WEATHER_CLOUD_THICKNESS = 100, 400
|
WEATHER_CLOUD_THICKNESS = 100, 400
|
||||||
WEATHER_CLOUD_BASE_MIN = 1600
|
WEATHER_CLOUD_BASE_MIN = 1600
|
||||||
|
|
||||||
|
WEATHER_FOG_CHANCE = 20
|
||||||
|
WEATHER_FOG_VISIBILITY = 2500, 5000
|
||||||
|
WEATHER_FOG_THICKNESS = 100, 500
|
||||||
|
|
||||||
RANDOM_TIME = {
|
RANDOM_TIME = {
|
||||||
"night": 5,
|
"night": 7,
|
||||||
"dusk": 30,
|
"dusk": 40,
|
||||||
"dawn": 30,
|
"dawn": 40,
|
||||||
"day": 100,
|
"day": 100,
|
||||||
}
|
}
|
||||||
|
|
||||||
RANDOM_WEATHER = {
|
RANDOM_WEATHER = {
|
||||||
1: 0, # heavy rain
|
1: 0, # thunderstorm
|
||||||
2: 10, # rain
|
2: 20, # rain
|
||||||
3: 20, # dynamic
|
3: 80, # clouds
|
||||||
4: 30, # clear
|
4: 100, # clear
|
||||||
5: 100, # random
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -49,7 +52,8 @@ class EnviromentGenerator:
|
|||||||
self.game = game
|
self.game = game
|
||||||
|
|
||||||
def _gen_random_time(self):
|
def _gen_random_time(self):
|
||||||
start_time = datetime.fromtimestamp(1527206400)
|
start_time = datetime.strptime('May 25 2018 12:00AM', '%b %d %Y %I:%M%p')
|
||||||
|
|
||||||
time_range = None
|
time_range = None
|
||||||
for k, v in RANDOM_TIME.items():
|
for k, v in RANDOM_TIME.items():
|
||||||
if self.game.settings.night_disabled and k == "night":
|
if self.game.settings.night_disabled and k == "night":
|
||||||
@@ -60,8 +64,36 @@ class EnviromentGenerator:
|
|||||||
break
|
break
|
||||||
|
|
||||||
start_time += timedelta(hours=random.randint(*time_range))
|
start_time += timedelta(hours=random.randint(*time_range))
|
||||||
|
logging.info("time - {}, slot - {}, night skipped - {}".format(
|
||||||
|
str(start_time),
|
||||||
|
str(time_range),
|
||||||
|
self.game.settings.night_disabled))
|
||||||
|
|
||||||
self.mission.start_time = start_time
|
self.mission.start_time = start_time
|
||||||
|
|
||||||
|
def _generate_wind(self, wind_speed, wind_direction=None):
|
||||||
|
# wind
|
||||||
|
if not wind_direction:
|
||||||
|
wind_direction = random.randint(0, 360)
|
||||||
|
|
||||||
|
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)
|
||||||
|
|
||||||
|
def _generate_base_weather(self):
|
||||||
|
# clouds
|
||||||
|
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
|
||||||
|
self._generate_wind(random.randint(0, 4))
|
||||||
|
|
||||||
|
# fog
|
||||||
|
if random.randint(0, 100) < WEATHER_FOG_CHANCE:
|
||||||
|
self.mission.weather.fog_visibility = random.randint(*WEATHER_FOG_VISIBILITY)
|
||||||
|
self.mission.weather.fog_thickness = random.randint(*WEATHER_FOG_THICKNESS)
|
||||||
|
|
||||||
def _gen_random_weather(self):
|
def _gen_random_weather(self):
|
||||||
weather_type = None
|
weather_type = None
|
||||||
for k, v in RANDOM_WEATHER.items():
|
for k, v in RANDOM_WEATHER.items():
|
||||||
@@ -71,32 +103,33 @@ class EnviromentGenerator:
|
|||||||
|
|
||||||
logging.info("generated weather {}".format(weather_type))
|
logging.info("generated weather {}".format(weather_type))
|
||||||
if weather_type == 1:
|
if weather_type == 1:
|
||||||
self.mission.weather.heavy_rain()
|
# thunderstorm
|
||||||
elif weather_type == 2:
|
self._generate_base_weather()
|
||||||
self.mission.weather.heavy_rain()
|
self._generate_wind(random.randint(8, 12))
|
||||||
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)
|
self.mission.weather.clouds_density = random.randint(9, 10)
|
||||||
wind_speed = random.randint(0, 13)
|
self.mission.weather.clouds_iprecptns = Weather.Preceptions.Thunderstorm
|
||||||
self.mission.weather.wind_at_ground = Wind(wind_direction, wind_speed)
|
elif weather_type == 2:
|
||||||
self.mission.weather.wind_at_2000 = Wind(wind_direction, wind_speed * 2)
|
# rain
|
||||||
self.mission.weather.wind_at_8000 = Wind(wind_direction, wind_speed * 3)
|
self._generate_base_weather()
|
||||||
|
self.mission.weather.clouds_density = random.randint(5, 8)
|
||||||
|
self.mission.weather.clouds_iprecptns = Weather.Preceptions.Rain
|
||||||
|
|
||||||
|
self._generate_wind(random.randint(4, 8))
|
||||||
|
elif weather_type == 3:
|
||||||
|
# clouds
|
||||||
|
self._generate_base_weather()
|
||||||
|
elif weather_type == 4:
|
||||||
|
# clear
|
||||||
|
pass
|
||||||
|
|
||||||
if self.mission.weather.clouds_density > 0:
|
if self.mission.weather.clouds_density > 0:
|
||||||
# sometimes clouds are randomized way too low and need to be fixed
|
# sometimes clouds are randomized way too low and need to be fixed
|
||||||
self.mission.weather.clouds_base = max(self.mission.weather.clouds_base, WEATHER_CLOUD_BASE_MIN)
|
self.mission.weather.clouds_base = max(self.mission.weather.clouds_base, WEATHER_CLOUD_BASE_MIN)
|
||||||
|
|
||||||
if self.mission.weather.wind_at_ground == 0:
|
if self.mission.weather.wind_at_ground.speed == 0:
|
||||||
# frontline smokes look silly w/o any wind
|
# frontline smokes look silly w/o any wind
|
||||||
self.mission.weather.wind_at_ground = random.randint(1, 2)
|
self._generate_wind(1)
|
||||||
|
|
||||||
def generate(self) -> EnvironmentSettings:
|
def generate(self) -> EnvironmentSettings:
|
||||||
self._gen_random_time()
|
self._gen_random_time()
|
||||||
|
|||||||
45
gen/forcedoptionsgen.py
Normal file
@@ -0,0 +1,45 @@
|
|||||||
|
import logging
|
||||||
|
import typing
|
||||||
|
from enum import IntEnum
|
||||||
|
|
||||||
|
from dcs.mission import Mission
|
||||||
|
from dcs.forcedoptions import ForcedOptions
|
||||||
|
|
||||||
|
from .conflictgen import *
|
||||||
|
|
||||||
|
|
||||||
|
class Labels(IntEnum):
|
||||||
|
Off = 0
|
||||||
|
Full = 1
|
||||||
|
Abbreviated = 2
|
||||||
|
Dot = 3
|
||||||
|
|
||||||
|
|
||||||
|
class ForcedOptionsGenerator:
|
||||||
|
def __init__(self, mission: Mission, conflict: Conflict, game):
|
||||||
|
self.mission = mission
|
||||||
|
self.conflict = conflict
|
||||||
|
self.game = game
|
||||||
|
|
||||||
|
def _set_options_view(self):
|
||||||
|
if self.game.settings.map_coalition_visibility == "All Units":
|
||||||
|
self.mission.forced_options.options_view = ForcedOptions.Views.All
|
||||||
|
elif self.game.settings.map_coalition_visibility == "Allied Units":
|
||||||
|
self.mission.forced_options.options_view = ForcedOptions.Views.Allies
|
||||||
|
elif self.game.settings.map_coalition_visibility == "Own Aircraft":
|
||||||
|
self.mission.forced_options.options_view = ForcedOptions.Views.MyAircraft
|
||||||
|
elif self.game.settings.map_coalition_visibility == "None":
|
||||||
|
self.mission.forced_options.options_view = ForcedOptions.Views.OnlyMap
|
||||||
|
|
||||||
|
def _set_labels(self):
|
||||||
|
if self.game.settings.labels == "Abbreviated":
|
||||||
|
self.mission.forced_options.labels = int(Labels.Abbreviated)
|
||||||
|
elif self.game.settings.labels == "Dot Only":
|
||||||
|
self.mission.forced_options.labels = int(Labels.Dot)
|
||||||
|
elif self.game.settings.labels == "Off":
|
||||||
|
self.mission.forced_options.labels = int(Labels.Off)
|
||||||
|
|
||||||
|
def generate(self):
|
||||||
|
self._set_options_view()
|
||||||
|
self._set_labels()
|
||||||
|
|
||||||
@@ -7,24 +7,40 @@ from .naming import *
|
|||||||
from dcs.mission import *
|
from dcs.mission import *
|
||||||
from dcs.statics import *
|
from dcs.statics import *
|
||||||
|
|
||||||
|
FARP_FRONTLINE_DISTANCE = 10000
|
||||||
CATEGORY_MAPPING = {
|
AA_CP_MIN_DISTANCE = 40000
|
||||||
"power": [Fortification.Workshop_A],
|
|
||||||
"warehouse": [Warehouse.Warehouse],
|
|
||||||
"fuel": [Warehouse.Tank],
|
|
||||||
"ammo": [Warehouse.Ammunition_depot],
|
|
||||||
"farp": [Fortification.FARP_Tent],
|
|
||||||
"comms": [Fortification.TV_tower],
|
|
||||||
"oil": [Fortification.Oil_platform],
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
class GroundObjectsGenerator:
|
class GroundObjectsGenerator:
|
||||||
|
FARP_CAPACITY = 4
|
||||||
|
|
||||||
def __init__(self, mission: Mission, conflict: Conflict, game):
|
def __init__(self, mission: Mission, conflict: Conflict, game):
|
||||||
self.m = mission
|
self.m = mission
|
||||||
self.conflict = conflict
|
self.conflict = conflict
|
||||||
self.game = game
|
self.game = game
|
||||||
|
|
||||||
|
def generate_farps(self, number_of_units=1) -> typing.Collection[StaticGroup]:
|
||||||
|
if self.conflict.is_vector:
|
||||||
|
center = self.conflict.center
|
||||||
|
heading = self.conflict.heading - 90
|
||||||
|
else:
|
||||||
|
center, heading = self.conflict.frontline_position(self.conflict.theater, self.conflict.from_cp, self.conflict.to_cp)
|
||||||
|
heading -= 90
|
||||||
|
|
||||||
|
initial_position = center.point_from_heading(heading, FARP_FRONTLINE_DISTANCE)
|
||||||
|
position = self.conflict.find_ground_position(initial_position, heading)
|
||||||
|
if not position:
|
||||||
|
position = initial_position
|
||||||
|
|
||||||
|
for i, _ in enumerate(range(0, number_of_units, self.FARP_CAPACITY)):
|
||||||
|
position = position.point_from_heading(0, i * 275)
|
||||||
|
|
||||||
|
yield self.m.farp(
|
||||||
|
country=self.m.country(self.game.player),
|
||||||
|
name="FARP",
|
||||||
|
position=position,
|
||||||
|
)
|
||||||
|
|
||||||
def generate(self):
|
def generate(self):
|
||||||
side = self.m.country(self.game.enemy)
|
side = self.m.country(self.game.enemy)
|
||||||
|
|
||||||
@@ -35,7 +51,13 @@ class GroundObjectsGenerator:
|
|||||||
cp = self.conflict.from_cp
|
cp = self.conflict.from_cp
|
||||||
|
|
||||||
for ground_object in cp.ground_objects:
|
for ground_object in cp.ground_objects:
|
||||||
if ground_object.category == "defense":
|
if ground_object.dcs_identifier == "AA":
|
||||||
|
if ground_object.position.distance_to_point(self.conflict.from_cp.position) < AA_CP_MIN_DISTANCE:
|
||||||
|
continue
|
||||||
|
|
||||||
|
if ground_object.is_dead:
|
||||||
|
continue
|
||||||
|
|
||||||
unit_type = random.choice(self.game.commision_unit_types(cp, AirDefence))
|
unit_type = random.choice(self.game.commision_unit_types(cp, AirDefence))
|
||||||
assert unit_type is not None, "Cannot find unit type for GroundObject defense ({})!".format(cp)
|
assert unit_type is not None, "Cannot find unit type for GroundObject defense ({})!".format(cp)
|
||||||
|
|
||||||
@@ -44,17 +66,27 @@ class GroundObjectsGenerator:
|
|||||||
name=ground_object.string_identifier,
|
name=ground_object.string_identifier,
|
||||||
_type=unit_type,
|
_type=unit_type,
|
||||||
position=ground_object.position,
|
position=ground_object.position,
|
||||||
heading=ground_object.heading
|
heading=ground_object.heading,
|
||||||
)
|
)
|
||||||
|
|
||||||
logging.info("generated defense object identifier {} with mission id {}".format(group.name, group.id))
|
logging.info("generated defense object identifier {} with mission id {}".format(group.name, group.id))
|
||||||
else:
|
else:
|
||||||
|
if ground_object.dcs_identifier in warehouse_map:
|
||||||
|
static_type = warehouse_map[ground_object.dcs_identifier]
|
||||||
|
else:
|
||||||
|
static_type = fortification_map[ground_object.dcs_identifier]
|
||||||
|
|
||||||
|
if not static_type:
|
||||||
|
print("Didn't find {} in static _map(s)!".format(ground_object.dcs_identifier))
|
||||||
|
continue
|
||||||
|
|
||||||
group = self.m.static_group(
|
group = self.m.static_group(
|
||||||
country=side,
|
country=side,
|
||||||
name=ground_object.string_identifier,
|
name=ground_object.string_identifier,
|
||||||
_type=random.choice(CATEGORY_MAPPING[ground_object.category]),
|
_type=static_type,
|
||||||
position=ground_object.position,
|
position=ground_object.position,
|
||||||
heading=ground_object.heading
|
heading=ground_object.heading,
|
||||||
|
dead=ground_object.is_dead,
|
||||||
)
|
)
|
||||||
|
|
||||||
logging.info("generated object identifier {} with mission id {}".format(group.name, group.id))
|
logging.info("generated {}object identifier {} with mission id {}".format("dead " if ground_object.is_dead else "", group.name, group.id))
|
||||||
|
|||||||
@@ -1,5 +0,0 @@
|
|||||||
from .aircraft import *
|
|
||||||
|
|
||||||
|
|
||||||
class HelicopterConflictGenerator(AircraftConflictGenerator):
|
|
||||||
pass
|
|
||||||
@@ -16,7 +16,13 @@ class ShipGenerator:
|
|||||||
self.m = mission
|
self.m = mission
|
||||||
self.conflict = conflict
|
self.conflict = conflict
|
||||||
|
|
||||||
def generate_carrier(self, type: ShipType, country: str, at: Point) -> ShipGroup:
|
def generate_carrier(self, for_units: typing.Collection[UnitType], country: str, at: Point) -> ShipGroup:
|
||||||
|
type = db.find_unittype(Carriage, country)[0]
|
||||||
|
for unit_type in for_units:
|
||||||
|
if unit_type in db.CARRIER_TYPE_BY_PLANE:
|
||||||
|
type = db.CARRIER_TYPE_BY_PLANE[unit_type]
|
||||||
|
break
|
||||||
|
|
||||||
group = self.m.ship_group(
|
group = self.m.ship_group(
|
||||||
country=self.m.country(country),
|
country=self.m.country(country),
|
||||||
name=namegen.next_carrier_name(self.m.country(country)),
|
name=namegen.next_carrier_name(self.m.country(country)),
|
||||||
@@ -29,17 +35,19 @@ class ShipGenerator:
|
|||||||
|
|
||||||
def generate_cargo(self, units: db.ShipDict) -> typing.Collection[ShipGroup]:
|
def generate_cargo(self, units: db.ShipDict) -> typing.Collection[ShipGroup]:
|
||||||
groups = []
|
groups = []
|
||||||
|
offset = 0
|
||||||
for unit_type, unit_count in units.items():
|
for unit_type, unit_count in units.items():
|
||||||
logging.info("shipgen: {} ({}) for {}".format(unit_type, unit_count, self.conflict.defenders_side))
|
for _ in range(unit_count):
|
||||||
group = self.m.ship_group(
|
offset += 1
|
||||||
country=self.conflict.defenders_side,
|
logging.info("shipgen: {} ({}) for {}".format(unit_type, unit_count, self.conflict.defenders_side))
|
||||||
name=namegen.next_unit_name(self.conflict.defenders_side, unit_type),
|
group = self.m.ship_group(
|
||||||
_type=unit_type,
|
country=self.conflict.defenders_side,
|
||||||
position=self.conflict.ground_defenders_location.random_point_within(SHIP_RANDOM_SPREAD, SHIP_RANDOM_SPREAD),
|
name=namegen.next_unit_name(self.conflict.defenders_side, unit_type),
|
||||||
group_size=unit_count,
|
_type=unit_type,
|
||||||
)
|
position=self.conflict.ground_defenders_location.random_point_within(SHIP_RANDOM_SPREAD, SHIP_RANDOM_SPREAD).point_from_heading(0, offset * SHIP_RANDOM_SPREAD)
|
||||||
|
)
|
||||||
|
|
||||||
group.add_waypoint(self.conflict.to_cp.position)
|
group.add_waypoint(self.conflict.to_cp.position)
|
||||||
groups.append(group)
|
groups.append(group)
|
||||||
|
|
||||||
return groups
|
return groups
|
||||||
|
|||||||
@@ -12,20 +12,23 @@ from dcs.action import *
|
|||||||
|
|
||||||
from game import db
|
from game import db
|
||||||
from theater import *
|
from theater import *
|
||||||
|
from gen.airsupportgen import AirSupportConflictGenerator
|
||||||
from gen import *
|
from gen import *
|
||||||
|
|
||||||
PUSH_TRIGGER_SIZE = 3000
|
PUSH_TRIGGER_SIZE = 3000
|
||||||
|
PUSH_TRIGGER_ACTIVATION_AGL = 25
|
||||||
|
|
||||||
REGROUP_ZONE_DISTANCE = 12000
|
REGROUP_ZONE_DISTANCE = 12000
|
||||||
REGROUP_ALT = 5000
|
REGROUP_ALT = 5000
|
||||||
|
|
||||||
TRIGGER_WAYPOINT_OFFSET = 2
|
TRIGGER_WAYPOINT_OFFSET = 2
|
||||||
TRIGGER_MIN_DISTANCE_FROM_START = 10000
|
TRIGGER_MIN_DISTANCE_FROM_START = 10000
|
||||||
TRIGGER_RADIUS_MINIMUM = 25000
|
TRIGGER_RADIUS_MINIMUM = 20000
|
||||||
|
|
||||||
TRIGGER_RADIUS_SMALL = 30000
|
TRIGGER_RADIUS_SMALL = 50000
|
||||||
TRIGGER_RADIUS_MEDIUM = 100000
|
TRIGGER_RADIUS_MEDIUM = 100000
|
||||||
TRIGGER_RADIUS_LARGE = 150000
|
TRIGGER_RADIUS_LARGE = 150000
|
||||||
|
TRIGGER_RADIUS_ALL_MAP = 3000000
|
||||||
|
|
||||||
|
|
||||||
class Silence(Option):
|
class Silence(Option):
|
||||||
@@ -56,9 +59,9 @@ class TriggersGenerator:
|
|||||||
if minimum_radius < 0:
|
if minimum_radius < 0:
|
||||||
minimum_radius = 0
|
minimum_radius = 0
|
||||||
|
|
||||||
result_radius = min(minimum_radius, radius)
|
radius = min(minimum_radius, radius)
|
||||||
|
|
||||||
activation_trigger_zone = self.mission.triggers.add_triggerzone(self.conflict.position, result_radius, name="Activation zone")
|
activation_trigger_zone = self.mission.triggers.add_triggerzone(self.conflict.position, radius, name="Activation zone")
|
||||||
activation_trigger = TriggerOnce(Event.NoEvent, "Activation trigger")
|
activation_trigger = TriggerOnce(Event.NoEvent, "Activation trigger")
|
||||||
activation_trigger.add_condition(PartOfCoalitionInZone(player_coalition, activation_trigger_zone.id))
|
activation_trigger.add_condition(PartOfCoalitionInZone(player_coalition, activation_trigger_zone.id))
|
||||||
activation_trigger.add_condition(FlagIsTrue())
|
activation_trigger.add_condition(FlagIsTrue())
|
||||||
@@ -72,41 +75,45 @@ class TriggersGenerator:
|
|||||||
for coalition_name, coalition in self.mission.coalition.items():
|
for coalition_name, coalition in self.mission.coalition.items():
|
||||||
for country in coalition.countries.values():
|
for country in coalition.countries.values():
|
||||||
if coalition_name == player_coalition:
|
if coalition_name == player_coalition:
|
||||||
for plane_group in country.plane_group + country.helicopter_group:
|
for group in country.plane_group + country.helicopter_group:
|
||||||
if plane_group.task == AWACS.name or plane_group.task == Refueling.name:
|
if group.task == AWACS.name or group.task == Refueling.name:
|
||||||
continue
|
continue
|
||||||
|
|
||||||
regroup_heading = self.conflict.to_cp.position.heading_between_point(player_cp.position)
|
push_by_trigger.append(group)
|
||||||
|
|
||||||
pos1 = plane_group.position.point_from_heading(regroup_heading, REGROUP_ZONE_DISTANCE)
|
if not group.units[0].is_human():
|
||||||
pos2 = plane_group.position.point_from_heading(regroup_heading, REGROUP_ZONE_DISTANCE+5000)
|
regroup_heading = self.conflict.to_cp.position.heading_between_point(player_cp.position)
|
||||||
w1 = plane_group.add_waypoint(pos1, REGROUP_ALT)
|
|
||||||
w2 = plane_group.add_waypoint(pos2, REGROUP_ALT)
|
|
||||||
|
|
||||||
plane_group.points.remove(w1)
|
pos1 = group.position.point_from_heading(regroup_heading, REGROUP_ZONE_DISTANCE)
|
||||||
plane_group.points.remove(w2)
|
pos2 = group.position.point_from_heading(regroup_heading, REGROUP_ZONE_DISTANCE+5000)
|
||||||
|
w1 = group.add_waypoint(pos1, REGROUP_ALT)
|
||||||
|
w2 = group.add_waypoint(pos2, REGROUP_ALT)
|
||||||
|
|
||||||
plane_group.points.insert(1, w2)
|
group.points.remove(w1)
|
||||||
plane_group.points.insert(1, w1)
|
group.points.remove(w2)
|
||||||
|
|
||||||
w1.tasks.append(Silence(True))
|
group.points.insert(1, w2)
|
||||||
|
group.points.insert(1, w1)
|
||||||
|
|
||||||
switch_waypoint_task = ControlledTask(SwitchWaypoint(from_waypoint=3, to_waypoint=2))
|
w1.tasks.append(Silence(True))
|
||||||
switch_waypoint_task.start_if_user_flag(1, False)
|
|
||||||
w2.tasks.append(switch_waypoint_task)
|
|
||||||
plane_group.points[3].tasks.append(Silence(False))
|
|
||||||
|
|
||||||
plane_group.add_trigger_action(SwitchWaypoint(to_waypoint=4))
|
switch_waypoint_task = ControlledTask(SwitchWaypoint(from_waypoint=3, to_waypoint=2))
|
||||||
push_by_trigger.append(plane_group)
|
switch_waypoint_task.start_if_user_flag(1, False)
|
||||||
|
w2.tasks.append(switch_waypoint_task)
|
||||||
|
group.points[3].tasks.append(Silence(False))
|
||||||
|
|
||||||
|
group.add_trigger_action(SwitchWaypoint(to_waypoint=4))
|
||||||
|
|
||||||
push_trigger_zone = self.mission.triggers.add_triggerzone(player_cp.position, PUSH_TRIGGER_SIZE, name="Push zone")
|
|
||||||
push_trigger = TriggerOnce(Event.NoEvent, "Push trigger")
|
push_trigger = TriggerOnce(Event.NoEvent, "Push trigger")
|
||||||
|
|
||||||
for group in push_by_trigger:
|
for group in push_by_trigger:
|
||||||
push_trigger.add_condition(AllOfGroupOutsideZone(group.id, push_trigger_zone.id))
|
for unit in group.units:
|
||||||
push_trigger.add_action(AITaskPush(group.id, 1))
|
push_trigger.add_condition(UnitAltitudeHigherAGL(unit.id, PUSH_TRIGGER_ACTIVATION_AGL))
|
||||||
|
|
||||||
message_string = self.mission.string("Task force is in the air, proceed with the objective (activate waypoint 3).")
|
if not group.units[0].is_human():
|
||||||
|
push_trigger.add_action(AITaskPush(group.id, 1))
|
||||||
|
|
||||||
|
message_string = self.mission.string("Task force is in the air, proceed with the objective.")
|
||||||
push_trigger.add_action(MessageToAll(message_string, clearview=True))
|
push_trigger.add_action(MessageToAll(message_string, clearview=True))
|
||||||
push_trigger.add_action(SetFlagValue())
|
push_trigger.add_action(SetFlagValue())
|
||||||
|
|
||||||
@@ -121,9 +128,9 @@ class TriggersGenerator:
|
|||||||
def _set_skill(self, player_coalition: str, enemy_coalition: str):
|
def _set_skill(self, player_coalition: str, enemy_coalition: str):
|
||||||
for coalition_name, coalition in self.mission.coalition.items():
|
for coalition_name, coalition in self.mission.coalition.items():
|
||||||
if coalition_name == player_coalition:
|
if coalition_name == player_coalition:
|
||||||
skill_level = self.game.settings.player_skill
|
skill_level = self.game.settings.player_skill, self.game.settings.player_skill
|
||||||
elif coalition_name == enemy_coalition:
|
elif coalition_name == enemy_coalition:
|
||||||
skill_level = self.game.settings.enemy_skill
|
skill_level = self.game.settings.enemy_skill, self.game.settings.enemy_vehicle_skill
|
||||||
else:
|
else:
|
||||||
continue
|
continue
|
||||||
|
|
||||||
@@ -131,10 +138,10 @@ class TriggersGenerator:
|
|||||||
for plane_group in country.plane_group:
|
for plane_group in country.plane_group:
|
||||||
for plane_unit in plane_group.units:
|
for plane_unit in plane_group.units:
|
||||||
if plane_unit.skill != Skill.Client and plane_unit.skill != Skill.Player:
|
if plane_unit.skill != Skill.Client and plane_unit.skill != Skill.Player:
|
||||||
plane_unit.skill = Skill(skill_level)
|
plane_unit.skill = Skill(skill_level[0])
|
||||||
|
|
||||||
for vehicle_group in country.vehicle_group:
|
for vehicle_group in country.vehicle_group:
|
||||||
vehicle_group.set_skill(Skill(skill_level))
|
vehicle_group.set_skill(Skill(skill_level[1]))
|
||||||
|
|
||||||
def generate(self, player_cp: ControlPoint, is_quick: bool, activation_trigger_radius: int, awacs_enabled: bool):
|
def generate(self, player_cp: ControlPoint, is_quick: bool, activation_trigger_radius: int, awacs_enabled: bool):
|
||||||
player_coalition = self.game.player == "USA" and "blue" or "red"
|
player_coalition = self.game.player == "USA" and "blue" or "red"
|
||||||
|
|||||||
@@ -9,6 +9,7 @@ from dcs.unit import Static
|
|||||||
from theater import *
|
from theater import *
|
||||||
from .conflictgen import *
|
from .conflictgen import *
|
||||||
#from game.game import Game
|
#from game.game import Game
|
||||||
|
from game import db
|
||||||
|
|
||||||
|
|
||||||
class MarkerSmoke(unittype.StaticType):
|
class MarkerSmoke(unittype.StaticType):
|
||||||
@@ -98,7 +99,14 @@ class VisualGenerator:
|
|||||||
|
|
||||||
def _generate_frontline_smokes(self):
|
def _generate_frontline_smokes(self):
|
||||||
for from_cp, to_cp in self.game.theater.conflicts():
|
for from_cp, to_cp in self.game.theater.conflicts():
|
||||||
point, heading = Conflict.frontline_position(from_cp, to_cp)
|
if from_cp.is_global or to_cp.is_global:
|
||||||
|
continue
|
||||||
|
|
||||||
|
frontline = Conflict.frontline_position(self.game.theater, from_cp, to_cp)
|
||||||
|
if not frontline:
|
||||||
|
continue
|
||||||
|
|
||||||
|
point, heading = frontline
|
||||||
plane_start = point.point_from_heading(turn_heading(heading, 90), FRONTLINE_LENGTH / 2)
|
plane_start = point.point_from_heading(turn_heading(heading, 90), FRONTLINE_LENGTH / 2)
|
||||||
|
|
||||||
for offset in range(0, FRONTLINE_LENGTH, FRONT_SMOKE_SPACING):
|
for offset in range(0, FRONTLINE_LENGTH, FRONT_SMOKE_SPACING):
|
||||||
@@ -117,6 +125,17 @@ class VisualGenerator:
|
|||||||
position=pos)
|
position=pos)
|
||||||
break
|
break
|
||||||
|
|
||||||
|
def _generate_stub_planes(self):
|
||||||
|
mission_units = set()
|
||||||
|
for coalition_name, coalition in self.mission.coalition.items():
|
||||||
|
for country in coalition.countries.values():
|
||||||
|
for group in country.plane_group + country.helicopter_group + country.vehicle_group:
|
||||||
|
for unit in group.units:
|
||||||
|
mission_units.add(db.unit_type_of(unit))
|
||||||
|
|
||||||
|
for unit_type in mission_units:
|
||||||
|
self.mission.static_group(self.mission.country("USA"), "a", unit_type, Point(0, 300000), hidden=True)
|
||||||
|
|
||||||
def generate_target_smokes(self, target):
|
def generate_target_smokes(self, target):
|
||||||
spread = target.size * DESTINATION_SMOKE_DISTANCE_FACTOR
|
spread = target.size * DESTINATION_SMOKE_DISTANCE_FACTOR
|
||||||
for _ in range(0, int(target.size * DESTINATION_SMOKE_AMOUNT_FACTOR * (1.1 - target.base.strength))):
|
for _ in range(0, int(target.size * DESTINATION_SMOKE_AMOUNT_FACTOR * (1.1 - target.base.strength))):
|
||||||
@@ -152,3 +171,4 @@ class VisualGenerator:
|
|||||||
|
|
||||||
def generate(self):
|
def generate(self):
|
||||||
self._generate_frontline_smokes()
|
self._generate_frontline_smokes()
|
||||||
|
self._generate_stub_planes()
|
||||||
|
|||||||
40
pyinstaller.spec
Normal file
@@ -0,0 +1,40 @@
|
|||||||
|
# -*- mode: python -*-
|
||||||
|
|
||||||
|
block_cipher = None
|
||||||
|
|
||||||
|
|
||||||
|
a = Analysis(['__init__.py'],
|
||||||
|
pathex=['C:\\Users\\shdwp\\PycharmProjects\\dcs_liberation'],
|
||||||
|
binaries=[],
|
||||||
|
datas=[
|
||||||
|
('resources', 'resources'),
|
||||||
|
('submodules/dcs/dcs/terrain/caucasus.p', 'dcs/terrain/'),
|
||||||
|
('submodules/dcs/dcs/terrain/nevada.p', 'dcs/terrain/'),
|
||||||
|
],
|
||||||
|
hookspath=[],
|
||||||
|
runtime_hooks=[],
|
||||||
|
excludes=[],
|
||||||
|
win_no_prefer_redirects=False,
|
||||||
|
win_private_assemblies=False,
|
||||||
|
cipher=block_cipher,
|
||||||
|
noarchive=False)
|
||||||
|
pyz = PYZ(a.pure, a.zipped_data,
|
||||||
|
cipher=block_cipher)
|
||||||
|
exe = EXE(pyz,
|
||||||
|
a.scripts,
|
||||||
|
[],
|
||||||
|
icon="resources/icon.ico",
|
||||||
|
exclude_binaries=True,
|
||||||
|
name='liberation_main',
|
||||||
|
debug=False,
|
||||||
|
bootloader_ignore_signals=False,
|
||||||
|
strip=False,
|
||||||
|
upx=True,
|
||||||
|
console=True )
|
||||||
|
coll = COLLECT(exe,
|
||||||
|
a.binaries,
|
||||||
|
a.zipfiles,
|
||||||
|
a.datas,
|
||||||
|
strip=False,
|
||||||
|
upx=True,
|
||||||
|
name='dcs_liberation')
|
||||||
|
Before Width: | Height: | Size: 157 KiB After Width: | Height: | Size: 247 KiB |
BIN
resources/groundobject_templates.p
Normal file
|
Before Width: | Height: | Size: 81 KiB After Width: | Height: | Size: 81 KiB |
|
Before Width: | Height: | Size: 117 KiB After Width: | Height: | Size: 467 KiB |
BIN
resources/nevlandmap.p
Normal file
|
Before Width: | Height: | Size: 62 KiB After Width: | Height: | Size: 260 KiB |
38
resources/tools/generate_example_groundobjects.py
Normal file
@@ -0,0 +1,38 @@
|
|||||||
|
import typing
|
||||||
|
|
||||||
|
from dcs.mission import *
|
||||||
|
from dcs.terrain import *
|
||||||
|
|
||||||
|
from theater.nevada import *
|
||||||
|
from theater.persiangulf import *
|
||||||
|
from theater.caucasus import *
|
||||||
|
from theater.controlpoint import *
|
||||||
|
|
||||||
|
def find_ground_location(near, theater, max, min) -> typing.Optional[Point]:
|
||||||
|
for _ in range(500):
|
||||||
|
p = near.random_point_within(max, min)
|
||||||
|
if theater.is_on_land(p):
|
||||||
|
return p
|
||||||
|
|
||||||
|
return None
|
||||||
|
|
||||||
|
|
||||||
|
mission = Mission(Nevada())
|
||||||
|
theater = NevadaTheater()
|
||||||
|
|
||||||
|
for cp in theater.enemy_points():
|
||||||
|
for _ in range(0, random.randrange(3, 6)):
|
||||||
|
p = find_ground_location(cp.position, theater, 120000, 5000)
|
||||||
|
if not p:
|
||||||
|
print("Didn't find ground location for {}".format(cp))
|
||||||
|
continue
|
||||||
|
|
||||||
|
mission.flight_group_inflight(
|
||||||
|
mission.country("USA"),
|
||||||
|
"",
|
||||||
|
A_10C,
|
||||||
|
p,
|
||||||
|
10000
|
||||||
|
)
|
||||||
|
|
||||||
|
mission.save("resources/tools/ground_objects_example.miz")
|
||||||
50
resources/tools/generate_groundobject_templates.py
Normal file
@@ -0,0 +1,50 @@
|
|||||||
|
import pickle
|
||||||
|
import typing
|
||||||
|
|
||||||
|
from dcs.mission import Mission
|
||||||
|
from dcs.mapping import Point
|
||||||
|
from dcs.unit import *
|
||||||
|
from dcs.statics import warehouse_map, fortification_map
|
||||||
|
|
||||||
|
|
||||||
|
def load_templates():
|
||||||
|
temp_mis = Mission()
|
||||||
|
temp_mis.load_file("resources/tools/groundobject_templates.miz")
|
||||||
|
|
||||||
|
groups = {} # type: typing.Dict[str, typing.Dict[int, typing.List[Static]]]
|
||||||
|
|
||||||
|
for static_group in temp_mis.country("USA").static_group:
|
||||||
|
for static in static_group.units:
|
||||||
|
static_name = str(static.name).split()[0]
|
||||||
|
tpl_name, tpl_idx = static_name[:-1], int(static_name[-1])
|
||||||
|
|
||||||
|
groups[tpl_name] = groups.get(tpl_name, {})
|
||||||
|
groups[tpl_name][tpl_idx] = groups[tpl_name].get(tpl_idx, [])
|
||||||
|
groups[tpl_name][tpl_idx].append(static)
|
||||||
|
|
||||||
|
tpls = {name: {idx: [] for idx in groups[name].keys()} for name in groups.keys()}
|
||||||
|
for category_name, category_groups in groups.items():
|
||||||
|
for idx, static_groups in category_groups.items():
|
||||||
|
dist = -1
|
||||||
|
a, b = None, None
|
||||||
|
for aa in static_groups:
|
||||||
|
for bb in static_groups:
|
||||||
|
if aa.position.distance_to_point(bb.position) > dist:
|
||||||
|
dist = aa.position.distance_to_point(bb.position)
|
||||||
|
a = aa
|
||||||
|
b = bb
|
||||||
|
|
||||||
|
center = a.position.point_from_heading(a.position.heading_between_point(b.position), dist / 2)
|
||||||
|
for static in static_groups:
|
||||||
|
tpls[category_name][idx].append({
|
||||||
|
"type": static.type,
|
||||||
|
"offset": Point(center.x - static.position.x, center.y - static.position.y),
|
||||||
|
"heading": static.heading,
|
||||||
|
})
|
||||||
|
|
||||||
|
tpls["aa"] = {0: [{"type": "AA", "offset": Point(0, 0), "heading": 0}]}
|
||||||
|
return tpls
|
||||||
|
|
||||||
|
|
||||||
|
with open("resources/groundobject_templates.p", "wb") as f:
|
||||||
|
pickle.dump(load_templates(), f)
|
||||||
@@ -1,86 +1,108 @@
|
|||||||
import pickle
|
import pickle
|
||||||
import typing
|
import typing
|
||||||
|
|
||||||
from game import db
|
|
||||||
from gen.groundobjectsgen import TheaterGroundObject
|
|
||||||
from dcs.mission import Mission
|
from dcs.mission import Mission
|
||||||
from dcs.mapping import Point
|
from dcs.mapping import Point
|
||||||
|
from dcs.terrain import *
|
||||||
|
from dcs.unitgroup import VehicleGroup, StaticGroup
|
||||||
|
from dcs import vehicles
|
||||||
|
from dcs.unit import *
|
||||||
|
from dcs.statics import warehouse_map, fortification_map
|
||||||
|
|
||||||
|
from game import db
|
||||||
|
from gen.groundobjectsgen import TheaterGroundObject
|
||||||
|
from theater.caucasus import CaucasusTheater
|
||||||
|
from theater.persiangulf import PersianGulfTheater
|
||||||
|
from theater.nevada import NevadaTheater
|
||||||
|
|
||||||
m = Mission()
|
m = Mission()
|
||||||
m.load_file("./cau_groundobjects.miz")
|
m.load_file("resources/tools/cau_groundobjects.miz")
|
||||||
|
|
||||||
result = {}
|
if isinstance(m.terrain, Caucasus):
|
||||||
result_by_groups = {} # type: typing.Dict[int, TheaterGroundObject]
|
theater = CaucasusTheater(load_ground_objects=False)
|
||||||
cp_counters = {}
|
elif isinstance(m.terrain, PersianGulf):
|
||||||
ids_counters = {}
|
theater = PersianGulfTheater(load_ground_objects=False)
|
||||||
group_id_counter = 0
|
elif isinstance(m.terrain, Nevada):
|
||||||
previous_group_id = None
|
theater = NevadaTheater(load_ground_objects=False)
|
||||||
|
else:
|
||||||
|
assert False
|
||||||
|
|
||||||
|
|
||||||
def append_group(cp_id, category, group_id, object_id, position, heading):
|
def closest_cp(location: Point) -> (int, float):
|
||||||
global result
|
global theater
|
||||||
global result_by_groups
|
min_distance, min_cp = None, None
|
||||||
|
|
||||||
ground_object = TheaterGroundObject(category, cp_id, group_id, object_id, position, heading)
|
for cp in theater.controlpoints:
|
||||||
|
if not min_distance or location.distance_to_point(cp.position) < min_distance:
|
||||||
|
min_distance = location.distance_to_point(cp.position)
|
||||||
|
min_cp = cp.id
|
||||||
|
|
||||||
if cp_id not in result:
|
assert min_cp is not None
|
||||||
result[cp_id] = []
|
return min_cp
|
||||||
result[cp_id].append(ground_object)
|
|
||||||
|
|
||||||
result_by_groups_key = "{}_{}_{}".format(cp_id, category, group_id)
|
|
||||||
if result_by_groups_key not in result_by_groups:
|
|
||||||
result_by_groups[result_by_groups_key] = []
|
|
||||||
result_by_groups[result_by_groups_key].append(ground_object)
|
|
||||||
|
|
||||||
|
|
||||||
def parse_name(name: str) -> typing.Tuple:
|
if __name__ == "__main__":
|
||||||
args = str(name.split()[0]).split("|")
|
theater_objects = []
|
||||||
|
|
||||||
if len(args) == 2:
|
for group in m.country("Russia").static_group + m.country("Russia").vehicle_group:
|
||||||
global group_id_counter
|
for unit in group.units:
|
||||||
group_id_counter += 1
|
theater_object = TheaterGroundObject()
|
||||||
args.append(str(group_id_counter))
|
theater_object.object_id = len(theater_objects) + 1
|
||||||
else:
|
|
||||||
global previous_group_id
|
|
||||||
if previous_group_id != args[2]:
|
|
||||||
group_id_counter += 1
|
|
||||||
previous_group_id = args[2]
|
|
||||||
|
|
||||||
return args[0], int(args[1]), int(args[2])
|
theater_object.position = unit.position
|
||||||
|
theater_object.heading = unit.heading
|
||||||
|
|
||||||
|
if isinstance(unit, Vehicle) and unit.type in vehicles.AirDefence.__dict__.values():
|
||||||
|
theater_object.dcs_identifier = "AA"
|
||||||
|
else:
|
||||||
|
theater_object.dcs_identifier = unit.type
|
||||||
|
|
||||||
for group in m.country("Russia").static_group + m.country("Russia").vehicle_group:
|
assert theater_object.dcs_identifier
|
||||||
try:
|
assert theater_object.object_id
|
||||||
category, cp_id, group_id = parse_name(str(group.name))
|
|
||||||
except:
|
|
||||||
print("Failed to parse {}".format(group.name))
|
|
||||||
continue
|
|
||||||
|
|
||||||
ids_counters_key = "{}_{}".format(cp_id, group_id)
|
theater_objects.append(theater_object)
|
||||||
ids_counters[ids_counters_key] = ids_counters.get(ids_counters_key, 0) + 1
|
|
||||||
object_id = ids_counters[ids_counters_key]
|
|
||||||
cp_counters[cp_id] = cp_counters.get(cp_id, 0) + 1
|
|
||||||
|
|
||||||
append_group(cp_id, category, group_id, object_id, group.position, group.units[0].heading)
|
group_ids = 1
|
||||||
|
for object_a in theater_objects:
|
||||||
|
for object_b in theater_objects:
|
||||||
|
if object_a.position.distance_to_point(object_b.position) < 2000:
|
||||||
|
if object_a.group_id and object_b.group_id:
|
||||||
|
continue
|
||||||
|
elif object_a.group_id:
|
||||||
|
object_b.group_id = object_a.group_id
|
||||||
|
object_b.cp_id = object_a.cp_id
|
||||||
|
elif object_b.group_id:
|
||||||
|
object_a.group_id = object_b.group_id
|
||||||
|
object_a.cp_id = object_b.cp_id
|
||||||
|
else:
|
||||||
|
object_a.group_id = group_ids
|
||||||
|
object_b.group_id = group_ids
|
||||||
|
object_a.cp_id = closest_cp(object_a.position)
|
||||||
|
object_b.cp_id = object_a.cp_id
|
||||||
|
group_ids += 1
|
||||||
|
|
||||||
GROUP_TRESHOLD = 2000
|
assert object_a.cp_id == object_b.cp_id, "Object {} and {} are placed in group with different airports!".format(object_a.string_identifier, object_b.string_identifier)
|
||||||
did_check_pairs = []
|
|
||||||
for group_id, objects_in_group in result_by_groups.items():
|
|
||||||
for a in objects_in_group:
|
|
||||||
for b in objects_in_group:
|
|
||||||
if (a, b) in did_check_pairs:
|
|
||||||
continue
|
|
||||||
|
|
||||||
did_check_pairs.append((a, b))
|
for a in theater_objects:
|
||||||
distance = a.position.distance_to_point(b.position)
|
if not a.group_id:
|
||||||
if distance > GROUP_TRESHOLD:
|
a.group_id = group_ids
|
||||||
print("Objects {} and {} in group {} are too far apart ({})!".format(a.string_identifier, b.string_identifier, group_id, distance))
|
a.cp_id = closest_cp(a.position)
|
||||||
|
group_ids += 1
|
||||||
|
|
||||||
print("Total {} objects".format(sum([len(x) for x in result.values()])))
|
with open("resources/cau_groundobjects.p", "wb") as f:
|
||||||
for cp_id, count in cp_counters.items():
|
result = {}
|
||||||
print("{} - {} objects".format(cp_id, count))
|
for theater_object in theater_objects:
|
||||||
|
assert theater_object.cp_id
|
||||||
|
assert theater_object.group_id
|
||||||
|
assert theater_object.object_id
|
||||||
|
|
||||||
|
if theater_object.cp_id not in result:
|
||||||
|
result[theater_object.cp_id] = []
|
||||||
|
result[theater_object.cp_id].append(theater_object)
|
||||||
|
|
||||||
with open("../cau_groundobjects.p", "wb") as f:
|
print("Total {} objects".format(len(theater_objects)))
|
||||||
pickle.dump(result, f)
|
for cp_id, objects in result.items():
|
||||||
|
print("{}: total {} objects".format(m.terrain.airport_by_id(cp_id), len(objects)))
|
||||||
|
|
||||||
|
pickle.dump(result, f)
|
||||||
|
|
||||||
|
|||||||
@@ -1,14 +1,26 @@
|
|||||||
import pickle
|
import pickle
|
||||||
|
|
||||||
from dcs.mission import Mission
|
from dcs.mission import Mission
|
||||||
|
from dcs.planes import A_10C
|
||||||
|
|
||||||
for terrain in ["cau", "gulf"]:
|
for terrain in ["cau", "gulf", "nev"]:
|
||||||
m = Mission()
|
m = Mission()
|
||||||
m.load_file("./{}_terrain.miz".format(terrain))
|
m.load_file("./{}_terrain.miz".format(terrain))
|
||||||
|
|
||||||
landmap = []
|
inclusion_zones = []
|
||||||
|
exclusion_zones = []
|
||||||
for plane_group in m.country("USA").plane_group:
|
for plane_group in m.country("USA").plane_group:
|
||||||
landmap.append([(x.position.x, x.position.y) for x in plane_group.points])
|
zone = [(x.position.x, x.position.y) for x in plane_group.points]
|
||||||
|
|
||||||
|
if terrain == "cau" and inclusion_zones:
|
||||||
|
# legacy
|
||||||
|
exclusion_zones.append(zone)
|
||||||
|
else:
|
||||||
|
if plane_group.units[0].type == "F-15C":
|
||||||
|
exclusion_zones.append(zone)
|
||||||
|
else:
|
||||||
|
inclusion_zones.append(zone)
|
||||||
|
|
||||||
with open("../{}landmap.p".format(terrain), "wb") as f:
|
with open("../{}landmap.p".format(terrain), "wb") as f:
|
||||||
pickle.dump(landmap, f)
|
print(len(inclusion_zones), len(exclusion_zones))
|
||||||
|
pickle.dump((inclusion_zones, exclusion_zones), f)
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
import os
|
import os
|
||||||
|
import sys
|
||||||
import dcs
|
import dcs
|
||||||
|
|
||||||
from game import db
|
from game import db
|
||||||
|
|||||||
BIN
resources/tools/groundobject_templates.miz
Normal file
@@ -1,4 +1,5 @@
|
|||||||
import os
|
import os
|
||||||
|
import shutil
|
||||||
|
|
||||||
from zipfile import *
|
from zipfile import *
|
||||||
|
|
||||||
@@ -42,11 +43,13 @@ def _mk_archieve():
|
|||||||
print("version already exists")
|
print("version already exists")
|
||||||
return
|
return
|
||||||
|
|
||||||
|
shutil.rmtree("./dist")
|
||||||
|
|
||||||
|
os.system("pyinstaller.exe pyinstaller.spec")
|
||||||
|
|
||||||
archieve = ZipFile(path, "w")
|
archieve = ZipFile(path, "w")
|
||||||
archieve.writestr("start.bat", "py.exe __init__.py \"%UserProfile%\" \"{}\"".format(VERSION))
|
archieve.writestr("dcs_liberation.bat", "cd dist\\dcs_liberation\r\nliberation_main \"%UserProfile%\\Saved Games\" \"{}\"".format(VERSION))
|
||||||
_zip_dir(archieve, ".")
|
_zip_dir(archieve, "./dist/dcs_liberation")
|
||||||
os.chdir("submodules\\dcs")
|
|
||||||
_zip_dir(archieve, "dcs")
|
|
||||||
|
|
||||||
|
|
||||||
_mk_archieve()
|
_mk_archieve()
|
||||||
BIN
resources/tools/nev_terrain.miz
Normal file
BIN
resources/ui/events/air_intercept.png
Normal file
|
After Width: | Height: | Size: 3.2 KiB |
BIN
resources/ui/events/attack.PNG
Normal file
|
After Width: | Height: | Size: 637 B |
BIN
resources/ui/events/capture.PNG
Normal file
|
After Width: | Height: | Size: 697 B |
BIN
resources/ui/events/convoy.png
Normal file
|
After Width: | Height: | Size: 804 B |
BIN
resources/ui/events/delivery.PNG
Normal file
|
After Width: | Height: | Size: 1.2 KiB |
BIN
resources/ui/events/infantry.PNG
Normal file
|
After Width: | Height: | Size: 910 B |
BIN
resources/ui/events/insurgent_attack.PNG
Normal file
|
After Width: | Height: | Size: 824 B |
BIN
resources/ui/events/naval_intercept.PNG
Normal file
|
After Width: | Height: | Size: 688 B |
BIN
resources/ui/events/strike.PNG
Normal file
|
After Width: | Height: | Size: 1.1 KiB |
BIN
resources/ui/ground_assets/aa.png
Normal file
|
After Width: | Height: | Size: 229 B |
BIN
resources/ui/ground_assets/ammo.png
Normal file
|
After Width: | Height: | Size: 197 B |
BIN
resources/ui/ground_assets/cleared.png
Normal file
|
After Width: | Height: | Size: 315 B |
BIN
resources/ui/ground_assets/comms.png
Normal file
|
After Width: | Height: | Size: 196 B |
BIN
resources/ui/ground_assets/factory.png
Normal file
|
After Width: | Height: | Size: 220 B |
BIN
resources/ui/ground_assets/farp.png
Normal file
|
After Width: | Height: | Size: 253 B |
BIN
resources/ui/ground_assets/fob.png
Normal file
|
After Width: | Height: | Size: 213 B |
BIN
resources/ui/ground_assets/fuel.png
Normal file
|
After Width: | Height: | Size: 224 B |
BIN
resources/ui/ground_assets/oil.png
Normal file
|
After Width: | Height: | Size: 227 B |
BIN
resources/ui/ground_assets/power.png
Normal file
|
After Width: | Height: | Size: 235 B |
BIN
resources/ui/ground_assets/target.png
Normal file
|
After Width: | Height: | Size: 230 B |
BIN
resources/ui/ground_assets/warehouse.png
Normal file
|
After Width: | Height: | Size: 232 B |
BIN
resources/ui/terrain_caucasus.gif
Normal file
|
After Width: | Height: | Size: 852 B |
|
Before Width: | Height: | Size: 1.2 KiB |
BIN
resources/ui/terrain_nevada.gif
Normal file
|
After Width: | Height: | Size: 1.4 KiB |
|
Before Width: | Height: | Size: 1.8 KiB |
BIN
resources/ui/terrain_pg.gif
Normal file
|
After Width: | Height: | Size: 1.4 KiB |
|
Before Width: | Height: | Size: 1.7 KiB |
0
tests/__init__.py
Normal file
10
tests/integration/__init__.py
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
from tests.integration import baseattack, convoystrike, frontlineattack, insurgentattack, intercept, navalintercept, strike
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
baseattack.execute_all()
|
||||||
|
convoystrike.execute_all()
|
||||||
|
frontlineattack.execute_all()
|
||||||
|
insurgentattack.execute_all()
|
||||||
|
intercept.execute_all()
|
||||||
|
navalintercept.execute_all()
|
||||||
|
strike.execute_all()
|
||||||
46
tests/integration/baseattack.py
Normal file
@@ -0,0 +1,46 @@
|
|||||||
|
from theater.caucasus import CaucasusTheater
|
||||||
|
from theater.nevada import NevadaTheater
|
||||||
|
|
||||||
|
from tests.integration.util import *
|
||||||
|
|
||||||
|
PLAYER_COUNTRY = "USA"
|
||||||
|
ENEMY_COUNTRY = "Russia"
|
||||||
|
|
||||||
|
|
||||||
|
def execute(game, player_cp, enemy_cp, departure_cp = None):
|
||||||
|
e = BaseAttackEvent(game, player_cp, enemy_cp, enemy_cp.position, PLAYER_COUNTRY, ENEMY_COUNTRY)
|
||||||
|
|
||||||
|
departures = [departure_cp] if departure_cp else game.theater.player_points()
|
||||||
|
for departure_cp in departures:
|
||||||
|
if e.is_departure_available_from(departure_cp):
|
||||||
|
print("{} for {} ({}) - {}".format(e, player_cp, departure_cp, enemy_cp))
|
||||||
|
e.departure_cp = departure_cp
|
||||||
|
e.player_attacking(autoflights_for(e, PLAYER_COUNTRY))
|
||||||
|
|
||||||
|
e.generate()
|
||||||
|
execute_autocommit(e)
|
||||||
|
e.generate_quick()
|
||||||
|
execute_autocommit(e)
|
||||||
|
|
||||||
|
|
||||||
|
def execute_theater(theater_klass):
|
||||||
|
print("Theater: {}".format(theater_klass))
|
||||||
|
game, theater = init(PLAYER_COUNTRY, ENEMY_COUNTRY, theater_klass)
|
||||||
|
|
||||||
|
total_events = 0
|
||||||
|
while len(theater.enemy_points()) > 0:
|
||||||
|
for player_cp, enemy_cp in theater.conflicts():
|
||||||
|
execute(game, player_cp, enemy_cp)
|
||||||
|
|
||||||
|
enemy_cp.captured = True
|
||||||
|
|
||||||
|
print("Total: {}".format(total_events))
|
||||||
|
|
||||||
|
|
||||||
|
def execute_all():
|
||||||
|
for theater_klass in [CaucasusTheater, PersianGulfTheater, NevadaTheater]:
|
||||||
|
execute_theater(theater_klass)
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
execute_all()
|
||||||
50
tests/integration/convoystrike.py
Normal file
@@ -0,0 +1,50 @@
|
|||||||
|
from theater.caucasus import CaucasusTheater
|
||||||
|
from theater.nevada import NevadaTheater
|
||||||
|
|
||||||
|
from tests.integration.util import *
|
||||||
|
|
||||||
|
PLAYER_COUNTRY = "USA"
|
||||||
|
ENEMY_COUNTRY = "Russia"
|
||||||
|
|
||||||
|
|
||||||
|
def execute(game, player_cp, enemy_cp, departure_cp = None):
|
||||||
|
e = ConvoyStrikeEvent(game, player_cp, enemy_cp, enemy_cp.position, PLAYER_COUNTRY, ENEMY_COUNTRY)
|
||||||
|
|
||||||
|
departures = [departure_cp] if departure_cp else game.theater.player_points()
|
||||||
|
for departure_cp in departures:
|
||||||
|
if e.is_departure_available_from(departure_cp):
|
||||||
|
enemy_cp.base.strength = 1
|
||||||
|
for _ in range(10):
|
||||||
|
print("{} for {} ({}) - {} ({})".format(e, player_cp, departure_cp, enemy_cp, enemy_cp.base.strength))
|
||||||
|
e.departure_cp = departure_cp
|
||||||
|
e.player_attacking(autoflights_for(e, PLAYER_COUNTRY))
|
||||||
|
|
||||||
|
e.generate()
|
||||||
|
execute_autocommit(e)
|
||||||
|
e.generate_quick()
|
||||||
|
execute_autocommit(e)
|
||||||
|
|
||||||
|
enemy_cp.base.affect_strength(-0.1)
|
||||||
|
|
||||||
|
|
||||||
|
def execute_theater(theater_klass):
|
||||||
|
print("Theater: {}".format(theater_klass))
|
||||||
|
game, theater = init(PLAYER_COUNTRY, ENEMY_COUNTRY, theater_klass)
|
||||||
|
|
||||||
|
total_events = 0
|
||||||
|
while len(theater.enemy_points()) > 0:
|
||||||
|
for player_cp, enemy_cp in theater.conflicts():
|
||||||
|
execute(game, player_cp, enemy_cp)
|
||||||
|
|
||||||
|
enemy_cp.captured = True
|
||||||
|
|
||||||
|
print("Total: {}".format(total_events))
|
||||||
|
|
||||||
|
|
||||||
|
def execute_all():
|
||||||
|
for theater_klass in [CaucasusTheater, PersianGulfTheater, NevadaTheater]:
|
||||||
|
execute_theater(theater_klass)
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
execute_all()
|
||||||
52
tests/integration/frontlineattack.py
Normal file
@@ -0,0 +1,52 @@
|
|||||||
|
from theater.caucasus import CaucasusTheater
|
||||||
|
from theater.nevada import NevadaTheater
|
||||||
|
|
||||||
|
from game.event.frontlineattack import FrontlineAttackEvent
|
||||||
|
|
||||||
|
from tests.integration.util import *
|
||||||
|
|
||||||
|
PLAYER_COUNTRY = "USA"
|
||||||
|
ENEMY_COUNTRY = "Russia"
|
||||||
|
|
||||||
|
|
||||||
|
def execute(game, player_cp, enemy_cp, departure_cp = None):
|
||||||
|
e = FrontlineAttackEvent(game, player_cp, enemy_cp, enemy_cp.position, PLAYER_COUNTRY, ENEMY_COUNTRY)
|
||||||
|
|
||||||
|
departures = [departure_cp] if departure_cp else game.theater.player_points()
|
||||||
|
for departure_cp in departures:
|
||||||
|
if e.is_departure_available_from(departure_cp):
|
||||||
|
enemy_cp.base.strength = 1
|
||||||
|
for _ in range(10):
|
||||||
|
print("{} for {} ({}) - {} ({})".format(e, player_cp, departure_cp, enemy_cp, enemy_cp.base.strength))
|
||||||
|
e.departure_cp = departure_cp
|
||||||
|
e.player_attacking(autoflights_for(e, PLAYER_COUNTRY))
|
||||||
|
|
||||||
|
e.generate()
|
||||||
|
execute_autocommit(e)
|
||||||
|
e.generate_quick()
|
||||||
|
execute_autocommit(e)
|
||||||
|
|
||||||
|
enemy_cp.base.affect_strength(-0.1)
|
||||||
|
|
||||||
|
|
||||||
|
def execute_theater(theater_klass):
|
||||||
|
print("Theater: {}".format(theater_klass))
|
||||||
|
game, theater = init(PLAYER_COUNTRY, ENEMY_COUNTRY, theater_klass)
|
||||||
|
|
||||||
|
total_events = 0
|
||||||
|
while len(theater.enemy_points()) > 0:
|
||||||
|
for player_cp, enemy_cp in theater.conflicts():
|
||||||
|
execute(game, player_cp, enemy_cp)
|
||||||
|
|
||||||
|
enemy_cp.captured = True
|
||||||
|
|
||||||
|
print("Total: {}".format(total_events))
|
||||||
|
|
||||||
|
|
||||||
|
def execute_all():
|
||||||
|
for theater_klass in [CaucasusTheater, PersianGulfTheater, NevadaTheater]:
|
||||||
|
execute_theater(theater_klass)
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
execute_all()
|
||||||
48
tests/integration/insurgentattack.py
Normal file
@@ -0,0 +1,48 @@
|
|||||||
|
from theater.caucasus import CaucasusTheater
|
||||||
|
from theater.nevada import NevadaTheater
|
||||||
|
|
||||||
|
from game.event.insurgentattack import InsurgentAttackEvent
|
||||||
|
|
||||||
|
from tests.integration.util import *
|
||||||
|
|
||||||
|
PLAYER_COUNTRY = "USA"
|
||||||
|
ENEMY_COUNTRY = "Russia"
|
||||||
|
|
||||||
|
|
||||||
|
def execute(game, player_cp, enemy_cp, departure_cp = None):
|
||||||
|
e = InsurgentAttackEvent(game, enemy_cp, player_cp, player_cp.position, ENEMY_COUNTRY, PLAYER_COUNTRY)
|
||||||
|
|
||||||
|
departures = [departure_cp] if departure_cp else game.theater.player_points()
|
||||||
|
for departure_cp in departures:
|
||||||
|
if e.is_departure_available_from(departure_cp):
|
||||||
|
print("{} for {} ({}) - {} ({})".format(e, player_cp, departure_cp, enemy_cp, enemy_cp.base.strength))
|
||||||
|
e.departure_cp = departure_cp
|
||||||
|
e.player_defending(autoflights_for(e, PLAYER_COUNTRY))
|
||||||
|
|
||||||
|
e.generate()
|
||||||
|
execute_autocommit(e)
|
||||||
|
e.generate_quick()
|
||||||
|
execute_autocommit(e)
|
||||||
|
|
||||||
|
|
||||||
|
def execute_theater(theater_klass):
|
||||||
|
print("Theater: {}".format(theater_klass))
|
||||||
|
game, theater = init(PLAYER_COUNTRY, ENEMY_COUNTRY, theater_klass)
|
||||||
|
|
||||||
|
total_events = 0
|
||||||
|
while len(theater.enemy_points()) > 0:
|
||||||
|
for player_cp, enemy_cp in theater.conflicts():
|
||||||
|
execute(game, player_cp, enemy_cp)
|
||||||
|
|
||||||
|
enemy_cp.captured = True
|
||||||
|
|
||||||
|
print("Total: {}".format(total_events))
|
||||||
|
|
||||||
|
|
||||||
|
def execute_all():
|
||||||
|
for theater_klass in [CaucasusTheater, PersianGulfTheater, NevadaTheater]:
|
||||||
|
execute_theater(theater_klass)
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
execute_all()
|
||||||
48
tests/integration/intercept.py
Normal file
@@ -0,0 +1,48 @@
|
|||||||
|
from theater.caucasus import CaucasusTheater
|
||||||
|
from theater.nevada import NevadaTheater
|
||||||
|
|
||||||
|
from game.event.intercept import InterceptEvent
|
||||||
|
|
||||||
|
from tests.integration.util import *
|
||||||
|
|
||||||
|
PLAYER_COUNTRY = "USA"
|
||||||
|
ENEMY_COUNTRY = "Russia"
|
||||||
|
|
||||||
|
|
||||||
|
def execute(game, player_cp, enemy_cp, departure_cp = None):
|
||||||
|
e = InterceptEvent(game, player_cp, enemy_cp, enemy_cp.position, PLAYER_COUNTRY, ENEMY_COUNTRY)
|
||||||
|
|
||||||
|
departures = [departure_cp] if departure_cp else game.theater.player_points()
|
||||||
|
for departure_cp in departures:
|
||||||
|
if e.is_departure_available_from(departure_cp):
|
||||||
|
print("{} for {} ({}) - {} ({})".format(e, player_cp, departure_cp, enemy_cp, enemy_cp.base.strength))
|
||||||
|
e.departure_cp = departure_cp
|
||||||
|
e.player_attacking(autoflights_for(e, PLAYER_COUNTRY))
|
||||||
|
|
||||||
|
e.generate()
|
||||||
|
execute_autocommit(e)
|
||||||
|
e.generate_quick()
|
||||||
|
execute_autocommit(e)
|
||||||
|
|
||||||
|
|
||||||
|
def execute_theater(theater_klass):
|
||||||
|
print("Theater: {}".format(theater_klass))
|
||||||
|
game, theater = init(PLAYER_COUNTRY, ENEMY_COUNTRY, theater_klass)
|
||||||
|
|
||||||
|
total_events = 0
|
||||||
|
while len(theater.enemy_points()) > 0:
|
||||||
|
for player_cp, enemy_cp in theater.conflicts():
|
||||||
|
execute(game, player_cp, enemy_cp)
|
||||||
|
|
||||||
|
enemy_cp.captured = True
|
||||||
|
|
||||||
|
print("Total: {}".format(total_events))
|
||||||
|
|
||||||
|
|
||||||
|
def execute_all():
|
||||||
|
for theater_klass in [CaucasusTheater, PersianGulfTheater, NevadaTheater]:
|
||||||
|
execute_theater(theater_klass)
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
execute_all()
|
||||||
49
tests/integration/navalintercept.py
Normal file
@@ -0,0 +1,49 @@
|
|||||||
|
from theater.caucasus import CaucasusTheater
|
||||||
|
from theater.nevada import NevadaTheater
|
||||||
|
|
||||||
|
from game.event.intercept import InterceptEvent
|
||||||
|
|
||||||
|
from tests.integration.util import *
|
||||||
|
|
||||||
|
PLAYER_COUNTRY = "USA"
|
||||||
|
ENEMY_COUNTRY = "Russia"
|
||||||
|
|
||||||
|
|
||||||
|
def execute(game, player_cp, enemy_cp, departure_cp = None):
|
||||||
|
e = NavalInterceptEvent(game, player_cp, enemy_cp, enemy_cp.position, PLAYER_COUNTRY, ENEMY_COUNTRY)
|
||||||
|
|
||||||
|
departures = [departure_cp] if departure_cp else game.theater.player_points()
|
||||||
|
for departure_cp in departures:
|
||||||
|
if e.is_departure_available_from(departure_cp):
|
||||||
|
print("{} for {} ({}) - {} ({})".format(e, player_cp, departure_cp, enemy_cp, enemy_cp.base.strength))
|
||||||
|
e.departure_cp = departure_cp
|
||||||
|
e.player_attacking(autoflights_for(e, PLAYER_COUNTRY))
|
||||||
|
|
||||||
|
e.generate()
|
||||||
|
execute_autocommit(e)
|
||||||
|
e.generate_quick()
|
||||||
|
execute_autocommit(e)
|
||||||
|
|
||||||
|
|
||||||
|
def execute_theater(theater_klass):
|
||||||
|
print("Theater: {}".format(theater_klass))
|
||||||
|
game, theater = init(PLAYER_COUNTRY, ENEMY_COUNTRY, theater_klass)
|
||||||
|
|
||||||
|
total_events = 0
|
||||||
|
while len(theater.enemy_points()) > 0:
|
||||||
|
for player_cp, enemy_cp in theater.conflicts():
|
||||||
|
if enemy_cp.radials != LAND:
|
||||||
|
execute(game, player_cp, enemy_cp)
|
||||||
|
|
||||||
|
enemy_cp.captured = True
|
||||||
|
|
||||||
|
print("Total: {}".format(total_events))
|
||||||
|
|
||||||
|
|
||||||
|
def execute_all():
|
||||||
|
for theater_klass in [CaucasusTheater, PersianGulfTheater, NevadaTheater]:
|
||||||
|
execute_theater(theater_klass)
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
execute_all()
|
||||||