mirror of
https://github.com/acemod/ACE3.git
synced 2024-08-30 18:23:18 +00:00
Initial rollback to old frag simulation and spalling simulation. Updated spall to function and helpter functions as well
This commit is contained in:
parent
15de0d9a3a
commit
b656ef6467
@ -87,6 +87,5 @@ class CfgAmmo {
|
||||
};
|
||||
|
||||
#include "CfgAmmoSpall.hpp"
|
||||
#include "CfgAmmoFragSpawner.hpp"
|
||||
#include "CfgAmmoFragParameters.hpp"
|
||||
};
|
||||
|
@ -372,7 +372,7 @@ class APERSMineDispenser_Mine_Ammo: APERSMine_Range_Ammo {
|
||||
GVAR(skip) = 1;
|
||||
};
|
||||
|
||||
class UXO_deploy_base_f: SubmunitionBase;
|
||||
class UXO_deploy_base_f: SubmunitionBase {};
|
||||
|
||||
class ClaymoreDirectionalMine_Remote_Ammo: DirectionalBombBase {
|
||||
GVAR(skip) = 1;
|
||||
|
@ -1,81 +0,0 @@
|
||||
#define TARGETED_SPAWNER_PROTOTYPE(size) class GVAR(DOUBLES(size,spawner_2_short)): GVAR(spawnbase_targeted) {\
|
||||
submunitionAmmo = QGVAR(size);\
|
||||
};\
|
||||
class GVAR(DOUBLES(size,spawner_2_mid)): GVAR(DOUBLES(size,spawner_2_short)) {\
|
||||
submunitionConeAngle = 2;\
|
||||
};\
|
||||
class GVAR(DOUBLES(size,spawner_2_far)): GVAR(DOUBLES(size,spawner_2_short)) {\
|
||||
submunitionConeAngle = 0.9;\
|
||||
};\
|
||||
class GVAR(DOUBLES(size,spawner_3_short)): GVAR(DOUBLES(size,spawner_2_short)) {\
|
||||
submunitionConeType[] = {"random", 3};\
|
||||
};\
|
||||
class GVAR(DOUBLES(size,spawner_3_mid)): GVAR(DOUBLES(size,spawner_3_short)) {\
|
||||
submunitionConeAngle = 2;\
|
||||
};\
|
||||
class GVAR(DOUBLES(size,spawner_3_far)): GVAR(DOUBLES(size,spawner_3_short)) {\
|
||||
submunitionConeAngle = 0.9;\
|
||||
}
|
||||
|
||||
#define RANDOM_SPAWNER_PROTOTYPE(size,count) class GVAR(DOUBLES(TRIPLES(random,size,count),mid)): GVAR(spawnbase) {\
|
||||
submunitionConeType[] = {"random", count};\
|
||||
submunitionAmmo = QGVAR(size);\
|
||||
submunitionConeAngle = 85;\
|
||||
triggerSpeedCoef[] = {-1.5, 1.5};\
|
||||
};\
|
||||
class GVAR(DOUBLES(TRIPLES(random,size,count),high)): GVAR(spawnbase) {\
|
||||
submunitionConeType[] = {"random", count};\
|
||||
submunitionAmmo = QGVAR(size);\
|
||||
submunitionConeAngle = 80;\
|
||||
triggerSpeedCoef[] = {0.75, 1.5};\
|
||||
};\
|
||||
class GVAR(DOUBLES(TRIPLES(random,size,count),top)): GVAR(spawnbase) {\
|
||||
submunitionConeType[] = {"random", count};\
|
||||
submunitionAmmo = QGVAR(size);\
|
||||
submunitionConeAngle = 60;\
|
||||
triggerSpeedCoef[] = {0.75, 1.5};\
|
||||
}
|
||||
|
||||
class GVAR(spawnbase): B_65x39_Caseless {
|
||||
GVAR(skip) = 1;
|
||||
deleteParentWhenTriggered = 1;
|
||||
explosionEffects = "";
|
||||
submunitionConeType[] = {"random", 25};
|
||||
submunitionAmmo[] = {QGVAR(tiny), 3, QGVAR(tiny_HD), 3, QGVAR(small), 4, QGVAR(small_HD), 4, QGVAR(medium_HD), 5};
|
||||
submunitionDirectionType = "SubmunitionModelDirection";
|
||||
submunitionConeAngleHorizontal = 15;
|
||||
submunitionConeAngle = 87;
|
||||
submunitionInitialOffset[] = {0,0,0};
|
||||
submunitionInitSpeed = 0;
|
||||
submunitionParentSpeedCoef = 1;
|
||||
triggerSpeedCoef[] = {0.75, 1.5};
|
||||
triggerTime = 0;
|
||||
};
|
||||
|
||||
RANDOM_SPAWNER_PROTOTYPE(tiny,15);
|
||||
RANDOM_SPAWNER_PROTOTYPE(tiny,10);
|
||||
RANDOM_SPAWNER_PROTOTYPE(tiny,5);
|
||||
RANDOM_SPAWNER_PROTOTYPE(small,15);
|
||||
RANDOM_SPAWNER_PROTOTYPE(small,10);
|
||||
RANDOM_SPAWNER_PROTOTYPE(small,5);
|
||||
|
||||
/*
|
||||
* Targeted fragment spawner, for when multiple fragments are spawned (1-3)
|
||||
*/
|
||||
class GVAR(spawnbase_targeted): GVAR(spawnbase) {
|
||||
submunitionConeType[] = {"random", 2};
|
||||
submunitionConeAngle = 4.5;
|
||||
triggerSpeedCoef[] = {0.5, 1};
|
||||
};
|
||||
|
||||
|
||||
TARGETED_SPAWNER_PROTOTYPE(tiny);
|
||||
TARGETED_SPAWNER_PROTOTYPE(tiny_HD);
|
||||
TARGETED_SPAWNER_PROTOTYPE(small);
|
||||
TARGETED_SPAWNER_PROTOTYPE(small_HD);
|
||||
TARGETED_SPAWNER_PROTOTYPE(medium);
|
||||
TARGETED_SPAWNER_PROTOTYPE(medium_HD);
|
||||
TARGETED_SPAWNER_PROTOTYPE(large);
|
||||
TARGETED_SPAWNER_PROTOTYPE(large_HD);
|
||||
TARGETED_SPAWNER_PROTOTYPE(huge);
|
||||
TARGETED_SPAWNER_PROTOTYPE(huge_HD);
|
@ -1,158 +1,15 @@
|
||||
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_small): GVAR(small) {
|
||||
timeToLive = 0.1;
|
||||
};
|
||||
|
||||
|
||||
/*
|
||||
* ground
|
||||
*/
|
||||
class GVAR(ground_spall_tiny): GVAR(spallBase) {
|
||||
submunitionAmmo[] = {QGVAR(tiny), 9, QGVAR(small), 1};
|
||||
submunitionConeType[] = {"poissondisccenter", 4};
|
||||
class GVAR(spall_medium): GVAR(medium) {
|
||||
timeToLive = 0.15;
|
||||
};
|
||||
|
||||
class GVAR(ground_spall_small): GVAR(spallBase) {
|
||||
submunitionAmmo[] = {QGVAR(small), 4,QGVAR(medium), 1};
|
||||
submunitionConeType[] = {"poissondisccenter", 6};
|
||||
class GVAR(spall_large): GVAR(large) {
|
||||
timeToLive = 0.25;
|
||||
};
|
||||
|
||||
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};
|
||||
};
|
||||
class GVAR(spall_huge): GVAR(huge) {
|
||||
timeToLive = 0.3;
|
||||
};
|
@ -8,18 +8,13 @@ PREP(dev_trackHitBox);
|
||||
PREP(dev_trackObj);
|
||||
PREP(dev_sphereDraw);
|
||||
PREP(doExplosions);
|
||||
PREP(doFrag);
|
||||
PREP(doFragRandom);
|
||||
PREP(doFragTargeted);
|
||||
PREP(doReflections);
|
||||
PREP(doSpallLocal);
|
||||
PREP(doSpallServer);
|
||||
PREP(findReflections);
|
||||
PREP(fired);
|
||||
PREP(getMaterialInfo);
|
||||
PREP(getSpallInfo);
|
||||
PREP(frago);
|
||||
PREP(getFragInfo);
|
||||
PREP(initMaterialCache);
|
||||
PREP(roundInitFrag);
|
||||
PREP(getSpallInfo);
|
||||
PREP(shouldFrag);
|
||||
PREP(shouldSpall);
|
||||
|
@ -28,14 +28,7 @@ GVAR(dev_drawPFEH) = -1;
|
||||
#endif
|
||||
|
||||
if (isServer) then {
|
||||
[
|
||||
QGVAR(explosionEvent),
|
||||
{
|
||||
params ["_posASL", "_velocity", "_ammo", "_shotParents"];
|
||||
[_posASL, _velocity, _ammo, _shotParents] call FUNC(doFrag);
|
||||
}
|
||||
] call CBA_fnc_addEventHandler;
|
||||
|
||||
[QGVAR(frag_eh), LINKFUNC(frago)] call CBA_fnc_addEventHandler;
|
||||
[QGVAR(spallEvent), LINKFUNC(doSpallServer)] call CBA_fnc_addEventHandler;
|
||||
};
|
||||
|
||||
|
@ -8,7 +8,7 @@ class CfgPatches {
|
||||
requiredVersion = REQUIRED_VERSION;
|
||||
requiredAddons[] = {"ace_common"};
|
||||
author = ECSTRING(common,ACETeam);
|
||||
authors[] = {"Nou"};
|
||||
authors[] = {"Nou", "johnb43" "Lambda.Tiger"};
|
||||
url = ECSTRING(main,URL);
|
||||
VERSION_CONFIG;
|
||||
};
|
||||
|
@ -1,65 +0,0 @@
|
||||
#include "..\script_component.hpp"
|
||||
/*
|
||||
* Author: Jaynus, NouberNou, Lambda.Tiger
|
||||
* This function handles creating both random and targeted fragments as well as handling some of the performance optimizations.
|
||||
*
|
||||
* Arguments:
|
||||
* 0: Position (posASL) of projectile <ARRAY>
|
||||
* 1: Velocity of projectile <ARRAY>
|
||||
* 2: Projectile CfgAmmo classname <STRING>
|
||||
* 3: getShotParents of projectile at EH <ARRAY>
|
||||
*
|
||||
* Return Value:
|
||||
* None
|
||||
*
|
||||
* Example:
|
||||
* [getPosASL _projectile, velocity _projectile, typeOf _projectile, getShotParents _projectile] call ace_frag_fnc_doFrag
|
||||
*
|
||||
* Public: No
|
||||
*/
|
||||
|
||||
#define ACE_FRAG_MIN_FRAG_BUDGET_FOR_RANDOM 3
|
||||
#define ACE_FRAG_NEGATIVE_AGL_OFFSET 0.1
|
||||
|
||||
params ["_posASL", "_projectileVelocity", "_ammo", "_shotParents"];
|
||||
TRACE_4("doFrag",_posASL,_projectileVelocity,_ammo,_shotParents);
|
||||
|
||||
// Don't let a single object cause all fragmentation events
|
||||
_shotParents params ["_shotParentVehicle"];
|
||||
if (_shotParentVehicle getVariable [QGVAR(obj_nextFragTime), -1] > CBA_missionTime) exitWith {
|
||||
TRACE_1("vehicleTimeExit",_shotParentVehicle);
|
||||
};
|
||||
|
||||
// Check normal round timeout and adjust _max frags
|
||||
private _timeSinceLastFrag = CBA_missionTime - GVAR(lastFragTime);
|
||||
if (_timeSinceLastFrag < ACE_FRAG_HOLDOFF || {_posASL isEqualTo [0, 0, 0]} || {_ammo isEqualTo ""}) exitWith {
|
||||
TRACE_3("timeExit",_timeSinceLastFrag,CBA_missionTime,GVAR(lastFragTime));
|
||||
};
|
||||
TRACE_3("willFrag",_timeSinceLastFrag,CBA_missionTime,_maxFragCount);
|
||||
_shotParentVehicle setVariable [QGVAR(obj_nextFragTime), CBA_missionTime + ACE_FRAG_HOLDOFF_VEHICLE];
|
||||
private _maxFragCount = round linearConversion [ACE_FRAG_COUNT_MIN_TIME, ACE_FRAG_COUNT_MAX_TIME, _timeSinceLastFrag, ACE_FRAG_COUNT_MIN, ACE_FRAG_COUNT_MAX, true];
|
||||
|
||||
_ammo call FUNC(getFragInfo) params ["_fragRange", "_fragVelocity", "_fragTypes", "_modFragCount"];
|
||||
// For low frag rounds limit the # of frags created
|
||||
if (_modFragCount < ACE_FRAG_LOW_FRAG_MOD_COUNT) then {
|
||||
_maxFragCount = _modFragCount * ACE_FRAG_LOW_FRAG_COEFF;
|
||||
GVAR(lastFragTime) = CBA_missionTime - ACE_FRAG_LOW_FRAG_HOLDOFF_REDUCTION;
|
||||
} else {
|
||||
GVAR(lastFragTime) = CBA_missionTime;
|
||||
};
|
||||
|
||||
// Double check if round is below, or too close to terrain for
|
||||
private _posAGL = ASLToAGL _posASL;
|
||||
if (_posAGL#2 < ACE_FRAG_NEGATIVE_AGL_OFFSET) then {
|
||||
TRACE_1("below 0 AGL",_posAGL);
|
||||
_posAGL set [2, ACE_FRAG_NEGATIVE_AGL_OFFSET];
|
||||
};
|
||||
|
||||
TRACE_3("doFrag choices",_maxFragCount,_fragRange,GVAR(fragSimComplexity));
|
||||
if (GVAR(fragSimComplexity) != 1 && _fragRange > 3) then {
|
||||
_maxFragCount = _maxFragCount - ([_posAGL, _fragVelocity, _fragRange, _maxFragCount, _fragTypes, _modFragCount, _shotParents] call FUNC(doFragTargeted));
|
||||
};
|
||||
|
||||
if (GVAR(fragSimComplexity) > 0 && _maxFragCount >= ACE_FRAG_MIN_FRAG_BUDGET_FOR_RANDOM) then {
|
||||
[_posAGL, _fragVelocity, _projectileVelocity, _fragTypes, _maxFragCount, _shotParents] call FUNC(doFragRandom);
|
||||
};
|
@ -1,62 +0,0 @@
|
||||
#include "..\script_component.hpp"
|
||||
/*
|
||||
* Author: Jaynus, NouberNou, Lambda.Tiger
|
||||
* This function creates fragments randomly spreading out from an explosion.
|
||||
* This function will spawn 5, 10, or 15 fragments.
|
||||
*
|
||||
* Arguments:
|
||||
* 0: Position (posAGL) of fragmenting projectile <ARRAY>
|
||||
* 1: Initial fragment velocity from Gurney equation <NUMBER>
|
||||
* 2: Velocity of the fragmenting projectile <ARRAY>
|
||||
* 3: Type of fragments to generate <ARRAY>
|
||||
* 4: Remaining fragment budget <NUMBER>
|
||||
* 5: Shot parents <ARRAY>
|
||||
*
|
||||
* Return Value:
|
||||
* None
|
||||
*
|
||||
* Example:
|
||||
* [ASLtoAGL (getPosASL _projectile), 800, 50, 50, [], 1, [player, player]] call ace_frag_fnc_doFragRandom
|
||||
*
|
||||
* Public: No
|
||||
*/
|
||||
|
||||
params ["_posAGL", "_fragVelocity", "_projectileVelocity", "_fragType", "_maxFragCount", "_shotParents"];
|
||||
TRACE_6("doFragRandom",_posAGL,_fragVelocity,_projectileVelocity,_fragType,_maxFragCount,_shotParents);
|
||||
|
||||
// See CfgAmmoFragSpawner for different frag types
|
||||
private _heightAGL = _posAGL#2;
|
||||
private _hMode = switch (true) do {
|
||||
case (_heightAGL > 10): {"_top"};
|
||||
case (_heightAGL > 4): {"_high"};
|
||||
default {"_mid"};
|
||||
};
|
||||
|
||||
private _type = [QGVAR(random_small_), QGVAR(random_tiny_)] select (_fragType isNotEqualTo [] && {"ace_frag_tiny" == (_fragType#0)});
|
||||
|
||||
_maxFragCount = switch (true) do {
|
||||
case (_maxFragCount <= 5): {"5"};
|
||||
case (_maxFragCount <= 10): {"10"};
|
||||
default {"15"};
|
||||
};
|
||||
|
||||
// Spawn the fragment spawner
|
||||
private _fragSpawner = createVehicle [_type + _maxFragCount + _hMode, _posAGL, [], 0, "CAN_COLLIDE"];
|
||||
private _randDir = random 360;
|
||||
_fragSpawner setVectorDirandUp [[0,0,-1], [cos _randDir, sin _randDir, 0]];
|
||||
_fragSpawner setVelocity (_projectileVelocity vectorAdd [0, 0, -_fragVelocity]);
|
||||
_fragSpawner setShotParents _shotParents;
|
||||
|
||||
TRACE_2("spawnedRandomFragmenter",typeOf _fragSpawner,getObjectID _fragSpawner);
|
||||
#ifdef DEBUG_MODE_DRAW
|
||||
_fragSpawner addEventHandler [
|
||||
"SubmunitionCreated",
|
||||
{
|
||||
params ["","_subProj"];
|
||||
[_subProj, "green", true] call FUNC(dev_trackObj);
|
||||
}
|
||||
];
|
||||
if (GVAR(dbgSphere)) then {
|
||||
[AGLtoASL _posAGL] call FUNC(dev_sphereDraw);
|
||||
};
|
||||
#endif
|
@ -1,184 +0,0 @@
|
||||
#include "..\script_component.hpp"
|
||||
/*
|
||||
* Author: Jaynus, NouberNou, Lambda.Tiger
|
||||
* This function creates fragments targeted at specific entities, up to _maxFrags.
|
||||
*
|
||||
* Arguments:
|
||||
* 0: Position of fragmenting projectile AGL <ARRAY>
|
||||
* 1: Velocity of the fragmenting projectile <ARRAY>
|
||||
* 2: Maximum range of fragments to calculate <NUMBER>
|
||||
* 3: Maximum number of fragments to produce <NUMBER>
|
||||
* 4: Types of fragments <ARRAY>
|
||||
* 5: A modified parameter used to calculate whether a fragment hits <NUMBER>
|
||||
* 6: Shot parent <ARRAY>
|
||||
*
|
||||
* Return Value:
|
||||
* Number of fragments created <NUMBER>
|
||||
*
|
||||
* Example:
|
||||
* [ASLtoAGL (getPosASL _projectile), velocity _projectile, 50, 50, [], 1, [player, player]] call ace_frag_fnc_doFragTargeted
|
||||
*
|
||||
* Public: No
|
||||
*/
|
||||
|
||||
#define ACE_FRAG_DEFAULT_HEIGHT 0.5
|
||||
#define ACE_FRAG_DEFAULT_CROSS_AREA 0.75
|
||||
#define ACE_FRAG_MIN_TARGET_AREA 0.5
|
||||
|
||||
params [ "_posAGL", "_fragVelocity", "_fragRange", "_maxFrags", "_fragTypes", "_modFragCount", "_shotParents"];
|
||||
TRACE_5("fnc_doFragTargeted",_posAGL,_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)
|
||||
];
|
||||
};
|
||||
|
||||
// Post 2.18 change - uncomment line 41, and remove lines 43, 50-55, 64-66
|
||||
// private _targets = [_posAGL, _fragRange, _fragRange, 0, false, _fragRange] nearEntities [["Car", "Motorcycle", "Tank", "StaticWeapon", "CAManBase", "Air", "Ship"], false, true, true];
|
||||
private _objects = _posAGL nearEntities [["Car", "Motorcycle", "Tank", "StaticWeapon", "CAManBase", "Air", "Ship"], _fragRange];
|
||||
if (_objects isEqualTo []) exitWith {
|
||||
TRACE_2("No nearby targets",_posAGL,_fragRange);
|
||||
0
|
||||
};
|
||||
|
||||
// grab crews and add them in so that targets stay approx. sorted by distance
|
||||
private _targets = [];
|
||||
{
|
||||
private _crew = crew _x;
|
||||
_crew pushBackUnique _x;
|
||||
_targets append _crew;
|
||||
} forEach _objects;
|
||||
|
||||
TRACE_3("Targets found",_posAGL,_fragRange,count _targets);
|
||||
|
||||
// limit number of fragments per direction (2D) to _fragsPerFragArc using _fragArcs
|
||||
private _fragArcs = createHashMap;
|
||||
private _fragsPerFragArc = _modFragCount * ACE_FRAG_FRAGS_PER_ARC_CONSTANT;
|
||||
private _totalFragCount = 0;
|
||||
{ // Begin of forEach iterating on _targets
|
||||
// Ignore dead units, curators and spectators
|
||||
if (!alive _x || {getNumber ((configOf _x) >> "isPlayableLogic") == 1}) then {
|
||||
TRACE_1("dead or logic",_x);
|
||||
continue;
|
||||
};
|
||||
private _target = _x;
|
||||
|
||||
#ifdef DEBUG_MODE_DRAW
|
||||
[_target, false] call FUNC(dev_trackHitBox);
|
||||
#endif
|
||||
|
||||
// Estimate volume and height of target
|
||||
private _height = ACE_FRAG_DEFAULT_HEIGHT;
|
||||
private _crossSectionArea = ACE_FRAG_DEFAULT_CROSS_AREA;
|
||||
private _isPerson = _target isKindOf "CAManBase";
|
||||
if (_isPerson) then {
|
||||
switch (stance _target) do {
|
||||
case "STAND": {
|
||||
_height = 1.9;
|
||||
_crossSectionArea = 1.5;
|
||||
};
|
||||
case "CROUCH": {
|
||||
_height = 1.2;
|
||||
_crossSectionArea = 1;
|
||||
};
|
||||
};
|
||||
} else {
|
||||
private _boxParams = boundingBoxReal [_target, "FireGeometry"];
|
||||
_boxParams params ["_pointA", "_pointB"];
|
||||
private _dims = _pointB vectorDiff _pointA;
|
||||
if (_dims#0 * _dims#1 * _dims#2 <= ACE_FRAG_MIN_TARGET_AREA) then {
|
||||
continue;
|
||||
};
|
||||
_crossSectionArea = _dims#1 * _dims#2;
|
||||
_height = _dims#2;
|
||||
};
|
||||
|
||||
private _distance = _target distance _posAGL;
|
||||
|
||||
// calculate chance to be hit by a fragment
|
||||
private _fragChance = _crossSectionArea * _modFragCount / _distance^2;
|
||||
private _fragCount = if (_fragChance > 1) then {
|
||||
3 min (floor _fragChance);
|
||||
} else {
|
||||
parseNumber (GVAR(atLeastOne) || {random 1 < _fragChance});
|
||||
};
|
||||
if (_fragCount == 0) then {
|
||||
TRACE_2("no fragments",_fragChance,_fragCount);
|
||||
continue;
|
||||
};
|
||||
|
||||
// handle limiting fragments per degree arc
|
||||
private _dir = floor (_posAGL getDir _target);
|
||||
private _fragPerArc = _fragArcs getOrDefault [_dir, 0];
|
||||
if (_fragPerArc > _fragsPerFragArc) then {
|
||||
continue;
|
||||
} else {
|
||||
_fragArcs set [_dir, _fragPerArc + _fragCount];
|
||||
};
|
||||
|
||||
// Approximate offset to hit including speed & gravity
|
||||
private _locFragVel = _fragVelocity * (1 - random 0.5);
|
||||
private _timeOfFlight = _distance / _locFragVel;
|
||||
|
||||
// target pos for fragment to hit
|
||||
private _targetPos = (velocity _target vectorMultiply _timeOfFlight) vectorAdd [0, 0, ACE_FRAG_HALF_GRAVITY_APPROX * _timeOfFlight ^ 2];
|
||||
_targetPos = if (_isPerson) then {
|
||||
private _hitPoint = selectRandom ACE_FRAG_HITPOINTS;
|
||||
private _hitPointPos = _target selectionPosition [_hitPoint, "HitPoints", "AveragePoint"];
|
||||
_target modelToWorld _hitPointPos vectorAdd _targetPos;
|
||||
} else {
|
||||
ASLtoAGL (_targetPos vectorAdd 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 (_fragCount > 1) then {
|
||||
_fragSpawner = _fragSpawner + "_spawner_" + str _fragCount + (switch (true) do {
|
||||
case (_distance < 10): {"_short"};
|
||||
case (_distance < 20): {"_mid"};
|
||||
default {"_far"};
|
||||
});
|
||||
};
|
||||
TRACE_4("fragments",_fragSpawner,_fragChance,_distance,_locFragVel);
|
||||
|
||||
// Create fragment
|
||||
private _vectorDir = _posAGL vectorFromTo _targetPos;
|
||||
private _fragObj = createVehicle [_fragSpawner, _posAGL, [], 0, "CAN_COLLIDE"];
|
||||
_fragObj setVectorDirAndUp [_vectorDir, _vectorDir vectorAdd [0, -1, 0]] ;
|
||||
_fragObj setVelocity (_vectorDir vectorMultiply _locFragVel);
|
||||
_fragObj setShotParents _shotParents;
|
||||
#ifdef DEBUG_MODE_DRAW
|
||||
[_fragObj, "purple", true] call FUNC(dev_trackObj);
|
||||
_fragObj addEventHandler [
|
||||
"SubmunitionCreated",
|
||||
{
|
||||
params ["","_subProj"];
|
||||
[_subProj, "purple", true] call FUNC(dev_trackObj);
|
||||
}
|
||||
];
|
||||
if (GVAR(dbgSphere)) then {
|
||||
[AGLtoASL _targetPos, "orange"] call FUNC(dev_sphereDraw);
|
||||
};
|
||||
#endif
|
||||
|
||||
_totalFragCount = _totalFragCount + _fragCount;
|
||||
if (_totalFragCount >= _maxFrags) then {
|
||||
TRACE_2("maxFrags",_totalFragCount,_maxFrags);
|
||||
break;
|
||||
};
|
||||
} forEach _targets;
|
||||
|
||||
#ifdef DEBUG_MODE_FULL
|
||||
systemChat ("targeted frag count: " + str _totalFragCount);
|
||||
TRACE_1("targeted frag count",_totalFragCount);
|
||||
#endif
|
||||
_totalFragCount
|
@ -1,134 +1,79 @@
|
||||
#include "..\script_component.hpp"
|
||||
/*
|
||||
* Author: Jaynus, NouberNou, Lambda.Tiger
|
||||
* This function runs on every client and determines if a spall event happened.
|
||||
* It is intended to be called a frame after the projectile has triggered a hit event,
|
||||
* where information such as the object hit, the projectiles velocity and position on impact
|
||||
* should be kept and passed to this function.If a spall event should happen, this function
|
||||
* calculates the spalling parameters and then calls a server event to create the desired spall effect.
|
||||
* Author: Jaynus, NouberNou, Lambda.Tiger,
|
||||
* This function check whether a spall event has occured and generates a spall event
|
||||
* request for the server if one is needed.
|
||||
*
|
||||
* Arguments:
|
||||
* 0: The projectile that may be creating spall <OBJECT>
|
||||
* 1: The object the projectile hit <OBJECT>
|
||||
* 2: The 3D position (ASL) of the projectile when it hit the object <ARRAY>
|
||||
* 3: The velocity of the projectile when it hit the object <ARRAY>
|
||||
* 4: The normal of the surface of the object hit <ARRAY>
|
||||
* 5: The class name of the surface, or the bisurf path of the surface hit <STRING>
|
||||
* 6: The class name of the projectile that may be spalling <STRING>
|
||||
* 7: The spalling projectile's shot parents <ARRAY>
|
||||
* 8: The "up" vector of the projectile when it hit the object <ARRAY>
|
||||
* 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 the projectile hit the object <ARRAY>
|
||||
* 4: The old velocity of the projectile <ARRAY>
|
||||
*
|
||||
* Return Value:
|
||||
* None
|
||||
*
|
||||
* Example:
|
||||
* [_projectile, _hitObject, _lastPosASLProjectile, _lastVelocityProjectile, _surfaceNorm, "a3\data_f\penetration\armour_plate.bisurf", "Sh_125mm_APFSDS", [0, 0, 1]] call ace_frag_fnc_doSpallLocal
|
||||
* [[1000, 45, 60], 0.8, getPosASL ace_player] call ace_frag_fnc_doSpallServer
|
||||
*
|
||||
* Public: No
|
||||
*/
|
||||
params ["_objectHit", "_roundType", "_round", "_oldPos", "_oldVelocity",];
|
||||
|
||||
#define GLUE(g1,g2) g1##g2
|
||||
TRACE_1("",_objectHit);
|
||||
if ((isNil "_objectHit") || {isNull _objectHit}) exitWith {WARNING_1("Problem with hitPart data - bad object [%1]",_objectHit);};
|
||||
|
||||
if (CBA_missionTime < GVAR(nextSpallAllowTime)) exitWith {
|
||||
TRACE_2("time exit",CBA_missionTime,GVAR(nextSpallAllowTime));
|
||||
};
|
||||
private _caliber = getNumber (configFile >> "CfgAmmo" >> _roundType >> "caliber");
|
||||
private _explosive = getNumber (configFile >> "CfgAmmo" >> _roundType >> "explosive");
|
||||
private _idh = getNumber (configFile >> "CfgAmmo" >> _roundType >> "indirectHitRange");
|
||||
|
||||
params ["_projectile", "_objectHit", "_lastPosASL", "_lastVelocity", "_surfaceNorm", "_surfaceType", "_ammo", "_shotParents", "_vectorUp"];
|
||||
TRACE_9("doSpall",_projectile,_objectHit,_lastPosASL,_lastVelocity,_surfaceNorm,_surfaceType,_ammo,_shotParents,_vectorUp);
|
||||
_roundType call FUNC(getSpallInfo) params ["_caliber", "_explosive"];
|
||||
|
||||
if (_ammo == "" || {_objectHit isKindOf "CAManBase"}) exitWith {
|
||||
TRACE_4("invalid round or hit",CBA_missionTime,GVAR(nextSpallAllowTime),_objectHit,_lastPosASL);
|
||||
};
|
||||
// ACE_player sideChat format ["BBBB"];
|
||||
private _exit = false;
|
||||
private _vm = 1;
|
||||
|
||||
private _material = _surfaceType call FUNC(getMaterialInfo);
|
||||
if (_material == "ground") exitWith {
|
||||
TRACE_1("ground",_surfaceType);
|
||||
};
|
||||
private _curVelocity = velocity _round;
|
||||
private _oldSpeed = vectorMagnitude _oldVelocity;
|
||||
private _curSpeed = vectorMagnitude _curVelocity;
|
||||
|
||||
// Find spall speed / fragment info
|
||||
_ammo call FUNC(getSpallInfo) params ["_caliber", "_explosive", "_indirectHit"];
|
||||
private _vel = if (alive _projectile) then {
|
||||
_explosive = 0; // didn't explode since it's alive a frame later
|
||||
velocity _projectile
|
||||
} else {
|
||||
[0, 0, 0]
|
||||
};
|
||||
|
||||
private _speedChange = 0 max (vectorMagnitude _lastVelocity - vectorMagnitude _vel);
|
||||
/*
|
||||
* 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 sqrt(2)/50 * 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 _insideObject = true;
|
||||
for "_i" from 2 to 21 do
|
||||
{
|
||||
private _nextPos = _spallPosASL vectorAdd _deltaStep;
|
||||
if (!lineIntersects [_spallPosASL, _nextPos]) then {
|
||||
_spallPosASL = _nextPos vectorAdd (_deltaStep vectorMultiply 2);
|
||||
_insideObject = false;
|
||||
break
|
||||
if (alive _round) then {
|
||||
private _diff = _oldVelocity vectorDiff _curVelocity;
|
||||
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,_curSpeed / _oldSpeed);
|
||||
};
|
||||
};
|
||||
_spallPosASL = _nextPos;
|
||||
};
|
||||
if (_exit) exitWith {};
|
||||
|
||||
private _unitDir = vectorNormalized _oldVelocity;
|
||||
|
||||
private _spallPos = [];
|
||||
if ((isNil "_oldPos") || {!(_oldPos isEqualTypeArray [0,0,0])}) exitWith {WARNING_1("Problem with hitPart data - bad pos [%1]",_oldPos);};
|
||||
for "_i" from 0 to 100 do {
|
||||
private _pos1 = _oldPos vectorAdd (_unitDir vectorMultiply (0.01 * _i));
|
||||
private _pos2 = _oldPos 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;
|
||||
};
|
||||
};
|
||||
if (_spallPos isEqualTo []) exitWith {};
|
||||
private _spallPolar = _oldVelocity call CBA_fnc_vect2polar;
|
||||
|
||||
if (_explosive > 0) then {
|
||||
_shellType call FUNC(getFragInfo) params ["", "_fragVelocity"];
|
||||
_spallPolar set [0, _fragVelocity * 0.66];
|
||||
};
|
||||
|
||||
if (_insideObject) exitWith {
|
||||
TRACE_3("insideObj",_lastPosASL,_spallPosASL,alive _projectile);
|
||||
};
|
||||
// Passed all exitWiths
|
||||
GVAR(nextSpallAllowTime) = CBA_missionTime + ACE_FRAG_SPALL_HOLDOFF;
|
||||
|
||||
#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"};
|
||||
};
|
||||
|
||||
#ifdef DEBUG_MODE_FULL
|
||||
systemChat ("spd: " + str (_speedChange * ACE_FRAG_SPALL_VELOCITY_INHERIT_COEFF) + ", spallPow: " + str _spallPower);
|
||||
#endif
|
||||
|
||||
TRACE_5("Calling event:",QUOTE(GLUE(ADDON,_)) + _material + _spawnSize,_lastVelocityNorm,_vectorUp,_speedChange,_shotParents);
|
||||
[
|
||||
QGVAR(spallEvent),
|
||||
[QUOTE(GLUE(ADDON,_)) + _material + _spawnSize, _lastVelocityNorm, _vectorUp, _speedChange, _shotParents]
|
||||
] call CBA_fnc_serverEvent;
|
||||
[QGVAR(spallEvent), [_spallVelocity, _vm, _spallPos]] call CBA_fnc_serverEvent;
|
@ -1,45 +1,66 @@
|
||||
#include "..\script_component.hpp"
|
||||
/*
|
||||
* Author: Jaynus, NouberNou, Lambda.Tiger,
|
||||
* This function creates the requested spalling submunition spawner at a speed and direction.
|
||||
* The function is intended to create the spalling on a server via an event call by
|
||||
* ace_frag_fnc_doSpallLocal. The "local" version determines whether an event has occured
|
||||
* and triggers this event if it has.
|
||||
* This function creates the requested spall on the server given a location, direction, and velocity.
|
||||
*
|
||||
* Arguments:
|
||||
* 0: Class name of the spall spawner <STRING>
|
||||
* 1: Normalized 3D vector direction of the spall spawner <ARRAY>
|
||||
* 2: "Up" vector for the projectile, required for the spawner to aim out of the 2D plane <ARRAY>
|
||||
* 3: The change in velocity that spalling projectile experienced <NUMBER>
|
||||
* 4: Shot parents array for the projectile that creates spall <ARRAY>
|
||||
* 0: A spherical coordinate vector of spall velocity <ARRAY>
|
||||
* 1: You're going to have to ask Nouber what this means <NUMBER>
|
||||
* 2: The position (ASL) of the spalling point <ARRAY>
|
||||
*
|
||||
* Return Value:
|
||||
* None
|
||||
*
|
||||
* Example:
|
||||
* [QGVAR(rock_spall_tiny), [1,0,0], [0,0,1], 300, [objNull, ace_player]] call ace_frag_fnc_doSpallServer
|
||||
* [[1000, 45, 60], 0.8, getPosASL ace_player] call ace_frag_fnc_doSpallServer
|
||||
*
|
||||
* Public: No
|
||||
*/
|
||||
params ["_spallSpawnerName", "_lastVelocityNorm", "_vectorUp", "_speedChange", "_shotParents"];
|
||||
#define WEIGHTED_SIZE [QGVAR(spall_small), 4, QGVAR(spall_medium), 3, QGVAR(spall_large), 2, QGVAR(spall_huge), 1]
|
||||
params ["_spallVelocity", "_vm", "_spallPos"];
|
||||
|
||||
private _spallSpawner = createVehicle [
|
||||
_spallSpawnerName,
|
||||
ASLToATL _spallPosASL,
|
||||
[],
|
||||
0,
|
||||
"CAN_COLLIDE"
|
||||
];
|
||||
_spallSpawner setVectorDirandUp [_lastVelocityNorm, _vectorUp];
|
||||
_spallSpawner setVelocityModelSpace [0, _speedChange * ACE_FRAG_SPALL_VELOCITY_INHERIT_COEFF, 0];
|
||||
_spallSpawner setShotParents _shotParents;
|
||||
// diag_log text format ["SPALL POWER: %1", _spallVelocity select 0];
|
||||
private _spread = 15 + (random 25);
|
||||
private _spallCount = 5 + (random 10);
|
||||
TRACE_1("",_spallCount);
|
||||
for "_i" from 1 to _spallCount do {
|
||||
private _elev = ((_spallVelocity select 2) - _spread) + (random (_spread * 2));
|
||||
private _dir = ((_spallVelocity select 1) - _spread) + (random (_spread * 2));
|
||||
if (abs _elev > 90) then {
|
||||
ADD(_dir,180);
|
||||
};
|
||||
_dir = _dir % 360;
|
||||
private _vel = (_spallVelocity select 0) * 0.33 * _vm;
|
||||
_vel = (_vel - (_vel * 0.25)) + (random (_vel * 0.5));
|
||||
|
||||
#ifdef DEBUG_MODE_DRAW
|
||||
_spallSpawner addEventHandler [
|
||||
"SubmunitionCreated",
|
||||
{
|
||||
params ["", "_submunitionProjectile"];
|
||||
_submunitionProjectile call FUNC(dev_addRound);
|
||||
}
|
||||
];
|
||||
#endif
|
||||
private _spallFragVect = [_vel, _dir, _elev] call CBA_fnc_polar2vect;
|
||||
private _fragment = (selectRandomWeighted WEIGHTED_SIZE) createVehicle [0,0,10000];
|
||||
_fragment setPosASL _spallPos;
|
||||
_fragment setVelocity _spallFragVect;
|
||||
|
||||
#ifdef DRAW_FRAG_INFO
|
||||
[_fragment, "orange", true] call FUNC(dev_trackObj);
|
||||
#endif
|
||||
};
|
||||
|
||||
_spread = 5 + (random 5);
|
||||
_spallCount = 3 + (random 5);
|
||||
for "_i" from 1 to _spallCount do {
|
||||
private _elev = ((_spallVelocity select 2) - _spread) + (random (_spread * 2));
|
||||
private _dir = ((_spallVelocity select 1) - _spread) + (random (_spread * 2));
|
||||
if (abs _elev > 90) then {
|
||||
ADD(_dir,180);
|
||||
};
|
||||
_dir = _dir % 360;
|
||||
private _vel = (_spallVelocity 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) createVehicle [0, 0, 10000];
|
||||
_fragment setPosASL _spallPos;
|
||||
_fragment setVelocity _spallFragVect;
|
||||
|
||||
#ifdef DRAW_FRAG_INFO
|
||||
[_fragment, "orange", true] call FUNC(dev_trackObj);
|
||||
#endif
|
||||
};
|
@ -23,13 +23,6 @@ if (_ammo isEqualTo "" || {isNull _projectile} ||
|
||||
TRACE_2("bad ammo or projectile, or blackList",_ammo,_projectile);
|
||||
};
|
||||
|
||||
|
||||
#ifdef DEBUG_MODE_DRAW
|
||||
if (GVAR(debugOptions) && {true in (_ammo call FUNC(shouldFrag)) || {_ammo call FUNC(shouldSpall)}}) then {
|
||||
[_projectile, "red", true] call FUNC(dev_trackObj);
|
||||
};
|
||||
#endif
|
||||
|
||||
if (GVAR(spallEnabled) && {_ammo call FUNC(shouldSpall)}) then {
|
||||
_projectile addEventHandler [
|
||||
"HitPart",
|
||||
@ -45,6 +38,11 @@ if (GVAR(spallEnabled) && {_ammo call FUNC(shouldSpall)}) then {
|
||||
private _shotParent = getShotParents _projectile;
|
||||
private _ammo = typeOf _projectile;
|
||||
private _vectorUp = vectorUp _projectile;
|
||||
|
||||
// only let a unit make a frag event once per second
|
||||
private _instigator = _shotParents#1;
|
||||
if (CBA_missionTime < (_instigator getVariable [QGVAR(nextSpallEvent), -1])) exitWith {};
|
||||
_instigator setVariable [QGVAR(nextSpallEvent), CBA_missionTime + 1];
|
||||
/*
|
||||
* Wait a frame to see what happens to the round, may result in
|
||||
* multiple hits / slowdowns getting shunted to the first hit
|
||||
@ -56,10 +54,40 @@ if (GVAR(spallEnabled) && {_ammo call FUNC(shouldSpall)}) then {
|
||||
}
|
||||
];
|
||||
};
|
||||
if !(GVAR(reflectionsEnabled) || GVAR(enabled)) exitWith {
|
||||
TRACE_1("firedExit No frag/reflections",_ammo);
|
||||
|
||||
if (GVAR(reflectionsEnabled) || GVAR(enabled) && _ammo call FUNC(shouldFrag)) then {
|
||||
_projectile addEventHandler [
|
||||
"Explode",
|
||||
{
|
||||
params ["_projectile", "_posASL", "_velocity"];
|
||||
|
||||
if (GVAR(reflectionsEnabled)) then {
|
||||
[_posASL, _ammo] call FUNC(doReflections);
|
||||
};
|
||||
if (_projectile getVariable [QGVAR(blacklisted), false]) exitWith {
|
||||
TRACE_2("projectile blackisted",typeOf _projectile,_projectile);
|
||||
};
|
||||
|
||||
private _shotParents = getShotParents _projectile;
|
||||
private _ammo = typeOf _projectile;
|
||||
|
||||
// only let a unit make a frag event once per second
|
||||
private _instigator = _shotParents#1;
|
||||
if (CBA_missionTime < (_instigator getVariable [QGVAR(nextFragEvent), -1])) exitWith {};
|
||||
_instigator setVariable [QGVAR(nextFragEvent), CBA_missionTime + 1];
|
||||
|
||||
// Wait a frame to make sure it doesn't target the dead
|
||||
[
|
||||
{ [QGVAR(frag_eh), _this] call CBA_fnc_serverEvent; },
|
||||
[_posASL, _velocity, _ammo, _shotParents]
|
||||
] call CBA_fnc_execNextFrame;
|
||||
}
|
||||
];
|
||||
};
|
||||
|
||||
[_ammo, _projectile] call FUNC(roundInitFrag);
|
||||
|
||||
#ifdef DEBUG_MODE_DRAW
|
||||
if (GVAR(debugOptions) && {_ammo call FUNC(shouldFrag) || {_ammo call FUNC(shouldSpall)}}) then {
|
||||
[_projectile, "red", true] call FUNC(dev_trackObj);
|
||||
};
|
||||
#endif
|
||||
TRACE_1("firedExit",_ammo);
|
||||
|
156
addons/frag/functions/fnc_frago.sqf
Normal file
156
addons/frag/functions/fnc_frago.sqf
Normal file
@ -0,0 +1,156 @@
|
||||
#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>
|
||||
* 3: Shot parents <ARRAY>
|
||||
*
|
||||
* 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", "_shotParents"];
|
||||
TRACE_4("frago",_lastPos,_lastVel,_shellType,_shotParents);
|
||||
|
||||
// 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;
|
||||
|
||||
_shellType call ace_frag_fnc_getFragInfo params ["_fragRange", "_fragVelocity", "_fragTypes"];
|
||||
|
||||
private _atlPos = ASLtoATL _lastPos;
|
||||
|
||||
private _fragVelocityRandom = _fragVelocity * 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 - (_fragVelocity / 8)) max 0) / _fragVelocity) * 10);
|
||||
|
||||
_targetPos = _targetPos vectorAdd [
|
||||
(_targetVel select 0) * (_distance / _fragVelocity),
|
||||
(_targetVel select 1) * (_distance / _fragVelocity),
|
||||
_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 = _fragVelocity - (random (_fragVelocityRandom));
|
||||
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;
|
||||
_fragObj setShotParents _shotParents;
|
||||
#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 = (_fragVelocity - (random (_fragVelocityRandom)));
|
||||
|
||||
_vel = _vec vectorMultiply _fp;
|
||||
|
||||
_fragObj = (selectRandom _fragTypes) createVehicleLocal [0, 0, 10000];
|
||||
_fragObj setPosASL _lastPos;
|
||||
_fragObj setVectorDir _vec;
|
||||
_fragObj setVelocity _vel;
|
||||
_fragObj setShotParents _shotParents;
|
||||
|
||||
#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);
|
@ -11,7 +11,6 @@
|
||||
* 0: Search range for fragments in meters <NUMBER>
|
||||
* 1: Gurney equation calculated speed <NUMBER>
|
||||
* 2: Array of fragment types <ARRAY>
|
||||
* 3: Modified frag count under assumptions of spherical fragmentation <NUMBER>
|
||||
*
|
||||
* Example:
|
||||
* "B_556x45_Ball" call ace_frag_fnc_getFragInfo
|
||||
@ -26,7 +25,13 @@ private _ammoInfo = GVAR(fragInfoCache) get _ammo;
|
||||
if (!isNil "_ammoInfo") exitWith {_ammoInfo};
|
||||
|
||||
private _ammoConfig = configFile >> "CfgAmmo" >> _ammo;
|
||||
private _fragTypes = [];
|
||||
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 _notifyMissingEntries = false;
|
||||
if (isArray (_ammoConfig >> QGVAR(classes))) then {
|
||||
_fragTypes = getArray (_ammoConfig >> QGVAR(classes));
|
||||
@ -78,29 +83,21 @@ if (_gurneyConstant == 0) then {
|
||||
_notifyMissingEntries = true;
|
||||
};
|
||||
|
||||
private _fragCount = getNumber (_ammoConfig >> QGVAR(fragCount));
|
||||
if (_fragCount == 0) then {
|
||||
private _indirectHitRange = getNumber (_ammoConfig >> "indirectHitRange");
|
||||
_fragCount = 4 * pi * ACE_FRAG_MIN_FRAG_HIT_CHANCE * (20 *_indirectHitRange)^2;
|
||||
_fragCount = _fragCount max 250;
|
||||
_notifyMissingEntries = true;
|
||||
};
|
||||
|
||||
if (_notifyMissingEntries) then {
|
||||
INFO_1("Ammo class %1 lacks proper explosive properties definitions for frag!",_ammo);
|
||||
};
|
||||
|
||||
private _indirectHitRange = getNumber(configFile >> "CfgAmmo" >> _shellType >> "indirecthitrange");
|
||||
|
||||
/********************** _ammoInfo format *************************//*
|
||||
* 0: _fragRange - search range for fragments, calculated with the minimum chance to hit as defined
|
||||
* 1: _fragVelocity - gurney equation calculated velocity
|
||||
* 2: _fragTypes - array of fragment types
|
||||
* 3: _fragCount - modified frag count used under assumptions of spherical fragmentation
|
||||
*/
|
||||
_ammoInfo = [
|
||||
ACE_FRAG_MAX_FRAG_RANGE min sqrt (_fragCount / (4 * pi * ACE_FRAG_MIN_FRAG_HIT_CHANCE)),
|
||||
80 * _indirectHitRange,
|
||||
ACE_FRAG_IMPERIC_VELOCITY_CONSTANT * _gurneyConstant * sqrt (_chargeMass / (_metalMass + _chargeMass * _geometryCoefficient)),
|
||||
_fragTypes,
|
||||
_fragCount / 4 / pi
|
||||
_fragTypes
|
||||
];
|
||||
|
||||
GVAR(fragInfoCache) set [_ammo, _ammoInfo];
|
||||
|
@ -1,62 +0,0 @@
|
||||
#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
|
@ -23,7 +23,7 @@ params ["_ammo"];
|
||||
GVAR(spallInfoCache) getOrDefaultCall [_ammo, {
|
||||
private _ammoConfig = configFile >> "CfgAmmo" >> _ammo;
|
||||
private _caliber = getNumber (_ammoConfig >> "caliber");
|
||||
private _explosive = 1 min getNumber (_ammoConfig >> "explosive");
|
||||
private _explosive = getNumber (_ammoConfig >> "explosive");
|
||||
private _indirectHit = getNumber (_ammoConfig >> "indirectHitRange");
|
||||
|
||||
[_caliber, _explosive, _indirectHit]
|
||||
|
@ -1,96 +0,0 @@
|
||||
#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"]
|
||||
];
|
@ -1,54 +0,0 @@
|
||||
#include "..\script_component.hpp"
|
||||
/*
|
||||
* Author: Lambda.Tiger
|
||||
* Adds fragmentation event handlers to projectiles as needed.
|
||||
*
|
||||
* Arguments:
|
||||
* 0: class name of projectile being initialized <STRING>
|
||||
* 1: Projectile being initialized <OBJECT>
|
||||
*
|
||||
* Return Value:
|
||||
* Nothing
|
||||
*
|
||||
* Example:
|
||||
* [clientFiredBIS-XEH] call ace_frag_fnc_fired
|
||||
*
|
||||
* Public: No
|
||||
*/
|
||||
params ["_ammo", "_projectile"];
|
||||
|
||||
_ammo call FUNC(shouldFrag) params ["_shouldFrag", "_submunitionShouldFrag"];
|
||||
if (_shouldFrag) then {
|
||||
_projectile addEventHandler [
|
||||
"Explode",
|
||||
{
|
||||
params ["_projectile", "_posASL", "_velocity"];
|
||||
|
||||
if (GVAR(reflectionsEnabled)) then {
|
||||
[_posASL, _ammo] call FUNC(doReflections);
|
||||
};
|
||||
|
||||
if (_projectile getVariable [QGVAR(blacklisted), false]) exitWith {
|
||||
TRACE_2("projectile blackisted",typeOf _projectile,_projectile);
|
||||
};
|
||||
|
||||
private _shotParents = getShotParents _projectile;
|
||||
private _ammo = typeOf _projectile;
|
||||
// Wait a frame to make sure it doesn't
|
||||
[
|
||||
{ [QGVAR(explosionEvent), _this] call CBA_fnc_serverEvent; },
|
||||
[_posASL, _velocity, _ammo, _shotParents]
|
||||
] call CBA_fnc_execNextFrame;
|
||||
}
|
||||
];
|
||||
};
|
||||
|
||||
if (_submunitionShouldFrag) then {
|
||||
_projectile addEventHandler [
|
||||
"SubmunitionCreated",
|
||||
{
|
||||
params ["", "_submunitionProjectile"];
|
||||
[typeOf _submunitionProjectile, _submunitionProjectile] call FUNC(roundInitFrag);
|
||||
}
|
||||
];
|
||||
};
|
@ -23,51 +23,30 @@ private _shouldFrag = GVAR(shouldFragCache) get _ammo;
|
||||
|
||||
if (!isNil "_shouldFrag") exitWith {_shouldFrag};
|
||||
|
||||
_shouldFrag = [true, false];
|
||||
_shouldFrag = true;
|
||||
|
||||
private _ammoConfig = configFile >> "CfgAmmo" >> _ammo;
|
||||
private _skip = getNumber (_ammoConfig >> QGVAR(skip));
|
||||
if (_skip == 1) then {
|
||||
_shouldFrag set [0, false];
|
||||
_shouldFrag = false;
|
||||
TRACE_1("No frag: skip",_skip);
|
||||
};
|
||||
|
||||
private _force = getNumber (_ammoConfig >> QGVAR(force));
|
||||
if (_shouldFrag#0 && _force == 0) then {
|
||||
if (_shouldFrag && !_force) then {
|
||||
private _explosive = getNumber (_ammoConfig >> "explosive");
|
||||
if (_explosive < 0.4) exitWith {
|
||||
_shouldFrag set [0, false];
|
||||
if (_explosive < 0.5) exitWith {
|
||||
_shouldFrag = false;
|
||||
TRACE_3("No frag: _explosive",_skip,_force,_explosive);
|
||||
};
|
||||
private _indirectHit = getNumber (_ammoConfig >> "indirectHit");
|
||||
private _indirectRange = getNumber (_ammoConfig >> "indirectHitRange");
|
||||
if (_indirectHit < 3 ||
|
||||
{_indirectRange < 5 && _indirectHit < _indirectRange}) then {
|
||||
_shouldFrag set [0, false];
|
||||
if (_indirectHit * sqrt(_indirectRange) < 35 || _indirectRange < 4.5) then {
|
||||
_shouldFrag = false;
|
||||
TRACE_5("No frag",_ammo,_skip,_explosive,_indirectRange,_indirectHit);
|
||||
};
|
||||
};
|
||||
|
||||
private _ammoSubmunitionConfigPath = _ammoConfig >> "submunitionAmmo";
|
||||
if (isText _ammoSubmunitionConfigPath) then {
|
||||
private _submunitionAmmo = getText _ammoSubmunitionConfigPath;
|
||||
TRACE_1("Submunition ammo text: ",_submunitionAmmo);
|
||||
if (_submunitionAmmo isNotEqualTo "") then {
|
||||
private _shouldSubmunitionFrag = _submunitionAmmo call FUNC(shouldFrag);
|
||||
_shouldFrag set [1, _shouldSubmunitionFrag#0 || _shouldSubmunitionFrag#1];
|
||||
};
|
||||
} else {
|
||||
private _submunitionArray = getArray _ammoSubmunitionConfigPath;
|
||||
TRACE_1("Submunition ammo array: ",_submunitionArray);
|
||||
for "_i" from 0 to count _submunitionArray - 1 step 2 do {
|
||||
private _submunitionAmmo = _submunitionArray#_i;
|
||||
if (_submunitionAmmo isNotEqualTo "") then {
|
||||
private _shouldSubmunitionFrag = _submunitionAmmo call FUNC(shouldFrag);
|
||||
_shouldFrag set [1, _shouldSubmunitionFrag#0 || _shouldSubmunitionFrag#1];
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
GVAR(shouldFragCache) set [_ammo, _shouldFrag];
|
||||
|
||||
_shouldFrag
|
||||
|
@ -20,5 +20,5 @@ params ["_ammo"];
|
||||
GVAR(shouldSpallCache) getOrDefaultCall [_ammo, {
|
||||
(_ammo call FUNC(getSpallInfo)) params ["_caliber", "_explosive", "_indirectHit"];
|
||||
|
||||
(_caliber * GVAR(spallIntensity) >= 1.25 * ACE_FRAG_SPALL_POWER_MIN) || {_explosive >= 0.5 && {_indirectHit * GVAR(spallIntensity) >= 2 * ACE_FRAG_SPALL_POWER_MIN}}
|
||||
_caliber >= 2.5 || (_explosive > 0 && _indirectHit >= 1)
|
||||
}, true]
|
||||
|
@ -1,58 +1,24 @@
|
||||
private _category = format ["ACE %1", LLSTRING(Module_DisplayName)];
|
||||
private _category = format ["ACE %1", localize LSTRING(Module_DisplayName)];
|
||||
|
||||
[
|
||||
QGVAR(enabled),
|
||||
"CHECKBOX",
|
||||
QGVAR(enabled), "CHECKBOX",
|
||||
[LSTRING(EnableFrag), LSTRING(EnableFrag_Desc)],
|
||||
[_category, LSTRING(Frag)],
|
||||
_category,
|
||||
true,
|
||||
1
|
||||
] call CBA_fnc_addSetting;
|
||||
|
||||
[
|
||||
QGVAR(spallEnabled),
|
||||
"CHECKBOX",
|
||||
QGVAR(spallEnabled), "CHECKBOX",
|
||||
[LSTRING(EnableSpall), LSTRING(EnableSpall_Desc)],
|
||||
[_category, LSTRING(Spall)],
|
||||
_category,
|
||||
false,
|
||||
1
|
||||
] call CBA_fnc_addSetting;
|
||||
|
||||
[
|
||||
QGVAR(reflectionsEnabled),
|
||||
"CHECKBOX",
|
||||
QGVAR(reflectionsEnabled), "CHECKBOX",
|
||||
[LSTRING(EnableReflections), LSTRING(EnableReflections_Desc)],
|
||||
[_category, LSTRING(Reflections)],
|
||||
_category,
|
||||
false,
|
||||
1
|
||||
] call CBA_fnc_addSetting;
|
||||
|
||||
[
|
||||
QGVAR(fragSimComplexity),
|
||||
"LIST",
|
||||
[LSTRING(FragMode), LSTRING(FragMode_Desc)],
|
||||
[_category, LSTRING(Frag)],
|
||||
[[2, 1, 0], [LSTRING(FragMode_Opt2),LSTRING(FragMode_Opt1),LSTRING(FragMode_Opt0)], 2],
|
||||
1
|
||||
] call CBA_fnc_addSetting;
|
||||
|
||||
[
|
||||
QGVAR(atLeastOne),
|
||||
"CHECKBOX",
|
||||
[LSTRING(MinFrag), LSTRING(MinFrag_Desc)],
|
||||
[_category, LSTRING(Frag)],
|
||||
false,
|
||||
1
|
||||
] call CBA_fnc_addSetting;
|
||||
|
||||
[
|
||||
QGVAR(spallIntensity),
|
||||
"SLIDER",
|
||||
[LSTRING(SpallIntensity), LSTRING(SpallIntensity_Desc)],
|
||||
[_category, LSTRING(Spall)],
|
||||
[0.1, 2, 1, 1],
|
||||
1,
|
||||
{
|
||||
GVAR(shouldSpallCache) = createHashMap;
|
||||
}
|
||||
] call CBA_fnc_addSetting;
|
||||
|
@ -123,33 +123,6 @@
|
||||
<Chinese>啟用ACE模擬爆炸反射</Chinese>
|
||||
<Czech>Povolit ACE simulaci odrazu exploze</Czech>
|
||||
</Key>
|
||||
<Key ID="STR_ACE_Frag_FragMode">
|
||||
<English>Simulation Mode</English>
|
||||
</Key>
|
||||
<Key ID="STR_ACE_Frag_FragMode_Desc">
|
||||
<English>Sets the fragmentation simulation mode, which can be targeted, random or both.</English>
|
||||
</Key>
|
||||
<Key ID="STR_ACE_Frag_FragMode_Opt0">
|
||||
<English>Unit targeted fragmentation</English>
|
||||
</Key>
|
||||
<Key ID="STR_ACE_Frag_FragMode_Opt1">
|
||||
<English>Random fragmentation</English>
|
||||
</Key>
|
||||
<Key ID="STR_ACE_Frag_FragMode_Opt2">
|
||||
<English>Random and targeted fragmentation</English>
|
||||
</Key>
|
||||
<Key ID="STR_ACE_Frag_MinFrag">
|
||||
<English>Disable Fragment Misses</English>
|
||||
</Key>
|
||||
<Key ID="STR_ACE_Frag_MinFrag_Desc">
|
||||
<English>Create a fragment for each unit that a fragment could be shot towards. This setting has no effect when "Random fragmentation" is selected.</English>
|
||||
</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">
|
||||
<English>Frag/Spall Debug Tracing</English>
|
||||
<German>Splitter-/Explosions-Debug-Verfolgung</German>
|
||||
|
Loading…
Reference in New Issue
Block a user