mirror of
https://github.com/FlightControl-Master/MOOSE.git
synced 2025-10-29 16:58:06 +00:00
Validate and Reposition Ground Units algorithm
[ADDED] UTILS.ValidateAndRepositionGroundUnits [ADDED] SPAWN:InitValidateAndRepositionGroundUnits [ADDED] CTLD.validateAndRepositionUnits
This commit is contained in:
parent
b9cf1e46af
commit
67cb844550
@ -1049,6 +1049,22 @@ function SPAWN:InitSetUnitAbsolutePositions(Positions)
|
||||
return self
|
||||
end
|
||||
|
||||
|
||||
--- Uses Disposition and other fallback logic to find better ground positions for ground units.
|
||||
--- NOTE: This is not a spawn randomizer.
|
||||
--- It will try to find clear ground locations avoiding trees, water, roads, runways, map scenery, statics and other units in the area.
|
||||
--- Maintains the original layout and unit positions as close as possible by searching for the next closest valid position to each unit.
|
||||
-- @param #boolean OnOff Enable/disable the feature.
|
||||
-- @param #number MaxRadius (Optional) Max radius to search for valid ground locations in meters. Default is double the max radius of the units.
|
||||
-- @param #number Spacing (Optional) Minimum spacing between units in meters. Default is 5% of the search radius or 5 meters, whichever is larger.
|
||||
-- @return #SPAWN
|
||||
function SPAWN:InitValidateAndRepositionGroundUnits(OnOff, MaxRadius, Spacing)
|
||||
self.SpawnValidateAndRepositionGroundUnits = OnOff
|
||||
self.SpawnValidateAndRepositionGroundUnitsRadius = MaxRadius
|
||||
self.SpawnValidateAndRepositionGroundUnitsSpacing = Spacing
|
||||
return self
|
||||
end
|
||||
|
||||
--- This method is rather complicated to understand. But I'll try to explain.
|
||||
-- This method becomes useful when you need to spawn groups with random templates of groups defined within the mission editor,
|
||||
-- but they will all follow the same Template route and have the same prefix name.
|
||||
@ -1829,7 +1845,13 @@ function SPAWN:SpawnWithIndex( SpawnIndex, NoBirth )
|
||||
if self.SpawnHiddenOnMap then
|
||||
SpawnTemplate.hidden=self.SpawnHiddenOnMap
|
||||
end
|
||||
|
||||
|
||||
if self.SpawnValidateAndRepositionGroundUnits then
|
||||
local units = SpawnTemplate.units
|
||||
local gPos = { x = SpawnTemplate.x, y = SpawnTemplate.y }
|
||||
UTILS.ValidateAndRepositionGroundUnits(gPos, units, self.SpawnValidateAndRepositionGroundUnitsRadius, self.SpawnValidateAndRepositionGroundUnitsSpacing)
|
||||
end
|
||||
|
||||
-- Set country, coalition and category.
|
||||
SpawnTemplate.CategoryID = self.SpawnInitCategory or SpawnTemplate.CategoryID
|
||||
SpawnTemplate.CountryID = self.SpawnInitCountry or SpawnTemplate.CountryID
|
||||
|
||||
@ -1564,7 +1564,9 @@ function CTLD:New(Coalition, Prefixes, Alias)
|
||||
self.FixedMinAngels = 165 -- for troop/cargo drop via chute
|
||||
self.FixedMaxAngels = 2000 -- for troop/cargo drop via chute
|
||||
self.FixedMaxSpeed = 77 -- 280 kph or 150kn eq 77 mps
|
||||
|
||||
|
||||
self.validateAndRepositionUnits = false -- 280 kph or 150kn eq 77 mps
|
||||
|
||||
-- message suppression
|
||||
self.suppressmessages = false
|
||||
|
||||
@ -3735,6 +3737,7 @@ function CTLD:_UnloadTroops(Group, Unit)
|
||||
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],type)
|
||||
@ -4181,11 +4184,13 @@ function CTLD:_BuildObjectFromCrates(Group,Unit,Build,Repair,RepairLocation,Mult
|
||||
self.DroppedTroops[self.TroopCounter] = SPAWN:NewWithAlias(_template,alias)
|
||||
--:InitRandomizeUnits(true,20,2)
|
||||
:InitDelayOff()
|
||||
:InitValidateAndRepositionGroundUnits(self.validateAndRepositionUnits)
|
||||
:OnSpawnGroup(function(grp) grp.spawntime = timer.getTime() end)
|
||||
:SpawnFromVec2(randomcoord)
|
||||
else -- don't random position of e.g. SAM units build as FOB
|
||||
self.DroppedTroops[self.TroopCounter] = SPAWN:NewWithAlias(_template,alias)
|
||||
:InitDelayOff()
|
||||
:InitValidateAndRepositionGroundUnits(self.validateAndRepositionUnits)
|
||||
:OnSpawnGroup(function(grp) grp.spawntime = timer.getTime() end)
|
||||
:SpawnFromVec2(randomcoord)
|
||||
end
|
||||
@ -5211,6 +5216,7 @@ function CTLD:_UnloadSingleTroopByID(Group, Unit, chunkID)
|
||||
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)
|
||||
|
||||
@ -4871,3 +4871,124 @@ function UTILS.FindNearestPointOnCircle(Vec1,Radius,Vec2)
|
||||
|
||||
return {x=qx, y=qy}
|
||||
end
|
||||
|
||||
--- This function uses Disposition and other fallback logic to find better ground positions for a group of ground units.
|
||||
--- NOTE: This is not a spawn randomizer.
|
||||
--- It will try to find clear ground locations avoiding trees, water, roads, runways, map scenery, statics and other units in the area and modifies the provided positions table.
|
||||
--- Maintains the original layout and unit positions as close as possible by searching for the next closest valid position to each unit.
|
||||
-- @param table Positions A table of DCS#Vec2 or DCS#Vec3, can be a units table from the group template.
|
||||
-- @param DCS#Vec2 Anchor (Optional) DCS#Vec2 or DCS#Vec3 as anchor point to calculate offset of the units.
|
||||
-- @param #number MaxRadius (Optional) Max radius to search for valid ground locations in meters. Default is double the max radius of the units.
|
||||
-- @param #number Spacing (Optional) Minimum spacing between units in meters. Default is 5% of the search radius or 5 meters, whichever is larger.
|
||||
function UTILS.ValidateAndRepositionGroundUnits(Anchor, Positions, MaxRadius, Spacing)
|
||||
local units = Positions
|
||||
Anchor = Anchor or UTILS.GetCenterPoint(units)
|
||||
local gPos = { x = Anchor.x, y = Anchor.z or Anchor.y }
|
||||
local maxRadius = 0
|
||||
local unitCount = 0
|
||||
for _, unit in pairs(units) do
|
||||
local pos = { x = unit.x, y = unit.z or unit.y }
|
||||
local dist = UTILS.VecDist2D(pos, gPos)
|
||||
if dist > maxRadius then
|
||||
maxRadius = dist
|
||||
end
|
||||
unitCount = unitCount + 1
|
||||
end
|
||||
maxRadius = MaxRadius or math.max(maxRadius * 2, 10)
|
||||
local spacing = Spacing or math.max(maxRadius * 0.05, 5)
|
||||
if unitCount > 0 and maxRadius > 5 then
|
||||
local spots = UTILS.GetSimpleZones(UTILS.Vec2toVec3(gPos), maxRadius, spacing, 1000)
|
||||
if spots and #spots > 0 then
|
||||
local validSpots = {}
|
||||
for _, spot in pairs(spots) do -- Disposition sometimes returns points on roads, hence this filter.
|
||||
if land.getSurfaceType(spot) == land.SurfaceType.LAND then
|
||||
table.insert(validSpots, spot)
|
||||
end
|
||||
end
|
||||
spots = validSpots
|
||||
end
|
||||
|
||||
local step = spacing
|
||||
for _, unit in pairs(units) do
|
||||
local pos = { x = unit.x, y = unit.z or unit.y }
|
||||
local isOnLand = land.getSurfaceType(pos) == land.SurfaceType.LAND
|
||||
local isValid = false
|
||||
if spots and #spots > 0 then
|
||||
local si = 1
|
||||
local sid = 0
|
||||
local closestDist = 100000000
|
||||
local closestSpot
|
||||
for _, spot in pairs(spots) do
|
||||
local dist = UTILS.VecDist2D(pos, spot)
|
||||
if dist < closestDist then
|
||||
local skip = false
|
||||
for _, unit2 in pairs(units) do
|
||||
local pos2 = { x = unit2.x, y = unit2.z or unit2.y }
|
||||
local dist2 = UTILS.VecDist2D(spot, pos2)
|
||||
if dist2 < spacing and isOnLand then
|
||||
skip = true
|
||||
break
|
||||
end
|
||||
end
|
||||
if not skip then
|
||||
closestDist = dist
|
||||
closestSpot = spot
|
||||
sid = si
|
||||
end
|
||||
end
|
||||
si = si + 1
|
||||
end
|
||||
if closestSpot then
|
||||
if closestDist >= spacing then
|
||||
pos = closestSpot
|
||||
end
|
||||
isValid = true
|
||||
table.remove(spots, sid)
|
||||
end
|
||||
end
|
||||
|
||||
-- Failsafe calculation
|
||||
if not isValid and not isOnLand then
|
||||
|
||||
local h = UTILS.HdgTo(pos, gPos)
|
||||
local retries = 0
|
||||
while not isValid and retries < 500 do
|
||||
|
||||
local dist = UTILS.VecDist2D(pos, gPos)
|
||||
pos = UTILS.Vec2Translate(pos, step, h)
|
||||
|
||||
local skip = false
|
||||
for _, unit2 in pairs(units) do
|
||||
if unit ~= unit2 then
|
||||
local pos2 = { x = unit2.x, y = unit2.z or unit2.y }
|
||||
local dist2 = UTILS.VecDist2D(pos, pos2)
|
||||
if dist2 < 12 then
|
||||
isValid = false
|
||||
skip = true
|
||||
break
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
if not skip and dist > step and land.getSurfaceType(pos) == land.SurfaceType.LAND then
|
||||
isValid = true
|
||||
break
|
||||
elseif dist <= step then
|
||||
break
|
||||
end
|
||||
|
||||
retries = retries + 1
|
||||
end
|
||||
end
|
||||
|
||||
if isValid then
|
||||
unit.x = pos.x
|
||||
if unit.z then
|
||||
unit.z = pos.y
|
||||
else
|
||||
unit.y = pos.y
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user