static void Postfix(ToHit __instance, ref string __result, AbstractActor attacker, Weapon weapon, ICombatant target, Vector3 attackPosition, Vector3 targetPosition, LineOfFireLevel lofLevel, bool isCalledShot) { Mod.Log.Trace?.Write("TH:GAMD entered"); if (attacker.HasMovedThisRound && attacker.JumpedLastRound || (SharedState.CombatHUD?.SelectionHandler?.ActiveState != null && SharedState.CombatHUD?.SelectionHandler?.ActiveState is SelectionStateJump)) { string localText = new Text(Mod.LocalizedText.Labels[ModText.LT_Label_Attacker_Jumped]).ToString(); __result = string.Format("{0}{1} {2:+#;-#}; ", __result, localText, Mod.Config.ToHitSelfJumped); } // Check melee patches MeleeAttack selectedAttack = ModState.GetSelectedAttack(attacker); if (selectedAttack != null && weapon.Type == WeaponType.Melee) { foreach (KeyValuePair <string, int> kvp in selectedAttack.AttackModifiers) { string localText = new Text(Mod.LocalizedText.Labels[kvp.Key]).ToString(); Mod.Log.Info?.Write($" - Found attack modifier for desc: {localText} = {kvp.Value}"); __result = string.Format("{0}{1} {2:+#;-#}; ", __result, localText, kvp.Value); } } }
static void Postfix(CombatHUDWeaponSlot __instance, Weapon ___displayedWeapon, CombatHUD ___HUD) { if (__instance == null || ___displayedWeapon == null || ___HUD.SelectedActor == null || !Mod.Config.Melee.FilterCanUseInMeleeWeaponsByAttack) { return; } MeleeAttack selectedAttack = ModState.GetSelectedAttack(___HUD.SelectedActor); if (selectedAttack != null) { Mod.UILog.Debug?.Write($"Checking ranged weapons attacker: {___HUD.SelectedActor.DistinctId()} using selectedAttack: {selectedAttack.Label}"); // Check if the weapon can fire according to the select melee type bool isAllowed = selectedAttack.IsRangedWeaponAllowed(___displayedWeapon); Mod.UILog.Debug?.Write($"Ranged weapon '{___displayedWeapon.UIName}' can fire in melee by type? {isAllowed}"); if (!isAllowed) { Mod.UILog.Debug?.Write($"Disabling weapon from selection"); __instance.ToggleButton.isChecked = false; Traverse showDisabledHexT = Traverse.Create(__instance).Method("ShowDisabledHex"); showDisabledHexT.GetValue(); } } //else //{ // // No selected attack, so revert the weapon to the state it should have // __instance.ToggleButton.isChecked = ___displayedWeapon.IsEnabled && !___displayedWeapon.IsDisabled; //} }
static void Postfix(ToHit __instance, ref float __result, Mech attacker, ICombatant target, Vector3 targetPosition, MeleeAttackType meleeAttackType) { Mod.Log.Trace?.Write("TH:GAMM entered"); if (__instance == null) { return; } MeleeAttack selectedAttack = ModState.GetSelectedAttack(attacker); if (selectedAttack == null) { return; } Mod.Log.Debug?.Write("Adding CBTBE modifiers to ToHit"); int sumMod = 0; foreach (KeyValuePair <string, int> kvp in selectedAttack.AttackModifiers) { string localText = new Text(Mod.LocalizedText.Labels[kvp.Key]).ToString(); Mod.Log.Debug?.Write($" - Found attack modifier: {localText} = {kvp.Value}, adding to sum modifier"); sumMod += kvp.Value; } __result += (float)sumMod; }
static void Postfix(Mech attacker, ICombatant target, ref MeleeAttackType __result) { MeleeAttack selectedAttack = ModState.GetSelectedAttack(attacker); if (selectedAttack != null) { Mod.Log.Info?.Write($"Forcing melee animation to {selectedAttack.AttackAnimation} for " + $"attacker: {attacker.DistinctId()} vs. target: {target.DistinctId()}"); __result = selectedAttack.AttackAnimation; } }
static void Postfix(SelectionStateJump __instance, ref float __result) { Mod.Log.Trace?.Write("SSJ:PSFS - entered."); MeleeAttack selectedAttack = ModState.GetSelectedAttack(__instance.SelectedActor); if (__instance.SelectedActor is Mech selectedMech && selectedAttack != null && __instance.PotentialMeleeTarget != null) { float newStability = selectedMech.CurrentStability + selectedAttack.AttackerInstability; float minStability = selectedMech.GetMinStability(StabilityChangeSource.DFA, newStability); Mod.Log.Debug?.Write($"Stability change for {CombatantUtils.Label(selectedMech)} => " + $"current: {selectedMech.CurrentStability} projectedNew: {selectedAttack.AttackerInstability} " + $"totalChange: {newStability} afterDump: {minStability}"); __result = minStability; } }
static void Postfix(MechMeleeSequence __instance, Mech mech, ICombatant meleeTarget, List <Weapon> requestedWeapons, Vector3 desiredMeleePosition) { try { // Find the selectedAttack we should use for this sequence MeleeAttack selectedAttack = ModState.GetSelectedAttack(mech); if (selectedAttack == null) { Mod.MeleeLog.Warn?.Write($"Melee sequence {__instance.SequenceGUID} has no pre-selected attack state, will have to autoselected. Let Frost know as this should not happen!"); MeleeState meleeState = ModState.AddorUpdateMeleeState(mech, desiredMeleePosition, meleeTarget as AbstractActor); if (meleeState == null) { Mod.Log.Error?.Write($"Could not build melee state for selected melee attack - this should NEVER happen!"); return; } selectedAttack = meleeState.GetHighestDamageAttackForUI(); } if (selectedAttack == null || !selectedAttack.IsValid) { Mod.Log.Error?.Write($"Could not select a valid attack for the selected sequence - this should NEVER happen!"); return; } // Check to see if we have an imaginary weapon to use; if not create it (Weapon meleeWeapon, Weapon dfaWeapon)weapons = ModState.GetFakedWeapons(mech); // Create the weapon + representation ModState.AddOrUpdateMeleeSequenceState(__instance.SequenceGUID, selectedAttack, weapons.meleeWeapon); StringBuilder sb = new StringBuilder(); foreach (Weapon weapon in requestedWeapons) { sb.Append(weapon.UIName); sb.Append(","); } Mod.MeleeLog.Info?.Write($" -- Initial requested weapons: {sb}"); isValid = true; } catch (Exception e) { Mod.Log.Error?.Write(e, $"Failed to initialize Melee sequence {__instance.SequenceGUID}!"); } }
static void Postfix(CombatHUDWeaponSlot __instance, Weapon ___displayedWeapon, CombatHUD ___HUD, int ___displayedHeat) { if (__instance == null || ___displayedWeapon == null || ___HUD.SelectedActor == null) { return; } Mod.UILog.Trace?.Write("CHUDWS:GTTS entered"); // Check melee patches MeleeAttack selectedAttack = ModState.GetSelectedAttack(___HUD.SelectedActor); if (selectedAttack != null && ___displayedWeapon.Type == WeaponType.Melee) { if (___displayedWeapon.WeaponSubType == WeaponSubType.Melee) { float targetDamage = selectedAttack.TargetDamageClusters.Sum(); Mod.UILog.Trace?.Write($" - Extra Strings for type: {___displayedWeapon.Type} && {___displayedWeapon.WeaponSubType} " + $"=> Damage: {targetDamage} instability: {selectedAttack.TargetInstability} " + $"heat: {___displayedHeat}"); __instance.ToolTipHoverElement.ExtraStrings = new List <Text> { new Text(Mod.LocalizedText.Labels[ModText.LT_Label_Weapon_Hover_Damage], targetDamage), new Text(Mod.LocalizedText.Labels[ModText.LT_Label_Weapon_Hover_Instability], selectedAttack.TargetInstability), new Text(Mod.LocalizedText.Labels[ModText.LT_Label_Weapon_Hover_Heat], ___displayedHeat) }; } else if (___displayedWeapon.WeaponSubType == WeaponSubType.DFA) { float targetDamage = selectedAttack.TargetDamageClusters.Sum(); Mod.UILog.Trace?.Write($" - Extra Strings for type: {___displayedWeapon.Type} && {___displayedWeapon.WeaponSubType} " + $"=> Damage: {targetDamage} instability: {selectedAttack.TargetInstability} " + $"heat: {___displayedHeat}"); __instance.ToolTipHoverElement.ExtraStrings = new List <Text> { new Text(Mod.LocalizedText.Labels[ModText.LT_Label_Weapon_Hover_Damage], targetDamage), new Text(Mod.LocalizedText.Labels[ModText.LT_Label_Weapon_Hover_Instability], selectedAttack.TargetInstability), new Text(Mod.LocalizedText.Labels[ModText.LT_Label_Weapon_Hover_Heat], ___displayedHeat) }; } } }
static void Postfix(CombatHUDWeaponSlot __instance, ICombatant target, Weapon ___displayedWeapon, CombatHUD ___HUD) { if (__instance == null || ___displayedWeapon == null || ___HUD.SelectedActor == null || target == null) { return; } Mod.UILog.Trace?.Write("CHUDWS:SHC entered"); Traverse addToolTipDetailT = Traverse.Create(__instance) .Method("AddToolTipDetail", new Type[] { typeof(string), typeof(int) }); AbstractActor actor = __instance.DisplayedWeapon.parent; if (actor.HasMovedThisRound && actor.JumpedLastRound || (SharedState.CombatHUD?.SelectionHandler?.ActiveState != null && SharedState.CombatHUD?.SelectionHandler?.ActiveState is SelectionStateJump)) { string localText = new Text(Mod.LocalizedText.Labels[ModText.LT_Label_Attacker_Jumped]).ToString(); Mod.UILog.Debug?.Write($" Adding Attacker Jump modifier of: {Mod.Config.ToHitSelfJumped}"); addToolTipDetailT.GetValue(new object[] { localText, Mod.Config.ToHitSelfJumped }); } // Check melee patches MeleeAttack selectedAttack = ModState.GetSelectedAttack(___HUD.SelectedActor); if (selectedAttack != null && ___displayedWeapon.Type == WeaponType.Melee) { if (___displayedWeapon.WeaponSubType == WeaponSubType.Melee || ___displayedWeapon.WeaponSubType == WeaponSubType.DFA) { foreach (KeyValuePair <string, int> kvp in selectedAttack.AttackModifiers) { string localText = new Text(Mod.LocalizedText.Labels[kvp.Key]).ToString(); Mod.UILog.Debug?.Write($" - SetHitChance found attack modifier: {localText} = {kvp.Value}"); addToolTipDetailT.GetValue(new object[] { localText, kvp.Value }); } } } }
static void Postfix(SelectionStateMove __instance, ref float __result) { Mod.Log.Trace?.Write("SSM:PSFS - entered."); MeleeAttack selectedAttack = ModState.GetSelectedAttack(__instance.SelectedActor); if (__instance.SelectedActor is Mech selectedMech && selectedAttack != null && __instance.PotentialMeleeTarget != null) { float newStability = selectedMech.CurrentStability + selectedAttack.AttackerInstability; List <WayPoint> waypoints = ActorMovementSequence.ExtractWaypointsFromPath(selectedMech, selectedMech.Pathing.CurrentPath, selectedMech.Pathing.ResultDestination, selectedMech.Pathing.CurrentMeleeTarget, selectedMech.Pathing.MoveType); StabilityChangeSource changeSource = StabilityChangeSource.Moving; if (WayPoint.GetDistFromWaypointList(selectedMech.CurrentPosition, waypoints) < 1f) { changeSource = StabilityChangeSource.RemainingStationary; } float minStability = selectedMech.GetMinStability(changeSource, newStability); Mod.Log.Debug?.Write($"Stability change for {CombatantUtils.Label(selectedMech)} => " + $"current: {selectedMech.CurrentStability} projectedNew: {selectedAttack.AttackerInstability} " + $"totalChange: {newStability} afterDump: {minStability}"); __result = minStability; } }
static void Postfix(CombatHUDWeaponSlot __instance, Weapon ___displayedWeapon, CombatHUD ___HUD) { if (__instance == null || ___displayedWeapon == null || ___HUD.SelectedActor == null) { return; } Mod.UILog.Trace?.Write("CHUDWS:UDFAW entered"); MeleeAttack selectedAttack = ModState.GetSelectedAttack(___HUD.SelectedActor); if (selectedAttack == null || !(selectedAttack is DFAAttack)) { Mod.UILog.Debug?.Write("Defaulting DFA damage."); Mech parentMech = ___displayedWeapon.parent as Mech; float targetDamage = parentMech.DFATargetDamage(); __instance.DamageText.SetText($"{targetDamage}"); } else if (selectedAttack is DFAAttack) { Mod.UILog.Debug?.Write("Updating labels for DFA state."); float totalDamage = selectedAttack.TargetDamageClusters.Sum(); if (selectedAttack.TargetDamageClusters.Length > 1) { int avgDamage = (int)Math.Floor(totalDamage / selectedAttack.TargetDamageClusters.Length); string damageS = $"{avgDamage} <size=80%>(x{selectedAttack.TargetDamageClusters.Length})"; Mod.UILog.Debug?.Write($" - damageS is: {damageS}"); __instance.DamageText.SetText(damageS); } else { __instance.DamageText.SetText($"{totalDamage}"); Mod.UILog.Debug?.Write($" - damageS is: {totalDamage}"); } } }
static void Postfix(CombatHUDWeaponSlot __instance, Weapon ___displayedWeapon, CombatHUD ___HUD) { if (__instance == null || ___displayedWeapon == null || ___HUD.SelectedActor == null) { return; } Mod.UILog.Trace?.Write("CHUDWS:UMW entered"); MeleeAttack selectedAttack = ModState.GetSelectedAttack(___HUD.SelectedActor); if (selectedAttack == null || selectedAttack is DFAAttack) { string weaponLabel = new Text(Mod.LocalizedText.Labels[ModText.LT_Label_Weapon_Panel_Melee_Weapon], new object[] { Mod.LocalizedText.Labels[ModText.LT_Label_Weapon_Panel_Melee_No_Attack_Type] } ).ToString(); __instance.WeaponText.SetText(weaponLabel); Mech parentMech = ___displayedWeapon.parent as Mech; float kickDam = parentMech.KickDamage(); float punchDam = parentMech.PunchDamage(); float weapDam = parentMech.PhysicalWeaponDamage(); string damageText = new Text(Mod.LocalizedText.Labels[ModText.LT_Label_Weapon_Panel_Melee_No_Attack_Type_Damage], new object[] { weapDam, punchDam, kickDam } ).ToString(); __instance.DamageText.SetText(damageText); } else if (selectedAttack is ChargeAttack || selectedAttack is KickAttack || selectedAttack is PunchAttack || selectedAttack is WeaponAttack) { string attackName = "UNKNOWN"; if (selectedAttack is ChargeAttack) { attackName = Mod.LocalizedText.Labels[ModText.LT_Label_Melee_Type_Charge]; } else if (selectedAttack is KickAttack) { attackName = Mod.LocalizedText.Labels[ModText.LT_Label_Melee_Type_Kick]; } else if (selectedAttack is WeaponAttack) { attackName = Mod.LocalizedText.Labels[ModText.LT_Label_Melee_Type_Physical_Weapon]; } else if (selectedAttack is PunchAttack) { attackName = Mod.LocalizedText.Labels[ModText.LT_Label_Melee_Type_Punch]; } string weaponLabel = new Text(Mod.LocalizedText.Labels[ModText.LT_Label_Weapon_Panel_Melee_Weapon], new object[] { attackName } ).ToString(); __instance.WeaponText.SetText(weaponLabel); float totalDamage = selectedAttack.TargetDamageClusters.Sum(); if (selectedAttack.TargetDamageClusters.Length > 1) { int avgDamage = (int)Math.Floor(totalDamage / selectedAttack.TargetDamageClusters.Length); __instance.DamageText.SetText($"{avgDamage} <size=80%>(x{selectedAttack.TargetDamageClusters.Length})"); } else { __instance.DamageText.SetText($"{totalDamage}"); } ___displayedWeapon.StatCollection.Set <float>(ModStats.HBS_Weapon_DamagePerShot, totalDamage); ___displayedWeapon.StatCollection.Set <float>(ModStats.HBS_Weapon_Instability, selectedAttack.TargetInstability); CustAmmoCategories.DamageModifiersCache.ClearDamageCache(___displayedWeapon); } }
static bool Prefix(CombatHUDFireButton __instance) { if (__instance == null || __instance.gameObject == null) { return(true); } Mod.UILog.Info?.Write($"CHUDFB - OnClick FIRED for FireMode: {__instance.CurrentFireMode}"); bool shouldReturn = true; CombatHUDAttackModeSelector selector = SharedState.CombatHUD.AttackModeSelector; if (__instance.gameObject.name == ModConsts.ChargeFB_GO_ID) { MeleeState meleeState = ModState.GetMeleeState( SharedState.CombatHUD.SelectionHandler.ActiveState.SelectedActor, SharedState.CombatHUD.SelectionHandler.ActiveState.PreviewPos); ModState.AddOrUpdateSelectedAttack( SharedState.CombatHUD.SelectionHandler.ActiveState.SelectedActor, meleeState.Charge ); Mod.UILog.Info?.Write("User selected Charge button"); shouldReturn = false; } else if (__instance.gameObject.name == ModConsts.KickFB_GO_ID) { MeleeState meleeState = ModState.GetMeleeState( SharedState.CombatHUD.SelectionHandler.ActiveState.SelectedActor, SharedState.CombatHUD.SelectionHandler.ActiveState.PreviewPos); ModState.AddOrUpdateSelectedAttack( SharedState.CombatHUD.SelectionHandler.ActiveState.SelectedActor, meleeState.Kick ); Mod.UILog.Info?.Write("User selected Kick button"); shouldReturn = false; } else if (__instance.gameObject.name == ModConsts.PhysicalWeaponFB_GO_ID) { MeleeState meleeState = ModState.GetMeleeState( SharedState.CombatHUD.SelectionHandler.ActiveState.SelectedActor, SharedState.CombatHUD.SelectionHandler.ActiveState.PreviewPos); ModState.AddOrUpdateSelectedAttack( SharedState.CombatHUD.SelectionHandler.ActiveState.SelectedActor, meleeState.PhysicalWeapon ); Mod.UILog.Info?.Write("User selected PhysWeap button"); shouldReturn = false; } else if (__instance.gameObject.name == ModConsts.PunchFB_GO_ID) { MeleeState meleeState = ModState.GetMeleeState( SharedState.CombatHUD.SelectionHandler.ActiveState.SelectedActor, SharedState.CombatHUD.SelectionHandler.ActiveState.PreviewPos); ModState.AddOrUpdateSelectedAttack( SharedState.CombatHUD.SelectionHandler.ActiveState.SelectedActor, meleeState.Punch ); Mod.UILog.Info?.Write("User selected Punch button"); shouldReturn = false; } else { MeleeAttack selectedAttack2 = ModState.GetSelectedAttack(SharedState.CombatHUD?.SelectionHandler?.ActiveState?.SelectedActor); if (selectedAttack2 != null) { Mod.UILog.Info?.Write("OnClick from generic CHUDFB with selected type, short-cutting to action."); // Disable the buttons to prevent accidental clicks? if (selectedAttack2 is ChargeAttack) { ModState.ChargeFB.CurrentFireMode = CombatHUDFireButton.FireMode.None; } if (selectedAttack2 is KickAttack) { ModState.KickFB.CurrentFireMode = CombatHUDFireButton.FireMode.None; } if (selectedAttack2 is WeaponAttack) { ModState.PhysicalWeaponFB.CurrentFireMode = CombatHUDFireButton.FireMode.None; } if (selectedAttack2 is PunchAttack) { ModState.PunchFB.CurrentFireMode = CombatHUDFireButton.FireMode.None; } return(true); } } MeleeAttack selectedAttack = ModState.GetSelectedAttack(SharedState.CombatHUD?.SelectionHandler?.ActiveState?.SelectedActor); if (selectedAttack != null) { Mod.UILog.Debug?.Write("Enabling description container for melee attack"); selector.DescriptionContainer.SetActive(true); selector.DescriptionContainer.gameObject.SetActive(true); HashSet <string> descriptonNotes = selectedAttack.DescriptionNotes; string description = String.Join(", ", descriptonNotes); Mod.UILog.Debug?.Write($"Aggregate description is: {description}"); selector.DescriptionText.SetText(description); selector.DescriptionText.ForceMeshUpdate(true); // TODO: Update weapon damage instead? // Update the weapon strings SharedState.CombatHUD.WeaponPanel.RefreshDisplayedWeapons(); } return(shouldReturn); }