Compare commits

...

18 Commits
1.4_rc4 ... 1.4

Author SHA1 Message Date
Vasyl Horbachenko
e9d7ee51f3 readme upd 2018-10-22 02:29:42 +03:00
Vasyl Horbachenko
9053408e13 Merge branch 'develop' 2018-10-22 02:18:26 +03:00
Vasyl Horbachenko
8f4094ee98 number of fixes 2018-10-22 02:13:38 +03:00
Vasyl Horbachenko
933e064079 fixed debriefing crash 2018-10-14 04:30:46 +03:00
Vasyl Horbachenko
274e08dd8b quick mission debriefing fixed; adjusted constants 2018-10-14 04:09:05 +03:00
Vasyl Horbachenko
05c968edc2 added Gazelle as CAS aircraft + minor fixes 2018-10-13 23:38:26 +03:00
Vasyl Horbachenko
270820de0b added Gazelle as CAS aircraft 2018-10-13 22:50:26 +03:00
Vasyl Horbachenko
e049a97bec minor text update 2018-10-13 22:30:43 +03:00
Vasyl Horbachenko
e2306ba0f3 new weather, strike objectives placement fixes & tarawa for av8b 2018-10-13 22:28:30 +03:00
Vasyl Horbachenko
6d0f488672 updated version compatibility check 2018-10-13 04:44:59 +03:00
Vasyl Horbachenko
397f9a58cb fixed naval intercept crash; fixed wrong targets order; fixed initial waypoint being WP #1; m2k a2g ccip; fixed time being time zone offset ahead; lowered rain weather chance 2018-10-13 04:41:18 +03:00
Vasyl Horbachenko
4fc766a524 trigger fixes; strike waypoint fixes; m2k strike payload update 2018-10-13 02:36:25 +03:00
Vasyl Horbachenko
8df4607e50 Update README.md 2018-09-14 23:49:50 +03:00
Vasyl Horbachenko
49f2c00d76 Update README.md 2018-09-09 04:39:53 +03:00
Vasyl Horbachenko
d284305323 Update README.md 2018-09-09 04:39:03 +03:00
Vasyl Horbachenko
c32ac8577c Update README.md 2018-09-09 04:38:47 +03:00
Vasyl Horbachenko
0d5530f5ea Update README.md 2018-08-15 15:14:19 +03:00
Vasyl Horbachenko
a528249062 Update README.md 2018-08-15 15:13:49 +03:00
37 changed files with 286 additions and 170 deletions

View File

