Files
7d2dXG/Mods/0A-KFCommonUtilityLib/Harmony/MultiActionPatches.cs
Nathaniel Cosford e06f2bd282 Add All Mods
2025-05-29 23:33:28 +09:30

2932 lines
132 KiB
C#

using GameEvent.SequenceActions;
using HarmonyLib;
using KFCommonUtilityLib.Scripts.NetPackages;
using KFCommonUtilityLib.Scripts.StaticManagers;
using KFCommonUtilityLib.Scripts.Utilities;
using System;
using System.Collections.Generic;
using System.Reflection;
using System.Reflection.Emit;
using UniLinq;
using UnityEngine;
namespace KFCommonUtilityLib.Harmony
{
//done?: patch all accesses to ItemClass.Actions so that they process all actions
//done?: patch ItemClass.ExecuteAction, MinEvent triggers
//done: replace GameManager.ItemReload*
//done: patch ItemActionRanged.ConsumeAmmo
//todo: patch passive effect handling and trigger effect firing, in ItemValue.ModifyValue set action index from tags
//todo: patch trigger action index enum/ or just let it use secondary and tag check?
//todo: handle ItemActionAttack.GetDamageEntity/GetDamageBlock and call sites actionIndex
//todo: sell, assemble, scrap remove ammo
//nope, not gonna work
//try old style action toggle,
//replace meta and ammo index when toggling, => solves ammo issue
//provide requirement for checking current mode, => solves effect group condition issue
//replace hardcoded action index with current mode => bulk patch is enough?
//ItemClass subclass? maybe not
//is inventory update on remote client really needed? put off
//todo: figure out when is meta and ammo index used, how to set their value in minimum patches
//ExecuteAction, Reload, what's more?
//safe to work within ItemAction scope
//even if meta and ammo index is set accordingly, better keep checking them in reload script
[HarmonyPatch]
public static class MultiActionPatches
{
#region Run Correct ItemAction
#region Ranged Reload
//Replace reload action index with animator item action index parameter
//set MinEventParams.ItemActionData before getting passive value
[HarmonyPatch(typeof(AnimatorRangedReloadState), nameof(AnimatorRangedReloadState.OnStateEnter))]
[HarmonyTranspiler]
private static IEnumerable<CodeInstruction> Transpiler_OnStateEnter_AnimatorRangedReloadState(IEnumerable<CodeInstruction> instructions, ILGenerator generator)
{
var codes = instructions.ToList();
LocalBuilder lbd_index = generator.DeclareLocal(typeof(int));
FieldInfo fld_action = AccessTools.Field(typeof(ItemClass), nameof(ItemClass.Actions));
FieldInfo fld_actionData = AccessTools.Field(typeof(ItemInventoryData), nameof(ItemInventoryData.actionData));
FieldInfo fld_meta = AccessTools.Field(typeof(ItemValue), nameof(ItemValue.Meta));
FieldInfo fld_ammoindex = AccessTools.Field(typeof(ItemValue), nameof(ItemValue.SelectedAmmoTypeIndex));
MethodInfo mtd_getvalue = AccessTools.Method(typeof(EffectManager), nameof(EffectManager.GetValue));
bool firstRet = true;
for (int i = 0; i < codes.Count; i++)
{
var code = codes[i];
if ((code.LoadsField(fld_action) || code.LoadsField(fld_actionData)) && codes[i + 1].opcode == OpCodes.Ldc_I4_0)
{
//get correct ItemAction and data
codes[i + 1].opcode = OpCodes.Ldloc_S;
codes[i + 1].operand = lbd_index;
}
else if (code.Calls(mtd_getvalue))
{
codes.InsertRange(i, new[]
{
new CodeInstruction(OpCodes.Ldarg_0),
CodeInstruction.LoadField(typeof(AnimatorRangedReloadState), "actionData"),
CodeInstruction.LoadField(typeof(ItemActionData), nameof(ItemActionData.invData)),
CodeInstruction.LoadField(typeof(ItemInventoryData), nameof(ItemInventoryData.holdingEntity)),
CodeInstruction.LoadField(typeof(EntityAlive), nameof(EntityAlive.MinEventContext)),
new CodeInstruction(OpCodes.Ldarg_0),
CodeInstruction.LoadField(typeof(AnimatorRangedReloadState), "actionData"),
CodeInstruction.StoreField(typeof(MinEventParams), nameof(MinEventParams.ItemActionData))
});
break;
}
else if (code.opcode == OpCodes.Ret && firstRet)
{
firstRet = false;
var insert = new[]
{
new CodeInstruction(OpCodes.Ldloc_0),
CodeInstruction.Call(typeof(MultiActionManager), nameof(MultiActionManager.GetActionIndexForEntity)),
new CodeInstruction(OpCodes.Stloc_S, lbd_index)
};
insert[0].MoveLabelsFrom(codes[i + 1]);
codes.InsertRange(i + 1, insert);
}
}
return codes;
}
[HarmonyPatch(typeof(AnimatorRangedReloadState), nameof(AnimatorRangedReloadState.OnStateExit))]
[HarmonyTranspiler]
private static IEnumerable<CodeInstruction> Transpiler_OnStateExit_AnimatorRangedReloadState(IEnumerable<CodeInstruction> instructions)
{
var codes = instructions.ToList();
MethodInfo mtd_getvalue = AccessTools.Method(typeof(EffectManager), nameof(EffectManager.GetValue));
for (int i = 0; i < codes.Count; i++)
{
var code = codes[i];
if (code.Calls(mtd_getvalue))
{
codes.InsertRange(i, new[]
{
new CodeInstruction(OpCodes.Ldarg_0),
CodeInstruction.LoadField(typeof(AnimatorRangedReloadState), nameof(AnimatorRangedReloadState.actionData)),
CodeInstruction.LoadField(typeof(ItemActionData), nameof(ItemActionData.invData)),
CodeInstruction.LoadField(typeof(ItemInventoryData), nameof(ItemInventoryData.holdingEntity)),
CodeInstruction.LoadField(typeof(EntityAlive), nameof(EntityAlive.MinEventContext)),
new CodeInstruction(OpCodes.Ldarg_0),
CodeInstruction.LoadField(typeof(AnimatorRangedReloadState), nameof(AnimatorRangedReloadState.actionData)),
CodeInstruction.StoreField(typeof(MinEventParams), nameof(MinEventParams.ItemActionData))
});
break;
}
}
return codes;
}
#endregion
//KEEP
#region Aiming
[HarmonyPatch(typeof(EntityPlayerLocal), nameof(EntityPlayerLocal.IsAimingGunPossible))]
[HarmonyPrefix]
private static bool Prefix_IsAimingGunPossible_EntityPlayerLocal(ref bool __result, EntityPlayerLocal __instance)
{
__result = true;
for (int i = 0; i < __instance.inventory.holdingItem.Actions.Length; i++)
{
ItemAction action = __instance.inventory.holdingItem.Actions[i];
ItemActionData actionData = __instance.inventory.holdingItemData.actionData[i];
__result &= (action == null || action.IsAimingGunPossible(actionData));
}
return false;
}
#endregion
//KEEP
#region Cancel bow draw
[HarmonyPatch(typeof(EntityPlayerLocal), nameof(EntityPlayerLocal.TryCancelBowDraw))]
[HarmonyPrefix]
private static bool Prefix_TryCancelBowDraw(EntityPlayerLocal __instance)
{
for (int i = 0; i < __instance.inventory.holdingItem.Actions.Length; i++)
{
ItemAction action = __instance.inventory.holdingItem.Actions[i];
ItemActionData actionData = __instance.inventory.holdingItemData.actionData[i];
if (action is ItemActionCatapult catapult)
{
action.CancelAction(actionData);
actionData.HasExecuted = false;
}
}
return false;
}
#endregion
//KEEP
#region Consume wheel scroll
[HarmonyPatch(typeof(ItemClass), nameof(ItemClass.ConsumeScrollWheel))]
[HarmonyPrefix]
private static bool Prefix_ConsumeScrollWheel_ItemClass(ItemClass __instance, ItemInventoryData _data, float _scrollWheelInput, PlayerActionsLocal _playerInput, ref bool __result)
{
__result = false;
for (int i = 0; i < __instance.Actions.Length; i++)
{
ItemAction action = __instance.Actions[i];
if (action != null && action.ConsumeScrollWheel(_data.actionData[i], _scrollWheelInput, _playerInput))
{
__result = true;
break;
}
}
return false;
}
#endregion
//KEEP
#region Create modifier data for more actions
[HarmonyPatch(typeof(ItemClass), nameof(ItemClass.CreateInventoryData))]
[HarmonyPostfix]
private static void Postfix_CreateInventoryData_ItemClass(ItemInventoryData __result, ItemClass __instance)
{
int prevCount = __result.actionData.Count;
while (__result.actionData.Count < __instance.Actions.Length)
{
__result.actionData.Add(null);
}
for (; prevCount < __instance.Actions.Length; prevCount++)
{
if (__instance.Actions[prevCount] != null)
__result.actionData[prevCount] = __instance.Actions[prevCount].CreateModifierData(__result, prevCount);
}
}
#endregion
//todo: should I patch ItemClass.ExecuteAction for melee actions?
//KEEP
#region IsFocusBlockInside?
[HarmonyPatch(typeof(ItemClass), nameof(ItemClass.IsFocusBlockInside))]
[HarmonyPrefix]
private static bool Prefix_IsFocusBlockInside_ItemClass(ItemClass __instance, ref bool __result)
{
__result = __instance.Actions.All(action => action != null && action.IsFocusBlockInside());
return false;
}
#endregion
//KEEP
#region IsHUDDisabled
[HarmonyPatch(typeof(ItemClass), nameof(ItemClass.IsHUDDisabled))]
[HarmonyPrefix]
private static bool Prefix_IsHUDDisabled_ItemClass(ItemClass __instance, ref bool __result, ItemInventoryData _data)
{
__result = false;
for (int i = 0; i < __instance.Actions.Length; i++)
{
__result |= __instance.Actions[i] != null && __instance.Actions[i].IsHUDDisabled(_data.actionData[i]);
}
return false;
}
#endregion
//KEEP
#region OnHUD
[HarmonyPatch(typeof(ItemClass), nameof(ItemClass.OnHUD))]
[HarmonyPrefix]
private static bool Prefix_OnHUD_ItemClass(ItemInventoryData _data, int _x, int _y, ItemClass __instance)
{
for (int i = 0; i < __instance.Actions.Length; i++)
{
__instance.Actions[i]?.OnHUD(_data.actionData[i], _x, _y);
}
return false;
}
[HarmonyPatch(typeof(ItemClass), nameof(ItemClass.GetCrosshairType))]
[HarmonyTranspiler]
private static IEnumerable<CodeInstruction> Transpiler_GetCrosshairType_ItemClass(IEnumerable<CodeInstruction> instructions, ILGenerator generator)
{
var codes = instructions.ToList();
LocalBuilder lbd_index = generator.DeclareLocal(typeof(int));
FieldInfo fld_action = AccessTools.Field(typeof(ItemClass), nameof(ItemClass.Actions));
for (int i = 0; i < codes.Count; i++)
{
var code = codes[i];
if (code.LoadsField(fld_action) && codes[i + 1].opcode == OpCodes.Ldc_I4_0)
{
codes[i + 1].opcode = OpCodes.Ldloc_S;
codes[i + 1].operand = lbd_index;
}
}
codes.InsertRange(0, new[]
{
new CodeInstruction(OpCodes.Ldarg_1),
CodeInstruction.LoadField(typeof(ItemInventoryData), nameof(ItemInventoryData.holdingEntity)),
CodeInstruction.Call(typeof(MultiActionManager), nameof(MultiActionManager.GetActionIndexForEntity)),
new CodeInstruction(OpCodes.Stloc_S, lbd_index)
});
return codes;
}
#endregion
//KEEP
#region OnScreenOverlay
[HarmonyPatch(typeof(ItemClass), nameof(ItemClass.OnScreenOverlay))]
[HarmonyPrefix]
private static bool Prefix_OnScreenOverlay_ItemClass(ItemInventoryData _data, ItemClass __instance)
{
for (int i = 0; i < __instance.Actions.Length; i++)
{
__instance.Actions[i]?.OnScreenOverlay(_data.actionData[i]);
}
return false;
}
#endregion
#region inventory related
[HarmonyPatch(typeof(Inventory), nameof(Inventory.GetHoldingGun))]
[HarmonyPrefix]
private static bool Prefix_GetHoldingGun_Inventory(Inventory __instance, ref ItemActionAttack __result)
{
__result = __instance.holdingItem.Actions[MultiActionManager.GetActionIndexForEntity(__instance.entity)] as ItemActionAttack ?? __instance.holdingItem.Actions[0] as ItemActionAttack;
return false;
}
[HarmonyPatch(typeof(Inventory), nameof(Inventory.GetHoldingDynamicMelee))]
[HarmonyPrefix]
private static bool Prefix_GetHoldingDynamicMelee_Inventory(Inventory __instance, ref ItemActionDynamic __result)
{
__result = __instance.holdingItem.Actions[MultiActionManager.GetActionIndexForEntity(__instance.entity)] as ItemActionDynamic ?? __instance.holdingItem.Actions[0] as ItemActionDynamic;
return false;
}
//[HarmonyPatch(typeof(Inventory), "clearSlotByIndex")]
//[HarmonyPrefix]
//private static bool Prefix_clearSlotByIndex_Inventory(int _idx, Inventory __instance, EntityAlive ___entity)
//{
// if (__instance.holdingItemIdx == _idx && ___entity != null && !___entity.isEntityRemote)
// {
// var mapping = MultiActionManager.GetMappingForEntity(___entity.entityId);
// mapping?.SaveMeta();
// }
// return true;
//}
#endregion
#region GameManager.ItemReload*
[HarmonyPatch(typeof(ItemActionRanged), nameof(ItemActionRanged.SetAmmoType))]
[HarmonyTranspiler]
private static IEnumerable<CodeInstruction> Transpiler_SetAmmoType_ItemActionRanged(IEnumerable<CodeInstruction> instructions)
{
var codes = instructions.ToList();
MethodInfo mtd_reloadserver = AccessTools.Method(typeof(GameManager), nameof(GameManager.ItemReloadServer));
for (int i = 0; i < codes.Count; i++)
{
var code = codes[i];
if (code.Calls(mtd_reloadserver))
{
codes.RemoveAt(i);
codes.InsertRange(i, new[]
{
new CodeInstruction(OpCodes.Ldarg_0),
CodeInstruction.LoadField(typeof(ItemAction), nameof(ItemAction.ActionIndex)),
CodeInstruction.Call(typeof(MultiActionUtils), nameof(MultiActionUtils.FixedItemReloadServer))
});
codes.RemoveAt(i - 3);
break;
}
}
return codes;
}
[HarmonyPatch(typeof(ItemActionRanged), nameof(ItemActionRanged.SwapAmmoType))]
[HarmonyTranspiler]
private static IEnumerable<CodeInstruction> Transpiler_SwapAmmoType_ItemActionRanged(IEnumerable<CodeInstruction> instructions)
{
var codes = instructions.ToList();
MethodInfo mtd_reloadserver = AccessTools.Method(typeof(GameManager), nameof(GameManager.ItemReloadServer));
FieldInfo fld_actiondata = AccessTools.Field(typeof(ItemInventoryData), nameof(ItemInventoryData.actionData));
for (int i = 0; i < codes.Count; i++)
{
var code = codes[i];
if (code.Calls(mtd_reloadserver))
{
codes.RemoveAt(i);
codes.InsertRange(i, new[]
{
new CodeInstruction(OpCodes.Ldarg_0),
CodeInstruction.LoadField(typeof(ItemAction), nameof(ItemAction.ActionIndex)),
CodeInstruction.Call(typeof(MultiActionUtils), nameof(MultiActionUtils.FixedItemReloadServer))
});
codes[i - 4].MoveLabelsFrom(codes[i - 5]);
codes.RemoveAt(i - 5);
break;
}
else if (code.LoadsField(fld_actiondata))
{
codes.RemoveAt(i + 1);
codes.InsertRange(i + 1, new[]
{
new CodeInstruction(OpCodes.Ldarg_0),
CodeInstruction.LoadField(typeof(ItemAction), nameof(ItemAction.ActionIndex))
});
i += 1;
}
}
return codes;
}
[HarmonyPatch(typeof(ItemActionLauncher), nameof(ItemActionLauncher.SwapAmmoType))]
[HarmonyTranspiler]
private static IEnumerable<CodeInstruction> Transpiler_SwapAmmoType_ItemActionLauncher(IEnumerable<CodeInstruction> instructions)
{
var codes = instructions.ToList();
FieldInfo fld_actiondata = AccessTools.Field(typeof(ItemInventoryData), nameof(ItemInventoryData.actionData));
for (int i = 0; i < codes.Count; i++)
{
var code = codes[i];
if (code.LoadsField(fld_actiondata))
{
codes.RemoveAt(i + 1);
codes.InsertRange(i + 1, new[]
{
new CodeInstruction(OpCodes.Ldarg_0),
CodeInstruction.LoadField(typeof(ItemAction), nameof(ItemAction.ActionIndex))
});
break;
}
}
return codes;
}
[HarmonyPatch(typeof(ItemActionRanged), nameof(ItemActionRanged.SwapSelectedAmmo))]
[HarmonyTranspiler]
private static IEnumerable<CodeInstruction> Transpiler_SwapSelectedAmmo_ItemActionRanged(IEnumerable<CodeInstruction> instructions)
{
var codes = instructions.ToList();
MethodInfo mtd_reloadserver = AccessTools.Method(typeof(GameManager), nameof(GameManager.ItemReloadServer));
MethodInfo mtd_canreload = AccessTools.Method(typeof(ItemActionAttack), nameof(ItemActionAttack.CanReload));
for (int i = 0; i < codes.Count; i++)
{
var code = codes[i];
if (code.Calls(mtd_reloadserver))
{
codes.RemoveAt(i);
codes.InsertRange(i, new[]
{
new CodeInstruction(OpCodes.Ldarg_0),
CodeInstruction.LoadField(typeof(ItemAction), nameof(ItemAction.ActionIndex)),
CodeInstruction.Call(typeof(MultiActionUtils), nameof(MultiActionUtils.FixedItemReloadServer))
});
codes.RemoveAt(i - 3);
break;
}
else if (code.Calls(mtd_canreload))
{
codes.RemoveAt(i - 2);
codes.InsertRange(i - 2, new[]
{
new CodeInstruction(OpCodes.Ldarg_0),
CodeInstruction.LoadField(typeof(ItemAction), nameof(ItemAction.ActionIndex))
});
codes.RemoveRange(i - 9, 3);
codes.Insert(i - 9, new CodeInstruction(OpCodes.Ldarg_0));
i--;
}
}
return codes;
}
[HarmonyPatch(typeof(ItemActionRanged), nameof(ItemActionRanged.loadNewAmmunition))]
[HarmonyTranspiler]
private static IEnumerable<CodeInstruction> Transpiler_loadNewAmmunition_ItemActionRanged(IEnumerable<CodeInstruction> instructions)
{
var codes = instructions.ToList();
var fld_actiondata = AccessTools.Field(typeof(ItemInventoryData), nameof(ItemInventoryData.actionData));
for (int i = 0; i < codes.Count; i++)
{
if (codes[i].LoadsField(fld_actiondata))
{
codes.RemoveAt(i + 1);
codes.InsertRange(i + 1, new[]
{
new CodeInstruction(OpCodes.Ldarg_3),
CodeInstruction.Call(typeof(MultiActionManager), nameof(MultiActionManager.GetActionIndexForEntity))
});
break;
}
}
return codes;
}
#endregion
//KEEP
#region Launcher logic
#region Launcher projectile meta and action index
[HarmonyPatch(typeof(ItemActionLauncher), nameof(ItemActionLauncher.StartHolding))]
[HarmonyTranspiler]
private static IEnumerable<CodeInstruction> Transpiler_StartHolding_ItemActionLauncher(IEnumerable<CodeInstruction> instructions, ILGenerator generator)
{
var codes = instructions.ToList();
var fld_meta = AccessTools.Field(typeof(ItemValue), nameof(ItemValue.Meta));
var mtd_delete = AccessTools.Method(typeof(ItemActionLauncher), nameof(ItemActionLauncher.DeleteProjectiles));
var prop_itemvalue = AccessTools.PropertyGetter(typeof(ItemInventoryData), nameof(ItemInventoryData.itemValue));
var lbd_meta = generator.DeclareLocal(typeof(int));
for (int i = 0; i < codes.Count; i++)
{
if (codes[i].Calls(mtd_delete))
{
codes.InsertRange(i + 1, new[]
{
new CodeInstruction(OpCodes.Ldarg_1),
CodeInstruction.LoadField(typeof(ItemActionData), nameof(ItemActionData.invData)),
new CodeInstruction(OpCodes.Callvirt, prop_itemvalue),
new CodeInstruction(OpCodes.Ldarg_1),
CodeInstruction.LoadField(typeof(ItemActionData), nameof(ItemActionData.indexInEntityOfAction)),
CodeInstruction.Call(typeof(MultiActionUtils), nameof(MultiActionUtils.GetMetaByActionIndex)),
new CodeInstruction(OpCodes.Stloc_S, lbd_meta)
});
i += 7;
}
else if (codes[i].LoadsField(fld_meta))
{
codes.Insert(i + 1, new CodeInstruction(OpCodes.Ldloc_S, lbd_meta));
codes.RemoveRange(i - 3, 4);
i -= 3;
}
}
return codes;
}
[HarmonyPatch(typeof(ItemActionLauncher), nameof(ItemActionLauncher.instantiateProjectile))]
[HarmonyTranspiler]
private static IEnumerable<CodeInstruction> Transpiler_instantiateProjectile_ItemActionLauncher_MetaIndex(IEnumerable<CodeInstruction> instructions)
{
var codes = instructions.ToList();
var fld_ammoindex = AccessTools.Field(typeof(ItemValue), nameof(ItemValue.SelectedAmmoTypeIndex));
for (int i = 0; i < codes.Count; i++)
{
if (codes[i].LoadsField(fld_ammoindex))
{
codes[i].opcode = OpCodes.Call;
codes[i].operand = AccessTools.Method(typeof(MultiActionUtils), nameof(MultiActionUtils.GetSelectedAmmoIndexByActionIndex));
codes.InsertRange(i, new[]
{
new CodeInstruction(OpCodes.Ldarg_1),
CodeInstruction.LoadField(typeof(ItemActionData), nameof(ItemActionData.indexInEntityOfAction))
});
break;
}
}
return codes;
}
#endregion
[HarmonyPatch(typeof(ItemActionLauncher), nameof(ItemActionLauncher.instantiateProjectile))]
[HarmonyTranspiler]
//use custom script
private static IEnumerable<CodeInstruction> Transpiler_instantiateProjectile_ItemActionLauncher_ReplaceScript(IEnumerable<CodeInstruction> instructions)
{
MethodInfo mtd_addcomponent = AccessTools.Method(typeof(GameObject), nameof(GameObject.AddComponent), Array.Empty<Type>());
MethodInfo mtd_addcomponentprev = mtd_addcomponent.MakeGenericMethod(typeof(ProjectileMoveScript));
MethodInfo mtd_addcomponentnew = mtd_addcomponent.MakeGenericMethod(typeof(CustomProjectileMoveScript));
foreach (var code in instructions)
{
if (code.Calls(mtd_addcomponentprev))
{
Log.Out("replacing launcher projectile script...");
code.operand = mtd_addcomponentnew;
}
yield return code;
}
}
//copy ItemValue to projectile
[HarmonyPatch(typeof(ItemActionLauncher), nameof(ItemActionLauncher.instantiateProjectile))]
[HarmonyPostfix]
private static void Postfix_instantiateProjectile_ItemActionLauncher(Transform __result)
{
var script = __result.GetComponent<ProjectileMoveScript>();
var projectileValue = script.itemValueProjectile;
var launcherValue = script.itemValueLauncher;
projectileValue.Activated = (byte)(Mathf.Clamp01(script.actionData.strainPercent) * byte.MaxValue);
MultiActionUtils.CopyLauncherValueToProjectile(launcherValue, projectileValue, script.actionData.indexInEntityOfAction);
}
//
[HarmonyPatch(typeof(ProjectileMoveScript), nameof(ProjectileMoveScript.Fire))]
[HarmonyPrefix]
private static bool Prefix_Fire_ProjectileMoveScript(ProjectileMoveScript __instance, Vector3 _idealStartPosition, Vector3 _flyDirection, Entity _firingEntity, int _hmOverride, float _radius)
{
if (_firingEntity is EntityAlive entityAlive)
entityAlive.MinEventContext.ItemActionData = __instance.actionData;
if (__instance is CustomProjectileMoveScript)
{
__instance.ProjectileFire(_idealStartPosition, _flyDirection, _firingEntity, _hmOverride, _radius);
return false;
}
return true;
}
#endregion
#endregion
#region FireEvent patches, set params
//KEEP
#region Ranged ExecuteAction FireEvent params
[HarmonyPatch(typeof(ItemClass), nameof(ItemClass.ExecuteAction))]
[HarmonyPrefix]
private static bool Prefix_ExecuteAction_ItemClass(ref int _actionIdx, ItemInventoryData _data, PlayerActionsLocal _playerActions)
{
if (_actionIdx != 0 || _playerActions == null || !(_data.holdingEntity is EntityPlayerLocal player))
{
return true;
}
_actionIdx = MultiActionManager.GetActionIndexForEntity(player);
player.MinEventContext.ItemActionData = _data.actionData[_actionIdx];
return true;
}
//why? ask TFP the fuck they are doing
[HarmonyPatch(typeof(ItemActionRanged), nameof(ItemActionRanged.ExecuteAction))]
[HarmonyTranspiler]
private static IEnumerable<CodeInstruction> Transpiler_ExecuteAction_ItemActionRanged(IEnumerable<CodeInstruction> instructions, ILGenerator generator)
{
var codes = instructions.ToList();
FieldInfo fld_itemactiondata = AccessTools.Field(typeof(MinEventParams), nameof(MinEventParams.ItemActionData));
MethodInfo mtd_reloadserver = AccessTools.Method(typeof(IGameManager), nameof(IGameManager.ItemReloadServer));
FieldInfo fld_gamemanager = AccessTools.Field(typeof(ItemInventoryData), nameof(ItemInventoryData.gameManager));
MethodInfo mtd_getkickback = AccessTools.Method(typeof(ItemActionAttack), nameof(ItemActionAttack.GetKickbackForce));
MethodInfo mtd_getmaxammo = AccessTools.Method(typeof(ItemActionRanged), nameof(ItemActionRanged.GetMaxAmmoCount));
for (int i = 0; i < codes.Count; i++)
{
var code = codes[i];
if (code.StoresField(fld_itemactiondata))
{
codes.Insert(i, new CodeInstruction(OpCodes.Ldarg_1));
codes.RemoveRange(i - 5, 5);
i -= 4;
}
else if (code.Calls(mtd_reloadserver))
{
int j = i;
while (!codes[j].LoadsField(fld_gamemanager))
{
j--;
}
codes.RemoveAt(i);
codes.InsertRange(i, new[]
{
new CodeInstruction(OpCodes.Ldloc_0),
CodeInstruction.LoadField(typeof(ItemActionData), nameof(ItemActionData.indexInEntityOfAction)),
CodeInstruction.Call(typeof(MultiActionUtils), nameof(MultiActionUtils.FixedItemReloadServer))
});
codes.RemoveRange(j - 2, 3);
i--;
}
//else if (code.Calls(mtd_getmaxammo))
//{
// int j = i + 1;
// for (; j < codes.Count; j++)
// {
// if (codes[j].Calls(mtd_getkickback))
// {
// break;
// }
// }
// if (j < codes.Count)
// {
// var jumpto = codes[j - 2];
// var label = generator.DefineLabel();
// jumpto.labels.Add(label);
// codes.Insert(i - 2, new CodeInstruction(OpCodes.Br_S, label));
// codes[i - 2].MoveLabelsFrom(codes[i - 1]);
// i++;
// }
//}
}
return codes;
}
#endregion
#region ItemAction.CancelAction
[HarmonyPatch(typeof(ItemActionCatapult), nameof(ItemActionCatapult.CancelAction))]
[HarmonyPrefix]
private static bool Prefix_CancelAction_ItemActionCatapult(ItemActionData _actionData)
{
_actionData.invData.holdingEntity.MinEventContext.ItemActionData = _actionData;
return true;
}
#endregion
#region ItemActionDynamicMelee.Raycast
[HarmonyPatch(typeof(ItemActionDynamicMelee), nameof(ItemActionDynamicMelee.Raycast))]
[HarmonyPrefix]
private static bool Prefix_Raycast_ItemActionDynamicMelee(ItemActionDynamic.ItemActionDynamicData _actionData)
{
_actionData.invData.holdingEntity.MinEventContext.ItemActionData = _actionData;
return true;
}
#endregion
#region Inventory.FireEvent, set current action
[HarmonyPatch(typeof(Inventory), nameof(Inventory.FireEvent))]
[HarmonyPrefix]
private static bool Prefix_FireEvent_Inventory(Inventory __instance)
{
MultiActionUtils.SetMinEventParamsByEntityInventory(__instance.entity);
return true;
}
#endregion
#region Inventory.syncHeldItem, set current action
[HarmonyPatch(typeof(Inventory), nameof(Inventory.syncHeldItem))]
[HarmonyTranspiler]
private static IEnumerable<CodeInstruction> Transpiler_syncHeldItem_Inventory(IEnumerable<CodeInstruction> instructions)
{
var codes = instructions.ToList();
var mtd_test = AccessTools.Method(typeof(FastTags<TagGroup.Global>), nameof(FastTags<TagGroup.Global>.Test_AnySet));
var fld_itemvalue = AccessTools.Field(typeof(MinEventParams), nameof(MinEventParams.ItemValue));
for (int i = 0; i < codes.Count; i++)
{
if (codes[i].StoresField(fld_itemvalue) && codes[i - 7].Calls(mtd_test))
{
codes.InsertRange(i + 1, new[]
{
new CodeInstruction(OpCodes.Ldarg_0),
CodeInstruction.LoadField(typeof(Inventory), nameof(Inventory.entity)),
CodeInstruction.Call(typeof(MultiActionUtils), nameof(MultiActionUtils.SetMinEventParamsByEntityInventory))
});
break;
}
}
return codes;
}
#endregion
#region ItemValue.FireEvent, read current action
[HarmonyPatch(typeof(ItemValue), nameof(ItemValue.FireEvent))]
[HarmonyTranspiler]
private static IEnumerable<CodeInstruction> Transpiler_FireEvent_ItemValue(IEnumerable<CodeInstruction> instructions, ILGenerator generator)
{
var codes = instructions.ToList();
LocalBuilder lbd_index = generator.DeclareLocal(typeof(int));
FieldInfo fld_action = AccessTools.Field(typeof(ItemClass), nameof(ItemClass.Actions));
FieldInfo fld_ammoindex = AccessTools.Field(typeof(ItemValue), nameof(ItemValue.SelectedAmmoTypeIndex));
MethodInfo mtd_fireevent = AccessTools.Method(typeof(ItemValue), nameof(ItemValue.FireEvent));
for (int i = 0; i < codes.Count; i++)
{
var code = codes[i];
if (code.LoadsField(fld_action) && codes[i + 1].opcode == OpCodes.Ldc_I4_0)
{
codes[i + 1].opcode = OpCodes.Ldloc_S;
codes[i + 1].operand = lbd_index;
if (codes[i - 9].opcode == OpCodes.Ret)
{
codes.InsertRange(i - 1, new[]
{
new CodeInstruction(OpCodes.Ldarg_2),
CodeInstruction.Call(typeof(MultiActionUtils), nameof(MultiActionUtils.GetActionIndexByEventParams)),
new CodeInstruction(OpCodes.Stloc_S, lbd_index)
});
i += 3;
}
}
else if (code.LoadsField(fld_ammoindex))
{
code.opcode = OpCodes.Call;
code.operand = AccessTools.Method(typeof(MultiActionUtils), nameof(MultiActionUtils.GetSelectedAmmoIndexByActionIndex));
codes.Insert(i, new CodeInstruction(OpCodes.Ldloc_S, lbd_index));
i++;
}
//action exclude mods
else if (code.Calls(mtd_fireevent) && codes[i + 1].opcode != OpCodes.Ldloc_0)
{
for (int j = i; j >= 0; j--)
{
if (codes[j].opcode == OpCodes.Brfalse_S || codes[j].opcode == OpCodes.Brfalse)
{
var label = codes[j].operand;
codes.InsertRange(j + 1, new[]
{
new CodeInstruction(OpCodes.Ldarg_0),
CodeInstruction.LoadField(typeof(ItemValue), nameof(ItemValue.type)),
new CodeInstruction(OpCodes.Ldarg_0),
new CodeInstruction(OpCodes.Ldfld, codes[j + 2].operand),
new CodeInstruction(codes[j + 3].opcode, codes[j + 3].operand),
new CodeInstruction(OpCodes.Ldelem_Ref),
CodeInstruction.LoadField(typeof(ItemValue), nameof(ItemValue.type)),
new CodeInstruction(OpCodes.Ldloc_S, lbd_index),
CodeInstruction.Call(typeof(MultiActionManager), nameof(MultiActionManager.ShouldExcludeTrigger)),
new CodeInstruction(OpCodes.Brtrue_S, label)
});
i += 10;
break;
}
}
}
}
return codes;
}
#endregion
//onSelfEquipStop, onSelfHoldingItemCreated, onSelfEquipStart are not available for individual action,
//some are already set in update or execute action
#endregion
#region EffectManager.GetValue patches, set params
//set correct action index for ItemValue
[HarmonyPatch(typeof(ItemValue), nameof(ItemValue.ModifyValue))]
[HarmonyTranspiler]
private static IEnumerable<CodeInstruction> Transpiler_ModifyValue_ItemValue(IEnumerable<CodeInstruction> instructions, ILGenerator generator)
{
var codes = instructions.ToList();
LocalBuilder lbd_index = generator.DeclareLocal(typeof(int));
FieldInfo fld_action = AccessTools.Field(typeof(ItemClass), nameof(ItemClass.Actions));
FieldInfo fld_ammoindex = AccessTools.Field(typeof(ItemValue), nameof(ItemValue.SelectedAmmoTypeIndex));
FieldInfo fld_mods = AccessTools.Field(typeof(ItemValue), nameof(ItemValue.Modifications));
FieldInfo fld_cos = AccessTools.Field(typeof(ItemValue), nameof(ItemValue.CosmeticMods));
MethodInfo mtd_modify = AccessTools.Method(typeof(ItemValue), nameof(ItemValue.ModifyValue));
for (int i = 0; i < codes.Count; i++)
{
var code = codes[i];
if (code.opcode == OpCodes.Stloc_1)
{
codes.InsertRange(i + 1, new[]
{
new CodeInstruction(OpCodes.Ldarg_1),
CodeInstruction.Call(typeof(MultiActionUtils), nameof(MultiActionUtils.GetActionIndexByEntityEventParams)),
new CodeInstruction(OpCodes.Stloc_S, lbd_index)
});
}
else if (code.LoadsField(fld_action) && codes[i + 1].opcode == OpCodes.Ldc_I4_0)
{
codes[i + 1].opcode = OpCodes.Ldloc_S;
codes[i + 1].operand = lbd_index;
}
else if (code.LoadsField(fld_ammoindex))
{
code.opcode = OpCodes.Call;
code.operand = AccessTools.Method(typeof(MultiActionUtils), nameof(MultiActionUtils.GetSelectedAmmoIndexByActionIndex));
codes.Insert(i, new CodeInstruction(OpCodes.Ldloc_S, lbd_index));
i++;
}
else if (code.Calls(mtd_modify))
{
for (int j = i; j >= 0; j--)
{
if (codes[j].opcode == OpCodes.Brfalse_S || codes[j].opcode == OpCodes.Brfalse)
{
var label = codes[j].operand;
codes.InsertRange(j + 1, new[]
{
new CodeInstruction(OpCodes.Ldarg_0),
CodeInstruction.LoadField(typeof(ItemValue), nameof(ItemValue.type)),
new CodeInstruction(OpCodes.Ldarg_0),
new CodeInstruction(OpCodes.Ldfld, codes[j + 2].operand),
new CodeInstruction(codes[j + 3].opcode, codes[j + 3].operand),
new CodeInstruction(OpCodes.Ldelem_Ref),
CodeInstruction.LoadField(typeof(ItemValue), nameof(ItemValue.type)),
new CodeInstruction(OpCodes.Ldloc_S, lbd_index),
CodeInstruction.Call(typeof(MultiActionManager), nameof(MultiActionManager.ShouldExcludePassive)),
new CodeInstruction(OpCodes.Brtrue_S, label)
});
i += 10;
break;
}
}
}
}
return codes;
}
//only current mode should execute OnHUD
[HarmonyPatch(typeof(EntityPlayerLocal), nameof(EntityPlayerLocal.OnHUD))]
[HarmonyPrefix]
private static bool Prefix_OnHUD_EntityPlayerLocal(EntityPlayerLocal __instance, out ItemActionData __state)
{
__state = __instance.MinEventContext.ItemActionData;
MultiActionUtils.SetMinEventParamsByEntityInventory(__instance);
return true;
}
[HarmonyPatch(typeof(EntityPlayerLocal), nameof(EntityPlayerLocal.OnHUD))]
[HarmonyPostfix]
private static void Postfix_OnHUD_EntityPlayerLocal(EntityPlayerLocal __instance, ItemActionData __state)
{
__instance.MinEventContext.ItemActionData = __state;
}
//for passive value calc
[HarmonyPatch(typeof(EntityPlayerLocal), nameof(EntityPlayerLocal.guiDrawCrosshair))]
[HarmonyPrefix]
private static bool Prefix_guiDrawCrosshair_EntityPlayerLocal(EntityPlayerLocal __instance)
{
MultiActionUtils.SetMinEventParamsByEntityInventory(__instance);
return true;
}
//draw crosshair for current action
[HarmonyPatch(typeof(EntityPlayerLocal), nameof(EntityPlayerLocal.guiDrawCrosshair))]
[HarmonyTranspiler]
private static IEnumerable<CodeInstruction> Transpiler_guiDrawCrosshair_EntityPlayerLocal(IEnumerable<CodeInstruction> instructions, ILGenerator generator)
{
var codes = instructions.ToList();
LocalBuilder lbd_index = generator.DeclareLocal(typeof(int));
FieldInfo fld_actiondata = AccessTools.Field(typeof(ItemInventoryData), nameof(ItemInventoryData.actionData));
for (int i = 0; i < codes.Count; i++)
{
if (codes[i].opcode == OpCodes.Stloc_1)
{
codes.InsertRange(i + 1, new[]
{
new CodeInstruction(OpCodes.Ldarg_0),
CodeInstruction.Call(typeof(MultiActionManager), nameof(MultiActionManager.GetActionIndexForEntity)),
new CodeInstruction(OpCodes.Stloc_S, lbd_index)
});
i += 3;
}
else if (codes[i].LoadsField(fld_actiondata) && codes[i + 1].opcode == OpCodes.Ldc_I4_0)
{
codes[i + 1].opcode = OpCodes.Ldloc_S;
codes[i + 1].operand = lbd_index;
}
}
return codes;
}
[HarmonyPatch(typeof(ItemAction), nameof(ItemAction.ExecuteBuffActions))]
[HarmonyPrefix]
private static bool Prefix_ExecuteBuffActions_ItemAction(int instigatorId, out (EntityAlive entity, ItemActionData actionData) __state)
{
__state = default;
EntityAlive entity = GameManager.Instance.World.GetEntity(instigatorId) as EntityAlive;
if (entity != null)
{
__state.entity = entity;
__state.actionData = entity.MinEventContext.ItemActionData;
MultiActionUtils.SetMinEventParamsByEntityInventory(entity);
}
else
return false;
return true;
}
[HarmonyPatch(typeof(ItemAction), nameof(ItemAction.ExecuteBuffActions))]
[HarmonyPostfix]
private static void Postfix_ExecuteBuffActions_ItemAction((EntityAlive entity, ItemActionData actionData) __state, bool __runOriginal)
{
if (__runOriginal && __state.entity != null)
__state.entity.MinEventContext.ItemActionData = __state.actionData;
}
//ItemAction.GetDismemberChance already set
//ItemActionDynamic.GetExecuteActionTarget not needed
[HarmonyPatch(typeof(ItemActionRanged), nameof(ItemActionRanged.ItemActionEffects))]
[HarmonyPrefix]
private static bool Prefix_ItemActionEffects_ItemActionLauncher(ItemActionData _actionData, out ItemActionData __state)
{
__state = _actionData.invData.holdingEntity.MinEventContext.ItemActionData;
_actionData.invData.holdingEntity.MinEventContext.ItemActionData = _actionData;
return true;
}
[HarmonyPatch(typeof(ItemActionRanged), nameof(ItemActionRanged.ItemActionEffects))]
[HarmonyPostfix]
private static void Postfix_ItemActionEffects_ItemActionLauncher(ItemActionData _actionData, ItemActionData __state)
{
_actionData.invData.holdingEntity.MinEventContext.ItemActionData = __state;
}
[HarmonyPatch(typeof(ItemActionLauncher), nameof(ItemActionLauncher.ClampAmmoCount))]
[HarmonyPrefix]
private static bool Prefix_ClampAmmoCount_ItemActionLauncher(ItemActionLauncher.ItemActionDataLauncher actionData, out ItemActionData __state)
{
__state = actionData.invData.holdingEntity.MinEventContext.ItemActionData;
actionData.invData.holdingEntity.MinEventContext.ItemActionData = actionData;
return true;
}
[HarmonyPatch(typeof(ItemActionLauncher), nameof(ItemActionLauncher.ClampAmmoCount))]
[HarmonyPostfix]
private static void Postfix_ClampAmmoCount_ItemActionLauncher(ItemActionLauncher.ItemActionDataLauncher actionData, ItemActionData __state)
{
actionData.invData.holdingEntity.MinEventContext.ItemActionData = __state;
}
#endregion
#region EffectManager.GetValuesAndSources patches, set params
[HarmonyPatch(typeof(EntityStats), nameof(EntityStats.Update))]
[HarmonyPrefix]
private static bool Prefix_Update_EntityStats(EntityStats __instance)
{
MultiActionUtils.SetMinEventParamsByEntityInventory(__instance.m_entity);
return true;
}
//set correct action index for ItemValue
[HarmonyPatch(typeof(ItemValue), nameof(ItemValue.GetModifiedValueData))]
[HarmonyTranspiler]
private static IEnumerable<CodeInstruction> Transpiler_GetModifiedValueData_ItemValue(IEnumerable<CodeInstruction> instructions, ILGenerator generator)
{
var codes = instructions.ToList();
LocalBuilder lbd_index = generator.DeclareLocal(typeof(int));
FieldInfo fld_action = AccessTools.Field(typeof(ItemClass), nameof(ItemClass.Actions));
FieldInfo fld_ammoindex = AccessTools.Field(typeof(ItemValue), nameof(ItemValue.SelectedAmmoTypeIndex));
FieldInfo fld_mods = AccessTools.Field(typeof(ItemValue), nameof(ItemValue.Modifications));
FieldInfo fld_cos = AccessTools.Field(typeof(ItemValue), nameof(ItemValue.CosmeticMods));
MethodInfo mtd_getvalue = AccessTools.Method(typeof(ItemValue), nameof(ItemValue.GetModifiedValueData));
for (int i = 0; i < codes.Count; i++)
{
var code = codes[i];
if (code.opcode == OpCodes.Stloc_0)
{
codes.InsertRange(i + 1, new[]
{
new CodeInstruction(OpCodes.Ldarg_3),
CodeInstruction.Call(typeof(MultiActionUtils), nameof(MultiActionUtils.GetActionIndexByEntityEventParams)),
new CodeInstruction(OpCodes.Stloc_S, lbd_index)
});
}
else if (code.LoadsField(fld_action) && codes[i + 1].opcode == OpCodes.Ldc_I4_0)
{
codes[i + 1].opcode = OpCodes.Ldloc_S;
codes[i + 1].operand = lbd_index;
}
else if (code.LoadsField(fld_ammoindex))
{
code.opcode = OpCodes.Call;
code.operand = AccessTools.Method(typeof(MultiActionUtils), nameof(MultiActionUtils.GetSelectedAmmoIndexByActionIndex));
codes.Insert(i, new CodeInstruction(OpCodes.Ldloc_S, lbd_index));
i++;
}
else if (code.Calls(mtd_getvalue) && codes[i + 1].opcode != OpCodes.Ldloc_0)
{
for (int j = i; j >= 0; j--)
{
if (codes[j].opcode == OpCodes.Brfalse_S || codes[j].opcode == OpCodes.Brfalse)
{
var label = codes[j].operand;
codes.InsertRange(j + 1, new[]
{
new CodeInstruction(OpCodes.Ldarg_0),
CodeInstruction.LoadField(typeof(ItemValue), nameof(ItemValue.type)),
new CodeInstruction(OpCodes.Ldarg_0),
new CodeInstruction(OpCodes.Ldfld, codes[j + 2].operand),
new CodeInstruction(codes[j + 3].opcode, codes[j + 3].operand),
new CodeInstruction(OpCodes.Ldelem_Ref),
CodeInstruction.LoadField(typeof(ItemValue), nameof(ItemValue.type)),
new CodeInstruction(OpCodes.Ldloc_S, lbd_index),
CodeInstruction.Call(typeof(MultiActionManager), nameof(MultiActionManager.ShouldExcludePassive)),
new CodeInstruction(OpCodes.Brtrue_S, label)
});
i += 10;
break;
}
}
}
}
return codes;
}
#endregion
//KEEP
#region Misc
//load correct property for melee
[HarmonyPatch(typeof(AnimatorMeleeAttackState), nameof(AnimatorMeleeAttackState.OnStateEnter))]
[HarmonyTranspiler]
private static IEnumerable<CodeInstruction> Transpiler_OnStateEnter_AnimatorMeleeAttackState(IEnumerable<CodeInstruction> instructions)
{
var codes = instructions.ToList();
var fld_actionindex = AccessTools.Field(typeof(AnimatorMeleeAttackState), nameof(AnimatorMeleeAttackState.actionIndex));
MethodInfo mtd_getvalue = AccessTools.Method(typeof(EffectManager), nameof(EffectManager.GetValue));
for (int i = 0; i < codes.Count; i++)
{
var code = codes[i];
if (code.LoadsField(fld_actionindex) && codes[i + 2].opcode == OpCodes.Ldstr)
{
string property = codes[i + 2].operand.ToString();
property = property.Split('.')[1];
codes.RemoveRange(i + 1, 4);
codes.InsertRange(i + 1, new[]
{
new CodeInstruction(OpCodes.Ldstr, property),
CodeInstruction.Call(typeof(MultiActionUtils), nameof(MultiActionUtils.GetPropertyName))
});
i -= 2;
}
else if (code.Calls(mtd_getvalue))
{
codes.InsertRange(i, new[]
{
new CodeInstruction(OpCodes.Ldarg_0),
CodeInstruction.LoadField(typeof(AnimatorMeleeAttackState), "entity"),
new CodeInstruction(OpCodes.Ldarg_0),
CodeInstruction.LoadField(typeof(AnimatorMeleeAttackState), "actionIndex"),
CodeInstruction.Call(typeof(MultiActionUtils), nameof(MultiActionUtils.SetMinEventParamsActionData))
});
i += 5;
}
}
return codes;
}
//make sure it's set to current action after for loop
[HarmonyPatch(typeof(ItemClass), nameof(ItemClass.OnHoldingUpdate))]
[HarmonyPostfix]
private static void Postfix_OnHoldingUpdate_ItemClass(ItemInventoryData _data)
{
MultiActionUtils.SetMinEventParamsByEntityInventory(_data.holdingEntity);
}
[HarmonyPatch(typeof(ItemClass), nameof(ItemClass.StopHolding))]
[HarmonyPostfix]
private static void Postfix_StopHolding_ItemClass(ItemInventoryData _data)
{
if (_data.holdingEntity != null)
{
MultiActionUtils.SetMinEventParamsByEntityInventory(_data.holdingEntity);
MultiActionManager.SetMappingForEntity(_data.holdingEntity.entityId, null);
}
}
[HarmonyPatch(typeof(ItemClass), nameof(ItemClass.StartHolding))]
[HarmonyPostfix]
private static void Postfix_StartHolding_ItemClass(ItemInventoryData _data)
{
MultiActionUtils.SetMinEventParamsByEntityInventory(_data.holdingEntity);
}
//should be fixed in Harmony 2.12.0.0
//[HarmonyPatch(typeof(ItemClassesFromXml), nameof(ItemClassesFromXml.parseItem))]
//[HarmonyPrefix]
//private static bool Prefix_parseItem_ItemClassesFromXml(XElement _node)
//{
// DynamicProperties dynamicProperties = new DynamicProperties();
// string attribute = _node.GetAttribute("name");
// if (attribute.Length == 0)
// {
// throw new Exception("Attribute 'name' missing on item");
// }
// //here
// List<IRequirement>[] array = new List<IRequirement>[ItemClass.cMaxActionNames];
// for (int i = 0; i < array.Length; i++)
// {
// array[i] = new List<IRequirement>();
// }
// foreach (XElement item in _node.Elements("property"))
// {
// dynamicProperties.Add(item);
// string attribute2 = item.GetAttribute("class");
// if (attribute2.StartsWith("Action"))
// {
// int num = attribute2[attribute2.Length - 1] - '0';
// array[num].AddRange(RequirementBase.ParseRequirements(item));
// }
// }
// if (dynamicProperties.Values.ContainsKey("Extends"))
// {
// string text = dynamicProperties.Values["Extends"];
// ItemClass itemClass = ItemClass.GetItemClass(text);
// if (itemClass == null)
// {
// throw new Exception($"Extends item {text} is not specified for item {attribute}'");
// }
// HashSet<string> hashSet = new HashSet<string> { Block.PropCreativeMode };
// if (dynamicProperties.Params1.ContainsKey("Extends"))
// {
// string[] array2 = dynamicProperties.Params1["Extends"].Split(new[] { ',' }, StringSplitOptions.None);
// foreach (string text2 in array2)
// {
// hashSet.Add(text2.Trim());
// }
// }
// DynamicProperties dynamicProperties2 = new DynamicProperties();
// dynamicProperties2.CopyFrom(itemClass.Properties, hashSet);
// dynamicProperties2.CopyFrom(dynamicProperties);
// dynamicProperties = dynamicProperties2;
// }
// ItemClass itemClass2;
// if (dynamicProperties.Values.ContainsKey("Class"))
// {
// string text3 = dynamicProperties.Values["Class"];
// if (!text3.Contains(","))
// {
// text3 += ",Assembly-CSharp";
// }
// try
// {
// itemClass2 = (ItemClass)Activator.CreateInstance(Type.GetType(text3));
// }
// catch (Exception)
// {
// throw new Exception("No item class '" + text3 + " found!");
// }
// }
// else
// {
// itemClass2 = new ItemClass();
// }
// itemClass2.Properties = dynamicProperties;
// if (dynamicProperties.Params1.ContainsKey("Extends"))
// {
// string text4 = dynamicProperties.Values["Extends"];
// if (ItemClass.GetItemClass(text4) == null)
// {
// throw new Exception($"Extends item {text4} is not specified for item {attribute}'");
// }
// }
// itemClass2.Effects = MinEffectController.ParseXml(_node, null, MinEffectController.SourceParentType.ItemClass, itemClass2.Id);
// itemClass2.SetName(attribute);
// itemClass2.setLocalizedItemName(Localization.Get(attribute));
// if (dynamicProperties.Values.ContainsKey("Stacknumber"))
// {
// itemClass2.Stacknumber = new DataItem<int>(int.Parse(dynamicProperties.Values["Stacknumber"]));
// }
// else
// {
// itemClass2.Stacknumber = new DataItem<int>(500);
// }
// if (dynamicProperties.Values.ContainsKey("Canhold"))
// {
// itemClass2.SetCanHold(StringParsers.ParseBool(dynamicProperties.Values["Canhold"]));
// }
// if (dynamicProperties.Values.ContainsKey("Candrop"))
// {
// itemClass2.SetCanDrop(StringParsers.ParseBool(dynamicProperties.Values["Candrop"]));
// }
// if (!dynamicProperties.Values.ContainsKey("Material"))
// {
// throw new Exception("Attribute 'material' missing on item '" + attribute + "'");
// }
// itemClass2.MadeOfMaterial = MaterialBlock.fromString(dynamicProperties.Values["Material"]);
// if (itemClass2.MadeOfMaterial == null)
// {
// throw new Exception("Attribute 'material' '" + dynamicProperties.Values["Material"] + "' refers to not existing material in item '" + attribute + "'");
// }
// if (!dynamicProperties.Values.ContainsKey("Meshfile") && itemClass2.CanHold())
// {
// throw new Exception("Attribute 'Meshfile' missing on item '" + attribute + "'");
// }
// itemClass2.MeshFile = dynamicProperties.Values["Meshfile"];
// DataLoader.PreloadBundle(itemClass2.MeshFile);
// StringParsers.TryParseFloat(dynamicProperties.Values["StickyOffset"], out itemClass2.StickyOffset);
// StringParsers.TryParseFloat(dynamicProperties.Values["StickyColliderRadius"], out itemClass2.StickyColliderRadius);
// StringParsers.TryParseSInt32(dynamicProperties.Values["StickyColliderUp"], out itemClass2.StickyColliderUp);
// StringParsers.TryParseFloat(dynamicProperties.Values["StickyColliderLength"], out itemClass2.StickyColliderLength);
// itemClass2.StickyMaterial = dynamicProperties.Values["StickyMaterial"];
// if (dynamicProperties.Values.ContainsKey("ImageEffectOnActive"))
// {
// itemClass2.ImageEffectOnActive = new DataItem<string>(dynamicProperties.Values["ImageEffectOnActive"]);
// }
// if (dynamicProperties.Values.ContainsKey("Active"))
// {
// itemClass2.Active = new DataItem<bool>(_startValue: false);
// }
// if (dynamicProperties.Values.ContainsKey(ItemClass.PropIsSticky))
// {
// itemClass2.IsSticky = StringParsers.ParseBool(dynamicProperties.Values[ItemClass.PropIsSticky]);
// }
// if (dynamicProperties.Values.ContainsKey("DropMeshfile") && itemClass2.CanHold())
// {
// itemClass2.DropMeshFile = dynamicProperties.Values["DropMeshfile"];
// DataLoader.PreloadBundle(itemClass2.DropMeshFile);
// }
// if (dynamicProperties.Values.ContainsKey("HandMeshfile") && itemClass2.CanHold())
// {
// itemClass2.HandMeshFile = dynamicProperties.Values["HandMeshfile"];
// DataLoader.PreloadBundle(itemClass2.HandMeshFile);
// }
// if (dynamicProperties.Values.ContainsKey("HoldType"))
// {
// string s = dynamicProperties.Values["HoldType"];
// int result = 0;
// if (!int.TryParse(s, out result))
// {
// throw new Exception("Cannot parse attribute hold_type for item '" + attribute + "'");
// }
// itemClass2.HoldType = new DataItem<int>(result);
// }
// if (dynamicProperties.Values.ContainsKey("RepairTools"))
// {
// string[] array3 = dynamicProperties.Values["RepairTools"].Replace(" ", "").Split(new[] { ',' }, StringSplitOptions.None);
// DataItem<string>[] array4 = new DataItem<string>[array3.Length];
// for (int k = 0; k < array3.Length; k++)
// {
// array4[k] = new DataItem<string>(array3[k]);
// }
// itemClass2.RepairTools = new ItemData.DataItemArrayRepairTools(array4);
// }
// if (dynamicProperties.Values.ContainsKey("RepairAmount"))
// {
// int result2 = 0;
// int.TryParse(dynamicProperties.Values["RepairAmount"], out result2);
// itemClass2.RepairAmount = new DataItem<int>(result2);
// }
// if (dynamicProperties.Values.ContainsKey("RepairTime"))
// {
// float _result = 0f;
// StringParsers.TryParseFloat(dynamicProperties.Values["RepairTime"], out _result);
// itemClass2.RepairTime = new DataItem<float>(_result);
// }
// else if (itemClass2.RepairAmount != null)
// {
// itemClass2.RepairTime = new DataItem<float>(1f);
// }
// if (dynamicProperties.Values.ContainsKey("Degradation"))
// {
// itemClass2.MaxUseTimes = new DataItem<int>(int.Parse(dynamicProperties.Values["Degradation"]));
// }
// else
// {
// itemClass2.MaxUseTimes = new DataItem<int>(0);
// itemClass2.MaxUseTimesBreaksAfter = new DataItem<bool>(_startValue: false);
// }
// if (dynamicProperties.Values.ContainsKey("DegradationBreaksAfter"))
// {
// itemClass2.MaxUseTimesBreaksAfter = new DataItem<bool>(StringParsers.ParseBool(dynamicProperties.Values["DegradationBreaksAfter"]));
// }
// else if (dynamicProperties.Values.ContainsKey("Degradation"))
// {
// itemClass2.MaxUseTimesBreaksAfter = new DataItem<bool>(_startValue: true);
// }
// if (dynamicProperties.Values.ContainsKey("EconomicValue"))
// {
// itemClass2.EconomicValue = StringParsers.ParseFloat(dynamicProperties.Values["EconomicValue"]);
// }
// if (dynamicProperties.Classes.ContainsKey("Preview"))
// {
// DynamicProperties dynamicProperties3 = dynamicProperties.Classes["Preview"];
// itemClass2.Preview = new PreviewData();
// if (dynamicProperties3.Values.ContainsKey("Zoom"))
// {
// itemClass2.Preview.Zoom = new DataItem<int>(int.Parse(dynamicProperties3.Values["Zoom"]));
// }
// if (dynamicProperties3.Values.ContainsKey("Pos"))
// {
// itemClass2.Preview.Pos = new DataItem<Vector2>(StringParsers.ParseVector2(dynamicProperties3.Values["Pos"]));
// }
// else
// {
// itemClass2.Preview.Pos = new DataItem<Vector2>(Vector2.zero);
// }
// if (dynamicProperties3.Values.ContainsKey("Rot"))
// {
// itemClass2.Preview.Rot = new DataItem<Vector3>(StringParsers.ParseVector3(dynamicProperties3.Values["Rot"]));
// }
// else
// {
// itemClass2.Preview.Rot = new DataItem<Vector3>(Vector3.zero);
// }
// }
// for (int l = 0; l < itemClass2.Actions.Length; l++)
// {
// string text5 = ItemClass.itemActionNames[l];
// if (dynamicProperties.Classes.ContainsKey(text5))
// {
// if (!dynamicProperties.Values.ContainsKey(text5 + ".Class"))
// {
// throw new Exception("No class attribute found on " + text5 + " in item with '" + attribute + "'");
// }
// string text6 = dynamicProperties.Values[text5 + ".Class"];
// ItemAction itemAction;
// try
// {
// itemAction = (ItemAction)Activator.CreateInstance(ReflectionHelpers.GetTypeWithPrefix("ItemAction", text6));
// }
// catch (Exception)
// {
// throw new Exception("ItemAction class '" + text6 + " could not be instantiated");
// }
// itemAction.item = itemClass2;
// itemAction.ActionIndex = l;
// itemAction.ReadFrom(dynamicProperties.Classes[text5]);
// if (array[l].Count > 0)
// {
// itemAction.ExecutionRequirements = array[l];
// }
// itemClass2.Actions[l] = itemAction;
// }
// }
// itemClass2.Init();
// return false;
//}
/// <summary>
/// fix requirement array count
/// </summary>
/// <param name="instructions"></param>
/// <returns></returns>
[HarmonyPatch(typeof(ItemClassesFromXml), nameof(ItemClassesFromXml.parseItem))]
[HarmonyTranspiler]
private static IEnumerable<CodeInstruction> Transpiler_parseItem_ItemClassesFromXml(IEnumerable<CodeInstruction> instructions)
{
bool isLastInsThrow = false;
foreach (var code in instructions)
{
if (isLastInsThrow)
{
isLastInsThrow = false;
if (code.opcode == OpCodes.Ldc_I4_3)
{
code.opcode = OpCodes.Ldc_I4_5;
}
}
if (code.opcode == OpCodes.Throw)
{
isLastInsThrow = true;
}
yield return code;
}
}
[HarmonyPatch(typeof(ItemModificationsFromXml), nameof(ItemModificationsFromXml.parseItem))]
[HarmonyTranspiler]
private static IEnumerable<CodeInstruction> Transpiler_parseItem_ItemModificationsFromXml(IEnumerable<CodeInstruction> instructions)
{
var codes = instructions.ToList();
for (int i = 0; i < codes.Count - 1; i++)
{
if (codes[i].opcode == OpCodes.Ldc_I4_3 && codes[i + 1].opcode == OpCodes.Newarr)
{
codes[i].opcode = OpCodes.Ldc_I4_5;
break;
}
}
return codes;
}
/*
[HarmonyPatch(typeof(ItemClassesFromXml), nameof(ItemClassesFromXml.parseItem))]
[HarmonyPrefix]
private static bool Prefix_parseItem_ItemClassesFromXml(XElement _node)
{
//throw new Exception("Exception thrown from here!");
DynamicProperties dynamicProperties = new DynamicProperties();
string attribute = _node.GetAttribute("name");
if (attribute.Length == 0)
{
throw new Exception("Attribute 'name' missing on item");
}
//Log.Out($"Parsing item {attribute}...");
List<IRequirement>[] array = new List<IRequirement>[5];
for (int i = 0; i < array.Length; i++)
{
array[i] = new List<IRequirement>();
}
foreach (XElement item in _node.Elements("property"))
{
dynamicProperties.Add(item);
string attribute2 = item.GetAttribute("class");
if (attribute2.StartsWith("Action"))
{
int num = attribute2[attribute2.Length - 1] - 48;
array[num].AddRange(RequirementBase.ParseRequirements(item));
}
}
if (dynamicProperties.Values.ContainsKey("Extends"))
{
string text = dynamicProperties.Values["Extends"];
ItemClass itemClass = ItemClass.GetItemClass(text);
if (itemClass == null)
{
throw new Exception($"Extends item {text} is not specified for item {attribute}'");
}
HashSet<string> hashSet = new HashSet<string> { Block.PropCreativeMode };
if (dynamicProperties.Params1.ContainsKey("Extends"))
{
string[] array2 = dynamicProperties.Params1["Extends"].Split(',');
foreach (string text2 in array2)
{
hashSet.Add(text2.Trim());
}
}
DynamicProperties dynamicProperties2 = new DynamicProperties();
dynamicProperties2.CopyFrom(itemClass.Properties, hashSet);
dynamicProperties2.CopyFrom(dynamicProperties);
dynamicProperties = dynamicProperties2;
}
ItemClass itemClass2;
if (dynamicProperties.Values.ContainsKey("Class"))
{
string text3 = dynamicProperties.Values["Class"];
if (!text3.Contains(","))
{
text3 += ",Assembly-CSharp";
}
try
{
itemClass2 = (ItemClass)Activator.CreateInstance(Type.GetType(text3));
}
catch (Exception)
{
throw new Exception("No item class '" + text3 + "' found!");
}
}
else
{
itemClass2 = new ItemClass();
}
itemClass2.Properties = dynamicProperties;
if (dynamicProperties.Params1.ContainsKey("Extends"))
{
string text4 = dynamicProperties.Values["Extends"];
if (ItemClass.GetItemClass(text4) == null)
{
throw new Exception($"Extends item {text4} is not specified for item {attribute}'");
}
}
itemClass2.Effects = MinEffectController.ParseXml(_node, null, MinEffectController.SourceParentType.ItemClass, itemClass2.Id);
itemClass2.SetName(attribute);
itemClass2.setLocalizedItemName(Localization.Get(attribute));
if (dynamicProperties.Values.ContainsKey("Stacknumber"))
{
itemClass2.Stacknumber = new DataItem<int>(int.Parse(dynamicProperties.Values["Stacknumber"]));
}
else
{
itemClass2.Stacknumber = new DataItem<int>(500);
}
if (dynamicProperties.Values.ContainsKey("Canhold"))
{
itemClass2.SetCanHold(StringParsers.ParseBool(dynamicProperties.Values["Canhold"]));
}
if (dynamicProperties.Values.ContainsKey("Candrop"))
{
itemClass2.SetCanDrop(StringParsers.ParseBool(dynamicProperties.Values["Candrop"]));
}
if (!dynamicProperties.Values.ContainsKey("Material"))
{
throw new Exception("Attribute 'material' missing on item '" + attribute + "'");
}
itemClass2.MadeOfMaterial = MaterialBlock.fromString(dynamicProperties.Values["Material"]);
if (itemClass2.MadeOfMaterial == null)
{
throw new Exception("Attribute 'material' '" + dynamicProperties.Values["Material"] + "' refers to not existing material in item '" + attribute + "'");
}
if (!dynamicProperties.Values.ContainsKey("Meshfile") && itemClass2.CanHold())
{
throw new Exception("Attribute 'Meshfile' missing on item '" + attribute + "'");
}
itemClass2.MeshFile = dynamicProperties.Values["Meshfile"];
DataLoader.PreloadBundle(itemClass2.MeshFile);
StringParsers.TryParseFloat(dynamicProperties.Values["StickyOffset"], out itemClass2.StickyOffset);
StringParsers.TryParseFloat(dynamicProperties.Values["StickyColliderRadius"], out itemClass2.StickyColliderRadius);
StringParsers.TryParseSInt32(dynamicProperties.Values["StickyColliderUp"], out itemClass2.StickyColliderUp);
StringParsers.TryParseFloat(dynamicProperties.Values["StickyColliderLength"], out itemClass2.StickyColliderLength);
itemClass2.StickyMaterial = dynamicProperties.Values["StickyMaterial"];
if (dynamicProperties.Values.ContainsKey("ImageEffectOnActive"))
{
itemClass2.ImageEffectOnActive = new DataItem<string>(dynamicProperties.Values["ImageEffectOnActive"]);
}
if (dynamicProperties.Values.ContainsKey("Active"))
{
itemClass2.Active = new DataItem<bool>(_startValue: false);
}
if (dynamicProperties.Values.ContainsKey(ItemClass.PropIsSticky))
{
itemClass2.IsSticky = StringParsers.ParseBool(dynamicProperties.Values[ItemClass.PropIsSticky]);
}
if (dynamicProperties.Values.ContainsKey("DropMeshfile") && itemClass2.CanHold())
{
itemClass2.DropMeshFile = dynamicProperties.Values["DropMeshfile"];
DataLoader.PreloadBundle(itemClass2.DropMeshFile);
}
if (dynamicProperties.Values.ContainsKey("HandMeshfile") && itemClass2.CanHold())
{
itemClass2.HandMeshFile = dynamicProperties.Values["HandMeshfile"];
DataLoader.PreloadBundle(itemClass2.HandMeshFile);
}
if (dynamicProperties.Values.ContainsKey("HoldType"))
{
string s = dynamicProperties.Values["HoldType"];
int result = 0;
if (!int.TryParse(s, out result))
{
throw new Exception("Cannot parse attribute hold_type for item '" + attribute + "'");
}
itemClass2.HoldType = new DataItem<int>(result);
}
if (dynamicProperties.Values.ContainsKey("RepairTools"))
{
string[] array3 = dynamicProperties.Values["RepairTools"].Replace(" ", "").Split(',');
DataItem<string>[] array4 = new DataItem<string>[array3.Length];
for (int k = 0; k < array3.Length; k++)
{
array4[k] = new DataItem<string>(array3[k]);
}
itemClass2.RepairTools = new ItemData.DataItemArrayRepairTools(array4);
}
if (dynamicProperties.Values.ContainsKey("RepairAmount"))
{
int result2 = 0;
int.TryParse(dynamicProperties.Values["RepairAmount"], out result2);
itemClass2.RepairAmount = new DataItem<int>(result2);
}
if (dynamicProperties.Values.ContainsKey("RepairTime"))
{
float _result = 0f;
StringParsers.TryParseFloat(dynamicProperties.Values["RepairTime"], out _result);
itemClass2.RepairTime = new DataItem<float>(_result);
}
else if (itemClass2.RepairAmount != null)
{
itemClass2.RepairTime = new DataItem<float>(1f);
}
if (dynamicProperties.Values.ContainsKey("Degradation"))
{
itemClass2.MaxUseTimes = new DataItem<int>(int.Parse(dynamicProperties.Values["Degradation"]));
}
else
{
itemClass2.MaxUseTimes = new DataItem<int>(0);
itemClass2.MaxUseTimesBreaksAfter = new DataItem<bool>(_startValue: false);
}
if (dynamicProperties.Values.ContainsKey("DegradationBreaksAfter"))
{
itemClass2.MaxUseTimesBreaksAfter = new DataItem<bool>(StringParsers.ParseBool(dynamicProperties.Values["DegradationBreaksAfter"]));
}
else if (dynamicProperties.Values.ContainsKey("Degradation"))
{
itemClass2.MaxUseTimesBreaksAfter = new DataItem<bool>(_startValue: true);
}
if (dynamicProperties.Values.ContainsKey("EconomicValue"))
{
itemClass2.EconomicValue = StringParsers.ParseFloat(dynamicProperties.Values["EconomicValue"]);
}
if (dynamicProperties.Classes.ContainsKey("Preview"))
{
DynamicProperties dynamicProperties3 = dynamicProperties.Classes["Preview"];
itemClass2.Preview = new PreviewData();
if (dynamicProperties3.Values.ContainsKey("Zoom"))
{
itemClass2.Preview.Zoom = new DataItem<int>(int.Parse(dynamicProperties3.Values["Zoom"]));
}
if (dynamicProperties3.Values.ContainsKey("Pos"))
{
itemClass2.Preview.Pos = new DataItem<Vector2>(StringParsers.ParseVector2(dynamicProperties3.Values["Pos"]));
}
else
{
itemClass2.Preview.Pos = new DataItem<Vector2>(Vector2.zero);
}
if (dynamicProperties3.Values.ContainsKey("Rot"))
{
itemClass2.Preview.Rot = new DataItem<Vector3>(StringParsers.ParseVector3(dynamicProperties3.Values["Rot"]));
}
else
{
itemClass2.Preview.Rot = new DataItem<Vector3>(Vector3.zero);
}
}
for (int l = 0; l < itemClass2.Actions.Length; l++)
{
string text5 = ItemClass.itemActionNames[l];
if (dynamicProperties.Classes.ContainsKey(text5))
{
if (!dynamicProperties.Values.ContainsKey(text5 + ".Class"))
{
throw new Exception("No class attribute found on " + text5 + " in item with '" + attribute + "'");
}
string text6 = dynamicProperties.Values[text5 + ".Class"];
ItemAction itemAction;
try
{
itemAction = (ItemAction)Activator.CreateInstance(ReflectionHelpers.GetTypeWithPrefix("ItemAction", text6));
}
catch (Exception)
{
throw new Exception("ItemAction class '" + text6 + " could not be instantiated");
}
itemAction.item = itemClass2;
itemAction.ActionIndex = l;
itemAction.ReadFrom(dynamicProperties.Classes[text5]);
if (array[l].Count > 0)
{
itemAction.ExecutionRequirements = array[l];
}
itemClass2.Actions[l] = itemAction;
}
}
itemClass2.Init();
return false;
}
*/
[HarmonyPatch(typeof(Inventory), nameof(Inventory.onInventoryChanged))]
[HarmonyPrefix]
private static bool Prefix_onInventoryChanged_Inventory(Inventory __instance)
{
if (__instance.entity != null)
MultiActionManager.UpdateLocalMetaSave(__instance.entity.entityId);
return true;
}
#endregion
//KEEP
#region Action mode handling
[HarmonyPatch(typeof(NetPackagePlayerStats), nameof(NetPackagePlayerStats.ProcessPackage))]
[HarmonyTranspiler]
private static IEnumerable<CodeInstruction> Transpiler_ProcessPackage_NetPackagePlayerStats(IEnumerable<CodeInstruction> instructions)
{
var codes = instructions.ToList();
var fld_entityid = AccessTools.Field(typeof(NetPackagePlayerStats), nameof(NetPackagePlayerStats.entityId));
var fld_itemstack = AccessTools.Field(typeof(NetPackagePlayerStats), nameof(NetPackagePlayerStats.holdingItemStack));
codes.InsertRange(codes.Count - 1, new[]
{
new CodeInstruction(OpCodes.Ldarg_0),
new CodeInstruction(OpCodes.Ldfld, fld_entityid),
new CodeInstruction(OpCodes.Ldarg_0),
new CodeInstruction(OpCodes.Ldfld, fld_itemstack),
CodeInstruction.Call(typeof(MultiActionPatches), nameof(CheckItemValueMode))
});
return codes;
}
[HarmonyPatch(typeof(NetPackageHoldingItem), nameof(NetPackageHoldingItem.ProcessPackage))]
[HarmonyTranspiler]
private static IEnumerable<CodeInstruction> Transpiler_ProcessPackage_NetPackageHoldingItem(IEnumerable<CodeInstruction> instructions)
{
var codes = instructions.ToList();
var fld_entityid = AccessTools.Field(typeof(NetPackagePlayerStats), nameof(NetPackageHoldingItem.entityId));
var fld_itemstack = AccessTools.Field(typeof(NetPackagePlayerStats), nameof(NetPackageHoldingItem.holdingItemStack));
codes.InsertRange(codes.Count - 1, new[]
{
new CodeInstruction(OpCodes.Ldarg_0),
new CodeInstruction(OpCodes.Ldfld, fld_entityid),
new CodeInstruction(OpCodes.Ldarg_0),
new CodeInstruction(OpCodes.Ldfld, fld_itemstack),
CodeInstruction.Call(typeof(MultiActionPatches), nameof(CheckItemValueMode))
});
return codes;
}
private static void CheckItemValueMode(int entityId, ItemStack holdingItemStack)
{
ItemValue itemValue = holdingItemStack.itemValue;
if (itemValue.HasMetadata(MultiActionMapping.STR_MULTI_ACTION_INDEX))
{
int mode = (int)itemValue.GetMetadata(MultiActionMapping.STR_MULTI_ACTION_INDEX);
if (MultiActionManager.SetModeForEntity(entityId, mode) && ConnectionManager.Instance.IsServer)
{
ConnectionManager.Instance.SendPackage(NetPackageManager.GetPackage<NetPackageEntityActionIndex>().Setup(entityId, mode), false, -1, entityId);
}
}
}
[HarmonyPatch(typeof(GameManager), nameof(GameManager.UpdateTick))]
[HarmonyPostfix]
private static void Postfix_UpdateTick_GameManager(GameManager __instance)
{
if (MultiActionManager.LocalModeChanged && __instance.m_World != null)
{
MultiActionManager.LocalModeChanged = false;
int playerID = __instance.m_World.GetPrimaryPlayerId();
if (ConnectionManager.Instance.IsClient)
{
ConnectionManager.Instance.SendToServer(NetPackageManager.GetPackage<NetPackageEntityActionIndex>().Setup(playerID, MultiActionManager.GetModeForEntity(playerID)));
}
else
{
ConnectionManager.Instance.SendPackage(NetPackageManager.GetPackage<NetPackageEntityActionIndex>().Setup(playerID, MultiActionManager.GetModeForEntity(playerID)), false, -1, playerID);
}
}
}
#endregion
#region Input Handling
[HarmonyPatch(typeof(PlayerMoveController), nameof(PlayerMoveController.Update))]
[HarmonyPrefix]
private static bool Prefix_Update_PlayerMoveController(PlayerMoveController __instance)
{
if (DroneManager.Debug_LocalControl || !__instance.gameManager.gameStateManager.IsGameStarted() || GameStats.GetInt(EnumGameStats.GameState) != 1)
return true;
bool isUIOpen = __instance.windowManager.IsCursorWindowOpen() || __instance.windowManager.IsInputActive() || __instance.windowManager.IsModalWindowOpen();
MultiActionManager.UpdateLocalInput(__instance.entityPlayerLocal, __instance.playerInput, isUIOpen, Time.deltaTime);
return true;
}
#endregion
#region HUD display
/// <summary>
/// redirect check to alternative action module
/// </summary>
/// <param name="instructions"></param>
/// <returns></returns>
[HarmonyPatch(typeof(XUiC_HUDStatBar), nameof(XUiC_HUDStatBar.HasChanged))]
[HarmonyTranspiler]
private static IEnumerable<CodeInstruction> Transpiler_HasChanged_XUiC_HUDStatBar(IEnumerable<CodeInstruction> instructions)
{
var codes = instructions.ToList();
MethodInfo mtd_edittool = AccessTools.Method(typeof(ItemAction), nameof(ItemAction.IsEditingTool));
for (int i = 0; i < codes.Count; i++)
{
var code = codes[i];
if (code.Calls(mtd_edittool))
{
codes.RemoveRange(i - 1, 3);
break;
}
}
return codes;
}
[HarmonyPatch(typeof(XUiC_HUDStatBar), nameof(XUiC_HUDStatBar.SetupActiveItemEntry))]
[HarmonyTranspiler]
private static IEnumerable<CodeInstruction> Transpiler_SetupActiveItemEntry_XUiC_HUDStatBar(IEnumerable<CodeInstruction> instructions, ILGenerator generator)
{
var codes = instructions.ToList();
var lbd_index = generator.DeclareLocal(typeof(int));
FieldInfo fld_action = AccessTools.Field(typeof(ItemClass), nameof(ItemClass.Actions));
FieldInfo fld_ammoindex = AccessTools.Field(typeof(ItemValue), nameof(ItemValue.SelectedAmmoTypeIndex));
for (int i = 0; i < codes.Count; i++)
{
if (codes[i].LoadsField(fld_action))
{
codes.RemoveAt(i + 1);
codes.InsertRange(i + 1, new[]
{
new CodeInstruction(OpCodes.Ldloc_2),
CodeInstruction.Call(typeof(MultiActionUtils), nameof(MultiActionUtils.GetActionIndexByMetaData)),
new CodeInstruction(OpCodes.Dup),
new CodeInstruction(OpCodes.Stloc_S, lbd_index)
});
i += 3;
}
else if (codes[i].LoadsField(fld_ammoindex))
{
codes[i].opcode = OpCodes.Call;
codes[i].operand = AccessTools.Method(typeof(MultiActionUtils), nameof(MultiActionUtils.GetSelectedAmmoIndexByActionIndex));
codes.Insert(i, new CodeInstruction(OpCodes.Ldloc_S, lbd_index));
break;
}
}
return codes;
}
[HarmonyPatch(typeof(XUiC_Radial), nameof(XUiC_Radial.handleActivatableItemCommand))]
[HarmonyPrefix]
private static bool Prefix_handleActivatableItemCommand_XUiC_Radial(XUiC_Radial _sender)
{
EntityPlayerLocal entityPlayer = _sender.xui.playerUI.entityPlayer;
MultiActionUtils.SetMinEventParamsByEntityInventory(entityPlayer);
return true;
}
[HarmonyPatch(typeof(XUiC_ItemInfoWindow), nameof(XUiC_ItemInfoWindow.GetBindingValue))]
[HarmonyTranspiler]
private static IEnumerable<CodeInstruction> Transpiler_GetBindingValue_XUiC_ItemInfoWindow(IEnumerable<CodeInstruction> instructions)
{
var codes = instructions.ToList();
var fld_actions = AccessTools.Field(typeof(ItemClass), nameof(ItemClass.Actions));
for (int i = 0; i < codes.Count; i++)
{
if (codes[i].LoadsField(fld_actions) && codes[i + 1].opcode == OpCodes.Ldc_I4_0)
{
codes.RemoveAt(i + 1);
codes.InsertRange(i + 1, new[]
{
new CodeInstruction(OpCodes.Ldarg_0),
CodeInstruction.LoadField(typeof(XUiC_ItemInfoWindow), nameof(XUiC_ItemInfoWindow.itemStack)),
CodeInstruction.LoadField(typeof(ItemStack), nameof(ItemStack.itemValue)),
CodeInstruction.Call(typeof(MultiActionUtils), nameof(MultiActionUtils.GetActionIndexByMetaData))
});
i += 3;
}
}
return codes;
}
#endregion
#region Cancel reload on switching item
//redirect these calls to action 0 and handle them in alternative module
//may change in the future
[HarmonyPatch(typeof(PlayerMoveController), nameof(PlayerMoveController.Update))]
[HarmonyTranspiler]
private static IEnumerable<CodeInstruction> Transpiler_Update_PlayerMoveController(IEnumerable<CodeInstruction> instructions)
{
var codes = instructions.ToList();
MethodInfo mtd_getgun = AccessTools.Method(typeof(Inventory), nameof(Inventory.GetHoldingGun));
MethodInfo mtd_getprimary = AccessTools.Method(typeof(Inventory), nameof(Inventory.GetHoldingPrimary));
FieldInfo fld_reload = AccessTools.Field(typeof(PlayerActionsPermanent), nameof(PlayerActionsPermanent.Reload));
for (int i = 0; i < codes.Count; i++)
{
if (codes[i].Calls(mtd_getgun))
{
codes[i].operand = mtd_getprimary;
//codes.RemoveAt(i - 2);
//codes.InsertRange(i - 2, new[]
//{
// new CodeInstruction(OpCodes.Ldarg_0),
// CodeInstruction.LoadField(typeof(PlayerMoveController), "entityPlayerLocal"),
// CodeInstruction.Call(typeof(MultiActionManager), nameof(MultiActionManager.GetActionIndexForEntity))
//});
//i += 2;
}
else if (codes[i].LoadsField(fld_reload))
{
var label = codes[i + 6].operand;
codes.InsertRange(i + 7, new[]
{
new CodeInstruction(OpCodes.Ldarg_0),
CodeInstruction.LoadField(typeof(PlayerMoveController), nameof(PlayerMoveController.entityPlayerLocal)),
CodeInstruction.LoadField(typeof(EntityAlive), nameof(EntityAlive.inventory)),
CodeInstruction.Call(typeof(Inventory), nameof(Inventory.GetIsFinishedSwitchingHeldItem)),
new CodeInstruction(OpCodes.Brfalse, label)
});
i += 5;
}
}
return codes;
}
#endregion
#region Underwater check
//skip underwater check if action is not current action
[HarmonyPatch(typeof(ItemActionRanged), nameof(ItemActionRanged.OnHoldingUpdate))]
[HarmonyTranspiler]
private static IEnumerable<CodeInstruction> Transpiler_OnHoldingUpdate_ItemActionRanged(IEnumerable<CodeInstruction> instructions)
{
var codes = instructions.ToList();
var fld_ammonames = AccessTools.Field(typeof(ItemActionAttack), nameof(ItemActionAttack.MagazineItemNames));
for (int i = 0; i < codes.Count; i++)
{
if (codes[i].LoadsField(fld_ammonames) && (codes[i + 1].opcode == OpCodes.Brfalse_S || codes[i + 1].opcode == OpCodes.Brfalse))
{
var jumpto = codes[i + 1].operand;
codes.InsertRange(i - 1, new[]
{
new CodeInstruction(OpCodes.Ldarg_1).WithLabels(codes[i - 1].ExtractLabels()),
CodeInstruction.LoadField(typeof(ItemActionData), nameof(ItemActionData.indexInEntityOfAction)),
new CodeInstruction(OpCodes.Ldarg_1),
CodeInstruction.LoadField(typeof(ItemActionData), nameof(ItemActionData.invData)),
CodeInstruction.LoadField(typeof(ItemInventoryData), nameof(ItemInventoryData.holdingEntity)),
CodeInstruction.Call(typeof(MultiActionManager), nameof(MultiActionManager.GetActionIndexForEntity)),
new CodeInstruction(OpCodes.Bne_Un_S, jumpto)
});
break;
}
}
return codes;
}
#endregion
#region GameEvent
[HarmonyPatch(typeof(ActionUnloadItems), nameof(ActionUnloadItems.HandleItemStackChange))]
[HarmonyTranspiler]
private static IEnumerable<CodeInstruction> Transpiler_HandleItemStackChange_ActionUnloadItems(IEnumerable<CodeInstruction> instructions, ILGenerator generator)
{
var codes = instructions.ToList();
var fld_actions = AccessTools.Field(typeof(ItemClass), nameof(ItemClass.Actions));
for (var i = 0; i < codes.Count; i++)
{
if (codes[i].LoadsField(fld_actions))
{
var label = generator.DefineLabel();
codes[i - 1].WithLabels(label);
codes.InsertRange(i - 1, new[]
{
new CodeInstruction(OpCodes.Ldarg_1),
new CodeInstruction(OpCodes.Ldind_Ref),
new CodeInstruction(OpCodes.Ldarg_0),
CodeInstruction.LoadField(typeof(ActionUnloadItems), nameof(ActionUnloadItems.ItemStacks)),
CodeInstruction.Call(typeof(MultiActionUtils), nameof(MultiActionUtils.MultiActionRemoveAmmoFromItemStack)),
new CodeInstruction(OpCodes.Brfalse_S, label),
new CodeInstruction(OpCodes.Ldc_I4_1),
new CodeInstruction(OpCodes.Ret)
});
break;
}
}
return codes;
}
#endregion
#region fast toolbelt item switching issue fix
private static Coroutine switchHoldingItemCo;
[HarmonyPatch(typeof(Inventory), nameof(Inventory.ShowHeldItem))]
[HarmonyPrefix]
private static bool Prefix_ShowHeldItem_Inventory(bool show, Inventory __instance)
{
//Log.Out($"ShowHeldItem {show} on entity {__instance.entity.entityName}\n{StackTraceUtility.ExtractStackTrace()}");
if (show && __instance.entity is EntityPlayerLocal && switchHoldingItemCo != null)
{
GameManager.Instance.StopCoroutine(switchHoldingItemCo);
switchHoldingItemCo = null;
__instance.SetIsFinishedSwitchingHeldItem();
}
return true;
}
[HarmonyPatch(typeof(Inventory), nameof(Inventory.ShowHeldItem))]
[HarmonyTranspiler]
private static IEnumerable<CodeInstruction> Transpiler_ShowHeldItem_Inventory(IEnumerable<CodeInstruction> instructions, ILGenerator generator)
{
var codes = instructions.ToList();
for (int i = 0; i < codes.Count; i++)
{
if (codes[i].opcode == OpCodes.Pop)
{
var label = generator.DefineLabel();
codes[i].WithLabels(label);
codes.InsertRange(i, new[]
{
new CodeInstruction(OpCodes.Ldarg_1),
new CodeInstruction(OpCodes.Brfalse_S, label),
new CodeInstruction(OpCodes.Ldarg_0),
CodeInstruction.LoadField(typeof(Inventory), nameof(Inventory.entity)),
new CodeInstruction(OpCodes.Isinst, typeof(EntityPlayerLocal)),
new CodeInstruction(OpCodes.Brfalse_S, label),
CodeInstruction.StoreField(typeof(MultiActionPatches), nameof(switchHoldingItemCo)),
new CodeInstruction(OpCodes.Ret)
});
break;
}
}
return codes;
}
#endregion
#region item info display fix
[HarmonyPatch(typeof(XUiM_ItemStack), nameof(XUiM_ItemStack.GetStatItemValueTextWithModInfo))]
[HarmonyPrefix]
private static bool Prefix_GetStatItemValueTextWithModInfo_XUiM_ItemStack(ItemStack itemStack)
{
MultiActionUtils.SetCachedEventParamsDummyAction(itemStack);
return true;
}
[HarmonyPatch(typeof(XUiM_ItemStack), nameof(XUiM_ItemStack.GetStatItemValueTextWithModColoring))]
[HarmonyPrefix]
private static bool Prefix_GetStatItemValueTextWithModColoring_XUiM_ItemStack(ItemStack itemStack)
{
MultiActionUtils.SetCachedEventParamsDummyAction(itemStack);
return true;
}
[HarmonyPatch(typeof(XUiM_ItemStack), nameof(XUiM_ItemStack.GetStatItemValueTextWithCompareInfo))]
[HarmonyTranspiler]
private static IEnumerable<CodeInstruction> Transpiler_GetStatItemValueTextWithCompareInfo_XUiM_ItemStack(IEnumerable<CodeInstruction> instructions)
{
var codes = instructions.ToList();
var mtd_getvalue = AccessTools.Method(typeof(EffectManager), nameof(EffectManager.GetValue));
var fld_seed = AccessTools.Field(typeof(MinEventParams), nameof(MinEventParams.Seed));
for (int i = 0; i < codes.Count; i++)
{
if (codes[i].Calls(mtd_getvalue))
{
codes.InsertRange(i + 2, new[]
{
new CodeInstruction(OpCodes.Ldarg_1),
CodeInstruction.Call(typeof(MultiActionUtils), nameof(MultiActionUtils.SetCachedEventParamsDummyAction)),
});
for (int j = i; j >= 0; j--)
{
if (codes[j].StoresField(fld_seed))
{
codes.InsertRange(j + 1, new[]
{
new CodeInstruction(OpCodes.Ldarg_0),
CodeInstruction.Call(typeof(MultiActionUtils), nameof(MultiActionUtils.SetCachedEventParamsDummyAction))
});
codes.RemoveRange(j - 8, 9);
break;
}
}
break;
}
}
return codes.Manipulator(static ins => ins.IsLdarg(2), static ins => ins.opcode = OpCodes.Ldnull);
}
[HarmonyPatch(typeof(XUiC_ItemStack), nameof(XUiC_ItemStack.GetBindingValue))]
[HarmonyTranspiler]
private static IEnumerable<CodeInstruction> Transpiler_GetBindingValue_XUiC_ItemStack(IEnumerable<CodeInstruction> instructions)
{
var codes = instructions.ToList();
var prop_perc = AccessTools.PropertyGetter(typeof(ItemValue), nameof(ItemValue.PercentUsesLeft));
for (int i = 0; i < codes.Count; i++)
{
if (codes[i].Calls(prop_perc))
{
codes.InsertRange(i - 3, new[]
{
new CodeInstruction(OpCodes.Ldarg_0),
CodeInstruction.LoadField(typeof(XUiC_ItemStack), nameof(XUiC_ItemStack.itemStack)),
CodeInstruction.Call(typeof(MultiActionUtils), nameof(MultiActionUtils.SetCachedEventParamsDummyAction))
});
break;
}
}
return codes;
}
[HarmonyPatch(typeof(PassiveEffect), nameof(PassiveEffect.ModifyValue))]
[HarmonyTranspiler]
private static IEnumerable<CodeInstruction> Transpiler_ModifyValue_PassiveEffect(IEnumerable<CodeInstruction> instructions)
{
var codes = instructions.ToList();
for (int i = 0; i < codes.Count - 1; i++)
{
if (codes[i].opcode == OpCodes.Ldloc_1 && codes[i + 1].Branches(out var lbl) && lbl != null)
{
codes.InsertRange(i + 2, new[]
{
new CodeInstruction(OpCodes.Ldarg_1),
new CodeInstruction(OpCodes.Brfalse_S, lbl)
});
break;
}
}
return codes;
}
#endregion
#region ItemAction exclude tags
[HarmonyPatch(typeof(GameManager), nameof(GameManager.StartGame))]
[HarmonyPrefix]
private static bool Prefix_StartGame_GameManager()
{
MultiActionManager.PreloadCleanup();
return true;
}
[HarmonyPatch(typeof(ItemClass), nameof(ItemClass.LateInit))]
[HarmonyPostfix]
private static void Postfix_LateInit_ItemClass(ItemClass __instance)
{
MultiActionManager.ParseItemActionExcludeTagsAndModifiers(__instance);
}
/// <summary>
///
/// </summary>
/// <param name="instructions"></param>
/// <returns></returns>
[HarmonyPatch(typeof(EffectManager), nameof(EffectManager.GetValue))]
[HarmonyTranspiler]
private static IEnumerable<CodeInstruction> Transpiler_GetValue_EffectManager(IEnumerable<CodeInstruction> instructions)
{
var codes = instructions.ToList();
for (int i = 0; i < codes.Count; i++)
{
if (codes[i].IsStarg(5))
{
codes.InsertRange(i + 1, new[]
{
new CodeInstruction(OpCodes.Ldarg_1).WithLabels(codes[i + 1].ExtractLabels()),
CodeInstruction.LoadField(typeof(MinEventParams), nameof(MinEventParams.CachedEventParam)),
CodeInstruction.LoadField(typeof(MinEventParams), nameof(MinEventParams.ItemActionData)),
new CodeInstruction(OpCodes.Ldarga_S, 5),
CodeInstruction.Call(typeof(MultiActionManager), nameof(MultiActionManager.ModifyItemTags))
});
break;
}
}
return codes;
}
[HarmonyPatch(typeof(EffectManager), nameof(EffectManager.GetValuesAndSources))]
[HarmonyPrefix]
private static bool Prefix_GetValuesAndSources_EffectManager(ItemValue _originalItemValue, EntityAlive _entity, ref FastTags<TagGroup.Global> tags)
{
MultiActionManager.ModifyItemTags(_originalItemValue, _entity?.MinEventContext?.ItemActionData, ref tags);
return true;
}
#endregion
#region ItemAction exclude modifiers
//see Transpiler_ModifyValue_ItemValue
//see Transpiler_GetModifiedValueData_ItemValue
//see MultiActionProjectileRewrites.ProjectileValueModifyValue
//see MultiActionUtils.GetPropertyOverrideForAction
//see MultiActionManager.ParseItemActionExcludeTagsAndModifiers
#endregion
#region requirement tags exclude
//[HarmonyPatch(typeof(TriggerHasTags), nameof(TriggerHasTags.IsValid))]
//[HarmonyTranspiler]
//private static IEnumerable<CodeInstruction> Transpiler_IsValid_TriggerHasTags(IEnumerable<CodeInstruction> instructions, ILGenerator generator)
//{
// var codes = instructions.ToList();
// var lbd_tags = generator.DeclareLocal(typeof(FastTags<TagGroup.Global>));
// FieldInfo fld_tags = AccessTools.Field(typeof(MinEventParams), nameof(MinEventParams.Tags));
// bool firstRet = true;
// for (int i = 0; i < codes.Count; i++)
// {
// if (codes[i].opcode == OpCodes.Ret && firstRet)
// {
// firstRet = false;
// codes.InsertRange(i + 1, new[]
// {
// new CodeInstruction(OpCodes.Ldarg_1),
// new CodeInstruction(OpCodes.Ldfld, fld_tags),
// new CodeInstruction(OpCodes.Stloc_S, lbd_tags),
// new CodeInstruction(OpCodes.Ldarg_1),
// CodeInstruction.LoadField(typeof(MinEventParams), nameof(MinEventParams.ItemValue)),
// new CodeInstruction(OpCodes.Ldarg_1),
// CodeInstruction.LoadField(typeof(MinEventParams), nameof(MinEventParams.ItemActionData)),
// new CodeInstruction(OpCodes.Ldloca_S, lbd_tags),
// CodeInstruction.Call(typeof(MultiActionManager), nameof(MultiActionManager.ModifyItemTags))
// });
// i += 9;
// }
// else if (codes[i].LoadsField(fld_tags))
// {
// codes[i].opcode = OpCodes.Ldloca_S;
// codes[i].operand = lbd_tags;
// codes[i].WithLabels(codes[i - 1].ExtractLabels());
// codes.RemoveAt(i - 1);
// i--;
// }
// }
// return codes;
//}
[HarmonyPatch(typeof(ItemHasTags), nameof(ItemHasTags.IsValid))]
[HarmonyTranspiler]
private static IEnumerable<CodeInstruction> Transpiler_IsValid_ItemHasTags(IEnumerable<CodeInstruction> instructions, ILGenerator generator)
{
var codes = instructions.ToList();
var lbd_tags = generator.DeclareLocal(typeof(FastTags<TagGroup.Global>));
FieldInfo fld_itemvalue = AccessTools.Field(typeof(MinEventParams), nameof(MinEventParams.ItemValue));
FieldInfo fld_hasalltags = AccessTools.Field(typeof(ItemHasTags), nameof(ItemHasTags.hasAllTags));
MethodInfo prop_itemclass = AccessTools.PropertyGetter(typeof(ItemValue), nameof(ItemValue.ItemClass));
MethodInfo mtd_hasanytags = AccessTools.Method(typeof(ItemClass), nameof(ItemClass.HasAnyTags));
MethodInfo mtd_hasalltags = AccessTools.Method(typeof(ItemClass), nameof(ItemClass.HasAllTags));
for (int i = 0; i < codes.Count; i++)
{
if (codes[i].LoadsField(fld_hasalltags))
{
codes.InsertRange(i - 1, new[]
{
new CodeInstruction(OpCodes.Ldarg_1),
new CodeInstruction(OpCodes.Ldfld, fld_itemvalue),
new CodeInstruction(OpCodes.Callvirt, prop_itemclass),
CodeInstruction.LoadField(typeof(ItemClass), nameof(ItemClass.ItemTags)),
new CodeInstruction(OpCodes.Stloc_S, lbd_tags),
new CodeInstruction(OpCodes.Ldarg_1),
new CodeInstruction(OpCodes.Ldfld, fld_itemvalue),
new CodeInstruction(OpCodes.Ldarg_1),
CodeInstruction.LoadField(typeof(MinEventParams), nameof(MinEventParams.ItemActionData)),
new CodeInstruction(OpCodes.Ldloca_S, lbd_tags),
CodeInstruction.Call(typeof(MultiActionManager), nameof(MultiActionManager.ModifyItemTags))
});
i += 11;
}
else if (codes[i].Calls(mtd_hasanytags))
{
codes[i].opcode = OpCodes.Call;
codes[i].operand = AccessTools.Method(typeof(FastTags<TagGroup.Global>), nameof(FastTags<TagGroup.Global>.Test_AnySet));
var labels = codes[i - 5].ExtractLabels();
codes.RemoveRange(i - 5, 3);
codes.Insert(i - 5, new CodeInstruction(OpCodes.Ldloca_S, lbd_tags).WithLabels(labels));
i -= 2;
}
else if (codes[i].Calls(mtd_hasalltags))
{
codes[i].opcode = OpCodes.Call;
codes[i].operand = AccessTools.Method(typeof(FastTags<TagGroup.Global>), nameof(FastTags<TagGroup.Global>.Test_AllSet));
var labels = codes[i - 5].ExtractLabels();
codes.RemoveRange(i - 5, 3);
codes.Insert(i - 5, new CodeInstruction(OpCodes.Ldloca_S, lbd_tags).WithLabels(labels));
i -= 2;
}
}
return codes;
}
[HarmonyPatch(typeof(HoldingItemHasTags), nameof(ItemHasTags.IsValid))]
[HarmonyTranspiler]
private static IEnumerable<CodeInstruction> Transpiler_IsValid_HoldingItemHasTags(IEnumerable<CodeInstruction> instructions, ILGenerator generator)
{
var codes = instructions.ToList();
var lbd_tags = generator.DeclareLocal(typeof(FastTags<TagGroup.Global>));
FieldInfo fld_itemvalue = AccessTools.Field(typeof(MinEventParams), nameof(MinEventParams.ItemValue));
FieldInfo fld_hasalltags = AccessTools.Field(typeof(HoldingItemHasTags), nameof(HoldingItemHasTags.hasAllTags));
MethodInfo prop_itemclass = AccessTools.PropertyGetter(typeof(ItemValue), nameof(ItemValue.ItemClass));
MethodInfo prop_itemvalue = AccessTools.PropertyGetter(typeof(Inventory), nameof(Inventory.holdingItemItemValue));
MethodInfo mtd_hasanytags = AccessTools.Method(typeof(ItemClass), nameof(ItemClass.HasAnyTags));
MethodInfo mtd_hasalltags = AccessTools.Method(typeof(ItemClass), nameof(ItemClass.HasAllTags));
for (int i = 0; i < codes.Count; i++)
{
if (codes[i].LoadsField(fld_hasalltags))
{
codes.InsertRange(i - 1, new[]
{
new CodeInstruction(OpCodes.Ldarg_0),
CodeInstruction.LoadField(typeof(HoldingItemHasTags), nameof(HoldingItemHasTags.target)),
CodeInstruction.LoadField(typeof(EntityAlive), nameof(EntityAlive.inventory)),
new CodeInstruction(OpCodes.Callvirt, prop_itemvalue),
new CodeInstruction(OpCodes.Callvirt, prop_itemclass),
CodeInstruction.LoadField(typeof(ItemClass), nameof(ItemClass.ItemTags)),
new CodeInstruction(OpCodes.Stloc_S, lbd_tags),
new CodeInstruction(OpCodes.Ldarg_0),
CodeInstruction.LoadField(typeof(HoldingItemHasTags), nameof(HoldingItemHasTags.target)),
CodeInstruction.LoadField(typeof(EntityAlive), nameof(EntityAlive.inventory)),
new CodeInstruction(OpCodes.Callvirt, prop_itemvalue),
new CodeInstruction(OpCodes.Ldarg_0),
CodeInstruction.LoadField(typeof(HoldingItemHasTags), nameof(HoldingItemHasTags.target)),
CodeInstruction.LoadField(typeof(EntityAlive), nameof(EntityAlive.MinEventContext)),
CodeInstruction.LoadField(typeof(MinEventParams), nameof(MinEventParams.ItemActionData)),
new CodeInstruction(OpCodes.Ldloca_S, lbd_tags),
CodeInstruction.Call(typeof(MultiActionManager), nameof(MultiActionManager.ModifyItemTags))
});
i += 17;
}
else if (codes[i].Calls(mtd_hasanytags))
{
codes[i].opcode = OpCodes.Call;
codes[i].operand = AccessTools.Method(typeof(FastTags<TagGroup.Global>), nameof(FastTags<TagGroup.Global>.Test_AnySet));
var labels = codes[i - 6].ExtractLabels();
codes.RemoveRange(i - 6, 4);
codes.Insert(i - 6, new CodeInstruction(OpCodes.Ldloca_S, lbd_tags).WithLabels(labels));
i -= 3;
}
else if (codes[i].Calls(mtd_hasalltags))
{
codes[i].opcode = OpCodes.Call;
codes[i].operand = AccessTools.Method(typeof(FastTags<TagGroup.Global>), nameof(FastTags<TagGroup.Global>.Test_AllSet));
var labels = codes[i - 6].ExtractLabels();
codes.RemoveRange(i - 6, 4);
codes.Insert(i - 6, new CodeInstruction(OpCodes.Ldloca_S, lbd_tags).WithLabels(labels));
i -= 3;
}
}
return codes;
}
#endregion
#region Inventory make ItemValue valid on creating inventory data
[HarmonyPatch(typeof(Inventory), nameof(Inventory.SetItem), new[] { typeof(int), typeof(ItemValue), typeof(int), typeof(bool) })]
[HarmonyTranspiler]
private static IEnumerable<CodeInstruction> Transpiler_SetItem_Inventory(IEnumerable<CodeInstruction> instructions)
{
var codes = instructions.ToList();
var mtd_clone = AccessTools.Method(typeof(ItemValue), nameof(ItemValue.Clone));
var mtd_create = AccessTools.Method(typeof(Inventory), nameof(Inventory.createHeldItem));
var mtd_invdata = AccessTools.Method(typeof(Inventory), nameof(Inventory.createInventoryData));
for (int i = 0; i < codes.Count; i++)
{
//if (codes[i].opcode == OpCodes.Ldarg_3 && (codes[i + 1].opcode == OpCodes.Brtrue_S || codes[i + 1].opcode == OpCodes.Brtrue))
//{
// var label = codes[i + 4].ExtractLabels();
// codes.InsertRange(i + 4, new[]
// {
// new CodeInstruction(OpCodes.Ldarg_2).WithLabels(label),
// new CodeInstruction(OpCodes.Callvirt, mtd_clone),
// new CodeInstruction(OpCodes.Starg_S, 2)
// });
// i += 7;
//}
//else
if (codes[i].Calls(mtd_create))
{
codes.InsertRange(i - 12, new[]
{
new CodeInstruction(OpCodes.Ldarg_2),
CodeInstruction.StoreField(typeof(ActionModuleAlternative), nameof(ActionModuleAlternative.InventorySetItemTemp))
});
i += 4;
for (int j = i; j < codes.Count; j++)
{
if (codes[j].Calls(mtd_invdata))
{
codes.InsertRange(j + 2, new[]
{
new CodeInstruction(OpCodes.Ldnull),
CodeInstruction.StoreField(typeof(ActionModuleAlternative), nameof(ActionModuleAlternative.InventorySetItemTemp))
});
i = j + 4;
break;
}
}
break;
}
//else if (codes[i].Calls(mtd_clone))
//{
// codes.RemoveAt(i);
// break;
//}
}
return codes;
}
[HarmonyPatch(typeof(Inventory), nameof(Inventory.ForceHoldingItemUpdate))]
[HarmonyTranspiler]
private static IEnumerable<CodeInstruction> Transpiler_ForceHoldingItemUpdate_Inventory(IEnumerable<CodeInstruction> instructions)
{
var codes = instructions.ToList();
var mtd_invdata = AccessTools.Method(typeof(Inventory), nameof(Inventory.createInventoryData));
for (int i = 0; i < codes.Count; i++)
{
if (codes[i].Calls(mtd_invdata))
{
codes.InsertRange(i + 2, new[]
{
new CodeInstruction(OpCodes.Ldnull),
CodeInstruction.StoreField(typeof(ActionModuleAlternative), nameof(ActionModuleAlternative.InventorySetItemTemp))
});
codes.InsertRange(i - 8, new[]
{
new CodeInstruction(OpCodes.Ldloc_0).WithLabels(codes[i - 8].ExtractLabels()),
CodeInstruction.StoreField(typeof(ActionModuleAlternative), nameof(ActionModuleAlternative.InventorySetItemTemp))
});
break;
}
}
return codes;
}
#endregion
#region Temporaty fix for hud ammo mismatch
[HarmonyPatch(typeof(XUiC_HUDStatBar), nameof(XUiC_HUDStatBar.Update))]
[HarmonyTranspiler]
private static IEnumerable<CodeInstruction> Transpiler_Update_XUiC_HUDStatBar(IEnumerable<CodeInstruction> instructions)
{
MethodInfo mtd_getfocus = AccessTools.Method(typeof(Inventory), nameof(Inventory.GetFocusedItemIdx));
MethodInfo mtd_getholding = AccessTools.PropertyGetter(typeof(Inventory), nameof(Inventory.holdingItemIdx));
foreach (var ins in instructions)
{
if (ins.Calls(mtd_getfocus))
{
ins.operand = mtd_getholding;
}
yield return ins;
}
}
#endregion
}
//Moved to MultiActionFix
//#region Ranged Reload
//[HarmonyPatch]
//public static class RangedReloadPatches
//{
// private static IEnumerable<MethodBase> TargetMethods()
// {
// return new MethodInfo[]
// {
// AccessTools.Method(typeof(ItemActionAttack), nameof(ItemActionAttack.ReloadGun)),
// AccessTools.Method(typeof(ItemActionRanged), nameof(ItemActionRanged.ReloadGun)),
// AccessTools.Method(typeof(ItemActionLauncher), nameof(ItemActionLauncher.ReloadGun))
// };
// }
// //Why? Ask TFP why they don't just call base.ReloadGun()
// [HarmonyPrefix]
// private static bool Prefix_ReloadGun(ItemActionData _actionData)
// {
// int reloadAnimationIndex = MultiActionManager.GetMetaIndexForActionIndex(_actionData.invData.holdingEntity.entityId, _actionData.indexInEntityOfAction);
// _actionData.invData.holdingEntity.emodel?.avatarController?.UpdateInt(AvatarController.itemActionIndexHash, reloadAnimationIndex, false);
// _actionData.invData.holdingEntity.MinEventContext.ItemActionData = _actionData;
// return true;
// }
//}
//#endregion
//KEEP
#region Melee action tags
[HarmonyPatch]
public static class ActionTagPatches1
{
private static IEnumerable<MethodBase> TargetMethods()
{
return new MethodInfo[]
{
AccessTools.Method(typeof(AnimatorMeleeAttackState), nameof(AnimatorMeleeAttackState.OnStateEnter), new[] {typeof(Animator), typeof(AnimatorStateInfo), typeof(int)}),
AccessTools.Method(typeof(ItemActionAttack), nameof(ItemActionAttack.GetDamageBlock)),
AccessTools.Method(typeof(ItemActionAttack), nameof(ItemActionAttack.GetDamageEntity)),
AccessTools.Method(typeof(ItemActionDynamic), nameof(ItemActionDynamic.GetDamageBlock)),
AccessTools.Method(typeof(ItemActionDynamic), nameof(ItemActionDynamic.GetDamageEntity)),
AccessTools.Method(typeof(ItemActionThrownWeapon), nameof(ItemActionThrownWeapon.GetDamageBlock)),
AccessTools.Method(typeof(ItemActionThrownWeapon), nameof(ItemActionThrownWeapon.GetDamageEntity))
};
}
//set correct tag for action index above 2
//only action 1 uses secondary tag, others still use primary
[HarmonyTranspiler]
private static IEnumerable<CodeInstruction> Transpiler(IEnumerable<CodeInstruction> instructions)
{
var codes = instructions.ToList();
FieldInfo fld_tag = AccessTools.Field(typeof(ItemActionAttack), nameof(ItemActionAttack.SecondaryTag));
for (int i = 0; i < codes.Count; i++)
{
var code = codes[i];
if (code.LoadsField(fld_tag))
{
codes.InsertRange(i - 1, new[]
{
new CodeInstruction(OpCodes.Ldc_I4_1),
new CodeInstruction(OpCodes.Ceq)
});
i += 2;
}
}
return codes;
}
}
[HarmonyPatch]
public static class ActionTagPatches2
{
private static IEnumerable<MethodBase> TargetMethods()
{
return new MethodInfo[]
{
AccessTools.Method(typeof(ItemActionDynamicMelee), nameof(ItemActionDynamicMelee.Raycast)),
AccessTools.Method(typeof(ItemActionDynamic), nameof(ItemActionDynamic.GetExecuteActionGrazeTarget)),
AccessTools.Method(typeof(ItemActionDynamic), nameof(ItemActionDynamic.hitTarget)),
AccessTools.Method(typeof(ItemActionDynamicMelee), nameof(ItemActionDynamicMelee.canStartAttack)),
AccessTools.Method(typeof(ItemActionDynamicMelee), nameof(ItemActionDynamicMelee.OnHoldingUpdate)),
AccessTools.Method(typeof(ItemActionDynamicMelee), nameof(ItemActionDynamicMelee.SetAttackFinished)),
AccessTools.Method(typeof(ItemActionMelee), nameof(ItemActionMelee.OnHoldingUpdate)),
AccessTools.Method(typeof(ItemActionRanged), nameof(ItemActionRanged.ExecuteAction)),
AccessTools.Method(typeof(ItemActionRanged), nameof(ItemActionRanged.fireShot)),
AccessTools.Method(typeof(ItemActionThrownWeapon), nameof(ItemActionThrownWeapon.throwAway)),
AccessTools.Method(typeof(ItemActionUseOther), nameof(ItemActionUseOther.ExecuteAction)),
};
}
[HarmonyTranspiler]
private static IEnumerable<CodeInstruction> Transpiler(IEnumerable<CodeInstruction> instructions)
{
var codes = instructions.ToList();
FieldInfo fld_index = AccessTools.Field(typeof(ItemActionData), nameof(ItemActionData.indexInEntityOfAction));
for (int i = 0; i < codes.Count - 1; i++)
{
var code = codes[i];
if (code.LoadsField(fld_index) && (codes[i + 1].opcode == OpCodes.Brfalse_S || codes[i + 1].opcode == OpCodes.Brfalse))
{
codes.InsertRange(i + 1, new[]
{
new CodeInstruction(OpCodes.Ldc_I4_1),
new CodeInstruction(OpCodes.Ceq)
});
i += 2;
}
}
return codes;
}
}
#endregion
//KEEP
#region 3
[HarmonyPatch]
public static class ThreePatches
{
private static IEnumerable<MethodBase> TargetMethods()
{
return new MethodInfo[]
{
AccessTools.Method(typeof(ItemClass), nameof(ItemClass.OnHoldingUpdate)),
AccessTools.Method(typeof(ItemClass), nameof(ItemClass.CleanupHoldingActions)),
AccessTools.Method(typeof(ItemClass), nameof(ItemClass.StartHolding)),
AccessTools.Method(typeof(ItemClass), nameof(ItemClass.StopHolding)),
AccessTools.Method(typeof(ItemClass), nameof(ItemClass.IsActionRunning))
};
}
[HarmonyTranspiler]
private static IEnumerable<CodeInstruction> Transpiler_Three(IEnumerable<CodeInstruction> instructions)
{
foreach (var instruction in instructions)
{
if (instruction.opcode == OpCodes.Ldc_I4_3)
instruction.opcode = OpCodes.Ldc_I4_5;
yield return instruction;
}
}
}
#endregion
#region ItemAction property override
[HarmonyPatch]
public static class ItemActionPropertyOverridePatches
{
private static IEnumerable<MethodBase> TargetMethods()
{
return AppDomain.CurrentDomain.GetAssemblies()
.SelectMany(a =>
{
try
{
return a.GetTypes();
}
catch
{
return new Type[0];
}
})
.Where(t => t.IsSubclassOf(typeof(ItemAction)))
.Select(t => AccessTools.Method(t, nameof(ItemAction.OnModificationsChanged)))
.Where(m => m.IsDeclaredMember());
}
[HarmonyTranspiler]
private static IEnumerable<CodeInstruction> Transpiler(IEnumerable<CodeInstruction> instructions, MethodBase __originalMethod)
{
Log.Out($"Patching property override method {__originalMethod.DeclaringType.Name}.{__originalMethod.Name}");
var codes = instructions.ToList();
var mtd_override = AccessTools.Method(typeof(ItemValue), nameof(ItemValue.GetPropertyOverride));
var mtd_newoverride = AccessTools.Method(typeof(MultiActionUtils), nameof(MultiActionUtils.GetPropertyOverrideForAction));
for (int i = 0; i < codes.Count; i++)
{
if (codes[i].Calls(mtd_override))
{
codes[i].opcode = OpCodes.Call;
codes[i].operand = mtd_newoverride;
codes.InsertRange(i, new[]
{
new CodeInstruction(OpCodes.Ldarg_0),
CodeInstruction.LoadField(typeof(ItemAction), nameof(ItemAction.ActionIndex))
});
i += 2;
}
}
return codes;
}
}
#endregion
#region Remove ammo
[HarmonyPatch]
public static class RemoveAmmoPatches
{
private static IEnumerable<MethodBase> TargetMethods()
{
return new[]
{
AccessTools.Method(typeof(ItemActionEntryAssemble), nameof(ItemActionEntryAssemble.HandleRemoveAmmo)),
AccessTools.Method(typeof(ItemActionEntryScrap), nameof(ItemActionEntryScrap.HandleRemoveAmmo)),
AccessTools.Method(typeof(ItemActionEntrySell), nameof(ItemActionEntrySell.HandleRemoveAmmo)),
};
}
[HarmonyPrefix]
private static bool Prefix(BaseItemActionEntry __instance, ItemStack stack, ref ItemStack __result)
{
List<ItemStack> list_ammo_stack = new List<ItemStack>();
if (!MultiActionUtils.MultiActionRemoveAmmoFromItemStack(stack, list_ammo_stack))
return true;
foreach (var ammoStack in list_ammo_stack)
{
if (!__instance.ItemController.xui.PlayerInventory.AddItem(ammoStack))
{
__instance.ItemController.xui.PlayerInventory.DropItem(ammoStack);
}
}
__result = stack;
return false;
}
}
#endregion
#region Generate initial meta
[HarmonyPatch]
public static class InitialMetaPatches
{
private static IEnumerable<MethodBase> TargetMethods()
{
return new MethodInfo[]
{
AccessTools.Method(typeof(EntityPlayerLocal), nameof(EntityPlayerLocal.SetupStartingItems)),
AccessTools.Method(typeof(ItemClass), nameof(ItemClass.CreateItemStacks))
};
}
[HarmonyTranspiler]
private static IEnumerable<CodeInstruction> Transpiler(IEnumerable<CodeInstruction> instructions)
{
var codes = instructions.ToList();
var mtd_initial = AccessTools.Method(typeof(ItemClass), nameof(ItemClass.GetInitialMetadata));
var mtd_initialnew = AccessTools.Method(typeof(MultiActionUtils), nameof(MultiActionUtils.GetMultiActionInitialMetaData));
for (int i = 0; i < codes.Count; i++)
{
if (codes[i].Calls(mtd_initial))
{
codes[i].opcode = OpCodes.Call;
codes[i].operand = mtd_initialnew;
break;
}
}
return codes;
}
}
#endregion
#region Item Info DisplayType
[HarmonyPatch]
public static class DisplayTypePatches
{
[HarmonyPatch(typeof(XUiC_AssembleWindow), nameof(XUiC_AssembleWindow.ItemStack), MethodType.Setter)]
[HarmonyTranspiler]
private static IEnumerable<CodeInstruction> Transpiler_ItemStack_XUiC_AssembleWindow(IEnumerable<CodeInstruction> instructions)
{
var codes = instructions.ToList();
var fld_displaytype = AccessTools.Field(typeof(ItemClass), nameof(ItemClass.DisplayType));
for (int i = 0; i < codes.Count; i++)
{
if (codes[i].LoadsField(fld_displaytype))
{
codes.RemoveRange(i - 1, 2);
codes.InsertRange(i - 1, new[]
{
CodeInstruction.LoadField(typeof(XUiC_AssembleWindow), nameof(XUiC_AssembleWindow.itemStack)),
CodeInstruction.Call(typeof(MultiActionUtils), nameof(MultiActionUtils.GetDisplayTypeForAction), new []{ typeof(ItemStack) })
});
break;
}
}
return codes;
}
[HarmonyPatch(typeof(XUiC_ItemInfoWindow), nameof(XUiC_ItemInfoWindow.SetInfo))]
[HarmonyTranspiler]
private static IEnumerable<CodeInstruction> Transpiler_SetInfo_XUiC_ItemInfoWindow(IEnumerable<CodeInstruction> instructions, MethodBase originalMethod)
{
var codes = instructions.ToList();
var fld_displaytype = AccessTools.Field(typeof(ItemClass), nameof(ItemClass.DisplayType));
for (int i = 0; i < codes.Count; i++)
{
if (codes[i].LoadsField(fld_displaytype))
{
codes.RemoveRange(i - 1, 2);
codes.InsertRange(i - 1, new[]
{
CodeInstruction.LoadField(typeof(XUiC_ItemInfoWindow), nameof(XUiC_ItemInfoWindow.itemStack)),
CodeInstruction.Call(typeof(MultiActionUtils), nameof(MultiActionUtils.GetDisplayTypeForAction), new []{ typeof(ItemStack) })
});
break;
}
}
return codes;
}
[HarmonyPatch(typeof(XUiM_ItemStack), nameof(XUiM_ItemStack.HasItemStats))]
[HarmonyTranspiler]
private static IEnumerable<CodeInstruction> Transpiler_HasItemStats_XUiM_ItemStack(IEnumerable<CodeInstruction> instructions)
{
var codes = instructions.ToList();
var fld_displaytype = AccessTools.Field(typeof(ItemClass), nameof(ItemClass.DisplayType));
for (int i = 0; i < codes.Count; i++)
{
if (codes[i].LoadsField(fld_displaytype))
{
codes.RemoveRange(i - 1, 2);
codes.Insert(i - 1, CodeInstruction.Call(typeof(MultiActionUtils), nameof(MultiActionUtils.GetDisplayTypeForAction), new []{ typeof(ItemValue) }));
break;
}
}
return codes;
}
[HarmonyPatch(typeof(XUiC_ItemInfoWindow), nameof(XUiC_ItemInfoWindow.HoverEntry), MethodType.Setter)]
[HarmonyTranspiler]
private static IEnumerable<CodeInstruction> Transpiler_HoverEntry_XUiC_ItemInfoWindow(IEnumerable<CodeInstruction> instructions)
{
var codes = instructions.ToList();
var mtd_cancompare = AccessTools.Method(typeof(XUiM_ItemStack), nameof(XUiM_ItemStack.CanCompare));
for (int i = 0; i < codes.Count; i++)
{
if (codes[i].Calls(mtd_cancompare))
{
codes[i] = CodeInstruction.Call(typeof(MultiActionUtils), nameof(MultiActionUtils.CanCompare));
codes[i - 1] = CodeInstruction.LoadField(typeof(XUiC_ItemInfoWindow), nameof(XUiC_ItemInfoWindow.itemStack));
codes.Insert(i, CodeInstruction.LoadField(typeof(ItemStack), nameof(ItemStack.itemValue)));
codes.RemoveAt(i - 3);
break;
}
}
return codes;
}
}
#endregion
}