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.InteropServices; using System.Runtime.Versioning; using BepInEx; using BepInEx.Configuration; using BepInEx.Logging; using ExitGames.Client.Photon; using HarmonyLib; using Photon.Pun; using Photon.Realtime; using PhotonCustomPropsUtils; using UnityEngine; using UnityEngine.SceneManagement; using Zorro.Core; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.Default | DebuggableAttribute.DebuggingModes.DisableOptimizations | DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints | DebuggableAttribute.DebuggingModes.EnableEditAndContinue)] [assembly: AssemblyTitle("Hikers Hunger")] [assembly: AssemblyDescription("")] [assembly: AssemblyConfiguration("")] [assembly: AssemblyCompany("")] [assembly: AssemblyProduct("Hikers Hunger")] [assembly: AssemblyCopyright("Copyright © 2025")] [assembly: AssemblyTrademark("")] [assembly: ComVisible(false)] [assembly: Guid("99b406ad-6d25-4637-9908-9ab308ceb06e")] [assembly: AssemblyFileVersion("1.0.0.0")] [assembly: TargetFramework(".NETFramework,Version=v4.7.2", FrameworkDisplayName = ".NET Framework 4.7.2")] [assembly: AssemblyVersion("1.0.0.0")] namespace HikersHunger; [BepInDependency(/*Could not decode attribute arguments.*/)] [BepInPlugin("tony4twentys.Hikers_Hunger", "Hikers Hunger", "2.0.0")] public class HikersHungerPlugin : BaseUnityPlugin, IInRoomCallbacks, IMatchmakingCallbacks, IOnEventCallback { [Serializable] public struct HostCfg { public bool manualCannibalSelection; public int initialSelectionDelay; public int postSelectionDelay; public bool enableCustomEatTime; public float customEatTime; public bool constantCannibal; public float safeZoneDelay; } [HarmonyPatch(typeof(Action_RestoreHunger), "RunAction")] public class RestoreHungerPatch { private static bool Prefix(Action_RestoreHunger __instance) { if ((Object)(object)((Component)__instance).GetComponent() != (Object)null) { int cannibalViewID = Instance.cannibalViewID; Character localCharacter = Character.localCharacter; int? obj; if (localCharacter == null) { obj = null; } else { PhotonView photonView = ((MonoBehaviourPun)localCharacter).photonView; obj = ((photonView != null) ? new int?(photonView.ViewID) : null); } if (cannibalViewID == obj) { return true; } } else { int cannibalViewID2 = Instance.cannibalViewID; Character localCharacter2 = Character.localCharacter; int? obj2; if (localCharacter2 == null) { obj2 = null; } else { PhotonView photonView2 = ((MonoBehaviourPun)localCharacter2).photonView; obj2 = ((photonView2 != null) ? new int?(photonView2.ViewID) : null); } if (cannibalViewID2 == obj2) { return false; } } return true; } } [HarmonyPatch(typeof(RunManager), "StartRun")] public class StartRunPatch { private static void Postfix() { if (!((Object)(object)Singleton.Instance == (Object)null)) { ((BaseUnityPlugin)Instance).Logger.LogInfo((object)"[HikersHunger] Run started!"); ((MonoBehaviour)Instance).StartCoroutine(Instance.SelectRandomCannibalAfterDelay()); } } } [HarmonyPatch(typeof(Campfire), "Light_Rpc")] public class CampfireLight_RpcPatch { private static void Postfix(Campfire __instance) { Instance.fireCount++; ((BaseUnityPlugin)Instance).Logger.LogInfo((object)$"[HikersHunger] Campfire lit! Total fires: {Instance.fireCount}"); foreach (Character allCharacter in Character.AllCharacters) { if (!allCharacter.isBot && !allCharacter.data.dead && (Object)(object)allCharacter.refs?.customization != (Object)null && allCharacter.IsLocal) { allCharacter.refs.customization.BecomeHuman(); } } ((BaseUnityPlugin)Instance).Logger.LogInfo((object)"[HikersHunger] All players restored to human appearance"); Instance.ForceRefreshSafeZoneStatus(); Instance.SelectNewCannibalAtCampfire(); } } [HarmonyPatch(typeof(CharacterInteractible), "GetInteractTime")] public class CharacterInteractible_GetInteractTime_Patch { private static void Postfix(CharacterInteractible __instance, Character interactor, ref float __result) { if (Synced.enableCustomEatTime && Instance.cannibalViewID == ((MonoBehaviourPun)interactor).photonView.ViewID) { if (IsCharacterNearCampfire(interactor)) { __result = 999f; } else if (Instance.inSafeZoneDelay) { ((BaseUnityPlugin)Instance).Logger.LogDebug((object)"[HikersHunger] Blocking eating during safe zone delay period"); __result = 999f; } else { __result = Synced.customEatTime; } } } } [HarmonyPatch(typeof(CharacterInteractible), "GetEaten")] public class CharacterInteractible_GetEaten_Patch { private static bool Prefix(CharacterInteractible __instance, Character eater) { if (Instance.cannibalViewID == ((MonoBehaviourPun)eater).photonView.ViewID) { if (IsCharacterNearCampfire(eater)) { ((BaseUnityPlugin)Instance).Logger.LogWarning((object)"[HikersHunger] BLOCKED: Cannibal attempted to eat player in safe zone!"); return false; } if (Instance.inSafeZoneDelay) { ((BaseUnityPlugin)Instance).Logger.LogWarning((object)"[HikersHunger] BLOCKED: Cannibal attempted to eat player during safe zone delay!"); ((BaseUnityPlugin)Instance).Logger.LogDebug((object)$"[HikersHunger] Safe zone delay state: inSafeZoneDelay={Instance.inSafeZoneDelay}, inSafeZone={Instance.inSafeZone}"); return false; } } return true; } private static void Postfix(CharacterInteractible __instance, Character eater) { if (Instance.cannibalViewID == ((MonoBehaviourPun)eater).photonView.ViewID) { eater.refs.afflictions.SetStatus((STATUSTYPE)5, 0f); ((BaseUnityPlugin)Instance).Logger.LogInfo((object)"[HikersHunger] Removed curse from cannibal after eating player"); } } } [HarmonyPatch(typeof(CharacterInteractible), "IsConstantlyInteractable")] public class CharacterInteractible_IsConstantlyInteractable_Patch { private static bool Postfix(bool __result, CharacterInteractible __instance, Character interactor) { if (__result && Instance.cannibalViewID == ((MonoBehaviourPun)interactor).photonView.ViewID) { if (IsCharacterNearCampfire(interactor)) { return false; } if (Instance.inSafeZoneDelay) { ((BaseUnityPlugin)Instance).Logger.LogDebug((object)"[HikersHunger] Blocking interaction during safe zone delay period"); return false; } } return __result; } } [CompilerGenerated] private sealed class d__74 : IEnumerator, IDisposable, IEnumerator { private int <>1__state; private object <>2__current; public int viewID; public HikersHungerPlugin <>4__this; private float 5__1; private float 5__2; private Exception 5__3; object IEnumerator.Current { [DebuggerHidden] get { return <>2__current; } } object IEnumerator.Current { [DebuggerHidden] get { return <>2__current; } } [DebuggerHidden] public d__74(int <>1__state) { this.<>1__state = <>1__state; } [DebuggerHidden] void IDisposable.Dispose() { 5__3 = null; <>1__state = -2; } private bool MoveNext() { //IL_006b: Unknown result type (might be due to invalid IL or missing references) //IL_0075: Expected O, but got Unknown switch (<>1__state) { default: return false; case 0: <>1__state = -1; 5__1 = Synced.postSelectionDelay; 5__2 = 0f; ((BaseUnityPlugin)<>4__this).Logger.LogInfo((object)$"[HikersHunger] Waiting {5__1} seconds before granting powers..."); break; case 1: <>1__state = -1; 5__2 += 1f; break; } if (5__2 < 5__1) { <>2__current = (object)new WaitForSeconds(1f); <>1__state = 1; return true; } try { <>4__this.manager.SetRoomProperty("cannibalViewId", (object)viewID); ((BaseUnityPlugin)<>4__this).Logger.LogInfo((object)"[HikersHunger] Cannibal powers now active!"); } catch (Exception ex) { 5__3 = ex; ((BaseUnityPlugin)<>4__this).Logger.LogError((object)("[HikersHunger] Failed to set room property: " + 5__3.Message)); } 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(); } } [CompilerGenerated] private sealed class d__37 : IEnumerator, IDisposable, IEnumerator { private int <>1__state; private object <>2__current; public HikersHungerPlugin <>4__this; private float 5__1; private float 5__2; private Exception 5__3; object IEnumerator.Current { [DebuggerHidden] get { return <>2__current; } } object IEnumerator.Current { [DebuggerHidden] get { return <>2__current; } } [DebuggerHidden] public d__37(int <>1__state) { this.<>1__state = <>1__state; } [DebuggerHidden] void IDisposable.Dispose() { 5__3 = null; <>1__state = -2; } private bool MoveNext() { //IL_006d: Unknown result type (might be due to invalid IL or missing references) //IL_0077: Expected O, but got Unknown switch (<>1__state) { default: return false; case 0: <>1__state = -1; 5__1 = Synced.safeZoneDelay; 5__2 = 0f; ((BaseUnityPlugin)<>4__this).Logger.LogInfo((object)$"[HikersHunger] Starting {5__1}s safe zone delay before restoring powers..."); break; case 1: <>1__state = -1; 5__2 += 0.5f; if (IsCharacterNearCampfire(Character.localCharacter)) { <>4__this.inSafeZone = true; ((BaseUnityPlugin)<>4__this).Logger.LogInfo((object)"[HikersHunger] Re-entered safe zone during delay - powers remain revoked"); return false; } if (Mathf.FloorToInt(5__2) % 5 == 0) { ((BaseUnityPlugin)<>4__this).Logger.LogInfo((object)$"[HikersHunger] Safe zone delay: {5__2:F1}s / {5__1}s remaining"); } break; } if (5__2 < 5__1 && !<>4__this.inSafeZone) { <>2__current = (object)new WaitForSeconds(0.5f); <>1__state = 1; return true; } if (!<>4__this.inSafeZone && <>4__this.IsLocalPlayerCannibal()) { <>4__this.inSafeZoneDelay = false; ((BaseUnityPlugin)<>4__this).Logger.LogInfo((object)"[HikersHunger] Safe zone delay completed - powers restored"); if (<>4__this.cannibalViewID != -1) { int cannibalViewID = <>4__this.cannibalViewID; Character localCharacter = Character.localCharacter; int? obj; if (localCharacter == null) { obj = null; } else { PhotonView photonView = ((MonoBehaviourPun)localCharacter).photonView; obj = ((photonView != null) ? new int?(photonView.ViewID) : null); } if (cannibalViewID == obj) { ((BaseUnityPlugin)<>4__this).Logger.LogInfo((object)$"[HikersHunger] Cannibal was reselected during delay (ViewID: {<>4__this.cannibalViewID}) - updating room property"); try { <>4__this.manager.SetRoomProperty("cannibalViewId", (object)<>4__this.cannibalViewID); ((BaseUnityPlugin)<>4__this).Logger.LogInfo((object)"[HikersHunger] Room property updated after safe zone delay completion"); } catch (Exception ex) { 5__3 = ex; ((BaseUnityPlugin)<>4__this).Logger.LogError((object)("[HikersHunger] Failed to update room property after delay: " + 5__3.Message)); } goto IL_02a8; } } ((BaseUnityPlugin)<>4__this).Logger.LogInfo((object)"[HikersHunger] No room property update needed - cannibal not reselected during delay"); goto IL_02a8; } goto IL_02b5; IL_02a8: <>4__this.GrantCannibalPowers(); goto IL_02b5; IL_02b5: 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(); } } [CompilerGenerated] private sealed class d__38 : IEnumerator, IDisposable, IEnumerator { private int <>1__state; private object <>2__current; public HikersHungerPlugin <>4__this; object IEnumerator.Current { [DebuggerHidden] get { return <>2__current; } } object IEnumerator.Current { [DebuggerHidden] get { return <>2__current; } } [DebuggerHidden] public d__38(int <>1__state) { this.<>1__state = <>1__state; } [DebuggerHidden] void IDisposable.Dispose() { <>1__state = -2; } private bool MoveNext() { //IL_0035: Unknown result type (might be due to invalid IL or missing references) //IL_003f: Expected O, but got Unknown switch (<>1__state) { default: return false; case 0: <>1__state = -1; break; case 1: <>1__state = -1; break; } <>4__this.CheckAllPlayersSafeZoneStatus(); <>2__current = (object)new WaitForSeconds(1f); <>1__state = 1; return true; } bool IEnumerator.MoveNext() { //ILSpy generated this explicit interface implementation from .override directive in MoveNext return this.MoveNext(); } [DebuggerHidden] void IEnumerator.Reset() { throw new NotSupportedException(); } } [CompilerGenerated] private sealed class d__75 : IEnumerator, IDisposable, IEnumerator { private int <>1__state; private object <>2__current; public HikersHungerPlugin <>4__this; private float 5__1; private float 5__2; object IEnumerator.Current { [DebuggerHidden] get { return <>2__current; } } object IEnumerator.Current { [DebuggerHidden] get { return <>2__current; } } [DebuggerHidden] public d__75(int <>1__state) { this.<>1__state = <>1__state; } [DebuggerHidden] void IDisposable.Dispose() { <>1__state = -2; } private bool MoveNext() { //IL_006b: Unknown result type (might be due to invalid IL or missing references) //IL_0075: Expected O, but got Unknown switch (<>1__state) { default: return false; case 0: <>1__state = -1; 5__1 = Synced.initialSelectionDelay; 5__2 = 0f; ((BaseUnityPlugin)<>4__this).Logger.LogInfo((object)$"[HikersHunger] Waiting {5__1} seconds before selecting cannibal..."); break; case 1: <>1__state = -1; 5__2 += 1f; break; } if (5__2 < 5__1) { <>2__current = (object)new WaitForSeconds(1f); <>1__state = 1; return true; } if (!PhotonNetwork.IsMasterClient) { return false; } if (Synced.manualCannibalSelection) { <>4__this.isWaitingForCannibalSelection = true; <>4__this.showCannibalSelectionGUI = true; } else { <>4__this.SelectRandomCannibal(); } 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(); } } private const string ROOM_CFG_KEY = "HIKERS_HUNGER_CFG_V1"; private const float CAMPFIRE_SAFE_RADIUS = 15f; private const float DEFAULT_SAFE_ZONE_DELAY = 30f; public static HikersHungerPlugin Instance; private int cannibalViewID = -1; private int fireCount = 0; private bool showCannibalSelectionGUI = false; private bool isWaitingForCannibalSelection = false; private bool cannibalPowersActive = false; private float safeZoneExitTime = 0f; private bool inSafeZone = false; private bool inSafeZoneDelay = false; private ConfigEntry manualCannibalSelection; private ConfigEntry initialSelectionDelay; private ConfigEntry postSelectionDelay; private ConfigEntry enableCustomEatTime; private ConfigEntry customEatTime; private ConfigEntry constantCannibal; private ConfigEntry safeZoneDelay; public static HostCfg Synced; private PhotonScopedManager manager; private Harmony harmony; private Coroutine safeZoneCheckCoroutine; private Coroutine powerRestorationCoroutine; private void Awake() { Instance = this; InitializeConfiguration(); InitializeHarmony(); InitializePhotonManager(); StartSafeZoneMonitoring(); SceneManager.activeSceneChanged += OnSceneChanged; UIPlayerNames.CANNIBAL_HUNGER_THRESHOLD = 2f; ((BaseUnityPlugin)this).Logger.LogInfo((object)"[HikersHunger] Plugin initialized successfully"); } private void InitializeConfiguration() { manualCannibalSelection = ((BaseUnityPlugin)this).Config.Bind("General", "ManualCannibalSelection", false, "When enabled, the host can manually select who becomes the cannibal using a GUI instead of random selection."); initialSelectionDelay = ((BaseUnityPlugin)this).Config.Bind("General", "InitialSelectionDelay", 20, "The initial delay (in seconds) before the game starts attempting to select a cannibal."); postSelectionDelay = ((BaseUnityPlugin)this).Config.Bind("General", "PostSelectionDelay", 120, "The delay (in seconds) after selecting a cannibal before actually giving them cannibal powers."); enableCustomEatTime = ((BaseUnityPlugin)this).Config.Bind("General", "EnableCustomEatTime", false, "When enabled, allows hosts to customize how long it takes to eat players."); customEatTime = ((BaseUnityPlugin)this).Config.Bind("General", "CustomEatTime", 0.01f, "The time (in seconds) it takes to eat a player. Lower values = faster eating. 0.01 = almost instant."); constantCannibal = ((BaseUnityPlugin)this).Config.Bind("General", "ConstantCannibal", false, "When enabled, the first person chosen as cannibal remains cannibal for the entire game until a new run starts."); safeZoneDelay = ((BaseUnityPlugin)this).Config.Bind("General", "SafeZoneDelay", 30f, "The delay (in seconds) after leaving a safe zone before cannibal powers are restored."); Synced = BuildFromHostConfig(); manualCannibalSelection.SettingChanged += delegate { OnHostConfigChanged(); }; initialSelectionDelay.SettingChanged += delegate { OnHostConfigChanged(); }; postSelectionDelay.SettingChanged += delegate { OnHostConfigChanged(); }; enableCustomEatTime.SettingChanged += delegate { OnHostConfigChanged(); }; customEatTime.SettingChanged += delegate { OnHostConfigChanged(); }; constantCannibal.SettingChanged += delegate { OnHostConfigChanged(); }; safeZoneDelay.SettingChanged += delegate { OnHostConfigChanged(); }; } private void InitializeHarmony() { //IL_0007: Unknown result type (might be due to invalid IL or missing references) //IL_0011: Expected O, but got Unknown harmony = new Harmony("tony4twentys.Hikers_Hunger"); harmony.PatchAll(); ((BaseUnityPlugin)this).Logger.LogInfo((object)"[HikersHunger] Harmony patches applied"); } private void InitializePhotonManager() { manager = PhotonCustomPropsUtilsPlugin.GetManager("tony4twentys.Hikers_Hunger"); manager.RegisterRoomProperty("cannibalViewId", (RoomEventType)2, (Action)delegate(int value) { cannibalViewID = value; OnCannibalChanged(); }); } private void StartSafeZoneMonitoring() { if (safeZoneCheckCoroutine != null) { ((MonoBehaviour)this).StopCoroutine(safeZoneCheckCoroutine); } safeZoneCheckCoroutine = ((MonoBehaviour)this).StartCoroutine(SafeZoneMonitoringLoop()); } private void OnCannibalChanged() { if (cannibalViewID == -1) { return; } Character characterByViewID = GetCharacterByViewID(cannibalViewID); if ((Object)(object)characterByViewID != (Object)null) { ManualLogSource logger = ((BaseUnityPlugin)this).Logger; Player owner = characterByViewID.refs.view.Owner; logger.LogInfo((object)("[HikersHunger] " + (((owner != null) ? owner.NickName : null) ?? "Unknown") + " is now the cannibal")); } if (IsLocalPlayerCannibal()) { ((BaseUnityPlugin)this).Logger.LogInfo((object)$"[HikersHunger] Local player is now cannibal - checking safe zone delay state: inSafeZoneDelay={inSafeZoneDelay}"); if (inSafeZoneDelay) { ((BaseUnityPlugin)this).Logger.LogInfo((object)"[HikersHunger] Local player is cannibal but still in safe zone delay - powers will be granted when delay completes"); return; } ((BaseUnityPlugin)this).Logger.LogInfo((object)"[HikersHunger] No safe zone delay active - granting powers immediately"); GrantCannibalPowers(); } } private Character GetCharacterByViewID(int viewID) { foreach (Character allCharacter in Character.AllCharacters) { if (allCharacter != null) { CharacterRefs refs = allCharacter.refs; int? obj; if (refs == null) { obj = null; } else { PhotonView view = refs.view; obj = ((view != null) ? new int?(view.ViewID) : null); } if (obj == viewID) { return allCharacter; } } } return null; } private bool IsLocalPlayerCannibal() { int num = cannibalViewID; Character localCharacter = Character.localCharacter; int? obj; if (localCharacter == null) { obj = null; } else { PhotonView photonView = ((MonoBehaviourPun)localCharacter).photonView; obj = ((photonView != null) ? new int?(photonView.ViewID) : null); } return num == obj; } private void GrantCannibalPowers() { if (IsLocalPlayerCannibal()) { UIPlayerNames.CANNIBAL_HUNGER_THRESHOLD = -1f; cannibalPowersActive = true; ((BaseUnityPlugin)this).Logger.LogInfo((object)"[HikersHunger] Local player cannibal powers granted"); CheckSafeZoneStatus(); } } private void RevokeCannibalPowers() { if (IsLocalPlayerCannibal()) { UIPlayerNames.CANNIBAL_HUNGER_THRESHOLD = 2f; cannibalPowersActive = false; ((BaseUnityPlugin)this).Logger.LogInfo((object)"[HikersHunger] Local player cannibal powers revoked"); } } private void StripCannibalPowers() { ((BaseUnityPlugin)this).Logger.LogInfo((object)"[HikersHunger] Stripping cannibal powers from all players"); UIPlayerNames.CANNIBAL_HUNGER_THRESHOLD = 2f; if (IsLocalPlayerCannibal()) { cannibalPowersActive = false; inSafeZone = false; safeZoneExitTime = 0f; if (powerRestorationCoroutine != null) { ((MonoBehaviour)this).StopCoroutine(powerRestorationCoroutine); } ((BaseUnityPlugin)this).Logger.LogInfo((object)$"[HikersHunger] Local cannibal powers stripped (inSafeZoneDelay={inSafeZoneDelay})"); } } private void CheckSafeZoneStatus() { if (!IsLocalPlayerCannibal()) { return; } bool flag = IsCharacterNearCampfire(Character.localCharacter); ((BaseUnityPlugin)this).Logger.LogDebug((object)$"[HikersHunger] Safe zone check: currentlyInSafeZone={flag}, inSafeZone={inSafeZone}"); if (flag && !inSafeZone) { inSafeZone = true; RevokeCannibalPowers(); safeZoneExitTime = 0f; if (powerRestorationCoroutine != null) { ((MonoBehaviour)this).StopCoroutine(powerRestorationCoroutine); } ((BaseUnityPlugin)this).Logger.LogInfo((object)"[HikersHunger] Entered campfire safe zone - powers revoked"); ((BaseUnityPlugin)this).Logger.LogInfo((object)$"[HikersHunger] Safe zone state: inSafeZone={inSafeZone}, inSafeZoneDelay={inSafeZoneDelay}"); } else if (!flag && inSafeZone) { inSafeZone = false; inSafeZoneDelay = true; safeZoneExitTime = Time.time; if (powerRestorationCoroutine != null) { ((MonoBehaviour)this).StopCoroutine(powerRestorationCoroutine); } powerRestorationCoroutine = ((MonoBehaviour)this).StartCoroutine(RestorePowersAfterDelay()); ((BaseUnityPlugin)this).Logger.LogInfo((object)"[HikersHunger] Exited campfire safe zone - starting power restoration timer"); ((BaseUnityPlugin)this).Logger.LogInfo((object)$"[HikersHunger] Safe zone state: inSafeZone={inSafeZone}, inSafeZoneDelay={inSafeZoneDelay}"); } } [IteratorStateMachine(typeof(d__37))] private IEnumerator RestorePowersAfterDelay() { //yield-return decompiler failed: Unexpected instruction in Iterator.Dispose() return new d__37(0) { <>4__this = this }; } [IteratorStateMachine(typeof(d__38))] private IEnumerator SafeZoneMonitoringLoop() { //yield-return decompiler failed: Unexpected instruction in Iterator.Dispose() return new d__38(0) { <>4__this = this }; } private void CheckAllPlayersSafeZoneStatus() { foreach (Character allCharacter in Character.AllCharacters) { if ((Object)(object)allCharacter == (Object)null || allCharacter.isBot || allCharacter.data.dead) { continue; } CharacterRefs refs = allCharacter.refs; int? obj; if (refs == null) { obj = null; } else { PhotonView view = refs.view; obj = ((view != null) ? new int?(view.ViewID) : null); } if (obj == cannibalViewID) { if (allCharacter.IsLocal) { CheckSafeZoneStatus(); } else { CheckRemoteCannibalSafeZoneStatus(allCharacter); } } } } private void CheckRemoteCannibalSafeZoneStatus(Character remoteCannibal) { bool flag = IsCharacterNearCampfire(remoteCannibal); } private void ForceRefreshSafeZoneStatus() { ((BaseUnityPlugin)this).Logger.LogInfo((object)"[HikersHunger] Force refreshing safe zone status for all players"); CheckAllPlayersSafeZoneStatus(); if (IsLocalPlayerCannibal()) { CheckSafeZoneStatus(); } } public static bool IsCharacterNearCampfire(Character character) { //IL_0014: Unknown result type (might be due to invalid IL or missing references) //IL_0019: Unknown result type (might be due to invalid IL or missing references) //IL_00cb: Unknown result type (might be due to invalid IL or missing references) //IL_00d0: Unknown result type (might be due to invalid IL or missing references) //IL_00d1: Unknown result type (might be due to invalid IL or missing references) //IL_00d6: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)character == (Object)null) { return false; } Vector3 center = character.Center; float num = 225f; try { Campfire[] array = Object.FindObjectsOfType(); if (array != null && array.Length != 0) { HikersHungerPlugin instance = Instance; if (instance != null) { ManualLogSource logger = ((BaseUnityPlugin)instance).Logger; if (logger != null) { object arg = array.Length; CharacterRefs refs = character.refs; object obj; if (refs == null) { obj = null; } else { PhotonView view = refs.view; if (view == null) { obj = null; } else { Player owner = view.Owner; obj = ((owner != null) ? owner.NickName : null); } } if (obj == null) { obj = "Unknown"; } logger.LogDebug((object)$"[HikersHunger] Found {arg} campfires, checking proximity for {obj}"); } } for (int i = 0; i < array.Length; i++) { Campfire val = array[i]; if ((Object)(object)val == (Object)null) { continue; } Vector3 val2 = ((Component)val).transform.position - center; float sqrMagnitude = ((Vector3)(ref val2)).sqrMagnitude; if (!(sqrMagnitude <= num)) { continue; } HikersHungerPlugin instance2 = Instance; if (instance2 != null) { ManualLogSource logger2 = ((BaseUnityPlugin)instance2).Logger; if (logger2 != null) { CharacterRefs refs2 = character.refs; object obj2; if (refs2 == null) { obj2 = null; } else { PhotonView view2 = refs2.view; if (view2 == null) { obj2 = null; } else { Player owner2 = view2.Owner; obj2 = ((owner2 != null) ? owner2.NickName : null); } } if (obj2 == null) { obj2 = "Unknown"; } logger2.LogDebug((object)$"[HikersHunger] {obj2} is within {Mathf.Sqrt(sqrMagnitude):F1}m of campfire {i + 1}"); } } return true; } HikersHungerPlugin instance3 = Instance; if (instance3 != null) { ManualLogSource logger3 = ((BaseUnityPlugin)instance3).Logger; if (logger3 != null) { CharacterRefs refs3 = character.refs; object obj3; if (refs3 == null) { obj3 = null; } else { PhotonView view3 = refs3.view; if (view3 == null) { obj3 = null; } else { Player owner3 = view3.Owner; obj3 = ((owner3 != null) ? owner3.NickName : null); } } if (obj3 == null) { obj3 = "Unknown"; } logger3.LogDebug((object)("[HikersHunger] " + (string?)obj3 + " is not near any campfires")); } } } else { HikersHungerPlugin instance4 = Instance; if (instance4 != null) { ManualLogSource logger4 = ((BaseUnityPlugin)instance4).Logger; if (logger4 != null) { logger4.LogDebug((object)"[HikersHunger] No campfires found in scene"); } } } } catch (Exception ex) { HikersHungerPlugin instance5 = Instance; if (instance5 != null) { ManualLogSource logger5 = ((BaseUnityPlugin)instance5).Logger; if (logger5 != null) { logger5.LogWarning((object)("[HikersHunger] Campfire search failed: " + ex.Message)); } } } return false; } private void OnEnable() { PhotonNetwork.AddCallbackTarget((object)this); } private void OnDisable() { PhotonNetwork.RemoveCallbackTarget((object)this); } private void OnDestroy() { if (safeZoneCheckCoroutine != null) { ((MonoBehaviour)this).StopCoroutine(safeZoneCheckCoroutine); } if (powerRestorationCoroutine != null) { ((MonoBehaviour)this).StopCoroutine(powerRestorationCoroutine); } PhotonNetwork.RemoveCallbackTarget((object)this); } private void OnSceneChanged(Scene oldScene, Scene newScene) { if (((Scene)(ref newScene)).name == "Airport") { ResetPluginState(); } TryReadConfigFromRoom(); } private void ResetPluginState() { ((BaseUnityPlugin)this).Logger.LogInfo((object)"[HikersHunger] Returned to Airport - resetting plugin state"); cannibalViewID = -1; fireCount = 0; showCannibalSelectionGUI = false; isWaitingForCannibalSelection = false; cannibalPowersActive = false; inSafeZone = false; inSafeZoneDelay = false; safeZoneExitTime = 0f; UIPlayerNames.CANNIBAL_HUNGER_THRESHOLD = 2f; foreach (Character allCharacter in Character.AllCharacters) { if ((Object)(object)allCharacter?.refs?.customization != (Object)null) { allCharacter.refs.customization.BecomeHuman(); } } } public void OnJoinedRoom() { if (PhotonNetwork.IsMasterClient) { ((BaseUnityPlugin)this).Logger.LogInfo((object)"[HikersHunger] Joined room as HOST"); PublishConfig(); } else { ((BaseUnityPlugin)this).Logger.LogInfo((object)"[HikersHunger] Joined room as CLIENT; reading config"); TryReadConfigFromRoom(); } } public void OnCreatedRoom() { } public void OnCreateRoomFailed(short returnCode, string message) { } public void OnFriendListUpdate(List friendList) { } public void OnJoinRandomFailed(short returnCode, string message) { } public void OnJoinRoomFailed(short returnCode, string message) { } public void OnLeftRoom() { } public void OnPlayerEnteredRoom(Player newPlayer) { if (PhotonNetwork.IsMasterClient) { PublishConfig(); } } public void OnPlayerLeftRoom(Player otherPlayer) { if (cannibalViewID == -1 || otherPlayer == null) { return; } foreach (Character allCharacter in Character.AllCharacters) { object obj; if (allCharacter == null) { obj = null; } else { CharacterRefs refs = allCharacter.refs; if (refs == null) { obj = null; } else { PhotonView view = refs.view; obj = ((view != null) ? view.Owner : null); } } if (obj == otherPlayer && allCharacter.refs.view.ViewID == cannibalViewID) { ((BaseUnityPlugin)this).Logger.LogWarning((object)("[HikersHunger] The cannibal (" + otherPlayer.NickName + ") has left the game!")); cannibalViewID = -1; cannibalPowersActive = false; inSafeZone = false; inSafeZoneDelay = false; UIPlayerNames.CANNIBAL_HUNGER_THRESHOLD = 2f; break; } } } public void OnRoomPropertiesUpdate(Hashtable propertiesThatChanged) { if (propertiesThatChanged != null && ((Dictionary)(object)propertiesThatChanged).ContainsKey((object)"HIKERS_HUNGER_CFG_V1") && propertiesThatChanged[(object)"HIKERS_HUNGER_CFG_V1"] is string s) { HostCfg synced = Synced; Synced = UnpackCfg(s); if (ConfigChanged(synced, Synced)) { ((BaseUnityPlugin)this).Logger.LogInfo((object)"[HikersHunger] Config updated from room"); } } } public void OnPlayerPropertiesUpdate(Player targetPlayer, Hashtable changedProps) { } public void OnMasterClientSwitched(Player newMasterClient) { if (PhotonNetwork.IsMasterClient) { PublishConfig(); } } public void OnEvent(EventData photonEvent) { } private HostCfg BuildFromHostConfig() { HostCfg result = default(HostCfg); result.manualCannibalSelection = manualCannibalSelection.Value; result.initialSelectionDelay = initialSelectionDelay.Value; result.postSelectionDelay = postSelectionDelay.Value; result.enableCustomEatTime = enableCustomEatTime.Value; result.customEatTime = customEatTime.Value; result.constantCannibal = constantCannibal.Value; result.safeZoneDelay = safeZoneDelay.Value; return result; } private static string PackCfg(HostCfg c) { CultureInfo invariantCulture = CultureInfo.InvariantCulture; return $"{c.manualCannibalSelection}|{c.initialSelectionDelay}|{c.postSelectionDelay}|{c.enableCustomEatTime}|{c.customEatTime}|{c.constantCannibal}|{c.safeZoneDelay}"; } private static HostCfg UnpackCfg(string s) { CultureInfo invariantCulture = CultureInfo.InvariantCulture; string[] array = (s ?? string.Empty).Split(new char[1] { '|' }); HostCfg hostCfg = default(HostCfg); hostCfg.manualCannibalSelection = false; hostCfg.initialSelectionDelay = 20; hostCfg.postSelectionDelay = 120; hostCfg.enableCustomEatTime = false; hostCfg.customEatTime = 0.01f; hostCfg.constantCannibal = false; hostCfg.safeZoneDelay = 30f; HostCfg result = hostCfg; if (array.Length >= 7) { bool.TryParse(array[0], out result.manualCannibalSelection); int.TryParse(array[1], NumberStyles.Integer, invariantCulture, out result.initialSelectionDelay); int.TryParse(array[2], NumberStyles.Integer, invariantCulture, out result.postSelectionDelay); bool.TryParse(array[3], out result.enableCustomEatTime); float.TryParse(array[4], NumberStyles.Float, invariantCulture, out result.customEatTime); bool.TryParse(array[5], out result.constantCannibal); float.TryParse(array[6], NumberStyles.Float, invariantCulture, out result.safeZoneDelay); } return result; } private bool ConfigChanged(HostCfg old, HostCfg newCfg) { return old.manualCannibalSelection != newCfg.manualCannibalSelection || old.initialSelectionDelay != newCfg.initialSelectionDelay || old.postSelectionDelay != newCfg.postSelectionDelay || old.enableCustomEatTime != newCfg.enableCustomEatTime || old.customEatTime != newCfg.customEatTime || old.constantCannibal != newCfg.constantCannibal || old.safeZoneDelay != newCfg.safeZoneDelay; } private void TryReadConfigFromRoom() { Room currentRoom = PhotonNetwork.CurrentRoom; object value = default(object); if (currentRoom != null && (((Dictionary)(object)((RoomInfo)currentRoom).CustomProperties)?.TryGetValue((object)"HIKERS_HUNGER_CFG_V1", out value)).GetValueOrDefault() && value is string s) { Synced = UnpackCfg(s); } } private void OnHostConfigChanged() { if (PhotonNetwork.IsMasterClient) { PublishConfig(); } } private void PublishConfig() { //IL_0027: 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_003e: Expected O, but got Unknown Room currentRoom = PhotonNetwork.CurrentRoom; if (currentRoom != null) { Synced = BuildFromHostConfig(); string text = PackCfg(Synced); Hashtable val = new Hashtable { [(object)"HIKERS_HUNGER_CFG_V1"] = text }; currentRoom.SetCustomProperties(val, (Hashtable)null, (WebFlags)null); ((BaseUnityPlugin)this).Logger.LogInfo((object)"[HikersHunger] Host published config to room"); } } private void OnGUI() { if (showCannibalSelectionGUI && PhotonNetwork.IsMasterClient && isWaitingForCannibalSelection) { DrawCannibalSelectionGUI(); } } private void DrawCannibalSelectionGUI() { //IL_001c: Unknown result type (might be due to invalid IL or missing references) //IL_003d: Unknown result type (might be due to invalid IL or missing references) //IL_004d: Unknown result type (might be due to invalid IL or missing references) //IL_0086: Unknown result type (might be due to invalid IL or missing references) //IL_00b0: Unknown result type (might be due to invalid IL or missing references) //IL_00c4: 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_00d2: Unknown result type (might be due to invalid IL or missing references) //IL_00df: Expected O, but got Unknown //IL_01f1: Unknown result type (might be due to invalid IL or missing references) //IL_016d: Unknown result type (might be due to invalid IL or missing references) HandleMenuInput(); GUI.color = new Color(0f, 0f, 0f, 0.8f); GUI.Box(new Rect(0f, 0f, (float)Screen.width, (float)Screen.height), ""); GUI.color = Color.white; float num = 400f; float num2 = 300f; float num3 = ((float)Screen.width - num) / 2f; float num4 = ((float)Screen.height - num2) / 2f; GUI.Box(new Rect(num3, num4, num, num2), "Select Cannibal"); GUI.Label(new Rect(num3 + 10f, num4 + 20f, num - 20f, 30f), "Choose who will become the cannibal:", new GUIStyle(GUI.skin.label) { fontSize = 16, alignment = (TextAnchor)4 }); float num5 = num4 + 60f; float num6 = 40f; float num7 = 5f; List validCandidates = GetValidCandidates(); foreach (Character item in validCandidates) { if (num5 + num6 > num4 + num2 - 60f) { break; } Player owner = item.refs.view.Owner; string text = ((owner != null) ? owner.NickName : null) ?? "Unknown Player"; if (GUI.Button(new Rect(num3 + 20f, num5, num - 40f, num6), text)) { if (cannibalViewID != -1) { StripCannibalPowers(); } SelectCannibal(item, isInitialSelection: true); CloseGUI(); } num5 += num6 + num7; } if (GUI.Button(new Rect(num3 + 20f, num4 + num2 - 40f, num - 40f, 30f), "Cancel (Random Selection)")) { CloseGUI(); ((MonoBehaviour)this).StartCoroutine(SelectRandomCannibalAfterDelay()); } } private void HandleMenuInput() { Cursor.lockState = (CursorLockMode)0; Cursor.visible = true; } private void CloseGUI() { showCannibalSelectionGUI = false; isWaitingForCannibalSelection = false; Cursor.lockState = (CursorLockMode)1; Cursor.visible = false; } private List GetValidCandidates() { List list = new List(); foreach (Character allCharacter in Character.AllCharacters) { object obj; if (allCharacter == null) { obj = null; } else { CharacterRefs refs = allCharacter.refs; if (refs == null) { obj = null; } else { PhotonView view = refs.view; obj = ((view != null) ? view.Owner : null); } } if (obj != null && !allCharacter.isBot && !allCharacter.data.dead) { list.Add(allCharacter); } } return list; } private void SelectCannibal(Character character, bool isInitialSelection = false) { if ((Object)(object)character?.refs?.view == (Object)null) { return; } int viewID = character.refs.view.ViewID; bool flag = cannibalViewID == viewID; string text = (isInitialSelection ? "initial" : "campfire"); ManualLogSource logger = ((BaseUnityPlugin)this).Logger; Player owner = character.refs.view.Owner; logger.LogInfo((object)("[HikersHunger] " + text + " cannibal selected: " + (((owner != null) ? owner.NickName : null) ?? "Unknown"))); if (isInitialSelection) { ((BaseUnityPlugin)this).Logger.LogInfo((object)"[HikersHunger] Applying initial selection delay..."); cannibalViewID = viewID; ((MonoBehaviour)this).StartCoroutine(DelayCannibalPowers(viewID)); return; } if (flag && inSafeZoneDelay) { ((BaseUnityPlugin)this).Logger.LogInfo((object)"[HikersHunger] Reselecting same cannibal who was in safe zone delay - powers will be granted when delay completes"); cannibalViewID = viewID; return; } ((BaseUnityPlugin)this).Logger.LogInfo((object)"[HikersHunger] Granting campfire selection powers immediately..."); cannibalViewID = viewID; try { manager.SetRoomProperty("cannibalViewId", (object)viewID); ((BaseUnityPlugin)this).Logger.LogInfo((object)"[HikersHunger] Cannibal powers now active!"); } catch (Exception ex) { ((BaseUnityPlugin)this).Logger.LogError((object)("[HikersHunger] Failed to set room property: " + ex.Message)); } } [IteratorStateMachine(typeof(d__74))] private IEnumerator DelayCannibalPowers(int viewID) { //yield-return decompiler failed: Unexpected instruction in Iterator.Dispose() return new d__74(0) { <>4__this = this, viewID = viewID }; } [IteratorStateMachine(typeof(d__75))] private IEnumerator SelectRandomCannibalAfterDelay() { //yield-return decompiler failed: Unexpected instruction in Iterator.Dispose() return new d__75(0) { <>4__this = this }; } private void SelectRandomCannibal() { if (PhotonNetwork.IsMasterClient) { List validCandidates = GetValidCandidates(); if (validCandidates.Count > 0) { int index = Random.Range(0, validCandidates.Count); SelectCannibal(validCandidates[index], isInitialSelection: true); } } } private void SelectNewCannibalAtCampfire() { if (!PhotonNetwork.IsMasterClient) { return; } if (Synced.constantCannibal) { ((BaseUnityPlugin)this).Logger.LogInfo((object)"[HikersHunger] Constant cannibal mode - keeping current cannibal"); return; } if (cannibalViewID != -1 && inSafeZoneDelay) { ((BaseUnityPlugin)this).Logger.LogInfo((object)"[HikersHunger] Current cannibal is in safe zone delay - will be reselected but powers delayed"); ManualLogSource logger = ((BaseUnityPlugin)this).Logger; object arg = cannibalViewID; Character localCharacter = Character.localCharacter; int? obj; if (localCharacter == null) { obj = null; } else { PhotonView photonView = ((MonoBehaviourPun)localCharacter).photonView; obj = ((photonView != null) ? new int?(photonView.ViewID) : null); } logger.LogInfo((object)$"[HikersHunger] Current cannibal ViewID: {arg}, Local player ViewID: {obj}"); } if (cannibalViewID != -1) { ((BaseUnityPlugin)this).Logger.LogInfo((object)"[HikersHunger] Stripping powers from current cannibal before selecting new one"); StripCannibalPowers(); } if (Synced.manualCannibalSelection) { isWaitingForCannibalSelection = true; showCannibalSelectionGUI = true; } else { SelectRandomCannibalAtCampfire(); } } private void SelectRandomCannibalAtCampfire() { if (PhotonNetwork.IsMasterClient) { List validCandidates = GetValidCandidates(); if (validCandidates.Count > 0) { int index = Random.Range(0, validCandidates.Count); Character val = validCandidates[index]; ManualLogSource logger = ((BaseUnityPlugin)this).Logger; Player owner = val.refs.view.Owner; logger.LogInfo((object)("[HikersHunger] Randomly selecting " + (((owner != null) ? owner.NickName : null) ?? "Unknown") + " as cannibal at campfire")); ((BaseUnityPlugin)this).Logger.LogInfo((object)$"[HikersHunger] Selected character ViewID: {val.refs.view.ViewID}, Current cannibal ViewID: {cannibalViewID}"); ((BaseUnityPlugin)this).Logger.LogInfo((object)$"[HikersHunger] Safe zone delay state: inSafeZoneDelay={inSafeZoneDelay}"); SelectCannibal(val); } } } }