Merge pull request #3438 from acemod/spareBarrelSimulation

AAR actions + track temperatures of spare barrels
This commit is contained in:
Glowbal 2016-05-07 21:48:03 +02:00
commit 5ff91484ed
20 changed files with 505 additions and 137 deletions

View File

@ -38,4 +38,10 @@ class ACE_Settings {
displayName = CSTRING(unJamFailChance_displayName);
description = CSTRING(unJamFailChance_description);
};
class GVAR(enabled) {
typeName = "BOOL";
value = 1;
displayName = CSTRING(enabled_displayName);
description = CSTRING(enabled_description);
};
};

View File

@ -16,11 +16,3 @@ class Extended_PostInit_EventHandlers {
init = QUOTE( call COMPILE_FILE(XEH_postInit) );
};
};
class Extended_Take_EventHandlers {
class CAManBase {
class GVAR(UnjamReload) {
clientTake = QUOTE( _this call FUNC(handleTakeEH) );
};
};
};

View File

@ -0,0 +1,11 @@
class CfgMagazines {
class CA_Magazine;
class ACE_SpareBarrel: CA_Magazine {
displayName = CSTRING(SpareBarrelName);
descriptionshort = CSTRING(SpareBarrelDescription);
picture = QUOTE(PATHTOF(UI\spare_barrel_ca.paa));
count = 1;
mass = 60;
ACE_isUnique = 1;
};
};

View File

