Upload from upload_mods.ps1

This commit is contained in:
Nathaniel Cosford
2025-06-04 16:13:32 +09:30
commit 7345f42201
470 changed files with 51966 additions and 0 deletions

View File

@@ -0,0 +1,261 @@
using GUI_2;
using HarmonyLib;
using KFCommonUtilityLib.Scripts.Attributes;
using KFCommonUtilityLib.Scripts.StaticManagers;
using KFCommonUtilityLib.Scripts.Utilities;
using System.Collections;
using Unity.Mathematics;
[TypeTarget(typeof(ItemActionAttack)), ActionDataTarget(typeof(AlternativeData))]
public class ActionModuleAlternative
{
internal static ItemValue InventorySetItemTemp;
[HarmonyPatch(nameof(ItemAction.StartHolding)), MethodTargetPrefix]
private bool Prefix_StartHolding(ItemActionData _data, AlternativeData __customData)
{
//__customData.Init();
int prevMode = __customData.mapping.CurMode;
__customData.UpdateUnlockState(_data.invData.itemValue);
if (prevMode != __customData.mapping.CurMode && _data.invData.holdingEntity is EntityPlayerLocal player)
{
MultiActionManager.FireToggleModeEvent(player, __customData.mapping);
}
MultiActionManager.SetMappingForEntity(_data.invData.holdingEntity.entityId, __customData.mapping);
if (_data.invData.holdingEntity is EntityPlayerLocal)
{
MultiActionManager.inputCD = math.max(0.5f, MultiActionManager.inputCD);
//ThreadManager.StartCoroutine(DelaySetExecutionIndex(_data.invData.holdingEntity, __customData.mapping));
}
return true;
}
//[MethodTargetPostfix(nameof(ItemActionAttack.StartHolding))]
//private void Postfix_StartHolding(AlternativeData __customData)
//{
// __customData.UpdateMuzzleTransformOverride();
// __customData.OverrideMuzzleTransform(__customData.mapping.CurMode);
//}
private static IEnumerator DelaySetExecutionIndex(EntityAlive player, MultiActionMapping mapping)
{
yield return null;
yield return null;
if (GameManager.Instance.GetGameStateManager().IsGameStarted())
player?.emodel?.avatarController?.UpdateInt(MultiActionUtils.ExecutingActionIndexHash, mapping.CurActionIndex);
}
[HarmonyPatch(nameof(ItemActionRanged.CancelReload)), MethodTargetPrefix]
private bool Prefix_CancelReload(ItemActionData _actionData, AlternativeData __customData)
{
if (__customData.mapping == null)
return true;
int actionIndex = __customData.mapping.CurActionIndex;
if (ConsoleCmdReloadLog.LogInfo)
Log.Out($"cancel reload {actionIndex}");
if (actionIndex == 0)
return true;
_actionData.invData.holdingEntity.inventory.holdingItem.Actions[actionIndex].CancelReload(_actionData.invData.holdingEntity.inventory.holdingItemData.actionData[actionIndex]);
return false;
}
[HarmonyPatch(nameof(ItemAction.CancelAction)), MethodTargetPrefix]
private bool Prefix_CancelAction(ItemActionData _actionData, AlternativeData __customData)
{
if (__customData.mapping == null)
return true;
int actionIndex = __customData.mapping.CurActionIndex;
if (ConsoleCmdReloadLog.LogInfo)
Log.Out($"cancel action {actionIndex}");
if (actionIndex == 0)
return true;
_actionData.invData.holdingEntity.inventory.holdingItem.Actions[actionIndex].CancelAction(_actionData.invData.holdingEntity.inventory.holdingItemData.actionData[actionIndex]);
return false;
}
[HarmonyPatch(nameof(ItemAction.IsStatChanged)), MethodTargetPrefix]
private bool Prefix_IsStatChanged(ref bool __result)
{
var mapping = MultiActionManager.GetMappingForEntity(GameManager.Instance.World.GetPrimaryPlayerId());
__result |= mapping != null && mapping.CheckDisplayMode();
return false;
}
//[MethodTargetPostfix(nameof(ItemActionAttack.StopHolding))]
//private void Postfix_StopHolding(AlternativeData __customData)
//{
// //moved to harmony patch
// //MultiActionManager.SetMappingForEntity(_data.invData.holdingEntity.entityId, null);
// __customData.mapping.SaveMeta();
//}
//todo: change to action specific property
[HarmonyPatch(nameof(ItemAction.OnModificationsChanged)), MethodTargetPostfix]
private void Postfix_OnModificationChanged(ItemActionData _data, ItemActionAttack __instance, AlternativeData __customData)
{
__instance.Properties.ParseString("ToggleActionSound", ref __customData.toggleSound);
__customData.toggleSound = _data.invData.itemValue.GetPropertyOverrideForAction("ToggleActionSound", __customData.toggleSound, __instance.ActionIndex);
__customData.mapping.toggleSound = __customData.toggleSound;
}
[HarmonyPatch(nameof(ItemAction.SetupRadial)), MethodTargetPrefix]
private bool Prefix_SetupRadial(XUiC_Radial _xuiRadialWindow, EntityPlayerLocal _epl)
{
var mapping = MultiActionManager.GetMappingForEntity(_epl.entityId);
if (mapping != null)
{
var radialContextItem = new AlternativeRadialContextItem(mapping, _xuiRadialWindow, _epl);
_xuiRadialWindow.SetCommonData(UIUtils.GetButtonIconForAction(_epl.playerInput.Reload), handleRadialCommand, radialContextItem, radialContextItem.PreSelectedIndex, false, radialValidTest);
}
return false;
}
private bool radialValidTest(XUiC_Radial _sender, XUiC_Radial.RadialContextAbs _context)
{
AlternativeRadialContextItem radialContextItem = _context as AlternativeRadialContextItem;
if (radialContextItem == null)
{
return false;
}
EntityPlayerLocal entityPlayer = _sender.xui.playerUI.entityPlayer;
return radialContextItem.mapping == MultiActionManager.GetMappingForEntity(entityPlayer.entityId) && radialContextItem.mapping.CurActionIndex == radialContextItem.ActionIndex;
}
//redirect reload call to shared meta action, which then sets ItemActionIndex animator param to its action index
//for example if action 3 share meta with action 0, then ItemActionIndex is set to 0 on reload begin.
//since event param item action data is set to the shared meta action data, all reload related passive calculation and trigger events goes there.
private void handleRadialCommand(XUiC_Radial _sender, int _commandIndex, XUiC_Radial.RadialContextAbs _context)
{
AlternativeRadialContextItem radialContextItem = _context as AlternativeRadialContextItem;
if (radialContextItem == null)
{
return;
}
EntityPlayerLocal entityPlayer = _sender.xui.playerUI.entityPlayer;
if (radialContextItem.mapping == MultiActionManager.GetMappingForEntity(entityPlayer.entityId) && radialContextItem.mapping.CurActionIndex == radialContextItem.ActionIndex)
{
entityPlayer.MinEventContext.ItemActionData = entityPlayer.inventory.holdingItemData.actionData?[radialContextItem.ActionIndex];
(entityPlayer.inventory.holdingItem.Actions?[radialContextItem.ActionIndex] as ItemActionRanged)?.SwapSelectedAmmo(entityPlayer, _commandIndex);
}
}
public class AlternativeData
{
public MultiActionMapping mapping;
public string toggleSound;
public ItemInventoryData invData;
//private bool inited = false;
private readonly bool[] unlocked = new bool[MultiActionIndice.MAX_ACTION_COUNT];
//public Transform[] altMuzzleTrans = new Transform[MultiActionIndice.MAX_ACTION_COUNT];
//public Transform[] altMuzzleTransDBarrel = new Transform[MultiActionIndice.MAX_ACTION_COUNT];
public AlternativeData(ItemInventoryData invData, int actionIndex, ActionModuleAlternative module)
{
this.invData = invData;
Init();
}
//public void UpdateMuzzleTransformOverride()
//{
// for (int i = 0; i < MultiActionIndice.MAX_ACTION_COUNT; i++)
// {
// int curActionIndex = mapping.indices.GetActionIndexForMode(i);
// if (curActionIndex < 0)
// {
// break;
// }
// var rangedData = invData.actionData[curActionIndex] as ItemActionRanged.ItemActionDataRanged;
// if (rangedData != null)
// {
// if (rangedData.IsDoubleBarrel)
// {
// altMuzzleTrans[i] = AnimationRiggingManager.GetTransformOverrideByName($"Muzzle_L{curActionIndex}", rangedData.invData.model) ?? rangedData.muzzle;
// altMuzzleTransDBarrel[i] = AnimationRiggingManager.GetTransformOverrideByName($"Muzzle_R{curActionIndex}", rangedData.invData.model) ?? rangedData.muzzle2;
// }
// else
// {
// altMuzzleTrans[i] = AnimationRiggingManager.GetTransformOverrideByName($"Muzzle{curActionIndex}", rangedData.invData.model) ?? rangedData.muzzle;
// }
// }
// }
//}
public void Init()
{
//if (inited)
// return;
//inited = true;
MultiActionIndice indices = MultiActionManager.GetActionIndiceForItemID(invData.item.Id);
mapping = new MultiActionMapping(this, indices, invData.holdingEntity, InventorySetItemTemp, toggleSound, invData.slotIdx, unlocked);
UpdateUnlockState(InventorySetItemTemp);
}
public void UpdateUnlockState(ItemValue itemValue)
{
//if (!inited)
// return;
unlocked[0] = true;
for (int i = 1; i < mapping.ModeCount; i++)
{
bool flag = true;
int actionIndex = mapping.indices.GetActionIndexForMode(i);
ItemAction action = itemValue.ItemClass.Actions[actionIndex];
action.Properties.ParseBool("ActionUnlocked", ref flag);
if (bool.TryParse(itemValue.GetPropertyOverride($"ActionUnlocked_{actionIndex}", flag.ToString()), out bool overrideFlag))
flag = overrideFlag;
unlocked[i] = flag;
}
//by the time we check unlock state, ItemValue in inventory slot might not be ready yet
mapping.SaveMeta(itemValue);
mapping.CurMode = mapping.CurMode;
mapping.ReadMeta(itemValue);
}
public bool IsActionUnlocked(int actionIndex)
{
int mode = mapping.indices.GetModeForAction(actionIndex);
if (mode >= MultiActionIndice.MAX_ACTION_COUNT || mode < 0)
return false;
return unlocked[mode];
}
// public void OverrideMuzzleTransform(int mode)
// {
// var rangedData = invData.actionData[mapping.indices.GetActionIndexForMode(mode)] as ItemActionRanged.ItemActionDataRanged;
// if (rangedData != null)
// {
// if (rangedData.IsDoubleBarrel)
// {
// rangedData.muzzle = altMuzzleTrans[mode];
// rangedData.muzzle2 = altMuzzleTransDBarrel[mode];
// }
// else
// {
// rangedData.muzzle = altMuzzleTrans[mode];
// }
// }
//#if DEBUG
// Log.Out($"setting muzzle transform for action {rangedData.indexInEntityOfAction} to {rangedData.muzzle.name}\n{StackTraceUtility.ExtractStackTrace()}");
//#endif
// }
}
//todo: don't setup for every mode, and use reload animation from shared action
public class AlternativeRadialContextItem : XUiC_Radial.RadialContextAbs
{
public MultiActionMapping mapping;
public int ActionIndex { get; private set; }
public int PreSelectedIndex { get; private set; }
public AlternativeRadialContextItem(MultiActionMapping mapping, XUiC_Radial _xuiRadialWindow, EntityPlayerLocal _epl)
{
this.mapping = mapping;
ActionIndex = mapping.CurActionIndex;
PreSelectedIndex = mapping.SetupRadial(_xuiRadialWindow, _epl);
}
}
}

View File

@@ -0,0 +1,36 @@
using HarmonyLib;
using KFCommonUtilityLib.Scripts.Attributes;
[TypeTarget(typeof(ItemAction)), ActionDataTarget(typeof(AnimationLockedData))]
public class ActionModuleAnimationLocked
{
[HarmonyPatch(nameof(ItemAction.StartHolding)), MethodTargetPostfix]
private void Postfix_StartHolding(AnimationLockedData __customData)
{
__customData.isLocked = false;
__customData.isReloadLocked = false;
}
[HarmonyPatch(nameof(ItemAction.IsActionRunning)), MethodTargetPostfix]
private void Postfix_IsActionRunning(AnimationLockedData __customData, ref bool __result)
{
__result |= __customData.isLocked;
}
[HarmonyPatch(typeof(ItemActionAttack), nameof(ItemActionAttack.CanReload)), MethodTargetPostfix]
private void Postfix_CanReload_ItemActionAttack(AnimationLockedData __customData, ref bool __result)
{
__result &= !__customData.isReloadLocked;
}
public class AnimationLockedData
{
public bool isLocked = false;
public bool isReloadLocked = false;
public AnimationLockedData(ItemInventoryData invData, int actionIndex, ActionModuleAnimationLocked module)
{
}
}
}

View File

