Decompiled source of Campfire Haven v1.1.0

plugins/CampfireHaven.dll

Decompiled 2 days ago
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
using BepInEx;
using BepInEx.Configuration;
using BepInEx.Logging;
using ExitGames.Client.Photon;
using Peak.Afflictions;
using Photon.Pun;
using Photon.Realtime;
using UnityEngine;
using Zorro.Core;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: TargetFramework(".NETStandard,Version=v2.1", FrameworkDisplayName = ".NET Standard 2.1")]
[assembly: AssemblyCompany("CampfireHaven")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyFileVersion("1.1.0.0")]
[assembly: AssemblyInformationalVersion("1.1.0+0383e9a2e04a5d5d8423b5aa94390544ff83e9a2")]
[assembly: AssemblyProduct("CampfireHaven")]
[assembly: AssemblyTitle("CampfireHaven")]
[assembly: AssemblyVersion("1.1.0.0")]
namespace CampfireHaven;

internal static class CampfireAura
{
	private const float RescanInterval = 120f;

	private static readonly List<Campfire> s_Tracked = new List<Campfire>();

	private static object s_SeenMapHandler;

	private static bool s_Seeded;

	private static float s_NextRescan;

	internal static List<Campfire> Tracked => s_Tracked;

	internal static void Refresh()
	{
		Prune();
		if (EnsureMapHandler())
		{
			SeedOnce();
			TrackSegmentCampfires();
			RescanIfDue();
		}
	}

	private static bool EnsureMapHandler()
	{
		MapHandler instance = Singleton<MapHandler>.Instance;
		if ((Object)(object)instance == (Object)null)
		{
			return false;
		}
		if (instance == s_SeenMapHandler)
		{
			return true;
		}
		s_SeenMapHandler = instance;
		s_Tracked.Clear();
		s_Seeded = false;
		s_NextRescan = 0f;
		return true;
	}

	private static void SeedOnce()
	{
		if (!s_Seeded)
		{
			try
			{
				ScanScene();
			}
			catch (Exception ex)
			{
				Diagnostics.Warn("[篝火] 开局扫描异常,下一 tick 重试:" + ex.GetType().Name + ": " + ex.Message);
				return;
			}
			s_Seeded = true;
			s_NextRescan = Time.time + 120f;
			Diagnostics.Trace($"[篝火] 开局扫描完成,共 {s_Tracked.Count} 个");
		}
	}

	private static void TrackSegmentCampfires()
	{
		try
		{
			TryTrack(MapHandler.CurrentCampfire);
		}
		catch
		{
		}
		try
		{
			TryTrack(MapHandler.PreviousCampfire);
		}
		catch
		{
		}
	}

	private static void RescanIfDue()
	{
		if (Time.time < s_NextRescan)
		{
			return;
		}
		s_NextRescan = Time.time + 120f;
		try
		{
			ScanScene();
		}
		catch (Exception ex)
		{
			Diagnostics.Warn("[篝火] 定期扫描异常:" + ex.GetType().Name + ": " + ex.Message);
		}
	}

	private static void ScanScene()
	{
		Campfire[] array = Object.FindObjectsByType<Campfire>((FindObjectsInactive)1, (FindObjectsSortMode)0);
		for (int i = 0; i < array.Length; i++)
		{
			TryTrack(array[i]);
		}
	}

	private static void TryTrack(Campfire campfire)
	{
		if (!((Object)(object)campfire == (Object)null) && !s_Tracked.Contains(campfire) && !(campfire.moraleBoostRadius <= 0f))
		{
			try
			{
				CampfireContext.Install(campfire);
			}
			catch (Exception ex)
			{
				Diagnostics.Warn("[篝火] 登记失败 " + ((Object)campfire).name + ",该篝火本次不生效:" + ex.GetType().Name + ": " + ex.Message);
				return;
			}
			s_Tracked.Add(campfire);
		}
	}

	private static void Prune()
	{
		for (int num = s_Tracked.Count - 1; num >= 0; num--)
		{
			if ((Object)(object)s_Tracked[num] == (Object)null)
			{
				s_Tracked.RemoveAt(num);
			}
		}
	}
}
internal sealed class CampfireContext : MonoBehaviour
{
	private const float BuffRefreshInterval = 1.5f;

	private StatusEmitter _emitter;

