Ground war rework

This commit is contained in:
Khopa 2020-05-24 20:32:45 +02:00
parent 5884d9d120
commit f57e453d8d
74 changed files with 1255 additions and 1965 deletions

View File

@ -168,6 +168,7 @@ PRICES = {
Armor.IFV_BMP_2: 16, Armor.IFV_BMP_2: 16,
Armor.IFV_BMP_3: 18, Armor.IFV_BMP_3: 18,
Armor.ZBD_04A: 12, Armor.ZBD_04A: 12,
Armor.ZTZ_96B: 35,
Armor.APC_Cobra: 4, Armor.APC_Cobra: 4,
Armor.APC_M113: 6, Armor.APC_M113: 6,
@ -187,6 +188,17 @@ PRICES = {
Armor.IFV_Marder: 10, Armor.IFV_Marder: 10,
Armor.IFV_MCV_80: 10, Armor.IFV_MCV_80: 10,
Artillery.MLRS_M270: 55,
Artillery.SPH_M109_Paladin: 25,
Artillery.SPH_2S9_Nona: 12,
Artillery.SPH_2S1_Gvozdika: 18,
Artillery.SPH_2S3_Akatsia: 24,
Artillery.SPH_2S19_Msta: 30,
Artillery.MLRS_BM_21_Grad: 15,
Artillery.MLRS_9K57_Uragan_BM_27: 40,
Artillery.MLRS_9A52_Smerch: 40,
Unarmed.Transport_UAZ_469: 3, Unarmed.Transport_UAZ_469: 3,
Unarmed.Transport_Ural_375: 3, Unarmed.Transport_Ural_375: 3,
Infantry.Infantry_M4: 1, Infantry.Infantry_M4: 1,
@ -220,11 +232,14 @@ PRICES = {
Armor.IFV_Sd_Kfz_234_2_Puma:4, Armor.IFV_Sd_Kfz_234_2_Puma:4,
Armor.MT_M4_Sherman:4, Armor.MT_M4_Sherman:4,
Armor.MT_M4A4_Sherman_Firefly:6, Armor.MT_M4A4_Sherman_Firefly:6,
Armor.CT_Cromwell_IV:8,
Armor.M30_Cargo_Carrier:2, Armor.M30_Cargo_Carrier:2,
Armor.APC_M2A1:2, Armor.APC_M2A1:2,
AirDefence.AAA_Bofors_40mm:4, AirDefence.AAA_Bofors_40mm:4,
AirDefence.AAA_Flak_36:6, AirDefence.AAA_Flak_36:6,
AirDefence.AAA_Flak_18:4, AirDefence.AAA_Flak_18:4,
Artillery.M12_GMC:2,
Artillery.Sturmpanzer_IV_Brummbär:2,
# ship # ship
CV_1143_5_Admiral_Kuznetsov: 100, CV_1143_5_Admiral_Kuznetsov: 100,
@ -341,8 +356,6 @@ UNIT_BY_TASK = {
Armor.ARV_BRDM_2, Armor.ARV_BRDM_2,
Armor.ARV_BRDM_2, Armor.ARV_BRDM_2,
Armor.ARV_BRDM_2, Armor.ARV_BRDM_2,
Armor.ARV_BRDM_2,
Armor.ARV_BRDM_2,
Armor.ARV_BTR_RD, Armor.ARV_BTR_RD,
Armor.ARV_BTR_RD, Armor.ARV_BTR_RD,
Armor.ARV_BTR_RD, Armor.ARV_BTR_RD,
@ -370,6 +383,7 @@ UNIT_BY_TASK = {
Armor.MBT_T_80U, Armor.MBT_T_80U,
Armor.MBT_T_80U, Armor.MBT_T_80U,
Armor.MBT_T_90, Armor.MBT_T_90,
Armor.ZTZ_96B,
Armor.APC_Cobra, Armor.APC_Cobra,
Armor.APC_Cobra, Armor.APC_Cobra,
@ -424,6 +438,7 @@ UNIT_BY_TASK = {
Armor.IFV_Sd_Kfz_234_2_Puma, Armor.IFV_Sd_Kfz_234_2_Puma,
Armor.MT_M4_Sherman, Armor.MT_M4_Sherman,
Armor.MT_M4A4_Sherman_Firefly, Armor.MT_M4A4_Sherman_Firefly,
Armor.CT_Cromwell_IV,
Armor.M30_Cargo_Carrier, Armor.M30_Cargo_Carrier,
Armor.M30_Cargo_Carrier, Armor.M30_Cargo_Carrier,
Armor.APC_M2A1, Armor.APC_M2A1,
@ -431,6 +446,19 @@ UNIT_BY_TASK = {
Armor.APC_M2A1, Armor.APC_M2A1,
Armor.APC_M2A1, Armor.APC_M2A1,
Artillery.MLRS_M270,
Artillery.SPH_M109_Paladin,
Artillery.SPH_2S9_Nona,
Artillery.SPH_2S1_Gvozdika,
Artillery.SPH_2S3_Akatsia,
Artillery.SPH_2S19_Msta,
Artillery.MLRS_BM_21_Grad,
Artillery.MLRS_BM_21_Grad,
Artillery.MLRS_9K57_Uragan_BM_27,
Artillery.MLRS_9A52_Smerch,
Artillery.M12_GMC,
Artillery.Sturmpanzer_IV_Brummbär,
], ],
AirDefence: [ AirDefence: [
@ -455,7 +483,7 @@ UNIT_BY_TASK = {
Nothing: [Infantry.Infantry_M4, Infantry.Soldier_AK, ], Nothing: [Infantry.Infantry_M4, Infantry.Soldier_AK, ],
Embarking: [], Embarking: [],
Carriage: [CVN_74_John_C__Stennis, LHA_1_Tarawa, 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, Armed_speedboat, ], CargoTransportation: [Dry_cargo_ship_Ivanov, Bulk_cargo_ship_Yakushev, Tanker_Elnya_160, Armed_speedboat, ]
} }
""" """
@ -801,6 +829,22 @@ def unit_task(unit: UnitType) -> Task:
def find_unittype(for_task: Task, country_name: str) -> typing.List[UnitType]: def find_unittype(for_task: Task, country_name: str) -> typing.List[UnitType]:
return [x for x in UNIT_BY_TASK[for_task] if x in FACTIONS[country_name]["units"]] return [x for x in UNIT_BY_TASK[for_task] if x in FACTIONS[country_name]["units"]]
def find_infantry(country_name: str) -> typing.List[UnitType]:
inf = [
Infantry.Paratrooper_AKS, Infantry.Paratrooper_AKS, Infantry.Paratrooper_AKS, Infantry.Paratrooper_AKS, Infantry.Paratrooper_AKS,
Infantry.Soldier_RPG,
Infantry.Infantry_M4, Infantry.Infantry_M4, Infantry.Infantry_M4, Infantry.Infantry_M4, Infantry.Infantry_M4,
Infantry.Soldier_M249,
Infantry.Soldier_AK, Infantry.Soldier_AK, Infantry.Soldier_AK, Infantry.Soldier_AK, Infantry.Soldier_AK,
Infantry.Paratrooper_RPG_16,
Infantry.Georgian_soldier_with_M4, Infantry.Georgian_soldier_with_M4, Infantry.Georgian_soldier_with_M4, Infantry.Georgian_soldier_with_M4,
Infantry.Infantry_Soldier_Rus, Infantry.Infantry_Soldier_Rus, Infantry.Infantry_Soldier_Rus, Infantry.Infantry_Soldier_Rus,
Infantry.Infantry_SMLE_No_4_Mk_1, Infantry.Infantry_SMLE_No_4_Mk_1, Infantry.Infantry_SMLE_No_4_Mk_1,
Infantry.Infantry_Mauser_98, Infantry.Infantry_Mauser_98, Infantry.Infantry_Mauser_98, Infantry.Infantry_Mauser_98,
Infantry.Infantry_M1_Garand, Infantry.Infantry_M1_Garand, Infantry.Infantry_M1_Garand,
Infantry.Infantry_Soldier_Insurgents, Infantry.Infantry_Soldier_Insurgents, Infantry.Infantry_Soldier_Insurgents
]
return [x for x in inf if x in FACTIONS[country_name]["units"]]
def unit_type_name(unit_type) -> str: def unit_type_name(unit_type) -> str:
return unit_type.id and unit_type.id or unit_type.name return unit_type.id and unit_type.id or unit_type.name
@ -825,7 +869,7 @@ def unit_type_of(unit: Unit) -> UnitType:
elif isinstance(unit, Ship): elif isinstance(unit, Ship):
return ship_map[unit.type] return ship_map[unit.type]
else: else:
return unit.unit_type return unit.type
def task_name(task) -> str: def task_name(task) -> str:

View File

@ -1,10 +1,2 @@
from .event import * from .event import *
from .frontlineattack import * from .frontlineattack import *
from .frontlinepatrol import *
from .intercept import *
from .baseattack import *
from .navalintercept import *
from .insurgentattack import *
from .convoystrike import *
from .infantrytransport import *
from .strike import *

View File

@ -1,106 +0,0 @@
from game.operation.baseattack import BaseAttackOperation
from .event import *
from game.db import assigned_units_from
class BaseAttackEvent(Event):
silent = True
BONUS_BASE = 15
STRENGTH_RECOVERY = 0.55
def __str__(self):
return "Base attack"
@property
def tasks(self):
return [CAP, CAS, PinpointStrike]
def flight_name(self, for_task: typing.Type[Task]) -> str:
if for_task == CAP:
return "Escort flight"
elif for_task == CAS:
return "CAS flight"
elif for_task == PinpointStrike:
return "Ground attack"
def is_successfull(self, debriefing: Debriefing):
if self.game.player_name == self.attacker_name:
attacker_country = self.game.player_country
defender_country = self.game.enemy_country
else:
attacker_country = self.game.enemy_country
defender_country = self.game.player_country
alive_attackers = sum([v for k, v in debriefing.alive_units.get(attacker_country, {}).items() if db.unit_task(k) == PinpointStrike])
alive_defenders = sum([v for k, v in debriefing.alive_units.get(defender_country, {}).items() if db.unit_task(k) == PinpointStrike])
attackers_success = alive_attackers >= alive_defenders
if self.departure_cp.captured:
return attackers_success
else:
return not attackers_success
def commit(self, debriefing: Debriefing):
super(BaseAttackEvent, self).commit(debriefing)
if self.is_successfull(debriefing):
if self.departure_cp.captured:
self.to_cp.captured = True
self.to_cp.ground_objects = []
self.to_cp.base.filter_units(db.FACTIONS[self.attacker_name]["units"])
self.to_cp.base.affect_strength(+self.STRENGTH_RECOVERY)
else:
if not self.departure_cp.captured:
self.to_cp.captured = False
self.to_cp.base.affect_strength(+self.STRENGTH_RECOVERY)
def skip(self):
if not self.is_player_attacking and self.to_cp.captured:
self.to_cp.captured = False
def player_defending(self, flights: db.TaskForceDict):
assert CAP in flights and len(flights) == 1, "Invalid scrambled flights"
cas = self.departure_cp.base.scramble_cas(self.game.settings.multiplier)
escort = self.departure_cp.base.scramble_sweep(self.game.settings.multiplier)
attackers = self.departure_cp.base.armor
op = BaseAttackOperation(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(cas=assigned_units_from(cas),
escort=assigned_units_from(escort),
intercept=flights[CAP],
attack=attackers,
defense=self.to_cp.base.armor,
aa=self.to_cp.base.aa)
self.operation = op
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"
op = BaseAttackOperation(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.scramble_last_defense()
#defenders.update(self.to_cp.base.scramble_cas(self.game.settings.multiplier))
op.setup(cas=flights[CAS],
escort=flights[CAP],
attack=unitdict_from(flights[PinpointStrike]),
intercept=assigned_units_from(defenders),
defense=self.to_cp.base.armor,
aa=self.to_cp.base.assemble_aa())
self.operation = op

View File

@ -1,89 +0,0 @@
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):
if self.game.player_name == self.attacker_name:
defender_country = self.game.enemy_country
else:
defender_country = self.game.player_country
killed_units = sum([v for k, v in debriefing.destroyed_units.get(defender_country, {}).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

View File

@ -1,86 +0,0 @@
from game.event import *
from game.operation.frontlinepatrol import FrontlinePatrolOperation
from userdata.debriefing import Debriefing
class FrontlinePatrolEvent(Event):
ESCORT_FACTOR = 0.5
STRENGTH_INFLUENCE = 0.3
SUCCESS_FACTOR = 0.8
cas = None # type: db.PlaneDict
escort = None # type: db.PlaneDict
@property
def threat_description(self):
return "{} aircraft + ? CAS".format(self.to_cp.base.scramble_count(self.game.settings.multiplier * self.ESCORT_FACTOR, CAP))
@property
def tasks(self):
return [CAP]
def flight_name(self, for_task: typing.Type[Task]) -> str:
if for_task == CAP:
return "CAP flight"
elif for_task == PinpointStrike:
return "Ground attack"
def __str__(self):
return "Frontline CAP"
def is_successfull(self, debriefing: Debriefing):
if self.game.player_name == self.attacker_name:
attacker_country = self.game.player_country
defender_country = self.game.enemy_country
else:
attacker_country = self.game.enemy_country
defender_country = self.game.player_country
alive_attackers = sum([v for k, v in debriefing.alive_units[attacker_country].items() if db.unit_task(k) == PinpointStrike])
alive_defenders = sum([v for k, v in debriefing.alive_units[defender_country].items() if db.unit_task(k) == PinpointStrike])
attackers_success = (float(alive_attackers) / (alive_defenders + 0.01)) >= self.SUCCESS_FACTOR
if self.from_cp.captured:
return attackers_success
else:
return not attackers_success
def commit(self, debriefing: Debriefing):
super(FrontlinePatrolEvent, self).commit(debriefing)
if self.from_cp.captured:
if self.is_successfull(debriefing):
self.to_cp.base.affect_strength(-self.STRENGTH_INFLUENCE)
else:
self.to_cp.base.affect_strength(+self.STRENGTH_INFLUENCE)
else:
if self.is_successfull(debriefing):
self.from_cp.base.affect_strength(-self.STRENGTH_INFLUENCE)
else:
self.to_cp.base.affect_strength(-self.STRENGTH_INFLUENCE)
def skip(self):
pass
def player_attacking(self, flights: db.TaskForceDict):
assert CAP in flights and len(flights) == 1, "Invalid flights"
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)
op = FrontlinePatrolOperation(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()
attackers = db.unitdict_restrict_count(self.from_cp.base.assemble_attack(), sum(defenders.values()))
op.setup(cas=assigned_units_from(self.cas),
escort=assigned_units_from(self.escort),
interceptors=flights[CAP],
armor_attackers=attackers,
armor_defenders=defenders)
self.operation = op

View File

@ -1,56 +0,0 @@
import math
import random
from dcs.task import *
from dcs.vehicles import *
from game import db
from game.operation.infantrytransport import InfantryTransportOperation
from theater.conflicttheater import *
from userdata.debriefing import Debriefing
from .event import *
class InfantryTransportEvent(Event):
STRENGTH_INFLUENCE = 0.3
def __str__(self):
return "Frontline transport troops"
@property
def tasks(self):
return [Embarking]
def flight_name(self, for_task: typing.Type[Task]) -> str:
if for_task == Embarking:
return "Transport flight"
def is_successfull(self, debriefing: Debriefing):
return True
def commit(self, debriefing: Debriefing):
super(InfantryTransportEvent, self).commit(debriefing)
if self.is_successfull(debriefing):
self.to_cp.base.affect_strength(-self.STRENGTH_INFLUENCE)
else:
self.departure_cp.base.affect_strength(-self.STRENGTH_INFLUENCE)
def player_attacking(self, flights: db.TaskForceDict):
assert Embarking in flights and len(flights) == 1, "Invalid flights"
op = InfantryTransportOperation(
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
)
air_defense = db.find_unittype(AirDefence, self.defender_name)[0]
op.setup(transport=flights[Embarking],
aa={air_defense: 2})
self.operation = op

View File

@ -1,71 +0,0 @@
import math
import random
from dcs.task import *
from game import *
from game.event import *
from game.event.frontlineattack import FrontlineAttackEvent
from game.operation.insurgentattack import InsurgentAttackOperation
from .event import *
class InsurgentAttackEvent(Event):
SUCCESS_FACTOR = 0.7
TARGET_VARIETY = 2
TARGET_AMOUNT_FACTOR = 0.5
STRENGTH_INFLUENCE = 0.1
@property
def threat_description(self):
return ""
@property
def tasks(self):
return [CAS]
def flight_name(self, for_task: typing.Type[Task]) -> str:
if for_task == CAS:
return "Ground intercept flight"
def __str__(self):
return "Destroy insurgents"
def skip(self):
self.to_cp.base.affect_strength(-self.STRENGTH_INFLUENCE)
def is_successfull(self, debriefing: Debriefing):
if self.game.player_name == self.attacker_name:
attacker_country = self.game.player_country
else:
attacker_country = self.game.enemy_country
killed_units = sum([v for k, v in debriefing.destroyed_units[attacker_country].items() if db.unit_task(k) == PinpointStrike])
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_defending(self, flights: db.TaskForceDict):
assert CAS in flights and len(flights) == 1, "Invalid flights"
suitable_unittypes = db.find_unittype(Reconnaissance, self.attacker_name)
random.shuffle(suitable_unittypes)
unittypes = suitable_unittypes[:self.TARGET_VARIETY]
typecount = max(math.floor(self.difficulty * self.TARGET_AMOUNT_FACTOR), 1)
self.targets = {unittype: typecount for unittype in unittypes}
op = InsurgentAttackOperation(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

View File

@ -1,124 +0,0 @@
from game.operation.intercept import InterceptOperation
from .event import *
class InterceptEvent(Event):
STRENGTH_INFLUENCE = 0.3
GLOBAL_STRENGTH_INFLUENCE = 0.3
AIRDEFENSE_COUNT = 3
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):
return "Air Intercept"
@property
def tasks(self):
return [CAP]
def flight_name(self, for_task: typing.Type[Task]) -> str:
if for_task == CAP:
if self.is_player_attacking:
return "Intercept flight"
else:
return "Escort flight"
def _enemy_scramble_multiplier(self) -> float:
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
@property
def threat_description(self):
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):
if self.game.player_name == self.attacker_name:
defender_country = self.game.enemy_country
else:
defender_country = self.game.player_country
units_destroyed = debriefing.destroyed_units.get(defender_country, {}).get(self.transport_unit, 0)
if self.from_cp.captured:
return units_destroyed > 0
else:
return units_destroyed == 0
def commit(self, debriefing: Debriefing):
super(InterceptEvent, self).commit(debriefing)
if self.attacker_name == self.game.player_name:
if self.is_successfull(debriefing):
for _, cp in self.game.theater.conflicts(True):
cp.base.affect_strength(-self.STRENGTH_INFLUENCE)
else:
self.from_cp.base.affect_strength(-self.STRENGTH_INFLUENCE)
else:
# enemy attacking
if self.is_successfull(debriefing):
self.from_cp.base.affect_strength(-self.STRENGTH_INFLUENCE)
else:
self.to_cp.base.affect_strength(-self.STRENGTH_INFLUENCE)
def skip(self):
if self.to_cp.captured:
self.to_cp.base.affect_strength(-self.STRENGTH_INFLUENCE)
def player_attacking(self, flights: db.TaskForceDict):
assert CAP in flights and len(flights) == 1, "Invalid flights"
escort = self.to_cp.base.scramble_sweep(self._enemy_scramble_multiplier())
self.transport_unit = random.choice(db.find_unittype(Transport, self.defender_name))
assert self.transport_unit is not None
airdefense_unit = db.find_unittype(AirDefence, self.defender_name)[-1]
op = InterceptOperation(game=self.game,
attacker_name=self.attacker_name,
defender_name=self.defender_name,
from_cp=self.from_cp,
departure_cp=self.departure_cp,
to_cp=self.to_cp)
op.setup(location=self.location,
escort=assigned_units_from(escort),
transport={self.transport_unit: 1},
airdefense={airdefense_unit: self.AIRDEFENSE_COUNT},
interceptors=flights[CAP])
self.operation = op
def player_defending(self, flights: db.TaskForceDict):
assert CAP in flights and len(flights) == 1, "Invalid flights"
interceptors = self.from_cp.base.scramble_interceptors(self.game.settings.multiplier)
self.transport_unit = random.choice(db.find_unittype(Transport, self.defender_name))
assert self.transport_unit is not None
op = InterceptOperation(game=self.game,
attacker_name=self.attacker_name,
defender_name=self.defender_name,
from_cp=self.from_cp,
departure_cp=self.departure_cp,
to_cp=self.to_cp)
op.setup(location=self.location,
escort=flights[CAP],
transport={self.transport_unit: 1},
interceptors=assigned_units_from(interceptors),
airdefense={})
self.operation = op

View File

@ -1,130 +0,0 @@
from game.operation.navalintercept import NavalInterceptionOperation
from .event import *
class NavalInterceptEvent(Event):
STRENGTH_INFLUENCE = 0.3
SUCCESS_RATE = 0.5
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:
from gen.conflictgen import IMPORTANCE_LOW
factor = (self.to_cp.importance - IMPORTANCE_LOW + 0.1) * 20
return max(int(factor), 1)
def __str__(self) -> str:
return "Naval intercept"
@property
def tasks(self):
if self.is_player_attacking:
return [CAS]
else:
return [CAP]
def flight_name(self, for_task: typing.Type[Task]) -> str:
if for_task == CAS:
return "Naval intercept flight"
elif for_task == CAP:
return "CAP flight"
@property
def threat_description(self):
s = "{} ship(s)".format(self._targets_count())
if not self.departure_cp.captured:
s += ", {} aircraft".format(self.departure_cp.base.scramble_count(self.game.settings.multiplier))
return s
@property
def global_cp_available(self) -> bool:
return True
def is_successfull(self, debriefing: Debriefing):
if self.game.player_name == self.attacker_name:
defender_country = self.game.enemy_country
else:
defender_country = self.game.player_country
total_targets = sum(self.targets.values())
destroyed_targets = 0
for unit, count in debriefing.destroyed_units.get(defender_country, {}).items():
if unit in self.targets:
destroyed_targets += count
if self.departure_cp.captured:
return math.ceil(float(destroyed_targets) / total_targets) > self.SUCCESS_RATE
else:
return math.ceil(float(destroyed_targets) / total_targets) < self.SUCCESS_RATE
def commit(self, debriefing: Debriefing):
super(NavalInterceptEvent, self).commit(debriefing)
if self.attacker_name == self.game.player_name:
if self.is_successfull(debriefing):
self.to_cp.base.affect_strength(-self.STRENGTH_INFLUENCE)
else:
self.departure_cp.base.affect_strength(-self.STRENGTH_INFLUENCE)
else:
# enemy attacking
if self.is_successfull(debriefing):
self.departure_cp.base.affect_strength(-self.STRENGTH_INFLUENCE)
else:
self.to_cp.base.affect_strength(-self.STRENGTH_INFLUENCE)
def skip(self):
if self.to_cp.captured:
self.to_cp.base.affect_strength(-self.STRENGTH_INFLUENCE)
def player_attacking(self, flights: db.TaskForceDict):
assert CAS in flights and len(flights) == 1, "Invalid flights"
self.targets = {
random.choice(db.find_unittype(CargoTransportation, self.defender_name)): self._targets_count(),
}
op = NavalInterceptionOperation(
self.game,
attacker_name=self.attacker_name,
defender_name=self.defender_name,
from_cp=self.from_cp,
departure_cp=self.departure_cp,
to_cp=self.to_cp
)
op.setup(location=self.location,
strikegroup=flights[CAS],
interceptors={},
targets=self.targets)
self.operation = op
def player_defending(self, flights: db.TaskForceDict):
assert CAP in flights and len(flights) == 1, "Invalid flights"
self.targets = {
random.choice(db.find_unittype(CargoTransportation, self.defender_name)): self._targets_count(),
}
op = NavalInterceptionOperation(
self.game,
attacker_name=self.attacker_name,
defender_name=self.defender_name,
from_cp=self.from_cp,
departure_cp=self.departure_cp,
to_cp=self.to_cp
)
strikegroup = self.departure_cp.base.scramble_cas(self.game.settings.multiplier)
op.setup(strikegroup=assigned_units_from(strikegroup),
interceptors=flights[CAP],
targets=self.targets)
self.operation = op

View File

@ -1,74 +0,0 @@
from game.operation.strike import StrikeOperation
from .event import *
class StrikeEvent(Event):
STRENGTH_INFLUENCE = 0.0
SINGLE_OBJECT_STRENGTH_INFLUENCE = 0.05
def __str__(self):
return "Strike / SEAD"
def is_successfull(self, debriefing: Debriefing):
return True
@property
def threat_description(self):
return "{} aircraft + AA".format(self.to_cp.base.scramble_count(self.game.settings.multiplier, CAP))
@property
def tasks(self):
if self.is_player_attacking:
return [CAP, CAS, SEAD]
else:
return [CAP]
@property
def ai_banned_tasks(self):
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:
if for_task == CAP:
if self.is_player_attacking:
return "Escort flight"
else:
return "CAP flight"
elif for_task == SEAD:
return "SEAD flight"
elif for_task == CAS:
return "Strike flight"
def commit(self, debriefing: Debriefing):
super(StrikeEvent, self).commit(debriefing)
#self.to_cp.base.affect_strength(-self.SINGLE_OBJECT_STRENGTH_INFLUENCE * len(debriefing.destroyed_objects))
pass
def player_attacking(self, flights: db.TaskForceDict):
assert CAP in flights and CAS in flights and SEAD in flights and len(flights) == 3, "Invalid flights"
op = StrikeOperation(
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
)
interceptors = self.to_cp.base.scramble_interceptors(self.game.settings.multiplier)
op.setup(strikegroup=flights[CAS],
sead=flights[SEAD],
escort=flights[CAP],
interceptors=assigned_units_from(interceptors))
self.operation = op

View File

@ -10,7 +10,9 @@ China_2000 = {
MiG_21Bis, # Standing as J-7 MiG_21Bis, # Standing as J-7
Su_30, Su_30,
Su_33,
J_11A, J_11A,
JF_17,
IL_76MD, IL_76MD,
IL_78M, IL_78M,
@ -26,14 +28,17 @@ China_2000 = {
AirDefence.SAM_SA_6_Kub_LN_2P25, AirDefence.SAM_SA_6_Kub_LN_2P25,
AirDefence.HQ_7_Self_Propelled_LN, AirDefence.HQ_7_Self_Propelled_LN,
Armor.MBT_T_72B, # Type 99 Armor.ZTZ_96B,
Armor.MBT_T_55, Armor.MBT_T_55,
Armor.ZBD_04A, Armor.ZBD_04A,
Armor.IFV_BMP_1, Armor.IFV_BMP_1,
Artillery.MLRS_9A52_Smerch,
Artillery.SPH_2S9_Nona,
Unarmed.Transport_Ural_375, Unarmed.Transport_Ural_375,
Unarmed.Transport_UAZ_469, Unarmed.Transport_UAZ_469,
Infantry.Soldier_AK, Infantry.Soldier_AK,
Infantry.Paratrooper_RPG_16,
CV_1143_5_Admiral_Kuznetsov, CV_1143_5_Admiral_Kuznetsov,
Bulk_cargo_ship_Yakushev, Bulk_cargo_ship_Yakushev,

View File

@ -23,6 +23,7 @@ France_1995 = {
Unarmed.Transport_M818, Unarmed.Transport_M818,
Infantry.Infantry_M4, Infantry.Infantry_M4,
Infantry.Infantry_M4,
AirDefence.SAM_Roland_ADS, AirDefence.SAM_Roland_ADS,
AirDefence.SAM_Hawk_PCP, AirDefence.SAM_Hawk_PCP,

View File

@ -1,6 +1,6 @@
from dcs.planes import MiG_15bis, IL_76MD, IL_78M, An_26B, An_30M, Yak_40 from dcs.planes import MiG_15bis, IL_76MD, IL_78M, An_26B, An_30M, Yak_40
from dcs.ships import CV_1143_5_Admiral_Kuznetsov, Bulk_cargo_ship_Yakushev, Dry_cargo_ship_Ivanov, Tanker_Elnya_160 from dcs.ships import CV_1143_5_Admiral_Kuznetsov, Bulk_cargo_ship_Yakushev, Dry_cargo_ship_Ivanov, Tanker_Elnya_160
from dcs.vehicles import AirDefence, Armor, Unarmed, Infantry from dcs.vehicles import AirDefence, Armor, Unarmed, Infantry, Artillery
Russia_1955 = { Russia_1955 = {
"country": "Russia", "country": "Russia",
@ -16,19 +16,23 @@ Russia_1955 = {
AirDefence.AAA_ZU_23_Closed, AirDefence.AAA_ZU_23_Closed,
AirDefence.AAA_ZU_23_on_Ural_375, AirDefence.AAA_ZU_23_on_Ural_375,
Armor.ARV_BRDM_2, Armor.ARV_BRDM_2,
Armor.ARV_MTLB_U_BOMAN, Armor.ARV_MTLB_U_BOMAN,
Armor.APC_MTLB, Armor.APC_MTLB,
Armor.MBT_T_55, Armor.MBT_T_55,
Artillery.MLRS_BM_21_Grad,
Unarmed.Transport_Ural_375, Unarmed.Transport_Ural_375,
Unarmed.Transport_UAZ_469, Unarmed.Transport_UAZ_469,
Infantry.Soldier_AK,
CV_1143_5_Admiral_Kuznetsov, CV_1143_5_Admiral_Kuznetsov,
Bulk_cargo_ship_Yakushev, Bulk_cargo_ship_Yakushev,
Dry_cargo_ship_Ivanov, Dry_cargo_ship_Ivanov,
Tanker_Elnya_160 Tanker_Elnya_160,
# Infantry squad
Infantry.Paratrooper_AKS,
Infantry.Infantry_Soldier_Rus,
Infantry.Paratrooper_RPG_16,
] ]
} }

View File

@ -1,7 +1,7 @@
from dcs.helicopters import Mi_8MT from dcs.helicopters import Mi_8MT
from dcs.planes import MiG_15bis, MiG_19P, MiG_21Bis, IL_76MD, IL_78M, An_26B, An_30M, Yak_40, A_50 from dcs.planes import MiG_15bis, MiG_19P, MiG_21Bis, IL_76MD, IL_78M, An_26B, An_30M, Yak_40, A_50
from dcs.ships import CV_1143_5_Admiral_Kuznetsov, Bulk_cargo_ship_Yakushev, Dry_cargo_ship_Ivanov, Tanker_Elnya_160 from dcs.ships import CV_1143_5_Admiral_Kuznetsov, Bulk_cargo_ship_Yakushev, Dry_cargo_ship_Ivanov, Tanker_Elnya_160
from dcs.vehicles import AirDefence, Armor, Unarmed, Infantry from dcs.vehicles import AirDefence, Armor, Unarmed, Infantry, Artillery
Russia_1965 = { Russia_1965 = {
"country": "Russia", "country": "Russia",
@ -31,15 +31,21 @@ Russia_1965 = {
Armor.IFV_BMD_1, Armor.IFV_BMD_1,
Armor.IFV_BMP_1, Armor.IFV_BMP_1,
Armor.MBT_T_55, Armor.MBT_T_55,
Artillery.MLRS_BM_21_Grad,
Unarmed.Transport_Ural_375, Unarmed.Transport_Ural_375,
Unarmed.Transport_UAZ_469, Unarmed.Transport_UAZ_469,
Infantry.Soldier_AK,
CV_1143_5_Admiral_Kuznetsov, CV_1143_5_Admiral_Kuznetsov,
Bulk_cargo_ship_Yakushev, Bulk_cargo_ship_Yakushev,
Dry_cargo_ship_Ivanov, Dry_cargo_ship_Ivanov,
Tanker_Elnya_160 Tanker_Elnya_160,
# Infantry squad
Infantry.Paratrooper_AKS,
Infantry.Infantry_Soldier_Rus,
Infantry.Paratrooper_RPG_16,
], ],
"shorad":[ "shorad":[
AirDefence.AAA_ZU_23_Closed AirDefence.AAA_ZU_23_Closed

View File

@ -2,7 +2,7 @@ from dcs.helicopters import Mi_8MT, Mi_24V
from dcs.planes import MiG_21Bis, MiG_23MLD, MiG_25PD, MiG_29A, Su_17M4, Su_24M, Su_25, IL_76MD, IL_78M, An_26B, An_30M, \ from dcs.planes import MiG_21Bis, MiG_23MLD, MiG_25PD, MiG_29A, Su_17M4, Su_24M, Su_25, IL_76MD, IL_78M, An_26B, An_30M, \
Yak_40, A_50 Yak_40, A_50
from dcs.ships import * from dcs.ships import *
from dcs.vehicles import AirDefence, Armor, Unarmed, Infantry from dcs.vehicles import AirDefence, Armor, Unarmed, Infantry, Artillery
Russia_1975 = { Russia_1975 = {
"country": "Russia", "country": "Russia",
@ -39,14 +39,22 @@ Russia_1975 = {
Armor.IFV_BMP_1, Armor.IFV_BMP_1,
Armor.MBT_T_55, Armor.MBT_T_55,
Artillery.SPH_2S9_Nona,
Artillery.SPH_2S1_Gvozdika,
Unarmed.Transport_Ural_375, Unarmed.Transport_Ural_375,
Unarmed.Transport_UAZ_469, Unarmed.Transport_UAZ_469,
Infantry.Soldier_AK,
CV_1143_5_Admiral_Kuznetsov, CV_1143_5_Admiral_Kuznetsov,
Bulk_cargo_ship_Yakushev, Bulk_cargo_ship_Yakushev,
Dry_cargo_ship_Ivanov, Dry_cargo_ship_Ivanov,
Tanker_Elnya_160 Tanker_Elnya_160,
# Infantry squad
Infantry.Paratrooper_AKS,
Infantry.Infantry_Soldier_Rus,
Infantry.Paratrooper_RPG_16,
], ],
"shorad": [ "shorad": [
AirDefence.AAA_ZU_23_Emplacement, AirDefence.AAA_ZU_23_Emplacement,

View File

@ -16,7 +16,6 @@ Russia_1990 = {
Su_27, Su_27,
Su_24M, Su_24M,
Su_24MR,
Su_25, Su_25,
IL_76MD, IL_76MD,
@ -39,15 +38,21 @@ Russia_1990 = {
Armor.IFV_BMD_1, Armor.IFV_BMD_1,
Armor.IFV_BMP_1, Armor.IFV_BMP_1,
Armor.MBT_T_55, Armor.MBT_T_55,
Artillery.MLRS_9K57_Uragan_BM_27,
Artillery.SPH_2S19_Msta,
Unarmed.Transport_Ural_375, Unarmed.Transport_Ural_375,
Unarmed.Transport_UAZ_469, Unarmed.Transport_UAZ_469,
Infantry.Soldier_AK,
CV_1143_5_Admiral_Kuznetsov, CV_1143_5_Admiral_Kuznetsov,
Bulk_cargo_ship_Yakushev, Bulk_cargo_ship_Yakushev,
Dry_cargo_ship_Ivanov, Dry_cargo_ship_Ivanov,
Tanker_Elnya_160 Tanker_Elnya_160,
# Infantry squad
Infantry.Paratrooper_AKS,
Infantry.Infantry_Soldier_Rus,
Infantry.Paratrooper_RPG_16,
], ],
"shorad":[ "shorad":[
AirDefence.SAM_SA_9_Strela_1_9P31, AirDefence.SAM_SA_9_Strela_1_9P31,

View File

@ -16,7 +16,7 @@ Russia_2010 = {
Su_25T, Su_25T,
Su_34, Su_34,
Su_24MR, Su_24M,
L_39ZA, L_39ZA,
IL_76MD, IL_76MD,
@ -38,13 +38,21 @@ Russia_2010 = {
Armor.MBT_T_80U, Armor.MBT_T_80U,
Armor.MBT_T_72B, Armor.MBT_T_72B,
Artillery.MLRS_9K57_Uragan_BM_27,
Artillery.SPH_2S19_Msta,
Unarmed.Transport_Ural_375, Unarmed.Transport_Ural_375,
Unarmed.Transport_UAZ_469, Unarmed.Transport_UAZ_469,
Infantry.Soldier_AK,
CV_1143_5_Admiral_Kuznetsov, CV_1143_5_Admiral_Kuznetsov,
Bulk_cargo_ship_Yakushev, Bulk_cargo_ship_Yakushev,
Dry_cargo_ship_Ivanov, Dry_cargo_ship_Ivanov,
Tanker_Elnya_160, Tanker_Elnya_160,
# Infantry squad
Infantry.Paratrooper_AKS,
Infantry.Infantry_Soldier_Rus,
Infantry.Paratrooper_RPG_16,
], ],
"shorad":[ "shorad":[
AirDefence.SAM_SA_19_Tunguska_2S6, AirDefence.SAM_SA_19_Tunguska_2S6,

View File

@ -15,6 +15,8 @@ USA_1944 = {
Armor.MT_M4A4_Sherman_Firefly, Armor.MT_M4A4_Sherman_Firefly,
Armor.M30_Cargo_Carrier, Armor.M30_Cargo_Carrier,
Armor.APC_M2A1, Armor.APC_M2A1,
Armor.CT_Cromwell_IV,
Artillery.M12_GMC,
Infantry.Infantry_M1_Garand, Infantry.Infantry_M1_Garand,

View File

@ -22,6 +22,7 @@ USA_1960 = {
Unarmed.Transport_M818, Unarmed.Transport_M818,
Infantry.Infantry_M4, Infantry.Infantry_M4,
Infantry.Soldier_M249,
AirDefence.AAA_Vulcan_M163, AirDefence.AAA_Vulcan_M163,

View File

@ -24,6 +24,7 @@ USA_1965 = {
Armor.APC_M113, Armor.APC_M113,
Unarmed.Transport_M818, Unarmed.Transport_M818,
Infantry.Infantry_M4, Infantry.Infantry_M4,
Infantry.Soldier_M249,
AirDefence.SAM_Chaparral_M48, AirDefence.SAM_Chaparral_M48,
AirDefence.SAM_Hawk_PCP, AirDefence.SAM_Hawk_PCP,

View File

@ -31,6 +31,7 @@ USA_1990 = {
Armor.ATGM_M1134_Stryker, Armor.ATGM_M1134_Stryker,
Unarmed.Transport_M818, Unarmed.Transport_M818,
Infantry.Infantry_M4, Infantry.Infantry_M4,
Infantry.Soldier_M249,
AirDefence.SAM_Hawk_PCP, AirDefence.SAM_Hawk_PCP,

View File

@ -11,6 +11,7 @@ USA_2005 = {
F_14B, F_14B,
FA_18C_hornet, FA_18C_hornet,
F_16C_50, F_16C_50,
JF_17,
A_10C, A_10C,
AV8BNA, AV8BNA,
@ -32,8 +33,12 @@ USA_2005 = {
Armor.APC_M1043_HMMWV_Armament, Armor.APC_M1043_HMMWV_Armament,
Armor.ATGM_M1045_HMMWV_TOW, Armor.ATGM_M1045_HMMWV_TOW,
Artillery.MLRS_M270,
Artillery.SPH_M109_Paladin,
Unarmed.Transport_M818, Unarmed.Transport_M818,
Infantry.Infantry_M4, Infantry.Infantry_M4,
Infantry.Soldier_M249,
AirDefence.SAM_Hawk_PCP, AirDefence.SAM_Hawk_PCP,
AirDefence.SAM_Patriot_EPP_III, AirDefence.SAM_Patriot_EPP_III,

View File

@ -9,6 +9,7 @@ from dcs.vehicles import *
from game.game_stats import GameStats from game.game_stats import GameStats
from gen.conflictgen import Conflict from gen.conflictgen import Conflict
from gen.flights.ai_flight_planner import FlightPlanner from gen.flights.ai_flight_planner import FlightPlanner
from gen.ground_forces.ai_ground_planner import GroundPlanner
from userdata.debriefing import Debriefing from userdata.debriefing import Debriefing
from theater import * from theater import *
@ -38,39 +39,6 @@ PLAYER_INTERCEPT_GLOBAL_PROBABILITY_BASE = 30
PLAYER_INTERCEPT_GLOBAL_PROBABILITY_LOG = 2 PLAYER_INTERCEPT_GLOBAL_PROBABILITY_LOG = 2
PLAYER_BASEATTACK_THRESHOLD = 0.4 PLAYER_BASEATTACK_THRESHOLD = 0.4
"""
Various events probabilities. First key is player probabilty, second is enemy probability.
For the enemy events, only 1 event of each type could be generated for a turn.
Events:
* BaseAttackEvent - capture base
* InterceptEvent - air intercept
* FrontlineAttackEvent - frontline attack
* NavalInterceptEvent - naval intercept
* StrikeEvent - strike event
* InfantryTransportEvent - helicopter infantry transport
"""
EVENT_PROBABILITIES = {
# events always present; only for the player
FrontlineAttackEvent: [100, 9],
#FrontlinePatrolEvent: [100, 0],
StrikeEvent: [100, 0],
# events randomly present; only for the player
#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
PLAYER_BASE_STRENGTH_RECOVERY = 0.2 PLAYER_BASE_STRENGTH_RECOVERY = 0.2
@ -81,9 +49,11 @@ ENEMY_BASE_STRENGTH_RECOVERY = 0.05
AWACS_BUDGET_COST = 4 AWACS_BUDGET_COST = 4
# Initial budget value # Initial budget value
PLAYER_BUDGET_INITIAL = 170 PLAYER_BUDGET_INITIAL = 450
# Base post-turn bonus value # Base post-turn bonus value
PLAYER_BUDGET_BASE = 14 PLAYER_BUDGET_BASE = 10
# Bonus multiplier logarithm base # Bonus multiplier logarithm base
PLAYER_BUDGET_IMPORTANCE_LOG = 2 PLAYER_BUDGET_IMPORTANCE_LOG = 2
@ -113,6 +83,7 @@ class Game:
self.game_stats = GameStats() self.game_stats = GameStats()
self.game_stats.update(self) self.game_stats.update(self)
self.planners = {} self.planners = {}
self.ground_planners = {}
def _roll(self, prob, mult): def _roll(self, prob, mult):
if self.settings.version == "dev": if self.settings.version == "dev":
@ -122,88 +93,11 @@ class Game:
return random.randint(1, 100) <= prob * mult return random.randint(1, 100) <= prob * mult
def _generate_player_event(self, event_class, player_cp, enemy_cp): def _generate_player_event(self, event_class, player_cp, enemy_cp):
if event_class == NavalInterceptEvent and enemy_cp.radials == LAND:
# skip naval events for non-coastal CPs
return
if event_class == BaseAttackEvent and enemy_cp.base.strength > PLAYER_BASEATTACK_THRESHOLD and self.settings.version != "dev":
# skip base attack events for CPs yet too strong
return
if event_class == StrikeEvent and not enemy_cp.ground_objects:
# skip strikes in case of no targets
return
self.events.append(event_class(self, player_cp, enemy_cp, enemy_cp.position, self.player_name, self.enemy_name)) self.events.append(event_class(self, player_cp, enemy_cp, enemy_cp.position, self.player_name, self.enemy_name))
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_name, self.player_name))
def _generate_events(self): def _generate_events(self):
strikes_generated_for = set()
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):
for event_class, (player_probability, enemy_probability) in EVENT_PROBABILITIES.items(): self._generate_player_event(FrontlineAttackEvent, player_cp, enemy_cp)
if event_class in [FrontlineAttackEvent, FrontlinePatrolEvent, InfantryTransportEvent, ConvoyStrikeEvent]:
# skip events requiring frontline
if not Conflict.has_frontline_between(player_cp, enemy_cp):
continue
# don't generate multiple 100% events from each attack direction
if event_class is StrikeEvent:
if enemy_cp in strikes_generated_for:
continue
if event_class is BaseAttackEvent:
if enemy_cp in base_attack_generated_for:
continue
if player_probability == 100 or player_probability > 0 and self._roll(player_probability, player_cp.base.strength):
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_probability == 100 or enemy_probability > 0 and self._roll(enemy_probability, enemy_cp.base.strength):
self._generate_enemy_event(event_class, player_cp, enemy_cp)
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)
@ -214,7 +108,7 @@ class Game:
return db.choose_units(for_task, importance_factor, COMMISION_UNIT_VARIETY, self.enemy_name) return db.choose_units(for_task, importance_factor, COMMISION_UNIT_VARIETY, self.enemy_name)
def _commision_units(self, cp: ControlPoint): def _commision_units(self, cp: ControlPoint):
for for_task in [PinpointStrike, CAS, CAP, AirDefence]: for for_task in [CAS, CAP, AirDefence]:
limit = COMMISION_LIMITS_FACTORS[for_task] * math.pow(cp.importance, COMMISION_LIMITS_SCALE) * self.settings.multiplier limit = COMMISION_LIMITS_FACTORS[for_task] * math.pow(cp.importance, COMMISION_LIMITS_SCALE) * self.settings.multiplier
missing_units = limit - cp.base.total_units(for_task) missing_units = limit - cp.base.total_units(for_task)
if missing_units > 0: if missing_units > 0:
@ -229,11 +123,32 @@ class Game:
@property @property
def budget_reward_amount(self): def budget_reward_amount(self):
reward = 0
if len(self.theater.player_points()) > 0: if len(self.theater.player_points()) > 0:
total_importance = sum([x.importance * x.base.strength for x in self.theater.player_points()]) reward = PLAYER_BUDGET_BASE * len(self.theater.player_points())
return math.ceil(math.log(total_importance + 1, PLAYER_BUDGET_IMPORTANCE_LOG) * PLAYER_BUDGET_BASE * self.settings.multiplier) for cp in self.theater.player_points():
for g in cp.ground_objects:
if g.category == "power":
reward = reward + 10
elif g.category == "warehouse":
reward = reward + 8
elif g.category == "fuel":
reward = reward + 10
elif g.category == "ammo":
reward = reward + 6
elif g.category == "farp":
reward = reward + 4
elif g.category == "fob":
reward = reward + 4
elif g.category == "factory":
reward = reward + 25
elif g.category == "comms":
reward = reward + 25
elif g.category == "oil":
reward = reward + 45
return reward
else: else:
return 0 return reward
def _budget_player(self): def _budget_player(self):
self.budget += self.budget_reward_amount self.budget += self.budget_reward_amount
@ -310,14 +225,21 @@ class Game:
# Update statistics # Update statistics
self.game_stats.update(self) self.game_stats.update(self)
# Plan flights for next turn # Plan flights & combat for next turn
self.planners = {} self.planners = {}
self.ground_planners = {}
for cp in self.theater.controlpoints: for cp in self.theater.controlpoints:
if cp.has_runway(): if cp.has_runway():
planner = FlightPlanner(cp, self) planner = FlightPlanner(cp, self)
planner.plan_flights() planner.plan_flights()
self.planners[cp.id] = planner self.planners[cp.id] = planner
if cp.has_frontline:
gplanner = GroundPlanner(cp, self)
gplanner.plan_groundwar()
self.ground_planners[cp.id] = gplanner
@property @property
def current_turn_daytime(self): def current_turn_daytime(self):

View File

@ -1,70 +0,0 @@
from game.db import assigned_units_split
from gen.triggergen import *
from .operation import *
class BaseAttackOperation(Operation):
cas = None # type: db.AssignedUnitsDict
escort = None # type: db.AssignedUnitsDict
intercept = None # type: db.AssignedUnitsDict
attack = None # type: db.ArmorDict
defense = None # type: db.ArmorDict
aa = None # type: db.AirDefenseDict
trigger_radius = TRIGGER_RADIUS_SMALL
def setup(self,
cas: db.AssignedUnitsDict,
escort: db.AssignedUnitsDict,
attack: db.AssignedUnitsDict,
intercept: db.AssignedUnitsDict,
defense: db.ArmorDict,
aa: db.AirDefenseDict):
self.cas = cas
self.escort = escort
self.intercept = intercept
self.attack = attack
self.defense = defense
self.aa = aa
def prepare(self, terrain: dcs.terrain.Terrain, is_quick: bool):
super(BaseAttackOperation, self).prepare(terrain, is_quick)
self.defenders_starting_position = None
if self.game.player_name == self.defender_name:
self.attackers_starting_position = None
conflict = Conflict.capture_conflict(
attacker_name=self.attacker_name,
defender_name=self.defender_name,
attacker=self.current_mission.country(self.attacker_country),
defender=self.current_mission.country(self.defender_country),
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):
self.armorgen.generate(self.attack, self.defense)
self.airgen.generate_defense(*assigned_units_split(self.intercept), at=self.defenders_starting_position)
self.airgen.generate_cas_strikegroup(*assigned_units_split(self.cas), 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.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."
if self.game.player_name == self.attacker_name:
self.briefinggen.append_waypoint("TARGET")
else:
pass
super(BaseAttackOperation, self).generate()

View File

@ -1,51 +0,0 @@
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_name=self.attacker_name,
defender_name=self.defender_name,
attacker=self.current_mission.country(self.attacker_country),
defender=self.current_mission.country(self.defender_country),
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()

View File

@ -1,61 +0,0 @@
from game.db import assigned_units_split
from .operation import *
MAX_DISTANCE_BETWEEN_GROUPS = 12000
class FrontlinePatrolOperation(Operation):
cas = None # type: db.AssignedUnitsDict
escort = None # type: db.AssignedUnitsDict
interceptors = None # type: db.AssignedUnitsDict
armor_attackers = None # type: db.ArmorDict
armor_defenders = None # type: db.ArmorDict
def setup(self,
cas: db.AssignedUnitsDict,
escort: db.AssignedUnitsDict,
interceptors: db.AssignedUnitsDict,
armor_attackers: db.ArmorDict,
armor_defenders: db.ArmorDict):
self.cas = cas
self.escort = escort
self.interceptors = interceptors
self.armor_attackers = armor_attackers
self.armor_defenders = armor_defenders
def prepare(self, terrain: Terrain, is_quick: bool):
super(FrontlinePatrolOperation, self).prepare(terrain, is_quick)
self.defenders_starting_position = None
conflict = Conflict.frontline_cap_conflict(
attacker_name=self.attacker_name,
defender_name=self.defender_name,
attacker=self.current_mission.country(self.attacker_country),
defender=self.current_mission.country(self.defender_country),
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.interceptors))
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.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.append_waypoint("CAP AREA IP")
self.briefinggen.append_waypoint("CAP AREA EGRESS")
super(FrontlinePatrolOperation, self).generate()

View File

@ -1,47 +0,0 @@
from game.db import assigned_units_split
from .operation import *
class InfantryTransportOperation(Operation):
transport = None # type: db.AssignedUnitsDict
aa = None # type: db.AirDefenseDict
def setup(self, transport: db.AssignedUnitsDict, aa: db.AirDefenseDict):
self.transport = transport
self.aa = aa
def prepare(self, terrain: Terrain, is_quick: bool):
super(InfantryTransportOperation, self).prepare(terrain, is_quick)
conflict = Conflict.transport_conflict(
attacker_name=self.attacker_name,
defender_name=self.defender_name,
attacker=self.current_mission.country(self.attacker_country),
defender=self.current_mission.country(self.defender_country),
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):
self.airgen.generate_passenger_transport(*assigned_units_split(self.transport), at=self.attackers_starting_position)
self.armorgen.generate_passengers(count=6)
self.visualgen.generate_transportation_marker(self.conflict.ground_attackers_location)
self.visualgen.generate_transportation_destination(self.conflict.position)
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.append_waypoint("DROP POINT")
# TODO: horrible, horrible hack
# this will disable vehicle activation triggers,
# which aren't needed on this type of missions
self.is_quick = True
super(InfantryTransportOperation, self).generate()
self.is_quick = False

View File

@ -1,40 +0,0 @@
from game.db import assigned_units_split
from .operation import *
class InsurgentAttackOperation(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(InsurgentAttackOperation, self).prepare(terrain, is_quick)
conflict = Conflict.ground_attack_conflict(
attacker_name=self.attacker_name,
defender_name=self.defender_name,
attacker=self.current_mission.country(self.attacker_country),
defender=self.current_mission.country(self.defender_country),
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):
self.airgen.generate_defenders_cas(*assigned_units_split(self.strikegroup), at=self.defenders_starting_position)
self.armorgen.generate(self.target, {})
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.append_waypoint("TARGET")
super(InsurgentAttackOperation, self).generate()

View File

@ -1,67 +0,0 @@
from game.db import assigned_units_split
from .operation import *
class InterceptOperation(Operation):
location = None # type: Point
escort = None # type: db.AssignedUnitsDict
transport = None # type: db.PlaneDict
interceptors = None # type: db.AssignedUnitsDict
airdefense = None # type: db.AirDefenseDict
trigger_radius = TRIGGER_RADIUS_LARGE
def setup(self,
location: Point,
escort: db.AssignedUnitsDict,
transport: db.PlaneDict,
airdefense: db.AirDefenseDict,
interceptors: db.AssignedUnitsDict):
self.location = location
self.escort = escort
self.transport = transport
self.airdefense = airdefense
self.interceptors = interceptors
def prepare(self, terrain: Terrain, is_quick: bool):
super(InterceptOperation, self).prepare(terrain, is_quick)
self.defenders_starting_position = None
if self.defender_name == self.game.player_name:
self.attackers_starting_position = None
conflict = Conflict.intercept_conflict(
attacker_name=self.attacker_name,
defender_name=self.defender_name,
attacker=self.current_mission.country(self.attacker_country),
defender=self.current_mission.country(self.defender_country),
position=self.location,
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.interceptors))
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_interception(*assigned_units_split(self.interceptors), at=self.attackers_starting_position)
self.briefinggen.title = "Air Intercept"
if self.game.player_name == 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()

View File

@ -1,69 +0,0 @@
from game.db import assigned_units_split
from .operation import *
class NavalInterceptionOperation(Operation):
location = None # type: Point
strikegroup = None # type: db.AssignedUnitsDict
interceptors = None # type: db.AssignedUnitsDict
targets = None # type: db.ShipDict
trigger_radius = TRIGGER_RADIUS_LARGE
def setup(self,
location: Point,
strikegroup: db.AssignedUnitsDict,
interceptors: db.AssignedUnitsDict,
targets: db.ShipDict):
self.location = location
self.strikegroup = strikegroup
self.interceptors = interceptors
self.targets = targets
def prepare(self, terrain: Terrain, is_quick: bool):
super(NavalInterceptionOperation, self).prepare(terrain, is_quick)
if self.defender_name == self.game.player_name:
self.attackers_starting_position = None
conflict = Conflict.naval_intercept_conflict(
attacker_name=self.attacker_name,
defender_name=self.defender_name,
attacker=self.current_mission.country(self.attacker_country),
defender=self.current_mission.country(self.defender_country),
position=self.location,
from_cp=self.from_cp,
to_cp=self.to_cp,
theater=self.game.theater
)
self.initialize(self.current_mission, conflict)
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)
self.airgen.generate_ship_strikegroup(
*assigned_units_split(self.strikegroup),
target_groups=target_groups,
at=self.attackers_starting_position
)
if self.interceptors:
self.airgen.generate_defense(
*assigned_units_split(self.interceptors),
at=self.defenders_starting_position
)
self.briefinggen.title = "Naval Intercept"
if self.game.player_name == 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()

View File

@ -65,7 +65,6 @@ class Operation:
def initialize(self, mission: Mission, conflict: Conflict): def initialize(self, mission: Mission, conflict: Conflict):
self.current_mission = mission self.current_mission = mission
self.conflict = conflict self.conflict = conflict
self.armorgen = ArmorConflictGenerator(mission, conflict)
self.airgen = AircraftConflictGenerator(mission, conflict, self.game.settings) self.airgen = AircraftConflictGenerator(mission, conflict, self.game.settings)
self.shipgen = ShipGenerator(mission, conflict) self.shipgen = ShipGenerator(mission, conflict)
self.airsupportgen = AirSupportConflictGenerator(mission, conflict, self.game) self.airsupportgen = AirSupportConflictGenerator(mission, conflict, self.game)
@ -158,10 +157,13 @@ class Operation:
self.current_mission.country(self.attacker_country), self.current_mission.country(self.attacker_country),
self.current_mission.country(self.defender_country), self.current_mission.country(self.defender_country),
player_cp, enemy_cp, self.game.theater) player_cp, enemy_cp, self.game.theater)
armorgen = ArmorConflictGenerator(self.current_mission, conflict) # Generate frontline ops
armorgen.generate_vec(player_cp.base.armor, enemy_cp.base.armor) player_gp = self.game.ground_planners[player_cp.id].units_per_cp[enemy_cp.id]
enemy_gp = self.game.ground_planners[enemy_cp.id].units_per_cp[player_cp.id]
groundConflictGen = GroundConflictGenerator(self.current_mission, conflict, self.game, player_gp, enemy_gp, player_cp.stances[enemy_cp.id])
groundConflictGen.generate()
#Setup combined arms parameters # Setup combined arms parameters
self.current_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_country in [country.name for country in self.current_mission.coalition["blue"].countries.values()]: if self.game.player_country in [country.name for country in self.current_mission.coalition["blue"].countries.values()]:
self.current_mission.groundControl.blue_tactical_commander = self.ca_slots self.current_mission.groundControl.blue_tactical_commander = self.ca_slots

View File

@ -1,96 +0,0 @@
from game.db import assigned_units_split
from .operation import *
class StrikeOperation(Operation):
strikegroup = None # type: db.AssignedUnitsDict
sead = None # type: db.AssignedUnitsDict
escort = None # type: db.AssignedUnitsDict
interceptors = None # type: db.AssignedUnitsDict
trigger_radius = TRIGGER_RADIUS_ALL_MAP
def setup(self,
strikegroup: db.AssignedUnitsDict,
sead: db.AssignedUnitsDict,
escort: db.AssignedUnitsDict,
interceptors: db.AssignedUnitsDict):
self.strikegroup = strikegroup
self.sead = sead
self.escort = escort
self.interceptors = interceptors
def prepare(self, terrain: Terrain, is_quick: bool):
super(StrikeOperation, self).prepare(terrain, is_quick)
self.defenders_starting_position = None
if self.game.player_name == self.defender_name:
self.attackers_starting_position = None
conflict = Conflict.strike_conflict(
attacker_name=self.attacker_name,
defender_name=self.defender_name,
attacker=self.current_mission.country(self.attacker_country),
defender=self.current_mission.country(self.defender_country),
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):
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]
processed_groups = []
for object in self.to_cp.ground_objects:
if object.group_identifier in processed_groups:
continue
processed_groups.append(object.group_identifier)
category_counters[object.category] = category_counters.get(object.category, 0) + 1
markpoint_name = "{}{}".format(object.name_abbrev, category_counters[object.category])
if object.category == "aa":
sead_targets.append((str(object), markpoint_name, object.position))
targets.append((str(object), markpoint_name, object.position))
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()}
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.description = "Destroy infrastructure assets and military supplies in the region. Each building destroyed will lower targets strength."
super(StrikeOperation, self).generate()

View File

@ -1,17 +1,8 @@
import logging
from random import randint
from itertools import zip_longest
from game import db
from .conflictgen import *
from .naming import *
from dcs.mission import *
from dcs.unittype import *
from dcs.point import *
from dcs.task import * from dcs.task import *
from dcs.country import *
from gen import namegen
from gen.ground_forces.ai_ground_planner import CombatGroupRole, DISTANCE_FROM_FRONTLINE
from .conflictgen import *
SPREAD_DISTANCE_FACTOR = 0.1, 0.3 SPREAD_DISTANCE_FACTOR = 0.1, 0.3
SPREAD_DISTANCE_SIZE_FACTOR = 0.1 SPREAD_DISTANCE_SIZE_FACTOR = 0.1
@ -20,123 +11,306 @@ FRONTLINE_CAS_FIGHTS_COUNT = 16, 24
FRONTLINE_CAS_GROUP_MIN = 1, 2 FRONTLINE_CAS_GROUP_MIN = 1, 2
FRONTLINE_CAS_PADDING = 12000 FRONTLINE_CAS_PADDING = 12000
RETREAT_DISTANCE = 20000
BREAKTHROUGH_OFFENSIVE_DISTANCE = 35000
AGGRESIVE_MOVE_DISTANCE = 16000
FIGHT_DISTANCE = 3500 FIGHT_DISTANCE = 3500
class GroundConflictGenerator:
class ArmorConflictGenerator: def __init__(self, mission: Mission, conflict: Conflict, game, player_planned_combat_groups, enemy_planned_combat_groups, player_stance):
def __init__(self, mission: Mission, conflict: Conflict):
self.m = mission self.m = mission
self.conflict = conflict self.conflict = conflict
self.enemy_planned_combat_groups = enemy_planned_combat_groups
self.player_planned_combat_groups = player_planned_combat_groups
self.player_stance = CombatStance(player_stance)
self.enemy_stance = CombatStance.AGGRESIVE if len(enemy_planned_combat_groups) > len(player_planned_combat_groups) else CombatStance.DEFENSIVE
self.game = game
def _group_point(self, point) -> Point: def _group_point(self, point) -> Point:
distance = randint( distance = randint(
int(self.conflict.size * SPREAD_DISTANCE_FACTOR[0]), int(self.conflict.size * SPREAD_DISTANCE_FACTOR[0]),
int(self.conflict.size * SPREAD_DISTANCE_FACTOR[1]), int(self.conflict.size * SPREAD_DISTANCE_FACTOR[1]),
) )
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, move_formation: PointAction = PointAction.OffRoad): def generate(self):
player_groups = []
enemy_groups = []
combat_width = self.conflict.distance/2
if combat_width > 500000:
combat_width = 500000
if combat_width < 35000:
combat_width = 35000
position = Conflict.frontline_position(self.game.theater, self.conflict.from_cp, self.conflict.to_cp)
# Create player groups at random position
for group in self.player_planned_combat_groups:
if group.role == CombatGroupRole.ARTILLERY:
distance_from_frontline = self.get_artilery_group_distance_from_frontline(group)
else:
distance_from_frontline = DISTANCE_FROM_FRONTLINE[group.role]
final_position = self.get_valid_position_for_group(position, True, combat_width, distance_from_frontline)
if final_position is not None:
g = self._generate_group(
side=self.m.country(self.game.player_country),
unit=group.units[0],
heading=self.conflict.heading+90,
count=len(group.units),
at=final_position)
g.set_skill(self.game.settings.player_skill)
player_groups.append((g,group))
self.gen_infantry_group_for_group(g, True, self.m.country(self.game.player_country), self.conflict.heading + 90)
# Create enemy groups at random position
for group in self.enemy_planned_combat_groups:
if group.role == CombatGroupRole.ARTILLERY:
distance_from_frontline = self.get_artilery_group_distance_from_frontline(group)
else:
distance_from_frontline = DISTANCE_FROM_FRONTLINE[group.role]
final_position = self.get_valid_position_for_group(position, False, combat_width, distance_from_frontline)
if final_position is not None:
g = self._generate_group(
side=self.m.country(self.game.enemy_country),
unit=group.units[0],
heading=self.conflict.heading - 90,
count=len(group.units),
at=final_position)
g.set_skill(self.game.settings.enemy_vehicle_skill)
enemy_groups.append((g, group))
self.gen_infantry_group_for_group(g, False, self.m.country(self.game.enemy_country), self.conflict.heading - 90)
# Plan combat actions for groups
self.plan_action_for_groups(self.player_stance, player_groups, enemy_groups, self.conflict.heading + 90, self.conflict.from_cp, self.conflict.to_cp)
self.plan_action_for_groups(self.enemy_stance, enemy_groups, player_groups, self.conflict.heading - 90, self.conflict.to_cp, self.conflict.from_cp)
def gen_infantry_group_for_group(self, group, is_player, side:Country, forward_heading):
infantry_position = group.points[0].position.random_point_within(250, 50)
if side == self.conflict.attackers_country: if side == self.conflict.attackers_country:
cp = self.conflict.from_cp cp = self.conflict.from_cp
else: else:
cp = self.conflict.to_cp cp = self.conflict.to_cp
for c in range(count): if is_player:
logging.info("armorgen: {} for {}".format(unit, side.id)) faction = self.game.player_name
group = self.m.vehicle_group( else:
side, faction = self.game.enemy_name
namegen.next_unit_name(side, cp.id, unit),
unit,
position=self._group_point(at),
group_size=1,
move_formation=move_formation)
vehicle: Vehicle = group.units[0] possible_infantry_units = db.find_infantry(faction)
if len(possible_infantry_units) == 0:
return
u = random.choice(possible_infantry_units)
self.m.vehicle_group(
side,
namegen.next_infantry_name(side, cp, u), u,
position=infantry_position,
group_size=1,
heading=forward_heading,
move_formation=PointAction.OffRoad)
for i in range(randint(3, 10)):
u = random.choice(possible_infantry_units)
position = infantry_position.random_point_within(55, 5)
self.m.vehicle_group(
side,
namegen.next_infantry_name(side, cp, u), u,
position=position,
group_size=1,
heading=forward_heading,
move_formation=PointAction.OffRoad)
def plan_action_for_groups(self, stance, ally_groups, enemy_groups, forward_heading, from_cp, to_cp):
for dcs_group, group in ally_groups:
if group.role == CombatGroupRole.ARTILLERY:
# Fire on any ennemy in range
target = self.get_artillery_target_in_range(dcs_group, group, enemy_groups)
if target is not None:
dcs_group.points[0].tasks.append(FireAtPoint(target, len(group.units) * 10, 100))
elif group.role in [CombatGroupRole.TANK, CombatGroupRole.IFV]:
if stance == CombatStance.AGGRESIVE:
# Attack nearest enemy if any
# Then move forward OR Attack enemy base if it is not too far away
target = self.find_nearest_enemy_group(dcs_group, enemy_groups)
if target is not None:
rand_offset = Point(random.randint(-50, 50), random.randint(-50, 50))
dcs_group.add_waypoint(target.points[0].position + rand_offset, PointAction.OffRoad)
dcs_group.points[1].tasks.append(AttackGroup(target.id))
if to_cp.position.distance_to_point(dcs_group.points[0].position) <= AGGRESIVE_MOVE_DISTANCE:
attack_point = to_cp.position.random_point_within(500, 0)
else:
attack_point = self.find_offensive_point(dcs_group, forward_heading, AGGRESIVE_MOVE_DISTANCE)
dcs_group.add_waypoint(attack_point, PointAction.OnRoad)
elif stance == CombatStance.BREAKTHROUGH:
# In breakthrough mode, the units will move forward
# If the enemy base is close enough, the units will attack the base
if to_cp.position.distance_to_point(
dcs_group.points[0].position) <= BREAKTHROUGH_OFFENSIVE_DISTANCE:
attack_point = to_cp.position.random_point_within(500, 0)
else:
attack_point = self.find_offensive_point(dcs_group, forward_heading, BREAKTHROUGH_OFFENSIVE_DISTANCE)
dcs_group.add_waypoint(attack_point, PointAction.OnRoad)
elif stance == CombatStance.ELIMINATION:
# In elimination mode, the units focus on destroying as much enemy groups as possible
targets = self.find_n_nearest_enemy_groups(dcs_group, enemy_groups, 3)
i = 1
for target in targets:
rand_offset = Point(random.randint(-50, 50), random.randint(-50, 50))
dcs_group.add_waypoint(target.points[0].position+rand_offset,PointAction.OffRoad)
dcs_group.points[i].tasks.append(AttackGroup(target.id))
i = i + 1
if to_cp.position.distance_to_point(dcs_group.points[0].position) <= AGGRESIVE_MOVE_DISTANCE:
attack_point = to_cp.position.random_point_within(500, 0)
dcs_group.add_waypoint(attack_point)
elif group.role in [CombatGroupRole.APC, CombatGroupRole.ATGM]:
if stance in [CombatStance.AGGRESIVE, CombatStance.BREAKTHROUGH, CombatStance.ELIMINATION]:
# APC & ATGM will never move too much forward, but will follow along any offensive
if to_cp.position.distance_to_point(dcs_group.points[0].position) <= AGGRESIVE_MOVE_DISTANCE:
attack_point = to_cp.position.random_point_within(500, 0)
else:
attack_point = self.find_offensive_point(dcs_group, forward_heading, AGGRESIVE_MOVE_DISTANCE)
dcs_group.add_waypoint(attack_point, PointAction.OnRoad)
if stance == CombatStance.RETREAT:
# In retreat mode, the units will fall back
# If the ally base is close enough, the units will even regroup there
if from_cp.position.distance_to_point(dcs_group.points[0].position) <= RETREAT_DISTANCE:
retreat_point = from_cp.position.random_point_within(500, 250)
else:
retreat_point = self.find_retreat_point(dcs_group, forward_heading)
reposition_point = retreat_point.point_from_heading(forward_heading, 10) # Another point to make the unit face the enemy
dcs_group.add_waypoint(retreat_point, PointAction.OnRoad)
dcs_group.add_waypoint(reposition_point, PointAction.OffRoad)
def find_retreat_point(self, dcs_group, frontline_heading):
"""
Find a point to retreat to
:param dcs_group: DCS mission group we are searching a retreat point for
:param frontline_heading: Heading of the frontline
:return: dcs.mapping.Point object with the desired position
"""
return dcs_group.points[0].position.point_from_heading(frontline_heading-180, RETREAT_DISTANCE)
def find_offensive_point(self, dcs_group, frontline_heading, distance):
"""
Find a point to attack
:param dcs_group: DCS mission group we are searching an attack point for
:param frontline_heading: Heading of the frontline
:param distance: Distance of the offensive (how far unit should move)
:return: dcs.mapping.Point object with the desired position
"""
return dcs_group.points[0].position.point_from_heading(frontline_heading, distance)
def find_n_nearest_enemy_groups(self, player_group, enemy_groups, n):
"""
Return the neaarest enemy group for the player group
@param group Group for which we should find the nearest ennemies
@param enemy_groups Potential enemy groups
@param n number of nearby groups to take
"""
targets = []
sorted_list = sorted(enemy_groups, key=lambda group: player_group.points[0].position.distance_to_point(group[0].points[0].position))
for i in range(n):
if len(sorted_list) <= i:
break
else:
targets.append(sorted_list[i][0])
return targets
def find_nearest_enemy_group(self, player_group, enemy_groups):
"""
Search the enemy groups for a potential target suitable to armored assault
@param group Group for which we should find the nearest ennemy
@param enemy_groups Potential enemy groups
"""
min_distance = 99999999
target = None
for dcs_group, group in enemy_groups:
dist = player_group.points[0].position.distance_to_point(dcs_group.points[0].position)
if dist < min_distance:
min_distance = dist
target = dcs_group
return target
def get_artillery_target_in_range(self, dcs_group, group, enemy_groups):
"""
Search the enemy groups for a potential target suitable to an artillery unit
"""
rng = group.units[0].threat_range
if len(enemy_groups) == 0:
return None
for o in range(10):
potential_target = random.choice(enemy_groups)[0]
distance_to_target = dcs_group.points[0].position.distance_to_point(potential_target.points[0].position)
if distance_to_target < rng:
return potential_target.points[0].position
return None
def get_artilery_group_distance_from_frontline(self, group):
"""
For artilery group, decide the distance from frontline with the range of the unit
"""
rg = group.units[0].threat_range - 7500
if rg > DISTANCE_FROM_FRONTLINE[CombatGroupRole.ARTILLERY]:
rg = DISTANCE_FROM_FRONTLINE[CombatGroupRole.ARTILLERY]
if rg < DISTANCE_FROM_FRONTLINE[CombatGroupRole.TANK]:
rg = DISTANCE_FROM_FRONTLINE[CombatGroupRole.TANK] + 100
return rg
def get_valid_position_for_group(self, conflict_position, isplayer, combat_width, distance_from_frontline):
i = 0
while i < 25: # 25 attempt for valid position
heading_diff = -90 if isplayer else 90
shifted = conflict_position[0].point_from_heading(self.conflict.heading,
random.randint(-combat_width / 2, combat_width / 2))
final_position = shifted.point_from_heading(self.conflict.heading + heading_diff, distance_from_frontline)
if self.conflict.theater.is_on_land(final_position):
return final_position
else:
i = i + 1
continue
return None
def _generate_group(self, side: Country, unit: VehicleType, count: int, at: Point, move_formation: PointAction = PointAction.OffRoad, heading=0):
if side == self.conflict.attackers_country:
cp = self.conflict.from_cp
else:
cp = self.conflict.to_cp
logging.info("armorgen: {} for {}".format(unit, side.id))
group = self.m.vehicle_group(
side,
namegen.next_unit_name(side, cp.id, unit), unit,
position=self._group_point(at),
group_size=count,
heading=heading,
move_formation=move_formation)
for c in range(count):
vehicle: Vehicle = group.units[c]
vehicle.player_can_drive = True vehicle.player_can_drive = True
if not to: return group
to = self.conflict.position.point_from_heading(0, 500)
wayp = group.add_waypoint(self._group_point(to), move_formation=move_formation)
wayp.tasks = []
def _generate_fight_at(self, attackers: db.ArmorDict, defenders: db.ArmorDict, position: Point):
print(attackers)
print(defenders)
if attackers:
attack_pos = position.point_from_heading(self.conflict.heading - 90, FIGHT_DISTANCE)
attack_dest = position.point_from_heading(self.conflict.heading + 90, FIGHT_DISTANCE * 2)
for type, count in attackers.items():
self._generate_group(
side=self.conflict.attackers_country,
unit=type,
count=count,
at=attack_pos,
to=attack_dest,
)
if defenders:
def_pos = position.point_from_heading(self.conflict.heading + 90, FIGHT_DISTANCE)
def_dest = position.point_from_heading(self.conflict.heading - 90, FIGHT_DISTANCE * 2)
for type, count in defenders.items():
self._generate_group(
side=self.conflict.defenders_country,
unit=type,
count=count,
at=def_pos,
to=def_dest,
)
def generate(self, attackers: db.ArmorDict, defenders: db.ArmorDict):
for type, count in attackers.items():
self._generate_group(
side=self.conflict.attackers_country,
unit=type,
count=count,
at=self.conflict.ground_attackers_location)
for type, count in defenders.items():
self._generate_group(
side=self.conflict.defenders_country,
unit=type,
count=count,
at=self.conflict.ground_defenders_location)
def generate_vec(self, attackers: db.ArmorDict, defenders: db.ArmorDict):
fights_count = randint(*FRONTLINE_CAS_FIGHTS_COUNT)
single_fight_defenders_count = min(int(sum(defenders.values()) / fights_count), randint(*FRONTLINE_CAS_GROUP_MIN))
defender_groups = list(db.unitdict_split(defenders, single_fight_defenders_count))
single_fight_attackers_count = min(int(sum(attackers.values()) / len(defender_groups)), randint(*FRONTLINE_CAS_GROUP_MIN))
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):
position = self.conflict.position.point_from_heading(self.conflict.heading,
random.randint(0, self.conflict.distance))
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_country,
unit=type,
count=count,
at=self.conflict.ground_defenders_location,
to=self.conflict.position,
move_formation=PointAction.OnRoad)
def generate_passengers(self, count: int):
unit_type = random.choice(db.find_unittype(Nothing, self.conflict.attackers_side.name))
self.m.vehicle_group(
country=self.conflict.attackers_side,
name=namegen.next_unit_name(self.conflict.attackers_side, unit_type),
_type=unit_type,
position=self.conflict.ground_attackers_location,
group_size=count
)

View File

@ -27,7 +27,7 @@ STRIKE_AIR_DEFENDERS_DISTANCE = 25000
CAP_CAS_DISTANCE = 10000, 120000 CAP_CAS_DISTANCE = 10000, 120000
GROUND_INTERCEPT_SPREAD = 5000 GROUND_INTERCEPT_SPREAD = 5000
GROUND_DISTANCE_FACTOR = 1 GROUND_DISTANCE_FACTOR = 1.4
GROUND_DISTANCE = 2000 GROUND_DISTANCE = 2000
GROUND_ATTACK_DISTANCE = 25000, 13000 GROUND_ATTACK_DISTANCE = 25000, 13000

View File

@ -0,0 +1,273 @@
import random
from enum import Enum
from dcs.vehicles import *
from gen.ground_forces.combat_stance import CombatStance
from theater import ControlPoint
TYPE_TANKS = [
Armor.MBT_T_55,
Armor.MBT_T_72B,
Armor.MBT_T_80U,
Armor.MBT_T_90,
Armor.MBT_Leopard_2,
Armor.MBT_Leopard_1A3,
Armor.MBT_Leclerc,
Armor.MBT_Challenger_II,
Armor.MBT_M1A2_Abrams,
Armor.MBT_M60A3_Patton,
Armor.MBT_Merkava_Mk__4,
Armor.MT_Pz_Kpfw_V_Panther_Ausf_G,
Armor.MT_Pz_Kpfw_IV_Ausf_H,
Armor.HT_Pz_Kpfw_VI_Tiger_I,
Armor.HT_Pz_Kpfw_VI_Ausf__B__Tiger_II,
Armor.MT_M4_Sherman,
Armor.MT_M4A4_Sherman_Firefly,
Armor.StuG_IV,
Armor.ZTZ_96B
]
TYPE_ATGM = [
Armor.ATGM_M1045_HMMWV_TOW,
Armor.ATGM_M1134_Stryker,
Armor.IFV_BMP_2,
]
TYPE_IFV = [
Armor.IFV_BMP_3,
Armor.IFV_BMP_2,
Armor.IFV_BMP_1,
Armor.IFV_Marder,
Armor.IFV_MCV_80,
Armor.IFV_LAV_25,
Armor.IFV_Sd_Kfz_234_2_Puma,
Armor.IFV_M2A2_Bradley,
Armor.IFV_BMD_1,
Armor.ZBD_04A,
Armor.APC_Sd_Kfz_251,
Armor.IFV_Sd_Kfz_234_2_Puma,
]
TYPE_APC = [
Armor.APC_M1043_HMMWV_Armament,
Armor.APC_M1126_Stryker_ICV,
Armor.APC_M113,
Armor.APC_BTR_80,
Armor.APC_MTLB,
Armor.APC_M2A1,
Armor.APC_Cobra,
Armor.APC_Sd_Kfz_251,
Armor.APC_AAV_7,
Armor.TPz_Fuchs,
Armor.ARV_BRDM_2,
Armor.ARV_BTR_RD,
Armor.ARV_MTLB_U_BOMAN,
Armor.M30_Cargo_Carrier,
Armor.APC_M2A1,
]
TYPE_ARTILLERY = [
Artillery.MLRS_9A52_Smerch,
Artillery.SPH_2S1_Gvozdika,
Artillery.SPH_2S3_Akatsia,
Artillery.MLRS_BM_21_Grad,
Artillery.MLRS_9K57_Uragan_BM_27,
Artillery.SPH_M109_Paladin,
Artillery.MLRS_M270,
Artillery.SPH_2S9_Nona,
Artillery.SpGH_Dana,
Artillery.SPH_2S19_Msta,
Artillery.M12_GMC,
Artillery.MLRS_FDDM,
Artillery.Sturmpanzer_IV_Brummbär
]
TYPE_LOGI = [
Unarmed.Transport_M818,
Unarmed.Transport_KAMAZ_43101,
Unarmed.Transport_Ural_375,
Unarmed.Transport_GAZ_66,
Unarmed.Transport_GAZ_3307,
Unarmed.Transport_GAZ_3308,
Unarmed.Transport_Ural_4320_31_Armored,
Unarmed.Transport_Ural_4320T,
Unarmed.Blitz_3_6_6700A,
Unarmed.Kübelwagen_82,
Unarmed.Sd_Kfz_7,
Unarmed.Sd_Kfz_2,
Unarmed.Willys_MB,
Unarmed.Land_Rover_109_S3,
Unarmed.Land_Rover_101_FC,
]
TYPE_INFANTRY = [
Infantry.Infantry_Soldier_Insurgents,
Infantry.Soldier_AK,
Infantry.Infantry_M1_Garand,
Infantry.Infantry_Mauser_98,
Infantry.Infantry_SMLE_No_4_Mk_1,
Infantry.Georgian_soldier_with_M4,
Infantry.Infantry_Soldier_Rus,
Infantry.Paratrooper_AKS,
Infantry.Paratrooper_RPG_16,
Infantry.Soldier_M249,
Infantry.Infantry_M4,
Infantry.Soldier_RPG,
]
MAX_COMBAT_GROUP_PER_CP = 10
class CombatGroupRole(Enum):
TANK = 1
APC = 2
IFV = 3
ARTILLERY = 4
SHORAD = 5
LOGI = 6
INFANTRY = 7
ATGM = 8
DISTANCE_FROM_FRONTLINE = {
CombatGroupRole.TANK:2800,
CombatGroupRole.APC:7000,
CombatGroupRole.IFV:3000,
CombatGroupRole.ARTILLERY:14000,
CombatGroupRole.SHORAD:12000,
CombatGroupRole.LOGI:18000,
CombatGroupRole.INFANTRY:2800,
CombatGroupRole.ATGM:5500
}
GROUP_SIZES_BY_COMBAT_STANCE = {
CombatStance.DEFENSIVE: [2, 4, 6],
CombatStance.AGGRESIVE: [2, 4, 6],
CombatStance.RETREAT: [2, 4, 6, 8],
CombatStance.BREAKTHROUGH: [4, 6, 6, 8],
CombatStance.ELIMINATION: [2, 4, 4, 4, 6],
CombatStance.AMBUSH: [1, 1, 2, 2, 2, 2, 4]
}
class CombatGroup:
def __init__(self, role:CombatGroupRole):
self.units = []
self.role = role
self.assigned_enemy_cp = None
def __str__(self):
s = ""
s += "ROLE : " + str(self.role) + "\n"
if len(self.units) > 0:
s += "UNITS " + self.units[0].name + " * " + str(len(self.units))
return s
class GroundPlanner:
cp = None
combat_groups_dict = {}
connected_enemy_cp = []
tank_groups = []
apc_group = []
ifv_group = []
art_group = []
shorad_groups = []
logi_groups = []
def __init__(self, cp:ControlPoint, game):
self.cp = cp
self.game = game
self.connected_enemy_cp = [cp for cp in self.cp.connected_points if cp.captured != self.cp.captured]
self.tank_groups = []
self.apc_group = []
self.ifv_group = []
self.art_group = []
self.atgm_group = []
self.logi_groups = []
self.shorad_groups = []
self.units_per_cp = {}
for cp in self.connected_enemy_cp:
self.units_per_cp[cp.id] = []
self.reserve = []
def plan_groundwar(self):
if hasattr(self.cp, 'stance'):
group_size_choice = GROUP_SIZES_BY_COMBAT_STANCE[self.cp.stance]
else:
self.cp.stance = CombatStance.DEFENSIVE
group_size_choice = GROUP_SIZES_BY_COMBAT_STANCE[CombatStance.DEFENSIVE]
# Create combat groups and assign them randomly to each enemy CP
for key in self.cp.base.armor.keys():
role = None
collection = None
if key in TYPE_TANKS:
collection = self.tank_groups
role = CombatGroupRole.TANK
elif key in TYPE_APC:
collection = self.apc_group
role = CombatGroupRole.APC
elif key in TYPE_ARTILLERY:
collection = self.art_group
role = CombatGroupRole.ARTILLERY
elif key in TYPE_IFV:
collection = self.ifv_group
role = CombatGroupRole.IFV
elif key in TYPE_LOGI:
collection = self.logi_groups
role = CombatGroupRole.LOGI
elif key in TYPE_ATGM:
collection = self.atgm_group
role = CombatGroupRole.ATGM
else:
print("Warning unit type not handled by ground generator")
print(key)
continue
available = self.cp.base.armor[key]
while available > 0:
n = random.choice(group_size_choice)
if n > available:
if available >= 2:
n = 2
else:
n = 1
available -= n
group = CombatGroup(role)
if len(self.connected_enemy_cp) > 0:
enemy_cp = random.choice(self.connected_enemy_cp).id
self.units_per_cp[enemy_cp].append(group)
group.assigned_enemy_cp = enemy_cp
else:
self.reserve.append(group)
group.assigned_enemy_cp = "__reserve__"
for i in range(n):
group.units.append(key)
collection.append(group)
print("------------------")
print("Ground Planner : ")
print(self.cp.name)
print("------------------")
for key in self.units_per_cp.keys():
print("For : #" + str(key))
for group in self.units_per_cp[key]:
print(str(group))

View File

@ -0,0 +1,11 @@
from enum import Enum
class CombatStance(Enum):
DEFENSIVE = 0 # Unit will adopt defensive stance with medium group of units
AGGRESIVE = 1 # Unit will attempt to make progress with medium sized group of units
RETREAT = 2 # Unit will retreat
BREAKTHROUGH = 3 # Unit will attempt a breakthrough, rushing forward very aggresively with big group of armored units, and even less armored units will move aggresively
ELIMINATION = 4 # Unit will progress aggresively toward anemy units, attempting to eliminate the ennemy force
AMBUSH = 5 # Units will adopt a defensive stance a bit different from 'DEFENSIVE', ATGM & INFANTRY with RPG will be located on frontline with the armored units. (The groups of units will be smaller)

View File

@ -96,6 +96,8 @@ class GroundObjectsGenerator:
ship.heading = u.heading ship.heading = u.heading
sg.add_unit(ship) sg.add_unit(ship)
sg.add_waypoint(sg.points[0].position.point_from_heading(g.units[0].heading, 100000))
else: else:
if ground_object.dcs_identifier in warehouse_map: if ground_object.dcs_identifier in warehouse_map:
static_type = warehouse_map[ground_object.dcs_identifier] static_type = warehouse_map[ground_object.dcs_identifier]

View File

@ -8,6 +8,10 @@ class NameGenerator:
self.number += 1 self.number += 1
return "unit|{}|{}|{}|{}|".format(country.id, self.number, parent_base_id, db.unit_type_name(unit_type)) return "unit|{}|{}|{}|{}|".format(country.id, self.number, parent_base_id, db.unit_type_name(unit_type))
def next_infantry_name(self, country, parent_base_id, unit_type):
self.number += 1
return "infantry|{}|{}|{}|{}|".format(country.id, self.number, parent_base_id, db.unit_type_name(unit_type))
def next_basedefense_name(self): def next_basedefense_name(self):
return "basedefense_aa|0|0|" return "basedefense_aa|0|0|"

View File

@ -126,6 +126,8 @@ class VisualGenerator:
break break
def _generate_stub_planes(self): def _generate_stub_planes(self):
pass
"""
mission_units = set() mission_units = set()
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():
@ -134,7 +136,7 @@ class VisualGenerator:
mission_units.add(db.unit_type_of(unit)) mission_units.add(db.unit_type_of(unit))
for unit_type in mission_units: for unit_type in mission_units:
self.mission.static_group(self.mission.country(self.game.player_country), "a", unit_type, Point(0, 300000), hidden=True) self.mission.static_group(self.mission.country(self.game.player_country), "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

View File

@ -4,8 +4,7 @@ from typing import Dict
from PySide2.QtGui import QColor, QFont, QPixmap from PySide2.QtGui import QColor, QFont, QPixmap
from game.event import BaseAttackEvent, FrontlinePatrolEvent, FrontlineAttackEvent, InfantryTransportEvent, \ from game.event import UnitsDeliveryEvent, FrontlineAttackEvent
InsurgentAttackEvent, ConvoyStrikeEvent, InterceptEvent, NavalInterceptEvent, StrikeEvent, UnitsDeliveryEvent
from theater.theatergroundobject import CATEGORY_MAP from theater.theatergroundobject import CATEGORY_MAP
URLS : Dict[str, str] = { URLS : Dict[str, str] = {
@ -85,15 +84,9 @@ EVENT_ICONS: Dict[str, QPixmap] = {}
def load_event_icons(): def load_event_icons():
for category, image in {BaseAttackEvent: "capture", for category, image in {
FrontlinePatrolEvent: "attack", "strike": "strike",
FrontlineAttackEvent: "attack", FrontlineAttackEvent: "attack",
InfantryTransportEvent: "infantry",
InsurgentAttackEvent: "insurgent_attack",
ConvoyStrikeEvent: "convoy",
InterceptEvent: "air_intercept",
NavalInterceptEvent: "naval_intercept",
StrikeEvent: "strike",
UnitsDeliveryEvent: "delivery"}.items(): UnitsDeliveryEvent: "delivery"}.items():
EVENT_ICONS[category] = QPixmap("./resources/ui/events/" + image + ".png") EVENT_ICONS[category] = QPixmap("./resources/ui/events/" + image + ".png")

View File

@ -11,6 +11,7 @@ class QBaseInformation(QGroupBox):
super(QBaseInformation, self).__init__("Base defenses") super(QBaseInformation, self).__init__("Base defenses")
self.cp = cp self.cp = cp
self.airport = airport self.airport = airport
self.setMinimumWidth(500)
self.init_ui() self.init_ui()
def init_ui(self): def init_ui(self):

View File

@ -8,8 +8,7 @@ from dcs import Point
import qt_ui.uiconstants as CONST import qt_ui.uiconstants as CONST
from game import Game, db from game import Game, db
from game.event import InfantryTransportEvent, StrikeEvent, BaseAttackEvent, UnitsDeliveryEvent, Event, \ from game.event import UnitsDeliveryEvent, Event, ControlPointType
FrontlineAttackEvent, FrontlinePatrolEvent, ConvoyStrikeEvent, ControlPointType
from gen import Conflict from gen import Conflict
from qt_ui.widgets.map.QLiberationScene import QLiberationScene from qt_ui.widgets.map.QLiberationScene import QLiberationScene
from qt_ui.widgets.map.QMapControlPoint import QMapControlPoint from qt_ui.widgets.map.QMapControlPoint import QMapControlPoint

View File

@ -5,7 +5,8 @@ from PySide2.QtWidgets import QGraphicsRectItem, QGraphicsSceneHoverEvent, QGrap
import qt_ui.uiconstants as CONST import qt_ui.uiconstants as CONST
from game import Game from game import Game
from qt_ui.windows.QBaseMenu import QBaseMenu from qt_ui.windows.basemenu.QBaseMenu import QBaseMenu
from qt_ui.windows.basemenu.QBaseMenu2 import QBaseMenu2
from theater import ControlPoint, db from theater import ControlPoint, db
@ -98,5 +99,5 @@ class QMapControlPoint(QGraphicsRectItem):
return self.model.captured and CONST.COLORS["bright_red"] or CONST.COLORS["dark_blue"] return self.model.captured and CONST.COLORS["bright_red"] or CONST.COLORS["dark_blue"]
def openBaseMenu(self): def openBaseMenu(self):
self.baseMenu = QBaseMenu(self.window(), self.model, self.game) self.baseMenu = QBaseMenu2(self.window(), self.model, self.game)
self.baseMenu.show() self.baseMenu.show()

View File

@ -5,7 +5,7 @@ from PySide2.QtWidgets import QHBoxLayout, QLabel, QWidget, QDialog, QVBoxLayout
QSpinBox, QPushButton, QMessageBox, QComboBox QSpinBox, QPushButton, QMessageBox, QComboBox
from pip._internal.utils import typing from pip._internal.utils import typing
from game.game import AWACS_BUDGET_COST, PinpointStrike, db, Event, FrontlineAttackEvent, FrontlinePatrolEvent, Task, \ from game.game import AWACS_BUDGET_COST, PinpointStrike, db, Event, FrontlineAttackEvent, Task, \
UnitType UnitType
from qt_ui.windows.QWaitingForMissionResultWindow import QWaitingForMissionResultWindow from qt_ui.windows.QWaitingForMissionResultWindow import QWaitingForMissionResultWindow
from userdata.persistency import base_path from userdata.persistency import base_path
@ -234,14 +234,14 @@ class QBriefingWindow(QDialog):
return return
if self.game.is_player_attack(self.gameEvent): if self.game.is_player_attack(self.gameEvent):
if isinstance(self.gameEvent, FrontlineAttackEvent) or isinstance(self.gameEvent, FrontlinePatrolEvent): if isinstance(self.gameEvent, FrontlineAttackEvent):
if self.base.total_armor == 0: if self.base.total_armor == 0:
self.showErrorMessage("No ground vehicles available to attack!") self.showErrorMessage("No ground vehicles available to attack!")
return return
self.gameEvent.player_attacking(flights) self.gameEvent.player_attacking(flights)
else: else:
if isinstance(self.gameEvent, FrontlineAttackEvent) or isinstance(self.gameEvent, FrontlinePatrolEvent): if isinstance(self.gameEvent, FrontlineAttackEvent):
if self.gameEvent.to_cp.base.total_armor == 0: if self.gameEvent.to_cp.base.total_armor == 0:
self.showErrorMessage("No ground vehicles available to defend!") self.showErrorMessage("No ground vehicles available to defend!")
return return

View File

@ -0,0 +1,73 @@
import traceback
from PySide2.QtCore import Qt
from PySide2.QtGui import QCloseEvent
from PySide2.QtWidgets import QHBoxLayout, QLabel, QWidget, QDialog, QVBoxLayout, QGridLayout, QPushButton, \
QGroupBox, QSizePolicy, QSpacerItem
from dcs.unittype import UnitType
from game.event import UnitsDeliveryEvent, ControlPointType
from qt_ui.widgets.QBudgetBox import QBudgetBox
from qt_ui.widgets.base.QAirportInformation import QAirportInformation
from qt_ui.widgets.base.QBaseInformation import QBaseInformation
from qt_ui.windows.basemenu.QBaseMenuTabs import QBaseMenuTabs
from qt_ui.windows.mission.QPlannedFlightsView import QPlannedFlightsView
from qt_ui.windows.GameUpdateSignal import GameUpdateSignal
from theater import ControlPoint, CAP, Embarking, CAS, PinpointStrike, db
from game import Game
class QBaseMenu2(QDialog):
def __init__(self, parent, cp: ControlPoint, game: Game):
super(QBaseMenu2, self).__init__(parent)
# Attrs
self.cp = cp
self.game = game
self.is_carrier = self.cp.cptype in [ControlPointType.AIRCRAFT_CARRIER_GROUP, ControlPointType.LHA_GROUP]
# Widgets
self.qbase_menu_tab = QBaseMenuTabs(cp, game)
try:
self.airport = game.theater.terrain.airport_by_id(self.cp.id)
except:
self.airport = None
if self.cp.captured:
self.deliveryEvent = None
self.setWindowFlags(Qt.WindowStaysOnTopHint)
self.setMinimumSize(300, 200)
self.setModal(True)
self.initUi()
def initUi(self):
self.setWindowTitle(self.cp.name)
self.topLayoutWidget = QWidget()
self.topLayout = QHBoxLayout()
self.topLayoutWidget = QWidget()
self.topLayout = QHBoxLayout()
title = QLabel("<b>" + self.cp.name + "</b>")
title.setAlignment(Qt.AlignLeft | Qt.AlignTop)
title.setProperty("style", "base-title")
unitsPower = QLabel("{} / {} / Runway : {}".format(self.cp.base.total_planes, self.cp.base.total_armor,
"Available" if self.cp.has_runway() else "Unavailable"))
self.topLayout.addWidget(title)
self.topLayout.addWidget(unitsPower)
self.topLayout.setAlignment(Qt.AlignTop)
self.topLayoutWidget.setProperty("style", "baseMenuHeader")
self.topLayoutWidget.setLayout(self.topLayout)
self.mainLayout = QGridLayout()
self.mainLayout.addWidget(self.topLayoutWidget, 0, 0)
self.mainLayout.addWidget(self.qbase_menu_tab, 1, 0)
self.setLayout(self.mainLayout)
def closeEvent(self, closeEvent:QCloseEvent):
GameUpdateSignal.get_instance().updateGame(self.game)

View File

@ -0,0 +1,37 @@
from PySide2.QtWidgets import QTabWidget, QFrame, QGridLayout, QLabel
from game import Game
from qt_ui.windows.basemenu.airfield.QAirfieldCommand import QAirfieldCommand
from qt_ui.windows.basemenu.base_defenses.QBaseDefensesHQ import QBaseDefensesHQ
from qt_ui.windows.basemenu.ground_forces.QGroundForcesHQ import QGroundForcesHQ
from qt_ui.windows.basemenu.intel.QIntelInfo import QIntelInfo
from theater import ControlPoint
class QBaseMenuTabs(QTabWidget):
def __init__(self, cp: ControlPoint, game: Game):
super(QBaseMenuTabs, self).__init__()
self.cp = cp
if cp:
if not cp.captured:
self.intel = QIntelInfo(cp, game)
self.addTab(self.intel, "Intel")
else:
if cp.has_runway():
self.airfield_command = QAirfieldCommand(cp, game)
self.addTab(self.airfield_command, "Airfield Command")
if not cp.is_carrier:
self.ground_forces_hq = QGroundForcesHQ(cp, game)
self.addTab(self.ground_forces_hq, "Ground Forces HQ")
self.base_defenses_hq = QBaseDefensesHQ(cp, game)
self.addTab(self.base_defenses_hq, "Base Defenses")
else:
tabError = QFrame()
l = QGridLayout()
l.addWidget(QLabel("No Control Point"))
tabError.setLayout(l)
self.addTab(tabError, "No Control Point")

View File

@ -0,0 +1,92 @@
from PySide2.QtWidgets import QLabel, QPushButton, \
QSizePolicy, QSpacerItem
from dcs.unittype import UnitType
from theater import db
class QRecruitBehaviour:
game = None
cp = None
deliveryEvent = None
existing_units_labels = None
bought_amount_labels = None
def __init__(self):
self.bought_amount_labels = {}
self.existing_units_labels = {}
def add_purchase_row(self, unit_type, layout, row):
existing_units = self.cp.base.total_units_of_type(unit_type)
scheduled_units = self.deliveryEvent.units.get(unit_type, 0)
unitName = QLabel("<b>" + db.unit_type_name(unit_type) + "</b>")
unitName.setSizePolicy(QSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding))
existing_units = QLabel(str(existing_units))
existing_units.setSizePolicy(QSizePolicy(QSizePolicy.Fixed, QSizePolicy.Fixed))
amount_bought = QLabel("[{}]".format(str(scheduled_units)))
amount_bought.setSizePolicy(QSizePolicy(QSizePolicy.Fixed, QSizePolicy.Fixed))
self.existing_units_labels[unit_type] = existing_units
self.bought_amount_labels[unit_type] = amount_bought
price = QLabel("{}m".format(db.PRICES[unit_type]))
price.setSizePolicy(QSizePolicy(QSizePolicy.Fixed, QSizePolicy.Fixed))
buy = QPushButton("+")
buy.setProperty("style", "btn-success")
buy.setMinimumSize(24, 24)
buy.clicked.connect(lambda: self.buy(unit_type))
buy.setSizePolicy(QSizePolicy(QSizePolicy.Fixed, QSizePolicy.Fixed))
sell = QPushButton("-")
sell.setProperty("style", "btn-danger")
sell.setMinimumSize(24, 24)
sell.setSizePolicy(QSizePolicy(QSizePolicy.Fixed, QSizePolicy.Fixed))
sell.clicked.connect(lambda: self.sell(unit_type))
layout.addWidget(unitName, row, 0)
layout.addItem(QSpacerItem(20, 0, QSizePolicy.Minimum, QSizePolicy.Minimum), row, 1)
layout.addWidget(existing_units, row, 2)
layout.addWidget(amount_bought, row, 3)
layout.addWidget(price, row, 4)
layout.addItem(QSpacerItem(20, 0, QSizePolicy.Minimum, QSizePolicy.Minimum), row, 5)
layout.addWidget(buy, row, 6)
layout.addWidget(sell, row, 7)
return row + 1
def _update_count_label(self, unit_type: UnitType):
self.bought_amount_labels[unit_type].setText("[{}]".format(
unit_type in self.deliveryEvent.units and "{}".format(self.deliveryEvent.units[unit_type]) or "0"
))
self.existing_units_labels[unit_type].setText("{}".format(
self.cp.base.total_units_of_type(unit_type)
))
def buy(self, unit_type):
price = db.PRICES[unit_type]
if self.game.budget >= price:
self.deliveryEvent.deliver({unit_type: 1})
self.game.budget -= price
self._update_count_label(unit_type)
def sell(self, unit_type):
if self.deliveryEvent.units.get(unit_type, 0) > 0:
price = db.PRICES[unit_type]
self.game.budget += price
self.deliveryEvent.units[unit_type] = self.deliveryEvent.units[unit_type] - 1
if self.deliveryEvent.units[unit_type] == 0:
del self.deliveryEvent.units[unit_type]
elif self.cp.base.total_units_of_type(unit_type) > 0:
price = db.PRICES[unit_type]
self.game.budget += price
self.cp.base.commit_losses({unit_type: 1})
self._update_count_label(unit_type)

View File

@ -0,0 +1,63 @@
import traceback
from PySide2.QtCore import Qt
from PySide2.QtGui import QCloseEvent
from PySide2.QtWidgets import QHBoxLayout, QLabel, QWidget, QDialog, QVBoxLayout, QGridLayout, QPushButton, \
QGroupBox, QSizePolicy, QSpacerItem, QFrame
from dcs.unittype import UnitType
from game.event import UnitsDeliveryEvent, ControlPointType
from qt_ui.widgets.QBudgetBox import QBudgetBox
from qt_ui.widgets.base.QAirportInformation import QAirportInformation
from qt_ui.widgets.base.QBaseInformation import QBaseInformation
from qt_ui.windows.basemenu.QRecruitBehaviour import QRecruitBehaviour
from qt_ui.windows.mission.QPlannedFlightsView import QPlannedFlightsView
from qt_ui.windows.GameUpdateSignal import GameUpdateSignal
from theater import ControlPoint, CAP, Embarking, CAS, PinpointStrike, db
from game import Game
class QAircraftRecruitmentMenu(QGroupBox, QRecruitBehaviour):
def __init__(self, cp:ControlPoint, game:Game):
QGroupBox.__init__(self, "Recruitment")
self.cp = cp
self.game = game
self.bought_amount_labels = {}
self.existing_units_labels = {}
for event in self.game.events:
if event.__class__ == UnitsDeliveryEvent and event.from_cp == self.cp:
self.deliveryEvent = event
if not self.deliveryEvent:
self.deliveryEvent = self.game.units_delivery_event(self.cp)
self.init_ui()
def init_ui(self):
layout = QVBoxLayout()
units = {
CAP: db.find_unittype(CAP, self.game.player_name),
CAS: db.find_unittype(CAS, self.game.player_name),
}
task_box_layout = QGridLayout()
row = 0
for task_type in units.keys():
units_column = list(set(units[task_type]))
if len(units_column) == 0: continue
units_column.sort(key=lambda x: db.PRICES[x])
for unit_type in units_column:
if self.cp.is_carrier and not unit_type in db.CARRIER_CAPABLE:
continue
row = self.add_purchase_row(unit_type, task_box_layout, row)
stretch = QVBoxLayout()
stretch.addStretch()
task_box_layout.addLayout(stretch, row, 0)
layout.addLayout(task_box_layout)
layout.addStretch()
self.setLayout(layout)

View File

@ -0,0 +1,31 @@
from PySide2.QtWidgets import QFrame, QGridLayout, QLabel, QHBoxLayout, QGroupBox, QVBoxLayout
from game import Game
from qt_ui.widgets.base.QAirportInformation import QAirportInformation
from qt_ui.windows.basemenu.airfield.QAircraftRecruitmentMenu import QAircraftRecruitmentMenu
from qt_ui.windows.mission.QPlannedFlightsView import QPlannedFlightsView
from theater import ControlPoint
class QAirfieldCommand(QFrame):
def __init__(self, cp:ControlPoint, game:Game):
super(QAirfieldCommand, self).__init__()
self.cp = cp
self.game = game
self.init_ui()
def init_ui(self):
layout = QGridLayout()
layout.addWidget(QAircraftRecruitmentMenu(self.cp, self.game), 0, 0)
try:
planned = QGroupBox("Planned Flights")
planned_layout = QVBoxLayout()
planned_layout.addWidget(QPlannedFlightsView(self.game.planners[self.cp.id]))
planned.setLayout(planned_layout)
layout.addWidget(planned, 0, 1)
except:
pass
#layout.addWidget(QAirportInformation(self.cp, self.game.theater.terrain.airport_by_id(self.cp.id)), 0, 2)
self.setLayout(layout)

View File

@ -0,0 +1,20 @@
from PySide2.QtWidgets import QFrame, QGridLayout, QLabel
from game import Game
from qt_ui.widgets.base.QBaseInformation import QBaseInformation
from theater import ControlPoint
class QBaseDefensesHQ(QFrame):
def __init__(self, cp:ControlPoint, game:Game):
super(QBaseDefensesHQ, self).__init__()
self.cp = cp
self.game = game
self.init_ui()
def init_ui(self):
airport = self.game.theater.terrain.airport_by_id(self.cp.id)
layout = QGridLayout()
layout.addWidget(QBaseInformation(self.cp, airport))
self.setLayout(layout)

View File

@ -0,0 +1,49 @@
from PySide2.QtWidgets import QVBoxLayout, QGridLayout, QGroupBox
from game import Game
from game.event import UnitsDeliveryEvent
from qt_ui.windows.basemenu.QRecruitBehaviour import QRecruitBehaviour
from theater import ControlPoint, PinpointStrike, db
class QArmorRecruitmentMenu(QGroupBox, QRecruitBehaviour):
def __init__(self, cp:ControlPoint, game:Game):
QGroupBox.__init__(self, "Recruitment")
self.cp = cp
self.game = game
self.bought_amount_labels = {}
self.existing_units_labels = {}
for event in self.game.events:
if event.__class__ == UnitsDeliveryEvent and event.from_cp == self.cp:
self.deliveryEvent = event
if not self.deliveryEvent:
self.deliveryEvent = self.game.units_delivery_event(self.cp)
self.init_ui()
def init_ui(self):
layout = QVBoxLayout()
units = {
PinpointStrike: db.find_unittype(PinpointStrike, self.game.player_name),
}
task_box_layout = QGridLayout()
row = 0
for task_type in units.keys():
units_column = list(set(units[task_type]))
if len(units_column) == 0: continue
units_column.sort(key=lambda x: db.PRICES[x])
for unit_type in units_column:
row = self.add_purchase_row(unit_type, task_box_layout, row)
stretch = QVBoxLayout()
stretch.addStretch()
task_box_layout.addLayout(stretch, row, 0)
layout.addLayout(task_box_layout)
layout.addStretch()
self.setLayout(layout)

View File

@ -0,0 +1,21 @@
from PySide2.QtWidgets import QFrame, QGridLayout
from game import Game
from qt_ui.windows.basemenu.ground_forces.QArmorRecruitmentMenu import QArmorRecruitmentMenu
from qt_ui.windows.basemenu.ground_forces.QGroundForcesStrategy import QGroundForcesStrategy
from theater import ControlPoint
class QGroundForcesHQ(QFrame):
def __init__(self, cp:ControlPoint, game:Game):
super(QGroundForcesHQ, self).__init__()
self.cp = cp
self.game = game
self.init_ui()
def init_ui(self):
layout = QGridLayout()
layout.addWidget(QArmorRecruitmentMenu(self.cp, self.game), 0, 0)
layout.addWidget(QGroundForcesStrategy(self.cp, self.game), 0, 1)
self.setLayout(layout)

View File

@ -0,0 +1,23 @@
from PySide2.QtWidgets import QLabel, QGroupBox, QVBoxLayout
from game import Game
from qt_ui.windows.basemenu.ground_forces.QGroundForcesStrategySelector import QGroundForcesStrategySelector
from theater import ControlPoint
class QGroundForcesStrategy(QGroupBox):
def __init__(self, cp:ControlPoint, game:Game):
super(QGroundForcesStrategy, self).__init__("Frontline operations :")
self.cp = cp
self.game = game
self.init_ui()
def init_ui(self):
layout = QVBoxLayout()
for enemy_cp in self.cp.connected_points:
if not enemy_cp.captured:
layout.addWidget(QLabel(enemy_cp.name))
layout.addWidget(QGroundForcesStrategySelector(self.cp, enemy_cp))
layout.addStretch()
self.setLayout(layout)

View File

@ -0,0 +1,25 @@
from PySide2.QtWidgets import QComboBox
from theater import ControlPoint, CombatStance
class QGroundForcesStrategySelector(QComboBox):
def __init__(self, cp:ControlPoint, enemy_cp:ControlPoint):
super(QGroundForcesStrategySelector, self).__init__()
self.cp = cp
self.enemy_cp = enemy_cp
if enemy_cp.id not in self.cp.stances:
self.cp.stances[enemy_cp.id] = CombatStance.DEFENSIVE
for i, stance in enumerate(CombatStance):
self.addItem(stance.name, userData=stance.value)
if self.cp.stances[enemy_cp.id] == stance.value:
self.setCurrentIndex(i)
self.currentTextChanged.connect(self.on_change)
def on_change(self):
print(self.currentData())
self.cp.stances[self.enemy_cp.id] = self.currentData()

View File

@ -0,0 +1,56 @@
from PySide2.QtWidgets import QLabel, QGroupBox, QVBoxLayout, QFrame, QGridLayout
from dcs.task import Embarking, CAS, PinpointStrike, CAP
from game import Game
from qt_ui.windows.basemenu.ground_forces.QGroundForcesStrategySelector import QGroundForcesStrategySelector
from theater import ControlPoint, db
class QIntelInfo(QFrame):
def __init__(self, cp:ControlPoint, game:Game):
super(QIntelInfo, self).__init__()
self.cp = cp
self.game = game
self.init_ui()
def init_ui(self):
layout = QVBoxLayout()
intel = QGroupBox("Intel")
intelLayout = QVBoxLayout()
units = {
CAP: db.find_unittype(CAP, self.game.enemy_name),
Embarking: db.find_unittype(Embarking, self.game.enemy_name),
CAS: db.find_unittype(CAS, self.game.enemy_name),
PinpointStrike: db.find_unittype(PinpointStrike, self.game.enemy_name),
}
for task_type in units.keys():
units_column = list(set(units[task_type]))
if sum([self.cp.base.total_units_of_type(u) for u in units_column]) > 0:
group = QGroupBox(db.task_name(task_type))
groupLayout = QGridLayout()
group.setLayout(groupLayout)
row = 0
for unit_type in units_column:
existing_units = self.cp.base.total_units_of_type(unit_type)
if existing_units == 0:
continue
groupLayout.addWidget(QLabel("<b>" + db.unit_type_name(unit_type) + "</b>"), row, 0)
groupLayout.addWidget(QLabel(str(existing_units)), row, 1)
row += 1
intelLayout.addWidget(group)
intelLayout.addStretch()
intel.setLayout(intelLayout)
layout.addWidget(intel)
layout.addStretch()
self.setLayout(layout)

View File

@ -1,7 +1,7 @@
from PySide2.QtCore import Qt, Slot, QItemSelectionModel, QPoint from PySide2.QtCore import Qt, Slot, QItemSelectionModel, QPoint
from PySide2.QtWidgets import QDialog, QGridLayout, QScrollArea, QVBoxLayout, QPushButton from PySide2.QtWidgets import QDialog, QGridLayout, QScrollArea, QVBoxLayout, QPushButton
from game import Game from game import Game
from game.event import StrikeEvent, InsurgentAttackEvent, FrontlineAttackEvent, CAP, CAS from game.event import CAP, CAS, FrontlineAttackEvent
from qt_ui.uiconstants import EVENT_ICONS from qt_ui.uiconstants import EVENT_ICONS
from qt_ui.windows.QWaitingForMissionResultWindow import QWaitingForMissionResultWindow from qt_ui.windows.QWaitingForMissionResultWindow import QWaitingForMissionResultWindow
from qt_ui.windows.mission.QPlannedFlightsView import QPlannedFlightsView from qt_ui.windows.mission.QPlannedFlightsView import QPlannedFlightsView
@ -18,7 +18,7 @@ class QMissionPlanning(QDialog):
self.setMinimumSize(750, 420) self.setMinimumSize(750, 420)
self.setModal(True) self.setModal(True)
self.setWindowTitle("Mission Preparation") self.setWindowTitle("Mission Preparation")
self.setWindowIcon(EVENT_ICONS[StrikeEvent]) self.setWindowIcon(EVENT_ICONS["strike"])
self.init_ui() self.init_ui()
print("DONE") print("DONE")
@ -84,14 +84,10 @@ class QMissionPlanning(QDialog):
# self.game.awacs_expense_commit() # self.game.awacs_expense_commit()
#else: #else:
# self.gameEvent.is_awacs_enabled = False # self.gameEvent.is_awacs_enabled = False
self.gameEvent.is_awacs_enabled = False self.gameEvent.is_awacs_enabled = True
self.gameEvent.ca_slots = 1 self.gameEvent.ca_slots = 1
self.gameEvent.departure_cp = self.game.theater.controlpoints[0] self.gameEvent.departure_cp = self.game.theater.controlpoints[0]
self.gameEvent.player_attacking({CAS:{}, CAP:{}})
if self.game.is_player_attack(self.gameEvent):
self.gameEvent.player_attacking({CAS:{}, CAP:{}})
else:
self.gameEvent.player_defending({CAS: {}, CAP: {}})
self.gameEvent.depart_from = self.game.theater.controlpoints[0] self.gameEvent.depart_from = self.game.theater.controlpoints[0]
self.game.initiate_event(self.gameEvent) self.game.initiate_event(self.gameEvent)

Binary file not shown.

Before

Width:  |  Height:  |  Size: 260 KiB

After

Width:  |  Height:  |  Size: 496 KiB

View File

@ -39,7 +39,7 @@ mist.scheduleFunction(write_state, {}, timer.getTime() + 10, 60, timer.getTime()
activeWeapons = {} activeWeapons = {}
local function onEvent(event) local function onEvent(event)
if event.id == world.event.S_EVENT_CRASH and event.initiator then if event.id == world.event.S_EVENT_CRASH and event.initiator then
messageAll("Destroyed :" .. event.initiator.getName(event.initiator)) --messageAll("Destroyed :" .. event.initiator.getName(event.initiator))
killed_aircrafts[#killed_aircrafts + 1] = event.initiator.getName(event.initiator) killed_aircrafts[#killed_aircrafts + 1] = event.initiator.getName(event.initiator)
end end
@ -52,7 +52,7 @@ local function onEvent(event)
end end
if event.id == world.event.S_EVENT_BASE_CAPTURED and event.place then if event.id == world.event.S_EVENT_BASE_CAPTURED and event.place then
messageAll("Base captured :" .. event.place.getName(event.place)) --messageAll("Base captured :" .. event.place.getName(event.place))
base_capture_events[#base_capture_events + 1] = event.place.getID(event.place) .. "||" .. event.place.getCoalition(event.place) .. "||" .. event.place.getName(event.place) base_capture_events[#base_capture_events + 1] = event.place.getID(event.place) .. "||" .. event.place.getCoalition(event.place) .. "||" .. event.place.getName(event.place)
end end

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.4 KiB

After

Width:  |  Height:  |  Size: 260 KiB

View File

View File

@ -1,10 +0,0 @@
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()

View File

@ -1,46 +0,0 @@
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()

View File

@ -1,50 +0,0 @@
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()

View File

@ -1,52 +0,0 @@
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()

View File

@ -1,48 +0,0 @@
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()

View File

@ -1,48 +0,0 @@
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()

View File

@ -1,49 +0,0 @@
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()

View File

@ -1,48 +0,0 @@
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 = StrikeEvent(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()

View File

@ -1,81 +0,0 @@
from dcs.mission import Mission
from game import *
from game.event import *
from game.db import *
from theater.persiangulf import *
from theater import start_generator
PLAYER_COUNTRY = None
ENEMY_COUNTRY = None
def init(player_country: str, enemy_country: str, theater_klass: typing.Type[ConflictTheater]) -> typing.Tuple[Game, ConflictTheater]:
global PLAYER_COUNTRY
global ENEMY_COUNTRY
PLAYER_COUNTRY = player_country
ENEMY_COUNTRY = enemy_country
# prerequisites
persistency.setup("./tests/userfolder/")
theater = theater_klass()
start_generator.generate_inital_units(theater, ENEMY_COUNTRY, True, 1)
game = Game(PLAYER_COUNTRY, ENEMY_COUNTRY, theater)
start_generator.generate_groundobjects(theater, game)
return game, theater
def autoflights_for(event: Event, country: str) -> TaskForceDict:
result = {}
for task in event.tasks:
result[task] = {find_unittype(task, country)[0]: (1, 1)}
return result
class AutodebriefType(Enum):
EVERYONE_DEAD = 0
PLAYER_DEAD = 1
ENEMY_DEAD = 2
def autodebrief_for(event: Event, type: AutodebriefType) -> Debriefing:
mission = event.operation.current_mission # type: Mission
countries = []
if type == AutodebriefType.PLAYER_DEAD or type == AutodebriefType.EVERYONE_DEAD:
countries.append(mission.country(PLAYER_COUNTRY))
if type == AutodebriefType.ENEMY_DEAD or type == AutodebriefType.EVERYONE_DEAD:
countries.append(mission.country(ENEMY_COUNTRY))
dead_units = []
for country in countries:
for group in country.plane_group + country.vehicle_group + country.helicopter_group:
for unit in group.units:
dead_units.append(str(unit.name))
return Debriefing(dead_units, [], [])
def event_state_save(e: Event) -> typing.Tuple[Base, Base]:
return (copy.deepcopy(e.from_cp.base), copy.deepcopy(e.to_cp.base))
def event_state_restore(e: Event, state: typing.Tuple[Base, Base]):
e.from_cp.base, e.to_cp.base = state[0], state[1]
def execute_autocommit(e: Event):
state = event_state_save(e)
e.commit(autodebrief_for(e, AutodebriefType.EVERYONE_DEAD))
event_state_restore(e, state)
state = event_state_save(e)
e.commit(autodebrief_for(e, AutodebriefType.PLAYER_DEAD))
event_state_restore(e, state)
state = event_state_save(e)
e.commit(autodebrief_for(e, AutodebriefType.ENEMY_DEAD))
event_state_restore(e, state)

View File

@ -7,10 +7,10 @@ from dcs.terrain import Airport
from dcs.ships import CVN_74_John_C__Stennis, LHA_1_Tarawa, CV_1143_5_Admiral_Kuznetsov from dcs.ships import CVN_74_John_C__Stennis, LHA_1_Tarawa, CV_1143_5_Admiral_Kuznetsov
from game import db from game import db
from gen.ground_forces.combat_stance import CombatStance
from .theatergroundobject import TheaterGroundObject from .theatergroundobject import TheaterGroundObject
class ControlPointType(Enum): class ControlPointType(Enum):
AIRBASE = 0 # An airbase with slot for everything AIRBASE = 0 # An airbase with slot for everything
AIRCRAFT_CARRIER_GROUP = 1 # A group with a Stennis type carrier (F/A-18, F-14 compatible) AIRCRAFT_CARRIER_GROUP = 1 # A group with a Stennis type carrier (F/A-18, F-14 compatible)
@ -56,6 +56,7 @@ class ControlPoint:
self.connected_points = [] self.connected_points = []
self.base = theater.base.Base() self.base = theater.base.Base()
self.cptype = cptype self.cptype = cptype
self.stances = {}
@classmethod @classmethod
def from_airport(cls, airport: Airport, radials: typing.Collection[int], size: int, importance: float, has_frontline=True): def from_airport(cls, airport: Airport, radials: typing.Collection[int], size: int, importance: float, has_frontline=True):
@ -75,6 +76,10 @@ class ControlPoint:
def is_global(self): def is_global(self):
return not self.connected_points return not self.connected_points
@property
def is_carrier(self):
return self.cptype in [ControlPointType.AIRCRAFT_CARRIER_GROUP, ControlPointType.LHA_GROUP]
@property @property
def sea_radials(self) -> typing.Collection[int]: def sea_radials(self) -> typing.Collection[int]:
# TODO: fix imports # TODO: fix imports
@ -87,6 +92,7 @@ class ControlPoint:
def connect(self, to): def connect(self, to):
self.connected_points.append(to) self.connected_points.append(to)
self.stances[to.id] = CombatStance.DEFENSIVE
def has_runway(self): def has_runway(self):
""" """

View File

@ -9,8 +9,9 @@ from .landmap import load_landmap
class PersianGulfTheater(ConflictTheater): class PersianGulfTheater(ConflictTheater):
terrain = dcs.terrain.PersianGulf() terrain = dcs.terrain.PersianGulf()
overview_image = "persiangulf.gif" overview_image = "persiangulf.gif"
reference_points = {(persiangulf.Sir_Abu_Nuayr.position.x, persiangulf.Sir_Abu_Nuayr.position.y): (321*4, 145*4), reference_points = {
(persiangulf.Sirri_Island.position.x, persiangulf.Sirri_Island.position.y): (347*4, 82*4), } (persiangulf.Sir_Abu_Nuayr.position.x, persiangulf.Sir_Abu_Nuayr.position.y): (423 * 4, 150 * 4),
(persiangulf.Sirri_Island.position.x, persiangulf.Sirri_Island.position.y): (447 * 4, 82 * 4), }
landmap = load_landmap("resources\\gulflandmap.p") landmap = load_landmap("resources\\gulflandmap.p")
daytime_map = { daytime_map = {
"dawn": (6, 8), "dawn": (6, 8),
@ -94,8 +95,8 @@ class IranianCampaign(ConflictTheater):
terrain = dcs.terrain.PersianGulf() terrain = dcs.terrain.PersianGulf()
overview_image = "persiangulf.gif" overview_image = "persiangulf.gif"
reference_points = {(persiangulf.Sir_Abu_Nuayr.position.x, persiangulf.Sir_Abu_Nuayr.position.y): (321*4, 145*4), reference_points = {(persiangulf.Sir_Abu_Nuayr.position.x, persiangulf.Sir_Abu_Nuayr.position.y): (423*4, 150*4),
(persiangulf.Sirri_Island.position.x, persiangulf.Sirri_Island.position.y): (347*4, 82*4), } (persiangulf.Sirri_Island.position.x, persiangulf.Sirri_Island.position.y): (447*4, 82*4), }
landmap = load_landmap("resources\\gulflandmap.p") landmap = load_landmap("resources\\gulflandmap.p")
daytime_map = { daytime_map = {
"dawn": (6, 8), "dawn": (6, 8),

View File

@ -29,7 +29,6 @@ ABBREV_NAME = {
"oil": "OILP" "oil": "OILP"
} }
CATEGORY_MAP = { CATEGORY_MAP = {
"CARRIER": ["CARRIER"], "CARRIER": ["CARRIER"],
"aa": ["AA"], "aa": ["AA"],