Overheating - Fix issues from release (#8617)

* move overheating cookoff into separate function

* move heatCoef and require mission restart for setting change

- move heatCoef to a more sensible place
- require mission restart for heatCoef setting change (it gets cached per ammo type)

* add exit to ammo temp loop if cookoffCoef is changed to 0 mid-mission

- add exit to ammo temp loop if cookoffCoef is changed to 0 mid-mission, this prevents an issue where all weapon cookoff regardless of temp, because required temp gets multiplied by cookoffCoef which has been set to 0.

* file end new line

* update header for ace_overheating_fnc_cookoffWeapon

* use ambientTemperature as floor for weapon and ammo temp

* add coolingCoef setting

* improve feature documentation

* add fnc_cookoffWeapon to XEH_PREP

* add type of jam to ace_weaponJammed local event

- add type of jam to ace_weaponJammed local event
- fix #8637

* fix misspelling

Co-authored-by: TyroneMF <TyroneMF@hotmail.com>

* clear all weapon heat on death

* Update addons/overheating/functions/fnc_updateTemperature.sqf

Co-authored-by: GhostIsSpooky <69561145+Salluci@users.noreply.github.com>

* deprecate ace_overheating_fnc_getBarrelMass, cache weapon bolt and barrel mass values

- cache closed bolt value by moving config look up to ace_overheating_fnc_getWeaponData
- cache barrel mass value by moving calculation from ace_overheating_fnc_getBarrelMass to ace_overheating_fnc_getWeaponData
- deprecate ace_overheating_fnc_getBarrelMass to be a wrapper for ace_overheating_fnc_getWeaponData that only returns barrel mass

* add public functions to get and set weapon and ammo temperature

* add `canCoolWeaponWithItem` function, workaround for #8657

* Apply suggestions from code review

Co-authored-by: PabstMirror <pabstmirror@gmail.com>

* add coef setting for addition heat from suppressor

* Update fnc_overheat.sqf

* improve fnc_canCoolWeaponWithItem

* remove extra (

* Move canCoolWeaponWithItem action code to function

* Use hashmaps and reset on settings change

* Apply suggestions from code review

Co-authored-by: jonpas <jonpas33@gmail.com>

Co-authored-by: TyroneMF <TyroneMF@hotmail.com>
Co-authored-by: GhostIsSpooky <69561145+Salluci@users.noreply.github.com>
Co-authored-by: PabstMirror <pabstmirror@gmail.com>
Co-authored-by: jonpas <jonpas33@gmail.com>
This commit is contained in:
Drofseh 2021-11-08 10:06:31 -08:00 committed by GitHub
parent 5bad6899cb
commit 99c85e3c12
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
23 changed files with 323 additions and 100 deletions

View File

@ -38,7 +38,7 @@ class CfgVehicles {
};
class GVAR(CoolWeaponWithItem) {
displayName = CSTRING(CoolWeaponWithItem);
condition = QUOTE(GVAR(enabled) && {isClass (configfile >> 'CfgPatches' >> 'acex_field_rations')});
condition = QUOTE(call FUNC(canCoolWeaponWithItem));
exceptions[] = {"isNotInside", "isNotSwimming", "isNotSitting"};
statement = "true";
showDisabled = 0;
@ -66,7 +66,7 @@ class CfgVehicles {
};
class GVAR(CoolWeaponWithItem) {
displayName = CSTRING(CoolWeaponWithItem);
condition = QUOTE(GVAR(enabled) && {isClass (configfile >> 'CfgPatches' >> 'acex_field_rations')});
condition = QUOTE(call FUNC(canCoolWeaponWithItem));
exceptions[] = {"isNotInside", "isNotSwimming", "isNotSitting"};
statement = "true";
showDisabled = 0;

View File

@ -6,19 +6,24 @@ PREP(canCheckSpareBarrelsTemperatures);
PREP(checkSpareBarrelsTemperatures);
PREP(checkTemperature);
PREP(clearJam);
PREP(cookoffWeapon);
PREP(coolWeaponWithItem);
PREP(coolWeaponWithWaterSource);
PREP(displayTemperature);
PREP(firedEH);
PREP(getAmmoTemperature);
PREP(getBarrelMass);
PREP(getConsumableChildren);
PREP(getWeaponData);
PREP(getWeaponTemperature);
PREP(handleTakeEH);
PREP(handleRespawn);
PREP(jamWeapon);
PREP(loadCoolestSpareBarrel);
PREP(overheat);
PREP(sendSpareBarrelsTemperaturesHint);
PREP(setAmmoTemperature);
PREP(setWeaponTemperature);
PREP(swapBarrel);
PREP(swapBarrelAssistant);
PREP(swapBarrelCallback);

View File

@ -44,9 +44,9 @@ if (hasInterface) then {
if !(hasInterface) exitWith {};
GVAR(cacheWeaponData) = call CBA_fnc_createNamespace;
GVAR(cacheAmmoData) = call CBA_fnc_createNamespace;
GVAR(cacheSilencerData) = call CBA_fnc_createNamespace;
GVAR(cacheWeaponData) = createHashMap;
GVAR(cacheAmmoData) = createHashMap;
GVAR(cacheSilencerData) = createHashMap;
//Add Take EH if required
if (GVAR(unJamOnReload) || {GVAR(cookoffCoef) > 0}) then {
@ -76,8 +76,18 @@ if (hasInterface) then {
}] call CBA_fnc_addClassEventHandler;
};
// Reset all weapon heat to ambient on death to prevent cookoffs when a unit respawns.
["CAManBase", "Killed", {
params ["_unit"];
{
_unit setVariable [_x, ambientTemperature select 0];
} forEach (_unit getVariable [QGVAR(trackedWeapons), []]);
_unit setVariable [QGVAR(trackedWeapons), []];
}] call CBA_fnc_addClassEventHandler;
// Install event handler to display temp when a barrel was swapped
[QGVAR(showWeaponTemperature), DFUNC(displayTemperature)] call CBA_fnc_addEventHandler;
// Install event handler to initiate an assisted barrel swap
[QGVAR(initiateSwapBarrelAssisted), DFUNC(swapBarrel)] call CBA_fnc_addEventHandler;
@ -107,5 +117,4 @@ if (hasInterface) then {
[]
] call CBA_fnc_waitUntilAndExecute;
};
}] call CBA_fnc_addEventHandler;

View File

@ -19,9 +19,12 @@
params ["_temperature", "_barrelMass", "_totalTime"];
if (_temperature < 1) exitWith {0};
// The lowest temperature a weapon can reach is the ambient air temperature.
private _ambientTemperature = ambientTemperature select 0;
// If a long time passed since the last shot, there's no need to calculate anything; the weapon should be cool
if (_totalTime > 1800) exitWith {0};
if (_totalTime > 1800) exitWith {_ambientTemperature};
if (_temperature <= _ambientTemperature) exitWith {_ambientTemperature};
//AR-15 (0.00570m bullet diameter) (barrel diameter usually 0.75" or 0.008255m radius)
//Steel Denisty = 7850 m^3 / kg
@ -51,9 +54,9 @@ while {true} do {
_convectionRate * _barrelSurface * _temperature
// Radiative cooling
+ 0.4 * 5.67e-8 * _barrelSurface * ((_temperature + 273.15) ^ 4 - 273.15 ^ 4)
) * _deltaTime / (_barrelMass * 466);
) * GVAR(coolingCoef) * _deltaTime / (_barrelMass * 466);
if (_temperature < 1) exitWith {0};
if (_temperature <= _ambientTemperature) exitWith {_ambientTemperature};
if (isNil "_temperature") exitWith {
diag_log text format ["[ACE] ERROR: _totalTime = %1; _time = %2; _deltaTime = %3;", _totalTime, _time, _deltaTime];
@ -61,5 +64,5 @@ while {true} do {
};
_time = _time + _deltaTime;
if (_time >= _totalTime) exitWith { _temperature max 0 };
if (_time >= _totalTime) exitWith {_temperature max _ambientTemperature};
};

