Fixed bug that allowed you to put a drop zone in/near a pickzone.

This commit is contained in:
iTracerFacer 2025-11-05 20:22:01 -06:00
parent bbd2bc0f73
commit 184970a01d
5 changed files with 176 additions and 31 deletions

View File

@ -0,0 +1,86 @@
-- 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 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,
AllowedAircraft = { -- transport-capable unit type names (case-sensitive as in DCS DB)
'UH-1H','Mi-8MTV2','Mi-24P','SA342M','SA342L','SA342Minigun','UH-60L','CH-47Fbl1','CH-47F','Mi-17','GazelleAI'
},
-- Optional: drive zone activation from mission flags (preferred: set per-zone below via flag/activeWhen)
Zones = {
PickupZones = { { name = 'Luostari Supply', smoke = trigger.smokeColor.Blue, flag = 9001, activeWhen = 0 },
{ name = 'Koshka Supply', smoke = trigger.smokeColor.Blue, flag = 9004, activeWhen = 0 },
{ name = 'Ivalo Supply', smoke = trigger.smokeColor.Blue, flag = 9005, activeWhen = 0 },
{ name = 'Alakurtti Supply', smoke = trigger.smokeColor.Blue, flag = 9006, activeWhen = 0 },
{ name = 'Dallas FARP Supply', smoke = trigger.smokeColor.Blue, flag = 9007, activeWhen = 0 },
{ name = 'Paris FARP Supply', smoke = trigger.smokeColor.Blue, flag = 9008, activeWhen = 0 },
{ name = 'London FARP Supply', smoke = trigger.smokeColor.Blue, flag = 9009, activeWhen = 0},
},
--DropZones = { { name = 'BRAVO', flag = 9002, activeWhen = 0 } },
--FOBZones = { { name = 'CHARLIE', flag = 9003, activeWhen = 0 } },
},
BuildRequiresGroundCrates = true,
})
ctldRed = _MOOSE_CTLD:New({
CoalitionSide = coalition.side.RED,
PickupZoneSmokeColor = trigger.smokeColor.Red,
AllowedAircraft = { -- transport-capable unit type names (case-sensitive as in DCS DB)
'UH-1H','Mi-8MTV2','Mi-24P','SA342M','SA342L','SA342Minigun','UH-60L','CH-47Fbl1','CH-47F','Mi-17','GazelleAI'
},
-- Optional: drive zone activation for RED via per-zone flag/activeWhen
Zones = {
PickupZones = { { name = 'Luostari Supply', smoke = trigger.smokeColor.Red, flag = 9101, activeWhen = 0 },
{ name = 'Severomorsk-1 Supply', smoke = trigger.smokeColor.Red, flag = 9104, activeWhen = 0 },
{ name = 'Severomorsk-3 Supply', smoke = trigger.smokeColor.Red, flag = 9105, activeWhen = 0 },
{ name = 'Alakurtti Supply', smoke = trigger.smokeColor.Red, flag = 9106, activeWhen = 0 },
{ name = 'Murmansk Supply', smoke = trigger.smokeColor.Red, flag = 9107, activeWhen = 0 },
{ name = 'Olenya Supply', smoke = trigger.smokeColor.Red, flag = 9108, activeWhen = 0 },
{ name = 'Monchegorsk Supply', smoke = trigger.smokeColor.Red, flag = 9109, activeWhen = 0},
{ name = 'Afrikanda Supply', smoke = trigger.smokeColor.Red, flag = 9110, activeWhen = 0 },
},
--DropZones = { { name = 'ECHO', flag = 9102, activeWhen = 0 } },
--FOBZones = { { name = 'FOXTROT', flag = 9103, activeWhen = 0 } },
},
BuildRequiresGroundCrates = true,
})
else
env.info('[init_mission_dual_coalition] Moose or CTLD missing; skipping CTLD init')
end
-- Optional: FAC/RECCE for both sides (requires Moose_CTLD_FAC.lua)
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 },
})
-- 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()
else
env.info('[init_mission_dual_coalition] FAC not initialized (missing Moose/CTLD/FAC or CTLD not created)')
end

View File

