Artillery rangletables (universal) (#6853)

* Initial

* Update artillerytables.cpp

* Pass by value, remove c17 features

* Tweak accuracy

* SQF work

- improve compat with a3 mlrs with remote cam (animationSourcePhase)
- handle non [0] turrets (rhs prp)
- add config entries
- use vectorCos to fix fp error (thanks commy)

* Support per mag air friction

* tweak friction

* Integrate with mk6

* more acos fixes

* Handle invalid memPointGunOptic (CUP_BM21_Base)

* Cleanup

* cleanup/tweaks

* Update checkConfigs.sqf

* Finish cleanup of ace_mk6mortar

* Update stringtable.xml

* fix bwc for ACE_RangeTable_82mm

* Update fnc_rangeTableCanUse.sqf

* build 32dll, fix some headers

* strncpy and move testing to seperate file

* Move to sub-category

* Update for ACE_Extensions changes and add warning to ace_common

* Update stringtable.xml

* Update addons/common/functions/fnc_checkFiles.sqf

Co-Authored-By: jonpas <jonpas33@gmail.com>

* Update stringtable.xml

* Update stringtable.xml

* test extension.yml update logical operator

* Revert "test extension.yml update logical operator"

This reverts commit b1871724ad.

* more guess and test
This commit is contained in:
PabstMirror 2019-12-17 10:14:45 -06:00 committed by GitHub
parent 240c7333e3
commit 8e3fd45500
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
58 changed files with 1483 additions and 786 deletions

View File

@ -20,6 +20,7 @@ jobs:
- name: Checkout the source code - name: Checkout the source code
uses: actions/checkout@master uses: actions/checkout@master
- name: Build - name: Build
shell: cmd
run: | run: |
cd extensions/build cd extensions/build
cmake .. && cmake --build . cmake .. && cmake --build .

BIN
ace_artillerytables.dll Normal file

Binary file not shown.

BIN
ace_artillerytables_x64.dll Normal file

Binary file not shown.

View File

@ -0,0 +1 @@
z\ace\addons\artillerytables

View File

@ -0,0 +1,15 @@
class Extended_PreStart_EventHandlers {
class ADDON {
init = QUOTE(call COMPILE_FILE(XEH_preStart));
};
};
class Extended_PreInit_EventHandlers {
class ADDON {
init = QUOTE(call COMPILE_FILE(XEH_preInit));
};
};
class Extended_PostInit_EventHandlers {
class ADDON {
init = QUOTE(call COMPILE_FILE(XEH_postInit));
};
};

View File

@ -0,0 +1,6 @@
class CfgMagazines {
class 32Rnd_155mm_Mo_shells;
class 8Rnd_82mm_Mo_shells: 32Rnd_155mm_Mo_shells {
GVAR(airFriction) = -0.0001;
};
};

View File

@ -0,0 +1,7 @@
class CfgVehicles {
class StaticWeapon;
class StaticMortar: StaticWeapon {
// Small mortars seem to need the alternate elevation calculations,
GVAR(showGunLaying) = 2;
};
};

View File

@ -0,0 +1,15 @@
class CfgWeapons {
class ACE_ItemCore;
class CBA_MiscItem_ItemInfo;
class ACE_artilleryTable: ACE_ItemCore {
author = ECSTRING(common,ACETeam);
scope = 2;
displayName = CSTRING(rangetable_displayName);
descriptionShort = CSTRING(rangetable_description);
picture = QPATHTOF(UI\icon_rangeTable.paa);
class ItemInfo: CBA_MiscItem_ItemInfo {
mass = 0.5;
};
};
};

View File

@ -0,0 +1,14 @@
ace_artillerytables
==========
Universal artillery rangetables.
#### Items Added:
`ACE_artilleryTable`
## Maintainers
The people responsible for merging changes to this component or answering potential questions.
- [PabstMirror](https://github.com/PabstMirror)

View File

@ -0,0 +1,96 @@
class GVAR(rangeTableDialog) {
idd = -1;
movingEnable = 1;
onLoad = QUOTE(with uiNameSpace do { GVAR(rangeTableDialog) = _this select 0 };);
objects[] = {};
class ControlsBackground {
class TableBackground: RscPicture {
idc = -1;
text = QPATHTOF(UI\RangeTable_background.paa);
x = "18 *(safeZoneH / 40) + (safezoneX + (safezoneW - safeZoneH) / 2)";
y = "1 * ((safeZoneH / 1.2) / 25) + (safezoneY + (safezoneH - (safeZoneH / 1.2)) / 2)";
w = "16.2634559672906 * (safeZoneH / 40)";
h = "23 * ((safeZoneH / 1.2) / 25)";
colorBackground[] = {1,1,1,1};
};
class LeftSideBackground: RscText {
idc = -1;
x = "13 *(safeZoneH / 40) + (safezoneX + (safezoneW - safeZoneH) / 2)";
y = "1 * ((safeZoneH / 1.2) / 25) + (safezoneY + (safezoneH - (safeZoneH / 1.2)) / 2)";
w = "5 * (safeZoneH / 40)";
h = "23 * ((safeZoneH / 1.2) / 25)";
colorBackground[] = {0,0,0,0.8};
};
};
class controls {
class TheTable: RscListNBox {
idc = IDC_TABLE;
x = "18 *(safeZoneH / 40) + (safezoneX + (safezoneW - safeZoneH) / 2)";
y = "3.76 * ((safeZoneH / 1.2) / 25) + (safezoneY + (safezoneH - (safeZoneH / 1.2)) / 2)";
w = "16.2634559672906 * (safeZoneH / 40)";
h = "20.24 * ((safeZoneH / 1.2) / 25)";
columns[] = {(10/867),(86/867),(171/867),(238/867),(320/867),(405/867),(485/867),(546/867),(607/867),(668/867),(729/867),(790/867)};
rowHeight = 0.015 * safeZoneH;
sizeEx = "0.014 * safeZoneH";
font = "EtelkaMonospacePro";
drawSideArrows = 1;
idcLeft = -1;
idcRight = -1;
colorText[] = {0, 0, 0, 1};
shadow = "0";
colorSelectBackground[] = {0, 0, 0, 0.025};
colorSelectBackground2[] = {0, 0, 0, 0.025};
colorScrollbar[] = {0.95,0,0.95,1};
class ListScrollBar: ScrollBar{
color[] = {0,0,0,0.6};
};
};
class ChargeListBox: RscListbox {
idc = IDC_CHARGELIST;
style = ST_RIGHT;
x = "13 *(safeZoneH / 40) + (safezoneX + (safezoneW - safeZoneH) / 2)";
y = "2 * ((safeZoneH / 1.2) / 25) + (safezoneY + (safezoneH - (safeZoneH / 1.2)) / 2)";
w = "5 * (safeZoneH / 40)";
h = "22 * ((safeZoneH / 1.2) / 25)";
onLBSelChanged = QUOTE([] call FUNC(rangeTableUpdate));
};
class elevationHigh: ctrlButton {
idc = IDC_BUTTON_ELEV_HIGH;
text = "High";
onButtonClick = QUOTE([true] call FUNC(rangeTableUpdate));
x = "13.1 *(safeZoneH / 40) + (safezoneX + (safezoneW - safeZoneH) / 2)";
y = "1.1 * ((safeZoneH / 1.2) / 25) + (safezoneY + (safezoneH - (safeZoneH / 1.2)) / 2)";
w = "2.3 * (safeZoneH / 40)";
h = "0.8 * ((safeZoneH / 1.2) / 25)";
};
class elevationLow: elevationHigh {
idc = IDC_BUTTON_ELEV_LOW;
text = "Low";
onButtonClick = QUOTE([false] call FUNC(rangeTableUpdate));
x = "15.6 *(safeZoneH / 40) + (safezoneX + (safezoneW - safeZoneH) / 2)";
};
class CloseBackground: RscText {
idc = -1;
x = "33.7634559672906 *(safeZoneH / 40) + (safezoneX + (safezoneW - safeZoneH) / 2)";
y = "1 * ((safeZoneH / 1.2) / 25) + (safezoneY + (safezoneH - (safeZoneH / 1.2)) / 2)";
w = "0.5 * (safeZoneH / 40)";
h = "0.5 * ((safeZoneH / 1.2) / 25)";
colorBackground[] = {0,0,0,0.5};
};
class CloseActiveText: RscActiveText {
idc = -1;
style = 48;
color[] = {1,1,1,0.7};
text = "A3\Ui_f\data\GUI\Rsc\RscDisplayArcadeMap\icon_exit_cross_ca.paa";
x = "33.7634559672906 *(safeZoneH / 40) + (safezoneX + (safezoneW - safeZoneH) / 2)";
y = "1 * ((safeZoneH / 1.2) / 25) + (safezoneY + (safezoneH - (safeZoneH / 1.2)) / 2)";
w = "0.5 * (safeZoneH / 40)";
h = "0.5 * ((safeZoneH / 1.2) / 25)";
colorText[] = {1,1,1,0.7};
colorActive[] = {1,1,1,1};
tooltip = "Close";
onButtonClick = "closeDialog 0";
};
};
};

View File

@ -0,0 +1,42 @@
class RscTitles {
class GVAR(modeDisplay) {
idd = -1;
onLoad = QUOTE(with uiNameSpace do { GVAR(display) = _this select 0 };);
movingEnable = 0;
duration = 60;
fadeIn = "false";
fadeOut = "false";
class controls {
class ModeControlGroup: RscControlsGroupNoScrollbars {
idc = IDC_MODECONTROLGROUP;
x = "3.8 * (((safezoneW / safezoneH) min 1.2) / 40) + (profilenamespace getvariable ['IGUI_GRID_WEAPON_X',((safezoneX + safezoneW) - (10 * (((safezoneW / safezoneH) min 1.2) / 40)) - 4.3 * (((safezoneW / safezoneH) min 1.2) / 40))])";
y = "2.5 * ((((safezoneW / safezoneH) min 1.2) / 1.2) / 25) + (profilenamespace getVariable ['IGUI_GRID_WEAPON_Y', (safezoneY + 0.5 * ((((safezoneW / safezoneH) min 1.2) / 1.2) / 25))])";
w = "10 * (((safezoneW / safezoneH) min 1.2) / 40)";
h = "1 * ((((safezoneW / safezoneH) min 1.2) / 1.2) / 25)";
class controls {
class Charge: RscText {
idc = IDC_CHARGE;
colorText[] = {1, 1, 1, 1};
colorBackground[] = {0, 0, 0, 0};
x = "0";
y = "0";
w = "(2) * (((safezoneW / safezoneH) min 1.2) / 40)";
h = "1 * ((((safezoneW / safezoneH) min 1.2) / 1.2) / 25)";
sizeEx = "0.8 * ((((safezoneW / safezoneH) min 1.2) / 1.2) / 25)";
};
class Azimuth: Charge {
idc = IDC_AZIMUTH;
x = "(2) * ((((safezoneW / safezoneH) min 1.2) / 1.2) / 25)";
w = "(3) * (((safezoneW / safezoneH) min 1.2) / 40)";
};
class Elevation: Azimuth {
idc = IDC_ELEVATION;
x = "(5) * ((((safezoneW / safezoneH) min 1.2) / 1.2) / 25)";
};
};
};
};
};
};

Binary file not shown.

Binary file not shown.

View File

@ -0,0 +1,8 @@
TRACE_1("prep",_this);
PREP(firedEH);
PREP(interactMenuOpened);
PREP(rangeTableOpen);
PREP(rangeTableUpdate);
PREP(turretChanged);
PREP(turretPFEH);

View File

@ -0,0 +1,36 @@
#include "script_component.hpp"
["ace_settingsInitialized", {
TRACE_2("ace_settingsInitialized",GVAR(advancedCorrections),GVAR(disableArtilleryComputer));
if (hasInterface) then {
// Add hud overlay for actuall azimuth and elevation:
GVAR(pfID) = -1;
["turret", LINKFUNC(turretChanged), true] call CBA_fnc_addPlayerEventHandler;
// Add ability to dynamically open rangetables:
["ace_interactMenuOpened", LINKFUNC(interactMenuOpened)] call CBA_fnc_addEventHandler;
};
if (GVAR(advancedCorrections)) then {
["LandVehicle", "init", {
params ["_vehicle"];
private _vehicleCfg = configFile >> "CfgVehicles" >> typeOf _vehicle;
// config "ace_artillerytables_applyCorrections" [0 disabled, 1 enabled] falls back to artilleryScanner
private _applyCorrections = if (isNumber (_vehicleCfg >> QGVAR(applyCorrections))) then {
getNumber (_vehicleCfg >> QGVAR(applyCorrections))
} else {
getNumber (_vehicleCfg >> "artilleryScanner")
};
if (_applyCorrections == 1) then {
TRACE_2("adding firedEH",_vehicle,configName _vehicleCfg);
_vehicle addEventHandler ["Fired", {call FUNC(firedEH)}];
};
}, true, [], true] call CBA_fnc_addClassEventHandler;
};
}] call CBA_fnc_addEventHandler;
#ifdef DEBUG_MODE_FULL
#include "dev\showShotInfo.sqf"
#include "dev\checkConfigs.sqf"
#endif

View File

@ -0,0 +1,11 @@
#include "script_component.hpp"
ADDON = false;
PREP_RECOMPILE_START;
#include "XEH_PREP.hpp"
PREP_RECOMPILE_END;
#include "initSettings.sqf"
ADDON = true;

View File

@ -0,0 +1,3 @@
#include "script_component.hpp"
#include "XEH_PREP.hpp"

View File

@ -0,0 +1,44 @@
#include "script_component.hpp"
class CfgPatches {
class ADDON {
name = COMPONENT_NAME;
units[] = {};
weapons[] = {};
requiredVersion = REQUIRED_VERSION;
requiredAddons[] = {"ace_interaction"};
author = ECSTRING(common,ACETeam);
authors[] = {"PabstMirror"};
url = ECSTRING(main,URL);
VERSION_CONFIG;
};
};
class ACE_Extensions {
class ace_artillerytables {
windows = 1;
client = 1;
};
};
#include "CfgEventHandlers.hpp"
#include "CfgMagazines.hpp"
#include "CfgVehicles.hpp"
#include "CfgWeapons.hpp"
// Common UI Stuff:
class RscText;
class RscListbox;
class RscListNBox;
class RscPicture;
class RscControlsGroup;
class RscControlsGroupNoScrollbars;
class ScrollBar;
class RscActiveText;
class RscStructuredText;
class ctrlButton;
#include "RscTitles.hpp"
#include "RscRangeTable.hpp"

View File

@ -0,0 +1,20 @@
diag_log text "-------------------------------------------";
INFO("Showing entries with custom configs");
diag_log text "-------------------------------------------";
private _fnc_showPropertyDefined = {
params ["_configBase", "_configProperty"];
private _customAll = configProperties [_configBase, 'isClass _x && {isNumber (_x >> _configProperty)}', true];
private _customExplicit = _customAll select {!([] isEqualTo configProperties [_x, 'configName _x == _configProperty', false])};
diag_log text format ["%1 with custom %2: %3 Explicit, %4 Total]", configName _configBase, _configProperty, count _customExplicit, count _customAll];
diag_log text format [" - Defined: %1", _customExplicit apply {configName _x}];
diag_log text format [" - Inherited: %1", _customAll apply {[configName _x, getNumber (_x >> _configProperty)]}];
};
[configFile >> "CfgMagazines", QGVAR(airFriction)] call _fnc_showPropertyDefined;
[configFile >> "CfgVehicles", QGVAR(showGunLaying)] call _fnc_showPropertyDefined;
[configFile >> "CfgVehicles", QGVAR(applyCorrections)] call _fnc_showPropertyDefined;
diag_log text "-------------------------------------------";

View File

@ -0,0 +1,47 @@
// #include "\z\ace\addons\artillerytables\script_component.hpp"
INFO("showing shot info");
["LandVehicle", "fired", {
params ["_shooter", "", "", "", "_ammo", "", "_proj"];
((velocity _proj) call CBA_fnc_vect2Polar) params ["_mag", "_dir", "_elev"];
private _shootPos = getPosASL _shooter;
if (_dir < 0) then {_dir = _dir + 360;};
private _offsetElev = _elev - (missionNamespace getVariable [QGVAR(predictedElevation), -999]);
private _offsetAz = _dir - (missionNamespace getVariable [QGVAR(predictedAzimuth), -999]);
hintSilent format ["%1 m/s\nAz: %2 [%3]\nEl: %4 [%5]\nError Az: %6\nError EL: %7",_mag toFixed 1, _dir toFixed 2, ((6400 / 360) * _dir) toFixed 0, _elev toFixed 2, ((6400 / 360) * _elev) toFixed 0,
_offsetAz toFixed 3, _offsetElev toFixed 3];
TRACE_2("",_offsetAz,_offsetElev);
private _submunitionAmmo = getText (configFile >> "CfgAmmo" >> _ammo >> "submunitionAmmo");
[{
params ["_proj", "_shootPos", "_lastPos", "_submunitionAmmo"];
if ((isNull _proj) && {_submunitionAmmo != ""}) then {
_proj = nearestObject [_lastPos, _submunitionAmmo];
_this set [0, _proj];
};
if (isNull _proj) exitWith {true};
_this set [2, getPosASL _proj];
false
}, {
params ["", "_shootPos", "_lastPos"];
private _mkrB = createMarker [format ["shotInfo-%1-%2",_shootPos,_lastPos], _lastPos];
_mkrB setMarkerType "mil_dot";
_mkrB setMarkerColor "ColorGreen";
_mkrB setMarkerSize [0.5,0.5];
private _diff = _lastPos vectorDiff _shootPos;
_mkrB setMarkerText format ["%1", _diff apply {round _x}];
private _dist2d = _shootPos distance2d _lastPos;
private _dir = _shootPos getDir _lastPos;
private _height = (_lastPos select 2) - (_shootPos select 2);
_mkrB setMarkerText format ["Dist: %1m Az: %2[%3] Height:%4", _dist2d toFixed 0, _dir toFixed 2, ((6400 / 360) * _dir) toFixed 0, _height toFixed 0];
}, [_proj, _shootPos, [0,0,0], _submunitionAmmo]] call CBA_fnc_waitUntilAndExecute;
private _mkrA = createMarker [format ["shotInfo-%1",_shootPos], _shootPos];
_mkrA setMarkerType "mil_dot";
_mkrA setMarkerColor "ColorRed";
_mkrA setMarkerSize [0.5,0.5];
}] call CBA_fnc_addClassEventHandler;

View File

@ -0,0 +1,88 @@
#include "script_component.hpp"
/*
* Author: PabstMirror
* Called when the mortar is fired.
*
* Arguments:
* 0: mortar - Object the event handler is assigned to <OBJECT>
* 1: weapon - Fired weapon <STRING>
* 2: muzzle - Muzzle that was used <STRING>
* 3: mode - Current mode of the fired weapon <STRING>
* 4: ammo - Ammo used <STRING>
* 5: magazine - magazine name which was used <STRING>
* 6: projectile - Object of the projectile that was shot <OBJECT>
* 7: gunner - Gunner <OBJECT>
*
* Return Value:
* None
*
* Example:
* [bisFiredEH] call ace_artillerytables_fnc_firedEH
*
* Public: No
*/
params ["_vehicle", "", "", "", "", "_magazine", "_projectile", "_gunner"];
TRACE_4("firedEH",_vehicle,_magazine,_projectile,_gunner);
if (!([_gunner] call EFUNC(common,isPlayer))) exitWith {}; // AI don't know how to use (this does give them more range than a player)
if ((gunner _vehicle) != _gunner) exitWith {}; // check if primaryGunner
// Get airFriction
private _airFriction = DEFAULT_AIR_FRICTION;
if (isNumber (configFile >> "CfgMagazines" >> _magazine >> QGVAR(airFriction))) then {
_airFriction = getNumber (configFile >> "CfgMagazines" >> _magazine >> QGVAR(airFriction));
};
TRACE_1("",_airFriction);
if (_airFriction >= 0) exitWith {}; // 0 disables everything, >0 makes no sense
BEGIN_COUNTER(adjustmentsCalc);
// Calculate air density
private _altitude = (getPosASL _vehicle) select 2;
private _temperature = _altitude call EFUNC(weather,calculateTemperatureAtHeight);
private _pressure = _altitude call EFUNC(weather,calculateBarometricPressure);
private _relativeHumidity = EGVAR(weather,currentHumidity);
private _airDensity = [_temperature, _pressure, _relativeHumidity] call EFUNC(weather,calculateAirDensity);
private _relativeDensity = _airDensity / 1.225;
TRACE_5("Weather",_temperature,_pressure,_relativeHumidity,_airDensity,_relativeDensity);
private _kFactor = _airFriction * _relativeDensity;
private _newMuzzleVelocityCoefficent = (((_temperature + 273.13) / 288.13 - 1) / 40 + 1);
TRACE_2("",_kFactor,_newMuzzleVelocityCoefficent);
// Apply powder effects
if (_newMuzzleVelocityCoefficent != 1) then {
private _bulletVelocity = velocity _projectile;
private _bulletSpeed = vectorMagnitude _bulletVelocity;
_bulletVelocity = (vectorNormalized _bulletVelocity) vectorMultiply (_bulletSpeed * _newMuzzleVelocityCoefficent);
_projectile setVelocity _bulletVelocity;
};
[{
params ["_projectile", "_kFactor", "_time"];
if (isNull _projectile) exitWith {true};
private _deltaT = CBA_missionTime - _time;
_this set[2, CBA_missionTime];
private _bulletVelocity = velocity _projectile;
private _trueVelocity = _bulletVelocity vectorDiff wind;
private _trueSpeed = vectorMagnitude _trueVelocity;
private _drag = _deltaT * _kFactor * _trueSpeed;
private _accel = _trueVelocity vectorMultiply _drag;
// TRACE_3("",_bulletVelocity,_trueSpeed,_accel);
_bulletVelocity = _bulletVelocity vectorAdd _accel;
_projectile setVelocity _bulletVelocity;
false
}, {
// TRACE_1("done",_this);
}, [_projectile, _kFactor, CBA_missionTime]] call CBA_fnc_waitUntilAndExecute;
END_COUNTER(adjustmentsCalc);

View File

@ -0,0 +1,108 @@
#include "script_component.hpp"
/*
* Author: PabstMirror
* Interaction menu opened, search for nearby artillery vehicles.
*
* Arguments:
* 0: Menu Type (1 is self interaction) <NUMBER>
*
* Return Value:
* Can Open <BOOL>
*
* Example:
* [1] call ace_artillerytables_fnc_interactMenuOpened
*
* Public: No
*/
params ["_menuType"];
TRACE_1("interactMenuOpened",_menuType);
if (_menuType != 1) exitWith {};
if (!("ACE_artilleryTable" in (ace_player call EFUNC(common,uniqueItems)))) exitWith {};
private _vehicleAdded = ace_player getVariable [QGVAR(vehiclesAdded), []];
private _rangeTablesShown = ace_player getVariable [QGVAR(rangeTablesShown), []];
TRACE_2("searching for new vehicles",_vehicleAdded,_rangeTablesShown);
{
private _vehicleCfg = configFile >> "CfgVehicles" >> typeOf _x;
// config "ace_artillerytables_showRangetable" [0 disabled, 1 enabled] falls back to artilleryScanner
private _showRangetable = if (isNumber (_vehicleCfg >> QGVAR(showRangetable))) then {
getNumber (_vehicleCfg >> QGVAR(showRangetable))
} else {
getNumber (_vehicleCfg >> "artilleryScanner")
};
if ((_showRangetable == 1) && {!(_x in _vehicleAdded)}) then {
private _vehicle = _x;
private _turret = [];
private _turretCfg = configNull; // find turret with artillery, will be one with primaryGunner = 1 (e.g. RHS PRP-3)
{
private _xTurretCfg = [_vehicleCfg, _x] call CBA_fnc_getTurret;
if ((getNumber (_xTurretCfg >> "primaryGunner")) == 1) exitWith {
_turret = _x;
_turretCfg = _xTurretCfg;
};
} forEach allTurrets _vehicle;
TRACE_3("",_vehicle,configName _vehicleCfg,_turret);
if (isNull _turretCfg) exitWith { ERROR_1("no primaryGunner %1",configName _vehicleCfg); };
if ((count _turret) != 1) then { WARNING_2("sub turret %1-%2",_typeOf,_turret); };
private _weaponsTurret = _vehicle weaponsTurret _turret;
if ((count _weaponsTurret) != 1) exitWith { WARNING_1("multiple weapons - %1",_typeOf); };
private _weapon = _weaponsTurret select 0;
private _turretAnimBody = getText (_turretCfg >> "animationSourceBody");
private _turretAnimGun = getText (_turretCfg >> "animationSourceGun");
// For artillery with detached camera (I_Truck_02_MRL_F) need to use animationSourcePhase
// But that command won't always work, so fallback to animationPhase
private _currentElevRad = _vehicle animationSourcePhase _turretAnimGun;
if (isNil "_currentElevRad") then { _currentElevRad = _vehicle animationPhase _turretAnimGun; };
private _currentTraverseRad = _vehicle animationSourcePhase _turretAnimBody;
if (isNil "_currentTraverseRad") then { _currentTraverseRad = _vehicle animationPhase _turretAnimBody; };
// Some turrets (MK6) have a neutralX rotation that we need to add to min/max config elevation to get actual limits
private _weaponDir = _vehicle weaponDirection _weapon;
private _turretRot = [vectorDir _vehicle, vectorUp _vehicle, deg _currentTraverseRad] call CBA_fnc_vectRotate3D;
private _neutralX = (acos ((_turretRot vectorCos _weaponDir) min 1)) - (deg _currentElevRad); // vectorCos can return values outside of -1..1
_neutralX = (round (_neutralX * 10)) / 10; // minimize floating point errors
private _minElev = _neutralX + getNumber (_turretCfg >> "minElev");
private _maxElev = _neutralX + getNumber (_turretCfg >> "maxElev");
private _applyCorrections = if (isNumber (_vehicleCfg >> QGVAR(applyCorrections))) then {
getNumber (_vehicleCfg >> QGVAR(applyCorrections))
} else {
getNumber (_vehicleCfg >> "artilleryScanner")
};
private _advCorrection = GVAR(advancedCorrections) && {_applyCorrections == 1};
if ((missionNamespace getVariable [QEGVAR(mk6Mortar,airResistanceEnabled), false]) && {_vehicle isKindOf "Mortar_01_base_F"}) then {
_advCorrection = true;
};
// check weapon and limits in case different vehicles use the same weapon (cammo variants should still produce the same array)
private _info = [_weapon, _minElev, _maxElev, _advCorrection];
_vehicleAdded pushBack _vehicle;
ace_player setVariable [QGVAR(vehiclesAdded), _vehicleAdded];
private _index = _rangeTablesShown pushBackUnique _info;
TRACE_2("",_info,_index);
if (_index != -1) then {
private _statement = {
params ["", "", "_info"];
TRACE_1("interaction statement",_info);
[FUNC(rangeTableOpen), _info] call CBA_fnc_execNextFrame; // delay a frame because of interaction menu closing dialogs
};
private _condition = {
//IGNORE_PRIVATE_WARNING ["_player"];
("ACE_artilleryTable" in (_player call EFUNC(common,uniqueItems))) && {[_player, objNull, ["notOnMap", "isNotSitting", "isNotInside"]] call EFUNC(common,canInteractWith)}
};
private _displayName = format ["%1%2", getText (_vehicleCfg >> "displayName"),["","*"] select _advCorrection];
private _action = [format ['QGVAR(%1)',_index], _displayName, QPATHTOF(UI\icon_rangeTable.paa), _statement, _condition, {}, _info] call EFUNC(interact_menu,createAction);
[ace_player, 1, ["ACE_SelfActions", "ACE_Equipment"], _action] call EFUNC(interact_menu,addActionToObject);
TRACE_1("added action",_displayName);
ace_player setVariable [QGVAR(rangeTablesShown), _rangeTablesShown];
};
};
} forEach (nearestObjects [ace_player, ["LandVehicle"], 25]);

View File

@ -0,0 +1,87 @@
#include "script_component.hpp"
/*
* Author: PabstMirror
* Opens the rangetable and fills the charge listbox.
*
* Arguments:
* 0: Weapon configname <STRING>
* 1: Elevation Min (Deg) <NUMBER>
* 2: Elevation Max (Deg) <NUMBER>
* 3: Advanced Corrections Enabled <BOOL>
*
* Return Value:
* None
*
* Example:
* ["mortar_155mm_AMOS", -5, 80, true] call ace_artillerytables_fnc_rangeTableOpen
*
* Public: No
*/
params ["_weaponName", "_elevMin", "_elevMax", "_advCorrection"];
TRACE_4("rangeTableOpen",_weaponName,_elevMin,_elevMax,_advCorrection);
BEGIN_COUNTER(rangeTableOpen);
createDialog QGVAR(rangeTableDialog);
private _dialog = uiNamespace getVariable [QGVAR(rangeTableDialog), displayNull];
if (isNull _dialog) exitWith{ERROR("Dialog failed to open");};
private _ctrlChargeList = _dialog displayCtrl IDC_CHARGELIST;
_dialog setVariable [QGVAR(elevMin),_elevMin];
_dialog setVariable [QGVAR(elevMax),_elevMax];
_dialog setVariable [QGVAR(advCorrection),_advCorrection];
TRACE_2("created dialog",_dialog,_ctrlChargeList);
// Get Mags:
private _mags = [_weaponName] call CBA_fnc_compatibleMagazines;
if (_mags isEqualTo []) exitWith {WARNING_1("No Mags",_weaponName);};
private _magCfg = configFile >> "CfgMagazines";
private _magParamsArray = [];
_mags = _mags apply {
private _initSpeed = getNumber (_magCfg >> _x >> "initSpeed");
_magParamsArray pushBackUnique _initSpeed;
private _airFriction = 0;
if (_advCorrection) then {
_airFriction = if (isNumber (_magCfg >> _x >> QGVAR(airFriction))) then { getNumber (_magCfg >> _x >> QGVAR(airFriction)) } else { DEFAULT_AIR_FRICTION };
};
_magParamsArray pushBackUnique _airFriction;
[getText (_magCfg >> _x >> "displayNameShort"), getText (_magCfg >> _x >> "displayName"), _initSpeed, _airFriction]
};
TRACE_2("",_magParamsArray,_mags);
if ((count _magParamsArray) == 2) then { // test if all magazines share the parameters
_mags = [["", "All Magazines", (_mags select 0) select 2, (_mags select 0) select 3]]; // simplify
};
// Get Firemodes:
private _fireModes = getArray (configFile >> "CfgWeapons" >> _weaponName >> "modes");
_fireModes = (_fireModes apply {configFile >> "CfgWeapons" >> _weaponName >> _x}) select {1 == getNumber (_x >> "showToPlayer")};
_fireModes = _fireModes apply {[getNumber (_x >> "artilleryCharge"), configName _x]};
_fireModes sort true;
private _allSameCharge = ((count _fireModes) == 1) && {((_fireModes select 0) select 0) == 1};
TRACE_2("",_allSameCharge,_fireModes);
GVAR(magModeData) = [];
{
_x params ["_xDisplayNameShort", "_xDisplayName", "_xInitSpeed", "_xAirFriction"];
if (_allSameCharge) then {
_ctrlChargeList lbAdd format ["%1", _xDisplayNameShort];
_ctrlChargeList lbSetTooltip [count GVAR(magModeData), format ["%1\n%2 m/s\n%3", _xDisplayName, _xInitSpeed toFixed 1, _xAirFriction]];
GVAR(magModeData) pushBack [_xInitSpeed, _xAirFriction];
} else {
{
_x params ["_xModeCharge"];
_ctrlChargeList lbAdd format ["[Charge %1] %2", _forEachIndex, _xDisplayNameShort]; // forEachIndex is from firemodes
_ctrlChargeList lbSetTooltip [count GVAR(magModeData), format ["%1\n%2 m/s\n%3", _xDisplayName, (_xInitSpeed * _xModeCharge) toFixed 1, _xAirFriction]];
GVAR(magModeData) pushBack [_xInitSpeed * _xModeCharge, _xAirFriction];
} forEach _fireModes;
};
} forEach _mags;
if (isNil QGVAR(lastElevationMode)) then {GVAR(lastElevationMode) = true;};
if (isNil QGVAR(lastTablePage)) then {GVAR(lastTablePage) = 0;};
if ((GVAR(lastTablePage) >= (count GVAR(magModeData))) || {GVAR(lastTablePage) < 0}) then { GVAR(lastTablePage) = 0; };
END_COUNTER(rangeTableOpen);
TRACE_2("trigger update",GVAR(lastElevationMode),GVAR(lastTablePage));
_ctrlChargeList lbSetCurSel GVAR(lastTablePage); // triggers call to FUNC(rangeTableUpdate)

View File

@ -0,0 +1,64 @@
#include "script_component.hpp"
/*
* Author: PabstMirror
* Called when listbox selection changes. Updates the rangetable with new values.
*
* Arguments:
* 0: Elevation Mode (true = high,false=low) <BOOL><OPTIONAL>
*
* Return Value:
* None
*
* Example:
* [] call ace_artillerytables_fnc_rangeTableUpdate
*
* Public: No
*/
private _dialog = uiNamespace getVariable [QGVAR(rangeTableDialog), displayNull];
private _ctrlRangeTable = _dialog displayCtrl IDC_TABLE;
private _ctrlChargeList = _dialog displayCtrl IDC_CHARGELIST;
private _ctrlElevationHigh = _dialog displayCtrl IDC_BUTTON_ELEV_HIGH;
private _ctrlElevationLow = _dialog displayCtrl IDC_BUTTON_ELEV_LOW;
GVAR(lastElevationMode) = param [0, GVAR(lastElevationMode)]; // update if passed a new value
GVAR(lastCharge) = lbCurSel _ctrlChargeList;
// get data for currently selected mag/mode combo:
(GVAR(magModeData) select GVAR(lastCharge)) params [["_muzzleVelocity", -1], ["_airFriction", 0]];
private _elevMin = _dialog getVariable [QGVAR(elevMin), 0];
private _elevMax = _dialog getVariable [QGVAR(elevMax), 0];
_ctrlElevationHigh ctrlSetTextColor ([[0.25,0.25,0.25,1],[1,1,1,1]] select GVAR(lastElevationMode));
_ctrlElevationLow ctrlSetTextColor ([[1,1,1,1],[0.25,0.25,0.25,1]] select GVAR(lastElevationMode));
lnbClear _ctrlRangeTable;
// Call extension with current data and start workers
TRACE_5("callExtension:start",_muzzleVelocity,_airFriction,_elevMin,_elevMax,GVAR(lastElevationMode));
private _ret = "ace_artillerytables" callExtension ["start", [_muzzleVelocity,_airFriction,_elevMin,_elevMax,GVAR(lastElevationMode)]];
TRACE_1("",_ret);
// Non-blocking read data out of extension as it becomes availiable
[{
private _dialog = uiNamespace getVariable [QGVAR(rangeTableDialog), displayNull];
private _ctrlRangeTable = _dialog displayCtrl IDC_TABLE;
if (isNull _dialog) exitWith {true};
private _status = 1; // 1 = data on line, 2 - data not ready, 3 - done
while {_status == 1} do {
private _ret = ("ace_artillerytables" callExtension ["getline", []]);
// TRACE_1("callExtension:getline",_ret);
_status = _ret select 1;
if (_status == 1) then { _ctrlRangeTable lnbAddRow parseSimpleArray (_ret select 0) };
};
(_status == 3) // exit loop when all data read
}, {
// put dummy line at end because scrolling is problematic and can't see last line
private _dialog = uiNamespace getVariable [QGVAR(rangeTableDialog), displayNull];
private _ctrlRangeTable = _dialog displayCtrl IDC_TABLE;
if (isNull _dialog) exitWith {TRACE_1("dialog closed",_this);};
_ctrlRangeTable lnbAddRow ["", "", "", "", "", "", "", "", "", "", ""];
TRACE_1("table filled",_ctrlRangeTable);
}, []] call CBA_fnc_waitUntilAndExecute;

View File

@ -0,0 +1,70 @@
#include "script_component.hpp"
/*
* Author: PabstMirror
* Turret changed, determine if we are in the gunner seat of an artillery vehicle.
*
* Arguments:
* 0: Player <OBJECT>
* 1: Turret <ARRAY>
*
* Return Value:
* Nothing
*
* Example:
* [player, [0]] call ace_artillerytables_fnc_turretChanged
*
* Public: No
*/
params ["_player", "_turret"];
private _vehicle = vehicle _player;
private _typeOf = typeOf _vehicle;
private _vehicleCfg = configFile >> "CfgVehicles" >> _typeOf;
// config "ace_artillerytables_showGunLaying" [0 disabled, 1 enabled, 2 enabled w/ alt elevationMode] falls back to artilleryScanner
private _showGunLaying = if (isNumber (_vehicleCfg >> QGVAR(showGunLaying))) then {
getNumber (_vehicleCfg >> QGVAR(showGunLaying))
} else {
getNumber (_vehicleCfg >> "artilleryScanner")
};
TRACE_4("turretChanged",_player,_typeOf,_turret,_showGunLaying);
if (GVAR(pfID) >= 0) then {
TRACE_1("removing pfEH and display",GVAR(pfID));
[GVAR(pfID)] call CBA_fnc_removePerFrameHandler;
GVAR(pfID) = -1;
if (!isNull (uiNamespace getVariable [QGVAR(display), displayNull])) then {
([QGVAR(modeDisplay)] call BIS_fnc_rscLayer) cutText ["", "PLAIN"];
};
};
if ((alive _player) && {_showGunLaying > 0} && {_player == gunner _vehicle}) then {
private _turretCfg = [_typeOf, _turret] call CBA_fnc_getTurret;
private _turretAnimBody = getText (_turretCfg >> "animationSourceBody");
private _useAltElevation = (_showGunLaying == 2); // StaticMortars need elevation calculated differently, see FUNC(turretPFEH)
// If the memory point is invalid, then the turret will always use real weapon dir (e.g. CUP BM21)
private _memoryPointGunnerOptics = getText (_turretCfg >> "memoryPointGunnerOptics");
private _invalidGunnerMem = (_vehicle selectionPosition [_memoryPointGunnerOptics, "Memory"]) isEqualTo [0,0,0];
if (_invalidGunnerMem) then { INFO_3("[%1-%2] turret's memoryPointGunnerOptics invalid [%3]",typeOf _vehicle,_turret,_memoryPointGunnerOptics); };
private _weaponsTurret = _vehicle weaponsTurret _turret;
if ((count _weaponsTurret) != 1) then { WARNING_2("not singular weapon in turret - %1 - %2",_typeOf,_turret); };
private _weapon = _weaponsTurret param [0, ""];
private _fireModes = getArray (configFile >> "CfgWeapons" >> _weapon >> "modes");
_fireModes = (_fireModes apply {configFile >> "CfgWeapons" >> _weapon >> _x}) select {1 == getNumber (_x >> "showToPlayer")};
_fireModes = _fireModes apply {[getNumber (_x >> "artilleryCharge"), configName _x]};
_fireModes sort true;
_fireModes = _fireModes apply {_x select 1};
GVAR(pfID) = [LINKFUNC(turretPFEH), 0, [_vehicle, _turret, _fireModes, _useAltElevation, _turretAnimBody, _invalidGunnerMem]] call CBA_fnc_addPerFrameHandler;
TRACE_4("added pfEH",GVAR(pfID),_useAltElevation,_turretAnimBody,_invalidGunnerMem);
#ifdef DEBUG_MODE_FULL
private _ballisticsComputer = getNumber (configFile >> "CfgWeapons" >> _weapon >> "ballisticsComputer");
private _elevationMode = getNumber (_turretCfg >> "elevationMode");
private _discreteDistance = getArray (_turretCfg >> "discreteDistance");
TRACE_4("",_weapon,_ballisticsComputer,_elevationMode,_discreteDistance);
#endif
};

View File

@ -0,0 +1,92 @@
#include "script_component.hpp"
/*
* Author: PabstMirror
* Shows real azimuth and elevation on hud
*
* Arguments:
* 0: PFEH Args <ARRAY>
*
* Return Value:
* Nothing
*
* Example:
* [[...]] call ace_artillerytables_fnc_turretPFEH
*
* Public: No
*/
(_this select 0) params ["_vehicle", "_turret", "_fireModes", "_useAltElevation", "_turretAnimBody", "_invalidGunnerMem"];
if (shownArtilleryComputer && {GVAR(disableArtilleryComputer)}) then {
// Still Don't like this solution, but it works
closeDialog 0;
[localize LSTRING(disableArtilleryComputer_displayName)] call EFUNC(common,displayTextStructured);
};
// Restart display if null (not just at start, this will happen periodicly)
if (isNull (uiNamespace getVariable [QGVAR(display), displayNull])) then {
TRACE_1("creating display",_this);
([QGVAR(modeDisplay)] call BIS_fnc_rscLayer) cutRsc [QGVAR(modeDisplay), "PLAIN", 1, false];
};
private _ctrlGroup = (uiNamespace getVariable [QGVAR(display), displayNull]) displayCtrl 1000;
if (cameraView != "GUNNER") exitWith { // need to be in gunner mode, so we can check where the optics are aiming at
_ctrlGroup ctrlShow false;
};
_ctrlGroup ctrlShow true;
BEGIN_COUNTER(pfeh);
private _currentFireMode = (weaponState [_vehicle, _turret]) select 2;
private _currentChargeMode = _fireModes find _currentFireMode;
private _lookVector = (AGLtoASL (positionCameraToWorld [0,0,0])) vectorFromTo (AGLtoASL (positionCameraToWorld [0,0,1]));
private _weaponDir = _vehicle weaponDirection (currentWeapon _vehicle);
// Calc real azimuth/elevation
// (looking at the sky VS looking at ground will radicaly change fire direction because BIS)
private _display = uiNamespace getVariable ["ACE_dlgArtillery", displayNull];
private _useRealWeaponDir = if ((isNull (_display displayCtrl 173)) || {(_vehicle ammo (currentWeapon _vehicle)) == 0}) then {
// With no ammo, distance display will be empty, but gun will still fire at wonky angle if aimed at ground
private _testSeekerPosASL = AGLtoASL (positionCameraToWorld [0,0,0]);
private _testPoint = _testSeekerPosASL vectorAdd (_lookVector vectorMultiply viewDistance);
!((terrainIntersectASL [_testSeekerPosASL, _testPoint]) || {lineIntersects [_testSeekerPosASL, _testPoint, _vehicle]});
} else {
((ctrlText (_display displayCtrl 173)) == "--")
};
private _realElevation = asin (_weaponDir select 2);
private _realAzimuth = 0;
if (_useRealWeaponDir || _invalidGunnerMem) then {
// No range (looking at sky), it will follow weaponDir:
_realAzimuth = (_weaponDir select 0) atan2 (_weaponDir select 1)
} else {
// Valid range, will fire at camera dir
// Azimuth will still be look dir EVEN IF elevation has flipped over 90! (on steep hills)
_realAzimuth = ((_lookVector select 0) atan2 (_lookVector select 1));
if (_useAltElevation) then {
// Some small mortars have odd launch angles (I think due to the addition of neutral elevation constant with the manual elevation)
// This gets very close, (should be less than 0.5deg deviation on a 20deg slope)
private _currentTraverseRad = _vehicle animationSourcePhase _turretAnimBody;
if (isNil "_currentTraverseRad") then { _currentTraverseRad = _vehicle animationPhase _turretAnimBody; };
// Get turret roatation around it's z axis, then calc weapon elev in it's projection
private _turretRot = [vectorDir _vehicle, vectorUp _vehicle, deg _currentTraverseRad] call CBA_fnc_vectRotate3D;
_realElevation = (acos ((_turretRot vectorCos _weaponDir) min 1)) + ((_turretRot call CBA_fnc_vect2polar) select 2);
if (_realElevation > 90) then { _realElevation = 180 - _realElevation; }; // does not flip azimuth!
};
};
if (_realAzimuth < 0) then { _realAzimuth = _realAzimuth + 360; }; // mils will be 0-6400
private _ctrlCharge = (uiNamespace getVariable [QGVAR(display), displayNull]) displayCtrl IDC_CHARGE;
private _ctrlAzimuth = (uiNamespace getVariable [QGVAR(display), displayNull]) displayCtrl IDC_AZIMUTH;
private _ctrlElevation = (uiNamespace getVariable [QGVAR(display), displayNull]) displayCtrl IDC_ELEVATION;
_ctrlAzimuth ctrlSetText Format ["AZ: %1", [DEGTOMILS * _realAzimuth, 4, 0] call CBA_fnc_formatNumber];
_ctrlElevation ctrlSetText Format ["EL: %1", [DEGTOMILS * _realElevation, 4, 0] call CBA_fnc_formatNumber];
_ctrlCharge ctrlSetText format ["CH: %1", _currentChargeMode];
// avalible for other addons (mk6)
GVAR(predictedAzimuth) = _realAzimuth;
GVAR(predictedElevation) = _realElevation;
END_COUNTER(pfeh);

View File

@ -0,0 +1 @@
#include "\z\ace\addons\artillerytables\script_component.hpp"

View File

@ -0,0 +1,23 @@
// CBA Settings [ADDON: ace_artillerytables]:
private _categoryName = format ["ACE %1", localize "str_a3_cfgmarkers_nato_art"];
[
QGVAR(advancedCorrections), "CHECKBOX",
[LSTRING(advancedCorrections_displayName), LSTRING(advancedCorrections_description)],
[_categoryName, QUOTE(COMPONENT_BEAUTIFIED)],
false, // default value
true, // isGlobal
{[QGVAR(advancedCorrections), _this] call EFUNC(common,cbaSettings_settingChanged)},
true // Needs mission restart
] call CBA_settings_fnc_init;
[
QGVAR(disableArtilleryComputer), "CHECKBOX",
[LSTRING(disableArtilleryComputer_displayName), LSTRING(disableArtilleryComputer_description)],
[_categoryName, QUOTE(COMPONENT_BEAUTIFIED)],
false, // default value
true, // isGlobal
{[QGVAR(disableArtilleryComputer), _this] call EFUNC(common,cbaSettings_settingChanged)},
false // Needs mission restart
] call CBA_settings_fnc_init;

View File

@ -0,0 +1,25 @@
#define COMPONENT artillerytables
#define COMPONENT_BEAUTIFIED ArtilleryTables
#include "\z\ace\addons\main\script_mod.hpp"
// #define DEBUG_MODE_FULL
// #define DISABLE_COMPILE_CACHE
// #define ENABLE_PERFORMANCE_COUNTERS
#include "\z\ace\addons\main\script_macros.hpp"
// This is a good fit for most large artillery, but a little low for lighter mortars
#define DEFAULT_AIR_FRICTION -0.00006
#define DEGTOMILS 17.7777778
#define IDC_MODECONTROLGROUP 1000
#define IDC_CHARGE 1001
#define IDC_AZIMUTH 1002
#define IDC_ELEVATION 1003
#define IDC_TABLE 2001
#define IDC_CHARGELIST 2002
#define IDC_BUTTON_ELEV_HIGH 2003
#define IDC_BUTTON_ELEV_LOW 2004

View File

@ -0,0 +1,49 @@
<?xml version="1.0" encoding="utf-8"?>
<Project name="ACE">
<Package name="ArtilleryTables">
<Key ID="STR_ACE_ArtilleryTables_rangetable_displayName">
<English>Artillery Rangetable</English>
</Key>
<Key ID="STR_ACE_ArtilleryTables_rangetable_description">
<English>Universal Artillery Rangetable</English>
</Key>
<Key ID="STR_ACE_ArtilleryTables_advancedCorrections_displayName">
<English>Air Resistance</English>
<Polish>Opór powietrza</Polish>
<Spanish>Resistencia al aire</Spanish>
<German>Luftwiderstand</German>
<Czech>Odpor vzduchu</Czech>
<Portuguese>Resistência do Ar</Portuguese>
<French>Résistance de l'air</French>
<Hungarian>Légellenállás</Hungarian>
<Russian>Сопротивление воздуха</Russian>
<Italian>Resistenza dell'Aria</Italian>
<Japanese>空気抵抗</Japanese>
<Korean>공기저항</Korean>
<Chinesesimp>空气阻力</Chinesesimp>
<Chinese>空氣阻力</Chinese>
</Key>
<Key ID="STR_ACE_ArtilleryTables_advancedCorrections_description">
<English>For Player Shots, Model Air Resistance and Wind Effects</English>
<Polish>Modeluj opór powietrza oraz wpływ wiatru na tor lotu pocisku dla strzałów z moździerza Mk6 przez graczy</Polish>
<Spanish>Para disparos del jugador, modelo de resistencia al aire y efectos de viento</Spanish>
<German>Für Spielerschüsse, Luftwiderstand und Windeffekte</German>
<Czech>Pro hráčovu střelbu, Model odporu vzduchu a povětrných podmínek</Czech>
<Portuguese>Para disparos do jogador, modelo de resistência de ar e efeitos de vento</Portuguese>
<French>Pour les tirs de joueurs, modèle de résistance à l'air et d'effet du vent</French>
<Hungarian>Játékos általi lövésekhez, legyen-e számított légellenállás és szélhatás</Hungarian>
<Russian>Для выстрелов игрока. Моделирует сопротивление воздуха и эффект ветра</Russian>
<Italian>Per Proiettili dei Giocatori, simula la Resistenza dell'Aria e gli Effetti del Vento</Italian>
<Japanese>プレイヤが射撃すると、空気抵抗モデルと風による影響をあたえます。</Japanese>
<Korean>플레이어 사격시 공기저항과 바람에 영향을 받습니다</Korean>
<Chinesesimp>设定由玩家射击的迫击炮,将会受到空气阻力与风力的影响</Chinesesimp>
<Chinese>設定由玩家射擊的迫擊砲,將會受到空氣阻力與風力的影響</Chinese>
</Key>
<Key ID="STR_ACE_ArtilleryTables_disableArtilleryComputer_displayName">
<English>Artillery Computer Disabled</English>
</Key>
<Key ID="STR_ACE_ArtilleryTables_disableArtilleryComputer_description">
<English>Disable the vanilla artillery computers</English>
</Key>
</Package>
</Project>

View File

@ -105,6 +105,9 @@ if (!isServer && {_platform in ["linux", "osx"]}) then {
}; };
} forEach ("true" configClasses (configFile >> "ACE_Extensions")); } forEach ("true" configClasses (configFile >> "ACE_Extensions"));
}; };
if (isArray (configFile >> "ACE_Extensions" >> "extensions")) then {
WARNING("extensions[] array no longer supported");
};
/////////////// ///////////////
// check server version/addons // check server version/addons

