Files
7d2dXG/Mods/0A-KFCommonUtilityLib/Harmony/Patches.cs
Nathaniel Cosford 062dfab2cd Patched
2025-05-30 01:04:40 +09:30

1341 lines
59 KiB
C#

using HarmonyLib;
using KFCommonUtilityLib.Scripts.StaticManagers;
using KFCommonUtilityLib.Scripts.Utilities;
using System;
using System.Collections.Generic;
using UniLinq;
using System.Reflection;
using System.Reflection.Emit;
using System.Xml.Linq;
using UnityEngine;
using KFCommonUtilityLib.Scripts.NetPackages;
using KFCommonUtilityLib;
using static ItemActionRanged;
[HarmonyPatch]
public static class CommonUtilityPatch
{
//fix reloading issue and onSelfRangedBurstShot timing
public static void FakeReload(EntityAlive holdingEntity, ItemActionRanged.ItemActionDataRanged _actionData)
{
if (!holdingEntity)
return;
_actionData.isReloading = true;
_actionData.isWeaponReloading = true;
holdingEntity.MinEventContext.ItemActionData = _actionData;
holdingEntity.FireEvent(MinEventTypes.onReloadStart, true);
_actionData.isReloading = false;
_actionData.isWeaponReloading = false;
_actionData.isReloadCancelled = false;
_actionData.isWeaponReloadCancelled = false;
holdingEntity.FireEvent(MinEventTypes.onReloadStop);
AnimationAmmoUpdateState.SetAmmoCountForEntity(holdingEntity, holdingEntity.inventory.holdingItemIdx);
}
[HarmonyPatch(typeof(ItemActionRanged), nameof(ItemActionRanged.SwapAmmoType))]
[HarmonyTranspiler]
private static IEnumerable<CodeInstruction> Transpiler_SwapAmmoType_ItemActionRanged(IEnumerable<CodeInstruction> instructions)
{
var codes = new List<CodeInstruction>(instructions);
for (int i = 0; i < codes.Count; ++i)
{
if (codes[i].opcode == OpCodes.Ret)
{
codes.InsertRange(i, new CodeInstruction[]
{
new CodeInstruction(OpCodes.Ldarg_1),
new CodeInstruction(OpCodes.Ldloc_0),
CodeInstruction.Call(typeof(CommonUtilityPatch), nameof(FakeReload))
});
break;
}
}
return codes;
}
[HarmonyPatch(typeof(ItemActionRanged), nameof(ItemActionRanged.ExecuteAction))]
[HarmonyTranspiler]
private static IEnumerable<CodeInstruction> Transpiler_ExecuteAction_ItemActionRanged(IEnumerable<CodeInstruction> instructions)
{
var codes = new List<CodeInstruction>(instructions);
var mtd_fire_event = AccessTools.Method(typeof(EntityAlive), nameof(EntityAlive.FireEvent));
var mtd_get_model_layer = AccessTools.Method(typeof(EntityAlive), nameof(EntityAlive.GetModelLayer));
var mtd_get_perc_left = AccessTools.PropertyGetter(typeof(ItemValue), nameof(ItemValue.PercentUsesLeft));
var mtd_check_ammo = AccessTools.Method(typeof(ItemActionRanged), nameof(ItemActionRanged.checkAmmo));
int take = -1, insert = -1;
for (int i = 0; i < codes.Count; ++i)
{
if (codes[i].opcode == OpCodes.Ldc_I4_S && codes[i].OperandIs((int)MinEventTypes.onSelfRangedBurstShotEnd) && codes[i + 2].Calls(mtd_fire_event))
take = i - 3;
else if (codes[i].Calls(mtd_get_model_layer))
insert = i + 2;
}
if (take < insert)
{
var list = codes.GetRange(take, 6);
codes.InsertRange(insert, list);
codes.RemoveRange(take, 6);
}
for (int i = 0; i < codes.Count; i++)
{
if (codes[i].Calls(mtd_check_ammo))
{
codes.InsertRange(i + 2, new[]
{
new CodeInstruction(OpCodes.Ldloc_0),
new CodeInstruction(OpCodes.Ldc_I4_0),
CodeInstruction.StoreField(typeof(ItemActionDataRanged), nameof(ItemActionDataRanged.state))
});
break;
}
}
return codes;
}
//fix recoil animation does not match weapon RPM
private static int weaponFireHash = Animator.StringToHash("WeaponFire");
private static int aimHash = Animator.StringToHash("IsAiming");
private static HashSet<int> hash_shot_state = new HashSet<int>();
private static HashSet<int> hash_aimshot_state = new HashSet<int>();
public static void InitShotStates()
{
string[] weapons =
{
"fpvAK47",
"fpvMagnum",
"fpvRocketLauncher",
"fpvSawedOffShotgun",
"fpvBlunderbuss",
"fpvCrossbow",
"fpvPistol",
"fpvHuntingRifle",
"fpvSMG",
"fpvSniperRifle",
"M60",
"fpvDoubleBarrelShotgun",
//"fpvJunkTurret",
"fpvTacticalAssaultRifle",
"fpvDesertEagle",
"fpvAutoShotgun",
"fpvSharpShooterRifle",
"fpvPipeMachineGun",
"fpvPipeRifle",
"fpvPipeRevolver",
"fpvPipeShotgun",
"fpvLeverActionRifle",
};
foreach (string weapon in weapons)
{
hash_shot_state.Add(Animator.StringToHash(weapon + "Fire"));
hash_aimshot_state.Add(Animator.StringToHash(weapon + "AimFire"));
}
}
[HarmonyPatch(typeof(EntityPlayerLocal), nameof(EntityPlayerLocal.OnFired))]
[HarmonyPostfix]
private static void Postfix_OnFired_EntityPlayerLocal(EntityPlayerLocal __instance)
{
if (!__instance.bFirstPersonView)
return;
ItemActionRanged.ItemActionDataRanged _rangedData;
if ((_rangedData = __instance.inventory.holdingItemData.actionData[0] as ItemActionRanged.ItemActionDataRanged) == null && (_rangedData = __instance.inventory.holdingItemData.actionData[1] as ItemActionRanged.ItemActionDataRanged) == null)
return;
if (_rangedData.invData.model.TryGetComponent<AnimationTargetsAbs>(out var targets) && targets.ItemFpv)
return;
var anim = (__instance.emodel.avatarController as AvatarLocalPlayerController).FPSArms.Animator;
if (anim.IsInTransition(0))
return;
var curState = anim.GetCurrentAnimatorStateInfo(0);
if (curState.length > _rangedData.Delay)
{
bool aimState = anim.GetBool(aimHash);
short shotState = 0;
if (hash_shot_state.Contains(curState.shortNameHash))
shotState = 1;
else if (hash_aimshot_state.Contains(curState.shortNameHash))
shotState = 2;
if (shotState == 0 || (shotState == 1 && aimState) || (shotState == 2 && !aimState))
{
if (shotState > 0)
anim.ResetTrigger(weaponFireHash);
return;
}
//current state, layer 0, offset 0
anim.PlayInFixedTime(0, 0, 0);
anim.ResetTrigger(weaponFireHash);
if (_rangedData.invData.itemValue.Meta == 0)
{
__instance.emodel.avatarController.CancelEvent(weaponFireHash);
Log.Out("Cancel fire event because meta is 0");
}
}
}
//[HarmonyPatch(typeof(ItemActionRanged), nameof(ItemActionRanged.ItemActionEffects))]
//[HarmonyPostfix]
//private static void Postfix_ItemActionEffects_ItemActionRanged(ItemActionData _actionData, int _firingState)
//{
// if (_firingState == 0 && _actionData.invData.holdingEntity is EntityPlayerLocal && !(_actionData.invData.itemValue.ItemClass.Actions[0] is ItemActionCatapult))
// {
// _actionData.invData.holdingEntity?.emodel.avatarController.CancelEvent(weaponFireHash);
// //Log.Out("Cancel fire event because firing state is 0\n" + StackTraceUtility.ExtractStackTrace());
// }
//}
//[HarmonyPatch(typeof(GameManager), "gmUpdate")]
//[HarmonyTranspiler]
//private static IEnumerable<CodeInstruction> Transpiler_gmUpdate_GameManager(IEnumerable<CodeInstruction> instructions)
//{
// var codes = new List<CodeInstruction>(instructions);
// var mtd_unload = AccessTools.Method(typeof(Resources), nameof(Resources.UnloadUnusedAssets));
// var fld_duration = AccessTools.Field(typeof(GameManager), "unloadAssetsDuration");
// for (int i = 0; i < codes.Count; ++i)
// {
// if (codes[i].opcode == OpCodes.Call && codes[i].Calls(mtd_unload))
// {
// for (int j = i; j >= 0; --j)
// {
// if (codes[j].opcode == OpCodes.Ldfld && codes[j].LoadsField(fld_duration) && codes[j + 1].opcode == OpCodes.Ldc_R4)
// codes[j + 1].operand = (float)codes[j + 1].operand / 2;
// }
// break;
// }
// }
// return codes;
//}
//internal static void ForceUpdateGC()
//{
// if (GameManager.IsDedicatedServer)
// return;
// if (GameManager.frameCount % 18000 == 0)
// {
// long rss = GetRSS.GetCurrentRSS();
// if (rss / 1024 / 1024 > 6144)
// {
// Log.Out("Memory usage exceeds threshold, now performing garbage collection...");
// GC.Collect();
// }
// }
//}
//altmode workarounds
//deprecated by action module
private static void ParseAltRequirements(XElement _node)
{
string itemName = _node.GetAttribute("name");
if (string.IsNullOrEmpty(itemName))
{
return;
}
ItemClass item = ItemClass.GetItemClass(itemName);
for (int i = 0; i < item.Actions.Length; i++)
{
if (item.Actions[i] is ItemActionAltMode _alt)
_alt.ParseAltRequirements(_node, i);
}
}
[HarmonyPatch(typeof(ItemClassesFromXml), nameof(ItemClassesFromXml.parseItem))]
[HarmonyPostfix]
private static void Postfix_parseItem_ItemClassesFromXml(XElement _node)
{
ParseAltRequirements(_node);
}
[HarmonyPatch(typeof(ItemClass), nameof(ItemClass.ExecuteAction))]
[HarmonyPrefix]
private static bool Prefix_ExecuteAction_ItemClass(ItemClass __instance, int _actionIdx, ItemInventoryData _data, bool _bReleased)
{
if (!_bReleased && __instance.Actions[_actionIdx] is ItemActionAltMode _alt)
_alt.SetAltRequirement(_data.actionData[_actionIdx]);
return true;
}
[HarmonyPatch(typeof(DynamicProperties), nameof(DynamicProperties.Parse))]
[HarmonyPrefix]
private static bool Prefix_Parse_DynamicProperties(XElement elementProperty)
{
if (elementProperty.Name.LocalName != "property")
return false;
return true;
}
//MinEventParams workarounds
[HarmonyPatch(typeof(ItemActionRanged), nameof(ItemActionRanged.fireShot))]
[HarmonyTranspiler]
private static IEnumerable<CodeInstruction> Transpiler_fireShot_ItemActionRanged(IEnumerable<CodeInstruction> instructions)
{
var codes = new List<CodeInstruction>(instructions);
var fld_ranged_tag = AccessTools.Field(typeof(ItemActionAttack), nameof(ItemActionAttack.RangedTag));
var fld_params = AccessTools.Field(typeof(EntityAlive), nameof(EntityAlive.MinEventContext));
for (int i = 0; i < codes.Count; i++)
{
if (codes[i].LoadsField(fld_ranged_tag))
{
if (!codes[i + 3].LoadsField(fld_params))
{
codes.InsertRange(i + 2, new CodeInstruction[]
{
new CodeInstruction(OpCodes.Ldloc_1),
CodeInstruction.LoadField(typeof(EntityAlive), nameof(EntityAlive.MinEventContext)),
new CodeInstruction(OpCodes.Dup),
new CodeInstruction(OpCodes.Ldloc, 10),
CodeInstruction.LoadField(typeof(WorldRayHitInfo), nameof(WorldRayHitInfo.hit)),
CodeInstruction.LoadField(typeof(HitInfoDetails), nameof(HitInfoDetails.pos)),
CodeInstruction.StoreField(typeof(MinEventParams), nameof(MinEventParams.Position)),
new CodeInstruction(OpCodes.Ldloc_1),
CodeInstruction.Call(typeof(EntityAlive), nameof(EntityAlive.GetPosition)),
CodeInstruction.StoreField(typeof(MinEventParams), nameof(MinEventParams.StartPosition))
});
}
break;
}
}
return codes;
}
[HarmonyPatch(typeof(ItemActionRanged), nameof(ItemActionRanged.OnHoldingUpdate))]
[HarmonyTranspiler]
private static IEnumerable<CodeInstruction> Transpiler_OnHoldingUpdate_ItemActionRanged(IEnumerable<CodeInstruction> instructions)
{
var mtd_release = AccessTools.Method(typeof(ItemActionRanged), nameof(ItemActionRanged.triggerReleased));
var codes = instructions.ToList();
for (int i = 0; i < codes.Count; i++)
{
if (codes[i].Calls(mtd_release))
{
codes[i + 1].labels.Clear();
codes[i + 1].MoveLabelsFrom(codes[i - 20]);
codes.RemoveRange(i - 20, 21);
break;
}
}
return codes;
}
[HarmonyPatch(typeof(ItemActionRanged), nameof(ItemActionRanged.OnHoldingUpdate))]
[HarmonyPostfix]
private static void Postfix_OnHoldingUpdate_ItemActionRanged(ItemActionRanged __instance, ItemActionData _actionData)
{
ItemActionDataRanged itemActionDataRanged = (ItemActionDataRanged)_actionData;
if (itemActionDataRanged.state != 0 && itemActionDataRanged.m_LastShotTime > 0f && Time.time > itemActionDataRanged.m_LastShotTime + itemActionDataRanged.Delay * 2f)
{
itemActionDataRanged.invData.gameManager.ItemActionEffectsServer(itemActionDataRanged.invData.holdingEntity.entityId, itemActionDataRanged.invData.slotIdx, itemActionDataRanged.indexInEntityOfAction, 0, Vector3.zero, Vector3.zero);
itemActionDataRanged.state = ItemActionFiringState.Off;
}
}
[HarmonyPatch(typeof(ItemActionRanged), nameof(ItemActionRanged.triggerReleased))]
[HarmonyTranspiler]
private static IEnumerable<CodeInstruction> Transpiler_triggerReleased_ItemActionRanged(IEnumerable<CodeInstruction> instructions)
{
var mtd_effect = AccessTools.Method(typeof(IGameManager), nameof(IGameManager.ItemActionEffectsServer));
var mtd_data = AccessTools.Method(typeof(ItemActionRanged), nameof(ItemActionRanged.getUserData));
var codes = instructions.ToList();
for (int i = 0; i < codes.Count; i++)
{
if (codes[i].Calls(mtd_effect))
{
codes.InsertRange(i, new CodeInstruction[]
{
new CodeInstruction(OpCodes.Ldarg_0),
new CodeInstruction(OpCodes.Ldarg_1),
new CodeInstruction(OpCodes.Callvirt, mtd_data)
});
codes.RemoveAt(i - 1);
break;
}
}
return codes;
}
[HarmonyPatch(typeof(GameManager), nameof(GameManager.StartGame))]
[HarmonyPrefix]
private static bool Prefix_StartGame_GameManager()
{
CustomEffectEnumManager.InitFinal();
return true;
}
[HarmonyPatch(typeof(PassiveEffect), nameof(PassiveEffect.ParsePassiveEffect))]
[HarmonyTranspiler]
private static IEnumerable<CodeInstruction> Transpiler_ParsePassiveEffect_PassiveEffect(IEnumerable<CodeInstruction> instructions)
{
return instructions.MethodReplacer(AccessTools.Method(typeof(EnumUtils), nameof(EnumUtils.Parse), new[] { typeof(string), typeof(bool) }, new[] { typeof(PassiveEffects) }),
AccessTools.Method(typeof(CustomEffectEnumManager), nameof(CustomEffectEnumManager.RegisterOrGetEnum), new[] { typeof(string), typeof(bool) }, new[] { typeof(PassiveEffects) }));
}
[HarmonyPatch(typeof(MinEventActionBase), nameof(MinEventActionBase.ParseXmlAttribute))]
[HarmonyTranspiler]
private static IEnumerable<CodeInstruction> Transpiler_ParseXmlAttribute_MinEventActionBase(IEnumerable<CodeInstruction> instructions)
{
return instructions.MethodReplacer(AccessTools.Method(typeof(EnumUtils), nameof(EnumUtils.Parse), new[] { typeof(string), typeof(bool) }, new[] { typeof(MinEventTypes) }),
AccessTools.Method(typeof(CustomEffectEnumManager), nameof(CustomEffectEnumManager.RegisterOrGetEnum), new[] { typeof(string), typeof(bool) }, new[] { typeof(MinEventTypes) }));
}
//todo: patch Quartz
[HarmonyPatch(typeof(UIDisplayInfoFromXml), nameof(UIDisplayInfoFromXml.ParseDisplayInfoEntry))]
[HarmonyTranspiler]
private static IEnumerable<CodeInstruction> Transpiler_ParseDisplayInfoEntry_UIDisplayInfoFromXml(IEnumerable<CodeInstruction> instructions)
{
return instructions.MethodReplacer(AccessTools.Method(typeof(EnumUtils), nameof(EnumUtils.Parse), new[] { typeof(string), typeof(bool) }, new[] { typeof(PassiveEffects) }),
AccessTools.Method(typeof(CustomEffectEnumManager), nameof(CustomEffectEnumManager.GetEnumOrThrow), new[] { typeof(string), typeof(bool) }, new[] { typeof(PassiveEffects) }));
}
[HarmonyPatch(typeof(ItemActionDynamicMelee), nameof(ItemActionDynamicMelee.OnHoldingUpdate))]
[HarmonyTranspiler]
private static IEnumerable<CodeInstruction> Transpiler_OnHoldingUpdate_ItemActionDynamicMelee(IEnumerable<CodeInstruction> instructions)
{
var codes = instructions.ToList();
for (int i = 0; i < codes.Count; i++)
{
if (codes[i].Is(OpCodes.Ldc_R4, 0.1f))
{
codes.RemoveRange(i, 2);
break;
}
}
return codes;
}
[HarmonyPatch(typeof(ItemActionDynamicMelee), nameof(ItemActionDynamicMelee.canStartAttack))]
[HarmonyTranspiler]
private static IEnumerable<CodeInstruction> Transpiler_canStartAttack_ItemActionDynamicMelee(IEnumerable<CodeInstruction> instructions)
{
var codes = instructions.ToList();
for (int i = 0; i < codes.Count; i++)
{
if (codes[i].Is(OpCodes.Ldc_R4, 0.1f))
{
codes.RemoveRange(i, 2);
break;
}
}
return codes;
}
/// <summary>
/// projectile direct hit damage percent
/// removed due to new explosion damage passives
/// </summary>
/// <param name="instructions"></param>
/// <returns></returns>
//[HarmonyPatch(typeof(ProjectileMoveScript), nameof(ProjectileMoveScript.checkCollision))]
//[HarmonyTranspiler]
//private static IEnumerable<CodeInstruction> Transpiler_checkCollision_ProjectileMoveScript(IEnumerable<CodeInstruction> instructions)
//{
// var codes = instructions.ToList();
// var fld_strain = AccessTools.Field(typeof(ItemActionLauncher.ItemActionDataLauncher), nameof(ItemActionLauncher.ItemActionDataLauncher.strainPercent));
// var mtd_block = AccessTools.Method(typeof(ItemActionAttack), nameof(ItemActionAttack.GetDamageBlock));
// for (int i = 0; i < codes.Count; i++)
// {
// if (codes[i].LoadsField(fld_strain))
// {
// codes.InsertRange(i + 1, new CodeInstruction[]
// {
// new CodeInstruction(OpCodes.Ldarg_0),
// CodeInstruction.LoadField(typeof(ProjectileMoveScript), nameof(ProjectileMoveScript.itemValueProjectile)),
// new CodeInstruction(OpCodes.Ldloc_S, 4),
// CodeInstruction.Call(typeof(CommonUtilityPatch), codes[i - 3].Calls(mtd_block) ? nameof(GetProjectileBlockDamagePerc) : nameof(GetProjectileEntityDamagePerc)),
// new CodeInstruction(OpCodes.Mul)
// });
// }
// }
// return codes;
//}
//public static float GetProjectileBlockDamagePerc(ItemValue _itemValue, EntityAlive _holdingEntity)
//{
// return EffectManager.GetValue(CustomEnums.ProjectileImpactDamagePercentBlock, _itemValue, 1, _holdingEntity, null);
//}
//public static float GetProjectileEntityDamagePerc(ItemValue _itemValue, EntityAlive _holdingEntity)
//{
// return EffectManager.GetValue(CustomEnums.ProjectileImpactDamagePercentEntity, _itemValue, 1, _holdingEntity, null);
//}
/// <summary>
/// force tpv crosshair
/// </summary>
/// <param name="instructions"></param>
/// <returns></returns>
[HarmonyPatch(typeof(EntityPlayerLocal), nameof(EntityPlayerLocal.guiDrawCrosshair))]
[HarmonyTranspiler]
private static IEnumerable<CodeInstruction> Transpiler_guiDrawCrosshair_EntityPlayerLocal(IEnumerable<CodeInstruction> instructions)
{
var codes = instructions.ToList();
FieldInfo fld_debug = AccessTools.Field(typeof(ItemAction), nameof(ItemAction.ShowDistanceDebugInfo));
for ( int i = 0; i < codes.Count; i++ )
{
if (codes[i].LoadsField(fld_debug))
{
var label = codes[i - 1].operand;
codes.InsertRange(i, new[]
{
new CodeInstruction(OpCodes.Ldarg_0),
CodeInstruction.LoadField(typeof(EntityPlayerLocal), nameof(EntityPlayerLocal.bFirstPersonView)),
new CodeInstruction(OpCodes.Brfalse_S, label)
});
break;
}
}
return codes;
}
/// <summary>
/// correctly apply muzzle flash silence with modifications
/// </summary>
/// <param name="_data"></param>
[HarmonyPatch(typeof(ItemActionRanged), nameof(ItemActionRanged.OnModificationsChanged))]
[HarmonyPostfix]
private static void Postfix_OnModificationsChanged_ItemActionRanged(ItemActionData _data)
{
ItemActionRanged.ItemActionDataRanged itemActionDataRanged = _data as ItemActionRanged.ItemActionDataRanged;
if (itemActionDataRanged.SoundStart.Contains("silenced"))
{
itemActionDataRanged.IsFlashSuppressed = true;
}
//should fix stuck on switching item?
itemActionDataRanged.isReloadCancelled = false;
itemActionDataRanged.isWeaponReloadCancelled = false;
itemActionDataRanged.isReloading = false;
itemActionDataRanged.isWeaponReloading = false;
itemActionDataRanged.isChangingAmmoType = false;
}
#region item tags modifier
/// <summary>
/// should handle swapping mod
/// first check if the mod to install can be installed after current mod is removed
/// then check if any other mod requires or conflicts current mod
/// </summary>
/// <param name="instructions"></param>
/// <param name="generator"></param>
/// <returns></returns>
[HarmonyPatch(typeof(XUiC_ItemPartStack), nameof(XUiC_ItemPartStack.CanSwap))]
[HarmonyTranspiler]
private static IEnumerable<CodeInstruction> Transpiler_CanSwap_XUiC_ItemPartStack(IEnumerable<CodeInstruction> instructions, ILGenerator generator)
{
var codes = instructions.ToList();
LocalBuilder lbd_tags_if_remove_prev = generator.DeclareLocal(typeof(FastTags<TagGroup.Global>));
LocalBuilder lbd_tags_if_install_new = generator.DeclareLocal(typeof(FastTags<TagGroup.Global>));
MethodInfo mtd_get_item_class = AccessTools.PropertyGetter(typeof(ItemValue), nameof(ItemValue.ItemClass));
MethodInfo mtd_has_any_tags = AccessTools.Method(typeof(ItemClass), nameof(ItemClass.HasAnyTags));
MethodInfo mtd_test_any_set = AccessTools.Method(typeof(FastTags<TagGroup.Global>), nameof(FastTags<TagGroup.Global>.Test_AnySet));
FieldInfo fld_mod = AccessTools.Field(typeof(ItemValue), nameof(ItemValue.Modifications));
FieldInfo fld_installable_tags = AccessTools.Field(typeof(ItemClassModifier), nameof(ItemClassModifier.InstallableTags));
for (int i = 3; i < codes.Count; i++)
{
//get current tags
if (codes[i].opcode == OpCodes.Stloc_2)
{
codes.InsertRange(i + 1, new[]
{
new CodeInstruction(OpCodes.Ldloc_1),
new CodeInstruction(OpCodes.Ldarg_0),
CodeInstruction.LoadField(typeof(XUiC_ItemPartStack), "itemValue"),
CodeInstruction.Call(typeof(LocalItemTagsManager), nameof(LocalItemTagsManager.GetTagsAsIfNotInstalled)),
new CodeInstruction(OpCodes.Stloc_S, lbd_tags_if_remove_prev),
new CodeInstruction(OpCodes.Ldloc_1),
new CodeInstruction(OpCodes.Ldloc_0),
CodeInstruction.Call(typeof(LocalItemTagsManager), nameof(LocalItemTagsManager.GetTagsAsIfInstalled)),
new CodeInstruction(OpCodes.Stloc_S, lbd_tags_if_install_new)
});
i += 10;
Log.Out("mod 1!!!");
}
//replace checking tags
else if (codes[i].Calls(mtd_has_any_tags) && codes[i - 3].opcode == OpCodes.Ldloc_2)
{
if (codes[i - 1].LoadsField(fld_installable_tags) && (codes[i + 1].opcode == OpCodes.Brtrue || codes[i + 1].opcode == OpCodes.Brtrue_S))
{
var lbl_prev = codes[i + 4].ExtractLabels();
var lbl_jump = generator.DefineLabel();
codes[i + 4].WithLabels(lbl_jump);
codes.InsertRange(i + 4, new[]
{
new CodeInstruction(OpCodes.Ldloc_1).WithLabels(lbl_prev),
new CodeInstruction(OpCodes.Ldarg_0),
CodeInstruction.LoadField(typeof(XUiC_ItemPartStack), "itemValue"),
new CodeInstruction(OpCodes.Ldloc_0),
CodeInstruction.Call(typeof(LocalItemTagsManager), nameof(LocalItemTagsManager.CanSwapMod)),
new CodeInstruction(OpCodes.Brtrue, lbl_jump),
new CodeInstruction(OpCodes.Ldc_I4_0),
new CodeInstruction(OpCodes.Ret)
});
}
codes[i - 3].opcode = OpCodes.Ldloca_S;
codes[i - 3].operand = lbd_tags_if_remove_prev;
codes[i].opcode = OpCodes.Call;
codes[i].operand = mtd_test_any_set;
Log.Out("mod 2!!!");
}
}
return codes;
}
[HarmonyPatch(typeof(XUiC_ItemCosmeticStack), nameof(XUiC_ItemCosmeticStack.CanSwap))]
[HarmonyTranspiler]
private static IEnumerable<CodeInstruction> Transpiler_CanSwap_XUiC_ItemCosmeticStack(IEnumerable<CodeInstruction> instructions, ILGenerator generator)
{
var codes = instructions.ToList();
LocalBuilder lbd_tags_if_remove_prev = generator.DeclareLocal(typeof(FastTags<TagGroup.Global>));
LocalBuilder lbd_tags_if_install_new = generator.DeclareLocal(typeof(FastTags<TagGroup.Global>));
LocalBuilder lbd_item_being_assembled = generator.DeclareLocal(typeof(ItemValue));
MethodInfo mtd_get_item_class = AccessTools.PropertyGetter(typeof(ItemValue), nameof(ItemValue.ItemClass));
MethodInfo mtd_has_any_tags = AccessTools.Method(typeof(ItemClass), nameof(ItemClass.HasAnyTags));
MethodInfo mtd_test_any_set = AccessTools.Method(typeof(FastTags<TagGroup.Global>), nameof(FastTags<TagGroup.Global>.Test_AnySet));
MethodInfo mtd_get_xui = AccessTools.PropertyGetter(typeof(XUiController), nameof(XUiController.xui));
MethodInfo mtd_get_cur_item = AccessTools.PropertyGetter(typeof(XUiM_AssembleItem), nameof(XUiM_AssembleItem.CurrentItem));
FieldInfo fld_cos = AccessTools.Field(typeof(ItemValue), nameof(ItemValue.CosmeticMods));
FieldInfo fld_installable_tags = AccessTools.Field(typeof(ItemClassModifier), nameof(ItemClassModifier.InstallableTags));
for (int i = 3; i < codes.Count; i++)
{
//get current tags
if ((codes[i].opcode == OpCodes.Brtrue || codes[i].opcode == OpCodes.Brtrue_S) && codes[i - 1].opcode == OpCodes.Ldloc_0)
{
codes.InsertRange(i + 3, new[]
{
new CodeInstruction(OpCodes.Ldarg_0).MoveLabelsFrom(codes[i + 3]),
new CodeInstruction(OpCodes.Call, mtd_get_xui),
CodeInstruction.LoadField(typeof(XUi), nameof(XUi.AssembleItem)),
new CodeInstruction(OpCodes.Callvirt, mtd_get_cur_item),
CodeInstruction.LoadField(typeof(ItemStack), nameof(ItemStack.itemValue)),
new CodeInstruction(OpCodes.Stloc_S, lbd_item_being_assembled),
new CodeInstruction(OpCodes.Ldloc_S, lbd_item_being_assembled),
new CodeInstruction(OpCodes.Ldarg_0),
CodeInstruction.LoadField(typeof(XUiC_ItemCosmeticStack), "itemValue"),
CodeInstruction.Call(typeof(LocalItemTagsManager), nameof(LocalItemTagsManager.GetTagsAsIfNotInstalled)),
new CodeInstruction(OpCodes.Stloc_S, lbd_tags_if_remove_prev),
new CodeInstruction(OpCodes.Ldloc_S, lbd_item_being_assembled),
new CodeInstruction(OpCodes.Ldloc_0),
CodeInstruction.Call(typeof(LocalItemTagsManager), nameof(LocalItemTagsManager.GetTagsAsIfInstalled)),
new CodeInstruction(OpCodes.Stloc_S, lbd_tags_if_install_new)
});
i += 18;
Log.Out("cos 1!!!");
}
//replace checking tags
else if (codes[i].Calls(mtd_has_any_tags) && codes[i - 3].Calls(mtd_get_item_class))
{
if (codes[i - 1].LoadsField(fld_installable_tags) && (codes[i + 1].opcode == OpCodes.Brtrue || codes[i + 1].opcode == OpCodes.Brtrue_S))
{
var lbl_prev = codes[i + 4].ExtractLabels();
var lbl_jump = generator.DefineLabel();
codes[i + 4].WithLabels(lbl_jump);
codes.InsertRange(i + 4, new[]
{
new CodeInstruction(OpCodes.Ldloc_S, lbd_item_being_assembled).WithLabels(lbl_prev),
new CodeInstruction(OpCodes.Ldarg_0),
CodeInstruction.LoadField(typeof(XUiC_ItemPartStack), "itemValue"),
new CodeInstruction(OpCodes.Ldloc_0),
CodeInstruction.Call(typeof(LocalItemTagsManager), nameof(LocalItemTagsManager.CanSwapMod)),
new CodeInstruction(OpCodes.Brtrue, lbl_jump),
new CodeInstruction(OpCodes.Ldc_I4_0),
new CodeInstruction(OpCodes.Ret)
});
}
codes[i - 8].MoveLabelsTo(codes[i - 3]);
codes[i - 3].opcode = OpCodes.Ldloca_S;
codes[i - 3].operand = lbd_tags_if_remove_prev;
codes[i].opcode = OpCodes.Call;
codes[i].operand = mtd_test_any_set;
codes.RemoveRange(i - 8, 5);
i -= 5;
Log.Out("cos 2!!!");
}
}
return codes;
}
/// <summary>
/// check if other mods relies on this one
/// </summary>
/// <param name="__result"></param>
/// <param name="__instance"></param>
/// <param name="___itemValue"></param>
[HarmonyPatch(typeof(XUiC_ItemPartStack), "CanRemove")]
[HarmonyPostfix]
private static void Postfix_CanRemove_XUiC_ItemPartStack(ref bool __result, XUiC_ItemPartStack __instance)
{
if (__result && __instance.xui?.AssembleItem?.CurrentItem?.itemValue is ItemValue itemValue)
{
ItemClass itemClass = itemValue.ItemClass;
FastTags<TagGroup.Global> tagsAfterRemove = LocalItemTagsManager.GetTagsAsIfNotInstalled(itemValue, __instance.itemValue);
if (tagsAfterRemove.IsEmpty)
{
__result = false;
return;
}
foreach (var mod in itemValue.Modifications)
{
if (mod.IsEmpty())
continue;
ItemClassModifier modClass = mod.ItemClass as ItemClassModifier;
if (modClass == null || !tagsAfterRemove.Test_AnySet(modClass.InstallableTags) || tagsAfterRemove.Test_AnySet(modClass.DisallowedTags))
{
__result = false;
return;
}
}
}
}
[HarmonyPatch(typeof(XUiC_ItemCosmeticStack), "CanRemove")]
[HarmonyPostfix]
private static void Postfix_CanRemove_XUiC_ItemCosmeticStack(ref bool __result, XUiC_ItemCosmeticStack __instance)
{
if (__result && __instance.xui?.AssembleItem?.CurrentItem?.itemValue is ItemValue itemValue)
{
ItemClass itemClass = itemValue.ItemClass;
FastTags<TagGroup.Global> tagsAfterRemove = LocalItemTagsManager.GetTagsAsIfNotInstalled(itemValue, __instance.itemValue);
if (tagsAfterRemove.IsEmpty)
{
__result = false;
return;
}
foreach (var mod in itemValue.CosmeticMods)
{
if (mod.IsEmpty())
continue;
ItemClassModifier modClass = mod.ItemClass as ItemClassModifier;
if (modClass == null || !tagsAfterRemove.Test_AnySet(modClass.InstallableTags) || tagsAfterRemove.Test_AnySet(modClass.DisallowedTags))
{
__result = false;
return;
}
}
}
}
/// <summary>
/// should update the gear icon?
/// </summary>
/// <param name="instructions"></param>
/// <param name="generator"></param>
/// <returns></returns>
[HarmonyPatch(typeof(XUiC_ItemStack), nameof(XUiC_ItemStack.updateLockTypeIcon))]
[HarmonyTranspiler]
private static IEnumerable<CodeInstruction> Transpiler_updateLockTypeIcon_XUiC_ItemStack(IEnumerable<CodeInstruction> instructions, ILGenerator generator)
{
var codes = instructions.ToList();
LocalBuilder lbd_tags = generator.DeclareLocal(typeof(FastTags<TagGroup.Global>));
MethodInfo mtd_has_any_tags = AccessTools.Method(typeof(ItemClass), nameof(ItemClass.HasAnyTags));
MethodInfo mtd_test_any_set = AccessTools.Method(typeof(FastTags<TagGroup.Global>), nameof(FastTags<TagGroup.Global>.Test_AnySet));
MethodInfo mtd_get_item_class = AccessTools.PropertyGetter(typeof(ItemValue), nameof(ItemValue.ItemClass));
MethodInfo mtd_get_cur_item = AccessTools.PropertyGetter(typeof(XUiM_AssembleItem), nameof(XUiM_AssembleItem.CurrentItem));
MethodInfo mtd_get_xui = AccessTools.PropertyGetter(typeof(XUiController), nameof(XUiController.xui));
for (int i = 3; i < codes.Count; i++)
{
//get current tags
if ((codes[i].opcode == OpCodes.Brfalse_S || codes[i].opcode == OpCodes.Brfalse) && codes[i - 1].Calls(mtd_get_cur_item))
{
codes.InsertRange(i + 1, new[]
{
new CodeInstruction(OpCodes.Ldarg_0),
new CodeInstruction(OpCodes.Call, mtd_get_xui),
CodeInstruction.LoadField(typeof(XUi), nameof(XUi.AssembleItem)),
new CodeInstruction(OpCodes.Callvirt, mtd_get_cur_item),
CodeInstruction.LoadField(typeof(ItemStack), nameof(ItemStack.itemValue)),
CodeInstruction.Call(typeof(LocalItemTagsManager), nameof(LocalItemTagsManager.GetTags)),
new CodeInstruction(OpCodes.Stloc_S, lbd_tags)
});
i += 7;
}
//do not touch check on the modification item
else if (codes[i].Calls(mtd_has_any_tags) && codes[i - 3].Calls(mtd_get_item_class))
{
codes[i].opcode = OpCodes.Call;
codes[i].operand = mtd_test_any_set;
var insert = new CodeInstruction(OpCodes.Ldloca_S, lbd_tags);
codes[i - 8].MoveLabelsTo(insert);
codes.RemoveRange(i - 8, 6);
codes.Insert(i - 8, insert);
i -= 5;
}
}
return codes;
}
/// <summary>
/// when installing new mod, use modified tags to check for compatibility
/// </summary>
/// <param name="instructions"></param>
/// <param name="generator"></param>
/// <returns></returns>
[HarmonyPatch(typeof(XUiM_AssembleItem), nameof(XUiM_AssembleItem.AddPartToItem))]
[HarmonyTranspiler]
private static IEnumerable<CodeInstruction> Transpiler_AddPartToItem_XUiM_AssembleItem(IEnumerable<CodeInstruction> instructions, ILGenerator generator)
{
var codes = instructions.ToList();
LocalBuilder lbd_tags_cur = generator.DeclareLocal(typeof(FastTags<TagGroup.Global>));
LocalBuilder lbd_tags_after_install = generator.DeclareLocal(typeof(FastTags<TagGroup.Global>));
MethodInfo mtd_has_any_tags = AccessTools.Method(typeof(ItemClass), nameof(ItemClass.HasAnyTags));
MethodInfo mtd_test_any_set = AccessTools.Method(typeof(FastTags<TagGroup.Global>), nameof(FastTags<TagGroup.Global>.Test_AnySet));
MethodInfo mtd_get_item_class = AccessTools.PropertyGetter(typeof(ItemValue), nameof(ItemValue.ItemClass));
MethodInfo mtd_get_cur_item = AccessTools.PropertyGetter(typeof(XUiM_AssembleItem), nameof(XUiM_AssembleItem.CurrentItem));
MethodInfo mtd_is_empty = AccessTools.Method(typeof(ItemValue), nameof(ItemValue.IsEmpty));
FieldInfo fld_cos = AccessTools.Field(typeof(ItemValue), nameof(ItemValue.CosmeticMods));
FieldInfo fld_mod = AccessTools.Field(typeof(ItemValue), nameof(ItemValue.Modifications));
FieldInfo fld_installable_tags = AccessTools.Field(typeof(ItemClassModifier), nameof(ItemClassModifier.InstallableTags));
for (int i = 3; i < codes.Count; i++)
{
//get current tags
if (codes[i].opcode == OpCodes.Stloc_0)
{
codes.InsertRange(i + 1, new[]
{
new CodeInstruction(OpCodes.Ldarg_0),
new CodeInstruction(OpCodes.Call, mtd_get_cur_item),
CodeInstruction.LoadField(typeof(ItemStack), nameof(ItemStack.itemValue)),
new CodeInstruction(OpCodes.Dup),
CodeInstruction.Call(typeof(LocalItemTagsManager), nameof(LocalItemTagsManager.GetTags)),
new CodeInstruction(OpCodes.Stloc_S, lbd_tags_cur),
new CodeInstruction(OpCodes.Ldloc_0),
CodeInstruction.Call(typeof(LocalItemTagsManager), nameof(LocalItemTagsManager.GetTagsAsIfInstalled)),
new CodeInstruction(OpCodes.Stloc_S, lbd_tags_after_install)
});
i += 9;
}
//do not touch check on the modification item, check if current mod can be installed
else if (codes[i].Calls(mtd_has_any_tags) && codes[i - 3].Calls(mtd_get_item_class))
{
if (codes[i - 1].LoadsField(fld_installable_tags))
{
codes.InsertRange(i + 2, new[]
{
new CodeInstruction(OpCodes.Ldarg_0),
new CodeInstruction(OpCodes.Call, mtd_get_cur_item),
CodeInstruction.LoadField(typeof(ItemStack), nameof(ItemStack.itemValue)),
new CodeInstruction(OpCodes.Ldloc_0),
CodeInstruction.Call(typeof(LocalItemTagsManager), nameof(LocalItemTagsManager.CanInstallMod)),
new CodeInstruction(OpCodes.Brfalse, codes[i + 1].operand)
});
}
codes[i].opcode = OpCodes.Call;
codes[i].operand = mtd_test_any_set;
var insert = new CodeInstruction(OpCodes.Ldloca_S, lbd_tags_cur);
codes[i - 6].MoveLabelsTo(insert);
codes.RemoveRange(i - 6, 4);
codes.Insert(i - 6, insert);
i -= 3;
}
}
return codes;
}
#endregion
//change when aiming events are fired
[HarmonyPatch(typeof(EntityPlayerLocal), nameof(EntityPlayerLocal.SetMoveState))]
[HarmonyTranspiler]
private static IEnumerable<CodeInstruction> Transpiler_SetMoveState_EntityPlayerLocal(IEnumerable<CodeInstruction> instructions)
{
var codes = instructions.ToList();
FieldInfo fld_msa = AccessTools.Field(typeof(EntityPlayerLocal), nameof(EntityPlayerLocal.moveStateAiming));
for (int i = 0; i < codes.Count - 2; i++)
{
if (codes[i].LoadsField(fld_msa) && codes[i + 2].opcode == OpCodes.Ldloc_1)
{
codes[i - 2].MoveLabelsTo(codes[i + 13]);
codes.RemoveRange(i - 2, 15);
break;
}
}
return codes;
}
[HarmonyPatch(typeof(EntityAlive), nameof(EntityAlive.AimingGun), MethodType.Setter)]
[HarmonyPrefix]
private static bool Prefix_AimingGun_EntityAlive(bool value, EntityAlive __instance)
{
if (__instance is EntityPlayerLocal && __instance.inventory != null)
{
bool isAimingGun = __instance.AimingGun;
if (value != isAimingGun)
{
__instance.FireEvent(value ? MinEventTypes.onSelfAimingGunStart : MinEventTypes.onSelfAimingGunStop, true);
#if DEBUG
Log.Out(value ? "START AIMING GUN FIRED" : "STOP AIMING GUN FIRED");
#endif
}
}
return true;
}
[HarmonyPatch(typeof(ItemActionAttack), nameof(ItemActionAttack.HasRadial))]
[HarmonyPostfix]
private static void Postfix_HasRadial_ItemActionAttack(ref bool __result)
{
EntityPlayerLocal player = GameManager.Instance.World?.GetPrimaryPlayer();
int index = MultiActionManager.GetActionIndexForEntity(player);
List<ItemActionData> actionDatas = player.inventory?.holdingItemData?.actionData;
if (actionDatas != null && actionDatas.Count > index && actionDatas[index] is ItemActionRanged.ItemActionDataRanged rangedData && (rangedData.isReloading || rangedData.isWeaponReloading))
{
__result = false;
}
}
[HarmonyPatch(typeof(ItemActionAttack), nameof(ItemActionAttack.SetupRadial))]
[HarmonyTranspiler]
private static IEnumerable<CodeInstruction> Transpiler_SetupRadial_ItemActionAttack(IEnumerable<CodeInstruction> instructions, ILGenerator generator)
{
var codes = instructions.ToList();
var fld_usable = AccessTools.Field(typeof(ItemClass), nameof(ItemClass.UsableUnderwater));
var lbd_states = generator.DeclareLocal(typeof(bool[]));
for (int i = 0; i < codes.Count; i++)
{
if (codes[i].opcode == OpCodes.Stloc_0)
{
codes.InsertRange(i + 1, new[]
{
new CodeInstruction(OpCodes.Ldloc_0),
new CodeInstruction(OpCodes.Ldarg_2),
new CodeInstruction(OpCodes.Ldc_I4_M1),
CodeInstruction.Call(typeof(CommonUtilityPatch), nameof(CommonUtilityPatch.GetUnusableItemEntries)),
new CodeInstruction(OpCodes.Stloc_S, lbd_states)
});
i += 4;
}
else if (codes[i].LoadsField(fld_usable))
{
codes.InsertRange(i + 2, new[]
{
new CodeInstruction(OpCodes.Ldloc_S, lbd_states).WithLabels(codes[i + 2].ExtractLabels()),
new CodeInstruction(OpCodes.Ldloc_2),
CodeInstruction.Call(typeof(CommonUtilityPatch), nameof(CommonUtilityPatch.IsAmmoDisabled)),
new CodeInstruction(OpCodes.Brtrue_S, codes[i + 1].operand)
});
break;
}
}
return codes;
}
[HarmonyPatch(typeof(ItemAction), nameof(ItemAction.StartHolding))]
[HarmonyPostfix]
private static void Postfix_StartHolding_ItemAction(ItemActionData _data, ItemAction __instance)
{
if (__instance is ItemActionAttack itemActionAttack && _data.invData.holdingEntity is EntityPlayerLocal player)
{
var arr_disabled_ammo = GetUnusableItemEntries(itemActionAttack.MagazineItemNames, player, _data.indexInEntityOfAction);
if (arr_disabled_ammo == null)
{
return;
}
var itemValue = _data.invData.itemValue;
int cur_index = itemValue.GetSelectedAmmoIndexByActionIndex(_data.indexInEntityOfAction);
if (arr_disabled_ammo[cur_index])
{
int first_enabled_index = Mathf.Max(Array.IndexOf(arr_disabled_ammo, false), 0);
var mapping = MultiActionManager.GetMappingForEntity(player.entityId);
if (mapping != null)
{
if (_data.indexInEntityOfAction == mapping.CurMetaIndex)
{
itemValue.SelectedAmmoTypeIndex = (byte)first_enabled_index;
}
else
{
itemValue.SetMetadata(MultiActionUtils.ActionSelectedAmmoNames[mapping.indices.GetMetaIndexForActionIndex(_data.indexInEntityOfAction)], first_enabled_index, TypedMetadataValue.TypeTag.Integer);
}
_data.invData.holdingEntity.inventory.CallOnToolbeltChangedInternal();
}
else
{
itemValue.SelectedAmmoTypeIndex = (byte)(first_enabled_index);
}
}
}
}
public static bool[] GetUnusableItemEntries(string[] ammoNames, EntityPlayerLocal player, int actionIndex = -1)
{
if (ammoNames == null)
{
return null;
}
if (actionIndex < 0)
{
actionIndex = MultiActionManager.GetActionIndexForEntity(player);
}
string str_disabled_ammo_names = player.inventory.holdingItemItemValue.GetPropertyOverrideForAction("DisableAmmo", "", actionIndex);
//Log.Out($"checking disabled ammo: {str_disabled_ammo_names}\n{StackTraceUtility.ExtractStackTrace()}");
bool[] arr_disable_states = new bool[ammoNames.Length];
if(!string.IsNullOrEmpty(str_disabled_ammo_names))
{
string[] arr_disabled_ammo_names = str_disabled_ammo_names.Split(',', StringSplitOptions.RemoveEmptyEntries);
foreach (var name in arr_disabled_ammo_names)
{
int index = Array.IndexOf(ammoNames, name.Trim());
if (index >= 0)
{
arr_disable_states[index] = true;
if (ConsoleCmdReloadLog.LogInfo)
Log.Out($"ammo {ammoNames[index]} is disabled");
}
}
}
return arr_disable_states;
}
private static bool IsAmmoDisabled(bool[] ammoStates, int index)
{
if (ammoStates == null || ammoStates.Length <= index)
{
return false;
}
return ammoStates[index];
}
//dont spread onSelfItemActivate/onSelfItemDeactivate to attachments
//handle start holding
[HarmonyPatch(typeof(Inventory), nameof(Inventory.syncHeldItem))]
[HarmonyTranspiler]
private static IEnumerable<CodeInstruction> Transpiler_syncHeldItem_Inventory(IEnumerable<CodeInstruction> instructions)
{
var codes = instructions.ToList();
var prop_itemvalue = AccessTools.PropertyGetter(typeof(Inventory), nameof(Inventory.holdingItemItemValue));
var mtd_fireevent = AccessTools.Method(typeof(ItemValue), nameof(ItemValue.FireEvent));
for (int i = 0; i < codes.Count; i++)
{
if (codes[i].Calls(mtd_fireevent) && codes[i - 5].Calls(prop_itemvalue))
{
codes[i] = CodeInstruction.Call(typeof(MinEffectController), nameof(MinEffectController.FireEvent));
codes.InsertRange(i - 4, new[]
{
CodeInstruction.Call(typeof(ItemValue), "get_ItemClass"),
CodeInstruction.LoadField(typeof(ItemClass), nameof(ItemClass.Effects))
});
i += 2;
}
}
return codes;
}
//handle radial activation
[HarmonyPatch(typeof(XUiC_Radial), nameof(XUiC_Radial.handleActivatableItemCommand))]
[HarmonyTranspiler]
private static IEnumerable<CodeInstruction> Transpiler_handleActivatableItemCommand_XUiC_Radial(IEnumerable<CodeInstruction> instructions)
{
var codes = instructions.ToList();
var mtd_fireevent = AccessTools.Method(typeof(ItemValue), nameof(ItemValue.FireEvent));
for (int i = 0; i < codes.Count; i++)
{
if (codes[i].Calls(mtd_fireevent))
{
codes[i] = CodeInstruction.Call(typeof(MinEffectController), nameof(MinEffectController.FireEvent));
codes.InsertRange(i - 2, new[]
{
CodeInstruction.Call(typeof(ItemValue), "get_ItemClass"),
CodeInstruction.LoadField(typeof(ItemClass), nameof(ItemClass.Effects))
});
i += 2;
}
}
return codes;
}
//handle equipments
[HarmonyPatch(typeof(Equipment), nameof(Equipment.SetSlotItem))]
[HarmonyTranspiler]
private static IEnumerable<CodeInstruction> Transpiler_SetSlotItem_Equipment(IEnumerable<CodeInstruction> instructions)
{
var codes = instructions.ToList();
var mtd_fireevent = AccessTools.Method(typeof(ItemValue), nameof(ItemValue.FireEvent));
for (int i = 0; i < codes.Count; i++)
{
if (codes[i].Calls(mtd_fireevent) && codes[i - 5].opcode == OpCodes.Ldloc_0 && codes[i - 4].OperandIs((int)MinEventTypes.onSelfItemDeactivate))
{
codes[i] = CodeInstruction.Call(typeof(MinEffectController), nameof(MinEffectController.FireEvent));
codes.InsertRange(i - 4, new[]
{
CodeInstruction.Call(typeof(ItemValue), "get_ItemClass"),
CodeInstruction.LoadField(typeof(ItemClass), nameof(ItemClass.Effects))
});
i += 2;
}
}
return codes;
}
[HarmonyPatch(typeof(GameManager), nameof(GameManager.DropContentOfLootContainerServer))]
[HarmonyTranspiler]
private static IEnumerable<CodeInstruction> Transpiler_DropContentOfLootContainerServer_GameManager(IEnumerable<CodeInstruction> instructions)
{
var codes = instructions.ToList();
var setter_localscale = AccessTools.PropertySetter(typeof(Transform), nameof(Transform.localScale));
for (var i = 0; i < codes.Count; i++)
{
if (codes[i].Calls(setter_localscale))
{
codes.RemoveRange(i - 6, 7);
break;
}
}
return codes;
}
[HarmonyPatch(typeof(ItemActionRanged), nameof(ItemActionRanged.onHoldingEntityFired))]
[HarmonyTranspiler]
private static IEnumerable<CodeInstruction> Transpiler_onHoldingEntityFired_ItemActionRanged(IEnumerable<CodeInstruction> instructions)
{
var codes = instructions.ToList();
for (int i = 0; i < codes.Count; i++)
{
if (codes[i].opcode == OpCodes.Ldc_R4 && codes[i].operand is 5f)
{
codes.RemoveAt(i);
codes.InsertRange(i, new[]
{
new CodeInstruction(OpCodes.Ldarg_1),
CodeInstruction.Call(typeof(CommonUtilityPatch), nameof(CommonUtilityPatch.GetMaxSpread))
});
break;
}
}
return codes;
}
private static float GetMaxSpread(ItemActionData _data)
{
return EffectManager.GetValue(CustomEnums.MaxWeaponSpread, _data.invData.itemValue, 5f, _data.invData.holdingEntity);
}
[HarmonyPatch(typeof(NetEntityDistributionEntry), nameof(NetEntityDistributionEntry.getSpawnPacket))]
[HarmonyPrefix]
private static bool Prefix_getSpawnPacket_NetEntityDistributionEntry(NetEntityDistributionEntry __instance, ref NetPackage __result)
{
if (__instance.trackedEntity is EntityAlive ea)
{
__result = NetPackageManager.GetPackage<NetPackageEntitySpawnWithCVar>().Setup(new EntityCreationData(__instance.trackedEntity, true), (EntityAlive)__instance.trackedEntity);
return false;
}
return true;
}
[HarmonyPatch(typeof(World), nameof(World.SpawnEntityInWorld))]
[HarmonyPostfix]
private static void Postfix_SpawnEntityInWorld_World(Entity _entity)
{
if (_entity is EntityAlive ea && !ea.isEntityRemote)
{
ea.FireEvent(CustomEnums.onSelfFirstCVarSync);
}
}
[HarmonyPatch(typeof(ItemActionRanged), nameof(ItemActionRanged.ItemActionEffects))]
[HarmonyTranspiler]
private static IEnumerable<CodeInstruction> Transpiler_ItemActionEffects_ItemActionRanged(IEnumerable<CodeInstruction> instructions)
{
var codes = instructions.ToList();
for (int i = 0; i < codes.Count - 2; i++)
{
if (codes[i].opcode == OpCodes.Ldloc_S && ((LocalBuilder)codes[i].operand).LocalIndex == 9 && codes[i + 2].Branches(out _))
{
codes.InsertRange(i + 3, new[]
{
new CodeInstruction(OpCodes.Ldloc_S, codes[i].operand).WithLabels(codes[i + 3].ExtractLabels()),
CodeInstruction.Call(typeof(CommonUtilityPatch), nameof(AddTmpMuzzleFlash)),
});
i += 5;
}
else if (codes[i].opcode == OpCodes.Ldloc_S && ((LocalBuilder)codes[i].operand).LocalIndex == 12 && codes[i + 2].Branches(out _))
{
codes.InsertRange(i + 3, new[]
{
new CodeInstruction(OpCodes.Ldloc_S, codes[i].operand).WithLabels(codes[i + 3].ExtractLabels()),
CodeInstruction.Call(typeof(CommonUtilityPatch), nameof(AddTmpMuzzleFlash)),
});
i += 5;
}
}
return codes;
}
private static void AddTmpMuzzleFlash(Transform trans)
{
if (trans.TryGetComponent<TemporaryObject>(out var tmp))
{
tmp.StopAllCoroutines();
Component.Destroy(tmp);
}
tmp = trans.AddMissingComponent<TemporaryMuzzleFlash>();
tmp.life = 5f;
}
[HarmonyPatch(typeof(vp_FPCamera), nameof(vp_FPCamera.UpdateShakes))]
[HarmonyTranspiler]
private static IEnumerable<CodeInstruction> Transpiler_UpdateShakes_vp_FPCamera(IEnumerable<CodeInstruction> instructions)
{
var codes = instructions.ToList();
var fld_shake = AccessTools.Field(typeof(vp_FPCamera), nameof(vp_FPCamera.m_Shake));
for (int i = 0; i < codes.Count; i++)
{
if (codes[i].StoresField(fld_shake))
{
codes.InsertRange(i + 1, new[]
{
new CodeInstruction(OpCodes.Ldarg_0),
CodeInstruction.Call(typeof(CommonUtilityPatch), nameof(CheckShakeNaN))
});
break;
}
}
return codes;
}
private static void CheckShakeNaN(vp_FPCamera fpcamera)
{
if (float.IsNaN(fpcamera.m_Shake.x) || float.IsNaN(fpcamera.m_Shake.y) || float.IsNaN(fpcamera.m_Shake.z))
{
Log.Warning("Shake1 NaN {0}, time {1}, speed {2}, amp {3}", new object[]
{
fpcamera.m_Shake,
Time.time,
fpcamera.ShakeSpeed,
fpcamera.ShakeAmplitude
});
fpcamera.ShakeSpeed = 0f;
fpcamera.m_Shake = Vector3.zero;
fpcamera.m_Pitch += -1f;
}
}
//[HarmonyPatch(typeof(EntityBuffs), nameof(EntityBuffs.AddBuff), typeof(string), typeof(Vector3i), typeof(int), typeof(bool), typeof(bool), typeof(float))]
//[HarmonyPostfix]
//private static void Postfix_AddBuff_EntityBuffs(string _name, EntityBuffs __instance, EntityBuffs.BuffStatus __result, bool _netSync)
//{
// if (_name.StartsWith("eftZombieRandomArmor") || _name.StartsWith("eftZombieArmor"))
// Log.Out($"AddBuff [{_name}] on entity {__instance.parent.GetDebugName()} should sync {_netSync} result {__result.ToStringCached()}\n{StackTraceUtility.ExtractStackTrace()}");
//}
//[HarmonyPatch(typeof(EntityBuffs), nameof(EntityBuffs.RemoveBuff))]
//[HarmonyPostfix]
//private static void Postfix_RemoveBuff_EntityBuffs(string _name, EntityBuffs __instance, bool _netSync)
//{
// if (_name.StartsWith("eftZombieRandomArmor") || _name.StartsWith("eftZombieArmor"))
// Log.Out($"RemoveBuff [{_name}] on entity {__instance.parent.GetDebugName()} should sync {_netSync}\n{StackTraceUtility.ExtractStackTrace()}");
//}
//[HarmonyPatch(typeof(Inventory), nameof(Inventory.Execute))]
//[HarmonyPrefix]
//private static void Prefix_Execute_Inventory(Inventory __instance, int _actionIdx, bool _bReleased, PlayerActionsLocal _playerActions = null)
//{
// Log.Out($"Execute Inventory holding item {__instance.holdingItem.Name} slot {__instance.holdingItemIdx} action index {_actionIdx} released {_bReleased} is holster delay {__instance.IsHolsterDelayActive()} is unholster delay {__instance.IsUnholsterDelayActive()}");
//}
//[HarmonyPatch(typeof(Inventory), nameof(Inventory.updateHoldingItem))]
//[HarmonyTranspiler]
//private static IEnumerable<CodeInstruction> Transpiler_updateHoldingItem_Inventory(IEnumerable<CodeInstruction> instructions)
//{
// var codes = instructions.ToList();
// var mtd_setholdingtrans = AccessTools.Method(typeof(Inventory), nameof(Inventory.setHoldingItemTransfrom));
// var mtd_showrighthand = AccessTools.Method(typeof(Inventory), nameof(Inventory.ShowRightHand));
// int insert = -1, take = -1;
// for (int i = 0; i < codes.Count; i++)
// {
// if (codes[i].Calls(mtd_showrighthand))
// {
// insert = i + 1;
// }
// else if (codes[i].Calls(mtd_setholdingtrans))
// {
// take = i - 6;
// }
// }
// if (take > insert)
// {
// var list_take = codes.GetRange(take, 7);
// codes.RemoveRange(take, 7);
// codes.InsertRange(insert, list_take);
// }
// return codes;
//}
//private static bool exported = false;
//[HarmonyPatch(typeof(EModelSDCS), nameof(EModelSDCS.createModel))]
//[HarmonyPostfix]
//private static void Postfix_test(EModelSDCS __instance)
//{
// if (!exported)
// {
// exported = true;
// var objects = new[] { __instance.entity.RootTransform.gameObject.GetComponentsInChildren<Animator>()[1] };
// Log.Out($"exporting objs: {objects.Length} avatar {objects[0].avatar.name} is human {objects[0].avatar.isHuman}");
// FbxExporter07.OnExport(objects, @"E:\Unity Projects\AnimationPlayground\Assets\ExportedProject\example_skinned_mesh_with_bones.fbx");
// Application.Quit();
// }
//}
}