Skip to content

Commit

Permalink
melee swarmses
Browse files Browse the repository at this point in the history
  • Loading branch information
ajkroeg committed Jul 24, 2023
1 parent 41fe388 commit 841378a
Show file tree
Hide file tree
Showing 9 changed files with 180 additions and 59 deletions.
9 changes: 7 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
# StrategicOperations

**Depends On Abilifier v1.4.1.0 or higher!**
**Depends On Abilifier v1.4.1.0 or higher!,**

**Versions 3.1.2.4 and higher depend on CBTBehaviorsEnhanced 2.3.5 or higher**

**Versions 3.1.0.0 and higher requires modtek v3 or higher**

Expand Down Expand Up @@ -518,7 +520,8 @@ settings in the mod.json:
"b": 255
}
],
"BAMountReminderText": "Shift-click unit in drop slot to set carrier"
"BAMountReminderText": "Shift-click unit in drop slot to set carrier",
"MeleeOnSwarmAttacks": true
```

`enableLogging` - bool, enable logging
Expand Down Expand Up @@ -774,6 +777,8 @@ Using the following settings, ClanGhostBear and ClanWolf have baseline 30% chanc
`BAMountPairColors` - list of color configs (same format as `MountIndicatorColor` setting) that defines possible overlay colors for pre-mission squad and carrier assignments.

`BAMountReminderText` - controls reminder text for shift-click to assign squads to carriers, which is displayed below the mech widget in the lance config screen

`MeleeOnSwarmAttacks` - if true, BA will initiate a melee weapon attack when swarming (and when attacking during an ongoing swarm).

## Spawns

Expand Down
44 changes: 35 additions & 9 deletions StrategicOperations/StrategicOperations/Framework/Classes.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
using BattleTech;
using BattleTech.Data;
using BattleTech.UI;
using CBTBehaviorsEnhanced.MeleeStates;
using CustomComponents;
using CustomUnits;
using HBS.Collections;
Expand All @@ -13,6 +14,7 @@
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using UnityEngine;
using static MonoMod.Cil.RuntimeILReferenceBag.FastDelegateInvokers;

namespace StrategicOperations.Framework
{
Expand Down Expand Up @@ -1342,17 +1344,41 @@ public override void CompleteOrders()
}

var weps = squad2.Weapons.Where(x => x.IsEnabled && x.HasAmmo).ToList();
var loc = ModState.BADamageTrackers[squad2.GUID].BA_MountedLocations.Values
.GetRandomElement();

var attackStackSequence = new AttackStackSequence(squad2, targetActor,
squad2.CurrentPosition,
squad2.CurrentRotation, weps, MeleeAttackType.NotSet, loc, -1);
squad2.Combat.MessageCenter.PublishMessage(
new AddSequenceToStackMessage(attackStackSequence));

// var baselineAccuracyModifier = actor.StatCollection.GetValue<float>("AccuracyModifier");
// actor.StatCollection.Set<float>("AccuracyModifier", -99999.0f);
// ModInit.modLog?.Trace?.Write($"[AbstractActor.DoneWithActor] Actor {actor.DisplayName} getting baselineAccuracyModifer set to {actor.AccuracyModifier}");

var loc = ModState.BADamageTrackers[squad2.GUID].BA_MountedLocations.Values.GetRandomElement();

ModInit.modLog?.Info?.Write(
$"[StrategicMovementSequence - CompleteOrders] Creating attack sequence on successful swarm attack targeting location {loc}.");


if (squad2 is Mech unitMech && ModInit.modSettings.MeleeOnSwarmAttacks)
{
if (!ModState.SwarmMeleeSequences.ContainsKey(squad2.GUID))
{
ModState.SwarmMeleeSequences.Add(squad2.GUID, loc);
}
var meleeState = CBTBehaviorsEnhanced.ModState.AddorUpdateMeleeState(squad2, targetActor.CurrentPosition, targetActor, true);
if (meleeState != null)
{
MeleeAttack highestDamageAttackForUI = meleeState.GetHighestDamageAttackForUI();
CBTBehaviorsEnhanced.ModState.AddOrUpdateSelectedAttack(squad2, highestDamageAttackForUI);
}
MessageCenterMessage meleeInvocationMessage = new MechMeleeInvocation(unitMech, targetActor, weps, targetActor.CurrentPosition);
squad2.Combat.MessageCenter.PublishInvocationExternal(meleeInvocationMessage);
}
else
{
var attackStackSequence = new AttackStackSequence(squad2, targetActor, squad2.CurrentPosition,
squad2.CurrentRotation, weps, MeleeAttackType.NotSet, loc, -1);
squad2.Combat.MessageCenter.PublishMessage(new AddSequenceToStackMessage(attackStackSequence));

// actor.StatCollection.Set<float>("AccuracyModifier", baselineAccuracyModifier);
// ModInit.modLog?.Trace?.Write($"[AbstractActor.DoneWithActor] Actor {actor.DisplayName} resetting baselineAccuracyModifer to {actor.AccuracyModifier}");

}
}

//doattacksequencehere
Expand Down
2 changes: 2 additions & 0 deletions StrategicOperations/StrategicOperations/Framework/ModState.cs
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ namespace StrategicOperations.Framework
{
public static class ModState
{
public static Dictionary<string, int> SwarmMeleeSequences = new Dictionary<string, int>();
public static Dictionary<string, AI_CmdInvocation> AiCmds = new Dictionary<string, AI_CmdInvocation>();

public static Dictionary<string, AI_DealWithBAInvocation> AiDealWithBattleArmorCmds = new Dictionary<string, AI_DealWithBAInvocation>();
Expand Down Expand Up @@ -186,6 +187,7 @@ public static void Initialize()

public static void ResetAll()
{
SwarmMeleeSequences = new Dictionary<string, int>();
UsedOverlayColors = new List<Color>();
UsedOverlayColorsByCarrier = new Dictionary<string, Color>();
PendingPairBAUnit = null;
Expand Down
2 changes: 1 addition & 1 deletion StrategicOperations/StrategicOperations/Framework/Utils.cs
Original file line number Diff line number Diff line change
Expand Up @@ -1661,7 +1661,7 @@ public static HeraldryDef SwapHeraldryColors(HeraldryDef def, DataManager dataMa
return newHeraldry;
}

public static void TeleportActorVisual(this AbstractActor actor, Vector3 newPosition)
public static void TeleportActorNoResetPathing(this AbstractActor actor, Vector3 newPosition)
{
actor.CurrentPosition = newPosition;
actor.GameRep.transform.position = newPosition;
Expand Down
1 change: 1 addition & 0 deletions StrategicOperations/StrategicOperations/ModInit.cs
Original file line number Diff line number Diff line change
Expand Up @@ -183,5 +183,6 @@ class Settings
//public List<string> BeaconExcludedContractIDs = new List<string>();
public bool UsingMechAffinityForSwarmBreach = false;
public bool ReworkedCarrierEvasion = true;
public bool MeleeOnSwarmAttacks = true;
}
}
56 changes: 38 additions & 18 deletions StrategicOperations/StrategicOperations/Patches/AI_Patches.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
using System.Reflection;
using Abilifier.Patches;
using BattleTech;
using CBTBehaviorsEnhanced.MeleeStates;
using CustomUnits;
using IRTweaks.Modules.Combat;
using StrategicOperations.Framework;
Expand All @@ -22,18 +23,6 @@ public static void Prefix(ref bool __runOriginal, AITeam __instance, AbstractAct
if (!__runOriginal) return;
if (unit.HasSwarmingUnits()){

if (unit is FakeVehicleMech && !unit.HasMovedThisRound && order.OrderType == OrderType.Move || order.OrderType == OrderType.JumpMove || order.OrderType == OrderType.SprintMove)
{
var ability = unit.GetDeswarmerAbilityForAI(true);
if (ability.IsAvailable && !ability.IsActive)
{
ability.Activate(unit, unit);
ModInit.modLog?.Info?.Write($"{unit.DisplayName} {unit.GUID} is vehicle being swarmed. Found movement order, activating erratic maneuvers ability.");
__runOriginal = true;
return;
}
}

if (ModState.AiDealWithBattleArmorCmds.ContainsKey(unit.GUID))
{
ModState.AiDealWithBattleArmorCmds[unit.GUID].ability.Activate(unit, unit);
Expand All @@ -53,6 +42,18 @@ public static void Prefix(ref bool __runOriginal, AITeam __instance, AbstractAct
__runOriginal = false;
return;
}

if (unit is FakeVehicleMech && !unit.HasMovedThisRound && order.OrderType == OrderType.Move || order.OrderType == OrderType.JumpMove || order.OrderType == OrderType.SprintMove)
{
var ability = unit.GetDeswarmerAbilityForAI(true);
if (ability.IsAvailable && !ability.IsActive)
{
ability.Activate(unit, unit);
ModInit.modLog?.Info?.Write($"{unit.DisplayName} {unit.GUID} is vehicle being swarmed. Found movement order, activating erratic maneuvers ability.");
__runOriginal = true;
return;
}
}
}

if (unit.IsMountedUnit() && !ModState.StrategicActorTargetInvocationCmds.ContainsKey(unit.GUID))
Expand Down Expand Up @@ -96,7 +97,7 @@ public static void Prefix(ref bool __runOriginal, AITeam __instance, AbstractAct
}

var weps = unit.Weapons.Where(x => x.IsEnabled && x.HasAmmo).ToList();
if (weps.Count <= 0)
if (weps.Count <= 0 && !ModInit.modSettings.MeleeOnSwarmAttacks)
{
//dismount, no more weapons...should probably loop back up to try and resupply??
unit.DismountBA(target, Vector3.zero, false);
Expand All @@ -107,11 +108,29 @@ public static void Prefix(ref bool __runOriginal, AITeam __instance, AbstractAct
var loc = ModState.BADamageTrackers[unit.GUID].BA_MountedLocations.Values.GetRandomElement();
//var attackStackSequence = new AttackStackSequence(unit, target, unit.CurrentPosition, unit.CurrentRotation, weps, MeleeAttackType.NotSet, loc, -1);
//unit.Combat.MessageCenter.PublishMessage(new AddSequenceToStackMessage(attackStackSequence));
var vent = unit.HasVentCoolantAbility && unit.CanVentCoolant;
__result = new AttackInvocation(unit, target, weps, MeleeAttackType.NotSet, loc)

if (unit is Mech unitMech && ModInit.modSettings.MeleeOnSwarmAttacks)
{
if (!ModState.SwarmMeleeSequences.ContainsKey(unit.GUID))
{
ModState.SwarmMeleeSequences.Add(unit.GUID, loc);
}
var meleeState = CBTBehaviorsEnhanced.ModState.AddorUpdateMeleeState(unit, target.CurrentPosition, target, true);
if (meleeState != null)
{
MeleeAttack highestDamageAttackForUI = meleeState.GetHighestDamageAttackForUI();
CBTBehaviorsEnhanced.ModState.AddOrUpdateSelectedAttack(unit, highestDamageAttackForUI);
}
__result = new MechMeleeInvocation(unitMech, target, weps, target.CurrentPosition);
}
else
{
ventHeatBeforeAttack = vent
}; // making a regular attack invocation here, instead of stacksequence + reserve
var vent = unit.HasVentCoolantAbility && unit.CanVentCoolant;
__result = new AttackInvocation(unit, target, weps, MeleeAttackType.NotSet, loc)
{
ventHeatBeforeAttack = vent
}; // making a regular attack invocation here, instead of stacksequence + reserve
}

//if (!unit.HasMovedThisRound)
//{
Expand Down Expand Up @@ -431,7 +450,7 @@ public static void Prefix(ref bool __runOriginal, CanMoveAndShootWithoutOverheat
}

var weps = __instance.unit.Weapons.Where(x => x.IsEnabled && x.HasAmmo).ToList();
if (weps.Count <= 0)
if (weps.Count <= 0 && !ModInit.modSettings.MeleeOnSwarmAttacks)
{
goto noswarm;
}
Expand Down Expand Up @@ -479,6 +498,7 @@ public static void Prefix(ref bool __runOriginal, CanMoveAndShootWithoutOverheat
var deswarm = __instance.unit.GetDeswarmerAbilityForAI();
if (deswarm != null && deswarm?.Def?.Description?.Id != null)
{
ModInit.modLog?.Trace?.Write($"[CanMoveAndShootWithoutOverheatingNode] unit {__instance.unit.DisplayName} is being swarmed, found deswarm ability {deswarm.Def.Description.Name}.");
if (ModState.AiDealWithBattleArmorCmds.ContainsKey(__instance.unit.GUID))
{
if (ModState.AiDealWithBattleArmorCmds[__instance.unit.GUID].active)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,13 +6,15 @@
using BattleTech.Save;
using BattleTech.UI;
using BattleTech.UI.TMProWrapper;
using CBTBehaviorsEnhanced.MeleeStates;
using CustomActivatableEquipment;
using CustomComponents;
using CustomUnits;
using HBS.Extensions;
using HBS.Math;
using IRTweaks.Modules.Combat;
using Localize;
using Steamworks;
using StrategicOperations.Framework;
using SVGImporter;
using TMPro;
Expand Down Expand Up @@ -729,22 +731,41 @@ public static void Prefix(ref bool __runOriginal, CombatHUDButtonBase __instance
var target = actor.Combat.FindActorByGUID(ModState.PositionLockSwarm[actor.GUID]);
ModInit.modLog?.Info?.Write(
$"[CombatHUDButtonBase.OnClick] Actor {actor.DisplayName} has active swarm attack on {target.DisplayName}");

var weps = actor.Weapons.Where(x => x.IsEnabled && x.HasAmmo).ToList();

// var baselineAccuracyModifier = actor.StatCollection.GetValue<float>("AccuracyModifier");
// actor.StatCollection.Set<float>("AccuracyModifier", -99999.0f);
// ModInit.modLog?.Trace?.Write($"[AbstractActor.DoneWithActor] Actor {actor.DisplayName} getting baselineAccuracyModifer set to {actor.AccuracyModifier}");

var loc = ModState.BADamageTrackers[actor.GUID].BA_MountedLocations.Values.GetRandomElement();
var attackStackSequence = new AttackStackSequence(actor, target, actor.CurrentPosition,
actor.CurrentRotation, weps, MeleeAttackType.NotSet, loc, -1);
actor.Combat.MessageCenter.PublishMessage(new AddSequenceToStackMessage(attackStackSequence));

// actor.StatCollection.Set<float>("AccuracyModifier", baselineAccuracyModifier);
// ModInit.modLog?.Trace?.Write($"[AbstractActor.DoneWithActor] Actor {actor.DisplayName} resetting baselineAccuracyModifer to {actor.AccuracyModifier}");
__runOriginal = true;
return;
if (actor is Mech unitMech && ModInit.modSettings.MeleeOnSwarmAttacks)
{
if (!ModState.SwarmMeleeSequences.ContainsKey(actor.GUID))
{
ModState.SwarmMeleeSequences.Add(actor.GUID, loc);
}
var meleeState = CBTBehaviorsEnhanced.ModState.AddorUpdateMeleeState(actor, target.CurrentPosition, target, true);
if (meleeState != null)
{
MeleeAttack highestDamageAttackForUI = meleeState.GetHighestDamageAttackForUI();
CBTBehaviorsEnhanced.ModState.AddOrUpdateSelectedAttack(actor, highestDamageAttackForUI);
}
MessageCenterMessage meleeInvocationMessage = new MechMeleeInvocation(unitMech, target, weps, target.CurrentPosition);
actor.Combat.MessageCenter.PublishInvocationExternal(meleeInvocationMessage);
}
else
{
var attackStackSequence = new AttackStackSequence(actor, target, actor.CurrentPosition,
actor.CurrentRotation, weps, MeleeAttackType.NotSet, loc, -1);
actor.Combat.MessageCenter.PublishMessage(new AddSequenceToStackMessage(attackStackSequence));

// actor.StatCollection.Set<float>("AccuracyModifier", baselineAccuracyModifier);
// ModInit.modLog?.Trace?.Write($"[AbstractActor.DoneWithActor] Actor {actor.DisplayName} resetting baselineAccuracyModifer to {actor.AccuracyModifier}");
__runOriginal = true;
return;
}
}
}

Expand Down Expand Up @@ -1998,6 +2019,36 @@ public static void Postfix(MechRepresentation __instance, bool headlightsActive)
}
}

[HarmonyPatch(typeof(MechMeleeSequence), "BuildWeaponDirectorSequence", new Type[] { })]
public static class MechMeleeSequence_BuildWeaponDirectorSequence
{
public static void Prefix(ref bool __runOriginal, MechMeleeSequence __instance)
{
if (!__runOriginal) return;
if (ModState.SwarmMeleeSequences.TryGetValue(__instance.OwningMech.GUID, out var loc))
{
__instance.requestedWeapons.RemoveAll((Weapon x) => x.Type == WeaponType.Melee);
if (__instance.requestedWeapons.Count > 0)
{
__instance.weaponSequence = new AttackStackSequence(__instance.OwningMech,
__instance.MeleeTarget, __instance.moveSequence.FinalPos,
Quaternion.LookRotation(__instance.moveSequence.FinalHeading), __instance.requestedWeapons,
MeleeAttackType.NotSet, loc, 1);
__instance.weaponSequence.willConsumeFiring = false;
__instance.weaponSequence.hasOwningSequence = true;
__instance.weaponSequence.directorSequences[0].isParentMelee = true;
__instance.weaponSequence.RootSequenceGUID = __instance.RootSequenceGUID;
if (__instance.cameraSequence != null)
{
__instance.weaponSequence.SetCamera(__instance.cameraSequence, __instance.MessageIndex);
}
}
ModState.SwarmMeleeSequences.Remove(__instance.OwningMech.GUID);
__runOriginal = false;
}
}
}

[HarmonyPatch(typeof(SelectionStateAbilityInstant), "OnAddToStack", new Type[] { })]
public static class SelectionStateAbilityInstant_OnAddToStack
{
Expand Down
Loading

0 comments on commit 841378a

Please sign in to comment.