@ -172,6 +172,7 @@ CTLD.Messages = {
-- Zone restrictions
drop_forbidden_in_pickup = "Cannot drop crates inside a Supply Zone. Move outside the zone boundary.",
troop_deploy_forbidden_in_pickup = "Cannot deploy troops inside a Supply Zone. Move outside the zone boundary.",
drop_zone_too_close_to_pickup = "Drop Zone creation blocked: too close to Supply Zone {zone} (need at least {need} {need_u}; current {dist} {dist_u}). Fly further away and try again.",
}
-- #endregion Messaging
@ -209,6 +210,8 @@ CTLD.Config = {
-- Dynamic Drop Zone settings
DropZoneRadius = 250, -- meters: radius used when creating a Drop Zone via the admin menu at player position
MinDropZoneDistanceFromPickup = 10000, -- meters: minimum distance from nearest Pickup Zone required to create a dynamic Drop Zone (0 to disable)
MinDropDistanceActivePickupOnly = true, -- when true, only ACTIVE pickup zones are considered for the minimum distance check
-- Attack/Defend AI behavior for deployed troops and built vehicles
AttackAI = {
@ -284,6 +287,7 @@ CTLD.Config = {
},
}
-- #endregion Config
-- #region State
@ -356,7 +360,7 @@ local function _nearestZonePoint(unit, list)
local up = du:getPoint()
local ux, uz = up.x, up.z
local best, bestd = nil, 1e12
local best, bestd = nil, nil
for _, z in ipairs(list or {}) do
local mz = _findZone(z)
local zx, zz
@ -380,6 +384,7 @@ local function _nearestZonePoint(unit, list)
if d < bestd then best, bestd = mz, d end
end
end
if not best then return nil, nil end
return best, bestd
end
@ -400,16 +405,28 @@ function CTLD:_isUnitInsidePickupZone(unit, activeOnly)
end
-- Helper: get nearest ACTIVE pickup zone (by configured list), respecting CTLD's active flags
function CTLD:_nearestActivePickupZone(unit)
local function _activePickupDefs()
local defs, out = self.Config.Zones.PickupZones or {}, {}
for _,z in ipairs(defs) do
local n = z.name
if (not n) or self._ZoneActive.Pickup[n] ~= false then table.insert(out, z) end
end
return out
function CTLD:_collectActivePickupDefs()
local out = {}
-- From config-defined zones
local defs = (self.Config and self.Config.Zones and self.Config.Zones.PickupZones) or {}
for _, z in ipairs(defs) do
local n = z.name
if (not n) or self._ZoneActive.Pickup[n] ~= false then table.insert(out, z) end
end
return _nearestZonePoint(unit, _activePickupDefs())
-- From MOOSE zone objects if present
if self.PickupZones and #self.PickupZones > 0 then
for _, mz in ipairs(self.PickupZones) do
if mz and mz.GetName then
local n = mz:GetName()
if self._ZoneActive.Pickup[n] ~= false then table.insert(out, { name = n }) end
end
end
end
return out
end
function CTLD:_nearestActivePickupZone(unit)
return _nearestZonePoint(unit, self:_collectActivePickupDefs())
end
local function _coalitionAddGroup(side, category, groupData)
@ -2458,17 +2475,9 @@ function CTLD:RequestCrateForGroup(group, crateKey)
if not cat then _msgGroup(group, 'Unknown crate type: '..tostring(crateKey)) return end
local unit = group:GetUnit(1)
if not unit or not unit:IsAlive() then return end
local function _activePickupDefs()
local defs, out = self.Config.Zones.PickupZones or {}, {}
for _,z in ipairs(defs) do
local n = z.name
if (not n) or self._ZoneActive.Pickup[n] ~= false then table.insert(out, z) end
end
return out
end
local zone, dist = _nearestZonePoint(unit, _activePickupDefs())
local hasPickupZones = ((self.PickupZones and #self.PickupZones > 0) or (self.Config.Zones and self.Config.Zones.PickupZones and #self.Config.Zones.PickupZones > 0))
and (next(self._ZoneActive.Pickup) ~= nil)
local zone, dist = self:_nearestActivePickupZone(unit)
local defs = self:_collectActivePickupDefs()
local hasPickupZones = (#defs > 0)
local spawnPoint
local maxd = (self.Config.PickupZoneMaxDistance or 10000)
-- Announce request
@ -2480,7 +2489,7 @@ function CTLD:RequestCrateForGroup(group, crateKey)
return
end
if zone and dist <= maxd then
if zone and dist and dist <= maxd then
-- Compute a random spawn point within the pickup zone to avoid stacking crates
local center = zone:GetPointVec3()
local rZone = self:_getZoneRadius(zone)
@ -3158,11 +3167,29 @@ function CTLD:LoadTroops(group, opts)
_eventSend(self, group, nil, 'no_pickup_zones', {})
return
end
local activeDefs = {}
for _,z in ipairs(self.Config.Zones.PickupZones or {}) do
if (not z.name) or self._ZoneActive.Pickup[z.name] ~= false then table.insert(activeDefs, z) end
local zone, dist = self:_nearestActivePickupZone(unit)
if not zone or not dist then
-- No active pickup zone resolvable; provide helpful vectors to nearest configured zone if any
local list = {}
if self.Config and self.Config.Zones and self.Config.Zones.PickupZones then
for _, z in ipairs(self.Config.Zones.PickupZones) do table.insert(list, z) end
elseif self.PickupZones and #self.PickupZones > 0 then
for _, mz in ipairs(self.PickupZones) do if mz and mz.GetName then table.insert(list, { name = mz:GetName() }) end end
end
local fbZone, fbDist = _nearestZonePoint(unit, list)
if fbZone and fbDist then
local isMetric = _getPlayerIsMetric(unit)
local rZone = self:_getZoneRadius(fbZone) or 0
local delta = math.max(0, fbDist - rZone)
local v, u = _fmtRange(delta, isMetric)
local up = unit:GetPointVec3(); local zp = fbZone:GetPointVec3()
local brg = _bearingDeg({ x = up.x, z = up.z }, { x = zp.x, z = zp.z })
_eventSend(self, group, nil, 'troop_pickup_zone_required', { zone_dist = v, zone_dist_u = u, zone_brg = brg })
else
_eventSend(self, group, nil, 'no_pickup_zones', {})
end
return
end
local zone, dist = _nearestZonePoint(unit, activeDefs)
local inside = false
if zone then
local rZone = self:_getZoneRadius(zone) or 0
@ -3170,8 +3197,8 @@ function CTLD:LoadTroops(group, opts)
end
if not inside then
local isMetric = _getPlayerIsMetric(unit)
local rZone = (zone and (self:_getZoneRadius(zone) or 0)) or 0
local delta = math.max(0, (dist or 0) - rZone)
local rZone = (self:_getZoneRadius(zone) or 0)
local delta = (dist and rZone) and math.max(0, dist - rZone) or 0
local v, u = _fmtRange(delta, isMetric)
-- Bearing from player to zone center
local up = unit:GetPointVec3()
@ -3403,12 +3430,44 @@ function CTLD:CreateDropZoneAtGroup(group)
if not group or not group:IsAlive() then return end
local unit = group:GetUnit(1)
if not unit or not unit:IsAlive() then return end
-- Prevent creating a Drop Zone too close to an active Pickup Zone to avoid overlap/confusion
local inside, pz, dist, pr = self:_isUnitInsidePickupZone(unit, true)
-- Prevent creating a Drop Zone inside or too close to a Pickup Zone
-- 1) Block if inside a (potentially active-only) pickup zone
local activeOnlyForInside = (self.Config and self.Config.ForbidChecksActivePickupOnly ~= false)
local inside, pz, distInside, pr = self:_isUnitInsidePickupZone(unit, activeOnlyForInside)
if inside then
_msgGroup(group, string.format('Too close to Pickup Zone %s (%.0fm). Move away and try again.', (pz and pz.GetName and pz:GetName()) or '(pickup)', dist or 0), 10)
local isMetric = _getPlayerIsMetric(unit)
local curV, curU = _fmtRange(distInside or 0, isMetric)
local needV, needU = _fmtRange(self.Config.MinDropZoneDistanceFromPickup or 10000, isMetric)
_eventSend(self, group, nil, 'drop_zone_too_close_to_pickup', {
zone = (pz and pz.GetName and pz:GetName()) or '(pickup)',
need = needV, need_u = needU,
dist = curV, dist_u = curU,
})
return
end
-- 2) Enforce a minimum distance from the nearest pickup zone (configurable)
local minD = tonumber(self.Config and self.Config.MinDropZoneDistanceFromPickup) or 0
if minD > 0 then
local considerActive = (self.Config and self.Config.MinDropDistanceActivePickupOnly ~= false)
local nearestZone, nearestDist
if considerActive then
nearestZone, nearestDist = self:_nearestActivePickupZone(unit)
else
local list = (self.Config and self.Config.Zones and self.Config.Zones.PickupZones) or {}
nearestZone, nearestDist = _nearestZonePoint(unit, list)
end
if nearestZone and nearestDist and nearestDist < minD then
local isMetric = _getPlayerIsMetric(unit)
local needV, needU = _fmtRange(minD, isMetric)
local curV, curU = _fmtRange(nearestDist, isMetric)
_eventSend(self, group, nil, 'drop_zone_too_close_to_pickup', {
zone = (nearestZone and nearestZone.GetName and nearestZone:GetName()) or '(pickup)',
need = needV, need_u = needU,
dist = curV, dist_u = curU,
})
return
end
end
local p = unit:GetPointVec3()
local baseName = group:GetName() or 'GROUP'
local safe = tostring(baseName):gsub('%W', '')

Binary file not shown.