	private float _lastBuffApplied;

	private bool _resolved;

	internal bool LocalInProtectionAura;

	internal bool LocalInWarmthAura;

	internal bool HasWarmth => (Object)(object)_emitter != (Object)null;

	internal Vector3 WarmthOrigin
	{
		get
		{
			//IL_0025: 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)
			if (!((Object)(object)_emitter == (Object)null))
			{
				return _emitter.transform.position;
			}
			return ((Component)this).transform.position;
		}
	}

	internal float WarmthRadius
	{
		get
		{
			if (!((Object)(object)_emitter == (Object)null))
			{
				return _emitter.radius + _emitter.outerFade;
			}
			return 0f;
		}
	}

	internal float WarmthPerSecond
	{
		get
		{
			if (!((Object)(object)_emitter == (Object)null))
			{
				return _emitter.amount;
			}
			return 0f;
		}
	}

	internal bool TryRefreshBuff(CharacterAfflictions afflictions)
	{
		if (!afflictions.canGetHungry && Time.time - _lastBuffApplied <= 1.5f)
		{
			return false;
		}
		_lastBuffApplied = Time.time;
		return true;
	}

	internal static CampfireContext Install(Campfire campfire)
	{
		if ((Object)(object)campfire == (Object)null)
		{
			return null;
		}
		CampfireContext campfireContext = default(CampfireContext);
		if (((Component)campfire).TryGetComponent<CampfireContext>(ref campfireContext))
		{
			if (!campfireContext._resolved)
			{
				campfireContext.Resolve(campfire);
			}
			return campfireContext;
		}
		CampfireContext campfireContext2 = ((Component)campfire).gameObject.AddComponent<CampfireContext>();
		campfireContext2.Resolve(campfire);
		return campfireContext2;
	}

	private void Resolve(Campfire campfire)
	{
		try
		{
			_emitter = FindWarmthEmitter(campfire);
		}
		catch (Exception ex)
		{
			Diagnostics.Warn("[篝火] 取暖组件解析失败 " + ((Object)campfire).name + ":" + ex.GetType().Name + ": " + ex.Message);
		}
		_resolved = true;
		Diagnostics.Trace($"[篝火] 已登记 {((Object)campfire).name}:防饥饿半径 {campfire.moraleBoostRadius:F1} 世界单位," + (HasWarmth ? $"取暖 {WarmthPerSecond:F3}/s 半径 {WarmthRadius:F1} 世界单位" : "未找到取暖组件"));
	}

	private static StatusEmitter FindWarmthEmitter(Campfire campfire)
	{
		if ((Object)(object)campfire.enableWhenLit != (Object)null)
		{
			StatusEmitter val = PickColdEmitter(campfire.enableWhenLit.GetComponentsInChildren<StatusEmitter>(true));
			if ((Object)(object)val != (Object)null)
			{
				return val;
			}
		}
		return PickColdEmitter(((Component)campfire).GetComponentsInChildren<StatusEmitter>(true));
	}

	private static StatusEmitter PickColdEmitter(StatusEmitter[] emitters)
	{
		//IL_000b: Unknown result type (might be due to invalid IL or missing references)
		//IL_0011: Invalid comparison between Unknown and I4
		foreach (StatusEmitter val in emitters)
		{
			if ((int)val.statusType == 2 && val.amount < 0f)
			{
				return val;
			}
		}
		return null;
	}
}
[BepInPlugin("campfirehaven.host", "Campfire Haven", "1.1.0")]
public sealed class CampfireHavenPlugin : BaseUnityPlugin
{
	public const string Guid = "campfirehaven.host";

	public const string Name = "Campfire Haven";

	public const string Version = "1.1.0";

