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_Vorona_HEAT;
class M_SPG9_HEAT; class M_SPG9_HEAT;
class R_MRAAWS_HEAT_F; class R_MRAAWS_HEAT_F;
class B_338_Ball;
class ACE_G_40mm_HE;
CREATE_INCENDIARY_AMMO(BulletBase, BulletCore, 0.1); CREATE_INCENDIARY_AMMO(BulletBase, BulletCore, 0.1);
CREATE_INCENDIARY_AMMO(ShellBase, ShellCore, 1.0); CREATE_INCENDIARY_AMMO(ShellBase, ShellCore, 1.0);

View File

@ -1,19 +1,17 @@
class Extended_PreStart_EventHandlers { class Extended_PreStart_EventHandlers {
class ADDON { class ADDON {
init = QUOTE(call COMPILE_SCRIPT(XEH_preStart)); 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 Extended_PreInit_EventHandlers {
class ADDON { class ADDON {
init = QUOTE(call COMPILE_SCRIPT(XEH_preInit)); 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(engineFireProb) = 0.5;
GVAR(detonationDuringFireProb) = 0.2; GVAR(detonationDuringFireProb) = 0.2;
GVAR(canHaveFireRing) = 0; GVAR(canHaveFireRing) = 0;
EGVAR(cookoff,canHaveFireJet) = 1;
}; };
class Wheeled_APC_F: Car_F { class Wheeled_APC_F: Car_F {
GVAR(hullDetonationProb) = 0.2; GVAR(hullDetonationProb) = 0.2;
@ -38,6 +39,7 @@ class CfgVehicles {
GVAR(engineFireProb) = 0.5; GVAR(engineFireProb) = 0.5;
GVAR(detonationDuringFireProb) = 0.2; GVAR(detonationDuringFireProb) = 0.2;
GVAR(canHaveFireRing) = 0; GVAR(canHaveFireRing) = 0;
EGVAR(cookoff,canHaveFireJet) = 1;
}; };
class APC_Tracked_01_base_F: Tank_F {}; class APC_Tracked_01_base_F: Tank_F {};
class B_APC_Tracked_01_base_F: APC_Tracked_01_base_F {}; class B_APC_Tracked_01_base_F: APC_Tracked_01_base_F {};
@ -288,4 +290,3 @@ class CfgVehicles {
GVAR(canHaveFireRing) = 1; GVAR(canHaveFireRing) = 1;
}; };
}; };

View File

@ -1,14 +1,13 @@
PREP(abandon); PREP(abandon);
PREP(addEventHandler); 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(blowOffTurret);
PREP(handleBail);
PREP(handleCookoff);
PREP(handleDamage);
PREP(handleDamageEjectIfDestroyed);
PREP(handleDetonation);
PREP(handleVehicleDamage);
PREP(knockOut);
PREP(medicalDamage); PREP(medicalDamage);
PREP(processHit);
PREP(setDamage);

View File

@ -1,35 +1,58 @@
#include "script_component.hpp" #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)); TRACE_1("settings init",GVAR(enabled));
if (GVAR(enabled)) then {
if (!GVAR(enabled)) exitWith {};
[QGVAR(medicalDamage), LINKFUNC(medicalDamage)] call CBA_fnc_addEventHandler; [QGVAR(medicalDamage), LINKFUNC(medicalDamage)] call CBA_fnc_addEventHandler;
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;
};
[QGVAR(bailOut), { [QGVAR(bailOut), {
params ["_center", "_crewman", "_vehicle"]; params ["_vehicle", "_unit"];
TRACE_3("bailOut",_center,_crewman,_vehicle);
if (isPlayer _crewman) exitWith {}; TRACE_2("bailOut",_vehicle,_unit);
if (!alive _crewman || {!([_crewman] call EFUNC(common,isAwake))}) exitWith {};
unassignVehicle _crewman; // Ignore players and the dead
_crewman leaveVehicle _vehicle; if (_unit call EFUNC(common,isPlayer) || {!(_unit call EFUNC(common,isAwake))}) exitWith {};
doGetOut _crewman;
unassignVehicle _unit;
_unit leaveVehicle _vehicle;
doGetOut _unit;
private _angle = floor (random 360); private _angle = floor (random 360);
private _dist = (30 + (random 10)); private _dist = 30 + (random 10);
private _escape = _center getPos [_dist, _angle]; private _escape = _vehicle getPos [_dist, _angle];
_crewman doMove _escape; _unit doMove _escape;
_crewman setSpeedMode "FULL"; _unit setSpeedMode "FULL";
}] call CBA_fnc_addEventHandler; }] call CBA_fnc_addEventHandler;
["Tank", "init", LINKFUNC(addEventHandler), true, [], true] call CBA_fnc_addClassEventHandler; GVAR(vehicleClassesHitPointHash) = createHashMap;
["Wheeled_APC_F", "init", LINKFUNC(addEventHandler), true, [], true] call CBA_fnc_addClassEventHandler;
if (GVAR(enableCarDamage)) then { ["Tank", "Init", LINKFUNC(addEventHandler), true, [], true] call CBA_fnc_addClassEventHandler;
["Car", "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 // Blow off turret effect
// TODO: Add blowing-off-turret effect to vehicles that cook-off but aren't destroyed (no catastrophic explosion) // TODO: Add blowing-off-turret effect to vehicles that cook-off but aren't destroyed (no catastrophic explosion)
@ -38,16 +61,5 @@
if (_this select 3 && random 1 < 0.15) then { if (_this select 3 && random 1 < 0.15) then {
(_this select 0) call FUNC(blowOffTurret); (_this select 0) call FUNC(blowOffTurret);
}; };
}, true, [], true] call CBA_fnc_addClassEventHandler; }] call CBA_fnc_addClassEventHandler;
};
// 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;
}] call CBA_fnc_addEventHandler; }] call CBA_fnc_addEventHandler;

View File

