void OnDamage(ManDamage.DamageInfo info) { foreach (Spinner target in targetSpinners) { Vector3 axis = target.m_RotationAxis, perp = new Vector3(0f, 1f - axis.y, axis.y); float angle = Vector3.SignedAngle(perp, target.trans.localRotation * perp, axis); target.SetAutoSpin(SetFullSpeedInstead); target.Reset(); target.SetAngle(angle); } }
private static int HandleCollision(Projectile __instance, ArmorPiercing armorPiercing, Damageable damageable, Vector3 hitPoint, Vector3 impactVector, Collider otherCollider, bool ForceDestroy) { int retVal = 0; if (!((Component)__instance).gameObject.activeInHierarchy) { DebugPrint("projectile is inactive in hierarchy"); return(0); } if ((bool)PatchProjectile.m_Stuck.GetValue(__instance)) { DebugPrint("projectile is stuck"); return(0); } bool singleImpact = (bool)PatchProjectile.m_SingleImpact.GetValue(__instance); bool hasHitTerrain = false; bool stickOnContact = (bool)PatchProjectile.m_StickOnContact.GetValue(__instance); float deathDelay = (float)PatchProjectile.GetDeathDelay.Invoke(__instance, null); // handle damage calculations and explosions if (damageable) { DebugPrint("Projectile hit a damageable"); float damage = armorPiercing.remainingDamage; // Armor pierce works as follows: // Deal full damage to the first block we hit. After that, (1 - pierce) * dealt damage is subtracted from remaining damage (if any) DebugPrint($"Stage 1 - create new DamageInfo"); ManDamage.DamageInfo damageInfo = new ManDamage.DamageInfo(damage, (ManDamage.DamageType)PatchProjectile.m_DamageType.GetValue(__instance), (ModuleWeapon)PatchProjectile.m_Weapon.GetValue(__instance), __instance.Shooter, hitPoint, __instance.rbody.velocity, 0f, 0f); DebugPrint($"Stage 2 - deal the damage"); float fracDamageRemaining = Singleton.Manager <ManDamage> .inst.DealDamage(damageInfo, damageable); DebugPrint("break 3"); float damageDealt = fracDamageRemaining > 0.0f ? damage * (1 - fracDamageRemaining) : damage; DebugPrint($"Stage 4a - Just dealt {damageDealt} damage to damageable {damageable.name} ({damageable.Health}), original shell has damage {damage}"); // block was destroyed, damage potentially leftover if (fracDamageRemaining > 0.0f) { retVal = (int)Mathf.Max(0.0f, (damage * fracDamageRemaining) - (damageDealt * (1.0f - armorPiercing.armorPierce))); DebugPrint($"Killed damageable {damageable.name}, SHELL DMG {damage} ==[REMAINING DMG]=> {retVal}"); } else { DebugPrint($"Failed to kill damageable {damageable.name}, SHELL DMG {damage} ==[REMAINING DMG]=> 0"); } // no damage leftover cases: if (retVal == 0) { if (deathDelay != 0.0f && !stickOnContact) { // penetration fuse, but failed to kill = flattened, spawn the explosion now deathDelay = 0.0f; PatchProjectile.SpawnExplosion.Invoke(__instance, new object[] { hitPoint, damageable }); } else if ((bool)PatchProjectile.IsProjectileArmed.Invoke(__instance, null) && !stickOnContact) { // no penetration fuse, check if armed and not stick on contact - stick on contact explosions are done later PatchProjectile.SpawnExplosion.Invoke(__instance, new object[] { hitPoint, damageable }); } } } else if (otherCollider is TerrainCollider || otherCollider.gameObject.layer == Globals.inst.layerLandmark || otherCollider.GetComponentInParents <TerrainObject>(true)) { DebugPrint("Stage 4b"); hasHitTerrain = true; PatchProjectile.SpawnTerrainHitEffect.Invoke(__instance, new object[] { hitPoint }); DebugPrint("Stage 4bb"); // if explode on terrain, explode and end, no matter death delay if ((bool)PatchProjectile.m_ExplodeOnTerrain.GetValue(__instance) && (bool)PatchProjectile.IsProjectileArmed.Invoke(__instance, null)) { PatchProjectile.SpawnExplosion.Invoke(__instance, new object[] { hitPoint, null }); // if default single impact behavior, explode on terrain, then die. // else, keep the bouncing explosions if (singleImpact) { __instance.Recycle(false); return(0); } } } DebugPrint("Stage 5 - play sfx"); Singleton.Manager <ManSFX> .inst.PlayImpactSFX(__instance.Shooter, (ManSFX.WeaponImpactSfxType) PatchProjectile.m_ImpactSFXType.GetValue(__instance), damageable, hitPoint, otherCollider); DebugPrint("Stage 6 - handle recycle"); // if here, then no stick on contact, and no damage is leftover, so start destruction sequence if (ForceDestroy) // if projectile hits a shield, always destroy { __instance.Recycle(false); } else if (deathDelay == 0f) { // If hasn't hit terrain, and still damage left, return here - don't recycle if (!hasHitTerrain && retVal > 0) { return(retVal); } __instance.Recycle(false); } else if (!(bool)PatchProjectile.m_HasSetCollisionDeathDelay.GetValue(__instance)) { PatchProjectile.m_HasSetCollisionDeathDelay.SetValue(__instance, true); PatchProjectile.SetProjectileForDelayedDestruction.Invoke(__instance, new object[] { deathDelay }); if (__instance.SeekingProjectile) { __instance.SeekingProjectile.enabled = false; } PatchProjectile.OnDelayedDeathSet.Invoke(__instance, null); } DebugPrint("Stage 7 - handle stick on terrain"); bool stickOnTerrain = (bool)PatchProjectile.m_StickOnTerrain.GetValue(__instance); DebugPrint("HUH"); if (stickOnContact && (stickOnTerrain || !hasHitTerrain)) { DebugPrint("WHAT"); GameObject test3 = otherCollider.gameObject; DebugPrint("WHAT 1"); Transform trans = test3.transform; DebugPrint("WHAT 2"); Vector3 scale = trans.lossyScale; DebugPrint("WHAT 3"); if (otherCollider.gameObject.transform.lossyScale.Approximately(Vector3.one, 0.001f)) { DebugPrint("Stage 7a"); ((Component)__instance).transform.SetParent(otherCollider.gameObject.transform); PatchProjectile.SetStuck.Invoke(__instance, new object[] { true }); SmokeTrail smoke = (SmokeTrail)PatchProjectile.m_Smoke.GetValue(__instance); if (smoke) { smoke.enabled = false; smoke.Reset(); } DebugPrint("Stage 7b"); Visible stuckTo = Singleton.Manager <ManVisible> .inst.FindVisible(otherCollider); PatchProjectile.m_VisibleStuckTo.SetValue(__instance, stuckTo); if (stuckTo.IsNotNull()) { stuckTo.RecycledEvent.Subscribe(new Action <Visible>((Action <Visible>)PatchProjectile.OnParentDestroyed.GetValue(__instance))); } DebugPrint("Stage 7c"); if ((bool)PatchProjectile.m_ExplodeOnStick.GetValue(__instance)) { Visible visible = (Visible)PatchProjectile.m_VisibleStuckTo.GetValue(__instance); Damageable directHitTarget = visible.IsNotNull() ? visible.damageable : null; PatchProjectile.SpawnExplosion.Invoke(__instance, new object[] { hitPoint, directHitTarget }); } DebugPrint("Stage 7d"); if (((Transform)PatchProjectile.m_StickImpactEffect.GetValue(__instance)).IsNotNull()) { PatchProjectile.SpawnStickImpactEffect.Invoke(__instance, new object[] { hitPoint }); } } else { d.LogWarning(string.Concat(new string[] { "Won't attach projectile ", __instance.name, " to ", otherCollider.name, ", as scale is not one" })); } } DebugPrint("FINAL"); return(retVal); }
public static void Postfix(ref BlockManager __instance, ref TankBlock block, ref bool isPropogating, ref bool rootTransfer) { ManDamage.DamageInfo damageInfo = new ManDamage.DamageInfo(float.PositiveInfinity, ManDamage.DamageType.Standard, (Component)null, (Tank)null, block.transform.position, default(Vector3), 0.0f, 0.0f); block.visible.damageable.TryToDamage(damageInfo, true); return; }
private static float DoDamage(ref ManDamage __instance, Damageable directHit, Damageable damageTarget, ManDamage.DamageInfo damageInfo) { ExplosionNerf.IngressPoint.DebugPrint("<ENM> ", "Doing DMG"); float cabDamageDissipationFactor = (float)PatchDamage.m_CabDamageDissipationFactor.GetValue(__instance); // ExplosionNerf.IngressPoint.DebugPrint("A"); float dmgDone = 0.0f; ManDamage.DamageInfo damageInfo1 = damageInfo.Clone(); if (directHit != null) { if ((UnityEngine.Object)PatchDamage.m_DamageMultiplierTable.GetValue(__instance) != (UnityEngine.Object)null) { // ExplosionNerf.IngressPoint.DebugPrint("B1"); float damageMultiplier = ((DamageMultiplierTable)PatchDamage.m_DamageMultiplierTable.GetValue(__instance)).GetDamageMultiplier(damageInfo.DamageType, directHit.DamageableType, true); // ExplosionNerf.IngressPoint.DebugPrint("B2"); if ((double)damageMultiplier != 1.0) { damageInfo1.ApplyDamageMultiplier(damageMultiplier); } // ExplosionNerf.IngressPoint.DebugPrint("B3"); } TankBlock hitBlock = damageTarget.Block; if (hitBlock != null) { damageInfo1.ApplyDamageMultiplier(Mathf.Pow(hitBlock.filledCells.Length, 2.0f / 3.0f)); } // ExplosionNerf.IngressPoint.DebugPrint("D"); int adjCount = 0; if ((double)cabDamageDissipationFactor > 0.0 && directHit.Block.IsNotNull() && directHit.Block.IsController) { // ExplosionNerf.IngressPoint.DebugPrint("E1"); adjCount = directHit.Block.ConnectedBlocksByAP.Count(); } if (adjCount > 0) { // ExplosionNerf.IngressPoint.DebugPrint("F1"); float cabDamageDissipationDetachFactor = (float)PatchDamage.m_CabDamageDissipationDetachFactor.GetValue(__instance); // ExplosionNerf.IngressPoint.DebugPrint("F2"); // ManDamage.DamageInfo damageInfo2 = damageInfo1.Clone(); float multiplier = cabDamageDissipationFactor / (float)(adjCount + 1); damageInfo1.ApplyDamageMultiplier((float)(1.0 - (double)multiplier * (double)adjCount)); } } dmgDone = damageTarget.TryToDamage(damageInfo1, true); // || damageTarget.Block == null || damageTarget.Block.tank == null if (dmgDone == 0.0) { if (damageTarget == null || damageTarget.Health <= 0.0f || damageTarget.Block == null || damageTarget.Block.PreExplodePulse || damageTarget.Block.tank == null) { dmgDone = 1.0f; } else { ModuleDamage module = damageTarget.Block.GetComponent <ModuleDamage>(); if (module != null) { dmgDone = (float)PatchDamage.m_ExplodeCountdownTimer.GetValue(module); } } } ExplosionNerf.IngressPoint.DebugPrint("<ENM> ", "Dmg Done: " + dmgDone.ToString()); return(dmgDone); }
private static float recursiveHandleDamage(ref ManDamage __instance, ref float __result, Damageable damageTarget, Damageable directHit, Component source, TankBlock targetBlock, Tank targetTank, string prefix, bool RecurseOverride = false) { if (RecurseOverride || PatchDamage.YetToHit.Contains(targetBlock)) { ExplosionNerf.IngressPoint.DebugPrint("\n" + prefix, "Resolving Block: " + targetBlock.name); PatchDamage.YetToHit.Remove(targetBlock); if (directHit != null) { ExplosionNerf.IngressPoint.DebugPrint(prefix, "direct hit detected"); TankBlock localHitBlock = directHit.Block; if (localHitBlock != PatchDamage.hitBlock) { ExplosionNerf.IngressPoint.DebugPrint(prefix, "ONLY should happen when direct hit RESOLVED, block NOT destroyed"); ExplosionNerf.IngressPoint.DebugPrint(prefix, "RESOLUTION - ALWAYS BLOCK"); return(0.0f); } if (directHit == damageTarget) { // do actual dmg to targetBlock ExplosionNerf.IngressPoint.DebugPrint(prefix, "Direct hit NEVER invincible - calculate dmg"); Explosion castSource = (Explosion)source; // Console.WriteLine(directHit.MaxHealth <= 1.0f); // Console.WriteLine(localHitBlock.GetComponent<ModuleShieldGenerator>() != null); // Console.WriteLine(PatchDamage.rejectDamageEvent.GetValue()); ManDamage.DamageInfo damageInfo = PatchDamage.NewDamageInfo(directHit, damageTarget, targetBlock); ExplosionNerf.IngressPoint.DebugPrint(prefix, "Damage: " + damageInfo.Damage.ToString()); float dmgDone = PatchDamage.DoDamage(ref __instance, directHit, damageTarget, damageInfo); if (dmgDone == 0.0f) { ExplosionNerf.IngressPoint.DebugPrint(prefix, "Block NOT destroyed - mark invincible for now"); if (!PatchDamage.DeterminedInvincible.ContainsKey(targetTank)) { PatchDamage.DeterminedInvincible[targetTank] = new HashSet <TankBlock>(); } PatchDamage.DeterminedInvincible[targetTank].Add(localHitBlock); } else { ExplosionNerf.IngressPoint.DebugPrint(prefix, "BLOCK DESTROYED - remove from consideration"); PatchDamage.DirectHit.SetValue(source, null); } PatchDamage.hitBlock = null; // modify damageInfo for recursion purposes - no longer necessary // damageInfo.ApplyDamageMultiplier(newDamageMult); // modify dmg energy left in explosion castSource.m_MaxDamageStrength *= (float)PatchDamage.m_AoEDamageBlockPercent.GetValue(directHit); // PatchDamage.DirectHit.SetValue(source, null); return(dmgDone); } else { ExplosionNerf.IngressPoint.DebugPrint(prefix, "Resolve Direct hit first"); float destroyed = PatchDamage.recursiveHandleDamage(ref __instance, ref __result, directHit, directHit, source, localHitBlock, targetTank, prefix + "| ", true); // if block was actually destroyed if (destroyed != 0.0f) { // do actual dmg to targetBlock PatchDamage.hitBlock = null; ExplosionNerf.IngressPoint.DebugPrint(prefix, "Resolve NOT NECESSARILY invincible - calculate dmg (re-recurse)"); PatchDamage.YetToHit.Add(targetBlock); directHit = (Damageable)PatchDamage.DirectHit.GetValue(source); return(PatchDamage.recursiveHandleDamage(ref __instance, ref __result, damageTarget, directHit, source, targetBlock, targetTank, prefix + "| ")); } else { ExplosionNerf.IngressPoint.DebugPrint(prefix, "ONLY should happen when direct hit RESOLVED, block NOT destroyed"); ExplosionNerf.IngressPoint.DebugPrint(prefix, "RESOLUTION - ALWAYS BLOCK"); if (!PatchDamage.DeterminedInvincible.ContainsKey(targetTank)) { PatchDamage.DeterminedInvincible[targetTank] = new HashSet <TankBlock>(); } PatchDamage.DeterminedInvincible[targetTank].Add(targetBlock); return(0.0f); } } } else { ExplosionNerf.IngressPoint.DebugPrint(prefix, "no direct hit - Raycast time"); Vector3 actual = source.transform.position - targetBlock.transform.position; RaycastHit[] results = new RaycastHit[((int)actual.magnitude)]; int hits = Physics.RaycastNonAlloc(new Ray(targetBlock.transform.position, actual), results, actual.magnitude, Singleton.Manager <ManVisible> .inst.VisiblePickerMaskNoTechs, QueryTriggerInteraction.Ignore); List <TankBlock> foundBlocks = new List <TankBlock>(); if (!PatchDamage.DeterminedInvincible.ContainsKey(targetTank)) { PatchDamage.DeterminedInvincible[targetTank] = new HashSet <TankBlock>(); } for (int i = 0; i < hits; i++) { // ExplosionNerf.IngressPoint.DebugPrint("Loop " + i.ToString()); RaycastHit test = results[i]; Visible visible = Visible.FindVisibleUpwards((Component)test.collider); if ((UnityEngine.Object)visible != (UnityEngine.Object)null) { TankBlock block = visible.block; if (block != null && block != targetBlock && block.tank != null) { if (block.tank == targetTank) { // ExplosionNerf.IngressPoint.DebugPrint("Damage blocked"); if (PatchDamage.YetToHit.Contains(block)) { ExplosionNerf.IngressPoint.DebugPrint(prefix, " Unresolved block found: " + block.name); foundBlocks.Add(block); } else if (PatchDamage.DeterminedInvincible[targetTank].Contains(block)) { ExplosionNerf.IngressPoint.DebugPrint(prefix, " Resolved INVINCIBLE found"); PatchDamage.DeterminedInvincible[targetTank].Add(targetBlock); foundBlocks.Clear(); return(0.0f); } } } } } ExplosionNerf.IngressPoint.DebugPrint(prefix, "Raycast initial pass done - no invincible found in path"); if (foundBlocks.Count > 0) { bool breakInvincible = false; foreach (TankBlock block in foundBlocks) { if (PatchDamage.YetToHit.Contains(block)) { float destroyed = PatchDamage.recursiveHandleDamage(ref __instance, ref __result, block.GetComponent <Damageable>(), directHit, source, block, targetTank, prefix + "| "); if (destroyed == 0.0f) { PatchDamage.DeterminedInvincible[targetTank].Add(targetBlock); breakInvincible = true; break; } } else if (PatchDamage.DeterminedInvincible[targetTank].Contains(block)) { PatchDamage.DeterminedInvincible[targetTank].Add(targetBlock); breakInvincible = true; ExplosionNerf.IngressPoint.DebugPrint(prefix, "Resolved INVINCIBLE found"); break; } } if (!breakInvincible) { ExplosionNerf.IngressPoint.DebugPrint(prefix, "(A) [Should be rare] Resolve NOT invincible - calculate dmg"); ManDamage.DamageInfo damageInfo = PatchDamage.NewDamageInfo(directHit, damageTarget, targetBlock); ExplosionNerf.IngressPoint.DebugPrint(prefix, "Damage: " + damageInfo.Damage.ToString()); float dmgDone = PatchDamage.DoDamage(ref __instance, directHit, damageTarget, damageInfo); if (dmgDone == 0.0f) { ExplosionNerf.IngressPoint.DebugPrint(prefix, "Block NOT destroyed - mark invincible for now"); if (!PatchDamage.DeterminedInvincible.ContainsKey(targetTank)) { PatchDamage.DeterminedInvincible[targetTank] = new HashSet <TankBlock>(); } PatchDamage.DeterminedInvincible[targetTank].Add(targetBlock); } else { ExplosionNerf.IngressPoint.DebugPrint(prefix, "BLOCK DESTROYED - remove from consideration"); } return(dmgDone); } else { ExplosionNerf.IngressPoint.DebugPrint(prefix, "Resolved INVINCIBLE found"); return(0.0f); } } else { ExplosionNerf.IngressPoint.DebugPrint(prefix, "No blocks in way - Resolve NOT invincible - calculate dmg"); ManDamage.DamageInfo damageInfo = PatchDamage.NewDamageInfo(directHit, damageTarget, targetBlock); ExplosionNerf.IngressPoint.DebugPrint(prefix, "Damage: " + damageInfo.Damage.ToString()); float dmgDone = PatchDamage.DoDamage(ref __instance, directHit, damageTarget, damageInfo); if (dmgDone == 0.0f) { ExplosionNerf.IngressPoint.DebugPrint(prefix, "Block NOT destroyed - mark invincible for now"); if (!PatchDamage.DeterminedInvincible.ContainsKey(targetTank)) { PatchDamage.DeterminedInvincible[targetTank] = new HashSet <TankBlock>(); } PatchDamage.DeterminedInvincible[targetTank].Add(targetBlock); } else { ExplosionNerf.IngressPoint.DebugPrint(prefix, "BLOCK DESTROYED - remove from consideration"); } return(dmgDone); } } } ExplosionNerf.IngressPoint.DebugPrint(prefix, "END"); return(0.0f); }