From 182ebc027b7c220a6ea866cdcc3c33179fbaf2c6 Mon Sep 17 00:00:00 2001 From: lambdatiger Date: Fri, 23 Aug 2024 18:27:19 -0500 Subject: [PATCH] Initial commit --- addons/frag/CfgAmmoSpall.hpp | 159 +++++++++++++++++- addons/frag/XEH_PREP.hpp | 6 +- addons/frag/XEH_preInit.sqf | 2 + addons/frag/functions/fnc_addBlacklist.sqf | 9 +- addons/frag/functions/fnc_doSpall.sqf | 127 -------------- addons/frag/functions/fnc_doSpallHitPart.sqf | 131 +++++++++++++++ .../frag/functions/fnc_doSpallPenetrate.sqf | 99 +++++++++++ addons/frag/functions/fnc_fired.sqf | 17 +- addons/frag/functions/fnc_getMaterialInfo.sqf | 62 +++++++ .../frag/functions/fnc_initMaterialCache.sqf | 96 +++++++++++ addons/frag/initSettings.inc.sqf | 26 ++- addons/frag/script_component.hpp | 19 ++- addons/frag/stringtable.xml | 6 + 13 files changed, 597 insertions(+), 162 deletions(-) delete mode 100644 addons/frag/functions/fnc_doSpall.sqf create mode 100644 addons/frag/functions/fnc_doSpallHitPart.sqf create mode 100644 addons/frag/functions/fnc_doSpallPenetrate.sqf create mode 100644 addons/frag/functions/fnc_getMaterialInfo.sqf create mode 100644 addons/frag/functions/fnc_initMaterialCache.sqf diff --git a/addons/frag/CfgAmmoSpall.hpp b/addons/frag/CfgAmmoSpall.hpp index b3214fc4b0..60780e564e 100644 --- a/addons/frag/CfgAmmoSpall.hpp +++ b/addons/frag/CfgAmmoSpall.hpp @@ -1,15 +1,158 @@ -class GVAR(spall_small): GVAR(small) { - timeToLive = 0.1; +class GVAR(spallBase): B_65x39_Caseless { + GVAR(skip) = 1; + submunitionAmmo[] = {QGVAR(small),4,QGVAR(medium),3,QGVAR(large),2,QGVAR(huge),1}; + submunitionConeType[] = {"random", 20}; + submunitionConeAngle = 40; + submunitionDirectionType = "SubmunitionModelDirection"; + triggerTime = 0; + submunitionInitialOffset[] = {0,0,0}; + submunitionInitSpeed = 0; + triggerSpeedCoef[] = {0.75,1.25}; + deleteParentWhenTriggered = 1; + submunitionParentSpeedCoef = 1; }; -class GVAR(spall_medium): GVAR(medium) { - timeToLive = 0.15; + +/* + * ground + */ +class GVAR(ground_spall_tiny): GVAR(spallBase) { + submunitionAmmo[] = {QGVAR(tiny), 9, QGVAR(small), 1}; + submunitionConeType[] = {"poissondisccenter", 4}; }; -class GVAR(spall_large): GVAR(large) { - timeToLive = 0.25; +class GVAR(ground_spall_small): GVAR(spallBase) { + submunitionAmmo[] = {QGVAR(small), 4,QGVAR(medium), 1}; + submunitionConeType[] = {"poissondisccenter", 6}; }; -class GVAR(spall_huge): GVAR(huge) { - timeToLive = 0.3; +class GVAR(ground_spall_medium): GVAR(spallBase) { + submunitionAmmo[] = {QGVAR(small), 2, QGVAR(small_HD), 1,QGVAR(medium), 3, QGVAR(medium_HD), 1, QGVAR(large), 2}; + submunitionConeType[] = {"poissondisccenter", 15}; +}; + +class GVAR(ground_spall_large): GVAR(spallBase) { + submunitionAmmo[] = {QGVAR(tiny), 3, QGVAR(tiny_HD), 3, QGVAR(small), 4, QGVAR(small_HD), 4, QGVAR(medium_HD), 5, QGVAR(large), 1, QGVAR(large_HD), 2}; + submunitionConeType[] = {"poissondisccenter", 15}; +}; + +class GVAR(ground_spall_huge): GVAR(spallBase) { + submunitionAmmo[] = {QGVAR(tiny), 3, QGVAR(tiny_HD), 3, QGVAR(small), 4, QGVAR(small_HD), 4, QGVAR(medium), 5, QGVAR(large), 1, QGVAR(large_HD), 2}; + submunitionConeType[] = {"poissondisccenter", 20}; +}; + + +/* + * rock + */ +class GVAR(rock_spall_tiny): GVAR(spallBase) { + submunitionAmmo[] = {QGVAR(small), 4, QGVAR(medium_HD), 1}; + submunitionConeType[] = {"poissondisccenter", 4}; +}; + +class GVAR(rock_spall_small): GVAR(spallBase) { + submunitionAmmo[] = {QGVAR(small), 4, QGVAR(medium), 1}; + submunitionConeType[] = {"poissondisccenter", 5}; +}; + +class GVAR(rock_spall_medium): GVAR(spallBase) { + submunitionAmmo[] = {QGVAR(small), 4, QGVAR(medium), 1, QGVAR(medium_HD), 2, QGVAR(large_HD), 1}; + submunitionConeType[] = {"poissondisccenter", 7}; +}; + +class GVAR(rock_spall_large): GVAR(spallBase) { + submunitionAmmo[] = {QGVAR(small), 5, QGVAR(medium), 2, QGVAR(large), 1}; + submunitionConeType[] = {"poissondisccenter", 10}; +}; + +class GVAR(rock_spall_huge): GVAR(spallBase) { + submunitionAmmo[] = {QGVAR(small), 5, QGVAR(medium), 2, QGVAR(large), 1, QGVAR(huge_HD), 1}; + submunitionConeType[] = {"poissondisccenter", 13}; +}; + + +/* + * wood + */ +class GVAR(wood_spall_tiny): GVAR(spallBase) { + submunitionAmmo[] = {QGVAR(tiny), 2, QGVAR(small), 4}; + submunitionConeType[] = {"poissondisccenter", 4}; +}; + +class GVAR(wood_spall_small): GVAR(spallBase) { + submunitionAmmo[] = {QGVAR(tiny), 2, QGVAR(small), 4, QGVAR(medium), 1}; + submunitionConeType[] = {"poissondisccenter", 6}; +}; + +class GVAR(wood_spall_medium): GVAR(spallBase) { + submunitionAmmo[] = {QGVAR(tiny), 2, QGVAR(small), 2, QGVAR(medium), 2, QGVAR(medium_HD), 1, QGVAR(large_HD), 1}; + submunitionConeType[] = {"poissondisccenter", 8}; +}; + +class GVAR(wood_spall_large): GVAR(spallBase) { + submunitionAmmo[] = {QGVAR(tiny), 1, QGVAR(small), 3, QGVAR(medium), 2, QGVAR(large_HD), 2, QGVAR(huge_HD), 1}; + submunitionConeType[] = {"poissondisccenter", 10}; +}; + +class GVAR(wood_spall_huge): GVAR(spallBase) { + submunitionAmmo[] = {QGVAR(tiny), 1, QGVAR(small), 3, QGVAR(medium), 2, QGVAR(large_HD), 2, QGVAR(huge_HD), 1}; + submunitionConeType[] = {"poissondisccenter", 12}; +}; + + +/* + * concrete + */ +class GVAR(concrete_spall_tiny): GVAR(spallBase) { + submunitionAmmo[] = {QGVAR(tiny), 6, QGVAR(tiny_HD), 3, QGVAR(small), 1}; + submunitionConeType[] = {"poissondisccenter", 4}; +}; + +class GVAR(concrete_spall_small): GVAR(spallBase) { + submunitionAmmo[] = {QGVAR(tiny), 4, QGVAR(tiny_HD), 2, QGVAR(small), 2, QGVAR(medium), 1}; + submunitionConeType[] = {"poissondisccenter", 6}; +}; + +class GVAR(concrete_spall_medium): GVAR(spallBase) { + submunitionAmmo[] = {QGVAR(tiny), 6, QGVAR(tiny_HD), 1, QGVAR(small), 4, QGVAR(medium), 5, QGVAR(large_HD), 1}; + submunitionConeType[] = {"poissondisccenter", 9}; +}; + +class GVAR(concrete_spall_large): GVAR(spallBase) { + submunitionAmmo[] = {QGVAR(tiny), 6, QGVAR(small), 4, QGVAR(medium), 3, QGVAR(large), 1, QGVAR(large_HD), 2}; + submunitionConeType[] = {"poissondisccenter", 12}; +}; + +class GVAR(concrete_spall_huge): GVAR(spallBase) { + submunitionAmmo[] = {QGVAR(tiny), 8, QGVAR(small), 4, QGVAR(medium), 3, QGVAR(large), 1, QGVAR(large_HD), 1, QGVAR(huge_HD), 1}; + submunitionConeType[] = {"poissondisccenter", 18}; +}; + + +/* + * metal + */ +class GVAR(metal_spall_tiny): GVAR(spallBase) { + submunitionAmmo[] = {QGVAR(tiny), 9, QGVAR(small), 1}; + submunitionConeType[] = {"poissondisccenter", 4}; +}; + +class GVAR(metal_spall_small): GVAR(spallBase) { + submunitionAmmo[] = {QGVAR(tiny), 4, QGVAR(small), 2, QGVAR(medium), 1}; + submunitionConeType[] = {"poissondisccenter", 6}; +}; + +class GVAR(metal_spall_medium): GVAR(spallBase) { + submunitionAmmo[] = {QGVAR(tiny), 5, QGVAR(small), 4, QGVAR(medium), 2, QGVAR(large), 1}; + submunitionConeType[] = {"poissondisccenter", 8}; +}; + +class GVAR(metal_spall_large): GVAR(spallBase) { + submunitionAmmo[] = {QGVAR(tiny), 4, QGVAR(small), 3, QGVAR(medium), 2, QGVAR(large), 1, QGVAR(huge), 1}; + submunitionConeType[] = {"poissondisccenter", 12}; +}; + +class GVAR(metal_spall_huge): GVAR(spallBase) { + submunitionAmmo[] = {QGVAR(tiny), 8, QGVAR(small), 3, QGVAR(medium), 2, QGVAR(large), 3, QGVAR(huge), 1}; + submunitionConeType[] = {"poissondisccenter", 18}; }; diff --git a/addons/frag/XEH_PREP.hpp b/addons/frag/XEH_PREP.hpp index b862ad7582..3adae5f8cd 100644 --- a/addons/frag/XEH_PREP.hpp +++ b/addons/frag/XEH_PREP.hpp @@ -9,11 +9,15 @@ PREP(dev_trackHitBox); PREP(dev_trackObj); PREP(doExplosions); PREP(doReflections); -PREP(doSpall); +PREP(doSpallHitPart); +PREP(doSpallPenetrate); PREP(findReflections); PREP(fired); PREP(frago); PREP(getFragInfo); +PREP(getMaterialInfo); PREP(getSpallInfo); +PREP(getSpallInfo); +PREP(initMaterialCache); PREP(shouldFrag); PREP(shouldSpall); diff --git a/addons/frag/XEH_preInit.sqf b/addons/frag/XEH_preInit.sqf index 50353d88dc..84f9bc2b54 100644 --- a/addons/frag/XEH_preInit.sqf +++ b/addons/frag/XEH_preInit.sqf @@ -6,8 +6,10 @@ PREP_RECOMPILE_START; #include "XEH_PREP.hpp" PREP_RECOMPILE_END; +call FUNC(initMaterialCache); GVAR(spallInfoCache) = createHashMap; GVAR(shouldSpallCache) = createHashMap; +GVAR(nextSpallAllowTime) = -1; GVAR(shouldFragCache) = createHashMap; GVAR(fragInfoCache) = createHashMap; diff --git a/addons/frag/functions/fnc_addBlacklist.sqf b/addons/frag/functions/fnc_addBlacklist.sqf index cb7c28e77c..4ee39ff1dd 100644 --- a/addons/frag/functions/fnc_addBlacklist.sqf +++ b/addons/frag/functions/fnc_addBlacklist.sqf @@ -19,11 +19,10 @@ params ["_projectile"]; TRACE_2("addBlackList",_projectile,typeOf projectile); _projectile setVariable [QGVAR(blacklisted), true]; -_projectile removeEventHandler [ - "HitPart", - _projectile getVariable [QGVAR(hitPartEventHandler), -1] -]; +(_projectile getVariable [QGVAR(spallEH), [-1, -1]]) params ["_hitPartEH", "_penetratedEH"]; +_projectile removeEventHandler ["HitPart", _hitPartEH]; +_projectile removeEventHandler ["Penetrated", _penetratedEH]; _projectile removeEventHandler [ "Explode", - _projectile getVariable [QGVAR(explodeEventHandler), -1] + _projectile getVariable [QGVAR(fragEH), -1] ]; diff --git a/addons/frag/functions/fnc_doSpall.sqf b/addons/frag/functions/fnc_doSpall.sqf deleted file mode 100644 index b6e6bb95a3..0000000000 --- a/addons/frag/functions/fnc_doSpall.sqf +++ /dev/null @@ -1,127 +0,0 @@ -#include "..\script_component.hpp" -/* - * Author: Jaynus, NouberNou, Lambda.Tiger, - * This function check whether a spall event has occured and generates spall. - * - * Arguments: - * 0: The object a projectile hit - * 1: The config name of the projectile - * 2: The projectile that should cause spalling - * 3: The position (ASL) the projectile hit the object - * 4: The old velocity of the projectile - * 5: The projectile's shotParents - * - * Return Value: - * None - * - * Example: - * [editorPlacedHouse_0, typeOf _projectile, _projectile, [1000, 40, 60], [0, 1000, 0], [objNull, ace_player]] call ace_frag_fnc_doSpall - * - * Public: No - */ -#define WEIGHTED_SIZE [QGVAR(spall_small), 4, QGVAR(spall_medium), 3, QGVAR(spall_large), 2, QGVAR(spall_huge), 1] -params ["_objectHit", "_roundType", "_round", "_oldPosASL", "_oldVelocity", "_shotParents"]; - -TRACE_6("",_objectHit,_roundType,_round,_oldPosASL,_oldVelocity,_shotParents); -if ((isNil "_objectHit") || {isNull _objectHit}) exitWith { - WARNING_1("Problem with hitPart data - bad object [%1]",_objectHit); -}; - -private _caliber = getNumber (configFile >> "CfgAmmo" >> _roundType >> "caliber"); -private _explosive = getNumber (configFile >> "CfgAmmo" >> _roundType >> "explosive"); -private _idh = getNumber (configFile >> "CfgAmmo" >> _roundType >> "indirectHitRange"); - -_roundType call FUNC(getSpallInfo) params ["_caliber", "_explosive"]; - -private _exit = false; -private _velocityModifier = 1; - -private _curVelocity = velocity _round; -private _oldSpeed = vectorMagnitude _oldVelocity; -private _curSpeed = vectorMagnitude _curVelocity; - -if (alive _round) then { - private _diff = _oldVelocity vectorDiff _curVelocity; - private _polar = _diff call CBA_fnc_vect2polar; - - if (abs (_polar select 1) > 45 || {abs (_polar select 2) > 45}) then { - if (_caliber < 2.5) then { - _exit = true; - } else { - SUB(_velocityModifier,_curSpeed / _oldSpeed); - }; - }; -}; -if (_exit) exitWith { - TRACE_1("exit alive",_caliber); -}; - -private _unitDir = vectorNormalized _oldVelocity; - -if ((isNil "_oldPosASL") || {!(_oldPosASL isEqualTypeArray [0,0,0])}) exitWith {WARNING_1("Problem with hitPart data - bad pos [%1]",_oldPosASL);}; -private _pos1 = _oldPosASL; -private _spallPosAGL = _pos1; -private _searchStepSize = _unitDir vectorMultiply 0.05; -for "_i" from 0 to 20 do { - _spallPosAGL = _pos1 vectorAdd _searchStepSize; - if (!lineIntersects [_pos1, _spallPosAGL]) exitWith {}; - _pos1 = _spallPosAGL; -}; -if (_spallPosAGL isEqualTo _pos1) exitWith { - TRACE_1("can't find other side",_oldPosASL); -}; -_spallPosAGL = ASLToAGL _spallPosAGL; - -(_shotParents#1) setVariable [QGVAR(nextSpallEvent), CBA_missionTime + ACE_FRAG_SPALL_UNIT_HOLDOFF]; -private _oldVelocitySpherical = _oldVelocity call CBA_fnc_vect2polar; - -if (_explosive > 0) then { - _roundType call FUNC(getFragInfo) params ["", "_fragVelocity"]; - _oldVelocitySpherical set [0, _fragVelocity * 0.66]; -}; -TRACE_2("spallPosandVel",_spallPosAGL,_oldVelocitySpherical); - -private _spread = 15 + (random 25); -private _spallCount = 5 + (random 10); -TRACE_1("",_spallCount); -for "_i" from 1 to _spallCount do { - private _fragmentElevation = ((_oldVelocitySpherical select 2) - _spread) + (random (_spread * 2)); - private _fragmentAzimuth = ((_oldVelocitySpherical select 1) - _spread) + (random (_spread * 2)); - if (abs _fragmentElevation > 90) then { - ADD(_fragmentAzimuth,180); - }; - _fragmentAzimuth = _fragmentAzimuth % 360; - private _fragmentSpeed = (_oldVelocitySpherical select 0) * 0.33 * _velocityModifier; - _fragmentSpeed = _fragmentSpeed * (0.75 + random 0.5); - - private _spallFragVect = [_fragmentSpeed, _fragmentAzimuth, _fragmentElevation] call CBA_fnc_polar2vect; - private _fragment = createVehicleLocal [selectRandomWeighted WEIGHTED_SIZE, _spallPosAGL, [], 0, "CAN_COLLIDE"]; - _fragment setVelocity _spallFragVect; - _fragment setShotParents _shotParents; - - #ifdef DEBUG_MODE_DRAW - [_fragment, "orange", true] call FUNC(dev_trackObj); - #endif -}; - -_spread = 5 + (random 5); -_spallCount = 3 + (random 5); -for "_i" from 1 to _spallCount do { - private _fragmentElevation = ((_oldVelocitySpherical select 2) - _spread) + (random (_spread * 2)); - private _fragmentAzimuth = ((_oldVelocitySpherical select 1) - _spread) + (random (_spread * 2)); - if (abs _fragmentElevation > 90) then { - ADD(_fragmentAzimuth,180); - }; - _fragmentAzimuth = _fragmentAzimuth % 360; - private _fragmentSpeed = (_oldVelocitySpherical select 0) * 0.55 * _velocityModifier; - _fragmentSpeed = _fragmentSpeed * (0.75 + random 0.5); - - private _spallFragVect = [_fragmentSpeed, _fragmentAzimuth, _fragmentElevation] call CBA_fnc_polar2vect; - private _fragment = createVehicleLocal [selectRandomWeighted WEIGHTED_SIZE, _spallPosAGL, [], 0, "CAN_COLLIDE"]; - _fragment setVelocity _spallFragVect; - _fragment setShotParents _shotParents; - - #ifdef DEBUG_MODE_DRAW - [_fragment, "purple", true] call FUNC(dev_trackObj); - #endif -}; diff --git a/addons/frag/functions/fnc_doSpallHitPart.sqf b/addons/frag/functions/fnc_doSpallHitPart.sqf new file mode 100644 index 0000000000..807556e6c0 --- /dev/null +++ b/addons/frag/functions/fnc_doSpallHitPart.sqf @@ -0,0 +1,131 @@ +#include "..\script_component.hpp" +/* + * Author: Jaynus, NouberNou, Lambda.Tiger, + * This function creates spalling when a projectile hits a object and comes to a stop. + * + * Arguments: + * 0: The projectile that may generate spalling + * 1: The object the projectile hit + * 2: The position (ASL) of the projectile hit + * 3: The velocity of the projectile before it hit the surface + * 4: The surface normal of the surface hit + * 5: The name of the config name or path to bisurf of the surface hit + * 6: The projectiles ammo config + * 7: The projectiles shot parent + * 8: The "up" vector of the projectile when it hit the object + * + * Return Value: + * None + * + * Example: + * [BIS_HITPART_EH_ARGS] call ace_frag_fnc_doSpallHitPart; + * + * Public: No + */ +TRACE_1("doSpallHitPart",_this); +if (CBA_missionTime < GVAR(nextSpallAllowTime)) exitWith { + TRACE_2("timeExit",CBA_missionTime,GVAR(nextSpallAllowTime)); +}; +params ["_projectile", "_objectHit", "_lastPosASL", "_lastVelocity", "_surfaceNorm", "_surfaceType", "_ammo", "_shotParents", "_vectorUp"]; + +// Find spall speed / fragment info +_ammo call FUNC(getSpallInfo) params ["_caliber", "_explosive", "_indirectHit"]; +// Add in v2.18 `getShotInfo`'s `fuseDistanceLeft <= 0` to _explosive +if ((alive _projectile && _explosive < 0.5) || + _ammo == "" || + _lastPosASL isEqualTo [0,0,0] || + {_objectHit isKindOf "CAManBase"}) exitWith { + TRACE_3("exitEarly",alive _projectile,_lastPosASL,_objectHit); +}; + +private _material = _surfaceType call FUNC(getMaterialInfo); +if (_material == "ground") exitWith { + TRACE_1("hitGround",_surfaceType); +}; + +private _speedChange = vectorMagnitude _lastVelocity; +/* + * This is all fudge factor since real spalling is too complex for calculation. + * There are two terms. The first is from round impact, taking a quasi scale + * of caliber coefficient * round caliber * srqt(change in speed). The second term is + * explosive * indirect hit, for any explosive contribution + */ +private _spallPower = (ACE_FRAG_SPALL_CALIBER_COEF * _caliber * sqrt _speedChange + _explosive * _indirectHit) * GVAR(spallIntensity); +TRACE_3("found speed",_speedChange,_caliber,_spallPower); + +if (_spallPower < ACE_FRAG_SPALL_POWER_MIN) exitWith { + TRACE_1("lowImpulse",_ammo); +}; + +private _lastVelocityNorm = vectorNormalized _lastVelocity; +private _deltaStep = _lastVelocityNorm vectorMultiply 0.05; + +if (terrainIntersectASL [_lastPosASL vectorAdd _deltaStep, _lastPosASL]) exitWith { + TRACE_2("terrainIntersect",_lastPosASL,_deltaStep); +}; + +#ifdef DEBUG_MODE_DRAW +if GVAR(dbgSphere) then { + [_lastPosASL vectorAdd _lastVelocityNorm, "orange"] call FUNC(dev_sphereDraw); + [_lastPosASL, "yellow"] call FUNC(dev_sphereDraw); +}; +#endif + +/* + * Improve performance of finding otherside of object on shallow angle + * impacts. 120 degrees due to 90 degree offset with _lastVelocityNorm into object. + */ +private _spallPosASL = _lastPosASL vectorAdd _deltaStep; +if (120 > acos (_lastVelocityNorm vectorDotProduct _surfaceNorm)) then { + _spallPosASL = _spallPosASL vectorAdd (_deltaStep vectorMultiply 5); +}; +private _lastSpallPos = _spallPosASL; +for "_i" from 2 to 21 do +{ + _spallPosASL = _lastSpallPos vectorAdd _deltaStep; + if (!lineIntersects [_lastSpallPos, _spallPosASL]) exitWith { + _spallPosASL = _lastSpallPos vectorAdd _deltaStep; + }; + _lastSpallPos = _spallPosASL; +}; + +if (_spallPosASL isEqualTo _lastSpallPos) exitWith { + TRACE_2("insideObj",_lastPosASL,_spallPosASL); +}; + +#ifdef DEBUG_MODE_DRAW +if GVAR(dbgSphere) then { + [_spallPosASL, "green"] call FUNC(dev_sphereDraw); +}; +#endif + +private _spawnSize = switch (true) do { + case (_spallPower < ACE_FRAG_SPALL_POWER_TINY_MAX): {"_spall_tiny"}; + case (_spallPower < ACE_FRAG_SPALL_POWER_SMALL_MAX): {"_spall_small"}; + case (_spallPower < ACE_FRAG_SPALL_POWER_MEDIUM_MAX): {"_spall_medium"}; + case (_spallPower < ACE_FRAG_SPALL_POWER_LARGE_MAX): {"_spall_large"}; + default {"_spall_huge"}; +}; +GVAR(nextSpallAllowTime) = CBA_missionTime + ACE_FRAG_SPALL_HOLDOFF; + +private _spallSpawner = createVehicleLocal [ + QUOTE(GLUE(ADDON,_)) + _material + _spawnSize, + ASLToAGL _spallPosASL, + [], + 0, + "CAN_COLLIDE" +]; +_spallSpawner setVectorDirandUp [_lastVelocityNorm, _vectorUp]; +_spallSpawner setVelocityModelSpace [0, _speedChange * ACE_FRAG_SPALL_VELOCITY_INHERIT_COEFF, 0]; +_spallSpawner setShotParents _shotParents; + +TRACE_3("createSpallSpawner",speed _spallSpawner,_material + _spawnSize,_spallPower); +#ifdef DEBUG_MODE_DRAW +_spallSpawner addEventHandler [ + "SubmunitionCreated", + { + params ["", "_subProj"]; + [_subProj, "purple", true] call FUNC(dev_trackObj); + } +]; +#endif \ No newline at end of file diff --git a/addons/frag/functions/fnc_doSpallPenetrate.sqf b/addons/frag/functions/fnc_doSpallPenetrate.sqf new file mode 100644 index 0000000000..71b2b0c276 --- /dev/null +++ b/addons/frag/functions/fnc_doSpallPenetrate.sqf @@ -0,0 +1,99 @@ +#include "..\script_component.hpp" +/* + * Author: Jaynus, NouberNou, Lambda.Tiger, + * This function creates spalling if a projectile penetrated a surface and was slowed down enough proportional to the caliber. + * It is dissimilar in function from fnc_doSpall, but leveraging the "Penetrated" projectile EH to process faster. + * + * Arguments: + * Arguments are the same as BI's "Penetratred" EH: + * https://community.bistudio.com/wiki/Arma_3:_Event_Handlers#Penetrated + * + * Return Value: + * None + * + * Example: + * [BIS_PENETRATED_EH_ARGS] call ace_frag_fnc_doSpallPenetrate; + * + * Public: No + */ +#define EPSILON 0.1 + +TRACE_1("doSpallPenetration",_this); +if (CBA_missionTime < GVAR(nextSpallAllowTime)) exitWith { + TRACE_1("timeExit",_this); +}; +params ["_projectile", "_hitObject", "_surfaceType", "", "_spallPosASL", "_newVelocity"]; + +private _ammo = typeOf _projectile; +if (_spallPosASL isEqualTo [0,0,0] || + {_ammo isEqualTo ""} || + {_hitObject isKindOf "CAManBase"}) exitWith { + TRACE_4("time/invalidHit",CBA_missionTime,GVAR(nextSpallAllowTime),_hitObject,_spallPosASL); +}; + +private _material = _surfaceType call FUNC(getMaterialInfo); +if (_material == "ground") exitWith { + #ifdef DEBUG_MODE_FULL + systemChat "ground spall"; + #endif +}; + +_ammo call FUNC(getSpallInfo) params ["_caliber", "_explosive", "_indirectHit"]; +private _velocityChange = velocity _projectile vectorDiff _newVelocity; +private _speedChange = vectorMagnitude _velocityChange; +/* + * This is all fudge factor since real spalling is too complex for calculation. + * The equation takes taking a quasi scale of energy using caliber and change in speed. + */ +private _spallPower = ACE_FRAG_SPALL_CALIBER_COEF * _caliber * sqrt _speedChange * GVAR(spallIntensity); +TRACE_5("found speed",_velocityChange,_speedChange,_caliber,_spallPower,_material); + +if (_spallPower < ACE_FRAG_SPALL_POWER_MIN) exitWith { + TRACE_1("lowImpulse",_ammo); +}; +private _spallDirection = vectorNormalized _velocityChange; + +#ifdef DEBUG_MODE_DRAW +if GVAR(dbgSphere) then { + [_spallPosASL, "green"] call FUNC(dev_sphereDraw); +}; +#endif + +private _spawnSize = switch (true) do { + case (_spallPower < ACE_FRAG_SPALL_POWER_TINY_MAX): {"_spall_tiny"}; + case (_spallPower < ACE_FRAG_SPALL_POWER_SMALL_MAX): {"_spall_small"}; + case (_spallPower < ACE_FRAG_SPALL_POWER_MEDIUM_MAX): {"_spall_medium"}; + case (_spallPower < ACE_FRAG_SPALL_POWER_LARGE_MAX): {"_spall_large"}; + default {"_spall_huge"}; +}; +GVAR(nextSpallAllowTime) = CBA_missionTime + ACE_FRAG_SPALL_HOLDOFF; + +// Solve for one of the vectors normal to _spallDirection on y = 0 plane +private _spallVectorUp = [0, 0, 1]; +if (_spallDirection#2 > EPSILON) then { + private _newZ = _spallDirection#0 / _spallDirection#2; + _spallVectorUp = vectorNormalized [1, 0, -_newZ]; +}; + +private _spallSpawner = createVehicleLocal [ + QUOTE(GLUE(ADDON,_)) + _material + _spawnSize, + ASLToAGL _spallPosASL, + [], + 0, + "CAN_COLLIDE" +]; +_spallSpawner setVectorDirandUp [_spallDirection, _spallVectorUp]; +_spallSpawner setVelocityModelSpace [0, _speedChange * ACE_FRAG_SPALL_VELOCITY_INHERIT_COEFF, 0]; +_spallSpawner setShotParents getShotParents _projectile; +TRACE_4("dir&up",_spallDirection,vectorDir _spallSpawner,_spallVectorUp,vectorUp _spallSpawner); + +TRACE_3("createSpallSpawner",speed _spallSpawner,_material + _spawnSize,_spallPower); +#ifdef DEBUG_MODE_DRAW +_spallSpawner addEventHandler [ + "SubmunitionCreated", + { + params ["", "_subProj"]; + [_subProj, "purple", true] call FUNC(dev_trackObj); + } +]; +#endif \ No newline at end of file diff --git a/addons/frag/functions/fnc_fired.sqf b/addons/frag/functions/fnc_fired.sqf index 10a9da223e..f350c50df5 100644 --- a/addons/frag/functions/fnc_fired.sqf +++ b/addons/frag/functions/fnc_fired.sqf @@ -28,29 +28,24 @@ if (GVAR(spallEnabled) && {_ammo call FUNC(shouldSpall)}) then { private _hitPartEventHandler = _projectile addEventHandler [ "HitPart", { - params ["_projectile", "_hitObject", "", "_posASL", "_velocity"]; + params ["_projectile", "_objectHit", "", "_posASL", "_velocity", "_surfNorm", "", "", "_surfType"]; // starting v2.18 it may be faster to use the instigator EH parameter, the same as the second entry shotParents, to recreate _shotParent // The "explode" EH does not get the same parameter private _shotParents = getShotParents _projectile; + _shotParents set [0, objNull]; private _ammo = typeOf _projectile; + private _vectorUp = vectorUp _projectile; /* * Wait a frame to see what happens to the round, may result in * multiple hits / slowdowns getting shunted to the first hit */ - [ - // only let a unit make a frag event once per ACE_FRAG_SPALL_UNIT_HOLDOFF - { - private _shotParents = _this#5; - if (CBA_missionTime < _shotParents#1 getVariable [QGVAR(nextSpallEvent), -1]) exitWith {}; - _this call FUNC(doSpall); - }, - [_hitObject, _ammo, _projectile, _posASL, _velocity, [objNull, _shotParents#1]] - ] call CBA_fnc_execNextFrame; + [LINKFUNC(doSpallHitPart), [_projectile, _objectHit, _posASL, _velocity, _surfNorm, _surfType, _ammo, _shotParents, _vectorUp]] call CBA_fnc_execNextFrame; } ]; - _projectile setVariable [QGVAR(hitPartEventHandler), _hitPartEventHandler]; + private _penetratedEventHandler = _projectile addEventHandler ["Penetrated",LINKFUNC(doSpallPenetrate)]; + _projectile setVariable [QGVAR(hitPartEventHandler), [_hitPartEventHandler, _penetratedEventHandler]]; }; if (GVAR(reflectionsEnabled) || (GVAR(enabled) && {_ammo call FUNC(shouldFrag)})) then { diff --git a/addons/frag/functions/fnc_getMaterialInfo.sqf b/addons/frag/functions/fnc_getMaterialInfo.sqf new file mode 100644 index 0000000000..199953b237 --- /dev/null +++ b/addons/frag/functions/fnc_getMaterialInfo.sqf @@ -0,0 +1,62 @@ +#include "..\script_component.hpp" +/* + * Author: Lambda.Tiger + * This function returns a classification of material type based on the surface hit. + * + * Arguments: + * 0: Surface type given as either a CfgSurfaces path or .bisurf filepath, same format as "HitPart" projectile parameter + * + * Return Value: + * Material categories as expanded on in line 44 below + * + * Example: + * "a3\data_f\penetration\concrete.bisurf" call ace_frag_fnc_getMaterialInfo + * + * Public: No + */ + +#define ACE_FRAG_SOUNDENVIRON_STR_LEN 12 +#define ACE_FRAG_SOUNDHIT_STR_LEN 8 +#define ACE_FRAG_MATERIAL_SEARCH_LEN 10 + +params ["_surfType"]; + +private _material = GVAR(spallMaterialCache) get _surfType; + +TRACE_2("materialCache",_surfType,_material); +if (!isNil "_material") exitWith {_material}; +// Use 'soundEnviron' or 'soundHit' to extract approx material +private _surfaceConfig = configFile >> "CfgSurfaces" >> _surfType; +if (isClass _surfaceConfig) then { + _material = getText (_surfaceConfig >> "soundEnviron"); + if (_material == "" || {_material == "empty"}) then { + _material = getText (_surfaceConfig >> "soundhit"); + }; +} else { // Messy way when a surface isn't added to CfgSurfaces + private _surfFileText = toLowerANSI preprocessFile _surfType; + _surfFileText = _surfFileText regexReplace ["[^a-z0-9]", ""]; + private _idx = ACE_FRAG_SOUNDENVIRON_STR_LEN + (_surfFileText find "soundenviron"); + if (_surfFileText select [_idx, 5] isEqualTo "empty") then { + _idx = ACE_FRAG_SOUNDHIT_STR_LEN + (_surfFileText find "soundhit"); + }; + _material = _surfFileText select [_idx, ACE_FRAG_MATERIAL_SEARCH_LEN]; +}; +TRACE_1("materialSubString",_material); + +_material = switch (true) do { + case ("dirt" in _material); + case ("grass" in _material): { "ground" }; + case ("gravel" in _material); + case ("rock" in _material): { "rock" }; + case ("wood" in _material): { "wood" }; + case ("lino" in _material); + case ("building" in _material); + case ("concrete" in _material): { "concrete" }; + case ("metal" in _material): { "metal" }; + default { "ground" }; +}; + +GVAR(spallMaterialCache) set [_surfType, _material]; +TRACE_2("materialCacheSet",_surfType,_material); + +_material diff --git a/addons/frag/functions/fnc_initMaterialCache.sqf b/addons/frag/functions/fnc_initMaterialCache.sqf new file mode 100644 index 0000000000..fd7a84a72d --- /dev/null +++ b/addons/frag/functions/fnc_initMaterialCache.sqf @@ -0,0 +1,96 @@ +#include "..\script_component.hpp" +/* + * Author: Lambda.Tiger + * For performance, we load a bunch of vanilla materials preemptively into the spall material cache. + * + * Arguments: + * None + * + * Return Value: + * None + * + * Example: + * call ace_frag_fnc_initMaterialCache + * + * Public: No + */ + +GVAR(spallMaterialCache) = createHashMapFromArray [ + ["a3\data_f\penetration\armour.bisurf","metal"], + ["a3\data_f\penetration\armour_plate.bisurf","metal"], + ["a3\data_f\penetration\armour_plate_100mm.bisurf","metal"], + ["a3\data_f\penetration\armour_plate_12mm.bisurf","metal"], + ["a3\data_f\penetration\armour_plate_16mm.bisurf","metal"], + ["a3\data_f\penetration\armour_plate_1mm.bisurf","metal"], + ["a3\data_f\penetration\armour_plate_20mm.bisurf","metal"], + ["a3\data_f\penetration\armour_plate_23mm.bisurf","metal"], + ["a3\data_f\penetration\armour_plate_250mm.bisurf","metal"], + ["a3\data_f\penetration\armour_plate_30mm.bisurf","metal"], + ["a3\data_f\penetration\armour_plate_3mm.bisurf","metal"], + ["a3\data_f\penetration\armour_plate_40mm.bisurf","metal"], + ["a3\data_f\penetration\armour_plate_5mm.bisurf","metal"], + ["a3\data_f\penetration\armour_plate_60mm.bisurf","metal"], + ["a3\data_f\penetration\armour_plate_7mm.bisurf","metal"], + ["a3\data_f\penetration\armour_plate_80mm.bisurf","metal"], + ["a3\data_f\penetration\armour_plate_heavy.bisurf","metal"], + ["a3\data_f\penetration\armour_plate_medium.bisurf","metal"], + ["a3\data_f\penetration\armour_plate_thin.bisurf","metal"], + ["a3\data_f\penetration\bell.bisurf","ground"], + ["a3\data_f\penetration\body.bisurf","ground"], + ["a3\data_f\penetration\building.bisurf","concrete"], + ["a3\data_f\penetration\building_dust_particle.bisurf","concrete"], + ["a3\data_f\penetration\building_dust_soft.bisurf","concrete"], + ["a3\data_f\penetration\building_plate.bisurf","concrete"], + ["a3\data_f\penetration\building_wood_particle.bisurf","wood"], + ["a3\data_f\penetration\cactus.bisurf","ground"], + ["a3\data_f\penetration\cloth.bisurf","ground"], + ["a3\data_f\penetration\cloth_plate.bisurf","ground"], + ["a3\data_f\penetration\concrete.bisurf","concrete"], + ["a3\data_f\penetration\concrete_plate.bisurf","concrete"], + ["a3\data_f\penetration\default.bisurf","ground"], + ["a3\data_f\penetration\engine.bisurf","metal"], + ["a3\data_f\penetration\foliage.bisurf","ground"], + ["a3\data_f\penetration\foliage_dead.bisurf","ground"], + ["a3\data_f\penetration\foliage_dead_plate.bisurf","ground"], + ["a3\data_f\penetration\foliage_green.bisurf","ground"], + ["a3\data_f\penetration\foliage_green_big.bisurf","ground"], + ["a3\data_f\penetration\foliage_green_big_plate.bisurf","ground"], + ["a3\data_f\penetration\foliage_green_plate.bisurf","ground"], + ["a3\data_f\penetration\foliage_palm.bisurf","ground"], + ["a3\data_f\penetration\foliage_palm_plate.bisurf","ground"], + ["a3\data_f\penetration\foliage_pine.bisurf","ground"], + ["a3\data_f\penetration\foliage_pine_plate.bisurf","ground"], + ["a3\data_f\penetration\foliage_plate.bisurf","ground"], + ["a3\data_f\penetration\fueltank.bisurf","metal"], + ["a3\data_f\penetration\glass.bisurf","ground"], + ["a3\data_f\penetration\glass_armored.bisurf","ground"], + ["a3\data_f\penetration\glass_armored_plate.bisurf","ground"], + ["a3\data_f\penetration\glass_plate.bisurf","ground"], + ["a3\data_f\penetration\granite.bisurf","ground"], + ["a3\data_f\penetration\granite_plate.bisurf","ground"], + ["a3\data_f\penetration\hard_ground.bisurf","ground"], + ["a3\data_f\penetration\hay.bisurf","ground"], + ["a3\data_f\penetration\iron_cast.bisurf","metal"], + ["a3\data_f\penetration\iron_cast_plate.bisurf","metal"], + ["a3\data_f\penetration\leather.bisurf","ground"], + ["a3\data_f\penetration\meat.bisurf","ground"], + ["a3\data_f\penetration\meatbones.bisurf","ground"], + ["a3\data_f\penetration\medium_ground.bisurf","ground"], + ["a3\data_f\penetration\metal.bisurf","metal"], + ["a3\data_f\penetration\metal_plate.bisurf","metal"], + ["a3\data_f\penetration\metal_plate_thin.bisurf","metal"], + ["a3\data_f\penetration\plastic.bisurf","ground"], + ["a3\data_f\penetration\plastic_plate.bisurf","ground"], + ["a3\data_f\penetration\plexiglass.bisurf","ground"], + ["a3\data_f\penetration\plexiglass_plate.bisurf","ground"], + ["a3\data_f\penetration\rubber.bisurf","ground"], + ["a3\data_f\penetration\soft_ground.bisurf","ground"], + ["a3\data_f\penetration\tyre.bisurf","ground"], + ["a3\data_f\penetration\tyre_armored.bisurf","ground"], + ["a3\data_f\penetration\vehicle_interior.bisurf","metal"], + ["a3\data_f\penetration\void.bisurf","ground"], + ["a3\data_f\penetration\water.bisurf","ground"], + ["a3\data_f\penetration\weapon_plate.bisurf","metal"], + ["a3\data_f\penetration\wood.bisurf","wood"], + ["a3\data_f\penetration\wood_plate.bisurf","wood"] +]; diff --git a/addons/frag/initSettings.inc.sqf b/addons/frag/initSettings.inc.sqf index b05f3280dd..07359b772e 100644 --- a/addons/frag/initSettings.inc.sqf +++ b/addons/frag/initSettings.inc.sqf @@ -8,13 +8,6 @@ private _category = format ["ACE %1", localize LSTRING(Module_DisplayName)]; 1 ] call CBA_fnc_addSetting; -[ - QGVAR(spallEnabled), "CHECKBOX", - [LSTRING(EnableSpall), LSTRING(EnableSpall_Desc)], - _category, - false, - 1 -] call CBA_fnc_addSetting; [ QGVAR(reflectionsEnabled), "CHECKBOX", [LSTRING(EnableReflections), LSTRING(EnableReflections_Desc)], @@ -22,3 +15,22 @@ private _category = format ["ACE %1", localize LSTRING(Module_DisplayName)]; false, 1 ] call CBA_fnc_addSetting; + +[ + QGVAR(spallEnabled), "CHECKBOX", + [LSTRING(EnableSpall), LSTRING(EnableSpall_Desc)], + _category, + false, + 1 +] call CBA_fnc_addSetting; + +[ + QGVAR(spallIntensity), "SLIDER", + [LSTRING(SpallIntensity), LSTRING(SpallIntensity_Desc)], + _category, + [0.1, 2, 1, 1], + 1, + { + GVAR(shouldSpallCache) = createHashMap; + } +] call CBA_fnc_addSetting; \ No newline at end of file diff --git a/addons/frag/script_component.hpp b/addons/frag/script_component.hpp index c0148818a5..29167c504b 100644 --- a/addons/frag/script_component.hpp +++ b/addons/frag/script_component.hpp @@ -3,9 +3,9 @@ #include "\z\ace\addons\main\script_mod.hpp" // #define LOG_FRAG_INFO -// #define DEBUG_MODE_FULL -// #define DEBUG_MODE_DRAW -// #define DISABLE_COMPILE_CACHE +#define DEBUG_MODE_FULL +#define DEBUG_MODE_DRAW +#define DISABLE_COMPILE_CACHE // #define ENABLE_PERFORMANCE_COUNTERS #ifdef DEBUG_ENABLED_FRAG @@ -19,6 +19,8 @@ #include "\z\ace\addons\main\script_macros.hpp" +#define GLUE(g1,g2) g1##g2 + // Mimimum hold-off time between frag events per unit #define ACE_FRAG_FRAG_UNIT_HOLDOFF 0.5 #define ACE_FRAG_SPALL_UNIT_HOLDOFF 0.5 @@ -28,3 +30,14 @@ #define ACE_FRAG_COUNT_MAX_TIME 1.5 #define ACE_FRAG_COUNT_MAX 50 #define ACE_FRAG_IMPERIC_VELOCITY_CONSTANT 0.8 + +// Spall values +#define ACE_FRAG_SPALL_HOLDOFF 0.2 +#define ACE_FRAG_SPALL_VELOCITY_INHERIT_COEFF 0.8 +// sqrt(2)/50 +#define ACE_FRAG_SPALL_CALIBER_COEF 0.02828427 +#define ACE_FRAG_SPALL_POWER_MIN 2 +#define ACE_FRAG_SPALL_POWER_TINY_MAX 5 +#define ACE_FRAG_SPALL_POWER_SMALL_MAX 8 +#define ACE_FRAG_SPALL_POWER_MEDIUM_MAX 11 +#define ACE_FRAG_SPALL_POWER_LARGE_MAX 15 diff --git a/addons/frag/stringtable.xml b/addons/frag/stringtable.xml index 8da69da086..c80af0708a 100644 --- a/addons/frag/stringtable.xml +++ b/addons/frag/stringtable.xml @@ -114,6 +114,12 @@ Debug + + Spalling Intensity + + + Modifier to increase or decrease the number and intensity of spalling events. Increasing this value may cause performance degradation. + Frag/Spall Debug Tracing Splitter-/Explosions-Debug-Verfolgung