View File

@ -0,0 +1,27 @@
#include "script_component.hpp"
/*
* Author: drofseh
* Return true if the target's weapon can be cooled with an item in the player's inventory
*
* Arguments:
* 0: Target <OBJECT>
* 1: Player <OBJECT>
*
* Return Value:
* Bool <BOOL>
*
* Example:
* [cursorObject, player] call ace_overheating_fnc_canCoolWeaponWithItem
*
* Public: No
*/
params ["_unit", "_player"];
TRACE_2("canCoolWeaponWithItem",_unit,_player);
GVAR(enabled)
&& {isClass (configfile >> "CfgPatches" >> "acex_field_rations")}
&& {!(_unit getVariable [QEGVAR(captives,isSurrendering), false])} // interaction point will overlap with ace_captives
&& {!(_unit getVariable [QEGVAR(captives,isHandcuffed), false])}
&& {[_unit, currentWeapon _unit] call FUNC(getWeaponTemperature) > (ambientTemperature select 0)}
&& {((_player call EFUNC(common,uniqueItems)) findIf {getNumber (configFile >> "CfgWeapons" >> _x >> QEXGVAR(field_rations,thirstQuenched)) > 0}) != -1}

View File

@ -0,0 +1,66 @@
#include "script_component.hpp"
/*
* Author: drofseh
* Cookoff loaded round.
*
* Arguments:
* 0: Unit <OBJECT>
* 1: Weapon <STRING>
* 2: Is Weapon Jammed <BOOL>
* 3: Type of Jam <STRING>
*
* Return Value:
* None
*
* Example:
* [player, currentWeapon player, true, "Fire"] call ace_overheating_fnc_cookoffWeapon
*
* Public: No
*/
params ["_unit", "_weapon", "_canUnjam", "_jamType"];
TRACE_4("params",_unit,_weapon,_canUnjam,_jamType);
// a weapon with a failure to fire or dud type jam will be unjammed from cooking off
// this is first so that the fired event from the cookoff can also cause a jam
if (_canUnjam && {_jamType in ["Fire","Dud"]}) then {
[_unit, currentMuzzle _unit, true] call FUNC(clearJam);
// clearJam will remove a dud round, but so will the forced fire, so give back the lost round and shoot it
if (_jamType isEqualTo "Dud") then {
_unit setAmmo [_weapon, (_unit ammo _weapon) + 1];
};
};
// get valid mode and muzzle for the main weapon, we don't want the cookoff to come from an underbarrel launcher
([_weapon] call FUNC(getWeaponData)) params ["", "", "", "_modes", "_muzzle", "_reloadTime"];
// get an appropriate firemode and muzzle, cache the current muzzle
// trying to match firemodes and switching back to the cached muzzle will hide the change from the player and prevent unexpected mode/muzzle changes (going from full auto to semi auto, or from underbarrel GL to rifle for example)
private _muzzleCache = currentMuzzle _unit;
private _mode = currentWeaponMode _unit;
if !(_mode in _modes) then {
_mode = _modes select 0;
};
// delay cookoff to ensure any previous animation from a fired event is finished
[
{
params ["_unit", "_mode", "_muzzle", "_muzzleCache"];
// fire the cookoff
_unit forceWeaponFire [_muzzle, _mode];
// switch back to the cached muzzle if required
if (_muzzle != _muzzleCache) then {
_unit selectWeapon _muzzleCache;
};
[
[localize LSTRING(WeaponCookedOff)],
true // allows the hint to be overwritten by another hint, such as a jam or another cookoff
] call CBA_fnc_notify;
},
[_unit, _mode, _muzzle, _muzzleCache],
_reloadTime
] call CBA_fnc_waitAndExecute;

