Compare commits

...

22 Commits
1.2_rc2 ... 1.3

Author SHA1 Message Date
Vasyl Horbachenko
7458181e90 success rate calculation division by zero fix 2018-07-29 04:41:32 +03:00
Vasyl Horbachenko
ec28cdc936 version bump 2018-07-29 04:20:30 +03:00
Vasyl Horbachenko
9dbc9a8a56 prompt window with logs on raised exception; minor UI updates; minor fixes 2018-07-29 04:16:39 +03:00
Vasyl Horbachenko
73d4a2d414 moved viggen to CAS; tanker position fix on enemy attacks; more logs 2018-07-28 04:58:28 +03:00
Vasyl Horbachenko
e93ad8b800 adjusted budget amounts 2018-07-20 04:08:04 +03:00
Vasyl Horbachenko
d48985ca6d new airports for PG; TACANs and ICLS; list frequencies in mission briefing; carrier ops improvements; cold start option 2018-07-20 04:06:13 +03:00
Vasyl Horbachenko
5f7d717b63 fixed sir abu having a frontline 2018-07-19 01:07:24 +03:00
Vasyl Horbachenko
e266698e68 fixed start.bat for usernames w/ spaces; fixed triggers for player defending; minor fixes 2018-07-19 00:57:15 +03:00
Vasyl Horbachenko
e8098e795c added symlink to pydcs to resources/tools 2018-07-18 03:20:18 +03:00
Vasyl Horbachenko
8d69724272 env gen minor update 2018-07-18 03:14:25 +03:00
Vasyl Horbachenko
683114f916 updates to CAP op 2018-07-18 00:45:55 +03:00
Vasyl Horbachenko
3b454470f9 Merge branch 'improved_cas' of https://github.com/shdwp/dcs_pmcliberation into improved_cas 2018-07-17 23:31:58 +03:00
Vasyl Horbachenko
f40f83bb09 fixes and improvements for fronline CAP 2018-07-17 14:22:45 +03:00
Vasyl Horbachenko
932bec2f84 fixes to frontline attack; frontline CAP WIP 2018-07-17 14:22:45 +03:00
Vasyl Horbachenko
820820eb92 frontline attack ops 2018-07-17 14:22:45 +03:00
Vasyl Horbachenko
6f5835a2b8 Improved Frontline CAS 2018-07-17 14:21:50 +03:00
Vasyl Horbachenko
b302372d4c fixes and improvements for fronline CAP 2018-07-17 05:22:41 +03:00
Vasyl Horbachenko
cad7d2c735 fixes to frontline attack; frontline CAP WIP 2018-07-17 02:14:46 +03:00
Vasyl Horbachenko
e4c3f8bce2 frontline attack ops 2018-07-16 23:58:01 +03:00
Vasyl Horbachenko
1fbf4e292a Merge branch 'improved_cas' of https://github.com/shdwp/dcs_pmcliberation into improved_cas 2018-07-16 23:03:47 +03:00
Vasyl Horbachenko
62f5b2d06d Improved Frontline CAS 2018-07-16 23:02:08 +03:00
Vasyl Horbachenko
b2545e4de0 Improved Frontline CAS 2018-07-16 22:19:19 +03:00
51 changed files with 1019 additions and 446 deletions

1
.gitignore vendored
View File

