using HarmonyLib; using KFCommonUtilityLib; using KFCommonUtilityLib.Scripts.Attributes; using System; using System.Collections.Generic; using System.Reflection.Emit; using UniLinq; [TypeTarget(typeof(ItemActionRanged))] public class ActionModuleMetaConsumer { public string[] consumeDatas; public FastTags[] consumeTags; private float[] consumeStocks; private float[] consumeValues; private static FastTags TagsConsumption = FastTags.Parse("ConsumptionValue"); [HarmonyPatch(nameof(ItemAction.ReadFrom)), MethodTargetPostfix] private void Postfix_ReadFrom(DynamicProperties _props, ItemAction __instance) { string consumeData = string.Empty; _props.Values.TryGetValue("ConsumeData", out consumeData); _props.Values.TryGetValue("ConsumeTags", out string tags); FastTags commonTags = string.IsNullOrEmpty(tags) ? FastTags.none : FastTags.Parse(tags); if (string.IsNullOrEmpty(consumeData)) { Log.Error($"No consume data found on item {__instance.item.Name} action {__instance.ActionIndex}"); return; } consumeDatas = consumeData.Split(new[] { ',' }, StringSplitOptions.RemoveEmptyEntries).Select(s => s.Trim()).ToArray(); consumeTags = consumeDatas.Select(s => FastTags.Parse(s) | commonTags | TagsConsumption).ToArray(); consumeStocks = new float[consumeDatas.Length]; consumeValues = new float[consumeDatas.Length]; } [HarmonyPatch(typeof(ItemActionRanged), nameof(ItemAction.ExecuteAction)), MethodTargetTranspiler] private static IEnumerable Transpiler_ItemActionRanged_ExecuteAction(IEnumerable instructions, ILGenerator generator) { var codes = instructions.ToList(); var fld_started = AccessTools.Field(typeof(ItemActionRanged.ItemActionDataRanged), nameof(ItemActionRanged.ItemActionDataRanged.burstShotStarted)); var fld_infinite = AccessTools.Field(typeof(ItemActionAttack), nameof(ItemActionAttack.InfiniteAmmo)); var mtd_consume = AccessTools.Method(typeof(ItemActionRanged), nameof(ItemActionRanged.ConsumeAmmo)); var lbd_module = generator.DeclareLocal(typeof(ActionModuleMetaConsumer)); var prop_instance = AccessTools.PropertyGetter(typeof(IModuleContainerFor), nameof(IModuleContainerFor.Instance)); var prop_itemvalue = AccessTools.PropertyGetter(typeof(ItemInventoryData), nameof(ItemInventoryData.itemValue)); for (int i = 0; i < codes.Count; i++) { if (codes[i].opcode == OpCodes.Stloc_S && ((LocalBuilder)codes[i].operand).LocalIndex == 6) { codes.InsertRange(i - 4, new[] { new CodeInstruction(OpCodes.Ldarg_0).WithLabels(codes[i - 4].ExtractLabels()), new CodeInstruction(OpCodes.Castclass, typeof(IModuleContainerFor)), new CodeInstruction(OpCodes.Callvirt, prop_instance), new CodeInstruction(OpCodes.Stloc_S, lbd_module), }); i += 4; } else if (codes[i].StoresField(fld_started) && codes[i - 1].LoadsConstant(1)) { var lbl = generator.DefineLabel(); var original = codes[i - 2]; codes.InsertRange(i - 2, new[] { new CodeInstruction(OpCodes.Ldloc_S, lbd_module).WithLabels(original.ExtractLabels()), new CodeInstruction(OpCodes.Ldloc_0), CodeInstruction.LoadField(typeof(ItemActionData), nameof(ItemActionData.invData)), new CodeInstruction(OpCodes.Callvirt, prop_itemvalue), new CodeInstruction(OpCodes.Ldloc_S, 6), new CodeInstruction(OpCodes.Ldarg_0), CodeInstruction.LoadField(typeof(ItemActionAttack), nameof(ItemActionAttack.soundEmpty)), CodeInstruction.Call(typeof(ActionModuleMetaConsumer), nameof(CheckAndCacheMetaData)), new CodeInstruction(OpCodes.Brtrue_S, lbl), new CodeInstruction(OpCodes.Ret) }); original.WithLabels(lbl); i += 10; } else if (codes[i].Calls(mtd_consume)) { var lbl = generator.DefineLabel(); for (int j = i - 1; j >= 0; j--) { if (codes[j].LoadsField(fld_infinite) && codes[j + 1].Branches(out _)) { codes[j + 1].operand = lbl; break; } } codes.InsertRange(i + 1, new[] { new CodeInstruction(OpCodes.Ldloc_S, lbd_module).WithLabels(lbl), new CodeInstruction(OpCodes.Ldloc_0), CodeInstruction.LoadField(typeof(ItemActionData), nameof(ItemActionData.invData)), new CodeInstruction(OpCodes.Callvirt, prop_itemvalue), new CodeInstruction(OpCodes.Ldloc_S, 6), CodeInstruction.Call(typeof(ActionModuleMetaConsumer), nameof(ConsumeMetaData)), }); break; } } return codes; } public bool CheckAndCacheMetaData(ItemValue itemValue, EntityAlive holdingEntity, string soundEmpty) { for (int i = 0; i < consumeDatas.Length; i++) { string consumeData = consumeDatas[i]; float stock = (float)itemValue.GetMetadata(consumeData); float consumption = EffectManager.GetValue(CustomEnums.CustomTaggedEffect, itemValue, float.MaxValue, holdingEntity, null, consumeTags[i]); if (stock < consumption) { holdingEntity.PlayOneShot(soundEmpty); return false; } consumeStocks[i] = stock; consumeValues[i] = consumption; } return true; } public void ConsumeMetaData(ItemValue itemValue, EntityAlive holdingEntity) { for (int i = 0; i < consumeDatas.Length; i++) { itemValue.SetMetadata(consumeDatas[i], consumeStocks[i] - consumeValues[i], TypedMetadataValue.TypeTag.Float); holdingEntity.MinEventContext.Tags = consumeTags[i]; holdingEntity.FireEvent(CustomEnums.onRechargeValueUpdate, true); } } //[HarmonyPatch(nameof(ItemAction.ExecuteAction)), MethodTargetPrefix] //private bool Prefix_ExecuteAction(ItemActionData _actionData, bool _bReleased, ItemActionRanged __instance) //{ // ItemActionRanged.ItemActionDataRanged _data = _actionData as ItemActionRanged.ItemActionDataRanged; // EntityAlive holdingEntity = _actionData.invData.holdingEntity; // ItemValue itemValue = _actionData.invData.itemValue; // if (!_bReleased) // { // int burstCount = __instance.GetBurstCount(_actionData); // if (holdingEntity.inventory.holdingItemItemValue.PercentUsesLeft <= 0f || (_data.curBurstCount >= burstCount && burstCount != -1) || (!__instance.InfiniteAmmo && itemValue.Meta <= 0)) // { // return true; // } // for (int i = 0; i < consumeDatas.Length; i++) // { // string consumeData = consumeDatas[i]; // float stock = (float)itemValue.GetMetadata(consumeData); // float consumption = EffectManager.GetValue(CustomEnums.CustomTaggedEffect, itemValue, float.MaxValue, _actionData.invData.holdingEntity, null, consumeTags[i]); // if (stock < consumption) // { // if (!_data.bPressed) // { // holdingEntity.PlayOneShot(__instance.soundEmpty); // _data.bPressed = true; // } // return false; // } // consumeStocks[i] = stock; // consumeValues[i] = consumption; // } // for (int i = 0; i < consumeDatas.Length; i++) // { // itemValue.SetMetadata(consumeDatas[i], consumeStocks[i] - consumeValues[i], TypedMetadataValue.TypeTag.Float); // holdingEntity.MinEventContext.Tags = consumeTags[i]; // holdingEntity.FireEvent(CustomEnums.onRechargeValueUpdate, true); // } // } // return true; //} }