Initial commit

This commit is contained in:
lambdatiger 2024-08-23 18:27:19 -05:00
parent 82d9044a9a
commit 182ebc027b
13 changed files with 597 additions and 162 deletions

View File

@ -1,15 +1,158 @@
class GVAR(spall_small): GVAR(small) { class GVAR(spallBase): B_65x39_Caseless {
timeToLive = 0.1; 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) { class GVAR(ground_spall_small): GVAR(spallBase) {
timeToLive = 0.25; submunitionAmmo[] = {QGVAR(small), 4,QGVAR(medium), 1};
submunitionConeType[] = {"poissondisccenter", 6};
}; };
class GVAR(spall_huge): GVAR(huge) { class GVAR(ground_spall_medium): GVAR(spallBase) {
timeToLive = 0.3; 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};
}; };

View File

@ -9,11 +9,15 @@ PREP(dev_trackHitBox);
PREP(dev_trackObj); PREP(dev_trackObj);
PREP(doExplosions); PREP(doExplosions);
PREP(doReflections); PREP(doReflections);
PREP(doSpall); PREP(doSpallHitPart);
PREP(doSpallPenetrate);
PREP(findReflections); PREP(findReflections);
PREP(fired); PREP(fired);
PREP(frago); PREP(frago);
PREP(getFragInfo); PREP(getFragInfo);
PREP(getMaterialInfo);
PREP(getSpallInfo); PREP(getSpallInfo);
PREP(getSpallInfo);
PREP(initMaterialCache);
PREP(shouldFrag); PREP(shouldFrag);
PREP(shouldSpall); PREP(shouldSpall);

View File

@ -6,8 +6,10 @@ PREP_RECOMPILE_START;
#include "XEH_PREP.hpp" #include "XEH_PREP.hpp"
PREP_RECOMPILE_END; PREP_RECOMPILE_END;
call FUNC(initMaterialCache);
GVAR(spallInfoCache) = createHashMap; GVAR(spallInfoCache) = createHashMap;
GVAR(shouldSpallCache) = createHashMap; GVAR(shouldSpallCache) = createHashMap;
GVAR(nextSpallAllowTime) = -1;
GVAR(shouldFragCache) = createHashMap; GVAR(shouldFragCache) = createHashMap;
GVAR(fragInfoCache) = createHashMap; GVAR(fragInfoCache) = createHashMap;

View File

@ -19,11 +19,10 @@ params ["_projectile"];
TRACE_2("addBlackList",_projectile,typeOf projectile); TRACE_2("addBlackList",_projectile,typeOf projectile);
_projectile setVariable [QGVAR(blacklisted), true]; _projectile setVariable [QGVAR(blacklisted), true];
_projectile removeEventHandler [ (_projectile getVariable [QGVAR(spallEH), [-1, -1]]) params ["_hitPartEH", "_penetratedEH"];
"HitPart", _projectile removeEventHandler ["HitPart", _hitPartEH];
_projectile getVariable [QGVAR(hitPartEventHandler), -1] _projectile removeEventHandler ["Penetrated", _penetratedEH];
];
_projectile removeEventHandler [ _projectile removeEventHandler [
"Explode", "Explode",
_projectile getVariable [QGVAR(explodeEventHandler), -1] _projectile getVariable [QGVAR(fragEH), -1]
]; ];

View File

@ -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 <OBJECT>
* 1: The config name of the projectile <STRING>
* 2: The projectile that should cause spalling <OBJECT>
* 3: The position (ASL) the projectile hit the object <ARRAY>
* 4: The old velocity of the projectile <ARRAY>
* 5: The projectile's shotParents <ARRAY>
*
* 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
};

View File

@ -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 <OBJECT>
* 1: The object the projectile hit <OBJECT>
* 2: The position (ASL) of the projectile hit<ARRAY>
* 3: The velocity of the projectile before it hit the surface <ARRAY>
* 4: The surface normal of the surface hit <ARRAY>
* 5: The name of the config name or path to bisurf of the surface hit <STRING>
* 6: The projectiles ammo config <STRING>
* 7: The projectiles shot parent <ARRAY>
* 8: The "up" vector of the projectile when it hit the object <ARRAY>
*
* 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

View File

@ -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

View File

