Skip to content

Commit

Permalink
- Refactored UrbanExplosion fix to catch HBS error in attackSequence.…
Browse files Browse the repository at this point in the history
… Transformer and Coolant explosions pass Buildings as targets to an ArtilleryObjectiveSequence, which expects inputs to be AbstractActors. This cases a hidden NRE that was unreported until I started patching it with harmony. This error is now correctly detected and prevented, allowing the attack sequence to finish. This does make them take slightly longer to complete, but should cause less weird errors.
  • Loading branch information
IceRaptor committed Jun 19, 2020
1 parent 421a571 commit 520424e
Show file tree
Hide file tree
Showing 5 changed files with 99 additions and 13 deletions.
1 change: 1 addition & 0 deletions IRTweaks/IRTweaks/IRTweaks.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -123,6 +123,7 @@
<Compile Include="Modules\UI\StoreQuantities.cs" />
<Compile Include="Modules\UI\UIModule.cs" />
<Compile Include="Modules\UI\WeaponTooltips.cs" />
<Compile Include="Patches\CombatGameStatePatches.cs" />
<Compile Include="Properties\AssemblyInfo.cs" />
<Compile Include="ModConfig.cs" />
<Compile Include="ModState.cs" />
Expand Down
9 changes: 3 additions & 6 deletions IRTweaks/IRTweaks/ModConfig.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@
namespace IRTweaks {

public class AbilityOpts {
public string FlexibleSensorLockId = "AbilityDefT8A";
public string JuggernautId = "AbilityDefGu8";
public string MultiTargetId = "AbilityDefG5";
}

Expand All @@ -23,9 +25,6 @@ public class CalledShotOpts {
public FlexibleSensorLockOptions FlexibleSensorLock = new FlexibleSensorLockOptions();
public class FlexibleSensorLockOptions {
public bool FreeActionWithAbility = false;

// Default to Master Tactician
public string AbilityId = "AbilityDefT8A";
}

public PainToleranceOpts PainTolerance = new PainToleranceOpts();
Expand Down Expand Up @@ -100,8 +99,6 @@ public class ModConfig {
public CombatOpts Combat = new CombatOpts();
public StoreOpts Store = new StoreOpts();

public string JuggernautAbilityId = "AbilityDefGu8";

public const string SimGameDifficultyString_Desc = "DESCRIPTION";
public const string SimGameDifficultyString_Label = "LABEL";
public Dictionary<string, string> SimGameDifficultyStrings = new Dictionary<string, string>()
Expand Down Expand Up @@ -154,7 +151,7 @@ public void LogConfig() {
Mod.Log.Info($" QuantityOnShift:{Store.QuantityOnShift} QuantityOnControl:{Store.QuantityOnControl}");

Mod.Log.Info(" -- Flexible Sensor Lock Options --");
Mod.Log.Info($" FreeActionWithAbility:{this.Combat.FlexibleSensorLock.FreeActionWithAbility} AbilityId:{this.Combat.FlexibleSensorLock.AbilityId}");
Mod.Log.Info($" FreeActionWithAbility:{this.Combat.FlexibleSensorLock.FreeActionWithAbility} AbilityId:{this.Abilities.FlexibleSensorLockId}");

Mod.Log.Info("=== MOD CONFIG END ===");
}
Expand Down
83 changes: 78 additions & 5 deletions IRTweaks/IRTweaks/Modules/Combat/UrbanExplosionsFix.cs
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
using BattleTech;
using CustomAmmoCategoriesLog;
using Harmony;
using IRBTModUtils.Extension;
using System;
using System.Collections.Generic;
using System.Linq;
using UnityEngine;

namespace IRTweaks.Modules.Combat
Expand All @@ -21,7 +23,7 @@ static void Postfix(ArtilleryObjectiveSequence __instance, ArtilleryVFXType ___a
(___artilleryVFXType == ArtilleryVFXType.ElectricTransformerExplosion || ___artilleryVFXType == ArtilleryVFXType.CoolantExplosion)
)
{
Mod.Log.Info("ELECTRICAL EXPLOSION FOUND!");
Mod.Log.Info($"ELECTRICAL EXPLOSION FOUND WITH SEQUENCE GUID {__instance.SequenceGUID}");
Mod.Log.Info($" - damageEachLocation: {__instance.damageEachLocation} heat: {__instance.heatDamage} stability: {__instance.stabilityDamage}");

foreach (Vector3 targetPos in __instance.TargetPositions)
Expand All @@ -43,22 +45,93 @@ static void Postfix(ArtilleryObjectiveSequence __instance, ArtilleryVFXType ___a
}
}


// The HBS logic has a subtle bug here... it passes ICombatant but then tries a cast to AbstractActor. Which will
// throw an NRE
[HarmonyPatch(typeof(ArtilleryObjectiveSequence), "AttackNextTarget")]
static class ArtilleryObjectiveSequence_AttackNextTarget
{
static bool Prepare() => Mod.Config.Fixes.UrbanExplosionsFix;

static void Prefix(ArtilleryObjectiveSequence __instance)
// Replica of HBS logic to work around the errors in this
static bool Prefix(ArtilleryObjectiveSequence __instance, float ___timeSinceLastAttack, float ___timeBetweenAttacks)
{
if (__instance != null && ModState.ExplosionSequences.Contains(__instance.SequenceGUID))

try
{
// If there's not
___timeSinceLastAttack += Time.deltaTime;
if (!(___timeSinceLastAttack > ___timeBetweenAttacks))
{
return false;
}

Traverse allTargetsT = Traverse.Create(__instance).Property("AllTargets");
List<ICombatant> allTargets = allTargetsT.GetValue<List<ICombatant>>();
if (allTargets != null && allTargets.Count > 0)
if (allTargets.Count > 0)
{
ICombatant combatant = allTargets[0];
Mod.Log.Info($"ArtilleryObjectiveSequence_{__instance.SequenceGUID} attacking target: {combatant.DistinctId()}");
allTargets.Remove(combatant);

if (__instance.damageEachLocation > 0f)
{
Mod.Log.Info($" -- applying {__instance.damageEachLocation} damage to all target locations.");
DamageOrderUtility.ApplyDamageToAllLocations("ArtillerySequence", __instance.SequenceGUID, __instance.RootSequenceGUID,
combatant, (int)__instance.damageEachLocation, (int)__instance.damageEachLocation, AttackDirection.FromArtillery, DamageType.Artillery);
}

if (__instance.heatDamage != 0)
{
Mod.Log.Info($" -- applying {__instance.damageEachLocation} heat to all target locations.");
DamageOrderUtility.ApplyHeatDamage(__instance.SequenceGUID, combatant, __instance.heatDamage);
}

if (__instance.stabilityDamage > 0)
{
Mod.Log.Info($" -- applying {__instance.damageEachLocation} instability to all target locations.");
DamageOrderUtility.ApplyStabilityDamage(__instance.SequenceGUID, combatant, __instance.stabilityDamage);
}

if (__instance.applyDesignMaskOnExplosion != 0)
{
AbstractActor targetActor = combatant as AbstractActor;
if (targetActor != null)
{
Mod.Log.Debug($" -- re-applying design masks to actor.");
targetActor.ReapplyDesignMasks();
}
else
{
Mod.Log.Debug($" -- target ICombatant isn't an AbstractActor, skipping design-mask re-apply.");
}
}
}

List<ICombatant> deadCombatants = new List<ICombatant>();
foreach (ICombatant combatant in allTargets)
{
Mod.Log.Info($" Attacking target: {allTargets[0].DistinctId()}");
if (combatant.IsDead || combatant.IsFlaggedForDeath)
{
Mod.Log.Info($" -- target {combatant.DistinctId()} is already dead, removing them from AllTargets.");
deadCombatants.Add(combatant);
}
}

foreach (ICombatant combatant in deadCombatants)
{
allTargets.Remove(combatant);
}

___timeSinceLastAttack = 0f;

return false;
}
catch (Exception e)
{
Mod.Log.Error("Failed to apply damage to targets from ArtilleryObjectiveSequence!", e);
return false;
}
}
}

Expand Down
15 changes: 15 additions & 0 deletions IRTweaks/IRTweaks/Patches/CombatGameStatePatches.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
using BattleTech;
using Harmony;

namespace IRTweaks.Patches
{
// Cleanup known combat state
[HarmonyPatch(typeof(CombatGameState), "OnCombatGameDestroyed")]
static class CombatGameState_OnCombatGameDestroyed
{
static void Postfix(CombatGameState __instance)
{
ModState.Reset();
}
}
}
4 changes: 2 additions & 2 deletions IRTweaks/IRTweaks/Properties/AssemblyInfo.cs
Original file line number Diff line number Diff line change
Expand Up @@ -31,5 +31,5 @@
// You can specify all the values or you can default the Build and Revision Numbers
// by using the '*' as shown below:
// [assembly: AssemblyVersion("1.0.*")]
[assembly: AssemblyVersion("0.6.7.0")]
[assembly: AssemblyFileVersion("0.6.7.0")]
[assembly: AssemblyVersion("0.6.8.0")]
[assembly: AssemblyFileVersion("0.6.8.0")]

0 comments on commit 520424e

Please sign in to comment.