Vehicle damage - Code cleanup (#9831)

* Cook-off improvements

* More changes

* Update fnc_getVehicleAmmo.sqf

* Better engine fire placement

* Update fnc_detonateAmmunition.sqf

* Update XEH_postInit.sqf

* Update fnc_getVehicleAmmo.sqf

* Update events-framework.md

* Various improvements

* Separate effect handling

* Tweaks

* Update XEH_postInit.sqf

* Prevent double ammo detonation

* Fixed objects not being able to cook-off again

* Added incendiary rounds as source of box cookoff

* Converted enable setting to bool

* Fixed brackets

* Update fnc_cookOff.sqf

* Update CfgEden.hpp

* Removed GVAR(enable), added GVAR(enableFire) back

* Vehicle damage fixes

* Made hitpoint hash common

* Update fnc_addEventHandler.sqf

* Update fnc_medicalDamage.sqf

* Update fnc_handleBail.sqf

* Changed API

* Remove `CBA_fnc_getConfigEntry` as much as possible, as it's 2x slower

* More cleanup

* More cleanup

* Fix merging issues, remove turret tossing

* Update translations

* More cleanup

* Reverted some logic back to original, minor tweaks & fixes

* Fix undefined variable

* Cleanup

* Fixed bad logic

* Update addons/vehicle_damage/script_macros.hpp

Co-authored-by: Grim <69561145+LinkIsGrim@users.noreply.github.com>

* Update addons/vehicle_damage/functions/fnc_handleDamage.sqf

* Update addons/vehicle_damage/stringtable.xml

Co-authored-by: Grim <69561145+LinkIsGrim@users.noreply.github.com>

* Update addons/vehicle_damage/stringtable.xml

Co-authored-by: Grim <69561145+LinkIsGrim@users.noreply.github.com>

* Update addons/vehicle_damage/XEH_postInit.sqf

Co-authored-by: PabstMirror <pabstmirror@gmail.com>

---------

Co-authored-by: Grim <69561145+LinkIsGrim@users.noreply.github.com>
Co-authored-by: PabstMirror <pabstmirror@gmail.com>
This commit is contained in:
johnb432 2024-08-20 21:23:21 +02:00 committed by GitHub
parent 2682778499
commit 80b2fa9a05
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
26 changed files with 694 additions and 756 deletions

View File

@ -21,9 +21,6 @@ class CfgAmmo {
class M_Vorona_HEAT;
class M_SPG9_HEAT;
class R_MRAAWS_HEAT_F;
class B_338_Ball;
class ACE_G_40mm_HE;
CREATE_INCENDIARY_AMMO(BulletBase, BulletCore, 0.1);
CREATE_INCENDIARY_AMMO(ShellBase, ShellCore, 1.0);

View File

@ -1,19 +1,17 @@
class Extended_PreStart_EventHandlers {
class ADDON {
init = QUOTE(call COMPILE_SCRIPT(XEH_preStart));
};
};
class Extended_PostInit_EventHandlers {
class ADDON {
init = QUOTE(call COMPILE_SCRIPT(XEH_postInit));
};
};
class Extended_PreInit_EventHandlers {
class ADDON {
init = QUOTE(call COMPILE_SCRIPT(XEH_preInit));
};
};
class Extended_PostInit_EventHandlers {
class ADDON {
init = QUOTE(call COMPILE_SCRIPT(XEH_postInit));
};
};

View File

@ -28,6 +28,7 @@ class CfgVehicles {
GVAR(engineFireProb) = 0.5;
GVAR(detonationDuringFireProb) = 0.2;
GVAR(canHaveFireRing) = 0;
EGVAR(cookoff,canHaveFireJet) = 1;
};
class Wheeled_APC_F: Car_F {
GVAR(hullDetonationProb) = 0.2;
@ -38,6 +39,7 @@ class CfgVehicles {
GVAR(engineFireProb) = 0.5;
GVAR(detonationDuringFireProb) = 0.2;
GVAR(canHaveFireRing) = 0;
EGVAR(cookoff,canHaveFireJet) = 1;
};
class APC_Tracked_01_base_F: Tank_F {};
class B_APC_Tracked_01_base_F: APC_Tracked_01_base_F {};
@ -288,4 +290,3 @@ class CfgVehicles {
GVAR(canHaveFireRing) = 1;
};
};

View File

@ -1,14 +1,13 @@
PREP(abandon);
PREP(addEventHandler);
PREP(handleBail);
PREP(handleVehicleDamage);
PREP(handleCookoff);
PREP(detonate);
PREP(processHit);
PREP(handleDetonation);
PREP(handleDamage);
PREP(knockOut);
PREP(addDamage);
PREP(handleDamageEjectIfDestroyed);
PREP(blowOffTurret);
PREP(handleBail);
PREP(handleCookoff);
PREP(handleDamage);
PREP(handleDamageEjectIfDestroyed);
PREP(handleDetonation);
PREP(handleVehicleDamage);
PREP(knockOut);
PREP(medicalDamage);
PREP(processHit);
PREP(setDamage);

View File

@ -1,53 +1,65 @@
#include "script_component.hpp"
["ace_settingsInitialized", {
// Init eject from destroyed vehicles
// See https://github.com/acemod/ACE3/pull/6330
// Still valid for Arma 2.16
{
[_x, "Init", {
params ["_vehicle"];
if (!alive _vehicle) exitWith {};
TRACE_2("ejectIfDestroyed init",_vehicle,typeOf _vehicle);
_vehicle addEventHandler ["HandleDamage", {call FUNC(handleDamageEjectIfDestroyed)}];
}, true, [], true] call CBA_fnc_addClassEventHandler;
} forEach EJECT_IF_DESTROYED_VEHICLES;
["CBA_settingsInitialized", {
TRACE_1("settings init",GVAR(enabled));
if (GVAR(enabled)) then {
[QGVAR(medicalDamage), LINKFUNC(medicalDamage)] call CBA_fnc_addEventHandler;
[QGVAR(bailOut), {
params ["_center", "_crewman", "_vehicle"];
TRACE_3("bailOut",_center,_crewman,_vehicle);
if (!GVAR(enabled)) exitWith {};
if (isPlayer _crewman) exitWith {};
if (!alive _crewman || {!([_crewman] call EFUNC(common,isAwake))}) exitWith {};
[QGVAR(medicalDamage), LINKFUNC(medicalDamage)] call CBA_fnc_addEventHandler;
unassignVehicle _crewman;
_crewman leaveVehicle _vehicle;
doGetOut _crewman;
private _angle = floor (random 360);
private _dist = (30 + (random 10));
private _escape = _center getPos [_dist, _angle];
_crewman doMove _escape;
_crewman setSpeedMode "FULL";
}] call CBA_fnc_addEventHandler;
["Tank", "init", LINKFUNC(addEventHandler), true, [], true] call CBA_fnc_addClassEventHandler;
["Wheeled_APC_F", "init", LINKFUNC(addEventHandler), true, [], true] call CBA_fnc_addClassEventHandler;
if (GVAR(enableCarDamage)) then {
["Car", "init", LINKFUNC(addEventHandler), true, [], true] call CBA_fnc_addClassEventHandler;
};
// Blow off turret effect
// TODO: Add blowing-off-turret effect to vehicles that cook-off but aren't destroyed (no catastrophic explosion)
// The problem is that vehicles are repairable if they haven't been destroyed. So if the turret is gone and vehicle is repaired, how do we handle that?
["Tank", "Killed", {
if (_this select 3 && random 1 < 0.15) then {
(_this select 0) call FUNC(blowOffTurret);
};
}, true, [], true] call CBA_fnc_addClassEventHandler;
if (isServer) then {
// To set source and instigator, setDamage must be executed on the server
[QGVAR(setDamage), {(_this select 0) setDamage (_this select 1)}] call CBA_fnc_addEventHandler;
};
// init eject from destroyed vehicle
{
[_x, "init", {
params ["_vehicle"];
if (!alive _vehicle) exitWith {};
TRACE_2("ejectIfDestroyed init",_vehicle,typeOf _vehicle);
_vehicle addEventHandler ["HandleDamage", {call FUNC(handleDamageEjectIfDestroyed)}];
}, true, [], true] call CBA_fnc_addClassEventHandler;
} forEach EJECT_IF_DESTROYED_VEHICLES;
[QGVAR(bailOut), {
params ["_vehicle", "_unit"];
TRACE_2("bailOut",_vehicle,_unit);
// Ignore players and the dead
if (_unit call EFUNC(common,isPlayer) || {!(_unit call EFUNC(common,isAwake))}) exitWith {};
unassignVehicle _unit;
_unit leaveVehicle _vehicle;
doGetOut _unit;
private _angle = floor (random 360);
private _dist = 30 + (random 10);
private _escape = _vehicle getPos [_dist, _angle];
_unit doMove _escape;
_unit setSpeedMode "FULL";
}] call CBA_fnc_addEventHandler;
GVAR(vehicleClassesHitPointHash) = createHashMap;
["Tank", "Init", LINKFUNC(addEventHandler), true, [], true] call CBA_fnc_addClassEventHandler;
// Wheeled_APC_F inherits from Car
[["Wheeled_Apc_F", "Car"] select GVAR(enableCarDamage), "Init", LINKFUNC(addEventHandler), true, [], true] call CBA_fnc_addClassEventHandler;
// Blow off turret effect
// TODO: Add blowing-off-turret effect to vehicles that cook-off but aren't destroyed (no catastrophic explosion)
// The problem is that vehicles are repairable if they haven't been destroyed. So if the turret is gone and vehicle is repaired, how do we handle that?
["Tank", "Killed", {
if (_this select 3 && random 1 < 0.15) then {
(_this select 0) call FUNC(blowOffTurret);
};
}] call CBA_fnc_addClassEventHandler;
}] call CBA_fnc_addEventHandler;

View File

@ -7,7 +7,7 @@ class CfgPatches {
weapons[] = {};
requiredVersion = REQUIRED_VERSION;
// ammo/vehicle config defines touch all of these
requiredAddons[] = { "ace_common", "ace_cookoff" };
requiredAddons[] = {"ace_common", "ace_cookoff"};
author = ECSTRING(common,ACETeam);
authors[] = {"tcvm"};
url = ECSTRING(main,URL);

View File

@ -4,29 +4,34 @@
* Forces the AI currently in a vehicle to bail out.
*
* Arguments:
* 0: The vehicle in which to bail out <OBJECT>
* 0: Vehicle <OBJECT>
*
* Return Value:
* None
*
* Example:
* [tank2] call ace_vehicle_damage_fnc_abandon;
* cursorObject call ace_vehicle_damage_fnc_abandon
*
* Public: No
*/
params ["_vehicle"];
TRACE_2("abandon",_vehicle,(crew _vehicle) select {alive _x});
if (_vehicle getVariable [QGVAR(allowCrewInImmobile), false]) exitWith {}; // check for API
// Check for API
if (_vehicle getVariable [QGVAR(allowCrewInImmobile), false]) exitWith {
TRACE_1("API prevented crew from dismounting",_vehicle);
};
TRACE_1("abandon",_vehicle);
[{
params ["_vehicle"];
_vehicle allowCrewInImmobile false;
private _center = getPosASL _vehicle;
TRACE_2("bailing out crew after delay",_vehicle,_center);
TRACE_2("bailing out crew after delay",_vehicle,crew _vehicle);
{
[QGVAR(bailOut), [_center, _x, _vehicle], _x] call CBA_fnc_targetEvent;
} forEach crew _vehicle;
}, _this, random MAX_CREW_BAILOUT_TIME] call CBA_fnc_waitAndExecute;
[QGVAR(bailOut), [_vehicle, _x], _x] call CBA_fnc_targetEvent;
} forEach (crew _vehicle);
}, _vehicle, random MAX_CREW_BAILOUT_TIME] call CBA_fnc_waitAndExecute;

View File

@ -1,42 +0,0 @@
#include "..\script_component.hpp"
/*
* Author: tcvm
* Sets vehicle damage based on HitIndex. Failing that it falls back to HitPoint name.
*
* Arguments:
* 0: The vehicle <OBJECT>
* 1: Hit Index <NUMBER>
* 2: Hit Point <STRING>
* 3: Damage <NUMBER>
* 4: Whether or not to cap the damage to maximum part damage <BOOL> (default: True)
*
* Return Value:
* None
*
* Example:
* [vehicle player, 234, "HitHull"] call ace_vehicle_damage_fnc_addDamage
*
* Public: No
*/
params ["_vehicle", "_hitIndex", "_hitPoint", "_damage", ["_capDamageAtCurret", true]];
private _currentDamage = _vehicle getHitPointDamage _hitPoint;
if (_capDamageAtCurret && { _damage < _currentDamage }) exitWith {
TRACE_4("capping damage at current",_capDamageAtCurret,_damage,_currentDamage,_hitPoint);
};
TRACE_4("adding damage to vehicle",_vehicle,_hitIndex,_hitPoint,_damage);
if (_hitPoint isEqualTo "#structural") then {
_hitPoint = "hithull";
_hitIndex = -1;
};
if (_hitIndex >= 0) then {
_vehicle setHitIndex [_hitIndex, _damage, true];
} else {
_vehicle setHitPointDamage [_hitPoint, _damage, true];
};
if (_hitPoint == "hitengine" && {_damage > 0.9}) then {
[QEGVAR(cookoff,engineFireServer), _vehicle] call CBA_fnc_serverEvent;
};

View File

@ -1,110 +1,137 @@
#include "..\script_component.hpp"
/*
* Author: tcvm
* Author: tcvm, johnb43
* Adds the event handler to a vehicle.
*
* Arguments:
* 0: The vehicle in which to add the event handler to <OBJECT>
* 0: Vehicle <OBJECT>
*
* Return Value:
* None
*
* Example:
* [tank2] call ace_vehicle_damage_fnc_addEventHandler;
* cursorObject call ace_vehicle_damage_fnc_addEventHandler
*
* Public: No
*/
params["_vehicle"];
params ["_vehicle"];
TRACE_2("addEventHandler",_vehicle,GVAR(enabled));
if !(GVAR(enabled)) exitWith {
if (!GVAR(enabled)) exitWith {
#ifdef DEBUG_MODE_FULL
[{ ["Warning: Vehicle Damage not enabled...", 2] call CBA_fnc_notify; }, [], 5] call CBA_fnc_waitAndExecute;
[CBA_fnc_notify, ["Warning: Vehicle Damage not enabled", 2], 5] call CBA_fnc_waitAndExecute;
#endif
};
private _hitpointHash = [[], nil] call CBA_fnc_hashCreate;
if (!isNil {_vehicle getVariable QGVAR(handleDamage)}) exitWith {};
_vehicle allowCrewInImmobile true;
// No clue why, but for some reason this needs to be exec'd next frame or else it isn't the last event handler in the system.
// Maybe its overridden somewhere else, but this makes sure it is the last one
[{
params ["_vehicle"];
if (!isNil {_vehicle getVariable QGVAR(handleDamage)}) exitWith {};
TRACE_1("added eh",_vehicle);
_vehicle setVariable [QGVAR(hitHash), createHashMap];
_vehicle setVariable [QGVAR(handleDamage), _vehicle addEventHandler ["HandleDamage", {_this call FUNC(handleDamage)}]];
}, _vehicle] call CBA_fnc_execNextFrame;
private _typeOf = typeOf _vehicle;
if (_typeOf in GVAR(vehicleClassesHitPointHash)) exitWith {};
private _hitPointHash = createHashMap;
private _vehicleConfig = configOf _vehicle;
private _hitpointsConfig = _vehicleConfig >> "HitPoints";
private _turretConfig = _vehicleConfig >> "Turrets";
private _eraHitpoints = ([_vehicleConfig >> QGVAR(eraHitpoints), "ARRAY", []] call CBA_fnc_getConfigEntry) apply {toLowerANSI _x};
private _slatHitpoints = ([_vehicleConfig >> QGVAR(slatHitpoints), "ARRAY", []] call CBA_fnc_getConfigEntry) apply {toLowerANSI _x};
private _hitPointsConfig = _vehicleConfig >> "HitPoints";
// Add hitpoint names to config for quick lookup
{
_x params ["_hitpoints", "_type"];
_x params ["_hitPoints", "_hitArea"];
{
[_hitpointHash, toLowerANSI _x, [_type, _hitpointsConfig >> _x, toLowerANSI _x]] call CBA_fnc_hashSet;
} forEach _hitpoints;
_hitPointHash set [toLowerANSI _x, [_hitArea, abs getNumber (_hitPointsConfig >> _x >> "minimalHit")]];
} forEach _hitPoints;
} forEach ALL_HITPOINTS;
_vehicle setVariable [QGVAR(hitpointHash), _hitpointHash];
// gun and turret hitpoints arent hardcoded anymore - dig through config to find correct names
private _iterateThroughConfig = {
params ["_vehicle", "_config", "_iterateThroughConfig", "_hitpointAliases"];
// Gun and turret hitpoints aren't hardcoded anymore - dig through config to find correct names
private _fnc_iterateThroughConfig = {
params ["_config"];
TRACE_1("checking config",_config);
private _configName = toLowerANSI configName _config;
private _isGun = ([_config >> "isGun", "NUMBER", 0] call CBA_fnc_getConfigEntry) == 1;
private _isTurret = ([_config >> "isTurret", "NUMBER", 0] call CBA_fnc_getConfigEntry) == 1;
private _isGun = getNumber (_config >> "isGun") == 1;
private _isTurret = getNumber (_config >> "isTurret") == 1;
private _isEra = _configName in _eraHitpoints;
private _isSlat = _configName in _slatHitpoints;
private _isMisc = false;
// prevent incompatibilites with old mods
if (_configName isEqualTo "hitturret") then {
// Prevent incompatibilites with old mods
if (_configName == "hitturret") then {
_isTurret = true;
};
if (_configName isEqualTo "hitgun") then {
if (_configName == "hitgun") then {
_isGun = true;
};
private _hash = _vehicle getVariable QGVAR(hitpointHash);
{
_x params ["_hitType", "_hitPoints"];
_x params ["_hitArea", "_hitPoints"];
if (_configName in _hitPoints) then {
[_hash, _configName, [_hitType, _config, _configName]] call CBA_fnc_hashSet;
_hitPointHash set [_configName, [_hitArea, abs getNumber (_config >> "minimalHit")]];
_isMisc = true;
};
} forEach _hitpointAliases;
} forEach _hitPointAliases;
if (_isGun || _isTurret || _isEra || _isSlat || _isMisc) then {
TRACE_6("found gun/turret/era/slat/misc",_isGun,_isTurret,_isEra,_isSlat,_isMisc,_hash);
if (_isGun) then {
[_hash, _configName, ["gun", _config, _configName]] call CBA_fnc_hashSet;
_hitPointHash set [_configName, ["gun", abs getNumber (_config >> "minimalHit")]];
};
if (_isTurret) then {
[_hash, _configName, ["turret", _config, _configName]] call CBA_fnc_hashSet;
_hitPointHash set [_configName, ["turret", abs getNumber (_config >> "minimalHit")]];
};
if (_isEra) then {
[_hash, _configName, ["era", _config, _configName]] call CBA_fnc_hashSet;
_hitPointHash set [_configName, ["era", abs getNumber (_config >> "minimalHit")]];
};
if (_isSlat) then {
[_hash, _configName, ["slat", _config, _configName]] call CBA_fnc_hashSet;
_hitPointHash set [_configName, ["slat", abs getNumber (_config >> "minimalHit")]];
};
_vehicle setVariable [QGVAR(hitpointHash), _hash];
TRACE_6("found gun/turret/era/slat/misc",_isGun,_isTurret,_isEra,_isSlat,_isMisc,_hash);
} else {
{
[_vehicle, _x, _iterateThroughConfig, _hitpointAliases] call _iterateThroughConfig;
_x call _fnc_iterateThroughConfig;
} forEach configProperties [_config, "isClass _x", true];
};
};
private _hitpointAliases = [_vehicleConfig >> QGVAR(hitpointAlias), "ARRAY", []] call CBA_fnc_getConfigEntry;
TRACE_1("hitpoint alias",_hitpointAliases);
[_vehicle, _hitpointsConfig, _iterateThroughConfig, _hitpointAliases] call _iterateThroughConfig;
[_vehicle, _turretConfig, _iterateThroughConfig, _hitpointAliases] call _iterateThroughConfig;
private _turretConfig = _vehicleConfig >> "Turrets";
private _eraHitpoints = (getArray (_vehicleConfig >> QGVAR(eraHitpoints))) apply {toLowerANSI _x};
private _slatHitpoints = (getArray (_vehicleConfig >> QGVAR(slatHitpoints))) apply {toLowerANSI _x};
_vehicle allowCrewInImmobile true;
private _eh = _vehicle getVariable [QGVAR(handleDamage), nil];
if (isNil "_eh") then {
// no clue why, but for some reason this needs to exec'd next frame or else it isnt the last event handler in the system.
// Maybe its overridden somewhere else, but this makes sure it is the last one
[{
params ["_vehicle"];
TRACE_1("EH not added yet - added eh now",_vehicle);
private _hd = _vehicle addEventHandler ["HandleDamage", { _this call FUNC(handleDamage) }];
_vehicle setVariable [QGVAR(handleDamage), _hd];
}, [_vehicle]] call CBA_fnc_execNextFrame;
private _fnc_toLowerCase = {
_this apply {
if (_x isEqualType []) then {
_x call _fnc_toLowerCase
} else {
toLowerANSI _x
};
};
};
// Convert areas to lower case
private _hitPointAliases = (getArray (_vehicleConfig >> QGVAR(hitpointAlias))) call _fnc_toLowerCase;
TRACE_1("hitpoint alias",_hitPointAliases);
_hitPointsConfig call _fnc_iterateThroughConfig;
_turretConfig call _fnc_iterateThroughConfig;
GVAR(vehicleClassesHitPointHash) set [_typeOf, _hitPointHash];
nil

View File

@ -10,7 +10,7 @@
* None
*
* Example:
* (vehicle player) call ace_vehicle_damage_fnc_blowOffTurret
* cursorObject call ace_vehicle_damage_fnc_blowOffTurret
*
* Public: No
*/
@ -19,6 +19,7 @@
// The sudden change in the model would cause nearby PhysX objects to get stuck
[{
params ["_vehicle"];
TRACE_1("blowOffTurret",_vehicle);
(getArray (configOf _vehicle >> QGVAR(turret))) params [["_model", "", [""]], ["_offset", [0, 0, 0], [[]], 3]];

View File

@ -1,118 +0,0 @@
#include "..\script_component.hpp"
/*
* Author: tcvm
* Calculates whether or not hit penetrated given armour or not. Only enabled with advanced penetration simulation turned on.
*
* Arguments:
* 0: Source of damage <OBJECT>
* 1: The vehicle <OBJECT>
* 2: Projectile that hit <OBJECT>
* 3: Hitpoint damaged <STRING>
*
* Return Value:
* None
*
* Example:
* [myVehicle, projectile, 5, 0.663] call ace_vehicle_damage_fnc_calculatePenetrationInfo;
*
* Public: No
*/
params ["_source", "_vehicle", "_projectileData", "_hitpointConfig"];
_projectileData params ["_projectileType", "_projectileConfig"];
/*
http://www.longrods.ch/peneq.php
https://www.scribd.com/doc/267210898/57-mm-APFSDS-2-000-m#download
Perforation Calculation of APFSDS:
Tungsten/Depleted Uranium: Rods
P/Lw = a * (1 / tanh(b0 + b1 * (Lw/D))) * cos^m (theta) * sqrt (Pp / Pt) * e^((-(c0 + c1 * BHNT) * BHNT) / (Pp * Vt^2))
Steel Rods
P/Lw = a * (1 / tanh(b0 + b1 * (Lw/D))) * cos^m (theta) * sqrt (Pp / Pt) * e^((-c * BHNT^k * BHNP^n) / (Pp * Vt^2))
Penetration Calculation of Tungsten APFSDS (Used for all penetrators):
P/Lw = a * (1 / tanh(b0 + b1 * (Lw/D))) sqrt (Pp / Pt) * e^((-(c0 + c1 * BHNT) * BHNT) / (Pp * Vt^2))
where:
Penetrator:
D = Diameter of penetrator rod (always 22mm)
L = Total length of penetrator in millimeters (always 950mm)
Lw = Working length of rod in millimeters
Vt = impact velocity in Kilometers/Second
theta = NATO Obliquity angle of Penetration
Pp = Penetrator Density in kg/m^3
BHNP = Brinell hardness number of penetrator
Target:
Pt = target density in kg/m^3 (always 7840kg/m^3)
d = plate thickness in millimeters
BHNT = Brinell hardness number of target (always 350)
Material Data:
Tungsten:
Pp = 19300
BHNP = N/A
a = 0.994
c0 = 134.5
c1 = -0.148
Depleted Uranium:
Pp = 18600
BHNP = N/A
a = 0.825
c0 = 90.0
c1 = -0.0849
Steel:
Pp = 7850
BHNP = 500
a = 1.104
c = 9874
k = 0.3598
n = -0.2342
Cofficients:
m = -0.224
b0 = 0.283
b1 = 0.0656
*/
private _enabled = ([_hitpointConfig >> QGVAR(enabled), "NUMBER", 0] call CBA_fnc_getConfigEntry) == 1;
#define MATERIAL_ARRAY ([[0, 0, 0, 0, 0, 0], "steel", [7850, 500, 1.104, 9874, 0.3598, -0.2342], "tungsten", [19300, 0, 0.994, 134.5, -0.148], "depleted_uranium", [18600, 0, 0.825, 90, -0.0849]])
private _rodMaterialStr = [_projectileConfig >> QGVAR(material), "STRING", "tungsten"] call CBA_fnc_getConfigEntry;
private _rodMaterialParams = MATERIAL_ARRAY select (1 + MATERIAL_ARRAY find toLowerANSI _rodMaterial);
if !(_enabled) exitWith { [false, 0, 0, 0, 0] };
if (_rodMaterialParams isEqualTo [0, 0, 0, 0, 0, 0]) exitWith { [] };
private _tanX = 2 * (0.283 * 0.0656 * (1));
private _tanh = 1 / (((exp _tanX) - 1) / ((exp _tanX) + 1));
private _cosm = (cos 0) ^ -0.224;
private _lw = 950; // technically this would be something else depending on armour slant but this is a good enough aproximation
private _aproximateVelocity = 0;
private _perf_pLw = 0;
private _pen_pLw = 0;
if (_rodMaterialStr isEqualTo "steel") then {
_rodMaterialParams params ["_Pp", "_BHNP", "_a", "_c", "_k", "_n"];
private _exp = (-_c * 350^_k * _BHNP^_n) / (_Pp * _aproximateVelocity * _aproximateVelocity);
_pen_pLw = _a * _tanh * sqrt (_Pp / 7840) * exp _exp;
_perf_pLw = _pen_pLw * _cosm;
} else {
_rodMaterialParams params ["_Pp", "_BHNP", "_a", "_c0", "_c1"];
private _exp = (-(_c0 + _c1 * 350) * 350) / (_Pp * _aproximateVelocity * _aproximateVelocity);
_pen_pLw = _a * _tanh * _cosm * sqrt (_Pp / 7840) * exp _exp;
_perf_pLw = _pen_pLw * _cosm;
};
private _perforationDistance = _lw * _perf_pLw;
private _penetrationDistance = _lw * _pen_pLw;
private _hitpointEffectiveArmour = [_hitpointConfig >> QGVAR(thickness), "NUMBER", 0] call CBA_fnc_getConfigEntry;
private _hitpointEffectiveSlope = [_hitpointConfig >> QGVAR(slope), "NUMBER", 0] call CBA_fnc_getConfigEntry;
_penetrationDistance = _penetrationDistance * cos (_hitpointEffectiveSlope);
[_penetrationDistance > _hitpointEffectiveArmour, _penetrationDistance - _hitpointEffectiveArmour, _penetrationDistance, _perforationDistance, _hitpointEffectiveArmour]

View File

@ -1,27 +0,0 @@
#include "..\script_component.hpp"
/*
* Author: tcvm
* Detonates vehicle ammo and heavily wounds all inside.
*
* Arguments:
* 0: The vehicle <OBJECT>
* 1: Person who caused detonation <OBJECT> (default: objNull)
*
* Return Value:
* None
*
* Example:
* [tank2] call ace_vehicle_damage_fnc_detonate;
*
* Public: No
*/
params ["_vehicle", ["_injurer", objNull]];
if (((_vehicle call EFUNC(cookoff,getVehicleAmmo)) select 1) > 0) then {
{
[QGVAR(medicalDamage), [_x, _injurer, _injurer], _x] call CBA_fnc_targetEvent;
} forEach (crew _vehicle);
};
[QEGVAR(cookoff,detonateAmmunitionServer), [_vehicle, false, _injurer, _injurer]] call CBA_fnc_serverEvent;

View File

@ -4,64 +4,66 @@
* Handles whether or not the crew should bail.
*
* Arguments:
* 0: The vehicle <OBJECT>
* 1: Can the vehicle move? <BOOL>
* 2: Can the vehicle shoot? <BOOL>
* 0: Vehicle <OBJECT>
*
* Return Value:
* None
*
* Example:
* [tank1, false, true] call ace_vehicle_damage_fnc_handleBail
* cursorObject call ace_vehicle_damage_fnc_handleBail
*
* Public: No
*/
params ["_vehicle", "_canMove", "_canShoot"];
private _isCar = (_vehicle isKindOf "Car" && { !(_vehicle isKindOf "Wheeled_APC_F") });
params ["_vehicle"];
TRACE_1("handleBail",_vehicle);
if (_canMove) then {
_canMove = alive driver _vehicle;
};
private _isCar = _vehicle isKindOf "Car" && {!(_vehicle isKindOf "Wheeled_APC_F")};
if (_canShoot) then {
_canShoot = alive gunner _vehicle;
};
// canFire command is broken, hence the variable
private _canMove = (_vehicle getVariable [QGVAR(canMove), true]) && {alive driver _vehicle};
private _canShoot = (_vehicle getVariable [QGVAR(canShoot), true]) && {alive gunner _vehicle};
_vehicle setVariable[QGVAR(canMove), _canMove];
_vehicle setVariable[QGVAR(canShoot), _canShoot];
_vehicle setVariable [QGVAR(canMove), _canMove];
_vehicle setVariable [QGVAR(canShoot), _canShoot];
private _rand = random 1;
if (_isCar) then {
if !(_canMove) then {
[_vehicle] spawn FUNC(abandon);
LOG_3("[%1] can't move and is bailing and is a car [%2 | %3]",_vehicle,_canMove,_isCar);
if (!_canMove) then {
_vehicle call FUNC(abandon);
TRACE_3("car immobile - bailing",_vehicle,_canMove,_isCar);
};
} else {
if (!_canMove && !_canShoot ) exitWith { // If you can't move and you can't shoot, you better GTFO
[_vehicle] spawn FUNC(abandon);
LOG_3("[%1] is a sitting duck and is bailing [%2 | %3]",_vehicle,_canMove,_canShoot);
// If you can't move and you can't shoot, you better GTFO
if (!_canMove && !_canShoot) exitWith {
_vehicle call FUNC(abandon);
TRACE_3("immobile and can't shoot - bailing",_vehicle,_canMove,_canShoot);
};
if (!_canShoot && !_isCar) then {
if (BAILOUT_CHANCE_SHOOT > _rand) then { // 50% chance of bailing out if turret/gun is destroyed
[_vehicle] spawn FUNC(abandon);
LOG_4("[%1] Cannot shoot and is bailing with chance [%2] [%3 | %4]",_vehicle,_rand,_canMove,_canShoot);
if (!_canShoot) then {
// 50% chance of bailing out if turret/gun is disabled
if (BAILOUT_CHANCE_SHOOT > _rand) then {
_vehicle call FUNC(abandon);
TRACE_4("can't shoot - bailing",_vehicle,_rand,_canMove,_canShoot);
} else {
_vehicle allowFleeing 1;
LOG_4("[%1] Cannot shoot and is fleeing with chance [%2] [%3 | %4]",_vehicle,_rand,_canMove,_canShoot);
TRACE_4("fleeing",_vehicle,_rand,_canMove,_canShoot);
};
};
if !(_canMove) then {
if (BAILOUT_CHANCE_MOVE > _rand) then { // 80% Chance of bailing out if engine is destroyed
[_vehicle] spawn FUNC(abandon);
LOG_4("[%1] Cannot move and is bailing with chance [%2] [%3 | %4]",_vehicle,_rand,_canMove,_canShoot);
if (!_canMove) then {
// 80% Chance of bailing out if engine is disabled
if (BAILOUT_CHANCE_MOVE > _rand) then {
_vehicle call FUNC(abandon);
TRACE_4("immobile - bailing",_vehicle,_rand,_canMove,_canShoot);
} else {
LOG_4("[%1] Cannot move and is bunkering with chance [%2] [%3 | %4]",_vehicle,_rand,_canMove,_canShoot);
TRACE_4("immobile - bunkering",_vehicle,_rand,_canMove,_canShoot);
};
};
};

View File

@ -1,59 +1,71 @@
#include "..\script_component.hpp"
/*
* Author: tcvm
* Checks hitpoint damage and determines if a vehicle should cookoff.
* Checks hitpoint damage and determines if a vehicle should cook off.
*
* Arguments:
* 0: The vehicle <OBJECT>
* 0: Vehicle <OBJECT>
* 1: Chance of fire <NUMBER>
* 2: Intensity of cookoff <NUMBER>
* 3: Person who instigated cookoff <OBJECT> (default: objNull)
* 4: Part of vehicle which got hit <STRING> (default: "")
* 5: Whether or not the vehicle can spawn ring-fire effect <BOOL> (default: false)
* 6: Can Jet <BOOL> (default: true)
* 3: Source of damage <OBJECT>
* 4: Person who caused damage <OBJECT>
* 5: Part of vehicle which got hit <STRING> (default: "")
* 6: Whether or not the vehicle can spawn ring-fire effect <BOOL> (default: false)
* 7: Whether or not the vehicle can spawn jet-fire effect <BOOL> (default: true)
*
* Return Value:
* If cooked off
* If vehicle started or already cooking off <BOOL>
*
* Example:
* [tank2, 0.1, 5] call ace_vehicle_damage_fnc_handleCookoff;
* [cursorObject, 0.1, 5, player, player] call ace_vehicle_damage_fnc_handleCookoff
*
* Public: No
*/
params ["_vehicle", "_chanceOfFire", "_intensity", ["_injurer", objNull], ["_hitPart", ""], ["_canRing", false], ["_canJet", true]];
params ["_vehicle", "_chanceOfFire", "_intensity", "_source", "_instigator", ["_hitPart", ""], ["_canRing", true], ["_canJet", true]];
TRACE_8("handleCookoff",_vehicle,_chanceOfFire,_intensity,_source,_instigator,_hitPart,_canRing,_canJet);
// Ignore if the vehicle is already cooking off
if (_vehicle getVariable [QEGVAR(cookoff,isCookingOff), false]) exitWith {true};
if (_vehicle getVariable [QEGVAR(cookoff,isCookingOff), false]) exitWith {
TRACE_3("already cooking off",_vehicle,_chanceOfFire,_intensity);
true // return
};
_chanceOfFire = _chanceOfFire * EGVAR(cookoff,probabilityCoef);
if (_chanceOfFire >= random 1) exitWith {
private _configOf = configOf _vehicle;
private _fireDetonateChance = [_configOf >> QGVAR(detonationDuringFireProb), "number", 0] call CBA_fnc_getConfigEntry;
if (_canRing) then {
_canRing = ([_configOf >> QGVAR(canHaveFireRing), "number", 0] call CBA_fnc_getConfigEntry) == 1;
};
// Failure to cook off
if (_chanceOfFire == 0 || {_chanceOfFire < random 1}) exitWith {
TRACE_3("no cook-off",_vehicle,_chanceOfFire,_intensity);
if (_canJet) then {
_canJet = ([_configOf >> QEGVAR(cookoff,canHaveFireJet), "number", 1] call CBA_fnc_getConfigEntry) == 1;
};
private _delayWithSmoke = _chanceOfFire < random 1;
private _detonateAfterCookoff = (_fireDetonateChance / 4) > random 1;
private _source = "";
if (_hitPart == "engine") then {
_source = ["hit_engine_point", "HitPoints"];
};
[QEGVAR(cookOff,cookOffServer), [_vehicle, _intensity, _injurer, _injurer, _delayWithSmoke, _fireDetonateChance, _detonateAfterCookoff, _source, _canRing, _canJet]] call CBA_fnc_serverEvent;
LOG_4("Cooking-off [%1] with a chance-of-fire [%2] - Delayed Smoke | Detonate after cookoff [%3 | %4]",_vehicle,_chanceOfFire,_delayWithSmoke,_detonateAfterCookoff);
[_vehicle] spawn FUNC(abandon);
LOG_1("[%1] is on fire is bailing",_vehicle);
true
false // return
};
LOG_2("[%1] No Cook-off - Chance of fire [%2]",_vehicle,_chanceOfFire);
false
// Vehicle will cook off
private _configOf = configOf _vehicle;
private _fireDetonateChance = getNumber (_configOf >> QGVAR(detonationDuringFireProb));
if (_canRing) then {
_canRing = getNumber (_configOf >> QGVAR(canHaveFireRing)) == 1;
};
if (_canJet) then {
_canJet = getNumber (_configOf >> QEGVAR(cookoff,canHaveFireJet)) == 1;
};
private _delaySmoke = _chanceOfFire < random 1;
private _detonateAfterCookoff = (_fireDetonateChance / 4) > random 1;
private _source = "";
if (_hitPart == "engine") then {
_source = ["hit_engine_point", "HitPoints"];
};
[QEGVAR(cookOff,cookOffServer), [_vehicle, _intensity, _injurer, _injurer, _delayWithSmoke, _fireDetonateChance, _detonateAfterCookoff, _source, _canRing, _canJet]] call CBA_fnc_serverEvent;
TRACE_4("cooking-off",_vehicle,_chanceOfFire,_delaySmoke,_detonateAfterCookoff);
// Abandon vehicle
_vehicle call FUNC(abandon);
true // return

View File

@ -4,91 +4,88 @@
* Called by "HandleDamage" event handler. Sets up hit array for this frame's damage.
*
* Arguments:
* 0: The vehicle <OBJECT>
* 1: Name of selection where unit was damaged <STRING> (unused)
* 2: Damage taken <NUMBER>
* 3: Source unit of damage <OBJECT> (unused)
* 0: Vehicle <OBJECT>
* 1: Selection <STRING>
* 2: New level of damage <NUMBER>
* 3: Source of damage <OBJECT>
* 4: Projectile that caused damage <STRING>
* 5: Hit part index of hit point <NUMBER>
* 6: Instigator of damage <OBJECT> (unused)
* 7: Hit point config name <STRING>
* 5: Hit index <NUMBER>
* 6: Person who caused damage <OBJECT>
* 7: Hit point <STRING>
*
* Return Value:
* Current or maximum damage of part
* Current or maximum damage of part <NUMBER>
*
* Example:
* [myVehicle, projectile, 5, 0.663] call ace_vehicle_damage_fnc_handleDamage;
* [cursorObject, "hit_engine_point", 0.5, player, projectile, 1, player, "HitEngine"] call ace_vehicle_damage_fnc_handleDamage
*
* Public: No
*/
params ["_vehicle", "_hitSelection", "_damage", "", "_projectile", "_hitIndex", "", "_hitPoint"];
TRACE_6("HandleDamage event inputs",_vehicle,_hitSelection,_damage,_projectile,_hitIndex,_hitPoint);
params ["_vehicle", "_selection", "_newDamage", "_source", "_projectile", "_hitIndex", "_instigator", "_hitPoint"];
TRACE_8("handleDamage",_vehicle,_selection,_newDamage,_source,_projectile,_hitIndex,_instigator,_hitPoint);
private _returnHit = if (_hitSelection isNotEqualTo "") then {
if (!local _vehicle) exitWith {};
private _currentDamage = if (_selection != "") then {
_vehicle getHitIndex _hitIndex
} else {
damage _vehicle
};
if !(_projectile in ["ace_ammoExplosion", "ACE_ammoExplosionLarge"]) then {
if (local _vehicle) then {
// set up hit array so we can execute all damage next frame. Always in order of hit done.
private _hitHash = _vehicle getVariable [QGVAR(hitHash), nil];
if (isNil "_hitHash") then {
_hitHash = [[], nil] call CBA_fnc_hashCreate;
};
private _currentFrameArray = [_hitHash, diag_frameNo] call CBA_fnc_hashGet;
if (isNil "_currentFrameArray") then {
_currentFrameArray = [];
};
// if a valid hit, process it
if !((_hitPoint find "#light") >= 0 || { _damage <= 0 }) then {
if (_currentFrameArray isEqualTo []) then {
[{
params ["_vehicle", "_processingFrame"];
private _frameHash = _vehicle getVariable [QGVAR(hitHash), nil];
private _hitArray = [_frameHash, _processingFrame] call CBA_fnc_hashGet;
if (_hitArray isEqualTo []) exitWith {};
// If an invalid hit, don't process it
if (_newDamage <= 0 || {"#light" in _hitPoint}) exitWith {};
reverse _hitArray;
TRACE_3("processing data from old frame",diag_frameNo,_processingFrame,_hitArray);
{
_x params ["_vehicle", "_selection", "_damage", "_injurer", "_projectile", "_hitIndex", "", "_hitPoint"];
// Set up hit array so we can execute all damage next frame. Always in order of hit done.
private _hitHash = _vehicle getVariable QGVAR(hitHash);
private _currentFrameArray = _hitHash getOrDefault [diag_frameNo, [], true];
private _returnHit = if (_selection isNotEqualTo "") then {
_vehicle getHitIndex _hitIndex
} else {
damage _vehicle
};
if (_currentFrameArray isEqualTo []) then {
[{
params ["_vehicle", "_processingFrame"];
private _newDamage = _damage - _returnHit;
if !([_vehicle, _hitPoint, _hitIndex, _injurer, _returnHit, _newDamage, _projectile, _selection] call FUNC(handleVehicleDamage)) exitWith {
LOG_2("cancelling rest of vehicle damage queue ( [%1] items left out of [%2] )",(count (_hitArray#1)) - _forEachIndex,count (_hitArray#1))
};
} forEach _hitArray;
private _hitHash = _vehicle getVariable QGVAR(hitHash);
private _hitArray = _hitHash deleteAt _processingFrame;
[_frameHash, _processingFrame] call CBA_fnc_hashRem;
if (_hitArray isEqualTo []) exitWith {};
}, [_vehicle, diag_frameNo]] call CBA_fnc_execNextFrame;
};
_currentFrameArray pushBack _this;
};
TRACE_3("processing data from old frame",diag_frameNo,_processingFrame,_hitArray);
[_hitHash, diag_frameNo, _currentFrameArray] call CBA_fnc_hashSet;
_vehicle setVariable [QGVAR(hitHash), _hitHash];
// Start from newest damage and work backwards
{
_x params ["_vehicle", "_selection", "_newDamage", "_source", "_projectile", "_hitIndex", "_instigator", "_hitPoint"];
private _currentDamage = if (_selection != "") then {
_vehicle getHitIndex _hitIndex
} else {
damage _vehicle
};
private _addedDamage = _newDamage - _currentDamage;
TRACE_1("handleDamage",_currentDamage);
if !([_vehicle, _hitPoint, _hitIndex, _selection, _addedDamage, _projectile, _source, _instigator] call FUNC(handleVehicleDamage)) exitWith {
TRACE_2("cancelling rest of vehicle damage queue",(count (_hitArray#1)) - _forEachIndex,count (_hitArray#1))
};
} forEachReversed _hitArray;
}, [_vehicle, diag_frameNo]] call CBA_fnc_execNextFrame;
};
_currentFrameArray pushBack _this;
};
// damage is never to be handled in-engine. Always handle out of engine with this event handler
// don't return 0 or else old parts will be reset in damage
private _criticalDamageIndex = (CRITICAL_HITPOINTS findIf { _x isEqualTo _hitPoint }) + 1;
if (_criticalDamageIndex > 0) then {
_returnHit = (_returnHit min (CRITICAL_HITPOINTS select _criticalDamageIndex));
// Damage is never to be handled in-engine. Always handle out of engine with this event handler
// Don't return 0 or else old parts will be reset in damage
private _criticalDamageIndex = CRITICAL_HITPOINTS findIf {_x == _hitPoint};
if (_criticalDamageIndex != -1) then {
_currentDamage = _currentDamage min (CRITICAL_HITPOINTS_THRESHOLDS select _criticalDamageIndex);
};
if (_hitPoint isEqualTo "" && _hitIndex < 0) then {
_returnHit = _returnHit min 0.89;
if (_hitPoint == "" && {_hitIndex < 0}) then {
_currentDamage = _currentDamage min 0.89;
};
_returnHit
_currentDamage

View File

@ -20,7 +20,7 @@ params ["_vehicle", "", "", "", "_ammo"];
if (alive _vehicle) exitWith {};
TRACE_2("ejectIfDestroyed HDEH",typeOf _vehicle,_this);
TRACE_2("handleDamageEjectIfDestroyed",typeOf _vehicle,_this);
if (!IS_EXPLOSIVE_AMMO(_ammo)) then {
{

View File

@ -1,37 +1,59 @@
#include "..\script_component.hpp"
/*
* Author: tcvm
* Checks hitpoint damage and determines if a vehicle should cookoff.
* Author: tcvm, johnb43
* Checks hitpoint damage and determines if a vehicle should detonate its ammo.
*
* Arguments:
* 0: The vehicle <OBJECT>
* 0: Vehicle <OBJECT>
* 1: Chance of detonation <NUMBER>
* 2: Vehicle ammo array <ARRAY>
* 3: How much explosive ammo is inside vehicle <NUMBER>
* 4: How much non-explosive ammo inside vehicle <NUMBER>
* 5: Person who instigated damage <OBJECT> (default: objNull)
* 2: If the vehicle should be knocked out <BOOL>
* 3: If the crew should be injured <BOOL>
* 4: Source of damage <OBJECT>
* 5: Person who caused damage <OBJECT>
*
* Return Value:
* Detonated <BOOL>
* None
*
* Example:
* [tank2, 0.5] call ace_vehicle_damage_fnc_handleDetonation;
* [cursorObject, 0.5, true, player, player] call ace_vehicle_damage_fnc_handleDetonation
*
* Public: No
*/
params ["_vehicle", "_chanceOfDetonate", "_vehicleAmmo", "_explosiveAmmoCount", "_nonExplosiveAmmoCount", ["_injurer", objNull]];
private _isKnockedOut = _explosiveAmmoCount > 0;
params ["_vehicle", "_chanceToDetonate", "_knockOut", "_injureCrew", "_source", "_instigator"];
// Ignore if the vehicle is already detonating ammo
if (_vehicle getVariable [QEGVAR(cookoff,isAmmoDetonating), false]) exitWith {_isKnockedOut};
if (_vehicle getVariable [QEGVAR(cookoff,isAmmoDetonating), false]) exitWith {
TRACE_2("already detonating",_vehicle,_chanceToDetonate);
if (_chanceOfDetonate >= random 1) exitWith {
[_vehicle, _injurer, _vehicleAmmo] call FUNC(detonate);
LOG_2("Detonating [%1] with a chance-to-detonate [%2]",_vehicle,_chanceOfDetonate);
_isKnockedOut
if (_knockOut) then {
[_vehicle, _source, _instigator] call FUNC(knockOut);
};
_knockOut // return
};
LOG_2("[%1] No Detonation - Chance of detonation [%2]",_vehicle,_chanceOfDetonate);
false
// Failure to detonate
if (_chanceToDetonate == 0 || {_chanceToDetonate < random 1}) exitWith {
TRACE_2("no detonation",_vehicle,_chanceToDetonate);
false // return
};
// Vehicle will be detonated
if (_injureCrew) then {
{
[QGVAR(medicalDamage), [_x, _source, _instigator], _x] call CBA_fnc_targetEvent;
} forEach (crew _vehicle);
};
TRACE_2("detonation",_vehicle,_chanceToDetonate);
// Detonate the vehicle
[QEGVAR(cookoff,detonateAmmunitionServer), [_vehicle, false, _source, _instigator]] call CBA_fnc_serverEvent;
if (_knockOut) then {
[_vehicle, _source, _instigator] call FUNC(knockOut);
};
_knockOut // return

View File

@ -4,96 +4,101 @@
* Process vehicle hit.
*
* Arguments:
* 0: The vehicle <OBJECT>
* 1: The hitpoint which got hit <STRING>
* 2: The index of what got hit <NUMBER>
* 3: The damage that the new part took <NUMBER>
* 4: Person who hit vehicle <OBJECT>
* 5: Damage before hit <NUMBER>
* 6: Damage after hit <NUMBER>
* 7: Projectile <OBJECT>
* 8: Selection that got hit <STIRNG>
* 0: Vehicle <OBJECT>
* 1: Hit point <STRING>
* 2: Hit index <NUMBER>
* 3: Selection <STIRNG>
* 4: Added damage to part <NUMBER>
* 5: Projectile <OBJECT>
* 6: Source of damage <OBJECT>
* 7: Person who caused damage <OBJECT>
*
* Return Value:
* Whether or not to continue handling last frame's damage
* Whether or not to continue handling last frame's damage <BOOL>
*
* Example:
* [ace_vehicle_damage_fnc_handleVehicleDamage, tank1, "Hit_Engine", 12]] call CBA_fnc_execNextFrame
* [ace_vehicle_damage_fnc_handleVehicleDamage, [cursorObject, "HitEngine", 12, "hit_engine_point", 0.25, projectile, player, player]] call CBA_fnc_execNextFrame
*
* Public: No
*/
params["_vehicle", "_hitPoint", "_hitIndex", "_injurer", "_oldDamage", "_newDamage", "_projectile", "_selection"];
TRACE_6("handleVehicleDamage",_vehicle,_hitPoint,_hitIndex,_injurer,_oldDamage,_newDamage);
if !(alive _vehicle) exitWith {
private _eventHandler = _vehicle getVariable[QGVAR(handleDamage), nil];
if !(isNil "_eventHandler") then {
_vehicle removeEventHandler ["HandleDamage", _eventHandler];
params ["_vehicle", "_hitPoint", "_hitIndex", "_selection", "_addedDamage", "_projectile", "_source", "_instigator"];
TRACE_8("handleVehicleDamage",_vehicle,_hitPoint,_hitIndex,_selection,_addedDamage,_projectile,_source,_instigator);
if (!alive _vehicle) exitWith {
private _handleDamageEH = _vehicle getVariable [QGVAR(handleDamage), nil];
if (!isNil "_handleDamageEH") then {
_vehicle removeEventHandler ["HandleDamage", _handleDamageEH];
};
LOG_1("Vehicle [%1] no longer alive",_vehicle);
true
TRACE_1("vehicle no longer alive",_vehicle);
false
};
_hitPoint = toLowerANSI _hitPoint;
private _hitpointHash = _vehicle getVariable [QGVAR(hitpointHash), []];
private _type = if (_hitpointHash isEqualTo []) then {
"exit"
} else {
([_hitpointHash, _hitPoint] call CBA_fnc_hashGet) select 0
};
private _hitPointHash = GVAR(vehicleClassesHitPointHash) getOrDefault [typeOf _vehicle, createHashMap];
private _type = (_hitPointHash getOrDefault [_hitPoint, []]) select 0;
if (isNil "_type") then {
_type = "exit";
};
// generic structural damage will be transfered into hull damage for simulation's sake
// Generic structural damage will be transfered into hull damage for simulation's sake
private _structural = false;
if (_selection isEqualTo "") then {
if (_selection == "") then {
_type = "hull";
_hitPoint = "hithull";
_structural = true;
TRACE_1("structural damage",_selection);
_newDamage = abs _newDamage;
_addedDamage = abs _addedDamage;
};
if (_type isEqualTo "exit") exitWith { LOG_1("No relevant hitpoints hit [%1]. Exiting",_hitPoint); true };
if (isNil "_type") exitWith {
TRACE_1("no relevant hitpoints hit, exiting",_hitPoint);
true
};
// Ignore multiple hits at the same time
private _ignoreHit = false;
private _ignoreBailCheck = false;
private _multHit = _vehicle getVariable [QGVAR(hitTime), nil];
if (isNil "_multHit") then {
_vehicle setVariable[QGVAR(hitTime), [CBA_missionTime, _injurer, [_hitPoint]]];
_vehicle setVariable [QGVAR(hitTime), [CBA_missionTime, _source, [_hitPoint]]];
} else {
private _hitPointInOldArray = _hitPoint in (_multHit select 2);
private _withinTime = (CBA_missionTime <= (_multHit select 0) + CONST_TIME) && { _injurer == (_multHit select 1) };
private _withinTime = (CBA_missionTime <= (_multHit select 0) + CONST_TIME) && {_source == (_multHit select 1)};
if (_hitPointInOldArray && _withinTime) then {
_ignoreHit = true;
} else {
// If the hitpoint isnt in the old array then that means that the time expired and a new array should be generated
if !(_hitPointInOldArray) then {
if (!_hitPointInOldArray) then {
private _oldHitPoints = _multHit select 2;
_oldHitPoints pushBack _hitPoint;
_vehicle setVariable [QGVAR(hitTime), [CBA_missionTime, _injurer, _oldHitPoints]];
_vehicle setVariable [QGVAR(hitTime), [CBA_missionTime, _source, _oldHitPoints]];
_ignoreBailCheck = true;
} else {
_vehicle setVariable [QGVAR(hitTime), [CBA_missionTime, _injurer, [_hitPoint]]];
_vehicle setVariable [QGVAR(hitTime), [CBA_missionTime, _source, [_hitPoint]]];
};
};
};
if (_ignoreHit && !_structural) exitWith {
LOG_3("Ignoring multiple hits done to vehicle [%1] by [%2] -- hitpoint [%3].",_vehicle,_injurer,_hitPoint);
TRACE_3("ignoring multiple hits done to vehicle",_vehicle,_source,_hitPoint);
true
};
LOG_3("Processing hit done to vehicle [%1] by [%2] at time [%3].",_vehicle,_injurer,CBA_missionTime);
if !([_vehicle, _projectile, _hitIndex, _newDamage, [_hitpointHash, _hitPoint] call CBA_fnc_hashGet, _injurer] call FUNC(processHit)) exitWith { false };
TRACE_3("processing hit done to vehicle",_vehicle,_source,CBA_missionTime);
private _canMove = _vehicle getVariable[QGVAR(canMove), true];
private _canShoot = _vehicle getVariable[QGVAR(canShoot), true];
if !([_vehicle, _hitPoint, _hitIndex, _addedDamage, _projectile, _source, _instigator] call FUNC(processHit)) exitWith {false};
if !(_ignoreBailCheck) then {
[_vehicle, _canMove, _canShoot] call FUNC(handleBail);
if (!_ignoreBailCheck) then {
_vehicle call FUNC(handleBail);
};
true

View File

@ -1,37 +1,37 @@
#include "..\script_component.hpp"
/*
* Author: tcvm
* Knock out vehicle from battle. Destroy all internal hitpoints.
* Knock out a vehicle from battle by destroying all internal hitpoints.
*
* Arguments:
* 0: The vehicle <OBJECT>
* 0: Vehicle <OBJECT>
* 1: Source of damage <OBJECT>
* 2: Person who caused damage <OBJECT>
*
* Return Value:
* None
*
* Example:
* [vehicle player] call ace_vehicle_damage_fnc_knockOut
* [cursorObject, player, player] call ace_vehicle_damage_fnc_knockOut
*
* Public: No
*/
params ["_vehicle"];
private _hash = _vehicle getVariable [QGVAR(hitpointHash), nil];
if (isNil "_hash") exitWith {};
params ["_vehicle", "_source", "_instigator"];
TRACE_3("knockOut",_vehicle,_source,_instigator);
[_hash, {
private _hitpointAlias = _value#0;
if (_hitpointAlias isEqualTo "hull") then {
[_vehicle, -1, _key, 0.89] call FUNC(addDamage);
{
private _hitArea = _y select 0;
if (_hitArea == "hull") then {
[_vehicle, _x, -1, 0.89, _source, _instigator] call FUNC(setDamage);
} else {
if (_hitpointAlias in ["fuel", "turret", "gun", "engine"]) then {
if ((0.3 > random 1) || { _hitpointAlias isEqualTo "engine" }) then {
[_vehicle, -1, _key, 1] call FUNC(addDamage);
if (_hitArea in ["fuel", "turret", "gun", "engine"]) then {
if ((0.3 > random 1) || {_hitArea == "engine"}) then {
[_vehicle, _x, -1, 1, _source, _instigator] call FUNC(setDamage);
} else {
private _currentDamage = _vehicle getHitpointDamage _key;
[_vehicle, -1, _key, (_currentDamage + (0.3 max random 1)) min 1] call FUNC(addDamage);
[_vehicle, _x, -1, ((_vehicle getHitPointDamage _x) + (0.3 max random 1)) min 1, _source, _instigator] call FUNC(setDamage);
};
};
};
}] call CBA_fnc_hashEachPair;
} forEach (GVAR(vehicleClassesHitPointHash) getOrDefault [typeOf _vehicle, createHashMap]);

View File

@ -1,133 +1,132 @@
#include "..\script_component.hpp"
/*
* Author: tcvm
* Author: tcvm, johnb43
* 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>
* 0: Vehicle <OBJECT>
* 1: Hit point <STRING>
* 2: Hit index <NUMBER>
* 3: Added damage to part <NUMBER>
* 4: Projectile <OBJECT>
* 5: Source of damage <OBJECT>
* 6: Person who caused damage <OBJECT>
*
* Return Value:
* None
* Whether or not to continue handling last frame's damage <BOOL>
*
* Example:
* [myVehicle, projectile, 5, 0.663] call ace_vehicle_damage_fnc_processHit;
* [cursorObject, "HitEngine", 1, 0.25, projectile, player, player] call ace_vehicle_damage_fnc_processHit
*
* Public: No
*/
params ["_vehicle", "_projectile", "_hitIndex", "_newDamage", "_hitpointData", "_injurer"];
_hitpointData params ["_hitArea", "_hitpointConfig", "_hitpointName"];
params ["_vehicle", "_hitPoint", "_hitIndex", "_addedDamage", "_projectile", "_source", "_instigator"];
TRACE_7("processHit",_vehicle,_hitPoint,_hitIndex,_addedDamage,_projectile,_source,_instigator);
private _return = true;
if (_newDamage < 0) then {
_newDamage = -_newDamage;
};
_addedDamage = abs _addedDamage;
private _currentPartDamage = _vehicle getHitIndex _hitIndex;
private _nextPartDamage = _currentPartDamage + _newDamage;
private _newPartDamage = _currentPartDamage + _addedDamage;
// Damage is high enough for immediate destruction
if (_addedDamage >= 15) exitWith {
TRACE_2("immediate destruction - high damage",_addedDamage,_currentPartDamage);
// 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;
[QGVAR(medicalDamage), [_x, _source, _instigator, true], _x] call CBA_fnc_targetEvent;
} forEach (crew _vehicle);
_vehicle setDamage 1;
_return = false;
_return
// setDamage triggers "Killed" EH in cookoff, which starts ammo cook-off
[QGVAR(setDamage), [_vehicle, [1, true, _source, _instigator]]] call CBA_fnc_serverEvent;
false
};
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 _warheadTypeStr = toLowerANSI getText (_projectileConfig >> "warheadName");
private _warheadType = ["he", "ap", "heat", "tandemheat"] find _warheadTypeStr; // numerical index for warhead type for quicker checks. Numbers defined in script_macros.hpp
private _minDamage = [_hitpointConfig >> "minimalHit", "NUMBER", 0] call CBA_fnc_getConfigEntry;
if (_minDamage < 0) then {
_minDamage = -_minDamage;
};
private _incendiary = [_projectileConfig >> QGVAR(incendiary), "NUMBER", [0.3, 0.1, 1, 1, 0] select _warheadType] call CBA_fnc_getConfigEntry;
private _ammoEffectiveness = 0;
private _projectileExplosive = [_projectileConfig >> "explosive", "NUMBER", 0] call CBA_fnc_getConfigEntry;
private _indirectHit = [_projectileConfig >> "indirectHit", "NUMBER", 0] call CBA_fnc_getConfigEntry;
private _hitPointHash = GVAR(vehicleClassesHitPointHash) getOrDefault [typeOf _vehicle, createHashMap];
(_hitPointHash getOrDefault [_hitPoint, []]) params ["_hitArea", "_minDamage"];
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 _projectileExplosive = getNumber (_projectileConfig >> "explosive");
private _indirectHit = getNumber (_projectileConfig >> "indirectHit");
if (_warheadType == WARHEAD_TYPE_AP) then {
// Change damage based on projectile speed (doesn't do this in vanilla Arma believe it or not)
if (!isNull _source) then {
private _airFriction = getNumber (_projectileConfig >> "airFriction");
private _distance = _source distance _vehicle;
_addedDamage = (1 - _projectileExplosive) * _addedDamage * 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);
// Added damage can't be 0, so don't need to worry about 0 division here
if (_addedDamage < _minDamage) then {
_penChance = _addedDamage / _minDamage;
TRACE_5("minimum damage modifying hit",_addedDamage,_penChance,_minDamage,_warheadTypeStr,_hitArea);
};
if (_penChance < random 1) exitWith {
TRACE_1("didn't penetrate",_penChance);
_return
true
};
if (_minDamage == 0) then {
_minDamage = 1;
};
if (_warheadType isEqualTo WARHEAD_TYPE_HE) then {
if (_warheadType == WARHEAD_TYPE_HE) then {
private _modifiedIndirectHit = _indirectHit / 100;
if (_newDamage > _modifiedIndirectHit) then {
_newDamage = _newDamage / 2;
if (_addedDamage > _modifiedIndirectHit) then {
_addedDamage = _addedDamage / 2;
};
_newDamage = (_newDamage * (_newDamage / _modifiedIndirectHit)) min _newDamage;
_addedDamage = (_addedDamage * (_addedDamage / _modifiedIndirectHit)) min _addedDamage;
};
_ammoEffectiveness = if (_warheadType isEqualTo WARHEAD_TYPE_AP) then {
0.15 max _newDamage
private _ammoEffectiveness = if (_warheadType == WARHEAD_TYPE_AP) then {
0.15 max _addedDamage
} else {
if (_warheadType isEqualTo WARHEAD_TYPE_HE) then {
(_newDamage / (_minDamage + (_indirectHit / 100)) * 0.2)
if (_warheadType == WARHEAD_TYPE_HE) then {
(_addedDamage / (_minDamage + (_indirectHit / 100)) * 0.2)
} else {
((_newDamage / _minDamage) * 0.4) min 1
((_addedDamage / _minDamage) * 0.4) min 1
};
};
TRACE_4("ammo effectiveness",_ammoEffectiveness,_newDamage,_minDamage,_warheadTypeStr);
TRACE_4("ammo effectiveness",_ammoEffectiveness,_addedDamage,_minDamage,_warheadTypeStr);
_incendiary = _incendiary * _ammoEffectiveness;
private _isCar = (_vehicle isKindOf "Car" && { !(_vehicle isKindOf "Wheeled_APC_F") });
private _isCar = _vehicle isKindOf "Car" && {!(_vehicle isKindOf "Wheeled_APC_F")};
if (_isCar) then {
_ammoEffectiveness = (_ammoEffectiveness + (_ammoEffectiveness * 0.5)) min 1;
_ammoEffectiveness = (_ammoEffectiveness * 1.5) min 1;
};
private _currentVehicleAmmo = _vehicle call EFUNC(cookoff,getVehicleAmmo);
(_vehicle call EFUNC(cookoff,getVehicleAmmo)) params ["_magazines", "_totalAmmo"];
private _chanceOfDetonation = 0;
private _explosiveAmmoCount = 0;
private _nonExplosiveAmmoCount = 0;
if ((_currentVehicleAmmo select 0) isNotEqualTo []) then {
if (_magazines 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;
@ -135,215 +134,241 @@ if ((_currentVehicleAmmo select 0) isNotEqualTo []) then {
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 {
if (_explosive > 0.5 || {_hit > 50}) then {
_explosiveAmmoCount = _explosiveAmmoCount + 1;
} else {
_nonExplosiveAmmoCount = _nonExplosiveAmmoCount + 1;
};
} forEach (_currentVehicleAmmo select 0);
} forEach _magazines;
if (_countOfExplodableAmmo != 0) then {
_chanceOfDetonation = _chanceOfDetonation / _countOfExplodableAmmo;
};
};
private _chanceToDetonate = 0;
private _chanceOfFire = 0;
private _currentFuel = fuel _vehicle;
private _vehicleConfig = _vehicle call CBA_fnc_getObjectConfig;
private _return = true;
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 _vehicleConfig = configOf _vehicle;
private _currentFuel = fuel _vehicle;
private _chanceToDetonate = getNumber (_vehicleConfig >> QGVAR(engineDetonationProb)) * _incendiary * _currentFuel * _penChance;
private _cookoffIntensity = 4 * _currentFuel;
TRACE_6("hit engine",_chanceToDetonate,_chanceOfFire,_incendiary,_chanceOfDetonation,_currentFuel,_cookoffIntensity);
TRACE_4("hit engine",_chanceToDetonate,_incendiary,_chanceOfDetonation,_currentFuel);
if (_isCar) then {
_chanceOfFire = 0; // no cookoff for cars
// Knock out and detonate vehicle if necessary
if ([_vehicle, _chanceToDetonate, _explosiveAmmoCount > 0, _totalAmmo > 0, _source, _instigator] call FUNC(handleDetonation)) exitWith {};
// Cap damage at 0.9 to avoid hard coded blow up
_newPartDamage = 0.9 min _newPartDamage;
// Fatal engine/drive system damage (engine and tracks stop working at 0.9)
if (0.8 * _ammoEffectiveness > random 1) then {
_newPartDamage = 0.9;
};
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);
if (_newPartDamage == 0.9) then {
_vehicle setVariable [QGVAR(canMove), false];
} else {
[_vehicle, _hitIndex, _hitpointName, _nextPartDamage * _penChance] call FUNC(addDamage);
};
[_vehicle, _chanceOfFire, _cookoffIntensity, _injurer, _hitArea, false] call FUNC(handleCookoff);
[_vehicle, _hitPoint, _hitIndex, _newPartDamage * _penChance, _source, _instigator] call FUNC(setDamage);
// No cookoff for cars
if (_isCar) exitWith {};
private _chanceOfFire = getNumber (_vehicleConfig >> QGVAR(engineFireProb)) * _incendiary * _currentFuel * _penChance;
private _cookoffIntensity = 4 * _currentFuel;
[_vehicle, _chanceOfFire, _cookoffIntensity, _source, _instigator, "engine", false, 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 _vehicleConfig = configOf _vehicle;
private _currentFuel = fuel _vehicle;
private _chanceToDetonate = getNumber (_vehicleConfig >> QGVAR(hullDetonationProb)) * _incendiary * ((_chanceOfDetonation + _currentFuel) / 2) * _penChance;
private _cookoffIntensity = 1.5 + (_explosiveAmmoCount * _chanceOfFire);
TRACE_6("hit hull",_chanceToDetonate,_chanceOfFire,_incendiary,_chanceOfDetonation,_currentFuel,_cookoffIntensity);
TRACE_4("hit hull",_chanceToDetonate,_incendiary,_chanceOfDetonation,_currentFuel);
if (_isCar) then {
_chanceOfFire = 0; // no cookoff for cars
// Knock out and detonate vehicle if necessary
if ([_vehicle, _chanceToDetonate, _explosiveAmmoCount > 0, _totalAmmo > 0, _source, _instigator] call FUNC(handleDetonation)) exitWith {
[_vehicle, _hitPoint, _hitIndex, 0.89 * _penChance, _source, _instigator] call FUNC(setDamage);
};
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;
[_vehicle, _hitPoint, _hitIndex, 0.89 * _penChance, _source, _instigator] call FUNC(setDamage);
// Iterate through all keys and find appropriate turret
{
if ((_y select 0) == "turret") then {
_partKill pushBack _x;
};
}] call CBA_fnc_hashEachPair;
} forEach _hitPointHash;
_vehicle setVariable [QGVAR(canShoot), false];
};
case (_rand < 0.5): {
[_vehicle, _hitIndex, _hitpointName, 0.89 * _penChance] call FUNC(addDamage);
_partKill = _partKill + ENGINE_HITPOINTS#0;
[_vehicle, _hitPoint, _hitIndex, 0.89 * _penChance, _source, _instigator] call FUNC(setDamage);
_partKill append (ENGINE_HITPOINTS select 0);
if !(_vehicle isKindOf "Wheeled_APC_F") then {
_partKill = _partKill + TRACK_HITPOINTS#0;
_partKill append (TRACK_HITPOINTS select 0);
};
_vehicle setVariable [QGVAR(canMove), false];
};
case (_rand < 0.75): {
[_vehicle, _hitIndex, _hitpointName, 0.89 * _penChance] call FUNC(addDamage);
_partKill = _partKill + ENGINE_HITPOINTS#0;
[_vehicle, _hitPoint, _hitIndex, 0.89 * _penChance, _source, _instigator] call FUNC(setDamage);
_partKill append (ENGINE_HITPOINTS select 0);
if !(_vehicle isKindOf "Wheeled_APC_F") then {
_partKill = _partKill + TRACK_HITPOINTS#0;
_partKill append (TRACK_HITPOINTS select 0);
};
// iterate through all keys and find appropriate turret
[_hash, {
if (_value#0 isEqualTo "turret") then {
_partKill pushBack _key;
// Iterate through all keys and find appropriate turret
{
if ((_y select 0) == "turret") then {
_partKill pushBack _x;
};
}] call CBA_fnc_hashEachPair;
} forEach _hitPointHash;
_vehicle setVariable [QGVAR(canMove), false];
_vehicle setVariable [QGVAR(canShoot), false];
};
default{};
};
};
{
[_vehicle, _x, -1, _penChance, _source, _instigator] call FUNC(setDamage);
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);
// No cookoff for cars
if (_isCar) exitWith {};
private _chanceOfFire = getNumber (_vehicleConfig >> QGVAR(hullFireProb)) * _incendiary * ((_chanceOfDetonation + _currentFuel) / 2) * _penChance;
private _cookoffIntensity = 1.5 + (_explosiveAmmoCount * _chanceOfFire);
[_vehicle, _chanceOfFire, _cookoffIntensity, _source, _instigator] 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 _vehicleConfig = configOf _vehicle;
private _chanceToDetonate = getNumber (_vehicleConfig >> QGVAR(turretDetonationProb)) * _incendiary * _chanceOfDetonation * _penChance;
private _cookoffIntensity = _explosiveAmmoCount * _chanceOfFire;
TRACE_6("hit turret",_chanceToDetonate,_chanceOfFire,_incendiary,_chanceOfDetonation,_currentFuel,_cookoffIntensity);
TRACE_3("hit turret",_chanceToDetonate,_incendiary,_chanceOfDetonation);
if (_isCar) then {
_chanceOfFire = 0; // no cookoff for cars
};
if ([_vehicle, _chanceToDetonate, _explosiveAmmoCount, _nonExplosiveAmmoCount, _injurer] call FUNC(handleDetonation)) exitWith {
[_vehicle] call FUNC(knockOut);
};
if ([_vehicle, _chanceToDetonate, _explosiveAmmoCount > 0, _totalAmmo > 0, _source, _instigator] call FUNC(handleDetonation)) exitWith {};
if (0.8 * _ammoEffectiveness > random 1) then {
TRACE_1("damaged turret",_ammoEffectiveness * 0.8);
[_vehicle, _hitIndex, _hitpointName, 1 * _penChance] call FUNC(addDamage);
[_vehicle, _hitPoint, _hitIndex, _penChance, _source, _instigator] call FUNC(setDamage);
_vehicle setVariable [QGVAR(canShoot), false];
};
[_vehicle, _chanceOfFire, _cookoffIntensity, _injurer, "", true] call FUNC(handleCookoff);
// No cookoff for cars
if (_isCar) exitWith {};
private _chanceOfFire = getNumber (_vehicleConfig >> QGVAR(turretFireProb)) * _incendiary * _chanceOfDetonation * _penChance;
private _cookoffIntensity = _explosiveAmmoCount * _chanceOfFire;
[_vehicle, _chanceOfFire, _cookoffIntensity, _source, _instigator] call FUNC(handleCookoff);
};
case "gun": {
TRACE_5("hit gun",_chanceToDetonate,_chanceOfFire,_incendiary,_chanceOfDetonation,_currentFuel);
TRACE_2("hit gun",_addedDamage,_minDamage);
if (0.8 * _ammoEffectiveness > random 1) then {
TRACE_1("damaged gun",_ammoEffectiveness * 0.8);
[_vehicle, _hitIndex, _hitpointName, 1 * _penChance] call FUNC(addDamage);
[_vehicle, _hitPoint, _hitIndex, _penChance, _source, _instigator] call FUNC(setDamage);
_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);
private _damage = (0.1 max (0.1 * _addedDamage / _minDamage)) min 1;
[_vehicle, _hitPoint, _hitIndex, (_currentPartDamage + _damage) * _penChance] call FUNC(setDamage);
TRACE_3("damaged track",_damage,_addedDamage,_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);
[_vehicle, _hitPoint, _hitIndex, (_currentPartDamage + _addedDamage) * _penChance, _source, _instigator] call FUNC(setDamage);
TRACE_1("damaged wheel",_addedDamage);
};
case "fuel": {
_chanceOfFire = (_incendiary * _currentFuel * _penChance) / 2;
private _damage = (0.1 max (0.1 * _addedDamage / _minDamage)) min 1;
[_vehicle, _hitPoint, _hitIndex, (_currentPartDamage + _damage) * _penChance, _source, _instigator] call FUNC(setDamage);
// No cookoff for cars
if (_isCar) exitWith {};
private _currentFuel = fuel _vehicle;
private _chanceOfFire = (_incendiary * _currentFuel * _penChance) / 2;
private _cookoffIntensity = _currentFuel * 5;
TRACE_2("damaged fuel",_chanceOfFire,_cookoffIntensity);
if (_isCar) then {
_chanceOfFire = 0; // no cookoff for cars
};
TRACE_4("damaged fuel",_chanceOfFire,_incendiary,_cookoffIntensity,_currentFuel);
[_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);
[_vehicle, _chanceOfFire, _cookoffIntensity, _source, _instigator, "", false, false] call FUNC(handleCookoff);
};
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 {
// 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);
[_vehicle, _hitPoint, _hitIndex, 1, _source, _instigator] call FUNC(setDamage);
} else {
[_vehicle, _hitIndex, _hitpointName, _currentDamage + (0.5 max random 1)] call FUNC(addDamage);
[_vehicle, _hitPoint, _hitIndex, _currentDamage + (0.5 max random 1), _source, _instigator] call FUNC(setDamage);
};
if (_currentDamage < 1 && _warheadType isEqualTo WARHEAD_TYPE_HEAT) then {
if (_currentDamage < 1 && {_warheadType == 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 {
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, _hitPoint, _hitIndex, 1, _source, _instigator] call FUNC(setDamage);
// Don't process anymore damage if this is HEAT - shouldn't happen anyway but Arma says it does so you know
if (_currentDamage < 1 && {_warheadType == WARHEAD_TYPE_HEAT}) then {
_return = false;
};
};
};
default {
TRACE_1("hit unknown hitpoint??",_hitArea);
}
};
};
_return

View File

@ -0,0 +1,45 @@
#include "..\script_component.hpp"
/*
* Author: tcvm
* Sets vehicle damage based on HitIndex. Failing that it falls back to HitPoint name.
*
* Arguments:
* 0: Vehicle <OBJECT>
* 1: Hit point <STRING>
* 2: Hit index <NUMBER>
* 3: Damage <NUMBER>
* 4: Source of damage <OBJECT>
* 5: Person who caused damage <OBJECT>
*
* Return Value:
* None
*
* Example:
* [cursorObject, "HitEngine", 1, 0.25, player, player] call ace_vehicle_damage_fnc_setDamage
*
* Public: No
*/
params ["_vehicle", "_hitPoint", "_hitIndex", "_damage", "_source", "_instigator"];
TRACE_6("setDamage",_vehicle,_hitPoint,_hitIndex,_damage,_source,_instigator);
private _currentDamage = _vehicle getHitPointDamage _hitPoint;
if (_damage < _currentDamage) exitWith {
TRACE_3("capping damage at current",_damage,_currentDamage,_hitPoint);
};
if (_hitPoint == "#structural") then {
_hitPoint = "hithull";
_hitIndex = -1;
};
if (_hitIndex >= 0) then {
_vehicle setHitIndex [_hitIndex, _damage, true, _source, _instigator];
} else {
_vehicle setHitPointDamage [_hitPoint, _damage, true, _source, _instigator];
};
if (_hitPoint == "HitEngine" && {_damage >= 0.9}) then {
[QEGVAR(cookoff,engineFireServer), _vehicle] call CBA_fnc_serverEvent;
};

View File

@ -1,19 +1,21 @@
[
QGVAR(enabled), "CHECKBOX",
QGVAR(enabled),
"CHECKBOX",
[ELSTRING(common,Enabled), LSTRING(setting_description)],
LSTRING(category_displayName),
false, // default value
true, // isGlobal
false,
1,
{[QGVAR(enabled), _this] call EFUNC(common,cbaSettings_settingChanged)},
true // Needs mission restart
] call CBA_settings_fnc_init;
] call CBA_fnc_addSetting;
[
QGVAR(enableCarDamage), "CHECKBOX",
QGVAR(enableCarDamage),
"CHECKBOX",
[LSTRING(carDamage_setting_enable), LSTRING(carDamage_setting_description)],
LSTRING(category_displayName),
false, // default value
true, // isGlobal
false,
1,
{[QGVAR(enableCarDamage), _this] call EFUNC(common,cbaSettings_settingChanged)},
true // Needs mission restart
] call CBA_settings_fnc_init;
] call CBA_fnc_addSetting;

View File

@ -7,21 +7,23 @@
#define IS_EXPLOSIVE_AMMO(ammo) (getNumber (ammo call CBA_fnc_getObjectConfig >> "explosive") > 0.5)
#define ENGINE_HITPOINTS [["hitengine"], "engine"]
#define HULL_HITPOINTS [["hithull", "hitbody", "#structural"],"hull"]
#define HULL_HITPOINTS [["hithull", "hitbody", "#structural"], "hull"]
#define TRACK_HITPOINTS [["hitltrack", "hitrtrack"], "track"]
#define WHEEL_HITPOINTS [["hitlbwheel", "hitlmwheel", "hitlfwheel", "hitlf2wheel", "hitrbwheel", "hitrmwheel", "hitrlwheel", "hitrfwheel", "hitrf2wheel"], "wheel"]
#define FUEL_HITPOINTS [["hitfuel"], "fuel"]
#define ALL_HITPOINTS [ENGINE_HITPOINTS, HULL_HITPOINTS, TRACK_HITPOINTS, WHEEL_HITPOINTS, FUEL_HITPOINTS]
#define CRITICAL_HITPOINTS ["hithull", 0.89, "hitbody", 0.89, "#structural", 0.89, "hitengine", 0.9]
#define CRITICAL_HITPOINTS ["hithull", "hitbody", "#structural", "hitengine"]
#define CRITICAL_HITPOINTS_THRESHOLDS [0.89, 0.89, 0.89, 0.9]
#define WARHEAD_TYPE_HE 0
#define WARHEAD_TYPE_AP 1
#define WARHEAD_TYPE_HEAT 2
#define WARHEAD_TYPE_TANDEM 3
#define WARHEAD_TYPE_NONE 4
#define EJECT_IF_DESTROYED_VEHICLES ["Boat_Transport_02_base_F", "Rubber_duck_base_F"]
#define CREATE_INCENDIARY_AMMO(ammo,base,inc) class ammo: base { GVAR(incendiary) = inc; }
#define CREATE_INCENDIARY_AMMO(ammo,base,inc)\
class ammo: base {\
GVAR(incendiary) = inc;\
}

View File

@ -15,43 +15,33 @@
<Portuguese>ACE Dano avançãdo de veículos</Portuguese>
</Key>
<Key ID="STR_ACE_Vehicle_Damage_setting_description">
<English>Enable/Disable advanced vehicle damage</English>
<Japanese>アドバンスドビークルダメージを有効/無効にする</Japanese>
<English>Enable advanced vehicle damage</English>
<French>Active les dégâts de véhicule avancés.</French>
<German>Aktiviert/Deaktiviert den Erweiterten Fahrzeugsschaden</German>
<German>Aktiviert den Erweiterten Fahrzeugsschaden</German>
<Italian>Abilità danni avanzati ai veicoli</Italian>
<Polish>Włącz/Wyłącz zaawansowane uszkodzenia pojazdów</Polish>
<Chinesesimp>启用/禁用高级载具损坏</Chinesesimp>
<Korean>고급 차량 피해 활성화/비활성화</Korean>
<Russian>Включить/выключить продвинутое повреждение техники</Russian>
<Spanish>Habilitar/Deshabilitar el daño avanzado de vehículos</Spanish>
<Portuguese>Ativar/Desativar dano avançado de veículo</Portuguese>
</Key>
<Key ID="STR_ACE_Vehicle_Damage_carDamage_setting_description">
<English>Enable/Disable advanced car damage (Experimental)</English>
<Japanese>アドバンスド車ダメージを有効/無効にする (試験的)</Japanese>
<French>Active les dégâts avancés sur les voitures (expérimental).</French>
<German>Aktiviert/Deaktiviert den Erweiterten Fahrzeugsschaden (Experimentell)</German>
<Italian>Abilita danni avanzati ai veicoli (sperimentale)</Italian>
<Polish>Włącz/Wyłącz zaawansowane uszkodzenia w samochodach (eksperymentalne)</Polish>
<Chinesesimp>启用/禁用高级车辆损坏(实验性)</Chinesesimp>
<Korean>고급 차량 피해(실험용) 활성화/비활성화</Korean>
<Russian>Включить/выключить продвинутое повреждение машин (экспериментальное)</Russian>
<Spanish>Habilita/Deshabilita el daño avanzado de coche (Experimental)</Spanish>
<Portuguese>Ativar/Desativar dano avançado de carro (Experimental)</Portuguese>
<Polish>Włącz zaawansowane uszkodzenia pojazdów</Polish>
<Russian>Включить продвинутое повреждение техники</Russian>
<Spanish>Habilitar el daño avanzado de vehículos</Spanish>
<Portuguese>Ativar dano avançado de veículo</Portuguese>
</Key>
<Key ID="STR_ACE_Vehicle_Damage_carDamage_setting_enable">
<English>Enable/Disable advanced car damage</English>
<Japanese>アドバンスド車ダメージを有効/無効にする</Japanese>
<English>Enable advanced car damage</English>
<French>Dégâts de voiture avancés</French>
<German>Aktiviert/Deaktiviert erweiterten Autoschaden</German>
<German>Aktiviert erweiterten Autoschaden</German>
<Italian>Abilita danni avanzati alle macchine</Italian>
<Polish>Włącz/Wyłącz zaawansowane uszkodzenia w samochodach</Polish>
<Chinesesimp>启用/禁用高级车辆损坏</Chinesesimp>
<Korean>고급 차량 피해 활성화/비활성화</Korean>
<Russian>Продвинутое повреждение машин</Russian>
<Spanish>Habilitar/Deshabilitar daño avanzado de coche (Experimental)</Spanish>
<Portuguese>Ativar/Desativar dano avançado de carro</Portuguese>
<Polish>Włącz zaawansowane uszkodzenia w samochodach</Polish>
<Spanish>Habilitar daño avanzado de coche</Spanish>
<Portuguese>Habilitar dano avançado para carros</Portuguese>
</Key>
<Key ID="STR_ACE_Vehicle_Damage_carDamage_setting_description">
<English>Enable advanced car damage (Experimental)</English>
<French>Active les dégâts avancés sur les voitures (expérimental).</French>
<German>Aktiviert den Erweiterten Fahrzeugsschaden (Experimentell)</German>
<Italian>Abilita danni avanzati ai veicoli (sperimentale)</Italian>
<Polish>Włącz zaawansowane uszkodzenia w samochodach (eksperymentalne)</Polish>
<Russian>Включить продвинутое повреждение машин (экспериментальное)</Russian>
<Spanish>Habilitar daño avanzado de coche (Experimental)</Spanish>
<Portuguese>Habilitar dano avançado para carros (Experimental)</Portuguese>
</Key>
<Key ID="STR_ACE_Vehicle_Damage_generic_turret_wreck">
<English>Wreck (Turret)</English>

View File

@ -19,7 +19,7 @@ Removes hit-point based damage on armoured vehicles.
### 1.1 Differences from vanilla
Vehicle damage is component based in this system. Rather than catastrophically exploding when an arbitrary amount of damage is recieved, the system calculates what warhead hit you, if it hit SLAT/ERA, and what components it hits.
Vehicle damage is component based in this system. Rather than catastrophically exploding when an arbitrary amount of damage is received, the system calculates what warhead hit you, if it hit SLAT/ERA, and what components it hits.
When you are hit, a calculation takes place and determines which people inside the vehicle should get hurt.
### 1.2 How is damage calculated
@ -116,20 +116,3 @@ When hit, HEAT warheads will be defeated and no damage will be applied. If hit w
- Can cook-off [N]
- Can injure occupants [N]
## 3. Addon Options
### 3.1 Enable
- Enables/Disables the vehicle damage simulation
- Default: On
### 3.2 Enable/Disable Ammo Removal During Cook-Off
- Enables/Disables whether or not vehicle ammo will be fully removed upon cooking-off
- Default: On
### 3.3 Enable/Disable advanced car damage (Experimental)
- Enable experimental car damage. System will apply vehicle damage simulation to "car" types (trucks, SUVs, Humvees, etc.). Not fully tested and could be immbalanced causing weird behaviours.
- Default: Off

View File

@ -73,7 +73,7 @@ Default: 0
Whether or not this vehicle can spawn a `jet` effect (Boolean value: 0 or 1)
Default: 0
Default: 1
#### 1.1.10 `ace_vehicle_damage_slatHitpoints`
@ -89,9 +89,9 @@ Default: {}
#### 1.1.12 `ace_vehicle_damage_turret`
String for turret classname to spawn when catastrophically destroyed. Turret will pop-off and this is the class spawned
String for turret classname to spawn when catastrophically destroyed, followed by offset the object should spawn with. Turret will pop-off and this is the class spawned
Default: ""
Default: {}
### 1.2 Defined Hitpoints