mirror of
https://github.com/iTracerFacer/DCS_MissionDev.git
synced 2025-12-03 04:14:46 +00:00
More menu updates.
This commit is contained in:
parent
093fe59fd8
commit
9c406cb83e
Binary file not shown.
Binary file not shown.
41
Moose_.lua
41
Moose_.lua
@ -1,4 +1,4 @@
|
|||||||
env.info('*** MOOSE GITHUB Commit Hash ID: 2025-11-11T12:57:41+01:00-d7b0b3c898fb636dd8b728721e247763383a5bdb ***')
|
env.info('*** MOOSE GITHUB Commit Hash ID: 2025-11-14T17:27:02+01:00-cdbf1e147e76dcfab3d1bc471edf593a0e92182a ***')
|
||||||
if not MOOSE_DEVELOPMENT_FOLDER then
|
if not MOOSE_DEVELOPMENT_FOLDER then
|
||||||
MOOSE_DEVELOPMENT_FOLDER='Scripts'
|
MOOSE_DEVELOPMENT_FOLDER='Scripts'
|
||||||
end
|
end
|
||||||
@ -31462,6 +31462,7 @@ self.Life0=Life0
|
|||||||
else
|
else
|
||||||
self:E(string.format("Static object %s does not exist!",tostring(self.StaticName)))
|
self:E(string.format("Static object %s does not exist!",tostring(self.StaticName)))
|
||||||
end
|
end
|
||||||
|
self._vec3=self:GetVec3()
|
||||||
return self
|
return self
|
||||||
end
|
end
|
||||||
function STATIC:GetLife0()
|
function STATIC:GetLife0()
|
||||||
@ -31474,6 +31475,20 @@ return DCSStatic:getLife()or 1
|
|||||||
end
|
end
|
||||||
return nil
|
return nil
|
||||||
end
|
end
|
||||||
|
function STATIC:GetVec2Cached()
|
||||||
|
local vec2=self:GetVec2()
|
||||||
|
if not vec2 and self._vec3 then
|
||||||
|
vec2={x=self._vec3.x,y=self._vec3.z}
|
||||||
|
end
|
||||||
|
return vec2
|
||||||
|
end
|
||||||
|
function STATIC:GetVec3Cached()
|
||||||
|
local vec3=self:GetVec3()
|
||||||
|
if not vec3 and self._vec3 then
|
||||||
|
vec3=self._vec3
|
||||||
|
end
|
||||||
|
return vec3
|
||||||
|
end
|
||||||
function STATIC:Find(DCSStatic)
|
function STATIC:Find(DCSStatic)
|
||||||
local StaticName=DCSStatic:getName()
|
local StaticName=DCSStatic:getName()
|
||||||
local StaticFound=_DATABASE:FindStatic(StaticName)
|
local StaticFound=_DATABASE:FindStatic(StaticName)
|
||||||
@ -31539,6 +31554,7 @@ SCHEDULER:New(nil,self.SpawnAt,{self,Coordinate,Heading},Delay)
|
|||||||
else
|
else
|
||||||
local SpawnStatic=SPAWNSTATIC:NewFromStatic(self.StaticName)
|
local SpawnStatic=SPAWNSTATIC:NewFromStatic(self.StaticName)
|
||||||
SpawnStatic:SpawnFromPointVec2(Coordinate,Heading,self.StaticName)
|
SpawnStatic:SpawnFromPointVec2(Coordinate,Heading,self.StaticName)
|
||||||
|
self._vec3=self:GetVec3()
|
||||||
end
|
end
|
||||||
return self
|
return self
|
||||||
end
|
end
|
||||||
@ -31549,6 +31565,7 @@ else
|
|||||||
CountryID=CountryID or self:GetCountry()
|
CountryID=CountryID or self:GetCountry()
|
||||||
local SpawnStatic=SPAWNSTATIC:NewFromStatic(self.StaticName,CountryID)
|
local SpawnStatic=SPAWNSTATIC:NewFromStatic(self.StaticName,CountryID)
|
||||||
SpawnStatic:Spawn(nil,self.StaticName)
|
SpawnStatic:Spawn(nil,self.StaticName)
|
||||||
|
self._vec3=self:GetVec3()
|
||||||
end
|
end
|
||||||
return self
|
return self
|
||||||
end
|
end
|
||||||
@ -31558,6 +31575,7 @@ SCHEDULER:New(nil,self.ReSpawnAt,{self,Coordinate,Heading},Delay)
|
|||||||
else
|
else
|
||||||
local SpawnStatic=SPAWNSTATIC:NewFromStatic(self.StaticName,self:GetCountry())
|
local SpawnStatic=SPAWNSTATIC:NewFromStatic(self.StaticName,self:GetCountry())
|
||||||
SpawnStatic:SpawnFromCoordinate(Coordinate,Heading,self.StaticName)
|
SpawnStatic:SpawnFromCoordinate(Coordinate,Heading,self.StaticName)
|
||||||
|
self._vec3=self:GetVec3()
|
||||||
end
|
end
|
||||||
return self
|
return self
|
||||||
end
|
end
|
||||||
@ -134336,6 +134354,16 @@ local Vector=NavFix.vector:Translate(UTILS.NMToMeters(Distance),Bearing,true)
|
|||||||
self=NAVFIX:NewFromVector(Name,Type,Vector)
|
self=NAVFIX:NewFromVector(Name,Type,Vector)
|
||||||
return self
|
return self
|
||||||
end
|
end
|
||||||
|
function NAVFIX:NewFromBeacon(Beacon)
|
||||||
|
local frequency,unit=BEACONS:_GetFrequency(Beacon.frequency)
|
||||||
|
frequency=string.format("%.3f",frequency)
|
||||||
|
if Beacon.typeName=="TACAN"then
|
||||||
|
frequency=Beacon.channel
|
||||||
|
unit="X"
|
||||||
|
end
|
||||||
|
self=NAVFIX:NewFromVector(string.format("%s %s %s",Beacon.typeName,frequency,unit),Beacon.typeName,Beacon.vec3)
|
||||||
|
return self
|
||||||
|
end
|
||||||
function NAVFIX:SetIntermediateFix(IntermediateFix)
|
function NAVFIX:SetIntermediateFix(IntermediateFix)
|
||||||
self.isIF=IntermediateFix
|
self.isIF=IntermediateFix
|
||||||
return self
|
return self
|
||||||
@ -134549,9 +134577,18 @@ return closest
|
|||||||
end
|
end
|
||||||
function BEACONS:GetBeacons(TypeID)
|
function BEACONS:GetBeacons(TypeID)
|
||||||
local beacons={}
|
local beacons={}
|
||||||
|
local keys={}
|
||||||
|
if TypeID~=nil and type(TypeID)~="table"then
|
||||||
|
TypeID={TypeID}
|
||||||
|
end
|
||||||
|
for _,_typeid in pairs(TypeID or{})do
|
||||||
|
if _typeid~=nil then
|
||||||
|
keys[_typeid]=_typeid
|
||||||
|
end
|
||||||
|
end
|
||||||
for _,_beacon in pairs(self.beacons)do
|
for _,_beacon in pairs(self.beacons)do
|
||||||
local bc=_beacon
|
local bc=_beacon
|
||||||
if TypeID==nil or TypeID==bc.type then
|
if TypeID==nil or keys[bc.type]~=nil then
|
||||||
table.insert(beacons,bc)
|
table.insert(beacons,bc)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|||||||
@ -4537,16 +4537,13 @@ function CTLD:BuildGroupMenus(group)
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
if bestName and bestMeta then
|
if bestName and bestMeta then
|
||||||
local zdef = { smoke = self.Config.PickupZoneSmokeColor }
|
|
||||||
local sx, sz = bestMeta.point.x, bestMeta.point.z
|
local sx, sz = bestMeta.point.x, bestMeta.point.z
|
||||||
local sy = 0
|
local sy = 0
|
||||||
if land and land.getHeight then
|
if land and land.getHeight then
|
||||||
-- land.getHeight expects Vec2 where y is z
|
|
||||||
local ok, h = pcall(land.getHeight, { x = sx, y = sz })
|
local ok, h = pcall(land.getHeight, { x = sx, y = sz })
|
||||||
if ok and type(h) == 'number' then sy = h end
|
if ok and type(h) == 'number' then sy = h end
|
||||||
end
|
end
|
||||||
-- Use new smoke helper with crate ID for refresh scheduling
|
local smokeColor = self.Config.PickupZoneSmokeColor
|
||||||
local smokeColor = (zdef and zdef.smoke) or self.Config.PickupZoneSmokeColor
|
|
||||||
_spawnCrateSmoke({ x = sx, y = sy, z = sz }, smokeColor, self.Config.CrateSmoke, bestName)
|
_spawnCrateSmoke({ x = sx, y = sy, z = sz }, smokeColor, self.Config.CrateSmoke, bestName)
|
||||||
_eventSend(self, group, nil, 'crate_re_marked', { id = bestName, mark = 'smoke' })
|
_eventSend(self, group, nil, 'crate_re_marked', { id = bestName, mark = 'smoke' })
|
||||||
else
|
else
|
||||||
@ -4564,13 +4561,14 @@ function CTLD:BuildGroupMenus(group)
|
|||||||
|
|
||||||
local infoRoot = MENU_GROUP:New(group, 'Recipe Info', logRoot)
|
local infoRoot = MENU_GROUP:New(group, 'Recipe Info', logRoot)
|
||||||
if self.Config.UseCategorySubmenus then
|
if self.Config.UseCategorySubmenus then
|
||||||
local submenus = {}
|
local reqSubmenus = {}
|
||||||
local function getSubmenu(catLabel)
|
local function getRequestSub(catLabel)
|
||||||
if not submenus[catLabel] then
|
if not reqSubmenus[catLabel] then
|
||||||
submenus[catLabel] = MENU_GROUP:New(group, catLabel, reqRoot)
|
reqSubmenus[catLabel] = MENU_GROUP:New(group, catLabel, reqRoot)
|
||||||
end
|
end
|
||||||
return submenus[catLabel]
|
return reqSubmenus[catLabel]
|
||||||
end
|
end
|
||||||
|
|
||||||
local infoSubs = {}
|
local infoSubs = {}
|
||||||
local function getInfoSub(catLabel)
|
local function getInfoSub(catLabel)
|
||||||
if not infoSubs[catLabel] then
|
if not infoSubs[catLabel] then
|
||||||
@ -4578,17 +4576,18 @@ function CTLD:BuildGroupMenus(group)
|
|||||||
end
|
end
|
||||||
return infoSubs[catLabel]
|
return infoSubs[catLabel]
|
||||||
end
|
end
|
||||||
|
|
||||||
local replacementQueue = {}
|
local replacementQueue = {}
|
||||||
for key,def in pairs(self.Config.CrateCatalog) do
|
for key,def in pairs(self.Config.CrateCatalog) do
|
||||||
if not (def and def.hidden) then
|
if not (def and def.hidden) then
|
||||||
local label = self:_formatMenuLabelWithCrates(key, def)
|
|
||||||
local sideOk = (not def.side) or def.side == self.Side
|
local sideOk = (not def.side) or def.side == self.Side
|
||||||
if sideOk then
|
if sideOk then
|
||||||
local catLabel = (def and def.menuCategory) or 'Other'
|
local catLabel = (def and def.menuCategory) or 'Other'
|
||||||
local parent = getSubmenu(catLabel)
|
local reqParent = getRequestSub(catLabel)
|
||||||
|
local label = self:_formatMenuLabelWithCrates(key, def)
|
||||||
|
|
||||||
if def and type(def.requires) == 'table' then
|
if def and type(def.requires) == 'table' then
|
||||||
-- Composite recipe: request full bundle of component crates
|
CMD(label, reqParent, function() self:RequestRecipeBundleForGroup(group, key) end)
|
||||||
CMD(label, parent, function() self:RequestRecipeBundleForGroup(group, key) end)
|
|
||||||
for reqKey,_ in pairs(def.requires) do
|
for reqKey,_ in pairs(def.requires) do
|
||||||
local compDef = self.Config.CrateCatalog[reqKey]
|
local compDef = self.Config.CrateCatalog[reqKey]
|
||||||
local compSideOk = (not compDef) or (not compDef.side) or compDef.side == self.Side
|
local compSideOk = (not compDef) or (not compDef.side) or compDef.side == self.Side
|
||||||
@ -4605,8 +4604,9 @@ function CTLD:BuildGroupMenus(group)
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
else
|
else
|
||||||
CMD(label, parent, function() self:RequestCrateForGroup(group, key) end)
|
CMD(label, reqParent, function() self:RequestCrateForGroup(group, key) end)
|
||||||
end
|
end
|
||||||
|
|
||||||
local infoParent = getInfoSub(catLabel)
|
local infoParent = getInfoSub(catLabel)
|
||||||
CMD((def and (def.menu or def.description)) or key, infoParent, function()
|
CMD((def and (def.menu or def.description)) or key, infoParent, function()
|
||||||
local text = self:_formatRecipeInfo(key, def)
|
local text = self:_formatRecipeInfo(key, def)
|
||||||
@ -4615,6 +4615,7 @@ function CTLD:BuildGroupMenus(group)
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
for catLabel,queue in pairs(replacementQueue) do
|
for catLabel,queue in pairs(replacementQueue) do
|
||||||
if queue and queue.list and #queue.list > 0 then
|
if queue and queue.list and #queue.list > 0 then
|
||||||
table.sort(queue.list, function(a,b)
|
table.sort(queue.list, function(a,b)
|
||||||
@ -4622,8 +4623,8 @@ function CTLD:BuildGroupMenus(group)
|
|||||||
local lb = (b.def and (b.def.menu or b.def.description)) or b.key
|
local lb = (b.def and (b.def.menu or b.def.description)) or b.key
|
||||||
return tostring(la) < tostring(lb)
|
return tostring(la) < tostring(lb)
|
||||||
end)
|
end)
|
||||||
local parent = getSubmenu(catLabel)
|
local reqParent = getRequestSub(catLabel)
|
||||||
local replMenu = MENU_GROUP:New(group, 'Replacement Crates', parent)
|
local replMenu = MENU_GROUP:New(group, 'Replacement Crates', reqParent)
|
||||||
for _,entry in ipairs(queue.list) do
|
for _,entry in ipairs(queue.list) do
|
||||||
local replLabel = string.format('Replacement: %s', self:_formatMenuLabelWithCrates(entry.key, entry.def))
|
local replLabel = string.format('Replacement: %s', self:_formatMenuLabelWithCrates(entry.key, entry.def))
|
||||||
CMD(replLabel, replMenu, function() self:RequestCrateForGroup(group, entry.key) end)
|
CMD(replLabel, replMenu, function() self:RequestCrateForGroup(group, entry.key) end)
|
||||||
@ -4635,9 +4636,9 @@ function CTLD:BuildGroupMenus(group)
|
|||||||
local replacementSeen = {}
|
local replacementSeen = {}
|
||||||
for key,def in pairs(self.Config.CrateCatalog) do
|
for key,def in pairs(self.Config.CrateCatalog) do
|
||||||
if not (def and def.hidden) then
|
if not (def and def.hidden) then
|
||||||
local label = self:_formatMenuLabelWithCrates(key, def)
|
|
||||||
local sideOk = (not def.side) or def.side == self.Side
|
local sideOk = (not def.side) or def.side == self.Side
|
||||||
if sideOk then
|
if sideOk then
|
||||||
|
local label = self:_formatMenuLabelWithCrates(key, def)
|
||||||
if def and type(def.requires) == 'table' then
|
if def and type(def.requires) == 'table' then
|
||||||
CMD(label, reqRoot, function() self:RequestRecipeBundleForGroup(group, key) end)
|
CMD(label, reqRoot, function() self:RequestRecipeBundleForGroup(group, key) end)
|
||||||
for reqKey,_ in pairs(def.requires) do
|
for reqKey,_ in pairs(def.requires) do
|
||||||
@ -4651,25 +4652,28 @@ function CTLD:BuildGroupMenus(group)
|
|||||||
else
|
else
|
||||||
CMD(label, reqRoot, function() self:RequestCrateForGroup(group, key) end)
|
CMD(label, reqRoot, function() self:RequestCrateForGroup(group, key) end)
|
||||||
end
|
end
|
||||||
CMD((def and (def.menu or def.description)) or key, infoParent, function()
|
|
||||||
|
CMD((def and (def.menu or def.description)) or key, infoRoot, function()
|
||||||
local text = self:_formatRecipeInfo(key, def)
|
local text = self:_formatRecipeInfo(key, def)
|
||||||
_msgGroup(group, text)
|
_msgGroup(group, text)
|
||||||
end)
|
end)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
if #replacementList > 0 then
|
if #replacementList > 0 then
|
||||||
-- Logistics -> Show Inventory at Nearest Pickup Zone/FOB
|
table.sort(replacementList, function(a,b)
|
||||||
CMD('Show Inventory at Nearest Zone', logRoot, function() self:ShowNearestZoneInventory(group) end)
|
local la = (a.def and (a.def.menu or a.def.description)) or a.key
|
||||||
end
|
local lb = (b.def and (b.def.menu or b.def.description)) or b.key
|
||||||
-- Use new smoke helper with crate ID for refresh scheduling
|
return tostring(la) < tostring(lb)
|
||||||
local smokeColor = (zdef and zdef.smoke) or self.Config.PickupZoneSmokeColor
|
|
||||||
_spawnCrateSmoke({ x = sx, y = sy, z = sz }, smokeColor, self.Config.CrateSmoke, bestName)
|
|
||||||
_eventSend(self, group, nil, 'crate_re_marked', { id = bestName, mark = 'smoke' })
|
|
||||||
else
|
|
||||||
_msgGroup(group, 'No friendly crates found to mark.')
|
|
||||||
end
|
|
||||||
end)
|
end)
|
||||||
|
local replMenu = MENU_GROUP:New(group, 'Replacement Crates', reqRoot)
|
||||||
|
for _,entry in ipairs(replacementList) do
|
||||||
|
local replLabel = string.format('Replacement: %s', self:_formatMenuLabelWithCrates(entry.key, entry.def))
|
||||||
|
CMD(replLabel, replMenu, function() self:RequestCrateForGroup(group, entry.key) end)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
-- Logistics -> Show Inventory at Nearest Pickup Zone/FOB
|
-- Logistics -> Show Inventory at Nearest Pickup Zone/FOB
|
||||||
CMD('Show Inventory at Nearest Zone', logRoot, function() self:ShowNearestZoneInventory(group) end)
|
CMD('Show Inventory at Nearest Zone', logRoot, function() self:ShowNearestZoneInventory(group) end)
|
||||||
@ -7709,22 +7713,32 @@ function CTLD:ScanHoverPickup()
|
|||||||
-- Group doesn't exist or is dead, remove from tracking
|
-- Group doesn't exist or is dead, remove from tracking
|
||||||
_removeFromSpatialGrid(troopGroupName, troopMeta.point, 'troops')
|
_removeFromSpatialGrid(troopGroupName, troopMeta.point, 'troops')
|
||||||
CTLD._deployedTroops[troopGroupName] = nil
|
CTLD._deployedTroops[troopGroupName] = nil
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
local coachEnabled = coachCfg.enabled
|
local coachEnabled = coachCfg.enabled
|
||||||
if CTLD._coachOverride and CTLD._coachOverride[gname] ~= nil then
|
if CTLD._coachOverride and CTLD._coachOverride[gname] ~= nil then
|
||||||
coachEnabled = CTLD._coachOverride[gname]
|
coachEnabled = CTLD._coachOverride[gname]
|
||||||
end
|
end
|
||||||
|
|
||||||
-- If coach is on, provide phased guidance
|
-- If coach is on, provide phased guidance
|
||||||
if coachEnabled and bestName and bestMeta then
|
if coachEnabled and bestName and bestMeta then
|
||||||
|
local thresholds = coachCfg.thresholds or {}
|
||||||
local isMetric = _getPlayerIsMetric(unit)
|
local isMetric = _getPlayerIsMetric(unit)
|
||||||
|
|
||||||
-- Arrival phase
|
-- Arrival phase
|
||||||
if bestd <= (coachCfg.thresholds.arrivalDist or 1000) then
|
if bestd <= (thresholds.arrivalDist or 1000) then
|
||||||
_coachSend(self, group, uname, 'coach_arrival', {}, false)
|
_coachSend(self, group, uname, 'coach_arrival', {}, false)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
-- Close-in guidance
|
||||||
|
if bestd <= (thresholds.closeDist or 100) then
|
||||||
_coachSend(self, group, uname, 'coach_close', {}, false)
|
_coachSend(self, group, uname, 'coach_close', {}, false)
|
||||||
end
|
end
|
||||||
|
|
||||||
-- Precision phase
|
-- Precision phase
|
||||||
if bestd <= (coachCfg.thresholds.precisionDist or 30) then
|
if bestd <= (thresholds.precisionDist or 30) then
|
||||||
local hdg, _ = _headingRadDeg(unit)
|
local hdg, _ = _headingRadDeg(unit)
|
||||||
local dx = (bestMeta.point.x - p3.x)
|
local dx = (bestMeta.point.x - p3.x)
|
||||||
local dz = (bestMeta.point.z - p3.z)
|
local dz = (bestMeta.point.z - p3.z)
|
||||||
@ -7745,8 +7759,8 @@ function CTLD:ScanHoverPickup()
|
|||||||
|
|
||||||
-- Vertical hint against AGL window
|
-- Vertical hint against AGL window
|
||||||
local vHint
|
local vHint
|
||||||
local aglMin = coachCfg.thresholds.aglMin or 5
|
local aglMin = thresholds.aglMin or 5
|
||||||
local aglMax = coachCfg.thresholds.aglMax or 20
|
local aglMax = thresholds.aglMax or 20
|
||||||
if agl < aglMin then
|
if agl < aglMin then
|
||||||
local dv, du = _fmtAGL(aglMin - agl, isMetric)
|
local dv, du = _fmtAGL(aglMin - agl, isMetric)
|
||||||
vHint = string.format("Up %d %s", dv, du)
|
vHint = string.format("Up %d %s", dv, du)
|
||||||
@ -7763,7 +7777,7 @@ function CTLD:ScanHoverPickup()
|
|||||||
_coachSend(self, group, uname, 'coach_hint', data, true)
|
_coachSend(self, group, uname, 'coach_hint', data, true)
|
||||||
|
|
||||||
-- Error prompts (dominant one)
|
-- Error prompts (dominant one)
|
||||||
local maxGS = coachCfg.thresholds.maxGS or (8/3.6)
|
local maxGS = thresholds.maxGS or (8/3.6)
|
||||||
local aglMinT = aglMin
|
local aglMinT = aglMin
|
||||||
local aglMaxT = aglMax
|
local aglMaxT = aglMax
|
||||||
if gs > maxGS then
|
if gs > maxGS then
|
||||||
|
|||||||
Binary file not shown.
Loading…
x
Reference in New Issue
Block a user