View File

@ -61,7 +61,7 @@ private _fnc_onSuccess = {
};
// cool the weapon
private _barrelMass = _weapon call FUNC(getBarrelMass);
([_weapon] call FUNC(getWeaponData)) params ["", "", "", "", "", "", "", "_barrelMass"];
_temperature = [_temperature, _barrelMass, _liquidAmount * 10] call FUNC(calculateCooling);
[_target, _tempVarName, _temperature, TEMP_TOLERANCE] call EFUNC(common,setApproximateVariablePublic);
};

View File

@ -59,7 +59,7 @@ private _fnc_condition = {
};
//Cool the weapon down
private _barrelMass = _weapon call FUNC(getBarrelMass);
([_weapon] call FUNC(getWeaponData)) params ["", "", "", "", "", "", "", "_barrelMass"];
_temperature = [_temperature, _barrelMass, 20] call FUNC(calculateCooling);
[_player, _tempVarName, _temperature, TEMP_TOLERANCE] call EFUNC(common,setApproximateVariablePublic);

View File

@ -0,0 +1,23 @@
#include "script_component.hpp"
/*
* Author: drofseh
* Get current temperature of weapon's ammo.
*
* Arguments:
* 0: Unit <OBJECT>
* 1: Weapon <STRING>
*
* Return Value:
* Current ammunition temperature <NUMBER>
*
* Example:
* [player, currentWeapon player] call ace_overheating_fnc_getAmmoTemperature
*
* Public: Yes
*/
params ["_unit", "_weapon"];
private _ammoTempVarName = format [QGVAR(%1_ammoTemp), _weapon];
_unit getVariable [_ammoTempVarName, ambientTemperature select 0]