@ -4,29 +4,34 @@
* Forces the AI currently in a vehicle to bail out. * Forces the AI currently in a vehicle to bail out.
* *
* Arguments: * Arguments:
* 0: The vehicle in which to bail out <OBJECT> * 0: Vehicle <OBJECT>
* *
* Return Value: * Return Value:
* None * None
* *
* Example: * Example:
* [tank2] call ace_vehicle_damage_fnc_abandon; * cursorObject call ace_vehicle_damage_fnc_abandon
* *
* Public: No * Public: No
*/ */
params ["_vehicle"]; 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"]; params ["_vehicle"];
_vehicle allowCrewInImmobile false; _vehicle allowCrewInImmobile false;
private _center = getPosASL _vehicle; TRACE_2("bailing out crew after delay",_vehicle,crew _vehicle);
TRACE_2("bailing out crew after delay",_vehicle,_center);
{ {
[QGVAR(bailOut), [_center, _x, _vehicle], _x] call CBA_fnc_targetEvent; [QGVAR(bailOut), [_vehicle, _x], _x] call CBA_fnc_targetEvent;
} forEach crew _vehicle; } forEach (crew _vehicle);
}, _this, random MAX_CREW_BAILOUT_TIME] call CBA_fnc_waitAndExecute; }, _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,16 +1,16 @@
#include "..\script_component.hpp" #include "..\script_component.hpp"
/* /*
* Author: tcvm * Author: tcvm, johnb43
* Adds the event handler to a vehicle. * Adds the event handler to a vehicle.
* *
* Arguments: * Arguments:
* 0: The vehicle in which to add the event handler to <OBJECT> * 0: Vehicle <OBJECT>
* *
* Return Value: * Return Value:
* None * None
* *
* Example: * Example:
* [tank2] call ace_vehicle_damage_fnc_addEventHandler; * cursorObject call ace_vehicle_damage_fnc_addEventHandler
* *
* Public: No * Public: No
*/ */
@ -18,93 +18,120 @@
params ["_vehicle"]; params ["_vehicle"];
TRACE_2("addEventHandler",_vehicle,GVAR(enabled)); TRACE_2("addEventHandler",_vehicle,GVAR(enabled));
if !(GVAR(enabled)) exitWith { if (!GVAR(enabled)) exitWith {
#ifdef DEBUG_MODE_FULL #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 #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 _vehicleConfig = configOf _vehicle;
private _hitpointsConfig = _vehicleConfig >> "HitPoints"; 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};
// Add hitpoint names to config for quick lookup // 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; _hitPointHash set [toLowerANSI _x, [_hitArea, abs getNumber (_hitPointsConfig >> _x >> "minimalHit")]];
} forEach _hitpoints; } forEach _hitPoints;
} forEach ALL_HITPOINTS; } forEach ALL_HITPOINTS;
_vehicle setVariable [QGVAR(hitpointHash), _hitpointHash]; // Gun and turret hitpoints aren't hardcoded anymore - dig through config to find correct names
private _fnc_iterateThroughConfig = {
// gun and turret hitpoints arent hardcoded anymore - dig through config to find correct names params ["_config"];
private _iterateThroughConfig = {
params ["_vehicle", "_config", "_iterateThroughConfig", "_hitpointAliases"];
TRACE_1("checking config",_config); TRACE_1("checking config",_config);
private _configName = toLowerANSI configName _config; private _configName = toLowerANSI configName _config;
private _isGun = ([_config >> "isGun", "NUMBER", 0] call CBA_fnc_getConfigEntry) == 1; private _isGun = getNumber (_config >> "isGun") == 1;
private _isTurret = ([_config >> "isTurret", "NUMBER", 0] call CBA_fnc_getConfigEntry) == 1; private _isTurret = getNumber (_config >> "isTurret") == 1;
private _isEra = _configName in _eraHitpoints; private _isEra = _configName in _eraHitpoints;
private _isSlat = _configName in _slatHitpoints; private _isSlat = _configName in _slatHitpoints;
private _isMisc = false; private _isMisc = false;
// prevent incompatibilites with old mods // Prevent incompatibilites with old mods
if (_configName isEqualTo "hitturret") then { if (_configName == "hitturret") then {
_isTurret = true; _isTurret = true;
}; };
if (_configName isEqualTo "hitgun") then {
if (_configName == "hitgun") then {
_isGun = true; _isGun = true;
}; };
private _hash = _vehicle getVariable QGVAR(hitpointHash);
{ {
_x params ["_hitType", "_hitPoints"]; _x params ["_hitArea", "_hitPoints"];
if (_configName in _hitPoints) then { if (_configName in _hitPoints) then {
[_hash, _configName, [_hitType, _config, _configName]] call CBA_fnc_hashSet; _hitPointHash set [_configName, [_hitArea, abs getNumber (_config >> "minimalHit")]];
_isMisc = true; _isMisc = true;
}; };
} forEach _hitpointAliases; } forEach _hitPointAliases;
if (_isGun || _isTurret || _isEra || _isSlat || _isMisc) then { if (_isGun || _isTurret || _isEra || _isSlat || _isMisc) then {
TRACE_6("found gun/turret/era/slat/misc",_isGun,_isTurret,_isEra,_isSlat,_isMisc,_hash);
if (_isGun) then { if (_isGun) then {
[_hash, _configName, ["gun", _config, _configName]] call CBA_fnc_hashSet; _hitPointHash set [_configName, ["gun", abs getNumber (_config >> "minimalHit")]];
}; };
if (_isTurret) then { if (_isTurret) then {
[_hash, _configName, ["turret", _config, _configName]] call CBA_fnc_hashSet; _hitPointHash set [_configName, ["turret", abs getNumber (_config >> "minimalHit")]];
}; };
if (_isEra) then { if (_isEra) then {
[_hash, _configName, ["era", _config, _configName]] call CBA_fnc_hashSet; _hitPointHash set [_configName, ["era", abs getNumber (_config >> "minimalHit")]];
}; };
if (_isSlat) then { 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 { } else {
{ {
[_vehicle, _x, _iterateThroughConfig, _hitpointAliases] call _iterateThroughConfig; _x call _fnc_iterateThroughConfig;
} forEach configProperties [_config, "isClass _x", true]; } forEach configProperties [_config, "isClass _x", true];
}; };
}; };
private _hitpointAliases = [_vehicleConfig >> QGVAR(hitpointAlias), "ARRAY", []] call CBA_fnc_getConfigEntry; private _turretConfig = _vehicleConfig >> "Turrets";
TRACE_1("hitpoint alias",_hitpointAliases); private _eraHitpoints = (getArray (_vehicleConfig >> QGVAR(eraHitpoints))) apply {toLowerANSI _x};
[_vehicle, _hitpointsConfig, _iterateThroughConfig, _hitpointAliases] call _iterateThroughConfig; private _slatHitpoints = (getArray (_vehicleConfig >> QGVAR(slatHitpoints))) apply {toLowerANSI _x};
[_vehicle, _turretConfig, _iterateThroughConfig, _hitpointAliases] call _iterateThroughConfig;
_vehicle allowCrewInImmobile true; private _fnc_toLowerCase = {
private _eh = _vehicle getVariable [QGVAR(handleDamage), nil]; _this apply {
if (isNil "_eh") then { if (_x isEqualType []) 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. _x call _fnc_toLowerCase
// Maybe its overridden somewhere else, but this makes sure it is the last one } else {
[{ toLowerANSI _x
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;
}; };
};
};
// 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 * None
* *
* Example: * Example:
* (vehicle player) call ace_vehicle_damage_fnc_blowOffTurret * cursorObject call ace_vehicle_damage_fnc_blowOffTurret
* *
* Public: No * Public: No
*/ */
@ -19,6 +19,7 @@
// The sudden change in the model would cause nearby PhysX objects to get stuck // The sudden change in the model would cause nearby PhysX objects to get stuck
[{ [{
params ["_vehicle"]; params ["_vehicle"];
TRACE_1("blowOffTurret",_vehicle);
(getArray (configOf _vehicle >> QGVAR(turret))) params [["_model", "", [""]], ["_offset", [0, 0, 0], [[]], 3]]; (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,29 +4,25 @@
* Handles whether or not the crew should bail. * Handles whether or not the crew should bail.
* *
* Arguments: * Arguments:
* 0: The vehicle <OBJECT> * 0: Vehicle <OBJECT>
* 1: Can the vehicle move? <BOOL>
* 2: Can the vehicle shoot? <BOOL>
* *
* Return Value: * Return Value:
* None * None
* *
* Example: * Example:
* [tank1, false, true] call ace_vehicle_damage_fnc_handleBail * cursorObject call ace_vehicle_damage_fnc_handleBail
* *
* Public: No * Public: No
*/ */
params ["_vehicle", "_canMove", "_canShoot"]; params ["_vehicle"];
private _isCar = (_vehicle isKindOf "Car" && { !(_vehicle isKindOf "Wheeled_APC_F") }); TRACE_1("handleBail",_vehicle);
if (_canMove) then { private _isCar = _vehicle isKindOf "Car" && {!(_vehicle isKindOf "Wheeled_APC_F")};
_canMove = alive driver _vehicle;
};
if (_canShoot) then { // canFire command is broken, hence the variable
_canShoot = alive gunner _vehicle; 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(canMove), _canMove];
_vehicle setVariable [QGVAR(canShoot), _canShoot]; _vehicle setVariable [QGVAR(canShoot), _canShoot];
@ -34,34 +30,40 @@ _vehicle setVariable[QGVAR(canShoot), _canShoot];
private _rand = random 1; private _rand = random 1;
if (_isCar) then { if (_isCar) then {
if !(_canMove) then { if (!_canMove) then {
[_vehicle] spawn FUNC(abandon); _vehicle call FUNC(abandon);
LOG_3("[%1] can't move and is bailing and is a car [%2 | %3]",_vehicle,_canMove,_isCar);
TRACE_3("car immobile - bailing",_vehicle,_canMove,_isCar);
}; };
} else { } else {
if (!_canMove && !_canShoot ) exitWith { // If you can't move and you can't shoot, you better GTFO // If you can't move and you can't shoot, you better GTFO
[_vehicle] spawn FUNC(abandon); if (!_canMove && !_canShoot) exitWith {
LOG_3("[%1] is a sitting duck and is bailing [%2 | %3]",_vehicle,_canMove,_canShoot); _vehicle call FUNC(abandon);
TRACE_3("immobile and can't shoot - bailing",_vehicle,_canMove,_canShoot);
}; };
if (!_canShoot && !_isCar) then { if (!_canShoot) then {
if (BAILOUT_CHANCE_SHOOT > _rand) then { // 50% chance of bailing out if turret/gun is destroyed // 50% chance of bailing out if turret/gun is disabled
[_vehicle] spawn FUNC(abandon); if (BAILOUT_CHANCE_SHOOT > _rand) then {
LOG_4("[%1] Cannot shoot and is bailing with chance [%2] [%3 | %4]",_vehicle,_rand,_canMove,_canShoot); _vehicle call FUNC(abandon);
TRACE_4("can't shoot - bailing",_vehicle,_rand,_canMove,_canShoot);
} else { } else {
_vehicle allowFleeing 1; _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 (!_canMove) then {
if (BAILOUT_CHANCE_MOVE > _rand) then { // 80% Chance of bailing out if engine is destroyed // 80% Chance of bailing out if engine is disabled
[_vehicle] spawn FUNC(abandon); if (BAILOUT_CHANCE_MOVE > _rand) then {
LOG_4("[%1] Cannot move and is bailing with chance [%2] [%3 | %4]",_vehicle,_rand,_canMove,_canShoot); _vehicle call FUNC(abandon);
TRACE_4("immobile - bailing",_vehicle,_rand,_canMove,_canShoot);
} else { } 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

@ -4,56 +4,68 @@
* Checks hitpoint damage and determines if a vehicle should cook off. * Checks hitpoint damage and determines if a vehicle should cook off.
* *
* Arguments: * Arguments:
* 0: The vehicle <OBJECT> * 0: Vehicle <OBJECT>
* 1: Chance of fire <NUMBER> * 1: Chance of fire <NUMBER>
* 2: Intensity of cookoff <NUMBER> * 2: Intensity of cookoff <NUMBER>
* 3: Person who instigated cookoff <OBJECT> (default: objNull) * 3: Source of damage <OBJECT>
* 4: Part of vehicle which got hit <STRING> (default: "") * 4: Person who caused damage <OBJECT>
* 5: Whether or not the vehicle can spawn ring-fire effect <BOOL> (default: false) * 5: Part of vehicle which got hit <STRING> (default: "")
* 6: Can Jet <BOOL> (default: true) * 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: * Return Value:
* If cooked off * If vehicle started or already cooking off <BOOL>
* *
* Example: * 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 * 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 // 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); _chanceOfFire = _chanceOfFire * EGVAR(cookoff,probabilityCoef);
if (_chanceOfFire >= random 1) exitWith { // Failure to cook off
if (_chanceOfFire == 0 || {_chanceOfFire < random 1}) exitWith {
TRACE_3("no cook-off",_vehicle,_chanceOfFire,_intensity);
false // return
};
// Vehicle will cook off
private _configOf = configOf _vehicle; private _configOf = configOf _vehicle;
private _fireDetonateChance = [_configOf >> QGVAR(detonationDuringFireProb), "number", 0] call CBA_fnc_getConfigEntry; private _fireDetonateChance = getNumber (_configOf >> QGVAR(detonationDuringFireProb));
if (_canRing) then { if (_canRing) then {
_canRing = ([_configOf >> QGVAR(canHaveFireRing), "number", 0] call CBA_fnc_getConfigEntry) == 1; _canRing = getNumber (_configOf >> QGVAR(canHaveFireRing)) == 1;
}; };
if (_canJet) then { if (_canJet) then {
_canJet = ([_configOf >> QEGVAR(cookoff,canHaveFireJet), "number", 1] call CBA_fnc_getConfigEntry) == 1; _canJet = getNumber (_configOf >> QEGVAR(cookoff,canHaveFireJet)) == 1;
}; };
private _delayWithSmoke = _chanceOfFire < random 1; private _delaySmoke = _chanceOfFire < random 1;
private _detonateAfterCookoff = (_fireDetonateChance / 4) > random 1; private _detonateAfterCookoff = (_fireDetonateChance / 4) > random 1;
private _source = ""; private _source = "";
if (_hitPart == "engine") then { if (_hitPart == "engine") then {
_source = ["hit_engine_point", "HitPoints"]; _source = ["hit_engine_point", "HitPoints"];
}; };
[QEGVAR(cookOff,cookOffServer), [_vehicle, _intensity, _injurer, _injurer, _delayWithSmoke, _fireDetonateChance, _detonateAfterCookoff, _source, _canRing, _canJet]] call CBA_fnc_serverEvent; [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); TRACE_4("cooking-off",_vehicle,_chanceOfFire,_delaySmoke,_detonateAfterCookoff);
[_vehicle] spawn FUNC(abandon);
LOG_1("[%1] is on fire is bailing",_vehicle);
true // Abandon vehicle
}; _vehicle call FUNC(abandon);
LOG_2("[%1] No Cook-off - Chance of fire [%2]",_vehicle,_chanceOfFire); true // return
false

View File

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

View File

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

View File

@ -1,37 +1,59 @@
#include "..\script_component.hpp" #include "..\script_component.hpp"
/* /*
* Author: tcvm * Author: tcvm, johnb43
* Checks hitpoint damage and determines if a vehicle should cookoff. * Checks hitpoint damage and determines if a vehicle should detonate its ammo.
* *
* Arguments: * Arguments:
* 0: The vehicle <OBJECT> * 0: Vehicle <OBJECT>
* 1: Chance of detonation <NUMBER> * 1: Chance of detonation <NUMBER>
* 2: Vehicle ammo array <ARRAY> * 2: If the vehicle should be knocked out <BOOL>
* 3: How much explosive ammo is inside vehicle <NUMBER> * 3: If the crew should be injured <BOOL>
* 4: How much non-explosive ammo inside vehicle <NUMBER> * 4: Source of damage <OBJECT>
* 5: Person who instigated damage <OBJECT> (default: objNull) * 5: Person who caused damage <OBJECT>
* *
* Return Value: * Return Value:
* Detonated <BOOL> * None
* *
* Example: * Example:
* [tank2, 0.5] call ace_vehicle_damage_fnc_handleDetonation; * [cursorObject, 0.5, true, player, player] call ace_vehicle_damage_fnc_handleDetonation
* *
* Public: No * Public: No
*/ */
params ["_vehicle", "_chanceOfDetonate", "_vehicleAmmo", "_explosiveAmmoCount", "_nonExplosiveAmmoCount", ["_injurer", objNull]]; params ["_vehicle", "_chanceToDetonate", "_knockOut", "_injureCrew", "_source", "_instigator"];
private _isKnockedOut = _explosiveAmmoCount > 0;
// Ignore if the vehicle is already detonating ammo // 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 { if (_knockOut) then {
[_vehicle, _injurer, _vehicleAmmo] call FUNC(detonate); [_vehicle, _source, _instigator] call FUNC(knockOut);
LOG_2("Detonating [%1] with a chance-to-detonate [%2]",_vehicle,_chanceOfDetonate);
_isKnockedOut
}; };
LOG_2("[%1] No Detonation - Chance of detonation [%2]",_vehicle,_chanceOfDetonate); _knockOut // return
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. * Process vehicle hit.
* *
* Arguments: * Arguments:
* 0: The vehicle <OBJECT> * 0: Vehicle <OBJECT>
* 1: The hitpoint which got hit <STRING> * 1: Hit point <STRING>
* 2: The index of what got hit <NUMBER> * 2: Hit index <NUMBER>
* 3: The damage that the new part took <NUMBER> * 3: Selection <STIRNG>
* 4: Person who hit vehicle <OBJECT> * 4: Added damage to part <NUMBER>
* 5: Damage before hit <NUMBER> * 5: Projectile <OBJECT>
* 6: Damage after hit <NUMBER> * 6: Source of damage <OBJECT>
* 7: Projectile <OBJECT> * 7: Person who caused damage <OBJECT>
* 8: Selection that got hit <STIRNG>
* *
* Return Value: * Return Value:
* Whether or not to continue handling last frame's damage * Whether or not to continue handling last frame's damage <BOOL>
* *
* Example: * 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 * Public: No
*/ */
params["_vehicle", "_hitPoint", "_hitIndex", "_injurer", "_oldDamage", "_newDamage", "_projectile", "_selection"]; params ["_vehicle", "_hitPoint", "_hitIndex", "_selection", "_addedDamage", "_projectile", "_source", "_instigator"];
TRACE_6("handleVehicleDamage",_vehicle,_hitPoint,_hitIndex,_injurer,_oldDamage,_newDamage); TRACE_8("handleVehicleDamage",_vehicle,_hitPoint,_hitIndex,_selection,_addedDamage,_projectile,_source,_instigator);
if !(alive _vehicle) exitWith {
private _eventHandler = _vehicle getVariable[QGVAR(handleDamage), nil]; if (!alive _vehicle) exitWith {
if !(isNil "_eventHandler") then { private _handleDamageEH = _vehicle getVariable [QGVAR(handleDamage), nil];
_vehicle removeEventHandler ["HandleDamage", _eventHandler];
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; _hitPoint = toLowerANSI _hitPoint;
private _hitpointHash = _vehicle getVariable [QGVAR(hitpointHash), []]; private _hitPointHash = GVAR(vehicleClassesHitPointHash) getOrDefault [typeOf _vehicle, createHashMap];
private _type = if (_hitpointHash isEqualTo []) then { private _type = (_hitPointHash getOrDefault [_hitPoint, []]) select 0;
"exit"
} else {
([_hitpointHash, _hitPoint] call CBA_fnc_hashGet) select 0
};
if (isNil "_type") then { // Generic structural damage will be transfered into hull damage for simulation's sake
_type = "exit";
};
// generic structural damage will be transfered into hull damage for simulation's sake
private _structural = false; private _structural = false;
if (_selection isEqualTo "") then {
if (_selection == "") then {
_type = "hull"; _type = "hull";
_hitPoint = "hithull"; _hitPoint = "hithull";
_structural = true; _structural = true;
TRACE_1("structural damage",_selection); 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 // Ignore multiple hits at the same time
private _ignoreHit = false; private _ignoreHit = false;
private _ignoreBailCheck = false; private _ignoreBailCheck = false;
private _multHit = _vehicle getVariable [QGVAR(hitTime), nil]; private _multHit = _vehicle getVariable [QGVAR(hitTime), nil];
if (isNil "_multHit") then { if (isNil "_multHit") then {
_vehicle setVariable[QGVAR(hitTime), [CBA_missionTime, _injurer, [_hitPoint]]]; _vehicle setVariable [QGVAR(hitTime), [CBA_missionTime, _source, [_hitPoint]]];
} else { } else {
private _hitPointInOldArray = _hitPoint in (_multHit select 2); 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 { if (_hitPointInOldArray && _withinTime) then {
_ignoreHit = true; _ignoreHit = true;
} else { } else {
// If the hitpoint isnt in the old array then that means that the time expired and a new array should be generated // 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; private _oldHitPoints = _multHit select 2;
_oldHitPoints pushBack _hitPoint; _oldHitPoints pushBack _hitPoint;
_vehicle setVariable [QGVAR(hitTime), [CBA_missionTime, _injurer, _oldHitPoints]]; _vehicle setVariable [QGVAR(hitTime), [CBA_missionTime, _source, _oldHitPoints]];
_ignoreBailCheck = true; _ignoreBailCheck = true;
} else { } else {
_vehicle setVariable [QGVAR(hitTime), [CBA_missionTime, _injurer, [_hitPoint]]]; _vehicle setVariable [QGVAR(hitTime), [CBA_missionTime, _source, [_hitPoint]]];
}; };
}; };
}; };
if (_ignoreHit && !_structural) exitWith { 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 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]; if !([_vehicle, _hitPoint, _hitIndex, _addedDamage, _projectile, _source, _instigator] call FUNC(processHit)) exitWith {false};
private _canShoot = _vehicle getVariable[QGVAR(canShoot), true];
if !(_ignoreBailCheck) then { if (!_ignoreBailCheck) then {
[_vehicle, _canMove, _canShoot] call FUNC(handleBail); _vehicle call FUNC(handleBail);
}; };
true true

View File

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

View File

@ -1,133 +1,132 @@
#include "..\script_component.hpp" #include "..\script_component.hpp"
/* /*
* Author: tcvm * Author: tcvm, johnb43
* Process hit by projectile against vehicle and apply appropiate damage to part. * Process hit by projectile against vehicle and apply appropiate damage to part.
* *
* Arguments: * Arguments:
* 0: The vehicle <OBJECT> * 0: Vehicle <OBJECT>
* 1: Projectile that hit <OBJECT> * 1: Hit point <STRING>
* 2: Hit index of potentially damaged part <NUMBER> * 2: Hit index <NUMBER>
* 3: New damage done to part <NUMBER> * 3: Added damage to part <NUMBER>
* 4: Information about hitpoint <ARRAY> * 4: Projectile <OBJECT>
* 5: Person who caused damage <OBJECT> * 5: Source of damage <OBJECT>
* 6: Person who caused damage <OBJECT>
* *
* Return Value: * Return Value:
* None * Whether or not to continue handling last frame's damage <BOOL>
* *
* Example: * 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 * Public: No
*/ */
params ["_vehicle", "_projectile", "_hitIndex", "_newDamage", "_hitpointData", "_injurer"]; params ["_vehicle", "_hitPoint", "_hitIndex", "_addedDamage", "_projectile", "_source", "_instigator"];
_hitpointData params ["_hitArea", "_hitpointConfig", "_hitpointName"]; TRACE_7("processHit",_vehicle,_hitPoint,_hitIndex,_addedDamage,_projectile,_source,_instigator);
private _return = true; _addedDamage = abs _addedDamage;
if (_newDamage < 0) then {
_newDamage = -_newDamage;
};
private _currentPartDamage = _vehicle getHitIndex _hitIndex; 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 // 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); } forEach (crew _vehicle);
_vehicle setDamage 1;
_return = false; // setDamage triggers "Killed" EH in cookoff, which starts ammo cook-off
_return [QGVAR(setDamage), [_vehicle, [1, true, _source, _instigator]]] call CBA_fnc_serverEvent;
false
}; };
private _projectileConfig = _projectile call CBA_fnc_getObjectConfig; private _projectileConfig = _projectile call CBA_fnc_getObjectConfig;
private _warheadTypeStr = getText (_projectileConfig >> "warheadName"); private _warheadTypeStr = toLowerANSI 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
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; private _incendiary = [_projectileConfig >> QGVAR(incendiary), "NUMBER", [0.3, 0.1, 1, 1, 0] select _warheadType] call CBA_fnc_getConfigEntry;
if (_minDamage < 0) then {
_minDamage = -_minDamage;
};
private _ammoEffectiveness = 0; private _hitPointHash = GVAR(vehicleClassesHitPointHash) getOrDefault [typeOf _vehicle, createHashMap];
private _projectileExplosive = [_projectileConfig >> "explosive", "NUMBER", 0] call CBA_fnc_getConfigEntry; (_hitPointHash getOrDefault [_hitPoint, []]) params ["_hitArea", "_minDamage"];
private _indirectHit = [_projectileConfig >> "indirectHit", "NUMBER", 0] call CBA_fnc_getConfigEntry;
if (_warheadType isEqualTo WARHEAD_TYPE_AP) then { private _projectileExplosive = getNumber (_projectileConfig >> "explosive");
// change damage based on projectile speed (doesn't do this in vanilla ARMA believe it or not) private _indirectHit = getNumber (_projectileConfig >> "indirectHit");
if !(isNull _injurer) then {
private _airFriction = [_projectileConfig >> "airFriction", "NUMBER", 0] call CBA_fnc_getConfigEntry; if (_warheadType == WARHEAD_TYPE_AP) then {
private _distance = _injurer distance _vehicle; // Change damage based on projectile speed (doesn't do this in vanilla Arma believe it or not)
_newDamage = (1 - _projectileExplosive) * _newDamage * exp(_airFriction * _distance); if (!isNull _source) then {
private _airFriction = getNumber (_projectileConfig >> "airFriction");
private _distance = _source distance _vehicle;
_addedDamage = (1 - _projectileExplosive) * _addedDamage * exp (_airFriction * _distance);
}; };
}; };
private _penChance = 1; private _penChance = 1;
if (_newDamage < _minDamage) then {
_penChance = _newDamage / _minDamage; // Added damage can't be 0, so don't need to worry about 0 division here
TRACE_5("minimum damage modifying hit",_newDamage,_penChance,abs _minDamage,_warheadTypeStr,_hitArea); if (_addedDamage < _minDamage) then {
_penChance = _addedDamage / _minDamage;
TRACE_5("minimum damage modifying hit",_addedDamage,_penChance,_minDamage,_warheadTypeStr,_hitArea);
}; };
if (_penChance < random 1) exitWith { if (_penChance < random 1) exitWith {
TRACE_1("didn't penetrate",_penChance); TRACE_1("didn't penetrate",_penChance);
_return
true
}; };
if (_minDamage == 0) then { if (_minDamage == 0) then {
_minDamage = 1; _minDamage = 1;
}; };
if (_warheadType isEqualTo WARHEAD_TYPE_HE) then { if (_warheadType == WARHEAD_TYPE_HE) then {
private _modifiedIndirectHit = _indirectHit / 100; private _modifiedIndirectHit = _indirectHit / 100;
if (_newDamage > _modifiedIndirectHit) then {
_newDamage = _newDamage / 2; if (_addedDamage > _modifiedIndirectHit) then {
}; _addedDamage = _addedDamage / 2;
_newDamage = (_newDamage * (_newDamage / _modifiedIndirectHit)) min _newDamage;
}; };
_ammoEffectiveness = if (_warheadType isEqualTo WARHEAD_TYPE_AP) then { _addedDamage = (_addedDamage * (_addedDamage / _modifiedIndirectHit)) min _addedDamage;
0.15 max _newDamage };
private _ammoEffectiveness = if (_warheadType == WARHEAD_TYPE_AP) then {
0.15 max _addedDamage
} else { } else {
if (_warheadType isEqualTo WARHEAD_TYPE_HE) then { if (_warheadType == WARHEAD_TYPE_HE) then {
(_newDamage / (_minDamage + (_indirectHit / 100)) * 0.2) (_addedDamage / (_minDamage + (_indirectHit / 100)) * 0.2)
} else { } 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; _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 { 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 _chanceOfDetonation = 0;
private _explosiveAmmoCount = 0; private _explosiveAmmoCount = 0;
private _nonExplosiveAmmoCount = 0;
if ((_currentVehicleAmmo select 0) isNotEqualTo []) then { if (_magazines isNotEqualTo []) then {
private _magConfig = configFile >> "CfgMagazines"; private _magConfig = configFile >> "CfgMagazines";
private _ammoConfig = configFile >> "CfgAmmo"; private _ammoConfig = configFile >> "CfgAmmo";
private _countOfExplodableAmmo = 0; private _countOfExplodableAmmo = 0;
{ {
_x params ["_magazineClassname", "_currentAmmoCount"]; _x params ["_magazineClassname", "_currentAmmoCount"];
private _initialAmmoCount = getNumber (_magConfig >> _magazineClassname >> "count"); private _initialAmmoCount = getNumber (_magConfig >> _magazineClassname >> "count");
_chanceOfDetonation = _chanceOfDetonation + (_currentAmmoCount / _initialAmmoCount); _chanceOfDetonation = _chanceOfDetonation + (_currentAmmoCount / _initialAmmoCount);
_countOfExplodableAmmo = _countOfExplodableAmmo + 1; _countOfExplodableAmmo = _countOfExplodableAmmo + 1;
@ -135,215 +134,241 @@ if ((_currentVehicleAmmo select 0) isNotEqualTo []) then {
private _ammoClassname = getText (_magConfig >> _magazineClassname >> "ammo"); private _ammoClassname = getText (_magConfig >> _magazineClassname >> "ammo");
private _explosive = getNumber (_ammoConfig >> _ammoClassname >> "explosive"); private _explosive = getNumber (_ammoConfig >> _ammoClassname >> "explosive");
private _hit = getNumber (_ammoConfig >> _ammoClassname >> "hit"); private _hit = getNumber (_ammoConfig >> _ammoClassname >> "hit");
if (_explosive > 0.5 || _hit > 50) then {
if (_explosive > 0.5 || {_hit > 50}) then {
_explosiveAmmoCount = _explosiveAmmoCount + 1; _explosiveAmmoCount = _explosiveAmmoCount + 1;
} else {
_nonExplosiveAmmoCount = _nonExplosiveAmmoCount + 1;
}; };
} forEach (_currentVehicleAmmo select 0); } forEach _magazines;
if (_countOfExplodableAmmo != 0) then { if (_countOfExplodableAmmo != 0) then {
_chanceOfDetonation = _chanceOfDetonation / _countOfExplodableAmmo; _chanceOfDetonation = _chanceOfDetonation / _countOfExplodableAmmo;
}; };
}; };
private _chanceToDetonate = 0;
private _chanceOfFire = 0; private _return = true;
private _currentFuel = fuel _vehicle;
private _vehicleConfig = _vehicle call CBA_fnc_getObjectConfig;
switch (_hitArea) do { switch (_hitArea) do {
case "engine": { case "engine": {
_chanceToDetonate = ([_vehicleConfig >> QGVAR(engineDetonationProb), "NUMBER", 0] call CBA_fnc_getConfigEntry) * _incendiary * _currentFuel * _penChance; private _vehicleConfig = configOf _vehicle;
_chanceOfFire = ([_vehicleConfig >> QGVAR(engineFireProb), "NUMBER", 0] call CBA_fnc_getConfigEntry) * _incendiary * _currentFuel * _penChance; private _currentFuel = fuel _vehicle;
private _chanceToDetonate = getNumber (_vehicleConfig >> QGVAR(engineDetonationProb)) * _incendiary * _currentFuel * _penChance;
private _cookoffIntensity = 4 * _currentFuel; TRACE_4("hit engine",_chanceToDetonate,_incendiary,_chanceOfDetonation,_currentFuel);
TRACE_6("hit engine",_chanceToDetonate,_chanceOfFire,_incendiary,_chanceOfDetonation,_currentFuel,_cookoffIntensity);
if (_isCar) then { // Knock out and detonate vehicle if necessary
_chanceOfFire = 0; // no cookoff for cars 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 { if (_newPartDamage == 0.9) then {
[_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]; _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": { case "hull": {
_chanceToDetonate = ([_vehicleConfig >> QGVAR(hullDetonationProb), "NUMBER", 0] call CBA_fnc_getConfigEntry) * _incendiary * ((_chanceOfDetonation + _currentFuel) / 2) * _penChance; private _vehicleConfig = configOf _vehicle;
_chanceOfFire = ([_vehicleConfig >> QGVAR(hullFireProb), "NUMBER", 0] call CBA_fnc_getConfigEntry) * _incendiary * ((_chanceOfDetonation + _currentFuel) / 2) * _penChance; private _currentFuel = fuel _vehicle;
private _chanceToDetonate = getNumber (_vehicleConfig >> QGVAR(hullDetonationProb)) * _incendiary * ((_chanceOfDetonation + _currentFuel) / 2) * _penChance;
private _cookoffIntensity = 1.5 + (_explosiveAmmoCount * _chanceOfFire); TRACE_4("hit hull",_chanceToDetonate,_incendiary,_chanceOfDetonation,_currentFuel);
TRACE_6("hit hull",_chanceToDetonate,_chanceOfFire,_incendiary,_chanceOfDetonation,_currentFuel,_cookoffIntensity);
if (_isCar) then { // Knock out and detonate vehicle if necessary
_chanceOfFire = 0; // no cookoff for cars 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 // 25% chance of jamming turret - 25% of mobility kill - 25% of both - 75% chance of critical hull damage
private _rand = random 1; private _rand = random 1;
TRACE_2("rolling hull damage",_ammoEffectiveness,_rand); TRACE_2("rolling hull damage",_ammoEffectiveness,_rand);
private _partKill = []; private _partKill = [];
if (_ammoEffectiveness > _rand) then { if (_ammoEffectiveness > _rand) then {
_rand = random 1; _rand = random 1;
TRACE_2("damaged hull part",_ammoEffectiveness,_rand); TRACE_2("damaged hull part",_ammoEffectiveness,_rand);
switch (true) do { switch (true) do {
case (_rand < 0.25): { case (_rand < 0.25): {
[_vehicle, _hitIndex, _hitpointName, 0.89 * _penChance] call FUNC(addDamage); [_vehicle, _hitPoint, _hitIndex, 0.89 * _penChance, _source, _instigator] call FUNC(setDamage);
// iterate through all keys and find appropriate turret
[_hash, { // Iterate through all keys and find appropriate turret
if (_value#0 isEqualTo "turret") then { {
_partKill pushBack _key; if ((_y select 0) == "turret") then {
_partKill pushBack _x;
}; };
}] call CBA_fnc_hashEachPair; } forEach _hitPointHash;
_vehicle setVariable [QGVAR(canShoot), false]; _vehicle setVariable [QGVAR(canShoot), false];
}; };
case (_rand < 0.5): { case (_rand < 0.5): {
[_vehicle, _hitIndex, _hitpointName, 0.89 * _penChance] call FUNC(addDamage); [_vehicle, _hitPoint, _hitIndex, 0.89 * _penChance, _source, _instigator] call FUNC(setDamage);
_partKill = _partKill + ENGINE_HITPOINTS#0;
_partKill append (ENGINE_HITPOINTS select 0);
if !(_vehicle isKindOf "Wheeled_APC_F") then { if !(_vehicle isKindOf "Wheeled_APC_F") then {
_partKill = _partKill + TRACK_HITPOINTS#0; _partKill append (TRACK_HITPOINTS select 0);
}; };
_vehicle setVariable [QGVAR(canMove), false]; _vehicle setVariable [QGVAR(canMove), false];
}; };
case (_rand < 0.75): { case (_rand < 0.75): {
[_vehicle, _hitIndex, _hitpointName, 0.89 * _penChance] call FUNC(addDamage); [_vehicle, _hitPoint, _hitIndex, 0.89 * _penChance, _source, _instigator] call FUNC(setDamage);
_partKill = _partKill + ENGINE_HITPOINTS#0;
_partKill append (ENGINE_HITPOINTS select 0);
if !(_vehicle isKindOf "Wheeled_APC_F") then { 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 // Iterate through all keys and find appropriate turret
[_hash, { {
if (_value#0 isEqualTo "turret") then { if ((_y select 0) == "turret") then {
_partKill pushBack _key; _partKill pushBack _x;
}; };
}] call CBA_fnc_hashEachPair; } forEach _hitPointHash;
_vehicle setVariable [QGVAR(canMove), false]; _vehicle setVariable [QGVAR(canMove), false];
_vehicle setVariable [QGVAR(canShoot), false]; _vehicle setVariable [QGVAR(canShoot), false];
}; };
default{};
}; };
}; };
{ {
[_vehicle, _x, -1, _penChance, _source, _instigator] call FUNC(setDamage);
TRACE_1("doing damage to hitpoint",_x); TRACE_1("doing damage to hitpoint",_x);
[_vehicle, -1, _x, 1 * _penChance] call FUNC(addDamage);
} forEach _partKill; } 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": { case "turret": {
_chanceToDetonate = ([_vehicleConfig >> QGVAR(turretDetonationProb), "NUMBER", 0] call CBA_fnc_getConfigEntry) * _incendiary * _chanceOfDetonation * _penChance; private _vehicleConfig = configOf _vehicle;
_chanceOfFire = ([_vehicleConfig >> QGVAR(turretFireProb), "NUMBER", 0] call CBA_fnc_getConfigEntry) * _incendiary * _chanceOfDetonation * _penChance; private _chanceToDetonate = getNumber (_vehicleConfig >> QGVAR(turretDetonationProb)) * _incendiary * _chanceOfDetonation * _penChance;
private _cookoffIntensity = _explosiveAmmoCount * _chanceOfFire; TRACE_3("hit turret",_chanceToDetonate,_incendiary,_chanceOfDetonation);
TRACE_6("hit turret",_chanceToDetonate,_chanceOfFire,_incendiary,_chanceOfDetonation,_currentFuel,_cookoffIntensity);
if (_isCar) then { if ([_vehicle, _chanceToDetonate, _explosiveAmmoCount > 0, _totalAmmo > 0, _source, _instigator] call FUNC(handleDetonation)) exitWith {};
_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 { if (0.8 * _ammoEffectiveness > random 1) then {
TRACE_1("damaged turret",_ammoEffectiveness * 0.8); 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 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": { case "gun": {
TRACE_5("hit gun",_chanceToDetonate,_chanceOfFire,_incendiary,_chanceOfDetonation,_currentFuel); TRACE_2("hit gun",_addedDamage,_minDamage);
if (0.8 * _ammoEffectiveness > random 1) then { if (0.8 * _ammoEffectiveness > random 1) then {
TRACE_1("damaged gun",_ammoEffectiveness * 0.8); 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]; _vehicle setVariable [QGVAR(canShoot), false];
}; };
}; };
case "track": { case "track": {
private _damage = (0.1 max (0.1 * _newDamage / _minDamage)) min 1; private _damage = (0.1 max (0.1 * _addedDamage / _minDamage)) min 1;
[_vehicle, _hitIndex, _hitpointName, (_currentPartDamage + _damage) * _penChance] call FUNC(addDamage);
TRACE_3("damaged track",_damage,_newDamage,_minDamage); [_vehicle, _hitPoint, _hitIndex, (_currentPartDamage + _damage) * _penChance] call FUNC(setDamage);
TRACE_3("damaged track",_damage,_addedDamage,_minDamage);
if ((_vehicle getHitIndex _hitIndex) >= 1) then { if ((_vehicle getHitIndex _hitIndex) >= 1) then {
_vehicle setVariable [QGVAR(canMove), false]; _vehicle setVariable [QGVAR(canMove), false];
}; };
}; };
case "wheel": { case "wheel": {
[_vehicle, _hitIndex, _hitpointName, (_currentPartDamage + _newDamage) * _penChance] call FUNC(addDamage); [_vehicle, _hitPoint, _hitIndex, (_currentPartDamage + _addedDamage) * _penChance, _source, _instigator] call FUNC(setDamage);
TRACE_1("damaged wheel",_newDamage);
TRACE_1("damaged wheel",_addedDamage);
}; };
case "fuel": { 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; private _cookoffIntensity = _currentFuel * 5;
TRACE_2("damaged fuel",_chanceOfFire,_cookoffIntensity);
if (_isCar) then { TRACE_4("damaged fuel",_chanceOfFire,_incendiary,_cookoffIntensity,_currentFuel);
_chanceOfFire = 0; // no cookoff for cars
};
[_vehicle, _chanceOfFire, _cookoffIntensity, _injurer, "", false] call FUNC(handleCookoff); [_vehicle, _chanceOfFire, _cookoffIntensity, _source, _instigator, "", false, 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": { case "slat": {
TRACE_2("hit slat",_warheadType,_warheadTypeStr); TRACE_2("hit slat",_warheadType,_warheadTypeStr);
// incredibly small chance of AP destroying SLAT
// 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 { 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; private _currentDamage = _vehicle getHitIndex _hitIndex;
TRACE_3("damaged slat",_warheadType,_warheadTypeStr,_currentDamage); TRACE_3("damaged slat",_warheadType,_warheadTypeStr,_currentDamage);
if (_warheadType in [WARHEAD_TYPE_HEAT, WARHEAD_TYPE_TANDEM, WARHEAD_TYPE_AP]) then { 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 { } 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; _return = false;
}; };
}; };
}; };
case "era": { case "era": {
TRACE_2("hit era",_warheadType,_warheadTypeStr); TRACE_2("hit era",_warheadType,_warheadTypeStr);
if (_warheadType in [WARHEAD_TYPE_AP, WARHEAD_TYPE_HEAT, WARHEAD_TYPE_TANDEM] || {0.05 > random 1}) then { if (_warheadType in [WARHEAD_TYPE_AP, WARHEAD_TYPE_HEAT, WARHEAD_TYPE_TANDEM] || {0.05 > random 1}) then {
private _currentDamage = _vehicle getHitIndex _hitIndex; 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 TRACE_3("damaged era",_warheadType,_warheadTypeStr,_currentDamage);
if (_currentDamage < 1 && _warheadType isEqualTo WARHEAD_TYPE_HEAT) then {
[_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; _return = false;
}; };
}; };
}; };
default { default {
TRACE_1("hit unknown hitpoint??",_hitArea); TRACE_1("hit unknown hitpoint??",_hitArea);
} };
}; };
_return _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)], [ELSTRING(common,Enabled), LSTRING(setting_description)],
LSTRING(category_displayName), LSTRING(category_displayName),
false, // default value false,
true, // isGlobal 1,
{[QGVAR(enabled), _this] call EFUNC(common,cbaSettings_settingChanged)}, {[QGVAR(enabled), _this] call EFUNC(common,cbaSettings_settingChanged)},
true // Needs mission restart 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(carDamage_setting_enable), LSTRING(carDamage_setting_description)],
LSTRING(category_displayName), LSTRING(category_displayName),
false, // default value false,
true, // isGlobal 1,
{[QGVAR(enableCarDamage), _this] call EFUNC(common,cbaSettings_settingChanged)}, {[QGVAR(enableCarDamage), _this] call EFUNC(common,cbaSettings_settingChanged)},
true // Needs mission restart true // Needs mission restart
] call CBA_settings_fnc_init; ] call CBA_fnc_addSetting;

View File

@ -13,15 +13,17 @@
#define FUEL_HITPOINTS [["hitfuel"], "fuel"] #define FUEL_HITPOINTS [["hitfuel"], "fuel"]
#define ALL_HITPOINTS [ENGINE_HITPOINTS, HULL_HITPOINTS, TRACK_HITPOINTS, WHEEL_HITPOINTS, FUEL_HITPOINTS] #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_HE 0
#define WARHEAD_TYPE_AP 1 #define WARHEAD_TYPE_AP 1
#define WARHEAD_TYPE_HEAT 2 #define WARHEAD_TYPE_HEAT 2
#define WARHEAD_TYPE_TANDEM 3 #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 EJECT_IF_DESTROYED_VEHICLES ["Boat_Transport_02_base_F", "Rubber_duck_base_F"]
#define CREATE_INCENDIARY_AMMO(ammo,base,inc)\
#define CREATE_INCENDIARY_AMMO(ammo,base,inc) class ammo: base { GVAR(incendiary) = inc; } class ammo: base {\
GVAR(incendiary) = inc;\
}

View File

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

View File

@ -19,7 +19,7 @@ Removes hit-point based damage on armoured vehicles.
### 1.1 Differences from vanilla ### 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. When you are hit, a calculation takes place and determines which people inside the vehicle should get hurt.
### 1.2 How is damage calculated ### 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 cook-off [N]
- Can injure occupants [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) 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` #### 1.1.10 `ace_vehicle_damage_slatHitpoints`
@ -89,9 +89,9 @@ Default: {}
#### 1.1.12 `ace_vehicle_damage_turret` #### 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 ### 1.2 Defined Hitpoints