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<Character>() != (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<MapHandler>.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 <DelayCannibalPowers>d__74 : IEnumerator<object>, IDisposable, IEnumerator
{
private int <>1__state;
private object <>2__current;
public int viewID;
public HikersHungerPlugin <>4__this;
private float <delay>5__1;
private float <elapsed>5__2;
private Exception <ex>5__3;
object IEnumerator<object>.Current
{
[DebuggerHidden]
get
{
return <>2__current;
}
}
object IEnumerator.Current
{
[DebuggerHidden]
get
{
return <>2__current;
}
}
[DebuggerHidden]
public <DelayCannibalPowers>d__74(int <>1__state)
{
this.<>1__state = <>1__state;
}
[DebuggerHidden]
void IDisposable.Dispose()
{
<ex>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;
<delay>5__1 = Synced.postSelectionDelay;
<elapsed>5__2 = 0f;
((BaseUnityPlugin)<>4__this).Logger.LogInfo((object)$"[HikersHunger] Waiting {<delay>5__1} seconds before granting powers...");
break;
case 1:
<>1__state = -1;
<elapsed>5__2 += 1f;
break;
}
if (<elapsed>5__2 < <delay>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)
{
<ex>5__3 = ex;
((BaseUnityPlugin)<>4__this).Logger.LogError((object)("[HikersHunger] Failed to set room property: " + <ex>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 <RestorePowersAfterDelay>d__37 : IEnumerator<object>, IDisposable, IEnumerator
{
private int <>1__state;
private object <>2__current;
public HikersHungerPlugin <>4__this;
private float <delay>5__1;
private float <elapsed>5__2;
private Exception <ex>5__3;
object IEnumerator<object>.Current
{
[DebuggerHidden]
get
{
return <>2__current;
}
}
object IEnumerator.Current
{
[DebuggerHidden]
get
{
return <>2__current;
}
}
[DebuggerHidden]
public <RestorePowersAfterDelay>d__37(int <>1__state)
{
this.<>1__state = <>1__state;
}
[DebuggerHidden]
void IDisposable.Dispose()
{
<ex>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;
<delay>5__1 = Synced.safeZoneDelay;
<elapsed>5__2 = 0f;
((BaseUnityPlugin)<>4__this).Logger.LogInfo((object)$"[HikersHunger] Starting {<delay>5__1}s safe zone delay before restoring powers...");
break;
case 1:
<>1__state = -1;
<elapsed>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(<elapsed>5__2) % 5 == 0)
{
((BaseUnityPlugin)<>4__this).Logger.LogInfo((object)$"[HikersHunger] Safe zone delay: {<elapsed>5__2:F1}s / {<delay>5__1}s remaining");
}
break;
}
if (<elapsed>5__2 < <delay>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)
{
<ex>5__3 = ex;
((BaseUnityPlugin)<>4__this).Logger.LogError((object)("[HikersHunger] Failed to update room property after delay: " + <ex>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 <SafeZoneMonitoringLoop>d__38 : IEnumerator<object>, IDisposable, IEnumerator
{
private int <>1__state;
private object <>2__current;
public HikersHungerPlugin <>4__this;
object IEnumerator<object>.Current
{
[DebuggerHidden]
get
{
return <>2__current;
}
}
object IEnumerator.Current
{
[DebuggerHidden]
get
{
return <>2__current;
}
}
[DebuggerHidden]
public <SafeZoneMonitoringLoop>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 <SelectRandomCannibalAfterDelay>d__75 : IEnumerator<object>, IDisposable, IEnumerator
{
private int <>1__state;
private object <>2__current;
public HikersHungerPlugin <>4__this;
private float <delay>5__1;
private float <elapsed>5__2;
object IEnumerator<object>.Current
{
[DebuggerHidden]
get
{
return <>2__current;
}
}
object IEnumerator.Current
{
[DebuggerHidden]
get
{
return <>2__current;
}
}
[DebuggerHidden]
public <SelectRandomCannibalAfterDelay>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;
<delay>5__1 = Synced.initialSelectionDelay;
<elapsed>5__2 = 0f;
((BaseUnityPlugin)<>4__this).Logger.LogInfo((object)$"[HikersHunger] Waiting {<delay>5__1} seconds before selecting cannibal...");
break;
case 1:
<>1__state = -1;
<elapsed>5__2 += 1f;
break;
}
if (<elapsed>5__2 < <delay>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<bool> manualCannibalSelection;
private ConfigEntry<int> initialSelectionDelay;
private ConfigEntry<int> postSelectionDelay;
private ConfigEntry<bool> enableCustomEatTime;
private ConfigEntry<float> customEatTime;
private ConfigEntry<bool> constantCannibal;
private ConfigEntry<float> 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<bool>("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<int>("General", "InitialSelectionDelay", 20, "The initial delay (in seconds) before the game starts attempting to select a cannibal.");
postSelectionDelay = ((BaseUnityPlugin)this).Config.Bind<int>("General", "PostSelectionDelay", 120, "The delay (in seconds) after selecting a cannibal before actually giving them cannibal powers.");
enableCustomEatTime = ((BaseUnityPlugin)this).Config.Bind<bool>("General", "EnableCustomEatTime", false, "When enabled, allows hosts to customize how long it takes to eat players.");
customEatTime = ((BaseUnityPlugin)this).Config.Bind<float>("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<bool>("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<float>("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<int>("cannibalViewId", (RoomEventType)2, (Action<int>)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(<RestorePowersAfterDelay>d__37))]
private IEnumerator RestorePowersAfterDelay()
{
//yield-return decompiler failed: Unexpected instruction in Iterator.Dispose()
return new <RestorePowersAfterDelay>d__37(0)
{
<>4__this = this
};
}
[IteratorStateMachine(typeof(<SafeZoneMonitoringLoop>d__38))]
private IEnumerator SafeZoneMonitoringLoop()
{
//yield-return decompiler failed: Unexpected instruction in Iterator.Dispose()
return new <SafeZoneMonitoringLoop>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<Campfire>();
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<FriendInfo> 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, object>)(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, object>)(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<Character> 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<Character> GetValidCandidates()
{
List<Character> list = new List<Character>();
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(<DelayCannibalPowers>d__74))]
private IEnumerator DelayCannibalPowers(int viewID)
{
//yield-return decompiler failed: Unexpected instruction in Iterator.Dispose()
return new <DelayCannibalPowers>d__74(0)
{
<>4__this = this,
viewID = viewID
};
}
[IteratorStateMachine(typeof(<SelectRandomCannibalAfterDelay>d__75))]
private IEnumerator SelectRandomCannibalAfterDelay()
{
//yield-return decompiler failed: Unexpected instruction in Iterator.Dispose()
return new <SelectRandomCannibalAfterDelay>d__75(0)
{
<>4__this = this
};
}
private void SelectRandomCannibal()
{
if (PhotonNetwork.IsMasterClient)
{
List<Character> 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<Character> 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);
}
}
}
}