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.version = "1.0.0"
asw.version = "1.0.1"
asw.verbose = false
asw.requiredLibs = {
"dcsCommon", -- always
@ -15,6 +15,7 @@ asw.fixes = {} -- all subs that we have a fix on. indexed by sub name
--[[--
Version History
1.0.0 - initial version
1.0.1 - integration with playerScore
--]]--
@ -36,6 +37,10 @@ function asw.createTorpedoForUnit(theUnit)
local t = asw.createTorpedo()
t.coalition = theUnit:getCoalition()
t.point = theUnit:getPoint()
t.droppedBy = theUnit
if theUnit.getPlayerName and theUnit:getPlayerName() ~= nil then
t.playerName = theUnit:getPlayerName()
end
return t
end
@ -716,6 +721,7 @@ function asw.updateTorpedo(theTorpedo, allSubs)
end
if dist < 1.2 * displacement then
theTorpedo.target = theSub
theTorpedo.state = 99 -- go boom
end
markTorpedo(theTorpedo)
@ -762,10 +768,26 @@ function asw.updateTorpedo(theTorpedo, allSubs)
return true
elseif theTorpedo.state == 99 then -- go boom
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)
else
if asw.verbose then
trigger.action.outText("t99 no target exist", 30)
end
end
-- 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 asw.redKill then
cfxZones.pollFlag(asw.redKill, asw.method, asw)
@ -796,6 +818,86 @@ function asw.updateTorpedo(theTorpedo, allSubs)
return true
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
--
@ -940,10 +1042,17 @@ function asw.readConfigZone()
asw.smokeColor = cfxZones.getSmokeColorStringFromZoneProperty(theZone, "smokeColor", "red")
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
trigger.action.outText("+++asw: read config", 30)
end
end
function asw.start()

View File