	private void Awake()
	{
		//IL_002a: Unknown result type (might be due to invalid IL or missing references)
		//IL_002f: Unknown result type (might be due to invalid IL or missing references)
		//IL_0035: Expected O, but got Unknown
		Diagnostics.Sink = ((BaseUnityPlugin)this).Logger;
		PluginConfig.Bind(((BaseUnityPlugin)this).Config);
		Diagnostics.Verbose = PluginConfig.DebugLog.Value;
		GameObject val = new GameObject("Campfire Haven");
		Object.DontDestroyOnLoad((Object)val);
		val.AddComponent<HostLoop>();
		Diagnostics.Info(string.Format("{0} {1} 已加载:防饥饿={2} ", "Campfire Haven", "1.1.0", PluginConfig.ProtectFromHunger.Value) + $"取暖={PluginConfig.ProtectFromCold.Value} 驱逐领队={PluginConfig.EvictScoutmaster.Value} " + $"半径(防饥饿/取暖/驱逐)={PluginConfig.ProtectionRadius.Value}/{PluginConfig.WarmthRadius.Value}/{PluginConfig.EvictionRadius.Value} 米 " + $"调试日志={Diagnostics.Verbose}");
	}
}
internal static class CampfireProtection
{
	private struct HungerHold
	{
		internal float Anchor;

		internal float Mirror;
	}

	private const float ColdQuantum = 0.025f;

	private const float EmitterTick = 0.5f;

	private static readonly Affliction_NoHunger s_Buff = new Affliction_NoHunger
	{
		totalTime = 3f
	};

	private static readonly Dictionary<Character, HungerHold> s_Holds = new Dictionary<Character, HungerHold>();

	private static readonly List<Character> s_Stale = new List<Character>();

	internal static void ApplyLocalHunger(CampfireContext context, Character local)
	{
		if (!((Object)(object)context == (Object)null) && context.TryRefreshBuff(local.refs.afflictions))
		{
			local.refs.afflictions.AddAffliction((Affliction)(object)s_Buff, false);
		}
	}

	internal static void ApplyLocalWarmth(Character local, float warmthPerSecond, float deltaTime)
	{
		float num = WarmthStep(warmthPerSecond, deltaTime);
		if (!(num <= 0f))
		{
			local.refs.afflictions.SubtractStatus((STATUSTYPE)2, num, false, false);
		}
	}

	internal static void WarmRemote(Character character, float warmthPerSecond, float deltaTime)
	{
		float num = WarmthStep(warmthPerSecond, deltaTime);
		if (!(num <= 0f))
		{
			StatusRelay.PushCold(character, 0f - num);
		}
	}

	private static float WarmthStep(float warmthPerSecond, float deltaTime)
	{
		int num = Mathf.Max(1, Mathf.RoundToInt(deltaTime / 0.5f));
		return Mathf.Abs(warmthPerSecond) * 0.5f * (float)num;
	}

	internal static void HoldRemoteHunger(Character character, bool insideAura)
	{
		if (!insideAura)
		{
			s_Holds.Remove(character);
			return;
		}
		float currentStatus = character.refs.afflictions.GetCurrentStatus((STATUSTYPE)1);
		if (!s_Holds.TryGetValue(character, out var value))
		{
			s_Holds[character] = new HungerHold
			{
				Anchor = currentStatus,
				Mirror = currentStatus
			};
			Diagnostics.Trace($"[篝火] 开始压制 {((Object)character).name} 的饥饿,基线 {currentStatus:F3}");
		}
		else if (currentStatus <= value.Anchor)
		{
			value.Anchor = currentStatus;
			value.Mirror = currentStatus;
			s_Holds[character] = value;
		}
		else if (currentStatus == value.Mirror)
		{
			s_Holds[character] = value;
		}
		else
		{
			value.Mirror = currentStatus;
			s_Holds[character] = value;
			StatusRelay.PushHunger(character, value.Anchor - currentStatus);
		}
	}

	internal static void ForgetAllHolds()
	{
		s_Holds.Clear();
	}

	internal static void DropDestroyedHolds()
	{
		if (s_Holds.Count == 0)
		{
			return;
		}
		s_Stale.Clear();
		foreach (KeyValuePair<Character, HungerHold> s_Hold in s_Holds)
		{
			if ((Object)(object)s_Hold.Key == (Object)null)
			{
				s_Stale.Add(s_Hold.Key);
			}
		}
		foreach (Character item in s_Stale)
		{
			s_Holds.Remove(item);
		}
	}
}
internal static class Diagnostics
{
	internal static ManualLogSource Sink;

	internal static bool Verbose;

	internal static void Trace(string message)
	{
		if (Verbose)
		{
			ManualLogSource sink = Sink;
			if (sink != null)
			{
				sink.LogInfo((object)message);
			}
		}
	}

