mirror of
https://github.com/weyne85/DML.git
synced 2025-10-29 16:57:49 +00:00
Version 1.2.5
CSAR over open water taxiPolice fireFX with alt
This commit is contained in:
parent
4d6f132003
commit
1939f376c5
Binary file not shown.
Binary file not shown.
@ -1,14 +1,18 @@
|
|||||||
autoCSAR = {}
|
autoCSAR = {}
|
||||||
autoCSAR.version = "1.0.0"
|
autoCSAR.version = "1.1.0"
|
||||||
autoCSAR.requiredLibs = {
|
autoCSAR.requiredLibs = {
|
||||||
"dcsCommon", -- always
|
"dcsCommon", -- always
|
||||||
"cfxZones", -- Zones, of course
|
"cfxZones", -- Zones, of course
|
||||||
}
|
}
|
||||||
autoCSAR.killDelay = 2 * 60
|
autoCSAR.killDelay = 2 * 60
|
||||||
autoCSAR.counter = 31 -- any number is good, to kick-off counting
|
autoCSAR.counter = 31 -- any number is good, to kick-off counting
|
||||||
|
autoCSAR.trackedEjects = {} -- we start tracking on eject
|
||||||
|
|
||||||
--[[--
|
--[[--
|
||||||
VERSION HISTORY
|
VERSION HISTORY
|
||||||
1.0.0 - Initial Version
|
1.0.0 - Initial Version
|
||||||
|
1.1.0 - allow open water CSAR, fake pilot with GRG Soldier
|
||||||
|
- can be disabled by seaCSAR = false
|
||||||
--]]--
|
--]]--
|
||||||
|
|
||||||
function autoCSAR.removeGuy(args)
|
function autoCSAR.removeGuy(args)
|
||||||
@ -18,6 +22,13 @@ function autoCSAR.removeGuy(args)
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
function autoCSAR.isOverWater(theUnit)
|
||||||
|
local pPoint = theUnit:getPoint()
|
||||||
|
pPoint.y = pPoint.z -- make it getSurfaceType compatible
|
||||||
|
local surf = land.getSurfaceType(pPoint)
|
||||||
|
return surf == 2 or surf == 3
|
||||||
|
end
|
||||||
|
|
||||||
function autoCSAR.createNewCSAR(theUnit)
|
function autoCSAR.createNewCSAR(theUnit)
|
||||||
if not csarManager then
|
if not csarManager then
|
||||||
trigger.action.outText("+++aCSAR: CSAR Manager not loaded, aborting", 30)
|
trigger.action.outText("+++aCSAR: CSAR Manager not loaded, aborting", 30)
|
||||||
@ -40,25 +51,120 @@ function autoCSAR.createNewCSAR(theUnit)
|
|||||||
-- for later expansion
|
-- for later expansion
|
||||||
local theGroup = theUnit:getGroup()
|
local theGroup = theUnit:getGroup()
|
||||||
if theGroup then
|
if theGroup then
|
||||||
trigger.action.outText("We have a group for <" .. theUnit:getName() .. ">", 30)
|
-- now happens for faked sea CSAR units
|
||||||
|
--trigger.action.outText("We have a group for <" .. theUnit:getName() .. ">", 30)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
-- now, if theUnit is over open water, this will be killed instantly
|
||||||
|
-- and must therefore be replaced with a stand-in
|
||||||
|
local pPoint = theUnit:getPoint()
|
||||||
|
pPoint.y = pPoint.z -- make it getSurfaceType compatible
|
||||||
|
local surf = land.getSurfaceType(pPoint)
|
||||||
|
local splashdown = false
|
||||||
|
|
||||||
|
if surf == 2 or surf == 3 then
|
||||||
|
trigger.action.outTextForCoalition(coa, "Parachute splashdown over open water reported!", 30)
|
||||||
|
splashdown = true
|
||||||
|
-- create a replacement unit since pilot will be killed
|
||||||
|
local theBoyGroup = dcsCommon.createSingleUnitGroup(
|
||||||
|
"Xray-" .. autoCSAR.counter,
|
||||||
|
"Soldier M4 GRG", -- "Soldier M4 GRG",
|
||||||
|
pPoint.x,
|
||||||
|
pPoint.z,
|
||||||
|
0)
|
||||||
|
local theSideCJTF = dcsCommon.coalition2county(coa) -- get the correct county CJTF
|
||||||
|
local theGroup = coalition.addGroup(theSideCJTF, Group.Category.GROUND, theBoyGroup)
|
||||||
|
-- now access replacement unit
|
||||||
|
local allUnits = theGroup:getUnits()
|
||||||
|
theUnit = allUnits[1] -- get first (and only) unit
|
||||||
|
end
|
||||||
-- create a CSAR mission now
|
-- create a CSAR mission now
|
||||||
csarManager.createCSARForParachutist(theUnit, "Xray-" .. autoCSAR.counter)
|
csarManager.createCSARForParachutist(theUnit, "Xray-" .. autoCSAR.counter)
|
||||||
autoCSAR.counter = autoCSAR.counter + 1
|
autoCSAR.counter = autoCSAR.counter + 1
|
||||||
|
|
||||||
-- schedule removal of pilot
|
-- schedule removal of pilot
|
||||||
local args = {}
|
local args = {}
|
||||||
args.theGuy = theUnit
|
args.theGuy = theUnit
|
||||||
timer.scheduleFunction(autoCSAR.removeGuy, args, timer.getTime() + autoCSAR.killDelay)
|
if splashdown then
|
||||||
|
timer.scheduleFunction(autoCSAR.removeGuy, args, timer.getTime() + 1) -- in one second
|
||||||
|
else
|
||||||
|
timer.scheduleFunction(autoCSAR.removeGuy, args, timer.getTime() + autoCSAR.killDelay)
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
function autoCSAR:onEvent(event)
|
function autoCSAR:onEvent(event)
|
||||||
if event.id == 31 then -- landing_after_eject
|
-- trigger.action.outText("autoCSAR: event = " .. event.id, 30)
|
||||||
|
if event.id == 31 then -- landing_after_eject, does not happen at sea
|
||||||
|
-- to prevent double invocations for same process
|
||||||
|
-- check that we are still tracking this ejection
|
||||||
if event.initiator then
|
if event.initiator then
|
||||||
autoCSAR.createNewCSAR(event.initiator)
|
local uid = tonumber(event.initiator:getID())
|
||||||
|
if autoCSAR.trackedEjects[uid] then
|
||||||
|
trigger.action.outText("aCSAR: filtered double sea csar (player) event for uid = <" .. uid .. ">", 30)
|
||||||
|
autoCSAR.trackedEjects[uid] = nil -- reset
|
||||||
|
return
|
||||||
|
end
|
||||||
|
autoCSAR.createNewCSAR(event.initiator)
|
||||||
|
-- autoCSAR.trackedEjects[event.initiator] = nil
|
||||||
|
-- trigger.action.outText("autocsar: LAE for " .. autoCSAR.trackedEjects[event.initiator], 30)
|
||||||
|
-- else
|
||||||
|
-- trigger.action.outText("autoCSAR: ignored LAE event", 30)
|
||||||
|
-- end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
if event.id == 6 and autoCSAR.seaCSAR then -- eject, start tracking
|
||||||
|
if event.initiator then
|
||||||
|
-- see if this happened over open water and immediately
|
||||||
|
-- create a seaCSAR
|
||||||
|
|
||||||
|
--local uid = tonumber(event.initiator:getID())
|
||||||
|
-- trigger.action.outText("autoCSAR: started tracking - chair + pilot", 30)
|
||||||
|
-- autoCSAR.trackedEjects[event.initiator] = "chair+pilot" -- start with this
|
||||||
|
if autoCSAR.isOverWater(event.initiator) then
|
||||||
|
--trigger.action.outText("attempting to walk on water", 30)
|
||||||
|
autoCSAR.createNewCSAR(event.initiator)
|
||||||
|
end
|
||||||
|
|
||||||
|
-- also mark this one as completed
|
||||||
|
local uid = tonumber(event.initiator:getID())
|
||||||
|
autoCSAR.trackedEjects[uid] = "processed"
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
--[[--
|
||||||
|
if event.id == 33 then -- separate chair from pilot
|
||||||
|
if event.initiator then
|
||||||
|
--local uid = tonumber(event.initiator:getID())
|
||||||
|
--local pid = tonumber(event.target:getID())
|
||||||
|
--if uid == 0 then
|
||||||
|
--trigger.action.outText("uid = 0, abort tracking", 30)
|
||||||
|
--return
|
||||||
|
--end
|
||||||
|
trigger.action.outText("autoCSAR: track change from seat to pilot <" .. event.target:getName() .. ">", 30)
|
||||||
|
autoCSAR.trackedEjects[event.initiator] = "chair only"
|
||||||
|
autoCSAR.trackedEjects[event.target] = "pilot"
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
if event.id == 9 then -- pilot dead
|
||||||
|
if event.initiator then
|
||||||
|
--local uid = tonumber(event.initiator:getID())
|
||||||
|
trigger.action.outText("autoCSAR: pilot id=xxx dead", 30)
|
||||||
|
if autoCSAR.trackedEjects[event.initiator] then
|
||||||
|
trigger.action.outText("confirm tracked pilot dead after ejection", 30)
|
||||||
|
if autoCSAR.isOverWater(event.initiator) then
|
||||||
|
trigger.action.outText("attempt to walk on water", 30)
|
||||||
|
autoCSAR.createNewCSAR(event.initiator)
|
||||||
|
end
|
||||||
|
autoCSAR.trackedEjects[event.initiator] = nil
|
||||||
|
end
|
||||||
|
else
|
||||||
|
trigger.action.outText("autoCSAR - no initiator for zed", 30)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
--]]--
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|
||||||
function autoCSAR.readConfigZone()
|
function autoCSAR.readConfigZone()
|
||||||
@ -80,6 +186,8 @@ function autoCSAR.readConfigZone()
|
|||||||
autoCSAR.blueCSAR = cfxZones.getBoolFromZoneProperty(theZone, "blueCSAR", true)
|
autoCSAR.blueCSAR = cfxZones.getBoolFromZoneProperty(theZone, "blueCSAR", true)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
autoCSAR.seaCSAR = cfxZones.getBoolFromZoneProperty(theZone, "seaCSAR", true)
|
||||||
|
|
||||||
if autoCSAR.verbose then
|
if autoCSAR.verbose then
|
||||||
trigger.action.outText("+++aCSAR: read config", 30)
|
trigger.action.outText("+++aCSAR: read config", 30)
|
||||||
end
|
end
|
||||||
|
|||||||
@ -1,5 +1,5 @@
|
|||||||
cfxReconMode = {}
|
cfxReconMode = {}
|
||||||
cfxReconMode.version = "2.1.3"
|
cfxReconMode.version = "2.1.4"
|
||||||
cfxReconMode.verbose = false -- set to true for debug info
|
cfxReconMode.verbose = false -- set to true for debug info
|
||||||
cfxReconMode.reconSound = "UI_SCI-FI_Tone_Bright_Dry_20_stereo.wav" -- to be played when somethiong discovered
|
cfxReconMode.reconSound = "UI_SCI-FI_Tone_Bright_Dry_20_stereo.wav" -- to be played when somethiong discovered
|
||||||
|
|
||||||
@ -84,6 +84,8 @@ VERSION HISTORY
|
|||||||
- <ele> wildcard in message format
|
- <ele> wildcard in message format
|
||||||
- fix for mgrs bug in message (zone coords, not unit)
|
- fix for mgrs bug in message (zone coords, not unit)
|
||||||
2.1.3 - added cfxReconMode.name to allow direct acces with test zone flag
|
2.1.3 - added cfxReconMode.name to allow direct acces with test zone flag
|
||||||
|
2.1.4 - canDetect() also checks if unit has been activated
|
||||||
|
canDetect has strenghtened isExist() guard
|
||||||
|
|
||||||
cfxReconMode is a script that allows units to perform reconnaissance
|
cfxReconMode is a script that allows units to perform reconnaissance
|
||||||
missions and, after detecting units, marks them on the map with
|
missions and, after detecting units, marks them on the map with
|
||||||
@ -319,7 +321,7 @@ function cfxReconMode.canDetect(scoutPos, theGroup, visRange)
|
|||||||
-- returns true and pos when detected
|
-- returns true and pos when detected
|
||||||
local allUnits = theGroup:getUnits()
|
local allUnits = theGroup:getUnits()
|
||||||
for idx, aUnit in pairs(allUnits) do
|
for idx, aUnit in pairs(allUnits) do
|
||||||
if aUnit:isExist() and aUnit:getLife() >= 1 then
|
if Unit.isExist(aUnit) and aUnit:isActive() and aUnit:getLife() >= 1 then
|
||||||
local uPos = aUnit:getPoint()
|
local uPos = aUnit:getPoint()
|
||||||
uPos.y = uPos.y + 3 -- raise my 3 meters
|
uPos.y = uPos.y + 3 -- raise my 3 meters
|
||||||
local d = dcsCommon.distFlat(scoutPos, uPos)
|
local d = dcsCommon.distFlat(scoutPos, uPos)
|
||||||
|
|||||||
@ -1,5 +1,5 @@
|
|||||||
cfxSmokeZone = {}
|
cfxSmokeZone = {}
|
||||||
cfxSmokeZone.version = "1.1.1"
|
cfxSmokeZone.version = "1.1.2"
|
||||||
cfxSmokeZone.requiredLibs = {
|
cfxSmokeZone.requiredLibs = {
|
||||||
"dcsCommon", -- always
|
"dcsCommon", -- always
|
||||||
"cfxZones", -- Zones, of course
|
"cfxZones", -- Zones, of course
|
||||||
@ -17,6 +17,7 @@ cfxSmokeZone.requiredLibs = {
|
|||||||
- random color support
|
- random color support
|
||||||
1.1.0 - Watchflag upgrade
|
1.1.0 - Watchflag upgrade
|
||||||
1.1.1 - stopSmoke? input
|
1.1.1 - stopSmoke? input
|
||||||
|
1.1.2 - 'agl', 'alt' synonymous for altitude to keep in line with fireFX
|
||||||
|
|
||||||
--]]--
|
--]]--
|
||||||
cfxSmokeZone.smokeZones = {}
|
cfxSmokeZone.smokeZones = {}
|
||||||
@ -36,6 +37,11 @@ function cfxSmokeZone.processSmokeZone(aZone)
|
|||||||
|
|
||||||
aZone.smokeColor = theColor
|
aZone.smokeColor = theColor
|
||||||
aZone.smokeAlt = cfxZones.getNumberFromZoneProperty(aZone, "altitude", 1)
|
aZone.smokeAlt = cfxZones.getNumberFromZoneProperty(aZone, "altitude", 1)
|
||||||
|
if cfxZones.hasProperty(aZone, "alt") then
|
||||||
|
aZone.smokeAlt = cfxZones.getNumberFromZoneProperty(aZone, "alt", 1)
|
||||||
|
elseif cfxZones.hasProperty(aZone, "agl") then
|
||||||
|
aZone.smokeAlt = cfxZones.getNumberFromZoneProperty(aZone, "agl", 1)
|
||||||
|
end
|
||||||
|
|
||||||
-- paused
|
-- paused
|
||||||
aZone.paused = cfxZones.getBoolFromZoneProperty(aZone, "paused", false)
|
aZone.paused = cfxZones.getBoolFromZoneProperty(aZone, "paused", false)
|
||||||
|
|||||||
@ -1,5 +1,5 @@
|
|||||||
cfxZones = {}
|
cfxZones = {}
|
||||||
cfxZones.version = "3.0.3"
|
cfxZones.version = "3.0.6"
|
||||||
|
|
||||||
-- cf/x zone management module
|
-- cf/x zone management module
|
||||||
-- reads dcs zones and makes them accessible and mutable
|
-- reads dcs zones and makes them accessible and mutable
|
||||||
@ -122,7 +122,8 @@ cfxZones.version = "3.0.3"
|
|||||||
- 3.0.3 - new getLinkedUnit()
|
- 3.0.3 - new getLinkedUnit()
|
||||||
- 3.0.4 - new createRandomPointOnZoneBoundary()
|
- 3.0.4 - new createRandomPointOnZoneBoundary()
|
||||||
- 3.0.5 - getPositiveRangeFromZoneProperty() now also supports upper bound (optional)
|
- 3.0.5 - getPositiveRangeFromZoneProperty() now also supports upper bound (optional)
|
||||||
|
- 3.0.6 - new createSimplePolyZone()
|
||||||
|
- new createSimpleQuadZone()
|
||||||
|
|
||||||
--]]--
|
--]]--
|
||||||
cfxZones.verbose = false
|
cfxZones.verbose = false
|
||||||
@ -366,7 +367,12 @@ function cfxZones.copyPoint(inPoint)
|
|||||||
local newPoint = {}
|
local newPoint = {}
|
||||||
newPoint.x = inPoint.x
|
newPoint.x = inPoint.x
|
||||||
newPoint.y = inPoint.y
|
newPoint.y = inPoint.y
|
||||||
newPoint.z = inPoint.z
|
-- handle xz only
|
||||||
|
if inPoint.z then
|
||||||
|
newPoint.z = inPoint.z
|
||||||
|
else
|
||||||
|
newPoint.z = inPoint.y
|
||||||
|
end
|
||||||
return newPoint
|
return newPoint
|
||||||
end
|
end
|
||||||
|
|
||||||
@ -536,6 +542,54 @@ function cfxZones.createCircleZone(name, x, z, radius)
|
|||||||
return newZone
|
return newZone
|
||||||
end
|
end
|
||||||
|
|
||||||
|
function cfxZones.createSimplePolyZone(name, location, points, addToManaged)
|
||||||
|
if not addToManaged then addToManaged = false end
|
||||||
|
if not location then
|
||||||
|
location = {}
|
||||||
|
end
|
||||||
|
if not location.x then location.x = 0 end
|
||||||
|
if not location.z then location.z = 0 end
|
||||||
|
|
||||||
|
local newZone = cfxZones.createPolyZone(name, points)
|
||||||
|
|
||||||
|
if addToManaged then
|
||||||
|
cfxZones.addZoneToManagedZones(newZone)
|
||||||
|
end
|
||||||
|
return newZone
|
||||||
|
end
|
||||||
|
|
||||||
|
function cfxZones.createSimpleQuadZone(name, location, points, addToManaged)
|
||||||
|
if not location then
|
||||||
|
location = {}
|
||||||
|
end
|
||||||
|
if not location.x then location.x = 0 end
|
||||||
|
if not location.z then location.z = 0 end
|
||||||
|
|
||||||
|
-- synthesize 4 points if they don't exist
|
||||||
|
-- remember: in DCS positive x is up, positive z is right
|
||||||
|
if not points then
|
||||||
|
points = {}
|
||||||
|
end
|
||||||
|
if not points[1] then
|
||||||
|
-- upper left
|
||||||
|
points[1] = {x = location.x-1, y = 0, z = location.z-1}
|
||||||
|
end
|
||||||
|
if not points[2] then
|
||||||
|
-- upper right
|
||||||
|
points[2] = {x = location.x-1, y = 0, z = location.z+1}
|
||||||
|
end
|
||||||
|
if not points[3] then
|
||||||
|
-- lower right
|
||||||
|
points[3] = {x = location.x+1, y = 0, z = location.z+1}
|
||||||
|
end
|
||||||
|
if not points[4] then
|
||||||
|
-- lower left
|
||||||
|
points[4] = {x = location.x+1, y = 0, z = location.z-1}
|
||||||
|
end
|
||||||
|
|
||||||
|
return cfxZones.createSimplePolyZone(name, location, points, addToManaged)
|
||||||
|
end
|
||||||
|
|
||||||
function cfxZones.createPolyZone(name, poly) -- poly must be array of point type
|
function cfxZones.createPolyZone(name, poly) -- poly must be array of point type
|
||||||
local newZone = {}
|
local newZone = {}
|
||||||
newZone.isCircle = false
|
newZone.isCircle = false
|
||||||
|
|||||||
@ -1,5 +1,5 @@
|
|||||||
csarManager = {}
|
csarManager = {}
|
||||||
csarManager.version = "2.2.0"
|
csarManager.version = "2.2.3"
|
||||||
csarManager.verbose = false
|
csarManager.verbose = false
|
||||||
csarManager.ups = 1
|
csarManager.ups = 1
|
||||||
|
|
||||||
@ -50,9 +50,19 @@ csarManager.ups = 1
|
|||||||
- score global and per-mission
|
- score global and per-mission
|
||||||
- isCSARTarget API
|
- isCSARTarget API
|
||||||
- 2.2.1 - added troopCarriers attribute to config
|
- 2.2.1 - added troopCarriers attribute to config
|
||||||
- passes own troop carriers to dcsCommin.isTroopCarrier()
|
- passes own troop carriers to dcsCommon.isTroopCarrier()
|
||||||
|
- 2.2.2 - enable CSAR missions in water
|
||||||
|
- csar name defaults to zone name
|
||||||
|
- better randomization of pilot's point in csar mission,
|
||||||
|
supports quad zone
|
||||||
|
- 2.2.3 - better support for red/blue
|
||||||
|
- allow neutral pick-up
|
||||||
|
- directions to closest safe zone
|
||||||
|
- CSARBASE attribute now carries coalition
|
||||||
|
- deprecated coalition attribute
|
||||||
|
|
||||||
|
INTEGRATES AUTOMATICALLY WITH playerScore IF INSTALLED
|
||||||
|
|
||||||
|
|
||||||
--]]--
|
--]]--
|
||||||
-- modules that need to be loaded BEFORE I run
|
-- modules that need to be loaded BEFORE I run
|
||||||
csarManager.requiredLibs = {
|
csarManager.requiredLibs = {
|
||||||
@ -61,15 +71,14 @@ csarManager.requiredLibs = {
|
|||||||
"cfxPlayer", -- player monitoring and group monitoring
|
"cfxPlayer", -- player monitoring and group monitoring
|
||||||
"nameStats", -- generic data module for weight
|
"nameStats", -- generic data module for weight
|
||||||
"cargoSuper",
|
"cargoSuper",
|
||||||
-- "cfxCommander", -- needed if you want to hand-create CSAR missions
|
-- "cfxCommander", -- needed only if you want to hand-create CSAR missions
|
||||||
}
|
}
|
||||||
-- integrates automatically with playerScore if installed
|
|
||||||
|
|
||||||
|
|
||||||
--
|
--
|
||||||
-- OPTIONS
|
-- OPTIONS
|
||||||
--
|
--
|
||||||
csarManager.useSmoke = true -- smoke is a performance killer, so you can turn it off
|
csarManager.useSmoke = true
|
||||||
csarManager.smokeColor = 4 -- when using smoke
|
csarManager.smokeColor = 4 -- when using smoke
|
||||||
|
|
||||||
|
|
||||||
@ -82,7 +91,7 @@ csarManager.myEvents = {3, 4, 5} -- 3 = take off, 4 = land, 5 = crash
|
|||||||
-- CASR MISSION
|
-- CASR MISSION
|
||||||
--
|
--
|
||||||
csarManager.openMissions = {} -- all currently available missions
|
csarManager.openMissions = {} -- all currently available missions
|
||||||
csarManager.csarBases = {} -- all bases where we can drop off rescued pilots
|
csarManager.csarBases = {} -- all bases where we can drop off rescued pilots. NOT a zone!
|
||||||
csarManager.csarZones = {} -- zones for spawning
|
csarManager.csarZones = {} -- zones for spawning
|
||||||
|
|
||||||
csarManager.missionID = 1 -- to create uuid
|
csarManager.missionID = 1 -- to create uuid
|
||||||
@ -113,6 +122,10 @@ function csarManager.createDownedPilot(theMission, existingUnit)
|
|||||||
local aLocation = {}
|
local aLocation = {}
|
||||||
local aHeading = 0 -- in rads
|
local aHeading = 0 -- in rads
|
||||||
local newTargetZone = theMission.zone
|
local newTargetZone = theMission.zone
|
||||||
|
-- if mission.radius is < 1 we do not randomize location
|
||||||
|
-- else location is somewhere in the middle of zone
|
||||||
|
-- csar mission zones randomize by themselves, and pass
|
||||||
|
-- a radius of <1, this is only for ejecting pilots
|
||||||
if newTargetZone.radius > 1 then
|
if newTargetZone.radius > 1 then
|
||||||
aLocation, aHeading = dcsCommon.randomPointOnPerimeter(newTargetZone.radius / 2 + 3, newTargetZone.point.x, newTargetZone.point.z)
|
aLocation, aHeading = dcsCommon.randomPointOnPerimeter(newTargetZone.radius / 2 + 3, newTargetZone.point.x, newTargetZone.point.z)
|
||||||
else
|
else
|
||||||
@ -546,8 +559,6 @@ function csarManager.heloDeparted(theUnit)
|
|||||||
local theGroup = theUnit:getGroup()
|
local theGroup = theUnit:getGroup()
|
||||||
conf.id = theGroup:getID()
|
conf.id = theGroup:getID()
|
||||||
conf.currentState = 1 -- in the air
|
conf.currentState = 1 -- in the air
|
||||||
|
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|
||||||
--
|
--
|
||||||
@ -555,7 +566,6 @@ end
|
|||||||
-- Helo Crashed
|
-- Helo Crashed
|
||||||
--
|
--
|
||||||
--
|
--
|
||||||
|
|
||||||
function csarManager.heloCrashed(theUnit)
|
function csarManager.heloCrashed(theUnit)
|
||||||
if not dcsCommon.isTroopCarrier(theUnit, csarManager.troopCarriers) then return end
|
if not dcsCommon.isTroopCarrier(theUnit, csarManager.troopCarriers) then return end
|
||||||
-- problem: this isn't called on network games.
|
-- problem: this isn't called on network games.
|
||||||
@ -698,6 +708,16 @@ function csarManager.setCommsMenu(theUnit)
|
|||||||
{conf, "unload one"}
|
{conf, "unload one"}
|
||||||
)
|
)
|
||||||
table.insert(conf.myCommands, theCommand)
|
table.insert(conf.myCommands, theCommand)
|
||||||
|
|
||||||
|
commandTxt = "Direction to nearest safe zone"
|
||||||
|
theCommand = missionCommands.addCommandForGroup(
|
||||||
|
conf.id,
|
||||||
|
commandTxt,
|
||||||
|
conf.myMainMenu,
|
||||||
|
csarManager.redirectDirections,
|
||||||
|
{conf, "redirect"}
|
||||||
|
)
|
||||||
|
table.insert(conf.myCommands, theCommand)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
@ -705,19 +725,32 @@ function csarManager.redirectListCSARRequests(args)
|
|||||||
timer.scheduleFunction(csarManager.doListCSARRequests, args, timer.getTime() + 0.1)
|
timer.scheduleFunction(csarManager.doListCSARRequests, args, timer.getTime() + 0.1)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
function csarManager.openMissionsForSide(theSide)
|
||||||
|
local theMissions = {}
|
||||||
|
for idx, aMission in pairs(csarManager.openMissions) do
|
||||||
|
if aMission.side == theSide or aMission.side == 0 then
|
||||||
|
table.insert(theMissions, aMission)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
return theMissions
|
||||||
|
end
|
||||||
|
|
||||||
function csarManager.doListCSARRequests(args)
|
function csarManager.doListCSARRequests(args)
|
||||||
local conf = args[1]
|
local conf = args[1]
|
||||||
local param = args[2]
|
local param = args[2]
|
||||||
local theUnit = conf.unit
|
local theUnit = conf.unit
|
||||||
local point = theUnit:getPoint()
|
local point = theUnit:getPoint()
|
||||||
|
local theSide = theUnit:getCoalition()
|
||||||
|
|
||||||
--trigger.action.outText("+++csar: ".. theUnit:getName() .." issued csar status request", 30)
|
--trigger.action.outText("+++csar: ".. theUnit:getName() .." issued csar status request", 30)
|
||||||
local report = "\nCrews requesting evacuation\n"
|
local report = "\nCrews requesting evacuation\n"
|
||||||
if #csarManager.openMissions < 1 then
|
local openMissions = csarManager.openMissionsForSide(theSide)
|
||||||
|
|
||||||
|
if #openMissions < 1 then
|
||||||
report = report .. "\nNo requests, all crew are safe."
|
report = report .. "\nNo requests, all crew are safe."
|
||||||
else
|
else
|
||||||
-- iterate through all troops onboard to get their status
|
-- iterate through all troops onboard to get their status
|
||||||
for idx, mission in pairs(csarManager.openMissions) do
|
for idx, mission in pairs(openMissions) do
|
||||||
local d = dcsCommon.distFlat(point, mission.zone.point) * 0.000539957
|
local d = dcsCommon.distFlat(point, mission.zone.point) * 0.000539957
|
||||||
d = math.floor(d * 10) / 10
|
d = math.floor(d * 10) / 10
|
||||||
local b = dcsCommon.bearingInDegreesFromAtoB(point, mission.zone.point)
|
local b = dcsCommon.bearingInDegreesFromAtoB(point, mission.zone.point)
|
||||||
@ -730,9 +763,9 @@ function csarManager.doListCSARRequests(args)
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
local myBases = csarManager.getCSARBasesForSide(theSide)
|
||||||
if #csarManager.csarBases < 1 then
|
if #myBases < 1 then
|
||||||
report = report .. "\n\nWARNING: NO CSAR BASES TO DELIVER EVACUEES"
|
report = report .. "\n\nWARNING: NO CSAR BASES TO DELIVER EVACUEES TO"
|
||||||
end
|
end
|
||||||
|
|
||||||
report = report .. "\n"
|
report = report .. "\n"
|
||||||
@ -818,6 +851,59 @@ function csarManager.unloadOne(args)
|
|||||||
end
|
end
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|
||||||
|
function csarManager.redirectDirections(args)
|
||||||
|
timer.scheduleFunction(csarManager.directions, args, timer.getTime() + 0.1)
|
||||||
|
end
|
||||||
|
|
||||||
|
function csarManager.directions(args)
|
||||||
|
local conf = args[1]
|
||||||
|
local param = args[2]
|
||||||
|
local theUnit = conf.unit
|
||||||
|
local myName = theUnit:getName()
|
||||||
|
local theSide = theUnit:getCoalition()
|
||||||
|
local report = "Nothing to report."
|
||||||
|
|
||||||
|
-- get all safe zones
|
||||||
|
local myBases = csarManager.getCSARBasesForSide(theSide)
|
||||||
|
if #myBases < 1 then
|
||||||
|
report = "\n\nWARNING: NO CSAR BASES TO DELIVER EVACUEES TO"
|
||||||
|
else
|
||||||
|
-- find nearest zone
|
||||||
|
local p = theUnit:getPoint()
|
||||||
|
p.y = 0
|
||||||
|
local dMin = math.huge
|
||||||
|
local theBase = nil
|
||||||
|
local dP = nil
|
||||||
|
for idx, aBase in pairs(myBases) do
|
||||||
|
local z = aBase.zone
|
||||||
|
local zp = cfxZones.getPoint(z)
|
||||||
|
zp.y = 0
|
||||||
|
local d = dcsCommon.dist(p, zp)
|
||||||
|
if d < dMin then
|
||||||
|
theBase = aBase
|
||||||
|
dP = zp
|
||||||
|
dMin = d
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
-- see if we are inside
|
||||||
|
if cfxZones.isPointInsideZone(p, theBase.zone) then
|
||||||
|
report = "\nYou are inside safe zone " .. theBase.name .. "."
|
||||||
|
else
|
||||||
|
-- get bearing and distance
|
||||||
|
dMin = dMin / 1000 * 0.539957 -- in nm
|
||||||
|
|
||||||
|
dMin = math.floor(dMin * 10) / 10
|
||||||
|
local bearing = dcsCommon.bearingInDegreesFromAtoB(p, dP)
|
||||||
|
report = "\nClosest safe zone for " .. myName .. " is " .. theBase.name ..", bearing " .. bearing .. " at " .. dMin .. "nm"
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
report = report .. "\n"
|
||||||
|
trigger.action.outTextForGroup(conf.id, report, 30)
|
||||||
|
trigger.action.outSoundForGroup(conf.id, csarManager.actionSound)
|
||||||
|
end
|
||||||
--
|
--
|
||||||
-- Player event callbacks
|
-- Player event callbacks
|
||||||
--
|
--
|
||||||
@ -870,14 +956,23 @@ end
|
|||||||
function csarManager.addCSARBase(aZone)
|
function csarManager.addCSARBase(aZone)
|
||||||
local csarBase = {}
|
local csarBase = {}
|
||||||
csarBase.zone = aZone
|
csarBase.zone = aZone
|
||||||
|
|
||||||
|
--[[--
|
||||||
local bName = cfxZones.getStringFromZoneProperty(aZone, "CSARBASE", "XXX")
|
local bName = cfxZones.getStringFromZoneProperty(aZone, "CSARBASE", "XXX")
|
||||||
if bName == "XXX" then bName = aZone.name end
|
if bName == "XXX" then bName = aZone.name end
|
||||||
csarBase.name = cfxZones.getStringFromZoneProperty(aZone, "name", bName)
|
csarBase.name = cfxZones.getStringFromZoneProperty(aZone, "name", bName)
|
||||||
|
--]]--
|
||||||
|
-- CSARBASE now carries the coalition in the CSARBASE attribute
|
||||||
|
csarBase.side = cfxZones.getCoalitionFromZoneProperty(aZone, "CSARBASE", 0)
|
||||||
|
-- backward-compatibility to older versions.
|
||||||
|
-- will be deprecated
|
||||||
|
if cfxZones.hasProperty(aZone, "coalition") then
|
||||||
|
csarBase.side = cfxZones.getCoalitionFromZoneProperty(aZone, "CSARBASE", 0)
|
||||||
|
end
|
||||||
|
|
||||||
-- read further properties like facilities that may
|
-- see if we have provided a name field, default zone name
|
||||||
-- need to be matched
|
csarBase.name = cfxZones.getStringFromZoneProperty(aZone, "name", aZone.name)
|
||||||
csarBase.side = cfxZones.getCoalitionFromZoneProperty(aZone, "coalition", 0)
|
|
||||||
|
|
||||||
table.insert(csarManager.csarBases, csarBase)
|
table.insert(csarManager.csarBases, csarBase)
|
||||||
|
|
||||||
if csarManager.verbose or aZone.verbose then
|
if csarManager.verbose or aZone.verbose then
|
||||||
@ -894,6 +989,15 @@ function csarManager.getCSARBaseforZone(aZone)
|
|||||||
return nil
|
return nil
|
||||||
end
|
end
|
||||||
|
|
||||||
|
function csarManager.getCSARBasesForSide(theSide)
|
||||||
|
local bases = {}
|
||||||
|
for idx, aBase in pairs(csarManager.csarBases) do
|
||||||
|
if aBase.side == 0 or aBase.side == theSide then
|
||||||
|
table.insert(bases, aBase)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
return bases
|
||||||
|
end
|
||||||
--
|
--
|
||||||
--
|
--
|
||||||
-- U P D A T E
|
-- U P D A T E
|
||||||
@ -947,7 +1051,8 @@ function csarManager.update() -- every second
|
|||||||
for idx, csarMission in pairs (csarManager.openMissions) do
|
for idx, csarMission in pairs (csarManager.openMissions) do
|
||||||
-- check if we are inside trigger range on the same side
|
-- check if we are inside trigger range on the same side
|
||||||
local d = dcsCommon.distFlat(uPoint, csarMission.zone.point)
|
local d = dcsCommon.distFlat(uPoint, csarMission.zone.point)
|
||||||
if (uSide == csarMission.side) and (d < csarManager.rescueTriggerRange) then
|
if ((uSide == csarMission.side) or (csarMission.side == 0) )
|
||||||
|
and (d < csarManager.rescueTriggerRange) then
|
||||||
-- we are in trigger distance. if we did not notify before
|
-- we are in trigger distance. if we did not notify before
|
||||||
-- do it now
|
-- do it now
|
||||||
if not dcsCommon.arrayContainsString(csarMission.messagedUnits, uName) then
|
if not dcsCommon.arrayContainsString(csarMission.messagedUnits, uName) then
|
||||||
@ -1064,15 +1169,18 @@ function csarManager.update() -- every second
|
|||||||
-- this should always be true, but you never know
|
-- this should always be true, but you never know
|
||||||
local currVal = cfxZones.getFlagValue(theZone.startCSAR, theZone)
|
local currVal = cfxZones.getFlagValue(theZone.startCSAR, theZone)
|
||||||
if currVal ~= theZone.lastCSARVal then
|
if currVal ~= theZone.lastCSARVal then
|
||||||
|
-- set up random point in zone
|
||||||
|
local mPoint = cfxZones.createRandomPointInZone(theZone)
|
||||||
local theMission = csarManager.createCSARMissionData(
|
local theMission = csarManager.createCSARMissionData(
|
||||||
cfxZones.getPoint(theZone),
|
mPoint, --cfxZones.getPoint(theZone), -- point
|
||||||
theZone.csarSide,
|
theZone.csarSide, -- theSide
|
||||||
theZone.csarFreq,
|
theZone.csarFreq, -- freq
|
||||||
theZone.csarName,
|
theZone.csarName, -- name
|
||||||
theZone.numCrew,
|
theZone.numCrew, -- numCrew
|
||||||
theZone.timeLimit,
|
theZone.timeLimit, -- timeLimit
|
||||||
theZone.csarMapMarker,
|
theZone.csarMapMarker, -- mapMarker
|
||||||
theZone.radius)
|
0.1, --theZone.radius) -- radius
|
||||||
|
nil) -- parashoo unit
|
||||||
csarManager.addMission(theMission)
|
csarManager.addMission(theMission)
|
||||||
theZone.lastCSARVal = currVal
|
theZone.lastCSARVal = currVal
|
||||||
if csarManager.verbose then
|
if csarManager.verbose then
|
||||||
@ -1097,9 +1205,12 @@ function csarManager.createCSARforUnit(theUnit, pilotName, radius, silent, score
|
|||||||
local csarPoint = dcsCommon.randomPointInCircle(radius, radius/2, point.x, point.z)
|
local csarPoint = dcsCommon.randomPointInCircle(radius, radius/2, point.x, point.z)
|
||||||
|
|
||||||
-- check the ground- water will kill the pilot
|
-- check the ground- water will kill the pilot
|
||||||
|
-- not any more! pilot can float
|
||||||
|
|
||||||
csarPoint.y = csarPoint.z
|
csarPoint.y = csarPoint.z
|
||||||
local surf = land.getSurfaceType(csarPoint)
|
local surf = land.getSurfaceType(csarPoint)
|
||||||
|
|
||||||
|
--[[--
|
||||||
if surf == 2 or surf == 3 then
|
if surf == 2 or surf == 3 then
|
||||||
if not silent then
|
if not silent then
|
||||||
trigger.action.outTextForCoalition(coal, "Bad chute! Bad chute! ".. pilotName .. " did not survive ejection out of their " .. theUnit:getTypeName(), 30)
|
trigger.action.outTextForCoalition(coal, "Bad chute! Bad chute! ".. pilotName .. " did not survive ejection out of their " .. theUnit:getTypeName(), 30)
|
||||||
@ -1107,18 +1218,19 @@ function csarManager.createCSARforUnit(theUnit, pilotName, radius, silent, score
|
|||||||
end
|
end
|
||||||
return
|
return
|
||||||
end
|
end
|
||||||
|
--]]--
|
||||||
|
|
||||||
csarPoint.y = land.getHeight(csarPoint)
|
csarPoint.y = land.getHeight(csarPoint)
|
||||||
|
|
||||||
-- when we get here, the terrain is ok, so let's drop the pilot
|
-- when we get here, the terrain is ok, so let's drop the pilot
|
||||||
local theMission = csarManager.createCSARMissionData(
|
local theMission = csarManager.createCSARMissionData(
|
||||||
csarPoint,
|
csarPoint, -- point
|
||||||
coal,
|
coal, -- side
|
||||||
nil,
|
nil, -- freq
|
||||||
pilotName,
|
pilotName, -- name
|
||||||
1,
|
1, -- num crew
|
||||||
nil,
|
nil, -- time limit
|
||||||
nil)
|
nil) -- map mark, inRadius, parashooUnit
|
||||||
theMission.score = score
|
theMission.score = score
|
||||||
csarManager.addMission(theMission)
|
csarManager.addMission(theMission)
|
||||||
if not silent then
|
if not silent then
|
||||||
@ -1130,13 +1242,13 @@ end
|
|||||||
function csarManager.createCSARForParachutist(theUnit, name) -- invoked with parachute guy on ground as theUnit
|
function csarManager.createCSARForParachutist(theUnit, name) -- invoked with parachute guy on ground as theUnit
|
||||||
local coa = theUnit:getCoalition()
|
local coa = theUnit:getCoalition()
|
||||||
local pos = theUnit:getPoint()
|
local pos = theUnit:getPoint()
|
||||||
-- unit DOES NOT HAVE GROUP!!!
|
-- unit DOES NOT HAVE GROUP!!! (unless water splashdown)
|
||||||
-- create a CSAR mission now
|
-- create a CSAR mission now
|
||||||
local theMission = csarManager.createCSARMissionData(pos, coa, nil, name, nil, nil, nil, 0.1, nil)
|
local theMission = csarManager.createCSARMissionData(pos, coa, nil, name, nil, nil, nil, 0.1, nil)
|
||||||
csarManager.addMission(theMission)
|
csarManager.addMission(theMission)
|
||||||
-- if not silent then
|
-- if not silent then
|
||||||
trigger.action.outTextForCoalition(coa, "MAYDAY MAYDAY MAYDAY! ".. name .. " requesting extraction after eject!", 30)
|
trigger.action.outTextForCoalition(coa, "MAYDAY MAYDAY MAYDAY! ".. name .. " requesting extraction after eject!", 30)
|
||||||
trigger.action.outSoundForGroup(coa, csarManager.actionSound) -- "Quest Snare 3.wav")
|
trigger.action.outSoundForGroup(coa, csarManager.actionSound) -- "Quest Snare 3.wav")
|
||||||
-- end
|
-- end
|
||||||
end
|
end
|
||||||
|
|
||||||
@ -1164,6 +1276,7 @@ function csarManager.readCSARZone(theZone)
|
|||||||
local theSide = cfxZones.getCoalitionFromZoneProperty(theZone, "coalition", 0)
|
local theSide = cfxZones.getCoalitionFromZoneProperty(theZone, "coalition", 0)
|
||||||
theZone.csarSide = theSide
|
theZone.csarSide = theSide
|
||||||
theZone.csarName = cfxZones.getStringFromZoneProperty(theZone, "name", "<none>")
|
theZone.csarName = cfxZones.getStringFromZoneProperty(theZone, "name", "<none>")
|
||||||
|
theZone.csarName = theZone.name -- default CSAR Name
|
||||||
if cfxZones.hasProperty(theZone, "csarName") then
|
if cfxZones.hasProperty(theZone, "csarName") then
|
||||||
theZone.csarName = cfxZones.getStringFromZoneProperty(theZone, "csarName", "<none>")
|
theZone.csarName = cfxZones.getStringFromZoneProperty(theZone, "csarName", "<none>")
|
||||||
end
|
end
|
||||||
@ -1199,15 +1312,17 @@ function csarManager.readCSARZone(theZone)
|
|||||||
end
|
end
|
||||||
|
|
||||||
if (not deferred) then
|
if (not deferred) then
|
||||||
|
local mPoint = cfxZones.createRandomPointInZone(theZone)
|
||||||
local theMission = csarManager.createCSARMissionData(
|
local theMission = csarManager.createCSARMissionData(
|
||||||
theZone.point,
|
mPoint,
|
||||||
theZone.csarSide,
|
theZone.csarSide,
|
||||||
theZone.csarFreq,
|
theZone.csarFreq,
|
||||||
theZone.csarName,
|
theZone.csarName,
|
||||||
theZone.numCrew,
|
theZone.numCrew,
|
||||||
theZone.timeLimit,
|
theZone.timeLimit,
|
||||||
theZone.csarMapMarker,
|
theZone.csarMapMarker,
|
||||||
theZone.radius)
|
0.1, -- theZone.radius,
|
||||||
|
nil) -- parashoo unit
|
||||||
csarManager.addMission(theMission)
|
csarManager.addMission(theMission)
|
||||||
end
|
end
|
||||||
|
|
||||||
@ -1385,4 +1500,8 @@ end
|
|||||||
- when unloading one by menu, update weight!!!
|
- when unloading one by menu, update weight!!!
|
||||||
|
|
||||||
-- allow neutral pick-up
|
-- allow neutral pick-up
|
||||||
|
|
||||||
|
-- allow any airfied to be csarsafe by default, no longer requires csarbase
|
||||||
|
-- get vector to closes csarbase
|
||||||
|
|
||||||
--]]--
|
--]]--
|
||||||
@ -1,5 +1,5 @@
|
|||||||
dcsCommon = {}
|
dcsCommon = {}
|
||||||
dcsCommon.version = "2.8.3"
|
dcsCommon.version = "2.8.4"
|
||||||
--[[-- VERSION HISTORY
|
--[[-- VERSION HISTORY
|
||||||
2.2.6 - compassPositionOfARelativeToB
|
2.2.6 - compassPositionOfARelativeToB
|
||||||
- clockPositionOfARelativeToB
|
- clockPositionOfARelativeToB
|
||||||
@ -140,12 +140,17 @@ dcsCommon.version = "2.8.3"
|
|||||||
- isTroopCarrierType uses wildArrayContainsString
|
- isTroopCarrierType uses wildArrayContainsString
|
||||||
2.8.3 - small optimizations in bearingFromAtoB()
|
2.8.3 - small optimizations in bearingFromAtoB()
|
||||||
- new whichSideOfMine()
|
- new whichSideOfMine()
|
||||||
|
2.8.4 - new rotatePointAroundOriginRad()
|
||||||
|
- new rotatePointAroundPointDeg()
|
||||||
|
- new rotatePointAroundPointRad()
|
||||||
|
- getClosestAirbaseTo() now supports passing list of air bases
|
||||||
|
|
||||||
|
|
||||||
--]]--
|
--]]--
|
||||||
|
|
||||||
-- dcsCommon is a library of common lua functions
|
-- dcsCommon is a library of common lua functions
|
||||||
-- for easy access and simple mission programming
|
-- for easy access and simple mission programming
|
||||||
-- (c) 2021, 2022 by Chritian Franz and cf/x AG
|
-- (c) 2021 - 2023 by Chritian Franz and cf/x AG
|
||||||
|
|
||||||
dcsCommon.verbose = false -- set to true to see debug messages. Lots of them
|
dcsCommon.verbose = false -- set to true to see debug messages. Lots of them
|
||||||
dcsCommon.uuidStr = "uuid-"
|
dcsCommon.uuidStr = "uuid-"
|
||||||
@ -506,9 +511,12 @@ dcsCommon.version = "2.8.3"
|
|||||||
return nil
|
return nil
|
||||||
end
|
end
|
||||||
|
|
||||||
function dcsCommon.getClosestAirbaseTo(thePoint, filterCat, filterCoalition)
|
function dcsCommon.getClosestAirbaseTo(thePoint, filterCat, filterCoalition, allYourBase)
|
||||||
local delta = math.huge
|
local delta = math.huge
|
||||||
local allYourBase = dcsCommon.getAirbasesWhoseNameContains("*", filterCat, filterCoalition) -- get em all and filter
|
if not allYourBase then
|
||||||
|
allYourBase = dcsCommon.getAirbasesWhoseNameContains("*", filterCat, filterCoalition) -- get em all and filter
|
||||||
|
end
|
||||||
|
|
||||||
local closestBase = nil
|
local closestBase = nil
|
||||||
for idx, aBase in pairs(allYourBase) do
|
for idx, aBase in pairs(allYourBase) do
|
||||||
-- iterate them all
|
-- iterate them all
|
||||||
@ -1857,9 +1865,7 @@ dcsCommon.version = "2.8.3"
|
|||||||
|
|
||||||
end;
|
end;
|
||||||
|
|
||||||
function dcsCommon.rotatePointAroundOrigin(inX, inY, angle) -- angle in degrees
|
function dcsCommon.rotatePointAroundOriginRad(inX, inY, angle) -- angle in degrees
|
||||||
local rads = 3.14152 / 180 -- convert to radiants.
|
|
||||||
angle = angle * rads -- turns into rads
|
|
||||||
local c = math.cos(angle)
|
local c = math.cos(angle)
|
||||||
local s = math.sin(angle)
|
local s = math.sin(angle)
|
||||||
local px
|
local px
|
||||||
@ -1868,6 +1874,38 @@ dcsCommon.version = "2.8.3"
|
|||||||
py = inX * s + inY * c
|
py = inX * s + inY * c
|
||||||
return px, py
|
return px, py
|
||||||
end
|
end
|
||||||
|
|
||||||
|
function dcsCommon.rotatePointAroundOrigin(inX, inY, angle) -- angle in degrees
|
||||||
|
local rads = 3.14152 / 180 -- convert to radiants.
|
||||||
|
angle = angle * rads -- turns into rads
|
||||||
|
local px, py = dcsCommon.rotatePointAroundOriginRad(inX, inY, angle)
|
||||||
|
return px, py
|
||||||
|
end
|
||||||
|
|
||||||
|
function dcsCommon.rotatePointAroundPointRad(x, y, px, py, angle)
|
||||||
|
x = x - px
|
||||||
|
y = y - py
|
||||||
|
x, y = dcsCommon.rotatePointAroundOriginRad(x, y, angle)
|
||||||
|
x = x + px
|
||||||
|
y = y + py
|
||||||
|
return x, y
|
||||||
|
end
|
||||||
|
|
||||||
|
function dcsCommon.rotatePointAroundPointDeg(x, y, px, py, degrees)
|
||||||
|
x, y = dcsCommon.rotatePointAroundPointRad(x, y, px, py, degrees * 3.14152 / 180)
|
||||||
|
return x, y
|
||||||
|
end
|
||||||
|
|
||||||
|
-- rotates a Vec3-base inPoly on XZ pane around inPoint on XZ pane
|
||||||
|
function dcsCommon.rotatePoly3AroundVec3Rad(inPoly, inPoint, rads)
|
||||||
|
local outPoly = {}
|
||||||
|
for idx, aVertex in pairs(inPoly) do
|
||||||
|
local x, z = dcsCommon.rotatePointAroundPointRad(aVertex.x, aVertex.z, inPoint.x, inPoint.z, rads)
|
||||||
|
local v3 = {x = x, y = aVertex.y, z = z}
|
||||||
|
outPoly[idx] = v3
|
||||||
|
end
|
||||||
|
return outPoly
|
||||||
|
end
|
||||||
|
|
||||||
function dcsCommon.rotateUnitData(theUnit, degrees, cx, cz)
|
function dcsCommon.rotateUnitData(theUnit, degrees, cx, cz)
|
||||||
if not cx then cx = 0 end
|
if not cx then cx = 0 end
|
||||||
|
|||||||
@ -12,6 +12,7 @@ fireFX.fx = {}
|
|||||||
Version History
|
Version History
|
||||||
1.0.0 - Initial version
|
1.0.0 - Initial version
|
||||||
1.1.0 - persistence
|
1.1.0 - persistence
|
||||||
|
1.1.1 - agl attribute
|
||||||
|
|
||||||
--]]--
|
--]]--
|
||||||
|
|
||||||
@ -60,6 +61,9 @@ function fireFX.createFXWithZone(theZone)
|
|||||||
|
|
||||||
theZone.density = cfxZones.getNumberFromZoneProperty(theZone, "density", 0.5)
|
theZone.density = cfxZones.getNumberFromZoneProperty(theZone, "density", 0.5)
|
||||||
|
|
||||||
|
theZone.agl = cfxZones.getNumberFromZoneProperty(theZone, "AGL", 0)
|
||||||
|
|
||||||
|
|
||||||
if cfxZones.hasProperty(theZone, "start?") then
|
if cfxZones.hasProperty(theZone, "start?") then
|
||||||
theZone.fxStart = cfxZones.getStringFromZoneProperty(theZone, "start?", "*<none>")
|
theZone.fxStart = cfxZones.getStringFromZoneProperty(theZone, "start?", "*<none>")
|
||||||
theZone.fxLastStart = cfxZones.getFlagValue(theZone.fxStart, theZone)
|
theZone.fxLastStart = cfxZones.getFlagValue(theZone.fxStart, theZone)
|
||||||
@ -96,7 +100,7 @@ end
|
|||||||
function fireFX.startTheFire(theZone)
|
function fireFX.startTheFire(theZone)
|
||||||
if not theZone.burning then
|
if not theZone.burning then
|
||||||
local p = cfxZones.getPoint(theZone)
|
local p = cfxZones.getPoint(theZone)
|
||||||
p.y = land.getHeight({x = p.x, y = p.z})
|
p.y = land.getHeight({x = p.x, y = p.z}) + theZone.agl
|
||||||
local preset = theZone.fxCode
|
local preset = theZone.fxCode
|
||||||
local density = theZone.density
|
local density = theZone.density
|
||||||
trigger.action.effectSmokeBig(p, preset, density, theZone.name)
|
trigger.action.effectSmokeBig(p, preset, density, theZone.name)
|
||||||
|
|||||||
280
modules/taxiPolice.lua
Normal file
280
modules/taxiPolice.lua
Normal file
@ -0,0 +1,280 @@
|
|||||||
|
taxiPolice = {}
|
||||||
|
taxiPolice.version = "0.0.0"
|
||||||
|
taxiPolice.verbose = true
|
||||||
|
taxiPolice.ups = 1 -- checks per second
|
||||||
|
taxiPolice.requiredLibs = {
|
||||||
|
"dcsCommon", -- always
|
||||||
|
"cfxZones", -- Zones, of course
|
||||||
|
}
|
||||||
|
--[[--
|
||||||
|
-- ensure that a player doesn't overspeed on taxiways. uses speedLimit and violateDuration to determine if to fine
|
||||||
|
|
||||||
|
-- create runway polys here: https://wiki.hoggitworld.com/view/DCS_func_getRunways
|
||||||
|
|
||||||
|
-- works as follows:
|
||||||
|
-- when a player's plane is not inAir, they are monitored.
|
||||||
|
-- when on a runway or too far from airfield (only airfields are monitored) monitoring ends
|
||||||
|
-- when monitored and overspeeding, they first receive a warning, and after n warnings they receive retribution
|
||||||
|
--]]--
|
||||||
|
taxiPolice.speedLimit = 14 -- m/s . 14 m/s = 50 km/h, 10 m/s = 36 kmh
|
||||||
|
taxiPolice.triggerTime = 3 -- seconds until we register a speeding violation
|
||||||
|
taxiPolice.rwyLeeway = 5 -- meters on each side
|
||||||
|
taxiPolice.rwyExtend = 500 -- meters in front and at end
|
||||||
|
taxiPolice.airfieldMaxDist = 3000 -- radius around airfield in which we operate
|
||||||
|
taxiPolice.runways = {} -- indexed by airbase name, then by rwName
|
||||||
|
taxiPolice.suspects = {} -- units that are currently behaving naughty
|
||||||
|
taxiPolice.tickets = {} -- number of warnings per player
|
||||||
|
taxiPolice.maxTickets = 3 -- number of tickes without retribution
|
||||||
|
|
||||||
|
function taxiPolice.buildRunways()
|
||||||
|
local bases = world.getAirbases()
|
||||||
|
local mId = 0
|
||||||
|
for idb, aBase in pairs (bases) do -- i = 1, #base do
|
||||||
|
local name = aBase:getName()
|
||||||
|
local rny = aBase:getRunways()
|
||||||
|
if rny then
|
||||||
|
local runways = {}
|
||||||
|
for idx, rwy in pairs(rny) do -- j = 1, #rny do
|
||||||
|
-- calcualte quad that encloses taxiway
|
||||||
|
local points = {} -- quad
|
||||||
|
local init = rwy.position
|
||||||
|
local bearing = rwy.course * -1 -- "*-1 to make meaningful"
|
||||||
|
local rwName = bearing * 57.2958 -- rads to degree
|
||||||
|
if rwName < 0 then rwName = rwName + 360 end
|
||||||
|
rwName = math.floor(rwName / 10)
|
||||||
|
rwName = tostring(rwName)
|
||||||
|
-- calculate start and end point of RWY, "heading 0"
|
||||||
|
local radius = rwy.length/2 + taxiPolice.rwyExtend
|
||||||
|
local pStart = {y=0, x = init.x + radius, z = init.z }
|
||||||
|
local pEnd = {y=0, x = init.x - radius, z = init.z}
|
||||||
|
-- Build runway with width; at 0 heading (trivial case)
|
||||||
|
local width = rwy.width/2 + taxiPolice.rwyLeeway
|
||||||
|
local dz1 = width
|
||||||
|
local dz2 = - width
|
||||||
|
points[1] = {y = 0, x = pStart.x, z = pStart.z + dz1}
|
||||||
|
points[2] = {y = 0, x = pStart.x, z = pStart.z + dz2}
|
||||||
|
points[3] = {y = 0, x = pEnd.x, z = pEnd.z + dz2}
|
||||||
|
points[4] = {y = 0, x = pEnd.x, z = pEnd.z + dz1}
|
||||||
|
-- rotate RWY "0" to RW "bearing"
|
||||||
|
local poly = dcsCommon.rotatePoly3AroundVec3Rad(points, init, bearing)
|
||||||
|
|
||||||
|
mId = mId + 1
|
||||||
|
-- draw on map
|
||||||
|
if taxiPolice.verbose then
|
||||||
|
trigger.action.quadToAll(-1, mId, poly[1], poly[2], poly[3], poly[4], {0, 0, 0, 1}, {0, 0, 0, .5}, 3)
|
||||||
|
end
|
||||||
|
-- save runway under name
|
||||||
|
runways[rwName] = poly
|
||||||
|
|
||||||
|
-- build a 100x100 quad to show base's center
|
||||||
|
end
|
||||||
|
|
||||||
|
taxiPolice.runways[name] = runways
|
||||||
|
if taxiPolice.verbose then
|
||||||
|
local points = {}
|
||||||
|
local pStart = aBase:getPoint()
|
||||||
|
local pEnd = aBase:getPoint()
|
||||||
|
local dx1 = 100
|
||||||
|
local dz1 = 100
|
||||||
|
local dx2 = -100
|
||||||
|
local dz2 = -100
|
||||||
|
points[1] = {y = 0, x = pStart.x + dx1, z = pStart.z + dz1}
|
||||||
|
points[2] = {y = 0, x = pStart.x + dx2, z = pStart.z + dz1}
|
||||||
|
points[3] = {y = 0, x = pEnd.x + dx2, z = pEnd.z + dz2}
|
||||||
|
points[4] = {y = 0, x = pEnd.x + dx1, z = pEnd.z + dz2}
|
||||||
|
mId = mId + 1
|
||||||
|
trigger.action.quadToAll(-1, mId, points[1], points[2], points[3], points[4], {1, 0, 0, 1}, {1, 0, 0, .5}, 3)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
--
|
||||||
|
-- Checking and Policing
|
||||||
|
--
|
||||||
|
function taxiPolice.retributeAgainst(theUnit)
|
||||||
|
-- player did not learn.
|
||||||
|
local player = theUnit:getPlayerName()
|
||||||
|
trigger.action.outText("Player <" .. player .. "> behaves reckless and is being reprimanded", 30)
|
||||||
|
|
||||||
|
-- do some harsh stuff
|
||||||
|
local pGrp = theUnit:getGroup()
|
||||||
|
local gID = pGrp:getID()
|
||||||
|
trigger.action.outTextForGroup(gID, "We don't appreciate your behavior. Stop it NOW. Here's something to think about...", 30)
|
||||||
|
|
||||||
|
trigger.action.setUnitInternalCargo(theUnit:getName() , 1000000 ) -- add 1000t
|
||||||
|
end
|
||||||
|
|
||||||
|
function taxiPolice.checkUnit(theUnit, allAirfields)
|
||||||
|
if not theUnit.getPlayerName then return end
|
||||||
|
local player = theUnit:getPlayerName()
|
||||||
|
if not player then return end
|
||||||
|
|
||||||
|
local p = theUnit:getPoint()
|
||||||
|
p.y = 0
|
||||||
|
local base, dist = dcsCommon.getClosestAirbaseTo(p, nil, nil, allAirfields)
|
||||||
|
if dist > taxiPolice.airfieldMaxDist then
|
||||||
|
taxiPolice.suspects[player] = nil -- remove watched status
|
||||||
|
return
|
||||||
|
end -- not interesting
|
||||||
|
|
||||||
|
local vel = dcsCommon.getUnitSpeed(theUnit)
|
||||||
|
|
||||||
|
-- if we get here, player is on the ground, in proximity to airfield
|
||||||
|
if vel < taxiPolice.speedLimit then
|
||||||
|
taxiPolice.suspects[player] = nil -- remove watched status
|
||||||
|
return
|
||||||
|
end -- not speeding
|
||||||
|
|
||||||
|
-- if we get here, we also exceed the speed limit
|
||||||
|
-- check if we are on a runway
|
||||||
|
local myRunways = taxiPolice.runways[base:getName()]
|
||||||
|
if not myRunways then
|
||||||
|
-- this base is turned off
|
||||||
|
--trigger.action.outText("unable to find raunways for <" .. base:getName() .. ">", 30)
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
|
for rwName, aRunway in pairs(myRunways) do
|
||||||
|
if cfxZones.isPointInsidePoly(p, aRunway) then
|
||||||
|
--trigger.action.outText("<" .. theUnit:getName() .. "> is on RWY <" .. rwName .. ">", 30)
|
||||||
|
taxiPolice.suspects[player] = nil -- remove watched status
|
||||||
|
return
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
-- if we get here, player is speeding on airfield
|
||||||
|
local speedingSince = taxiPolice.suspects[player] -- time since speeding started
|
||||||
|
|
||||||
|
if not speedingSince then
|
||||||
|
-- we start watching now. At least one second will be grace period
|
||||||
|
taxiPolice.suspects[player] = timer.getTime()
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
|
if timer.getTime() - speedingSince < taxiPolice.triggerTime then
|
||||||
|
-- we are watching, but not acting
|
||||||
|
--trigger.action.outText(player .. ", you are being watched: <" .. timer.getTime() - speedingSince .. ">", 30)
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
|
-- when we get here, player is in violation.
|
||||||
|
-- make sure we will not trigger again by setting future speedingsince to negative
|
||||||
|
taxiPolice.suspects[player] = timer.getTime() + 10000 -- 10000 seconds in the future
|
||||||
|
|
||||||
|
local vioNum = taxiPolice.tickets[player]
|
||||||
|
if not vioNum then vioNum = 0 end
|
||||||
|
vioNum = vioNum + 1
|
||||||
|
taxiPolice.tickets[player] = vioNum
|
||||||
|
|
||||||
|
local pGrp = theUnit:getGroup()
|
||||||
|
local gID = pGrp:getID()
|
||||||
|
|
||||||
|
if vioNum <= taxiPolice.maxTickets then
|
||||||
|
-- just post a warning
|
||||||
|
trigger.action.outTextForGroup(gID, player .. ", your taxi speed is reckless. Stop it. Violations registered against you: " .. vioNum, 30)
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
|
-- we have reached retribution stage
|
||||||
|
taxiPolice.retributeAgainst(theUnit)
|
||||||
|
end
|
||||||
|
|
||||||
|
---
|
||||||
|
--- UPDATE
|
||||||
|
---
|
||||||
|
function taxiPolice.update() -- every second
|
||||||
|
-- schedule next invocation
|
||||||
|
timer.scheduleFunction(taxiPolice.update, {}, timer.getTime() + 1/taxiPolice.ups)
|
||||||
|
|
||||||
|
local allAirfields = dcsCommon.getAirbasesWhoseNameContains("*", 0) -- all fixed bases, no FARP nor ships. Pre-collect
|
||||||
|
|
||||||
|
-- check all player units
|
||||||
|
local playerFactions = {1, 2}
|
||||||
|
for idx, aFaction in pairs(playerFactions) do
|
||||||
|
local allPlayers = coalition.getPlayers(aFaction)
|
||||||
|
for idy, aPlayer in pairs(allPlayers) do -- returns UNITS!
|
||||||
|
if Unit.isActive(aPlayer) and not aPlayer:inAir() then
|
||||||
|
taxiPolice.checkUnit(aPlayer, allAirfields)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
end
|
||||||
|
|
||||||
|
--
|
||||||
|
-- START
|
||||||
|
--
|
||||||
|
function taxiPolice.processTaxiZones()
|
||||||
|
local taxiZones = cfxZones.zonesWithProperty("taxiPolice")
|
||||||
|
local allAirfields = dcsCommon.getAirbasesWhoseNameContains("*", 0)
|
||||||
|
|
||||||
|
for idx, theZone in pairs(taxiZones) do
|
||||||
|
local isPoliced = cfxZones.getBoolFromZoneProperty(theZone, "taxiPolice", "true")
|
||||||
|
if not isPoliced then
|
||||||
|
local p = cfxZones.getPoint(theZone)
|
||||||
|
local base, dist = dcsCommon.getClosestAirbaseTo(p, nil, nil, allAirfields)
|
||||||
|
local name = base:getName()
|
||||||
|
taxiPolice.runways[name] = nil
|
||||||
|
if taxiPolice.verbose then
|
||||||
|
trigger.action.outText("txPol: base <" .. name .. "> taxiways not policed.", 30)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
function taxiPolice.readConfigZone()
|
||||||
|
local theZone = cfxZones.getZoneByName("taxiPoliceConfig")
|
||||||
|
if not theZone then
|
||||||
|
theZone = cfxZones.createSimpleZone("taxiPoliceConfig")
|
||||||
|
if taxiPolice.verbose then
|
||||||
|
trigger.action.outText("+++txPol: no config zone!", 30)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
taxiPolice.verbose = theZone.verbose
|
||||||
|
|
||||||
|
taxiPolice.speedLimit = cfxZones.getNumberFromZoneProperty(theZone, "speedLimit", 14) -- 14 -- m/s. 14 m/s = 50 km/h, 10 m/s = 36 kmh
|
||||||
|
taxiPolice.triggerTime = cfxZones.getNumberFromZoneProperty(theZone, "triggerTime", 3) --3 -- seconds until we register a speeding violation
|
||||||
|
taxiPolice.rwyLeeway = cfxZones.getNumberFromZoneProperty(theZone, "leeway", 5) -- 5 -- meters on each side
|
||||||
|
taxiPolice.rwyExtend = cfxZones.getNumberFromZoneProperty(theZone, "extend", 500) --500 -- meters in front and at end
|
||||||
|
taxiPolice.airfieldMaxDist = cfxZones.getNumberFromZoneProperty(theZone, "radius", 3000) -- 3000 -- radius around airfield in which we operate
|
||||||
|
taxiPolice.maxTickets = cfxZones.getNumberFromZoneProperty(theZone, "maxTickets", 3) -- 3
|
||||||
|
|
||||||
|
end
|
||||||
|
|
||||||
|
function taxiPolice.start()
|
||||||
|
-- lib check
|
||||||
|
if not dcsCommon.libCheck then
|
||||||
|
trigger.action.outText("cfx taxiPolice requires dcsCommon", 30)
|
||||||
|
return false
|
||||||
|
end
|
||||||
|
if not dcsCommon.libCheck("cfx taxiPolice", taxiPolice.requiredLibs) then
|
||||||
|
return false
|
||||||
|
end
|
||||||
|
|
||||||
|
-- read config
|
||||||
|
taxiPolice.readConfigZone()
|
||||||
|
|
||||||
|
-- build taxiway db
|
||||||
|
taxiPolice.buildRunways()
|
||||||
|
|
||||||
|
-- read taxiPolice attributes
|
||||||
|
taxiPolice.processTaxiZones()
|
||||||
|
|
||||||
|
-- start update
|
||||||
|
taxiPolice.update()
|
||||||
|
|
||||||
|
-- say hi!
|
||||||
|
trigger.action.outText("cfx taxiPolice v" .. taxiPolice.version .. " started.", 30)
|
||||||
|
return true
|
||||||
|
end
|
||||||
|
|
||||||
|
-- let's go!
|
||||||
|
if not taxiPolice.start() then
|
||||||
|
trigger.action.outText("cfx taxiPolice aborted: missing libraries", 30)
|
||||||
|
taxiPolice = nil
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
Binary file not shown.
Loading…
x
Reference in New Issue
Block a user