Add runway bombing missions.

This allows planning the missions and the missions are functional, but
they will have no effect on future turns yet.
This commit is contained in:
Dan Albert 2020-11-23 01:38:27 -08:00
parent 63bdbebcaa
commit 9394ed663a
10 changed files with 237 additions and 87 deletions

View File

@ -401,16 +401,18 @@ class Airfield(ControlPoint):
return True return True
def mission_types(self, for_player: bool) -> Iterator[FlightType]: def mission_types(self, for_player: bool) -> Iterator[FlightType]:
yield from super().mission_types(for_player) from gen.flights.flight import FlightType
if self.is_friendly(for_player): if self.is_friendly(for_player):
yield from [ yield from [
# TODO: FlightType.INTERCEPTION # TODO: FlightType.INTERCEPTION
# TODO: FlightType.LOGISTICS # TODO: FlightType.LOGISTICS
] ]
else: else:
yield from [ yield from [
# TODO: FlightType.STRIKE FlightType.RUNWAY_ATTACK,
] # TODO: FlightType.OCA_STRIKE
]
yield from super().mission_types(for_player)
@property @property
def total_aircraft_parking(self) -> int: def total_aircraft_parking(self) -> int:

View File

@ -41,6 +41,7 @@ from dcs.task import (
AntishipStrike, AntishipStrike,
AttackGroup, AttackGroup,
Bombing, Bombing,
BombingRunway,
CAP, CAP,
CAS, CAS,
ControlledTask, ControlledTask,
@ -55,6 +56,7 @@ from dcs.task import (
OptReactOnThreat, OptReactOnThreat,
OptRestrictJettison, OptRestrictJettison,
OrbitAction, OrbitAction,
RunwayAttack,
SEAD, SEAD,
StartCommand, StartCommand,
Targets, Targets,
@ -1169,6 +1171,18 @@ class AircraftConflictGenerator:
roe=OptROE.Values.OpenFire, roe=OptROE.Values.OpenFire,
restrict_jettison=True) restrict_jettison=True)
def configure_runway_attack(
self, group: FlyingGroup, package: Package, flight: Flight,
dynamic_runways: Dict[str, RunwayData]) -> None:
group.task = RunwayAttack.name
self._setup_group(group, RunwayAttack, package, flight,
dynamic_runways)
self.configure_behavior(
group,
react_on_threat=OptReactOnThreat.Values.EvadeFire,
roe=OptROE.Values.OpenFire,
restrict_jettison=True)
def configure_escort(self, group: FlyingGroup, package: Package, def configure_escort(self, group: FlyingGroup, package: Package,
flight: Flight, flight: Flight,
dynamic_runways: Dict[str, RunwayData]) -> None: dynamic_runways: Dict[str, RunwayData]) -> None:
@ -1206,6 +1220,9 @@ class AircraftConflictGenerator:
self.configure_anti_ship(group, package, flight, dynamic_runways) self.configure_anti_ship(group, package, flight, dynamic_runways)
elif flight_type == FlightType.ESCORT: elif flight_type == FlightType.ESCORT:
self.configure_escort(group, package, flight, dynamic_runways) self.configure_escort(group, package, flight, dynamic_runways)
elif flight_type == FlightType.RUNWAY_ATTACK:
self.configure_runway_attack(group, package, flight,
dynamic_runways)
else: else:
self.configure_unknown_task(group, flight) self.configure_unknown_task(group, flight)
@ -1345,6 +1362,7 @@ class PydcsWaypointBuilder:
FlightWaypointType.INGRESS_BAI: BaiIngressBuilder, FlightWaypointType.INGRESS_BAI: BaiIngressBuilder,
FlightWaypointType.INGRESS_CAS: CasIngressBuilder, FlightWaypointType.INGRESS_CAS: CasIngressBuilder,
FlightWaypointType.INGRESS_DEAD: DeadIngressBuilder, FlightWaypointType.INGRESS_DEAD: DeadIngressBuilder,
FlightWaypointType.INGRESS_RUNWAY_BOMBING: RunwayBombingIngressBuilder,
FlightWaypointType.INGRESS_SEAD: SeadIngressBuilder, FlightWaypointType.INGRESS_SEAD: SeadIngressBuilder,
FlightWaypointType.INGRESS_STRIKE: StrikeIngressBuilder, FlightWaypointType.INGRESS_STRIKE: StrikeIngressBuilder,
FlightWaypointType.JOIN: JoinPointBuilder, FlightWaypointType.JOIN: JoinPointBuilder,
@ -1480,6 +1498,22 @@ class DeadIngressBuilder(PydcsWaypointBuilder):
return waypoint return waypoint
class RunwayBombingIngressBuilder(PydcsWaypointBuilder):
def build(self) -> MovingPoint:
waypoint = super().build()
target = self.package.target
if not isinstance(target, Airfield):
logging.error(
"Unexpected target type for runway bombing mission: %s",
target.__class__.__name__)
return waypoint
waypoint.tasks.append(
BombingRunway(airport_id=target.airport.id, group_attack=True))
return waypoint
class SeadIngressBuilder(PydcsWaypointBuilder): class SeadIngressBuilder(PydcsWaypointBuilder):
def build(self) -> MovingPoint: def build(self) -> MovingPoint:
waypoint = super().build() waypoint = super().build()