	internal static void Info(string message)
	{
		ManualLogSource sink = Sink;
		if (sink != null)
		{
			sink.LogInfo((object)message);
		}
	}

	internal static void Warn(string message)
	{
		ManualLogSource sink = Sink;
		if (sink != null)
		{
			sink.LogWarning((object)message);
		}
	}
}
internal sealed class HostLoop : MonoBehaviour
{
	private const float TickInterval = 0.5f;

	private const float ReportInterval = 30f;

	private readonly List<Character> _characters = new List<Character>();

	private readonly HashSet<Character> _hungerCovered = new HashSet<Character>();

	private readonly HashSet<Character> _warmthCovered = new HashSet<Character>();

	private float _warmthPerSecond;

	private float _nextTick;

	private float _nextReport;

	private float _lastTickTime;

	private bool _reportedStartup;

	private bool _reportedActivation;

	private bool _reportedStandDown;

	private bool _reportedStandDownReasonIsHost;

	private bool _localInProtection;

	private bool _localInWarmth;

	private float _localWarmthPerSecond;

	private float _nearestDistance;

	private float _nearestRadius;

	private bool _nearestHasWarmth;

	private bool _hasNearest;

	private void Update()
	{
		if (Time.time < _nextTick)
		{
			return;
		}
		float deltaTime = Mathf.Clamp(Time.time - _lastTickTime, 0.1f, 1f);
		_lastTickTime = Time.time;
		_nextTick = Time.time + 0.5f;
		bool inRoom = PhotonNetwork.InRoom;
		bool flag = !inRoom || PhotonNetwork.IsMasterClient;
		try
		{
			ModPresence.Sync(inRoom, PluginConfig.Active);
		}
		catch (Exception ex)
		{
			Diagnostics.Warn("[篝火] 宣告状态同步异常:" + ex.GetType().Name + ": " + ex.Message);
		}
		if (!PluginConfig.Active)
		{
			return;
		}
		Diagnostics.Verbose = PluginConfig.DebugLog.Value;
		ReportStartup();
		bool flag2 = inRoom && ModPresence.HostHasMod;
		if (inRoom && !flag && (!PluginConfig.ClientFallback.Value || flag2))
		{
			CampfireProtection.ForgetAllHolds();
			ReportStandDown(flag2);
			return;
		}
		_reportedStandDown = false;
		try
		{
			CampfireAura.Refresh();
		}
		catch (Exception ex2)
		{
			Diagnostics.Warn("[篝火] 注册表刷新异常:" + ex2.GetType().Name + ": " + ex2.Message);
		}
		CollectCharacters();
		try
		{
			ApplyCampfireAuras(deltaTime, flag);
		}
		catch (Exception ex3)
		{
			Diagnostics.Warn("[篝火] 光环应用异常:" + ex3.GetType().Name + ": " + ex3.Message);
		}
		try
		{
			CampfireProtection.DropDestroyedHolds();
		}
		catch (Exception ex4)
		{
			Diagnostics.Warn("[篝火] 清理饥饿压制表异常:" + ex4.GetType().Name + ": " + ex4.Message);
		}
		if (PluginConfig.EvictScoutmaster.Value && inRoom && flag)
		{
			try
			{
				EvictScoutmasters();
			}
			catch (Exception ex5)
			{
				Diagnostics.Warn("[篝火] 驱逐领队异常:" + ex5.GetType().Name + ": " + ex5.Message);
			}
		}
		try
		{
			ReportPeriodic(flag, flag2);
		}
		catch (Exception ex6)
		{
			Diagnostics.Warn("[篝火] 状态汇总异常:" + ex6.GetType().Name);
		}
	}

	private void ReportStartup()
	{
		if (!_reportedStartup)
		{
			_reportedStartup = true;
			string text = $"[状态] HostLoop 已挂载 InRoom={PhotonNetwork.InRoom} ";
			object arg = PhotonNetwork.IsMasterClient;
			Player localPlayer = PhotonNetwork.LocalPlayer;
			Diagnostics.Trace(text + $"IsMaster={arg} Actor={((localPlayer != null) ? new int?(localPlayer.ActorNumber) : ((int?)null))} " + "游戏版本=" + Application.version);
		}
	}