View File

@ -1,35 +1,14 @@
class ACE_Settings { class ACE_Settings {
//These settings effect gameplay difficutly: defaults will leave the mortar the same as vanilla
class GVAR(airResistanceEnabled) { class GVAR(airResistanceEnabled) {
category = CSTRING(DisplayName); movedToSQF = 1;
displayName = CSTRING(airResistanceEnabled_DisplayName);
description = CSTRING(airResistanceEnabled_Description);
value = 0;
typeName = "BOOL";
isClientSetable = 0;
}; };
class GVAR(allowComputerRangefinder) { class GVAR(allowComputerRangefinder) {
category = CSTRING(DisplayName); movedToSQF = 1;
displayName = CSTRING(allowComputerRangefinder_DisplayName);
description = CSTRING(allowComputerRangefinder_Description);
value = 1;
typeName = "BOOL";
isClientSetable = 0;
}; };
class GVAR(allowCompass) { class GVAR(allowCompass) {
category = CSTRING(DisplayName); movedToSQF = 1;
displayName = CSTRING(allowCompass_DisplayName);
description = CSTRING(allowCompass_Description);
value = 1;
typeName = "BOOL";
isClientSetable = 0;
}; };
class GVAR(useAmmoHandling) { class GVAR(useAmmoHandling) {
category = CSTRING(DisplayName); movedToSQF = 1;
displayName = CSTRING(useAmmoHandling_DisplayName);
description = CSTRING(useAmmoHandling_Description);
value = 0;
typeName = "BOOL";
isClientSetable = 0;
}; };
}; };

