mirror of
https://github.com/iTracerFacer/DCS_MissionDev.git
synced 2025-12-03 04:14:46 +00:00
Too many changes to list accross to many files. Save more often dummy. :)
This commit is contained in:
207
Moose_CTLD_Pure/Moose_CTLD_FAC.lua
Normal file
207
Moose_CTLD_Pure/Moose_CTLD_FAC.lua
Normal file
@@ -0,0 +1,207 @@
|
||||
-- Moose_CTLD_FAC.lua
|
||||
-- FAC/RECCE features integrated with pure-MOOSE CTLD
|
||||
-- Provides: recce zones, auto target marking (smoke/illum), JTAC auto-lase bootstrap, optional artillery mark tasks
|
||||
|
||||
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.')
|
||||
end
|
||||
|
||||
local FAC = {}
|
||||
FAC.__index = FAC
|
||||
FAC.Version = '0.1.0-alpha'
|
||||
|
||||
FAC.Config = {
|
||||
CoalitionSide = coalition.side.BLUE,
|
||||
ScanInterval = 20, -- seconds between scans
|
||||
MarkSmokeColor = trigger.smokeColor.Red,
|
||||
MarkIllum = false, -- drop illumination at night if true
|
||||
MarkText = true, -- place map marks with target info
|
||||
DetectionRadius = 5000, -- meters within zone
|
||||
MinReportSeparation = 400, -- meters between subsequent marks to reduce spam
|
||||
UseGroupMenus = true,
|
||||
Debug = false,
|
||||
Arty = { -- optional artillery support
|
||||
Enabled = true,
|
||||
Groups = { -- names of friendly artillery groups to use for marking
|
||||
-- 'BLUE_ARTY_1',
|
||||
},
|
||||
Rounds = 3,
|
||||
Spread = 120, -- meters randomization around mark point
|
||||
}
|
||||
}
|
||||
|
||||
FAC._lastMarks = {} -- [zoneName] = { lastPoint = {x,z} }
|
||||
|
||||
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.Side = o.Config.CoalitionSide
|
||||
o.Zones = {}
|
||||
o.MenusByGroup = {}
|
||||
|
||||
if o.Config.UseGroupMenus then o:WireBirthHandler() end
|
||||
return o
|
||||
end
|
||||
|
||||
function FAC:WireBirthHandler()
|
||||
local handler = EVENTHANDLER:New()
|
||||
handler:HandleEvent(EVENTS.Birth)
|
||||
local selfref = self
|
||||
function handler:OnEventBirth(eventData)
|
||||
local unit = eventData.IniUnit
|
||||
if not unit or not unit:IsAlive() then return end
|
||||
if unit:GetCoalition() ~= selfref.Side then return end
|
||||
local grp = unit:GetGroup()
|
||||
if not grp then return end
|
||||
local gname = grp:GetName()
|
||||
if selfref.MenusByGroup[gname] then return end
|
||||
-- Simple menu: FAC actions
|
||||
local root = MENU_GROUP:New(grp, 'FAC/RECCE')
|
||||
MENU_GROUP_COMMAND:New(grp, 'List Recce Zones', root, function() selfref:MenuListZones(grp) end)
|
||||
MENU_GROUP_COMMAND:New(grp, 'Mark Contacts (all zones)', root, function() selfref:ForceScanAll(grp) end)
|
||||
selfref.MenusByGroup[gname] = root
|
||||
MESSAGE:New('FAC/RECCE menu available (F10)', 10):ToGroup(grp)
|
||||
end
|
||||
self.BirthHandler = handler
|
||||
end
|
||||
|
||||
function FAC:AddRecceZone(def)
|
||||
-- def: { name='ZONE_NAME' } or { coord={x,y,z}, radius=NN, name='Recce1' }
|
||||
local z
|
||||
if def.name then
|
||||
z = ZONE:FindByName(def.name)
|
||||
end
|
||||
if not z and def.coord then
|
||||
local r = def.radius or 5000
|
||||
z = ZONE_RADIUS:New(def.name or ('FAC_ZONE_'..math.random(10000,99999)), VECTOR2:New(def.coord.x, def.coord.z), r)
|
||||
end
|
||||
if not z then return nil end
|
||||
local Z = {
|
||||
Zone = z,
|
||||
Name = z:GetName(),
|
||||
Detector = self:CreateDetector(z),
|
||||
LastScan = 0,
|
||||
}
|
||||
table.insert(self.Zones, Z)
|
||||
return Z
|
||||
end
|
||||
|
||||
function FAC:CreateDetector(zone)
|
||||
-- Detection in areas using Moose detection classes
|
||||
local enemySide = (self.Side == coalition.side.BLUE) and coalition.side.RED or coalition.side.BLUE
|
||||
local setEnemies = SET_GROUP:New():FilterCoalitions(enemySide):FilterCategoryGround():FilterStart()
|
||||
local det = DETECTION_AREAS:New(setEnemies, zone:GetRadius())
|
||||
det:BoundZone(zone)
|
||||
return det
|
||||
end
|
||||
|
||||
function FAC:MenuListZones(group)
|
||||
local names = {}
|
||||
for _,Z in ipairs(self.Zones) do table.insert(names, Z.Name) end
|
||||
MESSAGE:New('Recce zones: '..(table.concat(names, ', '):gsub('^%s+$','none')), 15):ToGroup(group)
|
||||
end
|
||||
|
||||
function FAC:ForceScanAll(group)
|
||||
for _,Z in ipairs(self.Zones) do self:ScanZone(Z, group) end
|
||||
end
|
||||
|
||||
function FAC:Run()
|
||||
-- schedule periodic scanning
|
||||
if self.Sched then self.Sched:Stop() end
|
||||
self.Sched = SCHEDULER:New(nil, function()
|
||||
for _,Z in ipairs(self.Zones) do self:ScanZone(Z) end
|
||||
end, {}, 5, self.Config.ScanInterval)
|
||||
end
|
||||
|
||||
local function _p3(v2)
|
||||
return { x = v2.x, y = land.getHeight({x=v2.x, y=v2.y}), z = v2.y }
|
||||
end
|
||||
|
||||
function FAC:ScanZone(Z, notifyGroup)
|
||||
local now = timer.getTime()
|
||||
local det = Z.Detector
|
||||
det:DetectionUpdate()
|
||||
local reports = det:GetDetectedItems()
|
||||
if not reports or #reports == 0 then
|
||||
if notifyGroup then MESSAGE:New('No contacts detected in '..Z.Name, 10):ToGroup(notifyGroup) end
|
||||
return
|
||||
end
|
||||
|
||||
local enemySide = (self.Side == coalition.side.BLUE) and coalition.side.RED or coalition.side.BLUE
|
||||
for _,rep in ipairs(reports) do
|
||||
local contact = rep.object -- wrapper around GROUP or UNIT
|
||||
local pos2 = rep.point -- vec2
|
||||
if pos2 then
|
||||
local markPoint = { x = pos2.x, z = pos2.y }
|
||||
local allow = true
|
||||
local last = FAC._lastMarks[Z.Name]
|
||||
if last then
|
||||
local dx = (markPoint.x - last.x); local dz = (markPoint.z - last.z)
|
||||
if math.sqrt(dx*dx+dz*dz) < self.Config.MinReportSeparation then allow = false end
|
||||
end
|
||||
if allow then
|
||||
FAC._lastMarks[Z.Name] = { x = markPoint.x, z = markPoint.z }
|
||||
self:MarkTarget(Z, markPoint, rep, enemySide)
|
||||
if notifyGroup then MESSAGE:New(string.format('Marked contact in %s', Z.Name), 10):ToGroup(notifyGroup) end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function FAC:MarkTarget(Z, point, rep, enemySide)
|
||||
-- Smoke
|
||||
trigger.action.smoke(point, self.Config.MarkSmokeColor)
|
||||
-- Map mark
|
||||
if self.Config.MarkText then
|
||||
local txt = string.format('FAC: %s at %s', rep.type or 'Contact', coord.LLtoString(coord.LOtoLL(point), 0))
|
||||
trigger.action.markToCoalition(math.random(100000,999999), txt, point, self.Side, true)
|
||||
end
|
||||
-- Optional arty marking
|
||||
if self.Config.Arty.Enabled then
|
||||
self:ArtyMark(point)
|
||||
end
|
||||
end
|
||||
|
||||
function FAC:ArtyMark(point)
|
||||
local rounds = self.Config.Arty.Rounds or 1
|
||||
for _,gname in ipairs(self.Config.Arty.Groups or {}) do
|
||||
local g = Group.getByName(gname)
|
||||
if g and g:isExist() then
|
||||
local ctrl = g:getController()
|
||||
if ctrl then
|
||||
for i=1,rounds do
|
||||
local spread = self.Config.Arty.Spread or 0
|
||||
local tgt = { x = point.x + math.random(-spread, spread), y = point.z + math.random(-spread, spread) }
|
||||
local task = {
|
||||
id = 'FireAtPoint',
|
||||
params = {
|
||||
point = { x = tgt.x, y = land.getHeight({x=tgt.x, y=tgt.y}), z = tgt.y },
|
||||
expendQty = 1,
|
||||
dispersion = 50,
|
||||
attackQty = 1,
|
||||
weaponType = 0,
|
||||
}
|
||||
}
|
||||
ctrl:setTask(task)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
-- Bootstrap a JTAC on a spawned unit/group via MOOSE FAC_AUTO
|
||||
function FAC:StartJTACOnGroup(groupName, code, smoke)
|
||||
local grp = GROUP:FindByName(groupName)
|
||||
if not grp then return nil end
|
||||
local fac = FAC_AUTO:New(grp)
|
||||
fac:SetLaser(true, code or 1688)
|
||||
fac:SetSmoke(smoke or self.Config.MarkSmokeColor)
|
||||
fac:SetDetectVehicles()
|
||||
fac:Start()
|
||||
return fac
|
||||
end
|
||||
|
||||
_MOOSE_CTLD_FAC = FAC
|
||||
return FAC
|
||||
Reference in New Issue
Block a user