Merge branch 'missile_guidance_rewrite' into missile_manpad

This commit is contained in:
PabstMirror 2024-08-23 09:58:18 -05:00
commit ea02e6b47c
1344 changed files with 23745 additions and 83684 deletions

View File

@ -11,5 +11,8 @@ trim_trailing_whitespace = true
[*.md]
trim_trailing_whitespace = false
[*.yml]
indent_size = 2
[Makefile]
indent_style = tab

View File

@ -27,15 +27,6 @@ jobs:
- name: Validate function headers
run: python3 docs/tools/document_functions.py --debug
lint:
runs-on: ubuntu-latest
steps:
- name: Checkout the source code
uses: actions/checkout@v4
- name: Lint (sqflint)
uses: arma-actions/sqflint@master
continue-on-error: true # No failure due to many false-positives
build:
runs-on: ubuntu-latest
steps:

View File

@ -1,29 +1,65 @@
name: Extensions
on:
pull_request:
push:
paths:
- 'extensions/**'
- 'extension/**'
- 'Cargo.toml'
- 'Cargo.lock'
- '.github/workflows/extensions.yml'
jobs:
lint:
runs-on: ubuntu-latest
steps:
- name: Checkout the source code
uses: actions/checkout@master
- name: Install dependencies
run: |
rustup toolchain update stable --no-self-update
rustup default stable
rustup component add clippy rustfmt
- name: Run rustfmt
run: cargo fmt -- --check
- name: Run clippy
run: cargo clippy --all -- -Dwarnings
test:
runs-on: ubuntu-latest
container:
image: xd009642/tarpaulin
options: --security-opt seccomp=unconfined
steps:
- name: Checkout the source code
uses: actions/checkout@master
- name: Test & Coverage
run: cargo tarpaulin --verbose --no-default-features --workspace --timeout 240
build:
runs-on: ${{ matrix.os }}
strategy:
matrix:
os: [windows-latest]
arrays: [
os: { tag: "windows-latest", target: "i686-pc-windows-msvc" },
os: { tag: "windows-latest", target: "x86_64-pc-windows-msvc" },
]
runs-on: ${{ matrix.arrays.os.tag }}
steps:
- name: Checkout the source code
uses: actions/checkout@v4
- name: Build
shell: cmd
run: |
cd extensions
mkdir build
cd build
cmake .. && cmake --build .
- name: Upload Artifact
uses: actions/upload-artifact@v4
- name: Install stable Rust
uses: actions-rs/toolchain@v1
with:
name: ace3_extensions-${{ matrix.os }}-debug
path: extensions/build
target: ${{ matrix.arrays.os.target }}
toolchain: stable
default: true
- name: Rust Cache
uses: Swatinem/rust-cache@v2
- name: Build
run: cargo build --verbose
- name: Upload
uses: actions/upload-artifact@v2
with:
name: ${{ matrix.arrays.os.target }}
path: target/debug/ace.dll
if-no-files-found: error
retention-days: 30

View File

@ -33,6 +33,8 @@ jobs:
xcopy /e /h /q pullrequest\addons addons\
xcopy /e /h /q pullrequest\optionals optionals\
xcopy /e /h /q pullrequest\include include\
xcopy /y /h /q pullrequest\ace.dll ace.dll
xcopy /y /h /q pullrequest\ace_x64.dll ace_x64.dll
- name: Run HEMTT build
run: hemtt build
- name: Rename build folder

3
.gitignore vendored
View File

@ -2,8 +2,6 @@
*.zip
release/*
releases/*
extensions/vcproj32/*
extensions/vcproj64/*
.vscode/*
hemtt
hemtt.exe
@ -20,4 +18,5 @@ CHANGELOG.md
sqfvm.exe
ArmaScriptCompiler.exe
*.sqfc
target/
!extras/**/*.zip

View File

@ -34,32 +34,26 @@ workshop = [
]
[hemtt.launch.spe]
workshop = [
"450814997", # CBA_A3
]
extends = "default"
dlc = [
"spe"
]
[hemtt.launch.vn]
workshop = [
"450814997", # CBA_A3's Workshop ID
]
extends = "default"
dlc = [
"S.O.G. Prairie Fire",
]
[hemtt.launch.ws]
workshop = [
"450814997", # CBA_A3's Workshop ID
]
extends = "default"
dlc = [
"Western Sahara",
]
[hemtt.launch.rhs]
extends = "default"
workshop = [
"450814997", # CBA_A3's Workshop ID
"843425103", # RHS AFRF Workshop ID
"843577117", # RHS USAF Workshop ID
"843593391", # RHS GREF Workshop ID

1364
Cargo.lock generated Normal file

File diff suppressed because it is too large Load Diff

11
Cargo.toml Normal file
View File

@ -0,0 +1,11 @@
[workspace]
resolver = "2"
members = [
"extension"
]
[profile.release]
opt-level = "z"
lto = true
codegen-units = 1
strip = true

BIN
ace.dll Normal file

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

BIN
ace_x64.dll Normal file

Binary file not shown.

View File

@ -1,10 +1,10 @@
#include "script_component.hpp"
GVAR(currentbulletID) = -1;
#include "initKeybinds.inc.sqf"
GVAR(Protractor) = false;
GVAR(ProtractorStart) = CBA_missionTime;
GVAR(allBullets) = [];
GVAR(allBullets) = createHashMap;
GVAR(currentGrid) = 0;
if (!hasInterface) exitWith {};
@ -24,22 +24,6 @@ if (!hasInterface) exitWith {};
// Register Perframe Handler
[LINKFUNC(handleFirePFH), GVAR(simulationInterval)] call CBA_fnc_addPerFrameHandler;
//Add warnings for missing compat PBOs (only if AB is on)
{
_x params ["_modPBO", "_compatPBO"];
if ([_modPBO] call EFUNC(common,isModLoaded) && {!([_compatPBO] call EFUNC(common,isModLoaded))}) then {
WARNING_2("Weapon Mod [%1] missing ace compat pbo [%2] (from @ace\optionals)",_modPBO,_compatPBO);
};
} forEach [
["RH_acc","ace_compat_rh_acc"],
["RH_de_cfg","ace_compat_rh_de"],
["RH_m4_cfg","ace_compat_rh_m4"],
["RH_PDW","ace_compat_rh_pdw"],
["RKSL_PMII","ace_compat_rksl_pm_ii"],
["iansky_opt","ace_compat_sma3_iansky"],
["R3F_Armes","ace_compat_r3f"]
];
}] call CBA_fnc_addEventHandler;
#ifdef DEBUG_MODE_FULL

View File

@ -18,10 +18,3 @@ class CfgPatches {
#include "CfgVehicles.hpp"
#include "RscTitles.hpp"
#include "ACE_Settings.hpp"
class ACE_Extensions {
class ace_advanced_ballistics {
windows = 1;
client = 1;
};
};

View File

@ -1,6 +1,6 @@
#include "..\script_component.hpp"
/*
* Author: Glowbal, Ruthberg, joko // Jonas
* Author: Glowbal, Ruthberg, joko // Jonas, Brett Mayson
* Handle the PFH for Bullets
*
* Arguments:
@ -17,7 +17,7 @@
private _deleted = false;
{
_x params ["_bullet","_caliber","_bulletTraceVisible","_index"];
_y params ["_bullet","_caliber","_bulletTraceVisible"];
if (alive _bullet) then {
private _bulletVelocity = velocity _bullet;
@ -27,13 +27,21 @@ private _deleted = false;
drop ["\A3\data_f\ParticleEffects\Universal\Refract","","Billboard",1,0.1,getPos _bullet,[0,0,0],0,1.275,1,0,[0.02*_caliber,0.01*_caliber],[[0,0,0,0.65],[0,0,0,0.2]],[1,0],0,0,"","",""];
};
_bullet setVelocity (_bulletVelocity vectorAdd (parseSimpleArray ("ace_advanced_ballistics" callExtension format["simulate:%1:%2:%3:%4:%5:%6", _index, _bulletVelocity, _bulletPosition, wind, ASLToATL(_bulletPosition) select 2, CBA_missionTime toFixed 6])));
(
"ace" callExtension ["ballistics:bullet:simulate", [
_x,
_bulletVelocity,
_bulletPosition,
wind,
ASLToATL(_bulletPosition) select 2,
CBA_missionTime toFixed 6
]]
) params ["_data", "_code"];
if (_code == 0) then {
_bullet setVelocity (_bulletVelocity vectorAdd (parseSimpleArray (_data)));
};
} else {
GVAR(allBullets) set [_forEachIndex, objNull];
_deleted = true;
GVAR(allBullets) deleteAt _x;
"ace" callExtension ["ballistics:bullet:delete", [_x]];
};
} forEach GVAR(allBullets);
if (_deleted) then {
GVAR(allBullets) = GVAR(allBullets) - [objNull];
};
} forEach GVAR(allBullets)

View File

@ -1,6 +1,6 @@
#include "..\script_component.hpp"
/*
* Author: Glowbal, Ruthberg
* Author: Glowbal, Ruthberg, Brett Mayson
*
* Handles advanced ballistics for (BulletBase) projectiles. Called from the unified fired EH only for players.
*
@ -19,7 +19,7 @@
//IGNORE_PRIVATE_WARNING ["_unit", "_weapon", "_muzzle", "_mode", "_ammo", "_magazine", "_projectile", "_vehicle", "_gunner", "_turret"];
TRACE_10("firedEH:",_unit,_weapon,_muzzle,_mode,_ammo,_magazine,_projectile,_vehicle,_gunner,_turret);
if (!(_ammo isKindOf "BulletBase")) exitWith {};
if !(_ammo isKindOf "BulletBase") exitWith {};
if (!alive _projectile) exitWith {};
if (underwater _unit) exitWith {};
@ -62,11 +62,11 @@ if (_abort) exitWith {};
// Get Weapon and Ammo Configurations
private _AmmoCacheEntry = uiNamespace getVariable format[QGVAR(%1), _ammo];
if (isNil "_AmmoCacheEntry") then {
_AmmoCacheEntry = _ammo call FUNC(readAmmoDataFromConfig);
_AmmoCacheEntry = _ammo call FUNC(readAmmoDataFromConfig);
};
private _WeaponCacheEntry = uiNamespace getVariable format[QGVAR(%1), _weapon];
if (isNil "_WeaponCacheEntry") then {
_WeaponCacheEntry = _weapon call FUNC(readWeaponDataFromConfig);
_WeaponCacheEntry = _weapon call FUNC(readWeaponDataFromConfig);
};
_AmmoCacheEntry params ["_airFriction", "_caliber", "_bulletLength", "_bulletMass", "_transonicStabilityCoef", "_dragModel", "_ballisticCoefficients", "_velocityBoundaries", "_atmosphereModel", "_ammoTempMuzzleVelocityShifts", "_muzzleVelocityTable", "_barrelLengthTable", "_muzzleVelocityVariationSD"];
@ -120,8 +120,26 @@ if (_caliber * _bulletLength * _bulletMass * _barrelTwist > 0) then {
_stabilityFactor = [_caliber, _bulletLength, _bulletMass, _barrelTwist, _muzzleVelocity, _temperature, _barometricPressure] call FUNC(calculateStabilityFactor);
};
GVAR(currentbulletID) = (GVAR(currentbulletID) + 1) % 10000;
"ace_advanced_ballistics" callExtension format["new:%1:%2:%3:%4:%5:%6:%7:%8:%9:%10:%11:%12:%13:%14:%15:%16:%17:%18", GVAR(currentbulletID), _ammoCount, _airFriction, _ballisticCoefficients, _velocityBoundaries, _atmosphereModel, _dragModel, _stabilityFactor, _twistDirection, _transonicStabilityCoef, getPosASL _projectile, _bulletVelocity, EGVAR(common,mapLatitude), EGVAR(weather,currentTemperature), EGVAR(common,mapAltitude), EGVAR(weather,currentHumidity), EGVAR(weather,currentOvercast), CBA_missionTime toFixed 6];
GVAR(allBullets) pushBack [_projectile, _caliber, _bulletTraceVisible, GVAR(currentbulletID)];
("ace" callExtension [
"ballistics:bullet:new", [
_ammoCount,
_airFriction,
_ballisticCoefficients,
_velocityBoundaries,
_atmosphereModel,
_dragModel,
_stabilityFactor,
_twistDirection,
_transonicStabilityCoef,
_bulletVelocity,
EGVAR(common,mapLatitude),
EGVAR(weather,currentTemperature),
EGVAR(common,mapAltitude),
EGVAR(weather,currentHumidity),
EGVAR(weather,currentOvercast),
CBA_missionTime toFixed 6
]
]) params ["_id", "_code"];
if (_code == 0) then {
GVAR(allBullets) set [_id, [_projectile, _caliber, _bulletTraceVisible]];
};

View File

@ -21,7 +21,14 @@ if (!GVAR(enabled)) exitWith {};
private _initStartTime = diag_tickTime;
private _mapSize = worldSize;
if (("ace_advanced_ballistics" callExtension format["init:%1:%2", worldName, _mapSize]) == "Terrain already initialized") exitWith {
(
"ace" callExtension ["ballistics:map:init", [worldName, _mapSize]]
) params ["_data", "_code"];
if (_code != 0) exitWith {
ERROR("Error initializing map")
};
if (_data == "true") exitWith {
INFO_1("Terrain already initialized [world: %1]",worldName);
#ifdef DEBUG_MODE_FULL
systemChat "AdvancedBallistics: Terrain already initialized";
@ -53,8 +60,7 @@ INFO_2("Starting Terrain Extension [cells: %1] [world: %2]",_gridCells,worldName
private _gridCenter = [_x + 25, _y + 25];
private _gridHeight = round(getTerrainHeightASL _gridCenter);
private _gridNumObjects = count (_gridCenter nearObjects ["Building", 50]);
private _gridSurfaceIsWater = parseNumber (surfaceIsWater _gridCenter);
"ace_advanced_ballistics" callExtension format["set:%1:%2:%3", _gridHeight, _gridNumObjects, _gridSurfaceIsWater];
"ace" callExtension ["ballistics:map:set", [GVAR(currentGrid), _gridHeight, _gridNumObjects, surfaceIsWater _gridCenter]];
GVAR(currentGrid) = GVAR(currentGrid) + 1;
if (GVAR(currentGrid) >= _gridCells) exitWith {};
};

View File

@ -40,7 +40,7 @@ if (_transonicStabilityCoef == 0) then {
_transonicStabilityCoef = 0.5;
};
private _dragModel = getNumber(_ammoConfig >> "ACE_dragModel");
if (!(_dragModel in [1, 2, 5, 6, 7, 8])) then {
if !(_dragModel in [1, 2, 5, 6, 7, 8]) then {
_dragModel = 1;
};
private _ballisticCoefficients = getArray(_ammoConfig >> "ACE_ballisticCoefficients");

View File

@ -5,7 +5,9 @@ private _category = format ["ACE %1", localize LSTRING(DisplayName)];
[LSTRING(enabled_DisplayName), LSTRING(enabled_Description)],
_category,
false,
1
1,
{[QGVAR(enabled), _this] call EFUNC(common,cbaSettings_settingChanged)},
true // Needs mission restart
] call CBA_fnc_addSetting;
[
@ -45,5 +47,7 @@ private _category = format ["ACE %1", localize LSTRING(DisplayName)];
[LSTRING(simulationInterval_DisplayName), LSTRING(simulationInterval_Description)],
_category,
[0, 0.2, 0.05, 2],
1
1,
{[QGVAR(simulationInterval), _this] call EFUNC(common,cbaSettings_settingChanged)},
true // Needs mission restart
] call CBA_fnc_addSetting;

View File

@ -9,3 +9,5 @@ PREP(handleStaminaBar);
PREP(mainLoop);
PREP(moduleSettings);
PREP(removeDutyFactor);
PREP(renderDebugLines);
PREP(updateStaminaBar);

View File

@ -2,6 +2,10 @@
if (!hasInterface) exitWith {};
#ifdef DEBUG_MODE_FULL
call FUNC(renderDebugLines);
#endif
// recheck weapon inertia after weapon swap, change of attachments or switching unit
["weapon", {[ACE_player] call FUNC(getWeaponInertia)}, true] call CBA_fnc_addPlayerEventHandler;
["loadout", {[ACE_player] call FUNC(getWeaponInertia)}, true] call CBA_fnc_addPlayerEventHandler;
@ -10,6 +14,8 @@ if (!hasInterface) exitWith {};
["CBA_settingsInitialized", {
if (!GVAR(enabled)) exitWith {};
[QEGVAR(ui,hideHud), LINKFUNC(updateStaminaBar)] call CBA_fnc_addEventHandler;
["baseline", {
private _fatigue = ACE_player getVariable [QGVAR(aimFatigue), 0];
switch (stance ACE_player) do {
@ -33,25 +39,23 @@ if (!hasInterface) exitWith {};
GVAR(ppeBlackout) ppEffectCommit 0.4;
// - GVAR updating and initialization -----------------------------------------
["unit", LINKFUNC(handlePlayerChanged), true] call CBA_fnc_addPlayerEventHandler;
["unit", LINKFUNC(handlePlayerChanged)] call CBA_fnc_addPlayerEventHandler;
["visibleMap", {
params ["", "_visibleMap"]; // command visibleMap is updated one frame later
private _staminaBarContainer = uiNamespace getVariable [QGVAR(staminaBarContainer), controlNull];
_staminaBarContainer ctrlShow ((!_visibleMap) && {(vehicle ACE_player) == ACE_player});
(uiNamespace getVariable [QGVAR(staminaBarContainer), controlNull]) ctrlShow (!_visibleMap && isNull objectParent ACE_player);
}, true] call CBA_fnc_addPlayerEventHandler;
["vehicle", {
private _staminaBarContainer = uiNamespace getVariable [QGVAR(staminaBarContainer), controlNull];
_staminaBarContainer ctrlShow ((!visibleMap) && {(vehicle ACE_player) == ACE_player});
(uiNamespace getVariable [QGVAR(staminaBarContainer), controlNull]) ctrlShow (!visibleMap && isNull objectParent ACE_player);
}, true] call CBA_fnc_addPlayerEventHandler;
// - Duty factors -------------------------------------------------------------
if (GVAR(medicalLoaded)) then {
if (GETEGVAR(medical,enabled,false)) then {
[QEGVAR(medical,pain), { // 0->1.0, 0.5->1.05, 1->1.1
linearConversion [0, 1, (_this getVariable [QEGVAR(medical,pain), 0]), 1, 1.1, true];
linearConversion [0, 1, _this getVariable [QEGVAR(medical,pain), 0], 1, 1.1, true];
}] call FUNC(addDutyFactor);
[QEGVAR(medical,bloodVolume), { // 6->1.0, 5->1.167, 4->1.33
linearConversion [6, 0, (_this getVariable [QEGVAR(medical,bloodVolume), 6]), 1, 2, true];
linearConversion [6, 0, _this getVariable [QEGVAR(medical,bloodVolume), 6], 1, 2, true];
}] call FUNC(addDutyFactor);
};
if (["ace_dragging"] call EFUNC(common,isModLoaded)) then {
@ -62,7 +66,7 @@ if (!hasInterface) exitWith {};
// Weather has an off switch, Dragging & Medical don't.
if (missionNamespace getVariable [QEGVAR(weather,enabled), false]) then {
[QEGVAR(weather,temperature), { // 35->1, 45->2
linearConversion [35, 45, (missionNamespace getVariable [QEGVAR(weather,currentTemperature), 25]), 1, 2, true];
linearConversion [35, 45, missionNamespace getVariable [QEGVAR(weather,currentTemperature), 25], 1, 2, true];
}] call FUNC(addDutyFactor);
};

View File

@ -13,6 +13,5 @@ GVAR(dutyList) = createHashMap;
GVAR(setAnimExclusions) = [];
GVAR(inertia) = 0;
GVAR(inertiaCache) = createHashMap;
GVAR(medicalLoaded) = ["ace_medical"] call EFUNC(common,isModLoaded);
ADDON = true;

View File

@ -1,7 +1,7 @@
#include "..\script_component.hpp"
/*
* Author: BaerMitUmlaut
* Calculates the duty of the current animation.
* Calculates the duty ('postureWeight') of the current animation.
*
* Arguments:
* 0: Unit <OBJECT>

View File

@ -1,54 +1,74 @@
#include "..\script_component.hpp"
/*
* Author: BaerMitUmlaut
* Calculates the current metabolic costs for a unit.
* Author: BaerMitUmlaut, ulteq
* Calculates the current metabolic costs.
* Calculation is done according to the Pandolf/Wojtowicz formulas.
*
* Arguments:
* 0: Unit <OBJECT>
* 1: Speed <NUMBER>
* 0: Duty of animation
* 1: Mass of unit <NUMBER>
* 2: Terrain gradient <NUMBER>
* 3: Terrain factor <NUMBER>
* 4: Speed <NUMBER>
*
* Return Value:
* Metabolic cost <NUMBER>
*
* Example:
* [player, 3.3] call ace_advanced_fatigue_fnc_getMetabolicCosts
* [1, 840, 20, 1, 4] call ace_advanced_fatigue_fnc_getMetabolicCosts
*
* Public: No
*/
params ["_unit", "_velocity"];
private _gearMass = ((_unit getVariable [QEGVAR(movement,totalLoad), loadAbs _unit]) / 22.046) * GVAR(loadFactor);
private _terrainAngle = asin (1 - ((surfaceNormal getPosASL _unit) select 2));
private _terrainGradient = (_terrainAngle / 45 min 1) * 5 * GVAR(terrainGradientFactor);
private _duty = GVAR(animDuty);
{
if (_x isEqualType 0) then {
_duty = _duty * _x;
} else {
_duty = _duty * (_unit call _x);
};
} forEach (values GVAR(dutyList));
if (GVAR(isSwimming)) then {
_terrainGradient = 0;
};
params ["_duty", "_gearMass", "_terrainGradient", "_terrainFactor", "_speed"];
// Metabolic cost for walking and running is different
if (_velocity > 2) then {
if (_speed > 2) then {
// Running
#ifdef DEBUG_MODE_FULL
private _baseline = 2.1 * SIM_BODYMASS + 4 * (SIM_BODYMASS + _gearMass) * ((_gearMass / SIM_BODYMASS) ^ 2) + (SIM_BODYMASS + _gearMass) * 0.9 * (_speed ^ 2);
private _graded = 2.1 * SIM_BODYMASS + 4 * (SIM_BODYMASS + _gearMass) * ((_gearMass / SIM_BODYMASS) ^ 2) + _terrainFactor * (SIM_BODYMASS + _gearMass) * (0.9 * (_speed ^ 2) + 0.66 * _speed * _terrainGradient);
private _terrainImpact = abs ((_graded / _baseline) - 1);
hintSilent format ["FwdAngle: %1 | SideAngle: %2 \n TerrainFactor: %3 | TerrainGradient: %4 \n TerrainImpact: %5 \n Speed: %6 | CarriedLoad: %7 \n Duty: %8 | Work: %9",
_fwdAngle toFixed 1,
_sideAngle toFixed 1,
_terrainFactor toFixed 2,
_terrainGradient toFixed 1,
_terrainImpact toFixed 2,
_speed toFixed 2,
_gearMass toFixed 1,
_duty toFixed 2,
round (_graded * BIOMECH_EFFICIENCY * _duty)
];
#endif
(
2.10 * SIM_BODYMASS
2.1 * SIM_BODYMASS
+ 4 * (SIM_BODYMASS + _gearMass) * ((_gearMass / SIM_BODYMASS) ^ 2)
+ (SIM_BODYMASS + _gearMass) * (0.9 * (_velocity ^ 2) + 0.66 * _velocity * _terrainGradient)
) * 0.23 * _duty
+ _terrainFactor * (SIM_BODYMASS + _gearMass) * (0.9 * (_speed ^ 2) + 0.66 * _speed * _terrainGradient)
) * BIOMECH_EFFICIENCY * _duty
} else {
// Walking
#ifdef DEBUG_MODE_FULL
private _baseline = 1.05 * SIM_BODYMASS + 2 * (SIM_BODYMASS + _gearMass) * ((_gearMass / SIM_BODYMASS) ^ 2) + (SIM_BODYMASS + _gearMass) * 1.15 * (_speed ^ 2);
private _graded = 1.05 * SIM_BODYMASS + 2 * (SIM_BODYMASS + _gearMass) * ((_gearMass / SIM_BODYMASS) ^ 2) + _terrainFactor * (SIM_BODYMASS + _gearMass) * (1.15 * (_speed ^ 2) + 0.66 * _speed * _terrainGradient);
private _terrainImpact = abs ((_graded / _baseline) - 1);
hintSilent format ["FwdAngle: %1 | SideAngle: %2 \n TerrainFactor: %3 | TerrainGradient: %4 \n TerrainImpact: %5 \n Speed: %6 | CarriedLoad: %7 \n Duty: %8 | Work: %9",
_fwdAngle toFixed 1,
_sideAngle toFixed 1,
_terrainFactor toFixed 2,
_terrainGradient toFixed 1,
_terrainImpact toFixed 2,
_speed toFixed 2,
_gearMass toFixed 1,
_duty toFixed 2,
round (_graded * BIOMECH_EFFICIENCY * _duty)
];
#endif
(
1.05 * SIM_BODYMASS
+ 2 * (SIM_BODYMASS + _gearMass) * ((_gearMass / SIM_BODYMASS) ^ 2)
+ (SIM_BODYMASS + _gearMass) * (1.15 * (_velocity ^ 2) + 0.66 * _velocity * _terrainGradient)
) * 0.23 * _duty
+ _terrainFactor * (SIM_BODYMASS + _gearMass) * (1.15 * (_speed ^ 2) + 0.66 * _speed * _terrainGradient)
) * BIOMECH_EFFICIENCY * _duty
};

