diff --git a/addons/overheating/CfgVehicles.hpp b/addons/overheating/CfgVehicles.hpp index 5a854699e2..e17f030a2b 100644 --- a/addons/overheating/CfgVehicles.hpp +++ b/addons/overheating/CfgVehicles.hpp @@ -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; diff --git a/addons/overheating/XEH_PREP.hpp b/addons/overheating/XEH_PREP.hpp index f4b56c3c3f..5a1d3c5387 100644 --- a/addons/overheating/XEH_PREP.hpp +++ b/addons/overheating/XEH_PREP.hpp @@ -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); diff --git a/addons/overheating/XEH_postInit.sqf b/addons/overheating/XEH_postInit.sqf index fbd0f94f1c..66bb7bebfd 100644 --- a/addons/overheating/XEH_postInit.sqf +++ b/addons/overheating/XEH_postInit.sqf @@ -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; diff --git a/addons/overheating/functions/fnc_calculateCooling.sqf b/addons/overheating/functions/fnc_calculateCooling.sqf index 8fd39c7024..db65fb1d2a 100644 --- a/addons/overheating/functions/fnc_calculateCooling.sqf +++ b/addons/overheating/functions/fnc_calculateCooling.sqf @@ -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}; }; diff --git a/addons/overheating/functions/fnc_canCoolWeaponWithItem.sqf b/addons/overheating/functions/fnc_canCoolWeaponWithItem.sqf new file mode 100644 index 0000000000..ba31bbebc5 --- /dev/null +++ b/addons/overheating/functions/fnc_canCoolWeaponWithItem.sqf @@ -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 + * 1: Player + * + * Return Value: + * 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} diff --git a/addons/overheating/functions/fnc_cookoffWeapon.sqf b/addons/overheating/functions/fnc_cookoffWeapon.sqf new file mode 100644 index 0000000000..ae2311243c --- /dev/null +++ b/addons/overheating/functions/fnc_cookoffWeapon.sqf @@ -0,0 +1,66 @@ +#include "script_component.hpp" +/* + * Author: drofseh + * Cookoff loaded round. + * + * Arguments: + * 0: Unit + * 1: Weapon + * 2: Is Weapon Jammed + * 3: Type of Jam + * + * 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; diff --git a/addons/overheating/functions/fnc_coolWeaponWithItem.sqf b/addons/overheating/functions/fnc_coolWeaponWithItem.sqf index 7c3bf50aa9..3a8091584b 100644 --- a/addons/overheating/functions/fnc_coolWeaponWithItem.sqf +++ b/addons/overheating/functions/fnc_coolWeaponWithItem.sqf @@ -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); }; diff --git a/addons/overheating/functions/fnc_coolWeaponWithWaterSource.sqf b/addons/overheating/functions/fnc_coolWeaponWithWaterSource.sqf index 2e142602ec..2ed62e02f3 100644 --- a/addons/overheating/functions/fnc_coolWeaponWithWaterSource.sqf +++ b/addons/overheating/functions/fnc_coolWeaponWithWaterSource.sqf @@ -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); diff --git a/addons/overheating/functions/fnc_getAmmoTemperature.sqf b/addons/overheating/functions/fnc_getAmmoTemperature.sqf new file mode 100644 index 0000000000..9855335101 --- /dev/null +++ b/addons/overheating/functions/fnc_getAmmoTemperature.sqf @@ -0,0 +1,23 @@ +#include "script_component.hpp" +/* + * Author: drofseh + * Get current temperature of weapon's ammo. + * + * Arguments: + * 0: Unit + * 1: Weapon + * + * Return Value: + * Current ammunition temperature + * + * 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] diff --git a/addons/overheating/functions/fnc_getBarrelMass.sqf b/addons/overheating/functions/fnc_getBarrelMass.sqf index 1504442a60..3dab9cae89 100644 --- a/addons/overheating/functions/fnc_getBarrelMass.sqf +++ b/addons/overheating/functions/fnc_getBarrelMass.sqf @@ -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 diff --git a/addons/overheating/functions/fnc_getWeaponData.sqf b/addons/overheating/functions/fnc_getWeaponData.sqf index c5cfb40d4a..2a9dc406b9 100644 --- a/addons/overheating/functions/fnc_getWeaponData.sqf +++ b/addons/overheating/functions/fnc_getWeaponData.sqf @@ -7,9 +7,14 @@ * 0: weapon type * * Return Value: - * 0: dispresion + * 0: dispersion * 1: slowdownFactor * 2: jamChance + * 3: modes + * 4: muzzle + * 5: reloadTime + * 6: closedBolt + * 7: barrelMass * * 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 diff --git a/addons/overheating/functions/fnc_getWeaponTemperature.sqf b/addons/overheating/functions/fnc_getWeaponTemperature.sqf new file mode 100644 index 0000000000..7aa49d9d0b --- /dev/null +++ b/addons/overheating/functions/fnc_getWeaponTemperature.sqf @@ -0,0 +1,23 @@ +#include "script_component.hpp" +/* + * Author: drofseh + * Get current temperature of weapon. + * + * Arguments: + * 0: Unit + * 1: Weapon + * + * Return Value: + * Current ammunition temperature + * + * 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]; diff --git a/addons/overheating/functions/fnc_jamWeapon.sqf b/addons/overheating/functions/fnc_jamWeapon.sqf index ea77e11ba4..3f6f0838c3 100644 --- a/addons/overheating/functions/fnc_jamWeapon.sqf +++ b/addons/overheating/functions/fnc_jamWeapon.sqf @@ -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 { diff --git a/addons/overheating/functions/fnc_overheat.sqf b/addons/overheating/functions/fnc_overheat.sqf index f1eb7b16c0..d63dfe919d 100644 --- a/addons/overheating/functions/fnc_overheat.sqf +++ b/addons/overheating/functions/fnc_overheat.sqf @@ -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); diff --git a/addons/overheating/functions/fnc_setAmmoTemperature.sqf b/addons/overheating/functions/fnc_setAmmoTemperature.sqf new file mode 100644 index 0000000000..b999d609ac --- /dev/null +++ b/addons/overheating/functions/fnc_setAmmoTemperature.sqf @@ -0,0 +1,24 @@ +#include "script_component.hpp" +/* + * Author: drofseh + * Set weapon's ammo to specific temperature. + * + * Arguments: + * 0: Unit + * 1: Weapon + * 2: Temperature + * + * 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]; diff --git a/addons/overheating/functions/fnc_setWeaponTemperature.sqf b/addons/overheating/functions/fnc_setWeaponTemperature.sqf new file mode 100644 index 0000000000..06e183f91f --- /dev/null +++ b/addons/overheating/functions/fnc_setWeaponTemperature.sqf @@ -0,0 +1,24 @@ +#include "script_component.hpp" +/* + * Author: drofseh + * Set weapon to specific temperature. + * + * Arguments: + * 0: Unit + * 1: Weapon + * 2: Temperature + * + * 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]; diff --git a/addons/overheating/functions/fnc_swapBarrelCallback.sqf b/addons/overheating/functions/fnc_swapBarrelCallback.sqf index 1f458bcc26..a418d35510 100644 --- a/addons/overheating/functions/fnc_swapBarrelCallback.sqf +++ b/addons/overheating/functions/fnc_swapBarrelCallback.sqf @@ -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 diff --git a/addons/overheating/functions/fnc_updateAmmoTemperature.sqf b/addons/overheating/functions/fnc_updateAmmoTemperature.sqf index 97cbc1276c..fd1ac2880c 100644 --- a/addons/overheating/functions/fnc_updateAmmoTemperature.sqf +++ b/addons/overheating/functions/fnc_updateAmmoTemperature.sqf @@ -9,7 +9,7 @@ * 2: Barrel Temperature * * Return Value: - * Current ammunition temperature + * New temperature * * 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]; diff --git a/addons/overheating/functions/fnc_updateAmmoTemperatureThread.sqf b/addons/overheating/functions/fnc_updateAmmoTemperatureThread.sqf index 2b191102d5..4fff0f59db 100644 --- a/addons/overheating/functions/fnc_updateAmmoTemperatureThread.sqf +++ b/addons/overheating/functions/fnc_updateAmmoTemperatureThread.sqf @@ -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]; diff --git a/addons/overheating/functions/fnc_updateTemperature.sqf b/addons/overheating/functions/fnc_updateTemperature.sqf index 351323d54a..e1368ea2e5 100644 --- a/addons/overheating/functions/fnc_updateTemperature.sqf +++ b/addons/overheating/functions/fnc_updateTemperature.sqf @@ -9,7 +9,7 @@ * 2: Heat increment (J) * * Return Value: - * Current temperature + * New temperature * * 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]; diff --git a/addons/overheating/initSettings.sqf b/addons/overheating/initSettings.sqf index b351a8bed7..b3eaf24029 100644 --- a/addons/overheating/initSettings.sqf +++ b/addons/overheating/initSettings.sqf @@ -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; [ diff --git a/addons/overheating/stringtable.xml b/addons/overheating/stringtable.xml index 95172ac4ee..63b53d3d39 100644 --- a/addons/overheating/stringtable.xml +++ b/addons/overheating/stringtable.xml @@ -62,6 +62,18 @@ Koeffizient für die Menge an Hitze, die eine Waffe pro Schuss erzeugt.\nHöhere Werte beschleunigen die Erhitzung. Współczynnik wpływający na ilość ciepła generowanego przez broń przy każdym strzale. + + Cooling Coefficient + + + Coefficient for how quickly a weapon cools down.\nHigher value increases cooling speed. + + + Suppressor Coefficient + + + 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. + Display Text on Jam Zeige Text bei Ladehemmung @@ -274,8 +286,7 @@ Unjam on Barrel Swap 銃身交換で弾詰まり解消 Désenrayer l'arme au changement de canon - Замена ствола устраняет заклинивание оружия. - Ladehemmungen bei Lauf-Wechsel beheben + Замена ствола устраняет заклинивание оружия. Usuń zacięcie przy wymianie lufy diff --git a/docs/wiki/feature/overheating.md b/docs/wiki/feature/overheating.md index 1fe090aceb..eaad8357a5 100644 --- a/docs/wiki/feature/overheating.md +++ b/docs/wiki/feature/overheating.md @@ -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.