View File

@ -17,4 +17,6 @@
params ["_weapon"];
METAL_MASS_RATIO * (getNumber (configFile >> "CfgWeapons" >> _weapon >> "WeaponSlotsInfo" >> "mass") / 22.0) max 1.0;
([_weapon] call FUNC(getWeaponData)) params ["", "", "", "", "", "", "", "_barrelMass"];
_barrelMass

View File

@ -7,9 +7,14 @@
* 0: weapon type <STRING>
*
* Return Value:
* 0: dispresion <NUMBER>
* 0: dispersion <NUMBER>
* 1: slowdownFactor <NUMBER>
* 2: jamChance <NUMBER>
* 3: modes <ARRAY>
* 4: muzzle <STRING>
* 5: reloadTime <NUMBER>
* 6: closedBolt <NUMBER>
* 7: barrelMass <NUMBER>
*
* Example:
* ["gun"] call ace_overheating_fnc_getWeaponData
@ -20,7 +25,7 @@
params ["_weapon"];
// Look in the cache first
private _weaponData = GVAR(cacheWeaponData) getVariable _weapon;
private _weaponData = GVAR(cacheWeaponData) get _weapon;
if (!isNil "_weaponData") exitWith {_weaponData};
// Search the config
@ -71,11 +76,16 @@ private _muzzle = getArray (configFile >> "CfgWeapons" >> _weapon >> "muzzles")
if (_muzzle == "this") then {
_muzzle = _weapon;
};
private _reloadTime = getNumber (configfile >> "CfgWeapons" >> _weapon >> (_modes select 0) >> "reloadTime");
private _closedBolt = getNumber (configFile >> "CfgWeapons" >> _weapon >> QGVAR(closedBolt));
private _barrelMass = METAL_MASS_RATIO * (getNumber (configFile >> "CfgWeapons" >> _weapon >> "WeaponSlotsInfo" >> "mass") / 22.0) max 1.0;
// Cache the values
_weaponData = [_dispersion, _slowdownFactor, _jamChance, _modes, _muzzle, _reloadTime];
_weaponData = [_dispersion, _slowdownFactor, _jamChance, _modes, _muzzle, _reloadTime, _closedBolt, _barrelMass];
TRACE_2("building cache",_weapon,_weaponData);
GVAR(cacheWeaponData) setVariable [_weapon, _weaponData];
GVAR(cacheWeaponData) set [_weapon, _weaponData];
_weaponData

View File

@ -0,0 +1,23 @@
#include "script_component.hpp"
/*
* Author: drofseh
* Get current temperature of weapon.
*
* Arguments:
* 0: Unit <OBJECT>
* 1: Weapon <STRING>
*
* Return Value:
* Current ammunition temperature <NUMBER>
*
* Example:
* [player, currentWeapon player] call ace_overheating_fnc_getWeaponTemperature
*
* Public: Yes
*/
params ["_unit", "_weapon"];
private _weaponTempVarName = format [QGVAR(%1_temp), _weapon];
_unit getVariable [_weaponTempVarName, ambientTemperature select 0];

View File