@@ -1,8 +1,29 @@
[DCS World](https://www.digitalcombatsimulator.com/en/products/world/) single-player liberation dynamic campaign. ![Logo](https://i.imgur.com/c2k18E1.png)
[Installation instructions/Manual](https://github.com/shdwp/dcs_liberation/wiki) [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.
## Installation
1. Download and install **Python 3.6.0** package from https://www.python.org/downloads/release/python-360/ (look at the bottom under *Files*; any option will do if it matches your architecture) with default set of options (you need to have *Install launcher for all users (recommended)* checked)
1. Download archived release (https://github.com/shdwp/dcs_liberation/releases; **not source code zip**, file should be named **dcs_liberation_xx.zip**)
1. Unzip the archive somewhere. Path does not matter. **Application will not work** if you start it without extracting
1. Run **start.bat**
1. If **"Windows protected your PC"** popup appears on your computer (windows blocks any application unknown to it), you can click on **"More info"** and **"Run anyway"**
## 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.
* [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.

View File

@@ -43,7 +43,7 @@ def is_version_compatible(save_version):
if current_version_components == save_version_components: if current_version_components == save_version_components:
return True return True
if save_version in ["1.4_rc1", "1.4_rc2", "1.4_rc3"]: if save_version in ["1.4_rc1", "1.4_rc2", "1.4_rc3", "1.4_rc4", "1.4_rc5", "1.4_rc6"]:
return False return False
if current_version_components[:2] == save_version_components[:2]: if current_version_components[:2] == save_version_components[:2]:

View File

@@ -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
@@ -39,10 +41,10 @@ PRICES = {
# fighter # fighter
C_101CC: 8, C_101CC: 8,
MiG_23MLD: 18, MiG_23MLD: 18,
Su_27: 24, Su_27: 20,
Su_33: 25, Su_33: 22,
MiG_29A: 24, MiG_29A: 23,
MiG_29S: 26, MiG_29S: 25,
F_5E_3: 6, F_5E_3: 6,
MiG_15bis: 5, MiG_15bis: 5,
@@ -52,7 +54,7 @@ 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,
# 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
@@ -76,7 +79,6 @@ PRICES = {
S_3B_Tanker: 13, S_3B_Tanker: 13,
IL_78M: 13, IL_78M: 13,
KC_135: 13, KC_135: 13,
KC130: 13,
A_50: 8, A_50: 8,
E_3A: 8, E_3A: 8,
@@ -110,9 +112,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,
} }
@@ -158,6 +161,7 @@ UNIT_BY_TASK = {
Su_25T, Su_25T,
Su_34, Su_34,
Ka_50, Ka_50,
SA342M,
], ],
Transport: [ Transport: [
@@ -166,14 +170,13 @@ 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,
KC130, S_3B_Tanker,
], ],
AWACS: [E_3A, A_50, ], AWACS: [E_3A, A_50, ],
@@ -198,8 +201,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, ],
} }
""" """
@@ -265,6 +268,7 @@ UNIT_BY_COUNTRY = {
A_50, A_50,
Ka_50, Ka_50,
SA342M,
UH_1H, UH_1H,
Mi_8MT, Mi_8MT,
@@ -299,12 +303,12 @@ UNIT_BY_COUNTRY = {
AV8BNA, AV8BNA,
KC_135, KC_135,
KC130,
S_3B_Tanker, S_3B_Tanker,
C_130, C_130,
E_3A, E_3A,
Ka_50, Ka_50,
SA342M,
UH_1H, UH_1H,
Mi_8MT, Mi_8MT,
@@ -319,9 +323,19 @@ 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,
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:
@@ -341,6 +355,8 @@ 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",
PinpointStrike: "MK-82*8,AIM-9*2,AIM-7,FLIR Pod,Fuel",
AntishipStrike: "MK-82*8,AIM-9*2,AIM-7,FLIR Pod,Fuel",
}, },
Su_25T: { Su_25T: {
@@ -371,6 +387,7 @@ PLANE_PAYLOAD_OVERRIDES = {
M_2000C: { M_2000C: {
CAP: "Combat Air Patrol", CAP: "Combat Air Patrol",
GroundAttack: "MK-82S Heavy Strike",
}, },
MiG_21Bis: { MiG_21Bis: {
@@ -434,6 +451,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"
@@ -459,6 +485,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():

View File

@@ -79,7 +79,7 @@ class Event:
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):
@@ -88,7 +88,7 @@ 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():

View File

@@ -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())

View File

@@ -11,7 +11,7 @@ class NavalInterceptEvent(Event):
def _targets_count(self) -> int: def _targets_count(self) -> int:
from gen.conflictgen import IMPORTANCE_LOW 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:

View File

@@ -58,14 +58,14 @@ EVENT_PROBABILITIES = {
InfantryTransportEvent: [25, 0], InfantryTransportEvent: [25, 0],
# events conditionally present; for both enemy and player # events conditionally present; for both enemy and player
BaseAttackEvent: [100, 5], BaseAttackEvent: [100, 9],
# events randomly present; for both enemy and player # events randomly present; for both enemy and player
InterceptEvent: [25, 5], InterceptEvent: [25, 9],
NavalInterceptEvent: [25, 5], NavalInterceptEvent: [25, 9],
# events randomly present; only for the enemy # events randomly present; only for the enemy
InsurgentAttackEvent: [0, 4], InsurgentAttackEvent: [0, 6],
} }
# amount of strength player bases recover for the turn # amount of strength player bases recover for the turn
@@ -80,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

View File

@@ -37,13 +37,13 @@ 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):

View File

@@ -26,14 +26,14 @@ 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):
@@ -44,7 +44,7 @@ class FrontlineAttackOperation(Operation):
heli_flights = {k: v for k, v in self.strikegroup.items() if k in helicopters.helicopter_map.values()} heli_flights = {k: v for k, v in self.strikegroup.items() if k in helicopters.helicopter_map.values()}
if heli_flights: if heli_flights:
self.briefinggen.append_frequency("FARP", "127.5 MHz AM") 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()])), 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)): db.assignedunits_split_to_count(heli_flights, self.groundobjectgen.FARP_CAPACITY)):
self.airgen.generate_cas_strikegroup(*assigned_units_split(dict), self.airgen.generate_cas_strikegroup(*assigned_units_split(dict),

View File

@@ -32,14 +32,14 @@ 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):

View File

@@ -15,14 +15,14 @@ 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):