View File

@ -1,44 +1,44 @@
#include "..\script_component.hpp"
/*
* Author: BaerMitUmlaut
* Author: BaerMitUmlaut, ulteq
* Handles any audible, visual and physical effects of fatigue.
*
* Arguments:
* 0: Unit <OBJECT>
* 1: Fatigue <NUMBER>
* 2: Speed <NUMBER>
* 3: Overexhausted <BOOL>
* 2: Overexhausted <BOOL>
* 3: Forward Angle <NUMBER>
* 4: Side Angle <NUMBER>
*
* Return Value:
* None
*
* Example:
* [_player, 0.5, 3.3, true] call ace_advanced_fatigue_fnc_handleEffects
* [_player, 0.5, 3.3, true, 0, 0] call ace_advanced_fatigue_fnc_handleEffects
*
* Public: No
*/
params ["_unit", "_fatigue", "_speed", "_overexhausted"];
#ifdef DEBUG_MODE_FULL
systemChat str _fatigue;
systemChat str vectorMagnitude velocity _unit;
#endif
params ["_unit", "_fatigue", "_overexhausted", "_fwdAngle", "_sideAngle"];
// - Audible effects ----------------------------------------------------------
GVAR(lastBreath) = GVAR(lastBreath) + 1;
if (_fatigue > 0.4 && {GVAR(lastBreath) > (_fatigue * -10 + 9)} && {!underwater _unit}) then {
if (!isGameFocused) exitWith {};
switch (true) do {
case (_fatigue < 0.6): {
playSound (QGVAR(breathLow) + str(floor random 6));
playSound (QGVAR(breathLow) + str (floor random 6));
};
case (_fatigue < 0.85): {
playSound (QGVAR(breathMid) + str(floor random 6));
playSound (QGVAR(breathMid) + str (floor random 6));
};
default {
playSound (QGVAR(breathMax) + str(floor random 6));
playSound (QGVAR(breathMax) + str (floor random 6));
};
};
GVAR(lastBreath) = 0;
};
@ -62,31 +62,35 @@ if (GVAR(isSwimming)) exitWith {
if (GVAR(setAnimExclusions) isEqualTo []) then {
_unit setAnimSpeedCoef linearConversion [0.7, 0.9, _fatigue, 1, 0.5, true];
};
if ((isSprintAllowed _unit) && {_fatigue > 0.7}) then {
if (isSprintAllowed _unit && _fatigue > 0.7) then { // small checks like these are faster without lazy eval
[_unit, "blockSprint", QUOTE(ADDON), true] call EFUNC(common,statusEffect_set);
} else {
if ((!isSprintAllowed _unit) && {_fatigue < 0.7}) then {
if (!isSprintAllowed _unit && _fatigue < 0.7) then {
[_unit, "blockSprint", QUOTE(ADDON), false] call EFUNC(common,statusEffect_set);
};
};
};
if ((getAnimSpeedCoef _unit) != 1) then {
if (GVAR(setAnimExclusions) isEqualTo []) then {
TRACE_1("reset",getAnimSpeedCoef _unit);
_unit setAnimSpeedCoef 1;
};
// If other components are setting setAnimSpeedCoef, do not change animSpeedCoef
if (getAnimSpeedCoef _unit != 1 && {GVAR(setAnimExclusions) isEqualTo []}) then {
TRACE_1("reset",getAnimSpeedCoef _unit);
_unit setAnimSpeedCoef 1;
};
if (_overexhausted) then {
if (!isForcedWalk _unit && _fatigue >= 1) then { // small checks like these are faster without lazy eval
[_unit, "forceWalk", QUOTE(ADDON), true] call EFUNC(common,statusEffect_set);
[_unit, "blockSprint", QUOTE(ADDON), true] call EFUNC(common,statusEffect_set);
} else {
if (isForcedWalk _unit && {_fatigue < 0.7}) then {
if (isForcedWalk _unit && _fatigue < 0.7) then {
[_unit, "forceWalk", QUOTE(ADDON), false] call EFUNC(common,statusEffect_set);
[_unit, "blockSprint", QUOTE(ADDON), false] call EFUNC(common,statusEffect_set);
} else {
if ((isSprintAllowed _unit) && {_fatigue > 0.7}) then {
// Forward angle is the slope of the terrain, side angle simulates the unevenness/roughness of the terrain
if (isSprintAllowed _unit && {_fatigue > 0.7 || abs _fwdAngle > 20 || abs _sideAngle > 20}) then {
[_unit, "blockSprint", QUOTE(ADDON), true] call EFUNC(common,statusEffect_set);
} else {
if ((!isSprintAllowed _unit) && {_fatigue < 0.6}) then {
if (!isSprintAllowed _unit && _fatigue < 0.6 && abs _fwdAngle < 20 && abs _sideAngle < 20) then {
[_unit, "blockSprint", QUOTE(ADDON), false] call EFUNC(common,statusEffect_set);
};
};

View File

@ -1,7 +1,7 @@
#include "..\script_component.hpp"
/*
* Author: BaerMitUmlaut
* Handles switching units (once on init and afterwards via Zeus).
* Author: BaerMitUmlaut, ulteq
* Handles switching units (once on init and afterwards via Zeus). Also handles CBA setting change for performance factor.
*
* Arguments:
* 0: New Unit <OBJECT>
@ -15,20 +15,24 @@
*
* Public: No
*/
params ["_newUnit", "_oldUnit"];
TRACE_2("unit changed",_newUnit,_oldUnit);
if !(isNull _oldUnit) then {
if (!isNull _oldUnit) then {
TRACE_1("remove old",_oldUnit getVariable QGVAR(animHandler));
_oldUnit enableStamina true;
_oldUnit removeEventHandler ["AnimChanged", _oldUnit getVariable [QGVAR(animHandler), -1]];
_oldUnit setVariable [QGVAR(animHandler), nil];
TRACE_1("remove old",_oldUnit getVariable QGVAR(animHandler));
_oldUnit setVariable [QGVAR(ae1Reserve), GVAR(ae1Reserve)];
_oldUnit setVariable [QGVAR(ae2Reserve), GVAR(ae2Reserve)];
_oldUnit setVariable [QGVAR(anReserve), GVAR(anReserve)];
_oldUnit setVariable [QGVAR(anFatigue), GVAR(anFatigue)];
_oldUnit setVariable [QGVAR(muscleDamage), GVAR(muscleDamage)];
_oldUnit setVariable [QGVAR(respiratoryRate), GVAR(respiratoryRate)];
};
_newUnit enableStamina false;
@ -38,6 +42,7 @@ if (_newUnit getVariable [QGVAR(animHandler), -1] == -1) then {
private _animHandler = _newUnit addEventHandler ["AnimChanged", {
GVAR(animDuty) = _this call FUNC(getAnimDuty);
}];
TRACE_1("add new",_animHandler);
_newUnit setVariable [QGVAR(animHandler), _animHandler];
};
@ -47,18 +52,27 @@ GVAR(ae2Reserve) = _newUnit getVariable [QGVAR(ae2Reserve), AE2_MAXRESERVE]
GVAR(anReserve) = _newUnit getVariable [QGVAR(anReserve), AN_MAXRESERVE];
GVAR(anFatigue) = _newUnit getVariable [QGVAR(anFatigue), 0];
GVAR(muscleDamage) = _newUnit getVariable [QGVAR(muscleDamage), 0];
GVAR(respiratoryRate) = _newUnit getVariable [QGVAR(respiratoryRate), 0];
// Clean variables for respawning units
{
_newUnit setVariable [_x, nil];
} forEach [QGVAR(ae1Reserve), QGVAR(ae2Reserve), QGVAR(anReserve), QGVAR(anFatigue), QGVAR(muscleDamage)];
} forEach [QGVAR(ae1Reserve), QGVAR(ae2Reserve), QGVAR(anReserve), QGVAR(anFatigue), QGVAR(muscleDamage), QGVAR(respiratoryRate)];
GVAR(VO2Max) = 35 + 20 * (_newUnit getVariable [QGVAR(performanceFactor), GVAR(performanceFactor)]);
GVAR(VO2MaxPower) = GVAR(VO2Max) * SIM_BODYMASS * 0.23 * JOULES_PER_ML_O2 / 60;
GVAR(VO2MaxPower) = GVAR(VO2Max) * SIM_BODYMASS * BIOMECH_EFFICIENCY * JOULES_PER_ML_O2 / 60;
GVAR(peakPower) = VO2MAX_STRENGTH * GVAR(VO2MaxPower);
GVAR(ae1PathwayPower) = GVAR(peakPower) / (13.3 + 16.7 + 113.3) * 13.3 * ANTPERCENT ^ 1.28 * 1.362;
GVAR(ae2PathwayPower) = GVAR(peakPower) / (13.3 + 16.7 + 113.3) * 16.7 * ANTPERCENT ^ 1.28 * 1.362;
GVAR(ae1PathwayPower) = GVAR(peakPower) / (AE1_ATP_RELEASE_RATE + AE2_ATP_RELEASE_RATE + AN_ATP_RELEASE_RATE) * AE1_ATP_RELEASE_RATE * ANTPERCENT ^ 1.28 * 1.362;
GVAR(ae2PathwayPower) = GVAR(peakPower) / (AE1_ATP_RELEASE_RATE + AE2_ATP_RELEASE_RATE + AN_ATP_RELEASE_RATE) * AE2_ATP_RELEASE_RATE * ANTPERCENT ^ 1.28 * 1.362;
GVAR(aePathwayPower) = GVAR(ae1PathwayPower) + GVAR(ae2PathwayPower);
GVAR(anPathwayPower) = GVAR(peakPower) - GVAR(aePathwayPower);
GVAR(aeWattsPerATP) = GVAR(ae1PathwayPower) / AE1_ATP_RELEASE_RATE;
GVAR(anWattsPerATP) = GVAR(anPathwayPower) / AN_ATP_RELEASE_RATE;
GVAR(respiratoryBufferDivisor) = (RESPIRATORY_BUFFER - 1) / RESPIRATORY_BUFFER;
GVAR(maxPowerFatigueRatio) = 0.057 / GVAR(peakPower);
GVAR(ppeBlackoutLast) = 100;
GVAR(lastBreath) = 0;

View File

@ -1,6 +1,6 @@
#include "..\script_component.hpp"
/*
* Author: BaerMitUmlaut
* Author: BaerMitUmlaut, ulteq
* Main looping function that updates fatigue values.
*
* Arguments:
@ -17,73 +17,131 @@
// Dead people don't breathe, will also handle null (map intros)
if (!alive ACE_player) exitWith {
[FUNC(mainLoop), [], 1] call CBA_fnc_waitAndExecute;
[LINKFUNC(mainLoop), [], 1] call CBA_fnc_waitAndExecute;
private _staminaBarContainer = uiNamespace getVariable [QGVAR(staminaBarContainer), controlNull];
_staminaBarContainer ctrlSetFade 1;
_staminaBarContainer ctrlCommit 1;
};
private _oxygen = 0.9; // Default AF oxygen saturation
if (GVAR(medicalLoaded) && {EGVAR(medical_vitals,simulateSpo2)}) then {
_oxygen = (ACE_player getVariable [QEGVAR(medical,spo2), 97]) / 100;
};
private _velocity = velocity ACE_player;
private _normal = surfaceNormal (getPosWorld ACE_player);
private _movementVector = vectorNormalized _velocity;
private _sideVector = vectorNormalized (_movementVector vectorCrossProduct _normal);
private _fwdAngle = asin (_movementVector select 2);
private _sideAngle = asin (_sideVector select 2);
private _currentWork = REE;
private _currentSpeed = (vectorMagnitude (velocity ACE_player)) min 6;
private _currentSpeed = (vectorMagnitude _velocity) min 6;
// fix #4481. Diving to the ground is recorded as PRONE stance with running speed velocity. Cap maximum speed to fix.
if (GVAR(isProne)) then {
_currentSpeed = _currentSpeed min 1.5;
};
if ((vehicle ACE_player == ACE_player) && {_currentSpeed > 0.1} && {isTouchingGround ACE_player || {underwater ACE_player}}) then {
_currentWork = [ACE_player, _currentSpeed] call FUNC(getMetabolicCosts);
// Get the current duty
private _duty = GVAR(animDuty);
{
if (_x isEqualType 0) then {
_duty = _duty * _x;
} else {
_duty = _duty * (ACE_player call _x);
};
} forEach (values GVAR(dutyList));
private _terrainGradient = abs _fwdAngle;
private _terrainFactor = 1;
private _gearMass = 0 max (((ACE_player getVariable [QEGVAR(movement,totalLoad), loadAbs ACE_player]) / 22.046 - UNDERWEAR_WEIGHT) * GVAR(loadFactor));
if (isNull objectParent ACE_player && {_currentSpeed > 0.1} && {isTouchingGround ACE_player || {underwater ACE_player}}) then {
if (!GVAR(isSwimming)) then {
// If the unit is going downhill, it's much less demanding
if (_fwdAngle < 0) then {
_terrainGradient = 0.15 * _terrainGradient;
};
// Used to simulate the unevenness/roughness of the terrain
if ((getPosATL ACE_player) select 2 < 0.01) then {
private _sideGradient = abs (_sideAngle / 45) min 1;
_terrainFactor = 1 + _sideGradient ^ 4;
};
};
_currentWork = [_duty, _gearMass, _terrainGradient * GVAR(terrainGradientFactor), _terrainFactor, _currentSpeed] call FUNC(getMetabolicCosts);
_currentWork = _currentWork max REE;
};
// Oxygen calculation
private _oxygen = if (GETEGVAR(medical,enabled,false) && {EGVAR(medical_vitals,simulateSpo2)}) then { // Defer to medical
(ACE_player getVariable [QEGVAR(medical,spo2), 97]) / 100
} else {
1 - 0.131 * GVAR(respiratoryRate) ^ 2 // Default AF oxygen saturation
};
// Calculate muscle damage increase
// Note: Muscle damage recovery is ignored as it takes multiple days
GVAR(muscleDamage) = (GVAR(muscleDamage) + (_currentWork / GVAR(peakPower)) ^ 3.2 * 0.00004) min 1;
private _muscleIntegritySqrt = sqrt (1 - GVAR(muscleDamage));
GVAR(muscleDamage) = GVAR(muscleDamage) + (_currentWork / GVAR(peakPower)) ^ 3.2 * MUSCLE_TEAR_RATE;
// Calculate muscle damage recovery
GVAR(muscleDamage) = 0 max (GVAR(muscleDamage) - MUSCLE_RECOVERY * GVAR(recoveryFactor)) min 1;
private _muscleIntegrity = 1 - GVAR(muscleDamage);
private _muscleFactor = sqrt _muscleIntegrity;
// Calculate available power
private _ae1PathwayPowerFatigued = GVAR(ae1PathwayPower) * sqrt (GVAR(ae1Reserve) / AE1_MAXRESERVE) * _oxygen * _muscleIntegritySqrt;
private _ae2PathwayPowerFatigued = GVAR(ae2PathwayPower) * sqrt (GVAR(ae2Reserve) / AE2_MAXRESERVE) * _oxygen * _muscleIntegritySqrt;
private _ae1PathwayPowerFatigued = GVAR(ae1PathwayPower) * sqrt (GVAR(ae1Reserve) / AE1_MAXRESERVE) * _oxygen * _muscleFactor;
private _ae2PathwayPowerFatigued = GVAR(ae2PathwayPower) * sqrt (GVAR(ae2Reserve) / AE2_MAXRESERVE) * _oxygen * _muscleFactor;
private _aePathwayPowerFatigued = _ae1PathwayPowerFatigued + _ae2PathwayPowerFatigued;
private _anPathwayPowerFatigued = GVAR(anPathwayPower) * sqrt (GVAR(anReserve) / AN_MAXRESERVE) * _oxygen * _muscleIntegrity;
// Calculate how much power is consumed from each reserve
private _ae1Power = _currentWork min _ae1PathwayPowerFatigued;
private _ae2Power = ((_currentWork - _ae1Power) max 0) min _ae2PathwayPowerFatigued;
private _anPower = (_currentWork - _ae1Power - _ae2Power) max 0;
private _ae2Power = (_currentWork - _ae1Power) min _ae2PathwayPowerFatigued;
private _anPower = 0 max (_currentWork - _ae1Power - _ae2Power);
// Remove ATP from reserves for current work
GVAR(ae1Reserve) = GVAR(ae1Reserve) - _ae1Power / WATTSPERATP;
GVAR(ae2Reserve) = GVAR(ae2Reserve) - _ae2Power / WATTSPERATP;
GVAR(anReserve) = GVAR(anReserve) - _anPower / WATTSPERATP;
// Increase anearobic fatigue
GVAR(anFatigue) = GVAR(anFatigue) + _anPower * (0.057 / GVAR(peakPower)) * 1.1;
GVAR(ae1Reserve) = 0 max (GVAR(ae1Reserve) - _ae1Power / GVAR(aeWattsPerATP));
GVAR(ae2Reserve) = 0 max (GVAR(ae2Reserve) - _ae2Power / GVAR(aeWattsPerATP));
GVAR(anReserve) = 0 max (GVAR(anReserve) - _anPower / GVAR(anWattsPerATP));
// Acidosis accumulation
GVAR(anFatigue) = GVAR(anFatigue) + _anPower * GVAR(maxPowerFatigueRatio) * 1.1;
// Aerobic ATP reserve recovery
GVAR(ae1Reserve) = ((GVAR(ae1Reserve) + _oxygen * 6.60 * (GVAR(ae1PathwayPower) - _ae1Power) / GVAR(ae1PathwayPower) * GVAR(recoveryFactor)) min AE1_MAXRESERVE) max 0;
GVAR(ae2Reserve) = ((GVAR(ae2Reserve) + _oxygen * 5.83 * (GVAR(ae2PathwayPower) - _ae2Power) / GVAR(ae2PathwayPower) * GVAR(recoveryFactor)) min AE2_MAXRESERVE) max 0;
GVAR(ae1Reserve) = (GVAR(ae1Reserve) + _oxygen * GVAR(recoveryFactor) * AE1_ATP_RECOVERY * (GVAR(ae1PathwayPower) - _ae1Power) / GVAR(ae1PathwayPower)) min AE1_MAXRESERVE;
GVAR(ae2Reserve) = (GVAR(ae2Reserve) + _oxygen * GVAR(recoveryFactor) * AE2_ATP_RECOVERY * (GVAR(ae2PathwayPower) - _ae2Power) / GVAR(ae2PathwayPower)) min AE2_MAXRESERVE;
// Anaerobic ATP reserver and fatigue recovery
GVAR(anReserve) = ((GVAR(anReserve)
+ (_ae1PathwayPowerFatigued + _ae2PathwayPowerFatigued - _ae1Power - _ae2Power) / GVAR(VO2MaxPower) * 56.7 * GVAR(anFatigue) ^ 2 * GVAR(recoveryFactor)
) min AN_MAXRESERVE) max 0;
private _aeSurplus = _ae1PathwayPowerFatigued + _ae2PathwayPowerFatigued - _ae1Power - _ae2Power;
GVAR(anFatigue) = ((GVAR(anFatigue)
- (_ae1PathwayPowerFatigued + _ae2PathwayPowerFatigued - _ae1Power - _ae2Power) * (0.057 / GVAR(peakPower)) * GVAR(anFatigue) ^ 2 * GVAR(recoveryFactor)
) min 1) max 0;
// Anaerobic ATP reserve recovery
GVAR(anReserve) = 0 max (GVAR(anReserve) + _aeSurplus / GVAR(VO2MaxPower) * AN_ATP_RECOVERY * GVAR(recoveryFactor) * (GVAR(anFatigue) max linearConversion [AN_MAXRESERVE, 0, GVAR(anReserve), 0, 0.75, true]) ^ 2) min AN_MAXRESERVE; // max linearConversion ensures that if GVAR(anFatigue) is very low, it will still regenerate reserves
// Acidosis recovery
GVAR(anFatigue) = 0 max (GVAR(anFatigue) - _aeSurplus * GVAR(maxPowerFatigueRatio) * GVAR(recoveryFactor) * GVAR(anFatigue) ^ 2) min 1;
// Respiratory rate decrease
GVAR(respiratoryRate) = GVAR(respiratoryRate) * GVAR(respiratoryBufferDivisor);
// Respiratory rate increase
private _aePowerRatio = (GVAR(aePathwayPower) / _aePathwayPowerFatigued) min 2;
private _respiratorySampleDivisor = 1 / (RESPIRATORY_BUFFER * 4.72 * GVAR(VO2Max));
GVAR(respiratoryRate) = (GVAR(respiratoryRate) + _currentWork * _respiratorySampleDivisor * _aePowerRatio) min 1;
// Calculate a pseudo-perceived fatigue, which is used for effects
GVAR(aeReservePercentage) = (GVAR(ae1Reserve) / AE1_MAXRESERVE + GVAR(ae2Reserve) / AE2_MAXRESERVE) / 2;
GVAR(anReservePercentage) = GVAR(anReserve) / AN_MAXRESERVE;
private _perceivedFatigue = 1 - (GVAR(anReservePercentage) min GVAR(aeReservePercentage));
[ACE_player, _perceivedFatigue, _currentSpeed, GVAR(anReserve) == 0] call FUNC(handleEffects);
#ifdef DEBUG_MODE_FULL
systemChat format ["---- muscleDamage: %1 ----", GVAR(muscleDamage) toFixed 8];
systemChat format ["---- ae2: %1 - an: %2 ----", (GVAR(ae2Reserve) / AE2_MAXRESERVE) toFixed 2, (GVAR(anReserve) / AN_MAXRESERVE) toFixed 2];
systemChat format ["---- anFatigue: %1 - perceivedFatigue: %2 ----", GVAR(anFatigue) toFixed 2, _perceivedFatigue toFixed 2];
systemChat format ["---- velocity %1 - respiratoryRate: %2 ----", (vectorMagnitude _velocity) toFixed 2, GVAR(respiratoryRate) toFixed 2];
// systemChat format ["---- aePower: %1 ----", _aePathwayPowerFatigued toFixed 1];
#endif
if (GVAR(enableStaminaBar)) then {
[ACE_player, _perceivedFatigue, GVAR(anReserve) == 0, _fwdAngle, _sideAngle] call FUNC(handleEffects);
if (GVAR(enableStaminaBarRealized)) then {
[GVAR(anReserve) / AN_MAXRESERVE] call FUNC(handleStaminaBar);
};
[FUNC(mainLoop), [], 1] call CBA_fnc_waitAndExecute;
[LINKFUNC(mainLoop), [], 1] call CBA_fnc_waitAndExecute;

View File

@ -0,0 +1,40 @@
#include "..\script_component.hpp"
/*
* Author: ulteq
* Draw lines for debugging.
*
* Arguments:
* None
*
* Return Value:
* None
*
* Example:
* call ace_advanced_fatigue_fnc_renderDebugLines
*
* Public: No
*/
addMissionEventHandler ["Draw3D", {
private _normal = surfaceNormal (getPosWorld ACE_player);
private _beg = (getPosWorld ACE_player) vectorAdd (_normal vectorMultiply 0.5);
private _end = _beg vectorAdd (_normal vectorMultiply 2);
drawLine3D [ASLToATL _beg, ASLToATL _end, [0, 1, 0, 1]];
private _side = vectorNormalized (_normal vectorCrossProduct [0, 0, 1]);
private _end = _beg vectorAdd (_side vectorMultiply 2);
drawLine3D [ASLToATL _beg, ASLToATL _end, [0, 0, 1, 1]];
private _up = vectorNormalized (_normal vectorCrossProduct _side);
private _end = _beg vectorAdd (_up vectorMultiply 2);
drawLine3D [ASLToATL _beg, ASLToATL _end, [1, 0, 0, 1]];
private _movementVector = vectorNormalized (velocity ACE_player);
private _end = _beg vectorAdd (_movementVector vectorMultiply 2);
drawLine3D [ASLToATL _beg, ASLToATL _end, [1, 1, 0, 1]];
private _sideVector = vectorNormalized (_movementVector vectorCrossProduct _normal);
_sideVector set [2, 0];
private _end = _beg vectorAdd (_sideVector vectorMultiply 2);
drawLine3D [ASLToATL _beg, ASLToATL _end, [0, 1, 1, 1]];
}];

View File

@ -0,0 +1,25 @@
#include "..\script_component.hpp"
/*
* Author: PabstMirror
* Updates the stamina bar state
*
* Arguments:
* None
*
* Return Value:
* None
*
* Example:
* [] call ace_advanced_fatigue_fnc_updateStaminaBar
*
* Public: No
*/
GVAR(enableStaminaBarRealized) = GVAR(enabled) && GVAR(enableStaminaBar) && {!(missionNamespace getVariable [QEGVAR(ui,hideHud), false])};
TRACE_1("updateStaminaBar",GVAR(enableStaminaBarRealized));
private _staminaBarContainer = uiNamespace getVariable [QGVAR(staminaBarContainer), controlNull];
if (isNull _staminaBarContainer) exitWith {};
_staminaBarContainer ctrlSetFade ([1, 0] select GVAR(enableStaminaBarRealized));
_staminaBarContainer ctrlCommit 0;

View File

@ -4,12 +4,9 @@
[LSTRING(Enabled), LSTRING(Enabled_Description)],
LSTRING(DisplayName),
true,
true, {
if (!_this) then {
private _staminaBarContainer = uiNamespace getVariable [QGVAR(staminaBarContainer), controlNull];
_staminaBarContainer ctrlSetFade 1;
_staminaBarContainer ctrlCommit 0;
};
1,
{
call FUNC(updateStaminaBar);
[QGVAR(enabled), _this] call EFUNC(common,cbaSettings_settingChanged)
},
true // Needs mission restart
@ -21,13 +18,8 @@
[LSTRING(EnableStaminaBar), LSTRING(EnableStaminaBar_Description)],
LSTRING(DisplayName),
true,
true, {
if (!_this) then {
private _staminaBarContainer = uiNamespace getVariable [QGVAR(staminaBarContainer), controlNull];
_staminaBarContainer ctrlSetFade 1;
_staminaBarContainer ctrlCommit 0;
};
}
1,
{call FUNC(updateStaminaBar)}
] call CBA_fnc_addSetting;
[
@ -36,7 +28,8 @@
[LSTRING(FadeStaminaBar), LSTRING(FadeStaminaBar_Description)],
LSTRING(DisplayName),
true,
false, {
0,
{
if (!_this && GVAR(enabled) && GVAR(enableStaminaBar)) then {
private _staminaBarContainer = uiNamespace getVariable [QGVAR(staminaBarContainer), controlNull];
_staminaBarContainer ctrlSetFade 0;
@ -50,8 +43,14 @@
"SLIDER",
[LSTRING(PerformanceFactor), LSTRING(PerformanceFactor_Description)],
LSTRING(DisplayName),
[0, 5, 1, 1],
true
[0, 10, 1, 2],
1,
{
// Recalculate values if the setting is changed mid-mission
if (GVAR(enabled) && hasInterface && !isNull ACE_player) then {
[ACE_player, ACE_player] call FUNC(handlePlayerChanged);
};
}
] call CBA_fnc_addSetting;
[
@ -59,8 +58,8 @@
"SLIDER",
[LSTRING(RecoveryFactor), LSTRING(RecoveryFactor_Description)],
LSTRING(DisplayName),
[0, 5, 1, 1],
true
[0, 10, 1, 2],
1
] call CBA_fnc_addSetting;
[
@ -68,8 +67,8 @@
"SLIDER",
[LSTRING(LoadFactor), LSTRING(LoadFactor_Description)],
LSTRING(DisplayName),
[0, 5, 1, 1],
true
[0, 5, 1, 2],
1
] call CBA_fnc_addSetting;
[
@ -77,6 +76,6 @@
"SLIDER",
[LSTRING(TerrainGradientFactor), LSTRING(TerrainGradientFactor_Description)],
LSTRING(DisplayName),
[0, 5, 1, 1],
true
[0, 5, 1, 2],
1
] call CBA_fnc_addSetting;

View File

@ -16,14 +16,28 @@
#include "\z\ace\addons\main\script_macros.hpp"
#define UNDERWEAR_WEIGHT 3.5
#define ANTPERCENT 0.8
#define SIM_BODYMASS 70
#define JOULES_PER_ML_O2 20.9
#define VO2MAX_STRENGTH 4.1
#define REE 18.83 //((0.5617 * SIM_BODYMASS + 42.57) * 0.23)
#define OXYGEN 0.9
#define WATTSPERATP 7
#define BIOMECH_EFFICIENCY 0.23
#define REE 18.83 // ((0.5617 * SIM_BODYMASS + 42.57) * BIOMECH_EFFICIENCY)
#define AE1_MAXRESERVE 4000000
#define AE2_MAXRESERVE 84000
#define AN_MAXRESERVE 2300
#define RESPIRATORY_BUFFER 60
#define MUSCLE_TEAR_RATE 0.00004
#define MUSCLE_RECOVERY 0.00000386
#define AE1_ATP_RELEASE_RATE 13.3 // mmol
#define AE2_ATP_RELEASE_RATE 16.7 // mmol
#define AN_ATP_RELEASE_RATE 113.3 // mmol
#define AE1_ATP_RECOVERY 6.60 // mmol
#define AE2_ATP_RECOVERY 5.83 // mmol
#define AN_ATP_RECOVERY 56.70 // mmol
#define AE1_MAXRESERVE 4000000 // mmol
#define AE2_MAXRESERVE 84000 // mmol
#define AN_MAXRESERVE 2300 // mmol

View File

@ -10,20 +10,10 @@ if (!hasInterface) exitWith {};
// Temporary Wind Info indication
GVAR(tempWindInfo) = false;
// Ammo/Magazines look-up hash for correctness of initSpeed
GVAR(ammoMagLookup) = call CBA_fnc_createNamespace;
{
{
private _ammo = getText (configFile >> "CfgMagazines" >> _x >> "ammo");
if (_ammo != "") then { GVAR(ammoMagLookup) setVariable [_ammo, _x]; };
} forEach (getArray (configFile >> "CfgWeapons" >> "Throw" >> _x >> "magazines"));
} forEach getArray (configFile >> "CfgWeapons" >> "Throw" >> "muzzles");
// Add keybinds
["ACE3 Weapons", QGVAR(prepare), localize LSTRING(Prepare), {
// Condition
if (!([ACE_player] call FUNC(canPrepare))) exitWith {false};
if !([ACE_player] call FUNC(canPrepare)) exitWith {false};
if (EGVAR(common,isReloading)) exitWith {true};
// Statement

View File

@ -1,3 +1,21 @@
#include "script_component.hpp"
#include "XEH_PREP.hpp"
// Ammo/Magazines look-up hash for correctness of initSpeed
private _cfgMagazines = configFile >> "CfgMagazines";
private _cfgAmmo = configFile >> "CfgAmmo";
private _cfgThrow = configFile >> "CfgWeapons" >> "Throw";
private _ammoMagLookup = createHashMap;
{
{
private _ammo = getText (_cfgMagazines >> _x >> "ammo");
if (_ammo != "") then {
_ammoMagLookup set [configName (_cfgAmmo >> _ammo), _x];
};
} forEach (getArray (_cfgThrow >> _x >> "magazines"));
} forEach (getArray (_cfgThrow >> "muzzles"));
uiNamespace setVariable [QGVAR(ammoMagLookup), compileFinal _ammoMagLookup];

View File

@ -43,13 +43,10 @@ if ((!_primed) && {!((_throwableMag in (uniformItems ACE_player)) || {_throwable
// Get correct throw power for primed grenade
if (_primed) then {
private _ammoType = typeOf _activeThrowable;
_throwableMag = GVAR(ammoMagLookup) getVariable _ammoType;
if (isNil "_throwableMag") then {
// What we're trying to throw must not be a normal throwable because it is not in our lookup hash (e.g. 40mm smoke)
// Just use HandGrenade as it has an average initSpeed value
_throwableMag = "HandGrenade";
};
// If ammo type is not found:
// What we're trying to throw must not be a normal throwable because it is not in our lookup hash (e.g. 40mm smoke)
// Just use HandGrenade as it has an average initSpeed value
_throwableMag = (uiNamespace getVariable QGVAR(ammoMagLookup)) getOrDefault [typeOf _activeThrowable, "HandGrenade"];
};
// Some throwables have different classname for magazine and ammo

View File

@ -21,7 +21,7 @@ TRACE_1("params",_unit);
// Select next throwable if one already in hand
if (_unit getVariable [QGVAR(inHand), false]) exitWith {
TRACE_1("inHand",_unit);
if (!(_unit getVariable [QGVAR(primed), false])) then {
if !(_unit getVariable [QGVAR(primed), false]) then {
TRACE_1("not primed",_unit);
// Restore muzzle ammo (setAmmo 1 has no impact if no appliccable throwable in inventory)
// selectNextGrenade relies on muzzles array (setAmmo 0 removes the muzzle from the array and current can't be found, cycles between 0 and 1 muzzles)

View File

@ -20,7 +20,7 @@ TRACE_1("params",_unit);
// Prime the throwable if it hasn't been cooking already
// Next to proper simulation this also has to happen before delay for orientation of the throwable to be set
if (!(_unit getVariable [QGVAR(primed), false])) then {
if !(_unit getVariable [QGVAR(primed), false]) then {
[_unit] call FUNC(prime);
};

View File

@ -144,7 +144,7 @@
<English>Enables ability to pick up throwables from the ground.</English>
<Spanish>Activa la habilidad de coger objetos lanzados del suelo</Spanish>
<Russian>Включает возможность подбирать гранаты с земли.</Russian>
<Japanese>地面に落ちている投擲物を拾い上げる機能を有効化します。</Japanese>
<Japanese>地面に落ちている投擲物を拾機能を有効化します。</Japanese>
<Polish>Umożliwia podnoszenie obiektów miotanych z ziemi.</Polish>
<German>Aktiviert die Möglichkeit, geworfene Objekte wieder vom Boden aufzuheben.</German>
<Korean>땅에 떨어진 투척물을 주울 수 있게 해줍니다.</Korean>
@ -174,7 +174,7 @@
<English>Enables ability to pick up throwables from attached objects.</English>
<Spanish>Activa la habilidad de lanzar objetos enganchados</Spanish>
<Russian>Включает возможность подбирать гранаты, прикрепленные к объектам.</Russian>
<Japanese>オブジェクトに装着された投擲可能物を拾い上げる機能を有効化します。</Japanese>
<Japanese>オブジェクトに装着された投擲物を拾う機能を有効化します。</Japanese>
<Polish>Umożliwia podnoszenie obiektów miotanych przyczepionych do innych obiektów.</Polish>
<German>Aktiviert die Möglichkeit, befestigte Wurfobjekte erneut aufzunehmen.</German>
<Korean>부착된 투척물을 주울 수 있게 해줍니다.</Korean>
@ -254,7 +254,7 @@
<English>Primed</English>
<Spanish>Preparado</Spanish>
<Russian>Подготовлена</Russian>
<Japanese>点火</Japanese>
<Japanese>点火した</Japanese>
<Polish>Odbezpieczony</Polish>
<German>Scharf gemacht</German>
<Korean>뇌관 작동</Korean>

View File

@ -22,7 +22,7 @@ if (!alive _vehicle) exitWith {};
if (_vehicle getVariable [QGVAR(droneActionsAdded), false]) exitWith {};
_vehicle setVariable [QGVAR(droneActionsAdded), true];
// move to location
// Move to location
private _condition = {
params ["_vehicle"];
(missionNamespace getVariable [QGVAR(droneWaypoints), true]) && {waypointsEnabledUAV _vehicle} && {(ACE_controlledUAV select 2) isEqualTo [0]}
@ -37,14 +37,63 @@ private _action = [QGVAR(droneSetWaypointMove), localize "$STR_AC_MOVE",
"\a3\3DEN\Data\CfgWaypoints\Move_ca.paa", _statement, _condition] call EFUNC(interact_menu,createAction);
[_vehicle, 1, ["ACE_SelfActions"], _action] call EFUNC(interact_menu,addActionToObject);
// Follow unit/vehicle at turret location
_condition = {
params ["_vehicle"];
private _target = cursorTarget;
(missionNamespace getVariable [QGVAR(droneWaypoints), true]) && {waypointsEnabledUAV _vehicle} && {(ACE_controlledUAV select 2) isEqualTo [0]} && {!isNull _target} && {["CAManBase", "LandVehicle", "Ship"] findIf {_target isKindOf _x} != -1}
};
_statement = {
params ["_vehicle"];
private _group = group driver _vehicle;
private _pos = ([_vehicle, [0]] call FUNC(droneGetTurretTargetPos)) select 0;
[QGVAR(droneSetWaypoint), [_vehicle, _group, _pos, "FOLLOW", cursorTarget], _group] call CBA_fnc_targetEvent;
private _followDistance = _vehicle getVariable [QGVAR(wpFollowDistance), 0];
[[LLSTRING(DroneFollowHint), _followDistance], 3] call EFUNC(common,displayTextStructured);
};
_action = [QGVAR(droneSetWaypointFollow), localize "$STR_AC_FOLLOW", "\a3\3DEN\Data\CfgWaypoints\Follow_ca.paa", _statement, _condition] call EFUNC(interact_menu,createAction);
[_vehicle, 1, ["ACE_SelfActions"], _action] call EFUNC(interact_menu,addActionToObject);
// Set drone follow distance
_condition = {
params ["_vehicle"];
private _group = group driver _vehicle;
private _index = (currentWaypoint _group) min count waypoints _group;
private _waypoint = [_group, _index];
(missionNamespace getVariable [QGVAR(droneWaypoints), true]) && {waypointsEnabledUAV _vehicle} && {(ACE_controlledUAV select 2) isEqualTo [0]} && {(waypointType _waypoint) == "HOLD"}
};
_statement = {
params ["_vehicle", "", "_value"];
_vehicle setVariable [QGVAR(wpFollowDistance), _value];
[[LLSTRING(DroneFollowHint), _value], 3] call EFUNC(common,displayTextStructured);
};
_action = [QGVAR(droneSetFollowDistance), LLSTRING(DroneFollowDistance), "", {}, _condition] call EFUNC(interact_menu,createAction);
private _base = [_vehicle, 1, ["ACE_SelfActions"], _action] call EFUNC(interact_menu,addActionToObject);
private _followDistances = if (_vehicle isKindOf "Car_F") then {
[0, 25, 50, 100, 200]
} else {
[0, 100, 200, 300, 400, 500]
};
{
_action = [
QGVAR(droneSetFollowDistance_) + str _x,
str _x,
"",
_statement,
{true},
{},
_x
] call EFUNC(interact_menu,createAction);
[_vehicle, 1, _base, _action] call EFUNC(interact_menu,addActionToObject);
} forEach _followDistances;
if (_vehicle isKindOf "Air") then {
// loiter at location
// Loiter at location
_condition = {
params ["_vehicle"];
(missionNamespace getVariable [QGVAR(droneWaypoints), true]) && {waypointsEnabledUAV _vehicle} && {(ACE_controlledUAV select 2) isEqualTo [0]}
};
_statement = {
_statement = {
params ["_vehicle"];
private _group = group driver _vehicle;
private _pos = ([_vehicle, [0]] call FUNC(droneGetTurretTargetPos)) select 0;
@ -55,7 +104,7 @@ if (_vehicle isKindOf "Air") then {
[_vehicle, 1, ["ACE_SelfActions"], _action] call EFUNC(interact_menu,addActionToObject);
// set height
// Set height
_condition = {
params ["_vehicle"];
(missionNamespace getVariable [QGVAR(droneWaypoints), true]) && {waypointsEnabledUAV _vehicle} && {(ACE_controlledUAV select 2) isEqualTo [0]}
@ -74,7 +123,7 @@ if (_vehicle isKindOf "Air") then {
} forEach [20, 50, 200, 500, 2000];
// set loiter radius
// Set loiter radius
_condition = {
params ["_vehicle"];
private _group = group driver _vehicle;
@ -97,7 +146,7 @@ if (_vehicle isKindOf "Air") then {
} forEach [500, 750, 1000, 1250, 1500];
// set loiter direction
// Set loiter direction
_condition = {
params ["_vehicle", "", "_args"];
private _group = group driver _vehicle;

View File

@ -8,6 +8,7 @@
* 1: Group <GROUP>
* 2: Pos 2D <ARRAY>
* 3: Type <STRING>
* 4: Target to follow <OBJECT> (default: objNull)
*
* Return Value:
* None
@ -18,7 +19,7 @@
* Public: No
*/
params ["_vehicle", "_group", "_pos", "_type"];
params ["_vehicle", "_group", "_pos", "_type", ["_target", objNull]];
TRACE_4("droneSetWaypoint",_vehicle,_group,_pos,_type);
private _index = (currentWaypoint _group) min count waypoints _group;
@ -34,9 +35,34 @@ _pos set [
[0, _currentHeight] select (_currentHeight >= 50)
];
// [_group] call CBA_fnc_clearWaypoints;
_waypoint = _group addWaypoint [_pos, 0];
_waypoint setWaypointType _type;
// The Vanilla "FOLLOW"-type waypoint is not used directly, due to 2 main issues (as of v2.16):
// - It does not work at all for UGVs, which is a known issue https://feedback.bistudio.com/T126283;
// - No clear scripting way was found to mimic the UAV Terminal's "Follow Distance" functionality;
// Instead, the solution for both UAV and UGV following consists of a CBA PFH that moves a "HOLD"-type Waypoint every 3 seconds.
// Either on the target itself, or on the Drone's current position if the target is within the desired follow distance.
if (_type == "FOLLOW" && {["CAManBase", "LandVehicle", "Ship"] findIf {_target isKindOf _x} != -1}) then {
_waypoint setWaypointType "HOLD";
[{
params ["_args", "_handle"];
_args params ["_vehicle", "_group", "_waypoint", "_target"];
if ( // Abort PFH if a new waypoint is created via UAV Terminal or ACE Interaction
_waypoint select 1 != currentWaypoint _group ||
{!alive _vehicle} || {isNull _target}
) exitWith {
deleteWaypoint _waypoint;
[_handle] call CBA_fnc_removePerFrameHandler;
};
private _followDistance = _vehicle getVariable [QGVAR(wpFollowDistance), 0];
if ((_vehicle distance2D _target) < _followDistance) then {
_waypoint setWaypointPosition [getPosASL _vehicle, -1];
} else {
_waypoint setWaypointPosition [getPosASL _target, -1];
};
}, 3, [_vehicle, _group, _waypoint, _target]] call CBA_fnc_addPerFrameHandler;
};
TRACE_3("",_currentHeight,_currentLoiterRadius,_currentLoiterType);
if (_currentHeight > 1) then { _vehicle flyInHeight _currentHeight; };

View File

@ -129,7 +129,7 @@
<Japanese>30mm コンバット ミックス 4:1 劣化ウラン徹甲弾:焼夷榴弾</Japanese>
<Czech>30mm Bojový Mix 4:1 DU:HEI</Czech>
<Russian>30мм Смешанное боепитание 4:1 ОУ:ОФЗ</Russian>
<Korean>30mm 4:1 열화:고폭소이</Korean>
<Korean>30mm 열화우라늄:고폭소이 4:1 혼합</Korean>
<Portuguese>30mm Mix de Combate 4:1 DU:AEI</Portuguese>
</Key>
<Key ID="STR_ACE_Aircraft_GatlingDescriptionShortCM41">
@ -145,7 +145,7 @@
<Japanese>30mm CM 4:1</Japanese>
<Czech>30mm BM 4:1</Czech>
<Russian>30мм СБ 4:1</Russian>
<Korean>30mm CM 4:1</Korean>
<Korean>30mm 4:1 혼합</Korean>
</Key>
<Key ID="STR_ACE_Aircraft_GatlingDescriptionCM51">
<English>30mm Combat Mix 5:1 DU:HEI</English>
@ -160,7 +160,7 @@
<Japanese>30mm コンバット ミックス 5:1 劣化ウラン徹甲弾:焼夷榴弾</Japanese>
<Czech>30mm Bojový Mix 5:1 DU:HEI</Czech>
<Russian>30мм Смешанное боепитание 5:1 ОУ:ОФЗ</Russian>
<Korean>30mm 5:1 열화:고폭소이</Korean>
<Korean>30mm 열화우라늄:고폭소이 5:1 혼합</Korean>
</Key>
<Key ID="STR_ACE_Aircraft_GatlingDescriptionShortCM51">
<English>30mm CM 5:1</English>
@ -175,7 +175,23 @@
<Japanese>30mm CM 5:1</Japanese>
<Czech>30mm BM 5:1</Czech>
<Russian>30мм СБ 5:1</Russian>
<Korean>30mm CM 5:1</Korean>
<Korean>30mm 5:1 혼합</Korean>
</Key>
<Key ID="STR_ACE_Aircraft_DroneFollowDistance">
<English>Follow Distance</English>
<Italian>Distanza di seguimento</Italian>
<German>Folge-Entfernung</German>
<Korean>따라가는 거리</Korean>
<French>Distance de suivi</French>
<Japanese>追跡距離</Japanese>
</Key>
<Key ID="STR_ACE_Aircraft_DroneFollowHint">
<English>Following unit within %1m</English>
<Italian>Seguendo unità entro %1m</Italian>
<German>Folgt Einheit bis zu %1m</German>
<Korean>%1m 이내로 유닛을 따라갑니다</Korean>
<French>Suivre l'unité jusqu'à %1m</French>
<Japanese>%1m 間隔で 目標を追跡します</Japanese>
</Key>
</Package>
</Project>

View File

@ -78,6 +78,7 @@ PREP(removeStat);
PREP(removeVirtualItems);
PREP(renameDefaultLoadout);
PREP(replaceUniqueItemsLoadout);
PREP(saveLoadout);
PREP(scanConfig);
PREP(showItem);
PREP(sortPanel);

View File

@ -85,10 +85,10 @@ private _insigniaCondition = toString {
// Ref fnc_addListBoxItem, 0/nil = configFile, 1 = campaignConfigFile, 2 = missionConfigFile
{
GVAR(insigniaCache) set [_x, 1];
GVAR(insigniaCache) set [configName _x, 1];
} forEach (_insigniaCondition configClasses (campaignConfigFile >> "CfgUnitInsignia"));
{
GVAR(insigniaCache) set [_x, 2];
GVAR(insigniaCache) set [configName _x, 2];
} forEach (_insigniaCondition configClasses (missionConfigFile >> "CfgUnitInsignia"));
ADDON = true;

View File

@ -26,28 +26,27 @@ if (GVAR(shiftState)) then {
switch (true) do {
// Beginning
case (_index == -1): {
"ace_clipboard" callExtension (format ["[%1", endl]);
"ace" callExtension ["clipboard:append", [format ["[%1", endl]]];
};
// End
case (_index == _listLength): {
"ace_clipboard" callExtension "];";
"ace" callExtension ["clipboard:append", ["];"]];
};
// Rest
default {
"ace_clipboard" callExtension ([" ", str (GVAR(defaultLoadoutsList) select _index), [",", ""] select (_index == _listLength - 1), endl] joinString "");
"ace" callExtension ["clipboard:append", [[" ", str (GVAR(defaultLoadoutsList) select _index), [",", ""] select (_index == _listLength - 1), endl] joinString ""]];
};
};
};
"ace_clipboard" callExtension "--COMPLETE--";
"ace" callExtension ["clipboard:complete", []];
[_display, LLSTRING(exportDefault)] call FUNC(message);
} else {
// Export singular loadout
private _export = str (GVAR(center) call CBA_fnc_getLoadout);
"ace_clipboard" callExtension (_export + ";");
"ace_clipboard" callExtension "--COMPLETE--";
"ace" callExtension ["clipboard:append", [_export]];
"ace" callExtension ["clipboard:complete", []];
[_display, LLSTRING(exportCurrent)] call FUNC(message);
};

View File

@ -16,7 +16,13 @@
params ["_display"];
// Can be either a singular loadout or an array of loadouts
private _extendedLoadout = call compile copyFromClipboard;
private _extendedLoadout = if (isMultiplayer) then {
("ace" callExtension ["clipboard:loadout", []]) params ["_loadout", "_code"];
if (_code != 0) exitWith {};
parseSimpleArray _loadout
} else {
call compile copyFromClipboard
};
// If error, exit
if (isNil "_extendedLoadout" || {!(_extendedLoadout isEqualType [])}) exitWith {

View File

@ -18,7 +18,7 @@
(_this select 1) params ["", "_exitCode"];
[QGVAR(displayClosed), []] call CBA_fnc_localEvent;
removeMissionEventHandler ["draw3D", GVAR(camPosUpdateHandle)];
removeMissionEventHandler ["Draw3D", GVAR(camPosUpdateHandle)];
if (is3DEN) then {
private _centerOriginParent = objectParent GVAR(centerOrigin);

View File

@ -138,14 +138,6 @@ _actionsBoxCtrl ctrlSetPosition [
];
_actionsBoxCtrl ctrlCommit 0;
// Disable import in MP
if (isMultiplayer) then {
private _importButtonCtrl = _display displayCtrl IDC_buttonImport;
_importButtonCtrl ctrlEnable false;
_importButtonCtrl ctrlSetFade 0.6;
_importButtonCtrl ctrlCommit 0;
};
//--------------- Camera prep
cutText ["", "PLAIN"];
showCommandingMenu "";
@ -278,4 +270,4 @@ showCinemaBorder false;
//--------------- Reset camera pos
[nil, [controlNull, 0, 0]] call FUNC(handleMouse);
GVAR(camPosUpdateHandle) = addMissionEventHandler ["draw3D", {call FUNC(updateCamPos)}];
GVAR(camPosUpdateHandle) = addMissionEventHandler ["Draw3D", {call FUNC(updateCamPos)}];

View File

@ -94,8 +94,8 @@ if (!isNull _loadoutsDisplay) then {
};
} params ["_className"];
"ace_clipboard" callExtension (_className + ";");
"ace_clipboard" callExtension "--COMPLETE--";
"ace" callExtension ["clipboard:append", [_className]];
"ace" callExtension ["clipboard:complete", []];
[_display, LLSTRING(exportedClassnameText)] call FUNC(message);
} else {

View File

@ -382,6 +382,9 @@ switch (GVAR(currentLeftPanel)) do {
};
GVAR(currentItems) set [IDX_CURR_VEST, _item];
[GVAR(center), ""] call BIS_fnc_setUnitInsignia;
[GVAR(center), GVAR(currentInsignia)] call BIS_fnc_setUnitInsignia;
};
TOGGLE_RIGHT_PANEL_CONTAINER
@ -420,6 +423,9 @@ switch (GVAR(currentLeftPanel)) do {
};
GVAR(currentItems) set [IDX_CURR_BACKPACK, _item];
[GVAR(center), ""] call BIS_fnc_setUnitInsignia;
[GVAR(center), GVAR(currentInsignia)] call BIS_fnc_setUnitInsignia;
};
TOGGLE_RIGHT_PANEL_CONTAINER

View File

@ -0,0 +1,37 @@
#include "..\script_component.hpp"
/*
* Author: DartRuffian
* Saves a given loadout to the client's profile.
*
* Arguments:
* 0: Name of loadout <STRING>
* 1: CBA extended loadout or getUnitLoadout array <ARRAY>
* 2: Replace existing loadout <BOOL> (default: false)
*
* Return Value:
* True if loadout was saved, otherwise false <BOOL>
*
* Example:
* ["Current Loadout", getUnitLoadout ACE_player] call ace_arsenal_fnc_saveLoadout
*
* Public: Yes
*/
params [["_name", "", [""]], ["_loadout", [], [[]]], ["_replaceExisting", false, [false]]];
if (_name == "" || {_loadout isEqualTo []}) exitWith { false };
private _loadouts = profileNamespace getVariable [QGVAR(saved_loadouts), []];
private _loadoutIndex = _loadouts findIf {(_x#0) == _name};
// If a loadout with same name already exists and no overwriting enabled, quit
if (!_replaceExisting && {_loadoutIndex != -1}) exitWith { false };
if (_loadoutIndex == -1) then {
_loadouts pushBack [_name, _loadout];
} else {
_loadouts set [_loadoutIndex, [_name, _loadout]];
};
profileNamespace setVariable [QGVAR(saved_loadouts), _loadouts];
true

View File

@ -72,6 +72,6 @@ if (_nextAction != GVAR(currentAction)) then {
GVAR(currentAction) = _nextAction;
};
if (!(GVAR(currentAction) in ["Civil", "Salute"])) then {
if !(GVAR(currentAction) in ["Civil", "Salute"]) then {
GVAR(center) selectWeapon ([primaryWeapon GVAR(center), secondaryWeapon GVAR(center), handgunWeapon GVAR(center), binocular GVAR(center)] select GVAR(selectedWeaponType)); // select correct weapon, prevents floating weapons
};

View File

@ -14,4 +14,9 @@
params ["_config"];
(modParams [_config call EFUNC(common,getAddon), ["name"]]) param [0, ""]
private _addon = _config call EFUNC(common,getAddon);
// Calling modParams with "" prints 'ModParams - Undefined or empty mod directory' in RPT
if (_addon == "") exitWith {""};
(modParams [_addon, ["name"]]) param [0, ""]

View File

@ -63,7 +63,7 @@ _target switchMove "amovpercmstpslowwrfldnon";
_target setVariable ["origin", _position];
// When killed, respawn AI
_target addEventHandler ["killed", {
_target addEventHandler ["Killed", {
params ["_target"];
// Killed may fire twice, 2nd will be null - https://github.com/acemod/ACE3/pull/7561

View File

@ -23,7 +23,7 @@
<French>Masque l'interface</French>
<German>Oberfläche verstecken</German>
<Polish>Ukryj interfejs</Polish>
<Japanese>インタフェースを隠す</Japanese>
<Japanese>インタフェースを隠す</Japanese>
<Italian>Nascondi interfaccia</Italian>
<Korean>인터페이스 숨기기</Korean>
<Chinese>隱藏介面</Chinese>
@ -1245,6 +1245,7 @@
<Russian>Интегрирован тепловизор.</Russian>
<Korean>열화상 내장</Korean>
<French>Thermique intégrée</French>
<German>Thermal integriert</German>
<Spanish>Térmica integrada</Spanish>
</Key>
<Key ID="STR_ACE_Arsenal_statVisionMode_intPrimTi">
@ -1254,6 +1255,7 @@
<Russian>Интегрирован тепловизор и осн.прицел.</Russian>
<Korean>열화상과 주무기 내장</Korean>
<French>Thermique et primaire intégrés</French>
<German>Thermal und in Primärwaffe integriert</German>
<Spanish>Térmica y Primaria integrada</Spanish>
</Key>
<Key ID="STR_ACE_Arsenal_statVisionMode_NoSup">

View File

@ -1,8 +1,14 @@
TRACE_1("prep",_this);
PREP(adjustFire);
PREP(calculateElevation);
PREP(calculateMaxAngle);
PREP(calculateMuzzleVelocity);
PREP(calculateSolution);
PREP(firedEH);
PREP(interactMenuOpened);
PREP(rangeTableOpen);
PREP(rangeTableUpdate);
PREP(simulateShot);
PREP(turretChanged);
PREP(turretPFEH);

View File

@ -4,10 +4,13 @@
TRACE_2("CBA_settingsInitialized",GVAR(advancedCorrections),GVAR(disableArtilleryComputer));
if (hasInterface) then {
// Add hud overlay for actuall azimuth and elevation:
// Add hud overlay for actual azimuth and elevation:
GVAR(pfID) = -1;
["turret", LINKFUNC(turretChanged), true] call CBA_fnc_addPlayerEventHandler;
// Handles being teleported from one vehicle to another
["vehicle", {[_this select 0, (_this select 1) unitTurret (_this select 0)] call FUNC(turretChanged)}] call CBA_fnc_addPlayerEventHandler;
// Add ability to dynamically open rangetables:
["ace_interactMenuOpened", LINKFUNC(interactMenuOpened)] call CBA_fnc_addEventHandler;
};
@ -30,6 +33,33 @@
};
}] call CBA_fnc_addEventHandler;
addMissionEventHandler ["ExtensionCallback", {
params ["_name", "_function", "_data"];
if (_name == "ace:artillery" && {_function == "calculate_table"}) then {
(parseSimpleArray _data) params ["_line", "_data"];
if (_data isEqualType []) then {
GVAR(tableData) set [_line, _data];
};
GVAR(tableSizeReceived) = GVAR(tableSizeReceived) + 1;
if (GVAR(tableSizeReceived) == GVAR(tableSizeActual)) then {
private _dialog = uiNamespace getVariable [QGVAR(rangeTableDialog), displayNull];
private _ctrlRangeTable = _dialog displayCtrl IDC_TABLE;
if (isNull _dialog) exitWith {true};
for "_i" from 0 to GVAR(tableSizeActual) do {
private _row = GVAR(tableData) getOrDefault [_i, []];
if (count _row == 12) then {
_ctrlRangeTable lnbAddRow _row;
};
};
private _dialog = uiNamespace getVariable [QGVAR(rangeTableDialog), displayNull];
private _ctrlRangeTable = _dialog displayCtrl IDC_TABLE;
if (isNull _dialog) exitWith {TRACE_1("dialog closed",_this);};
_ctrlRangeTable lnbAddRow ["", "", "", "", "", "", "", "", "", "", ""];
TRACE_1("table filled",_ctrlRangeTable);
};
};
}];
#ifdef DEBUG_MODE_FULL
#include "dev\showShotInfo.inc.sqf"
#include "dev\checkConfigs.inc.sqf"

View File

@ -14,13 +14,6 @@ class CfgPatches {
};
};
class ACE_Extensions {
class ace_artillerytables {
windows = 1;
client = 1;
};
};
#include "CfgEventHandlers.hpp"
#include "CfgMagazines.hpp"
#include "CfgVehicles.hpp"

View File

@ -0,0 +1,41 @@
#include "..\script_component.hpp"
/*
* Author: LorenLuke
* Adjusts a target point north and east, and recalculates a solution in air based on atmospheric conditions
*
* Arguments:
* 0: Gun Position ASL <ARRAY>
* 1: Target Position ASL <ARRAY>
* 2: Adjustment to the East (negative is West); meters <NUMBER>
* 3: Adjustment to the North (negative is South); meters <NUMBER>
* 4: Adjustment vertically (negative is Down); meters <NUMBER>
* 5: Muzzle velocity; meters/second <NUMBER>
* 6: Air Friction; meters^-1 (m/s^2)/(m^2/s^2) <NUMBER>
* 7: High angle boolean (true is high angle) <BOOL>
* 8: Temperature; degrees Celsius <NUMBER>
* 9: Atmospheric Density; kg/(meters^3) <NUMBER>
* 10: Direction of wind; degrees clockwise from north <NUMBER>
* 11: Speed of wind; meters/second <NUMBER>
*
* Return Value:
* Array of returns <ARRAY>
* 0: Angle of shot; Milliradians <NUMBER>
* 1: Angle adjust left or right; Milliradians <NUMBER>
* 2: Time of flight; seconds <NUMBER>
*
* Example:
* [getposASL vehicle player, targetPos, 20, 50, 0, 200, -0.0001, true, 15, 1.225, 225, 5] call ace_artilleryTables_fnc_adjustFire
*
* Public: No
*/
params ["_gunPos", "_targetPos", "_adjustEast", "_adjustNorth", "_adjustUp", "_muzzleVelocity", "_airFriction", ["_highAngle", true], ["_temperature", 15], ["_airDensity", 1.225], ["_windDir", 0], ["_windSpeed", 0]];
//DEFAULT_AIR_FRICTION == -0.00006
//MK6_82mm_AIR_FRICTION == -0.0001
private _resultPos = [_adjustEast + _targetPos select 0, _adjustNorth + _targetPos select 1, _adjustUp + _targetPos select 2];
private _returns = ["_gunPos", "_resultPos", "_muzzleVelocity", "_highAngle", "_airFriction", "_temperature", "_airDensity", "_windDir", "_windSpeed"] call FUNC(calculateSolution);
_returns

View File

@ -0,0 +1,71 @@
#include "..\script_component.hpp"
/*
* Author: LorenLuke
* Adjusts a target point north and east, and recalculates a solution in air based on atmospheric conditions
*
* Arguments:
* 0: Distance to Target; meters <NUMBER>
* 1: Height of target; meters, relative to gun altitude (positive means target higher than gun) <NUMBER>
* 2: Muzzle velocity; meters/second <NUMBER>
* 3: High angle boolean (true is high angle) <BOOL>
* 4: Air Friction; meters^-1 [(m/s^2)/(m^2/s^2)] <NUMBER>
* 5: Temperature; degrees Celsius <NUMBER>
* 6: Atmospheric Density; kg/(meters^3) <NUMBER>
* 7: Cross wind; meters/second (negative is Right to Left) <NUMBER>
* 8: Tail wind; meters/second (negative is flying against the wind) <NUMBER>
*
* Return Value:
* Array of returns <ARRAY>
* 0: Angle of shot; Milliradians <NUMBER>
* 1: Angle adjust left or right; Milliradians <NUMBER>
* 2: Time of flight; seconds <NUMBER>
*
* Example:
* [myPos, 0, 200, true, -0.0001, 15, 1.225, 5, -10] call ace_artilleryTables_fnc_calculateElevation
*
* Public: No
*/
params ["_targetDistance", "_targetHeight", "_muzzleVelocity", ["_highArc", true], ["_airFriction", 0], ["_temperature", 15], ["_airDensity", 1.225], ["_crossWind", 0], ["_tailWind", 0]];
//DEFAULT_AIR_FRICTION == -0.00006
//MK6_82mm_AIR_FRICTION == -0.0001
if (_airFriction != 0) then {
_muzzleVelocity = [_muzzleVelocity, _temperature, _atmosphericDensity] call FUNC(calculateMuzzleVelocity);
};
private _maxResults = [_muzzleVelocity, _airFriction] call FUNC(calculateMaxAngle);
private _testShot = [_maxResults select 0, _targetHeight, _muzzleVelocity, _airFriction, _crossWind, _tailWind, _temperature, _airDensity] call FUNC(simulateShot);
if (_testShot select 1 < _targetDistance) exitWith {
//No way we can hit it so don't bother;
[-1, -1, -1]
};
private _useDistance = _targetDistance;
private _useAngle = 0;
private _resultDistance = 0;
private _xDeviation = 0;
private _tof = 0;
while {abs(_resultDistance - _targetDistance) > 0.5} do {
TRACE_7("callExtension:artillery:simulate_find_solution",_useDistance,_targetHeight,_muzzleVelocity,_airFriction,_higharc,DEFAULT_MIN_ELEV,DEFAULT_MAX_ELEV);
(
"ace" callExtension ["artillery:simulate_find_solution", [_useDistance, _targetHeight, _muzzleVelocity, _airFriction, _higharc, DEFAULT_MIN_ELEV, DEFAULT_MAX_ELEV]]
) params ["_data", "_code"];
TRACE_1("",_code);
(parseSimpleArray _data) params ["", "_useAngleRad", ""];
_useAngle = deg(_useAngleRad) * DEGTOMILS;
private _shotResults = [_useAngle, _targetHeight, _muzzleVelocity, _airFriction, _crossWind, _tailWind, _temperature, _airDensity] call FUNC(simulateShot);
_xDeviation = _shotResults select 0;
_resultDistance = _shotResults select 1;
_tof = _shotResults select 2;
_useDistance = (2 * _targetDistance) - _resultDistance;
};
private _angleOffsetDeg = _xDeviation atan2 _resultDistance;
private _angleOffset = _angleOffsetDeg * DEGTOMILS;
[_useAngle, -_angleOffset, _tof]

View File

@ -0,0 +1,31 @@
#include "..\script_component.hpp"
/*
* Author: LorenLuke
* Calculates the best possible angle to shoot farthest based on muzzle velocity and air friction.
*
* Arguments:
* 0: Initial Muzzle Velocity; meters/second <NUMBER>
* 1: Air Friction; meters^-1 (m/s^2)/(m^2/s^2) <NUMBER>
*
* Return Values: <ARRAY>
* 1: Best Angle; Milliradians <NUMBER>
* 2: Furthest Distance; Meters <NUMBER>
*
* Example:
* [200, -0.00006] call ace_artilleryTables_fnc_calculateMaxAngle
*
* Public: No
*/
params ["_muzzleVelocity", "_airFriction"];
TRACE_2("callExtension:artillery:find_max_angle",_muzzleVelocity,_airFriction);
(
"ace" callExtension ["artillery:find_max_angle", [_muzzleVelocity, _airFriction]]
) params ["_data", "_code"];
TRACE_1("",_code)
(parseSimpleArray _data) params ["_bestAngle", "_bestDistance", ""];
_returns = [deg _bestAngle * 6400 / 360, _bestDistance];
_returns params ["_bestAngle", "_bestDistance"];
_returns

View File

@ -0,0 +1,28 @@
#include "..\script_component.hpp"
/*
* Author: LorenLuke
* Calculates the muzzleVelocity change with advanced calculations.
*
* Arguments:
* 0: Initial Muzzle velocity; meters/second <NUMBER>
* 1: Temperature; degrees Celsius <NUMBER>
* 2: Atmospheric Density; kg/(meters^3) <NUMBER>
*
* Return Value:
* Adjusted Muzzle Velocity; Meters <NUMBER>
*
* Example:
* [200, 15, 1.225] call ace_artilleryTables_fnc_calculateMuzzleVelocity
*
* Public: No
*/
params ["_muzzleVelocity", "_temperature", "_airDensity"];
// Calculate air density
private _relativeDensity = _airDensity / 1.225;
private _newMuzzleVelocityCoefficient = (((_temperature + 273.13) / 288.13 - 1) / 40 + 1);
private _newMuzzleVelocity = _muzzleVelocity * _newMuzzleVelocityCoefficient;
_newMuzzleVelocity

View File

@ -0,0 +1,44 @@
#include "..\script_component.hpp"
/*
* Author: LorenLuke
* Provides fire angle and deflection solutions on a target of set distance and height, including accounting for drag and atmospheric wind conditions.
*
* Arguments:
* 0: Gun Position ASL; <ARRAY>
* 1: Target Position ASL; <ARRAY>
* 2: Muzzle Velocity; meters/second <NUMBER>
* 3: High angle boolean (true is high angle) <BOOL>
* 4: Air Friction; meters^-1 [(m/s^2)/(m^2/s^2)] <NUMBER>
* 5: Temperature; degrees Celsius <NUMBER>
* 6: Atmospheric Density; kg/(meters^3) <NUMBER>
* 7: Direction of wind; degrees clockwise from north <NUMBER>
* 8: Speed of wind; meters/second <NUMBER>
*
* Return Value:
* array of returns <ARRAY>
* 0: Angle of shot; Milliradians <NUMBER>
* 1: Angle adjust left or right; Milliradians <NUMBER>
* 2: Time of flight; seconds <NUMBER>
*
* Example:
* [myPos, targetPos, 200, true, -0.0001, 15, 1.225, 225, 5] call ace_artilleryTables_fnc_calculateSolution
*
* Public: No
*/
params ["_ownPos", "_targetPos", "_muzzleVelocity", ["_highAngle", true], ["_airFriction", 0], ["_temperature", 15], ["_airDensity", 1.225], ["_windDir", 0], ["_windSpeed", 0]];
//DEFAULT_AIR_FRICTION == -0.00006
//MK6_82mm_AIR_FRICTION == -0.0001
private _relPos = _targetPos vectorDiff _ownPos;
private _targetDir = (_relpos select 0) atan2 (_relPos select 1);
private _targetDist = sqrt( (_relPos select 0)^2 + (_relpos select 1)^2 );
private _heightDif = _relPos select 2;
private _crossWind = sin(_targetDir - _windDir) * _windSpeed;
private _tailWind = -cos(_targetDir - _windDir) * _windSpeed;
private _solutionReturns = [_targetDist, _heightDif, _muzzleVelocity, _highAngle, _airFriction, _crossWind, _tailWind, _temperature, _airDensity] call FUNC(calculateElevation);
_solutionReturns

View File

@ -25,7 +25,7 @@
params ["_vehicle", "", "", "", "", "_magazine", "_projectile", "_gunner"];
TRACE_4("firedEH",_vehicle,_magazine,_projectile,_gunner);
if (!([_gunner] call EFUNC(common,isPlayer))) exitWith {}; // AI don't know how to use (this does give them more range than a player)
if !([_gunner] call EFUNC(common,isPlayer)) exitWith {}; // AI don't know how to use (this does give them more range than a player)
if ((gunner _vehicle) != _gunner) exitWith {}; // check if primaryGunner
@ -35,7 +35,7 @@ if (isNumber (configFile >> "CfgMagazines" >> _magazine >> QGVAR(airFriction)))
_airFriction = getNumber (configFile >> "CfgMagazines" >> _magazine >> QGVAR(airFriction));
};
TRACE_1("",_airFriction);
if (_airFriction >= 0) exitWith {}; // 0 disables everything, >0 makes no sense
if (_airFriction == 0) exitWith {}; // 0 disables everything
BEGIN_COUNTER(adjustmentsCalc);
@ -60,6 +60,7 @@ if (_newMuzzleVelocityCoefficent != 1) then {
_projectile setVelocity _bulletVelocity;
};
if (_airFriction > 0) exitWith {}; // positive value indicates it has vanilla airFriction, so we can just exit
[{
params ["_projectile", "_kFactor", "_time"];

View File

@ -19,7 +19,7 @@ params ["_menuType"];
TRACE_1("interactMenuOpened",_menuType);
if (_menuType != 1) exitWith {};
if (!("ACE_artilleryTable" in (ace_player call EFUNC(common,uniqueItems)))) exitWith {};
if !("ACE_artilleryTable" in (ace_player call EFUNC(common,uniqueItems))) exitWith {};
private _vehicleAdded = ace_player getVariable [QGVAR(vehiclesAdded), []];
private _rangeTablesShown = ace_player getVariable [QGVAR(rangeTablesShown), []];

View File

@ -41,8 +41,15 @@ _mags = _mags apply {
private _initSpeed = getNumber (_magCfg >> _x >> "initSpeed");
_magParamsArray pushBackUnique _initSpeed;
private _airFriction = 0;
if (_advCorrection) then {
_airFriction = if (isNumber (_magCfg >> _x >> QGVAR(airFriction))) then { getNumber (_magCfg >> _x >> QGVAR(airFriction)) } else { DEFAULT_AIR_FRICTION };
private _magAirFriction = getNumber (_magCfg >> _x >> QGVAR(airFriction));
if (_magAirFriction <= 0) then {
if (_advCorrection) then {
_airFriction = [DEFAULT_AIR_FRICTION, _magAirFriction] select (isNumber (_magCfg >> _x >> QGVAR(airFriction)));
};
} else {
// positive value, use ammo's airFriction (regardless of setting)
private _ammo = getText (_magCfg >> _x >> "ammo");
_airFriction = getNumber (configFile >> "CfgAmmo" >> _ammo >> "airFriction");
};
_magParamsArray pushBackUnique _airFriction;
[getText (_magCfg >> _x >> "displayNameShort"), getText (_magCfg >> _x >> "displayName"), _initSpeed, _airFriction]

View File

@ -34,31 +34,12 @@ _ctrlElevationLow ctrlSetTextColor ([[1,1,1,1],[0.25,0.25,0.25,1]] select GVAR(l
lnbClear _ctrlRangeTable;
// Call extension with current data and start workers
TRACE_5("callExtension:start",_muzzleVelocity,_airFriction,_elevMin,_elevMax,GVAR(lastElevationMode));
private _ret = "ace_artillerytables" callExtension ["start", [_muzzleVelocity,_airFriction,_elevMin,_elevMax,GVAR(lastElevationMode)]];
TRACE_1("",_ret);
TRACE_5("callExtension:artillery:calculate_table",_muzzleVelocity,_airFriction,_elevMin,_elevMax,GVAR(lastElevationMode));
(
"ace" callExtension ["artillery:calculate_table", [_muzzleVelocity, _airFriction, _elevMin, _elevMax, GVAR(lastElevationMode)]]
) params ["_data", "_code"];
TRACE_1("",_code)
// Non-blocking read data out of extension as it becomes availiable
[{
private _dialog = uiNamespace getVariable [QGVAR(rangeTableDialog), displayNull];
private _ctrlRangeTable = _dialog displayCtrl IDC_TABLE;
if (isNull _dialog) exitWith {true};
private _status = 1; // 1 = data on line, 2 - data not ready, 3 - done
while {_status == 1} do {
private _ret = ("ace_artillerytables" callExtension ["getline", []]);
// TRACE_1("callExtension:getline",_ret);
_status = _ret select 1;
if (_status == 1) then { _ctrlRangeTable lnbAddRow parseSimpleArray (_ret select 0) };
};
(_status == 3) // exit loop when all data read
}, {
// put dummy line at end because scrolling is problematic and can't see last line
private _dialog = uiNamespace getVariable [QGVAR(rangeTableDialog), displayNull];
private _ctrlRangeTable = _dialog displayCtrl IDC_TABLE;
if (isNull _dialog) exitWith {TRACE_1("dialog closed",_this);};
_ctrlRangeTable lnbAddRow ["", "", "", "", "", "", "", "", "", "", ""];
TRACE_1("table filled",_ctrlRangeTable);
}, []] call CBA_fnc_waitUntilAndExecute;
GVAR(tableData) = createHashMap;
GVAR(tableSizeActual) = (parseSimpleArray _data) select 1;
GVAR(tableSizeReceived) = 0;

View File

@ -0,0 +1,47 @@
#include "..\script_component.hpp"
/*
* Author: LorenLuke
* Simulates an indirect shot on a target of known height with given drag, wind, and atmospheric conditions
*
* Arguments:
* 0: Gun Elevation Angle; milliradians <NUMBER>
* 1: Relative Target Height; meters, relative to gun altitude (positive means target higher than gun) <NUMBER>
* 2: Muzzle Velocity; meters/second <NUMBER>
* 3: Air Friction; meters^-1 [(m/s^2)/(m^2/s^2)] <NUMBER>
* 4: Cross wind; meters/second (negative is Right to Left) <NUMBER>
* 5: Tail wind; meters/second (negative is flying against the wind) <NUMBER>
* 6: Temperature; degrees Celsius <NUMBER>
* 7: Atmospheric Density; kg/(meters^3) <NUMBER>
*
* Return Value:
* array of returns <ARRAY>
* 0: Deflection Adjustment To Hit; Milliradians (negative is Left) <NUMBER>
* 1: Distance of Shot; meters <NUMBER>
* 2: Time of Flight; seconds <NUMBER>
*
* Example:
* [900, 10, 200, -0.0001, 4, 0, 15, 1.225] call ace_artilleryTables_fnc_simulateShot
*
* Public: No
*/
params ["_angle", "_targetHeight", "_muzzleVelocity", ["_airFriction", 0], ["_crossWind", 0], ["_tailWind", 0], ["_temperature", 15], ["_atmosphericDensity", 1.225]];
//DEFAULT_AIR_FRICTION == -0.00006
//MK6_82mm_AIR_FRICTION == -0.0001
if (_airFriction != 0) then {
_muzzleVelocity = [_muzzleVelocity, _temperature, _atmosphericDensity] call FUNC(calculateMuzzleVelocity);
};
private _atmosphericDensityRatio = _atmosphericDensity / 1.225;
private _radAngle = rad(_angle / DEGTOMILS);
TRACE_8("callExtension:artillery:simulate_shot",_radAngle,_targetHeight,_muzzleVelocity,_airFriction,_crossWind,_tailWind,_temperature,_atmosphericDensityRatio);
(
"ace" callExtension ["artillery:simulate_shot", [_radAngle, _targetHeight, _muzzleVelocity, _airFriction, _crossWind, _tailWind, _temperature, _atmosphericDensityRatio]]
) params ["_data", "_code"];
TRACE_1("",_code)
//[xDeviation, yDistance, timeOfFlight]
parseSimpleArray _data

View File

@ -30,7 +30,10 @@ if (isNull (uiNamespace getVariable [QGVAR(display), displayNull])) then {
};
private _ctrlGroup = (uiNamespace getVariable [QGVAR(display), displayNull]) displayCtrl 1000;
if (cameraView != "GUNNER") exitWith { // need to be in gunner mode, so we can check where the optics are aiming at
// Need to be in gunner mode, so we can check where the optics are aiming at
// However, if there are no optics, ignore the above
if (!_invalidGunnerMem && {cameraView != "GUNNER"}) exitWith {
_ctrlGroup ctrlShow false;
};
_ctrlGroup ctrlShow true;

View File

@ -15,7 +15,5 @@ private _categoryName = [format ["ACE %1", localize "str_a3_cfgmarkers_nato_art"
[LSTRING(disableArtilleryComputer_displayName), LSTRING(disableArtilleryComputer_description)],
_categoryName,
false, // default value
true, // isGlobal
{[QGVAR(disableArtilleryComputer), _this] call EFUNC(common,cbaSettings_settingChanged)},
false // Needs mission restart
true // isGlobal
] call CBA_fnc_addSetting;

View File

@ -12,6 +12,10 @@
// This is a good fit for most large artillery, but a little low for lighter mortars
#define DEFAULT_AIR_FRICTION -0.00006
#define DEFAULT_MIN_ELEV 0
// 90 degrees in radians
#define DEFAULT_MAX_ELEV 1.5708
#define DEGTOMILS 17.7777778
#define IDC_MODECONTROLGROUP 1000

View File

@ -6,7 +6,7 @@ class CfgPatches {
units[] = {"ACE_Item_ATragMX"};
weapons[] = {"ACE_ATragMX"};
requiredVersion = REQUIRED_VERSION;
requiredAddons[] = {"ACE_Advanced_Ballistics", "ACE_common", "ACE_weather"};
requiredAddons[] = {"ace_advanced_ballistics", "ace_common", "ace_weather"};
author = ECSTRING(common,ACETeam);
authors[] = {"Ruthberg"};
url = ECSTRING(main,URL);

View File

@ -30,9 +30,16 @@ while {_velocity > _thresholdVelocity} do {
private _bc = GVAR(targetSolutionInput) select 14;
private _dragModel = GVAR(targetSolutionInput) select 15;
private _temperature = GVAR(targetSolutionInput) select 5;
private _drag = parseNumber(("ace_advanced_ballistics" callExtension format["retard:%1:%2:%3:%4", _dragModel, _bc, _velocity, _temperature]));
_distance = _distance + _velocity * __DELTA_T;
_velocity = _velocity - (_drag * __DELTA_T);
private _data = (
"ace" callExtension ["ballistics:retard", [
_dragModel,
_bc,
_velocity,
_temperature
]]
) select 0;
_velocity = _velocity - ((parseNumber _data) * __DELTA_T);
};
_distance

View File

@ -90,7 +90,14 @@ private _wind1 = [cos(270 - _windDirection * 30) * _windSpeed1, sin(270 - _windD
private _wind2 = [cos(270 - _windDirection * 30) * _windSpeed2, sin(270 - _windDirection * 30) * _windSpeed2, 0];
private _windDrift = 0;
if (missionNamespace getVariable [QEGVAR(advanced_ballistics,enabled), false]) then {
_bc = parseNumber(("ace_advanced_ballistics" callExtension format["atmosphericCorrection:%1:%2:%3:%4:%5", _bc, _temperature, _barometricPressure, _relativeHumidity, _atmosphereModel]));
_bc = parseNumber (("ace" callExtension ["ballistics:atmospheric_correction", [
_bc,
_temperature,
_barometricPressure,
_relativeHumidity,
_atmosphereModel
]]
) select 0);
};
private _eoetvoesMultiplier = 0;
@ -113,8 +120,15 @@ while {_TOF < 15 && (_bulletPos select 1) < _targetRange} do {
_trueSpeed = vectorMagnitude _trueVelocity;
if (missionNamespace getVariable [QEGVAR(advanced_ballistics,enabled), false]) then {
private _drag = parseNumber(("ace_advanced_ballistics" callExtension format["retard:%1:%2:%3:%4", _dragModel, _bc, _trueSpeed, _temperature]));
_bulletAccel = (vectorNormalized _trueVelocity) vectorMultiply (-1 * _drag);
private _data = (
"ace" callExtension ["ballistics:retard", [
_dragModel,
_bc,
_trueSpeed,
_temperature
]]
) select 0;
_bulletAccel = (vectorNormalized _trueVelocity) vectorMultiply (-1 * (parseNumber _data));
} else {
_bulletAccel = _trueVelocity vectorMultiply (_trueSpeed * _airFriction);
};

View File

@ -23,7 +23,7 @@ if ((profileNamespace getVariable ["ACE_ATragMX_profileNamespaceVersion", 0]) ==
_resetGunList = false;
{
// Verify each gun has correct param type
if (!(_x isEqualTypeArray ["", 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, "", [], [], false])) exitWith {
if !(_x isEqualTypeArray ["", 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, "", [], [], false]) exitWith {
_resetGunList = true;
};
} forEach GVAR(gunList);

View File

@ -74,7 +74,7 @@ private _validate_preset = {
ERROR(_errorMsg);
_valid = false;
};
if (!((_this select 17) in ["ASM", "ICAO"])) then {
if !((_this select 17) in ["ASM", "ICAO"]) then {
private _errorMsg = format ["Invalid atmosphere model: %1", _this select 17];
ERROR(_errorMsg);
_valid = false;

View File

@ -26,7 +26,7 @@ if !(ctrlVisible 9000) then {
params ["_args"];
_args params ["_startTime"];
if (!(GVAR(speedAssistTimer))) exitWith {
if !(GVAR(speedAssistTimer)) exitWith {
GVAR(speedAssistTimer) = true;
ctrlSetText [8006, Str(Round((CBA_missionTime - _startTime) * 10) / 10)];

View File

@ -15,7 +15,7 @@
* Public: No
*/
if (!(missionNamespace getVariable [QEGVAR(advanced_ballistics,enabled), false])) exitWith {};
if !(missionNamespace getVariable [QEGVAR(advanced_ballistics,enabled), false]) exitWith {};
if (ctrlVisible 17000) then {
false call FUNC(show_c1_ballistic_coefficient_data);

View File

@ -15,7 +15,7 @@
* Public: No
*/
if (!(missionNamespace getVariable [QEGVAR(advanced_ballistics,enabled), false])) exitWith {};
if !(missionNamespace getVariable [QEGVAR(advanced_ballistics,enabled), false]) exitWith {};
if (ctrlVisible 16000) then {
false call FUNC(show_muzzle_velocity_data);

View File

@ -15,7 +15,7 @@
* Public: No
*/
if (!(missionNamespace getVariable [QEGVAR(advanced_ballistics,enabled), false])) exitWith {};
if !(missionNamespace getVariable [QEGVAR(advanced_ballistics,enabled), false]) exitWith {};
if (ctrlVisible 18000) then {
false call FUNC(show_truing_drop);

View File

@ -35,12 +35,26 @@ if (!GVAR(atmosphereModeTBH)) then {
_relativeHumidity = 0.5;
};
private _scopeBaseAngle = if (!(missionNamespace getVariable [QEGVAR(advanced_ballistics,enabled), false])) then {
private _zeroAngle = "ace_advanced_ballistics" callExtension format ["calcZero:%1:%2:%3:%4", _zeroRange, _muzzleVelocity, _airFriction, _boreHeight];
(parseNumber _zeroAngle)
private _scopeBaseAngle = if !(missionNamespace getVariable [QEGVAR(advanced_ballistics,enabled), false]) then {
parseNumber (("ace" callExtension ["ballistics:zero_vanilla", [
_zeroRange,
_muzzleVelocity,
_airFriction,
_boreHeight
]]) select 0)
} else {
private _zeroAngle = "ace_advanced_ballistics" callExtension format ["calcZeroAB:%1:%2:%3:%4:%5:%6:%7:%8:%9", _zeroRange, _muzzleVelocity, _boreHeight, _temperature, _barometricPressure, _relativeHumidity, _bc, _dragModel, _atmosphereModel];
(parseNumber _zeroAngle)
parseNumber (("ace" callExtension ["ballistics:zero_advanced", [
_zeroRange,
_muzzleVelocity,
_airFriction,
_boreHeight,
_temperature,
_barometricPressure,
_relativeHumidity,
_bc,
_dragModel,
_atmosphereModel
]]) select 0)
};
GVAR(workingMemory) set [2, _zeroRange];

View File

@ -50,8 +50,8 @@ if (_unit == _attachToVehicle) then { //Self Attachment
} else {
GVAR(placeAction) = PLACE_WAITING;
[_unit, "forceWalk", "ACE_Attach", true] call EFUNC(common,statusEffect_set);
[_unit, "blockThrow", "ACE_Attach", true] call EFUNC(common,statusEffect_set);
[_unit, "forceWalk", QUOTE(ADDON), true] call EFUNC(common,statusEffect_set);
[_unit, "blockThrow", QUOTE(ADDON), true] call EFUNC(common,statusEffect_set);
[{[localize LSTRING(PlaceAction), ""] call EFUNC(interaction,showMouseHint)}, []] call CBA_fnc_execNextFrame;
_unit setVariable [QGVAR(placeActionEH), [_unit, "DefaultAction", {true}, {GVAR(placeAction) = PLACE_APPROVE;}] call EFUNC(common,AddActionEventHandler)];
@ -88,8 +88,8 @@ if (_unit == _attachToVehicle) then { //Self Attachment
{!([_attachToVehicle, _unit, _itemClassname] call FUNC(canAttach))}) then {
[_idPFH] call CBA_fnc_removePerFrameHandler;
[_unit, "forceWalk", "ACE_Attach", false] call EFUNC(common,statusEffect_set);
[_unit, "blockThrow", "ACE_Attach", false] call EFUNC(common,statusEffect_set);
[_unit, "forceWalk", QUOTE(ADDON), false] call EFUNC(common,statusEffect_set);
[_unit, "blockThrow", QUOTE(ADDON), false] call EFUNC(common,statusEffect_set);
[] call EFUNC(interaction,hideMouseHint);
[_unit, "DefaultAction", (_unit getVariable [QGVAR(placeActionEH), -1])] call EFUNC(common,removeActionEventHandler);
_unit removeAction _actionID;

View File

@ -28,7 +28,7 @@ if (_attachedList isEqualTo []) exitWith {};
TRACE_2("detaching",_xObject,_deadUnit);
detach _xObject;
//If it's a vehicle, also delete the attached
if (!(_deadUnit isKindOf "CAManBase")) then {
if !(_deadUnit isKindOf "CAManBase") then {
_xObject setPos ((getPos _deadUnit) vectorAdd [0, 0, -1000]);
[{deleteVehicle (_this select 0)}, [_xObject], 2] call CBA_fnc_waitAndExecute;
};

View File

@ -1505,7 +1505,7 @@
<Portuguese>Carregador de 10 cartuchos 9.3 mm traçantes IR-DIM</Portuguese>
<Hungarian>9,3 mm 10-lövedékes infravörös nyomkövető tár</Hungarian>
<Japanese>9.3mm 10Rnd IR-DIM トレーサー マガジン</Japanese>
<Korean>10발들이 9.3mm IR-DIM 예광탄 탄창</Korean>
<Korean>10발 들이 9.3mm IR-DIM 예광탄 탄창</Korean>
<Chinese>9.3毫米 10發 低視度紅外線曳光彈 彈匣</Chinese>
<Chinesesimp>9.3 mm 10发 弹匣(红外曳光)</Chinesesimp>
<Turkish>9.3 mm 10Rnd Tracer IR-DIM Mag</Turkish>
@ -1608,7 +1608,7 @@
<Portuguese>Cinto de munição traçante 9.3 mm IR-DIM com 150 cartuchos</Portuguese>
<Hungarian>9,3 mm 150-lövedékes infravörös nyomkövető heveder</Hungarian>
<Japanese>9.3mm 150Rnd IR-DIM トレーサー ベルト</Japanese>
<Korean>150발들이 9.3mm IR-DIM 예광탄 벨트</Korean>
<Korean>150발 들이 9.3mm IR-DIM 예광탄 벨트</Korean>
<Chinese>9.3毫米 150發 低視度紅外線曳光彈 彈鏈</Chinese>
<Chinesesimp>9.3 mm 150发 弹链(红外曳光)</Chinesesimp>
<Turkish>9.3 mm 150Rnd Tracer IR-DIM Belt</Turkish>
@ -1659,7 +1659,7 @@
<Portuguese>Cinto de munição 9.3 mm AP com 150 cartuchos</Portuguese>
<Hungarian>9,3 mm 150-lövedékes páncéltörő heveder</Hungarian>
<Japanese>9.3mm 150Rnd 徹甲弾 ベルト</Japanese>
<Korean>150발들이 9.3mm 철갑탄 벨트</Korean>
<Korean>150발 들이 9.3mm 철갑탄 벨트</Korean>
<Chinese>9.3毫米 150發 穿甲彈 彈鏈</Chinese>
<Chinesesimp>9.3 mm 150发 弹链(穿甲)</Chinesesimp>
<Turkish>9.3 mm 150Rnd AP Belt</Turkish>
@ -1710,7 +1710,7 @@
<Portuguese>Carregador de 16 cartuchos 9x19 mm</Portuguese>
<Hungarian>9x19 mm 16-lövedékes tár</Hungarian>
<Japanese>9x19 mm 16Rnd マガジン</Japanese>
<Korean>17발들이 9x19mm 탄창</Korean>
<Korean>16발 들이 9x19mm 탄창</Korean>
<Chinese>9x19毫米 16發 彈匣</Chinese>
<Chinesesimp>9x19 mm 16发 弹匣</Chinesesimp>
<Turkish>9x19 mm 16Rnd Mag</Turkish>
@ -2016,7 +2016,7 @@
<Portuguese>Carregador 5.56 mm com 30 cartuchos (Mk318)</Portuguese>
<Hungarian>5,56 mm 30-lövedékes tár (Mk318)</Hungarian>
<Japanese>5.56mm 30Rnd マガジン (Mk318)</Japanese>
<Korean>30발들이 5.56mm 탄창 (Mk.318)</Korean>
<Korean>30발 들이 5.56mm 탄창 (Mk.318)</Korean>
<Chinese>5.56毫米 30發 彈匣 (Mk318 特戰專用彈)</Chinese>
<Chinesesimp>5.56 mm 30发 弹匣Mk318</Chinesesimp>
<Turkish>5.56 mm 30Rnd Mag (Mk318)</Turkish>
@ -2322,7 +2322,7 @@
<Portuguese>Carregador 7.62 mm com 10 cartuchos (Mk319 Mod 0)</Portuguese>
<Hungarian>7,62 mm 10-lövedékes tár (Mk319 Mod 0)</Hungarian>
<Japanese>7.62mm 10Rnd マガジン (Mk319 Mod 0)</Japanese>
<Korean>10발들이 7.62mm 탄창 (Mk.319 Mod 0)</Korean>
<Korean>10발 들이 7.62mm 탄창 (Mk.319 Mod 0)</Korean>
<Chinese>7.62毫米 10發 彈匣 (Mk319 Mod 0 特戰專用彈)</Chinese>
<Chinesesimp>7.62 mm 10发 弹匣Mk319 Mod 0</Chinesesimp>
<Turkish>7.62 mm 10Rnd Mag (Mk319 Mod 0)</Turkish>
@ -3344,7 +3344,7 @@
<Portuguese>Carregador 12.7x99 mm (AMAX) com 5 cartuchos </Portuguese>
<Hungarian>12,7x99 mm 5-lövedékes tár (AMAX)</Hungarian>
<Japanese>12.7x99mm 5Rnd マガジン (AMAX)</Japanese>
<Korean>5발들이 12.7x99mm 탄창 (AMAX)</Korean>
<Korean>5발 들이 12.7x99mm 탄창 (AMAX)</Korean>
<Chinese>12.7x99毫米 5發 彈匣 (AMAX 比賽專用彈)</Chinese>
<Chinesesimp>12.7x99 mm 5发 弹匣AMAX</Chinesesimp>
<Turkish>12.7x99 mm 5Rnd Şarjör (AMAX)</Turkish>
@ -3378,7 +3378,7 @@
<Portuguese>Carregador 12.7x99 mm (AMAX) com 10 cartuchos </Portuguese>
<Hungarian>12,7x99 mm 10-lövedékes tár (AMAX)</Hungarian>
<Japanese>12.7x99mm 10Rnd マガジン (AMAX)</Japanese>
<Korean>10발들이 12.7x99mm 탄창 (AMAX)</Korean>
<Korean>10발 들이 12.7x99mm 탄창 (AMAX)</Korean>
<Chinese>12.7x99毫米 10發 彈匣 (AMAX 比賽專用彈)</Chinese>
<Chinesesimp>12.7x99 mm 10发 弹匣AMAX</Chinesesimp>
<Turkish>12.7x99 mm 10Rnd Şarjör (AMAX)</Turkish>

View File

@ -24,7 +24,7 @@ if (isServer) then {
}];
};
["unit", FUNC(handlePlayerChanged)] call CBA_fnc_addPlayerEventHandler;
["unit", LINKFUNC(handlePlayerChanged)] call CBA_fnc_addPlayerEventHandler;
[QGVAR(moveInCaptive), LINKFUNC(vehicleCaptiveMoveIn)] call CBA_fnc_addEventHandler;
[QGVAR(moveOutCaptive), LINKFUNC(vehicleCaptiveMoveOut)] call CBA_fnc_addEventHandler;

View File

@ -21,7 +21,6 @@ params ["_unit", "_target"];
(_target getVariable [QGVAR(isHandcuffed), false]) &&
{isNull (attachedTo _target)} &&
{alive _target} &&
{!(_target getVariable ["ACE_isUnconscious", false])} &&
{_target call EFUNC(common,isAwake)} &&
{(vehicle _unit) == _unit} &&
{(vehicle _target) == _target}

View File

@ -20,7 +20,7 @@
params ["_unit", "_target", "_vehicle"];
// Don't show "Load Captive" if unit is unconscious (already has "Load Patient")
if (_target getVariable ["ACE_isUnconscious", false]) exitWith {false};
if !(_target call EFUNC(common,isAwake)) exitWith {false};
if ((isNull _target) && {_unit getVariable [QGVAR(isEscorting), false]}) then {
//Looking at a vehicle while escorting, get target from attached objects:

View File

@ -39,12 +39,12 @@ if (_state) then {
_args params ["_unit", "_target", "_actionID"];
if (_unit getVariable [QGVAR(isEscorting), false]) then {
if (!alive _target || {!alive _unit} || {!canStand _target} || {!canStand _unit} || {_target getVariable ["ACE_isUnconscious", false]} || {_unit getVariable ["ACE_isUnconscious", false]} || {!isNull (attachedTo _unit)}) then {
if (!canStand _target || {!canStand _unit} || {!(_target call EFUNC(common,isAwake))} || {!(_unit call EFUNC(common,isAwake))} || {!isNull (attachedTo _unit)}) then {
_unit setVariable [QGVAR(isEscorting), false, true];
};
};
if (!(_unit getVariable [QGVAR(isEscorting), false])) then {
if !(_unit getVariable [QGVAR(isEscorting), false]) then {
[(_this select 1)] call CBA_fnc_removePerFrameHandler;
[objNull, _target, false] call EFUNC(common,claim);
detach _target;

View File

@ -19,7 +19,7 @@
params ["_unit", "_newAnimation"];
TRACE_2("AnimChanged",_unit,_newAnimation);
if (_unit == (vehicle _unit)) then {
if ((_newAnimation != "ACE_AmovPercMstpSsurWnonDnon") && {!(_unit getVariable ["ACE_isUnconscious", false])}) then {
if ((_newAnimation != "ACE_AmovPercMstpSsurWnonDnon") && {_unit call EFUNC(common,isAwake)}) then {
TRACE_1("Handcuff animation interrupted",_newAnimation);
[_unit, "ACE_AmovPercMstpScapWnonDnon", 1] call EFUNC(common,doAnimation);
};

View File

@ -19,7 +19,7 @@
params ["_unit", "_newAnimation"];
TRACE_2("AnimChanged",_unit,_newAnimation);
if ((_newAnimation != "ACE_AmovPercMstpSsurWnonDnon") && {!(_unit getVariable ["ACE_isUnconscious", false])}) then {
if ((_newAnimation != "ACE_AmovPercMstpSsurWnonDnon") && {_unit call EFUNC(common,isAwake)}) then {
TRACE_1("Surrender animation interrupted",_newAnimation);
[_unit, "ACE_AmovPercMstpSsurWnonDnon", 1] call EFUNC(common,doAnimation);
};

View File

@ -40,12 +40,12 @@ if (_respawn > 3) then {
if (_unit getVariable [QGVAR(isHandcuffed), false]) then {
[_unit, false] call FUNC(setHandcuffed);
};
[_unit, "setCaptive", QGVAR(Handcuffed), false] call EFUNC(common,statusEffect_set);
[_unit, "setCaptive", QGVAR(handcuffed), false] call EFUNC(common,statusEffect_set);
if (_unit getVariable [QGVAR(isSurrendering), false]) then {
[_unit, false] call FUNC(setSurrendered);
};
[_unit, "setCaptive", QGVAR(Surrendered), false] call EFUNC(common,statusEffect_set);
[_unit, "setCaptive", QGVAR(surrendered), false] call EFUNC(common,statusEffect_set);
if (_unit getVariable [QGVAR(isEscorting), false]) then {
_unit setVariable [QGVAR(isEscorting), false, true];

View File

@ -41,8 +41,8 @@ if ((_unit getVariable [QGVAR(isHandcuffed), false]) isEqualTo _state) exitWith
if (_state) then {
_unit setVariable [QGVAR(isHandcuffed), true, true];
[_unit, "setCaptive", QGVAR(Handcuffed), true] call EFUNC(common,statusEffect_set);
[_unit, "blockRadio", QGVAR(Handcuffed), true] call EFUNC(common,statusEffect_set);
[_unit, "setCaptive", QGVAR(handcuffed), true] call EFUNC(common,statusEffect_set);
[_unit, "blockRadio", QGVAR(handcuffed), true] call EFUNC(common,statusEffect_set);
if (_unit getVariable [QGVAR(isSurrendering), false]) then { //If surrendering, stop
[_unit, false] call FUNC(setSurrendered);
@ -58,7 +58,7 @@ if (_state) then {
// fix anim on mission start (should work on dedicated servers)
[{
params ["_unit"];
if (!(_unit getVariable [QGVAR(isHandcuffed), false])) exitWith {};
if !(_unit getVariable [QGVAR(isHandcuffed), false]) exitWith {};
if ((vehicle _unit) == _unit) then {
[_unit] call EFUNC(common,fixLoweredRifleAnimation);
@ -82,8 +82,8 @@ if (_state) then {
}, [_unit], 0.01] call CBA_fnc_waitAndExecute;
} else {
_unit setVariable [QGVAR(isHandcuffed), false, true];
[_unit, "setCaptive", QGVAR(Handcuffed), false] call EFUNC(common,statusEffect_set);
[_unit, "blockRadio", QGVAR(Handcuffed), false] call EFUNC(common,statusEffect_set);
[_unit, "setCaptive", QGVAR(handcuffed), false] call EFUNC(common,statusEffect_set);
[_unit, "blockRadio", QGVAR(handcuffed), false] call EFUNC(common,statusEffect_set);
//remove AnimChanged EH
private _animChangedEHID = _unit getVariable [QGVAR(handcuffAnimEHID), -1];
@ -91,7 +91,7 @@ if (_state) then {
_unit removeEventHandler ["AnimChanged", _animChangedEHID];
_unit setVariable [QGVAR(handcuffAnimEHID), -1];
if (((vehicle _unit) == _unit) && {!(_unit getVariable ["ACE_isUnconscious", false])}) then {
if (((vehicle _unit) == _unit) && {_unit call EFUNC(common,isAwake)}) then {
//Break out of hands up animation loop
[_unit, "ACE_AmovPercMstpScapWnonDnon_AmovPercMstpSnonWnonDnon", 2] call EFUNC(common,doAnimation);
};

View File

@ -44,8 +44,8 @@ if (_state) then {
_unit setVariable [QGVAR(isSurrendering), true, true];
[_unit, "setCaptive", QGVAR(Surrendered), true] call EFUNC(common,statusEffect_set);
[_unit, "blockRadio", QGVAR(Surrendered), true] call EFUNC(common,statusEffect_set);
[_unit, "setCaptive", QGVAR(surrendered), true] call EFUNC(common,statusEffect_set);
[_unit, "blockRadio", QGVAR(surrendered), true] call EFUNC(common,statusEffect_set);
if (_unit == ACE_player) then {
["captive", [false, false, false, false, false, false, false, false, false, true]] call EFUNC(common,showHud);
@ -71,8 +71,8 @@ if (_state) then {
}, [_unit], 0.01] call CBA_fnc_waitAndExecute;
} else {
_unit setVariable [QGVAR(isSurrendering), false, true];
[_unit, "setCaptive", QGVAR(Surrendered), false] call EFUNC(common,statusEffect_set);
[_unit, "blockRadio", QGVAR(Surrendered), false] call EFUNC(common,statusEffect_set);
[_unit, "setCaptive", QGVAR(surrendered), false] call EFUNC(common,statusEffect_set);
[_unit, "blockRadio", QGVAR(surrendered), false] call EFUNC(common,statusEffect_set);
//remove AnimChanged EH
private _animChangedEHID = _unit getVariable [QGVAR(surrenderAnimEHID), -1];
@ -81,13 +81,12 @@ if (_state) then {
if (_unit == ACE_player) then {
//only re-enable HUD if not handcuffed
if (!(_unit getVariable [QGVAR(isHandcuffed), false])) then {
if !(_unit getVariable [QGVAR(isHandcuffed), false]) then {
["captive", []] call EFUNC(common,showHud); //same as showHud true;
};
};
if (!alive _unit) exitWith {};
if (_unit getVariable ["ACE_isUnconscious", false]) exitWith {}; //don't touch animations if unconscious
if !(_unit call EFUNC(common,isAwake)) exitWith {}; //don't touch animations if unconscious
//if we are in "hands up" animationState, crack it now
if (((vehicle _unit) == _unit) && {(animationState _unit) == "ACE_AmovPercMstpSsurWnonDnon"}) then {
@ -99,7 +98,7 @@ if (_state) then {
params ["_args", "_pfID"];
_args params ["_unit", "_maxTime"];
//If waited long enough or they re-surrendered or they are unconscious, exit loop
if ((CBA_missionTime > _maxTime) || {_unit getVariable [QGVAR(isSurrendering), false]} || {_unit getVariable ["ACE_isUnconscious", false]}) exitWith {
if ((CBA_missionTime > _maxTime) || {_unit getVariable [QGVAR(isSurrendering), false]} || {!(_unit call EFUNC(common,isAwake))}) exitWith {
[_pfID] call CBA_fnc_removePerFrameHandler;
};
//Only break animation if they are actualy the "hands up" animation (because we are using switchmove there won't be an transition)

View File

@ -158,6 +158,7 @@
<Japanese>目隠しを外す</Japanese>
<Russian>Снять повязку с глаз</Russian>
<Spanish>Quitar vendas de los ojos</Spanish>
<Portuguese>Remover a venda</Portuguese>
</Key>
<Key ID="STR_ACE_Captives_CableTie">
<English>Cable Tie</English>

Some files were not shown because too many files have changed in this diff Show More