mirror of
https://github.com/dcs-liberation/dcs_liberation.git
synced 2025-11-10 14:22:26 +00:00
Ground war rework
This commit is contained in:
parent
5884d9d120
commit
f57e453d8d
52
game/db.py
52
game/db.py
@ -168,6 +168,7 @@ PRICES = {
|
||||
Armor.IFV_BMP_2: 16,
|
||||
Armor.IFV_BMP_3: 18,
|
||||
Armor.ZBD_04A: 12,
|
||||
Armor.ZTZ_96B: 35,
|
||||
|
||||
Armor.APC_Cobra: 4,
|
||||
Armor.APC_M113: 6,
|
||||
@ -187,6 +188,17 @@ PRICES = {
|
||||
Armor.IFV_Marder: 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_Ural_375: 3,
|
||||
Infantry.Infantry_M4: 1,
|
||||
@ -220,11 +232,14 @@ PRICES = {
|
||||
Armor.IFV_Sd_Kfz_234_2_Puma:4,
|
||||
Armor.MT_M4_Sherman:4,
|
||||
Armor.MT_M4A4_Sherman_Firefly:6,
|
||||
Armor.CT_Cromwell_IV:8,
|
||||
Armor.M30_Cargo_Carrier:2,
|
||||
Armor.APC_M2A1:2,
|
||||
AirDefence.AAA_Bofors_40mm:4,
|
||||
AirDefence.AAA_Flak_36:6,
|
||||
AirDefence.AAA_Flak_18:4,
|
||||
Artillery.M12_GMC:2,
|
||||
Artillery.Sturmpanzer_IV_Brummbär:2,
|
||||
|
||||
# ship
|
||||
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_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_90,
|
||||
Armor.ZTZ_96B,
|
||||
|
||||
Armor.APC_Cobra,
|
||||
Armor.APC_Cobra,
|
||||
@ -424,6 +438,7 @@ UNIT_BY_TASK = {
|
||||
Armor.IFV_Sd_Kfz_234_2_Puma,
|
||||
Armor.MT_M4_Sherman,
|
||||
Armor.MT_M4A4_Sherman_Firefly,
|
||||
Armor.CT_Cromwell_IV,
|
||||
Armor.M30_Cargo_Carrier,
|
||||
Armor.M30_Cargo_Carrier,
|
||||
Armor.APC_M2A1,
|
||||
@ -431,6 +446,19 @@ UNIT_BY_TASK = {
|
||||
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: [
|
||||
|
||||
@ -455,7 +483,7 @@ UNIT_BY_TASK = {
|
||||
Nothing: [Infantry.Infantry_M4, Infantry.Soldier_AK, ],
|
||||
Embarking: [],
|
||||
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]:
|
||||
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:
|
||||
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):
|
||||
return ship_map[unit.type]
|
||||
else:
|
||||
return unit.unit_type
|
||||
return unit.type
|
||||
|
||||
|
||||
def task_name(task) -> str:
|
||||
|
||||
@ -1,10 +1,2 @@
|
||||
from .event 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 *
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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
|
||||
@ -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
|
||||
@ -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
|
||||
@ -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
|
||||
@ -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
|
||||
|
||||
|
||||
@ -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
|
||||
@ -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
|
||||
@ -10,7 +10,9 @@ China_2000 = {
|
||||
|
||||
MiG_21Bis, # Standing as J-7
|
||||
Su_30,
|
||||
Su_33,
|
||||
J_11A,
|
||||
JF_17,
|
||||
|
||||
IL_76MD,
|
||||
IL_78M,
|
||||
@ -26,14 +28,17 @@ China_2000 = {
|
||||
AirDefence.SAM_SA_6_Kub_LN_2P25,
|
||||
AirDefence.HQ_7_Self_Propelled_LN,
|
||||
|
||||
Armor.MBT_T_72B, # Type 99
|
||||
Armor.ZTZ_96B,
|
||||
Armor.MBT_T_55,
|
||||
Armor.ZBD_04A,
|
||||
Armor.IFV_BMP_1,
|
||||
Artillery.MLRS_9A52_Smerch,
|
||||
Artillery.SPH_2S9_Nona,
|
||||
|
||||
Unarmed.Transport_Ural_375,
|
||||
Unarmed.Transport_UAZ_469,
|
||||
Infantry.Soldier_AK,
|
||||
Infantry.Paratrooper_RPG_16,
|
||||
|
||||
CV_1143_5_Admiral_Kuznetsov,
|
||||
Bulk_cargo_ship_Yakushev,
|
||||
|
||||
@ -23,6 +23,7 @@ France_1995 = {
|
||||
|
||||
Unarmed.Transport_M818,
|
||||
Infantry.Infantry_M4,
|
||||
Infantry.Infantry_M4,
|
||||
|
||||
AirDefence.SAM_Roland_ADS,
|
||||
AirDefence.SAM_Hawk_PCP,
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
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.vehicles import AirDefence, Armor, Unarmed, Infantry
|
||||
from dcs.vehicles import AirDefence, Armor, Unarmed, Infantry, Artillery
|
||||
|
||||
Russia_1955 = {
|
||||
"country": "Russia",
|
||||
@ -16,19 +16,23 @@ Russia_1955 = {
|
||||
|
||||
AirDefence.AAA_ZU_23_Closed,
|
||||
AirDefence.AAA_ZU_23_on_Ural_375,
|
||||
|
||||
Armor.ARV_BRDM_2,
|
||||
Armor.ARV_MTLB_U_BOMAN,
|
||||
Armor.APC_MTLB,
|
||||
Armor.MBT_T_55,
|
||||
Artillery.MLRS_BM_21_Grad,
|
||||
|
||||
Unarmed.Transport_Ural_375,
|
||||
Unarmed.Transport_UAZ_469,
|
||||
Infantry.Soldier_AK,
|
||||
|
||||
CV_1143_5_Admiral_Kuznetsov,
|
||||
Bulk_cargo_ship_Yakushev,
|
||||
Dry_cargo_ship_Ivanov,
|
||||
Tanker_Elnya_160
|
||||
Tanker_Elnya_160,
|
||||
|
||||
# Infantry squad
|
||||
Infantry.Paratrooper_AKS,
|
||||
Infantry.Infantry_Soldier_Rus,
|
||||
Infantry.Paratrooper_RPG_16,
|
||||
]
|
||||
}
|
||||
@ -1,7 +1,7 @@
|
||||
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.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 = {
|
||||
"country": "Russia",
|
||||
@ -31,15 +31,21 @@ Russia_1965 = {
|
||||
Armor.IFV_BMD_1,
|
||||
Armor.IFV_BMP_1,
|
||||
Armor.MBT_T_55,
|
||||
Artillery.MLRS_BM_21_Grad,
|
||||
|
||||
Unarmed.Transport_Ural_375,
|
||||
Unarmed.Transport_UAZ_469,
|
||||
Infantry.Soldier_AK,
|
||||
|
||||
CV_1143_5_Admiral_Kuznetsov,
|
||||
Bulk_cargo_ship_Yakushev,
|
||||
Dry_cargo_ship_Ivanov,
|
||||
Tanker_Elnya_160
|
||||
Tanker_Elnya_160,
|
||||
|
||||
# Infantry squad
|
||||
Infantry.Paratrooper_AKS,
|
||||
Infantry.Infantry_Soldier_Rus,
|
||||
Infantry.Paratrooper_RPG_16,
|
||||
|
||||
],
|
||||
"shorad":[
|
||||
AirDefence.AAA_ZU_23_Closed
|
||||
|
||||
@ -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, \
|
||||
Yak_40, A_50
|
||||
from dcs.ships import *
|
||||
from dcs.vehicles import AirDefence, Armor, Unarmed, Infantry
|
||||
from dcs.vehicles import AirDefence, Armor, Unarmed, Infantry, Artillery
|
||||
|
||||
Russia_1975 = {
|
||||
"country": "Russia",
|
||||
@ -39,14 +39,22 @@ Russia_1975 = {
|
||||
Armor.IFV_BMP_1,
|
||||
Armor.MBT_T_55,
|
||||
|
||||
Artillery.SPH_2S9_Nona,
|
||||
Artillery.SPH_2S1_Gvozdika,
|
||||
|
||||
Unarmed.Transport_Ural_375,
|
||||
Unarmed.Transport_UAZ_469,
|
||||
Infantry.Soldier_AK,
|
||||
|
||||
CV_1143_5_Admiral_Kuznetsov,
|
||||
Bulk_cargo_ship_Yakushev,
|
||||
Dry_cargo_ship_Ivanov,
|
||||
Tanker_Elnya_160
|
||||
Tanker_Elnya_160,
|
||||
|
||||
# Infantry squad
|
||||
Infantry.Paratrooper_AKS,
|
||||
Infantry.Infantry_Soldier_Rus,
|
||||
Infantry.Paratrooper_RPG_16,
|
||||
|
||||
],
|
||||
"shorad": [
|
||||
AirDefence.AAA_ZU_23_Emplacement,
|
||||
|
||||
@ -16,7 +16,6 @@ Russia_1990 = {
|
||||
Su_27,
|
||||
|
||||
Su_24M,
|
||||
Su_24MR,
|
||||
Su_25,
|
||||
|
||||
IL_76MD,
|
||||
@ -39,15 +38,21 @@ Russia_1990 = {
|
||||
Armor.IFV_BMD_1,
|
||||
Armor.IFV_BMP_1,
|
||||
Armor.MBT_T_55,
|
||||
Artillery.MLRS_9K57_Uragan_BM_27,
|
||||
Artillery.SPH_2S19_Msta,
|
||||
|
||||
Unarmed.Transport_Ural_375,
|
||||
Unarmed.Transport_UAZ_469,
|
||||
Infantry.Soldier_AK,
|
||||
|
||||
CV_1143_5_Admiral_Kuznetsov,
|
||||
Bulk_cargo_ship_Yakushev,
|
||||
Dry_cargo_ship_Ivanov,
|
||||
Tanker_Elnya_160
|
||||
Tanker_Elnya_160,
|
||||
|
||||
# Infantry squad
|
||||
Infantry.Paratrooper_AKS,
|
||||
Infantry.Infantry_Soldier_Rus,
|
||||
Infantry.Paratrooper_RPG_16,
|
||||
],
|
||||
"shorad":[
|
||||
AirDefence.SAM_SA_9_Strela_1_9P31,
|
||||
|
||||
@ -16,7 +16,7 @@ Russia_2010 = {
|
||||
|
||||
Su_25T,
|
||||
Su_34,
|
||||
Su_24MR,
|
||||
Su_24M,
|
||||
L_39ZA,
|
||||
|
||||
IL_76MD,
|
||||
@ -38,13 +38,21 @@ Russia_2010 = {
|
||||
Armor.MBT_T_80U,
|
||||
Armor.MBT_T_72B,
|
||||
|
||||
Artillery.MLRS_9K57_Uragan_BM_27,
|
||||
Artillery.SPH_2S19_Msta,
|
||||
|
||||
Unarmed.Transport_Ural_375,
|
||||
Unarmed.Transport_UAZ_469,
|
||||
Infantry.Soldier_AK,
|
||||
|
||||
CV_1143_5_Admiral_Kuznetsov,
|
||||
Bulk_cargo_ship_Yakushev,
|
||||
Dry_cargo_ship_Ivanov,
|
||||
Tanker_Elnya_160,
|
||||
|
||||
# Infantry squad
|
||||
Infantry.Paratrooper_AKS,
|
||||
Infantry.Infantry_Soldier_Rus,
|
||||
Infantry.Paratrooper_RPG_16,
|
||||
],
|
||||
"shorad":[
|
||||
AirDefence.SAM_SA_19_Tunguska_2S6,
|
||||
|
||||
@ -15,6 +15,8 @@ USA_1944 = {
|
||||
Armor.MT_M4A4_Sherman_Firefly,
|
||||
Armor.M30_Cargo_Carrier,
|
||||
Armor.APC_M2A1,
|
||||
Armor.CT_Cromwell_IV,
|
||||
Artillery.M12_GMC,
|
||||
|
||||
Infantry.Infantry_M1_Garand,
|
||||
|
||||
|
||||
@ -22,6 +22,7 @@ USA_1960 = {
|
||||
|
||||
Unarmed.Transport_M818,
|
||||
Infantry.Infantry_M4,
|
||||
Infantry.Soldier_M249,
|
||||
|
||||
AirDefence.AAA_Vulcan_M163,
|
||||
|
||||
|
||||
@ -24,6 +24,7 @@ USA_1965 = {
|
||||
Armor.APC_M113,
|
||||
Unarmed.Transport_M818,
|
||||
Infantry.Infantry_M4,
|
||||
Infantry.Soldier_M249,
|
||||
|
||||
AirDefence.SAM_Chaparral_M48,
|
||||
AirDefence.SAM_Hawk_PCP,
|
||||
|
||||
@ -31,6 +31,7 @@ USA_1990 = {
|
||||
Armor.ATGM_M1134_Stryker,
|
||||
Unarmed.Transport_M818,
|
||||
Infantry.Infantry_M4,
|
||||
Infantry.Soldier_M249,
|
||||
|
||||
AirDefence.SAM_Hawk_PCP,
|
||||
|
||||
|
||||
@ -11,6 +11,7 @@ USA_2005 = {
|
||||
F_14B,
|
||||
FA_18C_hornet,
|
||||
F_16C_50,
|
||||
JF_17,
|
||||
|
||||
A_10C,
|
||||
AV8BNA,
|
||||
@ -32,8 +33,12 @@ USA_2005 = {
|
||||
Armor.APC_M1043_HMMWV_Armament,
|
||||
Armor.ATGM_M1045_HMMWV_TOW,
|
||||
|
||||
Artillery.MLRS_M270,
|
||||
Artillery.SPH_M109_Paladin,
|
||||
|
||||
Unarmed.Transport_M818,
|
||||
Infantry.Infantry_M4,
|
||||
Infantry.Soldier_M249,
|
||||
|
||||
AirDefence.SAM_Hawk_PCP,
|
||||
AirDefence.SAM_Patriot_EPP_III,
|
||||
|
||||
158
game/game.py
158
game/game.py
@ -9,6 +9,7 @@ from dcs.vehicles import *
|
||||
from game.game_stats import GameStats
|
||||
from gen.conflictgen import Conflict
|
||||
from gen.flights.ai_flight_planner import FlightPlanner
|
||||
from gen.ground_forces.ai_ground_planner import GroundPlanner
|
||||
from userdata.debriefing import Debriefing
|
||||
from theater import *
|
||||
|
||||
@ -38,39 +39,6 @@ PLAYER_INTERCEPT_GLOBAL_PROBABILITY_BASE = 30
|
||||
PLAYER_INTERCEPT_GLOBAL_PROBABILITY_LOG = 2
|
||||
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
|
||||
PLAYER_BASE_STRENGTH_RECOVERY = 0.2
|
||||
|
||||
@ -81,9 +49,11 @@ ENEMY_BASE_STRENGTH_RECOVERY = 0.05
|
||||
AWACS_BUDGET_COST = 4
|
||||
|
||||
# Initial budget value
|
||||
PLAYER_BUDGET_INITIAL = 170
|
||||
PLAYER_BUDGET_INITIAL = 450
|
||||
|
||||
# Base post-turn bonus value
|
||||
PLAYER_BUDGET_BASE = 14
|
||||
PLAYER_BUDGET_BASE = 10
|
||||
|
||||
# Bonus multiplier logarithm base
|
||||
PLAYER_BUDGET_IMPORTANCE_LOG = 2
|
||||
|
||||
@ -113,6 +83,7 @@ class Game:
|
||||
self.game_stats = GameStats()
|
||||
self.game_stats.update(self)
|
||||
self.planners = {}
|
||||
self.ground_planners = {}
|
||||
|
||||
def _roll(self, prob, mult):
|
||||
if self.settings.version == "dev":
|
||||
@ -122,88 +93,11 @@ class Game:
|
||||
return random.randint(1, 100) <= prob * mult
|
||||
|
||||
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))
|
||||
|
||||
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):
|
||||
strikes_generated_for = set()
|
||||
base_attack_generated_for = set()
|
||||
|
||||
for player_cp, enemy_cp in self.theater.conflicts(True):
|
||||
for event_class, (player_probability, enemy_probability) in EVENT_PROBABILITIES.items():
|
||||
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)
|
||||
self._generate_player_event(FrontlineAttackEvent, player_cp, enemy_cp)
|
||||
|
||||
def commision_unit_types(self, cp: ControlPoint, for_task: Task) -> typing.Collection[UnitType]:
|
||||
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)
|
||||
|
||||
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
|
||||
missing_units = limit - cp.base.total_units(for_task)
|
||||
if missing_units > 0:
|
||||
@ -229,11 +123,32 @@ class Game:
|
||||
|
||||
@property
|
||||
def budget_reward_amount(self):
|
||||
reward = 0
|
||||
if len(self.theater.player_points()) > 0:
|
||||
total_importance = sum([x.importance * x.base.strength for x in self.theater.player_points()])
|
||||
return math.ceil(math.log(total_importance + 1, PLAYER_BUDGET_IMPORTANCE_LOG) * PLAYER_BUDGET_BASE * self.settings.multiplier)
|
||||
reward = PLAYER_BUDGET_BASE * len(self.theater.player_points())
|
||||
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:
|
||||
return 0
|
||||
return reward
|
||||
|
||||
def _budget_player(self):
|
||||
self.budget += self.budget_reward_amount
|
||||
@ -310,14 +225,21 @@ class Game:
|
||||
# Update statistics
|
||||
self.game_stats.update(self)
|
||||
|
||||
# Plan flights for next turn
|
||||
# Plan flights & combat for next turn
|
||||
self.planners = {}
|
||||
self.ground_planners = {}
|
||||
for cp in self.theater.controlpoints:
|
||||
if cp.has_runway():
|
||||
planner = FlightPlanner(cp, self)
|
||||
planner.plan_flights()
|
||||
self.planners[cp.id] = planner
|
||||
|
||||
if cp.has_frontline:
|
||||
gplanner = GroundPlanner(cp, self)
|
||||
gplanner.plan_groundwar()
|
||||
self.ground_planners[cp.id] = gplanner
|
||||
|
||||
|
||||
|
||||
@property
|
||||
def current_turn_daytime(self):
|
||||
|
||||
@ -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()
|
||||
|
||||
@ -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()
|
||||
@ -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()
|
||||
@ -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
|
||||
@ -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()
|
||||
@ -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()
|
||||
|
||||
@ -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()
|
||||
|
||||
@ -65,7 +65,6 @@ class Operation:
|
||||
def initialize(self, mission: Mission, conflict: Conflict):
|
||||
self.current_mission = mission
|
||||
self.conflict = conflict
|
||||
self.armorgen = ArmorConflictGenerator(mission, conflict)
|
||||
self.airgen = AircraftConflictGenerator(mission, conflict, self.game.settings)
|
||||
self.shipgen = ShipGenerator(mission, conflict)
|
||||
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.defender_country),
|
||||
player_cp, enemy_cp, self.game.theater)
|
||||
armorgen = ArmorConflictGenerator(self.current_mission, conflict)
|
||||
armorgen.generate_vec(player_cp.base.armor, enemy_cp.base.armor)
|
||||
# Generate frontline ops
|
||||
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
|
||||
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
|
||||
|
||||
@ -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()
|
||||
386
gen/armor.py
386
gen/armor.py
@ -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.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_SIZE_FACTOR = 0.1
|
||||
@ -20,123 +11,306 @@ FRONTLINE_CAS_FIGHTS_COUNT = 16, 24
|
||||
FRONTLINE_CAS_GROUP_MIN = 1, 2
|
||||
FRONTLINE_CAS_PADDING = 12000
|
||||
|
||||
RETREAT_DISTANCE = 20000
|
||||
BREAKTHROUGH_OFFENSIVE_DISTANCE = 35000
|
||||
AGGRESIVE_MOVE_DISTANCE = 16000
|
||||
|
||||
FIGHT_DISTANCE = 3500
|
||||
|
||||
class GroundConflictGenerator:
|
||||
|
||||
class ArmorConflictGenerator:
|
||||
|
||||
def __init__(self, mission: Mission, conflict: Conflict):
|
||||
def __init__(self, mission: Mission, conflict: Conflict, game, player_planned_combat_groups, enemy_planned_combat_groups, player_stance):
|
||||
self.m = mission
|
||||
self.conflict = conflict
|
||||
self.enemy_planned_combat_groups = enemy_planned_combat_groups
|
||||
self.player_planned_combat_groups = player_planned_combat_groups
|
||||
self.player_stance = CombatStance(player_stance)
|
||||
self.enemy_stance = 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:
|
||||
distance = randint(
|
||||
int(self.conflict.size * SPREAD_DISTANCE_FACTOR[0]),
|
||||
int(self.conflict.size * SPREAD_DISTANCE_FACTOR[1]),
|
||||
)
|
||||
|
||||
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:
|
||||
cp = self.conflict.from_cp
|
||||
else:
|
||||
cp = self.conflict.to_cp
|
||||
|
||||
if is_player:
|
||||
faction = self.game.player_name
|
||||
else:
|
||||
faction = self.game.enemy_name
|
||||
|
||||
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
|
||||
|
||||
for c in range(count):
|
||||
logging.info("armorgen: {} for {}".format(unit, side.id))
|
||||
group = self.m.vehicle_group(
|
||||
side,
|
||||
namegen.next_unit_name(side, cp.id, unit),
|
||||
unit,
|
||||
namegen.next_unit_name(side, cp.id, unit), unit,
|
||||
position=self._group_point(at),
|
||||
group_size=1,
|
||||
group_size=count,
|
||||
heading=heading,
|
||||
move_formation=move_formation)
|
||||
|
||||
vehicle: Vehicle = group.units[0]
|
||||
for c in range(count):
|
||||
vehicle: Vehicle = group.units[c]
|
||||
vehicle.player_can_drive = True
|
||||
|
||||
if not to:
|
||||
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
|
||||
)
|
||||
return group
|
||||
@ -27,7 +27,7 @@ STRIKE_AIR_DEFENDERS_DISTANCE = 25000
|
||||
CAP_CAS_DISTANCE = 10000, 120000
|
||||
|
||||
GROUND_INTERCEPT_SPREAD = 5000
|
||||
GROUND_DISTANCE_FACTOR = 1
|
||||
GROUND_DISTANCE_FACTOR = 1.4
|
||||
GROUND_DISTANCE = 2000
|
||||
|
||||
GROUND_ATTACK_DISTANCE = 25000, 13000
|
||||
|
||||
273
gen/ground_forces/ai_ground_planner.py
Normal file
273
gen/ground_forces/ai_ground_planner.py
Normal 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))
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
11
gen/ground_forces/combat_stance.py
Normal file
11
gen/ground_forces/combat_stance.py
Normal 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)
|
||||
|
||||
@ -96,6 +96,8 @@ class GroundObjectsGenerator:
|
||||
ship.heading = u.heading
|
||||
sg.add_unit(ship)
|
||||
|
||||
sg.add_waypoint(sg.points[0].position.point_from_heading(g.units[0].heading, 100000))
|
||||
|
||||
else:
|
||||
if ground_object.dcs_identifier in warehouse_map:
|
||||
static_type = warehouse_map[ground_object.dcs_identifier]
|
||||
|
||||
@ -8,6 +8,10 @@ class NameGenerator:
|
||||
self.number += 1
|
||||
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):
|
||||
return "basedefense_aa|0|0|"
|
||||
|
||||
|
||||
@ -126,6 +126,8 @@ class VisualGenerator:
|
||||
break
|
||||
|
||||
def _generate_stub_planes(self):
|
||||
pass
|
||||
"""
|
||||
mission_units = set()
|
||||
for coalition_name, coalition in self.mission.coalition.items():
|
||||
for country in coalition.countries.values():
|
||||
@ -134,7 +136,7 @@ class VisualGenerator:
|
||||
mission_units.add(db.unit_type_of(unit))
|
||||
|
||||
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):
|
||||
spread = target.size * DESTINATION_SMOKE_DISTANCE_FACTOR
|
||||
|
||||
@ -4,8 +4,7 @@ from typing import Dict
|
||||
|
||||
from PySide2.QtGui import QColor, QFont, QPixmap
|
||||
|
||||
from game.event import BaseAttackEvent, FrontlinePatrolEvent, FrontlineAttackEvent, InfantryTransportEvent, \
|
||||
InsurgentAttackEvent, ConvoyStrikeEvent, InterceptEvent, NavalInterceptEvent, StrikeEvent, UnitsDeliveryEvent
|
||||
from game.event import UnitsDeliveryEvent, FrontlineAttackEvent
|
||||
from theater.theatergroundobject import CATEGORY_MAP
|
||||
|
||||
URLS : Dict[str, str] = {
|
||||
@ -85,15 +84,9 @@ EVENT_ICONS: Dict[str, QPixmap] = {}
|
||||
|
||||
|
||||
def load_event_icons():
|
||||
for category, image in {BaseAttackEvent: "capture",
|
||||
FrontlinePatrolEvent: "attack",
|
||||
for category, image in {
|
||||
"strike": "strike",
|
||||
FrontlineAttackEvent: "attack",
|
||||
InfantryTransportEvent: "infantry",
|
||||
InsurgentAttackEvent: "insurgent_attack",
|
||||
ConvoyStrikeEvent: "convoy",
|
||||
InterceptEvent: "air_intercept",
|
||||
NavalInterceptEvent: "naval_intercept",
|
||||
StrikeEvent: "strike",
|
||||
UnitsDeliveryEvent: "delivery"}.items():
|
||||
EVENT_ICONS[category] = QPixmap("./resources/ui/events/" + image + ".png")
|
||||
|
||||
|
||||
@ -11,6 +11,7 @@ class QBaseInformation(QGroupBox):
|
||||
super(QBaseInformation, self).__init__("Base defenses")
|
||||
self.cp = cp
|
||||
self.airport = airport
|
||||
self.setMinimumWidth(500)
|
||||
self.init_ui()
|
||||
|
||||
def init_ui(self):
|
||||
|
||||
@ -8,8 +8,7 @@ from dcs import Point
|
||||
|
||||
import qt_ui.uiconstants as CONST
|
||||
from game import Game, db
|
||||
from game.event import InfantryTransportEvent, StrikeEvent, BaseAttackEvent, UnitsDeliveryEvent, Event, \
|
||||
FrontlineAttackEvent, FrontlinePatrolEvent, ConvoyStrikeEvent, ControlPointType
|
||||
from game.event import UnitsDeliveryEvent, Event, ControlPointType
|
||||
from gen import Conflict
|
||||
from qt_ui.widgets.map.QLiberationScene import QLiberationScene
|
||||
from qt_ui.widgets.map.QMapControlPoint import QMapControlPoint
|
||||
|
||||
@ -5,7 +5,8 @@ from PySide2.QtWidgets import QGraphicsRectItem, QGraphicsSceneHoverEvent, QGrap
|
||||
|
||||
import qt_ui.uiconstants as CONST
|
||||
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
|
||||
|
||||
|
||||
@ -98,5 +99,5 @@ class QMapControlPoint(QGraphicsRectItem):
|
||||
return self.model.captured and CONST.COLORS["bright_red"] or CONST.COLORS["dark_blue"]
|
||||
|
||||
def openBaseMenu(self):
|
||||
self.baseMenu = QBaseMenu(self.window(), self.model, self.game)
|
||||
self.baseMenu = QBaseMenu2(self.window(), self.model, self.game)
|
||||
self.baseMenu.show()
|
||||
@ -5,7 +5,7 @@ from PySide2.QtWidgets import QHBoxLayout, QLabel, QWidget, QDialog, QVBoxLayout
|
||||
QSpinBox, QPushButton, QMessageBox, QComboBox
|
||||
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
|
||||
from qt_ui.windows.QWaitingForMissionResultWindow import QWaitingForMissionResultWindow
|
||||
from userdata.persistency import base_path
|
||||
@ -234,14 +234,14 @@ class QBriefingWindow(QDialog):
|
||||
return
|
||||
|
||||
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:
|
||||
self.showErrorMessage("No ground vehicles available to attack!")
|
||||
return
|
||||
|
||||
self.gameEvent.player_attacking(flights)
|
||||
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:
|
||||
self.showErrorMessage("No ground vehicles available to defend!")
|
||||
return
|
||||
|
||||
73
qt_ui/windows/basemenu/QBaseMenu2.py
Normal file
73
qt_ui/windows/basemenu/QBaseMenu2.py
Normal 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)
|
||||
37
qt_ui/windows/basemenu/QBaseMenuTabs.py
Normal file
37
qt_ui/windows/basemenu/QBaseMenuTabs.py
Normal 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")
|
||||
92
qt_ui/windows/basemenu/QRecruitBehaviour.py
Normal file
92
qt_ui/windows/basemenu/QRecruitBehaviour.py
Normal 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)
|
||||
63
qt_ui/windows/basemenu/airfield/QAircraftRecruitmentMenu.py
Normal file
63
qt_ui/windows/basemenu/airfield/QAircraftRecruitmentMenu.py
Normal 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)
|
||||
31
qt_ui/windows/basemenu/airfield/QAirfieldCommand.py
Normal file
31
qt_ui/windows/basemenu/airfield/QAirfieldCommand.py
Normal 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)
|
||||
20
qt_ui/windows/basemenu/base_defenses/QBaseDefensesHQ.py
Normal file
20
qt_ui/windows/basemenu/base_defenses/QBaseDefensesHQ.py
Normal 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)
|
||||
|
||||
@ -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)
|
||||
21
qt_ui/windows/basemenu/ground_forces/QGroundForcesHQ.py
Normal file
21
qt_ui/windows/basemenu/ground_forces/QGroundForcesHQ.py
Normal 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)
|
||||
@ -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)
|
||||
@ -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()
|
||||
56
qt_ui/windows/basemenu/intel/QIntelInfo.py
Normal file
56
qt_ui/windows/basemenu/intel/QIntelInfo.py
Normal 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)
|
||||
@ -1,7 +1,7 @@
|
||||
from PySide2.QtCore import Qt, Slot, QItemSelectionModel, QPoint
|
||||
from PySide2.QtWidgets import QDialog, QGridLayout, QScrollArea, QVBoxLayout, QPushButton
|
||||
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.windows.QWaitingForMissionResultWindow import QWaitingForMissionResultWindow
|
||||
from qt_ui.windows.mission.QPlannedFlightsView import QPlannedFlightsView
|
||||
@ -18,7 +18,7 @@ class QMissionPlanning(QDialog):
|
||||
self.setMinimumSize(750, 420)
|
||||
self.setModal(True)
|
||||
self.setWindowTitle("Mission Preparation")
|
||||
self.setWindowIcon(EVENT_ICONS[StrikeEvent])
|
||||
self.setWindowIcon(EVENT_ICONS["strike"])
|
||||
self.init_ui()
|
||||
print("DONE")
|
||||
|
||||
@ -84,14 +84,10 @@ class QMissionPlanning(QDialog):
|
||||
# self.game.awacs_expense_commit()
|
||||
#else:
|
||||
# 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.departure_cp = self.game.theater.controlpoints[0]
|
||||
|
||||
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.game.initiate_event(self.gameEvent)
|
||||
|
||||
Binary file not shown.
|
Before Width: | Height: | Size: 260 KiB After Width: | Height: | Size: 496 KiB |
@ -39,7 +39,7 @@ mist.scheduleFunction(write_state, {}, timer.getTime() + 10, 60, timer.getTime()
|
||||
activeWeapons = {}
|
||||
local function onEvent(event)
|
||||
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)
|
||||
end
|
||||
|
||||
@ -52,7 +52,7 @@ local function onEvent(event)
|
||||
end
|
||||
|
||||
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)
|
||||
end
|
||||
|
||||
|
||||
Binary file not shown.
|
Before Width: | Height: | Size: 1.4 KiB After Width: | Height: | Size: 260 KiB |
@ -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()
|
||||
@ -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()
|
||||
@ -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()
|
||||
@ -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()
|
||||
@ -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()
|
||||
@ -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()
|
||||
@ -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()
|
||||
@ -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()
|
||||
@ -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)
|
||||
@ -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 game import db
|
||||
from gen.ground_forces.combat_stance import CombatStance
|
||||
from .theatergroundobject import TheaterGroundObject
|
||||
|
||||
|
||||
|
||||
class ControlPointType(Enum):
|
||||
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)
|
||||
@ -56,6 +56,7 @@ class ControlPoint:
|
||||
self.connected_points = []
|
||||
self.base = theater.base.Base()
|
||||
self.cptype = cptype
|
||||
self.stances = {}
|
||||
|
||||
@classmethod
|
||||
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):
|
||||
return not self.connected_points
|
||||
|
||||
@property
|
||||
def is_carrier(self):
|
||||
return self.cptype in [ControlPointType.AIRCRAFT_CARRIER_GROUP, ControlPointType.LHA_GROUP]
|
||||
|
||||
@property
|
||||
def sea_radials(self) -> typing.Collection[int]:
|
||||
# TODO: fix imports
|
||||
@ -87,6 +92,7 @@ class ControlPoint:
|
||||
|
||||
def connect(self, to):
|
||||
self.connected_points.append(to)
|
||||
self.stances[to.id] = CombatStance.DEFENSIVE
|
||||
|
||||
def has_runway(self):
|
||||
"""
|
||||
|
||||
@ -9,8 +9,9 @@ from .landmap import load_landmap
|
||||
class PersianGulfTheater(ConflictTheater):
|
||||
terrain = dcs.terrain.PersianGulf()
|
||||
overview_image = "persiangulf.gif"
|
||||
reference_points = {(persiangulf.Sir_Abu_Nuayr.position.x, persiangulf.Sir_Abu_Nuayr.position.y): (321*4, 145*4),
|
||||
(persiangulf.Sirri_Island.position.x, persiangulf.Sirri_Island.position.y): (347*4, 82*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): (447 * 4, 82 * 4), }
|
||||
landmap = load_landmap("resources\\gulflandmap.p")
|
||||
daytime_map = {
|
||||
"dawn": (6, 8),
|
||||
@ -94,8 +95,8 @@ class IranianCampaign(ConflictTheater):
|
||||
|
||||
terrain = dcs.terrain.PersianGulf()
|
||||
overview_image = "persiangulf.gif"
|
||||
reference_points = {(persiangulf.Sir_Abu_Nuayr.position.x, persiangulf.Sir_Abu_Nuayr.position.y): (321*4, 145*4),
|
||||
(persiangulf.Sirri_Island.position.x, persiangulf.Sirri_Island.position.y): (347*4, 82*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): (447*4, 82*4), }
|
||||
landmap = load_landmap("resources\\gulflandmap.p")
|
||||
daytime_map = {
|
||||
"dawn": (6, 8),
|
||||
|
||||
@ -29,7 +29,6 @@ ABBREV_NAME = {
|
||||
"oil": "OILP"
|
||||
}
|
||||
|
||||
|
||||
CATEGORY_MAP = {
|
||||
"CARRIER": ["CARRIER"],
|
||||
"aa": ["AA"],
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user