Merge remote-tracking branch 'origin/master' into branch

This commit is contained in:
Applevangelist
2026-01-08 13:03:36 +01:00
+141 -83
View File
@@ -193,6 +193,7 @@ CTLD_CARGO = {
-- @param Wrapper.Unit#UNIT Unit
-- @return #boolean Outcome
function CTLD_CARGO:UnitCanCarry(Unit)
if not Unit then return false end
if self.TypeNames == nil then return true end
local typename = Unit:GetTypeName() or "none"
if self.TypeNames[typename] then
@@ -2910,6 +2911,28 @@ end
-- @return #CTLD self
function CTLD:_LoadTroopsQuantity(Group, Unit, Cargo, quantity)
local n = math.max(1, tonumber(quantity) or 1)
-- landed or hovering over load zone?
local grounded = not self:IsUnitInAir(Unit)
local hoverload = self:CanHoverLoad(Unit)
-- check if we are in LOAD zone
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 not inzone then
self:_SendMessage("You are not close enough to a logistics zone!", 10, false, Group)
if not self.debug then return self end
elseif not grounded and not hoverload then
self:_SendMessage("You need to land or hover in position to load!", 10, false, Group)
if not self.debug then return self end
elseif self.pilotmustopendoors and not UTILS.IsLoadingDoorOpen(Unit:GetName()) then
self:_SendMessage("You need to open the door(s) to load troops!", 10, false, Group)
if not self.debug then return self end
end
local prevSuppress = self.suppressmessages
self.suppressmessages = true
for i = 1, n do
@@ -2954,11 +2977,10 @@ function CTLD:_AddTroopQuantityMenus(Group, Unit, parentMenu, cargoObj)
if capacitySets < maxQuantity then maxQuantity = capacitySets end
end
for quantity = 1, maxQuantity do
local m = MENU_GROUP:New(Group, tostring(quantity), parentMenu)
if quantity == 1 then
MENU_GROUP_COMMAND:New(Group, "Load", m, self._LoadTroops, self, Group, Unit, cargoObj)
MENU_GROUP_COMMAND:New(Group, tostring(quantity), parentMenu, self._LoadTroops, self, Group, Unit, cargoObj)
else
MENU_GROUP_COMMAND:New(Group, "Load", m, self._LoadTroopsQuantity, self, Group, Unit, cargoObj, quantity)
MENU_GROUP_COMMAND:New(Group, tostring(quantity), parentMenu, self._LoadTroopsQuantity, self, Group, Unit, cargoObj, quantity)
end
end
return self
@@ -3137,6 +3159,17 @@ function CTLD:_AddCrateQuantityMenus(Group, Unit, parentMenu, cargoObj, stockSum
return self
end
--- User overrideable function to determine if a unit can get crates.
-- @param #CTLD self
-- @param Wrapper.Group#GROUP Group
-- @param Wrapper.Unit#UNIT Unit
-- @param #table Config Configuration entry for the unit.
-- @param #number quantity Number of crate sets requested.
-- @param #boolean quiet If true, do not send messages to the user.
function CTLD:CanGetUnits(Group, Unit, Config, quantity, quiet)
return true
end
--- (Internal) Spawn a “Get units” entry for a C-130J-30 at load zone.
-- @param #CTLD self
-- @param Wrapper.Group#GROUP Group
@@ -3167,6 +3200,9 @@ function CTLD:_C130GetUnits(Group, Unit, Name)
self:_SendMessage("You are not close enough to a logistics zone!",10,false,Group)
return self
end
if not self:CanGetUnits(Group, Unit, cfg, 1, false) then
return self
end
local coord = Unit:GetCoordinate() or Group:GetCoordinate()
local capabilities = self:_GetUnitCapabilities(Unit)
@@ -3514,9 +3550,9 @@ function CTLD:_GetCrates(Group, Unit, Cargo, number, drop, pack, quiet, suppress
Cargo:RemoveStock(requestedSets)
self:_RefreshCrateQuantityMenus(Group, Unit, Cargo)
end
local text = string.format("Crates for %s have been positioned near you!",cratename)
local text = string.format("%d crates for %s have been positioned near you!",number,cratename)
if drop then
text = string.format("Crates for %s have been dropped!",cratename)
text = string.format("%d crates for %s have been dropped!",number,cratename)
self:__CratesDropped(1, Group, Unit, droppedcargo)
else
if not quiet then
@@ -3612,7 +3648,7 @@ end
function CTLD:_ListCratesNearby( _group, _unit)
self:T(self.lid .. " _ListCratesNearby")
local finddist = self.CrateDistance or 35
local crates,number,loadedbygc,indexgc = self:_FindCratesNearby(_group,_unit, finddist,true,true) -- #table
local crates,number,loadedbygc,indexgc = self:_FindCratesNearby(_group,_unit, finddist,true,true,true) -- #table
if number > 0 or indexgc > 0 then
local text = REPORT:New("Crates Found Nearby:")
text:Add("------------------------------------------------------------")
@@ -3711,7 +3747,7 @@ end
function CTLD:_RemoveCratesNearby(_group, _unit)
self:T(self.lid.." _RemoveCratesNearby")
local finddist=self.CrateDistance or 35
local crates,number=self:_FindCratesNearby(_group,_unit,finddist,true,true)
local crates,number=self:_FindCratesNearby(_group,_unit,finddist,true,true,true)
if number>0 then
local removedIDs={}
local text=REPORT:New("Removing Crates Found Nearby:")
@@ -3824,7 +3860,7 @@ function CTLD:_FindCratesNearby( _group, _unit, _dist, _ignoreweight, ignoretype
self:T(self.lid .. " Loading restricted: " .. tostring(restricted))
local staticpos = static:GetCoordinate() --or dcsunitpos
local cando = cargo:UnitCanCarry(_unit)
if ignoretype == true then cando = true end
if ignoretype == true then cando = true restricted = false end
self:T(self.lid .. " Unit can carry: " .. tostring(cando))
--- Testing
local distance=self:_GetDistance(location,staticpos)
@@ -4618,7 +4654,7 @@ function CTLD:_BuildCrates(Group, Unit,Engineering,MultiDrop)
finddist=self.EngineerSearch
finddist=self.EngineerSearch
end
local crates,number = self:_FindCratesNearby(Group,Unit, finddist,true,true) -- #table
local crates,number = self:_FindCratesNearby(Group,Unit,finddist,true,true,not Engineering) -- #table
local buildables = {}
local foundbuilds = false
local canbuild = false
@@ -6411,9 +6447,11 @@ function CTLD:_RefreshDropCratesMenu(Group, Unit)
-- @param Wrapper.Unit#UNIT Unit The calling unit.
-- @param #number chunkID the Cargo ID
-- @return #CTLD self
function CTLD:_UnloadSingleTroopByID(Group, Unit, chunkID)
function CTLD:_UnloadSingleTroopByID(Group, Unit, chunkID, qty)
self:T(self.lid .. " _UnloadSingleTroopByID chunkID=" .. tostring(chunkID))
qty = qty or 1
local droppingatbase = false
local inzone, zonename, zone, distance = self:IsUnitInZone(Unit, CTLD.CargoZoneType.LOAD)
if not inzone then
@@ -6452,80 +6490,92 @@ function CTLD:_UnloadSingleTroopByID(Group, Unit, chunkID)
return self
end
local deployedTroopsByName = {}
local deployedEngineersByName = {}
-- Drop the FIRST cargo in that chunk
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
for n = 1, qty do
local foundCargo = chunk[1]
if not foundCargo then break 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
local offset = hoverunload and self.TroopUnloadDistHover or self.TroopUnloadDistGround
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
offset = self.TroopUnloadDistGroundHerc or 25
factor = foundCargo:GetCratesNeeded() or 1
zoneradius = Unit:GetVelocityMPS() or 100
end
if isHook then
offset = self.TroopUnloadDistGroundHook or 15
if hoverunload and self.TroopUnloadDistHoverHook then
offset = self.TroopUnloadDistHoverHook or 5
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
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
randomcoord:Translate(offset, Angle, nil, true)
end
local tempcount = 0
if isHook then
tempcount = self.ChinookTroopCircleRadius or 5
end
for _, _template in pairs(tmpl) 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)
:InitValidateAndRepositionGroundUnits(self.validateAndRepositionUnits)
: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)
deployedEngineersByName[name] = (deployedEngineersByName[name] or 0) + 1
else
deployedTroopsByName[name] = (deployedTroopsByName[name] or 0) + 1
end
table.remove(chunk, 1)
if #chunk == 0 then
self.TroopsIDToChunk[chunkID] = nil
break
end
randomcoord:Translate(offset, Angle, nil, true)
end
local tempcount = 0
if isHook then
tempcount = self.ChinookTroopCircleRadius or 5
local parts = {}
for nName,nCount in pairs(deployedTroopsByName) do
parts[#parts + 1] = tostring(nCount).."x Troops "..nName
end
for _, _template in pairs(tmpl) 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)
:InitValidateAndRepositionGroundUnits(self.validateAndRepositionUnits)
:OnSpawnGroup(function(grp) grp.spawntime = timer.getTime() end)
:SpawnFromVec2(randomcoord:GetVec2())
self:__TroopsDeployed(1, Group, Unit, self.DroppedTroops[self.TroopCounter], cType)
for nName,nCount in pairs(deployedEngineersByName) do
parts[#parts + 1] = tostring(nCount).."x Engineers "..nName
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)
if #parts > 0 then
self:_SendMessage("Dropped "..table.concat(parts, ", ").." into action!", 10, false, Group)
end
table.remove(chunk, 1)
if #chunk == 0 then
self.TroopsIDToChunk[chunkID] = nil
end
else
-- Return to base logic, remove ONLY the first cargo
self:_SendMessage("Troops have returned to base!", 10, false, Group)
@@ -6533,7 +6583,8 @@ function CTLD:_UnloadSingleTroopByID(Group, Unit, chunkID)
if self.TroopsIDToChunk and self.TroopsIDToChunk[chunkID] then
local chunk = self.TroopsIDToChunk[chunkID]
if #chunk > 0 then
for n = 1, qty do
if #chunk == 0 then break end
local firstObj = chunk[1]
local cName = firstObj:GetName()
local gentroops = self.Cargo_Troops
@@ -6548,9 +6599,9 @@ function CTLD:_UnloadSingleTroopByID(Group, Unit, chunkID)
end
firstObj:SetWasDropped(true)
table.remove(chunk, 1)
if #chunk == 0 then
self.TroopsIDToChunk[chunkID] = nil
end
end
if #chunk == 0 then
self.TroopsIDToChunk[chunkID] = nil
end
end
end
@@ -6625,15 +6676,22 @@ function CTLD:_RefreshDropTroopsMenu(Group, Unit)
for tName, objList in pairs(troopsByName) do
table.sort(objList, function(a,b) return a:GetID() < b:GetID() end)
local count = #objList
if count > 0 then
local chunkID = objList[1]:GetID()
self.TroopsIDToChunk[chunkID] = 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)
local label = string.format("Drop %s (%d)", tName, count)
if count == 1 then
MENU_GROUP_COMMAND:New(theGroup, label, dropTroopsMenu, self._UnloadSingleTroopByID, self, theGroup, theUnit, chunkID, 1)
else
local parentMenu = MENU_GROUP:New(theGroup, label, dropTroopsMenu)
for q = 1, count do
MENU_GROUP_COMMAND:New(theGroup, string.format("Drop (%d) %s", q, tName), parentMenu, self._UnloadSingleTroopByID, self, theGroup, theUnit, chunkID, q)
end
end
end
end
end
--- [Internal] Function to check if a template exists in the mission.
-- @param #CTLD self
-- @param #table temptable Table of string names