dont limit aircraft to predefined role; better scrambling screen

This commit is contained in:
Vasyl Horbachenko 2018-09-09 20:21:07 +03:00
parent 4ba1dd87e8
commit a918914431
25 changed files with 332 additions and 320 deletions

View File

@ -1,4 +1,5 @@
import typing
import enum
from dcs.vehicles import *
from dcs.unitgroup import *

View File

@ -1,13 +1,16 @@
import typing
import math
import random
from dcs.task import *
from dcs.unittype import UnitType
from game import db
from game.operation.baseattack import BaseAttackOperation
from userdata.debriefing import Debriefing
from .event import Event
from .event import *
from ..operation.operation import flight_dict_from
class BaseAttackEvent(Event):
@ -18,6 +21,18 @@ class BaseAttackEvent(Event):
def __str__(self):
return "Base attack"
@property
def tasks(self):
return [CAP, CAS, PinpointStrike]
def flight_name(self, for_task: typing.Type[Task]) -> str:
if for_task == CAP:
return "Escort flight"
elif for_task == CAS:
return "CAS flight"
elif for_task == PinpointStrike:
return "Ground attack"
def is_successfull(self, debriefing: Debriefing):
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])
@ -45,7 +60,9 @@ class BaseAttackEvent(Event):
if not self.is_player_attacking and self.to_cp.captured:
self.to_cp.captured = False
def player_defending(self, interceptors: db.PlaneDict, clients: db.PlaneDict):
def player_defending(self, flights: ScrambledFlightsDict):
assert len(flights) == 1 and flights[CAP], "Invalid scrambled flights"
cas = self.from_cp.base.scramble_cas(self.game.settings.multiplier)
escort = self.from_cp.base.scramble_sweep(self.game.settings.multiplier)
attackers = self.from_cp.base.armor
@ -53,36 +70,34 @@ class BaseAttackEvent(Event):
op = BaseAttackOperation(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(cas=cas,
escort=escort,
op.setup(cas=flight_dict_from(cas),
escort=flight_dict_from(escort),
intercept=flights[CAP],
attack=attackers,
intercept=interceptors,
defense=self.to_cp.base.armor,
aa=self.to_cp.base.aa)
self.operation = op
def player_attacking(self, cas: db.PlaneDict, escort: db.PlaneDict, armor: db.ArmorDict, clients: db.PlaneDict):
def player_attacking(self, flights: ScrambledFlightsDict):
assert flights[CAP] and flights[CAS] and flights[PinpointStrike] and len(flights) == 3, "Invalid flights"
op = BaseAttackOperation(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.scramble_sweep(self.game.settings.multiplier)
defenders.update(self.to_cp.base.scramble_cas(self.game.settings.multiplier))
op.setup(cas=cas,
escort=escort,
attack=armor,
intercept=defenders,
op.setup(cas=flights[CAS],
escort=flights[CAP],
attack=flights[PinpointStrike],
intercept=flight_dict_from(defenders),
defense=self.to_cp.base.armor,
aa=self.to_cp.base.assemble_aa())

View File

@ -1,16 +1,22 @@
import typing
import logging
from dcs.unittype import UnitType
from dcs.task import Task
from dcs.unittype import UnitType
from game import *
from theater import *
from gen.environmentgen import EnvironmentSettings
from game.operation.operation import flight_dict_from, dict_from_flight
from userdata.debriefing import Debriefing
from userdata import persistency
DIFFICULTY_LOG_BASE = 1.1
ScrambledFlightsDict = typing.Dict[typing.Type[Task], typing.Dict[typing.Type[UnitType], typing.Tuple[int, int]]]
class Event:
silent = False
@ -44,12 +50,25 @@ class Event:
def threat_description(self) -> str:
return ""
def flight_name(self, for_task: typing.Type[Task]) -> str:
return "Flight"
@property
def tasks(self) -> typing.Collection[Task]:
return []
def bonus(self) -> int:
return int(math.log(self.to_cp.importance + 1, DIFFICULTY_LOG_BASE) * self.BONUS_BASE)
def is_successfull(self, debriefing: Debriefing) -> bool:
return self.operation.is_successfull(debriefing)
def player_attacking(self, flights: ScrambledFlightsDict):
pass
def player_defending(self, flights: ScrambledFlightsDict):
pass
def generate(self):
self.operation.is_awacs_enabled = self.is_awacs_enabled

View File

@ -24,6 +24,21 @@ class FrontlineAttackEvent(Event):
def threat_description(self):
return "{} vehicles".format(self.to_cp.base.assemble_count())
@property
def tasks(self) -> typing.Collection[typing.Type[Task]]:
if self.is_player_attacking:
return [CAS, PinpointStrike]
else:
return [CAP, PinpointStrike]
def flight_name(self, for_task: typing.Type[Task]) -> str:
if for_task == CAS:
return "CAS flight"
elif for_task == CAP:
return "CAP flight"
elif for_task == PinpointStrike:
return "Ground attack"
def __str__(self):
return "Frontline attack"
@ -54,20 +69,21 @@ class FrontlineAttackEvent(Event):
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):
def player_attacking(self, flights: ScrambledFlightsDict):
assert flights[CAS] and flights[PinpointStrike] and len(flights) == 2, "Invalid flights"
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)
armor = dict_from_flight(flights[PinpointStrike])
op.setup(target=self.defenders,
attackers=db.unitdict_restrict_count(armor, sum(self.defenders.values())),
strikegroup=strikegroup)
strikegroup=flights[CAS])
self.operation = op

View File

@ -22,6 +22,16 @@ class FrontlinePatrolEvent(Event):
def threat_description(self):
return "{} aircraft + ? CAS".format(self.to_cp.base.scramble_count(self.game.settings.multiplier * self.ESCORT_FACTOR, CAP))
@property
def tasks(self):
return [CAP, PinpointStrike]
def flight_name(self, for_task: typing.Type[Task]) -> str:
if for_task == CAP:
return "CAP flight"
elif for_task == PinpointStrike:
return "Ground attack"
def __str__(self):
return "Frontline CAP"
@ -65,23 +75,23 @@ class FrontlinePatrolEvent(Event):
def skip(self):
pass
def player_attacking(self, interceptors: db.PlaneDict, clients: db.PlaneDict, armor: db.ArmorDict):
def player_attacking(self, flights: ScrambledFlightsDict):
assert flights[CAP] and flights[PinpointStrike] and len(flights) == 2, "Invalid flights"
self.cas = self.to_cp.base.scramble_cas(self.game.settings.multiplier)
self.escort = self.to_cp.base.scramble_sweep(self.game.settings.multiplier * self.ESCORT_FACTOR)
op = FrontlinePatrolOperation(game=self.game,
attacker_name=self.attacker_name,
defender_name=self.defender_name,
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())),
op.setup(cas=flight_dict_from(self.cas),
escort=flight_dict_from(self.escort),
interceptors=flights[CAP],
armor_attackers=db.unitdict_restrict_count(dict_from_flight(flights[PinpointStrike]), sum(defenders.values())),
armor_defenders=defenders)
self.operation = op

