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 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 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 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(); } } } }
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}"); } }
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 BuildCACDialogForTarget(AbstractActor source, ICombatant target, float range, bool hasVisualScan, SensorScanType scanType) { StringBuilder sb = new StringBuilder(); VisibilityLevel visLevel = source.VisibilityToTargetUnit(target); if (target is Mech mech) { string fullName = mech.Description.UIName; string chassisName = mech.UnitName; string partialName = mech.Nickname; string localName = CombatNameHelper.GetEnemyMechDetectionLabel(visLevel, scanType, fullName, partialName, chassisName).ToString(); string tonnage = "?"; if (scanType > SensorScanType.LocationAndType) { tonnage = new Text(Mod.LocalizedText.CACSidePanel[ModText.LT_CAC_SIDEPANEL_WEIGHT], new object[] { (int)Math.Floor(mech.tonnage) }).ToString(); } string titleText = new Text(Mod.LocalizedText.CACSidePanel[ModText.LT_CAC_SIDEPANEL_TITLE], new object[] { localName, tonnage }).ToString(); sb.Append(titleText); if (scanType > SensorScanType.StructAndWeaponID) { // Movement sb.Append(new Text(Mod.LocalizedText.CACSidePanel[ModText.LT_CAC_SIDEPANEL_MOVE_MECH], new object[] { mech.WalkSpeed, mech.RunSpeed, mech.JumpDistance }) .ToString() ); // Heat sb.Append(new Text(Mod.LocalizedText.CACSidePanel[ModText.LT_CAC_SIDEPANEL_HEAT], new object[] { mech.CurrentHeat, mech.MaxHeat }) .ToString() ); // Stability sb.Append(new Text(Mod.LocalizedText.CACSidePanel[ModText.LT_CAC_SIDEPANEL_STAB], new object[] { mech.CurrentStability, mech.MaxStability }) .ToString() ); } } else if (target is Turret turret) { string chassisName = turret.UnitName; string fullName = turret.Nickname; string localName = CombatNameHelper.GetTurretOrVehicleDetectionLabel(visLevel, scanType, fullName, chassisName, false).ToString(); string titleText = new Text(Mod.LocalizedText.CACSidePanel[ModText.LT_CAC_SIDEPANEL_TITLE], new object[] { localName, "" }).ToString(); sb.Append(titleText); } else if (target is Vehicle vehicle) { string chassisName = vehicle.UnitName; string fullName = vehicle.Nickname; string localName = CombatNameHelper.GetTurretOrVehicleDetectionLabel(visLevel, scanType, fullName, chassisName, true).ToString(); string tonnage = "?"; if (scanType > SensorScanType.LocationAndType) { tonnage = new Text(Mod.LocalizedText.CACSidePanel[ModText.LT_CAC_SIDEPANEL_WEIGHT], new object[] { (int)Math.Floor(vehicle.tonnage) }).ToString(); } string titleText = new Text(Mod.LocalizedText.CACSidePanel[ModText.LT_CAC_SIDEPANEL_TITLE], new object[] { localName, tonnage }).ToString(); sb.Append(titleText); if (scanType > SensorScanType.StructAndWeaponID) { // Movement sb.Append(new Text(Mod.LocalizedText.CACSidePanel[ModText.LT_CAC_SIDEPANEL_MOVE_VEHICLE], new object[] { vehicle.CruiseSpeed, vehicle.FlankSpeed }) .ToString() ); } } string distance = new Text(Mod.LocalizedText.CACSidePanel[ModText.LT_CAC_SIDEPANEL_DIST], new object[] { (int)Math.Ceiling(range) }).ToString(); sb.Append(distance); Text panelText = new Text(sb.ToString(), new object[] { }); CustAmmoCategories.CombatHUDInfoSidePanelHelper.SetTargetInfo(source, target, panelText); }
public static void UpdateActorLocks(AbstractActor source, ICombatant target, VisualScanType visualLock, SensorScanType sensorLock) { if (source != null && target != null) { Locks newLocks = new Locks(source, target, visualLock, sensorLock); if (PlayerActorLocks.ContainsKey(source.GUID)) { PlayerActorLocks[source.GUID][target.GUID] = newLocks; } else { PlayerActorLocks[source.GUID] = new Dictionary <string, Locks> { [target.GUID] = newLocks }; } } }