Decompiled source of ExtractionGoalEscalation v1.0.1

ExtractionGoalEscalation.dll

Decompiled 3 days ago
using System;
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;
using UnityEngine.SceneManagement;

[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("zabu")]
[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.Class | AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Event | AttributeTargets.Parameter | AttributeTargets.ReturnValue | AttributeTargets.GenericParameter, AllowMultiple = false, Inherited = false)]
	internal sealed class NullableAttribute : Attribute
	{
		public readonly byte[] NullableFlags;

		public NullableAttribute(byte P_0)
		{
			NullableFlags = new byte[1] { P_0 };
		}

		public NullableAttribute(byte[] P_0)
		{
			NullableFlags = P_0;
		}
	}
	[CompilerGenerated]
	[Microsoft.CodeAnalysis.Embedded]
	[AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct | AttributeTargets.Method | AttributeTargets.Interface | AttributeTargets.Delegate, AllowMultiple = false, Inherited = false)]
	internal sealed class NullableContextAttribute : Attribute
	{
		public readonly byte Flag;

		public NullableContextAttribute(byte P_0)
		{
			Flag = P_0;
		}
	}
	[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.ExtractionGoalEscalation
{
	[BepInPlugin("REPOJP.ExtractionGoalEscalation", "ExtractionGoalEscalation", "1.0.0")]
	public sealed class ExtractionGoalEscalationPlugin : BaseUnityPlugin
	{
		public const string PluginGuid = "REPOJP.ExtractionGoalEscalation";

		public const string PluginName = "ExtractionGoalEscalation";

		public const string PluginVersion = "1.0.0";

		internal static ExtractionGoalEscalationPlugin Instance;

		private ConfigEntry<bool> enableMod;

		private ConfigEntry<int> startDelayMinutes;

		private ConfigEntry<int> increaseIntervalSeconds;

		private ConfigEntry<float> increaseAmountK;

		private ConfigEntry<int> maximumGoalK;

		private ConfigEntry<bool> stopWhenGoalMet;

		private ConfigEntry<bool> logGoalIncrease;

		private ConfigEntry<bool> enableDebugLog;

		private Harmony harmony;

		private object trackedExtractionPoint;

		private object suspendedExtractionPoint;

		private float trackingStartTime;

		private float nextIncreaseTime;

		private int baseGoal;

		private int increaseCount;

		private Type roundDirectorType;

		private Type semiFuncType;

		private Type runManagerType;

		private MethodInfo semiFuncIsMasterClientOrSingleplayerMethod;

		private MethodInfo semiFuncRunIsShopMethod;

		private MethodInfo semiFuncShopIsOpenMethod;

		private MethodInfo semiFuncLevelIsShopMethod;

		private MethodInfo semiFuncSceneIsShopMethod;

		private FieldInfo roundDirectorInstanceField;

		private FieldInfo roundDirectorExtractionPointCurrentField;

		private FieldInfo roundDirectorExtractionHaulGoalField;

		private FieldInfo roundDirectorCurrentHaulField;

		private FieldInfo runManagerInstanceField;

		private FieldInfo runManagerLevelCurrentField;

		private FieldInfo extractionPointHaulGoalField;

		private MethodInfo extractionPointHaulGoalSetMethod;

		private MethodInfo extractionPointHaulGoalSetRpcMethod;

		private void Awake()
		{
			//IL_0022: Unknown result type (might be due to invalid IL or missing references)
			//IL_002c: Expected O, but got Unknown
			Instance = this;
			InitializeConfig();
			CacheTypes();
			CacheReflectionMembers();
			harmony = new Harmony("REPOJP.ExtractionGoalEscalation");
			harmony.PatchAll();
			((BaseUnityPlugin)this).Logger.LogInfo((object)"ExtractionGoalEscalation 1.0.0 loaded");
		}

		private void OnDestroy()
		{
			if (harmony != null)
			{
				harmony.UnpatchSelf();
			}
		}

		private void Update()
		{
			if (!enableMod.Value)
			{
				ResetTracking(clearSuspended: false);
				return;
			}
			if (!IsHost())
			{
				ResetTracking(clearSuspended: false);
				return;
			}
			if (IsInShop())
			{
				ResetTracking(clearSuspended: false);
				return;
			}
			object currentExtractionPoint = GetCurrentExtractionPoint();
			if (!IsAliveUnityObject(currentExtractionPoint))
			{
				ResetTracking(clearSuspended: true);
				return;
			}
			int currentExtractionGoal = GetCurrentExtractionGoal(currentExtractionPoint);
			int currentHaul = GetCurrentHaul();
			if (stopWhenGoalMet.Value && currentExtractionGoal > 0 && currentHaul >= currentExtractionGoal)
			{
				suspendedExtractionPoint = currentExtractionPoint;
				if (trackedExtractionPoint == currentExtractionPoint)
				{
					DebugLog("Tracking suspended because current haul already met current goal");
				}
				ResetTracking(clearSuspended: false);
				return;
			}
			if (suspendedExtractionPoint != null && suspendedExtractionPoint != currentExtractionPoint)
			{
				suspendedExtractionPoint = null;
			}
			if (suspendedExtractionPoint == null || suspendedExtractionPoint != currentExtractionPoint)
			{
				if (trackedExtractionPoint != currentExtractionPoint)
				{
					BeginTracking(currentExtractionPoint);
				}
				else if (!(Time.time < nextIncreaseTime))
				{
					ApplyEscalation(currentExtractionPoint);
				}
			}
		}

		private void InitializeConfig()
		{
			//IL_0046: Unknown result type (might be due to invalid IL or missing references)
			//IL_0050: Expected O, but got Unknown
			//IL_007c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0086: Expected O, but got Unknown
			//IL_00ba: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c4: Expected O, but got Unknown
			//IL_00ee: Unknown result type (might be due to invalid IL or missing references)
			//IL_00f8: Expected O, but got Unknown
			enableMod = ((BaseUnityPlugin)this).Config.Bind<bool>("A General", "EnableMod", true, "Enable or disable this mod.このMODの有効無効");
			startDelayMinutes = ((BaseUnityPlugin)this).Config.Bind<int>("B Escalation", "StartDelayMinutes", 5, new ConfigDescription("Minutes to wait after extraction point appears before escalation starts.納品所出現後に増額開始まで待機する分数", (AcceptableValueBase)(object)new AcceptableValueRange<int>(0, 30), Array.Empty<object>()));
			increaseIntervalSeconds = ((BaseUnityPlugin)this).Config.Bind<int>("B Escalation", "IncreaseIntervalSeconds", 1, new ConfigDescription("Seconds between each goal increase after the start delay.増額開始後に何秒ごとに必要金額を増やすか", (AcceptableValueBase)(object)new AcceptableValueRange<int>(1, 300), Array.Empty<object>()));
			increaseAmountK = ((BaseUnityPlugin)this).Config.Bind<float>("B Escalation", "IncreaseAmountK", 0.01f, new ConfigDescription("Increase amount in k$. 0.01 means +10 each interval.増額量のk$指定 0.01なら毎回+10", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0.01f, 10f), Array.Empty<object>()));
			maximumGoalK = ((BaseUnityPlugin)this).Config.Bind<int>("C Safety", "MaximumGoalK", 50, new ConfigDescription("Maximum extraction goal in k$. 50 means 50000.必要金額の上限k$指定 50なら50000", (AcceptableValueBase)(object)new AcceptableValueRange<int>(1, 100), Array.Empty<object>()));
			stopWhenGoalMet = ((BaseUnityPlugin)this).Config.Bind<bool>("C Safety", "StopWhenGoalMet", true, "Stop escalating when current haul already meets the current goal.現在納品額が現在目標額を満たしたら増額停止");
			logGoalIncrease = ((BaseUnityPlugin)this).Config.Bind<bool>("C Safety", "LogGoalIncrease", false, "Log each goal increase.金額が増えるたびにログ出力");
			enableDebugLog = ((BaseUnityPlugin)this).Config.Bind<bool>("Z Debug", "EnableDebugLog", false, "Enable debug logs.デバッグログ出力");
		}

		private void CacheTypes()
		{
			roundDirectorType = AccessTools.TypeByName("RoundDirector");
			semiFuncType = AccessTools.TypeByName("SemiFunc");
			runManagerType = AccessTools.TypeByName("RunManager");
		}

		private void CacheReflectionMembers()
		{
			semiFuncIsMasterClientOrSingleplayerMethod = FindStaticBoolMethodSilently(semiFuncType, "IsMasterClientOrSingleplayer");
			semiFuncRunIsShopMethod = FindStaticBoolMethodSilently(semiFuncType, "RunIsShop");
			semiFuncShopIsOpenMethod = FindStaticBoolMethodSilently(semiFuncType, "ShopIsOpen");
			semiFuncLevelIsShopMethod = FindStaticBoolMethodSilently(semiFuncType, "LevelIsShop");
			semiFuncSceneIsShopMethod = FindStaticBoolMethodSilently(semiFuncType, "SceneIsShop");
			roundDirectorInstanceField = FindFieldSilently(roundDirectorType, "instance");
			roundDirectorExtractionPointCurrentField = FindFieldSilently(roundDirectorType, "extractionPointCurrent");
			roundDirectorExtractionHaulGoalField = FindFieldSilently(roundDirectorType, "extractionHaulGoal");
			roundDirectorCurrentHaulField = FindFieldSilently(roundDirectorType, "currentHaul");
			runManagerInstanceField = FindFieldSilently(runManagerType, "instance");
			runManagerLevelCurrentField = FindFieldSilently(runManagerType, "levelCurrent");
		}

		private void BeginTracking(object extractionPoint)
		{
			int currentExtractionGoal = GetCurrentExtractionGoal(extractionPoint);
			if (currentExtractionGoal <= 0)
			{
				DebugLog("BeginTracking skipped because current goal <= 0");
				ResetTracking(clearSuspended: false);
				return;
			}
			CacheExtractionPointMembers(extractionPoint.GetType());
			trackedExtractionPoint = extractionPoint;
			trackingStartTime = Time.time;
			baseGoal = currentExtractionGoal;
			increaseCount = 0;
			nextIncreaseTime = trackingStartTime + GetStartDelaySeconds();
			DebugLog($"Start tracking extraction goal escalation | baseGoal={baseGoal} startDelayMinutes={startDelayMinutes.Value} intervalSeconds={increaseIntervalSeconds.Value} increaseK={increaseAmountK.Value:0.00}");
		}

		private void CacheExtractionPointMembers(Type extractionPointRuntimeType)
		{
			if (!(extractionPointRuntimeType == null) && (!(extractionPointHaulGoalField != null) || !(extractionPointHaulGoalField.DeclaringType == extractionPointRuntimeType) || !(extractionPointHaulGoalSetMethod != null) || !(extractionPointHaulGoalSetMethod.DeclaringType == extractionPointRuntimeType)))
			{
				extractionPointHaulGoalField = FindFieldSilently(extractionPointRuntimeType, "haulGoal");
				extractionPointHaulGoalSetMethod = FindMethodSilently(extractionPointRuntimeType, "HaulGoalSet", new Type[1] { typeof(int) });
				extractionPointHaulGoalSetRpcMethod = FindMethodSilently(extractionPointRuntimeType, "HaulGoalSetRPC", new Type[1] { typeof(int) });
			}
		}

		private void ApplyEscalation(object extractionPoint)
		{
			if (IsInShop())
			{
				ResetTracking(clearSuspended: false);
				return;
			}
			int currentExtractionGoal = GetCurrentExtractionGoal(extractionPoint);
			if (currentExtractionGoal <= 0)
			{
				DebugLog("ApplyEscalation skipped because current goal <= 0");
				ResetTracking(clearSuspended: false);
				return;
			}
			increaseCount++;
			int increaseAmountValue = GetIncreaseAmountValue();
			int num;
			try
			{
				num = checked(baseGoal + increaseCount * increaseAmountValue);
			}
			catch (OverflowException)
			{
				num = int.MaxValue;
			}
			int maximumGoalValue = GetMaximumGoalValue();
			if (num > maximumGoalValue)
			{
				num = maximumGoalValue;
			}
			if (num <= currentExtractionGoal)
			{
				nextIncreaseTime = Time.time + GetIncreaseIntervalSeconds();
				DebugLog($"ApplyEscalation skipped because newGoal <= currentGoal | current={currentExtractionGoal} new={num}");
				return;
			}
			if (!SetExtractionGoal(extractionPoint, num))
			{
				((BaseUnityPlugin)this).Logger.LogWarning((object)$"Failed to set extraction goal to {num}");
				nextIncreaseTime = Time.time + GetIncreaseIntervalSeconds();
				return;
			}
			nextIncreaseTime = Time.time + GetIncreaseIntervalSeconds();
			if (logGoalIncrease.Value)
			{
				((BaseUnityPlugin)this).Logger.LogInfo((object)$"Extraction goal escalated | count={increaseCount} baseGoal={baseGoal} currentGoal={currentExtractionGoal} newGoal={num} elapsed={Time.time - trackingStartTime:0.00}s");
			}
			if (stopWhenGoalMet.Value)
			{
				int currentHaul = GetCurrentHaul();
				if (currentHaul >= num)
				{
					suspendedExtractionPoint = extractionPoint;
					ResetTracking(clearSuspended: false);
					DebugLog("Tracking suspended immediately after escalation because current haul already met new goal");
				}
			}
		}

		private void ResetTracking(bool clearSuspended)
		{
			trackedExtractionPoint = null;
			trackingStartTime = 0f;
			nextIncreaseTime = 0f;
			baseGoal = 0;
			increaseCount = 0;
			if (clearSuspended)
			{
				suspendedExtractionPoint = null;
			}
		}

		private bool IsHost()
		{
			try
			{
				if (semiFuncIsMasterClientOrSingleplayerMethod != null)
				{
					object obj = semiFuncIsMasterClientOrSingleplayerMethod.Invoke(null, null);
					if (obj is bool)
					{
						bool result = (bool)obj;
						if (true)
						{
							return result;
						}
					}
				}
			}
			catch (Exception arg)
			{
				DebugLog($"IsHost SemiFunc fallback | {arg}");
			}
			try
			{
				return PhotonNetwork.IsMasterClient || PhotonNetwork.OfflineMode;
			}
			catch
			{
				return false;
			}
		}

		private bool IsInShop()
		{
			//IL_007e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0083: Unknown result type (might be due to invalid IL or missing references)
			try
			{
				if (InvokeStaticBoolIfExists(semiFuncRunIsShopMethod))
				{
					return true;
				}
				if (InvokeStaticBoolIfExists(semiFuncShopIsOpenMethod))
				{
					return true;
				}
				if (InvokeStaticBoolIfExists(semiFuncLevelIsShopMethod))
				{
					return true;
				}
				if (InvokeStaticBoolIfExists(semiFuncSceneIsShopMethod))
				{
					return true;
				}
			}
			catch (Exception arg)
			{
				DebugLog($"IsInShop SemiFunc fallback | {arg}");
			}
			try
			{
				Scene activeScene = SceneManager.GetActiveScene();
				string name = ((Scene)(ref activeScene)).name;
				if (!string.IsNullOrWhiteSpace(name) && name.IndexOf("shop", StringComparison.OrdinalIgnoreCase) >= 0)
				{
					return true;
				}
			}
			catch (Exception arg2)
			{
				DebugLog($"IsInShop SceneManager fallback | {arg2}");
			}
			try
			{
				object runManagerInstance = GetRunManagerInstance();
				if (runManagerInstance != null && runManagerLevelCurrentField != null)
				{
					object value = runManagerLevelCurrentField.GetValue(runManagerInstance);
					Object val = (Object)((value is Object) ? value : null);
					if (val != null && val != (Object)null)
					{
						string name2 = val.name;
						if (!string.IsNullOrWhiteSpace(name2) && name2.IndexOf("shop", StringComparison.OrdinalIgnoreCase) >= 0)
						{
							return true;
						}
					}
				}
			}
			catch (Exception arg3)
			{
				DebugLog($"IsInShop RunManager fallback | {arg3}");
			}
			return false;
		}

		private object GetCurrentExtractionPoint()
		{
			object roundDirectorInstance = GetRoundDirectorInstance();
			if (roundDirectorInstance == null || roundDirectorExtractionPointCurrentField == null)
			{
				return null;
			}
			return roundDirectorExtractionPointCurrentField.GetValue(roundDirectorInstance);
		}

		private int GetCurrentExtractionGoal(object extractionPoint)
		{
			if (extractionPoint != null)
			{
				CacheExtractionPointMembers(extractionPoint.GetType());
			}
			if (extractionPoint != null && extractionPointHaulGoalField != null)
			{
				object value = extractionPointHaulGoalField.GetValue(extractionPoint);
				return ConvertToInt(value);
			}
			object roundDirectorInstance = GetRoundDirectorInstance();
			if (roundDirectorInstance != null && roundDirectorExtractionHaulGoalField != null)
			{
				object value2 = roundDirectorExtractionHaulGoalField.GetValue(roundDirectorInstance);
				return ConvertToInt(value2);
			}
			return 0;
		}

		private int GetCurrentHaul()
		{
			object roundDirectorInstance = GetRoundDirectorInstance();
			if (roundDirectorInstance == null || roundDirectorCurrentHaulField == null)
			{
				return 0;
			}
			object value = roundDirectorCurrentHaulField.GetValue(roundDirectorInstance);
			return ConvertToInt(value);
		}

		private bool SetExtractionGoal(object extractionPoint, int newGoal)
		{
			try
			{
				CacheExtractionPointMembers(extractionPoint.GetType());
				if (extractionPointHaulGoalSetMethod != null)
				{
					extractionPointHaulGoalSetMethod.Invoke(extractionPoint, new object[1] { newGoal });
					return true;
				}
				if (extractionPointHaulGoalSetRpcMethod != null)
				{
					extractionPointHaulGoalSetRpcMethod.Invoke(extractionPoint, new object[1] { newGoal });
					return true;
				}
				bool flag = false;
				if (extractionPointHaulGoalField != null)
				{
					extractionPointHaulGoalField.SetValue(extractionPoint, newGoal);
					flag = true;
				}
				object roundDirectorInstance = GetRoundDirectorInstance();
				bool flag2 = false;
				if (roundDirectorInstance != null && roundDirectorExtractionHaulGoalField != null)
				{
					roundDirectorExtractionHaulGoalField.SetValue(roundDirectorInstance, newGoal);
					flag2 = true;
				}
				return flag || flag2;
			}
			catch (Exception arg)
			{
				((BaseUnityPlugin)this).Logger.LogError((object)$"SetExtractionGoal failed | newGoal={newGoal} | {arg}");
				return false;
			}
		}

		private object GetRoundDirectorInstance()
		{
			if (roundDirectorInstanceField == null)
			{
				return null;
			}
			return roundDirectorInstanceField.GetValue(null);
		}

		private object GetRunManagerInstance()
		{
			if (runManagerInstanceField == null)
			{
				return null;
			}
			return runManagerInstanceField.GetValue(null);
		}

		private float GetStartDelaySeconds()
		{
			return (float)startDelayMinutes.Value * 60f;
		}

		private float GetIncreaseIntervalSeconds()
		{
			return Mathf.Max(1f, (float)increaseIntervalSeconds.Value);
		}

		private int GetIncreaseAmountValue()
		{
			return Mathf.Max(1, Mathf.RoundToInt(increaseAmountK.Value * 1000f));
		}

		private int GetMaximumGoalValue()
		{
			return Mathf.Max(1, maximumGoalK.Value) * 1000;
		}

		private static MethodInfo FindStaticBoolMethodSilently(Type type, string methodName)
		{
			if (type == null || string.IsNullOrWhiteSpace(methodName))
			{
				return null;
			}
			MethodInfo method = type.GetMethod(methodName, BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic, null, Type.EmptyTypes, null);
			if (method != null && method.ReturnType == typeof(bool))
			{
				return method;
			}
			return null;
		}

		private static MethodInfo FindMethodSilently(Type type, string methodName, Type[] parameterTypes)
		{
			if (type == null || string.IsNullOrWhiteSpace(methodName))
			{
				return null;
			}
			return type.GetMethod(methodName, BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic, null, parameterTypes ?? Type.EmptyTypes, null);
		}

		private static FieldInfo FindFieldSilently(Type type, string fieldName)
		{
			if (type == null || string.IsNullOrWhiteSpace(fieldName))
			{
				return null;
			}
			return type.GetField(fieldName, BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic);
		}

		private static bool InvokeStaticBoolIfExists(MethodInfo method)
		{
			if (method == null)
			{
				return false;
			}
			object obj = method.Invoke(null, null);
			bool flag = default(bool);
			int num;
			if (obj is bool)
			{
				flag = (bool)obj;
				num = 1;
			}
			else
			{
				num = 0;
			}
			return (byte)((uint)num & (flag ? 1u : 0u)) != 0;
		}

		private static int ConvertToInt(object value)
		{
			if (value == null)
			{
				return 0;
			}
			try
			{
				return Convert.ToInt32(value);
			}
			catch
			{
				return 0;
			}
		}

		private static bool IsAliveUnityObject(object target)
		{
			if (target == null)
			{
				return false;
			}
			Object val = (Object)((target is Object) ? target : null);
			if (val != null)
			{
				return val != (Object)null;
			}
			return true;
		}

		private void DebugLog(string message)
		{
			if (enableDebugLog.Value)
			{
				((BaseUnityPlugin)this).Logger.LogInfo((object)("[Debug] " + message));
			}
		}
	}
}