#CONTROLLABLE - CONTROLLABLE now caches Options set on the object wit SetOption() like Alarm state etc to avoid EVENT 61 (option change) spamming; HINT: this requires you to consistently change options only with Moose and NOT in your own scripts by e.g. getting the Controller Object and using the API directly.Mix'n' match will fail.

As a helper you can also use `QueryCachedOption(OptionID)` which will return cached values, if set by Moose.
This commit is contained in:
Applevangelist
2026-08-23 14:34:35 +02:00
parent 8aa5e38ba2
commit 42d1cdfcd7
@@ -14,6 +14,7 @@
--- @type CONTROLLABLE --- @type CONTROLLABLE
-- @field DCS#Controllable DCSControllable The DCS controllable class. -- @field DCS#Controllable DCSControllable The DCS controllable class.
-- @field #string ControllableName The name of the controllable. -- @field #string ControllableName The name of the controllable.
-- @field #table ControllableOptions The Cache of controllable options.
-- @extends Wrapper.Positionable#POSITIONABLE -- @extends Wrapper.Positionable#POSITIONABLE
--- Wrapper class to handle the "DCS Controllable objects", which are Groups and Units: --- Wrapper class to handle the "DCS Controllable objects", which are Groups and Units:
@@ -203,7 +204,8 @@ function CONTROLLABLE:New( ControllableName )
local self = BASE:Inherit( self, POSITIONABLE:New( ControllableName ) ) -- #CONTROLLABLE local self = BASE:Inherit( self, POSITIONABLE:New( ControllableName ) ) -- #CONTROLLABLE
-- self:F( ControllableName ) -- self:F( ControllableName )
self.ControllableName = ControllableName self.ControllableName = ControllableName
-- Options Cache for Event ID 61 (ED DCS Release Aug 2026)
self.ControllableOptions = {}
self.TaskScheduler = SCHEDULER:New( self ) self.TaskScheduler = SCHEDULER:New( self )
return self return self
end end
@@ -3282,15 +3284,44 @@ end
-- @return #CONTROLLABLE self -- @return #CONTROLLABLE self
function CONTROLLABLE:SetOption( OptionID, OptionValue ) function CONTROLLABLE:SetOption( OptionID, OptionValue )
local DCSControllable = self:GetDCSObject() local setnewoption = false
if DCSControllable then
local Controller = self:_GetController()
Controller:setOption( OptionID, OptionValue ) -- Check if option has changed against cached option
local ID = tostring(OptionID)
return self if self.ControllableOptions then
if (self.ControllableOptions[ID] ~= OptionValue) or (self.ControllableOptions[ID]==nil) then
setnewoption = true
self.ControllableOptions[ID] = OptionValue
--self:I(string.format("CONTROLLABLE %s: Option CHANGE for option %d: New value: %s!",self.ControllableName, OptionID, tostring(OptionValue)))
end
end end
-- change option if changed
if setnewoption == true then
local DCSControllable = self:GetDCSObject()
if DCSControllable then
local Controller = self:_GetController()
--self:I(string.format("CONTROLLABLE %s: Setting OPTION %d: to value: %s!",self.ControllableName, OptionID, tostring(OptionValue)))
Controller:setOption( OptionID, OptionValue )
return self
end
--else
--self:I(string.format("CONTROLLABLE %s: Option NO CHANGE for option %d: Same value: %s!",self.ControllableName, OptionID, tostring(OptionValue)))
end
return nil
end
--- Query a (cached) option. Requires the option has been set with Moose(!) before.
-- @param #CONTROLLABLE self
-- @param #number OptionID ID/Type of the option.
-- @return #number OptionValue or nil if not chached (yet)
function CONTROLLABLE:QueryCachedOption(OptionID)
local ID = tostring(OptionID)
if self.ControllableOptions then
return self.ControllableOptions[ID]
end
return nil return nil
end end
@@ -3307,11 +3338,11 @@ function CONTROLLABLE:OptionROE( ROEvalue )
local Controller = self:_GetController() local Controller = self:_GetController()
if self:IsAir() then if self:IsAir() then
Controller:setOption( AI.Option.Air.id.ROE, ROEvalue ) self:SetOption( AI.Option.Air.id.ROE, ROEvalue )
elseif self:IsGround() then elseif self:IsGround() then
Controller:setOption( AI.Option.Ground.id.ROE, ROEvalue ) self:SetOption( AI.Option.Ground.id.ROE, ROEvalue )
elseif self:IsShip() then elseif self:IsShip() then
Controller:setOption( AI.Option.Naval.id.ROE, ROEvalue ) self:SetOption( AI.Option.Naval.id.ROE, ROEvalue )
end end
return self return self
@@ -3349,11 +3380,11 @@ function CONTROLLABLE:OptionROEHoldFire()
local Controller = self:_GetController() local Controller = self:_GetController()
if self:IsAir() then if self:IsAir() then
Controller:setOption( AI.Option.Air.id.ROE, AI.Option.Air.val.ROE.WEAPON_HOLD ) self:SetOption( AI.Option.Air.id.ROE, AI.Option.Air.val.ROE.WEAPON_HOLD )
elseif self:IsGround() then elseif self:IsGround() then
Controller:setOption( AI.Option.Ground.id.ROE, AI.Option.Ground.val.ROE.WEAPON_HOLD ) self:SetOption( AI.Option.Ground.id.ROE, AI.Option.Ground.val.ROE.WEAPON_HOLD )
elseif self:IsShip() then elseif self:IsShip() then
Controller:setOption( AI.Option.Naval.id.ROE, AI.Option.Naval.val.ROE.WEAPON_HOLD ) self:SetOption( AI.Option.Naval.id.ROE, AI.Option.Naval.val.ROE.WEAPON_HOLD )
end end
return self return self
@@ -3391,11 +3422,11 @@ function CONTROLLABLE:OptionROEReturnFire()
local Controller = self:_GetController() local Controller = self:_GetController()
if self:IsAir() then if self:IsAir() then
Controller:setOption( AI.Option.Air.id.ROE, AI.Option.Air.val.ROE.RETURN_FIRE ) self:SetOption( AI.Option.Air.id.ROE, AI.Option.Air.val.ROE.RETURN_FIRE )
elseif self:IsGround() then elseif self:IsGround() then
Controller:setOption( AI.Option.Ground.id.ROE, AI.Option.Ground.val.ROE.RETURN_FIRE ) self:SetOption( AI.Option.Ground.id.ROE, AI.Option.Ground.val.ROE.RETURN_FIRE )
elseif self:IsShip() then elseif self:IsShip() then
Controller:setOption( AI.Option.Naval.id.ROE, AI.Option.Naval.val.ROE.RETURN_FIRE ) self:SetOption( AI.Option.Naval.id.ROE, AI.Option.Naval.val.ROE.RETURN_FIRE )
end end
return self return self
@@ -3433,11 +3464,11 @@ function CONTROLLABLE:OptionROEOpenFire()
local Controller = self:_GetController() local Controller = self:_GetController()
if self:IsAir() then if self:IsAir() then
Controller:setOption( AI.Option.Air.id.ROE, AI.Option.Air.val.ROE.OPEN_FIRE ) self:SetOption( AI.Option.Air.id.ROE, AI.Option.Air.val.ROE.OPEN_FIRE )
elseif self:IsGround() then elseif self:IsGround() then
Controller:setOption( AI.Option.Ground.id.ROE, AI.Option.Ground.val.ROE.OPEN_FIRE ) self:SetOption( AI.Option.Ground.id.ROE, AI.Option.Ground.val.ROE.OPEN_FIRE )
elseif self:IsShip() then elseif self:IsShip() then
Controller:setOption( AI.Option.Naval.id.ROE, AI.Option.Naval.val.ROE.OPEN_FIRE ) self:SetOption( AI.Option.Naval.id.ROE, AI.Option.Naval.val.ROE.OPEN_FIRE )
end end
return self return self
@@ -3476,7 +3507,7 @@ function CONTROLLABLE:OptionROEOpenFireWeaponFree()
local Controller = self:_GetController() local Controller = self:_GetController()
if self:IsAir() then if self:IsAir() then
Controller:setOption( AI.Option.Air.id.ROE, AI.Option.Air.val.ROE.OPEN_FIRE_WEAPON_FREE ) self:SetOption( AI.Option.Air.id.ROE, AI.Option.Air.val.ROE.OPEN_FIRE_WEAPON_FREE )
end end
return self return self
@@ -3514,7 +3545,7 @@ function CONTROLLABLE:OptionROEWeaponFree()
local Controller = self:_GetController() local Controller = self:_GetController()
if self:IsAir() then if self:IsAir() then
Controller:setOption( AI.Option.Air.id.ROE, AI.Option.Air.val.ROE.WEAPON_FREE ) self:SetOption( AI.Option.Air.id.ROE, AI.Option.Air.val.ROE.WEAPON_FREE )
end end
return self return self
@@ -3552,7 +3583,7 @@ function CONTROLLABLE:OptionROTNoReaction()
local Controller = self:_GetController() local Controller = self:_GetController()
if self:IsAir() then if self:IsAir() then
Controller:setOption( AI.Option.Air.id.REACTION_ON_THREAT, AI.Option.Air.val.REACTION_ON_THREAT.NO_REACTION ) self:SetOption( AI.Option.Air.id.REACTION_ON_THREAT, AI.Option.Air.val.REACTION_ON_THREAT.NO_REACTION )
end end
return self return self
@@ -3573,7 +3604,7 @@ function CONTROLLABLE:OptionROT( ROTvalue )
local Controller = self:_GetController() local Controller = self:_GetController()
if self:IsAir() then if self:IsAir() then
Controller:setOption( AI.Option.Air.id.REACTION_ON_THREAT, ROTvalue ) self:SetOption( AI.Option.Air.id.REACTION_ON_THREAT, ROTvalue )
end end
return self return self
@@ -3611,7 +3642,7 @@ function CONTROLLABLE:OptionROTPassiveDefense()
local Controller = self:_GetController() local Controller = self:_GetController()
if self:IsAir() then if self:IsAir() then
Controller:setOption( AI.Option.Air.id.REACTION_ON_THREAT, AI.Option.Air.val.REACTION_ON_THREAT.PASSIVE_DEFENCE ) self:SetOption( AI.Option.Air.id.REACTION_ON_THREAT, AI.Option.Air.val.REACTION_ON_THREAT.PASSIVE_DEFENCE )
end end
return self return self
@@ -3631,7 +3662,7 @@ function CONTROLLABLE:OptionPreferVerticalLanding()
local Controller = self:_GetController() local Controller = self:_GetController()
if self:IsAir() then if self:IsAir() then
Controller:setOption( AI.Option.Air.id.PREFER_VERTICAL, true ) self:SetOption( AI.Option.Air.id.PREFER_VERTICAL, true )
end end
return self return self
@@ -3651,7 +3682,7 @@ function CONTROLLABLE:OptionAllowFormationSideSwap()
local Controller = self:_GetController() local Controller = self:_GetController()
if self:IsAir() then if self:IsAir() then
Controller:setOption( AI.Option.Air.id.ALLOW_FORMATION_SIDE_SWAP, true ) self:SetOption( AI.Option.Air.id.ALLOW_FORMATION_SIDE_SWAP, true )
end end
return self return self
@@ -3671,7 +3702,7 @@ function CONTROLLABLE:OptionAIRunwayLineUp()
local Controller = self:_GetController() local Controller = self:_GetController()
if self:IsAir() then if self:IsAir() then
Controller:setOption( 37, true ) self:SetOption( 37, true )
end end
return self return self
@@ -3691,7 +3722,7 @@ function CONTROLLABLE:OptionDisengageAndRTBAfterFormationLoss()
local Controller = self:_GetController() local Controller = self:_GetController()
if self:IsAir() then if self:IsAir() then
Controller:setOption( 38, 1 ) self:SetOption( 38, 1 )
end end
return self return self
@@ -3729,7 +3760,7 @@ function CONTROLLABLE:OptionROTEvadeFire()
local Controller = self:_GetController() local Controller = self:_GetController()
if self:IsAir() then if self:IsAir() then
Controller:setOption( AI.Option.Air.id.REACTION_ON_THREAT, AI.Option.Air.val.REACTION_ON_THREAT.EVADE_FIRE ) self:SetOption( AI.Option.Air.id.REACTION_ON_THREAT, AI.Option.Air.val.REACTION_ON_THREAT.EVADE_FIRE )
end end
return self return self
@@ -3767,7 +3798,7 @@ function CONTROLLABLE:OptionROTVertical()
local Controller = self:_GetController() local Controller = self:_GetController()
if self:IsAir() then if self:IsAir() then
Controller:setOption( AI.Option.Air.id.REACTION_ON_THREAT, AI.Option.Air.val.REACTION_ON_THREAT.BYPASS_AND_ESCAPE ) self:SetOption( AI.Option.Air.id.REACTION_ON_THREAT, AI.Option.Air.val.REACTION_ON_THREAT.BYPASS_AND_ESCAPE )
end end
return self return self
@@ -3787,10 +3818,10 @@ function CONTROLLABLE:OptionAlarmStateAuto()
local Controller = self:_GetController() local Controller = self:_GetController()
if self:IsGround() then if self:IsGround() then
Controller:setOption( AI.Option.Ground.id.ALARM_STATE, AI.Option.Ground.val.ALARM_STATE.AUTO ) self:SetOption( AI.Option.Ground.id.ALARM_STATE, AI.Option.Ground.val.ALARM_STATE.AUTO )
elseif self:IsShip() then elseif self:IsShip() then
-- Controller:setOption(AI.Option.Naval.id.ALARM_STATE, AI.Option.Naval.val.ALARM_STATE.AUTO) -- self:SetOption(AI.Option.Naval.id.ALARM_STATE, AI.Option.Naval.val.ALARM_STATE.AUTO)
Controller:setOption( 9, 0 ) self:SetOption( 9, 0 )
end end
return self return self
@@ -3810,11 +3841,11 @@ function CONTROLLABLE:OptionAlarmStateGreen()
local Controller = self:_GetController() local Controller = self:_GetController()
if self:IsGround() then if self:IsGround() then
Controller:setOption( AI.Option.Ground.id.ALARM_STATE, AI.Option.Ground.val.ALARM_STATE.GREEN ) self:SetOption( AI.Option.Ground.id.ALARM_STATE, AI.Option.Ground.val.ALARM_STATE.GREEN )
elseif self:IsShip() then elseif self:IsShip() then
-- AI.Option.Naval.id.ALARM_STATE does not seem to exist! -- AI.Option.Naval.id.ALARM_STATE does not seem to exist!
-- Controller:setOption( AI.Option.Naval.id.ALARM_STATE, AI.Option.Naval.val.ALARM_STATE.GREEN ) -- self:SetOption( AI.Option.Naval.id.ALARM_STATE, AI.Option.Naval.val.ALARM_STATE.GREEN )
Controller:setOption( 9, 1 ) self:SetOption( 9, 1 )
end end
return self return self
@@ -3834,10 +3865,10 @@ function CONTROLLABLE:OptionAlarmStateRed()
local Controller = self:_GetController() local Controller = self:_GetController()
if self:IsGround() then if self:IsGround() then
Controller:setOption( AI.Option.Ground.id.ALARM_STATE, AI.Option.Ground.val.ALARM_STATE.RED ) self:SetOption( AI.Option.Ground.id.ALARM_STATE, AI.Option.Ground.val.ALARM_STATE.RED )
elseif self:IsShip() then elseif self:IsShip() then
-- Controller:setOption(AI.Option.Naval.id.ALARM_STATE, AI.Option.Naval.val.ALARM_STATE.RED) -- self:SetOption(AI.Option.Naval.id.ALARM_STATE, AI.Option.Naval.val.ALARM_STATE.RED)
Controller:setOption( 9, 2 ) self:SetOption( 9, 2 )
end end
return self return self
@@ -3864,7 +3895,7 @@ function CONTROLLABLE:OptionRTBBingoFuel( RTB ) -- R2.2
local Controller = self:_GetController() local Controller = self:_GetController()
if self:IsAir() then if self:IsAir() then
Controller:setOption( AI.Option.Air.id.RTB_ON_BINGO, RTB ) self:SetOption( AI.Option.Air.id.RTB_ON_BINGO, RTB )
end end
return self return self
@@ -3885,7 +3916,7 @@ function CONTROLLABLE:OptionRTBAmmo( WeaponsFlag )
local Controller = self:_GetController() local Controller = self:_GetController()
if self:IsAir() then if self:IsAir() then
Controller:setOption( AI.Option.Air.id.RTB_ON_OUT_OF_AMMO, WeaponsFlag ) self:SetOption( AI.Option.Air.id.RTB_ON_OUT_OF_AMMO, WeaponsFlag )
end end
return self return self
@@ -3905,7 +3936,7 @@ function CONTROLLABLE:OptionAllowJettisonWeaponsOnThreat()
local Controller = self:_GetController() local Controller = self:_GetController()
if self:IsAir() then if self:IsAir() then
Controller:setOption( AI.Option.Air.id.PROHIBIT_JETT, false ) self:SetOption( AI.Option.Air.id.PROHIBIT_JETT, false )
end end
return self return self
@@ -3925,7 +3956,7 @@ function CONTROLLABLE:OptionKeepWeaponsOnThreat()
local Controller = self:_GetController() local Controller = self:_GetController()
if self:IsAir() then if self:IsAir() then
Controller:setOption( AI.Option.Air.id.PROHIBIT_JETT, true ) self:SetOption( AI.Option.Air.id.PROHIBIT_JETT, true )
end end
return self return self
@@ -3965,7 +3996,7 @@ function CONTROLLABLE:OptionEvasionOfARM(Seconds)
if self:IsGround() then if self:IsGround() then
if Seconds == nil then Seconds = false end if Seconds == nil then Seconds = false end
Controller:setOption( AI.Option.Ground.id.EVASION_OF_ARM, Seconds) self:SetOption( AI.Option.Ground.id.EVASION_OF_ARM, Seconds)
end end
end end
@@ -3986,7 +4017,7 @@ function CONTROLLABLE:OptionFormationInterval(meters)
if self:IsGround() then if self:IsGround() then
if meters == nil or meters > 100 or meters < 0 then meters = 50 end if meters == nil or meters > 100 or meters < 0 then meters = 50 end
Controller:setOption( 30, meters) self:SetOption( 30, meters)
end end
end end
@@ -4006,7 +4037,7 @@ function CONTROLLABLE:OptionECM( ECMvalue )
local Controller = self:_GetController() local Controller = self:_GetController()
if self:IsAir() then if self:IsAir() then
Controller:setOption( AI.Option.Air.id.ECM_USING, ECMvalue or 1 ) self:SetOption( AI.Option.Air.id.ECM_USING, ECMvalue or 1 )
end end
end end
@@ -4192,11 +4223,11 @@ function CONTROLLABLE:OptionRestrictBurner( RestrictBurner )
-- Issue https://github.com/FlightControl-Master/MOOSE/issues/1216 -- Issue https://github.com/FlightControl-Master/MOOSE/issues/1216
if RestrictBurner == true then if RestrictBurner == true then
if self:IsAir() then if self:IsAir() then
Controller:setOption( 16, true ) self:SetOption( 16, true )
end end
else else
if self:IsAir() then if self:IsAir() then
Controller:setOption( 16, false ) self:SetOption( 16, false )
end end
end end