View File

@ -9,7 +9,7 @@ from game.operation.infantrytransport import InfantryTransportOperation
from theater.conflicttheater import *
from userdata.debriefing import Debriefing
from .event import Event
from .event import *
class InfantryTransportEvent(Event):
@ -18,6 +18,14 @@ class InfantryTransportEvent(Event):
def __str__(self):
return "Frontline transport troops"
@property
def tasks(self):
return [Embarking]
def flight_name(self, for_task: typing.Type[Task]) -> str:
if for_task == Embarking:
return "Transport flight"
def is_successfull(self, debriefing: Debriefing):
return True
@ -29,19 +37,19 @@ class InfantryTransportEvent(Event):
else:
self.from_cp.base.affect_strength(-self.STRENGTH_INFLUENCE)
def player_attacking(self, transport: db.HeliDict, clients: db.HeliDict):
def player_attacking(self, flights: ScrambledFlightsDict):
assert flights[Embarking] and len(flights) == 1, "Invalid flights"
op = InfantryTransportOperation(
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
)
air_defense = db.find_unittype(AirDefence, self.defender_name)[0]
op.setup(transport=transport,
op.setup(transport=flights[Embarking],
aa={air_defense: 2})
self.operation = op

View File

@ -18,6 +18,14 @@ class InsurgentAttackEvent(Event):
def threat_description(self):
return ""
@property
def tasks(self):
return [CAS]
def flight_name(self, for_task: typing.Type[Task]) -> str:
if for_task == CAS:
return "Ground intercept flight"
def __str__(self):
return "Destroy insurgents"
@ -30,7 +38,9 @@ class InsurgentAttackEvent(Event):
else:
return not attackers_success
def player_defending(self, strikegroup: db.PlaneDict, clients: db.PlaneDict):
def player_defending(self, flights: ScrambledFlightsDict):
assert flights[CAS] and len(flights) == 1, "Invalid flights"
suitable_unittypes = db.find_unittype(Reconnaissance, self.attacker_name)
random.shuffle(suitable_unittypes)
unittypes = suitable_unittypes[:self.TARGET_VARIETY]
@ -40,12 +50,10 @@ class InsurgentAttackEvent(Event):
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)
strikegroup=flights[CAS])
self.operation = op

View File

