XM157 (NGSW-FC Smart Scope) Framework (#8897)

* XM157 (NGSW-FC Smart Scope)

* Update addons/common/functions/fnc_rscObjectHelper.sqf

Co-authored-by: Jouni Järvinen <rautamiekka@users.noreply.github.com>

* Update addons/xm157/functions/fnc_weaponInfo_draw.sqf

Co-authored-by: Jouni Järvinen <rautamiekka@users.noreply.github.com>

* Headers

* Handle prone-deploy weapon bank

* Disable Reticle picker for now

* some localizations

* Change font

looks like CUP modifies EtelkaMonospaceProBold for some reason??

* Create xm157-framework.md

* Formating

Co-authored-by: Jouni Järvinen <rautamiekka@users.noreply.github.com>
This commit is contained in:
PabstMirror 2022-09-01 13:44:19 -05:00 committed by GitHub
parent 77cf716e8b
commit cc3bad3c56
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
29 changed files with 1131 additions and 0 deletions

View File

@ -143,6 +143,7 @@ PREP(removeSpecificMagazine);
PREP(requestCallback);
PREP(resetAllDefaults);
PREP(restoreVariablesJIP);
PREP(rscObjectHelper);
PREP(runAfterSettingsInit);
PREP(runTests);
PREP(sanitizeString);

View File

@ -0,0 +1,64 @@
#include "script_component.hpp"
/*
* Author: PabstMirror
* Convert between screen and 3d object coordinates
*
* Arguments:
* 0: Function <STRING>
* 1: Input array <CODE>
* 2: Scale (optional: 1) <NUMBER>
*
* Return Value:
* Value <ARRAY>
*
* Example:
* ["2d", [0,0,0], 1] call ace_common_fnc_rscObjectHelper
*
* Public: Maybe
*/
params ["_func", "_array", ["_scale", 1]];
private _adjustCam = 1;
private _topFOV = getResolution # 6;
private _leftFOV = getResolution # 7;
private _topLeftX = (_leftFOV-1)*0.5/_leftFOV;
private _bottomRightX = 1-_topLeftX;
private _topLeftY = 0;
private _bottomRightY = 1;
private _return = [];
switch (toLower _func) do {
case ("2d"): {
_array params ["_pointX", "_z", "_pointY"];
private _scrX = _pointX * (_bottomRightX - _topLeftX) + _topLeftX;
private _vX = _leftFOV * (_scrX - 0.5) * _adjustCam * _z;
private _scrY = _pointY * (_bottomRightY - _topLeftY) + _topLeftY;
private _vY = _topFOV * (0.5 - _scrY) * _adjustCam * _z;
_vX = _vX / _scale;
_vY = _vY / _scale;
_return = [_vX, _vY, _z];
};
case ("3d"): {
_array params ["_vX", "_vY", "_z"]; // z is distance from screen
_vX = _vX * _scale;
_vY = _vY * _scale;
private _scrX = _vX / (_leftFOV * _adjustCam * _z) + 0.5;
private _pointX = (_scrX - _topLeftX) / (_bottomRightX - _topLeftX);
private _scrY = 0.5 - _vY / (_topFOV * _adjustCam * _z);
private _pointY = (_scrY - _topLeftY) / (_bottomRightY - _topLeftY);
_return = [_pointX, _z, _pointY];
};
};
_return

1
addons/xm157/$PBOPREFIX$ Normal file
View File

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

View File

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

View File

@ -0,0 +1,6 @@
class CfgSounds {
class GVAR(click) {
sound[] = {QPATHTOF(sounds\click.wav), db-30, 3};
titles[] = {};
};
};

View File

@ -0,0 +1,48 @@
class CfgWeapons {
class ItemCore;
class InventoryOpticsItem_Base_F;
class ace_xm157_prototype: ItemCore {
author = ECSTRING(common,ACETeam);
scope = 1; // hidden
displayName = "XM157 Prototype";
descriptionShort = "";
picture = "\a3\Weapons_F\acc\Data\UI\icon_optic_tws_ca.paa";
model = "\A3\weapons_f\acc\acco_tws_F";
inertia = 0.3;
class CBA_ScriptedOptic {
bodyTexture = QPATHTOF(data\ace_vector_body_co.paa);
// bodyTextureNight = ".paa"; // optional
bodyTextureSize = 1;
hideMagnification = 1; // no point, and it flickers at 1x
disableTilt = 0;
};
weaponInfoType = QGVAR(info);
class ItemInfo: InventoryOpticsItem_Base_F {
mass = 14;
optics = 1;
modelOptics = "\x\cba\addons\optics\cba_optic_big_100.p3d";
class OpticsModes {
class lpvo {
opticsID = 1;
useModelOptics = 1;
opticsPPEffects[] = { "OpticsCHAbera1", "OpticsBlur1" };
opticsZoomMin = "8 call (uiNamespace getVariable 'cba_optics_fnc_setOpticMagnificationHelper')";
opticsZoomMax = "1 call (uiNamespace getVariable 'cba_optics_fnc_setOpticMagnificationHelper')";
opticsZoomInit = "1 call (uiNamespace getVariable 'cba_optics_fnc_setOpticMagnificationHelper')";
discreteDistance[] = {100};
discreteDistanceInitIndex = 0;
distanceZoomMin = 100;
distanceZoomMax = 100;
memoryPointCamera = "opticView";
visionMode[] = {"Normal"};
opticsFlare = 1;
opticsDisablePeripherialVision = 1;
cameraDir = "";
};
};
};
};
};

2
addons/xm157/README.md Normal file
View File

@ -0,0 +1,2 @@
ace_xm157
==========

View File

@ -0,0 +1,79 @@
class RscObject;
class RscControlsGroupNoScrollbars;
class RscText;
class GVAR(text): RscText {
font = "EtelkaMonospacePro";
SizeEx = 0.04;
colorText[]={1,0.1,0.05,0.95};
shadow = 0;
};
class GVAR(textMenu): GVAR(text) {
SizeEx = 0.09;
style = 2+16;
};
class RscInGameUI {
class CBA_ScriptedOptic_zooming;
class GVAR(info): CBA_ScriptedOptic_zooming {
onLoad = QUOTE(call FUNC(weaponInfo_onLoad));
class objects {
class Optic: RscObject { // first focal plane
idc = IDC_SCOPE_OBJECT;
type = 82;
model = "\A3\Misc_F\Helpers\UserTexture1m.p3d";
x = 0;
y = 0;
z = 0;
xBack = 0.9;
yBack = 0.9;
zBack = 0.3;
inBack = 0;
enableZoom = 1;
zoomDuration = 0.001;
class Areas {
class usertexture {
selection = "usertexture";
class controls {
class test: RscControlsGroupNoScrollbars {
idc = IDC_SCOPE_GROUP;
x = 0;
y = 0;
w = 1;
h = 4/3;
};
};
};
};
};
class Screen: RscObject {
idc = IDC_SCREEN_OBJECT;
type = 82;
model = "\A3\Misc_F\Helpers\UserTexture1m.p3d";
x = 0;
y = 0;
z = 0;
xBack = 0.9;
yBack = 0.9;
zBack = 0.3;
inBack = 1;
enableZoom = 1;
zoomDuration = 0.001;
class Areas {
class usertexture {
selection = "usertexture";
class controls {
class test: RscControlsGroupNoScrollbars {
idc = IDC_SCREEN_GROUP;
x = 0;
y = 0;
w = 1;
h = 4/3;
};
};
};
};
};
};
};
};

View File

@ -0,0 +1,8 @@
LOG("prep");
PREP(ballistics_calculator);
PREP(ballistics_getData);
PREP(keyPress);
PREP(weaponInfo_draw);
PREP(weaponInfo_drawMenu);
PREP(weaponInfo_onLoad);

View File

@ -0,0 +1,66 @@
#include "script_component.hpp"
#include "\a3\ui_f\hpp\defineDIKCodes.inc"
GVAR(shown) = false;
GVAR(data) = createHashMap;
([worldName] call EFUNC(common,getMapData)) params ["_latitude"];
GVAR(data) set ["latitude", _latitude];
// Add Keybinds
["ACE3 Equipment", QGVAR(range), [format ["XM157 - %1", localize "str_a3_rscdisplayarsenal_stat_range"]], {
["range", true] call FUNC(keyPress);
}, {
["range", false] call FUNC(keyPress);
}, [DIK_TAB, [false, false, false]], false, 0] call CBA_fnc_addKeybind;
["ACE3 Equipment", QGVAR(left), [format ["XM157 - %1", localize "str_a3_left"]], {
["right", true] call FUNC(keyPress);
}, {
["right", false] call FUNC(keyPress);
}, [DIK_END, [false, false, false]], false, 0] call CBA_fnc_addKeybind;
["ACE3 Equipment", QGVAR(right), [format ["XM157 - %1", localize "str_a3_right"]], {
["left", true] call FUNC(keyPress);
}, {
["left", false] call FUNC(keyPress);
}, [DIK_DELETE, [false, false, false]], false, 0] call CBA_fnc_addKeybind;
["ACE3 Equipment", QGVAR(up), [format ["XM157 - %1", localize "str_a3_rscattributetargetstate_up"]], {
["up", true] call FUNC(keyPress);
}, {
["up", false] call FUNC(keyPress);
}, [DIK_PGUP, [false, false, false]], false, 0] call CBA_fnc_addKeybind;
["ACE3 Equipment", QGVAR(down), [format ["XM157 - %1", localize "str_a3_rscattributetargetstate_down"]], {
["down", true] call FUNC(keyPress);
}, {
["down", false] call FUNC(keyPress);
}, [DIK_PGDN, [false, false, false]], false, 0] call CBA_fnc_addKeybind;
#ifdef ENABLE_QUICK_TESTING
player addPrimaryWeaponItem "ace_xm157_prototype";
[player] call CBA_fnc_addUnitTrackProjectiles;
player addItem "ACE_ATragMX";
player addItem "ace_rangecard";
["recompile", "recompile", "recompile", {
private _start = diag_tickTime;
[] call ACE_PREP_RECOMPILE;
[] call ace_common_fnc_dumpPerformanceCounters;
private _end = diag_tickTime;
systemChat format ["recompile took [%1 ms]", (1000 * (_end - _start)) toFixed 1];
if (productVersion #4 == "Diag") then {
call compile "diag_mergeConfigFile ['P:\z\ace\addons\xm157\config.cpp']";
};
private _windSpd = vectorMagnitude wind;
private _windDir = (wind select 0) atan2 (wind select 1);
systemChat format ["Wind %1 @ %2", _windSpd, _windDir + 180];
false
}, {false}, [0x21, [false, false, false]], false] call CBA_fnc_addKeybind; // F Key
#endif

View File

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

View File

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

27
addons/xm157/config.cpp Normal file
View File

@ -0,0 +1,27 @@
#include "script_component.hpp"
class CfgPatches {
class ADDON {
name = COMPONENT_NAME;
units[] = {};
weapons[] = {"ace_xm157_prototype"};
requiredVersion = REQUIRED_VERSION;
requiredAddons[] = {"ace_advanced_ballistics", "ace_scopes"};
author = ECSTRING(common,ACETeam);
authors[] = {"PabstMirror"};
url = ECSTRING(main,URL);
VERSION_CONFIG;
};
};
#include "CfgEventHandlers.hpp"
#include "CfgSounds.hpp"
#include "CfgWeapons.hpp"
#include "RscInGameUI.hpp"
class asdg_OpticRail;
class asdg_OpticRail1913: asdg_OpticRail {
class compatibleItems {
ace_xm157_prototype = 1;
};
};

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@ -0,0 +1,178 @@
from PIL import Image, ImageDraw, ImageFont, ImageFilter
import math
# based on vortex razor hd
final_scale = 10
supersample = 4
# supersampling helps make rounded circles, does slighly blur text and straight lines
pixels_per_mrad = supersample * final_scale
image_size = supersample * 4096
image_center = image_size / 2
# create an image
out = Image.new("RGBA", (image_size, image_size), (255, 255, 255, 0))
# get a drawing context
d = ImageDraw.Draw(out)
line_thin = math.floor(0.07 * pixels_per_mrad)
line_thick = math.floor(0.15 * pixels_per_mrad)
font_size = math.floor(0.55 * pixels_per_mrad)
# source https://fonts.google.com/specimen/Electrolize (Open Font License)
fnt = ImageFont.truetype("Electrolize-Regular.ttf", font_size)
# draw center dot
d.ellipse([
(image_center - 0.1 * pixels_per_mrad, image_center - 0.1 * pixels_per_mrad),
(image_center + 0.1 * pixels_per_mrad, image_center + 0.1 * pixels_per_mrad)
], fill=(0, 0, 0))
# draw 3 main axis lines
d.line([
(image_center - 8 * pixels_per_mrad, image_center),
(image_center - 0.5 * pixels_per_mrad, image_center)
], fill=(0, 0, 0), width=line_thin)
d.line([
(0, image_center),
(image_center - 8.5 * pixels_per_mrad, image_center)
], fill=(0, 0, 0), width=line_thick)
d.line([
(image_center + 0.5 * pixels_per_mrad, image_center),
(image_center + 8 * pixels_per_mrad, image_center)
], fill=(0, 0, 0), width=line_thin)
d.line([
(image_center + 8.5 * pixels_per_mrad, image_center),
(image_size, image_center)
], fill=(0, 0, 0), width=line_thick)
d.line([
(image_center, image_center + 0.5 * pixels_per_mrad),
(image_center, image_center + 11 * pixels_per_mrad)
], fill=(0, 0, 0), width=line_thin)
d.line([
(image_center, image_center + 11.5 * pixels_per_mrad),
(image_center, image_size)
], fill=(0, 0, 0), width=line_thick)
# draw big triangle bar things
if (image_center - 15 * pixels_per_mrad > 0):
d.polygon([
(0, image_center + 2 * pixels_per_mrad),
(image_center - 20 * pixels_per_mrad, image_center + 2 * pixels_per_mrad),
(image_center - 15 * pixels_per_mrad, image_center),
(image_center - 20 * pixels_per_mrad, image_center - 2 * pixels_per_mrad),
(0, image_center - 2 * pixels_per_mrad),
], fill=(0, 0, 0))
d.polygon([
(image_size, image_center + 2 * pixels_per_mrad),
(image_center + 20 * pixels_per_mrad, image_center + 2 * pixels_per_mrad),
(image_center + 15 * pixels_per_mrad, image_center),
(image_center + 20 * pixels_per_mrad, image_center - 2 * pixels_per_mrad),
(image_size, image_center - 2 * pixels_per_mrad),
], fill=(0, 0, 0))
d.polygon([
(image_center - 2 * pixels_per_mrad, image_size),
(image_center - 2 * pixels_per_mrad, image_center + 20 * pixels_per_mrad),
(image_center, image_center + 15 * pixels_per_mrad),
(image_center + 2 * pixels_per_mrad, image_center + 20 * pixels_per_mrad),
(image_center + 2 * pixels_per_mrad, image_size),
], fill=(0, 0, 0))
# draw windage hash marks and text
for x in range(1, 9):
if (x % 2 == 0):
text = f"{abs(x)}"
# windage odd numbers
d.text((image_center + (x - 0.15) * pixels_per_mrad, image_center - 1.2 * pixels_per_mrad), text, font=fnt, fill=(0, 0, 0))
d.text((image_center - (x + 0.15) * pixels_per_mrad, image_center - 1.2 * pixels_per_mrad), text, font=fnt, fill=(0, 0, 0))
# windage mrad hashs
d.line([
(image_center + x * pixels_per_mrad, image_center + line_thin / 2),
(image_center + x * pixels_per_mrad, image_center - 0.5 * pixels_per_mrad)
], fill=(0, 0, 0), width=line_thin)
d.line([
(image_center - x * pixels_per_mrad, image_center + line_thin / 2),
(image_center - x * pixels_per_mrad, image_center - 0.5 * pixels_per_mrad)
], fill=(0, 0, 0), width=line_thin)
if (x % 2 == 0):
d.line([
(image_center - x * pixels_per_mrad, image_center + 0.2 * pixels_per_mrad),
(image_center - x * pixels_per_mrad, image_center + 0.7 * pixels_per_mrad)
], fill=(0, 0, 0), width=line_thick)
d.line([
(image_center + x * pixels_per_mrad, image_center + 0.2 * pixels_per_mrad),
(image_center + x * pixels_per_mrad, image_center + 0.7 * pixels_per_mrad)
], fill=(0, 0, 0), width=line_thick)
else:
d.line([
(image_center - x * pixels_per_mrad, image_center + 0.2 * pixels_per_mrad),
(image_center - x * pixels_per_mrad, image_center + 0.55 * pixels_per_mrad)
], fill=(0, 0, 0), width=line_thick)
d.line([
(image_center + x * pixels_per_mrad, image_center + 0.2 * pixels_per_mrad),
(image_center + x * pixels_per_mrad, image_center + 0.55 * pixels_per_mrad)
], fill=(0, 0, 0), width=line_thick)
if (x < 8):
# windage half mrad marks
d.line([
(image_center + (x + .5) * pixels_per_mrad, image_center),
(image_center + (x + .5) * pixels_per_mrad, image_center - 0.25 * pixels_per_mrad)
], fill=(0, 0, 0), width=line_thin)
d.line([
(image_center - (x + .5) * pixels_per_mrad, image_center),
(image_center - (x + .5) * pixels_per_mrad, image_center - 0.25 * pixels_per_mrad)
], fill=(0, 0, 0), width=line_thin)
# handle 10 mrad thick line
d.multiline_text((image_center + (10 - 0.4) * pixels_per_mrad, image_center - 1.2 * pixels_per_mrad), "10", font=fnt, fill=(0, 0, 0))
d.line([
(image_center + 10 * pixels_per_mrad, image_center - 0.5 * pixels_per_mrad),
(image_center + 10 * pixels_per_mrad, image_center + 0.5 * pixels_per_mrad)
], fill=(0, 0, 0), width=line_thick)
d.multiline_text((image_center + (-10 - 0.4) * pixels_per_mrad, image_center - 1.2 * pixels_per_mrad), "10", font=fnt, fill=(0, 0, 0))
d.line([
(image_center - 10 * pixels_per_mrad, image_center - 0.5 * pixels_per_mrad),
(image_center - 10 * pixels_per_mrad, image_center + 0.5 * pixels_per_mrad)
], fill=(0, 0, 0), width=line_thick)
for y in range(1, 12):
line_y = image_center + y * pixels_per_mrad
# elev hash marks
d.line([
(image_center - 0.5 * pixels_per_mrad, line_y),
(image_center + 0.5 * pixels_per_mrad, line_y)
], fill=(0, 0, 0), width=line_thin)
if (y < 11): # half marks
d.line([
(image_center - 0.1 * pixels_per_mrad, line_y + 0.5 * pixels_per_mrad),
(image_center + 0.1 * pixels_per_mrad, line_y + 0.5 * pixels_per_mrad)
], fill=(0, 0, 0), width=line_thin)
dot_count = 2
if (y > 2): dot_count = 3
if (y > 4): dot_count = 4
if (y > 6): dot_count = 5
for dot in range(1, dot_count + 1):
d.ellipse([
(image_center + (dot - 0.1) * pixels_per_mrad, line_y - 0.1 * pixels_per_mrad),
(image_center + (dot + 0.1) * pixels_per_mrad, line_y + 0.1 * pixels_per_mrad)
], fill=(0, 0, 0))
d.ellipse([
(image_center + (-dot - 0.1) * pixels_per_mrad, line_y - 0.1 * pixels_per_mrad),
(image_center + (-dot + 0.1) * pixels_per_mrad, line_y + 0.1 * pixels_per_mrad)
], fill=(0, 0, 0))
if (y % 2 == 0):
d.text((image_center + (dot_count + 0.85) * pixels_per_mrad, line_y - 0.3 * pixels_per_mrad), f"{y}", font=fnt, fill=(0, 0, 0))
if (y >= 10): dot_count += 0.2
d.text((image_center - (dot_count + 1.15) * pixels_per_mrad, line_y - 0.3 * pixels_per_mrad), f"{y}", font=fnt, fill=(0, 0, 0))
if (supersample > 1):
out = out.resize((4096, 4096))
output_filename = f"mrad_{final_scale}_ca.png"
out.show()
out.save(output_filename)
print(f"written to {output_filename}")

View File

@ -0,0 +1,101 @@
#include "script_component.hpp"
/*
* Author: PabstMirror, Ruthberg (Based on ace_atragmx_fnc_calculate_solution)
* Calculates elevation and windage
*
* Arguments:
* 0: Range <NUMBER>
* 1: Direction of Fire (deg) - Yaw <NUMBER>
* 2: Inlination (deg) - Pitch <NUMBER>
* 3: Bank (deg) - Roll <NUMBER>
*
* Return Value:
* Elevation and Windage in MRAD <ARRAY>
*
* Example:
* [500, 90, 0, 0] call ace_xm157_fnc_ballistics_calculator
*
* Public: No
*/
params ["_targetRange", "_directionOfFire", "_inclinationAngle", "_bank"];
private _weaponInfo = [] call FUNC(ballistics_getData);
if (_weaponInfo isEqualTo []) exitWith { [0,0] };
_weaponInfo params ["_scopeBaseAngle","_boreHeight","_airFriction","_muzzleVelocity","_bc",
"_dragModel","_atmosphereModel","_barrelTwist","_twistDirection","_caliber","_bulletLength","_bulletMass"];
private _latitude = GVAR(data) getOrDefault ["latitude", 0];
// Get Wind
private _windSpeed = GVAR(data) getOrDefault ["wind_speed", 0];
private _windDirection = 22.5 * (GVAR(data) getOrDefault ["wind_dir", 0]);
private _wind = [sin (_directionOfFire-_windDirection) * _windSpeed, -cos (_directionOfFire-_windDirection) * _windSpeed, 0];
// Get atmosphere
private _altitude = (getPosASL ace_player) select 2;
private _relativeHumidity = EGVAR(weather,currentHumidity);
private _temperature = _altitude call EFUNC(weather,calculateTemperatureAtHeight);
private _barometricPressure = _altitude call EFUNC(weather,calculateBarometricPressure);
private _bulletPos = [0,0,-(_boreHeight / 100)];
private _lastBulletPos = +_bulletPos;
private _bulletVelocity = [0,Cos(_scopeBaseAngle) * _muzzleVelocity,Sin(_scopeBaseAngle) * _muzzleVelocity];
private _gravity = [-sin (_bank) * cos(_scopeBaseAngle + _inclinationAngle) * -GRAVITY,
sin(_scopeBaseAngle + _inclinationAngle) * -GRAVITY,
cos (_bank) * cos(_scopeBaseAngle + _inclinationAngle) * -GRAVITY];
private _useAB = missionNamespace getVariable [QEGVAR(advanced_ballistics,enabled), false];
if (_useAB) then {
_bc = parseNumber(("ace_advanced_ballistics" callExtension format["atmosphericCorrection:%1:%2:%3:%4:%5", _bc, _temperature, _barometricPressure, _relativeHumidity, _atmosphereModel]));
};
private _deltaT = 1 / 60;
private _TOF = 0; // Limit TOF to 5 seconds!
while {(_TOF < 5) && {(_bulletPos # 1) < _targetRange}} do {
private _trueVelocity = _bulletVelocity vectorDiff _wind;
private _trueSpeed = vectorMagnitude _trueVelocity;
private _bulletAccel = if (_useAB) then {
private _drag = parseNumber(("ace_advanced_ballistics" callExtension format["retard:%1:%2:%3:%4", _dragModel, _bc, _trueSpeed, _temperature]));
(vectorNormalized _trueVelocity) vectorMultiply (-1 * _drag);
} else {
_trueVelocity vectorMultiply (_trueSpeed * _airFriction);
};
_bulletAccel = _bulletAccel vectorAdd _gravity;
_lastBulletPos = _bulletPos;
_bulletPos = _bulletPos vectorAdd (_bulletVelocity vectorMultiply (_deltaT * 0.5));
_bulletVelocity = _bulletVelocity vectorAdd (_bulletAccel vectorMultiply _deltaT);
_bulletPos = _bulletPos vectorAdd (_bulletVelocity vectorMultiply (_deltaT * 0.5));
_TOF = _TOF + _deltaT;
};
private _tx = (_lastBulletPos select 0) + (_targetRange - (_lastBulletPos select 1)) * ((_bulletPos select 0) - (_lastBulletPos select 0)) / ((_bulletPos select 1) - (_lastBulletPos select 1));
private _tz = (_lastBulletPos select 2) + (_targetRange - (_lastBulletPos select 1)) * ((_bulletPos select 2) - (_lastBulletPos select 2)) / ((_bulletPos select 1) - (_lastBulletPos select 1));
private _elevation = - atan(_tz / _targetRange);
private _windage = - atan(_tx / _targetRange);
if (_useAB && {(_bulletPos select 1) > 0}) then {
// Coriolis
private _horizontalDeflection = 0.0000729 * (_bulletPos select 1) * _TOF * sin(_latitude);
private _horizontalCoriolis = - atan(_horizontalDeflection / (_bulletPos select 1));
_windage = _windage + _horizontalCoriolis;
// Eoetvoes
private _eoetvoesMultiplier = 2 * (0.0000729 * _muzzleVelocity / -GRAVITY) * cos(_latitude) * sin(_directionOfFire);
private _verticalDeflection = (_bulletPos select 2) * _eoetvoesMultiplier;
private _verticalCoriolis = - atan(_verticalDeflection / (_bulletPos select 1));
_elevation = _elevation + _verticalCoriolis;
// Spin drift
private _stabilityFactor = 1.5;
if (_caliber * _bulletLength * _bulletMass * _barrelTwist > 0) then {
_stabilityFactor = [_caliber, _bulletLength, _bulletMass, _barrelTwist, _muzzleVelocity, _temperature, _barometricPressure] call FUNC(calculateStabilityFactor);
};
private _spinDeflection = _twistDirection * 0.0254 * 1.25 * (_stabilityFactor + 1.2) * _TOF ^ 1.83;
private _spinDrift = - atan(_spinDeflection / (_bulletPos select 1));
private _windage = _windage + _spinDrift;
};
[17.453*_elevation, 17.453*_windage] // Convert to MRAD and return

View File

@ -0,0 +1,62 @@
#include "script_component.hpp"
/*
* Author: PabstMirror, Ruthberg (Based on ace_atragmx)
* Gets ballistic info for a weapon, mag and ammo
*
* Arguments:
* None
*
* Return Value:
* Weapon Info <ARRAY>
*
* Example:
* [] call ace_xm157_fnc_ballistics_getData
*
* Public: No
*/
private _unit = ace_player;
private _weaponClass = primaryWeapon _unit;
private _magazineClass = (primaryWeaponMagazine _unit) param [0, ""];
private _ammoClass = getText (configFile >> "CfgMagazines" >> _magazineClass >> "ammo");
private _key = format ["weaponInfoCache-%1-%2-%3",_weaponClass,_magazineClass,_ammoClass];
private _weaponInfo = GVAR(data) getOrDefault [_key, []];
if ((_weaponInfo isEqualTo []) && {_magazineClass != ""}) then {
TRACE_3("new weapon/mag",_weaponClass,_magazineClass,_ammoClass);
private _useABConfig = (missionNamespace getVariable [QEGVAR(advanced_ballistics,enabled), false]);
private _zeroRange = 100;
private _boreHeight = [_unit, 0] call EFUNC(scopes,getBoreHeight);
private _ammoConfig = _ammoClass call EFUNC(advanced_ballistics,readAmmoDataFromConfig);
_ammoConfig params ["_airFriction","_caliber","_bulletLength","_bulletMass","","_dragModel","_ballisticCoefficients","","_atmosphereModel","","_muzzleVelocityTable","_barrelLengthTable"];
private _weaponConfig = _weaponClass call EFUNC(advanced_ballistics,readWeaponDataFromConfig);
_weaponConfig params ["_barrelTwist", "_twistDirection", "_barrelLength"];
private _bc = if (_ballisticCoefficients isEqualTo []) then { 0 } else { _ballisticCoefficients # 0 };
// Get Muzzle Velocity
private _muzzleVelocity = if (_barrelLength > 0 && _useABConfig && {_bc != 0}) then {
[_barrelLength, _muzzleVelocityTable, _barrelLengthTable, 0] call EFUNC(advanced_ballistics,calculateBarrelLengthVelocityShift)
} else {
private _initSpeed = getNumber (configFile >> "CfgMagazines" >> _magazineClass >> "initSpeed");
private _initSpeedCoef = getNumber (configFile >> "CfgWeapons" >> _weaponClass >> "initSpeed");
if (_initSpeedCoef < 0) then {
_initSpeed = _initSpeed * -_initSpeedCoef;
};
if (_initSpeedCoef > 0) then {
_initSpeed = _initSpeedCoef;
};
_initSpeed
};
// Scope Base Angle
private _zeroAngle = "ace_advanced_ballistics" callExtension format ["calcZero:%1:%2:%3:%4", _zeroRange, _muzzleVelocity, _airFriction, _boreHeight];
private _scopeBaseAngle = parseNumber _zeroAngle;
_weaponInfo = [_scopeBaseAngle,_boreHeight,_airFriction,_muzzleVelocity,_bc,_dragModel,_atmosphereModel,_barrelTwist,_twistDirection,_caliber,_bulletLength,_bulletMass];
GVAR(data) set [_key, _weaponInfo];
TRACE_1("setting cache",_weaponInfo);
};
_weaponInfo

View File

@ -0,0 +1,67 @@
#include "script_component.hpp"
/*
* Author: PabstMirror
* Handles key presses
*
* Arguments:
* 0: Type <STRING>
* 1: IsKeyDown <BOOL>
*
* Return Value:
* Handled <BOOL>
*
* Example:
* ["range", true] call ace_xm157_fnc_keyPress
*
* Public: No
*/
params ["_func", "_keyDown"];
if (!GVAR(shown)) exitWith { false }; // fast exit if not shown
if (!([ACE_player, objNull, ["isNotInside"]] call EFUNC(common,canInteractWith))) exitWith { false };
if (!(ACE_player call CBA_fnc_canUseWeapon)) exitWith { false };
private _display = uinamespace getVariable [QGVAR(display), displayNull];
if (isNull _display) exitWith { ERROR("keyPress-no display"); false };
if (_keyDown) then { playSound QGVAR(click); };
GVAR(data) set ["lastInputTime", CBA_missionTime];
call {
if (_func == "range") exitWith {
if (_keyDown) then {
GVAR(data) set ["range_keyDownStart", CBA_missionTime];
} else {
private _holdTime = CBA_missionTime - (GVAR(data) getOrDefault ["range_keyDownStart", 0]);
private _range = 0;
if (_holdTime < 0.5) then {
private _distance = round parseNumber ctrlText (_display displayCtrl 151);
if (_distance > 10 && {_distance < RANGEFINDER_MAX}) then {
_range = _distance;
} else {
_range = -1; // bad return
};
};
TRACE_1("Updating range",_range);
GVAR(data) set ["range", _range];
};
};
if (!_keyDown) exitWith {};
GVAR(data) set ["menu_updated", true];
private _index = GVAR(data) getOrDefault ["menu_index", 0];
if (_func in ["left", "right"]) exitWith {
_index = (_index + ([-1, 1] select (_func == "right")) + count GVAR(menu)) % count GVAR(menu);
GVAR(data) set ["menu_index", _index];
};
(GVAR(menu) # _index) params ["", "_var", "_thing", ["_upAction", {}], ["_downAction", {}]];
if (_func == "up") exitWith {
[_index, _var, _thing] call _upAction;
};
if (_func == "down") exitWith {
[_index, _var, _thing] call _downAction;
};
};
true

View File

@ -0,0 +1,144 @@
#include "script_component.hpp"
/*
* Author: PabstMirror
* Draw3D event handler when scope is active
*
* Arguments:
* None (implicit vars from missionEventHandler)
*
* Return Value:
* None
*
* Example:
* [] call ace_xm157_fnc_weaponInfo_draw
*
* Public: No
*/
//IGNORE_PRIVATE_WARNING ["_thisArgs", "_thisEventHandler"]; // from missionEventHandler
_thisArgs params ["_display"];
if (isNull _display) exitWith {
TRACE_1("cleaning up display",_thisEventHandler);
GVAR(shown) = false;
removeMissionEventHandler ["Draw3D", _thisEventHandler];
};
private _ctrlScopeObject = _display displayCtrl IDC_SCOPE_OBJECT;
private _ctrlScreenObject = _display displayCtrl IDC_SCREEN_OBJECT;
// Hide everything when not in scope
private _isUsingOptic = ctrlShown (_display displayCtrl 154);
if (!_isUsingOptic) exitWith {
_ctrlScopeObject ctrlShow false;
_ctrlScreenObject ctrlShow false;
};
_ctrlScopeObject ctrlShow true;
_ctrlScreenObject ctrlShow true;
BEGIN_COUNTER(draw);
// Get common info
private _weaponVec = ace_player weaponDirection currentWeapon ace_player;
(_weaponVec call CBA_fnc_vect2Polar) params ["", "_weaponDir", "_weaponPitch"];
private _weaponBank = call cba_optics_fnc_gunBank;
private _viewBank = _weaponBank;
if (isWeaponDeployed [player, true]) then { // prone deploy tilting is special (screen doesn't tilt, but player does)
_weaponBank = (((vectorUp ace_player) vectorCrossProduct _weaponVec) call CBA_fnc_vect2Polar) # 2; // I think this is right?
};
(0.25 call CBA_fnc_getFov) params ["_fov", "_zoom"];
private _fovMRAD = 1000 * _fov; // Real MRAD (not mils)
private _nonMagnified = _zoom < 1.1;
private _range = GVAR(data) getOrDefault ["range", 0];
private _needsUpdate = GVAR(data) getOrDefault ["menu_updated", true]; // Updated when a menu item changed
private _timeSinceLastInput = CBA_missionTime - (GVAR(data) getOrDefault ["lastInputTime", 0]);
// Bank-tilt display objects
_ctrlScopeObject ctrlSetModelDirAndUp [[0,1,0],[sin _viewBank,0,cos _viewBank]];
_ctrlScreenObject ctrlSetModelDirAndUp [[0,1,0],[sin _viewBank,0,cos _viewBank]];
// Scope - Handle etched reticle
private _retTex = QPATHTOF(data\mrad_10_ca.paa);
private _retScale = 4096/10; // texureResolution / (px/MRAD)
switch (true) do {
case (_fovMRAD < 4096/40): {
_retTex = QPATHTOF(data\mrad_40_ca.paa);
_retScale = 4096/40;
};
case (_fovMRAD < 4096/20): {
_retTex = QPATHTOF(data\mrad_20_ca.paa);
_retScale = 4096/20;
};
};
private _scale = 1 / (getResolution # 5);
_scale = 2 * _scale * _retScale / _fovMRAD;
_ctrlScopeObject ctrlSetModelScale _scale;
private _ctrlScopeReticle = _display displayCtrl IDC_SCOPE_RETICLE;
if (_retTex != ctrlText _ctrlScopeReticle) then { _ctrlScopeReticle ctrlSetText _retTex; };
// Screen - Draw menu
[_display, _needsUpdate] call FUNC(weaponInfo_drawMenu);
// Screen - Show range info
private _rangeInfo = _range call {
if (_range == 0) exitWith { "" };
if (_range < 0) exitWith { // range error - blink if recent
if ((_timeSinceLastInput < 3) && {(floor (4*_timeSinceLastInput)) % 2 == 1}) then { "----" } else { "" };
};
format ["%1 m", _range toFixed 0]
};
private _ctrl = _display displayCtrl IDC_SCREEN_TEXT_UPPER_RIGHT;
_ctrl ctrlSetText _rangeInfo;
// Screen - Show bearing info
private _bearingInfo = call {
private _bearingSetting = GVAR(data) getOrDefault ["bearing_show", 0];
if ((_bearingSetting == 2) && {_timeSinceLastInput > 2}) exitWith { "" };
if ((_bearingSetting == 1)) exitWith { format ["%1", floor (17.777777 * _weaponDir)]; }; // (6400 Mils, not MRAD)
format ["%1°", floor _weaponDir];
};
private _ctrl = _display displayCtrl IDC_SCREEN_TEXT_UPPER_LEFT;
_ctrl ctrlSetText _bearingInfo;
// Screen - update reticle type based on settings and zoom level
private _ctrl = _display displayCtrl IDC_SCREEN_RETICLE;
private _lastMagnified = GVAR(data) getOrDefault ["reticle_cache_lastMag", true];
private _size = GVAR(data) getOrDefault ["reticle_cache_size", 1];
if (_needsUpdate || {_nonMagnified isNotEqualTo _lastMagnified}) then {
private _tex = "";
if (_nonMagnified) then {
switch (GVAR(data) getOrDefault ["reticle_cqb", 0]) do {
case (0): { _tex = "\a3\weapons_f\acc\data\collimdot_dot_red_ca.paa"; _size = 1; };
case (1): { _tex = "\a3\weapons_f\acc\data\collimdot_dot_red_ca.paa"; _size = 2; };
case (2): { _tex = "\a3\weapons_f\acc\data\collimdot_circle_red_ca.paa"; _size = 1; };
case (3): { _tex = "\a3\weapons_f\acc\data\collimdot_dot_green_ca.paa"; _size = 1; };
case (4): { _size = 0; };
};
} else {
_tex = "\a3\weapons_f\acc\data\collimdot_dot_red_ca.paa"; _size = 1;
};
_ctrl ctrlSetText _tex;
GVAR(data) set ["reticle_cache_lastMag", _nonMagnified];
GVAR(data) set ["reticle_cache_size", _size];
};
// Screen - update reticle position based on ballistics computer
if (_range > 0 && {_size > 0}) then {
BEGIN_COUNTER(ballistics_calculator);
([_range, _weaponDir, _weaponPitch, _weaponBank] call FUNC(ballistics_calculator)) params ["_elevMRAD", "_windMRAD"];
END_COUNTER(ballistics_calculator);
_ctrl ctrlSetPosition [-_windMRAD / _fovMRAD + 0.5 - _size / 2, + 4/3 * (_elevMRAD / _fovMRAD + 0.5 - _size/2), _size, _size*4/3];
_ctrl ctrlCommit 0;
} else {
_ctrl ctrlSetPosition [0.5 - _size / 2, + 4/3 * (0.5 - _size/2), _size, _size*4/3];
_ctrl ctrlCommit 0;
};
END_COUNTER(draw);

View File

@ -0,0 +1,81 @@
#include "script_component.hpp"
/*
* Author: PabstMirror
* Updates the menu display
*
* Arguments:
* 0: Display <DISPLAY>
* 1: Menu Item Needs Updating <BOOL>
*
* Return Value:
* None
*
* Example:
* [display, true] call ace_xm157_fnc_weaponInfo_drawMenu
*
* Public: No
*/
params ["_display", "_needsUpdate"];
if (isNil QGVAR(menu)) then {
private _arrayUp = {
params ["", "_var", "_thing"];
private _value = GVAR(data) getOrDefault [_var, 0];
private _value = (_value + 1 + count _thing) % count _thing;
GVAR(data) set [_var, _value];
};
private _arrayDown = {
params ["", "_var", "_thing"];
private _value = GVAR(data) getOrDefault [_var, 0];
_value = (_value - 1 + count _thing) % count _thing;
GVAR(data) set [_var, _value];
};
private _rangeUp = {
private _range = GVAR(data) getOrDefault ["range", -1];
if (_range < 0) then { _range = 0; };
_range = RANGEFINDER_MAX min (100 + 100 * floor (_range/100));
GVAR(data) set ["range", _range];
};
private _rangeDown = {
private _range = GVAR(data) getOrDefault ["range", -1];
if (_range < 0) then { _range = 0; };
_range = 0 max (-100 + 100 * ceil (_range/100));
GVAR(data) set ["range", _range];
};
private _atmosphereInfo = {
private _altitude = (getPosASL ace_player) select 2;
private _relativeHumidity = EGVAR(weather,currentHumidity);
private _temperature = _altitude call EFUNC(weather,calculateTemperatureAtHeight);
private _barometricPressure = _altitude call EFUNC(weather,calculateBarometricPressure); // hPA
format ["%1%2 %3%4 %5hPA", _temperature toFixed 1, "°C", _relativeHumidity toFixed 1, "%", _barometricPressure toFixed 0]
};
GVAR(menu) = [
["", "", [""], _rangeUp, _rangeDown],
["Wind Speed (m/s)", "wind_speed", ["0","1","2","3","4","5","6","7","8","9","10","11","12","13","14","15","16","17","18","19","20"], _arrayUp, _arrayDown],
["Wind Direction", "wind_dir", ["N", "NNE", "NE", "ENE", "E", "ESE", "SE", "SSE", "S", "SSW", "SW", "WSW", "W", "WNW", "NW", "NNW"], _arrayUp, _arrayDown],
// ["CQB Reticle", "reticle_cqb", ["2 MOA Red Dot", "4 MOA Red Dot", "Off"], _arrayUp, _arrayDown],
["Bearing Display", "bearing_show", ["Degrees", "Mils", "Off"], _arrayUp, _arrayDown],
["Atmosphere", "", _atmosphereInfo, {}, {}]
];
};
private _index = GVAR(data) getOrDefault ["menu_index", 0];
(GVAR(menu) # _index) params ["_title", "_var", "_thing"];
if ((!_needsUpdate) && {_thing isEqualType []}) exitWith {};
private _ctrlMenuText = _display displayCtrl IDC_SCREEN_MENU_TEXT;
private _text = "";
if (_index != 0) then {
if (_thing isEqualType []) then {
GVAR(data) set ["menu_updated", false];
private _value = GVAR(data) getOrDefault [_var, 0];
_text = _title + "\n" + "<" + (_thing param [_value, "#BadIndex"]) + ">";
} else {
_text = _title + "\n" + ([_var] call _thing);
};
};
_ctrlMenuText ctrlSetText _text;
GVAR(data) set ["menu_updated", false];

View File

@ -0,0 +1,83 @@
#include "script_component.hpp"
/*
* Author: PabstMirror
* Creates UI
*
* Arguments:
* 0: Display <DISPLAY>
*
* Return Value:
* None
*
* Example:
* [display] call ace_xm157_fnc_weaponInfo_onLoad
*
* Public: No
*/
params ["_display"];
TRACE_1("weaponInfo_onLoad",_display);
uinamespace setVariable [QGVAR(display), _display];
[_display, true] call cba_optics_fnc_loadScriptedOptic; // pass thru to cba
// Setup the scope object
private _ctrlScopeObject = _display displayCtrl IDC_SCOPE_OBJECT;
private _scale = 1 / (getResolution # 5); // keep object the same size for any interface size
private _distance = 2.0 * 4/3;
if ((getResolution # 4) < (4/3)) then { _distance = _distance * (getResolution # 7); }; // eg 5x4
private _base = ["3d", [0,0,_distance], _scale] call EFUNC(common,rscObjectHelper);
_ctrlScopeObject ctrlSetPosition _base;
_ctrlScopeObject ctrlSetModelScale _scale;
_ctrlScopeObject ctrlSetModelDirAndUp [[0,1,0],[0,0,1]];
_ctrlScopeObject ctrlShow true;
private _ctrlScopeGroup = _display displayCtrl IDC_SCOPE_GROUP;
// Add reticle
private _ctrlScopeReticle = _display ctrlCreate ["RscPicture", IDC_SCOPE_RETICLE, _ctrlScopeGroup];
_ctrlScopeReticle ctrlSetPosition [0, 0, 1, 4/3];
_ctrlScopeReticle ctrlCommit 0;
// Setup the screen object
private _ctrlScreenObject = _display displayCtrl IDC_SCREEN_OBJECT;
private _scale = 1 / (getResolution # 5); // keep object the same size for any interface size
private _distance = 4/3;
if ((getResolution # 4) < (4/3)) then { _distance = _distance * (getResolution # 7); }; // eg 5x4
private _base = ["3d", [0,0,_distance], _scale] call EFUNC(common,rscObjectHelper);
_ctrlScreenObject ctrlSetPosition _base;
_ctrlScreenObject ctrlSetModelScale _scale;
_ctrlScreenObject ctrlSetModelDirAndUp [[0,1,0],[0,0,1]];
_ctrlScreenObject ctrlShow true;
private _ctrlScreenGroup = _display displayCtrl IDC_SCREEN_GROUP;
// Info display
private _ctrl = _display ctrlCreate [QGVAR(text), IDC_SCREEN_TEXT_UPPER_LEFT, _ctrlScreenGroup];
_ctrl ctrlSetPosition [0.3, 0.2, 0.25, 0.25];
_ctrl ctrlCommit 0;
private _ctrl = _display ctrlCreate [QGVAR(text), IDC_SCREEN_TEXT_UPPER_RIGHT, _ctrlScreenGroup];
_ctrl ctrlSetPosition [0.7, 0.2, 0.25, 0.25];
_ctrl ctrlCommit 0;
// Menu
private _ctrlMenuText = _display ctrlCreate [QGVAR(textMenu), IDC_SCREEN_MENU_TEXT, _ctrlScreenGroup];
_ctrlMenuText ctrlSetPosition [0.1, 0.45, 0.8, 0.7];
_ctrlMenuText ctrlCommit 0;
// Add screen reticle
private _ctrl = _display ctrlCreate ["RscPicture", IDC_SCREEN_RETICLE, _ctrlScreenGroup];
// Add dummy idcs for dist/angle from engine
// 151=Dist, 156=Heading, 182=Pitch,
private _ctrl = _display ctrlCreate ["RscText", 151]; // IDC_IGUI_WEAPON_DISTANCE
_ctrl ctrlSetPosition [-1, -1, 0, 0];
_ctrl ctrlCommit 0;
private _ctrl = _display displayCtrl 168; // hide ca_zeroing (from cba)
_ctrl ctrlShow false;
GVAR(shown) = true;
GVAR(data) set ["menu_updated", true];
addMissionEventHandler ["Draw3D", LINKFUNC(weaponInfo_draw), [_display]];

View File

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

View File

@ -0,0 +1,24 @@
#define COMPONENT xm157
#define COMPONENT_BEAUTIFIED XM157 Fire Control Scope
#include "\z\ace\addons\main\script_mod.hpp"
// #define DEBUG_MODE_FULL
// #define DISABLE_COMPILE_CACHE
// #define ENABLE_PERFORMANCE_COUNTERS
// #define ENABLE_QUICK_TESTING
#include "\z\ace\addons\main\script_macros.hpp"
#define RANGEFINDER_MAX 5000
#define IDC_SCOPE_OBJECT 7000
#define IDC_SCOPE_GROUP 7001
#define IDC_SCOPE_RETICLE 7201
#define IDC_SCREEN_OBJECT 8000
#define IDC_SCREEN_GROUP 8001
#define IDC_SCREEN_MENU_TEXT 8201
#define IDC_SCREEN_TEXT_UPPER_LEFT 8202
#define IDC_SCREEN_TEXT_UPPER_RIGHT 8203
#define IDC_SCREEN_RETICLE 8204

Binary file not shown.

View File

@ -0,0 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<Project name="ACE">
<Package name="XM157">
</Package>
</Project>

View File

@ -0,0 +1,56 @@
---
layout: wiki
title: XM157 Framework
description: Explains how to add the XM157 scope framework
group: framework
order: 5
parent: wiki
mod: ace
version:
major: 5
minor: 1
patch: 0
---
## 1. Config Values
```cpp
class CfgWeapons {
class ItemCore;
class InventoryOpticsItem_Base_F;
class your_XM157: ItemCore {
class CBA_ScriptedOptic {
bodyTexture = "\z\ace\addons\xm157\data\ace_vector_body_co.paa";
bodyTextureSize = 1;
hideMagnification = 1;
disableTilt = 0;
};
weaponInfoType = "ace_xm157_info";
class ItemInfo: InventoryOpticsItem_Base_F {
modelOptics = "\x\cba\addons\optics\cba_optic_big_100.p3d";
class OpticsModes {
class optic {
opticsID=1;
useModelOptics=1;
opticsPPEffects[]={ "OpticsCHAbera1", "OpticsBlur1" };
opticsZoomMin = "8 call (uiNamespace getVariable 'cba_optics_fnc_setOpticMagnificationHelper')";
opticsZoomMax = "1 call (uiNamespace getVariable 'cba_optics_fnc_setOpticMagnificationHelper')";
opticsZoomInit = "1 call (uiNamespace getVariable 'cba_optics_fnc_setOpticMagnificationHelper')";
discreteDistance[] = {100};
discreteDistanceInitIndex = 0;
distanceZoomMin=100;
distanceZoomMax=100;
memoryPointCamera="opticView";
visionMode[] = {"Normal"};
opticsFlare=1;
opticsDisablePeripherialVision=1;
cameraDir="";
};
};
};
};
};
```