	private void ReportStandDown(bool hostHasMod)
	{
		if (!_reportedStandDown || _reportedStandDownReasonIsHost != hostHasMod)
		{
			_reportedStandDown = true;
			_reportedStandDownReasonIsHost = hostHasMod;
			Diagnostics.Info(hostHasMod ? "[篝火] 主机已安装本模组,本客户端自动让位,不会重复生效" : "[篝火] 客户端回退已关闭,本客户端不工作");
		}
	}

	private void ReportPeriodic(bool isMaster, bool hostOwnsAuthority)
	{
		if (!(Time.time < _nextReport))
		{
			_nextReport = Time.time + 30f;
			string arg = ((!PhotonNetwork.InRoom) ? "单机" : (isMaster ? "主机" : (hostOwnsAuthority ? "客户端(让位)" : "客户端(自保)")));
			Character localCharacter = Character.localCharacter;
			string arg2 = (((Object)(object)localCharacter == (Object)null) ? "本地角色未就绪" : ($"饥饿={localCharacter.refs.afflictions.GetCurrentStatus((STATUSTYPE)1):F3} " + $"寒冷={localCharacter.refs.afflictions.GetCurrentStatus((STATUSTYPE)2):F3} " + $"离受冻={localCharacter.data.sinceAddedCold:F1}s " + $"饥饿速率={localCharacter.refs.afflictions.hungerPerSecond:F4}/s " + $"免饥饿={!localCharacter.refs.afflictions.canGetHungry} " + $"在防饥饿圈={_localInProtection} " + (_hasNearest ? $"最近篝火={_nearestDistance * CharacterStats.unitsToMeters:F1}m/{_nearestRadius * CharacterStats.unitsToMeters:F1}m " : "最近篝火=附近无未点燃篝火 ") + $"取暖组件={_nearestHasWarmth} " + $"在取暖圈={_localInWarmth}({Mathf.Abs(_localWarmthPerSecond):F3}/s)"));
			Diagnostics.Trace($"[状态] 角色={arg} 篝火={CampfireAura.Tracked.Count} 角色数={_characters.Count} " + $"远程饥饿={_hungerCovered.Count} 远程取暖={_warmthCovered.Count} | 本地 {arg2}");
		}
	}

	private void CollectCharacters()
	{
		_characters.Clear();
		_characters.AddRange(Character.AllCharacters);
		_characters.AddRange(Character.AllBotCharacters);
	}

