1326 lines
57 KiB
C#
1326 lines
57 KiB
C#
using HarmonyLib;
|
|
using KFCommonUtilityLib.Scripts.StaticManagers;
|
|
using KFCommonUtilityLib.Scripts.Utilities;
|
|
using System;
|
|
using System.Collections.Generic;
|
|
using UniLinq;
|
|
using System.Reflection;
|
|
using System.Reflection.Emit;
|
|
using System.Xml.Linq;
|
|
using UnityEngine;
|
|
using KFCommonUtilityLib.Scripts.NetPackages;
|
|
using KFCommonUtilityLib;
|
|
|
|
[HarmonyPatch]
|
|
public static class CommonUtilityPatch
|
|
{
|
|
//fix reloading issue and onSelfRangedBurstShot timing
|
|
public static void FakeReload(EntityAlive holdingEntity, ItemActionRanged.ItemActionDataRanged _actionData)
|
|
{
|
|
if (!holdingEntity)
|
|
return;
|
|
_actionData.isReloading = true;
|
|
_actionData.isWeaponReloading = true;
|
|
holdingEntity.MinEventContext.ItemActionData = _actionData;
|
|
holdingEntity.FireEvent(MinEventTypes.onReloadStart, true);
|
|
_actionData.isReloading = false;
|
|
_actionData.isWeaponReloading = false;
|
|
_actionData.isReloadCancelled = false;
|
|
_actionData.isWeaponReloadCancelled = false;
|
|
holdingEntity.FireEvent(MinEventTypes.onReloadStop);
|
|
|
|
AnimationAmmoUpdateState.SetAmmoCountForEntity(holdingEntity, holdingEntity.inventory.holdingItemIdx);
|
|
}
|
|
|
|
[HarmonyPatch(typeof(ItemActionRanged), nameof(ItemActionRanged.SwapAmmoType))]
|
|
[HarmonyTranspiler]
|
|
private static IEnumerable<CodeInstruction> Transpiler_SwapAmmoType_ItemActionRanged(IEnumerable<CodeInstruction> instructions)
|
|
{
|
|
var codes = new List<CodeInstruction>(instructions);
|
|
|
|
for (int i = 0; i < codes.Count; ++i)
|
|
{
|
|
if (codes[i].opcode == OpCodes.Ret)
|
|
{
|
|
codes.InsertRange(i, new CodeInstruction[]
|
|
{
|
|
new CodeInstruction(OpCodes.Ldarg_1),
|
|
new CodeInstruction(OpCodes.Ldloc_0),
|
|
CodeInstruction.Call(typeof(CommonUtilityPatch), nameof(FakeReload))
|
|
});
|
|
break;
|
|
}
|
|
}
|
|
|
|
return codes;
|
|
}
|
|
|
|
[HarmonyPatch(typeof(ItemActionRanged), nameof(ItemActionRanged.ExecuteAction))]
|
|
[HarmonyTranspiler]
|
|
private static IEnumerable<CodeInstruction> Transpiler_ExecuteAction_ItemActionRanged(IEnumerable<CodeInstruction> instructions)
|
|
{
|
|
var codes = new List<CodeInstruction>(instructions);
|
|
var mtd_fire_event = AccessTools.Method(typeof(EntityAlive), nameof(EntityAlive.FireEvent));
|
|
var mtd_get_model_layer = AccessTools.Method(typeof(EntityAlive), nameof(EntityAlive.GetModelLayer));
|
|
var mtd_get_perc_left = AccessTools.PropertyGetter(typeof(ItemValue), nameof(ItemValue.PercentUsesLeft));
|
|
|
|
int take = -1, insert = -1;
|
|
for (int i = 0; i < codes.Count; ++i)
|
|
{
|
|
if (codes[i].opcode == OpCodes.Ldc_I4_S && codes[i].OperandIs((int)MinEventTypes.onSelfRangedBurstShotEnd) && codes[i + 2].Calls(mtd_fire_event))
|
|
take = i - 3;
|
|
else if (codes[i].Calls(mtd_get_model_layer))
|
|
insert = i + 2;
|
|
}
|
|
|
|
if (take < insert)
|
|
{
|
|
var list = codes.GetRange(take, 6);
|
|
codes.InsertRange(insert, list);
|
|
codes.RemoveRange(take, 6);
|
|
}
|
|
|
|
return codes;
|
|
}
|
|
//fix recoil animation does not match weapon RPM
|
|
private static int weaponFireHash = Animator.StringToHash("WeaponFire");
|
|
private static int aimHash = Animator.StringToHash("IsAiming");
|
|
private static HashSet<int> hash_shot_state = new HashSet<int>();
|
|
private static HashSet<int> hash_aimshot_state = new HashSet<int>();
|
|
|
|
public static void InitShotStates()
|
|
{
|
|
string[] weapons =
|
|
{
|
|
"fpvAK47",
|
|
"fpvMagnum",
|
|
"fpvRocketLauncher",
|
|
"fpvSawedOffShotgun",
|
|
"fpvBlunderbuss",
|
|
"fpvCrossbow",
|
|
"fpvPistol",
|
|
"fpvHuntingRifle",
|
|
"fpvSMG",
|
|
"fpvSniperRifle",
|
|
"M60",
|
|
"fpvDoubleBarrelShotgun",
|
|
//"fpvJunkTurret",
|
|
"fpvTacticalAssaultRifle",
|
|
"fpvDesertEagle",
|
|
"fpvAutoShotgun",
|
|
"fpvSharpShooterRifle",
|
|
"fpvPipeMachineGun",
|
|
"fpvPipeRifle",
|
|
"fpvPipeRevolver",
|
|
"fpvPipeShotgun",
|
|
"fpvLeverActionRifle",
|
|
};
|
|
foreach (string weapon in weapons)
|
|
{
|
|
hash_shot_state.Add(Animator.StringToHash(weapon + "Fire"));
|
|
hash_aimshot_state.Add(Animator.StringToHash(weapon + "AimFire"));
|
|
}
|
|
}
|
|
|
|
[HarmonyPatch(typeof(EntityPlayerLocal), nameof(EntityPlayerLocal.OnFired))]
|
|
[HarmonyPostfix]
|
|
private static void Postfix_OnFired_EntityPlayerLocal(EntityPlayerLocal __instance)
|
|
{
|
|
if (!__instance.bFirstPersonView)
|
|
return;
|
|
|
|
ItemActionRanged.ItemActionDataRanged _rangedData;
|
|
if ((_rangedData = __instance.inventory.holdingItemData.actionData[0] as ItemActionRanged.ItemActionDataRanged) == null && (_rangedData = __instance.inventory.holdingItemData.actionData[1] as ItemActionRanged.ItemActionDataRanged) == null)
|
|
return;
|
|
|
|
if (_rangedData.invData.model.TryGetComponent<AnimationTargetsAbs>(out var targets) && targets.ItemFpv)
|
|
return;
|
|
|
|
var anim = (__instance.emodel.avatarController as AvatarLocalPlayerController).FPSArms.Animator;
|
|
if (anim.IsInTransition(0))
|
|
return;
|
|
|
|
var curState = anim.GetCurrentAnimatorStateInfo(0);
|
|
if (curState.length > _rangedData.Delay)
|
|
{
|
|
bool aimState = anim.GetBool(aimHash);
|
|
short shotState = 0;
|
|
if (hash_shot_state.Contains(curState.shortNameHash))
|
|
shotState = 1;
|
|
else if (hash_aimshot_state.Contains(curState.shortNameHash))
|
|
shotState = 2;
|
|
if (shotState == 0 || (shotState == 1 && aimState) || (shotState == 2 && !aimState))
|
|
{
|
|
if (shotState > 0)
|
|
anim.ResetTrigger(weaponFireHash);
|
|
return;
|
|
}
|
|
|
|
//current state, layer 0, offset 0
|
|
anim.PlayInFixedTime(0, 0, 0);
|
|
anim.ResetTrigger(weaponFireHash);
|
|
if (_rangedData.invData.itemValue.Meta == 0)
|
|
{
|
|
__instance.emodel.avatarController.CancelEvent(weaponFireHash);
|
|
Log.Out("Cancel fire event because meta is 0");
|
|
}
|
|
}
|
|
}
|
|
|
|
//[HarmonyPatch(typeof(ItemActionRanged), nameof(ItemActionRanged.ItemActionEffects))]
|
|
//[HarmonyPostfix]
|
|
//private static void Postfix_ItemActionEffects_ItemActionRanged(ItemActionData _actionData, int _firingState)
|
|
//{
|
|
// if (_firingState == 0 && _actionData.invData.holdingEntity is EntityPlayerLocal && !(_actionData.invData.itemValue.ItemClass.Actions[0] is ItemActionCatapult))
|
|
// {
|
|
// _actionData.invData.holdingEntity?.emodel.avatarController.CancelEvent(weaponFireHash);
|
|
// //Log.Out("Cancel fire event because firing state is 0\n" + StackTraceUtility.ExtractStackTrace());
|
|
// }
|
|
//}
|
|
|
|
//[HarmonyPatch(typeof(GameManager), "gmUpdate")]
|
|
//[HarmonyTranspiler]
|
|
//private static IEnumerable<CodeInstruction> Transpiler_gmUpdate_GameManager(IEnumerable<CodeInstruction> instructions)
|
|
//{
|
|
// var codes = new List<CodeInstruction>(instructions);
|
|
// var mtd_unload = AccessTools.Method(typeof(Resources), nameof(Resources.UnloadUnusedAssets));
|
|
// var fld_duration = AccessTools.Field(typeof(GameManager), "unloadAssetsDuration");
|
|
|
|
// for (int i = 0; i < codes.Count; ++i)
|
|
// {
|
|
// if (codes[i].opcode == OpCodes.Call && codes[i].Calls(mtd_unload))
|
|
// {
|
|
// for (int j = i; j >= 0; --j)
|
|
// {
|
|
// if (codes[j].opcode == OpCodes.Ldfld && codes[j].LoadsField(fld_duration) && codes[j + 1].opcode == OpCodes.Ldc_R4)
|
|
// codes[j + 1].operand = (float)codes[j + 1].operand / 2;
|
|
// }
|
|
// break;
|
|
// }
|
|
// }
|
|
|
|
// return codes;
|
|
//}
|
|
|
|
//internal static void ForceUpdateGC()
|
|
//{
|
|
// if (GameManager.IsDedicatedServer)
|
|
// return;
|
|
// if (GameManager.frameCount % 18000 == 0)
|
|
// {
|
|
// long rss = GetRSS.GetCurrentRSS();
|
|
// if (rss / 1024 / 1024 > 6144)
|
|
// {
|
|
// Log.Out("Memory usage exceeds threshold, now performing garbage collection...");
|
|
// GC.Collect();
|
|
// }
|
|
// }
|
|
//}
|
|
|
|
//altmode workarounds
|
|
//deprecated by action module
|
|
private static void ParseAltRequirements(XElement _node)
|
|
{
|
|
string itemName = _node.GetAttribute("name");
|
|
if (string.IsNullOrEmpty(itemName))
|
|
{
|
|
return;
|
|
}
|
|
ItemClass item = ItemClass.GetItemClass(itemName);
|
|
for (int i = 0; i < item.Actions.Length; i++)
|
|
{
|
|
if (item.Actions[i] is ItemActionAltMode _alt)
|
|
_alt.ParseAltRequirements(_node, i);
|
|
}
|
|
}
|
|
|
|
[HarmonyPatch(typeof(ItemClassesFromXml), nameof(ItemClassesFromXml.parseItem))]
|
|
[HarmonyPostfix]
|
|
private static void Postfix_parseItem_ItemClassesFromXml(XElement _node)
|
|
{
|
|
ParseAltRequirements(_node);
|
|
}
|
|
|
|
[HarmonyPatch(typeof(ItemClass), nameof(ItemClass.ExecuteAction))]
|
|
[HarmonyPrefix]
|
|
private static bool Prefix_ExecuteAction_ItemClass(ItemClass __instance, int _actionIdx, ItemInventoryData _data, bool _bReleased)
|
|
{
|
|
if (!_bReleased && __instance.Actions[_actionIdx] is ItemActionAltMode _alt)
|
|
_alt.SetAltRequirement(_data.actionData[_actionIdx]);
|
|
|
|
return true;
|
|
}
|
|
|
|
[HarmonyPatch(typeof(DynamicProperties), nameof(DynamicProperties.Parse))]
|
|
[HarmonyPrefix]
|
|
private static bool Prefix_Parse_DynamicProperties(XElement elementProperty)
|
|
{
|
|
if (elementProperty.Name.LocalName != "property")
|
|
return false;
|
|
return true;
|
|
}
|
|
|
|
//MinEventParams workarounds
|
|
[HarmonyPatch(typeof(ItemActionRanged), nameof(ItemActionRanged.fireShot))]
|
|
[HarmonyTranspiler]
|
|
private static IEnumerable<CodeInstruction> Transpiler_fireShot_ItemActionRanged(IEnumerable<CodeInstruction> instructions)
|
|
{
|
|
var codes = new List<CodeInstruction>(instructions);
|
|
|
|
var fld_ranged_tag = AccessTools.Field(typeof(ItemActionAttack), nameof(ItemActionAttack.RangedTag));
|
|
var fld_params = AccessTools.Field(typeof(EntityAlive), nameof(EntityAlive.MinEventContext));
|
|
|
|
for (int i = 0; i < codes.Count; i++)
|
|
{
|
|
if (codes[i].LoadsField(fld_ranged_tag))
|
|
{
|
|
if (!codes[i + 3].LoadsField(fld_params))
|
|
{
|
|
codes.InsertRange(i + 2, new CodeInstruction[]
|
|
{
|
|
new CodeInstruction(OpCodes.Ldloc_1),
|
|
CodeInstruction.LoadField(typeof(EntityAlive), nameof(EntityAlive.MinEventContext)),
|
|
new CodeInstruction(OpCodes.Dup),
|
|
new CodeInstruction(OpCodes.Ldloc, 10),
|
|
CodeInstruction.LoadField(typeof(WorldRayHitInfo), nameof(WorldRayHitInfo.hit)),
|
|
CodeInstruction.LoadField(typeof(HitInfoDetails), nameof(HitInfoDetails.pos)),
|
|
CodeInstruction.StoreField(typeof(MinEventParams), nameof(MinEventParams.Position)),
|
|
new CodeInstruction(OpCodes.Ldloc_1),
|
|
CodeInstruction.Call(typeof(EntityAlive), nameof(EntityAlive.GetPosition)),
|
|
CodeInstruction.StoreField(typeof(MinEventParams), nameof(MinEventParams.StartPosition))
|
|
});
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
|
|
return codes;
|
|
}
|
|
|
|
[HarmonyPatch(typeof(ItemActionRanged), nameof(ItemActionRanged.OnHoldingUpdate))]
|
|
[HarmonyTranspiler]
|
|
private static IEnumerable<CodeInstruction> Transpiler_OnHoldingUpdate_ItemActionRanged(IEnumerable<CodeInstruction> instructions)
|
|
{
|
|
var mtd_release = AccessTools.Method(typeof(ItemActionRanged), nameof(ItemActionRanged.triggerReleased));
|
|
var codes = instructions.ToList();
|
|
|
|
for (int i = 0; i < codes.Count; i++)
|
|
{
|
|
if (codes[i].Calls(mtd_release))
|
|
{
|
|
codes[i + 1].labels.Clear();
|
|
codes[i + 1].MoveLabelsFrom(codes[i - 20]);
|
|
codes.RemoveRange(i - 20, 21);
|
|
break;
|
|
}
|
|
}
|
|
|
|
return codes;
|
|
}
|
|
|
|
[HarmonyPatch(typeof(ItemActionRanged), nameof(ItemActionRanged.triggerReleased))]
|
|
[HarmonyTranspiler]
|
|
private static IEnumerable<CodeInstruction> Transpiler_triggerReleased_ItemActionRanged(IEnumerable<CodeInstruction> instructions)
|
|
{
|
|
var mtd_effect = AccessTools.Method(typeof(IGameManager), nameof(IGameManager.ItemActionEffectsServer));
|
|
var mtd_data = AccessTools.Method(typeof(ItemActionRanged), nameof(ItemActionRanged.getUserData));
|
|
var codes = instructions.ToList();
|
|
|
|
for (int i = 0; i < codes.Count; i++)
|
|
{
|
|
if (codes[i].Calls(mtd_effect))
|
|
{
|
|
codes.InsertRange(i, new CodeInstruction[]
|
|
{
|
|
new CodeInstruction(OpCodes.Ldarg_0),
|
|
new CodeInstruction(OpCodes.Ldarg_1),
|
|
new CodeInstruction(OpCodes.Callvirt, mtd_data)
|
|
});
|
|
codes.RemoveAt(i - 1);
|
|
break;
|
|
}
|
|
}
|
|
|
|
return codes;
|
|
}
|
|
|
|
[HarmonyPatch(typeof(GameManager), nameof(GameManager.StartGame))]
|
|
[HarmonyPrefix]
|
|
private static bool Prefix_StartGame_GameManager()
|
|
{
|
|
CustomEffectEnumManager.InitFinal();
|
|
return true;
|
|
}
|
|
|
|
[HarmonyPatch(typeof(PassiveEffect), nameof(PassiveEffect.ParsePassiveEffect))]
|
|
[HarmonyTranspiler]
|
|
private static IEnumerable<CodeInstruction> Transpiler_ParsePassiveEffect_PassiveEffect(IEnumerable<CodeInstruction> instructions)
|
|
{
|
|
var codes = instructions.ToList();
|
|
MethodInfo mtd_enum_parse = AccessTools.Method(typeof(EnumUtils), nameof(EnumUtils.Parse), new[] { typeof(string), typeof(bool) }, new[] { typeof(PassiveEffects) });
|
|
|
|
for (int i = 0; i < codes.Count; i++)
|
|
{
|
|
if (codes[i].Calls(mtd_enum_parse))
|
|
{
|
|
codes.Insert(i + 1, CodeInstruction.Call(typeof(CustomEffectEnumManager), nameof(CustomEffectEnumManager.RegisterOrGetEnum), new[] { typeof(string), typeof(bool) }, new[] { typeof(PassiveEffects) }));
|
|
codes.RemoveAt(i);
|
|
break;
|
|
}
|
|
}
|
|
|
|
return codes;
|
|
}
|
|
|
|
[HarmonyPatch(typeof(MinEventActionBase), nameof(MinEventActionBase.ParseXmlAttribute))]
|
|
[HarmonyTranspiler]
|
|
private static IEnumerable<CodeInstruction> Transpiler_ParseXmlAttribute_MinEventActionBase(IEnumerable<CodeInstruction> instructions)
|
|
{
|
|
var codes = instructions.ToList();
|
|
MethodInfo mtd_enum_parse = AccessTools.Method(typeof(EnumUtils), nameof(EnumUtils.Parse), new[] { typeof(string), typeof(bool) }, new[] { typeof(MinEventTypes) });
|
|
|
|
for (int i = 0; i < codes.Count; i++)
|
|
{
|
|
if (codes[i].Calls(mtd_enum_parse))
|
|
{
|
|
codes.Insert(i + 1, CodeInstruction.Call(typeof(CustomEffectEnumManager), nameof(CustomEffectEnumManager.RegisterOrGetEnum), new[] { typeof(string), typeof(bool) }, new[] { typeof(MinEventTypes) }));
|
|
codes.RemoveAt(i);
|
|
break;
|
|
}
|
|
}
|
|
|
|
return codes;
|
|
}
|
|
|
|
[HarmonyPatch(typeof(ItemActionDynamicMelee), nameof(ItemActionDynamicMelee.OnHoldingUpdate))]
|
|
[HarmonyTranspiler]
|
|
private static IEnumerable<CodeInstruction> Transpiler_OnHoldingUpdate_ItemActionDynamicMelee(IEnumerable<CodeInstruction> instructions)
|
|
{
|
|
var codes = instructions.ToList();
|
|
for (int i = 0; i < codes.Count; i++)
|
|
{
|
|
if (codes[i].Is(OpCodes.Ldc_R4, 0.1f))
|
|
{
|
|
codes.RemoveRange(i, 2);
|
|
break;
|
|
}
|
|
}
|
|
return codes;
|
|
}
|
|
|
|
[HarmonyPatch(typeof(ItemActionDynamicMelee), nameof(ItemActionDynamicMelee.canStartAttack))]
|
|
[HarmonyTranspiler]
|
|
private static IEnumerable<CodeInstruction> Transpiler_canStartAttack_ItemActionDynamicMelee(IEnumerable<CodeInstruction> instructions)
|
|
{
|
|
var codes = instructions.ToList();
|
|
for (int i = 0; i < codes.Count; i++)
|
|
{
|
|
if (codes[i].Is(OpCodes.Ldc_R4, 0.1f))
|
|
{
|
|
codes.RemoveRange(i, 2);
|
|
break;
|
|
}
|
|
}
|
|
return codes;
|
|
}
|
|
|
|
/// <summary>
|
|
/// projectile direct hit damage percent
|
|
/// removed due to new explosion damage passives
|
|
/// </summary>
|
|
/// <param name="instructions"></param>
|
|
/// <returns></returns>
|
|
//[HarmonyPatch(typeof(ProjectileMoveScript), nameof(ProjectileMoveScript.checkCollision))]
|
|
//[HarmonyTranspiler]
|
|
//private static IEnumerable<CodeInstruction> Transpiler_checkCollision_ProjectileMoveScript(IEnumerable<CodeInstruction> instructions)
|
|
//{
|
|
// var codes = instructions.ToList();
|
|
// var fld_strain = AccessTools.Field(typeof(ItemActionLauncher.ItemActionDataLauncher), nameof(ItemActionLauncher.ItemActionDataLauncher.strainPercent));
|
|
// var mtd_block = AccessTools.Method(typeof(ItemActionAttack), nameof(ItemActionAttack.GetDamageBlock));
|
|
|
|
// for (int i = 0; i < codes.Count; i++)
|
|
// {
|
|
// if (codes[i].LoadsField(fld_strain))
|
|
// {
|
|
// codes.InsertRange(i + 1, new CodeInstruction[]
|
|
// {
|
|
// new CodeInstruction(OpCodes.Ldarg_0),
|
|
// CodeInstruction.LoadField(typeof(ProjectileMoveScript), nameof(ProjectileMoveScript.itemValueProjectile)),
|
|
// new CodeInstruction(OpCodes.Ldloc_S, 4),
|
|
// CodeInstruction.Call(typeof(CommonUtilityPatch), codes[i - 3].Calls(mtd_block) ? nameof(GetProjectileBlockDamagePerc) : nameof(GetProjectileEntityDamagePerc)),
|
|
// new CodeInstruction(OpCodes.Mul)
|
|
// });
|
|
// }
|
|
// }
|
|
|
|
// return codes;
|
|
//}
|
|
|
|
//public static float GetProjectileBlockDamagePerc(ItemValue _itemValue, EntityAlive _holdingEntity)
|
|
//{
|
|
// return EffectManager.GetValue(CustomEnums.ProjectileImpactDamagePercentBlock, _itemValue, 1, _holdingEntity, null);
|
|
//}
|
|
|
|
//public static float GetProjectileEntityDamagePerc(ItemValue _itemValue, EntityAlive _holdingEntity)
|
|
//{
|
|
// return EffectManager.GetValue(CustomEnums.ProjectileImpactDamagePercentEntity, _itemValue, 1, _holdingEntity, null);
|
|
//}
|
|
|
|
/// <summary>
|
|
/// force tpv crosshair
|
|
/// </summary>
|
|
/// <param name="instructions"></param>
|
|
/// <returns></returns>
|
|
[HarmonyPatch(typeof(EntityPlayerLocal), nameof(EntityPlayerLocal.guiDrawCrosshair))]
|
|
[HarmonyTranspiler]
|
|
private static IEnumerable<CodeInstruction> Transpiler_guiDrawCrosshair_EntityPlayerLocal(IEnumerable<CodeInstruction> instructions)
|
|
{
|
|
var codes = instructions.ToList();
|
|
|
|
FieldInfo fld_debug = AccessTools.Field(typeof(ItemAction), nameof(ItemAction.ShowDistanceDebugInfo));
|
|
|
|
for ( int i = 0; i < codes.Count; i++ )
|
|
{
|
|
if (codes[i].LoadsField(fld_debug))
|
|
{
|
|
var label = codes[i - 1].operand;
|
|
codes.InsertRange(i, new[]
|
|
{
|
|
new CodeInstruction(OpCodes.Ldarg_0),
|
|
CodeInstruction.LoadField(typeof(EntityPlayerLocal), nameof(EntityPlayerLocal.bFirstPersonView)),
|
|
new CodeInstruction(OpCodes.Brfalse_S, label)
|
|
});
|
|
break;
|
|
}
|
|
}
|
|
|
|
return codes;
|
|
}
|
|
|
|
/// <summary>
|
|
/// correctly apply muzzle flash silence with modifications
|
|
/// </summary>
|
|
/// <param name="_data"></param>
|
|
[HarmonyPatch(typeof(ItemActionRanged), nameof(ItemActionRanged.OnModificationsChanged))]
|
|
[HarmonyPostfix]
|
|
private static void Postfix_OnModificationsChanged_ItemActionRanged(ItemActionData _data)
|
|
{
|
|
ItemActionRanged.ItemActionDataRanged itemActionDataRanged = _data as ItemActionRanged.ItemActionDataRanged;
|
|
if (itemActionDataRanged.SoundStart.Contains("silenced"))
|
|
{
|
|
itemActionDataRanged.IsFlashSuppressed = true;
|
|
}
|
|
|
|
//should fix stuck on switching item?
|
|
itemActionDataRanged.isReloadCancelled = false;
|
|
itemActionDataRanged.isWeaponReloadCancelled = false;
|
|
itemActionDataRanged.isReloading = false;
|
|
itemActionDataRanged.isWeaponReloading = false;
|
|
itemActionDataRanged.isChangingAmmoType = false;
|
|
}
|
|
|
|
#region item tags modifier
|
|
|
|
/// <summary>
|
|
/// should handle swapping mod
|
|
/// first check if the mod to install can be installed after current mod is removed
|
|
/// then check if any other mod requires or conflicts current mod
|
|
/// </summary>
|
|
/// <param name="instructions"></param>
|
|
/// <param name="generator"></param>
|
|
/// <returns></returns>
|
|
[HarmonyPatch(typeof(XUiC_ItemPartStack), nameof(XUiC_ItemPartStack.CanSwap))]
|
|
[HarmonyTranspiler]
|
|
private static IEnumerable<CodeInstruction> Transpiler_CanSwap_XUiC_ItemPartStack(IEnumerable<CodeInstruction> instructions, ILGenerator generator)
|
|
{
|
|
var codes = instructions.ToList();
|
|
|
|
LocalBuilder lbd_tags_if_remove_prev = generator.DeclareLocal(typeof(FastTags<TagGroup.Global>));
|
|
LocalBuilder lbd_tags_if_install_new = generator.DeclareLocal(typeof(FastTags<TagGroup.Global>));
|
|
MethodInfo mtd_get_item_class = AccessTools.PropertyGetter(typeof(ItemValue), nameof(ItemValue.ItemClass));
|
|
MethodInfo mtd_has_any_tags = AccessTools.Method(typeof(ItemClass), nameof(ItemClass.HasAnyTags));
|
|
MethodInfo mtd_test_any_set = AccessTools.Method(typeof(FastTags<TagGroup.Global>), nameof(FastTags<TagGroup.Global>.Test_AnySet));
|
|
FieldInfo fld_mod = AccessTools.Field(typeof(ItemValue), nameof(ItemValue.Modifications));
|
|
FieldInfo fld_installable_tags = AccessTools.Field(typeof(ItemClassModifier), nameof(ItemClassModifier.InstallableTags));
|
|
|
|
for (int i = 3; i < codes.Count; i++)
|
|
{
|
|
//get current tags
|
|
if (codes[i].opcode == OpCodes.Stloc_2)
|
|
{
|
|
codes.InsertRange(i + 1, new[]
|
|
{
|
|
new CodeInstruction(OpCodes.Ldloc_1),
|
|
new CodeInstruction(OpCodes.Ldarg_0),
|
|
CodeInstruction.LoadField(typeof(XUiC_ItemPartStack), "itemValue"),
|
|
CodeInstruction.Call(typeof(LocalItemTagsManager), nameof(LocalItemTagsManager.GetTagsAsIfNotInstalled)),
|
|
new CodeInstruction(OpCodes.Stloc_S, lbd_tags_if_remove_prev),
|
|
new CodeInstruction(OpCodes.Ldloc_1),
|
|
new CodeInstruction(OpCodes.Ldloc_0),
|
|
CodeInstruction.Call(typeof(LocalItemTagsManager), nameof(LocalItemTagsManager.GetTagsAsIfInstalled)),
|
|
new CodeInstruction(OpCodes.Stloc_S, lbd_tags_if_install_new)
|
|
});
|
|
i += 10;
|
|
Log.Out("mod 1!!!");
|
|
}
|
|
//replace checking tags
|
|
else if (codes[i].Calls(mtd_has_any_tags) && codes[i - 3].opcode == OpCodes.Ldloc_2)
|
|
{
|
|
if (codes[i - 1].LoadsField(fld_installable_tags) && (codes[i + 1].opcode == OpCodes.Brtrue || codes[i + 1].opcode == OpCodes.Brtrue_S))
|
|
{
|
|
var lbl_prev = codes[i + 4].ExtractLabels();
|
|
var lbl_jump = generator.DefineLabel();
|
|
codes[i + 4].WithLabels(lbl_jump);
|
|
codes.InsertRange(i + 4, new[]
|
|
{
|
|
new CodeInstruction(OpCodes.Ldloc_1).WithLabels(lbl_prev),
|
|
new CodeInstruction(OpCodes.Ldarg_0),
|
|
CodeInstruction.LoadField(typeof(XUiC_ItemPartStack), "itemValue"),
|
|
new CodeInstruction(OpCodes.Ldloc_0),
|
|
CodeInstruction.Call(typeof(LocalItemTagsManager), nameof(LocalItemTagsManager.CanSwapMod)),
|
|
new CodeInstruction(OpCodes.Brtrue, lbl_jump),
|
|
new CodeInstruction(OpCodes.Ldc_I4_0),
|
|
new CodeInstruction(OpCodes.Ret)
|
|
});
|
|
}
|
|
codes[i - 3].opcode = OpCodes.Ldloca_S;
|
|
codes[i - 3].operand = lbd_tags_if_remove_prev;
|
|
codes[i].opcode = OpCodes.Call;
|
|
codes[i].operand = mtd_test_any_set;
|
|
Log.Out("mod 2!!!");
|
|
}
|
|
}
|
|
|
|
return codes;
|
|
}
|
|
|
|
[HarmonyPatch(typeof(XUiC_ItemCosmeticStack), nameof(XUiC_ItemCosmeticStack.CanSwap))]
|
|
[HarmonyTranspiler]
|
|
private static IEnumerable<CodeInstruction> Transpiler_CanSwap_XUiC_ItemCosmeticStack(IEnumerable<CodeInstruction> instructions, ILGenerator generator)
|
|
{
|
|
var codes = instructions.ToList();
|
|
|
|
LocalBuilder lbd_tags_if_remove_prev = generator.DeclareLocal(typeof(FastTags<TagGroup.Global>));
|
|
LocalBuilder lbd_tags_if_install_new = generator.DeclareLocal(typeof(FastTags<TagGroup.Global>));
|
|
LocalBuilder lbd_item_being_assembled = generator.DeclareLocal(typeof(ItemValue));
|
|
MethodInfo mtd_get_item_class = AccessTools.PropertyGetter(typeof(ItemValue), nameof(ItemValue.ItemClass));
|
|
MethodInfo mtd_has_any_tags = AccessTools.Method(typeof(ItemClass), nameof(ItemClass.HasAnyTags));
|
|
MethodInfo mtd_test_any_set = AccessTools.Method(typeof(FastTags<TagGroup.Global>), nameof(FastTags<TagGroup.Global>.Test_AnySet));
|
|
MethodInfo mtd_get_xui = AccessTools.PropertyGetter(typeof(XUiController), nameof(XUiController.xui));
|
|
MethodInfo mtd_get_cur_item = AccessTools.PropertyGetter(typeof(XUiM_AssembleItem), nameof(XUiM_AssembleItem.CurrentItem));
|
|
FieldInfo fld_cos = AccessTools.Field(typeof(ItemValue), nameof(ItemValue.CosmeticMods));
|
|
FieldInfo fld_installable_tags = AccessTools.Field(typeof(ItemClassModifier), nameof(ItemClassModifier.InstallableTags));
|
|
|
|
for (int i = 3; i < codes.Count; i++)
|
|
{
|
|
//get current tags
|
|
if ((codes[i].opcode == OpCodes.Brtrue || codes[i].opcode == OpCodes.Brtrue_S) && codes[i - 1].opcode == OpCodes.Ldloc_0)
|
|
{
|
|
codes.InsertRange(i + 3, new[]
|
|
{
|
|
new CodeInstruction(OpCodes.Ldarg_0).MoveLabelsFrom(codes[i + 3]),
|
|
new CodeInstruction(OpCodes.Call, mtd_get_xui),
|
|
CodeInstruction.LoadField(typeof(XUi), nameof(XUi.AssembleItem)),
|
|
new CodeInstruction(OpCodes.Callvirt, mtd_get_cur_item),
|
|
CodeInstruction.LoadField(typeof(ItemStack), nameof(ItemStack.itemValue)),
|
|
new CodeInstruction(OpCodes.Stloc_S, lbd_item_being_assembled),
|
|
new CodeInstruction(OpCodes.Ldloc_S, lbd_item_being_assembled),
|
|
new CodeInstruction(OpCodes.Ldarg_0),
|
|
CodeInstruction.LoadField(typeof(XUiC_ItemCosmeticStack), "itemValue"),
|
|
CodeInstruction.Call(typeof(LocalItemTagsManager), nameof(LocalItemTagsManager.GetTagsAsIfNotInstalled)),
|
|
new CodeInstruction(OpCodes.Stloc_S, lbd_tags_if_remove_prev),
|
|
new CodeInstruction(OpCodes.Ldloc_S, lbd_item_being_assembled),
|
|
new CodeInstruction(OpCodes.Ldloc_0),
|
|
CodeInstruction.Call(typeof(LocalItemTagsManager), nameof(LocalItemTagsManager.GetTagsAsIfInstalled)),
|
|
new CodeInstruction(OpCodes.Stloc_S, lbd_tags_if_install_new)
|
|
});
|
|
i += 18;
|
|
Log.Out("cos 1!!!");
|
|
}
|
|
//replace checking tags
|
|
else if (codes[i].Calls(mtd_has_any_tags) && codes[i - 3].Calls(mtd_get_item_class))
|
|
{
|
|
if (codes[i - 1].LoadsField(fld_installable_tags) && (codes[i + 1].opcode == OpCodes.Brtrue || codes[i + 1].opcode == OpCodes.Brtrue_S))
|
|
{
|
|
var lbl_prev = codes[i + 4].ExtractLabels();
|
|
var lbl_jump = generator.DefineLabel();
|
|
codes[i + 4].WithLabels(lbl_jump);
|
|
codes.InsertRange(i + 4, new[]
|
|
{
|
|
new CodeInstruction(OpCodes.Ldloc_S, lbd_item_being_assembled).WithLabels(lbl_prev),
|
|
new CodeInstruction(OpCodes.Ldarg_0),
|
|
CodeInstruction.LoadField(typeof(XUiC_ItemPartStack), "itemValue"),
|
|
new CodeInstruction(OpCodes.Ldloc_0),
|
|
CodeInstruction.Call(typeof(LocalItemTagsManager), nameof(LocalItemTagsManager.CanSwapMod)),
|
|
new CodeInstruction(OpCodes.Brtrue, lbl_jump),
|
|
new CodeInstruction(OpCodes.Ldc_I4_0),
|
|
new CodeInstruction(OpCodes.Ret)
|
|
});
|
|
}
|
|
codes[i - 8].MoveLabelsTo(codes[i - 3]);
|
|
codes[i - 3].opcode = OpCodes.Ldloca_S;
|
|
codes[i - 3].operand = lbd_tags_if_remove_prev;
|
|
codes[i].opcode = OpCodes.Call;
|
|
codes[i].operand = mtd_test_any_set;
|
|
codes.RemoveRange(i - 8, 5);
|
|
i -= 5;
|
|
Log.Out("cos 2!!!");
|
|
}
|
|
}
|
|
|
|
return codes;
|
|
}
|
|
|
|
/// <summary>
|
|
/// check if other mods relies on this one
|
|
/// </summary>
|
|
/// <param name="__result"></param>
|
|
/// <param name="__instance"></param>
|
|
/// <param name="___itemValue"></param>
|
|
[HarmonyPatch(typeof(XUiC_ItemPartStack), "CanRemove")]
|
|
[HarmonyPostfix]
|
|
private static void Postfix_CanRemove_XUiC_ItemPartStack(ref bool __result, XUiC_ItemPartStack __instance)
|
|
{
|
|
if (__result && __instance.xui?.AssembleItem?.CurrentItem?.itemValue is ItemValue itemValue)
|
|
{
|
|
ItemClass itemClass = itemValue.ItemClass;
|
|
FastTags<TagGroup.Global> tagsAfterRemove = LocalItemTagsManager.GetTagsAsIfNotInstalled(itemValue, __instance.itemValue);
|
|
if (tagsAfterRemove.IsEmpty)
|
|
{
|
|
__result = false;
|
|
return;
|
|
}
|
|
|
|
foreach (var mod in itemValue.Modifications)
|
|
{
|
|
if (mod.IsEmpty())
|
|
continue;
|
|
ItemClassModifier modClass = mod.ItemClass as ItemClassModifier;
|
|
if (modClass == null || !tagsAfterRemove.Test_AnySet(modClass.InstallableTags) || tagsAfterRemove.Test_AnySet(modClass.DisallowedTags))
|
|
{
|
|
__result = false;
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
[HarmonyPatch(typeof(XUiC_ItemCosmeticStack), "CanRemove")]
|
|
[HarmonyPostfix]
|
|
private static void Postfix_CanRemove_XUiC_ItemCosmeticStack(ref bool __result, XUiC_ItemCosmeticStack __instance)
|
|
{
|
|
if (__result && __instance.xui?.AssembleItem?.CurrentItem?.itemValue is ItemValue itemValue)
|
|
{
|
|
ItemClass itemClass = itemValue.ItemClass;
|
|
FastTags<TagGroup.Global> tagsAfterRemove = LocalItemTagsManager.GetTagsAsIfNotInstalled(itemValue, __instance.itemValue);
|
|
if (tagsAfterRemove.IsEmpty)
|
|
{
|
|
__result = false;
|
|
return;
|
|
}
|
|
|
|
foreach (var mod in itemValue.CosmeticMods)
|
|
{
|
|
if (mod.IsEmpty())
|
|
continue;
|
|
ItemClassModifier modClass = mod.ItemClass as ItemClassModifier;
|
|
if (modClass == null || !tagsAfterRemove.Test_AnySet(modClass.InstallableTags) || tagsAfterRemove.Test_AnySet(modClass.DisallowedTags))
|
|
{
|
|
__result = false;
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// should update the gear icon?
|
|
/// </summary>
|
|
/// <param name="instructions"></param>
|
|
/// <param name="generator"></param>
|
|
/// <returns></returns>
|
|
[HarmonyPatch(typeof(XUiC_ItemStack), nameof(XUiC_ItemStack.updateLockTypeIcon))]
|
|
[HarmonyTranspiler]
|
|
private static IEnumerable<CodeInstruction> Transpiler_updateLockTypeIcon_XUiC_ItemStack(IEnumerable<CodeInstruction> instructions, ILGenerator generator)
|
|
{
|
|
var codes = instructions.ToList();
|
|
|
|
LocalBuilder lbd_tags = generator.DeclareLocal(typeof(FastTags<TagGroup.Global>));
|
|
MethodInfo mtd_has_any_tags = AccessTools.Method(typeof(ItemClass), nameof(ItemClass.HasAnyTags));
|
|
MethodInfo mtd_test_any_set = AccessTools.Method(typeof(FastTags<TagGroup.Global>), nameof(FastTags<TagGroup.Global>.Test_AnySet));
|
|
MethodInfo mtd_get_item_class = AccessTools.PropertyGetter(typeof(ItemValue), nameof(ItemValue.ItemClass));
|
|
MethodInfo mtd_get_cur_item = AccessTools.PropertyGetter(typeof(XUiM_AssembleItem), nameof(XUiM_AssembleItem.CurrentItem));
|
|
MethodInfo mtd_get_xui = AccessTools.PropertyGetter(typeof(XUiController), nameof(XUiController.xui));
|
|
|
|
for (int i = 3; i < codes.Count; i++)
|
|
{
|
|
//get current tags
|
|
if ((codes[i].opcode == OpCodes.Brfalse_S || codes[i].opcode == OpCodes.Brfalse) && codes[i - 1].Calls(mtd_get_cur_item))
|
|
{
|
|
codes.InsertRange(i + 1, new[]
|
|
{
|
|
new CodeInstruction(OpCodes.Ldarg_0),
|
|
new CodeInstruction(OpCodes.Call, mtd_get_xui),
|
|
CodeInstruction.LoadField(typeof(XUi), nameof(XUi.AssembleItem)),
|
|
new CodeInstruction(OpCodes.Callvirt, mtd_get_cur_item),
|
|
CodeInstruction.LoadField(typeof(ItemStack), nameof(ItemStack.itemValue)),
|
|
CodeInstruction.Call(typeof(LocalItemTagsManager), nameof(LocalItemTagsManager.GetTags)),
|
|
new CodeInstruction(OpCodes.Stloc_S, lbd_tags)
|
|
});
|
|
i += 7;
|
|
}
|
|
//do not touch check on the modification item
|
|
else if (codes[i].Calls(mtd_has_any_tags) && codes[i - 3].Calls(mtd_get_item_class))
|
|
{
|
|
codes[i].opcode = OpCodes.Call;
|
|
codes[i].operand = mtd_test_any_set;
|
|
var insert = new CodeInstruction(OpCodes.Ldloca_S, lbd_tags);
|
|
codes[i - 8].MoveLabelsTo(insert);
|
|
codes.RemoveRange(i - 8, 6);
|
|
codes.Insert(i - 8, insert);
|
|
i -= 5;
|
|
}
|
|
}
|
|
|
|
return codes;
|
|
}
|
|
|
|
/// <summary>
|
|
/// when installing new mod, use modified tags to check for compatibility
|
|
/// </summary>
|
|
/// <param name="instructions"></param>
|
|
/// <param name="generator"></param>
|
|
/// <returns></returns>
|
|
[HarmonyPatch(typeof(XUiM_AssembleItem), nameof(XUiM_AssembleItem.AddPartToItem))]
|
|
[HarmonyTranspiler]
|
|
private static IEnumerable<CodeInstruction> Transpiler_AddPartToItem_XUiM_AssembleItem(IEnumerable<CodeInstruction> instructions, ILGenerator generator)
|
|
{
|
|
var codes = instructions.ToList();
|
|
|
|
LocalBuilder lbd_tags_cur = generator.DeclareLocal(typeof(FastTags<TagGroup.Global>));
|
|
LocalBuilder lbd_tags_after_install = generator.DeclareLocal(typeof(FastTags<TagGroup.Global>));
|
|
MethodInfo mtd_has_any_tags = AccessTools.Method(typeof(ItemClass), nameof(ItemClass.HasAnyTags));
|
|
MethodInfo mtd_test_any_set = AccessTools.Method(typeof(FastTags<TagGroup.Global>), nameof(FastTags<TagGroup.Global>.Test_AnySet));
|
|
MethodInfo mtd_get_item_class = AccessTools.PropertyGetter(typeof(ItemValue), nameof(ItemValue.ItemClass));
|
|
MethodInfo mtd_get_cur_item = AccessTools.PropertyGetter(typeof(XUiM_AssembleItem), nameof(XUiM_AssembleItem.CurrentItem));
|
|
MethodInfo mtd_is_empty = AccessTools.Method(typeof(ItemValue), nameof(ItemValue.IsEmpty));
|
|
FieldInfo fld_cos = AccessTools.Field(typeof(ItemValue), nameof(ItemValue.CosmeticMods));
|
|
FieldInfo fld_mod = AccessTools.Field(typeof(ItemValue), nameof(ItemValue.Modifications));
|
|
FieldInfo fld_installable_tags = AccessTools.Field(typeof(ItemClassModifier), nameof(ItemClassModifier.InstallableTags));
|
|
|
|
for (int i = 3; i < codes.Count; i++)
|
|
{
|
|
//get current tags
|
|
if (codes[i].opcode == OpCodes.Stloc_0)
|
|
{
|
|
codes.InsertRange(i + 1, new[]
|
|
{
|
|
new CodeInstruction(OpCodes.Ldarg_0),
|
|
new CodeInstruction(OpCodes.Call, mtd_get_cur_item),
|
|
CodeInstruction.LoadField(typeof(ItemStack), nameof(ItemStack.itemValue)),
|
|
new CodeInstruction(OpCodes.Dup),
|
|
CodeInstruction.Call(typeof(LocalItemTagsManager), nameof(LocalItemTagsManager.GetTags)),
|
|
new CodeInstruction(OpCodes.Stloc_S, lbd_tags_cur),
|
|
new CodeInstruction(OpCodes.Ldloc_0),
|
|
CodeInstruction.Call(typeof(LocalItemTagsManager), nameof(LocalItemTagsManager.GetTagsAsIfInstalled)),
|
|
new CodeInstruction(OpCodes.Stloc_S, lbd_tags_after_install)
|
|
});
|
|
i += 9;
|
|
}
|
|
//do not touch check on the modification item, check if current mod can be installed
|
|
else if (codes[i].Calls(mtd_has_any_tags) && codes[i - 3].Calls(mtd_get_item_class))
|
|
{
|
|
if (codes[i - 1].LoadsField(fld_installable_tags))
|
|
{
|
|
codes.InsertRange(i + 2, new[]
|
|
{
|
|
new CodeInstruction(OpCodes.Ldarg_0),
|
|
new CodeInstruction(OpCodes.Call, mtd_get_cur_item),
|
|
CodeInstruction.LoadField(typeof(ItemStack), nameof(ItemStack.itemValue)),
|
|
new CodeInstruction(OpCodes.Ldloc_0),
|
|
CodeInstruction.Call(typeof(LocalItemTagsManager), nameof(LocalItemTagsManager.CanInstallMod)),
|
|
new CodeInstruction(OpCodes.Brfalse, codes[i + 1].operand)
|
|
});
|
|
}
|
|
codes[i].opcode = OpCodes.Call;
|
|
codes[i].operand = mtd_test_any_set;
|
|
var insert = new CodeInstruction(OpCodes.Ldloca_S, lbd_tags_cur);
|
|
codes[i - 6].MoveLabelsTo(insert);
|
|
codes.RemoveRange(i - 6, 4);
|
|
codes.Insert(i - 6, insert);
|
|
i -= 3;
|
|
}
|
|
}
|
|
|
|
return codes;
|
|
}
|
|
|
|
#endregion
|
|
|
|
//change when aiming events are fired
|
|
[HarmonyPatch(typeof(EntityPlayerLocal), nameof(EntityPlayerLocal.SetMoveState))]
|
|
[HarmonyTranspiler]
|
|
private static IEnumerable<CodeInstruction> Transpiler_SetMoveState_EntityPlayerLocal(IEnumerable<CodeInstruction> instructions)
|
|
{
|
|
var codes = instructions.ToList();
|
|
|
|
FieldInfo fld_msa = AccessTools.Field(typeof(EntityPlayerLocal), nameof(EntityPlayerLocal.moveStateAiming));
|
|
|
|
for (int i = 0; i < codes.Count - 2; i++)
|
|
{
|
|
if (codes[i].LoadsField(fld_msa) && codes[i + 2].opcode == OpCodes.Ldloc_1)
|
|
{
|
|
codes[i - 2].MoveLabelsTo(codes[i + 13]);
|
|
codes.RemoveRange(i - 2, 15);
|
|
break;
|
|
}
|
|
}
|
|
|
|
return codes;
|
|
}
|
|
|
|
[HarmonyPatch(typeof(EntityAlive), nameof(EntityAlive.AimingGun), MethodType.Setter)]
|
|
[HarmonyPrefix]
|
|
private static bool Prefix_AimingGun_EntityAlive(bool value, EntityAlive __instance)
|
|
{
|
|
if (__instance is EntityPlayerLocal && __instance.inventory != null)
|
|
{
|
|
bool isAimingGun = __instance.AimingGun;
|
|
if (value != isAimingGun)
|
|
{
|
|
__instance.FireEvent(value ? MinEventTypes.onSelfAimingGunStart : MinEventTypes.onSelfAimingGunStop, true);
|
|
#if DEBUG
|
|
Log.Out(value ? "START AIMING GUN FIRED" : "STOP AIMING GUN FIRED");
|
|
#endif
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
|
|
[HarmonyPatch(typeof(ItemActionAttack), nameof(ItemActionAttack.HasRadial))]
|
|
[HarmonyPostfix]
|
|
private static void Postfix_HasRadial_ItemActionAttack(ref bool __result)
|
|
{
|
|
EntityPlayerLocal player = GameManager.Instance.World?.GetPrimaryPlayer();
|
|
int index = MultiActionManager.GetActionIndexForEntity(player);
|
|
List<ItemActionData> actionDatas = player.inventory?.holdingItemData?.actionData;
|
|
if (actionDatas != null && actionDatas.Count > index && actionDatas[index] is ItemActionRanged.ItemActionDataRanged rangedData && (rangedData.isReloading || rangedData.isWeaponReloading))
|
|
{
|
|
__result = false;
|
|
}
|
|
}
|
|
|
|
[HarmonyPatch(typeof(ItemActionAttack), nameof(ItemActionAttack.SetupRadial))]
|
|
[HarmonyTranspiler]
|
|
private static IEnumerable<CodeInstruction> Transpiler_SetupRadial_ItemActionAttack(IEnumerable<CodeInstruction> instructions, ILGenerator generator)
|
|
{
|
|
var codes = instructions.ToList();
|
|
|
|
var fld_usable = AccessTools.Field(typeof(ItemClass), nameof(ItemClass.UsableUnderwater));
|
|
|
|
var lbd_states = generator.DeclareLocal(typeof(bool[]));
|
|
|
|
for (int i = 0; i < codes.Count; i++)
|
|
{
|
|
if (codes[i].opcode == OpCodes.Stloc_0)
|
|
{
|
|
codes.InsertRange(i + 1, new[]
|
|
{
|
|
new CodeInstruction(OpCodes.Ldloc_0),
|
|
new CodeInstruction(OpCodes.Ldarg_2),
|
|
new CodeInstruction(OpCodes.Ldc_I4_M1),
|
|
CodeInstruction.Call(typeof(CommonUtilityPatch), nameof(CommonUtilityPatch.GetUnusableItemEntries)),
|
|
new CodeInstruction(OpCodes.Stloc_S, lbd_states)
|
|
});
|
|
i += 4;
|
|
}
|
|
else if (codes[i].LoadsField(fld_usable))
|
|
{
|
|
codes.InsertRange(i + 2, new[]
|
|
{
|
|
new CodeInstruction(OpCodes.Ldloc_S, lbd_states).WithLabels(codes[i + 2].ExtractLabels()),
|
|
new CodeInstruction(OpCodes.Ldloc_2),
|
|
CodeInstruction.Call(typeof(CommonUtilityPatch), nameof(CommonUtilityPatch.IsAmmoDisabled)),
|
|
new CodeInstruction(OpCodes.Brtrue_S, codes[i + 1].operand)
|
|
});
|
|
break;
|
|
}
|
|
}
|
|
|
|
return codes;
|
|
}
|
|
|
|
[HarmonyPatch(typeof(ItemAction), nameof(ItemAction.StartHolding))]
|
|
[HarmonyPostfix]
|
|
private static void Postfix_StartHolding_ItemAction(ItemActionData _data, ItemAction __instance)
|
|
{
|
|
if (__instance is ItemActionAttack itemActionAttack && _data.invData.holdingEntity is EntityPlayerLocal player)
|
|
{
|
|
var arr_disabled_ammo = GetUnusableItemEntries(itemActionAttack.MagazineItemNames, player, _data.indexInEntityOfAction);
|
|
if (arr_disabled_ammo == null)
|
|
{
|
|
return;
|
|
}
|
|
var itemValue = _data.invData.itemValue;
|
|
int cur_index = itemValue.GetSelectedAmmoIndexByActionIndex(_data.indexInEntityOfAction);
|
|
if (arr_disabled_ammo[cur_index])
|
|
{
|
|
int first_enabled_index = Mathf.Max(Array.IndexOf(arr_disabled_ammo, false), 0);
|
|
|
|
var mapping = MultiActionManager.GetMappingForEntity(player.entityId);
|
|
if (mapping != null)
|
|
{
|
|
if (_data.indexInEntityOfAction == mapping.CurMetaIndex)
|
|
{
|
|
itemValue.SelectedAmmoTypeIndex = (byte)first_enabled_index;
|
|
}
|
|
else
|
|
{
|
|
itemValue.SetMetadata(MultiActionUtils.ActionSelectedAmmoNames[mapping.indices.GetMetaIndexForActionIndex(_data.indexInEntityOfAction)], first_enabled_index, TypedMetadataValue.TypeTag.Integer);
|
|
}
|
|
_data.invData.holdingEntity.inventory.CallOnToolbeltChangedInternal();
|
|
}
|
|
else
|
|
{
|
|
itemValue.SelectedAmmoTypeIndex = (byte)(first_enabled_index);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
public static bool[] GetUnusableItemEntries(string[] ammoNames, EntityPlayerLocal player, int actionIndex = -1)
|
|
{
|
|
if (ammoNames == null)
|
|
{
|
|
return null;
|
|
}
|
|
if (actionIndex < 0)
|
|
{
|
|
actionIndex = MultiActionManager.GetActionIndexForEntity(player);
|
|
}
|
|
string str_disabled_ammo_names = player.inventory.holdingItemItemValue.GetPropertyOverrideForAction("DisableAmmo", "", actionIndex);
|
|
//Log.Out($"checking disabled ammo: {str_disabled_ammo_names}\n{StackTraceUtility.ExtractStackTrace()}");
|
|
bool[] arr_disable_states = new bool[ammoNames.Length];
|
|
if(!string.IsNullOrEmpty(str_disabled_ammo_names))
|
|
{
|
|
string[] arr_disabled_ammo_names = str_disabled_ammo_names.Split(',', StringSplitOptions.RemoveEmptyEntries);
|
|
foreach (var name in arr_disabled_ammo_names)
|
|
{
|
|
int index = Array.IndexOf(ammoNames, name.Trim());
|
|
if (index >= 0)
|
|
{
|
|
arr_disable_states[index] = true;
|
|
if (ConsoleCmdReloadLog.LogInfo)
|
|
Log.Out($"ammo {ammoNames[index]} is disabled");
|
|
}
|
|
}
|
|
}
|
|
return arr_disable_states;
|
|
}
|
|
|
|
private static bool IsAmmoDisabled(bool[] ammoStates, int index)
|
|
{
|
|
if (ammoStates == null || ammoStates.Length <= index)
|
|
{
|
|
return false;
|
|
}
|
|
return ammoStates[index];
|
|
}
|
|
|
|
//dont spread onSelfItemActivate/onSelfItemDeactivate to attachments
|
|
//handle start holding
|
|
[HarmonyPatch(typeof(Inventory), nameof(Inventory.syncHeldItem))]
|
|
[HarmonyTranspiler]
|
|
private static IEnumerable<CodeInstruction> Transpiler_syncHeldItem_Inventory(IEnumerable<CodeInstruction> instructions)
|
|
{
|
|
var codes = instructions.ToList();
|
|
|
|
var prop_itemvalue = AccessTools.PropertyGetter(typeof(Inventory), nameof(Inventory.holdingItemItemValue));
|
|
var mtd_fireevent = AccessTools.Method(typeof(ItemValue), nameof(ItemValue.FireEvent));
|
|
|
|
for (int i = 0; i < codes.Count; i++)
|
|
{
|
|
if (codes[i].Calls(mtd_fireevent) && codes[i - 5].Calls(prop_itemvalue))
|
|
{
|
|
codes[i] = CodeInstruction.Call(typeof(MinEffectController), nameof(MinEffectController.FireEvent));
|
|
codes.InsertRange(i - 4, new[]
|
|
{
|
|
CodeInstruction.Call(typeof(ItemValue), "get_ItemClass"),
|
|
CodeInstruction.LoadField(typeof(ItemClass), nameof(ItemClass.Effects))
|
|
});
|
|
i += 2;
|
|
}
|
|
}
|
|
|
|
return codes;
|
|
}
|
|
|
|
//handle radial activation
|
|
[HarmonyPatch(typeof(XUiC_Radial), nameof(XUiC_Radial.handleActivatableItemCommand))]
|
|
[HarmonyTranspiler]
|
|
private static IEnumerable<CodeInstruction> Transpiler_handleActivatableItemCommand_XUiC_Radial(IEnumerable<CodeInstruction> instructions)
|
|
{
|
|
var codes = instructions.ToList();
|
|
|
|
var mtd_fireevent = AccessTools.Method(typeof(ItemValue), nameof(ItemValue.FireEvent));
|
|
for (int i = 0; i < codes.Count; i++)
|
|
{
|
|
if (codes[i].Calls(mtd_fireevent))
|
|
{
|
|
codes[i] = CodeInstruction.Call(typeof(MinEffectController), nameof(MinEffectController.FireEvent));
|
|
codes.InsertRange(i - 2, new[]
|
|
{
|
|
CodeInstruction.Call(typeof(ItemValue), "get_ItemClass"),
|
|
CodeInstruction.LoadField(typeof(ItemClass), nameof(ItemClass.Effects))
|
|
});
|
|
i += 2;
|
|
}
|
|
}
|
|
return codes;
|
|
}
|
|
|
|
//handle equipments
|
|
[HarmonyPatch(typeof(Equipment), nameof(Equipment.SetSlotItem))]
|
|
[HarmonyTranspiler]
|
|
private static IEnumerable<CodeInstruction> Transpiler_SetSlotItem_Equipment(IEnumerable<CodeInstruction> instructions)
|
|
{
|
|
var codes = instructions.ToList();
|
|
|
|
var mtd_fireevent = AccessTools.Method(typeof(ItemValue), nameof(ItemValue.FireEvent));
|
|
for (int i = 0; i < codes.Count; i++)
|
|
{
|
|
if (codes[i].Calls(mtd_fireevent) && codes[i - 5].opcode == OpCodes.Ldloc_0 && codes[i - 4].OperandIs((int)MinEventTypes.onSelfItemDeactivate))
|
|
{
|
|
codes[i] = CodeInstruction.Call(typeof(MinEffectController), nameof(MinEffectController.FireEvent));
|
|
codes.InsertRange(i - 4, new[]
|
|
{
|
|
CodeInstruction.Call(typeof(ItemValue), "get_ItemClass"),
|
|
CodeInstruction.LoadField(typeof(ItemClass), nameof(ItemClass.Effects))
|
|
});
|
|
i += 2;
|
|
}
|
|
}
|
|
return codes;
|
|
}
|
|
|
|
[HarmonyPatch(typeof(GameManager), nameof(GameManager.DropContentOfLootContainerServer))]
|
|
[HarmonyTranspiler]
|
|
private static IEnumerable<CodeInstruction> Transpiler_DropContentOfLootContainerServer_GameManager(IEnumerable<CodeInstruction> instructions)
|
|
{
|
|
var codes = instructions.ToList();
|
|
|
|
var setter_localscale = AccessTools.PropertySetter(typeof(Transform), nameof(Transform.localScale));
|
|
|
|
for (var i = 0; i < codes.Count; i++)
|
|
{
|
|
if (codes[i].Calls(setter_localscale))
|
|
{
|
|
codes.RemoveRange(i - 6, 7);
|
|
break;
|
|
}
|
|
}
|
|
|
|
return codes;
|
|
}
|
|
|
|
[HarmonyPatch(typeof(ItemActionRanged), nameof(ItemActionRanged.onHoldingEntityFired))]
|
|
[HarmonyTranspiler]
|
|
private static IEnumerable<CodeInstruction> Transpiler_onHoldingEntityFired_ItemActionRanged(IEnumerable<CodeInstruction> instructions)
|
|
{
|
|
var codes = instructions.ToList();
|
|
|
|
for (int i = 0; i < codes.Count; i++)
|
|
{
|
|
if (codes[i].opcode == OpCodes.Ldc_R4 && codes[i].operand is 5f)
|
|
{
|
|
codes.RemoveAt(i);
|
|
codes.InsertRange(i, new[]
|
|
{
|
|
new CodeInstruction(OpCodes.Ldarg_1),
|
|
CodeInstruction.Call(typeof(CommonUtilityPatch), nameof(CommonUtilityPatch.GetMaxSpread))
|
|
});
|
|
break;
|
|
}
|
|
}
|
|
return codes;
|
|
}
|
|
|
|
private static float GetMaxSpread(ItemActionData _data)
|
|
{
|
|
return EffectManager.GetValue(CustomEnums.MaxWeaponSpread, _data.invData.itemValue, 5f, _data.invData.holdingEntity);
|
|
}
|
|
|
|
[HarmonyPatch(typeof(NetEntityDistributionEntry), nameof(NetEntityDistributionEntry.getSpawnPacket))]
|
|
[HarmonyPrefix]
|
|
private static bool Prefix_getSpawnPacket_NetEntityDistributionEntry(NetEntityDistributionEntry __instance, ref NetPackage __result)
|
|
{
|
|
if (__instance.trackedEntity is EntityAlive ea)
|
|
{
|
|
__result = NetPackageManager.GetPackage<NetPackageEntitySpawnWithCVar>().Setup(new EntityCreationData(__instance.trackedEntity, true), (EntityAlive)__instance.trackedEntity);
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
[HarmonyPatch(typeof(World), nameof(World.SpawnEntityInWorld))]
|
|
[HarmonyPostfix]
|
|
private static void Postfix_SpawnEntityInWorld_World(Entity _entity)
|
|
{
|
|
if (_entity is EntityAlive ea && !ea.isEntityRemote)
|
|
{
|
|
ea.FireEvent(CustomEnums.onSelfFirstCVarSync);
|
|
}
|
|
}
|
|
|
|
[HarmonyPatch(typeof(ItemActionRanged), nameof(ItemActionRanged.ItemActionEffects))]
|
|
[HarmonyTranspiler]
|
|
private static IEnumerable<CodeInstruction> Transpiler_ItemActionEffects_ItemActionRanged(IEnumerable<CodeInstruction> instructions)
|
|
{
|
|
var codes = instructions.ToList();
|
|
|
|
for (int i = 0; i < codes.Count - 2; i++)
|
|
{
|
|
if (codes[i].opcode == OpCodes.Ldloc_S && ((LocalBuilder)codes[i].operand).LocalIndex == 9 && codes[i + 2].Branches(out _))
|
|
{
|
|
codes.InsertRange(i + 3, new[]
|
|
{
|
|
new CodeInstruction(OpCodes.Ldloc_S, codes[i].operand).WithLabels(codes[i + 3].ExtractLabels()),
|
|
CodeInstruction.Call(typeof(CommonUtilityPatch), nameof(AddTmpMuzzleFlash)),
|
|
});
|
|
i += 5;
|
|
}
|
|
else if (codes[i].opcode == OpCodes.Ldloc_S && ((LocalBuilder)codes[i].operand).LocalIndex == 12 && codes[i + 2].Branches(out _))
|
|
{
|
|
codes.InsertRange(i + 3, new[]
|
|
{
|
|
new CodeInstruction(OpCodes.Ldloc_S, codes[i].operand).WithLabels(codes[i + 3].ExtractLabels()),
|
|
CodeInstruction.Call(typeof(CommonUtilityPatch), nameof(AddTmpMuzzleFlash)),
|
|
});
|
|
i += 5;
|
|
}
|
|
}
|
|
return codes;
|
|
}
|
|
|
|
private static void AddTmpMuzzleFlash(Transform trans)
|
|
{
|
|
if (trans.TryGetComponent<TemporaryObject>(out var tmp))
|
|
{
|
|
tmp.StopAllCoroutines();
|
|
Component.Destroy(tmp);
|
|
}
|
|
tmp = trans.AddMissingComponent<TemporaryMuzzleFlash>();
|
|
tmp.life = 5f;
|
|
}
|
|
|
|
[HarmonyPatch(typeof(vp_FPCamera), nameof(vp_FPCamera.UpdateShakes))]
|
|
[HarmonyTranspiler]
|
|
private static IEnumerable<CodeInstruction> Transpiler_UpdateShakes_vp_FPCamera(IEnumerable<CodeInstruction> instructions)
|
|
{
|
|
var codes = instructions.ToList();
|
|
|
|
var fld_shake = AccessTools.Field(typeof(vp_FPCamera), nameof(vp_FPCamera.m_Shake));
|
|
|
|
for (int i = 0; i < codes.Count; i++)
|
|
{
|
|
if (codes[i].StoresField(fld_shake))
|
|
{
|
|
codes.InsertRange(i + 1, new[]
|
|
{
|
|
new CodeInstruction(OpCodes.Ldarg_0),
|
|
CodeInstruction.Call(typeof(CommonUtilityPatch), nameof(CheckShakeNaN))
|
|
});
|
|
break;
|
|
}
|
|
}
|
|
return codes;
|
|
}
|
|
|
|
private static void CheckShakeNaN(vp_FPCamera fpcamera)
|
|
{
|
|
if (float.IsNaN(fpcamera.m_Shake.x) || float.IsNaN(fpcamera.m_Shake.y) || float.IsNaN(fpcamera.m_Shake.z))
|
|
{
|
|
Log.Warning("Shake1 NaN {0}, time {1}, speed {2}, amp {3}", new object[]
|
|
{
|
|
fpcamera.m_Shake,
|
|
Time.time,
|
|
fpcamera.ShakeSpeed,
|
|
fpcamera.ShakeAmplitude
|
|
});
|
|
fpcamera.ShakeSpeed = 0f;
|
|
fpcamera.m_Shake = Vector3.zero;
|
|
fpcamera.m_Pitch += -1f;
|
|
}
|
|
}
|
|
|
|
//[HarmonyPatch(typeof(EntityBuffs), nameof(EntityBuffs.AddBuff), typeof(string), typeof(Vector3i), typeof(int), typeof(bool), typeof(bool), typeof(float))]
|
|
//[HarmonyPostfix]
|
|
//private static void Postfix_AddBuff_EntityBuffs(string _name, EntityBuffs __instance, EntityBuffs.BuffStatus __result, bool _netSync)
|
|
//{
|
|
// if (_name.StartsWith("eftZombieRandomArmor") || _name.StartsWith("eftZombieArmor"))
|
|
// Log.Out($"AddBuff [{_name}] on entity {__instance.parent.GetDebugName()} should sync {_netSync} result {__result.ToStringCached()}\n{StackTraceUtility.ExtractStackTrace()}");
|
|
//}
|
|
|
|
//[HarmonyPatch(typeof(EntityBuffs), nameof(EntityBuffs.RemoveBuff))]
|
|
//[HarmonyPostfix]
|
|
//private static void Postfix_RemoveBuff_EntityBuffs(string _name, EntityBuffs __instance, bool _netSync)
|
|
//{
|
|
// if (_name.StartsWith("eftZombieRandomArmor") || _name.StartsWith("eftZombieArmor"))
|
|
// Log.Out($"RemoveBuff [{_name}] on entity {__instance.parent.GetDebugName()} should sync {_netSync}\n{StackTraceUtility.ExtractStackTrace()}");
|
|
//}
|
|
|
|
//[HarmonyPatch(typeof(Inventory), nameof(Inventory.Execute))]
|
|
//[HarmonyPrefix]
|
|
//private static void Prefix_Execute_Inventory(Inventory __instance, int _actionIdx, bool _bReleased, PlayerActionsLocal _playerActions = null)
|
|
//{
|
|
// Log.Out($"Execute Inventory holding item {__instance.holdingItem.Name} slot {__instance.holdingItemIdx} action index {_actionIdx} released {_bReleased} is holster delay {__instance.IsHolsterDelayActive()} is unholster delay {__instance.IsUnholsterDelayActive()}");
|
|
//}
|
|
|
|
//[HarmonyPatch(typeof(Inventory), nameof(Inventory.updateHoldingItem))]
|
|
//[HarmonyTranspiler]
|
|
//private static IEnumerable<CodeInstruction> Transpiler_updateHoldingItem_Inventory(IEnumerable<CodeInstruction> instructions)
|
|
//{
|
|
// var codes = instructions.ToList();
|
|
|
|
// var mtd_setholdingtrans = AccessTools.Method(typeof(Inventory), nameof(Inventory.setHoldingItemTransfrom));
|
|
// var mtd_showrighthand = AccessTools.Method(typeof(Inventory), nameof(Inventory.ShowRightHand));
|
|
// int insert = -1, take = -1;
|
|
|
|
// for (int i = 0; i < codes.Count; i++)
|
|
// {
|
|
// if (codes[i].Calls(mtd_showrighthand))
|
|
// {
|
|
// insert = i + 1;
|
|
// }
|
|
// else if (codes[i].Calls(mtd_setholdingtrans))
|
|
// {
|
|
// take = i - 6;
|
|
// }
|
|
// }
|
|
|
|
// if (take > insert)
|
|
// {
|
|
// var list_take = codes.GetRange(take, 7);
|
|
// codes.RemoveRange(take, 7);
|
|
// codes.InsertRange(insert, list_take);
|
|
// }
|
|
|
|
// return codes;
|
|
//}
|
|
//private static bool exported = false;
|
|
//[HarmonyPatch(typeof(EModelSDCS), nameof(EModelSDCS.createModel))]
|
|
//[HarmonyPostfix]
|
|
//private static void Postfix_test(EModelSDCS __instance)
|
|
//{
|
|
// if (!exported)
|
|
// {
|
|
// exported = true;
|
|
// var objects = new[] { __instance.entity.RootTransform.gameObject.GetComponentsInChildren<Animator>()[1] };
|
|
// Log.Out($"exporting objs: {objects.Length} avatar {objects[0].avatar.name} is human {objects[0].avatar.isHuman}");
|
|
// FbxExporter07.OnExport(objects, @"E:\Unity Projects\AnimationPlayground\Assets\ExportedProject\example_skinned_mesh_with_bones.fbx");
|
|
// Application.Quit();
|
|
// }
|
|
//}
|
|
}
|
|
|