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 void Postfix(Vehicle __instance, ref Text __result, VisibilityLevel visLevel) { if (__instance == null) { return; } ; /* * Vehicle.UnitName = VehicleDef.Chassis.Description.Name -> * Alacorn Mk.VI-P / vehicledef_ARES_CLAN / Demolisher II / Galleon GAL-102 * Vehicle.NickName = VehicleDef.Description.Name -> * Pirate Alacorn Gauss Carrier / Ares / Demolisher II` * VehicleDef.Description.Id -> * / / vehicledef_DEMOLISHER-II / vehicledef_GALLEON_GAL102 */ if (__instance.Combat.HostilityMatrix.IsLocalPlayerEnemy(__instance.team.GUID)) { string chassisName = __instance.UnitName; string fullName = __instance.Nickname; SensorScanType scanType = SensorLockHelper.CalculateSharedLock(__instance, null); if (scanType < SensorScanType.ArmorAndWeaponType) { bool hasVisualScan = VisualLockHelper.CanSpotTargetUsingCurrentPositions(ModState.LastPlayerActorActivated, __instance); if (hasVisualScan) { scanType = SensorScanType.ArmorAndWeaponType; } } Text response = CombatNameHelper.GetTurretOrVehicleDetectionLabel(visLevel, scanType, fullName, chassisName, true); __result = response; } }
public static void Postfix(LineOfSight __instance, ref float __result, AbstractActor source, AbstractActor target, CombatGameState ___Combat) { if (__instance != null && source != null) { __result = SensorLockHelper.GetAdjustedSensorRange(source, target); } }
public static void Postfix(LineOfSight __instance, ref float __result, AbstractActor source) { if (__instance != null && source != null) { __result = SensorLockHelper.GetSensorsRange(source); } }
public static bool Prefix(LineOfSight __instance, ref VisibilityLevel __result, AbstractActor source, Vector3 sourcePosition, ICombatant target, Vector3 targetPosition, Quaternion targetRotation) { Mod.Log.Trace?.Write($"LOS:GVTTWPAR: source:{CombatantUtils.Label(source)} ==> target:{CombatantUtils.Label(target)}"); // Skip if we aren't ready to process // TODO: Is this necessary anymore? //if (State.TurnDirectorStarted == false || (target as AbstractActor) == null) { return true; } AbstractActor sourceActor = source as AbstractActor; // TODO: Handle buildings here bool sourceHasLineOfSight = VisualLockHelper.CanSpotTarget(sourceActor, sourcePosition, target, targetPosition, targetRotation, __instance); if (sourceHasLineOfSight) { __result = VisibilityLevel.LOSFull; } else { VisibilityLevel sensorsVisibility = VisibilityLevel.None; if (ModState.TurnDirectorStarted) { SensorScanType sensorLock = SensorLockHelper.CalculateSensorLock(sourceActor, sourcePosition, target, targetPosition); sensorsVisibility = sensorLock.Visibility(); } __result = sensorsVisibility; } //Mod.Log.Trace?.Write($"LOS:GVTTWPAR - [{__result}] visibility for source:{CombatantUtils.Label(source)} ==> target:{CombatantUtils.Label(target)}"); return(false); }
public static void Postfix(CombatHUDStatusPanel __instance, List <CombatHUDStatusIndicator> ___Buffs, List <CombatHUDStatusIndicator> ___Debuffs) { Mod.Log.Trace?.Write("CHUDSP:RDC - entered."); if (__instance != null && __instance.DisplayedCombatant != null) { AbstractActor target = __instance.DisplayedCombatant as AbstractActor; // We can receive a building here, so if (target != null) { if (target.Combat.HostilityMatrix.IsLocalPlayerEnemy(target.team)) { SensorScanType scanType = SensorLockHelper.CalculateSharedLock(target, ModState.LastPlayerActorActivated); // Hide the buffs and debuffs if the current scanType is less than allInfo if (scanType < SensorScanType.AllInformation) { //// Hide the buffs and debuffs ___Buffs.ForEach(si => si.gameObject.SetActive(false)); ___Debuffs.ForEach(si => si.gameObject.SetActive(false)); } } // Calculate stealth pips Traverse stealthDisplayT = Traverse.Create(__instance).Field("stealthDisplay"); CombatHUDStealthBarPips stealthDisplay = stealthDisplayT.GetValue <CombatHUDStealthBarPips>(); VfxHelper.CalculateMimeticPips(stealthDisplay, target); } } }
public static void Postfix(Turret __instance, ref Text __result, VisibilityLevel visLevel) { if (__instance == null) { return; } /* * Turret.UnitName = return (this.TurretDef == null) ? "UNDEFINED" : this.TurretDef.Chassis.Description.Name -> * Turret.NickName = (this.TurretDef == null) ? "UNDEFINED" : this.TurretDef.Description.Name -> */ if (__instance.Combat.HostilityMatrix.IsLocalPlayerEnemy(__instance.team.GUID)) { string chassisName = __instance.UnitName; string fullName = __instance.Nickname; SensorScanType scanType = SensorLockHelper.CalculateSharedLock(__instance, null); if (scanType < SensorScanType.ArmorAndWeaponType) { bool hasVisualScan = VisualLockHelper.CanSpotTargetUsingCurrentPositions(ModState.LastPlayerActorActivated, __instance); if (hasVisualScan) { scanType = SensorScanType.ArmorAndWeaponType; } } Text response = CombatNameHelper.GetTurretOrVehicleDetectionLabel(visLevel, scanType, fullName, chassisName, false); __result = response; } }
public static void ObfuscateArmorAndStructText(AbstractActor target, TextMeshProUGUI armorHover, TextMeshProUGUI structHover) { if (target == null) { Mod.Log.Warn?.Write("Helper::HideArmorAndStructure - target is null!"); } if (armorHover == null) { Mod.Log.Warn?.Write("Helper::HideArmorAndStructure - armorHover is null!"); } if (structHover == null) { Mod.Log.Warn?.Write("Helper::HideArmorAndStructure - structHover is null!"); } try { SensorScanType scanType = SensorLockHelper.CalculateSharedLock(target, ModState.LastPlayerActorActivated); bool hasVisualScan = VisualLockHelper.CanSpotTarget( ModState.LastPlayerActorActivated, ModState.LastPlayerActorActivated.CurrentPosition, target, target.CurrentPosition, target.CurrentRotation, target.Combat.LOS); string armorText; string structText; if (scanType >= SensorScanType.StructAndWeaponID) { // See all values armorText = armorHover.text; structText = structHover.text; } else if (scanType >= SensorScanType.ArmorAndWeaponType || hasVisualScan) { // See max armor, max struct string rawArmor = armorHover.text; string maxArmor = rawArmor.Split('/')[1]; string rawStruct = structHover.text; string maxStruct = rawStruct.Split('/')[1]; armorText = $"? / {maxArmor}"; structText = $"? / {maxStruct}"; } else { // See ? / ? armorText = "? / ?"; structText = "? / ?"; } // TODO: Sensor lock should give you an exact amount at the point you're locked armorHover.SetText(armorText); structHover.SetText(structText); } catch (Exception e) { Mod.Log.Error?.Write(e, $"Failed to obfuscate armor and structure text for unit: {CombatantUtils.Label(target)}" + $" from position of last-activated actor: {CombatantUtils.Label(ModState.LastPlayerActorActivated)}."); } }
public void TestTargetSignature_NoModifiers() { Mech attacker = TestHelper.BuildTestMech(); Mech target = TestHelper.BuildTestMech(); EWState attackerState = new EWState(attacker); EWState targetState = new EWState(target); Assert.AreEqual(1.0f, SensorLockHelper.GetTargetSignature(target, attackerState)); }
public void TestTargetSignature_Stealth_Minus_20pct() { Mech attacker = TestHelper.BuildTestMech(); Mech target = TestHelper.BuildTestMech(); // Stealth - <signature_modifier>_<details_modifier>_<mediumAttackMod>_<longAttackmod>_<extremeAttackMod> target.StatCollection.Set(ModStats.StealthEffect, "0.20_2_1_2_3"); EWState attackerState = new EWState(attacker); Assert.AreEqual(0.8f, SensorLockHelper.GetTargetSignature(target, attackerState)); }
public static void Prefix(CombatHUDWeaponPanel __instance, AbstractActor ___displayedActor) { if (__instance == null || ___displayedActor == null) { return; } Mod.Log.Trace?.Write("CHUDWP:RDW - entered."); Traverse targetT = Traverse.Create(__instance).Property("target"); Traverse hoveredTargetT = Traverse.Create(__instance).Property("hoveredTarget"); Traverse HUDT = Traverse.Create(__instance).Property("HUD"); CombatHUD HUD = HUDT.GetValue <CombatHUD>(); SelectionState activeState = HUD.SelectionHandler.ActiveState; ICombatant target; if (activeState != null && activeState is SelectionStateMove) { target = hoveredTargetT.GetValue <ICombatant>(); if (target == null) { target = targetT.GetValue <ICombatant>(); } } else { target = targetT.GetValue <ICombatant>(); if (target == null) { target = hoveredTargetT.GetValue <ICombatant>(); } } if (target == null) { return; } EWState attackerState = new EWState(___displayedActor); Mod.Log.Debug?.Write($"Attacker ({CombatantUtils.Label(___displayedActor)} => EWState: {attackerState}"); bool canSpotTarget = VisualLockHelper.CanSpotTarget(___displayedActor, ___displayedActor.CurrentPosition, target, target.CurrentPosition, target.CurrentRotation, ___displayedActor.Combat.LOS); SensorScanType sensorScan = SensorLockHelper.CalculateSharedLock(target, ___displayedActor); Mod.Log.Debug?.Write($" canSpotTarget: {canSpotTarget} sensorScan: {sensorScan}"); if (target is AbstractActor targetActor) { EWState targetState = new EWState(targetActor); Mod.Log.Debug?.Write($"Target ({CombatantUtils.Label(targetActor)} => EWState: {targetState}"); } }
public static void Postfix(CombatHUDActorNameDisplay __instance, VisibilityLevel visLevel, AbstractActor ___displayedActor) { if (___displayedActor != null && ModState.LastPlayerActorActivated != null && ModState.TurnDirectorStarted && !___displayedActor.Combat.HostilityMatrix.IsLocalPlayerFriendly(___displayedActor.TeamId)) { SensorScanType scanType = SensorLockHelper.CalculateSharedLock(___displayedActor, ModState.LastPlayerActorActivated); // TODO: Needs to be hidden or localized string nameText = scanType >= SensorScanType.AllInformation ? ___displayedActor.GetPilot().Name : ""; __instance.PilotNameText.SetText(nameText); } }
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); }
public void TestTargetSignature_Stealth_20pct() { Mech attacker = TestHelper.BuildTestMech(); Mech target = TestHelper.BuildTestMech(); Traverse isShutdownT = Traverse.Create(target).Field("_isShutDown"); isShutdownT.SetValue(true); EWState attackerState = new EWState(attacker); EWState targetState = new EWState(target); Assert.AreEqual(0.5f, SensorLockHelper.GetTargetSignature(target, attackerState)); }
public static void Postfix(CombatHUDVehicleArmorHover __instance) { HUDVehicleArmorReadout ___Readout = (HUDVehicleArmorReadout)Traverse.Create(__instance).Property("Readout").GetValue(); CombatHUDTooltipHoverElement ___ToolTip = (CombatHUDTooltipHoverElement)Traverse.Create(__instance).Property("ToolTip").GetValue(); if (___Readout != null && ___Readout.DisplayedVehicle != null && ___Readout.DisplayedVehicle.Combat != null && ___ToolTip != null) { Vehicle target = ___Readout.DisplayedVehicle; if (!target.Combat.HostilityMatrix.IsLocalPlayerFriendly(target.TeamId)) { SensorScanType scanType = SensorLockHelper.CalculateSharedLock(target, ModState.LastPlayerActorActivated); if (scanType < SensorScanType.AllInformation) { ___ToolTip.BuffStrings.Clear(); ___ToolTip.DebuffStrings.Clear(); } } } }
static void UpdateSensorAndVisualsIcons(MarkGOContainer container, AbstractActor displayedActor, AbstractActor lastActivated, bool isPlayer) { // Sensors and Visuals are only shown for non-local players if (!isPlayer) { // Check sensors bool hasSensorLock = SensorLockHelper.CalculateSharedLock(displayedActor, lastActivated) > SensorScanType.NoInfo; container.SensorsMark.SetActive(true); SVGImage sensorsImage = container.SensorsMark.GetComponent <SVGImage>(); if (hasSensorLock) { Mod.Log.Debug?.Write($" - Can sensors detect target, setting icon to green."); sensorsImage.color = Color.green; } else { Mod.Log.Debug?.Write($" - Can not sensors detect target, setting icon to red."); sensorsImage.color = Color.red; } bool canSpotTarget = VisualLockHelper.CanSpotTarget(lastActivated, lastActivated.CurrentPosition, displayedActor, displayedActor.CurrentPosition, displayedActor.CurrentRotation, ModState.Combat.LOS); SVGImage visualsImage = container.VisualsMark.GetComponent <SVGImage>(); container.VisualsMark.SetActive(true); if (canSpotTarget) { Mod.Log.Debug?.Write($" - Can spot target, setting icon to green."); visualsImage.color = Color.green; } else { Mod.Log.Debug?.Write($" - Cannot spot target, setting icon to red."); visualsImage.color = Color.red; } } else { container.SensorsMark.SetActive(false); container.VisualsMark.SetActive(false); } }
public static void Postfix(Mech __instance, ref Text __result, VisibilityLevel visLevel) { if (__instance == null) { return; } /* * Mech.UnitName = MechDef.Chassis.Description.Name -> Shadow Hawk / Atlas / Marauder * Mech.Nickname = Mech.Description.Name -> Shadow Hawk SHD-2D / Atlas AS7-D / Marauder ANU-O * Mech.Description.UIName -> Shadow Hawk SHD-2D / Atlas AS7-D Danielle / Anand ANU-O */ string fullName = __instance.Description.UIName; if (__instance.Combat.HostilityMatrix.IsLocalPlayerEnemy(__instance.team.GUID)) { string chassisName = __instance.UnitName; string partialName = __instance.Nickname; SensorScanType scanType = SensorLockHelper.CalculateSharedLock(__instance, null); if (scanType < SensorScanType.ArmorAndWeaponType) { bool hasVisualScan = VisualLockHelper.CanSpotTargetUsingCurrentPositions(ModState.LastPlayerActorActivated, __instance); if (hasVisualScan) { scanType = SensorScanType.ArmorAndWeaponType; } } __result = CombatNameHelper.GetEnemyMechDetectionLabel(visLevel, scanType, fullName, partialName, chassisName); } else { string displayName = __instance.DisplayName; __result = CombatNameHelper.GetNonHostileMechDetectionLabel(__instance, fullName, displayName); } }
public static void Postfix(CombatHUDTargetingComputer __instance, List <TextMeshProUGUI> ___weaponNames) { if (__instance == null || __instance.ActivelyShownCombatant == null || __instance.ActivelyShownCombatant.Combat == null || __instance.ActivelyShownCombatant.Combat.HostilityMatrix == null || __instance.WeaponList == null) { Mod.Log.Debug?.Write($"CHTC:RAI ~~~ TC, target, or WeaponList is null, skipping."); return; } if (ModState.LastPlayerActorActivated == null) { Mod.Log.Error?.Write("Attempting to refresh ActorInfo, but LastPlayerActorActivated is null. This should never happen!"); } if (__instance.ActivelyShownCombatant.Combat.HostilityMatrix.IsLocalPlayerFriendly(__instance.ActivelyShownCombatant.team.GUID)) { Mod.Log.Debug?.Write($"CHTC:RAI ~~~ target:{CombatantUtils.Label(__instance.ActivelyShownCombatant)} friendly, resetting."); __instance.WeaponList.SetActive(true); return; } // Only enemies or neutrals below this point Mod.Log.Debug?.Write($"CHTC:RAI ~~~ target:{CombatantUtils.Label(__instance.ActivelyShownCombatant)} is enemy"); try { if (__instance.ActivelyShownCombatant is AbstractActor target) { float range = Vector3.Distance(ModState.LastPlayerActorActivated.CurrentPosition, target.CurrentPosition); bool hasVisualScan = VisualLockHelper.CanSpotTarget(ModState.LastPlayerActorActivated, ModState.LastPlayerActorActivated.CurrentPosition, target, target.CurrentPosition, target.CurrentRotation, target.Combat.LOS); SensorScanType scanType = SensorLockHelper.CalculateSharedLock(target, ModState.LastPlayerActorActivated); Mod.Log.Debug?.Write($"CHTC:RAI ~~~ LastActivated:{CombatantUtils.Label(ModState.LastPlayerActorActivated)} vs. enemy:{CombatantUtils.Label(target)} " + $"at range: {range} has scanType:{scanType} visualScan:{hasVisualScan}"); // Build the CAC side-panel try { BuildCACDialogForTarget(ModState.LastPlayerActorActivated, __instance.ActivelyShownCombatant, range, hasVisualScan, scanType); } catch (Exception e) { Mod.Log.Error?.Write(e, $"Failed to initialize CAC SidePanel for source: {CombatantUtils.Label(ModState.LastPlayerActorActivated)} and " + $"target: {CombatantUtils.Label(__instance.ActivelyShownCombatant)}!"); } if (scanType >= SensorScanType.StructAndWeaponID) { __instance.WeaponList.SetActive(true); SetArmorDisplayActive(__instance, true); } else if (scanType >= SensorScanType.ArmorAndWeaponType || hasVisualScan) { SetArmorDisplayActive(__instance, true); ObfuscateWeaponLabels(___weaponNames, target); // Update the summary display __instance.WeaponList.SetActive(true); Transform weaponListT = __instance.WeaponList?.transform?.parent?.Find("tgtWeaponsLabel"); GameObject weaponsLabel = weaponListT.gameObject; TextMeshProUGUI labelText = weaponsLabel.GetComponent <TextMeshProUGUI>(); labelText.SetText(new Text(Mod.LocalizedText.TargetingComputer[ModText.LT_TARG_COMP_UNIDENTIFIED]).ToString()); } else { SetArmorDisplayActive(__instance, false); __instance.WeaponList.SetActive(false); Transform weaponListT = __instance.WeaponList?.transform?.parent?.Find("tgtWeaponsLabel"); GameObject weaponsLabel = weaponListT.gameObject; weaponsLabel.SetActive(false); } } else if (__instance.ActivelyShownCombatant is BattleTech.Building building) { Mod.Log.Debug?.Write($"CHTC:RAI ~~~ target:{CombatantUtils.Label(__instance.ActivelyShownCombatant)} is enemy building"); SetArmorDisplayActive(__instance, true); __instance.WeaponList.SetActive(false); Transform weaponListT = __instance.WeaponList?.transform?.parent?.Find("tgtWeaponsLabel"); GameObject weaponsLabel = weaponListT.gameObject; weaponsLabel.SetActive(false); } else { // WTF } } catch (Exception e) { Mod.Log.Error?.Write(e, "Failed to RefreshActorInfo!"); } }
private static void Postfix(CombatHUDWeaponSlot __instance, ICombatant target, Weapon ___displayedWeapon, CombatHUD ___HUD) { if (__instance == null || ___displayedWeapon == null || ___HUD.SelectedActor == null || target == null) { return; } Mod.Log.Trace?.Write("CHUDWS:SHC - entered."); AbstractActor attacker = __instance.DisplayedWeapon.parent; Traverse AddToolTipDetailMethod = Traverse.Create(__instance).Method("AddToolTipDetail", new Type[] { typeof(string), typeof(int) }); if (target is AbstractActor targetActor && __instance.DisplayedWeapon != null) { float magnitude = (attacker.CurrentPosition - target.CurrentPosition).magnitude; EWState attackerState = new EWState(attacker); EWState targetState = new EWState(targetActor); // If we can't see the target, apply the No Visuals penalty bool canSpotTarget = VisualLockHelper.CanSpotTarget(attacker, attacker.CurrentPosition, target, target.CurrentPosition, target.CurrentRotation, attacker.Combat.LOS); int mimeticMod = targetState.MimeticAttackMod(attackerState); int eyeballAttackMod = canSpotTarget ? mimeticMod : Mod.Config.Attack.NoVisualsPenalty; // Zoom applies independently of visibility (request from Harkonnen) LineOfFireLevel lofLevel; Vector3 attackPosition = ___HUD.SelectionHandler.ActiveState.PreviewPos; if (Vector3.Distance(attacker.CurrentPosition, attackPosition) > 0.1f) { Vector3 vector; lofLevel = attacker.Combat.LOS.GetLineOfFire(attacker, attackPosition, target, target.CurrentPosition, target.CurrentRotation, out vector); } else { lofLevel = attacker.VisibilityCache.VisibilityToTarget(target).LineOfFireLevel; } int zoomVisionMod = attackerState.GetZoomVisionAttackMod(__instance.DisplayedWeapon, magnitude); int zoomAttackMod = attackerState.HasZoomVisionToTarget(__instance.DisplayedWeapon, magnitude, lofLevel) ? zoomVisionMod - mimeticMod : Mod.Config.Attack.NoVisualsPenalty; Mod.Log.Debug?.Write($" Visual attack == eyeball: {eyeballAttackMod} mimetic: {mimeticMod} zoomAtack: {zoomAttackMod}"); bool hasVisualAttack = (eyeballAttackMod < Mod.Config.Attack.NoVisualsPenalty || zoomAttackMod < Mod.Config.Attack.NoVisualsPenalty); // Sensor attack bucket. Sensors always fallback, so roll everything up and cap int narcAttackMod = targetState.NarcAttackMod(attackerState); int tagAttackMod = targetState.TagAttackMod(attackerState); int ecmShieldAttackMod = targetState.ECMAttackMod(attackerState); int stealthAttackMod = targetState.StealthAttackMod(attackerState, __instance.DisplayedWeapon, magnitude); Mod.Log.Debug?.Write($" Sensor attack penalties == narc: {narcAttackMod} tag: {tagAttackMod} ecmShield: {ecmShieldAttackMod} stealth: {stealthAttackMod}"); bool hasSensorAttack = SensorLockHelper.CalculateSharedLock(targetActor, attacker) > SensorScanType.NoInfo; int sensorsAttackMod = Mod.Config.Attack.NoSensorsPenalty; if (hasSensorAttack) { sensorsAttackMod = 0; sensorsAttackMod -= narcAttackMod; sensorsAttackMod -= tagAttackMod; sensorsAttackMod += ecmShieldAttackMod; sensorsAttackMod += stealthAttackMod; } if (sensorsAttackMod > Mod.Config.Attack.NoSensorsPenalty) { Mod.Log.Debug?.Write($" Rollup of penalties {sensorsAttackMod} is > than NoSensors, defaulting to {Mod.Config.Attack.NoSensorsPenalty} "); hasSensorAttack = false; } // Check firing blind if (!hasVisualAttack && !hasSensorAttack) { string localText = new Localize.Text(Mod.Config.LocalizedText[ModConfig.LT_ATTACK_FIRING_BLIND]).ToString(); AddToolTipDetailMethod.GetValue(new object[] { localText, Mod.Config.Attack.FiringBlindPenalty }); } else { // Visual attacks if (!hasVisualAttack) { string localText = new Localize.Text(Mod.Config.LocalizedText[ModConfig.LT_ATTACK_NO_VISUALS]).ToString(); AddToolTipDetailMethod.GetValue(new object[] { localText, Mod.Config.Attack.NoVisualsPenalty }); } else { // If the zoom + mimetic is better than eyeball, use that. Otherwise, we're using the good ol mk.1 eyeball if (zoomAttackMod < eyeballAttackMod) { string localText = new Localize.Text(Mod.Config.LocalizedText[ModConfig.LT_ATTACK_ZOOM_VISION]).ToString(); AddToolTipDetailMethod.GetValue(new object[] { localText, zoomVisionMod }); } if (mimeticMod != 0) { string localText = new Localize.Text(Mod.Config.LocalizedText[ModConfig.LT_ATTACK_MIMETIC]).ToString(); AddToolTipDetailMethod.GetValue(new object[] { localText, mimeticMod }); } } if (attackerState.HasHeatVisionToTarget(__instance.DisplayedWeapon, magnitude)) { int heatAttackMod = attackerState.GetHeatVisionAttackMod(targetActor, magnitude, __instance.DisplayedWeapon); string localText = new Localize.Text(Mod.Config.LocalizedText[ModConfig.LT_ATTACK_HEAT_VISION]).ToString(); AddToolTipDetailMethod.GetValue(new object[] { localText, heatAttackMod }); } if (!hasSensorAttack) { string localText = new Localize.Text(Mod.Config.LocalizedText[ModConfig.LT_ATTACK_NO_SENSORS]).ToString(); AddToolTipDetailMethod.GetValue(new object[] { localText, Mod.Config.Attack.NoSensorsPenalty }); } else { if (ecmShieldAttackMod != 0) { string localText = new Localize.Text(Mod.Config.LocalizedText[ModConfig.LT_ATTACK_ECM_SHEILD]).ToString(); AddToolTipDetailMethod.GetValue(new object[] { localText, ecmShieldAttackMod }); } if (narcAttackMod != 0) { string localText = new Localize.Text(Mod.Config.LocalizedText[ModConfig.LT_ATTACK_NARCED]).ToString(); AddToolTipDetailMethod.GetValue(new object[] { localText, narcAttackMod }); } if (tagAttackMod != 0) { string localText = new Localize.Text(Mod.Config.LocalizedText[ModConfig.LT_ATTACK_TAGGED]).ToString(); AddToolTipDetailMethod.GetValue(new object[] { localText, tagAttackMod }); } if (stealthAttackMod != 0) { string localText = new Localize.Text(Mod.Config.LocalizedText[ModConfig.LT_ATTACK_STEALTH]).ToString(); AddToolTipDetailMethod.GetValue(new object[] { localText, stealthAttackMod }); } } } } }
private static void Postfix(ToHit __instance, ref float __result, AbstractActor attacker, Weapon weapon, ICombatant target, Vector3 attackPosition, Vector3 targetPosition, LineOfFireLevel lofLevel) { //Mod.Log.Debug?.Write($"Getting modifiers for attacker:{CombatantUtils.Label(attacker)} " + // $"using weapon:{weapon.Name} vs target:{CombatantUtils.Label(target)} with initial result:{__result}"); AbstractActor targetActor = target as AbstractActor; if (__instance != null && attacker != null && targetActor != null) { float distance = Vector3.Distance(attackPosition, targetPosition); // Cache these EWState attackerState = new EWState(attacker); EWState targetState = new EWState(targetActor); // If we can't see the target, apply the No Visuals penalty bool canSpotTarget = VisualLockHelper.CanSpotTarget(attacker, attacker.CurrentPosition, target, target.CurrentPosition, target.CurrentRotation, attacker.Combat.LOS); int mimeticMod = targetState.MimeticAttackMod(attackerState); int eyeballAttackMod = canSpotTarget ? mimeticMod : Mod.Config.Attack.NoVisualsPenalty; // Zoom applies independently of visibility (request from Harkonnen) int zoomVisionMod = attackerState.GetZoomVisionAttackMod(weapon, distance); int zoomAttackMod = attackerState.HasZoomVisionToTarget(weapon, distance, lofLevel) ? zoomVisionMod - mimeticMod : Mod.Config.Attack.NoVisualsPenalty; bool hasVisualAttack = (eyeballAttackMod < Mod.Config.Attack.NoVisualsPenalty || zoomAttackMod < Mod.Config.Attack.NoVisualsPenalty); // Sensor attack bucket. Sensors always fallback, so roll everything up and cap int narcAttackMod = targetState.NarcAttackMod(attackerState); int tagAttackMod = targetState.TagAttackMod(attackerState); int ecmShieldAttackMod = targetState.ECMAttackMod(attackerState); int stealthAttackMod = targetState.StealthAttackMod(attackerState, weapon, distance); bool hasSensorAttack = SensorLockHelper.CalculateSharedLock(targetActor, attacker) > SensorScanType.NoInfo; int sensorsAttackMod = Mod.Config.Attack.NoSensorsPenalty; if (hasSensorAttack) { sensorsAttackMod = 0; sensorsAttackMod -= narcAttackMod; sensorsAttackMod -= tagAttackMod; sensorsAttackMod += ecmShieldAttackMod; sensorsAttackMod += stealthAttackMod; } if (sensorsAttackMod > Mod.Config.Attack.NoSensorsPenalty) { sensorsAttackMod = Mod.Config.Attack.NoSensorsPenalty; hasSensorAttack = false; } // Check firing blind if (!hasVisualAttack && !hasSensorAttack) { __result += Mod.Config.Attack.FiringBlindPenalty; } else { __result += (zoomAttackMod < eyeballAttackMod) ? zoomAttackMod : eyeballAttackMod; if (attackerState.HasHeatVisionToTarget(weapon, distance)) { __result += attackerState.GetHeatVisionAttackMod(targetActor, distance, weapon); } __result += sensorsAttackMod; } } }
public static void Postfix(CombatHUDActorInfo __instance, AbstractActor ___displayedActor, BattleTech.Building ___displayedBuilding, ICombatant ___displayedCombatant) { if (__instance == null || ___displayedActor == null) { return; } try { bool isEnemyOrNeutral = false; VisibilityLevel visibilityLevel = VisibilityLevel.None; if (___displayedCombatant != null) { if (___displayedCombatant.IsForcedVisible) { visibilityLevel = VisibilityLevel.LOSFull; } else if (___displayedBuilding != null) { visibilityLevel = __instance.Combat.LocalPlayerTeam.VisibilityToTarget(___displayedBuilding); } else if (___displayedActor != null) { if (__instance.Combat.HostilityMatrix.IsLocalPlayerFriendly(___displayedActor.team)) { visibilityLevel = VisibilityLevel.LOSFull; } else { visibilityLevel = __instance.Combat.LocalPlayerTeam.VisibilityToTarget(___displayedActor); isEnemyOrNeutral = true; } } } Traverse setGOActiveMethod = Traverse.Create(__instance).Method("SetGOActive", new Type[] { typeof(MonoBehaviour), typeof(bool) }); // The actual method should handle allied and friendly units fine, so we can just change it for enemies if (isEnemyOrNeutral && visibilityLevel >= VisibilityLevel.Blip0Minimum && ___displayedActor != null) { SensorScanType scanType = SensorLockHelper.CalculateSharedLock(___displayedActor, ModState.LastPlayerActorActivated); bool hasVisualScan = VisualLockHelper.CanSpotTarget(ModState.LastPlayerActorActivated, ModState.LastPlayerActorActivated.CurrentPosition, ___displayedActor, ___displayedActor.CurrentPosition, ___displayedActor.CurrentRotation, ___displayedActor.Combat.LOS); Mod.Log.Debug?.Write($"Updating item visibility for enemy: {CombatantUtils.Label(___displayedActor)} to scanType: {scanType} and " + $"hasVisualScan: {hasVisualScan} from lastActivated: {CombatantUtils.Label(ModState.LastPlayerActorActivated)}"); // Values that are always displayed setGOActiveMethod.GetValue(__instance.NameDisplay, true); setGOActiveMethod.GetValue(__instance.PhaseDisplay, true); if (scanType >= SensorScanType.StructAndWeaponID) { // Show unit summary setGOActiveMethod.GetValue(__instance.DetailsDisplay, true); // Show active state setGOActiveMethod.GetValue(__instance.InspiredDisplay, false); // Show armor and struct setGOActiveMethod.GetValue(__instance.ArmorBar, true); setGOActiveMethod.GetValue(__instance.StructureBar, true); if (___displayedActor as Mech != null) { setGOActiveMethod.GetValue(__instance.StabilityDisplay, true); setGOActiveMethod.GetValue(__instance.HeatDisplay, true); } else { setGOActiveMethod.GetValue(__instance.StabilityDisplay, false); setGOActiveMethod.GetValue(__instance.HeatDisplay, false); } } else if (scanType >= SensorScanType.ArmorAndWeaponType || hasVisualScan) { // Show unit summary setGOActiveMethod.GetValue(__instance.DetailsDisplay, false); // Show active state setGOActiveMethod.GetValue(__instance.InspiredDisplay, false); // Show armor and struct setGOActiveMethod.GetValue(__instance.ArmorBar, true); setGOActiveMethod.GetValue(__instance.StructureBar, true); setGOActiveMethod.GetValue(__instance.StabilityDisplay, false); setGOActiveMethod.GetValue(__instance.HeatDisplay, false); } else { // Hide unit summary setGOActiveMethod.GetValue(__instance.DetailsDisplay, false); // Hide active state setGOActiveMethod.GetValue(__instance.InspiredDisplay, false); // Hide armor and struct setGOActiveMethod.GetValue(__instance.ArmorBar, false); setGOActiveMethod.GetValue(__instance.StructureBar, false); setGOActiveMethod.GetValue(__instance.StabilityDisplay, false); setGOActiveMethod.GetValue(__instance.HeatDisplay, false); } // TODO: DEBUG TESTING if (__instance.MarkDisplay != null) { setGOActiveMethod.GetValue(__instance.MarkDisplay, true); } CombatHUDStateStack stateStack = (CombatHUDStateStack)Traverse.Create(__instance).Property("StateStack").GetValue(); setGOActiveMethod.GetValue(stateStack, false); } else { if (__instance.MarkDisplay != null && ___displayedActor != null) { setGOActiveMethod.GetValue(__instance.MarkDisplay, ___displayedActor.IsMarked); } } } catch (Exception e) { Mod.Log.Info?.Write($"Error updating item visibility! Error was: {e.Message}"); } }
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); }