View File

@ -44,6 +44,8 @@ from gen.flights.ai_flight_planner_db import (
CAP_PREFERRED, CAP_PREFERRED,
CAS_CAPABLE, CAS_CAPABLE,
CAS_PREFERRED, CAS_PREFERRED,
RUNWAY_ATTACK_CAPABLE,
RUNWAY_ATTACK_PREFERRED,
SEAD_CAPABLE, SEAD_CAPABLE,
SEAD_PREFERRED, SEAD_PREFERRED,
STRIKE_CAPABLE, STRIKE_CAPABLE,
@ -160,6 +162,8 @@ class AircraftAllocator:
return CAS_PREFERRED return CAS_PREFERRED
elif task in (FlightType.DEAD, FlightType.SEAD): elif task in (FlightType.DEAD, FlightType.SEAD):
return SEAD_PREFERRED return SEAD_PREFERRED
elif task == FlightType.RUNWAY_ATTACK:
return RUNWAY_ATTACK_PREFERRED
elif task == FlightType.STRIKE: elif task == FlightType.STRIKE:
return STRIKE_PREFERRED return STRIKE_PREFERRED
elif task == FlightType.ESCORT: elif task == FlightType.ESCORT:
@ -180,6 +184,8 @@ class AircraftAllocator:
return CAS_CAPABLE return CAS_CAPABLE
elif task in (FlightType.DEAD, FlightType.SEAD): elif task in (FlightType.DEAD, FlightType.SEAD):
return SEAD_CAPABLE return SEAD_CAPABLE
elif task == FlightType.RUNWAY_ATTACK:
return RUNWAY_ATTACK_CAPABLE
elif task == FlightType.STRIKE: elif task == FlightType.STRIKE:
return STRIKE_CAPABLE return STRIKE_CAPABLE
elif task == FlightType.ESCORT: elif task == FlightType.ESCORT:

View File

@ -484,6 +484,13 @@ ANTISHIP_PREFERRED = [
Su_24M, Su_24M,
] ]
RUNWAY_ATTACK_PREFERRED = [
JF_17,
M_2000C,
]
RUNWAY_ATTACK_CAPABLE = STRIKE_CAPABLE
DRONES = [ DRONES = [
MQ_9_Reaper, MQ_9_Reaper,
RQ_1A_Predator, RQ_1A_Predator,

View File

@ -39,6 +39,7 @@ class FlightType(Enum):
EWAR = 16 EWAR = 16
SWEEP = 17 SWEEP = 17
RUNWAY_ATTACK = 18
class FlightWaypointType(Enum): class FlightWaypointType(Enum):
@ -66,6 +67,7 @@ class FlightWaypointType(Enum):
INGRESS_SWEEP = 21 INGRESS_SWEEP = 21
INGRESS_BAI = 22 INGRESS_BAI = 22
DIVERT = 23 DIVERT = 23
INGRESS_RUNWAY_BOMBING = 24
class FlightWaypoint: class FlightWaypoint:

View File

@ -20,6 +20,7 @@ from dcs.unit import Unit
from game.data.doctrine import Doctrine from game.data.doctrine import Doctrine
from game.theater import ( from game.theater import (
Airfield,
ControlPoint, ControlPoint,
FrontLine, FrontLine,
MissionTarget, MissionTarget,
@ -650,6 +651,8 @@ class FlightPlanBuilder:
return self.generate_dead(flight, custom_targets) return self.generate_dead(flight, custom_targets)
elif task == FlightType.ESCORT: elif task == FlightType.ESCORT:
return self.generate_escort(flight) return self.generate_escort(flight)
elif task == FlightType.RUNWAY_ATTACK:
return self.generate_runway_attack(flight)
elif task == FlightType.SEAD: elif task == FlightType.SEAD:
return self.generate_sead(flight, custom_targets) return self.generate_sead(flight, custom_targets)
elif task == FlightType.STRIKE: elif task == FlightType.STRIKE:
@ -713,7 +716,9 @@ class FlightPlanBuilder:
targets.append(StrikeTarget(building.category, building)) targets.append(StrikeTarget(building.category, building))
return self.strike_flightplan(flight, location, targets) return self.strike_flightplan(flight, location,
FlightWaypointType.INGRESS_STRIKE,
targets)
def generate_bai(self, flight: Flight) -> StrikeFlightPlan: def generate_bai(self, flight: Flight) -> StrikeFlightPlan:
"""Generates a BAI flight plan. """Generates a BAI flight plan.
@ -731,7 +736,8 @@ class FlightPlanBuilder:
targets.append( targets.append(
StrikeTarget(f"{group.name} at {location.name}", group)) StrikeTarget(f"{group.name} at {location.name}", group))
return self.strike_flightplan(flight, location, targets) return self.strike_flightplan(flight, location,
FlightWaypointType.INGRESS_BAI, targets)
def generate_anti_ship(self, flight: Flight) -> StrikeFlightPlan: def generate_anti_ship(self, flight: Flight) -> StrikeFlightPlan:
"""Generates an anti-ship flight plan. """Generates an anti-ship flight plan.
@ -756,7 +762,8 @@ class FlightPlanBuilder:
targets.append( targets.append(
StrikeTarget(f"{group.name} at {location.name}", group)) StrikeTarget(f"{group.name} at {location.name}", group))
return self.strike_flightplan(flight, location, targets) return self.strike_flightplan(flight, location,
FlightWaypointType.INGRESS_BAI, targets)
def generate_barcap(self, flight: Flight) -> BarCapFlightPlan: def generate_barcap(self, flight: Flight) -> BarCapFlightPlan:
"""Generate a BARCAP flight at a given location. """Generate a BARCAP flight at a given location.
@ -942,7 +949,25 @@ class FlightPlanBuilder:
for target in custom_targets: for target in custom_targets:
targets.append(StrikeTarget(location.name, target)) targets.append(StrikeTarget(location.name, target))
return self.strike_flightplan(flight, location, targets) return self.strike_flightplan(flight, location,
FlightWaypointType.INGRESS_DEAD, targets)
def generate_runway_attack(self, flight: Flight) -> StrikeFlightPlan:
"""Generate a runway attack flight plan at a given location.
Args:
flight: The flight to generate the flight plan for.
"""
location = self.package.target
if not isinstance(location, Airfield):
logging.exception(
f"Invalid Objective Location for runway bombing flight "
f"{flight=} at {location=}.")
raise InvalidObjectiveLocation(flight.flight_type, location)
return self.strike_flightplan(flight, location,
FlightWaypointType.INGRESS_RUNWAY_BOMBING)
def generate_sead(self, flight: Flight, def generate_sead(self, flight: Flight,
custom_targets: Optional[List[Unit]]) -> StrikeFlightPlan: custom_targets: Optional[List[Unit]]) -> StrikeFlightPlan:
@ -963,7 +988,8 @@ class FlightPlanBuilder:
for target in custom_targets: for target in custom_targets:
targets.append(StrikeTarget(location.name, target)) targets.append(StrikeTarget(location.name, target))
return self.strike_flightplan(flight, location, targets) return self.strike_flightplan(flight, location,
FlightWaypointType.INGRESS_SEAD, targets)
def generate_escort(self, flight: Flight) -> StrikeFlightPlan: def generate_escort(self, flight: Flight) -> StrikeFlightPlan:
assert self.package.waypoints is not None assert self.package.waypoints is not None
@ -1012,7 +1038,8 @@ class FlightPlanBuilder:
flight=flight, flight=flight,
patrol_duration=self.doctrine.cas_duration, patrol_duration=self.doctrine.cas_duration,
takeoff=builder.takeoff(flight.departure), takeoff=builder.takeoff(flight.departure),
patrol_start=builder.ingress_cas(ingress, location), patrol_start=builder.ingress(FlightWaypointType.INGRESS_CAS,
ingress, location),
target=builder.cas(center), target=builder.cas(center),
patrol_end=builder.egress(egress, location), patrol_end=builder.egress(egress, location),
land=builder.land(flight.arrival), land=builder.land(flight.arrival),
@ -1101,23 +1128,11 @@ class FlightPlanBuilder:
def strike_flightplan( def strike_flightplan(
self, flight: Flight, location: MissionTarget, self, flight: Flight, location: MissionTarget,
ingress_type: FlightWaypointType,
targets: Optional[List[StrikeTarget]] = None) -> StrikeFlightPlan: targets: Optional[List[StrikeTarget]] = None) -> StrikeFlightPlan:
assert self.package.waypoints is not None assert self.package.waypoints is not None
builder = WaypointBuilder(self.game.conditions, flight, self.doctrine, builder = WaypointBuilder(self.game.conditions, flight, self.doctrine,
targets) targets)
if flight.flight_type is FlightType.SEAD:
ingress = builder.ingress_sead(self.package.waypoints.ingress,
location)
elif flight.flight_type is FlightType.DEAD:
ingress = builder.ingress_dead(self.package.waypoints.ingress,
location)
elif flight.flight_type in {FlightType.ANTISHIP, FlightType.BAI}:
ingress = builder.ingress_bai(self.package.waypoints.ingress,
location)
else:
ingress = builder.ingress_strike(self.package.waypoints.ingress,
location)
target_waypoints: List[FlightWaypoint] = [] target_waypoints: List[FlightWaypoint] = []
if targets is not None: if targets is not None:
@ -1134,7 +1149,8 @@ class FlightPlanBuilder:
takeoff=builder.takeoff(flight.departure), takeoff=builder.takeoff(flight.departure),
hold=builder.hold(self._hold_point(flight)), hold=builder.hold(self._hold_point(flight)),
join=builder.join(self.package.waypoints.join), join=builder.join(self.package.waypoints.join),
ingress=ingress, ingress=builder.ingress(ingress_type,
self.package.waypoints.ingress, location),
targets=target_waypoints, targets=target_waypoints,
egress=builder.egress(self.package.waypoints.egress, location), egress=builder.egress(self.package.waypoints.egress, location),
split=builder.split(self.package.waypoints.split), split=builder.split(self.package.waypoints.split),

View File

@ -173,38 +173,8 @@ class WaypointBuilder:
waypoint.name = "SPLIT" waypoint.name = "SPLIT"
return waypoint return waypoint
def ingress_cas(self, position: Point, def ingress(self, ingress_type: FlightWaypointType, position: Point,
objective: MissionTarget) -> FlightWaypoint: objective: MissionTarget) -> FlightWaypoint:
return self._ingress(FlightWaypointType.INGRESS_CAS, position,
objective)
def ingress_escort(self, position: Point,
objective: MissionTarget) -> FlightWaypoint:
return self._ingress(FlightWaypointType.INGRESS_ESCORT, position,
objective)
def ingress_bai(self, position: Point,
objective: MissionTarget) -> FlightWaypoint:
return self._ingress(FlightWaypointType.INGRESS_BAI, position,
objective)
def ingress_dead(self, position:Point,
objective: MissionTarget) -> FlightWaypoint:
return self._ingress(FlightWaypointType.INGRESS_DEAD, position,
objective)
def ingress_sead(self, position: Point,
objective: MissionTarget) -> FlightWaypoint:
return self._ingress(FlightWaypointType.INGRESS_SEAD, position,
objective)
def ingress_strike(self, position: Point,
objective: MissionTarget) -> FlightWaypoint:
return self._ingress(FlightWaypointType.INGRESS_STRIKE, position,
objective)
def _ingress(self, ingress_type: FlightWaypointType, position: Point,
objective: MissionTarget) -> FlightWaypoint:
waypoint = FlightWaypoint( waypoint = FlightWaypoint(
ingress_type, ingress_type,
position.x, position.x,
@ -415,8 +385,8 @@ class WaypointBuilder:
# description in gen.aircraft.JoinPointBuilder), so instead we give # description in gen.aircraft.JoinPointBuilder), so instead we give
# the escort flights a flight plan including the ingress point, target # the escort flights a flight plan including the ingress point, target
# area, and egress point. # area, and egress point.
ingress = self._ingress(FlightWaypointType.INGRESS_ESCORT, ingress, ingress = self.ingress(FlightWaypointType.INGRESS_ESCORT, ingress,
target) target)
waypoint = FlightWaypoint( waypoint = FlightWaypoint(
FlightWaypointType.TARGET_GROUP_LOC, FlightWaypointType.TARGET_GROUP_LOC,

View File

@ -1,8 +1,17 @@
from PySide2.QtCore import Qt from PySide2.QtCore import Qt
from PySide2.QtGui import QCloseEvent, QPixmap from PySide2.QtGui import QCloseEvent, QPixmap
from PySide2.QtWidgets import QDialog, QGridLayout, QHBoxLayout, QLabel, QWidget from PySide2.QtWidgets import (
QDialog,
QHBoxLayout,
QLabel,
QPushButton,
QVBoxLayout,
QWidget,
)
from game.theater import ControlPoint, ControlPointType from game.theater import ControlPoint, ControlPointType
from gen.flights.flight import FlightType
from qt_ui.dialogs import Dialog
from qt_ui.models import GameModel from qt_ui.models import GameModel
from qt_ui.uiconstants import EVENT_ICONS from qt_ui.uiconstants import EVENT_ICONS
from qt_ui.windows.GameUpdateSignal import GameUpdateSignal from qt_ui.windows.GameUpdateSignal import GameUpdateSignal
@ -20,9 +29,6 @@ class QBaseMenu2(QDialog):
self.game_model = game_model self.game_model = game_model
self.objectName = "menuDialogue" self.objectName = "menuDialogue"
# Widgets
self.qbase_menu_tab = QBaseMenuTabs(cp, self.game_model)
try: try:
game = self.game_model.game game = self.game_model.game
self.airport = game.theater.terrain.airport_by_id(self.cp.id) self.airport = game.theater.terrain.airport_by_id(self.cp.id)
@ -39,15 +45,11 @@ class QBaseMenu2(QDialog):
self.setMinimumWidth(800) self.setMinimumWidth(800)
self.setMaximumWidth(800) self.setMaximumWidth(800)
self.setModal(True) self.setModal(True)
self.initUi()
def initUi(self):
self.setWindowTitle(self.cp.name) self.setWindowTitle(self.cp.name)
self.topLayoutWidget = QWidget()
self.topLayout = QHBoxLayout()
self.topLayoutWidget = QWidget() base_menu_header = QWidget()
self.topLayout = QHBoxLayout() top_layout = QHBoxLayout()
header = QLabel(self) header = QLabel(self)
header.setGeometry(0, 0, 655, 106) header.setGeometry(0, 0, 655, 106)
@ -57,26 +59,42 @@ class QBaseMenu2(QDialog):
title = QLabel("<b>" + self.cp.name + "</b>") title = QLabel("<b>" + self.cp.name + "</b>")
title.setAlignment(Qt.AlignLeft | Qt.AlignTop) title.setAlignment(Qt.AlignLeft | Qt.AlignTop)
title.setProperty("style", "base-title") title.setProperty("style", "base-title")
unitsPower = QLabel("{} / {} / Runway : {}".format(self.cp.base.total_aircraft, self.cp.base.total_armor, aircraft = self.cp.base.total_aircraft
"Available" if self.cp.has_runway() else "Unavailable")) armor = self.cp.base.total_armor
self.topLayout.addWidget(title) runway_status = "operational" if self.cp.has_runway() else "inoperative"
self.topLayout.addWidget(unitsPower) intel_summary = QLabel("\n".join([
self.topLayout.setAlignment(Qt.AlignTop) f"{aircraft} aircraft",
self.topLayoutWidget.setProperty("style", "baseMenuHeader") f"{armor} ground units",
self.topLayoutWidget.setLayout(self.topLayout) f"Runway {runway_status}"
]))
top_layout.addWidget(title)
top_layout.addWidget(intel_summary)
top_layout.setAlignment(Qt.AlignTop)
base_menu_header.setProperty("style", "baseMenuHeader")
base_menu_header.setLayout(top_layout)
self.mainLayout = QGridLayout() main_layout = QVBoxLayout()
self.mainLayout.addWidget(header, 0, 0) main_layout.addWidget(header)
self.mainLayout.addWidget(self.topLayoutWidget, 1, 0) main_layout.addWidget(base_menu_header)
self.mainLayout.addWidget(self.qbase_menu_tab, 2, 0) main_layout.addWidget(QBaseMenuTabs(cp, self.game_model))
totalBudget = QLabel( bottom_row = QHBoxLayout()
main_layout.addLayout(bottom_row)
if FlightType.RUNWAY_ATTACK in self.cp.mission_types(for_player=True):
runway_attack_button = QPushButton("Attack airfield")
bottom_row.addWidget(runway_attack_button)
runway_attack_button.setProperty("style", "btn-danger")
runway_attack_button.clicked.connect(self.new_package)
budget_display = QLabel(
QRecruitBehaviour.BUDGET_FORMAT.format(self.game_model.game.budget) QRecruitBehaviour.BUDGET_FORMAT.format(self.game_model.game.budget)
) )
totalBudget.setObjectName("budgetField") budget_display.setObjectName("budgetField")
totalBudget.setAlignment(Qt.AlignRight | Qt.AlignBottom) budget_display.setAlignment(Qt.AlignRight | Qt.AlignBottom)
totalBudget.setProperty("style", "budget-label") budget_display.setProperty("style", "budget-label")
self.mainLayout.addWidget(totalBudget) bottom_row.addWidget(budget_display)
self.setLayout(self.mainLayout) self.setLayout(main_layout)
def closeEvent(self, closeEvent:QCloseEvent): def closeEvent(self, closeEvent:QCloseEvent):
GameUpdateSignal.get_instance().updateGame(self.game_model.game) GameUpdateSignal.get_instance().updateGame(self.game_model.game)
@ -88,3 +106,6 @@ class QBaseMenu2(QDialog):
return "./resources/ui/lha.png" return "./resources/ui/lha.png"
else: else:
return "./resources/ui/airbase.png" return "./resources/ui/airbase.png"
def new_package(self) -> None:
Dialog.open_new_package_dialog(self.cp, parent=self.window())

View File

@ -265,6 +265,50 @@ local unitPayloads = {
[1] = 11, [1] = 11,
}, },
}, },
[7] = {
["name"] = "RUNWAY_ATTACK",
["pylons"] = {
[1] = {
["CLSID"] = "{5CE2FF2A-645A-4197-B48D-8720AC69394F}",
["num"] = 9,
},
[2] = {
["CLSID"] = "{5CE2FF2A-645A-4197-B48D-8720AC69394F}",
["num"] = 1,
},
[3] = {
["CLSID"] = "{BRU33_2X_MK-82}",
["num"] = 5,
},
[4] = {
["CLSID"] = "{40EF17B7-F508-45de-8566-6FFECC0C1AB8}",
["num"] = 6,
},
[5] = {
["CLSID"] = "{40EF17B7-F508-45de-8566-6FFECC0C1AB8}",
["num"] = 4,
},
[6] = {
["CLSID"] = "{FPU_8A_FUEL_TANK}",
["num"] = 7,
},
[7] = {
["CLSID"] = "{FPU_8A_FUEL_TANK}",
["num"] = 3,
},
[8] = {
["CLSID"] = "{BRU33_2X_MK-82}",
["num"] = 2,
},
[9] = {
["CLSID"] = "{BRU33_2X_MK-82}",
["num"] = 8,
},
},
["tasks"] = {
[1] = 34,
},
},
}, },
["tasks"] = { ["tasks"] = {
}, },

View File

@ -189,6 +189,54 @@ local unitPayloads = {
[1] = 11, [1] = 11,
}, },
}, },
[6] = {
["name"] = "RUNWAY_ATTACK",
["pylons"] = {
[1] = {
["CLSID"] = "{Eclair}",
["num"] = 10,
},
[2] = {
["CLSID"] = "{MMagicII}",
["num"] = 9,
},
[3] = {
["CLSID"] = "{MMagicII}",
["num"] = 1,
},
[4] = {
["CLSID"] = "{BLG66_BELOUGA_AC}",
["num"] = 8,
},
[5] = {
["CLSID"] = "{BLG66_BELOUGA_AC}",
["num"] = 2,
},
[6] = {
["CLSID"] = "{BLG66_BELOUGA_AC}",
["num"] = 7,
},
[7] = {
["CLSID"] = "{BLG66_BELOUGA_AC}",
["num"] = 3,
},
[8] = {
["CLSID"] = "{BLG66_BELOUGA_AC}",
["num"] = 4,
},
[9] = {
["CLSID"] = "{BLG66_BELOUGA_AC}",
["num"] = 6,
},
[10] = {
["CLSID"] = "{BLG66_BELOUGA_AC}",
["num"] = 5,
},
},
["tasks"] = {
[1] = 34,
},
},
}, },
["tasks"] = { ["tasks"] = {
}, },