Initial commit:

- Added new ammo cfg types
 - Added new caching functions
 - Added dev functions
 - Transfered core system to vanilla projectile EHs
 - Added stringtable sub categories
 - Reworked fragmenting and spalling to us submunitions
   - Frag
     - Implemented system around chance to hit
     - Switched from hitbox estimation to hitting specific HPs
     - Updated chance to miss method based on solid angle hit chance
     - Split random & targeted frag to their own subfunctions
   - Spall
     - Uses a system of estimated momentum changes to generate spall
This commit is contained in:
lambdatiger 2024-01-08 15:22:52 -06:00
parent 6c212ca377
commit 3c1e912787
41 changed files with 2058 additions and 1363 deletions

File diff suppressed because it is too large Load Diff

View File

@ -1,23 +1,33 @@
// dev
PREP(dev_fragCalcDump);
PREP(dev_debugAmmo);
PREP(dev_trackHitBox);
PREP(dev_fadeRound);
PREP(dev_sphereDraw);
PREP(dev_addRound);
PREP(dev_trackObj);
PREP(dev_drawTrace);
PREP(dev_clearTraces);
PREP(dev_switchUnitHandle);
PREP(doSpall);
// Frag
PREP(addBlackList);
PREP(initBlackList);
PREP(fired);
PREP(frago);
PREP(spallTrack);
PREP(submunition);
PREP(shouldFrag);
PREP(fragInfo);
PREP(doFrag);
PREP(doFragTargeted);
PREP(doFragRandom);
// Spall
PREP(shouldSpall);
PREP(doSpall);
PREP(doSpallMomentum);
// * Other */
PREP(addBlackList);
PREP(dev_addTrack);
PREP(dev_drawTraces);
PREP(spallHP);
PREP(dev_startTracing);
PREP(dev_stopTracing);
PREP(dev_trackTrace);
// New tracking mechanisms
PREP(masterPFH);
PREP(pfhRound);
PREP(addPfhRound);
//PREP(spallHP); Look at me !*!
// Explosive Reflection
PREP(findReflections);

View File

@ -1,32 +1,39 @@
#include "script_component.hpp"
if (isServer) then {
GVAR(lastFragTime) = -1;
[QGVAR(frag_eh), {_this call FUNC(frago);}] call CBA_fnc_addEventHandler;
};
["CBA_settingsInitialized", {
if (!GVAR(enabled)) exitWith {};
if (isServer) then {
[QGVAR(frag_eh), LINKFUNC(doFrag)] call CBA_fnc_addEventHandler;
[QGVAR(spall_eh), LINKFUNC(doFragMomentum)] call CBA_fnc_addEventHandler;
[] call FUNC(initBlackList);
};
if (hasInterface) then {
["ace_firedPlayer", LINKFUNC(fired)] call CBA_fnc_addEventHandler;
["ace_firedPlayerVehicle", LINKFUNC(fired)] call CBA_fnc_addEventHandler;
};
// Register fire event handler
["ace_firedPlayer", LINKFUNC(fired)] call CBA_fnc_addEventHandler;
["ace_firedNonPlayer", LINKFUNC(fired)] call CBA_fnc_addEventHandler;
["ace_firedPlayerVehicle", LINKFUNC(fired)] call CBA_fnc_addEventHandler;
["ace_firedNonPlayerVehicle", LINKFUNC(fired)] call CBA_fnc_addEventHandler;
["ace_firedNonPlayer", LINKFUNC(fired)] call CBA_fnc_addEventHandler;
["ace_firedNonPlayerVehicle", LINKFUNC(fired)] call CBA_fnc_addEventHandler;
addMissionEventHandler ["EachFrame", {call FUNC(masterPFH)}];
// Debug info
#ifdef DEBUG_MODE_FULL
if (hasInterface && GVAR(debugOptions)) then
{
private _h = [LINKFUNC(dev_drawTrace), 0] call CBA_fnc_addPerFrameHandler;
missionNamespace setVariable [QGVAR(dev_drawPFEH), _h];
["unit", LINKFUNC(dev_switchUnitHandle), true] call CBA_fnc_addPlayerEventHandler;
[objNull, ace_player] call FUNC(dev_switchUnitHandle);
["ace_firedPlayer", LINKFUNC(fired)] call CBA_fnc_addEventHandler;
["ace_firedNonPlayer", LINKFUNC(fired)] call CBA_fnc_addEventHandler;
["ace_firedPlayerVehicle", LINKFUNC(fired)] call CBA_fnc_addEventHandler;
["ace_firedNonPlayerVehicle", LINKFUNC(fired)] call CBA_fnc_addEventHandler;
};
#endif
}] call CBA_fnc_addEventHandler;
// Cache for ammo type configs
GVAR(cacheRoundsTypesToTrack) = [false] call CBA_fnc_createNamespace;
// Debug stuff:
#ifdef DRAW_FRAG_INFO
[] call FUNC(dev_startTracing);
#endif
#ifdef DEBUG_MODE_FULL
#ifdef LOG_FRAG_INFO
[true, true, 30] call FUNC(dev_debugAmmo);
#endif

View File

@ -6,20 +6,24 @@ PREP_RECOMPILE_START;
#include "XEH_PREP.hpp"
PREP_RECOMPILE_END;
GVAR(blackList) = [];
GVAR(traceFrags) = false;
GVAR(spallHPData) = [];
GVAR(spallIsTrackingCount) = 0;
GVAR(materialSpallCache) = createHashMap;
GVAR(spallRoundCache) = createHashMap;
GVAR(traceID) = -1;
GVAR(traces) = [];
GVAR(tracesStarted) = false;
GVAR(shouldFragCache) = createHashMap;
GVAR(fragInfoCache) = createHashMap;
GVAR(lastFragTime) = -2;
GVAR(lastIterationIndex) = 0;
GVAR(objects) = [];
GVAR(arguments) = [];
#include "initSettings.inc.sqf"
#ifdef DEBUG_MODE_FULL
GVAR(dev_trackLines) = createHashMap;
GVAR(dev_hitBoxes) = createHashMap;
GVAR(dev_failedToDelete) = 0;
GVAR(dev_eventSpheres) = [];
#include "initSettingsDebug.inc.sqf"
#endif
ADDON = true;

View File

@ -1,6 +1,6 @@
#include "..\script_component.hpp"
#include "script_component.hpp"
/*
* Author: Jaynus, NouberNou
* Author: Jaynus, NouberNou, Lambda.Tiger
* Adds a round to the blacklist (will be ignored).
*
* Arguments:
@ -10,12 +10,12 @@
* None
*
* Example:
* [bullet] call ace_frag_fnc_addBlackList
* [projectile] call ace_frag_fnc_addBlackList
*
* Public: No
*/
params ["_round"];
params ["_proj"];
TRACE_1("addBlackList",_round);
GVAR(blackList) pushBack _round;
GVAR(shouldFragCache) set [typeOf _ammo, [false, false]];

View File

@ -1,77 +0,0 @@
#include "..\script_component.hpp"
/*
* Author: Jaynus, NouberNou
* Starts tracking a round that will frag.
* Should only be called once per round.
*
* Arguments:
* 0: Shooter <OBJECT>
* 1: Ammo classname <STRING>
* 2: Projectile <OBJECT>
*
* Return Value:
* None
*
* Example:
* [player, "handGrenade", bullet] call ace_frag_fnc_addPfhRound
*
* Public: No
*/
params ["_gun", "_type", "_round"];
TRACE_3("addPfhRound",_gun,_type,_round);
if (!GVAR(enabled)) exitWith {TRACE_1("setting disabled",_this);};
if (!alive _round) exitWith {TRACE_1("round dead?",_this);};
if (_round in GVAR(blackList)) exitWith {
TRACE_1("round in blackList",_this);
REM(GVAR(blackList),_round);
};
// Exit on max track
if ((count GVAR(objects)) >= GVAR(maxTrack)) exitWith {TRACE_1("maxTrack limit",count GVAR(objects));};
private _doSpall = false;
if (GVAR(SpallEnabled)) then {
if (GVAR(spallIsTrackingCount) <= 0) then {
GVAR(spallHPData) = [];
};
if (GVAR(spallIsTrackingCount) > 5) then {
TRACE_1("At Spall Limit",GVAR(spallIsTrackingCount));
} else {
_doSpall = true;
INC(GVAR(spallIsTrackingCount));
};
TRACE_2("",_doSpall,GVAR(spallIsTrackingCount));
};
#ifdef DRAW_FRAG_INFO
[ACE_player, _round, [0, 1, 0, 1]] call FUNC(dev_addTrack);
#endif
// We only do the single track object check here.
// We should do an {!(_round in GVAR(objects))}
// But we leave that out here for optimization. So this cannot be a framework function
// Otherwise, it should only be added once and from the FiredEH
if (alive _round) then {
private _spallTrack = [];
private _spallTrackID = [];
private _args = [
_round, getPosASL _round, velocity _round, _type, diag_frameno, getPosASL _round, _doSpall, _spallTrack, _spallTrackID,
getNumber (configFile >> "CfgAmmo" >> _type >> QGVAR(skip)),
getNumber (configFile >> "CfgAmmo" >> _type >> "explosive"),
getNumber (configFile >> "CfgAmmo" >> _type >> "indirectHitRange"),
getNumber (configFile >> "CfgAmmo" >> _type >> QGVAR(force)),
getNumber (configFile >> "CfgAmmo" >> _type >> "indirecthit") * (sqrt (getNumber (configFile >> "CfgAmmo" >> _type >> "indirectHitRange")))
];
TRACE_1("Initializing track", _round);
GVAR(objects) pushBack _round;
GVAR(arguments) pushBack _args;
if (_doSpall) then {
[_round, 1, _spallTrack, _spallTrackID] call FUNC(spallTrack);
};
};

View File

