mirror of
https://github.com/iTracerFacer/DCS_MissionDev.git
synced 2025-12-03 04:14:46 +00:00
First run of it working and spawning a crate! ha! suck on that mist
This commit is contained in:
parent
9affd7a583
commit
b799e83283
@ -8,13 +8,53 @@
|
||||
-- Outputs: F10 menus for helo/transport groups; crate spawning/building; troop load/unload; optional JTAC hookup (via FAC module);
|
||||
-- Error modes: missing Moose -> abort; unknown crate key -> message; spawn blocked in enemy airbase; zone missing -> message.
|
||||
|
||||
if not _G.Moose or not _G.BASE then
|
||||
env.info('[Moose_CTLD] Moose not detected (BASE class missing). Ensure Moose.lua is loaded before Moose_CTLD.lua')
|
||||
if not _G.BASE then
|
||||
env.info('[Moose_CTLD] Moose (BASE) not detected. Ensure Moose.lua is loaded before Moose_CTLD.lua')
|
||||
end
|
||||
|
||||
local CTLD = {}
|
||||
CTLD.__index = CTLD
|
||||
|
||||
-- Safe deep copy: prefer MOOSE UTILS.DeepCopy when available; fallback to Lua implementation
|
||||
local function _deepcopy_fallback(obj, seen)
|
||||
if type(obj) ~= 'table' then return obj end
|
||||
seen = seen or {}
|
||||
if seen[obj] then return seen[obj] end
|
||||
local res = {}
|
||||
seen[obj] = res
|
||||
for k, v in pairs(obj) do
|
||||
res[_deepcopy_fallback(k, seen)] = _deepcopy_fallback(v, seen)
|
||||
end
|
||||
local mt = getmetatable(obj)
|
||||
if mt then setmetatable(res, mt) end
|
||||
return res
|
||||
end
|
||||
|
||||
local function DeepCopy(obj)
|
||||
if _G.UTILS and type(UTILS.DeepCopy) == 'function' then
|
||||
return UTILS.DeepCopy(obj)
|
||||
end
|
||||
return _deepcopy_fallback(obj)
|
||||
end
|
||||
|
||||
-- Deep-merge src into dst (recursively). Arrays/lists in src replace dst.
|
||||
local function DeepMerge(dst, src)
|
||||
if type(dst) ~= 'table' or type(src) ~= 'table' then return src end
|
||||
for k, v in pairs(src) do
|
||||
if type(v) == 'table' then
|
||||
local isArray = (rawget(v, 1) ~= nil) -- simple heuristic
|
||||
if isArray then
|
||||
dst[k] = DeepCopy(v)
|
||||
else
|
||||
dst[k] = DeepMerge(dst[k] or {}, v)
|
||||
end
|
||||
else
|
||||
dst[k] = v
|
||||
end
|
||||
end
|
||||
return dst
|
||||
end
|
||||
|
||||
-- =========================
|
||||
-- Defaults and State
|
||||
-- =========================
|
||||
@ -54,6 +94,7 @@ CTLD.Messages = {
|
||||
-- Crates
|
||||
crate_spawn_requested = "Request received—spawning {type} crate at {zone}.",
|
||||
pickup_zone_required = "Move within {zone_dist} {zone_dist_u} of a Supply Zone to request crates.",
|
||||
no_pickup_zones = "No Pickup Zones are configured for this coalition. Ask the mission maker to add supply zones or disable the pickup zone requirement.",
|
||||
crate_re_marked = "Re-marking crate {id} with {mark}.",
|
||||
crate_expired = "Crate {id} expired and was removed.",
|
||||
crate_max_capacity = "Max load reached ({total}). Drop or build before picking up more.",
|
||||
@ -290,12 +331,34 @@ end
|
||||
|
||||
local function _nearestZonePoint(unit, list)
|
||||
if not unit or not unit:IsAlive() then return nil end
|
||||
local p3 = unit:GetPointVec3()
|
||||
-- Get unit position using DCS API to avoid dependency on MOOSE point methods
|
||||
local uname = unit:GetName()
|
||||
local du = Unit.getByName and Unit.getByName(uname) or nil
|
||||
if not du or not du:getPoint() then return nil end
|
||||
local up = du:getPoint()
|
||||
local ux, uz = up.x, up.z
|
||||
|
||||
local best, bestd = nil, 1e12
|
||||
for _,z in ipairs(list or {}) do
|
||||
for _, z in ipairs(list or {}) do
|
||||
local mz = _findZone(z)
|
||||
if mz then
|
||||
local d = p3:DistanceFromPoint(mz:GetPointVec3())
|
||||
local zx, zz
|
||||
if z and z.name and trigger and trigger.misc and trigger.misc.getZone then
|
||||
local tz = trigger.misc.getZone(z.name)
|
||||
if tz and tz.point then zx, zz = tz.point.x, tz.point.z end
|
||||
end
|
||||
if (not zx) and mz and mz.GetPointVec3 then
|
||||
local zp = mz:GetPointVec3()
|
||||
-- Try to read numeric fields directly to avoid method calls
|
||||
if zp and type(zp) == 'table' and zp.x and zp.z then zx, zz = zp.x, zp.z end
|
||||
end
|
||||
if (not zx) and z and z.coord then
|
||||
zx, zz = z.coord.x, z.coord.z
|
||||
end
|
||||
|
||||
if zx and zz then
|
||||
local dx = (zx - ux)
|
||||
local dz = (zz - uz)
|
||||
local d = math.sqrt(dx*dx + dz*dz)
|
||||
if d < bestd then best, bestd = mz, d end
|
||||
end
|
||||
end
|
||||
@ -381,9 +444,11 @@ end
|
||||
|
||||
local function _fmtTemplate(tpl, data)
|
||||
if not tpl or tpl == '' then return '' end
|
||||
return (tpl:gsub('{(%w+)}', function(k)
|
||||
-- Support placeholder keys with underscores (e.g., {zone_dist_u})
|
||||
return (tpl:gsub('{([%w_]+)}', function(k)
|
||||
local v = data and data[k]
|
||||
if v == nil then return '' end
|
||||
-- If value is missing, leave placeholder intact to aid debugging
|
||||
if v == nil then return '{'..k..'}' end
|
||||
return tostring(v)
|
||||
end))
|
||||
end
|
||||
@ -487,8 +552,8 @@ end
|
||||
-- #region Construction
|
||||
function CTLD:New(cfg)
|
||||
local o = setmetatable({}, self)
|
||||
o.Config = BASE:DeepCopy(CTLD.Config)
|
||||
if cfg then o.Config = BASE:Inherit(o.Config, cfg) end
|
||||
o.Config = DeepCopy(CTLD.Config)
|
||||
if cfg then o.Config = DeepMerge(o.Config, cfg) end
|
||||
o.Side = o.Config.CoalitionSide
|
||||
o.MenuRoots = {}
|
||||
o.MenusByGroup = {}
|
||||
@ -511,6 +576,8 @@ function CTLD:New(cfg)
|
||||
end
|
||||
end
|
||||
o:InitZones()
|
||||
-- Validate configured zones and warn if missing
|
||||
o:ValidateZones()
|
||||
o:InitMenus()
|
||||
|
||||
-- Periodic cleanup for crates
|
||||
@ -555,6 +622,95 @@ function CTLD:InitZones()
|
||||
if mz then table.insert(self.FOBZones, mz); self._ZoneDefs.FOBZones[mz:GetName()] = z end
|
||||
end
|
||||
end
|
||||
|
||||
-- Validate configured zone names exist in the mission; warn coalition if any are missing.
|
||||
function CTLD:ValidateZones()
|
||||
local function zoneExistsByName(name)
|
||||
if not name or name == '' then return false end
|
||||
if trigger and trigger.misc and trigger.misc.getZone then
|
||||
local z = trigger.misc.getZone(name)
|
||||
if z then return true end
|
||||
end
|
||||
if ZONE and ZONE.FindByName then
|
||||
local mz = ZONE:FindByName(name)
|
||||
if mz then return true end
|
||||
end
|
||||
return false
|
||||
end
|
||||
|
||||
local function sideToStr(s)
|
||||
if coalition and coalition.side then
|
||||
if s == coalition.side.BLUE then return 'BLUE' end
|
||||
if s == coalition.side.RED then return 'RED' end
|
||||
if s == coalition.side.NEUTRAL then return 'NEUTRAL' end
|
||||
end
|
||||
return tostring(s)
|
||||
end
|
||||
|
||||
local function join(t)
|
||||
local s = ''
|
||||
for i,name in ipairs(t) do s = s .. (i>1 and ', ' or '') .. tostring(name) end
|
||||
return s
|
||||
end
|
||||
|
||||
local missing = { Pickup = {}, Drop = {}, FOB = {} }
|
||||
local found = { Pickup = {}, Drop = {}, FOB = {} }
|
||||
local coords = { Pickup = 0, Drop = 0, FOB = 0 }
|
||||
|
||||
for _,z in ipairs(self.Config.Zones.PickupZones or {}) do
|
||||
if z.name then
|
||||
if zoneExistsByName(z.name) then table.insert(found.Pickup, z.name) else table.insert(missing.Pickup, z.name) end
|
||||
elseif z.coord then
|
||||
coords.Pickup = coords.Pickup + 1
|
||||
end
|
||||
end
|
||||
for _,z in ipairs(self.Config.Zones.DropZones or {}) do
|
||||
if z.name then
|
||||
if zoneExistsByName(z.name) then table.insert(found.Drop, z.name) else table.insert(missing.Drop, z.name) end
|
||||
elseif z.coord then
|
||||
coords.Drop = coords.Drop + 1
|
||||
end
|
||||
end
|
||||
for _,z in ipairs(self.Config.Zones.FOBZones or {}) do
|
||||
if z.name then
|
||||
if zoneExistsByName(z.name) then table.insert(found.FOB, z.name) else table.insert(missing.FOB, z.name) end
|
||||
elseif z.coord then
|
||||
coords.FOB = coords.FOB + 1
|
||||
end
|
||||
end
|
||||
|
||||
-- Log a concise summary to dcs.log
|
||||
local sideStr = sideToStr(self.Side)
|
||||
env.info(string.format('[Moose_CTLD][ZoneValidation][%s] Pickup: configured=%d (named=%d, coord=%d) found=%d missing=%d',
|
||||
sideStr,
|
||||
#(self.Config.Zones.PickupZones or {}), #found.Pickup + #missing.Pickup, coords.Pickup, #found.Pickup, #missing.Pickup))
|
||||
env.info(string.format('[Moose_CTLD][ZoneValidation][%s] Drop : configured=%d (named=%d, coord=%d) found=%d missing=%d',
|
||||
sideStr,
|
||||
#(self.Config.Zones.DropZones or {}), #found.Drop + #missing.Drop, coords.Drop, #found.Drop, #missing.Drop))
|
||||
env.info(string.format('[Moose_CTLD][ZoneValidation][%s] FOB : configured=%d (named=%d, coord=%d) found=%d missing=%d',
|
||||
sideStr,
|
||||
#(self.Config.Zones.FOBZones or {}), #found.FOB + #missing.FOB, coords.FOB, #found.FOB, #missing.FOB))
|
||||
|
||||
local anyMissing = (#missing.Pickup > 0) or (#missing.Drop > 0) or (#missing.FOB > 0)
|
||||
if anyMissing then
|
||||
if #missing.Pickup > 0 then
|
||||
local msg = 'CTLD config warning: Missing Pickup Zones: '..join(missing.Pickup)
|
||||
_msgCoalition(self.Side, msg); env.info('[Moose_CTLD][ZoneValidation]['..sideStr..'] '..msg)
|
||||
end
|
||||
if #missing.Drop > 0 then
|
||||
local msg = 'CTLD config warning: Missing Drop Zones: '..join(missing.Drop)
|
||||
_msgCoalition(self.Side, msg); env.info('[Moose_CTLD][ZoneValidation]['..sideStr..'] '..msg)
|
||||
end
|
||||
if #missing.FOB > 0 then
|
||||
local msg = 'CTLD config warning: Missing FOB Zones: '..join(missing.FOB)
|
||||
_msgCoalition(self.Side, msg); env.info('[Moose_CTLD][ZoneValidation]['..sideStr..'] '..msg)
|
||||
end
|
||||
else
|
||||
env.info(string.format('[Moose_CTLD][ZoneValidation][%s] All configured zone names resolved successfully.', sideStr))
|
||||
end
|
||||
|
||||
self._MissingZones = missing
|
||||
end
|
||||
-- #endregion Construction
|
||||
|
||||
-- =========================
|
||||
@ -729,10 +885,18 @@ function CTLD:RequestCrateForGroup(group, crateKey)
|
||||
local unit = group:GetUnit(1)
|
||||
if not unit or not unit:IsAlive() then return end
|
||||
local zone, dist = _nearestZonePoint(unit, self.Config.Zones.PickupZones)
|
||||
local hasPickupZones = (self.PickupZones and #self.PickupZones > 0) or (self.Config.Zones and self.Config.Zones.PickupZones and #self.Config.Zones.PickupZones > 0)
|
||||
local spawnPoint
|
||||
local maxd = (self.Config.PickupZoneMaxDistance or 10000)
|
||||
-- Announce request
|
||||
_eventSend(self, group, nil, 'crate_spawn_requested', { type = tostring(crateKey), zone = zone and zone:GetName() or 'nearest zone' })
|
||||
local zoneName = zone and zone:GetName() or (hasPickupZones and 'nearest zone' or 'NO PICKUP ZONES CONFIGURED')
|
||||
_eventSend(self, group, nil, 'crate_spawn_requested', { type = tostring(crateKey), zone = zoneName })
|
||||
|
||||
if not hasPickupZones and self.Config.RequirePickupZoneForCrateRequest then
|
||||
_eventSend(self, group, nil, 'no_pickup_zones', {})
|
||||
return
|
||||
end
|
||||
|
||||
if zone and dist <= maxd then
|
||||
spawnPoint = zone:GetPointVec3()
|
||||
-- if pickup zone has smoke configured, mark it
|
||||
@ -976,7 +1140,7 @@ function CTLD:DropLoadedCrates(group, howMany)
|
||||
local toDrop = math.min(requested, initialTotal)
|
||||
_eventSend(self, group, nil, 'drop_initiated', { count = toDrop })
|
||||
-- Drop in key order
|
||||
for k,count in pairs(BASE:DeepCopy(lc.byKey)) do
|
||||
for k,count in pairs(DeepCopy(lc.byKey)) do
|
||||
if toDrop <= 0 then break end
|
||||
local dropNow = math.min(count, toDrop)
|
||||
for i=1,dropNow do
|
||||
@ -1258,54 +1422,61 @@ function CTLD:AutoBuildFOBCheck()
|
||||
local filtered = {}
|
||||
for _,c in ipairs(nearby) do if c.meta.side == self.Side then table.insert(filtered, c) end end
|
||||
nearby = filtered
|
||||
if #nearby == 0 then goto continue end
|
||||
|
||||
local counts = {}
|
||||
for _,c in ipairs(nearby) do counts[c.meta.key] = (counts[c.meta.key] or 0) + 1 end
|
||||
if #nearby > 0 then
|
||||
local counts = {}
|
||||
for _,c in ipairs(nearby) do counts[c.meta.key] = (counts[c.meta.key] or 0) + 1 end
|
||||
|
||||
local function consumeCrates(key, qty)
|
||||
local removed = 0
|
||||
for _,c in ipairs(nearby) do
|
||||
if removed >= qty then break end
|
||||
if c.meta.key == key then
|
||||
local obj = StaticObject.getByName(c.name)
|
||||
if obj then obj:destroy() end
|
||||
CTLD._crates[c.name] = nil
|
||||
removed = removed + 1
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
-- Prefer composite recipes
|
||||
for recipeKey,cat in pairs(fobDefs) do
|
||||
if type(cat.requires) == 'table' then
|
||||
local ok = true
|
||||
for reqKey,qty in pairs(cat.requires) do if (counts[reqKey] or 0) < qty then ok = false; break end end
|
||||
if ok then
|
||||
local gdata = cat.build({ x = center.x, z = center.z }, 0, cat.side or self.Side)
|
||||
local g = _coalitionAddGroup(cat.side or self.Side, cat.category or Group.Category.GROUND, gdata)
|
||||
if g then
|
||||
for reqKey,qty in pairs(cat.requires) do consumeCrates(reqKey, qty) end
|
||||
_msgCoalition(self.Side, string.format('FOB auto-built at %s', zone:GetName()))
|
||||
goto continue -- move to next zone; avoid multiple builds per tick
|
||||
local function consumeCrates(key, qty)
|
||||
local removed = 0
|
||||
for _,c in ipairs(nearby) do
|
||||
if removed >= qty then break end
|
||||
if c.meta.key == key then
|
||||
local obj = StaticObject.getByName(c.name)
|
||||
if obj then obj:destroy() end
|
||||
CTLD._crates[c.name] = nil
|
||||
removed = removed + 1
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
-- Then single-key FOB recipes
|
||||
for key,cat in pairs(fobDefs) do
|
||||
if not cat.requires and (counts[key] or 0) >= (cat.required or 1) then
|
||||
local gdata = cat.build({ x = center.x, z = center.z }, 0, cat.side or self.Side)
|
||||
local g = _coalitionAddGroup(cat.side or self.Side, cat.category or Group.Category.GROUND, gdata)
|
||||
if g then
|
||||
consumeCrates(key, cat.required or 1)
|
||||
_msgCoalition(self.Side, string.format('FOB auto-built at %s', zone:GetName()))
|
||||
goto continue
|
||||
|
||||
local built = false
|
||||
|
||||
-- Prefer composite recipes
|
||||
for recipeKey,cat in pairs(fobDefs) do
|
||||
if type(cat.requires) == 'table' then
|
||||
local ok = true
|
||||
for reqKey,qty in pairs(cat.requires) do if (counts[reqKey] or 0) < qty then ok = false; break end end
|
||||
if ok then
|
||||
local gdata = cat.build({ x = center.x, z = center.z }, 0, cat.side or self.Side)
|
||||
local g = _coalitionAddGroup(cat.side or self.Side, cat.category or Group.Category.GROUND, gdata)
|
||||
if g then
|
||||
for reqKey,qty in pairs(cat.requires) do consumeCrates(reqKey, qty) end
|
||||
_msgCoalition(self.Side, string.format('FOB auto-built at %s', zone:GetName()))
|
||||
built = true
|
||||
break -- move to next zone; avoid multiple builds per tick
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
::continue::
|
||||
-- Then single-key FOB recipes
|
||||
if not built then
|
||||
for key,cat in pairs(fobDefs) do
|
||||
if not cat.requires and (counts[key] or 0) >= (cat.required or 1) then
|
||||
local gdata = cat.build({ x = center.x, z = center.z }, 0, cat.side or self.Side)
|
||||
local g = _coalitionAddGroup(cat.side or self.Side, cat.category or Group.Category.GROUND, gdata)
|
||||
if g then
|
||||
consumeCrates(key, cat.required or 1)
|
||||
_msgCoalition(self.Side, string.format('FOB auto-built at %s', zone:GetName()))
|
||||
built = true
|
||||
break
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
-- next zone iteration continues automatically
|
||||
end
|
||||
end
|
||||
end
|
||||
-- #endregion Auto-build FOB in zones
|
||||
@ -1333,7 +1504,7 @@ function CTLD:AddDropZone(z)
|
||||
end
|
||||
|
||||
function CTLD:SetAllowedAircraft(list)
|
||||
self.Config.AllowedAircraft = BASE:DeepCopy(list)
|
||||
self.Config.AllowedAircraft = DeepCopy(list)
|
||||
end
|
||||
-- #endregion Public helpers
|
||||
|
||||
|
||||
@ -24,14 +24,54 @@ Design notes
|
||||
are conservative approximations to avoid silent failures.
|
||||
]]
|
||||
|
||||
if not _G.Moose or not _G.BASE then
|
||||
env.info('[Moose_CTLD_FAC] Moose not detected. Ensure Moose.lua is loaded before this script.')
|
||||
if not _G.BASE then
|
||||
env.info('[Moose_CTLD_FAC] Moose (BASE) not detected. Ensure Moose.lua is loaded before this script.')
|
||||
end
|
||||
|
||||
local FAC = {}
|
||||
FAC.__index = FAC
|
||||
FAC.Version = '0.2.0'
|
||||
|
||||
-- Safe deep copy: prefer MOOSE UTILS.DeepCopy when available; fallback to Lua implementation
|
||||
local function _deepcopy_fallback(obj, seen)
|
||||
if type(obj) ~= 'table' then return obj end
|
||||
seen = seen or {}
|
||||
if seen[obj] then return seen[obj] end
|
||||
local res = {}
|
||||
seen[obj] = res
|
||||
for k, v in pairs(obj) do
|
||||
res[_deepcopy_fallback(k, seen)] = _deepcopy_fallback(v, seen)
|
||||
end
|
||||
local mt = getmetatable(obj)
|
||||
if mt then setmetatable(res, mt) end
|
||||
return res
|
||||
end
|
||||
|
||||
local function DeepCopy(obj)
|
||||
if _G.UTILS and type(UTILS.DeepCopy) == 'function' then
|
||||
return UTILS.DeepCopy(obj)
|
||||
end
|
||||
return _deepcopy_fallback(obj)
|
||||
end
|
||||
|
||||
-- Deep-merge src into dst (recursively). Arrays/lists in src replace dst.
|
||||
local function DeepMerge(dst, src)
|
||||
if type(dst) ~= 'table' or type(src) ~= 'table' then return src end
|
||||
for k, v in pairs(src) do
|
||||
if type(v) == 'table' then
|
||||
local isArray = (rawget(v, 1) ~= nil)
|
||||
if isArray then
|
||||
dst[k] = DeepCopy(v)
|
||||
else
|
||||
dst[k] = DeepMerge(dst[k] or {}, v)
|
||||
end
|
||||
else
|
||||
dst[k] = v
|
||||
end
|
||||
end
|
||||
return dst
|
||||
end
|
||||
|
||||
-- #region Config
|
||||
-- Configuration for FAC behavior and UI. Adjust defaults here or pass overrides to :New().
|
||||
FAC.Config = {
|
||||
@ -218,8 +258,8 @@ end
|
||||
function FAC:New(ctld, cfg)
|
||||
local o = setmetatable({}, self)
|
||||
o._ctld = ctld
|
||||
o.Config = BASE:DeepCopy(FAC.Config)
|
||||
if cfg then o.Config = BASE:Inherit(o.Config, cfg) end
|
||||
o.Config = DeepCopy(FAC.Config)
|
||||
if cfg then o.Config = DeepMerge(o.Config, cfg) end
|
||||
o.Side = o.Config.CoalitionSide
|
||||
o._zones = {}
|
||||
|
||||
@ -336,6 +376,13 @@ function FAC:RunZones(interval)
|
||||
end, {}, 5, interval or 20)
|
||||
end
|
||||
|
||||
-- Backwards-compatible Run() entry point used by init scripts
|
||||
function FAC:Run()
|
||||
-- Schedulers for menus/status are started in New(); here we can kick off zone scans if any zones exist.
|
||||
if #self._zones > 0 then self:RunZones() end
|
||||
return self
|
||||
end
|
||||
|
||||
function FAC:_scanZone(Z)
|
||||
-- Perform one detection update and mark contacts, with spatial de-duplication
|
||||
Z.Detector:DetectionUpdate()
|
||||
|
||||
BIN
Moose_CTLD_Pure/Moose_CTLD_Pure.miz
Normal file
BIN
Moose_CTLD_Pure/Moose_CTLD_Pure.miz
Normal file
Binary file not shown.
@ -12,7 +12,8 @@
|
||||
-- Adjust names below if you use different zone names.
|
||||
|
||||
|
||||
-- Create CTLD for BLUE
|
||||
-- Create CTLD instances only if Moose and CTLD are available
|
||||
if _MOOSE_CTLD and _G.BASE then
|
||||
ctldBlue = _MOOSE_CTLD:New({
|
||||
CoalitionSide = coalition.side.BLUE,
|
||||
PickupZoneSmokeColor = trigger.smokeColor.Blue,
|
||||
@ -21,7 +22,7 @@ ctldBlue = _MOOSE_CTLD:New({
|
||||
},
|
||||
|
||||
Zones = {
|
||||
PickupZones = { { name = 'PICKUP_BLUE_MAIN', smoke = trigger.smokeColor.Blue } },
|
||||
PickupZones = { { name = 'Blue_PickupZone_1', smoke = trigger.smokeColor.Blue } },
|
||||
--DropZones = { { name = 'DROP_BLUE_1' } },
|
||||
-- FOBZones = { { name = 'FOB_BLUE_A' } },
|
||||
},
|
||||
@ -37,71 +38,19 @@ ctldRed = _MOOSE_CTLD:New({
|
||||
},
|
||||
|
||||
Zones = {
|
||||
PickupZones = { { name = 'PICKUP_RED_MAIN', smoke = trigger.smokeColor.Red } },
|
||||
PickupZones = { { name = 'Red_PickupZone_1', smoke = trigger.smokeColor.Red } },
|
||||
--DropZones = { { name = 'DROP_RED_1' } },
|
||||
-- FOBZones = { { name = 'FOB_RED_A' } },
|
||||
},
|
||||
BuildRequiresGroundCrates = true,
|
||||
})
|
||||
else
|
||||
env.info('[init_mission_dual_coalition] Moose or CTLD missing; skipping CTLD init')
|
||||
end
|
||||
|
||||
-- If the external catalog was loaded (as _CTLD_EXTRACTED_CATALOG), both instances auto-merged it
|
||||
-- thanks to Moose_CTLD.lua. If you want to load it manually or from a different path, you can do:
|
||||
-- local extracted = dofile(lfs.writedir()..[[Scripts\Moose_CTLD_Pure\catalogs\CrateCatalog_CTLD_Extract.lua]])
|
||||
-- ctldBlue:MergeCatalog(extracted)
|
||||
-- ctldRed:MergeCatalog(extracted)
|
||||
|
||||
-- Optional: add a couple of small, side-specific examples if you don't use the big catalog
|
||||
-- (Uncomment to add a simple MANPADS and AAA on each side)
|
||||
-- ctldBlue:RegisterCrate('MANPADS', {
|
||||
-- menuCategory='Infantry', description='2x crates -> MANPADS (Stinger)', dcsCargoType='uh1h_cargo', required=2,
|
||||
-- side=coalition.side.BLUE, category=Group.Category.GROUND,
|
||||
-- build=function(point, headingDeg)
|
||||
-- local hdg = math.rad(headingDeg or 0)
|
||||
-- return { visible=false, lateActivation=false, tasks={}, task='Ground Nothing', route={}, name='CTLD_BP_MANPADS_'..math.random(1,999999),
|
||||
-- units={ { type='Soldier stinger', name='CTLD-Stinger-'..math.random(1,999999), x=point.x, y=point.z, heading=hdg } } }
|
||||
-- end,
|
||||
-- })
|
||||
-- ctldBlue:RegisterCrate('AAA', {
|
||||
-- menuCategory='AAA', description='3x crates -> ZU-23 site', dcsCargoType='container_cargo', required=3,
|
||||
-- side=coalition.side.BLUE, category=Group.Category.GROUND,
|
||||
-- build=function(point, headingDeg)
|
||||
-- local hdg = math.rad(headingDeg or 0)
|
||||
-- local function off(dx,dz) return { x=point.x+dx, z=point.z+dz } end
|
||||
-- local units={
|
||||
-- { type='ZU-23 Emplacement', name='CTLD-ZU23-'..math.random(1,999999), x=point.x, y=point.z, heading=hdg },
|
||||
-- { type='Ural-375', name='CTLD-TRK-'..math.random(1,999999), x=off(15,12).x, y=off(15,12).z, heading=hdg },
|
||||
-- { type='Infantry AK', name='CTLD-INF-'..math.random(1,999999), x=off(-12,-15).x, y=off(-12,-15).z, heading=hdg },
|
||||
-- }
|
||||
-- return { visible=false, lateActivation=false, tasks={}, task='Ground Nothing', units=units, route={}, name='CTLD_BP_AAA_'..math.random(1,999999) }
|
||||
-- end,
|
||||
-- })
|
||||
-- ctldRed:RegisterCrate('MANPADS', {
|
||||
-- menuCategory='Infantry', description='2x crates -> MANPADS (Igla)', dcsCargoType='uh1h_cargo', required=2,
|
||||
-- side=coalition.side.RED, category=Group.Category.GROUND,
|
||||
-- build=function(point, headingDeg)
|
||||
-- local hdg = math.rad(headingDeg or 0)
|
||||
-- -- Using generic infantry to avoid DCS type-name mismatches; replace with accurate manpad unit if desired
|
||||
-- return { visible=false, lateActivation=false, tasks={}, task='Ground Nothing', route={}, name='CTLD_RP_MANPADS_'..math.random(1,999999),
|
||||
-- units={ { type='Infantry AK', name='CTLD-Igla-'..math.random(1,999999), x=point.x, y=point.z, heading=hdg } } }
|
||||
-- end,
|
||||
-- })
|
||||
-- ctldRed:RegisterCrate('AAA', {
|
||||
-- menuCategory='AAA', description='3x crates -> ZU-23 site', dcsCargoType='container_cargo', required=3,
|
||||
-- side=coalition.side.RED, category=Group.Category.GROUND,
|
||||
-- build=function(point, headingDeg)
|
||||
-- local hdg = math.rad(headingDeg or 0)
|
||||
-- local function off(dx,dz) return { x=point.x+dx, z=point.z+dz } end
|
||||
-- local units={
|
||||
-- { type='ZU-23 Emplacement', name='CTLD-ZU23-'..math.random(1,999999), x=point.x, y=point.z, heading=hdg },
|
||||
-- { type='Ural-375', name='CTLD-TRK-'..math.random(1,999999), x=off(15,12).x, y=off(15,12).z, heading=hdg },
|
||||
-- { type='Infantry AK', name='CTLD-INF-'..math.random(1,999999), x=off(-12,-15).x, y=off(-12,-15).z, heading=hdg },
|
||||
-- }
|
||||
-- return { visible=false, lateActivation=false, tasks={}, task='Ground Nothing', units=units, route={}, name='CTLD_RP_AAA_'..math.random(1,999999) }
|
||||
-- end,
|
||||
-- })
|
||||
|
||||
-- Optional: FAC/RECCE for both sides (requires Moose_CTLD_FAC.lua)
|
||||
if _MOOSE_CTLD_FAC then
|
||||
if _MOOSE_CTLD_FAC and _G.BASE and ctldBlue and ctldRed then
|
||||
facBlue = _MOOSE_CTLD_FAC:New(ctldBlue, {
|
||||
CoalitionSide = coalition.side.BLUE,
|
||||
Arty = { Enabled = false },
|
||||
@ -115,4 +64,6 @@ if _MOOSE_CTLD_FAC then
|
||||
})
|
||||
-- facRed:AddRecceZone({ name = 'RECCE_RED_1' })
|
||||
facRed:Run()
|
||||
else
|
||||
env.info('[init_mission_dual_coalition] FAC not initialized (missing Moose/CTLD/FAC or CTLD not created)')
|
||||
end
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user