@@ -0,0 +1,135 @@
using HarmonyLib;
using KFCommonUtilityLib.Scripts.Attributes;
using System.Collections.Generic;
using System.Reflection.Emit;
using UniLinq;
using static AnimationDelayData;
[TypeTarget(typeof(ItemAction))]
public class ActionModuleCustomAnimationDelay
{
[HarmonyPatch(typeof(ItemActionEat), nameof(ItemAction.OnHoldingUpdate))]
[HarmonyPatch(typeof(ItemActionGainSkill), nameof(ItemAction.OnHoldingUpdate))]
[HarmonyPatch(typeof(ItemActionLearnRecipe), nameof(ItemAction.OnHoldingUpdate))]
[HarmonyPatch(typeof(ItemActionQuest), nameof(ItemAction.OnHoldingUpdate))]
[HarmonyPatch(typeof(ItemActionEat), nameof(ItemAction.IsActionRunning))]
[HarmonyPatch(typeof(ItemActionGainSkill), nameof(ItemAction.IsActionRunning))]
[HarmonyPatch(typeof(ItemActionLearnRecipe), nameof(ItemAction.IsActionRunning))]
[HarmonyPatch(typeof(ItemActionQuest), nameof(ItemAction.IsActionRunning))]
[MethodTargetTranspiler]
private static IEnumerable<CodeInstruction> Transpiler_OnHoldingUpdate(IEnumerable<CodeInstruction> instructions)
{
var codes = instructions.ToList();
var fld_delayarr = AccessTools.Field(typeof(AnimationDelayData), nameof(AnimationDelayData.AnimationDelay));
var fld_raycast = AccessTools.Field(typeof(AnimationDelays), nameof(AnimationDelays.RayCast));
for (var i = 0; i < codes.Count; i++)
{
if (codes[i].LoadsField(fld_delayarr))
{
for (int j = i + 1; j < codes.Count; j++)
{
if (codes[j].LoadsField(fld_raycast))
{
bool flag = codes[i - 1].LoadsConstant(2f);
codes.RemoveRange(flag ? i - 1 : i, j - i + (flag ? 3 : 1));
codes.InsertRange(flag ? i - 1 : i, new[]
{
new CodeInstruction(OpCodes.Ldarg_0),
CodeInstruction.LoadField(typeof(ItemAction), nameof(ItemAction.Delay))
});
break;
}
}
break;
}
}
return codes;
}
//[HarmonyPatch(nameof(ItemAction.OnHoldingUpdate)), MethodTargetPrefix]
//private bool Prefix_OnHoldingUpdate(ItemAction __instance, ItemActionData _actionData, out AnimationDelays __state)
//{
// __state = AnimationDelayData.AnimationDelay[_actionData.invData.item.HoldType.Value];
// if (!__instance.UseAnimation)
// return true;
// var modifiedData = __state;
// modifiedData.RayCast = __instance.Delay;
// AnimationDelayData.AnimationDelay[_actionData.invData.item.HoldType.Value] = modifiedData;
// return true;
//}
//[HarmonyPatch(nameof(ItemAction.OnHoldingUpdate)), MethodTargetPostfix]
//private void Postfix_OnHoldingUpdate(ItemAction __instance, ItemActionData _actionData, AnimationDelays __state)
//{
// if (!__instance.UseAnimation)
// return;
// AnimationDelayData.AnimationDelay[_actionData.invData.item.HoldType.Value] = __state;
//}
//[HarmonyPatch(nameof(ItemAction.IsActionRunning)), MethodTargetPrefix]
//private bool Prefix_IsActionRunning(ItemAction __instance, ItemActionData _actionData, out AnimationDelays __state)
//{
// __state = AnimationDelayData.AnimationDelay[_actionData.invData.item.HoldType.Value];
// if (!__instance.UseAnimation)
// return true;
// var modifiedData = __state;
// modifiedData.RayCast = __instance.Delay * .5f;
// AnimationDelayData.AnimationDelay[_actionData.invData.item.HoldType.Value] = modifiedData;
// return true;
//}
//[HarmonyPatch(nameof(ItemAction.IsActionRunning)), MethodTargetPostfix]
//private void Postfix_IsActionRunning(ItemAction __instance, ItemActionData _actionData, AnimationDelays __state)
//{
// if (!__instance.UseAnimation)
// return;
// AnimationDelayData.AnimationDelay[_actionData.invData.item.HoldType.Value] = __state;
//}
//following are fix for item use time from menu entry
//when IsActionRunning is called from coroutine which is started by menu entry,
//as OnHoldingUpdate is not called every frame, the check might yield false before item actually gets consumed, thus returning the item
//so we call OnHoldingUpdate to properly consume the item
//vanilla method on the other hand, is forcing double delay in IsActionRunning
[HarmonyPatch(typeof(ItemActionEat), nameof(ItemAction.IsActionRunning)), MethodTargetPostfix]
private void Postfix_IsActionRunning_ItemActionEat(ItemActionEat __instance, ItemActionData _actionData/*, AnimationDelays __state*/, bool __result)
{
//Postfix_IsActionRunning(__instance, _actionData, __state);
if (!__result && ((ItemActionEat.MyInventoryData)_actionData).bEatingStarted)
{
__instance.OnHoldingUpdate(_actionData);
}
}
[HarmonyPatch(typeof(ItemActionGainSkill), nameof(ItemAction.IsActionRunning)), MethodTargetPostfix]
private void Postfix_IsActionRunning_ItemActionGainSkill(ItemActionGainSkill __instance, ItemActionData _actionData/*, AnimationDelays __state*/, bool __result)
{
//Postfix_IsActionRunning(__instance, _actionData, __state);
if (!__result && ((ItemActionGainSkill.MyInventoryData)_actionData).bReadingStarted)
{
__instance.OnHoldingUpdate(_actionData);
}
}
[HarmonyPatch(typeof(ItemActionLearnRecipe), nameof(ItemAction.IsActionRunning)), MethodTargetPostfix]
private void Postfix_IsActionRunning_ItemActionLearnRecipe(ItemActionLearnRecipe __instance, ItemActionData _actionData/*, AnimationDelays __state*/, bool __result)
{
//Postfix_IsActionRunning(__instance, _actionData, __state);
if (!__result && ((ItemActionLearnRecipe.MyInventoryData)_actionData).bReadingStarted)
{
__instance.OnHoldingUpdate(_actionData);
}
}
[HarmonyPatch(typeof(ItemActionQuest), nameof(ItemAction.IsActionRunning)), MethodTargetPostfix]
private void Postfix_IsActionRunning_ItemActionQuest(ItemActionQuest __instance, ItemActionData _actionData/*, AnimationDelays __state*/, bool __result)
{
//Postfix_IsActionRunning(__instance, _actionData, __state);
if (!__result && ((ItemActionQuest.MyInventoryData)_actionData).bQuestAccept)
{
__instance.OnHoldingUpdate(_actionData);
}
}
}

View File

@@ -0,0 +1,78 @@
using HarmonyLib;
using KFCommonUtilityLib.Scripts.Attributes;
public class DisplayAsBuffEntityUINotification : BuffEntityUINotification
{
public ActionModuleDisplayAsBuff.DisplayValueType displayType = ActionModuleDisplayAsBuff.DisplayValueType.Meta;
public string displayData = string.Empty;
public override float CurrentValue
{
get
{
EntityPlayerLocal player = GameManager.Instance.World.GetPrimaryPlayer();
if (player == null)
return 0;
switch (displayType)
{
case ActionModuleDisplayAsBuff.DisplayValueType.Meta:
return player.inventory.holdingItemItemValue.Meta;
case ActionModuleDisplayAsBuff.DisplayValueType.MetaData:
return (float)player.inventory.holdingItemItemValue.GetMetadata(displayData);
default:
return 0;
}
}
}
public override bool Visible => true;
public override EnumEntityUINotificationDisplayMode DisplayMode => EnumEntityUINotificationDisplayMode.IconPlusCurrentValue;
}
[TypeTarget(typeof(ItemActionRanged))]
public class ActionModuleDisplayAsBuff
{
public enum DisplayValueType
{
Meta,
MetaData
}
private DisplayAsBuffEntityUINotification notification;
private BuffClass buffClass;
[HarmonyPatch(nameof(ItemAction.ReadFrom)), MethodTargetPostfix]
private void Postfix_ReadFrom(DynamicProperties _props)
{
notification = new DisplayAsBuffEntityUINotification();
_props.Values.TryGetValue("DisplayType", out string str);
EnumUtils.TryParse(str, out notification.displayType, true);
_props.Values.TryGetValue("DisplayData", out notification.displayData);
_props.Values.TryGetValue("DisplayBuff", out str);
BuffClass buffClass = BuffManager.GetBuff(str);
BuffValue buff = new BuffValue(buffClass.Name, Vector3i.zero, -1, buffClass);
notification.SetBuff(buff);
}
[HarmonyPatch(nameof(ItemAction.StartHolding)), MethodTargetPostfix]
private void Postfix_StartHolding(ItemActionData _data)
{
EntityPlayerLocal player = _data.invData.holdingEntity as EntityPlayerLocal;
if (player != null && notification != null)
{
notification.SetStats(player.Stats);
player.Stats.NotificationAdded(notification);
}
}
[HarmonyPatch(nameof(ItemAction.StopHolding)), MethodTargetPostfix]
private void Postfix_StopHolding(ItemActionData _data)
{
EntityPlayerLocal player = _data.invData.holdingEntity as EntityPlayerLocal;
if (player != null && notification != null)
{
player.Stats.NotificationRemoved(notification);
}
}
}

View File

@@ -0,0 +1,67 @@
using HarmonyLib;
using KFCommonUtilityLib.Scripts.Attributes;
using KFCommonUtilityLib.Scripts.StaticManagers;
[TypeTarget(typeof(ItemActionDynamic))]
public class ActionModuleDynamicGraze
{
private string dynamicSoundStart = null;
[HarmonyPatch(nameof(ItemAction.ExecuteAction)), MethodTargetPrefix]
private bool Prefix_ExecuteAction(ItemActionDynamic __instance, ItemActionData _actionData, bool _bReleased, out (bool executed, string originalSound) __state)
{
if (!_bReleased && !string.IsNullOrEmpty(dynamicSoundStart) && _actionData.invData.holdingEntity is EntityPlayerLocal player)
{
var targets = AnimationRiggingManager.GetRigTargetsFromPlayer(player);
if (targets && !targets.Destroyed && targets.IsAnimationSet)
{
__state = (true, __instance.soundStart);
__instance.soundStart = dynamicSoundStart;
return true;
}
}
__state = (false, null);
return true;
}
[HarmonyPatch(nameof(ItemAction.ExecuteAction)), MethodTargetPostfix]
private void Postfix_ExecuteAction(ItemActionDynamic __instance, (bool executed, string originalSound) __state)
{
if (__state.executed)
{
__instance.soundStart = __state.originalSound;
}
}
[HarmonyPatch(nameof(ItemAction.OnHoldingUpdate)), MethodTargetPrefix]
private bool Prefix_OnHoldingUpdate(ItemActionDynamic __instance, ItemActionData _actionData, out (bool executed, bool useGrazeCast) __state)
{
if (_actionData.invData.holdingEntity is EntityPlayerLocal player)
{
var targets = AnimationRiggingManager.GetRigTargetsFromPlayer(player);
if (targets && !targets.Destroyed && targets.ItemCurrent)
{
__state = (true, __instance.UseGrazingHits);
__instance.UseGrazingHits = false;
return true;
}
}
__state = (false, false);
return true;
}
[HarmonyPatch(nameof(ItemAction.OnHoldingUpdate)), MethodTargetPostfix]
private void Postfix_OnHoldingUpdate(ItemActionDynamic __instance, (bool executed, bool useGrazeCast) __state)
{
if (__state.executed)
{
__instance.UseGrazingHits = __state.useGrazeCast;
}
}
[HarmonyPatch(nameof(ItemAction.ReadFrom)), MethodTargetPostfix]
private void Postfix_ReadFrom(DynamicProperties _props)
{
_props.ParseString("DynamicSoundStart", ref dynamicSoundStart);
}
}

View File

@@ -0,0 +1,84 @@
using HarmonyLib;
using KFCommonUtilityLib.Scripts.Attributes;
using KFCommonUtilityLib.Scripts.Utilities;
[TypeTarget(typeof(ItemActionAttack)), ActionDataTarget(typeof(DynamicMuzzleFlashData))]
public class ActionModuleDynamicMuzzleFlash
{
private struct State
{
public bool executed;
public string particlesMuzzleFire;
public string particlesMuzzleSmoke;
public string particlesMuzzleFireFpv;
public string particlesMuzzleSmokeFpv;
}
[HarmonyPatch(nameof(ItemAction.OnModificationsChanged)), MethodTargetPostfix]
private void Postfix_OnModificationsChanged(ItemActionAttack __instance, ItemActionAttackData _data, DynamicMuzzleFlashData __customData)
{
__customData.particlesMuzzleFire = _data.invData.itemValue.GetPropertyOverrideForAction("Particles_muzzle_fire", __instance.particlesMuzzleFire, __instance.ActionIndex);
__customData.particlesMuzzleFireFpv = _data.invData.itemValue.GetPropertyOverrideForAction("Particles_muzzle_fire_fpv", __instance.particlesMuzzleFireFpv, __instance.ActionIndex);
__customData.particlesMuzzleSmoke = _data.invData.itemValue.GetPropertyOverrideForAction("Particles_muzzle_smoke", __instance.particlesMuzzleSmoke, __instance.ActionIndex);
__customData.particlesMuzzleSmokeFpv = _data.invData.itemValue.GetPropertyOverrideForAction("Particles_muzzle_smoke_fpv", __instance.particlesMuzzleSmokeFpv, __instance.ActionIndex);
if (!string.IsNullOrEmpty(__customData.particlesMuzzleFire) && !ParticleEffect.IsAvailable(__customData.particlesMuzzleFire))
{
ParticleEffect.LoadAsset(__customData.particlesMuzzleFire);
}
if (!string.IsNullOrEmpty(__customData.particlesMuzzleFireFpv) && !ParticleEffect.IsAvailable(__customData.particlesMuzzleFireFpv))
{
ParticleEffect.LoadAsset(__customData.particlesMuzzleFireFpv);
}
if (!string.IsNullOrEmpty(__customData.particlesMuzzleSmoke) && !ParticleEffect.IsAvailable(__customData.particlesMuzzleSmoke))
{
ParticleEffect.LoadAsset(__customData.particlesMuzzleSmoke);
}
if (!string.IsNullOrEmpty(__customData.particlesMuzzleSmokeFpv) && !ParticleEffect.IsAvailable(__customData.particlesMuzzleSmokeFpv))
{
ParticleEffect.LoadAsset(__customData.particlesMuzzleSmokeFpv);
}
}
[HarmonyPatch(nameof(ItemAction.ItemActionEffects)), MethodTargetPrefix]
private bool Prefix_ItemActionEffects(ItemActionAttack __instance, DynamicMuzzleFlashData __customData, out State __state)
{
__state = new State()
{
executed = true,
particlesMuzzleFire = __instance.particlesMuzzleFire,
particlesMuzzleFireFpv = __instance.particlesMuzzleFireFpv,
particlesMuzzleSmoke = __instance.particlesMuzzleSmoke,
particlesMuzzleSmokeFpv = __instance.particlesMuzzleSmokeFpv
};
__instance.particlesMuzzleFire = __customData.particlesMuzzleFire;
__instance.particlesMuzzleFireFpv = __customData .particlesMuzzleFireFpv;
__instance.particlesMuzzleSmoke = __customData.particlesMuzzleSmoke;
__instance.particlesMuzzleSmokeFpv = __customData.particlesMuzzleSmokeFpv;
return true;
}
[HarmonyPatch(nameof(ItemAction.ItemActionEffects)), MethodTargetPostfix]
private void Postfix_ItemActionEffects(ItemActionAttack __instance, State __state)
{
if (__state.executed)
{
__instance.particlesMuzzleFire = __state.particlesMuzzleFire;
__instance.particlesMuzzleFireFpv = __state.particlesMuzzleFireFpv;
__instance.particlesMuzzleSmoke = __state.particlesMuzzleSmoke;
__instance.particlesMuzzleSmokeFpv = __state.particlesMuzzleSmokeFpv;
}
}
public class DynamicMuzzleFlashData
{
public string particlesMuzzleFire;
public string particlesMuzzleFireFpv;
public string particlesMuzzleSmoke;
public string particlesMuzzleSmokeFpv;
public DynamicMuzzleFlashData(ItemInventoryData _invData, int _indexOfAction, ActionModuleDynamicMuzzleFlash _module)
{
}
}
}

View File

@@ -0,0 +1,89 @@
using HarmonyLib;
using KFCommonUtilityLib;
using KFCommonUtilityLib.Scripts.Attributes;
using UnityEngine;
[TypeTarget(typeof(ItemActionZoom)), ActionDataTarget(typeof(DynamicSensitivityData))]
public class ActionModuleDynamicSensitivity
{
[HarmonyPatch(nameof(ItemAction.AimingSet)), MethodTargetPostfix]
private void Postfix_AimingSet(ItemActionData _actionData, bool _isAiming, bool _wasAiming, DynamicSensitivityData __customData)
{
float originalSensitivity = GamePrefs.GetFloat(EnumGamePrefs.OptionsZoomSensitivity);
if (_isAiming)
{
PlayerMoveController.Instance.mouseZoomSensitivity = originalSensitivity / Mathf.Sqrt(__customData.ZoomRatio);
}
else
{
PlayerMoveController.Instance.mouseZoomSensitivity = originalSensitivity;
}
}
[HarmonyPatch(nameof(ItemAction.OnModificationsChanged)), MethodTargetPostfix]
private void Postfix_OnModificationsChanged(ItemActionZoom __instance, ItemActionData _data, DynamicSensitivityData __customData)
{
if (_data is IModuleContainerFor<ActionModuleVariableZoom.VariableZoomData> variableZoomData)
{
__customData.variableZoomData = variableZoomData.Instance;
}
else
{
string str = __instance.Properties.GetString("ZoomRatio");
if (string.IsNullOrEmpty(str))
{
str = "1";
}
__customData.ZoomRatio = StringParsers.ParseFloat(_data.invData.itemValue.GetPropertyOverride("ZoomRatio", str));
}
__customData.dsRangeOverride = StringParsers.ParseVector2(_data.invData.itemValue.GetPropertyOverride("DynamicSensitivityRange", "0,0"));
}
[HarmonyPatch(nameof(ItemAction.OnHoldingUpdate)), MethodTargetPostfix]
private void Postfix_OnHoldingUpdate(ItemActionData _actionData, DynamicSensitivityData __customData)
{
if (((ItemActionZoom.ItemActionDataZoom)_actionData).aimingValue)
{
float originalSensitivity = GamePrefs.GetFloat(EnumGamePrefs.OptionsZoomSensitivity);
if (__customData.activated)
{
PlayerMoveController.Instance.mouseZoomSensitivity = originalSensitivity / Mathf.Sqrt(__customData.ZoomRatio);
}
else
{
PlayerMoveController.Instance.mouseZoomSensitivity = originalSensitivity;
}
}
}
public class DynamicSensitivityData
{
public ActionModuleVariableZoom.VariableZoomData variableZoomData = null;
private float zoomRatio = 1.0f;
public Vector2 dsRangeOverride = Vector2.zero;
public bool activated = false;
public float ZoomRatio
{
get
{
if (variableZoomData != null)
{
if (dsRangeOverride.x > 0 && dsRangeOverride.y >= dsRangeOverride.x)
{
return Mathf.Lerp(dsRangeOverride.x, dsRangeOverride.y, Mathf.InverseLerp(variableZoomData.minScale, variableZoomData.maxScale, variableZoomData.curScale));
}
return variableZoomData.curScale;
}
return zoomRatio;
}
set => zoomRatio = value;
}
public DynamicSensitivityData(ItemInventoryData _invData, int _indexInEntityOfAction, ActionModuleDynamicSensitivity _module)
{
}
}
}

