Update CTLD.lua

Reworked the logic of the menu.
Now it will Show be shown :
1. Ammo truck
2. Humvee
3. Ammo truck 1/2 -- 1/2 due to incomplete set.

And for the troops

Squad 8 (2) -- 2 set of squad 8, Selecting this will only deploy 1 set.
Squad 16

Changed so when loading troops, it will state 
Squad 8 boarded. same for the extraction.

I have tested it and it works
Heart8reaker also tested it and no issues so far.
This commit is contained in:
leka1986 2025-02-09 23:41:16 +01:00 committed by GitHub
parent 1d08bcf2e0
commit cad8f15b61
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194

View File

@ -2355,7 +2355,7 @@ function CTLD:_LoadTroops(Group, Unit, Cargotype, Inject)
loaded.Troopsloaded = loaded.Troopsloaded + troopsize loaded.Troopsloaded = loaded.Troopsloaded + troopsize
table.insert(loaded.Cargo,loadcargotype) table.insert(loaded.Cargo,loadcargotype)
self.Loaded_Cargo[unitname] = loaded self.Loaded_Cargo[unitname] = loaded
self:_SendMessage("Troops boarded!", 10, false, Group) self:_SendMessage(string.format("%s boarded!", cgoname), 10, false, Group)
self:_RefreshDropTroopsMenu(Group,Unit) self:_RefreshDropTroopsMenu(Group,Unit)
self:__TroopsPickedUp(1,Group, Unit, Cargotype) self:__TroopsPickedUp(1,Group, Unit, Cargotype)
self:_UpdateUnitCargoMass(Unit) self:_UpdateUnitCargoMass(Unit)
@ -2589,8 +2589,8 @@ end
loaded.Troopsloaded = loaded.Troopsloaded + troopsize loaded.Troopsloaded = loaded.Troopsloaded + troopsize
table.insert(loaded.Cargo,loadcargotype) table.insert(loaded.Cargo,loadcargotype)
self.Loaded_Cargo[unitname] = loaded self.Loaded_Cargo[unitname] = loaded
self:ScheduleOnce(running,self._SendMessage,self,"Troops boarded!", 10, false, Group) self:ScheduleOnce(running, self._SendMessage, self, string.format("%s boarded!", Cargotype.Name), 10, false, Group)
self:_SendMessage("Troops boarding!", 10, false, Group) self:_SendMessage(string.format("%s boarding!", Cargotype.Name), 10, false, Group)
self:_RefreshDropTroopsMenu(Group,Unit) self:_RefreshDropTroopsMenu(Group,Unit)
self:_UpdateUnitCargoMass(Unit) self:_UpdateUnitCargoMass(Unit)
local groupname = nearestGroup:GetName() local groupname = nearestGroup:GetName()
@ -3099,23 +3099,21 @@ function CTLD:_LoadCratesNearby(Group, Unit)
self:T(self.lid .. " _LoadCratesNearby") self:T(self.lid .. " _LoadCratesNearby")
-- load crates into heli -- load crates into heli
local group = Group -- Wrapper.Group#GROUP local group = Group -- Wrapper.Group#GROUP
local unit = Unit -- Wrapper.Unit#UNIT local unit = Unit -- Wrapper.Unit#UNIT
local unitname = unit:GetName() local unitname = unit:GetName()
-- see if this heli can load crates -- see if this heli can load crates
local unittype = unit:GetTypeName() local unittype = unit:GetTypeName()
local capabilities = self:_GetUnitCapabilities(Unit) -- #CTLD.UnitTypeCapabilities local capabilities = self:_GetUnitCapabilities(Unit) -- #CTLD.UnitTypeCapabilities
--local capabilities = self.UnitTypeCapabilities[unittype] -- #CTLD.UnitTypeCapabilities
local cancrates = capabilities.crates -- #boolean local cancrates = capabilities.crates -- #boolean
local cratelimit = capabilities.cratelimit -- #number local cratelimit = capabilities.cratelimit -- #number
local grounded = not self:IsUnitInAir(Unit) local grounded = not self:IsUnitInAir(Unit)
local canhoverload = self:CanHoverLoad(Unit) local canhoverload = self:CanHoverLoad(Unit)
-- Door check -- Door check
if self.pilotmustopendoors and not UTILS.IsLoadingDoorOpen(Unit:GetName()) then 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) self:_SendMessage("You need to open the door(s) to load cargo!", 10, false, Group)
if not self.debug then return self end if not self.debug then return self end
end end
--- cases ------------------------------- --- cases -------------------------------
-- Chopper can\'t do crates - bark & return -- Chopper can\'t do crates - bark & return
-- Chopper can do crates - -- Chopper can do crates -
@ -3123,77 +3121,97 @@ function CTLD:_LoadCratesNearby(Group, Unit)
-- --> hover or land if not forcedhover -- --> hover or land if not forcedhover
----------------------------------------- -----------------------------------------
if not cancrates then if not cancrates then
self:_SendMessage("Sorry this chopper cannot carry crates!", 10, false, Group) self:_SendMessage("Sorry this chopper cannot carry crates!", 10, false, Group)
elseif self.forcehoverload and not canhoverload then elseif self.forcehoverload and not canhoverload then
self:_SendMessage("Hover over the crates to pick them up!", 10, false, Group) self:_SendMessage("Hover over the crates to pick them up!", 10, false, Group)
elseif not grounded and not canhoverload then elseif not grounded and not canhoverload then
self:_SendMessage("Land or hover over the crates to pick them up!", 10, false, Group) self:_SendMessage("Land or hover over the crates to pick them up!", 10, false, Group)
else else
-- have we loaded stuff already? -- have we loaded stuff already?
local numberonboard = 0 local numberonboard = 0
local massonboard = 0 local loaded = {}
local loaded = {}
if self.Loaded_Cargo[unitname] then if self.Loaded_Cargo[unitname] then
loaded = self.Loaded_Cargo[unitname] -- #CTLD.LoadedCargo loaded = self.Loaded_Cargo[unitname] -- #CTLD.LoadedCargo
numberonboard = loaded.Cratesloaded or 0 numberonboard = loaded.Cratesloaded or 0
massonboard = self:_GetUnitCargoMass(Unit)
else else
loaded = {} -- #CTLD.LoadedCargo loaded = {}
loaded.Troopsloaded = 0 loaded.Troopsloaded = 0
loaded.Cratesloaded = 0 loaded.Cratesloaded = 0
loaded.Cargo = {} loaded.Cargo = {}
end end
-- get nearby crates -- get nearby crates
local finddist = self.CrateDistance or 35 local finddist = self.CrateDistance or 35
local nearcrates,number = self:_FindCratesNearby(Group,Unit,finddist,false,false) -- #table local nearcrates, number = self:_FindCratesNearby(Group,Unit,finddist,false,false)
self:T(self.lid .. " Crates found: " .. number) self:T(self.lid .. " Crates found: " .. number)
if number == 0 and self.hoverautoloading then if number == 0 and self.hoverautoloading then
return self -- exit return self
elseif number == 0 then elseif number == 0 then
self:_SendMessage("Sorry, no loadable crates nearby or max cargo weight reached!", 10, false, Group) self:_SendMessage("Sorry, no loadable crates nearby or max cargo weight reached!", 10, false, Group)
return self -- exit return self
elseif numberonboard == cratelimit then elseif numberonboard == cratelimit then
self:_SendMessage("Sorry, we are fully loaded!", 10, false, Group) self:_SendMessage("Sorry, we are fully loaded!", 10, false, Group)
return self -- exit return self
else else
-- go through crates and load
local capacity = cratelimit - numberonboard local capacity = cratelimit - numberonboard
local crateidsloaded = {} local crateidsloaded = {}
local loops = 0 local crateMap = {}
while loaded.Cratesloaded < cratelimit and loops < number do
loops = loops + 1 for _, cObj in pairs(nearcrates) do
local crateind = 0 if not cObj:HasMoved() or self.allowcratepickupagain then
-- get crate with largest index local cName = cObj:GetName() or "Unknown"
for _ind,_crate in pairs (nearcrates) do crateMap[cName] = crateMap[cName] or {}
if self.allowcratepickupagain then table.insert(crateMap[cName], cObj)
if _crate:GetID() > crateind and _crate.Positionable ~= nil then end
crateind = _crate:GetID() end
end for cName, crateList in pairs(crateMap) do
if capacity <= 0 then break end
table.sort(crateList, function(a, b) return a:GetID() > b:GetID() end)
local needed = crateList[1]:GetCratesNeeded() or 1
local totalFound = #crateList
local loadedHere = 0
while loaded.Cratesloaded < cratelimit and loadedHere < totalFound do
loadedHere = loadedHere + 1
local crate = crateList[loadedHere]
if crate and crate.Positionable then
loaded.Cratesloaded = loaded.Cratesloaded + 1
crate:SetHasMoved(true)
crate:SetWasDropped(false)
table.insert(loaded.Cargo, crate)
table.insert(crateidsloaded, crate:GetID())
-- destroy crate
crate:GetPositionable():Destroy(false)
crate.Positionable = nil
else else
if not _crate:HasMoved() and not _crate:WasDropped() and _crate:GetID() > crateind then loadedHere = loadedHere - 1
crateind = _crate:GetID() break
end
end end
end end
-- load one if we found one
if crateind > 0 then capacity = cratelimit - loaded.Cratesloaded
local crate = nearcrates[crateind] -- #CTLD_CARGO if loadedHere > 0 then
loaded.Cratesloaded = loaded.Cratesloaded + 1 local fullSets = math.floor(loadedHere / needed)
crate:SetHasMoved(true) local leftover = loadedHere % needed
crate:SetWasDropped(false)
table.insert(loaded.Cargo, crate) if needed > 1 then
table.insert(crateidsloaded,crate:GetID()) if fullSets > 0 and leftover == 0 then
-- destroy crate self:_SendMessage(string.format("Loaded %d %s.", fullSets, cName), 10, false, Group)
crate:GetPositionable():Destroy(false) elseif fullSets > 0 and leftover > 0 then
crate.Positionable = nil self:_SendMessage(string.format("Loaded %d %s(s), with %d leftover crate(s).", fullSets, cName, leftover), 10, false, Group)
self:_SendMessage(string.format("Crate ID %d for %s loaded!",crate:GetID(),crate:GetName()), 10, false, Group) else
table.remove(nearcrates,crate:GetID()) self:_SendMessage(string.format("Loaded only %d/%d crate(s) of %s.", loadedHere, needed, cName), 15, false, Group)
self:__CratesPickedUp(1, Group, Unit, crate) end
else
self:_SendMessage(string.format("Loaded %d %s(s).", loadedHere, cName), 10, false, Group)
end
end end
end end
self.Loaded_Cargo[unitname] = loaded self.Loaded_Cargo[unitname] = loaded
self:_UpdateUnitCargoMass(Unit) self:_UpdateUnitCargoMass(Unit)
self:_RefreshDropCratesMenu(Group,Unit) self:_RefreshDropCratesMenu(Group, Unit)
-- clean up real world crates -- clean up real world crates
self:_CleanupTrackedCrates(crateidsloaded) self:_CleanupTrackedCrates(crateidsloaded)
end end
@ -3201,6 +3219,7 @@ function CTLD:_LoadCratesNearby(Group, Unit)
return self return self
end end
--- (Internal) Function to clean up tracked cargo crates --- (Internal) Function to clean up tracked cargo crates
-- @param #CTLD self -- @param #CTLD self
-- @param #list crateIdsToRemove Table of IDs -- @param #list crateIdsToRemove Table of IDs
@ -3858,6 +3877,7 @@ end
-- @param #CTLD self -- @param #CTLD self
-- @param Wrapper.Group#GROUP Group -- @param Wrapper.Group#GROUP Group
-- @param Wrapper.Unit#UNIT Unit -- @param Wrapper.Unit#UNIT Unit
function CTLD:_PackCratesNearby(Group, Unit) function CTLD:_PackCratesNearby(Group, Unit)
self:T(self.lid .. " _PackCratesNearby") self:T(self.lid .. " _PackCratesNearby")
----------------------------------------- -----------------------------------------
@ -4294,7 +4314,7 @@ function CTLD:_RefreshF10Menus()
end end
for name, info in pairs(cargoByName) do for name, info in pairs(cargoByName) do
local line = string.format("Drop %s (%d/%d)", name, info.count, info.needed) 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) MENU_GROUP_COMMAND:New(_group, line, dropCratesMenu, self._UnloadSingleCrateSet, self, _group, _unit, name)
end end
end end
@ -4344,22 +4364,52 @@ end
-- @param #CTLD self -- @param #CTLD self
-- @param Wrapper.Group#GROUP Group The calling group. -- @param Wrapper.Group#GROUP Group The calling group.
-- @param Wrapper.Unit#UNIT Unit The calling unit. -- @param Wrapper.Unit#UNIT Unit The calling unit.
-- @param #string CrateName The name of the crate to unload -- @param #string setIndex The name of the crate to unload
-- @return #CTLD self -- @return #CTLD self
function CTLD:_UnloadSingleCrate(Group, Unit, CrateName) function CTLD:_UnloadSingleCrateSet(Group, Unit, setIndex)
self:T(self.lid .. " _UnloadSingleCrateSet")
-- Check if we are in a drop zone (unless we drop anywhere)
if not self.dropcratesanywhere then if not self.dropcratesanywhere then
local inzone, zoneName, zone, distance = self:IsUnitInZone(Unit, CTLD.CargoZoneType.DROP) local inzone, zoneName, zone, distance = self:IsUnitInZone(Unit, CTLD.CargoZoneType.DROP)
if not inzone then if not inzone then
self:_SendMessage("You are not close enough to a drop zone!", 10, false, Group) self:_SendMessage("You are not close enough to a drop zone!", 10, false, Group)
if not self.debug then if not self.debug then
return self return self
end end
end end
end end
-- Check if doors must be open
if self.pilotmustopendoors and not UTILS.IsLoadingDoorOpen(Unit:GetName()) then 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) self:_SendMessage("You need to open the door(s) to drop cargo!", 10, false, Group)
if not self.debug then return self end if not self.debug then return self end
end end
-- Check if the crate grouping data is available
local unitName = Unit:GetName()
if not self.CrateGroupList or not self.CrateGroupList[unitName] then
self:_SendMessage("No crate groups found for this unit!", 10, false, Group)
if not self.debug then return self end
return self
end
-- Find the selected chunk/set by index
local chunk = self.CrateGroupList[unitName][setIndex]
if not chunk then
self:_SendMessage("No crate set found or index invalid!", 10, false, Group)
if not self.debug then return self end
return self
end
-- Check if the chunk is empty
if #chunk == 0 then
self:_SendMessage("No crate found in that set!", 10, false, Group)
if not self.debug then return self end
return self
end
-- Check hover/airdrop/landed logic
local grounded = not self:IsUnitInAir(Unit) local grounded = not self:IsUnitInAir(Unit)
local hoverunload = self:IsCorrectHover(Unit) local hoverunload = self:IsCorrectHover(Unit)
local isHerc = self:IsHercules(Unit) local isHerc = self:IsHercules(Unit)
@ -4373,64 +4423,51 @@ function CTLD:_UnloadSingleCrate(Group, Unit, CrateName)
else else
self:_SendMessage("Nothing loaded or not hovering within parameters!", 10, false, Group) self:_SendMessage("Nothing loaded or not hovering within parameters!", 10, false, Group)
end end
if not self.debug then return self end
return self return self
end end
local unitName = Unit:GetName()
-- Get the first crate from this set
local crateObj = chunk[1]
if not crateObj then
self:_SendMessage("No crate found in that set!", 10, false, Group)
if not self.debug then return self end
return self
end
-- Perform the actual "drop" spawn
local needed = crateObj:GetCratesNeeded() or 1
self:_GetCrates(Group, Unit, crateObj, #chunk, true)
-- Mark all crates in the chunk as dropped
for _, cObj in ipairs(chunk) do
cObj:SetWasDropped(true)
cObj:SetHasMoved(true)
end
-- Rebuild the cargo list to remove the dropped crates
local loadedData = self.Loaded_Cargo[unitName] local loadedData = self.Loaded_Cargo[unitName]
if not loadedData or not loadedData.Cargo then if loadedData and loadedData.Cargo then
self:_SendMessage("Nothing loaded!", 10, false, Group) local newList = {}
return self local newCratesCount = 0
end for _, cObj in ipairs(loadedData.Cargo) do
local cargoList = loadedData.Cargo if not cObj:WasDropped() then
local needed = 0 table.insert(newList, cObj)
for _, cObj in ipairs(cargoList) do local ct = cObj:GetType()
if cObj:GetName() == CrateName and not cObj:WasDropped() then if ct ~= CTLD_CARGO.Enum.TROOPS and ct ~= CTLD_CARGO.Enum.ENGINEERS then
needed = cObj:GetCratesNeeded() or 1 newCratesCount = newCratesCount + 1
break end
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
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 end
loadedData.Cargo = newList
loadedData.Cratesloaded = newCratesCount
self.Loaded_Cargo[unitName] = loadedData
end end
loadedData.Cargo = newList
loadedData.Cratesloaded = newCratesCount -- Update cargo mass, refresh menu
self.Loaded_Cargo[unitName] = loadedData
self:_UpdateUnitCargoMass(Unit) self:_UpdateUnitCargoMass(Unit)
self:_RefreshDropCratesMenu(Group,Unit) self:_RefreshDropCratesMenu(Group, Unit)
return self return self
end end
@ -4445,29 +4482,67 @@ function CTLD:_RefreshDropCratesMenu(Group, Unit)
if not theGroup.CTLDTopmenu then return end if not theGroup.CTLDTopmenu then return end
local topCrates = theGroup.MyTopCratesMenu local topCrates = theGroup.MyTopCratesMenu
if not topCrates then return end if not topCrates then return end
if topCrates.DropCratesMenu then topCrates.DropCratesMenu:Remove() end if topCrates.DropCratesMenu then
topCrates.DropCratesMenu:Remove()
end
local dropCratesMenu = MENU_GROUP:New(theGroup, "Drop Crates", topCrates) local dropCratesMenu = MENU_GROUP:New(theGroup, "Drop Crates", topCrates)
topCrates.DropCratesMenu = dropCratesMenu topCrates.DropCratesMenu = dropCratesMenu
MENU_GROUP_COMMAND:New(theGroup, "Drop ALL crates", dropCratesMenu, self._UnloadCrates, self, theGroup, theUnit) MENU_GROUP_COMMAND:New(theGroup, "Drop ALL crates", dropCratesMenu, self._UnloadCrates, self, theGroup, theUnit)
local loadedData = self.Loaded_Cargo[Unit:GetName()] local loadedData = self.Loaded_Cargo[Unit:GetName()]
if loadedData and loadedData.Cargo then if not loadedData or not loadedData.Cargo then return end
local cargoByName = {}
for _, cargoObj in pairs(loadedData.Cargo) do local cargoByName = {}
if cargoObj and not cargoObj:WasDropped() then for _, cObj in ipairs(loadedData.Cargo) do
local ctype = cargoObj:GetType() if cObj and not cObj:WasDropped() then
if ctype ~= CTLD_CARGO.Enum.TROOPS and ctype ~= CTLD_CARGO.Enum.ENGINEERS and ctype ~= CTLD_CARGO.Enum.GCLOADABLE then local cType = cObj:GetType()
local cName = cargoObj:GetName() if cType ~= CTLD_CARGO.Enum.TROOPS and cType ~= CTLD_CARGO.Enum.ENGINEERS and cType ~= CTLD_CARGO.Enum.GCLOADABLE then
local needed = cargoObj:GetCratesNeeded() or 1 local name = cObj:GetName() or "Unknown"
if not cargoByName[cName] then cargoByName[name] = cargoByName[name] or {}
cargoByName[cName] = {count = 0, needed = needed} table.insert(cargoByName[name], cObj)
end
cargoByName[cName].count = cargoByName[cName].count + 1
end
end end
end end
for name, info in pairs(cargoByName) do end
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) self.CrateGroupList = self.CrateGroupList or {}
self.CrateGroupList[Unit:GetName()] = {}
-- A single global line index for ALL crate names
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() < b:GetID() end)
local i = 1
while i <= #list do
local left = (#list - i + 1)
if left >= needed then
local chunk = {}
for n = i, i + needed - 1 do
table.insert(chunk, list[n])
end
-- Now label uses the global lineIndex and increments after each chunk
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(theGroup, label, dropCratesMenu, self._UnloadSingleCrateSet, self, theGroup, theUnit, 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(theGroup, label, dropCratesMenu, self._UnloadSingleCrateSet, self, theGroup, theUnit, setIndex)
i = #list + 1
end
lineIndex = lineIndex + 1
end end
end end
end end
@ -4476,21 +4551,12 @@ end
-- @param #CTLD self -- @param #CTLD self
-- @param Wrapper.Group#GROUP Group The calling group. -- @param Wrapper.Group#GROUP Group The calling group.
-- @param Wrapper.Unit#UNIT Unit The calling unit. -- @param Wrapper.Unit#UNIT Unit The calling unit.
-- @param #number cargoId the Cargo ID -- @param #number chunkID the Cargo ID
-- @return #CTLD self -- @return #CTLD self
function CTLD:_UnloadSingleTroopByID(Group, Unit, cargoID) function CTLD:_UnloadSingleTroopByID(Group, Unit, chunkID)
self:T(self.lid .. " _UnloadSingleTroopByID for cargo ID " .. tostring(cargoID)) self:T(self.lid .. " _UnloadSingleTroopByID chunkID=" .. tostring(chunkID))
-- check if we are in LOAD zone
local droppingatbase = false 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) local inzone, zonename, zone, distance = self:IsUnitInZone(Unit, CTLD.CargoZoneType.LOAD)
if not inzone then if not inzone then
inzone, zonename, zone, distance = self:IsUnitInZone(Unit, CTLD.CargoZoneType.SHIP) inzone, zonename, zone, distance = self:IsUnitInZone(Unit, CTLD.CargoZoneType.SHIP)
@ -4499,120 +4565,116 @@ function CTLD:_UnloadSingleTroopByID(Group, Unit, cargoID)
droppingatbase = true droppingatbase = true
end end
-- check for hover unload if self.pilotmustopendoors and not UTILS.IsLoadingDoorOpen(Unit:GetName()) then
local hoverunload = self:IsCorrectHover(Unit) -- if true we\'re hovering in parameters self:_SendMessage("You need to open the door(s) to unload troops!", 10, false, Group)
local IsHerc = self:IsHercules(Unit) if not self.debug then return self end
local IsHook = self:IsHook(Unit)
if IsHerc and (not IsHook) then
-- no hover but airdrop here
hoverunload = self:IsCorrectFlightParameters(Unit)
end end
-- check if we\'re landed 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
local grounded = not self:IsUnitInAir(Unit) local grounded = not self:IsUnitInAir(Unit)
-- Get what we have loaded local unitName = Unit:GetName()
local unitname = Unit:GetName()
if self.Loaded_Cargo[unitname] and (grounded or hoverunload) then if self.Loaded_Cargo[unitName] and (grounded or hoverunload) then
if not droppingatbase or self.debug then if not droppingatbase or self.debug then
if not self.TroopsIDToChunk or not self.TroopsIDToChunk[chunkID] then
------------------------------------------------------------------------ self:_SendMessage(string.format("No troop cargo chunk found for ID %d!", chunkID), 10, false, Group)
-- (NEW CODE FOR SINGLE DROP) if not self.debug then return self end
-- Instead of dropping ALL troop cargo, we only drop the one matching cargoID. return self
------------------------------------------------------------------------
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 end
if foundCargo then local chunk = self.TroopsIDToChunk[chunkID]
local cType = foundCargo:GetType() if not chunk or #chunk == 0 then
local name = foundCargo:GetName() or "none" self:_SendMessage(string.format("Troop chunk is empty for ID %d!", chunkID), 10, false, Group)
local temptable = foundCargo:GetTemplates() or {} if not self.debug then return self end
local zoneradius = self.troopdropzoneradius or 100 -- drop zone radius return self
local factor = 1 end
if IsHerc then
factor = foundCargo:GetCratesNeeded() or 1 -- spread a bit more if airdropping -- Drop ONLY the FIRST cargo in that chunk
zoneradius = Unit:GetVelocityMPS() or 100 local foundCargo = chunk[1]
if not foundCargo then
self:_SendMessage(string.format("No troop cargo at chunk %d!", chunkID), 10, false, Group)
if not self.debug then return self end
return self
end
local cType = foundCargo:GetType()
local name = foundCargo:GetName() or "none"
local tmpl = foundCargo:GetTemplates() or {}
local zoneradius = self.troopdropzoneradius or 100
local factor = 1
if isHerc then
factor = foundCargo:GetCratesNeeded() or 1
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
if grounded or hoverunload then
randomcoord = Group:GetCoordinate()
local Angle = (heading + 270) % 360
if isHerc or isHook then
Angle = (heading + 180) % 360
end end
local zone = ZONE_GROUP:New(string.format("Unload zone-%s",unitname), Group, zoneradius * factor) local offset = hoverunload and self.TroopUnloadDistHover or self.TroopUnloadDistGround
local randomcoord = zone:GetRandomCoordinate(10, 30 * factor) if isHerc then
local heading = Group:GetHeading() or 0 offset = self.TroopUnloadDistGroundHerc or 25
-- Spawn troops left from us, closer when hovering, further off when landed end
if hoverunload or grounded then if isHook then
randomcoord = Group:GetCoordinate() offset = self.TroopUnloadDistGroundHook or 15
-- slightly left from us if hoverunload and self.TroopUnloadDistHoverHook then
local Angle = (heading + 270) % 360 offset = self.TroopUnloadDistHoverHook or 5
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 end
randomcoord:Translate(offset, Angle, nil, true)
end end
randomcoord:Translate(offset, Angle, nil, true)
end
local tempcount = 0 local tempcount = 0
if IsHook then tempcount = self.ChinookTroopCircleRadius or 5 end -- 10m circle for the Chinook if isHook then
for _, _template in pairs(temptable) do tempcount = self.ChinookTroopCircleRadius or 5
self.TroopCounter = self.TroopCounter + 1 end
tempcount = tempcount + 1 for _, _template in pairs(tmpl) do
local alias = string.format("%s-%d", _template, math.random(1,100000)) self.TroopCounter = self.TroopCounter + 1
local rad = 2.5 + (tempcount * 2) tempcount = tempcount + 1
local Positions = self:_GetUnitPositions(randomcoord, rad, heading, _template) local alias = string.format("%s-%d", _template, math.random(1,100000))
local rad = 2.5 + (tempcount * 2)
self.DroppedTroops[self.TroopCounter] = SPAWN:NewWithAlias(_template, alias) local Positions = self:_GetUnitPositions(randomcoord, rad, heading, _template)
:InitDelayOff() self.DroppedTroops[self.TroopCounter] = SPAWN:NewWithAlias(_template, alias)
:InitSetUnitAbsolutePositions(Positions) :InitDelayOff()
:OnSpawnGroup(function(grp) grp.spawntime = timer.getTime() end) :InitSetUnitAbsolutePositions(Positions)
:SpawnFromVec2(randomcoord:GetVec2()) :OnSpawnGroup(function(grp) grp.spawntime = timer.getTime() end)
:SpawnFromVec2(randomcoord:GetVec2())
self:__TroopsDeployed(1, Group, Unit, self.DroppedTroops[self.TroopCounter], cType) self:__TroopsDeployed(1, Group, Unit, self.DroppedTroops[self.TroopCounter], cType)
end end
foundCargo:SetWasDropped(true)
if cType == CTLD_CARGO.Enum.ENGINEERS then foundCargo:SetWasDropped(true)
self.Engineers = self.Engineers + 1 if cType == CTLD_CARGO.Enum.ENGINEERS then
local grpname = self.DroppedTroops[self.TroopCounter]:GetName() self.Engineers = self.Engineers + 1
self.EngineersInField[self.Engineers] = CTLD_ENGINEERING:New(name, grpname) self:_SendMessage(string.format("Dropped Engineers %s into action!", name), 10, false, Group)
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 else
-- We did not find any troop cargo with that ID self:_SendMessage(string.format("Dropped Troops %s into action!", name), 10, false, Group)
self:_SendMessage(string.format("No troop cargo with ID %d found or already dropped!", cargoID), 10, false, Group) end
table.remove(chunk, 1)
if #chunk == 0 then
self.TroopsIDToChunk[chunkID] = nil
end end
else else
-- droppingatbase logic -- Return to base logic, remove ONLY the first cargo
self:_SendMessage("Troops have returned to base!", 10, false, Group) self:_SendMessage("Troops have returned to base!", 10, false, Group)
self:__TroopsRTB(1, Group, Unit, zonename, zone) self:__TroopsRTB(1, Group, Unit, zonename, zone)
-------------------------------------------------------------------- if self.TroopsIDToChunk and self.TroopsIDToChunk[chunkID] then
-- (NEW CODE FOR SINGLE DROP AT BASE) local chunk = self.TroopsIDToChunk[chunkID]
-- If you want to return only the single cargo item with cargoID to stock if #chunk > 0 then
-- instead of returning all, you can do something similar here: local firstObj = chunk[1]
-------------------------------------------------------------------- local cName = firstObj:GetName()
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 local gentroops = self.Cargo_Troops
for _id, _troop in pairs(gentroops) do for _id, _troop in pairs(gentroops) do
if _troop.Name == cName then if _troop.Name == cName then
@ -4622,53 +4684,42 @@ function CTLD:_UnloadSingleTroopByID(Group, Unit, cargoID)
end end
end end
end end
-- Mark it as dropped so we remove it from the loaded cargo firstObj:SetWasDropped(true)
cObj:SetWasDropped(true) table.remove(chunk, 1)
if #chunk == 0 then
self.TroopsIDToChunk[chunkID] = nil
end
end end
end end
end end
------------------------------------------------------------------------ local cargoList = self.Loaded_Cargo[unitName].Cargo
-- 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 for i = #cargoList, 1, -1 do
if cargoList[i]:WasDropped() then if cargoList[i]:WasDropped() then
table.remove(cargoList, i) table.remove(cargoList, i)
end end
end end
-- 2) Recount
local troopsLoaded = 0 local troopsLoaded = 0
local cratesLoaded = 0 local cratesLoaded = 0
for _, cargo in ipairs(cargoList) do for _, cargo in ipairs(cargoList) do
local cType = cargo:GetType() local cT = cargo:GetType()
if cType == CTLD_CARGO.Enum.TROOPS or cType == CTLD_CARGO.Enum.ENGINEERS then if cT == CTLD_CARGO.Enum.TROOPS or cT == CTLD_CARGO.Enum.ENGINEERS then
-- If each cargo item represents just 1 group (or “1 load of troops”):
troopsLoaded = troopsLoaded + 1 troopsLoaded = troopsLoaded + 1
-- If you track “troops loaded” by `CratesNeeded()`,
-- then do: troopsLoaded = troopsLoaded + cargo:GetCratesNeeded()
else else
cratesLoaded = cratesLoaded + 1 cratesLoaded = cratesLoaded + 1
end end
end end
self.Loaded_Cargo[unitName].Troopsloaded = troopsLoaded
self.Loaded_Cargo[unitname].Troopsloaded = troopsLoaded self.Loaded_Cargo[unitName].Cratesloaded = cratesLoaded
self.Loaded_Cargo[unitname].Cratesloaded = cratesLoaded
self:_RefreshDropTroopsMenu(Group, Unit) self:_RefreshDropTroopsMenu(Group, Unit)
else else
if IsHerc then local isHerc = self:IsHercules(Unit)
if isHerc then
self:_SendMessage("Nothing loaded or not within airdrop parameters!", 10, false, Group) self:_SendMessage("Nothing loaded or not within airdrop parameters!", 10, false, Group)
else else
self:_SendMessage("Nothing loaded or not hovering within parameters!", 10, false, Group) self:_SendMessage("Nothing loaded or not hovering within parameters!", 10, false, Group)
end end
end end
return self return self
end end
@ -4678,31 +4729,46 @@ end
-- @param Wrapper.Unit#UNIT Unit The requesting unit. -- @param Wrapper.Unit#UNIT Unit The requesting unit.
-- @return #CTLD self -- @return #CTLD self
function CTLD:_RefreshDropTroopsMenu(Group, Unit) function CTLD:_RefreshDropTroopsMenu(Group, Unit)
local theGroup=Group local theGroup = Group
local theUnit=Unit local theUnit = Unit
if not theGroup.CTLDTopmenu then return end if not theGroup.CTLDTopmenu then return end
local topTroops=theGroup.MyTopTroopsMenu local topTroops = theGroup.MyTopTroopsMenu
if not topTroops then return end if not topTroops then return end
if topTroops.DropTroopsMenu then topTroops.DropTroopsMenu:Remove() end if topTroops.DropTroopsMenu then
local dropTroopsMenu=MENU_GROUP:New(theGroup,"Drop Troops",topTroops) topTroops.DropTroopsMenu:Remove()
topTroops.DropTroopsMenu=dropTroopsMenu end
MENU_GROUP_COMMAND:New(theGroup,"Drop ALL troops",dropTroopsMenu,self._UnloadTroops,self,theGroup,theUnit) local dropTroopsMenu = MENU_GROUP:New(theGroup, "Drop Troops", topTroops)
local loadedData=self.Loaded_Cargo[theUnit:GetName()] topTroops.DropTroopsMenu = dropTroopsMenu
if loadedData and loadedData.Cargo then MENU_GROUP_COMMAND:New(theGroup, "Drop ALL troops", dropTroopsMenu, self._UnloadTroops, self, theGroup, theUnit)
for i,cargoObj in ipairs(loadedData.Cargo) do
if cargoObj and (cargoObj:GetType()==CTLD_CARGO.Enum.TROOPS or cargoObj:GetType()==CTLD_CARGO.Enum.ENGINEERS) local loadedData = self.Loaded_Cargo[theUnit:GetName()]
and not cargoObj:WasDropped() if not loadedData or not loadedData.Cargo then return end
then
local name=cargoObj:GetName()or"Unknown" -- Gather troop cargo by name
local size=cargoObj:GetCratesNeeded()or 1 local troopsByName = {}
local cID=cargoObj:GetID() for _, cargoObj in ipairs(loadedData.Cargo) do
local index = i if cargoObj
local line = string.format("Drop: %s (#%d)", name, index) and (cargoObj:GetType() == CTLD_CARGO.Enum.TROOPS or cargoObj:GetType() == CTLD_CARGO.Enum.ENGINEERS)
MENU_GROUP_COMMAND:New(theGroup,line,dropTroopsMenu,self._UnloadSingleTroopByID,self,theGroup,theUnit,cID) and not cargoObj:WasDropped()
end then
local name = cargoObj:GetName() or "Unknown"
troopsByName[name] = troopsByName[name] or {}
table.insert(troopsByName[name], cargoObj)
end end
end end
return self
self.TroopsIDToChunk = self.TroopsIDToChunk or {}
for tName, objList in pairs(troopsByName) do
table.sort(objList, function(a,b) return a:GetID() < b:GetID() end)
local count = #objList
local chunkID = objList[1]:GetID()
self.TroopsIDToChunk[chunkID] = objList
local label = string.format("Drop %s (%d)", tName, count)
MENU_GROUP_COMMAND:New(theGroup, label, dropTroopsMenu, self._UnloadSingleTroopByID, self, theGroup, theUnit, chunkID)
end
end end
--- [Internal] Function to check if a template exists in the mission. --- [Internal] Function to check if a template exists in the mission.