Update CTLD.lua

Option to unload single cargo items by @lekaa
This commit is contained in:
Thomas 2025-02-08 14:29:35 +01:00 committed by GitHub
parent 1156971d94
commit f74d25b31c
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194

View File

@ -2356,6 +2356,7 @@ function CTLD:_LoadTroops(Group, Unit, Cargotype, Inject)
table.insert(loaded.Cargo,loadcargotype)
self.Loaded_Cargo[unitname] = loaded
self:_SendMessage("Troops boarded!", 10, false, Group)
self:_RefreshDropTroopsMenu(Group,Unit)
self:__TroopsPickedUp(1,Group, Unit, Cargotype)
self:_UpdateUnitCargoMass(Unit)
Cargotype:RemoveStock()
@ -2590,6 +2591,7 @@ end
self.Loaded_Cargo[unitname] = loaded
self:ScheduleOnce(running,self._SendMessage,self,"Troops boarded!", 10, false, Group)
self:_SendMessage("Troops boarding!", 10, false, Group)
self:_RefreshDropTroopsMenu(Group,Unit)
self:_UpdateUnitCargoMass(Unit)
local groupname = nearestGroup:GetName()
self:__TroopsExtracted(running,Group, Unit, nearestGroup, groupname)
@ -3639,6 +3641,7 @@ function CTLD:_UnloadTroops(Group, Unit)
end
self.Loaded_Cargo[unitname] = nil
self.Loaded_Cargo[unitname] = loaded
self:_RefreshDropTroopsMenu(Group,Unit)
self:_UpdateUnitCargoMass(Unit)
else
if IsHerc then
@ -3721,6 +3724,7 @@ function CTLD:_UnloadCrates(Group, Unit)
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)
@ -4093,26 +4097,26 @@ end
-- @return #CTLD self
function CTLD:_RefreshF10Menus()
self:T(self.lid .. " _RefreshF10Menus")
local PlayerSet = self.PilotGroups -- Core.Set#SET_GROUP
local PlayerTable = PlayerSet:GetSetObjects() -- #table of #GROUP objects
-- rebuild units table
-- 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 _key, _group in pairs (PlayerTable) do
local _unit = _group:GetFirstUnitAlive() -- Wrapper.Unit#UNIT Asume that there is only one unit in the flight for players
if _unit then
if _unit:IsAlive() and _unit:IsPlayer() then
if _unit:IsHelicopter() or (self:IsHercules(_unit) and self.enableHercules) then --ensure no stupid unit entries here
local unitName = _unit:GetName()
_UnitList[unitName] = unitName
else
local unitName = _unit:GetName()
_UnitList[unitName] = nil
end
end -- end isAlive
end -- end if _unit
end -- end for
for _, groupObj in pairs(PlayerTable) do
local firstUnit = groupObj:GetFirstUnitAlive()
if firstUnit then
if firstUnit:IsPlayer() then
if firstUnit:IsHelicopter() or (self.enableHercules and self:IsHercules(firstUnit)) then
local _unit = firstUnit:GetName()
_UnitList[_unit] = _unit
end
end
end
end
self.CtldUnits = _UnitList
-- subcats?
if self.usesubcats then
for _id,_cargo in pairs(self.Cargo_Crates) do
@ -4134,207 +4138,555 @@ function CTLD:_RefreshF10Menus()
end
end
end
-- build unit menus
local menucount = 0
local menus = {}
local menus = {}
for _, _unitName in pairs(self.CtldUnits) do
if (not self.MenusDone[_unitName]) or (self.showstockinmenuitems == true) then
local _unit = UNIT:FindByName(_unitName) -- Wrapper.Unit#UNIT
if _unit then
local _group = _unit:GetGroup() -- Wrapper.Group#GROUP
if (not self.MenusDone[_unitName]) or (self.showstockinmenuitems == true) then
local _unit = UNIT:FindByName(_unitName)
if _unit and _unit:IsAlive() then
local _group = _unit:GetGroup()
if _group then
-- get chopper capabilities
local unittype = _unit:GetTypeName()
local capabilities = self:_GetUnitCapabilities(_unit) -- #CTLD.UnitTypeCapabilities
local cantroops = capabilities.troops
local cancrates = capabilities.crates
local isHook = self:IsHook(_unit)
--local nohookswitch = not (isHook and self.enableChinookGCLoading)
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
-- top menu
--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 topmenu = MENU_GROUP:New(_group,"CTLD",nil)
_group.CTLDTopmenu = topmenu
local toptroops = nil
local topcrates = nil
local topmenu = MENU_GROUP:New(_group, "CTLD", nil)
_group.CTLDTopmenu = topmenu
if cantroops then
toptroops = MENU_GROUP:New(_group,"Manage Troops",topmenu)
end
if cancrates then
topcrates = MENU_GROUP:New(_group,"Manage Crates",topmenu)
end
local listmenu = MENU_GROUP_COMMAND:New(_group,"List boarded cargo",topmenu, self._ListCargo, self, _group, _unit)
local invtry = MENU_GROUP_COMMAND:New(_group,"Inventory",topmenu, self._ListInventory, self, _group, _unit)
local rbcns = 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)
local smokemenu = MENU_GROUP_COMMAND:New(_group,"Smoke zones nearby",smoketopmenu, self.SmokeZoneNearBy, self, _unit, false)
local smokeself = MENU_GROUP:New(_group,"Drop smoke now",smoketopmenu)
local smokeselfred = MENU_GROUP_COMMAND:New(_group,"Red smoke",smokeself, self.SmokePositionNow, self, _unit, false,SMOKECOLOR.Red)
local smokeselfblue = MENU_GROUP_COMMAND:New(_group,"Blue smoke",smokeself, self.SmokePositionNow, self, _unit, false,SMOKECOLOR.Blue)
local smokeselfgreen = MENU_GROUP_COMMAND:New(_group,"Green smoke",smokeself, self.SmokePositionNow, self, _unit, false,SMOKECOLOR.Green)
local smokeselforange = MENU_GROUP_COMMAND:New(_group,"Orange smoke",smokeself, self.SmokePositionNow, self, _unit, false,SMOKECOLOR.Orange)
local smokeselfwhite = MENU_GROUP_COMMAND:New(_group,"White smoke",smokeself, self.SmokePositionNow, self, _unit, false,SMOKECOLOR.White)
local flaremenu = MENU_GROUP_COMMAND:New(_group,"Flare zones nearby",smoketopmenu, self.SmokeZoneNearBy, self, _unit, true)
local flareself = MENU_GROUP_COMMAND:New(_group,"Fire flare now",smoketopmenu, self.SmokePositionNow, self, _unit, true)
local beaconself = MENU_GROUP_COMMAND:New(_group,"Drop beacon now",smoketopmenu, self.DropBeaconNow, self, _unit):Refresh()
-- sub menus
-- sub menu troops management
if cantroops then
local troopsmenu = MENU_GROUP:New(_group,"Load troops",toptroops)
if self.usesubcats then
local subcatmenus = {}
for _name,_entry in pairs(self.subcatsTroop) do
subcatmenus[_name] = MENU_GROUP:New(_group,_name,troopsmenu)
end
for _,_entry in pairs(self.Cargo_Troops) do
local entry = _entry -- #CTLD_CARGO
local subcat = entry.Subcategory
local noshow = entry.DontShowInMenu
local stock = _entry:GetStock()
if not noshow then
menucount = menucount + 1
local menutext = entry.Name
if stock >= 0 and self.showstockinmenuitems == true then
menutext = menutext.." ["..stock.."]"
end
menus[menucount] = MENU_GROUP_COMMAND:New(_group,menutext,subcatmenus[subcat],self._LoadTroops, self, _group, _unit, entry)
end
end
else
for _,_entry in pairs(self.Cargo_Troops) do
local entry = _entry -- #CTLD_CARGO
local noshow = entry.DontShowInMenu
local stock = _entry:GetStock()
if not noshow then
menucount = menucount + 1
local menutext = entry.Name
if stock >= 0 and self.showstockinmenuitems == true then
menutext = menutext.." ["..stock.."]"
end
menus[menucount] = MENU_GROUP_COMMAND:New(_group,menutext,troopsmenu,self._LoadTroops, self, _group, _unit, entry)
end
end
end
local unloadmenu1 = MENU_GROUP_COMMAND:New(_group,"Drop troops",toptroops, self._UnloadTroops, self, _group, _unit):Refresh()
local extractMenu1 = MENU_GROUP_COMMAND:New(_group, "Extract troops", toptroops, self._ExtractTroops, self, _group, _unit):Refresh()
end
-- sub menu crates management
if cancrates then
if nohookswitch then
local loadmenu = MENU_GROUP_COMMAND:New(_group,"Load crates",topcrates, self._LoadCratesNearby, self, _group, _unit)
end
local cratesmenu = MENU_GROUP:New(_group,"Get Crates",topcrates)
local packmenu = MENU_GROUP_COMMAND:New(_group, "Pack crates", topcrates, self._PackCratesNearby, self, _group, _unit)
local removecratesmenu = MENU_GROUP:New(_group, "Remove crates", topcrates)
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 _name,_entry in pairs(self.subcats) do
subcatmenus[_name] = MENU_GROUP:New(_group,_name,cratesmenu)
for catName, _ in pairs(self.subcatsTroop) do
subcatmenus[catName] = MENU_GROUP:New(_group, catName, troopsmenu)
end
for _,_entry in pairs(self.Cargo_Crates) do
local entry = _entry -- #CTLD_CARGO
local subcat = entry.Subcategory
local noshow = entry.DontShowInMenu
local zone = entry.Location
local stock = _entry:GetStock()
if not noshow then
menucount = menucount + 1
local menutext = string.format("Crate %s (%dkg)",entry.Name,entry.PerCrateMass or 0)
if zone then
menutext = string.format("Crate %s (%dkg)[R]",entry.Name,entry.PerCrateMass or 0)
end
if stock >= 0 and self.showstockinmenuitems == true then
menutext = menutext.."["..stock.."]"
end
menus[menucount] = MENU_GROUP_COMMAND:New(_group,menutext,subcatmenus[subcat],self._GetCrates, self, _group, _unit, entry)
end
end
for _,_entry in pairs(self.Cargo_Statics) do
local entry = _entry -- #CTLD_CARGO
local subcat = entry.Subcategory
local noshow = entry.DontShowInMenu
local zone = entry.Location
local stock = _entry:GetStock()
if not noshow then
menucount = menucount + 1
local menutext = string.format("Crate %s (%dkg)",entry.Name,entry.PerCrateMass or 0)
if zone then
menutext = string.format("Crate %s (%dkg)[R]",entry.Name,entry.PerCrateMass or 0)
end
if stock >= 0 and self.showstockinmenuitems == true then
menutext = menutext.."["..stock.."]"
end
menus[menucount] = MENU_GROUP_COMMAND:New(_group,menutext,subcatmenus[subcat],self._GetCrates, self, _group, _unit, entry)
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 _,_entry in pairs(self.Cargo_Crates) do
local entry = _entry -- #CTLD_CARGO
local noshow = entry.DontShowInMenu
local zone = entry.Location
local stock = _entry:GetStock()
if not noshow then
menucount = menucount + 1
local menutext = string.format("Crate %s (%dkg)",entry.Name,entry.PerCrateMass or 0)
if zone then
menutext = string.format("Crate %s (%dkg)[R]",entry.Name,entry.PerCrateMass or 0)
end
if stock >= 0 and self.showstockinmenuitems == true then
menutext = menutext.."["..stock.."]"
end
menus[menucount] = MENU_GROUP_COMMAND:New(_group,menutext,cratesmenu,self._GetCrates, self, _group, _unit, entry)
end
end
for _,_entry in pairs(self.Cargo_Statics) do
local entry = _entry -- #CTLD_CARGO
local noshow = entry.DontShowInMenu
local zone = entry.Location
local stock = _entry:GetStock()
if not noshow then
menucount = menucount + 1
local menutext = string.format("Crate %s (%dkg)",entry.Name,entry.PerCrateMass or 0)
if zone then
menutext = string.format("Crate %s (%dkg)[R]",entry.Name,entry.PerCrateMass or 0)
end
if stock >= 0 and self.showstockinmenuitems == true then
menutext = menutext.."["..stock.."]"
end
menus[menucount] = MENU_GROUP_COMMAND:New(_group,menutext,cratesmenu,self._GetCrates, self, _group, _unit, entry)
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
listmenu = MENU_GROUP_COMMAND:New(_group,"List crates nearby",topcrates, self._ListCratesNearby, self, _group, _unit)
local removecrates = MENU_GROUP_COMMAND:New(_group,"Remove crates nearby",removecratesmenu, self._RemoveCratesNearby, self, _group, _unit)
local unloadmenu
if nohookswitch then
unloadmenu = MENU_GROUP_COMMAND:New(_group,"Drop crates",topcrates, self._UnloadCrates, self, _group, _unit)
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)
local cratesmenu = MENU_GROUP:New(_group, "Get Crates", topcrates)
_group.MyTopCratesMenu = topcrates
-- Show "Load crates" on 1 line
MENU_GROUP_COMMAND:New(_group, "Load crates", topcrates, self._LoadCratesNearby, self, _group, _unit)
MENU_GROUP_COMMAND:New(_group, "Pack crates", topcrates, self._PackCratesNearby, self, _group, _unit)
local removecratesmenu = MENU_GROUP:New(_group, "Remove crates", topcrates)
MENU_GROUP_COMMAND:New(_group, "Remove crates nearby", removecratesmenu, self._RemoveCratesNearby, self, _group, _unit)
-- Build the “Get Crates” sub-menu items
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
MENU_GROUP_COMMAND:New(_group, "List crates nearby", topcrates, self._ListCratesNearby, self, _group, _unit)
if not self.nobuildmenu then
local buildmenu = MENU_GROUP_COMMAND:New(_group,"Build crates",topcrates, self._BuildCrates, self, _group, _unit)
local repairmenu = MENU_GROUP_COMMAND:New(_group,"Repair",topcrates, self._RepairCrates, self, _group, _unit):Refresh()
elseif unloadmenu then
unloadmenu:Refresh()
MENU_GROUP_COMMAND:New(_group, "Repair", topcrates, self._RepairCrates, self, _group, _unit):Refresh()
MENU_GROUP_COMMAND:New(_group, "Build crates", topcrates, self._BuildCrates, self, _group, _unit)
end
-- Drop Crates sub-menu
local dropCratesMenu = MENU_GROUP:New(_group, "Drop Crates", topcrates)
MENU_GROUP_COMMAND:New(_group, "Drop ALL crates", dropCratesMenu, self._UnloadCrates, 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._UnloadSingleCrate, self, _group, _unit, name)
end
end
end
-----------------------------------------------------
-- Misc submenus
-----------------------------------------------------
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:IsHercules(_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
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
function CTLD:_UnloadSingleCrate(Group, Unit, CrateName)
if not self.dropcratesanywhere then
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
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 grounded = not self:IsUnitInAir(Unit)
local hoverunload = self:IsCorrectHover(Unit)
local isHerc = self:IsHercules(Unit)
local isHook = self:IsHook(Unit)
if isHerc and not isHook then
hoverunload = self:IsCorrectFlightParameters(Unit)
end
if not grounded and not hoverunload then
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
return self
end
local unitName = Unit:GetName()
local loadedData = self.Loaded_Cargo[unitName]
if not loadedData or not loadedData.Cargo then
self:_SendMessage("Nothing loaded!", 10, false, Group)
return self
end
local cargoList = loadedData.Cargo
local needed = 0
for _, cObj in ipairs(cargoList) do
if cObj:GetName() == CrateName and not cObj:WasDropped() then
needed = cObj:GetCratesNeeded() or 1
break
end
end
if needed < 1 then
self:_SendMessage(string.format("No %s crate found or already dropped!", CrateName), 10, false, Group)
return self
end
local matched = {}
for _, cObj in ipairs(cargoList) do
local t = cObj:GetType()
if t ~= CTLD_CARGO.Enum.TROOPS
and t ~= CTLD_CARGO.Enum.ENGINEERS
and t ~= CTLD_CARGO.Enum.GCLOADABLE
and (not cObj:WasDropped() or self.allowcratepickupagain)
and cObj:GetName() == CrateName
then
table.insert(matched, cObj)
end
end
if #matched < needed then
self:_SendMessage(string.format("You only have %d of %d %s crates needed!", #matched, needed, CrateName), 10, false, Group)
return self
end
local crateToUse = matched[1]
self:_GetCrates(Group, Unit, crateToUse, needed, true)
local used = 0
for _, cObj in ipairs(matched) do
if used < needed then
used = used + 1
cObj:SetWasDropped(true)
cObj:SetHasMoved(true)
end
end
local newList = {}
local newCratesCount = 0
for _, cObj in ipairs(cargoList) do
if not cObj:WasDropped() then
table.insert(newList, cObj)
local ct = cObj:GetType()
if ct ~= CTLD_CARGO.Enum.TROOPS and ct ~= CTLD_CARGO.Enum.ENGINEERS then
newCratesCount = newCratesCount + 1
end
end
end
loadedData.Cargo = newList
loadedData.Cratesloaded = newCratesCount
self.Loaded_Cargo[unitName] = loadedData
self:_UpdateUnitCargoMass(Unit)
self:_RefreshDropCratesMenu(Group,Unit)
return self
end
function CTLD:_RefreshDropCratesMenu(Group, Unit)
local theGroup = Group
local theUnit = Unit
if not theGroup.CTLDTopmenu then return end
local topCrates = theGroup.MyTopCratesMenu
if not topCrates then return end
if topCrates.DropCratesMenu then topCrates.DropCratesMenu:Remove() end
local dropCratesMenu = MENU_GROUP:New(theGroup, "Drop Crates", topCrates)
topCrates.DropCratesMenu = dropCratesMenu
MENU_GROUP_COMMAND:New(theGroup, "Drop ALL crates", dropCratesMenu, self._UnloadCrates, self, theGroup, theUnit)
local loadedData = self.Loaded_Cargo[Unit:GetName()]
if loadedData and loadedData.Cargo then
local cargoByName = {}
for _, cargoObj in pairs(loadedData.Cargo) do
if cargoObj and not cargoObj:WasDropped() then
local ctype = cargoObj:GetType()
if ctype ~= CTLD_CARGO.Enum.TROOPS and ctype ~= CTLD_CARGO.Enum.ENGINEERS and ctype ~= CTLD_CARGO.Enum.GCLOADABLE then
local cName = cargoObj:GetName()
local needed = cargoObj:GetCratesNeeded() or 1
if not cargoByName[cName] then
cargoByName[cName] = {count = 0, needed = needed}
end
cargoByName[cName].count = cargoByName[cName].count + 1
end
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(theGroup, line, dropCratesMenu, self._UnloadSingleCrate, self, theGroup, theUnit, name)
end
end
end
function CTLD:_UnloadSingleTroopByID(Group, Unit, cargoID)
self:T(self.lid .. " _UnloadSingleTroopByID for cargo ID " .. tostring(cargoID))
-- check if we are in LOAD zone
local droppingatbase = false
local canunload = true
-- Door check
if self.pilotmustopendoors and not UTILS.IsLoadingDoorOpen(Unit:GetName()) then
self:_SendMessage("You need to open the door(s) to unload troops!", 10, false, Group)
if not self.debug then return self end
end
local inzone, zonename, zone, distance = self:IsUnitInZone(Unit, CTLD.CargoZoneType.LOAD)
if not inzone then
inzone, zonename, zone, distance = self:IsUnitInZone(Unit, CTLD.CargoZoneType.SHIP)
end
if inzone then
droppingatbase = true
end
-- check for hover unload
local hoverunload = self:IsCorrectHover(Unit) -- if true we\'re hovering in parameters
local IsHerc = self:IsHercules(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
if not droppingatbase or self.debug then
------------------------------------------------------------------------
-- (NEW CODE FOR SINGLE DROP)
-- Instead of dropping ALL troop cargo, we only drop the one matching cargoID.
------------------------------------------------------------------------
local loadedCargoData = self.Loaded_Cargo[unitname] or {} -- #CTLD.LoadedCargo
local cargoList = loadedCargoData.Cargo or {}
local foundCargo = nil
for _, cargoObj in ipairs(cargoList) do
if (cargoObj:GetType() == CTLD_CARGO.Enum.TROOPS or cargoObj:GetType() == CTLD_CARGO.Enum.ENGINEERS)
and not cargoObj:WasDropped()
and (cargoObj:GetID() == cargoID)
then
foundCargo = cargoObj
break
end
end
if foundCargo then
local cType = foundCargo:GetType()
local name = foundCargo:GetName() or "none"
local temptable = foundCargo:GetTemplates() or {}
local zoneradius = self.troopdropzoneradius or 100 -- drop zone radius
local factor = 1
if IsHerc then
factor = foundCargo:GetCratesNeeded() or 1 -- spread a bit more if airdropping
zoneradius = Unit:GetVelocityMPS() or 100
end
local zone = ZONE_GROUP:New(string.format("Unload zone-%s",unitname), Group, zoneradius * factor)
local randomcoord = zone:GetRandomCoordinate(10, 30 * factor)
local heading = Group:GetHeading() or 0
-- Spawn troops left from us, closer when hovering, further off when landed
if hoverunload or grounded then
randomcoord = Group:GetCoordinate()
-- slightly left from us
local Angle = (heading + 270) % 360
if IsHerc or IsHook then Angle = (heading + 180) % 360 end
local offset = hoverunload and self.TroopUnloadDistHover or self.TroopUnloadDistGround
if IsHerc then offset = self.TroopUnloadDistGroundHerc or 25 end
if IsHook then
offset = self.TroopUnloadDistGroundHook or 15
if hoverunload and self.TroopUnloadDistHoverHook then
offset = self.TroopUnloadDistHoverHook or 5
end
end
if self:IsHercules(_unit) then
local hoverpars = MENU_GROUP_COMMAND:New(_group,"Show flight parameters",topmenu, self._ShowFlightParams, self, _group, _unit):Refresh()
else
local hoverpars = MENU_GROUP_COMMAND:New(_group,"Show hover parameters",topmenu, self._ShowHoverParams, self, _group, _unit):Refresh()
randomcoord:Translate(offset, Angle, nil, true)
end
local tempcount = 0
if IsHook then tempcount = self.ChinookTroopCircleRadius or 5 end -- 10m circle for the Chinook
for _, _template in pairs(temptable) do
self.TroopCounter = self.TroopCounter + 1
tempcount = tempcount + 1
local alias = string.format("%s-%d", _template, math.random(1,100000))
local rad = 2.5 + (tempcount * 2)
local Positions = self:_GetUnitPositions(randomcoord, rad, heading, _template)
self.DroppedTroops[self.TroopCounter] = SPAWN:NewWithAlias(_template, alias)
:InitDelayOff()
:InitSetUnitAbsolutePositions(Positions)
:OnSpawnGroup(function(grp) grp.spawntime = timer.getTime() end)
:SpawnFromVec2(randomcoord:GetVec2())
self:__TroopsDeployed(1, Group, Unit, self.DroppedTroops[self.TroopCounter], cType)
end
foundCargo:SetWasDropped(true)
if cType == CTLD_CARGO.Enum.ENGINEERS then
self.Engineers = self.Engineers + 1
local grpname = self.DroppedTroops[self.TroopCounter]:GetName()
self.EngineersInField[self.Engineers] = CTLD_ENGINEERING:New(name, grpname)
self:_SendMessage(string.format("Dropped Engineers %s into action!", name), 10, false, Group)
else
self:_SendMessage(string.format("Dropped Troops %s into action!", name), 10, false, Group)
end
else
-- We did not find any troop cargo with that ID
self:_SendMessage(string.format("No troop cargo with ID %d found or already dropped!", cargoID), 10, false, Group)
end
else
-- droppingatbase logic
self:_SendMessage("Troops have returned to base!", 10, false, Group)
self:__TroopsRTB(1, Group, Unit, zonename, zone)
--------------------------------------------------------------------
-- (NEW CODE FOR SINGLE DROP AT BASE)
-- If you want to return only the single cargo item with cargoID to stock
-- instead of returning all, you can do something similar here:
--------------------------------------------------------------------
local loadedCargoData = self.Loaded_Cargo[unitname] or {}
local cargoList = loadedCargoData.Cargo or {}
for _, cObj in ipairs(cargoList) do
if (cObj:GetType() == CTLD_CARGO.Enum.TROOPS or cObj:GetType() == CTLD_CARGO.Enum.ENGINEERS)
and (cObj:GetID() == cargoID)
then
-- Return this one cargo to stock
local cName = cObj:GetName()
local gentroops = self.Cargo_Troops
for _id, _troop in pairs(gentroops) do
if _troop.Name == cName then
local st = _troop:GetStock()
if st and tonumber(st) >= 0 then
_troop:AddStock()
end
end
end
self.MenusDone[_unitName] = true
end -- end group
end -- end unit
else -- menu build check
self:T(self.lid .. " Menus already done for this group!")
end -- end menu build check
end -- end for
-- Mark it as dropped so we remove it from the loaded cargo
cObj:SetWasDropped(true)
end
end
end
------------------------------------------------------------------------
-- cleanup load list
------------------------------------------------------------------------
local cargoList = self.Loaded_Cargo[unitname].Cargo
-- 1) Remove all dropped cargo (iterate backward for table.remove)
for i = #cargoList, 1, -1 do
if cargoList[i]:WasDropped() then
table.remove(cargoList, i)
end
end
-- 2) Recount
local troopsLoaded = 0
local cratesLoaded = 0
for _, cargo in ipairs(cargoList) do
local cType = cargo:GetType()
if cType == CTLD_CARGO.Enum.TROOPS or cType == CTLD_CARGO.Enum.ENGINEERS then
-- If each cargo item represents just 1 group (or “1 load of troops”):
troopsLoaded = troopsLoaded + 1
-- If you track “troops loaded” by `CratesNeeded()`,
-- then do: troopsLoaded = troopsLoaded + cargo:GetCratesNeeded()
else
cratesLoaded = cratesLoaded + 1
end
end
self.Loaded_Cargo[unitname].Troopsloaded = troopsLoaded
self.Loaded_Cargo[unitname].Cratesloaded = cratesLoaded
self:_RefreshDropTroopsMenu(Group, Unit)
else
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
end
--------------------
function CTLD:_RefreshDropTroopsMenu(Group, Unit)
local theGroup=Group
local theUnit=Unit
if not theGroup.CTLDTopmenu then return end
local topTroops=theGroup.MyTopTroopsMenu
if not topTroops then return end
if topTroops.DropTroopsMenu then topTroops.DropTroopsMenu:Remove() end
local dropTroopsMenu=MENU_GROUP:New(theGroup,"Drop Troops",topTroops)
topTroops.DropTroopsMenu=dropTroopsMenu
MENU_GROUP_COMMAND:New(theGroup,"Drop ALL troops",dropTroopsMenu,self._UnloadTroops,self,theGroup,theUnit)
local loadedData=self.Loaded_Cargo[theUnit:GetName()]
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 size=cargoObj:GetCratesNeeded()or 1
local cID=cargoObj:GetID()
local index = i
local line = string.format("Drop: %s (#%d)", name, index)
MENU_GROUP_COMMAND:New(theGroup,line,dropTroopsMenu,self._UnloadSingleTroopByID,self,theGroup,theUnit,cID)
end
end
end
end
--- [Internal] Function to check if a template exists in the mission.
-- @param #CTLD self
@ -5367,105 +5719,138 @@ end
-- }
function CTLD:_CountStockPlusInHeloPlusAliveGroups(Restock,Threshold)
local Troopstable = {}
-- generics
for _id,_cargo in pairs(self.Cargo_Crates) do
local generic = _cargo -- #CTLD_CARGO
for _id, _cargo in pairs(self.Cargo_Crates) do
local generic = _cargo
local genname = generic:GetName()
if generic and generic:GetStock0() > 0 and not Troopstable[genname] then
Troopstable[genname] = {
Stock0 = generic:GetStock0(),
Stock = generic:GetStock(),
StockR = generic:GetRelativeStock(),
Infield = 0,
Inhelo = 0,
Sum = generic:GetStock(),
}
Stock0 = generic:GetStock0(),
Stock = generic:GetStock(),
StockR = generic:GetRelativeStock(),
Infield = 0,
Inhelo = 0,
CratesInfield = 0,
Sum = generic:GetStock(),
}
if Restock == true then
Troopstable[genname].GenericCargo = generic
end
end
end
---
for _id,_cargo in pairs(self.Cargo_Troops) do
local generic = _cargo -- #CTLD_CARGO
for _id, _cargo in pairs(self.Cargo_Troops) do
local generic = _cargo
local genname = generic:GetName()
if generic and generic:GetStock0() > 0 and not Troopstable[genname] then
Troopstable[genname] = {
Stock0 = generic:GetStock0(),
Stock = generic:GetStock(),
StockR = generic:GetRelativeStock(),
Infield = 0,
Inhelo = 0,
Sum = generic:GetStock(),
}
Stock0 = generic:GetStock0(),
Stock = generic:GetStock(),
StockR = generic:GetRelativeStock(),
Infield = 0,
Inhelo = 0,
CratesInfield = 0,
Sum = generic:GetStock(),
}
if Restock == true then
Troopstable[genname].GenericCargo = generic
end
end
end
-- Troops & Built Crates
for _index, _group in pairs (self.DroppedTroops) do
for _index, _group in pairs(self.DroppedTroops) do
if _group and _group:IsAlive() then
self:T("Looking at ".._group:GetName() .. " in the field")
local generic = self:GetGenericCargoObjectFromGroupName(_group:GetName()) -- #CTLD_CARGO
self:T("Looking at " .. _group:GetName() .. " in the field")
local generic = self:GetGenericCargoObjectFromGroupName(_group:GetName())
if generic then
local genname = generic:GetName()
self:T("Found Generic "..genname .. " in the field. Adding.")
if generic:GetStock0() > 0 then -- don't count unlimited stock
Troopstable[genname].Infield = Troopstable[genname].Infield + 1
Troopstable[genname].Sum = Troopstable[genname].Infield + Troopstable[genname].Stock + Troopstable[genname].Inhelo
end
local genname = generic:GetName()
self:T("Found Generic " .. genname .. " in the field. Adding.")
if generic:GetStock0() > 0 then
Troopstable[genname].Infield = Troopstable[genname].Infield + 1
Troopstable[genname].Sum = Troopstable[genname].Infield + Troopstable[genname].Stock + Troopstable[genname].Inhelo
end
else
self:E(self.lid.."Group without Cargo Generic: ".._group:GetName())
self:E(self.lid .. "Group without Cargo Generic: " .. _group:GetName())
end
end
end
-- Helos
for _unitname,_loaded in pairs(self.Loaded_Cargo) do
for _unitname, _loaded in pairs(self.Loaded_Cargo) do
local _unit = UNIT:FindByName(_unitname)
if _unit and _unit:IsAlive() then
local unitname = _unit:GetName()
local loadedcargo = self.Loaded_Cargo[unitname].Cargo or {}
for _,_cgo in pairs (loadedcargo) do
local cargo = _cgo -- #CTLD_CARGO
for _, _cgo in pairs(loadedcargo) do
local cargo = _cgo
local type = cargo.CargoType
local gname = cargo.Name
local gcargo = self:_FindCratesCargoObject(gname) or self:_FindTroopsCargoObject(gname)
self:T("Looking at ".. gname .. " in the helo - type = "..type)
self:T("Looking at " .. gname .. " in the helo - type = " .. type)
if (type == CTLD_CARGO.Enum.TROOPS or type == CTLD_CARGO.Enum.ENGINEERS or type == CTLD_CARGO.Enum.VEHICLE or type == CTLD_CARGO.Enum.FOB) then
-- valid troops/engineers
if gcargo and gcargo:GetStock0() > 0 then -- don't count unlimited stock
self:T("Adding ".. gname .. " in the helo - type = "..type)
if gcargo and gcargo:GetStock0() > 0 then
self:T("Adding " .. gname .. " in the helo - type = " .. type)
if (type == CTLD_CARGO.Enum.TROOPS or type == CTLD_CARGO.Enum.ENGINEERS) then
Troopstable[gname].Inhelo = Troopstable[gname].Inhelo + 1
end
if (type == CTLD_CARGO.Enum.VEHICLE or type == CTLD_CARGO.Enum.FOB) then
-- maybe multiple crates of the same type
local counting = gcargo.CratesNeeded
local added = 1
if counting > 1 then
added = added/counting
added = added / counting
end
Troopstable[gname].Inhelo = Troopstable[gname].Inhelo + added
end
Troopstable[gname].Sum = Troopstable[gname].Infield + Troopstable[gname].Stock + Troopstable[gname].Inhelo
Troopstable[gname].Sum = Troopstable[gname].Infield + Troopstable[gname].Stock + Troopstable[gname].Inhelo + Troopstable[gname].CratesInfield
end
end
end
end
end
-- Restock?
if Restock == true then
local threshold = Threshold or 75
for _name,_data in pairs(Troopstable) do
if _data.StockR and _data.StockR < threshold then
if _data.GenericCargo then
_data.GenericCargo:SetStock(_data.Stock0) -- refill to start level
end
if self.Spawned_Cargo then
-- First pass: just add fractional amounts for each crate on the ground
for i = #self.Spawned_Cargo, 1, -1 do
local cargo = self.Spawned_Cargo[i]
if cargo and cargo:GetPositionable() and cargo:GetPositionable():IsAlive() then
local genname = cargo:GetName()
local gcargo = self:_FindCratesCargoObject(genname)
if Troopstable[genname] and gcargo and gcargo:GetStock0() > 0 then
local needed = gcargo.CratesNeeded or 1
local added = 1
if needed > 1 then
added = added / needed
end
Troopstable[genname].CratesInfield = Troopstable[genname].CratesInfield + added
Troopstable[genname].Sum = Troopstable[genname].Infield + Troopstable[genname].Stock
+ Troopstable[genname].Inhelo + Troopstable[genname].CratesInfield
end
end
end
for i = #self.Spawned_Cargo, 1, -1 do
local cargo = self.Spawned_Cargo[i]
if cargo and cargo:GetPositionable() and cargo:GetPositionable():IsAlive() then
local genname = cargo:GetName()
if Troopstable[genname] then
if Troopstable[genname].Inhelo == 0 and Troopstable[genname].CratesInfield < 1 then
Troopstable[genname].CratesInfield = 0
Troopstable[genname].Sum = Troopstable[genname].Stock
cargo:GetPositionable():Destroy(false)
table.remove(self.Spawned_Cargo, i)
local leftover = Troopstable[genname].Stock0 - (Troopstable[genname].Infield + Troopstable[genname].Inhelo + Troopstable[genname].CratesInfield)
if leftover < Troopstable[genname].Stock then
Troopstable[genname].Stock = leftover
end
Troopstable[genname].Sum = Troopstable[genname].Stock + Troopstable[genname].Infield + Troopstable[genname].Inhelo + Troopstable[genname].CratesInfield
end
end
end
end
end
-- Return
if Restock == true then
local threshold = Threshold or 75
for _name,_data in pairs(Troopstable) do
if _data.StockR and _data.StockR < threshold then
if _data.GenericCargo then
_data.GenericCargo:SetStock(_data.Stock0) -- refill to start level
end
end
end
end
return Troopstable
end