View File

@@ -0,0 +1,153 @@
using HarmonyLib;
using KFCommonUtilityLib;
using KFCommonUtilityLib.Scripts.Attributes;
using KFCommonUtilityLib.Scripts.StaticManagers;
using System.Collections.Generic;
using System.Reflection.Emit;
using UniLinq;
using UnityEngine;
[TypeTarget(typeof(ItemActionZoom)), ActionDataTarget(typeof(ErgoData))]
public class ActionModuleErgoAffected
{
public static readonly int AimSpeedModifierHash = Animator.StringToHash("AimSpeedModifier");
public float zoomInTimeBase;
public float aimSpeedModifierBase;
[HarmonyPatch(nameof(ItemAction.OnModificationsChanged)), MethodTargetPostfix]
private void Postfix_OnModificationChanged(ItemActionData _data, ItemActionZoom __instance, ErgoData __customData)
{
zoomInTimeBase = 0.3f;
__instance.Properties.ParseFloat("ZoomInTimeBase", ref zoomInTimeBase);
aimSpeedModifierBase = 1f;
__instance.Properties.ParseFloat("AimSpeedModifierBase", ref aimSpeedModifierBase);
__customData.aimStartTime = float.MaxValue;
__customData.aimSet = false;
}
[HarmonyPatch(nameof(ItemAction.ExecuteAction)), MethodTargetPostfix]
private void Postfix_ExecuteAction(ItemActionData _actionData, ItemActionZoom __instance, bool _bReleased, ErgoData __customData)
{
EntityAlive holdingEntity = _actionData.invData.holdingEntity;
ItemActionData prevActionData = holdingEntity.MinEventContext.ItemActionData;
holdingEntity.MinEventContext.ItemActionData = _actionData.invData.actionData[MultiActionManager.GetActionIndexForEntity(holdingEntity)];
float ergoValue = EffectManager.GetValue(CustomEnums.WeaponErgonomics, _actionData.invData.itemValue, 0, holdingEntity);
float aimSpeedModifier = Mathf.Lerp(0.2f, 1, ergoValue);
Log.Out($"Ergo is {ergoValue}, base aim modifier is {aimSpeedModifierBase}, aim speed is {aimSpeedModifier * aimSpeedModifierBase}");
holdingEntity.emodel.avatarController.UpdateFloat(AimSpeedModifierHash, aimSpeedModifier * aimSpeedModifierBase, true);
holdingEntity.MinEventContext.ItemActionData = prevActionData;
if ((_actionData as ItemActionZoom.ItemActionDataZoom).aimingValue && !_bReleased)
{
__customData.aimStartTime = Time.time;
}
else if (!(_actionData as ItemActionZoom.ItemActionDataZoom).aimingValue)
{
__customData.aimStartTime = float.MaxValue;
}
__customData.aimSet = false;
}
//[MethodTargetPostfix(nameof(ItemAction.OnHoldingUpdate))]
//private void Postfix_OnHoldingUpdate(ItemActionData _actionData, ErgoData __customData)
//{
// if ((_actionData as ItemActionZoom.ItemActionDataZoom).aimingValue && Time.time - __customData.aimStartTime > zoomInTimeBase)
// {
// __customData.aimSet = true;
// }
// else
// {
// __customData.aimSet = false;
// }
//}
public class ErgoData
{
public float aimStartTime;
public bool aimSet;
public ActionModuleErgoAffected module;
public ErgoData(ItemInventoryData _invData, int _indexInEntityOfAction, ActionModuleErgoAffected _module)
{
aimStartTime = float.MaxValue;
aimSet = false;
module = _module;
}
}
}
[HarmonyPatch]
public static class ErgoPatches
{
[HarmonyPatch(typeof(ItemActionRanged), nameof(ItemActionRanged.updateAccuracy))]
[HarmonyTranspiler]
private static IEnumerable<CodeInstruction> Transpiler_ItemActionRanged_updateAccuracy(IEnumerable<CodeInstruction> instructions, ILGenerator generator)
{
var codes = instructions.ToList();
var mtd_lerp = AccessTools.Method(typeof(Mathf), nameof(Mathf.Lerp), new[] { typeof(float), typeof(float), typeof(float) });
for (int i = 0; i < codes.Count; i++)
{
if (codes[i].Calls(mtd_lerp))
{
codes.InsertRange(i, new[]
{
new CodeInstruction(OpCodes.Ldarg_0),
new CodeInstruction(OpCodes.Ldarg_1),
new CodeInstruction(OpCodes.Ldarg_2),
CodeInstruction.Call(typeof(ErgoPatches), nameof(CalcErgoModifier)),
});
break;
}
}
return codes;
}
private static float CalcErgoModifier(float originalValue, ItemAction action, ItemActionData actionData, bool aiming)
{
ItemActionRanged.ItemActionDataRanged rangedData = actionData as ItemActionRanged.ItemActionDataRanged;
if (aiming && rangedData.invData.actionData[1] is IModuleContainerFor<ActionModuleErgoAffected.ErgoData> dataModule && !dataModule.Instance.aimSet && Time.time - dataModule.Instance.aimStartTime > 0)
{
ActionModuleErgoAffected.ErgoData ergoData = dataModule.Instance;
float baseAimTime = ergoData.module.zoomInTimeBase;
float baseAimMultiplier = ergoData.module.aimSpeedModifierBase;
baseAimTime /= baseAimMultiplier;
float modifiedErgo = EffectManager.GetValue(CustomEnums.WeaponErgonomics, rangedData.invData.itemValue, 1f, rangedData.invData.holdingEntity);
modifiedErgo = Mathf.Lerp(0.2f, 1, modifiedErgo);
float perc = (Time.time - ergoData.aimStartTime) * modifiedErgo / baseAimTime;
if (perc >= 1)
{
ergoData.aimSet = true;
perc = 1;
}
//Log.Out($"Time passed {Time.time - dataModule.Instance.aimStartTime} base time {baseAimTime} perc {perc}");
return perc;
}
return originalValue;
}
[HarmonyPatch(typeof(ItemActionRanged), nameof(ItemActionRanged.onHoldingEntityFired))]
[HarmonyPrefix]
private static bool Prefix_onHoldingEntityFired_ItemActionRanged(ItemActionData _actionData, out float __state)
{
__state = (_actionData as ItemActionRanged.ItemActionDataRanged).lastAccuracy;
return true;
}
[HarmonyPatch(typeof(ItemActionRanged), nameof(ItemActionRanged.onHoldingEntityFired))]
[HarmonyPostfix]
private static void Postfix_onHoldingEntityFired_ItemActionRanged(ItemActionData _actionData, float __state)
{
ItemActionRanged.ItemActionDataRanged rangedData = _actionData as ItemActionRanged.ItemActionDataRanged;
if (rangedData.invData.holdingEntity.AimingGun && rangedData.invData.actionData[1] is IModuleContainerFor<ActionModuleErgoAffected.ErgoData> dataModule)
{
float aimMultiplier = EffectManager.GetValue(PassiveEffects.SpreadMultiplierAiming, rangedData.invData.itemValue, .1f, rangedData.invData.holdingEntity);
rangedData.lastAccuracy = Mathf.Lerp(__state, rangedData.lastAccuracy, aimMultiplier);
ActionModuleErgoAffected.ErgoData ergoData = dataModule.Instance;
if (Time.time > ergoData.aimStartTime)
{
ergoData.aimSet = false;
ergoData.aimStartTime = Time.time;
}
}
}
}

View File

@@ -0,0 +1,335 @@
using HarmonyLib;
using KFCommonUtilityLib;
using KFCommonUtilityLib.Scripts.Attributes;
using KFCommonUtilityLib.Scripts.StaticManagers;
using KFCommonUtilityLib.Scripts.Utilities;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
[TypeTarget(typeof(ItemActionRanged)), ActionDataTarget(typeof(FireModeData))]
public class ActionModuleFireModeSelector
{
public struct FireMode
{
public byte burstCount;
public bool isFullAuto;
}
public string fireModeSwitchingSound = null;
private List<FireMode> modeCache = new List<FireMode>();
private List<string> nameCache = new List<string>();
public static string[] FireModeNames = new[]
{
"FireMode",
"FireMode1",
"FireMode2",
"FireMode3",
"FireMode4",
};
public static int[] FireModeParamHashes = new[]
{
Animator.StringToHash("FireMode"),
Animator.StringToHash("FireMode1"),
Animator.StringToHash("FireMode2"),
Animator.StringToHash("FireMode3"),
Animator.StringToHash("FireMode4"),
};
public static int[] FireModeSwitchParamHashes = new[]
{
Animator.StringToHash("FireModeChanged"),
Animator.StringToHash("FireModeChanged1"),
Animator.StringToHash("FireModeChanged2"),
Animator.StringToHash("FireModeChanged3"),
Animator.StringToHash("FireModeChanged4"),
};
[HarmonyPatch(nameof(ItemAction.OnModificationsChanged)), MethodTargetPostfix]
private void Postfix_OnModificationChanged(ItemActionData _data, FireModeData __customData, ItemActionRanged __instance)
{
__instance.Properties.ParseString("FireModeSwitchingSound", ref fireModeSwitchingSound);
int actionIndex = _data.indexInEntityOfAction;
for (int i = 0; i < 99; i++)
{
if (!__instance.Properties.Contains($"FireMode{i}.BurstCount"))
{
break;
}
string burstCount = 1.ToString();
__instance.Properties.ParseString($"FireMode{i}.BurstCount", ref burstCount);
string isFullAuto = false.ToString();
__instance.Properties.ParseString($"FireMode{i}.IsFullAuto", ref isFullAuto);
string modeName = null;
__instance.Properties.ParseString($"FireMode{i}.ModeName", ref modeName);
modeCache.Add(new FireMode
{
burstCount = byte.Parse(_data.invData.itemValue.GetPropertyOverrideForAction($"FireMode{i}.BurstCount", burstCount, actionIndex)),
isFullAuto = bool.Parse(_data.invData.itemValue.GetPropertyOverrideForAction($"FireMode{i}.IsFullAuto", isFullAuto, actionIndex))
});
nameCache.Add(_data.invData.itemValue.GetPropertyOverrideForAction($"FireMode{i}.ModeName", modeName, actionIndex));
}
for (int i = 0; i < 99; i++)
{
string burstCount = _data.invData.itemValue.GetPropertyOverrideForAction($"FireModePlus{i}.BurstCount", null, actionIndex);
if (burstCount == null)
{
break;
}
modeCache.Add(new FireMode
{
burstCount = byte.Parse(_data.invData.itemValue.GetPropertyOverrideForAction($"FireModePlus{i}.BurstCount", burstCount, actionIndex)),
isFullAuto = bool.Parse(_data.invData.itemValue.GetPropertyOverrideForAction($"FireModePlus{i}.IsFullAuto", "false", actionIndex))
});
nameCache.Add(_data.invData.itemValue.GetPropertyOverrideForAction($"FireModePlus{i}.ModeName", null, actionIndex));
}
__customData.fireModes = modeCache.ToArray();
modeCache.Clear();
__customData.modeNames = nameCache.ToArray();
nameCache.Clear();
if (_data.invData.itemValue.GetMetadata(FireModeNames[actionIndex]) is int mode)
{
__customData.currentFireMode = (byte)mode;
}
if (__customData.currentFireMode < 0 || __customData.currentFireMode >= __customData.fireModes.Length)
{
__customData.currentFireMode = 0;
}
if (__customData.delayFiringCo != null)
{
ThreadManager.StopCoroutine(__customData.delayFiringCo);
__customData.delayFiringCo = null;
}
__customData.isRequestedByCoroutine = false;
}
[HarmonyPatch(nameof(ItemAction.StartHolding)), MethodTargetPostfix]
private void Postfix_StartHolding(ItemActionData _data, FireModeData __customData)
{
__customData.SetFireMode(_data, __customData.currentFireMode);
}
[HarmonyPatch(nameof(ItemAction.OnHoldingUpdate)), MethodTargetPostfix]
private static void Postfix_OnHoldingUpdate(ItemActionData _actionData, FireModeData __customData)
{
__customData.UpdateDelay(_actionData);
__customData.inputReleased = true;
}
[HarmonyPatch(nameof(ItemAction.StopHolding)), MethodTargetPostfix]
private static void Postfix_StopHolding(FireModeData __customData)
{
if (__customData.delayFiringCo != null)
{
ThreadManager.StopCoroutine(__customData.delayFiringCo);
__customData.delayFiringCo = null;
}
__customData.isRequestedByCoroutine = false;
__customData.inputReleased = true;
}
[HarmonyPatch(nameof(ItemAction.ExecuteAction)), MethodTargetPrefix]
private bool Prefix_ExecuteAction(ItemActionData _actionData, ItemActionRanged __instance, FireModeData __customData, bool _bReleased)
{
if (__customData.isRequestedByCoroutine)
{
return true;
}
__customData.inputReleased = _bReleased;
if (__customData.delayFiringCo == null)
{
if (_bReleased || _actionData.invData.itemValue.Meta == 0 || _actionData.invData.itemValue.PercentUsesLeft <= 0)
{
return true;
}
FireMode curFireMode = __customData.fireModes[__customData.currentFireMode];
if (curFireMode.burstCount == 1)
{
return true;
}
var rangedData = _actionData as ItemActionRanged.ItemActionDataRanged;
if (__instance.GetBurstCount(_actionData) > rangedData.curBurstCount)
{
__customData.StartFiring(__instance, _actionData);
}
}
return false;
}
[HarmonyPatch(nameof(ItemActionRanged.GetBurstCount)), MethodTargetPostfix]
private void Postfix_GetBurstCount(FireModeData __customData, ref int __result)
{
FireMode fireMode = __customData.fireModes[__customData.currentFireMode];
__result = fireMode.isFullAuto ? 999 : fireMode.burstCount;
}
[HarmonyPatch(nameof(ItemAction.IsActionRunning)), MethodTargetPostfix]
private void Postfix_IsActionRunning(FireModeData __customData, ref bool __result)
{
__result |= __customData.delayFiringCo != null;
}
public class FireModeData
{
public string switchSound;
public FireMode[] fireModes;
public string[] modeNames;
public byte currentFireMode;
public Coroutine delayFiringCo;
public bool isRequestedByCoroutine;
public float shotDelay;
public float burstDelay;
public bool inputReleased;
public FireModeData(ItemInventoryData invData, int actionIndex, ActionModuleFireModeSelector module)
{
}
public void CycleFireMode(ItemActionData _data)
{
SetFireMode(_data, (byte)((currentFireMode + 1) % fireModes.Length));
}
public void SetFireMode(ItemActionData _data, byte _fireMode)
{
if (currentFireMode != _fireMode)
{
currentFireMode = _fireMode;
FireMode curFireMode = fireModes[currentFireMode];
if (!string.IsNullOrEmpty(switchSound))
{
_data.invData.holdingEntity.PlayOneShot(switchSound);
}
_data.invData.holdingEntity.emodel.avatarController.TriggerEvent(FireModeSwitchParamHashes[_data.indexInEntityOfAction]);
}
if (!string.IsNullOrEmpty(modeNames[_fireMode]))
{
GameManager.ShowTooltip(_data.invData.holdingEntity as EntityPlayerLocal, modeNames[_fireMode], true);
}
else
{
GameManager.ShowTooltip(_data.invData.holdingEntity as EntityPlayerLocal, "ttCurrentFiringMode", _fireMode.ToString(), null, null, true);
}
//GameManager.ShowTooltip(_data.invData.holdingEntity as EntityPlayerLocal, "ttCurrentFiringMode", string.IsNullOrEmpty(modeNames[_fireMode]) ? _fireMode.ToString() : Localization.Get(modeNames[_fireMode]), null, null, true);
_data.invData.holdingEntity.FireEvent(CustomEnums.onSelfBurstModeChanged);
UpdateDelay(_data);
ItemValue itemValue = _data.invData.itemValue;
if (itemValue != null)
{
if (itemValue.Metadata == null)
{
itemValue.Metadata = new Dictionary<string, TypedMetadataValue>();
}
if (!itemValue.Metadata.TryGetValue(ActionModuleFireModeSelector.FireModeNames[_data.indexInEntityOfAction], out var metadata) || !metadata.SetValue((int)_fireMode))
{
itemValue.Metadata[ActionModuleFireModeSelector.FireModeNames[_data.indexInEntityOfAction]] = new TypedMetadataValue((int)_fireMode, TypedMetadataValue.TypeTag.Integer);
}
}
}
public void UpdateDelay(ItemActionData _data)
{
FireMode curFireMode = fireModes[currentFireMode];
if (curFireMode.burstCount == 1)
{
return;
}
float burstInterval = EffectManager.GetValue(CustomEnums.BurstShotInterval, _data.invData.itemValue, -1, _data.invData.holdingEntity);
var rangedData = _data as ItemActionRanged.ItemActionDataRanged;
if (burstInterval > 0 && rangedData.Delay > burstInterval)
{
shotDelay = burstInterval;
burstDelay = (rangedData.Delay - burstInterval) * curFireMode.burstCount;
}
else
{
shotDelay = rangedData.Delay;
burstDelay = 0;
}
}
public void StartFiring(ItemActionRanged _instance, ItemActionData _data)
{
UpdateDelay(_data);
if (delayFiringCo != null)
{
ThreadManager.StopCoroutine(delayFiringCo);
}
((ItemActionRanged.ItemActionDataRanged)_data).bPressed = true;
((ItemActionRanged.ItemActionDataRanged)_data).bReleased = false;
delayFiringCo = ThreadManager.StartCoroutine(DelayFiring(_instance, _data));
}
private IEnumerator DelayFiring(ItemActionRanged _instance, ItemActionData _data)
{
FireMode curFireMode = fireModes[currentFireMode];
var rangedData = _data as ItemActionRanged.ItemActionDataRanged;
byte curBurstCount = rangedData.curBurstCount;
for (int i = 0; i < curFireMode.burstCount; i++)
{
isRequestedByCoroutine = true;
rangedData.bPressed = true;
rangedData.bReleased = false;
rangedData.m_LastShotTime = 0;
_instance.ExecuteAction(_data, false);
rangedData.curBurstCount = (byte)(curBurstCount + i + 1);
isRequestedByCoroutine = false;
if (rangedData.invData.itemValue.Meta <= 0 && !_instance.HasInfiniteAmmo(_data))
{
goto cleanup;
}
yield return new WaitForSeconds(shotDelay);
}
yield return new WaitForSeconds(burstDelay);
cleanup:
delayFiringCo = null;
if (inputReleased)
{
_instance.ExecuteAction(_data, true);
}
}
}
}
[HarmonyPatch]
public static class FireModePatches
{
[HarmonyPatch(typeof(PlayerMoveController), nameof(PlayerMoveController.Update))]
[HarmonyPrefix]
private static bool Prefix_Update_PlayerMoveController(PlayerMoveController __instance)
{
if (DroneManager.Debug_LocalControl || !__instance.gameManager.gameStateManager.IsGameStarted() || GameStats.GetInt(EnumGameStats.GameState) != 1)
return true;
bool isUIOpen = __instance.windowManager.IsCursorWindowOpen() || __instance.windowManager.IsInputActive() || __instance.windowManager.IsModalWindowOpen();
UpdateLocalInput(__instance.entityPlayerLocal, __instance.playerInput, isUIOpen, Time.deltaTime);
return true;
}
private static void UpdateLocalInput(EntityPlayerLocal _player, PlayerActionsLocal _input, bool _isUIOpen, float _deltaTime)
{
if (_isUIOpen || _player.emodel.IsRagdollActive || _player.IsDead() || _player.AttachedToEntity != null)
{
return;
}
if (PlayerActionToggleFireMode.Instance.Enabled && PlayerActionToggleFireMode.Instance.Toggle.WasPressed)
{
if (_player.inventory.IsHoldingItemActionRunning())
{
return;
}
var actionData = _player.inventory.holdingItemData.actionData[MultiActionManager.GetActionIndexForEntity(_player)];
if (actionData is IModuleContainerFor<ActionModuleFireModeSelector.FireModeData> fireModeData)
{
fireModeData.Instance.CycleFireMode(actionData);
}
}
}
}