View File

@@ -17,14 +17,14 @@ 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):

View File

@@ -28,27 +28,18 @@ 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),
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: self.prepare_carriers(db.unitdict_from(self.interceptors))
if not global_cp.is_global:
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(*assigned_units_split(self.escort), at=self.defenders_starting_position) self.airgen.generate_defenders_escort(*assigned_units_split(self.escort), at=self.defenders_starting_position)

View File

@@ -23,16 +23,18 @@ 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),
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):
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(
@@ -50,7 +52,7 @@ class NavalInterceptionOperation(Operation):
self.briefinggen.title = "Naval Intercept" self.briefinggen.title = "Naval Intercept"
if self.game.player == self.attacker_name: 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." 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: for unit_type, count in self.targets.items():
self.briefinggen.append_target("{} ({})".format(db.unit_type_name(unit_type), count)) self.briefinggen.append_target("{} ({})".format(db.unit_type_name(unit_type), count))
else: else:
self.briefinggen.description = "Protect supply transport ships." self.briefinggen.description = "Protect supply transport ships."

View File

@@ -11,7 +11,9 @@ 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
@@ -51,7 +53,7 @@ class Operation:
return True return True
def initialize(self, mission: Mission, conflict: Conflict): def initialize(self, mission: Mission, conflict: Conflict):
self.mission = mission self.current_mission = mission
self.conflict = conflict 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)
@@ -72,8 +74,13 @@ 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:
@@ -83,6 +90,18 @@ class Operation:
self.attackers_starting_position = self.from_cp.at self.attackers_starting_position = self.from_cp.at
self.defenders_starting_position = self.to_cp.at self.defenders_starting_position = self.to_cp.at
def prepare_carriers(self, for_units: db.UnitsDict):
for global_cp in self.game.theater.controlpoints:
if not global_cp.is_global:
continue
ship = self.shipgen.generate_carrier(for_units=[t for t, c in for_units.items() if c > 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
def generate(self): def generate(self):
self.visualgen.generate() self.visualgen.generate()
@@ -95,11 +114,11 @@ class Operation:
self.briefinggen.append_frequency("AWACS", "133 MHz AM") self.briefinggen.append_frequency("AWACS", "133 MHz AM")
# combined arms # combined arms
self.mission.groundControl.pilot_can_control_vehicles = self.ca_slots > 0 self.current_mission.groundControl.pilot_can_control_vehicles = self.ca_slots > 0
if self.game.player in [country.name for country in self.mission.coalition["blue"].countries.values()]: if self.game.player in [country.name for country in self.current_mission.coalition["blue"].countries.values()]:
self.mission.groundControl.blue_tactical_commander = self.ca_slots self.current_mission.groundControl.blue_tactical_commander = self.ca_slots
else: else:
self.mission.groundControl.red_tactical_commander = self.ca_slots self.current_mission.groundControl.red_tactical_commander = self.ca_slots
# ground infrastructure # ground infrastructure
self.groundobjectgen.generate() self.groundobjectgen.generate()

View File

@@ -8,6 +8,8 @@ class StrikeOperation(Operation):
escort = None # type: db.AssignedUnitsDict escort = None # type: db.AssignedUnitsDict
interceptors = None # type: db.AssignedUnitsDict interceptors = None # type: db.AssignedUnitsDict
trigger_radius = TRIGGER_RADIUS_ALL_MAP
def setup(self, def setup(self,
strikegroup: db.AssignedUnitsDict, strikegroup: db.AssignedUnitsDict,
escort: db.AssignedUnitsDict, escort: db.AssignedUnitsDict,
@@ -24,29 +26,20 @@ 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):
for global_cp in self.game.theater.controlpoints: self.prepare_carriers(db.unitdict_merge(db.unitdict_from(self.strikegroup), db.unitdict_from(self.escort)))
if not global_cp.is_global:
continue
ship = self.shipgen.generate_carrier(type=db.find_unittype(Carriage, self.game.player)[0], targets = [] # type: typing.List[typing.Tuple[str, str, Point]]
country=self.game.player,
at=global_cp.at)
if global_cp == self.from_cp and not self.is_quick:
self.attackers_starting_position = ship
targets = [] # type: typing.List[typing.Tuple[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:
@@ -54,18 +47,18 @@ class StrikeOperation(Operation):
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)) targets.append((str(object), markpoint_name, object.position))
self.briefinggen.append_target(str(object))
self.briefinggen.append_waypoint("TARGET {} (TP {})".format(str(object), markpoint_name))
targets.sort(key=lambda x: self.from_cp.position.distance_to_point(x[1])) targets.sort(key=lambda x: self.from_cp.position.distance_to_point(x[2]))
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()} 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), self.airgen.generate_ground_attack_strikegroup(*assigned_units_split(planes_flights),
targets=targets, targets=[(mp, pos) for (n, mp, pos) in targets],
at=self.attackers_starting_position) at=self.attackers_starting_position)
heli_flights = {k: v for k, v in self.strikegroup.items() if k in helicopters.helicopter_map.values()} heli_flights = {k: v for k, v in self.strikegroup.items() if k in helicopters.helicopter_map.values()}
@@ -74,7 +67,7 @@ class StrikeOperation(Operation):
for farp, dict in zip(self.groundobjectgen.generate_farps(sum([x[0] for x in heli_flights.values()])), 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)): db.assignedunits_split_to_count(heli_flights, self.groundobjectgen.FARP_CAPACITY)):
self.airgen.generate_ground_attack_strikegroup(*assigned_units_split(dict), self.airgen.generate_ground_attack_strikegroup(*assigned_units_split(dict),
targets=targets, targets=[(mp, pos) for (n, mp, pos) in targets],
at=farp, at=farp,
escort=len(planes_flights) == 0) escort=len(planes_flights) == 0)

