diff --git a/Moose Development/Moose/Ops/CTLD.lua b/Moose Development/Moose/Ops/CTLD.lua index c0c30ec72..405ca9a07 100644 --- a/Moose Development/Moose/Ops/CTLD.lua +++ b/Moose Development/Moose/Ops/CTLD.lua @@ -20,11 +20,12 @@ -- -- ### Author: **Applevangelist** (Moose Version), ***Ciribob*** (original), Thanks to: Shadowze, Cammel (testing), bbirchnz (additional code!!) -- ### Repack addition for crates: **Raiden** +-- ### Additional cool features: **Lekaa** -- -- @module Ops.CTLD -- @image OPS_CTLD.jpg --- Last Update April 2025 +-- Last Update May 2025 ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ @@ -865,6 +866,7 @@ do -- my_ctld.TroopUnloadDistGroundHook = 15 -- On the ground, unload troops this far behind the Chinook -- my_ctld.TroopUnloadDistHoverHook = 5 -- When hovering, unload troops this far behind the Chinook -- my_ctld.showstockinmenuitems = false -- When set to true, the menu lines will also show the remaining items in stock (that is, if you set any), downside is that the menu for all will be build every 30 seconds anew. +-- my_ctld.onestepmenu = false -- When set to true, the menu will create Drop and build, Get and load, Pack and remove, Pack and load, Pack only. it will be a 1 step solution. -- -- ## 2.1 CH-47 Chinook support -- @@ -1412,7 +1414,7 @@ CTLD.FixedWingTypes = { --- CTLD class version. -- @field #string version -CTLD.version="1.2.33" +CTLD.version="1.3.34" --- Instantiate a new CTLD. -- @param #CTLD self @@ -1591,6 +1593,7 @@ function CTLD:New(Coalition, Prefixes, Alias) self.subcats = {} self.subcatsTroop = {} self.showstockinmenuitems = false + self.onestepmenu = false -- disallow building in loadzones self.nobuildinloadzones = true @@ -2913,10 +2916,10 @@ function CTLD:_GetCrates(Group, Unit, Cargo, number, drop, pack) if drop then text = string.format("Crates for %s have been dropped!",cratename) self:__CratesDropped(1, Group, Unit, droppedcargo) + else + self:_SendMessage(text, 10, false, Group) end - self:_SendMessage(text, 10, false, Group) self:_RefreshLoadCratesMenu(Group, Unit) - return self end @@ -3576,11 +3579,14 @@ end -- @param Wrapper.Unit#UNIT Unit -- @return #boolean Outcome function CTLD:IsHook(Unit) - if Unit and string.find(Unit:GetTypeName(),"CH.47") then - return true - else - return false - end + if not Unit then return false end + local typeName = Unit:GetTypeName() + if not typeName then return false end + if string.find(typeName, "CH.47") then + return true + else + return false + end end --- (Internal) Function to set troops positions of a template to a nice circle @@ -3763,89 +3769,103 @@ end -- @param Wrapper.Group#GROUP Group -- @param Wrapper.Unit#UNIT Unit function CTLD:_UnloadCrates(Group, Unit) - self:T(self.lid .. " _UnloadCrates") - - if not self.dropcratesanywhere then -- #1570 - -- check if we are in DROP zone - local inzone, zonename, zone, distance = self:IsUnitInZone(Unit,CTLD.CargoZoneType.DROP) - if not inzone then - self:_SendMessage("You are not close enough to a drop zone!", 10, false, Group) - if not self.debug then - return self - end - end - end - -- Door check - if self.pilotmustopendoors and not UTILS.IsLoadingDoorOpen(Unit:GetName()) then - self:_SendMessage("You need to open the door(s) to drop cargo!", 10, false, Group) - if not self.debug then return self end - end - -- check for hover unload - local hoverunload = self:IsCorrectHover(Unit) --if true we\'re hovering in parameters - local IsHerc = self:IsFixedWing(Unit) - local IsHook = self:IsHook(Unit) - if IsHerc and (not IsHook) then - -- no hover but airdrop here - hoverunload = self:IsCorrectFlightParameters(Unit) - end - -- check if we\'re landed - local grounded = not self:IsUnitInAir(Unit) - -- Get what we have loaded - local unitname = Unit:GetName() - if self.Loaded_Cargo[unitname] and (grounded or hoverunload) then - local loadedcargo = self.Loaded_Cargo[unitname] or {} -- #CTLD.LoadedCargo - -- looking for crate - local cargotable = loadedcargo.Cargo - for _,_cargo in pairs (cargotable) do - local cargo = _cargo -- #CTLD_CARGO - local type = cargo:GetType() -- #CTLD_CARGO.Enum - if type ~= CTLD_CARGO.Enum.TROOPS and type ~= CTLD_CARGO.Enum.ENGINEERS and type ~= CTLD_CARGO.Enum.GCLOADABLE and (not cargo:WasDropped() or self.allowcratepickupagain) then - -- unload crates - self:_GetCrates(Group, Unit, cargo, 1, true) - cargo:SetWasDropped(true) - cargo:SetHasMoved(true) - end - end - -- cleanup load list - local loaded = {} -- #CTLD.LoadedCargo - loaded.Troopsloaded = 0 - loaded.Cratesloaded = 0 - loaded.Cargo = {} + self:T(self.lid .. " _UnloadCrates") - for _,_cargo in pairs (cargotable) do - local cargo = _cargo -- #CTLD_CARGO - local type = cargo:GetType() -- #CTLD_CARGO.Enum - local size = cargo:GetCratesNeeded() - if type == CTLD_CARGO.Enum.TROOPS or type == CTLD_CARGO.Enum.ENGINEERS then - table.insert(loaded.Cargo,_cargo) - loaded.Troopsloaded = loaded.Troopsloaded + size - end - if type == CTLD_CARGO.Enum.GCLOADABLE and not cargo:WasDropped() then - table.insert(loaded.Cargo,_cargo) - loaded.Cratesloaded = loaded.Cratesloaded + size + if not self.dropcratesanywhere then -- #1570 + local inzone, zonename, zone, distance = self:IsUnitInZone(Unit,CTLD.CargoZoneType.DROP) + if not inzone then + self:_SendMessage("You are not close enough to a drop zone!", 10, false, Group) + if not self.debug then + return self + end end end - self.Loaded_Cargo[unitname] = nil - self.Loaded_Cargo[unitname] = loaded - - self:_UpdateUnitCargoMass(Unit) - self:_RefreshDropCratesMenu(Group,Unit) - else - if IsHerc then - self:_SendMessage("Nothing loaded or not within airdrop parameters!", 10, false, Group) + if self.pilotmustopendoors and not UTILS.IsLoadingDoorOpen(Unit:GetName()) then + self:_SendMessage("You need to open the door(s) to drop cargo!", 10, false, Group) + if not self.debug then return self end + end + local hoverunload = self:IsCorrectHover(Unit) + local IsHerc = self:IsFixedWing(Unit) + local IsHook = self:IsHook(Unit) + if IsHerc and (not IsHook) then + hoverunload = self:IsCorrectFlightParameters(Unit) + end + local grounded = not self:IsUnitInAir(Unit) + local unitname = Unit:GetName() + if self.Loaded_Cargo[unitname] and (grounded or hoverunload) then + local loadedcargo = self.Loaded_Cargo[unitname] or {} + local cargotable = loadedcargo.Cargo + local droppedCount = {} + local neededMap = {} + for _,_cargo in pairs (cargotable) do + local cargo = _cargo + local type = cargo:GetType() + if type ~= CTLD_CARGO.Enum.TROOPS and type ~= CTLD_CARGO.Enum.ENGINEERS and type ~= CTLD_CARGO.Enum.GCLOADABLE and (not cargo:WasDropped() or self.allowcratepickupagain) then + self:_GetCrates(Group, Unit, cargo, 1, true) + cargo:SetWasDropped(true) + cargo:SetHasMoved(true) + local cname = cargo:GetName() or "Unknown" + droppedCount[cname] = (droppedCount[cname] or 0) + 1 + if not neededMap[cname] then + neededMap[cname] = cargo:GetCratesNeeded() or 1 + end + end + end + for cname,count in pairs(droppedCount) do + local needed = neededMap[cname] or 1 + if needed > 1 then + local full = math.floor(count/needed) + local left = count % needed + if full > 0 and left == 0 then + self:_SendMessage(string.format("Dropped %d %s.",full,cname),10,false,Group) + elseif full > 0 and left > 0 then + self:_SendMessage(string.format("Dropped %d %s(s), with %d leftover crate(s).",full,cname,left),10,false,Group) + else + self:_SendMessage(string.format("Dropped %d/%d crate(s) of %s.",count,needed,cname),15,false,Group) + end + else + self:_SendMessage(string.format("Dropped %d %s(s).",count,cname),10,false,Group) + end + end + local loaded = {} + loaded.Troopsloaded = 0 + loaded.Cratesloaded = 0 + loaded.Cargo = {} + for _,_cargo in pairs (cargotable) do + local cargo = _cargo + local type = cargo:GetType() + local size = cargo:GetCratesNeeded() + if type == CTLD_CARGO.Enum.TROOPS or type == CTLD_CARGO.Enum.ENGINEERS then + table.insert(loaded.Cargo,_cargo) + loaded.Troopsloaded = loaded.Troopsloaded + size + end + if type == CTLD_CARGO.Enum.GCLOADABLE and not cargo:WasDropped() then + table.insert(loaded.Cargo,_cargo) + loaded.Cratesloaded = loaded.Cratesloaded + size + end + end + self.Loaded_Cargo[unitname] = nil + self.Loaded_Cargo[unitname] = loaded + + self:_UpdateUnitCargoMass(Unit) + self:_RefreshDropCratesMenu(Group,Unit) else - self:_SendMessage("Nothing loaded or not hovering within parameters!", 10, false, Group) - end + if IsHerc then + self:_SendMessage("Nothing loaded or not within airdrop parameters!", 10, false, Group) + else + self:_SendMessage("Nothing loaded or not hovering within parameters!", 10, false, Group) + end + end + return self end - return self -end --- (Internal) Function to build nearby crates. -- @param #CTLD self -- @param Wrapper.Group#GROUP Group -- @param Wrapper.Unit#UNIT Unit -- @param #boolean Engineering If true build is by an engineering team. -function CTLD:_BuildCrates(Group, Unit,Engineering) +-- @param #boolean MultiDrop If true and not engineering or FOB, vary position a bit. +function CTLD:_BuildCrates(Group, Unit,Engineering,MultiDrop) self:T(self.lid .. " _BuildCrates") -- avoid users trying to build from flying Hercs if self:IsFixedWing(Unit) and self.enableFixedWing and not Engineering then @@ -3939,12 +3959,13 @@ function CTLD:_BuildCrates(Group, Unit,Engineering) if build.CanBuild then self:_CleanUpCrates(crates,build,number) if self.buildtime and self.buildtime > 0 then - local buildtimer = TIMER:New(self._BuildObjectFromCrates,self,Group,Unit,build,false,Group:GetCoordinate()) + local buildtimer = TIMER:New(self._BuildObjectFromCrates,self,Group,Unit,build,false,Group:GetCoordinate(),MultiDrop) buildtimer:Start(self.buildtime) self:_SendMessage(string.format("Build started, ready in %d seconds!",self.buildtime),15,false,Group) self:__CratesBuildStarted(1,Group,Unit) + self:_RefreshDropTroopsMenu(Group,Unit) else - self:_BuildObjectFromCrates(Group,Unit,build) + self:_BuildObjectFromCrates(Group,Unit,build,false,nil,MultiDrop) end end end @@ -3983,13 +4004,14 @@ function CTLD:_PackCratesNearby(Group, Unit) _Group:Destroy() -- if a match is found destroy the Wrapper.Group#GROUP near the player self:_GetCrates(Group, Unit, _entry, nil, false, true) -- spawn the appropriate crates near the player self:_RefreshLoadCratesMenu(Group,Unit) -- call the refresher to show the crates in the menu - return self + return true end end end end end - return self + self:_SendMessage("Nothing to pack at this distance pilot!",10,false,Group) + return false end --- (Internal) Function to repair nearby vehicles / FOBs @@ -4082,7 +4104,8 @@ end -- @param #CTLD.Buildable Build -- @param #boolean Repair If true this is a repair and not a new build -- @param Core.Point#COORDINATE RepairLocation Location for repair (e.g. where the destroyed unit was) -function CTLD:_BuildObjectFromCrates(Group,Unit,Build,Repair,RepairLocation) +-- @param #boolean MultiDrop if true and not a repair, vary location a bit if not a FOB +function CTLD:_BuildObjectFromCrates(Group,Unit,Build,Repair,RepairLocation,MultiDrop) self:T(self.lid .. " _BuildObjectFromCrates") -- Spawn-a-crate-content if Group and Group:IsAlive() or (RepairLocation and not Repair) then @@ -4099,7 +4122,7 @@ function CTLD:_BuildObjectFromCrates(Group,Unit,Build,Repair,RepairLocation) if type(temptable) == "string" then temptable = {temptable} end - local zone = nil + local zone = nil -- Core.Zone#ZONE_RADIUS if RepairLocation and not Repair then -- timed build zone = ZONE_RADIUS:New(string.format("Build zone-%d",math.random(1,10000)),RepairLocation:GetVec2(),100) @@ -4108,6 +4131,10 @@ function CTLD:_BuildObjectFromCrates(Group,Unit,Build,Repair,RepairLocation) end --local randomcoord = zone:GetRandomCoordinate(35):GetVec2() local randomcoord = Build.Coord or zone:GetRandomCoordinate(35):GetVec2() + if MultiDrop and (not Repair) and canmove then + -- coordinate may be the same, avoid + local randomcoord = zone:GetRandomCoordinate(35):GetVec2() + end if Repair then randomcoord = RepairLocation:GetVec2() end @@ -4199,310 +4226,458 @@ function CTLD:_CleanUpCrates(Crates,Build,Number) return self end +--- (Internal) Helper - Drop **all** loaded crates nearby and build them. +-- @param Wrapper.Group#GROUP Group The calling group +-- @param Wrapper.Unit#UNIT Unit The calling unit +function CTLD:_DropAndBuild(Group,Unit) + if self.nobuildinloadzones then + if self:IsUnitInZone(Unit,CTLD.CargoZoneType.LOAD) then + self:_SendMessage("You cannot build in a loading area, Pilot!",10,false,Group) + return self + end + end + self:_UnloadCrates(Group,Unit) + timer.scheduleFunction(function() self:_BuildCrates(Group,Unit,false,true) end,{},timer.getTime()+1) + end + + --- (Internal) Helper - Drop a **single** crate set and build it. +-- @param Wrapper.Group#GROUP Group The calling group +-- @param Wrapper.Unit#UNIT Unit The calling unit +-- @param number setIndex Index of the crate-set to drop + function CTLD:_DropSingleAndBuild(Group,Unit,setIndex) + if self.nobuildinloadzones then + if self:IsUnitInZone(Unit,CTLD.CargoZoneType.LOAD) then + self:_SendMessage("You cannot build in a loading area, Pilot!",10,false,Group) + return self + end + end + self:_UnloadSingleCrateSet(Group,Unit,setIndex) + timer.scheduleFunction(function() self:_BuildCrates(Group,Unit,false) end,{},timer.getTime()+1) + end + +--- (Internal) Helper - Pack crates near the unit and load them. +-- @param Wrapper.Group#GROUP Group The calling group +-- @param Wrapper.Unit#UNIT Unit The calling unit +function CTLD:_PackAndLoad(Group,Unit) + if self.pilotmustopendoors and not UTILS.IsLoadingDoorOpen(Unit:GetName()) then + self:_SendMessage("You need to open the door(s) to load cargo!",10,false,Group) + return self + end + if not self:_PackCratesNearby(Group,Unit) then + return self + end + timer.scheduleFunction(function() self:_LoadCratesNearby(Group,Unit) end,{},timer.getTime()+1) + return self + end + +--- (Internal) Helper - Pack crates near the unit and then remove them. +-- @param Wrapper.Group#GROUP Group The calling group +-- @param Wrapper.Unit#UNIT Unit The calling unit +function CTLD:_PackAndRemove(Group,Unit) + if not self:_PackCratesNearby(Group,Unit) then + return self + end + timer.scheduleFunction(function() self:_RemoveCratesNearby(Group,Unit) end,{},timer.getTime()+1) + return self +end + +--- (Internal) Helper - get and load in one step +-- @param Wrapper.Group#GROUP Group The calling group +-- @param Wrapper.Unit#UNIT Unit The calling unit +function CTLD:_GetAndLoad(Group,Unit,cargoObj) + if self.pilotmustopendoors and not UTILS.IsLoadingDoorOpen(Unit:GetName()) then + self:_SendMessage("You need to open the door(s) to load cargo!",10,false,Group) + return self + end + self:_GetCrates(Group,Unit,cargoObj) + + timer.scheduleFunction(function() self:_LoadSingleCrateSet(Group,Unit,cargoObj.Name) end,{},timer.getTime()+1) +end + +-- @param Wrapper.Group#GROUP Group The player’s group that triggered the action +-- @param Wrapper.Unit#UNIT Unit The unit performing the pack-and-load +function CTLD:_GetAllAndLoad(Group,Unit) + if self.pilotmustopendoors and not UTILS.IsLoadingDoorOpen(Unit:GetName()) then + self:_SendMessage("You need to open the door(s) to load cargo!",10,false,Group) + return self + end + + timer.scheduleFunction(function() self:_LoadCratesNearby(Group,Unit) end,{},timer.getTime()+1) +end + --- (Internal) Housekeeping - Function to refresh F10 menus. -- @param #CTLD self -- @return #CTLD self function CTLD:_RefreshF10Menus() - self:T(self.lid .. " _RefreshF10Menus") - - -- 1) Gather all the pilot groups from our Set - local PlayerSet = self.PilotGroups - local PlayerTable = PlayerSet:GetSetObjects() - - -- 2) Rebuild the self.CtldUnits table - local _UnitList = {} - for _, groupObj in pairs(PlayerTable) do - local firstUnit = groupObj:GetFirstUnitAlive() - if firstUnit then - if firstUnit:IsPlayer() then - if firstUnit:IsHelicopter() or (self.enableFixedWing and self:IsFixedWing(firstUnit)) then - local _unit = firstUnit:GetName() - _UnitList[_unit] = _unit + self:T(self.lid .. " _RefreshF10Menus") + self.onestepmenu = self.onestepmenu or false -- hybrid toggle (default = false) + + -- 1) Gather all the pilot groups from our Set + local PlayerSet = self.PilotGroups + local PlayerTable = PlayerSet:GetSetObjects() + + -- 2) Rebuild the self.CtldUnits table + local _UnitList = {} + for _, groupObj in pairs(PlayerTable) do + local firstUnit = groupObj:GetFirstUnitAlive() + if firstUnit then + if firstUnit:IsPlayer() then + if firstUnit:IsHelicopter() or (self.enableFixedWing and self:IsFixedWing(firstUnit)) then + local _unit = firstUnit:GetName() + _UnitList[_unit] = _unit + end end end end - end - - -- 3) CA Units - if self.allowCATransport and self.CATransportSet then - for _,_clientobj in pairs(self.CATransportSet.Set) do - local client = _clientobj -- Wrapper.Client#CLIENT - if client:IsGround() then - local cname = client:GetName() - self:T(self.lid.."Adding: "..cname) - _UnitList[cname] = cname + + -- 3) CA Units + if self.allowCATransport and self.CATransportSet then + for _,_clientobj in pairs(self.CATransportSet.Set) do + local client = _clientobj -- Wrapper.Client#CLIENT + if client:IsGround() then + local cname = client:GetName() + self:T(self.lid.."Adding: "..cname) + _UnitList[cname] = cname + end end end + + self.CtldUnits = _UnitList + + -- subcats? + if self.usesubcats then + for _id,_cargo in pairs(self.Cargo_Crates) do + local entry = _cargo -- #CTLD_CARGO + if not self.subcats[entry.Subcategory] then + self.subcats[entry.Subcategory] = entry.Subcategory + end + end + for _id,_cargo in pairs(self.Cargo_Statics) do + local entry = _cargo -- #CTLD_CARGO + if not self.subcats[entry.Subcategory] then + self.subcats[entry.Subcategory] = entry.Subcategory + end + end + for _id,_cargo in pairs(self.Cargo_Troops) do + local entry = _cargo -- #CTLD_CARGO + if not self.subcatsTroop[entry.Subcategory] then + self.subcatsTroop[entry.Subcategory] = entry.Subcategory + end + end + end + + local menucount = 0 + local menus = {} + for _, _unitName in pairs(self.CtldUnits) do + if (not self.MenusDone[_unitName]) or (self.showstockinmenuitems == true) then + self:T(self.lid.."Menu not done yet for ".._unitName) + local _unit = UNIT:FindByName(_unitName) + if not _unit and self.allowCATransport then + _unit = CLIENT:FindByName(_unitName) + end + if _unit and _unit:IsAlive() then + local _group = _unit:GetGroup() + if _group then + self:T(self.lid.."Unit and Group exist") + local capabilities = self:_GetUnitCapabilities(_unit) + local cantroops = capabilities.troops + local cancrates = capabilities.crates + local unittype = _unit:GetTypeName() + local isHook = self:IsHook(_unit) + local nohookswitch = true + --local nohookswitch = not (isHook and self.enableChinookGCLoading) + -- Clear old topmenu if it existed + if _group.CTLDTopmenu then + _group.CTLDTopmenu:Remove() + _group.CTLDTopmenu = nil + end + local toptroops = nil + local topcrates = nil + local topmenu = MENU_GROUP:New(_group, "CTLD", nil) + _group.CTLDTopmenu = topmenu + + if cantroops then + local toptroops = MENU_GROUP:New(_group, "Manage Troops", topmenu) + local troopsmenu = MENU_GROUP:New(_group, "Load troops", toptroops) + _group.MyTopTroopsMenu = toptroops + + if self.usesubcats then + local subcatmenus = {} + for catName, _ in pairs(self.subcatsTroop) do + subcatmenus[catName] = MENU_GROUP:New(_group, catName, troopsmenu) + end + for _, cargoObj in pairs(self.Cargo_Troops) do + if not cargoObj.DontShowInMenu then + local stock = cargoObj:GetStock() + local menutext = cargoObj.Name + if (stock >= 0) and (self.showstockinmenuitems == true) then menutext = menutext.." ["..stock.."]" end + MENU_GROUP_COMMAND:New(_group, menutext, subcatmenus[cargoObj.Subcategory], self._LoadTroops, self, _group, _unit, cargoObj) + end + end + else + for _, cargoObj in pairs(self.Cargo_Troops) do + if not cargoObj.DontShowInMenu then + local stock = cargoObj:GetStock() + local menutext = cargoObj.Name + if (stock >= 0) and (self.showstockinmenuitems == true) then menutext = menutext.." ["..stock.."]" end + MENU_GROUP_COMMAND:New(_group, menutext, troopsmenu, self._LoadTroops, self, _group, _unit, cargoObj) + end + end + end + local dropTroopsMenu=MENU_GROUP:New(_group,"Drop Troops",toptroops):Refresh() + MENU_GROUP_COMMAND:New(_group,"Drop ALL troops",dropTroopsMenu,self._UnloadTroops,self,_group,_unit):Refresh() + MENU_GROUP_COMMAND:New(_group,"Extract troops",toptroops,self._ExtractTroops,self,_group,_unit):Refresh() + local uName=_unit:GetName() + local loadedData=self.Loaded_Cargo[uName] + if loadedData and loadedData.Cargo then + for i,cargoObj in ipairs(loadedData.Cargo) do + if cargoObj and (cargoObj:GetType()==CTLD_CARGO.Enum.TROOPS or cargoObj:GetType()==CTLD_CARGO.Enum.ENGINEERS) and not cargoObj:WasDropped() then + local name=cargoObj:GetName() or "Unknown" + local needed=cargoObj:GetCratesNeeded() or 1 + local cID=cargoObj:GetID() + local line=string.format("Drop: %s",name,needed,cID) + MENU_GROUP_COMMAND:New(_group,line,dropTroopsMenu,self._UnloadSingleTroopByID,self,_group,_unit,cID):Refresh() + end + end + end + end + if cancrates then + local topcrates = MENU_GROUP:New(_group, "Manage Crates", topmenu) + _group.MyTopCratesMenu = topcrates + + -- Build the “Get Crates” sub-menu items + local cratesmenu = MENU_GROUP:New(_group,"Get Crates",topcrates) + + if self.onestepmenu then + if self.usesubcats then + local subcatmenus = {} + for catName,_ in pairs(self.subcats) do + subcatmenus[catName] = MENU_GROUP:New(_group,catName,cratesmenu) + end + for _,cargoObj in pairs(self.Cargo_Crates) do + if not cargoObj.DontShowInMenu then + local txt = string.format("Crate %s (%dkg)",cargoObj.Name,cargoObj.PerCrateMass or 0) + if cargoObj.Location then txt = txt.."[R]" end + local stock = cargoObj:GetStock() + if stock>=0 and self.showstockinmenuitems then txt = txt.."["..stock.."]" end + local mSet = MENU_GROUP:New(_group,txt,subcatmenus[cargoObj.Subcategory]) + MENU_GROUP_COMMAND:New(_group,"Get and Load",mSet,self._GetAndLoad,self,_group,_unit,cargoObj) + MENU_GROUP_COMMAND:New(_group,"Get only",mSet,self._GetCrates,self,_group,_unit,cargoObj) + end + end + for _,cargoObj in pairs(self.Cargo_Statics) do + if not cargoObj.DontShowInMenu then + local txt = string.format("Crate %s (%dkg)",cargoObj.Name,cargoObj.PerCrateMass or 0) + if cargoObj.Location then txt = txt.."[R]" end + local stock = cargoObj:GetStock() + if stock>=0 and self.showstockinmenuitems then txt = txt.."["..stock.."]" end + local mSet = MENU_GROUP:New(_group,txt,subcatmenus[cargoObj.Subcategory]) + MENU_GROUP_COMMAND:New(_group,"Get and Load",mSet,self._GetAndLoad,self,_group,_unit,cargoObj) + MENU_GROUP_COMMAND:New(_group,"Get only",mSet,self._GetCrates,self,_group,_unit,cargoObj) + end + end + else + for _,cargoObj in pairs(self.Cargo_Crates) do + if not cargoObj.DontShowInMenu then + local txt = string.format("Crate %s (%dkg)",cargoObj.Name,cargoObj.PerCrateMass or 0) + if cargoObj.Location then txt = txt.."[R]" end + local stock = cargoObj:GetStock() + if stock>=0 and self.showstockinmenuitems then txt = txt.."["..stock.."]" end + local mSet = MENU_GROUP:New(_group,txt,cratesmenu) + MENU_GROUP_COMMAND:New(_group,"Get and Load",mSet,self._GetAndLoad,self,_group,_unit,cargoObj) + MENU_GROUP_COMMAND:New(_group,"Get only",mSet,self._GetCrates,self,_group,_unit,cargoObj) + end + end + for _,cargoObj in pairs(self.Cargo_Statics) do + if not cargoObj.DontShowInMenu then + local txt = string.format("Crate %s (%dkg)",cargoObj.Name,cargoObj.PerCrateMass or 0) + if cargoObj.Location then txt = txt.."[R]" end + local stock = cargoObj:GetStock() + if stock>=0 and self.showstockinmenuitems then txt = txt.."["..stock.."]" end + local mSet = MENU_GROUP:New(_group,txt,cratesmenu) + MENU_GROUP_COMMAND:New(_group,"Get and Load",mSet,self._GetAndLoad,self,_group,_unit,cargoObj) + MENU_GROUP_COMMAND:New(_group,"Get only",mSet,self._GetCrates,self,_group,_unit,cargoObj) + end + end + end + else + if self.usesubcats then + local subcatmenus = {} + for catName, _ in pairs(self.subcats) do + subcatmenus[catName] = MENU_GROUP:New(_group, catName, cratesmenu) -- fixed variable case + end + for _, cargoObj in pairs(self.Cargo_Crates) do + if not cargoObj.DontShowInMenu then + local txt = string.format("Crate %s (%dkg)", cargoObj.Name, cargoObj.PerCrateMass or 0) + if cargoObj.Location then txt = txt.."[R]" end + local stock = cargoObj:GetStock() + if stock >= 0 and self.showstockinmenuitems then txt = txt.."["..stock.."]" end + MENU_GROUP_COMMAND:New(_group, txt, subcatmenus[cargoObj.Subcategory], self._GetCrates, self, _group, _unit, cargoObj) + end + end + for _, cargoObj in pairs(self.Cargo_Statics) do + if not cargoObj.DontShowInMenu then + local txt = string.format("Crate %s (%dkg)", cargoObj.Name, cargoObj.PerCrateMass or 0) + if cargoObj.Location then txt = txt.."[R]" end + local stock = cargoObj:GetStock() + if stock >= 0 and self.showstockinmenuitems then txt = txt.."["..stock.."]" end + MENU_GROUP_COMMAND:New(_group, txt, subcatmenus[cargoObj.Subcategory], self._GetCrates, self, _group, _unit, cargoObj) + end + end + else + for _, cargoObj in pairs(self.Cargo_Crates) do + if not cargoObj.DontShowInMenu then + local txt = string.format("Crate %s (%dkg)", cargoObj.Name, cargoObj.PerCrateMass or 0) + if cargoObj.Location then txt = txt.."[R]" end + local stock = cargoObj:GetStock() + if stock >= 0 and self.showstockinmenuitems then txt = txt.."["..stock.."]" end + MENU_GROUP_COMMAND:New(_group, txt, cratesmenu, self._GetCrates, self, _group, _unit, cargoObj) + end + end + for _, cargoObj in pairs(self.Cargo_Statics) do + if not cargoObj.DontShowInMenu then + local txt = string.format("Crate %s (%dkg)", cargoObj.Name, cargoObj.PerCrateMass or 0) + if cargoObj.Location then txt = txt.."[R]" end + local stock = cargoObj:GetStock() + if stock >= 0 and self.showstockinmenuitems then txt = txt.."["..stock.."]" end + MENU_GROUP_COMMAND:New(_group, txt, cratesmenu, self._GetCrates, self, _group, _unit, cargoObj) + end + end + end + end + + local loadCratesMenu=MENU_GROUP:New(_group,"Load Crates",topcrates) + _group.MyLoadCratesMenu=loadCratesMenu + MENU_GROUP_COMMAND:New(_group,"Load ALL",loadCratesMenu,self._LoadCratesNearby,self,_group,_unit) + MENU_GROUP_COMMAND:New(_group,"Show loadable crates",loadCratesMenu,self._RefreshLoadCratesMenu,self,_group,_unit) + + local dropCratesMenu = MENU_GROUP:New(_group,"Drop Crates",topcrates) + topcrates.DropCratesMenu = dropCratesMenu + + if not self.nobuildmenu then + MENU_GROUP_COMMAND:New(_group, "Build crates", topcrates, self._BuildCrates, self, _group, _unit) + MENU_GROUP_COMMAND:New(_group, "Repair", topcrates, self._RepairCrates, self, _group, _unit):Refresh() + end + + local removecratesmenu = MENU_GROUP:New(_group, "Remove crates", topcrates) + MENU_GROUP_COMMAND:New(_group, "Remove crates nearby", removecratesmenu, self._RemoveCratesNearby, self, _group, _unit) + + if self.onestepmenu then + local mPack=MENU_GROUP:New(_group,"Pack crates",topcrates) + MENU_GROUP_COMMAND:New(_group,"Pack and Load",mPack,self._PackAndLoad,self,_group,_unit) + MENU_GROUP_COMMAND:New(_group,"Pack and Remove",mPack,self._PackAndRemove,self,_group,_unit) + MENU_GROUP_COMMAND:New(_group,"Pack only",mPack,self._PackCratesNearby,self,_group,_unit) + MENU_GROUP_COMMAND:New(_group, "List crates nearby", topcrates, self._ListCratesNearby, self, _group, _unit) + else + MENU_GROUP_COMMAND:New(_group, "Pack crates", topcrates, self._PackCratesNearby, self, _group, _unit) + MENU_GROUP_COMMAND:New(_group, "List crates nearby", topcrates, self._ListCratesNearby, self, _group, _unit) + end + + local uName = _unit:GetName() + local loadedData = self.Loaded_Cargo[uName] + if loadedData and loadedData.Cargo then + local cargoByName = {} + for _, cgo in pairs(loadedData.Cargo) do + if cgo and (not cgo:WasDropped()) then + local cname = cgo:GetName() + local cneeded = cgo:GetCratesNeeded() + cargoByName[cname] = cargoByName[cname] or { count=0, needed=cneeded } + cargoByName[cname].count = cargoByName[cname].count + 1 + end + end + for name, info in pairs(cargoByName) do + local line = string.format("Drop %s (%d/%d)", name, info.count, info.needed) + MENU_GROUP_COMMAND:New(_group, line, dropCratesMenu, self._UnloadSingleCrateSet, self, _group, _unit, name) + end + end + end + + ----------------------------------------------------- + -- Misc sub‐menus + ----------------------------------------------------- + MENU_GROUP_COMMAND:New(_group, "List boarded cargo", topmenu, self._ListCargo, self, _group, _unit) + MENU_GROUP_COMMAND:New(_group, "Inventory", topmenu, self._ListInventory, self, _group, _unit) + MENU_GROUP_COMMAND:New(_group, "List active zone beacons", topmenu, self._ListRadioBeacons, self, _group, _unit) + + local smoketopmenu = MENU_GROUP:New(_group, "Smokes, Flares, Beacons", topmenu) + MENU_GROUP_COMMAND:New(_group, "Smoke zones nearby", smoketopmenu, self.SmokeZoneNearBy, self, _unit, false) + local smokeself = MENU_GROUP:New(_group, "Drop smoke now", smoketopmenu) + MENU_GROUP_COMMAND:New(_group, "Red smoke", smokeself, self.SmokePositionNow, self, _unit, false, SMOKECOLOR.Red) + MENU_GROUP_COMMAND:New(_group, "Blue smoke", smokeself, self.SmokePositionNow, self, _unit, false, SMOKECOLOR.Blue) + MENU_GROUP_COMMAND:New(_group, "Green smoke", smokeself, self.SmokePositionNow, self, _unit, false, SMOKECOLOR.Green) + MENU_GROUP_COMMAND:New(_group, "Orange smoke", smokeself, self.SmokePositionNow, self, _unit, false, SMOKECOLOR.Orange) + MENU_GROUP_COMMAND:New(_group, "White smoke", smokeself, self.SmokePositionNow, self, _unit, false, SMOKECOLOR.White) + + MENU_GROUP_COMMAND:New(_group, "Flare zones nearby", smoketopmenu, self.SmokeZoneNearBy, self, _unit, true) + MENU_GROUP_COMMAND:New(_group, "Fire flare now", smoketopmenu, self.SmokePositionNow, self, _unit, true) + MENU_GROUP_COMMAND:New(_group, "Drop beacon now", smoketopmenu, self.DropBeaconNow, self, _unit):Refresh() + + if self:IsFixedWing(_unit) then + MENU_GROUP_COMMAND:New(_group, "Show flight parameters", topmenu, self._ShowFlightParams, self, _group, _unit):Refresh() + else + MENU_GROUP_COMMAND:New(_group, "Show hover parameters", topmenu, self._ShowHoverParams, self, _group, _unit):Refresh() + end + + -- Mark we built the menu + self.MenusDone[_unitName] = true + self:_RefreshLoadCratesMenu(_group,_unit) + self:_RefreshDropCratesMenu(_group,_unit) + + end -- if _group + end -- if _unit + else + self:T(self.lid .. " Menus already done for this group!") + end + end -- for all pilot units + + return self end - self.CtldUnits = _UnitList - - -- subcats? - if self.usesubcats then - for _id,_cargo in pairs(self.Cargo_Crates) do - local entry = _cargo -- #CTLD_CARGO - if not self.subcats[entry.Subcategory] then - self.subcats[entry.Subcategory] = entry.Subcategory - end - end - for _id,_cargo in pairs(self.Cargo_Statics) do - local entry = _cargo -- #CTLD_CARGO - if not self.subcats[entry.Subcategory] then - self.subcats[entry.Subcategory] = entry.Subcategory - end - end - for _id,_cargo in pairs(self.Cargo_Troops) do - local entry = _cargo -- #CTLD_CARGO - if not self.subcatsTroop[entry.Subcategory] then - self.subcatsTroop[entry.Subcategory] = entry.Subcategory - end - end - end - - local menucount = 0 - local menus = {} - for _, _unitName in pairs(self.CtldUnits) do - if (not self.MenusDone[_unitName]) or (self.showstockinmenuitems == true) then - self:T(self.lid.."Menu not done yet for ".._unitName) - local _unit = UNIT:FindByName(_unitName) - if not _unit and self.allowCATransport then - _unit = CLIENT:FindByName(_unitName) - end - if _unit and _unit:IsAlive() then - local _group = _unit:GetGroup() - if _group then - self:T(self.lid.."Unit and Group exist") - local capabilities = self:_GetUnitCapabilities(_unit) - local cantroops = capabilities.troops - local cancrates = capabilities.crates - local unittype = _unit:GetTypeName() - local isHook = self:IsHook(_unit) - local nohookswitch = true - --local nohookswitch = not (isHook and self.enableChinookGCLoading) - -- Clear old topmenu if it existed - if _group.CTLDTopmenu then - _group.CTLDTopmenu:Remove() - _group.CTLDTopmenu = nil - end - local toptroops = nil - local topcrates = nil - local topmenu = MENU_GROUP:New(_group, "CTLD", nil) - _group.CTLDTopmenu = topmenu - - if cantroops then - local toptroops = MENU_GROUP:New(_group, "Manage Troops", topmenu) - local troopsmenu = MENU_GROUP:New(_group, "Load troops", toptroops) - _group.MyTopTroopsMenu = toptroops - - if self.usesubcats then - local subcatmenus = {} - for catName, _ in pairs(self.subcatsTroop) do - subcatmenus[catName] = MENU_GROUP:New(_group, catName, troopsmenu) - end - for _, cargoObj in pairs(self.Cargo_Troops) do - if not cargoObj.DontShowInMenu then - local stock = cargoObj:GetStock() - local menutext = cargoObj.Name - if (stock >= 0) and (self.showstockinmenuitems == true) then menutext = menutext.." ["..stock.."]" end - MENU_GROUP_COMMAND:New(_group, menutext, subcatmenus[cargoObj.Subcategory], self._LoadTroops, self, _group, _unit, cargoObj) - end - end - else - for _, cargoObj in pairs(self.Cargo_Troops) do - if not cargoObj.DontShowInMenu then - local stock = cargoObj:GetStock() - local menutext = cargoObj.Name - if (stock >= 0) and (self.showstockinmenuitems == true) then menutext = menutext.." ["..stock.."]" end - MENU_GROUP_COMMAND:New(_group, menutext, troopsmenu, self._LoadTroops, self, _group, _unit, cargoObj) - - end - end - end - local dropTroopsMenu=MENU_GROUP:New(_group,"Drop Troops",toptroops):Refresh() - MENU_GROUP_COMMAND:New(_group,"Drop ALL troops",dropTroopsMenu,self._UnloadTroops,self,_group,_unit):Refresh() - MENU_GROUP_COMMAND:New(_group,"Extract troops",toptroops,self._ExtractTroops,self,_group,_unit):Refresh() - local uName=_unit:GetName() - local loadedData=self.Loaded_Cargo[uName] - if loadedData and loadedData.Cargo then - for i,cargoObj in ipairs(loadedData.Cargo) do - if cargoObj and (cargoObj:GetType()==CTLD_CARGO.Enum.TROOPS or cargoObj:GetType()==CTLD_CARGO.Enum.ENGINEERS) and not cargoObj:WasDropped() then - local name=cargoObj:GetName() or "Unknown" - local needed=cargoObj:GetCratesNeeded() or 1 - local cID=cargoObj:GetID() - local line=string.format("Drop: %s",name,needed,cID) - MENU_GROUP_COMMAND:New(_group,line,dropTroopsMenu,self._UnloadSingleTroopByID,self,_group,_unit,cID):Refresh() - end - end - end - end - if cancrates then - local topcrates = MENU_GROUP:New(_group, "Manage Crates", topmenu) - _group.MyTopCratesMenu = topcrates - - -- Build the “Get Crates” sub-menu items - local cratesmenu = MENU_GROUP:New(_group, "Get Crates", topcrates) - if self.usesubcats then - local subcatmenus = {} - for catName, _ in pairs(self.subcats) do - subcatmenus[catName] = MENU_GROUP:New(_group, catName, cratesmenu) - end - for _, cargoObj in pairs(self.Cargo_Crates) do - if not cargoObj.DontShowInMenu then - local txt = string.format("Crate %s (%dkg)", cargoObj.Name, cargoObj.PerCrateMass or 0) - if cargoObj.Location then txt = txt.."[R]" end - local stock = cargoObj:GetStock() - if stock >= 0 and self.showstockinmenuitems then txt = txt.."["..stock.."]" end - MENU_GROUP_COMMAND:New(_group, txt, subcatmenus[cargoObj.Subcategory], self._GetCrates, self, _group, _unit, cargoObj) - end - end - for _, cargoObj in pairs(self.Cargo_Statics) do - if not cargoObj.DontShowInMenu then - local txt = string.format("Crate %s (%dkg)", cargoObj.Name, cargoObj.PerCrateMass or 0) - if cargoObj.Location then txt = txt.."[R]" end - local stock = cargoObj:GetStock() - if stock >= 0 and self.showstockinmenuitems then txt = txt.."["..stock.."]" end - MENU_GROUP_COMMAND:New(_group, txt, subcatmenus[cargoObj.Subcategory], self._GetCrates, self, _group, _unit, cargoObj) - end - end - else - for _, cargoObj in pairs(self.Cargo_Crates) do - if not cargoObj.DontShowInMenu then - local txt = string.format("Crate %s (%dkg)", cargoObj.Name, cargoObj.PerCrateMass or 0) - if cargoObj.Location then txt = txt.."[R]" end - local stock = cargoObj:GetStock() - if stock >= 0 and self.showstockinmenuitems then txt = txt.."["..stock.."]" end - MENU_GROUP_COMMAND:New(_group, txt, cratesmenu, self._GetCrates, self, _group, _unit, cargoObj) - end - end - for _, cargoObj in pairs(self.Cargo_Statics) do - if not cargoObj.DontShowInMenu then - local txt = string.format("Crate %s (%dkg)", cargoObj.Name, cargoObj.PerCrateMass or 0) - if cargoObj.Location then txt = txt.."[R]" end - local stock = cargoObj:GetStock() - if stock >= 0 and self.showstockinmenuitems then txt = txt.."["..stock.."]" end - MENU_GROUP_COMMAND:New(_group, txt, cratesmenu, self._GetCrates, self, _group, _unit, cargoObj) - end - end - end - - local loadCratesMenu=MENU_GROUP:New(_group,"Load Crates",topcrates) - _group.MyLoadCratesMenu=loadCratesMenu - MENU_GROUP_COMMAND:New(_group,"Load ALL",loadCratesMenu,self._LoadCratesNearby,self,_group,_unit) - MENU_GROUP_COMMAND:New(_group,"Show loadable crates",loadCratesMenu,self._RefreshLoadCratesMenu,self,_group,_unit) - - local dropCratesMenu=MENU_GROUP:New(_group,"Drop Crates",topcrates) - topcrates.DropCratesMenu=dropCratesMenu - - if not self.nobuildmenu then - MENU_GROUP_COMMAND:New(_group, "Build crates", topcrates, self._BuildCrates, self, _group, _unit) - MENU_GROUP_COMMAND:New(_group, "Repair", topcrates, self._RepairCrates, self, _group, _unit):Refresh() - end - - local removecratesmenu = MENU_GROUP:New(_group, "Remove crates", topcrates) - MENU_GROUP_COMMAND:New(_group, "Remove crates nearby", removecratesmenu, self._RemoveCratesNearby, self, _group, _unit) - - MENU_GROUP_COMMAND:New(_group, "Pack crates", topcrates, self._PackCratesNearby, self, _group, _unit) - MENU_GROUP_COMMAND:New(_group, "List crates nearby", topcrates, self._ListCratesNearby, self, _group, _unit) - - local uName = _unit:GetName() - local loadedData = self.Loaded_Cargo[uName] - if loadedData and loadedData.Cargo then - local cargoByName = {} - for _, cgo in pairs(loadedData.Cargo) do - if cgo and (not cgo:WasDropped()) then - local cname = cgo:GetName() - local cneeded = cgo:GetCratesNeeded() - cargoByName[cname] = cargoByName[cname] or { count=0, needed=cneeded } - cargoByName[cname].count = cargoByName[cname].count + 1 - end - end - for name, info in pairs(cargoByName) do - local line = string.format("Drop %s (%d/%d)", name, info.count, info.needed) - MENU_GROUP_COMMAND:New(_group, line, dropCratesMenu, self._UnloadSingleCrateSet, self, _group, _unit, name) - end - end - end - - - - ----------------------------------------------------- - -- Misc sub‐menus - ----------------------------------------------------- - MENU_GROUP_COMMAND:New(_group, "List boarded cargo", topmenu, self._ListCargo, self, _group, _unit) - MENU_GROUP_COMMAND:New(_group, "Inventory", topmenu, self._ListInventory, self, _group, _unit) - MENU_GROUP_COMMAND:New(_group, "List active zone beacons", topmenu, self._ListRadioBeacons, self, _group, _unit) - - local smoketopmenu = MENU_GROUP:New(_group, "Smokes, Flares, Beacons", topmenu) - MENU_GROUP_COMMAND:New(_group, "Smoke zones nearby", smoketopmenu, self.SmokeZoneNearBy, self, _unit, false) - local smokeself = MENU_GROUP:New(_group, "Drop smoke now", smoketopmenu) - MENU_GROUP_COMMAND:New(_group, "Red smoke", smokeself, self.SmokePositionNow, self, _unit, false, SMOKECOLOR.Red) - MENU_GROUP_COMMAND:New(_group, "Blue smoke", smokeself, self.SmokePositionNow, self, _unit, false, SMOKECOLOR.Blue) - MENU_GROUP_COMMAND:New(_group, "Green smoke", smokeself, self.SmokePositionNow, self, _unit, false, SMOKECOLOR.Green) - MENU_GROUP_COMMAND:New(_group, "Orange smoke", smokeself, self.SmokePositionNow, self, _unit, false, SMOKECOLOR.Orange) - MENU_GROUP_COMMAND:New(_group, "White smoke", smokeself, self.SmokePositionNow, self, _unit, false, SMOKECOLOR.White) - - MENU_GROUP_COMMAND:New(_group, "Flare zones nearby", smoketopmenu, self.SmokeZoneNearBy, self, _unit, true) - MENU_GROUP_COMMAND:New(_group, "Fire flare now", smoketopmenu, self.SmokePositionNow, self, _unit, true) - MENU_GROUP_COMMAND:New(_group, "Drop beacon now", smoketopmenu, self.DropBeaconNow, self, _unit):Refresh() - - if self:IsFixedWing(_unit) then - MENU_GROUP_COMMAND:New(_group, "Show flight parameters", topmenu, self._ShowFlightParams, self, _group, _unit):Refresh() - else - MENU_GROUP_COMMAND:New(_group, "Show hover parameters", topmenu, self._ShowHoverParams, self, _group, _unit):Refresh() - end - - -- Mark we built the menu - self.MenusDone[_unitName] = true - self:_RefreshLoadCratesMenu(_group, _unit) - self:_RefreshDropCratesMenu(_group,_unit) - - end -- if _group - end -- if _unit - else - self:T(self.lid .. " Menus already done for this group!") - end - end -- for all pilot units - - return self -end - --- (Internal) Function to refresh the menu for load crates. Triggered from land/getcrate/pack and more -- @param #CTLD self -- @param Wrapper.Group#GROUP Group The calling group. -- @param Wrapper.Unit#UNIT Unit The calling unit. -- @return #CTLD self -function CTLD:_RefreshLoadCratesMenu(Group, Unit) - if not Group.MyLoadCratesMenu then return end - Group.MyLoadCratesMenu:RemoveSubMenus() - - local d = self.CrateDistance or 35 - local nearby, n = self:_FindCratesNearby(Group, Unit, d, true, true) - if n == 0 then - MENU_GROUP_COMMAND:New(Group, "No crates found! Rescan?", Group.MyLoadCratesMenu, function() self:_RefreshLoadCratesMenu(Group, Unit) end) - return - end - MENU_GROUP_COMMAND:New(Group, "Load ALL", Group.MyLoadCratesMenu, self._LoadCratesNearby, self, Group, Unit) - local cargoByName = {} - for _, crate in pairs(nearby) do - local cName = crate:GetName() - cargoByName[cName] = cargoByName[cName] or {} - table.insert(cargoByName[cName], crate) - end - - for cName, cList in pairs(cargoByName) do - local needed = cList[1]:GetCratesNeeded() or 1 - local found = #cList - - local line - if found >= needed then - line = string.format("Load %s", cName) - else - MENU_GROUP_COMMAND:New(Group, "Rescan?", Group.MyLoadCratesMenu, function() self:_RefreshLoadCratesMenu(Group, Unit) end) - line = string.format("Load %s (%d/%d)", cName, found, needed) +function CTLD:_RefreshLoadCratesMenu(Group,Unit) + if not Group.MyLoadCratesMenu then return end + Group.MyLoadCratesMenu:RemoveSubMenus() + + local d=self.CrateDistance or 35 + local nearby,n=self:_FindCratesNearby(Group,Unit,d,true,true) + if n==0 then + MENU_GROUP_COMMAND:New(Group,"No crates found! Rescan?",Group.MyLoadCratesMenu,function() self:_RefreshLoadCratesMenu(Group,Unit) end) + return + end + MENU_GROUP_COMMAND:New(Group,"Load ALL",Group.MyLoadCratesMenu,self._LoadCratesNearby,self,Group,Unit) + + local cargoByName={} + for _,crate in pairs(nearby) do + local name=crate:GetName() + cargoByName[name]=cargoByName[name] or{} + table.insert(cargoByName[name],crate) + end + + local lineIndex=1 + for cName,list in pairs(cargoByName) do + local needed=list[1]:GetCratesNeeded() or 1 + table.sort(list,function(a,b)return a:GetID()=needed then + label=string.format("%d. Load %s",lineIndex,cName) + i=i+needed + else + label=string.format("%d. Load %s (%d/%d)",lineIndex,cName,left,needed) + i=#list+1 + end + MENU_GROUP_COMMAND:New(Group,label,Group.MyLoadCratesMenu,self._LoadSingleCrateSet,self,Group,Unit,cName) + lineIndex=lineIndex+1 + end end - MENU_GROUP_COMMAND:New(Group, line, Group.MyLoadCratesMenu, self._LoadSingleCrateSet, self, Group, Unit, cName) end -end + --- -- Loads exactly `CratesNeeded` crates for one cargoName in range. @@ -4745,78 +4920,133 @@ end -- @param Wrapper.Unit#UNIT Unit The calling unit. -- @return #CTLD self function CTLD:_RefreshDropCratesMenu(Group, Unit) - if not Group.CTLDTopmenu then return end - local topCrates = Group.MyTopCratesMenu - if not topCrates then return end - if topCrates.DropCratesMenu then - topCrates.DropCratesMenu:RemoveSubMenus() - else - topCrates.DropCratesMenu = MENU_GROUP:New(Group, "Drop Crates", topCrates) - end - local dropCratesMenu = topCrates.DropCratesMenu - local loadedData = self.Loaded_Cargo[Unit:GetName()] - if not loadedData or not loadedData.Cargo then - MENU_GROUP_COMMAND:New(Group,"No crates to drop!",dropCratesMenu,function() end) - return - end - - local cargoByName={} - local dropableCrates=0 - for _,cObj in ipairs(loadedData.Cargo) do - if cObj and not cObj:WasDropped() then - local cType=cObj:GetType() - if cType~=CTLD_CARGO.Enum.TROOPS and cType~=CTLD_CARGO.Enum.ENGINEERS and cType~=CTLD_CARGO.Enum.GCLOADABLE then - local name=cObj:GetName()or"Unknown" - cargoByName[name]=cargoByName[name]or{} - table.insert(cargoByName[name],cObj) - dropableCrates=dropableCrates+1 + if not Group.CTLDTopmenu then return end + local topCrates = Group.MyTopCratesMenu + if not topCrates then return end + if topCrates.DropCratesMenu then + topCrates.DropCratesMenu:RemoveSubMenus() + else + topCrates.DropCratesMenu = MENU_GROUP:New(Group, "Drop Crates", topCrates) + end + + local dropCratesMenu = topCrates.DropCratesMenu + local loadedData = self.Loaded_Cargo[Unit:GetName()] + if not loadedData or not loadedData.Cargo then + MENU_GROUP_COMMAND:New(Group,"No crates to drop!",dropCratesMenu,function() end) + return + end + + local cargoByName={} + local dropableCrates=0 + for _,cObj in ipairs(loadedData.Cargo) do + if cObj and not cObj:WasDropped() then + local cType=cObj:GetType() + if cType~=CTLD_CARGO.Enum.TROOPS and cType~=CTLD_CARGO.Enum.ENGINEERS and cType~=CTLD_CARGO.Enum.GCLOADABLE then + local name=cObj:GetName()or"Unknown" + cargoByName[name]=cargoByName[name]or{} + table.insert(cargoByName[name],cObj) + dropableCrates=dropableCrates+1 + end + end + end + + if dropableCrates==0 then + MENU_GROUP_COMMAND:New(Group,"No crates to drop!",dropCratesMenu,function() end) + return + end + + ---------------------------------------------------------------------- + -- DEFAULT (“classic”) versus ONE-STEP behaviour + ---------------------------------------------------------------------- + if not self.onestepmenu then + -------------------------------------------------------------------- + -- classic menu + -------------------------------------------------------------------- + MENU_GROUP_COMMAND:New(Group,"Drop ALL crates",dropCratesMenu,self._UnloadCrates,self,Group,Unit) + + self.CrateGroupList=self.CrateGroupList or{} + self.CrateGroupList[Unit:GetName()]={} + + local lineIndex=1 + for cName,list in pairs(cargoByName) do + local needed=list[1]:GetCratesNeeded() or 1 + table.sort(list,function(a,b)return a:GetID()=needed then + local chunk={} + for n=i,i+needed-1 do + table.insert(chunk,list[n]) + end + local label=string.format("%d. %s",lineIndex,cName) + table.insert(self.CrateGroupList[Unit:GetName()],chunk) + local setIndex=#self.CrateGroupList[Unit:GetName()] + MENU_GROUP_COMMAND:New(Group,label,dropCratesMenu,self._UnloadSingleCrateSet,self,Group,Unit,setIndex) + i=i+needed + else + local chunk={} + for n=i,#list do + table.insert(chunk,list[n]) + end + local label=string.format("%d. %s %d/%d",lineIndex,cName,left,needed) + table.insert(self.CrateGroupList[Unit:GetName()],chunk) + local setIndex=#self.CrateGroupList[Unit:GetName()] + MENU_GROUP_COMMAND:New(Group,label,dropCratesMenu,self._UnloadSingleCrateSet,self,Group,Unit,setIndex) + i=#list+1 + end + lineIndex=lineIndex+1 + end + end + + else + -------------------------------------------------------------------- + -- one-step (enhanced) menu + -------------------------------------------------------------------- + local mAll=MENU_GROUP:New(Group,"Drop ALL crates",dropCratesMenu) + MENU_GROUP_COMMAND:New(Group,"Drop and build",mAll,self._DropAndBuild,self,Group,Unit) + MENU_GROUP_COMMAND:New(Group,"Drop only",mAll,self._UnloadCrates,self,Group,Unit) + + self.CrateGroupList=self.CrateGroupList or{} + self.CrateGroupList[Unit:GetName()]={} + + local lineIndex=1 + for cName,list in pairs(cargoByName) do + local needed=list[1]:GetCratesNeeded() or 1 + table.sort(list,function(a,b)return a:GetID()=needed then + local chunk={} + for n=i,i+needed-1 do + table.insert(chunk,list[n]) + end + local label=string.format("%d. %s",lineIndex,cName) + table.insert(self.CrateGroupList[Unit:GetName()],chunk) + local setIndex=#self.CrateGroupList[Unit:GetName()] + local mSet=MENU_GROUP:New(Group,label,dropCratesMenu) + MENU_GROUP_COMMAND:New(Group,"Drop and build",mSet,self._DropSingleAndBuild,self,Group,Unit,setIndex) + MENU_GROUP_COMMAND:New(Group,"Drop only",mSet,self._UnloadSingleCrateSet,self,Group,Unit,setIndex) + i=i+needed + else + local chunk={} + for n=i,#list do + table.insert(chunk,list[n]) + end + local label=string.format("%d. %s %d/%d",lineIndex,cName,left,needed) + table.insert(self.CrateGroupList[Unit:GetName()],chunk) + local setIndex=#self.CrateGroupList[Unit:GetName()] + MENU_GROUP_COMMAND:New(Group,label,dropCratesMenu,self._UnloadSingleCrateSet,self,Group,Unit,setIndex) + i=#list+1 + end + lineIndex=lineIndex+1 + end end end end - if dropableCrates==0 then - MENU_GROUP_COMMAND:New(Group,"No crates to drop!",dropCratesMenu,function() end) - return - end - - MENU_GROUP_COMMAND:New(Group,"Drop ALL crates",dropCratesMenu,self._UnloadCrates,self,Group,Unit) - self.CrateGroupList=self.CrateGroupList or{} - self.CrateGroupList[Unit:GetName()]={} - - local lineIndex=1 - for cName,list in pairs(cargoByName) do - local needed=list[1]:GetCratesNeeded() or 1 - table.sort(list,function(a,b)return a:GetID()=needed then - local chunk={} - for n=i,i+needed-1 do - table.insert(chunk,list[n]) - end - local label=string.format("%d. %s",lineIndex,cName) - table.insert(self.CrateGroupList[Unit:GetName()],chunk) - local setIndex=#self.CrateGroupList[Unit:GetName()] - MENU_GROUP_COMMAND:New(Group,label,dropCratesMenu,self._UnloadSingleCrateSet,self,Group,Unit,setIndex) - i=i+needed - else - local chunk={} - for n=i,#list do - table.insert(chunk,list[n]) - end - local label=string.format("%d. %s %d/%d",lineIndex,cName,left,needed) - table.insert(self.CrateGroupList[Unit:GetName()],chunk) - local setIndex=#self.CrateGroupList[Unit:GetName()] - MENU_GROUP_COMMAND:New(Group,label,dropCratesMenu,self._UnloadSingleCrateSet,self,Group,Unit,setIndex) - i=#list+1 - end - lineIndex=lineIndex+1 - end - end -end - --- (Internal) Function to unload a single Troop group by ID. -- @param #CTLD self -- @param Wrapper.Group#GROUP Group The calling group. @@ -8167,4 +8397,4 @@ end ------------------------------------------------------------------- -- End Ops.CTLD.lua -------------------------------------------------------------------- +------------------------------------------------------------------- \ No newline at end of file