View File

@@ -0,0 +1,85 @@
using HarmonyLib;
using KFCommonUtilityLib.Scripts.Attributes;
using System.Collections;
using UnityEngine;
[TypeTarget(typeof(ItemActionRanged))]
public class ActionModuleHoldOpen
{
private const string emptyAnimatorBool = "empty";
private int emptyAnimatorBoolHash;
[HarmonyPatch(nameof(ItemAction.ReadFrom)), MethodTargetPostfix]
private void Postfix_ReadFrom(DynamicProperties _props, ItemActionRanged __instance)
{
int metaIndex = __instance.ActionIndex;
if (_props.Values.TryGetValue("ShareMetaWith", out string str) && int.TryParse(str, out metaIndex))
{
}
if (metaIndex > 0)
{
emptyAnimatorBoolHash = Animator.StringToHash(emptyAnimatorBool + __instance.ActionIndex);
}
else
{
emptyAnimatorBoolHash = Animator.StringToHash(emptyAnimatorBool);
}
}
[HarmonyPatch(nameof(ItemActionRanged.getUserData)), MethodTargetPostfix]
public void Postfix_getUserData(ItemActionData _actionData, ref int __result)
{
__result |= (_actionData.invData.itemValue.Meta <= 0 ? 1 : 0);
}
[HarmonyPatch(nameof(ItemAction.ItemActionEffects)), MethodTargetPostfix]
public void Postfix_ItemActionEffects(ItemActionData _actionData, int _firingState, int _userData)
{
if (_firingState != (int)ItemActionFiringState.Off && (_userData & 1) > 0)
_actionData.invData.holdingEntity.emodel.avatarController.UpdateBool(emptyAnimatorBoolHash, true, false);
}
[HarmonyPatch(nameof(ItemActionRanged.ReloadGun)), MethodTargetPostfix]
public void Postfix_ReloadGun(ItemActionData _actionData)
{
//delay 2 frames before reloading, since the animation is likely to be triggered the next frame this is called
ThreadManager.StartCoroutine(DelaySetEmpty(_actionData, false, 2));
}
[HarmonyPatch(nameof(ItemAction.StartHolding)), MethodTargetPrefix]
public bool Prefix_StartHolding(ItemActionData _data)
{
//delay 1 frame before equipping weapon
if (_data.invData.itemValue.Meta <= 0)
ThreadManager.StartCoroutine(DelaySetEmpty(_data, true, 2));
return true;
}
[HarmonyPatch(nameof(ItemActionRanged.ConsumeAmmo)), MethodTargetPostfix]
public void Postfix_ConsumeAmmo(ItemActionData _actionData)
{
if (_actionData.invData.itemValue.Meta == 0)
_actionData.invData.holdingEntity.FireEvent(CustomEnums.onSelfMagzineDeplete, true);
}
[HarmonyPatch(nameof(ItemAction.SwapAmmoType)), MethodTargetPrefix]
public bool Prefix_SwapAmmoType(EntityAlive _entity)
{
_entity.emodel.avatarController.UpdateBool(emptyAnimatorBoolHash, true, false);
return true;
}
private IEnumerator DelaySetEmpty(ItemActionData _actionData, bool empty, int delay)
{
for (int i = 0; i < delay; i++)
{
yield return null;
}
if (_actionData.invData.holdingEntity.inventory.holdingItemIdx == _actionData.invData.slotIdx)
{
_actionData.invData.holdingEntity.emodel.avatarController.UpdateBool(emptyAnimatorBoolHash, empty, false);
}
yield break;
}
}

View File

@@ -0,0 +1,24 @@
using HarmonyLib;
using KFCommonUtilityLib.Scripts.Attributes;
[TypeTarget(typeof(ItemAction))]
public class ActionModuleInspectable
{
public bool allowEmptyInspect;
[HarmonyPatch(nameof(ItemAction.ReadFrom)), MethodTargetPostfix]
private void Postfix_ReadFrom(DynamicProperties _props)
{
allowEmptyInspect = _props.GetBool("allowEmptyInspect");
}
[HarmonyPatch(typeof(ItemActionDynamic), nameof(ItemAction.CancelAction)), MethodTargetPostfix]
private void Postfix_CancelAction_ItemActionDynamic(ItemActionDynamic.ItemActionDynamicData _actionData)
{
var entity = _actionData.invData.holdingEntity;
if (!entity.MovementRunning && _actionData != null && !entity.inventory.holdingItem.IsActionRunning(entity.inventory.holdingItemData))
{
entity.emodel.avatarController._setTrigger("weaponInspect", false);
}
}
}

View File

@@ -0,0 +1,225 @@
using HarmonyLib;
using KFCommonUtilityLib;
using KFCommonUtilityLib.Scripts.Attributes;
using KFCommonUtilityLib.Scripts.StaticManagers;
using UnityEngine;
[TypeTarget(typeof(ItemActionRanged)), ActionDataTarget(typeof(InterruptData))]
public class ActionModuleInterruptReload
{
public float holdBeforeCancel = 0.06f;
public string firingStateName = "";
public bool instantFiringCancel = false;
[HarmonyPatch(nameof(ItemAction.StartHolding)), MethodTargetPrefix]
private bool Prefix_StartHolding(InterruptData __customData)
{
__customData.Reset();
return true;
}
[HarmonyPatch(nameof(ItemAction.ReadFrom)), MethodTargetPostfix]
private void Postfix_ReadFrom(DynamicProperties _props)
{
firingStateName = _props.GetString("FiringStateFullName");
instantFiringCancel = _props.GetBool("InstantFiringCancel");
}
[HarmonyPatch(nameof(ItemAction.OnModificationsChanged)), MethodTargetPostfix]
private void Postfix_OnModificationsChanged(ItemActionData _data, InterruptData __customData)
{
var invData = _data.invData;
__customData.itemAnimator = AnimationGraphBuilder.DummyWrapper;
__customData.eventBridge = null;
if (invData.model && invData.model.TryGetComponent<AnimationTargetsAbs>(out var targets) && !targets.Destroyed && targets.IsAnimationSet)
{
__customData.itemAnimator = targets.GraphBuilder.WeaponWrapper;
if (__customData.itemAnimator.IsValid)
{
__customData.eventBridge = targets.ItemAnimator.GetComponent<AnimationReloadEvents>();
}
}
}
private struct State
{
public bool executed;
public bool isReloading;
public bool isWeaponReloading;
public float lastShotTime;
}
[HarmonyPatch(nameof(ItemAction.IsActionRunning)), MethodTargetPostfix]
private void Postfix_IsActionRunning(ref bool __result, InterruptData __customData)
{
__result &= !__customData.instantFiringRequested;
}
[HarmonyPatch(nameof(ItemAction.ExecuteAction)), MethodTargetPrefix]
private bool Prefix_ExecuteAction(ItemActionData _actionData, bool _bReleased, InterruptData __customData, out State __state)
{
__state = default;
if (!_bReleased && __customData.isInterruptRequested && __customData.instantFiringRequested)
{
if (_actionData.invData.itemValue.Meta > 0)
{
if (ConsoleCmdReloadLog.LogInfo)
Log.Out($"instant firing cancel prefix!");
ItemActionRanged.ItemActionDataRanged rangedData = _actionData as ItemActionRanged.ItemActionDataRanged;
__state.executed = true;
__state.isReloading = rangedData.isReloading;
__state.isWeaponReloading = rangedData.isWeaponReloading;
__state.lastShotTime = rangedData.m_LastShotTime;
rangedData.isReloading = false;
rangedData.isWeaponReloading = false;
}
else
{
if (ConsoleCmdReloadLog.LogInfo)
Log.Out($"not fired! meta is 0");
__customData.isInterruptRequested = false;
__customData.instantFiringRequested = false;
return false;
}
}
return true;
}
[HarmonyPatch(nameof(ItemAction.ExecuteAction)), MethodTargetPostfix]
private void Postfix_ExecuteAction(ItemActionData _actionData, InterruptData __customData, State __state)
{
if (__state.executed)
{
if (ConsoleCmdReloadLog.LogInfo)
Log.Out($"instant firing cancel postfix!");
ItemActionRanged.ItemActionDataRanged rangedData = _actionData as ItemActionRanged.ItemActionDataRanged;
rangedData.isReloading = __state.isReloading;
rangedData.isWeaponReloading = __state.isWeaponReloading;
if (__customData.itemAnimator.IsValid && __customData.eventBridge)
{
if (rangedData.m_LastShotTime > __state.lastShotTime && rangedData.m_LastShotTime < Time.time + 1f)
{
if (ConsoleCmdReloadLog.LogInfo)
Log.Out($"executed!");
__customData.eventBridge.OnReloadEnd();
__customData.itemAnimator.Play(firingStateName, -1, 0f);
}
else
{
if (ConsoleCmdReloadLog.LogInfo)
Log.Out($"not fired! last shot time {__state.lastShotTime} ranged data shot time {rangedData.m_LastShotTime} cur time {Time.time}");
__customData.isInterruptRequested = false;
__customData.instantFiringRequested = false;
}
}
}
}
[HarmonyPatch(nameof(ItemAction.ItemActionEffects)), MethodTargetPrefix]
private bool Prefix_ItemActionEffects(ItemActionData _actionData, int _firingState, InterruptData __customData)
{
var rangedData = _actionData as ItemActionRanged.ItemActionDataRanged;
if (_firingState != 0 && (rangedData.isReloading || rangedData.isWeaponReloading) && !(rangedData.invData.holdingEntity is EntityPlayerLocal) && __customData.eventBridge)
{
__customData.eventBridge.OnReloadEnd();
__customData.itemAnimator.Play(firingStateName, -1, 0f);
}
return true;
}
public bool IsRequestPossible(InterruptData interruptData)
{
return interruptData.eventBridge && interruptData.itemAnimator.IsValid;
}
public class InterruptData
{
public bool isInterruptRequested;
public float holdStartTime = -1f;
public bool instantFiringRequested = false;
public AnimationReloadEvents eventBridge;
public IAnimatorWrapper itemAnimator;
public InterruptData(ItemInventoryData invData, int actionIndex, ActionModuleInterruptReload module)
{
//if (invData.model && invData.model.TryGetComponent<AnimationTargetsAbs>(out var targets) && !targets.Destroyed)
//{
// itemAnimator = targets.ItemAnimator;
// if (itemAnimator)
// {
// eventBridge = itemAnimator.GetComponent<AnimationReloadEvents>();
// }
//}
}
public void Reset()
{
isInterruptRequested = false;
holdStartTime = -1f;
instantFiringRequested = false;
}
}
}
[HarmonyPatch]
internal static class ReloadInterruptionPatches
{
//interrupt reload with firing
[HarmonyPatch(typeof(ItemClass), nameof(ItemClass.ExecuteAction))]
[HarmonyPrefix]
private static bool Prefix_ExecuteAction_ItemClass(ItemClass __instance, int _actionIdx, ItemInventoryData _data, bool _bReleased, PlayerActionsLocal _playerActions)
{
ItemAction curAction = __instance.Actions[_actionIdx];
if (curAction is ItemActionRanged || curAction is ItemActionZoom)
{
int curActionIndex = MultiActionManager.GetActionIndexForEntity(_data.holdingEntity);
var rangedAction = __instance.Actions[curActionIndex] as ItemActionRanged;
var rangedData = _data.actionData[curActionIndex] as ItemActionRanged.ItemActionDataRanged;
if (rangedData != null && rangedData is IModuleContainerFor<ActionModuleInterruptReload.InterruptData> dataModule && rangedAction is IModuleContainerFor<ActionModuleInterruptReload> actionModule)
{
if (!_bReleased && _playerActions != null && actionModule.Instance.IsRequestPossible(dataModule.Instance) && ((_playerActions.Primary.IsPressed && _actionIdx == curActionIndex && _data.itemValue.Meta > 0) || (_playerActions.Secondary.IsPressed && curAction is ItemActionZoom)) && (rangedData.isReloading || rangedData.isWeaponReloading) && !dataModule.Instance.isInterruptRequested)
{
if (dataModule.Instance.holdStartTime < 0)
{
dataModule.Instance.holdStartTime = Time.time;
return false;
}
if (Time.time - dataModule.Instance.holdStartTime >= actionModule.Instance.holdBeforeCancel)
{
if (!rangedAction.reloadCancelled(rangedData))
{
rangedAction.CancelReload(rangedData);
}
if (ConsoleCmdReloadLog.LogInfo)
Log.Out($"interrupt requested!");
dataModule.Instance.isInterruptRequested = true;
if (actionModule.Instance.instantFiringCancel && curAction is ItemActionRanged)
{
if (ConsoleCmdReloadLog.LogInfo)
Log.Out($"instant firing cancel!");
dataModule.Instance.instantFiringRequested = true;
return true;
}
}
return false;
}
if (_bReleased)
{
dataModule.Instance.Reset();
}
}
}
return true;
}
[HarmonyPatch(typeof(ItemAction), nameof(ItemAction.CancelReload))]
[HarmonyPrefix]
private static bool Prefix_CancelReload_ItemAction(ItemActionData _actionData)
{
if (_actionData?.invData?.holdingEntity is EntityPlayerLocal && AnimationRiggingManager.IsHoldingRiggedWeapon(_actionData.invData.holdingEntity as EntityPlayerLocal))
{
return false;
}
return true;
}
}

