mirror of
https://github.com/weyne85/DML.git
synced 2025-10-29 16:57:49 +00:00
Version 2.0.1
Updates to Debugger, csarManager, PlayerScore/objectDestructDetector
This commit is contained in:
parent
c9694c3176
commit
38d6487de7
Binary file not shown.
Binary file not shown.
@ -12,27 +12,7 @@ rndFlags.requiredLibs = {
|
||||
Copyright 2022 by Christian Franz and cf/x
|
||||
|
||||
Version History
|
||||
1.0.0 - Initial Version
|
||||
1.1.0 - DML flag conversion:
|
||||
flagArrayFromString: strings OK, trim
|
||||
remove pollFlag
|
||||
pollFlag from cfxZones, include zone
|
||||
randomBetween for pollSize
|
||||
pollFlag to bang done with inc
|
||||
getFlagValue in update
|
||||
some code clean-up
|
||||
rndMethod synonym
|
||||
1.2.0 - Watchflag integration
|
||||
1.3.0 - DML simplification: RND!
|
||||
zone-local verbosity
|
||||
1.3.1 - 'done+1' --> 'done!', using rndMethod instead of 'inc'
|
||||
- added zonal verbosity
|
||||
- added 'rndDone!' flag
|
||||
- rndMethod defaults to "inc"
|
||||
1.3.2 - moved flagArrayFromString to dcsCommon
|
||||
- minor clean-up
|
||||
1.4.0 - persistence
|
||||
1.4.1 - a little less verbosity
|
||||
|
||||
2.0.0 - dmlZones, OOP
|
||||
|
||||
--]]
|
||||
|
||||
@ -158,3 +158,13 @@ if not cfxSSBSingleUse.start()then
|
||||
trigger.action.outText("SSB Single Use failed to start up.", 30)
|
||||
cfxSSBSingleUse = nil
|
||||
end
|
||||
|
||||
|
||||
--[[--
|
||||
limited use rebuild
|
||||
have planes inside a zone to regulate:
|
||||
numUp attribute for number of lives. defaults to one
|
||||
reinforce? to make availabe again, resets numLeft to numUp - warning: check with other inhibitors
|
||||
onStart = no will inhibit at start
|
||||
exempt - limits to not apply to planes in this zone. same as -1 for numUp
|
||||
--]]--
|
||||
@ -1,5 +1,5 @@
|
||||
tdz = {}
|
||||
tdz.version = "1.0.2"
|
||||
tdz.version = "1.0.3"
|
||||
tdz.requiredLibs = {
|
||||
"dcsCommon", -- always
|
||||
"cfxZones", -- Zones, of course
|
||||
@ -17,6 +17,7 @@ VERSION HISTORY
|
||||
helo attribute
|
||||
1.0.2 - manual placement option
|
||||
filters FARPs
|
||||
1.0.3 - "manual" now defaults to false
|
||||
|
||||
--]]--
|
||||
|
||||
@ -89,7 +90,7 @@ function tdz.createTDZ(theZone)
|
||||
|
||||
local nearestRwy = nil
|
||||
-- see if this is a manually placed runway
|
||||
if theZone:getBoolFromZoneProperty("manual", true) then
|
||||
if theZone:getBoolFromZoneProperty("manual", false) then
|
||||
-- construct runway from trigger zone attributes
|
||||
if theZone.verbose or tdz.verbose then
|
||||
trigger.action.outText("+++TDZ: runway for <" .. theZone.name .. "> is manually placed", 30)
|
||||
|
||||
@ -1,34 +1,26 @@
|
||||
airfield = {}
|
||||
airfield.version = "2.0.0"
|
||||
airfield.version = "2.1.1"
|
||||
airfield.requiredLibs = {
|
||||
"dcsCommon",
|
||||
"cfxZones",
|
||||
}
|
||||
airfield.myAirfields = {} -- indexed by name
|
||||
airfield.myAirfields = {} -- indexed by af name, zone that links to it
|
||||
airfield.gracePeriod = 3
|
||||
airfield.allAirfields = {} -- inexed by name
|
||||
airfield.allAirfields = {} -- inexed by af name, db entries: base, cat
|
||||
|
||||
--[[--
|
||||
This module generates signals when the nearest airfield changes hands,
|
||||
can force the coalition of an airfield, and always provides the
|
||||
current owner as a value
|
||||
|
||||
Version History
|
||||
1.0.0 - initial release
|
||||
1.1.0 - added 'fixed' attribute
|
||||
- added 'farps' attribute to individual zones
|
||||
- allow zone.local farps designation
|
||||
- always checks farp cap events
|
||||
- added verbosity
|
||||
1.1.1 - GC grace period correction of ownership
|
||||
1.1.2 - 'show' attribute
|
||||
line color attributes per zone
|
||||
line color defaults in config
|
||||
Version History^
|
||||
2.0.0 - show all airfields option
|
||||
- fully reworked show options
|
||||
- unmanaged airfields are automatically updated
|
||||
- full color support
|
||||
-- support for FARPS as well
|
||||
2.1.0 - added support for makeNeutral?
|
||||
2.1.1 - bug fixing for DCS 2.9x airfield retrofit
|
||||
|
||||
--]]--
|
||||
|
||||
@ -39,7 +31,9 @@ function airfield.collectAll()
|
||||
local dropped = 0
|
||||
for idx, aBase in pairs(allBases) do
|
||||
local entry = {}
|
||||
local cat = Airbase.getCategory(aBase) -- DCS 2.9 hardened
|
||||
--local cat = Airbase.getCategory(aBase) -- DCS 2.9 hardened
|
||||
-- ho! dcs 2.9.x retrofit screwed with Airfield.getCategory.
|
||||
local cat = aBase:getDesc().category
|
||||
-- cats: 0 = airfield, 1 = farp, 2 = ship
|
||||
if (cat == 0) or (cat == 1) then
|
||||
local name = aBase:getName()
|
||||
@ -50,6 +44,7 @@ function airfield.collectAll()
|
||||
count = count + 1
|
||||
else
|
||||
dropped = dropped + 1
|
||||
-- trigger.action.outText("***dropped airbase <" .. aBase:getName() .. ">, cat = <" .. cat .. ">", 30)
|
||||
end
|
||||
end
|
||||
if airfield.verbose then
|
||||
@ -98,6 +93,11 @@ function airfield.createAirFieldFromZone(theZone)
|
||||
theZone.lastMakeBlue = trigger.misc.getUserFlag(theZone.makeBlue)
|
||||
end
|
||||
|
||||
if theZone:hasProperty("makeNeutral?") then
|
||||
theZone.makeNeutral = theZone:getStringFromZoneProperty("makeNeutral?", "<none>")
|
||||
theZone.lastMakeNeutral = trigger.misc.getUserFlag(theZone.makeNeutral)
|
||||
end
|
||||
|
||||
if theZone:hasProperty("autoCap?") then
|
||||
theZone.autoCap = theZone:getStringFromZoneProperty("autoCap?", "<none>")
|
||||
theZone.lastAutoCap = trigger.misc.getUserFlag(theZone.autoCap)
|
||||
@ -146,8 +146,11 @@ function airfield.createAirFieldFromZone(theZone)
|
||||
|
||||
-- now mark this zone as handled
|
||||
local entry = airfield.allAirfields[theZone.afName]
|
||||
if not entry then
|
||||
trigger.action.outText("+++airF: WARNING - unlinked airfield <" .. theZone.afName .. "> for zone <" .. theZone.name .. ">", 30)
|
||||
else
|
||||
entry.linkedTo = theZone -- only remember last, but that's enough.
|
||||
|
||||
end
|
||||
end
|
||||
|
||||
function airfield.markAirfieldOnMap(theAirfield, lineColor, fillColor)
|
||||
@ -246,6 +249,10 @@ function airfield.airfieldCaptured(theBase)
|
||||
airfield.untendedCapture(bName, theBase)
|
||||
return
|
||||
end -- not attached to a zone
|
||||
if theZone.verbose or airfield.verbose then
|
||||
trigger.action.outText("+++airF: capturing <" .. bName .. "> for zone <" .. theZone.name .. ">", 30)
|
||||
end
|
||||
|
||||
local newCoa = theBase:getCoalition()
|
||||
theZone.owner = newCoa
|
||||
|
||||
@ -324,6 +331,21 @@ function airfield.update()
|
||||
end
|
||||
end
|
||||
|
||||
if theZone.makeNeutral and theZone:testZoneFlag(theZone.makeNeutral, theZone.triggerMethod, "lastMakeNeutral") then
|
||||
if theZone.verbose or airfield.verbose then
|
||||
trigger.action.outText("+++airF: 'makeNeutral' triggered for airfield <" .. afName .. "> in zone <" .. theZone:getName() .. ">", 30)
|
||||
end
|
||||
if theAirfield:autoCaptureIsOn() then
|
||||
-- turn off autoCap
|
||||
airfield.assumeControl(theZone)
|
||||
end
|
||||
theAirfield:setCoalition(0) -- make it blue
|
||||
if theZone.owner ~= 0 then -- only send cap event when capped
|
||||
airfield.airfieldCaptured(theAirfield) -- 0 cap will not cause any signals, but we do this anyway
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
if theZone.autoCap and theZone:testZoneFlag(theZone.autoCap, theZone.triggerMethod, "lastAutoCap") then
|
||||
if theAirfield:autoCaptureIsOn() then
|
||||
-- do nothing
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
cfxZones = {}
|
||||
cfxZones.version = "4.1.1"
|
||||
cfxZones.version = "4.1.2"
|
||||
|
||||
-- cf/x zone management module
|
||||
-- reads dcs zones and makes them accessible and mutable
|
||||
@ -43,6 +43,7 @@ cfxZones.version = "4.1.1"
|
||||
- 4.0.10 - getBoolFromZoneProperty also supports "on" (=true) and "off" (=false)
|
||||
- 4.1.0 - getBoolFromZoneProperty 'on/off' support for dml variant as well
|
||||
- 4.1.1 - evalRemainder() updates
|
||||
- 4.1.2 - hash property missing warning
|
||||
|
||||
--]]--
|
||||
|
||||
@ -2299,6 +2300,14 @@ function dmlZone:hasProperty(theProperty)
|
||||
return false
|
||||
end
|
||||
|
||||
if string.sub(theProperty, -1) == "#" then
|
||||
local lessOp = theProperty:sub(1,-2)
|
||||
if self:getZoneProperty(lessOp) ~= nil then
|
||||
trigger.action.outText("*** NOTE: " .. self.name .. "'s property <" .. lessOp .. "> may be missing a hash mark ('#') at end", 30)
|
||||
end
|
||||
return false
|
||||
end
|
||||
|
||||
return false
|
||||
end
|
||||
return true
|
||||
|
||||
@ -1,37 +1,6 @@
|
||||
civAir = {}
|
||||
civAir.version = "3.0.1"
|
||||
civAir.version = "3.0.2"
|
||||
--[[--
|
||||
1.0.0 initial version
|
||||
1.1.0 exclude list for airfields
|
||||
1.1.1 bug fixes with remove flight
|
||||
and betweenHubs
|
||||
check if slot is really free before spawning
|
||||
add overhead waypoint
|
||||
1.1.2 inAir start possible
|
||||
1.2.0 civAir can use own config file
|
||||
1.2.1 slight update to config file (moved active/idle)
|
||||
1.3.0 add ability to use zones to add closest airfield to
|
||||
trafficCenters or excludeAirfields
|
||||
1.4.0 ability to load config from zone to override
|
||||
all configs it finds
|
||||
module check
|
||||
removed obsolete civAirConfig module
|
||||
1.5.0 process zones as in all other modules
|
||||
verbose is part of config zone
|
||||
reading type array from config corrected
|
||||
massive simplifications: always between zoned airfieds
|
||||
exclude list and include list
|
||||
1.5.1 added depart only and arrive only options for airfields
|
||||
1.5.2 fixed bugs inb verbosity
|
||||
2.0.0 dmlZones
|
||||
inbound zones
|
||||
outbound zones
|
||||
on start location is randomizes 30-70% of the way
|
||||
guarded 'no longer exist' warning for verbosity
|
||||
changed unit naming from -civA to -GA
|
||||
strenghtened guard on testing against free slots for other units
|
||||
flights are now of random neutral countries
|
||||
maxFlights synonym for maxTraffic
|
||||
3.0.0 liveries support
|
||||
default liveries for Yak-50 (main test case)
|
||||
default liveries for C-130, c-17A, IL-76MD, An-30M, An-26B
|
||||
@ -45,6 +14,7 @@ civAir.version = "3.0.1"
|
||||
3.0.1 protest option, on by default
|
||||
protest action
|
||||
spawning now works correctly for groupType
|
||||
3.0.2 clean-up
|
||||
|
||||
--]]--
|
||||
|
||||
|
||||
@ -259,6 +259,14 @@ function cloneZones.createClonerWithZone(theZone) -- has "Cloner"
|
||||
trigger.action.outText("+++clnZ: masterOwner for <" .. theZone.name .. "> set successfully to to itself, currently owned by faction <" .. theZone.owner .. ">", 30)
|
||||
end
|
||||
end
|
||||
if theZone.verbose or cloneZones.verbose then
|
||||
trigger.action.outText("+++clnZ: ownership of <" .. theZone.name .. "> tied to zone <" .. theZone.masterOwner .. ">", 30)
|
||||
end
|
||||
-- check that the zone exists in DCS
|
||||
local theMaster = cfxZones.getZoneByName(theZone.masterOwner)
|
||||
if not theMaster then
|
||||
trigger.action.outText("clnZ: WARNING: cloner's <" .. theZone.name .. "> master owner named <" .. theZone.masterOwner .. "> does not exist!", 30)
|
||||
end
|
||||
end
|
||||
|
||||
theZone.turn = theZone:getNumberFromZoneProperty("turn", 0)
|
||||
|
||||
@ -1,70 +1,9 @@
|
||||
csarManager = {}
|
||||
csarManager.version = "2.3.2"
|
||||
csarManager.verbose = false
|
||||
csarManager.version = "3.0.0"
|
||||
csarManager.ups = 1
|
||||
|
||||
--[[-- VERSION HISTORY
|
||||
- 1.0.0 initial version
|
||||
- 1.0.1 - smoke optional
|
||||
- airframeCrashed method for airframe manager
|
||||
- removed '(downed )' when re-picked up
|
||||
- fixed oclock
|
||||
- 1.0.2 - hover retrieval
|
||||
- 1.0.3 - corrected a bug in oclock during hovering
|
||||
- 1.0.4 - now correctly allocates pilot to coalition via dcscommon.coalition2county
|
||||
- 1.1.0 - pilot adds weight to unit
|
||||
- module check
|
||||
- 2.0.0 - weight managed via cargoSuper
|
||||
- 2.0.1 - getCSARBaseforZone()
|
||||
- check if zone landed in has owner attribute
|
||||
to provide compatibility with owned zones,
|
||||
FARPZones etc that keep zone.owner up to date
|
||||
- 2.0.2 - use parametric csarManager.hoverAlt
|
||||
- use hoverDuration
|
||||
- 2.0.3 - corrected bug in hoverDuration
|
||||
- 2.0.4 - guard in createCSARMission for cfxCommander
|
||||
- 2.1.0 - startCSAR?
|
||||
- deferrable missions
|
||||
- verbose
|
||||
- ups
|
||||
- useSmoke
|
||||
- smokeColor
|
||||
- reworked smoking the loc
|
||||
- config zone
|
||||
- csarRedDelivered
|
||||
- csarBlueDelivered
|
||||
- finally fixed smoke performance bug
|
||||
- csarManager.vectoring optional
|
||||
- 2.1.1 - zone-local verbosity
|
||||
- 2.1.2 - 'downed' machinations (paranthese)S
|
||||
- verbosity
|
||||
- 2.1.3 - theMassObject now local
|
||||
- winch pickup now also adds weight so they can be returned
|
||||
- made some improvements to performance by making vars local
|
||||
- 2.2.0 - interface for autoCSAR
|
||||
createDownedPilot() - added existingUnit option
|
||||
createCSARMissionData() - added existinUnit option
|
||||
- when no config zone, runs through empty zone
|
||||
- actionSound
|
||||
- integration with playerScore
|
||||
- score global and per-mission
|
||||
- isCSARTarget API
|
||||
- 2.2.1 - added troopCarriers attribute to config
|
||||
- passes own troop carriers to dcsCommon.isTroopCarrier()
|
||||
- 2.2.2 - enable CSAR missions in water
|
||||
- csar name defaults to zone name
|
||||
- better randomization of pilot's point in csar mission,
|
||||
supports quad zone
|
||||
- 2.2.3 - better support for red/blue
|
||||
- allow neutral pick-up
|
||||
- directions to closest safe zone
|
||||
- CSARBASE attribute now carries coalition
|
||||
- deprecated coalition attribute
|
||||
- 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
|
||||
|
||||
- 2.3.0 - dmlZones
|
||||
- onRoad attribute for CSAR mission Zones
|
||||
- rndLoc support
|
||||
@ -74,32 +13,27 @@ csarManager.ups = 1
|
||||
- offset zone on randomized soldier
|
||||
- smokeDist
|
||||
- 2.3.2 - DCS 2.9 getCategory() fix
|
||||
- 3.0.0 - moved mission creation out of update loop into own
|
||||
- removed cfxPlayer dependency
|
||||
- new event manager
|
||||
- no longer single-proccing pilots
|
||||
- can also handle aircraft - isTroopCarrier
|
||||
|
||||
INTEGRATES AUTOMATICALLY WITH playerScore IF INSTALLED
|
||||
INTEGRATES WITH LIMITE AIRFRAMES IF INSTALLED
|
||||
|
||||
--]]--
|
||||
-- modules that need to be loaded BEFORE I run
|
||||
csarManager.requiredLibs = {
|
||||
"dcsCommon", -- common is of course needed for everything
|
||||
"cfxZones", -- zones management for CSAR and CSAR Mission zones
|
||||
"cfxPlayer", -- player monitoring and group monitoring
|
||||
"nameStats", -- generic data module for weight
|
||||
"cargoSuper",
|
||||
-- "cfxCommander", -- needed only if you want to hand-create CSAR missions
|
||||
}
|
||||
|
||||
|
||||
--
|
||||
-- OPTIONS
|
||||
--
|
||||
csarManager.useSmoke = true
|
||||
csarManager.smokeColor = 4 -- when using smoke
|
||||
|
||||
|
||||
-- unitConfigs contain the config data for any helicopter
|
||||
-- currently in the game. The Array is indexed by unit name
|
||||
csarManager.unitConfigs = {}
|
||||
csarManager.myEvents = {3, 4, 5} -- 3 = take off, 4 = land, 5 = crash
|
||||
|
||||
--
|
||||
-- CASR MISSION
|
||||
@ -157,15 +91,10 @@ function csarManager.createDownedPilot(theMission, existingUnit)
|
||||
aLocation.z,
|
||||
-aHeading + 1.5) -- + 1.5 to turn inwards
|
||||
|
||||
-- WARNING:
|
||||
-- coalition.addGroup takes the COUNTRY of the group, and derives the
|
||||
-- coalition from that. So if mission.sie is 0, we use UN, if it is 1 (red) it
|
||||
-- is joint red, if 2 it is joint blue
|
||||
local theSideCJTF = dcsCommon.coalition2county(theMission.side) -- get the correct county CJTF
|
||||
local theSideCJTF = dcsCommon.getACountryForCoalition(theMission.side)
|
||||
theMission.group = coalition.addGroup(theSideCJTF,
|
||||
Group.Category.GROUND,
|
||||
theBoyGroup)
|
||||
|
||||
if theBoyGroup then
|
||||
|
||||
else
|
||||
@ -176,10 +105,10 @@ function csarManager.createDownedPilot(theMission, existingUnit)
|
||||
end
|
||||
|
||||
-- we now use commands to send radio transmissions
|
||||
local ADF = 20 + math.random(90)
|
||||
local ADF = 20 + math.random(150) -- create a random number between 20 and 110 --> 200'000 .. 1'700'000 KHz = 200KHz .. 1'700 KHz
|
||||
if theMission.freq then ADF = theMission.freq else theMission.freq = ADF end
|
||||
local theCommands = cfxCommander.createCommandDataTableFor(theMission.group)
|
||||
local cmd = cfxCommander.createSetFrequencyCommand(ADF) -- freq in 10000 Hz
|
||||
local cmd = cfxCommander.createSetFrequencyCommand(ADF) -- freq in 10'000 Hz
|
||||
cfxCommander.addCommand(theCommands, cmd)
|
||||
cmd = cfxCommander.createTransmissionCommand(csarManager.beaconSound)
|
||||
cfxCommander.addCommand(theCommands, cmd)
|
||||
@ -259,7 +188,7 @@ end
|
||||
-- UNIT CONFIG
|
||||
--
|
||||
function csarManager.resetConfig(conf)
|
||||
-- reset only ovberwrites mission-relevant data
|
||||
-- reset only overwrites mission-relevant data
|
||||
conf.troopsOnBoard = {} -- number of rescued missions
|
||||
local myName = conf.name
|
||||
cargoSuper.removeAllMassForCargo(myName, "Evacuees") -- will allocate new empty table
|
||||
@ -309,47 +238,17 @@ end
|
||||
--
|
||||
-- E V E N T H A N D L I N G
|
||||
--
|
||||
function csarManager.isInteresting(eventID)
|
||||
-- return true if we are interested in this event, false else
|
||||
for key, evType in pairs(csarManager.myEvents) do
|
||||
if evType == eventID then return true end
|
||||
end
|
||||
return false
|
||||
end
|
||||
|
||||
function csarManager.preProcessor(event)
|
||||
function csarManager:onEvent(event)
|
||||
-- make sure it has an initiator
|
||||
if not event.initiator then return false end -- no initiator
|
||||
if not event.initiator then return end
|
||||
local theUnit = event.initiator
|
||||
if not theUnit.getDesc then return fase end -- not a unit
|
||||
local cat = theUnit:getDesc().category --theUnit:getCategory()
|
||||
if cat ~= 1 then -- Unit.Category.HELICOPTER
|
||||
return false
|
||||
end
|
||||
|
||||
--trigger.action.outText("+++csar: event " .. event.id .. " for cat = " .. cat .. " (helicopter?) unit " .. theUnit:getName(), 30)
|
||||
if not dcsCommon.isPlayerUnit(theUnit) then return end -- not a player unit
|
||||
|
||||
if not cfxPlayer.isPlayerUnit(theUnit) then
|
||||
--trigger.action.outText("+++csar: rejected event: " .. theUnit:getName() .. " not a player helo", 30)
|
||||
return false
|
||||
end -- not a player unit
|
||||
return csarManager.isInteresting(event.id)
|
||||
end
|
||||
-- only proceed if troop carrier (no more helo checks, all troop carriers, so osprey and harrier can be used if so desired)
|
||||
if not dcsCommon.isTroopCarrier(theUnit, csarManager.troopCarriers) then return end
|
||||
|
||||
function csarManager.postProcessor(event)
|
||||
-- don't do anything for now
|
||||
end
|
||||
|
||||
function csarManager.somethingHappened(event)
|
||||
-- when this is invoked, the preprocessor guarantees that
|
||||
-- it's an interesting event
|
||||
-- unit is valid and player
|
||||
-- airframe category is helicopter
|
||||
local theUnit = event.initiator
|
||||
local ID = event.id
|
||||
|
||||
local myType = theUnit:getTypeName()
|
||||
|
||||
if ID == 4 then -- landed
|
||||
csarManager.heloLanded(theUnit)
|
||||
end
|
||||
@ -362,15 +261,16 @@ function csarManager.somethingHappened(event)
|
||||
csarManager.heloCrashed(theUnit)
|
||||
end
|
||||
|
||||
if ID == 15 then -- player helicopter birth
|
||||
-- we need to set up comms for this unit
|
||||
csarManager.setCommsMenu(theUnit)
|
||||
end
|
||||
|
||||
--
|
||||
end
|
||||
|
||||
--
|
||||
-- CSAR LANDED
|
||||
--
|
||||
--
|
||||
|
||||
function csarManager.successMission(who, where, theMission)
|
||||
-- who is
|
||||
-- where is
|
||||
@ -397,7 +297,6 @@ function csarManager.successMission(who, where, theMission)
|
||||
|
||||
-- now call callback for coalition side
|
||||
-- callback has format callback(coalition, success true/false, numberSaved, descriptionText)
|
||||
|
||||
csarManager.invokeCallbacks(theMission.side, true, 1, "success")
|
||||
|
||||
trigger.action.outSoundForCoalition(theMission.side, csarManager.actionSound) -- "Quest Snare 3.wav")
|
||||
@ -491,13 +390,11 @@ function csarManager.heloLanded(theUnit)
|
||||
conf.troopsOnBoard = {} -- empty out troops on board
|
||||
-- we do *not* return so we can pick up troops on
|
||||
-- a CSARBASE if they were dropped there
|
||||
|
||||
else
|
||||
if csarManager.verbose or base.zone.verbose then
|
||||
trigger.action.outText("+++csar: touchdown of <" .. myName .. "> occured outside of csar zone <" .. base.zone.name .. ">", 30)
|
||||
end
|
||||
end
|
||||
|
||||
else -- not on my side
|
||||
if csarManager.verbose or base.zone.verbose then
|
||||
trigger.action.outText("+++csar: base <" .. base.zone.name .. "> is on side <" .. currentBaseSide .. ">, which is not on my side <" .. mySide .. ">.", 30)
|
||||
@ -930,46 +827,6 @@ function csarManager.directions(args)
|
||||
trigger.action.outTextForGroup(conf.id, report, 30)
|
||||
trigger.action.outSoundForGroup(conf.id, csarManager.actionSound)
|
||||
end
|
||||
--
|
||||
-- Player event callbacks
|
||||
--
|
||||
function csarManager.playerChangeEvent(evType, description, player, data)
|
||||
if evType == "newGroup" then
|
||||
local theUnit = data.primeUnit
|
||||
if not dcsCommon.isTroopCarrier(theUnit, csarManager.troopCarriers) then return end
|
||||
|
||||
csarManager.setCommsMenu(theUnit) -- allocates new config
|
||||
-- trigger.action.outText("+++csar: added " .. theUnit:getName() .. " to comms menu", 30)
|
||||
return
|
||||
end
|
||||
|
||||
if evType == "removeGroup" then
|
||||
-- trigger.action.outText("+++csar: a group disappeared", 30)
|
||||
local conf = csarManager.getConfigForUnitNamed(data.primeUnitName)
|
||||
if conf then
|
||||
csarManager.removeCommsFromConfig(conf)
|
||||
end
|
||||
return
|
||||
end
|
||||
|
||||
if evType == "leave" then
|
||||
local conf = csarManager.getConfigForUnitNamed(player.unitName)
|
||||
if conf then
|
||||
csarManager.resetConfig(conf)
|
||||
end
|
||||
end
|
||||
|
||||
if evType == "unit" then
|
||||
-- player changed units. almost never in MP, but possible in solo
|
||||
-- because of 1 seconds timing loop
|
||||
-- will result in a new group appearing and a group disappearing, so we are good,
|
||||
-- except we need to reset the conf so no troops are carried any longer
|
||||
local conf = csarManager.getConfigForUnitNamed(data.oldUnitName)
|
||||
if conf then
|
||||
csarManager.resetConfig(conf)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
--
|
||||
-- CSAR Bases
|
||||
@ -1056,6 +913,10 @@ function csarManager.launchFlare(args)
|
||||
trigger.action.signalFlare(loc, color, 0)
|
||||
end
|
||||
|
||||
|
||||
-- WE ASSUME MISSIONS AREN'T TOO CLOSE TOGETHER TO
|
||||
-- MESS UP MESSAGING OR PICKUP
|
||||
-- if they are less than 2d apart, they can crosstalk each other
|
||||
function csarManager.update() -- every second
|
||||
-- schedule next invocation
|
||||
timer.scheduleFunction(csarManager.update, {}, timer.getTime() + 1/csarManager.ups)
|
||||
@ -1065,20 +926,22 @@ function csarManager.update() -- every second
|
||||
|
||||
-- now scan through all helo groups and see if they are close to a
|
||||
-- CSAR zone and initiate the help sequence
|
||||
local allPlayerGroups = cfxPlayerGroups -- cfxPlayerGroups is a global, don't fuck with it!
|
||||
-- contains per group a player record, use prime unit to access player's unit
|
||||
for gname, pgroup in pairs(allPlayerGroups) do
|
||||
local aUnit = pgroup.primeUnit -- get prime unit of that group
|
||||
if aUnit:isExist() and aUnit:inAir() then -- exists and is flying
|
||||
-- local allPlayerGroups = cfxPlayerGroups -- cfxPlayerGroups is a global, don't fuck with it!
|
||||
local allPlayerUnits = dcsCommon.getAllExistingPlayersAndUnits() -- indexed by player name
|
||||
--old -- contains per group a player record, use prime unit to access player's unit
|
||||
for pname, aUnit in pairs(allPlayerUnits) do
|
||||
if --aUnit:isExist() and
|
||||
aUnit:inAir() and
|
||||
dcsCommon.isTroopCarrier(aUnit, csarManager.troopCarriers)
|
||||
then -- troop carrier and is flying
|
||||
local uPoint = aUnit:getPoint()
|
||||
local uName = aUnit:getName()
|
||||
local uGroup = aUnit:getGroup()
|
||||
local uID = uGroup:getID()
|
||||
local uSide = aUnit:getCoalition()
|
||||
local agl = dcsCommon.getUnitAGL(aUnit)
|
||||
if dcsCommon.isTroopCarrier(aUnit, csarManager.troopCarriers) then
|
||||
-- scan through all available csar missions to see if we are close
|
||||
-- enough to trigger comms
|
||||
local needsGC = false
|
||||
-- local hasMessaged = false
|
||||
for idx, csarMission in pairs (csarManager.openMissions) do
|
||||
-- check if we are inside trigger range on the same side
|
||||
local mp = cfxZones.getPoint(csarMission.zone, true)
|
||||
@ -1086,7 +949,7 @@ function csarManager.update() -- every second
|
||||
if ((uSide == csarMission.side) or (csarMission.side == 0) )
|
||||
and (d < csarManager.rescueTriggerRange) then
|
||||
-- we are in trigger distance. if we did not notify before
|
||||
-- do it now
|
||||
-- do it now, we ever only do this once for a unit for any mission
|
||||
if not dcsCommon.arrayContainsString(csarMission.messagedUnits, uName) then
|
||||
-- radio this unit with oclock and tell it they are in 2k range
|
||||
-- also note if LZ is hot
|
||||
@ -1117,24 +980,25 @@ function csarManager.update() -- every second
|
||||
trigger.action.outSoundForGroup(uID, csarManager.actionSound) -- "Quest Snare 3.wav")
|
||||
table.insert(csarMission.messagedUnits, uName) -- remember that we messaged them so we don't do again
|
||||
end
|
||||
|
||||
-- also pop smoke if not popped already, or more than 5 minutes ago
|
||||
if csarManager.useSmoke and (timer.getTime() - csarMission.lastSmokeTime) >= 5 * 60 then
|
||||
local smokePoint = dcsCommon.randomPointOnPerimeter(
|
||||
csarManager.smokeDist, csarMission.zone.point.x, csarMission.zone.point.z) --cfxZones.createHeightCorrectedPoint(csarMission.zone.point)
|
||||
-- trigger.action.smoke(smokePoint, 4 )
|
||||
csarManager.smokeDist, csarMission.zone.point.x, csarMission.zone.point.z)
|
||||
dcsCommon.markPointWithSmoke(smokePoint, csarManager.smokeColor)
|
||||
csarMission.lastSmokeTime = timer.getTime()
|
||||
end
|
||||
|
||||
-- now check if we are inside hover range and alt
|
||||
-- in order to simultate winch ops
|
||||
-- WARNING: WE ALWAYS ONLY CHECK A SINGLE UNIT - the first alive
|
||||
local evacuee = csarMission.group:getUnit(1)
|
||||
-- if competition picked up, we skip this loop
|
||||
local evacuee = nil
|
||||
if csarMission.group then evacuee = csarMission.group:getUnit(1) end
|
||||
if evacuee then
|
||||
local ep = evacuee:getPoint()
|
||||
d = dcsCommon.distFlat(uPoint, ep)
|
||||
d = math.floor(d * 10) / 10
|
||||
if d < csarManager.rescueTriggerRange * 0.5 then --csarManager.hoverRadius * 2 then
|
||||
if d < csarManager.rescueTriggerRange * 0.5 then
|
||||
local ownHeading = dcsCommon.getUnitHeadingDegrees(aUnit)
|
||||
local oclock = dcsCommon.clockPositionOfARelativeToB(ep, uPoint, ownHeading) .. " o'clock"
|
||||
-- log distance
|
||||
@ -1156,10 +1020,11 @@ function csarManager.update() -- every second
|
||||
-- we rescued the guy!
|
||||
hoverMsg = "We have " .. csarMission.name .. " safely on board!"
|
||||
local conf = csarManager.getUnitConfig(aUnit)
|
||||
csarManager.removeMission(csarMission)
|
||||
-- mission now GC's after iteration csarManager.removeMission(csarMission)
|
||||
table.insert(conf.troopsOnBoard, csarMission)
|
||||
csarMission.group:destroy() -- will shut up radio as well
|
||||
csarMission.group = nil
|
||||
csarMission.group = nil -- no more evacuees
|
||||
needsGC = true -- need filtering missions
|
||||
|
||||
-- now handle weight using cargoSuper
|
||||
local theMassObject = cargoSuper.createMassObject(
|
||||
@ -1178,13 +1043,13 @@ function csarManager.update() -- every second
|
||||
trigger.action.outText("+++csar: <" .. uName .. "> now has <" .. #allEvacuees .. "> groups of evacuees on board, totalling " .. totalMass .. "kg", 30)
|
||||
end
|
||||
|
||||
trigger.action.outTextForGroup(uID, hoverMsg, 30, true)
|
||||
trigger.action.outSoundForGroup(uID, csarManager.actionSound) --"Quest Snare 3.wav")
|
||||
--trigger.action.outTextForGroup(uID, hoverMsg, 30, true)
|
||||
trigger.action.outSoundForGroup(uID, csarManager.actionSound)
|
||||
|
||||
return -- we only ever rescue one
|
||||
--return -- we only ever rescue one
|
||||
end -- hovered long enough
|
||||
trigger.action.outTextForGroup(uID, hoverMsg, 30, true)
|
||||
return -- only ever one winch op
|
||||
--trigger.action.outTextForGroup(uID, hoverMsg, 30, true)
|
||||
-- return -- only ever one winch op
|
||||
else -- too high for hover
|
||||
hoverMsg = "Evacuee " .. d * 1 .. "m on your " .. oclock .. " o'clock; land or descend to between 10 and 90 AGL for winching"
|
||||
csarMission.hoveringUnits[uName] = nil -- reset timer
|
||||
@ -1194,18 +1059,28 @@ function csarManager.update() -- every second
|
||||
csarMission.hoveringUnits[uName] = nil
|
||||
end
|
||||
trigger.action.outTextForGroup(uID, hoverMsg, 30, true)
|
||||
return -- only ever one winch op
|
||||
--return -- only ever one winch op
|
||||
else
|
||||
-- remove the hover indicator for this unit
|
||||
csarMission.hoveringUnits[uName] = nil
|
||||
end -- inside 2 * hover dist?
|
||||
|
||||
end -- has evacuee
|
||||
else
|
||||
-- somebody snatched the evacuee
|
||||
end -- if has evacuee
|
||||
end -- if in range
|
||||
end -- for all missions
|
||||
end -- if troop carrier
|
||||
end -- if exists
|
||||
end -- for all players
|
||||
-- now GC all missions if we lifted a pilot up (we no longer return after first succesful)
|
||||
if needsGC then
|
||||
local filtered = {}
|
||||
for idx, csarMission in pairs(csarManager.openMissions) do
|
||||
if csarMission.group then
|
||||
table.insert(filtered, csarMission)
|
||||
end
|
||||
end
|
||||
csarManager.openMissions = filtered
|
||||
end
|
||||
end -- if in Air
|
||||
end -- for all player units
|
||||
|
||||
-- now see and check if we need to spawn from a csar zone
|
||||
-- that has been told to spawn
|
||||
@ -1216,6 +1091,18 @@ function csarManager.update() -- every second
|
||||
-- local currVal = theZone:getFlagValue(theZone.startCSAR)
|
||||
-- if currVal ~= theZone.lastCSARVal then
|
||||
if theZone:testZoneFlag(theZone.startCSAR, theZone.triggerMethod, "lastCSARVal") then
|
||||
local theMission = csarManager.createCSARMissionFromZone(theZone)
|
||||
csarManager.addMission(theMission)
|
||||
--theZone.lastCSARVal = currVal
|
||||
if csarManager.verbose or theZone.verbose then
|
||||
trigger.action.outText("+++csar: started CSAR mission for <" .. theZone.csarName .. ">", 30)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function csarManager.createCSARMissionFromZone(theZone)
|
||||
-- set up random point in zone
|
||||
local mPoint = theZone:getPoint()
|
||||
if theZone.rndLoc then mPoint = theZone:createRandomPointInZone() end
|
||||
@ -1232,14 +1119,7 @@ function csarManager.update() -- every second
|
||||
theZone.csarMapMarker, -- mapMarker
|
||||
0.1, --theZone.radius) -- radius
|
||||
nil) -- parashoo unit
|
||||
csarManager.addMission(theMission)
|
||||
--theZone.lastCSARVal = currVal
|
||||
if csarManager.verbose then
|
||||
trigger.action.outText("+++csar: started CSAR mission " .. theZone.csarName, 30)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
return theMission
|
||||
end
|
||||
|
||||
--
|
||||
@ -1412,9 +1292,6 @@ function csarManager.readConfigZone()
|
||||
csarManager.name = "csarManagerConfig" -- compat with cfxZones
|
||||
local theZone = cfxZones.getZoneByName("csarManagerConfig")
|
||||
if not theZone then
|
||||
if csarManager.verbose then
|
||||
trigger.action.outText("+++csar: NO config zone!", 30)
|
||||
end
|
||||
theZone = cfxZones.createSimpleZone("csarManagerConfig")
|
||||
end
|
||||
csarManager.configZone = theZone -- save for flag banging compatibility
|
||||
@ -1487,20 +1364,6 @@ function csarManager.start()
|
||||
-- read config
|
||||
csarManager.readConfigZone()
|
||||
|
||||
-- install callbacks for helo-relevant events
|
||||
dcsCommon.addEventHandler(csarManager.somethingHappened, csarManager.preProcessor, csarManager.postProcessor)
|
||||
|
||||
-- now iterate through all player groups and install the CSAR Menu
|
||||
|
||||
local allPlayerGroups = cfxPlayerGroups -- cfxPlayerGroups is a global, don't fuck with it!
|
||||
-- contains per group a player record, use prime unit to access player's unit
|
||||
for gname, pgroup in pairs(allPlayerGroups) do
|
||||
local aUnit = pgroup.primeUnit -- get prime unit of that group
|
||||
csarManager.setCommsMenu(aUnit)
|
||||
end
|
||||
-- now install the new group notifier for new groups so we can remove and add CSAR menus
|
||||
cfxPlayer.addMonitor(csarManager.playerChangeEvent)
|
||||
|
||||
-- now scan all zones that are CSAR drop-off for quick access
|
||||
csarManager.processCSARBASE()
|
||||
|
||||
@ -1508,17 +1371,27 @@ function csarManager.start()
|
||||
-- and populate the available mission.
|
||||
csarManager.processCSARZones()
|
||||
|
||||
-- now call update so we can monitor progress of all helos, and alert them
|
||||
-- when they are close to a CSAR
|
||||
-- install callbacks for helo-relevant events
|
||||
--dcsCommon.addEventHandler(csarManager.somethingHappened, csarManager.preProcessor, csarManager.postProcessor)
|
||||
world.addEventHandler(csarManager)
|
||||
|
||||
-- now iterate through all player groups and install the CSAR Menu
|
||||
local allPlayerUnits = dcsCommon.getAllExistingPlayerUnitsRaw()
|
||||
for pName, aUnit in pairs(allPlayerUnits) do
|
||||
csarManager.setCommsMenu(aUnit)
|
||||
end
|
||||
|
||||
-- start updating and track all helicopters in the air against missions
|
||||
csarManager.update()
|
||||
|
||||
-- say hi!
|
||||
trigger.action.outText("cf/x CSAR v" .. csarManager.version .. " started", 30)
|
||||
trigger.action.outText("cf/x CSAR Manager v" .. csarManager.version .. " started", 30)
|
||||
return true
|
||||
end
|
||||
|
||||
-- let's get rolling
|
||||
if not csarManager.start() then
|
||||
trigger.action.outText("cf/x CSAR Manager v" .. csarManager.version .. " FAILED to run", 30)
|
||||
csarManager = nil
|
||||
end
|
||||
|
||||
@ -1540,5 +1413,6 @@ end
|
||||
|
||||
-- allow any airfied to be csarsafe by default, no longer *requires* csarbase
|
||||
|
||||
-- remove cfxPlayer dependency
|
||||
-- minFreq, maxFreq settings for config and mission-individual
|
||||
|
||||
--]]--
|
||||
@ -1,5 +1,5 @@
|
||||
dcsCommon = {}
|
||||
dcsCommon.version = "3.0.0"
|
||||
dcsCommon.version = "3.0.1"
|
||||
--[[-- VERSION HISTORY
|
||||
3.0.0 - removed bad bug in stringStartsWith, only relevant if caseSensitive is false
|
||||
- point2text new intsOnly option
|
||||
@ -8,6 +8,7 @@ dcsCommon.version = "3.0.0"
|
||||
- new pointInDirectionOfPointXYY()
|
||||
- createGroundGroupWithUnits now supports liveries
|
||||
- new getAllExistingPlayersAndUnits()
|
||||
3.0.1 - clone: better handling of string type
|
||||
--]]--
|
||||
|
||||
-- dcsCommon is a library of common lua functions
|
||||
@ -1187,6 +1188,9 @@ dcsCommon.version = "3.0.0"
|
||||
copy = tmp
|
||||
end
|
||||
end
|
||||
elseif orig_type == "string" then
|
||||
local tmp = ""
|
||||
copy = tmp .. orig
|
||||
else -- number, string, boolean, etc
|
||||
copy = orig
|
||||
end
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
factoryZone = {}
|
||||
factoryZone.version = "3.0.0"
|
||||
factoryZone.version = "3.1.0"
|
||||
factoryZone.verbose = false
|
||||
factoryZone.name = "factoryZone"
|
||||
|
||||
@ -11,6 +11,12 @@ factoryZone.name = "factoryZone"
|
||||
- use maxRadius from zone for spawning to support quad zones
|
||||
3.0.0 - support for liveries via "factoryLiveries" zone
|
||||
- OOP dmlZones
|
||||
3.1.0 - redD!, blueD!
|
||||
- redP!, blueP!
|
||||
- method
|
||||
- productionTime config synonyme
|
||||
- defendMe? attribute
|
||||
- triggered 'shocked' mode via defendMe
|
||||
|
||||
--]]--
|
||||
factoryZone.requiredLibs = {
|
||||
@ -104,16 +110,55 @@ function factoryZone.addFactoryZone(aZone)
|
||||
aZone.factoryTriggerMethod = aZone:getStringFromZoneProperty( "factoryTriggerMethod", "change")
|
||||
end
|
||||
|
||||
aZone.factoryMethod = aZone:getStringFromZoneProperty("factoryMethod", "inc")
|
||||
if aZone:hasProperty("method") then
|
||||
aZone.factoryMethod = aZone:getStringFromZoneProperty("method", "inc")
|
||||
end
|
||||
|
||||
if aZone:hasProperty("redP!") then
|
||||
aZone.redP = aZone:getStringFromZoneProperty("redP!", "none")
|
||||
end
|
||||
if aZone.redP and aZone.attackersRED ~= "none" then
|
||||
trigger.action.outText("***WARNING: factory <" .. aZone.name .. "> has RED production and uses 'redP!'", 30)
|
||||
end
|
||||
|
||||
if aZone:hasProperty("blueP!") then
|
||||
aZone.blueP = aZone:getStringFromZoneProperty("blueP!", "none")
|
||||
end
|
||||
if aZone.blueP and aZone.attackersBLUE ~= "none" then
|
||||
trigger.action.outText("***WARNING: factory <" .. aZone.name .. "> has BLUE production and uses 'blueP!'", 30)
|
||||
end
|
||||
|
||||
if aZone:hasProperty("redD!") then
|
||||
aZone.redD = aZone:getStringFromZoneProperty("redD!", "none")
|
||||
end
|
||||
if aZone.redD and aZone.defendersRED ~= "none" then
|
||||
trigger.action.outText("***WARNING: factory <" .. aZone.name .. "> has RED defenders and uses 'redD!'", 30)
|
||||
end
|
||||
|
||||
|
||||
if aZone:hasProperty("blueD!") then
|
||||
aZone.blueD = aZone:getStringFromZoneProperty("blueD!", "none")
|
||||
end
|
||||
if aZone.blueD and aZone.defendersBLUE ~= "none" then
|
||||
trigger.action.outText("***WARNING: factory <" .. aZone.name .. "> has BLUE defenders and uses 'blueD!'", 30)
|
||||
end
|
||||
|
||||
if aZone:hasProperty("defendMe?") then
|
||||
aZone.defendMe = aZone:getStringFromZoneProperty("defendMe?", "none")
|
||||
aZone.lastDefendMeValue = trigger.misc.getUserFlag(aZone.defendMe)
|
||||
end
|
||||
|
||||
factoryZone.zones[aZone.name] = aZone
|
||||
factoryZone.verifyZone(aZone)
|
||||
end
|
||||
|
||||
function factoryZone.verifyZone(aZone)
|
||||
-- do some sanity checks
|
||||
if not cfxGroundTroops and (aZone.attackersRED ~= "none" or aZone.attackersBLUE ~= "none") then
|
||||
trigger.action.outText("+++factZ: " .. aZone.name .. " attackers need cfxGroundTroops to function", 30)
|
||||
end
|
||||
|
||||
-- if not cfxGroundTroops and (aZone.attackersRED ~= "none" or aZone.attackersBLUE ~= "none") then
|
||||
-- now can also bang on flags, no more verification
|
||||
-- unless we want to beef them up
|
||||
-- end
|
||||
end
|
||||
|
||||
function factoryZone.spawnAttackTroops(theTypes, aZone, aCoalition, aFormation)
|
||||
@ -183,6 +228,7 @@ end
|
||||
--
|
||||
|
||||
function factoryZone.sendOutAttackers(aZone)
|
||||
|
||||
-- sanity check: never done for neutral zones
|
||||
if aZone.owner == 0 then
|
||||
if aZone.verbose or factoryZone.verbose then
|
||||
@ -193,16 +239,31 @@ function factoryZone.sendOutAttackers(aZone)
|
||||
|
||||
-- only spawn if there are zones to attack
|
||||
if not cfxOwnedZones.enemiesRemaining(aZone) then
|
||||
if factoryZone.verbose then
|
||||
if aZone.verbose or factoryZone.verbose then
|
||||
trigger.action.outText("+++factZ - no enemies, resting ".. aZone.name, 30)
|
||||
end
|
||||
return
|
||||
end
|
||||
|
||||
if factoryZone.verbose then
|
||||
if factoryZone.verbose or aZone.verbose then
|
||||
trigger.action.outText("+++factZ - attack cycle for ".. aZone.name, 30)
|
||||
end
|
||||
|
||||
-- bang on xxxP!
|
||||
if aZone.owner == 1 and aZone.redP then
|
||||
if aZone.verbose or factoryZone.verbose then
|
||||
trigger.action.outText("+++factZ: polling redP! <" .. aZone.redP .. "> for factrory <" .. aZone.name .. ">")
|
||||
end
|
||||
aZone:pollFlag(aZone.redP, aZone.factoryMethod)
|
||||
end
|
||||
|
||||
if aZone.owner == 2 and aZone.blueP then
|
||||
if aZone.verbose or factoryZone.verbose then
|
||||
trigger.action.outText("+++factZ: polling blueP! <" .. aZone.blueP .. "> for factrory <" .. aZone.name .. ">")
|
||||
end
|
||||
aZone:pollFlag(aZone.blueP, aZone.factoryMethod)
|
||||
end
|
||||
|
||||
-- step one: get the attackers
|
||||
local attackers = aZone.attackersRED;
|
||||
if (aZone.owner == 2) then attackers = aZone.attackersBLUE end
|
||||
@ -248,8 +309,23 @@ function factoryZone.repairDefenders(aZone)
|
||||
if (aZone.owner == 2) then defenders = aZone.defendersBLUE end
|
||||
local unitTypes = {} -- build type names
|
||||
|
||||
-- if none, we are done
|
||||
if defenders == "none" then return end
|
||||
-- if none, we are done, save for the outputs
|
||||
if (not defenders) or (defenders == "none") then
|
||||
if aZone.owner == 1 and aZone.redD then
|
||||
if aZone.verbose or factoryZone.verbose then
|
||||
trigger.action.outText("+++factZ: polling redD! <" .. aZone.redD .. "> for repair factory <" .. aZone.name .. ">", 30)
|
||||
end
|
||||
aZone:pollFlag(aZone.redD, aZone.factoryMethod)
|
||||
end
|
||||
|
||||
if aZone.owner == 2 and aZone.blueD then
|
||||
if aZone.verbose or factoryZone.verbose then
|
||||
trigger.action.outText("+++factZ: polling blueD! <" .. aZone.blueD .. "> for repair factory <" .. aZone.name .. ">", 30)
|
||||
end
|
||||
aZone:pollFlag(aZone.blueD, aZone.factoryMethod)
|
||||
end
|
||||
return
|
||||
end
|
||||
|
||||
-- split theTypes into an array of types
|
||||
allTypes = dcsCommon.trimArray(
|
||||
@ -308,6 +384,10 @@ end
|
||||
|
||||
function factoryZone.spawnDefenders(aZone)
|
||||
-- sanity check: never done for non-neutral zones
|
||||
if aZone.verbose or factoryZone.verbose then
|
||||
trigger.action.outText("+++factZ: starting defender cycle for <" .. aZone.name .. ">", 30)
|
||||
end
|
||||
|
||||
if aZone.owner == 0 then
|
||||
if aZone.verbose or factoryZone.verbose then
|
||||
trigger.action.outText("+++factZ: spawnDefenders invoked for NEUTRAL zone <" .. aZone.name .. ">", 30)
|
||||
@ -315,7 +395,21 @@ function factoryZone.spawnDefenders(aZone)
|
||||
return
|
||||
end
|
||||
|
||||
-- bang! on xxxD!
|
||||
local defenders = aZone.defendersRED;
|
||||
if aZone.owner == 1 and aZone.redD then
|
||||
if aZone.verbose or factoryZone.verbose then
|
||||
trigger.action.outText("+++factZ: polling redD! <" .. aZone.redD .. "> for factrory <" .. aZone.name .. ">", 30)
|
||||
end
|
||||
aZone:pollFlag(aZone.redD, aZone.factoryMethod)
|
||||
end
|
||||
|
||||
if aZone.owner == 2 and aZone.blueD then
|
||||
if aZone.verbose or factoryZone.verbose then
|
||||
trigger.action.outText("+++factZ: polling blueD! <" .. aZone.blueD .. "> for factory <" .. aZone.name .. ">", 30)
|
||||
end
|
||||
aZone:pollFlag(aZone.blueD, aZone.factoryMethod)
|
||||
end
|
||||
|
||||
if (aZone.owner == 2) then defenders = aZone.defendersBLUE end
|
||||
-- before we spawn new defenders, remove the old ones
|
||||
@ -388,7 +482,7 @@ function factoryZone.updateZoneProduction(aZone)
|
||||
aZone.defenders then
|
||||
-- we have defenders
|
||||
if aZone.defenders:isExist() then
|
||||
-- isee if group was damaged
|
||||
-- see if group was damaged
|
||||
if not aZone.lastDefenders then
|
||||
-- fresh group, probably from persistence, needs init
|
||||
aZone.lastDefenders = -1
|
||||
@ -458,7 +552,7 @@ function factoryZone.updateZoneProduction(aZone)
|
||||
if timer.getTime() > aZone.timeStamp + factoryZone.repairTime then
|
||||
aZone.timeStamp = timer.getTime()
|
||||
-- wait's up, repair one defender, then check if full strength
|
||||
factoryZone.repairDefenders(aZone)
|
||||
factoryZone.repairDefenders(aZone) -- will also bang on redD and blueD if present
|
||||
-- see if we are full strenght and if so go to attack, else set timer to reair the next unit
|
||||
if aZone.defenders and aZone.defenders:isExist() and aZone.defenders:getSize() >= aZone.defenders:getInitialSize() then
|
||||
-- we are at max size, time to produce some attackers
|
||||
@ -468,6 +562,13 @@ function factoryZone.updateZoneProduction(aZone)
|
||||
if factoryZone.verbose then
|
||||
trigger.action.outText("+++factZ: State " .. aZone.state .. " to " .. nextState .. " for " .. aZone.name, 30)
|
||||
end
|
||||
elseif (aZone.redD or aZone.blueD) then
|
||||
-- we start attacking cycle for out signal
|
||||
nextState = "attacking"
|
||||
aZone.timeStamp = timer.getTime()
|
||||
if factoryZone.verbose then
|
||||
trigger.action.outText("+++factZ: progessing tate " .. aZone.state .. " to " .. nextState .. " for " .. aZone.name .. " for redD/blueD", 30)
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
@ -540,6 +641,19 @@ function factoryZone.update()
|
||||
if theZone.activateFlag and cfxZones.testZoneFlag(theZone, theZone.activateFlag, theZone.factoryTriggerMethod, "lastActivateValue") then
|
||||
theZone.paused = false
|
||||
end
|
||||
|
||||
-- see if zone external defendMe was polled to bring it to
|
||||
-- shoked state
|
||||
if theZone.defendMe and theZone:testZoneFlag(theZone.defendMe, theZone.factoryTriggerMethod, "lastDefendMeValue") then
|
||||
if theZone.verbose or factoryZone.verbose then
|
||||
trigger.action.outText("+++factZ: setting factory <" .. theZone.name .. "> to shocked/produce defender mode", 30)
|
||||
end
|
||||
theZone.state = "shocked"
|
||||
theZone.timeStamp = timer.getTime()
|
||||
theZone.lastDefenders = 0
|
||||
theZone.defenders = nil -- nil, but no delete!
|
||||
end
|
||||
|
||||
-- do production for this zone
|
||||
factoryZone.updateZoneProduction(theZone)
|
||||
end -- iterating all zones
|
||||
@ -677,6 +791,9 @@ function factoryZone.readConfigZone(theZone)
|
||||
factoryZone.verbose = theZone.verbose
|
||||
factoryZone.defendingTime = theZone:getNumberFromZoneProperty( "defendingTime", 100)
|
||||
factoryZone.attackingTime = theZone:getNumberFromZoneProperty( "attackingTime", 300)
|
||||
if theZone:hasProperty("productionTime") then
|
||||
factoryZone.attackingTime = theZone:getNumberFromZoneProperty( "productionTime", 300)
|
||||
end
|
||||
factoryZone.shockTime = theZone:getNumberFromZoneProperty("shockTime", 200)
|
||||
factoryZone.repairTime = theZone:getNumberFromZoneProperty( "repairTime", 200)
|
||||
factoryZone.targetZones = "OWNED"
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
cfxObjectDestructDetector = {}
|
||||
cfxObjectDestructDetector.version = "2.0.0"
|
||||
cfxObjectDestructDetector.version = "2.0.2"
|
||||
cfxObjectDestructDetector.verbose = false
|
||||
cfxObjectDestructDetector.requiredLibs = {
|
||||
"dcsCommon", -- always
|
||||
@ -18,6 +18,10 @@ cfxObjectDestructDetector.requiredLibs = {
|
||||
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)
|
||||
--]]--
|
||||
|
||||
cfxObjectDestructDetector.objectZones = {}
|
||||
@ -78,8 +82,59 @@ function cfxObjectDestructDetector.processObjectDestructZone(aZone)
|
||||
elseif aZone:hasProperty("objectDestroyed!") then
|
||||
aZone.outDestroyFlag = aZone:getStringFromZoneProperty( "objectDestroyed!", "*none")
|
||||
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
|
||||
--
|
||||
@ -106,7 +161,7 @@ function cfxObjectDestructDetector:onEvent(event)
|
||||
-- invoke callbacks
|
||||
cfxObjectDestructDetector.invokeCallbacksFor(aZone)
|
||||
if aZone.verbose or cfxObjectDestructDetector.verbose then
|
||||
trigger.action.outText("OBJECT KILL: " .. id, 30)
|
||||
trigger.action.outText("OBJECT KILL: " .. matchMe .. " for odd <" .. aZone.name .. ">", 30)
|
||||
end
|
||||
-- save state for persistence
|
||||
aZone.isDestroyed = true
|
||||
|
||||
@ -12,6 +12,7 @@ cfxPlayerScore.firstSave = true -- to force overwrite
|
||||
- sceneryObject detection improvements
|
||||
- DCS 2.9 safe
|
||||
3.0.1 - cleanup
|
||||
3.0.2 - interface with ObjectDestructDetector for scoring scenery objects
|
||||
|
||||
--]]--
|
||||
|
||||
@ -208,8 +209,9 @@ function cfxPlayerScore.cat2BaseScore(inCat)
|
||||
return 1
|
||||
end
|
||||
|
||||
function cfxPlayerScore.object2score(inVictim) -- does not have group
|
||||
function cfxPlayerScore.object2score(inVictim, killSide) -- does not have group
|
||||
if not inVictim then return 0 end
|
||||
if not killSide then killSide = -1 end
|
||||
local inName = inVictim:getName()
|
||||
if dcsCommon.isSceneryObject(inVictim) then
|
||||
local desc = inVictim:getDesc()
|
||||
@ -217,6 +219,12 @@ function cfxPlayerScore.object2score(inVictim) -- does not have group
|
||||
-- same as object destruct detector to
|
||||
-- avoid ID changes
|
||||
inName = desc.typeName
|
||||
if cfxObjectDestructDetector then
|
||||
-- ask ODD if it knows the object and what score was
|
||||
-- awarded for a kill from that side
|
||||
local objectScore = cfxObjectDestructDetector.playerScoreForKill(inVictim, killSide)
|
||||
if objectScore then return objectScore end
|
||||
end
|
||||
end
|
||||
if not inName then return 0 end
|
||||
if type(inName) == "number" then
|
||||
@ -744,7 +752,7 @@ function cfxPlayerScore.killDetected(theEvent)
|
||||
if wasBuilding then
|
||||
-- these objects have no coalition; we simply award the score if
|
||||
-- it exists in look-up table.
|
||||
local staticScore = cfxPlayerScore.object2score(victim)
|
||||
local staticScore = cfxPlayerScore.object2score(victim, killSide)
|
||||
if staticScore > 0 then
|
||||
trigger.action.outSoundForCoalition(killSide, cfxPlayerScore.scoreSound)
|
||||
cfxPlayerScore.awardScoreTo(killSide, staticScore, killerName)
|
||||
@ -754,8 +762,10 @@ function cfxPlayerScore.killDetected(theEvent)
|
||||
end
|
||||
|
||||
-- was it fratricide?
|
||||
-- if we get here, it CANT be a scenery object
|
||||
-- but can be a static object, and stO have a coalition
|
||||
local vicSide = victim:getCoalition()
|
||||
local fraternicide = killSide == vicSide
|
||||
local fraternicide = (killSide == vicSide)
|
||||
local vicDesc = victim:getTypeName()
|
||||
local scoreMod = 1 -- start at one
|
||||
|
||||
@ -767,7 +777,7 @@ function cfxPlayerScore.killDetected(theEvent)
|
||||
-- static objects have no group
|
||||
local staticName = victim:getName() -- on statics, this returns
|
||||
-- name as entered in TOP LINE
|
||||
local staticScore = cfxPlayerScore.object2score(victim)
|
||||
local staticScore = cfxPlayerScore.object2score(victim, killSide)
|
||||
|
||||
if staticScore > 0 then
|
||||
-- this was a named static, return the score - unless our own
|
||||
@ -1488,6 +1498,7 @@ score zones
|
||||
- zones outside of which no scoring counts, but feats are still ok
|
||||
|
||||
- add take off feats
|
||||
- integrate with objectDestructDetector
|
||||
|
||||
can be extended with other, standalone feat modules that follow the
|
||||
same pattern, e.g. enter a zone, detect someone
|
||||
|
||||
@ -1,8 +1,8 @@
|
||||
-- theDebugger
|
||||
-- theDebugger 2.x
|
||||
debugger = {}
|
||||
debugger.version = "2.0.0"
|
||||
debugger.version = "2.1.0"
|
||||
debugDemon = {}
|
||||
debugDemon.version = "2.0.0"
|
||||
debugDemon.version = "2.1.0"
|
||||
|
||||
debugger.verbose = false
|
||||
debugger.ups = 4 -- every 0.25 second
|
||||
@ -33,6 +33,13 @@ debugger.log = ""
|
||||
- debuggerSpawnTypes zone
|
||||
- reading debuggerSpawnTypes
|
||||
- removed some silly bugs / inconsistencies
|
||||
2.1.0 - debugging code is now invoked deferred to avoid
|
||||
DCS crash after exiting. Debug code now executes
|
||||
outside of the event code's bracket.
|
||||
debug invocation on clone of data structure
|
||||
readback verification of flag set
|
||||
fixed getProperty() in debugger with zone
|
||||
|
||||
--]]--
|
||||
|
||||
debugger.requiredLibs = {
|
||||
@ -201,7 +208,7 @@ end
|
||||
function debugger.createDebuggerWithZone(theZone)
|
||||
-- watchflag input trigger
|
||||
theZone.debugInputMethod = theZone:getStringFromZoneProperty( "triggerMethod", "change")
|
||||
if theZone.hasProperty("debugTriggerMethod") then
|
||||
if theZone:hasProperty("debugTriggerMethod") then
|
||||
theZone.debugInputMethod = theZone:getStringFromZoneProperty("debugTriggerMethod", "change")
|
||||
elseif theZone:hasProperty("inputMethod") then
|
||||
theZone.debugInputMethod = theZone:getStringFromZoneProperty(theZone, "inputMethod", "change")
|
||||
@ -644,7 +651,8 @@ debugDemon.splitDelimiter = " "
|
||||
debugDemon.commandTable = {} -- key, value pair for command processing per keyword
|
||||
debugDemon.keepOpen = false -- keep mark open after a successful command
|
||||
debugDemon.snapshot = {}
|
||||
|
||||
debugDemon.activeIdx = -1 -- to detect if a window was close
|
||||
-- and prevent execution of debugger
|
||||
function debugDemon.hasMark(theString)
|
||||
-- check if the string begins with the sequece to identify commands
|
||||
if not theString then return false end
|
||||
@ -676,6 +684,7 @@ function debugDemon:onEvent(theEvent)
|
||||
end
|
||||
|
||||
if theEvent.id == world.event.S_EVENT_MARK_CHANGE then
|
||||
-- trigger.action.outText("debugger: Mark Change event received", 30)
|
||||
-- when changed, the mark's text is examined for a command
|
||||
-- if it starts with the 'mark' string ("-" by default) it is processed
|
||||
-- by the command processor
|
||||
@ -683,12 +692,19 @@ function debugDemon:onEvent(theEvent)
|
||||
-- else an error is displayed and the mark remains.
|
||||
if debugDemon.hasMark(theEvent.text) then
|
||||
-- strip the mark
|
||||
local commandString = theEvent.text:sub(1+debugDemon.markOfDemon:len())
|
||||
local cCommand = dcsCommon.clone(theEvent.text, true)
|
||||
local commandString = cCommand:sub(1+debugDemon.markOfDemon:len())
|
||||
-- break remainder apart into <command> <arg1> ... <argn>
|
||||
local commands = dcsCommon.splitString(commandString, debugDemon.splitDelimiter)
|
||||
|
||||
-- this is a command. process it and then remove it if it was executed successfully
|
||||
local success = debugDemon.executeCommand(commands, theEvent)
|
||||
local cTheEvent = dcsCommon.clone(theEvent, true) -- strip meta tables
|
||||
local args = {commands, cTheEvent}
|
||||
-- defer execution for 0.1s to get out of trx bracked
|
||||
timer.scheduleFunction(debugDemon.deferredDebug, args, timer.getTime() + 0.1)
|
||||
debugDemon.activeIdx = cTheEvent.idx
|
||||
--[[--
|
||||
local success = debugDemon.executeCommand(commands, cTheEvent) -- execute on a clone, not original
|
||||
|
||||
-- remove this mark after successful execution
|
||||
if success then
|
||||
@ -696,10 +712,32 @@ function debugDemon:onEvent(theEvent)
|
||||
else
|
||||
-- we could play some error sound
|
||||
end
|
||||
--]]--
|
||||
end
|
||||
end
|
||||
|
||||
if theEvent.id == world.event.S_EVENT_MARK_REMOVED then
|
||||
-- trigger.action.outText("Mark Remove received, removing idx <" .. theEvent.idx .. ">.", 30)
|
||||
debugDemon.activeIdx = nil
|
||||
end
|
||||
end
|
||||
|
||||
function debugDemon.deferredDebug(args)
|
||||
-- trigger.action.outText("enter deferred debug command", 30)
|
||||
-- if not debugDemon.activeIdx then
|
||||
-- trigger.action.outText("Debugger: window was closed, debug command ignored.", 30)
|
||||
-- return
|
||||
-- end
|
||||
local commands = args[1]
|
||||
local cTheEvent = args[2]
|
||||
local success = debugDemon.executeCommand(commands, cTheEvent) -- execute on a clone, not original
|
||||
|
||||
-- remove this mark after successful execution
|
||||
if success then
|
||||
trigger.action.removeMark(cTheEvent.idx)
|
||||
debugDemon.activeIdx = nil
|
||||
else
|
||||
-- we could play some error sound
|
||||
end
|
||||
end
|
||||
|
||||
@ -1103,6 +1141,11 @@ function debugDemon.processSetCommand(args, event)
|
||||
|
||||
debugger.outText("*** [" .. dcsCommon.nowString() .. "] debug: set flag <" .. theName .. "> to <" .. theVal .. ">" .. note, 30)
|
||||
|
||||
local newVal = trigger.misc.getUserFlag(theName)
|
||||
if theVal ~= newVal then
|
||||
debugger.outText("*** [" .. dcsCommon.nowString() .. "] debug: readback failure for flag <" .. theName .. ">: expected <" .. theVal .. ">, got <" .. newVal .. "!", 30)
|
||||
end
|
||||
|
||||
return true
|
||||
end
|
||||
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
williePete = {}
|
||||
williePete.version = "2.0.0"
|
||||
williePete.version = "2.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
|
||||
@ -17,6 +17,8 @@ williePete.requiredLibs = {
|
||||
2.0.0 - dmlZones, OOP
|
||||
- Guards for multi-unit player groups
|
||||
- getFirstLivingPlayerInGroupNamed()
|
||||
2.0.1 - added Harrier's FFAR M156 WP
|
||||
2.0.2 - hardened playerUpdate()
|
||||
--]]--
|
||||
|
||||
williePete.willies = {}
|
||||
@ -28,7 +30,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", "SNEB_TYPE254_H1_GREEN", "SNEB_TYPE254_H1_RED", "SNEB_TYPE254_H1_YELLOW"}
|
||||
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", "FFAR M156 WP"}
|
||||
|
||||
function williePete.addWillie(theWillie)
|
||||
table.insert(williePete.willies, theWillie)
|
||||
@ -617,6 +619,7 @@ function williePete.playerUpdate()
|
||||
-- make sure at least one unit still exists
|
||||
local dropUnit = true
|
||||
local theGroup = Group.getByName(unitInfo.gName)
|
||||
if theGroup then
|
||||
local allUnits = theGroup:getUnits()
|
||||
for idx, theUnit in pairs(allUnits) do
|
||||
--local theUnit = Unit.getByName(unitInfo.name)
|
||||
@ -631,6 +634,9 @@ function williePete.playerUpdate()
|
||||
end
|
||||
end
|
||||
end
|
||||
else
|
||||
trigger.action.outText("+++wp: strange issues with group <" .. gName .. ">, does not exist. Skipped in playerUpdate()", 30)
|
||||
end
|
||||
if dropUnit then
|
||||
-- all outside, remove from zone check-in
|
||||
-- williePete.doCheckOut(unitInfo)
|
||||
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
BIN
tutorial & demo missions/demo - Clone Factory.miz
Normal file
BIN
tutorial & demo missions/demo - Clone Factory.miz
Normal file
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Loading…
x
Reference in New Issue
Block a user