public static void Postfix(ref CalledShotAttackOrderInfo __result, AbstractActor attackingUnit, int enemyUnitIndex) { Mod.Log.Trace?.Write("AE:CCSLTC entered"); ICombatant combatant = attackingUnit.BehaviorTree.enemyUnits[enemyUnitIndex]; if (combatant is AbstractActor targetActor) { // Prevents blips from being the targets of called shots VisibilityLevel targetVisibility = attackingUnit.VisibilityToTargetUnit(targetActor); if (targetVisibility < VisibilityLevel.LOSFull) { Mod.Log.Info?.Write($"Target {CombatantUtils.Label(combatant)} is a blip, cannot be targeted by AI called shot"); __result = null; return; } float distance = Vector3.Distance(attackingUnit.CurrentPosition, targetActor.CurrentPosition); bool hasVisualScan = VisualLockHelper.GetVisualScanRange(attackingUnit) >= distance; SensorScanType sensorScan = SensorLockHelper.CalculateSharedLock(targetActor, attackingUnit); if (sensorScan < SensorScanType.ArmorAndWeaponType && !hasVisualScan) { Mod.Log.Info?.Write($"Target {CombatantUtils.Label(targetActor)} sensor info {sensorScan} is less than SurfaceScan and outside visualID, cannot be targeted by AI called shot"); __result = null; return; } } }
// CLONE OF HBS CODE - LIKELY BRITTLE! public static CalledShotAttackOrderInfo MakeCalledShotOrder(AbstractActor attackingUnit, AttackEvaluator.AttackEvaluation evaluatedAttack, ICombatant target, bool isMoraleAttack) { Mech mech = target as Mech; if (mech == null || !mech.IsVulnerableToCalledShots() || evaluatedAttack.AttackType == AIUtil.AttackType.Melee || evaluatedAttack.AttackType == AIUtil.AttackType.DeathFromAbove) { return(null); } Mech mech2 = attackingUnit as Mech; for (int i = 0; i < evaluatedAttack.WeaponList.Count; i++) { Weapon weapon = evaluatedAttack.WeaponList[i]; if (weapon.WeaponCategoryValue.IsMelee || weapon.Type == WeaponType.Melee || (mech2 != null && (weapon == mech2.DFAWeapon || weapon == mech2.MeleeWeapon))) { return(null); } } List <ArmorLocation> list = new List <ArmorLocation> { ArmorLocation.Head, ArmorLocation.CenterTorso, ArmorLocation.LeftTorso, ArmorLocation.LeftArm, ArmorLocation.LeftLeg, ArmorLocation.RightTorso, ArmorLocation.RightArm, ArmorLocation.RightLeg }; List <ChassisLocations> list2 = new List <ChassisLocations> { ChassisLocations.Head, ChassisLocations.CenterTorso, ChassisLocations.LeftTorso, ChassisLocations.LeftArm, ChassisLocations.LeftLeg, ChassisLocations.RightTorso, ChassisLocations.RightArm, ChassisLocations.RightLeg }; List <float> list3 = new List <float>(list.Count); float num = 0f; for (int j = 0; j < list.Count; j++) { float num2 = CalcCalledShotLocationTargetChance(mech, list[j], list2[j]); list3.Add(num2); num += num2; } float num3 = UnityEngine.Random.Range(0f, num); CalledShotAttackOrderInfo calledShotAttackOrderInfo = null; for (int k = 0; k < list.Count; k++) { float num4 = list3[k]; if (num3 < num4) { calledShotAttackOrderInfo = new CalledShotAttackOrderInfo(mech, list[k], isMoraleAttack); break; } num3 -= num4; } if (calledShotAttackOrderInfo == null) { Debug.LogError("Failed to calculate called shot. Targeting head as fallback."); calledShotAttackOrderInfo = new CalledShotAttackOrderInfo(mech, ArmorLocation.Head, isMoraleAttack); } for (int l = 0; l < evaluatedAttack.WeaponList.Count; l++) { Weapon weapon2 = evaluatedAttack.WeaponList[l]; AIUtil.LogAI("Called Shot: Adding weapon " + weapon2.Name, "AI.DecisionMaking"); calledShotAttackOrderInfo.AddWeapon(weapon2); } return(calledShotAttackOrderInfo); }