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 Transpiler_SwapAmmoType_ItemActionRanged(IEnumerable instructions) { var codes = new List(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 Transpiler_ExecuteAction_ItemActionRanged(IEnumerable instructions) { var codes = new List(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 hash_shot_state = new HashSet(); private static HashSet hash_aimshot_state = new HashSet(); 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(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 Transpiler_gmUpdate_GameManager(IEnumerable instructions) //{ // var codes = new List(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 Transpiler_fireShot_ItemActionRanged(IEnumerable instructions) { var codes = new List(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 Transpiler_OnHoldingUpdate_ItemActionRanged(IEnumerable 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 Transpiler_triggerReleased_ItemActionRanged(IEnumerable 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 Transpiler_ParsePassiveEffect_PassiveEffect(IEnumerable 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 Transpiler_ParseXmlAttribute_MinEventActionBase(IEnumerable 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 Transpiler_ParseDisplayInfoEntry_UIDisplayInfoFromXml(IEnumerable 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 Transpiler_OnHoldingUpdate_ItemActionDynamicMelee(IEnumerable 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 Transpiler_canStartAttack_ItemActionDynamicMelee(IEnumerable 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; } /// /// projectile direct hit damage percent /// removed due to new explosion damage passives /// /// /// //[HarmonyPatch(typeof(ProjectileMoveScript), nameof(ProjectileMoveScript.checkCollision))] //[HarmonyTranspiler] //private static IEnumerable Transpiler_checkCollision_ProjectileMoveScript(IEnumerable 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); //} /// /// force tpv crosshair /// /// /// [HarmonyPatch(typeof(EntityPlayerLocal), nameof(EntityPlayerLocal.guiDrawCrosshair))] [HarmonyTranspiler] private static IEnumerable Transpiler_guiDrawCrosshair_EntityPlayerLocal(IEnumerable 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; } /// /// correctly apply muzzle flash silence with modifications /// /// [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 /// /// 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 /// /// /// /// [HarmonyPatch(typeof(XUiC_ItemPartStack), nameof(XUiC_ItemPartStack.CanSwap))] [HarmonyTranspiler] private static IEnumerable Transpiler_CanSwap_XUiC_ItemPartStack(IEnumerable instructions, ILGenerator generator) { var codes = instructions.ToList(); LocalBuilder lbd_tags_if_remove_prev = generator.DeclareLocal(typeof(FastTags)); LocalBuilder lbd_tags_if_install_new = generator.DeclareLocal(typeof(FastTags)); 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), nameof(FastTags.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 Transpiler_CanSwap_XUiC_ItemCosmeticStack(IEnumerable instructions, ILGenerator generator) { var codes = instructions.ToList(); LocalBuilder lbd_tags_if_remove_prev = generator.DeclareLocal(typeof(FastTags)); LocalBuilder lbd_tags_if_install_new = generator.DeclareLocal(typeof(FastTags)); 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), nameof(FastTags.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; } /// /// check if other mods relies on this one /// /// /// /// [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 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 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; } } } } /// /// should update the gear icon? /// /// /// /// [HarmonyPatch(typeof(XUiC_ItemStack), nameof(XUiC_ItemStack.updateLockTypeIcon))] [HarmonyTranspiler] private static IEnumerable Transpiler_updateLockTypeIcon_XUiC_ItemStack(IEnumerable instructions, ILGenerator generator) { var codes = instructions.ToList(); LocalBuilder lbd_tags = generator.DeclareLocal(typeof(FastTags)); MethodInfo mtd_has_any_tags = AccessTools.Method(typeof(ItemClass), nameof(ItemClass.HasAnyTags)); MethodInfo mtd_test_any_set = AccessTools.Method(typeof(FastTags), nameof(FastTags.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; } /// /// when installing new mod, use modified tags to check for compatibility /// /// /// /// [HarmonyPatch(typeof(XUiM_AssembleItem), nameof(XUiM_AssembleItem.AddPartToItem))] [HarmonyTranspiler] private static IEnumerable Transpiler_AddPartToItem_XUiM_AssembleItem(IEnumerable instructions, ILGenerator generator) { var codes = instructions.ToList(); LocalBuilder lbd_tags_cur = generator.DeclareLocal(typeof(FastTags)); LocalBuilder lbd_tags_after_install = generator.DeclareLocal(typeof(FastTags)); MethodInfo mtd_has_any_tags = AccessTools.Method(typeof(ItemClass), nameof(ItemClass.HasAnyTags)); MethodInfo mtd_test_any_set = AccessTools.Method(typeof(FastTags), nameof(FastTags.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 Transpiler_SetMoveState_EntityPlayerLocal(IEnumerable 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 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 Transpiler_SetupRadial_ItemActionAttack(IEnumerable 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 Transpiler_syncHeldItem_Inventory(IEnumerable 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 Transpiler_handleActivatableItemCommand_XUiC_Radial(IEnumerable 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 Transpiler_SetSlotItem_Equipment(IEnumerable 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 Transpiler_DropContentOfLootContainerServer_GameManager(IEnumerable 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 Transpiler_onHoldingEntityFired_ItemActionRanged(IEnumerable 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().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 Transpiler_ItemActionEffects_ItemActionRanged(IEnumerable 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(out var tmp)) { tmp.StopAllCoroutines(); Component.Destroy(tmp); } tmp = trans.AddMissingComponent(); tmp.life = 5f; } [HarmonyPatch(typeof(vp_FPCamera), nameof(vp_FPCamera.UpdateShakes))] [HarmonyTranspiler] private static IEnumerable Transpiler_UpdateShakes_vp_FPCamera(IEnumerable 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 Transpiler_updateHoldingItem_Inventory(IEnumerable 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()[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(); // } //} }