mirror of
https://github.com/weyne85/DML.git
synced 2025-10-29 16:57:49 +00:00
305 lines
10 KiB
Lua
305 lines
10 KiB
Lua
cfxObjectDestructDetector = {}
|
|
cfxObjectDestructDetector.version = "2.0.3"
|
|
cfxObjectDestructDetector.verbose = false
|
|
cfxObjectDestructDetector.requiredLibs = {
|
|
"dcsCommon", -- always
|
|
"cfxZones", -- Zones, of course
|
|
}
|
|
--[[--
|
|
VERSION HISTORY
|
|
1.0.0 initial version, based on parashoo, arty zones
|
|
1.0.1 fixed bug: trigger.MISC.getUserFlag()
|
|
1.1.0 added support for method, f! and destroyed!
|
|
1.2.0 DML / Watchflag support
|
|
1.3.0 Persistence support
|
|
2.0.0 dmlZone OOP support
|
|
clean-up
|
|
re-wrote object determination to not be affected by
|
|
ID changes (happens with map updates)
|
|
fail addZone when name property is missing
|
|
2.0.1 check that the object is within the zone onEvent
|
|
2.0.2 redScore and bluescore attributes
|
|
API for PlayerScore to pass back redScore/blueScore
|
|
if objects was killed by player
|
|
verbosity bug fixed after kill (ref to old ID)
|
|
2.0.3 if no output! given,a warning and default are given
|
|
--]]--
|
|
|
|
cfxObjectDestructDetector.objectZones = {}
|
|
|
|
--
|
|
-- C A L L B A C K S
|
|
--
|
|
cfxObjectDestructDetector.callbacks = {}
|
|
function cfxObjectDestructDetector.addCallback(theCallback)
|
|
table.insert(cfxObjectDestructDetector.callbacks, theCallback)
|
|
end
|
|
|
|
function cfxObjectDestructDetector.invokeCallbacksFor(zone)
|
|
for idx, theCB in pairs (cfxObjectDestructDetector.callbacks) do
|
|
theCB(zone, zone.ID, zone.name, zone.objName)
|
|
end
|
|
end
|
|
|
|
--
|
|
-- zone handling
|
|
--
|
|
function cfxObjectDestructDetector.addObjectDetectZone(aZone)
|
|
-- add landHeight to this zone
|
|
table.insert(cfxObjectDestructDetector.objectZones, aZone)
|
|
end
|
|
|
|
function cfxObjectDestructDetector.getObjectDetectZoneByName(aName)
|
|
for idx, aZone in pairs(cfxObjectDestructDetector.objectZones) do
|
|
if aZone.name == aName then return aZone end
|
|
end
|
|
-- add landHeight to this zone
|
|
return nil
|
|
end
|
|
|
|
--
|
|
-- processing of zones
|
|
--
|
|
function cfxObjectDestructDetector.processObjectDestructZone(aZone)
|
|
if aZone:hasProperty("name") then
|
|
aZone.objName = string.upper(aZone:getStringFromZoneProperty("NAME", "default"))
|
|
else
|
|
trigger.action.outText("+++OOD: Zone <" .. aZone.name .. "> lacks name attribute, ignored for destruct detection.")
|
|
return false
|
|
end
|
|
|
|
-- persistence interface
|
|
aZone.isDestroyed = false
|
|
|
|
aZone.oddMethod = aZone:getStringFromZoneProperty("method", "inc")
|
|
if aZone:hasProperty("oddMethod") then
|
|
aZone.oddMethod = aZone:getStringFromZoneProperty("oddMethod", "inc")
|
|
end
|
|
|
|
if aZone:hasProperty("f!") then
|
|
aZone.outDestroyFlag = aZone:getStringFromZoneProperty("f!", "*none")
|
|
elseif aZone:hasProperty("destroyed!") then
|
|
aZone.outDestroyFlag = aZone:getStringFromZoneProperty("destroyed!", "*none")
|
|
elseif aZone:hasProperty("objectDestroyed!") then
|
|
aZone.outDestroyFlag = aZone:getStringFromZoneProperty( "objectDestroyed!", "*none")
|
|
else
|
|
trigger.action.outText("+++ODD: WARNING: destrcut detector <" .. aZone.name .. "> has no output! attribute", 30)
|
|
aZone.outDestroyFlag = "sorryIforgot" -- so saving works
|
|
end
|
|
|
|
--PlayerScore interface (data)
|
|
if aZone:hasProperty("redScore") then
|
|
aZone.redScore = aZone:getNumberFromZoneProperty("redScore", 0)
|
|
-- if aZone.verbose then
|
|
-- trigger.action.outText("")
|
|
-- end
|
|
end
|
|
|
|
if aZone:hasProperty("blueScore") then
|
|
aZone.blueScore = aZone:getNumberFromZoneProperty("blueScore", 0)
|
|
end
|
|
|
|
return true
|
|
end
|
|
|
|
--
|
|
-- Interface with PlayerScore
|
|
-- ==========================
|
|
-- PlayerScore invokes us when it finds a scenery object was killed
|
|
-- we check if it is one of ours, and if so, if a score is attached
|
|
-- for that side
|
|
function cfxObjectDestructDetector.playerScoreForKill(theObject, killSide)
|
|
if not theObject then return nil end
|
|
if not killSide then return nil end
|
|
local pos = theObject:getPoint()
|
|
local desc = theObject:getDesc()
|
|
if not desc then return nil end
|
|
desc = desc.typeName -- deref type name to match zone objName
|
|
if not desc then return end
|
|
for idx, theZone in pairs (cfxObjectDestructDetector.objectZones) do
|
|
-- see if we can find a matching ODD
|
|
if (not theZone.isDestroyed) -- make sure it's not a dupe
|
|
and theZone.objName == desc
|
|
and theZone:pointInZone(pos)
|
|
then
|
|
-- yes, ODD tracks this object
|
|
if cfxObjectDestructDetector.verbose or theZone.verbose then
|
|
trigger.action.outText("OOD: score invocation for hit scenery object <" .. desc .. ">, tracked with <" .. theZone.name .. ">", 30)
|
|
end
|
|
if killSide == 1 then return theZone.redScore end -- can be nil!
|
|
if killSide == 2 then return theZone.blueScore end
|
|
-- if we get here, the object is tracked, but has no
|
|
-- playerScore attached. simply exist with nil
|
|
if cfxObjectDestructDetector.verbose or theZone.verbose then
|
|
trigger.action.outText("OOD: scenery object <" .. desc .. ">, tracked but no player score defined for coa <" .. killSide .. ">.", 30)
|
|
end
|
|
return nil
|
|
end
|
|
end
|
|
return nil
|
|
end
|
|
|
|
--
|
|
-- ON EVENT
|
|
--
|
|
function cfxObjectDestructDetector:onEvent(event)
|
|
if event.id == world.event.S_EVENT_DEAD then
|
|
if not event.initiator then return end
|
|
local theObject = event.initiator
|
|
-- check location
|
|
local pos = theObject:getPoint()
|
|
local desc = theObject:getDesc()
|
|
if not desc then return end
|
|
local matchMe = desc.typeName -- we home in on object's typeName
|
|
if not matchMe then return end
|
|
matchMe = string.upper(matchMe)
|
|
|
|
for idx, aZone in pairs(cfxObjectDestructDetector.objectZones) do
|
|
if (not aZone.isDestroyed)
|
|
and aZone.objName == matchMe
|
|
and aZone:pointInZone(pos) -- make sure it's not a dupe
|
|
then
|
|
if aZone.outDestroyFlag then
|
|
aZone:pollFlag(aZone.outDestroyFlag, aZone.oddMethod)
|
|
end
|
|
-- invoke callbacks
|
|
cfxObjectDestructDetector.invokeCallbacksFor(aZone)
|
|
if aZone.verbose or cfxObjectDestructDetector.verbose then
|
|
trigger.action.outText("OBJECT KILL: " .. matchMe .. " for odd <" .. aZone.name .. ">", 30)
|
|
end
|
|
-- save state for persistence
|
|
aZone.isDestroyed = true
|
|
return
|
|
end
|
|
end
|
|
|
|
end
|
|
|
|
end
|
|
|
|
--
|
|
-- persistence: save and load data
|
|
--
|
|
function cfxObjectDestructDetector.saveData() -- invoked by persistence
|
|
local theData = {}
|
|
local zoneInfo = {}
|
|
for idx, aZone in pairs(cfxObjectDestructDetector.objectZones) do
|
|
-- save all pertinent info. in our case, it's just
|
|
-- the isDestroyed and flag info info
|
|
info = {}
|
|
info.isDestroyed = aZone.isDestroyed
|
|
info.outDestroyVal = aZone:getFlagValue(aZone.outDestroyFlag)
|
|
zoneInfo[aZone.name] = info
|
|
end
|
|
-- expasion proof: assign as own field
|
|
theData.zoneInfo = zoneInfo
|
|
return theData
|
|
end
|
|
|
|
function cfxObjectDestructDetector.loadMission()
|
|
if cfxObjectDestructDetector.verbose then
|
|
trigger.action.outText("+++oDDet: persistence - loading data", 30)
|
|
end
|
|
|
|
local theData = persistence.getSavedDataForModule("cfxObjectDestructDetector")
|
|
if not theData then
|
|
return
|
|
end
|
|
|
|
-- iterate the data, and fail graciously if
|
|
-- we can't find a zone. it's probably beed edited out
|
|
local zoneInfo = theData.zoneInfo
|
|
if not zoneInfo then return end
|
|
if cfxObjectDestructDetector.verbose then
|
|
trigger.action.outText("+++oDDet: persistence - processing data", 30)
|
|
end
|
|
|
|
for zName, info in pairs (zoneInfo) do
|
|
local theZone = cfxObjectDestructDetector.getObjectDetectZoneByName(zName)
|
|
if theZone then
|
|
theZone.isDestroyed = info.isDestroyed
|
|
theZone:setFlagValue(theZone.outDestroyFlag, info.outDestroyVal)
|
|
if cfxObjectDestructDetector.verbose or theZone.verbose then
|
|
trigger.action.outText("+++oDDet: persistence setting flag <" .. theZone.outDestroyFlag .. "> to <" .. info.outDestroyVal .. ">",30)
|
|
end
|
|
local theName = tostring(theZone.ID)
|
|
if info.isDestroyed then
|
|
-- We now get the scenery object in that zone
|
|
-- and remove it
|
|
-- note that dcsCommon methods use DCS zones, not cfx
|
|
local theObject = dcsCommon.getSceneryObjectInZoneByName(theName, theZone.dcsZone)
|
|
if theObject then
|
|
if cfxObjectDestructDetector.verbose or theZone.verbose then
|
|
trigger.action.outText("+++oDDet: persistence removing dead scenery object <" .. theName .. ">",30)
|
|
end
|
|
theObject:destroy()
|
|
else
|
|
if cfxObjectDestructDetector.verbose or theZone.verbose then
|
|
trigger.action.outText("+++oDDet: persistence - can't find scenery objects <" .. theName .. ">, skipped destruction",30)
|
|
end
|
|
end
|
|
else
|
|
if cfxObjectDestructDetector.verbose or theZone.verbose then
|
|
trigger.action.outText("+++oDDet: persistence - scenery objects <" .. theName .. "> is healthy",30)
|
|
end
|
|
end
|
|
else
|
|
trigger.action.outText("+++oDDet: persistence - can't find detector <" .. zName .. "> on load. skipping", 30)
|
|
end
|
|
end
|
|
if cfxObjectDestructDetector.verbose then
|
|
trigger.action.outText("+++oDDet: persistence - processing complete", 30)
|
|
end
|
|
end
|
|
|
|
--
|
|
-- start
|
|
--
|
|
|
|
function cfxObjectDestructDetector.start()
|
|
if not dcsCommon.libCheck("cfx Object Destruct Detector",
|
|
cfxObjectDestructDetector.requiredLibs) then
|
|
return false
|
|
end
|
|
|
|
-- collect all zones with 'OBJECT ID' attribute
|
|
local attrZones = cfxZones.getZonesWithAttributeNamed("OBJECT ID")
|
|
|
|
|
|
for k, aZone in pairs(attrZones) do
|
|
if cfxObjectDestructDetector.processObjectDestructZone(aZone) then
|
|
cfxObjectDestructDetector.addObjectDetectZone(aZone)
|
|
end
|
|
end
|
|
|
|
-- add myself as event handler
|
|
world.addEventHandler(cfxObjectDestructDetector)
|
|
|
|
-- persistence: see if we have any data to process
|
|
-- for all our zones, and sign up for data saving
|
|
if persistence and persistence.active then
|
|
-- sign up for saves
|
|
callbacks = {}
|
|
callbacks.persistData = cfxObjectDestructDetector.saveData
|
|
persistence.registerModule("cfxObjectDestructDetector", callbacks)
|
|
|
|
if persistence.hasData then
|
|
cfxObjectDestructDetector.loadMission()
|
|
end
|
|
else
|
|
if cfxObjectDestructDetector.verbose then
|
|
trigger.action.outText("no persistence for cfxObjectDestructDetector", 30)
|
|
end
|
|
end
|
|
|
|
-- say hi
|
|
trigger.action.outText("cfx Object Destruct Zones v" .. cfxObjectDestructDetector.version .. " started.", 30)
|
|
return true
|
|
end
|
|
|
|
-- let's go
|
|
if not cfxObjectDestructDetector.start() then
|
|
trigger.action.outText("cf/x Object Destruct Zones aborted: missing libraries", 30)
|
|
cfxObjectDestructDetector = nil
|
|
end
|