@ -0,0 +1,114 @@
#include "script_component.hpp"
/*
* Author: Lambda.Tiger
* This function adds a round to be traced
*
* Arguments:
* 0: Projectile <OBJECT>
* 1: Add projectile event handlers <BOOL>
* 2: Is the round blue <BOOL>
*
* Return Value:
* None
*
* Example:
* [_proj, false, false] call ace_frag_dev_addRound;
*
* Public: No
*/
params [
"_proj",
["_addEHs", true, [false]],
["_sidePlayer", true, [false]]
];
/// track round on each frame
// Create entry in position array from hashmap
private _pID = getObjectID _proj;
if (GVAR(fadeRounds)) then
{
if (_sidePlayer) then
{
GVAR(dev_trackLines) set [_pID, [__FADE_INIT, [getposATL _proj], [0, 0, 1, __FADE_INIT]]];
} else
{
GVAR(dev_trackLines) set [_pID, [__FADE_INIT, [getposATL _proj], [1, 0.5, 0, __FADE_INIT]]];
};
// add fading factor
[LINKFUNC(dev_fadeRound), __FADE_INTERVAL, [_pID]] call CBA_fnc_addPerFrameHandler;
} else
{
if (_sidePlayer) then
{
GVAR(dev_trackLines) set [_pID, [1, [getposATL _proj], [0, 0, 1, 1]]];
} else
{
GVAR(dev_trackLines) set [_pID, [1, [getposATL _proj], [1, 0, 0, 1]]];
};
};
// eventhandler to track round and cleanup when round is "dead"
[
{
if (isGamePaused) exitWith {};
params ["_par", "_h"];
_par params ["_proj"];
if (!alive _proj) exitWith
{
[_h] call CBA_fnc_removePerFrameHandler;
};
private _arr = GVAR(dev_trackLines) getOrDefault [(getObjectID _proj), -1];
if (typeName _arr == "SCALAR") exitWith {};
(_arr#1) pushBack getPosATL _proj;
if (_arr#0 <= 0) exitWith
{
[_h] call CBA_fnc_removePerFrameHandler;
};
},
0,
[_proj]
] call CBA_fnc_addPerFrameHandler;
if (!_addEHs) exitWith {};
// Add hitpart eventHandler
_proj addEventHandler [
"HitPart",
{
params ["_proj", "", "", "_posASL"];
private _arr = (GVAR(dev_trackLines) get (getObjectID _proj))#1;
_arr pushBack ASLtoATL _posASL;
if (GVAR(dbgSphere)) then
{
[_posASL, "green"] call FUNC(dev_sphereDraw);
};
}
];
// Add explode eventHandler
_proj addEventHandler [
"Explode",
{
params ["_proj", "_posASL"];
private _arr = (GVAR(dev_trackLines) get (getObjectID _proj))#1;
_arr pushBack ASLtoATL _posASL;
if (GVAR(dbgSphere)) then
{
[_posASL, "red"] call FUNC(dev_sphereDraw);
};
}
];
// Add deflected eventHandler
_proj addEventHandler [
"Deflected",
{
params ["_proj", "_posASL"];
private _arr = (GVAR(dev_trackLines) get (getObjectID _proj))#1;
_arr pushBack ASLtoATL _posASL;
if (GVAR(dbgSphere)) then
{
[_posASL, "blue"] call FUNC(dev_sphereDraw);
};
}
];

View File

@ -1,26 +0,0 @@
#include "..\script_component.hpp"
/*
* Author: ACE-Team
*
*
* Arguments:
* None
*
* Return Value:
* None
*
* Example:
* call ace_frag_fnc_dev_addTrack
*
* Public: No
*/
params ["_origin", "_obj", ["_color", [1, 0, 0, 1]]];
private _positions = [];
private _objSpd = vectorMagnitude (velocity _obj);
_positions pushBack [getPos _obj, _objSpd];
private _data = [_origin, typeOf _origin, typeOf _obj, _objSpd, _positions, _color];
private _index = GVAR(traces) pushBack _data;
[DFUNC(dev_trackTrace), 0, [_obj, _index, CBA_missionTime]] call CBA_fnc_addPerFrameHandler;

View File

@ -0,0 +1,24 @@
#include "script_component.hpp"
/*
* Author: Lambda.Tiger
* Cleares all dev spheres and traces
*
* Arguments:
* None
*
* Return Value:
* None
*
* Example:
* [] call ace_frag_fnc_dev_clearTraces;
*
* Public: No
*/
GVAR(dev_trackLines) = createHashMap;
for "_i" from 0 to count GVAR(dev_eventSpheres) - 1 do
{
deleteVehicle (GVAR(dev_eventSpheres)#_i);
};
GVAR(dev_eventSpheres) = +[];
GVAR(dev_hitBoxes) = createHashMap;

View File

@ -0,0 +1,75 @@
#include "script_component.hpp"
/*
* Author: Lambda.Tiger
* Per frame function to draw all dev traces
*
* Arguments:
* none
*
* Return Value:
* None
*
* Example:
*
* Public: No
*/
private _deleteArr = [];
{
private _alpha = _y#0;
// leave if trace is not to be drawn
if (GVAR(dltTrace) && _alpha <= 0) then
{
_deleteArr pushBack _x;
continue;
};
if (count (_y#1) > 1) then
{
private _color = _y#2;
for "_j" from 1 to count (_y#1) - 1 do
{
drawLine3D [_y#1#(_j-1), _y#1#_j, _color];
};
};
} forEach GVAR(dev_trackLines);
if (GVAR(dltTrace)) then
{
for "_i" from 0 to count _deleteArr - 1 do
{
GVAR(dev_trackLines) deleteAt (_deleteArr#_i);
};
};
if (GVAR(drawHitBox)) then {
#define HB_DRAW_ARRS [[3,2,1,5,6,7,3,0,4,5],[0,1],[2,6],[7,4]]
_deleteArr = [];
{
_y params ["_obj", "_pts", "_color"];
if (!alive _obj) then
{
_deleteArr pushBack _x;
continue;
};
{
for "_i" from 1 to count _x -1 do
{
drawLine3D [_obj modelToWorld (_pts#(_x#_i)), _obj modelToWorld (_pts#(_x#(_i-1))), _color];
};
} forEach HB_DRAW_ARRS;
} forEach GVAR(dev_hitBoxes);
for "_i" from 0 to count _deleteArr - 1 do
{
GVAR(dev_hitBoxes) deleteAt (_deleteArr#_i);
};
};
if (GVAR(frameHint)) then
{
hintsilent str (1/diag_deltaTime);
};

View File

@ -1,37 +0,0 @@
#include "..\script_component.hpp"
/*
* Author: ACE-Team
*
*
* Arguments:
* None
*
* Return Value:
* None
*
* Example:
* call ace_frag_fnc_dev_drawTraces
*
* Public: No
*/
{
_x params ["", "", "", "", "_positions", "_color"];
private _index = 0;
private _max = count _positions;
// private _lastSpd = [];
private _lastPos = [];
while {_index < _max} do {
_data1 = _positions select _index;
_data2 = _positions select ([_index + ACE_TRACE_DRAW_INC, _max - 1] select (_index + ACE_TRACE_DRAW_INC >= _max));
_pos1 = _data1 select 0;
_pos2 = _data2 select 0;
ADD(_index,ACE_TRACE_DRAW_INC);
drawLine3D [_pos1, _pos2, _color];
_lastPos = _pos2;
// _lastSpd = _data1 select 1;
};
// drawIcon3D ["", [1,0,0,1], _lastPos, 0, 0, 0, format ["%1m/s", _lastSpd], 1, 0.05, "RobotoCondensed"];
} forEach GVAR(traces);

View File

@ -0,0 +1,34 @@
#include "script_component.hpp"
/*
* Author: Lambda.Tiger
* Fades a trace over time based on given parameters
*
* Arguments:
* 0: PFEH arguments <ARRAY>
* 1: PHEH handle <SCALAR>
*
* Return Value:
* None
*
* Example:
*
* Public: No
*/
params ["_args", "_h"];
private _arr = GVAR(dev_trackLines) getOrDefault [(_args#0), []];
if (count _arr < 3) exitWith
{
[_h] call CBA_fnc_removePerFrameHandler;
};
private _alpha = _arr#0;
private _color = _arr#2;
_alpha = (_alpha - __FADE_RATE) min 1;
_arr set [0, _alpha];
_color set [3, _alpha];
_color set [1, (0.5 - _alpha/2) max 0];
if (_alpha <= 0 ) then
{
[_h] call CBA_fnc_removePerFrameHandler;
};

View File

@ -0,0 +1,90 @@
#include "script_component.hpp"
/*
* Author: Lambda.Tiger, based on fnc_dev_debugAmmo by "ACE-Team"
* Dumps all ammo types to see if there's any reason to spawn fragments
* given power, distance, and lifetime of each fragement
*
* Arguments:
* 0: _dispAll <BOOL> - Display rounds that will never frag (power < 5).
* Default value false
* 1: _minFrgPowRng <FLOAT> - minimum range for sqrt power calculation
*
* Return Value:
* None
*
* Example:
* [false, 10] call ace_frag_fnc_fragoCalcDump
*
* Public: No
*/
params [["_dispAll", false, [false]], ["_minFrgPowRng", 35, [123]]];
#define DT 0.01
private _allMagsConfigs = configProperties [configFile >> "cfgAmmo", "isClass _x && !('ace' in configName _x)", true];
private _processedCfgAmmos = [];
private _nPrinted = 0;
diag_log text "//****************** fragCalcDump Beg ******************//";
// Processing ammo types
{
private _ammo = toLower configName _x;
if (_ammo == "" || {_ammo in _processedCfgAmmos} ) then {continue};
// calculating hit range
private _skip = getNumber (configFile >> "cfgAmmo" >> _ammo >> QGVAR(skip));
private _force = getNumber (configFile >> "cfgAmmo" >> _ammo >> QGVAR(force));
private _explosive = getNumber (configFile >> "cfgAmmo" >> _ammo >> "explosive");
private _indirectHit = getNumber(configFile >> "cfgAmmo" >> _ammo >> "indirectHit");
private _indirectRange = getNumber(configFile >> "cfgAmmo" >> _ammo >> "indirectHitRange");
_shouldFrag = if (_skip == 1 || (_force == 0 && {_explosive < 0.5 || {_indirectHit < 3
|| {_indirectRange < 5 && _indirectHit < _indirectRange}}})) then {
false;
} else {
true;
};
// Gunery equation from frago
private _c = getNumber (configFile >> "cfgAmmo" >> _ammo >> QGVAR(CHARGE));
if (_c == 0) then {_c = 1;};
private _m = getNumber (configFile >> "cfgAmmo" >> _ammo >> QGVAR(METAL));
if (_m == 0) then {_m = 2;};
private _k = getNumber (configFile >> "cfgAmmo" >> _ammo >> QGVAR(GURNEY_K));
if (_k == 0) then {_k = 0.8;};
private _gC = getNumber (configFile >> "cfgAmmo" >> _ammo >> QGVAR(GURNEY_C));
if (_gC == 0) then {_gC = 2440;};
private _fragCount = getNumber (configFile >> "cfgAmmo" >> _ammo >> QGVAR(fragCount));
if (_fragCount == 0) then {_fragCount = 200; _warn = true};
profilerLog (str (sqrt (_fragCount / (4 * pi * 0.005))));
private _velocity = 0.8 * _gC * sqrt (_c /(_m + _c * _k));
// number of shrapnel to send a direction
private _count = ceil (random (sqrt (_m / 1000)));
private _fragPowerSpeedRange = [0.5, 1] vectorMultiply _fragPower;
if (_nSkip || _dispALl) then
{
diag_log text format ["Ammo type: %1", _ammo];
diag_log text format [" Indirect hit range: %1", _indirectHitRange];
diag_log text format [" Frag sqrtPower: %1", _fragPowerSqrt];
diag_log text format [" Frag range: %1", _fragRange];
diag_log text format [" Frag speed range: %1", _fragPowerSpeedRange];
diag_log text format [" Number frags: %1", _count];
diag_log text " ~~~ Fragments ~~~";
INC(_nPrinted);
};
_processedCfgAmmos pushBack _ammo;
} forEach _allMagsConfigs;
diag_log text "//****************** fragCalcDump End ******************//";
diag_log text format ["//********************** printed %1 *********************//", _nPrinted];

View File

@ -0,0 +1,52 @@
#include "script_component.hpp"
/*
* Author: Lambda.Tiger
* Add a colored sphere at a specified point
*
* Arguments:
* 0: ASL position to add sphere <ARRAY>
* 1: Color of sphere <STRING>
*
* Return Value:
* None
*
* Example:
* [unit0, player] call ace_frag_fnc_dev_switchUnitHandle;
*
* Public: No
*/
params [
["_posASL", [0,0,0], [[]], [2,3]],
["_color", "(1,0,0,0.5)", [""]]
];
if (count _posASL < 3) then
{
_posASL pushBack 0;
_posASL = ASLtoATL _posASL;
_posASL set [2, 0];
_posASL = ATLtoASL _posASL;
};
if (_color select [0,1] != "(") then
{
switch (toLower _color) do
{
case "blue": { _color = "(0,0,0.8,0.5)"; };
case "black": { _color = "(1,1,1,0.5)"; };
case "white": { _color = "(0,0,0,0.5)"; };
case "red": { _color = "(0.8,0,0,0.5)"; };
case "green": { _color = "(0,0.8,0,0.5)"; };
case "yellow": { _color = "(0.8,0.8,0,0.5)"; };
case "orange": { _color = "(0.8,0.518,0,0.5)"; };
default { _color = "(0.8,0.8,0,0.5)";};
};
};
private _clrStr = "#(argb,8,8,3)color" + _color;
private _sphere = "Sign_Sphere25cm_F" createVehicle [1,2,34];
_sphere setObjectTexture [0, _clrStr];
_sphere setPosASL _posASL;
GVAR(dev_eventSpheres) pushBack _sphere;
_sphere;

View File

@ -1,23 +0,0 @@
#include "..\script_component.hpp"
/*
* Author: ACE-Team
*
*
* Arguments:
* None
*
* Return Value:
* None
*
* Example:
* call ace_frag_fnc_dev_startTracing
*
* Public: No
*/
if (GVAR(tracesStarted)) exitWith {};
INFO("Starting Trace Drawing");
GVAR(tracesStarted) = true;
GVAR(traceID) = [LINKFUNC(dev_drawTraces), 0, []] call CBA_fnc_addPerFrameHandler;

View File

@ -1,23 +0,0 @@
#include "..\script_component.hpp"
/*
* Author: ACE-Team
* Dev things
*
* Arguments:
* None
*
* Return Value:
* None
*
* Example:
* None
*
* Public: No
*/
if (!GVAR(tracesStarted)) exitWith {};
INFO("Ending Trace Drawing");
GVAR(tracesStarted) = false;
[GVAR(traceID)] call CBA_fnc_removePerFrameHandler;

View File

@ -0,0 +1,43 @@
#include "script_component.hpp"
/*
* Author: Lambda.Tiger
* Handle for debug actions when switching units
*
* Arguments:
* 0: Last unit <OBJECT>
* 1: Current unit <OBJECT>
*
* Return Value:
* None
*
* Example:
* [unit0, player] call ace_frag_fnc_dev_switchUnitHandle;
*
* Public: No
*/
params ["_lVic", "_cVic"];
if (_cVic isEqualTo objNull || {!local _cVic || _lVic isEqualTo _cVic}) exitWith {};
private _aID = missionNamespace getVariable [QGVAR(dev_clearTraceAction), -1];
if (_aID > -1 && {_lVic isNotEqualTo objNull}) then
{
_lVic removeAction _aID;
};
_aID = _cVic addAction
[
"Reset Lines",
FUNC(dev_clearTraces),
nil, // arguments
1.5, // priority
true, // showWindow
false, // hideOnUse
"", // shortcut
"true", // condition
8
];
missionNamespace getVariable [QGVAR(dev_clearTraceAction), _aID];

View File

@ -0,0 +1,71 @@
#include "script_component.hpp"
/*
* Author: Lambda.Tiger
* Add a hitbox outline to an object
*
* Arguments:
* 0: Object to draw hitbox <OBJECT>
* 1: Add center sphere <BOOL>
*
* Return Value:
* None
*
* Example:
* [player] call ace_frag_fnc_dev_trackHitBox;
*
* Public: No
*/
params [
["_obj", objNull, [objNull]],
["_addSphere", true, [false]]
];
if (isNull _obj) exitWith {};
private _box = [];
if (_obj isKindOf "CAManBase") then {
if (vehicle _obj == _obj) then {
_box = 0 boundingBox _obj;
} else {
_box = boundingBoxReal [_obj, "Geometry"];
};
} else {
_box = boundingBoxReal [_obj, "FireGeometry"];
};
_box params ["_lowP","_upP"];
private _stance = stance _obj;
switch (true) do {
case (_stance isEqualTo "STAND"): {_upP set [2, 1.9];};
case (_stance isEqualTo "CROUCH"): {_upP set [2, 1.3];};
case (_stance isEqualTo "PRONE"): {_upP set [2, 0.8];};
};
private _centerPoint = ASLToAGL getPosASL _obj;
if (_addSphere && vehicle _obj isEqualTo _obj) then {
private _centerSphere = [getPosASL _obj, "yellow"] call FUNC(dev_sphereDraw);
_centerSphere disableCollisionWith vehicle _obj;
_centerSphere attachTo [_obj, _obj worldToModel _centerPoint];
};
private _p1 = _upP;
private _p7 = _lowP;
private _points =[
_upP,
[_p1#0,_p7#1,_p1#2],
[_p7#0,_p7#1,_p1#2],
[_p7#0,_p1#1,_p1#2],
[_p1#0,_p1#1,_p7#2],
[_p1#0,_p7#1,_p7#2],
_lowP,
[_p7#0,_p1#1,_p7#2]
];
_color = switch (side _obj) do {
case east: {[1, 0, 0, 1]};
case resistance: {[0, 1, 0, 1]};
default {[0, 0, 1, 1]};
};
//TRACE_3("box params", _obj, _points, _color);
GVAR(dev_hitBoxes) set [getObjectID _obj, [_obj, _points, _color]];

View File

@ -0,0 +1,123 @@
#include "script_component.hpp"
/*
* Author: Lambda.Tiger
* This function adds an object to have it's course tracked (every frame).
*
* Arguments:
* 0: Object to draw hitbox <OBJECT>
* 1: Color of trace <STRING>
* 2: Whether the object is a projectile <BOOL>
*
* Return Value:
* None
*
* Example:
* [player] call ace_frag_fnc_dev_trackObj;
*
* Public: No
*/
params ["_obj", ["_color", "blue", [""]], ["_isProj", false, [false]]];
/// track round on each frame
// Create entry in position array from hashmap
private _pID = getObjectID _obj;
if (GVAR(fadeRounds)) then
{
private _colorArray = switch (toLower _color) do {
case "purple": {[0.8, 0, 0.8, __FADE_INIT]};
case "blue": {[0, 0, 0.8, __FADE_INIT]};
case "green": {[0, 0.8, 0, __FADE_INIT]};
case "orange": {[0.8, 0.518, 0, __FADE_INIT]};
case "yellow": {[0.8, 0.8, 0, __FADE_INIT] };
case "red": {[0.8, 0, 0, __FADE_INIT]};
case "black": {[1, 1, 1, __FADE_INIT]};
case "white": {[0, 0, 0, __FADE_INIT]};
default {[0, 0.8, 0.8, __FADE_INIT]};
};
GVAR(dev_trackLines) set [_pID, [__FADE_INIT, [getposATL _obj], _colorArray]];
// add fading factor
[LINKFUNC(dev_fadeRound), __FADE_INTERVAL, [_pID]] call CBA_fnc_addPerFrameHandler;
} else
{
private _colorArray = switch (toLower _color) do {
case "purple": {[0.8, 0, 0.8, 1]};
case "blue": {[0, 0, 0.8, 1]};
case "green": {[0, 0.8, 0, 1]};
case "orange": {[0.8, 0.518, 0, 1]};
case "yellow": {[0.8, 0.8, 0, 1] };
case "red": {[0.8, 0, 0, 1]};
case "black": {[1, 1, 1, 1]};
case "white": {[0, 0, 0, 1]};
default {[0, 0.8, 0.8, 1]};
};
GVAR(dev_trackLines) set [_pID, [1, [getposATL _obj], _colorArray]];
};
// eventhandler to track round and cleanup when round is "dead"
[
{
if (isGamePaused) exitWith {};
params ["_par", "_h"];
_par params ["_obj"];
if (!alive _obj) exitWith {
[_h] call CBA_fnc_removePerFrameHandler;
};
private _arr = GVAR(dev_trackLines) getOrDefault [(getObjectID _obj), -1];
if (typeName _arr isEqualTo "SCALAR") exitWith {
[_h] call CBA_fnc_removePerFrameHandler;
};
(_arr#1) pushBack getPosATL _obj;
if (_arr#0 <= 0) exitWith {
[_h] call CBA_fnc_removePerFrameHandler;
};
},
0,
[_obj]
] call CBA_fnc_addPerFrameHandler;
if (!_isProj) exitWith {};
// Add hitpart eventHandler
_obj addEventHandler [
"HitPart",
{
params ["_proj", "", "", "_posASL"];
private _arr = (GVAR(dev_trackLines) get (getObjectID _proj))#1;
_arr pushBack ASLtoATL _posASL;
if (GVAR(dbgSphere)) then
{
[_posASL, "green"] call FUNC(dev_sphereDraw);
};
}
];
// Add explode eventHandler
_obj addEventHandler [
"Explode",
{
params ["_proj", "_posASL"];
private _arr = (GVAR(dev_trackLines) get (getObjectID _proj))#1;
_arr pushBack ASLtoATL _posASL;
if (GVAR(dbgSphere)) then
{
[_posASL, "red"] call FUNC(dev_sphereDraw);
};
}
];
// Add deflected eventHandler
_proj addEventHandler [
"Deflected",
{
params ["_proj", "_posASL"];
private _arr = (GVAR(dev_trackLines) get (getObjectID _proj))#1;
_arr pushBack ASLtoATL _posASL;
if (GVAR(dbgSphere)) then
{
[_posASL, "blue"] call FUNC(dev_sphereDraw);
};
}
];

View File

@ -1,27 +0,0 @@
#include "..\script_component.hpp"
/*
* Author: ACE-Team
* Dev things
*
* Arguments:
* None
*
* Return Value:
* None
*
* Example:
* call ace_frag_fnc_dev_trackTrace
*
* Public: No
*/
params ["_args", "_pfhID"];
_args params ["_tracerObj", "_index"];
if (alive _tracerObj && {GVAR(traces) isNotEqualTo []}) then {
private _data = GVAR(traces) select _index;
private _positions = _data select 4;
_positions pushBack [getPos _tracerObj, vectorMagnitude (velocity _tracerObj)];
} else {
[_pfhID] call CBA_fnc_removePerFrameHandler;
};

View File

@ -0,0 +1,59 @@
#include "script_component.hpp"
/*
* Author: Lambda.Tiger
* This function handles creating both random and targeted fragments as well
* as handling some of the performance optimizations.
*
* Arguments:
* 0: Array of argumentse
* 0.0: projectile that's fragmenting <OBJECT>
* 0.1: ASL position of projectile <ARRAY>
* 0.2: velocity of projectile <ARRAY>
* 1: Whether the projectile is a submunition <BOOL>
*
* Return Value:
* None
*
* Example:
* [[_proj, getPosASL _proj, velocity _proj]] call ace_frag_fnc_doFrag;
*
* Public: No
*/
params ["_args", ["_isSubMunit", false, [false]]];
_args params [
["_proj", objNull, [objNull]],
["_posASL", [0,0,0], [[]], [3]],
["_vel", [0,0,0] , [[]], [3]]
];
private _timeSince = CBA_missionTime - GVAR(lastFragTime);
if (isNull _proj || {_posASL isEqualTo [0,0,0] || _timeSince < 0.2}) exitWith {};
GVAR(lastFragTime) = CBA_missionTime;
private _maxFrags = round (linearConversion [0.1, 1.5, _timeSince, MAX_FRAG_COUNT_MIN, MAX_FRAG_COUNT_MAX, true]);
private _ammo = typeOf _proj;
private _ammoArr = [_ammo] call FUNC(fragInfo);
_ammoArr params ["_fragRange", "_fragVel", "_fragTypes", "_modFragCount"];
private _shotParents = getShotParents _proj;
private _heightAGL = (ASLToAGL _posASL)#2;
if (_heightAGL < 0.25) then {
_posASL = _posASL vectorAdd [0, 0, 0.25];
};
// make timesince a gvar?
TRACE_4("fnc_doFragTargeted IF", _fragRange, _timeSince, _isSubMunit, GVAR(enSubMunit));
if (_fragRange > 3 && _timeSince > 0.3 && {!_isSubMunit || {GVAR(enSubMunit) == 2}}) then {
_maxFrags = _maxFrags - ([_posASL, _fragVel, _fragRange, _maxFrags, _fragTypes, _modFragCount, _shotParents] call FUNC(doFragTargeted));
};
// make a gvar?
if (_timeSince > 0.2 && {GVAR(enSubMunit) != 0}) then {
[_posASL, _vel, _heightAGL, _fragTypes, _maxFrags, _shotParents] call FUNC(doFragRandom);
};
if (GVAR(reflectionsEnabled)) then {
[_posASL, _shellType] call FUNC(doReflections);
};

View File

@ -0,0 +1,71 @@
#include "script_component.hpp"
/*
* Author: Lambda.Tiger
* This function creates fragments randomly spreading out from an explosion to
* a maximum of 15
*
* Arguments:
* 0: Position of fragmenting projectile ASL <ARRAY>
* 1: Velocity of the fragmenting projectile <ARRAY>
* 2: Height (AGL) of the fragmenting projectile <SCALAR>
* 3: Type of fragments to generate
* 4: Remaining fragment budget <SCALAR>
* 5: Shot parent <ARRAY>
*
* Return Value:
* None
*
* Example:
* [getPosASL _proj, velocity _proj, 50, 50, [], 1, [player, player]] call ace_frag_fnc_doFragRandom;
*
* Public: No
*/
params [
"_posASL",
["_projVel", [0,0,0]],
["_heightAGL", 2, [123]],
["_fragType", [], [[]]],
["_fragCnt", 10, [123]],
["_shotPrnt", [objNull, objNull], [[]], [2]]
];
TRACE_5("fnc_doFragRandom", _posASL, _projVel, _heightAGL, _fragType, _fragCnt);
// See CfgAmmo for different frag types
private _hMode = switch (true) do {
case (_heightAGL > 10): {"_top"};
case (_heightAGL > 5): {"_hi"};
case (_heightAGL > 1.5): {"_mid"};
default {"_mid"};
};
// Select the cfgAmmo type
private _type = if (count _fragType > 0 &&
{"ace_frag_tiny" isEqualTo (_fragType#0)}) then {
QGVAR(def_tiny_)
} else {
QGVAR(def_small_)
};
_fragCnt = switch (true) do {
case (_fragCnt <= 5): {"5"};
case (_fragCnt <= 10): {"10"};
default {"15"};
};
// Spawn the fragment spawner
private _fragSpawner = createVehicle [_type + _fragCnt + _hMode, ASLToATL _posASL, [], 0, "CAN_COLLIDE"];
_fragSpawner setVelocity _projVel;
_fragSpawner setShotParents _shotParents;
#ifdef DEBUG_MODE_FULL
systemChat ("fragging, id: " + getObjectID _proj);
_fragSpawner addEventHandler [
"SubmunitionCreated",
{
params ["","_proj","_posASL"];
[_posASL] call FUNC(dev_sphereDraw);
[_proj, "green", true] call FUNC(dev_trackObj);
}
];
#endif

View File

@ -0,0 +1,175 @@
#include "script_component.hpp"
/*
* Author: Lambda.Tiger
* This function creates fragments targeted at specific entities, up to
* a configured maximum
*
* Arguments:
* 0: Position of fragmenting projectile ASL <ARRAY>
* 1: Velocity of the fragmenting projectile <ARRAY>
* 2: Maximum range of fragments to calculate <SCALAR>
* 3: Maximum number of fragments to produce <SCALAR>
* 4: Types of fragments <ARRAY>
* 5: A modified parameter used to calulate whether a framgent hits <SCALAR>
* 6: Shot parent <ARRAY>
*
* Return Value:
* None
*
* Example:
* [getPosASL _proj, velocity _proj, 50, 50, [], 1, [player, player]] call ace_frag_fnc_doFragTargeted;
*
* Public: No
*/
params [
"_posASL",
["_fragVel", 800, [123]],
["_fragRange", 50, [123]],
["_maxFrags", 20, [123]],
["_fragTypes", [], [[]]],
["_modFragCount", 1, [123]],
["_shotPrnt", [objNull, objNull], [[]], [2]]
];
TRACE_5("fnc_doFragTargeted", _posASL, _fragRange, _maxFrags, _fragTypes, _modFragCount);
if (_fragTypes isEqualTo []) then {
_fragTypes = [
QGVAR(tiny), QGVAR(tiny), QGVAR(tiny),
QGVAR(tiny_HD), QGVAR(tiny_HD), QGVAR(tiny_HD),
QGVAR(small), QGVAR(small), QGVAR(small), QGVAR(small),
QGVAR(small_HD), QGVAR(small_HD), QGVAR(small_HD), QGVAR(small_HD),
QGVAR(medium_HD), QGVAR(medium_HD), QGVAR(medium_HD), QGVAR(medium_HD), QGVAR(medium_HD)
];
};
private _objects = (ASLToATL _posASL) nearEntities [["Car", "Motorcycle", "Tank", "StaticWeapon", "CAManBase", "Air", "Ship"], _fragRange];
if (_objects isEqualTo []) exitWith {
TRACE_2("No nearby targets", _posASL, _fragRange);
0;
};
// grab crews and add them in so that targets stay approx. sorted by distance
{
private _crew = (crew _x);
if (count _crew > 1) then {
private _arr = [_x];
{
_arr pushBackUnique _x;
} forEach _crew;
_objects set [_forEachIndex, _arr];
};
} forEach _objects;
_objects = flatten _objects; // flatten out sub arrays
TRACE_3("Targets found", _posASL, _fragRange, count _objects);
// limit number of fragments per direction (2D) to 10 using _fragArcs
private _fragArcs = createHashMap;
private _fragCount = 0; // limit of # of fragments to _maxFrags
{
if (!alive _x) then {continue};
private _target = _x;
#ifdef DEBUG_MODE_DRAWFRAG
[_target, false] call FUNC(dev_trackHitBox);
#endif
// Calculate volume and height of target
private _vol = 1.5;
private _height = 0;
private _crossSectionArea = 1;
private _isPerson = _target isKindOf "CAManBase";
if (_isPerson) then {
private _stance = stance _target;
switch (true) do {
case (_stance isEqualTo "STAND"): {_height = 1.9; _crossSectionArea = 1.5;};
case (_stance isEqualTo "CROUCH"): {_height = 1.2; _crossSectionArea = 1;};
default {_height = 0.5; _crossSectionArea = 0.75;};
};
} else {
private _boxParams = boundingBoxReal [_target, "FireGeometry"];
_boxParams params ["_pointA", "_pointB"];
private _dims = _pointB vectorDiff _pointA;
_vol = (_dims#0) * (_dims#1) * (_dims#2);
_crossSectionArea = (_dims#1)*(_dims#2);
_height = _dims#2;
};
if (_vol <= 0.5) then {continue}; // too small => exit
private _distance = _target distance _posASL;
// calculate chance to be hit by a fragment
private _fragChance = _crossSectionArea*_modFragCount/(_distance^2);
private _count = if (_fragChance > 1) then {
3 min (floor _fragChance);
} else {
[0, 1] select (GVAR(atLeastOne) || {random 1 < _fragChance});
};
if (_count == 0) then {TRACE_2("fragments",_fragChance,_count); continue};
// Approximate offset to hit including speed & gravity
private _locFragVel = _fragVel * (1 - random 0.5);
private _tof = _distance / _locFragVel;
private _targetPos = (velocity _target vectorMultiply _tof) vectorAdd [0, 0, 9.81 / 2 * _tof ^ 2];
// handle limiting fragments per dewgree arc
private _dir = floor (_posASL getDir _target);
private _fragPerArc = _fragArcs getOrDefault [_dir, 0];
if (_fragPerArc > 10) then {
continue;
} else {
_fragArcs set [_dir, _fragPerArc + _count];
};
// actual target pos for fragment to hit
if _isPerson then {
private _hitPoint = selectRandom _FRAG_HITPOINTS;
private _hitPointPos = _target selectionPosition [_hitPoint, "HitPoints", "AveragePoint"];
_targetPos = _target modelToWorldWorld _hitPointPos;
} else {
_targetPos = getPosASL _target vectorAdd [
-0.5 + random 1,
-0.5 + random 1,
(0.1 + random 0.4) * _height
];
};
// select a fragment / submunition frag spawner
private _fragSpawner = selectRandom _fragTypes;
if (_count > 1) then {
_fragSpawner = _fragSpawner + "_spawner_" + str _count + (switch (true) do {
case (_distance < 10): {"_short"};
case (_distance < 20): {"_mid"};
default {"_far"};
});
};
TRACE_4("fragments",_fragSpawner,_fragChance,_distance,_locFragVel);
// Create fragment
private _vecDir = _posASL vectorFromTo _targetPos;
private _fragObj = createVehicle [_fragSpawner, ASLtoATL _posASL, [], 0, "CAN_COLLIDE"];
_fragObj setVectorDir _vecDir;
_fragObj setVelocity (_vecDir vectorMultiply _locFragVel);
_fragObj setShotParents _shotPrnt;
#ifdef DEBUG_MODE_DRAWFRAG
[_fragObj, "purple", true] call FUNC(dev_trackObj);
[_targetPos, "orange"] call FUNC(dev_sphereDraw);
#endif
_fragCount = _fragCount + _count;
if (_fragCount >= _maxFrags) then {
TRACE_2("maxFrags", _fragCount, _maxFrags);
break
};
} forEach _objects;
#ifdef DEBUG_MODE_FULL
systemChat ("fragCount cnt: " + str _fragCount);
TRACE_1("fragCount",_fragCount);
#endif
_fragCount

View File

@ -1,4 +1,4 @@
#include "..\script_component.hpp"
#include "script_component.hpp"
/*
* Author: ACE-Team
* Dev things

View File

@ -1,4 +1,4 @@
#include "..\script_component.hpp"
#include "script_component.hpp"
/*
* Author: ACE-Team
* Dev things
@ -14,126 +14,97 @@
*
* Public: No
*/
params ["_projectile", "_hitObj", "", "_lPosASL", "_lVel", "", "", "" ,"_surfaceType"];
#define WEIGHTED_SIZE [QGVAR(spall_small), 4, QGVAR(spall_medium), 3, QGVAR(spall_large), 2, QGVAR(spall_huge), 1]
if (!isNil "_hitObj" && {_hitObj isKindOf "man"}) exitWith {};
params ["_hitData", "_hitPartDataIndex"];
private _initialData = GVAR(spallHPData) select (_hitData select 0);
_initialData params ["_hpId", "_object", "_roundType", "_round", "_curPos", "_velocity"];
if ((isNil "_lPosASL") || {!(_lPosASL isEqualTypeArray [0,0,0])}) exitWith {WARNING_1("Problem with hitPart data - bad pos [%1]",_lPosASL);};
private _hpData = (_hitData select 1) select _hitPartDataIndex;
private _objectHit = _hpData param [0, objNull];
TRACE_1("",_objectHit);
if ((isNil "_objectHit") || {isNull _objectHit}) exitWith {WARNING_1("Problem with hitPart data - bad object [%1]",_objectHit);};
_objectHit removeEventHandler ["hitPart", _hpId];
private _caliber = getNumber (configFile >> "CfgAmmo" >> _roundType >> "caliber");
private _explosive = getNumber (configFile >> "CfgAmmo" >> _roundType >> "explosive");
private _idh = getNumber (configFile >> "CfgAmmo" >> _roundType >> "indirectHitRange");
if !(_caliber >= 2.5 || {(_explosive > 0 && {_idh >= 1})}) exitWith {};
// ACE_player sideChat format ["BBBB"];
private _exit = false;
private _vm = 1;
private _oldVelocity = vectorMagnitude _velocity;
private _curVelocity = vectorMagnitude (velocity _round);
if (alive _round) then {
private _diff = _velocity vectorDiff (velocity _round);
private _polar = _diff call CBA_fnc_vect2polar;
// ACE_player sideChat format ["polar: %1", _polar];
if (abs (_polar select 1) > 45 || {abs (_polar select 2) > 45}) then {
if (_caliber < 2.5) then {
// ACE_player sideChat format ["exit!"];
_exit = true;
} else {
SUB(_vm,_curVelocity / _oldVelocity);
};
private _posASL = _lPosASL;
private _vel = [0, 0, 0];
private _lVelUnit = vectorNormalized _lVel;
private _velMod = 1;
if (alive _projectile) then {
_vel = velocity _projectile;
_posASL = getPosASL _projectile;
// Dot product math
private _diffAngle = acos (_lVelUnit vectorDotProduct vectorNormalized _vel);
if (abs _diffAngle > 45) then {
SUB(_velMod, (vectorMagnitude _vel) / (vectorMagnitude _lVel));
};
_projectile setVariable [QGVAR(lastSpallTime), diag_tickTime ];
};
if (_exit) exitWith {};
private _unitDir = vectorNormalized _velocity;
private _pos = _hpData select 3;
private _spallPos = [];
if ((isNil "_pos") || {!(_pos isEqualTypeArray [0,0,0])}) exitWith {WARNING_1("Problem with hitPart data - bad pos [%1]",_pos);};
for "_i" from 0 to 100 do {
private _pos1 = _pos vectorAdd (_unitDir vectorMultiply (0.01 * _i));
private _pos2 = _pos vectorAdd (_unitDir vectorMultiply (0.01 * (_i + 1)));
// _data = [nil, nil, nil, 1, [[ASLtoATL _pos1, 1], [ASLtoATL _pos2, 1]]];
// NOU_TRACES pushBack _data;
if (!lineIntersects [_pos1, _pos2]) exitWith {
// ACE_player sideChat format ["FOUND!"];
_spallPos = _pos2;
};
//** start calculating where the spalling should come **//
private _unitStep = _lVelUnit vectorMultiply 0.05;
private _spallPos = +_lPosASL;
// exit if we hit the ground
if (terrainIntersectASL [_lPosASL vectorAdd _unitStep, _lPosASL]) exitWith {};
// step through
for "_i" from 1 to 20 do
{
private _nPos = _spallPos vectorAdd _unitStep;
if (!lineIntersects [_spallPos, _nPos]) then {break};
_spallPos = +_nPos;
};
if (_spallPos isEqualTo []) exitWith {};
private _spallPolar = _velocity call CBA_fnc_vect2polar;
#ifdef DEBUG_MODE_FULL
[_spallPos, "orange"] call FUNC(dev_sphereDraw);
[_lPosASL, "orange"] call FUNC(dev_sphereDraw);
#endif
// find last intersect with the object
private _ammo = typeOf _projectile;
private _explosive = getNumber (configFile >> "CfgAmmo" >> _ammo >> "explosive");
#ifdef DEBUG_MODE_FULL
private _dv = vectorMagnitude _lVel - vectorMagnitude _vel;
private _caliber = getNumber (configFile >> "cfgAmmo" >> _ammo >> "caliber");
systemChat ("dV: " + str _dv + ", caliber: " + str _caliber + ", product: " + str (_caliber*_dv));
#endif
if (_explosive > 0) then {
// ACE_player sideChat format ["EXPLOSIVE!"];
private _warn = false;
private _c = getNumber (configFile >> "CfgAmmo" >> _roundType >> QGVAR(CHARGE));
if (_c == 0) then {_c = 1; _warn = true;};
private _m = getNumber (configFile >> "CfgAmmo" >> _roundType >> QGVAR(METAL));
if (_m == 0) then {_m = 2; _warn = true;};
private _k = getNumber (configFile >> "CfgAmmo" >> _roundType >> QGVAR(GURNEY_K));
if (_k == 0) then {_k = 1 / 2; _warn = true;};
private _gC = getNumber (configFile >> "CfgAmmo" >> _roundType >> QGVAR(GURNEY_C));
if (_gC == 0) then {_gC = 2440; _warn = true;};
//private _warn = false;
private _c = getNumber (configFile >> "CfgAmmo" >> _ammo >> "ace_frag_CHARGE");
if (_c == 0) then {_c = 1}; //; _warn = true;};
private _m = getNumber (configFile >> "CfgAmmo" >> _ammo >> "ace_frag_METAL");
if (_m == 0) then {_m = 2;};// _warn = true;};
private _k = getNumber (configFile >> "CfgAmmo" >> _ammo >> "ace_frag_GURNEY_K");
if (_k == 0) then {_k = 1 / 2;};// _warn = true;};
private _gC = getNumber (configFile >> "CfgAmmo" >> _ammo >> "ace_frag_GURNEY_C");
if (_gC == 0) then {_gC = 2440};// _warn = true;};
// if (_warn) then {
// WARNING_1("Ammo class %1 lacks proper explosive properties definitions for frag!",_roundType); //TODO: turn this off when we get closer to release
// };
private _fragPower = (((_m / _c) + _k) ^ - (1 / 2)) * _gC;
_spallPolar set [0, _fragPower * 0.66];
_velMod = (((_m / _c) + _k) ^ - (1 / 2)) * _gC * 0.66 * _velMod;
};
// diag_log text format ["SPALL POWER: %1", _spallPolar select 0];
private _spread = 15 + (random 25);
private _spallCount = 5 + (random 10);
TRACE_1("",_spallCount);
for "_i" from 1 to _spallCount do {
private _elev = ((_spallPolar select 2) - _spread) + (random (_spread * 2));
private _dir = ((_spallPolar select 1) - _spread) + (random (_spread * 2));
if (abs _elev > 90) then {
ADD(_dir,180);
};
_dir = _dir % 360;
private _vel = (_spallPolar select 0) * 0.33 * _vm;
_vel = (_vel - (_vel * 0.25)) + (random (_vel * 0.5));
// range of spread 15-40
// N rounds 5-15
// speed from 0.75-1.5
// range of spread 5-10
// N rounds 3-8
// speed from 0.75-1.5
private _velScalar = 0.33 * _velMod;
private _spallFragVect = [_vel, _dir, _elev] call CBA_fnc_polar2vect;
private _fragment = (selectRandomWeighted WEIGHTED_SIZE) createVehicleLocal [0,0,10000];
_fragment setPosASL _spallPos;
_fragment setVelocity _spallFragVect;
private _fragSpawner = QGVAR(base) createVehicleLocal [0,0,12345];
_fragSpawner setPosASL _spallPos;
_fragSpawner setVectorDirandUp [vectorDir _projectile, vectorUp _projectile];
_fragSpawner setVelocity (_lVelUnit vectorMultiply _velScalar);
#ifdef DRAW_FRAG_INFO
[ACE_player, _fragment, [1, 0.5, 0, 1]] call FUNC(dev_addTrack);
#endif
};
_spread = 5 + (random 5);
_spallCount = 3 + (random 5);
for "_i" from 1 to _spallCount do {
private _elev = ((_spallPolar select 2) - _spread) + (random (_spread * 2));
private _dir = ((_spallPolar select 1) - _spread) + (random (_spread * 2));
if (abs _elev > 90) then {
ADD(_dir,180);
};
_dir = _dir % 360;
private _vel = (_spallPolar select 0) * 0.55 * _vm;
_vel = (_vel - (_vel * 0.25)) + (random (_vel * 0.5));
private _spallFragVect = [_vel, _dir, _elev] call CBA_fnc_polar2vect;
private _fragment = (selectRandomWeighted WEIGHTED_SIZE) createVehicleLocal [0, 0, 10000];
_fragment setPosASL _spallPos;
_fragment setVelocity _spallFragVect;
#ifdef DRAW_FRAG_INFO
[ACE_player, _fragment, [1, 0, 0, 1]] call FUNC(dev_addTrack);
#endif
};
#ifdef DEBUG_MODE_FULL
_fragSpawner addEventHandler [
"SubmunitionCreated",
{
params ["", "_subProj"];
[_subProj] call FUNC(dev_addRound);
}
];
#endif

View File

@ -0,0 +1,109 @@
#include "script_component.hpp"
/*
* Author: Lambda.Tiger
* This function creates spalling if the hit slowed the speed down enough.
*
* Arguments:
* Arguments are the same as BI's "HitPart" EH:
* https://community.bistudio.com/wiki/Arma_3:_Event_Handlers#HitPart
*
* Return Value:
* None
*
* Example:
* [BIS_HITPART_EH_ARGS] call ace_frag_fnc_doSpallMomentum;
*
* Public: No
*/
params ["_projectile", "_hitObj", "", "_lPosASL", "_lVel", "", "", "" ,"_surfaceType"];
if (CBA_missionTime - GVAR(lastHitTick) < ACE_FRAG_SPALL_HOLDOFF) exitWith {};
if (_hitObj isNotEqualTo objNull && {_hitObj isKindOf "man"}) exitWith {};
if ((isNil "_lPosASL") || {!(_lPosASL isEqualTypeArray [0,0,0])}) exitWith {
TRACE_1("Problem with hitPart data - bad pos",_lPosASL);
};
private _vel = if (alive _projectile) then {
_velocity _projectile;
} else {
[0, 0, 0]
};
private _lVelUnit = vectorNormalized _lVel;
// Find spall speed / fragment
private _ammo = typeOf _projectile;
private _dV = vectorMagnitude _lVel - vectorMagnitude _vel;
private _caliber = getNumber (configFile >> "cfgAmmo" >> _ammo >> "caliber"); // !*! optimize this later?
private _deltaMomentum = 0.4 * _caliber * sqrt( _dV * 0.032 );
TRACE_3("found speed",_dV,_caliber,_deltaMomentum);
if (_deltaMomentum < 1) exitWith {
TRACE_1("lowImpulse",_ammo);
};
//** start calculating where the spalling should come !*! could be better **//
private _unitStep = _lVelUnit vectorMultiply 0.05;
private _spallPos = +_lPosASL;
// exit if we hit the ground
if (terrainIntersectASL [_lPosASL vectorAdd _unitStep, _lPosASL]) exitWith {
TRACE_3("terrainIntersect",_lPosASL,_unitStep,_lPosASL);
};
// step through
for "_i" from 1 to 20 do
{
private _nPos = _spallPos vectorAdd _unitStep;
if (!lineIntersects [_spallPos, _nPos]) then { _spallPos = _nPos; break};
_spallPos = +_nPos;
};
#ifdef DEBUG_MODE_FULL
[_spallPos, "green"] call FUNC(dev_sphereDraw);
[_lPosASL vectorAdd _lVelUnit, "orange"] call FUNC(dev_sphereDraw);
[_lPosASL, "orange"] call FUNC(dev_sphereDraw);
private _str = GVAR(hitLog) getOrDefault [str _surfaceType, "["];
_str =_str + str [_dV, _caliber, abs vectorMagnitude (_lPosASL vectorDiff _spallPos)] + ";";
GVAR(hitLog) set [str _surfaceType, _str];
if (_deltaMomentum < 2) exitWith {};
#endif
//***** Passed all other exit withs, performance o'clock */
GVAR(lastHitTick) = CBA_missionTime;
//***** Select spalled fragments **//
// diag_log text format ["SPALL POWER: %1", _spallPolar select 0];
// range of spread 15-40
// N rounds 5-15
// speed from 0.75-1.5
// range of spread 5-10
// N rounds 3-8
// speed from 0.75-1.5
private _fragSpawnType = switch (true) do
{
case (_deltaMomentum < 3): { QGVAR(spall_tiny) };
case (_deltaMomentum < 5): { QGVAR(spall_small) };
case (_deltaMomentum < 8): { QGVAR(spall_medium) };
case (_deltaMomentum < 11): { QGVAR(spall_large) };
default { QGVAR(spall_huge) };
};
//***** Spawn spalled fragments
private _fragSpawner = createVehicleLocal [_fragSpawnType, ASLToATL _spallPos, [], 0, "CAN_COLLIDE"];
_fragSpawner setVectorDirandUp [vectorDir _projectile, vectorUp _projectile];
_fragSpawner setVelocity _lVelUnit;
#ifdef DEBUG_MODE_FULL
systemChat ("bSpd: " + str speed _fragSpawner + ", frag: " + _fragSpawnType + ", dm: " + str _deltaMomentum);
_fragSpawner addEventHandler [
"SubmunitionCreated",
{
params ["", "_subProj"];
[_subProj] call FUNC(dev_addRound);
}
];
#endif

View File

@ -1,6 +1,6 @@
#include "..\script_component.hpp"
#include "script_component.hpp"
/*
* Author: nou, jaynus, PabstMirror
* Author: nou, jaynus, PabstMirror, Lambda.Tiger
* Called from the unified fired EH for all.
* If spall is not enabled (default), then cache and only track those that will actually trigger fragmentation.
*
@ -17,42 +17,44 @@
*/
//IGNORE_PRIVATE_WARNING ["_unit", "_weapon", "_muzzle", "_mode", "_ammo", "_magazine", "_projectile", "_vehicle", "_gunner", "_turret"];
TRACE_10("firedEH:",_unit, _weapon, _muzzle, _mode, _ammo, _magazine, _projectile, _vehicle, _gunner, _turret);
private _shouldAdd = GVAR(cacheRoundsTypesToTrack) getVariable _ammo;
if (isNil "_shouldAdd") then {
TRACE_1("no cache for round",_ammo);
//Read configs and test if it would actually cause a frag, using same logic as FUNC(pfhRound)
private _skip = getNumber (configFile >> "CfgAmmo" >> _ammo >> QGVAR(skip));
private _explosive = getNumber (configFile >> "CfgAmmo" >> _ammo >> "explosive");
private _indirectRange = getNumber (configFile >> "CfgAmmo" >> _ammo >> "indirectHitRange");
private _force = getNumber (configFile >> "CfgAmmo" >> _ammo >> QGVAR(force));
private _fragPower = getNumber (configFile >> "CfgAmmo" >> _ammo >> "indirecthit") * (sqrt (getNumber (configFile >> "CfgAmmo" >> _ammo >> "indirectHitRange")));
_shouldAdd = (_skip == 0) && {(_force == 1) || {_explosive > 0.5 && {_indirectRange >= 4.5} && {_fragPower >= 35}}};
if (GVAR(spallEnabled) && {!_shouldAdd}) then {
private _caliber = getNumber (configFile >> "CfgAmmo" >> _ammo >> "caliber");
if !(_caliber >= 2.5 || {(_explosive > 0 && {_indirectRange >= 1})}) exitWith {}; // from check in doSpall: line 34
TRACE_1("Won't frag, but will spall",_caliber);
_shouldAdd = true;
};
TRACE_6("Setting Cache",_skip,_explosive,_indirectRange,_force,_fragPower,_shouldAdd);
GVAR(cacheRoundsTypesToTrack) setVariable [_ammo, _shouldAdd];
if (isNil "_ammo" ||
{_ammo isEqualTo "" ||
{isNil "_projectile" ||
{isNull _projectile}}}) exitWith {
WARNING("bad ammo or projectile");
};
if (_shouldAdd) then {
// firedMan will have nil "_gunner", so just check _unit; for firedVehicle we want to check _gunner
private _localShooter = if (isNil "_gunner") then {local _unit} else {local _gunner};
TRACE_4("",_localShooter,_unit,_ammo,_projectile);
if (!_localShooter) exitWith {};
if (_weapon == "Put") exitWith {}; // Ignore explosives placed without ace_explosives
/******* _shouldFrag format *****/
// 0: doFragmnent - will the piece fragment
// 1: hasSubmuntion - will the round create submunitions
private _shouldFrag = _ammo call FUNC(shouldFrag);
_shouldFrag params ["_doFrag", "_doSubmunit"];
// Skip if less than 0.5 second from last shot
if ((CBA_missionTime - (_unit getVariable [QGVAR(lastTrack), -1])) < 0.5) exitWith {};
_unit setVariable [QGVAR(lastTrack), CBA_missionTime];
[_unit, _ammo, _projectile] call FUNC(addPfhRound);
if (_doFrag) then {
// wait for frag damage to kill units before spawning fragments
_projectile addEventHandler ["Explode", {
if (isServer) then {
[FUNC(doFrag), [_this]] call CBA_fnc_execNextFrame;
} else {
[QGVAR(frag_eh), [_this]] call CBA_fnc_serverEvent;
};
}
];
};
if (_doSubmunit && {GVAR(enSubMunit)> 0}) then {
_projectile addEventHandler ["SubmunitionCreated", {_this call FUNC(submunition)}];
};
private _shouldSpall = _ammo call FUNC(shouldSpall);
if (GVAR(spallEnabled) && {_shouldSpall}) then
{_projectile addEventHandler [
"HitPart",
{
[LINKFUNC(doSpallMomentum), _this] call CBA_fnc_execNextFrame;
[QGVAR(spall_eh), [_this]] call CBA_fnc_serverEvent;
}
];
};

View File

@ -0,0 +1,86 @@
#include "script_component.hpp"
/*
* Author: Lambda.Tiger
* This function returns fragmentation parameters for a specific
* ammo type.
*
* Arguments:
* 0: _ammo <STRING> - cfgAmmo type of ammo to check
*
* Return Value:
* _ammoInfo <ARRAY>
* 0: _fragRange - search range for fragments
* 1: _fragVel - gurney equation calculated velocity
* 2: _fragTypes - array of fragment types
* 3: _fragCount - modified frag count used under assumptions
* of spherical fragmentation
*
* Example:
* ["B_556x45_Ball"] call ace_frag_fnc_fragInfo;
*
* Public: No
*/
params ["_ammo"];
private _ammoInfo = GVAR(fragInfoCache) get _ammo;
if !(isNil "_ammoInfo") exitWith {_ammoInfo};
private _fragTypes = [];
private _warn = false;
if (isArray (configFile >> "cfgAmmo" >> _ammo >> QGVAR(CLASSES))) then {
_fragTypes = getArray (configFile >> "cfgAmmo" >> _ammo >> QGVAR(CLASSES));
} else {
_warn = true;
};
/************ Gurney equation notes *****************/
// see https://en.wikipedia.org/wiki/Gurney_equations
//
// GURNEY_K is the constant added to _m/_c
// GURNEY_C = sqrt(2E)
//
// _c = 185; // grams of comp-b
// _m = 210; // grams of fragmentating metal
// _k = 3/5; // spherical K factor
// _gC = 2843; // Gurney constant of comp-b in /ms
// _c = 429; // grams of tritonal
// _m = 496; // grams of fragmentating metal
// _k = 1/2; // cylindrical K factor
// _gC = 2320; // Gurney constant of tritonal in m/s
// Equation - 0.8 for empirical 80% speed
// 0.8 * (((_m / _c) + _k) ^ - (1 / 2)) * _gC;
// or 0.8 * _gC * sqrt (_c /(_m + _c * _k)); (slightly faster to compute)
private _c = getNumber (configFile >> "cfgAmmo" >> _ammo >> QGVAR(CHARGE));
if (_c == 0) then {_c = 1; _warn = true;};
private _m = getNumber (configFile >> "cfgAmmo" >> _ammo >> QGVAR(METAL));
if (_m == 0) then {_m = 2; _warn = true;};
private _k = getNumber (configFile >> "cfgAmmo" >> _ammo >> QGVAR(GURNEY_K));
if (_k == 0) then {_k = 0.8; _warn = true;};
private _gC = getNumber (configFile >> "cfgAmmo" >> _ammo >> QGVAR(GURNEY_C));
if (_gC == 0) then {_gC = 2440; _warn = true;};
private _fragCount = getNumber (configFile >> "cfgAmmo" >> _ammo >> QGVAR(fragCount));
if (_fragCount == 0) then {_fragCount = 200; _warn = true;};
if (_warn) then {
INFO_1("Ammo class %1 lacks proper explosive properties definitions for frag!",_ammo);
};
/********************** _ammoInfo format *************************/
// 0: _fragRange - search range for fragments
// 1: _fragVel - gurney equation calculated velocity
// 2: _fragTypes - array of fragment types
// 3: _fragCount - modified frag count used under assumptions
// of spherical fragmentation
_ammoInfo = [
sqrt (_fragCount / (4 * pi * 0.005)),
0.8 * _gC * sqrt (_c / (_m + _c * _k)),
_fragTypes,
_fragCount / 4 / pi
];
GVAR(fragInfoCache) set [_ammo, _ammoInfo];
_ammoInfo

View File

@ -1,194 +0,0 @@
#include "..\script_component.hpp"
/*
* Author: Jaynus, NouberNou
* Server func to create the fragmentation for a round.
*
* Arguments:
* 0: Last Position (ASL) <ARRAY>
* 1: Velocity <ARRAY>
* 2: Ammo Classname <STRING>
*
* Return Value:
* None
*
* Example:
* [[], [], "handGrenade"] call ace_frag_fnc_frago
*
* Public: No
*/
#define FRAG_VEC_VAR 0.004
#define MAX_FRAG_COUNT 50
BEGIN_COUNTER(frago);
params ["_lastPos", "_lastVel", "_shellType"];
TRACE_3("frago",_lastPos,_lastVel,_shellType);
// Limit max frag count if there was a recent frag
private _maxFrags = round (MAX_FRAG_COUNT * linearConversion [0.1, 1.5, (CBA_missionTime - GVAR(lastFragTime)), 0.1, 1, true]);
TRACE_2("",_maxFrags,CBA_missionTime - GVAR(lastFragTime));
GVAR(lastFragTime) = CBA_missionTime;
private _fragTypes = [
QGVAR(tiny), QGVAR(tiny), QGVAR(tiny),
QGVAR(tiny_HD), QGVAR(tiny_HD), QGVAR(tiny_HD),
QGVAR(small), QGVAR(small), QGVAR(small), QGVAR(small),
QGVAR(small_HD), QGVAR(small_HD), QGVAR(small_HD), QGVAR(small_HD),
QGVAR(medium_HD), QGVAR(medium_HD), QGVAR(medium_HD), QGVAR(medium_HD), QGVAR(medium_HD)
];
private _warn = false;
if (isArray (configFile >> "CfgAmmo" >> _shellType >> QGVAR(CLASSES))) then {
_fragTypes = getArray (configFile >> "CfgAmmo" >> _shellType >> QGVAR(CLASSES));
} else {
_warn = true;
};
private _indirectHitRange = getNumber(configFile >> "CfgAmmo" >> _shellType >> "indirecthitrange");
private _fragRange = 20 * _indirectHitRange * 4;
// _c = 185; // grams of comp-b
// _m = 210; // grams of fragmentating metal
// _k = 3/5; // spherical K factor
// _gC = 2843; // Gurney constant of comp-b in /ms
// _c = 429; // grams of tritonal
// _m = 496; // grams of fragmentating metal
// _k = 1/2; // spherical K factor
// _gC = 2320; // Gurney constant of tritonal in /ms
private _c = getNumber (configFile >> "CfgAmmo" >> _shellType >> QGVAR(CHARGE));
if (_c == 0) then {_c = 1; _warn = true;};
private _m = getNumber (configFile >> "CfgAmmo" >> _shellType >> QGVAR(METAL));
if (_m == 0) then {_m = 2; _warn = true;};
private _k = getNumber (configFile >> "CfgAmmo" >> _shellType >> QGVAR(GURNEY_K));
if (_k == 0) then {_k = 0.5; _warn = true;};
private _gC = getNumber (configFile >> "CfgAmmo" >> _shellType >> QGVAR(GURNEY_C));
if (_gC == 0) then {_gC = 2440; _warn = true;};
if (_warn) then {
INFO_1("Ammo class %1 lacks proper explosive properties definitions for frag!",_shellType);
};
// Gunery equation is for a non-fragmenting metal, imperical value of 80% represents fragmentation
private _fragPower = 0.8 * (((_m / _c) + _k) ^ - (1 / 2)) * _gC;
private _atlPos = ASLtoATL _lastPos;
private _fragPowerRandom = _fragPower * 0.5;
if ((_atlPos select 2) < 0.5) then {
_lastPos vectorAdd [0, 0, 0.5];
};
private _objects = _atlPos nearEntities [["Car", "Motorcycle", "Tank", "StaticWeapon", "CAManBase", "Air", "Ship"], _fragRange];
// Add unique crews in faster way
{
{
_objects pushBackUnique _x;
} forEach (crew _x);
} forEach _objects;
TRACE_2("",_fragRange,count _objects);
private _fragCount = 0;
private _fragArcs = [];
_fragArcs set [360, 0];
private _doRandom = true;
if (_objects isNotEqualTo []) then {
if (GVAR(reflectionsEnabled)) then {
[_lastPos, _shellType] call FUNC(doReflections);
};
{
private _target = _x;
if (alive _target) then {
(boundingBox _target) params ["_boundingBoxA", "_boundingBoxB"];
private _cubic = ((abs (_boundingBoxA select 0)) + (_boundingBoxB select 0)) * ((abs (_boundingBoxA select 1)) + (_boundingBoxB select 1)) * ((abs (_boundingBoxA select 2)) + (_boundingBoxB select 2));
if (_cubic <= 1) exitWith {};
// _doRandom = true;
private _targetVel = velocity _target;
private _targetPos = getPosASL _target;
private _distance = _targetPos vectorDistance _lastPos;
private _add = ((_boundingBoxB select 2) / 2) + ((((_distance - (_fragpower / 8)) max 0) / _fragPower) * 10);
_targetPos = _targetPos vectorAdd [
(_targetVel select 0) * (_distance / _fragPower),
(_targetVel select 1) * (_distance / _fragPower),
_add
];
private _baseVec = _lastPos vectorFromTo _targetPos;
private _dir = floor (_baseVec call CBA_fnc_vectDir);
private _currentCount = RETDEF(_fragArcs select _dir,0);
if (_currentCount < 10) then {
private _count = ceil (random (sqrt (_m / 1000)));
private _vecVar = FRAG_VEC_VAR;
if (!(_target isKindOf "Man")) then {
ADD(_vecVar,(sqrt _cubic) / 2000);
if ((crew _target) isEqualTo [] && {_count > 0}) then {
_count = 0 max (_count / 2);
};
};
for "_i" from 1 to _count do {
private _vec = _baseVec vectorDiff [
(_vecVar / 2) + (random _vecVar),
(_vecVar / 2) + (random _vecVar),
(_vecVar / 2) + (random _vecVar)
];
private _fp = _fragPower - (random (_fragPowerRandom));
private _vel = _vec vectorMultiply _fp;
private _fragObj = (selectRandom _fragTypes) createVehicleLocal [0,0,10000];
// TRACE_4("targeted",_fp, typeOf _fragObj,_lastPos vectorDistance _targetPos,typeOf _x);
_fragObj setPosASL _lastPos;
_fragObj setVectorDir _vec;
_fragObj setVelocity _vel;
#ifdef DRAW_FRAG_INFO
[ACE_player, _fragObj, [1,0,0,1]] call FUNC(dev_addTrack);
#endif
INC(_fragCount);
INC(_currentCount);
};
_fragArcs set [_dir, _currentCount];
};
};
if (_fragCount > _maxFrags) exitWith {};
} forEach _objects;
TRACE_1("targeted",_fragCount);
if (_fragCount > _maxFrags) exitWith {};
private _randomCount = ceil ((_maxFrags - _fragCount) * 0.35);
TRACE_1("",_randomCount);
private _sectorSize = 360 / (_randomCount max 1);
if (_doRandom) then {
for "_i" from 1 to _randomCount do {
// Distribute evenly
private _sectorOffset = 360 * (_i - 1) / (_randomCount max 1);
private _randomDir = random (_sectorSize);
_vec = [cos (_sectorOffset + _randomDir), sin (_sectorOffset + _randomDir), sin (30 - (random 45))];
_fp = (_fragPower - (random (_fragPowerRandom)));
_vel = _vec vectorMultiply _fp;
_fragObj = (selectRandom _fragTypes) createVehicleLocal [0, 0, 10000];
_fragObj setPosASL _lastPos;
_fragObj setVectorDir _vec;
_fragObj setVelocity _vel;
#ifdef DRAW_FRAG_INFO
[ACE_player, _fragObj, [1,0.5,0,1]] call FUNC(dev_addTrack);
#endif
INC(_fragCount);
};
};
};
TRACE_1("total created",_fragCount);
END_COUNTER(frago);

View File

@ -0,0 +1,51 @@
#include "script_component.hpp"
/*
* Author: Lambda.Tiger
* Adds setting defined blacklisted rounds to blacklist
*
* Arguments:
* Mne
*
* Return Value:
* None
*
* Example:
* [] call ace_frag_fnc_addBlackList
*
* Public: No
*/
TRACE_1("Beginning blacklist init", GVAR(BlackList));
if (!ADDON) then {
[FUNC(initBlackList), [], 1] call CBA_fnc_waitAndExecute;
};
// could improve text parsing
private _convArray = parseSimpleArray GVAR(BlackList);
if (count _convArray == 0 ) exitWith {
TRACE_1("Empty blacklist", _convArray);
};
private _errors = 0;
private _items = count _convArray;
for "_i" from 0 to _items - 1 do {
private _ammo = _convArray#_i;
if (typeName _ammo isNotEqualTo "STRING") then {
INFO_1("Improper ammo string at index %1", _i);
INC(_errors);
continue;
};
if (!isClass (configFile >> "cfgAmmo" >> _ammo)) then {
INFO_1("Ammo class: %1 does not exist", str _ammo);
INC(_errors);
continue;
};
GVAR(shouldFragCache) set [_convArray#_i, [false, false]];
};
INFO_2("Initialized blacklist. Total items found: %1, number of items failed: %2", _items, _errors);

View File

@ -1,56 +0,0 @@
#include "..\script_component.hpp"
/*
* Author: jaynus
* Master single PFH abstraction for all rounds being tracked by frag/spall.
*
* Arguments:
* None
*
* Return Value:
* None
*
* Example:
* call ace_frag_fnc_masterPFH
*
* Public: No
*/
BEGIN_COUNTER(PFH);
// Fast exit if nothing to do
if (GVAR(objects) isEqualTo []) exitWith {END_COUNTER(PFH);};
private _gcIndex = [];
private _iter = 0;
private _objectCount = count GVAR(objects);
while {_objectCount > 0 && {_iter < (GVAR(maxTrackPerFrame) min _objectCount)}} do {
if (GVAR(lastIterationIndex) >= _objectCount) then {
GVAR(lastIterationIndex) = 0;
};
private _object = GVAR(objects) select GVAR(lastIterationIndex);
if (!isNil "_object") then {
private _args = GVAR(arguments) select GVAR(lastIterationIndex);
if (!(_args call FUNC(pfhRound))) then {
_gcIndex pushBack GVAR(lastIterationIndex); // Add it to the GC if it returns false
};
};
INC(_iter);
INC(GVAR(lastIterationIndex));
};
// Clean up dead object references
private _deletionCount = 0;
{
TRACE_1("GC Projectile", _x);
private _deleteIndex = _x - _deletionCount;
GVAR(objects) deleteAt _deleteIndex;
GVAR(arguments) deleteAt _deleteIndex;
INC(_deletionCount);
} forEach _gcIndex;
END_COUNTER(PFH);

View File

@ -1,59 +0,0 @@
#include "..\script_component.hpp"
/*
* Author: ACE-Team
*
*
* Arguments:
* None
*
* Return Value:
* None
*
* Example:
* call ace_frag_fnc_pfhRound
*
* Public: No
*/
params ["_round", "_lastPos", "_lastVel", "_shellType", "_firedFrame", "_firedPos", "_doSpall", "_spallTrack", "_foundObjectHPIds", "_skip", "_explosive", "_indirectRange", "_force", "_fragPower"];
if (_round in GVAR(blackList)) exitWith {
false
};
if (!alive _round) exitWith {
if ((diag_frameno - _firedFrame) > 1) then { //skip if deleted within a single frame
if (_skip == 0) then {
if ((_explosive > 0.5 && {_indirectRange >= 4.5} && {_fragPower >= 35}) || {_force == 1}) then {
// shotbullet, shotShell don't seem to explode when touching water, so don't create frags
if ((surfaceIsWater _lastPos) && {(toLower getText (configFile >> "CfgAmmo" >> _shellType >> "simulation")) in ["shotbullet", "shotshell"]}) exitWith {};
private _fuseDist = getNumber(configFile >> "CfgAmmo" >> _shellType >> "fuseDistance");
private _isArmed = _firedPos vectorDistance _lastPos >= _fuseDist; // rounds explode at exactly fuseDistance, so check inclusive
TRACE_2("",_fuseDist,_isArmed);
if (!_isArmed) exitWith {TRACE_1("round not armed",_this);};
TRACE_3("Sending frag event to server",_lastPos,_lastVel,_shellType);
[QGVAR(frag_eh), [_lastPos,_lastVel,_shellType]] call CBA_fnc_serverEvent;
};
};
};
if (_doSpall) then {
DEC(GVAR(spallIsTrackingCount));
TRACE_1("doSpall",_foundObjectHPIds);
{
if (!isNil "_x") then {
_x removeEventHandler ["hitPart", _foundObjectHPIds select _forEachIndex];
};
} forEach _spallTrack;
};
false
};
_this set [1, getPosASL _round];
_this set [2, velocity _round];
if (_doSpall) then {
private _scale = ((count GVAR(objects)) / GVAR(maxTrackPerFrame)) max 0.1;
[_round, _scale, _spallTrack, _foundObjectHPIds] call FUNC(spallTrack);
};
true

View File

@ -0,0 +1,55 @@
#include "script_component.hpp"
/*
* Author: Lambda.Tiger
* This function checks whether an ammunition type should cause fragmentation
* and whether any submunitions exist
*
* Arguments:
* 0: _ammo <STRING> - cfgAmmo type of ammo to check
*
* Return Value:
* _shouldFrag <ARRAY>
* 0 - Should the specific round fragment
* 1 - Does the munition have a child submunition
*
* Example:
* ["B_556x45_Ball"] call ace_frag_fnc_shouldFrag;
*
* Public: No
*/
params ["_ammo"];
private _shouldFrag = GVAR(shouldFragCache) get _ammo;
if !(isNil "_shouldFrag") exitWith {_shouldFrag};
// two arguments, 1st for munition should frag, 2nd for
_shouldFrag = [true, false];
private _skip = getNumber (configFile >> "cfgAmmo" >> _ammo >> QGVAR(skip));
private _force = getNumber (configFile >> "cfgAmmo" >> _ammo >> QGVAR(force));
private _explosive = getNumber (configFile >> "cfgAmmo" >> _ammo >> "explosive");
private _indirectHit = getNumber(configFile >> "cfgAmmo" >> _ammo >> "indirectHit");
private _indirectRange = getNumber(configFile >> "cfgAmmo" >> _ammo >> "indirectHitRange");
if (_skip == 1 || (_force == 0 && {_explosive < 0.5 || {_indirectHit < 3
|| {_indirectRange < 5 && _indirectHit < _indirectRange}}})) then {
TRACE_4("No frag" ,_skip, _explosive, _indirectRange, _indirectHit);
_shouldFrag set [0, false];
};
if (GVAR(enSubMunit) > 0) then {
private _hasSubmunit = if (isText (configFile >> "cfgAmmo" >> _ammo >> "submunitionAmmo")) then {
"" isNotEqualTo getText (configFile >> "cfgAmmo" >> _ammo >> "submunitionAmmo");
} else
{
[] isNotEqualTo getArray (configFile >> "cfgAmmo" >> _ammo >> "submunitionAmmo");
};
_shouldFrag set [1, _hasSubmunit];
TRACE_2("Submunition" ,_ammo, _hasSubmunit);
};
GVAR(shouldFragCache) set [_ammo, _shouldFrag];
_shouldFrag

View File

@ -0,0 +1,31 @@
#include "script_component.hpp"
/*
* Author: Lambda.Tiger
* This function checks whether an ammunition type should cause spalling
*
*
* Arguments:
* 0: _ammo <STRING> - cfgAmmo type of ammo to check
*
* Return Value:
* Whether the round type would spall when hitting an object <BOOL>
*
* Example:
* ["B_556x45_Ball"] call ace_frag_fnc_shouldSpall;
*
* Public: No
*/
params ["_ammo"];
private _shouldSpall = GVAR(spallCahche) get _ammo;
if !(isNil "_shouldSpall") exitWith {_shouldSpall};
private _caliber = getNumber (configFile >> "CfgAmmo" >> _ammo >> "caliber");
private _explosive = getNumber (configFile >> "CfgAmmo" >> _ammo >> "explosive");
private _idH = getNumber (configFile >> "CfgAmmo" >> _ammo >> "indirectHitRange");
_shouldSpall = (_caliber >= 2.5 || _explosive > 0 && _idh >= 1);
_shouldSpall

View File

@ -1,42 +0,0 @@
#include "..\script_component.hpp"
/*
* Author: ACE-Team
* Handles the HitPart event
*
* Arguments:
* None
*
* Return Value:
* None
*
* Example:
* call ace_frag_fnc_spallHP
*
* Public: No
*/
//player sideChat format ["f: %1 c: %2", (_this select 0), (count GVAR(spallHPData))];
params ["_index", "_hitPartData"];
private _initialData = GVAR(spallHPData) param [_index, []];
if (_initialData isEqualTo []) exitWith {};
private _hpRound = (_hitPartData select 0) select 2;
private _round = _initialData select 3;
private _hpDirect = (_hitPartData select 0) select 10;
if (_hpDirect && {_round == _hpRound}) then {
{
// diag_log text format ["HPDUMP-------------------------------------"];
// {
// _hp = _x;
// diag_log text format ["%1 --", _forEachIndex];
// {
// diag_log text format ["%1: %2", _forEachIndex, _x];
// } forEach _hp;
// } forEach (_this select 1);
[DFUNC(doSpall), [_this, _forEachIndex]] call CBA_fnc_execNextFrame;
// player sideChat "WEEE";
} forEach _hitPartData;
};

View File

@ -1,39 +0,0 @@
#include "..\script_component.hpp"
/*
* Author: ACE-Team
* Add HitPart EventHandler to objects in the projectile's path
*
* Arguments:
* None
*
* Return Value:
* None
*
* Example:
* call ace_frag_fnc_spallTrack
*
* Public: No
*/
params ["_round", "_multiplier", "_foundObjects", "_foundObjectHPIds"];
private _delta = (1 / diag_fps) * _multiplier;
private _curPos = getPosASL _round;
private _velocity = velocity _round;
private _velocityStep = _velocity vectorMultiply _delta;
private _forwardPos = _curPos vectorAdd _velocityStep;
private _intersectsWith = lineIntersectsWith [_curPos, _forwardPos];
if (_intersectsWith isEqualTo []) exitWith {};
{
// diag_log text format ["Adding HP: %1", _x];
private _index = count GVAR(spallHPData);
private _hpId = _x addEventHandler ["hitPart", compile format ["[%1, _this] call " + QFUNC(spallHP), _index]];
_foundObjects pushBack _x;
_foundObjectHPIds pushBack _hpId;
private _data = [_hpId, _x, typeOf _round, _round, _curPos, _velocity, 0, _foundObjects, _foundObjectHPIds];
GVAR(spallHPData) pushBack _data;
} forEach (_intersectsWith select {!(_x in _foundObjects)});

View File

@ -0,0 +1,40 @@
#include "script_component.hpp"
/*
* Author: Lambda.Tiger
* This function adds event handlers for submunition fragmentation.
* It begins by confirming fragmentation and submunition fragmentation is
* enabled, requests cached (or live calculated) ammo information array.
* and uses that information to add event handlers as needed to the given
* submunition.
*
* Arguments:
* Inherits from BI SubmunitionCraeted EH
*
* Return Value:
* none
*
* Example:
* ["", _submunitionProjectile] call ace_frag_submunition
*
* Public: No
*/
if (!GVAR(enabled) || {GVAR(enSubMunit) == 0}) exitWith {};
// params ["_projectile", "_submunitionProjectile", "_pos", "_velocity"];
params ["", "_submunitionProjectile"];
private _shouldFrag = GVAR(shouldFragCache) getOrDefaultCall [typeOf _submunitionProjectile, FUNC(shouldFrag), true];
_shouldFrag params ["_doFrag", "_doSubmunit"];
if (_doFrag) then {
_submunitionProjectile addEventHandler ["Explode", {[_this, true] call FUNC(doFrag)}];
};
if (_doSubmunit) then {
_submunitionProjectile addEventHandler ["SubmunitionCreated", {_this call FUNC(submunition)}];
};
#ifdef DEBUG_MODE_FULL
[_submunitionProjectile] call UNC(dev_addRound);
#endif

View File

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

View File

@ -26,29 +26,29 @@ private _category = format ["ACE %1", localize LSTRING(Module_DisplayName)];
/// !*! TODO: add stringtable entries
[
QGVAR(enSubMunit), "LIST",
["Enable submunition fragmentation", "Enables submunition fragmentation when fragmentation is enabled"],
QGVAR(enSubMunit), "LIST",
["Enable submunition fragmentation", "Enables submunition fragmentation when fragmentation is enabled"],
[_category, LSTRING(Frag)],
[[2, 1, 0], ["complex fragementation","simple fragmentation","no fragmentation"], 2]
[[2, 1, 0], ["complex fragementation","simple fragmentation","no fragmentation"], 2]
] call CBA_fnc_addSetting;
[
QGVAR(reflectionsEnabled), "CHECKBOX",
"Enable reflections",
QGVAR(reflectionsEnabled), "CHECKBOX",
"Enable reflections",
[_category, LSTRING(Frag)],
false
false
] call CBA_fnc_addSetting;
[
QGVAR(atLeastOne), "CHECKBOX",
"At least one round hit",
QGVAR(atLeastOne), "CHECKBOX",
"At least one round hit",
[_category, LSTRING(Frag)],
true
true
] call CBA_fnc_addSetting;
[
QGVAR(BlackList), "EDITBOX",
["Default BlackList", "Array of ammo classnames strings to blackist fragmentation for."],
QGVAR(BlackList), "EDITBOX",
["Default BlackList", "Array of ammo classnames strings to blackist fragmentation for."],
[_category, LSTRING(Frag)],
QUOTE(['B_556x45_Ball'])
QUOTE(['B_556x45_Ball'])
] call CBA_fnc_addSetting;

View File

@ -1,54 +1,54 @@
private _category = format ["ACE %1", localize LSTRING(Module_DisplayName)];
[
QGVAR(debugOptions),
"CHECKBOX",
"Enable debug mode",
[_category, LSTRING(Debug)],
true // [min, max, default, trailing decimals]
QGVAR(debugOptions),
"CHECKBOX",
"Enable debug mode",
[_category, LSTRING(Debug)],
true // [min, max, default, trailing decimals]
] call CBA_fnc_addSetting;
// debug options
[
QGVAR(dbgSphere),
"CHECKBOX",
"Enable debug impact spheres",
[_category, LSTRING(Debug)],
false,
0,
{},
true
QGVAR(dbgSphere),
"CHECKBOX",
"Enable debug impact spheres",
[_category, LSTRING(Debug)],
false,
0,
{},
true
] call CBA_fnc_addSetting;
[
QGVAR(frameHint),
"CHECKBOX",
"Show framerate hint",
[_category, LSTRING(Debug)],
true
QGVAR(frameHint),
"CHECKBOX",
"Show framerate hint",
[_category, LSTRING(Debug)],
true
] call CBA_fnc_addSetting;
[
QGVAR(fadeRounds),
"CHECKBOX",
"Fade round traces over time",
[_category, LSTRING(Debug)],
true
QGVAR(fadeRounds),
"CHECKBOX",
"Fade round traces over time",
[_category, LSTRING(Debug)],
true
] call CBA_fnc_addSetting;
[
QGVAR(dltTrace),
"CHECKBOX",
"Delete fire trace on fade",
[_category, LSTRING(Debug)],
true
QGVAR(dltTrace),
"CHECKBOX",
"Delete fire trace on fade",
[_category, LSTRING(Debug)],
true
] call CBA_fnc_addSetting;
[
QGVAR(drawHitBox),
"CHECKBOX",
"Draw unit hitboxes",
[_category, LSTRING(Debug)],
true
QGVAR(drawHitBox),
"CHECKBOX",
"Draw unit hitboxes",
[_category, LSTRING(Debug)],
true
] call CBA_fnc_addSetting;

View File

@ -17,4 +17,4 @@
#include "\z\ace\addons\main\script_macros.hpp"
#define ACE_TRACE_DRAW_INC 1
#define ACE_FRAG_SPALL_HOLDOFF 0.1