public virtual void Explode(Thing instigator, Vector3 pos, Map map, float scaleFactor = 1, float?direction = null, List <Thing> ignoredThings = null) { var posIV = pos.ToIntVec3(); if (map == null) { Log.Warning("Tried to do explodeCE in a null map."); return; } if (!posIV.InBounds(map)) { Log.Warning("Tried to explodeCE out of bounds"); return; } //Try to throw fragments -- increase count by scaleFactor parent.TryGetComp <CompFragments>()?.Throw(pos, map, instigator);//scaleFactor); if (Props.explosiveRadius > 0 && //&& Props.damageAmountBase > 0 Disabled to allow flame explosions etc parent.def != null) { //Call GenExplosionCE for main explosion GenExplosionCE.DoExplosion(posIV, map, Props.explosiveRadius, Props.explosiveDamageType, instigator, GenMath.RoundRandom(Props.damageAmountBase), Props.damageAmountBase * 0.1f, Props.explosionSound, null, parent.def, null, Props.postExplosionSpawnThingDef, Props.postExplosionSpawnChance, Props.postExplosionSpawnThingCount, Props.applyDamageToExplosionCellsNeighbors, Props.preExplosionSpawnThingDef, Props.preExplosionSpawnChance, Props.preExplosionSpawnThingCount, Props.chanceToStartFire, Props.damageFalloff, direction, ignoredThings, pos.y, scaleFactor); } }
private bool TryDetonate(float stackCountScale = 1) { if (Find.Maps.IndexOf(Map) < 0) { return(false); } CompExplosiveCE comp = this.TryGetComp <CompExplosiveCE>(); var detProps = AmmoDef?.detonateProjectile?.projectile; if (comp != null || detProps != null) { if (Rand.Chance(Mathf.Clamp01(0.75f - Mathf.Pow(HitPoints / MaxHitPoints, 2)))) { if (comp != null) { comp.Explode(this, Position.ToVector3Shifted(), Map, Mathf.Pow(stackCountScale, 0.333f), null, new List <Thing>() { this }); } else { this.TryGetComp <CompFragments>()?.Throw(Position.ToVector3Shifted(), Map, this); //Mathf.Pow(scale, 0.333f)); } if (detProps != null) { GenExplosionCE.DoExplosion(Position, Map, detProps.explosionRadius, detProps.damageDef, this, detProps.GetDamageAmount(1), GenExplosionCE.GetExplosionAP(detProps), detProps.soundExplode, null, def, null, detProps.postExplosionSpawnThingDef, detProps.postExplosionSpawnChance, detProps.postExplosionSpawnThingCount, detProps.applyDamageToExplosionCellsNeighbors, detProps.preExplosionSpawnThingDef, detProps.preExplosionSpawnChance, detProps.preExplosionSpawnThingCount, detProps.explosionChanceToStartFire, detProps.explosionDamageFalloff, null, new List <Thing>() { this }, 0f, Mathf.Pow(stackCountScale, 0.333f)); } } return(true); } return(false); }
protected virtual void Impact(Thing hitThing) { var ignoredThings = new List <Thing>(); //Spawn things from preExplosionSpawnThingDef != null if (Position.IsValid && def.projectile.preExplosionSpawnChance > 0 && def.projectile.preExplosionSpawnThingDef != null && (Controller.settings.EnableAmmoSystem || !(def.projectile.preExplosionSpawnThingDef is AmmoDef)) && Rand.Value < def.projectile.preExplosionSpawnChance) { var thingDef = def.projectile.preExplosionSpawnThingDef; if (thingDef.IsFilth && Position.Walkable(Map)) { FilthMaker.TryMakeFilth(Position, Map, thingDef); } else if (Controller.settings.ReuseNeolithicProjectiles) { var reusableAmmo = ThingMaker.MakeThing(thingDef); reusableAmmo.stackCount = 1; reusableAmmo.SetForbidden(true, false); GenPlace.TryPlaceThing(reusableAmmo, Position, Map, ThingPlaceMode.Near); LessonAutoActivator.TeachOpportunity(CE_ConceptDefOf.CE_ReusableNeolithicProjectiles, reusableAmmo, OpportunityType.GoodToKnow); ignoredThings.Add(reusableAmmo); } } var explodePos = hitThing?.DrawPos ?? ExactPosition; if (!explodePos.ToIntVec3().IsValid) { Destroy(); return; } var explodingComp = this.TryGetComp <CompExplosiveCE>(); if (explodingComp == null) { this.TryGetComp <CompFragments>()?.Throw(explodePos, Map, launcher); } //If the comp exists, it'll already call CompFragments if (explodingComp != null || def.projectile.explosionRadius > 0) { //Handle anything explosive if (hitThing is Pawn && (hitThing as Pawn).Dead) { ignoredThings.Add((hitThing as Pawn).Corpse); } var suppressThings = new List <Pawn>(); var dir = new float?(origin.AngleTo(Vec2Position())); // Opt-out for things without explosionRadius if (def.projectile.explosionRadius > 0) { GenExplosionCE.DoExplosion(explodePos.ToIntVec3(), Map, def.projectile.explosionRadius, def.projectile.damageDef, launcher, def.projectile.GetDamageAmount(1), def.projectile.GetDamageAmount(1) * 0.1f, def.projectile.soundExplode, equipmentDef, def, null, def.projectile.postExplosionSpawnThingDef, def.projectile.postExplosionSpawnChance, def.projectile.postExplosionSpawnThingCount, def.projectile.applyDamageToExplosionCellsNeighbors, def.projectile.preExplosionSpawnThingDef, def.projectile.preExplosionSpawnChance, def.projectile.preExplosionSpawnThingCount, def.projectile.explosionChanceToStartFire, def.projectile.explosionDamageFalloff, dir, ignoredThings, explodePos.y); // Apply suppression around impact area if (explodePos.y < SuppressionRadius) { suppressThings.AddRange(GenRadial.RadialDistinctThingsAround(explodePos.ToIntVec3(), Map, SuppressionRadius + def.projectile.explosionRadius, true) .Where(x => x is Pawn).Select(x => x as Pawn)); } } if (explodingComp != null) { explodingComp.Explode(this, explodePos, Map, 1f, dir, ignoredThings); if (explodePos.y < SuppressionRadius) { suppressThings.AddRange(GenRadial.RadialDistinctThingsAround(explodePos.ToIntVec3(), Map, SuppressionRadius + (explodingComp.props as CompProperties_ExplosiveCE).explosiveRadius, true) .Where(x => x is Pawn).Select(x => x as Pawn)); } } foreach (var thing in suppressThings) { ApplySuppression(thing as Pawn); } } Destroy(); }
/// <summary> /// Generates a readout text for a projectile with the damage amount, type, secondary explosion and other CE stats for /// display in info-box /// </summary> /// <param name="projectileDef">The projectile's ThingDef</param> /// <returns>Formatted string listing projectile stats</returns> public static string GetProjectileReadout(this ThingDef projectileDef, Thing weapon) { // Append ammo stats var props = projectileDef?.projectile as ProjectilePropertiesCE; if (props == null) { Log.Error("CE tried getting projectile readout with null props"); return(""); } var stringBuilder = new StringBuilder(); // Damage type/amount var dmgList = " " + "CE_DescDamage".Translate() + ": "; if (!props.secondaryDamage.NullOrEmpty()) { // If we have multiple damage types, put every one in its own line stringBuilder.AppendLine(dmgList); stringBuilder.AppendLine(" " + GenText.ToStringByStyle(props.GetDamageAmount(weapon), ToStringStyle.Integer) + " (" + props.damageDef.LabelCap + ")"); foreach (var sec in props.secondaryDamage) { var secondaryChance = sec.chance >= 1.0f ? "" : $"({GenText.ToStringByStyle(sec.chance, ToStringStyle.PercentZero)} {"CE_Chance".Translate()})"; stringBuilder.AppendLine($" {GenText.ToStringByStyle(sec.amount, ToStringStyle.Integer)} ({sec.def.LabelCap}) {secondaryChance}"); } } else { stringBuilder.AppendLine(dmgList + GenText.ToStringByStyle(props.GetDamageAmount(weapon), ToStringStyle.Integer) + " (" + props.damageDef.LabelCap + ")"); } // Explosion radius if (props.explosionRadius > 0) { stringBuilder.AppendLine(" " + "CE_DescExplosionRadius".Translate() + ": " + props.explosionRadius.ToStringByStyle(ToStringStyle.FloatOne)); } // Sharp / blunt AP if (props.explosionRadius > 0) { if (props.damageDef.armorCategory != CE_DamageArmorCategoryDefOf.Heat && props.damageDef.armorCategory != CE_DamageArmorCategoryDefOf.Electric && props.damageDef != DamageDefOf.Stun && props.damageDef != DamageDefOf.Extinguish && props.damageDef != DamageDefOf.Smoke) { stringBuilder.AppendLine(" " + "CE_DescBluntPenetration".Translate() + ": " + GenExplosionCE.GetExplosionAP(props) + " " + "CE_MPa".Translate()); } } else { stringBuilder.AppendLine(" " + "CE_DescSharpPenetration".Translate() + ": " + props.armorPenetrationSharp.ToStringByStyle(ToStringStyle.FloatTwo) + " " + "CE_mmRHA".Translate()); stringBuilder.AppendLine(" " + "CE_DescBluntPenetration".Translate() + ": " + props.armorPenetrationBlunt.ToStringByStyle(ToStringStyle.FloatTwo) + " " + "CE_MPa".Translate()); } // Secondary explosion var secExpProps = projectileDef.GetCompProperties <CompProperties_ExplosiveCE>(); if (secExpProps != null) { if (secExpProps.explosiveRadius > 0) { stringBuilder.AppendLine(" " + "CE_DescSecondaryExplosion".Translate() + ":"); stringBuilder.AppendLine(" " + " " + "CE_DescDamage".Translate() + ": " + secExpProps.damageAmountBase.ToStringByStyle(ToStringStyle.Integer) + " (" + secExpProps.explosiveDamageType.LabelCap + ")"); stringBuilder.AppendLine(" " + " " + "CE_DescExplosionRadius".Translate() + ": " + secExpProps.explosiveRadius.ToStringByStyle(ToStringStyle.FloatOne)); } } // Pellets if (props.pelletCount > 1) { stringBuilder.AppendLine(" " + "CE_DescPelletCount".Translate() + ": " + GenText.ToStringByStyle(props.pelletCount, ToStringStyle.Integer)); } if (props.spreadMult != 1) { stringBuilder.AppendLine(" " + "CE_DescSpreadMult".Translate() + ": " + props.spreadMult.ToStringByStyle(ToStringStyle.PercentZero)); } // Fragments var fragmentComp = projectileDef.GetCompProperties <CompProperties_Fragments>(); if (fragmentComp != null) { stringBuilder.AppendLine(" " + "CE_DescFragments".Translate() + ":"); foreach (var fragmentDef in fragmentComp.fragments) { var fragmentProps = fragmentDef?.thingDef?.projectile as ProjectilePropertiesCE; stringBuilder.AppendLine(" " + " " + fragmentDef.LabelCap); stringBuilder.AppendLine(" " + " " + " " + "CE_DescSharpPenetration".Translate() + ": " + fragmentProps?.armorPenetrationSharp.ToStringByStyle(ToStringStyle.FloatTwo) + " " + "CE_mmRHA".Translate()); stringBuilder.AppendLine(" " + " " + " " + "CE_DescBluntPenetration".Translate() + ": " + fragmentProps?.armorPenetrationBlunt.ToStringByStyle(ToStringStyle.FloatTwo) + " " + "CE_MPa".Translate()); } } return(stringBuilder.ToString()); }