Merge branch 'master' into pr/9580

This commit is contained in:
johnb432 2024-07-23 16:03:07 +02:00
commit e25cb3b53d
1711 changed files with 28012 additions and 14014 deletions

View File

@ -23,14 +23,14 @@ All good? Then proceed and fill out the items below.
**Mods (complete and add to the following information):**
- **Arma 3:** `x.xx` [e.g. 1.00 stable, rc, dev]
- **CBA:** `3.x.x` [e.g. 3.0.0 stable, commit hash]
- **ACE3:** `3.x.x` [eg. 3.0.0 stable, commit hash]
- **ACE3:** `3.x.x` [e.g. 3.0.0 stable, commit hash]
<!-- Make sure to reproduce the issue with only CBA and ACE3 on a newly created mission! -->
**Description:**
A clear and concise description of what the bug is.
**Steps to reproduce:**
_Follow [https://ace3.acemod.org/img/wiki/user/issue_flowchart.webp](this flowchart)!_
_Follow [this flowchart](https://ace3.acemod.org/img/wiki/user/issue_flowchart.webp)!_
1. _Go to ..._
2. _Click ..._

View File

@ -5,14 +5,16 @@ branches:
name-template: 'Version $NEXT_PATCH_VERSION'
tag-template: 'v$NEXT_PATCH_VERSION'
sort-by: title # sort alphabetically
sort-direction: ascending
categories:
- title: '**ADDED:**'
labels:
- 'kind/feature request'
- 'kind/added feature'
- 'kind/feature'
- title: '**FIXED:**'
labels:
- 'kind/bug fix'
- 'kind/bug-fix'
- title: '**IMPROVED:**'
labels:
- 'kind/enhancement'
@ -20,13 +22,16 @@ categories:
- title: '**CHANGED:**'
labels:
- 'kind/cleanup'
- 'area/compatibility'
- 'kind/change'
- title: '**SETTINGS:**'
labels:
- 'kind/setting'
- title: '**TRANSLATIONS:**'
labels:
- 'area/translations'
- 'kind/translation'
exclude-labels:
- 'ignore changelog'
- 'ignore-changelog'
- 'dependencies'
change-template: '- $TITLE (#$NUMBER)'
@ -39,5 +44,5 @@ template: |
replacers:
# Category titles
- search: '/\#\# (\*\*(ADDED|FIXED|IMPROVED|CHANGED|TRANSLATIONS):\*\*)/g'
- search: '/\#\# (\*\*(ADDED|FIXED|IMPROVED|CHANGED|SETTINGS|TRANSLATIONS):\*\*)/g'
replace: '$1'

View File

@ -48,7 +48,7 @@ jobs:
- name: Rename build folder
run: mv .hemttout/build .hemttout/@ace
- name: Upload Artifact
uses: actions/upload-artifact@v3
uses: actions/upload-artifact@v4
with:
name: ace3-${{ github.sha }}-nobin
path: .hemttout/@*

View File

@ -47,7 +47,7 @@ jobs:
destination: docs/_site/
- name: Upload Artifact
uses: actions/upload-artifact@v3
uses: actions/upload-artifact@v4
with:
name: jekyll-site
path: docs/_site/
@ -63,7 +63,7 @@ jobs:
steps:
- name: Download Artifacts
uses: actions/download-artifact@v3
uses: actions/download-artifact@v4
with:
name: jekyll-site
path: _site/

View File

@ -23,7 +23,7 @@ jobs:
cd build
cmake .. && cmake --build .
- name: Upload Artifact
uses: actions/upload-artifact@v3
uses: actions/upload-artifact@v4
with:
name: ace3_extensions-${{ matrix.os }}-debug
path: extensions/build

View File

@ -38,7 +38,7 @@ jobs:
- name: Rename build folder
run: mv .hemttout/build .hemttout/@ace
- name: Upload Artifact
uses: actions/upload-artifact@v3
uses: actions/upload-artifact@v4
with:
name: ace3-${{ github.sha }}
path: .hemttout/@*

View File

@ -25,7 +25,7 @@ jobs:
Invoke-WebRequest "$env:RHSUSF_URL" -OutFile rhsusf.zip
$files = @("arma3.zip", "rhsafrf.zip", "rhsgref.zip", "rhssaf.zip", "rhsusf.zip")
ForEach ($file in $files) {
Extract-7Zip -Path $file -DestinationPath .
Expand-7ZipArchive -Path $file -DestinationPath .
Remove-Item $file
}
env:
@ -67,18 +67,19 @@ jobs:
xcopy /e /h /q z\ace\tools\pDummies\gm gm\
xcopy /e /h /q z\ace\tools\pDummies\vn vn\
xcopy /e /h /q z\ace\tools\pDummies\WW2 WW2\
xcopy /e /h /q z\ace\tools\pDummies\CUP CUP\
- name: Build
run: py P:\z\ace\tools\make.py ci
env:
PYTHONUNBUFFERED: 1
- name: Archive logs
uses: actions/upload-artifact@v3
uses: actions/upload-artifact@v4
if: ${{ always() }}
with:
name: logs
path: temp/*.log
- name: Archive @ace
uses: actions/upload-artifact@v3
uses: actions/upload-artifact@v4
with:
name: '@ace3-${{ github.sha }}'
path: z\ace\release\@ace

View File

@ -11,6 +11,6 @@ jobs:
steps:
- name: Release Drafter
if: github.repository == 'acemod/ACE3'
uses: release-drafter/release-drafter@v5
uses: release-drafter/release-drafter@v6
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}

1
.gitignore vendored
View File

@ -20,3 +20,4 @@ CHANGELOG.md
sqfvm.exe
ArmaScriptCompiler.exe
*.sqfc
!extras/**/*.zip

View File

@ -18,22 +18,17 @@ include = [
[version]
git_hash = 0
# Unused in HEMTT v1.11 or higher, kept for backwards compatibility
[asc]
enabled = true
exclude = [
"/initsettings.sqf",
"/initkeybinds.sqf",
"/xeh_prep.sqf",
".inc.sqf",
"/dev/",
"medical_ai/statemachine.sqf",
"common/functions/fnc_dummy.sqf",
"zeus/functions/fnc_zeusAttributes.sqf",
]
[hemtt.config]
preset = "Hemtt"
[hemtt.launch]
[hemtt.launch.default]
workshop = [
"450814997", # CBA_A3
]

View File

@ -6,6 +6,7 @@
# request, preferably including an email address.
# CORE TEAM
BaerMitUmlaut
Brett Mayson
bux578 <github@jonathandavid.de>
commy2
@ -25,6 +26,7 @@ Kieran
kymckay
mharis001 <mhariszakar@gmail.com>
MikeMF
MiszczuZPolski
NouberNou
PabstMirror <pabstmirror@gmail.com>
Ruthberg <ulteq@web.de>
@ -56,7 +58,6 @@ Arcanum417 <lubos.len@gmail.com>
Arkhir <wonsz666@gmail.com >
ARV187 aka Spark23
Asgar Serran <piechottaf@web.de>
BaerMitUmlaut
Bamse <bamsis@gmail.com>
Barman75
Bla1337
@ -126,6 +127,7 @@ Keithen <Keithen.Neu@gmail.com>
Kllrt <kllrtik@gmail.com>
KokaKolaA3
Krzyciu
LAxemann
legman <juicemelon@msn.com>
Legolasindar "Viper" <legolasindar@gmail.com>
licht-im-Norden87 <lichtimnorden87@gmail.com>
@ -190,3 +192,4 @@ YetheSamartaka
xrufix
Zakant <Zakant@gmx.de>
zGuba
Zman6258

View File

@ -5,7 +5,7 @@
<p align="center">
<a href="https://github.com/acemod/ACE3/releases/latest">
<img src="https://img.shields.io/badge/Version-3.16.1-blue.svg?style=flat-square" alt="ACE3 Version">
<img src="https://img.shields.io/github/release/acemod/ACE3.svg?style=flat-square&label=Version" alt="ACE3 Version">
</a>
<a href="https://github.com/acemod/ACE3/issues">
<img src="https://img.shields.io/github/issues-raw/acemod/ACE3.svg?style=flat-square&label=Issues" alt="ACE3 Issues">

View File

@ -1,7 +1,5 @@
#include "script_component.hpp"
#include "initKeybinds.sqf"
GVAR(currentbulletID) = -1;
GVAR(Protractor) = false;
@ -11,6 +9,8 @@ GVAR(currentGrid) = 0;
if (!hasInterface) exitWith {};
#include "initKeybinds.inc.sqf"
["CBA_settingsInitialized", {
//If not enabled, dont't add PFEH
if (!GVAR(enabled)) exitWith {};
@ -19,11 +19,11 @@ if (!hasInterface) exitWith {};
[] call FUNC(initializeTerrainExtension);
// Register fire event handler
["ace_firedPlayer", DFUNC(handleFired)] call CBA_fnc_addEventHandler;
["ace_firedPlayerNonLocal", DFUNC(handleFired)] call CBA_fnc_addEventHandler;
["ace_firedPlayer", LINKFUNC(handleFired)] call CBA_fnc_addEventHandler;
["ace_firedPlayerNonLocal", LINKFUNC(handleFired)] call CBA_fnc_addEventHandler;
// Register Perframe Handler
[FUNC(handleFirePFH), GVAR(simulationInterval)] call CBA_fnc_addPerFrameHandler;
[LINKFUNC(handleFirePFH), GVAR(simulationInterval)] call CBA_fnc_addPerFrameHandler;
//Add warnings for missing compat PBOs (only if AB is on)
{

View File

@ -6,6 +6,6 @@ PREP_RECOMPILE_START;
#include "XEH_PREP.hpp"
PREP_RECOMPILE_END;
#include "initSettings.sqf"
#include "initSettings.inc.sqf"
ADDON = true;

View File

@ -17,9 +17,9 @@
*/
//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);
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 {};

View File

@ -22,7 +22,7 @@ private _initStartTime = diag_tickTime;
private _mapSize = worldSize;
if (("ace_advanced_ballistics" callExtension format["init:%1:%2", worldName, _mapSize]) == "Terrain already initialized") exitWith {
INFO_1("Terrain already initialized [world: %1]", worldName);
INFO_1("Terrain already initialized [world: %1]",worldName);
#ifdef DEBUG_MODE_FULL
systemChat "AdvancedBallistics: Terrain already initialized";
#endif
@ -33,14 +33,14 @@ private _gridCells = _mapGrids * _mapGrids;
GVAR(currentGrid) = 0;
INFO_2("Starting Terrain Extension [cells: %1] [world: %2]", _gridCells, worldName);
INFO_2("Starting Terrain Extension [cells: %1] [world: %2]",_gridCells,worldName);
[{
params ["_args","_idPFH"];
_args params ["_mapGrids", "_gridCells", "_initStartTime"];
if (GVAR(currentGrid) >= _gridCells) exitWith {
INFO_2("Finished terrain initialization in %1 seconds [world: %2]", (diag_tickTime - _initStartTime) toFixed 2, worldName);
INFO_2("Finished terrain initialization in %1 seconds [world: %2]",(diag_tickTime - _initStartTime) toFixed 2,worldName);
#ifdef DEBUG_MODE_FULL
systemChat format["AdvancedBallistics: Finished terrain initialization in %1 seconds", (diag_tickTime - _initStartTime) toFixed 2];
#endif
@ -53,7 +53,7 @@ INFO_2("Starting Terrain Extension [cells: %1] [world: %2]", _gridCells, worldNa
private _gridCenter = [_x + 25, _y + 25];
private _gridHeight = round(getTerrainHeightASL _gridCenter);
private _gridNumObjects = count (_gridCenter nearObjects ["Building", 50]);
private _gridSurfaceIsWater = if (surfaceIsWater _gridCenter) then {1} else {0};
private _gridSurfaceIsWater = parseNumber (surfaceIsWater _gridCenter);
"ace_advanced_ballistics" callExtension format["set:%1:%2:%3", _gridHeight, _gridNumObjects, _gridSurfaceIsWater];
GVAR(currentGrid) = GVAR(currentGrid) + 1;
if (GVAR(currentGrid) >= _gridCells) exitWith {};

View File

@ -40,10 +40,13 @@ 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");
if (_ballisticCoefficients isEqualTo []) then {
_ballisticCoefficients = [0.5];
};
private _velocityBoundaries = getArray(_ammoConfig >> "ACE_velocityBoundaries");
private _atmosphereModel = getText(_ammoConfig >> "ACE_standardAtmosphere");
if (_atmosphereModel isEqualTo "") then {
@ -66,7 +69,7 @@ if ((_typicalSpeed > 0) && {_typicalSpeed < 360}) then {
if (_inheritedBarrelConfig || _inheritedTempConfig) then {
private _parentConfig = inheritsFrom _ammoConfig;
private _parentSpeed = getNumber (_parentConfig >> "typicalSpeed");
WARNING_4("Subsonic Ammo %1 (%2 m/s) missing `ACE_muzzleVelocities` or `ACE_ammoTempMuzzleVelocityShifts` configs, attempting to use parent %3 (%4m/s)",_this,_typicalSpeed,configName _parentConfig, _parentSpeed);
WARNING_4("Subsonic Ammo %1 (%2 m/s) missing `ACE_muzzleVelocities` or `ACE_ammoTempMuzzleVelocityShifts` configs, attempting to use parent %3 (%4m/s)",_this,_typicalSpeed,configName _parentConfig,_parentSpeed);
if (_parentSpeed <= 0) exitWith {//Handle weird or null parent
_muzzleVelocityTable = [];
_ammoTempMuzzleVelocityShifts = [];

View File

@ -21,7 +21,7 @@
private _weaponConfig = (configFile >> "CfgWeapons" >> _this);
private _barrelTwist = 0 max getNumber(_weaponConfig >> "ACE_barrelTwist");
private _twistDirection = [0, 1] select (_barrelTwist != 0);
private _twistDirection = parseNumber (_barrelTwist != 0);
if (isNumber (_weaponConfig >> "ACE_twistDirection")) then {
_twistDirection = getNumber (_weaponConfig >> "ACE_twistDirection");
if !(_twistDirection in [-1, 0, 1]) then {

View File

@ -4,7 +4,7 @@
<Key ID="STR_ACE_Advanced_Ballistics_WindInfoKey">
<English>Show Wind Info</English>
<Polish>Pokaż inf. o wietrze</Polish>
<Italian>Mostra indicazioni del vento</Italian>
<Italian>Mostra indicazioni vento</Italian>
<Russian>Показать информацию о ветре</Russian>
<French>Afficher les infos sur le vent</French>
<Spanish>Mostrar información del viento</Spanish>
@ -46,7 +46,7 @@
<Hungarian>Fejlett ballisztika</Hungarian>
<Russian>Продвинутая баллистика</Russian>
<Italian>Balistica Avanzata</Italian>
<Japanese>アドバンスド バリスティックス</Japanese>
<Japanese>アドバンスド弾道計算</Japanese>
<Korean>고급 탄도학</Korean>
<Chinese>先進彈道系統</Chinese>
<Chinesesimp>进阶弹道系统</Chinesesimp>
@ -63,7 +63,7 @@
<Hungarian>Fejlett ballisztika</Hungarian>
<Russian>Продвинутая баллистика</Russian>
<Italian>Balistica Avanzata</Italian>
<Japanese>アドバンスド バリスティックスを有効化</Japanese>
<Japanese>アドバンスド弾道計算を有効化</Japanese>
<Korean>고급 탄도학</Korean>
<Chinese>先進彈道系統</Chinese>
<Chinesesimp>进阶弹道系统</Chinesesimp>
@ -80,7 +80,7 @@
<Hungarian>Engedélyezi a fejlett ballisztikát</Hungarian>
<Russian>Включает продвинутую баллистику</Russian>
<Italian>Abilita Balistica Avanzata</Italian>
<Japanese>アドバンスド バリスティックスを有効化します。</Japanese>
<Japanese>アドバンスド弾道計算は高度な弾道計算処理を有効化します。</Japanese>
<Korean>고급 탄도학을 적용합니다</Korean>
<Chinese>啟用先進彈道系統</Chinese>
<Chinesesimp>启用进阶弹道系统</Chinesesimp>
@ -106,8 +106,8 @@
<English>Simulates slight variations in muzzle velocity between each shot</English>
<Spanish>Simula variaciones ligeras de velocidad entre cada disparo</Spanish>
<German>Simuliert leichte Variationen der Mündungsgeschwindigkeit zwischen jedem Schuss.</German>
<Japanese>発射毎の銃口初速の変動をシミュレートします。</Japanese>
<Italian>Simula lievi variazioni della velocità della volata tra un colpo e l'altro</Italian>
<Japanese>発射毎の銃口初速のわずかな変動をシミュレートします。</Japanese>
<Italian>Simula piccole variazioni della velocità alla volata tra ogni colpo.</Italian>
<Korean>각 사격 사이에 총구속도 변화를 시뮬레이션 합니다.</Korean>
<Chinese>模擬每發子彈的槍口初速都略有不同</Chinese>
<Chinesesimp>模拟每发子弹的枪口初速都略有不同</Chinesesimp>
@ -128,8 +128,8 @@
<French>Activer la simulation de la température</French>
<Hungarian>Lőszer-hő szimuláció engedélyezése</Hungarian>
<Russian>Симуляция температуры для боеприпасов</Russian>
<Italian>Abilita simulazione della temperatura delle munizioni</Italian>
<Japanese>弾薬温度シミュレーションを有効化</Japanese>
<Italian>Abilita simulazione temperatura munizioni</Italian>
<Japanese>弾薬温度シミュレーションを有効化</Japanese>
<Korean>탄약 온도 구현 적용</Korean>
<Chinese>啟用彈藥溫度模擬系統</Chinese>
<Chinesesimp>启用弹药温度模拟系统</Chinesesimp>
@ -145,7 +145,7 @@
<French>La vitesse initiale varie en fonction de la température des munitions.</French>
<Hungarian>A kezdősebesség a lőszer hőmérsékletétől függően változó</Hungarian>
<Russian>Начальная скорость пули зависит от температуры</Russian>
<Italian>La velocità dello sparo varia a seconda della temperatura delle munizioni</Italian>
<Italian>La velocità alla volata varia a seconda della temperatura delle munizioni.</Italian>
<Japanese>弾薬の温度により銃口初速を変動させます。</Japanese>
<Korean>탄약 온도에 비례해 총구 속도가 달라집니다</Korean>
<Chinese>子彈初速將隨彈藥溫度而有所變化</Chinese>
@ -162,8 +162,8 @@
<French>Activer la simulation de la longueur de canon</French>
<Hungarian>Csőhossz-szimuláció engedélyezése</Hungarian>
<Russian>Симуляция длины ствола</Russian>
<Italian>Abilita simulazione della lunghezza della canna</Italian>
<Japanese>銃身長シミュレーションを有効化</Japanese>
<Italian>Abilita simulazione lunghezza canna</Italian>
<Japanese>銃身長シミュレーションを有効化</Japanese>
<Korean>총열 길이 구현 적용</Korean>
<Chinese>啟用槍管長度模擬系統</Chinese>
<Chinesesimp>启用枪管长度模拟系统</Chinesesimp>
@ -179,7 +179,7 @@
<French>La vitesse initiale varie en fonction de la longueur du canon.</French>
<Hungarian>A kezdősebesség a cső hosszától függően változó</Hungarian>
<Russian>Начальная скорость пули зависит от длины ствола</Russian>
<Italian>La velocità di sparo varia a seconda della lunghezza della canna</Italian>
<Italian>La velocità alla volata varia a seconda della lunghezza della canna.</Italian>
<Japanese>銃身長により銃口初速を変動させます。</Japanese>
<Korean>총구 속도가 총열에 비례해 달라집니다</Korean>
<Chinese>子彈初速將隨槍管長度而有所變化</Chinese>
@ -196,7 +196,7 @@
<French>Activer l'effet de balle traçante</French>
<Hungarian>Nyomkövető-effekt engedélyezése</Hungarian>
<Russian>Следы пуль</Russian>
<Italian>Abilita effetto dei Proiettili Traccianti</Italian>
<Italian>Abilita effetto di traccia proiettile</Italian>
<Japanese>弾丸の軌跡エフェクトを有効化</Japanese>
<Korean>예광탄 효과 적용</Korean>
<Chinese>啟用曳光彈效果</Chinese>
@ -213,7 +213,7 @@
<French>Active un effet de balle traçante pour les munitions de gros calibre (seulement visible en utilisant une optique à fort grossissement).</French>
<Hungarian>Engedélyezi a nagy kaliberű lövedékek nyomának vizuális követését (csak nagy teljesítményű optikán keresztül látható)</Hungarian>
<Russian>Включает эффект следов пуль для больших калибров (видны только через мощную оптику)</Russian>
<Italian>Abilita effetto dei proiettili traccianti di alto calibro (visibile solo attraverso ottiche ad alto potenziale)</Italian>
<Italian>Abilita effetto di traccia lasciata da proiettili di alto calibro (visibile solo attraverso ottiche ad alto ingrandimento)</Italian>
<Japanese>大口径弾の軌跡エフェクトを有効化します。 (高性能光学機器を介してのみ見ることができます)</Japanese>
<Korean>대구경 탄환에 예광탄 효과를 적용합니다(오직 고성능 조준경 사용시에만 보입니다)</Korean>
<Chinese>啟用曳光彈效果給大口徑子彈 (只有透過高倍率光學瞄鏡才能看到)</Chinese>
@ -230,7 +230,7 @@
<French>Intervalle de simulation</French>
<Hungarian>Szimuláció intervalluma</Hungarian>
<Russian>Интервал симуляции</Russian>
<Italian>Intervallo Simulazione</Italian>
<Italian>Intervallo di Simulazione</Italian>
<Japanese>シミュレーション間隔</Japanese>
<Korean>구현 간격</Korean>
<Chinese>模擬間隔</Chinese>
@ -246,7 +246,7 @@
<French>Définit l'intervalle entre chacune des étapes de calcul.</French>
<Hungarian>Meghatározza a számítási lépések közötti időintervallumot</Hungarian>
<Russian>Определяет временной интервал между вычислениями</Russian>
<Italian>Definisce l'intervallo tra ogni step di calcolo</Italian>
<Italian>Definisce l'intervallo tra ogni passo del calcolo</Italian>
<Japanese>各シミュレーション毎の間隔を定義します。</Japanese>
<Korean>각 계산 단위의 간격을 정의합니다</Korean>
<Chinese>定義每個模擬計算之間的時間間隔</Chinese>
@ -263,7 +263,7 @@
<Hungarian>Szimuláció hatóköre</Hungarian>
<Russian>Радиус симуляции</Russian>
<Italian>Raggio Simulazione</Italian>
<Japanese>シミュレーションの範囲</Japanese>
<Japanese>シミュレーションの半径</Japanese>
<Korean>구현 범위</Korean>
<Chinese>模擬半徑</Chinese>
<Chinesesimp>模拟半径</Chinesesimp>
@ -279,8 +279,8 @@
<French>Définit le rayon autour du joueur (en mètres), dans lequel la balistique avancée est appliquée aux projectiles.</French>
<Hungarian>Meghatározza a játékos körüli hatókört (méterben), ahol a lövedékek fejlett ballisztikát használnak</Hungarian>
<Russian>Определяет радиус вокруг игрока (в метрах), в котором продвинутая баллистика применяется к снарядам</Russian>
<Italian>Definisce il raggio attorno al giocatore (in metri) per cui la Balistica Avanzata è applicata ai proiettili</Italian>
<Japanese>アドバンスド バリスティックスの適用半径範囲 (プレイヤー中心、メートル単位) を定義します。</Japanese>
<Italian>Definisce il raggio attorno al giocatore (in metri) entro il quale la Balistica Avanzata è applicata ai proiettili</Italian>
<Japanese>アドバンスド弾道計算の適用半径範囲 (プレイヤー中心、メートル単位) を定義します。</Japanese>
<Korean>플레이어 주위의 발사체를 고급 탄도학으로 정의하는 범위를 정합니다(미터)</Korean>
<Chinese>以玩家的半徑距離(公尺)定義先進彈道系統啟用範圍</Chinese>
<Chinesesimp>定义玩家周围的半径(米),在这个半径内,进阶弹道系统会被启用</Chinesesimp>
@ -296,8 +296,8 @@
<Hungarian>Ez a modul engedélyezi a fejlett ballisztikai szimulációt - a lövedékek röppályáját befolyásolni fogja a levegő hőmérséklete, légnyomás, páratartalom, gravitáció, a lövedék fajtája, valamint a fegyver, amiből kilőtték a lövedéket.</Hungarian>
<Russian>Этот модуль включает симуляцию продвинутой баллистики - при этом на траекторию полета снаряда влияют различные параметры, такие как температура воздуха, атмосферное давление, влажность, гравитация, тип боеприпаса и оружия, из которого произвели выстрел.</Russian>
<Spanish>Este módulo permite la simulación balística avanzada - es decir, la trayectoria de los proyectiles está influenciada por variables como la temperatura del aire, la presión atmosférica, la humedad, la gravedad, el tipo de municiones y el arma desde el que fue disparada.</Spanish>
<Italian>Questo modulo abilita la simulazione della Balistica Avanzata - cioè la traiettoria dei proiettili è influenzata da variabili come la temperatura dell'aria, pressione atmosferica, umidità, gravità, il tipo di munizione e l'arma da cui è sparata</Italian>
<Japanese>アドバンスド バリスティックスのシミュレーションを有効化します。 弾道は気温・気圧・湿度・重力・弾薬の種類・発射する武器などの変化による影響を受けるようになります。</Japanese>
<Italian>Questo modulo abilita la simulazione della Balistica Avanzata - essa comporta che la traiettoria dei proiettili è influenzata da variabili come la temperatura dell'aria, pressione atmosferica, umidità, gravità, il tipo di munizione e l'arma da cui è sparata.</Italian>
<Japanese>アドバンスド弾道計算のシミュレーションを有効化します。 弾道は気温・気圧・湿度・重力・弾薬の種類・発射する武器などの変化による影響を受けるようになります。</Japanese>
<Korean>이 모듈은 고급 탄도학을 적용시킵니다 - 이는 발사체의 궤적이 기온, 대기압, 습도, 중력, 탄환의 종류와 어느 무기에서 발사되는지에 따라 영향을 받습니다.</Korean>
<Chinese>該模塊實現先進的彈道仿真 - 這意味著子彈的軌跡是由空氣溫度、大氣壓力、濕度、重力、彈藥類型以及射擊的武器所影響</Chinese>
<Chinesesimp>该模块实现增强的弹道模拟—子弹的轨迹由空气温度、大气压力、湿度、重力、弹药类型和射击的武器等变量所影响</Chinesesimp>

View File

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

View File

@ -2,34 +2,9 @@
if (!hasInterface) exitWith {};
["baseline", {
private _fatigue = ACE_player getVariable [QGVAR(aimFatigue), 0];
switch (stance ACE_player) do {
case ("CROUCH"): {
(1.0 + _fatigue ^ 2 * 0.1)
};
case ("PRONE"): {
(1.0 + _fatigue ^ 2 * 2.0)
};
default {
(1.5 + _fatigue ^ 2 * 3.0)
};
};
}, QUOTE(ADDON)] call EFUNC(common,addSwayFactor);
["multiplier", {
switch (true) do {
case (isWeaponRested ACE_player): {
GVAR(swayFactor) * GVAR(restedSwayFactor)
};
case (isWeaponDeployed ACE_player): {
GVAR(swayFactor) * GVAR(deployedSwayFactor)
};
default {
GVAR(swayFactor)
};
};
}, QUOTE(ADDON)] call EFUNC(common,addSwayFactor);
#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;
@ -39,6 +14,21 @@ if (!hasInterface) exitWith {};
["CBA_settingsInitialized", {
if (!GVAR(enabled)) exitWith {};
["baseline", {
private _fatigue = ACE_player getVariable [QGVAR(aimFatigue), 0];
switch (stance ACE_player) do {
case ("CROUCH"): {
(1.0 + _fatigue ^ 2 * 0.1)
};
case ("PRONE"): {
(1.0 + _fatigue ^ 2 * 2.0)
};
default {
(1.5 + _fatigue ^ 2 * 3.0)
};
};
}, QUOTE(ADDON)] call EFUNC(common,addSwayFactor);
// - Post process effect ------------------------------------------------------
GVAR(ppeBlackout) = ppEffectCreate ["ColorCorrections", 4220];
GVAR(ppeBlackout) ppEffectEnable true;
@ -47,28 +37,26 @@ 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 (["ACE_Medical"] call EFUNC(common,isModLoaded)) 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 {
if (["ace_dragging"] call EFUNC(common,isModLoaded)) then {
[QEGVAR(dragging,isCarrying), {
[1, 3] select (_this getVariable [QEGVAR(dragging,isCarrying), false]);
}] call FUNC(addDutyFactor);
@ -76,7 +64,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

@ -6,7 +6,7 @@ PREP_RECOMPILE_START;
#include "XEH_PREP.hpp"
PREP_RECOMPILE_END;
#include "initSettings.sqf"
#include "initSettings.inc.sqf"
GVAR(staminaBarWidth) = 10 * (((safezoneW / safezoneH) min 1.2) / 40);
GVAR(dutyList) = createHashMap;

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,67 +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 _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;
private _aeReservePercentage = (GVAR(ae1Reserve) / AE1_MAXRESERVE + GVAR(ae2Reserve) / AE2_MAXRESERVE) / 2;
private _anReservePercentage = GVAR(anReserve) / AN_MAXRESERVE;
private _perceivedFatigue = 1 - (_anReservePercentage min _aeReservePercentage);
// Respiratory rate decrease
GVAR(respiratoryRate) = GVAR(respiratoryRate) * GVAR(respiratoryBufferDivisor);
[ACE_player, _perceivedFatigue, _currentSpeed, GVAR(anReserve) == 0] call FUNC(handleEffects);
// 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));
#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
[ACE_player, _perceivedFatigue, GVAR(anReserve) == 0, _fwdAngle, _sideAngle] call FUNC(handleEffects);
if (GVAR(enableStaminaBar)) 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

@ -4,12 +4,14 @@
[LSTRING(Enabled), LSTRING(Enabled_Description)],
LSTRING(DisplayName),
true,
true, {
1,
{
if (!_this) then {
private _staminaBarContainer = uiNamespace getVariable [QGVAR(staminaBarContainer), controlNull];
_staminaBarContainer ctrlSetFade 1;
_staminaBarContainer ctrlCommit 0;
};
[QGVAR(enabled), _this] call EFUNC(common,cbaSettings_settingChanged)
},
true // Needs mission restart
@ -21,7 +23,8 @@
[LSTRING(EnableStaminaBar), LSTRING(EnableStaminaBar_Description)],
LSTRING(DisplayName),
true,
true, {
1,
{
if (!_this) then {
private _staminaBarContainer = uiNamespace getVariable [QGVAR(staminaBarContainer), controlNull];
_staminaBarContainer ctrlSetFade 1;
@ -36,7 +39,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 +54,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 +69,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 +78,8 @@
"SLIDER",
[LSTRING(LoadFactor), LSTRING(LoadFactor_Description)],
LSTRING(DisplayName),
[0, 5, 1, 1],
true
[0, 5, 1, 2],
1
] call CBA_fnc_addSetting;
[
@ -77,33 +87,6 @@
"SLIDER",
[LSTRING(TerrainGradientFactor), LSTRING(TerrainGradientFactor_Description)],
LSTRING(DisplayName),
[0, 5, 1, 1],
true
] call CBA_fnc_addSetting;
[
QGVAR(swayFactor),
"SLIDER",
[LSTRING(SwayFactor), LSTRING(SwayFactor_Description)],
LSTRING(DisplayName),
[0, 5, 1, 1],
true
] call CBA_fnc_addSetting;
[
QGVAR(restedSwayFactor),
"SLIDER",
[LSTRING(RestedSwayFactor), LSTRING(RestedSwayFactor_Description)],
LSTRING(DisplayName),
[0, 5, 1, 2],
true
] call CBA_fnc_addSetting;
[
QGVAR(deployedSwayFactor),
"SLIDER",
[LSTRING(DeployedSwayFactor), LSTRING(DeployedSwayFactor_Description)],
LSTRING(DisplayName),
[0, 5, 1, 2],
true
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

@ -7,7 +7,7 @@
<German>ACE Erweiterte Ausdauer</German>
<Chinese>ACE 進階疲勞</Chinese>
<Chinesesimp>ACE 进阶体力</Chinesesimp>
<Japanese>ACE アドバンスド ファティーグ</Japanese>
<Japanese>ACE アドバンスド疲労</Japanese>
<Italian>ACE Fatica Avanzata</Italian>
<Korean>ACE 고급 피로도</Korean>
<French>ACE Fatigue avancée</French>
@ -41,10 +41,10 @@
<Polish>Wpływa na ogólną wydolność organizmu u wszystkich graczy bez ustawionego niestandardowego współczynnika. Więcej znaczy lepiej.</Polish>
<Korean>모든 성능이 임의로 설정된 값 없이 영향받습니다. 값이 클수록 더 나은 성능을 발휘합니다</Korean>
<French>Impacte la performance globale de tous les joueurs n'ayant pas de facteur personnalisé.\nPlus la valeur est élevée, plus le joueur est performant.</French>
<Italian>Influenza qualsiasi prestazione di tutti i giocatori smuniti di un fattore personalizzato. Maggiore significa migliore.</Italian>
<Italian>Influenza la prestazione generale di tutti i giocatori smuniti di un fattore personalizzato. Maggiore significa migliore.</Italian>
<Chinese>影響所有玩家的體力表現,值越高代表體力越好</Chinese>
<Chinesesimp>影响所有玩家的体力表现,值越高代表体力越好</Chinesesimp>
<Russian>Влияет на общую производительность игроков, у которых не задано персональное значение.</Russian>
<Russian>Влияет на общую производительность игроков, у которых не задано персональное значение. Чем выше, тем лучше.</Russian>
<Portuguese>Influencia na performance geral de todos os jogadores sem nenhum fator personalizado. Quanto maior, melhor.</Portuguese>
<Czech>Ovlivňuje celkový výkon všech hráčů bez vlastního faktoru. Vyšší znamená lépe.</Czech>
</Key>
@ -56,10 +56,10 @@
<Polish>Wpływa na ogólną wydolność tej jednostki. Więcej znaczy lepiej.</Polish>
<Korean>모든 성능이 이 단위로 영향을 받습니다. 값이 클수록 더 나은 성능을 발휘합니다</Korean>
<French>Impacte la performance globale de cette unité.\nPlus la valeur est élevée, plus l'unité est performante.</French>
<Italian>Influenza qualsiasi prestazione di questa unità. Maggiore significa migliore.</Italian>
<Italian>Influenza la prestazione personalizzata di questa unità. Maggiore significa migliore.</Italian>
<Chinese>影響這個單位的體力表現,值越高代表體力越好</Chinese>
<Chinesesimp>影响这个单位的体力表现,值越高代表体力越好</Chinesesimp>
<Russian>Влияет на общую производительность юнита.</Russian>
<Russian>Влияет на общую производительность юнита.Чем выше, тем лучше.</Russian>
<Portuguese>Influencia na performance geral dessa unidade. Quanto maior, melhor.</Portuguese>
<Czech>Ovlivňuje celkový výkon této jednotky. Vyšší znamená lépe.</Czech>
</Key>
@ -87,7 +87,7 @@
<Polish>Wpływa na czas regeneracji podczas postoju. Więcej znaczy szybciej.</Polish>
<Korean>얼마나 빨리 회복하는지를 바꿉니다. 값이 클수록 더 나은 성능을 발휘합니다</Korean>
<French>Modifie la vitesse à laquelle le joueur récupère lorsqu'il se repose.\nPlus la valeur est élevée, plus la récupération est rapide.</French>
<Italian>Determina in quanto tempo il giocatore recupera quando rilassato. Maggiore significa migliore.</Italian>
<Italian>Determina quanto velocemente il giocatore recupera le energie quando si ferma. Maggiore significa migliore.</Italian>
<Chinese>決定玩家休息多久就能回復體力,值越高恢復越快</Chinese>
<Chinesesimp>决定玩家休息多久就能恢复体力,值越高恢复越快</Chinesesimp>
<Russian>Изменяет скорость восстановления игрока во время отдыха. Чем выше, тем быстрее.</Russian>
@ -102,7 +102,7 @@
<Polish>Współczynnik masy ekwipunku</Polish>
<Korean>부담 요인</Korean>
<French>Facteur de charge</French>
<Italian>Fattore Caricamento</Italian>
<Italian>Fattore Carico</Italian>
<Chinese>負重量</Chinese>
<Chinesesimp>重量系数</Chinesesimp>
<Russian>Фактор нагрузки</Russian>
@ -117,7 +117,7 @@
<Polish>Zmniejsza lub zwiększa wpływ ciężaru ekwipunku na wydolność gracza. Zero oznacza kompletny brak wpływu na wydolność.</Polish>
<Korean>플레이어가 무게에 따라 얼마나 영향받는지를 증가시키거나 감소시킵니다. 0의 경우 플레이어가 장비 무게에 영향받지 않습니다.</Korean>
<French>Augmente ou diminue l'influence du poids sur les performances du joueur.\nUne valeur nulle indique que le poids de l'équipement n'a aucun impact sur les performances.</French>
<Italian>Incrementa o decrementa quanto il peso influenza le prestazioni dei giocatori. Zero significa che il peso dell'equipaggiamento non ha alcuna influenza nelle prestazioni.</Italian>
<Italian>Determina quanto il peso trasportato influenza le prestazioni dei giocatori. Zero significa che il peso dell'equipaggiamento non influisce sulle prestazioni.</Italian>
<Chinese>增加或降低玩家所能承受的負重量. 如設定值為0, 代表裝備的重量將不會影響到玩家的體力表現</Chinese>
<Chinesesimp>增加或降低玩家所能承受的负重量。如设定值为0代表装备的重量将不会影响到玩家的体力表现</Chinesesimp>
<Russian>Увеличивает или уменьшает вес, влияющий на производительность игроков. Ноль означает, что вес снаряжения не влияет на производительность</Russian>
@ -147,76 +147,22 @@
<Polish>Wpływa na to w jakim stopniu stromy teren wpływa na utratę wytrzymałości. Więcej oznacza szybszą utratę wytrzymałości.</Polish>
<Korean>경사도에 따라 얼마나 피로해지는지를 정합니다. 값이 클수록 더 많은 피로를 유발합니다.</Korean>
<French>Définit à quel point un terrain escarpé réduit l'endurance du joueur.\nPlus la valeur est élevée, moins le joueur est endurant.</French>
<Italian>Stabilisce quanto la pendenza del terreno incrementa la perdita della stamina. Maggiore significa più stamina persa.</Italian>
<Italian>Determina quanto la pendenza del terreno incrementa la perdita della stamina. Maggiore significa più stamina persa.</Italian>
<Chinese>設定陡峭的地形將會影響多少體力的流失,值越高代表體力流失越快</Chinese>
<Chinesesimp>设定陡峭的地形将会影响多少体力的流失速度,值越高代表体力流失越快</Chinesesimp>
<Russian>Устанавливает, насколько крутая местность увеличивает потерю выносливости. Чем выше, тем быстрее теряется выносливость.</Russian>
<Portuguese>Define o quanto que um terreno íngrime aumenta na perda de estamina. Quanto maior, maior a perda de estamina.</Portuguese>
<Czech>Nastavuje, o kolik strmý terén zvyšuje ztrátu výdrže. Vyšší znamená vyšší ztrátu výdrže.</Czech>
</Key>
<Key ID="STR_ACE_Advanced_Fatigue_SwayFactor">
<English>Sway factor</English>
<Spanish>Factor de balanceo de mira</Spanish>
<German>Verwacklungsfaktor</German>
<Japanese>手ぶれ因数</Japanese>
<Chinesesimp>抖动系数</Chinesesimp>
<Chinese>抖動因素</Chinese>
<French>Facteur de tremblement</French>
<Italian>Fattore di oscillazione</Italian>
<Polish>Czynnik kołysania</Polish>
<Russian>Фактор колебания прицела</Russian>
<Portuguese>Fator de Balanço de Mira</Portuguese>
<Czech>Faktor kývání</Czech>
<Korean>손떨림 정도</Korean>
</Key>
<Key ID="STR_ACE_Advanced_Fatigue_SwayFactor_Description">
<English>Influences the amount of weapon sway. Higher means more sway.</English>
<Spanish>Afecta al la estabilidad de la mira. Más alto significa más balanceo</Spanish>
<German>Beeinflusst, wie ruhig man eine Waffe halten kann. Ein höherer Wert bedeutet weniger Stabilisierung.</German>
<Japanese>武器を持つ手のぶれ度合いを設定します。 値が高ければ高いほど、手ぶれが強くなります。</Japanese>
<Chinesesimp>影响手持武器的晃动程度,数值越高,抖动的越厉害。</Chinesesimp>
<Chinese>影響手持武器晃動程度,數值越高抖動越厲害</Chinese>
<French>Influe sur l'amplitude du tremblement de l'arme. Une valeur plus élevée signifie plus de tremblement.</French>
<Italian>Influenza l'ammontare di oscillazione dell'arma. Maggiore significa più oscillazione.</Italian>
<Polish>Wpływa na poziom kołysania broni. Większa ilość znaczy większe kołysanie.</Polish>
<Russian>Влияет на колебания прицела оружия. Чем выше - тем больше.</Russian>
<Portuguese>Influencia a quantidade de balanço da mira da arma. Quanto maior, mais balanço.</Portuguese>
<Czech>Ovlivňuje množství kývání zbraní. Vyšší znamená více kývání.</Czech>
<Korean>손떨림의 정도를 정합니다. 높을 수록 많이 휘적입니다.</Korean>
</Key>
<Key ID="STR_ACE_Advanced_Fatigue_RestedSwayFactor">
<English>Rested sway factor</English>
<French>Facteur de balancement au repos</French>
<Korean>휴식 시 손떨림 정도</Korean>
<German>Verwacklungsfaktor, wenn aufgelegt</German>
</Key>
<Key ID="STR_ACE_Advanced_Fatigue_RestedSwayFactor_Description">
<English>Influences the amount of weapon sway while weapon is rested.</English>
<French>Influence le degré de balancement de l'arme au repos.</French>
<Korean>무기가 아무런 행동도 하지 않는 동안 무기가 흔들리는 정도를 정합니다.</Korean>
<German>Beeinflusst, wie ruhig man die Waffe hält, während sie aufgelegt ist.</German>
</Key>
<Key ID="STR_ACE_Advanced_Fatigue_DeployedSwayFactor">
<English>Deployed sway factor</English>
<French>Facteur de balancement déployé</French>
<Korean>거치 시 손떨림 정도</Korean>
<German>Verwacklungsfaktor, wenn Zweibein aufgestellt ist.</German>
</Key>
<Key ID="STR_ACE_Advanced_Fatigue_DeployedSwayFactor_Description">
<English>Influences the amount of weapon sway while weapon is deployed.</English>
<French>Influence le degré de balancement de l'arme déployée.</French>
<Korean>무기를 거치하는 동안 무기를 흔드는 정도를 정합니다.</Korean>
<German>Beeinflusst, wie ruhig man die Waffen hält, während das Zweibein aufgestellt ist.</German>
</Key>
<Key ID="STR_ACE_Advanced_Fatigue_Enabled">
<English>Enabled</English>
<Spanish>Activada</Spanish>
<German>Aktiv</German>
<Japanese>アドバンスド ファティーグを有効化</Japanese>
<Japanese>有効化</Japanese>
<Polish>Włączone</Polish>
<Korean>활성화</Korean>
<French>Activé</French>
<Italian>Abilitato</Italian>
<Italian>Abilitata</Italian>
<Chinese>啟用</Chinese>
<Chinesesimp>启用</Chinesesimp>
<Russian>Включена</Russian>
@ -227,14 +173,14 @@
<English>Enables/disables Advanced Fatigue.</English>
<Spanish>Activa/desactiva la fatiga avanzada</Spanish>
<German>Aktiviert/deaktiviert Advanced Fatigue.</German>
<Japanese>アドバンスド ファティーグを有効化します。</Japanese>
<Japanese>アドバンスド疲労は高度な疲労管理システムを有効化します。</Japanese>
<Polish>Włącza/wyłącza zaawansowaną wytrzymałość</Polish>
<Korean>고급 피로도 활성화/비활성화</Korean>
<French>Active/Désactive la fatigue avancée.</French>
<Italian>Abilita/disabilita la Fatica Avanzata.</Italian>
<Italian>Abilita/Disabilita la Fatica Avanzata.</Italian>
<Chinese>啟用/關閉進階體力.</Chinese>
<Chinesesimp>启用/关闭进阶体力。</Chinesesimp>
<Russian>Включает / Отключает Продвинутую усталость</Russian>
<Russian>Включает/отключает Продвинутую усталость</Russian>
<Portuguese>Ativa/Desativa Fadiga Avançada.</Portuguese>
<Czech>Aktivuje / deaktivuje Pokročilou únavu.</Czech>
</Key>
@ -275,7 +221,7 @@
<German>Blende Ausdauerleiste automatisch aus</German>
<Chinese>體力條自動淡去</Chinese>
<Chinesesimp>自动淡化体力条</Chinesesimp>
<Italian>Dissolvenza automatica della barra della stamina</Italian>
<Italian>Nascondi in automatico la barra della stamina</Italian>
<Czech>Automaticky schovat lištu výdrže</Czech>
<French>Fondu automatique de la barre d'endurance</French>
<Japanese>自動的にスタミナバーを非表示</Japanese>

View File

@ -2,26 +2,18 @@
// Fired XEH
GVAR(ammoEventHandlers) = createHashMap;
[QGVAR(throwFiredXEH), FUNC(throwFiredXEH)] call CBA_fnc_addEventHandler;
[QGVAR(throwFiredXEH), LINKFUNC(throwFiredXEH)] call CBA_fnc_addEventHandler;
// Exit on HC
if (!hasInterface) exitWith {};
// 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]; };
} count (getArray (configFile >> "CfgWeapons" >> "Throw" >> _x >> "magazines"));
nil
} count getArray (configFile >> "CfgWeapons" >> "Throw" >> "muzzles");
// Temporary Wind Info indication
GVAR(tempWindInfo) = false;
// 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

@ -6,6 +6,6 @@ PREP_RECOMPILE_START;
#include "XEH_PREP.hpp"
PREP_RECOMPILE_END;
#include "initSettings.sqf"
#include "initSettings.inc.sqf"
ADDON = true;

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

@ -19,7 +19,7 @@ params ["_unit"];
if !(_unit getVariable [QGVAR(inHand), false]) exitWith {false};
if (vehicle _unit != _unit) exitWith {
if (!isNull objectParent _unit) exitWith {
private _startPos = eyePos _unit;
private _aimLinePos = AGLToASL (positionCameraToWorld [0, 0, 1]);
private _intersections = lineIntersectsSurfaces [_startPos, _aimLinePos, _unit, objNull, false];

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

@ -53,6 +53,12 @@ _unit setVariable [QGVAR(dropDistance), DROP_DISTANCE_DEFAULT];
// Remove controls hint (check if ever enabled is inside the function)
call EFUNC(interaction,hideMouseHint);
// Hide wind info after throw, if it was temporarily enabled for the throw
if (GVAR(tempWindInfo)) then {
EGVAR(weather,WindInfo) = false;
GVAR(tempWindInfo) = false;
};
// Remove throw action
[_unit, "DefaultAction", _unit getVariable [QGVAR(throwAction), -1]] call EFUNC(common,removeActionEventHandler);

View File

@ -17,9 +17,9 @@
params ["_magazineClassname"];
_magazineClassname = toLower _magazineClassname;
_magazineClassname = toLowerANSI _magazineClassname;
private _throwMuzzles = getArray (configFile >> "CfgWeapons" >> "Throw" >> "muzzles");
_throwMuzzles = _throwMuzzles select {_magazineClassname in ((getArray (configFile >> "CfgWeapons" >> "Throw" >> _x >> "magazines")) apply {toLower _x})};
_throwMuzzles = _throwMuzzles select {_magazineClassname in ((getArray (configFile >> "CfgWeapons" >> "Throw" >> _x >> "magazines")) apply {toLowerANSI _x})};
[_throwMuzzles select 0, ""] select (_throwMuzzles isEqualTo [])

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)
@ -35,6 +35,11 @@ if (isNull (_unit getVariable [QGVAR(activeThrowable), objNull]) && {(currentThr
TRACE_1("no throwables",_unit);
};
// Temporarily enable wind info, to aid in throwing smoke grenades effectively
if (GVAR(enableTempWindInfo) && {!(missionNamespace getVariable [QEGVAR(weather,WindInfo), false])}) then {
[] call EFUNC(weather,displayWindInfo);
GVAR(tempWindInfo) = true;
};
_unit setVariable [QGVAR(inHand), true];

View File

@ -44,8 +44,7 @@
_addedPickUpHelpers pushBack _pickUpHelper;
_throwablesHelped pushBack _x;
};
nil
} count _nearThrowables;
} forEach _nearThrowables;
_args set [0, getPosASL ACE_player];
_args set [3, _nearThrowables];
@ -56,11 +55,10 @@
{
// Only handling with attachTo works nicely
_x attachTo [_x getVariable [QGVAR(throwable), objNull], [0, 0, 0]];
nil
} count _addedPickUpHelpers;
} forEach _addedPickUpHelpers;
} else {
TRACE_1("Cleaning Pick Up Helpers",count _addedPickUpHelpers);
{deleteVehicle _x} count _addedPickUpHelpers;
{deleteVehicle _x} forEach _addedPickUpHelpers;
[_idPFH] call CBA_fnc_removePerFrameHandler;
};
}, 0, [(getPosASL ACE_player) vectorAdd [-100, 0, 0], [], [], []]] call CBA_fnc_addPerFrameHandler;

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);
};
@ -45,7 +45,7 @@ if (!(_unit getVariable [QGVAR(primed), false])) then {
private _newVelocity = (_p1 vectorFromTo _p2) vectorMultiply _velocity;
// Adjust for throwing from inside vehicles, where we have a vehicle-based velocity that can't be compensated for by a human
if (vehicle _unit != _unit) then {
if (!isNull objectParent _unit) then {
_newVelocity = _newVelocity vectorAdd (velocity (vehicle _unit));
};

View File

@ -40,3 +40,11 @@ private _category = format ["ACE %1", localize LSTRING(Category)];
true,
1
] call CBA_fnc_addSetting;
[
QGVAR(enableTempWindInfo), "CHECKBOX",
[LSTRING(EnableTempWindInfo_DisplayName), LSTRING(EnableTempWindInfo_Description)],
_category,
true,
0
] call CBA_fnc_addSetting;

View File

@ -5,7 +5,7 @@
<English>Advanced Throwing</English>
<Spanish>Lanzamiento Avanzado</Spanish>
<Russian>Улучшенный бросок гранат</Russian>
<Japanese>アドバンスド スローイング</Japanese>
<Japanese>アドバンスド投擲</Japanese>
<Polish>Zaawansowane rzucanie</Polish>
<German>Erweitertes Wurfsystem</German>
<Korean>고급 투척</Korean>
@ -21,7 +21,7 @@
<English>Allows changing advanced throwing behaviour.</English>
<Spanish>Permite modificar el comportamiento del lanzamiento avanzado</Spanish>
<Russian>Позволяет настраивать поведение улучшенного броска гранат.</Russian>
<Japanese>アドバンスド スローイングの動作挙動を変更します。</Japanese>
<Japanese>アドバンスド投擲は投擲の高度な動作挙動を変更可能にします。</Japanese>
<Polish>Zezwala na zmianę zachowania zaawansowanego trybu rzucania.</Polish>
<German>Erlaubt es, das Verhalten des erweiterten Wurfsystems zu ändern.</German>
<Korean>고급 투척 행위를 허가합니다</Korean>
@ -36,7 +36,7 @@
<English>Enable Advanced Throwing</English>
<Spanish>Activar Lanzamiento Avanzado</Spanish>
<Russian>Вкл. улучшенный бросок</Russian>
<Japanese>アドバンスド スローイングを有効化</Japanese>
<Japanese>アドバンスド投擲を有効化</Japanese>
<Polish>Aktywuj zaawansowane rzucanie</Polish>
<German>Aktiviere erweitertes Wurfsystem</German>
<Korean>고급 투척 활성화 </Korean>
@ -52,7 +52,7 @@
<English>Enables advanced throwing system.</English>
<Spanish>Activa el Lanzamiento Avanzado</Spanish>
<Russian>Включает систему улучшенного броска.</Russian>
<Japanese>アドバンスド スローイングを有効化します。</Japanese>
<Japanese>アドバンスド投擲は高度な投擲システムを有効化します。</Japanese>
<Polish>Aktywuje system zaawansowanego rzucania.</Polish>
<German>Aktiviert das erweiterte Wurfsystem.</German>
<Korean>고급 투척을 활성화 합니다</Korean>
@ -68,7 +68,7 @@
<English>Show Throw Arc</English>
<Spanish>Mostrar Arco de Lanzamiento</Spanish>
<Russian>Показать траекторию броска</Russian>
<Japanese>てき軌道を表示</Japanese>
<Japanese>軌道を表示</Japanese>
<Polish>Pokaż trasę lotu</Polish>
<German>Zeige Wurfbogen</German>
<Korean>투척 궤적 표시</Korean>
@ -84,7 +84,7 @@
<English>Enables visualization of the throw arc (where throwable will fly).</English>
<Spanish>Activa la visualización del arco de lanzamiento (donde irá el objeto lanzado)</Spanish>
<Russian>Включает визуализацию траектории броска (как полетит граната).</Russian>
<Japanese>てき軌道 (投てき物がどこに飛ぶか) の表示を有効化します。</Japanese>
<Japanese>擲軌道 (投擲物がどこに飛ぶか) の表示を有効化します。</Japanese>
<Polish>Wyświetla wizualizację trasy przelotu granatu.</Polish>
<German>Aktiviert die Visualisierung des Wurfbogens (wohin das Objekt geworfen werden wird).</German>
<Korean>투척 궤도를 시각화 합니다(투척물이 어디로 갈지)</Korean>
@ -99,12 +99,12 @@
<English>Show Throwing Mouse Controls</English>
<Spanish>Mostrar controles de ratón de lanzamiento</Spanish>
<Russian>Показывать управление мышью</Russian>
<Japanese>てきのマウス操作を表示</Japanese>
<Japanese>のマウス操作を表示</Japanese>
<Polish>Pokaż podpowiedzi sterowania myszą</Polish>
<German>Zeige Maussteuerung beim Werfen</German>
<Korean>마우스 조작 표시</Korean>
<French>Afficher les contrôles à la souris du lancé</French>
<Italian>Mostra Comandi Mouse Lancio</Italian>
<Italian>Mostra Comandi Mouse di Lancio</Italian>
<Chinese>顯示滑鼠投擲控制提示</Chinese>
<Chinesesimp>显示鼠标投掷控制提示</Chinesesimp>
<Portuguese>Mostrar os controles de mouse para Arremesso</Portuguese>
@ -114,7 +114,7 @@
<English>Enables visual cues for mouse controls when throwable is prepared.</English>
<Spanish>Activa muestras visuales para los controles del ratón cuando el objeto lanzable está preparado</Spanish>
<Russian>Включает отображение подсказок по управлению мышью, когда граната подготовлена.</Russian>
<Japanese>てき物を構える時、マウス操作の説明表示を有効化します。</Japanese>
<Japanese>物を構える時、マウス操作の説明表示を有効化します。</Japanese>
<Polish>Wyświetla podpowiedzi sterowania myszą kiedy obiekt miotany jest w ręku.</Polish>
<German>Aktiviert visuelle Hinweise zur Maussteuerung, wenn ein Objekt zum Werfen vorbereitet wird.</German>
<Korean>투척물을 준비시 마우스 조작을 시각화해서 보여줍니다</Korean>
@ -129,12 +129,12 @@
<English>Enable Throwables Pick Up</English>
<Spanish>Habilitar recoger objetos lanzados</Spanish>
<Russian>Вкл. подбор гранат</Russian>
<Japanese>てき物の拾い上げを有効化</Japanese>
<Japanese>物の拾い上げを有効化</Japanese>
<Polish>Zezwól na podnoszenie obiektów miotanych</Polish>
<German>Aktiviere Aufheben von Wurfobjekten</German>
<Korean>투척물 줍기 활성화</Korean>
<French>Permettre le ramassage d'objets lançables</French>
<Italian>Abilita Raccogli Oggetti</Italian>
<Italian>Abilita Raccolta Lanciabili</Italian>
<Chinese>啟用可撿取地面投擲物</Chinese>
<Chinesesimp>启用捡取地面投掷物</Chinesesimp>
<Portuguese>Permitir pegar arremessáveis</Portuguese>
@ -144,12 +144,12 @@
<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>
<French>Active la possibilité de ramasser des objets lançables du sol.</French>
<Italian>Abilita la possibilità di raccogliere un oggetto lanciabile da terra.</Italian>
<Italian>Permette ai giocatori di raccogliere un oggetto lanciabile da terra.</Italian>
<Chinese>啟用後, 可撿取地面上的投擲物</Chinese>
<Chinesesimp>启用后,可捡取地面上的投掷物</Chinesesimp>
<Portuguese>Permite que objetos arremessados sejam pegos do chão. (ACE Menu de Interação)</Portuguese>
@ -159,12 +159,12 @@
<English>Enable Attached Throwables Pick Up</English>
<Spanish>Activar coger objetos lanzables que se enganchan</Spanish>
<Russian>Вкл. подбор прикрепленных гранат</Russian>
<Japanese>取り付けられた投てき物の拾い上げを有効化</Japanese>
<Japanese>装着済の投擲物の拾い上げを有効化</Japanese>
<Polish>Zezwól na podnoszenie przyczepionych obiektów miotanych</Polish>
<German>Aktiviere erneute Aufnahme befestigter Wurfobjekte</German>
<Korean>부착 투척물 줍기 활성화</Korean>
<French>Activer le ramassage d'objets lançables attachés</French>
<Italian>Abilita Raccogli Oggetti Lanciabili da altri Oggetti</Italian>
<Italian>Abilita Raccolta Lanciabili Attaccati</Italian>
<Chinese>啟用可撿取附著投擲物</Chinese>
<Chinesesimp>启用捡取附着投掷物</Chinesesimp>
<Portuguese>Permitir pegar arremessáveis fixados</Portuguese>
@ -174,27 +174,47 @@
<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>
<French>Active la possibilité de ramasser des objets lançables attachés à d'autres objets.</French>
<Italian>Abilita la possibilità di raccogliere gli oggetti lanciabili dagli altri oggetti. </Italian>
<Italian>Permette ai giocatori di raccogliere gli oggetti lanciabili da altri oggetti attaccati.</Italian>
<Chinese>啟用後, 可撿取附著在物件上的投擲物</Chinese>
<Chinesesimp>启用后,可捡取附着在物体上的投掷物</Chinesesimp>
<Portuguese>Permite que arremessáveis fixados em objetos sejam pegos.</Portuguese>
<Czech>Zapíná schopnost zvednutí předmětů z objektů ke kterým jsou připnuté.</Czech>
</Key>
<Key ID="STR_ACE_Advanced_Throwing_EnableTempWindInfo_DisplayName">
<English>Show Temporary Wind Info</English>
<German>Zeige temporäre Windinformationen</German>
<Italian>Mostra informazioni sul vento temporaneamente</Italian>
<Japanese>一時的に風の情報を表示</Japanese>
<Korean>바람 정보 임시로 표시</Korean>
<French>Afficher temporairement les informations sur le vent</French>
<Russian>Временно показать информацию о ветре</Russian>
<Spanish>Mostrar información del viento temporalmente</Spanish>
</Key>
<Key ID="STR_ACE_Advanced_Throwing_EnableTempWindInfo_Description">
<English>Temporarily display Wind Info while throwing, to aid in placing smoke grenades effectively.</English>
<German>Zeige während des werfens Windinformationen an, um Rauchgranaten effektiver zu platzieren.</German>
<Italian>Mostra le informazioni sul vento durante il lancio di granate, facilitando il piazzamento ottimale di fumogeni.</Italian>
<Japanese>投擲行動中に風向きの情報を一時的に表示し、発煙手榴弾の煙幕を効果的に展開しやすくします。</Japanese>
<Korean>연막탄을 효과적으로 배치하는 데 도움이 되도록 투척하는 동안 일시적으로 바람 정보를 표시합니다.</Korean>
<French>Affiche les informations sur le vent pendant le lancement pour placer les grenades fumigènes plus efficacement.</French>
<Russian>Временно отображайте информацию о ветре во время броска, чтобы помочь эффективно разместить дымовые шашки.</Russian>
<Spanish>Mostrar información del viento temporalmente mientras se lanza, para ayudar a lanzar las granadas de humo de forma efectiva.</Spanish>
</Key>
<Key ID="STR_ACE_Advanced_Throwing_Prepare">
<English>Prepare/Change Throwable</English>
<Spanish>Preparar/Cambiar objetos lanzables</Spanish>
<Russian>Подготовить/заменить гранату</Russian>
<Japanese>投てき物の準備/変更</Japanese>
<Japanese>物の準備/変更</Japanese>
<Polish>Przygotuj/zmień ob. miotany</Polish>
<German>Wurfobjekt vorbereiten/wechseln</German>
<Korean>투척물 준비/변경</Korean>
<French>Préparer/Changer d'objet lançable</French>
<Italian>Prepara/Cambia Oggetto lanciabile</Italian>
<Italian>Prepara/Cambia Oggetto Lanciabile</Italian>
<Chinese>準備/變更投擲物</Chinese>
<Chinesesimp>准备/变更投掷物</Chinesesimp>
<Portuguese>Preparar/Mudar Arremessável</Portuguese>
@ -204,12 +224,12 @@
<English>Throwable Drop Mode (Hold)</English>
<Spanish>Modo soltar objeto lanzable (Mantener pulsado)</Spanish>
<Russian>Режим броска гранаты (удерживать)</Russian>
<Japanese>てきモード (押している間)</Japanese>
<Japanese>モード (押している間)</Japanese>
<Polish>Tryb upuszczania ob. miotanego (przytrzymaj)</Polish>
<German>Wurfobjekt Fallmodus (halten)</German>
<Korean>투척물 떨어뜨리기 모드(꾹눌러서)</Korean>
<French>Mode de lancé de l'objet (Tenir)</French>
<Italian>Modalità Oggetto Gettabile (Mantenere)</Italian>
<Italian>Modalità Gettare (Tieni premuto)</Italian>
<Chinese>投擲模式 (按住)</Chinese>
<Chinesesimp>投掷模式(按住)</Chinesesimp>
<Portuguese>Modo de Arremesso (Segurar)</Portuguese>
@ -219,12 +239,12 @@
<English>Throwable Drop Mode (Toggle)</English>
<Spanish>Modo soltar objeto lanzable (interruptor)</Spanish>
<Russian>Режим броска гранаты (переключить)</Russian>
<Japanese>てきモード (切り替え)</Japanese>
<Japanese>モード (切り替え)</Japanese>
<Polish>Tryb upuszczania ob. miotanego (przełącz)</Polish>
<German>Wurfobjekt Fallmodus (umschalten)</German>
<Korean>투척물 떨어뜨리기 모드(토글)</Korean>
<French>Mode de lancé de l'objet (Basculer)</French>
<Italian>Modalità Oggetto lanciabile Gettabile (Interruttore)</Italian>
<Italian>Modalità Getta Oggetto (Cambia)</Italian>
<Chinese>投擲模式 (切換)</Chinese>
<Chinesesimp>投掷模式(切换)</Chinesesimp>
<Portuguese>Modo de Arremesso (Alternar)</Portuguese>
@ -234,12 +254,12 @@
<English>Primed</English>
<Spanish>Preparado</Spanish>
<Russian>Подготовлена</Russian>
<Japanese>点火</Japanese>
<Japanese>点火した</Japanese>
<Polish>Odbezpieczony</Polish>
<German>Scharf gemacht</German>
<Korean>뇌관 작동</Korean>
<French>amorcée</French>
<Italian>Armato</Italian>
<Italian>Innescato</Italian>
<Chinese>引信開始燃燒</Chinese>
<Chinesesimp>已开引信</Chinesesimp>
<Portuguese>Preparado</Portuguese>
@ -254,7 +274,7 @@
<German>Werfen</German>
<Korean>던지기</Korean>
<French>Lancer</French>
<Italian>Lanciare</Italian>
<Italian>Lancia</Italian>
<Chinese>投擲</Chinese>
<Chinesesimp>投掷</Chinesesimp>
<Portuguese>Arremessar</Portuguese>
@ -270,7 +290,7 @@
<German>(Scrollen) Modus wechseln</German>
<Korean>(마우스 휠) 모드 변경</Korean>
<French>(Défilement) Changer de mode</French>
<Italian>(Scorrere) Cambio Modalità</Italian>
<Italian>(Scorri) Cambia Modalità</Italian>
<Chinese>(滾輪) 變更模式</Chinese>
<Chinesesimp>(滚轮)变更模式</Chinesesimp>
<Portuguese>(Roda do Mouse) Alternar modo</Portuguese>
@ -286,7 +306,7 @@
<German>(Scrollen) Erweitern</German>
<Korean>(마우스 휠) 연장</Korean>
<French>(Défilement) Étendre</French>
<Italian>(Scorrere) Estendere</Italian>
<Italian>(Scorri) Estendi</Italian>
<Chinese>(滾輪) 延長</Chinese>
<Chinesesimp>(滚轮)延长</Chinesesimp>
<Portuguese>(Roda do Mouse) Estender</Portuguese>
@ -302,7 +322,7 @@
<German>(Klicken) Abkochen</German>
<Korean>(클릭) 예열</Korean>
<French>(Clique) Dégoupiller</French>
<Italian>(Click) Arma</Italian>
<Italian>(Click) Innesca</Italian>
<Chinese>(點擊) 提早拉開引信</Chinese>
<Chinesesimp>(点击)提早拉开引信</Chinesesimp>
<Portuguese>(Clique) Cozinhar</Portuguese>
@ -313,7 +333,7 @@
<English>Pick Up</English>
<Spanish>Coger</Spanish>
<Russian>Подобрать</Russian>
<Japanese>い上げる</Japanese>
<Japanese></Japanese>
<Polish>Podnieś</Polish>
<German>Aufheben</German>
<Korean>줍기</Korean>

View File

@ -16,7 +16,7 @@
} forEach _sections;
}] call CBA_fnc_addEventHandler;
[QGVAR(unGarrison), FUNC(unGarrison)] call CBA_fnc_addEventHandler;
[QGVAR(unGarrison), LINKFUNC(unGarrison)] call CBA_fnc_addEventHandler;
[QGVAR(doMove), {
params ["_unitsArray"];
@ -73,6 +73,6 @@
if (isServer) then {
["CAManBase", "init", {
// wait for HMD to be assigned so `hmd _unit` works
[FUNC(assignNVG), _this, 1] call CBA_fnc_waitAndExecute;
[LINKFUNC(assignNVG), _this, 1] call CBA_fnc_waitAndExecute;
}] call CBA_fnc_addClassEventHandler;
};

View File

@ -11,6 +11,6 @@ if (isServer) then {
GVAR(assignNVGstate) = false;
};
#include "initSettings.sqf"
#include "initSettings.inc.sqf"
ADDON = true;

View File

@ -7,8 +7,8 @@
* 0: The building(s) nearest this position are used <POSITION>
* 1: Limit the building search to those type of building <ARRAY>
* 2: Units that will be garrisoned <ARRAY>
* 3: Radius to fill building(s) <SCALAR> (default: 50)
* 4: 0: even filling, 1: building by building, 2: random filling <SCALAR> (default: 0)
* 3: Radius to fill building(s) <NUMBER> (default: 50)
* 4: 0: even filling, 1: building by building, 2: random filling <NUMBER> (default: 0)
* 5: True to fill building(s) from top to bottom <BOOL> (default: false) (note: only works with filling mode 0 and 1)
* 6: Teleport units <BOOL> (default: false)
@ -33,7 +33,7 @@ if (_startingPos isEqualTo [0,0,0]) exitWith {
[LSTRING(GarrisonInvalidPosition)] call EFUNC(common,displayTextStructured);
};
if (count _unitsArray == 0 || {isNull (_unitsArray select 0)}) exitWith {
if (_unitsArray isEqualTo [] || {isNull (_unitsArray select 0)}) exitWith {
TRACE_1("fnc_garrison: Units error",_unitsArray);
[LSTRING(GarrisonNoUnits)] call EFUNC(common,displayTextStructured);
};
@ -43,7 +43,7 @@ if (_fillingRadius >= 50) then {
_buildings = [_buildings] call CBA_fnc_shuffle;
};
if (count _buildings == 0) exitWith {
if (_buildings isEqualTo []) exitWith {
TRACE_1("fnc_garrison: Building error",_buildings);
[LSTRING(GarrisonNoBuilding)] call EFUNC(common,displayTextStructured);
};
@ -128,7 +128,7 @@ switch (_fillingType) do {
} else {
private _pos = _building select 0;
private _nearestUnits = (_pos nearEntities ["CAManBase", 2]);
LOG(format [ARR_3("fnc_garrison: Unit detection | %1 units nearby | %2 units within height",count _nearestUnits, {floor ((getPos _x) select 2) == floor (_pos select 2)} count _nearestUnits)]);
LOG(format [ARR_3("fnc_garrison: Unit detection | %1 units nearby | %2 units within height",count _nearestUnits,{floor ((getPos _x) select 2) == floor (_pos select 2)} count _nearestUnits)]);
if (count _nearestUnits > 0 && {[_nearestUnits, _pos] call _fnc_comparePos}) then {
LOG(format [ARR_2("fnc_garrison: Unit present | removing position | %1 positions remaining for this building",count (_buildingsIndex select (_buildingsIndex find _building)) - 1)]);
@ -177,7 +177,7 @@ switch (_fillingType) do {
} else {
private _pos = _building select 0;
private _nearestUnits = (_pos nearEntities ["CAManBase", 2]);
LOG(format [ARR_3("fnc_garrison: Unit detection | %1 units nearby | %2 units within height",count _nearestUnits, {floor ((getPos _x) select 2) == floor (_pos select 2)} count _nearestUnits)]);
LOG(format [ARR_3("fnc_garrison: Unit detection | %1 units nearby | %2 units within height",count _nearestUnits,{floor ((getPos _x) select 2) == floor (_pos select 2)} count _nearestUnits)]);
if (count _nearestUnits > 0 && {[_nearestUnits, _pos] call _fnc_comparePos}) then {
LOG(format [ARR_2("fnc_garrison: Unit present | removing position | %1 positions remaining for this building",count (_buildingsIndex select (_buildingsIndex find _building)) - 1)]);
@ -224,7 +224,7 @@ switch (_fillingType) do {
} else {
private _pos = selectRandom _building;
private _nearestUnits = (_pos nearEntities ["CAManBase", 2]);
LOG(format [ARR_3("fnc_garrison: Unit detection | %1 units nearby | %2 units within height",count _nearestUnits, {floor ((getPos _x) select 2) == floor (_pos select 2)} count _nearestUnits)]);
LOG(format [ARR_3("fnc_garrison: Unit detection | %1 units nearby | %2 units within height",count _nearestUnits,{floor ((getPos _x) select 2) == floor (_pos select 2)} count _nearestUnits)]);
if (count _nearestUnits > 0 && {[_nearestUnits, _pos] call _fnc_comparePos}) then {
LOG(format [ARR_2("fnc_garrison: Unit present | removing position | %1 positions remaining for this building",count (_buildingsIndex select (_buildingsIndex find _building)) - 1)]);
@ -258,7 +258,7 @@ switch (_fillingType) do {
};
};
TRACE_1(format [ARR_2("fnc_garrison: while loop ended | %1 units ready to be treated by PFH",count _unitMoveList)], _teleport);
TRACE_1(format [ARR_2("fnc_garrison: while loop ended | %1 units ready to be treated by PFH",count _unitMoveList)],_teleport);
// Update the unit list and remove duplicate positions and units
private _garrison_unitMoveList = missionNameSpace getVariable [QGVAR(garrison_unitMoveList), []];
@ -279,5 +279,5 @@ if (_teleport) then {
[_unitMoveList] call FUNC(garrisonMove);
};
TRACE_1(format [ARR_3("fnc_garrison: End | %1 units left | %2 buildings left", count _unitsArray, count _buildingsIndex)], _unitsArray);
TRACE_1(format [ARR_3("fnc_garrison: End | %1 units left | %2 buildings left",count _unitsArray,count _buildingsIndex)],_unitsArray);
_unitsArray

View File

@ -81,13 +81,13 @@ if (isNil QGVAR(garrison_moveUnitPFH)) then {
[QGVAR(enableAttack), [[_unit], true], _unit] call CBA_fnc_targetEvent;
};
LOG(format [ARR_2("garrisonMove PFH: unit in position | %1 units left", count _unitMoveList)]);
LOG(format [ARR_2("garrisonMove PFH: unit in position | %1 units left",count _unitMoveList)]);
};
// Check if unit is alive or even existing
if (!alive _unit || {_unit getVariable [QGVAR(garrisoned), false]}) then {
_unitMoveList deleteAt (_unitMoveList find _x);
LOG(format [ARR_2("garrisonMove PFH: unit dead, deleted or garrisoned via TP | %1 units left", count _unitMoveList)]);
LOG(format [ARR_2("garrisonMove PFH: unit dead, deleted or garrisoned via TP | %1 units left",count _unitMoveList)]);
} else {
private _unitPos = getPos _unit;

View File

@ -28,7 +28,7 @@ _units = _units select {local _x};
private _leader = leader _unit;
TRACE_3("fnc_ungarrison: unit and leader",_unit , _leader, (_leader == _unit));
TRACE_3("fnc_ungarrison: unit and leader",_unit,_leader,(_leader == _unit));
_unit setVariable [QGVAR(garrisonned), false, true];

View File

@ -6,7 +6,7 @@
<Chinese>AI</Chinese>
<French>IA</French>
<Spanish>IA</Spanish>
<Italian>AI</Italian>
<Italian>IA</Italian>
<Polish>SI</Polish>
<Russian>ИИ</Russian>
<German>KI</German>
@ -85,15 +85,25 @@
<English>Auto-Equip NVGs</English>
<Polish>Automatyczne zakładanie NVG</Polish>
<German>Automatisch NVGs ausrüsten</German>
<Italian>Equipaggia NVG in automatico</Italian>
<Korean>야투경 자동 창착</Korean>
<French>Equipement JVN automatique</French>
<Portuguese>Equipar NVGs automaticamente</Portuguese>
<Japanese>暗視装置の自動装備</Japanese>
<Russian>Автоматическое оснащение ПНВ</Russian>
<Spanish>Auto equipar gafas de visión nocturna</Spanish>
</Key>
<Key ID="STR_ACE_AI_AssignNVG_Description">
<English>Equips NVG in inventory during night time and unequips it during day time.\nDoes not add NVGs to inventory!</English>
<Polish>Zakłada NVG w nocy i zdejmuje je w ciągu dnia.\nNie dodaje NVG do ekwipunku!</Polish>
<German>Rüstet NVG nachts aus dem Inventar aus und entfernt es tagsüber.\nFügt keine NVGs zum Inventar hinzu!</German>
<Italian>Equipaggia NVG dall'inventario durante la notte, e li mette nell'inventario durante il giorno.\nNon aggiunge NVG all'inventario se non sono presenti.</Italian>
<Korean>야간에는 야투경을 소지품에 장착하고 주간에는 장착을 해제합니다.\n주의! 소지품에 야투경을 추가하는 것이 아닙니다!</Korean>
<French>Equipe des JVN pendant la nuit et les déséquipe le jour.\nN'ajoute pas les JVN dans l'intenvaire !</French>
<Portuguese>Equipa o NVG do inventário durante a noite e desequipa durante o dia.\nNão adiciona NVGs ao inventário!</Portuguese>
<Japanese>インベントリ内の暗視装置を夜間に装備し、日中は解除し収納します。\nこれはNVGをインベントリに追加しません。</Japanese>
<Russian>Экипирует ПНВ в ночное время и отключает его в дневное время.\nНе добавляет ПНВ в инвентарь!</Russian>
<Spanish>Equipa las gafas de visión nocturna en el inventario cuando es de noche, y las desequipa cuando es de día.\nNo añade las gafas al inventario!</Spanish>
</Key>
</Package>
</Project>

View File

@ -30,6 +30,6 @@ _vehicle == vehicle _unit
if (_unit == _x select FULLCREW_UNIT) exitWith {
_ejectVarName = format [QGVAR(ejectAction_%1_%2), _x select FULLCREW_ROLE, _x select FULLCREW_TURRETPATH];
};
} count fullCrew _vehicle;
} forEach fullCrew _vehicle;
_vehicle getVariable [_ejectVarName, false]
}

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

@ -17,13 +17,13 @@
*
* Public: No
*/
params ["_vehicle", "_group", "_type", "_value"];
TRACE_4("droneModifyWaypoint",_vehicle,_group,_type,_value);
private _index = (currentWaypoint _group) min count waypoints _group;
private _waypoint = [_group, _index];
switch (toLower _type) do {
switch (toLowerANSI _type) do {
case ("height"): {
private _pos = waypointPosition _waypoint;
_pos set [2, _value];

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;
@ -29,11 +30,39 @@ private _currentLoiterRadius = waypointLoiterRadius _waypoint;
private _currentLoiterType = waypointLoiterType _waypoint;
// Set pos to ATL
_pos set [2, if (_currentHeight >= 50) then { _currentHeight } else { 0 }];
_pos set [
2,
[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

@ -29,7 +29,7 @@
<Russian>Открыть грузовой отсек</Russian>
<Italian>Apri la rampa di carico</Italian>
<Portuguese>Abrir porta de carga</Portuguese>
<Japanese>カーゴ ドアを開く</Japanese>
<Japanese>貨物室ドアを 開く</Japanese>
<Korean>화물칸 개방</Korean>
<Chinese>開啟貨艙門</Chinese>
<Chinesesimp>开启货舱门</Chinesesimp>
@ -46,7 +46,7 @@
<Russian>Закрыть грузовой отсек</Russian>
<Italian>Chiudi la rampa di carico</Italian>
<Portuguese>Fechar porta de carga</Portuguese>
<Japanese>カーゴ ドアを閉じる</Japanese>
<Japanese>貨物室ドアを 閉じる</Japanese>
<Korean>화물칸 폐쇄</Korean>
<Chinese>關閉貨艙門</Chinese>
<Chinesesimp>关闭货舱门</Chinesesimp>
@ -61,16 +61,19 @@
<Polish>30mm Odłamkowo-Burzące - Zapalające</Polish>
<Turkish>30mm Yüksek Patlayıcı </Turkish>
<German>30mm Hochexplosiv/Brandladung</German>
<Japanese>30mm 焼夷りゅう弾</Japanese>
<Italian>30mm Esplosivo/Incendiario</Italian>
<Japanese>30mm 焼夷榴弾</Japanese>
<Czech>30mm Tříštivo-trhavá zápalná střela</Czech>
<Russian>30мм Осколочно-Фугасный Зажигательный</Russian>
<Korean>30mm 고폭소이탄</Korean>
<Portuguese>30mm Alto-Explosivo Incendiária</Portuguese>
</Key>
<Key ID="STR_ACE_Aircraft_GatlingDescriptionShortHEI">
<English>30mm HEI</English>
<Spanish>30mm AEI</Spanish>
<Chinese>30毫米高爆燃燒</Chinese>
<Chinesesimp>30mm 高爆燃烧</Chinesesimp>
<Italian>30mm HEI</Italian>
<French>30 mm HEI</French>
<Polish>30mm OB-Z</Polish>
<Turkish>30mm HEI</Turkish>
@ -79,6 +82,7 @@
<Czech>30mm HEI</Czech>
<Russian>30мм ОФЗ</Russian>
<Korean>30mm HEI</Korean>
<Portuguese>30mm AEI</Portuguese>
</Key>
<Key ID="STR_ACE_Aircraft_GatlingDescriptionAP">
<English>30mm DU Armor Piercing</English>
@ -86,13 +90,15 @@
<Chinese>30毫米貧化鈾穿甲彈 </Chinese>
<Chinesesimp>30 mm 贫铀穿甲</Chinesesimp>
<French>30 mm UA Armor Piercing</French>
<Italian>30mm AP Uranio Impoverito</Italian>
<Polish>30mm Zubożony Uran - Przebijające</Polish>
<Turkish>30mm DU Zırh Delici</Turkish>
<German>30mm abgereichertes panzerbrechendes Uraniumgeschoss</German>
<Japanese>30mm DU 徹甲弾</Japanese>
<Japanese>30mm 劣化ウラン徹甲弾</Japanese>
<Czech>30mm Protipancéřová střela z ochuzeného Uranu</Czech>
<Russian>30мм ОУ Бронебойный Снаряд</Russian>
<Korean>30mm 열화우라늄 철갑탄</Korean>
<Portuguese>30mm DU Perfurante de Blindagem</Portuguese>
</Key>
<Key ID="STR_ACE_Aircraft_GatlingDescriptionShortAP">
<English>30mm DU AP</English>
@ -100,6 +106,7 @@
<Chinese>30毫米貧鈾穿甲</Chinese>
<Chinesesimp>30mm 贫铀穿甲</Chinesesimp>
<French>30 mm UA AP</French>
<Italian>30mm AP-UI</Italian>
<Polish>30mm ZU-P</Polish>
<Turkish>30mm DU AP</Turkish>
<German>30mm DU-PB</German>
@ -107,6 +114,7 @@
<Czech>30 mm DU AP</Czech>
<Russian>30мм ОУ БС</Russian>
<Korean>30mm DU AP</Korean>
<Portuguese>30mm DU AP</Portuguese>
</Key>
<Key ID="STR_ACE_Aircraft_GatlingDescriptionCM41">
<English>30mm Combat Mix 4:1 DU:HEI</English>
@ -114,13 +122,15 @@
<Chinese>30毫米戰鬥混合彈4:1 穿甲:高爆</Chinese>
<Chinesesimp>30mm 战斗混合 穿甲/高爆 4:1</Chinesesimp>
<French>30 mm Mix de Combat 4:1 UA:HEI</French>
<Italian>30mm Misto 4:1 UI:HEI</Italian>
<Polish>30mm Mieszanka bojowa 4:1 ZU:OB-Z</Polish>
<Turkish>30mm Combat Mix 4:1 DU:HEI</Turkish>
<German>30mm Kampfmischung 4:1 DU:HEB</German>
<Japanese>30mm コンバット ミックス 4:1 DU:HEI</Japanese>
<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">
<English>30mm CM 4:1</English>
@ -128,13 +138,14 @@
<Chinese>30毫米 穿高混合 4:1</Chinese>
<Chinesesimp>30mm 穿爆混合 4:1</Chinesesimp>
<French>30 mm MdC 4:1</French>
<Italian>30mm Misto 4:1</Italian>
<Polish>30mm MB 4:1</Polish>
<Turkish>30mm CM 4:1</Turkish>
<German>30mm KM 4:1</German>
<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>
@ -142,13 +153,14 @@
<Chinese>30毫米戰鬥混合彈5:1 穿甲:高爆</Chinese>
<Chinesesimp>30 mm 战斗混合 穿甲/高爆 5:1</Chinesesimp>
<French>30 mm Mix de Combat 5:1 UA:HEI</French>
<Italian>30mm Misto 5:1 UI:HEI</Italian>
<Polish>30mm Mieszanka bojowa 5:1 ZU:OB-Z</Polish>
<Turkish>30mm Combat Mix 5:1 DU:HEI</Turkish>
<German>30mm Kampfmischung 5:1 DU:HEB</German>
<Japanese>30mm コンバット ミックス 5:1 DU:HEI</Japanese>
<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>
@ -156,13 +168,28 @@
<Chinese>30毫米 穿高混合 5:1</Chinese>
<Chinesesimp>30mm 穿爆混合 5:1</Chinesesimp>
<French>30 mm MdC 5:1</French>
<Italian>30mm Misto 5:1</Italian>
<Polish>30mm MB 5:1</Polish>
<Turkish>30mm CM 5:1</Turkish>
<German>30mm KM 5:1</German>
<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>
<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>
<Japanese>%1m 間隔で 目標を追跡します</Japanese>
</Key>
</Package>
</Project>

View File

@ -123,7 +123,9 @@ class GVAR(stats) {
stats[] = {"maximumLoad"};
displayName = "$STR_a3_rscdisplayarsenal_stat_load";
showBar = 1;
showText = 1;
barStatement = QUOTE([ARR_3((_this select 0) select 0,_this select 1,[ARR_3([ARR_2(0,500)],[ARR_2(0.01,1)],false)])] call FUNC(statBarStatement_default));
textStatement = QUOTE(call FUNC(statTextStatement_load));
tabs[] = {{3,4,5}, {}};
};
class ACE_smokeChemTTL: statBase {

View File

@ -110,7 +110,7 @@ class Cfg3DEN {
h = QUOTE(65 * ATTRIBUTE_H);
drawSideArrows = 1;
disableOverflow = 1;
columns[] = {0.05, 0.15, 0.85};
columns[] = {0.05, 0.15, 0.83, 0.87};
};
class ArrowLeft: ctrlButton {
idc = IDC_ATTRIBUTE_LIST_LEFT;

View File

@ -16,6 +16,8 @@ PREP(attributeKeyDown);
PREP(attributeLoad);
PREP(attributeMode);
PREP(attributeSelect);
PREP(baseAttachment);
PREP(baseOptic);
PREP(baseWeapon);
PREP(buttonActionsPage);
PREP(buttonCargo);
@ -96,6 +98,7 @@ PREP(statBarStatement_rateOfFIre);
PREP(statTextStatement_accuracy);
PREP(statTextStatement_explosionTime);
PREP(statTextStatement_illuminators);
PREP(statTextStatement_load);
PREP(statTextStatement_magCount);
PREP(statTextStatement_mass);
PREP(statTextStatement_rateOfFire);

View File

@ -18,6 +18,16 @@ GVAR(lastSortDirectionRight) = DESCENDING;
[QGVAR(removeDefaultLoadout), LINKFUNC(removeDefaultLoadout)] call CBA_fnc_addEventHandler;
[QGVAR(renameDefaultLoadout), LINKFUNC(renameDefaultLoadout)] call CBA_fnc_addEventHandler;
[QGVAR(refresh), {
params ["_object"];
// If the arsenal is already open, refresh arsenal display
// Deliberate == check, fail on objNull
if (!isNil QGVAR(currentBox) && {GVAR(currentBox) == _object}) then {
[true, true] call FUNC(refresh);
};
}] call CBA_fnc_addEventHandler;
[QGVAR(broadcastFace), {
params ["_unit", "_face"];
_unit setFace _face;
@ -60,7 +70,7 @@ GVAR(lastSortDirectionRight) = DESCENDING;
if (!isNil QGVAR(currentLoadoutsTab) && {GVAR(currentLoadoutsTab) == IDC_buttonSharedLoadouts}) then {
private _curSelData = _contentPanelCtrl lnbData [lnbCurSelRow _contentPanelCtrl, 1];
([_loadoutData] call FUNC(verifyLoadout)) params ["_extendedLoadout", "_nullItemsAmount", "_unavailableItemsAmount"];
([_loadoutData] call FUNC(verifyLoadout)) params ["_extendedLoadout", "_nullItemsList", "_unavailableItemsList"];
_extendedLoadout params ["_loadout"];
private _newRow = _contentPanelCtrl lnbAddRow [_playerName, _loadoutName];
@ -71,10 +81,10 @@ GVAR(lastSortDirectionRight) = DESCENDING;
_contentPanelCtrl lnbSetData [[_newRow, 1], _playerName + _loadoutName];
// Set color of row, depending if items are unavailable/missing
if (_nullItemsAmount > 0) then {
if (_nullItemsList isNotEqualTo []) then {
_contentPanelCtrl lnbSetColor [[_newRow, 1], [1, 0, 0, 0.8]];
} else {
if (_unavailableItemsAmount > 0) then {
if (_unavailableItemsList isNotEqualTo []) then {
_contentPanelCtrl lnbSetColor [[_newRow, 1], [1, 1, 1, 0.25]];
};
};
@ -98,24 +108,16 @@ GVAR(lastSortDirectionRight) = DESCENDING;
private _face = _extendedInfo getOrDefault [QGVAR(face), ""];
if (_face != "") then {
if (isMultiplayer) then {
private _id = [QGVAR(broadcastFace), [_unit, _face], QGVAR(centerFace_) + netId _unit] call CBA_fnc_globalEventJIP;
[_id, _unit] call CBA_fnc_removeGlobalEventJIP;
} else {
_unit setFace _face;
};
private _id = [QGVAR(broadcastFace), [_unit, _face], QGVAR(centerFace_) + hashValue _unit] call CBA_fnc_globalEventJIP;
[_id, _unit] call CBA_fnc_removeGlobalEventJIP;
};
// Set voice
private _voice = _extendedInfo getOrDefault [QGVAR(voice), ""];
if (_voice != "") then {
if (isMultiplayer) then {
private _id = [QGVAR(broadcastVoice), [_unit, _voice], QGVAR(centerVoice_) + netId _unit] call CBA_fnc_globalEventJIP;
[_id, _unit] call CBA_fnc_removeGlobalEventJIP;
} else {
_unit setSpeaker _voice;
};
private _id = [QGVAR(broadcastVoice), [_unit, _voice], QGVAR(centerVoice_) + hashValue _unit] call CBA_fnc_globalEventJIP;
[_id, _unit] call CBA_fnc_removeGlobalEventJIP;
};
// Set insignia
@ -137,7 +139,7 @@ GVAR(lastSortDirectionRight) = DESCENDING;
// Set voice if enabled
if (GVAR(loadoutsSaveVoice)) then {
_extendedInfo set [QGVAR(voice), speaker _unit];
_extendedInfo set [QGVAR(voice), (speaker _unit) call EFUNC(common,getConfigName)];
};
// Set insignia if enabled

View File

@ -9,60 +9,13 @@ PREP_RECOMPILE_START;
#include "XEH_PREP.hpp"
PREP_RECOMPILE_END;
#include "initSettings.sqf"
#include "initSettings.inc.sqf"
// Arsenal events
[QGVAR(statsToggle), {
params ["_display", "_showStats"];
private _statsCtrlGroupCtrl = _display displayCtrl IDC_statsBox;
private _statsPreviousPageCtrl = _display displayCtrl IDC_statsPreviousPage;
private _statsNextPageCtrl = _display displayCtrl IDC_statsNextPage;
private _statsCurrentPageCtrl = _display displayCtrl IDC_statsCurrentPage;
{
_x ctrlShow (GVAR(showStats) && {_showStats});
} forEach [
_statsCtrlGroupCtrl,
_statsPreviousPageCtrl,
_statsNextPageCtrl,
_statsCurrentPageCtrl
];
}] call CBA_fnc_addEventHandler;
[QGVAR(statsChangePage), {
_this call FUNC(buttonStatsPage);
}] call CBA_fnc_addEventHandler;
[QGVAR(displayStats), {
_this call FUNC(handleStats);
}] call CBA_fnc_addEventHandler;
[QGVAR(actionsChangePage), {
_this call FUNC(buttonActionsPage);
}] call CBA_fnc_addEventHandler;
[QGVAR(displayActions), {
_this call FUNC(handleActions);
}] call CBA_fnc_addEventHandler;
[QGVAR(actionsToggle), {
params ["_display", "_showActions"];
private _actionsCtrlGroupCtrl = _display displayCtrl IDC_actionsBox;
private _actionsPreviousPageCtrl = _display displayCtrl IDC_actionsPreviousPage;
private _actionsNextPageCtrl = _display displayCtrl IDC_actionsNextPage;
private _actionsCurrentPageCtrl = _display displayCtrl IDC_actionsCurrentPage;
{
_x ctrlShow (GVAR(showActions) && {_showActions});
} forEach [
_actionsCtrlGroupCtrl,
_actionsPreviousPageCtrl,
_actionsNextPageCtrl,
_actionsCurrentPageCtrl
];
}] call CBA_fnc_addEventHandler;
[QGVAR(statsChangePage), LINKFUNC(buttonStatsPage)] call CBA_fnc_addEventHandler;
[QGVAR(displayStats), LINKFUNC(handleStats)] call CBA_fnc_addEventHandler;
[QGVAR(actionsChangePage), LINKFUNC(buttonActionsPage)] call CBA_fnc_addEventHandler;
[QGVAR(displayActions), LINKFUNC(handleActions)] call CBA_fnc_addEventHandler;
call FUNC(compileActions);
call FUNC(compileSorts);
@ -83,8 +36,59 @@ call FUNC(compileStats);
EGVAR(common,blockItemReplacement) = false;
}] call CBA_fnc_addEventHandler;
[QGVAR(cargoChanged), {
params ["_display"];
// Only update actions if necessary, this can get performance-intensive using the arrow keys
if (!GVAR(updateActionsOnCargoChange)) exitWith {};
private _actionInfo = [_display];
_actionInfo append GVAR(actionInfo);
[QGVAR(displayActions), _actionInfo] call CBA_fnc_localEvent;
}] call CBA_fnc_addEventHandler;
// Setup Tools tab
[keys (uiNamespace getVariable [QGVAR(configItemsTools), createHashMap]), LLSTRING(toolsTab), TOOLS_TAB_ICON, -1, true] call FUNC(addRightPanelButton);
// TODO: make IDCs able to match IDX with simple math?
GVAR(idxMap) = createHashMapFromArray [
[IDC_buttonPrimaryWeapon, IDX_VIRT_PRIMARY_WEAPONS],
[IDC_buttonHandgun, IDX_VIRT_HANDGUN_WEAPONS],
[IDC_buttonSecondaryWeapon, IDX_VIRT_SECONDARY_WEAPONS],
[IDC_buttonHeadgear, IDX_VIRT_HEADGEAR],
[IDC_buttonUniform, IDX_VIRT_UNIFORM],
[IDC_buttonVest, IDX_VIRT_VEST],
[IDC_buttonBackpack, IDX_VIRT_BACKPACK],
[IDC_buttonGoggles, IDX_VIRT_GOGGLES],
[IDC_buttonNVG, IDX_VIRT_NVG],
[IDC_buttonBinoculars, IDX_VIRT_BINO],
[IDC_buttonMap, IDX_VIRT_MAP],
[IDC_buttonGPS, IDX_VIRT_COMMS],
[IDC_buttonRadio, IDX_VIRT_RADIO],
[IDC_buttonCompass, IDX_VIRT_COMPASS],
[IDC_buttonWatch, IDX_VIRT_WATCH]
];
// Make new hashmaps for face/voice/insignia so mission makers can disable them
// Copies of hashmaps aren't final
GVAR(faceCache) = +(uiNamespace getVariable QGVAR(faceCache));
GVAR(voiceCache) = +(uiNamespace getVariable QGVAR(voiceCache));
GVAR(insigniaCache) = +(uiNamespace getVariable QGVAR(insigniaCache));
// Get mission/campaign insignias
// BIS_fnc_setUnitInsignia will look in mission config, then campaign, then global config last, so overwrite accordingly
private _insigniaCondition = toString {
if (isNumber (_x >> "scope")) then {
getNumber (_x >> "scope") == 2
} else {
true
};
};
// Ref fnc_addListBoxItem, 0/nil = configFile, 1 = campaignConfigFile, 2 = missionConfigFile
{
GVAR(insigniaCache) set [_x, 1];
} forEach (_insigniaCondition configClasses (campaignConfigFile >> "CfgUnitInsignia"));
{
GVAR(insigniaCache) set [_x, 2];
} forEach (_insigniaCondition configClasses (missionConfigFile >> "CfgUnitInsignia"));
ADDON = true;

View File

@ -155,6 +155,8 @@
#define IDC_statsNextPage 53
#define IDC_statsCurrentPage 54
#define IDC_actionsBox 90
#define IDC_actionsBackground1 90010
#define IDC_actionsBackground2 90011
#define IDC_actionsText1 9001
#define IDC_actionsButton1 9002
#define IDC_actionsText2 9003
@ -268,6 +270,7 @@
#define IDX_VIRT_UNIQUE_UNKNOWN_ITEMS 25
// Indexes of current items array
// Should match IDX_VIRT_X macros for any left panel tabs
#define IDX_CURR_PRIMARY_WEAPON 0
#define IDX_CURR_SECONDARY_WEAPON 1
#define IDX_CURR_HANDGUN_WEAPON 2
@ -487,3 +490,6 @@ if (!isNil QGVAR(customRightPanelButtons)) then {\
_contentPanelCtrl lnbSetPicture [[_newRow, 7], getText (configFile >> "CfgVehicles" >> (_loadout select IDX_LOADOUT_BACKPACK) select 0 >> "picture")];\
_contentPanelCtrl lnbSetPicture [[_newRow, 8], getText (_cfgWeapons >> _loadout select IDX_LOADOUT_HEADGEAR >> "picture")];\
_contentPanelCtrl lnbSetPicture [[_newRow, 9], getText (configFile >> "CfgGlasses" >> _loadout select IDX_LOADOUT_GOGGLES >> "picture")];
#define ACTION_TYPE_TEXT 0
#define ACTION_TYPE_BUTTON 1

View File

@ -1,4 +1,5 @@
#include "..\script_component.hpp"
#include "..\defines.hpp"
/*
* Author: johnb43
* Adds custom action buttons.
@ -10,6 +11,7 @@
* 3: Actions <ARRAY of ARRAYS>
* 4: Condition <CODE> (default: {true})
* 5: Scope editor <NUMBER> (default: 2)
* 6: Update when cargo content changes <BOOL> (default: false)
*
* Return Value:
* 0: Array of IDs <ARRAY of STRINGS>
@ -30,7 +32,8 @@ params [
["_title", "", [""]],
["_actions", [], [[]]],
["_rootCondition", {true}, [{}]],
["_scopeEditor", 2, [0]]
["_scopeEditor", 2, [0]],
["_updateOnCargoChange", false, [false]]
];
// Compile actions from config (in case this is called before preInit)
@ -38,13 +41,13 @@ call FUNC(compileActions);
// Skip if not allowed in editor and in editor
if (is3DEN && {_scopeEditor != 2}) exitWith {
TRACE_1("Skipping action because in editor", _rootClass);
TRACE_1("Skipping action because in editor",_rootClass);
[]
};
// Class can't contain ~, because it's used for formatting result
if ("~" in _rootClass) exitWith {
TRACE_1("Classname can't contain '~'", _rootClass);
TRACE_1("Classname can't contain '~'",_rootClass);
[]
};
@ -65,7 +68,7 @@ private _fnc_addToGroup = {
// Don't allow two of the same class
if (_group findIf {(_x select 0) == _class} != -1) then {
TRACE_1("An action with this ID already exists", _class);
TRACE_1("An action with this ID already exists",_class);
continue;
};
@ -119,4 +122,8 @@ private _group = [];
};
} forEach _tabs;
if (_updateOnCargoChange) then {
GVAR(updateActionsOnCargoChange) = true;
};
_return

View File

@ -20,8 +20,9 @@
params [["_name", "", [""]], ["_loadout", [], [[]]], ["_global", false, [false]]];
if (_global) then {
[QGVAR(addDefaultLoadout), [_name, _loadout]] call CBA_fnc_remoteEvent;
if (_global) exitWith {
private _eventID = format [QGVAR(loadouts_%1), _name];
[QGVAR(addDefaultLoadout), [_name, _loadout], _eventID] call CBA_fnc_globalEventJIP;
};
private _extendedInfo = createHashMap;

View File

@ -6,9 +6,10 @@
*
* Arguments:
* 0: Config category, must be "CfgWeapons", "CfgVehicles", "CfgMagazines", "CfgVoice" or "CfgUnitInsignia" <STRING>
* 1: Classname <STRING>
* 1: Classname (must be in config case) <STRING>
* 2: Panel control <CONTROL>
* 3: Name of the picture entry in that Cfg class <STRING> (default: "picture")
* 4: Config root <NUMBER> (default: 0 -> configFile)
*
* Return Value:
* None
@ -19,9 +20,9 @@
* Public: Yes
*/
params ["_configCategory", "_className", "_ctrlPanel", ["_pictureEntryName", "picture", [""]]];
params ["_configCategory", "_className", "_ctrlPanel", ["_pictureEntryName", "picture", [""]], ["_configRoot", 0, [0]]];
private _skip = GVAR(favoritesOnly) && {!(_className in GVAR(currentItems))} && {!((toLower _className) in GVAR(favorites))};
private _skip = GVAR(favoritesOnly) && {!(_className in GVAR(currentItems))} && {!((toLowerANSI _className) in GVAR(favorites))};
if (_skip) then {
switch (GVAR(currentLeftPanel)) do {
case IDC_buttonPrimaryWeapon: {
@ -41,10 +42,10 @@ if (_skip) then {
if (_skip) exitWith {};
// Sanitise key, as it's public; If not in cache, find info and cache it for later use
((uiNamespace getVariable QGVAR(addListBoxItemCache)) getOrDefaultCall [_configCategory + _className, {
// If not in cache, find info and cache it for later use
((uiNamespace getVariable QGVAR(addListBoxItemCache)) getOrDefaultCall [_configCategory + _className + str _configRoot, {
// Get classname (config case), display name, picture and DLC
private _configPath = configFile >> _configCategory >> _className;
private _configPath = ([configFile, campaignConfigFile, missionConfigFile] select _configRoot) >> _configCategory >> _className;
private _dlcName = _configPath call EFUNC(common,getAddon);
// If _pictureEntryName is empty, then this item has no picture (e.g. faces)
@ -57,7 +58,7 @@ _ctrlPanel lbSetPicture [_lbAdd, _itemPicture];
_ctrlPanel lbSetPictureRight [_lbAdd, ["", _modPicture] select GVAR(enableModIcons)];
_ctrlPanel lbSetTooltip [_lbAdd, format ["%1\n%2", _displayName, _className]];
if ((toLower _className) in GVAR(favorites)) then {
if ((toLowerANSI _className) in GVAR(favorites)) then {
_ctrlPanel lbSetColor [_lbAdd, FAVORITES_COLOR];
_ctrlPanel lbSetSelectColor [_lbAdd, FAVORITES_COLOR];
};

View File

@ -66,7 +66,8 @@ _items = _items select {
_x isKindOf ["CBA_MiscItem", _cfgWeapons] && {getNumber (_configItemInfo >> "type") in [TYPE_MUZZLE, TYPE_OPTICS, TYPE_FLASHLIGHT, TYPE_BIPOD]} ||
{getNumber (_configItemInfo >> "type") in [TYPE_FIRST_AID_KIT, TYPE_MEDIKIT, TYPE_TOOLKIT]} ||
{getText (_cfgWeapons >> _x >> "simulation") == "ItemMineDetector"} ||
{getNumber (_cfgMagazines >> _x >> "ACE_isUnique") == 1}
{getNumber (_cfgMagazines >> _x >> "ACE_isUnique") == 1} ||
{getNumber (_cfgMagazines >> _x >> "ACE_asItem") == 1}
};
GVAR(customRightPanelButtons) set [_position, [_items apply {_x call EFUNC(common,getConfigName)}, _picture, _tooltip, _moveOnOverwrite]];

View File

@ -72,7 +72,7 @@ private _fnc_addToTabs = {
_currentTab pushBack _sort;
_return pushBack _sortName;
} else {
TRACE_1("A sort with this ID already exists", _sortName);
TRACE_1("A sort with this ID already exists",_sortName);
};
} forEach _tabsToAddTo;
};

View File

@ -77,7 +77,7 @@ private _fnc_addToTabs = {
// Find if there is an entry with same ID
if (_currentTab findIf {_x select 5 == _statName} != -1) then {
TRACE_1("A stat with this ID already exists", _statName);
TRACE_1("A stat with this ID already exists",_statName);
} else {
_stat = +_finalArray;
_stat set [5, _statName];
@ -109,11 +109,10 @@ private _tabToChange = [];
{
_x params ["_tab", "_tabSide"];
_tabToChange = if (_tabSide == "R") then {
_tabToChange = [
GVAR(statsListLeftPanel),
GVAR(statsListRightPanel)
} else {
GVAR(statsListLeftPanel)
};
] select (_tabSide == "R");
_stats = _tabToChange select _tab;

View File

@ -125,6 +125,8 @@ if (_items isEqualType true) then {
_object setVariable [QGVAR(virtualItems), _cargo, _global];
// If the arsenal is already open, refresh arsenal display
if (!isNil QGVAR(currentBox) && {GVAR(currentBox) isEqualTo _object}) then {
[true, true] call FUNC(refresh);
if (_global) then {
[QGVAR(refresh), _object] call CBA_fnc_globalEvent;
} else {
[QGVAR(refresh), _object] call CBA_fnc_localEvent;
};

View File

@ -46,6 +46,7 @@ private _cfgWeapons = configFile >> "CfgWeapons";
private _cfgMagazines = configFile >> "CfgMagazines";
private _cfgVehicles = configFile >> "CfgVehicles";
private _cfgGlasses = configFile >> "CfgGlasses";
private _dlcName = "";
// Exit with current items (no specific category)
if (_category == IDX_CAT_ALL) exitWith {
@ -73,6 +74,12 @@ if (_category == IDX_CAT_ALL) exitWith {
_listbox lnbSetData [[_index, 1], _x];
_listbox lnbSetPicture [[_index, 0], getText (_config >> "picture")];
_listbox lnbSetTooltip [[_index, 0], _x];
_dlcName = _config call EFUNC(common,getAddon);
if (_dlcName != "") then {
_listbox lnbSetPicture [[_index, 2], (modParams [_dlcName, ["logo"]]) param [0, ""]];
};
};
} forEach _attributeItems;
@ -130,12 +137,20 @@ private _config = _cfgClass;
_alpha = 0.5;
};
_index = _listbox lnbAddRow ["", _displayName, _symbol];
_index = _listbox lnbAddRow ["", _displayName, "", _symbol];
_listbox lnbSetData [[_index, 1], _x];
_listbox lnbSetPicture [[_index, 0], getText (_config >> _x >> "picture")];
_listbox lnbSetTooltip [[_index, 0], _x];
_listbox lnbSetColor [[_index, 1], [1, 1, 1, _alpha]];
_listbox lnbSetColor [[_index, 2], [1, 1, 1, _alpha]];
_listbox lnbSetColor [[_index, 3], [1, 1, 1, _alpha]];
// Mod icon is in column 2
_dlcName = (_config >> _x) call EFUNC(common,getAddon);
if (_dlcName != "") then {
_listbox lnbSetPicture [[_index, 2], (modParams [_dlcName, ["logo"]]) param [0, ""]];
_listbox lnbSetPictureColor [[_index, 2], [1, 1, 1, _alpha]];
};
};
} forEach (keys _categoryItems);

View File

@ -24,14 +24,12 @@ if (_mode > 0) then {
// Blacklist: add full arsenal and take items away
[_object, true, true] call FUNC(initBox);
// Need to delay removal by 2 frames
// Wait until all items have been added, so that the blacklisted items can be removed
[{
[{
params ["_object", "_items"];
[_object, _items, true] call FUNC(removeVirtualItems);
}, _this] call CBA_fnc_execNextFrame;
}, [_object, _items]] call CBA_fnc_execNextFrame;
!isNil {(_this select 0) getVariable QGVAR(virtualItems)}
}, {
[_this select 0, _this select 1, true] call FUNC(removeVirtualItems);
}, [_object, _items], 20] call CBA_fnc_waitUntilAndExecute; // 20s timeout in case of failure
} else {
// Exit on whitelist mode with no items
if (_items isEqualTo []) exitWith {};

View File

@ -35,9 +35,10 @@ if (_addItem && {_itemIndex == -1}) exitWith {
_attributeItems pushBack _itemClassname;
// Change symbol and increase alpha
_listbox lnbSetText [[_currentRow, 2], [SYMBOL_ITEM_VIRTUAL, SYMBOL_ITEM_REMOVE] select _attributeMode];
_listbox lnbSetText [[_currentRow, 3], [SYMBOL_ITEM_VIRTUAL, SYMBOL_ITEM_REMOVE] select _attributeMode];
_listbox lnbSetColor [[_currentRow, 1], [1, 1, 1, 1]];
_listbox lnbSetColor [[_currentRow, 2], [1, 1, 1, 1]];
_listbox lnbSetPictureColor [[_currentRow, 2], [1, 1, 1, 1]]; // mod icon is in column 2
_listbox lnbSetColor [[_currentRow, 3], [1, 1, 1, 1]];
};
// Remove item if in list
@ -45,7 +46,8 @@ if (!_addItem && {_itemIndex != -1}) exitWith {
_attributeItems deleteAt _itemIndex;
// Change symbol and reduce alpha
_listbox lnbSetText [[_currentRow, 2], SYMBOL_ITEM_NONE];
_listbox lnbSetText [[_currentRow, 3], SYMBOL_ITEM_NONE];
_listbox lnbSetColor [[_currentRow, 1], [1, 1, 1, 0.5]];
_listbox lnbSetColor [[_currentRow, 2], [1, 1, 1, 0.5]];
_listbox lnbSetPictureColor [[_currentRow, 2], [1, 1, 1, 0.5]]; // mod icon is in column 2
_listbox lnbSetColor [[_currentRow, 3], [1, 1, 1, 0.5]];
};

View File

@ -0,0 +1,59 @@
#include "..\script_component.hpp"
/*
* Author: Jonpas, LinkIsGrim
* Returns base attachment for CBA scripted attachment
* Adapted from CBA_fnc_switchableAttachments
*
* Arguments:
* 0: Attachment <STRING>
*
* Return Value:
* Base attachment <STRING>
*
* Example:
* "ACE_acc_pointer_green_IR" call ace_arsenal_fnc_baseAttachment
*
* Public: Yes
*/
params [["_item", "", [""]]];
TRACE_1("looking up base attachment",_item);
private _switchableClasses = [];
private _cfgWeapons = configfile >> "CfgWeapons";
private _config = _cfgWeapons >> _item;
_item = configName _config;
// If the switch config entries are inherited, ignore
if (
(inheritsFrom (_config >> "MRT_SwitchItemNextClass") isNotEqualTo _config) ||
{inheritsFrom (_config >> "MRT_SwitchItemPrevClass") isNotEqualTo _config}
) exitWith {
_item // return
};
while {
_config = _cfgWeapons >> getText (_config >> "MRT_SwitchItemNextClass");
isClass _config && {_switchableClasses pushBackUnique configName _config != -1}
} do {};
_config = _cfgWeapons >> _item;
private _backward = [];
while {
_config = _cfgWeapons >> getText (_config >> "MRT_SwitchItemPrevClass");
isClass _config && {_backward pushBackUnique configName _config != -1}
} do {};
_switchableClasses append _backward;
_switchableClasses = _switchableClasses arrayIntersect _switchableClasses;
{
if (getNumber (_cfgWeapons >> _x >> "scope") == 2) exitWith {
TRACE_2("found class",_item,_x);
_item = _x;
};
} forEach _switchableClasses;
_item

View File

@ -0,0 +1,32 @@
#include "..\script_component.hpp"
/*
* Author: Jonpas, LinkIsGrim
* Returns base optic for CBA scripted optics (PIP and 2D)
*
* Arguments:
* 0: Optic <STRING>
*
* Return Value:
* Base optic <STRING>
*
* Example:
* "CUP_optic_Elcan_SpecterDR_black_PIP" call ace_arsenal_fnc_baseOptic
*
* Public: Yes
*/
params [["_optic", "", [""]]];
// PIP
private _baseClasses = configProperties [configFile >> "CBA_PIPItems", "getText _x == _optic"];
// Carry Handle
{
_baseClasses append (configProperties [_x, "getText _x == _optic"]);
} forEach configProperties [configFile >> "CBA_CarryHandleTypes"];
if (_baseClasses isNotEqualTo []) then {
_optic = configName (_baseClasses select 0);
};
_optic

View File

@ -19,7 +19,7 @@
params [["_weapon", "", [""]]];
// Check if item is cached
(uiNamespace getVariable QGVAR(baseWeaponNameCache)) getOrDefaultCall [toLower _weapon, {
(uiNamespace getVariable QGVAR(baseWeaponNameCache)) getOrDefaultCall [toLowerANSI _weapon, {
private _cfgWeapons = configfile >> "CfgWeapons";
private _config = _cfgWeapons >> _weapon;

View File

@ -2,12 +2,12 @@
#include "..\defines.hpp"
/*
* Author: Brett Mayson
* Handles the previous / next page buttons for actions
* Handles the previous / next page buttons for actions.
*
* Arguments:
* 0: Arsenal display <DISPLAY>
* 1: Actions control <CONTROL>
* 2: Previous or next <BOOL> (false = previous, true = next)
* 1: Actions page <CONTROL>
* 2: Previous (false) or next (true) page <BOOL>
*
* Return Value:
* None
@ -17,10 +17,11 @@
params ["_display", "_control", "_nextPage"];
TRACE_1("control enabled", ctrlEnabled _control);
TRACE_1("control enabled",ctrlEnabled _control);
if !(ctrlEnabled _control) exitWith {};
GVAR(currentActionPage) = GVAR(currentActionPage) + ([-1, 1] select _nextPage);
GVAR(actionsInfo) params ["_panelControl", "_curSel", "_itemCfg"];
[QGVAR(displayActions), [_display, _panelControl, _curSel, _itemCfg]] call CBA_fnc_localEvent;

View File

@ -47,8 +47,8 @@ private _container = switch (GVAR(currentLeftPanel)) do {
};
};
/// Get all items from container (excluding container itself)
_containerItems = [GVAR(center), 0, 3, 0, 0, false] call EFUNC(common,uniqueUnitItems);
// Get all items from container
_containerItems = uniformItems GVAR(center);
// Update currentItems
GVAR(currentItems) set [IDX_CURR_UNIFORM_ITEMS, ((getUnitLoadout GVAR(center)) select IDX_LOADOUT_UNIFORM) param [1, []]];
@ -75,8 +75,8 @@ private _container = switch (GVAR(currentLeftPanel)) do {
};
};
// Get all items from container (excluding container itself)
_containerItems = [GVAR(center), 0, 0, 3, 0, false] call EFUNC(common,uniqueUnitItems);
// Get all items from container
_containerItems = vestItems GVAR(center);
// Update currentItems
GVAR(currentItems) set [IDX_CURR_VEST_ITEMS, ((getUnitLoadout GVAR(center)) select IDX_LOADOUT_VEST) param [1, []]];
@ -103,8 +103,8 @@ private _container = switch (GVAR(currentLeftPanel)) do {
};
};
// Get all items from container (excluding container itself)
_containerItems = [GVAR(center), 0, 0, 0, 3, false] call EFUNC(common,uniqueUnitItems);
// Get all items from container
_containerItems = backpackItems GVAR(center);
// Update currentItems
GVAR(currentItems) set [IDX_CURR_BACKPACK_ITEMS, ((getUnitLoadout GVAR(center)) select IDX_LOADOUT_BACKPACK) param [1, []]];
@ -117,7 +117,7 @@ private _container = switch (GVAR(currentLeftPanel)) do {
};
// Find out how many items of that type there are and update the number displayed
_ctrlList lnbSetText [[_lnbCurSel, 2], str (_containerItems getOrDefault [_item, 0])];
_ctrlList lnbSetText [[_lnbCurSel, 2], str ({_item == _x} count _containerItems)];
[QGVAR(cargoChanged), [_display, _item, _addOrRemove, GVAR(shiftState)]] call CBA_fnc_localEvent;

View File

@ -52,10 +52,20 @@ private _ctrl = controlNull;
IDC_buttonCurrentMag2,
IDC_iconBackgroundCurrentMag,
IDC_iconBackgroundCurrentMag2,
IDC_statsBox,
IDC_statsPreviousPage,
IDC_statsNextPage,
IDC_statsCurrentPage
IDC_statsCurrentPage,
IDC_actionsBox,
IDC_actionsPreviousPage,
IDC_actionsNextPage,
IDC_actionsCurrentPage
];
[QGVAR(statsToggle), [_display, _showToggle]] call CBA_fnc_localEvent;
[QGVAR(actionsToggle), [_display, _showToggle]] call CBA_fnc_localEvent;
if (!_showToggle) exitWith {};
// When showing the stats/actions again, update them to fit with currently selected item
GVAR(actionsInfo) params ["_control", "_curSel", "_itemCfg"];
[QGVAR(displayStats), [_display, _control, _curSel, _itemCfg]] call CBA_fnc_localEvent;
[QGVAR(displayActions), [_display, _control, _curSel, _itemCfg]] call CBA_fnc_localEvent;

View File

@ -43,11 +43,7 @@ private _loadoutIndex = _data findIf {(_x select 0) == _loadoutName};
(_data select _loadoutIndex) set [0, _editBoxContent];
if (GVAR(currentLoadoutsTab) == IDC_buttonDefaultLoadouts) then {
if (is3DEN) then {
set3DENMissionAttributes [[QGVAR(DummyCategory), QGVAR(DefaultLoadoutsListAttribute), GVAR(defaultLoadoutsList)]];
} else {
[QGVAR(renameDefaultLoadout), [_loadoutName, _editBoxContent]] call CBA_fnc_remoteEvent;
};
set3DENMissionAttributes [[QGVAR(DummyCategory), QGVAR(DefaultLoadoutsListAttribute), GVAR(defaultLoadoutsList)]];
};
private _currentLoadoutsTab = str GVAR(currentLoadoutsTab);

View File

@ -17,11 +17,11 @@
params ["_display", "_control", "_nextPage"];
TRACE_1("control enabled", ctrlEnabled _control);
TRACE_1("control enabled",ctrlEnabled _control);
if !(ctrlEnabled _control) exitWith {};
GVAR(currentStatPage) = [GVAR(currentStatPage) - 1, GVAR(currentStatPage) + 1] select _nextPage;
GVAR(currentStatPage) = GVAR(currentStatPage) + ([-1, 1] select _nextPage);
GVAR(statsInfo) params ["_isLeftPanel", "_panelControl", "_curSel", "_itemCfg"];
GVAR(statsInfo) params ["", "_panelControl", "_curSel", "_itemCfg"];
[QGVAR(displayStats), [_display, _panelControl, _curSel, _itemCfg]] call CBA_fnc_localEvent;

View File

@ -1,4 +1,5 @@
#include "..\script_component.hpp"
#include "..\defines.hpp"
/*
* Author: Brett Mayson
* Create the internal actions arrays when needed for the first time.
@ -37,6 +38,8 @@ private _actionList = [
private _configGroupEntries = "true" configClasses (configFile >> QGVAR(actions));
GVAR(updateActionsOnCargoChange) = false;
{
private _scopeEditor = getNumber (_x >> "scopeEditor");
@ -48,6 +51,10 @@ private _configGroupEntries = "true" configClasses (configFile >> QGVAR(actions)
private _rootDisplayName = getText (_x >> "displayName");
private _rootCondition = getText (_x >> "condition");
private _rootTabs = getArray (_x >> "tabs");
private _updateOnCargoChanged = getNumber (_x >> "updateOnCargoChanged");
if (_updateOnCargoChanged > 0) then {
GVAR(updateActionsOnCargoChange) = true;
};
if (_rootCondition != "") then {
_rootCondition = compile _rootCondition;

View File

@ -101,7 +101,7 @@ private _priority = 0;
(_finalArray select 4) set [1, compile (getText (_x >> "textStatement"))];
};
TRACE_3("stats array", _finalArray, _leftTabsList, _rightTabsList);
TRACE_3("stats array",_finalArray,_leftTabsList,_rightTabsList);
if (_leftTabsList isNotEqualTo []) then {
[_statsListLeftPanel, _leftTabsList, "L"] call _fnc_addToTabs;

View File

@ -1,7 +1,7 @@
#include "..\script_component.hpp"
#include "..\defines.hpp"
/*
* Author: Alganthe, johnb43
* Author: Alganthe, johnb43, LinkIsGrim
* Fills left panel.
*
* Arguments:
@ -17,21 +17,28 @@
params ["_display", "_control", ["_animate", true]];
private _ctrlIDC = ctrlIDC _control;
private _ctrlPanel = _display displayCtrl IDC_leftTabContent;
private _idxVirt = GVAR(idxMap) getOrDefault [_ctrlIDC, -1, true];
// Fade old control background
if (!isNil QGVAR(currentLeftPanel)) then {
private _previousCtrlBackground = _display displayCtrl (GVAR(currentLeftPanel) - 1);
_previousCtrlBackground ctrlSetFade 1;
_previousCtrlBackground ctrlCommit ([0, FADE_DELAY] select _animate);
// When switching tabs, clear searchbox
if (GVAR(currentLeftPanel) != _ctrlIDC) then {
(_display displayCtrl IDC_leftSearchbar) ctrlSetText "";
(_display displayCtrl IDC_rightSearchbar) ctrlSetText "";
};
};
// Show new control background
private _ctrlIDC = ctrlIDC _control;
private _ctrlBackground = _display displayCtrl (_ctrlIDC - 1);
_ctrlBackground ctrlSetFade 0;
_ctrlBackground ctrlCommit ([0, FADE_DELAY] select _animate);
private _ctrlPanel = _display displayCtrl IDC_leftTabContent;
// Force a "refresh" animation of the panel
if (_animate) then {
_ctrlPanel ctrlSetFade 1;
@ -41,212 +48,82 @@ if (_animate) then {
};
_ctrlPanel lbSetCurSel -1;
// Purge old data
lbClear _ctrlPanel;
// Handle icons and filling
private _selectedItem = switch (true) do {
// Primary weapons, secondary weapons, handgun weapons
case (_ctrlIDC in [IDC_buttonPrimaryWeapon, IDC_buttonHandgun, IDC_buttonSecondaryWeapon]): {
// Purge old data
lbClear _ctrlPanel;
// Add "Empty" entry
private _addEmpty = _ctrlPanel lbAdd format [" <%1>", localize "str_empty"];
_ctrlPanel lbSetValue [_addEmpty, -1];
// Add selected tab's weapons
private _index = [IDC_buttonPrimaryWeapon, IDC_buttonSecondaryWeapon, IDC_buttonHandgun] find _ctrlIDC;
{
["CfgWeapons", _x, _ctrlPanel] call FUNC(addListBoxItem);
} forEach (keys ((GVAR(virtualItems) get IDX_VIRT_WEAPONS) get _index));
GVAR(currentItems) select _index
};
// Uniforms, vests, backpacks
case (_ctrlIDC in [IDC_buttonUniform, IDC_buttonVest, IDC_buttonBackpack]): {
// Purge old data
lbClear _ctrlPanel;
// Add "Empty" entry
private _addEmpty = _ctrlPanel lbAdd format [" <%1>", localize "str_empty"];
_ctrlPanel lbSetValue [_addEmpty, -1];
switch (_ctrlIDC) do {
// Add uniforms
case IDC_buttonUniform: {
{
["CfgWeapons", _x, _ctrlPanel] call FUNC(addListBoxItem);
} forEach (keys (GVAR(virtualItems) get IDX_VIRT_UNIFORM));
GVAR(currentItems) select IDX_CURR_UNIFORM
};
// Add vests
case IDC_buttonVest: {
{
["CfgWeapons", _x, _ctrlPanel] call FUNC(addListBoxItem);
} forEach (keys (GVAR(virtualItems) get IDX_VIRT_VEST));
GVAR(currentItems) select IDX_CURR_VEST
};
// Add backpacks
case IDC_buttonBackpack: {
{
["CfgVehicles", _x, _ctrlPanel] call FUNC(addListBoxItem);
} forEach (keys (GVAR(virtualItems) get IDX_VIRT_BACKPACK));
GVAR(currentItems) select IDX_CURR_BACKPACK
};
};
};
// Other
default {
// Don't reset right panel selection if left tab is binos
if (_ctrlIDC != IDC_buttonBinoculars) then {
GVAR(currentRightPanel) = nil;
};
lbClear _ctrlPanel;
// For every left tab except faces and voices, add "Empty" entry
if !(_ctrlIDC in [IDC_buttonFace, IDC_buttonVoice]) then {
private _addEmpty = _ctrlPanel lbAdd format [" <%1>", localize "str_empty"];
_ctrlPanel lbSetValue [_addEmpty, -1];
};
switch (_ctrlIDC) do {
// Headgear
case IDC_buttonHeadgear: {
{
["CfgWeapons", _x, _ctrlPanel] call FUNC(addListBoxItem);
} forEach (keys (GVAR(virtualItems) get IDX_VIRT_HEADGEAR));
GVAR(currentItems) select IDX_CURR_HEADGEAR
};
// Facewear
case IDC_buttonGoggles: {
{
["CfgGlasses", _x, _ctrlPanel] call FUNC(addListBoxItem);
} forEach (keys (GVAR(virtualItems) get IDX_VIRT_GOGGLES));
GVAR(currentItems) select IDX_CURR_GOGGLES
};
// NVGs
case IDC_buttonNVG: {
{
["CfgWeapons", _x, _ctrlPanel] call FUNC(addListBoxItem);
} forEach (keys (GVAR(virtualItems) get IDX_VIRT_NVG));
GVAR(currentItems) select IDX_CURR_NVG
};
// Binoculars
case IDC_buttonBinoculars: {
{
["CfgWeapons", _x, _ctrlPanel] call FUNC(addListBoxItem);
} forEach (keys (GVAR(virtualItems) get IDX_VIRT_BINO));
GVAR(currentItems) select IDX_CURR_BINO
};
// Maps
case IDC_buttonMap: {
{
["CfgWeapons", _x, _ctrlPanel] call FUNC(addListBoxItem);
} forEach (keys (GVAR(virtualItems) get IDX_VIRT_MAP));
GVAR(currentItems) select IDX_CURR_MAP
};
// Compasses
case IDC_buttonCompass: {
{
["CfgWeapons", _x, _ctrlPanel] call FUNC(addListBoxItem);
} forEach (keys (GVAR(virtualItems) get IDX_VIRT_COMPASS));
GVAR(currentItems) select IDX_CURR_COMPASS
};
// Radios
case IDC_buttonRadio: {
{
["CfgWeapons", _x, _ctrlPanel] call FUNC(addListBoxItem);
} forEach (keys (GVAR(virtualItems) get IDX_VIRT_RADIO));
GVAR(currentItems) select IDX_CURR_RADIO
};
// Watches
case IDC_buttonWatch: {
{
["CfgWeapons", _x, _ctrlPanel] call FUNC(addListBoxItem);
} forEach (keys (GVAR(virtualItems) get IDX_VIRT_WATCH));
GVAR(currentItems) select IDX_CURR_WATCH
};
// GPS and UAV Terminals
case IDC_buttonGPS: {
{
["CfgWeapons", _x, _ctrlPanel] call FUNC(addListBoxItem);
} forEach (keys (GVAR(virtualItems) get IDX_VIRT_COMMS));
GVAR(currentItems) select IDX_CURR_COMMS
};
// Faces
case IDC_buttonFace: {
private _lbAdd = -1;
{
_y params ["_displayName", "_modPicture"];
_lbAdd = _ctrlPanel lbAdd _displayName;
_ctrlPanel lbSetData [_lbAdd, _x];
_ctrlPanel lbSetTooltip [_lbAdd, format ["%1\n%2", _displayName, _x]];
_ctrlPanel lbSetPictureRight [_lbAdd, _modPicture];
} forEach (uiNamespace getVariable QGVAR(faceCache));
GVAR(currentFace)
};
// Voices
case IDC_buttonVoice: {
{
["CfgVoice", _x, _ctrlPanel, "icon"] call FUNC(addListBoxItem);
} forEach (uiNamespace getVariable QGVAR(voiceCache));
GVAR(currentVoice)
};
// Insignia
case IDC_buttonInsignia: {
// Insignia from config
{
["CfgUnitInsignia", _x, _ctrlPanel, "texture"] call FUNC(addListBoxItem);
} forEach (uiNamespace getVariable QGVAR(insigniaCache));
private _displayName = "";
private _className = "";
private _lbAdd = -1;
// Insignia from mission file
{
_className = configName _x;
_displayName = getText (_x >> "displayName");
_lbAdd = _ctrlPanel lbAdd _displayName;
_ctrlPanel lbSetData [_lbAdd, _className];
_ctrlPanel lbSetPicture [_lbAdd, getText (_x >> "texture")];
_ctrlPanel lbSetTooltip [_lbAdd, format ["%1\n%2", _displayName, _className]];
} forEach ("(if (isNumber (_x >> 'scope')) then {getNumber (_x >> 'scope')} else {2}) == 2" configClasses (missionConfigFile >> "CfgUnitInsignia"));
GVAR(currentInsignia)
};
// Unknown
default {""};
};
};
// For every left tab except faces and voices, add "Empty" entry
if !(_ctrlIDC in [IDC_buttonFace, IDC_buttonVoice]) then {
private _addEmpty = _ctrlPanel lbAdd format [" <%1>", localize "str_empty"];
_ctrlPanel lbSetValue [_addEmpty, -1];
};
// When switching tabs, clear searchbox
if (GVAR(currentLeftPanel) != _ctrlIDC) then {
(_display displayCtrl IDC_leftSearchbar) ctrlSetText "";
(_display displayCtrl IDC_rightSearchbar) ctrlSetText "";
// Don't reset the current right panel for weapons, binos and containers
if !(_idxVirt in [IDX_VIRT_PRIMARY_WEAPONS, IDX_VIRT_SECONDARY_WEAPONS, IDX_VIRT_HANDGUN_WEAPONS, IDX_VIRT_BINO, IDX_VIRT_UNIFORM, IDX_VIRT_VEST, IDX_VIRT_BACKPACK]) then {
GVAR(currentRightPanel) = nil;
};
GVAR(currentLeftPanel) = _ctrlIDC;
// Add items to the listbox
private _selectedItem = if (_idxVirt != -1) then { // Items
private _configParent = switch (_idxVirt) do {
case IDX_VIRT_GOGGLES: {"CfgGlasses"};
case IDX_VIRT_BACKPACK: {"CfgVehicles"};
default {"CfgWeapons"};
};
private _items = if (_idxVirt < IDX_VIRT_HEADGEAR) then {
keys ((GVAR(virtualItems) get IDX_VIRT_WEAPONS) get _idxVirt)
} else {
keys (GVAR(virtualItems) get _idxVirt)
};
{
[_configParent, _x, _ctrlPanel] call FUNC(addListBoxItem);
} forEach _items;
GVAR(currentItems) select _idxVirt
} else { // Special cases
switch (_ctrlIDC) do {
// Faces
case IDC_buttonFace: {
private _lbAdd = -1; // micro-optimization
// Faces need to be added like this because their config path is
// configFile >> "CfgFaces" >> face category >> className
{
_y params ["_displayName", "_modPicture"];
_lbAdd = _ctrlPanel lbAdd _displayName;
_ctrlPanel lbSetData [_lbAdd, _x];
_ctrlPanel lbSetTooltip [_lbAdd, format ["%1\n%2", _displayName, _x]];
_ctrlPanel lbSetPictureRight [_lbAdd, ["", _modPicture] select GVAR(enableModIcons)];
} forEach GVAR(faceCache); // HashMap, not array
GVAR(currentFace)
};
// Voices
case IDC_buttonVoice: {
{
["CfgVoice", _x, _ctrlPanel, "icon"] call FUNC(addListBoxItem);
} forEach (keys GVAR(voiceCache));
GVAR(currentVoice)
};
// Insignia
case IDC_buttonInsignia: {
{
["CfgUnitInsignia", _x, _ctrlPanel, "texture", _y] call FUNC(addListBoxItem);
} forEach GVAR(insigniaCache);
GVAR(currentInsignia)
};
// Unknown
default {
WARNING_1("Unknown arsenal left panel with IDC %1, update ace_arsenal_idxMap and relevant macros if adding a new tab",_ctrlIDC);
""
};
};
};
// Trigger event
GVAR(currentLeftPanel) = _ctrlIDC;
[QGVAR(leftPanelFilled), [_display, _ctrlIDC, GVAR(currentRightPanel)]] call CBA_fnc_localEvent;
// Sort

View File

@ -1,12 +1,16 @@
#include "..\script_component.hpp"
#include "..\defines.hpp"
/*
* Author: Alganthe, johnb43
* Fill loadouts list.
* Author: Alganthe, johnb43, LinkIsGrim
* Fill loadouts list over multiple frames. LOADOUTS_PER_FRAME macro does what it says on the tin.
* Should only ever be called by display load (with optional params as default) and by itself.
* Listen to ace_arsenal_loadoutsListFilled event if you need to iterate over the loadouts list.
*
* Arguments:
* 0: Loadouts display <DISPLAY>
* 1: Tab control <CONTROL>
* 2: Current frame filling loadouts list <NUMBER> (default: 0)
* 3: Frames necessary to fill loadouts list <NUMBER> (default: -1)
*
* Return Value:
* None
@ -14,29 +18,46 @@
* Public: No
*/
params ["_display", "_control"];
// Can just be modified directly, no further setup needed
#define LOADOUTS_PER_FRAME 10
(_display displayCtrl IDC_textEditBox) ctrlSetText "";
params ["_display", "_control", ["_currentFrame", 0], ["_framesToFill", -1]];
if (isNull _display) exitWith {
TRACE_2("display closed, aborting",_currentFrame,_framesToFill);
};
private _contentPanelCtrl = _display displayCtrl IDC_contentPanel;
if (_currentFrame == 0) then {
(_display displayCtrl IDC_textEditBox) ctrlSetText "";
// Force a "refresh" animation of the panel
_contentPanelCtrl ctrlSetFade 1;
_contentPanelCtrl ctrlCommit 0;
_contentPanelCtrl ctrlSetFade 0;
_contentPanelCtrl ctrlCommit FADE_DELAY;
// Force a "refresh" animation of the panel
_contentPanelCtrl ctrlSetFade 1;
_contentPanelCtrl ctrlCommit 0;
_contentPanelCtrl ctrlSetFade 0;
_contentPanelCtrl ctrlCommit FADE_DELAY;
_contentPanelCtrl lnbSetCurSelRow -1;
lnbClear _contentPanelCtrl;
_contentPanelCtrl lnbSetCurSelRow -1;
lnbClear _contentPanelCtrl;
};
private _sharedLoadoutsVars = GVAR(sharedLoadoutsNamespace) getVariable QGVAR(sharedLoadoutsVars);
private _cfgWeapons = configFile >> "CfgWeapons";
private _cfgWeapons = configFile >> "CfgWeapons"; // Used by ADD_LOADOUTS_LIST_PICTURES macro, do not remove
private _newRow = -1;
if (GVAR(currentLoadoutsTab) != IDC_buttonSharedLoadouts) then {
private _loadoutNameAndTab = "";
private _loadoutCachedInfo = "";
private _sharingEnabled = GVAR(allowSharedLoadouts) && {isMultiplayer};
private _loadouts = [
profileNamespace getVariable [QGVAR(saved_loadouts), []],
GVAR(defaultLoadoutsList)
] select (ctrlIDC _control == IDC_buttonDefaultLoadouts);
if (_currentFrame == 0) then {
_framesToFill = floor ((count _loadouts) / LOADOUTS_PER_FRAME);
TRACE_2("filling loadouts list",_currentFrame,_framesToFill);
_this set [3, _framesToFill];
};
// Add all loadouts to loadout list
{
@ -50,15 +71,16 @@ if (GVAR(currentLoadoutsTab) != IDC_buttonSharedLoadouts) then {
_loadoutCachedInfo = [_loadoutData] call FUNC(verifyLoadout);
_contentPanelCtrl setVariable [_loadoutNameAndTab, _loadoutCachedInfo];
_loadoutCachedInfo params ["", "_nullItemsAmount", "_unavailableItemsAmount", "_nullItemsList", "_unavailableItemsList"];
_loadoutCachedInfo params ["", "_nullItemsList", "_unavailableItemsList", "_missingExtendedInfo"];
// Log missing / nil items to RPT (only once per arsenal session)
if (GVAR(EnableRPTLog) && {(_nullItemsAmount > 0) || {_unavailableItemsAmount > 0}}) then {
if (GVAR(EnableRPTLog) && {(_nullItemsList isNotEqualTo []) || {_unavailableItemsList isNotEqualTo [] || {_missingExtendedInfo isNotEqualTo []}}}) then {
private _printComponent = "ACE_Arsenal - Loadout:";
private _printNullItemsList = ["Missing items:", str _nullItemsList] joinString " ";
private _printUnavailableItemsList = ["Unavailable items:", str _unavailableItemsList] joinString " ";
private _printMissingExtendedInfo = ["Missing extended loadout:", str _missingExtendedInfo] joinString " ";
diag_log text (format ["%1%5 %2%5 %3%5 %4", _printComponent, "Name: " + _loadoutName, _printNullItemsList, _printUnavailableItemsList, endl]);
diag_log text (format ["%1%6 %2%6 %3%6 %4%6 %5", _printComponent, "Name: " + _loadoutName, _printNullItemsList, _printUnavailableItemsList, _printMissingExtendedInfo, endl]);
};
};
@ -69,18 +91,18 @@ if (GVAR(currentLoadoutsTab) != IDC_buttonSharedLoadouts) then {
_contentPanelCtrl lnbSetColumnsPos [0, 0.05, 0.40, 0.50, 0.60, 0.70, 0.75, 0.80, 0.85, 0.90];
};
_loadoutCachedInfo params ["_extendedLoadout", "_nullItemsAmount", "_unavailableItemsAmount"];
_extendedLoadout params ["_loadout"];
_loadoutCachedInfo params ["_extendedLoadout", "_nullItemsList", "_unavailableItemsList"];
_extendedLoadout params ["_loadout"]; // Used by ADD_LOADOUTS_LIST_PICTURES macro, do not remove
_newRow = _contentPanelCtrl lnbAddRow ["", _loadoutName];
ADD_LOADOUTS_LIST_PICTURES
// Change color on loadout lines that have items that aren't available or don't exist
if (_nullItemsAmount > 0) then {
if (_nullItemsList isNotEqualTo []) then {
_contentPanelCtrl lnbSetColor [[_newRow, 1], [1, 0, 0, 0.8]]; // Red
} else {
if (_unavailableItemsAmount > 0) then {
if (_unavailableItemsList isNotEqualTo []) then {
_contentPanelCtrl lnbSetColor [[_newRow, 1], [1, 1, 1, 0.25]]; // Gray
};
};
@ -90,10 +112,16 @@ if (GVAR(currentLoadoutsTab) != IDC_buttonSharedLoadouts) then {
_contentPanelCtrl lnbSetPicture [[_newRow, 0], QPATHTOF(data\iconPublic.paa)];
_contentPanelCtrl lnbSetValue [[_newRow, 0], 1];
};
} forEach ([profileNamespace getVariable [QGVAR(saved_loadouts), []], GVAR(defaultLoadoutsList)] select (ctrlIDC _control == IDC_buttonDefaultLoadouts));
} forEach (_loadouts select [_currentFrame * LOADOUTS_PER_FRAME, [LOADOUTS_PER_FRAME, count _loadouts] select is3DEN]);
} else {
private _allPlayerNames = allPlayers apply {name _x};
private _loadouts = _sharedLoadoutsVars apply {GVAR(sharedLoadoutsNamespace) getVariable _x};
private _loadoutVar = "";
if (_currentFrame == 0) then {
_framesToFill = floor ((count _loadouts) / LOADOUTS_PER_FRAME);
TRACE_2("filling loadouts list",_currentFrame,_framesToFill);
_this set [3, _framesToFill];
};
{
_x params ["_playerName", "_loadoutName", "_loadoutData"];
@ -107,8 +135,8 @@ if (GVAR(currentLoadoutsTab) != IDC_buttonSharedLoadouts) then {
[QGVAR(loadoutUnshared), [_contentPanelCtrl, profileName, _loadoutName]] call CBA_fnc_remoteEvent;
} else {
([_loadoutData] call FUNC(verifyLoadout)) params ["_extendedLoadout", "_nullItemsAmount", "_unavailableItemsAmount"];
_extendedLoadout params ["_loadout"];
([_loadoutData] call FUNC(verifyLoadout)) params ["_extendedLoadout", "_nullItemsList", "_unavailableItemsList"];
_extendedLoadout params ["_loadout"]; // Used by ADD_LOADOUTS_LIST_PICTURES macro, do not remove
_contentPanelCtrl lnbSetColumnsPos [0, 0.15, 0.40, 0.50, 0.60, 0.70, 0.75, 0.80, 0.85, 0.90];
_newRow = _contentPanelCtrl lnbAddRow [_playerName, _loadoutName];
@ -118,17 +146,23 @@ if (GVAR(currentLoadoutsTab) != IDC_buttonSharedLoadouts) then {
_contentPanelCtrl lnbSetData [[_newRow, 1], _loadoutVar];
// Change color on loadout lines that have items that aren't available or don't exist
if (_nullItemsAmount > 0) then {
if (_nullItemsList isNotEqualTo []) then {
_contentPanelCtrl lnbSetColor [[_newRow, 1], [1, 0, 0, 0.8]]; // Red
} else {
if (_unavailableItemsAmount > 0) then {
if (_unavailableItemsList isNotEqualTo []) then {
_contentPanelCtrl lnbSetColor [[_newRow, 1], [1, 1, 1, 0.25]]; // Gray
};
};
};
} forEach (_sharedLoadoutsVars apply {GVAR(sharedLoadoutsNamespace) getVariable _x});
} forEach (_loadouts select [_currentFrame * LOADOUTS_PER_FRAME, [LOADOUTS_PER_FRAME, count _loadouts] select is3DEN]);
};
if (!is3DEN && _currentFrame != _framesToFill) exitWith {
_this set [2, _currentFrame + 1];
[FUNC(fillLoadoutsList), _this] call CBA_fnc_execNextFrame;
};
TRACE_3("finished",_currentFrame,_framesToFill,lnbSize _contentPanelCtrl);
[QGVAR(loadoutsListFilled), [_display, _control]] call CBA_fnc_localEvent;
// Sort loadouts alphabetically

View File

@ -44,12 +44,16 @@ private _cfgMagazines = configFile >> "CfgMagazines";
private _cfgWeapons = configFile >> "CfgWeapons";
private _rightPanelCache = uiNamespace getVariable QGVAR(rightPanelCache);
private _currentCargo = itemsWithMagazines GVAR(center);
private _currentCargo = []; // we only need this if we're filtering for favorites
if (GVAR(favoritesOnly)) then {
_currentCargo = itemsWithMagazines GVAR(center) + backpacks GVAR(center);
_currentCargo = _currentCargo arrayIntersect _currentCargo;
};
private _fnc_fillRightContainer = {
params ["_configCategory", "_className", "_hasItemInfo", ["_isUnique", false, [false]], ["_unknownOrigin", false, [false]]];
params ["_configCategory", "_className", ["_isUnique", false, [false]], ["_unknownOrigin", false, [false]]];
if (GVAR(favoritesOnly) && {!(_className in _currentCargo)} && {!((toLower _className) in GVAR(favorites))}) exitWith {};
if (GVAR(favoritesOnly) && {!(_className in _currentCargo)} && {!((toLowerANSI _className) in GVAR(favorites))}) exitWith {};
// If item is not in the arsenal, it must be unique
if (!_isUnique && {!(_className in GVAR(virtualItemsFlat))}) then {
@ -64,7 +68,6 @@ private _fnc_fillRightContainer = {
// "Misc. items" magazines (e.g. spare barrels, intel, photos)
if (_className in (uiNamespace getVariable QGVAR(magazineMiscItems))) then {
_configPath = _cfgMagazines >> _className;
_hasItemInfo = false;
};
// If an item with unknown origin is in the arsenal list, try to find it
@ -75,37 +78,18 @@ private _fnc_fillRightContainer = {
if (isNull _configPath) then {
_configPath = _className call CBA_fnc_getObjectConfig;
};
// Check if item is has item info
_itemInfo = isClass (_cfgWeapons >> configName _configPath);
};
// Get mass
private _mass = if (!_hasItemInfo) then {
getNumber (_configPath >> "mass")
} else {
private _mass = getNumber (_configPath >> "itemInfo" >> "mass");
if (_mass == 0) then {
_mass = getNumber (_configPath >> "WeaponSlotsInfo" >> "mass");
};
_mass
};
_rightPanelCache set [_className, _mass]; // Needed because this provides more accurate weight for FUNC(updateRightPanel)
[getText (_configPath >> "displayName"), getText (_configPath >> "picture"), _mass]
}, true]) params ["_displayName", "_picture", "_mass"];
[getText (_configPath >> "displayName"), getText (_configPath >> "picture")]
}, true]) params ["_displayName", "_picture"];
private _lbAdd = _ctrlPanel lnbAddRow ["", _displayName, "0"];
_ctrlPanel lnbSetText [[_lbAdd, 1], _displayName];
_ctrlPanel lnbSetData [[_lbAdd, 0], _className];
_ctrlPanel lnbSetPicture [[_lbAdd, 0], _picture];
_ctrlPanel lnbSetValue [[_lbAdd, 0], _mass];
_ctrlPanel lnbSetValue [[_lbAdd, 2], [0, 1] select _isUnique];
_ctrlPanel lnbSetValue [[_lbAdd, 2], parseNumber _isUnique];
_ctrlPanel lnbSetTooltip [[_lbAdd, 0], format ["%1\n%2", _displayName, _className]];
if ((toLower _className) in GVAR(favorites)) then {
if ((toLowerANSI _className) in GVAR(favorites)) then {
_ctrlPanel lnbSetColor [[_lbAdd, 1], FAVORITES_COLOR];
_ctrlPanel lnbSetColorRight [[_lbAdd, 1], FAVORITES_COLOR];
};
@ -114,15 +98,9 @@ private _fnc_fillRightContainer = {
private _ctrlPanel = _display displayCtrl IDC_rightTabContent;
private _listnBox = _display displayCtrl IDC_rightTabContentListnBox;
// Reset right panel content
lbClear _ctrlPanel;
lbClear _listnBox;
_ctrlPanel lbSetCurSel -1;
_listnBox lbSetCurSel -1;
// Retrieve compatible items
private _itemToCheck = "";
private _isContainer = false;
private _selectedItem = "";
private _compatibleItems = [];
private _compatibleMagsMuzzle = [];
private _compatibleMagsAll = createHashMap;
@ -154,12 +132,12 @@ switch (GVAR(currentLeftPanel)) do {
// Check if weapon attachement or magazine
if (_index != -1) then {
_itemToCheck = (GVAR(currentItems) select _currentWeaponItemsIndex) select _index;
_selectedItem = (GVAR(currentItems) select _currentWeaponItemsIndex) select _index;
// If weapon attachment, get base weapon; Get compatible items
if (_index <= 3) then {
_compatibleItems = compatibleItems _weapon;
_itemToCheck = _itemToCheck call FUNC(baseWeapon);
_selectedItem = _selectedItem call FUNC(baseWeapon);
} else {
// Get compatible magazines for primary & secondary muzzle (secondary muzzle is not guaranteed to exist)
// Assumption: One weapon can have two muzzles maximum
@ -171,7 +149,15 @@ switch (GVAR(currentLeftPanel)) do {
case IDC_buttonUniform;
case IDC_buttonVest;
case IDC_buttonBackpack: {
_ctrlPanel = _listnBox;
_isContainer = true;
// Get the currently selected item in panel
private _selectedItemIndex = lnbCurSelRow _listnBox;
// If something is selected, save it
if (_selectedItemIndex != -1) then {
_selectedItem = _listnBox lnbData [_selectedItemIndex, 0];
};
// This is for the "compatible magazines" tab when a container is open
if (_ctrlIDC == IDC_buttonMag) then {
@ -183,6 +169,17 @@ switch (GVAR(currentLeftPanel)) do {
};
};
// Reset right panel content
lbClear _ctrlPanel;
lnbClear _listnBox;
_ctrlPanel lbSetCurSel -1;
_listnBox lnbSetCurSelRow -1;
if (_isContainer) then {
_ctrlPanel = _listnBox;
};
// Force a "refresh" animation of the panel
if (_animate) then {
_ctrlPanel ctrlSetFade 1;
@ -217,11 +214,11 @@ switch (_ctrlIDC) do {
} forEach _compatibleItems;
} else {
{
["CfgWeapons", _x, true] call _fnc_fillRightContainer;
["CfgWeapons", _x] call _fnc_fillRightContainer;
} forEach (keys ((GVAR(virtualItems) get IDX_VIRT_ATTACHMENTS) get _index));
{
["CfgWeapons", _x, true, true] call _fnc_fillRightContainer;
["CfgWeapons", _x, true] call _fnc_fillRightContainer;
} forEach (keys ((GVAR(virtualItems) get IDX_VIRT_UNIQUE_ATTACHMENTS) get _index));
};
};
@ -240,44 +237,44 @@ switch (_ctrlIDC) do {
case IDC_buttonMag: {
{
if (_x in (GVAR(virtualItems) get IDX_VIRT_ITEMS_ALL)) then {
["CfgMagazines", _x, false] call _fnc_fillRightContainer;
["CfgMagazines", _x] call _fnc_fillRightContainer;
continue;
};
if (_x in (GVAR(virtualItems) get IDX_VIRT_UNIQUE_VIRT_ITEMS_ALL)) then {
["CfgMagazines", _x, false, true] call _fnc_fillRightContainer;
["CfgMagazines", _x, true] call _fnc_fillRightContainer;
};
} forEach (keys _compatibleMagsAll);
};
// All magazines
case IDC_buttonMagALL: {
{
["CfgMagazines", _x, false] call _fnc_fillRightContainer;
["CfgMagazines", _x] call _fnc_fillRightContainer;
} forEach (keys (GVAR(virtualItems) get IDX_VIRT_ITEMS_ALL));
{
["CfgMagazines", _x, false, true] call _fnc_fillRightContainer;
["CfgMagazines", _x, true] call _fnc_fillRightContainer;
} forEach (keys (GVAR(virtualItems) get IDX_VIRT_UNIQUE_VIRT_ITEMS_ALL));
};
// Grenades
case IDC_buttonThrow: {
{
["CfgMagazines", _x, false] call _fnc_fillRightContainer;
["CfgMagazines", _x] call _fnc_fillRightContainer;
} forEach (keys (GVAR(virtualItems) get IDX_VIRT_GRENADES));
{
["CfgMagazines", _x, false, true] call _fnc_fillRightContainer;
["CfgMagazines", _x, true] call _fnc_fillRightContainer;
} forEach (keys (GVAR(virtualItems) get IDX_VIRT_UNIQUE_GRENADES));
};
// Explosives
case IDC_buttonPut: {
{
["CfgMagazines", _x, false] call _fnc_fillRightContainer;
["CfgMagazines", _x] call _fnc_fillRightContainer;
} forEach (keys (GVAR(virtualItems) get IDX_VIRT_EXPLOSIVES));
{
["CfgMagazines", _x, false, true] call _fnc_fillRightContainer;
["CfgMagazines", _x, true] call _fnc_fillRightContainer;
} forEach (keys (GVAR(virtualItems) get IDX_VIRT_UNIQUE_EXPLOSIVES));
};
// Misc. items
@ -296,33 +293,35 @@ switch (_ctrlIDC) do {
// "Regular" misc. items
{
if !(_x in _items) then {
["CfgWeapons", _x, true] call _fnc_fillRightContainer;
["CfgWeapons", _x] call _fnc_fillRightContainer;
};
} forEach (keys (GVAR(virtualItems) get IDX_VIRT_MISC_ITEMS));
// Unique items
{
if !(_x in _items) then {
["CfgWeapons", _x, true, true] call _fnc_fillRightContainer;
["CfgWeapons", _x, true] call _fnc_fillRightContainer;
};
} forEach (keys (GVAR(virtualItems) get IDX_VIRT_UNIQUE_MISC_ITEMS));
// Unique backpacks
{
if !(_x in _items) then {
["CfgVehicles", _x, false, true] call _fnc_fillRightContainer;
["CfgVehicles", _x, true] call _fnc_fillRightContainer;
};
} forEach (keys (GVAR(virtualItems) get IDX_VIRT_UNIQUE_BACKPACKS));
// Unique goggles
{
if !(_x in _items) then {
["CfgGlasses", _x, false, true] call _fnc_fillRightContainer;
// _y indicates if an item is truly unique or if it's a non-inventory item in a container (e.g. goggles in backpack)
["CfgGlasses", _x, _y] call _fnc_fillRightContainer;
};
} forEach (keys (GVAR(virtualItems) get IDX_VIRT_UNIQUE_GOGGLES));
} forEach (GVAR(virtualItems) get IDX_VIRT_UNIQUE_GOGGLES);
// Unknown items
{
if !(_x in _items) then {
["CfgWeapons", _x, true, true, true] call _fnc_fillRightContainer;
// _y indicates if an item is truly unique or if it's a non-inventory item in a container (e.g. helmet in backpack)
["CfgWeapons", _x, _y, true] call _fnc_fillRightContainer;
};
} forEach (keys (GVAR(virtualItems) get IDX_VIRT_UNIQUE_UNKNOWN_ITEMS));
} forEach (GVAR(virtualItems) get IDX_VIRT_UNIQUE_UNKNOWN_ITEMS); // if an item is here but in virtual items, it's just in the wrong place
};
// Custom buttons
default {
@ -334,23 +333,23 @@ switch (_ctrlIDC) do {
switch (true) do {
// "Regular" misc. items
case (_x in (GVAR(virtualItems) get IDX_VIRT_MISC_ITEMS)): {
["CfgWeapons", _x, true] call _fnc_fillRightContainer;
["CfgWeapons", _x] call _fnc_fillRightContainer;
};
// Unique items
case (_x in (GVAR(virtualItems) get IDX_VIRT_UNIQUE_MISC_ITEMS)): {
["CfgWeapons", _x, true, true] call _fnc_fillRightContainer;
["CfgWeapons", _x, true] call _fnc_fillRightContainer;
};
// Unique backpacks
case (_x in (GVAR(virtualItems) get IDX_VIRT_UNIQUE_BACKPACKS)): {
["CfgVehicles", _x, false, true] call _fnc_fillRightContainer;
["CfgVehicles", _x, true] call _fnc_fillRightContainer;
};
// Unique goggles
case (_x in (GVAR(virtualItems) get IDX_VIRT_UNIQUE_GOGGLES)): {
["CfgGlasses", _x, false, true] call _fnc_fillRightContainer;
["CfgGlasses", _x, GVAR(virtualItems) get IDX_VIRT_UNIQUE_GOGGLES get _x] call _fnc_fillRightContainer;
};
// Unknown items
case (_x in (GVAR(virtualItems) get IDX_VIRT_UNIQUE_UNKNOWN_ITEMS)): {
["CfgWeapons", _x, true, true, true] call _fnc_fillRightContainer;
["CfgWeapons", _x, GVAR(virtualItems) get IDX_VIRT_UNIQUE_UNKNOWN_ITEMS get _x, true] call _fnc_fillRightContainer;
};
};
} forEach _items;
@ -368,7 +367,7 @@ GVAR(currentRightPanel) = _ctrlIDC;
[QGVAR(rightPanelFilled), [_display, GVAR(currentLeftPanel), _ctrlIDC]] call CBA_fnc_localEvent;
// Add current items, change progress bar of container load and get relevant container
if (GVAR(currentLeftPanel) in [IDC_buttonUniform, IDC_buttonVest, IDC_buttonBackpack]) then {
if (_isContainer) then {
private _containerItems = [];
private _container = switch (GVAR(currentLeftPanel)) do {
// Uniform
@ -376,8 +375,8 @@ if (GVAR(currentLeftPanel) in [IDC_buttonUniform, IDC_buttonVest, IDC_buttonBack
// Update load bar
(_display displayCtrl IDC_loadIndicatorBar) progressSetPosition (loadUniform GVAR(center));
// Get all items from container (excluding container itself)
_containerItems = [GVAR(center), 0, 3, 0, 0, false] call EFUNC(common,uniqueUnitItems);
// Get all items from container
_containerItems = uniformItems GVAR(center);
uniformContainer GVAR(center)
};
@ -386,8 +385,8 @@ if (GVAR(currentLeftPanel) in [IDC_buttonUniform, IDC_buttonVest, IDC_buttonBack
// Update load bar
(_display displayCtrl IDC_loadIndicatorBar) progressSetPosition (loadVest GVAR(center));
// Get all items from container (excluding container itself)
_containerItems = [GVAR(center), 0, 0, 3, 0, false] call EFUNC(common,uniqueUnitItems);
// Get all items from container
_containerItems = vestItems GVAR(center);
vestContainer GVAR(center)
};
@ -396,8 +395,8 @@ if (GVAR(currentLeftPanel) in [IDC_buttonUniform, IDC_buttonVest, IDC_buttonBack
// Update load bar
(_display displayCtrl IDC_loadIndicatorBar) progressSetPosition (loadBackpack GVAR(center));
// Get all items from container (excluding container itself)
_containerItems = [GVAR(center), 0, 0, 0, 3, false] call EFUNC(common,uniqueUnitItems);
// Get all items from container
_containerItems = backpackItems GVAR(center);
backpackContainer GVAR(center)
};
@ -405,7 +404,8 @@ if (GVAR(currentLeftPanel) in [IDC_buttonUniform, IDC_buttonVest, IDC_buttonBack
// Find out how many items of a type there are and update the number displayed
for "_lbIndex" from 0 to (lnbSize _ctrlPanel select 0) - 1 do {
_ctrlPanel lnbSetText [[_lbIndex, 2], str (_containerItems getOrDefault [_ctrlPanel lnbData [_lbIndex, 0], 0])];
private _xItem = _ctrlPanel lnbData [_lbIndex, 0];
_ctrlPanel lnbSetText [[_lbIndex, 2], str ({_xItem == _x} count _containerItems)];
};
// Refresh availibility of items based on space remaining in container
@ -415,17 +415,34 @@ if (GVAR(currentLeftPanel) in [IDC_buttonUniform, IDC_buttonVest, IDC_buttonBack
// Sorting
[_display, _control, _display displayCtrl IDC_sortRightTab, _display displayCtrl IDC_sortRightTabDirection] call FUNC(fillSort);
// Try to select previously selected item again, otherwise select first item ("Empty")
if (_itemToCheck != "") then {
private _index = 0;
if (_selectedItem != "") then {
if (_isContainer) then {
// Try to select previously selected item again, otherwise select nothing
private _index = -1;
for "_lbIndex" from 0 to (lbSize _ctrlPanel) - 1 do {
if ((_ctrlPanel lbData _lbIndex) == _itemToCheck) exitWith {
_index = _lbIndex;
for "_lbIndex" from 0 to (lnbSize _ctrlPanel select 0) - 1 do {
if ((_ctrlPanel lnbData [_lbIndex, 0]) == _selectedItem) exitWith {
_index = _lbIndex;
};
};
};
_ctrlPanel lbSetCurSel _index;
_ctrlPanel lnbSetCurSelRow _index;
} else {
// Try to select previously selected item again, otherwise select first item ("Empty")
private _index = 0;
for "_lbIndex" from 0 to (lbSize _ctrlPanel) - 1 do {
if ((_ctrlPanel lbData _lbIndex) == _selectedItem) exitWith {
_index = _lbIndex;
};
};
_ctrlPanel lbSetCurSel _index;
};
} else {
_ctrlPanel lbSetCurSel 0;
if (_isContainer) then {
_ctrlPanel lnbSetCurSelRow -1; // select nothing
} else {
_ctrlPanel lbSetCurSel 0; // select "Empty"
};
};

View File

@ -7,7 +7,7 @@
* Arguments:
* 0: Arsenal display <DISPLAY>
* 1: Current panel control <CONTROL>
* 2: Current panel selection <SCALAR>
* 2: Current panel selection <NUMBER>
* 3: Item config entry <CONFIG>
*
* Return Value:
@ -15,7 +15,6 @@
*
* Public: No
*/
params ["_display", "_control", "_curSel", "_itemCfg"];
GVAR(actionsInfo) = [_control, _curSel, _itemCfg];
@ -46,13 +45,12 @@ private _groups = (GVAR(actionList) select _panel) select {
};
private _show = _groups isNotEqualTo [];
private _ctrl = _display displayCtrl IDC_actionsBox;
_ctrl ctrlShow _show;
_ctrl ctrlCommit 0.15;
private _actionsBoxCtrl = _display displayCtrl IDC_actionsBox;
_actionsBoxCtrl ctrlShow _show;
_actionsBoxCtrl ctrlCommit FADE_DELAY;
if (!_show) exitWith {};
private _actionsBoxCtrl = _display displayCtrl IDC_actionsBox;
private _actionsCurrentPageCtrl = _display displayCtrl IDC_actionsCurrentPage;
private _currentPage = GVAR(currentActionPage);
@ -79,33 +77,29 @@ private _items = _group select 3 select {
};
_actionsCurrentPageCtrl ctrlSetText (_group select 1);
_actionsCurrentPageCtrl ctrlSetFade 0;
_actionsCurrentPageCtrl ctrlShow true;
_actionsCurrentPageCtrl ctrlCommit 0;
private _activeCtrls = [];
{
_x params ["", "_type", "_label", "_statement"];
private _idc = 9001 + _forEachIndex * 2;
private _actionTextCtrl = _display displayCtrl _idc;
private _actionButtonCtrl = _display displayCtrl (_idc + 1);
private _idc = IDC_actionsText1 + _type + _forEachIndex * 2;
private _actionCtrl = _display displayCtrl _idc;
switch (_type) do {
case ACTION_TYPE_BUTTON: {
_actionButtonCtrl ctrlRemoveAllEventHandlers "ButtonClick";
_actionButtonCtrl ctrlAddEventHandler ["ButtonClick", {
_actionCtrl ctrlRemoveAllEventHandlers "ButtonClick";
_actionCtrl ctrlAddEventHandler ["ButtonClick", {
if (is3DEN) exitWith {[true] call FUNC(refresh)};
[{
[true] call FUNC(refresh);
}] call CBA_fnc_execNextFrame;
}];
_actionButtonCtrl ctrlAddEventHandler ["ButtonClick", _statement];
_actionButtonCtrl ctrlSetText _label;
_actionButtonCtrl ctrlSetFade 0;
_actionButtonCtrl ctrlEnable true;
_actionButtonCtrl ctrlCommit 0;
_actionTextCtrl ctrlSetFade 1;
_actionTextCtrl ctrlCommit 0;
_actionCtrl ctrlAddEventHandler ["ButtonClick", _statement];
_actionCtrl ctrlSetText _label;
_actionCtrl ctrlEnable true;
};
case ACTION_TYPE_TEXT: {
private _text = call _statement;
@ -113,36 +107,43 @@ _actionsCurrentPageCtrl ctrlCommit 0;
if (isNil "_text") then {
_text = "";
};
if (_text isEqualType []) then {
_text = _text joinString endl;
};
_actionTextCtrl ctrlSetText _text;
_actionTextCtrl ctrlSetFade 0;
_actionTextCtrl ctrlCommit 0;
_actionButtonCtrl ctrlSetFade 1;
_actionButtonCtrl ctrlCommit 0;
};
default {
_actionTextCtrl ctrlSetFade 1;
_actionTextCtrl ctrlCommit 0;
_actionButtonCtrl ctrlSetFade 1;
_actionButtonCtrl ctrlCommit 0;
_actionCtrl ctrlSetText _text;
_actionCtrl ctrlSetPositionH (ctrlTextHeight _actionCtrl);
_actionCtrl ctrlEnable false;
};
};
if (_activeCtrls isNotEqualTo []) then {
(ctrlPosition (_activeCtrls select -1)) params ["", "_lastPosY", "", "_lastPosH"];
_actionCtrl ctrlSetPositionY (_lastPosY + _lastPosH + GRID_H);
} else {
_actionCtrl ctrlSetPositionY ((5 + _type) * GRID_H);
};
_actionCtrl ctrlShow true;
_actionCtrl ctrlCommit 0;
_activeCtrls pushBack _actionCtrl;
} forEach _items;
private _actionCount = count _items;
{
private _idc = 9001 + _x * 2;
private _actionTextCtrl = _display displayCtrl _idc;
private _actionButtonCtrl = _display displayCtrl (_idc + 1);
private _idc = ctrlIDC _x;
if (_idc < IDC_actionsText1 || _idc > IDC_actionsButton5) then {continue};
_actionTextCtrl ctrlSetFade 1;
_actionTextCtrl ctrlCommit 0;
_actionButtonCtrl ctrlSetFade 1;
_actionButtonCtrl ctrlCommit 0;
} forEach ([0, 1, 2, 3, 4] select [_actionCount, 5]);
_x ctrlShow false;
_x ctrlEnable false;
_x ctrlSetPositionY 0;
_x ctrlCommit 0;
} forEach ((allControls _actionsBoxCtrl) select {!(_x in _activeCtrls)});
private _pos = ctrlPosition _actionsBoxCtrl;
_pos set [3, ([11, (5 * _actionCount) + 6] select (_actionCount > 0)) * GRID_H];
_actionsBoxCtrl ctrlSetPosition _pos;
(ctrlPosition (_activeCtrls select -1)) params ["", "_lastPosY", "", "_lastPosH"];
private _actionsBoxHeight = _lastPosY + _lastPosH + GRID_H;
_actionsBoxCtrl ctrlSetPositionH _actionsBoxHeight;
_actionsBoxCtrl ctrlCommit 0;
private _background = _display displayCtrl IDC_actionsBackground1;
_background ctrlSetPositionH _actionsBoxHeight;
_background ctrlCommit 0;

View File

@ -49,7 +49,7 @@ if ((ctrlIDC _control) == IDC_rightSearchbar) then {
// If something is selected, save it
if (_selectedItemIndex != -1) then {
_selectedItem = _rightPanelCtrl lbText _selectedItemIndex;
_selectedItem = _rightPanelCtrl lbData _selectedItemIndex;
};
private _currentDisplayName = "";
@ -72,7 +72,7 @@ if ((ctrlIDC _control) == IDC_rightSearchbar) then {
// Try to find previously selected item in panel
for "_lbIndex" from 0 to (lbSize _rightPanelCtrl) - 1 do {
if ((_rightPanelCtrl lbText _lbIndex) == _selectedItem) exitWith {
if ((_rightPanelCtrl lbData _lbIndex) == _selectedItem) exitWith {
_index = _lbIndex;
};
};
@ -89,7 +89,7 @@ if ((ctrlIDC _control) == IDC_rightSearchbar) then {
// If something is selected, save it
if (_selectedItemIndex != -1) then {
_selectedItem = _rightPanelCtrl lnbText [_selectedItemIndex, 1];
_selectedItem = _rightPanelCtrl lnbData [_selectedItemIndex, 0];
};
private _currentDisplayName = "";
@ -112,7 +112,7 @@ if ((ctrlIDC _control) == IDC_rightSearchbar) then {
// Try to find previously selected item in panel
for "_lbIndex" from 0 to (lnbSize _rightPanelCtrl select 0) - 1 do {
if ((_rightPanelCtrl lnbText [_lbIndex, 1]) == _selectedItem) exitWith {
if ((_rightPanelCtrl lnbData [_lbIndex, 0]) == _selectedItem) exitWith {
_index = _lbIndex;
};
};
@ -145,7 +145,7 @@ if ((ctrlIDC _control) == IDC_rightSearchbar) then {
// If something is selected, save it
if (_selectedItemIndex != -1) then {
_selectedItem = _leftPanelCtrl lbText _selectedItemIndex;
_selectedItem = _leftPanelCtrl lbData _selectedItemIndex;
};
private _currentDisplayName = "";
@ -167,7 +167,7 @@ if ((ctrlIDC _control) == IDC_rightSearchbar) then {
private _index = -1;
for "_lbIndex" from 0 to (lbSize _leftPanelCtrl) - 1 do {
if ((_leftPanelCtrl lbText _lbIndex) == _selectedItem) exitWith {
if ((_leftPanelCtrl lbData _lbIndex) == _selectedItem) exitWith {
_index = _lbIndex;
};
};

View File

@ -51,7 +51,10 @@ if (_global && {isMultiplayer} && {isNil {_object getVariable QGVAR(initBoxJIP)}
] call EFUNC(interact_menu,createAction);
[_object, 0, ["ACE_MainActions"], _action] call EFUNC(interact_menu,addActionToObject);
[_object, _items, false] call FUNC(addVirtualItems);
// If items were set globally, do not add items locally
if (isNil {_object getVariable QGVAR(virtualItems)}) then {
[_object, _items, false] call FUNC(addVirtualItems);
};
[QGVAR(boxInitialized), [_object, _items]] call CBA_fnc_localEvent;
};

View File

@ -63,6 +63,7 @@ switch (ctrlIDC _control) do {
_saveButtonCtrl ctrlSetTooltip LLSTRING(buttonSaveTooltip);
};
_renameButtonCtrl ctrlEnable is3DEN; // no renaming mid-mission
_saveButtonCtrl ctrlEnable call FUNC(canEditDefaultLoadout);
_saveButtonCtrl ctrlCommit 0;
};

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);
@ -51,13 +51,6 @@ if (is3DEN) then {
["ShowInterface", true] call BIS_fnc_3DENInterface;
GVAR(visionMode) call BIS_fnc_3DENVisionMode;
} else {
// Select correct weapon
switch (GVAR(selectedWeaponType)) do {
case 0: {GVAR(center) selectWeapon (primaryWeapon GVAR(center))};
case 1: {GVAR(center) selectWeapon (secondaryWeapon GVAR(center))};
case 2: {GVAR(center) selectWeapon (handgunWeapon GVAR(center))};
};
if (!isNull curatorCamera && {ACE_player == player}) then {
curatorCamera cameraEffect ["Internal", "BACK"];
} else {
@ -85,10 +78,10 @@ if (!isNull curatorCamera) then {
// Make face and voice selection JIP compatible; 3DEN doesn't need this though
if (isMultiplayer && {!is3DEN}) then {
private _id = [QGVAR(broadcastFace), [GVAR(center), GVAR(currentFace)], QGVAR(centerFace_) + netId GVAR(center)] call CBA_fnc_globalEventJIP;
private _id = [QGVAR(broadcastFace), [GVAR(center), GVAR(currentFace)], QGVAR(centerFace_) + hashValue GVAR(center)] call CBA_fnc_globalEventJIP;
[_id, GVAR(center)] call CBA_fnc_removeGlobalEventJIP;
_id = [QGVAR(broadcastVoice), [GVAR(center), GVAR(currentVoice)], QGVAR(centerVoice_) + netId GVAR(center)] call CBA_fnc_globalEventJIP;
_id = [QGVAR(broadcastVoice), [GVAR(center), GVAR(currentVoice)], QGVAR(centerVoice_) + hashValue GVAR(center)] call CBA_fnc_globalEventJIP;
[_id, GVAR(center)] call CBA_fnc_removeGlobalEventJIP;
};
@ -122,12 +115,11 @@ GVAR(currentVoice) = nil;
GVAR(currentInsignia) = nil;
GVAR(currentAction) = nil;
GVAR(showStats) = nil;
GVAR(currentStatPage) = nil;
GVAR(statsInfo) = nil;
GVAR(showActions) = nil;
GVAR(currentActionPage) = nil;
GVAR(actionsInfo) = nil;
profileNamespace setVariable [QGVAR(favorites), GVAR(favorites)];
GVAR(favoritesOnly) = nil;
@ -136,4 +128,6 @@ GVAR(favorites) = nil;
GVAR(center) = nil;
GVAR(centerNotPlayer) = nil;
GVAR(ignoredVirtualItems) = nil;
[QUOTE(ADDON), []] call EFUNC(common,showHud);

View File

@ -68,18 +68,17 @@ if (isNil QGVAR(virtualItems)) then {
GVAR(virtualItemsFlatAll) = +GVAR(virtualItemsFlat);
GVAR(currentFace) = face GVAR(center);
GVAR(currentVoice) = speaker GVAR(center);
GVAR(currentVoice) = (speaker GVAR(center)) call EFUNC(common,getConfigName);
GVAR(currentInsignia) = GVAR(center) call BIS_fnc_getUnitInsignia;
GVAR(currentAction) = "Stand";
GVAR(shiftState) = false;
GVAR(showStats) = true;
GVAR(currentStatPage) = 0;
GVAR(statsInfo) = [true, controlNull, nil, nil];
GVAR(showActions) = true;
GVAR(currentActionPage) = 0;
GVAR(actionsInfo) = [controlNull, nil, nil];
// Update current item list
call FUNC(updateCurrentItemsList);
@ -243,7 +242,15 @@ GVAR(refreshing) = false;
_panel ctrlCommit 0;
} forEach [IDC_leftTabContent, IDC_rightTabContent, IDC_rightTabContentListnBox];
[_display, _display displayCtrl IDC_buttonPrimaryWeapon] call FUNC(fillLeftPanel);
// Open left panel for current weapon, do some math
GVAR(selectedWeaponType) = [primaryWeapon GVAR(center), secondaryWeapon GVAR(center), handgunWeapon GVAR(center), binocular GVAR(center)] find (currentWeapon GVAR(center));
if (GVAR(selectedWeaponType) == -1) then {
GVAR(selectedWeaponType) = 0; // default to primary
};
private _leftPanelIDC = [IDC_buttonPrimaryWeapon, IDC_buttonSecondaryWeapon, IDC_buttonHandgun, IDC_buttonBinoculars] select GVAR(selectedWeaponType);
[_display, _display displayCtrl _leftPanelIDC] call FUNC(fillLeftPanel);
//--------------- Init camera
if (isNil QGVAR(cameraPosition)) then {
@ -271,4 +278,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

@ -147,7 +147,7 @@ if (!isNull _loadoutsDisplay) then {
// Right panel lnb + and - buttons
case (_keyPressed in [DIK_LEFT, DIK_RIGHT]): {
if (GVAR(rightTabLnBFocus)) then {
[_display, [1, 0] select (_keyPressed == DIK_LEFT)] call FUNC(buttonCargo);
[_display, parseNumber (_keyPressed != DIK_LEFT)] call FUNC(buttonCargo);
};
};
};

View File

@ -27,9 +27,9 @@ private _favorited = false;
// Favorites/blacklist will always be lowercase to handle configCase changes
private _item = "";
if (_isLnB) then {
_item = toLower (_control lnbData [_curSel, 0]);
_item = toLowerANSI (_control lnbData [_curSel, 0]);
} else {
_item = toLower (_control lbData _curSel);
_item = toLowerANSI (_control lbData _curSel);
};
if (_item in GVAR(favorites)) then {

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
@ -683,14 +689,19 @@ switch (GVAR(currentLeftPanel)) do {
TOGGLE_RIGHT_PANEL_HIDE
private _unitInsigniaConfig = configFile >> "CfgUnitInsignia" >> _item;
// Check for correct config: First mission, then campaign and finally regular config
private _itemCfg = missionConfigFile >> "CfgUnitInsignia" >> _item;
if (isNull _itemCfg) then {
_itemCfg = campaignConfigFile >> "CfgUnitInsignia" >> _item;
};
if (isNull _itemCfg) then {
_itemCfg = configFile >> "CfgUnitInsignia" >> _item;
};
// Display new items's info on the bottom right
if (isNull _unitInsigniaConfig) then {
[_display, _control, _curSel, missionConfigFile >> "CfgUnitInsignia" >> _item] call FUNC(itemInfo);
} else {
[_display, _control, _curSel, _unitInsigniaConfig] call FUNC(itemInfo);
};
[_display, _control, _curSel, _itemCfg] call FUNC(itemInfo);
};
};

View File

@ -69,7 +69,14 @@ switch (_currentItemsIndex) do {
// Secondary weapon
case IDX_CURR_SECONDARY_WEAPON_ITEMS: {
private _currentItemInSlot = (GVAR(currentItems) select IDX_CURR_SECONDARY_WEAPON_ITEMS) select _itemIndex;
private _isDisposable = CBA_disposable_replaceDisposableLauncher && {!isNil {CBA_disposable_loadedLaunchers getVariable (secondaryWeapon GVAR(center))}};
private _isDisposable = CBA_disposable_replaceDisposableLauncher && {!isNil "CBA_disposable_loadedLaunchers"} &&
{
if (CBA_disposable_loadedLaunchers isEqualType createHashMap) then { // after CBA 3.18
(secondaryWeapon GVAR(center)) in CBA_disposable_loadedLaunchers
} else {
!isNil {CBA_disposable_loadedLaunchers getVariable (secondaryWeapon player)}
}
};
// If removal
if (_item == "") then {

View File

@ -28,13 +28,15 @@ if (
) exitWith {};
// If object has no arsenal and chosen option is to not ignore virtual items of object, exit
if (isNil {_object getVariable QGVAR(virtualItems)} && {!_mode}) exitWith {
private _virtualItems = _object getVariable QGVAR(virtualItems);
if (isNil "_virtualItems" && {!_mode}) exitWith {
[LLSTRING(noVirtualItems), false, 5, 1] call EFUNC(common,displayText);
};
// Don't execute in scheduled environment
if (canSuspend) exitWith {
[{_this call FUNC(openBox)}, _this] call CBA_fnc_directCall;
[FUNC(openBox), _this] call CBA_fnc_directCall;
};
private _displayToUse = findDisplay IDD_RSCDISPLAYCURATOR;
@ -46,37 +48,23 @@ if (isNull _displayToUse || {!isNil QGVAR(camera)}) exitWith {
[LLSTRING(CantOpenDisplay), false, 5, 1] call EFUNC(common,displayText);
};
GVAR(center) = _center;
GVAR(currentBox) = _object;
if (_mode) then {
// Add all the items from the game that the arsenal has detected
GVAR(virtualItems) = +(uiNamespace getVariable QGVAR(configItems));
GVAR(virtualItemsFlat) = +(uiNamespace getVariable QGVAR(configItemsFlat));
GVAR(ignoredVirtualItems) = true;
} else {
// Add only specified items to the arsenal
private _virtualItems = _object getVariable QGVAR(virtualItems);
GVAR(virtualItems) = if (isNil "_virtualItems") then {
_virtualItems = [
[IDX_VIRT_WEAPONS, createHashMapFromArray [[IDX_VIRT_PRIMARY_WEAPONS, createHashMap], [IDX_VIRT_SECONDARY_WEAPONS, createHashMap], [IDX_VIRT_HANDGUN_WEAPONS, createHashMap]]],
[IDX_VIRT_ATTACHMENTS, createHashMapFromArray [[IDX_VIRT_OPTICS_ATTACHMENTS, createHashMap], [IDX_VIRT_FLASHLIGHT_ATTACHMENTS, createHashMap], [IDX_VIRT_MUZZLE_ATTACHMENTS, createHashMap], [IDX_VIRT_BIPOD_ATTACHMENTS, createHashMap]]]
];
_virtualItems = createHashMapFromArray _virtualItems;
for "_index" from IDX_VIRT_ITEMS_ALL to IDX_VIRT_MISC_ITEMS do {
_virtualItems set [_index, createHashMap];
};
} else {
+_virtualItems
};
GVAR(virtualItems) = +_virtualItems;
// Flatten out hashmaps for easy checking later
call FUNC(updateVirtualItemsFlat);
};
GVAR(center) = _center;
if (is3DEN) then {
_displayToUse createDisplay QGVAR(display);
} else {

View File

@ -25,6 +25,11 @@ if (canSuspend) exitWith {
[{_this call FUNC(refresh)}, _this] call CBA_fnc_directCall;
};
private _display = findDisplay IDD_ace_arsenal;
// Exit quietly if no display found
if (isNull _display) exitWith {};
if (_updateItems) then {
// Update current item list
call FUNC(updateCurrentItemsList);
@ -42,7 +47,8 @@ if (is3DEN) then {
_animate = true; // CBA frame functions are disabled during preInit
};
if (isNil "_virtualItems") exitWith {
// Do not close an arsenal if it was opened with ignoring the existing content (see FUNC(openBox))
if (isNil "_virtualItems" && {isNil QGVAR(ignoredVirtualItems)}) exitWith {
[LLSTRING(noVirtualItems), false, 5, 1] call EFUNC(common,displayText);
// Delay a frame in case this is running on display open
[{(findDisplay IDD_ace_arsenal) closeDisplay 0}] call CBA_fnc_execNextFrame;
@ -64,6 +70,4 @@ if (!_animate) then {
[{GVAR(refreshing) = false}, nil, 3] call CBA_fnc_execAfterNFrames;
};
private _display = findDisplay IDD_ace_arsenal;
[_display, _display displayCtrl GVAR(currentLeftPanel), _animate] call FUNC(fillLeftPanel);

View File

@ -25,7 +25,7 @@ private _id = _object getVariable QGVAR(initBoxJIP);
if (_global && {isMultiplayer} && {!isNil "_id"}) then {
// Remove event from JIP queue
[_id] call CBA_fnc_removeGlobalEventJIP;
_id call CBA_fnc_removeGlobalEventJIP;
// Reset JIP ID
_object setVariable [QGVAR(initBoxJIP), nil, true];
@ -41,9 +41,15 @@ if (_global && {isMultiplayer} && {!isNil "_id"}) then {
[QGVAR(boxRemoved), _object] call CBA_fnc_localEvent;
};
// If the arsenal is already open, close arsenal display
if (!isNil QGVAR(currentBox) && {GVAR(currentBox) isEqualTo _object}) then {
[LLSTRING(noVirtualItems), false, 5, 1] call EFUNC(common,displayText);
// Delay a frame in case this is running on display open
[{(findDisplay IDD_ace_arsenal) closeDisplay 0}] call CBA_fnc_execNextFrame;
// If the arsenal is already open and not ignoring content (see FUNC(openBox)), close arsenal display
// Deliberate == check, fail on objNull
if (!isNil QGVAR(currentBox) && {GVAR(currentBox) == _object} && {isNil QGVAR(ignoredVirtualItems)}) then {
// Delay a frame in case this is running on display open/close
[{
private _display = findDisplay IDD_ace_arsenal;
if (isNull _display) exitWith {};
[LLSTRING(noVirtualItems), false, 5, 1] call EFUNC(common,displayText);
_display closeDisplay 0;
}] call CBA_fnc_execNextFrame;
};

View File

@ -17,8 +17,10 @@
*/
params [["_name", "", [""]], ["_global", false, [false]]];
if (_global) then {
[QGVAR(removeDefaultLoadout), [_name]] call CBA_fnc_remoteEvent;
if (_global) exitWith {
private _eventID = format [QGVAR(loadouts_%1), _name];
[_eventID] call CBA_fnc_removeGlobalEventJIP;
[QGVAR(removeDefaultLoadout), [_name]] call CBA_fnc_globalEvent;
};
GVAR(defaultLoadoutsList) deleteAt (GVAR(defaultLoadoutsList) findIf {(_x select 0) == _name});

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