@@ -2,6 +2,7 @@
__pycache__ __pycache__
build/* build/*
resources/payloads/*.lua resources/payloads/*.lua
venv
logs.txt logs.txt
.DS_Store .DS_Store
# User-specific stuff # User-specific stuff

View File

@@ -14,48 +14,9 @@ import ui.corruptedsavemenu
from game.game import Game from game.game import Game
from theater import start_generator from theater import start_generator
from userdata import persistency from userdata import persistency, logging
"""
from dcs.lua.parse import *
a = loads(open("build/mission", "r").read())
b = loads(open("build/mission_workin.lua", "r").read())
def get(a, k):
b = a
for x in k.strip().split(" "):
if isinstance(a, dict):
y = a
a = a.get(x, None)
if a is None:
try:
a = y.get(int(x), None)
except:
pass
else:
break
if a is None:
pass
return a
def cycle(kk, ref, v):
if isinstance(v, dict):
for k, v in v.items():
cycle(kk + " " + str(k), ref, v)
elif isinstance(v, list):
for i, v in enumerate(v):
cycle(kk + " " + str(i), ref, v)
else:
if get(ref, kk) != v:
print(kk, v)
print(get(ref, kk))
cycle("", a, b)
sys.exit(0)
"""
persistency.setup(sys.argv[1]) persistency.setup(sys.argv[1])
dcs.planes.FlyingType.payload_dirs.append(os.path.join(os.path.dirname(os.path.realpath(__file__)), "resources\\payloads")) dcs.planes.FlyingType.payload_dirs.append(os.path.join(os.path.dirname(os.path.realpath(__file__)), "resources\\payloads"))

View File

@@ -73,6 +73,8 @@ PRICES = {
An_30M: 13, An_30M: 13,
Yak_40: 13, Yak_40: 13,
S_3B_Tanker: 13, S_3B_Tanker: 13,
IL_78M: 13,
KC_135: 13,
A_50: 8, A_50: 8,
E_3A: 8, E_3A: 8,
@@ -134,7 +136,6 @@ Following tasks are present:
UNIT_BY_TASK = { UNIT_BY_TASK = {
CAP: [ CAP: [
C_101CC, C_101CC,
AJS37,
F_5E_3, F_5E_3,
Su_27, Su_27,
Su_33, Su_33,
@@ -149,6 +150,7 @@ UNIT_BY_TASK = {
MiG_15bis, MiG_15bis,
L_39ZA, L_39ZA,
AV8BNA, AV8BNA,
AJS37,
A_10A, A_10A,
A_10C, A_10C,
Su_25, Su_25,
@@ -167,6 +169,11 @@ UNIT_BY_TASK = {
C_130, C_130,
], ],
Refueling: [
IL_78M,
KC_135,
],
AWACS: [E_3A, A_50, ], AWACS: [E_3A, A_50, ],
PinpointStrike: [Armor.MBT_T_90, Armor.MBT_T_80U, Armor.MBT_T_55, Armor.MBT_M1A2_Abrams, Armor.MBT_M60A3_Patton, Armor.ATGM_M1134_Stryker, Armor.APC_BTR_80, ], PinpointStrike: [Armor.MBT_T_90, Armor.MBT_T_80U, Armor.MBT_T_55, Armor.MBT_M1A2_Abrams, Armor.MBT_M60A3_Patton, Armor.ATGM_M1134_Stryker, Armor.APC_BTR_80, ],
@@ -251,6 +258,7 @@ UNIT_BY_COUNTRY = {
L_39ZA, L_39ZA,
IL_76MD, IL_76MD,
IL_78M,
An_26B, An_26B,
An_30M, An_30M,
Yak_40, Yak_40,
@@ -290,6 +298,7 @@ UNIT_BY_COUNTRY = {
A_10C, A_10C,
AV8BNA, AV8BNA,
KC_135,
S_3B_Tanker, S_3B_Tanker,
C_130, C_130,
E_3A, E_3A,
@@ -338,6 +347,10 @@ PLANE_PAYLOAD_OVERRIDES = {
"*": "R-73*4,R-27R*2,R-27ER*6", "*": "R-73*4,R-27R*2,R-27ER*6",
}, },
AJS37: {
CAS: "CAS (75 GUN): RB-75*2, AKAN",
},
AV8BNA: { AV8BNA: {
CAS: "AS 2", CAS: "AS 2",
}, },
@@ -432,6 +445,34 @@ def choose_units(for_task: Task, factor: float, count: int, country: str) -> typ
return list(set(suitable_unittypes[index_start:index_end])) return list(set(suitable_unittypes[index_start:index_end]))
def unitdict_append(unit_dict: UnitsDict, unit_type: UnitType, count: int):
unit_dict[unit_type] = unit_dict.get(unit_type, 0) + 1
def unitdict_split(unit_dict: UnitsDict, count: int):
buffer_dict = {}
for unit_type, unit_count in unit_dict.items():
for _ in range(unit_count):
unitdict_append(buffer_dict, unit_type, 1)
if sum(buffer_dict.values()) >= count:
yield buffer_dict
buffer_dict = {}
if len(buffer_dict):
yield buffer_dict
def unitdict_restrict_count(unit_dict: UnitsDict, total_count: int) -> UnitsDict:
if total_count == 0:
return {}
groups = list(unitdict_split(unit_dict, total_count))
if len(groups) > 0:
return groups[0]
else:
return {}
def _validate_db(): def _validate_db():
# check unit by task uniquity # check unit by task uniquity
total_set = set() total_set = set()

View File

@@ -1,8 +1,9 @@
from .event import * from .event import *
from .groundintercept import * from .frontlineattack import *
from .frontlinepatrol import *
from .intercept import * from .intercept import *
from .capture import * from .baseattack import *
from .navalintercept import * from .navalintercept import *
from .antiaastrike import * from .antiaastrike import *
from .groundattack import * from .insurgentattack import *
from .infantrytransport import * from .infantrytransport import *

View File

@@ -17,7 +17,7 @@ class AntiAAStrikeEvent(Event):
targets = None # type: db.ArmorDict targets = None # type: db.ArmorDict
def __str__(self): def __str__(self):
return "Anti-AA strike from {} at {}".format(self.from_cp, self.to_cp) return "Anti-AA strike"
def is_successfull(self, debriefing: Debriefing): def is_successfull(self, debriefing: Debriefing):
total_targets = sum(self.targets.values()) total_targets = sum(self.targets.values())

View File

@@ -4,19 +4,19 @@ import random
from dcs.task import * from dcs.task import *
from game import db from game import db
from game.operation.capture import CaptureOperation from game.operation.baseattack import BaseAttackOperation
from userdata.debriefing import Debriefing from userdata.debriefing import Debriefing
from .event import Event from .event import Event
class CaptureEvent(Event): class BaseAttackEvent(Event):
silent = True silent = True
BONUS_BASE = 15 BONUS_BASE = 15
STRENGTH_RECOVERY = 0.55 STRENGTH_RECOVERY = 0.55
def __str__(self): def __str__(self):
return "Attack from {} to {}".format(self.from_cp, self.to_cp) return "Base attack"
def is_successfull(self, debriefing: Debriefing): def is_successfull(self, debriefing: Debriefing):
alive_attackers = sum([v for k, v in debriefing.alive_units[self.attacker_name].items() if db.unit_task(k) == PinpointStrike]) alive_attackers = sum([v for k, v in debriefing.alive_units[self.attacker_name].items() if db.unit_task(k) == PinpointStrike])
@@ -28,7 +28,7 @@ class CaptureEvent(Event):
return not attackers_success return not attackers_success
def commit(self, debriefing: Debriefing): def commit(self, debriefing: Debriefing):
super(CaptureEvent, self).commit(debriefing) super(BaseAttackEvent, self).commit(debriefing)
if self.is_successfull(debriefing): if self.is_successfull(debriefing):
if self.from_cp.captured: if self.from_cp.captured:
self.to_cp.captured = True self.to_cp.captured = True
@@ -49,13 +49,13 @@ class CaptureEvent(Event):
escort = self.from_cp.base.scramble_sweep(self.game.settings.multiplier) escort = self.from_cp.base.scramble_sweep(self.game.settings.multiplier)
attackers = self.from_cp.base.armor attackers = self.from_cp.base.armor
op = CaptureOperation(game=self.game, op = BaseAttackOperation(game=self.game,
attacker_name=self.attacker_name, attacker_name=self.attacker_name,
defender_name=self.defender_name, defender_name=self.defender_name,
attacker_clients={}, attacker_clients={},
defender_clients=clients, defender_clients=clients,
from_cp=self.from_cp, from_cp=self.from_cp,
to_cp=self.to_cp) to_cp=self.to_cp)
op.setup(cas=cas, op.setup(cas=cas,
escort=escort, escort=escort,
@@ -67,13 +67,13 @@ class CaptureEvent(Event):
self.operation = op self.operation = op
def player_attacking(self, cas: db.PlaneDict, escort: db.PlaneDict, armor: db.ArmorDict, clients: db.PlaneDict): def player_attacking(self, cas: db.PlaneDict, escort: db.PlaneDict, armor: db.ArmorDict, clients: db.PlaneDict):
op = CaptureOperation(game=self.game, op = BaseAttackOperation(game=self.game,
attacker_name=self.attacker_name, attacker_name=self.attacker_name,
defender_name=self.defender_name, defender_name=self.defender_name,
attacker_clients=clients, attacker_clients=clients,
defender_clients={}, defender_clients={},
from_cp=self.from_cp, from_cp=self.from_cp,
to_cp=self.to_cp) to_cp=self.to_cp)
defenders = self.to_cp.base.scramble_sweep(self.game.settings.multiplier) defenders = self.to_cp.base.scramble_sweep(self.game.settings.multiplier)
defenders.update(self.to_cp.base.scramble_cas(self.game.settings.multiplier)) defenders.update(self.to_cp.base.scramble_cas(self.game.settings.multiplier))

View File

@@ -0,0 +1,73 @@
import math
import random
from dcs.task import *
from dcs.vehicles import AirDefence
from game import *
from game.event import *
from game.operation.frontlineattack import FrontlineAttackOperation
from userdata.debriefing import Debriefing
class FrontlineAttackEvent(Event):
TARGET_VARIETY = 2
TARGET_AMOUNT_FACTOR = 0.5
ATTACKER_AMOUNT_FACTOR = 0.4
ATTACKER_DEFENDER_FACTOR = 0.7
STRENGTH_INFLUENCE = 0.2
SUCCESS_FACTOR = 1.5
defenders = None # type: db.ArmorDict
@property
def threat_description(self):
return "{} vehicles".format(self.to_cp.base.assemble_count())
def __str__(self):
return "Frontline attack"
def is_successfull(self, debriefing: Debriefing):
alive_attackers = sum([v for k, v in debriefing.alive_units[self.attacker_name].items() if db.unit_task(k) == PinpointStrike])
alive_defenders = sum([v for k, v in debriefing.alive_units[self.defender_name].items() if db.unit_task(k) == PinpointStrike])
attackers_success = (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(FrontlineAttackEvent, self).commit(debriefing)
if self.from_cp.captured:
if self.is_successfull(debriefing):
self.to_cp.base.affect_strength(-self.STRENGTH_INFLUENCE)
else:
self.to_cp.base.affect_strength(+self.STRENGTH_INFLUENCE)
else:
if self.is_successfull(debriefing):
self.from_cp.base.affect_strength(-self.STRENGTH_INFLUENCE)
else:
self.to_cp.base.affect_strength(-self.STRENGTH_INFLUENCE)
def skip(self):
if self.to_cp.captured:
self.to_cp.base.affect_strength(-0.1)
def player_attacking(self, armor: db.ArmorDict, strikegroup: db.PlaneDict, clients: db.PlaneDict):
self.defenders = self.to_cp.base.assemble_attack()
op = FrontlineAttackOperation(game=self.game,
attacker_name=self.attacker_name,
defender_name=self.defender_name,
attacker_clients=clients,
defender_clients={},
from_cp=self.from_cp,
to_cp=self.to_cp)
op.setup(target=self.defenders,
attackers=db.unitdict_restrict_count(armor, sum(self.defenders.values())),
strikegroup=strikegroup)
self.operation = op

View File

@@ -0,0 +1,87 @@
import math
import random
from dcs.task import *
from dcs.vehicles import AirDefence
from game import *
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.2
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))
def __str__(self):
return "Frontline CAP"
"""
def is_successfull(self, debriefing: Debriefing):
total_targets = sum(self.cas.values())
destroyed_targets = 0
for unit, count in debriefing.destroyed_units[self.defender_name].items():
if unit in self.cas:
destroyed_targets += count
if self.from_cp.captured:
return float(destroyed_targets) / total_targets >= self.SUCCESS_TARGETS_HIT_PERCENTAGE
else:
return float(destroyed_targets) / total_targets < self.SUCCESS_TARGETS_HIT_PERCENTAGE
"""
def is_successfull(self, debriefing: Debriefing):
alive_attackers = sum([v for k, v in debriefing.alive_units[self.attacker_name].items() if db.unit_task(k) == PinpointStrike])
alive_defenders = sum([v for k, v in debriefing.alive_units[self.defender_name].items() if db.unit_task(k) == PinpointStrike])
attackers_success = (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, interceptors: db.PlaneDict, clients: db.PlaneDict, armor: db.ArmorDict):
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,
attacker_clients=clients,
defender_clients={},
from_cp=self.from_cp,
to_cp=self.to_cp)
defenders = self.to_cp.base.assemble_attack()
op.setup(cas=self.cas,
escort=self.escort,
interceptors=interceptors,
armor_attackers=db.unitdict_restrict_count(armor, sum(defenders.values())),
armor_defenders=defenders)
self.operation = op

View File

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

View File

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

View File

@@ -16,7 +16,7 @@ class InfantryTransportEvent(Event):
STRENGTH_INFLUENCE = 0.3 STRENGTH_INFLUENCE = 0.3
def __str__(self): def __str__(self):
return "Frontline transport troops to {}".format(self.to_cp) return "Frontline transport troops"
def is_successfull(self, debriefing: Debriefing): def is_successfull(self, debriefing: Debriefing):
return True return True

View File

@@ -0,0 +1,53 @@
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
class InsurgentAttackEvent(Event):
SUCCESS_FACTOR = 0.7
TARGET_VARIETY = 2
TARGET_AMOUNT_FACTOR = 0.5
@property
def threat_description(self):
return ""
def __str__(self):
return "Destroy insurgents"
def is_successfull(self, debriefing: Debriefing):
killed_units = sum([v for k, v in debriefing.destroyed_units[self.attacker_name].items() if db.unit_task(k) == PinpointStrike])
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, strikegroup: db.PlaneDict, clients: db.PlaneDict):
suitable_unittypes = db.find_unittype(Reconnaissance, self.attacker_name)
random.shuffle(suitable_unittypes)
unittypes = suitable_unittypes[:self.TARGET_VARIETY]
typecount = max(math.floor(self.difficulty * self.TARGET_AMOUNT_FACTOR), 1)
self.targets = {unittype: typecount for unittype in unittypes}
op = InsurgentAttackOperation(game=self.game,
attacker_name=self.attacker_name,
defender_name=self.defender_name,
attacker_clients={},
defender_clients=clients,
from_cp=self.from_cp,
to_cp=self.to_cp)
op.setup(target=self.targets,
strikegroup=strikegroup)
self.operation = op
def player_attacking(self, interceptors: db.PlaneDict, clients: db.PlaneDict):
assert False

View File

@@ -20,11 +20,15 @@ class InterceptEvent(Event):
transport_unit = None # type: FlyingType transport_unit = None # type: FlyingType
def __str__(self): def __str__(self):
return "Intercept from {} at {}".format(self.from_cp, self.to_cp) return "Intercept"
def _enemy_scramble_multiplier(self) -> float:
is_global = self.from_cp.is_global or self.to_cp.is_global
return self.game.settings.multiplier * is_global and 0.5 or 1
@property @property
def threat_description(self): def threat_description(self):
return "{} aircraft".format(self.enemy_cp.base.scramble_count(self.game.settings.multiplier, CAP)) return "{} aircraft".format(self.enemy_cp.base.scramble_count(self._enemy_scramble_multiplier(), CAP))
def is_successfull(self, debriefing: Debriefing): def is_successfull(self, debriefing: Debriefing):
units_destroyed = debriefing.destroyed_units[self.defender_name].get(self.transport_unit, 0) units_destroyed = debriefing.destroyed_units[self.defender_name].get(self.transport_unit, 0)
@@ -54,7 +58,7 @@ class InterceptEvent(Event):
self.to_cp.base.affect_strength(-self.STRENGTH_INFLUENCE) self.to_cp.base.affect_strength(-self.STRENGTH_INFLUENCE)
def player_attacking(self, interceptors: db.PlaneDict, clients: db.PlaneDict): def player_attacking(self, interceptors: db.PlaneDict, clients: db.PlaneDict):
escort = self.to_cp.base.scramble_sweep(self.game.settings.multiplier) escort = self.to_cp.base.scramble_sweep(self._enemy_scramble_multiplier())
self.transport_unit = random.choice(db.find_unittype(Transport, self.defender_name)) self.transport_unit = random.choice(db.find_unittype(Transport, self.defender_name))
assert self.transport_unit is not None assert self.transport_unit is not None

View File

@@ -24,7 +24,7 @@ class NavalInterceptEvent(Event):
return max(int(factor), 1) return max(int(factor), 1)
def __str__(self) -> str: def __str__(self) -> str:
return "Naval intercept at {}".format(self.to_cp) return "Naval intercept"
@property @property
def threat_description(self): def threat_description(self):

View File

@@ -1,3 +1,4 @@
import logging
import typing import typing
import random import random
import math import math
@@ -5,6 +6,7 @@ import math
from dcs.task import * from dcs.task import *
from dcs.vehicles import * from dcs.vehicles import *
from gen.conflictgen import Conflict
from userdata.debriefing import Debriefing from userdata.debriefing import Debriefing
from theater import * from theater import *
@@ -23,14 +25,15 @@ COMMISION_LIMITS_FACTORS = {
COMMISION_AMOUNTS_SCALE = 1.5 COMMISION_AMOUNTS_SCALE = 1.5
COMMISION_AMOUNTS_FACTORS = { COMMISION_AMOUNTS_FACTORS = {
PinpointStrike: 2, PinpointStrike: 6,
CAS: 1, CAS: 1,
CAP: 2, CAP: 2,
AirDefence: 0.3, AirDefence: 0.3,
} }
PLAYER_INTERCEPT_GLOBAL_PROBABILITY_BASE = 25 PLAYER_INTERCEPT_GLOBAL_PROBABILITY_BASE = 30
PLAYER_INTERCEPT_GLOBAL_PROBABILITY_LOG = 2 PLAYER_INTERCEPT_GLOBAL_PROBABILITY_LOG = 2
PLAYER_BASEATTACK_THRESHOLD = 0.2
""" """
Various events probabilities. First key is player probabilty, second is enemy probability. Various events probabilities. First key is player probabilty, second is enemy probability.
@@ -39,17 +42,18 @@ For the enemy events, only 1 event of each type could be generated for a turn.
Events: Events:
* CaptureEvent - capture base * CaptureEvent - capture base
* InterceptEvent - air intercept * InterceptEvent - air intercept
* GroundInterceptEvent - frontline CAS * FrontlineAttack - frontline attack
* GroundAttackEvent - destroy insurgents * GroundAttackEvent - destroy insurgents
* NavalInterceptEvent - naval intercept * NavalInterceptEvent - naval intercept
* AntiAAStrikeEvent - anti-AA strike * AntiAAStrikeEvent - anti-AA strike
* InfantryTransportEvent - helicopter infantry transport * InfantryTransportEvent - helicopter infantry transport
""" """
EVENT_PROBABILITIES = { EVENT_PROBABILITIES = {
CaptureEvent: [100, 10], BaseAttackEvent: [100, 10],
FrontlineAttackEvent: [100, 0],
FrontlinePatrolEvent: [100, 0],
InterceptEvent: [25, 10], InterceptEvent: [25, 10],
GroundInterceptEvent: [25, 10], InsurgentAttackEvent: [0, 10],
GroundAttackEvent: [0, 10],
NavalInterceptEvent: [25, 10], NavalInterceptEvent: [25, 10],
AntiAAStrikeEvent: [25, 10], AntiAAStrikeEvent: [25, 10],
InfantryTransportEvent: [25, 0], InfantryTransportEvent: [25, 0],
@@ -65,9 +69,9 @@ ENEMY_BASE_STRENGTH_RECOVERY = 0.05
AWACS_BUDGET_COST = 4 AWACS_BUDGET_COST = 4
# Initial budget value # Initial budget value
PLAYER_BUDGET_INITIAL = 120 PLAYER_BUDGET_INITIAL = 170
# Base post-turn bonus value # Base post-turn bonus value
PLAYER_BUDGET_BASE = 10 PLAYER_BUDGET_BASE = 17
# Bonus multiplier logarithm base # Bonus multiplier logarithm base
PLAYER_BUDGET_IMPORTANCE_LOG = 2 PLAYER_BUDGET_IMPORTANCE_LOG = 2
@@ -112,11 +116,18 @@ class Game:
continue continue
for event_class, (player_probability, enemy_probability) in EVENT_PROBABILITIES.items(): for event_class, (player_probability, enemy_probability) in EVENT_PROBABILITIES.items():
if event_class == FrontlineAttackEvent or event_class == InfantryTransportEvent or event_class == FrontlinePatrolEvent:
if not Conflict.has_frontline_between(player_cp, enemy_cp):
continue
if self._roll(player_probability, player_cp.base.strength): if self._roll(player_probability, player_cp.base.strength):
if event_class == NavalInterceptEvent and enemy_cp.radials == LAND: if event_class == NavalInterceptEvent and enemy_cp.radials == LAND:
pass pass
else: else:
self.events.append(event_class(self.player, self.enemy, player_cp, enemy_cp, self)) if event_class == BaseAttackEvent and enemy_cp.base.strength > PLAYER_BASEATTACK_THRESHOLD:
pass
else:
self.events.append(event_class(self.player, self.enemy, player_cp, enemy_cp, self))
elif self._roll(enemy_probability, enemy_cp.base.strength): elif self._roll(enemy_probability, enemy_cp.base.strength):
if event_class in enemy_generated_types: if event_class in enemy_generated_types:
continue continue
@@ -130,7 +141,7 @@ class Game:
if event_class == NavalInterceptEvent: if event_class == NavalInterceptEvent:
if player_cp.radials == LAND: if player_cp.radials == LAND:
continue continue
elif event_class == CaptureEvent: elif event_class == BaseAttackEvent:
if enemy_cap_generated: if enemy_cap_generated:
continue continue
if enemy_cp.base.total_armor == 0: if enemy_cp.base.total_armor == 0:
@@ -161,7 +172,7 @@ class Game:
if points_to_spend > 0: if points_to_spend > 0:
unittypes = self.commision_unit_types(cp, for_task) unittypes = self.commision_unit_types(cp, for_task)
d = {random.choice(unittypes): points_to_spend} d = {random.choice(unittypes): points_to_spend}
print("Commision {}: {}".format(cp, d)) logging.info("Commision {}: {}".format(cp, d))
cp.base.commision_units(d) cp.base.commision_units(d)
@property @property
@@ -194,10 +205,13 @@ class Game:
def initiate_event(self, event: Event): def initiate_event(self, event: Event):
assert event in self.events assert event in self.events
logging.info("Generating {} (regular)".format(event))
event.generate() event.generate()
logging.info("Generating {} (quick)".format(event))
event.generate_quick() event.generate_quick()
def finish_event(self, event: Event, debriefing: Debriefing): def finish_event(self, event: Event, debriefing: Debriefing):
logging.info("Finishing event {}".format(event))
event.commit(debriefing) event.commit(debriefing)
if event.is_successfull(debriefing): if event.is_successfull(debriefing):
self.budget += event.bonus() self.budget += event.bonus()
@@ -205,12 +219,16 @@ class Game:
if event in self.events: if event in self.events:
self.events.remove(event) self.events.remove(event)
else: else:
print("finish_event: event not in the events!") logging.info("finish_event: event not in the events!")
def is_player_attack(self, event: Event): def is_player_attack(self, event):
return event.attacker_name == self.player if isinstance(event, Event):
return event.attacker_name == self.player
else:
return event.name == self.player
def pass_turn(self, no_action=False, ignored_cps: typing.Collection[ControlPoint]=None): def pass_turn(self, no_action=False, ignored_cps: typing.Collection[ControlPoint]=None):
logging.info("Pass turn")
for event in self.events: for event in self.events:
event.skip() event.skip()

View File

@@ -6,7 +6,7 @@ from gen.aircraft import *
from gen.aaa import * from gen.aaa import *
from gen.shipgen import * from gen.shipgen import *
from gen.triggergen import * from gen.triggergen import *
from gen.awacsgen import * from gen.airsupportgen import *
from gen.visualgen import * from gen.visualgen import *
from gen.conflictgen import Conflict from gen.conflictgen import Conflict

View File

@@ -6,13 +6,13 @@ from gen.aircraft import *
from gen.aaa import * from gen.aaa import *
from gen.shipgen import * from gen.shipgen import *
from gen.triggergen import * from gen.triggergen import *
from gen.awacsgen import * from gen.airsupportgen import *
from gen.visualgen import * from gen.visualgen import *
from .operation import Operation from .operation import Operation
class CaptureOperation(Operation): class BaseAttackOperation(Operation):
cas = None # type: db.PlaneDict cas = None # type: db.PlaneDict
escort = None # type: db.PlaneDict escort = None # type: db.PlaneDict
intercept = None # type: db.PlaneDict intercept = None # type: db.PlaneDict
@@ -37,7 +37,7 @@ class CaptureOperation(Operation):
self.aa = aa self.aa = aa
def prepare(self, terrain: dcs.terrain.Terrain, is_quick: bool): def prepare(self, terrain: dcs.terrain.Terrain, is_quick: bool):
super(CaptureOperation, self).prepare(terrain, is_quick) super(BaseAttackOperation, self).prepare(terrain, is_quick)
self.defenders_starting_position = None self.defenders_starting_position = None
if self.game.player == self.defender_name: if self.game.player == self.defender_name:
@@ -60,8 +60,8 @@ class CaptureOperation(Operation):
self.airgen.generate_defense(self.intercept, clients=self.defender_clients, at=self.defenders_starting_position) self.airgen.generate_defense(self.intercept, clients=self.defender_clients, at=self.defenders_starting_position)
self.airgen.generate_cas_strikegroup(self.cas, clients=self.attacker_clients, at=self.attackers_starting_position) self.airgen.generate_cas_strikegroup(self.cas, clients=self.attacker_clients, at=self.attackers_starting_position)
self.airgen.generate_strikegroup_escort(self.escort, clients=self.attacker_clients, at=self.attackers_starting_position) self.airgen.generate_attackers_escort(self.escort, clients=self.attacker_clients, at=self.attackers_starting_position)
self.visualgen.generate_target_smokes(self.to_cp) self.visualgen.generate_target_smokes(self.to_cp)
super(CaptureOperation, self).generate() super(BaseAttackOperation, self).generate()

View File

@@ -1,3 +1,5 @@
from itertools import zip_longest
from dcs.terrain import Terrain from dcs.terrain import Terrain
from game import db from game import db
@@ -6,33 +8,36 @@ from gen.aircraft import *
from gen.aaa import * from gen.aaa import *
from gen.shipgen import * from gen.shipgen import *
from gen.triggergen import * from gen.triggergen import *
from gen.awacsgen import * from gen.airsupportgen import *
from gen.visualgen import * from gen.visualgen import *
from gen.conflictgen import Conflict from gen.conflictgen import Conflict
from .operation import Operation from .operation import Operation
class GroundInterceptOperation(Operation): MAX_DISTANCE_BETWEEN_GROUPS = 12000
class FrontlineAttackOperation(Operation):
attackers = None # type: db.ArmorDict
strikegroup = None # type: db.PlaneDict strikegroup = None # type: db.PlaneDict
interceptors = None # type: db.PlaneDict
target = None # type: db.ArmorDict target = None # type: db.ArmorDict
def setup(self, def setup(self,
target: db.ArmorDict, target: db.ArmorDict,
strikegroup: db.PlaneDict, attackers: db.ArmorDict,
interceptors: db.PlaneDict): strikegroup: db.PlaneDict):
self.strikegroup = strikegroup self.strikegroup = strikegroup
self.interceptors = interceptors
self.target = target self.target = target
self.attackers = attackers
def prepare(self, terrain: Terrain, is_quick: bool): def prepare(self, terrain: Terrain, is_quick: bool):
super(GroundInterceptOperation, self).prepare(terrain, is_quick) super(FrontlineAttackOperation, self).prepare(terrain, is_quick)
if self.defender_name == self.game.player: if self.defender_name == self.game.player:
self.attackers_starting_position = None self.attackers_starting_position = None
self.defenders_starting_position = None self.defenders_starting_position = None
conflict = Conflict.ground_intercept_conflict( conflict = Conflict.frontline_cas_conflict(
attacker=self.mission.country(self.attacker_name), attacker=self.mission.country(self.attacker_name),
defender=self.mission.country(self.defender_name), defender=self.mission.country(self.defender_name),
from_cp=self.from_cp, from_cp=self.from_cp,
@@ -44,10 +49,6 @@ class GroundInterceptOperation(Operation):
conflict=conflict) conflict=conflict)
def generate(self): def generate(self):
self.armorgen.generate_vec(self.attackers, self.target)
self.airgen.generate_cas_strikegroup(self.strikegroup, clients=self.attacker_clients, at=self.attackers_starting_position) self.airgen.generate_cas_strikegroup(self.strikegroup, clients=self.attacker_clients, at=self.attackers_starting_position)
super(FrontlineAttackOperation, self).generate()
if self.interceptors:
self.airgen.generate_defense(self.interceptors, clients=self.defender_clients, at=self.defenders_starting_position)
self.armorgen.generate({}, self.target)
super(GroundInterceptOperation, self).generate()

View File

@@ -0,0 +1,58 @@
from itertools import zip_longest
from dcs.terrain import Terrain
from game import db
from gen.armor import *
from gen.aircraft import *
from gen.aaa import *
from gen.shipgen import *
from gen.triggergen import *
from gen.airsupportgen import *
from gen.visualgen import *
from gen.conflictgen import Conflict
from .operation import Operation
MAX_DISTANCE_BETWEEN_GROUPS = 12000
class FrontlinePatrolOperation(Operation):
cas = None # type: db.PlaneDict
escort = None # type: db.PlaneDict
interceptors = None # type: db.PlaneDict
armor_attackers = None # type: db.ArmorDict
armor_defenders = None # type: db.ArmorDict
def setup(self, cas: db.PlaneDict, escort: db.PlaneDict, interceptors: db.PlaneDict, 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=self.mission.country(self.attacker_name),
defender=self.mission.country(self.defender_name),
from_cp=self.from_cp,
to_cp=self.to_cp,
theater=self.game.theater
)
self.initialize(mission=self.mission,
conflict=conflict)
def generate(self):
self.airgen.generate_defenders_cas(self.cas, {}, self.defenders_starting_position)
self.airgen.generate_defenders_escort(self.escort, {}, self.defenders_starting_position)
self.airgen.generate_patrol(self.interceptors, self.attacker_clients, self.attackers_starting_position)
self.armorgen.generate_vec(self.armor_attackers, self.armor_defenders)
super(FrontlinePatrolOperation, self).generate()

View File

@@ -6,7 +6,7 @@ from gen.aircraft import *
from gen.aaa import * from gen.aaa import *
from gen.shipgen import * from gen.shipgen import *
from gen.triggergen import * from gen.triggergen import *
from gen.awacsgen import * from gen.airsupportgen import *
from gen.visualgen import * from gen.visualgen import *
from gen.conflictgen import Conflict from gen.conflictgen import Conflict

View File

@@ -6,14 +6,14 @@ from gen.aircraft import *
from gen.aaa import * from gen.aaa import *
from gen.shipgen import * from gen.shipgen import *
from gen.triggergen import * from gen.triggergen import *
from gen.awacsgen import * from gen.airsupportgen import *
from gen.visualgen import * from gen.visualgen import *
from gen.conflictgen import Conflict from gen.conflictgen import Conflict
from .operation import Operation from .operation import Operation
class GroundAttackOperation(Operation): class InsurgentAttackOperation(Operation):
strikegroup = None # type: db.PlaneDict strikegroup = None # type: db.PlaneDict
target = None # type: db.ArmorDict target = None # type: db.ArmorDict
@@ -24,7 +24,7 @@ class GroundAttackOperation(Operation):
self.target = target self.target = target
def prepare(self, terrain: Terrain, is_quick: bool): def prepare(self, terrain: Terrain, is_quick: bool):
super(GroundAttackOperation, self).prepare(terrain, is_quick) super(InsurgentAttackOperation, self).prepare(terrain, is_quick)
conflict = Conflict.ground_attack_conflict( conflict = Conflict.ground_attack_conflict(
attacker=self.mission.country(self.attacker_name), attacker=self.mission.country(self.attacker_name),
@@ -41,4 +41,4 @@ class GroundAttackOperation(Operation):
self.airgen.generate_defense(self.strikegroup, self.defender_clients, self.defenders_starting_position) self.airgen.generate_defense(self.strikegroup, self.defender_clients, self.defenders_starting_position)
self.armorgen.generate(self.target, {}) self.armorgen.generate(self.target, {})
super(GroundAttackOperation, self).generate() super(InsurgentAttackOperation, self).generate()

View File

@@ -52,7 +52,7 @@ class InterceptOperation(Operation):
self.attackers_starting_position = ship self.attackers_starting_position = ship
self.airgen.generate_transport(self.transport, self.to_cp.at) self.airgen.generate_transport(self.transport, self.to_cp.at)
self.airgen.generate_transport_escort(self.escort, clients=self.defender_clients) self.airgen.generate_defenders_escort(self.escort, clients=self.defender_clients)
self.airgen.generate_interception(self.interceptors, clients=self.attacker_clients, at=self.attackers_starting_position) self.airgen.generate_interception(self.interceptors, clients=self.attacker_clients, at=self.attackers_starting_position)
super(InterceptOperation, self).generate() super(InterceptOperation, self).generate()

View File

@@ -18,7 +18,7 @@ class Operation:
extra_aagen = None # type: ExtraAAConflictGenerator extra_aagen = None # type: ExtraAAConflictGenerator
shipgen = None # type: ShipGenerator shipgen = None # type: ShipGenerator
triggersgen = None # type: TriggersGenerator triggersgen = None # type: TriggersGenerator
awacsgen = None # type: AWACSConflictGenerator awacsgen = None # type: AirSupportConflictGenerator
visualgen = None # type: VisualGenerator visualgen = None # type: VisualGenerator
envgen = None # type: EnvironmentGenerator envgen = None # type: EnvironmentGenerator
@@ -52,7 +52,7 @@ class Operation:
self.airgen = AircraftConflictGenerator(mission, conflict, self.game.settings) self.airgen = AircraftConflictGenerator(mission, conflict, self.game.settings)
self.aagen = AAConflictGenerator(mission, conflict) self.aagen = AAConflictGenerator(mission, conflict)
self.shipgen = ShipGenerator(mission, conflict) self.shipgen = ShipGenerator(mission, conflict)
self.awacsgen = AWACSConflictGenerator(mission, conflict, self.game) self.awacsgen = AirSupportConflictGenerator(mission, conflict, self.game)
self.triggersgen = TriggersGenerator(mission, conflict, self.game) self.triggersgen = TriggersGenerator(mission, conflict, self.game)
self.visualgen = VisualGenerator(mission, conflict, self.game) self.visualgen = VisualGenerator(mission, conflict, self.game)
self.envgen = EnviromentGenerator(mission, conflict, self.game) self.envgen = EnviromentGenerator(mission, conflict, self.game)
@@ -78,12 +78,19 @@ class Operation:
def generate(self): def generate(self):
self.visualgen.generate() self.visualgen.generate()
self.awacsgen.generate(self.is_awacs_enabled)
if self.is_awacs_enabled:
self.awacsgen.generate()
self.extra_aagen.generate() self.extra_aagen.generate()
self.triggersgen.generate(self.is_quick, self.trigger_radius)
if self.game.is_player_attack(self.conflict.attackers_side):
cp = self.conflict.from_cp
else:
cp = self.conflict.to_cp
self.triggersgen.generate(player_cp=cp,
is_quick=self.is_quick,
activation_trigger_radius=self.trigger_radius,
awacs_enabled=self.is_awacs_enabled)
if self.environment_settings is None: if self.environment_settings is None:
self.environment_settings = self.envgen.generate() self.environment_settings = self.envgen.generate()

View File

@@ -6,3 +6,4 @@ class Settings:
night_disabled = False night_disabled = False
multiplier = 1 multiplier = 1
sams = True sams = True
cold_start = False

View File

@@ -1,7 +1,7 @@
from .aaa import * from .aaa import *
from .aircraft import * from .aircraft import *
from .armor import * from .armor import *
from .awacsgen import * from .airsupportgen import *
from .conflictgen import * from .conflictgen import *
from .shipgen import * from .shipgen import *
from .visualgen import * from .visualgen import *

View File

@@ -1,3 +1,5 @@
import logging
from game import db from game import db
from game.settings import Settings from game.settings import Settings
from .conflictgen import * from .conflictgen import *
@@ -14,7 +16,7 @@ SPREAD_DISTANCE_FACTOR = 1, 2
ESCORT_ENGAGEMENT_MAX_DIST = 100000 ESCORT_ENGAGEMENT_MAX_DIST = 100000
WORKAROUND_WAYP_DIST = 1000 WORKAROUND_WAYP_DIST = 1000
WARM_START_HELI_AIRSPEED = 200 WARM_START_HELI_AIRSPEED = 120
WARM_START_HELI_ALT = 1000 WARM_START_HELI_ALT = 1000
WARM_START_ALTITUDE = 3000 WARM_START_ALTITUDE = 3000
@@ -45,6 +47,9 @@ class AircraftConflictGenerator:
self.conflict = conflict self.conflict = conflict
self.escort_targets = [] self.escort_targets = []
def _start_type(self) -> StartType:
return self.settings.cold_start and StartType.Cold or StartType.Warm
def _group_point(self, point) -> Point: def _group_point(self, point) -> Point:
distance = randint( distance = randint(
int(self.conflict.size * SPREAD_DISTANCE_FACTOR[0]), int(self.conflict.size * SPREAD_DISTANCE_FACTOR[0]),
@@ -103,13 +108,14 @@ class AircraftConflictGenerator:
assert count > 0 assert count > 0
assert unit is not None assert unit is not None
logging.info("airgen: {} for {} at {}".format(unit_type, side.id, airport))
return self.m.flight_group_from_airport( return self.m.flight_group_from_airport(
country=side, country=side,
name=name, name=name,
aircraft_type=unit_type, aircraft_type=unit_type,
airport=self.m.terrain.airport_by_id(airport.id), airport=self.m.terrain.airport_by_id(airport.id),
maintask=None, maintask=None,
start_type=StartType.Warm, start_type=self._start_type(),
group_size=count, group_size=count,
parking_slots=None) parking_slots=None)
@@ -117,7 +123,7 @@ class AircraftConflictGenerator:
assert count > 0 assert count > 0
assert unit is not None assert unit is not None
if unit_type in helicopters.helicopter_map: if unit_type in helicopters.helicopter_map.values():
alt = WARM_START_HELI_ALT + random.randint(50, 200) alt = WARM_START_HELI_ALT + random.randint(50, 200)
speed = WARM_START_HELI_AIRSPEED speed = WARM_START_HELI_AIRSPEED
else: else:
@@ -126,6 +132,7 @@ class AircraftConflictGenerator:
pos = Point(at.x + random.randint(100, 200), at.y + random.randint(100, 200)) pos = Point(at.x + random.randint(100, 200), at.y + random.randint(100, 200))
logging.info("airgen: {} for {} at {} at {}".format(unit_type, side.id, alt, speed))
return self.m.flight_group( return self.m.flight_group(
country=side, country=side,
name=name, name=name,
@@ -135,20 +142,21 @@ class AircraftConflictGenerator:
altitude=alt, altitude=alt,
speed=speed, speed=speed,
maintask=None, maintask=None,
start_type=StartType.Warm, start_type=self._start_type(),
group_size=count) group_size=count)
def _generate_at_carrier(self, name: str, side: Country, unit_type: FlyingType, count: int, client_count: int, at: ShipGroup) -> FlyingGroup: def _generate_at_carrier(self, name: str, side: Country, unit_type: FlyingType, count: int, client_count: int, at: ShipGroup) -> FlyingGroup:
assert count > 0 assert count > 0
assert unit is not None assert unit is not None
logging.info("airgen: {} for {} at carrier {}".format(unit_type, side.id, at))
return self.m.flight_group_from_unit( return self.m.flight_group_from_unit(
country=side, country=side,
name=name, name=name,
aircraft_type=unit_type, aircraft_type=unit_type,
pad_group=at, pad_group=at,
maintask=None, maintask=None,
start_type=StartType.Warm, start_type=self._start_type(),
group_size=count) group_size=count)
def _generate_group(self, name: str, side: Country, unit_type: FlyingType, count: int, client_count: int, at: db.StartingPosition): def _generate_group(self, name: str, side: Country, unit_type: FlyingType, count: int, client_count: int, at: db.StartingPosition):
@@ -206,9 +214,11 @@ class AircraftConflictGenerator:
group.task = Escort.name group.task = Escort.name
"""
heading = group.position.heading_between_point(self.conflict.position) heading = group.position.heading_between_point(self.conflict.position)
position = group.position # type: Point position = group.position # type: Point
wayp = group.add_waypoint(position.point_from_heading(heading, WORKAROUND_WAYP_DIST), CAS_ALTITUDE, WARM_START_AIRSPEED) wayp = group.add_waypoint(position.point_from_heading(heading, WORKAROUND_WAYP_DIST), CAS_ALTITUDE, WARM_START_AIRSPEED)
"""
self._setup_group(group, CAP, client_count) self._setup_group(group, CAP, client_count)
for escorted_group, waypoint_index in self.escort_targets: for escorted_group, waypoint_index in self.escort_targets:
@@ -216,7 +226,7 @@ class AircraftConflictGenerator:
if not is_quick: if not is_quick:
waypoint_index += TRIGGER_WAYPOINT_OFFSET waypoint_index += TRIGGER_WAYPOINT_OFFSET
wayp.tasks.append(EscortTaskAction(escorted_group.id, engagement_max_dist=ESCORT_ENGAGEMENT_MAX_DIST, lastwpt=waypoint_index)) group.points[0].tasks.append(EscortTaskAction(escorted_group.id, engagement_max_dist=ESCORT_ENGAGEMENT_MAX_DIST, lastwpt=waypoint_index))
if should_orbit: if should_orbit:
orbit_task = ControlledTask(OrbitAction(ATTACK_CIRCLE_ALT, pattern=OrbitAction.OrbitPattern.Circle)) orbit_task = ControlledTask(OrbitAction(ATTACK_CIRCLE_ALT, pattern=OrbitAction.OrbitPattern.Circle))
@@ -242,11 +252,39 @@ class AircraftConflictGenerator:
at=at and at or self._group_point(self.conflict.air_attackers_location)) at=at and at or self._group_point(self.conflict.air_attackers_location))
waypoint = group.add_waypoint(self.conflict.position, CAS_ALTITUDE, WARM_START_AIRSPEED) waypoint = group.add_waypoint(self.conflict.position, CAS_ALTITUDE, WARM_START_AIRSPEED)
if self.conflict.is_vector:
group.add_waypoint(self.conflict.tail, CAS_ALTITUDE, WARM_START_AIRSPEED)
group.task = CAS.name group.task = CAS.name
self._setup_group(group, CAS, client_count) self._setup_group(group, CAS, client_count)
self.escort_targets.append((group, group.points.index(waypoint))) self.escort_targets.append((group, group.points.index(waypoint)))
self._rtb_for(group, self.conflict.from_cp, at) self._rtb_for(group, self.conflict.from_cp, at)
def generate_defenders_cas(self, defenders: db.PlaneDict, clients: db.PlaneDict, at: db.StartingPosition = None):
assert len(self.escort_targets) == 0
for flying_type, count, client_count in self._split_to_groups(defenders, clients):
group = self._generate_group(
name=namegen.next_unit_name(self.conflict.defenders_side, flying_type),
side=self.conflict.defenders_side,
unit_type=flying_type,
count=count,
client_count=client_count,
at=at and at or self._group_point(self.conflict.air_defenders_location))
location = self._group_point(self.conflict.air_defenders_location)
insertion_point = self.conflict.find_insertion_point(location)
waypoint = group.add_waypoint(insertion_point, CAS_ALTITUDE, WARM_START_AIRSPEED)
if self.conflict.is_vector:
destination_tail = self.conflict.tail.distance_to_point(insertion_point) > self.conflict.position.distance_to_point(insertion_point)
group.add_waypoint(destination_tail and self.conflict.tail or self.conflict.position, CAS_ALTITUDE, WARM_START_AIRSPEED)
group.task = CAS.name
self._setup_group(group, CAS, client_count)
self.escort_targets.append((group, group.points.index(waypoint)))
self._rtb_for(group, self.conflict.to_cp, at)
def generate_ship_strikegroup(self, attackers: db.PlaneDict, clients: db.PlaneDict, target_groups: typing.Collection[ShipGroup], at: db.StartingPosition = None): def generate_ship_strikegroup(self, attackers: db.PlaneDict, clients: db.PlaneDict, target_groups: typing.Collection[ShipGroup], at: db.StartingPosition = None):
assert len(self.escort_targets) == 0 assert len(self.escort_targets) == 0
@@ -268,7 +306,7 @@ class AircraftConflictGenerator:
self.escort_targets.append((group, group.points.index(wayp))) self.escort_targets.append((group, group.points.index(wayp)))
self._rtb_for(group, self.conflict.from_cp, at) self._rtb_for(group, self.conflict.from_cp, at)
def generate_strikegroup_escort(self, attackers: db.PlaneDict, clients: db.PlaneDict, at: db.StartingPosition = None): def generate_attackers_escort(self, attackers: db.PlaneDict, clients: db.PlaneDict, at: db.StartingPosition = None):
for g in self._generate_escort( for g in self._generate_escort(
side=self.conflict.attackers_side, side=self.conflict.attackers_side,
units=attackers, units=attackers,
@@ -278,7 +316,7 @@ class AircraftConflictGenerator:
should_orbit=True): should_orbit=True):
self._rtb_for(g, self.conflict.from_cp, at) self._rtb_for(g, self.conflict.from_cp, at)
def generate_transport_escort(self, escort: db.PlaneDict, clients: db.PlaneDict, at: db.StartingPosition = None): def generate_defenders_escort(self, escort: db.PlaneDict, clients: db.PlaneDict, at: db.StartingPosition = None):
for g in self._generate_escort( for g in self._generate_escort(
side=self.conflict.defenders_side, side=self.conflict.defenders_side,
units=escort, units=escort,
@@ -305,6 +343,24 @@ class AircraftConflictGenerator:
self._setup_group(group, CAP, client_count) self._setup_group(group, CAP, client_count)
self._rtb_for(group, self.conflict.to_cp, at) self._rtb_for(group, self.conflict.to_cp, at)
def generate_patrol(self, patrol: db.PlaneDict, clients: db.PlaneDict, at: db.StartingPosition = None):
for flying_type, count, client_count in self._split_to_groups(patrol, clients):
group = self._generate_group(
name=namegen.next_unit_name(self.conflict.attackers_side, flying_type),
side=self.conflict.attackers_side,
unit_type=flying_type,
count=count,
client_count=client_count,
at=at and at or self._group_point(self.conflict.air_attackers_location))
waypoint = group.add_waypoint(self.conflict.position, WARM_START_ALTITUDE, WARM_START_AIRSPEED)
if self.conflict.is_vector:
group.add_waypoint(self.conflict.tail, WARM_START_ALTITUDE, WARM_START_AIRSPEED)
group.task = CAP.name
self._setup_group(group, CAP, client_count)
self._rtb_for(group, self.conflict.from_cp, at)
def generate_transport(self, transport: db.PlaneDict, destination: Airport): def generate_transport(self, transport: db.PlaneDict, destination: Airport):
assert len(self.escort_targets) == 0 assert len(self.escort_targets) == 0
@@ -341,6 +397,10 @@ class AircraftConflictGenerator:
wayp = group.add_waypoint(self.conflict.position, WARM_START_ALTITUDE, INTERCEPTION_AIRSPEED) wayp = group.add_waypoint(self.conflict.position, WARM_START_ALTITUDE, INTERCEPTION_AIRSPEED)
wayp.tasks.append(EngageTargets(max_distance=INTERCEPT_MAX_DISTANCE)) wayp.tasks.append(EngageTargets(max_distance=INTERCEPT_MAX_DISTANCE))
if self.conflict.is_vector:
group.add_waypoint(self.conflict.tail, CAS_ALTITUDE, WARM_START_ALTITUDE)
self._setup_group(group, CAP, client_count) self._setup_group(group, CAP, client_count)
self._rtb_for(group, self.conflict.from_cp, at) self._rtb_for(group, self.conflict.from_cp, at)

54
gen/airsupportgen.py Normal file
View File

@@ -0,0 +1,54 @@
from game import db
from .conflictgen import *
from .naming import *
from dcs.mission import *
from dcs.unitgroup import *
from dcs.unittype import *
from dcs.task import *
from dcs.terrain.terrain import NoParkingSlotError
TANKER_DISTANCE = 15000
TANKER_ALT = 10000
AWACS_DISTANCE = 150000
AWACS_ALT = 10000
class AirSupportConflictGenerator:
def __init__(self, mission: Mission, conflict: Conflict, game):
self.mission = mission
self.conflict = conflict
self.game = game
def generate(self, is_awacs_enabled):
player_cp = self.conflict.from_cp if self.conflict.from_cp.captured else self.conflict.to_cp
tanker_unit = db.find_unittype(Refueling, self.conflict.attackers_side.name)[0]
tanker_heading = self.conflict.to_cp.position.heading_between_point(self.conflict.from_cp.position)
tanker_position = player_cp.position.point_from_heading(tanker_heading, TANKER_DISTANCE)
tanker_group = self.mission.refuel_flight(
country=self.mission.country(self.game.player),
name=namegen.next_tanker_name(self.mission.country(self.game.player)),
airport=None,
plane_type=tanker_unit,
position=tanker_position,
altitude=TANKER_ALT,
frequency=140,
start_type=StartType.Warm,
tacanchannel="99X",
)
tanker_group.points[0].tasks.append(ActivateBeaconCommand(channel=10, unit_id=tanker_group.id, aa=False))
if is_awacs_enabled:
awacs_unit = db.find_unittype(AWACS, self.conflict.attackers_side.name)[0]
self.mission.awacs_flight(
country=self.mission.country(self.game.player),
name=namegen.next_awacs_name(self.mission.country(self.game.player),),
plane_type=awacs_unit,
altitude=AWACS_ALT,
airport=None,
position=self.conflict.position.random_point_within(AWACS_DISTANCE, AWACS_DISTANCE),
frequency=180,
start_type=StartType.Warm,
)

View File

@@ -1,3 +1,8 @@
import logging
from random import randint
from itertools import zip_longest
from game import db from game import db
from .conflictgen import * from .conflictgen import *
from .naming import * from .naming import *
@@ -11,6 +16,10 @@ from dcs.country import *
SPREAD_DISTANCE_FACTOR = 0.1, 0.3 SPREAD_DISTANCE_FACTOR = 0.1, 0.3
SPREAD_DISTANCE_SIZE_FACTOR = 0.1 SPREAD_DISTANCE_SIZE_FACTOR = 0.1
FRONTLINE_CAS_FIGHTS_COUNT = 4, 8
FRONTLINE_CAS_GROUP_MIN = 1, 2
FRONTLINE_CAS_PADDING = 12000
class ArmorConflictGenerator: class ArmorConflictGenerator:
def __init__(self, mission: Mission, conflict: Conflict): def __init__(self, mission: Mission, conflict: Conflict):
@@ -25,8 +34,9 @@ class ArmorConflictGenerator:
return point.random_point_within(distance, self.conflict.size * SPREAD_DISTANCE_SIZE_FACTOR) return point.random_point_within(distance, self.conflict.size * SPREAD_DISTANCE_SIZE_FACTOR)
def _generate_group(self, side: Country, unit: VehicleType, count: int, at: Point): def _generate_group(self, side: Country, unit: VehicleType, count: int, at: Point, to: Point = None):
for c in range(count): for c in range(count):
logging.info("armorgen: {} for {}".format(unit, side.id))
group = self.m.vehicle_group( group = self.m.vehicle_group(
side, side,
namegen.next_unit_name(side, unit), namegen.next_unit_name(side, unit),
@@ -34,24 +44,65 @@ class ArmorConflictGenerator:
position=self._group_point(at), position=self._group_point(at),
group_size=1, group_size=1,
move_formation=PointAction.OffRoad) move_formation=PointAction.OffRoad)
initial_position = self.conflict.position.point_from_heading(0, 500)
wayp = group.add_waypoint(self._group_point(initial_position)) if not to:
to = self.conflict.position.point_from_heading(0, 500)
wayp = group.add_waypoint(self._group_point(to))
wayp.tasks = [] wayp.tasks = []
def _generate_fight_at(self, attackers: db.ArmorDict, defenders: db.ArmorDict, position: Point):
if attackers:
attack_pos = position.point_from_heading(self.conflict.heading - 90, 8000)
attack_dest = position.point_from_heading(self.conflict.heading + 90, 25000)
for type, count in attackers.items():
self._generate_group(
side=self.conflict.attackers_side,
unit=type,
count=count,
at=attack_pos,
to=attack_dest,
)
if defenders:
def_pos = position.point_from_heading(self.conflict.heading + 90, 4000)
def_dest = position.point_from_heading(self.conflict.heading - 90, 25000)
for type, count in defenders.items():
self._generate_group(
side=self.conflict.defenders_side,
unit=type,
count=count,
at=def_pos,
to=def_dest,
)
def generate(self, attackers: db.ArmorDict, defenders: db.ArmorDict): def generate(self, attackers: db.ArmorDict, defenders: db.ArmorDict):
for type, count in attackers.items(): for type, count in attackers.items():
self._generate_group( self._generate_group(
side=self.conflict.attackers_side, side=self.conflict.attackers_side,
unit=type, unit=type,
count=count, count=count,
at=self.conflict.ground_attackers_location) at=self.conflict.ground_attackers_location)
for type, count in defenders.items(): for type, count in defenders.items():
self._generate_group( self._generate_group(
side=self.conflict.defenders_side, side=self.conflict.defenders_side,
unit=type, unit=type,
count=count, count=count,
at=self.conflict.ground_defenders_location) 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(FRONTLINE_CAS_PADDING, int(self.conflict.distance - FRONTLINE_CAS_PADDING)))
self._generate_fight_at(attacker_group_dict, target_group_dict, position)
def generate_passengers(self, count: int): def generate_passengers(self, count: int):
unit_type = random.choice(db.find_unittype(Nothing, self.conflict.attackers_side.name)) unit_type = random.choice(db.find_unittype(Nothing, self.conflict.attackers_side.name))

View File

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

View File

@@ -1,3 +1,4 @@
import logging
import typing import typing
import pdb import pdb
import dcs import dcs
@@ -20,6 +21,7 @@ AIR_DISTANCE = 40000
CAPTURE_AIR_ATTACKERS_DISTANCE = 25000 CAPTURE_AIR_ATTACKERS_DISTANCE = 25000
CAPTURE_AIR_DEFENDERS_DISTANCE = 60000 CAPTURE_AIR_DEFENDERS_DISTANCE = 60000
CAP_CAS_DISTANCE = 10000, 120000
GROUND_INTERCEPT_SPREAD = 5000 GROUND_INTERCEPT_SPREAD = 5000
GROUND_DISTANCE_FACTOR = 1 GROUND_DISTANCE_FACTOR = 1
@@ -38,8 +40,9 @@ NAVAL_INTERCEPT_DISTANCE_FACTOR = 1
NAVAL_INTERCEPT_DISTANCE_MAX = 40000 NAVAL_INTERCEPT_DISTANCE_MAX = 40000
NAVAL_INTERCEPT_STEP = 5000 NAVAL_INTERCEPT_STEP = 5000
FRONT_SMOKE_MIN_DISTANCE = 5000 FRONTLINE_LENGTH = 80000
FRONT_SMOKE_DISTANCE_FACTOR = 0.5 FRONTLINE_MIN_CP_DISTANCE = 5000
FRONTLINE_DISTANCE_STRENGTH_FACTOR = 0.7
def _opposite_heading(h): def _opposite_heading(h):
@@ -65,28 +68,35 @@ class Conflict:
size = None # type: int size = None # type: int
radials = None # type: typing.List[int] radials = None # type: typing.List[int]
heading = None # type: int
distance = None # type: int
ground_attackers_location = None # type: Point ground_attackers_location = None # type: Point
ground_defenders_location = None # type: Point ground_defenders_location = None # type: Point
air_attackers_location = None # type: Point air_attackers_location = None # type: Point
air_defenders_location = None # type: Point air_defenders_location = None # type: Point
def __init__(self, def __init__(self,
position: Point,
theater: ConflictTheater, theater: ConflictTheater,
from_cp: ControlPoint, from_cp: ControlPoint,
to_cp: ControlPoint, to_cp: ControlPoint,
attackers_side: Country, attackers_side: Country,
defenders_side: Country, defenders_side: Country,
ground_attackers_location: Point, position: Point,
ground_defenders_location: Point, heading=None,
air_attackers_location: Point, distance=None,
air_defenders_location: Point): ground_attackers_location: Point = None,
ground_defenders_location: Point = None,
air_attackers_location: Point = None,
air_defenders_location: Point = None):
self.attackers_side = attackers_side self.attackers_side = attackers_side
self.defenders_side = defenders_side self.defenders_side = defenders_side
self.from_cp = from_cp self.from_cp = from_cp
self.to_cp = to_cp self.to_cp = to_cp
self.theater = theater self.theater = theater
self.position = position self.position = position
self.heading = heading
self.distance = distance
self.size = to_cp.size self.size = to_cp.size
self.radials = to_cp.radials self.radials = to_cp.radials
self.ground_attackers_location = ground_attackers_location self.ground_attackers_location = ground_attackers_location
@@ -94,15 +104,73 @@ class Conflict:
self.air_attackers_location = air_attackers_location self.air_attackers_location = air_attackers_location
self.air_defenders_location = air_defenders_location self.air_defenders_location = air_defenders_location
@property
def center(self) -> Point:
return self.position.point_from_heading(self.heading, self.distance / 2)
@property
def tail(self) -> Point:
return self.position.point_from_heading(self.heading, self.distance)
@property
def is_vector(self) -> bool:
return self.heading is not None
@property
def opposite_heading(self) -> int:
return _heading_sum(self.heading, 180)
@property @property
def to_size(self): def to_size(self):
return self.to_cp.size * GROUND_DISTANCE_FACTOR return self.to_cp.size * GROUND_DISTANCE_FACTOR
def find_insertion_point(self, other_point: Point) -> Point:
dx = self.position.x - self.tail.x
dy = self.position.y - self.tail.y
dr2 = float(dx ** 2 + dy ** 2)
lerp = ((other_point.x - self.tail.x) * dx + (other_point.y - self.tail.y) * dy) / dr2
if lerp < 0:
lerp = 0
elif lerp > 1:
lerp = 1
x = lerp * dx + self.tail.x
y = lerp * dy + self.tail.y
return Point(x, y)
@classmethod @classmethod
def frontline_position(cls, from_cp: ControlPoint, to_cp: ControlPoint): def has_frontline_between(cls, from_cp: ControlPoint, to_cp: ControlPoint) -> bool:
distance = max(from_cp.position.distance_to_point(to_cp.position) * FRONT_SMOKE_DISTANCE_FACTOR * to_cp.base.strength, FRONT_SMOKE_MIN_DISTANCE) return from_cp.has_frontline and to_cp.has_frontline
@classmethod
def frontline_position(cls, from_cp: ControlPoint, to_cp: ControlPoint) -> typing.Tuple[Point, int]:
distance = max(from_cp.position.distance_to_point(to_cp.position) * FRONTLINE_DISTANCE_STRENGTH_FACTOR * to_cp.base.strength, FRONTLINE_MIN_CP_DISTANCE)
heading = to_cp.position.heading_between_point(from_cp.position) heading = to_cp.position.heading_between_point(from_cp.position)
return to_cp.position.point_from_heading(heading, distance) return to_cp.position.point_from_heading(heading, distance), heading
@classmethod
def frontline_vector(cls, from_cp: ControlPoint, to_cp: ControlPoint, theater: ConflictTheater) -> typing.Tuple[Point, int, int]:
center_position, heading = cls.frontline_position(from_cp, to_cp)
left_position = center_position
for offset in range(0, int(FRONTLINE_LENGTH / 2), 1000):
pos = center_position.point_from_heading(_heading_sum(heading, -90), offset)
if not theater.is_on_land(pos):
break
else:
left_position = pos
right_position = center_position
for offset in range(0, int(FRONTLINE_LENGTH / 2), 1000):
pos = center_position.point_from_heading(_heading_sum(heading, 90), offset)
if not theater.is_on_land(pos):
break
else:
right_position = pos
return left_position, _heading_sum(heading, 90), right_position.distance_to_point(left_position)
@classmethod @classmethod
def _find_ground_location(cls, initial: Point, max_distance: int, heading: int, theater: ConflictTheater) -> Point: def _find_ground_location(cls, initial: Point, max_distance: int, heading: int, theater: ConflictTheater) -> Point:
@@ -115,7 +183,7 @@ class Conflict:
initial = initial.point_from_heading(heading, 800) initial = initial.point_from_heading(heading, 800)
print("Didn't find ground position!") logging.info("Didn't find ground position!")
return None return None
@classmethod @classmethod
@@ -189,25 +257,45 @@ class Conflict:
) )
@classmethod @classmethod
def ground_intercept_conflict(cls, attacker: Country, defender: Country, from_cp: ControlPoint, to_cp: ControlPoint, theater: ConflictTheater): def frontline_cas_conflict(cls, attacker: Country, defender: Country, from_cp: ControlPoint, to_cp: ControlPoint, theater: ConflictTheater):
heading = to_cp.position.heading_between_point(from_cp.position) assert cls.has_frontline_between(from_cp, to_cp)
initial_location = cls.frontline_position(from_cp, to_cp).random_point_within(GROUND_INTERCEPT_SPREAD) position, heading, distance = cls.frontline_vector(from_cp, to_cp, theater)
position = Conflict._find_ground_location(initial_location, GROUND_INTERCEPT_SPREAD, heading, theater)
if not position:
heading = to_cp.find_radial(to_cp.position.heading_between_point(from_cp.position))
position = to_cp.position.point_from_heading(heading, to_cp.size * GROUND_DISTANCE_FACTOR)
return cls( return cls(
position=position, position=position,
heading=heading,
distance=distance,
theater=theater, theater=theater,
from_cp=from_cp, from_cp=from_cp,
to_cp=to_cp, to_cp=to_cp,
attackers_side=attacker, attackers_side=attacker,
defenders_side=defender, defenders_side=defender,
ground_attackers_location=None, ground_attackers_location=None,
ground_defenders_location=position, ground_defenders_location=None,
air_attackers_location=position.point_from_heading(random.randint(*INTERCEPT_ATTACKERS_HEADING) + heading, AIR_DISTANCE), air_attackers_location=position.point_from_heading(random.randint(*INTERCEPT_ATTACKERS_HEADING) + heading, AIR_DISTANCE),
air_defenders_location=position.point_from_heading(random.randint(*INTERCEPT_ATTACKERS_HEADING) + _opposite_heading(heading), AIR_DISTANCE) air_defenders_location=position.point_from_heading(random.randint(*INTERCEPT_ATTACKERS_HEADING) + _opposite_heading(heading), AIR_DISTANCE),
)
@classmethod
def frontline_cap_conflict(cls, attacker: Country, defender: Country, from_cp: ControlPoint, to_cp: ControlPoint, theater: ConflictTheater):
assert cls.has_frontline_between(from_cp, to_cp)
position, heading, distance = cls.frontline_vector(from_cp, to_cp, theater)
attack_position = position.point_from_heading(heading, randint(0, int(distance)))
attackers_position = attack_position.point_from_heading(heading - 90, AIR_DISTANCE)
defenders_position = attack_position.point_from_heading(heading + 90, random.randint(*CAP_CAS_DISTANCE))
return cls(
position=position,
heading=heading,
distance=distance,
theater=theater,
from_cp=from_cp,
to_cp=to_cp,
attackers_side=attacker,
defenders_side=defender,
air_attackers_location=attackers_position,
air_defenders_location=defenders_position,
) )
@classmethod @classmethod
@@ -261,8 +349,7 @@ class Conflict:
@classmethod @classmethod
def transport_conflict(cls, attacker: Country, defender: Country, from_cp: ControlPoint, to_cp: ControlPoint, theater: ConflictTheater): def transport_conflict(cls, attacker: Country, defender: Country, from_cp: ControlPoint, to_cp: ControlPoint, theater: ConflictTheater):
frontline_position = cls.frontline_position(from_cp, to_cp) frontline_position, heading = cls.frontline_position(from_cp, to_cp)
heading = to_cp.position.heading_between_point(from_cp.position)
initial_dest = frontline_position.point_from_heading(heading, TRANSPORT_FRONTLINE_DIST) initial_dest = frontline_position.point_from_heading(heading, TRANSPORT_FRONTLINE_DIST)
dest = cls._find_ground_location(initial_dest, from_cp.position.distance_to_point(to_cp.position) / 3, heading, theater) dest = cls._find_ground_location(initial_dest, from_cp.position.distance_to_point(to_cp.position) / 3, heading, theater)
if not dest: if not dest:

View File

@@ -1,3 +1,4 @@
import logging
import typing import typing
import random import random
from datetime import datetime, timedelta, time from datetime import datetime, timedelta, time
@@ -18,7 +19,7 @@ from gen import *
WEATHER_CLOUD_BASE = 2000, 3000 WEATHER_CLOUD_BASE = 2000, 3000
WEATHER_CLOUD_DENSITY = 1, 8 WEATHER_CLOUD_DENSITY = 1, 8
WEATHER_CLOUD_THICKNESS = 100, 400 WEATHER_CLOUD_THICKNESS = 100, 400
WEATHER_CLOUD_BASE_MIN = 1200 WEATHER_CLOUD_BASE_MIN = 1600
RANDOM_TIME = { RANDOM_TIME = {
"night": 5, "night": 5,
@@ -28,10 +29,10 @@ RANDOM_TIME = {
} }
RANDOM_WEATHER = { RANDOM_WEATHER = {
1: 5, # heavy rain 1: 0, # heavy rain
2: 15, # rain 2: 10, # rain
3: 25, # dynamic 3: 20, # dynamic
4: 35, # clear 4: 30, # clear
5: 100, # random 5: 100, # random
} }
@@ -68,7 +69,7 @@ class EnviromentGenerator:
weather_type = k weather_type = k
break break
print("generated weather {}".format(weather_type)) logging.info("generated weather {}".format(weather_type))
if weather_type == 1: if weather_type == 1:
self.mission.weather.heavy_rain() self.mission.weather.heavy_rain()
elif weather_type == 2: elif weather_type == 2:
@@ -90,8 +91,13 @@ class EnviromentGenerator:
self.mission.weather.wind_at_8000 = Wind(wind_direction, wind_speed * 3) self.mission.weather.wind_at_8000 = Wind(wind_direction, wind_speed * 3)
if self.mission.weather.clouds_density > 0: if self.mission.weather.clouds_density > 0:
# sometimes clouds are randomized way too low and need to be fixed
self.mission.weather.clouds_base = max(self.mission.weather.clouds_base, WEATHER_CLOUD_BASE_MIN) self.mission.weather.clouds_base = max(self.mission.weather.clouds_base, WEATHER_CLOUD_BASE_MIN)
if self.mission.weather.wind_at_ground == 0:
# frontline smokes look silly w/o any wind
self.mission.weather.wind_at_ground = random.randint(1, 2)
def generate(self) -> EnvironmentSettings: def generate(self) -> EnvironmentSettings:
self._gen_random_time() self._gen_random_time()
self._gen_random_weather() self._gen_random_weather()

View File

@@ -15,6 +15,10 @@ class NameGenerator:
self.number += 1 self.number += 1
return "awacs|{}|{}|0|".format(country.id, self.number) return "awacs|{}|{}|0|".format(country.id, self.number)
def next_tanker_name(self, country):
self.number += 1
return "tanker|{}|{}|0|".format(country.id, self.number)
def next_carrier_name(self, country): def next_carrier_name(self, country):
self.number += 1 self.number += 1
return "carrier|{}|{}|0|".format(country.id, self.number) return "carrier|{}|{}|0|".format(country.id, self.number)

View File

@@ -1,3 +1,5 @@
import logging
from game import db from game import db
from .conflictgen import * from .conflictgen import *
from .naming import * from .naming import *
@@ -15,15 +17,20 @@ class ShipGenerator:
self.conflict = conflict self.conflict = conflict
def generate_carrier(self, type: ShipType, country: str, at: Point) -> ShipGroup: def generate_carrier(self, type: ShipType, country: str, at: Point) -> ShipGroup:
return self.m.ship_group( group = self.m.ship_group(
country=self.m.country(country), country=self.m.country(country),
name=namegen.next_carrier_name(self.m.country(country)), name=namegen.next_carrier_name(self.m.country(country)),
_type=type, _type=type,
position=at) position=at)
group.points[0].tasks.append(ActivateBeaconCommand(unit_id=group.id, channel=20, callsign="SHDW", aa=False))
group.points[0].tasks.append(ActivateICLSCommand(unit_id=group.id, channel=1))
return group
def generate_cargo(self, units: db.ShipDict) -> typing.Collection[ShipGroup]: def generate_cargo(self, units: db.ShipDict) -> typing.Collection[ShipGroup]:
groups = [] groups = []
for unit_type, unit_count in units.items(): for unit_type, unit_count in units.items():
logging.info("shipgen: {} ({}) for {}".format(unit_type, unit_count, self.conflict.defenders_side))
group = self.m.ship_group( group = self.m.ship_group(
country=self.conflict.defenders_side, country=self.conflict.defenders_side,
name=namegen.next_unit_name(self.conflict.defenders_side, unit_type), name=namegen.next_unit_name(self.conflict.defenders_side, unit_type),

View File

@@ -38,7 +38,7 @@ class TriggersGenerator:
self.conflict = conflict self.conflict = conflict
self.game = game self.game = game
def _gen_activation_trigger(self, radius: int, player_coalition: str, enemy_coalition: str): def _gen_activation_trigger(self, radius: int, player_cp: ControlPoint, player_coalition: str, enemy_coalition: str):
activate_by_trigger = [] activate_by_trigger = []
for coalition_name, coalition in self.mission.coalition.items(): for coalition_name, coalition in self.mission.coalition.items():
for country in coalition.countries.values(): for country in coalition.countries.values():
@@ -51,7 +51,7 @@ class TriggersGenerator:
vehicle_group.late_activation = True vehicle_group.late_activation = True
activate_by_trigger.append(vehicle_group) activate_by_trigger.append(vehicle_group)
conflict_distance = self.conflict.from_cp.position.distance_to_point(self.conflict.position) conflict_distance = player_cp.position.distance_to_point(self.conflict.position)
minimum_radius = max(conflict_distance - TRIGGER_MIN_DISTANCE_FROM_START, TRIGGER_RADIUS_MINIMUM) minimum_radius = max(conflict_distance - TRIGGER_MIN_DISTANCE_FROM_START, TRIGGER_RADIUS_MINIMUM)
if minimum_radius < 0: if minimum_radius < 0:
minimum_radius = 0 minimum_radius = 0
@@ -67,16 +67,16 @@ class TriggersGenerator:
self.mission.triggerrules.triggers.append(activation_trigger) self.mission.triggerrules.triggers.append(activation_trigger)
def _gen_push_trigger(self, player_coalition: str): def _gen_push_trigger(self, player_cp: ControlPoint, player_coalition: str):
push_by_trigger = [] push_by_trigger = []
for coalition_name, coalition in self.mission.coalition.items(): for coalition_name, coalition in self.mission.coalition.items():
for country in coalition.countries.values(): for country in coalition.countries.values():
if coalition_name == player_coalition: if coalition_name == player_coalition:
for plane_group in country.plane_group + country.helicopter_group: for plane_group in country.plane_group + country.helicopter_group:
if plane_group.task == AWACS.name: if plane_group.task == AWACS.name or plane_group.task == Refueling.name:
continue continue
regroup_heading = self.conflict.to_cp.position.heading_between_point(self.conflict.from_cp.position) regroup_heading = self.conflict.to_cp.position.heading_between_point(player_cp.position)
pos1 = plane_group.position.point_from_heading(regroup_heading, REGROUP_ZONE_DISTANCE) pos1 = plane_group.position.point_from_heading(regroup_heading, REGROUP_ZONE_DISTANCE)
pos2 = plane_group.position.point_from_heading(regroup_heading, REGROUP_ZONE_DISTANCE+5000) pos2 = plane_group.position.point_from_heading(regroup_heading, REGROUP_ZONE_DISTANCE+5000)
@@ -99,7 +99,7 @@ class TriggersGenerator:
plane_group.add_trigger_action(SwitchWaypoint(to_waypoint=4)) plane_group.add_trigger_action(SwitchWaypoint(to_waypoint=4))
push_by_trigger.append(plane_group) push_by_trigger.append(plane_group)
push_trigger_zone = self.mission.triggers.add_triggerzone(self.conflict.from_cp.position, PUSH_TRIGGER_SIZE, name="Push zone") push_trigger_zone = self.mission.triggers.add_triggerzone(player_cp.position, PUSH_TRIGGER_SIZE, name="Push zone")
push_trigger = TriggerOnce(Event.NoEvent, "Push trigger") push_trigger = TriggerOnce(Event.NoEvent, "Push trigger")
for group in push_by_trigger: for group in push_by_trigger:
@@ -136,7 +136,7 @@ class TriggersGenerator:
for vehicle_group in country.vehicle_group: for vehicle_group in country.vehicle_group:
vehicle_group.set_skill(Skill(skill_level)) vehicle_group.set_skill(Skill(skill_level))
def generate(self, is_quick: bool, activation_trigger_radius: int): def generate(self, player_cp: ControlPoint, is_quick: bool, activation_trigger_radius: int, awacs_enabled: bool):
player_coalition = self.game.player == "USA" and "blue" or "red" player_coalition = self.game.player == "USA" and "blue" or "red"
enemy_coalition = player_coalition == "blue" and "red" or "blue" enemy_coalition = player_coalition == "blue" and "red" or "blue"
@@ -146,8 +146,21 @@ class TriggersGenerator:
self._set_skill(player_coalition, enemy_coalition) self._set_skill(player_coalition, enemy_coalition)
self._set_allegiances(player_coalition, enemy_coalition) self._set_allegiances(player_coalition, enemy_coalition)
description = ""
description += "FREQUENCIES:"
description += "\nFlight: 251 MHz AM"
description += "\nTanker: 10X/140 MHz"
if awacs_enabled:
description += "\nAWACS: 180 MHz"
if self.conflict.from_cp.is_global or self.conflict.to_cp.is_global:
description += "\nCarrier: 20X/ICLS CHAN1"
self.mission.set_description_text(description)
if not is_quick: if not is_quick:
# TODO: waypoint parts of this should not be post-hacked but added in airgen # TODO: waypoint parts of this should not be post-hacked but added in airgen
self._gen_activation_trigger(activation_trigger_radius, player_coalition, enemy_coalition) self._gen_activation_trigger(activation_trigger_radius, player_cp, player_coalition, enemy_coalition)
self._gen_push_trigger(player_coalition) self._gen_push_trigger(player_cp, player_coalition)

View File

@@ -62,7 +62,6 @@ def __monkey_static_dict(self: Static):
__original_static_dict = Static.dict __original_static_dict = Static.dict
Static.dict = __monkey_static_dict Static.dict = __monkey_static_dict
FRONT_SMOKE_LENGTH = 80000
FRONT_SMOKE_SPACING = 800 FRONT_SMOKE_SPACING = 800
FRONT_SMOKE_RANDOM_SPREAD = 4000 FRONT_SMOKE_RANDOM_SPREAD = 4000
FRONT_SMOKE_TYPE_CHANCES = { FRONT_SMOKE_TYPE_CHANCES = {
@@ -99,11 +98,10 @@ class VisualGenerator:
def _generate_frontline_smokes(self): def _generate_frontline_smokes(self):
for from_cp, to_cp in self.game.theater.conflicts(): for from_cp, to_cp in self.game.theater.conflicts():
heading = to_cp.position.heading_between_point(from_cp.position) point, heading = Conflict.frontline_position(from_cp, to_cp)
point = Conflict.frontline_position(from_cp, to_cp) plane_start = point.point_from_heading(turn_heading(heading, 90), FRONTLINE_LENGTH / 2)
plane_start = point.point_from_heading(turn_heading(heading, 90), FRONT_SMOKE_LENGTH / 2)
for offset in range(0, FRONT_SMOKE_LENGTH, FRONT_SMOKE_SPACING): for offset in range(0, FRONTLINE_LENGTH, FRONT_SMOKE_SPACING):
position = plane_start.point_from_heading(turn_heading(heading, - 90), offset) position = plane_start.point_from_heading(turn_heading(heading, - 90), offset)
for k, v in FRONT_SMOKE_TYPE_CHANCES.items(): for k, v in FRONT_SMOKE_TYPE_CHANCES.items():

Binary file not shown.

Before

Width:  |  Height:  |  Size: 80 KiB

After

Width:  |  Height:  |  Size: 62 KiB

1
resources/tools/dcs Symbolic link
View File

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

View File

@@ -0,0 +1,42 @@
import sys
from dcs.lua.parse import *
from dcs.lua.serialize import *
a = loads(open(sys.argv[1], "r").read())
b = loads(open(sys.argv[2], "r").read())
def get(a, k):
b = a
for x in k.strip().split(" "):
if isinstance(a, dict):
y = a
a = a.get(x, None)
if a is None:
try:
a = y.get(int(x), None)
except:
pass
else:
break
if a is None:
pass
return a
def cycle(kk, ref, v):
if isinstance(v, dict):
for k, v in v.items():
cycle(kk + " " + str(k), ref, v)
elif isinstance(v, list):
for i, v in enumerate(v):
cycle(kk + " " + str(i), ref, v)
else:
if get(ref, kk) != v:
print(kk)
print(v)
print(get(ref, kk))
cycle("", a, b)

View File

@@ -1 +1 @@
py.exe __init__.py %UserProfile% > logs.txt 2>&1 py.exe __init__.py "%UserProfile%"

View File

@@ -1,3 +1,4 @@
import logging
import typing import typing
import math import math
import itertools import itertools
@@ -53,7 +54,7 @@ class Base:
def _find_best_unit(self, dict, for_type: Task, count: int) -> typing.Dict: def _find_best_unit(self, dict, for_type: Task, count: int) -> typing.Dict:
if count <= 0: if count <= 0:
print("{}: no units for {}".format(self, for_type)) logging.info("{}: no units for {}".format(self, for_type))
return {} return {}
sorted_units = [key for key in dict.keys() if key in db.UNIT_BY_TASK[for_type]] sorted_units = [key for key in dict.keys() if key in db.UNIT_BY_TASK[for_type]]
@@ -74,7 +75,7 @@ class Base:
assert result_unit_count > 0 assert result_unit_count > 0
result[unit_type] = result.get(unit_type, 0) + result_unit_count result[unit_type] = result.get(unit_type, 0) + result_unit_count
print("{} for {} ({}): {}".format(self, for_type, count, result)) logging.info("{} for {} ({}): {}".format(self, for_type, count, result))
return result return result
def _find_best_planes(self, for_type: Task, count: int) -> typing.Dict[PlaneType, int]: def _find_best_planes(self, for_type: Task, count: int) -> typing.Dict[PlaneType, int]:
@@ -150,7 +151,7 @@ class Base:
return min(min(max(count, PLANES_SCRAMBLE_MIN_BASE), int(PLANES_SCRAMBLE_MAX_BASE * multiplier)), count) return min(min(max(count, PLANES_SCRAMBLE_MIN_BASE), int(PLANES_SCRAMBLE_MAX_BASE * multiplier)), count)
def assemble_count(self): def assemble_count(self):
return int(self.total_armor * min(self.strength + 0.5, 1)) return int(self.total_armor * 0.5)
def assemble_aa_count(self) -> int: def assemble_aa_count(self) -> int:
if self.strength > STRENGTH_AA_ASSEMBLE_MIN: if self.strength > STRENGTH_AA_ASSEMBLE_MIN:
@@ -167,11 +168,12 @@ class Base:
def scramble_interceptors(self, multiplier: float) -> typing.Dict[PlaneType, int]: def scramble_interceptors(self, multiplier: float) -> typing.Dict[PlaneType, int]:
return self._find_best_planes(CAP, self.scramble_count(multiplier, CAP)) return self._find_best_planes(CAP, self.scramble_count(multiplier, CAP))
def assemble_cap(self) -> typing.Dict[Armor, int]: def assemble_attack(self) -> typing.Dict[Armor, int]:
return self._find_best_armor(PinpointStrike, self.assemble_count()) return self._find_best_armor(PinpointStrike, self.assemble_count())
def assemble_defense(self) -> typing.Dict[Armor, int]: def assemble_defense(self) -> typing.Dict[Armor, int]:
return self._find_best_armor(PinpointStrike, self.assemble_count()) count = int(self.total_armor * min(self.strength + 0.5, 1))
return self._find_best_armor(PinpointStrike, count)
def assemble_aa(self, count=None) -> typing.Dict[AirDefence, int]: def assemble_aa(self, count=None) -> typing.Dict[AirDefence, int]:
return self._find_best_unit(self.aa, AirDefence, count and min(count, self.total_aa) or self.assemble_aa_count()) return self._find_best_unit(self.aa, AirDefence, count and min(count, self.total_aa) or self.assemble_aa_count())

View File

@@ -10,10 +10,11 @@ class ControlPoint:
connected_points = [] # type: typing.List[ControlPoint] connected_points = [] # type: typing.List[ControlPoint]
position = None # type: Point position = None # type: Point
captured = False captured = False
base: None # type: theater.base.Base has_frontline = True
at: None # type: db.StartPosition base = None # type: theater.base.Base
at = None # type: db.StartPosition
def __init__(self, name: str, position: Point, at, radials: typing.Collection[int], size: int, importance: int): def __init__(self, name: str, position: Point, at, radials: typing.Collection[int], size: int, importance: int, has_frontline=True):
import theater.base import theater.base
self.name = " ".join(re.split(r" |-", name)[:2]) self.name = " ".join(re.split(r" |-", name)[:2])
@@ -24,14 +25,15 @@ class ControlPoint:
self.size = size self.size = size
self.importance = importance self.importance = importance
self.captured = False self.captured = False
self.has_frontline = has_frontline
self.radials = radials self.radials = radials
self.connected_points = [] self.connected_points = []
self.base = theater.base.Base() self.base = theater.base.Base()
@classmethod @classmethod
def from_airport(cls, airport: Airport, radials: typing.Collection[int], size: int, importance: int): def from_airport(cls, airport: Airport, radials: typing.Collection[int], size: int, importance: int, has_frontline=True):
assert airport assert airport
return cls(airport.name, airport.position, airport, radials, size, importance) return cls(airport.name, airport.position, airport, radials, size, importance, has_frontline)
@classmethod @classmethod
def carrier(cls, name: str, at: Point): def carrier(cls, name: str, at: Point):

View File

@@ -9,8 +9,8 @@ from .landmap import load_poly
class PersianGulfTheater(ConflictTheater): class PersianGulfTheater(ConflictTheater):
terrain = dcs.terrain.PersianGulf() terrain = dcs.terrain.PersianGulf()
overview_image = "persiangulf.gif" overview_image = "persiangulf.gif"
reference_points = {(persiangulf.Sir_Abu_Nuayr.position.x, persiangulf.Sir_Abu_Nuayr.position.y): (351, 115), reference_points = {(persiangulf.Sir_Abu_Nuayr.position.x, persiangulf.Sir_Abu_Nuayr.position.y): (321, 145),
(persiangulf.Sirri_Island.position.x, persiangulf.Sirri_Island.position.y): (389, 22), } (persiangulf.Sirri_Island.position.x, persiangulf.Sirri_Island.position.y): (347, 82), }
landmap_poly = load_poly("resources\\gulflandmap.p") landmap_poly = load_poly("resources\\gulflandmap.p")
daytime_map = { daytime_map = {
"dawn": (6, 8), "dawn": (6, 8),
@@ -19,27 +19,29 @@ class PersianGulfTheater(ConflictTheater):
"night": (0, 5), "night": (0, 5),
} }
al_dhafra = ControlPoint.from_airport(persiangulf.Al_Dhafra_AB, LAND, SIZE_BIG, IMPORTANCE_LOW) al_dhafra = ControlPoint.from_airport(persiangulf.Al_Dhafra_AB, LAND, SIZE_BIG, IMPORTANCE_HIGH)
al_maktoum = ControlPoint.from_airport(persiangulf.Al_Maktoum_Intl, LAND, SIZE_BIG, IMPORTANCE_LOW) al_maktoum = ControlPoint.from_airport(persiangulf.Al_Maktoum_Intl, LAND, SIZE_BIG, IMPORTANCE_HIGH)
al_minhad = ControlPoint.from_airport(persiangulf.Al_Minhad_AB, LAND, SIZE_REGULAR, IMPORTANCE_LOW) al_minhad = ControlPoint.from_airport(persiangulf.Al_Minhad_AB, LAND, SIZE_REGULAR, IMPORTANCE_HIGH)
sir_abu_nuayr = ControlPoint.from_airport(persiangulf.Sir_Abu_Nuayr, [0, 330], SIZE_SMALL, 1.1) sir_abu_nuayr = ControlPoint.from_airport(persiangulf.Sir_Abu_Nuayr, [0, 330], SIZE_SMALL, 1.3, has_frontline=False)
dubai = ControlPoint.from_airport(persiangulf.Dubai_Intl, COAST_DL_E, SIZE_LARGE, 1.3) dubai = ControlPoint.from_airport(persiangulf.Dubai_Intl, COAST_DL_E, SIZE_LARGE, IMPORTANCE_HIGH)
sharjah = ControlPoint.from_airport(persiangulf.Sharjah_Intl, LAND, SIZE_BIG, 1.2) sharjah = ControlPoint.from_airport(persiangulf.Sharjah_Intl, LAND, SIZE_BIG, 1.3)
fujairah = ControlPoint.from_airport(persiangulf.Fujairah_Intl, COAST_V_W, SIZE_REGULAR, 1.3) fujairah = ControlPoint.from_airport(persiangulf.Fujairah_Intl, COAST_V_W, SIZE_REGULAR, IMPORTANCE_HIGH)
khasab = ControlPoint.from_airport(persiangulf.Khasab, LAND, SIZE_SMALL, 1.3) khasab = ControlPoint.from_airport(persiangulf.Khasab, LAND, SIZE_SMALL, IMPORTANCE_HIGH)
sirri = ControlPoint.from_airport(persiangulf.Sirri_Island, COAST_DL_W, SIZE_REGULAR, 1.2) sirri = ControlPoint.from_airport(persiangulf.Sirri_Island, COAST_DL_W, SIZE_REGULAR, IMPORTANCE_LOW, has_frontline=False)
abu_musa = ControlPoint.from_airport(persiangulf.Abu_Musa_Island_Airport, LAND, SIZE_SMALL, 1.0) abu_musa = ControlPoint.from_airport(persiangulf.Abu_Musa_Island_Airport, LAND, SIZE_SMALL, IMPORTANCE_MEDIUM, has_frontline=False)
tunb_island = ControlPoint.from_airport(persiangulf.Tunb_Island_AFB, [0, 270, 330], SIZE_REGULAR, 1.1) tunb_island = ControlPoint.from_airport(persiangulf.Tunb_Island_AFB, [0, 270, 330], IMPORTANCE_LOW, 1.1, has_frontline=False)
tunb_kochak = ControlPoint.from_airport(persiangulf.Tunb_Kochak, [135, 180], SIZE_SMALL, IMPORTANCE_HIGH) tunb_kochak = ControlPoint.from_airport(persiangulf.Tunb_Kochak, [135, 180], SIZE_SMALL, 1.2, has_frontline=False)
bandar_lengeh = ControlPoint.from_airport(persiangulf.Bandar_Lengeh, [270, 315, 0, 45], SIZE_SMALL, IMPORTANCE_HIGH) bandar_lengeh = ControlPoint.from_airport(persiangulf.Bandar_Lengeh, [270, 315, 0, 45], SIZE_SMALL, 1.1)
qeshm = ControlPoint.from_airport(persiangulf.Qeshm_Island, [270, 315, 0, 45, 90, 135, 180], SIZE_SMALL, 1.1) qeshm = ControlPoint.from_airport(persiangulf.Qeshm_Island, [270, 315, 0, 45, 90, 135, 180], SIZE_SMALL, 1.3, has_frontline=False)
havadarya = ControlPoint.from_airport(persiangulf.Havadarya, COAST_DL_W, SIZE_REGULAR, IMPORTANCE_HIGH) havadarya = ControlPoint.from_airport(persiangulf.Havadarya, COAST_DL_W, SIZE_REGULAR, 1.2)
bandar_abbas = ControlPoint.from_airport(persiangulf.Bandar_Abbas_Intl, LAND, SIZE_BIG, IMPORTANCE_HIGH) bandar_abbas = ControlPoint.from_airport(persiangulf.Bandar_Abbas_Intl, LAND, SIZE_BIG, 1.3)
lar = ControlPoint.from_airport(persiangulf.Lar_Airbase, LAND, SIZE_REGULAR, IMPORTANCE_HIGH) lar = ControlPoint.from_airport(persiangulf.Lar_Airbase, LAND, SIZE_REGULAR, 1.1)
shiraz = ControlPoint.from_airport(persiangulf.Shiraz_International_Airport, LAND, SIZE_BIG, IMPORTANCE_LOW)
kerman = ControlPoint.from_airport(persiangulf.Kerman_Airport, LAND, SIZE_BIG, IMPORTANCE_LOW)
west_carrier = ControlPoint.carrier("East carrier", Point(-100531.972946, 60939.275818)) west_carrier = ControlPoint.carrier("East carrier", Point(-100531.972946, 60939.275818))
@@ -61,15 +63,17 @@ class PersianGulfTheater(ConflictTheater):
self.add_controlpoint(self.tunb_island, connected_to=[self.khasab, self.qeshm, self.tunb_kochak]) self.add_controlpoint(self.tunb_island, connected_to=[self.khasab, self.qeshm, self.tunb_kochak])
self.add_controlpoint(self.bandar_lengeh, connected_to=[self.tunb_island, self.lar, self.qeshm]) self.add_controlpoint(self.bandar_lengeh, connected_to=[self.tunb_island, self.lar, self.qeshm])
self.add_controlpoint(self.qeshm, connected_to=[self.bandar_lengeh, self.havadarya, self.tunb_island]) self.add_controlpoint(self.qeshm, connected_to=[self.bandar_lengeh, self.havadarya, self.tunb_island, self.lar])
self.add_controlpoint(self.havadarya, connected_to=[self.lar, self.qeshm, self.bandar_abbas]) self.add_controlpoint(self.havadarya, connected_to=[self.lar, self.qeshm, self.bandar_abbas])
self.add_controlpoint(self.bandar_abbas, connected_to=[self.havadarya]) self.add_controlpoint(self.bandar_abbas, connected_to=[self.havadarya])
self.add_controlpoint(self.lar, connected_to=[self.bandar_lengeh, self.qeshm, self.havadarya]) self.add_controlpoint(self.lar, connected_to=[self.bandar_lengeh, self.qeshm, self.havadarya, self.shiraz, self.kerman])
self.add_controlpoint(self.shiraz, connected_to=[self.lar, self.kerman])
self.add_controlpoint(self.kerman, connected_to=[self.lar, self.shiraz])
self.add_controlpoint(self.west_carrier) self.add_controlpoint(self.west_carrier)
self.west_carrier.captured = True self.west_carrier.captured = True
self.al_dhafra.captured = True self.kerman.captured = True
""" """
Mid game: Mid game:

View File

@@ -35,5 +35,5 @@ def generate_initial(theater: ConflictTheater, enemy: str, sams: bool, multiplie
count = max(COUNT_BY_TASK[task] * multiplier * (1+count_log), 1) count = max(COUNT_BY_TASK[task] * multiplier * (1+count_log), 1)
count_per_type = max(int(float(count) / len(unittypes)), 1) count_per_type = max(int(float(count) / len(unittypes)), 1)
for unit_type in unittypes: for unit_type in unittypes:
print("{} - {} {}".format(cp.name, db.unit_type_name(unit_type), count_per_type)) logging.info("{} - {} {}".format(cp.name, db.unit_type_name(unit_type), count_per_type))
cp.base.commision_units({unit_type: count_per_type}) cp.base.commision_units({unit_type: count_per_type})

View File

@@ -20,11 +20,15 @@ class ConfigurationMenu(Menu):
self.night_var = BooleanVar() self.night_var = BooleanVar()
self.night_var.set(self.game.settings.night_disabled) self.night_var.set(self.game.settings.night_disabled)
self.cold_start_var = BooleanVar()
self.cold_start_var.set(self.game.settings.cold_start)
def dismiss(self): def dismiss(self):
self.game.settings.player_skill = self.player_skill_var.get() self.game.settings.player_skill = self.player_skill_var.get()
self.game.settings.enemy_skill = self.enemy_skill_var.get() self.game.settings.enemy_skill = self.enemy_skill_var.get()
self.game.settings.only_player_takeoff = self.takeoff_var.get() self.game.settings.only_player_takeoff = self.takeoff_var.get()
self.game.settings.night_disabled = self.night_var.get() self.game.settings.night_disabled = self.night_var.get()
self.game.settings.cold_start = self.cold_start_var.get()
super(ConfigurationMenu, self).dismiss() super(ConfigurationMenu, self).dismiss()
def display(self): def display(self):
@@ -36,11 +40,16 @@ class ConfigurationMenu(Menu):
OptionMenu(self.frame, self.player_skill_var, "Average", "Good", "High", "Excellent").grid(row=0, column=1) OptionMenu(self.frame, self.player_skill_var, "Average", "Good", "High", "Excellent").grid(row=0, column=1)
OptionMenu(self.frame, self.enemy_skill_var, "Average", "Good", "High", "Excellent").grid(row=1, column=1) OptionMenu(self.frame, self.enemy_skill_var, "Average", "Good", "High", "Excellent").grid(row=1, column=1)
Checkbutton(self.frame, text="Takeoff only for player group", variable=self.takeoff_var).grid(row=2, column=0, columnspan=2) Label(self.frame, text="Aircraft cold start").grid(row=2, column=0)
Checkbutton(self.frame, text="Disable night missions", variable=self.night_var).grid(row=3, column=0, columnspan=2) Label(self.frame, text="Takeoff only for player group").grid(row=3, column=0)
Label(self.frame, text="Disable night missions").grid(row=4, column=0)
Button(self.frame, text="Back", command=self.dismiss).grid(row=4, column=0, columnspan=1) Checkbutton(self.frame, variable=self.cold_start_var).grid(row=2, column=1)
Button(self.frame, text="Cheat +200m", command=self.cheat_money).grid(row=5, column=0) Checkbutton(self.frame, variable=self.takeoff_var).grid(row=3, column=1)
Checkbutton(self.frame, variable=self.night_var).grid(row=4, column=1)
Button(self.frame, text="Back", command=self.dismiss).grid(row=5, column=0, columnspan=1)
Button(self.frame, text="Cheat +200m", command=self.cheat_money).grid(row=6, column=1)
def cheat_money(self): def cheat_money(self):
self.game.budget += 200 self.game.budget += 200

View File

@@ -1,9 +1,23 @@
from dcs.helicopters import helicopter_map
from ui.eventresultsmenu import * from ui.eventresultsmenu import *
from game import * from game import *
from game.event import * from game.event import *
UNITTYPES_FOR_EVENTS = {
FrontlineAttackEvent: [CAS, PinpointStrike],
FrontlinePatrolEvent: [CAP, PinpointStrike],
BaseAttackEvent: [CAP, CAS, PinpointStrike],
InterceptEvent: [CAP],
InsurgentAttackEvent: [CAS],
NavalInterceptEvent: [CAS],
AntiAAStrikeEvent: [CAS],
InfantryTransportEvent: [Embarking],
}
class EventMenu(Menu): class EventMenu(Menu):
aircraft_scramble_entries = None # type: typing.Dict[PlaneType , Entry] aircraft_scramble_entries = None # type: typing.Dict[PlaneType , Entry]
aircraft_client_entries = None # type: typing.Dict[PlaneType, Entry] aircraft_client_entries = None # type: typing.Dict[PlaneType, Entry]
@@ -87,7 +101,14 @@ class EventMenu(Menu):
Label(self.frame, text="Client slots").grid(row=row, column=3, columnspan=2) Label(self.frame, text="Client slots").grid(row=row, column=3, columnspan=2)
row += 1 row += 1
filter_to = UNITTYPES_FOR_EVENTS[self.event.__class__]
for unit_type, count in self.base.aircraft.items(): for unit_type, count in self.base.aircraft.items():
if filter_to and db.unit_task(unit_type) not in filter_to:
continue
if unit_type in helicopter_map and self.event.__class__ != InsurgentAttackEvent:
continue
scrable_row(unit_type, count) scrable_row(unit_type, count)
if not self.base.total_planes: if not self.base.total_planes:
@@ -95,6 +116,9 @@ class EventMenu(Menu):
label("Armor") label("Armor")
for unit_type, count in self.base.armor.items(): for unit_type, count in self.base.armor.items():
if filter_to and db.unit_task(unit_type) not in filter_to:
continue
scramble_armor_row(unit_type, count) scramble_armor_row(unit_type, count)
if not self.base.total_armor: if not self.base.total_armor:
@@ -172,8 +196,8 @@ class EventMenu(Menu):
if amount > 0: if amount > 0:
scrambled_armor[unit_type] = amount scrambled_armor[unit_type] = amount
if type(self.event) is CaptureEvent: if type(self.event) is BaseAttackEvent:
e = self.event # type: CaptureEvent e = self.event # type: BaseAttackEvent
if self.game.is_player_attack(self.event): if self.game.is_player_attack(self.event):
e.player_attacking(cas=scrambled_cas, e.player_attacking(cas=scrambled_cas,
escort=scrambled_sweep, escort=scrambled_sweep,
@@ -190,12 +214,12 @@ class EventMenu(Menu):
else: else:
e.player_defending(escort=scrambled_aircraft, e.player_defending(escort=scrambled_aircraft,
clients=scrambled_clients) clients=scrambled_clients)
elif type(self.event) is GroundInterceptEvent: elif type(self.event) is FrontlineAttackEvent:
e = self.event # type: GroundInterceptEvent e = self.event # type: FrontlineAttackEvent
if self.game.is_player_attack(self.event): e.player_attacking(armor=scrambled_armor, strikegroup=scrambled_aircraft, clients=scrambled_clients)
e.player_attacking(strikegroup=scrambled_aircraft, clients=scrambled_clients) elif type(self.event) is FrontlinePatrolEvent:
else: e = self.event # type: FrontlinePatrolEvent
e.player_defending(interceptors=scrambled_aircraft, clients=scrambled_clients) e.player_attacking(interceptors=scrambled_aircraft, clients=scrambled_clients, armor=scrambled_armor)
elif type(self.event) is NavalInterceptEvent: elif type(self.event) is NavalInterceptEvent:
e = self.event # type: NavalInterceptEvent e = self.event # type: NavalInterceptEvent
@@ -209,8 +233,8 @@ class EventMenu(Menu):
e.player_attacking(strikegroup=scrambled_aircraft, clients=scrambled_clients) e.player_attacking(strikegroup=scrambled_aircraft, clients=scrambled_clients)
else: else:
e.player_defending(interceptors=scrambled_aircraft, clients=scrambled_clients) e.player_defending(interceptors=scrambled_aircraft, clients=scrambled_clients)
elif type(self.event) is GroundAttackEvent: elif type(self.event) is InsurgentAttackEvent:
e = self.event # type: GroundAttackEvent e = self.event # type: InsurgentAttackEvent
if self.game.is_player_attack(self.event): if self.game.is_player_attack(self.event):
assert False assert False
else: else:

View File

@@ -22,21 +22,20 @@ class EventResultsMenu(Menu):
self.window.clear_right_pane() self.window.clear_right_pane()
if not self.finished: if not self.finished:
"""
For debugging purposes
Button(self.frame, text="no losses, succ", command=self.simulate_result(0, 1)).grid()
Button(self.frame, text="no losses, fail", command=self.simulate_result(0, 1)).grid(row=1, column=1)
Button(self.frame, text="half losses, succ", command=self.simulate_result(0.5, 0.5)).grid(row=2, )
Button(self.frame, text="half losses, fail", command=self.simulate_result(0.5, 0.5)).grid(row=2, column=1)
Button(self.frame, text="full losses, succ", command=self.simulate_result(1, 0)).grid(row=3, )
Button(self.frame, text="full losses, fail", command=self.simulate_result(1, 0)).grid(row=3, column=1)
"""
Label(self.frame, text="Play the mission and save debriefing to").grid(row=0, column=0) Label(self.frame, text="Play the mission and save debriefing to").grid(row=0, column=0)
Label(self.frame, text=debriefing_directory_location()).grid(row=1, column=0) Label(self.frame, text=debriefing_directory_location()).grid(row=1, column=0)
"""
For debugging purposes
"""
row = 3
Separator(self.frame, orient=HORIZONTAL).grid(row=row, sticky=EW); row += 1
Label(self.frame, text="Cheat operation results: ").grid(row=row); row += 1
Button(self.frame, text="full enemy losses", command=self.simulate_result(0, 1)).grid(row=row); row += 1
Button(self.frame, text="full player losses", command=self.simulate_result(1, 0)).grid(row=row); row += 1
Button(self.frame, text="some enemy losses", command=self.simulate_result(0, 0.8)).grid(row=row); row += 1
Button(self.frame, text="some player losses", command=self.simulate_result(0.8, 0)).grid(row=row); row += 1
else: else:
row = 0 row = 0
if self.event.is_successfull(self.debriefing): if self.event.is_successfull(self.debriefing):
@@ -81,27 +80,53 @@ class EventResultsMenu(Menu):
def action(): def action():
debriefing = Debriefing({}) debriefing = Debriefing({})
def count_planes(groups: typing.List[FlyingGroup], mult: float) -> typing.Dict[UnitType, int]: def count(country: Country) -> typing.Dict[UnitType, int]:
result = {} result = {}
for group in groups: for g in country.plane_group + country.vehicle_group + country.helicopter_group + country.ship_group:
group = g # type: Group
for unit in group.units: for unit in group.units:
result[unit.unit_type] = result.get(unit.unit_type, 0) + 1 * mult unit_type = None
if isinstance(unit, Vehicle):
unit_type = vehicle_map[unit.type]
elif isinstance(unit, Ship):
unit_type = ship_map[unit.type]
else:
unit_type = unit.unit_type
return {x: math.ceil(y) for x, y in result.items() if y >= 1} if unit_type in db.EXTRA_AA.values():
continue
player_planes = self.event.operation.mission.country(self.game.player).plane_group result[unit_type] = result.get(unit_type, 0) + 1
enemy_planes = self.event.operation.mission.country(self.game.enemy).plane_group
self.player_losses = count_planes(player_planes, player_factor) return result
self.enemy_losses = count_planes(enemy_planes, enemy_factor)
player = self.event.operation.mission.country(self.game.player)
enemy = self.event.operation.mission.country(self.game.enemy)
alive_player_units = count(player)
alive_enemy_units = count(enemy)
destroyed_player_units = db.unitdict_restrict_count(alive_player_units, math.ceil(sum(alive_player_units.values()) * player_factor))
destroyed_enemy_units = db.unitdict_restrict_count(alive_enemy_units, math.ceil(sum(alive_enemy_units.values()) * enemy_factor))
alive_player_units = {k: v - destroyed_player_units.get(k, 0) for k, v in alive_player_units.items()}
alive_enemy_units = {k: v - destroyed_enemy_units.get(k, 0) for k, v in alive_enemy_units.items()}
debriefing.alive_units = {
enemy.name: alive_enemy_units,
player.name: alive_player_units,
}
debriefing.destroyed_units = { debriefing.destroyed_units = {
self.game.player: self.player_losses, player.name: destroyed_player_units,
self.game.enemy: self.enemy_losses, enemy.name: destroyed_enemy_units,
} }
self.finished = True self.finished = True
self.debriefing = debriefing self.debriefing = debriefing
self.player_losses = debriefing.destroyed_units.get(self.game.player, {})
self.enemy_losses = debriefing.destroyed_units.get(self.game.enemy, {})
self.game.finish_event(self.event, debriefing) self.game.finish_event(self.event, debriefing)
self.display() self.display()
self.game.pass_turn() self.game.pass_turn()

View File

@@ -34,13 +34,18 @@ class MainMenu(Menu):
def event_button(event): def event_button(event):
nonlocal row nonlocal row
Message(self.frame, text="{}{}".format( Message(self.frame, text="{}{} at {}".format(
event.defender_name == self.game.player and "Enemy attacking: " or "", event.defender_name == self.game.player and "Enemy attacking: " or "",
event event,
event.to_cp,
), aspect=1600).grid(column=0, row=row, sticky=NW) ), aspect=1600).grid(column=0, row=row, sticky=NW)
Button(self.frame, text=">", command=self.start_event(event)).grid(column=0, row=row, sticky=NE+S) Button(self.frame, text=">", command=self.start_event(event)).grid(column=0, row=row, sticky=NE+S); row += 1
row += 1
Separator(self.frame, orient='horizontal').grid(row=row, sticky=EW); row += 1 def destination_header(text, separator=True):
nonlocal row
if separator:
Separator(self.frame, orient=HORIZONTAL).grid(row=row, sticky=EW); row += 1
Label(self.frame, text=text).grid(column=0, row=row, sticky=N); row += 1
Button(self.frame, text="Configuration", command=self.configuration_menu).grid(column=0, row=0, sticky=NE) Button(self.frame, text="Configuration", command=self.configuration_menu).grid(column=0, row=0, sticky=NE)
Button(self.frame, text="Pass turn", command=self.pass_turn).grid(column=0, row=0, sticky=NW) Button(self.frame, text="Pass turn", command=self.pass_turn).grid(column=0, row=0, sticky=NW)
@@ -48,9 +53,20 @@ class MainMenu(Menu):
Separator(self.frame, orient='horizontal').grid(row=row, sticky=EW); row += 1 Separator(self.frame, orient='horizontal').grid(row=row, sticky=EW); row += 1
events = self.game.events events = self.game.events
events.sort(key=lambda x: x.from_cp.name)
events.sort(key=lambda x: x.informational and 2 or (self.game.is_player_attack(x) and 1 or 0)) events.sort(key=lambda x: x.informational and 2 or (self.game.is_player_attack(x) and 1 or 0))
destination = None
for event in events: for event in events:
if not event.informational:
if self.game.is_player_attack(event):
new_destination = event.from_cp.name
else:
new_destination = "Enemy attack"
if destination != new_destination:
destination_header(new_destination, destination is not None)
destination = new_destination
if event.informational: if event.informational:
label(str(event)) label(str(event))
else: else:

View File

@@ -6,6 +6,7 @@ from tkinter.ttk import *
from ui.window import * from ui.window import *
from game.game import * from game.game import *
from gen.conflictgen import Conflict
from theater.conflicttheater import * from theater.conflicttheater import *
@@ -27,24 +28,28 @@ class OverviewCanvas:
point_b = list(self.game.theater.reference_points.keys())[1] point_b = list(self.game.theater.reference_points.keys())[1]
point_b_img = self.game.theater.reference_points[point_b] point_b_img = self.game.theater.reference_points[point_b]
x_dist = point_a_img[0] - point_b_img[0] Y_dist = point_a_img[0] - point_b_img[0]
lon_dist = point_a[1] - point_b[1] lon_dist = point_a[1] - point_b[1]
y_dist = point_a_img[1] - point_b_img[1] X_dist = point_a_img[1] - point_b_img[1]
lat_dist = point_b[0] - point_a[0] lat_dist = point_b[0] - point_a[0]
x_scale = float(x_dist) / float(lon_dist) Y_scale = float(Y_dist) / float(lon_dist)
y_scale = float(y_dist) / float(lat_dist) X_scale = float(X_dist) / float(lat_dist)
# --- # ---
x_offset = p.x - point_a[0] Y_offset = p.x - point_a[0]
y_offset = p.y - point_a[1] X_offset = p.y - point_a[1]
return point_b_img[1] + y_offset * y_scale, point_a_img[0] - x_offset * x_scale X = point_b_img[1] + X_offset * X_scale
Y = point_a_img[0] - Y_offset * Y_scale
treshold = 30
return X > treshold and X or treshold, Y > treshold and Y or treshold
def create_cp_title(self, coords, cp: ControlPoint): def create_cp_title(self, coords, cp: ControlPoint):
title = cp.name title = cp.name
font = ("Helvetica", 13) font = ("Helvetica", 10)
id = self.canvas.create_text(coords[0]+1, coords[1]+1, text=title, fill='white', font=font) id = self.canvas.create_text(coords[0]+1, coords[1]+1, text=title, fill='white', font=font)
self.canvas.tag_bind(id, "<Button-1>", self.display(cp)) self.canvas.tag_bind(id, "<Button-1>", self.display(cp))
@@ -74,9 +79,14 @@ class OverviewCanvas:
self.canvas.create_line((coords[0], coords[1], connected_coords[0], connected_coords[1]), width=2, fill=color) self.canvas.create_line((coords[0], coords[1], connected_coords[0], connected_coords[1]), width=2, fill=color)
if cp.captured and not connected_cp.captured and Conflict.has_frontline_between(cp, connected_cp):
frontline_pos, heading, distance = Conflict.frontline_vector(cp, connected_cp, self.game.theater)
start_coords, end_coords = self.transform_point(frontline_pos), self.transform_point(frontline_pos.point_from_heading(heading, distance))
self.canvas.create_line((*start_coords, *end_coords), width=2, fill=color)
for cp in self.game.theater.controlpoints: for cp in self.game.theater.controlpoints:
coords = self.transform_point(cp.position) coords = self.transform_point(cp.position)
arc_size = 22 * math.pow(cp.importance, 1) arc_size = 16 * math.pow(cp.importance, 1)
extent = max(cp.base.strength * 180, 10) extent = max(cp.base.strength * 180, 10)
start = (180 - extent) / 2 start = (180 - extent) / 2

View File

@@ -1,3 +1,4 @@
import logging
import typing import typing
import re import re
import threading import threading
@@ -83,17 +84,16 @@ class Debriefing:
try: try:
components = event["initiator"].split("|") components = event["initiator"].split("|")
print(components)
category, country_id, group_id, unit_type = components[0], int(components[1]), int(components[2]), db.unit_type_from_name(components[3]) category, country_id, group_id, unit_type = components[0], int(components[1]), int(components[2]), db.unit_type_from_name(components[3])
if unit_type is None: if unit_type is None:
print("Skipped due to no unit type") logging.info("Skipped due to no unit type")
continue continue
if category != "unit": if category != "unit":
print("Skipped due to category") logging.info("Skipped due to category")
continue continue
except Exception as e: except Exception as e:
print(e) logging.error(e)
continue continue
if country_id not in dead_units: if country_id not in dead_units:

29
userdata/logging.py Normal file
View File

@@ -0,0 +1,29 @@
import logging
import traceback
from io import StringIO
from tkinter import *
from tkinter.scrolledtext import *
log_stream = StringIO()
logging.basicConfig(stream=log_stream, level=logging.INFO)
def _error_prompt():
tk = Tk()
Label(tk, text="Oops, something went wrong.").grid(row=0)
Label(tk, text="Please send following text to the developer:").grid(row=1)
text = ScrolledText(tk)
text.insert("0.0", log_stream.getvalue())
text.grid(row=2, sticky=NSEW)
tk.focus()
def _handle_exception(self, exception: BaseException, *args):
logging.exception(exception)
_error_prompt()
Tk.report_callback_exception = _handle_exception
logging.info("DCS Libration 1.3 RC2")

View File

@@ -1,3 +1,4 @@
import logging
import typing import typing
import pickle import pickle
import os import os
@@ -19,7 +20,7 @@ def base_path() -> str:
if os.path.exists(openbeta_path): if os.path.exists(openbeta_path):
return openbeta_path return openbeta_path
else: else:
return os.path.join(_user_folder, "Saved Games" , "DCS") return os.path.join(_user_folder, "Saved Games", "DCS")
def _save_file() -> str: def _save_file() -> str:
@@ -56,5 +57,5 @@ def save_game(game) -> bool:
shutil.copy(_temporary_save_file(), _save_file()) shutil.copy(_temporary_save_file(), _save_file())
return True return True
except Exception as e: except Exception as e:
print(e) logging.error(e)
return False return False