View File

@@ -0,0 +1,60 @@
using HarmonyLib;
using KFCommonUtilityLib.Scripts.Attributes;
using KFCommonUtilityLib.Scripts.StaticManagers;
using System.Collections.Generic;
using System.Reflection.Emit;
using UniLinq;
[TypeTarget(typeof(ItemActionRanged))]
public class ActionModuleInvariableRPM
{
//added as a transpiler so that it's applied before all post processing
[HarmonyPatch(typeof(ItemActionRanged), nameof(ItemAction.OnHoldingUpdate)), MethodTargetTranspiler]
private static IEnumerable<CodeInstruction> Transpiler_OnHoldingUpdate_ItemActionRanged(IEnumerable<CodeInstruction> instructions)
{
var codes = instructions.ToList();
var mtd_getvalue = AccessTools.Method(typeof(EffectManager), nameof(EffectManager.GetValue));
for (int i = 0; i < codes.Count; i++)
{
if (codes[i].Calls(mtd_getvalue))
{
int start = -1;
for (int j = i; j >= 0; j--)
{
if (codes[j].opcode == OpCodes.Stloc_0)
{
start = j + 2;
break;
}
}
if (start >= 0)
{
codes.InsertRange(i + 2, new[]
{
new CodeInstruction(OpCodes.Ldarg_0),
new CodeInstruction(OpCodes.Ldloc_0),
CodeInstruction.Call(typeof(ActionModuleInvariableRPM), nameof(CalcFixedRPM))
});
codes.RemoveRange(start, i - start + 2);
//Log.Out("Invariable RPM Patch applied!");
}
break;
}
}
return codes;
}
private static float CalcFixedRPM(ItemActionRanged rangedAction, ItemActionRanged.ItemActionDataRanged rangedData)
{
float rpm = 60f / rangedData.OriginalDelay;
float perc = 1f;
var tags = rangedData.invData.item.ItemTags;
MultiActionManager.ModifyItemTags(rangedData.invData.itemValue, rangedData, ref tags);
rangedData.invData.item.Effects.ModifyValue(rangedData.invData.holdingEntity, PassiveEffects.RoundsPerMinute, ref rpm, ref perc, rangedData.invData.itemValue.Quality, tags);
//Log.Out($"fixed RPM {res}");
return 60f / (rpm * perc);
}
}

View File

@@ -0,0 +1,105 @@
using KFCommonUtilityLib.Scripts.Attributes;
using KFCommonUtilityLib.Scripts.StaticManagers;
using System;
using System.Collections;
using System.Collections.Generic;
[TypeTarget(typeof(ItemAction)), ActionDataTarget(typeof(LocalPassiveCacheData))]
public class ActionModuleLocalPassiveCache
{
//public int[] nameHashes;
//[MethodTargetPostfix(nameof(ItemAction.ReadFrom))]
//private void Postfix_ReadFrom(DynamicProperties _props)
//{
// string str = _props.Values["CachePassives"];
// if (!string.IsNullOrEmpty(str))
// {
// nameHashes = Array.ConvertAll(str.Split(new[] { ',' }, StringSplitOptions.RemoveEmptyEntries), s => s.GetHashCode());
// }
//}
//[MethodTargetPrefix(nameof(ItemAction.StartHolding))]
//private bool Prefix_StartHolding(ItemActionData _data, LocalPassiveCacheData __customData)
//{
// if (nameHashes != null)
// {
// for (int i = 0; i < nameHashes.Length; i++)
// {
// __customData.passives[i] = EffectManager.GetValue(nameHashes[i], _data.invData.itemValue, 0, _data.invData.holdingEntity);
// __customData.markedForCache[i] = false;
// }
// }
// return true;
//}
//[MethodTargetPrefix(nameof(ItemAction.OnHoldingUpdate))]
//private bool Prefix_OnHoldingUpdate(ItemActionData _actionData, LocalPassiveCacheData __customData)
//{
// if (!_actionData.invData.holdingEntity.isEntityRemote && nameHashes != null)
// {
// for (int i = 0; i < nameHashes.Length; i++)
// {
// if (__customData.markedForCache[i])
// {
// __customData.cache[i] = EffectManager.GetValue(nameHashes[i], _actionData.invData.itemValue, 0, _actionData.invData.holdingEntity);
// __customData.markedForCache[i] = false;
// }
// }
// }
// return true;
//}
public class LocalPassiveCacheData : IEnumerable<int>
{
//public float[] cache;
//public bool[] markedForCache;
//public ActionModuleLocalPassiveCache _cacheModule;
public ItemInventoryData invData;
private Dictionary<int, float> dict_hash_value = new Dictionary<int, float>();
private Dictionary<int, string> dict_hash_name = new Dictionary<int, string>();
public LocalPassiveCacheData(ItemInventoryData _invData, int _indexOfAction, ActionModuleLocalPassiveCache _cacheModule)
{
//this._cacheModule = _cacheModule;
this.invData = _invData;
//if (_cacheModule.nameHashes != null)
//{
// cache = new float[_cacheModule.nameHashes.Length];
// //markedForCache = new bool[_cacheModule.nameHashes.Length];
//}
}
public void CachePassive(PassiveEffects target, int targetHash, string targetStr, FastTags<TagGroup.Global> tags)
{
if (invData.holdingEntity.isEntityRemote)
return;
if (!dict_hash_name.ContainsKey(targetHash))
dict_hash_name[targetHash] = targetStr;
dict_hash_value[targetHash] = EffectManager.GetValue(target, invData.itemValue, 0, invData.holdingEntity, null, tags);
//markedForCache[index] = true;
}
public float GetCachedValue(int targetHash)
{
return dict_hash_value.TryGetValue(targetHash, out float res) ? res : 0;
}
public string GetCachedName(int targetHash)
{
return dict_hash_name.TryGetValue(targetHash, out string res) ? res : string.Empty;
}
public IEnumerator<int> GetEnumerator()
{
return dict_hash_value.Keys.GetEnumerator();
}
IEnumerator IEnumerable.GetEnumerator()
{
return GetEnumerator();
}
}
}

View File

@@ -0,0 +1,178 @@
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;
//}
}

View File

@@ -0,0 +1,166 @@
using HarmonyLib;
using KFCommonUtilityLib.Scripts.Attributes;
using KFCommonUtilityLib.Scripts.StaticManagers;
using System;
using UniLinq;
using UnityEngine;
[TypeTarget(typeof(ItemActionRanged)), ActionDataTarget(typeof(MetaRechargerData))]
public class ActionModuleMetaRecharger
{
public struct RechargeTags
{
public FastTags<TagGroup.Global> tagsOriginal;
public FastTags<TagGroup.Global> tagsInterval;
public FastTags<TagGroup.Global> tagsMaximum;
public FastTags<TagGroup.Global> tagsValue;
public FastTags<TagGroup.Global> tagsDecrease;
public FastTags<TagGroup.Global> tagsDecreaseInterval;
}
public string[] rechargeDatas;
public RechargeTags[] rechargeTags;
private static readonly FastTags<TagGroup.Global> TagsInterval = FastTags<TagGroup.Global>.Parse("RechargeDataInterval");
private static readonly FastTags<TagGroup.Global> TagsMaximum = FastTags<TagGroup.Global>.Parse("RechargeDataMaximum");
private static readonly FastTags<TagGroup.Global> TagsValue = FastTags<TagGroup.Global>.Parse("RechargeDataValue");
private static readonly FastTags<TagGroup.Global> TagsDecrease = FastTags<TagGroup.Global>.Parse("RechargeDataDecrease");
private static readonly FastTags<TagGroup.Global> TagsDecreaseInterval = FastTags<TagGroup.Global>.Parse("RechargeDecreaseInterval");
[HarmonyPatch(nameof(ItemAction.ReadFrom)), MethodTargetPostfix]
private void Postfix_ReadFrom(DynamicProperties _props, ItemAction __instance)
{
rechargeDatas = null;
rechargeTags = null;
string rechargeData = string.Empty;
_props.Values.TryGetValue("RechargeData", out rechargeData);
_props.Values.TryGetValue("RechargeTags", out string tags);
FastTags<TagGroup.Global> commonTags = string.IsNullOrEmpty(tags) ? FastTags<TagGroup.Global>.none : FastTags<TagGroup.Global>.Parse(tags);
if (string.IsNullOrEmpty(rechargeData))
{
Log.Error($"No recharge data found on item {__instance.item.Name} action {__instance.ActionIndex}");
return;
}
rechargeDatas = rechargeData.Split(new char[] { ',' }, StringSplitOptions.RemoveEmptyEntries).Select(s => s.Trim()).ToArray();
rechargeTags = rechargeDatas.Select(s =>
{
var _tags = FastTags<TagGroup.Global>.Parse(s) | commonTags;
return new RechargeTags
{
tagsOriginal = _tags,
tagsInterval = _tags | TagsInterval,
tagsMaximum = _tags | TagsMaximum,
tagsValue = _tags | TagsValue,
tagsDecrease = _tags | TagsDecrease,
tagsDecreaseInterval = _tags | TagsDecreaseInterval,
};
}).ToArray();
}
[HarmonyPatch(nameof(ItemAction.StartHolding)), MethodTargetPrefix]
private bool Prefix_StartHolding(ItemActionData _data, MetaRechargerData __customData)
{
EntityAlive holdingEntity = _data.invData.holdingEntity;
if (holdingEntity.isEntityRemote)
return true;
for (int i = 0; i < rechargeDatas.Length; i++)
{
holdingEntity.MinEventContext.Tags = rechargeTags[i].tagsOriginal;
holdingEntity.FireEvent(CustomEnums.onRechargeValueUpdate, true);
}
return true;
}
public class MetaRechargerData : IBackgroundInventoryUpdater
{
private ActionModuleMetaRecharger module;
private float lastUpdateTime, lastDecreaseTime;
private int indexOfAction;
public int Index => indexOfAction;
public MetaRechargerData(ItemInventoryData _invData, int _indexOfAction, ActionModuleMetaRecharger _rechargeModule)
{
module = _rechargeModule;
indexOfAction = _indexOfAction;
lastUpdateTime = lastDecreaseTime = Time.time;
if (_rechargeModule.rechargeDatas == null)
return;
BackgroundInventoryUpdateManager.RegisterUpdater(_invData.holdingEntity, _invData.slotIdx, this);
}
public bool OnUpdate(ItemInventoryData invData)
{
ItemValue itemValue = invData.itemValue;
EntityAlive holdingEntity = invData.holdingEntity;
holdingEntity.MinEventContext.ItemInventoryData = invData;
holdingEntity.MinEventContext.ItemValue = itemValue;
holdingEntity.MinEventContext.ItemActionData = invData.actionData[indexOfAction];
float curTime = Time.time;
bool res = false;
for (int i = 0; i < module.rechargeDatas.Length; i++)
{
string rechargeData = module.rechargeDatas[i];
RechargeTags rechargeTag = module.rechargeTags[i];
float updateInterval = EffectManager.GetValue(CustomEnums.CustomTaggedEffect, itemValue, float.MaxValue, holdingEntity, null, rechargeTag.tagsInterval);
float decreaseInterval = EffectManager.GetValue(CustomEnums.CustomTaggedEffect, itemValue, float.MaxValue, holdingEntity, null, rechargeTag.tagsDecreaseInterval);
float deltaTime = curTime - lastUpdateTime;
float deltaDecreaseTime = curTime - lastDecreaseTime;
if (deltaTime > updateInterval || deltaDecreaseTime > decreaseInterval)
{
//Log.Out($"last update time {lastUpdateTime} cur time {curTime} update interval {updateInterval}");
float cur;
if (!itemValue.HasMetadata(rechargeData))
{
itemValue.SetMetadata(rechargeData, 0, TypedMetadataValue.TypeTag.Float);
cur = 0;
}
else
{
cur = (float)itemValue.GetMetadata(rechargeData);
}
float max = EffectManager.GetValue(CustomEnums.CustomTaggedEffect, itemValue, 0, holdingEntity, null, rechargeTag.tagsMaximum);
bool modified = false;
if (cur > max)
{
if (deltaDecreaseTime > decreaseInterval)
{
//the result updated here won't exceed max so it's set somewhere else, decrease slowly
float dec = EffectManager.GetValue(CustomEnums.CustomTaggedEffect, itemValue, float.MaxValue, holdingEntity, null, rechargeTag.tagsDecrease);
cur = Mathf.Max(cur - dec, max);
lastDecreaseTime = curTime;
modified = true;
}
lastUpdateTime = curTime;
}
else
{
if (cur < max && deltaTime > updateInterval)
{
//add up and clamp to max
float add = EffectManager.GetValue(CustomEnums.CustomTaggedEffect, itemValue, 0, holdingEntity, null, rechargeTag.tagsValue);
cur = Mathf.Min(cur + add, max);
lastUpdateTime = curTime;
modified = true;
}
//always set lastDecreaseTime if not overcharged, since we don't want overcharged data to decrease right after it's charged
lastDecreaseTime = curTime;
}
if (modified)
{
itemValue.SetMetadata(rechargeData, cur, TypedMetadataValue.TypeTag.Float);
}
if (invData.slotIdx == holdingEntity.inventory.holdingItemIdx && invData.slotIdx >= 0)
{
holdingEntity.MinEventContext.Tags = rechargeTag.tagsOriginal;
itemValue.FireEvent(CustomEnums.onRechargeValueUpdate, holdingEntity.MinEventContext);
//Log.Out($"action index is {holdingEntity.MinEventContext.ItemActionData.indexInEntityOfAction} after firing event");
}
res |= modified;
}
}
return res;
}
}
}