@ -9,7 +9,7 @@ from game.operation.intercept import InterceptOperation
from theater.conflicttheater import *
from userdata.debriefing import Debriefing
from .event import Event
from .event import *
class InterceptEvent(Event):
@ -22,6 +22,17 @@ class InterceptEvent(Event):
def __str__(self):
return "Air Intercept"
@property
def tasks(self):
return [CAP]
def flight_name(self, for_task: typing.Type[Task]) -> str:
if for_task == CAP:
if self.is_player_attacking:
return "Intercept flight"
else:
return "Escort flight"
def _enemy_scramble_multiplier(self) -> float:
is_global = self.from_cp.is_global or self.to_cp.is_global
return self.game.settings.multiplier * is_global and 0.5 or 1
@ -57,7 +68,9 @@ class InterceptEvent(Event):
if self.to_cp.captured:
self.to_cp.base.affect_strength(-self.STRENGTH_INFLUENCE)
def player_attacking(self, interceptors: db.PlaneDict, clients: db.PlaneDict):
def player_attacking(self, flights: ScrambledFlightsDict):
assert flights[CAP] and len(flights) == 1, "Invalid flights"
escort = self.to_cp.base.scramble_sweep(self._enemy_scramble_multiplier())
self.transport_unit = random.choice(db.find_unittype(Transport, self.defender_name))
@ -67,20 +80,17 @@ class InterceptEvent(Event):
op = InterceptOperation(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(escort=escort,
op.setup(escort=flight_dict_from(escort),
transport={self.transport_unit: 1},
airdefense={airdefense_unit: self.AIRDEFENSE_COUNT},
interceptors=interceptors)
interceptors=flights[CAP])
self.operation = op
def player_defending(self, escort: db.PlaneDict, clients: db.PlaneDict):
# TODO: even not quick mission is too quick
def player_defending(self, flights: ScrambledFlightsDict):
interceptors = self.from_cp.base.scramble_interceptors(self.game.settings.multiplier)
self.transport_unit = random.choice(db.find_unittype(Transport, self.defender_name))
@ -89,14 +99,12 @@ class InterceptEvent(Event):
op = InterceptOperation(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(escort=escort,
op.setup(escort=flights[CAP],
transport={self.transport_unit: 1},
interceptors=interceptors,
interceptors=flight_dict_from(interceptors),
airdefense={})
self.operation = op

View File

@ -9,7 +9,7 @@ from game import db
from game.operation.navalintercept import NavalInterceptionOperation
from userdata.debriefing import Debriefing
from .event import Event
from .event import *
class NavalInterceptEvent(Event):
@ -26,6 +26,19 @@ class NavalInterceptEvent(Event):
def __str__(self) -> str:
return "Naval intercept"
@property
def tasks(self):
if self.is_player_attacking:
return [CAS]
else:
return [CAP]
def flight_name(self, for_task: typing.Type[Task]) -> str:
if for_task == CAS:
return "Naval intercept flight"
elif for_task == CAP:
return "CAP flight"
@property
def threat_description(self):
s = "{} ship(s)".format(self._targets_count())
@ -64,7 +77,9 @@ class NavalInterceptEvent(Event):
if self.to_cp.captured:
self.to_cp.base.affect_strength(-self.STRENGTH_INFLUENCE)
def player_attacking(self, strikegroup: db.PlaneDict, clients: db.PlaneDict):
def player_attacking(self, flights: ScrambledFlightsDict):
assert flights[CAS] and len(flights) == 1, "Invalid flights"
self.targets = {
random.choice(db.find_unittype(CargoTransportation, self.defender_name)): self._targets_count(),
}
@ -73,19 +88,19 @@ class NavalInterceptEvent(Event):
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(strikegroup=strikegroup,
op.setup(strikegroup=flights[CAS],
interceptors={},
targets=self.targets)
self.operation = op
def player_defending(self, interceptors: db.PlaneDict, clients: db.PlaneDict):
def player_defending(self, flights: ScrambledFlightsDict):
assert flights[CAP] and len(flights) == 1, "Invalid flights"
self.targets = {
random.choice(db.find_unittype(CargoTransportation, self.defender_name)): self._targets_count(),
}
@ -94,15 +109,13 @@ class NavalInterceptEvent(Event):
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(strikegroup=strikegroup,
interceptors=interceptors,
op.setup(strikegroup=flight_dict_from(strikegroup),
interceptors=flights[CAP],
targets=self.targets)
self.operation = op

View File

@ -9,7 +9,7 @@ from game.operation.strike import StrikeOperation
from theater.conflicttheater import *
from userdata.debriefing import Debriefing
from .event import Event
from .event import *
class StrikeEvent(Event):
@ -22,25 +22,40 @@ class StrikeEvent(Event):
def is_successfull(self, debriefing: Debriefing):
return True
@property
def tasks(self):
if self.is_player_attacking:
return [CAP, CAS]
else:
return [CAP]
def flight_name(self, for_task: typing.Type[Task]) -> str:
if for_task == CAP:
if self.is_player_attacking:
return "Escort flight"
else:
return "CAP flight"
elif for_task == CAS:
return "Strike flight"
def commit(self, debriefing: Debriefing):
super(StrikeEvent, self).commit(debriefing)
self.to_cp.base.affect_strength(-self.SINGLE_OBJECT_STRENGTH_INFLUENCE * len(debriefing.destroyed_objects))
def player_attacking(self, strikegroup: db.PlaneDict, escort: db.PlaneDict, clients: db.PlaneDict):
def player_attacking(self, flights: ScrambledFlightsDict):
assert flights[CAP] and flights[CAS] and len(flights) == 2, "Invalid flights"
op = StrikeOperation(
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
)
interceptors = self.to_cp.base.scramble_interceptors(self.game.settings.multiplier)
op.setup(strikegroup=strikegroup,
escort=escort,
interceptors=interceptors)
op.setup(strikegroup=flights[CAS],
escort=flights[CAP],
interceptors=flight_dict_from(interceptors))
self.operation = op

View File

@ -120,7 +120,7 @@ class Game:
if not Conflict.has_frontline_between(player_cp, enemy_cp):
continue
if self._roll(player_probability, player_cp.base.strength):
if player_probability == 100 or self._roll(player_probability, player_cp.base.strength):
if event_class == NavalInterceptEvent and enemy_cp.radials == LAND:
pass
else:
@ -131,7 +131,7 @@ class Game:
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 enemy_probability == 100 or self._roll(enemy_probability, enemy_cp.base.strength):
if event_class in enemy_generated_types:
continue

View File

@ -9,13 +9,13 @@ from gen.triggergen import *
from gen.airsupportgen import *
from gen.visualgen import *
from .operation import Operation
from .operation import *
class BaseAttackOperation(Operation):
cas = None # type: db.PlaneDict
escort = None # type: db.PlaneDict
intercept = None # type: db.PlaneDict
cas = None # type: FlightDict
escort = None # type: FlightDict
intercept = None # type: FlightDict
attack = None # type: db.ArmorDict
defense = None # type: db.ArmorDict
aa = None # type: db.AirDefenseDict
@ -23,10 +23,10 @@ class BaseAttackOperation(Operation):
trigger_radius = TRIGGER_RADIUS_SMALL
def setup(self,
cas: db.PlaneDict,
escort: db.PlaneDict,
attack: db.ArmorDict,
intercept: db.PlaneDict,
cas: FlightDict,
escort: FlightDict,
attack: FlightDict,
intercept: FlightDict,
defense: db.ArmorDict,
aa: db.AirDefenseDict):
self.cas = cas
@ -57,10 +57,10 @@ class BaseAttackOperation(Operation):
self.armorgen.generate(self.attack, self.defense)
self.aagen.generate(self.aa)
self.airgen.generate_defense(self.intercept, clients=self.defender_clients, at=self.defenders_starting_position)
self.airgen.generate_defense(*flight_arguments(self.intercept), at=self.defenders_starting_position)
self.airgen.generate_cas_strikegroup(self.cas, 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.airgen.generate_cas_strikegroup(*flight_arguments(self.cas), at=self.attackers_starting_position)
self.airgen.generate_attackers_escort(*flight_arguments(self.escort), at=self.attackers_starting_position)
self.visualgen.generate_target_smokes(self.to_cp)

View File

@ -12,21 +12,21 @@ from gen.airsupportgen import *
from gen.visualgen import *
from gen.conflictgen import Conflict
from .operation import Operation
from .operation import *
MAX_DISTANCE_BETWEEN_GROUPS = 12000
class FrontlineAttackOperation(Operation):
strikegroup = None # type: FlightDict
attackers = None # type: db.ArmorDict
strikegroup = None # type: db.PlaneDict
target = None # type: db.ArmorDict
def setup(self,
target: db.ArmorDict,
attackers: db.ArmorDict,
strikegroup: db.PlaneDict):
strikegroup: FlightDict):
self.strikegroup = strikegroup
self.target = target
self.attackers = attackers
@ -50,7 +50,7 @@ class FrontlineAttackOperation(Operation):
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(*flight_arguments(self.strikegroup), at=self.attackers_starting_position)
self.briefinggen.title = "Frontline CAS"
self.briefinggen.description = "Provide CAS for the ground forces attacking enemy lines. Operation will be considered successful if total number of enemy units will be lower than your own by a factor of 1.5 (i.e. with 12 units from both sides, enemy forces need to be reduced to at least 8), meaning that you (and, probably, your wingmans) should concentrate on destroying the enemy units. Target base strength will be lowered as a result. Be advised that your flight will not attack anything until you explicitly tell them so by comms menu."

View File

@ -12,21 +12,26 @@ from gen.airsupportgen import *
from gen.visualgen import *
from gen.conflictgen import Conflict
from .operation import Operation
from .operation import *
MAX_DISTANCE_BETWEEN_GROUPS = 12000
class FrontlinePatrolOperation(Operation):
cas = None # type: db.PlaneDict
escort = None # type: db.PlaneDict
interceptors = None # type: db.PlaneDict
cas = None # type: FlightDict
escort = None # type: FlightDict
interceptors = None # type: FlightDict
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):
def setup(self,
cas: FlightDict,
escort: FlightDict,
interceptors: FlightDict,
armor_attackers: db.ArmorDict,
armor_defenders: db.ArmorDict):
self.cas = cas
self.escort = escort
self.interceptors = interceptors
@ -50,9 +55,9 @@ class FrontlinePatrolOperation(Operation):
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_migcap(self.interceptors, self.attacker_clients, self.attackers_starting_position)
self.airgen.generate_defenders_cas(*flight_arguments(self.cas), at=self.defenders_starting_position)
self.airgen.generate_defenders_escort(*flight_arguments(self.escort), at=self.defenders_starting_position)
self.airgen.generate_migcap(*flight_arguments(self.interceptors), at=self.attackers_starting_position)
self.armorgen.generate_vec(self.armor_attackers, self.armor_defenders)

View File

@ -10,14 +10,14 @@ from gen.airsupportgen import *
from gen.visualgen import *
from gen.conflictgen import Conflict
from .operation import Operation
from .operation import *
class InfantryTransportOperation(Operation):
transport = None # type: db.HeliDict
transport = None # type: FlightDict
aa = None # type: db.AirDefenseDict
def setup(self, transport: db.HeliDict, aa: db.AirDefenseDict):
def setup(self, transport: FlightDict, aa: db.AirDefenseDict):
self.transport = transport
self.aa = aa
@ -36,11 +36,7 @@ class InfantryTransportOperation(Operation):
conflict=conflict)
def generate(self):
self.airgen.generate_passenger_transport(
helis=self.transport,
clients=self.attacker_clients,
at=self.attackers_starting_position
)
self.airgen.generate_passenger_transport(*flight_arguments(self.transport), at=self.attackers_starting_position)
self.armorgen.generate_passengers(count=6)
self.aagen.generate_at_defenders_location(self.aa)

View File

@ -10,16 +10,16 @@ from gen.airsupportgen import *
from gen.visualgen import *
from gen.conflictgen import Conflict
from .operation import Operation
from .operation import *
class InsurgentAttackOperation(Operation):
strikegroup = None # type: db.PlaneDict
strikegroup = None # type: FlightDict
target = None # type: db.ArmorDict
def setup(self,
target: db.ArmorDict,
strikegroup: db.PlaneDict):
strikegroup: FlightDict):
self.strikegroup = strikegroup
self.target = target
@ -38,7 +38,7 @@ class InsurgentAttackOperation(Operation):
conflict=conflict)
def generate(self):
self.airgen.generate_defense(self.strikegroup, self.defender_clients, self.defenders_starting_position)
self.airgen.generate_defense(*flight_arguments(self.strikegroup), at=self.defenders_starting_position)
self.armorgen.generate(self.target, {})
self.briefinggen.title = "Destroy insurgents"

View File

@ -1,22 +1,22 @@
from dcs.terrain import Terrain
from gen import *
from .operation import Operation
from .operation import *
class InterceptOperation(Operation):
escort = None # type: db.PlaneDict
escort = None # type: FlightDict
transport = None # type: db.PlaneDict
interceptors = None # type: db.PlaneDict
interceptors = None # type: FlightDict
airdefense = None # type: db.AirDefenseDict
trigger_radius = TRIGGER_RADIUS_LARGE
def setup(self,
escort: db.PlaneDict,
escort: FlightDict,
transport: db.PlaneDict,
airdefense: db.AirDefenseDict,
interceptors: db.PlaneDict):
interceptors: FlightDict):
self.escort = escort
self.transport = transport
self.airdefense = airdefense
@ -52,9 +52,9 @@ class InterceptOperation(Operation):
self.attackers_starting_position = ship
self.airgen.generate_transport(self.transport, self.to_cp.at)
self.airgen.generate_defenders_escort(self.escort, clients=self.defender_clients)
self.airgen.generate_defenders_escort(*flight_arguments(self.escort), at=self.defenders_starting_position)
self.airgen.generate_interception(self.interceptors, clients=self.attacker_clients, at=self.attackers_starting_position)
self.airgen.generate_interception(*flight_arguments(self.interceptors), at=self.attackers_starting_position)
self.briefinggen.title = "Air Intercept"
self.briefinggen.description = "Intercept enemy supply transport aircraft. Escort will also be present if there are available planes on the base. Operation will be considered successful if most of the targets are destroyed, lowering targets strength as a result"

