284 const vector charAimPosition = character.AimingPosition();
285 float distanceAim =
vector.Distance(startingPos, charAimPosition);
286 vector charDirection =
vector.Direction(startingPos, charAimPosition).Normalized();
288 const vector nearestPositionAim = startingPos +
direction * dotAim * distanceAim;
289 distanceAim =
vector.Distance(nearestPositionAim, charAimPosition);
292 vector charPosition = character.EyePosition();
294 charDirection =
vector.Direction(startingPos, charPosition).Normalized();
297 if (distanceAim >
vector.Distance(nearestPosition, charPosition) && dot >= 0 && dot > dotAim)
302 m_DebugShapeMgr.AddSphere(charPosition, DEBUG_SPHERE_RADIUS * 0.3,
Color.SPRING_GREEN, DEBUG_SHAPE_FLAGS);
303 m_DebugShapeMgr.AddLine(charPosition, nearestPosition,
Color.SPRING_GREEN);
304 m_DebugShapeMgr.AddSphere(nearestPosition, DEBUG_SPHERE_RADIUS * 0.3,
Color.DARK_GREEN, DEBUG_SHAPE_FLAGS);
306 AddDebugInfo(
"Distance:",
"Eyes - " + GetPrefabName(character) +
" at " + charPosition +
" distance = " +
distance.ToString(lenDec: 2));
313 charPosition = character.GetOrigin();
315 charDirection =
vector.Direction(startingPos, charPosition).Normalized();
318 if (distanceAim >
vector.Distance(nearestPosition, charPosition) && dot >= 0 && dot > dotAim)
323 m_DebugShapeMgr.AddSphere(charPosition, DEBUG_SPHERE_RADIUS * 0.3,
Color.GRAY_25, DEBUG_SHAPE_FLAGS);
324 m_DebugShapeMgr.AddLine(charPosition, nearestPosition,
Color.SPRING_GREEN);
325 m_DebugShapeMgr.AddSphere(nearestPosition, DEBUG_SPHERE_RADIUS * 0.3,
Color.GRAY, DEBUG_SHAPE_FLAGS);
327 AddDebugInfo(
"Distance:",
"Root - " + GetPrefabName(character) +
" at " + charPosition +
" distance = " +
distance.ToString(lenDec: 2));
336 m_DebugShapeMgr.AddSphere(charAimPosition, DEBUG_SPHERE_RADIUS * 0.3,
Color.DODGER_BLUE, DEBUG_SHAPE_FLAGS);
337 m_DebugShapeMgr.AddLine(charAimPosition, nearestPositionAim,
Color.SPRING_GREEN);
338 m_DebugShapeMgr.AddSphere(nearestPositionAim, DEBUG_SPHERE_RADIUS * 0.3,
Color.DARK_BLUE, DEBUG_SHAPE_FLAGS);
340 AddDebugInfo(
"Distance:",
"Center of mass - " + GetPrefabName(character) +
" at " + charAimPosition +
" distance = " + distanceAim.ToString(lenDec: 2));
344 return charAimPosition;
390 protected void QueryBlastedCharacters(
vector startingPos[4],
float length, out notnull array<ref SCR_BlastedEntityEntry> blastedEntities,
float additionalDistance = 0)
392 if (!m_aFoundCharacters)
393 m_aFoundCharacters = {};
395 m_aFoundCharacters.Clear();
401 const float blastLength =
data.GetBlastLength();
402 const float coneAngle =
data.GetBlastConeAngle();
404 const float radius =
Math.Clamp(length *
Math.Tan(coneAngle *
Math.DEG2RAD), 0, length);
405 const vector mins = {-radius, -radius, 0};
406 const vector maxs = {radius, radius, length};
411 const vector direction =
vector.Direction(startingPos[3],
vector.Forward.Multiply3(startingPos) * length + startingPos[3]).Normalized();
415 bool showDebug = debugValue < 5;
416 if (additionalDistance != 0)
417 showDebug = debugValue < 4 || debugValue == 5;
419 if (showDebug && debugValue != 0)
422 m_DebugShapeMgr.AddRectangle(startingPos[3],
direction, length, radius * 2,
Color.BLACK);
425 if (additionalDistance == 0)
428 m_DebugShapeMgr.AddTrapezoidalPrism(startingPos[3],
direction, additionalDistance *
Math.Tan(coneAngle *
Math.DEG2RAD), (length + additionalDistance) *
Math.Tan(coneAngle *
Math.DEG2RAD), length, DEBUG_CONE_SUBDIV,
Color.YELLOW,
ShapeFlags.NOZBUFFER |
ShapeFlags.TRANSP);
432 if (m_aFoundCharacters.IsEmpty())
435 if (m_aFoundCharacters.Count() > MAX_BLAST_MEMBERS)
438 m_aFoundCharacters.Resize(MAX_BLAST_MEMBERS);
441 const float maxCos =
Math.Cos(coneAngle *
Math.DEG2RAD);
444 vector entDirection, entPosition, nearestPosition;
445 CharacterControllerComponent controller;
449 traceParam.Start = startingPos[3];
451 traceParam.LayerMask = EPhysicsLayerDefs.Projectile;
456 foreach (
IEntity ent : m_aFoundCharacters)
462 if (m_bIsAiCharacter && !
data.CanAIDamageItself() && character == m_Instigator.GetInstigatorEntity())
465 controller = character.GetCharacterController();
469 if (!controller.IsPlayerControlled() &&
data.ShouldIgnoreAIUnits())
476 if (showDebug && (debugValue == 2 || debugValue >= 4))
478 if (character == m_Instigator.GetInstigatorEntity())
479 m_DebugShapeMgr.AddArrow(nearestPosition +
vector.Up, nearestPosition, 0,
Color.PINK);
481 m_DebugShapeMgr.AddArrow(nearestPosition +
vector.Up, nearestPosition, 0,
Color.VIOLET);
483 AddDebugInfo(
"Distance:",
"Rejected " + GetPrefabName(character) +
" at " + nearestPosition +
" - too far");
489 entDirection =
vector.Direction(startingPos[3], nearestPosition).Normalized();
494 if (showDebug && (debugValue == 2 || debugValue >= 4))
496 if (character == m_Instigator.GetInstigatorEntity())
497 m_DebugShapeMgr.AddArrow(nearestPosition +
vector.Up, nearestPosition, 0,
Color.CYAN);
499 m_DebugShapeMgr.AddArrow(nearestPosition +
vector.Up, nearestPosition, 0,
Color.DARK_CYAN);
501 AddDebugInfo(
"Direction:",
"Rejected " + GetPrefabName(character) +
" at " + nearestPosition +
" - wrong direction");
516 if (showDebug && (debugValue == 2 || debugValue >= 4))
518 if (character == m_Instigator.GetInstigatorEntity())
519 m_DebugShapeMgr.AddArrow(entPosition +
vector.Up, entPosition, 0,
Color.SPRING_GREEN);
521 m_DebugShapeMgr.AddArrow(entPosition +
vector.Up, entPosition, 0,
Color.DARK_GREEN);
523 AddDebugInfo(
"Distance:",
"Rejected " + GetPrefabName(character) +
" at " + entPosition +
" - too far");
529 entDirection =
vector.Direction(startingPos[3], entPosition).Normalized();
532 if (additionalDistance > 0 && dot >= 0 && dot < maxCos)
533 dot =
vector.Dot(
vector.Direction(startingPos[3] -
vector.Forward.Multiply3(startingPos) * additionalDistance, entPosition).Normalized(),
direction);
539 if (showDebug && (debugValue == 2 || debugValue >= 4))
541 if (character == m_Instigator.GetInstigatorEntity())
542 m_DebugShapeMgr.AddArrow(entPosition +
vector.Up, entPosition, 0,
Color.DODGER_BLUE);
544 m_DebugShapeMgr.AddArrow(entPosition +
vector.Up, entPosition, 0,
Color.DARK_BLUE);
546 AddDebugInfo(
"Angle:",
"Rejected " + GetPrefabName(character) +
" at " + entPosition +
" - outside of the max angle");
552 traceParam.End = entPosition;
553 traceParam.TraceEnt = null;
554 m_VerifiedEntity = character;
558 if (traceParam.TraceEnt && character != traceParam.TraceEnt)
561 if (showDebug && (debugValue == 2 || debugValue >= 4))
563 if (character == m_Instigator.GetInstigatorEntity())
564 m_DebugShapeMgr.AddArrow(entPosition +
vector.Up, entPosition, 0,
Color.GRAY_25);
566 m_DebugShapeMgr.AddArrow(entPosition +
vector.Up, entPosition, 0,
Color.GRAY);
568 AddDebugInfo(
"Obstructed:",
"At " + entPosition +
" " + GetPrefabName(character) +
" by " + GetPrefabName(traceParam.TraceEnt));
574 hitPosDirNorm[0] = entDirection *
distance + traceParam.Start;
575 hitPosDirNorm[1] = entDirection;
576 hitPosDirNorm[2] = traceParam.TraceNorm.Normalized();
578 SCR_BlastedEntityEntry entry =
new SCR_BlastedEntityEntry(character, hitPosDirNorm[0], hitPosDirNorm[1], hitPosDirNorm[2], dot,
vector.Distance(startingPos[3], entPosition) + additionalDistance, traceParam.NodeIndex, traceParam.ColliderIndex, traceParam.SurfaceProps);
579 blastedEntities.Insert(entry);
581 if (showDebug && debugValue != 0)
583 if (character == m_Instigator.GetInstigatorEntity())
584 m_DebugShapeMgr.AddArrow(traceParam.Start, entPosition, 0,
Color.RED);
586 m_DebugShapeMgr.AddArrow(traceParam.Start, entPosition, 0,
Color.DARK_RED);
588 AddDebugInfo(
"Hit:", GetPrefabName(character) +
" at " + entPosition);
593 m_aFoundCharacters.Clear();
594 m_VerifiedEntity = null;
601 if (!m_Instigator || m_Instigator.GetInstigatorType() ==
InstigatorType.INSTIGATOR_NONE)
604 m_Instigator =
Instigator.CreateInstigator(ownerCharacter);
609 m_BlastOrigin.GetWorldTransform(startTransform);
610 array<ref SCR_BlastedEntityEntry> blastedEntities = {};
612 if (!m_DebugShapeMgr)
615 m_DebugShapeMgr.Clear();
617 DbgUI.BeginCleanupScope();
620 DbgUI.Begin(
"Weapon blast legend");
621 AddDebugLegendEntry(
"VIOLET", dbgPanelXSize, dbgPanelYSize,
Color.VIOLET,
"VIOLET Arrow - Rejected. Target entity is too far");
622 AddDebugLegendEntry(
"PINK", dbgPanelXSize, dbgPanelYSize,
Color.PINK,
"PINK Arrow - Rejected. Character who caused the blast is too far");
623 AddDebugLegendEntry(
"CYAN", dbgPanelXSize, dbgPanelYSize,
Color.CYAN,
"CYAN Arrow - Rejected. Outside of the area of influence");
624 AddDebugLegendEntry(
"GREEN", dbgPanelXSize, dbgPanelYSize,
Color.GREEN,
"GREEN Arrow - Rejected. Selected hit zone is too far from the blast source");
625 AddDebugLegendEntry(
"BLUE", dbgPanelXSize, dbgPanelYSize,
Color.BLUE,
"BLUE Arrow - Rejected. Direction to it deviates too much from the direction of the blast");
626 AddDebugLegendEntry(
"GRAY", dbgPanelXSize, dbgPanelYSize,
Color.GRAY,
"GRAY Arrow - Rejected. Something obstructing it");
627 AddDebugLegendEntry(
"RED", dbgPanelXSize, dbgPanelYSize,
Color.RED,
"RED Arrow - Object hit");
628 AddDebugLegendEntry(
"GREEN_S", dbgPanelYSize, dbgPanelYSize,
Color.GREEN,
"GREEN Sphere - Using character eye position as it is the closest point");
629 AddDebugLegendEntry(
"GRAY_S", dbgPanelYSize, dbgPanelYSize,
Color.GRAY,
"GRAY Sphere - Using character root position as it is the closest point");
630 AddDebugLegendEntry(
"BLUE_S", dbgPanelYSize, dbgPanelYSize,
Color.BLUE,
"BLUE Sphere - Using character center of mass as it is the closest point");
631 AddDebugLegendEntry(
"BLACK_S", dbgPanelYSize, dbgPanelYSize,
Color.BLACK,
"BLACK Sphere - Rejected. Inssuficient amount of damage");
632 AddDebugLegendEntry(
"RED_S", dbgPanelYSize, dbgPanelYSize,
Color.RED,
"RED Sphere - Object damaged");
638 m_aDebugData.Clear();
647 float blastLength =
data.GetBlastLength();
648 if (!m_bIsAiCharacter ||
data.CanAIBlastCharacters())
651 if (
data.CanBlastRicochet())
656 traceParam.Start = startTransform[3];
657 traceParam.End =
vector.Forward.Multiply3(startTransform) * blastLength + traceParam.Start;
659 array<IEntity> excludeArray = {owner, m_Instigator.GetInstigatorEntity()};
660 traceParam.ExcludeArray = excludeArray;
661 traceParam.LayerMask = EPhysicsLayerDefs.Projectile;
662 float hit = world.TraceMove(traceParam,
TraceFilter);
664 if (traceParam.TraceEnt && hit < 1 && !
float.AlmostEqual(hit, 1, 0.1))
666 bool ricochetFromDamageEnt;
669 if (entry.GetTargetEntity() == traceParam.TraceEnt)
671 ricochetFromDamageEnt =
true;
672 entry.SetAngleToTarget(1);
678 vector dir = traceParam.End - traceParam.Start;
679 hitPosDirNorm[0] = dir * hit + traceParam.Start;
680 hitPosDirNorm[1] = dir.Normalized();
681 hitPosDirNorm[2] = traceParam.TraceNorm.Normalized();
683 float hitDistance = blastLength * hit;
684 bool ricochetFromCharacter =
ChimeraCharacter.Cast(traceParam.TraceEnt) != null;
685 if (!ricochetFromDamageEnt && (!m_bIsAiCharacter || !ricochetFromCharacter &&
data.CanAIBlastDestructible() || ricochetFromCharacter &&
data.CanAIBlastCharacters()))
686 blastedEntities.Insert(
new SCR_BlastedEntityEntry(traceParam.TraceEnt, hitPosDirNorm[0], hitPosDirNorm[1], hitPosDirNorm[2], 1, hitDistance, traceParam.NodeIndex, traceParam.ColliderIndex, traceParam.SurfaceProps));
688 CalculateRicochetDirection(hitPosDirNorm, startTransform,
data.GetDeflectionFlatteningStrength());
692 m_DebugShapeMgr.AddArrow(traceParam.Start, hitPosDirNorm[0], 0,
Color.ORANGE);
693 m_DebugShapeMgr.AddSphere(startTransform[3], DEBUG_SPHERE_RADIUS,
Color.ORANGE, DEBUG_SHAPE_FLAGS);
696 m_VerifiedEntity = traceParam.TraceEnt;
698 if (!m_bIsAiCharacter ||
data.CanAIBlastCharacters())
703 if (blastedEntities.Count() > 0)
706 m_Instigator =
Instigator.CreateInstigator(null);
707 m_bIsAiCharacter =
false;
712 const string time =
System.GetUnixTime().ToString();
715 DbgUI.Begin(
"Weapon blast details", y: 300);
718 DbgUI.List(
"List_" + i + time, outSelection, arr);
723 DbgUI.EndCleanupScope();
728 protected void ApplyDamage(notnull array<ref SCR_BlastedEntityEntry> blastedEntities)
735 array<SCR_WeaponBlastEffect> blastEffects = {};
736 if (
data.GetBlastEffects(blastEffects) < 1)
743 SCR_DamageManagerComponent damageManager;
746 float computedDamageAmount;
748 const float blastLength =
data.GetBlastLength();
749 const float destructibleDamageMultiplier =
data.GetDestructibleDamageMultiplier();
755 if (destructibleEntity)
759 damageValue = effect.GetComputedDamage(entry.GetDistanceToTarget() / blastLength, entry.GetAngleToTarget()) * destructibleDamageMultiplier;
761 entry.GetTargetHitPosDirNorm(hitPosDirNorm);
762 destructibleEntity.HandleDamage(effect.GetDamageType(), damageValue, hitPosDirNorm);
766 float factor =
Math.Lerp(0, 1, (1 - entry.GetDistanceToTarget() / blastLength) * entry.GetAngleToTarget());
767 Color damageColor =
new Color(factor * 0.1, factor, factor, 1);
768 m_DebugShapeMgr.AddSphere(hitPosDirNorm[0], DEBUG_SPHERE_RADIUS + i * 0.05, damageColor.PackToInt(), DEBUG_SHAPE_FLAGS);
770 AddDebugInfo(
"Damage dealt:", GetPrefabName(destructibleEntity) +
" at " + hitPosDirNorm[0] +
", value = " + damageValue.ToString(lenDec: 3));
780 if (!damageManager || !hitZone)
783 entry.GetTargetHitPosDirNorm(hitPosDirNorm);
784 maxHealth = hitZone.GetMaxHealth();
787 damageValue = effect.GetComputedDamage(entry.GetDistanceToTarget() / blastLength, entry.GetAngleToTarget());
789 damageValue *= destructibleDamageMultiplier;
791 context =
new SCR_DamageContext(effect.GetDamageType(), damageValue, hitPosDirNorm,
792 entry.GetTargetEntity(), hitZone, m_Instigator,
793 entry.GetTargetSurfaceProps(), entry.GetTargetColliderId(), entry.GetTargetNodeId());
795 context.damageEffect = effect.GetDamageEffect();
796 context.damageSource = owner;
798 if (computedDamageAmount == 0 || computedDamageAmount / maxHealth < 0.01)
803 m_DebugShapeMgr.AddSphere(hitPosDirNorm[0], DEBUG_SPHERE_RADIUS + i * 0.05,
Color.BLACK, DEBUG_SHAPE_FLAGS);
805 AddDebugInfo(
"Damage insufficient:",
"Effect nr = " + i +
" " + GetPrefabName(context.hitEntity) +
" at " + context.hitPosition +
", value = " + computedDamageAmount +
" < " + (maxHealth * 0.01));
811 damageManager.HandleDamage(context);
815 float factor =
Math.Lerp(0, 1, context.damageValue / effect.GetDamageValueRaw());
817 Color damageColor =
new Color(factor, factor * 0.1, factor * 0.1, 1);
818 m_DebugShapeMgr.AddSphere(hitPosDirNorm[0], DEBUG_SPHERE_RADIUS + i * 0.05, damageColor.PackToInt(), DEBUG_SHAPE_FLAGS);
820 AddDebugInfo(
"Damage dealt:",
"Effect nr = " + i +
" " + GetPrefabName(context.hitEntity) +
" at " + context.hitPosition +
", value = " + context.damageValue.ToString(lenDec: 3) +
" (max dmg = " + effect.GetDamageValueRaw() +
")");