diff --git a/addons/overheating/ACE_Settings.hpp b/addons/overheating/ACE_Settings.hpp index a2bdeb7cc3..5febcd6f7f 100644 --- a/addons/overheating/ACE_Settings.hpp +++ b/addons/overheating/ACE_Settings.hpp @@ -2,6 +2,9 @@ class ACE_Settings { class GVAR(enabled) { movedToSQF = 1; }; + class GVAR(heatCoef) { + movedToSQF = 1; + }; class GVAR(showParticleEffects) { movedToSQF = 1; }; @@ -11,13 +14,28 @@ class ACE_Settings { class GVAR(overheatingDispersion) { movedToSQF = 1; }; + class GVAR(particleEffectsAndDispersionDistance) { + movedToSQF = 1; + }; + class GVAR(overheatingRateOfFire) { + movedToSQF = 1; + }; class GVAR(displayTextOnJam) { movedToSQF = 1; }; - class GVAR(unJamOnreload) { + class GVAR(jamChanceCoef) { + movedToSQF = 1; + }; + class GVAR(unJamOnReload) { + movedToSQF = 1; + }; + class GVAR(unJamOnSwapBarrel) { movedToSQF = 1; }; class GVAR(unJamFailChance) { movedToSQF = 1; }; + class GVAR(cookoffCoef) { + movedToSQF = 1; + }; }; diff --git a/addons/overheating/CfgSounds.hpp b/addons/overheating/CfgSounds.hpp index bd2831eeb6..2b333c3381 100644 --- a/addons/overheating/CfgSounds.hpp +++ b/addons/overheating/CfgSounds.hpp @@ -23,4 +23,26 @@ class CfgSounds { sound[]={QPATHTOF(sounds\fixing_pistol.wav),1,1}; titles[]={}; }; + /* // to be added when licence compatible audio can be found or recorded + class GVAR(pouring_long) { + name= QGVAR(pouring_long); + sound[]={QPATHTOF(sounds\pouring_long.ogg),5,1}; + titles[]={}; + }; + class GVAR(pouring_short) { + name= QGVAR(pouring_short); + sound[]={QPATHTOF(sounds\pouring_short.ogg),5,1}; + titles[]={}; + }; + class GVAR(sizzling_long) { + name= QGVAR(sizzling_long); + sound[]={QPATHTOF(sounds\sizzling_long.ogg),5,1}; + titles[]={}; + }; + class GVAR(sizzling_short) { + name= QGVAR(sizzling_short); + sound[]={QPATHTOF(sounds\sizzling_short.ogg),5,1}; + titles[]={}; + }; + */ }; diff --git a/addons/overheating/CfgVehicles.hpp b/addons/overheating/CfgVehicles.hpp index e142b71a89..beb6a910e4 100644 --- a/addons/overheating/CfgVehicles.hpp +++ b/addons/overheating/CfgVehicles.hpp @@ -36,6 +36,15 @@ class CfgVehicles { showDisabled = 0; icon = QUOTE(PATHTOF(UI\temp_ca.paa)); }; + class GVAR(CoolWeaponWithItem) { + displayName = CSTRING(CoolWeaponWithItem); + condition = QUOTE(GVAR(enabled) && {isClass(configfile >> 'CfgPatches' >> 'acex_field_rations')}); + exceptions[] = {"isNotInside", "isNotSwimming", "isNotSitting"}; + statement = "true"; + showDisabled = 0; + insertChildren = QUOTE(_player call FUNC(getConsumableChildren)); + icon = QPATHTOF(UI\pour_water_ca.paa); + }; }; }; @@ -55,6 +64,15 @@ class CfgVehicles { statement = QUOTE( [ARR_3(_player, _target, currentWeapon _target)] call FUNC(checkTemperature); ); icon = QUOTE(PATHTOF(UI\temp_ca.paa)); }; + class GVAR(CoolWeaponWithItem) { + displayName = CSTRING(CoolWeaponWithItem); + condition = QUOTE(GVAR(enabled) && {isClass(configfile >> 'CfgPatches' >> 'acex_field_rations')}); + exceptions[] = {"isNotInside", "isNotSwimming", "isNotSitting"}; + statement = "true"; + showDisabled = 0; + insertChildren = QUOTE(_player call FUNC(getConsumableChildren)); + icon = QPATHTOF(UI\pour_water_ca.paa); + }; }; }; }; diff --git a/addons/overheating/CfgWeapons.hpp b/addons/overheating/CfgWeapons.hpp index bc9930a420..0ba620ae3a 100644 --- a/addons/overheating/CfgWeapons.hpp +++ b/addons/overheating/CfgWeapons.hpp @@ -1,4 +1,19 @@ class CfgWeapons { + class PistolCore; + class Pistol : PistolCore { + //Closed Bolt (Closed Bolt will cook off if too hot) + //Pistols are nearly universally closed bolt. + GVAR(closedBolt) = 1; + }; + + class Pistol_Base_F : Pistol {}; + class hgun_Pistol_heavy_02_F : Pistol_Base_F { + GVAR(jamTypesAllowed) = ["Fire","Dud"]; + }; + class hgun_Pistol_Signal_F : Pistol_Base_F { + GVAR(jamTypesAllowed) = ["Fire","Dud"]; + }; + class RifleCore; class Rifle: RifleCore { //Mean Rounds Between Stoppages (this will be scaled based on the barrel temp) @@ -9,10 +24,17 @@ class CfgWeapons { //Slowdown Factor (this will be scaled based on the barrel temp) GVAR(slowdownFactor) = 1; + + //Closed Bolt, most weapons are closed bolt + GVAR(closedBolt) = 1; }; class Rifle_Base_F; class Rifle_Long_Base_F: Rifle_Base_F { GVAR(dispersion) = 0.75; + + // Open Bolt, most machine guns are open bolt, which cannot normally cook off, and use this as a parent class + // A lot of sniper rifles also use this as a parent class, they will need to be indivisually set to closed bolt, but it's probably not an issue as they are unlikely to overheat + GVAR(closedBolt) = 0; }; class arifle_MX_Base_F: Rifle_Base_F { @@ -26,6 +48,36 @@ class CfgWeapons { GVAR(allowSwapBarrel) = 1; GVAR(dispersion) = 0.75; }; + class DMR_01_base_F : Rifle_Long_Base_F { + GVAR(closedBolt) = 1; + }; + class DMR_02_base_F : Rifle_Long_Base_F { + GVAR(closedBolt) = 1; + }; + class DMR_03_base_F : Rifle_Long_Base_F { + GVAR(closedBolt) = 1; + }; + class DMR_04_base_F : Rifle_Long_Base_F { + GVAR(closedBolt) = 1; + }; + class DMR_05_base_F : Rifle_Long_Base_F { + GVAR(closedBolt) = 1; + }; + class DMR_06_base_F : Rifle_Long_Base_F { + GVAR(closedBolt) = 1; + }; + class DMR_07_base_F : Rifle_Long_Base_F { + GVAR(closedBolt) = 1; + }; + class EBR_base_F : Rifle_Long_Base_F { + GVAR(closedBolt) = 1; + }; + class GM6_base_F : Rifle_Long_Base_F { + GVAR(closedBolt) = 1; + }; + class LRR_base_F : Rifle_Long_Base_F { + GVAR(closedBolt) = 1; + }; class MMG_01_base_F: Rifle_Long_Base_F { GVAR(allowSwapBarrel) = 1; }; @@ -41,6 +93,10 @@ class CfgWeapons { class LMG_03_Base_F: Rifle_Long_Base_F { GVAR(allowSwapBarrel) = 1; }; + class sgun_HunterShotgun_01_base_F : Rifle_Long_Base_F { + GVAR(closedBolt) = 1; + GVAR(jamTypesAllowed) = ["Fire","Dud"]; + }; class ACE_ItemCore; class CBA_MiscItem_ItemInfo; class ACE_SpareBarrel_Item: ACE_ItemCore { diff --git a/addons/overheating/UI/pour_water_ca.paa b/addons/overheating/UI/pour_water_ca.paa new file mode 100644 index 0000000000..060a4572d1 Binary files /dev/null and b/addons/overheating/UI/pour_water_ca.paa differ diff --git a/addons/overheating/XEH_PREP.hpp b/addons/overheating/XEH_PREP.hpp index 60f3d09fa2..f4b56c3c3f 100644 --- a/addons/overheating/XEH_PREP.hpp +++ b/addons/overheating/XEH_PREP.hpp @@ -6,8 +6,12 @@ PREP(canCheckSpareBarrelsTemperatures); PREP(checkSpareBarrelsTemperatures); PREP(checkTemperature); PREP(clearJam); +PREP(coolWeaponWithItem); +PREP(coolWeaponWithWaterSource); PREP(displayTemperature); PREP(firedEH); +PREP(getBarrelMass); +PREP(getConsumableChildren); PREP(getWeaponData); PREP(handleTakeEH); PREP(handleRespawn); @@ -18,6 +22,8 @@ PREP(sendSpareBarrelsTemperaturesHint); PREP(swapBarrel); PREP(swapBarrelAssistant); PREP(swapBarrelCallback); +PREP(updateAmmoTemperature); +PREP(updateAmmoTemperatureThread); PREP(updateSpareBarrelsTemperaturesThread); PREP(updateTemperature); PREP(updateTemperatureThread); diff --git a/addons/overheating/XEH_postInit.sqf b/addons/overheating/XEH_postInit.sqf index 0d97065983..4a932058bb 100644 --- a/addons/overheating/XEH_postInit.sqf +++ b/addons/overheating/XEH_postInit.sqf @@ -48,22 +48,64 @@ if (hasInterface) then { GVAR(cacheAmmoData) = call CBA_fnc_createNamespace; GVAR(cacheSilencerData) = call CBA_fnc_createNamespace; - //Add Take EH (for reload) - ["CAManBase", "Take", {_this call FUNC(handleTakeEH);}] call CBA_fnc_addClassEventHandler; + //Add Take EH if required + if (GVAR(unJamOnReload) || {GVAR(cookoffCoef) > 0}) then { + ["CAManBase", "Take", {_this call FUNC(handleTakeEH);}] call CBA_fnc_addClassEventHandler; + }; // Register fire event handler ["ace_firedPlayer", DFUNC(firedEH)] call CBA_fnc_addEventHandler; // Only add eh to non local players if dispersion is enabled - if (GVAR(overheatingDispersion)) then { + if (GVAR(overheatingDispersion) || {GVAR(showParticleEffectsForEveryone)}) then { ["ace_firedPlayerNonLocal", DFUNC(firedEH)] call CBA_fnc_addEventHandler; }; // Schedule cool down calculation of player weapons at (infrequent) regular intervals [] call FUNC(updateTemperatureThread); + //Add event handlers and start ammo heating loop for cookoff + if (GVAR(cookoffCoef) > 0) then { + [] call FUNC(updateAmmoTemperatureThread); + + // Reset ammo temperature on reload, unless the reload is a second muzzle. + ["CAManBase", "Reloaded", { + params ["_unit", "_weapon", "_muzzle"]; + if (_muzzle == _weapon) then { + _unit setVariable [format [QGVAR(%1_ammoTemp), _weapon], 0]; + }; + }] 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; + // Add an action to allow hot weapons to be cooled off in AceX Field Rations water sources + if (isClass(configfile >> "CfgPatches" >> "acex_field_rations")) then { + [ + {acex_field_rations_enabled || CBA_missionTime > 1}, + { + if (!acex_field_rations_enabled) exitWith {}; + + _CoolWeaponWithWaterSourceAction = [ + QGVAR(CoolWeaponWithWaterSource), + LLSTRING(CoolWeaponWithWaterSource), + "\z\acex\addons\field_rations\ui\icon_water_tap.paa", + { + private _waterSource = _target getVariable ["acex_field_rations_waterSource", objNull]; + [_player, _waterSource] call FUNC(coolWeaponWithWaterSource); + }, + { + private _waterSource = _target getVariable ["acex_field_rations_waterSource", objNull]; + [_player, _waterSource] call acex_field_rations_fnc_canDrinkFromSource; + } + ] call EFUNC(interact_menu,createAction); + + ["acex_field_rations_helper", 0, ["acex_field_rations_waterSource"], _CoolWeaponWithWaterSourceAction] call EFUNC(interact_menu,addActionToClass); + }, + [] + ] 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 01983814f5..8fd39c7024 100644 --- a/addons/overheating/functions/fnc_calculateCooling.sqf +++ b/addons/overheating/functions/fnc_calculateCooling.sqf @@ -30,6 +30,15 @@ if (_totalTime > 1800) exitWith {0}; //So Area = 210 * 1.1 * (mass / 7850) = mass * 0.029427 (for steel near that diameter) private _barrelSurface = _barrelMass * 0.029427; +private _convectionRate = 25; + +//provide additional cooling if swimming or raining or windy +if (ACE_player call EFUNC(common,isSwimming)) then { + _convectionRate = 500; +} else { + // this will give a convection rate between 25 (no wind or rain) and 125 (max rain and >=50 m/s wind) + _convectionRate = _convectionRate * ((linearConversion [0,1,rain,1,5,true] + (5 min (vectorMagnitude wind / 10))) / 2); +}; TRACE_4("cooling",_temperature,_totalTime,_barrelMass,_barrelSurface); @@ -38,13 +47,10 @@ while {true} do { private _deltaTime = (_totalTime - _time) min 20; _temperature = _temperature - ( - // Convective cooling - 25 * _barrelSurface * _temperature - // Radiative cooling - + 0.4 * 5.67e-8 * _barrelSurface * - ( (_temperature + 273.15)*(_temperature + 273.15) - * (_temperature + 273.15)*(_temperature + 273.15) - - 273.15 * 273.15 * 273.15 *273.15 ) + // Convective cooling + _convectionRate * _barrelSurface * _temperature + // Radiative cooling + + 0.4 * 5.67e-8 * _barrelSurface * ((_temperature + 273.15) ^ 4 - 273.15 ^ 4) ) * _deltaTime / (_barrelMass * 466); if (_temperature < 1) exitWith {0}; diff --git a/addons/overheating/functions/fnc_canCheckSpareBarrelsTemperatures.sqf b/addons/overheating/functions/fnc_canCheckSpareBarrelsTemperatures.sqf index ca4d76e77b..0f8775ae25 100644 --- a/addons/overheating/functions/fnc_canCheckSpareBarrelsTemperatures.sqf +++ b/addons/overheating/functions/fnc_canCheckSpareBarrelsTemperatures.sqf @@ -20,7 +20,7 @@ params ["_player"]; //Get the classname of the spare barrel for the weapon private _weaponBarrelClass = getText (configFile >> 'CfgWeapons' >> currentWeapon _player >> QGVAR(barrelClassname)); //If the weapon has no defined classname then use the ACE one -if(_weaponBarrelClass == "") then { +if (_weaponBarrelClass == "") then { _weaponBarrelClass = "ACE_SpareBarrel"; }; //Check if the player has the barrel and the weapon can have its barrel swapped diff --git a/addons/overheating/functions/fnc_canSwapBarrel.sqf b/addons/overheating/functions/fnc_canSwapBarrel.sqf index 53104e19d5..38115f6f7a 100644 --- a/addons/overheating/functions/fnc_canSwapBarrel.sqf +++ b/addons/overheating/functions/fnc_canSwapBarrel.sqf @@ -18,12 +18,12 @@ params ["_unit","_weapon"]; //Check if weapon can have its barrel swapped. If not exit out of function -if( !GVAR(enabled) || {getNumber (configFile >> 'CfgWeapons' >> _weapon >> QGVAR(allowSwapBarrel)) != 1}) exitWith{false}; +if ( !GVAR(enabled) || {getNumber (configFile >> 'CfgWeapons' >> _weapon >> QGVAR(allowSwapBarrel)) != 1}) exitWith{false}; //Get the classname of the spare barrel for the weapon private _weaponBarrelClass = getText (configFile >> 'CfgWeapons' >> _weapon >> QGVAR(barrelClassname)); //If the weapon has no defined classname then use the ACE one -if(_weaponBarrelClass == "") then { +if (_weaponBarrelClass == "") then { _weaponBarrelClass = "ACE_SpareBarrel"; }; //If the player has the spare barrel then it can be swapped diff --git a/addons/overheating/functions/fnc_checkTemperature.sqf b/addons/overheating/functions/fnc_checkTemperature.sqf index b7be4f4c13..b771293e6b 100644 --- a/addons/overheating/functions/fnc_checkTemperature.sqf +++ b/addons/overheating/functions/fnc_checkTemperature.sqf @@ -12,7 +12,7 @@ * None * * Example: - * [player, currentWeapon player] call ace_overheating_fnc_checkTemperature + * [player, player, currentWeapon player] call ace_overheating_fnc_checkTemperature * * Public: No */ diff --git a/addons/overheating/functions/fnc_clearJam.sqf b/addons/overheating/functions/fnc_clearJam.sqf index 61e84fe906..3e63ed5cb7 100644 --- a/addons/overheating/functions/fnc_clearJam.sqf +++ b/addons/overheating/functions/fnc_clearJam.sqf @@ -33,6 +33,7 @@ if (_weapon in _jammedWeapons) then { }; [_unit, _clearJamAction, 1] call EFUNC(common,doGesture); + if (_weapon == primaryWeapon _unit) then { playSound QGVAR(fixing_rifle); } else { @@ -42,21 +43,33 @@ if (_weapon in _jammedWeapons) then { }; }; - // Check if the jam will be successfull + // Check if the jam clearing will be successfull if (random 1 > GVAR(unJamFailChance)) then { // Success - _jammedWeapons = _jammedWeapons - [_weapon]; - _unit setVariable [QGVAR(jammedWeapons), _jammedWeapons]; - if (_jammedWeapons isEqualTo []) then { - private _id = _unit getVariable [QGVAR(JammingActionID), -1]; - [_unit, "DefaultAction", _id] call EFUNC(common,removeActionEventHandler); - _unit setVariable [QGVAR(JammingActionID), -1]; - }; - if (GVAR(DisplayTextOnJam)) then { - [{ + + [{ + params ["_unit", "_weapon", "_jammedWeapons"]; + _jammedWeapons = _jammedWeapons - [_weapon]; + _unit setVariable [QGVAR(jammedWeapons), _jammedWeapons]; + + // If the round is a dud eject the round + if (_unit getVariable [format [QGVAR(%1_jamType), _weapon], "None"] isEqualTo "Dud") then { + private _ammo = _unit ammo _weapon; + _unit setAmmo [_weapon, _ammo - 1]; + }; + + _unit setVariable [format [QGVAR(%1_jamType), _weapon], "None"]; + + if (_jammedWeapons isEqualTo []) then { + private _id = _unit getVariable [QGVAR(JammingActionID), -1]; + [_unit, "DefaultAction", _id] call EFUNC(common,removeActionEventHandler); + _unit setVariable [QGVAR(JammingActionID), -1]; + }; + + if (GVAR(DisplayTextOnJam)) then { [localize LSTRING(WeaponUnjammed)] call EFUNC(common,displayTextStructured); - }, [], _delay] call CBA_fnc_waitAndExecute; - }; + }; + }, [_unit, _weapon, _jammedWeapons], _delay] call CBA_fnc_waitAndExecute; } else { // Failure if (GVAR(DisplayTextOnJam)) then { diff --git a/addons/overheating/functions/fnc_coolWeaponWithItem.sqf b/addons/overheating/functions/fnc_coolWeaponWithItem.sqf new file mode 100644 index 0000000000..ee3e1437f9 --- /dev/null +++ b/addons/overheating/functions/fnc_coolWeaponWithItem.sqf @@ -0,0 +1,98 @@ +#include "script_component.hpp" +/* + * Author: mharis001, Glowbal, PabstMirror, drofseh + * Cool a weapon with an item and consume the item being used to cool it. + * + * Arguments: + * 0: Target + * 1: Player + * 2: Item + * + * Return Value: + * None + * + * Example: + * [ACE_player, ACE_player, "ACE_WaterBottle"] call ace_overheating_fnc_coolWeaponWithItem + * + * Public: No + */ + +params ["_target", "_unit", "_item"]; + +private _config = configFile >> "CfgWeapons" >> _item; + +// Get values +private _weapon = currentWeapon _target; +private _tempVarName = format [QGVAR(%1_temp), _weapon]; +private _temperature = _target getVariable [_tempVarName, 0]; +private _replacementItem = getText (_config >> "acex_field_rations_replacementItem"); +private _liquidAmount = getNumber (_config >> "acex_field_rations_thirstQuenched"); +private _consumeText = format [LLSTRING(CoolingWeaponWithItem), getText (configFile >> "CfgWeapons" >> _weapon >> "displayName"), getText (_config >> "displayName")]; + +/* // to be added when licence compatible audio can be found or recorded +private _pouringSound = QPATHTO_R(sounds\sizzling_short.ogg); + +if (_temperature < 100) then { + if (_liquidAmount > 5) then { + _pouringSound = QPATHTO_R(sounds\pouring_long.ogg); + } else { + _pouringSound = QPATHTO_R(sounds\pouring_short.ogg); + }; +} else { + if (_liquidAmount > 5) then { + _pouringSound = QPATHTO_R(sounds\sizzling_long.ogg); + }; +}; + +playSound3D [_pouringSound, _target, false, AGLToASL (_target modelToWorld (_target selectionPosition "RightHand")), 5, 1, 10]; +*/ + +private _fnc_onSuccess = { + params ["_args"]; + _args params ["_target", "_unit", "_item", "_weapon", "_tempVarName", "_temperature", "_replacementItem", "_liquidAmount"]; + TRACE_1("Cool weapon with item successful",_args); + + // remove the item + _unit removeItem _item; + + // Add replacement item if needed + if (_replacementItem != "") then { + [_unit, _replacementItem] call EFUNC(common,addToInventory); + }; + + // cool the weapon + private _barrelMass = _weapon call FUNC(getBarrelMass); + _temperature = [_temperature, _barrelMass, _liquidAmount * 10] call FUNC(calculateCooling); + [_target, _tempVarName, _temperature, TEMP_TOLERANCE] call EFUNC(common,setApproximateVariablePublic); +}; + +/* +private _fnc_onFailure = { + params ["_args","_elapsedTime"]; + _args params ["_target", "_unit"]; +}; +*/ + +private _fnc_condition = { + params ["_args"]; + _args params ["", "_unit", "_item"]; + _item in (_unit call EFUNC(common,uniqueItems)) +}; + +[ + _liquidAmount, + [ + _target, + _unit, + _item, + _weapon, + _tempVarName, + _temperature, + _replacementItem, + _liquidAmount + ], + _fnc_onSuccess, + {}, //_fnc_onFailure, + _consumeText, + _fnc_condition +] call EFUNC(common,progressBar); \ No newline at end of file diff --git a/addons/overheating/functions/fnc_coolWeaponWithWaterSource.sqf b/addons/overheating/functions/fnc_coolWeaponWithWaterSource.sqf new file mode 100644 index 0000000000..5e7269d065 --- /dev/null +++ b/addons/overheating/functions/fnc_coolWeaponWithWaterSource.sqf @@ -0,0 +1,117 @@ +#include "script_component.hpp" +/* + * Author: mharis001, Glowbal, PabstMirror, drofseh + * Cool a weapon with an AceX water source. + * + * Arguments: + * 0: Target + * 1: Player + * + * Return Value: + * None + * + * Example: + * [ACE_player, WaterTank] call ace_overheating_fnc_coolWeaponWithWaterSource + * + * Public: No +*/ + +params ["_player", "_target"]; + +private _weapon = currentWeapon _player; +private _tempVarName = format [QGVAR(%1_temp), _weapon]; +private _temperature = _player getVariable [_tempVarName, 0]; +private _consumeText = LLSTRING(CoolingWeaponWithWaterSource); +GVAR(coolingWeaponWithWaterSource) = false; + +private _fnc_onFinish = { + params ["_args"]; + _args params ["_player", "_target", "_weapon", "_tempVarName"]; + + private _water = _target call acex_field_rations_fnc_getRemainingWater; + + if (_water <= 0 && {_water != -10}) exitWith { + [ + [LLSTRING(CoolWeaponNotEnoughWater)], + true + ] call CBA_fnc_notify; + }; + + [_player, _player, currentWeapon _player] call ace_overheating_fnc_checkTemperature; + GVAR(coolingWeaponWithWaterSource) = false; +}; + +private _fnc_condition = { + params ["_args"]; + _args params ["_player", "_target", "_weapon", "_tempVarName"]; + + private _temperature = _player getVariable [_tempVarName, 0]; + private _water = _target call acex_field_rations_fnc_getRemainingWater; + + if (_water <= 0 && {_water != -10}) exitWith {false}; + + if !(GVAR(coolingWeaponWithWaterSource)) then { + GVAR(coolingWeaponWithWaterSource) = true; + + //Remove water from the source, unless it's unlimited + if (_water != -10) then { + [_target, _water - 1] call acex_field_rations_fnc_setRemainingWater; + }; + + //Cool the weapon down + private _barrelMass = _weapon call FUNC(getBarrelMass); + _temperature = [_temperature, _barrelMass, 20] call FUNC(calculateCooling); + [_player, _tempVarName, _temperature, TEMP_TOLERANCE] call EFUNC(common,setApproximateVariablePublic); + +/* // to be added when licence compatible audio can be found or recorded + // water sound, either boiling or not + if (_temperature < 100) then { + [ + [LLSTRING(CoolWeaponIsCool)], + true // allows the hint to be overwritten by another hint, such as a jam or another cookoff + ] call CBA_fnc_notify; + + playSound3D [ + QPATHTO_R(sounds\pouring_short.ogg), + _player, + false, + AGLToASL (_player modelToWorld (_player selectionPosition "RightHand")), + 5, 1, 10 + ]; + } else { + playSound3D [ + QPATHTO_R(sounds\sizzling_short.ogg), + _player, + false, + AGLToASL (_player modelToWorld (_player selectionPosition "RightHand")), + 5, 1, 10 + ]; + }; +*/ + + // wait 1 second before allowing cooling to happen again + [ + { + GVAR(coolingWeaponWithWaterSource) = false; + }, + [], + 1 + ] call CBA_fnc_waitAndExecute; + }; + + true +}; + +[ + 1 max (_temperature / 10), + [ + _player, + _target, + _weapon, + _tempVarName + ], + _fnc_onFinish, + _fnc_onFinish, + _consumeText, + _fnc_condition +] call EFUNC(common,progressBar); diff --git a/addons/overheating/functions/fnc_firedEH.sqf b/addons/overheating/functions/fnc_firedEH.sqf index dd6b408a21..5f43ca1881 100644 --- a/addons/overheating/functions/fnc_firedEH.sqf +++ b/addons/overheating/functions/fnc_firedEH.sqf @@ -20,7 +20,7 @@ TRACE_10("firedEH:",_unit, _weapon, _muzzle, _mode, _ammo, _magazine, _projectil BEGIN_COUNTER(firedEH); -if ((_unit distance ACE_player) > 3000 +if ((_unit distance ACE_player) > GVAR(particleEffectsAndDispersionDistance) || {(_muzzle != (primaryWeapon _unit)) && {_muzzle != (handgunWeapon _unit)}}) exitWith { // Only rifle or pistol muzzles (ignore grenades / GLs) END_COUNTER(firedEH); }; @@ -54,12 +54,12 @@ if (_scaledTemperature > 0.1) then { [_projectile, _dispersionX * _dispersion, _dispersionY * _dispersion, _slowdownFactor * vectorMagnitude (velocity _projectile)] call EFUNC(common,changeProjectileDirection); TRACE_PROJECTILE_INFO(_projectile); }; - + // Particle Effects if (GVAR(showParticleEffects) && {GVAR(showParticleEffectsForEveryone) || {_unit == ACE_player} || {_unit distance ACE_player <= 20}} && {CBA_missionTime > (_unit getVariable [QGVAR(lastDrop), -1000]) + 0.40}) then { - + _unit setVariable [QGVAR(lastDrop), CBA_missionTime]; private _direction = (_unit weaponDirection _weapon) vectorMultiply 0.25; @@ -94,9 +94,24 @@ if ((_unit ammo _weapon) % 3 == 0) then { _this call FUNC(overheat); }; +// reset cookoff heat +if (GVAR(cookoffCoef) > 0) then { + _unit setVariable [format [QGVAR(%1_ammoTemp), _weapon], 0]; + [_unit, _weapon, _temperature] call FUNC(updateAmmoTemperature); +}; + +// decrease time to next shot as heat increases, value is a coef where 1 is unchanged and 0 is instant, 0.8 is a 25% faster ROF. +// this could be filtered by weapon type, but I think the heat gain and rate of fire on non-automatic weapons is low enough not to bother +if (GVAR(overheatingRateOfFire)) then { + _unit setWeaponReloadingTime [_unit, _muzzle, linearConversion [0, 0.5, _scaledTemperature, 1, 0.909, true]]; +}; + +// Don't bother with jamming if coef makes the chance 0. +if (GVAR(jamChanceCoef) == 0) exitWith {END_COUNTER(firedEH);}; + private _value = 5 * _scaledTemperature; private _array = [0.5, 1, 2, 8, 20, 150]; -_jamChance = _jamChance * linearConversion [0, 1, _value % 1, _array select floor _value, _array select ceil _value]; +_jamChance = _jamChance * GVAR(jamChanceCoef) * linearConversion [0, 1, _value % 1, _array select floor _value, _array select ceil _value]; TRACE_3("check for random jam",_unit,_weapon,_jamChance); diff --git a/addons/overheating/functions/fnc_getBarrelMass.sqf b/addons/overheating/functions/fnc_getBarrelMass.sqf new file mode 100644 index 0000000000..1504442a60 --- /dev/null +++ b/addons/overheating/functions/fnc_getBarrelMass.sqf @@ -0,0 +1,20 @@ +#include "script_component.hpp" +/* + * Author: mharis001, Glowbal, PabstMirror, drofseh + * Get the mass of the weapons barrel. + * + * Arguments: + * 0: Weapon + * + * Return Value: + * Barrel Mass + * + * Example: + * [currentWeapon ACE_player] call ace_overheating_fnc_getBarrelMass + * + * Public: No + */ + +params ["_weapon"]; + +METAL_MASS_RATIO * (getNumber (configFile >> "CfgWeapons" >> _weapon >> "WeaponSlotsInfo" >> "mass") / 22.0) max 1.0; diff --git a/addons/overheating/functions/fnc_getConsumableChildren.sqf b/addons/overheating/functions/fnc_getConsumableChildren.sqf new file mode 100644 index 0000000000..a46c32a88a --- /dev/null +++ b/addons/overheating/functions/fnc_getConsumableChildren.sqf @@ -0,0 +1,41 @@ +#include "script_component.hpp" +/* + * Author: mharis001, Glowbal, PabstMirror + * Returns children actions for consumable items in player's inventory. + * + * Arguments: + * 0: Player + * + * Return Value: + * Actions + * + * Example: + * [_unit] call ace_overheating_fnc_getConsumableChildren + * + * Public: No + */ + +params ["_unit"]; + +private _fnc_getActions = { + TRACE_1("Creating overheating consumable item actions",_unit); + + private _actions = []; + private _cfgWeapons = configFile >> "CfgWeapons"; + + { + private _config = _cfgWeapons >> _x; + if (getNumber (_config >> "acex_field_rations_thirstQuenched") > 0) then { + private _displayName = getText (_config >> "displayName"); + private _picture = getText (_config >> "picture"); + + // Exec next frame so closing interaction menu doesn't block progressBar + private _action = [_x, _displayName, _picture, {[FUNC(coolWeaponWithItem), _this] call CBA_fnc_execNextFrame}, {true}, {}, _x] call EFUNC(interact_menu,createAction); + _actions pushBack [_action, [], _unit]; + }; + } forEach (_unit call EFUNC(common,uniqueItems)); + + _actions +}; + +[[], _fnc_getActions, _unit, QGVAR(overheatingConsumableActionsCache), 9999, "cba_events_loadoutEvent"] call EFUNC(common,cachedCall); diff --git a/addons/overheating/functions/fnc_getWeaponData.sqf b/addons/overheating/functions/fnc_getWeaponData.sqf index a055f65b1e..c5cfb40d4a 100644 --- a/addons/overheating/functions/fnc_getWeaponData.sqf +++ b/addons/overheating/functions/fnc_getWeaponData.sqf @@ -65,8 +65,16 @@ if (isArray _property) then { }; }; +// for cookoff +private _modes = getArray (configFile >> "CfgWeapons" >> _weapon >> "modes"); +private _muzzle = getArray (configFile >> "CfgWeapons" >> _weapon >> "muzzles") select 0; +if (_muzzle == "this") then { + _muzzle = _weapon; +}; +private _reloadTime = getNumber (configfile >> "CfgWeapons" >> _weapon >> (_modes select 0) >> "reloadTime"); + // Cache the values -_weaponData = [_dispersion, _slowdownFactor, _jamChance]; +_weaponData = [_dispersion, _slowdownFactor, _jamChance, _modes, _muzzle, _reloadTime]; TRACE_2("building cache",_weapon,_weaponData); GVAR(cacheWeaponData) setVariable [_weapon, _weaponData]; diff --git a/addons/overheating/functions/fnc_handleTakeEH.sqf b/addons/overheating/functions/fnc_handleTakeEH.sqf index d347e5f071..266274e5bf 100644 --- a/addons/overheating/functions/fnc_handleTakeEH.sqf +++ b/addons/overheating/functions/fnc_handleTakeEH.sqf @@ -18,16 +18,20 @@ * Public: No */ -if !(GVAR(unJamOnreload)) exitWith {}; - params ["_unit", "_container", "_item"]; TRACE_3("params",_unit,_container,_item); if ((_unit == ACE_player) - && {_container in [uniformContainer _unit, vestContainer _unit, backpackContainer _unit]} - && {_item == currentMagazine _unit}) then { //Todo: should this be any valid magazine for any jammed gun? + && {_container in [uniformContainer _unit, vestContainer _unit, backpackContainer _unit]} + && {_item == currentMagazine _unit} +) then { //Todo: should this be any valid magazine for any jammed gun? - TRACE_1("clearing jam",currentWeapon _unit); - [_unit, currentWeapon _unit, true] call FUNC(clearJam) + if (GVAR(unJamOnReload)) then { + TRACE_1("clearing jam",currentWeapon _unit); + [_unit, currentWeapon _unit, true] call FUNC(clearJam); + }; + if (GVAR(cookoffCoef) > 0) then { + _unit setVariable [format [QGVAR(%1_ammoTemp), currentWeapon _unit], 0]; + }; }; diff --git a/addons/overheating/functions/fnc_jamWeapon.sqf b/addons/overheating/functions/fnc_jamWeapon.sqf index e3a5bb793a..c4e74f201f 100644 --- a/addons/overheating/functions/fnc_jamWeapon.sqf +++ b/addons/overheating/functions/fnc_jamWeapon.sqf @@ -21,24 +21,45 @@ TRACE_2("params",_unit,_weapon); // don't jam a weapon with no rounds left private _ammo = _unit ammo _weapon; -if (_ammo == 0) exitWith {}; +if (_ammo < 1) exitWith {}; private _jammedWeapons = _unit getVariable [QGVAR(jammedWeapons), []]; _jammedWeapons pushBack _weapon; _unit setVariable [QGVAR(jammedWeapons), _jammedWeapons]; +// Get jam types, select one from available types +// Cookoffs only happen on Fire and Dud, dud rounds are lost on jam clear. +// Reduce chance of duds as temp increases (functionally increasing the chance of the others but with fewer commands) +private _temp = 1 max (_unit getVariable [format [QGVAR(%1_temp), _weapon], 0]); +private _jamTypesAllowed = getArray (configFile >> 'CfgWeapons' >> currentWeapon _player >> QGVAR(jamTypesAllowed)); +if (_jamTypesAllowed == []) then { + _jamTypesAllowed = ["Eject", 1, "Extract", 1, "Feed", 1, "Fire", 1, "Dud", (5 / (_temp / 5))]; +} else { + for "_i" from count _jamTypesAllowed to 1 step -1 do { + private _jamCurretType = _jamTypesAllowed select _i; + if !(_jamCurretType in ["Eject", "Extract", "Feed", "Fire", "Dud"]) exitWith { // check config values and switch to default values if unusual value found + ERROR_2("Weapon '%1' has unexpected value %2 in QQGVAR(jamTypesAllowed). Expected values are 'Eject', 'Extract', 'Feed', 'Fire', 'Dud'.",_weapon,_jamCurretType); + _jamTypesAllowed = ["Eject", 1, "Extract", 1, "Feed", 1, "Fire", 1, "Dud", (5 / (_temp / 5))]; + }; + if (_jamCurretType == "Dud") then { + _jamTypesAllowed insert [_i, [5 / (_temp / 5)]]; + } else { + _jamTypesAllowed insert [_i, [1]]; + }; + }; +}; + +_unit setVariable [format [QGVAR(%1_jamType), _weapon], selectRandomWeighted _jamTypesAllowed]; // Stop current burst -if (_ammo > 0) then { - _unit setAmmo [_weapon, 0]; - // this is to re-activate the 'DefaultAction', so you can jam a weapon while full auto shootin - [{ - params ["_unit", "_weapon", "_ammo"]; - _unit setAmmo [_weapon, _ammo]; - }, [_unit, _weapon, _ammo]] call CBA_fnc_execNextFrame; -}; +_unit setAmmo [_weapon, 0]; +// this is to re-activate the 'DefaultAction', so you can jam a weapon while full auto shooting +[{ + params ["_unit", "_weapon", "_ammo"]; + _unit setAmmo [_weapon, _ammo]; +}, [_unit, _weapon, _ammo]] call CBA_fnc_execNextFrame; if (_weapon == primaryWeapon _unit) then { playSound QGVAR(jamming_rifle); @@ -53,20 +74,32 @@ GVAR(knowAboutJam) = false; ["ace_weaponJammed", [_unit,_weapon]] call CBA_fnc_localEvent; - if (_unit getVariable [QGVAR(JammingActionID), -1] == -1) then { private _condition = { - [_this select 1] call CBA_fnc_canUseWeapon - && {currentMuzzle (_this select 1) in ((_this select 1) getVariable [QGVAR(jammedWeapons), []])} - && {!(currentMuzzle (_this select 1) in ((_this select 1) getVariable [QEGVAR(safemode,safedWeapons), []]))} + private _unit = _this select 1; + [_unit] call CBA_fnc_canUseWeapon + && {currentMuzzle _unit in (_unit getVariable [QGVAR(jammedWeapons), []])} + && {!(currentMuzzle _unit in (_unit getVariable [QEGVAR(safemode,safedWeapons), []]))} }; private _statement = { - playSound3D ["a3\sounds_f\weapons\Other\dry9.wss", _this select 0, false, eyePos (_this select 0), 1, 1, 15]; + params ["_zero","_one"]; - if (!(missionNamespace getVariable [QGVAR(knowAboutJam), false]) && {(_this select 1) ammo currentWeapon (_this select 1) > 0} && {GVAR(DisplayTextOnJam)}) then { - [localize LSTRING(WeaponJammed)] call EFUNC(common,displayTextStructured); + playSound3D ["a3\sounds_f\weapons\Other\dry9.wss", _zero, false, eyePos _zero, 1, 1, 15]; + + if (!(missionNamespace getVariable [QGVAR(knowAboutJam), false]) && {_one ammo currentWeapon _one > 0} && {GVAR(DisplayTextOnJam)}) then { + private _jamType = _one getVariable [format [QGVAR(%1_jamType), currentWeapon _one], "None"]; + private _jamMessage = localize LSTRING(FailureToFire); + switch true do { + case (_jamType isEqualTo "Eject"): {_jamMessage = localize LSTRING(FailureToEject)}; + case (_jamType isEqualTo "Extract"): {_jamMessage = localize LSTRING(FailureToExtract)}; + case (_jamType isEqualTo "Feed"): {_jamMessage = localize LSTRING(FailureToFeed)}; + }; + [ + [localize LSTRING(WeaponJammed)], + [_jamMessage] + ] call CBA_fnc_notify; GVAR(knowAboutJam) = true; }; }; diff --git a/addons/overheating/functions/fnc_loadCoolestSpareBarrel.sqf b/addons/overheating/functions/fnc_loadCoolestSpareBarrel.sqf index b6666ddb53..f356fb254b 100644 --- a/addons/overheating/functions/fnc_loadCoolestSpareBarrel.sqf +++ b/addons/overheating/functions/fnc_loadCoolestSpareBarrel.sqf @@ -25,7 +25,7 @@ params ["_assistant", "_gunner", "_weapon", "_weaponTemp", "_barrelMass"]; TRACE_5("loadCoolestSpareBarrel1",_assistant,_gunner,_weapon,_weaponTemp,_barrelMass); private _weaponBarrelClass = getText (configFile >> 'CfgWeapons' >> _weapon >> QGVAR(barrelClassname)); //If the weapon has no defined classname then use the ACE one -if(_weaponBarrelClass == "") then { +if (_weaponBarrelClass == "") then { _weaponBarrelClass = "ACE_SpareBarrel"; }; // Find all spare barrel the player has diff --git a/addons/overheating/functions/fnc_sendSpareBarrelsTemperaturesHint.sqf b/addons/overheating/functions/fnc_sendSpareBarrelsTemperaturesHint.sqf index c34e54508d..7eaf62d0e7 100644 --- a/addons/overheating/functions/fnc_sendSpareBarrelsTemperaturesHint.sqf +++ b/addons/overheating/functions/fnc_sendSpareBarrelsTemperaturesHint.sqf @@ -27,7 +27,7 @@ private _weapon = currentWeapon _player; //Get the classname of the spare barrel of that weapon private _weaponBarrelClass = getText (configFile >> 'CfgWeapons' >> _weapon >> QGVAR(barrelClassname)); //If the weapon has no defined classname then use the ACE one -if(_weaponBarrelClass == "") then { +if (_weaponBarrelClass == "") then { _weaponBarrelClass = "ACE_SpareBarrel"; }; private _allBarrels = [_unit, _weaponBarrelClass] call CBA_fnc_getMagazineIndex; diff --git a/addons/overheating/functions/fnc_swapBarrelCallback.sqf b/addons/overheating/functions/fnc_swapBarrelCallback.sqf index 0b0dec4916..1f458bcc26 100644 --- a/addons/overheating/functions/fnc_swapBarrelCallback.sqf +++ b/addons/overheating/functions/fnc_swapBarrelCallback.sqf @@ -26,11 +26,15 @@ if (_assistant isEqualTo _gunner) then { playSound "ACE_BarrelSwap"; }; +if (GVAR(unJamOnSwapBarrel) && {[_gunner] call FUNC(canUnjam)}) then { + [_gunner, currentMuzzle _gunner, true] call FUNC(clearJam); +}; + // don't consume the barrel, but rotate through them. [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 = METAL_MASS_RATIO * (getNumber (configFile >> "CfgWeapons" >> _weapon >> "WeaponSlotsInfo" >> "mass") / 22.0) max 1.0; +private _barrelMass = _weapon call FUNC(getBarrelMass); // 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 new file mode 100644 index 0000000000..12944774b7 --- /dev/null +++ b/addons/overheating/functions/fnc_updateAmmoTemperature.sqf @@ -0,0 +1,106 @@ +#include "script_component.hpp" +/* + * Author: drofseh + * Update temperature of the round in the chamber and determine if a cookoff should occur. + * + * Arguments: + * 0: Unit + * 1: Weapon + * 2: Barrel Temperature + * + * Return Value: + * Current ammunition temperature + * + * Example: + * [player, currentWeapon player, 600] call ace_overheating_fnc_updateAmmoTemperature + * + * Public: No + */ + +params ["_unit", "_weapon", "_barrelTemperature"]; +TRACE_3("params",_unit,_weapon,_barrelTemperature); + +private _closedBolt = getNumber (configFile >> "CfgWeapons" >> _weapon >> QGVAR(closedBolt)); +private _canUnjam = [_unit] call FUNC(canUnjam); + +// 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"])}} + // 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"])}}}} +) exitWith { + _unit setVariable [format [QGVAR(%1_ammoTemp), _weapon], 0]; + 0 +}; + +private _ammoTempVarName = format [QGVAR(%1_ammoTemp), _weapon]; +private _ammoTemperature = _unit getVariable [_ammoTempVarName, 0]; + +// heat or cool the ammo +if (_ammoTemperature < _barrelTemperature) then { + // this is functional and feels ok, but someone please do better heat transfer math here, my head hurts. + private _temperatureDifference = _barrelTemperature - _ammoTemperature; + _ammoTemperature = _ammoTemperature + (1 max ((_temperatureDifference / 2.75) - 100)); +} else { + _ammoTemperature = _barrelTemperature; +}; + +// check for cook off +if (_ammoTemperature > (GUNPOWDER_IGNITION_TEMP * GVAR(cookoffCoef))) then { + + // 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; +}; + +_unit setVariable [_ammoTempVarName, _ammoTemperature]; + +_ammoTemperature diff --git a/addons/overheating/functions/fnc_updateAmmoTemperatureThread.sqf b/addons/overheating/functions/fnc_updateAmmoTemperatureThread.sqf new file mode 100644 index 0000000000..2b191102d5 --- /dev/null +++ b/addons/overheating/functions/fnc_updateAmmoTemperatureThread.sqf @@ -0,0 +1,25 @@ +#include "script_component.hpp" +/* + * Author: esteldunedain & drofseh + * Update . + * + * Arguments: + * None + * + * Return Value: + * None + * + * Example: + * [] call ace_overheating_fnc_updateAmmoTemperatureThread + * + * Public: No + */ + +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]; + [ACE_player, _currentWeapon, _temperature] call FUNC(updateAmmoTemperature); +}; + +// Schedule for execution again after 1 seconds. A quick loop is needed for ammo temperature in order to have faster cookoffs at higher barrel temps +[DFUNC(updateAmmoTemperatureThread), [], 1] call CBA_fnc_waitAndExecute; diff --git a/addons/overheating/functions/fnc_updateTemperature.sqf b/addons/overheating/functions/fnc_updateTemperature.sqf index 475f0454f9..351323d54a 100644 --- a/addons/overheating/functions/fnc_updateTemperature.sqf +++ b/addons/overheating/functions/fnc_updateTemperature.sqf @@ -27,7 +27,7 @@ private _timeVarName = format [QGVAR(%1_time), _weapon]; private _temperature = _unit getVariable [_tempVarName, 0]; private _lastTime = _unit getVariable [_timeVarName, 0]; -private _barrelMass = METAL_MASS_RATIO * (getNumber (configFile >> "CfgWeapons" >> _weapon >> "WeaponSlotsInfo" >> "mass") / 22.0) max 1.0; +private _barrelMass = _weapon call FUNC(getBarrelMass); // Calculate cooling _temperature = [_temperature, _barrelMass, CBA_missionTime - _lastTime] call FUNC(calculateCooling); @@ -35,7 +35,7 @@ _temperature = [_temperature, _barrelMass, CBA_missionTime - _lastTime] call FUN TRACE_1("cooledTo",_temperature); // Calculate heating // Steel Heat Capacity = 466 J/(Kg.K) -_temperature = _temperature + _heatIncrement / (_barrelMass * 466); +_temperature = _temperature + _heatIncrement * GVAR(heatCoef) / (_barrelMass * 466); // Publish the temperature variable [_unit, _tempVarName, _temperature, TEMP_TOLERANCE] call EFUNC(common,setApproximateVariablePublic); diff --git a/addons/overheating/functions/fnc_updateTemperatureThread.sqf b/addons/overheating/functions/fnc_updateTemperatureThread.sqf index 03f2bee53f..670a330c51 100644 --- a/addons/overheating/functions/fnc_updateTemperatureThread.sqf +++ b/addons/overheating/functions/fnc_updateTemperatureThread.sqf @@ -15,9 +15,20 @@ * Public: No */ -private _currentWeapon = currentWeapon ACE_player; -if ((_currentWeapon != "") && {_currentWeapon == primaryWeapon ACE_player || {_currentWeapon == handgunWeapon ACE_player}}) then { - [ACE_player, _currentWeapon, 0] call FUNC(updateTemperature); +if (ACE_player call EFUNC(common,isSwimming)) then { // cool off both weapons while swimming because currentWeapon == "" + private _primaryWeapon = primaryWeapon ACE_player; + private _handgunWeapon = handgunWeapon ACE_player; + if (_primaryWeapon != "") then { + [ACE_player, _primaryWeapon, 0] call FUNC(updateTemperature); + }; + if (_handgunWeapon != "") then { + [ACE_player, _handgunWeapon, 0] call FUNC(updateTemperature); + }; +} else { + private _currentWeapon = currentWeapon ACE_player; + if ((_currentWeapon != "") && {_currentWeapon == primaryWeapon ACE_player || {_currentWeapon == handgunWeapon ACE_player}}) then { + [ACE_player, _currentWeapon, 0] call FUNC(updateTemperature); + }; }; // Schedule for execution again after 5 seconds diff --git a/addons/overheating/initSettings.sqf b/addons/overheating/initSettings.sqf index 74e49c2bcb..3b7f1618d4 100644 --- a/addons/overheating/initSettings.sqf +++ b/addons/overheating/initSettings.sqf @@ -5,6 +5,16 @@ private _category = format ["ACE %1", localize LSTRING(DisplayName)]; [LSTRING(enabled_displayName), LSTRING(enabled_description)], _category, true, + 1, + {}, + true +] call CBA_fnc_addSetting; + +[ + QGVAR(heatCoef), "SLIDER", + [LSTRING(heatCoef_displayName), LSTRING(heatCoef_description)], + _category, + [0, 5, 1, 2], 1 ] call CBA_fnc_addSetting; @@ -21,7 +31,9 @@ private _category = format ["ACE %1", localize LSTRING(DisplayName)]; [LSTRING(showParticleEffectsForEveryone_displayName), LSTRING(showParticleEffectsForEveryone_description)], _category, false, - 0 + 0, + {}, + true ] call CBA_fnc_addSetting; [ @@ -29,6 +41,24 @@ private _category = format ["ACE %1", localize LSTRING(DisplayName)]; [LSTRING(overheatingDispersion_displayName), LSTRING(overheatingDispersion_description)], _category, true, + 1, + {}, + true +] call CBA_fnc_addSetting; + +[ + QGVAR(particleEffectsAndDispersionDistance), "SLIDER", + [LSTRING(particleEffectsAndDispersionDistance_displayName), LSTRING(particleEffectsAndDispersionDistance_description)], + _category, + [1, 5000, 3000, 0], + 0 +] call CBA_fnc_addSetting; + +[ + QGVAR(overheatingRateOfFire), "CHECKBOX", + [LSTRING(overheatingRateOfFire_displayName), LSTRING(overheatingRateOfFire_description)], + _category, + true, 1 ] call CBA_fnc_addSetting; @@ -40,11 +70,29 @@ private _category = format ["ACE %1", localize LSTRING(DisplayName)]; 0 ] call CBA_fnc_addSetting; +[ + QGVAR(jamChanceCoef), "SLIDER", + [LSTRING(jamChanceCoef_displayName), LSTRING(jamChanceCoef_description)], + _category, + [0, 5, 1, 2], + 0 +] call CBA_fnc_addSetting; + [ QGVAR(unJamOnreload), "CHECKBOX", [LSTRING(unJamOnreload_displayName), LSTRING(unJamOnreload_description)], _category, false, + 1, + {}, + true +] call CBA_fnc_addSetting; + +[ + QGVAR(unJamOnSwapBarrel), "CHECKBOX", + [LSTRING(unJamOnSwapBarrel_displayName), LSTRING(unJamOnSwapBarrel_description)], + _category, + false, 1 ] call CBA_fnc_addSetting; @@ -55,3 +103,13 @@ private _category = format ["ACE %1", localize LSTRING(DisplayName)]; [0, 1, 0.1, 2], 1 ] call CBA_fnc_addSetting; + +[ + QGVAR(cookoffCoef), "SLIDER", + [LSTRING(cookoffCoef_displayName), LSTRING(cookoffCoef_description)], + _category, + [0, 5, 1, 2], + 1, + {}, + true +] call CBA_fnc_addSetting; diff --git a/addons/overheating/script_component.hpp b/addons/overheating/script_component.hpp index d7dde59da2..0837733cd3 100644 --- a/addons/overheating/script_component.hpp +++ b/addons/overheating/script_component.hpp @@ -18,6 +18,7 @@ #define TEMP_TOLERANCE 50 #define METAL_MASS_RATIO 0.55 +#define GUNPOWDER_IGNITION_TEMP 180 #ifdef DEBUG_MODE_FULL #define TRACE_PROJECTILE_INFO(BULLET) _vdir = vectorNormalized velocity BULLET; _dir = (_vdir select 0) atan2 (_vdir select 1); _up = asin (_vdir select 2); _mv = vectorMagnitude velocity BULLET; TRACE_3("adjusted projectile",_dir,_up,_mv); diff --git a/addons/overheating/stringtable.xml b/addons/overheating/stringtable.xml index ff7e2c1152..dc1cfed94b 100644 --- a/addons/overheating/stringtable.xml +++ b/addons/overheating/stringtable.xml @@ -16,8 +16,44 @@ Surchauffe Přehřívání + + Overheating Enabled + Überhitzung aktiviert + Activado Sobrecalentamiento + Superaquecimento ativado + Surchauffe activée + Перегрев включен + Přehřívání povoleno + 過熱を有効化 + Przegrzewanie włączone + 과열 활성화 + Surriscaldamento Abilitato + 启用过热 + 啟用過熱 + + + Master enable for the overheating/jamming module + Activación maestra para Sobrecalentamiento + Chave mestra para o módulo de superaquecimento/emperramento + Active le module de surchauffe/d'enrayement. + Главный включатель для модуля перегрева/заклинивания + 過熱と弾詰まりモジュールを全て有効化します + Główny włącznik modułu przegrzewania/zacinania się broni + Hauptschalter, um die Überhitzung-/Ladehemmung-Module zu aktivieren + 과열/탄걸림 최종 활성화 + Abilitazione master per il modulo di surriscaldamento / inceppamento + 启用枪管过热/干扰模块 + 啟用槍管過熱/干擾模塊 + Hlavní přepínač pro modul přehřívání/zasekávání + + + Heating Coefficient + + + Coefficient for the amount of heat a weapon generates per shot.\nHigher value increases heat. + - Display text on jam + Display Text on Jam Zeige Text bei Ladehemmung Mostrar texto al encasquillarse Показывать текст, когда клинит оружие @@ -81,7 +117,7 @@ Fegyvertúlmelegedést okozó részecskehatások mutatása - Overheating Particle Effects for everyone + Overheating Particle Effects for Everyone Zeige Partikeleffekt bei Überhitzung für jeden Pokaż efekty cząsteczkowe dla wszystkich Effetti Particellari Surriscaldamento per tutti @@ -144,8 +180,26 @@ 過熱的武器將會有打不準和減少射擊初速的情況。適用於所有玩家 A túlmelegedett fegyverek kevésbé lesznek pontosak és csökkent a lövés sebessége. Minden játékosra vonatkozik. - - Unjam weapon on reload + + Distance for Effects and Dispersion + + + The distance, in meters, from the player within which overheating particle effects and dispersion are visible. + + + Heat Increases Fire Rate + + + As weapons heat up, their rate of fire increases by up to 10%. + + + Jam Chance Coefficient + + + Coefficient for the chance that a weapon will jam from overheating.\nHigher value make jams more likely.\nSet to 0 to disable jamming. + + + Unjam Weapon on Reload Behebt Ladehemmung beim Nachladen Desencasquillar el arma al recargar. Usuń zacięcie przy przeładowaniu @@ -160,7 +214,7 @@ 重裝彈匣以解決卡彈 Távolítsa el az akadályt újratöltéskor - + Reloading clears a weapon jam. Nachladen behebt eine Ladehemmung. Recargar el arma la desencasquilla. @@ -176,8 +230,14 @@ 利用重裝彈匣來解決卡彈 Az újratöltés megszünteti a fegyver elakadását. + + Unjam on Barrel Swap + + + Controls whether swapping barrels clears a weapon jam. + - Chance of unjam failing + Chance of Unjam Failing Wahrscheinlichkeit, dass Ladehemmung nicht behoben wird Probabilidad de falla al desencasquillar. Szansa na porażkę usuw. zacięcia @@ -208,6 +268,12 @@ 清除卡彈時有可能會失敗,需要反覆進行清槍。 Valószínűsége annak, hogy egy akadály eltávolítás művelet kudarcot vall, megismétlést igényel. + + Overheating Cookoff Coefficient + + + Coefficient for the heat required for cookoffs to occur.\nHigher values require more heat to cookoff.\nSet to 0 to disable cookoff. + Spare barrel Ersatzlauf @@ -256,6 +322,21 @@ 武器卡弹! 武器卡彈! + + Weapon cooked off! + + + Failure to eject. + + + Failure to extract. + + + Failure to feed. + + + Failure to fire. + Clear jam Ladehemmung beheben @@ -429,6 +510,24 @@ 正在检查枪管温度... 檢查槍管溫度中... + + Cool weapon with... + + + Cooling %1 with %2. + + + Cool weapon in water source. + + + Cooling weapon in water source. + + + Container doesn't have enough water. + + + Weapon is cool enough the water has stopped boiling. + Temperature Temperatur @@ -520,35 +619,5 @@ 备用枪管温度超级热 備用槍管溫度超級熱 - - Overheating Enabled - Überhitzung aktiviert - Activado Sobrecalentamiento - Superaquecimento ativado - Surchauffe activée - Перегрев включен - Přehřívání povoleno - 過熱を有効化 - Przegrzewanie włączone - 과열 활성화 - Surriscaldamento Abilitato - 启用过热 - 啟用過熱 - - - Master enable for the overheating/jamming module - Activación maestra para Sobrecalentamiento - Chave mestra para o módulo de superaquecimento/emperramento - Active le module de surchauffe/d'enrayement. - Главный включатель для модуля перегрева/заклинивания - 過熱と弾詰まりモジュールを全て有効化します - Główny włącznik modułu przegrzewania/zacinania się broni - Hauptschalter, um die Überhitzung-/Ladehemmung-Module zu aktivieren - 과열/탄걸림 최종 활성화 - Abilitazione master per il modulo di surriscaldamento / inceppamento - 启用枪管过热/干扰模块 - 啟用槍管過熱/干擾模塊 - Hlavní přepínač pro modul přehřívání/zasekávání - diff --git a/docs/wiki/development/ace3-config-entries.md b/docs/wiki/development/ace3-config-entries.md index a11ec0499b..f018246a99 100644 --- a/docs/wiki/development/ace3-config-entries.md +++ b/docs/wiki/development/ace3-config-entries.md @@ -49,6 +49,7 @@ ace_recoil_shakemultiplier ace_overpressure_angle ace_overpressure_range ace_overpressure_damage +ace_overheating_closedbolt ace_overheating_dispersion ace_overheating_slowdownfactor ace_overheating_jamchance diff --git a/docs/wiki/feature/overheating.md b/docs/wiki/feature/overheating.md index 3739b2aa52..13ecc83964 100644 --- a/docs/wiki/feature/overheating.md +++ b/docs/wiki/feature/overheating.md @@ -15,27 +15,36 @@ version: ## 1. Overview ### 1.1 Weapon Jamming -Adds a probability to jam a weapon when firing. Jams can be cleared by reloading or by using the clear jam-key. + +Adds a probability to jam a weapon when firing. Jams can be cleared by reloading, using the clear jam-key, or using the self interaction menu. ### 1.2 Temperature simulation -Introduces weapon temperature simulation depending on weapon and bullet mass. Hot weapons are more prone to jamming. Depending on weapon type the accuracy and in extreme cases the muzzle velocity might be reduced on high temperatures. Adds smoke puff and heat refraction effects to indicate this. -### 1.3 Spare barrels -Adds the ability to changes barrels on machine guns to compensate for those effects. +Introduces weapon temperature simulation depending on weapon and bullet mass. Hot weapons are more prone to jamming and will have an increase in their cyclic rate of fire. Depending on weapon type the accuracy and in extreme cases the muzzle velocity might be reduced on high temperatures. Adds smoke puff and heat refraction effects to indicate this. +### 1.3 Cookoff + +Hot weapons can also cause chambered ammunition to spontaneously ignite. The higher the temperature of the weapon the sooner a cookoff can happen. Open bolt weapons (most machineguns) cannot cookoff unless jammed. Jammed weapons will not cookoff unless the jam is a failure to fire. + +### 1.4 Spare barrels + +Adds the ability to changes barrels on machine guns to compensate for those effects. Changing the barrel can also unjam the gun. ## 2. Usage ### 2.1 Clearing a jammed weapon + - To clear a jammed weapon, press SHIFT + R (ACE3 default key bind `Clear jam`). ### 2.2 Swapping barrels + - For this you need a `Spare barrel` and a compatible weapon. - Press self interaction Ctrl + ⊞ Win (ACE3 default key bind `Self Interaction Key`). - Select `Equipment`. - Select `Swap barrel`. ### 2.3 Checking your barrel temperature + - Press self interaction Ctrl + ⊞ Win. - Select `Equipment`. - Select `Check weapon temperature`. @@ -43,6 +52,18 @@ Adds the ability to changes barrels on machine guns to compensate for those effe **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. +### 2.4 Cooling your weapon + +- Weapons and spare barrels will cool off over time. +- Cooling speed of weapons in increased in windy or rainy weather, and when swimming. +- If AceX 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 AceX Field Rations is enabled then weapons can also be cooled with the same water sources used to refill canteens and water bottles. + +### 2.5 Avoiding cookoffs + +- After a firefight unload closed bolt firearms (most rifles) until the barrel temperature has gone down to less than 180°C (two sections or less on the bar). +- Clear failure to fire jams quickly + ## 3. Dependencies {% include dependencies_list.md component="overheating" %} diff --git a/docs/wiki/framework/overheating-framework.md b/docs/wiki/framework/overheating-framework.md index d714f4d854..b72294d20e 100644 --- a/docs/wiki/framework/overheating-framework.md +++ b/docs/wiki/framework/overheating-framework.md @@ -27,7 +27,16 @@ class CfgWeapons { }; ``` -### 1.2 Custom jam clearing animation +### 1.2 Custom jam types + +```cpp +class CfgWeapons { + class Pistol_Base_F; + class MyRevolver : Pistol_Base_F { + ace_overheating_jamTypesAllowed = ["Fire","Dud"]; //Allowed and default values are ["Eject", "Extract", "Feed", "Fire", "Dud"]. In the example here a revolver does not eject, extract, or feed on each shot to those values are removed. + }; +``` +### 1.3 Custom jam clearing animation ```cpp class CfgWeapons { @@ -36,3 +45,19 @@ class CfgWeapons { }; }; ``` + +### 1.4 Cook Off + +```cpp +class CfgWeapons { + class Rifle_Long_Base_F ; + + class MySniper : Rifle_Long_Base_F { + ace_overheating_closedBolt = 1; // Closed bolt, can cook off from barrel heat. + }; + + class MyMG : Rifle_Long_Base_F { + ace_overheating_closedBolt = 0; // Open bolt, can only cook off on failure to fire type jams. + }; +}; +``` diff --git a/optionals/compat_rhs_gref3/CfgWeapons.hpp b/optionals/compat_rhs_gref3/CfgWeapons.hpp index d7c0418982..9728eb1547 100644 --- a/optionals/compat_rhs_gref3/CfgWeapons.hpp +++ b/optionals/compat_rhs_gref3/CfgWeapons.hpp @@ -107,6 +107,14 @@ class CfgWeapons { ACE_barrelLength = 610.0; }; + class rhs_weap_Izh18 : Rifle_Base_F { + ace_overheating_jamTypesAllowed = ["Fire","Dud"]; + }; + + class rhs_weap_m79 : Rifle_Base_F { + ace_overheating_jamTypesAllowed = ["Fire","Dud"]; + }; + CREATE_CSW_PROXY(rhs_weap_DSHKM); class Launcher; diff --git a/optionals/compat_rhs_usf3/CfgWeapons.hpp b/optionals/compat_rhs_usf3/CfgWeapons.hpp index 915178386b..9b9dec9343 100644 --- a/optionals/compat_rhs_usf3/CfgWeapons.hpp +++ b/optionals/compat_rhs_usf3/CfgWeapons.hpp @@ -157,6 +157,9 @@ class CfgWeapons { ACE_twistDirection = 0; ACE_barrelLength = 508.0; }; + class rhs_weap_m32_Base_F : Rifle_Base_F { + ace_overheating_jamTypesAllowed = ["Fire","Dud"]; + }; class SMG_02_base_F; class rhsusf_weap_MP7A1_base_f: SMG_02_base_F { ACE_barrelLength = 180; @@ -180,6 +183,10 @@ class CfgWeapons { ACE_barrelTwist = 248.92; ACE_barrelLength = 124.46; }; + class rhs_weap_M320_Base_F: Pistol_Base_F { + ace_overheating_jamTypesAllowed = ["Fire","Dud"]; + }; + // RHS sniper scopes class ItemCore; class InventoryOpticsItem_Base_F;