mirror of
https://github.com/dcs-liberation/dcs_liberation.git
synced 2025-11-10 14:22:26 +00:00
Compare commits
22 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
7458181e90 | ||
|
|
ec28cdc936 | ||
|
|
9dbc9a8a56 | ||
|
|
73d4a2d414 | ||
|
|
e93ad8b800 | ||
|
|
d48985ca6d | ||
|
|
5f7d717b63 | ||
|
|
e266698e68 | ||
|
|
e8098e795c | ||
|
|
8d69724272 | ||
|
|
683114f916 | ||
|
|
3b454470f9 | ||
|
|
f40f83bb09 | ||
|
|
932bec2f84 | ||
|
|
820820eb92 | ||
|
|
6f5835a2b8 | ||
|
|
b302372d4c | ||
|
|
cad7d2c735 | ||
|
|
e4c3f8bce2 | ||
|
|
1fbf4e292a | ||
|
|
62f5b2d06d | ||
|
|
b2545e4de0 |
1
.gitignore
vendored
1
.gitignore
vendored
@@ -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
|
||||||
|
|||||||
41
__init__.py
41
__init__.py
@@ -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"))
|
||||||
|
|
||||||
|
|||||||
43
game/db.py
43
game/db.py
@@ -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()
|
||||||
|
|||||||
@@ -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 *
|
||||||
|
|||||||
@@ -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())
|
||||||
|
|||||||
@@ -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))
|
||||||
73
game/event/frontlineattack.py
Normal file
73
game/event/frontlineattack.py
Normal 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
|
||||||
|
|
||||||
87
game/event/frontlinepatrol.py
Normal file
87
game/event/frontlinepatrol.py
Normal 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
|
||||||
@@ -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
|
|
||||||
@@ -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
|
|
||||||
@@ -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
|
||||||
|
|||||||
53
game/event/insurgentattack.py
Normal file
53
game/event/insurgentattack.py
Normal 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
|
||||||
@@ -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
|
||||||
|
|||||||
@@ -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):
|
||||||
|
|||||||
46
game/game.py
46
game/game.py
@@ -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()
|
||||||
|
|
||||||
|
|||||||
@@ -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
|
||||||
|
|
||||||
|
|||||||
@@ -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()
|
||||||
|
|
||||||
@@ -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()
|
|
||||||
58
game/operation/frontlinepatrol.py
Normal file
58
game/operation/frontlinepatrol.py
Normal 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()
|
||||||
@@ -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
|
||||||
|
|
||||||
|
|||||||
@@ -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()
|
||||||
@@ -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()
|
||||||
|
|||||||
@@ -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()
|
||||||
|
|||||||
@@ -6,3 +6,4 @@ class Settings:
|
|||||||
night_disabled = False
|
night_disabled = False
|
||||||
multiplier = 1
|
multiplier = 1
|
||||||
sams = True
|
sams = True
|
||||||
|
cold_start = False
|
||||||
|
|||||||
@@ -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 *
|
||||||
|
|||||||
@@ -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
54
gen/airsupportgen.py
Normal 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,
|
||||||
|
)
|
||||||
73
gen/armor.py
73
gen/armor.py
@@ -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))
|
||||||
|
|||||||
@@ -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
|
|
||||||
)
|
|
||||||
@@ -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:
|
||||||
|
|||||||
@@ -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()
|
||||||
|
|||||||
@@ -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)
|
||||||
|
|||||||
@@ -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),
|
||||||
|
|||||||
@@ -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)
|
||||||
|
|
||||||
|
|||||||
@@ -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
1
resources/tools/dcs
Symbolic link
@@ -0,0 +1 @@
|
|||||||
|
../../submodules/dcs/dcs
|
||||||
42
resources/tools/miz_diff.py
Normal file
42
resources/tools/miz_diff.py
Normal 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)
|
||||||
@@ -1 +1 @@
|
|||||||
py.exe __init__.py %UserProfile% > logs.txt 2>&1
|
py.exe __init__.py "%UserProfile%"
|
||||||
|
|||||||
@@ -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())
|
||||||
|
|||||||
@@ -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):
|
||||||
|
|||||||
@@ -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:
|
||||||
|
|||||||
@@ -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})
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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:
|
||||||
|
|||||||
@@ -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()
|
||||||
|
|||||||
@@ -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:
|
||||||
|
|||||||
@@ -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
|
||||||
|
|
||||||
|
|||||||
@@ -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
29
userdata/logging.py
Normal 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")
|
||||||
@@ -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
|
||||||
|
|||||||
Reference in New Issue
Block a user