using System; using System.Collections; using System.Collections.Generic; using System.Diagnostics; using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.Versioning; using System.Security; using System.Security.Permissions; using BepInEx; using BepInEx.Configuration; using HarmonyLib; using Microsoft.CodeAnalysis; using Photon.Pun; using UnityEngine; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.Default | DebuggableAttribute.DebuggingModes.DisableOptimizations | DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints | DebuggableAttribute.DebuggingModes.EnableEditAndContinue)] [assembly: TargetFramework(".NETStandard,Version=v2.1", FrameworkDisplayName = ".NET Standard 2.1")] [assembly: IgnoresAccessChecksTo("")] [assembly: AssemblyCompany("REPOJP")] [assembly: AssemblyConfiguration("Debug")] [assembly: AssemblyFileVersion("1.0.0.0")] [assembly: AssemblyInformationalVersion("1.0.0")] [assembly: AssemblyProduct("zabuMod")] [assembly: AssemblyTitle("zabuMod")] [assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)] [assembly: AssemblyVersion("1.0.0.0")] [module: UnverifiableCode] [module: RefSafetyRules(11)] namespace Microsoft.CodeAnalysis { [CompilerGenerated] [Microsoft.CodeAnalysis.Embedded] internal sealed class EmbeddedAttribute : Attribute { } } namespace System.Runtime.CompilerServices { [CompilerGenerated] [Microsoft.CodeAnalysis.Embedded] [AttributeUsage(AttributeTargets.Module, AllowMultiple = false, Inherited = false)] internal sealed class RefSafetyRulesAttribute : Attribute { public readonly int Version; public RefSafetyRulesAttribute(int P_0) { Version = P_0; } } } namespace REPOJP.SharedLiabilityDamage { [BepInPlugin("REPOJP.SharedLiabilityDamage", "SharedLiabilityDamage", "4.0.0")] public sealed class SharedLiabilityDamagePlugin : BaseUnityPlugin { [CompilerGenerated] private sealed class d__23 : IEnumerator, IEnumerator, IDisposable { private int <>1__state; private object <>2__current; public SharedLiabilityDamagePlugin <>4__this; private int 5__1; private PlayerAvatar[] 5__2; private PlayerAvatar[] <>s__3; private int <>s__4; private PlayerAvatar 5__5; private PlayerHealth 5__6; private int 5__7; private int 5__8; private Exception 5__9; object IEnumerator.Current { [DebuggerHidden] get { return <>2__current; } } object IEnumerator.Current { [DebuggerHidden] get { return <>2__current; } } [DebuggerHidden] public d__23(int <>1__state) { this.<>1__state = <>1__state; } [DebuggerHidden] void IDisposable.Dispose() { 5__2 = null; <>s__3 = null; 5__5 = null; 5__6 = null; 5__9 = null; <>1__state = -2; } private bool MoveNext() { //IL_01dc: Unknown result type (might be due to invalid IL or missing references) switch (<>1__state) { default: return false; case 0: <>1__state = -1; <>2__current = null; <>1__state = 1; return true; case 1: <>1__state = -1; 5__1 = Mathf.Clamp(DamagePercent.Value, 0, 100); if (5__1 <= 0) { return false; } if (!GameManager.Multiplayer()) { return false; } if (!PhotonNetwork.IsMasterClient) { return false; } PurgeExpiredMarks(); 5__2 = Object.FindObjectsOfType(); if (5__2 == null || 5__2.Length == 0) { return false; } <>s__3 = 5__2; for (<>s__4 = 0; <>s__4 < <>s__3.Length; <>s__4++) { 5__5 = <>s__3[<>s__4]; if (Object.op_Implicit((Object)(object)5__5)) { 5__6 = 5__5.playerHealth; if (Object.op_Implicit((Object)(object)5__6) && IsAlive(5__5, 5__6) && !ShouldExcludeBySafeZone(5__5)) { 5__7 = GetIntField(Field_PlayerHealth_maxHealth, 5__6, 100); if (5__7 <= 0) { 5__7 = 1; } 5__8 = Mathf.RoundToInt((float)5__7 * ((float)5__1 / 100f)); 5__8 = Mathf.Clamp(5__8, 1, 5__7); MarkSolidarityTarget(5__5); try { InvokeHurtOther(5__6, 5__8, Vector3.zero, savingGrace: false, -1, hurtByHeal: false); } catch (Exception ex) { 5__9 = ex; ((BaseUnityPlugin)<>4__this).Logger.LogError((object)("Failure: HurtOther\n" + 5__9)); } 5__6 = null; 5__5 = null; } } } <>s__3 = null; return false; } } bool IEnumerator.MoveNext() { //ILSpy generated this explicit interface implementation from .override directive in MoveNext return this.MoveNext(); } [DebuggerHidden] void IEnumerator.Reset() { throw new NotSupportedException(); } } public const string PluginGuid = "REPOJP.SharedLiabilityDamage"; public const string PluginName = "SharedLiabilityDamage"; public const string PluginVersion = "4.0.0"; internal static SharedLiabilityDamagePlugin Instance; internal static ConfigEntry DamagePercent; internal static ConfigEntry ExcludeTruckAndExtractionPointPlayers; private static FieldInfo Field_PlayerAvatar_deadSet; private static FieldInfo Field_PlayerHealth_health; private static FieldInfo Field_PlayerHealth_maxHealth; private static FieldInfo Field_RoomVolumeCheck_inTruck; private static FieldInfo Field_RoomVolumeCheck_inExtractionPoint; private static MethodInfo Method_PlayerHealth_HurtOther; private static int Method_PlayerHealth_HurtOtherArgCount; private static MethodInfo Method_PlayerHealth_HurtOtherRpc; private static int Method_PlayerHealth_HurtOtherRpcArgCount; private static MethodInfo Method_PlayerHealth_Hurt; private static int Method_PlayerHealth_HurtArgCount; private const float MarkTtlSeconds = 3f; private static readonly Dictionary MarkedBySolidarityUntil = new Dictionary(16); private Harmony _harmony; private void Awake() { //IL_002b: Unknown result type (might be due to invalid IL or missing references) //IL_0035: Expected O, but got Unknown //IL_00e3: Unknown result type (might be due to invalid IL or missing references) //IL_00ed: Expected O, but got Unknown Instance = this; DamagePercent = ((BaseUnityPlugin)this).Config.Bind("General", "DamagePercent", 10, new ConfigDescription("Each death deals N% of each survivor's max health to that survivor (0-100), and 0 disables the mod.誰かが死ぬたびに生存者ごとの最大体力N%を生存者本人へ与える 0-100 0は無効化", (AcceptableValueBase)(object)new AcceptableValueRange(0, 100), Array.Empty())); ExcludeTruckAndExtractionPointPlayers = ((BaseUnityPlugin)this).Config.Bind("General", "ExcludeTruckAndExtractionPointPlayers", true, "Do not apply shared liability damage to players inside the truck or any extraction point room.トラック内または納品所内にいるプレイヤーへ連帯責任ダメージを与えない"); Field_PlayerAvatar_deadSet = AccessTools.Field(typeof(PlayerAvatar), "deadSet"); Field_PlayerHealth_health = AccessTools.Field(typeof(PlayerHealth), "health"); Field_PlayerHealth_maxHealth = AccessTools.Field(typeof(PlayerHealth), "maxHealth"); Field_RoomVolumeCheck_inTruck = AccessTools.Field(typeof(RoomVolumeCheck), "inTruck"); Field_RoomVolumeCheck_inExtractionPoint = AccessTools.Field(typeof(RoomVolumeCheck), "inExtractionPoint"); ResolveDamageMethods(); _harmony = new Harmony("REPOJP.SharedLiabilityDamage"); _harmony.PatchAll(); ((BaseUnityPlugin)this).Logger.LogInfo((object)"Loaded v4.0.0"); } private void OnDestroy() { try { if (_harmony != null) { _harmony.UnpatchSelf(); } } catch { } Instance = null; } internal void OnPlayerDeath(PlayerAvatar deadAvatar, int enemyIndex) { int num = Mathf.Clamp(DamagePercent.Value, 0, 100); if (num > 0 && GameManager.Multiplayer() && PhotonNetwork.IsMasterClient && (!Object.op_Implicit((Object)(object)deadAvatar) || enemyIndex != -1 || !ConsumeSolidarityMark(deadAvatar))) { ((MonoBehaviour)this).StartCoroutine(ApplyDamageToSurvivorsNextFrame()); } } [IteratorStateMachine(typeof(d__23))] private IEnumerator ApplyDamageToSurvivorsNextFrame() { //yield-return decompiler failed: Unexpected instruction in Iterator.Dispose() return new d__23(0) { <>4__this = this }; } private static bool ShouldExcludeBySafeZone(PlayerAvatar avatar) { if (ExcludeTruckAndExtractionPointPlayers == null || !ExcludeTruckAndExtractionPointPlayers.Value) { return false; } if (!Object.op_Implicit((Object)(object)avatar)) { return false; } RoomVolumeCheck val = null; try { val = avatar.RoomVolumeCheck; } catch { val = null; } if (!Object.op_Implicit((Object)(object)val)) { return false; } try { val.CheckSet(); } catch { } if (GetBoolField(Field_RoomVolumeCheck_inTruck, val, defaultValue: false)) { return true; } if (GetBoolField(Field_RoomVolumeCheck_inExtractionPoint, val, defaultValue: false)) { return true; } return false; } private static void ResolveDamageMethods() { Method_PlayerHealth_HurtOther = FindPlayerHealthMethod("HurtOther", 5, 4); Method_PlayerHealth_HurtOtherArgCount = GetCallableArgumentCount(Method_PlayerHealth_HurtOther); Method_PlayerHealth_HurtOtherRpc = FindPlayerHealthMethod("HurtOtherRPC", 5, 4); Method_PlayerHealth_HurtOtherRpcArgCount = GetCallableArgumentCount(Method_PlayerHealth_HurtOtherRpc); Method_PlayerHealth_Hurt = FindPlayerHealthMethod("Hurt", 4, 3, 2); Method_PlayerHealth_HurtArgCount = GetCallableArgumentCount(Method_PlayerHealth_Hurt); } private static MethodInfo FindPlayerHealthMethod(string methodName, params int[] preferredArgCounts) { MethodInfo[] methods = typeof(PlayerHealth).GetMethods(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); foreach (int num in preferredArgCounts) { foreach (MethodInfo methodInfo in methods) { if (!(methodInfo == null) && !(methodInfo.Name != methodName) && GetCallableArgumentCount(methodInfo) == num && IsCompatibleDamageMethod(methodName, methodInfo, num)) { return methodInfo; } } } return null; } private static bool IsCompatibleDamageMethod(string methodName, MethodInfo method, int callableArgumentCount) { ParameterInfo[] parameters = method.GetParameters(); if (parameters == null || parameters.Length == 0) { return false; } if (methodName == "HurtOther" || methodName == "HurtOtherRPC") { if (callableArgumentCount < 4) { return false; } return parameters[0].ParameterType == typeof(int) && parameters[1].ParameterType == typeof(Vector3) && parameters[2].ParameterType == typeof(bool) && parameters[3].ParameterType == typeof(int); } if (methodName == "Hurt") { if (callableArgumentCount < 2) { return false; } return parameters[0].ParameterType == typeof(int) && parameters[1].ParameterType == typeof(bool); } return false; } private static int GetCallableArgumentCount(MethodInfo method) { if (method == null) { return 0; } ParameterInfo[] parameters = method.GetParameters(); if (parameters == null) { return 0; } int num = 0; for (int i = 0; i < parameters.Length && !(parameters[i].ParameterType == typeof(PhotonMessageInfo)); i++) { num++; } return num; } private static void InvokeHurtOther(PlayerHealth ph, int damage, Vector3 hurtPosition, bool savingGrace, int enemyIndex, bool hurtByHeal) { //IL_0030: Unknown result type (might be due to invalid IL or missing references) //IL_0085: Unknown result type (might be due to invalid IL or missing references) if (Object.op_Implicit((Object)(object)ph)) { if (Method_PlayerHealth_HurtOther != null) { Method_PlayerHealth_HurtOther.Invoke(ph, BuildHurtOtherArguments(Method_PlayerHealth_HurtOtherArgCount, damage, hurtPosition, savingGrace, enemyIndex, hurtByHeal)); } else if (GameManager.Multiplayer() && (Object)(object)ph.photonView != (Object)null && Method_PlayerHealth_HurtOtherRpc != null) { ph.photonView.RPC(Method_PlayerHealth_HurtOtherRpc.Name, (RpcTarget)0, BuildHurtOtherArguments(Method_PlayerHealth_HurtOtherRpcArgCount, damage, hurtPosition, savingGrace, enemyIndex, hurtByHeal)); } else if (!GameManager.Multiplayer() && Method_PlayerHealth_Hurt != null) { Method_PlayerHealth_Hurt.Invoke(ph, BuildHurtArguments(Method_PlayerHealth_HurtArgCount, damage, savingGrace, enemyIndex, hurtByHeal)); } } } private static object[] BuildHurtOtherArguments(int argumentCount, int damage, Vector3 hurtPosition, bool savingGrace, int enemyIndex, bool hurtByHeal) { //IL_0056: Unknown result type (might be due to invalid IL or missing references) //IL_001e: Unknown result type (might be due to invalid IL or missing references) if (argumentCount >= 5) { return new object[5] { damage, hurtPosition, savingGrace, enemyIndex, hurtByHeal }; } return new object[4] { damage, hurtPosition, savingGrace, enemyIndex }; } private static object[] BuildHurtArguments(int argumentCount, int damage, bool savingGrace, int enemyIndex, bool hurtByHeal) { if (argumentCount >= 4) { return new object[4] { damage, savingGrace, enemyIndex, hurtByHeal }; } if (argumentCount == 3) { return new object[3] { damage, savingGrace, enemyIndex }; } return new object[2] { damage, savingGrace }; } private static bool IsAlive(PlayerAvatar avatar, PlayerHealth ph) { if (GetBoolField(Field_PlayerAvatar_deadSet, avatar, defaultValue: false)) { return false; } int intField = GetIntField(Field_PlayerHealth_health, ph, 1); if (intField <= 0) { return false; } return true; } private static void MarkSolidarityTarget(PlayerAvatar avatar) { if (Object.op_Implicit((Object)(object)avatar) && Object.op_Implicit((Object)(object)avatar.photonView)) { int viewID = avatar.photonView.ViewID; if (viewID > 0) { MarkedBySolidarityUntil[viewID] = Time.time + 3f; } } } private static bool ConsumeSolidarityMark(PlayerAvatar avatar) { if (!Object.op_Implicit((Object)(object)avatar) || !Object.op_Implicit((Object)(object)avatar.photonView)) { return false; } int viewID = avatar.photonView.ViewID; if (viewID <= 0) { return false; } float time = Time.time; if (!MarkedBySolidarityUntil.TryGetValue(viewID, out var value)) { return false; } if (value < time) { MarkedBySolidarityUntil.Remove(viewID); return false; } MarkedBySolidarityUntil.Remove(viewID); return true; } private static void PurgeExpiredMarks() { if (MarkedBySolidarityUntil.Count == 0) { return; } float time = Time.time; List list = null; foreach (KeyValuePair item in MarkedBySolidarityUntil) { if (item.Value < time) { if (list == null) { list = new List(8); } list.Add(item.Key); } } if (list == null) { return; } foreach (int item2 in list) { MarkedBySolidarityUntil.Remove(item2); } } private static int GetIntField(FieldInfo fi, object instance, int defaultValue) { if (fi == null || instance == null) { return defaultValue; } try { object value = fi.GetValue(instance); if (value is int) { return (int)value; } } catch { return defaultValue; } return defaultValue; } private static bool GetBoolField(FieldInfo fi, object instance, bool defaultValue) { if (fi == null || instance == null) { return defaultValue; } try { object value = fi.GetValue(instance); if (value is bool) { return (bool)value; } } catch { return defaultValue; } return defaultValue; } } [HarmonyPatch(typeof(PlayerAvatar), "PlayerDeathRPC")] internal static class Patch_PlayerAvatar_PlayerDeathRPC { private static void Postfix(PlayerAvatar __instance, int enemyIndex, PhotonMessageInfo _info) { if (GameManager.Multiplayer() && PhotonNetwork.IsMasterClient && !((Object)(object)SharedLiabilityDamagePlugin.Instance == (Object)null)) { SharedLiabilityDamagePlugin.Instance.OnPlayerDeath(__instance, enemyIndex); } } } }