336 lines
14 KiB
C#
336 lines
14 KiB
C#
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);
|
|
}
|
|
}
|
|
}
|
|
} |