mirror of
https://github.com/dcs-liberation/dcs_liberation.git
synced 2025-11-10 14:22:26 +00:00
frontline attack ops
This commit is contained in:
parent
6f5835a2b8
commit
820820eb92
39
__init__.py
39
__init__.py
@ -17,45 +17,6 @@ from theater import start_generator
|
|||||||
from userdata import persistency
|
from userdata import persistency
|
||||||
|
|
||||||
|
|
||||||
"""
|
|
||||||
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"))
|
||||||
|
|
||||||
|
|||||||
@ -448,6 +448,11 @@ def unitdict_split(unit_dict: UnitsDict, count: int):
|
|||||||
if len(buffer_dict):
|
if len(buffer_dict):
|
||||||
yield buffer_dict
|
yield buffer_dict
|
||||||
|
|
||||||
|
|
||||||
|
def unitdict_restrict_count(unit_dict: UnitsDict, total_count: int) -> UnitsDict:
|
||||||
|
return list(unitdict_split(unit_dict, total_count))[0]
|
||||||
|
|
||||||
|
|
||||||
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,8 @@
|
|||||||
from .event import *
|
from .event import *
|
||||||
from .frontlinecas import *
|
from .frontlineattack 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 *
|
||||||
|
|||||||
@ -4,13 +4,13 @@ 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
|
||||||
@ -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))
|
||||||
76
game/event/frontlineattack.py
Normal file
76
game/event/frontlineattack.py
Normal file
@ -0,0 +1,76 @@
|
|||||||
|
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.3
|
||||||
|
SUCCESS_MIN_TARGETS = 3
|
||||||
|
|
||||||
|
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 from {} at {}".format(self.from_cp, self.to_cp)
|
||||||
|
|
||||||
|
def is_successfull(self, debriefing: Debriefing):
|
||||||
|
total_targets = sum(self.defenders.values())
|
||||||
|
destroyed_targets = 0
|
||||||
|
for unit, count in debriefing.destroyed_units[self.defender_name].items():
|
||||||
|
if unit in self.defenders:
|
||||||
|
destroyed_targets += count
|
||||||
|
|
||||||
|
if self.from_cp.captured:
|
||||||
|
return float(destroyed_targets) >= min(self.SUCCESS_MIN_TARGETS, total_targets)
|
||||||
|
else:
|
||||||
|
return float(destroyed_targets) < min(self.SUCCESS_MIN_TARGETS, total_targets)
|
||||||
|
|
||||||
|
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_cap()
|
||||||
|
|
||||||
|
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
|
||||||
|
|
||||||
@ -1,91 +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.frontlinecas import FrontlineCASOperation
|
|
||||||
from userdata.debriefing import Debriefing
|
|
||||||
|
|
||||||
|
|
||||||
class FrontlineCASEvent(Event):
|
|
||||||
TARGET_VARIETY = 2
|
|
||||||
TARGET_AMOUNT_FACTOR = 0.5
|
|
||||||
ATTACKER_AMOUNT_FACTOR = 0.4
|
|
||||||
STRENGTH_INFLUENCE = 0.3
|
|
||||||
SUCCESS_MIN_TARGETS = 3
|
|
||||||
|
|
||||||
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(FrontlineCASEvent, 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) >= min(self.SUCCESS_MIN_TARGETS, total_targets)
|
|
||||||
else:
|
|
||||||
return float(destroyed_targets) < min(self.SUCCESS_MIN_TARGETS, total_targets)
|
|
||||||
|
|
||||||
def commit(self, debriefing: Debriefing):
|
|
||||||
super(FrontlineCASEvent, 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_armor_targets = db.find_unittype(PinpointStrike, self.defender_name)
|
|
||||||
random.shuffle(suitable_armor_targets)
|
|
||||||
|
|
||||||
target_types = suitable_armor_targets[:self.TARGET_VARIETY]
|
|
||||||
typecount = max(math.floor(self.to_cp.base.assemble_count() * self.TARGET_AMOUNT_FACTOR), 1)
|
|
||||||
self.targets = {unittype: typecount for unittype in target_types}
|
|
||||||
|
|
||||||
defense_aa_unit = random.choice(self.game.commision_unit_types(self.to_cp, AirDefence))
|
|
||||||
self.targets[defense_aa_unit] = 1
|
|
||||||
|
|
||||||
suitable_armor_attackers = db.find_unittype(PinpointStrike, self.attacker_name)
|
|
||||||
random.shuffle(suitable_armor_attackers)
|
|
||||||
attacker_types = suitable_armor_attackers[:self.TARGET_VARIETY]
|
|
||||||
typecount = max(math.floor(self.from_cp.base.assemble_count() * self.ATTACKER_AMOUNT_FACTOR), 1)
|
|
||||||
attackers = {unittype: typecount for unittype in attacker_types}
|
|
||||||
|
|
||||||
op = FrontlineCASOperation(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,
|
|
||||||
attackers=attackers,
|
|
||||||
strikegroup=strikegroup)
|
|
||||||
|
|
||||||
self.operation = op
|
|
||||||
|
|
||||||
@ -5,11 +5,11 @@ from dcs.task import *
|
|||||||
|
|
||||||
from game import *
|
from game import *
|
||||||
from game.event import *
|
from game.event import *
|
||||||
from game.event.frontlinecas import FrontlineCASEvent
|
from game.event.frontlineattack import FrontlineAttackEvent
|
||||||
from game.operation.groundattack import GroundAttackOperation
|
from game.operation.insurgentattack import InsurgentAttackOperation
|
||||||
|
|
||||||
|
|
||||||
class GroundAttackEvent(FrontlineCASEvent):
|
class InsurgentAttackEvent(FrontlineAttackEvent):
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
return "Destroy insurgents at {}".format(self.to_cp)
|
return "Destroy insurgents at {}".format(self.to_cp)
|
||||||
|
|
||||||
@ -24,13 +24,13 @@ class GroundAttackEvent(FrontlineCASEvent):
|
|||||||
typecount = max(math.floor(self.difficulty * self.TARGET_AMOUNT_FACTOR), 1)
|
typecount = max(math.floor(self.difficulty * self.TARGET_AMOUNT_FACTOR), 1)
|
||||||
self.targets = {unittype: typecount for unittype in unittypes}
|
self.targets = {unittype: typecount for unittype in unittypes}
|
||||||
|
|
||||||
op = GroundAttackOperation(game=self.game,
|
op = InsurgentAttackOperation(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(target=self.targets,
|
op.setup(target=self.targets,
|
||||||
strikegroup=strikegroup)
|
strikegroup=strikegroup)
|
||||||
|
|
||||||
10
game/game.py
10
game/game.py
@ -39,17 +39,17 @@ 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
|
||||||
* FrontlineCASEvent - 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],
|
||||||
InterceptEvent: [25, 10],
|
InterceptEvent: [25, 10],
|
||||||
FrontlineCASEvent: [250, 0],
|
FrontlineAttackEvent: [100, 0],
|
||||||
GroundAttackEvent: [0, 10],
|
InsurgentAttackEvent: [0, 10],
|
||||||
NavalInterceptEvent: [25, 10],
|
NavalInterceptEvent: [25, 10],
|
||||||
AntiAAStrikeEvent: [25, 10],
|
AntiAAStrikeEvent: [25, 10],
|
||||||
InfantryTransportEvent: [25, 0],
|
InfantryTransportEvent: [25, 0],
|
||||||
@ -130,7 +130,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:
|
||||||
|
|||||||
@ -12,7 +12,7 @@ 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:
|
||||||
@ -63,5 +63,5 @@ class CaptureOperation(Operation):
|
|||||||
self.airgen.generate_strikegroup_escort(self.escort, 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.visualgen.generate_target_smokes(self.to_cp)
|
self.visualgen.generate_target_smokes(self.to_cp)
|
||||||
super(CaptureOperation, self).generate()
|
super(BaseAttackOperation, self).generate()
|
||||||
|
|
||||||
@ -18,7 +18,7 @@ from .operation import Operation
|
|||||||
MAX_DISTANCE_BETWEEN_GROUPS = 12000
|
MAX_DISTANCE_BETWEEN_GROUPS = 12000
|
||||||
|
|
||||||
|
|
||||||
class FrontlineCASOperation(Operation):
|
class FrontlineAttackOperation(Operation):
|
||||||
attackers = None # type: db.ArmorDict
|
attackers = None # type: db.ArmorDict
|
||||||
strikegroup = None # type: db.PlaneDict
|
strikegroup = None # type: db.PlaneDict
|
||||||
target = None # type: db.ArmorDict
|
target = None # type: db.ArmorDict
|
||||||
@ -32,7 +32,7 @@ class FrontlineCASOperation(Operation):
|
|||||||
self.attackers = attackers
|
self.attackers = attackers
|
||||||
|
|
||||||
def prepare(self, terrain: Terrain, is_quick: bool):
|
def prepare(self, terrain: Terrain, is_quick: bool):
|
||||||
super(FrontlineCASOperation, 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
|
||||||
@ -51,4 +51,4 @@ class FrontlineCASOperation(Operation):
|
|||||||
def generate(self):
|
def generate(self):
|
||||||
self.armorgen.generate_vec(self.attackers, self.target)
|
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(FrontlineCASOperation, self).generate()
|
super(FrontlineAttackOperation, self).generate()
|
||||||
@ -13,7 +13,7 @@ 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()
|
||||||
23
gen/armor.py
23
gen/armor.py
@ -1,3 +1,4 @@
|
|||||||
|
from random import randint
|
||||||
from itertools import zip_longest
|
from itertools import zip_longest
|
||||||
|
|
||||||
from game import db
|
from game import db
|
||||||
@ -13,6 +14,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):
|
||||||
@ -50,7 +55,7 @@ class ArmorConflictGenerator:
|
|||||||
side=self.conflict.attackers_side,
|
side=self.conflict.attackers_side,
|
||||||
unit=type,
|
unit=type,
|
||||||
count=count,
|
count=count,
|
||||||
at=position.point_from_heading(self.conflict.heading - 90, 600),
|
at=position.point_from_heading(self.conflict.heading - 90, 5000),
|
||||||
to=position)
|
to=position)
|
||||||
|
|
||||||
if defenders:
|
if defenders:
|
||||||
@ -59,7 +64,7 @@ class ArmorConflictGenerator:
|
|||||||
side=self.conflict.defenders_side,
|
side=self.conflict.defenders_side,
|
||||||
unit=type,
|
unit=type,
|
||||||
count=count,
|
count=count,
|
||||||
at=position.point_from_heading(self.conflict.heading + 90, 600),
|
at=position.point_from_heading(self.conflict.heading + 90, 1000),
|
||||||
to=position)
|
to=position)
|
||||||
|
|
||||||
def generate(self, attackers: db.ArmorDict, defenders: db.ArmorDict):
|
def generate(self, attackers: db.ArmorDict, defenders: db.ArmorDict):
|
||||||
@ -78,16 +83,16 @@ class ArmorConflictGenerator:
|
|||||||
at=self.conflict.ground_defenders_location)
|
at=self.conflict.ground_defenders_location)
|
||||||
|
|
||||||
def generate_vec(self, attackers: db.ArmorDict, defenders: db.ArmorDict):
|
def generate_vec(self, attackers: db.ArmorDict, defenders: db.ArmorDict):
|
||||||
defender_groups = list(db.unitdict_split(defenders, 6))
|
fights_count = randint(*FRONTLINE_CAS_FIGHTS_COUNT)
|
||||||
distance_between_groups = min(self.conflict.distance / len(defender_groups), 12000)
|
single_fight_defenders_count = min(int(sum(defenders.values()) / fights_count), randint(*FRONTLINE_CAS_GROUP_MIN))
|
||||||
total_distance = distance_between_groups * len(defender_groups)
|
defender_groups = list(db.unitdict_split(defenders, single_fight_defenders_count))
|
||||||
|
|
||||||
attacker_groups = list(db.unitdict_split(attackers,
|
single_fight_attackers_count = min(int(sum(attackers.values()) / len(defender_groups)), randint(*FRONTLINE_CAS_GROUP_MIN))
|
||||||
int(sum(attackers.values()) / len(defender_groups))))
|
attacker_groups = list(db.unitdict_split(attackers, single_fight_attackers_count))
|
||||||
|
|
||||||
position = self.conflict.center.point_from_heading(self.conflict.opposite_heading, total_distance / 2)
|
|
||||||
for attacker_group_dict, target_group_dict in zip_longest(attacker_groups, defender_groups):
|
for attacker_group_dict, target_group_dict in zip_longest(attacker_groups, defender_groups):
|
||||||
position = position.point_from_heading(self.conflict.heading, distance_between_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)
|
self._generate_fight_at(attacker_group_dict, target_group_dict, position)
|
||||||
|
|
||||||
def generate_passengers(self, count: int):
|
def generate_passengers(self, count: int):
|
||||||
|
|||||||
@ -38,8 +38,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):
|
||||||
@ -127,7 +128,7 @@ class Conflict:
|
|||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def frontline_position(cls, from_cp: ControlPoint, to_cp: ControlPoint) -> typing.Tuple[Point, int]:
|
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) * FRONT_SMOKE_DISTANCE_FACTOR * to_cp.base.strength, FRONT_SMOKE_MIN_DISTANCE)
|
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), heading
|
return to_cp.position.point_from_heading(heading, distance), heading
|
||||||
|
|
||||||
@ -136,7 +137,7 @@ class Conflict:
|
|||||||
center_position, heading = cls.frontline_position(from_cp, to_cp)
|
center_position, heading = cls.frontline_position(from_cp, to_cp)
|
||||||
|
|
||||||
left_position = center_position
|
left_position = center_position
|
||||||
for offset in range(0, 80000, 1000):
|
for offset in range(0, int(FRONTLINE_LENGTH / 2), 1000):
|
||||||
pos = center_position.point_from_heading(_heading_sum(heading, -90), offset)
|
pos = center_position.point_from_heading(_heading_sum(heading, -90), offset)
|
||||||
if not theater.is_on_land(pos):
|
if not theater.is_on_land(pos):
|
||||||
break
|
break
|
||||||
@ -144,7 +145,7 @@ class Conflict:
|
|||||||
left_position = pos
|
left_position = pos
|
||||||
|
|
||||||
right_position = center_position
|
right_position = center_position
|
||||||
for offset in range(0, 80000, 1000):
|
for offset in range(0, int(FRONTLINE_LENGTH / 2), 1000):
|
||||||
pos = center_position.point_from_heading(_heading_sum(heading, 90), offset)
|
pos = center_position.point_from_heading(_heading_sum(heading, 90), offset)
|
||||||
if not theater.is_on_land(pos):
|
if not theater.is_on_land(pos):
|
||||||
break
|
break
|
||||||
|
|||||||
@ -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 = {
|
||||||
@ -100,9 +99,9 @@ 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():
|
||||||
point, heading = Conflict.frontline_position(from_cp, to_cp)
|
point, heading = Conflict.frontline_position(from_cp, to_cp)
|
||||||
plane_start = point.point_from_heading(turn_heading(heading, 90), FRONT_SMOKE_LENGTH / 2)
|
plane_start = point.point_from_heading(turn_heading(heading, 90), FRONTLINE_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():
|
||||||
|
|||||||
39
resources/tools/miz_diff.py
Normal file
39
resources/tools/miz_diff.py
Normal file
@ -0,0 +1,39 @@
|
|||||||
|
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)
|
||||||
@ -150,7 +150,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:
|
||||||
@ -171,7 +171,8 @@ class Base:
|
|||||||
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())
|
||||||
|
|||||||
@ -172,8 +172,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 +190,9 @@ 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 FrontlineCASEvent:
|
elif type(self.event) is FrontlineAttackEvent:
|
||||||
e = self.event # type: FrontlineCASEvent
|
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)
|
|
||||||
else:
|
|
||||||
e.player_defending(interceptors=scrambled_aircraft, clients=scrambled_clients)
|
|
||||||
elif type(self.event) is NavalInterceptEvent:
|
elif type(self.event) is NavalInterceptEvent:
|
||||||
e = self.event # type: NavalInterceptEvent
|
e = self.event # type: NavalInterceptEvent
|
||||||
|
|
||||||
@ -209,8 +206,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:
|
||||||
|
|||||||
@ -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 *
|
||||||
|
|
||||||
|
|
||||||
@ -44,7 +45,7 @@ class OverviewCanvas:
|
|||||||
|
|
||||||
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 +75,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
|
||||||
|
|
||||||
|
|||||||
@ -19,7 +19,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:
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user