View File

@@ -0,0 +1,222 @@
using HarmonyLib;
using KFCommonUtilityLib.Scripts.Attributes;
using KFCommonUtilityLib.Scripts.StaticManagers;
using KFCommonUtilityLib.Scripts.Utilities;
[TypeTarget(typeof(ItemActionAttack)), ActionDataTarget(typeof(MultiActionData))]
public class ActionModuleMultiActionFix
{
private int actionIndex;
public string GetDisplayType(ItemValue itemValue)
{
string displayType = itemValue.GetPropertyOverrideForAction("DisplayType", null, actionIndex);
if (string.IsNullOrEmpty(displayType))
{
displayType = itemValue.ItemClass.DisplayType;
}
return displayType;
}
[HarmonyPatch(nameof(ItemAction.ReadFrom)), MethodTargetPostfix]
public void Postfix_ReadFrom(ItemActionAttack __instance)
{
actionIndex = __instance.ActionIndex;
}
[HarmonyPatch(nameof(ItemAction.StartHolding)), MethodTargetPrefix]
public bool Prefix_StartHolding(ItemActionData _data, out ItemActionData __state)
{
SetAndSaveItemActionData(_data, out __state);
return true;
}
[HarmonyPatch(nameof(ItemAction.StartHolding)), MethodTargetPostfix]
public void Postfix_StartHolding(ItemActionData _data, ItemActionData __state)
{
RestoreItemActionData(_data, __state);
}
[HarmonyPatch(typeof(ItemActionRanged), nameof(ItemAction.OnModificationsChanged)), MethodTargetPostfix]
public void Postfix_OnModificationChanged_ItemActionRanged(ItemActionData _data, ItemActionAttack __instance)
{
var rangedData = _data as ItemActionRanged.ItemActionDataRanged;
if (rangedData != null)
{
string muzzleName;
string indexExtension = (_data.indexInEntityOfAction > 0 ? _data.indexInEntityOfAction.ToString() : "");
if (rangedData.IsDoubleBarrel)
{
muzzleName = _data.invData.itemValue.GetPropertyOverrideForAction($"Muzzle_L_Name", $"Muzzle_L{indexExtension}", _data.indexInEntityOfAction);
rangedData.muzzle = AnimationRiggingManager.GetTransformOverrideByName(rangedData.invData.model, muzzleName) ?? rangedData.muzzle;
muzzleName = _data.invData.itemValue.GetPropertyOverrideForAction($"Muzzle_R_Name", $"Muzzle_R{indexExtension}", _data.indexInEntityOfAction);
rangedData.muzzle2 = AnimationRiggingManager.GetTransformOverrideByName(rangedData.invData.model, muzzleName) ?? rangedData.muzzle2;
}
else
{
muzzleName = _data.invData.itemValue.GetPropertyOverrideForAction($"Muzzle_Name", $"Muzzle{indexExtension}", _data.indexInEntityOfAction);
rangedData.muzzle = AnimationRiggingManager.GetTransformOverrideByName(rangedData.invData.model, muzzleName) ?? rangedData.muzzle;
}
}
}
[HarmonyPatch(typeof(ItemActionLauncher), nameof(ItemAction.OnModificationsChanged)), MethodTargetPostfix]
public void Postfix_OnModificationChanged_ItemActionLauncher(ItemActionData _data, ItemActionAttack __instance)
{
Postfix_OnModificationChanged_ItemActionRanged(_data, __instance);
if (_data is ItemActionLauncher.ItemActionDataLauncher launcherData)
{
string indexExtension = (_data.indexInEntityOfAction > 0 ? _data.indexInEntityOfAction.ToString() : "");
string jointName = _data.invData.itemValue.GetPropertyOverrideForAction($"ProjectileJoint_Name", $"ProjectileJoint{indexExtension}", _data.indexInEntityOfAction);
launcherData.projectileJoint = AnimationRiggingManager.GetTransformOverrideByName(launcherData.invData.model, jointName) ?? launcherData.projectileJoint;
}
}
[HarmonyPatch(nameof(ItemAction.StopHolding)), MethodTargetPrefix]
public bool Prefix_StopHolding(ItemActionData _data, out ItemActionData __state)
{
SetAndSaveItemActionData(_data, out __state);
return true;
}
[HarmonyPatch(nameof(ItemAction.StopHolding)), MethodTargetPostfix]
public void Postfix_StopHolding(ItemActionData _data, ItemActionData __state)
{
RestoreItemActionData(_data, __state);
}
[HarmonyPatch(typeof(ItemActionLauncher), nameof(ItemAction.ItemActionEffects)), MethodTargetPrefix]
public bool Prefix_ItemActionEffects(ItemActionData _actionData, out ItemActionData __state)
{
SetAndSaveItemActionData(_actionData, out __state);
return true;
}
[HarmonyPatch(typeof(ItemActionLauncher), nameof(ItemAction.ItemActionEffects)), MethodTargetPostfix]
public void Postfix_ItemActionEffects(ItemActionData _actionData, ItemActionData __state)
{
RestoreItemActionData(_actionData, __state);
}
[HarmonyPatch(nameof(ItemAction.CancelAction)), MethodTargetPrefix]
public bool Prefix_CancelAction(ItemActionData _actionData, out ItemActionData __state)
{
SetAndSaveItemActionData(_actionData, out __state);
return true;
}
[HarmonyPatch(nameof(ItemAction.CancelAction)), MethodTargetPostfix]
public void Postfix_CancelAction(ItemActionData _actionData, ItemActionData __state)
{
RestoreItemActionData(_actionData, __state);
}
[HarmonyPatch(nameof(ItemActionAttack.CancelReload)), MethodTargetPrefix]
public bool Prefix_CancelReload(ItemActionData _actionData, out ItemActionData __state)
{
SetAndSaveItemActionData(_actionData, out __state);
return true;
}
[HarmonyPatch(nameof(ItemActionAttack.CancelReload)), MethodTargetPostfix]
public void Postfix_CancelReload(ItemActionData _actionData, ItemActionData __state)
{
RestoreItemActionData(_actionData, __state);
}
[HarmonyPatch(nameof(ItemActionAttack.ReloadGun)), MethodTargetPrefix]
public bool Prefix_ReloadGun(ItemActionData _actionData)
{
//int reloadAnimationIndex = MultiActionManager.GetMetaIndexForActionIndex(_actionData.invData.holdingEntity.entityId, _actionData.indexInEntityOfAction);
_actionData.invData.holdingEntity.emodel?.avatarController?.UpdateInt(MultiActionUtils.ExecutingActionIndexHash, _actionData.indexInEntityOfAction, false);
_actionData.invData.holdingEntity.MinEventContext.ItemActionData = _actionData;
//MultiActionManager.GetMappingForEntity(_actionData.invData.holdingEntity.entityId)?.SaveMeta();
return true;
}
[HarmonyPatch(nameof(ItemAction.OnHUD)), MethodTargetPrefix]
public bool Prefix_OnHUD(ItemActionData _actionData)
{
if (_actionData.invData?.holdingEntity?.MinEventContext?.ItemActionData == null || _actionData.indexInEntityOfAction != _actionData.invData.holdingEntity.MinEventContext.ItemActionData.indexInEntityOfAction)
return false;
return true;
}
//[MethodTargetPrefix(nameof(ItemActionAttack.ExecuteAction), typeof(ItemActionRanged))]
//public bool Prefix_ExecuteAction(ItemActionData _actionData, MultiActionData __customData)
//{
// //when executing action, set last action index so that correct accuracy is used for drawing crosshair
// if (_actionData.invData.holdingEntity is EntityPlayerLocal player)
// {
// ((ItemActionRanged.ItemActionDataRanged)_actionData).lastAccuracy = __customData.lastAccuracy;
// }
// return true;
//}
//[MethodTargetPrefix("updateAccuracy", typeof(ItemActionRanged))]
//public bool Prefix_updateAccuracy(ItemActionData _actionData, MultiActionData __customData)
//{
// if (_actionData.invData.holdingEntity is EntityPlayerLocal player && MultiActionManager.GetActionIndexForEntityID(player.entityId) == _actionData.indexInEntityOfAction)
// return true;
// //always update custom accuracy
// ItemActionRanged.ItemActionDataRanged rangedData = _actionData as ItemActionRanged.ItemActionDataRanged;
// (rangedData.lastAccuracy, __customData.lastAccuracy) = (__customData.lastAccuracy, rangedData.lastAccuracy);
// return true;
//}
//[MethodTargetPostfix("updateAccuracy", typeof(ItemActionRanged))]
//public void Postfix_updateAccuracy(ItemActionData _actionData, MultiActionData __customData)
//{
// //retain rangedData accuracy if it's the last executed action
// ItemActionRanged.ItemActionDataRanged rangedData = _actionData as ItemActionRanged.ItemActionDataRanged;
// if (_actionData.invData.holdingEntity is EntityPlayerLocal player && MultiActionManager.GetActionIndexForEntityID(player.entityId) == _actionData.indexInEntityOfAction)
// {
// __customData.lastAccuracy = rangedData.lastAccuracy;
// }
// else
// {
// (rangedData.lastAccuracy, __customData.lastAccuracy) = (__customData.lastAccuracy, rangedData.lastAccuracy);
// }
//}
[HarmonyPatch(typeof(ItemActionRanged), nameof(ItemActionRanged.onHoldingEntityFired)), MethodTargetPrefix]
public bool Prefix_onHoldingEntityFired(ItemActionData _actionData)
{
if (!_actionData.invData.holdingEntity.isEntityRemote)
{
_actionData.invData.holdingEntity?.emodel?.avatarController.UpdateInt(MultiActionUtils.ExecutingActionIndexHash, _actionData.indexInEntityOfAction);
//_actionData.invData.holdingEntity?.emodel?.avatarController.CancelEvent("WeaponFire");
}
return true;
}
//[MethodTargetPostfix("onHoldingEntityFired", typeof(ItemActionRanged))]
//public void Postfix_onHoldingEntityFired(ItemActionData _actionData, MultiActionData __customData)
//{
// //after firing, if it's the last executed action then update custom accuracy
// if (_actionData.invData.holdingEntity is EntityPlayerLocal player && MultiActionManager.GetActionIndexForEntityID(player.entityId) == _actionData.indexInEntityOfAction)
// {
// __customData.lastAccuracy = ((ItemActionRanged.ItemActionDataRanged)_actionData).lastAccuracy;
// }
//}
public static void SetAndSaveItemActionData(ItemActionData _actionData, out ItemActionData lastActionData)
{
lastActionData = _actionData.invData.holdingEntity.MinEventContext.ItemActionData;
_actionData.invData.holdingEntity.MinEventContext.ItemActionData = _actionData;
}
public static void RestoreItemActionData(ItemActionData _actionData, ItemActionData lastActionData)
{
if (lastActionData != null)
_actionData.invData.holdingEntity.MinEventContext.ItemActionData = lastActionData;
}
public class MultiActionData
{
public float lastAccuracy;
public MultiActionData(ItemInventoryData _invData, int _indexInEntityOfAction, ActionModuleMultiActionFix _module)
{
}
}
}

View File