	private void ApplyCampfireAuras(float deltaTime, bool isMaster)
	{
		//IL_018e: Unknown result type (might be due to invalid IL or missing references)
		//IL_0193: Unknown result type (might be due to invalid IL or missing references)
		//IL_01b9: Unknown result type (might be due to invalid IL or missing references)
		//IL_01bb: Unknown result type (might be due to invalid IL or missing references)
		//IL_0127: Unknown result type (might be due to invalid IL or missing references)
		//IL_012c: Unknown result type (might be due to invalid IL or missing references)
		//IL_0130: Unknown result type (might be due to invalid IL or missing references)
		//IL_0135: Unknown result type (might be due to invalid IL or missing references)
		//IL_01d9: Unknown result type (might be due to invalid IL or missing references)
		//IL_01db: Unknown result type (might be due to invalid IL or missing references)
		Character localCharacter = Character.localCharacter;
		bool value = PluginConfig.ProtectFromHunger.Value;
		bool value2 = PluginConfig.ProtectFromCold.Value;
		_hungerCovered.Clear();
		_warmthCovered.Clear();
		_warmthPerSecond = 0f;
		CampfireContext campfireContext = null;
		_localInProtection = false;
		_localInWarmth = false;
		_localWarmthPerSecond = 0f;
		_nearestDistance = float.MaxValue;
		_nearestRadius = 0f;
		_nearestHasWarmth = false;
		_hasNearest = false;
		foreach (Campfire item in CampfireAura.Tracked)
		{
			if ((Object)(object)item == (Object)null || !((Component)item).gameObject.activeInHierarchy || item.Lit)
			{
				continue;
			}
			CampfireContext campfireContext2 = CampfireContext.Install(item);
			if ((Object)(object)campfireContext2 == (Object)null)
			{
				continue;
			}
			float num = PluginConfig.ToWorldUnits(PluginConfig.ProtectionRadius.Value, item.moraleBoostRadius);
			bool flag = value2 && campfireContext2.HasWarmth;
			float num2 = (flag ? PluginConfig.ToWorldUnits(PluginConfig.WarmthRadius.Value, campfireContext2.WarmthRadius) : 0f);
			Vector3 position = ((Component)item).transform.position;
			Vector3 warmthOrigin = campfireContext2.WarmthOrigin;
			foreach (Character character in _characters)
			{
				if ((Object)(object)character == (Object)null || character.isBot || character.data.dead || (!character.IsLocal && !isMaster))
				{
					continue;
				}
				Vector3 center;
				try
				{
					center = character.Center;
				}
				catch (Exception ex)
				{
					Diagnostics.Warn("[篝火] 取角色坐标失败,已跳过:" + ex.GetType().Name);
					continue;
				}
				float num3 = Vector3.Distance(position, center);
				bool flag2 = value && num3 <= num;
				bool flag3 = flag && Vector3.Distance(warmthOrigin, center) <= num2;
				if (character.IsLocal)
				{
					if (num3 < _nearestDistance)
					{
						_nearestDistance = num3;
						_nearestRadius = num;
						_nearestHasWarmth = campfireContext2.HasWarmth;
						_hasNearest = true;
					}
					if (flag2)
					{
						_localInProtection = true;
						if ((Object)(object)campfireContext == (Object)null)
						{
							campfireContext = campfireContext2;
						}
					}
					if (flag3)
					{
						_localInWarmth = true;
						_localWarmthPerSecond = campfireContext2.WarmthPerSecond;
					}
					LogLocalTransition(item, campfireContext2, flag2, flag3, num3, num);
				}
				else
				{
					if (flag2)
					{
						_hungerCovered.Add(character);
					}
					if (flag3)
					{
						_warmthCovered.Add(character);
						_warmthPerSecond = campfireContext2.WarmthPerSecond;
					}
				}
			}
		}
		ApplyLocal(localCharacter, campfireContext, deltaTime);
		if (isMaster)
		{
			ApplyRemote(deltaTime);
		}
		else
		{
			_hungerCovered.Clear();
			_warmthCovered.Clear();
		}
		ReportFirstActivation();
	}

	private void ApplyLocal(Character local, CampfireContext protector, float deltaTime)
	{
		if (!((Object)(object)local == (Object)null) && !local.data.dead)
		{
			if ((Object)(object)protector != (Object)null)
			{
				CampfireProtection.ApplyLocalHunger(protector, local);
			}
			if (_localInWarmth)
			{
				CampfireProtection.ApplyLocalWarmth(local, _localWarmthPerSecond, deltaTime);
			}
		}
	}

	private void ApplyRemote(float deltaTime)
	{
		foreach (Character character in _characters)
		{
			if (!((Object)(object)character == (Object)null) && !character.isBot && !character.IsLocal)
			{
				CampfireProtection.HoldRemoteHunger(character, _hungerCovered.Contains(character));
				if (_warmthCovered.Contains(character))
				{
					CampfireProtection.WarmRemote(character, _warmthPerSecond, deltaTime);
				}
			}
		}
	}

	private void EvictScoutmasters()
	{
		foreach (Character character in _characters)
		{
			if (!((Object)(object)character == (Object)null) && character.data.isScoutmaster)
			{
				ScoutmasterEviction.EvictInsideCampfires(character);
			}
		}
	}

	private void ReportFirstActivation()
	{
		if (!_reportedActivation && (_localInProtection || _localInWarmth))
		{
			_reportedActivation = true;
			Diagnostics.Info("[篝火] 本地玩家已进入未点燃篝火的保护范围," + $"防饥饿={_localInProtection} 取暖={_localInWarmth}");
		}
	}

	private static void LogLocalTransition(Campfire campfire, CampfireContext context, bool inProtection, bool inWarmth, float distance, float radius)
	{
		if (Diagnostics.Verbose)
		{
			if (inProtection != context.LocalInProtectionAura)
			{
				context.LocalInProtectionAura = inProtection;
				Diagnostics.Trace(inProtection ? $"[篝火] 进入 {((Object)campfire).name} 防饥饿范围,距离 {distance * CharacterStats.unitsToMeters:F1} 米 / {radius * CharacterStats.unitsToMeters:F1} 米" : ("[篝火] 离开 " + ((Object)campfire).name + " 防饥饿范围"));
			}
			if (inWarmth != context.LocalInWarmthAura)
			{
				context.LocalInWarmthAura = inWarmth;
				Diagnostics.Trace(inWarmth ? $"[篝火] 开始取暖 {((Object)campfire).name},{context.WarmthPerSecond:F3}/s" : ("[篝火] 结束取暖 " + ((Object)campfire).name));
			}
		}
	}
}
internal static class ModPresence
{
	private const string PropertyKey = "campfirehaven.host";

