// Verse.Verb // Token: 0x060022E2 RID: 8930 RVA: 0x000D4A4C File Offset: 0x000D2C4C public new bool TryFindShootLineFromTo(IntVec3 root, LocalTargetInfo targ, out ShootLine resultingLine) { if (targ.HasThing && targ.Thing.Map != this.Caster.Map) { resultingLine = default(ShootLine); return(false); } if (this.verbProps.IsMeleeAttack || this.EffectiveRange <= 1.42f) { resultingLine = new ShootLine(root, targ.Cell); return(ReachabilityImmediate.CanReachImmediate(root, targ, this.Caster.Map, PathEndMode.Touch, null)); } CellRect cellRect = targ.HasThing ? targ.Thing.OccupiedRect() : CellRect.SingleCell(targ.Cell); float num = this.verbProps.EffectiveMinRange(targ, this.Caster); float num2 = cellRect.ClosestDistSquaredTo(root); if (num2 > this.EffectiveRange * this.EffectiveRange || num2 < num * num) { resultingLine = new ShootLine(root, targ.Cell); return(false); } if (!this.verbProps.requireLineOfSight) { resultingLine = new ShootLine(root, targ.Cell); return(true); } if (this.CasterIsPawn) { if (this.CanHitFromCellIgnoringRange(root, targ, out IntVec3 dest)) { resultingLine = new ShootLine(root, dest); return(true); } ShootLeanUtility.LeanShootingSourcesFromTo(root, cellRect.ClosestCellTo(root), this.Caster.Map, Verb_ShootCompMountedCE.tempLeanShootSources); for (int i = 0; i < Verb_ShootCompMountedCE.tempLeanShootSources.Count; i++) { IntVec3 intVec = Verb_ShootCompMountedCE.tempLeanShootSources[i]; if (this.CanHitFromCellIgnoringRange(intVec, targ, out dest)) { resultingLine = new ShootLine(intVec, dest); return(true); } } } else { IntVec2 size = new IntVec2(Caster.def.size.x + 1, Caster.def.size.z + 1); foreach (IntVec3 intVec2 in GenAdj.OccupiedRect(Caster.Position, Caster.Rotation, size)) { if (this.CanHitFromCellIgnoringRange(intVec2, targ, out IntVec3 dest)) { resultingLine = new ShootLine(intVec2, dest); return(true); } } } resultingLine = new ShootLine(root, targ.Cell); return(false); }
public bool TryFindCEShootLineFromTo(IntVec3 root, LocalTargetInfo targ, out ShootLine resultingLine) { if (targ.HasThing && targ.Thing.Map != caster.Map) { resultingLine = default(ShootLine); return(false); } if (verbProps.range <= ShootTuning.MeleeRange) // If this verb has a MAX range up to melee range (NOT a MIN RANGE!) { resultingLine = new ShootLine(root, targ.Cell); return(ReachabilityImmediate.CanReachImmediate(root, targ, caster.Map, PathEndMode.Touch, null)); } CellRect cellRect = (!targ.HasThing) ? CellRect.SingleCell(targ.Cell) : targ.Thing.OccupiedRect(); float num = cellRect.ClosestDistSquaredTo(root); if (num > verbProps.range * verbProps.range || num < verbProps.minRange * verbProps.minRange) { resultingLine = new ShootLine(root, targ.Cell); return(false); } //if (!this.verbProps.NeedsLineOfSight) This method doesn't consider the currently loaded projectile if (Projectile.projectile.flyOverhead) { resultingLine = new ShootLine(root, targ.Cell); return(true); } // First check current cell for early opt-out IntVec3 dest; var shotSource = root.ToVector3Shifted(); shotSource.y = ShotHeight; if (CanHitFromCellIgnoringRange(shotSource, targ, out dest)) { resultingLine = new ShootLine(root, dest); return(true); } // For pawns, calculate possible lean locations if (CasterIsPawn) { // Next check lean sources ShootLeanUtility.LeanShootingSourcesFromTo(root, cellRect.ClosestCellTo(root), caster.Map, tempLeanShootSources); foreach (var leanLoc in tempLeanShootSources) { var leanOffset = (leanLoc - root).ToVector3() * 0.5f; if (CanHitFromCellIgnoringRange(shotSource + leanOffset, targ, out dest)) { resultingLine = new ShootLine(leanLoc, dest); return(true); } } } resultingLine = new ShootLine(root, targ.Cell); return(false); }
private static Building GetCorrectlyRotatedChairAt(IntVec3 x, Map map, CellRect spectateRect) { if (!x.InBounds(map)) { return(null); } Building edifice = x.GetEdifice(map); if (edifice == null || edifice.def.category != ThingCategory.Building || !edifice.def.building.isSittable) { return(null); } if (GenGeo.AngleDifferenceBetween(edifice.Rotation.AsAngle, (spectateRect.ClosestCellTo(x) - edifice.Position).AngleFlat) > 75f) { return(null); } return(edifice); }
public static bool HasLoSFromTo(IntVec3 root, LocalTargetInfo targ, Thing caster, float minRange, float maxRange) { float range = (targ.Cell - root).LengthHorizontal; if (targ.HasThing && targ.Thing.Map != caster.Map) { return(false); } if (range <= minRange || range >= maxRange) { return(false); } CellRect cellRect = (!targ.HasThing) ? CellRect.SingleCell(targ.Cell) : targ.Thing.OccupiedRect(); if (caster is Pawn) { if (GenSight.LineOfSight(caster.Position, targ.Cell, caster.Map, skipFirstCell: true)) { return(true); } List <IntVec3> tempLeanShootSources = new List <IntVec3>(); ShootLeanUtility.LeanShootingSourcesFromTo(root, cellRect.ClosestCellTo(root), caster.Map, tempLeanShootSources); for (int i = 0; i < tempLeanShootSources.Count; i++) { IntVec3 intVec = tempLeanShootSources[i]; if (GenSight.LineOfSight(intVec, targ.Cell, caster.Map, skipFirstCell: true)) { return(true); } } } else { if (GenSight.LineOfSight(root, targ.Cell, caster.Map, skipFirstCell: true)) { return(true); } } return(false); }
/* * public bool CanSeeTarget(LocalTargetInfo targ) * { * var glow = targ.Thing.Map.glowGrid.GameGlowAt(targ.Cell); * var glowmultiplier = Mathf.Lerp(1, 5, Mathf.Clamp(glow * 5f, 0f, 1f)); * * float lengthToTarget = Mathf.Abs((targ.Cell - caster.Position).LengthHorizontal); * if (targ.Thing.Map.glowGrid.GameGlowAt(targ.Cell) <= 0.2f) * { * Log.Message("target: " + targ.Thing + " glow: " + glow.ToString() + " glowmult: " + glowmultiplier.ToString()); * if (lengthToTarget > glowmultiplier) * { * Log.Message("cant see: " + targ.Thing.ToString()); * return false; * } * } * return true; * } */ public bool TryFindCEShootLineFromTo(IntVec3 root, LocalTargetInfo targ, out ShootLine resultingLine) { // Log.Message("root: " + root.ToString() + " target: " + targ.Thing.ToString() + " pos: " + targ.Cell.ToString()); if (targ.HasThing && targ.Thing.Map != this.caster.Map) { resultingLine = default(ShootLine); // Log.Message("shootline false: 1" + " line: " + resultingLine.ToString()); return(false); } // Log.Message("effminrange: " + this.verbProps.EffectiveMinRange(targ, this.caster).ToString() + " meleerange: " + ShootTuning.MeleeRange); if (this.verbProps.IsMeleeAttack) { resultingLine = new ShootLine(root, targ.Cell); var ri = ReachabilityImmediate.CanReachImmediate(root, targ, this.caster.Map, PathEndMode.Touch, null); // Log.Message("ri: " + ri.ToString() + " line: " + resultingLine.ToString()); return(ri); } CellRect cellRect = (!targ.HasThing) ? CellRect.SingleCell(targ.Cell) : targ.Thing.OccupiedRect(); float num = cellRect.ClosestDistSquaredTo(root); if (num > this.verbProps.range * this.verbProps.range || num < this.verbProps.minRange * this.verbProps.minRange) { resultingLine = new ShootLine(root, targ.Cell); // Log.Message("shootline false: 2" + " line: " + resultingLine.ToString()); return(false); } //if (!this.verbProps.NeedsLineOfSight) This method doesn't consider the currently loaded projectile if (Projectile.projectile.flyOverhead) { resultingLine = new ShootLine(root, targ.Cell); // Log.Message("shootline true: 3" + " line: " + resultingLine.ToString()); return(true); } if (this.CasterIsPawn) { IntVec3 dest; ShootLeanUtility.LeanShootingSourcesFromTo(root, cellRect.ClosestCellTo(root), this.caster.Map, tempLeanShootSources); if (tempLeanShootSources.Count < 2) { if (this.CanHitFromCellIgnoringRange(root, root, targ, out dest)) { resultingLine = new ShootLine(root, dest); // Log.Message("shootline true: 4" + " line: " + resultingLine.ToString()); return(true); } } if (tempLeanShootSources.Count >= 2) { int bestZ = 0; int bestX = 0; int cachedbz = -1; int cachedbx = -1; // Log.Message("pawn: " + CasterPawn.ToString() + " pawn pos: " + root.ToString() + " target: " + targ.Thing.ToString() + " target pos: " + targ.Cell.ToString()); foreach (var bsp in tempLeanShootSources) { var bz = Mathf.Abs(bsp.z - targ.Cell.z); if (cachedbz < 0 || (bz < cachedbz)) { cachedbz = bz; bestZ = bsp.z; } var bx = Mathf.Abs(bsp.x - targ.Cell.x); if (cachedbx < 0 || (bx < cachedbx)) { cachedbx = bx; bestX = bsp.x; } } bestshootposition = new IntVec3(bestX, root.y, bestZ); // Log.Message("bestshootposition: " + bestshootposition.ToString()); if (this.CanHitFromCellIgnoringRange(bestshootposition, root, targ, out dest)) { resultingLine = new ShootLine(bestshootposition, dest); // Log.Message("shootline true: 5" + " line: " + resultingLine.ToString()); return(true); } } else { for (int i = 0; i < tempLeanShootSources.Count; i++) { IntVec3 intVec = tempLeanShootSources[i]; if (this.CanHitFromCellIgnoringRange(intVec, root, targ, out dest)) { resultingLine = new ShootLine(intVec, dest); // Log.Message("shootline true: 6" + " line: " + resultingLine.ToString()); return(true); } } } } else { CellRect.CellRectIterator iterator = this.caster.OccupiedRect().GetIterator(); while (!iterator.Done()) { IntVec3 current = iterator.Current; IntVec3 dest; if (this.CanHitFromCellIgnoringRange(current, root, targ, out dest)) { resultingLine = new ShootLine(current, dest); // Log.Message("shootline true: 7: " + " line: " + resultingLine.ToString()); return(true); } iterator.MoveNext(); } } resultingLine = new ShootLine(root, targ.Cell); // Log.Message("shootline false: 8" + " line: " + resultingLine.ToString()); return(false); }
public static bool TryFindSpectatorCellFor(Pawn p, CellRect spectateRect, Map map, out IntVec3 cell, SpectateRectSide allowedSides = SpectateRectSide.All, int margin = 1, List <IntVec3> extraDisallowedCells = null) { spectateRect.ClipInsideMap(map); if (spectateRect.Area == 0 || allowedSides == SpectateRectSide.None) { cell = IntVec3.Invalid; return(false); } CellRect rectWithMargin = spectateRect.ExpandedBy(margin).ClipInsideMap(map); Predicate <IntVec3> predicate = delegate(IntVec3 x) { if (!x.InBounds(map)) { return(false); } if (!x.Standable(map)) { return(false); } if (x.Fogged(map)) { return(false); } if (rectWithMargin.Contains(x)) { return(false); } if ((x.z <= rectWithMargin.maxZ || (allowedSides & SpectateRectSide.Up) != SpectateRectSide.Up) && (x.x <= rectWithMargin.maxX || (allowedSides & SpectateRectSide.Right) != SpectateRectSide.Right) && (x.z >= rectWithMargin.minZ || (allowedSides & SpectateRectSide.Down) != SpectateRectSide.Down) && (x.x >= rectWithMargin.minX || (allowedSides & SpectateRectSide.Left) != SpectateRectSide.Left)) { return(false); } IntVec3 intVec3 = spectateRect.ClosestCellTo(x); if ((float)intVec3.DistanceToSquared(x) > 210.25f) { return(false); } if (!GenSight.LineOfSight(intVec3, x, map, true, null, 0, 0)) { return(false); } if (x.GetThingList(map).Find((Thing y) => y is Pawn && y != p) != null) { return(false); } if (p != null) { if (!p.CanReserveAndReach(x, PathEndMode.OnCell, Danger.Some, 1, -1, null, false)) { return(false); } Building edifice = x.GetEdifice(map); if (edifice != null && edifice.def.category == ThingCategory.Building && edifice.def.building.isSittable && !p.CanReserve(edifice, 1, -1, null, false)) { return(false); } if (x.IsForbidden(p)) { return(false); } if (x.GetDangerFor(p, map) != Danger.None) { return(false); } } if (extraDisallowedCells != null && extraDisallowedCells.Contains(x)) { return(false); } if (!SpectatorCellFinder.CorrectlyRotatedChairAt(x, map, spectateRect)) { int num = 0; for (int k = 0; k < GenAdj.AdjacentCells.Length; k++) { IntVec3 x2 = x + GenAdj.AdjacentCells[k]; if (SpectatorCellFinder.CorrectlyRotatedChairAt(x2, map, spectateRect)) { num++; } } if (num >= 3) { return(false); } int num2 = SpectatorCellFinder.DistanceToClosestChair(x, new IntVec3(-1, 0, 0), map, 4, spectateRect); if (num2 >= 0) { int num3 = SpectatorCellFinder.DistanceToClosestChair(x, new IntVec3(1, 0, 0), map, 4, spectateRect); if (num3 >= 0 && Mathf.Abs(num2 - num3) <= 1) { return(false); } } int num4 = SpectatorCellFinder.DistanceToClosestChair(x, new IntVec3(0, 0, 1), map, 4, spectateRect); if (num4 >= 0) { int num5 = SpectatorCellFinder.DistanceToClosestChair(x, new IntVec3(0, 0, -1), map, 4, spectateRect); if (num5 >= 0 && Mathf.Abs(num4 - num5) <= 1) { return(false); } } } return(true); }; if (p != null && predicate(p.Position) && SpectatorCellFinder.CorrectlyRotatedChairAt(p.Position, map, spectateRect)) { cell = p.Position; return(true); } for (int i = 0; i < 1000; i++) { IntVec3 intVec = rectWithMargin.CenterCell + GenRadial.RadialPattern[i]; if (predicate(intVec)) { if (!SpectatorCellFinder.CorrectlyRotatedChairAt(intVec, map, spectateRect)) { for (int j = 0; j < 90; j++) { IntVec3 intVec2 = intVec + GenRadial.RadialPattern[j]; if (SpectatorCellFinder.CorrectlyRotatedChairAt(intVec2, map, spectateRect) && predicate(intVec2)) { cell = intVec2; return(true); } } } cell = intVec; return(true); } } cell = IntVec3.Invalid; return(false); }
public bool WillBeAffectedBy(ThingDef def, Faction faction, IntVec3 pos, Rot4 rotation) { CellRect cellRect = GenAdj.OccupiedRect(pos, rotation, def.size); foreach (FocusStrengthOffset offset in Props.offsets) { FocusStrengthOffset_ArtificialBuildings focusStrengthOffset_ArtificialBuildings; if ((focusStrengthOffset_ArtificialBuildings = (offset as FocusStrengthOffset_ArtificialBuildings)) != null && MeditationUtility.CountsAsArtificialBuilding(def, faction) && cellRect.ClosestCellTo(parent.Position).DistanceTo(parent.Position) <= focusStrengthOffset_ArtificialBuildings.radius) { return(true); } } return(false); }
public static bool TryFindShootLineFromTo(Verb __instance, ref bool __result, IntVec3 root, LocalTargetInfo targ, out ShootLine resultingLine) { if (targ.HasThing && targ.Thing.Map != __instance.caster.Map) { resultingLine = default; __result = false; return(false); } if (__instance.verbProps.IsMeleeAttack || __instance.verbProps.range <= 1.42f) { resultingLine = new ShootLine(root, targ.Cell); __result = ReachabilityImmediate.CanReachImmediate(root, targ, __instance.caster.Map, PathEndMode.Touch, null); return(false); } CellRect cellRect = targ.HasThing ? targ.Thing.OccupiedRect() : CellRect.SingleCell(targ.Cell); float num = __instance.verbProps.EffectiveMinRange(targ, __instance.caster); float num2 = cellRect.ClosestDistSquaredTo(root); if (num2 > __instance.verbProps.range * __instance.verbProps.range || num2 < num * num) { resultingLine = new ShootLine(root, targ.Cell); __result = false; return(false); } if (!__instance.verbProps.requireLineOfSight) { resultingLine = new ShootLine(root, targ.Cell); __result = true; return(false); } IntVec3 goodDest; if (__instance.CasterIsPawn) { if (CanHitFromCellIgnoringRange2(__instance, root, targ, out goodDest)) { resultingLine = new ShootLine(root, goodDest); __result = true; return(false); } List <IntVec3> tempLeanShootSources = new List <IntVec3>(); //ADDED ShootLeanUtility.LeanShootingSourcesFromTo( root, cellRect.ClosestCellTo(root), __instance.caster.Map, tempLeanShootSources); //CHANGED tempLeanShootSources for (int i = 0; i < tempLeanShootSources.Count; i++) //CHANGED tempLeanShootSources { IntVec3 intVec = tempLeanShootSources[i]; //CHANGED tempLeanShootSources if (CanHitFromCellIgnoringRange2(__instance, intVec, targ, out goodDest)) { resultingLine = new ShootLine(intVec, goodDest); __result = true; return(false); } } } else { foreach (IntVec3 item in __instance.caster.OccupiedRect()) { if (CanHitFromCellIgnoringRange2(__instance, item, targ, out goodDest)) { resultingLine = new ShootLine(item, goodDest); __result = true; return(false); } } } resultingLine = new ShootLine(root, targ.Cell); __result = false; return(false); }
public bool TryFindCEShootLineFromTo(IntVec3 root, LocalTargetInfo targ, out ShootLine resultingLine) { if (targ.HasThing && targ.Thing.Map != this.caster.Map) { resultingLine = default(ShootLine); return(false); } if (this.verbProps.range <= ShootTuning.MeleeRange) // If this verb has a MAX range up to melee range (NOT a MIN RANGE!) { resultingLine = new ShootLine(root, targ.Cell); return(ReachabilityImmediate.CanReachImmediate(root, targ, this.caster.Map, PathEndMode.Touch, null)); } CellRect cellRect = (!targ.HasThing) ? CellRect.SingleCell(targ.Cell) : targ.Thing.OccupiedRect(); float num = cellRect.ClosestDistSquaredTo(root); if (num > this.verbProps.range * this.verbProps.range || num < this.verbProps.minRange * this.verbProps.minRange) { resultingLine = new ShootLine(root, targ.Cell); return(false); } //if (!this.verbProps.NeedsLineOfSight) This method doesn't consider the currently loaded projectile if (Projectile.projectile.flyOverhead) { resultingLine = new ShootLine(root, targ.Cell); return(true); } if (this.CasterIsPawn) { IntVec3 dest; if (this.CanHitFromCellIgnoringRange(root, targ, out dest)) { resultingLine = new ShootLine(root, dest); return(true); } ShootLeanUtility.LeanShootingSourcesFromTo(root, cellRect.ClosestCellTo(root), this.caster.Map, tempLeanShootSources); for (int i = 0; i < tempLeanShootSources.Count; i++) { IntVec3 intVec = tempLeanShootSources[i]; if (this.CanHitFromCellIgnoringRange(intVec, targ, out dest)) { resultingLine = new ShootLine(intVec, dest); return(true); } } } else { CellRect.CellRectIterator iterator = this.caster.OccupiedRect().GetIterator(); while (!iterator.Done()) { IntVec3 current = iterator.Current; IntVec3 dest; if (this.CanHitFromCellIgnoringRange(current, targ, out dest)) { resultingLine = new ShootLine(current, dest); return(true); } iterator.MoveNext(); } } resultingLine = new ShootLine(root, targ.Cell); return(false); }
public new bool TryFindShootLineFromTo(IntVec3 root, LocalTargetInfo targ, out ShootLine resultingLine) { // Log.Message("Verb_MeleeAttackDamage_Polearm TryFindShootLineFromTo"); if (targ.HasThing && targ.Thing.Map != this.caster.Map) { resultingLine = default(ShootLine); return(false); } /* * if (this.verbProps.IsMeleeAttack || this.EffectiveRange <= 1.42f) * { * resultingLine = new ShootLine(root, targ.Cell); * * return this.CanReachImmediate(root, targ, this.caster.Map, PathEndMode.Touch, null); * } */ CellRect cellRect = targ.HasThing ? targ.Thing.OccupiedRect() : CellRect.SingleCell(targ.Cell); float num = this.verbProps.EffectiveMinRange(targ, this.caster); float num2 = cellRect.ClosestDistSquaredTo(root); if (num2 > this.EffectiveRange * this.EffectiveRange || num2 < num * num) { resultingLine = new ShootLine(root, targ.Cell); return(false); } if (!this.verbProps.requireLineOfSight) { resultingLine = new ShootLine(root, targ.Cell); return(true); } if (this.CasterIsPawn) { if (this.CanHitFromCellIgnoringRange(root, targ, out IntVec3 dest)) { resultingLine = new ShootLine(root, dest); return(true); } ShootLeanUtility.LeanShootingSourcesFromTo(root, cellRect.ClosestCellTo(root), this.caster.Map, Verb_MeleeAttackDamage_Polearm.tempLeanShootSources); for (int i = 0; i < Verb_MeleeAttackDamage_Polearm.tempLeanShootSources.Count; i++) { IntVec3 intVec = Verb_MeleeAttackDamage_Polearm.tempLeanShootSources[i]; if (this.CanHitFromCellIgnoringRange(intVec, targ, out dest)) { resultingLine = new ShootLine(intVec, dest); return(true); } } } else { foreach (IntVec3 intVec2 in this.caster.OccupiedRect()) { if (this.CanHitFromCellIgnoringRange(intVec2, targ, out IntVec3 dest)) { resultingLine = new ShootLine(intVec2, dest); return(true); } } } resultingLine = new ShootLine(root, targ.Cell); return(false); }
public static bool CanReachImmediateShip(IntVec3 start, CellRect rect, Map map, PathEndMode peMode, Pawn pawn) { IntVec3 c = rect.ClosestCellTo(start); return(ShipReachabilityImmediate.CanReachImmediateShip(start, c, map, peMode, pawn)); }
public static bool CanReachImmediate(IntVec3 start, CellRect rect, Map map, PathEndMode peMode, Actor actor) { IntVec3 intVec3 = rect.ClosestCellTo(start); return(CanReachImmediate(start, intVec3, map, peMode, actor)); }
public bool TryFindCEShootLineFromTo(IntVec3 root, LocalTargetInfo targ, out ShootLine resultingLine) { if (targ.HasThing && targ.Thing.Map != caster.Map) { resultingLine = default(ShootLine); return(false); } if (verbProps.range <= ShootTuning.MeleeRange) // If this verb has a MAX range up to melee range (NOT a MIN RANGE!) { resultingLine = new ShootLine(root, targ.Cell); return(ReachabilityImmediate.CanReachImmediate(root, targ, caster.Map, PathEndMode.Touch, null)); } CellRect cellRect = (!targ.HasThing) ? CellRect.SingleCell(targ.Cell) : targ.Thing.OccupiedRect(); float num = cellRect.ClosestDistSquaredTo(root); if (num > verbProps.range * verbProps.range || num < verbProps.minRange * verbProps.minRange) { resultingLine = new ShootLine(root, targ.Cell); return(false); } //if (!this.verbProps.NeedsLineOfSight) This method doesn't consider the currently loaded projectile if (Projectile.projectile.flyOverhead) { resultingLine = new ShootLine(root, targ.Cell); return(true); } // First check current cell for early opt-out IntVec3 dest; var shotSource = root.ToVector3Shifted(); shotSource.y = ShotHeight; // Adjust for multi-tile turrets if (caster.def.building?.IsTurret ?? false) { shotSource = ShotSource; } if (CanHitFromCellIgnoringRange(shotSource, targ, out dest)) { resultingLine = new ShootLine(root, dest); return(true); } // For pawns, calculate possible lean locations if (CasterIsPawn) { ShootLeanUtility.LeanShootingSourcesFromTo(root, cellRect.ClosestCellTo(root), caster.Map, tempLeanShootSources); foreach (var leanLoc in tempLeanShootSources.OrderBy(c => c.DistanceTo(targ.Cell))) { var leanOffset = (leanLoc - root).ToVector3() * 0.51f; //using exactly 0.5f makes it always round up, which causes issues if offset is negative, when adding leanOffset to shotSource. Setting to 0.51f to fix. if (CanHitFromCellIgnoringRange(shotSource + leanOffset, targ, out dest)) { resultingLine = new ShootLine(leanLoc, dest); return(true); } } } resultingLine = new ShootLine(root, targ.Cell); return(false); }