@ -51,7 +51,9 @@ if (_jamTypesAllowed isEqualTo []) then {
};
};
_unit setVariable [format [QGVAR(%1_jamType), _weapon], selectRandomWeighted _jamTypesAllowed];
private _jamType = selectRandomWeighted _jamTypesAllowed;
_unit setVariable [format [QGVAR(%1_jamType), _weapon], _jamType];
// Stop current burst
_unit setAmmo [_weapon, 0];
@ -72,7 +74,7 @@ if (_weapon == primaryWeapon _unit) then {
// only display the hint once, after you try to shoot an already jammed weapon
GVAR(knowAboutJam) = false;
["ace_weaponJammed", [_unit,_weapon]] call CBA_fnc_localEvent;
["ace_weaponJammed", [_unit, _weapon, _jamType]] call CBA_fnc_localEvent;
if (_unit getVariable [QGVAR(JammingActionID), -1] == -1) then {

View File

@ -26,9 +26,9 @@ TRACE_4("params",_unit,_weapon,_ammo,_projectile);
BEGIN_COUNTER(overheat);
// Get bullet parameters
private _energyIncrement = GVAR(cacheAmmoData) getVariable _ammo;
private _energyIncrement = GVAR(cacheAmmoData) get _ammo;
if (isNil "_energyIncrement") then {
_bulletMass = getNumber (configFile >> "CfgAmmo" >> _ammo >> "ACE_BulletMass");
private _bulletMass = getNumber (configFile >> "CfgAmmo" >> _ammo >> "ACE_BulletMass");
if (_bulletMass == 0) then {
// If the bullet mass is not configured, estimate it
_bulletMass = 3.4334 + 0.5171 * (getNumber (configFile >> "CfgAmmo" >> _ammo >> "hit") + getNumber (configFile >> "CfgAmmo" >> _ammo >> "caliber"));
@ -38,28 +38,27 @@ if (isNil "_energyIncrement") then {
// Ref: https://en.wikipedia.org/wiki/Physics_of_firearms
// Muzzle Engergy = 1/2 * m * v^2 = (1/2 * 0.001 g/kg * bulletMass (grams) * v^2)
// Multiple by 3 becase we only calc every 3rd bullet: (3 * 1/2 * 0.001) = 0.0015
_energyIncrement = 0.0015 * _bulletMass * (vectorMagnitudeSqr velocity _projectile);
_energyIncrement = GVAR(heatCoef) * 0.0015 * _bulletMass * (vectorMagnitudeSqr velocity _projectile);
GVAR(cacheAmmoData) setVariable [_ammo, _energyIncrement];
GVAR(cacheAmmoData) set [_ammo, _energyIncrement];
};
// Increase overheating depending on how obstrusive is the current supressor,
// if any. Typical arma supressors have visibleFire=0.5 and audibleFire=0.3,
// so they produce x2.1 overheating
private _silencer = switch (_weapon) do {
private _suppressor = switch (_weapon) do {
case (primaryWeapon _unit) : {(primaryWeaponItems _unit) select 0};
case (handgunWeapon _unit) : {(handgunItems _unit) select 0};
default {""};
};
if (_silencer != "") then {
private _silencerCoef = GVAR(cacheSilencerData) getVariable _silencer;
if (isNil "_silencerCoef") then {
_silencerCoef = 1 +
(1 - getNumber (configFile >> "CfgWeapons" >> _silencer >> "ItemInfo" >> "AmmoCoef" >> "audibleFire")) +
(1 - getNumber (configFile >> "CfgWeapons" >> _silencer >> "ItemInfo" >> "AmmoCoef" >> "visibleFire"));
GVAR(cacheSilencerData) setVariable [_silencer, _silencerCoef];
if (_suppressor != "" && {GVAR(suppressorCoef) > 0}) then {
private _suppressorCoef = GVAR(cacheSilencerData) get _suppressor;
if (isNil "_suppressorCoef") then {
private _config = configFile >> "CfgWeapons" >> _suppressor >> "ItemInfo" >> "AmmoCoef";
_suppressorCoef = GVAR(suppressorCoef) * (1 + (1 - getNumber (_config >> "audibleFire")) + (1 - getNumber (_config >> "visibleFire")));
GVAR(cacheSilencerData) set [_suppressor, _suppressorCoef];
};
_energyIncrement = _energyIncrement * _silencerCoef;
_energyIncrement = _energyIncrement * _suppressorCoef;
};
TRACE_1("heat",_energyIncrement);

View File

@ -0,0 +1,24 @@
#include "script_component.hpp"
/*
* Author: drofseh
* Set weapon's ammo to specific temperature.
*
* Arguments:
* 0: Unit <OBJECT>
* 1: Weapon <STRING>
* 2: Temperature <STRING>
*
* Return Value:
* None
*
* Example:
* [player, currentWeapon player] call ace_overheating_fnc_setAmmoTemperature
*
* Public: Yes
*/
params ["_unit", "_weapon", "_temp"];
private _ammoTempVarName = format [QGVAR(%1_ammoTemp), _weapon];
_unit setVariable [_ammoTempVarName, _temp];

View File

@ -0,0 +1,24 @@
#include "script_component.hpp"
/*
* Author: drofseh
* Set weapon to specific temperature.
*
* Arguments:
* 0: Unit <OBJECT>
* 1: Weapon <STRING>
* 2: Temperature <STRING>
*
* Return Value:
* None
*
* Example:
* [player, currentWeapon player] call ace_overheating_fnc_setWeaponTemperature
*
* Public: Yes
*/
params ["_unit", "_weapon", "_temp"];
private _ammoTempVarName = format [QGVAR(%1_ammoTemp), _weapon];
_unit setVariable [_ammoTempVarName, _temp];

View File

@ -34,7 +34,7 @@ if (GVAR(unJamOnSwapBarrel) && {[_gunner] call FUNC(canUnjam)}) then {
[localize LSTRING(SwappedBarrel), QPATHTOF(UI\spare_barrel_ca.paa)] call EFUNC(common,displayTextPicture);
private _temp = _gunner getVariable [format [QGVAR(%1_temp), _weapon], 0];
private _barrelMass = _weapon call FUNC(getBarrelMass);
([_weapon] call FUNC(getWeaponData)) params ["", "", "", "", "", "", "", "_barrelMass"];
// Instruct the server to load the coolest spare barrel into the weapon and
// store the removed barrel with the former weapon temperature. The server

View File

@ -9,7 +9,7 @@
* 2: Barrel Temperature <STRING>
*
* Return Value:
* Current ammunition temperature <NUMBER>
* New temperature <NUMBER>
*
* Example:
* [player, currentWeapon player, 600] call ace_overheating_fnc_updateAmmoTemperature
@ -20,23 +20,25 @@
params ["_unit", "_weapon", "_barrelTemperature"];
TRACE_3("params",_unit,_weapon,_barrelTemperature);
private _closedBolt = getNumber (configFile >> "CfgWeapons" >> _weapon >> QGVAR(closedBolt));
([_weapon] call FUNC(getWeaponData)) params ["", "", "", "", "", "", "_closedBolt"];
private _canUnjam = [_unit] call FUNC(canUnjam);
private _jamType = _unit getVariable [format [QGVAR(%1_jamType), _weapon], "None"];
// Skip if no ammo in chamber
if (
_unit ammo _weapon < 1
// closed bolt, and jammed and type not failure to fire
|| {_closedBolt == 1 && {_canUnjam} && {!(_unit getVariable [format [QGVAR(%1_jamType), _weapon], "None"] in ["Fire","Dud"])}}
|| {_closedBolt == 1 && {_canUnjam} && {!(_jamType in ["Fire","Dud"])}}
// open bolt, and not jammed, or jammed and type not failure to fire
|| {_closedBolt == 0 && {!_canUnjam || {_canUnjam && {!(_unit getVariable [format [QGVAR(%1_jamType), _weapon], "None"] in ["Fire","Dud"])}}}}
|| {_closedBolt == 0 && {!_canUnjam || {_canUnjam && {!(_jamType in ["Fire","Dud"])}}}}
) exitWith {
_unit setVariable [format [QGVAR(%1_ammoTemp), _weapon], 0];
0
private _ambientTemperature = ambientTemperature select 0;
_unit setVariable [format [QGVAR(%1_ammoTemp), _weapon], _ambientTemperature];
_ambientTemperature
};
private _ammoTempVarName = format [QGVAR(%1_ammoTemp), _weapon];
private _ammoTemperature = _unit getVariable [_ammoTempVarName, 0];
private _ammoTemperature = _unit getVariable [_ammoTempVarName, ambientTemperature select 0];
// heat or cool the ammo
if (_ammoTemperature < _barrelTemperature) then {
@ -47,58 +49,12 @@ if (_ammoTemperature < _barrelTemperature) then {
_ammoTemperature = _barrelTemperature;
};
// check for cook off
// cookoff if too hot
if (_ammoTemperature > (GUNPOWDER_IGNITION_TEMP * GVAR(cookoffCoef))) then {
[_unit, _weapon, _canUnjam, _jamType] call FUNC(cookoffWeapon);
// a weapon with a failure to fire or dud type jam will be unjammed from cooking off
// this is first so that the fired event from the cookoff can also cause a jam
private _jamType = _unit getVariable [format [QGVAR(%1_jamType), _weapon], "None"];
if (_canUnjam && {_jamType in ["Fire","Dud"]}) then {
[_unit, currentMuzzle _unit, true] call FUNC(clearJam);
// clearJam will remove a dud round, but so will the forced fire, so give back the lost round and shoot it
if (_jamType isEqualTo "Dud") then {
private _ammo = _unit ammo _weapon;
_unit setAmmo [_weapon, _ammo + 1];
};
};
// get valid mode and muzzle for the main weapon, we don't want the cookoff to come from an underbarrel launcher
([_weapon] call FUNC(getWeaponData)) params ["", "", "", "_modes", "_muzzle", "_reloadTime"];
// get an appropriate firemode and muzzle, cache the current muzzle
// trying to match firemodes and switching back to the cached muzzle will hide the change from the player and prevent unexpected mode/muzzle changes (going from full auto to semi auto, or from underbarrel GL to rifle for example)
private _muzzleCache = currentMuzzle _unit;
private _mode = currentWeaponMode _unit;
if !(_mode in _modes) then {
_mode = _modes select 0;
};
// delay cookoff to ensure any previous animation from a fired event is finished
[
{
params ["_unit", "_muzzleCache", "_mode", "_muzzle"];
// fire the cookoff
_unit forceWeaponFire [_muzzle, _mode];
// switch back to the cached muzzle if required
if (_muzzle != _muzzleCache) then {
_unit selectWeapon _muzzleCache;
};
[
[localize LSTRING(WeaponCookedOff)],
true // allows the hint to be overwritten by another hint, such as a jam or another cookoff
] call CBA_fnc_notify;
},
[_unit, _muzzleCache, _mode, _muzzle],
_reloadTime
] call CBA_fnc_waitAndExecute;
// if the cookoff happened then the next round should start at 0
_ammoTemperature = 0;
// since a cookoff happened then the next round should start at the ambient temperature.
_ammoTemperature = ambientTemperature select 0;
};
_unit setVariable [_ammoTempVarName, _ammoTemperature];

View File

@ -15,6 +15,11 @@
* Public: No
*/
// If the ace_overheating_cookoffCoef setting is set to 0 mid mission we want to exit right away or it will immediate cause all player weapons to cook off.
if (GVAR(cookoffCoef) isEqualTo 0) exitWith {
WARNING_1("'%1' has been set to 0 mid mission. Changing this setting requires mission restart.",GVAR(cookoffCoef));
};
private _currentWeapon = currentWeapon ACE_player;
if ((_currentWeapon != "") && {_currentWeapon == primaryWeapon ACE_player || {_currentWeapon == handgunWeapon ACE_player}}) then {
private _temperature = ACE_player getVariable [format [QGVAR(%1_temp), _currentWeapon], 0];

View File

@ -9,7 +9,7 @@
* 2: Heat increment (J) <NUMBER>
*
* Return Value:
* Current temperature <NUMBER>
* New temperature <NUMBER>
*
* Example:
* [player, currentWeapon player, 2000] call ace_overheating_fnc_updateTemperature
@ -27,18 +27,23 @@ private _timeVarName = format [QGVAR(%1_time), _weapon];
private _temperature = _unit getVariable [_tempVarName, 0];
private _lastTime = _unit getVariable [_timeVarName, 0];
private _barrelMass = _weapon call FUNC(getBarrelMass);
// keep track of weapons that have heat, so they can be set to ambient temperaure on killed/respawn
private _trackedWeapons = _unit getVariable [QGVAR(trackedWeapons), []];
_trackedWeapons pushBackUnique _tempVarName;
_unit setVariable [QGVAR(trackedWeapons), _trackedWeapons];
// Calculate cooling
([_weapon] call FUNC(getWeaponData)) params ["", "", "", "", "", "", "", "_barrelMass"];
_temperature = [_temperature, _barrelMass, CBA_missionTime - _lastTime] call FUNC(calculateCooling);
TRACE_1("cooledTo",_temperature);
// Calculate heating
// Steel Heat Capacity = 466 J/(Kg.K)
_temperature = _temperature + _heatIncrement * GVAR(heatCoef) / (_barrelMass * 466);
_temperature = _temperature + _heatIncrement / (_barrelMass * 466);
// Publish the temperature variable
[_unit, _tempVarName, _temperature, TEMP_TOLERANCE] call EFUNC(common,setApproximateVariablePublic);
// Store the update time locally
_unit setVariable [_timeVarName, CBA_missionTime];

View File

@ -15,7 +15,37 @@ private _category = format ["ACE %1", localize LSTRING(DisplayName)];
[LSTRING(heatCoef_displayName), LSTRING(heatCoef_description)],
_category,
[0, 5, 1, 2],
1
1,
{
if (!GVAR(enabled)) exitWith {};
TRACE_2("reseting cache",GVAR(heatCoef),count GVAR(cacheAmmoData));
GVAR(cacheAmmoData) = createHashMap;
},
false
] call CBA_fnc_addSetting;
[
QGVAR(coolingCoef), "SLIDER",
[LSTRING(coolingCoef_displayName), LSTRING(coolingCoef_description)],
_category,
[0, 5, 1, 2],
1,
{},
true
] call CBA_fnc_addSetting;
[
QGVAR(suppressorCoef), "SLIDER",
[LSTRING(suppressorCoef_displayName), LSTRING(suppressorCoef_description)],
_category,
[0, 5, 1, 2],
1,
{
if (!GVAR(enabled)) exitWith {};
TRACE_2("reseting cache",GVAR(suppressorCoef),count GVAR(cacheSilencerData));
GVAR(cacheSilencerData) = createHashMap;
},
false
] call CBA_fnc_addSetting;
[

View File

@ -62,6 +62,18 @@
<German>Koeffizient für die Menge an Hitze, die eine Waffe pro Schuss erzeugt.\nHöhere Werte beschleunigen die Erhitzung.</German>
<Polish>Współczynnik wpływający na ilość ciepła generowanego przez broń przy każdym strzale.</Polish>
</Key>
<Key ID="STR_ACE_Overheating_coolingCoef_displayName">
<English>Cooling Coefficient</English>
</Key>
<Key ID="STR_ACE_Overheating_coolingCoef_description">
<English>Coefficient for how quickly a weapon cools down.\nHigher value increases cooling speed.</English>
</Key>
<Key ID="STR_ACE_Overheating_suppressorCoef_displayName">
<English>Suppressor Coefficient</English>
</Key>
<Key ID="STR_ACE_Overheating_suppressorCoef_description">
<English>Coefficient for how much additional heat is added from having a suppressor attached.\nHigher value increases heat, 0 means no additional heat from the suppressor.</English>
</Key>
<Key ID="STR_ACE_Overheating_DisplayTextOnJam_displayName">
<English>Display Text on Jam</English>
<German>Zeige Text bei Ladehemmung</German>
@ -274,8 +286,7 @@
<English>Unjam on Barrel Swap</English>
<Japanese>銃身交換で弾詰まり解消</Japanese>
<French>Désenrayer l'arme au changement de canon</French>
<Russian>Замена ствола устраняет заклинивание оружия.</Russian>
<German>Ladehemmungen bei Lauf-Wechsel beheben</German>
<Russian>Замена ствола устраняет заклинивание оружия.</Russian>
<Polish>Usuń zacięcie przy wymianie lufy</Polish>
</Key>
<Key ID="STR_ACE_Overheating_unJamOnSwapBarrel_description">

View File

@ -53,12 +53,11 @@ Jams can be cleared in the following ways:
- Select `Equipment`.
- Select `Check weapon temperature`.
**NOTE** When the bar is half full (yellow) it means the barrel is around 500°c.
Your weapon will be even more prone to jams, and it'll get worse if you don't let the barrel cool down or swap it.
**NOTE** Each section on the bar represents 100°C. When the bar reaches 2 sections weapons can start to cookoff. When it is half full (yellow) it means the barrel is around 500°C. Your weapon will be even more prone to jams, and it'll get worse if you don't let the barrel cool down or swap it.
### 2.4 Cooling your weapon
- Weapons and spare barrels will cool off over time.
- Weapons and spare barrels will cool off over time, down to the ambient temperature in the mission.
- Cooling speed of weapons in increased in windy or rainy weather, and when swimming.
- If ACE Field Rations is loaded then weapons can be cooled with canteens, water bottles, or other beverage items. This does not require the Field Rations system to be enabled.
- If ACE Field Rations is enabled then weapons can also be cooled with the same water sources used to refill canteens and water bottles.