using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Runtime.Versioning;
using BepInEx;
using BepInEx.Configuration;
using BepInEx.Logging;
using ExitGames.Client.Photon;
using HarmonyLib;
using Photon.Pun;
using REPOLib.Modules;
using TMPro;
using UnityEngine;
using UnityEngine.UI;
[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.Default | DebuggableAttribute.DebuggingModes.DisableOptimizations | DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints | DebuggableAttribute.DebuggingModes.EnableEditAndContinue)]
[assembly: AssemblyTitle("TimeAttack")]
[assembly: AssemblyDescription("")]
[assembly: AssemblyConfiguration("")]
[assembly: AssemblyCompany("")]
[assembly: AssemblyProduct("TimeAttack")]
[assembly: AssemblyCopyright("Copyright © 2025")]
[assembly: AssemblyTrademark("")]
[assembly: ComVisible(false)]
[assembly: Guid("bef66bd7-b3d3-41df-a8f0-e5f682750c8e")]
[assembly: AssemblyFileVersion("1.0.0.0")]
[assembly: TargetFramework(".NETFramework,Version=v4.8", FrameworkDisplayName = ".NET Framework 4.8")]
[assembly: AssemblyVersion("1.0.0.0")]
namespace TimeAttack;
internal class ConfigManager
{
public static DifficultyManager.LevelTimeDifficulty levelTimeDifficulty = DifficultyManager.LevelTimeDifficulty.Standard;
public static DifficultyManager.ExtractionBonusDifficulty extractionBonusDifficulty = DifficultyManager.ExtractionBonusDifficulty.Standard;
public static DifficultyManager.FinalExtractionDifficulty finalExtractionDifficulty = DifficultyManager.FinalExtractionDifficulty.Standard;
public static DifficultyManager.RemainingTimeBonus remainingTimeBonus = DifficultyManager.RemainingTimeBonus.Standard;
public static void Set()
{
levelTimeDifficulty = Plugin.ltd.Value;
extractionBonusDifficulty = Plugin.ebd.Value;
finalExtractionDifficulty = Plugin.fed.Value;
remainingTimeBonus = Plugin.rtb.Value;
}
}
public class DifficultyManager
{
public enum LevelTimeDifficulty
{
Merciful,
Lenient,
Easy,
Standard,
Hard,
Extreme,
Insane,
Nightmare,
Impossible
}
public enum ExtractionBonusDifficulty
{
Merciful,
Lenient,
Easy,
Standard,
Hard,
Extreme,
Insane,
Nightmare,
Impossible
}
public enum FinalExtractionDifficulty
{
Merciful,
Lenient,
Easy,
Standard,
Hard,
Extreme,
Insane,
Nightmare,
Impossible
}
public enum RemainingTimeBonus
{
None,
Negligible,
Little,
Less,
Standard,
Extra,
Profitable,
Double
}
public static float LevelTimeDifficultyMultiplier => ConfigManager.levelTimeDifficulty switch
{
LevelTimeDifficulty.Merciful => 2f,
LevelTimeDifficulty.Lenient => 1.67f,
LevelTimeDifficulty.Easy => 1.33f,
LevelTimeDifficulty.Standard => 1f,
LevelTimeDifficulty.Hard => 0.8f,
LevelTimeDifficulty.Extreme => 0.67f,
LevelTimeDifficulty.Insane => 0.5f,
LevelTimeDifficulty.Nightmare => 0.33f,
LevelTimeDifficulty.Impossible => 0.17f,
_ => 1f,
};
public static float ExtractionBonusDifficultyMultiplier => ConfigManager.extractionBonusDifficulty switch
{
ExtractionBonusDifficulty.Merciful => 2f,
ExtractionBonusDifficulty.Lenient => 1.67f,
ExtractionBonusDifficulty.Easy => 1.33f,
ExtractionBonusDifficulty.Standard => 1f,
ExtractionBonusDifficulty.Hard => 0.8f,
ExtractionBonusDifficulty.Extreme => 0.65f,
ExtractionBonusDifficulty.Insane => 0.5f,
ExtractionBonusDifficulty.Nightmare => 0.35f,
ExtractionBonusDifficulty.Impossible => 0.2f,
_ => 1f,
};
public static float FinalExtractionDifficultyMultiplier => ConfigManager.finalExtractionDifficulty switch
{
FinalExtractionDifficulty.Merciful => 1.75f,
FinalExtractionDifficulty.Lenient => 1.33f,
FinalExtractionDifficulty.Easy => 1f,
FinalExtractionDifficulty.Standard => 0.8f,
FinalExtractionDifficulty.Hard => 0.67f,
FinalExtractionDifficulty.Extreme => 0.5f,
FinalExtractionDifficulty.Insane => 0.4f,
FinalExtractionDifficulty.Nightmare => 0.33f,
FinalExtractionDifficulty.Impossible => 0.17f,
_ => 1f,
};
public static float RemainingTimeBonusMultiplier => ConfigManager.remainingTimeBonus switch
{
RemainingTimeBonus.None => 0f,
RemainingTimeBonus.Negligible => 0.1f,
RemainingTimeBonus.Little => 0.33f,
RemainingTimeBonus.Less => 0.67f,
RemainingTimeBonus.Standard => 1f,
RemainingTimeBonus.Extra => 1.33f,
RemainingTimeBonus.Profitable => 1.67f,
RemainingTimeBonus.Double => 2f,
_ => 1f,
};
}
internal class NotificationManager
{
public enum NotificationEmoji
{
Clock,
Money
}
public static float notificationTimer = 0f;
public static string notificationText = "";
public static NotificationEmoji notificationEmoji = NotificationEmoji.Clock;
private static readonly Dictionary<NotificationEmoji, string> emojis = new Dictionary<NotificationEmoji, string>
{
{
NotificationEmoji.Clock,
"{clock}"
},
{
NotificationEmoji.Money,
"{$$}"
}
};
public static void Update()
{
//IL_0055: Unknown result type (might be due to invalid IL or missing references)
//IL_005a: Unknown result type (might be due to invalid IL or missing references)
if (notificationTimer != 0f)
{
notificationTimer = Mathf.Max(0f, notificationTimer - Time.deltaTime);
SemiFunc.UIBigMessage(notificationText, emojis[notificationEmoji], 28f, new Color(0f, 1f, 0.2f), Color.white);
}
}
public static void ShowTimerExtendedMessage(float seconds, float time)
{
notificationTimer = time;
notificationText = $"<size=48>+{Mathf.RoundToInt(seconds)}</size> SEC.";
notificationEmoji = NotificationEmoji.Clock;
}
public static void ShowBonusMoneyMessage(int amount)
{
notificationTimer = 3f;
notificationText = $"<size=48>+${Mathf.RoundToInt((float)amount)}K</size>\nTIME BONUS";
notificationEmoji = NotificationEmoji.Money;
}
}
internal class Patches
{
private static float surplusBonusFactor = 1f;
public static PlayerChatBoxState currentTruckState = (PlayerChatBoxState)0;
public static bool mapToolActive = false;
public static bool TruckLeaving => (int)currentTruckState == 2 || (int)currentTruckState == 3;
[HarmonyPatch(typeof(MapToolController), "Update")]
[HarmonyPostfix]
private static void MapToolController_Update(ref bool ___Active, ref PhotonView ___photonView)
{
if (!GameManager.Multiplayer() || ___photonView.IsMine)
{
mapToolActive = ___Active;
}
}
[HarmonyPatch(typeof(TruckScreenText), "Awake")]
[HarmonyPostfix]
private static void TruckScreenText_Awake()
{
//IL_0002: Unknown result type (might be due to invalid IL or missing references)
currentTruckState = (PlayerChatBoxState)0;
}
[HarmonyPatch(typeof(TruckScreenText), "PlayerChatBoxStateUpdateRPC")]
[HarmonyPostfix]
private static void TruckScreenText_PlayerChatBoxStateUpdateRPC(ref PlayerChatBoxState _state)
{
//IL_0003: Unknown result type (might be due to invalid IL or missing references)
currentTruckState = _state;
Plugin.loggy.LogDebug((object)$"TST: changing state to {_state}");
}
[HarmonyPatch(typeof(GoalUI), "Start")]
[HarmonyPostfix]
[HarmonyPriority(1)]
private static void GoalUI_Start()
{
if (SemiFunc.RunIsLevel())
{
Plugin.CreateCustomSemiUI();
}
}
private static void OnLevelChange(int level)
{
if ((Object)(object)TimerUI.instance != (Object)null)
{
TimerUI.instance.timerRunning = false;
}
ConfigManager.Set();
mapToolActive = false;
NotificationManager.notificationTimer = 0f;
NotificationManager.notificationText = "";
if (!SemiFunc.RunIsLevel())
{
return;
}
TimeProperties.BaseLevelTimer = TimeProperties.GetThisLevelBaseTime(level);
TimeProperties.BaseLevelTimerPostProcess = TimeProperties.GetThisLevelTime(level);
if (!((Object)(object)TimerUI.instance == (Object)null))
{
if (TimerUI.instance.gameOverCoroutine != null)
{
((MonoBehaviour)TimerUI.instance).StopCoroutine(TimerUI.instance.gameOverCoroutine);
TimerUI.instance.gameOverFinished = false;
TimerUI.instance.gameOverStarted = false;
}
TimerUI.instance.SetTimer(TimeProperties.BaseLevelTimerPostProcess, pause: true);
Plugin.loggy.LogDebug((object)"Level change, setting timer");
}
}
[HarmonyPatch(typeof(RunManager), "ChangeLevel")]
[HarmonyPostfix]
public static void RunManager_ChangeLevel(RunManager __instance)
{
OnLevelChange(__instance.levelsCompleted + 1);
}
[HarmonyPatch(typeof(RunManager), "UpdateLevel")]
[HarmonyPostfix]
public static void RunManager_UpdateLevel(RunManager __instance)
{
OnLevelChange(__instance.levelsCompleted + 1);
}
[HarmonyPatch(typeof(ExtractionPoint), "ButtonPress")]
[HarmonyPostfix]
public static void ExtractionPoint_ButtonPress()
{
if (SemiFunc.RunIsLevel())
{
if (!TimerUI.instance.timerRunning)
{
ConfigManager.Set();
TimeProperties.BaseLevelTimer = TimeProperties.GetThisLevelBaseTime(RunManager.instance.levelsCompleted + 1);
TimeProperties.BaseLevelTimerPostProcess = TimeProperties.GetThisLevelTime(RunManager.instance.levelsCompleted + 1);
TimerUI.instance.SetTimer(TimeProperties.BaseLevelTimerPostProcess);
TimerUI.instance.timerRunning = true;
Plugin.loggy.LogDebug((object)"First extraction activated, starting timer...");
}
else
{
Plugin.loggy.LogDebug((object)"Extraction activated, adding time...");
TimerUI.instance.SetTimer(TimerUI.instance.currentTimeRemaining);
TimerUI.instance.AddTimeToTimer(TimeProperties.ExtractionActivationBonusTime);
}
}
}
[HarmonyPatch(typeof(ExtractionPoint), "StateExtracting")]
[HarmonyPostfix]
public static void ExtractionPoint_StateExtracting(ref ExtractionPoint __instance)
{
if (SemiFunc.RunIsLevel() && !((Object)(object)__instance != (Object)(object)Traverse.Create((object)RoundDirector.instance).Field("extractionPointCurrent").GetValue<ExtractionPoint>()))
{
MethodInfo method = typeof(ExtractionPoint).GetMethod("StateIs", BindingFlags.Instance | BindingFlags.NonPublic);
if ((bool)method.Invoke(__instance, new object[1] { (object)(State)6 }) && Traverse.Create((object)__instance).Field("tubeHit").GetValue<bool>() && !TimerUI.instance.timerPaused)
{
Plugin.loggy.LogDebug((object)"Extraction started, pausing timer...");
TimerUI.instance.SetTimer(TimerUI.instance.currentTimeRemaining, pause: true);
int value = Traverse.Create((object)RoundDirector.instance).Field("currentHaul").GetValue<int>();
int value2 = Traverse.Create((object)__instance).Field("haulSurplus").GetValue<int>();
surplusBonusFactor = Mathf.Clamp((float)value / Mathf.Max(1000f, (float)value2), 1f, 10f);
}
}
}
[HarmonyPatch(typeof(RoundDirector), "ExtractionCompleted")]
[HarmonyPostfix]
public static void RoundDirector_ExtractionCompleted(ref RoundDirector __instance)
{
if (SemiFunc.RunIsLevel())
{
int value = Traverse.Create((object)__instance).Field("extractionPointsCompleted").GetValue<int>();
int value2 = Traverse.Create((object)__instance).Field("extractionPoints").GetValue<int>();
if (value >= value2)
{
Plugin.loggy.LogDebug((object)"All extractions done, setting final extract timer...");
TimerUI.instance.GenerateBonusMoney(TimerUI.instance.currentTimeRemaining);
TimerUI.instance.SetTimer(TimeProperties.GetThisLevelFinalExtractTime(RunManager.instance.levelsCompleted + 1));
}
else
{
Plugin.loggy.LogDebug((object)"Extraction completed, unpausing timer and adding time...");
TimerUI.instance.SetTimer(TimerUI.instance.currentTimeRemaining);
TimerUI.instance.AddTimeToTimer(TimeProperties.GetThisLevelAddedTime(RunManager.instance.levelsCompleted + 1, value2 - 1, surplusBonusFactor));
}
}
}
}
[BepInPlugin("Swaggies.TimeAttack", "TimeAttack", "1.1.0")]
[BepInDependency(/*Could not decode attribute arguments.*/)]
public class Plugin : BaseUnityPlugin
{
private const string _guid = "Swaggies.TimeAttack";
private const string _name = "TimeAttack";
public const string _ver = "1.1.0";
private readonly Harmony harmony = new Harmony("Swaggies.TimeAttack");
public static ManualLogSource loggy;
public static ConfigEntry<DifficultyManager.LevelTimeDifficulty> ltd;
public static ConfigEntry<DifficultyManager.ExtractionBonusDifficulty> ebd;
public static ConfigEntry<DifficultyManager.FinalExtractionDifficulty> fed;
public static ConfigEntry<DifficultyManager.RemainingTimeBonus> rtb;
private void Awake()
{
loggy = Logger.CreateLogSource("Swaggies.TimeAttack");
loggy.LogMessage((object)"TimeAttack's up and runnin' on 1.1.0");
harmony.PatchAll(typeof(Plugin));
harmony.PatchAll(typeof(Patches));
Syncing.Setup();
ltd = ((BaseUnityPlugin)this).Config.Bind<DifficultyManager.LevelTimeDifficulty>("Difficulty Settings", "Level Time Difficulty", DifficultyManager.LevelTimeDifficulty.Standard, "How much time you start each level with. Higher difficulty means less time.");
ebd = ((BaseUnityPlugin)this).Config.Bind<DifficultyManager.ExtractionBonusDifficulty>("Difficulty Settings", "Extraction Bonus Difficulty", DifficultyManager.ExtractionBonusDifficulty.Standard, "How much time you regain after completing an extraction. Higher difficulty means less time.");
fed = ((BaseUnityPlugin)this).Config.Bind<DifficultyManager.FinalExtractionDifficulty>("Difficulty Settings", "Final Extraction Difficulty", DifficultyManager.FinalExtractionDifficulty.Standard, "How much time you have to leave the level after completing the final extraction. Higher difficulty means less time.");
rtb = ((BaseUnityPlugin)this).Config.Bind<DifficultyManager.RemainingTimeBonus>("Difficulty Settings", "Remaining Time Bonus", DifficultyManager.RemainingTimeBonus.Standard, "How much bonus money you earn for completing a level with time to spare.");
}
public static void CreateCustomSemiUI()
{
//IL_0041: Unknown result type (might be due to invalid IL or missing references)
//IL_0055: Unknown result type (might be due to invalid IL or missing references)
//IL_005a: Unknown result type (might be due to invalid IL or missing references)
//IL_00be: Unknown result type (might be due to invalid IL or missing references)
loggy.LogDebug((object)"creating TimerUI object...");
GameObject val = GameObject.Find("Energy");
GameObject val2 = Object.Instantiate<GameObject>(val, val.transform.parent);
((Object)val2).name = "TimeAttackTimerUI";
Transform transform = val2.transform;
transform.localPosition -= new Vector3(25f, 80f, 0f);
EnergyUI val3 = default(EnergyUI);
if (val2.TryGetComponent<EnergyUI>(ref val3))
{
Object.DestroyImmediate((Object)(object)val3);
}
TextMeshProUGUI component = ((Component)val2.transform.Find("EnergyMax")).GetComponent<TextMeshProUGUI>();
if ((Object)(object)component != (Object)null)
{
Object.DestroyImmediate((Object)(object)component);
}
TextMeshProUGUI[] componentsInChildren = val2.GetComponentsInChildren<TextMeshProUGUI>();
foreach (TextMeshProUGUI val4 in componentsInChildren)
{
((Graphic)val4).color = Color.white;
}
((Behaviour)((Component)val2.transform).gameObject.GetComponentInChildren<Image>()).enabled = false;
val2.AddComponent<TimerUI>();
}
}
internal class Syncing
{
public static NetworkedEvent TimerSync;
public static NetworkedEvent TimerAdd;
public static NetworkedEvent TimerSet;
public static NetworkedEvent GameOverSequence;
public static NetworkedEvent BonusMoney;
public static void Setup()
{
//IL_0012: Unknown result type (might be due to invalid IL or missing references)
//IL_001c: Expected O, but got Unknown
//IL_002d: Unknown result type (might be due to invalid IL or missing references)
//IL_0037: Expected O, but got Unknown
//IL_0048: Unknown result type (might be due to invalid IL or missing references)
//IL_0052: Expected O, but got Unknown
//IL_0063: Unknown result type (might be due to invalid IL or missing references)
//IL_006d: Expected O, but got Unknown
//IL_007e: Unknown result type (might be due to invalid IL or missing references)
//IL_0088: Expected O, but got Unknown
TimerSync = new NetworkedEvent("Timer Sync", (Action<EventData>)SyncTimer);
TimerAdd = new NetworkedEvent("Timer Add", (Action<EventData>)AddToTimer);
TimerSet = new NetworkedEvent("Timer Set", (Action<EventData>)SetTimer);
GameOverSequence = new NetworkedEvent("Out Of Time Sequence", (Action<EventData>)GameOverSeq);
BonusMoney = new NetworkedEvent("Remaining Time Bonus", (Action<EventData>)RemainingTimeBonus);
}
private static void SyncTimer(EventData eventData)
{
float num = (float)eventData.CustomData;
if (!(Math.Abs(num - TimerUI.instance.currentTimeRemaining) < 0.33f))
{
TimerUI.instance.currentTimeRemaining = num;
Plugin.loggy.LogDebug((object)$"Set timer to {num}");
if (TimerUI.instance.currentTimeRemaining > 0f || TimerUI.instance.timerPaused)
{
TimerUI.instance.gameOverStarted = false;
TimerUI.instance.gameOverFinished = false;
}
Plugin.loggy.LogDebug((object)$"Syncing timer to {num:0.0}");
}
}
private static void AddToTimer(EventData eventData)
{
//IL_0034: Unknown result type (might be due to invalid IL or missing references)
float num = (float)eventData.CustomData;
TimerUI.instance.gameOverStarted = false;
TimerUI.instance.gameOverFinished = false;
NotificationManager.ShowTimerExtendedMessage(num, 3.3f);
TimerUI.instance.SetTimerPing(TimeProperties.timerExtendedPing, 3f);
TimerUI.instance.currentTimeRemaining += num;
((SemiUI)TimerUI.instance).SemiUISpringShakeY(8f, 10f, 1f);
((SemiUI)TimerUI.instance).SemiUISpringScale(0.05f, 5f, 0.8f);
Plugin.loggy.LogDebug((object)$"Adding {num:0.0} to timer");
}
private static void SetTimer(EventData eventData)
{
//IL_0006: Unknown result type (might be due to invalid IL or missing references)
TimerUI.instance.SetTimerPing(TimeProperties.timerNeutralPing, 2f);
TimerUI.instance.timerPaused = (bool)eventData.CustomData;
if (TimerUI.instance.timerPaused)
{
TimerUI.instance.gameOverStarted = false;
TimerUI.instance.gameOverFinished = false;
}
((SemiUI)TimerUI.instance).SemiUISpringShakeY(4f, 10f, 0.5f);
((SemiUI)TimerUI.instance).SemiUISpringScale(0.04f, 5f, 0.4f);
Plugin.loggy.LogDebug((object)$"Setting paused to {TimerUI.instance.timerPaused}");
}
private static void GameOverSeq(EventData eventData)
{
bool flag = (bool)eventData.CustomData;
Plugin.loggy.LogDebug((object)$"Setting game over to {flag}");
if (flag)
{
TimerUI.instance.gameOverStarted = true;
TimerUI.instance.gameOverFinished = false;
TimerUI.instance.gameOverCoroutine = ((MonoBehaviour)TimerUI.instance).StartCoroutine(TimerUI.instance.Death());
return;
}
if (TimerUI.instance.gameOverCoroutine != null)
{
((MonoBehaviour)TimerUI.instance).StopCoroutine(TimerUI.instance.gameOverCoroutine);
}
TimerUI.instance.gameOverStarted = false;
}
private static void RemainingTimeBonus(EventData eventData)
{
int moneyAmount = (int)eventData.CustomData;
((MonoBehaviour)TimerUI.instance).StartCoroutine(RemainingTimeBonusNotification(moneyAmount));
}
private static IEnumerator RemainingTimeBonusNotification(int moneyAmount)
{
yield return (object)new WaitForSeconds(2.5f);
Plugin.loggy.LogDebug((object)$"Adding bonus money - {moneyAmount}");
NotificationManager.ShowBonusMoneyMessage(moneyAmount);
SemiFunc.StatSetRunCurrency(SemiFunc.StatGetRunCurrency() + moneyAmount);
SemiFunc.StatSetRunTotalHaul(SemiFunc.StatGetRunTotalHaul() + moneyAmount);
CurrencyUI.instance.FetchCurrency();
}
}
internal class TimeProperties
{
public static Color timerExtendedPing = new Color(0f, 1f, 0.1f);
public static Color timerLowPing = new Color(0.93f, 0.43f, 0.45f);
public static Color timerNeutralPing = new Color(0.95f, 0.95f, 0.16f);
public static Color timerNormalBase = new Color(0.12f, 0.54f, 0.83f);
public static Color timerLowBase = new Color(0.97f, 0.12f, 0.28f);
public static Color timerPausedBase = new Color(0.62f, 0.65f, 0.68f);
public static int BaseLevelTimer = 0;
public static int BaseLevelTimerPostProcess = 0;
public static int ExtractionActivationBonusTime => RoundTo5(30f * PlayerCountMultiplier);
public static float PlayerCountMultiplier => GameDirector.instance.PlayerList.Count switch
{
1 => 2f,
2 => 1.4f,
3 => 1.25f,
4 => 1.1f,
5 => 1.05f,
6 => 1f,
_ => 0.8f,
};
private static int RoundTo5(float number)
{
return Mathf.RoundToInt(number / 5f) * 5;
}
public static int GetThisLevelBaseTime(int level)
{
if (level <= 10)
{
float number = 300f - 25.25f * Mathf.Pow(Mathf.Sqrt((float)(level - 1)), 1.25f);
return RoundTo5(number);
}
if (level <= 100)
{
float number2 = 200f - 9.84f * Mathf.Pow(Mathf.Sqrt((float)(level - 10)), 1.18f);
return RoundTo5(number2);
}
return 60;
}
public static int GetThisLevelTime(int level)
{
return RoundTo5((float)GetThisLevelBaseTime(level) * DifficultyManager.LevelTimeDifficultyMultiplier * PlayerCountMultiplier);
}
public static int GetThisLevelAddedTime(int level, int totalExtractions, float bonus)
{
float num = 1.1f;
float num2 = Mathf.Clamp(1f / Mathf.Pow(1.33f, (float)(totalExtractions - 1)), 0.25f, 1f);
if (level > 10)
{
num = 1.1f - 0.0504f * Mathf.Pow(Mathf.Sqrt((float)(level - 10)), 1.2f);
}
else if (level > 100)
{
num = 0.35f;
}
float num3 = 1f + 0.5f * Mathf.Sqrt(Mathf.Clamp(bonus, 1f, 10f) - 1f);
int num4 = RoundTo5((float)BaseLevelTimer * num * num2 * num3 * DifficultyManager.ExtractionBonusDifficultyMultiplier * PlayerCountMultiplier);
Plugin.loggy.LogDebug((object)($"EXTRACTION ADDED TIME: {num4} s" + $"\n >> Multiplier: {num:0.00}x" + $"\n >> ExtractMulti: {num2:0.00}x" + $"\n >> SurplusBonus: {num3:0.00}x ({bonus:0.00}x)"));
return num4;
}
public static int GetThisLevelFinalExtractTime(int level)
{
float num = 1f - 0.0375f * Mathf.Pow(Mathf.Sqrt((float)(level - 1)), 1.25f);
if (level > 100)
{
num = 1f / 3f;
}
return RoundTo5((float)BaseLevelTimer * num * DifficultyManager.FinalExtractionDifficultyMultiplier);
}
public static int GetBonusMoneyInThousands(float timeRemaining)
{
if (timeRemaining < 15f)
{
return 0;
}
float num = GetThisLevelTime(RunManager.instance.levelsCompleted + 1);
float num2 = 0f;
int value = Traverse.Create((object)RoundDirector.instance).Field("extractionPoints").GetValue<int>();
for (int i = 0; i < value; i++)
{
num += (float)GetThisLevelAddedTime(RunManager.instance.levelsCompleted + 1, value, 0f) * 0.75f;
}
float num3 = Mathf.Clamp(timeRemaining / (num + num2), 0f, 2f);
int num4 = ((num3 < 0.2f) ? Mathf.CeilToInt(Mathf.Lerp(0f, 5f, num3 / 0.2f)) : ((num3 < 0.5f) ? Mathf.CeilToInt(Mathf.Lerp(5f, 20f, (num3 - 0.2f) / 0.3f)) : ((!(num3 < 1f)) ? Mathf.CeilToInt(Mathf.Lerp(50f, 99f, (num3 - 1f) / 1f)) : Mathf.CeilToInt(Mathf.Lerp(20f, 50f, (num3 - 0.5f) / 0.5f)))));
Plugin.loggy.LogDebug((object)($"BONUS MONEY: ${num4}K for {timeRemaining:0.0} seconds" + $"\n >> MaxTimeOrig: {num:0.0} s" + $"\n >> MaxTimeExt: {num2:0.0} s" + $"\n >> TimeProgress: {num3:0.000}f"));
return Mathf.Clamp(num4, 0, 99);
}
}
internal class TimerUI : SemiUI
{
public static TimerUI instance;
private TextMeshProUGUI text;
public float lastTimerSync = 0f;
public float currentTimeRemaining = 0f;
public int lastTimeRemaining = 0;
public float timerPingLerp = 0f;
public float timerPingLerpMax = 0f;
public bool timerRunning = false;
public bool timerPaused = false;
public bool gameOverStarted = false;
public bool gameOverFinished = false;
private Color pingColor = Color.white;
public Coroutine gameOverCoroutine;
private float DisplayTimer => Mathf.Clamp(currentTimeRemaining, 0f, 5999.999f);
private int IntSeconds => Mathf.CeilToInt(DisplayTimer);
private int IntSecondsFloor => (int)DisplayTimer;
private int DisplayMilliseconds => Mathf.CeilToInt(DisplayTimer * 1000f) % 1000;
private int DisplaySeconds => IntSecondsFloor % 60;
private int DisplayMinutes => IntSecondsFloor / 60;
private bool ShouldCancelGameOver => !timerRunning || timerPaused || currentTimeRemaining > 0f;
protected override void Start()
{
//IL_002e: Unknown result type (might be due to invalid IL or missing references)
//IL_0047: Unknown result type (might be due to invalid IL or missing references)
//IL_004c: Unknown result type (might be due to invalid IL or missing references)
((SemiUI)this).Start();
instance = this;
text = ((Component)this).GetComponent<TextMeshProUGUI>();
((TMP_Text)text).richText = true;
TextMeshProUGUI obj = text;
((TMP_Text)obj).margin = ((TMP_Text)obj).margin + new Vector4(0f, 0f, -500f, 0f);
TextMeshProUGUI obj2 = text;
((TMP_Text)obj2).fontSize = ((TMP_Text)obj2).fontSize * 0.833f;
((SemiUI)this).Hide();
BigMessageUI val = Object.FindObjectOfType<BigMessageUI>();
if (!((Object)(object)val == (Object)null))
{
((TMP_Text)text).spriteAsset = ((TMP_Text)Traverse.Create((object)val).Field("emojiText").GetValue<TextMeshProUGUI>()).spriteAsset;
}
}
protected override void Update()
{
//IL_0034: Unknown result type (might be due to invalid IL or missing references)
//IL_003a: Invalid comparison between Unknown and I4
//IL_005b: Unknown result type (might be due to invalid IL or missing references)
((SemiUI)this).Update();
if (!SemiFunc.RunIsLevel())
{
((SemiUI)this).Hide();
}
bool flag = !SemiFunc.RunIsLevel() || !timerRunning || gameOverFinished || (int)Patches.currentTruckState == 3;
if (flag || Patches.mapToolActive)
{
((SemiUI)this).SemiUIScoot(new Vector2(-220f, 0f));
}
if (!flag)
{
NotificationManager.Update();
if (!timerPaused)
{
currentTimeRemaining = Mathf.Max(0f, currentTimeRemaining - Time.deltaTime);
}
if (Math.Abs(currentTimeRemaining - lastTimerSync) >= 10f)
{
SyncTimeBetweenPlayers(currentTimeRemaining);
}
LowTimerPingLogic();
UILogic();
ColorLogic();
ShowRedEyesLowTimer();
if (currentTimeRemaining == 0f && !timerPaused)
{
SyncTimeBetweenPlayers(0f);
OutOfTime();
}
}
}
private void ColorLogic()
{
//IL_0001: Unknown result type (might be due to invalid IL or missing references)
//IL_0006: Unknown result type (might be due to invalid IL or missing references)
//IL_001b: Unknown result type (might be due to invalid IL or missing references)
//IL_0020: Unknown result type (might be due to invalid IL or missing references)
//IL_0031: Unknown result type (might be due to invalid IL or missing references)
//IL_0033: Unknown result type (might be due to invalid IL or missing references)
//IL_0045: Unknown result type (might be due to invalid IL or missing references)
//IL_004a: Unknown result type (might be due to invalid IL or missing references)
//IL_0073: Unknown result type (might be due to invalid IL or missing references)
//IL_002b: 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)
Color val = TimeProperties.timerNormalBase;
if (currentTimeRemaining <= 60f)
{
val = TimeProperties.timerLowBase;
}
if (timerPaused)
{
val = TimeProperties.timerPausedBase;
}
Color color = Color.Lerp(val, pingColor, timerPingLerp / timerPingLerpMax);
timerPingLerp = Mathf.Clamp(timerPingLerp - Time.deltaTime, 0f, timerPingLerpMax);
((Graphic)text).color = color;
}
private void UILogic()
{
string text = "<size=120%><sprite name=clock></size>";
string text2 = $"{DisplayMinutes:00}";
string text3 = $"{DisplaySeconds:00}";
string text4 = $"{DisplayMilliseconds:000}";
((TMP_Text)this.text).text = text + " <b>" + text2 + ":" + text3 + "." + text4 + "</b>";
}
private void LowTimerPingLogic()
{
//IL_0025: Unknown result type (might be due to invalid IL or missing references)
if (IntSeconds <= 60 && lastTimeRemaining != IntSeconds)
{
SetTimerPing(TimeProperties.timerLowPing, 0.8f);
if (IntSeconds <= 30)
{
((SemiUI)this).SemiUISpringShakeY(4f, 12f, 0.4f);
((SemiUI)this).SemiUISpringScale(0.03f, 8f, 0.3f);
}
}
lastTimeRemaining = IntSeconds;
}
public void SetTimerPing(Color color, float time)
{
//IL_0010: Unknown result type (might be due to invalid IL or missing references)
//IL_0011: Unknown result type (might be due to invalid IL or missing references)
timerPingLerp = time;
timerPingLerpMax = time;
pingColor = color;
}
private void SyncTimeBetweenPlayers(float seconds)
{
//IL_001f: Unknown result type (might be due to invalid IL or missing references)
if (SemiFunc.IsMasterClientOrSingleplayer())
{
Syncing.TimerSync.RaiseEvent((object)seconds, NetworkingEvents.RaiseOthers, SendOptions.SendReliable);
currentTimeRemaining = seconds;
lastTimerSync = seconds;
}
}
public void SetTimer(float seconds, bool pause = false)
{
//IL_001f: Unknown result type (might be due to invalid IL or missing references)
if (SemiFunc.IsMasterClientOrSingleplayer())
{
Syncing.TimerSet.RaiseEvent((object)pause, NetworkingEvents.RaiseAll, SendOptions.SendReliable);
SyncTimeBetweenPlayers(seconds);
}
}
public void AddTimeToTimer(float seconds)
{
//IL_001f: Unknown result type (might be due to invalid IL or missing references)
if (SemiFunc.IsMasterClientOrSingleplayer())
{
Syncing.TimerAdd.RaiseEvent((object)seconds, NetworkingEvents.RaiseAll, SendOptions.SendReliable);
}
}
public void GenerateBonusMoney(float secondsRemaining)
{
//IL_0041: Unknown result type (might be due to invalid IL or missing references)
if (SemiFunc.IsMasterClientOrSingleplayer())
{
int bonusMoneyInThousands = TimeProperties.GetBonusMoneyInThousands(secondsRemaining);
int num = Mathf.CeilToInt((float)bonusMoneyInThousands * DifficultyManager.RemainingTimeBonusMultiplier);
if (num > 0)
{
Syncing.BonusMoney.RaiseEvent((object)num, NetworkingEvents.RaiseAll, SendOptions.SendReliable);
}
}
}
public void ShowRedEyesLowTimer()
{
if (currentTimeRemaining > 15f || !SemiFunc.FPSImpulse5() || timerPaused)
{
return;
}
foreach (PlayerAvatar player in GameDirector.instance.PlayerList)
{
if (!Traverse.Create((object)player).Field("isDisabled").GetValue<bool>())
{
if (Patches.TruckLeaving && Traverse.Create((object)player.RoomVolumeCheck).Field("inTruck").GetValue<bool>())
{
Plugin.loggy.LogDebug((object)"a player is inside truck while its starting, skipping them for red eyes...");
}
else
{
player.playerHealth.EyeMaterialOverride((EyeOverrideState)1, 1f, 2);
}
}
}
Plugin.loggy.LogDebug((object)"Setting red eyes...");
}
private void OutOfTime()
{
//IL_0037: Unknown result type (might be due to invalid IL or missing references)
if (SemiFunc.IsMasterClientOrSingleplayer() && !gameOverStarted)
{
Plugin.loggy.LogDebug((object)"Time's up!");
Syncing.GameOverSequence.RaiseEvent((object)true, NetworkingEvents.RaiseAll, SendOptions.SendReliable);
}
}
private bool CancelGameOver()
{
//IL_0034: Unknown result type (might be due to invalid IL or missing references)
if (!ShouldCancelGameOver || !SemiFunc.IsMasterClientOrSingleplayer() || !gameOverStarted)
{
return false;
}
Syncing.GameOverSequence.RaiseEvent((object)false, NetworkingEvents.RaiseAll, SendOptions.SendReliable);
return true;
}
public IEnumerator Death()
{
if (CancelGameOver())
{
yield break;
}
yield return (object)new WaitForSeconds(0.5f);
if (CancelGameOver())
{
yield break;
}
Plugin.loggy.LogDebug((object)"Killing self...");
PlayerAvatar localPlayer = null;
foreach (PlayerAvatar avatar in GameDirector.instance.PlayerList)
{
if (SemiFunc.IsMultiplayer() && !avatar.photonView.IsMine)
{
continue;
}
localPlayer = avatar;
break;
}
if ((Object)(object)localPlayer == (Object)null)
{
gameOverStarted = false;
gameOverFinished = true;
}
else if (Traverse.Create((object)localPlayer).Field("isDisabled").GetValue<bool>())
{
gameOverStarted = false;
gameOverFinished = true;
}
else if (Patches.TruckLeaving && Traverse.Create((object)localPlayer.RoomVolumeCheck).Field("inTruck").GetValue<bool>())
{
Plugin.loggy.LogDebug((object)"a player is inside truck while its starting, skipping them for death...");
gameOverStarted = false;
gameOverFinished = true;
}
else
{
localPlayer.PlayerDeath(-1);
gameOverStarted = false;
gameOverFinished = true;
}
}
}