View File

@ -16,11 +16,3 @@ class Extended_PostInit_EventHandlers {
init = QUOTE(call COMPILE_FILE(XEH_postInit)); init = QUOTE(call COMPILE_FILE(XEH_postInit));
}; };
}; };
class Extended_FiredBIS_EventHandlers {
class Mortar_01_base_F {
class ADDON {
firedBIS = QUOTE(_this call FUNC(handleFired));
};
};
};

View File

@ -4,16 +4,8 @@ class RscInGameUI {
}; };
class ACE_Mk6_RscWeaponRangeArtillery: RscWeaponRangeArtillery { class ACE_Mk6_RscWeaponRangeArtillery: RscWeaponRangeArtillery {
onLoad = QUOTE(uiNamespace setVariable [ARR_2('ACE_Mk6_RscWeaponRangeArtillery', _this select 0)]; [ARR_2('ace_infoDisplayChanged', [ARR_2(_this select 0, 'Mk6Mortar')])] call CBA_fnc_localEvent;); onLoad = QUOTE(uiNamespace setVariable [ARR_2('ACE_Mk6_RscWeaponRangeArtillery', _this select 0)]; [ARR_2('ace_infoDisplayChanged', [ARR_2(_this select 0, 'Mk6Mortar')])] call CBA_fnc_localEvent;);
controls[] = {"ACE_ChargeDisplay", "ACE_MILS_GROUP", "CA_IGUI_elements_group","CA_RangeElements_group"}; controls[] = {"ACE_MILS_GROUP", "CA_IGUI_elements_group","CA_RangeElements_group"};
class ACE_ChargeDisplay: RscStructuredText {
idc = 80085;
colorText[] = {1, 1, 1, 1};
colorBackground[] = {0, 0, 0, 0.1};
x = "3.8 * (((safezoneW / safezoneH) min 1.2) / 40) + (profilenamespace getvariable [""IGUI_GRID_WEAPON_X"",((safezoneX + safezoneW) - (10 * (((safezoneW / safezoneH) min 1.2) / 40)) - 4.3 * (((safezoneW / safezoneH) min 1.2) / 40))])";
y = "2.5 * ((((safezoneW / safezoneH) min 1.2) / 1.2) / 25) + (profilenamespace getVariable ['IGUI_GRID_WEAPON_Y', (safezoneY + 0.5 * ((((safezoneW / safezoneH) min 1.2) / 1.2) / 25))])";
w = "10 * (((safezoneW / safezoneH) min 1.2) / 40)";
h = "1 * ((((safezoneW / safezoneH) min 1.2) / 1.2) / 25)";
};
class ACE_MILS_GROUP: CA_IGUI_elements_group { class ACE_MILS_GROUP: CA_IGUI_elements_group {
idc = 80170; idc = 80170;
class controls { class controls {

View File

@ -1,18 +1,9 @@
PREP(dev_buildTable);
PREP(dev_formatNumber);
PREP(dev_simulateCalcRangeTableLine);
PREP(dev_simulateFindSolution);
PREP(dev_simulateShot);
PREP(csw_getProxyWeapon); PREP(csw_getProxyWeapon);
PREP(handleFired); PREP(handleFired);
PREP(handlePlayerVehicleChanged); PREP(handlePlayerVehicleChanged);
PREP(moduleInit); PREP(moduleInit);
PREP(rangeTableCanUse); PREP(rangeTableCanUse);
PREP(rangeTableOpen); PREP(rangeTableOpen);
PREP(rangeTablePageChange);
PREP(rangeTablePreCalculatedValues);
PREP(toggleMils); PREP(toggleMils);
PREP(turretDisplayLoaded); PREP(turretDisplayLoaded);

View File

@ -5,7 +5,12 @@ if (hasInterface) then {
}; };
["ace_settingsInitialized", { ["ace_settingsInitialized", {
TRACE_1("ace_settingsInitialized",GVAR(useAmmoHandling)); TRACE_4("ace_settingsInitialized",GVAR(airResistanceEnabled),GVAR(allowComputerRangefinder),GVAR(allowCompass),GVAR(useAmmoHandling));
["vehicle", FUNC(handlePlayerVehicleChanged), true] call CBA_fnc_addPlayerEventHandler; ["vehicle", FUNC(handlePlayerVehicleChanged), true] call CBA_fnc_addPlayerEventHandler;
if (!GVAR(airResistanceEnabled)) exitWith {};
if (EGVAR(artillerytables,advancedCorrections)) exitWith { TRACE_1("defer firedEH to artillerytables",_this); };
["Mortar_01_base_F", "fired", {call FUNC(handleFired)}] call CBA_fnc_addClassEventHandler;
}] call CBA_fnc_addEventHandler; }] call CBA_fnc_addEventHandler;

View File

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

View File

@ -7,7 +7,7 @@ class CfgPatches {
"ACE_Box_82mm_Mo_Illum","ACE_Box_82mm_Mo_Combo"}; "ACE_Box_82mm_Mo_Illum","ACE_Box_82mm_Mo_Combo"};
weapons[] = {"ACE_RangeTable_82mm","ace_mortar_82mm"}; weapons[] = {"ACE_RangeTable_82mm","ace_mortar_82mm"};
requiredVersion = REQUIRED_VERSION; requiredVersion = REQUIRED_VERSION;
requiredAddons[] = {"ace_csw"}; requiredAddons[] = {"ace_csw", "ace_artillerytables"};
author = ECSTRING(common,ACETeam); author = ECSTRING(common,ACETeam);
authors[] = {"PabstMirror","Grey","VKing"}; authors[] = {"PabstMirror","Grey","VKing"};
url = ECSTRING(main,URL); url = ECSTRING(main,URL);
@ -33,4 +33,3 @@ class RscActiveText;
class RscStructuredText; class RscStructuredText;
#include "RscInGameUI.hpp" #include "RscInGameUI.hpp"
#include "RscRangeTable.hpp"

View File

@ -61,7 +61,7 @@ if (_proxyWeaponNeeded || GVAR(useAmmoHandling)) then {
} forEach (magazinesAllTurrets _mortar); } forEach (magazinesAllTurrets _mortar);
// remove orignal mags and add 1rnd versions: // remove orignal mags and add 1rnd versions:
{ _staticWeapon removeMagazinesTurret _x; } forEach _magsToRemove; { _mortar removeMagazinesTurret _x; } forEach _magsToRemove;
{ _mortar addMagazineTurret _x; } forEach _convertedMags; { _mortar addMagazineTurret _x; } forEach _convertedMags;
}; };

View File

@ -1,84 +0,0 @@
#include "script_component.hpp"
/*
* Author: PabstMirror
* DEV function to build mortar tables, very cpu intensive (never used durring normal gameplay)
*
* Arguments:
* 0: Muzzle Velocity <NUMBER>
* 1: Air Friction <NUMBER>
*
* Return Value:
* None <Data in clipboard>
*
* Example:
* [100, -0.0001] spawn ace_mk6mortar_fnc_dev_buildTable; //spawn (scheduled) is slower
* [100, -0.0001] call ace_mk6mortar_fnc_dev_buildTable; //faster, but will lock while processing
*
* Public: No
*/
private _muzzleVelocity = _this select 0;
private _airFriction = _this select 1;
private _stillInRange = true;
private _currentRange = 100;
private _increasePerRow = 50;
private _outputArray = [];
//[_rangeToHit, _lineElevation, _lineHeightElevation, _lineHeightTimeDelta, _lineTimeOfFlight, _lineCrosswindDeg, _lineHeadwindMeters, _lineTailWindMeters, _lineTempDec, _lineTempInc, _lineAirDensDec, _lineAirDensInc]
while {_stillInRange} do {
private _result = [_muzzleVelocity, _currentRange, _airFriction] call FUNC(dev_simulateCalcRangeTableLine);
if (_result isEqualTo []) then {
_stillInRange = false;
} else {
if (_airFriction == 0) then {
_result set [5, 0];
_result set [6, 0];
_result set [7, 0];
_result set [8, 0];
_result set [9, 0];
_result set [10, 0];
_result set [11, 0];
};
if ((_result select 1) < 88) then {
_outputArray pushBack [
([(_result select 0), "meters", false] call FUNC(dev_formatNumber)),
([(_result select 1), "mil", true] call FUNC(dev_formatNumber)),
([(_result select 2), "mil", true] call FUNC(dev_formatNumber)),
([(_result select 3), "sec", false] call FUNC(dev_formatNumber)),
([(_result select 4), "sec", false] call FUNC(dev_formatNumber)),
([(_result select 5), "milPrecise", true] call FUNC(dev_formatNumber)),
([(_result select 6), "metersprecise", false] call FUNC(dev_formatNumber)),
([(_result select 7), "metersprecise", false] call FUNC(dev_formatNumber)),
([(_result select 8), "metersprecise", false] call FUNC(dev_formatNumber)),
([(_result select 9), "metersprecise", false] call FUNC(dev_formatNumber)),
([(_result select 10), "metersprecise", false] call FUNC(dev_formatNumber)),
([(_result select 11), "metersprecise", false] call FUNC(dev_formatNumber))
];
};
_currentRange = _currentRange + _increasePerRow;
};
hintSilent str _currentRange;
};
//handle floating point rounding errors
private _outputString = format ["case ((abs(_muzzleVelocity - %1) < 0.00001) && {(abs(_airFriction - %2) < 0.00001)}): {
[
", _muzzleVelocity, _airFriction];
{
if (_forEachIndex < ((count _outputArray) - 1)) then {
_outputString = _outputString + format ["%1,
", _x];
} else {
_outputString = _outputString + format ["%1
]
};", _x];
};
} forEach _outputArray;
copyToClipboard _outputString;
rangeTableOutput = _outputString;
hint "done";

View File

@ -1,76 +0,0 @@
#include "script_component.hpp"
/*
* Author: Pabst Mirror
* Converts numbers into nicely formated strings.
*
* Arguments:
* 0: Input number <NUMBER>
* 1: Output type (see case statement) <STRING>
* 2: If output type is mil, convert input type from deg->mil <BOOL>
*
* Return Value:
* Formatted number <STRING>
*
* Example:
* [45, "mil4", true] call ace_mk6mortar_fnc_dev_formatNumber = "0800"
*
* Public: No
*/
params ["_theNumber", "_inputType", "_convertToMils"];
private _decimalPlaces = -1;
private _integerPlaces = -1;
switch (toLower _inputType) do {
case ("meters"): {
_decimalPlaces = 0;
_integerPlaces = 1;
};
case ("metersprecise"): {
_decimalPlaces = 1;
_integerPlaces = 1;
};
case ("meters4"): {
_decimalPlaces = 0;
_integerPlaces = 4;
};
case ("deg3precise"): {
_decimalPlaces = 2;
_integerPlaces = 3;
};
case ("mil"): {
_decimalPlaces = 0;
_integerPlaces = 1;
if (_convertToMils) then {
_theNumber = _theNumber * (6400 / 360);
};
};
case ("mil4"): {
_decimalPlaces = 0;
_integerPlaces = 4;
if (_convertToMils) then {
_theNumber = _theNumber * (6400 / 360);
};
};
case ("milprecise"): {
_decimalPlaces = 1;
_integerPlaces = 1;
if (_convertToMils) then {
_theNumber = _theNumber * (6400 / 360);
};
};
case ("sec"): {
_decimalPlaces = 1;
_integerPlaces = 1;
};
default {systemChat format ["badtype %1", _inputType];};
};
//CBA_fnc_formatNumber is silly: [-9.58545, 1, 1, false] call CBA_fnc_formatNumber == "-9.-6"
private _prefix = if (_theNumber < 0) then {"-"} else {""};
private _return = [abs (_theNumber), _integerPlaces, _decimalPlaces, false] call CBA_fnc_formatNumber;
(_prefix + _return)

View File

@ -1,77 +0,0 @@
#include "script_component.hpp"
/*
* Author: Pabst Mirror
* Builds a rangeTable line for a certian range, given muzzle velocity and air friction, returns [] if out of range.
*
* Arguments:
* 0: Muzzle Velocity <NUMBER>
* 1: Air Friction <NUMBER>
* 2: Range To Hit <NUMBER>
*
* Return Value:
* Range Table Line Data (see return line) <ARRAY>
*
* Example:
* [300, -0.0001, 3000] call ace_mk6mortar_fnc_simulateCalcRangeTableLine
*
* Public: No
*/
#define TIME_STEP (1/50)
params ["_muzzleVelocity", "_rangeToHit", "_airFriction"];
private _startTime = diag_tickTime;
//Run Binary search for correct elevation
private _solution = [_rangeToHit, 0, _muzzleVelocity, _airFriction, TIME_STEP] call FUNC(dev_simulateFindSolution);
if (_solution isEqualTo []) exitWith {[]};
//Real Elevation
private _lineElevation = _solution select 0;
//Time Of Flight:
private _lineTimeOfFlight = _solution select 1;
//Height Adjustment for -100m (another binary search)
private _solution = [_rangeToHit, -100, _muzzleVelocity, _airFriction, TIME_STEP] call FUNC(dev_simulateFindSolution);
if (_solution isEqualTo []) exitWith {[]};//should never be triggered (lower elevation easier to hit)
private _lineHeightElevation = ((_solution select 0) - _lineElevation);
private _lineHeightTimeDelta = (_solution select 1) - _lineTimeOfFlight;
//Compute for 10x and divide to minimize rounding errors
//Crosswind
private _lastTestResult = [_lineElevation, _muzzleVelocity, _airFriction, 15, 1, 0, 10, 0, TIME_STEP] call FUNC(dev_simulateShot);
private _lineCrosswindDeg = (_lastTestResult select 2) / 10;
//Headwind:
_lastTestResult = [_lineElevation, _muzzleVelocity, _airFriction, 15, 1, -10, 0, 0, TIME_STEP] call FUNC(dev_simulateShot);
private _lineHeadwindMeters = (_rangeToHit - (_lastTestResult select 0)) / 10;
//TailWind:
_lastTestResult = [_lineElevation, _muzzleVelocity, _airFriction, 15, 1, 10, 0, 0, TIME_STEP] call FUNC(dev_simulateShot);
private _lineTailWindMeters = (_rangeToHit - (_lastTestResult select 0)) / 10;
//Air Temp Dec
_lastTestResult = [_lineElevation, _muzzleVelocity, _airFriction, (15 - 10), 1, 0, 0, 0, TIME_STEP] call FUNC(dev_simulateShot);
_lineTempDec = (_rangeToHit - (_lastTestResult select 0)) / 10;
//Air Temp Inc
_lastTestResult = [_lineElevation, _muzzleVelocity, _airFriction, (15 + 10), 1, 0, 0, 0, TIME_STEP] call FUNC(dev_simulateShot);
_lineTempInc = (_rangeToHit - (_lastTestResult select 0)) / 10;
//Air Density Dec
_lastTestResult = [_lineElevation, _muzzleVelocity, _airFriction, 15, 0.9, 0, 0, 0, TIME_STEP] call FUNC(dev_simulateShot);
private _lineAirDensDec = (_rangeToHit - (_lastTestResult select 0)) / 10;
//Air Density Inc
_lastTestResult = [_lineElevation, _muzzleVelocity, _airFriction, 15, 1.1, 0, 0, 0, TIME_STEP] call FUNC(dev_simulateShot);
private _lineAirDensInc = (_rangeToHit - (_lastTestResult select 0)) / 10;
// systemChat format ["debug: Range %1 - in %2 sec", _rangeToHit, (diag_tickTime - _startTime)];
[_rangeToHit, _lineElevation, _lineHeightElevation, _lineHeightTimeDelta, _lineTimeOfFlight, _lineCrosswindDeg, _lineHeadwindMeters, _lineTailWindMeters, _lineTempDec, _lineTempInc, _lineAirDensDec, _lineAirDensInc]

View File

@ -1,48 +0,0 @@
#include "script_component.hpp"
/*
* Author: PabstMirror
* DEV to find a firing solution for a given range
*
* Arguments:
* 0: Range to Hit (Meters) <NUMBER>
* 1: Height To Hit (Meters) <NUMBER>
* 2: Muzzle Velocity (M/S) <NUMBER>
* 3: Air Friction <NUMBER>
* 4: Time Step (seconds) (eg 1/50 will simulate 50 cycles per second) <NUMBER>
*
* Return Value:
* [NUMBER - Elevation In Degrees, NUMBER - Shot Durration] <ARRAY>
*
* Example:
* [_rangeToHit, _heightToHit, _muzzleVelocity, _airFriction, TIME_STEP] call ace_mk6mortar_fnc_dev_simulateFindSolution;
*
* Public: No
*/
#define MAX_ATTEMPTS 22
params ["_rangeToHit", "_heightToHit", "_muzzleVelocity", "_airFriction","_timeStep"];
private _maxElev = 90;
private _minElev = 45; //todo - Low Angle Howitzers???
private _error = 10000;
private _solutionElevation = -1;
private _lastTestResult = [];
private _numberOfAttempts = 0;
//(binary search)
while {(_numberOfAttempts < MAX_ATTEMPTS) && {(abs _error) > 0.2}} do {
_numberOfAttempts = _numberOfAttempts + 1;
_solutionElevation = (_maxElev + _minElev) / 2;
_lastTestResult = [_solutionElevation, _muzzleVelocity, _airFriction, 15, 1, 0, 0, _heightToHit, _timeStep] call FUNC(dev_simulateShot);
_error = _rangeToHit - (_lastTestResult select 0);
if (_error > 0) then {
_maxElev = _solutionElevation; //test range was short
} else {
_minElev = _solutionElevation; //test range was long
};
};
if (_numberOfAttempts >= MAX_ATTEMPTS) exitWith {[]};
//return the elevation and time required
[_solutionElevation, (_lastTestResult select 1)]

View File

@ -1,63 +0,0 @@
#include "script_component.hpp"
/*
* Author: PabstMirror
* DEV function to build mortar tables, very cpu intensive (never used durring normal gameplay)
*
* Arguments:
* 0: Shot Angle (degrees) <NUMBER>
* 1: Muzzle Velocity (m/s) <NUMBER>
* 2: Air Friction <NUMBER>
* 3: Tempeture (degres celcius) <NUMBER>
* 4: Relative Air Denisty <NUMBER>
* 5: Tail Wind (m/s) <NUMBER>
* 6: Cross Wind (m/s) <NUMBER>
* 7: Height Of Target (M) <NUMBER>
* 8: Time Step (fraction of a second) <NUMBER>
*
* Return Value:
* <ARRAY> [Distance Traveled<NUMBER>, Shot Time<NUMBER>, Offset (degrees)<NUMBER>]
*
* Example:
* [45, 180, -0.0001, 15, 1, 10, 0, 0, 1/50] call ace_mk6mortar_fnc_dev_simulateShot;
*
* Public: No
*/
params ["_angleDeg", "_muzzleVelocity", "_airFriction", "_temp", "_relDensity", "_tailWind", "_crosswind", "_heightOfTarget", "_timeStep"];
private _wind = [_crosswind, _tailWind, 0];
private _gravity = [0,0,-9.8];
private _currentPos = [0,0,0];
private _muzzleVelocity = _muzzleVelocity * (((_temp + 273.13) / 288.13 - 1) / 40 + 1);
private _currentVelocity = [0, (_muzzleVelocity * cos _angleDeg), (_muzzleVelocity * sin _angleDeg)];
private _currentTime = 0;
private _lastPos = _currentPos;
private _kCoefficent = -1 * _relDensity * _airFriction; //save time in the loop and compute once
while {((_currentVelocity select 2) > 0) || ((_currentPos select 2) >= _heightOfTarget)} do {
_lastPos = _currentPos;
private _aparentWind = _wind vectorDiff _currentVelocity;
private _changeInVelocity = _gravity vectorAdd (_aparentWind vectorMultiply ((vectorMagnitude _aparentWind) * _kCoefficent));
_currentVelocity = _currentVelocity vectorAdd (_changeInVelocity vectorMultiply _timeStep);
_currentPos = _currentPos vectorAdd (_currentVelocity vectorMultiply _timeStep);
_currentTime = _currentTime + _timeStep;
};
//Uses linearConversion to get a weighted average betwen points before and after dropping below target height
private _linConversion = linearConversion [(_lastPos select 2), (_currentPos select 2), _heightOfTarget, 0, 1, true];
private _middlePos = (_lastPos vectorMultiply (1 - _linConversion)) vectorAdd (_currentPos vectorMultiply (_linConversion));
// private _middlePosOld = (_lastPos vectorAdd _currentPos) vectorMultiply 0.5;
//Same to find travel time
private _middleTotalTravelTime = _currentTime - (_timeStep * (1-_linConversion));
//Find shot offset (from crosswind), in degrees
private _offsetDeg = (_middlePos select 0) aTan2 (_middlePos select 1);
[(_middlePos select 1), _middleTotalTravelTime, _offsetDeg]

View File

@ -21,15 +21,13 @@
* Public: No * Public: No
*/ */
params ["_vehicle", "_weapon", "_muzzle", "_mode", "_ammo", "_magazine", "_projectile"]; params ["_vehicle", "", "", "", "", "", "_projectile"];
if (!GVAR(airResistanceEnabled)) exitWith {};
// Large enough distance to not simulate any wind deflection // Large enough distance to not simulate any wind deflection
if (_vehicle distance ACE_player > 8000) exitWith {false}; if (_vehicle distance ACE_player > 8000) exitWith {false};
//AI will have no clue how to use: //AI will have no clue how to use:
_shooterMan = gunner _vehicle; private _shooterMan = gunner _vehicle;
if (!([_shooterMan] call EFUNC(common,isPlayer))) exitWith {false}; if (!([_shooterMan] call EFUNC(common,isPlayer))) exitWith {false};
//Calculate air density: //Calculate air density:
@ -45,8 +43,8 @@ TRACE_5("FiredWeather",_temperature,_pressure,_relativeHumidity,_airDensity,_rel
//powder effects: //powder effects:
private _newMuzzleVelocityCoefficent = (((_temperature + 273.13) / 288.13 - 1) / 40 + 1); private _newMuzzleVelocityCoefficent = (((_temperature + 273.13) / 288.13 - 1) / 40 + 1);
if (_newMuzzleVelocityCoefficent != 1) then { if (_newMuzzleVelocityCoefficent != 1) then {
_bulletVelocity = velocity _projectile; private _bulletVelocity = velocity _projectile;
_bulletSpeed = vectorMagnitude _bulletVelocity; private _bulletSpeed = vectorMagnitude _bulletVelocity;
_bulletVelocity = (vectorNormalized _bulletVelocity) vectorMultiply (_bulletSpeed * _newMuzzleVelocityCoefficent); _bulletVelocity = (vectorNormalized _bulletVelocity) vectorMultiply (_bulletSpeed * _newMuzzleVelocityCoefficent);
_projectile setVelocity _bulletVelocity; _projectile setVelocity _bulletVelocity;
}; };
@ -64,7 +62,6 @@ if (_newMuzzleVelocityCoefficent != 1) then {
_args set[2, CBA_missionTime]; _args set[2, CBA_missionTime];
private _bulletVelocity = velocity _shell; private _bulletVelocity = velocity _shell;
private _bulletSpeed = vectorMagnitude _bulletVelocity;
private _trueVelocity = _bulletVelocity vectorDiff wind; private _trueVelocity = _bulletVelocity vectorDiff wind;
private _trueSpeed = vectorMagnitude _trueVelocity; private _trueSpeed = vectorMagnitude _trueVelocity;

View File

@ -56,40 +56,13 @@ if (_lastFireMode != -1) then {
private _display = uiNamespace getVariable ["ACE_Mk6_RscWeaponRangeArtillery", displayNull]; private _display = uiNamespace getVariable ["ACE_Mk6_RscWeaponRangeArtillery", displayNull];
if (isNull _display) exitWith {}; //It may be null for the first frame if (isNull _display) exitWith {}; //It may be null for the first frame
private _chargeText = format ["<t size='0.8'>%1: %2 <img image='%3'/></t>", (localize LSTRING(rangetable_charge)), _currentChargeMode, QPATHTOF(UI\ui_charges.paa)];
//Hud should hidden in 3rd person //Hud should hidden in 3rd person
private _notGunnerView = cameraView != "GUNNER"; private _notGunnerView = cameraView != "GUNNER";
//Calc real azimuth/elevation // Get aiming values from ace_artillerytables
//(looking at the sky VS looking at ground will radicaly change fire direction because BIS) // Note: it also handles displaying the "charge" level
private _realAzimuth = -1; private _realAzimuth = missionNamespace getVariable [QEGVAR(artillerytables,predictedAzimuth), -1];
private _realElevation = -1; private _realElevation = missionNamespace getVariable [QEGVAR(artillerytables,predictedElevation), -1];
private _useRealWeaponDir = (ctrlText (_display displayCtrl 173)) == "--";
if (_useRealWeaponDir && {(_mortarVeh ammo (currentWeapon _mortarVeh)) == 0}) then {
// With no ammo, distance display will be empty, but gun will still fire at wonky angle if aimed at ground
private _testSeekerPosASL = AGLtoASL (positionCameraToWorld [0,0,0]);
private _testSeekerDir = _testSeekerPosASL vectorFromTo (AGLtoASL (positionCameraToWorld [0,0,1]));
private _testPoint = _testSeekerPosASL vectorAdd (_testSeekerDir vectorMultiply viewDistance);
if ((terrainIntersectASL [_testSeekerPosASL, _testPoint]) || {lineIntersects [_testSeekerPosASL, _testPoint]}) then {
_useRealWeaponDir = false; // If we are not looking at infinity (based on viewDistance)
};
};
if (_useRealWeaponDir) then {
//No range (looking at sky), it will follow weaponDir:
private _weaponDir = _mortarVeh weaponDirection (currentWeapon _mortarVeh);
_realAzimuth = (_weaponDir select 0) atan2 (_weaponDir select 1);
_realElevation = asin (_weaponDir select 2);
} else {
//Valid range, will fire at camera dir
private _lookVector = ((positionCameraToWorld [0,0,0]) call EFUNC(common,positionToASL)) vectorFromTo ((positionCameraToWorld [0,0,10]) call EFUNC(common,positionToASL));
_realAzimuth = ((_lookVector select 0) atan2 (_lookVector select 1));
private _upVectorDir = (((vectorUp _mortarVeh) select 0) atan2 ((vectorUp _mortarVeh) select 1));
private _elevationDiff = (cos (_realAzimuth - _upVectorDir)) * acos ((vectorUp _mortarVeh) select 2);
_realElevation = ((180 / PI) * (_mortarVeh animationPhase "mainGun")) + 75 - _elevationDiff;
};
//Update Heading Display: //Update Heading Display:
if (_notGunnerView || (!GVAR(allowCompass))) then { if (_notGunnerView || (!GVAR(allowCompass))) then {
@ -102,13 +75,10 @@ if (_lastFireMode != -1) then {
}; };
}; };
//Update CurrentElevation Display and "charge" text //Update CurrentElevation Display
if (_notGunnerView) then { if (_notGunnerView) then {
(_display displayCtrl 80085) ctrlSetStructuredText parseText "";
(_display displayCtrl 80175) ctrlSetText ""; (_display displayCtrl 80175) ctrlSetText "";
} else { } else {
(_display displayCtrl 80085) ctrlSetStructuredText parseText _chargeText;
if (_useMils) then { if (_useMils) then {
(_display displayCtrl 80175) ctrlSetText str ((round (_realElevation * 6400 / 360)) % 6400); (_display displayCtrl 80175) ctrlSetText str ((round (_realElevation * 6400 / 360)) % 6400);
} else { } else {

View File

@ -17,7 +17,7 @@
* Public: No * Public: No
*/ */
params ["_logic", "_syncedUnits", "_activated"]; params ["_logic", "", "_activated"];
if (!_activated) exitWith {WARNING("Module - placed but not active");}; if (!_activated) exitWith {WARNING("Module - placed but not active");};

View File

@ -15,32 +15,6 @@
* Public: No * Public: No
*/ */
#define LIST_CHARGE ((uiNamespace getVariable "ACE_82mm_RangeTable_Dialog") displayCtrl 1501) TRACE_1("rangeTableOpen - defer to artillerytables",_this);
private _weaponName = "mortar_82mm"; //todo: work on other weapons ["mortar_82mm", 45, 88, GVAR(airResistanceEnabled) || EGVAR(artillerytables,advancedCorrections)] call EFUNC(artillerytables,rangeTableOpen);
createDialog "ACE_82mm_RangeTable_Dialog";
if (isNull (uiNamespace getVariable ["ACE_82mm_RangeTable_Dialog", displayNull])) exitWith {ERROR("Dialog failed to open");};
//Get Magazine Types
private _magazines = getArray (configFile >> "CfgWeapons" >> _weaponName >> "magazines");
//For now just get settings from first mag, all rounds have same flight characteristics:
if ((count _magazines) < 1) exitWith {ERROR("No Magazines for weapon");};
private _initSpeed = getNumber (configFile >> "CfgMagazines" >> (_magazines select 0) >> "initSpeed");
//Get Charge Modes
private _fireModes = getArray (configFile >> "CfgWeapons" >> _weaponName >> "modes");
private _muzzleVelocities = [];
{
private _showToPlayer = getNumber (configFile >> "CfgWeapons" >> _weaponName >> _x >> "showToPlayer");
if (_showToPlayer == 1) then {
private _artilleryCharge = getNumber (configFile >> "CfgWeapons" >> _weaponName >> _x >> "artilleryCharge");
LIST_CHARGE lbAdd format ["%1: %2", (localize LSTRING(rangetable_charge)), (count _muzzleVelocities)];
LIST_CHARGE lbSetData [(count _muzzleVelocities), str (_artilleryCharge * _initSpeed)];
_muzzleVelocities pushBack _artilleryCharge;
};
} forEach _fireModes;
LIST_CHARGE lbSetCurSel 0;

View File

@ -1,35 +0,0 @@
#include "script_component.hpp"
/*
* Author: PabstMirror
* Called when listbox selection changes. Updates the rangetable with new values.
*
* Arguments:
* None
*
* Return Value:
* None
*
* Example:
* [] call ace_mk6mortar_fnc_rangeTablePageChange
*
* Public: No
*/
#define RANGE_TABLE ((uiNamespace getVariable "ACE_82mm_RangeTable_Dialog") displayCtrl 20001)
#define LIST_CHARGE ((uiNamespace getVariable "ACE_82mm_RangeTable_Dialog") displayCtrl 1501)
private _listBoxData = LIST_CHARGE lbData (lbCurSel LIST_CHARGE);
if (isNil "_listBoxData" || {_listBoxData == ""}) exitWith {ERROR("lbCurSel out of bounds or no data");};
private _muzzleVelocity = parseNumber _listBoxData;
private _airFriction = if (GVAR(airResistanceEnabled)) then {MK6_82mm_AIR_FRICTION} else {0};
private _precalcArray = [_muzzleVelocity, _airFriction] call FUNC(rangeTablePreCalculatedValues);
lnbClear RANGE_TABLE;
{
RANGE_TABLE lnbAddRow _x;
} forEach _precalcArray;
//put dummy line at end because scrolling is fucked and can't see last line
RANGE_TABLE lnbAddRow ["", "", "", "", "", "", "", "", "", "", ""];

View File

@ -1,271 +0,0 @@
#include "script_component.hpp"
/*
* Author: PabstMirror
* Simple Lookup Table for various muzzle velocities and air frictions.
* Use ace_mk6mortar_fnc_dev_buildTable to build
*
* Arguments:
* 0: Muzzle Velocity <NUMBER>
* 1: Air Friction <NUMBER>
*
* Return Value:
* Array <ARRAY>
*
* Example:
* [200, 0] call ace_mk6mortar_fnc_rangeTablePreCalculatedValues
*
* Public: No
*/
params ["_muzzleVelocity", "_airFriction"];
switch (true) do {
case ((abs(_muzzleVelocity - 70) < 0.00001) && {(abs(_airFriction - -0.0001) < 0.00001)}): {
[
["100","1493","9","1.4","14.0","3.7","0.4","-0.3","0.0","0.0","0.0","0.0"],
["150","1438","14","1.4","13.9","2.5","0.4","-0.4","0.0","0.0","-0.1","0.0"],
["200","1381","20","1.4","13.8","1.9","0.5","-0.4","0.0","0.0","-0.1","0.1"],
["250","1321","27","1.5","13.6","1.5","0.5","-0.4","0.0","0.0","-0.1","0.1"],
["300","1256","36","1.6","13.3","1.3","0.6","-0.5","0.0","-0.1","-0.1","0.1"],
["350","1183","49","1.7","12.9","1.1","0.6","-0.5","0.1","-0.1","-0.1","0.1"],
["400","1097","70","1.9","12.4","0.9","0.6","-0.5","0.1","-0.1","-0.2","0.1"],
["450","979","113","2.3","11.6","0.8","0.6","-0.5","0.1","-0.1","-0.2","0.2"]
]
};
case ((abs(_muzzleVelocity - 140) < 0.00001) && {(abs(_airFriction - -0.0001) < 0.00001)}): {
[
["150","1556","1","0.8","27.2","16.3","2.5","-2.4","0.0","0.0","-0.2","0.2"],
["200","1541","1","0.8","27.2","12.3","2.5","-2.4","0.0","0.0","-0.3","0.2"],
["250","1527","2","0.8","27.2","9.9","2.6","-2.4","0.0","0.0","-0.3","0.3"],
["300","1512","2","0.8","27.2","8.3","2.7","-2.4","0.1","0.0","-0.4","0.4"],
["350","1497","3","0.8","27.1","7.1","2.7","-2.5","0.0","-0.1","-0.5","0.4"],
["400","1482","3","0.8","27.1","6.2","2.7","-2.5","0.1","-0.1","-0.5","0.5"],
["450","1467","3","0.8","27.0","5.6","2.8","-2.5","0.1","-0.1","-0.6","0.6"],
["500","1451","4","0.8","27.0","5.0","2.9","-2.6","0.1","-0.1","-0.6","0.6"],
["550","1436","4","0.8","26.9","4.6","2.9","-2.6","0.1","-0.1","-0.7","0.7"],
["600","1420","5","0.8","26.8","4.2","3.0","-2.7","0.1","-0.1","-0.8","0.8"],
["650","1404","5","0.8","26.8","3.9","3.0","-2.7","0.1","-0.1","-0.9","0.8"],
["700","1388","6","0.8","26.7","3.6","3.1","-2.8","0.1","-0.1","-0.9","0.9"],
["750","1372","6","0.8","26.6","3.4","3.2","-2.8","0.1","-0.1","-1.0","1.0"],
["800","1355","7","0.8","26.5","3.2","3.2","-2.9","0.1","-0.1","-1.1","1.1"],
["850","1338","8","0.8","26.4","3.0","3.3","-2.9","0.1","-0.1","-1.1","1.1"],
["900","1321","8","0.8","26.2","2.8","3.4","-3.0","0.1","-0.1","-1.2","1.2"],
["950","1303","9","0.9","26.1","2.7","3.4","-3.1","0.1","-0.2","-1.3","1.2"],
["1000","1285","10","0.9","26.0","2.6","3.5","-3.1","0.2","-0.1","-1.4","1.3"],
["1050","1266","11","0.9","25.8","2.4","3.5","-3.2","0.1","-0.2","-1.4","1.4"],
["1100","1247","12","0.9","25.7","2.3","3.6","-3.3","0.1","-0.2","-1.5","1.4"],
["1150","1228","13","0.9","25.5","2.2","3.7","-3.3","0.2","-0.2","-1.6","1.5"],
["1200","1207","14","1.0","25.3","2.1","3.7","-3.4","0.2","-0.2","-1.7","1.6"],
["1250","1186","15","1.0","25.1","2.0","3.8","-3.4","0.2","-0.2","-1.7","1.7"],
["1300","1163","17","1.0","24.8","1.9","3.8","-3.5","0.2","-0.2","-1.8","1.7"],
["1350","1140","19","1.0","24.6","1.9","3.9","-3.5","0.2","-0.2","-1.9","1.8"],
["1400","1115","21","1.1","24.3","1.8","3.9","-3.6","0.2","-0.2","-1.9","1.9"],
["1450","1088","24","1.1","23.9","1.7","4.0","-3.6","0.2","-0.2","-2.0","1.9"],
["1500","1060","27","1.2","23.6","1.6","4.0","-3.7","0.2","-0.2","-2.1","2.0"],
["1550","1028","32","1.3","23.1","1.5","4.0","-3.7","0.2","-0.2","-2.1","2.1"],
["1600","991","38","1.4","22.6","1.5","4.0","-3.7","0.2","-0.2","-2.2","2.1"],
["1650","947","49","1.7","21.9","1.4","4.0","-3.7","0.2","-0.3","-2.3","2.2"],
["1700","888","71","2.1","21.0","1.3","3.9","-3.6","0.3","-0.3","-2.3","2.2"]
]
};
case ((abs(_muzzleVelocity - 200) < 0.00001) && {(abs(_airFriction - -0.0001) < 0.00001)}): {
[
["250","1559","1","0.6","37.3","23.8","6.1","-5.9","0.0","0.0","-0.6","0.5"],
["300","1551","1","0.6","37.3","20.0","6.1","-5.9","0.1","0.0","-0.7","0.7"],
["350","1543","1","0.6","37.3","17.2","6.2","-5.9","0.0","-0.1","-0.8","0.7"],
["400","1535","1","0.6","37.3","15.1","6.2","-5.9","0.1","0.0","-0.9","0.9"],
["450","1527","1","0.6","37.3","13.4","6.3","-6.0","0.1","-0.1","-1.0","1.0"],
["500","1519","1","0.6","37.2","12.1","6.3","-6.0","0.1","-0.1","-1.1","1.1"],
["550","1510","1","0.6","37.2","11.0","6.4","-6.0","0.1","-0.1","-1.3","1.2"],
["600","1502","1","0.6","37.2","10.1","6.4","-6.1","0.1","-0.1","-1.4","1.3"],
["650","1494","1","0.6","37.2","9.4","6.5","-6.1","0.1","-0.1","-1.5","1.4"],
["700","1485","2","0.6","37.1","8.7","6.5","-6.2","0.1","-0.1","-1.6","1.5"],
["750","1477","2","0.6","37.1","8.2","6.6","-6.2","0.1","-0.1","-1.7","1.6"],
["800","1468","2","0.6","37.0","7.7","6.7","-6.3","0.1","-0.1","-1.8","1.8"],
["850","1460","2","0.6","37.0","7.2","6.7","-6.3","0.1","-0.1","-2.0","1.9"],
["900","1451","2","0.6","37.0","6.8","6.8","-6.4","0.1","-0.1","-2.1","2.0"],
["950","1443","2","0.6","36.9","6.5","6.9","-6.4","0.1","-0.1","-2.2","2.1"],
["1000","1434","2","0.6","36.9","6.2","6.9","-6.5","0.1","-0.1","-2.3","2.2"],
["1050","1425","2","0.6","36.8","5.9","7.0","-6.6","0.1","-0.2","-2.5","2.3"],
["1100","1417","3","0.6","36.8","5.6","7.1","-6.6","0.1","-0.2","-2.6","2.4"],
["1150","1408","3","0.6","36.7","5.4","7.1","-6.7","0.2","-0.2","-2.7","2.5"],
["1200","1399","3","0.6","36.6","5.2","7.2","-6.7","0.2","-0.2","-2.8","2.7"],
["1250","1390","3","0.6","36.6","5.0","7.3","-6.8","0.2","-0.2","-2.9","2.8"],
["1300","1381","3","0.6","36.5","4.8","7.4","-6.9","0.2","-0.2","-3.0","2.9"],
["1350","1372","3","0.6","36.4","4.6","7.4","-6.9","0.2","-0.2","-3.2","3.0"],
["1400","1362","4","0.6","36.4","4.4","7.5","-7.0","0.2","-0.2","-3.3","3.1"],
["1450","1353","4","0.6","36.3","4.3","7.6","-7.1","0.2","-0.2","-3.4","3.2"],
["1500","1344","4","0.6","36.2","4.2","7.7","-7.1","0.2","-0.2","-3.5","3.4"],
["1550","1334","4","0.6","36.1","4.0","7.7","-7.2","0.2","-0.2","-3.7","3.5"],
["1600","1324","4","0.6","36.0","3.9","7.8","-7.3","0.2","-0.2","-3.8","3.6"],
["1650","1314","4","0.7","35.9","3.8","7.9","-7.3","0.2","-0.2","-3.9","3.7"],
["1700","1304","5","0.7","35.8","3.7","7.9","-7.4","0.2","-0.2","-4.0","3.8"],
["1750","1294","5","0.7","35.7","3.6","8.0","-7.5","0.2","-0.2","-4.2","3.9"],
["1800","1284","5","0.7","35.6","3.5","8.1","-7.6","0.2","-0.3","-4.3","4.0"],
["1850","1274","5","0.7","35.5","3.4","8.2","-7.6","0.2","-0.3","-4.4","4.2"],
["1900","1263","6","0.7","35.4","3.3","8.2","-7.7","0.2","-0.3","-4.5","4.3"],
["1950","1253","6","0.7","35.2","3.2","8.3","-7.8","0.2","-0.3","-4.7","4.4"],
["2000","1242","6","0.7","35.1","3.1","8.4","-7.8","0.3","-0.3","-4.8","4.5"],
["2050","1231","7","0.7","35.0","3.0","8.4","-7.9","0.3","-0.3","-4.9","4.7"],
["2100","1219","7","0.7","34.8","2.9","8.5","-8.0","0.3","-0.3","-5.0","4.8"],
["2150","1208","7","0.7","34.7","2.9","8.5","-8.0","0.3","-0.3","-5.2","4.9"],
["2200","1196","8","0.7","34.5","2.8","8.6","-8.1","0.3","-0.3","-5.3","5.0"],
["2250","1184","8","0.7","34.3","2.7","8.7","-8.2","0.3","-0.3","-5.4","5.1"],
["2300","1171","9","0.8","34.2","2.7","8.7","-8.2","0.3","-0.3","-5.5","5.2"],
["2350","1158","9","0.8","34.0","2.6","8.8","-8.3","0.3","-0.3","-5.7","5.4"],
["2400","1145","10","0.8","33.8","2.5","8.8","-8.3","0.3","-0.3","-5.8","5.5"],
["2450","1132","10","0.8","33.6","2.5","8.9","-8.4","0.3","-0.3","-5.9","5.6"],
["2500","1118","11","0.8","33.3","2.4","8.9","-8.4","0.3","-0.3","-6.0","5.7"],
["2550","1103","12","0.8","33.1","2.4","9.0","-8.5","0.3","-0.3","-6.1","5.8"],
["2600","1088","13","0.9","32.8","2.3","9.0","-8.5","0.4","-0.3","-6.2","5.9"],
["2650","1072","14","0.9","32.6","2.2","9.0","-8.6","0.4","-0.4","-6.4","6.0"],
["2700","1056","15","0.9","32.3","2.2","9.0","-8.6","0.3","-0.4","-6.5","6.1"],
["2750","1038","16","1.0","31.9","2.1","9.1","-8.6","0.4","-0.4","-6.6","6.3"],
["2800","1020","18","1.0","31.6","2.1","9.1","-8.6","0.4","-0.4","-6.7","6.4"],
["2850","1000","20","1.1","31.2","2.0","9.1","-8.6","0.4","-0.4","-6.8","6.5"],
["2900","978","22","1.1","30.8","1.9","9.0","-8.6","0.4","-0.4","-6.9","6.5"],
["2950","954","26","1.2","30.3","1.9","9.0","-8.6","0.4","-0.4","-7.0","6.6"],
["3000","927","31","1.4","29.7","1.8","8.9","-8.5","0.4","-0.4","-7.1","6.7"],
["3050","894","38","1.6","29.0","1.7","8.8","-8.4","0.4","-0.4","-7.2","6.8"],
["3100","849","54","2.0","27.9","1.6","8.5","-8.3","0.4","-0.4","-7.2","6.8"]
]
};
case ((abs(_muzzleVelocity - 70) < 0.00001) && {(abs(_airFriction - 0) < 0.00001)}): {
[
["100","1497","9","1.3","14.2","0.0","0.0","0.0","0.0","0.0","0.0","0.0"],
["150","1445","14","1.3","14.1","0.0","0.0","0.0","0.0","0.0","0.0","0.0"],
["200","1390","19","1.4","14.0","0.0","0.0","0.0","0.0","0.0","0.0","0.0"],
["250","1333","26","1.4","13.8","0.0","0.0","0.0","0.0","0.0","0.0","0.0"],
["300","1272","34","1.5","13.5","0.0","0.0","0.0","0.0","0.0","0.0","0.0"],
["350","1204","45","1.6","13.2","0.0","0.0","0.0","0.0","0.0","0.0","0.0"],
["400","1127","61","1.8","12.8","0.0","0.0","0.0","0.0","0.0","0.0","0.0"],
["450","1028","91","2.1","12.1","0.0","0.0","0.0","0.0","0.0","0.0","0.0"]
]
};
case ((abs(_muzzleVelocity - 140) < 0.00001) && {(abs(_airFriction - 0) < 0.00001)}): {
[
["150","1562","1","0.7","28.5","0.0","0.0","0.0","0.0","0.0","0.0","0.0"],
["200","1549","1","0.7","28.5","0.0","0.0","0.0","0.0","0.0","0.0","0.0"],
["250","1536","2","0.7","28.5","0.0","0.0","0.0","0.0","0.0","0.0","0.0"],
["300","1523","2","0.7","28.5","0.0","0.0","0.0","0.0","0.0","0.0","0.0"],
["350","1510","2","0.7","28.4","0.0","0.0","0.0","0.0","0.0","0.0","0.0"],
["400","1497","3","0.7","28.4","0.0","0.0","0.0","0.0","0.0","0.0","0.0"],
["450","1484","3","0.7","28.4","0.0","0.0","0.0","0.0","0.0","0.0","0.0"],
["500","1471","3","0.7","28.3","0.0","0.0","0.0","0.0","0.0","0.0","0.0"],
["550","1458","4","0.7","28.3","0.0","0.0","0.0","0.0","0.0","0.0","0.0"],
["600","1445","4","0.7","28.2","0.0","0.0","0.0","0.0","0.0","0.0","0.0"],
["650","1431","4","0.7","28.2","0.0","0.0","0.0","0.0","0.0","0.0","0.0"],
["700","1418","5","0.7","28.1","0.0","0.0","0.0","0.0","0.0","0.0","0.0"],
["750","1404","5","0.7","28.0","0.0","0.0","0.0","0.0","0.0","0.0","0.0"],
["800","1390","6","0.7","27.9","0.0","0.0","0.0","0.0","0.0","0.0","0.0"],
["850","1376","6","0.7","27.9","0.0","0.0","0.0","0.0","0.0","0.0","0.0"],
["900","1362","6","0.8","27.8","0.0","0.0","0.0","0.0","0.0","0.0","0.0"],
["950","1348","7","0.8","27.7","0.0","0.0","0.0","0.0","0.0","0.0","0.0"],
["1000","1333","7","0.8","27.6","0.0","0.0","0.0","0.0","0.0","0.0","0.0"],
["1050","1318","8","0.8","27.5","0.0","0.0","0.0","0.0","0.0","0.0","0.0"],
["1100","1303","9","0.8","27.3","0.0","0.0","0.0","0.0","0.0","0.0","0.0"],
["1150","1288","9","0.8","27.2","0.0","0.0","0.0","0.0","0.0","0.0","0.0"],
["1200","1272","10","0.8","27.1","0.0","0.0","0.0","0.0","0.0","0.0","0.0"],
["1250","1256","11","0.8","26.9","0.0","0.0","0.0","0.0","0.0","0.0","0.0"],
["1300","1239","12","0.8","26.8","0.0","0.0","0.0","0.0","0.0","0.0","0.0"],
["1350","1222","13","0.9","26.6","0.0","0.0","0.0","0.0","0.0","0.0","0.0"],
["1400","1205","13","0.9","26.4","0.0","0.0","0.0","0.0","0.0","0.0","0.0"],
["1450","1187","15","0.9","26.2","0.0","0.0","0.0","0.0","0.0","0.0","0.0"],
["1500","1168","16","0.9","26.0","0.0","0.0","0.0","0.0","0.0","0.0","0.0"],
["1550","1148","18","1.0","25.8","0.0","0.0","0.0","0.0","0.0","0.0","0.0"],
["1600","1127","19","1.0","25.5","0.0","0.0","0.0","0.0","0.0","0.0","0.0"],
["1650","1105","21","1.1","25.2","0.0","0.0","0.0","0.0","0.0","0.0","0.0"],
["1700","1082","24","1.1","24.9","0.0","0.0","0.0","0.0","0.0","0.0","0.0"],
["1750","1057","27","1.2","24.6","0.0","0.0","0.0","0.0","0.0","0.0","0.0"],
["1800","1029","31","1.3","24.2","0.0","0.0","0.0","0.0","0.0","0.0","0.0"],
["1850","997","37","1.4","23.7","0.0","0.0","0.0","0.0","0.0","0.0","0.0"],
["1900","960","46","1.6","23.1","0.0","0.0","0.0","0.0","0.0","0.0","0.0"],
["1950","912","63","1.9","22.3","0.0","0.0","0.0","0.0","0.0","0.0","0.0"]
]
};
case ((abs(_muzzleVelocity - 200) < 0.00001) && {(abs(_airFriction - 0) < 0.00001)}): {
[
["300","1563","0","0.5","40.8","0.0","0.0","0.0","0.0","0.0","0.0","0.0"],
["350","1556","1","0.5","40.8","0.0","0.0","0.0","0.0","0.0","0.0","0.0"],
["400","1550","1","0.5","40.7","0.0","0.0","0.0","0.0","0.0","0.0","0.0"],
["450","1544","1","0.5","40.7","0.0","0.0","0.0","0.0","0.0","0.0","0.0"],
["500","1537","1","0.5","40.7","0.0","0.0","0.0","0.0","0.0","0.0","0.0"],
["550","1531","1","0.5","40.7","0.0","0.0","0.0","0.0","0.0","0.0","0.0"],
["600","1525","1","0.5","40.7","0.0","0.0","0.0","0.0","0.0","0.0","0.0"],
["650","1519","1","0.5","40.7","0.0","0.0","0.0","0.0","0.0","0.0","0.0"],
["700","1512","1","0.5","40.6","0.0","0.0","0.0","0.0","0.0","0.0","0.0"],
["750","1506","1","0.5","40.6","0.0","0.0","0.0","0.0","0.0","0.0","0.0"],
["800","1499","1","0.5","40.6","0.0","0.0","0.0","0.0","0.0","0.0","0.0"],
["850","1493","1","0.5","40.6","0.0","0.0","0.0","0.0","0.0","0.0","0.0"],
["900","1487","1","0.5","40.5","0.0","0.0","0.0","0.0","0.0","0.0","0.0"],
["950","1480","1","0.5","40.5","0.0","0.0","0.0","0.0","0.0","0.0","0.0"],
["1000","1474","2","0.5","40.5","0.0","0.0","0.0","0.0","0.0","0.0","0.0"],
["1050","1467","2","0.5","40.5","0.0","0.0","0.0","0.0","0.0","0.0","0.0"],
["1100","1461","2","0.5","40.4","0.0","0.0","0.0","0.0","0.0","0.0","0.0"],
["1150","1454","2","0.5","40.4","0.0","0.0","0.0","0.0","0.0","0.0","0.0"],
["1200","1448","2","0.5","40.3","0.0","0.0","0.0","0.0","0.0","0.0","0.0"],
["1250","1441","2","0.5","40.3","0.0","0.0","0.0","0.0","0.0","0.0","0.0"],
["1300","1435","2","0.5","40.3","0.0","0.0","0.0","0.0","0.0","0.0","0.0"],
["1350","1428","2","0.5","40.2","0.0","0.0","0.0","0.0","0.0","0.0","0.0"],
["1400","1422","2","0.5","40.2","0.0","0.0","0.0","0.0","0.0","0.0","0.0"],
["1450","1415","2","0.5","40.1","0.0","0.0","0.0","0.0","0.0","0.0","0.0"],
["1500","1408","2","0.5","40.1","0.0","0.0","0.0","0.0","0.0","0.0","0.0"],
["1550","1402","3","0.5","40.0","0.0","0.0","0.0","0.0","0.0","0.0","0.0"],
["1600","1395","3","0.5","40.0","0.0","0.0","0.0","0.0","0.0","0.0","0.0"],
["1650","1388","3","0.5","39.9","0.0","0.0","0.0","0.0","0.0","0.0","0.0"],
["1700","1381","3","0.5","39.9","0.0","0.0","0.0","0.0","0.0","0.0","0.0"],
["1750","1374","3","0.5","39.8","0.0","0.0","0.0","0.0","0.0","0.0","0.0"],
["1800","1367","3","0.5","39.7","0.0","0.0","0.0","0.0","0.0","0.0","0.0"],
["1850","1360","3","0.5","39.7","0.0","0.0","0.0","0.0","0.0","0.0","0.0"],
["1900","1353","3","0.5","39.6","0.0","0.0","0.0","0.0","0.0","0.0","0.0"],
["1950","1346","4","0.5","39.5","0.0","0.0","0.0","0.0","0.0","0.0","0.0"],
["2000","1339","4","0.5","39.5","0.0","0.0","0.0","0.0","0.0","0.0","0.0"],
["2050","1332","4","0.5","39.4","0.0","0.0","0.0","0.0","0.0","0.0","0.0"],
["2100","1325","4","0.6","39.3","0.0","0.0","0.0","0.0","0.0","0.0","0.0"],
["2150","1317","4","0.6","39.2","0.0","0.0","0.0","0.0","0.0","0.0","0.0"],
["2200","1310","4","0.6","39.2","0.0","0.0","0.0","0.0","0.0","0.0","0.0"],
["2250","1302","4","0.6","39.1","0.0","0.0","0.0","0.0","0.0","0.0","0.0"],
["2300","1295","5","0.6","39.0","0.0","0.0","0.0","0.0","0.0","0.0","0.0"],
["2350","1287","5","0.6","38.9","0.0","0.0","0.0","0.0","0.0","0.0","0.0"],
["2400","1280","5","0.6","38.8","0.0","0.0","0.0","0.0","0.0","0.0","0.0"],
["2450","1272","5","0.6","38.7","0.0","0.0","0.0","0.0","0.0","0.0","0.0"],
["2500","1264","5","0.6","38.6","0.0","0.0","0.0","0.0","0.0","0.0","0.0"],
["2550","1256","5","0.6","38.5","0.0","0.0","0.0","0.0","0.0","0.0","0.0"],
["2600","1248","6","0.6","38.4","0.0","0.0","0.0","0.0","0.0","0.0","0.0"],
["2650","1240","6","0.6","38.3","0.0","0.0","0.0","0.0","0.0","0.0","0.0"],
["2700","1232","6","0.6","38.2","0.0","0.0","0.0","0.0","0.0","0.0","0.0"],
["2750","1223","6","0.6","38.0","0.0","0.0","0.0","0.0","0.0","0.0","0.0"],
["2800","1215","7","0.6","37.9","0.0","0.0","0.0","0.0","0.0","0.0","0.0"],
["2850","1206","7","0.6","37.8","0.0","0.0","0.0","0.0","0.0","0.0","0.0"],
["2900","1197","7","0.6","37.6","0.0","0.0","0.0","0.0","0.0","0.0","0.0"],
["2950","1188","7","0.7","37.5","0.0","0.0","0.0","0.0","0.0","0.0","0.0"],
["3000","1179","8","0.7","37.4","0.0","0.0","0.0","0.0","0.0","0.0","0.0"],
["3050","1170","8","0.7","37.2","0.0","0.0","0.0","0.0","0.0","0.0","0.0"],
["3100","1160","8","0.7","37.1","0.0","0.0","0.0","0.0","0.0","0.0","0.0"],
["3150","1151","9","0.7","36.9","0.0","0.0","0.0","0.0","0.0","0.0","0.0"],
["3200","1141","9","0.7","36.7","0.0","0.0","0.0","0.0","0.0","0.0","0.0"],
["3250","1131","10","0.7","36.5","0.0","0.0","0.0","0.0","0.0","0.0","0.0"],
["3300","1120","10","0.7","36.3","0.0","0.0","0.0","0.0","0.0","0.0","0.0"],
["3350","1109","11","0.8","36.2","0.0","0.0","0.0","0.0","0.0","0.0","0.0"],
["3400","1098","11","0.8","35.9","0.0","0.0","0.0","0.0","0.0","0.0","0.0"],
["3450","1087","12","0.8","35.7","0.0","0.0","0.0","0.0","0.0","0.0","0.0"],
["3500","1075","13","0.8","35.5","0.0","0.0","0.0","0.0","0.0","0.0","0.0"],
["3550","1062","14","0.8","35.2","0.0","0.0","0.0","0.0","0.0","0.0","0.0"],
["3600","1049","15","0.9","35.0","0.0","0.0","0.0","0.0","0.0","0.0","0.0"],
["3650","1036","16","0.9","34.7","0.0","0.0","0.0","0.0","0.0","0.0","0.0"],
["3700","1021","17","0.9","34.4","0.0","0.0","0.0","0.0","0.0","0.0","0.0"],
["3750","1006","19","1.0","34.1","0.0","0.0","0.0","0.0","0.0","0.0","0.0"],
["3800","990","21","1.1","33.7","0.0","0.0","0.0","0.0","0.0","0.0","0.0"],
["3850","971","24","1.1","33.3","0.0","0.0","0.0","0.0","0.0","0.0","0.0"],
["3900","952","27","1.2","32.8","0.0","0.0","0.0","0.0","0.0","0.0","0.0"],
["3950","929","32","1.4","32.2","0.0","0.0","0.0","0.0","0.0","0.0","0.0"],
["4000","900","40","1.6","31.5","0.0","0.0","0.0","0.0","0.0","0.0","0.0"],
["4050","861","56","2.1","30.5","0.0","0.0","0.0","0.0","0.0","0.0","0.0"]
]
};
default {
ERROR("MuzzleVelocity not found in LUT");
[]
};
};

View File

@ -5,7 +5,6 @@
* *
* Arguments: * Arguments:
* 0: Vehicle <OBJECT> * 0: Vehicle <OBJECT>
* 1: Player <OBJECT>
* *
* Return Value: * Return Value:
* None * None
@ -16,7 +15,8 @@
* Public: No * Public: No
*/ */
params ["_mortarVeh", "_unit"]; params ["_mortarVeh"];
TRACE_1("toggleMils",_mortarVeh);
private _currentSetting = _mortarVeh getVariable [QGVAR(useMils), true]; private _currentSetting = _mortarVeh getVariable [QGVAR(useMils), true];
_mortarVeh setVariable [QGVAR(useMils), (!_currentSetting)]; _mortarVeh setVariable [QGVAR(useMils), (!_currentSetting)];

View File

@ -19,7 +19,7 @@
disableSerialization; disableSerialization;
params ["_display", "_rscType"]; params ["_display", "_rscType"];
TRACE_2("params",_display,_rscType); TRACE_2("turretDisplayLoaded",_display,_rscType);
if (_rscType != "Mk6Mortar") exitWith {}; if (_rscType != "Mk6Mortar") exitWith {};
if (isNull _display) exitWith {}; if (isNull _display) exitWith {};

View File

@ -0,0 +1,44 @@
// CBA Settings [ADDON: ace_mk6mortar]:
// These settings effect gameplay difficutly: defaults will leave the mortar the same as vanilla
private _category = [format ["ACE %1", localize "str_a3_cfgmarkers_nato_art"], localize LSTRING(DisplayName)];
[
QGVAR(airResistanceEnabled), "CHECKBOX",
[LSTRING(airResistanceEnabled_DisplayName), LSTRING(airResistanceEnabled_Description)],
_category,
false, // default value
true, // isGlobal
{[QGVAR(airResistanceEnabled), _this] call EFUNC(common,cbaSettings_settingChanged)},
true // Needs mission restart
] call CBA_settings_fnc_init;
[
QGVAR(allowComputerRangefinder), "CHECKBOX",
[LSTRING(allowComputerRangefinder_DisplayName), LSTRING(allowComputerRangefinder_Description)],
_category,
true, // default value
true, // isGlobal
{[QGVAR(allowComputerRangefinder), _this] call EFUNC(common,cbaSettings_settingChanged)},
true // Needs mission restart
] call CBA_settings_fnc_init;
[
QGVAR(allowCompass), "CHECKBOX",
[LSTRING(allowCompass_DisplayName), LSTRING(allowCompass_Description)],
_category,
true, // default value
true, // isGlobal
{[QGVAR(allowCompass), _this] call EFUNC(common,cbaSettings_settingChanged)},
true // Needs mission restart
] call CBA_settings_fnc_init;
[
QGVAR(useAmmoHandling), "CHECKBOX",
[LSTRING(useAmmoHandling_DisplayName), LSTRING(useAmmoHandling_Description)],
_category,
false, // default value
true, // isGlobal
{[QGVAR(useAmmoHandling), _this] call EFUNC(common,cbaSettings_settingChanged)},
true // Needs mission restart
] call CBA_settings_fnc_init;

View File

@ -99,8 +99,8 @@ endif()
string(TIMESTAMP ACE_BUILDSTAMP "%Y-%m-%dT%H:%M:%SZ") string(TIMESTAMP ACE_BUILDSTAMP "%Y-%m-%dT%H:%M:%SZ")
set(ACE_VERSION_MAJOR 3) set(ACE_VERSION_MAJOR 3)
set(ACE_VERSION_MINOR 12) set(ACE_VERSION_MINOR 13)
set(ACE_VERSION_REVISION 3) set(ACE_VERSION_REVISION 0)
EXECUTE_PROCESS(COMMAND git rev-parse --verify HEAD EXECUTE_PROCESS(COMMAND git rev-parse --verify HEAD
OUTPUT_VARIABLE T_ACE_VERSION_BUILD OUTPUT_VARIABLE T_ACE_VERSION_BUILD
OUTPUT_STRIP_TRAILING_WHITESPACE OUTPUT_STRIP_TRAILING_WHITESPACE
@ -137,6 +137,7 @@ add_subdirectory(clipboard)
add_subdirectory(advanced_ballistics) add_subdirectory(advanced_ballistics)
add_subdirectory(medical) add_subdirectory(medical)
add_subdirectory(parse_imagepath) add_subdirectory(parse_imagepath)
add_subdirectory(artillerytables)
# Test Extension for dynamically loading/unloading built extensions; does not build in release # Test Extension for dynamically loading/unloading built extensions; does not build in release
if (DEVEL) if (DEVEL)

View File

@ -0,0 +1,24 @@
set(ACE_EXTENSION_NAME "ace_artillerytables")
file(GLOB SOURCES *.h *.hpp *.c *.cpp)
add_library( ${ACE_EXTENSION_NAME} SHARED ${SOURCES} ${GLOBAL_SOURCES})
target_link_libraries(${ACE_EXTENSION_NAME} ace_common)
set_target_properties(${ACE_EXTENSION_NAME} PROPERTIES PREFIX "")
set_target_properties(${ACE_EXTENSION_NAME} PROPERTIES FOLDER Extensions)
if(CMAKE_COMPILER_IS_GNUCXX)
set_target_properties(${ACE_EXTENSION_NAME} PROPERTIES LINK_SEARCH_START_STATIC 1)
set_target_properties(${ACE_EXTENSION_NAME} PROPERTIES LINK_SEARCH_END_STATIC 1)
endif()
# enable_testing()
# set(ACE_TEST_NAME Test_${ACE_EXTENSION_NAME})
# add_executable(${ACE_TEST_NAME} tests/tester.cpp ${SOURCES})
# target_link_libraries(${ACE_TEST_NAME} ace_common)
# target_link_libraries(${ACE_TEST_NAME} ${ACE_EXTENSION_NAME})
# target_link_libraries(${ACE_TEST_NAME} gtest_main)
# add_test(${ACE_TEST_NAME} ${ACE_TEST_NAME})
# set_target_properties(${ACE_TEST_NAME} PROPERTIES FOLDER Tests)

View File

@ -0,0 +1,283 @@
/*
* artillerytables.hpp
* Author: PabstMirror
*/
#include <iomanip>
#include <future>
#include <sstream>
#include "artillerytables.hpp"
// Constants
static const double timeStep = 1.0 / 60;
static const double rangeSearchErrorMax = 0.001; // ratio * distance
static const double rangeSearchAngleConvergance = 0.000025;
static const double gravityABS = 9.8066;
static const ace::vector3<double> gravityAccl(0, 0, -1 * gravityABS);
// Globals:
std::vector<std::future<std::string>> fWorkers;
unsigned int getLineIndex = 0;
std::tuple<double, double, double> simulateShot(const double _fireAngleRad, const double _muzzleVelocity, const double _heightOfTarget, const double _crossWind, const double _tailWind, const double _temperature, const double _airDensity, double _airFriction) {
// returns: dist traveled to the side (crosswind), dist traveled foward (headwind), time of flight
// note: if shot never reaches height of target, then results are undefined (use negative)
const double kCoefficient = -1.0 * _airDensity * _airFriction;
const double powderEffects = (_airFriction) ? ((_temperature + 273.13) / 288.13 - 1) / 40 + 1 : 1.0;
double currentTime = 0;
ace::vector3<double> currentPosition(0, 0, 0);
ace::vector3<double> lastPosition(currentPosition);
ace::vector3<double> currentVelocity(0, powderEffects * _muzzleVelocity * cos(_fireAngleRad), powderEffects * _muzzleVelocity * sin(_fireAngleRad));
const ace::vector3<double> wind(_crossWind, _tailWind, 0);
while ((currentVelocity.z() > 0) || (currentPosition.z() >= _heightOfTarget)) {
lastPosition = currentPosition;
ace::vector3<double> apparentWind = wind - currentVelocity;
ace::vector3<double> changeInVelocity = gravityAccl + apparentWind * (kCoefficient * apparentWind.magnitude());
currentVelocity += changeInVelocity * timeStep;
currentPosition += currentVelocity * timeStep;
currentTime += timeStep;
}
const double lastCurrentRatio((_heightOfTarget - currentPosition.z()) / (lastPosition.z() - currentPosition.z()));
ace::vector3<double> finalPos = lastPosition.lerp(currentPosition, lastCurrentRatio);
return { finalPos.x(), finalPos.y(), currentTime };
}
std::tuple<double, double> findMaxAngle(const double _muzzleVelocity, const double _airFriction) {
// retrns: angle that goes the furthest, max distance traveled
if (_airFriction == 0) {
return { M_PI_4, _muzzleVelocity * _muzzleVelocity / gravityABS };
}
// With air resitsnce, max distance angle won't be 45 degrees
double bestAngle = M_PI_4;
double bestDistance = -1;
double testResultDist;
for (double testAngle = M_PI_4; testAngle > 0; testAngle -= (M_PI_4 / 100.0)) {
std::tie(std::ignore, testResultDist, std::ignore) = simulateShot(testAngle, _muzzleVelocity, 0, 0, 0, 15, 1, _airFriction);
if (testResultDist > bestDistance) {
bestAngle = testAngle;
bestDistance = testResultDist;
}
}
return { bestAngle, bestDistance };
}
std::tuple<double, double, double> simulateFindSolution(const double _rangeToHit, const double _heightToHit, const double _muzzleVelocity, const double _airFriction, const double _minElev, const double _maxElev, const bool _highArc) {
// returns: actual distance traveled, elevation, time of flight
double searchMin = _minElev;
double searchMax = _maxElev;
if (!_airFriction) {
// can do trivial ballistics physics to get angle, could compute tof as well, but run through sim once to ensure consistancy (uses positive value of g)
double radicand = pow(_muzzleVelocity, 4) - gravityABS * (gravityABS * pow(_rangeToHit, 2) + 2 * _heightToHit * pow(_muzzleVelocity, 2));
if ((!_rangeToHit) || (radicand < 0)) { // radican't
return { -1, -1, -1 };
}
radicand = sqrt(radicand);
double angleRoot = atan((pow(_muzzleVelocity, 2) + radicand) / (gravityABS * _rangeToHit));
if ((angleRoot > _maxElev) || (angleRoot < _minElev)) {
angleRoot = atan((pow(_muzzleVelocity, 2) - radicand) / (gravityABS * _rangeToHit));
}
if ((angleRoot > _maxElev) || (angleRoot < _minElev)) {
return { -1, -1, -1 };
};
const double tof = _rangeToHit / (_muzzleVelocity * cos(angleRoot));
return { _rangeToHit, angleRoot, tof };
}
int numberOfAttempts = 0;
double resultDistance = -1;
double resultTime = -1;
double currentError = 9999;
double currentElevation = -1;
do {
if (numberOfAttempts++ > 50) break; // for safetey, min/max should converge long before
currentElevation = (searchMin + searchMax) / 2.0;
std::tie(std::ignore, resultDistance, resultTime) = simulateShot(currentElevation, _muzzleVelocity, _heightToHit, 0, 0, 15, 1, _airFriction);
currentError = _rangeToHit - resultDistance;
// printf("elev %f [%f, %f]range%f\n goes %f [%f]\n", currentElevation, searchMin, searchMax, (searchMax - searchMin), resultDistance, currentError);
if ((currentError > 0) ^ (!_highArc)) {
searchMax = currentElevation;
} else {
searchMin = currentElevation;
}
} while ((searchMax - searchMin) > rangeSearchAngleConvergance);
// printf("[%f, %f] Actuall [%f] err [%f of %f]\n", _rangeToHit, _heightToHit, resultDistance, currentError, (_rangeToHit * rangeSearchErrorMax * (_highArc ? 1.0 : 2.0)));
// On some low angle shots, it will really struggle to converge because of precision issues
if ((abs(currentError) > (_rangeToHit * rangeSearchErrorMax * (_highArc ? 1.0 : 2.0)))) {
return { -1, -1, -1 };
}
return { resultDistance, currentElevation, resultTime };
}
void writeNumber(std::stringstream & ss, double _num, const int _widthInt, const int _widthDec) {
if ((_num < 0) && (_num > -0.05)) { // hard coded fix -0.0 rounding errors
_num = 0;
}
if (_widthInt > 1) {
ss << std::setfill('0');
}
ss << std::fixed << std::setw(_widthInt) << std::setprecision(_widthDec) << _num;
}
std::string simulateCalcRangeTableLine(const double _rangeToHit, const double _muzzleVelocity, const double _airFriction, const double _minElev, const double _maxElev, const bool _highArc) {
double actualDistance, lineElevation, lineTimeOfFlight;
std::tie(actualDistance, lineElevation, lineTimeOfFlight) = simulateFindSolution(_rangeToHit, 0, _muzzleVelocity, _airFriction, _minElev, _maxElev, _highArc);
if (lineTimeOfFlight < 0) {
return "";
}
double actualDistanceHeight, lineHeightElevation, lineHeightTimeOfFlight;
std::tie(actualDistanceHeight, lineHeightElevation, lineHeightTimeOfFlight) = simulateFindSolution(_rangeToHit, -100, _muzzleVelocity, _airFriction, _minElev, _maxElev, _highArc);
std::stringstream returnSS;
returnSS << "[\"";
writeNumber(returnSS, _rangeToHit, 0, 0);
returnSS << "\",\"";
writeNumber(returnSS, lineElevation * 3200.0 / M_PI, 0, 0);
returnSS << "\",\"";
if (lineHeightElevation > 0) {
const double drElevAdjust = lineHeightElevation - lineElevation;
const double drTofAdjust = lineHeightTimeOfFlight - lineTimeOfFlight;
writeNumber(returnSS, drElevAdjust * 3200.0 / M_PI, 0, 0);
returnSS << "\",\"";
writeNumber(returnSS, drTofAdjust, 0, 1);
} else {
// low angle shots won't be able to adjust down further
returnSS << "-\",\"-";
}
returnSS << "\",\"";
writeNumber(returnSS, lineTimeOfFlight, 0, ((lineTimeOfFlight < 99.945) ? 1 : 0)); // round TOF when high
returnSS << "\",\"";
if (_airFriction) {
// Calc corrections:
double xOffset, yOffset;
// Crosswind
std::tie(xOffset, std::ignore, std::ignore) = simulateShot(lineElevation, _muzzleVelocity, 0, 10, 0, 15, 1, _airFriction);
const double crosswindOffsetRad = atan2(xOffset, actualDistance) / 10;
// Headwind
std::tie(std::ignore, yOffset, std::ignore) = simulateShot(lineElevation, _muzzleVelocity, 0, 0, -10, 15, 1, _airFriction);
const double headwindOffset = (actualDistance - yOffset) / 10;
// Tailwind
std::tie(std::ignore, yOffset, std::ignore) = simulateShot(lineElevation, _muzzleVelocity, 0, 0, 10, 15, 1, _airFriction);
const double tailwindOffset = (actualDistance - yOffset) / 10;
// Air Temp Dec
std::tie(std::ignore, yOffset, std::ignore) = simulateShot(lineElevation, _muzzleVelocity, 0, 0, 0, 5, 1, _airFriction);
const double tempDecOffset = (actualDistance - yOffset) / 10;
// Air Temp Inc
std::tie(std::ignore, yOffset, std::ignore) = simulateShot(lineElevation, _muzzleVelocity, 0, 0, 0, 25, 1, _airFriction);
const double tempIncOffset = (actualDistance - yOffset) / 10;
// Air Density Dec
std::tie(std::ignore, yOffset, std::ignore) = simulateShot(lineElevation, _muzzleVelocity, 0, 0, 0, 15, 0.9, _airFriction);
const double airDensityDecOffset = (actualDistance - yOffset) / 10;
// Air Density Inc
std::tie(std::ignore, yOffset, std::ignore) = simulateShot(lineElevation, _muzzleVelocity, 0, 0, 0, 15, 1.1, _airFriction);
const double airDensityIncOffset = (actualDistance - yOffset) / 10;
writeNumber(returnSS, crosswindOffsetRad * 3200.0 / M_PI, 1, 1);
returnSS << "\",\"";
writeNumber(returnSS, headwindOffset, 1, (abs(headwindOffset) > 9.949) ? 0 : 1);
returnSS << "\",\"";
writeNumber(returnSS, tailwindOffset, 1, (abs(tailwindOffset) > 9.949) ? 0 : 1);
returnSS << "\",\"";
writeNumber(returnSS, tempDecOffset, 1, (abs(tempDecOffset) > 9.949) ? 0 : 1);
returnSS << "\",\"";
writeNumber(returnSS, tempIncOffset, 1, (abs(tempIncOffset) > 9.949) ? 0 : 1);
returnSS << "\",\"";
writeNumber(returnSS, airDensityDecOffset, 1, (abs(airDensityDecOffset) > 9.949) ? 0 : 1);
returnSS << "\",\"";
writeNumber(returnSS, airDensityIncOffset, 1, (abs(airDensityIncOffset) > 9.949) ? 0 : 1);
returnSS << "\"]";
} else {
returnSS << "-\",\"-\",\"-\",\"-\",\"-\",\"-\",\"-\"]"; // 7 dashes
}
return (returnSS.str());
}
void __stdcall RVExtensionVersion(char* output, int outputSize) {
strncpy(output, ACE_FULL_VERSION_STR, outputSize);
}
void __stdcall RVExtension(char* output, int outputSize, const char* function) {
if (!strcmp(function, "version")) {
RVExtensionVersion(output, outputSize);
return;
}
strncpy(output, "error - use args version of callExtension", outputSize);
}
int __stdcall RVExtensionArgs(char* output, int outputSize, const char* function, const char** args, int argsCnt) {
if (!strcmp(function, "version")) {
RVExtensionVersion(output, outputSize);
return 0;
}
if (!strcmp(function, "start")) {
if (argsCnt != 5) { return -2; } // Error: not enough args
const double muzzleVelocity = strtod(args[0], NULL);
const double airFriction = strtod(args[1], NULL);
double minElev = (M_PI / 180.0) * strtod(args[2], NULL);
double maxElev = (M_PI / 180.0) * strtod(args[3], NULL);
const bool highArc = !strcmp(args[4], "true");
// Reset workers:
fWorkers.clear();
getLineIndex = 0;
double bestAngle, bestDistance;
std::tie(bestAngle, bestDistance) = findMaxAngle(muzzleVelocity, airFriction);
minElev = std::max(minElev, 2 * (M_PI / 180.0)); // cap min to 2 degrees (negative elev might get messy)
maxElev = std::min(maxElev, 88 * (M_PI / 180.0)); // cap max to 88 degrees (mk6)
if (highArc) {
minElev = std::max(minElev, bestAngle);
} else {
maxElev = std::min(maxElev, bestAngle);
}
const double loopStart = (bestDistance < 4000) ? 50 : 100;
const double loopInc = (bestDistance < 5000) ? 50 : 100; // simplify when range gets high
const double loopMaxRange = std::min(bestDistance, 30000.0); // with no air resistance, max range could go higher than 60km
if (maxElev > minElev) { // don't bother if we can't hit anything (e.g. mortar in low mode)
for (double range = loopStart; range < loopMaxRange; range += loopInc) {
fWorkers.emplace_back(std::async(&simulateCalcRangeTableLine, range, muzzleVelocity, airFriction, minElev, maxElev, highArc));
}
}
std::stringstream outputStr; // debug max distance and thead count
outputStr << "[" << bestDistance << "," << fWorkers.size() << "]";
strncpy(output, outputStr.str().c_str(), outputSize);
return 0;
}
if (!strcmp(function, "getline")) {
// 1 = data on line, 2 - data not ready, 3 - done
std::string result = "";
std::future_status workerStatus;
while (result.empty()) {
if (getLineIndex >= fWorkers.size()) {
return 3;
}
workerStatus = fWorkers[getLineIndex].wait_for(std::chrono::seconds(0));
if (workerStatus != std::future_status::ready) {
return 2;
}
result = fWorkers[getLineIndex].get();
getLineIndex++;
}
strncpy(output, result.c_str(), outputSize);
return 1;
}
strncpy(output, "error - invalid function", outputSize);
return RETURN_INVALID_FUNCTION; // Error: function not valid
}

View File

@ -0,0 +1,22 @@
/*
* artillerytables.hpp
* Author: PabstMirror
*/
// ace libs:
#include "vector.hpp"
#include "shared.hpp"
#define RETURN_INVALID_FUNCTION -1001
#define RETURN_WRONG_ARG_COUNT -1002
extern "C" {
__declspec(dllexport) void __stdcall RVExtension(char* output, int outputSize, const char* function);
__declspec(dllexport) int __stdcall RVExtensionArgs(char* output, int outputSize, const char* function, const char** argv, int argc);
__declspec(dllexport) void __stdcall RVExtensionVersion(char* output, int outputSize);
}
std::tuple<double, double, double> simulateShot(const double _fireAngleRad, const double _muzzleVelocity, const double _heightOfTarget, const double _crossWind, const double _tailWind, const double _temperature, const double _airDensity, double _airFriction);
std::tuple<double, double> findMaxAngle(const double _muzzleVelocity, const double _airFriction);
std::tuple<double, double, double> simulateFindSolution(const double _rangeToHit, const double _heightToHit, const double _muzzleVelocity, const double _airFriction, const double _minElev, const double _maxElev, const bool _highArc);
void writeNumber(std::stringstream & ss, double _num, const int _widthInt, const int _widthDec);
std::string simulateCalcRangeTableLine(const double _rangeToHit, const double _muzzleVelocity, const double _airFriction, const double _minElev, const double _maxElev, const bool _highArc);

View File

@ -0,0 +1,100 @@
#include <iostream>
#include <chrono>
#include "gtest/gtest.h"
#include "../artillerytables.hpp"
extern "C" {
__declspec(dllexport) void __stdcall RVExtension(char* output, int outputSize, const char* function);
__declspec(dllexport) int __stdcall RVExtensionArgs(char* output, int outputSize, const char* function, const char** argv, int argc);
__declspec(dllexport) void __stdcall RVExtensionVersion(char* output, int outputSize);
}
namespace test_ace_artillerytables {
TEST(Extension, VersionOld) {
char output[256];
char function[] = "version";
RVExtension(output, 256, function);
std::cout << "VersionOld: " << output << "\n";
ASSERT_STREQ(output, ACE_FULL_VERSION_STR);
}
TEST(Extension, VersionRVExtensionVersion) {
char output[256];
RVExtensionVersion(output, 256);
std::cout << "VersionExtension: " << output << "\n";
ASSERT_STREQ(output, ACE_FULL_VERSION_STR);
}
TEST(Extension, VersionArray) {
char output[256];
char function[] = "version";
int extReturn = RVExtensionArgs(output, 256, function, NULL, 0);
std::cout << "VersionNew: " << output << "\n";
ASSERT_EQ(extReturn, 0);
ASSERT_STREQ(output, ACE_FULL_VERSION_STR);
}
TEST(Extension, InvalidFuncOld) {
char output[256];
char function[] = "blah";
RVExtension(output, 256, function);
ASSERT_STREQ(output, "error - use args version of callExtension");
}
TEST(Extension, InvalidFuncArray) {
char output[256];
char function[] = "blah";
int extReturn = RVExtensionArgs(output, 256, function, nullptr, 0);
std::cout << "InvalidFunc: " << output << "\n";
ASSERT_EQ(extReturn, RETURN_INVALID_FUNCTION);
ASSERT_STREQ(output, "error - invalid function");
}
TEST(Extension, TestRun) {
// very basic test that it runs the correct number of lines
char output[256];
// Start:
char function1[] = "start";
const char* args1[] = { "400", "-0.00005", "-5", "80", "true" };
auto t1 = std::chrono::high_resolution_clock::now();
int ret1 = RVExtensionArgs(output, 256, function1, args1, 5);
auto t2 = std::chrono::high_resolution_clock::now();
std::printf("ret: %d - %s\n", ret1, output);
std::printf("func %s: %1.1f ms\n", function1, std::chrono::duration<double, std::milli>(t2 - t1).count());
ASSERT_STREQ(output, "[10391.8,103]");
ASSERT_EQ(ret1, 0);
int lines = 0;
auto t3 = std::chrono::high_resolution_clock::now();
char function2[] = "getline";
int ret2 = 0;
while (ret2 != 3) { // dumb spin
ret2 = RVExtensionArgs(output, 256, function2, NULL, 0);
if (ret2 == 1) {
lines++;
// std::printf("ret: %d - %s\n", ret2, output);
}
}
auto t4 = std::chrono::high_resolution_clock::now();
std::printf("func %s: %1.1f ms with %d lines\n", function2, std::chrono::duration<double, std::milli>(t3 - t2).count(), lines);
std::printf("callExtensions finished in %1.1f ms\n", std::chrono::duration<double, std::milli>(t4 - t1).count());
ASSERT_EQ(lines, 69);
}
}
int main(int argc, char** argv) {
// Misc Testing code
// Determine realistic air firiction values
//double mv = 241;
//std::printf(" %f m/s\n", mv);
//double range;
//for (double ar = 0; ar > -0.00015; ar -= 0.00001) {
// std::tie(std::ignore, range) = findMaxAngle(mv, ar);
// printf("[%f] = %f\n", ar, range);
//}
::testing::InitGoogleTest(&argc, argv);
std::cout << "Starting tests!\n";
return RUN_ALL_TESTS();
}