View File

@@ -5,6 +5,7 @@ class Settings:
enemy_vehicle_skill = "Average" enemy_vehicle_skill = "Average"
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

View File

@@ -112,7 +112,12 @@ class AircraftConflictGenerator:
group.units[idx].set_client() group.units[idx].set_client()
group.points[0].tasks.append(OptReactOnThreat(OptReactOnThreat.Values.EvadeFire)) group.points[0].tasks.append(OptReactOnThreat(OptReactOnThreat.Values.EvadeFire))
group.set_frequency(251.0)
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

View File

@@ -9,11 +9,11 @@ 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 TANKER_HEADING_OFFSET = 45
AWACS_DISTANCE = 150000 AWACS_DISTANCE = 150000
AWACS_ALT = 10000 AWACS_ALT = 13000
class AirSupportConflictGenerator: class AirSupportConflictGenerator:

View File

@@ -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 = 1500
class ArmorConflictGenerator: class ArmorConflictGenerator:
def __init__(self, mission: Mission, conflict: Conflict): def __init__(self, mission: Mission, conflict: Conflict):
@@ -56,8 +58,8 @@ class ArmorConflictGenerator:
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,
@@ -68,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,

View File

@@ -58,6 +58,6 @@ class BriefingGenerator:
if self.waypoints: if self.waypoints:
description += "\n\nWAYPOINTS:" description += "\n\nWAYPOINTS:"
for i, descr in enumerate(self.waypoints): for i, descr in enumerate(self.waypoints):
description += "\n#{}: {}".format(i+1, descr) description += "\n#{}: {}".format(i, descr)
self.m.set_description_text(description) self.m.set_description_text(description)

View File

@@ -166,7 +166,7 @@ class Conflict:
if ground_position: if ground_position:
return ground_position, _opposite_heading(attack_heading) return ground_position, _opposite_heading(attack_heading)
else: else:
print("Coudn't find frontline position between {} and {}!".format(from_cp, to_cp)) logging.warning("Coudn't find frontline position between {} and {}!".format(from_cp, to_cp))
return position, _opposite_heading(attack_heading) return position, _opposite_heading(attack_heading)

View File

@@ -21,6 +21,10 @@ 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": 5,
"dusk": 30, "dusk": 30,
@@ -29,11 +33,10 @@ RANDOM_TIME = {
} }
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()

View File