@@ -0,0 +1,302 @@
using HarmonyLib;
using KFCommonUtilityLib;
using KFCommonUtilityLib.Scripts.Attributes;
using KFCommonUtilityLib.Scripts.StaticManagers;
using KFCommonUtilityLib.Scripts.Utilities;
using System.Collections.Generic;
using System.Reflection.Emit;
using UniLinq;
using UnityEngine;
[TypeTarget(typeof(ItemActionRanged)), ActionDataTarget(typeof(MultiBarrelData))]
public class ActionModuleMultiBarrel
{
[HarmonyPatch(nameof(ItemAction.OnModificationsChanged)), MethodTargetPostfix]
public void Postfix_OnModificationChanged(ItemActionData _data, MultiBarrelData __customData, ItemActionRanged __instance)
{
int actionIndex = _data.indexInEntityOfAction;
string originalValue = false.ToString();
__instance.Properties.ParseString("MuzzleIsPerRound", ref originalValue);
__customData.muzzleIsPerRound = bool.Parse(_data.invData.itemValue.GetPropertyOverrideForAction("MuzzleIsPerRound", originalValue, actionIndex));
originalValue = false.ToString();
__instance.Properties.ParseString("OneRoundMultiShot", ref originalValue);
__customData.oneRoundMultishot = bool.Parse(_data.invData.itemValue.GetPropertyOverrideForAction("OneRoundMultiShot", originalValue, actionIndex));
originalValue = 1.ToString();
__instance.Properties.ParseString("RoundsPerShot", ref originalValue);
__customData.roundsPerShot = int.Parse(_data.invData.itemValue.GetPropertyOverrideForAction("RoundsPerShot", originalValue, actionIndex));
originalValue = 1.ToString();
__instance.Properties.ParseString("BarrelCount", ref originalValue);
__customData.barrelCount = int.Parse(_data.invData.itemValue.GetPropertyOverrideForAction("BarrelCount", originalValue, actionIndex));
//Log.Out($"MuzzleIsPerRound: {__customData.muzzleIsPerRound} OneRoundMultiShot: {__customData.oneRoundMultishot} RoundsPerShot: {__customData.roundsPerShot} BarrelCount: {__customData.barrelCount}");
__customData.muzzles = new Transform[__customData.barrelCount];
__customData.projectileJoints = new Transform[__customData.barrelCount];
for (int i = 0; i < __customData.barrelCount; i++)
{
string muzzleName = _data.invData.itemValue.GetPropertyOverrideForAction($"MBMuzzle{i}_Name", $"MBMuzzle{i}", actionIndex);
__customData.muzzles[i] = AnimationRiggingManager.GetTransformOverrideByName(_data.invData.model, muzzleName);
string jointName = _data.invData.itemValue.GetPropertyOverrideForAction($"MBProjectileJoint{i}_Name", $"MBProjectileJoint{i}", actionIndex);
__customData.projectileJoints[i] = AnimationRiggingManager.GetTransformOverrideByName(_data.invData.model, jointName);
}
int meta = MultiActionUtils.GetMetaByActionIndex(_data.invData.itemValue, actionIndex);
__customData.SetCurrentBarrel(meta);
((ItemActionRanged.ItemActionDataRanged)_data).IsDoubleBarrel = false;
}
[HarmonyPatch(typeof(ItemActionLauncher), nameof(ItemAction.StartHolding)), MethodTargetPrefix]
public void Prefix_StartHolding_ItemActionLauncher(ItemActionData _data, ItemActionLauncher __instance, MultiBarrelData __customData)
{
ItemActionLauncher.ItemActionDataLauncher launcherData = _data as ItemActionLauncher.ItemActionDataLauncher;
launcherData.projectileJoint = __customData.projectileJoints[0];
}
[HarmonyPatch(typeof(ItemActionLauncher), nameof(ItemAction.StartHolding)), MethodTargetPostfix]
public void Postfix_StartHolding_ItemActionLauncher(ItemActionData _data, ItemActionLauncher __instance, MultiBarrelData __customData)
{
ItemActionLauncher.ItemActionDataLauncher launcherData = _data as ItemActionLauncher.ItemActionDataLauncher;
if (launcherData?.projectileInstance != null && __customData.oneRoundMultishot && __customData.roundsPerShot > 1)
{
int count = launcherData.projectileInstance.Count;
int times = __customData.roundsPerShot - 1;
for (int i = 0; i < times; i++)
{
launcherData.projectileJoint = __customData.projectileJoints[i + 1];
for (int j = 0; j < count; j++)
{
launcherData.projectileInstance.Add(__instance.instantiateProjectile(_data));
}
}
}
launcherData.projectileJoint = __customData.projectileJoints[__customData.curBarrelIndex];
}
[HarmonyPatch(nameof(ItemActionRanged.getUserData)), MethodTargetPostfix]
public void Postfix_getUserData(MultiBarrelData __customData, ref int __result)
{
__result |= ((byte)__customData.curBarrelIndex) << 8;
}
[HarmonyPatch(typeof(ItemActionRanged), nameof(ItemAction.ItemActionEffects)), MethodTargetPrefix]
public bool Prefix_ItemActionEffects_ItemActionRanged(ItemActionData _actionData, int _userData, int _firingState, MultiBarrelData __customData)
{
ItemActionRanged.ItemActionDataRanged rangedData = _actionData as ItemActionRanged.ItemActionDataRanged;
if (rangedData != null && _firingState != 0)
{
byte index = (byte)(_userData >> 8);
rangedData.muzzle = __customData.muzzles[index];
__customData.SetAnimatorParam(index);
}
return true;
}
[HarmonyPatch(typeof(ItemActionLauncher), nameof(ItemAction.ItemActionEffects)), MethodTargetPrefix]
public bool Prefix_ItemActionEffects_ItemActionLauncher(ItemActionData _actionData, int _userData, int _firingState, MultiBarrelData __customData)
{
ItemActionLauncher.ItemActionDataLauncher launcherData = _actionData as ItemActionLauncher.ItemActionDataLauncher;
if (launcherData != null)
{
launcherData.projectileJoint = __customData.projectileJoints[(byte)(_userData >> 8)];
}
return Prefix_ItemActionEffects_ItemActionRanged(_actionData, _userData, _firingState, __customData);
}
[HarmonyPatch(typeof(ItemActionRanged), nameof(ItemAction.ExecuteAction)), MethodTargetTranspiler]
private static IEnumerable<CodeInstruction> Transpiler_ExecuteAction_ItemActionRanged(IEnumerable<CodeInstruction> instructions, ILGenerator generator)
{
var codes = instructions.ToList();
var mtd_getmax = AccessTools.Method(typeof(ItemActionRanged), nameof(ItemActionRanged.GetMaxAmmoCount));
var mtd_consume = AccessTools.Method(typeof(ItemActionRanged), nameof(ItemActionRanged.ConsumeAmmo));
var prop_instance = AccessTools.PropertyGetter(typeof(IModuleContainerFor<MultiBarrelData>), nameof(IModuleContainerFor<MultiBarrelData>.Instance));
Label loopStart = generator.DefineLabel();
Label loopCondi = generator.DefineLabel();
LocalBuilder lbd_data_module = generator.DeclareLocal(typeof(ActionModuleMultiBarrel.MultiBarrelData));
LocalBuilder lbd_i = generator.DeclareLocal(typeof(int));
LocalBuilder lbd_rounds = generator.DeclareLocal(typeof(int));
for (int i = 0; i < codes.Count; i++)
{
//prepare loop and store local variables
if (codes[i].opcode == OpCodes.Stloc_S && ((LocalBuilder)codes[i].operand).LocalIndex == 6)
{
codes[i + 1].WithLabels(loopStart);
codes.InsertRange(i + 1, new[]
{
new CodeInstruction(OpCodes.Ldarg_1),
new CodeInstruction(OpCodes.Castclass, typeof(IModuleContainerFor<MultiBarrelData>)),
new CodeInstruction(OpCodes.Callvirt, prop_instance),
new CodeInstruction(OpCodes.Stloc_S, lbd_data_module),
new CodeInstruction(OpCodes.Ldc_I4_0),
new CodeInstruction(OpCodes.Stloc_S, lbd_i),
new CodeInstruction(OpCodes.Ldloc_S, lbd_data_module),
CodeInstruction.LoadField(typeof(ActionModuleMultiBarrel.MultiBarrelData), nameof(ActionModuleMultiBarrel.MultiBarrelData.roundsPerShot)),
new CodeInstruction(OpCodes.Stloc_S, lbd_rounds),
new CodeInstruction(OpCodes.Br_S, loopCondi),
});
i += 11;
}
//one round multi shot check
else if (codes[i].Calls(mtd_consume))
{
Label lbl = generator.DefineLabel();
codes[i - 5].WithLabels(lbl);
codes.InsertRange(i - 5, new[]
{
new CodeInstruction(OpCodes.Ldloc_S, lbd_data_module),
CodeInstruction.LoadField(typeof(ActionModuleMultiBarrel.MultiBarrelData), nameof(ActionModuleMultiBarrel.MultiBarrelData.oneRoundMultishot)),
new CodeInstruction(OpCodes.Brfalse_S, lbl),
new CodeInstruction(OpCodes.Ldloc_S, lbd_i),
new CodeInstruction(OpCodes.Ldc_I4_0),
new CodeInstruction(OpCodes.Bgt_S, codes[i - 3].operand)
});
i += 6;
}
//loop conditions and cycle barrels
else if (codes[i].Calls(mtd_getmax))
{
Label lbl_pre = generator.DefineLabel();
Label lbl_post = generator.DefineLabel();
CodeInstruction origin = codes[i - 2];
codes.InsertRange(i - 2, new[]
{
new CodeInstruction(OpCodes.Ldloc_S, lbd_data_module).WithLabels(origin.ExtractLabels()),
CodeInstruction.LoadField(typeof(ActionModuleMultiBarrel.MultiBarrelData), nameof(ActionModuleMultiBarrel.MultiBarrelData.muzzleIsPerRound)),
new CodeInstruction(OpCodes.Brfalse_S, lbl_pre),
new CodeInstruction(OpCodes.Ldloc_S, lbd_data_module),
CodeInstruction.Call(typeof(ActionModuleMultiBarrel.MultiBarrelData), nameof(ActionModuleMultiBarrel.MultiBarrelData.CycleBarrels)),
new CodeInstruction(OpCodes.Ldloc_S, 6).WithLabels(lbl_pre),
CodeInstruction.LoadField(typeof(EntityAlive), nameof(EntityAlive.inventory)),
CodeInstruction.Call(typeof(Inventory), nameof(Inventory.CallOnToolbeltChangedInternal)),
new CodeInstruction(OpCodes.Ldloc_S, lbd_i),
new CodeInstruction(OpCodes.Ldc_I4_1),
new CodeInstruction(OpCodes.Add),
new CodeInstruction(OpCodes.Stloc_S, lbd_i),
new CodeInstruction(OpCodes.Ldloc_S, lbd_i).WithLabels(loopCondi),
new CodeInstruction(OpCodes.Ldloc_S, lbd_rounds),
new CodeInstruction(OpCodes.Blt_S, loopStart),
new CodeInstruction(OpCodes.Ldloc_S, lbd_data_module),
CodeInstruction.LoadField(typeof(ActionModuleMultiBarrel.MultiBarrelData), nameof(ActionModuleMultiBarrel.MultiBarrelData.muzzleIsPerRound)),
new CodeInstruction(OpCodes.Brtrue_S, lbl_post),
new CodeInstruction(OpCodes.Ldloc_S, lbd_data_module),
CodeInstruction.Call(typeof(ActionModuleMultiBarrel.MultiBarrelData), nameof(ActionModuleMultiBarrel.MultiBarrelData.CycleBarrels))
});
origin.WithLabels(lbl_post);
break;
}
}
return codes;
}
private static void LogInfo(int cur, int max) => Log.Out($"max rounds {max}, cur {cur}");
public class MultiBarrelData
{
public ItemInventoryData invData;
public int actionIndex;
public ActionModuleMultiBarrel module;
public bool muzzleIsPerRound;
public bool oneRoundMultishot;
public int roundsPerShot;
public int barrelCount;
public int curBarrelIndex;
public Transform[] muzzles;
public Transform[] projectileJoints;
public MultiBarrelData(ItemInventoryData _invData, int _indexInEntityOfAction, ActionModuleMultiBarrel _module)
{
invData = _invData;
actionIndex = _indexInEntityOfAction;
module = _module;
}
public void CycleBarrels()
{
curBarrelIndex = ++curBarrelIndex >= barrelCount ? 0 : curBarrelIndex;
//Log.Out($"cycle barrel index {curBarrelIndex}");
}
public void SetCurrentBarrel(int roundLeft)
{
if (muzzleIsPerRound)
{
int totalSwitches;
if (oneRoundMultishot)
{
totalSwitches = roundLeft * roundsPerShot;
}
else
{
totalSwitches = roundLeft;
}
int lastCycleSwitches = totalSwitches % barrelCount;
int barrelGroup = barrelCount / roundsPerShot;
curBarrelIndex = (barrelCount - lastCycleSwitches) / barrelGroup * barrelGroup;
}
else
{
if (oneRoundMultishot)
{
curBarrelIndex = barrelCount - (roundLeft % barrelCount);
}
else
{
curBarrelIndex = barrelCount - ((roundLeft + 1) / roundsPerShot) % barrelCount;
}
}
if (curBarrelIndex >= barrelCount)
{
curBarrelIndex = 0;
}
SetAnimatorParam(curBarrelIndex);
//Log.Out($"set barrel index {curBarrelIndex}");
}
public void SetAnimatorParam(int barrelIndex)
{
invData.holdingEntity.emodel.avatarController.UpdateInt("barrelIndex", barrelIndex, true);
//Log.Out($"set param index {barrelIndex}");
}
}
}
[HarmonyPatch]
public class MultiBarrelPatches
{
[HarmonyPatch(typeof(AnimatorRangedReloadState), nameof(AnimatorRangedReloadState.OnStateEnter))]
[HarmonyPostfix]
private static void Postfix_OnStateEnter_AnimatorRangedReloadState(AnimatorRangedReloadState __instance)
{
ItemActionLauncher.ItemActionDataLauncher launcherData = __instance.actionData as ItemActionLauncher.ItemActionDataLauncher;
if (launcherData != null && launcherData is IModuleContainerFor<ActionModuleMultiBarrel.MultiBarrelData> dataModule && dataModule.Instance.oneRoundMultishot && dataModule.Instance.roundsPerShot > 1)
{
int count = launcherData.projectileInstance.Count;
int times = dataModule.Instance.roundsPerShot - 1;
for (int i = 0; i < count; i++)
{
for (int j = 0; j < times; j++)
{
launcherData.projectileJoint = dataModule.Instance.projectileJoints[j + 1];
launcherData.projectileInstance.Insert(i * (times + 1) + j + 1, ((ItemActionLauncher)__instance.actionRanged).instantiateProjectile(launcherData));
}
}
}
}
[HarmonyPatch(typeof(AnimatorRangedReloadState), nameof(AnimatorRangedReloadState.OnStateExit))]
[HarmonyPostfix]
private static void Postfix_OnStateExit_AnimatorRangedReloadState(AnimatorRangedReloadState __instance)
{
if (__instance.actionData is IModuleContainerFor<ActionModuleMultiBarrel.MultiBarrelData> dataModule)
{
dataModule.Instance.SetCurrentBarrel(__instance.actionData.invData.itemValue.Meta);
}
}
}

View File

