Files
MOOSE/Moose Development/Moose/Python/MooseBridgeAuftragExecutionExtension.lua
T
2026-08-17 18:08:43 +02:00

1793 lines
78 KiB
Lua

-- Optional approval-gated AUFTRAG execution extension for MOOSE Bridge.
--
-- Load after MooseBridge.lua and before creating the bridge instance. This file
-- exposes narrow, explicit AUFTRAG creation commands. Python should only call
-- them after an advisory recommendation has passed all hard filters and the user
-- explicitly requested execution.
if not MOOSE_BRIDGE then error("Load MooseBridge.lua before MooseBridgeAuftragExecutionExtension.lua") end
local bridge_unpack = table.unpack or unpack
local function bridge_split_object_id(object_id)
if type(object_id) ~= "string" then return nil, nil end
local prefix, name = string.match(object_id, "^([^:]+):(.+)$")
if not prefix or not name then return nil, nil end
return string.upper(prefix), name
end
local function bridge_safe_tostring(value)
if value == nil then return "nil" end
return tostring(value)
end
local function bridge_table_keys(value)
if type(value) ~= "table" then return "<" .. type(value) .. ">" end
local keys = {}
for key, _ in pairs(value) do keys[#keys + 1] = tostring(key) end
table.sort(keys)
return table.concat(keys, ",")
end
local function bridge_param_debug(command, params)
return "command_keys=[" .. bridge_table_keys(command) .. "] " ..
"params_keys=[" .. bridge_table_keys(command and command.params) .. "] " ..
"payload_keys=[" .. bridge_table_keys(command and command.payload) .. "] " ..
"resolved_keys=[" .. bridge_table_keys(params) .. "]"
end
local function bridge_optional_string_param(value)
if type(value) ~= "string" then return nil end
local trimmed = string.match(value, "^%s*(.-)%s*$")
if trimmed == "" or string.lower(trimmed) == "null" then return nil end
return trimmed
end
local function bridge_optional_bool(value)
if value == nil then return nil end
return value and true or false
end
local function bridge_bool_param(value)
if value == nil then return nil end
if type(value) == "boolean" then return value end
local text = string.lower(tostring(value))
if text == "true" or text == "1" or text == "yes" or text == "y" then return true end
if text == "false" or text == "0" or text == "no" or text == "n" then return false end
return value and true or false
end
local function bridge_number_param(value)
if value == nil then return nil end
if type(value) == "number" then return value end
local text = bridge_optional_string_param(value)
if not text then return nil end
local number = tonumber(text)
if number == nil then error("Expected number parameter, got " .. bridge_safe_tostring(value)) end
return number
end
local function bridge_coalition_param(value)
if value == nil then return nil end
if type(value) == "number" then return value end
local text = bridge_optional_string_param(value)
if not text then return nil end
local number = tonumber(text)
if number ~= nil then return number end
local normalized = string.lower(text)
if normalized == "red" then return coalition and coalition.side and coalition.side.RED or 1 end
if normalized == "blue" then return coalition and coalition.side and coalition.side.BLUE or 2 end
if normalized == "neutral" then return coalition and coalition.side and coalition.side.NEUTRAL or 0 end
error("Unknown coalition parameter: " .. bridge_safe_tostring(value))
end
local function bridge_time_param(value)
if value == nil then return nil end
if type(value) == "number" then return value end
return bridge_optional_string_param(value)
end
local function bridge_auftrag_now()
if timer and timer.getAbsTime then return timer.getAbsTime() end
if timer and timer.getTime then return timer.getTime() end
return nil
end
local function bridge_auftrag_summary(summary)
if type(summary) ~= "table" then return nil end
return {
success=bridge_optional_bool(summary.success),
Ntargets0=summary.Ntargets0,
Ntargets=summary.Ntargets,
damage=summary.damage,
Ndestroyed=summary.Ndestroyed,
Nkills=summary.Nkills,
Nelements=summary.Nelements,
targetLife=summary.targetLife,
category=summary.category,
Ncasualties=summary.Ncasualties,
}
end
local function bridge_auftrag_ready_to_evaluate(auftrag)
local tover = auftrag and auftrag.Tover or nil
local dtevaluate = auftrag and auftrag.dTevaluate or nil
local now = bridge_auftrag_now()
if tover and dtevaluate and now then return now - tover >= dtevaluate end
return false
end
function MOOSE_BRIDGE:_CommandParams(command)
if type(command) ~= "table" then return {} end
if type(command.params) == "table" then return command.params end
if type(command.payload) == "table" then
if type(command.payload.params) == "table" then return command.payload.params end
return command.payload
end
return {}
end
function MOOSE_BRIDGE:_TrackAuftragReference(auftrag)
if type(auftrag) ~= "table" then return self end
local object_id = self:_AuftragObjectId(auftrag)
if not object_id then return self end
self.TrackedAuftraege = self.TrackedAuftraege or {}
self.TrackedAuftraege[object_id] = auftrag
return self
end
function MOOSE_BRIDGE:_FindAuftragInQueueById(queue, auftrag_id)
if type(queue) ~= "table" then return nil end
for _, auftrag in pairs(queue) do
if self:_AuftragObjectId(auftrag) == auftrag_id then return auftrag end
end
return nil
end
function MOOSE_BRIDGE:_ResolveTrackedAuftragById(auftrag_id)
local id = bridge_optional_string_param(auftrag_id)
if not id then return nil, "Missing AUFTRAG id" end
if type(self.TrackedAuftraege) == "table" and type(self.TrackedAuftraege[id]) == "table" then
return self.TrackedAuftraege[id], nil
end
if _DATABASE and type(_DATABASE.LEGIONS) == "table" then
for _, legion in pairs(_DATABASE.LEGIONS) do
local queues = {legion.missionqueue, legion.missions, legion.auftraege, legion.missionQueue}
for _, queue in ipairs(queues) do
local found = self:_FindAuftragInQueueById(queue, id)
if found then self:_TrackAuftragReference(found); return found, nil end
end
end
end
for _, commander in pairs(self.RegisteredCommanders or {}) do
local found = self:_FindAuftragInQueueById(commander.missionqueue, id)
if found then self:_TrackAuftragReference(found); return found, nil end
end
if _DATABASE and type(_DATABASE.FLIGHTGROUPS) == "table" then
for _, opsgroup in pairs(_DATABASE.FLIGHTGROUPS) do
local found = self:_FindAuftragInQueueById(opsgroup.missionqueue, id)
if found then self:_TrackAuftragReference(found); return found, nil end
end
end
for _, opsgroup in pairs(self.RegisteredOpsGroups or {}) do
local found = self:_FindAuftragInQueueById(opsgroup.missionqueue, id)
if found then self:_TrackAuftragReference(found); return found, nil end
end
return nil, "AUFTRAG not found: " .. id
end
function MOOSE_BRIDGE:_CommandAuftragId(cmd)
local p = self:_CommandParams(cmd)
return bridge_optional_string_param(p.object_id) or bridge_optional_string_param(p.auftrag_id)
end
function MOOSE_BRIDGE:_ResolveLegionById(legion_id)
local prefix, name = bridge_split_object_id(legion_id)
if prefix ~= "LEGION" or not name then return nil, "Invalid LEGION id " .. bridge_safe_tostring(legion_id) end
if _DATABASE then
local found = self:_SafeCallArg(_DATABASE, "FindLegion", name)
if found then return found, nil end
if type(_DATABASE.LEGIONS) == "table" then
for key, legion in pairs(_DATABASE.LEGIONS) do
local legion_name = self:_ObjectName(legion) or (type(key) == "string" and key or nil)
if legion_name == name then return legion, nil end
end
end
end
return nil, "LEGION not found: " .. name
end
function MOOSE_BRIDGE:_ResolveCommanderById(commander_id)
local prefix, name = bridge_split_object_id(commander_id)
if prefix ~= "COMMANDER" or not name then return nil, "Invalid COMMANDER id " .. bridge_safe_tostring(commander_id) end
for key, commander in pairs(self.RegisteredCommanders or {}) do
local commander_name = self:_CommanderName(commander, type(key) == "string" and key or nil)
if commander_name == name then return commander, nil end
end
if _DATABASE and type(_DATABASE.COMMANDERS) == "table" then
for key, commander in pairs(_DATABASE.COMMANDERS) do
local commander_name = self:_CommanderName(commander, type(key) == "string" and key or nil)
if commander_name == name then return commander, nil end
end
end
return nil, "COMMANDER not found: " .. name
end
function MOOSE_BRIDGE:_ResolveCohortById(cohort_id)
local prefix, name = bridge_split_object_id(cohort_id)
if prefix ~= "COHORT" or not name then return nil, "Invalid COHORT id " .. bridge_safe_tostring(cohort_id) end
if _DATABASE and type(_DATABASE.COHORTS) == "table" then
for key, cohort in pairs(_DATABASE.COHORTS) do
local cohort_name = self:_CohortName(cohort, type(key) == "string" and key or nil)
if cohort_name == name then return cohort, nil end
end
end
return nil, "COHORT not found: " .. name
end
function MOOSE_BRIDGE:_ResolveOpsGroupById(opsgroup_id)
local prefix, name = bridge_split_object_id(opsgroup_id)
if prefix ~= "OPSGROUP" or not name then return nil, "Invalid OPSGROUP id " .. bridge_safe_tostring(opsgroup_id) end
if type(self.RegisteredOpsGroups) == "table" then
for key, opsgroup in pairs(self.RegisteredOpsGroups) do
local opsgroup_name = self:_OpsName(opsgroup, type(key) == "string" and key or nil)
if opsgroup_name == name then return opsgroup, nil end
end
end
if _DATABASE and type(_DATABASE.FLIGHTGROUPS) == "table" then
for key, opsgroup in pairs(_DATABASE.FLIGHTGROUPS) do
local opsgroup_name = self:_OpsName(opsgroup, type(key) == "string" and key or nil)
if opsgroup_name == name then return opsgroup, nil end
end
end
return nil, "OPSGROUP not found: " .. name
end
function MOOSE_BRIDGE:_ResolveAuftragTargetById(target_id)
local prefix, name = bridge_split_object_id(target_id)
if not prefix or not name then return nil, "Invalid target id " .. bridge_safe_tostring(target_id) end
if prefix == "GROUP" then
if GROUP and GROUP.FindByName then
local target = GROUP:FindByName(name)
if target then return target, nil end
end
if Group and Group.getByName then
local dcs_group = Group.getByName(name)
if dcs_group then return dcs_group, nil end
end
return nil, "GROUP target not found: " .. name
end
if prefix == "UNIT" then
if UNIT and UNIT.FindByName then
local target = UNIT:FindByName(name)
if target then return target, nil end
end
if Unit and Unit.getByName then
local dcs_unit = Unit.getByName(name)
if dcs_unit then return dcs_unit, nil end
end
return nil, "UNIT target not found: " .. name
end
if prefix == "STATIC" then
if STATIC and STATIC.FindByName then
local target = STATIC:FindByName(name)
if target then return target, nil end
end
if StaticObject and StaticObject.getByName then
local dcs_static = StaticObject.getByName(name)
if dcs_static then return dcs_static, nil end
end
return nil, "STATIC target not found: " .. name
end
return nil, "Unsupported AUFTRAG target type: " .. prefix
end
function MOOSE_BRIDGE:_ResolveCoordinateFromInputs(inputs)
local x = inputs.x ~= nil and tonumber(inputs.x) or nil
local z = inputs.z ~= nil and tonumber(inputs.z) or nil
local y = inputs.y ~= nil and tonumber(inputs.y) or 0
if not x or not z then
local latitude = inputs.latitude ~= nil and tonumber(inputs.latitude) or nil
local longitude = inputs.longitude ~= nil and tonumber(inputs.longitude) or nil
if latitude and longitude then
if not coord or not coord.LLtoLO then return nil, "coord.LLtoLO is not available" end
local point = coord.LLtoLO({lat=latitude, lon=longitude, alt=0})
if point then
x = tonumber(point.x)
z = tonumber(point.z)
y = tonumber(point.y) or y
end
end
end
if not x or not z then return nil, "Coordinate target requires numeric x/z or latitude/longitude" end
if not COORDINATE or not COORDINATE.New then return nil, "COORDINATE:New is not available" end
return COORDINATE:New(x, y, z), nil
end
function MOOSE_BRIDGE:_BuildOpsZoneSnapshotItem(zone_name, opszone, source)
local name = self:_OpsName(opszone, zone_name)
if not name then return nil end
local point = self:_PointFromMooseObject(opszone)
local state = self:_OpsState(opszone)
local n_red = opszone and tonumber(opszone.Nred) or 0
local n_blue = opszone and tonumber(opszone.Nblu) or 0
local item = {
object_id="OPSZONE:"..bridge_safe_tostring(name),
dcs_name=bridge_safe_tostring(name),
object_type="OPSZONE",
category=self:_OpsClassName(opszone, "OPSZONE"),
source=source,
name=bridge_safe_tostring(name),
zone_name=opszone and opszone.zoneName and tostring(opszone.zoneName) or nil,
zone_type=opszone and opszone.zoneType and tostring(opszone.zoneType) or nil,
zone_radius=opszone and opszone.zoneRadius or nil,
state=state and tostring(state) or nil,
owner_current_name=self:_CoalitionToName(opszone and opszone.ownerCurrent),
owner_previous_name=self:_CoalitionToName(opszone and opszone.ownerPrevious),
is_contested=n_red > 0 and n_blue > 0,
n_red=n_red,
n_blue=n_blue,
n_neutral=opszone and opszone.Nnut or 0,
threat_red=opszone and opszone.Tred or 0,
threat_blue=opszone and opszone.Tblu or 0,
threat_neutral=opszone and opszone.Tnut or 0,
airbase_name=opszone and opszone.airbaseName and tostring(opszone.airbaseName) or nil,
capture_event_callback_type=opszone and type(opszone.OnAfterCaptured) or "nil",
capture_event_forwarder_attached=opszone and
opszone.MooseBridgeCapturedEventForwarder ~= nil and
opszone.OnAfterCaptured == opszone.MooseBridgeCapturedEventForwarder or false,
}
if point then self:_AddPointFields(item, point) end
return item
end
--- Forward the MOOSE OPSZONE Captured event through its public callback.
-- @param Ops.OpsZone#OPSZONE opszone OPSZONE instance.
-- @param #string zone_name Registered zone name.
-- @return #MOOSE_BRIDGE self
function MOOSE_BRIDGE:_AttachOpsZoneEventForwarder(opszone, zone_name)
if type(opszone) ~= "table" then return self end
if opszone.MooseBridgeCapturedEventForwarder and
opszone.OnAfterCaptured == opszone.MooseBridgeCapturedEventForwarder then
return self
end
local bridge = self
local user_callback = opszone.OnAfterCaptured
local forwarder = function(opszone_self, From, Event, To, Coalition)
if type(user_callback) == "function" then
user_callback(opszone_self, From, Event, To, Coalition)
end
local item = bridge:_BuildOpsZoneSnapshotItem(zone_name, opszone_self, "event")
if item and item.object_id then
bridge:SendEvent("opszone.owner_changed", {
opszone_id=item.object_id,
previous_coalition=item.owner_previous_name,
coalition=item.owner_current_name,
capturing_coalition=bridge:_CoalitionToName(Coalition),
fsm_event=Event,
from_state=From,
to_state=To,
opszone=item,
})
end
end
opszone.OnAfterCaptured = forwarder
opszone.MooseBridgeCapturedEventForwarder = forwarder
return self
end
function MOOSE_BRIDGE:BuildOpsZoneSnapshot()
local result = {}; local seen = {}
for name, opszone in pairs(self.RegisteredOpsZones or {}) do
self:_AttachOpsZoneEventForwarder(opszone, name)
local ok, item = pcall(function() return self:_BuildOpsZoneSnapshotItem(name, opszone, "registered") end)
if ok and item and item.object_id then result[#result + 1] = item; seen[item.object_id] = true end
end
if _DATABASE and type(_DATABASE.OPSZONES) == "table" then
for name, opszone in pairs(_DATABASE.OPSZONES) do
self:_AttachOpsZoneEventForwarder(opszone, name)
local ok, item = pcall(function() return self:_BuildOpsZoneSnapshotItem(name, opszone, "database.OPSZONES") end)
if ok and item and item.object_id and not seen[item.object_id] then result[#result + 1] = item; seen[item.object_id] = true end
end
end
return result
end
function MOOSE_BRIDGE:_CoordinateTargetFromObjectId(target_id)
local prefix, name = bridge_split_object_id(target_id)
if not prefix or not name then return nil, "Invalid coordinate target id " .. bridge_safe_tostring(target_id) end
local point = nil
if prefix == "OPSZONE" then
point = self:_PointForOpsZoneName(name)
elseif prefix == "SCENERY" then
return nil, "SCENERY coordinate target resolution is not available yet"
else
local ok, value = pcall(function() return self:_PointForObjectId(target_id) end)
if ok then point = value else return nil, bridge_safe_tostring(value) end
end
if not point then return nil, "Coordinate target point not found for " .. bridge_safe_tostring(target_id) end
local ok_coordinate, coordinate = pcall(function() return self:_CoordinateFromPoint(point) end)
if ok_coordinate then return coordinate, nil end
return nil, bridge_safe_tostring(coordinate)
end
function MOOSE_BRIDGE:_ResolveCoordinateAuftragTarget(inputs)
if inputs.target_id then
return self:_CoordinateTargetFromObjectId(inputs.target_id)
end
return self:_ResolveCoordinateFromInputs(inputs)
end
function MOOSE_BRIDGE:_CommonAuftragCommandInputs(cmd)
local p = self:_CommandParams(cmd)
local legacy_params = type(p.params) == "table" and p.params or {}
local inputs = {
params=p,
commander_id=bridge_optional_string_param(p.commander_id) or bridge_optional_string_param(legacy_params.commander_id),
legion_id=bridge_optional_string_param(p.legion_id) or bridge_optional_string_param(legacy_params.legion_id),
opsgroup_id=bridge_optional_string_param(p.opsgroup_id) or bridge_optional_string_param(legacy_params.opsgroup_id),
cohort_id=bridge_optional_string_param(p.cohort_id) or bridge_optional_string_param(legacy_params.cohort_id),
allowed_legion_ids=p.allowed_legion_ids or legacy_params.allowed_legion_ids,
allowed_cohort_ids=p.allowed_cohort_ids or legacy_params.allowed_cohort_ids,
clock_start=bridge_time_param(p.clock_start) or bridge_time_param(p.ClockStart) or bridge_time_param(legacy_params.clock_start) or bridge_time_param(legacy_params.ClockStart),
clock_stop=bridge_time_param(p.clock_stop) or bridge_time_param(p.ClockStop) or bridge_time_param(legacy_params.clock_stop) or bridge_time_param(legacy_params.ClockStop),
duration=bridge_number_param(p.duration) or bridge_number_param(p.Duration) or bridge_number_param(legacy_params.duration) or bridge_number_param(legacy_params.Duration),
required_assets_min=bridge_number_param(p.required_assets_min) or bridge_number_param(p.nassets_min) or bridge_number_param(p.NassetsMin) or bridge_number_param(legacy_params.required_assets_min) or bridge_number_param(legacy_params.nassets_min) or bridge_number_param(legacy_params.NassetsMin),
required_assets_max=bridge_number_param(p.required_assets_max) or bridge_number_param(p.nassets_max) or bridge_number_param(p.NassetsMax) or bridge_number_param(legacy_params.required_assets_max) or bridge_number_param(legacy_params.nassets_max) or bridge_number_param(legacy_params.NassetsMax),
weapon_type=bridge_number_param(p.weapon_type) or bridge_number_param(p.WeaponType) or bridge_number_param(legacy_params.weapon_type) or bridge_number_param(legacy_params.WeaponType),
target_id=bridge_optional_string_param(p.target) or bridge_optional_string_param(legacy_params.target),
opszone_id=bridge_optional_string_param(p.opszone) or bridge_optional_string_param(p.opszone_id) or bridge_optional_string_param(legacy_params.opszone) or bridge_optional_string_param(legacy_params.opszone_id),
capture_coalition=bridge_coalition_param(p.capture_coalition) or bridge_coalition_param(p.capture_coalition_id) or bridge_coalition_param(p.CaptureCoalition) or bridge_coalition_param(legacy_params.capture_coalition) or bridge_coalition_param(legacy_params.capture_coalition_id) or bridge_coalition_param(legacy_params.CaptureCoalition),
stay_in_zone_time_s=bridge_number_param(p.stay_in_zone_time_s) or bridge_number_param(p.stay_in_zone_time) or bridge_number_param(p.StayInZoneTime) or bridge_number_param(legacy_params.stay_in_zone_time_s) or bridge_number_param(legacy_params.stay_in_zone_time) or bridge_number_param(legacy_params.StayInZoneTime),
zone_id=bridge_optional_string_param(p.zone) or bridge_optional_string_param(p.zone_id) or bridge_optional_string_param(legacy_params.zone) or bridge_optional_string_param(legacy_params.zone_id),
zones=p.zones or p.zone_ids or legacy_params.zones or legacy_params.zone_ids,
coordinate_id=bridge_optional_string_param(p.coordinate) or bridge_optional_string_param(p.coordinate_id) or bridge_optional_string_param(legacy_params.coordinate) or bridge_optional_string_param(legacy_params.coordinate_id),
dropoff_id=bridge_optional_string_param(p.dropoff) or bridge_optional_string_param(p.dropoff_id) or bridge_optional_string_param(legacy_params.dropoff) or bridge_optional_string_param(legacy_params.dropoff_id),
pickup_id=bridge_optional_string_param(p.pickup) or bridge_optional_string_param(p.pickup_id) or bridge_optional_string_param(legacy_params.pickup) or bridge_optional_string_param(legacy_params.pickup_id),
x=p.x or legacy_params.x,
y=p.y or legacy_params.y,
z=p.z or legacy_params.z,
latitude=p.latitude or legacy_params.latitude,
longitude=p.longitude or legacy_params.longitude,
dropoff_x=p.dropoff_x or legacy_params.dropoff_x,
dropoff_y=p.dropoff_y or legacy_params.dropoff_y,
dropoff_z=p.dropoff_z or legacy_params.dropoff_z,
pickup_x=p.pickup_x or legacy_params.pickup_x,
pickup_y=p.pickup_y or legacy_params.pickup_y,
pickup_z=p.pickup_z or legacy_params.pickup_z,
altitude_ft=p.altitude_ft or legacy_params.altitude_ft,
selected_payload_uid=p.selected_payload_uid or legacy_params.selected_payload_uid,
engage_weapon_type=p.engage_weapon_type or p.EngageWeaponType or legacy_params.engage_weapon_type or legacy_params.EngageWeaponType,
divebomb=p.divebomb,
nshots=p.nshots or p.Nshots or legacy_params.nshots or legacy_params.Nshots,
radius_m=p.radius_m or p.radius or p.Radius or legacy_params.radius_m or legacy_params.radius or legacy_params.Radius,
carpet_length_m=p.carpet_length_m or p.carpet_length or p.CarpetLength or legacy_params.carpet_length_m or legacy_params.carpet_length or legacy_params.CarpetLength,
length_m=p.length_m or p.length or p.Length or legacy_params.length_m or legacy_params.length or legacy_params.Length,
speed_kts=p.speed_kts or p.speed or p.Speed or legacy_params.speed_kts or legacy_params.speed or legacy_params.Speed,
formation=bridge_optional_string_param(p.formation) or bridge_optional_string_param(p.Formation) or bridge_optional_string_param(legacy_params.formation) or bridge_optional_string_param(legacy_params.Formation),
depth_m=p.depth_m or p.depth or p.Depth or legacy_params.depth_m or legacy_params.depth or legacy_params.Depth,
heading_deg=p.heading_deg or p.heading or p.Heading or legacy_params.heading_deg or legacy_params.heading or legacy_params.Heading,
leg_nm=p.leg_nm or p.leg or p.Leg or legacy_params.leg_nm or legacy_params.leg or legacy_params.Leg,
refuel_system=p.refuel_system or p.RefuelSystem or legacy_params.refuel_system or legacy_params.RefuelSystem,
target_types=p.target_types or p.TargetTypes or legacy_params.target_types or legacy_params.TargetTypes,
range_max_nm=p.range_max_nm or p.range_max or p.RangeMax or legacy_params.range_max_nm or legacy_params.range_max or legacy_params.RangeMax,
orbit_distance_nm=p.orbit_distance_nm or p.orbit_distance or p.OrbitDistance or legacy_params.orbit_distance_nm or legacy_params.orbit_distance or legacy_params.OrbitDistance,
engage_max_distance_nm=p.engage_max_distance_nm or p.engage_max_distance or p.EngageMaxDistance or legacy_params.engage_max_distance_nm or legacy_params.engage_max_distance or legacy_params.EngageMaxDistance,
offset_x=p.offset_x or legacy_params.offset_x,
offset_y=p.offset_y or legacy_params.offset_y,
offset_z=p.offset_z or legacy_params.offset_z,
transport_groups=p.transport_groups or p.transport_group or p.TransportGroups or legacy_params.transport_groups or legacy_params.transport_group or legacy_params.TransportGroups,
pickup_radius_m=p.pickup_radius_m or p.pickup_radius or p.PickupRadius or legacy_params.pickup_radius_m or legacy_params.pickup_radius or legacy_params.PickupRadius,
no_engage_zones=p.no_engage_zones or p.no_engage_zone or p.NoEngageZones or legacy_params.no_engage_zones or legacy_params.no_engage_zone or legacy_params.NoEngageZones,
frequency_mhz=p.frequency_mhz or p.frequency or p.Frequency or legacy_params.frequency_mhz or legacy_params.frequency or legacy_params.Frequency,
modulation=p.modulation or p.Modulation or legacy_params.modulation or legacy_params.Modulation,
designation=bridge_optional_string_param(p.designation) or bridge_optional_string_param(p.Designation) or bridge_optional_string_param(legacy_params.designation) or bridge_optional_string_param(legacy_params.Designation),
data_link=p.data_link,
ad_infinitum=p.ad_infinitum,
randomly=p.randomly,
}
if inputs.divebomb == nil then inputs.divebomb = legacy_params.divebomb end
if inputs.data_link == nil then inputs.data_link = p.datalink end
if inputs.data_link == nil then inputs.data_link = p.DataLink end
if inputs.data_link == nil then inputs.data_link = legacy_params.data_link end
if inputs.data_link == nil then inputs.data_link = legacy_params.datalink end
if inputs.data_link == nil then inputs.data_link = legacy_params.DataLink end
if inputs.ad_infinitum == nil then inputs.ad_infinitum = p.Adinfinitum end
if inputs.ad_infinitum == nil then inputs.ad_infinitum = legacy_params.ad_infinitum end
if inputs.ad_infinitum == nil then inputs.ad_infinitum = legacy_params.Adinfinitum end
if inputs.randomly == nil then inputs.randomly = p.Randomly end
if inputs.randomly == nil then inputs.randomly = legacy_params.randomly end
if inputs.randomly == nil then inputs.randomly = legacy_params.Randomly end
local target_count = (inputs.commander_id and 1 or 0) + (inputs.legion_id and 1 or 0) + (inputs.opsgroup_id and 1 or 0)
if target_count ~= 1 then error("Specify exactly one of commander_id, legion_id or opsgroup_id; " .. bridge_param_debug(cmd, p)) end
inputs.allowed_legion_ids = self:_NormalizeStringList(inputs.allowed_legion_ids) or {}
inputs.allowed_cohort_ids = self:_NormalizeStringList(inputs.allowed_cohort_ids) or {}
if inputs.cohort_id then inputs.allowed_cohort_ids[#inputs.allowed_cohort_ids + 1] = inputs.cohort_id end
if inputs.opsgroup_id and (#inputs.allowed_legion_ids > 0 or #inputs.allowed_cohort_ids > 0) then
error("LEGION or COHORT constraints cannot be used with opsgroup_id")
end
if inputs.legion_id and #inputs.allowed_legion_ids > 0 then
error("allowed_legion_ids requires commander_id")
end
if inputs.commander_id then
local commander, commander_err = self:_ResolveCommanderById(inputs.commander_id)
if not commander then error(commander_err) end
inputs.commander = commander
end
if inputs.legion_id then
local legion, legion_err = self:_ResolveLegionById(inputs.legion_id)
if not legion then error(legion_err) end
inputs.legion = legion
end
if inputs.opsgroup_id then
local opsgroup, opsgroup_err = self:_ResolveOpsGroupById(inputs.opsgroup_id)
if not opsgroup then error(opsgroup_err) end
inputs.opsgroup = opsgroup
end
inputs.allowed_legions = {}
for _, legion_id in ipairs(inputs.allowed_legion_ids) do
local legion, legion_err = self:_ResolveLegionById(legion_id)
if not legion then error(legion_err) end
inputs.allowed_legions[#inputs.allowed_legions + 1] = legion
end
inputs.allowed_cohorts = {}
for _, cohort_id in ipairs(inputs.allowed_cohort_ids) do
local cohort, cohort_err = self:_ResolveCohortById(cohort_id)
if not cohort then error(cohort_err) end
inputs.allowed_cohorts[#inputs.allowed_cohorts + 1] = cohort
end
return inputs
end
function MOOSE_BRIDGE:_ResolveObjectAuftragTarget(inputs)
if not inputs.target_id then return nil, "Missing target" end
return self:_ResolveAuftragTargetById(inputs.target_id)
end
function MOOSE_BRIDGE:_ResolveGroupAuftragTarget(inputs)
if not inputs.target_id then return nil, "Missing target" end
local prefix = bridge_split_object_id(inputs.target_id)
if prefix ~= "GROUP" then return nil, "Target must be GROUP:<name>" end
return self:_ResolveAuftragTargetById(inputs.target_id)
end
function MOOSE_BRIDGE:_ResolveGroupOrUnitAuftragTarget(inputs)
if not inputs.target_id then return nil, "Missing target" end
local prefix = bridge_split_object_id(inputs.target_id)
if prefix ~= "GROUP" and prefix ~= "UNIT" then return nil, "Target must be GROUP:<name> or UNIT:<name>" end
return self:_ResolveAuftragTargetById(inputs.target_id)
end
function MOOSE_BRIDGE:_ResolvePositionableAttackAuftragTarget(inputs)
if not inputs.target_id then return nil, "Missing target" end
local prefix = bridge_split_object_id(inputs.target_id)
if prefix ~= "GROUP" and prefix ~= "UNIT" and prefix ~= "STATIC" then return nil, "Target must be GROUP:<name>, UNIT:<name> or STATIC:<name>" end
return self:_ResolveAuftragTargetById(inputs.target_id)
end
function MOOSE_BRIDGE:_ResolveUnitAuftragTarget(inputs)
if not inputs.target_id then return nil, "Missing target" end
local prefix = bridge_split_object_id(inputs.target_id)
if prefix ~= "UNIT" then return nil, "Target must be UNIT:<name>" end
return self:_ResolveAuftragTargetById(inputs.target_id)
end
function MOOSE_BRIDGE:_ResolveAirbaseAuftragTarget(inputs)
if not inputs.target_id then return nil, "Missing target" end
local prefix, name = bridge_split_object_id(inputs.target_id)
if prefix ~= "AIRBASE" then return nil, "BOMBRUNWAY target must be AIRBASE:<name>" end
if AIRBASE and AIRBASE.FindByName then
local ok, airbase = pcall(function() return AIRBASE:FindByName(name) end)
if ok and airbase then return airbase, nil end
end
return nil, "AIRBASE target not found: " .. bridge_safe_tostring(name)
end
function MOOSE_BRIDGE:_ResolveBombingTarget(inputs)
return self:_ResolveCoordinateAuftragTarget(inputs)
end
function MOOSE_BRIDGE:_ResolveBombCarpetTarget(inputs)
if inputs.target_id then
local prefix = bridge_split_object_id(inputs.target_id)
if prefix ~= "GROUP" and prefix ~= "UNIT" and prefix ~= "STATIC" then
return nil, "BOMBCARPET target must be GROUP:<name>, UNIT:<name>, STATIC:<name> or direct x/z coordinates"
end
end
return self:_ResolveCoordinateAuftragTarget(inputs)
end
function MOOSE_BRIDGE:_ResolveStrafingTarget(inputs)
if inputs.target_id then
local prefix = bridge_split_object_id(inputs.target_id)
if prefix ~= "GROUP" and prefix ~= "UNIT" and prefix ~= "STATIC" then
return nil, "STRAFING target must be GROUP:<name>, UNIT:<name>, STATIC:<name> or direct x/z coordinates"
end
end
return self:_ResolveCoordinateAuftragTarget(inputs)
end
function MOOSE_BRIDGE:_ResolveArtyTarget(inputs)
return self:_ResolveCoordinateAuftragTarget(inputs)
end
function MOOSE_BRIDGE:_ResolveStrikeTarget(inputs)
return self:_ResolveCoordinateAuftragTarget(inputs)
end
function MOOSE_BRIDGE:_ResolveOrbitTarget(inputs)
return self:_ResolveCoordinateAuftragTarget(inputs)
end
function MOOSE_BRIDGE:_ResolveOnGuardTarget(inputs)
return self:_ResolveCoordinateAuftragTarget(inputs)
end
function MOOSE_BRIDGE:_ResolveZonePatrolZone(inputs)
if not inputs.zone_id then return nil, "Missing zone" end
local ok, zone = pcall(function() return self:_ZoneForDrawObjectId(inputs.zone_id) end)
if ok and zone then return zone, nil end
return nil, bridge_safe_tostring(zone)
end
function MOOSE_BRIDGE:_ResolveCaptureOpsZone(inputs)
local opszone_id = inputs.opszone_id or inputs.zone_id or inputs.target_id
if not opszone_id then return nil, "Missing opszone" end
local prefix, name = bridge_split_object_id(opszone_id)
if prefix ~= "OPSZONE" then return nil, "CAPTUREZONE requires OPSZONE:<name>, got " .. bridge_safe_tostring(opszone_id) end
local opszone = self.RegisteredOpsZones and self.RegisteredOpsZones[name]
if not opszone and _DATABASE and type(_DATABASE.OPSZONES) == "table" then opszone = _DATABASE.OPSZONES[name] end
if not opszone then return nil, "OPSZONE not found: " .. bridge_safe_tostring(opszone_id) end
inputs.opszone_id = opszone_id
return opszone, nil
end
function MOOSE_BRIDGE:_ResolveZonePatrolCoordinate(inputs)
if inputs.coordinate_id then
return self:_CoordinateTargetFromObjectId(inputs.coordinate_id)
end
if inputs.x ~= nil or inputs.z ~= nil then
return self:_ResolveCoordinateFromInputs(inputs)
end
return nil, nil
end
function MOOSE_BRIDGE:_NormalizeTargetTypes(value)
if type(value) == "table" then return value end
if type(value) ~= "string" or value == "" then return nil end
local result = {}
for item in string.gmatch(value, "([^,]+)") do
item = item:gsub("^%s+", ""):gsub("%s+$", "")
if item ~= "" then result[#result + 1] = item end
end
if #result > 0 then return result end
return nil
end
function MOOSE_BRIDGE:_BuildOffsetVector(inputs)
if inputs.offset_x == nil and inputs.offset_y == nil and inputs.offset_z == nil then return nil end
return {
x=inputs.offset_x ~= nil and tonumber(inputs.offset_x) or 0,
y=inputs.offset_y ~= nil and tonumber(inputs.offset_y) or 0,
z=inputs.offset_z ~= nil and tonumber(inputs.offset_z) or 0,
}
end
function MOOSE_BRIDGE:_ResolveCoordinateFromNamedFields(object_id, x_value, y_value, z_value, label, required)
if object_id then
return self:_CoordinateTargetFromObjectId(object_id)
end
if x_value ~= nil or z_value ~= nil then
return self:_ResolveCoordinateFromInputs({x=x_value, y=y_value, z=z_value})
end
if required then return nil, label .. " coordinate requires object id or numeric x and z" end
return nil, nil
end
function MOOSE_BRIDGE:_NormalizeStringList(value)
if type(value) == "table" then return value end
if type(value) ~= "string" or value == "" then return nil end
local result = {}
for item in string.gmatch(value, "([^,]+)") do
item = item:gsub("^%s+", ""):gsub("%s+$", "")
if item ~= "" then result[#result + 1] = item end
end
if #result > 0 then return result end
return nil
end
function MOOSE_BRIDGE:_BuildGroupSet(value)
local group_ids = self:_NormalizeStringList(value)
if not group_ids or #group_ids == 0 then error("TransportGroupSet requires at least one GROUP") end
if not SET_GROUP or not SET_GROUP.New then error("SET_GROUP:New is not available") end
local set_group = SET_GROUP:New()
for _, group_id in ipairs(group_ids) do
local inputs = {target_id=group_id}
local group, group_err = self:_ResolveGroupAuftragTarget(inputs)
if not group then error(group_err) end
if type(set_group.AddGroup) == "function" then
set_group:AddGroup(group)
elseif type(set_group.Add) == "function" then
set_group:Add(group)
elseif type(set_group.AddObject) == "function" then
set_group:AddObject(group)
else
error("SET_GROUP add method is not available")
end
end
return set_group, group_ids
end
function MOOSE_BRIDGE:_CallAuftragLifecycleMethod(cmd, action, method_name)
local auftrag_id = self:_CommandAuftragId(cmd)
local auftrag, auftrag_err = self:_ResolveTrackedAuftragById(auftrag_id)
if not auftrag then error(auftrag_err) end
if type(auftrag[method_name]) ~= "function" then error("AUFTRAG:" .. method_name .. " is not available for " .. bridge_safe_tostring(auftrag_id)) end
local ok, result = pcall(function() return auftrag[method_name](auftrag) end)
if not ok then error("AUFTRAG:" .. method_name .. " failed: " .. bridge_safe_tostring(result)) end
self:_TrackAuftragReference(auftrag)
return {
action=action,
auftrag_id=auftrag_id,
auftragsnummer=self:_AuftragNumber(auftrag),
auftrag_type=self:_SafeCall(auftrag, "GetType") or auftrag.type,
status=self:_SafeCall(auftrag, "GetState") or self:_SafeCall(auftrag, "GetStatus"),
}
end
function MOOSE_BRIDGE:_AssignTrackedAuftrag(cmd)
local inputs = self:_CommonAuftragCommandInputs(cmd)
local auftrag_id = self:_CommandAuftragId(cmd)
local auftrag, auftrag_err = self:_ResolveTrackedAuftragById(auftrag_id)
if not auftrag then error(auftrag_err) end
self:_AddAuftragToTarget(auftrag, inputs)
return self:_BuildAuftragCommandResult("auftrag.assign", auftrag, inputs)
end
function MOOSE_BRIDGE:_SetCohortWeaponRange(cmd)
local p = self:_CommandParams(cmd)
local cohort_id = bridge_optional_string_param(p.cohort_id)
local weapon_type = bridge_number_param(p.weapon_type)
local minimum_m = bridge_number_param(p.minimum_m)
local maximum_m = bridge_number_param(p.maximum_m)
if not cohort_id then error("cohort.set_weapon_range requires cohort_id") end
if weapon_type == nil then error("cohort.set_weapon_range requires weapon_type") end
if minimum_m == nil then minimum_m = 0 end
if maximum_m == nil or maximum_m <= 0 then error("cohort.set_weapon_range requires positive maximum_m") end
if minimum_m < 0 or minimum_m > maximum_m then error("Invalid cohort weapon range") end
local cohort, cohort_err = self:_ResolveCohortById(cohort_id)
if not cohort then error(cohort_err) end
if type(cohort.AddWeaponRange) ~= "function" then error("COHORT:AddWeaponRange is not available") end
local previous = type(cohort.weaponData) == "table" and cohort.weaponData[bridge_safe_tostring(weapon_type)] or nil
local ok, err = pcall(function()
return cohort:AddWeaponRange(minimum_m / 1852, maximum_m / 1852, weapon_type)
end)
if not ok then error("COHORT:AddWeaponRange failed: " .. bridge_safe_tostring(err)) end
return {
action="cohort.set_weapon_range",
cohort_id=cohort_id,
weapon_type=weapon_type,
minimum_m=minimum_m,
maximum_m=maximum_m,
previous_minimum_m=previous and previous.RangeMin or nil,
previous_maximum_m=previous and previous.RangeMax or nil,
mission_range_m=self:_SafeCallArg(cohort, "GetMissionRange", {weapon_type}),
}
end
function MOOSE_BRIDGE:_BuildZoneSet(value, label, required)
local zone_ids = self:_NormalizeStringList(value)
if not zone_ids or #zone_ids == 0 then
if required then error(label .. " requires at least one ZONE") end
return nil, nil
end
if not SET_ZONE or not SET_ZONE.New then error("SET_ZONE:New is not available") end
local set_zone = SET_ZONE:New()
for _, zone_id in ipairs(zone_ids) do
local ok_zone, zone = pcall(function() return self:_ZoneForDrawObjectId(zone_id) end)
if not ok_zone or not zone then error(label .. " zone not found: " .. bridge_safe_tostring(zone_id)) end
if type(set_zone.AddZone) == "function" then
set_zone:AddZone(zone)
elseif type(set_zone.Add) == "function" then
set_zone:Add(zone)
elseif type(set_zone.AddObject) == "function" then
set_zone:AddObject(zone)
else
error("SET_ZONE add method is not available")
end
end
return set_zone, zone_ids
end
function MOOSE_BRIDGE:_BuildNoEngageZoneSet(value)
return self:_BuildZoneSet(value, "NoEngage", false)
end
function MOOSE_BRIDGE:_AddAuftragToLegion(auftrag, inputs)
if not auftrag then error("AUFTRAG constructor returned nil") end
self:_ApplyAuftragAssignments(auftrag, inputs)
self:_ApplyAuftragTiming(auftrag, inputs)
self:_RegisterAuftragEvents(auftrag, inputs)
self:_SendAuftragStatusEvent(auftrag, inputs, "Planned")
local add_ok, add_result = pcall(function() return inputs.legion:AddMission(auftrag) end)
if not add_ok then error("LEGION:AddMission failed: " .. bridge_safe_tostring(add_result)) end
self:_TrackAuftragReference(auftrag)
return auftrag
end
function MOOSE_BRIDGE:_AddAuftragToCommander(auftrag, inputs)
if not auftrag then error("AUFTRAG constructor returned nil") end
if type(inputs.commander.AddMission) ~= "function" then error("COMMANDER:AddMission is not available") end
self:_ApplyAuftragAssignments(auftrag, inputs)
self:_ApplyAuftragTiming(auftrag, inputs)
self:_RegisterAuftragEvents(auftrag, inputs)
self:_SendAuftragStatusEvent(auftrag, inputs, "Planned")
local add_ok, add_result = pcall(function() return inputs.commander:AddMission(auftrag) end)
if not add_ok then error("COMMANDER:AddMission failed: " .. bridge_safe_tostring(add_result)) end
self:_TrackAuftragReference(auftrag)
return auftrag
end
function MOOSE_BRIDGE:_AddAuftragToOpsGroup(auftrag, inputs)
if not auftrag then error("AUFTRAG constructor returned nil") end
if type(inputs.opsgroup.AddMission) ~= "function" then error("OPSGROUP:AddMission is not available") end
self:_ApplyAuftragTiming(auftrag, inputs)
self:_RegisterAuftragEvents(auftrag, inputs)
self:_SendAuftragStatusEvent(auftrag, inputs, "Planned")
local add_ok, add_result = pcall(function() return inputs.opsgroup:AddMission(auftrag) end)
if not add_ok then error("OPSGROUP:AddMission failed: " .. bridge_safe_tostring(add_result)) end
self:_TrackAuftragReference(auftrag)
return auftrag
end
function MOOSE_BRIDGE:_ApplyAuftragAssignments(auftrag, inputs)
if not auftrag or not inputs then return auftrag end
for _, legion in ipairs(inputs.allowed_legions or {}) do
if type(auftrag.AssignLegion) ~= "function" then error("AUFTRAG:AssignLegion is not available") end
local ok, err = pcall(function() return auftrag:AssignLegion(legion) end)
if not ok then error("AUFTRAG:AssignLegion failed: " .. bridge_safe_tostring(err)) end
end
for _, cohort in ipairs(inputs.allowed_cohorts or {}) do
if type(auftrag.AssignCohort) ~= "function" then error("AUFTRAG:AssignCohort is not available") end
local ok, err = pcall(function() return auftrag:AssignCohort(cohort) end)
if not ok then error("AUFTRAG:AssignCohort failed: " .. bridge_safe_tostring(err)) end
end
return auftrag
end
function MOOSE_BRIDGE:_ApplyAuftragTiming(auftrag, inputs)
if not auftrag or not inputs then return auftrag end
if inputs.clock_start ~= nil or inputs.clock_stop ~= nil then
if type(auftrag.SetTime) ~= "function" then error("AUFTRAG:SetTime is not available") end
local time_ok, time_err = pcall(function() return auftrag:SetTime(inputs.clock_start, inputs.clock_stop) end)
if not time_ok then error("AUFTRAG:SetTime failed: " .. bridge_safe_tostring(time_err)) end
end
if inputs.duration ~= nil then
if type(auftrag.SetDuration) ~= "function" then error("AUFTRAG:SetDuration is not available") end
local duration_ok, duration_err = pcall(function() return auftrag:SetDuration(inputs.duration) end)
if not duration_ok then error("AUFTRAG:SetDuration failed: " .. bridge_safe_tostring(duration_err)) end
end
if inputs.required_assets_min ~= nil or inputs.required_assets_max ~= nil then
if type(auftrag.SetRequiredAssets) ~= "function" then error("AUFTRAG:SetRequiredAssets is not available") end
local nassets_min = inputs.required_assets_min
if nassets_min == nil then nassets_min = 1 end
local assets_ok, assets_err = pcall(function() return auftrag:SetRequiredAssets(nassets_min, inputs.required_assets_max) end)
if not assets_ok then error("AUFTRAG:SetRequiredAssets failed: " .. bridge_safe_tostring(assets_err)) end
end
if inputs.weapon_type ~= nil then
if type(auftrag.SetWeaponType) ~= "function" then error("AUFTRAG:SetWeaponType is not available") end
local weapon_ok, weapon_err = pcall(function() return auftrag:SetWeaponType(inputs.weapon_type) end)
if not weapon_ok then error("AUFTRAG:SetWeaponType failed: " .. bridge_safe_tostring(weapon_err)) end
end
return auftrag
end
function MOOSE_BRIDGE:_SendAuftragStatusEvent(auftrag, inputs, fsm_event, From, Event, To)
if type(auftrag) ~= "table" then return end
local object_id = self:_AuftragObjectId(auftrag)
local ok, err = pcall(function()
self:SendEvent("auftrag.status", {
auftrag_id=object_id,
auftragsnummer=self:_AuftragNumber(auftrag),
auftrag_type=self:_SafeCall(auftrag, "GetType") or auftrag.type,
status=self:_SafeCall(auftrag, "GetState") or self:_SafeCall(auftrag, "GetStatus") or To,
fsm_event=fsm_event,
from=From,
fsm_event_name=Event,
to=To,
commander_id=inputs and inputs.commander_id or nil,
legion_id=inputs and inputs.legion_id or nil,
opsgroup_id=inputs and inputs.opsgroup_id or nil,
cohort_id=inputs and inputs.cohort_id or nil,
target=inputs and (inputs.target_id or inputs.zone_id) or nil,
})
end)
if not ok and env and env.error then env.error("MooseBridge AUFTRAG status event failed: " .. bridge_safe_tostring(err)) end
end
function MOOSE_BRIDGE:_SendAuftragEvaluatedEvent(auftrag, inputs, From, Event, To, Summary)
if type(auftrag) ~= "table" then return end
local summary = bridge_auftrag_summary(Summary)
local object_id = self:_AuftragObjectId(auftrag)
local ok, err = pcall(function()
self:SendEvent("auftrag.evaluated", {
auftrag_id=object_id,
auftragsnummer=self:_AuftragNumber(auftrag),
auftrag_type=self:_SafeCall(auftrag, "GetType") or auftrag.type,
status=self:_SafeCall(auftrag, "GetState") or self:_SafeCall(auftrag, "GetStatus") or To,
fsm_event="Evaluated",
from=From,
fsm_event_name=Event,
to=To,
commander_id=inputs and inputs.commander_id or nil,
legion_id=inputs and inputs.legion_id or nil,
opsgroup_id=inputs and inputs.opsgroup_id or nil,
cohort_id=inputs and inputs.cohort_id or nil,
target=inputs and (inputs.target_id or inputs.zone_id) or nil,
zones=inputs and inputs.zones or nil,
summary=summary,
auftrag={
object_id=object_id,
auftragsnummer=self:_AuftragNumber(auftrag),
type=self:_SafeCall(auftrag, "GetType") or auftrag.type,
status=self:_SafeCall(auftrag, "GetState") or self:_SafeCall(auftrag, "GetStatus") or To,
zones=inputs and inputs.zones or nil,
summary_available=summary ~= nil,
summary=summary,
},
})
end)
if not ok and env and env.error then env.error("MooseBridge OnAfterEvaluated send failed: " .. bridge_safe_tostring(err)) end
end
function MOOSE_BRIDGE:_RegisterAuftragEvents(auftrag, inputs)
if type(auftrag) ~= "table" then return end
if auftrag.MooseBridgeEvaluatedEventRegistered then return end
local bridge = self
auftrag.MooseBridgeEvaluatedEventRegistered = true
local sent_status_events = {}
local function register_after_event(method_event, output_event)
output_event = output_event or method_event
local method_name = "OnAfter" .. method_event
local previous_handler = auftrag[method_name]
auftrag[method_name] = function(auftrag_object, From, Event, To, ...)
local extra = {...}
if type(previous_handler) == "function" then
pcall(function() previous_handler(auftrag_object, From, Event, To, bridge_unpack(extra)) end)
end
if output_event == "Evaluated" then
bridge:_SendAuftragEvaluatedEvent(auftrag_object, inputs, From, Event, To, extra[1])
return
end
if sent_status_events[output_event] then return end
sent_status_events[output_event] = true
bridge:_SendAuftragStatusEvent(auftrag_object, inputs, output_event, From, Event, To)
end
end
register_after_event("Queued")
register_after_event("Requested")
register_after_event("Scheduled")
register_after_event("Started")
register_after_event("Executing")
register_after_event("Done")
register_after_event("Cancel")
register_after_event("Evaluated")
end
function MOOSE_BRIDGE:_AddAuftragToTarget(auftrag, inputs)
if inputs.opsgroup then return self:_AddAuftragToOpsGroup(auftrag, inputs) end
if inputs.commander then return self:_AddAuftragToCommander(auftrag, inputs) end
return self:_AddAuftragToLegion(auftrag, inputs)
end
function MOOSE_BRIDGE:_BuildAuftragCommandResult(action, auftrag, inputs)
return {
action=action,
commander_id=inputs.commander_id,
legion_id=inputs.legion_id,
opsgroup_id=inputs.opsgroup_id,
cohort_id=inputs.cohort_id,
allowed_legion_ids=inputs.allowed_legion_ids,
allowed_cohort_ids=inputs.allowed_cohort_ids,
clock_start=inputs.clock_start,
clock_stop=inputs.clock_stop,
duration=inputs.duration,
required_assets_min=inputs.required_assets_min,
required_assets_max=inputs.required_assets_max,
weapon_type=inputs.weapon_type,
target=inputs.target_id,
opszone=inputs.opszone_id,
capture_coalition=inputs.capture_coalition,
stay_in_zone_time_s=inputs.stay_in_zone_time_s,
zone=inputs.zone_id,
zones=inputs.zones,
coordinate=inputs.coordinate_id,
dropoff=inputs.dropoff_id,
pickup=inputs.pickup_id,
x=inputs.x,
y=inputs.y,
z=inputs.z,
dropoff_x=inputs.dropoff_x,
dropoff_y=inputs.dropoff_y,
dropoff_z=inputs.dropoff_z,
pickup_x=inputs.pickup_x,
pickup_y=inputs.pickup_y,
pickup_z=inputs.pickup_z,
altitude_ft=inputs.altitude_ft,
engage_weapon_type=inputs.engage_weapon_type,
divebomb=inputs.divebomb,
nshots=inputs.nshots,
radius_m=inputs.radius_m,
carpet_length_m=inputs.carpet_length_m,
length_m=inputs.length_m,
speed_kts=inputs.speed_kts,
ad_infinitum=inputs.ad_infinitum,
randomly=inputs.randomly,
formation=inputs.formation,
depth_m=inputs.depth_m,
heading_deg=inputs.heading_deg,
leg_nm=inputs.leg_nm,
refuel_system=inputs.refuel_system,
range_max_nm=inputs.range_max_nm,
no_engage_zones=inputs.no_engage_zones,
frequency_mhz=inputs.frequency_mhz,
modulation=inputs.modulation,
designation=inputs.designation,
data_link=inputs.data_link,
target_types=inputs.target_types,
orbit_distance_nm=inputs.orbit_distance_nm,
engage_max_distance_nm=inputs.engage_max_distance_nm,
offset_x=inputs.offset_x,
offset_y=inputs.offset_y,
offset_z=inputs.offset_z,
transport_groups=inputs.transport_groups,
pickup_radius_m=inputs.pickup_radius_m,
selected_payload_uid=inputs.selected_payload_uid,
auftrag_id=self:_AuftragObjectId(auftrag),
auftragsnummer=self:_AuftragNumber(auftrag),
auftrag_type=self:_SafeCall(auftrag, "GetType") or auftrag.type,
added=true,
}
end
function MOOSE_BRIDGE:_CreateZonePatrolAuftrag(cmd, action, constructor_name)
local inputs = self:_CommonAuftragCommandInputs(cmd)
local zone, zone_err = self:_ResolveZonePatrolZone(inputs)
if not zone then error(zone_err) end
local coordinate, coordinate_err = self:_ResolveZonePatrolCoordinate(inputs)
if coordinate_err then error(coordinate_err) end
if not AUFTRAG or type(AUFTRAG[constructor_name]) ~= "function" then error("AUFTRAG:" .. constructor_name .. " is not available") end
local altitude_ft = inputs.altitude_ft and tonumber(inputs.altitude_ft) or nil
local speed_kts = inputs.speed_kts and tonumber(inputs.speed_kts) or nil
local heading_deg = inputs.heading_deg and tonumber(inputs.heading_deg) or nil
local leg_nm = inputs.leg_nm and tonumber(inputs.leg_nm) or nil
local target_types = self:_NormalizeTargetTypes(inputs.target_types)
inputs.target_types = target_types
local auftrag = AUFTRAG[constructor_name](AUFTRAG, zone, altitude_ft, speed_kts, coordinate, heading_deg, leg_nm, target_types)
self:_AddAuftragToTarget(auftrag, inputs)
return self:_BuildAuftragCommandResult(action, auftrag, inputs)
end
function MOOSE_BRIDGE:_CreateOrbitRoleAuftrag(cmd, action, constructor_name, include_refuel_system)
local inputs = self:_CommonAuftragCommandInputs(cmd)
local target, target_err = self:_ResolveOrbitTarget(inputs)
if not target then error(target_err) end
if not AUFTRAG or type(AUFTRAG[constructor_name]) ~= "function" then error("AUFTRAG:" .. constructor_name .. " is not available") end
local altitude_ft = inputs.altitude_ft and tonumber(inputs.altitude_ft) or nil
local speed_kts = inputs.speed_kts and tonumber(inputs.speed_kts) or nil
local heading_deg = inputs.heading_deg and tonumber(inputs.heading_deg) or nil
local leg_nm = inputs.leg_nm and tonumber(inputs.leg_nm) or nil
local refuel_system = include_refuel_system and inputs.refuel_system and tonumber(inputs.refuel_system) or nil
local auftrag = nil
if include_refuel_system then
auftrag = AUFTRAG[constructor_name](AUFTRAG, target, altitude_ft, speed_kts, heading_deg, leg_nm, refuel_system)
else
auftrag = AUFTRAG[constructor_name](AUFTRAG, target, altitude_ft, speed_kts, heading_deg, leg_nm)
end
self:_AddAuftragToTarget(auftrag, inputs)
return self:_BuildAuftragCommandResult(action, auftrag, inputs)
end
function MOOSE_BRIDGE:_CreateCasEnhancedAuftrag(cmd)
local inputs = self:_CommonAuftragCommandInputs(cmd)
local zone, zone_err = self:_ResolveZonePatrolZone(inputs)
if not zone then error(zone_err) end
if not AUFTRAG or not AUFTRAG.NewCASENHANCED then error("AUFTRAG:NewCASENHANCED is not available") end
local altitude_ft = inputs.altitude_ft and tonumber(inputs.altitude_ft) or nil
local speed_kts = inputs.speed_kts and tonumber(inputs.speed_kts) or nil
local range_max_nm = inputs.range_max_nm and tonumber(inputs.range_max_nm) or nil
local no_engage_zone_set = self:_BuildNoEngageZoneSet(inputs.no_engage_zones)
local target_types = self:_NormalizeTargetTypes(inputs.target_types)
inputs.no_engage_zones = self:_NormalizeStringList(inputs.no_engage_zones)
inputs.target_types = target_types
local auftrag = AUFTRAG:NewCASENHANCED(zone, altitude_ft, speed_kts, range_max_nm, no_engage_zone_set, target_types)
self:_AddAuftragToTarget(auftrag, inputs)
return self:_BuildAuftragCommandResult("auftrag.create_casenhanced", auftrag, inputs)
end
function MOOSE_BRIDGE:_CreateFacAuftrag(cmd)
local inputs = self:_CommonAuftragCommandInputs(cmd)
local zone, zone_err = self:_ResolveZonePatrolZone(inputs)
if not zone then error(zone_err) end
if not AUFTRAG or not AUFTRAG.NewFAC then error("AUFTRAG:NewFAC is not available") end
local speed_kts = inputs.speed_kts and tonumber(inputs.speed_kts) or nil
local altitude_ft = inputs.altitude_ft and tonumber(inputs.altitude_ft) or nil
local frequency_mhz = inputs.frequency_mhz and tonumber(inputs.frequency_mhz) or nil
local modulation = inputs.modulation and tonumber(inputs.modulation) or nil
local auftrag = AUFTRAG:NewFAC(zone, speed_kts, altitude_ft, frequency_mhz, modulation)
self:_AddAuftragToTarget(auftrag, inputs)
return self:_BuildAuftragCommandResult("auftrag.create_fac", auftrag, inputs)
end
function MOOSE_BRIDGE:_CreatePatrolZoneAuftrag(cmd)
local inputs = self:_CommonAuftragCommandInputs(cmd)
local zone, zone_err = self:_ResolveZonePatrolZone(inputs)
if not zone then error(zone_err) end
if not AUFTRAG or not AUFTRAG.NewPATROLZONE then error("AUFTRAG:NewPATROLZONE is not available") end
local speed_kts = inputs.speed_kts and tonumber(inputs.speed_kts) or nil
local altitude_ft = inputs.altitude_ft and tonumber(inputs.altitude_ft) or nil
local auftrag = AUFTRAG:NewPATROLZONE(zone, speed_kts, altitude_ft, inputs.formation)
self:_AddAuftragToTarget(auftrag, inputs)
return self:_BuildAuftragCommandResult("auftrag.create_patrolzone", auftrag, inputs)
end
function MOOSE_BRIDGE:_CreateReconAuftrag(cmd)
local inputs = self:_CommonAuftragCommandInputs(cmd)
local zone_set, zone_ids = self:_BuildZoneSet(inputs.zones, "RECON", true)
inputs.zones = zone_ids
if not AUFTRAG or not AUFTRAG.NewRECON then error("AUFTRAG:NewRECON is not available") end
local speed_kts = inputs.speed_kts and tonumber(inputs.speed_kts) or nil
local altitude_ft = inputs.altitude_ft and tonumber(inputs.altitude_ft) or nil
local auftrag = AUFTRAG:NewRECON(
zone_set,
speed_kts,
altitude_ft,
inputs.ad_infinitum,
inputs.randomly,
inputs.formation
)
self:_AddAuftragToTarget(auftrag, inputs)
return self:_BuildAuftragCommandResult("auftrag.create_recon", auftrag, inputs)
end
function MOOSE_BRIDGE:_CreateCaptureZoneAuftrag(cmd)
local inputs = self:_CommonAuftragCommandInputs(cmd)
local opszone, opszone_err = self:_ResolveCaptureOpsZone(inputs)
if not opszone then error(opszone_err) end
if inputs.capture_coalition == nil then error("CAPTUREZONE requires capture_coalition") end
if not AUFTRAG or not AUFTRAG.NewCAPTUREZONE then error("AUFTRAG:NewCAPTUREZONE is not available") end
local speed_kts = inputs.speed_kts and tonumber(inputs.speed_kts) or nil
local altitude_ft = inputs.altitude_ft and tonumber(inputs.altitude_ft) or nil
local stay_in_zone_time_s = inputs.stay_in_zone_time_s and tonumber(inputs.stay_in_zone_time_s) or nil
local auftrag = AUFTRAG:NewCAPTUREZONE(opszone, inputs.capture_coalition, speed_kts, altitude_ft, inputs.formation, stay_in_zone_time_s)
self:_AddAuftragToTarget(auftrag, inputs)
return self:_BuildAuftragCommandResult("auftrag.create_capturezone", auftrag, inputs)
end
function MOOSE_BRIDGE:_CreateZoneOnlyAuftrag(cmd, action, constructor_name)
local inputs = self:_CommonAuftragCommandInputs(cmd)
local zone, zone_err = self:_ResolveZonePatrolZone(inputs)
if not zone then error(zone_err) end
if not AUFTRAG or type(AUFTRAG[constructor_name]) ~= "function" then error("AUFTRAG:" .. constructor_name .. " is not available") end
local auftrag = AUFTRAG[constructor_name](AUFTRAG, zone)
self:_AddAuftragToTarget(auftrag, inputs)
return self:_BuildAuftragCommandResult(action, auftrag, inputs)
end
function MOOSE_BRIDGE:_CreateFacaAuftrag(cmd)
local inputs = self:_CommonAuftragCommandInputs(cmd)
local target, target_err = self:_ResolveGroupAuftragTarget(inputs)
if not target then error(target_err) end
if not AUFTRAG or not AUFTRAG.NewFACA then error("AUFTRAG:NewFACA is not available") end
local data_link = bridge_bool_param(inputs.data_link)
local frequency_mhz = inputs.frequency_mhz and tonumber(inputs.frequency_mhz) or nil
local modulation = inputs.modulation and tonumber(inputs.modulation) or nil
local auftrag = AUFTRAG:NewFACA(target, inputs.designation, data_link, frequency_mhz, modulation)
self:_AddAuftragToTarget(auftrag, inputs)
return self:_BuildAuftragCommandResult("auftrag.create_faca", auftrag, inputs)
end
function MOOSE_BRIDGE:_CreateSeadAuftrag(cmd)
local inputs = self:_CommonAuftragCommandInputs(cmd)
local target, target_err = self:_ResolveGroupOrUnitAuftragTarget(inputs)
if not target then error(target_err) end
if not AUFTRAG or not AUFTRAG.NewSEAD then error("AUFTRAG:NewSEAD is not available") end
local altitude_ft = inputs.altitude_ft and tonumber(inputs.altitude_ft) or nil
local auftrag = AUFTRAG:NewSEAD(target, altitude_ft)
self:_AddAuftragToTarget(auftrag, inputs)
return self:_BuildAuftragCommandResult("auftrag.create_sead", auftrag, inputs)
end
function MOOSE_BRIDGE:_CreateAntiShipAuftrag(cmd)
local inputs = self:_CommonAuftragCommandInputs(cmd)
local target, target_err = self:_ResolveGroupOrUnitAuftragTarget(inputs)
if not target then error(target_err) end
if not AUFTRAG or not AUFTRAG.NewANTISHIP then error("AUFTRAG:NewANTISHIP is not available") end
local altitude_ft = inputs.altitude_ft and tonumber(inputs.altitude_ft) or nil
local auftrag = AUFTRAG:NewANTISHIP(target, altitude_ft)
self:_AddAuftragToTarget(auftrag, inputs)
return self:_BuildAuftragCommandResult("auftrag.create_antiship", auftrag, inputs)
end
function MOOSE_BRIDGE:_CreateOnGuardAuftrag(cmd)
local inputs = self:_CommonAuftragCommandInputs(cmd)
local target, target_err = self:_ResolveOnGuardTarget(inputs)
if not target then error(target_err) end
if not AUFTRAG or not AUFTRAG.NewONGUARD then error("AUFTRAG:NewONGUARD is not available") end
local auftrag = AUFTRAG:NewONGUARD(target)
self:_AddAuftragToTarget(auftrag, inputs)
return self:_BuildAuftragCommandResult("auftrag.create_onguard", auftrag, inputs)
end
function MOOSE_BRIDGE:_CreateInterceptAuftrag(cmd)
local inputs = self:_CommonAuftragCommandInputs(cmd)
local target, target_err = self:_ResolveGroupOrUnitAuftragTarget(inputs)
if not target then error(target_err) end
if not AUFTRAG or not AUFTRAG.NewINTERCEPT then error("AUFTRAG:NewINTERCEPT is not available") end
local auftrag = AUFTRAG:NewINTERCEPT(target)
self:_AddAuftragToTarget(auftrag, inputs)
return self:_BuildAuftragCommandResult("auftrag.create_intercept", auftrag, inputs)
end
function MOOSE_BRIDGE:_CreateStrikeAuftrag(cmd)
local inputs = self:_CommonAuftragCommandInputs(cmd)
local target, target_err = self:_ResolveStrikeTarget(inputs)
if not target then error(target_err) end
if not AUFTRAG or not AUFTRAG.NewSTRIKE then error("AUFTRAG:NewSTRIKE is not available") end
local altitude_ft = inputs.altitude_ft and tonumber(inputs.altitude_ft) or nil
local engage_weapon_type = inputs.engage_weapon_type and tonumber(inputs.engage_weapon_type) or nil
local auftrag = AUFTRAG:NewSTRIKE(target, altitude_ft, engage_weapon_type)
self:_AddAuftragToTarget(auftrag, inputs)
return self:_BuildAuftragCommandResult("auftrag.create_strike", auftrag, inputs)
end
function MOOSE_BRIDGE:_CreateStrafingAuftrag(cmd)
local inputs = self:_CommonAuftragCommandInputs(cmd)
local target, target_err = self:_ResolveStrafingTarget(inputs)
if not target then error(target_err) end
if not AUFTRAG or not AUFTRAG.NewSTRAFING then error("AUFTRAG:NewSTRAFING is not available") end
local altitude_ft = inputs.altitude_ft and tonumber(inputs.altitude_ft) or nil
local length_m = inputs.length_m and tonumber(inputs.length_m) or nil
local auftrag = AUFTRAG:NewSTRAFING(target, altitude_ft, length_m)
self:_AddAuftragToTarget(auftrag, inputs)
return self:_BuildAuftragCommandResult("auftrag.create_strafing", auftrag, inputs)
end
function MOOSE_BRIDGE:_CreateBombRunwayAuftrag(cmd)
local inputs = self:_CommonAuftragCommandInputs(cmd)
local target, target_err = self:_ResolveAirbaseAuftragTarget(inputs)
if not target then error(target_err) end
if not AUFTRAG or not AUFTRAG.NewBOMBRUNWAY then error("AUFTRAG:NewBOMBRUNWAY is not available") end
local altitude_ft = inputs.altitude_ft and tonumber(inputs.altitude_ft) or nil
local auftrag = AUFTRAG:NewBOMBRUNWAY(target, altitude_ft)
self:_AddAuftragToTarget(auftrag, inputs)
return self:_BuildAuftragCommandResult("auftrag.create_bombrunway", auftrag, inputs)
end
function MOOSE_BRIDGE:_CreateBombCarpetAuftrag(cmd)
local inputs = self:_CommonAuftragCommandInputs(cmd)
local target, target_err = self:_ResolveBombCarpetTarget(inputs)
if not target then error(target_err) end
if not AUFTRAG or not AUFTRAG.NewBOMBCARPET then error("AUFTRAG:NewBOMBCARPET is not available") end
local altitude_ft = inputs.altitude_ft and tonumber(inputs.altitude_ft) or nil
local carpet_length_m = inputs.carpet_length_m and tonumber(inputs.carpet_length_m) or nil
local auftrag = AUFTRAG:NewBOMBCARPET(target, altitude_ft, carpet_length_m)
self:_AddAuftragToTarget(auftrag, inputs)
return self:_BuildAuftragCommandResult("auftrag.create_bombcarpet", auftrag, inputs)
end
function MOOSE_BRIDGE:_CreateGroundEscortAuftrag(cmd)
local inputs = self:_CommonAuftragCommandInputs(cmd)
local target, target_err = self:_ResolveGroupAuftragTarget(inputs)
if not target then error(target_err) end
if not AUFTRAG or not AUFTRAG.NewGROUNDESCORT then error("AUFTRAG:NewGROUNDESCORT is not available") end
local orbit_distance_nm = inputs.orbit_distance_nm and tonumber(inputs.orbit_distance_nm) or nil
local target_types = self:_NormalizeTargetTypes(inputs.target_types)
inputs.target_types = target_types
local auftrag = AUFTRAG:NewGROUNDESCORT(target, orbit_distance_nm, target_types)
self:_AddAuftragToTarget(auftrag, inputs)
return self:_BuildAuftragCommandResult("auftrag.create_groundescort", auftrag, inputs)
end
function MOOSE_BRIDGE:_CreateGroundAttackAuftrag(cmd)
local inputs = self:_CommonAuftragCommandInputs(cmd)
local target, target_err = self:_ResolvePositionableAttackAuftragTarget(inputs)
if not target then error(target_err) end
if not AUFTRAG or not AUFTRAG.NewGROUNDATTACK then error("AUFTRAG:NewGROUNDATTACK is not available") end
local speed_kts = inputs.speed_kts and tonumber(inputs.speed_kts) or nil
local auftrag = AUFTRAG:NewGROUNDATTACK(target, speed_kts, inputs.formation)
self:_AddAuftragToTarget(auftrag, inputs)
return self:_BuildAuftragCommandResult("auftrag.create_groundattack", auftrag, inputs)
end
function MOOSE_BRIDGE:_CreateNavalEngagementAuftrag(cmd)
local inputs = self:_CommonAuftragCommandInputs(cmd)
local target, target_err = self:_ResolvePositionableAttackAuftragTarget(inputs)
if not target then error(target_err) end
if not AUFTRAG or not AUFTRAG.NewNAVALENGAGEMENT then error("AUFTRAG:NewNAVALENGAGEMENT is not available") end
local speed_kts = inputs.speed_kts and tonumber(inputs.speed_kts) or nil
local depth_m = inputs.depth_m and tonumber(inputs.depth_m) or nil
local auftrag = AUFTRAG:NewNAVALENGAGEMENT(target, speed_kts, depth_m)
self:_AddAuftragToTarget(auftrag, inputs)
return self:_BuildAuftragCommandResult("auftrag.create_navalengagement", auftrag, inputs)
end
function MOOSE_BRIDGE:_CreateEscortAuftrag(cmd)
local inputs = self:_CommonAuftragCommandInputs(cmd)
local target, target_err = self:_ResolveGroupAuftragTarget(inputs)
if not target then error(target_err) end
if not AUFTRAG or not AUFTRAG.NewESCORT then error("AUFTRAG:NewESCORT is not available") end
local offset_vector = self:_BuildOffsetVector(inputs)
local engage_max_distance_nm = inputs.engage_max_distance_nm and tonumber(inputs.engage_max_distance_nm) or nil
local target_types = self:_NormalizeTargetTypes(inputs.target_types)
inputs.target_types = target_types
local auftrag = AUFTRAG:NewESCORT(target, offset_vector, engage_max_distance_nm, target_types)
self:_AddAuftragToTarget(auftrag, inputs)
return self:_BuildAuftragCommandResult("auftrag.create_escort", auftrag, inputs)
end
function MOOSE_BRIDGE:_CreateRescueHeloAuftrag(cmd)
local inputs = self:_CommonAuftragCommandInputs(cmd)
local target, target_err = self:_ResolveUnitAuftragTarget(inputs)
if not target then error(target_err) end
if not AUFTRAG or not AUFTRAG.NewRESCUEHELO then error("AUFTRAG:NewRESCUEHELO is not available") end
local auftrag = AUFTRAG:NewRESCUEHELO(target)
self:_AddAuftragToTarget(auftrag, inputs)
return self:_BuildAuftragCommandResult("auftrag.create_rescuehelo", auftrag, inputs)
end
function MOOSE_BRIDGE:_CreateTroopTransportAuftrag(cmd)
local inputs = self:_CommonAuftragCommandInputs(cmd)
local transport_group_set, group_ids = self:_BuildGroupSet(inputs.transport_groups)
inputs.transport_groups = group_ids
local dropoff_coordinate, dropoff_err = self:_ResolveCoordinateFromNamedFields(inputs.dropoff_id, inputs.dropoff_x, inputs.dropoff_y, inputs.dropoff_z, "Dropoff", true)
if not dropoff_coordinate then error(dropoff_err) end
local pickup_coordinate, pickup_err = self:_ResolveCoordinateFromNamedFields(inputs.pickup_id, inputs.pickup_x, inputs.pickup_y, inputs.pickup_z, "Pickup", false)
if pickup_err then error(pickup_err) end
if not AUFTRAG or not AUFTRAG.NewTROOPTRANSPORT then error("AUFTRAG:NewTROOPTRANSPORT is not available") end
local pickup_radius_m = inputs.pickup_radius_m and tonumber(inputs.pickup_radius_m) or nil
local auftrag = AUFTRAG:NewTROOPTRANSPORT(transport_group_set, dropoff_coordinate, pickup_coordinate, pickup_radius_m)
self:_AddAuftragToTarget(auftrag, inputs)
return self:_BuildAuftragCommandResult("auftrag.create_trooptransport", auftrag, inputs)
end
function MOOSE_BRIDGE:RegisterAuftragExecutionCommands()
self:RegisterCommand("auftrag.create_bai", function(cmd)
local inputs = self:_CommonAuftragCommandInputs(cmd)
local target, target_err = self:_ResolveObjectAuftragTarget(inputs)
if not target then error(target_err) end
if not AUFTRAG or not AUFTRAG.NewBAI then error("AUFTRAG:NewBAI is not available") end
local auftrag = nil
if inputs.altitude_ft ~= nil then
auftrag = AUFTRAG:NewBAI(target, tonumber(inputs.altitude_ft))
else
auftrag = AUFTRAG:NewBAI(target)
end
self:_AddAuftragToTarget(auftrag, inputs)
return self:_BuildAuftragCommandResult("auftrag.create_bai", auftrag, inputs)
end)
self:RegisterCommand("auftrag.create_bombing", function(cmd)
local inputs = self:_CommonAuftragCommandInputs(cmd)
local target, target_err = self:_ResolveBombingTarget(inputs)
if not target then error(target_err) end
if not AUFTRAG or not AUFTRAG.NewBOMBING then error("AUFTRAG:NewBOMBING is not available") end
local altitude_ft = inputs.altitude_ft and tonumber(inputs.altitude_ft) or nil
local engage_weapon_type = inputs.engage_weapon_type and tonumber(inputs.engage_weapon_type) or nil
local divebomb = bridge_bool_param(inputs.divebomb)
local auftrag = AUFTRAG:NewBOMBING(target, altitude_ft, engage_weapon_type, divebomb)
self:_AddAuftragToTarget(auftrag, inputs)
return self:_BuildAuftragCommandResult("auftrag.create_bombing", auftrag, inputs)
end)
self:RegisterCommand("auftrag.create_arty", function(cmd)
local inputs = self:_CommonAuftragCommandInputs(cmd)
local target, target_err = self:_ResolveArtyTarget(inputs)
if not target then error(target_err) end
if not AUFTRAG or not AUFTRAG.NewARTY then error("AUFTRAG:NewARTY is not available") end
local nshots = inputs.nshots and tonumber(inputs.nshots) or nil
local radius_m = inputs.radius_m and tonumber(inputs.radius_m) or nil
local auftrag = AUFTRAG:NewARTY(target, nshots, radius_m)
self:_AddAuftragToTarget(auftrag, inputs)
return self:_BuildAuftragCommandResult("auftrag.create_arty", auftrag, inputs)
end)
self:RegisterCommand("auftrag.create_orbit", function(cmd)
local inputs = self:_CommonAuftragCommandInputs(cmd)
local target, target_err = self:_ResolveOrbitTarget(inputs)
if not target then error(target_err) end
if not AUFTRAG or not AUFTRAG.NewORBIT then error("AUFTRAG:NewORBIT is not available") end
local altitude_ft = inputs.altitude_ft and tonumber(inputs.altitude_ft) or nil
local speed_kts = inputs.speed_kts and tonumber(inputs.speed_kts) or nil
local heading_deg = inputs.heading_deg and tonumber(inputs.heading_deg) or nil
local leg_nm = inputs.leg_nm and tonumber(inputs.leg_nm) or nil
local auftrag = AUFTRAG:NewORBIT(target, altitude_ft, speed_kts, heading_deg, leg_nm)
self:_AddAuftragToTarget(auftrag, inputs)
return self:_BuildAuftragCommandResult("auftrag.create_orbit", auftrag, inputs)
end)
self:RegisterCommand("auftrag.create_awacs", function(cmd)
return self:_CreateOrbitRoleAuftrag(cmd, "auftrag.create_awacs", "NewAWACS", false)
end)
self:RegisterCommand("auftrag.create_tanker", function(cmd)
return self:_CreateOrbitRoleAuftrag(cmd, "auftrag.create_tanker", "NewTANKER", true)
end)
self:RegisterCommand("auftrag.create_cap", function(cmd)
return self:_CreateZonePatrolAuftrag(cmd, "auftrag.create_cap", "NewCAP")
end)
self:RegisterCommand("auftrag.create_cas", function(cmd)
return self:_CreateZonePatrolAuftrag(cmd, "auftrag.create_cas", "NewCAS")
end)
self:RegisterCommand("auftrag.create_casenhanced", function(cmd)
return self:_CreateCasEnhancedAuftrag(cmd)
end)
self:RegisterCommand("auftrag.create_fac", function(cmd)
return self:_CreateFacAuftrag(cmd)
end)
self:RegisterCommand("auftrag.create_patrolzone", function(cmd)
return self:_CreatePatrolZoneAuftrag(cmd)
end)
self:RegisterCommand("auftrag.create_recon", function(cmd)
return self:_CreateReconAuftrag(cmd)
end)
self:RegisterCommand("auftrag.create_capturezone", function(cmd)
return self:_CreateCaptureZoneAuftrag(cmd)
end)
self:RegisterCommand("auftrag.create_faca", function(cmd)
return self:_CreateFacaAuftrag(cmd)
end)
self:RegisterCommand("auftrag.create_ammosupply", function(cmd)
return self:_CreateZoneOnlyAuftrag(cmd, "auftrag.create_ammosupply", "NewAMMOSUPPLY")
end)
self:RegisterCommand("auftrag.create_fuelsupply", function(cmd)
return self:_CreateZoneOnlyAuftrag(cmd, "auftrag.create_fuelsupply", "NewFUELSUPPLY")
end)
self:RegisterCommand("auftrag.create_rearming", function(cmd)
return self:_CreateZoneOnlyAuftrag(cmd, "auftrag.create_rearming", "NewREARMING")
end)
self:RegisterCommand("auftrag.create_airdefense", function(cmd)
return self:_CreateZoneOnlyAuftrag(cmd, "auftrag.create_airdefense", "NewAIRDEFENSE")
end)
self:RegisterCommand("auftrag.create_ewr", function(cmd)
return self:_CreateZoneOnlyAuftrag(cmd, "auftrag.create_ewr", "NewEWR")
end)
self:RegisterCommand("auftrag.create_nothing", function(cmd)
return self:_CreateZoneOnlyAuftrag(cmd, "auftrag.create_nothing", "NewNOTHING")
end)
self:RegisterCommand("auftrag.create_sead", function(cmd)
return self:_CreateSeadAuftrag(cmd)
end)
self:RegisterCommand("auftrag.create_antiship", function(cmd)
return self:_CreateAntiShipAuftrag(cmd)
end)
self:RegisterCommand("auftrag.create_onguard", function(cmd)
return self:_CreateOnGuardAuftrag(cmd)
end)
self:RegisterCommand("auftrag.create_intercept", function(cmd)
return self:_CreateInterceptAuftrag(cmd)
end)
self:RegisterCommand("auftrag.create_strike", function(cmd)
return self:_CreateStrikeAuftrag(cmd)
end)
self:RegisterCommand("auftrag.create_strafing", function(cmd)
return self:_CreateStrafingAuftrag(cmd)
end)
self:RegisterCommand("auftrag.create_bombrunway", function(cmd)
return self:_CreateBombRunwayAuftrag(cmd)
end)
self:RegisterCommand("auftrag.create_bombcarpet", function(cmd)
return self:_CreateBombCarpetAuftrag(cmd)
end)
self:RegisterCommand("auftrag.create_groundescort", function(cmd)
return self:_CreateGroundEscortAuftrag(cmd)
end)
self:RegisterCommand("auftrag.create_groundattack", function(cmd)
return self:_CreateGroundAttackAuftrag(cmd)
end)
self:RegisterCommand("auftrag.create_navalengagement", function(cmd)
return self:_CreateNavalEngagementAuftrag(cmd)
end)
self:RegisterCommand("auftrag.create_escort", function(cmd)
return self:_CreateEscortAuftrag(cmd)
end)
self:RegisterCommand("auftrag.create_rescuehelo", function(cmd)
return self:_CreateRescueHeloAuftrag(cmd)
end)
self:RegisterCommand("auftrag.create_trooptransport", function(cmd)
return self:_CreateTroopTransportAuftrag(cmd)
end)
self:RegisterCommand("auftrag.cancel", function(cmd)
return self:_CallAuftragLifecycleMethod(cmd, "auftrag.cancel", "Cancel")
end)
self:RegisterCommand("auftrag.pause", function(cmd)
return self:_CallAuftragLifecycleMethod(cmd, "auftrag.pause", "Pause")
end)
self:RegisterCommand("auftrag.resume", function(cmd)
return self:_CallAuftragLifecycleMethod(cmd, "auftrag.resume", "Resume")
end)
self:RegisterCommand("auftrag.assign", function(cmd)
return self:_AssignTrackedAuftrag(cmd)
end)
self:RegisterCommand("cohort.set_weapon_range", function(cmd)
return self:_SetCohortWeaponRange(cmd)
end)
end
local _moose_bridge_base_add_auftrag_candidate = MOOSE_BRIDGE._AddAuftragCandidate
function MOOSE_BRIDGE:_AddAuftragCandidate(result, seen, auftrag, source)
self:_TrackAuftragReference(auftrag)
return _moose_bridge_base_add_auftrag_candidate(self, result, seen, auftrag, source)
end
local _moose_bridge_base_build_cohort_snapshot_item = MOOSE_BRIDGE._BuildCohortSnapshotItem
function MOOSE_BRIDGE:_BuildCohortSnapshotItem(cohort_name, cohort, source)
local item = _moose_bridge_base_build_cohort_snapshot_item(self, cohort_name, cohort, source)
if type(item) ~= "table" or type(cohort) ~= "table" then return item end
local mission_range = self:_SafeCall(cohort, "GetMissionRange") or cohort.missionRange or cohort.MissionRange
item.mission_range_m = mission_range
return item
end
local _moose_bridge_base_build_auftrag_snapshot_item = MOOSE_BRIDGE._BuildAuftragSnapshotItem
function MOOSE_BRIDGE:_BuildAuftragSnapshotItem(auftrag, source)
local item = _moose_bridge_base_build_auftrag_snapshot_item(self, auftrag, source)
if type(item) ~= "table" or type(auftrag) ~= "table" then return item end
local summary = bridge_auftrag_summary(auftrag.summary)
item.d_tevaluate = auftrag.dTevaluate
item.ready_to_evaluate = bridge_auftrag_ready_to_evaluate(auftrag)
item.summary_available = summary ~= nil
item.summary = summary
return item
end
function MOOSE_BRIDGE:_CollectAuftragCandidatesFromLegion(result, seen, legion)
if type(legion) ~= "table" then return end
local queues = {
legion.missionqueue,
legion.missions,
legion.auftraege,
legion.missionQueue,
}
for _, queue in ipairs(queues) do
if type(queue) == "table" then
for _, auftrag in pairs(queue) do self:_AddAuftragCandidate(result, seen, auftrag, "legion.missionqueue") end
end
end
end
function MOOSE_BRIDGE:_CollectAuftragCandidatesFromTracked(result, seen)
if type(self.TrackedAuftraege) ~= "table" then return end
for object_id, auftrag in pairs(self.TrackedAuftraege) do
if type(auftrag) == "table" then
self:_AddAuftragCandidate(result, seen, auftrag, "bridge.tracked")
else
self.TrackedAuftraege[object_id] = nil
end
end
end
function MOOSE_BRIDGE:_CollectAuftragCandidatesFromCommanders(result, seen)
for _, commander in pairs(self.RegisteredCommanders or {}) do
if type(commander) == "table" and type(commander.missionqueue) == "table" then
for _, auftrag in pairs(commander.missionqueue) do
self:_AddAuftragCandidate(result, seen, auftrag, "commander.missionqueue")
end
end
end
end
local _moose_bridge_base_build_auftrag_snapshot = MOOSE_BRIDGE.BuildAuftragSnapshot
function MOOSE_BRIDGE:BuildAuftragSnapshot()
local result = _moose_bridge_base_build_auftrag_snapshot(self) or {}
local seen = {}
for _, item in ipairs(result) do
if item.object_id then seen[item.object_id] = true end
end
if _DATABASE and type(_DATABASE.LEGIONS) == "table" then
for _, legion in pairs(_DATABASE.LEGIONS) do
self:_CollectAuftragCandidatesFromLegion(result, seen, legion)
end
end
self:_CollectAuftragCandidatesFromCommanders(result, seen)
self:_CollectAuftragCandidatesFromTracked(result, seen)
return result
end
local _moose_bridge_base_register_default_commands = MOOSE_BRIDGE.RegisterDefaultCommands
function MOOSE_BRIDGE:RegisterDefaultCommands()
_moose_bridge_base_register_default_commands(self)
self:RegisterAuftragExecutionCommands()
end