@ -1,5 +1,5 @@
aswGUI = {}
aswGUI.version = "1.0.1"
aswGUI.version = "1.0.2"
aswGUI.verbose = false
aswGUI.requiredLibs = {
"dcsCommon", -- always
@ -12,6 +12,7 @@ aswGUI.requiredLibs = {
Version History
1.0.0 - initial version
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
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
--
@ -571,7 +588,10 @@ function aswGUI.start()
-- subscribe to world events
world.addEventHandler(aswGUI)
-- install menus in all existing players
dcsCommon.iteratePlayers(aswGUI.processPlayerUnit)
-- say Hi
trigger.action.outText("cfx ASW GUI v" .. aswGUI.version .. " started.", 30)
return true

View File

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

View File

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

View File

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

View File

@ -1,5 +1,5 @@
cfxSpawnZones = {}
cfxSpawnZones.version = "1.7.4"
cfxSpawnZones.version = "1.7.5"
cfxSpawnZones.requiredLibs = {
"dcsCommon", -- common is of course needed for everything
-- 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.3 - ability to hand off to delicates, useDelicates attribute
1.7.4 - wait-attackZone fixes
1.7.5 - improved verbosity on spawning
- getRequestableSpawnersInRange() ignores height for distance
- types - type strings, comma separated
@ -204,6 +206,9 @@ function cfxSpawnZones.createSpawner(inZone)
theSpawner.requestable = cfxZones.getBoolFromZoneProperty(inZone, "requestable", false)
if theSpawner.requestable then
theSpawner.paused = true
if inZone.verbose or cfxSpawnZones.verbose then
trigger.action.outText("+++spwn: spawner <" .. inZone.name .. "> paused: requestable enabled", 30)
end
end
if cfxZones.hasProperty(inZone, "target") then
theSpawner.target = cfxZones.getStringFromZoneProperty(inZone, "target", "")
@ -248,7 +253,7 @@ function cfxSpawnZones.getRequestableSpawnersInRange(aPoint, aRange, aSide)
for aZone, aSpawner in pairs(cfxSpawnZones.allSpawners) do
-- iterate all zones and collect those that match
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 aSide ~= 0 then
-- check if side is correct for owned zone
@ -333,9 +338,20 @@ function cfxSpawnZones.spawnWithSpawner(aSpawner)
theCoalition,
aSpawner.baseName .. "-" .. aSpawner.count, -- must be unique
aSpawner.zone,
unitTypes,
unitTypes,
aSpawner.formation,
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.count = aSpawner.count + 1

View File

@ -1,5 +1,5 @@
cfxZones = {}
cfxZones.version = "3.0.8"
cfxZones.version = "3.0.9"
-- cf/x zone management module
-- reads dcs zones and makes them accessible and mutable
@ -126,6 +126,7 @@ cfxZones.version = "3.0.8"
- new createSimpleQuadZone()
- 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.9 - new getFlareColorStringFromZoneProperty()
--]]--
cfxZones.verbose = false
@ -2241,6 +2242,30 @@ function cfxZones.getSmokeColorStringFromZoneProperty(theZone, theProperty, defa
return default
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
--

View File

@ -1,5 +1,5 @@
csarManager = {}
csarManager.version = "2.2.5"
csarManager.version = "2.2.6"
csarManager.verbose = false
csarManager.ups = 1
@ -63,6 +63,8 @@ csarManager.ups = 1
- 2.2.4 - CSAR attribute value defaults name
- start? attribute for CSAR as startCSAR? synonym
- 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
@ -1028,6 +1030,18 @@ function csarManager.updateCSARMissions()
csarManager.openMissions = newMissions -- this is the new batch
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
-- schedule next invocation
timer.scheduleFunction(csarManager.update, {}, timer.getTime() + 1/csarManager.ups)
@ -1053,7 +1067,8 @@ function csarManager.update() -- every second
-- enough to trigger comms
for idx, csarMission in pairs (csarManager.openMissions) do
-- 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) )
and (d < csarManager.rescueTriggerRange) then
-- 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 msg = "\n" .. uName ..", " .. csarMission.name .. ". We can hear you, check your " .. oclock
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 .. "."
if csarMission.isHot then
msg = msg .. " Be advised: LZ is hot."
end
@ -1394,6 +1423,10 @@ function csarManager.readConfigZone()
csarManager.smokeColor = cfxZones.getSmokeColorStringFromZoneProperty(theZone, "smokeColor", "blue")
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
csarManager.csarRedDelivered = cfxZones.getStringFromZoneProperty(theZone, "csarRedDelivered!", "*<none>")

View File

@ -1,5 +1,5 @@
dcsCommon = {}
dcsCommon.version = "2.8.5"
dcsCommon.version = "2.8.7"
--[[-- VERSION HISTORY
2.2.6 - compassPositionOfARelativeToB
- clockPositionOfARelativeToB
@ -147,7 +147,9 @@ dcsCommon.version = "2.8.5"
2.8.5 - better guard in getGroupUnit()
2.8.6 - phonetic helpers
new spellString()
2.8.7 - new flareColor2Num()
- new flareColor2Text()
- new iteratePlayers()
--]]--
@ -2545,6 +2547,15 @@ end
return ("unknown: " .. smokeColor)
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)
if not smokeColor then smokeColor = "green" end
if type(smokeColor) ~= "string" then return 0 end
@ -2556,6 +2567,20 @@ end
if (smokeColor == "blue") then return 4 end
return 0
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)
if not smokeColor then smokeColor = 0 end
@ -3315,6 +3340,23 @@ function dcsCommon.spellString(inString)
return res
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
--

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.version = "1.0.1"
williePete.version = "1.0.2"
williePete.ups = 10 -- we update at 10 fps, so accuracy of a
-- missile moving at Mach 2 is within 33 meters,
-- with interpolation even at 3 meters
@ -12,7 +12,8 @@ williePete.requiredLibs = {
--[[--
Version History
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
-- 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)
table.insert(williePete.willies, theWillie)