@ -4,32 +4,59 @@ class CfgVehicles {
class CAManBase: Man {
class ACE_SelfActions {
class ACE_Equipment {
class ACE_UnJam {
class GVAR(UnJam) {
displayName = CSTRING(UnjamWeapon);
condition = QUOTE( [_player] call FUNC(canUnjam) );
condition = QUOTE( GVAR(enabled) && {[_player] call FUNC(canUnjam)} );
exceptions[] = {"isNotInside", "isNotSitting"};
statement = QUOTE( [ARR_2(_player, currentMuzzle _player)] call FUNC(clearJam); );
showDisabled = 0;
priority = 4;
icon = QUOTE(PATHTOF(UI\unjam_ca.paa));
};
class ACE_SwapBarrel {
class GVAR(SwapBarrel) {
displayName = CSTRING(SwapBarrel);
condition = QUOTE( 'ACE_SpareBarrel' in items _player && {getNumber (configFile >> 'CfgWeapons' >> currentWeapon _player >> 'ACE_Overheating_allowSwapBarrel') == 1} );
statement = QUOTE( [ARR_2(_player, currentWeapon _player)] call FUNC(swapBarrel); );
condition = QUOTE( GVAR(enabled) && {'ACE_SpareBarrel' in magazines _player} && {getNumber (configFile >> 'CfgWeapons' >> currentWeapon _player >> 'ACE_Overheating_allowSwapBarrel') == 1} );
statement = QUOTE( [ARR_3(_player, _player, currentWeapon _player)] call FUNC(swapBarrel); );
showDisabled = 0;
priority = 3;
icon = QUOTE(PATHTOF(UI\spare_barrel_ca.paa));
};
class ACE_CheckTemperature {
class GVAR(CheckTemperature) {
displayName = CSTRING(CheckTemperatureShort);
condition = "switch (currentWeapon _player) do {case (''): {false}; case (primaryWeapon _player); case (secondaryWeapon _player); case (handgunWeapon _player): {true}; default {false}}";
condition = "ace_overheating_enabled && {switch (currentWeapon _player) do {case (''): {false}; case (primaryWeapon _player); case (handgunWeapon _player): {true}; default {false}}}";
exceptions[] = {"isNotInside", "isNotSitting"};
statement = QUOTE( [ARR_2(_player, currentWeapon _player)] call FUNC(CheckTemperature); );
statement = QUOTE( [ARR_3(_player, _player, currentWeapon _player)] call FUNC(checkTemperature); );
showDisabled = 0;
priority = 2.9;
icon = QUOTE(PATHTOF(UI\temp_ca.paa));
};
class GVAR(CheckTemperatureSpareBarrels) {
displayName = CSTRING(CheckTemperatureSpareBarrelsShort);
condition = QUOTE( GVAR(enabled) && {'ACE_SpareBarrel' in magazines _player});
exceptions[] = {"isNotInside", "isNotSitting"};
statement = QUOTE( [_player] call FUNC(checkSpareBarrelsTemperatures); );
showDisabled = 0;
priority = 2.8;
icon = QUOTE(PATHTOF(UI\temp_ca.paa));
};
};
};
class ACE_Actions {
class ACE_Weapon {
class GVAR(SwapBarrel) {
displayName = CSTRING(SwapBarrel);
condition = QUOTE( GVAR(enabled) && {'ACE_SpareBarrel' in magazines _player} && {getNumber (configFile >> 'CfgWeapons' >> currentWeapon _target >> 'ACE_Overheating_allowSwapBarrel') == 1} );
statement = QUOTE([ARR_3(_player, _target, currentWeapon _target)] call FUNC(swapBarrelAssistant););
icon = QUOTE(PATHTOF(UI\spare_barrel_ca.paa));
};
class GVAR(CheckTemperature) {
displayName = CSTRING(CheckTemperatureShort);
condition = "ace_overheating_enabled && {switch (currentWeapon _target) do {case (''): {false}; case (primaryWeapon _target); case (handgunWeapon _target): {true}; default {false}}}";
exceptions[] = {"isNotInside", "isNotSitting"};
statement = QUOTE( [ARR_3(_player, _target, currentWeapon _target)] call FUNC(checkTemperature); );
icon = QUOTE(PATHTOF(UI\temp_ca.paa));
};
};
};
};

View File

@ -2,17 +2,6 @@ class CfgWeapons {
class ACE_ItemCore;
class InventoryItem_Base_F;
class ACE_SpareBarrel: ACE_ItemCore {
displayname = CSTRING(SpareBarrelName);
descriptionshort = CSTRING(SpareBarrelDescription);
//model = "";
picture = QUOTE(PATHTOF(UI\spare_barrel_ca.paa));
scope = 2;
class ItemInfo: InventoryItem_Base_F {
mass = 30;
};
};
class RifleCore;
class Rifle: RifleCore {
//Mean Rounds Between Stoppages (this will be scaled based on the barrel temp)

View File

@ -1,5 +1,7 @@
PREP(calculateCooling);
PREP(canUnjam);
PREP(checkSpareBarrelsTemperatures);
PREP(checkTemperature);
PREP(clearJam);
PREP(displayTemperature);
@ -7,8 +9,12 @@ PREP(firedEH);
PREP(getWeaponData);
PREP(handleTakeEH);
PREP(jamWeapon);
PREP(loadCoolestSpareBarrel);
PREP(overheat);
PREP(sendSpareBarrelsTemperaturesHint);
PREP(swapBarrel);
PREP(swapBarrelAssistant);
PREP(swapBarrelCallback);
PREP(updateSpareBarrelsTemperaturesThread);
PREP(updateTemperature);
PREP(updateTemperatureThread);

View File

@ -1,47 +1,69 @@
// by esteldunedain
#include "script_component.hpp"
if (isServer) then {
GVAR(pseudoRandomList) = [];
// Construct a list of pseudo random 2D vectors
for "_i" from 0 to 30 do {
GVAR(pseudoRandomList) pushBack [-1 + random 2, -1 + random 2];
};
publicVariable QGVAR(pseudoRandomList);
if (hasInterface) then {
// Add keybinds
["ACE3 Weapons", QGVAR(unjamWeapon), localize LSTRING(UnjamWeapon),
{
// Conditions: canInteract
if !([ACE_player, objNull, ["isNotInside"]] call EFUNC(common,canInteractWith)) exitWith {false};
// Conditions: specific
if !(GVAR(enabled) && {[ACE_player] call FUNC(canUnjam)}) exitWith {false};
// Statement
[ACE_player, currentMuzzle ACE_player, false] call FUNC(clearJam);
true
},
{false},
[19, [true, false, false]], false] call CBA_fnc_addKeybind; //SHIFT + R Key
};
if !(hasInterface) exitWith {};
GVAR(cacheWeaponData) = call CBA_fnc_createNamespace;
GVAR(cacheAmmoData) = call CBA_fnc_createNamespace;
GVAR(cacheSilencerData) = call CBA_fnc_createNamespace;
// Add keybinds
["ACE3 Weapons", QGVAR(unjamWeapon), localize LSTRING(UnjamWeapon),
{
// Conditions: canInteract
if !([ACE_player, objNull, ["isNotInside"]] call EFUNC(common,canInteractWith)) exitWith {false};
// Conditions: specific
if !([ACE_player] call FUNC(canUnjam)) exitWith {false};
// Statement
[ACE_player, currentMuzzle ACE_player, false] call FUNC(clearJam);
true
},
{false},
[19, [true, false, false]], false] call CBA_fnc_addKeybind; //SHIFT + R Key
// Schedule cool down calculation of player weapons at (infrequent) regular intervals
[] call FUNC(updateTemperatureThread);
["SettingsInitialized", {
TRACE_1("SettingsInitialized eh", GVAR(enabled));
if (!GVAR(enabled)) exitWith {};
if (isServer) then {
GVAR(pseudoRandomList) = [];
// Construct a list of pseudo random 2D vectors
for "_i" from 0 to 30 do {
GVAR(pseudoRandomList) pushBack [-1 + random 2, -1 + random 2];
};
publicVariable QGVAR(pseudoRandomList);
// Keep track of the temperature of stored spare barrels
GVAR(storedSpareBarrels) = [] call CBA_fnc_hashCreate;
// Install event handlers for spare barrels
["spareBarrelsSendTemperatureHint", FUNC(sendSpareBarrelsTemperaturesHint)] call EFUNC(common,addEventHandler);
["spareBarrelsLoadCoolest", FUNC(loadCoolestSpareBarrel)] call EFUNC(common,addEventHandler);
// Schedule cool down calculation of stored spare barrels
[] call FUNC(updateSpareBarrelsTemperaturesThread);
};
if !(hasInterface) exitWith {};
GVAR(cacheWeaponData) = call CBA_fnc_createNamespace;
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;
// Register fire event handler
["firedPlayer", DFUNC(firedEH)] call EFUNC(common,addEventHandler);
// Only add eh to non local players if dispersion is enabled
if (GVAR(overheatingDispersion)) then {
["firedPlayerNonLocal", DFUNC(firedEH)] call EFUNC(common,addEventHandler);
};
// Schedule cool down calculation of player weapons at (infrequent) regular intervals
[] call FUNC(updateTemperatureThread);
// Install event handler to display temp when a barrel was swapped
["showWeaponTemperature", DFUNC(displayTemperature)] call EFUNC(common,addEventHandler);
// Install event handler to initiate an assisted barrel swap
["initiateSwapBarrelAssisted", DFUNC(swapBarrel)] call EFUNC(common,addEventHandler);
}] call EFUNC(common,addEventHandler);

View File

@ -4,4 +4,16 @@ ADDON = false;
#include "XEH_PREP.hpp"
if (isNil "CBA_fnc_getMagazineIndex") then {
CBA_fnc_getMagazineIndex = {
params [["_unit", objNull, [objNull]], ["_magazine", "", [""]]];
private _displayName = getText (configFile >> "CfgMagazines" >> _magazine >> "displayName");
if (_displayName isEqualTo "") exitWith {[]};
(magazinesDetail _unit select {_x find _displayName == 0}) apply {_x = _x splitString "[:]"; _x select (count _x - 1)};
};
};
ADDON = true;

View File

@ -18,6 +18,8 @@ class CfgPatches {
#include "CfgVehicles.hpp"
#include "CfgMagazines.hpp"
#include "CfgWeapons.hpp"
#include "ACE_Settings.hpp"

View File

@ -0,0 +1,58 @@
/*
* Author: esteldunedain
* Calculate the cooling down of a weapon over a time interval.
*
* Argument:
* 0: Initial temperature <NUMBER>
* 1: Barrel mass <NUMBER>
* 2: Time interval <NUMBER>
*
* Return value:
* Final temperature <NUMBER>
*
* Example:
* [_temperature, _barrelMass, _totalTime] call ace_overheating_fnc_calculateCooling
*
* Public: No
*/
#include "script_component.hpp"
params ["_temperature", "_barrelMass", "_totalTime"];
// 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};
//AR-15 (0.00570m bullet diameter) (barrel diameter usually 0.75" or 0.008255m radius)
//Steel Denisty = 7850 m^3 / kg
//Area of a cylinder (2/r)*(Pi * r^3 + V) - for a 0.008255m radius barrel -> Area = 210(1/meters) * Volume
//Adjusted volume for being hollowed out is ~1.1x
//So Area = 210 * 1.1 * (mass / 7850) = mass * 0.029427 (for steel near that diameter)
private _barrelSurface = _barrelMass * 0.029427;
TRACE_4("cooling",_temperature,_totalTime,_barrelMass,_barrelSurface);
private _time = 0;
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 )
) * _deltaTime / (_barrelMass * 466);
if (_temperature < 1) exitWith {0};
if (isNil "_temperature") exitWith {
diag_log text format ["[ACE] ERROR: _totalTime = %1; _time = %2; _deltaTime = %3;", _totalTime, _time, _deltaTime];
0
};
_time = _time + _deltaTime;
if (_time >= _totalTime) exitWith { _temperature max 0 };
};

View File

@ -0,0 +1,38 @@
/*
* Author: esteldunedain
* Make the player check the temperature of his spare barrels
*
* Arguments:
* 0: Player <OBJECT>
*
* Return Value:
* None
*
*
* Public: No
*/
#include "script_component.hpp"
params ["_player"];
// Check canInteractWith:
if (!([_player, objNull, ["isNotInside", "isNotSitting"]] call EFUNC(common,canInteractWith))) exitWith {};
// Make the unit go kneeling
[_player] call EFUNC(common,goKneeling);
// Spawn a progress bar
[
5.0,
[_player],
{
params ["_args", "_elapsedTime", "_totalTime", "_errorCode"];
_args params ["_player"];
// Time has enlapsed, ask the server to send the hint
['spareBarrelsSendTemperatureHint', [_player, _player]] call EFUNC(common,serverEvent);
},
{},
(localize LSTRING(CheckingSpareBarrelsTemperatures)),
{true},
["isNotInside", "isNotSitting"]
] call EFUNC(common,progressBar);

View File

@ -3,8 +3,9 @@
* Make the player check the temperature of his weapon
*
* Arguments:
* 0: Player <OBJECT>
* 1: Weapon <STRING>
* 0: Unit checking <OBJECT>
* 1: Unit that has the weapon <OBJECT>
* 2: Weapon <STRING>
*
* Return Value:
* None
@ -16,17 +17,18 @@
*/
#include "script_component.hpp"
params ["_player", "_weapon"];
TRACE_2("params",_player,_weapon);
params ["_assistant", "_gunner", "_weapon"];
TRACE_3("params",_assistant,_gunner,_weapon);
// Play animation and report temperature
private _action = getText (configFile >> "CfgWeapons" >> _weapon >> "ACE_checkTemperatureAction");
if (_action == "") then {
_action = "Gear";
private _action = "PutDown";
if (_assistant isEqualTo _gunner) then {
_action = getText (configFile >> "CfgWeapons" >> _weapon >> "ACE_checkTemperatureAction");
if (_action == "") then {
_action = "Gear";
};
};
_player playActionNow _action;
_assistant playActionNow _action;
// Waits a sec before displaying the temperature
[FUNC(displayTemperature), [_player, _weapon], 1.0] call EFUNC(common,waitAndExecute);
[FUNC(displayTemperature), [_gunner, _weapon], 1.0] call EFUNC(common,waitAndExecute);

View File

@ -0,0 +1,53 @@
/*
* Author: esteldunedain
* Collect the temperature of all the spare barrels a unit has and load the
* coolest on the unit weapon. Runs on the server.
*
* Argument:
* 0: Unit that has the spare barrels <OBJECT>
* 1: Unit that has the weapon <OBJECT>
* 2: Weapon <STRING>
* 3: Weapon temp before switching <NUMBER>
* 4: Mass of the removed barrel <NUMBER>
*
* Return value:
* None
*
*
* Public: No
*/
#include "script_component.hpp"
params ["_assistant", "_gunner", "_weapon", "_weaponTemp", "_barrelMass"];
TRACE_5("loadCoolestSpareBarrel1",_assistant,_gunner,_weapon,_weaponTemp,_barrelMass);
// Find all spare barrel the player has
private _allBarrels = [_assistant, "ACE_SpareBarrel"] call CBA_fnc_getMagazineIndex;
TRACE_1("_allBarrels",_allBarrels);
if ((count _allBarrels) < 1) exitWith {};
// Determine which on is coolest
private _coolestTemp = 10000;
private _coolestMag = _allBarrels select 0;
{
private _temp = 0;
if ([GVAR(storedSpareBarrels), _x] call CBA_fnc_hashHasKey) then {
_temp = ([GVAR(storedSpareBarrels), _x] call CBA_fnc_hashGet) select 0;
};
TRACE_2("loadCoolestSpareBarrel4",_x,_temp);
if (_temp < _coolestTemp) then {
_coolestTemp = _temp;
_coolestMag = _x;
};
} forEach _allBarrels;
TRACE_3("loadCoolestSpareBarrel5",_coolestTemp,_coolestMag,_weaponTemp);
// The new weapon temperature is similar to the coolest barrel
// Publish the new temperature value
_gunner setVariable [format [QGVAR(%1_temp), _weapon], _coolestTemp, true];
// Heat up the coolest barrel to the former weapon temperature
[GVAR(storedSpareBarrels), _coolestMag, [_weaponTemp, ACE_Time, _barrelMass]] call CBA_fnc_hashSet;
// Send an event so the machines of the assistant and gunner can show the hint
["showWeaponTemperature", [_assistant, _gunner], [_gunner, _weapon]] call EFUNC(common,targetEvent);

View File

@ -0,0 +1,76 @@
/*
* Author: esteldunedain
* Collect the temperature of all the spare barrels a unit has and send a hint
* to a client. Runs on the server.
*
* Argument:
* 0: Target unit of the hint <OBJECT>
* 1: Unit that has the spare barrels <STRING>
*
* Return value:
* None
*
*
* Public: No
*/
#include "script_component.hpp"
params ["_player","_unit"];
// Find all spare barrel the player has
TRACE_2("sendSpareBarrelsTemperatureHunt",_player,_unit);
private _allBarrels = [_unit, "ACE_SpareBarrel"] call CBA_fnc_getMagazineIndex;
TRACE_1("_allBarrels",_allBarrels);
if ((count _allBarrels) < 1) exitWith {};
// Determine the temp of each barrel
private _temps = [];
{
private _temp = 0;
if ([GVAR(storedSpareBarrels), _x] call CBA_fnc_hashHasKey) then {
_temp = ([GVAR(storedSpareBarrels), _x] call CBA_fnc_hashGet) select 0;
};
_temps pushBack _temp;
} forEach _allBarrels;
TRACE_1("_temps",_temps);
// Count cool
private _countCool = {_x < 20} count _temps;
private _countWarm = {(_x >= 20) && (_x < 100)} count _temps;
private _countHot = {(_x >= 100) && (_x < 200)} count _temps;
private _countVeryHot = {(_x >= 200) && (_x < 600)} count _temps;
private _countExtremelyHot = {_x >= 600} count _temps;
private _output = ["%1 %2%3%4 %5%6%7 %8%9%10 %11%12%13 %14"];
private _size = 1.0;
if (_countCool > 0) then {
_output pushBack _countCool;
_output pushBack LSTRING(BarrelCool);
_output pushBack "<br/>";
_size = _size + 0.5;
};
if (_countWarm > 0) then {
_output pushBack _countWarm;
_output pushBack LSTRING(BarrelWarm);
_output pushBack "<br/>";
_size = _size + 0.5;
};
if (_countHot > 0) then {
_output pushBack _countHot;
_output pushBack LSTRING(BarrelHot);
_output pushBack "<br/>";
_size = _size + 0.5;
};
if (_countVeryHot > 0) then {
_output pushBack _countVeryHot;
_output pushBack LSTRING(BarrelVeryHot);
_output pushBack "<br/>";
_size = _size + 0.5;
};
if (_countExtremelyHot > 0) then {
_output pushBack _countExtremelyHot;
_output pushBack LSTRING(BarrelExtremelyHot);
_size = _size + 0.5;
};
TRACE_1("_output",_output);
["displayTextStructured", [_player], [_output, _size, _player]] call EFUNC(common,targetEvent);

View File

@ -3,29 +3,35 @@
* Make a unit start swapping it's barrel
*
* Argument:
* 0: Unit <OBJECT>
* 1: Weapon <STRING>
* 0: Unit initiating the action <OBJECT>
* 1: Unit that has the weapon <OBJECT>
* 2: Weapon <STRING>
*
* Return value:
* None
*
* Example:
* [player, currentWeapon player] call ace_overheating_fnc_swapBarrel
* [cursorTarget, player, currentWeapon player] call ace_overheating_fnc_swapBarrel
*
* Public: No
*/
#include "script_component.hpp"
params ["_player", "_weapon"];
TRACE_2("params",_player,_weapon);
params ["_assistant", "_gunner", "_weapon"];
TRACE_3("params",_assistant,_gunner,_weapon);
// Make the standing player kneel down
if (stance _player != "PRONE") then {
[_player, "amovpknlmstpsraswrfldnon", 1] call EFUNC(common,doAnimation);
if (stance _gunner != "PRONE") then {
[_gunner, "amovpknlmstpsraswrfldnon", 1] call EFUNC(common,doAnimation);
};
// Barrel dismount gesture
_player playActionNow QGVAR(GestureDismountMuzzle);
_gunner playActionNow QGVAR(GestureDismountMuzzle);
playSound "ACE_BarrelSwap";
[5, [_player, _weapon], {(_this select 0) call FUNC(swapBarrelCallback)}, {}, (localize LSTRING(SwappingBarrel))] call EFUNC(common,progressBar);
private _duration = 3.0;
if (_assistant isEqualTo _gunner) then {
_duration = 5.0;
};
[_duration, [_assistant,_gunner,_weapon], {(_this select 0) call FUNC(swapBarrelCallback)}, {}, (localize LSTRING(SwappingBarrel))] call EFUNC(common,progressBar);

View File

@ -0,0 +1,33 @@
/*
* Author: esteldunedain, Commy2
* Make a unit start swapping the barrel of another unit
*
* Argument:
* 0: Unit initiating the action <OBJECT>
* 1: Unit that has the weapon <OBJECT>
* 2: Weapon <STRING>
*
* Return value:
* None
*
* Example:
* [player, cursorTarget, currentWeapon cursorTarget] call ace_overheating_fnc_swapBarrelAssistant
*
* Public: No
*/
#include "script_component.hpp"
params ["_assistant", "_gunner", "_weapon"];
TRACE_3("params",_assistant,_gunner,_weapon);
// Make the standing player kneel down
if (stance _assistant != "PRONE") then {
[_assistant, "amovpknlmstpsraswrfldnon", 1] call EFUNC(common,doAnimation);
};
// Barrel dismount gesture
playSound "ACE_BarrelSwap";
[3, [_assistant, _gunner, _weapon], {}, {}, (localize LSTRING(SwappingBarrel))] call EFUNC(common,progressBar);
["initiateSwapBarrelAssisted", _gunner, [_assistant, _gunner, _weapon]] call EFUNC(common,objectEvent);

View File

@ -1,10 +1,11 @@
/*
* Author: Commy2
* Author: Commy2, esteldunedain
* Swap barrel callback
*
* Argument:
* 0: Unit <OBJECT>
* 1: Weapon <STRING>
* 0: Unit initiating the action <OBJECT>
* 1: Unit that has the weapon <OBJECT>
* 2: Weapon <STRING>
*
* Return value:
* None
@ -14,19 +15,26 @@
*
* Public: No
*/
// #define DEBUG_MODE_FULL
#include "script_component.hpp"
params ["_player", "_weapon"];
TRACE_2("params",_player,_weapon);
params ["_assistant", "_gunner", "_weapon"];
TRACE_3("params",_assistant,_gunner,_weapon);
// Barrel mount gesture
_player playAction QGVAR(GestureMountMuzzle);
playSound "ACE_BarrelSwap";
if (_assistant isEqualTo _gunner) then {
// Barrel mount gesture
_gunner playAction QGVAR(GestureMountMuzzle);
playSound "ACE_BarrelSwap";
};
// don't consume the barrel, but rotate through them.
[localize LSTRING(SwappedBarrel), QUOTE(PATHTOF(UI\spare_barrel_ca.paa))] call EFUNC(common,displayTextPicture);
private _temp = _gunner getVariable [format [QGVAR(%1_temp), _weapon], 0];
private _barrelMass = 0.50 * (getNumber (configFile >> "CfgWeapons" >> _weapon >> "WeaponSlotsInfo" >> "mass") / 22.0) max 1.0;
// Instruct the server to load the coolest spare barrel into the weapon and
// store the removed barrel with the former weapon temperature. The server
// also updates the current weapon temperature to match that of the new
// loaded barrel.
["spareBarrelsLoadCoolest", [_assistant, _gunner, _weapon, _temp, _barrelMass]] call EFUNC(common,serverEvent);
// Publish the temperature variable
_player setVariable [format [QGVAR(%1_temp), _weapon], 0, true];
// Store the update time
_player setVariable [format [QGVAR(%1_time), _weapon], ACE_time];
_gunner setVariable [format [QGVAR(%1_time), _weapon], ACE_time];

View File

@ -0,0 +1,40 @@
/*
* Author: esteldunedain
* Calculate cooldown of all the stored spare barrels.
*
* Argument:
* None
*
* Return value:
* None
*
* Example:
* [] call ace_overheating_fnc_updateSpareBarrelsTemperaturesThread
*
* Public: No
*/
// #define DEBUG_MODE_FULL
#include "script_component.hpp"
private _pairs = [];
TRACE_1("updateSpareBarrelsTemperaturesThread1",GVAR(storedSpareBarrels));
[GVAR(storedSpareBarrels), {_pairs pushBack [_key, _value];}] call CBA_fnc_hashEachPair;
TRACE_1("updateSpareBarrelsTemperaturesThread2",_pairs);
{
_x params ["_barrelMagazineID","_value"];
_value params ["_initialTemp","_initialTime", "_barrelMass"];
// Calculate cooling
private _finalTemp = [_initialTemp, _barrelMass, ACE_time - _initialTime] call FUNC(calculateCooling);
TRACE_4("updateSpareBarrelsTemperaturesThread3",_barrelMagazineID,_initialTemp,_finalTemp,_barrelMass);
if (_finalTemp < 5) then {
// The barrel is cool enough to keep calculating. Remove it from the hash
[GVAR(storedSpareBarrels), _barrelMagazineID] call CBA_fnc_hashRem;
} else {
// Store the new temp
[GVAR(storedSpareBarrels), _barrelMagazineID, [_finalTemp, ACE_time, _barrelMass]] call CBA_fnc_hashSet;
};
} forEach _pairs;
// Schedule for execution again after 10 seconds
[DFUNC(updateSpareBarrelsTemperaturesThread), [], 10] call EFUNC(common,waitAndExecute);

View File

@ -29,50 +29,8 @@ private _lastTime = _unit getVariable [_timeVarName, 0];
private _barrelMass = 0.50 * (getNumber (configFile >> "CfgWeapons" >> _weapon >> "WeaponSlotsInfo" >> "mass") / 22.0) max 1.0;
_fnc_cooling = {
params ["_temperature", "_barrelMass", "_totalTime"];
// 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};
//AR-15 (0.00570m bullet diameter) (barrel diameter usually 0.75" or 0.008255m radius)
//Steel Denisty = 7850 m^3 / kg
//Area of a cylinder (2/r)*(Pi * r^3 + V) - for a 0.008255m radius barrel -> Area = 210(1/meters) * Volume
//Adjusted volume for being hollowed out is ~1.1x
//So Area = 210 * 1.1 * (mass / 7850) = mass * 0.029427 (for steel near that diameter)
private _barrelSurface = _barrelMass * 0.029427;
TRACE_4("cooling",_temperature,_totalTime,_barrelMass,_barrelSurface);
private _time = 0;
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 )
) * _deltaTime / (_barrelMass * 466);
if (_temperature < 1) exitWith {0};
if (isNil "_temperature") exitWith {
diag_log text format ["[ACE] ERROR: _totalTime = %1; _time = %2; _deltaTime = %3;", _totalTime, _time, _deltaTime];
0
};
_time = _time + _deltaTime;
if (_time >= _totalTime) exitWith { _temperature max 0 };
};
};
// Calculate cooling
_temperature = [_temperature, _barrelMass, ACE_time - _lastTime] call _fnc_cooling;
_temperature = [_temperature, _barrelMass, ACE_time - _lastTime] call FUNC(calculateCooling);
TRACE_1("cooledTo",_temperature);
// Calculate heating
// Steel Heat Capacity = 466 J/(Kg.K)

View File

@ -251,6 +251,12 @@
<Italian>Sto controllando la temperatura...</Italian>
<Russian>Проверка температуры...</Russian>
</Key>
<Key ID="STR_ACE_Overheating_CheckTemperatureSpareBarrelsShort">
<English>Check spare barrels temperatures</English>
</Key>
<Key ID="STR_ACE_Overheating_CheckingSpareBarrelsTemperatures">
<English>Checking spare barrels temperatures...</English>
</Key>
<Key ID="STR_ACE_Overheating_Temperature">
<English>Temperature</English>
<German>Temperatur</German>
@ -263,5 +269,28 @@
<Italian>Temperatura</Italian>
<Russian>Температура</Russian>
</Key>
<Key ID="STR_ACE_Overheating_BarrelCool">
<English>Cool Spare Barrel/s</English>
</Key>
<Key ID="STR_ACE_Overheating_BarrelWarm">
<English>Warm Spare Barrel/s</English>
</Key>
<Key ID="STR_ACE_Overheating_BarrelHot">
<English>Hot Spare Barrel/s</English>
</Key>
<Key ID="STR_ACE_Overheating_BarrelVeryHot">
<English>Very Hot Spare Barrel/s</English>
</Key>
<Key ID="STR_ACE_Overheating_BarrelExtremelyHot">
<English>Extremely Hot Spare Barrel/s</English>
</Key>
<Key ID="STR_ACE_Overheating_enabled_displayName">
<English>Overheating Enabled</English>
<German>Überhitzen Aktiviert</German>
<Spanish>Activada Sobrecalentamiento</Spanish>
</Key>
<Key ID="STR_ACE_Overheating_enabled_description">
<English>Master enable for the overheating/jamming module</English>
</Key>
</Package>
</Project>