mirror of
https://github.com/weyne85/DML.git
synced 2025-10-29 16:57:49 +00:00
2.0.3
civHelo
This commit is contained in:
parent
61f33561fc
commit
a44f145218
Binary file not shown.
Binary file not shown.
@ -1,11 +1,11 @@
|
||||
cfxZones = {}
|
||||
cfxZones.version = "4.1.2"
|
||||
cfxZones.version = "4.2.0"
|
||||
|
||||
-- cf/x zone management module
|
||||
-- reads dcs zones and makes them accessible and mutable
|
||||
-- by scripting.
|
||||
--
|
||||
-- Copyright (c) 2021 - 2023 by Christian Franz and cf/x AG
|
||||
-- Copyright (c) 2021 - 2024 by Christian Franz and cf/x AG
|
||||
--
|
||||
|
||||
--[[-- VERSION HISTORY
|
||||
@ -44,6 +44,7 @@ cfxZones.version = "4.1.2"
|
||||
- 4.1.0 - getBoolFromZoneProperty 'on/off' support for dml variant as well
|
||||
- 4.1.1 - evalRemainder() updates
|
||||
- 4.1.2 - hash property missing warning
|
||||
- 4.2.0 - new createRandomPointInPopulatedZone()
|
||||
|
||||
--]]--
|
||||
|
||||
@ -425,6 +426,95 @@ function dmlZone:createRandomPointInPolyZone(onEdge)
|
||||
return p, dx, dz
|
||||
end
|
||||
|
||||
function dmlZone:createRandomPointInPopulatedZone(radius, maxTries)
|
||||
if not maxTries then maxTries = 20 end
|
||||
if not radius then radius = 10 end -- meters
|
||||
local cnt = 0
|
||||
local p, dx, dz
|
||||
repeat
|
||||
p, dx, dz = self:createRandomPointInZone() -- p is x, 0, z
|
||||
local hits, collector = cfxZones.objectsInRange(p, radius)
|
||||
if hits < 1 then return p, dx, dz end
|
||||
if hits == 1 then
|
||||
local o = collector[1]
|
||||
local op = o:getPoint()
|
||||
d = dcsCommon.distFlat(op, p)
|
||||
-- trigger.action.outText("singleDist = " .. d, 30)
|
||||
if d > radius/2 then
|
||||
-- trigger.action.outText("good enough, will use", 30)
|
||||
return p, dx, dz
|
||||
end
|
||||
end
|
||||
cnt = cnt + 1
|
||||
-- trigger.action.outText(hits .. "hits --> failed try " .. cnt, 30)
|
||||
until cnt > maxTries
|
||||
return p, dx, dz
|
||||
end
|
||||
|
||||
function cfxZones.createRandomPointInPopulatedZone(theZone, radius, maxTries)
|
||||
if not theZone then return nil, nil, nil end
|
||||
local p, dx, dz = theZone:createRandomPointInPopulatedZone(radius, maxTries)
|
||||
return p, dx, dz
|
||||
end
|
||||
|
||||
--[[--
|
||||
function dmlZone:createRandomPointInPopulatedZone(radius, maxTries)
|
||||
if not maxTries then maxTries = 20 end
|
||||
local cnt = 0
|
||||
local p, dx, dz
|
||||
p, dx, dz = self:createRandomPointInZone() -- p is x, 0, z
|
||||
repeat
|
||||
local hits = cfxZones.objectsInRange(p, radius)
|
||||
if hits < 1 then return p, dx, dz end
|
||||
-- move to the right by radius
|
||||
p.z = p.z + radius
|
||||
dz = dz + radius
|
||||
cnt = cnt + 1
|
||||
trigger.action.outText("failed try " .. cnt, 30)
|
||||
until cnt > maxTries
|
||||
return p, dx, dz
|
||||
end
|
||||
--]]--
|
||||
function cfxZones.objectHandler(theObject, theCollector) -- for world.search
|
||||
table.insert(theCollector, theObject)
|
||||
return true
|
||||
end
|
||||
|
||||
function cfxZones.objectsInRange(pt, range)
|
||||
if not range then range = 100 end -- meters
|
||||
local allCats = {1, 2, 3, 4, 5, 6} -- all cats
|
||||
local lp = {x = pt.x, y = pt.z}
|
||||
pt.y = land.getHeight(lp)
|
||||
local collector = {}
|
||||
-- now build the search argument
|
||||
local args = {
|
||||
id = world.VolumeType.SPHERE,
|
||||
params = {
|
||||
point = pt,
|
||||
radius = range -- range
|
||||
}
|
||||
}
|
||||
-- now call search
|
||||
world.searchObjects(allCats, args, cfxZones.objectHandler, collector)
|
||||
-- now filter for distance because search finds too many
|
||||
local filtered = {}
|
||||
for idx, anObject in pairs(collector) do
|
||||
-- calc dist and filter
|
||||
local op = anObject:getPoint()
|
||||
local dist = dcsCommon.dist(pt, op)
|
||||
if dist < range then
|
||||
-- local e = {
|
||||
-- dist = dist,
|
||||
-- o = anObject
|
||||
-- }
|
||||
-- table.insert(filtered, e)
|
||||
table.insert(filtered, anObject)
|
||||
end
|
||||
end
|
||||
|
||||
return #filtered, filtered
|
||||
end
|
||||
|
||||
function cfxZones.addZoneToManagedZones(theZone)
|
||||
local upperName = string.upper(theZone.name) -- newZone.name:upper()
|
||||
cfxZones.zones[upperName] = theZone
|
||||
|
||||
474
modules/civHelo.lua
Normal file
474
modules/civHelo.lua
Normal file
@ -0,0 +1,474 @@
|
||||
civHelo = {}
|
||||
civHelo.version = "1.0.0"
|
||||
civHelo.requiredLibs = {
|
||||
"dcsCommon", -- always
|
||||
"cfxZones",
|
||||
}
|
||||
--[[--
|
||||
Version History
|
||||
1.0.0 - Initial version
|
||||
|
||||
--]]--
|
||||
|
||||
civHelo.flights = {} -- currently active flights
|
||||
civHelo.ports = {} -- civHelo zones where flight can take off and land
|
||||
civHelo.maxDist = 600000 -- 60 km
|
||||
civHelo.minDist = 1000 -- 1 km
|
||||
-- helos and liveries
|
||||
civHelo.types = {"CH-47D", "CH-53E", "Ka-27", "Mi-24V", "Mi-26", "Mi-28N", "Mi-8MT","OH-58D", "SA342L", "SH-60B", "UH-1H", "UH-60A",} -- default set
|
||||
|
||||
civHelo.liveries = {
|
||||
["CH-47D"] = {"Australia RAAF", "ch-47_green neth", "ch-47_green spain", "ch-47_green uk", "Greek Army", "standard", },
|
||||
["CH-53E"] = {"standard",},
|
||||
["Ka-27"] = {"China PLANAF", "standard", "ukraine camo 1",},
|
||||
["Mi-24V"] = {"Abkhazia", "Algerian AF Black", "Algerian AF New Desert", "Algerian AF Old Desert", "Russia_FSB", "Russia_MVD", "South Ossetia", "standard", "standard 1", "standard 2 (faded and sun-bleached)", "ukraine", "Ukraine UN", },
|
||||
["Mi-26"] = {"7th Separate Brigade of AA (Kalinov)", "Algerian Air Force SL-22", "China Flying Dragon Aviation", "RF Air Force", "Russia_FSB", "Russia_MVD", "United Nations", },
|
||||
["Mi-28N"] = {"AAF SC-11", "AAF SC-12", "night", "standard", },
|
||||
["Mi-8MT"] = {"China UN", "IR Iranian Special Police Forces", "Russia_Gazprom", "Russia_PF_Ambulance", "Russia_Police", "Russia_UN", "Russia_UTair", "Russia_Aeroflot", "Russia_KazanVZ", "Russia_LII_Gromov RA-25546", "Russia_Vertolety_Russia", "Russia_Vertolety_Russia_2", "Russia_Naryan-Mar", },
|
||||
--["OH-58D"] = {"",},
|
||||
--["SA342L"] = {"",},
|
||||
["SH-60B"] = {"Hellenic Navy", "standard", },
|
||||
["UH-1H"] = {"[Civilian] Medical", "[Civilian] NASA", "[Civilian] Standard", "[Civilian] VIP", "Greek Army Aviation Medic", "Italy 15B Stormo S.A.R -Soccorso", "Norwegian Coast Guard (235)", "Norwegian UN", "Spanish UN", "USA UN", },
|
||||
["UH-60A"] = {"ISRAIL_UN", }
|
||||
}
|
||||
--
|
||||
-- process civHelo zone
|
||||
--
|
||||
function civHelo.readCivHeloZone(theZone)
|
||||
-- process properties
|
||||
theZone.canLand = theZone:getBoolFromZoneProperty("land", true)
|
||||
theZone.canStart = theZone:getBoolFromZoneProperty("start", true)
|
||||
theZone.hotStart = theZone:getBoolFromZoneProperty("hot", true)
|
||||
|
||||
if theZone:hasProperty("types") then
|
||||
local hTypes = theZone:getStringFromZoneProperty("types", "xxx")
|
||||
local typeArray = dcsCommon.splitString(hTypes, ",")
|
||||
typeArray = dcsCommon.trimArray(typeArray)
|
||||
theZone.types = typeArray
|
||||
end
|
||||
-- set active flag
|
||||
theZone.inUse = nil -- if true zone is in use for a flight
|
||||
end
|
||||
|
||||
function civHelo.addCivHeloZone(theZone)
|
||||
table.insert(civHelo.ports, theZone)
|
||||
end
|
||||
|
||||
function civHelo.getPortNamed(name)
|
||||
for idx, theZone in pairs(civHelo.ports) do
|
||||
if theZone.name == name then return theZone end
|
||||
end
|
||||
if civHelo.verbose then
|
||||
trigger.action.outText("+++civH: cannot find port <" .. name .. ">", 30)
|
||||
end
|
||||
return nil
|
||||
end
|
||||
|
||||
function civHelo.getFreePort(source, dest, anchor)
|
||||
collector = {}
|
||||
local a
|
||||
if anchor then a = anchor:getPoint() end -- for dist calc
|
||||
for idx, theZone in pairs(civHelo.ports) do
|
||||
if theZone.inUse then
|
||||
else
|
||||
if (source and theZone.canStart) or
|
||||
(dest and theZone.canLand) then
|
||||
if anchor then
|
||||
-- must be at least minDist and at most maxDist away
|
||||
local p = theZone:getPoint()
|
||||
local d = dcsCommon.dist(a, p)
|
||||
if d > civHelo.minDist and d < civHelo.maxDist then
|
||||
table.insert(collector, theZone)
|
||||
else
|
||||
-- trigger.action.outText("+++civH: disregarded dest zone <" .. theZone.name .. ">: dist <" .. math.floor(d) / 1000 .. " km> out of bounds", 30)
|
||||
end
|
||||
else
|
||||
table.insert(collector, theZone)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
if #collector < 1 then return nil end
|
||||
local theZone = dcsCommon.pickRandom(collector)
|
||||
return theZone
|
||||
end
|
||||
|
||||
function civHelo.getSourceAndDest()
|
||||
local source = civHelo.getFreePort(true, false)
|
||||
if not source then return nil, nil end
|
||||
source.inUse = true
|
||||
local dest = civHelo.getFreePort(false, true, source)
|
||||
if not dest then
|
||||
source.inUse = nil
|
||||
return nil, nil
|
||||
end
|
||||
dest.inUse = true
|
||||
return source, dest
|
||||
end
|
||||
|
||||
|
||||
function civHelo.createCommandTask(theCommand, num)
|
||||
if not num then num = 1 end
|
||||
local t = {}
|
||||
t.enabled = true
|
||||
t.auto = false
|
||||
t.id = "WrappedAction"
|
||||
t.number = num
|
||||
local params = {}
|
||||
t.params = params
|
||||
local action = {}
|
||||
params.action = action
|
||||
action.id = "Script"
|
||||
local p2 = {}
|
||||
action.params = p2
|
||||
p2.command = theCommand
|
||||
return t
|
||||
end
|
||||
|
||||
function civHelo.createLandTask(p, duration, num)
|
||||
if not num then num = 1 end
|
||||
local t = {}
|
||||
t.enabled = true
|
||||
t.auto = false
|
||||
t.id = "ControlledTask"
|
||||
t.number = num
|
||||
local params = {}
|
||||
t.params = params
|
||||
|
||||
local ptsk = {}
|
||||
params.task = ptsk
|
||||
ptsk.id = "Land"
|
||||
local ptp = {}
|
||||
ptsk.params = ptp
|
||||
ptp.x = p.x
|
||||
ptp.y = p.z
|
||||
ptp.duration = "300" -- not sure why
|
||||
ptp.durationFlag = false -- off anyway
|
||||
local stopCon = {}
|
||||
stopCon.duration = duration
|
||||
params.stopCondition = stopCon
|
||||
|
||||
return t
|
||||
end
|
||||
|
||||
function civHelo.createFlight(name, theType, fromZone, toZone) --, inAir)
|
||||
if not fromZone then return nil end
|
||||
if not toZone then return nil end
|
||||
-- if not inAir then inAir = false end
|
||||
|
||||
local theGroup = dcsCommon.createEmptyAircraftGroupData (name)
|
||||
local theHUnit = dcsCommon.createAircraftUnitData(name .. "-H", theType, false)
|
||||
if fromZone.hdg then
|
||||
|
||||
end
|
||||
-- add livery capability for this aircraft
|
||||
--civHelo.processLiveriesFor(theHUnit, theType)
|
||||
civHelo.getLiveryForType(theType, theHUnit)
|
||||
|
||||
-- enforce civ attribute
|
||||
theHUnit.civil_plane = true
|
||||
|
||||
theHUnit.payload.fuel = 5000 -- 5t of fuel
|
||||
dcsCommon.addUnitToGroupData(theHUnit, theGroup)
|
||||
|
||||
local A = fromZone:getPoint()
|
||||
local B = toZone:getPoint()
|
||||
|
||||
-- unit is done, let's do the route
|
||||
-- WP 1: take off
|
||||
local fromWP, omwWP
|
||||
fromWP = dcsCommon.createTakeOffFromGroundRoutePointData(fromZone:getPoint(true), fromZone.hotStart) -- last true = hot
|
||||
fromWP.alt_type = "RADIO" -- AGL instead of MSL
|
||||
theHUnit.alt = fromWP.alt
|
||||
-- WP2: signal that we are 1km away so source can be freed from flight
|
||||
local dir = dcsCommon.bearingFromAtoB(A, B) -- x0z coords
|
||||
local omw = dcsCommon.pointInDirectionOfPointXYY(dir, 1000, A)
|
||||
omwWP = dcsCommon.createSimpleRoutePointData(omw, civHelo.alt, civHelo.speed)
|
||||
omwWP.alt_type = "RADIO"
|
||||
-- create a command waypoint
|
||||
local task = {}
|
||||
task.id = "ComboTask"
|
||||
task.params = {}
|
||||
local ttsk = {}
|
||||
local command = "civHelo.departedCB('" .. name .. "', '" .. fromZone:getName() .. "')"
|
||||
ttsk[1] = civHelo.createCommandTask(command,1)
|
||||
task.params.tasks = ttsk
|
||||
omwWP.task = task
|
||||
|
||||
-- now set up destination point: land
|
||||
-- at destination and add a small script
|
||||
local toWP
|
||||
-- add destination WP. this is common to both
|
||||
toWP = dcsCommon.createSimpleRoutePointData(toZone:getPoint(), civHelo.alt, civHelo.speed)
|
||||
toWP.alt_type = "RADIO"
|
||||
|
||||
local task = {}
|
||||
task.id = "ComboTask"
|
||||
task.params = {}
|
||||
local ttsk = {}
|
||||
local p = toZone:getPoint()
|
||||
ttsk[1] = civHelo.createLandTask(p, civHelo.landingDuration, 1)
|
||||
local command = "civHelo.landedCB('" .. name .. "', '" .. toZone:getName() .. "')"
|
||||
ttsk[2] = civHelo.createCommandTask(command,2)
|
||||
task.params.tasks = ttsk
|
||||
toWP.task = task
|
||||
|
||||
-- move group to WP1 and add WP1 and WP2 to route
|
||||
dcsCommon.moveGroupDataTo(theGroup,
|
||||
fromWP.x,
|
||||
fromWP.y)
|
||||
dcsCommon.addRoutePointForGroupData(theGroup, fromWP)
|
||||
if not inAir then
|
||||
dcsCommon.addRoutePointForGroupData(theGroup, omwWP)
|
||||
end
|
||||
dcsCommon.addRoutePointForGroupData(theGroup, toWP)
|
||||
|
||||
-- spawn
|
||||
local groupCat = Group.Category.HELICOPTER
|
||||
local theSpawnedGroup = coalition.addGroup(civHelo.owner, groupCat, theGroup)
|
||||
|
||||
return theSpawnedGroup
|
||||
end
|
||||
|
||||
function civHelo.openPort(where)
|
||||
local thePort = civHelo.getPortNamed(where)
|
||||
if thePort then
|
||||
thePort.inUse = nil
|
||||
end
|
||||
if civHelo.verbose then trigger.action.outText("+++civH: opening port <" .. where .. ">", 30) end
|
||||
end
|
||||
|
||||
function civHelo.openPortUsedBy(name)
|
||||
for idx, theZone in pairs(civHelo.ports) do
|
||||
if theZone.inUse == name then
|
||||
theZone.inUse = nil
|
||||
if civHelo.verbose then
|
||||
trigger.action.outText("+++civH: clearing port <" .. theZone.name .. "> from flight <" .. name .. ">", 30)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function civHelo.departedCB(who, where)
|
||||
-- free the port that we just took off from
|
||||
civHelo.openPort(where)
|
||||
end
|
||||
|
||||
function civHelo.landedCB(who, where)
|
||||
-- step 1: remove the flight
|
||||
local theGroup = Group.getByName(who)
|
||||
if theGroup then
|
||||
if Group.isExist(theGroup) then
|
||||
Group.destroy(theGroup)
|
||||
end
|
||||
else
|
||||
trigger.action.outText("+++civH: cannot find group <" .. who .. ">", 30)
|
||||
end
|
||||
civHelo.flights[who] = nil
|
||||
|
||||
-- step 2: schedule opening the port
|
||||
-- do it immediately first
|
||||
civHelo.openPort(where)
|
||||
end
|
||||
|
||||
--
|
||||
-- new flight
|
||||
--
|
||||
function civHelo.getType(theZone)
|
||||
local types = civHelo.types -- load default
|
||||
if theZone.types then types = theZone.types end
|
||||
local hType = dcsCommon.pickRandom(types)
|
||||
return hType
|
||||
end
|
||||
|
||||
function civHelo.getLiveryForType(theType, theData)
|
||||
if civHelo.liveries[theType] then
|
||||
local available = civHelo.liveries[theType]
|
||||
local chosen = dcsCommon.pickRandom(available)
|
||||
theData.livery_id = chosen
|
||||
end
|
||||
end
|
||||
|
||||
function civHelo.newFlight()
|
||||
local source, dest = civHelo.getSourceAndDest()
|
||||
if source and dest then
|
||||
-- source and dest "inUse" already have been marked inUse
|
||||
-- but still need the name of the flight
|
||||
local theType = civHelo.getType(source)
|
||||
local name = source:getName() .. "-" .. dest:getName()
|
||||
local theFlight = civHelo.createFlight(name, theType, source, dest)
|
||||
if theFlight then
|
||||
civHelo.flights[name] = theFlight
|
||||
source.inUse = name
|
||||
dest.inUse = name
|
||||
if civHelo.verbose then
|
||||
trigger.action.outText("+++civH: created new flight <" .. name .. ">", 30)
|
||||
end
|
||||
else
|
||||
trigger.action.outText("+++civH: cant create flight <" .. name .. ">", 30)
|
||||
source.inUse = nil
|
||||
dest.inUse = nil
|
||||
end
|
||||
else
|
||||
if civHelo.verbose then
|
||||
trigger.action.outText("+++civH: no ports available, can't create new flight. Numflights = <" .. dcsCommon.getSizeOfTable(civHelo.flights) .. ">", 30)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
--
|
||||
-- event handler
|
||||
--
|
||||
function civHelo:onEvent(theEvent)
|
||||
-- trigger.action.outText("event", 30)
|
||||
if not theEvent.initiator then return end
|
||||
local theUnit = theEvent.initiator
|
||||
if not theUnit.getGroup then return end
|
||||
local theGroup = theUnit:getGroup()
|
||||
if not theGroup then return end
|
||||
local gName = theGroup:getName()
|
||||
|
||||
-- see if it's an event for one of mine
|
||||
local mine = false
|
||||
for name, aGroup in pairs(civHelo.flights) do
|
||||
if name == gName then mine = true end
|
||||
end
|
||||
if not mine then
|
||||
return
|
||||
end
|
||||
|
||||
local id = theEvent.id
|
||||
if id == 9 or -- pilot dead
|
||||
id == 30 or -- unit lost
|
||||
id == 5 -- crash
|
||||
then
|
||||
if civHelo.verbose then
|
||||
trigger.action.outText("+++civH: cancelling flight <" .. gName .. ">: mishap", 30)
|
||||
end
|
||||
civHelo.openPortUsedBy(gName)
|
||||
civHelo.flights[gName] = nil
|
||||
end
|
||||
end
|
||||
|
||||
--
|
||||
-- update
|
||||
--
|
||||
function civHelo.update()
|
||||
-- schedule again
|
||||
timer.scheduleFunction(civHelo.update, {}, timer.getTime() + 1/civHelo.ups )
|
||||
|
||||
-- see how many flights are live
|
||||
if dcsCommon.getSizeOfTable(civHelo.flights) < civHelo.maxFlights then
|
||||
civHelo.newFlight()
|
||||
end
|
||||
end
|
||||
|
||||
--
|
||||
-- Config
|
||||
--
|
||||
function civHelo.addTypesAndLiveries(rawIn)
|
||||
local newTypes = {}
|
||||
local newLiveries = {}
|
||||
-- now iterate the input table, and generate new types and
|
||||
-- liveries from it
|
||||
for theType, liveries in pairs (rawIn) do
|
||||
if civHelo.verbose then
|
||||
trigger.action.outText("+++civH: processing type <" .. theType .. ">:<" .. liveries .. ">", 30)
|
||||
end
|
||||
local livA = dcsCommon.splitString(liveries, ',')
|
||||
livA = dcsCommon.trimArray(livA)
|
||||
table.insert(newTypes, theType)
|
||||
newLiveries[theType] = livA
|
||||
end
|
||||
|
||||
return newTypes, newLiveries
|
||||
end
|
||||
|
||||
function civHelo.readConfigZone()
|
||||
local theZone = cfxZones.getZoneByName("civHeloConfig")
|
||||
if not theZone then
|
||||
theZone = cfxZones.createSimpleZone("civHeloConfig")
|
||||
end
|
||||
civHelo.verbose = theZone.verbose
|
||||
civHelo.owner = theZone:getNumberFromZoneProperty("country", 82) --82 -- UN peacekeepers
|
||||
civHelo.ups = theZone:getNumberFromZoneProperty("ups", 1 / 30)
|
||||
civHelo.maxFlights = theZone:getNumberFromZoneProperty("maxFlights", 5)
|
||||
civHelo.landingDuration = theZone:getNumberFromZoneProperty("landingDuration", 180) -- seconds = 3 minutes
|
||||
civHelo.alt = theZone:getNumberFromZoneProperty("alt", 100) -- 100 m
|
||||
civHelo.speed = theZone:getNumberFromZoneProperty("speed", 30)
|
||||
civHelo.maxDist = theZone:getNumberFromZoneProperty("maxDist", 60000)
|
||||
civHelo.minDist = theZone:getNumberFromZoneProperty("minDist", 1000)
|
||||
if theZone:hasProperty("types") then
|
||||
local hTypes = theZone:getStringFromZoneProperty("types", "xxx")
|
||||
local typeArray = dcsCommon.splitString(hTypes, ",")
|
||||
typeArray = dcsCommon.trimArray(typeArray)
|
||||
civHelo.types = typeArray
|
||||
end
|
||||
|
||||
-- now get types and liveries from 'helo_liveries' if present
|
||||
local livZone = cfxZones.getZoneByName("helo_liveries")
|
||||
if livZone then
|
||||
if civHelo.verbose then
|
||||
trigger.action.outText("civH: found and processing 'helo_liveries' zone data.", 30)
|
||||
end
|
||||
|
||||
-- read all into my types registry, replacing whatever is there
|
||||
local rawLiver = cfxZones.getAllZoneProperties(livZone)
|
||||
local newTypes, newLiveries = civAir.addTypesAndLiveries(rawLiver)
|
||||
-- now types to existing types if not already there
|
||||
for idx, aType in pairs(newTypes) do
|
||||
dcsCommon.addToTableIfNew(civHelo.types, aType)
|
||||
if civHelo.verbose then
|
||||
trigger.action.outText("+++civH: processed and added helo <" .. aType .. "> to civHelo", 30)
|
||||
end
|
||||
end
|
||||
-- now replace liveries or add if not already there
|
||||
for aType, liveries in pairs(newLiveries) do
|
||||
civHelo.liveries[aType] = liveries
|
||||
if civHelo.verbose then
|
||||
trigger.action.outText("+++civH: replaced/added liveries for helicopter <" .. aType .. ">", 30)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
--
|
||||
-- start
|
||||
--
|
||||
function civHelo.start()
|
||||
-- lib check
|
||||
if not dcsCommon.libCheck then
|
||||
trigger.action.outText("cfx civ helo requires dcsCommon", 30)
|
||||
return false
|
||||
end
|
||||
if not dcsCommon.libCheck("cfx civ helo", civHelo.requiredLibs) then
|
||||
return false
|
||||
end
|
||||
|
||||
-- read config
|
||||
civHelo.readConfigZone()
|
||||
|
||||
-- process civHelo Zones
|
||||
-- old style
|
||||
local attrZones = cfxZones.getZonesWithAttributeNamed("civHelo")
|
||||
for k, aZone in pairs(attrZones) do
|
||||
civHelo.readCivHeloZone(aZone) -- process attributes
|
||||
civHelo.addCivHeloZone(aZone) -- add to list
|
||||
end
|
||||
|
||||
-- start update in 5 seconds
|
||||
timer.scheduleFunction(civHelo.update, {}, timer.getTime() + 5)
|
||||
|
||||
-- install event handler
|
||||
world.addEventHandler(civHelo)
|
||||
|
||||
-- say hi
|
||||
trigger.action.outText("civHelo v" .. civHelo.version .. " started.", 30)
|
||||
return true
|
||||
end
|
||||
|
||||
if not civHelo.start() then
|
||||
trigger.action.outText("civHelo failed to start.")
|
||||
civHelo = nil
|
||||
end
|
||||
|
||||
@ -303,7 +303,9 @@ function cloneZones.createClonerWithZone(theZone) -- has "Cloner"
|
||||
if theZone:hasProperty("wholeGroups") then
|
||||
theZone.centerOnly = theZone:getBoolFromZoneProperty( "wholeGroups", false)
|
||||
end
|
||||
|
||||
if theZone:hasProperty("inBuiltup") then
|
||||
theZone.inBuiltup = theZone:getNumberFromZoneProperty("inBuiltup", 10) -- 10 meter radius must be free -- small houses
|
||||
end
|
||||
theZone.rndHeading = theZone:getBoolFromZoneProperty("rndHeading", false)
|
||||
|
||||
theZone.onRoad = theZone:getBoolFromZoneProperty("onRoad", false)
|
||||
@ -1063,6 +1065,8 @@ function cloneZones.spawnWithTemplateForZone(theZone, spawnZone)
|
||||
local loc, dx, dy
|
||||
if spawnZone.onPerimeter then
|
||||
loc, dx, dy = spawnZone:createRandomPointOnZoneBoundary()
|
||||
elseif spawnZone.inBuiltup then
|
||||
loc, dx, dy = spawnZone:createRandomPointInPopulatedZone(spawnZone.inBuiltup)
|
||||
else
|
||||
loc, dx, dy = spawnZone:createRandomPointInZone() -- also supports polygonal zones
|
||||
end
|
||||
@ -1072,6 +1076,8 @@ function cloneZones.spawnWithTemplateForZone(theZone, spawnZone)
|
||||
-- *every unit's displacement is randomized
|
||||
if spawnZone.onPerimeter then
|
||||
loc, dx, dy = spawnZone:createRandomPointOnZoneBoundary()
|
||||
elseif spawnZone.inBuiltup then
|
||||
loc, dx, dy = spawnZone:createRandomPointInPopulatedZone(spawnZone.inBuiltup)
|
||||
else
|
||||
loc, dx, dy = spawnZone:createRandomPointInZone()
|
||||
end
|
||||
@ -1322,6 +1328,8 @@ function cloneZones.spawnWithTemplateForZone(theZone, spawnZone)
|
||||
local loc, dx, dy
|
||||
if spawnZone.onPerimeter then
|
||||
loc, dx, dy = spawnZone:createRandomPointOnZoneBoundary()
|
||||
elseif spawnZone.inBuiltup then
|
||||
loc, dx, dy = spawnZone:createRandomPointInPopulatedZone(spawnZone.inBuiltup)
|
||||
else
|
||||
loc, dx, dy = spawnZone:createRandomPointInZone() -- also supports polygonal zones
|
||||
end
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
csarManager = {}
|
||||
csarManager.version = "3.1.0"
|
||||
csarManager.version = "3.2.0"
|
||||
csarManager.ups = 1
|
||||
|
||||
--[[-- VERSION HISTORY
|
||||
@ -28,9 +28,14 @@ csarManager.ups = 1
|
||||
- inflight status reflects time limit but will not time out
|
||||
- pickupSound option
|
||||
- lostSound option
|
||||
- 3.1.1 - birth clears troopsOnBoard
|
||||
- 3.2.0 - inPopulated csar option
|
||||
- clearance csar attribute
|
||||
- maxTries csar attribute
|
||||
|
||||
INTEGRATES AUTOMATICALLY WITH playerScore IF INSTALLED
|
||||
INTEGRATES WITH LIMITED AIRFRAMES IF INSTALLED
|
||||
INTEGRATES AUTOMATICALLY WITH playerScore
|
||||
INTEGRATES WITH LIMITED AIRFRAMES
|
||||
INTEGRATES AUTOMATICALLY WITH SCRIBE
|
||||
|
||||
--]]--
|
||||
-- modules that need to be loaded BEFORE I run
|
||||
@ -297,17 +302,26 @@ function csarManager:onEvent(event)
|
||||
csarManager.heloLanded(theUnit)
|
||||
end
|
||||
|
||||
if ID == 3 then -- take off
|
||||
if ID == 3 or ID == 55 then -- take off, postponed take-off
|
||||
csarManager.heloDeparted(theUnit)
|
||||
end
|
||||
|
||||
if ID == 5 then -- crash
|
||||
csarManager.heloCrashed(theUnit)
|
||||
-- note: maybe not called in network missions.
|
||||
-- correction: is called in 2.9
|
||||
end
|
||||
|
||||
if ID == 15 then -- player helicopter birth
|
||||
-- we need to set up comms for this unit
|
||||
csarManager.setCommsMenu(theUnit)
|
||||
-- we also need to make sure that there are no
|
||||
-- more troopsOnBoard
|
||||
|
||||
local conf = csarManager.getUnitConfig(theUnit)
|
||||
conf.unit = theUnit
|
||||
conf.troopsOnBoard = {}
|
||||
|
||||
end
|
||||
|
||||
end
|
||||
@ -421,7 +435,7 @@ function csarManager.heloLanded(theUnit)
|
||||
end
|
||||
|
||||
for idx, msn in pairs(conf.troopsOnBoard) do
|
||||
-- each troopsOnboard is actually the
|
||||
-- each troopsOnBoard is actually the
|
||||
-- csar mission that I picked up
|
||||
csarManager.successMission(myName, base.name, msn)
|
||||
end
|
||||
@ -792,7 +806,7 @@ function csarManager.doStatusCarrying(args)
|
||||
local evacMission = conf.troopsOnBoard[i]
|
||||
report = report .. "\n".. i .. ") " .. evacMission.name
|
||||
if evacMission.expires then
|
||||
delta = math.floor ((mission.expires - now) / 60)
|
||||
delta = math.floor ((evacMission.expires - now) / 60)
|
||||
if delta > 20 then
|
||||
report = report .. " is hurt but stable"
|
||||
elseif delta > 10 then
|
||||
@ -1207,6 +1221,9 @@ function csarManager.createCSARMissionFromZone(theZone)
|
||||
if theZone.rndLoc then mPoint = theZone:createRandomPointInZone() end
|
||||
if theZone.onRoad then
|
||||
mPoint.x, mPoint.z = land.getClosestPointOnRoads('roads',mPoint.x, mPoint.z)
|
||||
elseif theZone.inPopulated then
|
||||
local aPoint = theZone:createRandomPointInPopulatedZone(theZone.clearance, theZone.maxTries)
|
||||
mPoint = aPoint -- safety in case we need to mod aPoint
|
||||
end
|
||||
local theMission = csarManager.createCSARMissionData(
|
||||
mPoint,
|
||||
@ -1218,6 +1235,7 @@ function csarManager.createCSARMissionFromZone(theZone)
|
||||
theZone.csarMapMarker, -- mapMarker
|
||||
0.1, --theZone.radius) -- radius
|
||||
nil) -- parashoo unit
|
||||
theMission.inPopulated = theZone.inPopulated -- transfer for csarFX
|
||||
return theMission
|
||||
end
|
||||
|
||||
@ -1338,12 +1356,22 @@ function csarManager.readCSARZone(theZone)
|
||||
theZone.triggerMethod = theZone:getStringFromZoneProperty("triggerMethod", "change")
|
||||
theZone.rndLoc = theZone:getBoolFromZoneProperty("rndLoc", true)
|
||||
theZone.onRoad = theZone:getBoolFromZoneProperty("onRoad", false)
|
||||
theZone.inPopulated = theZone:getBoolFromZoneProperty("inPopulated", false)
|
||||
theZone.clearance = theZone:getNumberFromZoneProperty("clearance", 10)
|
||||
theZone.maxTries = theZone:getNumberFromZoneProperty("maxTries", 20)
|
||||
|
||||
if theZone.onRoad and theZone.inPopulated then
|
||||
trigger.action.outText("warning: competing 'onRoad' and 'inPopulated' attributes in zone <" .. theZone.name .. ">. Using 'onRoad'.", 30)
|
||||
end
|
||||
|
||||
if (not deferred) then
|
||||
local mPoint = theZone:getPoint()
|
||||
if theZone.rndLoc then mPoint = theZone:createRandomPointInZone() end
|
||||
if theZone.onRoad then
|
||||
mPoint.x, mPoint.z = land.getClosestPointOnRoads('roads',mPoint.x, mPoint.z)
|
||||
elseif theZone.inPopulated then
|
||||
local aPoint = theZone:createRandomPointInPopulatedZone(theZone.clearance, theZone.maxTries)
|
||||
mPoint = aPoint -- safety in case we need to mod aPoint
|
||||
end
|
||||
local theMission = csarManager.createCSARMissionData(
|
||||
mPoint,
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
dcsCommon = {}
|
||||
dcsCommon.version = "3.0.2"
|
||||
dcsCommon.version = "3.0.3"
|
||||
--[[-- VERSION HISTORY
|
||||
3.0.0 - removed bad bug in stringStartsWith, only relevant if caseSensitive is false
|
||||
- point2text new intsOnly option
|
||||
@ -11,6 +11,8 @@ dcsCommon.version = "3.0.2"
|
||||
3.0.1 - clone: better handling of string type
|
||||
3.0.2 - new getPlayerUnit()
|
||||
3.0.3 - createStaticObjectForCoalitionInRandomRing() returns x and z
|
||||
- isTroopCarrier() also supports 'helo' keyword
|
||||
- new createTakeOffFromGroundRoutePointData()
|
||||
--]]--
|
||||
|
||||
-- dcsCommon is a library of common lua functions
|
||||
@ -1285,6 +1287,25 @@ dcsCommon.version = "3.0.2"
|
||||
return route
|
||||
end
|
||||
|
||||
function dcsCommon.createTakeOffFromGroundRoutePointData(pt, isHot) -- vec 3!
|
||||
if not pt then return nil end
|
||||
|
||||
local rp = {}
|
||||
rp.x = pt.x
|
||||
rp.y = pt.z
|
||||
rp.alt = pt.y
|
||||
if isHot then
|
||||
rp.action = "From Ground Area Hot"
|
||||
rp.type = "TakeOffGroundHot"
|
||||
else
|
||||
rp.action = "From Ground Area" -- add " Hot" if hot
|
||||
rp.type = "TakeOffGround" -- add "Hot" (NO blank) if hot
|
||||
end
|
||||
rp.speed = 10 -- that's 36 km/h
|
||||
rp.alt_type = "BARO"
|
||||
return rp
|
||||
end
|
||||
|
||||
function dcsCommon.createTakeOffFromParkingRoutePointData(aerodrome)
|
||||
if not aerodrome then return nil end
|
||||
|
||||
@ -2700,7 +2721,16 @@ end
|
||||
|
||||
function dcsCommon.isTroopCarrier(theUnit, carriers)
|
||||
-- return true if conf can carry troups
|
||||
if not theUnit then return false end
|
||||
if not theUnit then return false end
|
||||
|
||||
-- see if carriers contains "helo" and theUnit is a helo
|
||||
if dcsCommon.arrayContainsString(carriers, "helo") or dcsCommon.arrayContainsString(carriers, "helos")then
|
||||
local grp = theUnit:getGroup()
|
||||
if grp:getCategory() == 1 then -- NOT category bug prone, is a group check
|
||||
return true
|
||||
end
|
||||
end
|
||||
|
||||
local uType = theUnit:getTypeName()
|
||||
return dcsCommon.isTroopCarrierType(uType, carriers)
|
||||
end
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
scribe = {}
|
||||
scribe.version = "1.0.0"
|
||||
scribe.version = "1.0.1"
|
||||
scribe.requiredLibs = {
|
||||
"dcsCommon", -- always
|
||||
"cfxZones", -- Zones, of course
|
||||
@ -9,7 +9,8 @@ scribe.requiredLibs = {
|
||||
--[[--
|
||||
Player statistics package
|
||||
VERSION HISTORY
|
||||
1.0.0 Initial Version
|
||||
1.0.0 Initial Version
|
||||
1.0.1 postponed land, postponed takeoff, unit_lost
|
||||
--]]--
|
||||
scribe.verbose = true
|
||||
scribe.db = {} -- indexed by player name
|
||||
@ -234,8 +235,9 @@ function scribe.playerEjected(playerName)
|
||||
end
|
||||
|
||||
function scribe.playerDied(playerName)
|
||||
-- trigger.action.outText("+++scb: player <" .. playerName .. "> DEAD event, handing off to crashS", 30)
|
||||
-- counts as a crash
|
||||
if scribe.verbose then
|
||||
trigger.action.outText("+++scb: player <" .. playerName .. "> DEAD event, handing off to crashS", 30)
|
||||
end -- counts as a crash
|
||||
local theEntry = scribe.getPlayerNamed(playerName)
|
||||
if not theEntry.isActive then
|
||||
if scribe.verbose then
|
||||
@ -365,7 +367,9 @@ function scribe:onEvent(theEvent)
|
||||
scribe.playerUnits[uName] = playerName -- for crash helo detection
|
||||
end
|
||||
|
||||
if theEvent.id == 8 or theEvent.id == 9 then -- dead, pilot_dead
|
||||
if theEvent.id == 8 or
|
||||
theEvent.id == 9 or
|
||||
theEvent.id == 30 then -- dead, pilot_dead, unit_lost
|
||||
scribe.playerDied(playerName)
|
||||
end
|
||||
|
||||
@ -373,15 +377,17 @@ function scribe:onEvent(theEvent)
|
||||
scribe.playerEjected(playerName)
|
||||
end
|
||||
|
||||
if theEvent.id == 5 then -- crash
|
||||
if theEvent.id == 5 then -- crash, maybe not called in MP
|
||||
scribe.playerCrashed(playerName)
|
||||
end
|
||||
|
||||
if theEvent.id == 4 then -- landed
|
||||
if theEvent.id == 4 or -- landed
|
||||
theEvent.id == 56 then
|
||||
scribe.playerLanded(playerName)
|
||||
end
|
||||
|
||||
if theEvent.id == 3 then -- take-off
|
||||
if theEvent.id == 3 or -- take-off
|
||||
theEvent.id == 55 then -- postponed take-off
|
||||
scribe.playerDeparted(playerName)
|
||||
-- trigger.action.outText("departure detected", 30)
|
||||
end
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
valet = {}
|
||||
valet.version = "1.0.2"
|
||||
valet.version = "1.0.3"
|
||||
valet.verbose = false
|
||||
valet.requiredLibs = {
|
||||
"dcsCommon", -- always
|
||||
@ -12,6 +12,7 @@ valet.valets = {}
|
||||
1.0.0 - initial version
|
||||
1.0.1 - typos in verbosity corrected
|
||||
1.0.2 - also scan birth events
|
||||
1.0.3 - outSoundFile now working correctly
|
||||
|
||||
--]]--
|
||||
|
||||
@ -41,7 +42,7 @@ function valet.createValetWithZone(theZone)
|
||||
theZone.firstInSoundFile = cfxZones.getStringFromZoneProperty(theZone, "firstInSoundFile", "<none>")
|
||||
end
|
||||
|
||||
theZone.outSoundFile = cfxZones.getStringFromZoneProperty(theZone, "outSoundFile", "<none>")
|
||||
theZone.outSoundFile = cfxZones.getStringFromZoneProperty(theZone, "outSoundFile", theZone.inSoundFile)
|
||||
|
||||
-- greeting/first greeting, handle if "" = no text out
|
||||
if cfxZones.hasProperty(theZone, "firstGreeting") then
|
||||
@ -195,7 +196,7 @@ function valet.sendOffPlayer(playerName, aPlayerUnit, theZone, theDesc)
|
||||
-- player has left the area
|
||||
local msg = theZone.goodbye or ""
|
||||
local dur = theZone.duration
|
||||
local fileName = "l10n/DEFAULT/" .. theZone.inSoundFile
|
||||
local fileName = "l10n/DEFAULT/" .. theZone.outSoundFile
|
||||
local ID = aPlayerUnit:getID()
|
||||
|
||||
if msg == "<none>" then msg = "" end
|
||||
|
||||
Binary file not shown.
BIN
tutorial & demo missions/demo - civvy heli.miz
Normal file
BIN
tutorial & demo missions/demo - civvy heli.miz
Normal file
Binary file not shown.
Loading…
x
Reference in New Issue
Block a user