From a047a04d0998601c7d0141b73a60b0144bad127d Mon Sep 17 00:00:00 2001 From: Pax1601 Date: Fri, 21 Apr 2023 19:00:29 +0200 Subject: [PATCH] Issue fixed by updating MIST and checking method existance --- scripts/OlympusCommand.lua | 5 +- scripts/{mist_4_4_90.lua => mist.lua} | 2289 ++++++++++++++++++++++--- src/core/src/scriptloader.cpp | 2 +- 3 files changed, 2017 insertions(+), 279 deletions(-) rename scripts/{mist_4_4_90.lua => mist.lua} (73%) diff --git a/scripts/OlympusCommand.lua b/scripts/OlympusCommand.lua index b3bf9c57..153d57d5 100644 --- a/scripts/OlympusCommand.lua +++ b/scripts/OlympusCommand.lua @@ -1,6 +1,6 @@ local version = "v0.2.0-alpha" -local debug = true +local debug = false Olympus.unitCounter = 1 Olympus.payloadRegistry = {} @@ -38,7 +38,7 @@ end function Olympus.getUnitByID(ID) for name, table in pairs(mist.DBs.unitsByName) do local unit = Unit.getByName(name) - if unit and unit:getObjectID() == ID then + if unit and unit["getObjectID"] and unit:getObjectID() == ID then return unit end end @@ -553,6 +553,7 @@ function Olympus.setMissionData(arg, time) local mission = {} mission.theatre = env.mission.theatre + mission.time = DCS.getRealTime() -- Assemble missionData table missionData["bullseyes"] = bullseyes diff --git a/scripts/mist_4_4_90.lua b/scripts/mist.lua similarity index 73% rename from scripts/mist_4_4_90.lua rename to scripts/mist.lua index fbf4f6ac..431848d8 100644 --- a/scripts/mist_4_4_90.lua +++ b/scripts/mist.lua @@ -34,17 +34,19 @@ mist = {} -- don't change these mist.majorVersion = 4 -mist.minorVersion = 4 -mist.build = 90 +mist.minorVersion = 5 +mist.build = 107 -- forward declaration of log shorthand local log - +local dbLog + local mistSettings = { errorPopup = false, -- errors printed by mist logger will create popup warning you warnPopup = false, infoPopup = false, logLevel = 'warn', + dbLog = 'warn', } do -- the main scope @@ -73,11 +75,11 @@ do -- the main scope mist.nextGroupId = 1 mist.nextUnitId = 1 - local dbLog + local function initDBs() -- mist.DBs scope mist.DBs = {} - + mist.DBs.markList = {} mist.DBs.missionData = {} if env.mission then @@ -106,6 +108,25 @@ do -- the main scope zone.point.x = zone_data.x zone.point.y = 0 zone.point.z = zone_data.y + zone.properties = {} + if zone_data.properties then + for propInd, prop in pairs(zone_data.properties) do + if prop.value and type(prop.value) == 'string' and prop.value ~= "" then + zone.properties[prop.key] = prop.value + end + end + end + if zone.verticies then -- trust but verify + local r = 0 + for i = 1, #zone.verticies do + local dist = mist.utils.get2DDist(zone.point, zone.verticies[i]) + if dist > r then + r = mist.utils.deepCopy(dist) + end + end + zone.radius = r + + end mist.DBs.zonesByName[zone_data.name] = zone mist.DBs.zonesByNum[#mist.DBs.zonesByNum + 1] = mist.utils.deepCopy(zone) --[[deepcopy so that the zone in zones_by_name and the zone in @@ -113,6 +134,85 @@ do -- the main scope end end end + + mist.DBs.drawingByName = {} + mist.DBs.drawingIndexed = {} + + if env.mission.drawings and env.mission.drawings.layers then + for i = 1, #env.mission.drawings.layers do + local l = env.mission.drawings.layers[i] + + for j = 1, #l.objects do + local copy = mist.utils.deepCopy(l.objects[j]) + --log:warn(copy) + local doOffset = false + copy.layer = l.name + + local theta = copy.angle or 0 + theta = math.rad(theta) + if copy.primitiveType == "Polygon" then + + if copy.polygonMode == 'rect' then + local h, w = copy.height, copy.width + copy.points = {} + copy.points[1] = {x = h/2, y = w/2} + copy.points[2] = {x = -h/2, y = w/2} + copy.points[3] = {x = -h/2, y = -w/2} + copy.points[4] = {x = h/2, y = -w/2} + doOffset = true + elseif copy.polygonMode == "circle" then + copy.points = {x = copy.mapX, y = copy.mapY} + elseif copy.polygonMode == 'oval' then + copy.points = {} + local numPoints = 24 + local angleStep = (math.pi*2)/numPoints + doOffset = true + for v = 1, numPoints do + local pointAngle = v * angleStep + local x = copy.r1 * math.cos(pointAngle) + local y = copy.r2 * math.sin(pointAngle) + + table.insert(copy.points,{x=x,y=y}) + + end + elseif copy.polygonMode == "arrow" then + doOffset = true + end + + + if theta ~= 0 and copy.points and doOffset == true then + + --log:warn('offsetting Values') + for p = 1, #copy.points do + local offset = mist.vec.rotateVec2(copy.points[p], theta) + copy.points[p] = offset + end + --log:warn(copy.points[1]) + end + + elseif copy.primitiveType == "Line" and copy.closed == true then + table.insert(copy.points, mist.utils.deepCopy(copy.points[1])) + end + if copy.points and #copy.points > 1 then + for u = 1, #copy.points do + copy.points[u].x = mist.utils.round(copy.points[u].x + copy.mapX, 2) + copy.points[u].y = mist.utils.round(copy.points[u].y + copy.mapY, 2) + end + + end + if mist.DBs.drawingByName[copy.name] then + log:warn("Drawing by the name of [ $1 ] already exists in DB. Failed to add to mist.DBs.drawingByName.", copy.name) + else + + mist.DBs.drawingByName[copy.name] = copy + end + table.insert(mist.DBs.drawingIndexed, copy) + end + + end + + end + mist.DBs.navPoints = {} mist.DBs.units = {} @@ -151,28 +251,31 @@ do -- the main scope for cntry_id, cntry_data in pairs(coa_data.country) do local countryName = string.lower(cntry_data.name) + if cntry_data.id and country.names[cntry_data.id] then + countryName = string.lower(country.names[cntry_data.id]) + end mist.DBs.units[coa_name][countryName] = {} mist.DBs.units[coa_name][countryName].countryId = cntry_data.id if type(cntry_data) == 'table' then --just making sure - for obj_type_name, obj_type_data in pairs(cntry_data) do + for obj_cat_name, obj_cat_data in pairs(cntry_data) do - if obj_type_name == "helicopter" or obj_type_name == "ship" or obj_type_name == "plane" or obj_type_name == "vehicle" or obj_type_name == "static" then --should be an unncessary check + if obj_cat_name == "helicopter" or obj_cat_name == "ship" or obj_cat_name == "plane" or obj_cat_name == "vehicle" or obj_cat_name == "static" then --should be an unncessary check - local category = obj_type_name + local category = obj_cat_name - if ((type(obj_type_data) == 'table') and obj_type_data.group and (type(obj_type_data.group) == 'table') and (#obj_type_data.group > 0)) then --there's a group! + if ((type(obj_cat_data) == 'table') and obj_cat_data.group and (type(obj_cat_data.group) == 'table') and (#obj_cat_data.group > 0)) then --there's a group! mist.DBs.units[coa_name][countryName][category] = {} - for group_num, group_data in pairs(obj_type_data.group) do + for group_num, group_data in pairs(obj_cat_data.group) do if group_data and group_data.units and type(group_data.units) == 'table' then --making sure again- this is a valid group mist.DBs.units[coa_name][countryName][category][group_num] = {} local groupName = group_data.name - if env.mission.version > 7 then + if env.mission.version > 7 and env.mission.version < 19 then groupName = env.getValueDictByKey(groupName) end mist.DBs.units[coa_name][countryName][category][group_num].groupName = groupName @@ -196,7 +299,7 @@ do -- the main scope local units_tbl = mist.DBs.units[coa_name][countryName][category][group_num].units --pointer to the units table for this group units_tbl[unit_num] = {} - if env.mission.version > 7 then + if env.mission.version > 7 and env.mission.version < 19 then units_tbl[unit_num].unitName = env.getValueDictByKey(unit_data.name) else units_tbl[unit_num].unitName = unit_data.name @@ -240,6 +343,7 @@ do -- the main scope if category == 'static' then units_tbl[unit_num].categoryStatic = unit_data.category units_tbl[unit_num].shape_name = unit_data.shape_name + units_tbl[unit_num].linkUnit = unit_data.linkUnit if unit_data.mass then units_tbl[unit_num].mass = unit_data.mass end @@ -251,10 +355,10 @@ do -- the main scope end --for unit_num, unit_data in pairs(group_data.units) do end --if group_data and group_data.units then - end --for group_num, group_data in pairs(obj_type_data.group) do - end --if ((type(obj_type_data) == 'table') and obj_type_data.group and (type(obj_type_data.group) == 'table') and (#obj_type_data.group > 0)) then - end --if obj_type_name == "helicopter" or obj_type_name == "ship" or obj_type_name == "plane" or obj_type_name == "vehicle" or obj_type_name == "static" then - end --for obj_type_name, obj_type_data in pairs(cntry_data) do + end --for group_num, group_data in pairs(obj_cat_data.group) do + end --if ((type(obj_cat_data) == 'table') and obj_cat_data.group and (type(obj_cat_data.group) == 'table') and (#obj_cat_data.group > 0)) then + end --if obj_cat_name == "helicopter" or obj_cat_name == "ship" or obj_cat_name == "plane" or obj_cat_name == "vehicle" or obj_cat_name == "static" then + end --for obj_cat_name, obj_cat_data in pairs(cntry_data) do end --if type(cntry_data) == 'table' then end --for cntry_id, cntry_data in pairs(coa_data.country) do end --if coa_data.country then --there is a country table @@ -305,6 +409,11 @@ do -- the main scope ['Arco'] = 2, ['Shell'] = 3, }, + ['TRANSPORT'] = { + ['Heavy'] = 9, + ['Trash'] = 10, + ['Cargo'] = 11, + ['Ascot'] = 12, ['JTAC'] = { ['Axeman'] = 1, ['Darknight'] = 2, @@ -346,14 +455,105 @@ do -- the main scope ['rules'] = { ['canUseAircraft'] = true, ['appliesTo'] = { - 'A-10C', + 'A-10C_2', + 'A-10C', 'A-10A', }, }, - }, - }, - }, - } + }, + ['f16'] = { + Viper = 9, + Venom = 10, + Lobo = 11, + Cowboy = 12, + Python = 13, + Rattler =14, + Panther = 15, + Wolf = 16, + Weasel = 17, + Wild = 18, + Ninja = 19, + Jedi = 20, + rules = { + ['canUseAircraft'] = true, + ['appliesTo'] = { + 'F-16C_50', + 'F-16C bl.52d', + 'F-16C bl.50', + 'F-16A MLU', + 'F-16A', + }, + }, + + }, + ['f18'] = { + ['Hornet'] = 9, + ['Squid'] = 10, + ['Ragin'] = 11, + ['Roman'] = 12, + Sting = 13, + Jury =14, + Jokey = 15, + Ram = 16, + Hawk = 17, + Devil = 18, + Check = 19, + Snake = 20, + ['rules'] = { + ['canUseAircraft'] = true, + ['appliesTo'] = { + + "FA-18C_hornet", + 'F/A-18C', + }, + }, + }, + ['b1'] = { + ['Bone'] = 9, + ['Dark'] = 10, + ['Vader'] = 11, + ['rules'] = { + ['canUseAircraft'] = true, + ['appliesTo'] = { + 'B-1B', + }, + }, + }, + ['b52'] = { + ['Buff'] = 9, + ['Dump'] = 10, + ['Kenworth'] = 11, + ['rules'] = { + ['canUseAircraft'] = true, + ['appliesTo'] = { + 'B-52H', + }, + }, + }, + ['f15e'] = { + ['Dude'] = 9, + ['Thud'] = 10, + ['Gunny'] = 11, + ['Trek'] = 12, + Sniper = 13, + Sled =14, + Best = 15, + Jazz = 16, + Rage = 17, + Tahoe = 18, + ['rules'] = { + ['canUseAircraft'] = true, + ['appliesTo'] = { + 'F-15E', + --'F-15ERAZBAM', + }, + }, + }, + + }, + }, + }, + } mist.DBs.const.shapeNames = { ["Landmine"] = "landmine", ["FARP CP Blindage"] = "kp_ug", @@ -443,6 +643,42 @@ do -- the main scope ["Small house 1A area"] = "domik1a-all", ["White_Flag"] = "H-Flag_W", ["Airshow_Cone"] = "Comp_cone", + ["Bulk Cargo Ship Ivanov"] = "barge-1", + ["Bulk Cargo Ship Yakushev"] = "barge-2", + ["Outpost"]="block", + ["Road outpost"]="block-onroad", + ["Container camo"] = "bw_container_cargo", + ["Tech Hangar A"] = "ceh_ang_a", + ["Bunker 1"] = "dot", + ["Bunker 2"] = "dot2", + ["Tanker Elnya 160"] = "elnya", + ["F-shape barrier"] = "f_bar_cargo", + ["Helipad Single"] = "farp", + ["FARP"] = "farps", + ["Fueltank"] = "fueltank_cargo", + ["Gate"] = "gate", + ["FARP Fuel Depot"] = "gsm rus", + ["Armed house"] = "home1_a", + ["FARP Command Post"] = "kp-ug", + ["Watch Tower Armed"] = "ohr-vyshka", + ["Oiltank"] = "oiltank_cargo", + ["Pipes small"] = "pipes_small_cargo", + ["Pipes big"] = "pipes_big_cargo", + ["Oil platform"] = "plavbaza", + ["Tetrapod"] = "tetrapod_cargo", + ["Fuel tank"] = "toplivo", + ["Trunks long"] = "trunks_long_cargo", + ["Trunks small"] = "trunks_small_cargo", + ["Passenger liner"] = "yastrebow", + ["Passenger boat"] = "zwezdny", + ["Oil rig"] = "oil_platform", + ["Gas platform"] = "gas_platform", + ["Container 20ft"] = "container_20ft", + ["Container 40ft"] = "container_40ft", + ["Downed pilot"] = "cadaver", + ["Parachute"] = "parash", + ["Pilot F15 Parachute"] = "pilot_f15_parachute", + ["Pilot standing"] = "pilot_parashut", } @@ -504,8 +740,8 @@ do -- the main scope mist.DBs.MEgroupsById = mist.utils.deepCopy(mist.DBs.groupsById) mist.DBs.deadObjects = {} - - do + + do local mt = {} function mt.__newindex(t, key, val) @@ -649,9 +885,14 @@ do -- the main scope newTable.category = 'static' else unitOneRef = newObject:getUnits() - newTable.countryId = tonumber(unitOneRef[1]:getCountry()) - newTable.coalitionId = tonumber(unitOneRef[1]:getCoalition()) - newTable.category = tonumber(newObject:getCategory()) + if #unitOneRef > 0 and unitOneRef[1] and type(unitOneRef[1]) == 'table' then + newTable.countryId = tonumber(unitOneRef[1]:getCountry()) + newTable.coalitionId = tonumber(unitOneRef[1]:getCoalition()) + newTable.category = tonumber(newObject:getCategory()) + else + log:warn('getUnits failed to return on $1 ; Built Data: $2.', event, newTable) + return false + end end for countryData, countryId in pairs(country.id) do if newTable.country and string.upper(countryData) == string.upper(newTable.country) or countryId == newTable.countryId then @@ -782,6 +1023,8 @@ do -- the main scope newTable.units[1].canCargo = data.canCargo newTable.units[1].categoryStatic = data.categoryStatic newTable.units[1].type = data.type + newTable.units[1].linkUnit = data.linkUnit + mistAddedObjects[index] = nil break end @@ -818,6 +1061,7 @@ do -- the main scope --dbLog:info('iterate') for name, gData in pairs(tempSpawnedGroups) do --env.info(name) + --dbLog:info(gData) local updated = false local stillExists = false if not gData.checked then @@ -825,9 +1069,9 @@ do -- the main scope local _g = gData.gp or Group.getByName(name) if mist.DBs.groupsByName[name] then -- first check group level properties, groupId, countryId, coalition - -- dbLog:info('Found in DBs, check if updated') + --dbLog:info('Found in DBs, check if updated') local dbTable = mist.DBs.groupsByName[name] - -- dbLog:info(dbTable) + --dbLog:info(dbTable) if gData.type ~= 'static' then -- dbLog:info('Not static') @@ -848,11 +1092,11 @@ do -- the main scope end --dbLog:info('Updated: $1', updated) if updated == false and gData.type ~= 'static' then -- time to check units - --dbLog:info('No Group Mismatch, Check Units') + --dbLog:info('No Group Mismatch, Check Units') if _g and _g:isExist() == true then stillExists = true for index, uObject in pairs(_g:getUnits()) do - --dbLog:info(index) + --dbLog:info(index) if mist.DBs.unitsByName[uObject:getName()] then --dbLog:info('UnitByName table exists') local uTable = mist.DBs.unitsByName[uObject:getName()] @@ -870,8 +1114,10 @@ do -- the main scope if stillExists == true and (updated == true or not mist.DBs.groupsByName[name]) then --dbLog:info('Get Table') - writeGroups[#writeGroups+1] = {data = dbUpdate(name, gData.type), isUpdated = updated} - + local dbData = dbUpdate(name, gData.type) + if dbData and type(dbData) == 'table' then + writeGroups[#writeGroups+1] = {data = dbData, isUpdated = updated} + end end -- Work done, so remove end @@ -998,19 +1244,20 @@ do -- the main scope local function groupSpawned(event) -- dont need to add units spawned in at the start of the mission if mist is loaded in init line if event.id == world.event.S_EVENT_BIRTH and timer.getTime0() < timer.getAbsTime() then - --dbLog:info('unitSpawnEvent') - + --log:info('unitSpawnEvent') + --log:info(event) + --log:info(event.initiator:getTypeName()) --table.insert(tempSpawnedUnits,(event.initiator)) ------- -- New functionality below. ------- if Object.getCategory(event.initiator) == 1 and not Unit.getPlayerName(event.initiator) then -- simple player check, will need to later check to see if unit was spawned with a player in a flight - --dbLog:info('Object is a Unit') + --log:info('Object is a Unit') if Unit.getGroup(event.initiator) then - --dbLog:info(Unit.getGroup(event.initiator):getName()) + -- log:info(Unit.getGroup(event.initiator):getName()) local g = Unit.getGroup(event.initiator) if not tempSpawnedGroups[g:getName()] then - --dbLog:info('added') + --log:info('added') tempSpawnedGroups[g:getName()] = {type = 'group', gp = g} tempSpawnGroupsCounter = tempSpawnGroupsCounter + 1 end @@ -1018,7 +1265,7 @@ do -- the main scope log:error('Group not accessible by unit in event handler. This is a DCS bug') end elseif Object.getCategory(event.initiator) == 3 or Object.getCategory(event.initiator) == 6 then - --dbLog:info('Object is Static') + --log:info('Object is Static') tempSpawnedGroups[StaticObject.getName(event.initiator)] = {type = 'static'} tempSpawnGroupsCounter = tempSpawnGroupsCounter + 1 end @@ -1169,10 +1416,13 @@ do -- the main scope for i = 1, #st do local s = st[i] if StaticObject.isExist(s) then - if not mist.DBs.unitsByName[s:getName()] then - --env.info(StaticObject.getID(s) .. ' Not found in DB yet') - tempSpawnedGroups[s:getName()] = {type = 'static'} - tempSpawnGroupsCounter = tempSpawnGroupsCounter + 1 + local name = s:getName() + if not mist.DBs.unitsByName[name] then + dbLog:warn('$1 Not found in DB yet. ID: $2', name, StaticObject.getID(s)) + if string.len(name) > 0 then -- because in this mission someone sent the name was returning as an empty string. Gotta be careful. + tempSpawnedGroups[s:getName()] = {type = 'static'} + tempSpawnGroupsCounter = tempSpawnGroupsCounter + 1 + end end end end @@ -1180,6 +1430,7 @@ do -- the main scope end end + --- init function. -- creates logger, adds default event handler @@ -1284,8 +1535,9 @@ do -- the main scope --- Spawns a static object to the game world. -- @todo write good docs -- @tparam table staticObj table containing data needed for the object creation - function mist.dynAddStatic(newObj) - log:info(newObj) + function mist.dynAddStatic(n) + --log:info(newObj) + local newObj = mist.utils.deepCopy(n) if newObj.units and newObj.units[1] then -- if its mist format for entry, val in pairs(newObj.units[1]) do if newObj[entry] and newObj[entry] ~= val or not newObj[entry] then @@ -1366,7 +1618,7 @@ do -- the main scope mistAddedObjects[#mistAddedObjects + 1] = mist.utils.deepCopy(newObj) if newObj.x and newObj.y and newObj.type and type(newObj.x) == 'number' and type(newObj.y) == 'number' and type(newObj.type) == 'string' then - log:info(newObj) + --log:warn(newObj) coalition.addStaticObject(country.id[newCountry], newObj) return newObj @@ -1379,8 +1631,10 @@ do -- the main scope -- Same as coalition.add function in SSE. checks the passed data to see if its valid. -- Will generate groupId, groupName, unitId, and unitName if needed -- @tparam table newGroup table containting values needed for spawning a group. - function mist.dynAdd(newGroup) - + function mist.dynAdd(ng) + + local newGroup = mist.utils.deepCopy(ng) + --log:warn(newGroup) --mist.debug.writeData(mist.utils.serialize,{'msg', newGroup}, 'newGroupOrig.lua') local cntry = newGroup.country if newGroup.countryId then @@ -1452,7 +1706,11 @@ do -- the main scope end if newGroup.clone and mist.DBs.groupsByName[newGroup.name] or not newGroup.name then - newGroup.name = tostring(newCountry .. tostring(typeName) .. mistDynAddIndex[typeName]) + --if newGroup.baseName then + -- idea of later. So custmozed naming can be created + -- else + newGroup.name = tostring(newCountry .. tostring(typeName) .. mistDynAddIndex[typeName]) + --end end if not newGroup.hidden then @@ -1537,16 +1795,32 @@ do -- the main scope end end else -- if aircraft and no route assigned. make a quick and stupid route so AI doesnt RTB immediately - if newCat == 'AIRPLANE' or newCat == 'HELICOPTER' then + --if newCat == 'AIRPLANE' or newCat == 'HELICOPTER' then newGroup.route = {} newGroup.route.points = {} newGroup.route.points[1] = {} - end + --end end newGroup.country = newCountry - - --mist.debug.writeData(mist.utils.serialize,{'msg', newGroup}, 'newGroup.lua') + -- update and verify any self tasks + if newGroup.route and newGroup.route.points then + for i, pData in pairs(newGroup.route.points) do + if pData.task and pData.task.params and pData.task.params.tasks and #pData.task.params.tasks > 0 then + for tIndex, tData in pairs(pData.task.params.tasks) do + if tData.params and tData.params.action then + if tData.params.action.id == "EPLRS" then + tData.params.action.params.groupId = newGroup.groupId + elseif tData.params.action.id == "ActivateBeacon" or tData.params.action.id == "ActivateICLS" then + tData.params.action.params.unitId = newGroup.units[1].unitId + end + end + end + end + + end + end + --mist.debug.writeData(mist.utils.serialize,{'msg', newGroup}, 'newGroupPushedToAddGroup.lua') --log:warn(newGroup) -- sanitize table newGroup.groupName = nil @@ -1559,7 +1833,7 @@ do -- the main scope for unitIndex, unitData in pairs(newGroup.units) do newGroup.units[unitIndex].unitName = nil end - + coalition.addGroup(country.id[newCountry], Unit.Category[newCat], newGroup) return newGroup @@ -1816,10 +2090,10 @@ do if type(coa_data) == 'table' then if coa_data.country then --there is a country table for cntry_id, cntry_data in pairs(coa_data.country) do - for obj_type_name, obj_type_data in pairs(cntry_data) do - if obj_type_name == "helicopter" or obj_type_name == "ship" or obj_type_name == "plane" or obj_type_name == "vehicle" then -- only these types have points - if ((type(obj_type_data) == 'table') and obj_type_data.group and (type(obj_type_data.group) == 'table') and (#obj_type_data.group > 0)) then --there's a group! - for group_num, group_data in pairs(obj_type_data.group) do + for obj_cat_name, obj_cat_data in pairs(cntry_data) do + if obj_cat_name == "helicopter" or obj_cat_name == "ship" or obj_cat_name == "plane" or obj_cat_name == "vehicle" then -- only these types have points + if ((type(obj_cat_data) == 'table') and obj_cat_data.group and (type(obj_cat_data.group) == 'table') and (#obj_cat_data.group > 0)) then --there's a group! + for group_num, group_data in pairs(obj_cat_data.group) do if group_data and group_data.groupId == gpId then -- this is the group we are looking for if group_data.route and group_data.route.points and #group_data.route.points > 0 then local points = {} @@ -1834,10 +2108,10 @@ do end return end --if group_data and group_data.name and group_data.name == 'groupname' - end --for group_num, group_data in pairs(obj_type_data.group) do - end --if ((type(obj_type_data) == 'table') and obj_type_data.group and (type(obj_type_data.group) == 'table') and (#obj_type_data.group > 0)) then - end --if obj_type_name == "helicopter" or obj_type_name == "ship" or obj_type_name == "plane" or obj_type_name == "vehicle" or obj_type_name == "static" then - end --for obj_type_name, obj_type_data in pairs(cntry_data) do + end --for group_num, group_data in pairs(obj_cat_data.group) do + end --if ((type(obj_cat_data) == 'table') and obj_cat_data.group and (type(obj_cat_data.group) == 'table') and (#obj_cat_data.group > 0)) then + end --if obj_cat_name == "helicopter" or obj_cat_name == "ship" or obj_cat_name == "plane" or obj_cat_name == "vehicle" or obj_cat_name == "static" then + end --for obj_cat_name, obj_cat_data in pairs(cntry_data) do end --for cntry_id, cntry_data in pairs(coa_data.country) do end --if coa_data.country then --there is a country table end --if coa_name == 'red' or coa_name == 'blue' and type(coa_data) == 'table' then @@ -2214,9 +2488,25 @@ do --- Returns a table containing unit names. -- @tparam table tbl sequential strings -- @treturn table @{UnitNameTable} - function mist.makeUnitTable(tbl) + function mist.makeUnitTable(tbl, exclude) --Assumption: will be passed a table of strings, sequential --log:info(tbl) + + + local excludeType = {} + if exclude then + if type(exclude) == 'table' then + for x, y in pairs(exclude) do + excludeType[x] = true + excludeType[y] = true + end + else + excludeType[exclude] = true + end + + end + + local units_by_name = {} local l_munits = mist.DBs.units --local reference for faster execution @@ -2277,12 +2567,15 @@ do elseif unit:sub(4,12) == '[vehicle]' then category = 'vehicle' country_start = 13 + elseif unit:sub(4, 11) == '[static]' then + category = 'static' + country_start = 12 end for coa, coa_tbl in pairs(l_munits) do for country, country_table in pairs(coa_tbl) do if country == string.lower(unit:sub(country_start)) then -- match for unit_type, unit_type_tbl in pairs(country_table) do - if type(unit_type_tbl) == 'table' and (category == '' or unit_type == category) then + if type(unit_type_tbl) == 'table' and (category == '' or unit_type == category) and not excludeType[unit_type] then for group_ind, group_tbl in pairs(unit_type_tbl) do if type(group_tbl) == 'table' then for unit_ind, unit in pairs(group_tbl.units) do @@ -2310,12 +2603,15 @@ do elseif unit:sub(5,13) == '[vehicle]' then category = 'vehicle' country_start = 14 + elseif unit:sub(5, 12) == '[static]' then + category = 'static' + country_start = 13 end for coa, coa_tbl in pairs(l_munits) do for country, country_table in pairs(coa_tbl) do if country == string.lower(unit:sub(country_start)) then -- match for unit_type, unit_type_tbl in pairs(country_table) do - if type(unit_type_tbl) == 'table' and (category == '' or unit_type == category) then + if type(unit_type_tbl) == 'table' and (category == '' or unit_type == category) and not excludeType[unit_type] then for group_ind, group_tbl in pairs(unit_type_tbl) do if type(group_tbl) == 'table' then for unit_ind, unit in pairs(group_tbl.units) do @@ -2340,12 +2636,14 @@ do category = 'ship' elseif unit:sub(7) == '[vehicle]' then category = 'vehicle' - end + elseif unit:sub(7) == '[static]' then + category = 'static' + end for coa, coa_tbl in pairs(l_munits) do if coa == 'blue' then for country, country_table in pairs(coa_tbl) do for unit_type, unit_type_tbl in pairs(country_table) do - if type(unit_type_tbl) == 'table' and (category == '' or unit_type == category) then + if type(unit_type_tbl) == 'table' and (category == '' or unit_type == category) and not excludeType[unit_type] then for group_ind, group_tbl in pairs(unit_type_tbl) do if type(group_tbl) == 'table' then for unit_ind, unit in pairs(group_tbl.units) do @@ -2368,12 +2666,14 @@ do category = 'ship' elseif unit:sub(8) == '[vehicle]' then category = 'vehicle' + elseif unit:sub(8) == '[static]' then + category = 'static' end for coa, coa_tbl in pairs(l_munits) do if coa == 'blue' then for country, country_table in pairs(coa_tbl) do for unit_type, unit_type_tbl in pairs(country_table) do - if type(unit_type_tbl) == 'table' and (category == '' or unit_type == category) then + if type(unit_type_tbl) == 'table' and (category == '' or unit_type == category) and not excludeType[unit_type] then for group_ind, group_tbl in pairs(unit_type_tbl) do if type(group_tbl) == 'table' then for unit_ind, unit in pairs(group_tbl.units) do @@ -2398,12 +2698,14 @@ do category = 'ship' elseif unit:sub(6) == '[vehicle]' then category = 'vehicle' + elseif unit:sub(6) == '[static]' then + category = 'static' end for coa, coa_tbl in pairs(l_munits) do if coa == 'red' then for country, country_table in pairs(coa_tbl) do for unit_type, unit_type_tbl in pairs(country_table) do - if type(unit_type_tbl) == 'table' and (category == '' or unit_type == category) then + if type(unit_type_tbl) == 'table' and (category == '' or unit_type == category) and not excludeType[unit_type] then for group_ind, group_tbl in pairs(unit_type_tbl) do if type(group_tbl) == 'table' then for unit_ind, unit in pairs(group_tbl.units) do @@ -2426,12 +2728,14 @@ do category = 'ship' elseif unit:sub(7) == '[vehicle]' then category = 'vehicle' + elseif unit:sub(7) == '[static]' then + category = 'static' end for coa, coa_tbl in pairs(l_munits) do if coa == 'red' then for country, country_table in pairs(coa_tbl) do for unit_type, unit_type_tbl in pairs(country_table) do - if type(unit_type_tbl) == 'table' and (category == '' or unit_type == category) then + if type(unit_type_tbl) == 'table' and (category == '' or unit_type == category) and not excludeType[unit_type] then for group_ind, group_tbl in pairs(unit_type_tbl) do if type(group_tbl) == 'table' then for unit_ind, unit in pairs(group_tbl.units) do @@ -2456,11 +2760,13 @@ do category = 'ship' elseif unit:sub(6) == '[vehicle]' then category = 'vehicle' + elseif unit:sub(6) == '[static]' then + category = 'static' end for coa, coa_tbl in pairs(l_munits) do for country, country_table in pairs(coa_tbl) do for unit_type, unit_type_tbl in pairs(country_table) do - if type(unit_type_tbl) == 'table' and (category == '' or unit_type == category) then + if type(unit_type_tbl) == 'table' and (category == '' or unit_type == category) and not excludeType[unit_type] then for group_ind, group_tbl in pairs(unit_type_tbl) do if type(group_tbl) == 'table' then for unit_ind, unit in pairs(group_tbl.units) do @@ -2482,11 +2788,13 @@ do category = 'ship' elseif unit:sub(7) == '[vehicle]' then category = 'vehicle' + elseif unit:sub(7) == '[static]' then + category = 'static' end for coa, coa_tbl in pairs(l_munits) do for country, country_table in pairs(coa_tbl) do for unit_type, unit_type_tbl in pairs(country_table) do - if type(unit_type_tbl) == 'table' and (category == '' or unit_type == category) then + if type(unit_type_tbl) == 'table' and (category == '' or unit_type == category) and not excludeType[unit_type] then for group_ind, group_tbl in pairs(unit_type_tbl) do if type(group_tbl) == 'table' then for unit_ind, unit in pairs(group_tbl.units) do @@ -2517,6 +2825,126 @@ do return units_tbl end +function mist.getUnitsByAttribute(att, rnum, id) + local cEntry = {} + cEntry.typeName = att.type or att.typeName or att.typename + cEntry.country = att.country + cEntry.coalition = att.coalition + cEntry.skill = att.skill + cEntry.categry = att.category + + local num = rnum or 1 + + if cEntry.skill == 'human' then + cEntry.skill = {'Client', 'Player'} + end + + + local checkedVal = {} + local units = {} + for uName, uData in pairs(mist.DBs.unitsByName) do + local matched = 0 + for cName, cVal in pairs(cEntry) do + if type(cVal) == 'table' then + for sName, sVal in pairs(cVal) do + if (uData[cName] and uData[cName] == sVal) or (uData[cName] and uData[cName] == sName) then + matched = matched + 1 + end + end + else + if uData[cName] and uData[cName] == cVal then + matched = matched + 1 + end + end + end + if matched >= num then + if id then + units[uData.unitId] = true + else + + units[uName] = true + end + end + end + + local rtn = {} + for name, _ in pairs(units) do + table.insert(rtn, name) + end + return rtn + +end + +function mist.getGroupsByAttribute(att, rnum, id) + local cEntry = {} + cEntry.typeName = att.type or att.typeName or att.typename + cEntry.country = att.country + cEntry.coalition = att.coalition + cEntry.skill = att.skill + cEntry.categry = att.category + + local num = rnum or 1 + + if cEntry.skill == 'human' then + cEntry.skill = {'Client', 'Player'} + end + local groups = {} + for gName, gData in pairs(mist.DBs.groupsByName) do + local matched = 0 + for cName, cVal in pairs(cEntry) do + if type(cVal) == 'table' then + for sName, sVal in pairs(cVal) do + if cName == 'skill' or cName == 'typeName' then + local lMatch = 0 + for uId, uData in pairs(gData.units) do + if (uData[cName] and uData[cName] == sVal) or (gData[cName] and gData[cName] == sName) then + lMatch = lMatch + 1 + break + end + end + if lMatch > 0 then + matched = matched + 1 + end + end + if (gData[cName] and gData[cName] == sVal) or (gData[cName] and gData[cName] == sName) then + matched = matched + 1 + break + end + end + else + if cName == 'skill' or cName == 'typeName' then + local lMatch = 0 + for uId, uData in pairs(gData.units) do + if (uData[cName] and uData[cName] == sVal) then + lMatch = lMatch + 1 + break + end + end + if lMatch > 0 then + matched = matched + 1 + end + end + if gData[cName] and gData[cName] == cVal then + matched = matched + 1 + end + end + end + if matched >= num then + if id then + groups[gData.groupid] = true + else + groups[gName] = true + end + end + end + local rtn = {} + for name, _ in pairs(groups) do + table.insert(rtn, name) + end + return rtn + +end + function mist.getDeadMapObjsInZones(zone_names) -- zone_names: table of zone names -- returns: table of dead map objects (indexed numerically) @@ -2552,6 +2980,160 @@ function mist.getDeadMapObjsInPolygonZone(zone) end return map_objs end +mist.shape = {} +function mist.shape.insideShape(shape1, shape2, full) + if shape1.radius then -- probably a circle + if shape2.radius then + return mist.shape.circleInCircle(shape1, shape2, full) + elseif shape2[1] then + return mist.shape.circleInPoly(shape1, shape2, full) + end + + elseif shape1[1] then -- shape1 is probably a polygon + if shape2.radius then + return mist.shape.polyInCircle(shape1, shape2, full) + elseif shape2[1] then + return mist.shape.polyInPoly(shape1, shape2, full) + end + end + return false +end + +function mist.shape.circleInCircle(c1, c2, full) + if not full then -- quick partial check + if mist.utils.get2DDist(c1.point, c2.point) <= c2.radius then + return true + end + end + local theta = mist.utils.getHeadingPoints(c2.point, c1.point) -- heading from + if full then + return mist.utils.get2DDist(mist.projectPoint(c1.point, c1.radius, theta), c2.point) <= c2.radius + else + return mist.utils.get2DDist(mist.projectPoint(c1.point, c1.radius, theta + math.pi), c2.point) <= c2.radius + end + return false +end + + +function mist.shape.circleInPoly(circle, poly, full) + + if poly and type(poly) == 'table' and circle and type(circle) == 'table' and circle.radius and circle.point then + if not full then + for i = 1, #poly do + if mist.utils.get2DDist(circle.point, poly[i]) <= circle.radius then + return true + end + end + end + -- no point is inside of the zone, now check if any part is + local count = 0 + for i = 1, #poly do + local theta -- heading of each set of points + if i == #poly then + theta = mist.utils.getHeadingPoints(poly[i],poly[1]) + else + theta = mist.utils.getHeadingPoints(poly[i],poly[i+1]) + end + -- offset + local pPoint = mist.projectPoint(circle.point, circle.radius, theta - (math.pi/180)) + local oPoint = mist.projectPoint(circle.point, circle.radius, theta + (math.pi/180)) + + + if mist.pointInPolygon(pPoint, poly) == true then + if (full and mist.pointInPolygon(oPoint, poly) == true) or not full then + return true + + end + + end + end + + end + return false +end + + +function mist.shape.polyInPoly(p1, p2, full) + local count = 0 + for i = 1, #p1 do + + if mist.pointInPolygon(p1[i], p2) then + count = count + 1 + end + if (not full) and count > 0 then + return true + end + end + if count == #p1 then + return true + end + + return false +end + +function mist.shape.polyInCircle(poly, circle, full) + local count = 0 + for i = 1, #poly do + if mist.utils.get2DDist(circle.point, poly[i]) <= circle.radius then + if full then + count = count + 1 + else + return true + end + end + end + if count == #poly then + return true + end + + return false +end + +function mist.shape.getPointOnSegment(point, seg, isSeg) + local p = mist.utils.makeVec2(point) + local s1 = mist.utils.makeVec2(seg[1]) + local s2 = mist.utils.makeVec2(seg[2]) + + + local cx, cy = p.x - s1.x, p.y - s1.y + local dx, dy = s2.x - s1.x, s2.x - s1.y + local d = (dx*dx + dy*dy) + + if d == 0 then + return {x = s1.x, y = s1.y} + end + local u = (cx*dx + cy*dy)/d + if isSeg then + if u < 0 then + u = 0 + elseif u > 1 then + u = 1 + end + end + return {x = s1.x + u*dx, y = s1.y + u*dy} +end + + +function mist.shape.segmentIntersect(segA, segB) + local dx1, dy1 = segA[2].x - segA[1].x, segA[2] - segA[1].y + local dx2, dy2 = segB[2].x - segB[1].x, segB[2] - segB[1].y + local dx3, dy3 = segA[1].x - segB[1].x, segA[1].y - segB[1].y + local d = dx1*dy2 - dy1*dx2 + if d == 0 then + return false + end + local t1 = (dx2*dy3 - dy2*dx3)/d + if t1 < 0 or t1 > 1 then + return false + end + local t2 = (dx1*dy3 - dy1*dx3)/d + if t2 < 0 or t2 > 1 then + return false + end + -- point of intersection + return true, segA[1].x + t1*dx1, segA[1].y + t1*dy1 +end + function mist.pointInPolygon(point, poly, maxalt) --raycasting point in polygon. Code from http://softsurfer.com/Archive/algorithm_0103/algorithm_0103.htm --[[local type_tbl = { @@ -2591,17 +3173,23 @@ function mist.pointInPolygon(point, poly, maxalt) --raycasting point in polygon. end end +function mist.mapValue(val, inMin, inMax, outMin, outMax) + return (val - inMin) * (outMax - outMin) / (inMax - inMin) + outMin +end + function mist.getUnitsInPolygon(unit_names, polyZone, max_alt) local units = {} for i = 1, #unit_names do - units[#units + 1] = Unit.getByName(unit_names[i]) + units[#units + 1] = Unit.getByName(unit_names[i]) or StaticObject.getByName(unit_names[i]) end local inZoneUnits = {} for i =1, #units do - if units[i]:isActive() and mist.pointInPolygon(units[i]:getPosition().p, polyZone, max_alt) then - inZoneUnits[#inZoneUnits + 1] = units[i] + local lUnit = units[i] + local lCat = lUnit:getCategory() + if ((lCat == 1 and lUnit:isActive()) or lCat ~= 1) and mist.pointInPolygon(lUnit:getPosition().p, polyZone, max_alt) then + inZoneUnits[#inZoneUnits + 1] = lUnit end end @@ -2609,8 +3197,7 @@ function mist.getUnitsInPolygon(unit_names, polyZone, max_alt) end function mist.getUnitsInZones(unit_names, zone_names, zone_type) - - zone_type = zone_type or 'cylinder' + zone_type = zone_type or 'cylinder' if zone_type == 'c' or zone_type == 'cylindrical' or zone_type == 'C' then zone_type = 'cylinder' end @@ -2622,9 +3209,13 @@ function mist.getUnitsInZones(unit_names, zone_names, zone_type) local units = {} local zones = {} - + + if zone_names and type(zone_names) == 'string' then + zone_names = {zone_names} + end for k = 1, #unit_names do - local unit = Unit.getByName(unit_names[k]) + + local unit = Unit.getByName(unit_names[k]) or StaticObject.getByName(unit_names[k]) if unit then units[#units + 1] = unit end @@ -2632,31 +3223,40 @@ function mist.getUnitsInZones(unit_names, zone_names, zone_type) for k = 1, #zone_names do - local zone = trigger.misc.getZone(zone_names[k]) + local zone = mist.DBs.zonesByName[zone_names[k]] if zone then - zones[#zones + 1] = {radius = zone.radius, x = zone.point.x, y = zone.point.y, z = zone.point.z} + zones[#zones + 1] = {radius = zone.radius, x = zone.point.x, y = zone.point.y, z = zone.point.z, verts = zone.verticies} end end local in_zone_units = {} - for units_ind = 1, #units do - for zones_ind = 1, #zones do + local lUnit = units[units_ind] + local unit_pos = lUnit:getPosition().p + local lCat = lUnit:getCategory() + for zones_ind = 1, #zones do if zone_type == 'sphere' then --add land height value for sphere zone type local alt = land.getHeight({x = zones[zones_ind].x, y = zones[zones_ind].z}) if alt then zones[zones_ind].y = alt end end - local unit_pos = units[units_ind]:getPosition().p - if unit_pos and units[units_ind]:isActive() == true then - if zone_type == 'cylinder' and (((unit_pos.x - zones[zones_ind].x)^2 + (unit_pos.z - zones[zones_ind].z)^2)^0.5 <= zones[zones_ind].radius) then - in_zone_units[#in_zone_units + 1] = units[units_ind] - break - elseif zone_type == 'sphere' and (((unit_pos.x - zones[zones_ind].x)^2 + (unit_pos.y - zones[zones_ind].y)^2 + (unit_pos.z - zones[zones_ind].z)^2)^0.5 <= zones[zones_ind].radius) then - in_zone_units[#in_zone_units + 1] = units[units_ind] - break - end + + 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 zones[zones_ind].verts then + if mist.pointInPolygon(unit_pos, zones[zones_ind].verts) then + in_zone_units[#in_zone_units + 1] = lUnit + end + + else + if zone_type == 'cylinder' and (((unit_pos.x - zones[zones_ind].x)^2 + (unit_pos.z - zones[zones_ind].z)^2)^0.5 <= zones[zones_ind].radius) then + in_zone_units[#in_zone_units + 1] = lUnit + break + elseif zone_type == 'sphere' and (((unit_pos.x - zones[zones_ind].x)^2 + (unit_pos.y - zones[zones_ind].y)^2 + (unit_pos.z - zones[zones_ind].z)^2)^0.5 <= zones[zones_ind].radius) then + in_zone_units[#in_zone_units + 1] = lUnit + break + end + end end end end @@ -2679,14 +3279,14 @@ function mist.getUnitsInMovingZones(unit_names, zone_unit_names, radius, zone_ty local zone_units = {} for k = 1, #unit_names do - local unit = Unit.getByName(unit_names[k]) + local unit = Unit.getByName(unit_names[k]) or StaticObject.getByName(unit_names[k]) if unit then units[#units + 1] = unit end end for k = 1, #zone_unit_names do - local unit = Unit.getByName(zone_unit_names[k]) + local unit = Unit.getByName(zone_unit_names[k]) or StaticObject.getByName(zone_unit_names[k]) if unit then zone_units[#zone_units + 1] = unit end @@ -2695,15 +3295,18 @@ function mist.getUnitsInMovingZones(unit_names, zone_unit_names, radius, zone_ty local in_zone_units = {} for units_ind = 1, #units do + local lUnit = units[units_ind] + local lCat = lUnit:getCategory() + local unit_pos = lUnit:getPosition().p for zone_units_ind = 1, #zone_units do - local unit_pos = units[units_ind]:getPosition().p + local zone_unit_pos = zone_units[zone_units_ind]:getPosition().p - if unit_pos and zone_unit_pos and units[units_ind]:isActive() == true then + if unit_pos and zone_unit_pos and ((lCat == 1 and lUnit:isActive()) or lCat ~= 1) then if zone_type == 'cylinder' and (((unit_pos.x - zone_unit_pos.x)^2 + (unit_pos.z - zone_unit_pos.z)^2)^0.5 <= radius) then - in_zone_units[#in_zone_units + 1] = units[units_ind] + in_zone_units[#in_zone_units + 1] = lUnit break elseif zone_type == 'sphere' and (((unit_pos.x - zone_unit_pos.x)^2 + (unit_pos.y - zone_unit_pos.y)^2 + (unit_pos.z - zone_unit_pos.z)^2)^0.5 <= radius) then - in_zone_units[#in_zone_units + 1] = units[units_ind] + in_zone_units[#in_zone_units + 1] = lUnit break end end @@ -2721,7 +3324,8 @@ function mist.getUnitsLOS(unitset1, altoffset1, unitset2, altoffset2, radius) -- get the positions all in one step, saves execution time. for unitset1_ind = 1, #unitset1 do local unit1 = Unit.getByName(unitset1[unitset1_ind]) - if unit1 and unit1:isActive() == true then + local lCat = unit1:getCategory() + if unit1 and ((lCat == 1 and unit1:isActive()) or lCat ~= 1) then unit_info1[#unit_info1 + 1] = {} unit_info1[#unit_info1].unit = unit1 unit_info1[#unit_info1].pos = unit1:getPosition().p @@ -2730,7 +3334,8 @@ function mist.getUnitsLOS(unitset1, altoffset1, unitset2, altoffset2, radius) for unitset2_ind = 1, #unitset2 do local unit2 = Unit.getByName(unitset2[unitset2_ind]) - if unit2 and unit2:isActive() == true then + local lCat = unit2:getCategory() + if unit2 and ((lCat == 1 and unit2:isActive()) or lCat ~= 1) then unit_info2[#unit_info2 + 1] = {} unit_info2[#unit_info2].unit = unit2 unit_info2[#unit_info2].pos = unit2:getPosition().p @@ -2766,7 +3371,8 @@ end function mist.getAvgPoint(points) local avgX, avgY, avgZ, totNum = 0, 0, 0, 0 for i = 1, #points do - local nPoint = mist.utils.makeVec3(points[i]) + --log:warn(points[i]) + local nPoint = mist.utils.makeVec3(points[i]) if nPoint.z then avgX = avgX + nPoint.x avgY = avgY + nPoint.y @@ -2859,13 +3465,13 @@ function mist.getBRString(vars) local metric = vars.metric local avgPos = mist.getAvgPos(units) if avgPos then - local vec = {x = avgPos.x - ref.x, y = avgPos.y - ref.y, z = avgPos.z - ref.z} - local dir = mist.utils.getDir(vec, ref) - local dist = mist.utils.get2DDist(avgPos, ref) - if alt then - alt = avgPos.y - end - return mist.tostringBR(dir, dist, alt, metric) + local vec = {x = avgPos.x - ref.x, y = avgPos.y - ref.y, z = avgPos.z - ref.z} + local dir = mist.utils.getDir(vec, ref) + local dist = mist.utils.get2DDist(avgPos, ref) + if alt then + alt = avgPos.y + end + return mist.tostringBR(dir, dist, alt, metric) end end @@ -2891,10 +3497,11 @@ function mist.getLeadingPos(vars) unitPosTbl[#unitPosTbl + 1] = unit:getPosition().p end end + if #unitPosTbl > 0 then -- one more more units found. -- first, find the unit most in the heading direction local maxPos = -math.huge - + heading = heading * -1 -- rotated value appears to be opposite of what was expected local maxPosInd -- maxPos - the furthest in direction defined by heading; maxPosInd = for i = 1, #unitPosTbl do local rotatedVec2 = mist.vec.rotateVec2(mist.utils.makeVec2(unitPosTbl[i]), heading) @@ -2985,8 +3592,147 @@ function mist.getLeadingBRString(vars) end end +--[[getPathLength from GSH +-- Returns the length between the defined set of points. Can also return the point index before the cutoff was achieved +p - table of path points, vec2 or vec3 +cutoff - number distance after which to stop at +topo - boolean for if it should get the topographical distance + +]] + +function mist.getPathLength(p, cutoff, topo) + local l = 0 + local cut = 0 or cutOff + local path = {} + + for i = 1, #p do + if topo then + table.insert(path, mist.utils.makeVec3GL(p[i])) + else + table.insert(path, mist.utils.makeVec3(p[i])) + end + end + + for i = 1, #path do + if i + 1 <= #path then + if topo then + l = mist.utils.get3DDist(path[i], path[i+1]) + l + else + l = mist.utils.get2DDist(path[i], path[i+1]) + l + end + end + if cut ~= 0 and l > cut then + return l, i + end + end + return l end +--[[ +Return a series of points to simplify the input table. Best used in conjunction with findPathOnRoads to turn the massive table into a list of X points. +p - table of path points, can be vec2 or vec3 +num - number of segments. +exact - boolean for whether or not it returns the exact distance or uses the first WP to that distance. + + +]] + +function mist.getPathInSegments(p, num, exact) + local tot = mist.getPathLength(p) + local checkDist = tot/num + local typeUsed = 'vec2' + + local points = {[1] = p[1]} + local curDist = 0 + for i = 1, #p do + if i + 1 <= #p then + curDist = mist.utils.get2DDist(p[i], p[i+1]) + curDist + if curDist > checkDist then + curDist = 0 + if exact then + -- get avg point between the two + -- insert into point table + -- need to be accurate... maybe reassign the point for the value it is checking? + -- insert into p table? + else + table.insert(points, p[i]) + end + end + + end + + end + return points + +end + + +function mist.getPointAtDistanceOnPath(p, dist, r, rtn) + log:info('find distance: $1', dist) + local rType = r or 'roads' + local point = {x= 0, y = 0, z = 0} + local path = {} + local ret = rtn or 'vec2' + local l = 0 + if p[1] and #p == 2 then + path = land.findPathOnRoads(rType, p[1].x, p[1].y, p[2].x, p[2].y) + else + path = p + end + for i = 1, #path do + if i + 1 <= #path then + nextPoint = path[i+1] + if topo then + l = mist.utils.get3DDist(path[i], path[i+1]) + l + else + l = mist.utils.get2DDist(path[i], path[i+1]) + l + end + end + if l > dist then + local diff = dist + if i ~= 1 then -- get difference + diff = l - dist + end + local dir = mist.utils.getHeadingPoints(mist.utils.makeVec3(path[i]), mist.utils.makeVec3(path[i+1])) + local x, y + if r then + x, y = land.getClosestPointOnRoads(rType, mist.utils.round((math.cos(dir) * diff) + path[i].x,1), mist.utils.round((math.sin(dir) * diff) + path[i].y,1)) + else + x, y = mist.utils.round((math.cos(dir) * diff) + path[i].x,1), mist.utils.round((math.sin(dir) * diff) + path[i].y,1) + end + + if ret == 'vec2' then + return {x = x, y = y}, dir + elseif ret == 'vec3' then + return {x = x, y = 0, z = y}, dir + end + + return {x = x, y = y}, dir + end + end + log:warn('Find point at distance: $1, path distance $2', dist, l) + return false +end + + +function mist.projectPoint(point, dist, theta) + local newPoint = {} + if point.z then + newPoint.z = mist.utils.round(math.sin(theta) * dist + point.z, 3) + newPoint.y = mist.utils.deepCopy(point.y) + else + newPoint.y = mist.utils.round(math.sin(theta) * dist + point.y, 3) + end + newPoint.x = mist.utils.round(math.cos(theta) * dist + point.x, 3) + + return newPoint +end + +end + + + + --- Group functions. -- @section groups do -- group functions scope @@ -3037,6 +3783,9 @@ do -- group functions scope newData.units = {} local newUnits = newGroup:getUnits() + if #newUnits == 0 then + log:warn('getCurrentGroupData has returned no units for: $1', gpName) + end for unitNum, unitData in pairs(newGroup:getUnits()) do newData.units[unitNum] = {} local uName = unitData:getName() @@ -3074,7 +3823,7 @@ do -- group functions scope end - function mist.getGroupData(gpName) + function mist.getGroupData(gpName, route) local found = false local newData = {} if mist.DBs.groupsByName[gpName] then @@ -3115,14 +3864,17 @@ do -- group functions scope newData.units[unitNum].unitName = unitData.unitName newData.units[unitNum].heading = unitData.heading -- added to DBs newData.units[unitNum].playerCanDrive = unitData.playerCanDrive -- added to DBs - + newData.units[unitNum].livery_id = unitData.livery_id + newData.units[unitNum].AddPropAircraft = unitData.AddPropAircraft + newData.units[unitNum].AddPropVehicle = unitData.AddPropVehicle + if newData.category == 'plane' or newData.category == 'helicopter' then newData.units[unitNum].payload = payloads[unitNum] - newData.units[unitNum].livery_id = unitData.livery_id + newData.units[unitNum].onboard_num = unitData.onboard_num newData.units[unitNum].callsign = unitData.callsign - newData.units[unitNum].AddPropAircraft = unitData.AddPropAircraft + end if newData.category == 'static' then newData.units[unitNum].categoryStatic = unitData.categoryStatic @@ -3132,6 +3884,10 @@ do -- group functions scope end end --log:info(newData) + if route then + newData.route = mist.getGroupRoute(gpName, true) + end + return newData else log:error('$1 not found in MIST database', gpName) @@ -3156,10 +3912,10 @@ do -- group functions scope if (coa_name == 'red' or coa_name == 'blue') and type(coa_data) == 'table' then if coa_data.country then --there is a country table for cntry_id, cntry_data in pairs(coa_data.country) do - for obj_type_name, obj_type_data in pairs(cntry_data) do - if obj_type_name == "helicopter" or obj_type_name == "ship" or obj_type_name == "plane" or obj_type_name == "vehicle" then -- only these types have points - if ((type(obj_type_data) == 'table') and obj_type_data.group and (type(obj_type_data.group) == 'table') and (#obj_type_data.group > 0)) then --there's a group! - for group_num, group_data in pairs(obj_type_data.group) do + for obj_cat_name, obj_cat_data in pairs(cntry_data) do + if obj_cat_name == "helicopter" or obj_cat_name == "ship" or obj_cat_name == "plane" or obj_cat_name == "vehicle" then -- only these types have points + if ((type(obj_cat_data) == 'table') and obj_cat_data.group and (type(obj_cat_data.group) == 'table') and (#obj_cat_data.group > 0)) then --there's a group! + for group_num, group_data in pairs(obj_cat_data.group) do if group_data and group_data.groupId == gpId then for unitIndex, unitData in pairs(group_data.units) do --group index if unitData.unitId == unitId then @@ -3198,10 +3954,10 @@ do -- group functions scope if type(coa_data) == 'table' then if coa_data.country then --there is a country table for cntry_id, cntry_data in pairs(coa_data.country) do - for obj_type_name, obj_type_data in pairs(cntry_data) do - if obj_type_name == "helicopter" or obj_type_name == "ship" or obj_type_name == "plane" or obj_type_name == "vehicle" then -- only these types have points - if ((type(obj_type_data) == 'table') and obj_type_data.group and (type(obj_type_data.group) == 'table') and (#obj_type_data.group > 0)) then --there's a group! - for group_num, group_data in pairs(obj_type_data.group) do + for obj_cat_name, obj_cat_data in pairs(cntry_data) do + if obj_cat_name == "helicopter" or obj_cat_name == "ship" or obj_cat_name == "plane" or obj_cat_name == "vehicle" then -- only these types have points + if ((type(obj_cat_data) == 'table') and obj_cat_data.group and (type(obj_cat_data.group) == 'table') and (#obj_cat_data.group > 0)) then --there's a group! + for group_num, group_data in pairs(obj_cat_data.group) do if group_data and group_data.groupId == gpId then local payloads = {} for unitIndex, unitData in pairs(group_data.units) do --group index @@ -3223,16 +3979,53 @@ do -- group functions scope end log:warn("Couldn't find payload for group: $1", groupIdent) return - end + function mist.getGroupTable(groupIdent) + local gpId = groupIdent + if type(groupIdent) == 'string' and not tonumber(groupIdent) then + if mist.DBs.MEgroupsByName[groupIdent] then + gpId = mist.DBs.MEgroupsByName[groupIdent].groupId + else + log:error('$1 not found in mist.DBs.MEgroupsByName', groupIdent) + end + end + + if gpId then + for coa_name, coa_data in pairs(env.mission.coalition) do + if type(coa_data) == 'table' then + if coa_data.country then --there is a country table + for cntry_id, cntry_data in pairs(coa_data.country) do + for obj_cat_name, obj_cat_data in pairs(cntry_data) do + if obj_cat_name == "helicopter" or obj_cat_name == "ship" or obj_cat_name == "plane" or obj_cat_name == "vehicle" then -- only these types have points + if ((type(obj_cat_data) == 'table') and obj_cat_data.group and (type(obj_cat_data.group) == 'table') and (#obj_cat_data.group > 0)) then --there's a group! + for group_num, group_data in pairs(obj_cat_data.group) do + if group_data and group_data.groupId == gpId then + return group_data + end + end + end + end + end + end + end + end + end + else + log:error('Need string or number. Got: $1', type(groupIdent)) + return false + end + log:warn("Couldn't find table for group: $1", groupIdent) + + end + function mist.getValidRandomPoint(vars) end function mist.teleportToPoint(vars) -- main teleport function that all of teleport/respawn functions call - --log:info(vars) + --log:warn(vars) local point = vars.point local gpName if vars.gpName then @@ -3243,6 +4036,14 @@ do -- group functions scope log:error('Missing field groupName or gpName in variable table') end + --[[New vars to add, mostly for when called via inZone functions + anyTerrain + offsetWP1 + offsetRoute + initTasks + + ]] + local action = vars.action local disperse = vars.disperse or false @@ -3250,8 +4051,9 @@ do -- group functions scope local radius = vars.radius or 0 local innerRadius = vars.innerRadius - local route = vars.route local dbData = false + + local newGroupData if gpName and not vars.groupData then @@ -3272,18 +4074,33 @@ do -- group functions scope action = 'tele' newGroupData = vars.groupData end + + if vars.newGroupName then + newGroupData.groupName = vars.newGroupName + end + if #newGroupData.units == 0 then + log:warn('$1 has no units in group table', gpName) + return + end + --log:info('get Randomized Point') local diff = {x = 0, y = 0} local newCoord, origCoord local validTerrain = {'LAND', 'ROAD', 'SHALLOW_WATER', 'WATER', 'RUNWAY'} - if string.lower(newGroupData.category) == 'ship' then - validTerrain = {'SHALLOW_WATER' , 'WATER'} - elseif string.lower(newGroupData.category) == 'vehicle' then - validTerrain = {'LAND', 'ROAD'} + if vars.anyTerrain then + -- do nothing + elseif vars.validTerrain then + validTerrain = vars.validTerrain + else + if string.lower(newGroupData.category) == 'ship' then + validTerrain = {'SHALLOW_WATER' , 'WATER'} + elseif string.lower(newGroupData.category) == 'vehicle' then + validTerrain = {'LAND', 'ROAD'} + end end - local offsets = {} + if point and radius >= 0 then local valid = false -- new thoughts @@ -3303,7 +4120,7 @@ do -- group functions scope ---- old for i = 1, 100 do newCoord = mist.getRandPointInCircle(point, radius, innerRadius) - if mist.isTerrainValid(newCoord, validTerrain) then + if vars.anyTerrain or mist.isTerrainValid(newCoord, validTerrain) then origCoord = mist.utils.deepCopy(newCoord) diff = {x = (newCoord.x - newGroupData.units[1].x), y = (newCoord.y - newGroupData.units[1].y)} valid = true @@ -3381,20 +4198,46 @@ do -- group functions scope end - if route then - newGroupData.route = route - end - --log:info(newGroupData) + + local tempRoute + + if mist.DBs.MEgroupsByName[gpName] and not vars.route then + -- log:warn('getRoute') + tempRoute = mist.getGroupRoute(gpName, true) + elseif vars.route then + -- log:warn('routeExist') + tempRoute = mist.utils.deepCopy(vars.route) + end + -- log:warn(tempRoute) + if tempRoute then + if (vars.offsetRoute or vars.offsetWP1 or vars.initTasks) then + for i = 1, #tempRoute do + -- log:warn(i) + if (vars.offsetRoute) or (i == 1 and vars.offsetWP1) or (i == 1 and vars.initTasks) then + -- log:warn('update offset') + tempRoute[i].x = tempRoute[i].x + diff.x + tempRoute[i].y = tempRoute[i].y + diff.y + elseif vars.initTasks and i > 1 then + --log:warn('deleteWP') + tempRoute[i] = nil + end + end + end + newGroupData.route = tempRoute + end + + + --log:warn(newGroupData) --mist.debug.writeData(mist.utils.serialize,{'teleportToPoint', newGroupData}, 'newGroupData.lua') if string.lower(newGroupData.category) == 'static' then - --log:info(newGroupData) + --log:warn(newGroupData) return mist.dynAddStatic(newGroupData) end return mist.dynAdd(newGroupData) end - function mist.respawnInZone(gpName, zone, disperse, maxDisp) + function mist.respawnInZone(gpName, zone, disperse, maxDisp, v) if type(gpName) == 'table' and gpName:getName() then gpName = gpName:getName() @@ -3405,9 +4248,9 @@ do -- group functions scope end if type(zone) == 'string' then - zone = trigger.misc.getZone(zone) - elseif type(zone) == 'table' and not zone.radius then - zone = trigger.misc.getZone(zone[math.random(1, #zone)]) + zone = mist.DBs.zonesByName[zone] + elseif type(zone) == 'table' and not zone.radius then + zone = mist.DBs.zonesByName[zone[math.random(1, #zone)]] end local vars = {} vars.gpName = gpName @@ -3416,10 +4259,17 @@ do -- group functions scope vars.radius = zone.radius vars.disperse = disperse vars.maxDisp = maxDisp + + if v and type(v) == 'table' then + for index, val in pairs(v) do + vars[index] = val + end + end + return mist.teleportToPoint(vars) end - function mist.cloneInZone(gpName, zone, disperse, maxDisp) + function mist.cloneInZone(gpName, zone, disperse, maxDisp, v) --log:info('cloneInZone') if type(gpName) == 'table' then gpName = gpName:getName() @@ -3428,9 +4278,9 @@ do -- group functions scope end if type(zone) == 'string' then - zone = trigger.misc.getZone(zone) - elseif type(zone) == 'table' and not zone.radius then - zone = trigger.misc.getZone(zone[math.random(1, #zone)]) + zone = mist.DBs.zonesByName[zone] + elseif type(zone) == 'table' and not zone.radius then + zone = mist.DBs.zonesByName[zone[math.random(1, #zone)]] end local vars = {} vars.gpName = gpName @@ -3440,10 +4290,15 @@ do -- group functions scope vars.disperse = disperse vars.maxDisp = maxDisp --log:info('do teleport') + if v and type(v) == 'table' then + for index, val in pairs(v) do + vars[index] = val + end + end return mist.teleportToPoint(vars) end - function mist.teleportInZone(gpName, zone, disperse, maxDisp) -- groupName, zoneName or table of Zone Names, keepForm is a boolean + function mist.teleportInZone(gpName, zone, disperse, maxDisp, v) -- groupName, zoneName or table of Zone Names, keepForm is a boolean if type(gpName) == 'table' and gpName:getName() then gpName = gpName:getName() else @@ -3451,9 +4306,9 @@ do -- group functions scope end if type(zone) == 'string' then - zone = trigger.misc.getZone(zone) - elseif type(zone) == 'table' and not zone.radius then - zone = trigger.misc.getZone(zone[math.random(1, #zone)]) + zone = mist.DBs.zonesByName[zone] + elseif type(zone) == 'table' and not zone.radius then + zone = mist.DBs.zonesByName[zone[math.random(1, #zone)]] end local vars = {} @@ -3463,6 +4318,11 @@ do -- group functions scope vars.radius = zone.radius vars.disperse = disperse vars.maxDisp = maxDisp + if v and type(v) == 'table' then + for index, val in pairs(v) do + vars[index] = val + end + end return mist.teleportToPoint(vars) end @@ -3688,14 +4548,20 @@ do -- group functions scope end return choices[rtnVal] end + + function mist.stringCondense(s) + local exclude = {'%-', '%(', '%)', '%_', '%[', '%]', '%.', '%#', '% ', '%{', '%}', '%$', '%%', '%?', '%+', '%^'} + for i , str in pairs(exclude) do + s = string.gsub(s, str, '') + end + return s + end function mist.stringMatch(s1, s2, bool) - local exclude = {'%-', '%(', '%)', '%_', '%[', '%]', '%.', '%#', '% ', '%{', '%}', '%$', '%%', '%?', '%+', '%^'} + if type(s1) == 'string' and type(s2) == 'string' then - for i , str in pairs(exclude) do - s1 = string.gsub(s1, str, '') - s2 = string.gsub(s2, str, '') - end + s1 = mist.stringCondense(s1) + s2 = mist.stringCondense(s2) if not bool then s1 = string.lower(s1) s2 = string.lower(s2) @@ -3823,6 +4689,25 @@ do -- mist.util scope function mist.utils.celsiusToFahrenheit(c) return c*(9/5)+32 end + + function mist.utils.hexToRGB(hex, l) -- because for some reason the draw tools use hex when everything is rgba 0 - 1 + local int = 255 + if l then + int = 1 + end + if hex and type(hex) == 'string' then + local val = {} + hex = string.gsub(hex, '0x', '') + if string.len(hex) == 8 then + val[1] = tonumber("0x"..hex:sub(1,2)) / int + val[2] = tonumber("0x"..hex:sub(3,4)) / int + val[3] = tonumber("0x"..hex:sub(5,6)) / int + val[4] = tonumber("0x"..hex:sub(7,8)) / int + + return val + end + end + end function mist.utils.converter(t1, t2, val) if type(t1) == 'string' then @@ -4024,7 +4909,7 @@ do -- mist.util scope --- Returns the center of a zone as Vec3. -- @tparam string|table zone trigger zone name or table -- @treturn Vec3 center of the zone - function mist.utils.zoneToVec3(zone) + function mist.utils.zoneToVec3(zone, gl) local new = {} if type(zone) == 'table' then if zone.point then @@ -4032,7 +4917,7 @@ do -- mist.util scope new.y = zone.point.y new.z = zone.point.z elseif zone.x and zone.y and zone.z then - return zone + new = mist.utils.deepCopy(zone) end return new elseif type(zone) == 'string' then @@ -4041,11 +4926,21 @@ do -- mist.util scope new.x = zone.point.x new.y = zone.point.y new.z = zone.point.z - return new end end + if new.x and gl then + new.y = land.getHeight({x = new.x, y = new.z}) + end + return new end + function mist.utils.getHeadingPoints(point1, point2, north) -- sick of writing this out. + if north then + return mist.utils.getDir(mist.vec.sub(mist.utils.makeVec3(point2), mist.utils.makeVec3(point1)), (mist.utils.makeVec3(point1))) + else + return mist.utils.getDir(mist.vec.sub(mist.utils.makeVec3(point2), mist.utils.makeVec3(point1))) + end + end --- Returns heading-error corrected direction. -- True-north corrected direction from point along vector vec. -- @tparam Vec3 vec @@ -4067,6 +4962,12 @@ do -- mist.util scope -- @tparam Vec2|Vec3 point2 second point -- @treturn number distance between given points. function mist.utils.get2DDist(point1, point2) + if not point1 then + log:warn("mist.utils.get2DDist 1st input value is nil") + end + if not point2 then + log:warn("mist.utils.get2DDist 2nd input value is nil") + end point1 = mist.utils.makeVec3(point1) point2 = mist.utils.makeVec3(point2) return mist.vec.mag({x = point1.x - point2.x, y = 0, z = point1.z - point2.z}) @@ -4077,6 +4978,12 @@ do -- mist.util scope -- @tparam Vec3 point2 second point -- @treturn number distancen between given points in 3D space. function mist.utils.get3DDist(point1, point2) + if not point1 then + log:warn("mist.utils.get2DDist 1st input value is nil") + end + if not point2 then + log:warn("mist.utils.get2DDist 2nd input value is nil") + end return mist.vec.mag({x = point1.x - point2.x, y = point1.y - point2.y, z = point1.z - point2.z}) end @@ -4507,6 +5414,23 @@ end do -- mist.debug scope mist.debug = {} + function mist.debug.changeSetting(s) + if type(s) == 'table' then + for sName, sVal in pairs(s) do + if type(sVal) == 'string' or type(sVal) == 'number' then + if sName == 'log' then + mistSettings[sName] = sVal + mist.log:setLevel(sVal) + elseif sName == 'dbLog' then + mistSettings[sName] = sVal + dblog:setLevel(sVal) + end + else + mistSettings[sName] = sVal + end + end + end + end --- Dumps the global table _G. -- This dumps the global table _G to a file in -- the DCS\Logs directory. @@ -4514,11 +5438,24 @@ do -- mist.debug scope -- in $DCS_ROOT\Scripts\MissionScripting.lua to access lfs and io -- libraries. -- @param fname - function mist.debug.dump_G(fname) + function mist.debug.dump_G(fname, simp) if lfs and io then local fdir = lfs.writedir() .. [[Logs\]] .. fname local f = io.open(fdir, 'w') - f:write(mist.utils.tableShow(_G)) + if simp then + local g = mist.utils.deepCopy(_G) + g.mist = nil + g.slmod = nil + g.env.mission = nil + g.env.warehouses = nil + g.country.by_idx = nil + g.country.by_country = nil + + f:write(mist.utils.tableShow(g)) + else + + f:write(mist.utils.tableShow(_G)) + end f:close() log:info('Wrote debug data to $1', fdir) --trigger.action.outText(errmsg, 10) @@ -4562,6 +5499,141 @@ do -- mist.debug scope end end end + + -- write group table + function mist.debug.writeGroup(gName, data) + if gName and mist.DBs.groupsByName[gName] then + local dat + if data then + dat = mist.getGroupData(gName) + else + dat = mist.getGroupTable(gName) + end + if dat then + dat.route = {points = mist.getGroupRoute(gName, true)} + end + + if io and lfs and dat then + mist.debug.writeData(mist.utils.serialize, {gName, dat}, gName .. '_table.lua') + else + if dat then + trigger.action.outText('Error: insufficient libraries to run mist.debug.writeGroup, you must disable the sanitization of the io and lfs libraries in ./Scripts/MissionScripting.lua \nGroup table written to DCS.log file instead.', 10) + log:warn('$1 dataTable: $2', gName, dat) + else + trigger.action.outText('Unable to write group table for: ' .. gName .. '\n Error: insufficient libraries to run mist.debug.writeGroup, you must disable the sanitization of the io and lfs libraries in ./Scripts/MissionScripting.lua', 10) + end + end + end + end + + + + -- write all object types in mission. + function mist.debug.writeTypes(fName) + local wt = 'mistDebugWriteTypes.lua' + if fName and type(fName) == 'string' and string.find(fName, '.lua') then + wt = fName + end + local output = {units = {}, countries = {}} + for coa_name_miz, coa_data in pairs(env.mission.coalition) do + if type(coa_data) == 'table' then + if coa_data.country then --there is a country table + for cntry_id, cntry_data in pairs(coa_data.country) do + local countryName = string.lower(cntry_data.name) + if cntry_data.id and country.names[cntry_data.id] then + countryName = string.lower(country.names[cntry_data.id]) + end + output.countries[countryName] = {} + if type(cntry_data) == 'table' then --just making sure + for obj_cat_name, obj_cat_data in pairs(cntry_data) do + if obj_cat_name == "helicopter" or obj_cat_name == "ship" or obj_cat_name == "plane" or obj_cat_name == "vehicle" or obj_cat_name == "static" then --should be an unncessary check + local category = obj_cat_name + if not output.countries[countryName][category] then + -- log:warn('Create: $1', category) + output.countries[countryName][category] = {} + end + if ((type(obj_cat_data) == 'table') and obj_cat_data.group and (type(obj_cat_data.group) == 'table') and (#obj_cat_data.group > 0)) then --there's a group! + for group_num, group_data in pairs(obj_cat_data.group) do + if group_data and group_data.units and type(group_data.units) == 'table' then --making sure again- this is a valid group + for i = 1, #group_data.units do + if group_data.units[i] then + local u = group_data.units[i] + local liv = u.livery_id or 'default' + if not output.units[u.type] then -- create unit table + -- log:warn('Create: $1', u.type) + output.units[u.type] = {count = 0, livery_id = {}} + end + + if not output.countries[countryName][category][u.type] then + -- log:warn('Create country, category, unit: $1', u.type) + output.countries[countryName][category][u.type] = 0 + end + -- add to count + output.countries[countryName][category][u.type] = output.countries[countryName][category][u.type] + 1 + output.units[u.type].count = output.units[u.type].count + 1 + + if liv and not output.units[u.type].livery_id[countryName] then + -- log:warn('Create livery country: $1', countryName) + output.units[u.type].livery_id[countryName] = {} + end + if liv and not output.units[u.type].livery_id[countryName][liv] then + --log:warn('Create Livery: $1', liv) + output.units[u.type].livery_id[countryName][liv] = 0 + end + if liv then + output.units[u.type].livery_id[countryName][liv] = output.units[u.type].livery_id[countryName][liv] + 1 + end + if u.payload and u.payload.pylons then + if not output.units[u.type].CLSID then + output.units[u.type].CLSID = {} + output.units[u.type].pylons = {} + end + + for pyIndex, pData in pairs(u.payload.pylons) do + if not output.units[u.type].CLSID[pData.CLSID] then + output.units[u.type].CLSID[pData.CLSID] = 0 + end + output.units[u.type].CLSID[pData.CLSID] = output.units[u.type].CLSID[pData.CLSID] + 1 + + if not output.units[u.type].pylons[pyIndex] then + output.units[u.type].pylons[pyIndex] = {} + end + if not output.units[u.type].pylons[pyIndex][pData.CLSID] then + output.units[u.type].pylons[pyIndex][pData.CLSID] = 0 + end + output.units[u.type].pylons[pyIndex][pData.CLSID] = output.units[u.type].pylons[pyIndex][pData.CLSID] + 1 + end + + end + end + end + end + end + end + end + end + end + end + end + end + end + if io and lfs then + mist.debug.writeData(mist.utils.serialize, {'mistDebugWriteTypes', output}, wt) + else + trigger.action.outText('Error: insufficient libraries to run mist.debug.writeTypes, you must disable the sanitization of the io and lfs libraries in ./Scripts/MissionScripting.lua \n writeTypes table written to DCS.log file instead.', 10) + log:warn('mist.debug.writeTypes: $1', output) + end + return output + end + function mist.debug.writeWeapons(unit) + + end + + function mist.debug.mark(msg, coord) + + mist.marker.add({point = coord, text = msg}) + log:warn('debug.mark: $1 $2', msg, coord) + end end --- 3D Vector functions @@ -4633,6 +5705,13 @@ do -- mist.vec scope function mist.vec.rotateVec2(vec2, theta) return { x = vec2.x*math.cos(theta) - vec2.y*math.sin(theta), y = vec2.x*math.sin(theta) + vec2.y*math.cos(theta)} end + + function mist.vec.normalize(vec3) + local mag = mist.vec.mag(vec3) + if mag ~= 0 then + return mist.vec.scalar_mult(vec3, 1.0 / mag) + end + end end --- Flag functions. @@ -4826,7 +5905,7 @@ unitTableDef = table or nil if stopflag == -1 or (type(trigger.misc.getUserFlag(stopflag)) == 'number' and trigger.misc.getUserFlag(stopflag) == 0) or (type(trigger.misc.getUserFlag(stopflag)) == 'boolean' and trigger.misc.getUserFlag(stopflag) == 0) then local num_in_zone = 0 for i = 1, #units do - local unit = Unit.getByName(units[i]) + local unit = Unit.getByName(units[i]) or StaticObject.getByName(units[i]) if unit then local pos = unit:getPosition().p if mist.pointInPolygon(pos, zone, maxalt) then @@ -4913,7 +5992,13 @@ unitTableDef = table or nil end end - + --[[ + function mist.flagFunc.weapon_in_zones(vars) + -- borrow from suchoi surprise. While running enabled event handler that checks for weapons in zone. + -- Choice is weapon category or weapon strings. + + end +]] --- Sets a flag if unit(s) is/are inside a moving zone. -- @todo document function mist.flagFunc.units_in_moving_zones(vars) @@ -5282,13 +6367,15 @@ do -- mist.msg scope local caSlots = false local caMSGtoGroup = false + local anyUpdate = false + local lastMessageTime = nil if env.mission.groundControl then -- just to be sure? for index, value in pairs(env.mission.groundControl) do if type(value) == 'table' then for roleName, roleVal in pairs(value) do for rIndex, rVal in pairs(roleVal) do - if env.mission.groundControl[index][roleName][rIndex] > 0 then + if type(rVal) == 'number' and rVal > 0 then caSlots = true break end @@ -5303,11 +6390,124 @@ do -- mist.msg scope end local function mistdisplayV5() - --[[thoughts to improve upon - event handler based activeClients table. - display messages only when there is an update - possibly co-routine it. - ]] + --log:warn("mistdisplayV5: $1", timer.getTime()) + + local clearView = true + if #messageList > 0 then + --log:warn('Updates: $1', anyUpdate) + if anyUpdate == true then + local activeClients = {} + + for clientId, clientData in pairs(mist.DBs.humansById) do + if Unit.getByName(clientData.unitName) and Unit.getByName(clientData.unitName):isExist() == true then + activeClients[clientData.groupId] = clientData.groupName + end + end + anyUpdate = false + if displayActive == false then + displayActive = true + end + --mist.debug.writeData(mist.utils.serialize,{'msg', messageList}, 'messageList.lua') + local msgTableText = {} + local msgTableSound = {} + local curTime = timer.getTime() + for mInd, messageData in pairs(messageList) do + --log:warn(messageData) + if messageData.displayTill < curTime then + messageData:remove() -- now using the remove/destroy function. + else + if messageData.displayedFor then + messageData.displayedFor = curTime - messageData.addedAt + end + local nextSound = 1000 + local soundIndex = 0 + + if messageData.multSound and #messageData.multSound > 0 then + for index, sData in pairs(messageData.multSound) do + if sData.time <= messageData.displayedFor and sData.played == false and sData.time < nextSound then -- find index of the next sound to be played + nextSound = sData.time + soundIndex = index + end + end + if soundIndex ~= 0 then + messageData.multSound[soundIndex].played = true + end + end + + for recIndex, recData in pairs(messageData.msgFor) do -- iterate recipiants + if recData == 'RED' or recData == 'BLUE' or activeClients[recData] then -- rec exists + if messageData.text then -- text + if not msgTableText[recData] then -- create table entry for text + msgTableText[recData] = {} + msgTableText[recData].text = {} + if recData == 'RED' or recData == 'BLUE' then + msgTableText[recData].text[1] = '-------Combined Arms Message-------- \n' + end + msgTableText[recData].text[#msgTableText[recData].text + 1] = messageData.text + msgTableText[recData].displayTime = messageData.displayTime - messageData.displayedFor + else -- add to table entry and adjust display time if needed + if recData == 'RED' or recData == 'BLUE' then + msgTableText[recData].text[#msgTableText[recData].text + 1] = '\n ---------------- Combined Arms Message: \n' + else + msgTableText[recData].text[#msgTableText[recData].text + 1] = '\n ---------------- \n' + end + table.insert(msgTableText[recData].text, messageData.text) + if msgTableText[recData].displayTime < messageData.displayTime - messageData.displayedFor then + msgTableText[recData].displayTime = messageData.displayTime - messageData.displayedFor + else + --msgTableText[recData].displayTime = 10 + end + end + end + if soundIndex ~= 0 then + msgTableSound[recData] = messageData.multSound[soundIndex].file + end + end + + end + messageData.update = nil + + end + + end + ------- new display + + if caSlots == true and caMSGtoGroup == false then + if msgTableText.RED then + trigger.action.outTextForCoalition(coalition.side.RED, table.concat(msgTableText.RED.text), msgTableText.RED.displayTime, clearView) + + end + if msgTableText.BLUE then + trigger.action.outTextForCoalition(coalition.side.BLUE, table.concat(msgTableText.BLUE.text), msgTableText.BLUE.displayTime, clearView) + end + end + + for index, msgData in pairs(msgTableText) do + if type(index) == 'number' then -- its a groupNumber + trigger.action.outTextForGroup(index, table.concat(msgData.text), msgData.displayTime, clearView) + end + end + --- new audio + if msgTableSound.RED then + trigger.action.outSoundForCoalition(coalition.side.RED, msgTableSound.RED) + end + if msgTableSound.BLUE then + trigger.action.outSoundForCoalition(coalition.side.BLUE, msgTableSound.BLUE) + end + + + for index, file in pairs(msgTableSound) do + if type(index) == 'number' then -- its a groupNumber + trigger.action.outSoundForGroup(index, file) + end + end + + end + + else + mist.removeFunction(displayFuncId) + displayActive = false + end end local function mistdisplayV4() @@ -5468,14 +6668,15 @@ end]] ]] - + local new = {} new.text = vars.text -- The actual message new.displayTime = vars.displayTime -- How long will the message appear for new.displayedFor = 0 -- how long the message has been displayed so far + new.displayTill = timer.getTime() + vars.displayTime new.name = vars.name -- ID to overwrite the older message (if it exists) Basically it replaces a message that is displayed with new text. new.addedAt = timer.getTime() - new.update = true + --log:warn('New Message: $1', new.text) if vars.multSound and vars.multSound[1] then new.multSound = vars.multSound @@ -5560,19 +6761,21 @@ end]] if messageList[i].name then if messageList[i].name == vars.name then --log:info('updateMessage') + messageList[i].displayTill = timer.getTime() + messageList[i].displayTime messageList[i].displayedFor = 0 messageList[i].addedAt = timer.getTime() messageList[i].sound = new.sound messageList[i].text = new.text messageList[i].msgFor = new.msgFor messageList[i].multSound = new.multSound - messageList[i].update = true + anyUpdate = true + --log:warn('Message updated: $1', new.messageID) return messageList[i].messageID end end end end - + anyUpdate = true messageID = messageID + 1 new.messageID = messageID @@ -5586,7 +6789,7 @@ end]] if displayActive == false then displayActive = true - displayFuncId = mist.scheduleFunction(mistdisplayV4, {}, timer.getTime() + messageDisplayRate, messageDisplayRate) + displayFuncId = mist.scheduleFunction(mistdisplayV5, {}, timer.getTime() + messageDisplayRate, messageDisplayRate) end return messageID @@ -5597,6 +6800,7 @@ end]] for i, msgData in pairs(messageList) do if messageList[i] == self then table.remove(messageList, i) + anyUpdate = true return true --removal successful end end @@ -5607,6 +6811,7 @@ end]] for i, msgData in pairs(messageList) do if messageList[i].messageID == id then table.remove(messageList, i) + anyUpdate = true return true --removal successful end end @@ -6007,6 +7212,9 @@ do -- mist.demos scope end end + + + do --[[ stuff for marker panels marker.add() add marker. Point of these functions is to simplify process and to store all mark panels added. @@ -6026,98 +7234,382 @@ do If mark added to a group before a client joins slot is synced. Mark made for cliet A in Slot A. Client A leaves, Client B joins in slot A. What do they see? + May need to automate process... + + + Could release this. But things I might need to add/change before doing so. + - removing marks and re-adding in same sequence doesn't appear to work. May need to schedule adding mark if updating an entry. + - I really dont like the old message style code for which groups get the message. Perhaps change to unitsTable and create function for getting humanUnitsTable. + = Event Handler, and check it, for marks added via script or user to deconflict Ids. + - Full validation of passed values for a specific shape type. ]] - --[[ - local typeBase = { - ['Mi-8MT'] = {'Mi-8MTV2', 'Mi-8MTV', 'Mi-8'}, - ['MiG-21Bis'] = {'Mig-21'}, - ['MiG-15bis'] = {'Mig-15'}, - ['FW-190D9'] = {'FW-190'}, - ['Bf-109K-4'] = {'Bf-109'}, - } - - - local mId = 1337 + + local usedMarks = {} + + local mDefs = { + coa = { + ['red'] = {fillColor = {.8, 0 , 0, .5}, color = {.8, 0 , 0, .5}, lineType = 2, fontSize = 16}, + ['blue'] = {fillColor = {0, 0 , 0.8, .5}, color = {0, 0 , 0.8, .5}, lineType = 2, fontSize = 16}, + ['all'] = {fillColor = {.1, .1 , .1, .5}, color = {.9, .9 , .9, .5}, lineType = 2, fontSize = 16}, + ['neutral'] = {fillColor = {.1, .1 , .1, .5}, color = {.2, .2 , .2, .5}, lineType = 2, fontSize = 16}, + }, + } + + local userDefs = {['red'] = {},['blue'] = {},['all'] = {},['neutral'] = {}} + local mId = 1000 + + local tNames = {'line', 'circle','rect', 'arrow', 'text', 'quad', 'freeform'} + local tLines = {[0] = 'no line', [1] = 'solid', [2] = 'dashed',[3] = 'dotted', [4] = 'dot dash' ,[5] = 'long dash', [6] = 'two dash'} + local coas = {[-1] = 'all', [0] = 'neutral', [1] = 'red', [2] = 'blue'} + + local altNames = {['poly'] = 7, ['lines'] = 1, ['polygon'] = 7 } + + local function draw(s) + --log:warn(s) + if type(s) == 'table' then + local mType = s.markType + if mType == 'panel' then + if markScope == 'coa' then + trigger.action.markToCoalition(s.markId, s.text, s.pos, s.markFor, s.readOnly) + elseif markScope == 'group' then + trigger.action.markToGroup(s.markId, s.text, s.pos, s.markFor, s.readOnly) + else + trigger.action.markToAll(s.markId, s.text, s.pos, s.readOnly) + end + elseif mType == 'line' then + trigger.action.lineToAll(s.coa, s.markId, s.pos[1], s.pos[2], s.color, s.fillColor, s.lineType, s.readOnly, s.message) + elseif mType == 'circle' then + trigger.action.circleToAll(s.coa, s.markId, s.pos[1], s.radius, s.color, s.fillColor, s.lineType, s.readOnly, s.message) + elseif mType == 'rect' then + trigger.action.rectToAll(s.coa, s.markId, s.pos[1], s.pos[2], s.color, s.fillColor, s.lineType, s.readOnly, s.message) + elseif mType == 'arrow' then + trigger.action.arrowToAll(s.coa, s.markId, s.pos[1], s.pos[2], s.color, s.fillColor, s.lineType, s.readOnly, s.message) + elseif mType == 'text' then + trigger.action.textToAll(s.coa, s.markId, s.pos[1], s.color, s.fillColor, s.fontSize, s.readOnly, s.text) + elseif mType == 'quad' then + trigger.action.quadToAll(s.coa, s.markId, s.pos[1], s.pos[2], s.pos[3], s.pos[4], s.color, s.fillColor, s.lineType, s.readOnly, s.message) + end + if s.name and not usedMarks[s.name] then + usedMarks[s.name] = s.markId + end + elseif type(s) == 'string' then + --log:warn(s) + mist.utils.dostring(s) + end + end + mist.marker = {} - mist.marker.list = {} + local function markSpamFilter(recList, spamBlockOn) for id, name in pairs(recList) do if name == spamBlockOn then - log:info('already on recList') + --log:info('already on recList') return recList end end - log:info('add to recList') + --log:info('add to recList') table.insert(recList, spamBlockOn) return recList end local function iterate() - mId = mId + 1 - return mId + while mId < 10000000 do + if usedMarks[mId] then + mId = mId + 1 + else + return mist.utils.deepCopy(mId) + end + end + return mist.utils.deepCopy(mId) end + + local function validateColor(val) + if type(val) == 'table' then + for i = 1, #val do + if type(val[i]) == 'number' and val[i] > 1 then + val[i] = val[i]/255 -- convert RGB values from 0-255 to 0-1 equivilent. + end + end + elseif type(val) == 'string' then + val = mist.utils.hexToRGB(val) + + end + return val + end + + local function checkDefs(vName, coa) + --log:warn('CheckDefs: $1 $2', vName, coa) + local coaName + if type(coa) == 'number' then + if coas[coa] then + coaName = coas[coa] + end + elseif type(coa) == 'string' then + coaName = coa + end + + -- log:warn(coaName) + if userDefs[coaName] and userDefs[coaName][vName] then + return userDefs[coaName][vName] + elseif mDefs.coa[coaName] and mDefs.coa[coaName][vName] then + return mDefs.coa[coaName][vName] + end + + end + + function mist.marker.getNextId() + return iterate() + end + + local handle = {} + function handle:onEvent(e) + if world.event.S_EVENT_MARK_ADDED == e.id and e.idx then + usedMarks[e.idx] = e.idx + if not mist.DBs.markList[e.idx] then + --log:info('create maker DB: $1', e.idx) + mist.DBs.markList[e.idx] = {time = e.time, pos = e.pos, groupId = e.groupId, mType = 'panel', text = e.text, markId = e.idx, coalition = e.coalition} + if e.unit then + mist.DBs.markList[e.idx].unit = e.initiaor:getName() + end + --log:info(mist.marker.list[e.idx]) + end + + elseif world.event.S_EVENT_MARK_CHANGE == e.id and e.idx then + if mist.DBs.markList[e.idx] then + mist.DBs.markList[e.idx].text = e.text + end + elseif world.event.S_EVENT_MARK_REMOVE == e.id and e.idx then + if mist.DBs.markList[e.idx] then + mist.DBs.markList[e.idx] = nil + end + end + + end + + local function getMarkId(id) + if mist.DBs.markList[id] then + return id + else + for mEntry, mData in pairs(mist.DBs.markList) do + if id == mData.name or id == mData.id then + return mData.id + end + end + end + + + end + + + local function removeMark(id) + --log:info("Removing Mark: $1", id + local removed = false + if type(id) == 'table' then + for ind, val in pairs(id) do + local r = getMarkId(val) + if r then + trigger.action.removeMark(r) + mist.DBs.markList[r] = nil + removed = true + end + end + + else + local r = getMarkId(id) + trigger.action.removeMark(r) + mist.DBs.markList[r] = nil + removed = true + end + return removed + end + + world.addEventHandler(handle) + function mist.marker.setDefault(vars) + local anyChange = false + if vars and type(vars) == 'table' then + for l1, l1Data in pairs(vars) do + if type(l1Data) == 'table' then + if not userDefs[l1] then + userDefs[l1] = {} + end + + for l2, l2Data in pairs(l1Data) do + userDefs[l1][l2] = l2Data + anyChange = true + end + else + userDefs[l1] = l1Data + anyChange = true + end + end + + end + return anyChange + end - function mist.marker.add(pos, text, markFor, id) - log:warn('markerFunc') - log:info('Pos: $1, Text: $2, markFor: $3, id: $4', pos, text, markFor, id) - if not id then - - else + function mist.marker.add(vars) + --log:warn('markerFunc') + --log:warn(vars) + local pos = vars.point or vars.points or vars.pos + local text = vars.text or '' + local markFor = vars.markFor + local markForCoa = vars.markForCoa or vars.coa -- optional, can be used if you just want to mark to a specific coa/all + local id = vars.id or vars.markId or vars.markid + local mType = vars.mType or vars.markType or vars.type or 0 + local color = vars.color + local fillColor = vars.fillColor + local lineType = vars.lineType or 2 + local readOnly = vars.readOnly or true + local message = vars.message + local fontSize = vars.fontSize + local name = vars.name + local radius = vars.radius or 500 + + local coa = -1 + local usedId = 0 + + + if id then + if type(id) ~= 'number' then + name = id + usedId = iterate() + end + --log:info('checkIfIdExist: $1', id) + --[[ + Maybe it should treat id or name as the same thing/single value. + + If passed number it will use that as the first Id used and will delete/update any marks associated with that same value. + + + ]] + + local lId = id or name + if mist.DBs.markList[id] then ---------- NEED A BETTER WAY TO ASSOCIATE THE ID VALUE. CUrrnetly deleting from table and checking if that deleted entry exists which it wont. + --log:warn('active mark to be removed: $1', id) + name = mist.DBs.markList[id].name or id + removeMark(id) + elseif usedMarks[id] then + --log:info('exists in usedMarks: $1', id) + removeMark(usedMarks[id]) + elseif name and usedMarks[name] then + --log:info('exists in usedMarks: $1', name) + removeMark(usedMarks[name]) + end + usedId = iterate() + usedMarks[id] = usedId -- redefine the value used end - local markType = 'all' + if name then + usedMarks[name] = usedId + end + + if usedId == 0 then + usedId = iterate() + end + if mType then + if type(mType) == 'string' then + for i = 1, #tNames do + --log:warn(tNames[i]) + if mist.stringMatch(mType, tNames[i]) then + mType = i + break + end + end + elseif type(mType) == 'number' and mType > #tNames then + mType = 0 + end + end + --log:warn(mType) + local markScope = 'all' local markForTable = {} - if pos then - pos = mist.utils.makeVec3(pos) + + if pos then + if pos[1] then + for i = 1, #pos do + pos[i] = mist.utils.makeVec3(pos[i]) + end + + else + pos[1] = mist.utils.makeVec3(pos) + end + end if text and type(text) ~= string then text = tostring(text) - else - text = '' end - - if markFor then + + if markForCoa then + if type(markForCoa) == 'string' then + if tonumber(markForCoa) then + coa = coas[tonumber(markForCoa)] + markScope = 'coa' + else + for ind, cName in pairs(coas) do + if mist.stringMatch(cName, markForCoa) then + coa = ind + markScope = 'coa' + break + end + end + end + elseif type(markForCoa) == 'number' and markForCoa >=-1 and markForCoa <= #coas then + coa = markForCoa + markScore = 'coa' + end + + + + elseif markFor then if type(markFor) == 'number' then -- groupId if mist.DBs.groupsById[markFor] then - markType = 'group' + markScope = 'group' end elseif type(markFor) == 'string' then -- groupName if mist.DBs.groupsByName[markFor] then - markType = 'group' + markScope = 'group' markFor = mist.DBs.groupsByName[markFor].groupId end elseif type(markFor) == 'table' then -- multiple groupName, country, coalition, all - markType = 'table' - log:info(markFor) + markScope = 'table' + --log:warn(markFor) for forIndex, forData in pairs(markFor) do -- need to rethink this part and organization. Gotta be a more logical way to send messages to coa, groups, or all. - log:info(forIndex) - log:info(forData) for list, listData in pairs(forData) do - log:info(listData) + --log:warn(listData) forIndex = string.lower(forIndex) if type(listData) == 'string' then listData = string.lower(listData) end if listData == 'all' then - markType = 'all' + markScope = 'all' break elseif (forIndex == 'coa' or forIndex == 'ca') then -- mark for coa or CA. - for name, index in pairs (coalition.side) do + local matches = 0 + for name, index in pairs (coalition.side) do if listData == string.lower(name) then - markType = 'coalition' + markScope = 'coa' + markFor = index + coa = index + matches = matches + 1 end end - elseif (forIndex == 'countries' and string.lower(clientData.country) == listData) or (forIndex == 'units' and string.lower(clientData.unitName) == listData) then - markForTable = markSpamFilter(markForTable, clientData.groupId) + if matches > 1 then + markScope = 'all' + end + elseif forIndex == 'countries' then + for clienId, clientData in pairs(mist.DBs.humansById) do + if (string.lower(clientData.country) == listData) or (forIndex == 'units' and string.lower(clientData.unitName) == listData) then + markForTable = markSpamFilter(markForTable, clientData.groupId) + end + end elseif forIndex == 'unittypes' then -- mark to group -- iterate play units for clientId, clientData in pairs(mist.DBs.humansById) do for typeId, typeData in pairs(listData) do - log:info(typeData) + --log:warn(typeData) local found = false if list == 'all' or clientData.coalition and type(clientData.coalition) == 'string' and mist.stringMatch(clientData.coalition, list) then if mist.matchString(typeData, clientData.type) then @@ -6160,61 +7652,268 @@ do end end else - markType = 'all' + markScope = 'all' end + if mType == 0 then + local data = {markId = usedId, text = text, pos = pos[1], markScope = markScope, markFor = markFor, markType = 'panel', name = name, time = timer.getTime()} + if markScope ~= 'table' then + -- create marks + + mist.DBs.markList[usedId] = data-- add to the DB + + else + if #markForTable > 0 then + --log:info('iterate') + local list = {} + if id and not name then + name = id + end + for i = 1, #markForTable do + local newId = iterate() + local data = {markId = newId, text = text, pos = pos[i], markFor = markForTable[i], markType = 'panel', name = name, readOnly = readOnly, time = timer.getTime()} + mist.DBs.markList[newId] = data + table.insert(list, data) - + draw(data) + + end + return list + end + end - - - if markType ~= 'table' then - local newId = iterate() - local data = {markId = newId, text = text, pos = pos, markType = markType, markFor = markFor} + draw(data) + + return data + elseif mType > 0 then + local newId = iterate() + local fCal = {} + fCal[#fCal+1] = mType + fCal[#fCal+1] = coa + fCal[#fCal+1] = usedId + + local likeARainCoat = false + if mType == 7 then + local score = 0 + for i = 1, #pos do + if i < #pos then + local val = ((pos[i+1].x - pos[i].x)*(pos[i+1].z + pos[i].z)) + --log:warn("$1 index score is: $2", i, val) + score = score + val + else + score = score + ((pos[1].x - pos[i].x)*(pos[1].z + pos[i].z)) + end + end + --log:warn(score) + if score > 0 then -- it is anti-clockwise. Due to DCS bug make it clockwise. + likeARainCoat = true + --log:warn('flip') + + for i = #pos, 1, -1 do + fCal[#fCal+1] = pos[i] + end + end + end + if likeARainCoat == false then + for i = 1, #pos do + fCal[#fCal+1] = pos[i] + end + end + if radius and mType == 2 then + fCal[#fCal+1] = radius + end + + if not color then + color = checkDefs('color', coa) + else + color = validateColor(color) + end + fCal[#fCal+1] = color + + + if not fillColor then + fillColor = checkDefs('fillColor', coa) + else + fillColor = validateColor(fillColor) + end + fCal[#fCal+1] = fillColor + + if mType == 5 then -- text to all + if not fontSize then + fontSize = checkDefs('fontSize', coa) or 16 + end + fCal[#fCal+1] = fontSize + else + if not lineType then + lineType = checkDefs('lineType', coa) or 2 + end + end + fCal[#fCal+1] = lineType + if not readOnly then + readOnly = true + end + fCal[#fCal+1] = readOnly + if mType == 5 then + fCal[#fCal+1] = text + else + + fCal[#fCal+1] = message + end + local data = {coa = coa, markId = usedId, pos = pos, markFor = markFor, color = color, readOnly = readOnly, message = message, fillColor = fillColor, lineType = lineType, markType = tNames[mType], name = name, radius = radius, text = text, fontSize = fontSize, time = timer.getTime()} + mist.DBs.markList[usedId] = data + + if mType == 7 or mType == 1 then + local s = "trigger.action.markupToAll(" - -- create marks - if markType == 'coa' then - trigger.action.markToCoalition(newId, text, pos, markFor) - elseif markType == 'group' then - trigger.action.markToGroup(newId, text, pos, markFor) - else - trigger.action.markToAll(iterate(), text, pos) - end - table.insert(mist.marker.list, data) -- add to the DB - else - if #markForTable > 0 then - log:info('iterate') - for i = 1, #markForTable do - local newId = iterate() - local data = {markId = newId, text = text, pos = pos, markFor = markFor} - log:info(data) - table.insert(mist.marker.list, data) - trigger.action.markToGroup(newId, text, pos, markForTable[i]) - end - end - end - + for i = 1, #fCal do + --log:warn(fCal[i]) + if type(fCal[i]) == 'table' or type(fCal[i]) == 'boolean' then + s = s .. mist.utils.oneLineSerialize(fCal[i]) + else + s = s .. fCal[i] + end + if i < #fCal then + s = s .. ',' + end + end + + s = s .. ')' + if name then + usedMarks[name] = usedId + end + draw(s) + + else + + draw(data) + + end + return data + end end function mist.marker.remove(id) - for i, data in pairs(mist.marker.list) do - if id == data.markId then - trigger.action.removeMark(id) - end - end + return removeMark(id) end function mist.marker.get(id) - + if mist.DBs.markList[id] then + return mist.DBs.markList[id] + end + local names = {} + for markId, data in pairs(mist.DBs.markList) do + if data.name and data.name == id then + table.insert(names, data) + end + end + if #names >= 1 then + return names + end end - function mist.marker.coords(pos, cType, markFor, id) -- wrapper function to just display coordinates of a specific format at location - - - end - ]] + function mist.marker.drawZone(name, v) + if mist.DBs.zonesByName[name] then + --log:warn(mist.DBs.zonesByName[name]) + local vars = v or {} + local ref = mist.utils.deepCopy(mist.DBs.zonesByName[name]) + + if ref.type == 2 then -- it is a quad, but use freeform cause it isnt as bugged + vars.mType = 6 + vars.point = ref.verticies + else + vars.mType = 2 + vars.radius = ref.radius + vars.point = ref.point + end + + + if not (vars.ignoreColor and vars.ignoreColor == true) and not vars.fillColor then + vars.fillColor = ref.color + end + + --log:warn(vars) + return mist.marker.add(vars) + end + end + + function mist.marker.drawShape(name, v) + if mist.DBs.drawingByName[name] then + + local d = v or {} + local o = mist.utils.deepCopy(mist.DBs.drawingByName[name]) + --mist.marker.add({point = {x = o.mapX, z = o.mapY}, text = name}) + --log:warn(o) + d.points = o.points or {} + if o.primitiveType == "Polygon" then + d.mType = 7 + + if o.polygonMode == "rect" then + d.mType = 6 + elseif o.polygonMode == "circle" then + d.mType = 2 + d.points = {x = o.mapX, y = o.mapY} + d.radius = o.radius + end + elseif o.primitiveType == "TextBox" then + d.mType = 5 + d.points = {x = o.mapX, y = o.mapY} + d.text = o.text or d.text + d.fontSize = d.fontSize or o.fontSize + end + -- NOTE TO SELF. FIGURE OUT WHICH SHAPES NEED TO BE OFFSET. OVAL YES. + + if o.fillColorString and not d.fillColor then + d.fillColor = mist.utils.hexToRGB(o.fillColorString) + end + if o.colorString then + d.color = mist.utils.hexToRGB(o.colorString) + end + + + if o.thickness == 0 then + d.lineType = 0 + elseif o.style == 'solid' then + d.lineType = 1 + elseif o.style == 'dot' then + d.lineType = 2 + elseif o.style == 'dash' then + d.lineType = 3 + else + d.lineType = 1 + end + + + if o.primitiveType == "Line" and #d.points >= 2 then + d.mType = 1 + local rtn = {} + for i = 1, #d.points -1 do + local var = mist.utils.deepCopy(d) + var.points = {} + var.points[1] = d.points[i] + var.points[2] = d.points[i+1] + table.insert(rtn, mist.marker.add(var)) + end + return rtn + else + if d.mType then + --log:warn(d) + return mist.marker.add(d) + end + end + end + + + end + + + --[[ + function mist.marker.circle(v) + + + end +]] end --- Time conversion functions. -- @section mist.time @@ -6430,6 +8129,7 @@ do -- group tasks scope mist.air = {} mist.air.fixedWing = {} mist.air.heli = {} + mist.ship = {} --- Tasks group to follow a route. -- This sets the mission task for the given group. @@ -6453,7 +8153,7 @@ do -- group tasks scope if group then local groupCon = group:getController() if groupCon then - log:warn(misTask) + --log:warn(misTask) groupCon:setTask(misTask) return true end @@ -6475,17 +8175,17 @@ do -- group tasks scope if type(coa_data) == 'table' then if coa_data.country then --there is a country table for cntry_id, cntry_data in pairs(coa_data.country) do - for obj_type_name, obj_type_data in pairs(cntry_data) do - if obj_type_name == "helicopter" or obj_type_name == "ship" or obj_type_name == "plane" or obj_type_name == "vehicle" then -- only these types have points - if ((type(obj_type_data) == 'table') and obj_type_data.group and (type(obj_type_data.group) == 'table') and (#obj_type_data.group > 0)) then --there's a group! - for group_num, group_data in pairs(obj_type_data.group) do + for obj_cat_name, obj_cat_data in pairs(cntry_data) do + if obj_cat_name == "helicopter" or obj_cat_name == "ship" or obj_cat_name == "plane" or obj_cat_name == "vehicle" then -- only these types have points + if ((type(obj_cat_data) == 'table') and obj_cat_data.group and (type(obj_cat_data.group) == 'table') and (#obj_cat_data.group > 0)) then --there's a group! + for group_num, group_data in pairs(obj_cat_data.group) do if group_data and group_data.groupId == gpId then -- this is the group we are looking for if group_data.route and group_data.route.points and #group_data.route.points > 0 then local points = {} for point_num, point in pairs(group_data.route.points) do local routeData = {} - if env.mission.version > 7 then + if env.mission.version > 7 and env.mission.version < 19 then routeData.name = env.getValueDictByKey(point.name) else routeData.name = point.name @@ -6515,10 +8215,10 @@ do -- group tasks scope log:error('Group route not defined in mission editor for groupId: $1', gpId) return end --if group_data and group_data.name and group_data.name == 'groupname' - end --for group_num, group_data in pairs(obj_type_data.group) do - end --if ((type(obj_type_data) == 'table') and obj_type_data.group and (type(obj_type_data.group) == 'table') and (#obj_type_data.group > 0)) then - end --if obj_type_name == "helicopter" or obj_type_name == "ship" or obj_type_name == "plane" or obj_type_name == "vehicle" or obj_type_name == "static" then - end --for obj_type_name, obj_type_data in pairs(cntry_data) do + end --for group_num, group_data in pairs(obj_cat_data.group) do + end --if ((type(obj_cat_data) == 'table') and obj_cat_data.group and (type(obj_cat_data.group) == 'table') and (#obj_cat_data.group > 0)) then + end --if obj_cat_name == "helicopter" or obj_cat_name == "ship" or obj_cat_name == "plane" or obj_cat_name == "vehicle" or obj_cat_name == "static" then + end --for obj_cat_name, obj_cat_data in pairs(cntry_data) do end --for cntry_id, cntry_data in pairs(coa_data.country) do end --if coa_data.country then --there is a country table end --if coa_name == 'red' or coa_name == 'blue' and type(coa_data) == 'table' then @@ -6822,14 +8522,19 @@ do -- group tasks scope end -- need to return a Vec3 or Vec2? - function mist.getRandPointInCircle(p, radius, innerRadius, maxA, minA) + function mist.getRandPointInCircle(p, r, innerRadius, maxA, minA) local point = mist.utils.makeVec3(p) local theta = 2*math.pi*math.random() + local radius = r or 1000 local minR = innerRadius or 0 if maxA and not minA then theta = math.rad(math.random(0, maxA - math.random())) - elseif maxA and minA and minA < maxA then - theta = math.rad(math.random(minA, maxA) - math.random()) + elseif maxA and minA then + if minA < maxA then + theta = math.rad(math.random(minA, maxA) - math.random()) + else + theta = math.rad(math.random(maxA, minA) - math.random()) + end end local rad = math.random() + math.random() if rad > 1 then @@ -6854,14 +8559,21 @@ do -- group tasks scope end function mist.getRandomPointInZone(zoneName, innerRadius, maxA, minA) - if type(zoneName) == 'string' and type(trigger.misc.getZone(zoneName)) == 'table' then - return mist.getRandPointInCircle(trigger.misc.getZone(zoneName).point, trigger.misc.getZone(zoneName).radius, innerRadius, maxA, minA) - end + if type(zoneName) == 'string' then + local zone = mist.DBs.zonesByName[zoneName] + if zone.type and zone.type == 2 then + return mist.getRandomPointInPoly(zone.verticies) + else + return mist.getRandPointInCircle(zone.point, zone.radius, innerRadius, maxA, minA) + end + end return false end function mist.getRandomPointInPoly(zone) - local avg = mist.getAvgPoint(zone) + --env.info('Zone Size: '.. #zone) + local avg = mist.getAvgPoint(zone) + --log:warn(avg) local radius = 0 local minR = math.huge local newCoord = {} @@ -6873,6 +8585,8 @@ do -- group tasks scope minR = mist.utils.get2DDist(avg, zone[i]) end end + --log:warn('Radius: $1', radius) + --log:warn('minR: $1', minR) local lSpawnPos = {} for j = 1, 100 do newCoord = mist.getRandPointInCircle(avg, radius) @@ -6886,6 +8600,19 @@ do -- group tasks scope end return newCoord end + + function mist.getWindBearingAndVel(p) + local point = mist.utils.makeVec3(o) + local gLevel = land.getHeight({x = point.x, y = point.z}) + if point.y <= gLevel then + point.y = gLevel + 10 + end + local t = atmosphere.getWind(point) + local bearing = math.tan(t.z/t.x) + local vel = math.sqrt(t.x^2 + t.z^2) + return bearing, vel + + end function mist.groupToRandomPoint(vars) local group = vars.group --Required @@ -6941,25 +8668,25 @@ do -- group tasks scope return end - function mist.groupRandomDistSelf(gpData, dist, form, heading, speed) + function mist.groupRandomDistSelf(gpData, dist, form, heading, speed, disableRoads) local pos = mist.getLeadPos(gpData) local fakeZone = {} fakeZone.radius = dist or math.random(300, 1000) fakeZone.point = {x = pos.x, y = pos.y, z = pos.z} - mist.groupToRandomZone(gpData, fakeZone, form, heading, speed) + mist.groupToRandomZone(gpData, fakeZone, form, heading, speed, disableRoads) return end - function mist.groupToRandomZone(gpData, zone, form, heading, speed) + function mist.groupToRandomZone(gpData, zone, form, heading, speed, disableRoads) if type(gpData) == 'string' then gpData = Group.getByName(gpData) end if type(zone) == 'string' then - zone = trigger.misc.getZone(zone) + zone = mist.DBs.zonesByName[zone] elseif type(zone) == 'table' and not zone.radius then - zone = trigger.misc.getZone(zone[math.random(1, #zone)]) + zone = mist.DBs.zonesByName[zone[math.random(1, #zone)]] end if speed then @@ -6973,7 +8700,7 @@ do -- group tasks scope vars.headingDegrees = heading vars.speed = speed vars.point = mist.utils.zoneToVec3(zone) - + vars.disableRoads = disableRoads mist.groupToRandomPoint(vars) return @@ -7042,7 +8769,7 @@ do -- group tasks scope function mist.groupToPoint(gpData, point, form, heading, speed, useRoads) if type(point) == 'string' then - point = trigger.misc.getZone(point) + point = mist.DBs.zonesByName[point] end if speed then speed = mist.utils.kmphToMps(speed) @@ -7081,6 +8808,16 @@ do -- group tasks scope return leader:getPosition().p end end + + function mist.groupIsDead(groupName) -- copy more or less from on station + if Group.getByName(groupName) then + local gp = Group.getByName(groupName) + if #gp:getUnits() > 0 or gp:isExist() == true then + return false + end + end + return true + end end @@ -7200,7 +8937,7 @@ do -- mist.Logger scope -- @usage -- log everything --myLogger:setLevel(3) function mist.Logger:setLevel(level) - if not level then + if not level then self.level = 2 else if type(level) == 'string' then diff --git a/src/core/src/scriptloader.cpp b/src/core/src/scriptloader.cpp index 6cffd5c2..850baba6 100644 --- a/src/core/src/scriptloader.cpp +++ b/src/core/src/scriptloader.cpp @@ -47,7 +47,7 @@ void registerLuaFunctions(lua_State* L) return; } - executeLuaScript(L, modLocation + "\\Scripts\\mist_4_4_90.lua"); + executeLuaScript(L, modLocation + "\\Scripts\\mist.lua"); executeLuaScript(L, modLocation + "\\Scripts\\OlympusCommand.lua"); executeLuaScript(L, modLocation + "\\Scripts\\unitPayloads.lua"); }