using System; using System.Collections.Generic; using System.Reflection; using RimWorld; using UnityEngine; using Verse; using Verse.AI; using Verse.Sound; namespace WulaFallenEmpire { public class Projectile_Homing : Bullet { public HomingProjectileDef HomingDef { get { bool flag = this.homingDefInt == null; if (flag) { this.homingDefInt = this.def.GetModExtension(); } return this.homingDefInt; } } public override void Launch(Thing launcher, Vector3 origin, LocalTargetInfo usedTarget, LocalTargetInfo intendedTarget, ProjectileHitFlags hitFlags, bool preventFriendlyFire = false, Thing equipment = null, ThingDef targetCoverDef = null) { bool flag = false; bool flag2 = usedTarget.HasThing && usedTarget.Thing is IAttackTarget; if (flag2) { bool flag3 = Rand.Chance(this.GetHitChance(usedTarget.Thing)); if (flag3) { hitFlags |= ProjectileHitFlags.IntendedTarget; intendedTarget = usedTarget; flag = true; } } else { bool flag4 = Rand.Chance(this.GetHitChance(intendedTarget.Thing)); if (flag4) { hitFlags |= ProjectileHitFlags.IntendedTarget; usedTarget = intendedTarget; flag = true; } } bool flag5 = flag; if (flag5) { hitFlags &= ~ProjectileHitFlags.IntendedTarget; } base.Launch(launcher, origin, usedTarget, intendedTarget, hitFlags, preventFriendlyFire, equipment, targetCoverDef); this.exactPositionInt = origin.Yto0() + Vector3.up * this.def.Altitude; Vector3 normalized = (this.destination - origin).Yto0().normalized; float degrees = Rand.Range(-this.HomingDef.initRotateAngle, this.HomingDef.initRotateAngle); Vector2 vector = new Vector2(normalized.x, normalized.z); vector = vector.RotatedBy(degrees); Vector3 a = new Vector3(vector.x, 0f, vector.y); bool flag6 = this.HomingDef.speedRangeOverride == null; if (flag6) { this.curSpeed = a * this.def.projectile.SpeedTilesPerTick; } else { this.curSpeed = a * this.HomingDef.SpeedRangeTilesPerTickOverride.RandomInRange; } this.ticksToImpact = int.MaxValue; this.lifetime = int.MaxValue; this.ReflectInit(); } protected void ReflectInit() { bool flag = !this.def.projectile.soundAmbient.NullOrUndefined(); if (flag) { this.ambientSustainer = (Sustainer)NonPublicFields.Projectile_AmbientSustainer.GetValue(this); } this.comps = (List)NonPublicFields.ThingWithComps_comps.GetValue(this); } public float GetHitChance(Thing thing) { float num = this.HomingDef.hitChance; bool flag = thing == null; float result; if (flag) { result = num; } else { Pawn pawn = thing as Pawn; bool flag2 = pawn != null; if (flag2) { num *= Mathf.Clamp(pawn.BodySize, 0.5f, 1.5f); bool flag3 = pawn.GetPosture() > PawnPosture.Standing; if (flag3) { num *= 0.5f; } float num2 = 1f; switch (this.equipmentQuality) { case QualityCategory.Awful: num2 = 0.5f; goto IL_DD; case QualityCategory.Poor: num2 = 0.75f; goto IL_DD; case QualityCategory.Normal: num2 = 1f; goto IL_DD; case QualityCategory.Excellent: num2 = 1.1f; goto IL_DD; case QualityCategory.Masterwork: num2 = 1.2f; goto IL_DD; case QualityCategory.Legendary: num2 = 1.3f; goto IL_DD; } Log.Message("Unknown QualityCategory, returning default qualityFactor = 1"); IL_DD: num *= num2; } else { num *= 1.5f * thing.def.fillPercent; } result = Mathf.Clamp(num, 0f, 1f); } return result; } public override Vector3 ExactPosition { get { return this.exactPositionInt; } } public override Quaternion ExactRotation { get { return Quaternion.LookRotation(this.curSpeed); } } public virtual void MovementTick() { Vector3 vect = this.ExactPosition + this.curSpeed; ShootLine shootLine = new ShootLine(this.ExactPosition.ToIntVec3(), vect.ToIntVec3()); Vector3 vector = (this.intendedTarget.Cell.ToVector3() - this.ExactPosition).Yto0(); bool flag = this.homing; if (flag) { Vector3 a = vector.normalized - this.curSpeed.normalized; bool flag2 = a.sqrMagnitude >= 1.414f; if (flag2) { this.homing = false; this.lifetime = this.HomingDef.destroyTicksAfterLosingTrack.RandomInRange; this.ticksToImpact = this.lifetime; base.HitFlags &= ~ProjectileHitFlags.IntendedTarget; base.HitFlags |= ProjectileHitFlags.NonTargetPawns; base.HitFlags |= ProjectileHitFlags.NonTargetWorld; } else { this.curSpeed += a * this.HomingDef.homingSpeed * this.curSpeed.magnitude; } } foreach (IntVec3 b in shootLine.Points()) { bool flag3 = (this.intendedTarget.Cell - b).SqrMagnitude <= this.HomingDef.proximityFuseRange * this.HomingDef.proximityFuseRange; if (flag3) { this.homing = false; this.lifetime = this.HomingDef.destroyTicksAfterLosingTrack.RandomInRange; bool flag4 = (base.HitFlags & ProjectileHitFlags.IntendedTarget) == ProjectileHitFlags.IntendedTarget || this.HomingDef.proximityFuseRange > 0f; if (flag4) { this.lifetime = 0; this.ticksToImpact = 0; vect = b.ToVector3(); bool flag5 = Find.TickManager.CurTimeSpeed == TimeSpeed.Normal && this.def.projectile.soundImpactAnticipate != null; if (flag5) { this.def.projectile.soundImpactAnticipate.PlayOneShot(this); } } } } this.exactPositionInt = vect; this.curSpeed *= (this.curSpeed.magnitude + this.HomingDef.SpeedChangeTilesPerTickOverride) / this.curSpeed.magnitude; } protected override void Tick() { this.ThingWithCompsTick(); this.lifetime--; if (this.HomingDef.tailFleckDef != null) { FleckMaker.Static(this.ExactPosition, base.Map, this.HomingDef.tailFleckDef, 1f); } bool landed = this.landed; if (!landed) { Vector3 exactPosition = this.ExactPosition; this.ticksToImpact--; this.MovementTick(); bool flag = !this.ExactPosition.InBounds(base.Map); if (flag) { base.Position = exactPosition.ToIntVec3(); this.Destroy(DestroyMode.Vanish); } else { Vector3 exactPosition2 = this.ExactPosition; object[] parameters = new object[] { exactPosition, exactPosition2 }; bool flag2 = (bool)Projectile_Homing.ProjectileCheckForFreeInterceptBetween.Invoke(this, parameters); if (!flag2) { base.Position = this.ExactPosition.ToIntVec3(); bool flag3 = this.ticksToImpact == 60 && Find.TickManager.CurTimeSpeed == TimeSpeed.Normal && this.def.projectile.soundImpactAnticipate != null; if (flag3) { this.def.projectile.soundImpactAnticipate.PlayOneShot(this); } bool flag4 = this.ticksToImpact <= 0; if (flag4) { this.ImpactSomething(); } else { bool flag5 = this.ambientSustainer != null; if (flag5) { this.ambientSustainer.Maintain(); } } } } } } private void ThingWithCompsTick() { bool flag = this.comps != null; if (flag) { int i = 0; int count = this.comps.Count; while (i < count) { this.comps[i].CompTick(); i++; } } } protected override void Impact(Thing hitThing, bool blockedByShield = false) { Map map = base.Map; IntVec3 position = base.Position; base.Impact(hitThing, blockedByShield); bool flag = this.HomingDef.extraProjectile != null; if (flag) { bool flag2 = hitThing != null && hitThing.Spawned; if (flag2) { ((Projectile)GenSpawn.Spawn(this.HomingDef.extraProjectile, base.Position, map, WipeMode.Vanish)).Launch(this.launcher, this.ExactPosition, hitThing, hitThing, ProjectileHitFlags.All, false, null, null); } else { ((Projectile)GenSpawn.Spawn(this.HomingDef.extraProjectile, base.Position, map, WipeMode.Vanish)).Launch(this.launcher, this.ExactPosition, position, position, ProjectileHitFlags.All, false, null, null); } } } public override void ExposeData() { base.ExposeData(); Scribe_Values.Look(ref this.exactPositionInt, "exactPosition", default(Vector3), false); Scribe_Values.Look(ref this.curSpeed, "curSpeed", default(Vector3), false); Scribe_Values.Look(ref this.homing, "homing", false, false); bool flag = Scribe.mode == LoadSaveMode.PostLoadInit; if (flag) { this.ReflectInit(); } } private HomingProjectileDef homingDefInt; private Sustainer ambientSustainer; private List comps; protected Vector3 exactPositionInt; public Vector3 curSpeed; public bool homing = true; private static MethodInfo ProjectileCheckForFreeInterceptBetween = typeof(Projectile).GetMethod("CheckForFreeInterceptBetween", BindingFlags.Instance | BindingFlags.NonPublic); } }