2023-09-12 18:58:10 +00:00
#include "..\script_component.hpp"
2021-09-03 19:39:52 +00:00
/*
2023-08-16 23:18:01 +00:00
* Author: tcvm
2021-09-03 19:39:52 +00:00
* Toggle speed limiter for Driver in Plane. Uses a simple PID controller to manage thrust
*
* Arguments:
* 0: Driver <OBJECT>
* 1: Vehicle <OBJECT>
2023-06-28 14:17:14 +00:00
* 2: Preserve Speed Limit <BOOL>
2021-09-03 19:39:52 +00:00
*
* Return Value:
* None
*
* Example:
* [player, car] call ace_vehicles_fnc_autoThrottle
*
* Public: No
*/
#define PID_P 1
#define PID_I 0.3
#define PID_D 0
#define EPSILON 0.001
2023-06-28 14:17:14 +00:00
params ["_driver", "_vehicle", ["_preserveSpeedLimit", false]];
2021-09-03 19:39:52 +00:00
if (GVAR(isSpeedLimiter)) exitWith {
[localize LSTRING(Off)] call EFUNC(common,displayTextStructured);
playSound "ACE_Sound_Click";
GVAR(isSpeedLimiter) = false;
};
[localize LSTRING(On)] call EFUNC(common,displayTextStructured);
playSound "ACE_Sound_Click";
GVAR(isSpeedLimiter) = true;
2023-06-28 14:17:14 +00:00
GVAR(isCruiseControl) = true; // enables SET/RESUME
2021-09-03 19:39:52 +00:00
2023-06-28 14:17:14 +00:00
if (!_preserveSpeedLimit) then {
// Convert forward speed to KM/H. `speed _vehicle` isnt accurate enough for this controller to work well, so its easier to use M/S. The system assumes it is KM/H so we need the conversion
GVAR(speedLimit) = (((velocityModelSpace _vehicle) select 1) * 3.6) max 5;
};
2021-09-03 19:39:52 +00:00
[{
params ["_args", "_idPFH"];
_args params ["_driver", "_vehicle", "_autothrottleParameters"];
_autothrottleParameters params ["_lastVelocity", "_integralValue", "_lastTime", "_lastThrottleValue", "_throttleLogValue"];
// this will take into account game being pausesd
private _deltaTime = CBA_missionTime - _lastTime;
2023-06-28 14:17:14 +00:00
private _role = _driver call EFUNC(common,getUavControlPosition);
if (GVAR(isUAV)) then {
if (_role != "DRIVER") then {
GVAR(isSpeedLimiter) = false;
};
} else {
if (_driver != currentPilot _vehicle || {_role != ""}) then {
GVAR(isSpeedLimiter) = false;
};
2021-09-03 19:39:52 +00:00
};
if (_throttleLogValue == 0) then {
_throttleLogValue = 1;
};
private _currentThrottle = (airplaneThrottle _vehicle) ^ _throttleLogValue;
if (_lastThrottleValue != -1 && { EPSILON < abs (_currentThrottle - _lastThrottleValue) }) then {
// player/script has moved throttle, stop limiting speed
// ARMA will allow an increment of one throttle unit per frame, so if there is a difference between our known throttle value and actual throttle value, the player must of changed it
[localize LSTRING(Off)] call EFUNC(common,displayTextStructured);
playSound "ACE_Sound_Click";
GVAR(isSpeedLimiter) = false;
};
2023-06-28 14:17:14 +00:00
if (call CBA_fnc_getActiveFeatureCamera != "") then {
GVAR(isSpeedLimiter) = false;
};
2021-09-03 19:39:52 +00:00
if (!GVAR(isSpeedLimiter)) exitWith {
[_idPFH] call CBA_fnc_removePerFrameHandler;
};
if (_deltaTime == 0) exitWith {};
private _forwardVelocity = (velocityModelSpace _vehicle) select 1;
// convert from KM/H to M/S
private _velocityError = (GVAR(speedLimit) / 3.6) - _forwardVelocity;
// strictly speaking this would work better if this error was time to zero acceleration. I can't find the acceleration values in config, however, so this works instead
private _errorDiff = _velocityError - _lastVelocity;
private _p = PID_P * _velocityError;
private _i = _integralValue + (PID_I * _errorDiff * _deltaTime);
private _d = PID_D * _errorDiff / _deltaTime;
private _outputBeforeSaturation = _p + _i + _d;
private _throttle = 0 max (_outputBeforeSaturation min 1);
// if we are saturated, we clamp the integral value to avoid integral windup
if (_outputBeforeSaturation != _throttle) then {
// saturated
_i = _integralValue;
_throttle = 0 max ((_p + _d) min 1);
};
_vehicle setAirplaneThrottle _throttle;
_autothrottleParameters set [0, _d];
_autothrottleParameters set [1, _i];
_autothrottleParameters set [2, CBA_missionTime];
_autothrottleParameters set [3, _throttle];
}, 0, [_driver, _vehicle, [0, 0, CBA_missionTime, -1, getNumber (configOf _vehicle >> "throttleToThrustLogFactor")]]] call CBA_fnc_addPerFrameHandler;