View File

@ -1,18 +1,18 @@
from dcs.terrain import Terrain
from gen import *
from .operation import Operation
from .operation import *
class NavalInterceptionOperation(Operation):
strikegroup = None # type: db.PlaneDict
interceptors = None # type: db.PlaneDict
strikegroup = None # type: FlightDict
interceptors = None # type: FlightDict
targets = None # type: db.ShipDict
trigger_radius = TRIGGER_RADIUS_LARGE
def setup(self,
strikegroup: db.PlaneDict,
interceptors: db.PlaneDict,
strikegroup: FlightDict,
interceptors: FlightDict,
targets: db.ShipDict):
self.strikegroup = strikegroup
self.interceptors = interceptors
@ -37,16 +37,14 @@ class NavalInterceptionOperation(Operation):
target_groups = self.shipgen.generate_cargo(units=self.targets)
self.airgen.generate_ship_strikegroup(
attackers=self.strikegroup,
clients=self.attacker_clients,
*flight_arguments(self.strikegroup),
target_groups=target_groups,
at=self.attackers_starting_position
)
if self.interceptors:
self.airgen.generate_defense(
defenders=self.interceptors,
clients=self.defender_clients,
*flight_arguments(self.interceptors),
at=self.defenders_starting_position
)

View File

@ -1,11 +1,27 @@
from dcs.terrain import Terrain
import typing
from dcs.lua.parse import loads
from dcs.unittype import UnitType
from userdata.debriefing import *
from theater import *
from gen import *
FlightDict = typing.Dict[typing.Type[UnitType], typing.Tuple[int, int]]
def flight_arguments(fd: FlightDict) -> typing.Tuple[db.PlaneDict, db.PlaneDict]:
return {k: v1 for k, (v1, v2) in fd.items()}, {k: v2 for k, (v1, v2) in fd.items()},
def flight_dict_from(d: db.PlaneDict) -> FlightDict:
return {k: (v, 0) for k, v in d.items()}
def dict_from_flight(fd: FlightDict) -> db.Dict:
return {k: v1 for k, (v1, v2) in fd.items()}
class Operation:
attackers_starting_position = None # type: db.StartingPosition
@ -33,15 +49,11 @@ class Operation:
game,
attacker_name: str,
defender_name: str,
attacker_clients: db.PlaneDict,
defender_clients: db.PlaneDict,
from_cp: ControlPoint,
to_cp: ControlPoint = None):
self.game = game
self.attacker_name = attacker_name
self.defender_name = defender_name
self.attacker_clients = attacker_clients
self.defender_clients = defender_clients
self.from_cp = from_cp
self.to_cp = to_cp
self.is_quick = False

