Upload from upload_mods.ps1
This commit is contained in:
261
Scripts/Items/ModularActions/ActionModuleAlternative.cs
Normal file
261
Scripts/Items/ModularActions/ActionModuleAlternative.cs
Normal 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
36
Scripts/Items/ModularActions/ActionModuleAnimationLocked.cs
Normal file
36
Scripts/Items/ModularActions/ActionModuleAnimationLocked.cs
Normal 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)
|
||||
{
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
135
Scripts/Items/ModularActions/ActionModuleCustomAnimationDelay.cs
Normal file
135
Scripts/Items/ModularActions/ActionModuleCustomAnimationDelay.cs
Normal 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
78
Scripts/Items/ModularActions/ActionModuleDisplayAsBuff.cs
Normal file
78
Scripts/Items/ModularActions/ActionModuleDisplayAsBuff.cs
Normal 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
67
Scripts/Items/ModularActions/ActionModuleDynamicGraze.cs
Normal file
67
Scripts/Items/ModularActions/ActionModuleDynamicGraze.cs
Normal 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);
|
||||
}
|
||||
}
|
||||
@@ -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)
|
||||
{
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,93 @@
|
||||
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));
|
||||
}
|
||||
if (!variableZoomData.forceFov)
|
||||
{
|
||||
return variableZoomData.curScale;
|
||||
}
|
||||
return 1f;
|
||||
}
|
||||
return zoomRatio;
|
||||
}
|
||||
set => zoomRatio = value;
|
||||
}
|
||||
|
||||
public DynamicSensitivityData(ItemInventoryData _invData, int _indexInEntityOfAction, ActionModuleDynamicSensitivity _module)
|
||||
{
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
157
Scripts/Items/ModularActions/ActionModuleErgoAffected.cs
Normal file
157
Scripts/Items/ModularActions/ActionModuleErgoAffected.cs
Normal file
@@ -0,0 +1,157 @@
|
||||
using HarmonyLib;
|
||||
using KFCommonUtilityLib;
|
||||
using KFCommonUtilityLib.Scripts.Attributes;
|
||||
using KFCommonUtilityLib.Scripts.StaticManagers;
|
||||
using System.Collections.Generic;
|
||||
using System.Reflection.Emit;
|
||||
using UniLinq;
|
||||
using UnityEngine;
|
||||
using static ActionModuleErgoAffected;
|
||||
|
||||
[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)];
|
||||
__customData.curErgo = EffectManager.GetValue(CustomEnums.WeaponErgonomics, _actionData.invData.itemValue, 0, holdingEntity);
|
||||
float aimSpeedModifier = __customData.ModifiedErgo;
|
||||
Log.Out($"Ergo is {__customData.curErgo}, 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 float curErgo;
|
||||
public float ModifiedErgo => Mathf.Lerp(0.2f, 1, curErgo);
|
||||
|
||||
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);
|
||||
float modifiedErgo = ergoData.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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
336
Scripts/Items/ModularActions/ActionModuleFireModeSelector.cs
Normal file
336
Scripts/Items/ModularActions/ActionModuleFireModeSelector.cs
Normal file
@@ -0,0 +1,336 @@
|
||||
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);
|
||||
}
|
||||
_data.invData.holdingEntity.inventory.CallOnToolbeltChangedInternal();
|
||||
}
|
||||
}
|
||||
|
||||
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, isUIOpen);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private static void UpdateLocalInput(EntityPlayerLocal _player, bool _isUIOpen)
|
||||
{
|
||||
if (_isUIOpen || _player.emodel.IsRagdollActive || _player.IsDead() || _player.AttachedToEntity != null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (PlayerActionKFLib.Instance.Enabled && PlayerActionKFLib.Instance.ToggleFireMode.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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
85
Scripts/Items/ModularActions/ActionModuleHoldOpen.cs
Normal file
85
Scripts/Items/ModularActions/ActionModuleHoldOpen.cs
Normal 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;
|
||||
}
|
||||
}
|
||||
24
Scripts/Items/ModularActions/ActionModuleInspectable.cs
Normal file
24
Scripts/Items/ModularActions/ActionModuleInspectable.cs
Normal 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
225
Scripts/Items/ModularActions/ActionModuleInterruptReload.cs
Normal file
225
Scripts/Items/ModularActions/ActionModuleInterruptReload.cs
Normal 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;
|
||||
}
|
||||
}
|
||||
60
Scripts/Items/ModularActions/ActionModuleInvariableRPM.cs
Normal file
60
Scripts/Items/ModularActions/ActionModuleInvariableRPM.cs
Normal 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);
|
||||
}
|
||||
}
|
||||
105
Scripts/Items/ModularActions/ActionModuleLocalPassiveCache.cs
Normal file
105
Scripts/Items/ModularActions/ActionModuleLocalPassiveCache.cs
Normal 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();
|
||||
}
|
||||
}
|
||||
}
|
||||
178
Scripts/Items/ModularActions/ActionModuleMetaConsumer.cs
Normal file
178
Scripts/Items/ModularActions/ActionModuleMetaConsumer.cs
Normal 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;
|
||||
//}
|
||||
}
|
||||
166
Scripts/Items/ModularActions/ActionModuleMetaRecharger.cs
Normal file
166
Scripts/Items/ModularActions/ActionModuleMetaRecharger.cs
Normal 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
222
Scripts/Items/ModularActions/ActionModuleMultiActionFix.cs
Normal file
222
Scripts/Items/ModularActions/ActionModuleMultiActionFix.cs
Normal 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)
|
||||
{
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
302
Scripts/Items/ModularActions/ActionModuleMultiBarrel.cs
Normal file
302
Scripts/Items/ModularActions/ActionModuleMultiBarrel.cs
Normal 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
271
Scripts/Items/ModularActions/ActionModuleProceduralAiming.cs
Normal file
271
Scripts/Items/ModularActions/ActionModuleProceduralAiming.cs
Normal file
@@ -0,0 +1,271 @@
|
||||
using HarmonyLib;
|
||||
using KFCommonUtilityLib;
|
||||
using KFCommonUtilityLib.Scripts.Attributes;
|
||||
using KFCommonUtilityLib.Scripts.StaticManagers;
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine;
|
||||
|
||||
[TypeTarget(typeof(ItemActionZoom)), ActionDataTarget(typeof(ProceduralAimingData))]
|
||||
public class ActionModuleProceduralAiming
|
||||
{
|
||||
[HarmonyPatch(nameof(ItemAction.OnModificationsChanged)), MethodTargetPostfix]
|
||||
public void Postfix_OnModificationsChanged(ItemActionZoom __instance, ItemActionData _data, ProceduralAimingData __customData)
|
||||
{
|
||||
if (_data is IModuleContainerFor<ActionModuleErgoAffected.ErgoData> dataModule)
|
||||
{
|
||||
__customData.zoomInTime = dataModule.Instance.module.zoomInTimeBase / dataModule.Instance.module.aimSpeedModifierBase;
|
||||
__customData.ergoData = dataModule.Instance;
|
||||
}
|
||||
else
|
||||
{
|
||||
float zoomInTimeBase = 0.3f;
|
||||
__instance.Properties.ParseFloat("ZoomInTimeBase", ref zoomInTimeBase);
|
||||
float aimSpeedModifierBase = 1f;
|
||||
__instance.Properties.ParseFloat("AimSpeedModifierBase", ref aimSpeedModifierBase);
|
||||
__customData.zoomInTime = zoomInTimeBase / aimSpeedModifierBase;
|
||||
__customData.ergoData = null;
|
||||
}
|
||||
|
||||
__customData.playerOriginTransform = null;
|
||||
__customData.playerCameraPosRef = _data.invData.holdingEntity is EntityPlayerLocal player && player.bFirstPersonView ? player.cameraTransform : null;
|
||||
var targets = AnimationRiggingManager.GetRigTargetsFromPlayer(_data.invData.holdingEntity);
|
||||
if (__customData.playerCameraPosRef)
|
||||
{
|
||||
if (targets.ItemFpv)
|
||||
{
|
||||
if (targets is RigTargets)
|
||||
{
|
||||
__customData.isRigWeapon = true;
|
||||
__customData.playerOriginTransform = targets.ItemAnimator.transform;
|
||||
__customData.rigWeaponLocalPosition = __customData.playerOriginTransform.localPosition;
|
||||
__customData.rigWeaponLocalRotation = __customData.playerOriginTransform.localRotation;
|
||||
}
|
||||
else
|
||||
{
|
||||
__customData.isRigWeapon = false;
|
||||
__customData.playerOriginTransform = __customData.playerCameraPosRef.FindInAllChildren("Hips");
|
||||
}
|
||||
__customData.playerCameraPosRef = targets.ItemFpv.Find("PlayerCameraPositionReference");
|
||||
}
|
||||
else
|
||||
{
|
||||
__customData.playerCameraPosRef = null;
|
||||
}
|
||||
}
|
||||
if (__customData.playerCameraPosRef)
|
||||
{
|
||||
__customData.aimRefTransform = targets.ItemFpv.Find("ScopeBasePositionReference");
|
||||
if (__customData.aimRefTransform)
|
||||
{
|
||||
var scopeRefTrans = __customData.aimRefTransform.Find("ScopePositionReference");
|
||||
if (!scopeRefTrans)
|
||||
{
|
||||
scopeRefTrans = new GameObject("ScopePositionReference").transform;
|
||||
scopeRefTrans.SetParent(__customData.aimRefTransform, false);
|
||||
}
|
||||
scopeRefTrans.SetLocalPositionAndRotation(Vector3.zero, Quaternion.identity);
|
||||
scopeRefTrans.localScale = Vector3.one;
|
||||
__customData.aimRefTransform = scopeRefTrans;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
__customData.aimRefTransform = null;
|
||||
}
|
||||
|
||||
__customData.ResetAiming();
|
||||
__customData.UpdateCurrentReference(true);
|
||||
}
|
||||
|
||||
[HarmonyPatch(nameof(ItemAction.StopHolding)), MethodTargetPostfix]
|
||||
public void Postfix_StopHolding(ProceduralAimingData __customData)
|
||||
{
|
||||
__customData.ResetAiming();
|
||||
}
|
||||
|
||||
//[HarmonyPatch(nameof(ItemAction.ExecuteAction)), MethodTargetPostfix]
|
||||
//public void Postfix_ExecuteAction(ProceduralAimingData __customData, ItemActionData _actionData)
|
||||
//{
|
||||
// if (__customData.isAiming != ((ItemActionZoom.ItemActionDataZoom)_actionData).aimingValue)
|
||||
// {
|
||||
// __customData.UpdateCurrentReference();
|
||||
// __customData.isAiming = ((ItemActionZoom.ItemActionDataZoom)_actionData).aimingValue;
|
||||
// }
|
||||
//}
|
||||
|
||||
public class ProceduralAimingData
|
||||
{
|
||||
public ActionModuleErgoAffected.ErgoData ergoData;
|
||||
public float zoomInTime;
|
||||
public Transform aimRefTransform;
|
||||
public Transform playerCameraPosRef;
|
||||
public Transform playerOriginTransform;
|
||||
public bool isRigWeapon;
|
||||
public Vector3 rigWeaponLocalPosition;
|
||||
public Quaternion rigWeaponLocalRotation;
|
||||
|
||||
public bool isAiming;
|
||||
public int curAimRefIndex = -1;
|
||||
//move curAimRefOffset towards aimRefOffset first, then move curAimOffset towards curAimRefOffset
|
||||
public Vector3 aimRefPosOffset;
|
||||
public Quaternion aimRefRotOffset;
|
||||
public Vector3 curAimPosOffset;
|
||||
public Quaternion curAimRotOffset;
|
||||
private Vector3 curAimPosVelocity;
|
||||
private Quaternion curAimRotVelocity;
|
||||
private Vector3 targetSwitchPosVelocity;
|
||||
private Quaternion targetSwitchRotVelocity;
|
||||
public List<AimReference> registeredReferences = new List<AimReference>();
|
||||
private EntityPlayerLocal holdingEntity;
|
||||
private int CurAimRefIndex
|
||||
{
|
||||
get
|
||||
{
|
||||
for (int i = registeredReferences.Count - 1; i >= 0; i--)
|
||||
{
|
||||
if (registeredReferences[i].gameObject.activeInHierarchy)
|
||||
{
|
||||
return i;
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
private AimReference CurAimRef => curAimRefIndex >= 0 && curAimRefIndex < registeredReferences.Count ? registeredReferences[curAimRefIndex] : null;
|
||||
|
||||
public ProceduralAimingData(ItemInventoryData _invData, int _indexInEntityOfAction, ActionModuleProceduralAiming _module)
|
||||
{
|
||||
holdingEntity = _invData.holdingEntity as EntityPlayerLocal;
|
||||
}
|
||||
|
||||
public void ResetAiming()
|
||||
{
|
||||
isAiming = false;
|
||||
curAimRefIndex = -1;
|
||||
aimRefPosOffset = Vector3.zero;
|
||||
aimRefRotOffset = Quaternion.identity;
|
||||
if (aimRefTransform)
|
||||
{
|
||||
aimRefTransform.SetLocalPositionAndRotation(Vector3.zero, Quaternion.identity);
|
||||
}
|
||||
curAimPosOffset = Vector3.zero;
|
||||
curAimRotOffset = Quaternion.identity;
|
||||
curAimPosVelocity = Vector3.zero;
|
||||
curAimRotVelocity = Quaternion.identity;
|
||||
targetSwitchPosVelocity = Vector3.zero;
|
||||
targetSwitchRotVelocity = Quaternion.identity;
|
||||
if (isRigWeapon && playerOriginTransform)
|
||||
{
|
||||
playerOriginTransform.localPosition = rigWeaponLocalPosition;
|
||||
playerOriginTransform.localRotation = rigWeaponLocalRotation;
|
||||
}
|
||||
}
|
||||
|
||||
public bool RegisterGroup(AimReference[] group, string name)
|
||||
{
|
||||
if (holdingEntity && holdingEntity.bFirstPersonView)
|
||||
{
|
||||
foreach (var reference in group)
|
||||
{
|
||||
if (reference.index == -1)
|
||||
{
|
||||
reference.index = registeredReferences.Count;
|
||||
registeredReferences.Add(reference);
|
||||
}
|
||||
}
|
||||
UpdateCurrentReference();
|
||||
//Log.Out($"Register group {name}\n{StackTraceUtility.ExtractStackTrace()}");
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public void UpdateCurrentReference(bool snapTo = false)
|
||||
{
|
||||
curAimRefIndex = CurAimRefIndex;
|
||||
AimReference curAimRef = CurAimRef;
|
||||
if (aimRefTransform && curAimRef)
|
||||
{
|
||||
aimRefPosOffset = curAimRef.positionOffset;
|
||||
aimRefRotOffset = curAimRef.rotationOffset;
|
||||
if (curAimRef.asReference)
|
||||
{
|
||||
aimRefPosOffset -= Vector3.Project(aimRefPosOffset - aimRefTransform.parent.InverseTransformPoint(playerCameraPosRef.position), aimRefRotOffset * Vector3.forward);
|
||||
}
|
||||
if (snapTo)
|
||||
{
|
||||
aimRefTransform.localPosition = aimRefPosOffset;
|
||||
aimRefTransform.localRotation = aimRefRotOffset;
|
||||
}
|
||||
}
|
||||
|
||||
for (int i = 0; i < registeredReferences.Count; i++)
|
||||
{
|
||||
registeredReferences[i].UpdateEnableState(isAiming && curAimRefIndex == i);
|
||||
}
|
||||
}
|
||||
|
||||
public void LateUpdateAiming()
|
||||
{
|
||||
if (aimRefTransform && playerCameraPosRef && playerOriginTransform && CurAimRef)
|
||||
{
|
||||
if (isRigWeapon)
|
||||
{
|
||||
playerOriginTransform.SetLocalPositionAndRotation(rigWeaponLocalPosition, rigWeaponLocalRotation);
|
||||
}
|
||||
float zoomInTimeMod = ergoData == null ? zoomInTime : zoomInTime / ergoData.ModifiedErgo;
|
||||
zoomInTimeMod *= 0.25f;
|
||||
//move aimRef towards target
|
||||
aimRefTransform.localPosition = Vector3.SmoothDamp(aimRefTransform.localPosition, aimRefPosOffset, ref targetSwitchPosVelocity, 0.075f);
|
||||
aimRefTransform.localRotation = QuaternionUtil.SmoothDamp(aimRefTransform.localRotation, aimRefRotOffset, ref targetSwitchRotVelocity, 0.075f);
|
||||
//calculate current target aim offset
|
||||
Vector3 aimTargetPosOffset = playerCameraPosRef.InverseTransformDirection(playerCameraPosRef.position - aimRefTransform.position);
|
||||
Quaternion aimTargetRotOffset = playerCameraPosRef.localRotation * Quaternion.Inverse(aimRefTransform.parent.localRotation * aimRefTransform.localRotation);
|
||||
//move current aim offset towards target aim offset
|
||||
if (isAiming)
|
||||
{
|
||||
curAimPosOffset = Vector3.SmoothDamp(curAimPosOffset, aimTargetPosOffset, ref curAimPosVelocity, zoomInTimeMod);
|
||||
curAimRotOffset = QuaternionUtil.SmoothDamp(curAimRotOffset, aimTargetRotOffset, ref curAimRotVelocity, zoomInTimeMod);
|
||||
}
|
||||
else
|
||||
{
|
||||
curAimPosOffset = Vector3.SmoothDamp(curAimPosOffset, Vector3.zero, ref curAimPosVelocity, zoomInTimeMod);
|
||||
curAimRotOffset = QuaternionUtil.SmoothDamp(curAimRotOffset, Quaternion.identity, ref curAimRotVelocity, zoomInTimeMod);
|
||||
}
|
||||
//apply offset to player
|
||||
if (isRigWeapon)
|
||||
{
|
||||
(playerCameraPosRef.parent.rotation * curAimRotOffset * Quaternion.Inverse(playerCameraPosRef.parent.rotation)).ToAngleAxis(out var angle, out var axis);
|
||||
playerOriginTransform.RotateAround(aimRefTransform.position, axis, angle);
|
||||
playerOriginTransform.position += playerCameraPosRef.TransformDirection(curAimPosOffset);
|
||||
}
|
||||
else
|
||||
{
|
||||
playerOriginTransform.position += playerCameraPosRef.TransformDirection(curAimPosOffset);
|
||||
(playerCameraPosRef.parent.rotation * curAimRotOffset * Quaternion.Inverse(playerCameraPosRef.parent.rotation)).ToAngleAxis(out var angle, out var axis);
|
||||
playerOriginTransform.RotateAround(aimRefTransform.position, axis, angle);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
[HarmonyPatch]
|
||||
public static class ProceduralAimingPatches
|
||||
{
|
||||
[HarmonyPatch(typeof(EntityPlayerLocal), nameof(EntityPlayerLocal.LateUpdate))]
|
||||
[HarmonyPostfix]
|
||||
private static void Postfix_LateUpdate_EntityPlayerLocal(EntityPlayerLocal __instance)
|
||||
{
|
||||
if (__instance.inventory?.holdingItemData?.actionData?[1] is IModuleContainerFor<ActionModuleProceduralAiming.ProceduralAimingData> module)
|
||||
{
|
||||
if (__instance.AimingGun != module.Instance.isAiming)
|
||||
{
|
||||
module.Instance.isAiming = __instance.AimingGun;
|
||||
module.Instance.UpdateCurrentReference(true);
|
||||
}
|
||||
module.Instance.LateUpdateAiming();
|
||||
}
|
||||
}
|
||||
}
|
||||
276
Scripts/Items/ModularActions/ActionModuleRampUp.cs
Normal file
276
Scripts/Items/ModularActions/ActionModuleRampUp.cs
Normal 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
30
Scripts/Items/ModularActions/ActionModuleTagged.cs
Normal file
30
Scripts/Items/ModularActions/ActionModuleTagged.cs
Normal 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)
|
||||
{
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
48
Scripts/Items/ModularActions/ActionModuleTranspilerTest.cs
Normal file
48
Scripts/Items/ModularActions/ActionModuleTranspilerTest.cs
Normal 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()}");
|
||||
}
|
||||
}
|
||||
260
Scripts/Items/ModularActions/ActionModuleVariableZoom.cs
Normal file
260
Scripts/Items/ModularActions/ActionModuleVariableZoom.cs
Normal file
@@ -0,0 +1,260 @@
|
||||
using HarmonyLib;
|
||||
using KFCommonUtilityLib;
|
||||
using KFCommonUtilityLib.Scripts.Attributes;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Reflection.Emit;
|
||||
using UnityEngine;
|
||||
|
||||
[TypeTarget(typeof(ItemActionZoom)), ActionDataTarget(typeof(VariableZoomData))]
|
||||
public class ActionModuleVariableZoom
|
||||
{
|
||||
private const string METASAVENAME = "CurZoomStep";
|
||||
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.isToggleOnly)
|
||||
{
|
||||
__customData.curStep = Utils.FastClamp01(__customData.curStep + _scrollWheelInput);
|
||||
__customData.stepSign = Mathf.Sign(_scrollWheelInput);
|
||||
__customData.UpdateByStep();
|
||||
ItemValue scopeValue = __customData.ScopeValue;
|
||||
if (scopeValue != null)
|
||||
{
|
||||
scopeValue.SetMetadata(METASAVENAME, __customData.SignedStep, TypedMetadataValue.TypeTag.Float);
|
||||
_actionData.invData.holdingEntity.inventory.CallOnToolbeltChangedInternal();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
[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));
|
||||
|
||||
str = _data.invData.itemValue.GetPropertyOverride("ToggleOnly", null);
|
||||
if (!string.IsNullOrEmpty(str) && bool.TryParse(str, out __customData.isToggleOnly)) ;
|
||||
|
||||
str = _data.invData.itemValue.GetPropertyOverride("ForceFovRange", null);
|
||||
if (!string.IsNullOrEmpty(str) && StringParsers.TryParseRange(str, out __customData.fovRange) && __customData.fovRange.min > 0 && __customData.fovRange.max > 0)
|
||||
{
|
||||
__customData.fovRange = new FloatRange(Mathf.Min(__customData.fovRange.max, __customData.fovRange.min), Mathf.Max(__customData.fovRange.max, __customData.fovRange.min));
|
||||
__customData.forceFov = true;
|
||||
}
|
||||
|
||||
//__customData.maxFov = ScaleToFov(__customData.minScale);
|
||||
//__customData.minFov = ScaleToFov(__customData.maxScale);
|
||||
__customData.scopeValueIndex = _data.invData.itemValue.Modifications == null ? -1 : Array.FindIndex(_data.invData.itemValue.Modifications, static v => v?.ItemClass is IModuleContainerFor<ItemModuleVariableZoom>);
|
||||
if (__customData.scopeValueIndex == -1 && _data.invData.itemValue.ItemClass is not IModuleContainerFor<ItemModuleVariableZoom>)
|
||||
{
|
||||
__customData.scopeValueIndex = int.MinValue;
|
||||
}
|
||||
ItemValue scopeValue = __customData.ScopeValue;
|
||||
if (scopeValue != null)
|
||||
{
|
||||
if (scopeValue.GetMetadata(METASAVENAME) is float curStep)
|
||||
{
|
||||
__customData.curStep = Mathf.Abs(curStep);
|
||||
__customData.stepSign = Mathf.Sign(curStep);
|
||||
}
|
||||
__customData.curStep = Utils.FastClamp01(__customData.curStep);
|
||||
scopeValue.SetMetadata(METASAVENAME, __customData.SignedStep, TypedMetadataValue.TypeTag.Float);
|
||||
_data.invData.holdingEntity.inventory.CallOnToolbeltChangedInternal();
|
||||
}
|
||||
else
|
||||
{
|
||||
__customData.curStep = Utils.FastClamp01(__customData.curStep);
|
||||
}
|
||||
__customData.UpdateByStep();
|
||||
}
|
||||
|
||||
//public static float FovToScale(float fov)
|
||||
//{
|
||||
// return Mathf.Rad2Deg * 2 * Mathf.Atan(Mathf.Tan(Mathf.Deg2Rad * 27.5f) / fov);
|
||||
//}
|
||||
|
||||
//public static float ScaleToFov(float scale)
|
||||
//{
|
||||
// return Mathf.Rad2Deg * 2 * Mathf.Atan(Mathf.Tan(Mathf.Deg2Rad * 27.5f) / scale);
|
||||
//}
|
||||
|
||||
//public static float GetNext(float cur)
|
||||
//{
|
||||
// return Mathf.Sin(Mathf.PI * cur / 2);
|
||||
//}
|
||||
|
||||
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 bool forceFov = false;
|
||||
public FloatRange fovRange = new FloatRange(15f, 15f);
|
||||
public float curStep = 0;
|
||||
public float stepSign = 1f;
|
||||
public bool isToggleOnly = false;
|
||||
public bool shouldUpdate = true;
|
||||
public int scopeValueIndex = int.MinValue;
|
||||
|
||||
public float SignedStep => curStep * stepSign;
|
||||
public ItemValue ScopeValue
|
||||
{
|
||||
get
|
||||
{
|
||||
if (invData == null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
if (scopeValueIndex == -1)
|
||||
{
|
||||
return invData.itemValue;
|
||||
}
|
||||
else if (scopeValueIndex >= 0 && scopeValueIndex < invData.itemValue.Modifications.Length)
|
||||
{
|
||||
return invData.itemValue.Modifications[scopeValueIndex];
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
public ItemInventoryData invData = null;
|
||||
|
||||
public VariableZoomData(ItemInventoryData _invData, int _indexInEntityOfAction, ActionModuleVariableZoom _module)
|
||||
{
|
||||
invData = _invData;
|
||||
}
|
||||
|
||||
public void ToggleZoom()
|
||||
{
|
||||
//if (scopeValue != null && scopeValue.GetMetadata(METASAVENAME) is float curStep)
|
||||
//{
|
||||
// this.curStep = Mathf.Abs(curStep);
|
||||
// stepSign = MathF.Sign(curStep);
|
||||
//}
|
||||
if (stepSign > 0)
|
||||
{
|
||||
if (this.curStep >= 1)
|
||||
{
|
||||
this.curStep = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
this.curStep = 1;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (this.curStep <= 0)
|
||||
{
|
||||
this.curStep = 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
this.curStep = 0;
|
||||
}
|
||||
}
|
||||
UpdateByStep();
|
||||
ItemValue scopeValue = ScopeValue;
|
||||
if (scopeValue != null)
|
||||
{
|
||||
scopeValue.SetMetadata(METASAVENAME, SignedStep, TypedMetadataValue.TypeTag.Float);
|
||||
invData.holdingEntity.inventory.CallOnToolbeltChangedInternal();
|
||||
}
|
||||
}
|
||||
|
||||
public void UpdateByStep()
|
||||
{
|
||||
//curFov = Utils.FastLerp(maxFov, minFov, GetNext(curStep));
|
||||
//curScale = FovToScale(curFov);
|
||||
curScale = Utils.FastLerp(minScale, maxScale, curStep);
|
||||
shouldUpdate = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
[HarmonyPatch]
|
||||
public static class VariableZoomPatches
|
||||
{
|
||||
[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, isUIOpen);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private static void UpdateLocalInput(EntityPlayerLocal _player, bool _isUIOpen)
|
||||
{
|
||||
if (_isUIOpen || _player.emodel.IsRagdollActive || _player.IsDead() || _player.AttachedToEntity != null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (PlayerActionKFLib.Instance.Enabled && PlayerActionKFLib.Instance.ToggleZoom.WasPressed)
|
||||
{
|
||||
var actionData = _player.inventory.holdingItemData.actionData[1];
|
||||
if (actionData is IModuleContainerFor<ActionModuleVariableZoom.VariableZoomData> variableZoomData)
|
||||
{
|
||||
variableZoomData.Instance.ToggleZoom();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//[HarmonyPatch(typeof(Inventory), nameof(Inventory.SetItem), new Type[] { typeof(int), typeof(ItemValue), typeof(int), typeof(bool) })]
|
||||
//[HarmonyTranspiler]
|
||||
//private static IEnumerable<CodeInstruction> Transpiler_Test(IEnumerable<CodeInstruction> instructions)
|
||||
//{
|
||||
// var codes = instructions.ToList();
|
||||
// var fld = AccessTools.Field(typeof(ItemStack), nameof(ItemStack.count));
|
||||
|
||||
// for (int i = 0; i < codes.Count; i++)
|
||||
// {
|
||||
// if (codes[i].StoresField(fld))
|
||||
// {
|
||||
// codes.InsertRange(i + 1, new[]
|
||||
// {
|
||||
// new CodeInstruction(OpCodes.Ldloc_0),
|
||||
// new CodeInstruction(OpCodes.Ldarg_0),
|
||||
// new CodeInstruction(OpCodes.Ldarg_1),
|
||||
// CodeInstruction.Call(typeof(VariableZoomPatches), nameof(LogMsg))
|
||||
// });
|
||||
// break;
|
||||
// }
|
||||
// }
|
||||
// return codes;
|
||||
//}
|
||||
|
||||
//private static void LogMsg(bool flag, Inventory inv, int idx)
|
||||
//{
|
||||
// if (inv.holdingItemIdx == idx)
|
||||
// Log.Out($"changed: {flag}\n{StackTraceUtility.ExtractStackTrace()}");
|
||||
//}
|
||||
}
|
||||
Reference in New Issue
Block a user