using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using System.Globalization;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
using System.Security;
using System.Security.Permissions;
using System.Text;
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.IgnoreSymbolStoreSequencePoints)]
[assembly: TargetFramework(".NETStandard,Version=v2.1", FrameworkDisplayName = ".NET Standard 2.1")]
[assembly: IgnoresAccessChecksTo("")]
[assembly: AssemblyCompany("REPOJP")]
[assembly: AssemblyConfiguration("Release")]
[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.PerfectExtractionBonus
{
[BepInPlugin("REPOJP.PerfectExtractionBonus", "PerfectExtractionBonus", "4.0.0")]
public sealed class PerfectExtractionBonusPlugin : BaseUnityPlugin
{
[HarmonyPatch(typeof(LevelGenerator), "GenerateDone")]
private static class LevelGenerator_GenerateDone_Patch
{
private static void Postfix()
{
try
{
StartInitialSnapshotTrackingFromLevelGenerated();
}
catch (Exception ex)
{
if ((Object)(object)Instance != (Object)null)
{
((BaseUnityPlugin)Instance).Logger.LogError((object)"Failure: LevelGenerator.GenerateDone Postfix");
((BaseUnityPlugin)Instance).Logger.LogError((object)ex);
}
}
}
}
[HarmonyPatch(typeof(ExtractionPoint), "DestroyTheFirstPhysObjectsInHaulList")]
private static class ExtractionPoint_DestroyTheFirstPhysObjectsInHaulList_Patch
{
private static void Prefix()
{
try
{
SnapshotDeliveredValuablesFromHaulList(firstOnly: true);
}
catch (Exception ex)
{
if ((Object)(object)Instance != (Object)null)
{
((BaseUnityPlugin)Instance).Logger.LogError((object)"Failure: DestroyTheFirstPhysObjectsInHaulList Prefix");
((BaseUnityPlugin)Instance).Logger.LogError((object)ex);
}
}
}
private static void Postfix()
{
try
{
CommitPendingDeliveredValuables();
}
catch (Exception ex)
{
if ((Object)(object)Instance != (Object)null)
{
((BaseUnityPlugin)Instance).Logger.LogError((object)"Failure: DestroyTheFirstPhysObjectsInHaulList Postfix");
((BaseUnityPlugin)Instance).Logger.LogError((object)ex);
}
}
}
}
[HarmonyPatch(typeof(ExtractionPoint), "DestroyAllPhysObjectsInHaulList")]
private static class ExtractionPoint_DestroyAllPhysObjectsInHaulList_Patch
{
private static void Prefix()
{
try
{
SnapshotDeliveredValuablesFromHaulList(firstOnly: false);
}
catch (Exception ex)
{
if ((Object)(object)Instance != (Object)null)
{
((BaseUnityPlugin)Instance).Logger.LogError((object)"Failure: DestroyAllPhysObjectsInHaulList Prefix");
((BaseUnityPlugin)Instance).Logger.LogError((object)ex);
}
}
}
private static void Postfix()
{
try
{
CommitPendingDeliveredValuables();
}
catch (Exception ex)
{
if ((Object)(object)Instance != (Object)null)
{
((BaseUnityPlugin)Instance).Logger.LogError((object)"Failure: DestroyAllPhysObjectsInHaulList Postfix");
((BaseUnityPlugin)Instance).Logger.LogError((object)ex);
}
}
}
}
[HarmonyPatch(typeof(RunManager), "ChangeLevel")]
private static class RunManager_ChangeLevel_Patch
{
private static void Prefix(bool _completedLevel, bool _levelFailed, ChangeLevelType _changeLevelType)
{
//IL_00a2: Unknown result type (might be due to invalid IL or missing references)
//IL_00a4: Invalid comparison between Unknown and I4
try
{
if (!ModEnabled.Value || !SemiFunc.IsMasterClientOrSingleplayer() || !IsTrackedGameplayLevel() || LevelResultProcessed)
{
return;
}
string currentLevelKey = GetCurrentLevelKey();
if (!string.Equals(CurrentTrackedLevelKey, currentLevelKey, StringComparison.Ordinal))
{
CurrentTrackedLevelKey = currentLevelKey;
}
if (_levelFailed)
{
LevelResultProcessed = true;
int perfectClearStreak = PerfectClearStreak;
if (ResetStreakOnLevelFail.Value)
{
PerfectClearStreak = 0;
WriteLog($"ResetStreak Reason=LevelFailed Previous={perfectClearStreak} Current={PerfectClearStreak}");
}
WriteLog("LevelResultProcessed Result=Failed LevelKey=" + CurrentTrackedLevelKey + " BonusResult=NotGranted");
}
else if ((int)_changeLevelType == 5 || (_completedLevel && !SemiFunc.RunIsLobby() && !SemiFunc.RunIsShop() && !SemiFunc.RunIsArena()))
{
LevelResultProcessed = true;
WriteLog("LevelResultProcessed Result=Completed LevelKey=" + CurrentTrackedLevelKey);
TryApplyPerfectExtractionBonus();
}
}
catch (Exception ex)
{
if ((Object)(object)Instance != (Object)null)
{
((BaseUnityPlugin)Instance).Logger.LogError((object)"Failure: RunManager.ChangeLevel Prefix");
((BaseUnityPlugin)Instance).Logger.LogError((object)ex);
}
}
finally
{
StopInitialSnapshotCoroutine();
StopMoneyBagEffect("ChangeLevel", destroySpawnedObjects: true);
ClearLevelRuntimeTrackingAfterChangeLevel("ChangeLevel");
}
}
}
[CompilerGenerated]
private sealed class <InitialSnapshotRoutine>d__67 : IEnumerator<object>, IEnumerator, IDisposable
{
private int <>1__state;
private object <>2__current;
public string levelKey;
object IEnumerator<object>.Current
{
[DebuggerHidden]
get
{
return <>2__current;
}
}
object IEnumerator.Current
{
[DebuggerHidden]
get
{
return <>2__current;
}
}
[DebuggerHidden]
public <InitialSnapshotRoutine>d__67(int <>1__state)
{
this.<>1__state = <>1__state;
}
[DebuggerHidden]
void IDisposable.Dispose()
{
<>1__state = -2;
}
private bool MoveNext()
{
//IL_0041: Unknown result type (might be due to invalid IL or missing references)
//IL_004b: Expected O, but got Unknown
switch (<>1__state)
{
default:
return false;
case 0:
{
<>1__state = -1;
float num = Mathf.Max(0f, InitialSnapshotDelaySeconds.Value);
if (num > 0f)
{
<>2__current = (object)new WaitForSeconds(num);
<>1__state = 1;
return true;
}
break;
}
case 1:
<>1__state = -1;
break;
case 2:
<>1__state = -1;
break;
}
if (!InitialSnapshotTaken)
{
if (TryCaptureInitialValuablesStep(levelKey))
{
InitialSnapshotCoroutine = null;
return false;
}
<>2__current = null;
<>1__state = 2;
return true;
}
InitialSnapshotCoroutine = 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.PerfectExtractionBonus";
public const string PluginName = "PerfectExtractionBonus";
public const string PluginVersion = "4.0.0";
internal static PerfectExtractionBonusPlugin Instance;
private Harmony harmony;
internal static ConfigEntry<bool> ModEnabled;
internal static ConfigEntry<int> BaseBonusPercent;
internal static ConfigEntry<bool> EnableStreakBonus;
internal static ConfigEntry<int> StreakAddPercent;
internal static ConfigEntry<int> MaxBonusPercent;
internal static ConfigEntry<int> NoLossExtraBonusPercent;
internal static ConfigEntry<bool> ResetStreakOnNonPerfectClear;
internal static ConfigEntry<bool> ResetStreakOnLevelFail;
internal static ConfigEntry<bool> ShowBonusUI;
internal static ConfigEntry<float> InitialSnapshotDelaySeconds;
internal static ConfigEntry<int> SnapshotStableFrames;
internal static ConfigEntry<bool> EnableMoneyBagEffect;
internal static ConfigEntry<int> MoneyBagEffectValue;
internal static ConfigEntry<int> MaxAliveMoneyBagEffects;
internal static ConfigEntry<bool> LogEnabled;
internal static ConfigEntry<string> PerfectBonusPublicChatMessage;
internal static ConfigEntry<string> NoLossPerfectBonusPublicChatMessage;
internal static ConfigEntry<bool> AnnounceRemainingCountInPublicChat;
internal static ConfigEntry<string> RemainingCountPublicChatMessage;
private static FieldInfo valuableDollarValueOriginalField;
private static FieldInfo valuableDollarValueCurrentField;
private static FieldInfo valuableDollarValueOverrideField;
private static FieldInfo valuableDollarValueSetField;
private static FieldInfo valuableDirectorSetupCompleteField;
private static FieldInfo physGrabObjectSpawnTorqueField;
private static FieldInfo roundDirectorHaulGoalMaxField;
internal static string CurrentTrackedLevelKey = string.Empty;
internal static Coroutine InitialSnapshotCoroutine;
internal static bool WaitingInitialSnapshot;
internal static bool InitialSnapshotTaken;
internal static string LastObservedFingerprint = string.Empty;
internal static int StableObservedFrames;
internal static bool LevelResultProcessed;
internal static bool BonusGrantedThisLevel;
internal static bool PublicBonusChatSentThisLevel;
internal static readonly HashSet<int> InitialValuableIds = new HashSet<int>();
internal static readonly HashSet<int> DeliveredInitialValuableIds = new HashSet<int>();
internal static readonly List<int> PendingDeliveredIds = new List<int>();
internal static readonly Dictionary<int, float> InitialValuableOriginalValues = new Dictionary<int, float>();
internal static readonly HashSet<int> ValueLostValuableIds = new HashSet<int>();
internal static bool NoLossExtraBonusEligible;
internal static float NextValueLossCheckTime;
internal static int PerfectClearStreak;
private const float MoneyBagEffectSpawnInterval = 1f / 3f;
private const float MoneyBagEffectCeilingOffset = 0.35f;
internal static bool MoneyBagEffectActive;
internal static float NextMoneyBagEffectSpawnTime;
internal static readonly List<GameObject> SpawnedMoneyBagEffects = new List<GameObject>();
internal static readonly List<Collider> TruckSpawnColliders = new List<Collider>();
internal static float NextTruckSpawnColliderRefreshTime;
private void Awake()
{
//IL_0058: Unknown result type (might be due to invalid IL or missing references)
//IL_0062: Expected O, but got Unknown
try
{
Instance = this;
if ((Object)(object)((Component)this).transform.parent != (Object)null)
{
((Component)this).transform.parent = null;
}
((Object)((Component)this).gameObject).hideFlags = (HideFlags)61;
Object.DontDestroyOnLoad((Object)(object)((Component)this).gameObject);
InitializeConfig();
InitializeReflectionCache();
ResetLevelState("Initial");
harmony = new Harmony("REPOJP.PerfectExtractionBonus");
harmony.PatchAll();
WriteLog("Loaded Version=4.0.0");
}
catch (Exception ex)
{
((BaseUnityPlugin)this).Logger.LogError((object)"Failure: Awake");
((BaseUnityPlugin)this).Logger.LogError((object)ex);
}
}
private void OnDestroy()
{
try
{
StopInitialSnapshotCoroutine();
StopMoneyBagEffect("OnDestroy", destroySpawnedObjects: true);
if (harmony != null)
{
harmony.UnpatchSelf();
}
}
catch (Exception ex)
{
((BaseUnityPlugin)this).Logger.LogError((object)"Failure: OnDestroy");
((BaseUnityPlugin)this).Logger.LogError((object)ex);
}
}
private void Update()
{
try
{
if (ModEnabled != null && ModEnabled.Value && (InitialSnapshotTaken || MoneyBagEffectActive || SpawnedMoneyBagEffects.Count != 0) && SemiFunc.IsMasterClientOrSingleplayer())
{
TryMonitorNoLossExtraBonusEligibility();
TryRunMoneyBagEffect();
TryCorrectEffectMoneyBagHaulGoal();
}
}
catch (Exception ex)
{
((BaseUnityPlugin)this).Logger.LogError((object)"Failure: Update");
((BaseUnityPlugin)this).Logger.LogError((object)ex);
}
}
private void InitializeConfig()
{
//IL_0044: Unknown result type (might be due to invalid IL or missing references)
//IL_004e: Expected O, but got Unknown
//IL_0096: Unknown result type (might be due to invalid IL or missing references)
//IL_00a0: Expected O, but got Unknown
//IL_00c9: Unknown result type (might be due to invalid IL or missing references)
//IL_00d3: Expected O, but got Unknown
//IL_00fc: Unknown result type (might be due to invalid IL or missing references)
//IL_0106: Expected O, but got Unknown
//IL_0199: Unknown result type (might be due to invalid IL or missing references)
//IL_01a3: Expected O, but got Unknown
//IL_01cf: Unknown result type (might be due to invalid IL or missing references)
//IL_01d9: Expected O, but got Unknown
//IL_0228: Unknown result type (might be due to invalid IL or missing references)
//IL_0232: Expected O, but got Unknown
//IL_025e: Unknown result type (might be due to invalid IL or missing references)
//IL_0268: Expected O, but got Unknown
ModEnabled = ((BaseUnityPlugin)this).Config.Bind<bool>("General", "ModEnabled", true, "Enable or disable PerfectExtractionBonus.PerfectExtractionBonusの有効無効");
BaseBonusPercent = ((BaseUnityPlugin)this).Config.Bind<int>("Bonus", "BaseBonusPercent", 10, new ConfigDescription("Base bonus percent for a perfect extraction clear.完全納品クリア時の基本ボーナス率", (AcceptableValueBase)(object)new AcceptableValueRange<int>(0, 100), Array.Empty<object>()));
EnableStreakBonus = ((BaseUnityPlugin)this).Config.Bind<bool>("Bonus", "EnableStreakBonus", true, "Enable additional bonus percent for consecutive perfect extractions.連続完全納品時の追加ボーナス率を有効化");
StreakAddPercent = ((BaseUnityPlugin)this).Config.Bind<int>("Bonus", "StreakAddPercent", 5, new ConfigDescription("Additional bonus percent added for each consecutive perfect clear after the first.2連続目以降に1回ごと加算するボーナス率", (AcceptableValueBase)(object)new AcceptableValueRange<int>(0, 100), Array.Empty<object>()));
MaxBonusPercent = ((BaseUnityPlugin)this).Config.Bind<int>("Bonus", "MaxBonusPercent", 50, new ConfigDescription("Maximum total bonus percent for the regular perfect extraction bonus.通常の完全納品ボーナス率の上限", (AcceptableValueBase)(object)new AcceptableValueRange<int>(0, 100), Array.Empty<object>()));
NoLossExtraBonusPercent = ((BaseUnityPlugin)this).Config.Bind<int>("Bonus", "NoLossExtraBonusPercent", 10, new ConfigDescription("Additional bonus percent granted when all initial valuables are extracted without any value loss.初期貴重品すべてを一度も価値減少させずに納品した場合の追加ボーナス率", (AcceptableValueBase)(object)new AcceptableValueRange<int>(0, 100), Array.Empty<object>()));
ResetStreakOnNonPerfectClear = ((BaseUnityPlugin)this).Config.Bind<bool>("Streak", "ResetStreakOnNonPerfectClear", true, "Reset streak when the level is cleared without a perfect extraction.通常クリアだが完全納品でない場合に連続数をリセット");
ResetStreakOnLevelFail = ((BaseUnityPlugin)this).Config.Bind<bool>("Streak", "ResetStreakOnLevelFail", true, "Reset streak when the level fails.レベル失敗時に連続数をリセット");
ShowBonusUI = ((BaseUnityPlugin)this).Config.Bind<bool>("UI", "ShowBonusUI", true, "Show bonus UI when the reward is granted.ボーナス獲得時にUI表示");
InitialSnapshotDelaySeconds = ((BaseUnityPlugin)this).Config.Bind<float>("Tracking", "InitialSnapshotDelaySeconds", 0.75f, new ConfigDescription("Delay after LevelGenerator.GenerateDone before stable initial valuable detection begins.LevelGenerator.GenerateDone後に初期貴重品安定検出を始めるまでの待機秒数", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 10f), Array.Empty<object>()));
SnapshotStableFrames = ((BaseUnityPlugin)this).Config.Bind<int>("Tracking", "SnapshotStableFrames", 20, new ConfigDescription("Number of consecutive frames the initial valuable set must remain unchanged before it is locked.初期貴重品集合が変化せず確定する必要がある連続フレーム数", (AcceptableValueBase)(object)new AcceptableValueRange<int>(1, 600), Array.Empty<object>()));
EnableMoneyBagEffect = ((BaseUnityPlugin)this).Config.Bind<bool>("Effect", "EnableMoneyBagEffect", true, "Spawn 9999-dollar money bags in the truck after perfect extraction is confirmed.完全納品確定後にトラック内へ9999ドルのお金袋を生成するエフェクトの有効無効");
MoneyBagEffectValue = ((BaseUnityPlugin)this).Config.Bind<int>("Effect", "MoneyBagEffectValue", 9999, new ConfigDescription("Dollar value displayed on each effect money bag. This does not directly add to run currency.エフェクト用お金袋の表示価値。実行中の通貨へ直接加算しない値", (AcceptableValueBase)(object)new AcceptableValueRange<int>(1, 999999), Array.Empty<object>()));
MaxAliveMoneyBagEffects = ((BaseUnityPlugin)this).Config.Bind<int>("Effect", "MaxAliveMoneyBagEffects", 120, new ConfigDescription("Maximum number of effect money bags kept alive before old ones are removed.古いお金袋を削除して維持する最大エフェクト数", (AcceptableValueBase)(object)new AcceptableValueRange<int>(3, 300), Array.Empty<object>()));
LogEnabled = ((BaseUnityPlugin)this).Config.Bind<bool>("Debug", "LogEnabled", true, "Enable detailed debug logging.詳細ログの出力設定");
PerfectBonusPublicChatMessage = ((BaseUnityPlugin)this).Config.Bind<string>("Chat", "PerfectBonusPublicChatMessage", "Perfect Extraction Bonus 〇〇k$", "Public forced chat message for a regular perfect extraction bonus. Replace the exact text 〇〇k$ with the dynamic bonus amount automatically.通常の完全納品ボーナス時に公開強制チャットで送る文言。文字列中の 〇〇k$ は動的なボーナス額に自動置換");
NoLossPerfectBonusPublicChatMessage = ((BaseUnityPlugin)this).Config.Bind<string>("Chat", "NoLossPerfectBonusPublicChatMessage", "Wow!!! Super No-loss Perfect Extraction Bonus 〇〇k$", "Public forced chat message for a no-loss perfect extraction bonus. Replace the exact text 〇〇k$ with the dynamic bonus amount automatically.無減額完全納品ボーナス時に公開強制チャットで送る文言。文字列中の 〇〇k$ は動的なボーナス額に自動置換");
AnnounceRemainingCountInPublicChat = ((BaseUnityPlugin)this).Config.Bind<bool>("Chat", "AnnounceRemainingCountInPublicChat", false, "Force all players to publicly chat the remaining undelivered initial valuable count after each successful delivery.途中納品ごとに残り未納品数を全員強制公開チャット");
RemainingCountPublicChatMessage = ((BaseUnityPlugin)this).Config.Bind<string>("Chat", "RemainingCountPublicChatMessage", "{remaining} valuables left", "Public chat message after each successful delivery. Supported placeholders: {remaining} {delivered} {initial}.途中納品ごとの公開チャット文。使用可能プレースホルダー: {remaining} {delivered} {initial}");
}
private static void InitializeReflectionCache()
{
valuableDollarValueOriginalField = AccessTools.Field(typeof(ValuableObject), "dollarValueOriginal");
valuableDollarValueCurrentField = AccessTools.Field(typeof(ValuableObject), "dollarValueCurrent");
valuableDollarValueOverrideField = AccessTools.Field(typeof(ValuableObject), "dollarValueOverride");
valuableDollarValueSetField = AccessTools.Field(typeof(ValuableObject), "dollarValueSet");
valuableDirectorSetupCompleteField = AccessTools.Field(typeof(ValuableDirector), "setupComplete");
physGrabObjectSpawnTorqueField = AccessTools.Field(typeof(PhysGrabObject), "spawnTorque");
roundDirectorHaulGoalMaxField = AccessTools.Field(typeof(RoundDirector), "haulGoalMax");
}
internal static void WriteLog(string message)
{
if (!((Object)(object)Instance == (Object)null) && LogEnabled != null && LogEnabled.Value)
{
((BaseUnityPlugin)Instance).Logger.LogInfo((object)("[PerfectExtractionBonus] " + message));
}
}
internal static void ClearLevelRuntimeTrackingAfterChangeLevel(string reason)
{
WaitingInitialSnapshot = false;
InitialSnapshotTaken = false;
LastObservedFingerprint = string.Empty;
StableObservedFrames = 0;
LevelResultProcessed = false;
BonusGrantedThisLevel = false;
PublicBonusChatSentThisLevel = false;
InitialValuableIds.Clear();
DeliveredInitialValuableIds.Clear();
PendingDeliveredIds.Clear();
InitialValuableOriginalValues.Clear();
ValueLostValuableIds.Clear();
NoLossExtraBonusEligible = true;
NextValueLossCheckTime = 0f;
CurrentTrackedLevelKey = GetCurrentLevelKey();
TruckSpawnColliders.Clear();
NextTruckSpawnColliderRefreshTime = 0f;
WriteLog("ClearLevelRuntimeTracking Reason=" + reason + " LevelKey=" + CurrentTrackedLevelKey);
}
internal static void ResetLevelState(string reason)
{
StopInitialSnapshotCoroutine();
CurrentTrackedLevelKey = GetCurrentLevelKey();
WaitingInitialSnapshot = false;
InitialSnapshotTaken = false;
LastObservedFingerprint = string.Empty;
StableObservedFrames = 0;
LevelResultProcessed = false;
BonusGrantedThisLevel = false;
PublicBonusChatSentThisLevel = false;
InitialValuableIds.Clear();
DeliveredInitialValuableIds.Clear();
PendingDeliveredIds.Clear();
InitialValuableOriginalValues.Clear();
ValueLostValuableIds.Clear();
NoLossExtraBonusEligible = true;
NextValueLossCheckTime = 0f;
StopMoneyBagEffect(reason, destroySpawnedObjects: false);
TruckSpawnColliders.Clear();
NextTruckSpawnColliderRefreshTime = 0f;
WriteLog("ResetLevelState Reason=" + reason + " LevelKey=" + CurrentTrackedLevelKey);
}
internal static string GetCurrentLevelKey()
{
//IL_002f: Unknown result type (might be due to invalid IL or missing references)
//IL_0034: Unknown result type (might be due to invalid IL or missing references)
if ((Object)(object)RunManager.instance != (Object)null && (Object)(object)RunManager.instance.levelCurrent != (Object)null)
{
return ((Object)RunManager.instance.levelCurrent).name;
}
Scene activeScene = SceneManager.GetActiveScene();
return ((Scene)(ref activeScene)).name;
}
internal static bool IsTrackedGameplayLevel()
{
if ((Object)(object)RunManager.instance == (Object)null)
{
return false;
}
if (SemiFunc.MenuLevel())
{
return false;
}
if (SemiFunc.RunIsLobby())
{
return false;
}
if (SemiFunc.RunIsShop())
{
return false;
}
if (SemiFunc.RunIsArena())
{
return false;
}
if (SemiFunc.RunIsTutorial())
{
return false;
}
if (SemiFunc.RunIsRecording())
{
return false;
}
if (SemiFunc.IsSplashScreen())
{
return false;
}
return true;
}
internal static void StartInitialSnapshotTrackingFromLevelGenerated()
{
if (!((Object)(object)Instance == (Object)null) && ModEnabled.Value && SemiFunc.IsMasterClientOrSingleplayer() && IsTrackedGameplayLevel())
{
ResetLevelState("LevelGenerator.GenerateDone");
CurrentTrackedLevelKey = GetCurrentLevelKey();
WaitingInitialSnapshot = true;
InitialSnapshotCoroutine = ((MonoBehaviour)Instance).StartCoroutine(InitialSnapshotRoutine(CurrentTrackedLevelKey));
WriteLog("InitialSnapshotRoutineStarted LevelKey=" + CurrentTrackedLevelKey);
}
}
internal static void StopInitialSnapshotCoroutine()
{
if ((Object)(object)Instance == (Object)null)
{
InitialSnapshotCoroutine = null;
}
else if (InitialSnapshotCoroutine != null)
{
((MonoBehaviour)Instance).StopCoroutine(InitialSnapshotCoroutine);
InitialSnapshotCoroutine = null;
}
}
[IteratorStateMachine(typeof(<InitialSnapshotRoutine>d__67))]
private static IEnumerator InitialSnapshotRoutine(string levelKey)
{
//yield-return decompiler failed: Unexpected instruction in Iterator.Dispose()
return new <InitialSnapshotRoutine>d__67(0)
{
levelKey = levelKey
};
}
internal static bool TryCaptureInitialValuablesStep(string levelKey)
{
try
{
if (!ModEnabled.Value)
{
return true;
}
if (!SemiFunc.IsMasterClientOrSingleplayer())
{
return true;
}
if (!IsTrackedGameplayLevel())
{
return true;
}
if (!string.Equals(levelKey, GetCurrentLevelKey(), StringComparison.Ordinal))
{
WriteLog("InitialSnapshotStopped Reason=LevelKeyChanged From=" + levelKey + " To=" + GetCurrentLevelKey());
return true;
}
if ((Object)(object)LevelGenerator.Instance == (Object)null || !LevelGenerator.Instance.Generated)
{
return false;
}
if ((Object)(object)ValuableDirector.instance == (Object)null)
{
return false;
}
if (!GetValuableDirectorSetupComplete())
{
return false;
}
HashSet<int> hashSet = BuildObservedInitialValuableIdSet();
if (hashSet.Count <= 0)
{
return false;
}
string text = BuildFingerprint(hashSet);
if (!string.Equals(text, LastObservedFingerprint, StringComparison.Ordinal))
{
LastObservedFingerprint = text;
StableObservedFrames = 1;
WriteLog($"InitialSnapshotObservedChanged LevelKey={levelKey} Count={hashSet.Count} StableFrames={StableObservedFrames}/{SnapshotStableFrames.Value}");
return false;
}
StableObservedFrames++;
if (StableObservedFrames < SnapshotStableFrames.Value)
{
return false;
}
LockInitialValuables(hashSet);
return true;
}
catch (Exception ex)
{
if ((Object)(object)Instance != (Object)null)
{
((BaseUnityPlugin)Instance).Logger.LogError((object)"Failure: TryCaptureInitialValuablesStep");
((BaseUnityPlugin)Instance).Logger.LogError((object)ex);
}
return true;
}
}
internal static HashSet<int> BuildObservedInitialValuableIdSet()
{
HashSet<int> hashSet = new HashSet<int>();
if ((Object)(object)ValuableDirector.instance == (Object)null || ValuableDirector.instance.valuableList == null)
{
return hashSet;
}
foreach (ValuableObject valuable in ValuableDirector.instance.valuableList)
{
if (!((Object)(object)valuable == (Object)null) && !((Object)(object)((Component)valuable).gameObject == (Object)null) && !((Object)(object)((Component)valuable).GetComponent<PerfectExtractionEffectMoneyBagMarker>() != (Object)null))
{
int valuableId = GetValuableId(((Component)valuable).gameObject);
if (valuableId != 0)
{
hashSet.Add(valuableId);
}
}
}
return hashSet;
}
internal static string BuildFingerprint(HashSet<int> valuableIds)
{
List<int> list = new List<int>(valuableIds);
list.Sort();
StringBuilder stringBuilder = new StringBuilder(list.Count * 8);
foreach (int item in list)
{
stringBuilder.Append(item);
stringBuilder.Append('|');
}
return stringBuilder.ToString();
}
internal static void LockInitialValuables(HashSet<int> observedInitialIds)
{
InitialValuableIds.Clear();
DeliveredInitialValuableIds.Clear();
PendingDeliveredIds.Clear();
InitialValuableOriginalValues.Clear();
ValueLostValuableIds.Clear();
NoLossExtraBonusEligible = true;
NextValueLossCheckTime = Time.time + 0.25f;
if ((Object)(object)ValuableDirector.instance != (Object)null && ValuableDirector.instance.valuableList != null)
{
foreach (ValuableObject valuable in ValuableDirector.instance.valuableList)
{
if (!((Object)(object)valuable == (Object)null) && !((Object)(object)((Component)valuable).gameObject == (Object)null) && !((Object)(object)((Component)valuable).GetComponent<PerfectExtractionEffectMoneyBagMarker>() != (Object)null))
{
int valuableId = GetValuableId(((Component)valuable).gameObject);
if (valuableId != 0 && observedInitialIds.Contains(valuableId))
{
float valuableOriginalValue = GetValuableOriginalValue(valuable);
InitialValuableIds.Add(valuableId);
InitialValuableOriginalValues[valuableId] = valuableOriginalValue;
}
}
}
}
CurrentTrackedLevelKey = GetCurrentLevelKey();
WaitingInitialSnapshot = false;
InitialSnapshotTaken = true;
StableObservedFrames = 0;
WriteLog($"InitialSnapshotCaptured LevelKey={CurrentTrackedLevelKey} InitialValuables={InitialValuableIds.Count} NoLossEligible={NoLossExtraBonusEligible}");
}
internal static int GetValuableId(GameObject gameObject)
{
if ((Object)(object)gameObject == (Object)null)
{
return 0;
}
PhotonView component = gameObject.GetComponent<PhotonView>();
if ((Object)(object)component != (Object)null && component.ViewID != 0)
{
return component.ViewID;
}
return ((Object)gameObject).GetInstanceID();
}
internal static bool GetValuableDirectorSetupComplete()
{
if ((Object)(object)ValuableDirector.instance == (Object)null)
{
return false;
}
if (valuableDirectorSetupCompleteField == null)
{
return true;
}
object value = valuableDirectorSetupCompleteField.GetValue(ValuableDirector.instance);
if (value is bool)
{
return (bool)value;
}
return true;
}
internal static float GetValuableOriginalValue(ValuableObject valuableObject)
{
if ((Object)(object)valuableObject == (Object)null)
{
return 0f;
}
float valuableFloatField = GetValuableFloatField(valuableObject, valuableDollarValueOriginalField);
if (valuableFloatField > 0f)
{
return valuableFloatField;
}
return Mathf.Max(0f, GetValuableCurrentValue(valuableObject));
}
internal static float GetValuableCurrentValue(ValuableObject valuableObject)
{
if ((Object)(object)valuableObject == (Object)null)
{
return 0f;
}
return Mathf.Max(0f, GetValuableFloatField(valuableObject, valuableDollarValueCurrentField));
}
internal static float GetValuableFloatField(ValuableObject valuableObject, FieldInfo fieldInfo)
{
if ((Object)(object)valuableObject == (Object)null || fieldInfo == null)
{
return 0f;
}
object value = fieldInfo.GetValue(valuableObject);
if (value is float)
{
return (float)value;
}
if (value is int)
{
return (int)value;
}
return 0f;
}
internal static bool GetValuableBoolField(ValuableObject valuableObject, FieldInfo fieldInfo)
{
if ((Object)(object)valuableObject == (Object)null || fieldInfo == null)
{
return false;
}
object value = fieldInfo.GetValue(valuableObject);
if (value is bool)
{
return (bool)value;
}
return false;
}
internal static void SetValuableIntField(ValuableObject valuableObject, FieldInfo fieldInfo, int value)
{
if (!((Object)(object)valuableObject == (Object)null) && !(fieldInfo == null))
{
fieldInfo.SetValue(valuableObject, value);
}
}
internal static void TryMonitorNoLossExtraBonusEligibility()
{
if (!InitialSnapshotTaken || !NoLossExtraBonusEligible || NoLossExtraBonusPercent.Value <= 0 || Time.time < NextValueLossCheckTime)
{
return;
}
NextValueLossCheckTime = Time.time + 0.25f;
if ((Object)(object)ValuableDirector.instance == (Object)null || ValuableDirector.instance.valuableList == null)
{
return;
}
foreach (ValuableObject valuable in ValuableDirector.instance.valuableList)
{
if (!((Object)(object)valuable == (Object)null))
{
EvaluateValueLossState(valuable, "PeriodicCheck");
if (!NoLossExtraBonusEligible)
{
break;
}
}
}
}
internal static void EvaluateValueLossState(ValuableObject valuableObject, string source)
{
if ((Object)(object)valuableObject == (Object)null || (Object)(object)((Component)valuableObject).GetComponent<PerfectExtractionEffectMoneyBagMarker>() != (Object)null || !InitialSnapshotTaken || !NoLossExtraBonusEligible || NoLossExtraBonusPercent.Value <= 0)
{
return;
}
int valuableId = GetValuableId(((Component)valuableObject).gameObject);
if (valuableId != 0 && InitialValuableIds.Contains(valuableId) && InitialValuableOriginalValues.TryGetValue(valuableId, out var value))
{
float valuableCurrentValue = GetValuableCurrentValue(valuableObject);
if (!(valuableCurrentValue + 0.01f >= value))
{
NoLossExtraBonusEligible = false;
ValueLostValuableIds.Add(valuableId);
WriteLog($"NoLossExtraBonusDisabled Source={source} ValuableId={valuableId} InitialValue={value:0.###} CurrentValue={valuableCurrentValue:0.###}");
}
}
}
internal static void SnapshotDeliveredValuablesFromHaulList(bool firstOnly)
{
PendingDeliveredIds.Clear();
if (!ModEnabled.Value || !SemiFunc.IsMasterClientOrSingleplayer() || !InitialSnapshotTaken || (Object)(object)RoundDirector.instance == (Object)null || RoundDirector.instance.dollarHaulList == null)
{
return;
}
if (firstOnly)
{
if (RoundDirector.instance.dollarHaulList.Count != 0)
{
GameObject gameObject = RoundDirector.instance.dollarHaulList[0];
TryAddPendingDeliveredId(gameObject);
}
return;
}
foreach (GameObject dollarHaul in RoundDirector.instance.dollarHaulList)
{
TryAddPendingDeliveredId(dollarHaul);
}
}
internal static void TryAddPendingDeliveredId(GameObject gameObject)
{
if ((Object)(object)gameObject == (Object)null)
{
return;
}
ValuableObject component = gameObject.GetComponent<ValuableObject>();
if (!((Object)(object)component == (Object)null) && !((Object)(object)gameObject.GetComponent<PerfectExtractionEffectMoneyBagMarker>() != (Object)null))
{
EvaluateValueLossState(component, "DeliverySnapshot");
int valuableId = GetValuableId(gameObject);
if (valuableId != 0 && InitialValuableIds.Contains(valuableId) && !PendingDeliveredIds.Contains(valuableId))
{
PendingDeliveredIds.Add(valuableId);
}
}
}
internal static void CommitPendingDeliveredValuables()
{
if (!ModEnabled.Value)
{
PendingDeliveredIds.Clear();
}
else if (!SemiFunc.IsMasterClientOrSingleplayer())
{
PendingDeliveredIds.Clear();
}
else
{
if (PendingDeliveredIds.Count == 0)
{
return;
}
int num = 0;
foreach (int pendingDeliveredId in PendingDeliveredIds)
{
if (pendingDeliveredId != 0 && DeliveredInitialValuableIds.Add(pendingDeliveredId))
{
num++;
}
}
PendingDeliveredIds.Clear();
if (num > 0)
{
int missingInitialValuableCount = GetMissingInitialValuableCount();
WriteLog($"CommitDelivered Added={num} Delivered={DeliveredInitialValuableIds.Count} Initial={InitialValuableIds.Count} Remaining={missingInitialValuableCount}");
if (AnnounceRemainingCountInPublicChat.Value)
{
string message = FormatRemainingCountChatMessage(missingInitialValuableCount);
ForcePublicChatFromAllPlayers(message, "RemainingCount");
}
TryBroadcastPublicBonusChatIfPerfect("FinalDelivery");
TryStartMoneyBagEffectIfPerfect("FinalDelivery");
}
}
}
internal static bool IsPerfectExtraction()
{
if (!InitialSnapshotTaken)
{
return false;
}
if (InitialValuableIds.Count <= 0)
{
return false;
}
foreach (int initialValuableId in InitialValuableIds)
{
if (!DeliveredInitialValuableIds.Contains(initialValuableId))
{
return false;
}
}
return true;
}
internal static bool IsNoLossPerfectExtraction()
{
if (!IsPerfectExtraction())
{
return false;
}
if (NoLossExtraBonusPercent.Value <= 0)
{
return false;
}
if (!NoLossExtraBonusEligible)
{
return false;
}
return true;
}
internal static int GetMissingInitialValuableCount()
{
if (!InitialSnapshotTaken)
{
return 0;
}
int num = InitialValuableIds.Count - DeliveredInitialValuableIds.Count;
if (num < 0)
{
num = 0;
}
return num;
}
internal static void GetCurrentBonusCalculation(out int baseBonusPercent, out int streakStepCount, out int streakAddPercentPerStep, out int streakBonusPercent, out int uncappedBonusPercent, out int finalBonusPercent, out int maxBonusPercent)
{
baseBonusPercent = Mathf.Max(0, BaseBonusPercent.Value);
streakStepCount = 0;
streakAddPercentPerStep = 0;
streakBonusPercent = 0;
maxBonusPercent = Mathf.Max(0, MaxBonusPercent.Value);
if (EnableStreakBonus.Value && PerfectClearStreak > 0)
{
streakStepCount = PerfectClearStreak;
streakAddPercentPerStep = Mathf.Max(0, StreakAddPercent.Value);
streakBonusPercent = streakAddPercentPerStep * streakStepCount;
}
uncappedBonusPercent = baseBonusPercent + streakBonusPercent;
finalBonusPercent = Mathf.Min(uncappedBonusPercent, maxBonusPercent);
}
internal static void GetCurrentGrantedBonusAmounts(out int currencyBefore, out int rewardStreakNumber, out int baseBonusPercent, out int streakStepCount, out int streakAddPercentPerStep, out int streakBonusPercent, out int uncappedBonusPercent, out int regularBonusPercent, out int maxBonusPercent, out int regularBonusAmount, out int noLossExtraBonusPercentApplied, out int noLossExtraBonusAmount, out int totalBonusPercentForDisplay, out int totalBonusAmount, out int currencyAfter, out bool isNoLossPerfectExtraction)
{
currencyBefore = SemiFunc.StatGetRunCurrency();
rewardStreakNumber = PerfectClearStreak + 1;
GetCurrentBonusCalculation(out baseBonusPercent, out streakStepCount, out streakAddPercentPerStep, out streakBonusPercent, out uncappedBonusPercent, out regularBonusPercent, out maxBonusPercent);
isNoLossPerfectExtraction = IsNoLossPerfectExtraction();
noLossExtraBonusPercentApplied = (isNoLossPerfectExtraction ? Mathf.Max(0, NoLossExtraBonusPercent.Value) : 0);
regularBonusAmount = Mathf.FloorToInt((float)currencyBefore * ((float)regularBonusPercent / 100f));
noLossExtraBonusAmount = Mathf.FloorToInt((float)currencyBefore * ((float)noLossExtraBonusPercentApplied / 100f));
totalBonusPercentForDisplay = regularBonusPercent + noLossExtraBonusPercentApplied;
totalBonusAmount = regularBonusAmount + noLossExtraBonusAmount;
currencyAfter = currencyBefore + totalBonusAmount;
}
internal static string FormatBonusAmountForPublicChat(int bonusAmount)
{
return ((float)Mathf.Max(0, bonusAmount) / 1000f).ToString("0.##", CultureInfo.InvariantCulture) + "k$";
}
internal static string BuildPublicBonusChatMessage(string template, int bonusAmount)
{
if (string.IsNullOrEmpty(template))
{
return string.Empty;
}
return template.Replace("〇〇k$", FormatBonusAmountForPublicChat(bonusAmount));
}
internal static string FormatRemainingCountChatMessage(int remainingCount)
{
string value = RemainingCountPublicChatMessage.Value;
if (string.IsNullOrEmpty(value))
{
return string.Empty;
}
string text = value;
text = text.Replace("{remaining}", remainingCount.ToString());
text = text.Replace("{delivered}", DeliveredInitialValuableIds.Count.ToString());
return text.Replace("{initial}", InitialValuableIds.Count.ToString());
}
internal static void ForcePublicChatFromAllPlayers(string message, string source)
{
if (string.IsNullOrWhiteSpace(message))
{
WriteLog("ForcePublicChatSkipped Source=" + source + " Reason=EmptyMessage");
return;
}
int num = 0;
if ((Object)(object)GameDirector.instance != (Object)null && GameDirector.instance.PlayerList != null)
{
foreach (PlayerAvatar player in GameDirector.instance.PlayerList)
{
if ((Object)(object)player == (Object)null)
{
continue;
}
try
{
MethodInfo methodInfo = AccessTools.Method(((object)player).GetType(), "ChatMessageSend", new Type[1] { typeof(string) }, (Type[])null);
if (methodInfo != null)
{
methodInfo.Invoke(player, new object[1] { message });
num++;
}
}
catch (Exception ex)
{
if ((Object)(object)Instance != (Object)null)
{
((BaseUnityPlugin)Instance).Logger.LogError((object)"Failure: ForcePublicChatFromAllPlayers");
((BaseUnityPlugin)Instance).Logger.LogError((object)ex);
}
}
}
}
if (num <= 0 && (Object)(object)TruckScreenText.instance != (Object)null)
{
try
{
TruckScreenText.instance.MessageSendCustom(string.Empty, message, 0);
num = 1;
}
catch (Exception ex2)
{
if ((Object)(object)Instance != (Object)null)
{
((BaseUnityPlugin)Instance).Logger.LogError((object)"Failure: ForcePublicChatFromAllPlayers Fallback");
((BaseUnityPlugin)Instance).Logger.LogError((object)ex2);
}
}
}
WriteLog($"ForcePublicChatSent Source={source} SentCount={num} Message={message}");
}
internal static void TryBroadcastPublicBonusChatIfPerfect(string source)
{
if (ModEnabled.Value && SemiFunc.IsMasterClientOrSingleplayer() && IsTrackedGameplayLevel() && InitialSnapshotTaken && !PublicBonusChatSentThisLevel && IsPerfectExtraction())
{
GetCurrentGrantedBonusAmounts(out var _, out var rewardStreakNumber, out var _, out var _, out var _, out var _, out var _, out var _, out var _, out var regularBonusAmount, out var _, out var _, out var _, out var totalBonusAmount, out var _, out var isNoLossPerfectExtraction);
string template = (isNoLossPerfectExtraction ? NoLossPerfectBonusPublicChatMessage.Value : PerfectBonusPublicChatMessage.Value);
int num = (isNoLossPerfectExtraction ? totalBonusAmount : regularBonusAmount);
string text = BuildPublicBonusChatMessage(template, num);
if (string.IsNullOrEmpty(text))
{
WriteLog("PublicBonusChatSkipped Source=" + source + " Reason=EmptyMessage");
return;
}
ForcePublicChatFromAllPlayers(text, source);
PublicBonusChatSentThisLevel = true;
WriteLog($"PublicBonusChatSent Source={source} NoLoss={isNoLossPerfectExtraction} RewardStreak={rewardStreakNumber} ChatBonusAmount={num} Message={text}");
}
}
internal static void ShowPerfectBonusUI(int totalBonusPercent, int totalBonusAmount, int streakValue, int noLossExtraBonusPercentApplied)
{
//IL_0067: Unknown result type (might be due to invalid IL or missing references)
//IL_006c: Unknown result type (might be due to invalid IL or missing references)
if (!ShowBonusUI.Value)
{
return;
}
try
{
string text = ((noLossExtraBonusPercentApplied <= 0) ? $"PERFECT EXTRACTION BONUS\n+{totalBonusPercent}% +{SemiFunc.DollarGetString(totalBonusAmount)}\nSTREAK x{streakValue}" : $"PERFECT EXTRACTION BONUS\n+{totalBonusPercent}% +{SemiFunc.DollarGetString(totalBonusAmount)}\nSTREAK x{streakValue}\nNO LOSS BONUS +{noLossExtraBonusPercentApplied}%");
SemiFunc.UIFocusText(text, Color.yellow, Color.green, 4f);
}
catch (Exception ex)
{
if ((Object)(object)Instance != (Object)null)
{
((BaseUnityPlugin)Instance).Logger.LogError((object)"Failure: ShowPerfectBonusUI");
((BaseUnityPlugin)Instance).Logger.LogError((object)ex);
}
}
}
internal static void TryApplyPerfectExtractionBonus()
{
if (!ModEnabled.Value || !SemiFunc.IsMasterClientOrSingleplayer() || !IsTrackedGameplayLevel() || BonusGrantedThisLevel)
{
return;
}
bool flag = IsPerfectExtraction();
int missingInitialValuableCount = GetMissingInitialValuableCount();
WriteLog($"BonusEvaluation LevelKey={CurrentTrackedLevelKey} SnapshotTaken={InitialSnapshotTaken} Delivered={DeliveredInitialValuableIds.Count} Initial={InitialValuableIds.Count} Missing={missingInitialValuableCount} Perfect={flag} NoLossEligible={NoLossExtraBonusEligible} NoLossExtraBonusPercent={Mathf.Max(0, NoLossExtraBonusPercent.Value)} ValueLostCount={ValueLostValuableIds.Count}");
if (!flag)
{
int perfectClearStreak = PerfectClearStreak;
if (ResetStreakOnNonPerfectClear.Value)
{
PerfectClearStreak = 0;
WriteLog($"ResetStreak Reason=NonPerfectClear Previous={perfectClearStreak} Current={PerfectClearStreak}");
}
WriteLog($"BonusResult Granted=False Reason=NotPerfectExtraction Delivered={DeliveredInitialValuableIds.Count} Initial={InitialValuableIds.Count} Missing={missingInitialValuableCount} CurrentStreak={PerfectClearStreak}");
return;
}
TryBroadcastPublicBonusChatIfPerfect("LevelCompletionFallback");
GetCurrentGrantedBonusAmounts(out var currencyBefore, out var rewardStreakNumber, out var baseBonusPercent, out var streakStepCount, out var streakAddPercentPerStep, out var streakBonusPercent, out var uncappedBonusPercent, out var regularBonusPercent, out var maxBonusPercent, out var regularBonusAmount, out var noLossExtraBonusPercentApplied, out var noLossExtraBonusAmount, out var totalBonusPercentForDisplay, out var totalBonusAmount, out var currencyAfter, out var isNoLossPerfectExtraction);
WriteLog($"RegularBonusRateCalculation RewardStreak={rewardStreakNumber} Base={baseBonusPercent}% + Streak({streakStepCount} x {streakAddPercentPerStep}%)={streakBonusPercent}% => Uncapped={uncappedBonusPercent}% => Final={regularBonusPercent}% Max={maxBonusPercent}%");
WriteLog($"RegularBonusAmountCalculation floor({currencyBefore} x ({regularBonusPercent} / 100)) = {regularBonusAmount}");
if (isNoLossPerfectExtraction)
{
WriteLog($"NoLossExtraBonusRateCalculation RewardStreak={rewardStreakNumber} Extra={noLossExtraBonusPercentApplied}%");
WriteLog($"NoLossExtraBonusAmountCalculation floor({currencyBefore} x ({noLossExtraBonusPercentApplied} / 100)) = {noLossExtraBonusAmount}");
}
else
{
WriteLog($"NoLossExtraBonusResult Granted=False Eligible={NoLossExtraBonusEligible} ConfigPercent={Mathf.Max(0, NoLossExtraBonusPercent.Value)} ValueLostCount={ValueLostValuableIds.Count}");
}
WriteLog($"TotalBonusAmountCalculation {regularBonusAmount} + {noLossExtraBonusAmount} = {totalBonusAmount}");
SemiFunc.StatSetRunCurrency(currencyAfter);
PerfectClearStreak++;
BonusGrantedThisLevel = true;
WriteLog($"BonusResult Granted=True RewardStreak={PerfectClearStreak} RegularBonusPercent={regularBonusPercent}% RegularBonusAmount={regularBonusAmount} NoLossExtraBonusPercent={noLossExtraBonusPercentApplied}% NoLossExtraBonusAmount={noLossExtraBonusAmount} TotalBonusPercent={totalBonusPercentForDisplay}% TotalBonusAmount={totalBonusAmount} CurrencyBefore={currencyBefore} CurrencyAfter={currencyAfter}");
ShowPerfectBonusUI(totalBonusPercentForDisplay, totalBonusAmount, PerfectClearStreak, noLossExtraBonusPercentApplied);
}
internal static void TryStartMoneyBagEffectIfPerfect(string source)
{
if (EnableMoneyBagEffect.Value && !MoneyBagEffectActive && IsPerfectExtraction() && IsTrackedGameplayLevel())
{
MoneyBagEffectActive = true;
NextMoneyBagEffectSpawnTime = Time.time;
RefreshTruckSpawnColliders(force: true);
WriteLog("MoneyBagEffectStarted Source=" + source + " Value=" + MoneyBagEffectValue.Value);
}
}
internal static void StopMoneyBagEffect(string reason, bool destroySpawnedObjects)
{
if (!MoneyBagEffectActive && SpawnedMoneyBagEffects.Count == 0)
{
return;
}
MoneyBagEffectActive = false;
NextMoneyBagEffectSpawnTime = 0f;
if (destroySpawnedObjects)
{
DestroyAllMoneyBagEffects();
}
else
{
SpawnedMoneyBagEffects.RemoveAll((GameObject item) => (Object)(object)item == (Object)null);
}
WriteLog("MoneyBagEffectStopped Reason=" + reason + " Destroy=" + destroySpawnedObjects);
}
internal static void TryRunMoneyBagEffect()
{
if (!MoneyBagEffectActive)
{
return;
}
if (!EnableMoneyBagEffect.Value)
{
StopMoneyBagEffect("ConfigDisabled", destroySpawnedObjects: true);
return;
}
if (!IsTrackedGameplayLevel())
{
StopMoneyBagEffect("NotTrackedLevel", destroySpawnedObjects: true);
return;
}
if (!IsPerfectExtraction())
{
StopMoneyBagEffect("NoLongerPerfect", destroySpawnedObjects: true);
return;
}
int num = 0;
while (Time.time >= NextMoneyBagEffectSpawnTime && num < 6)
{
SpawnOneMoneyBagEffect();
NextMoneyBagEffectSpawnTime += 1f / 3f;
num++;
}
}
internal static void SpawnOneMoneyBagEffect()
{
//IL_002a: Unknown result type (might be due to invalid IL or missing references)
//IL_002f: Unknown result type (might be due to invalid IL or missing references)
//IL_0030: Unknown result type (might be due to invalid IL or missing references)
//IL_0035: Unknown result type (might be due to invalid IL or missing references)
//IL_0065: Unknown result type (might be due to invalid IL or missing references)
//IL_0066: Unknown result type (might be due to invalid IL or missing references)
//IL_0058: Unknown result type (might be due to invalid IL or missing references)
//IL_0059: Unknown result type (might be due to invalid IL or missing references)
//IL_012f: Unknown result type (might be due to invalid IL or missing references)
//IL_0143: Unknown result type (might be due to invalid IL or missing references)
//IL_014f: Unknown result type (might be due to invalid IL or missing references)
//IL_0163: Unknown result type (might be due to invalid IL or missing references)
//IL_0102: Unknown result type (might be due to invalid IL or missing references)
//IL_010c: Unknown result type (might be due to invalid IL or missing references)
if ((Object)(object)AssetManager.instance == (Object)null || (Object)(object)AssetManager.instance.surplusValuableSmall == (Object)null)
{
WriteLog("MoneyBagEffectSpawnSkipped Reason=MissingSurplusValuableSmall");
return;
}
Vector3 randomTruckCeilingPosition = GetRandomTruckCeilingPosition();
Quaternion rotation = Random.rotation;
GameObject surplusValuableSmall = AssetManager.instance.surplusValuableSmall;
GameObject val = ((!SemiFunc.IsMultiplayer()) ? Object.Instantiate<GameObject>(surplusValuableSmall, randomTruckCeilingPosition, rotation) : PhotonNetwork.InstantiateRoomObject("Valuables/" + ((Object)surplusValuableSmall).name, randomTruckCeilingPosition, rotation, (byte)0, (object[])null));
if (!((Object)(object)val == (Object)null))
{
PerfectExtractionEffectMoneyBagMarker perfectExtractionEffectMoneyBagMarker = val.GetComponent<PerfectExtractionEffectMoneyBagMarker>();
if ((Object)(object)perfectExtractionEffectMoneyBagMarker == (Object)null)
{
perfectExtractionEffectMoneyBagMarker = val.AddComponent<PerfectExtractionEffectMoneyBagMarker>();
}
perfectExtractionEffectMoneyBagMarker.Value = Mathf.Max(1, MoneyBagEffectValue.Value);
perfectExtractionEffectMoneyBagMarker.HaulGoalCorrected = false;
ValuableObject component = val.GetComponent<ValuableObject>();
if ((Object)(object)component != (Object)null)
{
SetValuableIntField(component, valuableDollarValueOverrideField, perfectExtractionEffectMoneyBagMarker.Value);
component.DollarValueSetLogic();
}
PhysGrabObject component2 = val.GetComponent<PhysGrabObject>();
if ((Object)(object)component2 != (Object)null && physGrabObjectSpawnTorqueField != null)
{
physGrabObjectSpawnTorqueField.SetValue(component2, Random.insideUnitSphere * 0.08f);
}
Rigidbody component3 = val.GetComponent<Rigidbody>();
if ((Object)(object)component3 != (Object)null)
{
component3.velocity = Vector3.down * Random.Range(0.25f, 1.25f);
component3.angularVelocity = Random.insideUnitSphere * Random.Range(1f, 4f);
}
SpawnedMoneyBagEffects.Add(val);
TrimOldMoneyBagEffects();
}
}
internal static Vector3 GetRandomTruckCeilingPosition()
{
//IL_016b: Unknown result type (might be due to invalid IL or missing references)
//IL_0175: Unknown result type (might be due to invalid IL or missing references)
//IL_017a: Unknown result type (might be due to invalid IL or missing references)
//IL_0186: Unknown result type (might be due to invalid IL or missing references)
//IL_018b: Unknown result type (might be due to invalid IL or missing references)
//IL_0197: Unknown result type (might be due to invalid IL or missing references)
//IL_019e: Unknown result type (might be due to invalid IL or missing references)
//IL_01a3: Unknown result type (might be due to invalid IL or missing references)
//IL_022b: Unknown result type (might be due to invalid IL or missing references)
//IL_0235: Unknown result type (might be due to invalid IL or missing references)
//IL_00ef: Unknown result type (might be due to invalid IL or missing references)
//IL_00f4: Unknown result type (might be due to invalid IL or missing references)
//IL_00f7: Unknown result type (might be due to invalid IL or missing references)
//IL_0103: Unknown result type (might be due to invalid IL or missing references)
//IL_0114: Unknown result type (might be due to invalid IL or missing references)
//IL_0126: Unknown result type (might be due to invalid IL or missing references)
//IL_013d: Unknown result type (might be due to invalid IL or missing references)
//IL_0149: Unknown result type (might be due to invalid IL or missing references)
//IL_0158: Unknown result type (might be due to invalid IL or missing references)
//IL_0051: Unknown result type (might be due to invalid IL or missing references)
//IL_005b: Unknown result type (might be due to invalid IL or missing references)
//IL_0060: Unknown result type (might be due to invalid IL or missing references)
//IL_006e: Unknown result type (might be due to invalid IL or missing references)
//IL_00a2: Unknown result type (might be due to invalid IL or missing references)
//IL_00a7: Unknown result type (might be due to invalid IL or missing references)
//IL_00ae: Unknown result type (might be due to invalid IL or missing references)
//IL_00b9: Unknown result type (might be due to invalid IL or missing references)
//IL_00c2: Unknown result type (might be due to invalid IL or missing references)
//IL_00c9: Unknown result type (might be due to invalid IL or missing references)
//IL_00d4: Unknown result type (might be due to invalid IL or missing references)
//IL_00d9: Unknown result type (might be due to invalid IL or missing references)
//IL_00de: Unknown result type (might be due to invalid IL or missing references)
//IL_00e6: Unknown result type (might be due to invalid IL or missing references)
//IL_00e8: Unknown result type (might be due to invalid IL or missing references)
//IL_008f: Unknown result type (might be due to invalid IL or missing references)
//IL_01f0: Unknown result type (might be due to invalid IL or missing references)
//IL_01fa: Unknown result type (might be due to invalid IL or missing references)
//IL_01ff: Unknown result type (might be due to invalid IL or missing references)
//IL_0208: Unknown result type (might be due to invalid IL or missing references)
//IL_020d: Unknown result type (might be due to invalid IL or missing references)
//IL_0219: Unknown result type (might be due to invalid IL or missing references)
//IL_0220: Unknown result type (might be due to invalid IL or missing references)
//IL_0225: Unknown result type (might be due to invalid IL or missing references)
RefreshTruckSpawnColliders(force: false);
if (TruckSpawnColliders.Count > 0)
{
Collider val = TruckSpawnColliders[Random.Range(0, TruckSpawnColliders.Count)];
if ((Object)(object)val != (Object)null)
{
BoxCollider val2 = (BoxCollider)(object)((val is BoxCollider) ? val : null);
if ((Object)(object)val2 != (Object)null)
{
Vector3 val3 = val2.size * 0.5f;
float num = 0.35f;
if (Mathf.Abs(((Component)val2).transform.lossyScale.y) > 0.01f)
{
num = 0.35f / Mathf.Abs(((Component)val2).transform.lossyScale.y);
}
Vector3 val4 = val2.center + new Vector3(Random.Range(0f - val3.x, val3.x), val3.y - num, Random.Range(0f - val3.z, val3.z));
return ((Component)val2).transform.TransformPoint(val4);
}
Bounds bounds = val.bounds;
return new Vector3(Random.Range(((Bounds)(ref bounds)).min.x, ((Bounds)(ref bounds)).max.x), Mathf.Max(((Bounds)(ref bounds)).min.y + 0.5f, ((Bounds)(ref bounds)).max.y - 0.35f), Random.Range(((Bounds)(ref bounds)).min.z, ((Bounds)(ref bounds)).max.z));
}
}
if ((Object)(object)TruckSafetySpawnPoint.instance != (Object)null)
{
Vector2 val5 = Random.insideUnitCircle * 2.5f;
return ((Component)TruckSafetySpawnPoint.instance).transform.position + new Vector3(val5.x, 2.5f, val5.y);
}
if ((Object)(object)GameDirector.instance != (Object)null && GameDirector.instance.PlayerList != null && GameDirector.instance.PlayerList.Count > 0)
{
PlayerAvatar val6 = GameDirector.instance.PlayerList[0];
if ((Object)(object)val6 != (Object)null)
{
Vector2 val7 = Random.insideUnitCircle * 2.5f;
return ((Component)val6).transform.position + new Vector3(val7.x, 2.5f, val7.y);
}
}
return Vector3.up * 2.5f;
}
internal static void RefreshTruckSpawnColliders(bool force)
{
if (!force && Time.time < NextTruckSpawnColliderRefreshTime)
{
return;
}
NextTruckSpawnColliderRefreshTime = Time.time + 5f;
TruckSpawnColliders.Clear();
RoomVolume[] array = Object.FindObjectsOfType<RoomVolume>();
RoomVolume[] array2 = array;
foreach (RoomVolume val in array2)
{
if ((Object)(object)val == (Object)null || !val.Truck)
{
continue;
}
Collider[] componentsInChildren = ((Component)val).GetComponentsInChildren<Collider>(true);
Collider[] array3 = componentsInChildren;
foreach (Collider val2 in array3)
{
if (!((Object)(object)val2 == (Object)null) && val2.enabled && (val2 is BoxCollider || val2 is MeshCollider))
{
TruckSpawnColliders.Add(val2);
}
}
}
WriteLog("TruckSpawnCollidersRefreshed Count=" + TruckSpawnColliders.Count);
}
internal static void TrimOldMoneyBagEffects()
{
SpawnedMoneyBagEffects.RemoveAll((GameObject item) => (Object)(object)item == (Object)null);
int num = Mathf.Max(3, MaxAliveMoneyBagEffects.Value);
while (SpawnedMoneyBagEffects.Count > num)
{
GameObject gameObject = SpawnedMoneyBagEffects[0];
SpawnedMoneyBagEffects.RemoveAt(0);
DestroyMoneyBagEffect(gameObject);
}
}
internal static void DestroyAllMoneyBagEffects()
{
for (int num = SpawnedMoneyBagEffects.Count - 1; num >= 0; num--)
{
DestroyMoneyBagEffect(SpawnedMoneyBagEffects[num]);
}
SpawnedMoneyBagEffects.Clear();
}
internal static void DestroyMoneyBagEffect(GameObject gameObject)
{
if ((Object)(object)gameObject == (Object)null)
{
return;
}
try
{
PhysGrabObject component = gameObject.GetComponent<PhysGrabObject>();
if ((Object)(object)component != (Object)null)
{
component.DestroyPhysGrabObject();
return;
}
if (SemiFunc.IsMultiplayer())
{
PhotonView component2 = gameObject.GetComponent<PhotonView>();
if ((Object)(object)component2 != (Object)null)
{
PhotonNetwork.Destroy(gameObject);
return;
}
}
Object.Destroy((Object)(object)gameObject);
}
catch (Exception ex)
{
if ((Object)(object)Instance != (Object)null)
{
((BaseUnityPlugin)Instance).Logger.LogError((object)"Failure: DestroyMoneyBagEffect");
((BaseUnityPlugin)Instance).Logger.LogError((object)ex);
}
}
}
internal static void TryCorrectEffectMoneyBagHaulGoal()
{
if (SpawnedMoneyBagEffects.Count == 0 || (Object)(object)RoundDirector.instance == (Object)null || roundDirectorHaulGoalMaxField == null)
{
return;
}
SpawnedMoneyBagEffects.RemoveAll((GameObject item) => (Object)(object)item == (Object)null);
foreach (GameObject spawnedMoneyBagEffect in SpawnedMoneyBagEffects)
{
if ((Object)(object)spawnedMoneyBagEffect == (Object)null)
{
continue;
}
PerfectExtractionEffectMoneyBagMarker component = spawnedMoneyBagEffect.GetComponent<PerfectExtractionEffectMoneyBagMarker>();
if ((Object)(object)component == (Object)null || component.HaulGoalCorrected)
{
continue;
}
ValuableObject component2 = spawnedMoneyBagEffect.GetComponent<ValuableObject>();
if ((Object)(object)component2 == (Object)null)
{
component.HaulGoalCorrected = true;
}
else if (GetValuableBoolField(component2, valuableDollarValueSetField))
{
object value = roundDirectorHaulGoalMaxField.GetValue(RoundDirector.instance);
int num = 0;
if (value is int)
{
num = (int)value;
}
int num2 = Mathf.RoundToInt(GetValuableCurrentValue(component2));
int num3 = Mathf.Max(0, num - num2);
roundDirectorHaulGoalMaxField.SetValue(RoundDirector.instance, num3);
component.HaulGoalCorrected = true;
WriteLog("MoneyBagEffectHaulGoalCorrected Value=" + num2 + " Before=" + num + " After=" + num3);
}
}
}
}
internal sealed class PerfectExtractionEffectMoneyBagMarker : MonoBehaviour
{
internal int Value;
internal bool HaulGoalCorrected;
}
}