@ -28,29 +28,24 @@ if (GVAR(spallEnabled) && {_ammo call FUNC(shouldSpall)}) then {
private _hitPartEventHandler = _projectile addEventHandler [ private _hitPartEventHandler = _projectile addEventHandler [
"HitPart", "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 // 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 // The "explode" EH does not get the same parameter
private _shotParents = getShotParents _projectile; private _shotParents = getShotParents _projectile;
_shotParents set [0, objNull];
private _ammo = typeOf _projectile; private _ammo = typeOf _projectile;
private _vectorUp = vectorUp _projectile;
/* /*
* Wait a frame to see what happens to the round, may result in * Wait a frame to see what happens to the round, may result in
* multiple hits / slowdowns getting shunted to the first hit * multiple hits / slowdowns getting shunted to the first hit
*/ */
[ [LINKFUNC(doSpallHitPart), [_projectile, _objectHit, _posASL, _velocity, _surfNorm, _surfType, _ammo, _shotParents, _vectorUp]] call CBA_fnc_execNextFrame;
// 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;
} }
]; ];
_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 { if (GVAR(reflectionsEnabled) || (GVAR(enabled) && {_ammo call FUNC(shouldFrag)})) then {

View File

@ -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 <STRING>
*
* Return Value:
* Material categories as expanded on in line 44 below <STRING>
*
* 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

View File

@ -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"]
];

View File

@ -8,13 +8,6 @@ private _category = format ["ACE %1", localize LSTRING(Module_DisplayName)];
1 1
] call CBA_fnc_addSetting; ] call CBA_fnc_addSetting;
[
QGVAR(spallEnabled), "CHECKBOX",
[LSTRING(EnableSpall), LSTRING(EnableSpall_Desc)],
_category,
false,
1
] call CBA_fnc_addSetting;
[ [
QGVAR(reflectionsEnabled), "CHECKBOX", QGVAR(reflectionsEnabled), "CHECKBOX",
[LSTRING(EnableReflections), LSTRING(EnableReflections_Desc)], [LSTRING(EnableReflections), LSTRING(EnableReflections_Desc)],
@ -22,3 +15,22 @@ private _category = format ["ACE %1", localize LSTRING(Module_DisplayName)];
false, false,
1 1
] call CBA_fnc_addSetting; ] 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;

View File

@ -3,9 +3,9 @@
#include "\z\ace\addons\main\script_mod.hpp" #include "\z\ace\addons\main\script_mod.hpp"
// #define LOG_FRAG_INFO // #define LOG_FRAG_INFO
// #define DEBUG_MODE_FULL #define DEBUG_MODE_FULL
// #define DEBUG_MODE_DRAW #define DEBUG_MODE_DRAW
// #define DISABLE_COMPILE_CACHE #define DISABLE_COMPILE_CACHE
// #define ENABLE_PERFORMANCE_COUNTERS // #define ENABLE_PERFORMANCE_COUNTERS
#ifdef DEBUG_ENABLED_FRAG #ifdef DEBUG_ENABLED_FRAG
@ -19,6 +19,8 @@
#include "\z\ace\addons\main\script_macros.hpp" #include "\z\ace\addons\main\script_macros.hpp"
#define GLUE(g1,g2) g1##g2
// Mimimum hold-off time between frag events per unit // Mimimum hold-off time between frag events per unit
#define ACE_FRAG_FRAG_UNIT_HOLDOFF 0.5 #define ACE_FRAG_FRAG_UNIT_HOLDOFF 0.5
#define ACE_FRAG_SPALL_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_TIME 1.5
#define ACE_FRAG_COUNT_MAX 50 #define ACE_FRAG_COUNT_MAX 50
#define ACE_FRAG_IMPERIC_VELOCITY_CONSTANT 0.8 #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

View File

@ -114,6 +114,12 @@
<Key ID="STR_ACE_Frag_Debug"> <Key ID="STR_ACE_Frag_Debug">
<English>Debug</English> <English>Debug</English>
</Key> </Key>
<Key ID="STR_ACE_Frag_SpallIntensity">
<English>Spalling Intensity</English>
</Key>
<Key ID="STR_ACE_Frag_SpallIntensity_Desc">
<English>Modifier to increase or decrease the number and intensity of spalling events. Increasing this value may cause performance degradation.</English>
</Key>
<Key ID="STR_ACE_Frag_EnableDebugTrace"> <Key ID="STR_ACE_Frag_EnableDebugTrace">
<English>Frag/Spall Debug Tracing</English> <English>Frag/Spall Debug Tracing</English>
<German>Splitter-/Explosions-Debug-Verfolgung</German> <German>Splitter-/Explosions-Debug-Verfolgung</German>