View File

@ -10,18 +10,18 @@ from gen.airsupportgen import *
from gen.visualgen import *
from gen.conflictgen import Conflict
from .operation import Operation
from .operation import *
class StrikeOperation(Operation):
strikegroup = None # type: db.PlaneDict
escort = None # type: db.PlaneDict
interceptors = None # type: db.PlaneDict
strikegroup = None # type: FlightDict
escort = None # type: FlightDict
interceptors = None # type: FlightDict
def setup(self,
strikegroup: db.PlaneDict,
escort: db.PlaneDict,
interceptors: db.PlaneDict):
strikegroup: FlightDict,
escort: FlightDict,
interceptors: FlightDict):
self.strikegroup = strikegroup
self.escort = escort
self.interceptors = interceptors
@ -49,10 +49,10 @@ class StrikeOperation(Operation):
category_counters = {} # type: typing.Dict[str, int]
processed_groups = []
for object in self.to_cp.ground_objects:
if object.group_id in processed_groups:
if object.group_identifier in processed_groups:
continue
processed_groups.append(object.group_id)
processed_groups.append(object.group_identifier)
category_counters[object.category] = category_counters.get(object.category, 0) + 1
markpoint_name = "{}{}".format(object.name_abbrev, category_counters[object.category])
@ -61,18 +61,13 @@ class StrikeOperation(Operation):
targets.sort(key=lambda x: self.from_cp.position.distance_to_point(x[1]))
self.airgen.generate_ground_attack_strikegroup(strikegroup=self.strikegroup,
self.airgen.generate_ground_attack_strikegroup(*flight_arguments(self.strikegroup),
targets=targets,
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.airgen.generate_attackers_escort(*flight_arguments(self.escort), at=self.attackers_starting_position)
self.airgen.generate_barcap(patrol=self.interceptors,
clients={},
at=self.defenders_starting_position)
self.airgen.generate_barcap(*flight_arguments(self.interceptors), at=self.defenders_starting_position)
self.briefinggen.title = "Strike"
self.briefinggen.description = "Destroy infrastructure assets and military supplies in the region. Each building destroyed will lower targets strength."

View File

@ -2,7 +2,7 @@
class Settings:
player_skill = "Good"
enemy_skill = "Average"
only_player_takeoff = False
only_player_takeoff = True
night_disabled = False
multiplier = 1
sams = True

View File

@ -265,7 +265,7 @@ class AircraftConflictGenerator:
self.escort_targets.append((group, group.points.index(waypoint)))
self._rtb_for(group, self.conflict.from_cp, at)
def generate_ground_attack_strikegroup(self, strikegroup: db.PlaneDict, targets: typing.List[typing.Tuple[str, Point]], clients: db.PlaneDict, at: db.StartingPosition = None):
def generate_ground_attack_strikegroup(self, strikegroup: db.PlaneDict, clients: db.PlaneDict, targets: typing.List[typing.Tuple[str, Point]], at: db.StartingPosition = None):
assert len(self.escort_targets) == 0
for flying_type, count, client_count in self._split_to_groups(strikegroup, clients):

5
gen/heli.py Normal file
View File

@ -0,0 +1,5 @@
from .aircraft import *
class HelicopterConflictGenerator(AircraftConflictGenerator):
pass

View File

@ -41,6 +41,10 @@ class TheaterGroundObject:
def string_identifier(self):
return "{}|{}|{}|{}".format(self.category, self.cp_id, self.group_id, self.object_id)
@property
def group_identifier(self) -> str:
return "{}|{}".format(self.category, self.group_id)
@property
def name_abbrev(self) -> str:
return ABBREV_NAME[self.category]

View File

@ -7,27 +7,9 @@ from game.event import *
from .styles import STYLES, RED
UNITTYPES_FOR_EVENTS = {
FrontlineAttackEvent: [[CAS, PinpointStrike], [CAP]],
FrontlinePatrolEvent: [[CAP, PinpointStrike], [CAP]],
BaseAttackEvent: [[CAP, CAS, PinpointStrike], [CAP, CAS, PinpointStrike]],
StrikeEvent: [[CAP, CAS], [CAP]],
InterceptEvent: [[CAP], [CAP]],
InsurgentAttackEvent: [[CAS], [CAP]],
NavalInterceptEvent: [[CAS], [CAP]],
InfantryTransportEvent: [[Embarking], [CAP]],
}
AI_BAN_FOR_EVENTS = {
InfantryTransportEvent: [Embarking],
StrikeEvent: [CAS],
}
class EventMenu(Menu):
aircraft_scramble_entries = None # type: typing.Dict[PlaneType , Entry]
aircraft_client_entries = None # type: typing.Dict[PlaneType, Entry]
armor_scramble_entries = None # type: typing.Dict[VehicleType, Entry]
scramble_entries = None # type: typing.Dict[typing.Type[Task], typing.Dict[typing.Type[UnitType], typing.Tuple[Entry, Entry]]]
error_label = None # type: Label
awacs = None # type: IntVar
@ -35,9 +17,7 @@ class EventMenu(Menu):
super(EventMenu, self).__init__(window, parent, game)
self.event = event
self.aircraft_scramble_entries = {}
self.armor_scramble_entries = {}
self.aircraft_client_entries = {}
self.scramble_entries = {k: {} for k in self.event.tasks}
if self.event.attacker_name == self.game.player:
self.base = self.event.from_cp.base
@ -68,32 +48,24 @@ class EventMenu(Menu):
return new_label
def scrable_row(unit_type, unit_count):
def scrable_row(task_type, unit_type, unit_count, client_slots: bool):
nonlocal row
Label(self.frame, text="{} ({})".format(db.unit_type_name(unit_type), unit_count), **STYLES["widget"]).grid(row=row, sticky=W)
scramble_entry = Entry(self.frame, width=2)
scramble_entry.grid(column=1, row=row, sticky=E, padx=5)
scramble_entry.insert(0, "0")
self.aircraft_scramble_entries[unit_type] = scramble_entry
Button(self.frame, text="+", command=self.scramble_half(True, unit_type), **STYLES["btn-primary"]).grid(column=2, row=row)
Button(self.frame, text="+", command=self.scramble_half(task_type, unit_type), **STYLES["btn-primary"]).grid(column=2, row=row)
client_entry = Entry(self.frame, width=2)
client_entry.grid(column=3, row=row, sticky=E, padx=5)
client_entry.insert(0, "0")
self.aircraft_client_entries[unit_type] = client_entry
Button(self.frame, text="+", command=self.client_one(unit_type), **STYLES["btn-primary"]).grid(column=4, row=row)
if client_slots:
client_entry = Entry(self.frame, width=2)
client_entry.grid(column=3, row=row, sticky=E, padx=5)
client_entry.insert(0, "0")
Button(self.frame, text="+", command=self.client_one(task_type, unit_type), **STYLES["btn-primary"]).grid(column=4, row=row)
else:
client_entry = None
row += 1
def scramble_armor_row(unit_type, unit_count):
nonlocal row
Label(self.frame, text="{} ({})".format(db.unit_type_name(unit_type), unit_count), **STYLES["widget"]).grid(row=row, sticky=W)
scramble_entry = Entry(self.frame, width=2)
scramble_entry.insert(0, "0")
scramble_entry.grid(column=1, row=row, sticky=E, padx=5)
self.armor_scramble_entries[unit_type] = scramble_entry
Button(self.frame, text="+", command=self.scramble_half(False, unit_type),**STYLES["btn-primary"]).grid(column=2, row=row)
self.scramble_entries[task_type][unit_type] = scramble_entry, client_entry
row += 1
@ -108,84 +80,53 @@ class EventMenu(Menu):
Label(self.frame, text="{}. {}".format(self.event, threat_descr), **STYLES["mission-preview"]).grid(row=row, column=0, columnspan=5, sticky=S+EW, padx=5, pady=5)
row += 1
header("Aircraft :")
Label(self.frame, text="Amount", **STYLES["widget"]).grid(row=row, column=1, columnspan=2)
Label(self.frame, text="Client slots", **STYLES["widget"]).grid(row=row, column=3, columnspan=2)
row += 1
if self.base.aircraft:
Label(self.frame, text="Amount", **STYLES["widget"]).grid(row=row, column=1, columnspan=2)
Label(self.frame, text="Client slots", **STYLES["widget"]).grid(row=row, column=3, columnspan=2)
row += 1
for flight_task in self.event.tasks:
header("{}:".format(self.event.flight_name(flight_task)))
if flight_task == PinpointStrike:
if not self.base.armor:
label("No units")
for t, c in self.base.armor.items():
scrable_row(flight_task, t, c, client_slots=False)
else:
if not self.base.aircraft:
label("No units")
for t, c in self.base.aircraft.items():
scrable_row(flight_task, t, c, client_slots=True)
filter_attackers_index = 0 if self.game.is_player_attack(self.event) else 1
filter_to = UNITTYPES_FOR_EVENTS[self.event.__class__][filter_attackers_index]
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)
if not self.base.total_planes:
label("None", sticky=W)
header("Armor :")
armor_counter = 0
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)
armor_counter += 1
if not self.base.total_armor or armor_counter == 0:
label("None", sticky=W)
header("Support :")
header("Support:")
# Options
awacs_enabled = self.game.budget >= AWACS_BUDGET_COST and NORMAL or DISABLED
Checkbutton(self.frame, var=self.awacs, state=awacs_enabled, **STYLES["radiobutton"]).grid(row=row, column=0, sticky=E)
Label(self.frame, text="AWACS ({}m)".format(AWACS_BUDGET_COST), **STYLES["widget"]).grid(row=row, column=3, sticky=W, padx=5, pady=5)
row += 1
header("Ready ?")
header("Ready?")
self.error_label = label("")
self.error_label["fg"] = RED
Button(self.frame, text="Commit", command=self.start, **STYLES["btn-primary"]).grid(column=0, row=row, sticky=E, padx=5, pady=(10,10))
Button(self.frame, text="Back", command=self.dismiss, **STYLES["btn-warning"]).grid(column=3, row=row, sticky=E, padx=5, pady=(10,10))
row += 1
def _scrambled_aircraft_count(self, unit_type: UnitType) -> int:
value = self.aircraft_scramble_entries[unit_type].get()
if value and int(value) > 0:
return min(int(value), self.base.aircraft[unit_type])
return 0
def _scrambled_armor_count(self, unit_type: UnitType) -> int:
value = self.armor_scramble_entries[unit_type].get()
if value and int(value) > 0:
return min(int(value), self.base.armor[unit_type])
return 0
def scramble_half(self, aircraft: bool, unit_type: UnitType) -> typing.Callable:
def scramble_half(self, task: typing.Type[UnitType], unit_type: UnitType) -> typing.Callable:
def action():
entry = None # type: Entry
total_count = 0
if aircraft:
entry = self.aircraft_scramble_entries[unit_type]
total_count = self.base.aircraft[unit_type]
else:
entry = self.armor_scramble_entries[unit_type]
total_count = self.base.armor[unit_type]
entry = self.scramble_entries[task][unit_type][0] # type: Entry
value = entry.get()
existing_count = int(entry.get())
total_units = self.base.total_units_of_type(unit_type)
amount = int(value and value or "0")
entry.delete(0, END)
entry.insert(0, "{}".format(int(existing_count + math.ceil(total_count/2))))
entry.insert(0, str(amount + int(math.ceil(total_units/2))))
return action
def client_one(self, unit_type: UnitType) -> typing.Callable:
def client_one(self, task: typing.Type[Task], unit_type: UnitType) -> typing.Callable:
def action():
entry = self.aircraft_client_entries[unit_type] # type: Entry
entry = self.scramble_entries[task][unit_type][1] # type: Entry
value = entry.get()
amount = int(value and value or "0")
entry.delete(0, END)
@ -199,94 +140,37 @@ class EventMenu(Menu):
else:
self.event.is_awacs_enabled = False
scrambled_aircraft = {}
scrambled_sweep = {}
scrambled_cas = {}
for unit_type, field in self.aircraft_scramble_entries.items():
amount = self._scrambled_aircraft_count(unit_type)
if amount > 0:
task = db.unit_task(unit_type)
flights = {k: {} for k in self.event.tasks} # type: ScrambledFlightsDict
total_counts_scrambled = {} # type: typing.Dict[typing.Type[UnitType], int]
scrambled_aircraft[unit_type] = amount
if task == CAS:
scrambled_cas[unit_type] = amount
elif task == CAP:
scrambled_sweep[unit_type] = amount
def dampen_count(unit_type: typing.Type[UnitType], count: int) -> int:
nonlocal total_counts_scrambled
total_count = self.base.total_units_of_type(unit_type)
scrambled_clients = {}
for unit_type, field in self.aircraft_client_entries.items():
value = field.get()
if value and int(value) > 0:
amount = int(value)
scrambled_clients[unit_type] = amount
total_scrambled = total_counts_scrambled.get(unit_type, 0)
dampened_value = count if count + total_scrambled < total_count else total_count - total_scrambled
total_counts_scrambled[unit_type] = total_counts_scrambled.get(unit_type, 0) + dampened_value
return dampened_value
scrambled_armor = {}
for unit_type, field in self.armor_scramble_entries.items():
amount = self._scrambled_armor_count(unit_type)
if amount > 0:
scrambled_armor[unit_type] = amount
for task_type, dict in self.scramble_entries.items():
for unit_type, (count_entry, clients_entry) in dict.items():
try:
count = int(count_entry.get())
except:
count = 0
if type(self.event) in AI_BAN_FOR_EVENTS:
banned_tasks_for_ai = AI_BAN_FOR_EVENTS[type(self.event)]
for task in banned_tasks_for_ai:
scrambled_slots = [1 for x in scrambled_aircraft if db.unit_task(x) == task]
scrambled_client_slots = [1 for x in scrambled_clients if db.unit_task(x) == task]
try:
clients_count = int(clients_entry and clients_entry.get() or 0)
except:
clients_count = 0
if scrambled_slots != scrambled_client_slots:
self.error_label["text"] = "AI slots of task {} are not supported for this operation".format(task.name)
return
flights[task_type][unit_type] = dampen_count(unit_type, count), clients_count
if type(self.event) is BaseAttackEvent:
e = self.event # type: BaseAttackEvent
if self.game.is_player_attack(self.event):
e.player_attacking(cas=scrambled_cas,
escort=scrambled_sweep,
armor=scrambled_armor,
clients=scrambled_clients)
else:
e.player_defending(interceptors=scrambled_aircraft,
clients=scrambled_clients)
elif type(self.event) is InterceptEvent:
e = self.event # type: InterceptEvent
if self.game.is_player_attack(self.event):
e.player_attacking(interceptors=scrambled_aircraft,
clients=scrambled_clients)
else:
e.player_defending(escort=scrambled_aircraft,
clients=scrambled_clients)
elif type(self.event) is FrontlineAttackEvent:
e = self.event # type: FrontlineAttackEvent
e.player_attacking(armor=scrambled_armor, strikegroup=scrambled_aircraft, clients=scrambled_clients)
elif type(self.event) is FrontlinePatrolEvent:
e = self.event # type: FrontlinePatrolEvent
e.player_attacking(interceptors=scrambled_aircraft, clients=scrambled_clients, armor=scrambled_armor)
elif type(self.event) is NavalInterceptEvent:
e = self.event # type: NavalInterceptEvent
if self.game.is_player_attack(self.event):
e.player_attacking(strikegroup=scrambled_aircraft, clients=scrambled_clients)
else:
e.player_defending(interceptors=scrambled_aircraft, clients=scrambled_clients)
elif type(self.event) is InsurgentAttackEvent:
e = self.event # type: InsurgentAttackEvent
if self.game.is_player_attack(self.event):
assert False
else:
e.player_defending(strikegroup=scrambled_aircraft, clients=scrambled_clients)
elif type(self.event) is InfantryTransportEvent:
e = self.event # type: InfantryTransportEvent
if self.game.is_player_attack(self.event):
e.player_attacking(transport=scrambled_aircraft, clients=scrambled_clients)
else:
assert False
elif type(self.event) is StrikeEvent:
e = self.event # type: StrikeEvent
if self.game.is_player_attack(self.event):
e.player_attacking(strikegroup=scrambled_cas,
escort=scrambled_sweep,
clients=scrambled_clients)
else:
assert False
if self.game.is_player_attack(self.event):
self.event.player_attacking(flights)
else:
self.event.player_defending(flights)
self.game.initiate_event(self.event)
EventResultsMenu(self.window, self.parent, self.game, self.event).display()