1052 lines
36 KiB
Lua
1052 lines
36 KiB
Lua
--- DEFAULT Values
|
|
if Spearhead == nil then Spearhead = {} end
|
|
|
|
local UTIL = {}
|
|
do -- INIT UTIL
|
|
---splits a string in sub parts by seperator
|
|
---@param input string
|
|
---@param seperator string
|
|
---@return table result list of strings
|
|
function UTIL.split_string(input, seperator)
|
|
if seperator == nil then
|
|
seperator = " "
|
|
end
|
|
|
|
local result = {}
|
|
if input == nil then
|
|
return result
|
|
end
|
|
|
|
for str in string.gmatch(input, "[^" .. seperator .. "]+") do
|
|
table.insert(result, str)
|
|
end
|
|
return result
|
|
end
|
|
|
|
---comment
|
|
---@param table any
|
|
---@return number
|
|
function UTIL.tableLength(table)
|
|
if table == nil then return 0 end
|
|
|
|
local count = 0
|
|
for _ in pairs(table) do count = count + 1 end
|
|
return count
|
|
end
|
|
|
|
---Gets a random from the list
|
|
---@param list Array
|
|
---@return any @random element from the list
|
|
function UTIL.randomFromList(list)
|
|
local max = #list
|
|
|
|
if max == 0 or max == nil then
|
|
return nil
|
|
end
|
|
|
|
local random = math.random(0, max)
|
|
if random == 0 then random = 1 end
|
|
|
|
return list[random]
|
|
end
|
|
|
|
local function table_print(tt, indent, done)
|
|
done = done or {}
|
|
indent = indent or 0
|
|
if type(tt) == "table" then
|
|
local sb = {}
|
|
for key, value in pairs(tt) do
|
|
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, table_print(value, indent + 2, done))
|
|
table.insert(sb, string.rep(" ", indent)) -- indent it
|
|
table.insert(sb, "}\n");
|
|
elseif "number" == type(key) then
|
|
table.insert(sb, string.format("\"%s\"\n", tostring(value)))
|
|
else
|
|
table.insert(sb, string.format(
|
|
"%s = \"%s\"\n", tostring(key), tostring(value)))
|
|
end
|
|
end
|
|
return table.concat(sb)
|
|
else
|
|
return tt .. "\n"
|
|
end
|
|
end
|
|
|
|
---comment
|
|
---@param str string
|
|
---@param findable string
|
|
---@param ignoreCase boolean?
|
|
---@return boolean
|
|
UTIL.startswith = function(str, findable, ignoreCase)
|
|
|
|
if ignoreCase == true then
|
|
return string.lower(str):find('^' .. string.lower(findable)) ~= nil
|
|
end
|
|
|
|
return str:find('^' .. findable) ~= nil
|
|
end
|
|
|
|
---comment
|
|
---@param str string
|
|
---@param findable string
|
|
---@return boolean
|
|
UTIL.strContains = function(str, findable)
|
|
return str:find(findable) ~= nil
|
|
end
|
|
|
|
---comment
|
|
---@param str string
|
|
---@param findableTable table
|
|
---@return boolean
|
|
UTIL.startswithAny = function(str, findableTable)
|
|
for key, value in pairs(findableTable) do
|
|
if type(value) == "string" and UTIL.startswith(str, value) then return true end
|
|
end
|
|
return false
|
|
end
|
|
|
|
function UTIL.toString(something)
|
|
if something == nil then
|
|
return "nil"
|
|
elseif "table" == type(something) then
|
|
return table_print(something)
|
|
elseif "string" == type(something) then
|
|
return something
|
|
else
|
|
return tostring(something)
|
|
end
|
|
end
|
|
|
|
---comment
|
|
---@param a Vec2 DCS Point vector {x, z , y}
|
|
---@param b Vec2 DCS Point vector {x, z , y}
|
|
---@return number
|
|
function UTIL.VectorDistance2d(a, b)
|
|
return math.sqrt((b.x - a.x) ^ 2 + (b.y - a.y) ^ 2)
|
|
end
|
|
|
|
---comment
|
|
---@param a Vec3
|
|
---@param b Vec3
|
|
---@return number
|
|
function UTIL.VectorDistance3d(a, b)
|
|
return UTIL.vectorMagnitude({ x = a.x - b.x, y = a.y - b.y, z = a.z - b.z })
|
|
end
|
|
|
|
---comment
|
|
---@param vec Vec3
|
|
---@return number
|
|
function UTIL.vectorMagnitude(vec)
|
|
return (vec.x ^ 2 + vec.y ^ 2 + vec.z ^ 2) ^ 0.5
|
|
end
|
|
|
|
|
|
---comment
|
|
---@param polygon table of pairs { x, z }
|
|
---@param x number X location
|
|
---@param z number Y location
|
|
---@return boolean
|
|
function UTIL.IsPointInPolygon(polygon, x, z)
|
|
local function isInComplexPolygon(polygon, x, z)
|
|
local function getEdges(poly)
|
|
local result = {}
|
|
for i = 1, #poly do
|
|
local point1 = poly[i]
|
|
local point2Index = i + 1
|
|
if point2Index > #poly then point2Index = 1 end
|
|
local point2 = poly[point2Index]
|
|
local edge = { x1 = point1.x, z1 = point1.z, x2 = point2.x, z2 = point2.z }
|
|
table.insert(result, edge)
|
|
end
|
|
return result
|
|
end
|
|
|
|
local edges = getEdges(polygon)
|
|
local count = 0;
|
|
for _, edge in pairs(edges) do
|
|
if (x < edge.x1) ~= (x < edge.x2) and z < edge.z1 + ((x - edge.x1) / (edge.x2 - edge.x1)) * (edge.z2 - edge.z1) then
|
|
count = count + 1
|
|
-- if (yp < y1) != (yp < y2) and xp < x1 + ((yp-y1)/(y2-y1))*(x2-x1) then
|
|
-- count = count + 1
|
|
end
|
|
end
|
|
return count % 2 == 1
|
|
end
|
|
return isInComplexPolygon(polygon, x, z)
|
|
end
|
|
|
|
---comment
|
|
---@param points table points { x, z }
|
|
---@return table hullPoints { x, z }
|
|
function UTIL.getConvexHull(points)
|
|
if #points == 0 then
|
|
return {}
|
|
end
|
|
|
|
local function ccw(a,b,c)
|
|
return (b.z - a.z) * (c.x - a.x) > (b.x - a.x) * (c.z - a.z)
|
|
end
|
|
|
|
table.sort(points, function(left,right)
|
|
return left.z < right.z
|
|
end)
|
|
|
|
local hull = {}
|
|
-- lower hull
|
|
for _,point in pairs(points) do
|
|
while #hull >= 2 and not ccw(hull[#hull-1], hull[#hull], point) do
|
|
table.remove(hull,#hull)
|
|
end
|
|
table.insert(hull,point)
|
|
end
|
|
|
|
-- upper hull
|
|
local t = #hull + 1
|
|
for i=#points, 1, -1 do
|
|
local point = points[i]
|
|
while #hull >= t and not ccw(hull[#hull-1], hull[#hull], point) do
|
|
table.remove(hull,#hull)
|
|
end
|
|
table.insert(hull,point)
|
|
end
|
|
table.remove(hull,#hull)
|
|
return hull
|
|
end
|
|
|
|
function UTIL.enlargeConvexHull(points, meters)
|
|
|
|
local allpoints = {}
|
|
|
|
for _, point in pairs(points) do
|
|
table.insert(allpoints, point)
|
|
table.insert(allpoints, { x = point.x + meters, z = point.z, y= 0 })
|
|
table.insert(allpoints, { x = point.x - meters, z = point.z, y= 0 })
|
|
table.insert(allpoints, { x = point.x, z = point.z + meters, y= 0 })
|
|
table.insert(allpoints, { x = point.x, z = point.z - meters, y= 0 })
|
|
|
|
table.insert(allpoints, { x = point.x + math.cos(math.rad(45)) * meters, z = point.z + math.sin(math.rad(45)) * meters, y= 0 })
|
|
table.insert(allpoints, { x = point.x - math.cos(math.rad(45)) * meters, z = point.z - math.sin(math.rad(45)) * meters, y= 0 })
|
|
table.insert(allpoints, { x = point.x - math.cos(math.rad(45)) * meters, z = point.z + math.sin(math.rad(45)) * meters, y= 0 })
|
|
table.insert(allpoints, { x = point.x + math.cos(math.rad(45)) * meters, z = point.z - math.sin(math.rad(45)) * meters, y= 0 })
|
|
|
|
end
|
|
|
|
return UTIL.getConvexHull(allpoints)
|
|
end
|
|
end
|
|
Spearhead.Util = UTIL
|
|
|
|
---DCS UTIL Takes inspiration from MIST but only takes the things it needs, changes for DCS updates and different vision for advanced mission scripting stuff.
|
|
---It also adds functions that make the other TDCS scripts easier without taking too much "control" away like MOOSE can sometimes.
|
|
local DCS_UTIL = {}
|
|
do -- INIT DCS_UTIL
|
|
do -- local databases
|
|
--[[
|
|
groupdata = {
|
|
category,
|
|
country_id,
|
|
group_template
|
|
}
|
|
]] --
|
|
DCS_UTIL.__miz_groups = {}
|
|
DCS_UTIL.__groupNames = {}
|
|
DCS_UTIL.__blueGroupNames = {}
|
|
DCS_UTIL.__redGroupNames = {}
|
|
--[[
|
|
zone = {
|
|
name,
|
|
|
|
zone_type,
|
|
x,
|
|
z,
|
|
radius
|
|
verts,
|
|
|
|
}
|
|
]] --
|
|
DCS_UTIL.__trigger_zones = {}
|
|
end
|
|
|
|
DCS_UTIL.Coalition =
|
|
{
|
|
NEUTRAL = 0,
|
|
RED = 1,
|
|
BLUE = 2
|
|
}
|
|
|
|
DCS_UTIL.ZoneType = {
|
|
Cilinder = 0,
|
|
Polygon = 2
|
|
}
|
|
|
|
DCS_UTIL.GroupCategory = {
|
|
AIRPLANE = 0,
|
|
HELICOPTER = 1,
|
|
GROUND = 2,
|
|
SHIP = 3,
|
|
TRAIN = 4,
|
|
STATIC = 5 --CUSTOM CATEGORY
|
|
}
|
|
|
|
DCS_UTIL.__airbaseNamesById = {}
|
|
--[[
|
|
zone = {
|
|
name,
|
|
zone_type,
|
|
x,
|
|
z,
|
|
radius
|
|
verts,
|
|
|
|
}
|
|
]] --
|
|
DCS_UTIL.__airbaseZonesById = {}
|
|
|
|
DCS_UTIL.__airportsStartingCoalition = {}
|
|
DCS_UTIL.__warehouseStartingCoalition = {}
|
|
function DCS_UTIL.__INIT()
|
|
do -- INITS ALL TABLES WITH DATA THAT's from the MIZ environment
|
|
do -- init group tables
|
|
for coalition_name, coalition_data in pairs(env.mission.coalition) do
|
|
local coalition_nr = DCS_UTIL.stringToCoalition(coalition_name)
|
|
if coalition_data.country then
|
|
for country_index, country_data in pairs(coalition_data.country) do
|
|
for category_name, categorydata in pairs(country_data) do
|
|
local category_id = DCS_UTIL.stringToGroupCategory(category_name)
|
|
if category_id ~= nil and type(categorydata) == "table" and categorydata.group ~= nil and type(categorydata.group) == "table" then
|
|
for group_index, group in pairs(categorydata.group) do
|
|
local name = group.name
|
|
if category_id == DCS_UTIL.GroupCategory.STATIC then
|
|
local unit = group.units[1]
|
|
name = unit.name
|
|
local staticObj = {
|
|
heading = unit.heading,
|
|
name = unit.name,
|
|
x = unit.x,
|
|
y = unit.y,
|
|
type = unit.type,
|
|
dead = group.dead
|
|
}
|
|
|
|
if string.lower(unit.category) == "planes" then
|
|
staticObj.livery_id = unit.livery_id
|
|
end
|
|
|
|
group = staticObj
|
|
end
|
|
|
|
table.insert(DCS_UTIL.__groupNames, name)
|
|
DCS_UTIL.__miz_groups[name] =
|
|
{
|
|
category = category_id,
|
|
country_id = country_data.id,
|
|
group_template = group
|
|
}
|
|
|
|
if coalition_nr == 1 then
|
|
table.insert(DCS_UTIL.__redGroupNames, name)
|
|
elseif coalition_nr == 2 then
|
|
table.insert(DCS_UTIL.__blueGroupNames, name)
|
|
end
|
|
end
|
|
end
|
|
end
|
|
end
|
|
end
|
|
end
|
|
end
|
|
|
|
do --init trigger zones
|
|
for i, trigger_zone in pairs(env.mission.triggers.zones) do
|
|
|
|
-- reorder verts as they are not ordered correctly in the ME
|
|
verts = {}
|
|
if Spearhead.Util.tableLength(trigger_zone.verticies) >=4 then
|
|
table.insert(verts, { x = trigger_zone.verticies[4].x , z = trigger_zone.verticies[4].y })
|
|
table.insert(verts, { x = trigger_zone.verticies[3].x , z = trigger_zone.verticies[3].y })
|
|
table.insert(verts, { x = trigger_zone.verticies[2].x , z = trigger_zone.verticies[2].y })
|
|
table.insert(verts, { x = trigger_zone.verticies[1].x , z = trigger_zone.verticies[1].y })
|
|
end
|
|
|
|
local zone = {
|
|
name = trigger_zone.name,
|
|
zone_type = trigger_zone.type,
|
|
x = trigger_zone.x,
|
|
z = trigger_zone.y,
|
|
radius = trigger_zone.radius,
|
|
verts = verts
|
|
}
|
|
|
|
DCS_UTIL.__trigger_zones[zone.name] = zone
|
|
end
|
|
end
|
|
|
|
do -- init airports and warehouses
|
|
if env.warehouses.airports then
|
|
for warehouse_id, value in pairs(env.warehouses.airports) do
|
|
if warehouse_id ~= nil then
|
|
warehouse_id = tostring(warehouse_id) or "nil"
|
|
local coalitionNumber = DCS_UTIL.stringToCoalition(value.coalition)
|
|
DCS_UTIL.__airportsStartingCoalition[warehouse_id] = coalitionNumber
|
|
end
|
|
end
|
|
end
|
|
|
|
if env.warehouses.warehouses then
|
|
DCS_UTIL.__warehouseStartingCoalition[-1] = "placeholder"
|
|
for warehouse_id, value in pairs(env.warehouses.warehouses) do
|
|
if warehouse_id ~= nil then
|
|
warehouse_id = tostring(warehouse_id) or "nil"
|
|
local coalitionNumber = DCS_UTIL.stringToCoalition(value.coalition)
|
|
DCS_UTIL.__warehouseStartingCoalition[warehouse_id] = coalitionNumber
|
|
end
|
|
end
|
|
end
|
|
end
|
|
|
|
do -- fill airbaseNames and zones
|
|
local airbases = world.getAirbases()
|
|
if airbases then
|
|
for _, airbase in pairs(airbases) do
|
|
local name = airbase:getName()
|
|
local id = tostring(airbase:getID())
|
|
|
|
if name and id then
|
|
DCS_UTIL.__airbaseNamesById[id] = name
|
|
|
|
local relevantPoints = {}
|
|
for _, x in pairs(airbase:getRunways()) do
|
|
if x.position and x.position.x and x.position.z then
|
|
table.insert(relevantPoints, { x = x.position.x, z = x.position.z, y=0})
|
|
end
|
|
end
|
|
|
|
for _, x in pairs(airbase:getParking()) do
|
|
if x.vTerminalPos and x.vTerminalPos.x and x.vTerminalPos.z then
|
|
table.insert(relevantPoints, { x = x.vTerminalPos.x, z = x.vTerminalPos.z, y=0})
|
|
end
|
|
end
|
|
|
|
local points = UTIL.getConvexHull(relevantPoints)
|
|
local enlargedPoints = UTIL.enlargeConvexHull(points, 750)
|
|
|
|
DCS_UTIL.__airbaseZonesById[id] = {
|
|
name = name,
|
|
zone_type = DCS_UTIL.ZoneType.Polygon,
|
|
verts = enlargedPoints
|
|
}
|
|
end
|
|
end
|
|
end
|
|
end
|
|
end
|
|
end
|
|
|
|
---maps the coalition name to the DCS coalition integer
|
|
---@param input string the name
|
|
---@return integer
|
|
function DCS_UTIL.stringToCoalition(input)
|
|
--[[
|
|
coalition.side = {
|
|
NEUTRAL = 0
|
|
RED = 1
|
|
BLUE = 2
|
|
}
|
|
]] --
|
|
local input = string.lower(input)
|
|
if input == 'neutrals' or input == "neutral" or input == "0" then
|
|
return DCS_UTIL.Coalition.NEUTRAL
|
|
end
|
|
|
|
if input == 'red' or input == "1" then
|
|
return DCS_UTIL.Coalition.RED
|
|
end
|
|
|
|
if input == 'blue' or input == "2" then
|
|
return DCS_UTIL.Coalition.BLUE
|
|
end
|
|
|
|
return -1
|
|
end
|
|
|
|
---checks if the groupname is a static group
|
|
---@param groupName any
|
|
function DCS_UTIL.IsGroupStatic(groupName)
|
|
if DCS_UTIL.__miz_groups[groupName] then
|
|
return DCS_UTIL.__miz_groups[groupName].category == 5;
|
|
end
|
|
|
|
return StaticObject.getByName(groupName) ~= nil
|
|
end
|
|
|
|
---destroy the given group
|
|
---@param groupName string
|
|
function DCS_UTIL.DestroyGroup(groupName)
|
|
if DCS_UTIL.IsGroupStatic(groupName) then
|
|
local object = StaticObject.getByName(groupName)
|
|
if object ~= nil then
|
|
object:destroy()
|
|
end
|
|
else
|
|
local group = Group.getByName(groupName)
|
|
if group and group:isExist() then
|
|
group:destroy()
|
|
end
|
|
end
|
|
end
|
|
|
|
---destroy the given unit
|
|
function DCS_UTIL.DestroyUnit(unitName)
|
|
if DCS_UTIL.IsGroupStatic(unitName) == true then
|
|
local object = StaticObject.getByName(unitName)
|
|
if object ~= nil then
|
|
object:destroy()
|
|
end
|
|
else
|
|
local unit = Unit.getByName(unitName)
|
|
if unit and unit:isExist() then
|
|
unit:destroy()
|
|
end
|
|
end
|
|
end
|
|
|
|
|
|
--- takes a list of units and returns all the units that are in any of the zones
|
|
---@param unit_names table unit names
|
|
---@param zone_names table zone names
|
|
---@return table unit list of objects { unit = UNIT, zone_name = zoneName}
|
|
function DCS_UTIL.getUnitsInZones(unit_names, zone_names)
|
|
local units = {}
|
|
local zones = {}
|
|
|
|
for k = 1, #unit_names do
|
|
local unit = Unit.getByName(unit_names[k]) or StaticObject.getByName(unit_names[k])
|
|
if unit and unit:isExist() == true then
|
|
units[#units + 1] = unit
|
|
end
|
|
end
|
|
|
|
for index, zone_name in pairs(zone_names) do
|
|
local zone = DCS_UTIL.__trigger_zones[zone_name]
|
|
if zone then
|
|
zones[#zones + 1] = zone
|
|
end
|
|
end
|
|
|
|
local in_zone_units = {}
|
|
for units_ind = 1, #units do
|
|
local lUnit = units[units_ind]
|
|
local unit_pos = lUnit:getPosition().p
|
|
local lCat = Object.getCategory(lUnit)
|
|
for zone_name, zone in pairs(zones) do
|
|
if unit_pos and ((lCat == 1 and lUnit:isActive() == true) or lCat ~= 1) then -- it is a unit and is active or it is not a unit
|
|
if zone.zone_type == DCS_UTIL.ZoneType.Polygon and zone.verts then
|
|
if UTIL.IsPointInPolygon(zone.verts, unit_pos.x, unit_pos.z) == true then
|
|
in_zone_units[#in_zone_units + 1] = { unit = lUnit, zone_name = zone.name }
|
|
end
|
|
else
|
|
if (((unit_pos.x - zone.x) ^ 2 + (unit_pos.z - zone.z) ^ 2) ^ 0.5 <= zone.radius) then
|
|
in_zone_units[#in_zone_units + 1] = { unit = lUnit, zone_name = zone.name }
|
|
end
|
|
end
|
|
end
|
|
end
|
|
end
|
|
return in_zone_units
|
|
end
|
|
|
|
--- takes a list of groups and returns all the group leaders that are in any of the zones
|
|
---@param group_names table unit names
|
|
---@param zone_name string zone names
|
|
---@return table groupnames list of group names
|
|
function DCS_UTIL.getGroupsInZone(group_names, zone_name)
|
|
local zone = DCS_UTIL.__trigger_zones[zone_name]
|
|
if zone == nil then
|
|
return {}
|
|
end
|
|
|
|
-- MAP Just for mapping sake
|
|
local custom_zone = {
|
|
x = zone.x,
|
|
z = zone.z,
|
|
zone_type = zone.zone_type,
|
|
radius = zone.radius,
|
|
verts = zone.verts
|
|
}
|
|
|
|
return DCS_UTIL.areGroupsInCustomZone(group_names, custom_zone)
|
|
end
|
|
|
|
--- takes a x, y poistion and checks if it is inside any of the zones
|
|
---@param group_names table North South position
|
|
---@param zone table { x, z, zonetype, radius, verts }
|
|
---@return table groupnames list of groups that are in the zone
|
|
function DCS_UTIL.areGroupsInCustomZone(group_names, zone)
|
|
local units = {}
|
|
if Spearhead.Util.tableLength(group_names) < 1 then return {} end
|
|
|
|
for k = 1, #group_names do
|
|
local entry = nil
|
|
local group = Group.getByName(group_names[k])
|
|
if group ~= nil then
|
|
entry = { unit = group:getUnit(1), groupname = group_names[k] }
|
|
else
|
|
entry = { unit = StaticObject.getByName(group_names[k]), groupname = group_names[k] }
|
|
end
|
|
|
|
if entry and entry.unit and entry.unit:isExist() == true then
|
|
units[#units + 1] = entry
|
|
end
|
|
end
|
|
|
|
local result_groups = {}
|
|
for _, entry in pairs(units) do
|
|
local pos = entry.unit:getPoint()
|
|
if zone.zone_type == DCS_UTIL.ZoneType.Polygon and zone.verts then
|
|
if UTIL.IsPointInPolygon(zone.verts, pos.x, pos.z) == true then
|
|
table.insert(result_groups, entry.groupname)
|
|
end
|
|
else
|
|
if (((pos.x - zone.x) ^ 2 + (pos.z - zone.z) ^ 2) ^ 0.5 <= zone.radius) then
|
|
table.insert(result_groups, entry.groupname)
|
|
end
|
|
end
|
|
end
|
|
return result_groups
|
|
end
|
|
|
|
--- takes a x, y poistion and checks if it is inside any of the zones
|
|
---@param x number North South position
|
|
---@param z number West East position
|
|
---@param zone_names table zone names
|
|
---@return table zones list of objects { zone_name = zoneName}
|
|
function DCS_UTIL.isPositionInZones(x, z, zone_names)
|
|
local zones = {}
|
|
for index, zone_name in pairs(zone_names) do
|
|
local zone = DCS_UTIL.__trigger_zones[zone_name]
|
|
if zone then
|
|
zones[#zones + 1] = zone
|
|
end
|
|
end
|
|
|
|
local result_zones = {}
|
|
for zone_name, zone in pairs(zones) do
|
|
if zone.zone_type == DCS_UTIL.ZoneType.Polygon and zone.verts then
|
|
if UTIL.IsPointInPolygon(zone.verts, x, z) == true then
|
|
result_zones[#result_zones + 1] = zone.name
|
|
end
|
|
else
|
|
if (((x - zone.x) ^ 2 + (z - zone.z) ^ 2) ^ 0.5 <= zone.radius) then
|
|
result_zones[#result_zones + 1] = zone.name
|
|
end
|
|
end
|
|
end
|
|
return result_zones
|
|
end
|
|
|
|
--- takes a x, y poistion and checks if it is inside any of the zones
|
|
---@param x number North South position
|
|
---@param z number West East position
|
|
---@param zone_name string zone name
|
|
---@return boolean result
|
|
function DCS_UTIL.isPositionInZone(x, z, zone_name)
|
|
local zone = DCS_UTIL.__trigger_zones[zone_name]
|
|
if zone.zone_type == DCS_UTIL.ZoneType.Polygon and zone.verts then
|
|
if UTIL.IsPointInPolygon(zone.verts, x, z) == true then
|
|
return true
|
|
end
|
|
else
|
|
if (((x - zone.x) ^ 2 + (z - zone.z) ^ 2) ^ 0.5 <= zone.radius) then
|
|
return true
|
|
end
|
|
end
|
|
return false
|
|
end
|
|
|
|
--- takes a x, y poistion and checks if it is inside any of the zones
|
|
---@param zone_name string
|
|
---@param parent_zone_name string
|
|
---@return boolean result
|
|
function DCS_UTIL.isZoneInZone(zone_name, parent_zone_name)
|
|
local zoneA = DCS_UTIL.__trigger_zones[zone_name]
|
|
local zoneB = DCS_UTIL.__trigger_zones[parent_zone_name]
|
|
|
|
if zoneB.zone_type == DCS_UTIL.ZoneType.Polygon and zoneB.verts then
|
|
if UTIL.IsPointInPolygon(zoneB.verts, zoneA.x, zoneA.z) == true then
|
|
return true
|
|
end
|
|
else
|
|
if (((zoneA.x - zoneB.x) ^ 2 + (zoneA.z - zoneB.z) ^ 2) ^ 0.5 <= zoneB.radius) then
|
|
return true
|
|
end
|
|
end
|
|
return false
|
|
end
|
|
|
|
--- takes a x, y poistion and checks if it is inside any of the zones
|
|
---@param x number North South position
|
|
---@param z number West East position
|
|
---@param zone table { x, z, zonetype, radius }
|
|
---@return boolean result
|
|
function DCS_UTIL.isPositionInCustomZone(x, z, zone)
|
|
if zone.zone_type == DCS_UTIL.ZoneType.Polygon and zone.verts then
|
|
if UTIL.IsPointInPolygon(zone.verts, x, z) == true then
|
|
return true
|
|
end
|
|
else
|
|
if (((x - zone.x) ^ 2 + (z - zone.z) ^ 2) ^ 0.5 <= zone.radius) then
|
|
return true
|
|
end
|
|
end
|
|
return false
|
|
end
|
|
|
|
---comment
|
|
---@param zone_name any
|
|
---@return table? zone { name,b zone_type, x, z, radius, verts }
|
|
function DCS_UTIL.getZoneByName(zone_name)
|
|
if zone_name == nil then return nil end
|
|
return DCS_UTIL.__trigger_zones[zone_name]
|
|
end
|
|
|
|
---comment
|
|
---@param airbaseId any
|
|
---@return table? zone { name,b zone_type, x, z, radius, verts }
|
|
function DCS_UTIL.getAirbaseZoneById(airbaseId)
|
|
local string = tostring(airbaseId)
|
|
if string == nil then return nil end
|
|
return DCS_UTIL.__airbaseZonesById[string]
|
|
end
|
|
|
|
|
|
|
|
---maps the category name to the DCS group category
|
|
---@param input string the name
|
|
---@return integer?
|
|
function DCS_UTIL.stringToGroupCategory(input)
|
|
input = string.lower(input)
|
|
if input == 'airplane' or input == 'plane' then
|
|
return DCS_UTIL.GroupCategory.AIRPLANE
|
|
end
|
|
if input == 'helicopter' then
|
|
return DCS_UTIL.GroupCategory.HELICOPTER
|
|
end
|
|
if input == 'ground' or input == 'vehicle' then
|
|
return DCS_UTIL.GroupCategory.GROUND
|
|
end
|
|
if input == 'ship' then
|
|
return DCS_UTIL.GroupCategory.SHIP
|
|
end
|
|
if input == 'train' then
|
|
return DCS_UTIL.GroupCategory.TRAIN
|
|
end
|
|
if input == "static" then
|
|
return DCS_UTIL.GroupCategory.STATIC
|
|
end
|
|
return nil;
|
|
end
|
|
|
|
--- get the group config as per start of the mission
|
|
--- group = {
|
|
--- category,
|
|
--- country_id,
|
|
--- group_template
|
|
--- }
|
|
---@param groupname string groupName you're looking for
|
|
function DCS_UTIL.GetMizGroupOrDefault(groupname, default)
|
|
local group = DCS_UTIL.__miz_groups[groupname]
|
|
if group == nil then
|
|
return default
|
|
end
|
|
return group
|
|
end
|
|
|
|
---comment Get all group names. Can be a LOT
|
|
---Includes statics
|
|
---@return table groups
|
|
function DCS_UTIL.getAllGroupNames()
|
|
return DCS_UTIL.__groupNames
|
|
end
|
|
|
|
---comment Get all BLUE group names. Can be a LOT
|
|
---Includes statics
|
|
---@return table groups
|
|
function DCS_UTIL.getAllBlueGroupNames()
|
|
return DCS_UTIL.__blueGroupNames
|
|
end
|
|
|
|
---comment Get all RED group names. Can be a LOT
|
|
---Includes statics
|
|
---@return table groups
|
|
function DCS_UTIL.getAllRedGroupNames()
|
|
return DCS_UTIL.__redGroupNames
|
|
end
|
|
|
|
---comment Get all units that are players
|
|
---@return Array<Unit> units
|
|
function DCS_UTIL.getAllPlayerUnits()
|
|
local units = {}
|
|
for i = 0,2 do
|
|
local players = coalition.getPlayers(i)
|
|
for key, unit in pairs(players) do
|
|
units[#units + 1] = unit
|
|
end
|
|
end
|
|
return units
|
|
end
|
|
|
|
---get base name from ID
|
|
---@param baseId number
|
|
---@return string? name
|
|
function DCS_UTIL.getAirbaseName(baseId)
|
|
local stringified = tostring(baseId)
|
|
return DCS_UTIL.__airbaseNamesById[stringified]
|
|
end
|
|
|
|
---get base from id
|
|
---@param baseId number
|
|
---@return table? table
|
|
function DCS_UTIL.getAirbaseById(baseId)
|
|
local name = DCS_UTIL.getAirbaseName(baseId)
|
|
if name == nil then return nil end
|
|
return Airbase.getByName(name)
|
|
end
|
|
|
|
---Get the starting coalition of a farp or airbase
|
|
---@param airbase Airbase
|
|
---@return number? coalition
|
|
function DCS_UTIL.getStartingCoalition(airbase)
|
|
if airbase == nil then
|
|
return nil
|
|
end
|
|
|
|
--STRING based dictionary otherwise it'll be a string/collapsed array
|
|
local baseId = tostring(airbase:getID())
|
|
|
|
local result = DCS_UTIL.__airportsStartingCoalition[baseId]
|
|
if result == nil then
|
|
result = DCS_UTIL.__warehouseStartingCoalition[baseId]
|
|
end
|
|
return result
|
|
end
|
|
|
|
---Spawn an corpse
|
|
---@param countryId number countryId
|
|
---@param unitType string
|
|
---@param location table { z, y, z}
|
|
---@param heading number
|
|
function DCS_UTIL.SpawnCorpse(countryId, unitName, unitType, location, heading)
|
|
local name = "dead_" .. unitName
|
|
|
|
local staticObj = {
|
|
["heading"] = heading,
|
|
--["shape_name"] = "stolovaya",
|
|
["type"] = unitType,
|
|
["name"] = name,
|
|
["y"] = location.z,
|
|
["x"] = location.x,
|
|
["dead"] = true,
|
|
}
|
|
|
|
coalition.addStaticObject(countryId, staticObj)
|
|
end
|
|
|
|
function DCS_UTIL.CleanCorpse(unitName)
|
|
local unitName = "dead_" .. unitName
|
|
|
|
local object = StaticObject.getByName(unitName)
|
|
|
|
if object then
|
|
object:destroy()
|
|
end
|
|
end
|
|
|
|
--- spawns the units as specified in the mission file itself
|
|
--- location and route can be nil and will then use default route
|
|
---@param groupName string
|
|
---@param location table? vector 3 data. { x , z, alt }
|
|
---@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
|
|
---@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)
|
|
if groupName == nil then
|
|
return nil, nil
|
|
end
|
|
|
|
local template = DCS_UTIL.GetMizGroupOrDefault(groupName, nil)
|
|
if template == nil then
|
|
return nil, nil
|
|
end
|
|
if template.category == DCS_UTIL.GroupCategory.STATIC then
|
|
--TODO: Implement location and route stuff
|
|
local spawn_template = template.group_template
|
|
return coalition.addStaticObject(template.country_id, spawn_template), true
|
|
else
|
|
local spawn_template = template.group_template
|
|
if location ~= nil then
|
|
local x_offset
|
|
if location.x ~= nil then x_offset = spawn_template.x - location.x end
|
|
|
|
local y_offset
|
|
if location.z ~= nil then y_offset = spawn_template.y - location.z end
|
|
|
|
spawn_template.x = location.x
|
|
spawn_template.y = location.z
|
|
|
|
for i, unit in pairs(spawn_template.units) do
|
|
unit.x = unit.x - x_offset
|
|
unit.y = unit.y - y_offset
|
|
unit.alt = location.alt
|
|
end
|
|
end
|
|
|
|
if route ~= nil then
|
|
spawn_template.route = route
|
|
end
|
|
|
|
if uncontrolled ~= nil then
|
|
spawn_template.uncontrolled = uncontrolled
|
|
end
|
|
local new_group = coalition.addGroup(template.country_id, template.category, spawn_template)
|
|
return new_group, false
|
|
end
|
|
end
|
|
|
|
function DCS_UTIL.IsBingoFuel(groupName, offset)
|
|
if offset == nil then offset = 0 end
|
|
local bingoSetting = 0.20
|
|
bingoSetting = bingoSetting + offset
|
|
|
|
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 and unit:getFuel() < bingoSetting then
|
|
return true
|
|
end
|
|
end
|
|
end
|
|
|
|
return false
|
|
end
|
|
|
|
---comment
|
|
---@param groupId number
|
|
---@return Group?
|
|
function DCS_UTIL.GetPlayerGroupByGroupID(groupId)
|
|
for i = 0,2 do
|
|
local players = coalition.getPlayers(i)
|
|
for key, unit in pairs(players) do
|
|
if unit and unit:isExist() == true then
|
|
local group = unit:getGroup()
|
|
if group and group:getID() == groupId then
|
|
return group
|
|
end
|
|
end
|
|
end
|
|
end
|
|
end
|
|
|
|
DCS_UTIL.__INIT();
|
|
end
|
|
Spearhead.DcsUtil = DCS_UTIL
|
|
|
|
--- @class Logger
|
|
--- @field LoggerName string the name of the logger
|
|
--- @field LogLevel string the log level of the logger
|
|
local LOGGER = {}
|
|
do
|
|
local PreFix = "Spearhead"
|
|
|
|
---comment
|
|
---@param logger_name any
|
|
---@param logLevel LogLevel
|
|
---@return Logger
|
|
function LOGGER.new(logger_name, logLevel)
|
|
LOGGER.__index = LOGGER
|
|
local self = setmetatable({}, LOGGER)
|
|
self.LoggerName = logger_name or "(loggername not set)"
|
|
self.LogLevel = logLevel or "INFO"
|
|
|
|
return self
|
|
end
|
|
|
|
---@param message any the message
|
|
function LOGGER:info(message)
|
|
if message == nil then
|
|
return
|
|
end
|
|
message = UTIL.toString(message)
|
|
|
|
if self.LogLevel == "INFO" or self.LogLevel == "DEBUG" then
|
|
env.info("[" .. PreFix .. "]" .. "[" .. self.LoggerName .. "] " .. message)
|
|
end
|
|
end
|
|
|
|
---comment
|
|
---@param message string
|
|
function LOGGER:warn(message)
|
|
if message == nil then
|
|
return
|
|
end
|
|
message = UTIL.toString(message)
|
|
|
|
if self.LogLevel == "INFO" or self.LogLevel == "DEBUG" or self.LogLevel == "WARN" then
|
|
env.warning("[" .. PreFix .. "]" .. "[" .. self.LoggerName .. "] " .. message)
|
|
end
|
|
end
|
|
|
|
---@param message any -- the message
|
|
function LOGGER:error(message)
|
|
if message == nil then
|
|
return
|
|
end
|
|
|
|
message = UTIL.toString(message)
|
|
|
|
if self.LogLevel == "INFO" or self.LogLevel == "DEBUG" or self.LogLevel == "WARN" or self.LogLevel == "ERROR" then
|
|
env.error("[" .. PreFix .. "]" .. "[" .. self.LoggerName .. "] " .. message)
|
|
end
|
|
end
|
|
|
|
---@param message any the message
|
|
function LOGGER:debug(message)
|
|
if message == nil then
|
|
return
|
|
end
|
|
|
|
message = UTIL.toString(message)
|
|
if self.LogLevel == "DEBUG" then
|
|
env.info("[" .. PreFix .. "]" .. "[" .. self.LoggerName .. "][DEBUG] " .. message)
|
|
end
|
|
end
|
|
end
|
|
Spearhead.LoggerTemplate = LOGGER
|
|
|
|
Spearhead.MissionEditingWarnings = {}
|
|
function Spearhead.AddMissionEditorWarning(warningMessage)
|
|
table.insert(Spearhead.MissionEditingWarnings, warningMessage or "skip")
|
|
end
|
|
|
|
local loadDone = false
|
|
Spearhead.LoadingDone = function()
|
|
if loadDone == true then
|
|
return
|
|
end
|
|
|
|
local warningLogger = Spearhead.LoggerTemplate.new("MISSIONPARSER", "INFO")
|
|
if Spearhead.Util.tableLength(Spearhead.MissionEditingWarnings) > 0 then
|
|
for key, message in pairs(Spearhead.MissionEditingWarnings) do
|
|
warningLogger:warn(message)
|
|
end
|
|
else
|
|
warningLogger:info("No issues detected")
|
|
end
|
|
|
|
loadDone = true
|
|
end
|