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;
                }
            }
        }
        public static bool Prefix(SelectionStateFire __instance, ref bool __result, ICombatant combatant)
        {
            Mod.Log.Trace?.Write("SSF:PCC:PRE entered");

            if (__instance != null && combatant != null && combatant is AbstractActor targetActor && __instance.SelectedActor != null)
            {
                CombatGameState Combat           = __instance.SelectedActor.Combat;
                bool            targetIsFriendly = Combat.HostilityMatrix.IsFriendly(combatant.team.GUID, Combat.LocalPlayerTeamGuid);
                if (targetIsFriendly)
                {
                    Mod.Log.Trace?.Write("Friendly target, skipping check");
                    return(true);
                }

                EWState targetState   = new EWState(targetActor);
                EWState attackerState = new EWState(__instance.SelectedActor);

                if (__instance.SelectionType == SelectionType.FireMorale)
                {
                    // Prevents blips from being the targets of called shots
                    VisibilityLevel targetVisibility = __instance.SelectedActor.VisibilityToTargetUnit(targetActor);
                    if (targetVisibility < VisibilityLevel.LOSFull)
                    {
                        Mod.Log.Info?.Write($"Target {CombatantUtils.Label(combatant)} is a blip, cannot be targeted by called shot");
                        __result = false;
                        return(false);
                    }

                    float          distance      = Vector3.Distance(__instance.SelectedActor.CurrentPosition, targetActor.CurrentPosition);
                    bool           hasVisualScan = VisualLockHelper.GetVisualScanRange(__instance.SelectedActor) >= distance;
                    SensorScanType sensorScan    = SensorLockHelper.CalculateSharedLock(targetActor, __instance.SelectedActor);
                    if (sensorScan < SensorScanType.ArmorAndWeaponType && !hasVisualScan)
                    {
                        Mod.Log.Info?.Write($"Target {CombatantUtils.Label(targetActor)} sensor info {sensorScan} is less than SurfaceScan and range:{distance} outside visualScan range, cannot be targeted by called shot");
                        __result = false;
                        return(false);
                    }
                }
                else if (__instance.SelectionType == SelectionType.FireMulti)
                {
                    if (targetState.HasStealth() || targetState.HasMimetic())
                    {
                        Mod.Log.Info?.Write($"Target {CombatantUtils.Label(targetActor)} has stealth, cannot be multi-targeted!");
                        __result = false;
                        return(false);
                    }
                }
            }

            __result = false;
            return(true);
        }
        private static string BuildToolTip(AbstractActor actor)
        {
            //Mod.Log.Debug?.Write($"EW State for actor:{CombatantUtils.Label(actor)} = {ewState}");

            List <string> details = new List <string>();

            // Visuals check
            float visualLockRange = VisualLockHelper.GetVisualLockRange(actor);
            float visualScanRange = VisualLockHelper.GetVisualScanRange(actor);

            details.Add(
                new Text(Mod.LocalizedText.StatusPanel[ModText.LT_PANEL_VISUALS],
                         new object[] { visualLockRange, visualScanRange, ModState.GetMapConfig().UILabel() })
                .ToString()
                );

            // Sensors check
            EWState ewState = new EWState(actor);

            int            totalDetails = ewState.GetCurrentEWCheck() + ewState.AdvancedSensorsMod();
            SensorScanType checkLevel   = SensorScanTypeHelper.DetectionLevelForCheck(totalDetails);

            float rawRangeMulti = SensorLockHelper.GetAllSensorRangeMultipliers(actor);
            float rangeMulti    = rawRangeMulti + ewState.GetSensorsRangeMulti();

            float  sensorsRange = SensorLockHelper.GetSensorsRange(actor);
            string sensorColor  = ewState.GetCurrentEWCheck() >= 0 ? "00FF00" : "FF0000";

            details.Add(
                new Text(Mod.LocalizedText.StatusPanel[ModText.LT_PANEL_SENSORS],
                         new object[] { sensorColor, sensorsRange, sensorColor, rangeMulti, checkLevel.Label() })
                .ToString()
                );

            // Details
            //{ LT_PANEL_DETAILS, "  Total: <color=#{0}>{1:+0;-#}</color><size=90%> Roll: <color=#{2}>{3:+0;-#}</color> Tactics: <color=#00FF00>{4:+0;-#}</color> AdvSen: <color=#{5}>{6:+0;-#}</color>\n"
            string totalColor  = totalDetails >= 0 ? "00FF00" : "FF0000";
            string checkColor  = ewState.GetRawCheck() >= 0 ? "00FF00" : "FF0000";
            string advSenColor = ewState.AdvancedSensorsMod() >= 0 ? "00FF00" : "FF0000";

            details.Add(
                new Text(Mod.LocalizedText.StatusPanel[ModText.LT_PANEL_DETAILS],
                         new object[] { totalColor, totalDetails, checkColor, ewState.GetRawCheck(), ewState.GetRawTactics(), advSenColor, ewState.AdvancedSensorsMod() })
                .ToString()
                );

            //  Heat Vision
            if (ewState.GetRawHeatVision() != null)
            {
                // { LT_PANEL_HEAT, "<b>Thermals</b><size=90%> Mod:<color=#{0}>{1:+0;-#}</color> / {2} heat Range:{3}m\n" },
                HeatVision heatVis = ewState.GetRawHeatVision();
                // Positive is bad, negative is good
                string modColor = heatVis.AttackMod >= 0 ? "FF0000" : "00FF00";
                details.Add(
                    new Text(Mod.LocalizedText.StatusPanel[ModText.LT_PANEL_HEAT],
                             new object[] { modColor, heatVis.AttackMod, heatVis.HeatDivisor, heatVis.MaximumRange })
                    .ToString()
                    );
            }

            //  Zoom Vision
            if (ewState.GetRawZoomVision() != null)
            {
                // { LT_PANEL_ZOOM, "<b>Zoom</b><size=90%> Mod:<color=#{0}>{1:+0;-#}</color? Cap:<color=#{2}>{3:+0;-#}</color> Range:{4}m\n" },
                ZoomVision zoomVis = ewState.GetRawZoomVision();
                // Positive is bad, negative is good
                string modColor = zoomVis.AttackMod >= 0 ? "FF0000" : "00FF00";
                string capColor = zoomVis.AttackCap >= 0 ? "FF0000" : "00FF00";
                details.Add(
                    new Text(Mod.LocalizedText.StatusPanel[ModText.LT_PANEL_ZOOM],
                             new object[] { modColor, zoomVis.AttackMod, capColor, zoomVis.AttackCap, zoomVis.MaximumRange })
                    .ToString()
                    );
            }

            string tooltipText = String.Join("", details.ToArray());

            return(tooltipText);
        }