	private static int s_AnnouncedActor = -1;

	internal static bool HostHasMod
	{
		get
		{
			Player masterClient = PhotonNetwork.MasterClient;
			if (masterClient == null)
			{
				return false;
			}
			return ((Dictionary<object, object>)(object)masterClient.CustomProperties)?.ContainsKey((object)"campfirehaven.host") ?? false;
		}
	}

	internal static void Sync(bool inRoom, bool active)
	{
		//IL_0076: Unknown result type (might be due to invalid IL or missing references)
		//IL_007b: Unknown result type (might be due to invalid IL or missing references)
		//IL_008b: Expected O, but got Unknown
		//IL_0092: Expected O, but got Unknown
		//IL_0037: Unknown result type (might be due to invalid IL or missing references)
		//IL_003c: Unknown result type (might be due to invalid IL or missing references)
		//IL_0048: Expected O, but got Unknown
		//IL_004f: Expected O, but got Unknown
		Player localPlayer = PhotonNetwork.LocalPlayer;
		if (localPlayer == null)
		{
			s_AnnouncedActor = -1;
			return;
		}
		Hashtable customProperties = localPlayer.CustomProperties;
		if (!(inRoom && active))
		{
			((Dictionary<object, object>)(object)customProperties)?.Remove((object)"campfirehaven.host");
			if (inRoom && s_AnnouncedActor != -1)
			{
				Hashtable val = new Hashtable();
				((Dictionary<object, object>)val).Add((object)"campfirehaven.host", (object)null);
				localPlayer.SetCustomProperties(val, (Hashtable)null, (WebFlags)null);
			}
			s_AnnouncedActor = -1;
		}
		else if (s_AnnouncedActor != localPlayer.ActorNumber || customProperties == null || !((Dictionary<object, object>)(object)customProperties).ContainsKey((object)"campfirehaven.host"))
		{
			Hashtable val2 = new Hashtable();
			((Dictionary<object, object>)val2).Add((object)"campfirehaven.host", (object)"1.1.0");
			if (!localPlayer.SetCustomProperties(val2, (Hashtable)null, (WebFlags)null))
			{
				s_AnnouncedActor = -1;
			}
			else
			{
				s_AnnouncedActor = localPlayer.ActorNumber;
			}
		}
	}
}
internal static class PluginConfig
{
	internal static ConfigEntry<bool> Enabled;

	internal static ConfigEntry<bool> DebugLog;

	internal static ConfigEntry<bool> ClientFallback;

	internal static ConfigEntry<bool> ProtectFromHunger;

	internal static ConfigEntry<bool> ProtectFromCold;

	internal static ConfigEntry<float> ProtectionRadius;

	internal static ConfigEntry<float> WarmthRadius;

	internal static ConfigEntry<bool> EvictScoutmaster;

	internal static ConfigEntry<float> EvictionRadius;

	internal static bool Active
	{
		get
		{
			if (Enabled != null)
			{
				return Enabled.Value;
			}
			return true;
		}
	}