@@ -0,0 +1,276 @@
using HarmonyLib;
using KFCommonUtilityLib.Scripts.Attributes;
using KFCommonUtilityLib.Scripts.Utilities;
using UnityEngine;
using static ItemActionRanged;
[TypeTarget(typeof(ItemActionRanged)), ActionDataTarget(typeof(RampUpData))]
public class ActionModuleRampUp
{
public enum State
{
RampUp,
Stable,
RampDown
}
private readonly static int prepareHash = Animator.StringToHash("prepare");
private readonly static int prepareSpeedHash = Animator.StringToHash("prepareSpeed");
private readonly static int rampHash = Animator.StringToHash("ramp");
private readonly static int prepareRatioHash = Animator.StringToHash("prepareRatio");
private readonly static int rampRatioHash = Animator.StringToHash("rampRatio");
private readonly static int totalRatioHash = Animator.StringToHash("totalRatio");
[HarmonyPatch(nameof(ItemAction.OnHoldingUpdate)), MethodTargetPostfix]
public void Postfix_OnHoldingUpdate(ItemActionData _actionData, RampUpData __customData, ItemActionRanged __instance)
{
var rangedData = _actionData as ItemActionDataRanged;
__customData.originalDelay = rangedData.Delay;
if (rangedData.invData.holdingEntity.isEntityRemote)
return;
bool aiming = rangedData.invData.holdingEntity.AimingGun;
bool isRampUp = ((rangedData.bPressed && !rangedData.bReleased && __instance.notReloading(rangedData) && rangedData.curBurstCount < __instance.GetBurstCount(rangedData)) || (__customData.zoomPrepare && aiming)) && (__instance.InfiniteAmmo || _actionData.invData.itemValue.Meta > 0) && _actionData.invData.itemValue.PercentUsesLeft > 0;
UpdateTick(__customData, _actionData, isRampUp);
if (__customData.rampRatio > 0)
{
rangedData.Delay /= __customData.rampRatio >= 1f ? __customData.maxMultiplier : __customData.rampRatio * (__customData.maxMultiplier - 1f) + 1f;
}
}
[HarmonyPatch(nameof(ItemAction.OnModificationsChanged)), MethodTargetPostfix]
public void Postfix_OnModificationsChanged(ItemActionData _data, RampUpData __customData, ItemActionRanged __instance)
{
int actionIndex = __instance.ActionIndex;
string originalValue = 1.ToString();
__instance.Properties.ParseString("RampMultiplier", ref originalValue);
__customData.maxMultiplier = Mathf.Max(float.Parse(_data.invData.itemValue.GetPropertyOverrideForAction("RampMultiplier", originalValue, actionIndex)), 1);
originalValue = 0.ToString();
__instance.Properties.ParseString("RampUpTime", ref originalValue);
__customData.rampUpTime = float.Parse(_data.invData.itemValue.GetPropertyOverrideForAction("RampTime", originalValue, actionIndex));
originalValue = string.Empty;
__instance.Properties.ParseString("RampUpSound", ref originalValue);
__customData.rampUpSound = _data.invData.itemValue.GetPropertyOverrideForAction("RampStartSound", originalValue, actionIndex);
originalValue = 0.ToString();
__instance.Properties.ParseString("RampDownTime", ref originalValue);
__customData.rampDownTime = Mathf.Max(float.Parse(_data.invData.itemValue.GetPropertyOverrideForAction("RampTime", originalValue, actionIndex)), 0);
originalValue = string.Empty;
__instance.Properties.ParseString("RampDownSound", ref originalValue);
__customData.rampDownSound = _data.invData.itemValue.GetPropertyOverrideForAction("RampStartSound", originalValue, actionIndex);
originalValue = 0.ToString();
__instance.Properties.ParseString("PrepareTime", ref originalValue);
__customData.prepareTime = float.Parse(_data.invData.itemValue.GetPropertyOverrideForAction("PrepareTime", originalValue, actionIndex));
__customData.prepareSpeed = float.Parse(originalValue) / __customData.prepareTime;
originalValue = string.Empty;
__instance.Properties.ParseString("PrepareSound", ref originalValue);
__customData.prepareSound = _data.invData.itemValue.GetPropertyOverrideForAction("PrepareSound", originalValue, actionIndex);
originalValue = false.ToString();
__instance.Properties.ParseString("PrepareOnAim", ref originalValue);
__customData.zoomPrepare = bool.Parse(_data.invData.itemValue.GetPropertyOverrideForAction("PrepareOnAim", originalValue, actionIndex));
originalValue = string.Empty;
__instance.Properties.ParseString("RampStableSound", ref originalValue);
__customData.rampStableSound = _data.invData.itemValue.GetPropertyOverrideForAction("RampStableSound", originalValue, actionIndex);
__customData.totalChargeTime = __customData.prepareTime + __customData.rampUpTime;
__customData.rampDownTimeScale = __customData.rampDownTime > 0 ? (__customData.totalChargeTime) / __customData.rampDownTime : float.MaxValue;
ResetAll(__customData, _data);
}
[HarmonyPatch(nameof(ItemAction.StopHolding)), MethodTargetPostfix]
public void Postfix_StopHolding(RampUpData __customData, ItemActionData _data)
{
ResetAll(__customData, _data);
}
[HarmonyPatch(nameof(ItemAction.ExecuteAction)), MethodTargetPrefix]
public bool Prefix_ExecuteAction(RampUpData __customData, ItemActionRanged __instance, ItemActionData _actionData, bool _bReleased)
{
ItemActionDataRanged rangedData = _actionData as ItemActionDataRanged;
if (!_bReleased && (__instance.InfiniteAmmo || _actionData.invData.itemValue.Meta > 0) && _actionData.invData.itemValue.PercentUsesLeft > 0)
{
rangedData.bReleased = false;
rangedData.bPressed = true;
if (__customData.curTime < __customData.prepareTime)
return false;
}
return true;
}
private void UpdateTick(RampUpData data, ItemActionData actionData, bool isRampUp)
{
float previousTime = data.curTime;
float deltaTime = Time.time - data.lastTickTime;
data.lastTickTime = Time.time;
ref float curTime = ref data.curTime;
ref State curState = ref data.curState;
float totalChargeTime = data.totalChargeTime;
switch (curState)
{
case State.RampUp:
{
curTime = Mathf.Max(curTime, 0);
if (isRampUp)
{
actionData.invData.holdingEntity.emodel.avatarController.UpdateBool(prepareHash, true, true);
if (curTime < totalChargeTime)
{
curTime += deltaTime;
}
if (curTime >= data.prepareTime)
{
actionData.invData.holdingEntity.emodel.avatarController.UpdateBool(rampHash, true, true);
}
if (curTime >= totalChargeTime)
{
//Log.Out($"change state from {curState} to stable");
actionData.invData.holdingEntity.PlayOneShot(data.rampStableSound);
curState = State.Stable;
}
}
else
{
//Log.Out($"change state from {curState} to ramp down");
actionData.invData.holdingEntity.StopOneShot(data.rampUpSound);
actionData.invData.holdingEntity.PlayOneShot(data.rampDownSound);
curState = State.RampDown;
}
break;
}
case State.RampDown:
{
curTime = Mathf.Min(curTime, totalChargeTime);
if (!isRampUp)
{
actionData.invData.holdingEntity.emodel.avatarController.UpdateBool(rampHash, false, true);
if (curTime > 0)
{
curTime -= deltaTime * data.rampDownTimeScale;
}
if (curTime < data.prepareTime)
{
actionData.invData.holdingEntity.emodel.avatarController.UpdateBool(prepareHash, false, true);
}
if (curTime <= 0)
{
//Log.Out($"change state from {curState} to stable");
//actionData.invData.holdingEntity.PlayOneShot(data.rampStableSound);
curState = State.Stable;
}
}
else
{
//Log.Out($"change state from {curState} to ramp up");
actionData.invData.holdingEntity.StopOneShot(data.rampDownSound);
actionData.invData.holdingEntity.PlayOneShot(data.rampUpSound);
curState = State.RampUp;
}
break;
}
case State.Stable:
{
if (isRampUp)
{
if (curTime < totalChargeTime)
{
//Log.Out($"change state from {curState} to ramp up");
actionData.invData.holdingEntity.StopOneShot(data.rampStableSound);
actionData.invData.holdingEntity.StopOneShot(data.rampDownSound);
actionData.invData.holdingEntity.PlayOneShot(data.rampUpSound);
curState = State.RampUp;
}
else
{
actionData.invData.holdingEntity.emodel.avatarController.UpdateBool(prepareHash, true, true);
actionData.invData.holdingEntity.emodel.avatarController.UpdateBool(rampHash, true, true);
}
}
else
{
if (curTime > 0)
{
//Log.Out($"change state from {curState} to ramp down");
actionData.invData.holdingEntity.StopOneShot(data.rampStableSound);
actionData.invData.holdingEntity.StopOneShot(data.rampUpSound);
actionData.invData.holdingEntity.PlayOneShot(data.rampDownSound);
curState = State.RampDown;
}
else
{
actionData.invData.holdingEntity.emodel.avatarController.UpdateBool(prepareHash, false, true);
actionData.invData.holdingEntity.emodel.avatarController.UpdateBool(rampHash, false, true);
}
}
break;
}
}
//Log.Out($"turret burst fire rate {turret.burstFireRate} max {turret.burstFireRateMax} cur time {curTime} cur state {curState} is ramp up {isRampUp} turret: ison {turret.IsOn} has target {turret.hasTarget} state {turret.state}");
actionData.invData.holdingEntity.emodel.avatarController.UpdateFloat(prepareSpeedHash, data.prepareSpeed);
if (curTime != previousTime)
{
actionData.invData.holdingEntity.emodel.avatarController.UpdateFloat(prepareRatioHash, data.prepareRatio = (data.prepareTime == 0 ? 1f : Mathf.Clamp01(curTime / data.prepareTime)));
actionData.invData.holdingEntity.emodel.avatarController.UpdateFloat(rampRatioHash, data.rampRatio = (data.rampUpTime == 0 ? 1f : Mathf.Clamp01((curTime - data.prepareTime) / data.rampUpTime)));
actionData.invData.holdingEntity.emodel.avatarController.UpdateFloat(totalRatioHash, data.totalRatio = (totalChargeTime == 0 ? 1f : Mathf.Clamp01(curTime / totalChargeTime)));
}
}
private void ResetAll(RampUpData _rampData, ItemActionData _actionData)
{
_rampData.curTime = 0f;
_rampData.lastTickTime = Time.time;
_rampData.curState = State.Stable;
_rampData.prepareRatio = 0f;
_rampData.rampRatio = 0f;
_rampData.totalRatio = 0f;
((ItemActionDataRanged)_actionData).Delay = _rampData.originalDelay;
_actionData.invData.holdingEntity.StopOneShot(_rampData.prepareSound);
_actionData.invData.holdingEntity.emodel.avatarController.UpdateBool(prepareHash, false, true);
_actionData.invData.holdingEntity.StopOneShot(_rampData.rampUpSound);
_actionData.invData.holdingEntity.emodel.avatarController.UpdateBool(rampHash, false, true);
//Log.Out("Reset all!");
}
public class RampUpData
{
public float maxMultiplier = 1f;
public string prepareSound = string.Empty;
public float prepareSpeed = 1f;
public float prepareTime = 0f;
public string rampUpSound = string.Empty;
public float rampUpTime = 0f;
public float totalChargeTime = 0f;
public string rampDownSound = string.Empty;
public float rampDownTime = 0f;
public float rampDownTimeScale = float.MaxValue;
public string rampStableSound = string.Empty;
public float originalDelay = 0f;
public float curTime = 0f;
public State curState = State.Stable;
public float prepareRatio = 0f;
public float rampRatio = 0f;
public float totalRatio = 0f;
public float lastTickTime = 0f;
public bool zoomPrepare = false;
public ActionModuleRampUp rampUpModule;
public RampUpData(ItemInventoryData _invData, int _indexInEntityOfAction, ActionModuleRampUp _module)
{
rampUpModule = _module;
}
}
}

View File

@@ -0,0 +1,30 @@
using HarmonyLib;
using KFCommonUtilityLib.Scripts.Attributes;
using KFCommonUtilityLib.Scripts.Utilities;
using UniLinq;
[TypeTarget(typeof(ItemAction)), ActionDataTarget(typeof(TaggedData))]
public class ActionModuleTagged
{
[HarmonyPatch(nameof(ItemAction.OnModificationsChanged)), MethodTargetPostfix]
private void Postfix_OnModificationChanged(ItemAction __instance, ItemActionData _data, TaggedData __customData)
{
var tags = __instance.Properties.GetString("ActionTags").Split(',', System.StringSplitOptions.RemoveEmptyEntries);
var tags_to_add = _data.invData.itemValue.GetAllPropertyOverridesForAction("ActionTagsAppend", __instance.ActionIndex).SelectMany(s => s.Split(',', System.StringSplitOptions.RemoveEmptyEntries));
var tags_to_remove = _data.invData.itemValue.GetAllPropertyOverridesForAction("ActionTagsRemove", __instance.ActionIndex).SelectMany(s => s.Split(',', System.StringSplitOptions.RemoveEmptyEntries));
var tags_result = tags.Union(tags_to_add);
tags_result = tags_result.Except(tags_to_remove);
__customData.tags = tags_result.Any() ? FastTags<TagGroup.Global>.Parse(string.Join(",", tags_result)) : FastTags<TagGroup.Global>.none;
//Log.Out($"tags: {string.Join(",", tags_result)}");
}
public class TaggedData
{
public FastTags<TagGroup.Global> tags;
public TaggedData(ItemInventoryData _invData, int _indexInEntityOfAction, ActionModuleTagged _module)
{
}
}
}

View File

@@ -0,0 +1,48 @@
using HarmonyLib;
using KFCommonUtilityLib.Scripts.Attributes;
using System.Collections.Generic;
using System.Reflection.Emit;
using UnityEngine;
[TypeTarget(typeof(ItemAction))]
public class ActionModuleTranspilerTest
{
[HarmonyPatch(typeof(ItemActionAttack), nameof(ItemAction.ExecuteAction)), MethodTargetTranspiler]
private static IEnumerable<CodeInstruction> Transpiler_InvalidTest(IEnumerable<CodeInstruction> instructions)
{
yield return new CodeInstruction(OpCodes.Ldstr, "Ranged!");
yield return CodeInstruction.Call(typeof(ActionModuleTranspilerTest), nameof(ActionModuleTranspilerTest.CallSomething));
foreach (var ins in instructions)
{
yield return ins;
}
}
[HarmonyPatch(typeof(ItemActionRanged), nameof(ItemAction.ExecuteAction)), MethodTargetTranspiler]
private static IEnumerable<CodeInstruction> Transpiler_RangedTest(IEnumerable<CodeInstruction> instructions)
{
yield return new CodeInstruction(OpCodes.Ldstr, "Ranged!");
yield return CodeInstruction.Call(typeof(ActionModuleTranspilerTest), nameof(ActionModuleTranspilerTest.CallSomething));
foreach (var ins in instructions)
{
yield return ins;
}
}
[HarmonyPatch(typeof(ItemActionCatapult), nameof(ItemAction.ExecuteAction)), MethodTargetTranspiler]
private static IEnumerable<CodeInstruction> Transpiler_CatapultTest(IEnumerable<CodeInstruction> instructions)
{
yield return new CodeInstruction(OpCodes.Ldstr, "Catapult!");
yield return CodeInstruction.Call(typeof(ActionModuleTranspilerTest), nameof(ActionModuleTranspilerTest.CallSomething));
foreach (var ins in instructions)
{
yield return ins;
}
}
private static void CallSomething(string str)
{
Log.Out($"Call something: {str}\n{StackTraceUtility.ExtractStackTrace()}");
}
}

View File

@@ -0,0 +1,73 @@
using HarmonyLib;
using KFCommonUtilityLib.Scripts.Attributes;
using UnityEngine;
[TypeTarget(typeof(ItemActionZoom)), ActionDataTarget(typeof(VariableZoomData))]
public class ActionModuleVariableZoom
{
public static float zoomScale = 7.5f;
[HarmonyPatch(nameof(ItemAction.ConsumeScrollWheel)), MethodTargetPostfix]
private void Postfix_ConsumeScrollWheel(ItemActionData _actionData, float _scrollWheelInput, PlayerActionsLocal _playerInput, VariableZoomData __customData)
{
if (!_actionData.invData.holdingEntity.AimingGun || _scrollWheelInput == 0f)
{
return;
}
ItemActionZoom.ItemActionDataZoom itemActionDataZoom = (ItemActionZoom.ItemActionDataZoom)_actionData;
if (!itemActionDataZoom.bZoomInProgress)
{
//__customData.curScale = Utils.FastClamp(__customData.curScale + _scrollWheelInput * zoomScale, __customData.minScale, __customData.maxScale);
__customData.curSteps = Utils.FastClamp01(__customData.curSteps + _scrollWheelInput);
__customData.curFov = Utils.FastLerp(__customData.maxFov, __customData.minFov, GetNext(__customData.curSteps));
__customData.curScale = Mathf.Pow(Mathf.Rad2Deg * 2 * Mathf.Atan(Mathf.Tan(Mathf.Deg2Rad * 7.5f) / __customData.curFov), 2);
__customData.shouldUpdate = true;
}
}
private float GetNext(float cur)
{
return Mathf.Sin(Mathf.PI * cur / 2);
}
[HarmonyPatch(nameof(ItemAction.OnModificationsChanged)), MethodTargetPostfix]
private void Postfix_OnModificationChanged(ItemActionZoom __instance, ItemActionData _data, VariableZoomData __customData)
{
string str = __instance.Properties.GetString("ZoomRatio");
if (string.IsNullOrEmpty(str))
{
str = "1";
}
__customData.maxScale = StringParsers.ParseFloat(_data.invData.itemValue.GetPropertyOverride("ZoomRatio", str));
str = __instance.Properties.GetString("ZoomRatioMin");
if (string.IsNullOrEmpty(str))
{
str = __customData.maxScale.ToString();
}
__customData.minScale = StringParsers.ParseFloat(_data.invData.itemValue.GetPropertyOverride("ZoomRatioMin", str));
//__customData.curScale = Utils.FastClamp(__customData.curScale, __customData.minScale, __customData.maxScale);
__customData.maxFov = Mathf.Rad2Deg * 2 * Mathf.Atan(Mathf.Tan(Mathf.Deg2Rad * 7.5f) / Mathf.Sqrt(__customData.minScale));
__customData.minFov = Mathf.Rad2Deg * 2 * Mathf.Atan(Mathf.Tan(Mathf.Deg2Rad * 7.5f) / Mathf.Sqrt(__customData.maxScale));
__customData.curFov = Utils.FastClamp(__customData.curFov, __customData.minFov, __customData.maxFov);
__customData.curScale = Mathf.Pow(Mathf.Rad2Deg * 2 * Mathf.Atan(Mathf.Tan(Mathf.Deg2Rad * 7.5f) / __customData.curFov), 2);
__customData.curSteps = Mathf.InverseLerp(__customData.maxFov, __customData.minFov, __customData.curFov);
__customData.shouldUpdate = true;
}
public class VariableZoomData
{
public float maxScale = 1f;
public float minScale = 1f;
public float curScale = 0f;
public float maxFov = 15f;
public float minFov = 15f;
public float curFov = 90f;
public float curSteps = 0;
public bool shouldUpdate = true;
public VariableZoomData(ItemInventoryData _invData, int _indexInEntityOfAction, ActionModuleVariableZoom _module)
{
}
}
}