Version 1.3.0

FlareZone
PlayerZone
shallows
This commit is contained in:
Christian Franz 2023-05-11 09:16:58 +02:00
parent 97d3c10540
commit 83f9478f86
19 changed files with 623 additions and 38 deletions

Binary file not shown.

Binary file not shown.

View File

@ -1,5 +1,5 @@
asw = {} asw = {}
asw.version = "1.0.0" asw.version = "1.0.1"
asw.verbose = false asw.verbose = false
asw.requiredLibs = { asw.requiredLibs = {
"dcsCommon", -- always "dcsCommon", -- always
@ -15,6 +15,7 @@ asw.fixes = {} -- all subs that we have a fix on. indexed by sub name
--[[-- --[[--
Version History Version History
1.0.0 - initial version 1.0.0 - initial version
1.0.1 - integration with playerScore
--]]-- --]]--
@ -36,6 +37,10 @@ function asw.createTorpedoForUnit(theUnit)
local t = asw.createTorpedo() local t = asw.createTorpedo()
t.coalition = theUnit:getCoalition() t.coalition = theUnit:getCoalition()
t.point = theUnit:getPoint() t.point = theUnit:getPoint()
t.droppedBy = theUnit
if theUnit.getPlayerName and theUnit:getPlayerName() ~= nil then
t.playerName = theUnit:getPlayerName()
end
return t return t
end end
@ -716,6 +721,7 @@ function asw.updateTorpedo(theTorpedo, allSubs)
end end
if dist < 1.2 * displacement then if dist < 1.2 * displacement then
theTorpedo.target = theSub
theTorpedo.state = 99 -- go boom theTorpedo.state = 99 -- go boom
end end
markTorpedo(theTorpedo) markTorpedo(theTorpedo)
@ -762,10 +768,26 @@ function asw.updateTorpedo(theTorpedo, allSubs)
return true return true
elseif theTorpedo.state == 99 then -- go boom elseif theTorpedo.state == 99 then -- go boom
if Unit.isExist(theTorpedo.target) then if Unit.isExist(theTorpedo.target) then
if asw.verbose then
trigger.action.outText("99 torpedoes have target", 30)
end
-- interface to playerScore
if cfxPlayerScore then
asw.doScore(theTorpedo)
else
if asw.verbose then
trigger.action.outText("No playerScore present", 30)
end
end
Unit.destroy(theTorpedo.target) Unit.destroy(theTorpedo.target)
else
if asw.verbose then
trigger.action.outText("t99 no target exist", 30)
end
end end
-- impact! -- impact!
trigger.action.outTextForCoalition(theTorpedo.coalition, "Impact for " .. theTorpedo.name .. "! We have confirmed hit on submerged contact!", 30) trigger.action.outTextForCoalition(theTorpedo.coalition, "Impact for " .. theTorpedo.name .. "! We have confirmed hit on submerged contact!", 30)
if theTorpedo.coalition == 1 then if theTorpedo.coalition == 1 then
if asw.redKill then if asw.redKill then
cfxZones.pollFlag(asw.redKill, asw.method, asw) cfxZones.pollFlag(asw.redKill, asw.method, asw)
@ -796,6 +818,86 @@ function asw.updateTorpedo(theTorpedo, allSubs)
return true return true
end end
-- PlayerScore interface
function processFeat(inMsg, playerUnit, victim, timeFormat)
if not inMsg then return "<nil inMsg>" end
-- replace <t> with current mission time HMS
local absSecs = timer.getAbsTime()-- + env.mission.start_time
while absSecs > 86400 do
absSecs = absSecs - 86400 -- subtract out all days
end
if not timeFormat then timeFormat = "<:h>:<:m>:<:s>" end
-- <t>
local timeString = dcsCommon.processHMS(timeFormat, absSecs)
local outMsg = inMsg:gsub("<t>", timeString)
-- <n>
outMsg = dcsCommon.processStringWildcards(outMsg) -- <n>
-- <unit, type, player etc>
outMsg = cfxPlayerScore.preprocessWildcards(outMsg, playerUnit, victim)
return outMsg
end
function asw.doScore(theTorpedo)
if asw.verbose then
trigger.action.outText("asw: enter doScore", 30)
end
-- make sure that this is a player-dropped torpedo
if not theTorpedo then
if asw.verbose then
trigger.action.outText("no torpedo", 30)
end
return
end
local theUnit = theTorpedo.target
if not theTorpedo.playerName then
if asw.verbose then
trigger.action.outText("no torpedo", 30)
end
return
end
local pName = theTorpedo.playerName
-- make sure that the player's original unit still exists
if not (theTorpedo.droppedBy and Unit.isExist(theTorpedo.droppedBy)) then
if asw.verbose then
trigger.action.outText("torpedo dropper dead", 30)
end
return -- torpedo-dropping unit did not survive
end
local fratricide = (theTorpedo.coalition == theUnit:getCoalition())
if fratricide then
if asw.verbose then
trigger.action.outText("+++asw: fratricide detected", 30)
end
end
if asw.killScore > 0 then
-- award score
local score = asw.killScore
if fratricide then score = -1 * score end
cfxPlayerScore.logKillForPlayer(pName, theUnit)
cfxPlayerScore.awardScoreTo(theTorpedo.coalition, score, pName)
if asw.verbose then
trigger.action.outText("updated score (" .. score .. ") for player <" .. pName .. ">", 30)
end
else
if asw.verbose then
trigger.action.outText("no score num defined", 30)
end
end
if asw.killFeat and (not fratricide) then
-- we treat killFeat as boolean
local theFeat = "Killed type <type> submerged vessel <unit> at <t>"
theFeat = processFeat(theFeat, theTorpedo.droppedBy, theUnit)
cfxPlayerScore.logFeatForPlayer(pName, theFeat)
else
if asw.verbose then
trigger.action.outText("no feat defined or fratricide", 30)
end
end
end
-- --
-- MAIN UPDATE -- MAIN UPDATE
-- --
@ -940,10 +1042,17 @@ function asw.readConfigZone()
asw.smokeColor = cfxZones.getSmokeColorStringFromZoneProperty(theZone, "smokeColor", "red") asw.smokeColor = cfxZones.getSmokeColorStringFromZoneProperty(theZone, "smokeColor", "red")
asw.smokeColor = dcsCommon.smokeColor2Num(asw.smokeColor) asw.smokeColor = dcsCommon.smokeColor2Num(asw.smokeColor)
asw.killScore = cfxZones.getNumberFromZoneProperty(theZone, "killScore", 0)
if cfxZones.hasProperty(theZone, "killFeat") then
asw.killFeat = cfxZones.getStringFromZoneProperty(theZone, "killFeat", "Sub Kill")
end
if asw.verbose then if asw.verbose then
trigger.action.outText("+++asw: read config", 30) trigger.action.outText("+++asw: read config", 30)
end end
end end
function asw.start() function asw.start()

View File

@ -1,5 +1,5 @@
aswGUI = {} aswGUI = {}
aswGUI.version = "1.0.1" aswGUI.version = "1.0.2"
aswGUI.verbose = false aswGUI.verbose = false
aswGUI.requiredLibs = { aswGUI.requiredLibs = {
"dcsCommon", -- always "dcsCommon", -- always
@ -12,6 +12,7 @@ aswGUI.requiredLibs = {
Version History Version History
1.0.0 - initial version 1.0.0 - initial version
1.0.1 - env.info clean-up, verbosity clean-up 1.0.1 - env.info clean-up, verbosity clean-up
1.0.2 - late start capability
--]]-- --]]--
@ -524,6 +525,22 @@ function aswGUI:onEvent(theEvent)
end end
end end
function aswGUI.processPlayerUnit(theUnit)
local name = theUnit:getName()
local conf = aswGUI.aswCraft[name]
if not conf then
-- let's init it
conf = aswGUI.initUnit(name)
aswGUI.aswCraft[name] = conf
else
aswGUI.resetConf(conf)
end
aswGUI.setMenuForUnit(theUnit)
if aswGUI.verbose then
trigger.action.outText("aswG: set up player <" .. theUnit:getPlayerName() .. "> in <" .. name .. ">", 30)
end
end
-- --
-- Config & start -- Config & start
-- --
@ -571,7 +588,10 @@ function aswGUI.start()
-- subscribe to world events -- subscribe to world events
world.addEventHandler(aswGUI) world.addEventHandler(aswGUI)
-- install menus in all existing players
dcsCommon.iteratePlayers(aswGUI.processPlayerUnit)
-- say Hi -- say Hi
trigger.action.outText("cfx ASW GUI v" .. aswGUI.version .. " started.", 30) trigger.action.outText("cfx ASW GUI v" .. aswGUI.version .. " started.", 30)
return true return true

View File

@ -1,5 +1,5 @@
cfxPlayerScore = {} cfxPlayerScore = {}
cfxPlayerScore.version = "2.0.0" cfxPlayerScore.version = "2.0.1"
cfxPlayerScore.name = "cfxPlayerScore" -- compatibility with flag bangers cfxPlayerScore.name = "cfxPlayerScore" -- compatibility with flag bangers
cfxPlayerScore.badSound = "Death BRASS.wav" cfxPlayerScore.badSound = "Death BRASS.wav"
cfxPlayerScore.scoreSound = "Quest Snare 3.wav" cfxPlayerScore.scoreSound = "Quest Snare 3.wav"
@ -70,7 +70,9 @@ cfxPlayerScore.firstSave = true -- to force overwrite
- pkMod attribute - pkMod attribute
- pvp feat - pvp feat
- immediate awarding of all negative scores, even if deferred - immediate awarding of all negative scores, even if deferred
2.0.1 - corrected access to nowString()
- more robust config reading
--]]-- --]]--
cfxPlayerScore.requiredLibs = { cfxPlayerScore.requiredLibs = {
@ -1278,7 +1280,7 @@ function cfxPlayerScore.saveScores(theText, name)
end end
-- prepend time for score -- prepend time for score
theText = "\n\n====== Mission Time: " .. dcsCommon.nowstring() .. "\n" .. theText theText = "\n\n====== Mission Time: " .. dcsCommon.nowString() .. "\n" .. theText
end end
if persistence.saveText(theText, name, shared, append) then if persistence.saveText(theText, name, shared, append) then
@ -1360,10 +1362,11 @@ function cfxPlayerScore.start()
local theZone = cfxZones.getZoneByName("playerScoreConfig") local theZone = cfxZones.getZoneByName("playerScoreConfig")
if not theZone then if not theZone then
trigger.action.outText("+++scr: no config!", 30) trigger.action.outText("+++scr: no config!", 30)
else theZone = cfxZones.createSimpleZone("playerScoreConfig")
cfxPlayerScore.readConfigZone(theZone)
trigger.action.outText("+++scr: read config", 30)
end end
cfxPlayerScore.readConfigZone(theZone)
-- trigger.action.outText("+++scr: read config", 30)
-- read all scoreSafe zones -- read all scoreSafe zones
local safeZones = cfxZones.zonesWithProperty("scoreSafe") local safeZones = cfxZones.zonesWithProperty("scoreSafe")

View File

@ -1,14 +1,15 @@
cfxPlayerScoreUI = {} cfxPlayerScoreUI = {}
cfxPlayerScoreUI.version = "2.0.0" cfxPlayerScoreUI.version = "2.0.1"
cfxPlayerScoreUI.verbose = false cfxPlayerScoreUI.verbose = false
--[[-- VERSION HISTORY --[[-- VERSION HISTORY
- 1.0.2 - initial version - 1.0.2 - initial version
- 1.0.3 - module check - 1.0.3 - module check
- 2.0.0 - removed cfxPlayer dependency, handles own commands - 2.0.0 - removed cfxPlayer dependency, handles own commands
- 2.0.1 - late start capability
--]]-- --]]--
cfxPlayerScoreUI.rootCommands = {} -- by unit's group name, for player aircraft cfxPlayerScoreUI.rootCommands = {} -- by unit's GROUP name, for player aircraft
-- redirect: avoid the debug environ of missionCommand -- redirect: avoid the debug environ of missionCommand
function cfxPlayerScoreUI.redirectCommandX(args) function cfxPlayerScoreUI.redirectCommandX(args)
@ -23,7 +24,7 @@ function cfxPlayerScoreUI.doCommandX(args)
local gid = theGroup:getID() local gid = theGroup:getID()
if not cfxPlayerScore.scoreTextForPlayerNamed then if not cfxPlayerScore.scoreTextForPlayerNamed then
trigger.action.outText("***pSGui: CANNOT FIND PlayerScore MODULE", 30) trigger.action.outText("***pSGUI: CANNOT FIND PlayerScore MODULE", 30)
return return
end end
local desc = cfxPlayerScore.scoreTextForPlayerNamed(playerName) local desc = cfxPlayerScore.scoreTextForPlayerNamed(playerName)
@ -35,10 +36,7 @@ end
-- event handling: we are only interested in birth events -- event handling: we are only interested in birth events
-- for player aircraft -- for player aircraft
-- --
function cfxPlayerScoreUI:onEvent(event) function cfxPlayerScore.processPlayerUnit(theUnit)
if event.id ~= 15 then return end -- only birth
if not event.initiator then return end -- no initiator, no joy
local theUnit = event.initiator
if not theUnit.getPlayerName then return end -- no player name, bye! if not theUnit.getPlayerName then return end -- no player name, bye!
local playerName = theUnit:getPlayerName() local playerName = theUnit:getPlayerName()
if not playerName then return end if not playerName then return end
@ -61,13 +59,7 @@ function cfxPlayerScoreUI:onEvent(event)
-- we need to install a group menu item for scores. -- we need to install a group menu item for scores.
-- will persist through death -- will persist through death
local commandTxt = "Show Score / Kills" local commandTxt = "Show Score / Kills"
local theCommand = missionCommands.addCommandForGroup( local theCommand = missionCommands.addCommandForGroup(gid, commandTxt, nil, cfxPlayerScoreUI.redirectCommandX, {groupName, playerName, "score"} )
gid,
commandTxt,
nil, -- root level
cfxPlayerScoreUI.redirectCommandX,
{groupName, playerName, "score"}
)
cfxPlayerScoreUI.rootCommands[groupName] = theCommand cfxPlayerScoreUI.rootCommands[groupName] = theCommand
if cfxPlayerScoreUI.verbose then if cfxPlayerScoreUI.verbose then
@ -75,13 +67,21 @@ function cfxPlayerScoreUI:onEvent(event)
end end
end end
function cfxPlayerScoreUI:onEvent(event)
if event.id ~= 15 then return end -- only birth
if not event.initiator then return end -- no initiator, no joy
local theUnit = event.initiator
cfxPlayerScore.processPlayerUnit(theUnit)
end
-- --
-- Start -- Start
-- --
function cfxPlayerScoreUI.start() function cfxPlayerScoreUI.start()
-- install the event handler for new player planes -- install the event handler for new player planes
world.addEventHandler(cfxPlayerScoreUI) world.addEventHandler(cfxPlayerScoreUI)
-- process all existing players (late start)
dcsCommon.iteratePlayers(cfxPlayerScore.processPlayerUnit)
trigger.action.outText("cf/x cfxPlayerScoreUI v" .. cfxPlayerScoreUI.version .. " started", 30) trigger.action.outText("cf/x cfxPlayerScoreUI v" .. cfxPlayerScoreUI.version .. " started", 30)
return true return true
end end

View File

@ -1,5 +1,5 @@
cfxSmokeZone = {} cfxSmokeZone = {}
cfxSmokeZone.version = "1.1.2" cfxSmokeZone.version = "1.1.3"
cfxSmokeZone.requiredLibs = { cfxSmokeZone.requiredLibs = {
"dcsCommon", -- always "dcsCommon", -- always
"cfxZones", -- Zones, of course "cfxZones", -- Zones, of course
@ -18,6 +18,7 @@ cfxSmokeZone.requiredLibs = {
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 1.1.2 - 'agl', 'alt' synonymous for altitude to keep in line with fireFX
1.1.3 - corrected smokeTriggerMethod in zone definition
--]]-- --]]--
cfxSmokeZone.smokeZones = {} cfxSmokeZone.smokeZones = {}
@ -67,7 +68,7 @@ function cfxSmokeZone.processSmokeZone(aZone)
aZone.smokeTriggerMethod = cfxZones.getStringFromZoneProperty(aZone, "triggerMethod", "change") aZone.smokeTriggerMethod = cfxZones.getStringFromZoneProperty(aZone, "triggerMethod", "change")
if cfxZones.hasProperty(aZone, "smokeTriggerMethod") then if cfxZones.hasProperty(aZone, "smokeTriggerMethod") then
aZone.delayTriggerMethod = cfxZones.getStringFromZoneProperty(aZone, "smokeTriggerMethod", "change") aZone.smokeTriggerMethod = cfxZones.getStringFromZoneProperty(aZone, "smokeTriggerMethod", "change")
end end
end end

View File

@ -1,5 +1,5 @@
cfxSpawnZones = {} cfxSpawnZones = {}
cfxSpawnZones.version = "1.7.4" cfxSpawnZones.version = "1.7.5"
cfxSpawnZones.requiredLibs = { cfxSpawnZones.requiredLibs = {
"dcsCommon", -- common is of course needed for everything "dcsCommon", -- common is of course needed for everything
-- pretty stupid to check for this since we -- pretty stupid to check for this since we
@ -66,6 +66,8 @@ cfxSpawnZones.spawnedGroups = {}
1.7.2 - baseName now can can be set to zone name by issuing "*" 1.7.2 - baseName now can can be set to zone name by issuing "*"
1.7.3 - ability to hand off to delicates, useDelicates attribute 1.7.3 - ability to hand off to delicates, useDelicates attribute
1.7.4 - wait-attackZone fixes 1.7.4 - wait-attackZone fixes
1.7.5 - improved verbosity on spawning
- getRequestableSpawnersInRange() ignores height for distance
- types - type strings, comma separated - types - type strings, comma separated
@ -204,6 +206,9 @@ function cfxSpawnZones.createSpawner(inZone)
theSpawner.requestable = cfxZones.getBoolFromZoneProperty(inZone, "requestable", false) theSpawner.requestable = cfxZones.getBoolFromZoneProperty(inZone, "requestable", false)
if theSpawner.requestable then if theSpawner.requestable then
theSpawner.paused = true theSpawner.paused = true
if inZone.verbose or cfxSpawnZones.verbose then
trigger.action.outText("+++spwn: spawner <" .. inZone.name .. "> paused: requestable enabled", 30)
end
end end
if cfxZones.hasProperty(inZone, "target") then if cfxZones.hasProperty(inZone, "target") then
theSpawner.target = cfxZones.getStringFromZoneProperty(inZone, "target", "") theSpawner.target = cfxZones.getStringFromZoneProperty(inZone, "target", "")
@ -248,7 +253,7 @@ function cfxSpawnZones.getRequestableSpawnersInRange(aPoint, aRange, aSide)
for aZone, aSpawner in pairs(cfxSpawnZones.allSpawners) do for aZone, aSpawner in pairs(cfxSpawnZones.allSpawners) do
-- iterate all zones and collect those that match -- iterate all zones and collect those that match
local hasMatch = true local hasMatch = true
local delta = dcsCommon.dist(aPoint, aZone.point) local delta = dcsCommon.distFlat(aPoint, cfxZones.getPoint(aZone))
if delta>aRange then hasMatch = false end if delta>aRange then hasMatch = false end
if aSide ~= 0 then if aSide ~= 0 then
-- check if side is correct for owned zone -- check if side is correct for owned zone
@ -333,9 +338,20 @@ function cfxSpawnZones.spawnWithSpawner(aSpawner)
theCoalition, theCoalition,
aSpawner.baseName .. "-" .. aSpawner.count, -- must be unique aSpawner.baseName .. "-" .. aSpawner.count, -- must be unique
aSpawner.zone, aSpawner.zone,
unitTypes, unitTypes,
aSpawner.formation, aSpawner.formation,
aSpawner.heading) aSpawner.heading)
if cfxSpawnZones.verbose or theZone.verbose then
-- check created group size versus requested size
trigger.action.outText("+++spwn: created <" .. theGroup:getSize() .. "> units, requested <" .. #unitTypes .. "> units, formation <" .. aSpawner.formation .. ">", 30)
trigger.action.outText("+++spwn: zone <" .. theZone.name .. ">center at <" .. dcsCommon.point2text(p) .. ">", 30)
local allUnits = theGroup:getUnits()
for idx, myUnit in pairs (allUnits) do
local pos = myUnit:getPoint()
trigger.action.outText("unit <" .. myUnit:getName() .. "> at " .. dcsCommon.point2text(pos), 30)
end
end
aSpawner.theSpawn = theGroup aSpawner.theSpawn = theGroup
aSpawner.count = aSpawner.count + 1 aSpawner.count = aSpawner.count + 1

View File

@ -1,5 +1,5 @@
cfxZones = {} cfxZones = {}
cfxZones.version = "3.0.8" cfxZones.version = "3.0.9"
-- 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
@ -126,6 +126,7 @@ cfxZones.version = "3.0.8"
- new createSimpleQuadZone() - new createSimpleQuadZone()
- 3.0.7 - getPoint() can also get land y when passing true as second param - 3.0.7 - getPoint() can also get land y when passing true as second param
- 3.0.8 - new cfxZones.pointInOneOfZones(thePoint, zoneArray, useOrig) - 3.0.8 - new cfxZones.pointInOneOfZones(thePoint, zoneArray, useOrig)
- 3.0.9 - new getFlareColorStringFromZoneProperty()
--]]-- --]]--
cfxZones.verbose = false cfxZones.verbose = false
@ -2241,6 +2242,30 @@ function cfxZones.getSmokeColorStringFromZoneProperty(theZone, theProperty, defa
return default return default
end end
function cfxZones.getFlareColorStringFromZoneProperty(theZone, theProperty, default) -- smoke as 'red', 'green', or 1..5
if not default then default = "red" end
local s = cfxZones.getStringFromZoneProperty(theZone, theProperty, default)
s = s:lower()
s = dcsCommon.trim(s)
-- check numbers
if (s == "rnd") then return "random" end
if (s == "0") then return "green" end
if (s == "1") then return "red" end
if (s == "2") then return "white" end
if (s == "3") then return "yellow" end
if (s == "-1") then return "random" end
if s == "green" or
s == "red" or
s == "white" or
s == "yellow" or
s == "random" then
return s end
return default
end
-- --
-- Zone-based wildcard processing -- Zone-based wildcard processing
-- --

View File

@ -1,5 +1,5 @@
csarManager = {} csarManager = {}
csarManager.version = "2.2.5" csarManager.version = "2.2.6"
csarManager.verbose = false csarManager.verbose = false
csarManager.ups = 1 csarManager.ups = 1
@ -63,6 +63,8 @@ csarManager.ups = 1
- 2.2.4 - CSAR attribute value defaults name - 2.2.4 - CSAR attribute value defaults name
- start? attribute for CSAR as startCSAR? synonym - start? attribute for CSAR as startCSAR? synonym
- 2.2.5 - manual freq for CSAR was off by a factor of 10 - Corrected - 2.2.5 - manual freq for CSAR was off by a factor of 10 - Corrected
- 2.2.6 - useFlare, now also launches a flare in addition to smoke
- zone testing uses getPoint for zones, supports moving csar zones
INTEGRATES AUTOMATICALLY WITH playerScore IF INSTALLED INTEGRATES AUTOMATICALLY WITH playerScore IF INSTALLED
@ -1028,6 +1030,18 @@ function csarManager.updateCSARMissions()
csarManager.openMissions = newMissions -- this is the new batch csarManager.openMissions = newMissions -- this is the new batch
end end
function csarManager.launchFlare(args)
local color = args.color
if color < 0 then color = math.random(4) - 1 end
local loc = args.loc -- with height
if csarManager.verbose then
trigger.action.outText("+++csarM: launching flare, c = " .. color .. " (" .. dcsCommon.flareColor2Text(color) .. ")", 30)
end
trigger.action.outTextForGroup(args.uID, "Launching flare!", 30)
loc.y = loc.y + 3 -- launch 3 meters above ground
trigger.action.signalFlare(loc, color, 0)
end
function csarManager.update() -- every second function csarManager.update() -- every second
-- schedule next invocation -- schedule next invocation
timer.scheduleFunction(csarManager.update, {}, timer.getTime() + 1/csarManager.ups) timer.scheduleFunction(csarManager.update, {}, timer.getTime() + 1/csarManager.ups)
@ -1053,7 +1067,8 @@ function csarManager.update() -- every second
-- enough to trigger comms -- enough to trigger comms
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 mp = cfxZones.getPoint(csarMission.zone, true)
local d = dcsCommon.distFlat(uPoint, mp)
if ((uSide == csarMission.side) or (csarMission.side == 0) ) if ((uSide == csarMission.side) or (csarMission.side == 0) )
and (d < csarManager.rescueTriggerRange) then 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
@ -1065,7 +1080,21 @@ function csarManager.update() -- every second
local oclock = dcsCommon.clockPositionOfARelativeToB(csarMission.zone.point, uPoint, ownHeading) .. " o'clock" local oclock = dcsCommon.clockPositionOfARelativeToB(csarMission.zone.point, uPoint, ownHeading) .. " o'clock"
local msg = "\n" .. uName ..", " .. csarMission.name .. ". We can hear you, check your " .. oclock local msg = "\n" .. uName ..", " .. csarMission.name .. ". We can hear you, check your " .. oclock
if csarManager.useSmoke then msg = msg .. " - popping smoke" end if csarManager.useSmoke then msg = msg .. " - popping smoke" end
if csarManager.useFlare then
if csarManager.useSmoke then
msg = msg .. " and will launch flare in a few seconds"
else
msg = msg .. " - preparing flare"
end
-- schedule flare launch in 5-10 seconds
local args = {}
args.loc = mp
args.color = csarManager.flareColor
args.uID = uID
timer.scheduleFunction(csarManager.launchFlare, args, timer.getTime() + math.random(5))
end
msg = msg .. "." msg = msg .. "."
if csarMission.isHot then if csarMission.isHot then
msg = msg .. " Be advised: LZ is hot." msg = msg .. " Be advised: LZ is hot."
end end
@ -1394,6 +1423,10 @@ function csarManager.readConfigZone()
csarManager.smokeColor = cfxZones.getSmokeColorStringFromZoneProperty(theZone, "smokeColor", "blue") csarManager.smokeColor = cfxZones.getSmokeColorStringFromZoneProperty(theZone, "smokeColor", "blue")
csarManager.smokeColor = dcsCommon.smokeColor2Num(csarManager.smokeColor) csarManager.smokeColor = dcsCommon.smokeColor2Num(csarManager.smokeColor)
csarManager.useFlare = cfxZones.getBoolFromZoneProperty(theZone, "useFlare", true)
csarManager.flareColor = cfxZones.getFlareColorStringFromZoneProperty(theZone, "flareColor", "red")
csarManager.flareColor = dcsCommon.flareColor2Num(csarManager.flareColor)
if cfxZones.hasProperty(theZone, "csarRedDelivered!") then if cfxZones.hasProperty(theZone, "csarRedDelivered!") then
csarManager.csarRedDelivered = cfxZones.getStringFromZoneProperty(theZone, "csarRedDelivered!", "*<none>") csarManager.csarRedDelivered = cfxZones.getStringFromZoneProperty(theZone, "csarRedDelivered!", "*<none>")

View File

@ -1,5 +1,5 @@
dcsCommon = {} dcsCommon = {}
dcsCommon.version = "2.8.5" dcsCommon.version = "2.8.7"
--[[-- VERSION HISTORY --[[-- VERSION HISTORY
2.2.6 - compassPositionOfARelativeToB 2.2.6 - compassPositionOfARelativeToB
- clockPositionOfARelativeToB - clockPositionOfARelativeToB
@ -147,7 +147,9 @@ dcsCommon.version = "2.8.5"
2.8.5 - better guard in getGroupUnit() 2.8.5 - better guard in getGroupUnit()
2.8.6 - phonetic helpers 2.8.6 - phonetic helpers
new spellString() new spellString()
2.8.7 - new flareColor2Num()
- new flareColor2Text()
- new iteratePlayers()
--]]-- --]]--
@ -2545,6 +2547,15 @@ end
return ("unknown: " .. smokeColor) return ("unknown: " .. smokeColor)
end end
function dcsCommon.flareColor2Text(flareColor)
if (flareColor == 0) then return "Green" end
if (flareColor == 1) then return "Red" end
if (flareColor == 2) then return "White" end
if (flareColor == 3) then return "Yellow" end
if (flareColor < 0) then return "Random" end
return ("unknown: " .. flareColor)
end
function dcsCommon.smokeColor2Num(smokeColor) function dcsCommon.smokeColor2Num(smokeColor)
if not smokeColor then smokeColor = "green" end if not smokeColor then smokeColor = "green" end
if type(smokeColor) ~= "string" then return 0 end if type(smokeColor) ~= "string" then return 0 end
@ -2556,6 +2567,20 @@ end
if (smokeColor == "blue") then return 4 end if (smokeColor == "blue") then return 4 end
return 0 return 0
end end
function dcsCommon.flareColor2Num(flareColor)
if not flareColor then flareColor = "green" end
if type(flareColor) ~= "string" then return 0 end
flareColor = flareColor:lower()
if (flareColor == "green") then return 0 end
if (flareColor == "red") then return 1 end
if (flareColor == "white") then return 2 end
if (flareColor == "yellow") then return 3 end
if (flareColor == "random") then return -1 end
if (flareColor == "rnd") then return -1 end
return 0
end
function dcsCommon.markPointWithSmoke(p, smokeColor) function dcsCommon.markPointWithSmoke(p, smokeColor)
if not smokeColor then smokeColor = 0 end if not smokeColor then smokeColor = 0 end
@ -3315,6 +3340,23 @@ function dcsCommon.spellString(inString)
return res return res
end end
--
-- iterators
--
-- iteratePlayers - call callback for all player units
-- callback is of signature callback(playerUnit)
--
function dcsCommon.iteratePlayers(callBack)
local factions = {0, 1, 2}
for idx, theFaction in pairs(factions) do
local players = coalition.getPlayers(theFaction)
for idy, theUnit in pairs(players) do
callBack(theUnit)
end
end
end
-- --
-- SEMAPHORES -- SEMAPHORES
-- --

116
modules/flareZone.lua Normal file
View File

@ -0,0 +1,116 @@
flareZone = {}
flareZone.version = "1.0.0"
flareZone.verbose = false
flareZone.name = "flareZone"
--[[-- VERSION HISTORY
1.0.0 - initial version
--]]--
flareZone.requiredLibs = {
"dcsCommon",
"cfxZones",
}
flareZone.flares = {} -- all flare zones
function flareZone.addFlareZone(theZone)
theZone.flareColor = cfxZones.getFlareColorStringFromZoneProperty(theZone, "flare", "green")
theZone.flareColor = dcsCommon.flareColor2Num(theZone.flareColor)
if cfxZones.hasProperty(theZone, "f?") then
cfxZones.theZone.doFlare = cfxZones.getStringFromZoneProperty(theZone, "f?", "<none>")
elseif cfxZones.hasProperty(theZone, "launchFlare?") then
theZone.doFlare = cfxZones.getStringFromZoneProperty(theZone, "launchFlare?", "<none>")
else
theZone.doFlare = cfxZones.getStringFromZoneProperty(theZone, "launch?", "<none>")
end
theZone.lastDoFlare = trigger.misc.getUserFlag(theZone.doFlare)
-- triggerMethod
theZone.flareTriggerMethod = cfxZones.getStringFromZoneProperty(theZone, "triggerMethod", "change")
if cfxZones.hasProperty(theZone, "flareTriggerMethod") then
theZone.flareTriggerMethod = cfxZones.getStringFromZoneProperty(theZone, "flareTriggerMethod", "change")
end
theZone.azimuthL, theZone.azimuthH = cfxZones.getPositiveRangeFromZoneProperty(theZone, "direction", 90) -- in degrees
-- in DCS documentation, the parameter is incorrectly called 'azimuth'
if cfxZones.hasProperty(theZone, "azimuth") then
theZone.azimuthL, theZone.azimuthH = cfxZones.getPositiveRangeFromZoneProperty(theZone, "azimuth", 90) -- in degrees
end
-- theZone.azimuth = theZone.azimuth * 0.0174533 -- rads
theZone.flareAlt = cfxZones.getNumberFromZoneProperty(theZone, "altitude", 1)
if cfxZones.hasProperty(theZone, "alt") then
theZone.flareAlt = cfxZones.getNumberFromZoneProperty(theZone, "alt", 1)
elseif cfxZones.hasProperty(theZone, "flareAlt") then
theZone.flareAlt = cfxZones.getNumberFromZoneProperty(theZone, "flareAlt", 1)
elseif cfxZones.hasProperty(theZone, "agl") then
theZone.flareAlt = cfxZones.getNumberFromZoneProperty(theZone, "agl", 1)
end
theZone.salvoSizeL, theZone.salvoSizeH = cfxZones.getPositiveRangeFromZoneProperty(theZone, "salvo", 1)
theZone.salvoDurationL, theZone.salvoDurationH = cfxZones.getPositiveRangeFromZoneProperty(theZone, "duration", 1)
if theZone.verbose or flareZone.verbose then
trigger.action.outText("+++flrZ: new flare <" .. theZone.name .. ">, color (" .. theZone.flareColor .. ")", 30)
end
table.insert(flareZone.flares, theZone)
end
function flareZone.launch(theZone)
local color = theZone.flareColor
if color < 0 then color = math.random(4) - 1 end
if flareZone.verbose or theZone.verbose then
trigger.action.outText("+++flrZ: launching <" .. theZone.name .. ">, c = " .. color .. " (" .. dcsCommon.flareColor2Text(color) .. ")", 30)
end
local loc = cfxZones.getPoint(theZone, true) -- with height
loc.y = loc.y + theZone.flareAlt
-- calculate azimuth
local azimuth = cfxZones.randomInRange(theZone.azimuthL, theZone.azimuthH) * 0.0174533 -- in rads
trigger.action.signalFlare(loc, color, azimuth)
end
function flareZone.update()
-- call me again in a second
timer.scheduleFunction(flareZone.update, {}, timer.getTime() + 1)
-- launch if flag banged
for idx, theZone in pairs(flareZone.flares) do
if cfxZones.testZoneFlag(theZone, theZone.doFlare, theZone.flareTriggerMethod, "lastDoFlare") then
local salvo = cfxZones.randomInRange(theZone.salvoSizeL, theZone.salvoSizeH)
if salvo < 2 then
-- one-shot
flareZone.launch(theZone)
else
-- pick a duration from range
local duration = cfxZones.randomInRange(theZone.salvoDurationL, theZone.salvoDurationH)
local duration = duration / salvo
local d = 0
for l=1, salvo do
timer.scheduleFunction(flareZone.launch, theZone, timer.getTime() + d + 0.1)
d = d + duration
end
end
end
end
end
function flareZone.start()
if not dcsCommon.libCheck("cfx Flare Zones", flareZone.requiredLibs) then return false end
-- collect all flares
local attrZones = cfxZones.getZonesWithAttributeNamed("flare")
for k, theZone in pairs(attrZones) do
flareZone.addFlareZone(theZone) -- process attribute and add to zone
end
-- start update
flareZone.update() -- also starts all unpaused
-- say hi
trigger.action.outText("cfx Flare Zone v" .. flareZone.version .. " started.", 30)
return true
end
-- let's go
if not flareZone.start() then
trigger.action.outText("cf/x Flare Zones aborted: missing libraries", 30)
cfxSmokeZone = nil
end

158
modules/playerZone.lua Normal file
View File

@ -0,0 +1,158 @@
playerZone = {}
playerZone.version = "1.0.0"
playerZone.requiredLibs = {
"dcsCommon", -- always
"cfxZones", -- Zones, of course
}
playerZone.playerZones = {}
--[[--
Version History
1.0.0 - Initial version
--]]--
function playerZone.createPlayerZone(theZone)
-- start val - a range
theZone.pzCoalition = cfxZones.getCoalitionFromZoneProperty(theZone, "playerZone", 0)
-- Method for outputs
theZone.pzMethod = cfxZones.getStringFromZoneProperty(theZone, "method", "inc")
if cfxZones.hasProperty(theZone, "pzMethod") then
theZone.pzMethod = cfxZones.getStringFromZoneProperty(theZone, "pwMethod", "inc")
end
if cfxZones.hasProperty(theZone, "pNum") then
theZone.pNum = cfxZones.getStringFromZoneProperty(theZone, "pNum", "none")
end
if cfxZones.hasProperty(theZone, "added!") then
theZone.pAdd = cfxZones.getStringFromZoneProperty(theZone, "added!", "none")
end
if cfxZones.hasProperty(theZone, "gone!") then
theZone.pRemove = cfxZones.getStringFromZoneProperty(theZone, "gone!", "none")
end
theZone.playersInZone = {} -- indexed by unit name
end
function playerZone.collectPlayersForZone(theZone)
local factions = {0, 1, 2}
local zonePlayers = {}
for idx, f in pairs (factions) do
if theZone.pzCoalition == 0 or f == theZone.pzCoalition then
local allPlayers = coalition.getPlayers(f)
for idy, theUnit in pairs (allPlayers) do
local loc = theUnit:getPoint()
if cfxZones.pointInZone(loc, theZone) then
zonePlayers[theUnit:getName()] = theUnit
end
end
end
end
return zonePlayers
end
function playerZone.processZone(theZone)
local nowInZone = playerZone.collectPlayersForZone(theZone)
-- find new players in zone
local hasNew = false
local newCount = 0
for name, theUnit in pairs(nowInZone) do
if not theZone.playersInZone[name] then
-- this unit was not here last time
hasNew = true
if playerZone.verbose or theZone.verbose then
trigger.action.outText("+++pZone: new player unit <" .. name .. "> in zone <" .. theZone.name .. ">", 30)
end
end
newCount = newCount + 1
end
-- find if players have left the zone
local hasGone = false
for name, theUnit in pairs(theZone.playersInZone) do
if not nowInZone[name] then
hasGone = true
if playerZone.verbose or theZone.verbose then
trigger.action.outText("+++pZone: player unit <" .. name .. "> disappeared from <" .. theZone.name .. ">", 30)
end
end
end
-- flag handling and banging
if theZone.pNum then
cfxZones.setFlagValueMult(theZone.pNum, newCount, theZone)
end
if theZone.pAdd and hasNew then
if theZone.verbose or playerZone.verbose then
trigger.action.outText("+++pZone: banging <" .. theZone.name .. ">'s 'added!' flags <" .. theZone.pAdd .. ">", 30)
end
cfxZones.pollFlag(theZone.pAdd, theZone.pzMethod, theZone)
end
if theZone.pRemove and hasGone then
if theZone.verbose or playerZone.verbose then
trigger.action.outText("+++pZone: banging <" .. theZone.name .. ">'s 'gone' flags <" .. theZone.pRemove .. ">", 30)
end
cfxZones.pollFlag(theZone.pAdd, theZone.pzMethod, theZone)
end
end
--
-- Update
--
function playerZone.update()
-- re-invoke in 1 second
timer.scheduleFunction(playerZone.update, {}, timer.getTime() + 1)
-- iterate all zones and check them
for idx, theZone in pairs(playerZone.playerZones) do
playerZone.processZone(theZone)
end
end
--
-- Read Config Zone
--
function playerZone.readConfigZone(theZone)
-- currently nothing to do
end
--
-- Start
--
function playerZone.start()
if not dcsCommon.libCheck("cfx Player Zone",
playerZone.requiredLibs)
then return false end
local theZone = cfxZones.getZoneByName("playerZoneConfig")
if not theZone then
theZone = cfxZones.createSimpleZone("playerZoneConfig")
end
playerZone.readConfigZone(theZone)
local pZones = cfxZones.zonesWithProperty("playerZone")
for k, aZone in pairs(pZones) do
playerZone.createPlayerZone(aZone)
playerZone.playerZones[aZone.name] = aZone
end
-- start update cycle
playerZone.update()
return true
end
if not playerZone.start() then
trigger.action.outText("+++ aborted playerZone v" .. playerZone.version .. " -- start failed", 30)
playerZone = nil
end
--[[--
additional features:
- filter by type
- filter by cat
--]]--

61
modules/shallows.lua Normal file
View File

@ -0,0 +1,61 @@
shallows = {}
-- script to remove dead naval hulls that failed to sink
-- once dead, smoke is put over the hull for 5 minutes
-- and if still in game later, the hull is removed
shallows.version = "1.0.0"
shallows.removeAfter = 5 -- minutes after kill event
shallows.verbose = false
shallows.uuid = 1
-- uuid
function shallows.getUuid()
shallows.uuid = shallows.uuid + 1
return shallows.uuid
end
-- remove hull
function shallows.removeHull(args)
if shallows.verbose then
trigger.action.outText("enter remove hull for <" .. args.name .. ">", 30)
end
-- remove smoke and whatever's left of ship
trigger.action.effectSmokeStop(args.sName)
Object.destroy(args.theUnit)
if shallows.verbose then
trigger.action.outText("Shallows: Removed <" .. args.name .. ">", 30)
end
end
-- watch the world turn and ships get killed
function shallows:onEvent(event)
if event.id ~= 28 then return end -- only kill events
if not event.target then return end
-- must be a ship
local theUnit = event.target
if not theUnit.getGroup or not theUnit:getGroup() then return end
local theGroup = theUnit:getGroup()
local cat = theGroup:getCategory()
if cat ~= 3 then return end -- not a ship
if shallows.verbose then
trigger.action.outText("Shallows: marking <" .. theUnit:getName() .. "> for deep-sixing", 30)
end
-- mark it with smoke and fire
local pos = theUnit:getPoint()
local sName = theUnit:getName() .. shallows.getUuid()
trigger.action.effectSmokeBig(pos, 2, 0.5, sName)
-- set timer to re-visit later
local args = {}
args.name = theUnit:getName()
args.sName = sName
args.theUnit = theUnit
timer.scheduleFunction(shallows.removeHull, args, timer.getTime() + shallows.removeAfter * 60)
end
-- start
world.addEventHandler(shallows)
trigger.action.outText("shallows " .. shallows.version .. " started", 30)

View File

@ -1,5 +1,5 @@
williePete = {} williePete = {}
williePete.version = "1.0.1" williePete.version = "1.0.2"
williePete.ups = 10 -- we update at 10 fps, so accuracy of a williePete.ups = 10 -- we update at 10 fps, so accuracy of a
-- missile moving at Mach 2 is within 33 meters, -- missile moving at Mach 2 is within 33 meters,
-- with interpolation even at 3 meters -- with interpolation even at 3 meters
@ -12,7 +12,8 @@ williePete.requiredLibs = {
--[[-- --[[--
Version History Version History
1.0.0 - Initial version 1.0.0 - Initial version
1.0.1 - update to suppress verbosity 1.0.1 - update to suppress verbosity
1.0.2 - added Gazelle WP
--]]-- --]]--
@ -23,7 +24,7 @@ williePete.blastedObjects = {} -- used when we detonate something
-- recognizes WP munitions. May require regular update when new -- recognizes WP munitions. May require regular update when new
-- models come out. -- models come out.
williePete.smokeWeapons = {"HYDRA_70_M274","HYDRA_70_MK61","HYDRA_70_MK1","HYDRA_70_WTU1B","HYDRA_70_M156","HYDRA_70_M158","BDU_45B","BDU_33","BDU_45","BDU_45LGB","BDU_50HD","BDU_50LD","BDU_50LGB","C_8CM"} williePete.smokeWeapons = {"HYDRA_70_M274","HYDRA_70_MK61","HYDRA_70_MK1","HYDRA_70_WTU1B","HYDRA_70_M156","HYDRA_70_M158","BDU_45B","BDU_33","BDU_45","BDU_45LGB","BDU_50HD","BDU_50LD","BDU_50LGB","C_8CM", "SNEB_TYPE254_H1_GREEN", "SNEB_TYPE254_H1_RED", "SNEB_TYPE254_H1_YELLOW"}
function williePete.addWillie(theWillie) function williePete.addWillie(theWillie)
table.insert(williePete.willies, theWillie) table.insert(williePete.willies, theWillie)