@@ -24,10 +24,13 @@ class GroundObjectsGenerator:
center = self.conflict.center center = self.conflict.center
heading = self.conflict.heading - 90 heading = self.conflict.heading - 90
else: else:
center, heading = self.conflict.frontline_position(self.conflict.from_cp, self.conflict.to_cp) center, heading = self.conflict.frontline_position(self.conflict.theater, self.conflict.from_cp, self.conflict.to_cp)
heading -= 90 heading -= 90
position = self.conflict.find_ground_position(center.point_from_heading(heading, FARP_FRONTLINE_DISTANCE), heading) position = self.conflict.find_ground_position(center.point_from_heading(heading, FARP_FRONTLINE_DISTANCE), heading)
if not position:
return
for i, _ in enumerate(range(0, number_of_units, self.FARP_CAPACITY)): for i, _ in enumerate(range(0, number_of_units, self.FARP_CAPACITY)):
position = position.point_from_heading(0, i * 275) position = position.point_from_heading(0, i * 275)

View File

@@ -1,5 +0,0 @@
from .aircraft import *
class HelicopterConflictGenerator(AircraftConflictGenerator):
pass

View File

@@ -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)),

View File

@@ -16,7 +16,7 @@ from gen.airsupportgen import AirSupportConflictGenerator
from gen import * from gen import *
PUSH_TRIGGER_SIZE = 3000 PUSH_TRIGGER_SIZE = 3000
PUSH_TRIGGER_ACTIVATION_AGL = 100 PUSH_TRIGGER_ACTIVATION_AGL = 25
REGROUP_ZONE_DISTANCE = 12000 REGROUP_ZONE_DISTANCE = 12000
REGROUP_ALT = 5000 REGROUP_ALT = 5000
@@ -25,9 +25,10 @@ TRIGGER_WAYPOINT_OFFSET = 2
TRIGGER_MIN_DISTANCE_FROM_START = 10000 TRIGGER_MIN_DISTANCE_FROM_START = 10000
TRIGGER_RADIUS_MINIMUM = 20000 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):
@@ -53,14 +54,16 @@ class TriggersGenerator:
vehicle_group.late_activation = True vehicle_group.late_activation = True
activate_by_trigger.append(vehicle_group) activate_by_trigger.append(vehicle_group)
"""
conflict_distance = player_cp.position.distance_to_point(self.conflict.position) conflict_distance = player_cp.position.distance_to_point(self.conflict.position)
minimum_radius = max(conflict_distance - TRIGGER_MIN_DISTANCE_FROM_START, TRIGGER_RADIUS_MINIMUM) minimum_radius = max(conflict_distance - TRIGGER_MIN_DISTANCE_FROM_START, TRIGGER_RADIUS_MINIMUM)
if minimum_radius < 0: if minimum_radius < 0:
minimum_radius = 0 minimum_radius = 0
result_radius = min(minimum_radius, radius) result_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())
@@ -112,10 +115,10 @@ class TriggersGenerator:
for unit in group.units: for unit in group.units:
push_trigger.add_condition(UnitAltitudeHigherAGL(unit.id, PUSH_TRIGGER_ACTIVATION_AGL)) push_trigger.add_condition(UnitAltitudeHigherAGL(unit.id, PUSH_TRIGGER_ACTIVATION_AGL))
if group.units[0].is_human(): if not group.units[0].is_human():
push_trigger.add_action(AITaskPush(group.id, 1)) push_trigger.add_action(AITaskPush(group.id, 1))
message_string = self.mission.string("Task force is in the air, proceed with the objective (activate waypoint 3).") 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())

Binary file not shown.

View File

@@ -10,9 +10,9 @@ from dcs.task import *
from game import db from game import db
STRENGTH_AA_ASSEMBLE_MIN = 0.2 STRENGTH_AA_ASSEMBLE_MIN = 0.2
PLANES_SCRAMBLE_MIN_BASE = 4 PLANES_SCRAMBLE_MIN_BASE = 2
PLANES_SCRAMBLE_MAX_BASE = 8 PLANES_SCRAMBLE_MAX_BASE = 8
PLANES_SCRAMBLE_FACTOR = 0.6 PLANES_SCRAMBLE_FACTOR = 0.3
BASE_MAX_STRENGTH = 1 BASE_MAX_STRENGTH = 1
BASE_MIN_STRENGTH = 0 BASE_MIN_STRENGTH = 0

View File

@@ -33,7 +33,6 @@ class CaucasusTheater(ConflictTheater):
gelendzhik = ControlPoint.from_airport(caucasus.Gelendzhik, COAST_DR_E, SIZE_BIG, 1.1) gelendzhik = ControlPoint.from_airport(caucasus.Gelendzhik, COAST_DR_E, SIZE_BIG, 1.1)
maykop = ControlPoint.from_airport(caucasus.Maykop_Khanskaya, LAND, SIZE_LARGE, IMPORTANCE_HIGH) maykop = ControlPoint.from_airport(caucasus.Maykop_Khanskaya, LAND, SIZE_LARGE, IMPORTANCE_HIGH)
krasnodar = ControlPoint.from_airport(caucasus.Krasnodar_Center, LAND, SIZE_LARGE, IMPORTANCE_HIGH) krasnodar = ControlPoint.from_airport(caucasus.Krasnodar_Center, LAND, SIZE_LARGE, IMPORTANCE_HIGH)
novorossiysk = ControlPoint.from_airport(caucasus.Novorossiysk, COAST_DR_E, SIZE_BIG, 1.2)
krymsk = ControlPoint.from_airport(caucasus.Krymsk, LAND, SIZE_LARGE, 1.2) krymsk = ControlPoint.from_airport(caucasus.Krymsk, LAND, SIZE_LARGE, 1.2)
anapa = ControlPoint.from_airport(caucasus.Anapa_Vityazevo, LAND, SIZE_LARGE, IMPORTANCE_HIGH) anapa = ControlPoint.from_airport(caucasus.Anapa_Vityazevo, LAND, SIZE_LARGE, IMPORTANCE_HIGH)

View File

@@ -72,8 +72,8 @@ class PersianGulfTheater(ConflictTheater):
self.add_controlpoint(self.havadarya, connected_to=[self.lar, self.qeshm, self.bandar_abbas]) self.add_controlpoint(self.havadarya, connected_to=[self.lar, self.qeshm, self.bandar_abbas])
self.add_controlpoint(self.bandar_abbas, connected_to=[self.havadarya]) self.add_controlpoint(self.bandar_abbas, connected_to=[self.havadarya])
self.add_controlpoint(self.east_carrier)
self.add_controlpoint(self.west_carrier) self.add_controlpoint(self.west_carrier)
self.add_controlpoint(self.east_carrier)
self.west_carrier.captured = True self.west_carrier.captured = True
self.east_carrier.captured = True self.east_carrier.captured = True

View File

@@ -2,6 +2,7 @@ import math
import pickle import pickle
import random import random
import typing import typing
import logging
from theater.base import * from theater.base import *
from theater.conflicttheater import * from theater.conflicttheater import *
@@ -72,8 +73,8 @@ def generate_groundobjects(theater: ConflictTheater):
group_id = 0 group_id = 0
for cp in theater.enemy_points(): for cp in theater.enemy_points():
for _ in range(0, random.randrange(3, 6)): for _ in range(0, random.randrange(2, 4)):
available_categories = list(tpls) + ["aa", "aa", "aa"] available_categories = list(tpls) + ["aa", "aa"]
tpl_category = random.choice(available_categories) tpl_category = random.choice(available_categories)
tpl = random.choice(list(tpls[tpl_category].values())) tpl = random.choice(list(tpls[tpl_category].values()))
@@ -84,14 +85,17 @@ def generate_groundobjects(theater: ConflictTheater):
print("Couldn't find point for {}".format(cp)) print("Couldn't find point for {}".format(cp))
continue continue
dist = point.distance_to_point(cp.position) """
dist = point.distance_to_point(cp.position) - 15000
for another_cp in theater.enemy_points(): for another_cp in theater.enemy_points():
if another_cp.position.distance_to_point(point) < dist: if another_cp.position.distance_to_point(point) < dist:
cp = another_cp cp = another_cp
"""
group_id += 1 group_id += 1
object_id = 0 object_id = 0
logging.info("generated {} for {}".format(tpl_category, cp))
for object in tpl: for object in tpl:
object_id += 1 object_id += 1

View File

@@ -194,6 +194,12 @@ class EventMenu(Menu):
self.error_label["text"] = "Need at least one player in flight {}".format(self.event.flight_name(task)) self.error_label["text"] = "Need at least one player in flight {}".format(self.event.flight_name(task))
return return
if isinstance(self.event, FrontlineAttackEvent) or isinstance(self.event, FrontlinePatrolEvent):
if tasks_scramble_counts.get(PinpointStrike, 0) == 0:
self.error_label["text"] = "No ground vehicles assigned to attack!"
return
if self.game.is_player_attack(self.event): if self.game.is_player_attack(self.event):
self.event.player_attacking(flights) self.event.player_attacking(flights)
else: else:

View File

@@ -54,6 +54,7 @@ class EventResultsMenu(Menu):
pg.start(10) pg.start(10)
row += 1 row += 1
"""
Label(self.frame, text="Cheat operation results: ", **STYLES["strong"]).grid(column=0, row=row, Label(self.frame, text="Cheat operation results: ", **STYLES["strong"]).grid(column=0, row=row,
columnspan=2, sticky=NSEW, columnspan=2, sticky=NSEW,
pady=5) pady=5)
@@ -69,6 +70,7 @@ class EventResultsMenu(Menu):
Button(self.frame, text="some player losses", command=self.simulate_result(0.8, 0), Button(self.frame, text="some player losses", command=self.simulate_result(0.8, 0),
**STYLES["btn-warning"]).grid(column=1, row=row, padx=5, pady=5) **STYLES["btn-warning"]).grid(column=1, row=row, padx=5, pady=5)
row += 1 row += 1
"""
else: else:
row = 0 row = 0
@@ -99,12 +101,14 @@ class EventResultsMenu(Menu):
Label(self.frame, text="{}".format(count), **STYLES["widget"]).grid(column=1, row=row) Label(self.frame, text="{}".format(count), **STYLES["widget"]).grid(column=1, row=row)
row += 1 row += 1
Button(self.frame, text="Okay", command=self.dismiss, **STYLES["btn-primary"]).grid(columnspan=1, row=row); Button(self.frame, text="Okay", command=self.dismiss, **STYLES["btn-primary"]).grid(columnspan=1, row=row)
row += 1 row += 1
def process_debriefing(self, debriefing: Debriefing): def process_debriefing(self, debriefing: Debriefing):
self.debriefing = debriefing self.debriefing = debriefing
debriefing.calculate_units(mission=self.event.operation.mission,
debriefing.calculate_units(regular_mission=self.event.operation.regular_mission,
quick_mission=self.event.operation.quick_mission,
player_name=self.game.player, player_name=self.game.player,
enemy_name=self.game.enemy) enemy_name=self.game.enemy)

View File

@@ -98,9 +98,6 @@ class NewGameMenu(Menu):
Label(terrain, text="Persian Gulf", **STYLES["widget"]).grid(row=2, column=1, sticky=W) Label(terrain, text="Persian Gulf", **STYLES["widget"]).grid(row=2, column=1, sticky=W)
self.create_label_image(terrain, "terrain_pg.gif").grid(row=2, column=2, padx=5) self.create_label_image(terrain, "terrain_pg.gif").grid(row=2, column=2, padx=5)
Label(terrain, text="Currently strike missions are only\navailable for a number of airports only in Caucasus", **STYLES["widget"]) \
.grid(row=3, column=0, columnspan=3, sticky=W)
# Misc Options # Misc Options
options = LabelFrame(body, text="Misc Options", **STYLES["label-frame"]) options = LabelFrame(body, text="Misc Options", **STYLES["label-frame"])
options.grid(row=0, column=2, sticky=NE, padx=5) options.grid(row=0, column=2, sticky=NE, padx=5)

View File

@@ -90,7 +90,6 @@ class OverviewCanvas:
if cp.captured and not connected_cp.captured and Conflict.has_frontline_between(cp, connected_cp): if cp.captured and not connected_cp.captured and Conflict.has_frontline_between(cp, connected_cp):
frontline = Conflict.frontline_vector(cp, connected_cp, self.game.theater) frontline = Conflict.frontline_vector(cp, connected_cp, self.game.theater)
if not frontline: if not frontline:
print(cp, connected_cp)
continue continue
frontline_pos, heading, distance = frontline frontline_pos, heading, distance = frontline

View File

@@ -60,11 +60,12 @@ def parse_mutliplayer_debriefing(contents: str):
class Debriefing: class Debriefing:
def __init__(self, dead_units): def __init__(self, dead_units, trigger_state):
self.destroyed_units = {} # type: typing.Dict[str, typing.Dict[UnitType, int]] self.destroyed_units = {} # type: typing.Dict[str, typing.Dict[UnitType, int]]
self.alive_units = {} # type: typing.Dict[str, typing.Dict[UnitType, int]] self.alive_units = {} # type: typing.Dict[str, typing.Dict[UnitType, int]]
self.destroyed_objects = [] # type: typing.List[str] self.destroyed_objects = [] # type: typing.List[str]
self._trigger_state = trigger_state
self._dead_units = dead_units self._dead_units = dead_units
@classmethod @classmethod
@@ -99,29 +100,16 @@ class Debriefing:
if event_type in ["crash", "dead"]: if event_type in ["crash", "dead"]:
parse_dead_object(event) parse_dead_object(event)
""" trigger_state = table.get("debriefing", {}).get("triggers_state", {})
initiator_components = event["initiator"].split("|")
if initiator_components[0] in CATEGORY_MAP: return Debriefing(dead_units, trigger_state)
parse_dead_object(event)
else:
parse_dead_unit(event)
"""
return Debriefing(dead_units) def calculate_units(self, regular_mission: Mission, quick_mission: Mission, player_name: str, enemy_name: str):
def calculate_units(self, mission: Mission, player_name: str, enemy_name: str):
def count_groups(groups: typing.List[UnitType]) -> typing.Dict[UnitType, int]: def count_groups(groups: typing.List[UnitType]) -> typing.Dict[UnitType, int]:
result = {} result = {}
for group in groups: for group in groups:
for unit in group.units: for unit in group.units:
if isinstance(unit, Vehicle): unit_type = db.unit_type_of(unit)
unit_type = vehicle_map[unit.type]
elif isinstance(unit, Ship):
unit_type = ship_map[unit.type]
else:
unit_type = unit.unit_type
if unit_type in db.EXTRA_AA.values(): if unit_type in db.EXTRA_AA.values():
continue continue
@@ -129,6 +117,8 @@ class Debriefing:
return result return result
mission = regular_mission if len(self._trigger_state) else quick_mission
player = mission.country(player_name) player = mission.country(player_name)
enemy = mission.country(enemy_name) enemy = mission.country(enemy_name)
@@ -151,19 +141,24 @@ class Debriefing:
for group in country_groups: for group in country_groups:
for unit in group.units: for unit in group.units:
if unit.id in self._dead_units: if unit.id in self._dead_units:
logging.info("debriefing: found dead unit {} ({})".format(str(unit.name), unit.id)) unit_type = db.unit_type_of(unit)
unit_klass = db.unit_type_from_name(unit.type) logging.info("debriefing: found dead unit {} ({}, {})".format(str(unit.name), unit.id, unit_type))
self.destroyed_units[country_name][unit_klass] = self.destroyed_units[country_name].get(unit_klass, 0) + 1
assert country_name
assert unit_type
self.destroyed_units[country_name][unit_type] = self.destroyed_units[country_name].get(unit_type, 0) + 1
self._dead_units.remove(unit.id) self._dead_units.remove(unit.id)
for group in static_groups: for group in static_groups:
identifier = group.units[0].id identifier = group.units[0].id
if identifier in self._dead_units: if identifier in self._dead_units:
logging.info("debriefing: found dead static {} ({})".format(str(group.name), identifier)) logging.info("debriefing: found dead static {} ({})".format(str(group.name), identifier))
assert str(group.name)
self.destroyed_objects.append(str(group.name)) self.destroyed_objects.append(str(group.name))
self._dead_units.remove(identifier) self._dead_units.remove(identifier)
print("debriefing: unsatistied ids: {}".format(self._dead_units)) logging.info("debriefing: unsatistied ids: {}".format(self._dead_units))
self.alive_units = { self.alive_units = {
player.name: {k: v - self.destroyed_units[player.name].get(k, 0) for k, v in player_units.items()}, player.name: {k: v - self.destroyed_units[player.name].get(k, 0) for k, v in player_units.items()},