mirror of
https://github.com/iTracerFacer/DCS_MissionDev.git
synced 2025-12-03 04:14:46 +00:00
Added support for dual coalition.
This commit is contained in:
@@ -28,6 +28,7 @@ CTLD.Config = {
|
|||||||
},
|
},
|
||||||
UseGroupMenus = true, -- if true, F10 menus per player group; otherwise coalition-wide
|
UseGroupMenus = true, -- if true, F10 menus per player group; otherwise coalition-wide
|
||||||
UseCategorySubmenus = true, -- if true, organize crate requests by category submenu (menuCategory)
|
UseCategorySubmenus = true, -- if true, organize crate requests by category submenu (menuCategory)
|
||||||
|
UseBuiltinCatalog = false, -- if false, starts with an empty catalog; intended when you preload a global catalog and want only that
|
||||||
RestrictFOBToZones = false, -- if true, recipes marked isFOB only build inside configured FOBZones
|
RestrictFOBToZones = false, -- if true, recipes marked isFOB only build inside configured FOBZones
|
||||||
AutoBuildFOBInZones = false, -- if true, CTLD auto-builds FOB recipes when required crates are inside a FOB zone
|
AutoBuildFOBInZones = false, -- if true, CTLD auto-builds FOB recipes when required crates are inside a FOB zone
|
||||||
BuildRadius = 60, -- meters around build point to collect crates
|
BuildRadius = 60, -- meters around build point to collect crates
|
||||||
@@ -38,10 +39,21 @@ CTLD.Config = {
|
|||||||
|
|
||||||
Zones = { -- Optional: supply by name (ME trigger zones) or define coordinates inline
|
Zones = { -- Optional: supply by name (ME trigger zones) or define coordinates inline
|
||||||
PickupZones = {
|
PickupZones = {
|
||||||
-- examples:
|
-- Examples:
|
||||||
-- { name = 'PICKUP_BLUE_MAIN' },
|
-- Create 5 trigger zones in the Mission Editor named exactly as below to get started quickly.
|
||||||
-- { name = 'Pickup-West', smoke = trigger.smokeColor.Green },
|
-- If you run separate CTLD instances for BLUE and RED, give each side its own set of uniquely named zones
|
||||||
-- { coord = { x = 12345, y = 0, z = 67890 }, radius = 150, name = 'ScriptPickup1' },
|
-- (recommended to avoid overlap). You can keep the simple "Pickup Zone #" pattern per side if you prefer,
|
||||||
|
-- just ensure the names in the ME match what you configure here.
|
||||||
|
--
|
||||||
|
-- Uncomment the lines you want to use:
|
||||||
|
-- { name = 'Pickup Zone 1', smoke = trigger.smokeColor.Green },
|
||||||
|
-- { name = 'Pickup Zone 2', smoke = trigger.smokeColor.Blue },
|
||||||
|
-- { name = 'Pickup Zone 3', smoke = trigger.smokeColor.Orange },
|
||||||
|
-- { name = 'Pickup Zone 4', smoke = trigger.smokeColor.White },
|
||||||
|
-- { name = 'Pickup Zone 5', smoke = trigger.smokeColor.Red },
|
||||||
|
--
|
||||||
|
-- Tip: You can also define zones purely in script (no ME zone needed):
|
||||||
|
-- { coord = { x = 12345, y = 0, z = 67890 }, radius = 150, name = 'Pickup Zone 1' },
|
||||||
},
|
},
|
||||||
DropZones = {
|
DropZones = {
|
||||||
-- { name = 'DROP_BLUE_1' },
|
-- { name = 'DROP_BLUE_1' },
|
||||||
@@ -92,7 +104,7 @@ CTLD.Config = {
|
|||||||
end,
|
end,
|
||||||
},
|
},
|
||||||
-- Example: FARP/FOB build from 4 crates (spawns helipads + support statics/vehicles)
|
-- Example: FARP/FOB build from 4 crates (spawns helipads + support statics/vehicles)
|
||||||
FOB = {
|
FOB = {
|
||||||
description = '4x Crates -> FARP/FOB',
|
description = '4x Crates -> FARP/FOB',
|
||||||
weight = 500,
|
weight = 500,
|
||||||
dcsCargoType = 'container_cargo',
|
dcsCargoType = 'container_cargo',
|
||||||
@@ -100,7 +112,7 @@ CTLD.Config = {
|
|||||||
isFOB = true, -- mark as FOB recipe for zone restrictions/auto-build
|
isFOB = true, -- mark as FOB recipe for zone restrictions/auto-build
|
||||||
side = coalition.side.BLUE,
|
side = coalition.side.BLUE,
|
||||||
category = Group.Category.GROUND,
|
category = Group.Category.GROUND,
|
||||||
build = function(point, headingDeg)
|
build = function(point, headingDeg, spawnSide)
|
||||||
local heading = math.rad(headingDeg or 0)
|
local heading = math.rad(headingDeg or 0)
|
||||||
-- Spawn statics that provide FARP services
|
-- Spawn statics that provide FARP services
|
||||||
local function addStatic(typeName, dx, dz, nameSuffix)
|
local function addStatic(typeName, dx, dz, nameSuffix)
|
||||||
@@ -111,7 +123,7 @@ CTLD.Config = {
|
|||||||
x = p.x, y = p.z,
|
x = p.x, y = p.z,
|
||||||
heading = heading,
|
heading = heading,
|
||||||
}
|
}
|
||||||
coalition.addStaticObject(coalition.side.BLUE, st)
|
coalition.addStaticObject(spawnSide or coalition.side.BLUE, st)
|
||||||
end
|
end
|
||||||
-- Common FARP layout
|
-- Common FARP layout
|
||||||
addStatic('FARP', 0, 0, 'PAD')
|
addStatic('FARP', 0, 0, 'PAD')
|
||||||
@@ -208,6 +220,33 @@ local function _vec3FromUnit(unit)
|
|||||||
return { x = p.x, y = p.y, z = p.z }
|
return { x = p.x, y = p.y, z = p.z }
|
||||||
end
|
end
|
||||||
|
|
||||||
|
-- Determine an approximate radius for a ZONE. Tries MOOSE radius, then trigger zone radius, then configured radius.
|
||||||
|
function CTLD:_getZoneRadius(zone)
|
||||||
|
if zone and zone.Radius then return zone.Radius end
|
||||||
|
local name = zone and zone.GetName and zone:GetName() or nil
|
||||||
|
if name and trigger and trigger.misc and trigger.misc.getZone then
|
||||||
|
local z = trigger.misc.getZone(name)
|
||||||
|
if z and z.radius then return z.radius end
|
||||||
|
end
|
||||||
|
if name and self._ZoneDefs and self._ZoneDefs.FOBZones and self._ZoneDefs.FOBZones[name] then
|
||||||
|
local d = self._ZoneDefs.FOBZones[name]
|
||||||
|
if d and d.radius then return d.radius end
|
||||||
|
end
|
||||||
|
return 150
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Check if a 2D point (x,z) lies within any FOB zone; returns (bool, zone)
|
||||||
|
function CTLD:IsPointInFOBZones(point)
|
||||||
|
for _,z in ipairs(self.FOBZones or {}) do
|
||||||
|
local pz = z:GetPointVec3()
|
||||||
|
local r = self:_getZoneRadius(z)
|
||||||
|
local dx = (pz.x - point.x)
|
||||||
|
local dz = (pz.z - point.z)
|
||||||
|
if (dx*dx + dz*dz) <= (r*r) then return true, z end
|
||||||
|
end
|
||||||
|
return false, nil
|
||||||
|
end
|
||||||
|
|
||||||
-- =========================
|
-- =========================
|
||||||
-- Construction
|
-- Construction
|
||||||
-- =========================
|
-- =========================
|
||||||
@@ -218,6 +257,24 @@ function CTLD:New(cfg)
|
|||||||
o.Side = o.Config.CoalitionSide
|
o.Side = o.Config.CoalitionSide
|
||||||
o.MenuRoots = {}
|
o.MenuRoots = {}
|
||||||
o.MenusByGroup = {}
|
o.MenusByGroup = {}
|
||||||
|
|
||||||
|
-- If caller disabled builtin catalog, clear it before merging any globals
|
||||||
|
if o.Config.UseBuiltinCatalog == false then
|
||||||
|
o.Config.CrateCatalog = {}
|
||||||
|
end
|
||||||
|
|
||||||
|
-- If a global catalog was loaded earlier (via DO SCRIPT FILE), merge it automatically
|
||||||
|
-- Supported globals: _CTLD_EXTRACTED_CATALOG (our extractor), CTLD_CATALOG, MOOSE_CTLD_CATALOG
|
||||||
|
do
|
||||||
|
local globalsToCheck = { '_CTLD_EXTRACTED_CATALOG', 'CTLD_CATALOG', 'MOOSE_CTLD_CATALOG' }
|
||||||
|
for _,gn in ipairs(globalsToCheck) do
|
||||||
|
local t = rawget(_G, gn)
|
||||||
|
if type(t) == 'table' then
|
||||||
|
o:MergeCatalog(t)
|
||||||
|
if o.Config.Debug then env.info('[Moose_CTLD] Merged crate catalog from global '..gn) end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
o:InitZones()
|
o:InitZones()
|
||||||
o:InitMenus()
|
o:InitMenus()
|
||||||
|
|
||||||
@@ -415,6 +472,12 @@ function CTLD:BuildAtGroup(group)
|
|||||||
local here = { x = p.x, z = p.z }
|
local here = { x = p.x, z = p.z }
|
||||||
local radius = self.Config.BuildRadius
|
local radius = self.Config.BuildRadius
|
||||||
local nearby = self:GetNearbyCrates(here, radius)
|
local nearby = self:GetNearbyCrates(here, radius)
|
||||||
|
-- filter crates to coalition side for this CTLD instance
|
||||||
|
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 _msgGroup(group, 'No crates within '..radius..'m') return end
|
if #nearby == 0 then _msgGroup(group, 'No crates within '..radius..'m') return end
|
||||||
|
|
||||||
-- Count by key
|
-- Count by key
|
||||||
@@ -437,24 +500,30 @@ function CTLD:BuildAtGroup(group)
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
local insideFOBZone, fz = self:IsPointInFOBZones(here)
|
||||||
|
local fobBlocked = false
|
||||||
-- Try composite recipes first (requires is a map of key->qty)
|
-- Try composite recipes first (requires is a map of key->qty)
|
||||||
for recipeKey,cat in pairs(self.Config.CrateCatalog) do
|
for recipeKey,cat in pairs(self.Config.CrateCatalog) do
|
||||||
if type(cat.requires) == 'table' and cat.build then
|
if type(cat.requires) == 'table' and cat.build then
|
||||||
local ok = true
|
if cat.isFOB and self.Config.RestrictFOBToZones and not insideFOBZone then
|
||||||
for reqKey,qty in pairs(cat.requires) do
|
fobBlocked = true
|
||||||
if (counts[reqKey] or 0) < qty then ok = false; break end
|
else
|
||||||
end
|
local ok = true
|
||||||
if ok then
|
for reqKey,qty in pairs(cat.requires) do
|
||||||
local hdg = unit:GetHeading()
|
if (counts[reqKey] or 0) < qty then ok = false; break end
|
||||||
local gdata = cat.build({ x = here.x, z = here.z }, math.deg(hdg))
|
end
|
||||||
local g = _coalitionAddGroup(cat.side or self.Side, cat.category or Group.Category.GROUND, gdata)
|
if ok then
|
||||||
if g then
|
local hdg = unit:GetHeading()
|
||||||
for reqKey,qty in pairs(cat.requires) do consumeCrates(reqKey, qty) end
|
local gdata = cat.build({ x = here.x, z = here.z }, math.deg(hdg), cat.side or self.Side)
|
||||||
_msgGroup(group, string.format('Built %s at your location', cat.description or recipeKey))
|
local g = _coalitionAddGroup(cat.side or self.Side, cat.category or Group.Category.GROUND, gdata)
|
||||||
return
|
if g then
|
||||||
else
|
for reqKey,qty in pairs(cat.requires) do consumeCrates(reqKey, qty) end
|
||||||
_msgGroup(group, 'Build failed: DCS group spawn error')
|
_msgGroup(group, string.format('Built %s at your location', cat.description or recipeKey))
|
||||||
return
|
return
|
||||||
|
else
|
||||||
|
_msgGroup(group, 'Build failed: DCS group spawn error')
|
||||||
|
return
|
||||||
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
@@ -464,20 +533,29 @@ function CTLD:BuildAtGroup(group)
|
|||||||
for key,count in pairs(counts) do
|
for key,count in pairs(counts) do
|
||||||
local cat = self.Config.CrateCatalog[key]
|
local cat = self.Config.CrateCatalog[key]
|
||||||
if cat and cat.build and (not cat.requires) and count >= (cat.required or 1) then
|
if cat and cat.build and (not cat.requires) and count >= (cat.required or 1) then
|
||||||
local hdg = unit:GetHeading()
|
if cat.isFOB and self.Config.RestrictFOBToZones and not insideFOBZone then
|
||||||
local gdata = cat.build({ x = here.x, z = here.z }, math.deg(hdg))
|
fobBlocked = true
|
||||||
local g = _coalitionAddGroup(cat.side or self.Side, cat.category or Group.Category.GROUND, gdata)
|
|
||||||
if g then
|
|
||||||
consumeCrates(key, cat.required or 1)
|
|
||||||
_msgGroup(group, string.format('Built %s at your location', cat.description or key))
|
|
||||||
return
|
|
||||||
else
|
else
|
||||||
_msgGroup(group, 'Build failed: DCS group spawn error')
|
local hdg = unit:GetHeading()
|
||||||
return
|
local gdata = cat.build({ x = here.x, z = here.z }, math.deg(hdg), 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)
|
||||||
|
_msgGroup(group, string.format('Built %s at your location', cat.description or key))
|
||||||
|
return
|
||||||
|
else
|
||||||
|
_msgGroup(group, 'Build failed: DCS group spawn error')
|
||||||
|
return
|
||||||
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
if fobBlocked then
|
||||||
|
_msgGroup(group, 'FOB building is restricted to designated FOB zones. Move inside a FOB zone to build.')
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
_msgGroup(group, 'Insufficient crates to build any asset here')
|
_msgGroup(group, 'Insufficient crates to build any asset here')
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -530,6 +608,78 @@ function CTLD:UnloadTroops(group)
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
-- =========================
|
||||||
|
-- Public helpers
|
||||||
|
-- =========================
|
||||||
|
-- =========================
|
||||||
|
-- Auto-build FOB in zones
|
||||||
|
-- =========================
|
||||||
|
function CTLD:AutoBuildFOBCheck()
|
||||||
|
if not (self.FOBZones and #self.FOBZones > 0) then return end
|
||||||
|
-- Find any FOB recipe definitions
|
||||||
|
local fobDefs = {}
|
||||||
|
for key,def in pairs(self.Config.CrateCatalog) do if def.isFOB and def.build then fobDefs[key] = def end end
|
||||||
|
if next(fobDefs) == nil then return end
|
||||||
|
|
||||||
|
for _,zone in ipairs(self.FOBZones) do
|
||||||
|
local center = zone:GetPointVec3()
|
||||||
|
local radius = self:_getZoneRadius(zone)
|
||||||
|
local nearby = self:GetNearbyCrates({ x = center.x, z = center.z }, radius)
|
||||||
|
-- filter to this coalition side
|
||||||
|
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
|
||||||
|
|
||||||
|
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
|
||||||
|
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
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
::continue::
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
-- =========================
|
-- =========================
|
||||||
-- Public helpers
|
-- Public helpers
|
||||||
-- =========================
|
-- =========================
|
||||||
|
|||||||
@@ -10,9 +10,10 @@ Pure-MOOSE CTLD-style logistics and FAC/RECCE without MIST or mission editor tem
|
|||||||
|
|
||||||
## Quick start
|
## Quick start
|
||||||
|
|
||||||
1) Load `Moose.lua` first, then include these files (order matters):
|
1) Load `Moose.lua` first, then include these files (order matters). Easiest path: load the crate catalog first so CTLD auto-detects it.
|
||||||
- `Moose_CTLD_Pure/Moose_CTLD.lua`
|
- `Moose_CTLD_Pure/catalogs/CrateCatalog_CTLD_Extract.lua` (sets `_CTLD_EXTRACTED_CATALOG`)
|
||||||
- `Moose_CTLD_Pure/Moose_CTLD_FAC.lua` (optional, for FAC/RECCE)
|
- `Moose_CTLD_Pure/Moose_CTLD.lua`
|
||||||
|
- `Moose_CTLD_Pure/Moose_CTLD_FAC.lua` (optional, for FAC/RECCE)
|
||||||
|
|
||||||
2) Initialize CTLD with minimal config:
|
2) Initialize CTLD with minimal config:
|
||||||
|
|
||||||
@@ -20,15 +21,16 @@ Pure-MOOSE CTLD-style logistics and FAC/RECCE without MIST or mission editor tem
|
|||||||
local CTLD = dofile(lfs.writedir()..[[Scripts\Moose_CTLD_Pure\Moose_CTLD.lua]])
|
local CTLD = dofile(lfs.writedir()..[[Scripts\Moose_CTLD_Pure\Moose_CTLD.lua]])
|
||||||
local ctld = CTLD:New({
|
local ctld = CTLD:New({
|
||||||
CoalitionSide = coalition.side.BLUE,
|
CoalitionSide = coalition.side.BLUE,
|
||||||
|
-- If you want to rely ONLY on the external catalog, disable built-ins
|
||||||
|
-- UseBuiltinCatalog = false,
|
||||||
Zones = {
|
Zones = {
|
||||||
PickupZones = { { name = 'PICKUP_BLUE_MAIN' } },
|
PickupZones = { { name = 'PICKUP_BLUE_MAIN' } },
|
||||||
DropZones = { { name = 'DROP_BLUE_1' } },
|
DropZones = { { name = 'DROP_BLUE_1' } },
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
|
||||||
-- Optionally load a larger crate catalog extracted from your CTLD.lua
|
-- No manual merge needed if you loaded the catalog file before CTLD.lua.
|
||||||
local extracted = dofile(lfs.writedir()..[[Scripts\Moose_CTLD_Pure\catalogs\CrateCatalog_CTLD_Extract.lua]])
|
-- Supported globals that auto-merge: _CTLD_EXTRACTED_CATALOG, CTLD_CATALOG, MOOSE_CTLD_CATALOG
|
||||||
ctld:MergeCatalog(extracted)
|
|
||||||
```
|
```
|
||||||
|
|
||||||
- If you don't have ME trigger zones, define by coordinates:
|
- If you don't have ME trigger zones, define by coordinates:
|
||||||
@@ -96,6 +98,12 @@ CrateCatalog = {
|
|||||||
- Artillery marking uses `Controller.setTask('FireAtPoint')` on configured groups. Ensure those groups exist and are artillery-capable.
|
- Artillery marking uses `Controller.setTask('FireAtPoint')` on configured groups. Ensure those groups exist and are artillery-capable.
|
||||||
- JTAC Auto-Lase helper provided: `fac:StartJTACOnGroup(groupName, laserCode, smokeColor)` uses `FAC_AUTO`.
|
- JTAC Auto-Lase helper provided: `fac:StartJTACOnGroup(groupName, laserCode, smokeColor)` uses `FAC_AUTO`.
|
||||||
|
|
||||||
|
### Catalog sources and precedence
|
||||||
|
|
||||||
|
- By default, CTLD includes a small built-in sample catalog so it works out-of-the-box.
|
||||||
|
- If you load a catalog file before calling `CTLD:New()`, CTLD auto-merges the global catalog (no extra code needed).
|
||||||
|
- To use only your external catalog and avoid sample entries, set `UseBuiltinCatalog = false` in the `CTLD:New({...})` config.
|
||||||
|
|
||||||
## Extending
|
## Extending
|
||||||
|
|
||||||
- Add radio beacons, FOB build recipes, fuel/ammo crates, and CSAR hooks by registering more `CrateCatalog` entries and/or adding helper methods.
|
- Add radio beacons, FOB build recipes, fuel/ammo crates, and CSAR hooks by registering more `CrateCatalog` entries and/or adding helper methods.
|
||||||
|
|||||||
@@ -160,7 +160,9 @@ cat['FOB_SMALL'] = { menuCategory='Support', menu='FOB Crate - Smal
|
|||||||
-- spawns a harmless placeholder truck for visibility; consumed by FOB_SITE build
|
-- spawns a harmless placeholder truck for visibility; consumed by FOB_SITE build
|
||||||
return singleUnit('Ural-375')(point, headingDeg)
|
return singleUnit('Ural-375')(point, headingDeg)
|
||||||
end }
|
end }
|
||||||
cat['FOB_SITE'] = { menuCategory='Support', menu='FOB Crates - All', description='FOB Site', dcsCargoType='container_cargo', requires={ FOB_SMALL=3 }, side=nil, category=Group.Category.GROUND,
|
cat['FOB_SITE'] = { menuCategory='Support', menu='FOB Crates - All', description='FOB Site', isFOB=true, dcsCargoType='container_cargo', requires={ FOB_SMALL=3 }, side=nil, category=Group.Category.GROUND,
|
||||||
build=multiUnits({ {type='HEMTT TFFT'}, {type='Ural-375 PBU', dx=10, dz=8}, {type='Ural-375', dx=-10, dz=8} }) }
|
build=multiUnits({ {type='HEMTT TFFT'}, {type='Ural-375 PBU', dx=10, dz=8}, {type='Ural-375', dx=-10, dz=8} }) }
|
||||||
|
|
||||||
|
-- Also export as a global for mission setups that load via DO SCRIPT FILE (no return capture)
|
||||||
|
_CTLD_EXTRACTED_CATALOG = cat
|
||||||
return cat
|
return cat
|
||||||
|
|||||||
113
Moose_CTLD_Pure/init_mission_dual_coalition.lua
Normal file
113
Moose_CTLD_Pure/init_mission_dual_coalition.lua
Normal file
@@ -0,0 +1,113 @@
|
|||||||
|
-- init_mission_dual_coalition.lua
|
||||||
|
-- Use in Mission Editor with DO SCRIPT FILE load order:
|
||||||
|
-- 1) Moose.lua
|
||||||
|
-- 2) Moose_CTLD_Pure/Moose_CTLD.lua
|
||||||
|
-- 3) Moose_CTLD_Pure/catalogs/CrateCatalog_CTLD_Extract.lua -- optional but recommended catalog with BLUE+RED items (_CTLD_EXTRACTED_CATALOG)
|
||||||
|
-- 4) Moose_CTLD_Pure/Moose_CTLD_FAC.lua -- optional FAC/RECCE support
|
||||||
|
-- 5) DO SCRIPT: dofile on this file OR paste the block below directly
|
||||||
|
--
|
||||||
|
-- Zones you should create in the Mission Editor (as trigger zones):
|
||||||
|
-- BLUE: PICKUP_BLUE_MAIN, DROP_BLUE_1, FOB_BLUE_A
|
||||||
|
-- RED : PICKUP_RED_MAIN, DROP_RED_1, FOB_RED_A
|
||||||
|
-- Adjust names below if you use different zone names.
|
||||||
|
|
||||||
|
-- Create CTLD for BLUE
|
||||||
|
ctldBlue = _MOOSE_CTLD:New({
|
||||||
|
CoalitionSide = coalition.side.BLUE,
|
||||||
|
UseCategorySubmenus = true,
|
||||||
|
UseBuiltinCatalog = false, -- rely on external catalog (recommended)
|
||||||
|
RestrictFOBToZones = true,
|
||||||
|
AutoBuildFOBInZones = true,
|
||||||
|
Zones = {
|
||||||
|
PickupZones = { { name = 'PICKUP_BLUE_MAIN' } },
|
||||||
|
DropZones = { { name = 'DROP_BLUE_1' } },
|
||||||
|
FOBZones = { { name = 'FOB_BLUE_A' } },
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
|
-- Create CTLD for RED
|
||||||
|
ctldRed = _MOOSE_CTLD:New({
|
||||||
|
CoalitionSide = coalition.side.RED,
|
||||||
|
UseCategorySubmenus = true,
|
||||||
|
UseBuiltinCatalog = false, -- rely on external catalog (recommended)
|
||||||
|
RestrictFOBToZones = true,
|
||||||
|
AutoBuildFOBInZones = true,
|
||||||
|
Zones = {
|
||||||
|
PickupZones = { { name = 'PICKUP_RED_MAIN' } },
|
||||||
|
DropZones = { { name = 'DROP_RED_1' } },
|
||||||
|
FOBZones = { { name = 'FOB_RED_A' } },
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
|
-- 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
|
||||||
|
facBlue = _MOOSE_CTLD_FAC:New(ctldBlue, {
|
||||||
|
CoalitionSide = coalition.side.BLUE,
|
||||||
|
Arty = { Enabled = false },
|
||||||
|
})
|
||||||
|
-- facBlue:AddRecceZone({ name = 'RECCE_BLUE_1' })
|
||||||
|
facBlue:Run()
|
||||||
|
|
||||||
|
facRed = _MOOSE_CTLD_FAC:New(ctldRed, {
|
||||||
|
CoalitionSide = coalition.side.RED,
|
||||||
|
Arty = { Enabled = false },
|
||||||
|
})
|
||||||
|
-- facRed:AddRecceZone({ name = 'RECCE_RED_1' })
|
||||||
|
facRed:Run()
|
||||||
|
end
|
||||||
46
Moose_CTLD_Pure/init_mission_example.lua
Normal file
46
Moose_CTLD_Pure/init_mission_example.lua
Normal file
@@ -0,0 +1,46 @@
|
|||||||
|
-- init_mission_example.lua
|
||||||
|
-- Use this with Mission Editor triggers as DO SCRIPT (no paths)
|
||||||
|
-- Load order (Mission Start):
|
||||||
|
-- Option A (catalog first):
|
||||||
|
-- 1) DO SCRIPT FILE: Moose.lua
|
||||||
|
-- 2) DO SCRIPT FILE: Moose_CTLD_Pure/catalogs/CrateCatalog_CTLD_Extract.lua -- sets _CTLD_EXTRACTED_CATALOG
|
||||||
|
-- 3) DO SCRIPT FILE: Moose_CTLD_Pure/Moose_CTLD.lua
|
||||||
|
-- 4) DO SCRIPT FILE: Moose_CTLD_Pure/Moose_CTLD_FAC.lua (optional)
|
||||||
|
-- 5) DO SCRIPT: paste the block below
|
||||||
|
-- Option B (catalog after CTLD file):
|
||||||
|
-- 1) DO SCRIPT FILE: Moose.lua
|
||||||
|
-- 2) DO SCRIPT FILE: Moose_CTLD_Pure/Moose_CTLD.lua
|
||||||
|
-- 3) DO SCRIPT FILE: Moose_CTLD_Pure/catalogs/CrateCatalog_CTLD_Extract.lua -- still fine: load before ctld:New()
|
||||||
|
-- 4) DO SCRIPT FILE: Moose_CTLD_Pure/Moose_CTLD_FAC.lua (optional)
|
||||||
|
-- 5) DO SCRIPT: paste the block below
|
||||||
|
|
||||||
|
-- create CTLD instance for BLUE
|
||||||
|
ctld = _MOOSE_CTLD:New({
|
||||||
|
CoalitionSide = coalition.side.BLUE,
|
||||||
|
UseCategorySubmenus = true,
|
||||||
|
-- Set to false if you want ONLY the external catalog and no built-in examples
|
||||||
|
-- UseBuiltinCatalog = false,
|
||||||
|
RestrictFOBToZones = true,
|
||||||
|
AutoBuildFOBInZones = true,
|
||||||
|
Zones = {
|
||||||
|
PickupZones = { { name = 'PICKUP_BLUE_MAIN' } },
|
||||||
|
DropZones = { { name = 'DROP_BLUE_1' } },
|
||||||
|
FOBZones = { { name = 'FOB_ALPHA' } },
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
|
-- No manual merge needed: Moose_CTLD auto-detects and merges global catalogs
|
||||||
|
-- supported global names: _CTLD_EXTRACTED_CATALOG, CTLD_CATALOG, MOOSE_CTLD_CATALOG
|
||||||
|
|
||||||
|
-- set allowed transports (optional override)
|
||||||
|
-- ctld:SetAllowedAircraft({'UH-1H','Mi-8MTV2','Mi-24P'})
|
||||||
|
|
||||||
|
-- FAC/RECCE (optional)
|
||||||
|
if _MOOSE_CTLD_FAC then
|
||||||
|
fac = _MOOSE_CTLD_FAC:New(ctld, {
|
||||||
|
CoalitionSide = coalition.side.BLUE,
|
||||||
|
Arty = { Enabled=true, Groups={'BLUE_ARTY_1'}, Rounds=2, Spread=80 },
|
||||||
|
})
|
||||||
|
fac:AddRecceZone({ name = 'RECCE_ZONE_1' })
|
||||||
|
fac:Run()
|
||||||
|
end
|
||||||
Reference in New Issue
Block a user