	internal static void Bind(ConfigFile config)
	{
		Enabled = config.Bind<bool>("General", "Enabled", true, "总开关,关闭后插件不产生任何效果");
		DebugLog = config.Bind<bool>("General", "DebugLog", false, "输出详细诊断日志。仅在排查问题时打开,正常游玩建议关闭");
		ClientFallback = config.Bind<bool>("General", "ClientFallback", true, "主机已经装了本模组时,非主机客户端自动让位。主机没装时,非主机是否只保护自己(关闭则非主机完全不工作)");
		ProtectFromHunger = config.Bind<bool>("Campfire", "ProtectFromHunger", true, "未点燃的篝火是否阻止饥饿");
		ProtectFromCold = config.Bind<bool>("Campfire", "ProtectFromCold", true, "未点燃的篝火是否消除寒冷");
		ProtectionRadius = config.Bind<float>("Campfire", "ProtectionRadius", 19.2f, "防饥饿半径,单位是游戏内提示显示的米。19.2 米等于原版篝火的 12 世界单位。填 0 表示沿用每座篝火自己的数值");
		WarmthRadius = config.Bind<float>("Campfire", "WarmthRadius", 6.4f, "取暖半径,单位是游戏内提示显示的米。6.4 米等于原版篝火点燃时的 4 世界单位。填 0 表示沿用每座篝火自己的数值");
		EvictScoutmaster = config.Bind<bool>("Scoutmaster", "Enabled", true, "童子军领队进入篝火范围时是否让其消失");
		EvictionRadius = config.Bind<float>("Scoutmaster", "EvictionRadius", 19.2f, "驱逐半径,单位是游戏内提示显示的米。19.2 米等于原版篝火的 12 世界单位。填 0 表示沿用每座篝火自己的数值");
	}

	internal static float ToWorldUnits(float meters, float fallbackUnits)
	{
		if (meters <= 0f)
		{
			return fallbackUnits;
		}
		float unitsToMeters = CharacterStats.unitsToMeters;
		if (!(unitsToMeters > 0f))
		{
			return meters;
		}
		return meters / unitsToMeters;
	}
}
internal static class ScoutmasterEviction
{
	internal static void EvictInsideCampfires(Character scoutmaster)
	{
		//IL_0001: Unknown result type (might be due to invalid IL or missing references)
		//IL_0006: Unknown result type (might be due to invalid IL or missing references)
		//IL_000c: Unknown result type (might be due to invalid IL or missing references)
		Vector3 center;
		try
		{
			center = scoutmaster.Center;
		}
		catch
		{
			return;
		}
		if (TryFindContainingCampfire(center, out var nearestDistance))
		{
			Evict(scoutmaster, nearestDistance);
		}
	}

	private static bool TryFindContainingCampfire(Vector3 point, out float nearestDistance)
	{
		//IL_0050: Unknown result type (might be due to invalid IL or missing references)
		//IL_0055: Unknown result type (might be due to invalid IL or missing references)
		nearestDistance = float.MaxValue;
		bool result = false;
		foreach (Campfire item in CampfireAura.Tracked)
		{
			if ((Object)(object)item == (Object)null || !((Component)item).gameObject.activeInHierarchy)
			{
				continue;
			}
			float num = PluginConfig.ToWorldUnits(PluginConfig.EvictionRadius.Value, item.moraleBoostRadius);
			float num2 = Vector3.Distance(((Component)item).transform.position, point);
			if (!(num2 > num))
			{
				result = true;
				if (num2 < nearestDistance)
				{
					nearestDistance = num2;
				}
			}
		}
		return result;
	}

	private static void Evict(Character scoutmaster, float distance)
	{
		if (PhotonNetwork.InRoom && PhotonNetwork.IsMasterClient && !((Object)(object)((MonoBehaviourPun)scoutmaster).photonView == (Object)null) && ((MonoBehaviourPun)scoutmaster).photonView.IsMine)
		{
			if ((Object)(object)scoutmaster.data.grabJoint != (Object)null)
			{
				scoutmaster.refs.view.RPC("RPCA_GrabUnattach", (RpcTarget)0, Array.Empty<object>());
			}
			Diagnostics.Info($"[篝火] 童子军领队进入保护范围({distance * CharacterStats.unitsToMeters:F1} 米),已移除");
			PhotonNetwork.Destroy(((MonoBehaviourPun)scoutmaster).photonView);
		}
	}
}
internal static class StatusRelay
{
	internal static void PushHunger(Character character, float delta)
	{
		Push(character, 1, delta);
	}

	internal static void PushCold(Character character, float delta)
	{
		Push(character, 2, delta);
	}

	private static void Push(Character character, int statusIndex, float delta)
	{
		if (!(delta >= 0f) && PhotonNetwork.InRoom && PhotonNetwork.IsMasterClient)
		{
			float[] array = new float[character.refs.afflictions.currentStatuses.Length];
			array[statusIndex] = delta;
			((MonoBehaviourPun)character).photonView.RPC("RPC_ApplyStatusesFromFloatArray", (RpcTarget)0, new object[1] { array });
		}
	}
}