mirror of
https://github.com/dcs-retribution/dcs-retribution.git
synced 2025-11-10 15:41:24 +00:00
commit
53582ba539
1
.gitignore
vendored
1
.gitignore
vendored
@ -21,4 +21,3 @@ logs/
|
||||
qt_ui/logs/liberation.log
|
||||
|
||||
*.psd
|
||||
resources/scripts/plugins/*
|
||||
|
||||
6
.gitmodules
vendored
6
.gitmodules
vendored
@ -2,3 +2,9 @@
|
||||
path = pydcs
|
||||
url = https://github.com/pydcs/dcs
|
||||
branch = master
|
||||
[submodule "plugin/veaf"]
|
||||
path = plugin/veaf
|
||||
url = https://github.com/VEAF/dcs-liberation-veaf-framework
|
||||
[submodule "resources/plugins/veaf"]
|
||||
path = resources/plugins/veaf
|
||||
url = https://github.com/VEAF/dcs-liberation-veaf-framework
|
||||
|
||||
@ -28,6 +28,7 @@ from .event.event import Event, UnitsDeliveryEvent
|
||||
from .event.frontlineattack import FrontlineAttackEvent
|
||||
from .infos.information import Information
|
||||
from .settings import Settings
|
||||
from plugin import LuaPluginManager
|
||||
from .weather import Conditions, TimeOfDay
|
||||
|
||||
COMMISION_UNIT_VARIETY = 4
|
||||
@ -223,6 +224,10 @@ class Game:
|
||||
|
||||
def on_load(self) -> None:
|
||||
ObjectiveDistanceCache.set_theater(self.theater)
|
||||
|
||||
# set the settings in all plugins
|
||||
for plugin in LuaPluginManager().getPlugins():
|
||||
plugin.setSettings(self.settings)
|
||||
|
||||
# Save game compatibility.
|
||||
|
||||
|
||||
@ -14,7 +14,7 @@ from dcs.translation import String
|
||||
from dcs.triggers import TriggerStart
|
||||
from dcs.unittype import UnitType
|
||||
|
||||
from gen import Conflict, VisualGenerator
|
||||
from gen import Conflict, VisualGenerator, FlightType
|
||||
from gen.aircraft import AIRCRAFT_DATA, AircraftConflictGenerator, FlightData
|
||||
from gen.airfields import AIRFIELD_DATA
|
||||
from gen.airsupportgen import AirSupport, AirSupportConflictGenerator
|
||||
@ -31,7 +31,7 @@ from gen.triggergen import TRIGGER_RADIUS_MEDIUM, TriggersGenerator
|
||||
from theater import ControlPoint
|
||||
from .. import db
|
||||
from ..debriefing import Debriefing
|
||||
|
||||
from plugin import LuaPluginManager
|
||||
|
||||
class Operation:
|
||||
attackers_starting_position = None # type: db.StartingPosition
|
||||
@ -74,6 +74,7 @@ class Operation:
|
||||
self.departure_cp = departure_cp
|
||||
self.to_cp = to_cp
|
||||
self.is_quick = False
|
||||
self.listOfPluginsScripts = []
|
||||
|
||||
def units_of(self, country_name: str) -> List[UnitType]:
|
||||
return []
|
||||
@ -132,6 +133,34 @@ class Operation:
|
||||
else:
|
||||
self.defenders_starting_position = None
|
||||
|
||||
def injectLuaTrigger(self, luascript, comment = "LUA script"):
|
||||
trigger = TriggerStart(comment=comment)
|
||||
trigger.add_action(DoScript(String(luascript)))
|
||||
self.current_mission.triggerrules.triggers.append(trigger)
|
||||
|
||||
def bypassPluginScript(self, pluginName, scriptFileMnemonic):
|
||||
self.listOfPluginsScripts.append(scriptFileMnemonic)
|
||||
|
||||
def injectPluginScript(self, pluginName, scriptFile, scriptFileMnemonic):
|
||||
if not scriptFileMnemonic in self.listOfPluginsScripts:
|
||||
self.listOfPluginsScripts.append(scriptFileMnemonic)
|
||||
|
||||
plugin_path = Path("./resources/plugins",pluginName)
|
||||
|
||||
if scriptFile != None:
|
||||
scriptFile_path = Path(plugin_path, scriptFile)
|
||||
if scriptFile_path.exists():
|
||||
trigger = TriggerStart(comment="Load " + scriptFileMnemonic)
|
||||
filename = scriptFile_path.resolve()
|
||||
fileref = self.current_mission.map_resource.add_resource_file(filename)
|
||||
trigger.add_action(DoScriptFile(fileref))
|
||||
self.current_mission.triggerrules.triggers.append(trigger)
|
||||
else:
|
||||
logging.error(f"Cannot find script file {scriptFile} for plugin {pluginName}")
|
||||
|
||||
else:
|
||||
logging.debug(f"Skipping script file {scriptFile} for plugin {pluginName}")
|
||||
|
||||
def generate(self):
|
||||
radio_registry = RadioRegistry()
|
||||
tacan_registry = TacanRegistry()
|
||||
@ -254,98 +283,181 @@ class Operation:
|
||||
if self.game.settings.perf_smoke_gen:
|
||||
visualgen.generate()
|
||||
|
||||
# Inject Plugins Lua Scripts
|
||||
listOfPluginsScripts = []
|
||||
plugin_file_path = Path("./resources/scripts/plugins/__plugins.lst")
|
||||
if plugin_file_path.exists():
|
||||
for line in plugin_file_path.read_text().splitlines():
|
||||
name = line.strip()
|
||||
if not name.startswith( '#' ):
|
||||
trigger = TriggerStart(comment="Load " + name)
|
||||
listOfPluginsScripts.append(name)
|
||||
fileref = self.current_mission.map_resource.add_resource_file("./resources/scripts/plugins/" + name)
|
||||
trigger.add_action(DoScriptFile(fileref))
|
||||
self.current_mission.triggerrules.triggers.append(trigger)
|
||||
else:
|
||||
logging.info(
|
||||
f"Not loading plugins, {plugin_file_path} does not exist")
|
||||
luaData = {}
|
||||
luaData["AircraftCarriers"] = {}
|
||||
luaData["Tankers"] = {}
|
||||
luaData["AWACs"] = {}
|
||||
luaData["JTACs"] = {}
|
||||
luaData["TargetPoints"] = {}
|
||||
|
||||
# Inject Mist Script if not done already in the plugins
|
||||
if not "mist.lua" in listOfPluginsScripts and not "mist_4_3_74.lua" in listOfPluginsScripts: # don't load the script twice
|
||||
trigger = TriggerStart(comment="Load Mist Lua framework")
|
||||
fileref = self.current_mission.map_resource.add_resource_file("./resources/scripts/mist_4_3_74.lua")
|
||||
trigger.add_action(DoScriptFile(fileref))
|
||||
self.current_mission.triggerrules.triggers.append(trigger)
|
||||
self.assign_channels_to_flights(airgen.flights,
|
||||
airsupportgen.air_support)
|
||||
|
||||
# Inject JSON library if not done already in the plugins
|
||||
if not "json.lua" in listOfPluginsScripts : # don't load the script twice
|
||||
trigger = TriggerStart(comment="Load JSON Lua library")
|
||||
fileref = self.current_mission.map_resource.add_resource_file("./resources/scripts/json.lua")
|
||||
trigger.add_action(DoScriptFile(fileref))
|
||||
self.current_mission.triggerrules.triggers.append(trigger)
|
||||
kneeboard_generator = KneeboardGenerator(self.current_mission)
|
||||
for dynamic_runway in groundobjectgen.runways.values():
|
||||
self.briefinggen.add_dynamic_runway(dynamic_runway)
|
||||
|
||||
for tanker in airsupportgen.air_support.tankers:
|
||||
self.briefinggen.add_tanker(tanker)
|
||||
kneeboard_generator.add_tanker(tanker)
|
||||
luaData["Tankers"][tanker.callsign] = {
|
||||
"dcsGroupName": tanker.dcsGroupName,
|
||||
"callsign": tanker.callsign,
|
||||
"variant": tanker.variant,
|
||||
"radio": tanker.freq.mhz,
|
||||
"tacan": str(tanker.tacan.number) + tanker.tacan.band.name
|
||||
}
|
||||
|
||||
if self.is_awacs_enabled:
|
||||
for awacs in airsupportgen.air_support.awacs:
|
||||
self.briefinggen.add_awacs(awacs)
|
||||
kneeboard_generator.add_awacs(awacs)
|
||||
luaData["AWACs"][awacs.callsign] = {
|
||||
"dcsGroupName": awacs.dcsGroupName,
|
||||
"callsign": awacs.callsign,
|
||||
"radio": awacs.freq.mhz
|
||||
}
|
||||
|
||||
for jtac in jtacs:
|
||||
self.briefinggen.add_jtac(jtac)
|
||||
kneeboard_generator.add_jtac(jtac)
|
||||
luaData["JTACs"][jtac.callsign] = {
|
||||
"dcsGroupName": jtac.dcsGroupName,
|
||||
"callsign": jtac.callsign,
|
||||
"zone": jtac.region,
|
||||
"dcsUnit": jtac.unit_name,
|
||||
"laserCode": jtac.code
|
||||
}
|
||||
|
||||
for flight in airgen.flights:
|
||||
self.briefinggen.add_flight(flight)
|
||||
kneeboard_generator.add_flight(flight)
|
||||
if flight.friendly and flight.flight_type in [FlightType.ANTISHIP, FlightType.DEAD, FlightType.SEAD, FlightType.STRIKE]:
|
||||
flightType = flight.flight_type.name
|
||||
flightTarget = flight.targetPoint
|
||||
if flightTarget:
|
||||
flightTargetName = None
|
||||
flightTargetType = None
|
||||
if hasattr(flightTarget, 'obj_name'):
|
||||
flightTargetName = flightTarget.obj_name
|
||||
flightTargetType = flightType + f" TGT ({flightTarget.category})"
|
||||
elif hasattr(flightTarget, 'name'):
|
||||
flightTargetName = flightTarget.name
|
||||
flightTargetType = flightType + " TGT (Airbase)"
|
||||
luaData["TargetPoints"][flightTargetName] = {
|
||||
"name": flightTargetName,
|
||||
"type": flightTargetType,
|
||||
"position": { "x": flightTarget.position.x, "y": flightTarget.position.y}
|
||||
}
|
||||
|
||||
|
||||
self.briefinggen.generate()
|
||||
kneeboard_generator.generate()
|
||||
|
||||
# Inject Ciribob's JTACAutoLase if not done already in the plugins
|
||||
if not "JTACAutoLase.lua" in listOfPluginsScripts : # don't load the script twice
|
||||
trigger = TriggerStart(comment="Load JTACAutoLase.lua script")
|
||||
fileref = self.current_mission.map_resource.add_resource_file("./resources/scripts/JTACAutoLase.lua")
|
||||
trigger.add_action(DoScriptFile(fileref))
|
||||
self.current_mission.triggerrules.triggers.append(trigger)
|
||||
|
||||
# set a LUA table with data from Liberation that we want to set
|
||||
# at the moment it contains Liberation's install path, and an overridable definition for the JTACAutoLase function
|
||||
# later, we'll add data about the units and points having been generated, in order to facilitate the configuration of the plugin lua scripts
|
||||
state_location = "[[" + os.path.abspath("state.json") + "]]"
|
||||
lua = """
|
||||
-- setting configuration table
|
||||
env.info("DCSLiberation|: setting configuration table")
|
||||
|
||||
-- all data in this table is overridable.
|
||||
dcsLiberation = {}
|
||||
|
||||
-- the base location for state.json; if non-existent, it'll be replaced with LIBERATION_EXPORT_DIR, TEMP, or DCS working directory
|
||||
dcsLiberation.installPath=""" + state_location + """
|
||||
|
||||
-- you can override dcsLiberation.JTACAutoLase to make it use your own function ; it will be called with these parameters : ({jtac.unit_name}, {jtac.code}, {smoke}, 'vehicle') for all JTACs
|
||||
if ctld then
|
||||
dcsLiberation.JTACAutoLase=ctld.JTACAutoLase
|
||||
elseif JTACAutoLase then
|
||||
dcsLiberation.JTACAutoLase=JTACAutoLase
|
||||
end
|
||||
|
||||
-- later, we'll add more data to the table
|
||||
--dcsLiberation.POIs = {}
|
||||
--dcsLiberation.BASEs = {}
|
||||
--dcsLiberation.JTACs = {}
|
||||
"""
|
||||
-- setting configuration table
|
||||
env.info("DCSLiberation|: setting configuration table")
|
||||
|
||||
-- all data in this table is overridable.
|
||||
dcsLiberation = {}
|
||||
|
||||
-- the base location for state.json; if non-existent, it'll be replaced with LIBERATION_EXPORT_DIR, TEMP, or DCS working directory
|
||||
dcsLiberation.installPath=""" + state_location + """
|
||||
|
||||
"""
|
||||
# Process the tankers
|
||||
lua += """
|
||||
|
||||
-- list the tankers generated by Liberation
|
||||
dcsLiberation.Tankers = {
|
||||
"""
|
||||
for key in luaData["Tankers"]:
|
||||
data = luaData["Tankers"][key]
|
||||
dcsGroupName= data["dcsGroupName"]
|
||||
callsign = data["callsign"]
|
||||
variant = data["variant"]
|
||||
tacan = data["tacan"]
|
||||
radio = data["radio"]
|
||||
lua += f" {{dcsGroupName='{dcsGroupName}', callsign='{callsign}', variant='{variant}', tacan='{tacan}', radio='{radio}' }}, \n"
|
||||
#lua += f" {{name='{dcsGroupName}', description='{callsign} ({variant})', information='Tacan:{tacan} Radio:{radio}' }}, \n"
|
||||
lua += "}"
|
||||
|
||||
# Process the AWACSes
|
||||
lua += """
|
||||
|
||||
-- list the AWACs generated by Liberation
|
||||
dcsLiberation.AWACs = {
|
||||
"""
|
||||
for key in luaData["AWACs"]:
|
||||
data = luaData["AWACs"][key]
|
||||
dcsGroupName= data["dcsGroupName"]
|
||||
callsign = data["callsign"]
|
||||
radio = data["radio"]
|
||||
lua += f" {{dcsGroupName='{dcsGroupName}', callsign='{callsign}', radio='{radio}' }}, \n"
|
||||
#lua += f" {{name='{dcsGroupName}', description='{callsign} (AWACS)', information='Radio:{radio}' }}, \n"
|
||||
lua += "}"
|
||||
|
||||
# Process the JTACs
|
||||
lua += """
|
||||
|
||||
-- list the JTACs generated by Liberation
|
||||
dcsLiberation.JTACs = {
|
||||
"""
|
||||
for key in luaData["JTACs"]:
|
||||
data = luaData["JTACs"][key]
|
||||
dcsGroupName= data["dcsGroupName"]
|
||||
callsign = data["callsign"]
|
||||
zone = data["zone"]
|
||||
laserCode = data["laserCode"]
|
||||
dcsUnit = data["dcsUnit"]
|
||||
lua += f" {{dcsGroupName='{dcsGroupName}', callsign='{callsign}', zone='{zone}', laserCode='{laserCode}', dcsUnit='{dcsUnit}' }}, \n"
|
||||
#lua += f" {{name='{dcsGroupName}', description='JTAC {callsign} ', information='Laser:{laserCode}', jtac={laserCode} }}, \n"
|
||||
lua += "}"
|
||||
|
||||
# Process the Target Points
|
||||
lua += """
|
||||
|
||||
-- list the target points generated by Liberation
|
||||
dcsLiberation.TargetPoints = {
|
||||
"""
|
||||
for key in luaData["TargetPoints"]:
|
||||
data = luaData["TargetPoints"][key]
|
||||
name = data["name"]
|
||||
pointType = data["type"]
|
||||
positionX = data["position"]["x"]
|
||||
positionY = data["position"]["y"]
|
||||
lua += f" {{name='{name}', pointType='{pointType}', positionX='{positionX}', positionY='{positionY}' }}, \n"
|
||||
#lua += f" {{name='{pointType} {name}', point{{x={positionX}, z={positionY} }} }}, \n"
|
||||
lua += "}"
|
||||
|
||||
lua += """
|
||||
|
||||
-- list the airbases generated by Liberation
|
||||
-- dcsLiberation.Airbases = {}
|
||||
|
||||
-- list the aircraft carriers generated by Liberation
|
||||
-- dcsLiberation.Carriers = {}
|
||||
|
||||
-- later, we'll add more data to the table
|
||||
|
||||
"""
|
||||
|
||||
|
||||
trigger = TriggerStart(comment="Set DCS Liberation data")
|
||||
trigger.add_action(DoScript(String(lua)))
|
||||
self.current_mission.triggerrules.triggers.append(trigger)
|
||||
|
||||
# Inject DCS-Liberation script if not done already in the plugins
|
||||
if not "dcs_liberation.lua" in listOfPluginsScripts : # don't load the script twice
|
||||
trigger = TriggerStart(comment="Load DCS Liberation script")
|
||||
fileref = self.current_mission.map_resource.add_resource_file("./resources/scripts/dcs_liberation.lua")
|
||||
trigger.add_action(DoScriptFile(fileref))
|
||||
self.current_mission.triggerrules.triggers.append(trigger)
|
||||
# Inject Plugins Lua Scripts and data
|
||||
self.listOfPluginsScripts = []
|
||||
|
||||
# add a configuration for JTACAutoLase and start lasing for all JTACs
|
||||
smoke = "true"
|
||||
if hasattr(self.game.settings, "jtac_smoke_on"):
|
||||
if not self.game.settings.jtac_smoke_on:
|
||||
smoke = "false"
|
||||
|
||||
lua = """
|
||||
-- setting and starting JTACs
|
||||
env.info("DCSLiberation|: setting and starting JTACs")
|
||||
"""
|
||||
|
||||
for jtac in jtacs:
|
||||
lua += f"if dcsLiberation.JTACAutoLase then dcsLiberation.JTACAutoLase('{jtac.unit_name}', {jtac.code}, {smoke}, 'vehicle') end\n"
|
||||
|
||||
trigger = TriggerStart(comment="Start JTACs")
|
||||
trigger.add_action(DoScript(String(lua)))
|
||||
self.current_mission.triggerrules.triggers.append(trigger)
|
||||
for plugin in LuaPluginManager().getPlugins():
|
||||
plugin.injectScripts(self)
|
||||
plugin.injectConfiguration(self)
|
||||
|
||||
self.assign_channels_to_flights(airgen.flights,
|
||||
airsupportgen.air_support)
|
||||
|
||||
@ -1,3 +1,4 @@
|
||||
from plugin import LuaPluginManager
|
||||
|
||||
class Settings:
|
||||
|
||||
@ -24,8 +25,6 @@ class Settings:
|
||||
self.sams = True # Legacy parameter do not use
|
||||
self.cold_start = False # Legacy parameter do not use
|
||||
self.version = None
|
||||
self.include_jtac_if_available = True
|
||||
self.jtac_smoke_on = True
|
||||
|
||||
# Performance oriented
|
||||
self.perf_red_alert_state = True
|
||||
@ -40,6 +39,12 @@ class Settings:
|
||||
self.perf_culling = False
|
||||
self.perf_culling_distance = 100
|
||||
|
||||
# LUA Plugins system
|
||||
self.plugins = {}
|
||||
for plugin in LuaPluginManager().getPlugins():
|
||||
plugin.setSettings(self)
|
||||
|
||||
|
||||
# Cheating
|
||||
self.show_red_ato = False
|
||||
|
||||
|
||||
@ -237,11 +237,14 @@ class FlightData:
|
||||
#: Map of radio frequencies to their assigned radio and channel, if any.
|
||||
frequency_to_channel_map: Dict[RadioFrequency, ChannelAssignment]
|
||||
|
||||
#: Data concerning the target of a CAS/Strike/SEAD flight, or None else
|
||||
targetPoint = None
|
||||
|
||||
def __init__(self, flight_type: FlightType, units: List[FlyingUnit],
|
||||
size: int, friendly: bool, departure_delay: int,
|
||||
departure: RunwayData, arrival: RunwayData,
|
||||
divert: Optional[RunwayData], waypoints: List[FlightWaypoint],
|
||||
intra_flight_channel: RadioFrequency) -> None:
|
||||
intra_flight_channel: RadioFrequency, targetPoint: Optional) -> None:
|
||||
self.flight_type = flight_type
|
||||
self.units = units
|
||||
self.size = size
|
||||
@ -254,6 +257,7 @@ class FlightData:
|
||||
self.intra_flight_channel = intra_flight_channel
|
||||
self.frequency_to_channel_map = {}
|
||||
self.callsign = create_group_callsign_from_unit(self.units[0])
|
||||
self.targetPoint = targetPoint
|
||||
|
||||
@property
|
||||
def client_units(self) -> List[FlyingUnit]:
|
||||
@ -645,7 +649,8 @@ class AircraftConflictGenerator:
|
||||
divert=None,
|
||||
# Waypoints are added later, after they've had their TOTs set.
|
||||
waypoints=[],
|
||||
intra_flight_channel=channel
|
||||
intra_flight_channel=channel,
|
||||
targetPoint=flight.targetPoint,
|
||||
))
|
||||
|
||||
# Special case so Su 33 carrier take off
|
||||
|
||||
@ -30,6 +30,7 @@ AWACS_ALT = 13000
|
||||
@dataclass
|
||||
class AwacsInfo:
|
||||
"""AWACS information for the kneeboard."""
|
||||
dcsGroupName: str
|
||||
callsign: str
|
||||
freq: RadioFrequency
|
||||
|
||||
@ -37,6 +38,7 @@ class AwacsInfo:
|
||||
@dataclass
|
||||
class TankerInfo:
|
||||
"""Tanker information for the kneeboard."""
|
||||
dcsGroupName: str
|
||||
callsign: str
|
||||
variant: str
|
||||
freq: RadioFrequency
|
||||
@ -116,7 +118,7 @@ class AirSupportConflictGenerator:
|
||||
tanker_group.points[0].tasks.append(SetInvisibleCommand(True))
|
||||
tanker_group.points[0].tasks.append(SetImmortalCommand(True))
|
||||
|
||||
self.air_support.tankers.append(TankerInfo(callsign, variant, freq, tacan))
|
||||
self.air_support.tankers.append(TankerInfo(str(tanker_group.name), callsign, variant, freq, tacan))
|
||||
|
||||
if is_awacs_enabled:
|
||||
try:
|
||||
@ -138,6 +140,6 @@ class AirSupportConflictGenerator:
|
||||
awacs_flight.points[0].tasks.append(SetImmortalCommand(True))
|
||||
|
||||
self.air_support.awacs.append(AwacsInfo(
|
||||
callsign_for_support_unit(awacs_flight), freq))
|
||||
str(awacs_flight.name), callsign_for_support_unit(awacs_flight), freq))
|
||||
except:
|
||||
print("No AWACS for faction")
|
||||
@ -34,6 +34,7 @@ from gen.ground_forces.ai_ground_planner import (
|
||||
from .callsigns import callsign_for_support_unit
|
||||
from .conflictgen import Conflict
|
||||
from .ground_forces.combat_stance import CombatStance
|
||||
from plugin import LuaPluginManager
|
||||
|
||||
SPREAD_DISTANCE_FACTOR = 0.1, 0.3
|
||||
SPREAD_DISTANCE_SIZE_FACTOR = 0.1
|
||||
@ -54,6 +55,7 @@ RANDOM_OFFSET_ATTACK = 250
|
||||
@dataclass(frozen=True)
|
||||
class JtacInfo:
|
||||
"""JTAC information."""
|
||||
dcsGroupName: str
|
||||
unit_name: str
|
||||
callsign: str
|
||||
region: str
|
||||
@ -138,7 +140,9 @@ class GroundConflictGenerator:
|
||||
self.plan_action_for_groups(self.enemy_stance, enemy_groups, player_groups, self.conflict.heading - 90, self.conflict.to_cp, self.conflict.from_cp)
|
||||
|
||||
# Add JTAC
|
||||
if "has_jtac" in self.game.player_faction and self.game.player_faction["has_jtac"] and self.game.settings.include_jtac_if_available:
|
||||
jtacPlugin = LuaPluginManager().getPlugin("jtacautolase")
|
||||
useJTAC = jtacPlugin and jtacPlugin.isEnabled()
|
||||
if "has_jtac" in self.game.player_faction and self.game.player_faction["has_jtac"] and useJTAC:
|
||||
n = "JTAC" + str(self.conflict.from_cp.id) + str(self.conflict.to_cp.id)
|
||||
code = 1688 - len(self.jtacs)
|
||||
|
||||
@ -158,7 +162,7 @@ class GroundConflictGenerator:
|
||||
frontline = f"Frontline {self.conflict.from_cp.name}/{self.conflict.to_cp.name}"
|
||||
# Note: Will need to change if we ever add ground based JTAC.
|
||||
callsign = callsign_for_support_unit(jtac)
|
||||
self.jtacs.append(JtacInfo(n, callsign, frontline, str(code)))
|
||||
self.jtacs.append(JtacInfo(str(jtac.name), n, callsign, frontline, str(code)))
|
||||
|
||||
def gen_infantry_group_for_group(self, group, is_player, side:Country, forward_heading):
|
||||
|
||||
|
||||
@ -209,6 +209,7 @@ class PackageBuilder:
|
||||
flight = Flight(aircraft, plan.num_aircraft, airfield, plan.task,
|
||||
self.start_type)
|
||||
self.package.add_flight(flight)
|
||||
flight.targetPoint = self.package.target
|
||||
return True
|
||||
|
||||
def build(self) -> Package:
|
||||
@ -221,7 +222,7 @@ class PackageBuilder:
|
||||
for flight in flights:
|
||||
self.global_inventory.return_from_flight(flight)
|
||||
self.package.remove_flight(flight)
|
||||
|
||||
flight.targetPoint = None
|
||||
|
||||
class ObjectiveFinder:
|
||||
"""Identifies potential objectives for the mission planner."""
|
||||
|
||||
@ -137,6 +137,7 @@ class Flight:
|
||||
use_custom_loadout = False
|
||||
preset_loadout_name = ""
|
||||
group = False # Contains DCS Mission group data after mission has been generated
|
||||
targetPoint = None # Contains either None or a Strike/SEAD target point location
|
||||
|
||||
def __init__(self, unit_type: UnitType, count: int, from_cp: ControlPoint,
|
||||
flight_type: FlightType, start_type: str) -> None:
|
||||
|
||||
2
plugin/__init__.py
Normal file
2
plugin/__init__.py
Normal file
@ -0,0 +1,2 @@
|
||||
from .luaplugin import LuaPlugin
|
||||
from .manager import LuaPluginManager
|
||||
199
plugin/luaplugin.py
Normal file
199
plugin/luaplugin.py
Normal file
@ -0,0 +1,199 @@
|
||||
from typing import List
|
||||
from pathlib import Path
|
||||
from PySide2.QtCore import QSize, Qt, QItemSelectionModel, QPoint
|
||||
from PySide2.QtWidgets import QLabel, QDialog, QGridLayout, QListView, QStackedLayout, QComboBox, QWidget, \
|
||||
QAbstractItemView, QPushButton, QGroupBox, QCheckBox, QVBoxLayout, QSpinBox
|
||||
import json
|
||||
|
||||
class LuaPluginWorkOrder():
|
||||
|
||||
def __init__(self, parent, filename:str, mnemonic:str, disable:bool):
|
||||
self.filename = filename
|
||||
self.mnemonic = mnemonic
|
||||
self.disable = disable
|
||||
self.parent = parent
|
||||
|
||||
def work(self, mnemonic:str, operation):
|
||||
if self.disable:
|
||||
operation.bypassPluginScript(self.parent.mnemonic, self.mnemonic)
|
||||
else:
|
||||
operation.injectPluginScript(self.parent.mnemonic, self.filename, self.mnemonic)
|
||||
|
||||
class LuaPluginSpecificOption():
|
||||
|
||||
def __init__(self, parent, mnemonic:str, nameInUI:str, defaultValue:bool):
|
||||
self.mnemonic = mnemonic
|
||||
self.nameInUI = nameInUI
|
||||
self.defaultValue = defaultValue
|
||||
self.parent = parent
|
||||
|
||||
class LuaPlugin():
|
||||
NAME_IN_SETTINGS_BASE:str = "plugins."
|
||||
|
||||
def __init__(self, jsonFilename:str):
|
||||
self.mnemonic:str = None
|
||||
self.skipUI:bool = False
|
||||
self.nameInUI:str = None
|
||||
self.nameInSettings:str = None
|
||||
self.defaultValue:bool = False
|
||||
self.specificOptions = []
|
||||
self.scriptsWorkOrders: List[LuaPluginWorkOrder] = None
|
||||
self.configurationWorkOrders: List[LuaPluginWorkOrder] = None
|
||||
self.initFromJson(jsonFilename)
|
||||
self.enabled = self.defaultValue
|
||||
self.settings = None
|
||||
|
||||
def initFromJson(self, jsonFilename:str):
|
||||
jsonFile:Path = Path(jsonFilename)
|
||||
if jsonFile.exists():
|
||||
jsonData = json.loads(jsonFile.read_text())
|
||||
self.mnemonic = jsonData.get("mnemonic")
|
||||
self.skipUI = jsonData.get("skipUI", False)
|
||||
self.nameInUI = jsonData.get("nameInUI")
|
||||
self.nameInSettings = LuaPlugin.NAME_IN_SETTINGS_BASE + self.mnemonic
|
||||
self.defaultValue = jsonData.get("defaultValue", False)
|
||||
self.specificOptions = []
|
||||
for jsonSpecificOption in jsonData.get("specificOptions"):
|
||||
mnemonic = jsonSpecificOption.get("mnemonic")
|
||||
nameInUI = jsonSpecificOption.get("nameInUI", mnemonic)
|
||||
defaultValue = jsonSpecificOption.get("defaultValue")
|
||||
self.specificOptions.append(LuaPluginSpecificOption(self, mnemonic, nameInUI, defaultValue))
|
||||
self.scriptsWorkOrders = []
|
||||
for jsonWorkOrder in jsonData.get("scriptsWorkOrders"):
|
||||
file = jsonWorkOrder.get("file")
|
||||
mnemonic = jsonWorkOrder.get("mnemonic")
|
||||
disable = jsonWorkOrder.get("disable", False)
|
||||
self.scriptsWorkOrders.append(LuaPluginWorkOrder(self, file, mnemonic, disable))
|
||||
self.configurationWorkOrders = []
|
||||
for jsonWorkOrder in jsonData.get("configurationWorkOrders"):
|
||||
file = jsonWorkOrder.get("file")
|
||||
mnemonic = jsonWorkOrder.get("mnemonic")
|
||||
disable = jsonWorkOrder.get("disable", False)
|
||||
self.configurationWorkOrders.append(LuaPluginWorkOrder(self, file, mnemonic, disable))
|
||||
|
||||
def setupUI(self, settingsWindow, row:int):
|
||||
# set the game settings
|
||||
self.setSettings(settingsWindow.game.settings)
|
||||
|
||||
if not self.skipUI:
|
||||
# create the plugin choice checkbox interface
|
||||
self.uiWidget: QCheckBox = QCheckBox()
|
||||
self.uiWidget.setChecked(self.isEnabled())
|
||||
self.uiWidget.toggled.connect(lambda: self.applySetting(settingsWindow))
|
||||
|
||||
settingsWindow.pluginsGroupLayout.addWidget(QLabel(self.nameInUI), row, 0)
|
||||
settingsWindow.pluginsGroupLayout.addWidget(self.uiWidget, row, 1, Qt.AlignRight)
|
||||
|
||||
# if needed, create the plugin options special page
|
||||
if settingsWindow.pluginsOptionsPageLayout and self.specificOptions != None:
|
||||
self.optionsGroup: QGroupBox = QGroupBox(self.nameInUI)
|
||||
optionsGroupLayout = QGridLayout();
|
||||
optionsGroupLayout.setAlignment(Qt.AlignTop)
|
||||
self.optionsGroup.setLayout(optionsGroupLayout)
|
||||
settingsWindow.pluginsOptionsPageLayout.addWidget(self.optionsGroup)
|
||||
|
||||
# browse each option in the specific options list
|
||||
row = 0
|
||||
for specificOption in self.specificOptions:
|
||||
nameInSettings = self.nameInSettings + "." + specificOption.mnemonic
|
||||
if not nameInSettings in self.settings.plugins:
|
||||
self.settings.plugins[nameInSettings] = specificOption.defaultValue
|
||||
|
||||
specificOption.uiWidget = QCheckBox()
|
||||
specificOption.uiWidget.setChecked(self.settings.plugins[nameInSettings])
|
||||
#specificOption.uiWidget.setEnabled(False)
|
||||
specificOption.uiWidget.toggled.connect(lambda: self.applySetting(settingsWindow))
|
||||
|
||||
optionsGroupLayout.addWidget(QLabel(specificOption.nameInUI), row, 0)
|
||||
optionsGroupLayout.addWidget(specificOption.uiWidget, row, 1, Qt.AlignRight)
|
||||
|
||||
row += 1
|
||||
|
||||
# disable or enable the UI in the plugins special page
|
||||
self.enableOptionsGroup()
|
||||
|
||||
def enableOptionsGroup(self):
|
||||
if self.optionsGroup:
|
||||
self.optionsGroup.setEnabled(self.isEnabled())
|
||||
|
||||
def setSettings(self, settings):
|
||||
self.settings = settings
|
||||
|
||||
# ensure the setting exist
|
||||
if not self.nameInSettings in self.settings.plugins:
|
||||
self.settings.plugins[self.nameInSettings] = self.defaultValue
|
||||
|
||||
# do the same for each option in the specific options list
|
||||
for specificOption in self.specificOptions:
|
||||
nameInSettings = self.nameInSettings + "." + specificOption.mnemonic
|
||||
if not nameInSettings in self.settings.plugins:
|
||||
self.settings.plugins[nameInSettings] = specificOption.defaultValue
|
||||
|
||||
def applySetting(self, settingsWindow):
|
||||
# apply the main setting
|
||||
self.settings.plugins[self.nameInSettings] = self.uiWidget.isChecked()
|
||||
self.enabled = self.settings.plugins[self.nameInSettings]
|
||||
|
||||
# do the same for each option in the specific options list
|
||||
for specificOption in self.specificOptions:
|
||||
nameInSettings = self.nameInSettings + "." + specificOption.mnemonic
|
||||
self.settings.plugins[nameInSettings] = specificOption.uiWidget.isChecked()
|
||||
|
||||
# disable or enable the UI in the plugins special page
|
||||
self.enableOptionsGroup()
|
||||
|
||||
def injectScripts(self, operation):
|
||||
# set the game settings
|
||||
self.setSettings(operation.game.settings)
|
||||
|
||||
# execute the work order
|
||||
if self.scriptsWorkOrders != None:
|
||||
for workOrder in self.scriptsWorkOrders:
|
||||
workOrder.work(self.mnemonic, operation)
|
||||
|
||||
# serves for subclasses
|
||||
return self.isEnabled()
|
||||
|
||||
def injectConfiguration(self, operation):
|
||||
# set the game settings
|
||||
self.setSettings(operation.game.settings)
|
||||
|
||||
# inject the plugin options
|
||||
if len(self.specificOptions) > 0:
|
||||
defineAllOptions = ""
|
||||
for specificOption in self.specificOptions:
|
||||
nameInSettings = self.nameInSettings + "." + specificOption.mnemonic
|
||||
value = "true" if self.settings.plugins[nameInSettings] else "false"
|
||||
defineAllOptions += f" dcsLiberation.plugins.{self.mnemonic}.{specificOption.mnemonic} = {value} \n"
|
||||
|
||||
|
||||
lua = f"-- {self.mnemonic} plugin configuration.\n"
|
||||
lua += "\n"
|
||||
lua += "if dcsLiberation then\n"
|
||||
lua += " if not dcsLiberation.plugins then \n"
|
||||
lua += " dcsLiberation.plugins = {}\n"
|
||||
lua += " end\n"
|
||||
lua += f" dcsLiberation.plugins.{self.mnemonic} = {{}}\n"
|
||||
lua += defineAllOptions
|
||||
lua += "end"
|
||||
|
||||
operation.injectLuaTrigger(lua, f"{self.mnemonic} plugin configuration")
|
||||
|
||||
# execute the work order
|
||||
if self.configurationWorkOrders != None:
|
||||
for workOrder in self.configurationWorkOrders:
|
||||
workOrder.work(self.mnemonic, operation)
|
||||
|
||||
# serves for subclasses
|
||||
return self.isEnabled()
|
||||
|
||||
def isEnabled(self) -> bool:
|
||||
if not self.settings:
|
||||
return False
|
||||
|
||||
self.setSettings(self.settings) # create the necessary settings keys if needed
|
||||
|
||||
return self.settings != None and self.settings.plugins[self.nameInSettings]
|
||||
|
||||
def hasUI(self) -> bool:
|
||||
return not self.skipUI
|
||||
43
plugin/manager.py
Normal file
43
plugin/manager.py
Normal file
@ -0,0 +1,43 @@
|
||||
from .luaplugin import LuaPlugin
|
||||
from typing import List
|
||||
import glob
|
||||
from pathlib import Path
|
||||
import json
|
||||
import logging
|
||||
|
||||
|
||||
class LuaPluginManager():
|
||||
PLUGINS_RESOURCE_PATH = Path("resources/plugins")
|
||||
PLUGINS_LIST_FILENAME = "plugins.json"
|
||||
PLUGINS_JSON_FILENAME = "plugin.json"
|
||||
|
||||
__plugins = None
|
||||
def __init__(self):
|
||||
if not LuaPluginManager.__plugins:
|
||||
LuaPluginManager.__plugins= []
|
||||
jsonFile:Path = Path(LuaPluginManager.PLUGINS_RESOURCE_PATH, LuaPluginManager.PLUGINS_LIST_FILENAME)
|
||||
if jsonFile.exists():
|
||||
logging.info(f"Reading plugins list from {jsonFile}")
|
||||
|
||||
jsonData = json.loads(jsonFile.read_text())
|
||||
for plugin in jsonData:
|
||||
jsonPluginFolder = Path(LuaPluginManager.PLUGINS_RESOURCE_PATH, plugin)
|
||||
jsonPluginFile = Path(jsonPluginFolder, LuaPluginManager.PLUGINS_JSON_FILENAME)
|
||||
if jsonPluginFile.exists():
|
||||
logging.info(f"Reading plugin {plugin} from {jsonPluginFile}")
|
||||
plugin = LuaPlugin(jsonPluginFile)
|
||||
LuaPluginManager.__plugins.append(plugin)
|
||||
else:
|
||||
logging.error(f"Missing configuration file {jsonPluginFile} for plugin {plugin}")
|
||||
else:
|
||||
logging.error(f"Missing plugins list file {jsonFile}")
|
||||
|
||||
def getPlugins(self):
|
||||
return LuaPluginManager.__plugins
|
||||
|
||||
def getPlugin(self, pluginName):
|
||||
for plugin in LuaPluginManager.__plugins:
|
||||
if plugin.mnemonic == pluginName:
|
||||
return plugin
|
||||
|
||||
return None
|
||||
@ -112,6 +112,8 @@ def load_icons():
|
||||
ICONS["Generator"] = QPixmap("./resources/ui/misc/"+get_theme_icons()+"/generator.png")
|
||||
ICONS["Missile"] = QPixmap("./resources/ui/misc/"+get_theme_icons()+"/missile.png")
|
||||
ICONS["Cheat"] = QPixmap("./resources/ui/misc/"+get_theme_icons()+"/cheat.png")
|
||||
ICONS["Plugins"] = QPixmap("./resources/ui/misc/"+get_theme_icons()+"/plugins.png")
|
||||
ICONS["PluginsOptions"] = QPixmap("./resources/ui/misc/"+get_theme_icons()+"/pluginsoptions.png")
|
||||
|
||||
ICONS["TaskCAS"] = QPixmap("./resources/ui/tasks/cas.png")
|
||||
ICONS["TaskCAP"] = QPixmap("./resources/ui/tasks/cap.png")
|
||||
|
||||
@ -26,7 +26,7 @@ from game.infos.information import Information
|
||||
from qt_ui.widgets.QLabeledWidget import QLabeledWidget
|
||||
from qt_ui.windows.GameUpdateSignal import GameUpdateSignal
|
||||
from qt_ui.windows.finances.QFinancesMenu import QHorizontalSeparationLine
|
||||
|
||||
from plugin import LuaPluginManager
|
||||
|
||||
class CheatSettingsBox(QGroupBox):
|
||||
def __init__(self, game: Game, apply_settings: Callable[[], None]) -> None:
|
||||
@ -51,6 +51,8 @@ class QSettingsWindow(QDialog):
|
||||
super(QSettingsWindow, self).__init__()
|
||||
|
||||
self.game = game
|
||||
self.pluginsPage = None
|
||||
self.pluginsOptionsPage = None
|
||||
|
||||
self.setModal(True)
|
||||
self.setWindowTitle("Settings")
|
||||
@ -69,38 +71,53 @@ class QSettingsWindow(QDialog):
|
||||
|
||||
self.categoryModel = QStandardItemModel(self.categoryList)
|
||||
|
||||
self.categoryList.setIconSize(QSize(32, 32))
|
||||
|
||||
self.initDifficultyLayout()
|
||||
difficulty = QStandardItem("Difficulty")
|
||||
difficulty.setIcon(CONST.ICONS["Missile"])
|
||||
difficulty.setEditable(False)
|
||||
difficulty.setSelectable(True)
|
||||
self.categoryModel.appendRow(difficulty)
|
||||
self.right_layout.addWidget(self.difficultyPage)
|
||||
|
||||
self.initGeneratorLayout()
|
||||
generator = QStandardItem("Mission Generator")
|
||||
generator.setIcon(CONST.ICONS["Generator"])
|
||||
generator.setEditable(False)
|
||||
generator.setSelectable(True)
|
||||
self.categoryModel.appendRow(generator)
|
||||
self.right_layout.addWidget(self.generatorPage)
|
||||
|
||||
self.initCheatLayout()
|
||||
cheat = QStandardItem("Cheat Menu")
|
||||
cheat.setIcon(CONST.ICONS["Cheat"])
|
||||
cheat.setEditable(False)
|
||||
cheat.setSelectable(True)
|
||||
|
||||
self.categoryList.setIconSize(QSize(32, 32))
|
||||
self.categoryModel.appendRow(difficulty)
|
||||
self.categoryModel.appendRow(generator)
|
||||
self.categoryModel.appendRow(cheat)
|
||||
self.right_layout.addWidget(self.cheatPage)
|
||||
|
||||
self.initPluginsLayout()
|
||||
if self.pluginsPage:
|
||||
plugins = QStandardItem("LUA Plugins")
|
||||
plugins.setIcon(CONST.ICONS["Plugins"])
|
||||
plugins.setEditable(False)
|
||||
plugins.setSelectable(True)
|
||||
self.categoryModel.appendRow(plugins)
|
||||
self.right_layout.addWidget(self.pluginsPage)
|
||||
if self.pluginsOptionsPage:
|
||||
pluginsOptions = QStandardItem("LUA Plugins Options")
|
||||
pluginsOptions.setIcon(CONST.ICONS["PluginsOptions"])
|
||||
pluginsOptions.setEditable(False)
|
||||
pluginsOptions.setSelectable(True)
|
||||
self.categoryModel.appendRow(pluginsOptions)
|
||||
self.right_layout.addWidget(self.pluginsOptionsPage)
|
||||
|
||||
self.categoryList.setSelectionBehavior(QAbstractItemView.SelectRows)
|
||||
self.categoryList.setModel(self.categoryModel)
|
||||
self.categoryList.selectionModel().setCurrentIndex(self.categoryList.indexAt(QPoint(1,1)), QItemSelectionModel.Select)
|
||||
self.categoryList.selectionModel().selectionChanged.connect(self.onSelectionChanged)
|
||||
|
||||
self.initDifficultyLayout()
|
||||
self.initGeneratorLayout()
|
||||
self.initCheatLayout()
|
||||
|
||||
self.right_layout.addWidget(self.difficultyPage)
|
||||
self.right_layout.addWidget(self.generatorPage)
|
||||
self.right_layout.addWidget(self.cheatPage)
|
||||
|
||||
self.layout.addWidget(self.categoryList, 0, 0, 1, 1)
|
||||
self.layout.addLayout(self.right_layout, 0, 1, 5, 1)
|
||||
@ -200,28 +217,10 @@ class QSettingsWindow(QDialog):
|
||||
self.generate_marks.setChecked(self.game.settings.generate_marks)
|
||||
self.generate_marks.toggled.connect(self.applySettings)
|
||||
|
||||
|
||||
if not hasattr(self.game.settings, "include_jtac_if_available"):
|
||||
self.game.settings.include_jtac_if_available = True
|
||||
if not hasattr(self.game.settings, "jtac_smoke_on"):
|
||||
self.game.settings.jtac_smoke_on= True
|
||||
|
||||
self.include_jtac_if_available = QCheckBox()
|
||||
self.include_jtac_if_available.setChecked(self.game.settings.include_jtac_if_available)
|
||||
self.include_jtac_if_available.toggled.connect(self.applySettings)
|
||||
|
||||
self.jtac_smoke_on = QCheckBox()
|
||||
self.jtac_smoke_on.setChecked(self.game.settings.jtac_smoke_on)
|
||||
self.jtac_smoke_on.toggled.connect(self.applySettings)
|
||||
|
||||
self.gameplayLayout.addWidget(QLabel("Use Supercarrier Module"), 0, 0)
|
||||
self.gameplayLayout.addWidget(self.supercarrier, 0, 1, Qt.AlignRight)
|
||||
self.gameplayLayout.addWidget(QLabel("Put Objective Markers on Map"), 1, 0)
|
||||
self.gameplayLayout.addWidget(self.generate_marks, 1, 1, Qt.AlignRight)
|
||||
self.gameplayLayout.addWidget(QLabel("Include JTAC (If available)"), 2, 0)
|
||||
self.gameplayLayout.addWidget(self.include_jtac_if_available, 2, 1, Qt.AlignRight)
|
||||
self.gameplayLayout.addWidget(QLabel("Enable JTAC smoke markers"), 3, 0)
|
||||
self.gameplayLayout.addWidget(self.jtac_smoke_on, 3, 1, Qt.AlignRight)
|
||||
|
||||
self.performance = QGroupBox("Performance")
|
||||
self.performanceLayout = QGridLayout()
|
||||
@ -318,6 +317,34 @@ class QSettingsWindow(QDialog):
|
||||
self.moneyCheatBoxLayout.addWidget(btn, i/2, i%2)
|
||||
self.cheatLayout.addWidget(self.moneyCheatBox, stretch=1)
|
||||
|
||||
def initPluginsLayout(self):
|
||||
uiPrepared = False
|
||||
row:int = 0
|
||||
for plugin in LuaPluginManager().getPlugins():
|
||||
if plugin.hasUI():
|
||||
if not uiPrepared:
|
||||
uiPrepared = True
|
||||
|
||||
self.pluginsOptionsPage = QWidget()
|
||||
self.pluginsOptionsPageLayout = QVBoxLayout()
|
||||
self.pluginsOptionsPageLayout.setAlignment(Qt.AlignTop)
|
||||
self.pluginsOptionsPage.setLayout(self.pluginsOptionsPageLayout)
|
||||
|
||||
self.pluginsPage = QWidget()
|
||||
self.pluginsPageLayout = QVBoxLayout()
|
||||
self.pluginsPageLayout.setAlignment(Qt.AlignTop)
|
||||
self.pluginsPage.setLayout(self.pluginsPageLayout)
|
||||
|
||||
self.pluginsGroup = QGroupBox("Plugins")
|
||||
self.pluginsGroupLayout = QGridLayout();
|
||||
self.pluginsGroupLayout.setAlignment(Qt.AlignTop)
|
||||
self.pluginsGroup.setLayout(self.pluginsGroupLayout)
|
||||
|
||||
self.pluginsPageLayout.addWidget(self.pluginsGroup)
|
||||
|
||||
plugin.setupUI(self, row)
|
||||
row = row + 1
|
||||
|
||||
def cheatLambda(self, amount):
|
||||
return lambda: self.cheatMoney(amount)
|
||||
|
||||
@ -339,8 +366,6 @@ class QSettingsWindow(QDialog):
|
||||
self.game.settings.map_coalition_visibility = self.mapVisibiitySelection.currentData()
|
||||
self.game.settings.external_views_allowed = self.ext_views.isChecked()
|
||||
self.game.settings.generate_marks = self.generate_marks.isChecked()
|
||||
self.game.settings.include_jtac_if_available = self.include_jtac_if_available.isChecked()
|
||||
self.game.settings.jtac_smoke_on = self.jtac_smoke_on.isChecked()
|
||||
|
||||
self.game.settings.supercarrier = self.supercarrier.isChecked()
|
||||
|
||||
|
||||
BIN
resources/plugins/_doc/0.png
Normal file
BIN
resources/plugins/_doc/0.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 25 KiB |
BIN
resources/plugins/_doc/1.png
Normal file
BIN
resources/plugins/_doc/1.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 26 KiB |
BIN
resources/plugins/_doc/2.png
Normal file
BIN
resources/plugins/_doc/2.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 19 KiB |
84
resources/plugins/_doc/plugins_readme.md
Normal file
84
resources/plugins/_doc/plugins_readme.md
Normal file
@ -0,0 +1,84 @@
|
||||
# LUA Plugin system
|
||||
|
||||
This plugin system was made for injecting LUA scripts in dcs-liberation missions.
|
||||
|
||||
The resources for the plugins are stored in the `resources/plugins` folder ; each plugin has its own folder.
|
||||
|
||||
## How does the system work ?
|
||||
|
||||
The application first reads the `resources/plugins/plugins.json` file to get a list of plugins to load, in order.
|
||||
Each entry in this list should correspond to a subfolder of the `resources/plugins` directory, where a `plugin.json` file exists.
|
||||
This file is the description of the plugin.
|
||||
|
||||
### plugin.json
|
||||
|
||||
The *base* and *jtacautolase* plugins both are included in the standard dcs-liberation distribution.
|
||||
You can check their respective `plugin.json` files to understand how they work.
|
||||
Here's a quick rundown of the file's components :
|
||||
|
||||
- `mnemonic` : the short, technical name of the plugin. It's the name of the folder, and the name of the plugin in the application's settings
|
||||
- `skipUI` : if *true*, this plugin will not appear in the plugins selection user interface. Useful to force a plugin ON or OFF (see the *base* plugin)
|
||||
- `nameInUI` : the title of the plugin as it will appear in the plugins selection user interface.
|
||||
- `defaultValue` : the selection value of the plugin, when first installed ; if true, plugin is selected.
|
||||
- `specificOptions` : a list of specific plugin options
|
||||
- `nameInUI` : the title of the option as it will appear in the plugins specific options user interface.
|
||||
- `mnemonic` : the short, technical name of the option. It's the name of the LUA variable passed to the configuration script, and the name of the option in the application's settings
|
||||
- `defaultValue` : the selection value of the option, when first installed ; if true, option is selected.
|
||||
- `scriptsWorkOrders` : a list of work orders that can be used to load or disable loading a specific LUA script
|
||||
- `file` : the name of the LUA file in the plugin folder.
|
||||
- `mnemonic` : the technical name of the LUA component. The filename may be more precise than needed (e.g. include a version number) ; this is used to load each file only once, and also to disable loading a file
|
||||
- `disable` : if true, the script will be disabled instead of loaded
|
||||
- `configurationWorkOrders` : a list of work orders that can be used to load a configuration LUA script (same description as above)
|
||||
|
||||
## Standard plugins
|
||||
|
||||
### The *base* plugin
|
||||
|
||||
The *base* plugin contains the scripts that are going to be injected in every dcs-liberation missions.
|
||||
It is mandatory.
|
||||
|
||||
### The *JTACAutolase* plugin
|
||||
|
||||
This plugin replaces the vanilla JTAC functionality in dcs-liberation.
|
||||
|
||||
### The *VEAF framework* plugin
|
||||
|
||||
When enabled, this plugin will inject and configure the VEAF Framework scripts in the mission.
|
||||
|
||||
These scripts add a lot of runtime functionalities :
|
||||
|
||||
- spawning of units and groups (and portable TACANs)
|
||||
- air-to-ground missions
|
||||
- air-to-air missions
|
||||
- transport missions
|
||||
- carrier operations (not Moose)
|
||||
- tanker move
|
||||
- weather and ATC
|
||||
- shelling a zone, lighting it up
|
||||
- managing assets (tankers, awacs, aircraft carriers) : getting info, state, respawning them if needed
|
||||
- managing named points (position, info, ATC)
|
||||
- managing a dynamic radio menu
|
||||
- managing remote calls to the mission through NIOD (RPC) and SLMOD (LUA sockets)
|
||||
- managing security (not allowing everyone to do every action)
|
||||
- define groups templates
|
||||
|
||||
You can find the *VEAF Framework* plugin [on GitHub](https://github.com/VEAF/dcs-liberation-veaf-framework/releases)
|
||||
For more information, please visit the [VEAF Framework documentation site](https://veaf.github.io/VEAF-Mission-Creation-Tools/) (work in progress)
|
||||
|
||||
## Custom plugins
|
||||
|
||||
The easiest way to create a custom plugin is to copy an existing plugin, and modify the files.
|
||||
|
||||
## New settings pages
|
||||
|
||||

|
||||
|
||||
Custom plugins can be enabled or disabled in the new *LUA Plugins* settings page.
|
||||
|
||||

|
||||
|
||||
For plugins which expose specific options (such as "use smoke" for the *JTACAutoLase* plugin), the *LUA Plugins Options* settings page lists these options.
|
||||
|
||||

|
||||
|
||||
|
||||
22
resources/plugins/base/plugin.json
Normal file
22
resources/plugins/base/plugin.json
Normal file
@ -0,0 +1,22 @@
|
||||
{
|
||||
"mnemonic": "base",
|
||||
"skipUI": true,
|
||||
"nameInUI": "",
|
||||
"defaultValue": true,
|
||||
"specificOptions": [],
|
||||
"scriptsWorkOrders": [
|
||||
{
|
||||
"file": "mist_4_3_74.lua",
|
||||
"mnemonic": "mist"
|
||||
},
|
||||
{
|
||||
"file": "json.lua",
|
||||
"mnemonic": "json"
|
||||
},
|
||||
{
|
||||
"file": "dcs_liberation.lua",
|
||||
"mnemonic": "liberation"
|
||||
}
|
||||
],
|
||||
"configurationWorkOrders": []
|
||||
}
|
||||
37
resources/plugins/jtacautolase/jtacautolase-config.lua
Normal file
37
resources/plugins/jtacautolase/jtacautolase-config.lua
Normal file
@ -0,0 +1,37 @@
|
||||
-------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||
-- configuration file for the JTAC Autolase framework
|
||||
--
|
||||
-- This configuration is tailored for a mission generated by DCS Liberation
|
||||
-- see https://github.com/Khopa/dcs_liberation
|
||||
-------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||
|
||||
-- JTACAutolase plugin - configuration
|
||||
env.info("DCSLiberation|JTACAutolase plugin - configuration")
|
||||
|
||||
if dcsLiberation then
|
||||
env.info(string.format("DCSLiberation|JTACAutolase plugin - dcsLiberation"))
|
||||
|
||||
-- specific options
|
||||
local smoke = false
|
||||
|
||||
-- retrieve specific options values
|
||||
if dcsLiberation.plugins then
|
||||
env.info(string.format("DCSLiberation|JTACAutolase plugin - dcsLiberation.plugins"))
|
||||
|
||||
if dcsLiberation.plugins.jtacautolase then
|
||||
env.info(string.format("DCSLiberation|JTACAutolase plugin - dcsLiberation.plugins.jtacautolase"))
|
||||
smoke = dcsLiberation.plugins.jtacautolase.smoke
|
||||
env.info(string.format("DCSLiberation|JTACAutolase plugin - smoke = %s",tostring(smoke)))
|
||||
end
|
||||
end
|
||||
|
||||
-- actual configuration code
|
||||
for _, jtac in pairs(dcsLiberation.JTACs) do
|
||||
env.info(string.format("DCSLiberation|JTACAutolase plugin - setting up %s",jtac.dcsUnit))
|
||||
if JTACAutoLase then
|
||||
env.info(string.format("DCSLiberation|JTACAutolase plugin - calling dcsLiberation.JTACAutoLase"))
|
||||
JTACAutoLase(jtac.dcsUnit, jtac.laserCode, smoke, 'vehicle')
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
6822
resources/plugins/jtacautolase/mist_4_3_74.lua
Normal file
6822
resources/plugins/jtacautolase/mist_4_3_74.lua
Normal file
File diff suppressed because it is too large
Load Diff
28
resources/plugins/jtacautolase/plugin.json
Normal file
28
resources/plugins/jtacautolase/plugin.json
Normal file
@ -0,0 +1,28 @@
|
||||
{
|
||||
"mnemonic": "jtacautolase",
|
||||
"nameInUI": "JTAC Autolase",
|
||||
"defaultValue": true,
|
||||
"specificOptions": [
|
||||
{
|
||||
"nameInUI": "Use smoke",
|
||||
"mnemonic": "smoke",
|
||||
"defaultValue": true
|
||||
}
|
||||
],
|
||||
"scriptsWorkOrders": [
|
||||
{
|
||||
"file": "mist_4_3_74.lua",
|
||||
"mnemonic": "mist"
|
||||
},
|
||||
{
|
||||
"file": "JTACAutoLase.lua",
|
||||
"mnemonic": "jtacautolase-script"
|
||||
}
|
||||
],
|
||||
"configurationWorkOrders": [
|
||||
{
|
||||
"file": "jtacautolase-config.lua",
|
||||
"mnemonic": "jtacautolase-config"
|
||||
}
|
||||
]
|
||||
}
|
||||
5
resources/plugins/plugins.json
Normal file
5
resources/plugins/plugins.json
Normal file
@ -0,0 +1,5 @@
|
||||
[
|
||||
"veaf",
|
||||
"jtacautolase",
|
||||
"base"
|
||||
]
|
||||
@ -1,29 +0,0 @@
|
||||
# this is a list of lua scripts that will be injected in the mission, in the same order
|
||||
mist.lua
|
||||
Moose.lua
|
||||
CTLD.lua
|
||||
NIOD.lua
|
||||
WeatherMark.lua
|
||||
veaf.lua
|
||||
dcsUnits.lua
|
||||
# JTACAutoLase is an empty file, only there to disable loading the official script (already included in CTLD)
|
||||
JTACAutoLase.lua
|
||||
veafAssets.lua
|
||||
veafCarrierOperations.lua
|
||||
veafCarrierOperations2.lua
|
||||
veafCasMission.lua
|
||||
veafCombatMission.lua
|
||||
veafCombatZone.lua
|
||||
veafGrass.lua
|
||||
veafInterpreter.lua
|
||||
veafMarkers.lua
|
||||
veafMove.lua
|
||||
veafNamedPoints.lua
|
||||
veafRadio.lua
|
||||
veafRemote.lua
|
||||
veafSecurity.lua
|
||||
veafShortcuts.lua
|
||||
veafSpawn.lua
|
||||
veafTransportMission.lua
|
||||
veafUnits.lua
|
||||
missionConfig.lua
|
||||
BIN
resources/ui/misc/light/plugins.png
Normal file
BIN
resources/ui/misc/light/plugins.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 1.2 KiB |
BIN
resources/ui/misc/light/pluginsoptions.png
Normal file
BIN
resources/ui/misc/light/pluginsoptions.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 1.3 KiB |
Loading…
x
Reference in New Issue
Block a user