private void CalculateBlast() { if (part.Resources.Contains("HighExplosive")) { if (part.Resources["HighExplosive"].amount == previousMass) { return; } tntMass = (float)(part.Resources["HighExplosive"].amount * part.Resources["HighExplosive"].info.density * 1000) * 1.5f; part.explosionPotential = tntMass / 10f; previousMass = part.Resources["HighExplosive"].amount; } blastRadius = BlastPhysicsUtils.CalculateBlastRange(tntMass); }
public static void CreateExplosion(Vector3 position, float tntMassEquivalent, string explModelPath, string soundPath, ExplosionSourceType explosionSourceType, float caliber = 0, Part explosivePart = null, string sourceVesselName = null, string sourceWeaponName = null, Vector3 direction = default(Vector3), bool isfx = false) { CreateObjectPool(explModelPath, soundPath); Quaternion rotation; if (direction == default(Vector3)) { rotation = Quaternion.LookRotation(VectorUtils.GetUpDirection(position)); } else { rotation = Quaternion.LookRotation(direction); } GameObject newExplosion = explosionFXPools[explModelPath + soundPath].GetPooledObject(); newExplosion.transform.SetPositionAndRotation(position, rotation); ExplosionFx eFx = newExplosion.GetComponent <ExplosionFx>(); eFx.Range = BlastPhysicsUtils.CalculateBlastRange(tntMassEquivalent); eFx.Position = position; eFx.Power = tntMassEquivalent; eFx.ExplosionSource = explosionSourceType; eFx.SourceVesselName = sourceVesselName != null ? sourceVesselName : explosionSourceType == ExplosionSourceType.Missile ? (explosivePart != null && explosivePart.vessel != null ? explosivePart.vessel.GetName() : null) : null; // Use the sourceVesselName if specified, otherwise get the sourceVesselName from the missile if it is one. eFx.SourceWeaponName = sourceWeaponName; eFx.Caliber = caliber; eFx.ExplosivePart = explosivePart; eFx.Direction = direction; eFx.isFX = isfx; eFx.pEmitters = newExplosion.GetComponentsInChildren <KSPParticleEmitter>(); eFx.audioSource = newExplosion.GetComponent <AudioSource>(); if (tntMassEquivalent <= 5) { eFx.audioSource.minDistance = 4f; eFx.audioSource.maxDistance = 3000; eFx.audioSource.priority = 9999; } newExplosion.SetActive(true); }
private void CalculateBlast() { foreach (PartResource resource in GetResources()) { var resources = part.Resources.ToList(); using (IEnumerator <PartResource> ammo = resources.GetEnumerator()) while (ammo.MoveNext()) { if (ammo.Current == null) { continue; } if (ammo.Current.resourceName == resource.resourceName) { ammoMass = ammo.Current.info.density; ammoQuantity = ammo.Current.amount; ammoExplosionYield += (((ammoMass * 1000) * ammoQuantity) / 6); } } } blastRadius = BlastPhysicsUtils.CalculateBlastRange(ammoExplosionYield); }
void Detonate() { if (!parentPart.partName.Contains("exploding")) { bool excessFuel = false; parentPart.partName += "exploding"; PartResource fuel = parentPart.Resources.Where(pr => pr.resourceName == "LiquidFuel").FirstOrDefault(); PartResource ox = parentPart.Resources.Where(pr => pr.resourceName == "Oxidizer").FirstOrDefault(); if (fuel != null) { tntMassEquivilent += Mathf.Clamp((float)fuel.amount, ((float)fuel.maxAmount * 0.05f), ((float)fuel.maxAmount * 0.2f)); if (fuel != null && ox != null) { tntMassEquivilent += Mathf.Clamp((float)ox.amount, ((float)ox.maxAmount * 0.1f), ((float)ox.maxAmount * 0.3f)); tntMassEquivilent *= 1.3f; } if (fuel.amount > fuel.maxAmount * 0.3f) { excessFuel = true; } } PartResource mp = parentPart.Resources.Where(pr => pr.resourceName == "MonoPropellant").FirstOrDefault(); if (mp != null) { tntMassEquivilent += Mathf.Clamp((float)mp.amount, ((float)mp.maxAmount * 0.1f), ((float)mp.maxAmount * 0.3f)); if (mp.amount > mp.maxAmount * 0.3f) { excessFuel = true; } } PartResource ec = parentPart.Resources.Where(pr => pr.resourceName == "ElectricCharge").FirstOrDefault(); if (ec != null) { tntMassEquivilent += ((float)ec.maxAmount / 5000); //fix for cockpit batteries weighing a tonne+ ec.maxAmount = 0; ec.isVisible = false; parentPart.RemoveResource(ec);//destroy battery. not calling part.destroy, since some batteries in cockpits. Misc.Misc.RefreshAssociatedWindows(parentPart); } if (excessFuel) { float blastRadius = BlastPhysicsUtils.CalculateBlastRange(tntMassEquivilent); using (var blastHits = Physics.OverlapSphere(parentPart.transform.position, blastRadius, 9076737).AsEnumerable().GetEnumerator()) { while (blastHits.MoveNext()) { if (blastHits.Current == null) { continue; } try { Part partHit = blastHits.Current.GetComponentInParent <Part>(); if (partHit != null && partHit.mass > 0) { Rigidbody rb = partHit.Rigidbody; Vector3 distToG0 = parentPart.transform.position - partHit.transform.position; Ray LoSRay = new Ray(parentPart.transform.position, partHit.transform.position - parentPart.transform.position); RaycastHit hit; if (Physics.Raycast(LoSRay, out hit, distToG0.magnitude, 9076737)) { KerbalEVA eva = hit.collider.gameObject.GetComponentUpwards <KerbalEVA>(); Part p = eva ? eva.part : hit.collider.gameObject.GetComponentInParent <Part>(); if (p == partHit) { if (rb == null) { return; } BulletHitFX.AttachFire(hit, p, 1, SourceVessel, 20); } } } } catch (Exception e) { Debug.LogWarning("[BDArmory.FireFX]: Exception thrown in Detonate: " + e.Message + "\n" + e.StackTrace); } } } } ExplosionFx.CreateExplosion(parentPart.transform.position, tntMassEquivilent, explModelPath, explSoundPath, ExplosionSourceType.Bullet, 0, null, parentPart.vessel != null ? parentPart.vessel.name : null, null); // needs to be Explosiontype Bullet since missile only returns Module MissileLauncher gameObject.SetActive(false); } }
public void Start() { // extension for feature_engagementenvelope InitializeEngagementRange(0, maxTargetingRange); SetupAudio(); if (HighLogic.LoadedSceneIsFlight) { part.force_activate(); aimerTexture = BDArmorySetup.Instance.greenPointCircleTexture; // GameDatabase.Instance.GetTexture("BDArmory/Textures/grayCircle", false); MakeRocketArray(); UpdateRocketScales(); if (shortName == string.Empty) { shortName = part.partInfo.title; } UpdateAudio(); BDArmorySetup.OnVolumeChange += UpdateAudio; gauge = (BDStagingAreaGauge)part.AddModule("BDStagingAreaGauge"); gauge.AmmoName = rocketType; gauge.AudioSource = sfAudioSource; } if (HighLogic.LoadedSceneIsFlight || HighLogic.LoadedSceneIsEditor) { List <ModuleTurret> .Enumerator turr = part.FindModulesImplementing <ModuleTurret>().GetEnumerator(); while (turr.MoveNext()) { if (turr.Current == null) { continue; } if (turr.Current.turretID != turretID) { continue; } turret = turr.Current; targetInTurretView = false; break; } turr.Dispose(); if (turret) { Events["GuiFire"].guiActive = false; Events["Jettison"].guiActive = false; Actions["AGFire"].active = false; if (HighLogic.LoadedSceneIsFlight) { Events["ToggleTurret"].guiActive = true; } } if (!string.IsNullOrEmpty(deployAnimationName)) { deployAnimState = Misc.Misc.SetUpSingleAnimation(deployAnimationName, part); hasDeployAnimation = true; readyToFire = false; } } blastForce = BlastPhysicsUtils.CalculateExplosiveMass(blastRadius); }
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 } }
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 } }