public static bool trySwapToMoreAccurateRangedWeapon(Pawn pawn, LocalTargetInfo target, bool dropCurrent, bool skipManualUse, bool skipDangerous = true, bool skipEMP = true) { CompSidearmMemory pawnMemory = CompSidearmMemory.GetMemoryCompForPawn(pawn); if (pawn == null || pawn.Dead || pawnMemory == null || pawn.equipment == null || pawn.inventory == null) { return(false); } if (pawnMemory.IsCurrentWeaponForced(false)) { return(false); } (ThingWithComps weapon, float dps, float averageSpeed)bestWeapon = GettersFilters.findBestRangedWeapon(pawn, target, skipManualUse, skipDangerous, skipEMP, true); if (bestWeapon.weapon == null) { return(false); } CellRect cellRect = (!target.HasThing) ? CellRect.SingleCell(target.Cell) : target.Thing.OccupiedRect(); float range = cellRect.ClosestDistSquaredTo(pawn.Position); float currentDPS = StatCalculator.RangedDPS(pawn.equipment.Primary, Settings.SpeedSelectionBiasRanged, bestWeapon.averageSpeed, range); if (bestWeapon.dps < currentDPS + MiscUtils.ANTI_OSCILLATION_FACTOR) { return(false); } equipSpecificWeaponFromInventory(pawn, bestWeapon.weapon, dropCurrent, false); return(true); }
public static (ThingWithComps weapon, float dps, float averageSpeed) findBestRangedWeapon(Pawn pawn, LocalTargetInfo?target = null, bool skipManualUse = true, bool skipDangerous = true, bool skipEMP = true, bool includeEquipped = true) { if (pawn == null || pawn.Dead || pawn.equipment == null || pawn.inventory == null) { return(null, -1, 0); } IEnumerable <ThingWithComps> options = pawn.getCarriedWeapons(includeEquipped).Where(t => t.def.IsRangedWeapon); if (!Settings.AllowBlockedWeaponUse) { options = options.Where(t => StatCalculator.canUseSidearmInstance(t, pawn, out _)); } options = options.Where(t => !pawn.IsColonistPlayerControlled || !isManualUse(t)); if (skipManualUse) { options = options.Where(t => (!isManualUse(t))); } if (skipDangerous) { options = options.Where(t => (!isDangerousWeapon(t))); } if (skipEMP) { options = options.Where(t => !isEMPWeapon(t)); } if (options.Count() == 0) { return(null, -1, 0); } float averageSpeed = AverageSpeedRanged(options); if (target.HasValue) { CellRect cellRect = (!target.Value.HasThing) ? CellRect.SingleCell(target.Value.Cell) : target.Value.Thing.OccupiedRect(); float targetDistance = cellRect.ClosestDistSquaredTo(pawn.Position); options = options.Where(t => { VerbProperties atkProps = (t.GetComp <CompEquippable>())?.PrimaryVerb?.verbProps; if (atkProps == null) { return(false); } return(atkProps.range >= targetDistance); }); if (options.Count() == 0) { return(null, -1, 0); } //TODO: handle DPS vs. armor? (ThingWithComps thing, float dps, float averageSpeed)best = (null, -1, averageSpeed); foreach (ThingWithComps candidate in options) { float dps = StatCalculator.RangedDPS(candidate, Settings.SpeedSelectionBiasRanged, averageSpeed, targetDistance); if (dps > best.dps) { best = (candidate, dps, averageSpeed); } } return(best); } else { (ThingWithComps thing, float dps, float averageSpeed)best = (null, -1, averageSpeed); foreach (ThingWithComps candidate in options) { float dps = StatCalculator.RangedDPSAverage(candidate, Settings.SpeedSelectionBiasRanged, averageSpeed); if (dps > best.dps) { best = (candidate, dps, averageSpeed); } } return(best); } }