Cap rework (#48)

- Refactored all CAP classes.
- Improved timing
- Zones are now not tied to MissionStages
  - CAPROUTE_<routeID>_<free>
  - routeID is now what is used in the naming convention.
- Units taxi back to their spawn parking space and rearm there.
  (Units destroyed while in the Rearming or Repairing state are not taken into account)
This commit is contained in:
2025-06-01 18:58:35 +02:00
committed by GitHub
parent 059081890d
commit 0233b9e2d7
21 changed files with 1519 additions and 832 deletions
+104 -10
View File
@@ -36,15 +36,15 @@ do -- INIT UTIL
---@param orig table
---@return table copy
function UTIL.copyTable(orig)
function UTIL.deepCopyTable(orig)
local orig_type = type(orig)
local copy
if orig_type == 'table' then
copy = {}
for orig_key, orig_value in next, orig, nil do
copy[UTIL.copyTable(orig_key)] = UTIL.copyTable(orig_value)
copy[UTIL.deepCopyTable(orig_key)] = UTIL.deepCopyTable(orig_value)
end
setmetatable(copy, UTIL.copyTable(getmetatable(orig)))
setmetatable(copy, UTIL.deepCopyTable(getmetatable(orig)))
else -- number, string, boolean, etc
copy = orig
end
@@ -88,15 +88,15 @@ do -- INIT UTIL
table.insert(sb, string.rep(" ", indent)) -- indent it
if type(value) == "table" and not done[value] then
done[value] = true
table.insert(sb, key .. " = {\n");
table.insert(sb, "[\"" ..key .. "\"]" .. " = {\n");
table.insert(sb, table_print(value, indent + 2, done))
table.insert(sb, string.rep(" ", indent)) -- indent it
table.insert(sb, "}\n");
table.insert(sb, "},\n");
elseif "number" == type(key) then
table.insert(sb, string.format("\"%s\"\n", tostring(value)))
table.insert(sb, string.format("\"%s\",\n", tostring(value)))
else
table.insert(sb, string.format(
"%s = \"%s\"\n", tostring(key), tostring(value)))
"[\"%s\"] = \"%s\",\n", tostring(key), tostring(value)))
end
end
return table.concat(sb)
@@ -172,6 +172,54 @@ do -- INIT UTIL
return (vec.x ^ 2 + vec.y ^ 2 + vec.z ^ 2) ^ 0.5
end
---@param vec Vec3
---@return Vec3
function UTIL.vectorNormalize(vec)
local magnitude = UTIL.vectorMagnitude(vec)
if magnitude == 0 then
return { x = 0, y = 0, z = 0 }
end
return { x = vec.x / magnitude, y = vec.y / magnitude, z = vec.z / magnitude }
end
---@param vec Vec2
---@param direction number @in degrees
---@param distance number
---@return Vec2
function UTIL.vectorMove(vec, direction, distance)
local rad = math.rad(direction)
local x = vec.x + (math.cos(rad) * distance)
local y = vec.y + (math.sin(rad) * distance)
return { x = x, y = y}
end
---comment
---@param vec1 Vec2
---@param vec2 Vec2
---@return number in degrees
function UTIL.vectorHeadingFromTo(vec1, vec2)
local dx = vec2.x - vec1.x
local dy = vec2.y - vec1.y
local heading = math.deg(math.atan2(dy, dx))
if heading < 0 then
heading = heading + 360
end
return heading
end
---@param vec1 Vec3
---@param vec2 Vec3
---@return number alignment a number in range [-1,1]. > 0 same direction. < 0 opposite direction
function UTIL.vectorAlignment(vec1, vec2)
local vec1Norm = UTIL.vectorNormalize(vec1)
local vec2Norm = UTIL.vectorNormalize(vec2)
return ((vec1Norm.x * vec2Norm.x) + (vec1Norm.y * vec2Norm.y) + (vec1Norm.z * vec2Norm.z))
end
local function isInComplexPolygon(polygon, x, y)
local function getEdges(poly)
local result = {}
@@ -706,7 +754,7 @@ do -- INIT DCS_UTIL
---@param groupName string
function DCS_UTIL.IsGroupStatic(groupName)
if DCS_UTIL.__miz_groups[groupName] then
return DCS_UTIL.__miz_groups[groupName].category == DCS_UTIL.GroupCategory.STATIC;
return DCS_UTIL.__miz_groups[groupName].category == DCS_UTIL.GroupCategory.STATIC
end
return StaticObject.getByName(groupName) ~= nil
@@ -1255,9 +1303,10 @@ do -- INIT DCS_UTIL
---@param route table? route of the group. If nil wil be the default route.
---@param uncontrolled boolean? Sets the group to be uncontrolled on spawn
---@param countryId CountryID? Overwrites the country
---@param emptyLoadouts boolean? If true, the group will spawn with empty loadouts
---@return table? new_group the Group class that was spawned
---@return boolean? isStatic whether the group is a static or not
function DCS_UTIL.SpawnGroupTemplate(groupName, location, route, uncontrolled, countryId)
function DCS_UTIL.SpawnGroupTemplate(groupName, location, route, uncontrolled, countryId, emptyLoadouts)
if groupName == nil then
return nil, nil
end
@@ -1280,7 +1329,7 @@ do -- INIT DCS_UTIL
return object, true
end
else
local spawn_template = template.group_template
local spawn_template = Spearhead.Util.deepCopyTable(template.group_template)
if location ~= nil then
local x_offset
if location.x ~= nil then x_offset = spawn_template.x - location.x end
@@ -1298,6 +1347,26 @@ do -- INIT DCS_UTIL
end
end
if spawn_template.units then
for _, unit in pairs(spawn_template.units) do
if unit["parking"] then
unit["parking_landing"] = unit["parking"]
end
if unit["parking_id"] then
unit["parking_landing_id"] = unit["parking_id"]
end
end
if emptyLoadouts == true then
for _, unit in pairs(spawn_template.units) do
if unit["payload"] and unit["payload"]["pylons"] then
unit["payload"]["pylons"] = {}
end
end
end
end
if route ~= nil then
spawn_template.route = route
end
@@ -1305,6 +1374,7 @@ do -- INIT DCS_UTIL
if uncontrolled ~= nil then
spawn_template.uncontrolled = uncontrolled
end
local country = countryId or template.country_id
local new_group = coalition.addGroup(country, template.category, spawn_template)
return new_group, false
@@ -1320,6 +1390,30 @@ do -- INIT DCS_UTIL
end
end
function DCS_UTIL.NeedsRTBInTen(groupName, fuelOffset)
local isBingo = Spearhead.DcsUtil.IsBingoFuel(groupName, fuelOffset)
if isBingo then return true end
local aliveUnits = 0
local group = Group.getByName(groupName)
if group then
for _ , unit in pairs(group:getUnits()) do
if unit and unit:isExist() == true and unit:inAir() == true then
aliveUnits = aliveUnits + 1
end
end
if aliveUnits / group:getInitialSize() <= 0.5 then
return true
end
end
return false
end
---@return boolean
function DCS_UTIL.IsBingoFuel(groupName, offset)
if offset == nil then offset = 0 end
local bingoSetting = 0.20