using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Runtime.Versioning;
using BepInEx;
using HarmonyLib;
using Microsoft.CodeAnalysis;
using Photon.Pun;
using Photon.Realtime;
using TMPro;
using UnityEngine;
using UnityEngine.UI;
[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: AssemblyTitle("TimerMod")]
[assembly: AssemblyDescription("")]
[assembly: AssemblyConfiguration("")]
[assembly: AssemblyCompany("")]
[assembly: AssemblyProduct("TimerMod")]
[assembly: AssemblyCopyright("Copyright © 2025")]
[assembly: AssemblyTrademark("")]
[assembly: ComVisible(false)]
[assembly: Guid("46c2292a-1cf5-4b1d-a029-feaa0871ed7a")]
[assembly: AssemblyFileVersion("1.0.0.0")]
[assembly: TargetFramework(".NETFramework,Version=v4.8", FrameworkDisplayName = ".NET Framework 4.8")]
[assembly: AssemblyVersion("1.0.0.0")]
namespace TimerMod
{
[BepInPlugin("Choom.repo.timermod", "Timer Mod", "0.9.7")]
public class TimerMod : BaseUnityPlugin
{
private void Awake()
{
//IL_0015: Unknown result type (might be due to invalid IL or missing references)
((BaseUnityPlugin)this).Logger.LogInfo((object)"TimerMod initialized!");
new Harmony("Choom.repo.timermod").PatchAll(typeof(TimerPatch));
((BaseUnityPlugin)this).Logger.LogInfo((object)"Harmony patches applied to TimerPatch!");
}
}
[HarmonyPatch]
public static class TimerPatch
{
private static float timer = 600f;
private static bool timerActive = false;
private static float lastTime;
private static float updateInterval = 1f;
private static float timeSinceLastUpdate;
private static TextMeshProUGUI timerText;
private static GameObject canvObj;
private static string currentLevelName = "";
private static bool hasResetForNonPlayable = false;
private static List<PlayerAvatar> playerAvatars = new List<PlayerAvatar>();
private static List<(PlayerAvatar avatar, float delay, string message)> pendingExplosions = new List<(PlayerAvatar, float, string)>();
private static bool explosionsScheduled = false;
private static readonly List<string> startMessages = new List<string> { "The countdown has started! 10 minutes left!", "Timer’s on! We’ve got 10 minutes to live!", "Here we go—10 minutes on the clock!", "The timer’s ticking! 10 minutes, people!", "Ten minutes until doom—let’s move!", "Clock’s running—10 minutes to survive!", "It’s begun! 10 minutes before we blow!", "Timer activated! 10 minutes, no pressure!", "We’re on the clock—10 minutes left!", "Countdown’s live! 10 minutes to go!" };
private static readonly List<string> addTimeMessages = new List<string> { "Five more minutes! Let’s hustle!", "Added 5 minutes—keep moving!", "We’ve got 5 extra minutes—go!", "Five minutes added! Don’t slack!", "More time! 5 minutes to breathe!", "Timer bumped—5 minutes more!", "Five minutes gained—use it wisely!", "Extra 5 minutes—let’s not waste it!", "Time extended! 5 minutes added!", "Five more minutes on the clock—hurry!" };
private static readonly List<string> twoMinuteWarnings = new List<string> { "Two minutes left! Hurry up!", "Only 2 minutes—things are getting tense!", "Two minutes to go—move it!", "We’re down to 2 minutes—panic time!", "Two minutes remaining—oh no!", "Clock’s at 2 minutes—speed up!", "Two minutes until boom—help!", "Just 2 minutes left—run!", "Two minutes on the timer—yikes!", "We’ve got 2 minutes—do something!" };
private static readonly List<string> thirtySecondWarnings = new List<string> { "30 seconds! I’m doomed!", "Half a minute left—I’m sweating!", "30 seconds to live—oh man!", "Thirty seconds—say your prayers!", "30 seconds on the clock—help me!", "Half a minute until kaboom!", "30 seconds left—I’m toast!", "Thirty seconds—this is it!", "30 seconds remaining—goodbye!", "Half a minute—I can’t make it!" };
private static readonly List<string> deathMessages = new List<string>
{
"I’m about to explode!", "This is the end—BOOM!", "Timer got me—goodbye!", "I’m blowing up—argh!", "No time left—kaboom!", "It’s over—I’m done!", "Exploding now—see ya!", "The clock won—blast!", "I’m a goner—boom!", "Time’s up—farewell!",
"Bye Bye world!", "I’m toast—kaboom!", "I’m exploding—help!"
};
private static string GetCurrentLevelName()
{
if ((Object)(object)RunManager.instance == (Object)null)
{
return "MainMenu";
}
FieldInfo field = typeof(RunManager).GetField("levelCurrent", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
if (field != null)
{
object value = field.GetValue(RunManager.instance);
if (value != null)
{
PropertyInfo property = value.GetType().GetProperty("name", BindingFlags.Instance | BindingFlags.Public);
if (property != null)
{
return (property.GetValue(value) as string) ?? "MainMenu";
}
}
}
return "MainMenu";
}
public static bool IsPlayableLevel(string levelName)
{
if (string.IsNullOrEmpty(levelName) || levelName == "MainMenu" || levelName.Contains("Menu"))
{
return false;
}
if (levelName.Contains("Shop") || levelName.Contains("Arena") || levelName.Contains("LobbyMenu") || levelName.Contains("Lobby Menu"))
{
return false;
}
return levelName.StartsWith("Level -");
}
[HarmonyPatch(typeof(RunManager), "ChangeLevel")]
[HarmonyPostfix]
public static void ChangeLevelPostfix(RunManager __instance)
{
string text = GetCurrentLevelName();
Debug.Log((object)$"ChangeLevel detected, current level: {text}, timerActive before reset: {timerActive}");
if (IsPlayableLevel(text) && text != currentLevelName)
{
if ((Object)(object)canvObj != (Object)null)
{
Object.Destroy((Object)(object)canvObj);
}
canvObj = null;
timerText = null;
timerActive = false;
timer = 600f;
currentLevelName = text;
hasResetForNonPlayable = false;
pendingExplosions.Clear();
explosionsScheduled = false;
Debug.Log((object)$"UI and timer reset for: {currentLevelName}, timerActive after reset: {timerActive}");
}
else if (!IsPlayableLevel(text) && !hasResetForNonPlayable)
{
if ((Object)(object)canvObj != (Object)null)
{
Object.Destroy((Object)(object)canvObj);
}
canvObj = null;
timerText = null;
timerActive = false;
currentLevelName = text;
hasResetForNonPlayable = true;
pendingExplosions.Clear();
explosionsScheduled = false;
Debug.Log((object)("UI and timer reset for non-playable level: " + currentLevelName + ", waiting for playable level."));
}
else if (!IsPlayableLevel(text) && hasResetForNonPlayable)
{
Debug.Log((object)("Already reset for non-playable level: " + currentLevelName + ", skipping."));
}
}
private static void TryInitializeUI()
{
//IL_0050: Unknown result type (might be due to invalid IL or missing references)
//IL_005a: Expected O, but got Unknown
//IL_00b8: Unknown result type (might be due to invalid IL or missing references)
//IL_00be: Expected O, but got Unknown
//IL_0102: Unknown result type (might be due to invalid IL or missing references)
//IL_0138: Unknown result type (might be due to invalid IL or missing references)
//IL_014d: Unknown result type (might be due to invalid IL or missing references)
//IL_0162: Unknown result type (might be due to invalid IL or missing references)
//IL_0177: Unknown result type (might be due to invalid IL or missing references)
//IL_018c: Unknown result type (might be due to invalid IL or missing references)
//IL_01b1: Unknown result type (might be due to invalid IL or missing references)
if (IsPlayableLevel(GetCurrentLevelName()) && !((Object)(object)RunManager.instance == (Object)null) && (!((Object)(object)canvObj != (Object)null) || !((Object)(object)timerText != (Object)null) || !canvObj.activeSelf))
{
Debug.Log((object)"Initializing UI for playable level...");
canvObj = new GameObject("TimerCanvas");
Canvas val = canvObj.AddComponent<Canvas>();
val.renderMode = (RenderMode)0;
val.sortingOrder = 100;
canvObj.AddComponent<CanvasScaler>();
canvObj.SetActive(true);
Debug.Log((object)$"Canvas created: active = {canvObj.activeSelf}, sortingOrder = {val.sortingOrder}");
GameObject val2 = new GameObject("TimerText");
val2.transform.SetParent(canvObj.transform, false);
timerText = val2.AddComponent<TextMeshProUGUI>();
((TMP_Text)timerText).text = "10:00";
((TMP_Text)timerText).fontSize = 36f;
((Graphic)timerText).color = Color.white;
((TMP_Text)timerText).alignment = (TextAlignmentOptions)514;
val2.SetActive(true);
RectTransform component = ((Component)timerText).GetComponent<RectTransform>();
component.anchorMin = new Vector2(0.5f, 1f);
component.anchorMax = new Vector2(0.5f, 1f);
component.pivot = new Vector2(0.5f, 1f);
component.anchoredPosition = new Vector2(0f, -100f);
component.sizeDelta = new Vector2(200f, 50f);
Debug.Log((object)$"Text created: active = {val2.activeSelf}, text = {((TMP_Text)timerText).text}, Position: {component.anchoredPosition}");
ExtractionPoint val3 = Object.FindObjectOfType<ExtractionPoint>();
if ((Object)(object)val3 != (Object)null && (Object)(object)val3.haulGoalScreen != (Object)null)
{
((TMP_Text)timerText).font = ((TMP_Text)val3.haulGoalScreen).font;
Debug.Log((object)"Game font applied!");
}
else
{
Debug.Log((object)"Default font used, ExtractionPoint not found.");
}
}
}
[HarmonyPatch(typeof(ExtractionPoint), "ButtonPress")]
[HarmonyPostfix]
public static void ButtonPressPostfix()
{
string text = GetCurrentLevelName();
Debug.Log((object)$"ButtonPress detected, current level: {text}, timerActive: {timerActive}");
if (!IsPlayableLevel(text))
{
Debug.Log((object)("ButtonPress ignored in non-playable level: " + text));
return;
}
if ((Object)(object)canvObj == (Object)null)
{
TryInitializeUI();
}
if (!timerActive)
{
timer = 600f;
timerActive = true;
lastTime = Time.realtimeSinceStartup - updateInterval;
timeSinceLastUpdate = 0f;
Debug.Log((object)"Timer started via ButtonPress (10:00)!");
if (PhotonNetwork.IsMasterClient)
{
UpdatePlayerList();
SendRandomMessageToAll(startMessages);
}
}
else
{
timer += 300f;
Debug.Log((object)"+ 5 minutes added to the timer!");
if (PhotonNetwork.IsMasterClient)
{
SendRandomMessageToAll(addTimeMessages);
}
}
UpdateTimerText();
}
[HarmonyPatch(typeof(GameDirector), "Update")]
[HarmonyPostfix]
public static void UpdatePostfix()
{
if (!IsPlayableLevel(GetCurrentLevelName()) || (Object)(object)RunManager.instance == (Object)null)
{
if ((Object)(object)canvObj != (Object)null)
{
Object.Destroy((Object)(object)canvObj);
canvObj = null;
timerText = null;
}
timerActive = false;
}
else
{
if (!timerActive && pendingExplosions.Count == 0)
{
return;
}
float realtimeSinceStartup = Time.realtimeSinceStartup;
float num = realtimeSinceStartup - lastTime;
lastTime = realtimeSinceStartup;
if (timerActive)
{
timer -= num;
timeSinceLastUpdate += num;
if (timeSinceLastUpdate >= updateInterval)
{
UpdateTimerText();
timeSinceLastUpdate = 0f;
if (PhotonNetwork.IsMasterClient)
{
if (timer <= 120f && timer > 119f)
{
SendRandomMessageToAll(twoMinuteWarnings);
}
else if (timer <= 30f && timer > 29f)
{
SendRandomMessageToAll(thirtySecondWarnings);
}
else if (timer <= 0f && !explosionsScheduled)
{
ScheduleExplosions();
explosionsScheduled = true;
}
}
}
}
ProcessPendingExplosions(num);
}
}
public static void ScheduleExplosions()
{
foreach (PlayerAvatar playerAvatar in playerAvatars)
{
if ((Object)(object)playerAvatar != (Object)null && (Object)(object)playerAvatar.playerHealth != (Object)null && IsPlayerAlive(playerAvatar))
{
string text = deathMessages[Random.Range(0, deathMessages.Count)];
playerAvatar.ChatMessageSend(text, false);
float num = (float)text.Length * 0.25f;
pendingExplosions.Add((playerAvatar, num, text));
Debug.Log((object)$"Scheduled explosion for {((Object)playerAvatar).name} in {num}s: '{text}'");
}
}
timerActive = false;
}
public static void ProcessPendingExplosions(float deltaTime)
{
//IL_0067: Unknown result type (might be due to invalid IL or missing references)
for (int num = pendingExplosions.Count - 1; num >= 0; num--)
{
(PlayerAvatar avatar, float delay, string message) tuple = pendingExplosions[num];
PlayerAvatar item = tuple.avatar;
float item2 = tuple.delay;
string item3 = tuple.message;
item2 -= deltaTime;
if (item2 <= 0f)
{
if ((Object)(object)item != (Object)null && (Object)(object)item.playerHealth != (Object)null && IsPlayerAlive(item))
{
item.playerHealth.HurtOther(1000, Vector3.zero, false, -1);
Debug.Log((object)("Player " + ((Object)item).name + " exploded after saying: '" + item3 + "'!"));
}
pendingExplosions.RemoveAt(num);
}
else
{
pendingExplosions[num] = (item, item2, item3);
}
}
}
public static bool IsPlayerAlive(PlayerAvatar avatar)
{
if ((Object)(object)avatar == (Object)null || (Object)(object)avatar.playerHealth == (Object)null)
{
return false;
}
FieldInfo field = ((object)avatar.playerHealth).GetType().GetField("health", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
if (field != null)
{
object value = field.GetValue(avatar.playerHealth);
if (value is int num)
{
return num > 0;
}
Debug.LogWarning((object)("Unexpected health type for " + ((Object)avatar).name + ": " + value.GetType().Name + ", assuming alive."));
return true;
}
return true;
}
public static void UpdatePlayerList()
{
playerAvatars.Clear();
Player[] playerList = PhotonNetwork.PlayerList;
foreach (Player val in playerList)
{
PlayerAvatar val2 = FindPlayerAvatar(val);
if ((Object)(object)val2 != (Object)null)
{
playerAvatars.Add(val2);
Debug.Log((object)("ℹ\ufe0fPlayer added to list: " + val.NickName));
}
}
}
public static void SendRandomMessageToAll(List<string> messageList)
{
List<string> list = messageList.OrderBy((string x) => Guid.NewGuid()).ToList();
int num = 0;
foreach (PlayerAvatar playerAvatar in playerAvatars)
{
if ((Object)(object)playerAvatar != (Object)null && IsPlayerAlive(playerAvatar))
{
string text = list[num % list.Count];
playerAvatar.ChatMessageSend(text, false);
num++;
}
}
}
public static PlayerAvatar FindPlayerAvatar(Player player)
{
PlayerAvatar[] array = Object.FindObjectsOfType<PlayerAvatar>();
foreach (PlayerAvatar val in array)
{
PhotonView component = ((Component)val).GetComponent<PhotonView>();
if ((Object)(object)component != (Object)null && component.Owner == player)
{
return val;
}
}
Debug.LogWarning((object)("⚠\ufe0f PlayerAvatar not found for " + player.NickName));
return null;
}
public static void UpdateTimerText()
{
//IL_009e: 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_00c9: Unknown result type (might be due to invalid IL or missing references)
if ((Object)(object)timerText == (Object)null || !canvObj.activeSelf)
{
Debug.Log((object)"⚠\ufe0f timerText or Canvas lost, reinitializing...");
TryInitializeUI();
}
int num = Mathf.FloorToInt(Mathf.Max(timer, 0f) / 60f);
int num2 = Mathf.FloorToInt(Mathf.Max(timer, 0f) % 60f);
string text = $"{num:00}:{num2:00}";
if ((Object)(object)timerText != (Object)null)
{
((TMP_Text)timerText).text = text;
if (timer <= 60f)
{
((Graphic)timerText).color = Color.red;
}
else if (timer <= 120f)
{
((Graphic)timerText).color = new Color(1f, 0.5f, 0f);
}
else
{
((Graphic)timerText).color = Color.white;
}
}
}
}
}
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 Microsoft.CodeAnalysis
{
[CompilerGenerated]
[Microsoft.CodeAnalysis.Embedded]
internal sealed class EmbeddedAttribute : Attribute
{
}
}