-
Notifications
You must be signed in to change notification settings - Fork 749
Expand file tree
/
Copy pathfnc_processHit.sqf
More file actions
349 lines (300 loc) · 14.8 KB
/
fnc_processHit.sqf
File metadata and controls
349 lines (300 loc) · 14.8 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
#include "..\script_component.hpp"
/*
* Author: tcvm
* Process hit by projectile against vehicle and apply appropiate damage to part.
*
* Arguments:
* 0: The vehicle <OBJECT>
* 1: Projectile that hit <OBJECT>
* 2: Hit index of potentially damaged part <NUMBER>
* 3: New damage done to part <NUMBER>
* 4: Information about hitpoint <ARRAY>
* 5: Person who caused damage <OBJECT>
*
* Return Value:
* None
*
* Example:
* [myVehicle, projectile, 5, 0.663] call ace_vehicle_damage_fnc_processHit;
*
* Public: No
*/
params ["_vehicle", "_projectile", "_hitIndex", "_newDamage", "_hitpointData", "_injurer"];
_hitpointData params ["_hitArea", "_hitpointConfig", "_hitpointName"];
private _return = true;
if (_newDamage < 0) then {
_newDamage = -_newDamage;
};
private _currentPartDamage = _vehicle getHitIndex _hitIndex;
private _nextPartDamage = _currentPartDamage + _newDamage;
// damage is high enough for immediate destruction
if (_newDamage >= 15) exitWith {
TRACE_2("immediate destruction - high damage",_newDamage,_currentPartDamage);
[_vehicle] call FUNC(knockOut);
[_vehicle, 1] call FUNC(handleDetonation);
// Kill everyone inside for very insane damage
{
[QGVAR(medicalDamage), [_x, _injurer, _injurer, true], _x] call CBA_fnc_targetEvent;
} forEach (crew _vehicle);
_vehicle setDamage 1;
_return = false;
_return
};
private _projectileConfig = _projectile call CBA_fnc_getObjectConfig;
private _warheadTypeStr = getText (_projectileConfig >> "warheadName");
private _incendiary = [_projectileConfig >> QGVAR(incendiary), "NUMBER", -1] call CBA_fnc_getConfigEntry;
private _warheadType = ["HE", "AP", "HEAT", "TandemHEAT"] find _warheadTypeStr; // numerical index for warhead type for quicker checks. Numbers defined in script_macros.hpp
if (_warheadType < 0) then {
_warheadType = WARHEAD_TYPE_NONE;
};
if (_incendiary < 0) then {
_incendiary = [0.3, 0.1, 1, 1, 0] select _warheadType;
};
private _minDamage = [_hitpointConfig >> "minimalHit", "NUMBER", 0] call CBA_fnc_getConfigEntry;
if (_minDamage < 0) then {
_minDamage = -_minDamage;
};
private _ammoEffectiveness = 0;
private _projectileExplosive = [_projectileConfig >> "explosive", "NUMBER", 0] call CBA_fnc_getConfigEntry;
private _indirectHit = [_projectileConfig >> "indirectHit", "NUMBER", 0] call CBA_fnc_getConfigEntry;
if (_warheadType isEqualTo WARHEAD_TYPE_AP) then {
// change damage based on projectile speed (doesn't do this in vanilla ARMA believe it or not)
if !(isNull _injurer) then {
private _airFriction = [_projectileConfig >> "airFriction", "NUMBER", 0] call CBA_fnc_getConfigEntry;
private _distance = _injurer distance _vehicle;
_newDamage = (1 - _projectileExplosive) * _newDamage * exp(_airFriction * _distance);
};
};
private _penChance = 1;
if (_newDamage < _minDamage) then {
_penChance = _newDamage / _minDamage;
TRACE_5("minimum damage modifying hit",_newDamage,_penChance,abs _minDamage,_warheadTypeStr,_hitArea);
};
if (_penChance < random 1) exitWith {
TRACE_1("didn't penetrate",_penChance);
_return
};
if (_minDamage == 0) then {
_minDamage = 1;
};
if (_warheadType isEqualTo WARHEAD_TYPE_HE) then {
private _modifiedIndirectHit = _indirectHit / 100;
if (_newDamage > _modifiedIndirectHit) then {
_newDamage = _newDamage / 2;
};
_newDamage = (_newDamage * (_newDamage / _modifiedIndirectHit)) min _newDamage;
};
_ammoEffectiveness = if (_warheadType isEqualTo WARHEAD_TYPE_AP) then {
0.15 max _newDamage
} else {
if (_warheadType isEqualTo WARHEAD_TYPE_HE) then {
(_newDamage / (_minDamage + (_indirectHit / 100)) * 0.2)
} else {
((_newDamage / _minDamage) * 0.4) min 1
};
};
TRACE_4("ammo effectiveness",_ammoEffectiveness,_newDamage,_minDamage,_warheadTypeStr);
_incendiary = _incendiary * _ammoEffectiveness;
private _isCar = (_vehicle isKindOf "Car" && { !(_vehicle isKindOf "Wheeled_APC_F") });
if (_isCar) then {
_ammoEffectiveness = (_ammoEffectiveness + (_ammoEffectiveness * 0.5)) min 1;
};
private _currentVehicleAmmo = _vehicle call EFUNC(cookoff,getVehicleAmmo);
private _chanceOfDetonation = 0;
private _explosiveAmmoCount = 0;
private _nonExplosiveAmmoCount = 0;
if ((_currentVehicleAmmo select 0) isNotEqualTo []) then {
private _magConfig = configFile >> "CfgMagazines";
private _ammoConfig = configFile >> "CfgAmmo";
private _countOfExplodableAmmo = 0;
{
_x params ["_magazineClassname", "_currentAmmoCount"];
private _initialAmmoCount = getNumber (_magConfig >> _magazineClassname >> "count");
_chanceOfDetonation = _chanceOfDetonation + (_currentAmmoCount / _initialAmmoCount);
_countOfExplodableAmmo = _countOfExplodableAmmo + 1;
private _ammoClassname = getText (_magConfig >> _magazineClassname >> "ammo");
private _explosive = getNumber (_ammoConfig >> _ammoClassname >> "explosive");
private _hit = getNumber (_ammoConfig >> _ammoClassname >> "hit");
if (_explosive > 0.5 || _hit > 50) then {
_explosiveAmmoCount = _explosiveAmmoCount + 1;
} else {
_nonExplosiveAmmoCount = _nonExplosiveAmmoCount + 1;
};
} forEach (_currentVehicleAmmo select 0);
if (_countOfExplodableAmmo != 0) then {
_chanceOfDetonation = _chanceOfDetonation / _countOfExplodableAmmo;
};
};
private _chanceToDetonate = 0;
private _chanceOfFire = 0;
private _currentFuel = fuel _vehicle;
private _vehicleConfig = _vehicle call CBA_fnc_getObjectConfig;
switch (_hitArea) do {
case "engine": {
_chanceToDetonate = ([_vehicleConfig >> QGVAR(engineDetonationProb), "NUMBER", 0] call CBA_fnc_getConfigEntry) * _incendiary * _currentFuel * _penChance;
_chanceOfFire = ([_vehicleConfig >> QGVAR(engineFireProb), "NUMBER", 0] call CBA_fnc_getConfigEntry) * _incendiary * _currentFuel * _penChance;
private _cookoffIntensity = 4 * _currentFuel;
TRACE_6("hit engine",_chanceToDetonate,_chanceOfFire,_incendiary,_chanceOfDetonation,_currentFuel,_cookoffIntensity);
if (_isCar) then {
_chanceOfFire = 0; // no cookoff for cars
};
if ([_vehicle, _chanceToDetonate, _explosiveAmmoCount, _nonExplosiveAmmoCount, _injurer] call FUNC(handleDetonation)) exitWith {
[_vehicle] call FUNC(knockOut);
};
// cap damage at 0.9 to avoid hard coded blow up
_nextPartDamage = (0.9 min _nextPartDamage);
// fatal engine/drive system damage
if (_nextPartDamage == 0.9 || { 0.8 * _ammoEffectiveness > random 1 }) then {
[_vehicle, _hitIndex, _hitpointName, 0.9 * _penChance] call FUNC(addDamage);
_vehicle setVariable [QGVAR(canMove), false];
} else {
[_vehicle, _hitIndex, _hitpointName, _nextPartDamage * _penChance] call FUNC(addDamage);
};
[_vehicle, _chanceOfFire, _cookoffIntensity, _injurer, _hitArea, false] call FUNC(handleCookoff);
};
case "hull": {
_chanceToDetonate = ([_vehicleConfig >> QGVAR(hullDetonationProb), "NUMBER", 0] call CBA_fnc_getConfigEntry) * _incendiary * ((_chanceOfDetonation + _currentFuel) / 2) * _penChance;
_chanceOfFire = ([_vehicleConfig >> QGVAR(hullFireProb), "NUMBER", 0] call CBA_fnc_getConfigEntry) * _incendiary * ((_chanceOfDetonation + _currentFuel) / 2) * _penChance;
private _cookoffIntensity = 1.5 + (_explosiveAmmoCount * _chanceOfFire);
TRACE_6("hit hull",_chanceToDetonate,_chanceOfFire,_incendiary,_chanceOfDetonation,_currentFuel,_cookoffIntensity);
if (_isCar) then {
_chanceOfFire = 0; // no cookoff for cars
};
if ([_vehicle, _chanceToDetonate, _explosiveAmmoCount, _nonExplosiveAmmoCount, _injurer] call FUNC(handleDetonation)) exitWith {
[_vehicle, _hitIndex, _hitpointName, 0.89 * _penChance] call FUNC(addDamage);
[_vehicle] call FUNC(knockOut);
};
private _hash = _vehicle getVariable [QGVAR(hitpointHash), []];
private _hashKeys = [_hash] call CBA_fnc_hashKeys;
// 25% chance of jamming turret - 25% of mobility kill - 25% of both - 75% chance of critical hull damage
private _rand = random 1;
TRACE_2("rolling hull damage",_ammoEffectiveness,_rand);
private _partKill = [];
if (_ammoEffectiveness > _rand) then {
_rand = random 1;
TRACE_2("damaged hull part",_ammoEffectiveness,_rand);
switch (true) do {
case (_rand < 0.25): {
[_vehicle, _hitIndex, _hitpointName, 0.89 * _penChance] call FUNC(addDamage);
// iterate through all keys and find appropriate turret
[_hash, {
if (_value#0 isEqualTo "turret") then {
_partKill pushBack _key;
};
}] call CBA_fnc_hashEachPair;
_vehicle setVariable [QGVAR(canShoot), false];
};
case (_rand < 0.5): {
[_vehicle, _hitIndex, _hitpointName, 0.89 * _penChance] call FUNC(addDamage);
_partKill = _partKill + ENGINE_HITPOINTS#0;
if !(_vehicle isKindOf "Wheeled_APC_F") then {
_partKill = _partKill + TRACK_HITPOINTS#0;
};
_vehicle setVariable [QGVAR(canMove), false];
};
case (_rand < 0.75): {
[_vehicle, _hitIndex, _hitpointName, 0.89 * _penChance] call FUNC(addDamage);
_partKill = _partKill + ENGINE_HITPOINTS#0;
if !(_vehicle isKindOf "Wheeled_APC_F") then {
_partKill = _partKill + TRACK_HITPOINTS#0;
};
// iterate through all keys and find appropriate turret
[_hash, {
if (_value#0 isEqualTo "turret") then {
_partKill pushBack _key;
};
}] call CBA_fnc_hashEachPair;
_vehicle setVariable [QGVAR(canMove), false];
_vehicle setVariable [QGVAR(canShoot), false];
};
default{};
};
};
{
TRACE_1("doing damage to hitpoint",_x);
[_vehicle, -1, _x, 1 * _penChance] call FUNC(addDamage);
} forEach _partKill;
[_vehicle, _chanceOfFire, _cookoffIntensity, _injurer, "", true] call FUNC(handleCookoff);
};
case "turret": {
_chanceToDetonate = ([_vehicleConfig >> QGVAR(turretDetonationProb), "NUMBER", 0] call CBA_fnc_getConfigEntry) * _incendiary * _chanceOfDetonation * _penChance;
_chanceOfFire = ([_vehicleConfig >> QGVAR(turretFireProb), "NUMBER", 0] call CBA_fnc_getConfigEntry) * _incendiary * _chanceOfDetonation * _penChance;
private _cookoffIntensity = _explosiveAmmoCount * _chanceOfFire;
TRACE_6("hit turret",_chanceToDetonate,_chanceOfFire,_incendiary,_chanceOfDetonation,_currentFuel,_cookoffIntensity);
if (_isCar) then {
_chanceOfFire = 0; // no cookoff for cars
};
if ([_vehicle, _chanceToDetonate, _explosiveAmmoCount, _nonExplosiveAmmoCount, _injurer] call FUNC(handleDetonation)) exitWith {
[_vehicle] call FUNC(knockOut);
};
if (0.8 * _ammoEffectiveness > random 1) then {
TRACE_1("damaged turret",_ammoEffectiveness * 0.8);
[_vehicle, _hitIndex, _hitpointName, 1 * _penChance] call FUNC(addDamage);
_vehicle setVariable [QGVAR(canShoot), false];
};
[_vehicle, _chanceOfFire, _cookoffIntensity, _injurer, "", true] call FUNC(handleCookoff);
};
case "gun": {
TRACE_5("hit gun",_chanceToDetonate,_chanceOfFire,_incendiary,_chanceOfDetonation,_currentFuel);
if (0.8 * _ammoEffectiveness > random 1) then {
TRACE_1("damaged gun",_ammoEffectiveness * 0.8);
[_vehicle, _hitIndex, _hitpointName, 1 * _penChance] call FUNC(addDamage);
_vehicle setVariable [QGVAR(canShoot), false];
};
};
case "track": {
private _damage = (0.1 max (0.1 * _newDamage / _minDamage)) min 1;
[_vehicle, _hitIndex, _hitpointName, (_currentPartDamage + _damage) * _penChance] call FUNC(addDamage);
TRACE_3("damaged track",_damage,_newDamage,_minDamage);
if ((_vehicle getHitIndex _hitIndex) >= 1) then {
_vehicle setVariable [QGVAR(canMove), false];
};
};
case "wheel": {
[_vehicle, _hitIndex, _hitpointName, (_currentPartDamage + _newDamage) * _penChance] call FUNC(addDamage);
TRACE_1("damaged wheel",_newDamage);
};
case "fuel": {
_chanceOfFire = (_incendiary * _currentFuel * _penChance) / 2;
private _cookoffIntensity = _currentFuel * 5;
TRACE_2("damaged fuel",_chanceOfFire,_cookoffIntensity);
if (_isCar) then {
_chanceOfFire = 0; // no cookoff for cars
};
[_vehicle, _chanceOfFire, _cookoffIntensity, _injurer, "", false] call FUNC(handleCookoff);
private _damage = (0.1 max (0.1 * _newDamage / _minDamage)) min 1;
[_vehicle, _hitIndex, _hitpointName, (_currentPartDamage + _damage) * _penChance] call FUNC(addDamage);
};
case "slat": {
TRACE_2("hit slat",_warheadType,_warheadTypeStr);
// incredibly small chance of AP destroying SLAT
if (_warheadType in [WARHEAD_TYPE_HE, WARHEAD_TYPE_AP, WARHEAD_TYPE_HEAT, WARHEAD_TYPE_TANDEM] || { 0.01 > random 1 }) then {
private _currentDamage = _vehicle getHitIndex _hitIndex;
TRACE_3("damaged slat",_warheadType,_warheadTypeStr,_currentDamage);
if (_warheadType in [WARHEAD_TYPE_HEAT, WARHEAD_TYPE_TANDEM, WARHEAD_TYPE_AP]) then {
[_vehicle, _hitIndex, _hitpointName, 1] call FUNC(addDamage);
} else {
[_vehicle, _hitIndex, _hitpointName, _currentDamage + (0.5 max random 1)] call FUNC(addDamage);
};
if (_currentDamage < 1 && _warheadType isEqualTo WARHEAD_TYPE_HEAT) then {
_return = false;
};
};
};
case "era": {
TRACE_2("hit era",_warheadType,_warheadTypeStr);
if (_warheadType in [WARHEAD_TYPE_AP, WARHEAD_TYPE_HEAT, WARHEAD_TYPE_TANDEM] || { 0.05 > random 1 }) then {
private _currentDamage = _vehicle getHitIndex _hitIndex;
TRACE_3("damaged era",_warheadType,_warheadTypeStr,_currentDamage);
[_vehicle, _hitIndex, _hitpointName, 1] call FUNC(addDamage);
// dont process anymore damage if this is HEAT - shouldnt happen anyway but ARMA says it does so you know
if (_currentDamage < 1 && _warheadType isEqualTo WARHEAD_TYPE_HEAT) then {
_return = false;
};
};
};
default {
TRACE_1("hit unknown hitpoint??",_hitArea);
}
};
_return