Files
0A-KFCommonUtilityLib/Scripts/Items/ModularActions/ActionModuleMetaConsumer.cs
2025-06-04 16:13:32 +09:30

178 lines
8.5 KiB
C#

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<TagGroup.Global>[] consumeTags;
private float[] consumeStocks;
private float[] consumeValues;
private static FastTags<TagGroup.Global> TagsConsumption = FastTags<TagGroup.Global>.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<TagGroup.Global> commonTags = string.IsNullOrEmpty(tags) ? FastTags<TagGroup.Global>.none : FastTags<TagGroup.Global>.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<TagGroup.Global>.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<CodeInstruction> Transpiler_ItemActionRanged_ExecuteAction(IEnumerable<CodeInstruction> 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<ActionModuleMetaConsumer>), nameof(IModuleContainerFor<ActionModuleMetaConsumer>.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<ActionModuleMetaConsumer>)),
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;
//}
}