27 [
Attribute(
"3.5",
desc:
"Max distance of hitzone, which should receive damage, from contact point.",
category:
"Collision Damage")]
28 protected float m_fMaxSharedDamageDistance;
48 [
Attribute(
"30",
desc:
"Speed in km/h over which will occupants be dealt damage in collision",
category:
"Collision Damage")]
51 [
Attribute(
"100",
desc:
"Speed in km/h over which will occupants die in collision",
category:
"Collision Damage")]
54 [
Attribute(
"0 0 0",
desc:
"Position for frontal impact calculation",
category:
"Collision Damage",
params:
"inf inf 0 purpose=coords space=entity coordsVar=m_vFrontalImpact")]
57 [
Attribute(
"0",
desc:
"Frontal impact damage needed to destroy this vehicle\nTakes into account current damage multipliers\nUse context menu on Damage Manager component to compute this value.\n[hp]",
category:
"Collision Damage")]
58 protected float m_fVehicleDestroyDamage;
61 protected float m_fVehicleDamageSpeedThreshold;
64 protected float m_fVehicleSpeedDestroy;
66 [
Attribute(
"200",
desc:
"Explosion damage value which, if exceeded, may cause passengers to be ejected from the vehicle",
category:
"Passenger Ejection")]
69 [
Attribute(
"0.5",
desc:
"Chance victim is ejected when explosion damage is sufficient to pass the m_iMinExplosionEjectionDamageThreshold \nLess than 0 = fully random \n1 = always",
params:
"-1 1 0.01",
category:
"Passenger Ejection")]
72 [
Attribute(
"70",
desc:
"Collision damage value which, if exceeded, may cause passengers to be ejected from the vehicle",
category:
"Passenger Ejection")]
75 [
Attribute(
"0.5",
desc:
"Chance victim is ejected when collisiondamage is sufficient to pass the m_iMinCollisionEjectionDamageThreshold \nLess than 0 = fully random \n1 = always",
params:
"-1 1 0.01",
category:
"Passenger Ejection")]
83 protected static const float APPROXIMATE_CHARACTER_LETHAL_DAMAGE = 150;
89 return m_fMaxSharedDamageDistance;
112 if (momentumVehicleDestroy == 0)
131 if (momentumOccupantsDeath == 0)
158 return m_fVehicleSpeedDestroy;
165 return m_fVehicleDamageSpeedThreshold;
289 [
Attribute(defvalue:
"0.7",
desc:
"Engine efficiency at which it is considered to be malfunctioning\n[x * 100%]",
category:
"Vehicle Damage")]
290 protected float m_fEngineMalfunctioningThreshold;
292 [
Attribute(defvalue:
"VehicleFireState",
desc:
"Vehicle parts fire state signal name",
category:
"Secondary damage")]
295 [
Attribute(defvalue:
"FuelTankFireState",
desc:
"Fuel tank fire signal name",
category:
"Secondary damage")]
298 [
Attribute(defvalue:
"SuppliesFireState",
desc:
"Supplies fire signal name",
category:
"Secondary damage")]
302 protected float m_fFuelTankFireDamageRate;
305 protected float m_fSuppliesFireDamageRate;
308 protected float m_fSecondaryFireDamageDelay;
314 protected static ref
ScriptInvoker s_OnVehicleDamageStateChanged;
351 [
RplProp(onRplName:
"OnVehicleFireStateChanged")]
354 [
RplProp(onRplName:
"OnFuelTankFireStateChanged")]
357 [
RplProp(onRplName:
"OnSuppliesFireStateChanged")]
369#ifdef VEHICLE_DAMAGE_DEBUG
370 protected ref array<ref Shape> m_aDebugShapes = {};
372#ifdef VEHICLE_DEBUG_OTHER
373 protected ref array<ref Shape> m_aDebugShapes = {};
380 protected ref array<ref Tuple2<vector, bool>> m_aLocalCollisions = {};
388 protected const float HACK_DAMAGE_CALCULATION_REDUCTION = 0.9;
402 SCR_VehicleDamageManagerComponentClass prefabData =
GetPrefabData();
406 return prefabData.GetTopMultiplier();
413 SCR_VehicleDamageManagerComponentClass prefabData =
GetPrefabData();
417 return prefabData.GetRightMultiplier();
424 SCR_VehicleDamageManagerComponentClass prefabData =
GetPrefabData();
428 return prefabData.GetLeftMultiplier();
435 SCR_VehicleDamageManagerComponentClass prefabData =
GetPrefabData();
439 return prefabData.GetRearMultiplier();
446 SCR_VehicleDamageManagerComponentClass prefabData =
GetPrefabData();
450 return prefabData.GetBottomMultiplier();
457 SCR_VehicleDamageManagerComponentClass prefabData =
GetPrefabData();
461 return prefabData.GetFrontMultiplier();
468 SCR_VehicleDamageManagerComponentClass prefabData =
GetPrefabData();
472 return prefabData.GetMaxSharedDamageDistance();
479 SCR_VehicleDamageManagerComponentClass prefabData =
GetPrefabData();
483 return prefabData.GetOccupantsDamageSpeedThreshold();
490 SCR_VehicleDamageManagerComponentClass prefabData =
GetPrefabData();
494 return prefabData.GetOccupantsSpeedDeath();
501 SCR_VehicleDamageManagerComponentClass prefabData =
GetPrefabData();
505 return prefabData.GetMinCollisionEjectionDamageThreshold();
512 SCR_VehicleDamageManagerComponentClass prefabData =
GetPrefabData();
516 return prefabData.GetMinExplosionEjectionDamageThreshold();
523 SCR_VehicleDamageManagerComponentClass prefabData =
GetPrefabData();
527 return prefabData.GetCollisionDamageEjectionChance();
534 SCR_VehicleDamageManagerComponentClass prefabData =
GetPrefabData();
538 return prefabData.GetExplosionDamageEjectionChance();
545 SCR_VehicleDamageManagerComponentClass prefabData =
GetPrefabData();
549 return prefabData.GetFrontalImpact();
580 super.OnPostInit(owner);
586 m_CompartmentManager = SCR_BaseCompartmentManagerComponent.Cast(vehicle.FindComponent(SCR_BaseCompartmentManagerComponent));
607 SCR_VehicleBuoyancyComponent vehicleBuoyancy = SCR_VehicleBuoyancyComponent.Cast(vehicle.FindComponent(SCR_VehicleBuoyancyComponent));
613 RplComponent rpl = vehicle.GetRplComponent();
614 if (rpl && rpl.IsProxy())
651 float engineEfficiency;
652 bool engineFunctional;
655 float gearboxEfficiency;
656 bool gearboxFunctional;
669 if (engineHitZone.GetDamageState() !=
EDamageState.DESTROYED)
670 engineFunctional =
true;
681 if (gearboxHitZone.GetDamageState() !=
EDamageState.DESTROYED)
682 gearboxFunctional =
true;
694 if (gearboxCount > 0)
707 float movementDamage;
714 SetMovementDamage(movementDamage);
716 VehicleControllerComponent controller = VehicleControllerComponent.Cast(
m_Controller);
718 controller.SetCanMove(movementDamage < 1);
736 VehicleControllerComponent controller = VehicleControllerComponent.Cast(
m_Controller);
737 if (controller && controller.IsEngineOn())
738 controller.StopEngine(
false);
745 return m_fEngineMalfunctioningThreshold;
761 if (simulation && simulation.IsValid())
763 simulation.EngineSetPeakTorqueState(efficiency * simulation.EngineGetPeakTorque());
764 simulation.EngineSetPeakPowerState(efficiency * simulation.EngineGetPeakPower());
773 VehicleControllerComponent controller = VehicleControllerComponent.Cast(
m_Controller);
775 controller.SetCanMove(functional);
798 if (simulation && simulation.IsValid())
799 simulation.GearboxSetEfficiencyState(efficiency * simulation.GearboxGetEfficiency());
815 array<HitZone> allHitZones = {};
816 int count = GetAllHitZonesInHierarchy(allHitZones);
821 array<int> hitZoneColliderIDs = {};
822 float distancePercent;
823 float currentDistance;
824 float maxDistanceSq = maxDistance * maxDistance;
831 foreach (
HitZone hitZone : allHitZones)
833 minDistance =
float.MAX;
837 if (hitZone.GetBaseDamageMultiplier() == 0)
840 if (hitZone.GetDamageMultiplier(damageType) == 0)
843 if (hitZone.GetColliderIDs(hitZoneColliderIDs) == 0)
845 hitZoneContainer = hitZone.GetHitZoneContainer();
846 if (!hitZoneContainer || hitZoneContainer ==
this)
849 hitZoneParentEntity = hitZoneContainer.GetOwner();
850 hitZoneParentEntity.
GetBounds(mins, maxs);
852 center = mins + (maxs - mins) * 0.5;
857 foreach (
int id : hitZoneColliderIDs)
859 currentDistance =
vector.DistanceSq(
position, physics.GetGeomWorldPosition(
id));
861 if (currentDistance < minDistance)
862 minDistance = currentDistance;
866 if (minDistance > maxDistanceSq)
869 minDistance =
Math.Sqrt(minDistance);
870 distancePercent = 1 - minDistance / maxDistance;
872 hitZonesDistancePercentSum += distancePercent;
873 outHitZoneDistanceProcentageMap.Insert(hitZone, distancePercent);
874 outHitZones.Insert(hitZone);
877 return outHitZones.Count();
893 array<HitZone> hitZones = {};
895 float hitZonesDistancePercentSum;
897 if (hitZonesCount <= 0)
905 Print(
" Number of hit zones to which damage will be passed: " + hitZonesCount,
LogLevel.NORMAL);
907 float leftoverDamage = damage;
909 float currentDamagePercent;
911 float damageMultiplier;
918 foreach (
HitZone hitZone,
float distancePercent : hitZoneDistancePercentMap)
920 currentDamagePercent = distancePercent / hitZonesDistancePercentSum;
921 currentDamage = damage * currentDamagePercent;
923 hitZoneHealth = hitZone.GetHealth();
927 Print(
" hit zone name = " + hitZone.GetName(),
LogLevel.NORMAL);
931 Print(
" hit zone health = " + hitZoneHealth,
LogLevel.NORMAL);
932 Print(
" distance percent from the impact point = " + distancePercent,
LogLevel.NORMAL);
933 Print(
" percentage of the vehicle damage that can be transfered to this hit zone = " + currentDamagePercent +
" = " + distancePercent +
" / " + hitZonesDistancePercentSum,
LogLevel.NORMAL);
934 Print(
" damage that can be done to this hit zone = " + currentDamage +
" = " + damage +
" * " + currentDamagePercent,
LogLevel.NORMAL);
938 damageMultiplier = hitZone.GetDamageMultiplier(damageType) * hitZone.GetBaseDamageMultiplier();
939 if (damageMultiplier != 0)
943 Print(
" clamped damage = " + (
Math.Clamp(currentDamage, 0, (hitZoneHealth + hitZone.GetDamageReduction()) / damageMultiplier)) +
" = Clamp(" + currentDamage +
", 0, (" + hitZoneHealth +
" + " + hitZone.GetDamageReduction() +
") / " + damageMultiplier +
")",
LogLevel.NORMAL);
945 currentDamage =
Math.Clamp(currentDamage, 0, (hitZoneHealth + hitZone.GetDamageReduction()) / damageMultiplier);
957 damageManager.HandleDamage(damageContext);
959 leftoverDamage -= currentDamage;
963 Print(
" damage done to the hit zone = " + currentDamage,
LogLevel.NORMAL);
971 Print(
" damage absorbed by hit zones = " + (damage - leftoverDamage),
LogLevel.NORMAL);
973 leftoverDamage =
Math.Clamp(leftoverDamage, 0,
float.
MAX);
978 Print(
" leftoverDamage returned to the hull = " + leftoverDamage,
LogLevel.NORMAL);
979 Print(
" =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=",
LogLevel.NORMAL);
982 return leftoverDamage;
1008 vector bboxCenter = (maxs - mins) * 0.5 + mins;
1011 contact = contact - bboxCenter;
1015 vector normalXZA, normalXZB, normalYZA, normalYZB, normalXYA, normalXYB;
1016 float distanceXZA, distanceXZB, distanceYZA, distanceYZB, distanceXYA, distanceXYB;
1018 normalYZA =
Vector(0, mins[2] - maxs[2], maxs[1] - mins[1]).Normalized();
1019 distanceYZA =
vector.Dot(normalYZA,
Vector(0, mins[1], mins[2]));
1021 normalYZB =
Vector(0, mins[2] - maxs[2], mins[1] - maxs[1]).Normalized();
1022 distanceYZB =
vector.Dot(normalYZB,
Vector(0, mins[1], mins[2]));
1024 normalXZA =
Vector(maxs[2] - mins[2], 0, mins[0] - maxs[0]).Normalized();
1025 distanceXZA =
vector.Dot(normalXZA,
Vector(mins[0], 0, mins[2]));
1027 normalXZB =
Vector(mins[2] - maxs[2], 0, mins[0] - maxs[0]).Normalized();
1028 distanceXZB =
vector.Dot(normalXZB,
Vector(mins[0], 0, mins[2]));
1030 normalXYA =
Vector(maxs[1] - mins[1], mins[0] - maxs[0], 0).Normalized();
1031 distanceXYA =
vector.Dot(normalXYA,
Vector(mins[0], mins[1], 0));
1033 normalXYB =
Vector(mins[1] - maxs[1], mins[0] - maxs[0], 0).Normalized();
1034 distanceXYB =
vector.Dot(normalXYB,
Vector(mins[0], mins[1], 0));
1036#ifdef VEHICLE_DAMAGE_DEBUG
1039 m_aDebugShapes.Clear();
1047 m_aDebugShapes.Insert(
Shape.CreateLines(
ARGB(255, 255, 0, 0),
ShapeFlags.NOZBUFFER, p, 2));
1050 m_aDebugShapes.Insert(
Shape.CreateLines(
ARGB(255, 0, 255, 0),
ShapeFlags.NOZBUFFER, p, 2));
1053 m_aDebugShapes.Insert(
Shape.CreateLines(
ARGB(255, 0, 0, 255),
ShapeFlags.NOZBUFFER, p, 2));
1056 m_aDebugShapes.Insert(
Shape.CreateLines(
ARGB(255, 255, 0, 255),
ShapeFlags.NOZBUFFER, p, 2));
1059 m_aDebugShapes.Insert(
Shape.CreateLines(
ARGB(255, 0, 255, 255),
ShapeFlags.NOZBUFFER, p, 2));
1065 m_aDebugShapes.Insert(
Shape.CreateLines(
ARGB(255, 255, 155, 255),
ShapeFlags.NOZBUFFER, p, 2));
1067#ifdef VEHICLE_DEBUG_OTHER
1070 m_aDebugShapes.Clear();
1073 vector globalMins, globalMaxs;
1102 p[1][1] = globalMaxs[1];
1103 m_aDebugShapes.Insert(
Shape.CreateLines(
ARGB(255, 255, 0, 0),
ShapeFlags.NOZBUFFER, p, 2));
1106 p[0][0] = globalMaxs[0];
1108 p[1][1] = globalMaxs[1];
1109 m_aDebugShapes.Insert(
Shape.CreateLines(
ARGB(255, 255, 0, 0),
ShapeFlags.NOZBUFFER, p, 2));
1113 p[1][1] = globalMins[1];
1114 m_aDebugShapes.Insert(
Shape.CreateLines(
ARGB(255, 255, 0, 0),
ShapeFlags.NOZBUFFER, p, 2));
1117 p[0][0] = globalMins[0];
1119 p[1][1] = globalMins[1];
1120 m_aDebugShapes.Insert(
Shape.CreateLines(
ARGB(255, 255, 0, 0),
ShapeFlags.NOZBUFFER, p, 2));
1123 Math3D.MatrixIdentity4(mat);
1126 p[0][1] = maxs[1] * 0.5;
1129 m_aDebugShapes.Insert(shape);
1131 shape.SetMatrix(mat);
1136 p[1][1] = p[1][1] * 0.5;
1138 m_aDebugShapes.Insert(shape);
1139 shape.SetMatrix(mat);
1166 bool XZA, XZB, YZA, YZB, XYA, XYB;
1167 XZA = (
vector.Dot(contact, normalXZA) > 0) - distanceXZA;
1168 XZB = (
vector.Dot(contact, normalXZB) > 0) - distanceXZB;
1169 YZA = (
vector.Dot(contact, normalYZA) > 0) - distanceYZA;
1170 YZB = (
vector.Dot(contact, normalYZB) > 0) - distanceYZB;
1171 XYA = (
vector.Dot(contact, normalXYA) > 0) - distanceXYA;
1172 XYB = (
vector.Dot(contact, normalXYB) > 0) - distanceXYB;
1189 if (XZA && !XZB && XYA && !XYB)
1193 if (!XZA && XZB && !XYA && XYB)
1197 if (YZA && YZB && XYA && XYB)
1201 if (!YZA && !YZB && !XYA && !XYB)
1205 if (!XZA && !XZB && YZA && !YZB)
1209 if (XZA && XZB && !YZA && YZB)
1218 float CalculateCriticalCollisionDamage()
1221 array<HitZone> hitZones = {};
1224 float procentageSum;
1230 HitZone defaultHitZone = GetDefaultHitZone();
1234 damage = damage * procentageSum / count;
1236 float defaultHitZoneDamageMultipliers = defaultHitZone.GetBaseDamageMultiplier() * defaultHitZone.GetDamageMultiplier(damageType);
1237 if (defaultHitZoneDamageMultipliers == 0)
1240 float defaultHitZoneMaxDamage = (defaultHitZone.GetMaxHealth() + defaultHitZone.GetDamageReduction()) / defaultHitZoneDamageMultipliers;
1241 if (defaultHitZoneMaxDamage < defaultHitZone.GetDamageThreshold())
1242 defaultHitZoneMaxDamage += defaultHitZone.GetDamageThreshold();
1244 if (defaultHitZoneMaxDamage == 0)
1250 int securityCounter = 20;
1252 while (damage > 0 && !
float.AlmostEqual(excessDamage, defaultHitZoneMaxDamage, 0.1))
1254 if (excessDamage == 0)
1257 if (excessDamage < defaultHitZoneMaxDamage)
1258 prc = -defaultHitZoneMaxDamage / excessDamage;
1260 prc = 1 - defaultHitZoneMaxDamage / excessDamage;
1262 damage = damage - excessDamage * prc;
1266 if (securityCounter < 0)
1279 Print(
"Cannot destroy selected vehicle on collision",
LogLevel.WARNING);
1283 return Math.Ceil(damage);
1296 float impactSpeed =
Math.AbsFloat(physics.GetVelocity()[1]);
1308 protected void DebugSphereUpdate()
1310 m_DebugShape.Clear();
1311 if (DiagMenu.GetValue(
SCR_DebugMenuID.DEBUGUI_VEHICLES_COLLISIONS) == 0)
1313 m_aLocalCollisions.Clear();
1314 GetGame().GetCallqueue().Remove(DebugSphereUpdate);
1319 foreach (Tuple2<vector, bool> entry : m_aLocalCollisions)
1337 return super.FilterContact(owner, other, contact);;
1346 m_ImpactEffectComponent.OnImpact(other, contact.Impulse, contact.Position, contact.Normal, contact.Material2, contact.VelocityBefore1, contact.VelocityAfter1);
1349 if (rplComp.IsProxy())
1362 m_DebugShape.AddSphere(contact.Position, 0.3,
Color.RED,
ShapeFlags.WIREFRAME);
1363 if (m_aLocalCollisions.IsEmpty())
1364 GetGame().GetCallqueue().CallLater(DebugSphereUpdate, 0,
true);
1366 m_aLocalCollisions.Insert(
new Tuple2<vector, bool>(localPosition,
false));
1367 Print(
" Collision detected between owner = " + owner.
GetID() +
" and other = " + other.
GetID() +
" at position = " + contact.Position,
LogLevel.NORMAL);
1368 Print(
" Owner Colliding surfaces = " + contact.Material1 +
" | other = " + contact.Material2,
LogLevel.NORMAL);
1369 Print(
" Owner shape indencies = " + contact.ShapeIndex1 +
" | other = " + contact.ShapeIndex2,
LogLevel.NORMAL);
1370 Print(
" Impuls = " + contact.Impulse);
1371 float dotMultiplier =
vector.Dot(contact.VelocityAfter1.Normalized(), contact.VelocityBefore1.Normalized());
1372 vector velocityDiff = contact.VelocityBefore1 - contact.VelocityAfter1 * dotMultiplier;
1373 float impactSpeedDiff =
Math.AbsFloat(velocityDiff.Length());
1374 Print(
" Owner impact speed diff = " + impactSpeedDiff +
"m/s (" + impactSpeedDiff *
Physics.MS2KMH +
"kph)",
LogLevel.NORMAL);
1375 dotMultiplier =
vector.Dot(contact.VelocityAfter2.Normalized(), contact.VelocityBefore2.Normalized());
1376 velocityDiff = contact.VelocityBefore2 - contact.VelocityAfter2 * dotMultiplier;
1377 impactSpeedDiff =
Math.AbsFloat(velocityDiff.Length());
1378 Print(
" Other impact speed diff = " + impactSpeedDiff +
"m/s (" + impactSpeedDiff *
Physics.MS2KMH +
"kph)",
LogLevel.NORMAL);
1385 GetGame().GetCallqueue().CallLater(EnableDamageSystemOnFrame);
1391 if (collision.m_Other != other)
1394 existingCollisionInstance = collision;
1398 if (!existingCollisionInstance)
1402 Print(
" No previous collisions cached for contact between " + owner.
GetID() +
" and " + other.
GetID() +
". Caching this collision data!",
LogLevel.NORMAL);
1409 if (contact.Impulse < existingCollisionInstance.m_fImpulse)
1413 Print(
" Detected collision has smaller impuls - discarding!",
LogLevel.NORMAL);
1419 if (contact.Physics1)
1420 mass = contact.Physics1.GetMass();
1422 float dotMultiplier =
vector.Dot(contact.VelocityAfter1.Normalized(), contact.VelocityBefore1.Normalized());
1423 float momentumB = contact.VelocityBefore1.Length() * mass;
1424 float momentumA = contact.VelocityAfter1.Length() * mass * dotMultiplier;
1425 float totalMomentumOwner =
Math.AbsFloat(momentumB - momentumA * dotMultiplier);
1430 Print(
" owner momentum before = " + momentumB +
" | momentum after = " + momentumA,
LogLevel.NORMAL);
1431 Print(
" owner dotMultiplier = " + dotMultiplier,
LogLevel.NORMAL);
1435 if (totalMomentumOwner > existingCollisionInstance.m_fTotalMomentumOwner)
1436 existingCollisionInstance.m_fTotalMomentumOwner = totalMomentumOwner;
1438 if (contact.Physics2)
1439 mass = contact.Physics2.GetMass();
1443 dotMultiplier =
vector.Dot(contact.VelocityAfter2.Normalized(), contact.VelocityBefore2.Normalized());
1444 momentumB = contact.VelocityBefore2.Length() * mass;
1445 momentumA = contact.VelocityAfter2.Length() * mass * dotMultiplier;
1446 float totalMomentumOther =
Math.AbsFloat(momentumB - momentumA);
1451 Print(
" total owner momentum = " + totalMomentumOwner +
" while previous was = " + existingCollisionInstance.m_fTotalMomentumOwner,
LogLevel.NORMAL);
1452 Print(
" other momentum before = " + momentumB +
" | momentum after = " + momentumA,
LogLevel.NORMAL);
1453 Print(
" other dotMultiplier = " + dotMultiplier,
LogLevel.NORMAL);
1454 Print(
" total other momentum= " + totalMomentumOther +
" while previous was = " + existingCollisionInstance.m_fTotalMomentumOther,
LogLevel.NORMAL);
1455 Print(
" Detected collision has larger impuls!",
LogLevel.NORMAL);
1459 if (totalMomentumOther > existingCollisionInstance.m_fTotalMomentumOther)
1460 existingCollisionInstance.m_fTotalMomentumOther = totalMomentumOther;
1462 existingCollisionInstance.m_fImpulse = contact.Impulse;
1468 super.OnFrame(owner, timeSlice);
1475 DisableDamageSystemOnFrame();
1479 bool continuedCollision;
1488 continuedCollision =
false;
1491 if (collision.m_Other == oldCollision.m_Other)
1493 continuedCollision =
true;
1498 if (continuedCollision)
1514 SCR_VehicleDamageManagerComponentClass prefabData =
GetPrefabData();
1520 if (!ownerPhysics.IsDynamic())
1528 m_DebugShape.AddSphere(impactPosition, 0.2,
Color.GREEN,
ShapeFlags.WIREFRAME);
1529 m_aLocalCollisions.Insert(
new Tuple2<vector, bool>(collision.GetAverageImpactPosition(),
true));
1531 Print(
" Cached owner speed before collision = " + collision.GetOwnerVelocityBefore().Length() *
Physics.MS2KMH +
"kph)",
LogLevel.NORMAL);
1532 Print(
" Cached other speed before collision = " + collision.GetOtherVelocityBefore().Length() *
Physics.MS2KMH +
"kph)",
LogLevel.NORMAL);
1535 int ownerResponseIndex = ownerPhysics.GetResponseIndex();
1537 if (!collision.m_bOtherIsDynamic && collision.m_bOtherIsDestructible && collision.m_iOtherResponseIndex - MIN_DESTRUCTION_RESPONSE_INDEX <= ownerResponseIndex -
MIN_MOMENTUM_RESPONSE_INDEX)
1540#ifdef DISABLE_VEHICLE_COLLISION_DAMAGE
1544 float ownerMass = ownerPhysics.GetMass();
1545 float totalMomentum = collision.m_fTotalMomentumOwner + collision.m_fTotalMomentumOther;
1551 float momentumOverOccupantsThreshold = totalMomentum * collision.m_fDamageShare - prefabData.GetOccupantsMomentumThreshold(ownerMass);
1554 if (momentumOverOccupantsThreshold > 0)
1558 if (collision.m_bOtherIsDynamic && collision.GetOwnerVelocityBefore().LengthSq() > prefabData.GetVehicleDamageSpeedThreshold() * prefabData.GetVehicleDamageSpeedThreshold())
1561 if (collision.m_OtherPilot)
1567 vector directionToOther = (collision.m_vOtherOriginAtThePointOfImpact - collision.m_vOwnerOriginAtThePointOfImpact).Normalized();
1568 if (
vector.Dot(collision.GetOwnerVelocityBefore().Normalized(), directionToOther) < 0.5)
1569 instigatorEntity = collision.m_OtherPilot;
1574 if (!instigatorEntity)
1578 if (!instigatorEntity && thisVehicle)
1581 CharacterControllerComponent pilotCharacterControler;
1583 pilotCharacterControler = pilot.GetCharacterController();
1585 if (pilotCharacterControler && pilotCharacterControler.GetLifeState() !=
ECharacterLifeState.ALIVE)
1587 SCR_DamageManagerComponent pilotDamageManager = pilot.GetDamageManager();
1588 if (pilotDamageManager)
1589 instigatorEntity = pilotDamageManager.GetInstigator().GetInstigatorEntity();
1593 instigatorEntity = pilot;
1603 float collisionDamage = prefabData.GetVehicleDamageScale(ownerMass) * (totalMomentum - prefabData.GetVehicleMomentumThreshold(ownerMass));
1606 Print(
" Collision damage = " + collisionDamage +
" = " + prefabData.GetVehicleDamageScale(ownerMass) +
" * ((" + collision.m_fTotalMomentumOwner +
" + " + collision.m_fTotalMomentumOther +
") - " + prefabData.GetVehicleMomentumThreshold(ownerMass) +
")",
LogLevel.NORMAL);
1609 if (collisionDamage > 0)
1616 Print(
" Collision damage = " + (collisionDamage*damageSideMultiplier*collision.m_fDamageShare) +
" = " + collisionDamage +
" * " + damageSideMultiplier +
" * " + collision.m_fDamageShare,
LogLevel.NORMAL);
1618 collisionDamage *= damageSideMultiplier * collision.m_fDamageShare;
1625 SCR_DamageContext damageContext =
new SCR_DamageContext(
EDamageType.COLLISION,
DamageSurroundingHitzones(impactPosition, collisionDamage,
EDamageType.COLLISION), empty,
GetOwner(), GetDefaultHitZone(),
Instigator.CreateInstigator(instigatorEntity), null, -1, -1);
1636 if (!s_OnVehicleDestroyed)
1639 return s_OnVehicleDestroyed;
1646 if (!s_OnVehicleDamageStateChanged)
1649 return s_OnVehicleDamageStateChanged;
1656 super.OnDamageStateChanged(newState, previousDamageState, isJIP);
1658 if (s_OnVehicleDamageStateChanged)
1659 s_OnVehicleDamageStateChanged.Invoke(
this);
1661 HitZone defaultHitZone = GetDefaultHitZone();
1663 SCR_VehicleBuoyancyComponent vehicleBuoyancy = SCR_VehicleBuoyancyComponent.Cast(
GetOwner().FindComponent(SCR_VehicleBuoyancyComponent));
1664 if (vehicleBuoyancy)
1666 vehicleBuoyancy.SetHealth(defaultHitZone.GetDamageStateThreshold(newState));
1669 vehicleBuoyancy.GetOnWaterEnter().Remove(
OnWaterEnter);
1672 if (s_OnVehicleDestroyed && newState ==
EDamageState.DESTROYED)
1674 s_OnVehicleDestroyed.Invoke(
GetInstigator().GetInstigatorPlayerID());
1694 super.OnDamage(damageContext);
1696 if (damageContext.damageType ==
EDamageType.EXPLOSIVE && damageContext.struckHitZone == GetDefaultHitZone())
1705 BaseVehicleNodeComponent vehicleNode = BaseVehicleNodeComponent.Cast(
GetOwner().FindComponent(BaseVehicleNodeComponent));
1708 VehicleControllerComponent vehicleController = VehicleControllerComponent.Cast(vehicleNode.FindComponent(VehicleControllerComponent));
1709 if (vehicleController && vehicleController.GetEngineDrowned())
1713 return super.CanBeHealed();
1722 BaseVehicleNodeComponent vehicleNode = BaseVehicleNodeComponent.Cast(
GetOwner().FindComponent(BaseVehicleNodeComponent));
1725 VehicleControllerComponent vehicleController = VehicleControllerComponent.Cast(vehicleNode.FindComponent(VehicleControllerComponent));
1726 if (vehicleController && vehicleController.GetEngineDrowned())
1727 vehicleController.SetEngineDrowned(
false);
1731 super.FullHeal(ignoreHealingDOT);
1769 float momentum = physics.GetMass() * physics.GetVelocity().Length();
1770 float currentIndexMinMomentum = s_mResponseIndexMomentumMap.Get(physics.GetResponseIndex());
1771 int currentIndex = physics.GetResponseIndex();
1776 currentIndexMinMomentum = s_mResponseIndexMomentumMap.Get(currentIndex);
1777 if (momentum < currentIndexMinMomentum)
1781 currentIndexMinMomentum = s_mResponseIndexMomentumMap.Get(currentIndex);
1789 currentIndexMinMomentum = s_mResponseIndexMomentumMap.Get(currentIndex);
1790 if (momentum > currentIndexMinMomentum)
1794 physics.SetResponseIndex(currentIndex);
1826 RplComponent rplComponent = vehicle.GetRplComponent();
1830 if (rplComponent.IsProxy() && !rplComponent.IsOwner())
1836 else if (rplComponent.IsOwner() || !rplComponent.IsProxy())
1956 spawnParams.Transform[3] = averagePosition;
1991 if (
float.AlmostEqual(burningFuel, 0))
2003 spawnParams.Transform[3] = averagePosition;
2009 fuelBurnRate = fireRate * m_fSecondaryFireDamageDelay * m_fFuelTankFireDamageRate;
2013 if (fuelBurnRate > 0)
2015 SCR_FuelNode fuelTank;
2017 array<BaseFuelNode> fuelTanks = {};
2021 fuelTank = SCR_FuelNode.Cast(fuelNode);
2026 fuelLoss = fuelBurnRate * (1 - fuelTank.GetHealth()) * fuelNode.GetFuel() / burningFuel;
2028 fuelTank.SetFuel(fuelTank.GetFuel() - fuelLoss);
2059 if (totalResources <= 0)
2075 for (
int i; i < containerCount; i++)
2087 averagePosition +=
position * weight;
2094 float suppliesDamage;
2100 spawnParams.Transform[3] = averagePosition;
2106 suppliesDamage =
Math.Ceil(fireRate * m_fSecondaryFireDamageDelay * m_fSuppliesFireDamageRate);
2109 if (containerCount > 1)
2110 suppliesDamage /= containerCount;
2112 for (
int i; i < containerCount; i++)
2149 SCR_FlammableHitZone flammableHitZone = SCR_FlammableHitZone.Cast(GetDefaultHitZone());
2150 Instigator fireInstigator = flammableHitZone.GetFireInstigator();
2151 if (fireInstigator && flammableHitZone &&
IsOnFire(flammableHitZone) && flammableHitZone.GetFireInstigator() != newInstigator)
2155 if (newInstigator.GetInstigatorType() ==
InstigatorType.INSTIGATOR_NONE)
2178 DiagMenu.RegisterItem(
SCR_DebugMenuID.DEBUGUI_VEHICLES_COLLISIONS,
"",
"Collisions debug prints",
"Vehicles",
"disabled,base,extended,all");
ArmaReforgerScripted GetGame()
override string GetID(string fileName, string varName, array< BaseContainer > objects, array< int > indexes)
override bool FilterContact(IEntity owner, IEntity other, Contact contact)
Armor doesn't take collisiondamage.
SCR_CacheNoteComponentClass ScriptComponentClass RplProp()] protected ref array< string > m_aLines
override bool HandleDamage(BaseDamageContext damageContext, IEntity owner)
SCR_CharacterBloodHitZone OnDamage
Resilience - incapacitation or death, depending on game mode settings.
SCR_CharacterSoundComponentClass GetComponentData()
bool IsOnFire(HitZone hitZone=null)
void UpdateFireParticles(vector position, out ParticleEffectEntity particles, SCR_ESecondaryExplosionScale state, SCR_ESecondaryExplosionType fireType, EResourceType resourceType=EResourceType.SUPPLIES)
ResourceName GetSecondaryExplosion(float value, SCR_ESecondaryExplosionType explosionType, EResourceType resourceType=EResourceType.SUPPLIES, bool fire=false, out bool hasData=false)
Determine secondary explosion prefab based on explosion value, type and resource type if defined.
vector GetSecondaryExplosionPosition(typename hitZoneType, out float totalWeight=0)
Get weighed average position of explosion for hitzones of specified type.
void SecondaryExplosion(ResourceName prefabName, notnull Instigator instigator, notnull EntitySpawnParams spawnParams)
Spawn secondary explosion.
SCR_ResourceEncapsulator GetResourceEncapsulator(EResourceType suppliesType=EResourceType.SUPPLIES)
float GetMinDestroyDamage(EDamageType damageType, array< HitZone > hitzones)
SCR_DestructibleEntityClass MIN_MOMENTUM_RESPONSE_INDEX
override float GetSecondaryExplosionScale()
override void UpdateMovementDamage()
override void OnDamageStateChanged(EDamageState newState, EDamageState previousDamageState, bool isJIP)
override void SetEngineFunctional(bool functional)
override void UnregisterVehicleHitZone(HitZone hitZone)
override void RegisterVehicleHitZone(notnull HitZone hitZone)
override void UpdateVehicleState()
void ParticleEffectEntity(IEntitySource src, IEntity parent)
UI Textures DeployMenu Briefing conflict_HintBanner_1_UI desc
override bool ShouldOverrideInstigator(notnull Instigator currentInstigator, notnull Instigator newInstigator)
override void OnFilteredContact(IEntity owner, IEntity other, Contact contact)
ScriptInvokerBase< ScriptInvokerIntMethod > ScriptInvokerInt
SCR_ESecondaryExplosionType
Exploding material type.
SCR_BaseCompartmentManagerComponent m_CompartmentManager
float GetVehicleDestructionSpeed()
float m_fFuelTankFireDamageTimeout
void OnSuppliesFireStateChanged()
ParticleEffectEntity m_FuelTankFireParticle
SignalsManagerComponent m_SignalsManager
void RPC_OnPhysicsActive(bool activeState)
float GetOccupantsSpeedDeath()
float GetRightMultiplier()
override void FullHeal(bool ignoreHealingDOT=true)
float m_fDamageScaleToCharacter
enum SCR_EPhysicsResponseIndex MIN_IMPULSE_THRESHOLD
float GetBottomMultiplier()
void UpdateFuelTankFireState(float fireRate, float timeSlice)
float m_fMomentumVehicleThreshold
float m_fDamageScaleToVehicle
float GetCollisionDamageEjectionChance()
int GetMinCollisionEjectionDamageThreshold()
void OnWaterEnter()
Determine whether collision into water is severe enough to apply damage, then get velocities an manua...
SCR_ESecondaryExplosionScale m_eVehicleFireState
void SetGearboxFunctional(bool functional)
void ToggleResponseIndexTicking(IEntity owner, bool activeState)
float m_fOccupantsSpeedDeath
float m_fEngineEfficiency
SCR_ImpactEffectComponent m_ImpactEffectComponent
int m_fMinWaterFallDamageVelocity
vector m_vVehicleFireOrigin
ref array< ref SCR_CollisionDamageContainer > m_CurrentCollisions
vector GetFrontalImpact()
void HandlePassengerDamage(EDamageType damageType, float damage, notnull Instigator instigator)
Damage and eject passengers based on vehicle taking damage.
override bool CanBeHealed(bool ignoreHealingDOT=true)
Return true if there is damage that can be repaired.
float m_fOccupantsDamageSpeedThreshold
void UpdateVehicleFireState(out float fireRate, float timeSlice)
int GetMinExplosionDamageEjectionThreshold()
void ProcessCollision(notnull SCR_CollisionDamageContainer collision)
Processing of cached collision data in order to deal damage to the vhehicle and its passengers.
vector m_vFuelTankFireOrigin
void SetEngineEfficiency(float efficiency)
const float HITZONE_DAMAGE_VALUE_THRESHOLD
int m_iVehicleFireStateSignalIdx
float GetLeftMultiplier()
ref array< SCR_FlammableHitZone > m_aFlammableHitZones
SCR_VehicleDamageManagerComponentClass GetPrefabData()
void RegisterFlammableHitZone(notnull SCR_FlammableHitZone hitZone)
string m_sFuelTankFireStateSignal
float GetEngineEfficiency()
bool GetGearboxFunctional()
const int MAX_RESPONSE_INDEX
float GetCharacterDamageScale(float ownerMass)
float GetEngineMalfunctionThreshold()
int m_iMinCollisionEjectionDamageThreshold
float m_fBottomMultiplier
float m_iExplosionDamageEjectionChance
SCR_EPhysicsResponseIndex
float GetMaxSharedDamageDistance()
float GetVehicleDamageSpeedThreshold()
const int MIN_RESPONSE_INDEX
void UnregisterFlammableHitZone(notnull SCR_FlammableHitZone hitZone)
void OnVehicleFireStateChanged()
float GetRearMultiplier()
ref array< HitZone > m_aVehicleHitZones
Common vehicle features that will influence its simulation.
int m_iSuppliesFireStateSignalIdx
float GetVehicleMomentumThreshold(float ownerMass)
int m_iFuelTankFireStateSignalIdx
float m_iCollisionDamageEjectionChance
string m_sSuppliesFireStateSignal
void SetSuppliesFireState(SCR_ESecondaryExplosionScale state, vector origin=vector.Zero)
SCR_EBoxSide GetHitDirection(vector position)
SCR_ESecondaryExplosionScale m_eFuelTankFireState
float m_fSuppliesFireDamageTimeout
float GetVehicleDamageScale(float ownerMass)
override void UpdateFireDamage(float timeSlice)
void SetGearboxEfficiency(float efficiency)
void UpdateSuppliesFireState(float fireRate, float timeSlice)
void SCR_VehicleDamageManagerComponent(IEntityComponentSource src, IEntity ent, IEntity parent)
int GetMinExplosionEjectionDamageThreshold()
float GetSideDamageMultiplier(SCR_EBoxSide side)
void OnFuelTankFireStateChanged()
float GetOccupantsDamageSpeedThreshold()
bool GetEngineFunctional()
FuelManagerComponent m_FuelManager
CompartmentControllerComponent m_Controller
int GetSurroundingHitzones(vector position, notnull Physics physics, float maxDistance, EDamageType damageType, out notnull array< HitZone > outHitZones, out float hitZonesDistancePercentSum=0, out map< HitZone, float > outHitZoneDistanceProcentageMap=null)
float m_fVehicleFireDamageTimeout
float m_fVelocityMultiplierOnWaterEnter
float GetExplosionDamageEjectionChance()
float GetFrontMultiplier()
float m_fGearboxEfficiency
string m_sVehicleFireStateSignal
float GetGearboxEfficiency()
ParticleEffectEntity m_SuppliesFireParticle
void ~SCR_VehicleDamageManagerComponent()
int m_iMinExplosionEjectionDamageThreshold
void EOnPhysicsActive(IEntity owner, bool activeState)
int GetMinCollisionDamageEjectionThreshold()
void SetVehicleFireState(SCR_ESecondaryExplosionScale state, vector origin=vector.Zero)
float GetOccupantsMomentumThreshold(float ownerMass)
void TickResponseIndexCheck()
float m_fMomentumOccupantsThreshold
vector m_vSuppliesFireOrigin
float DamageSurroundingHitzones(vector position, float damage, EDamageType damageType, bool dontApply=false)
SCR_ESecondaryExplosionScale m_eSuppliesFireState
const int CACHED_COLLISION_PROCESSING_DELAY
VehicleBaseSimulation m_Simulation
void InitStaticMapForIndices()
void SetFuelTankFireState(SCR_ESecondaryExplosionScale state, vector origin=vector.Zero)
bool m_bGearboxFunctional
ref array< ref SCR_CollisionDamageContainer > m_LastFrameCollisions
enum EVehicleType IEntity
void Register(SCR_DamageManagerComponent component)
proto external Managed FindComponent(typename typeName)
proto external vector GetOrigin()
proto external Physics GetPhysics()
proto external void GetBounds(out vector mins, out vector maxs)
proto external BaseWorld GetWorld()
proto external EntityID GetID()
proto external vector CoordToParent(vector coord)
proto external vector CoordToLocal(vector coord)
void UpdateImpactPosition(vector position)
static RplComponent GetEntityRplComponent(notnull IEntity entity)
static void StopParticleEmissionAndLights(notnull ParticleEffectEntity particleEntity, int lightEmitterID=0)
bool DecreaseResourceValue(float value, bool notifyChange=true)
SCR_ResourceContainer GetContainerAt(int index)
override float GetAggregatedResourceValue()
override SCR_ResourceContainerQueueBase GetContainerQueue()
override int GetContainerCount()
Instance of created debug visualizer.
enum EPhysicsLayerPresets Vehicle
IEntity GetOwner()
Owner entity of the fuel tank.
proto external vector GetVelocity()
proto void Print(void var, LogLevel level=LogLevel.NORMAL)
Prints content of variable to console/log.
LogLevel
Enum with severity of the logging message.
SCR_FieldOfViewSettings Attribute
GenericWorldEntityClass GenericEntityClass GetTerrain(float x, float z)
EntityEvent
Various entity events.
proto external EParticleEffectState GetState()
BaseProjectileComponentClass GameComponentClass GetInstigator()
void RplRpc(RplChannel channel, RplRcver rcver, RplCondition condition=RplCondition.None, string customConditionName="")
RplChannel
Communication channel. Reliable is guaranteed to be delivered. Unreliable not.
proto native vector Vector(float x, float y, float z)
proto int ARGB(int a, int r, int g, int b)