void Update() { if (HighLogic.LoadedSceneIsFlight && FlightGlobals.ready && vessel != null && !vessel.packed) { if (this.part.temperature > 493) //autoignition temp of kerosene is 220 c { var isOnFire = part.GetComponentInChildren <FireFX>(); if (isOnFire == null) { string fireStarter; var vesselFire = part.vessel.GetComponentInChildren <FireFX>(); if (vesselFire != null) { fireStarter = vesselFire.SourceVessel; } else { fireStarter = part.vessel.GetName(); } Vector3 firePosition = part.transform.up * 10; Ray LoSRay = new Ray(transform.position, (transform.position + firePosition) - transform.position); RaycastHit hit; if (Physics.Raycast(LoSRay, out hit, 10, 9076737)) // only add fires to parts in LoS of blast { BulletHitFX.AttachFire(hit, part, 50, fireStarter); } Debug.Log("[SelfSealingTank] Fuel auto-ignition! " + part.name + " is on fire!"); } } } }
public static void CheckDamageFX(Part part, float caliber, float penetrationFactor, bool explosivedamage, string attacker, RaycastHit hitLoc) { if (!BDArmorySettings.BATTLEDAMAGE || BDArmorySettings.PAINTBALL_MODE) { return; } double damageChance = Mathf.Clamp((BDArmorySettings.BD_DAMAGE_CHANCE * ((1 - part.GetDamagePercentage()) * 10) * (penetrationFactor / 2)), 0, 100); //more heavily damaged parts more likely to take battledamage if (BDArmorySettings.BD_TANKS) { if (part.HasFuel()) { var alreadyburning = part.GetComponentInChildren <FireFX>(); var rubbertank = part.FindModuleImplementing <ModuleSelfSealingTank>(); if (rubbertank != null) { if (rubbertank.SSTank && part.GetDamagePercentage() > 0.66f) { return; } } if (penetrationFactor > 1.2) { if (alreadyburning != null) { BulletHitFX.AttachFire(hitLoc, part, caliber, attacker); } else { BulletHitFX.AttachLeak(hitLoc, part, caliber, explosivedamage, attacker); } } } } if (BDArmorySettings.BD_FIRES_ENABLED) { if (part.isBattery()) { var alreadyburning = part.GetComponentInChildren <FireFX>(); if (alreadyburning == null) { double Diceroll = UnityEngine.Random.Range(0, 100); if (explosivedamage) { Diceroll *= 0.66; } if (BDArmorySettings.DRAW_DEBUG_LABELS) { Debug.Log("[BD Debug]: Battery Dice Roll: " + Diceroll); } if (Diceroll <= BDArmorySettings.BD_DAMAGE_CHANCE) { BulletHitFX.AttachFire(hitLoc, part, caliber, attacker); } } } } //AmmoBins if (BDArmorySettings.BD_AMMOBINS && penetrationFactor > 1.2 && part.GetDamagePercentage() < 0.9f) //explosions have penetration of 0.5, should stop explosions phasing though parts from detonating ammo { var ammo = part.FindModuleImplementing <ModuleCASE>(); if (ammo != null) { double Diceroll = UnityEngine.Random.Range(0, 100); if (BDArmorySettings.DRAW_DEBUG_LABELS) { Debug.Log("[BD Debug]: Ammo TAC DiceRoll: " + Diceroll + "; needs: " + damageChance); } if (Diceroll <= (damageChance) && part.GetDamagePercentage() < 0.95f) { ammo.SourceVessel = attacker; ammo.DetonateIfPossible(); } } } //Propulsaion Damage if (BDArmorySettings.BD_PROPULSION) { if (part.isEngine() && part.GetDamagePercentage() < 0.95f) //first hit's free { foreach (var engine in part.GetComponentsInChildren <ModuleEngines>()) { if (engine.thrustPercentage > 20) //engines take thrust damage per hit { //engine.maxThrust -= ((engine.maxThrust * 0.125f) / 100); // doesn't seem to adjust thrust; investigate //engine.thrustPercentage -= ((engine.maxThrust * 0.125f) / 100); //workaround hack engine.thrustPercentage -= (((1 - part.GetDamagePercentage()) * (penetrationFactor / 2)) * BDArmorySettings.BD_PROP_DAM_RATE); //AP does bonus damage Mathf.Clamp(engine.thrustPercentage, 15f, 100); //even heavily damaged engines will still put out something if (BDArmorySettings.DRAW_DEBUG_LABELS) { Debug.Log("[BD Debug]: engine thrust: " + engine.thrustPercentage); } /* * float enginelevel = engine.thrustPercentage; * if (BDArmorySettings.BD_BALANCED_THRUST) //need to poke this more later, not working properly * { * using (List<Part>.Enumerator pSym = part.symmetryCounterparts.GetEnumerator()) * while (pSym.MoveNext()) * { * if (pSym.Current == null) continue; * if (pSym.Current != part && pSym.Current.vessel == part.vessel) * { * var symEngine = pSym.Current.FindModuleImplementing<ModuleEngines>(); * if (symEngine != null) * { * symEngine.thrustPercentage = enginelevel; * } * } * } * } */ } if (part.GetDamagePercentage() < 0.75f || (part.GetDamagePercentage() < 0.82f && penetrationFactor > 2)) { var leak = part.GetComponentInChildren <FuelLeakFX>(); if (leak == null) { BulletHitFX.AttachLeak(hitLoc, part, caliber, explosivedamage, attacker); } } if (part.GetDamagePercentage() < 0.50f || (part.GetDamagePercentage() < 0.625f && penetrationFactor > 2)) { var alreadyburning = part.GetComponentInChildren <FireFX>(); if (alreadyburning == null) { BulletHitFX.AttachFire(hitLoc, part, caliber, attacker); } } if (part.GetDamagePercentage() < 0.25f) { if (engine.EngineIgnited) { engine.PlayFlameoutFX(true); engine.Shutdown(); //kill a badly damaged engine and don't allow restart engine.allowRestart = false; } } } } if (BDArmorySettings.BD_INTAKES) //intake damage { var intake = part.FindModuleImplementing <ModuleResourceIntake>(); if (intake != null) { float HEBonus = 0.7f; if (explosivedamage) { HEBonus = 1.4f; } intake.intakeSpeed *= (1 - (((1 - part.GetDamagePercentage()) * HEBonus) / BDArmorySettings.BD_PROP_DAM_RATE)); //HE does bonus damage Mathf.Clamp((float)intake.intakeSpeed, 0, 99999); intake.area *= (1 - (((1 - part.GetDamagePercentage()) * HEBonus) / BDArmorySettings.BD_PROP_DAM_RATE)); //HE does bonus damage Mathf.Clamp((float)intake.area, 0.0002f, 99999); //even shredded intake ducting will still get some air to engines if (BDArmorySettings.DRAW_DEBUG_LABELS) { Debug.Log("[BD Debug]: Intake damage: Current Area: " + intake.area + "; Intake Speed: " + intake.intakeSpeed); } } } if (BDArmorySettings.BD_GIMBALS) //engine gimbal damage { var gimbal = part.FindModuleImplementing <ModuleGimbal>(); if (gimbal != null) { double HEBonus = 1; if (explosivedamage) { HEBonus = 1.5; } //gimbal.gimbalRange *= (1 - (((1 - part.GetDamagePercentatge()) * HEBonus) / BDArmorySettings.BD_PROP_DAM_RATE)); //HE does bonus damage double Diceroll = UnityEngine.Random.Range(0, 100); if (BDArmorySettings.DRAW_DEBUG_LABELS) { Debug.Log("[BD Debug]: Gimbal DiceRoll: " + Diceroll); } if (Diceroll <= (BDArmorySettings.BD_DAMAGE_CHANCE * HEBonus)) { gimbal.enabled = false; gimbal.gimbalRange = 0; } } } } //Aero Damage if (BDArmorySettings.BD_AEROPARTS) { float HEBonus = 1; if (explosivedamage) { HEBonus = 2; //explosive rounds blow bigger holes in wings } Mathf.Clamp(penetrationFactor, 0.1f, 3); HEBonus /= penetrationFactor; //faster rounds punch cleaner holes float liftDam = ((caliber / 20000) * HEBonus) * BDArmorySettings.BD_LIFT_LOSS_RATE; if (part.GetComponent <ModuleLiftingSurface>() != null) { ModuleLiftingSurface wing; wing = part.GetComponent <ModuleLiftingSurface>(); //2x4m wing board = 2 Lift, 0.25 Lift/m2. 20mm round = 20*20=400/20000= 0.02 Lift reduced per hit, 100 rounds to reduce lift to 0. mind you, it only takes ~15 rounds to destroy the wing... if (wing.deflectionLiftCoeff > ((part.mass * 5) + liftDam)) //stock mass/lift ratio is 10; 0.2t wing has 2.0 lift; clamp lift lost at half { wing.deflectionLiftCoeff -= liftDam; } if (BDArmorySettings.DRAW_DEBUG_LABELS) { Debug.Log("[BD DEBUG] " + part.name + "took lift damage: " + liftDam + ", current lift: " + wing.deflectionLiftCoeff); } } if (part.GetComponent <ModuleControlSurface>() != null && part.GetDamagePercentage() > 0.125f) { ModuleControlSurface aileron; aileron = part.GetComponent <ModuleControlSurface>(); if (aileron.deflectionLiftCoeff > ((part.mass * 2.5f) + liftDam)) //stock ctrl surface mass/lift ratio is 5 { aileron.deflectionLiftCoeff -= liftDam; } if (BDArmorySettings.BD_CTRL_SRF) { int Diceroll = (int)UnityEngine.Random.Range(0f, 100f); if (explosivedamage) { HEBonus = 1.2f; } if (Diceroll <= (BDArmorySettings.BD_DAMAGE_CHANCE * HEBonus)) { aileron.actuatorSpeed = 0; aileron.authorityLimiter = 0; aileron.ctrlSurfaceRange = 0; } } } } //Subsystems if (BDArmorySettings.BD_SUBSYSTEMS) { double Diceroll = UnityEngine.Random.Range(0, 100); if (BDArmorySettings.DRAW_DEBUG_LABELS) { Debug.Log("[BD Debug]: Subsystem DiceRoll: " + Diceroll + "; needs: " + damageChance); } if (Diceroll <= (damageChance) && part.GetDamagePercentage() < 0.95f) { if (part.GetComponent <ModuleReactionWheel>() != null) //should have this be separate dice rolls, else a part with more than one of these will lose them all { ModuleReactionWheel SAS; //could have torque reduced per hit SAS = part.GetComponent <ModuleReactionWheel>(); part.RemoveModule(SAS); } if (part.GetComponent <ModuleRadar>() != null) { ModuleRadar radar; //would need to mod detection curve to degrade performance on hit radar = part.GetComponent <ModuleRadar>(); part.RemoveModule(radar); } if (part.GetComponent <ModuleAlternator>() != null) { ModuleAlternator alt; //damaging alternator is probably just petty. Could reduce output per hit alt = part.GetComponent <ModuleAlternator>(); part.RemoveModule(alt); } if (part.GetComponent <ModuleAnimateGeneric>() != null) { ModuleAnimateGeneric anim; anim = part.GetComponent <ModuleAnimateGeneric>(); // could reduce anim speed, open percent per hit part.RemoveModule(anim); } if (part.GetComponent <ModuleDecouple>() != null) { ModuleDecouple stage; stage = part.GetComponent <ModuleDecouple>(); //decouplers decouple stage.Decouple(); } if (part.GetComponent <ModuleECMJammer>() != null) { ModuleECMJammer ecm; ecm = part.GetComponent <ModuleECMJammer>(); //could reduce ecm strngth/rcs modifier part.RemoveModule(ecm); } if (part.GetComponent <ModuleGenerator>() != null) { ModuleGenerator gen; gen = part.GetComponent <ModuleGenerator>(); part.RemoveModule(gen); } if (part.GetComponent <ModuleResourceConverter>() != null) { ModuleResourceConverter isru; isru = part.GetComponent <ModuleResourceConverter>(); //could reduce efficiency, increase heat per hit part.RemoveModule(isru); } if (part.GetComponent <ModuleResourceConverter>() != null) { ModuleTurret turret; turret = part.GetComponent <ModuleTurret>(); //could reduce traverse speed, range per hit part.RemoveModule(turret); } if (part.GetComponent <ModuleTargetingCamera>() != null) { ModuleTargetingCamera cam; cam = part.GetComponent <ModuleTargetingCamera>(); // gimbal range?? part.RemoveModule(cam); } if (BDArmorySettings.DRAW_DEBUG_LABELS) { Debug.Log("[BD DEBUG] " + part.name + "took subsystem damage"); } } } //Command parts if (BDArmorySettings.BD_COCKPITS && penetrationFactor > 1 && part.GetDamagePercentage() < 0.9f) //lets have this be triggered by penetrative damage, not blast splash { if (part.GetComponent <ModuleCommand>() != null) { double ControlDiceRoll = UnityEngine.Random.Range(0, 100); if (BDArmorySettings.DRAW_DEBUG_LABELS) { Debug.Log("[BD Debug]: Command DiceRoll: " + ControlDiceRoll); } if (ControlDiceRoll <= (BDArmorySettings.BD_DAMAGE_CHANCE * 2)) { using (List <Part> .Enumerator craftPart = part.vessel.parts.GetEnumerator()) { using (List <BDModulePilotAI> .Enumerator control = part.vessel.FindPartModulesImplementing <BDModulePilotAI>().GetEnumerator()) while (control.MoveNext()) { if (control.Current == null) { continue; } control.Current.evasionThreshold += 10; //pilot jitteriness increases control.Current.maxSteer *= 0.9f; if (control.Current.steerDamping > 0.625f) //damage to controls { control.Current.steerDamping -= 0.125f; } if (control.Current.dynamicSteerDampingPitchFactor > 0.625f) { control.Current.dynamicSteerDampingPitchFactor -= 0.125f; } if (control.Current.dynamicSteerDampingRollFactor > 0.625f) { control.Current.dynamicSteerDampingRollFactor -= 0.125f; } if (control.Current.dynamicSteerDampingYawFactor > 0.625f) { control.Current.dynamicSteerDampingYawFactor -= 0.125f; } } //GuardRange reduction to sim canopy/sensor damage? if (BDArmorySettings.DRAW_DEBUG_LABELS) { Debug.Log("[BD DEBUG] " + part.name + "took command damage"); } } } } } if (part.protoModuleCrew.Count > 0 && penetrationFactor > 1 && part.GetDamagePercentage() < 0.95f) { if (BDArmorySettings.BD_PILOT_KILLS) { float PilotTAC = Mathf.Clamp((BDArmorySettings.BD_DAMAGE_CHANCE / part.mass), 0.01f, 100); //larger cockpits = greater volume = less chance any hit will pass through a region of volume containing a pilot float killchance = UnityEngine.Random.Range(0, 100); if (BDArmorySettings.DRAW_DEBUG_LABELS) { Debug.Log("[BD Debug]: Pilot TAC: " + PilotTAC + "; dice roll: " + killchance); } if (killchance <= PilotTAC) //add penetrationfactor threshold? hp threshold? { ProtoCrewMember crewMember = part.protoModuleCrew.FirstOrDefault(x => x != null); if (crewMember != null) { crewMember.UnregisterExperienceTraits(part); //crewMember.outDueToG = true; //implement temp KO to simulate wounding? crewMember.Die(); if (part.IsKerbalEVA()) { part.Die(); } else { part.RemoveCrewmember(crewMember); // sadly, I wasn't able to get the K.I.A. portrait working } //Vessel.CrewWasModified(part.vessel); //Debug.Log(crewMember.name + " was killed by damage to cabin!"); if (HighLogic.CurrentGame.Parameters.Difficulty.MissingCrewsRespawn) { crewMember.StartRespawnPeriod(); } //ScreenMessages.PostScreenMessage(crewMember.name + " killed by damage to " + part.vessel.name + part.partName + ".", 5.0f, ScreenMessageStyle.UPPER_LEFT); ScreenMessages.PostScreenMessage("Cockpit snipe! " + crewMember.name + " killed!", 5.0f, ScreenMessageStyle.UPPER_LEFT); } } } } }
void Detonate(Vector3 pos, bool missed) { if (!missed) { if (tntMass > 0) { Vector3 direction = default(Vector3); if (shaped) { direction = (pos + rb.velocity * Time.deltaTime).normalized; } if (gravitic) { using (var hitsEnu = Physics.OverlapSphere(transform.position, blastRadius, 557057).AsEnumerable().GetEnumerator()) { while (hitsEnu.MoveNext()) { if (hitsEnu.Current == null) { continue; } Part partHit = hitsEnu.Current.GetComponentInParent <Part>(); if (partHit == null) { continue; } if (ProjectileUtils.IsIgnoredPart(partHit)) { continue; // Ignore ignored parts. } float distance = Vector3.Distance(transform.position, partHit.transform.position); if (gravitic) { if (partHit.mass > 0) { var ME = partHit.vessel.rootPart.FindModuleImplementing <ModuleMassAdjust>(); if (ME == null) { ME = (ModuleMassAdjust)partHit.vessel.rootPart.AddModule("ModuleMassAdjust"); } ME.massMod += (massMod * (1 - (distance / blastRadius))); //this way craft at edge of blast might only get disabled instead of bricked ME.duration += (BDArmorySettings.WEAPON_FX_DURATION * (1 - (distance / blastRadius))); //can bypass EMP damage cap } } } } } if (incendiary) { for (int f = 0; f < 20; f++) //throw 20 random raytraces out in a sphere and see what gets tagged { Ray LoSRay = new Ray(transform.position, VectorUtils.GaussianDirectionDeviation(transform.forward, 170)); RaycastHit hit; if (Physics.Raycast(LoSRay, out hit, blastRadius * 1.2f, 9076737)) // only add fires to parts in LoS of blast { KerbalEVA eva = hit.collider.gameObject.GetComponentUpwards <KerbalEVA>(); Part p = eva ? eva.part : hit.collider.gameObject.GetComponentInParent <Part>(); float distance = Vector3.Distance(transform.position, hit.point); if (p != null) { BulletHitFX.AttachFire(hit, p, caliber, sourceVesselName, BDArmorySettings.WEAPON_FX_DURATION * (1 - (distance / blastRadius)), 1, false, true); //else apply fire to occluding part if (BDArmorySettings.DRAW_DEBUG_LABELS) { Debug.Log("[BDArmory.Rocket]: Applying fire to " + p.name + " at distance " + distance + "m, for " + BDArmorySettings.WEAPON_FX_DURATION * (1 - (distance / blastRadius)) + " seconds"); } ; } } if (BDArmorySettings.DRAW_DEBUG_LABELS) { Debug.Log("[Rocket] incendiary raytrace: " + hit.point.x + "; " + hit.point.y + "; " + hit.point.z); } } } if (concussion || EMP || choker) { using (var hitsEnu = Physics.OverlapSphere(transform.position, 25, 557057).AsEnumerable().GetEnumerator()) { var craftHit = new HashSet <Vessel>(); while (hitsEnu.MoveNext()) { if (hitsEnu.Current == null) { continue; } if (hitsEnu.Current.gameObject == FlightGlobals.currentMainBody.gameObject) { continue; // Ignore terrain hits. } Part partHit = hitsEnu.Current.GetComponentInParent <Part>(); if (partHit == null) { continue; } if (ProjectileUtils.IsIgnoredPart(partHit)) { continue; // Ignore ignored parts. } if (craftHit.Contains(partHit.vessel)) { continue; // Don't hit the same craft multiple times. } craftHit.Add(partHit.vessel); float Distance = Vector3.Distance(partHit.transform.position, this.transform.position); if (partHit != null) { if (concussion && partHit.mass > 0) { partHit.rb.AddForceAtPosition((partHit.transform.position - this.transform.position).normalized * impulse, partHit.transform.position, ForceMode.Acceleration); } if (EMP) { var MDEC = partHit.vessel.rootPart.FindModuleImplementing <ModuleDrainEC>(); if (MDEC == null) { MDEC = (ModuleDrainEC)partHit.vessel.rootPart.AddModule("ModuleDrainEC"); } MDEC.incomingDamage += ((25 - Distance) * 5); //this way craft at edge of blast might only get disabled instead of bricked MDEC.softEMP = false; //can bypass EMP damage cap } if (choker) { var ash = partHit.vessel.rootPart.FindModuleImplementing <ModuleDrainIntakes>(); if (ash == null) { ash = (ModuleDrainIntakes)partHit.vessel.rootPart.AddModule("ModuleDrainIntakes"); } ash.drainDuration += BDArmorySettings.WEAPON_FX_DURATION * (1 - (Distance / 25)); //reduce intake knockout time based on distance from epicenter } } } } ExplosionFx.CreateExplosion(pos, tntMass, explModelPath, explSoundPath, ExplosionSourceType.Bullet, caliber, null, sourceVesselName, null, direction, true); } else { ExplosionFx.CreateExplosion(pos, tntMass, explModelPath, explSoundPath, ExplosionSourceType.Bullet, caliber, null, sourceVesselName, null, direction); } } } // needs to be Explosiontype Bullet since missile only returns Module MissileLauncher gameObject.SetActive(false); }