mirror of
https://github.com/acemod/ACE3.git
synced 2024-08-30 18:23:18 +00:00
Initial
This commit is contained in:
parent
28620d86d1
commit
4120eca98b
BIN
ace_artillerytables_x64.dll
Normal file
BIN
ace_artillerytables_x64.dll
Normal file
Binary file not shown.
1
addons/artillerytables/$PBOPREFIX$
Normal file
1
addons/artillerytables/$PBOPREFIX$
Normal file
@ -0,0 +1 @@
|
||||
z\ace\addons\artillerytables
|
15
addons/artillerytables/CfgEventHandlers.hpp
Normal file
15
addons/artillerytables/CfgEventHandlers.hpp
Normal 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));
|
||||
};
|
||||
};
|
3
addons/artillerytables/CfgVehicles.hpp
Normal file
3
addons/artillerytables/CfgVehicles.hpp
Normal file
@ -0,0 +1,3 @@
|
||||
class CfgVehicles {
|
||||
|
||||
};
|
15
addons/artillerytables/CfgWeapons.hpp
Normal file
15
addons/artillerytables/CfgWeapons.hpp
Normal 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;
|
||||
};
|
||||
};
|
||||
};
|
14
addons/artillerytables/README.md
Normal file
14
addons/artillerytables/README.md
Normal file
@ -0,0 +1,14 @@
|
||||
ace_artillerytables
|
||||
==========
|
||||
|
||||
Universal artillertables.
|
||||
|
||||
#### Items Added:
|
||||
`ACE_artilleryTable`
|
||||
|
||||
|
||||
## Maintainers
|
||||
|
||||
The people responsible for merging changes to this component or answering potential questions.
|
||||
|
||||
- [PabstMirror](https://github.com/PabstMirror)
|
102
addons/artillerytables/RscRangeTable.hpp
Normal file
102
addons/artillerytables/RscRangeTable.hpp
Normal file
@ -0,0 +1,102 @@
|
||||
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 ChargeBackground: RscText {
|
||||
idc = -1;
|
||||
x = "14 *(safeZoneH / 40) + (safezoneX + (safezoneW - safeZoneH)/2)";
|
||||
y = "1 * ((safeZoneH / 1.2) / 25) + (safezoneY + (safezoneH - (safeZoneH / 1.2))/2)";
|
||||
w = "4 * (safeZoneH / 40)";
|
||||
h = "8 * ((safeZoneH / 1.2) / 25)";
|
||||
colorBackground[] = {0,0,0,0.9};
|
||||
};
|
||||
};
|
||||
class controls {
|
||||
class TheTable: RscListNBox {
|
||||
idc = IDC_TABLE;
|
||||
// style = ST_CENTER + ST_MULTI + LB_TEXTURES;
|
||||
// style = ST_LEFT + ST_MULTI + LB_TEXTURES;
|
||||
// style = LB_MULTI + ST_LEFT; // Style
|
||||
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 = 14124;
|
||||
idcRight = 412343243;
|
||||
colorText[] = {0, 0, 0, 1};
|
||||
shadow = "0";
|
||||
// colorBorder[] = {1,0,0,1};
|
||||
// colorBackground[] = {1, 0, 0, 1};
|
||||
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 = "14 *(safeZoneH / 40) + (safezoneX + (safezoneW - safeZoneH)/2)";
|
||||
y = "2 * ((safeZoneH / 1.2) / 25) + (safezoneY + (safezoneH - (safeZoneH / 1.2))/2)";
|
||||
w = "4 * (safeZoneH / 40)";
|
||||
h = "7 * ((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 = "14.1 *(safeZoneH / 40) + (safezoneX + (safezoneW - safeZoneH)/2)";
|
||||
y = "1.1 * ((safeZoneH / 1.2) / 25) + (safezoneY + (safezoneH - (safeZoneH / 1.2))/2)";
|
||||
w = "1.8 * (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 = "16.1 *(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";
|
||||
};
|
||||
};
|
||||
};
|
42
addons/artillerytables/RscTitles.hpp
Normal file
42
addons/artillerytables/RscTitles.hpp
Normal 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)";
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
};
|
||||
};
|
||||
};
|
BIN
addons/artillerytables/UI/RangeTable_background.paa
Normal file
BIN
addons/artillerytables/UI/RangeTable_background.paa
Normal file
Binary file not shown.
BIN
addons/artillerytables/UI/icon_rangeTable.paa
Normal file
BIN
addons/artillerytables/UI/icon_rangeTable.paa
Normal file
Binary file not shown.
8
addons/artillerytables/XEH_PREP.hpp
Normal file
8
addons/artillerytables/XEH_PREP.hpp
Normal file
@ -0,0 +1,8 @@
|
||||
TRACE_1("prep",_this);
|
||||
|
||||
PREP(firedEH);
|
||||
PREP(interactMenuOpened);
|
||||
PREP(rangeTableOpen);
|
||||
PREP(rangeTableUpdate);
|
||||
PREP(turretChanged);
|
||||
PREP(turretPFEH);
|
32
addons/artillerytables/XEH_postInit.sqf
Normal file
32
addons/artillerytables/XEH_postInit.sqf
Normal file
@ -0,0 +1,32 @@
|
||||
#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 {
|
||||
[{ // add a frame later to allow other modules to set variables
|
||||
["LandVehicle", "init", {
|
||||
params ["_vehicle"];
|
||||
private _enabled = _vehicle getVariable [QGVAR(enabled), getNumber (configFile >> "CfgVehicles" >> typeOf _vehicle >> "artilleryScanner")];
|
||||
if (_enabled in [0, false]) exitWith {};
|
||||
if (1 == getNumber (configFile >> "CfgVehicles" >> typeOf _vehicle >> QGVAR(skipCorrections))) exitWith {};
|
||||
|
||||
TRACE_3("enabled",_vehicle,typeOf _vehicle,_enabled);
|
||||
_vehicle addEventHandler ["Fired", {call FUNC(firedEH)}];
|
||||
}, true, [], true] call CBA_fnc_addClassEventHandler;
|
||||
}, []] call CBA_fnc_execNextFrame;
|
||||
};
|
||||
}] call CBA_fnc_addEventHandler;
|
||||
|
||||
#ifdef DEBUG_MODE_FULL
|
||||
#include "dev\showShotInfo.sqf"
|
||||
#endif
|
25
addons/artillerytables/XEH_preInit.sqf
Normal file
25
addons/artillerytables/XEH_preInit.sqf
Normal file
@ -0,0 +1,25 @@
|
||||
#include "script_component.hpp"
|
||||
|
||||
ADDON = false;
|
||||
|
||||
PREP_RECOMPILE_START;
|
||||
#include "XEH_PREP.hpp"
|
||||
PREP_RECOMPILE_END;
|
||||
|
||||
#include "initSettings.sqf"
|
||||
|
||||
DFUNC(rotateVector3d) = {
|
||||
params ["_vector", "_rotAxis", "_angle"];
|
||||
|
||||
_vector params ["_x", "_y", "_z"];
|
||||
(vectorNormalized _rotAxis) params ["_u", "_v", "_w"];
|
||||
|
||||
private _f = (_u*_x + _v*_y + _w*_z) * (1-cos(_angle));
|
||||
[
|
||||
_u*_f + _x*cos(_angle) + (_v*_z - _w*_y)*sin(_angle),
|
||||
_v*_f + _y*cos(_angle) + (_w*_x - _u*_z)*sin(_angle),
|
||||
_w*_f + _z*cos(_angle) + (_u*_y - _v*_x)*sin(_angle)
|
||||
]
|
||||
};
|
||||
|
||||
ADDON = true;
|
3
addons/artillerytables/XEH_preStart.sqf
Normal file
3
addons/artillerytables/XEH_preStart.sqf
Normal file
@ -0,0 +1,3 @@
|
||||
#include "script_component.hpp"
|
||||
|
||||
#include "XEH_PREP.hpp"
|
40
addons/artillerytables/config.cpp
Normal file
40
addons/artillerytables/config.cpp
Normal file
@ -0,0 +1,40 @@
|
||||
#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 {
|
||||
extensions[] += {"ace_artillerytables"};
|
||||
};
|
||||
|
||||
#include "CfgEventHandlers.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"
|
||||
|
43
addons/artillerytables/dev/showShotInfo.sqf
Normal file
43
addons/artillerytables/dev/showShotInfo.sqf
Normal file
@ -0,0 +1,43 @@
|
||||
// #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;};
|
||||
hintSilent format ["%1 m/s\nAz: %2 [%3]\nEl: %4 [%5]\nError: %6",_mag toFixed 1, _dir toFixed 2, ((6400 / 360) * _dir) toFixed 0, _elev toFixed 2, ((6400 / 360) * _elev) toFixed 0,
|
||||
_elev - (missionNamespace getVariable [QGVAR(prediction), 0])];
|
||||
TRACE_1("elev offset",_elev - GVAR(prediction));
|
||||
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;
|
88
addons/artillerytables/functions/fnc_firedEH.sqf
Normal file
88
addons/artillerytables/functions/fnc_firedEH.sqf
Normal 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 ((_vehicle turretUnit [0]) != _gunner) exitWith {};
|
||||
|
||||
|
||||
// Get airFriction
|
||||
private _airFriction = -0.00005;
|
||||
if (!isNull (configFile >> "CfgMagazines" >> _magazine >> QGVAR(airFriction))) then {
|
||||
_airFriction = getNumber (configFile >> "CfgMagazines" >> _magazine >> QGVAR(airFriction));
|
||||
};
|
||||
TRACE_1("",_airFriction);
|
||||
if (_airFriction == 0) exitWith {}; // 0 disables everything
|
||||
|
||||
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);
|
83
addons/artillerytables/functions/fnc_interactMenuOpened.sqf
Normal file
83
addons/artillerytables/functions/fnc_interactMenuOpened.sqf
Normal file
@ -0,0 +1,83 @@
|
||||
#include "script_component.hpp"
|
||||
/*
|
||||
* Author: PabstMirror
|
||||
* Interaction menu opened, search for nearby artillery vehicles.
|
||||
*
|
||||
* Arguments:
|
||||
* 0: Vehicle <OBJECT>
|
||||
* 1: Player <OBJECT>
|
||||
*
|
||||
* Return Value:
|
||||
* Can Open <BOOL>
|
||||
*
|
||||
* Example:
|
||||
* [bob, bob] 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("",_vehicleAdded,_rangeTablesShown);
|
||||
|
||||
{
|
||||
private _vehicle = _x;
|
||||
private _typeOf = typeOf _vehicle;
|
||||
private _enabled = _vehicle getVariable [QGVAR(enabled), getNumber (configFile >> "CfgVehicles" >> _typeOf >> "artilleryScanner")];
|
||||
TRACE_3("",_vehicle,_typeOf,_enabled);
|
||||
|
||||
if ((_enabled in [1,true]) && {!(_vehicle in _vehicleAdded)}) then {
|
||||
private _vehicle = _vehicle;
|
||||
private _weaponsTurret = _vehicle weaponsTurret [0];
|
||||
if ((count _weaponsTurret) != 1) exitWith { WARNING_1("multiple weapons - %1",_typeOf); };
|
||||
private _weapon = _weaponsTurret select 0;
|
||||
|
||||
private _weaponDir = _vehicle weaponDirection _weapon;
|
||||
private _turretRot = [vectorDir _vehicle, vectorUp _vehicle, (180 / PI) * (_vehicle animationPhase "mainTurret")] call FUNC(rotateVector3d);
|
||||
private _neutralX = acos (_turretRot vectorDotProduct _weaponDir) - (180 / PI) * (_vehicle animationPhase "mainGun");
|
||||
_neutralX = (round (_neutralX * 10)) / 10; // minimize floating point errors
|
||||
|
||||
private _turretCfg = [_typeOf, [0]] call CBA_fnc_getTurret;
|
||||
private _minElev = _neutralX + getNumber (_turretCfg >> "minElev");
|
||||
private _maxElev = _neutralX + getNumber (_turretCfg >> "maxElev");
|
||||
|
||||
private _advCorrection = GVAR(advancedCorrections) && {(getNumber (configFile >> "CfgVehicles" >> _typeOf >> QGVAR(skipCorrections))) != 1};
|
||||
|
||||
private _info = [_weapon, _minElev, _maxElev, _advCorrection]; // in case multiple vehicle types use the same weapon
|
||||
|
||||
_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);
|
||||
[{
|
||||
_this call FUNC(rangeTableOpen); // delay a frame because of interaction menu closing dialogs
|
||||
}, _info] call CBA_fnc_execNextFrame;
|
||||
};
|
||||
private _condition = {
|
||||
//IGNORE_PRIVATE_WARNING ["_player"];
|
||||
// ("ACE_artilleryTable" in (_player call EFUNC(common,uniqueItems)))
|
||||
// && {[_player, objNull, ["notOnMap", "isNotSitting"]] call EFUNC(common,canInteractWith)}
|
||||
true
|
||||
};
|
||||
private _displayName = format ["%1%2", getText (configFile >> "CfgVehicles" >> _typeOf >> "displayName"),["","*"] select _advCorrection];
|
||||
private _action = [format ['QGVAR(%1)',_index], _displayName, QPATHTOF(UI\icon_rangeTable.paa), _statement, _condition, {}, _info] call EFUNC(interact_menu,createAction);
|
||||
private _ret = [ace_player, 1, ["ACE_SelfActions", "ACE_Equipment"], _action] call EFUNC(interact_menu,addActionToObject);
|
||||
TRACE_1("added action",_ret);
|
||||
ace_player setVariable [QGVAR(rangeTablesShown), _rangeTablesShown];
|
||||
};
|
||||
};
|
||||
} forEach (nearestObjects [ace_player, ["LandVehicle"], 25]);
|
||||
|
||||
|
78
addons/artillerytables/functions/fnc_rangeTableOpen.sqf
Normal file
78
addons/artillerytables/functions/fnc_rangeTableOpen.sqf
Normal file
@ -0,0 +1,78 @@
|
||||
#include "script_component.hpp"
|
||||
/*
|
||||
* Author: PabstMirror
|
||||
* Opens the rangetable and fills the charge listbox.
|
||||
*
|
||||
* Arguments:
|
||||
* None
|
||||
*
|
||||
* Return Value:
|
||||
* None
|
||||
*
|
||||
* Example:
|
||||
* [] call ace_artillerytables_fnc_rangeTableOpen
|
||||
*
|
||||
* Public: No
|
||||
*/
|
||||
|
||||
params ["_weaponName", "_elevMin", "_elevMax", "_advCorrection"];
|
||||
TRACE_3("rangeTableOpen",_weaponName,_elevMin,_elevMax);
|
||||
|
||||
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 _firstSpeed = getNumber (_magCfg >> (_mags select 0) >> "initSpeed");
|
||||
private _allSame = true;
|
||||
_mags = _mags apply {
|
||||
private _initSpeed = getNumber (_magCfg >> _x >> "initSpeed");
|
||||
if (_initSpeed != _firstSpeed) then {_allSame = false};
|
||||
[getText (_magCfg >> _x >> "displayNameShort"), _initSpeed]
|
||||
};
|
||||
if (_allSame) then { _mags = [["-", _firstSpeed]]; };
|
||||
TRACE_2("",_allSame,_mags);
|
||||
|
||||
// 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);
|
||||
TRACE_2("",_allSameCharge,_fireModes);
|
||||
|
||||
private _index = 0;
|
||||
{
|
||||
_x params ["_xMagName", "_xMagInitSpeed"];
|
||||
if (_allSameCharge) then {
|
||||
_ctrlChargeList lbAdd format ["%1", _xMagName];
|
||||
_ctrlChargeList lbSetTooltip [_index, format ["%1 m/s",_xMagInitSpeed toFixed 1]];
|
||||
_ctrlChargeList lbSetData [_index, str (_xMagInitSpeed)];
|
||||
_index = _index + 1;
|
||||
} else {
|
||||
{
|
||||
_x params ["_xArtilleryCharge"];
|
||||
_ctrlChargeList lbAdd format ["%1 Charge: %2", _xMagName, _forEachIndex]; // forEachIndex is for firemodes
|
||||
_ctrlChargeList lbSetTooltip [_index, format ["%1 m/s", (_xMagInitSpeed * _xArtilleryCharge) toFixed 1]];
|
||||
_ctrlChargeList lbSetData [_index, str (_xMagInitSpeed * _xArtilleryCharge)];
|
||||
_index = _index + 1;
|
||||
} forEach _fireModes;
|
||||
};
|
||||
} forEach _mags;
|
||||
|
||||
if (_index == 0) exitWith {ERROR_1("no modes %1",_weaponName);};
|
||||
|
||||
if (isNil QGVAR(lastElevationMode)) then {GVAR(lastElevationMode) = true;};
|
||||
if (isNil QGVAR(lastCharge)) then {GVAR(lastCharge) = 0;};
|
||||
if ((GVAR(lastCharge) >= _index) || {GVAR(lastCharge) < 0}) then { GVAR(lastCharge) = 0; };
|
||||
|
||||
TRACE_2("",GVAR(lastElevationMode),GVAR(lastCharge));
|
||||
_ctrlChargeList lbSetCurSel GVAR(lastCharge); // triggers call to FUNC(rangeTableUpdate)
|
71
addons/artillerytables/functions/fnc_rangeTableUpdate.sqf
Normal file
71
addons/artillerytables/functions/fnc_rangeTableUpdate.sqf
Normal file
@ -0,0 +1,71 @@
|
||||
#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)];
|
||||
GVAR(lastCharge) = lbCurSel _ctrlChargeList;
|
||||
|
||||
private _listBoxData = _ctrlChargeList lbData GVAR(lastCharge);
|
||||
if (isNil "_listBoxData" || {_listBoxData == ""}) exitWith {ERROR("lbCurSel out of bounds or no data");};
|
||||
private _muzzleVelocity = parseNumber _listBoxData;
|
||||
|
||||
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));
|
||||
|
||||
private _advCorrection = _dialog getVariable [QGVAR(advCorrection), false];
|
||||
private _airFriction = if (_advCorrection) then {-0.00005} else {0};
|
||||
|
||||
TRACE_4("",_muzzleVelocity,_elevMin,_elevMax,_airFriction);
|
||||
|
||||
|
||||
lnbClear _ctrlRangeTable;
|
||||
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;
|
59
addons/artillerytables/functions/fnc_turretChanged.sqf
Normal file
59
addons/artillerytables/functions/fnc_turretChanged.sqf
Normal file
@ -0,0 +1,59 @@
|
||||
#include "script_component.hpp"
|
||||
/*
|
||||
* Author: PabstMirror
|
||||
* Turret changed.
|
||||
*
|
||||
* Arguments:
|
||||
* 0: Player <OBJECT>
|
||||
* 1: Turret <ARRAY>
|
||||
*
|
||||
* Return Value:
|
||||
* Nothing
|
||||
*
|
||||
* Example:
|
||||
* [player] call ace_artillerytables_fnc_turretChanged
|
||||
*
|
||||
* Public: No
|
||||
*/
|
||||
|
||||
params ["_player", "_turret"];
|
||||
private _vehicle = vehicle _player;
|
||||
private _typeOf = typeOf _vehicle;
|
||||
TRACE_3("turretEH",_player,_turret,_typeOf);
|
||||
|
||||
private _enabled = (alive _player) && {_turret isEqualTo [0]}
|
||||
&& { ((_vehicle getVariable [QGVAR(enabled), getNumber (configFile >> "CfgVehicles" >> _typeOf >> "artilleryScanner")]) in [1, true])};
|
||||
|
||||
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 (_enabled) then {
|
||||
// Ugh, see FUNC(turretPFEH)
|
||||
private _useAltElevation = (["Mortar_01_base_F", "rhs_2b14_82mm_Base", "RHS_M252_Base", "CUP_B_M1129_MC_MK19_Desert"] findIf {_typeOf isKindOf _x}) > -1;
|
||||
|
||||
private _weaponsTurret = _vehicle weaponsTurret _turret;
|
||||
if ((count _weaponsTurret) != 1) then { WARNING_1("multiple weapons - %1",typeOf _vehicle); };
|
||||
private _weapon = _weaponsTurret param [0, ""];
|
||||
|
||||
#ifdef DEBUG_MODE_FULL
|
||||
private _ballisticsComputer = getNumber (configFile >> "CfgWeapons" >> _weapon >> "ballisticsComputer");
|
||||
private _elevationMode = getNumber (([_typeOf, _turret] call CBA_fnc_getTurret) >> "elevationMode");
|
||||
private _discreteDistance = getArray (([_typeOf, _turret] call CBA_fnc_getTurret) >> "discreteDistance");
|
||||
TRACE_4("",_weapon,_ballisticsComputer,_elevationMode,_discreteDistance);
|
||||
#endif
|
||||
|
||||
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]] call CBA_fnc_addPerFrameHandler;
|
||||
TRACE_1("added pfEH",GVAR(pfID));
|
||||
};
|
91
addons/artillerytables/functions/fnc_turretPFEH.sqf
Normal file
91
addons/artillerytables/functions/fnc_turretPFEH.sqf
Normal file
@ -0,0 +1,91 @@
|
||||
#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 ["_mortarVeh", "_turret", "_fireModes", "_useAltElevation"];
|
||||
|
||||
|
||||
if (shownArtilleryComputer && {GVAR(disableArtilleryComputer)}) then {
|
||||
// Still Don't like this solution, but it works
|
||||
closeDialog 0;
|
||||
[parseText "Computer Disabled"] 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 [_mortarVeh, _turret]) select 2;
|
||||
private _currentChargeMode = _fireModes find _currentFireMode;
|
||||
|
||||
private _lookVector = (AGLtoASL (positionCameraToWorld [0,0,0])) vectorFromTo (AGLtoASL (positionCameraToWorld [0,0,1]));
|
||||
private _weaponDir = _mortarVeh weaponDirection (currentWeapon _mortarVeh);
|
||||
|
||||
// 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)) || {(_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 _testPoint = _testSeekerPosASL vectorAdd (_lookVector vectorMultiply viewDistance);
|
||||
!((terrainIntersectASL [_testSeekerPosASL, _testPoint]) || {lineIntersects [_testSeekerPosASL, _testPoint, _mortarVeh]});
|
||||
} else {
|
||||
((ctrlText (_display displayCtrl 173)) == "--")
|
||||
};
|
||||
|
||||
private _realElevation = asin (_weaponDir select 2);
|
||||
private _realAzimuth = if (_useRealWeaponDir) then {
|
||||
// No range (looking at sky), it will follow weaponDir:
|
||||
(_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)
|
||||
if (_useAltElevation) then {
|
||||
// Some mortars have odd launch angles... still not sure why
|
||||
// This gets very close, (should be less than 0.5deg deviation on a 20deg slope)
|
||||
private _turretRot = [vectorDir _mortarVeh, vectorUp _mortarVeh, (180 / PI) * (_mortarVeh animationPhase "mainTurret")] call FUNC(rotateVector3d);
|
||||
_realElevation = acos (_turretRot vectorDotProduct _weaponDir) + ((_turretRot call CBA_fnc_vect2polar) select 2);
|
||||
if (_realElevation > 90) then { _realElevation = 180 - _realElevation; }; // does not flip azimuth!
|
||||
};
|
||||
((_lookVector select 0) atan2 (_lookVector select 1))
|
||||
};
|
||||
if (_realAzimuth < 0) then { _realAzimuth = _realAzimuth + 360; };
|
||||
|
||||
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];
|
||||
|
||||
END_COUNTER(pfeh);
|
||||
|
||||
#ifdef DEBUG_MODE_FULL
|
||||
// private _lookVector = (AGLtoASL (positionCameraToWorld [0,0,0])) vectorFromTo (AGLtoASL (positionCameraToWorld [0,0,10]));
|
||||
// systemChat format ["AZ: %1 EL: %2", _realAzimuth toFixed 1, _realElevation toFixed 1];
|
||||
// systemChat format ["Slope: %1 - Look: %2 [%3]", (acos ((vectorUp _mortarVeh) select 2)) toFixed 1, ((_lookVector select 0) atan2 (_lookVector select 1)), ["GND","SKY"] select _useRealWeaponDir];
|
||||
GVAR(prediction) = _realElevation;
|
||||
#endif
|
1
addons/artillerytables/functions/script_component.hpp
Normal file
1
addons/artillerytables/functions/script_component.hpp
Normal file
@ -0,0 +1 @@
|
||||
#include "\z\ace\addons\artillerytables\script_component.hpp"
|
21
addons/artillerytables/initSettings.sqf
Normal file
21
addons/artillerytables/initSettings.sqf
Normal file
@ -0,0 +1,21 @@
|
||||
// CBA Settings [ADDON: ace_artillerytables]:
|
||||
|
||||
[
|
||||
QGVAR(advancedCorrections), "CHECKBOX",
|
||||
[LSTRING(advancedCorrections_displayName), LSTRING(advancedCorrections_description)],
|
||||
"ACE Artillery",
|
||||
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)],
|
||||
"ACE Artillery",
|
||||
false, // default value
|
||||
true, // isGlobal
|
||||
{[QGVAR(disableArtilleryComputer), _this] call EFUNC(common,cbaSettings_settingChanged)},
|
||||
false // Needs mission restart
|
||||
] call CBA_settings_fnc_init;
|
21
addons/artillerytables/script_component.hpp
Normal file
21
addons/artillerytables/script_component.hpp
Normal file
@ -0,0 +1,21 @@
|
||||
#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"
|
||||
|
||||
#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
|
49
addons/artillerytables/stringtable.xml
Normal file
49
addons/artillerytables/stringtable.xml
Normal file
@ -0,0 +1,49 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project name="ACE">
|
||||
<Package name="Mk6Mortar">
|
||||
<Key ID="STR_ACE_artillerytables_rangetable_displayName">
|
||||
<English>Artillery Rangetable</English>
|
||||
</Key>
|
||||
<Key ID="STR_ACE_artillerytables_rangetable_descriptionn">
|
||||
<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>Disable Artillery Computer</English>
|
||||
</Key>
|
||||
<Key ID="STR_ACE_artillerytables_disableArtilleryComputer_description">
|
||||
<English>Disable the vanilla artillery computers</English>
|
||||
</Key>
|
||||
</Package>
|
||||
</Project>
|
@ -89,8 +89,8 @@ endif()
|
||||
|
||||
string(TIMESTAMP ACE_BUILDSTAMP "%Y-%m-%dT%H:%M:%SZ")
|
||||
set(ACE_VERSION_MAJOR 3)
|
||||
set(ACE_VERSION_MINOR 12)
|
||||
set(ACE_VERSION_REVISION 3)
|
||||
set(ACE_VERSION_MINOR 15)
|
||||
set(ACE_VERSION_REVISION 6)
|
||||
EXECUTE_PROCESS(COMMAND git rev-parse --verify HEAD
|
||||
OUTPUT_VARIABLE T_ACE_VERSION_BUILD
|
||||
OUTPUT_STRIP_TRAILING_WHITESPACE
|
||||
@ -127,6 +127,7 @@ add_subdirectory(clipboard)
|
||||
add_subdirectory(advanced_ballistics)
|
||||
add_subdirectory(medical)
|
||||
add_subdirectory(parse_imagepath)
|
||||
add_subdirectory(artillerytables)
|
||||
|
||||
# Test Extension for dynamically loading/unloading built extensions; does not build in release
|
||||
if (DEVEL)
|
||||
|
12
extensions/artillerytables/CMakeLists.txt
Normal file
12
extensions/artillerytables/CMakeLists.txt
Normal file
@ -0,0 +1,12 @@
|
||||
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()
|
360
extensions/artillerytables/artillerytables.cpp
Normal file
360
extensions/artillerytables/artillerytables.cpp
Normal file
@ -0,0 +1,360 @@
|
||||
/*
|
||||
* ace_artillerytables.cpp
|
||||
* Author: PabstMirror
|
||||
*/
|
||||
|
||||
//#define TEST_EXE
|
||||
|
||||
#define _USE_MATH_DEFINES
|
||||
#include <cmath>
|
||||
#include <vector>
|
||||
#include <string>
|
||||
#include <sstream>
|
||||
#include <iostream>
|
||||
#include <iomanip>
|
||||
#include <fstream>
|
||||
#include <tuple>
|
||||
#include <algorithm>
|
||||
#include <chrono>
|
||||
#include <future>
|
||||
|
||||
// ace libs:
|
||||
#include "vector.hpp"
|
||||
|
||||
#ifndef TEST_EXE
|
||||
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);
|
||||
}
|
||||
#endif
|
||||
|
||||
// Constants
|
||||
const double timeStep = 1.0 / 100;
|
||||
const double rangeSearchErrorMax = 0.001; // ratio * distance
|
||||
const double rangeSearchAngleConvergance = 0.00001;
|
||||
const double gravityABS = 9.8066;
|
||||
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) {
|
||||
double kCoefficient = -1.0 * _airDensity * _airFriction;
|
||||
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));
|
||||
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;
|
||||
}
|
||||
|
||||
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) {
|
||||
if (_airFriction == 0) {
|
||||
return { M_PI_4, _muzzleVelocity * _muzzleVelocity / gravityABS };
|
||||
}
|
||||
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 };
|
||||
};
|
||||
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) {
|
||||
auto [actualDistance, lineElevation, lineTimeOfFlight] = simulateFindSolution(_rangeToHit, 0, _muzzleVelocity, _airFriction, _minElev, _maxElev, _highArc);
|
||||
if (lineTimeOfFlight < 0) {
|
||||
return "";
|
||||
}
|
||||
auto [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) {
|
||||
double drElevAdjust = lineHeightElevation - lineElevation;
|
||||
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);
|
||||
double crosswindOffsetRad = atan2(xOffset, actualDistance) / 10;
|
||||
// Headwind
|
||||
std::tie(std::ignore, yOffset, std::ignore) = simulateShot(lineElevation, _muzzleVelocity, 0, 0, -10, 15, 1, _airFriction);
|
||||
double headwindOffset = (actualDistance - yOffset) / 10;
|
||||
// Tailwind
|
||||
std::tie(std::ignore, yOffset, std::ignore) = simulateShot(lineElevation, _muzzleVelocity, 0, 0, 10, 15, 1, _airFriction);
|
||||
double tailwindOffset = (actualDistance - yOffset) / 10;
|
||||
// Air Temp Dec
|
||||
std::tie(std::ignore, yOffset, std::ignore) = simulateShot(lineElevation, _muzzleVelocity, 0, 0, 0, 5, 1, _airFriction);
|
||||
double tempDecOffset = (actualDistance - yOffset) / 10;
|
||||
// Air Temp Inc
|
||||
std::tie(std::ignore, yOffset, std::ignore) = simulateShot(lineElevation, _muzzleVelocity, 0, 0, 0, 25, 1, _airFriction);
|
||||
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);
|
||||
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);
|
||||
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());
|
||||
}
|
||||
|
||||
#ifndef ACE_FULL_VERSION_STR
|
||||
#define ACE_FULL_VERSION_STR "not defined"
|
||||
#endif
|
||||
void RVExtensionVersion(char* output, int outputSize) {
|
||||
strncpy_s(output, outputSize, ACE_FULL_VERSION_STR, _TRUNCATE);
|
||||
}
|
||||
void RVExtension(char* output, int outputSize, const char* function) {
|
||||
if (!strcmp(function, "version")) {
|
||||
RVExtensionVersion(output, outputSize);
|
||||
return;
|
||||
}
|
||||
strncpy_s(output, outputSize, "error", _TRUNCATE);
|
||||
}
|
||||
int 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
|
||||
double muzzleVelocity = strtod(args[0], NULL);
|
||||
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);
|
||||
bool highArc = !strcmp(args[4], "true");
|
||||
|
||||
// Reset workers:
|
||||
fWorkers.clear();
|
||||
getLineIndex = 0;
|
||||
|
||||
auto [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);
|
||||
}
|
||||
double loopStart = (bestDistance < 4000) ? 50 : 100;
|
||||
double loopInc = (bestDistance < 5000) ? 50 : 100; // simplify when range gets high
|
||||
double loopMaxRange = std::min(bestDistance, 25000.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_s(output, outputSize, outputStr.str().c_str(), _TRUNCATE);
|
||||
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_s(output, outputSize, result.c_str(), _TRUNCATE);
|
||||
return 1;
|
||||
}
|
||||
|
||||
return -1; // Error: function not valid
|
||||
}
|
||||
|
||||
|
||||
#ifdef TEST_EXE
|
||||
int main() {
|
||||
|
||||
//double a, b;
|
||||
//std::tie(a, b) = simulateFindSolution(200,50, 100, 0, 0, 45 * (M_PI / 180.0), false);
|
||||
//printf("sim: %f, %f\n",a,b);
|
||||
|
||||
//std::string r = simulateCalcRangeTableLine(4000, 810, );
|
||||
//printf("result: [%s]\n", r.c_str());
|
||||
|
||||
//auto [lineElevation, lineTimeOfFlight] = simulateFindSolution(4000, 0, 810, -0.00005, 5 * (M_PI / 180.0), 80 * (M_PI / 180.0), false);
|
||||
//printf("result: [%f, %f]\n", lineElevation, lineTimeOfFlight);
|
||||
|
||||
|
||||
// Determine realistic air firiction values
|
||||
/*
|
||||
double mv = 241;
|
||||
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);
|
||||
}
|
||||
*/
|
||||
/*
|
||||
// test callExtension
|
||||
char output[256];
|
||||
char function1[] = "start";
|
||||
//const char* args1[] = { "200", "0", "-5", "80", "false" };
|
||||
//const char* args1[] = { "153.9", "-0.00005", "-5", "80", "false" };
|
||||
const char* args1[] = { "810", "-0.00005", "-5", "80", "false" };
|
||||
//const char* args1[] = { "810", "0", "-5", "80", "true" };
|
||||
auto t1 = std::chrono::high_resolution_clock::now();
|
||||
int ret = RVExtensionArgs(output, 256, function1, args1, 5);
|
||||
auto t2 = std::chrono::high_resolution_clock::now();
|
||||
std::printf("ret: %d - %s\n", ret, output);
|
||||
std::printf("func %s: %1.1f ms\n", function1, std::chrono::duration<double, std::milli>(t2 - t1).count());
|
||||
|
||||
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>(t4 - t3).count(), lines);
|
||||
|
||||
std::printf("callExtensions finished in %1.1f ms\n", std::chrono::duration<double, std::milli>(t4 - t1).count());
|
||||
*/
|
||||
|
||||
}
|
||||
#endif
|
Loading…
Reference in New Issue
Block a user