private void ExecutePartBlastEvent(PartBlastHitEvent eventToExecute) { if (eventToExecute.Part == null || eventToExecute.Part.Rigidbody == null || eventToExecute.Part.vessel == null || eventToExecute.Part.partInfo == null) { return; } try { Part part = eventToExecute.Part; Rigidbody rb = part.Rigidbody; var realDistance = eventToExecute.Distance; if (!eventToExecute.IsNegativePressure) { BlastInfo blastInfo = BlastPhysicsUtils.CalculatePartBlastEffects(part, realDistance, part.vessel.totalMass * 1000f, Power, Range); if (BDArmorySettings.DRAW_DEBUG_LABELS) { Debug.Log( "[BDArmory]: Executing blast event Part: {" + part.name + "}, " + " VelocityChange: {" + blastInfo.VelocityChange + "}," + " Distance: {" + realDistance + "}," + " TotalPressure: {" + blastInfo.TotalPressure + "}," + " Damage: {" + blastInfo.Damage + "}," + " EffectiveArea: {" + blastInfo.EffectivePartArea + "}," + " Positive Phase duration: {" + blastInfo.PositivePhaseDuration + "}," + " Vessel mass: {" + Math.Round(part.vessel.totalMass * 1000f) + "}," + " TimeIndex: {" + TimeIndex + "}," + " TimePlanned: {" + eventToExecute.TimeToImpact + "}," + " NegativePressure: {" + eventToExecute.IsNegativePressure + "}"); } // Add Reverse Negative Event ExplosionEvents.Enqueue(new PartBlastHitEvent() { Distance = Range - realDistance, Part = part, TimeToImpact = 2 * (Range / ExplosionVelocity) + (Range - realDistance) / ExplosionVelocity, IsNegativePressure = true, NegativeForce = blastInfo.VelocityChange * 0.25f }); AddForceAtPosition(rb, (eventToExecute.HitPoint + part.rb.velocity * TimeIndex - Position).normalized * blastInfo.VelocityChange * BDArmorySettings.EXP_IMP_MOD, eventToExecute.HitPoint + part.rb.velocity * TimeIndex); var damage = part.AddExplosiveDamage(blastInfo.Damage, Caliber, ExplosionSource); if (BDArmorySettings.BATTLEDAMAGE) { Misc.BattleDamageHandler.CheckDamageFX(part, 50, 0.5f, true, SourceVesselName, eventToExecute.Hit); } // Update scoring structures switch (ExplosionSource) { case ExplosionSourceType.Bullet: case ExplosionSourceType.Missile: var aName = eventToExecute.SourceVesselName; // Attacker var tName = part.vessel.GetName(); // Target if (aName != tName && BDACompetitionMode.Instance.Scores.ContainsKey(tName) && BDACompetitionMode.Instance.Scores.ContainsKey(aName)) { var tData = BDACompetitionMode.Instance.Scores[tName]; // Track damage switch (ExplosionSource) { case ExplosionSourceType.Bullet: if (tData.damageFromBullets.ContainsKey(aName)) { tData.damageFromBullets[aName] += damage; } else { tData.damageFromBullets.Add(aName, damage); } if (BDArmorySettings.REMOTE_LOGGING_ENABLED) { BDAScoreService.Instance.TrackDamage(aName, tName, damage); } break; case ExplosionSourceType.Missile: if (tData.damageFromMissiles.ContainsKey(aName)) { tData.damageFromMissiles[aName] += damage; } else { tData.damageFromMissiles.Add(aName, damage); } if (BDArmorySettings.REMOTE_LOGGING_ENABLED) { BDAScoreService.Instance.TrackMissileDamage(aName, tName, damage); } break; default: break; } } break; default: break; } } else { if (BDArmorySettings.DRAW_DEBUG_LABELS) { Debug.Log( "[BDArmory]: Executing blast event Part: {" + part.name + "}, " + " VelocityChange: {" + eventToExecute.NegativeForce + "}," + " Distance: {" + realDistance + "}," + " Vessel mass: {" + Math.Round(part.vessel.totalMass * 1000f) + "}," + " TimeIndex: {" + TimeIndex + "}," + " TimePlanned: {" + eventToExecute.TimeToImpact + "}," + " NegativePressure: {" + eventToExecute.IsNegativePressure + "}"); } AddForceAtPosition(rb, (Position - part.transform.position).normalized * eventToExecute.NegativeForce * BDArmorySettings.EXP_IMP_MOD * 0.25f, part.transform.position); } } catch { // ignored due to depending on previous event an object could be disposed } }
void Detonate() //borrowed from Stockalike Project Orion { if (hasDetonated || FlightGlobals.currentMainBody == null || VesselSpawner.Instance.vesselsSpawning) // Don't trigger on scene changes or during spawning. { return; } if (BDArmorySettings.DRAW_DEBUG_LABELS) { Debug.Log("[BDArmory.NukeTest]: Running Detonate() on nerva in vessel " + Sourcevessel); } //affect any nearby parts/vessels that aren't the source vessel Dictionary <string, int> vesselsHitByMissiles = new Dictionary <string, int>(); using (var blastHits = Physics.OverlapSphere(part.transform.position, thermalRadius, 9076737).AsEnumerable().GetEnumerator()) { partsHit.Clear(); while (blastHits.MoveNext()) { if (blastHits.Current == null) { continue; } if (blastHits.Current.gameObject == FlightGlobals.currentMainBody.gameObject) { continue; // Ignore terrain hits. } Part partHit = blastHits.Current.GetComponentInParent <Part>(); if (partsHit.Contains(partHit)) { continue; // Don't hit the same part multiple times. } partsHit.Add(partHit); if (partHit != null && partHit.mass > 0) { var distToG0 = Math.Max((part.transform.position - partHit.transform.position).magnitude, 1f); float radiativeArea = !double.IsNaN(partHit.radiativeArea) ? (float)partHit.radiativeArea : partHit.GetArea(); if (BDArmorySettings.DRAW_DEBUG_LABELS && double.IsNaN(partHit.radiativeArea)) { Debug.Log("[BDArmory.NukeTest]: radiative area of part " + partHit + " was NaN, using approximate area " + radiativeArea + " instead."); } //if (partHit.vessel != this.vessel) if (partHit != part) { partHit.skinTemperature += fluence * 3370000000 / (4 * Math.PI * Math.Pow(distToG0, 2.0)) * radiativeArea / 2; // Fluence scales linearly w/ yield, 1 Kt will produce between 33 TJ and 337 kJ at 0-1000m, } // everything gets heated via atmosphere Ray LoSRay = new Ray(part.transform.position, partHit.transform.position - part.transform.position); RaycastHit hit; if (Physics.Raycast(LoSRay, out hit, distToG0, 9076737)) // only add impulse to parts with line of sight to detonation { KerbalEVA eva = hit.collider.gameObject.GetComponentUpwards <KerbalEVA>(); Part p = eva ? eva.part : hit.collider.gameObject.GetComponentInParent <Part>(); float blastDamage = 100; if (p == partHit) { //if (p.vessel != this.vessel) if (p != part) { // Forces if (p.rb != null && p.rb.mass > 0) // Don't apply forces to physicsless parts. { var blastImpulse = Mathf.Pow(3.01f * 1100f / distToG0, 1.25f) * 6.894f * lastValidAtmDensity * yieldCubeRoot * radiativeArea / 3f; // Math.Pow(Math.Pow(Math.Pow(9.54e-3 * 2200.0 / distToG0, 1.95), 4.0) + Math.Pow(Math.Pow(3.01 * 1100.0 / distToG0, 1.25), 4.0), 0.25) * 6.894 * vessel.atmDensity * Math.Pow(yield, 1.0 / 3.0) * partHit.radiativeArea / 3.0; //assuming a 0.05 kT yield if (float.IsNaN(blastImpulse)) { Debug.LogWarning("[BDArmory.NukeTest]: blast impulse is NaN. distToG0: " + distToG0 + ", vessel: " + vessel + ", atmDensity: " + lastValidAtmDensity + ", yield^(1/3): " + yieldCubeRoot + ", partHit: " + partHit + ", radiativeArea: " + radiativeArea); } else { if (BDArmorySettings.DRAW_DEBUG_LABELS) { Debug.Log("[BDArmory.NukeTest]: Applying " + blastImpulse.ToString("0.0") + " impulse to " + p + " of mass " + p.mass + " at distance " + distToG0 + "m"); } p.rb.AddForceAtPosition((partHit.transform.position - part.transform.position).normalized * (float)blastImpulse, partHit.transform.position, ForceMode.Impulse); } } // Damage blastDamage = ((float)((yield * 3370000000) / (4f * Mathf.PI * distToG0 * distToG0) * (radiativeArea / 2f))); if (float.IsNaN(blastDamage)) { Debug.LogWarning("[BDArmory.NukeTest]: blast damage is NaN. distToG0: " + distToG0 + ", yield: " + yield + ", part: " + partHit + ", radiativeArea: " + radiativeArea); continue; } p.AddExplosiveDamage(blastDamage, 100, ExplosionSourceType.Missile); // Scoring var aName = Sourcevessel; // Attacker var tName = p.vessel.GetName(); // Target if (tName != null && aName != tName && BDACompetitionMode.Instance.Scores.ContainsKey(tName) && BDACompetitionMode.Instance.Scores.ContainsKey(aName)) { // Part hit counts if (BDACompetitionMode.Instance.Scores[tName].missilePartDamageCounts.ContainsKey(aName)) { ++BDACompetitionMode.Instance.Scores[tName].missilePartDamageCounts[aName]; } else { BDACompetitionMode.Instance.Scores[tName].missilePartDamageCounts[aName] = 1; } if (!BDACompetitionMode.Instance.Scores[tName].everyoneWhoHitMeWithMissiles.Contains(aName)) { BDACompetitionMode.Instance.Scores[tName].everyoneWhoHitMeWithMissiles.Add(aName); } ++BDACompetitionMode.Instance.Scores[aName].totalDamagedPartsDueToMissiles; BDACompetitionMode.Instance.Scores[tName].lastMissileHitTime = Planetarium.GetUniversalTime(); BDACompetitionMode.Instance.Scores[tName].lastPersonWhoHitMeWithAMissile = aName; if (vesselsHitByMissiles.ContainsKey(tName)) { ++vesselsHitByMissiles[tName]; } else { vesselsHitByMissiles[tName] = 1; } if (BDArmorySettings.REMOTE_LOGGING_ENABLED) { BDAScoreService.Instance.TrackMissileParts(aName, tName, 1); } // Part damage scoring var tData = BDACompetitionMode.Instance.Scores[tName]; if (tData.damageFromMissiles.ContainsKey(aName)) { tData.damageFromMissiles[aName] += blastDamage; } else { tData.damageFromMissiles.Add(aName, blastDamage); } if (BDArmorySettings.REMOTE_LOGGING_ENABLED) { BDAScoreService.Instance.TrackMissileDamage(aName, tName, blastDamage); } if (BDArmorySettings.DRAW_DEBUG_LABELS) { Debug.Log("[BDArmory.NukeTest]: " + aName + " did " + blastDamage + " blast damage to " + tName + " at " + distToG0.ToString("0.000") + "m"); } } } } } } else { DestructibleBuilding building = blastHits.Current.GetComponentInParent <DestructibleBuilding>(); if (building != null) { var distToEpicenter = Mathf.Max((part.transform.position - building.transform.position).magnitude, 1f); var blastImpulse = Mathf.Pow(3.01f * 1100f / distToEpicenter, 1.25f) * 6.894f * lastValidAtmDensity * yieldCubeRoot; // blastImpulse = (((((Math.Pow((Math.Pow((Math.Pow((9.54 * Math.Pow(10.0, -3.0) * (2200.0 / distToEpicenter)), 1.95)), 4.0) + Math.Pow((Math.Pow((3.01 * (1100.0 / distToEpicenter)), 1.25)), 4.0)), 0.25)) * 6.894) * (vessel.atmDensity)) * Math.Pow(yield, (1.0 / 3.0)))); if (!double.IsNaN(blastImpulse) && blastImpulse > 140) //140kPa, level at which reinforced concrete structures are destroyed { building.Demolish(); } } } } } if (vesselsHitByMissiles.Count > 0) { string message = ""; foreach (var vesselName in vesselsHitByMissiles.Keys) { message += (message == "" ? "" : " and ") + vesselName + " had " + vesselsHitByMissiles[vesselName]; } message += " parts damaged (Blast Wave) by " + Sourcevessel + "'s exploding engine core."; BDACompetitionMode.Instance.competitionStatus.Add(message); Debug.Log("[BDArmory.NukeTest]: " + message); } ExplosionFx.CreateExplosion(part.transform.position, 1, explModelPath, explSoundPath, ExplosionSourceType.Missile, 0, null, Sourcevessel, "Reactor Containment Failure"); hasDetonated = true; if (part.vessel != null) // Already in the process of being destroyed. { part.Destroy(); } }
private void ExecutePartBlastEvent(PartBlastHitEvent eventToExecute) { if (eventToExecute.Part == null || eventToExecute.Part.Rigidbody == null || eventToExecute.Part.vessel == null || eventToExecute.Part.partInfo == null) { return; } Part part = eventToExecute.Part; Rigidbody rb = part.Rigidbody; var realDistance = eventToExecute.Distance; if (!eventToExecute.IsNegativePressure) { BlastInfo blastInfo = BlastPhysicsUtils.CalculatePartBlastEffects(part, realDistance, part.vessel.totalMass * 1000f, Power, Range); // Overly simplistic approach: simply reduce damage by amount of HP/2 and Armour in the way. (HP/2 to simulate weak parts not fully blocking damage.) Does not account for armour reduction or angle of incidence of intermediate parts. // A better approach would be to properly calculate the damage and pressure in CalculatePartBlastEffects due to the series of parts in the way. var damageWithoutIntermediateParts = blastInfo.Damage; var cumulativeHPOfIntermediateParts = eventToExecute.IntermediateParts.Select(p => p.Item2).Sum(); var cumulativeArmourOfIntermediateParts = eventToExecute.IntermediateParts.Select(p => p.Item3).Sum(); blastInfo.Damage = Mathf.Max(0f, blastInfo.Damage - 0.5f * cumulativeHPOfIntermediateParts - cumulativeArmourOfIntermediateParts); if (blastInfo.Damage > 0) { if (BDArmorySettings.DRAW_DEBUG_LABELS) { Debug.Log( "[BDArmory.ExplosionFX]: Executing blast event Part: {" + part.name + "}, " + " VelocityChange: {" + blastInfo.VelocityChange + "}," + " Distance: {" + realDistance + "}," + " TotalPressure: {" + blastInfo.TotalPressure + "}," + " Damage: {" + blastInfo.Damage + "} (reduced from " + damageWithoutIntermediateParts + " by " + eventToExecute.IntermediateParts.Count + " parts)," + " EffectiveArea: {" + blastInfo.EffectivePartArea + "}," + " Positive Phase duration: {" + blastInfo.PositivePhaseDuration + "}," + " Vessel mass: {" + Math.Round(part.vessel.totalMass * 1000f) + "}," + " TimeIndex: {" + TimeIndex + "}," + " TimePlanned: {" + eventToExecute.TimeToImpact + "}," + " NegativePressure: {" + eventToExecute.IsNegativePressure + "}"); } // Add Reverse Negative Event ExplosionEvents.Enqueue(new PartBlastHitEvent() { Distance = Range - realDistance, Part = part, TimeToImpact = 2 * (Range / ExplosionVelocity) + (Range - realDistance) / ExplosionVelocity, IsNegativePressure = true, NegativeForce = blastInfo.VelocityChange * 0.25f }); if (rb != null && rb.mass > 0) { AddForceAtPosition(rb, (eventToExecute.HitPoint + rb.velocity * TimeIndex - Position).normalized * blastInfo.VelocityChange * BDArmorySettings.EXP_IMP_MOD, eventToExecute.HitPoint + rb.velocity * TimeIndex); } var damage = part.AddExplosiveDamage(blastInfo.Damage, Caliber, ExplosionSource); if (BDArmorySettings.BATTLEDAMAGE) { Misc.BattleDamageHandler.CheckDamageFX(part, 50, 0.5f, true, SourceVesselName, eventToExecute.Hit); } // Update scoring structures switch (ExplosionSource) { case ExplosionSourceType.Bullet: case ExplosionSourceType.Missile: var aName = eventToExecute.SourceVesselName; // Attacker var tName = part.vessel.GetName(); // Target if (aName != tName && BDACompetitionMode.Instance.Scores.ContainsKey(tName) && BDACompetitionMode.Instance.Scores.ContainsKey(aName)) { var tData = BDACompetitionMode.Instance.Scores[tName]; // Track damage switch (ExplosionSource) { case ExplosionSourceType.Bullet: if (tData.damageFromBullets.ContainsKey(aName)) { tData.damageFromBullets[aName] += damage; } else { tData.damageFromBullets.Add(aName, damage); } if (BDArmorySettings.REMOTE_LOGGING_ENABLED) { BDAScoreService.Instance.TrackDamage(aName, tName, damage); } break; case ExplosionSourceType.Missile: if (tData.damageFromMissiles.ContainsKey(aName)) { tData.damageFromMissiles[aName] += damage; } else { tData.damageFromMissiles.Add(aName, damage); } if (BDArmorySettings.REMOTE_LOGGING_ENABLED) { BDAScoreService.Instance.TrackMissileDamage(aName, tName, damage); } break; default: break; } } break; default: break; } } else if (BDArmorySettings.DRAW_DEBUG_LABELS) { Debug.Log("[BDArmory.ExplosiveFX]: Part " + part.name + " at distance " + realDistance + "m took no damage due to parts with " + cumulativeHPOfIntermediateParts + "HP and " + cumulativeArmourOfIntermediateParts + " Armour in the way."); } } else { if (BDArmorySettings.DRAW_DEBUG_LABELS) { Debug.Log( "[BDArmory.ExplosionFX]: Executing blast event Part: {" + part.name + "}, " + " VelocityChange: {" + eventToExecute.NegativeForce + "}," + " Distance: {" + realDistance + "}," + " Vessel mass: {" + Math.Round(part.vessel.totalMass * 1000f) + "}," + " TimeIndex: {" + TimeIndex + "}," + " TimePlanned: {" + eventToExecute.TimeToImpact + "}," + " NegativePressure: {" + eventToExecute.IsNegativePressure + "}"); } if (rb != null && rb.mass > 0) { AddForceAtPosition(rb, (Position - part.transform.position).normalized * eventToExecute.NegativeForce * BDArmorySettings.EXP_IMP_MOD * 0.25f, part.transform.position); } } }
private void ExecutePartBlastEvent(PartBlastHitEvent eventToExecute) { if (eventToExecute.Part == null || eventToExecute.Part.Rigidbody == null || eventToExecute.Part.vessel == null || eventToExecute.Part.partInfo == null) { return; } try { Part part = eventToExecute.Part; Rigidbody rb = part.Rigidbody; var realDistance = eventToExecute.Distance; if (!eventToExecute.IsNegativePressure) { BlastInfo blastInfo = BlastPhysicsUtils.CalculatePartBlastEffects(part, realDistance, part.vessel.totalMass * 1000f, Power, Range); if (BDArmorySettings.DRAW_DEBUG_LABELS) { Debug.Log( "[BDArmory]: Executing blast event Part: {" + part.name + "}, " + " VelocityChange: {" + blastInfo.VelocityChange + "}," + " Distance: {" + realDistance + "}," + " TotalPressure: {" + blastInfo.TotalPressure + "}," + " Damage: {" + blastInfo.Damage + "}," + " EffectiveArea: {" + blastInfo.EffectivePartArea + "}," + " Positive Phase duration: {" + blastInfo.PositivePhaseDuration + "}," + " Vessel mass: {" + Math.Round(part.vessel.totalMass * 1000f) + "}," + " TimeIndex: {" + TimeIndex + "}," + " TimePlanned: {" + eventToExecute.TimeToImpact + "}," + " NegativePressure: {" + eventToExecute.IsNegativePressure + "}"); } // Add Reverse Negative Event ExplosionEvents.Enqueue(new PartBlastHitEvent() { Distance = Range - realDistance, Part = part, TimeToImpact = 2 * (Range / ExplosionVelocity) + (Range - realDistance) / ExplosionVelocity, IsNegativePressure = true, NegativeForce = blastInfo.VelocityChange * 0.25f }); AddForceAtPosition(rb, (eventToExecute.HitPoint + part.rb.velocity * TimeIndex - Position).normalized * blastInfo.VelocityChange * BDArmorySettings.EXP_IMP_MOD, eventToExecute.HitPoint + part.rb.velocity * TimeIndex); part.AddExplosiveDamage(blastInfo.Damage, Caliber, IsMissile); } else { if (BDArmorySettings.DRAW_DEBUG_LABELS) { Debug.Log( "[BDArmory]: Executing blast event Part: {" + part.name + "}, " + " VelocityChange: {" + eventToExecute.NegativeForce + "}," + " Distance: {" + realDistance + "}," + " Vessel mass: {" + Math.Round(part.vessel.totalMass * 1000f) + "}," + " TimeIndex: {" + TimeIndex + "}," + " TimePlanned: {" + eventToExecute.TimeToImpact + "}," + " NegativePressure: {" + eventToExecute.IsNegativePressure + "}"); } AddForceAtPosition(rb, (Position - part.transform.position).normalized * eventToExecute.NegativeForce * BDArmorySettings.EXP_IMP_MOD * 0.25f, part.transform.position); } } catch { // ignored due to depending on previous event an object could be disposed } }