using System; 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 System.Security; using System.Security.Permissions; using System.Text; using BepInEx; using BepInEx.Configuration; using BepInEx.Logging; using ExitGames.Client.Photon; using HarmonyLib; using Microsoft.CodeAnalysis; using PEAKLib.Core; using Photon.Pun; using Photon.Realtime; using PhotonCustomPropsUtils; using UnityEngine; 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("Healing Campfires")] [assembly: AssemblyDescription("")] [assembly: AssemblyConfiguration("")] [assembly: AssemblyCompany("")] [assembly: AssemblyProduct("Healing Campfires")] [assembly: AssemblyCopyright("Copyright © 2025")] [assembly: AssemblyTrademark("")] [assembly: ComVisible(false)] [assembly: Guid("56f9024b-9ced-4a82-bd45-483d74b8ea2e")] [assembly: AssemblyFileVersion("1.0.0.0")] [assembly: TargetFramework(".NETFramework,Version=v4.7.2", FrameworkDisplayName = ".NET Framework 4.7.2")] [assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)] [assembly: AssemblyVersion("1.0.0.0")] [module: UnverifiableCode] namespace Microsoft.CodeAnalysis { [CompilerGenerated] [Embedded] internal sealed class EmbeddedAttribute : Attribute { } } namespace System.Runtime.CompilerServices { [CompilerGenerated] [Embedded] [AttributeUsage(AttributeTargets.Class | AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Event | AttributeTargets.Parameter | AttributeTargets.ReturnValue | AttributeTargets.GenericParameter, AllowMultiple = false, Inherited = false)] internal sealed class NullableAttribute : Attribute { public readonly byte[] NullableFlags; public NullableAttribute(byte P_0) { NullableFlags = new byte[1] { P_0 }; } public NullableAttribute(byte[] P_0) { NullableFlags = P_0; } } [CompilerGenerated] [Embedded] [AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct | AttributeTargets.Method | AttributeTargets.Interface | AttributeTargets.Delegate, AllowMultiple = false, Inherited = false)] internal sealed class NullableContextAttribute : Attribute { public readonly byte Flag; public NullableContextAttribute(byte P_0) { Flag = P_0; } } } namespace Healing_Campfires { internal sealed class CampfireConfigSnapshot { public float HealPerSecond = 0.025f; public float MoraleBoostRadius; public bool HealAfflictionsEnabled = true; public bool HealAllStatuses = true; public bool RespawnEnabled = true; public bool BlockNegativeStatus = true; public bool BlockAllStatuses = true; public bool RemoveRestriction = true; public bool RevivePassedOut; public bool EnforceRevivePenalty; public bool DynamicMallows = true; public bool MallowDecrease = true; public bool MallowMultiplierEnabled; public int MallowMultiplier = 2; public float GlizzyChance = 0.25f; public bool DynamicBackpacks = true; public bool BackpackDecrease = true; public bool EnableSoloMoraleBoost = true; public float MoraleBoostStamina = 0.25f; public bool HealInjury; public bool HealDrowsy; public bool HealPoison; public bool HealCold; public bool HealCurse; public bool HealHunger; public bool HealHot; public bool HealThorns; public bool HealSpores; public bool HealWeb; public bool HealCrab; public bool BlockInjury; public bool BlockCold; public bool BlockPoison; public bool BlockCrab; public bool BlockCurse; public bool BlockDrowsy; public bool BlockHot; public bool BlockThorns; public bool BlockSpores; public bool BlockWeb; } internal static class CampfireConfigSyncService { public const int SchemaVersion = 9; public const char PairSeparator = ';'; public const char KeyValueSeparator = '='; public static bool HasRemoteHostConfig { get; private set; } public static void ClearRemoteHostConfig() { HasRemoteHostConfig = false; } public static void ApplyLocalAuthoritativeConfig() { if (!((Object)(object)CampfireHealingMod.Instance == (Object)null)) { ClearRemoteHostConfig(); CampfireHealingMod.Instance.ApplySnapshot(CampfireHealingMod.Instance.BuildSnapshot()); } } public static void ApplyRemoteSnapshot(string payload) { if (PhotonNetwork.IsMasterClient || string.IsNullOrEmpty(payload)) { return; } if (!TryDeserialize(payload, out var snapshot)) { ManualLogSource log = CampfireHealingMod.Log; if (log != null) { log.LogError((object)"[Healing Campfires] Failed to apply host config snapshot."); } } else { HasRemoteHostConfig = true; CampfireHealingMod.Instance.ApplySnapshot(snapshot); } } public static string Serialize(CampfireConfigSnapshot snapshot) { if (snapshot == null) { return string.Empty; } StringBuilder stringBuilder = new StringBuilder(520); AppendPair(stringBuilder, "v", 9.ToString(CultureInfo.InvariantCulture)); AppendPair(stringBuilder, "healRate", snapshot.HealPerSecond.ToString(CultureInfo.InvariantCulture)); AppendPair(stringBuilder, "moraleRadius", snapshot.MoraleBoostRadius.ToString(CultureInfo.InvariantCulture)); AppendPair(stringBuilder, "healEnabled", snapshot.HealAfflictionsEnabled ? "1" : "0"); AppendPair(stringBuilder, "healAll", snapshot.HealAllStatuses ? "1" : "0"); AppendPair(stringBuilder, "respawn", snapshot.RespawnEnabled ? "1" : "0"); AppendPair(stringBuilder, "blockStatus", snapshot.BlockNegativeStatus ? "1" : "0"); AppendPair(stringBuilder, "blockAll", snapshot.BlockAllStatuses ? "1" : "0"); AppendPair(stringBuilder, "removeRestriction", snapshot.RemoveRestriction ? "1" : "0"); AppendPair(stringBuilder, "revivePassedOut", snapshot.RevivePassedOut ? "1" : "0"); AppendPair(stringBuilder, "enforceRevivePenalty", snapshot.EnforceRevivePenalty ? "1" : "0"); AppendPair(stringBuilder, "dynMallows", snapshot.DynamicMallows ? "1" : "0"); AppendPair(stringBuilder, "mallowDecrease", snapshot.MallowDecrease ? "1" : "0"); AppendPair(stringBuilder, "mallowMultOn", snapshot.MallowMultiplierEnabled ? "1" : "0"); AppendPair(stringBuilder, "mallowMult", snapshot.MallowMultiplier.ToString(CultureInfo.InvariantCulture)); AppendPair(stringBuilder, "glizzyChance", snapshot.GlizzyChance.ToString(CultureInfo.InvariantCulture)); AppendPair(stringBuilder, "dynBackpacks", snapshot.DynamicBackpacks ? "1" : "0"); AppendPair(stringBuilder, "backpackDecrease", snapshot.BackpackDecrease ? "1" : "0"); AppendPair(stringBuilder, "soloMorale", snapshot.EnableSoloMoraleBoost ? "1" : "0"); AppendPair(stringBuilder, "moraleStamina", snapshot.MoraleBoostStamina.ToString(CultureInfo.InvariantCulture)); AppendPair(stringBuilder, "healInjury", snapshot.HealInjury ? "1" : "0"); AppendPair(stringBuilder, "healDrowsy", snapshot.HealDrowsy ? "1" : "0"); AppendPair(stringBuilder, "healPoison", snapshot.HealPoison ? "1" : "0"); AppendPair(stringBuilder, "healCold", snapshot.HealCold ? "1" : "0"); AppendPair(stringBuilder, "healCurse", snapshot.HealCurse ? "1" : "0"); AppendPair(stringBuilder, "healHunger", snapshot.HealHunger ? "1" : "0"); AppendPair(stringBuilder, "healHot", snapshot.HealHot ? "1" : "0"); AppendPair(stringBuilder, "healThorns", snapshot.HealThorns ? "1" : "0"); AppendPair(stringBuilder, "healSpores", snapshot.HealSpores ? "1" : "0"); AppendPair(stringBuilder, "healWeb", snapshot.HealWeb ? "1" : "0"); AppendPair(stringBuilder, "healCrab", snapshot.HealCrab ? "1" : "0"); AppendPair(stringBuilder, "blockInjury", snapshot.BlockInjury ? "1" : "0"); AppendPair(stringBuilder, "blockCold", snapshot.BlockCold ? "1" : "0"); AppendPair(stringBuilder, "blockPoison", snapshot.BlockPoison ? "1" : "0"); AppendPair(stringBuilder, "blockCrab", snapshot.BlockCrab ? "1" : "0"); AppendPair(stringBuilder, "blockCurse", snapshot.BlockCurse ? "1" : "0"); AppendPair(stringBuilder, "blockDrowsy", snapshot.BlockDrowsy ? "1" : "0"); AppendPair(stringBuilder, "blockHot", snapshot.BlockHot ? "1" : "0"); AppendPair(stringBuilder, "blockThorns", snapshot.BlockThorns ? "1" : "0"); AppendPair(stringBuilder, "blockSpores", snapshot.BlockSpores ? "1" : "0"); AppendPair(stringBuilder, "blockWeb", snapshot.BlockWeb ? "1" : "0"); return stringBuilder.ToString(); } public static bool TryDeserialize(string payload, out CampfireConfigSnapshot snapshot) { snapshot = new CampfireConfigSnapshot(); if (string.IsNullOrEmpty(payload)) { return false; } string[] array = payload.Split(new char[1] { ';' }); int result = 0; bool flag = false; foreach (string text in array) { if (string.IsNullOrEmpty(text)) { continue; } int num = text.IndexOf('='); if (num <= 0) { continue; } string text2 = text.Substring(0, num); string text3 = text.Substring(num + 1); switch (text2) { case "v": flag = int.TryParse(text3, NumberStyles.Integer, CultureInfo.InvariantCulture, out result); break; case "healRate": float.TryParse(text3, NumberStyles.Float, CultureInfo.InvariantCulture, out snapshot.HealPerSecond); break; case "moraleRadius": float.TryParse(text3, NumberStyles.Float, CultureInfo.InvariantCulture, out snapshot.MoraleBoostRadius); break; case "radius": if (snapshot.MoraleBoostRadius <= 0f) { float.TryParse(text3, NumberStyles.Float, CultureInfo.InvariantCulture, out snapshot.MoraleBoostRadius); } break; case "healEnabled": snapshot.HealAfflictionsEnabled = text3 == "1"; break; case "healAll": snapshot.HealAllStatuses = text3 == "1"; break; case "respawn": snapshot.RespawnEnabled = text3 == "1"; break; case "blockStatus": snapshot.BlockNegativeStatus = text3 == "1"; break; case "blockAll": snapshot.BlockAllStatuses = text3 == "1"; break; case "removeRestriction": snapshot.RemoveRestriction = text3 == "1"; break; case "revivePassedOut": snapshot.RevivePassedOut = text3 == "1"; break; case "enforceRevivePenalty": snapshot.EnforceRevivePenalty = text3 == "1"; break; case "dynMallows": snapshot.DynamicMallows = text3 == "1"; break; case "mallowDecrease": snapshot.MallowDecrease = text3 == "1"; break; case "mallowMultOn": snapshot.MallowMultiplierEnabled = text3 == "1"; break; case "mallowMult": { if (int.TryParse(text3, NumberStyles.Integer, CultureInfo.InvariantCulture, out var result4)) { snapshot.MallowMultiplier = Math.Max(1, result4); } break; } case "glizzyChance": { if (float.TryParse(text3, NumberStyles.Float, CultureInfo.InvariantCulture, out var result3)) { snapshot.GlizzyChance = Math.Max(0f, Math.Min(1f, result3)); } break; } case "dynBackpacks": snapshot.DynamicBackpacks = text3 == "1"; break; case "backpackDecrease": snapshot.BackpackDecrease = text3 == "1"; break; case "soloMorale": snapshot.EnableSoloMoraleBoost = text3 == "1"; break; case "moraleStamina": { if (float.TryParse(text3, NumberStyles.Float, CultureInfo.InvariantCulture, out var result2)) { snapshot.MoraleBoostStamina = Math.Max(0f, Math.Min(1f, result2)); } break; } case "healInjury": snapshot.HealInjury = text3 == "1"; break; case "healDrowsy": snapshot.HealDrowsy = text3 == "1"; break; case "healPoison": snapshot.HealPoison = text3 == "1"; break; case "healCold": snapshot.HealCold = text3 == "1"; break; case "healCurse": snapshot.HealCurse = text3 == "1"; break; case "healHunger": snapshot.HealHunger = text3 == "1"; break; case "healHot": snapshot.HealHot = text3 == "1"; break; case "healThorns": snapshot.HealThorns = text3 == "1"; break; case "healSpores": snapshot.HealSpores = text3 == "1"; break; case "healWeb": snapshot.HealWeb = text3 == "1"; break; case "healCrab": snapshot.HealCrab = text3 == "1"; break; case "blockInjury": snapshot.BlockInjury = text3 == "1"; break; case "blockCold": snapshot.BlockCold = text3 == "1"; break; case "blockPoison": snapshot.BlockPoison = text3 == "1"; break; case "blockCrab": snapshot.BlockCrab = text3 == "1"; break; case "blockCurse": snapshot.BlockCurse = text3 == "1"; break; case "blockDrowsy": snapshot.BlockDrowsy = text3 == "1"; break; case "blockHot": snapshot.BlockHot = text3 == "1"; break; case "blockThorns": snapshot.BlockThorns = text3 == "1"; break; case "blockSpores": snapshot.BlockSpores = text3 == "1"; break; case "blockWeb": snapshot.BlockWeb = text3 == "1"; break; } } return flag && result >= 1 && result <= 9; } private static void AppendPair(StringBuilder builder, string key, string value) { if (builder.Length > 0) { builder.Append(';'); } builder.Append(key); builder.Append('='); builder.Append(value ?? string.Empty); } } internal static class CampfireDynamicBackpackService { private sealed class CampfireBackpackState { public GameObject Root; public string PrefabName = "Backpack"; public readonly List SpawnPoints = new List(); public readonly List TrackedViews = new List(); public float RegisteredAt; } private const string FallbackPrefabName = "Backpack"; private const float NearbyPlayerSyncDistance = 80f; private const float RecentRegisterWindowSeconds = 120f; private const float ScatterMin = 0.45f; private const float ScatterMax = 1.15f; private const float CountDistance = 14f; private static readonly Dictionary StatesByRootId = new Dictionary(); private static int _lastPlayerCount = -1; private static float _deferredSyncAt = -1f; private static string _deferredSyncReason = string.Empty; private static int _deferredSyncRootId; public static void OnCampfireItemsSpawned(GameObject campfireRoot) { //IL_0026: Unknown result type (might be due to invalid IL or missing references) if (!PhotonNetwork.IsMasterClient || (Object)(object)campfireRoot == (Object)null) { return; } CampfireBackpackState campfireBackpackState = RegisterOrUpdate(campfireRoot); AttachNearbyBackpackSpawners(campfireBackpackState, campfireRoot.transform.position, 40f); if (campfireBackpackState.SpawnPoints.Count != 0) { ManualLogSource log = CampfireHealingMod.Log; if (log != null) { log.LogInfo((object)$"[Dynamic Backpacks] Registered '{((Object)campfireRoot).name}' with {campfireBackpackState.SpawnPoints.Count} backpack spawn point(s), prefab='{campfireBackpackState.PrefabName}'."); } RequestDeferredSync("campfire-items-spawned", ((Object)campfireRoot).GetInstanceID()); } } private static void AttachNearbyBackpackSpawners(CampfireBackpackState state, Vector3 center, float maxDistance) { //IL_003a: Unknown result type (might be due to invalid IL or missing references) //IL_003f: Unknown result type (might be due to invalid IL or missing references) //IL_0040: Unknown result type (might be due to invalid IL or missing references) //IL_0045: Unknown result type (might be due to invalid IL or missing references) if (state == null) { return; } float num = maxDistance * maxDistance; SingleItemSpawner[] array = Object.FindObjectsByType((FindObjectsInactive)1, (FindObjectsSortMode)0); foreach (SingleItemSpawner val in array) { if (IsBackpackSpawner(val)) { Vector3 val2 = ((Component)val).transform.position - center; if (!(((Vector3)(ref val2)).sqrMagnitude > num)) { EnsureSpawnPoint(state, ((Component)val).transform); TryReadPrefabName(((Component)val).transform, state); } } } } public static void TrackVanillaSpawnedViews(SingleItemSpawner spawner, List views) { if (!PhotonNetwork.IsMasterClient || (Object)(object)spawner == (Object)null || views == null || views.Count == 0 || !IsBackpackSpawner(spawner)) { return; } GameObject val = FindNearestCampfireRoot(((Component)spawner).transform, 40f); if ((Object)(object)val == (Object)null) { val = ((Component)spawner).gameObject; } CampfireBackpackState campfireBackpackState = RegisterOrUpdate(val); EnsureSpawnPoint(campfireBackpackState, ((Component)spawner).transform); if ((Object)(object)spawner.prefab != (Object)null && !string.IsNullOrEmpty(((Object)spawner.prefab).name)) { campfireBackpackState.PrefabName = ((Object)spawner.prefab).name; } for (int i = 0; i < views.Count; i++) { PhotonView val2 = views[i]; if ((Object)(object)val2 == (Object)null) { continue; } bool flag = false; for (int j = 0; j < campfireBackpackState.TrackedViews.Count; j++) { if ((Object)(object)campfireBackpackState.TrackedViews[j] != (Object)null && campfireBackpackState.TrackedViews[j].ViewID == val2.ViewID) { flag = true; break; } } if (!flag) { campfireBackpackState.TrackedViews.Add(val2); } } RequestDeferredSync("backpack-spawned", ((Object)val).GetInstanceID()); } public static void RequestDeferredSync(string reason, int rootId = 0) { _deferredSyncReason = reason ?? "sync"; _deferredSyncAt = Time.time + 0.75f; _deferredSyncRootId = rootId; } public static void Tick() { if (!PhotonNetwork.IsMasterClient || !PhotonNetwork.InRoom || !CampfireHealingMod.syncedDynamicBackpacks) { return; } int roomPlayerCount = CampfireDynamicMallowService.GetRoomPlayerCount(); if (roomPlayerCount != _lastPlayerCount) { int lastPlayerCount = _lastPlayerCount; _lastPlayerCount = roomPlayerCount; ManualLogSource log = CampfireHealingMod.Log; if (log != null) { log.LogInfo((object)$"[Dynamic Backpacks] Player count {lastPlayerCount} -> {roomPlayerCount}. Target={roomPlayerCount}."); } RequestDeferredSync("player-count-changed"); } if (!(_deferredSyncAt < 0f) && !(Time.time < _deferredSyncAt)) { _deferredSyncAt = -1f; string deferredSyncReason = _deferredSyncReason; int deferredSyncRootId = _deferredSyncRootId; _deferredSyncReason = string.Empty; _deferredSyncRootId = 0; if (deferredSyncRootId != 0) { SyncOneCampfire(deferredSyncRootId, deferredSyncReason); } else { SyncRelevantCampfires(deferredSyncReason); } } } public static void SyncAllCampfiresNow(string reason) { if (PhotonNetwork.IsMasterClient && CampfireHealingMod.syncedDynamicBackpacks) { SyncRelevantCampfires(reason ?? "manual"); } } private static void SyncOneCampfire(int rootId, string reason) { if (CampfireHealingMod.IsModActive && CampfireHealingMod.syncedDynamicBackpacks && StatesByRootId.ContainsKey(rootId)) { ManualLogSource log = CampfireHealingMod.Log; if (log != null) { log.LogInfo((object)$"[Dynamic Backpacks] Syncing 1 campfire ({reason}). players={CampfireDynamicMallowService.GetRoomPlayerCount()}."); } SyncCampfire(rootId); } } private static void SyncRelevantCampfires(string reason) { if (!CampfireHealingMod.IsModActive || !CampfireHealingMod.syncedDynamicBackpacks) { return; } if (StatesByRootId.Count == 0) { DiscoverCampfires(); } List list = new List(); foreach (KeyValuePair item in StatesByRootId) { if (IsRelevantForPlayerCountSync(item.Value)) { list.Add(item.Key); } } ManualLogSource log = CampfireHealingMod.Log; if (log != null) { log.LogInfo((object)$"[Dynamic Backpacks] Syncing {list.Count}/{StatesByRootId.Count} relevant campfire(s) ({reason}). players={CampfireDynamicMallowService.GetRoomPlayerCount()}, decrease={CampfireHealingMod.syncedBackpackDecrease}."); } for (int i = 0; i < list.Count; i++) { SyncCampfire(list[i]); } } private static bool IsRelevantForPlayerCountSync(CampfireBackpackState state) { //IL_0089: Unknown result type (might be due to invalid IL or missing references) //IL_008e: Unknown result type (might be due to invalid IL or missing references) //IL_00c0: Unknown result type (might be due to invalid IL or missing references) //IL_00c5: Unknown result type (might be due to invalid IL or missing references) //IL_00c6: 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) if (state == null || (Object)(object)state.Root == (Object)null || !state.Root.activeInHierarchy) { return false; } if (Time.time - state.RegisteredAt <= 120f) { return true; } Campfire componentInChildren = state.Root.GetComponentInChildren(true); if ((Object)(object)componentInChildren != (Object)null && componentInChildren.Lit && !CampfireHealingMod.CampfireHelper.IsPortableStove(componentInChildren)) { return true; } Vector3 spawnCenter = GetSpawnCenter(state); float num = 6400f; foreach (Character allPlayerCharacter in PlayerHandler.GetAllPlayerCharacters()) { if (!((Object)(object)allPlayerCharacter == (Object)null)) { Vector3 val = allPlayerCharacter.Center - spawnCenter; if (((Vector3)(ref val)).sqrMagnitude <= num) { return true; } } } return false; } private static void DiscoverCampfires() { SingleItemSpawner[] array = Object.FindObjectsByType((FindObjectsInactive)1, (FindObjectsSortMode)0); foreach (SingleItemSpawner val in array) { if (IsBackpackSpawner(val)) { GameObject campfireRoot = FindNearestCampfireRoot(((Component)val).transform, 40f) ?? ((Component)val).gameObject; CampfireBackpackState state = RegisterOrUpdate(campfireRoot); EnsureSpawnPoint(state, ((Component)val).transform); TryReadPrefabName(((Component)val).transform, state); } } } private static bool IsBackpackSpawner(SingleItemSpawner spawner) { return (Object)(object)spawner != (Object)null && IsBackpackObjectName(((Object)((Component)spawner).gameObject).name); } private static bool IsBackpackObjectName(string name) { return !string.IsNullOrEmpty(name) && name.IndexOf("Backpack", StringComparison.OrdinalIgnoreCase) >= 0; } private static GameObject FindNearestCampfireRoot(Transform start, float maxDistance) { //IL_0058: Unknown result type (might be due to invalid IL or missing references) //IL_005d: Unknown result type (might be due to invalid IL or missing references) //IL_0097: Unknown result type (might be due to invalid IL or missing references) //IL_009c: Unknown result type (might be due to invalid IL or missing references) //IL_009d: Unknown result type (might be due to invalid IL or missing references) //IL_00a2: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)start == (Object)null) { return null; } Transform val = start; while ((Object)(object)val != (Object)null) { if ((Object)(object)((Component)val).GetComponent() != (Object)null) { return ((Component)val).gameObject; } val = val.parent; } Campfire[] array = Object.FindObjectsByType((FindObjectsInactive)1, (FindObjectsSortMode)0); Vector3 position = start.position; float num = maxDistance * maxDistance; Campfire val2 = null; float num2 = num; foreach (Campfire val3 in array) { if (!((Object)(object)val3 == (Object)null) && !CampfireHealingMod.CampfireHelper.IsPortableStove(val3)) { Vector3 val4 = ((Component)val3).transform.position - position; float sqrMagnitude = ((Vector3)(ref val4)).sqrMagnitude; if (sqrMagnitude <= num2) { num2 = sqrMagnitude; val2 = val3; } } } return ((Object)(object)val2 != (Object)null) ? ((Component)val2).gameObject : null; } private static void EnsureSpawnPoint(CampfireBackpackState state, Transform point) { if (state == null || (Object)(object)point == (Object)null) { return; } for (int i = 0; i < state.SpawnPoints.Count; i++) { if ((Object)(object)state.SpawnPoints[i] == (Object)(object)point) { return; } } state.SpawnPoints.Add(point); } private static CampfireBackpackState RegisterOrUpdate(GameObject campfireRoot) { int instanceID = ((Object)campfireRoot).GetInstanceID(); if (!StatesByRootId.TryGetValue(instanceID, out var value)) { value = new CampfireBackpackState { Root = campfireRoot, RegisteredAt = Time.time }; StatesByRootId[instanceID] = value; } else { value.Root = campfireRoot; value.RegisteredAt = Time.time; } if (string.IsNullOrEmpty(value.PrefabName)) { value.PrefabName = "Backpack"; } SingleItemSpawner[] componentsInChildren = campfireRoot.GetComponentsInChildren(true); foreach (SingleItemSpawner val in componentsInChildren) { if (IsBackpackSpawner(val)) { EnsureSpawnPoint(value, ((Component)val).transform); TryReadPrefabName(((Component)val).transform, value); } } return value; } private static void TryReadPrefabName(Transform t, CampfireBackpackState state) { if ((Object)(object)t == (Object)null || state == null) { return; } SingleItemSpawner component = ((Component)t).GetComponent(); if ((Object)(object)component != (Object)null && (Object)(object)component.prefab != (Object)null && !string.IsNullOrEmpty(((Object)component.prefab).name)) { state.PrefabName = ((Object)component.prefab).name; return; } Component[] components = ((Component)t).GetComponents(); foreach (Component val in components) { if ((Object)(object)val == (Object)null) { continue; } FieldInfo field = ((object)val).GetType().GetField("prefab"); if (!(field == null) && !(field.FieldType != typeof(GameObject))) { object? value = field.GetValue(val); GameObject val2 = (GameObject)((value is GameObject) ? value : null); if ((Object)(object)val2 != (Object)null && !string.IsNullOrEmpty(((Object)val2).name)) { state.PrefabName = ((Object)val2).name; break; } } } } private static void SyncCampfire(int rootId) { if (!StatesByRootId.TryGetValue(rootId, out var value)) { return; } if ((Object)(object)value.Root == (Object)null) { StatesByRootId.Remove(rootId); return; } if (value.SpawnPoints.Count == 0) { RegisterOrUpdate(value.Root); } if (value.SpawnPoints.Count == 0) { return; } PruneTracked(value); int num = CountPresentBackpacks(value); int roomPlayerCount = CampfireDynamicMallowService.GetRoomPlayerCount(); int num2 = 0; int num3 = 0; while (num < roomPlayerCount && TrySpawnOne(value)) { num2++; num = CountPresentBackpacks(value); } num = CountPresentBackpacks(value); if (CampfireHealingMod.syncedBackpackDecrease) { while (num > roomPlayerCount && TryDestroyOneExtra(value)) { num3++; num = CountPresentBackpacks(value); } } ManualLogSource log = CampfireHealingMod.Log; if (log != null) { log.LogInfo((object)$"[Dynamic Backpacks] '{((Object)value.Root).name}': present~{num}, target={roomPlayerCount}, spawned+{num2}, destroyed-{num3}, tracked={value.TrackedViews.Count}, prefab='{value.PrefabName}'."); } } private static void PruneTracked(CampfireBackpackState state) { for (int num = state.TrackedViews.Count - 1; num >= 0; num--) { if ((Object)(object)state.TrackedViews[num] == (Object)null) { state.TrackedViews.RemoveAt(num); } } } private static bool MatchesBackpack(Item item) { if ((Object)(object)item == (Object)null) { return false; } if (item is Backpack) { return true; } string text = (((Object)(object)((Component)item).gameObject != (Object)null) ? ((Object)((Component)item).gameObject).name : string.Empty); string text2 = ((Object)item).name ?? string.Empty; return text.IndexOf("Backpack", StringComparison.OrdinalIgnoreCase) >= 0 || text2.IndexOf("Backpack", StringComparison.OrdinalIgnoreCase) >= 0; } private static int CountPresentBackpacks(CampfireBackpackState state) { //IL_0011: Unknown result type (might be due to invalid IL or missing references) //IL_0016: Unknown result type (might be due to invalid IL or missing references) //IL_00fd: Unknown result type (might be due to invalid IL or missing references) //IL_0102: Unknown result type (might be due to invalid IL or missing references) PruneTracked(state); HashSet hashSet = new HashSet(); int num = 0; Vector3 spawnCenter = GetSpawnCenter(state); for (int i = 0; i < state.TrackedViews.Count; i++) { PhotonView val = state.TrackedViews[i]; if (!((Object)(object)val == (Object)null) && hashSet.Add(val.ViewID) && !IsClaimedOrGone(val)) { num++; } } Item[] array = Object.FindObjectsByType((FindObjectsInactive)0, (FindObjectsSortMode)0); foreach (Item val2 in array) { if (!((Object)(object)val2 == (Object)null) && !((Object)(object)((MonoBehaviourPun)val2).photonView == (Object)null) && MatchesBackpack(val2) && hashSet.Add(((MonoBehaviourPun)val2).photonView.ViewID) && !IsClaimedOrGone(((MonoBehaviourPun)val2).photonView) && !(Vector3.Distance(((Component)val2).transform.position, spawnCenter) > 14f)) { state.TrackedViews.Add(((MonoBehaviourPun)val2).photonView); num++; } } return num; } private static bool IsClaimedOrGone(PhotonView view) { //IL_003d: Unknown result type (might be due to invalid IL or missing references) //IL_0043: Invalid comparison between Unknown and I4 //IL_0046: Unknown result type (might be due to invalid IL or missing references) //IL_004c: Invalid comparison between Unknown and I4 if ((Object)(object)view == (Object)null) { return true; } Item component = ((Component)view).GetComponent(); if ((Object)(object)component == (Object)null) { return true; } if ((Object)(object)component.holderCharacter != (Object)null) { return true; } return (int)component.itemState == 1 || (int)component.itemState == 2; } private static Vector3 GetSpawnCenter(CampfireBackpackState state) { //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_0028: Unknown result type (might be due to invalid IL or missing references) //IL_0035: Unknown result type (might be due to invalid IL or missing references) //IL_003a: Unknown result type (might be due to invalid IL or missing references) //IL_003f: Unknown result type (might be due to invalid IL or missing references) //IL_0067: Unknown result type (might be due to invalid IL or missing references) //IL_006a: Unknown result type (might be due to invalid IL or missing references) //IL_006f: 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_009f: Unknown result type (might be due to invalid IL or missing references) //IL_00a4: Unknown result type (might be due to invalid IL or missing references) //IL_00c8: Unknown result type (might be due to invalid IL or missing references) //IL_00b6: Unknown result type (might be due to invalid IL or missing references) //IL_00cd: Unknown result type (might be due to invalid IL or missing references) Vector3 val = Vector3.zero; int num = 0; for (int i = 0; i < state.SpawnPoints.Count; i++) { if (!((Object)(object)state.SpawnPoints[i] == (Object)null)) { val += state.SpawnPoints[i].position; num++; } } if (num > 0) { return val / (float)num; } Campfire val2 = (((Object)(object)state.Root != (Object)null) ? state.Root.GetComponentInChildren(true) : null); if ((Object)(object)val2 != (Object)null) { return val2.Center(); } return ((Object)(object)state.Root != (Object)null) ? state.Root.transform.position : Vector3.zero; } private static bool TrySpawnOne(CampfireBackpackState state) { //IL_0083: Unknown result type (might be due to invalid IL or missing references) //IL_0088: Unknown result type (might be due to invalid IL or missing references) //IL_008d: Unknown result type (might be due to invalid IL or missing references) //IL_00a7: Unknown result type (might be due to invalid IL or missing references) //IL_00ac: 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_00b1: Unknown result type (might be due to invalid IL or missing references) //IL_0149: Unknown result type (might be due to invalid IL or missing references) //IL_015d: Unknown result type (might be due to invalid IL or missing references) if (state.SpawnPoints.Count == 0) { return false; } int index = state.TrackedViews.Count % state.SpawnPoints.Count; Transform val = state.SpawnPoints[index] ?? state.SpawnPoints[0]; if ((Object)(object)val == (Object)null) { return false; } string text = (string.IsNullOrEmpty(state.PrefabName) ? "Backpack" : state.PrefabName); Vector3 scatteredPosition = GetScatteredPosition(val.position); Quaternion val2 = Quaternion.Euler(0f, Random.Range(0f, 360f), 0f); try { GameObject val3 = PhotonNetwork.InstantiateItemRoom(text, scatteredPosition, val2); if ((Object)(object)val3 == (Object)null) { ManualLogSource log = CampfireHealingMod.Log; if (log != null) { log.LogWarning((object)("[Dynamic Backpacks] InstantiateItemRoom returned null for '" + text + "'.")); } return false; } PhotonView component = val3.GetComponent(); if ((Object)(object)component == (Object)null) { return false; } Item component2 = val3.GetComponent(); if ((Object)(object)component2 != (Object)null) { component.RPC("SetKinematicRPC", (RpcTarget)3, new object[3] { true, val3.transform.position, val3.transform.rotation }); } state.TrackedViews.Add(component); return true; } catch (Exception ex) { ManualLogSource log2 = CampfireHealingMod.Log; if (log2 != null) { log2.LogWarning((object)("[Dynamic Backpacks] Spawn failed for '" + text + "': " + ex.Message)); } return false; } } private static Vector3 GetScatteredPosition(Vector3 basePosition) { //IL_0021: Unknown result type (might be due to invalid IL or missing references) //IL_0030: Unknown result type (might be due to invalid IL or missing references) //IL_003c: Unknown result type (might be due to invalid IL or missing references) //IL_004b: Unknown result type (might be due to invalid IL or missing references) //IL_0050: Unknown result type (might be due to invalid IL or missing references) //IL_0053: Unknown result type (might be due to invalid IL or missing references) float num = Random.Range(0f, (float)Math.PI * 2f); float num2 = Random.Range(0.45f, 1.15f); return new Vector3(basePosition.x + Mathf.Cos(num) * num2, basePosition.y + 0.05f, basePosition.z + Mathf.Sin(num) * num2); } private static bool TryDestroyOneExtra(CampfireBackpackState state) { //IL_0067: Unknown result type (might be due to invalid IL or missing references) //IL_006c: 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) for (int num = state.TrackedViews.Count - 1; num >= 0; num--) { PhotonView val = state.TrackedViews[num]; state.TrackedViews.RemoveAt(num); if (!((Object)(object)val == (Object)null) && !IsClaimedOrGone(val)) { PhotonNetwork.Destroy(val); return true; } } Vector3 spawnCenter = GetSpawnCenter(state); Item[] array = Object.FindObjectsByType((FindObjectsSortMode)0); for (int num2 = array.Length - 1; num2 >= 0; num2--) { Item val2 = array[num2]; if (!((Object)(object)val2 == (Object)null) && !((Object)(object)((MonoBehaviourPun)val2).photonView == (Object)null) && MatchesBackpack(val2) && !IsClaimedOrGone(((MonoBehaviourPun)val2).photonView) && !(Vector3.Distance(((Component)val2).transform.position, spawnCenter) > 14f)) { PhotonNetwork.Destroy(((MonoBehaviourPun)val2).photonView); return true; } } return false; } } [HarmonyPatch(typeof(MapHandler), "SpawnCampfireItems")] internal static class MapHandlerSpawnCampfireItemsBackpackPatch { private static void Postfix(GameObject campfireRoot) { CampfireDynamicBackpackService.OnCampfireItemsSpawned(campfireRoot); } } [HarmonyPatch(typeof(SingleItemSpawner), "TrySpawnItems")] internal static class SingleItemSpawnerTrySpawnItemsBackpackPatch { private static void Postfix(SingleItemSpawner __instance, List __result) { CampfireDynamicBackpackService.TrackVanillaSpawnedViews(__instance, __result); } } internal static class CampfireDynamicMallowService { private sealed class CampfireMallowState { public GameObject Root; public string PrefabName = "Marshmallow"; public readonly List SpawnPoints = new List(); public readonly List TrackedViews = new List(); public float RegisteredAt; public float LastSyncedAt = -1f; } private const string MarshmallowPrefabName = "Marshmallow"; private const string GlizzyPrefabName = "Glizzy"; private const string FallbackPrefabName = "Marshmallow"; private const float NearbyPlayerSyncDistance = 80f; private const float RecentRegisterWindowSeconds = 120f; private const float CircleInnerRadius = 1.2f; private const float CircleRingGap = 0.75f; private const float CircleMinSpacing = 0.7f; private static readonly Dictionary StatesByRootId = new Dictionary(); private static int _lastPlayerCount = -1; private static float _deferredSyncAt = -1f; private static string _deferredSyncReason = string.Empty; private static int _deferredSyncRootId; public static int GetRoomPlayerCount() { if (PhotonNetwork.CurrentRoom != null) { return Mathf.Max(1, PhotonNetwork.CurrentRoom.PlayerCount); } if (PhotonNetwork.PlayerList != null && PhotonNetwork.PlayerList.Length != 0) { return PhotonNetwork.PlayerList.Length; } return 1; } public static int GetTargetMallowCount() { int roomPlayerCount = GetRoomPlayerCount(); int num = 1; if (CampfireHealingMod.syncedMallowMultiplierEnabled) { num = Mathf.Max(1, CampfireHealingMod.syncedMallowMultiplier); } return roomPlayerCount * num; } public static void OnCampfireItemsSpawned(GameObject campfireRoot) { if (PhotonNetwork.IsMasterClient && !((Object)(object)campfireRoot == (Object)null)) { CampfireMallowState campfireMallowState = RegisterOrUpdate(campfireRoot); ManualLogSource log = CampfireHealingMod.Log; if (log != null) { log.LogInfo((object)$"[Dynamic Mallows] Registered '{((Object)campfireRoot).name}' with {campfireMallowState.SpawnPoints.Count} food spawn point(s), prefab='{campfireMallowState.PrefabName}'."); } RequestDeferredSync("campfire-items-spawned", ((Object)campfireRoot).GetInstanceID()); } } public static bool TrySpawnCampfireFoodWithGlizzyChance(SingleItemSpawner spawner, out List result) { //IL_0212: Unknown result type (might be due to invalid IL or missing references) //IL_0214: 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) //IL_014e: Unknown result type (might be due to invalid IL or missing references) //IL_0153: Unknown result type (might be due to invalid IL or missing references) //IL_015d: Unknown result type (might be due to invalid IL or missing references) //IL_0162: Unknown result type (might be due to invalid IL or missing references) //IL_0172: Unknown result type (might be due to invalid IL or missing references) //IL_0185: Unknown result type (might be due to invalid IL or missing references) //IL_0187: Unknown result type (might be due to invalid IL or missing references) //IL_017d: Unknown result type (might be due to invalid IL or missing references) //IL_018c: Unknown result type (might be due to invalid IL or missing references) //IL_0194: Unknown result type (might be due to invalid IL or missing references) //IL_0196: Unknown result type (might be due to invalid IL or missing references) result = null; if (!PhotonNetwork.IsMasterClient || !CampfireHealingMod.IsModActive || !CampfireHealingMod.syncedDynamicMallows || (Object)(object)spawner == (Object)null) { return false; } if (!IsCampfireFoodSpawner(spawner)) { return false; } Component component = ((Component)spawner).GetComponent("SpawnedItemTracker"); if ((Object)(object)component != (Object)null) { PropertyInfo property = ((object)component).GetType().GetProperty("HasSpawnHistory"); bool flag = default(bool); int num; if (property != null) { object value = property.GetValue(component); if (value is bool) { flag = (bool)value; num = 1; } else { num = 0; } } else { num = 0; } if (((uint)num & (flag ? 1u : 0u)) != 0) { return false; } } if (spawner.belowAscentRequirement != -1 && Ascents.currentAscent >= spawner.belowAscentRequirement) { result = new List(); return true; } if (spawner.playersInRoomRequirement > PhotonNetwork.PlayerList.Length) { result = new List(); return true; } GameObject val = FindCampfireRoot(((Component)spawner).transform); CampfireMallowState campfireMallowState = (((Object)(object)val != (Object)null) ? RegisterOrUpdate(val) : null); int slotIndex = ((campfireMallowState != null) ? CountPresentMallows(campfireMallowState) : 0); int targetMallowCount = GetTargetMallowCount(); string text = PickFoodPrefabName(); Vector3 val2 = ((campfireMallowState != null) ? GetCircleSpawnPosition(campfireMallowState, slotIndex, targetMallowCount) : (((Component)spawner).transform.position + Vector3.up * 0.1f)); Quaternion val3 = ((campfireMallowState != null) ? GetOutwardSpawnRotation(campfireMallowState, val2) : ((Component)spawner).transform.rotation); PhotonView val4 = null; try { GameObject val5 = PhotonNetwork.InstantiateItemRoom(text, val2, val3); if ((Object)(object)val5 != (Object)null) { val4 = val5.GetComponent(); } } catch (Exception ex) { ManualLogSource log = CampfireHealingMod.Log; if (log != null) { log.LogWarning((object)("[Dynamic Mallows] Vanilla-override spawn failed for '" + text + "': " + ex.Message)); } } if ((Object)(object)val4 == (Object)null && !string.Equals(text, "Marshmallow", StringComparison.OrdinalIgnoreCase)) { try { GameObject val6 = PhotonNetwork.InstantiateItemRoom("Marshmallow", val2, val3); if ((Object)(object)val6 != (Object)null) { val4 = val6.GetComponent(); } } catch (Exception ex2) { ManualLogSource log2 = CampfireHealingMod.Log; if (log2 != null) { log2.LogWarning((object)("[Dynamic Mallows] Marshmallow fallback failed: " + ex2.Message)); } } } if ((Object)(object)val4 == (Object)null) { result = new List(); return true; } spawner.InitializePhysics(val4); result = new List { val4 }; if ((Object)(object)component != (Object)null) { MethodInfo method = ((object)component).GetType().GetMethod("TrackSpawnedItems"); if (method != null) { method.Invoke(component, new object[1] { result }); } } return true; } public static void TrackVanillaSpawnedViews(SingleItemSpawner spawner, List views) { if (!PhotonNetwork.IsMasterClient || (Object)(object)spawner == (Object)null || views == null || views.Count == 0) { return; } GameObject val = FindCampfireRoot(((Component)spawner).transform); if ((Object)(object)val == (Object)null || !IsCampfireFoodSpawner(spawner)) { return; } CampfireMallowState campfireMallowState = RegisterOrUpdate(val); if ((Object)(object)spawner.prefab != (Object)null && !string.IsNullOrEmpty(((Object)spawner.prefab).name)) { campfireMallowState.PrefabName = ((Object)spawner.prefab).name; } for (int i = 0; i < views.Count; i++) { PhotonView val2 = views[i]; if ((Object)(object)val2 == (Object)null) { continue; } bool flag = false; for (int j = 0; j < campfireMallowState.TrackedViews.Count; j++) { if ((Object)(object)campfireMallowState.TrackedViews[j] != (Object)null && campfireMallowState.TrackedViews[j].ViewID == val2.ViewID) { flag = true; break; } } if (!flag) { campfireMallowState.TrackedViews.Add(val2); } } } private static bool IsCampfireFoodSpawner(SingleItemSpawner spawner) { if ((Object)(object)spawner == (Object)null) { return false; } if (IsCampfireFoodObjectName(((Object)((Component)spawner).gameObject).name)) { return true; } Transform parent = ((Component)spawner).transform.parent; return (Object)(object)parent != (Object)null && ((Object)parent).name.IndexOf("Mallow", StringComparison.OrdinalIgnoreCase) >= 0 && (Object)(object)FindCampfireRoot(((Component)spawner).transform) != (Object)null; } public static void RequestDeferredSync(string reason, int rootId = 0) { _deferredSyncReason = reason ?? "sync"; _deferredSyncAt = Time.time + 0.75f; _deferredSyncRootId = rootId; } public static void Tick() { if (!PhotonNetwork.IsMasterClient || !PhotonNetwork.InRoom || !CampfireHealingMod.syncedDynamicMallows) { return; } int roomPlayerCount = GetRoomPlayerCount(); if (roomPlayerCount != _lastPlayerCount) { int lastPlayerCount = _lastPlayerCount; _lastPlayerCount = roomPlayerCount; ManualLogSource log = CampfireHealingMod.Log; if (log != null) { log.LogInfo((object)$"[Dynamic Mallows] Player count {lastPlayerCount} -> {roomPlayerCount}. Target={GetTargetMallowCount()}."); } RequestDeferredSync("player-count-changed"); } if (!(_deferredSyncAt < 0f) && !(Time.time < _deferredSyncAt)) { _deferredSyncAt = -1f; string deferredSyncReason = _deferredSyncReason; int deferredSyncRootId = _deferredSyncRootId; _deferredSyncReason = string.Empty; _deferredSyncRootId = 0; if (deferredSyncRootId != 0) { SyncOneCampfire(deferredSyncRootId, deferredSyncReason); } else { SyncRelevantCampfires(deferredSyncReason); } } } public static void SyncAllCampfiresNow(string reason) { if (PhotonNetwork.IsMasterClient && CampfireHealingMod.syncedDynamicMallows) { SyncRelevantCampfires(reason ?? "manual"); } } private static void SyncOneCampfire(int rootId, string reason) { if (CampfireHealingMod.IsModActive && CampfireHealingMod.syncedDynamicMallows && StatesByRootId.ContainsKey(rootId)) { ManualLogSource log = CampfireHealingMod.Log; if (log != null) { log.LogInfo((object)$"[Dynamic Mallows] Syncing 1 campfire ({reason}). players={GetRoomPlayerCount()}, target={GetTargetMallowCount()}."); } SyncCampfire(rootId); } } private static void SyncRelevantCampfires(string reason) { if (!CampfireHealingMod.IsModActive || !CampfireHealingMod.syncedDynamicMallows) { ManualLogSource log = CampfireHealingMod.Log; if (log != null) { log.LogInfo((object)$"[Dynamic Mallows] Skip sync ({reason}): modActive={CampfireHealingMod.IsModActive}, enabled={CampfireHealingMod.syncedDynamicMallows}."); } return; } if (StatesByRootId.Count == 0) { DiscoverCampfires(); } List list = new List(); foreach (KeyValuePair item in StatesByRootId) { if (IsRelevantForPlayerCountSync(item.Value)) { list.Add(item.Key); } } ManualLogSource log2 = CampfireHealingMod.Log; if (log2 != null) { log2.LogInfo((object)$"[Dynamic Mallows] Syncing {list.Count}/{StatesByRootId.Count} relevant campfire(s) ({reason}). players={GetRoomPlayerCount()}, target={GetTargetMallowCount()}, decrease={CampfireHealingMod.syncedMallowDecrease}, multOn={CampfireHealingMod.syncedMallowMultiplierEnabled}, mult={CampfireHealingMod.syncedMallowMultiplier}."); } for (int i = 0; i < list.Count; i++) { SyncCampfire(list[i]); } } private static bool IsRelevantForPlayerCountSync(CampfireMallowState state) { //IL_0089: Unknown result type (might be due to invalid IL or missing references) //IL_008e: Unknown result type (might be due to invalid IL or missing references) //IL_00c0: Unknown result type (might be due to invalid IL or missing references) //IL_00c5: Unknown result type (might be due to invalid IL or missing references) //IL_00c6: 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) if (state == null || (Object)(object)state.Root == (Object)null || !state.Root.activeInHierarchy) { return false; } if (Time.time - state.RegisteredAt <= 120f) { return true; } Campfire componentInChildren = state.Root.GetComponentInChildren(true); if ((Object)(object)componentInChildren != (Object)null && componentInChildren.Lit && !CampfireHealingMod.CampfireHelper.IsPortableStove(componentInChildren)) { return true; } Vector3 mallowsCenter = GetMallowsCenter(state); float num = 6400f; foreach (Character allPlayerCharacter in PlayerHandler.GetAllPlayerCharacters()) { if (!((Object)(object)allPlayerCharacter == (Object)null)) { Vector3 val = allPlayerCharacter.Center - mallowsCenter; if (((Vector3)(ref val)).sqrMagnitude <= num) { return true; } } } return false; } private static void DiscoverCampfires() { Transform[] array = Object.FindObjectsByType((FindObjectsInactive)1, (FindObjectsSortMode)0); foreach (Transform val in array) { if (!((Object)(object)val == (Object)null) && IsCampfireFoodObjectName(((Object)val).name)) { GameObject val2 = FindCampfireRoot(val); if ((Object)(object)val2 != (Object)null) { RegisterOrUpdate(val2); } } } } private static bool IsCampfireFoodObjectName(string name) { return !string.IsNullOrEmpty(name) && name.IndexOf("CampfireFood", StringComparison.OrdinalIgnoreCase) >= 0; } private static GameObject FindCampfireRoot(Transform start) { Transform val = start; GameObject result = null; while ((Object)(object)val != (Object)null) { if ((Object)(object)((Component)val).GetComponentInChildren(true) != (Object)null) { result = ((Component)val).gameObject; if (((Object)val).name.IndexOf("Campfire", StringComparison.OrdinalIgnoreCase) >= 0) { return ((Component)val).gameObject; } } val = val.parent; } return result; } private static CampfireMallowState RegisterOrUpdate(GameObject campfireRoot) { int instanceID = ((Object)campfireRoot).GetInstanceID(); if (!StatesByRootId.TryGetValue(instanceID, out var value)) { value = new CampfireMallowState { Root = campfireRoot, RegisteredAt = Time.time }; StatesByRootId[instanceID] = value; } else { value.Root = campfireRoot; value.RegisteredAt = Time.time; } value.SpawnPoints.Clear(); if (string.IsNullOrEmpty(value.PrefabName)) { value.PrefabName = "Marshmallow"; } Transform[] componentsInChildren = campfireRoot.GetComponentsInChildren(true); foreach (Transform val in componentsInChildren) { if (!((Object)(object)val == (Object)null) && IsCampfireFoodObjectName(((Object)val).name)) { value.SpawnPoints.Add(val); TryReadPrefabName(val, value); } } if (value.SpawnPoints.Count == 0) { Component[] componentsInChildren2 = campfireRoot.GetComponentsInChildren(typeof(ISpawner), true); foreach (Component val2 in componentsInChildren2) { if (!((Object)(object)val2 == (Object)null)) { Transform parent = val2.transform.parent; if (((Object)(object)parent != (Object)null && ((Object)parent).name.IndexOf("Mallow", StringComparison.OrdinalIgnoreCase) >= 0) || IsCampfireFoodObjectName(((Object)val2.gameObject).name)) { value.SpawnPoints.Add(val2.transform); TryReadPrefabName(val2.transform, value); } } } } if (value.SpawnPoints.Count == 0) { value.SpawnPoints.Add(campfireRoot.transform); ManualLogSource log = CampfireHealingMod.Log; if (log != null) { log.LogWarning((object)("[Dynamic Mallows] '" + ((Object)campfireRoot).name + "' had no CampfireFood children — using root transform. Child dump follows.")); } LogChildNames(campfireRoot.transform, 0, 3); } return value; } private static void TryReadPrefabName(Transform t, CampfireMallowState state) { if ((Object)(object)t == (Object)null || state == null) { return; } SingleItemSpawner component = ((Component)t).GetComponent(); if ((Object)(object)component != (Object)null && (Object)(object)component.prefab != (Object)null && !string.IsNullOrEmpty(((Object)component.prefab).name)) { state.PrefabName = ((Object)component.prefab).name; return; } Component[] components = ((Component)t).GetComponents(); foreach (Component val in components) { if ((Object)(object)val == (Object)null) { continue; } FieldInfo field = ((object)val).GetType().GetField("prefab"); if (!(field == null) && !(field.FieldType != typeof(GameObject))) { object? value = field.GetValue(val); GameObject val2 = (GameObject)((value is GameObject) ? value : null); if ((Object)(object)val2 != (Object)null && !string.IsNullOrEmpty(((Object)val2).name)) { state.PrefabName = ((Object)val2).name; break; } } } } private static void LogChildNames(Transform root, int depth, int maxDepth) { if ((Object)(object)root == (Object)null || depth > maxDepth) { return; } for (int i = 0; i < root.childCount; i++) { Transform child = root.GetChild(i); ManualLogSource log = CampfireHealingMod.Log; if (log != null) { log.LogInfo((object)("[Dynamic Mallows] " + new string(' ', depth * 2) + "- " + ((Object)child).name)); } LogChildNames(child, depth + 1, maxDepth); } } private static void SyncCampfire(int rootId) { if (!StatesByRootId.TryGetValue(rootId, out var value)) { return; } if ((Object)(object)value.Root == (Object)null) { StatesByRootId.Remove(rootId); return; } if (value.SpawnPoints.Count == 0) { RegisterOrUpdate(value.Root); } if (value.SpawnPoints.Count == 0) { ManualLogSource log = CampfireHealingMod.Log; if (log != null) { log.LogWarning((object)("[Dynamic Mallows] '" + ((Object)value.Root).name + "' still has no spawn points.")); } return; } PruneTracked(value); int num = CountPresentMallows(value); int targetMallowCount = GetTargetMallowCount(); int num2 = 0; int num3 = 0; while (num < targetMallowCount && TrySpawnOne(value)) { num2++; num = CountPresentMallows(value); } num = CountPresentMallows(value); if (CampfireHealingMod.syncedMallowDecrease) { while (num > targetMallowCount && TryDestroyOneExtra(value)) { num3++; num = CountPresentMallows(value); } } value.LastSyncedAt = Time.time; ManualLogSource log2 = CampfireHealingMod.Log; if (log2 != null) { log2.LogInfo((object)$"[Dynamic Mallows] '{((Object)value.Root).name}': present~{num}, target={targetMallowCount}, spawned+{num2}, destroyed-{num3}, tracked={value.TrackedViews.Count}, prefab='{value.PrefabName}', points={value.SpawnPoints.Count}."); } } private static void PruneTracked(CampfireMallowState state) { for (int num = state.TrackedViews.Count - 1; num >= 0; num--) { if ((Object)(object)state.TrackedViews[num] == (Object)null) { state.TrackedViews.RemoveAt(num); } } } private static bool MatchesMallowPrefab(Item item, string prefabName) { if ((Object)(object)item == (Object)null) { return false; } string text = (((Object)(object)((Component)item).gameObject != (Object)null) ? ((Object)((Component)item).gameObject).name : string.Empty); string text2 = ((Object)item).name ?? string.Empty; if (!string.IsNullOrEmpty(prefabName) && (text.IndexOf(prefabName, StringComparison.OrdinalIgnoreCase) >= 0 || text2.IndexOf(prefabName, StringComparison.OrdinalIgnoreCase) >= 0)) { return true; } return text.IndexOf("Marshmallow", StringComparison.OrdinalIgnoreCase) >= 0 || text.IndexOf("Glizzy", StringComparison.OrdinalIgnoreCase) >= 0 || text2.IndexOf("Marshmallow", StringComparison.OrdinalIgnoreCase) >= 0 || text2.IndexOf("Glizzy", StringComparison.OrdinalIgnoreCase) >= 0; } private static int CountPresentMallows(CampfireMallowState state) { //IL_0081: 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_010d: Unknown result type (might be due to invalid IL or missing references) //IL_0112: Unknown result type (might be due to invalid IL or missing references) PruneTracked(state); HashSet hashSet = new HashSet(); int num = 0; for (int i = 0; i < state.TrackedViews.Count; i++) { PhotonView val = state.TrackedViews[i]; if (!((Object)(object)val == (Object)null) && hashSet.Add(val.ViewID) && !IsHeldOrGone(val)) { num++; } } string prefabName = state.PrefabName; Vector3 mallowsCenter = GetMallowsCenter(state); Item[] array = Object.FindObjectsByType((FindObjectsInactive)0, (FindObjectsSortMode)0); foreach (Item val2 in array) { if (!((Object)(object)val2 == (Object)null) && !((Object)(object)((MonoBehaviourPun)val2).photonView == (Object)null) && MatchesMallowPrefab(val2, prefabName) && hashSet.Add(((MonoBehaviourPun)val2).photonView.ViewID) && !IsHeldOrGone(((MonoBehaviourPun)val2).photonView) && !(Vector3.Distance(((Component)val2).transform.position, mallowsCenter) > 12f)) { state.TrackedViews.Add(((MonoBehaviourPun)val2).photonView); num++; } } return num; } private static bool IsHeldOrGone(PhotonView view) { //IL_003d: Unknown result type (might be due to invalid IL or missing references) //IL_0043: Invalid comparison between Unknown and I4 //IL_0046: Unknown result type (might be due to invalid IL or missing references) //IL_004c: Invalid comparison between Unknown and I4 if ((Object)(object)view == (Object)null) { return true; } Item component = ((Component)view).GetComponent(); if ((Object)(object)component == (Object)null) { return true; } if ((Object)(object)component.holderCharacter != (Object)null) { return true; } return (int)component.itemState == 1 || (int)component.itemState == 2; } private static Vector3 GetMallowsCenter(CampfireMallowState state) { //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_0026: 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_0038: 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_0071: Unknown result type (might be due to invalid IL or missing references) //IL_0074: Unknown result type (might be due to invalid IL or missing references) //IL_006a: Unknown result type (might be due to invalid IL or missing references) //IL_0079: Unknown result type (might be due to invalid IL or missing references) //IL_007d: Unknown result type (might be due to invalid IL or missing references) Vector3 val = Vector3.zero; int num = 0; for (int i = 0; i < state.SpawnPoints.Count; i++) { if (!((Object)(object)state.SpawnPoints[i] == (Object)null)) { val += state.SpawnPoints[i].position; num++; } } return (num > 0) ? (val / (float)num) : state.Root.transform.position; } private static bool TrySpawnOne(CampfireMallowState state) { //IL_0030: Unknown result type (might be due to invalid IL or missing references) //IL_0035: Unknown result type (might be due to invalid IL or missing references) //IL_0037: Unknown result type (might be due to invalid IL or missing references) //IL_0038: 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_0041: Unknown result type (might be due to invalid IL or missing references) //IL_0042: Unknown result type (might be due to invalid IL or missing references) //IL_0067: Unknown result type (might be due to invalid IL or missing references) //IL_0068: Unknown result type (might be due to invalid IL or missing references) if (state.SpawnPoints.Count == 0) { return false; } int slotIndex = CountPresentMallows(state); int targetMallowCount = GetTargetMallowCount(); string text = PickFoodPrefabName(); Vector3 circleSpawnPosition = GetCircleSpawnPosition(state, slotIndex, targetMallowCount); Quaternion outwardSpawnRotation = GetOutwardSpawnRotation(state, circleSpawnPosition); if (!TryInstantiateExtra(state, text, circleSpawnPosition, outwardSpawnRotation)) { if (!string.Equals(text, "Marshmallow", StringComparison.OrdinalIgnoreCase) && TryInstantiateExtra(state, "Marshmallow", circleSpawnPosition, outwardSpawnRotation)) { return true; } return false; } return true; } private static string PickFoodPrefabName() { float num = Mathf.Clamp01(CampfireHealingMod.syncedGlizzyChance); if (num >= 1f) { return "Glizzy"; } if (num <= 0f) { return "Marshmallow"; } return (Random.value < num) ? "Glizzy" : "Marshmallow"; } private static bool TryInstantiateExtra(CampfireMallowState state, string prefabName, Vector3 spawnPos, Quaternion spawnRot) { //IL_0003: Unknown result type (might be due to invalid IL or missing references) //IL_0004: Unknown result type (might be due to invalid IL or missing references) //IL_008f: Unknown result type (might be due to invalid IL or missing references) //IL_00a2: Unknown result type (might be due to invalid IL or missing references) try { GameObject val = PhotonNetwork.InstantiateItemRoom(prefabName, spawnPos, spawnRot); if ((Object)(object)val == (Object)null) { ManualLogSource log = CampfireHealingMod.Log; if (log != null) { log.LogWarning((object)("[Dynamic Mallows] InstantiateItemRoom returned null for '" + prefabName + "'.")); } return false; } PhotonView component = val.GetComponent(); if ((Object)(object)component == (Object)null) { return false; } Item component2 = val.GetComponent(); if ((Object)(object)component2 != (Object)null) { component.RPC("SetKinematicRPC", (RpcTarget)3, new object[3] { true, val.transform.position, val.transform.rotation }); } state.TrackedViews.Add(component); return true; } catch (Exception ex) { ManualLogSource log2 = CampfireHealingMod.Log; if (log2 != null) { log2.LogWarning((object)("[Dynamic Mallows] Spawn failed for '" + prefabName + "': " + ex.Message)); } return false; } } private static Vector3 GetFireCenter(CampfireMallowState state) { //IL_0017: Unknown result type (might be due to invalid IL or missing references) //IL_001c: Unknown result type (might be due to invalid IL or missing references) //IL_004b: Unknown result type (might be due to invalid IL or missing references) //IL_0050: Unknown result type (might be due to invalid IL or missing references) //IL_0038: 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_0053: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)state?.Root == (Object)null) { return Vector3.zero; } Campfire componentInChildren = state.Root.GetComponentInChildren(true); if ((Object)(object)componentInChildren != (Object)null) { return componentInChildren.Center(); } return state.Root.transform.position; } private static float GetSpawnHeight(CampfireMallowState state) { //IL_0002: Unknown result type (might be due to invalid IL or missing references) return GetMallowsCenter(state).y + 0.08f; } private static Vector3 GetCircleSpawnPosition(CampfireMallowState state, int slotIndex, int totalCount) { //IL_0002: Unknown result type (might be due to invalid IL or missing references) //IL_0007: Unknown result type (might be due to invalid IL or missing references) //IL_00f7: Unknown result type (might be due to invalid IL or missing references) //IL_010b: Unknown result type (might be due to invalid IL or missing references) //IL_011e: Unknown result type (might be due to invalid IL or missing references) //IL_0123: 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_00a4: Unknown result type (might be due to invalid IL or missing references) //IL_00b6: Unknown result type (might be due to invalid IL or missing references) //IL_00c7: Unknown result type (might be due to invalid IL or missing references) //IL_00cc: Unknown result type (might be due to invalid IL or missing references) Vector3 fireCenter = GetFireCenter(state); float spawnHeight = GetSpawnHeight(state); totalCount = Mathf.Max(1, totalCount); if (slotIndex < 0) { slotIndex = 0; } int num = 0; for (int i = 0; i < 12; i++) { float num2 = 1.2f + (float)i * 0.75f; int num3 = Mathf.Max(1, Mathf.FloorToInt((float)Math.PI * 2f * num2 / 0.7f)); int num4 = Mathf.Min(num3, totalCount - num); if (num4 <= 0) { break; } if (slotIndex < num + num4) { int num5 = slotIndex - num; float num6 = (float)num5 / (float)num4 * ((float)Math.PI * 2f) + (float)i * 0.35f; return new Vector3(fireCenter.x + Mathf.Cos(num6) * num2, spawnHeight, fireCenter.z + Mathf.Sin(num6) * num2); } num += num4; } float num7 = (float)slotIndex / (float)totalCount * ((float)Math.PI * 2f); return new Vector3(fireCenter.x + Mathf.Cos(num7) * 1.2f, spawnHeight, fireCenter.z + Mathf.Sin(num7) * 1.2f); } private static Quaternion GetOutwardSpawnRotation(CampfireMallowState state, Vector3 spawnPos) { //IL_0002: Unknown result type (might be due to invalid IL or missing references) //IL_0007: Unknown result type (might be due to invalid IL or missing references) //IL_0008: Unknown result type (might be due to invalid IL or missing references) //IL_0009: Unknown result type (might be due to invalid IL or missing references) //IL_000a: Unknown result type (might be due to invalid IL or missing references) //IL_000f: Unknown result type (might be due to invalid IL or missing references) //IL_0051: Unknown result type (might be due to invalid IL or missing references) //IL_0056: Unknown result type (might be due to invalid IL or missing references) //IL_0074: Unknown result type (might be due to invalid IL or missing references) //IL_0079: Unknown result type (might be due to invalid IL or missing references) //IL_007e: Unknown result type (might be due to invalid IL or missing references) //IL_0047: Unknown result type (might be due to invalid IL or missing references) //IL_004c: Unknown result type (might be due to invalid IL or missing references) //IL_0081: Unknown result type (might be due to invalid IL or missing references) Vector3 fireCenter = GetFireCenter(state); Vector3 val = spawnPos - fireCenter; val.y = 0f; if (((Vector3)(ref val)).sqrMagnitude < 0.0001f) { return Quaternion.Euler(0f, Random.Range(0f, 360f), 0f); } return Quaternion.LookRotation(((Vector3)(ref val)).normalized) * Quaternion.Euler(0f, Random.Range(-18f, 18f), 0f); } private static bool TryDestroyOneExtra(CampfireMallowState state) { //IL_0072: Unknown result type (might be due to invalid IL or missing references) //IL_0077: Unknown result type (might be due to invalid IL or missing references) //IL_00dc: Unknown result type (might be due to invalid IL or missing references) //IL_00e1: Unknown result type (might be due to invalid IL or missing references) for (int num = state.TrackedViews.Count - 1; num >= 0; num--) { PhotonView val = state.TrackedViews[num]; state.TrackedViews.RemoveAt(num); if (!((Object)(object)val == (Object)null) && !IsHeldOrGone(val)) { PhotonNetwork.Destroy(val); return true; } } string prefabName = state.PrefabName; Vector3 mallowsCenter = GetMallowsCenter(state); Item[] array = Object.FindObjectsByType((FindObjectsSortMode)0); for (int num2 = array.Length - 1; num2 >= 0; num2--) { Item val2 = array[num2]; if (!((Object)(object)val2 == (Object)null) && !((Object)(object)((MonoBehaviourPun)val2).photonView == (Object)null) && !IsHeldOrGone(((MonoBehaviourPun)val2).photonView) && MatchesMallowPrefab(val2, prefabName) && !(Vector3.Distance(((Component)val2).transform.position, mallowsCenter) > 10f)) { PhotonNetwork.Destroy(((MonoBehaviourPun)val2).photonView); return true; } } return false; } } [HarmonyPatch(typeof(MapHandler), "SpawnCampfireItems")] internal static class MapHandlerSpawnCampfireItemsMallowPatch { private static void Postfix(GameObject campfireRoot) { CampfireDynamicMallowService.OnCampfireItemsSpawned(campfireRoot); } } [HarmonyPatch(typeof(SingleItemSpawner), "TrySpawnItems")] internal static class SingleItemSpawnerTrySpawnItemsMallowPatch { private static bool Prefix(SingleItemSpawner __instance, ref List __result) { if (!CampfireDynamicMallowService.TrySpawnCampfireFoodWithGlizzyChance(__instance, out var result)) { return true; } __result = result ?? new List(); return false; } private static void Postfix(SingleItemSpawner __instance, List __result) { CampfireDynamicMallowService.TrackVanillaSpawnedViews(__instance, __result); } } internal static class CampfireModAuthorizationService { public const string RoomConfigPropertyKey = "healingCampfiresConfigV2"; private static PhotonScopedManager _photonManager; private static bool _photonManagerReady; private static bool _hostHasHealingCampfiresMod; private static string _pendingHostConfigPayload; public static bool IsGameplayEnabled { get; private set; } public static void Initialize() { Networking.AddHostHasPluginListeners("tony4twentys.Healing_Campfires", (Action)OnHostHasHealingCampfiresMod, (Action)OnHostMissingHealingCampfiresMod); if (PhotonCustomPropsUtilsPlugin.IsReady) { SetupPhotonManager(); } else { PhotonCustomPropsUtilsPlugin.OnReady += SetupPhotonManager; } if (!PhotonNetwork.InRoom) { SetSoloAuthorized(); } } public static void SetSoloAuthorized() { _hostHasHealingCampfiresMod = true; IsGameplayEnabled = true; CampfireConfigSyncService.ApplyLocalAuthoritativeConfig(); } private static void SetupPhotonManager() { if (_photonManagerReady) { return; } _photonManagerReady = true; _photonManager = PhotonCustomPropsUtilsPlugin.GetManager("tony4twentys.Healing_Campfires"); _photonManager.RegisterOnJoinedRoom((Action)delegate { if (PhotonNetwork.InRoom && PhotonNetwork.IsMasterClient && _hostHasHealingCampfiresMod) { CampfireConfigSyncService.ApplyLocalAuthoritativeConfig(); PublishHostConfigIfMaster(); } }); _photonManager.RegisterRoomProperty("healingCampfiresConfigV2", (RoomEventType)2, (Action)delegate(string value) { if (!PhotonNetwork.IsMasterClient && !string.IsNullOrEmpty(value)) { if (!_hostHasHealingCampfiresMod) { _pendingHostConfigPayload = value; } else { CampfireConfigSyncService.ApplyRemoteSnapshot(value); } } }); } private static void OnHostHasHealingCampfiresMod() { if (PhotonNetwork.InRoom) { _hostHasHealingCampfiresMod = true; IsGameplayEnabled = true; if (PhotonNetwork.IsMasterClient) { CampfireConfigSyncService.ApplyLocalAuthoritativeConfig(); PublishHostConfigIfMaster(); } else if (!string.IsNullOrEmpty(_pendingHostConfigPayload)) { CampfireConfigSyncService.ApplyRemoteSnapshot(_pendingHostConfigPayload); _pendingHostConfigPayload = null; } } } private static void OnHostMissingHealingCampfiresMod() { if (PhotonNetwork.InRoom) { _hostHasHealingCampfiresMod = false; IsGameplayEnabled = false; _pendingHostConfigPayload = null; CampfireConfigSyncService.ClearRemoteHostConfig(); CampfireConfigSyncService.ApplyLocalAuthoritativeConfig(); } } public static void OnHostConfigChangedLocally() { if (!PhotonNetwork.InRoom) { CampfireConfigSyncService.ApplyLocalAuthoritativeConfig(); } else if (IsGameplayEnabled && PhotonNetwork.IsMasterClient) { CampfireConfigSyncService.ApplyLocalAuthoritativeConfig(); PublishHostConfigIfMaster(); } } private static void PublishHostConfigIfMaster() { if (_photonManager != null && PhotonNetwork.InRoom && PhotonNetwork.IsMasterClient && !((Object)(object)CampfireHealingMod.Instance == (Object)null)) { string text = CampfireConfigSyncService.Serialize(CampfireHealingMod.Instance.BuildSnapshot()); _photonManager.SetRoomProperty("healingCampfiresConfigV2", (object)text); } } } internal static class CampfireRadiusService { private static readonly Dictionary OriginalRadiusById = new Dictionary(); public static void RegisterAndApply(Campfire fire) { if (!((Object)(object)fire == (Object)null) && !CampfireHealingMod.CampfireHelper.IsPortableStove(fire)) { int instanceID = ((Object)fire).GetInstanceID(); if (!OriginalRadiusById.ContainsKey(instanceID)) { OriginalRadiusById[instanceID] = fire.moraleBoostRadius; } Apply(fire); } } public static void ApplyToAllCampfires() { Campfire[] array = Object.FindObjectsByType((FindObjectsInactive)1, (FindObjectsSortMode)0); for (int i = 0; i < array.Length; i++) { RegisterAndApply(array[i]); } } public static void Apply(Campfire fire) { if (!((Object)(object)fire == (Object)null) && !CampfireHealingMod.CampfireHelper.IsPortableStove(fire)) { int instanceID = ((Object)fire).GetInstanceID(); if (!OriginalRadiusById.TryGetValue(instanceID, out var value)) { value = fire.moraleBoostRadius; OriginalRadiusById[instanceID] = value; } float syncedMoraleBoostRadius = CampfireHealingMod.syncedMoraleBoostRadius; fire.moraleBoostRadius = ((syncedMoraleBoostRadius > 0f) ? syncedMoraleBoostRadius : value); } } public static void Cleanup(Campfire fire) { if (!((Object)(object)fire == (Object)null)) { OriginalRadiusById.Remove(((Object)fire).GetInstanceID()); } } } [HarmonyPatch(typeof(Campfire), "Awake")] internal static class CampfireAwakeRadiusPatch { private static void Postfix(Campfire __instance) { CampfireRadiusService.RegisterAndApply(__instance); } } [HarmonyPatch(typeof(Campfire), "OnDestroy")] internal static class CampfireOnDestroyRadiusPatch { private static void Prefix(Campfire __instance) { CampfireRadiusService.Cleanup(__instance); } } internal static class CampfireRestrictionService { private sealed class PendingCampfireState { public Campfire Fire; public bool AwaitingUnlock; public bool UnlockRpcSent; public float UnlockNotBefore; } internal static readonly byte ReviveRequestEventCode = ComputeReviveEventCode(); private const string ReviveRequestMagic = "HC_REVIVE_V1"; private static readonly Dictionary PendingByInstanceId = new Dictionary(); private static bool _eventsRegistered; public static bool HoldReviveActive => CampfireHealingMod.IsModActive && CampfireHealingMod.syncedRespawnEnabled; public static bool ShouldDeferUnlock => CampfireHealingMod.IsModActive && CampfireHealingMod.syncedRemoveRestriction; private static byte ComputeReviveEventCode() { int num = -2128831035; string text = "tony4twentys.Healing_Campfires"; for (int i = 0; i < text.Length; i++) { num = (num ^ text[i]) * 16777619; } int num2 = ((num != int.MinValue) ? Math.Abs(num) : 0); return (byte)(180 + num2 % 20); } public static void RegisterPhotonEvents() { if (!_eventsRegistered) { PhotonNetwork.NetworkingClient.EventReceived += OnPhotonEvent; _eventsRegistered = true; ManualLogSource log = CampfireHealingMod.Log; if (log != null) { log.LogInfo((object)string.Format("[Healing Campfires] Hold-revive RaiseEvent code={0} (magic={1}).", ReviveRequestEventCode, "HC_REVIVE_V1")); } } } private static void OnPhotonEvent(EventData photonEvent) { //IL_0041: Unknown result type (might be due to invalid IL or missing references) if (photonEvent != null && photonEvent.Code == ReviveRequestEventCode && PhotonNetwork.IsMasterClient && TryReadReviveRequest(photonEvent.CustomData, out var position)) { CampfireHealingMod.RespawnAllDeadPlayers(position); } } private static bool TryReadReviveRequest(object customData, out Vector3 position) { //IL_0002: Unknown result type (might be due to invalid IL or missing references) //IL_0087: Unknown result type (might be due to invalid IL or missing references) //IL_008c: Unknown result type (might be due to invalid IL or missing references) //IL_009f: Unknown result type (might be due to invalid IL or missing references) //IL_00a4: Unknown result type (might be due to invalid IL or missing references) //IL_00b2: Unknown result type (might be due to invalid IL or missing references) //IL_00b4: Unknown result type (might be due to invalid IL or missing references) position = default(Vector3); if (customData is object[] array && array.Length >= 4 && array[0] is string text && text == "HC_REVIVE_V1" && array[1] is float num && array[2] is float num2 && array[3] is float num3) { position = new Vector3(num, num2, num3); return true; } if (customData is Vector3 val) { position = val; return true; } return false; } public static void RequestReviveAt(Vector3 position) { //IL_0032: Unknown result type (might be due to invalid IL or missing references) //IL_0040: Unknown result type (might be due to invalid IL or missing references) //IL_004e: Unknown result type (might be due to invalid IL or missing references) //IL_0061: Unknown result type (might be due to invalid IL or missing references) //IL_0066: Unknown result type (might be due to invalid IL or missing references) //IL_0068: Unknown result type (might be due to invalid IL or missing references) //IL_006d: Unknown result type (might be due to invalid IL or missing references) //IL_0077: Expected O, but got Unknown //IL_0019: Unknown result type (might be due to invalid IL or missing references) if (HoldReviveActive) { if (PhotonNetwork.IsMasterClient) { CampfireHealingMod.RespawnAllDeadPlayers(position); return; } object[] array = new object[4] { "HC_REVIVE_V1", position.x, position.y, position.z }; PhotonNetwork.RaiseEvent(ReviveRequestEventCode, (object)array, new RaiseEventOptions { Receivers = (ReceiverGroup)2 }, SendOptions.SendReliable); } } public static string LocalizedPrompt(string key, string fallback) { try { string text = LocalizedText.GetText(key, false); if (!string.IsNullOrEmpty(text)) { return text; } } catch { } return string.IsNullOrEmpty(fallback) ? key : fallback; } public static float GetMoraleRadius(Campfire fire) { return ((Object)(object)fire != (Object)null) ? fire.moraleBoostRadius : 0f; } public static bool HasRevivablePlayers() { List allCharacters = Character.AllCharacters; if (allCharacters == null) { return false; } foreach (Character item in allCharacters) { if (!((Object)(object)item == (Object)null) && !((Object)(object)item.data == (Object)null) && !item.isBot) { if (item.data.dead) { return true; } if (CampfireHealingMod.syncedRevivePassedOut && item.data.fullyPassedOut) { return true; } } } return false; } public static bool AllPlayersAliveAndInMoraleRadius(Campfire fire) { //IL_0021: Unknown result type (might be due to invalid IL or missing references) //IL_0026: Unknown result type (might be due to invalid IL or missing references) //IL_0097: Unknown result type (might be due to invalid IL or missing references) //IL_009a: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)fire == (Object)null) { return false; } float moraleRadius = GetMoraleRadius(fire); Vector3 position = ((Component)fire).transform.position; bool result = false; foreach (Character allPlayerCharacter in PlayerHandler.GetAllPlayerCharacters()) { if (!((Object)(object)allPlayerCharacter == (Object)null) && !((Object)(object)allPlayerCharacter.data == (Object)null) && !allPlayerCharacter.isBot) { result = true; if (allPlayerCharacter.data.dead || allPlayerCharacter.data.fullyPassedOut) { return false; } if (Vector3.Distance(position, allPlayerCharacter.Center) > moraleRadius) { return false; } } } return result; } public static string BuildFarPlayersPrintout(Campfire fire) { //IL_0025: Unknown result type (might be due to invalid IL or missing references) //IL_002a: Unknown result type (might be due to invalid IL or missing references) //IL_0112: Unknown result type (might be due to invalid IL or missing references) //IL_0115: Unknown result type (might be due to invalid IL or missing references) //IL_012d: 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) if ((Object)(object)fire == (Object)null) { return string.Empty; } float moraleRadius = GetMoraleRadius(fire); Vector3 position = ((Component)fire).transform.position; StringBuilder stringBuilder = new StringBuilder(); foreach (Character allPlayerCharacter in PlayerHandler.GetAllPlayerCharacters()) { if (!((Object)(object)allPlayerCharacter == (Object)null) && !((Object)(object)allPlayerCharacter.data == (Object)null) && !allPlayerCharacter.isBot) { string value = (((Object)(object)((MonoBehaviourPun)allPlayerCharacter).photonView != (Object)null && ((MonoBehaviourPun)allPlayerCharacter).photonView.Owner != null) ? ((MonoBehaviourPun)allPlayerCharacter).photonView.Owner.NickName : ((Object)((Component)allPlayerCharacter).gameObject).name); if (allPlayerCharacter.data.dead || allPlayerCharacter.data.fullyPassedOut) { stringBuilder.Append('\n'); stringBuilder.Append(value); stringBuilder.Append(allPlayerCharacter.data.dead ? " DEAD" : " PASSED OUT"); } else if (!(Vector3.Distance(position, allPlayerCharacter.Center) <= moraleRadius)) { float num = Vector3.Distance(position, allPlayerCharacter.Center) * CharacterStats.unitsToMeters; stringBuilder.Append('\n'); stringBuilder.Append(value); stringBuilder.Append(' '); stringBuilder.Append(Mathf.RoundToInt(num)); stringBuilder.Append('m'); } } } return stringBuilder.ToString(); } public static void MarkAwaitingUnlock(Campfire fire) { if (!((Object)(object)fire == (Object)null)) { int instanceID = ((Object)fire).GetInstanceID(); PendingByInstanceId[instanceID] = new PendingCampfireState { Fire = fire, AwaitingUnlock = true, UnlockRpcSent = false, UnlockNotBefore = 0f }; } } public static bool IsAwaitingUnlock(Campfire fire) { if ((Object)(object)fire == (Object)null) { return false; } PendingCampfireState value; return PendingByInstanceId.TryGetValue(((Object)fire).GetInstanceID(), out value) && value.AwaitingUnlock; } public static void ClearAwaitingUnlock(Campfire fire) { if (!((Object)(object)fire == (Object)null) && PendingByInstanceId.TryGetValue(((Object)fire).GetInstanceID(), out var value)) { value.AwaitingUnlock = false; } } public static bool ShouldSuppressMoraleBoost(Vector3 origin) { //IL_005d: Unknown result type (might be due to invalid IL or missing references) //IL_0062: Unknown result type (might be due to invalid IL or missing references) //IL_0063: Unknown result type (might be due to invalid IL or missing references) //IL_0068: Unknown result type (might be due to invalid IL or missing references) if (!ShouldDeferUnlock) { return false; } foreach (PendingCampfireState value in PendingByInstanceId.Values) { if (value != null && value.AwaitingUnlock && !((Object)(object)value.Fire == (Object)null)) { Vector3 val = ((Component)value.Fire).transform.position - origin; if (((Vector3)(ref val)).sqrMagnitude <= 1f) { return true; } } } return false; } public static void SkipVanillaFirstLightMorale(Campfire fire) { //IL_004c: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)fire == (Object)null || fire.fireHasStarted) { return; } if (fire.fireStart != null) { for (int i = 0; i < fire.fireStart.Length; i++) { if ((Object)(object)fire.fireStart[i] != (Object)null) { fire.fireStart[i].Play(((Component)fire).transform.position); } } } fire.fireHasStarted = true; } public static void DelayUnlockAfterRevive(Vector3 campfirePosition) { //IL_0053: Unknown result type (might be due to invalid IL or missing references) //IL_0058: Unknown result type (might be due to invalid IL or missing references) //IL_0059: Unknown result type (might be due to invalid IL or missing references) //IL_005e: Unknown result type (might be due to invalid IL or missing references) foreach (PendingCampfireState value in PendingByInstanceId.Values) { if (value == null || !value.AwaitingUnlock || (Object)(object)value.Fire == (Object)null || value.UnlockRpcSent) { continue; } Vector3 val = ((Component)value.Fire).transform.position - campfirePosition; if (!(((Vector3)(ref val)).sqrMagnitude > 4f)) { float num = Time.time + 0.75f; if (num > value.UnlockNotBefore) { value.UnlockNotBefore = num; } } } } public static void TryCompleteUnlock(Campfire fire) { //IL_0105: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)fire == (Object)null || !ShouldDeferUnlock || !IsAwaitingUnlock(fire) || !PhotonNetwork.IsMasterClient || !fire.Lit || CampfireHealingMod.CampfireHelper.IsPortableStove(fire) || !AllPlayersAliveAndInMoraleRadius(fire)) { return; } int instanceID = ((Object)fire).GetInstanceID(); if (!PendingByInstanceId.TryGetValue(instanceID, out var value) || value.UnlockRpcSent || Time.time < value.UnlockNotBefore) { return; } value.UnlockRpcSent = true; if ((Object)(object)fire.view != (Object)null) { fire.view.RPC("Light_Rpc", (RpcTarget)0, new object[2] { true, fire.beenBurningFor }); ManualLogSource log = CampfireHealingMod.Log; if (log != null) { log.LogInfo((object)$"Campfire unlock RPC sent at {((Component)fire).transform.position} (morale + next segment)."); } } } public static void GrantDeferredMorale(Campfire fire) { //IL_0033: Unknown result type (might be due to invalid IL or missing references) //IL_00bb: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)fire == (Object)null) { return; } int num = (CampfireHealingMod.syncedEnableSoloMoraleBoost ? 1 : 2); float syncedMoraleBoostStamina = CampfireHealingMod.syncedMoraleBoostStamina; if (PhotonNetwork.IsMasterClient) { bool flag = MoraleBoost.SpawnMoraleBoost(((Component)fire).transform.position, fire.moraleBoostRadius, syncedMoraleBoostStamina, fire.moraleBoostPerAdditionalScout, true, num); if (flag && (Object)(object)Singleton.Instance != (Object)null) { Singleton.Instance.IncrementSteamStat((STEAMSTATTYPE)2, 1); } if (!flag) { return; } } if (fire.moraleBoost == null) { return; } for (int i = 0; i < fire.moraleBoost.Length; i++) { if ((Object)(object)fire.moraleBoost[i] != (Object)null) { fire.moraleBoost[i].Play(((Component)fire).transform.position); } } } internal static int CountCharactersInRadiusPublic(Vector3 origin, float radius) { //IL_003f: Unknown result type (might be due to invalid IL or missing references) //IL_0044: Unknown result type (might be due to invalid IL or missing references) int num = 0; List allCharacters = Character.AllCharacters; if (allCharacters == null) { return 0; } for (int i = 0; i < allCharacters.Count; i++) { Character val = allCharacters[i]; if (!((Object)(object)val == (Object)null) && (radius == -1f || Vector3.Distance(val.Center, origin) <= radius)) { num++; } } return num; } internal static void ApplyMoraleToCharactersInRadiusPublic(Vector3 origin, float radius, float stamina, bool sendToAll) { //IL_0042: Unknown result type (might be due to invalid IL or missing references) //IL_0047: Unknown result type (might be due to invalid IL or missing references) List allCharacters = Character.AllCharacters; if (allCharacters == null) { return; } List list = new List(); for (int i = 0; i < allCharacters.Count; i++) { Character val = allCharacters[i]; if (!((Object)(object)val == (Object)null) && (radius == -1f || Vector3.Distance(val.Center, origin) <= radius)) { list.Add(val); } } for (int j = 0; j < list.Count; j++) { Character val2 = list[j]; if (!((Object)(object)((MonoBehaviourPun)val2).photonView == (Object)null)) { if (sendToAll) { ((MonoBehaviourPun)val2).photonView.RPC("MoraleBoost", (RpcTarget)0, new object[2] { stamina, list.Count }); } else { val2.MoraleBoost(stamina, list.Count); } } } } public static void CleanupFire(Campfire fire) { if (!((Object)(object)fire == (Object)null)) { PendingByInstanceId.Remove(((Object)fire).GetInstanceID()); } } } [HarmonyPatch(typeof(Campfire), "Interact_CastFinished")] internal static class CampfireInteractCastFinishedPatch { private static bool Prefix(Campfire __instance, Character interactor) { //IL_0063: Unknown result type (might be due to invalid IL or missing references) if (!CampfireHealingMod.IsModActive || (Object)(object)__instance == (Object)null || CampfireHealingMod.CampfireHelper.IsPortableStove(__instance)) { return true; } if (__instance.Lit) { if ((Object)(object)__instance.currentlyCookingItem != (Object)null) { return true; } if (CampfireRestrictionService.HoldReviveActive && CampfireRestrictionService.HasRevivablePlayers()) { CampfireRestrictionService.RequestReviveAt(((Component)__instance).transform.position); return false; } return true; } if (!CampfireHealingMod.syncedRemoveRestriction) { return true; } if ((Object)(object)__instance.view != (Object)null) { __instance.view.RPC("Light_Rpc", (RpcTarget)0, new object[2] { true, 0f }); } return false; } } [HarmonyPatch(typeof(Campfire), "Light_Rpc")] internal static class CampfireLightRpcRestrictionPatch { private static void Prefix(Campfire __instance, ref bool updateSegment, out int __state) { __state = 0; if (CampfireHealingMod.IsModActive && CampfireHealingMod.syncedRemoveRestriction && !((Object)(object)__instance == (Object)null) && !CampfireHealingMod.CampfireHelper.IsPortableStove(__instance)) { if (updateSegment && __instance.Lit && CampfireRestrictionService.IsAwaitingUnlock(__instance)) { CampfireRestrictionService.ClearAwaitingUnlock(__instance); __state = 2; } else if (updateSegment) { CampfireRestrictionService.MarkAwaitingUnlock(__instance); updateSegment = false; __state = 1; } } } private static void Postfix(Campfire __instance, bool updateSegment, int __state) { //IL_0050: Unknown result type (might be due to invalid IL or missing references) if (CampfireHealingMod.IsModActive && !((Object)(object)__instance == (Object)null) && !CampfireHealingMod.CampfireHelper.IsPortableStove(__instance)) { if ((__state == 1 || (__state == 0 && updateSegment && !CampfireHealingMod.syncedRemoveRestriction)) && CampfireHealingMod.syncedRespawnEnabled) { CampfireHealingMod.RespawnAllDeadPlayers(((Component)__instance).transform.position); } if (__state == 1) { CampfireRestrictionService.SkipVanillaFirstLightMorale(__instance); } if (__state == 2) { CampfireRestrictionService.GrantDeferredMorale(__instance); } } } } [HarmonyPatch(typeof(MoraleBoost), "SpawnMoraleBoost")] internal static class MoraleBoostSpawnPatch { private static bool Prefix(Vector3 origin, float radius, ref float baselineStaminaBoost, float staminaBoostPerAdditionalScout, bool sendToAll, int minScouts, ref bool __result) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) //IL_002b: Unknown result type (might be due to invalid IL or missing references) //IL_004d: Unknown result type (might be due to invalid IL or missing references) if (CampfireRestrictionService.ShouldSuppressMoraleBoost(origin)) { __result = false; return false; } if (!CampfireHealingMod.IsModActive) { return true; } baselineStaminaBoost = CampfireHealingMod.syncedMoraleBoostStamina; int num = CampfireRestrictionService.CountCharactersInRadiusPublic(origin, radius); if (CampfireHealingMod.syncedEnableSoloMoraleBoost && num == 1 && minScouts > 1) { CampfireRestrictionService.ApplyMoraleToCharactersInRadiusPublic(origin, radius, baselineStaminaBoost, sendToAll); __result = true; return false; } if (!CampfireHealingMod.syncedEnableSoloMoraleBoost && num < 2) { __result = false; return false; } return true; } } [HarmonyPatch(/*Could not decode attribute arguments.*/)] internal static class CampfireCanBurnOutPatch { private static void Postfix(Campfire __instance, ref bool __result) { if (__result && !((Object)(object)__instance == (Object)null) && CampfireRestrictionService.IsAwaitingUnlock(__instance)) { __result = false; } } } [HarmonyPatch(typeof(Campfire), "GetInteractionText")] internal static class CampfireGetInteractionTextPatch { private static bool Prefix(Campfire __instance, ref string __result) { if (!CampfireHealingMod.IsModActive || (Object)(object)__instance == (Object)null || CampfireHealingMod.CampfireHelper.IsPortableStove(__instance)) { return true; } bool flag = (Object)(object)Character.localCharacter != (Object)null && (Object)(object)Character.localCharacter.data != (Object)null && (Object)(object)Character.localCharacter.data.currentItem != (Object)null && (Object)(object)Character.localCharacter.data.currentItem.cooking != (Object)null && Character.localCharacter.data.currentItem.cooking.canBeCooked; if (__instance.Lit && CampfireRestrictionService.HoldReviveActive && CampfireRestrictionService.HasRevivablePlayers() && !flag) { string text = (CampfireHealingMod.syncedRemoveRestriction ? CampfireRestrictionService.BuildFarPlayersPrintout(__instance) : string.Empty); __result = CampfireRestrictionService.LocalizedPrompt("REVIVE", "REVIVE") + text; return false; } if (!CampfireHealingMod.syncedRemoveRestriction) { return true; } string text2 = CampfireRestrictionService.BuildFarPlayersPrintout(__instance); if (!__instance.Lit) { __result = CampfireRestrictionService.LocalizedPrompt("LIGHT", "LIGHT") + text2; return false; } if (flag) { __result = CampfireRestrictionService.LocalizedPrompt("COOK", "COOK") + text2; return false; } __result = CampfireRestrictionService.LocalizedPrompt("COOK", "COOK") + text2; return false; } } [HarmonyPatch(typeof(Campfire), "IsInteractible")] internal static class CampfireIsInteractiblePatch { private static void Postfix(Campfire __instance, Character interactor, ref bool __result) { //IL_0039: Unknown result type (might be due to invalid IL or missing references) //IL_003f: Invalid comparison between Unknown and I4 if (!__result && !((Object)(object)__instance == (Object)null) && !((Object)(object)interactor == (Object)null) && CampfireRestrictionService.HoldReviveActive && !CampfireHealingMod.CampfireHelper.IsPortableStove(__instance) && (int)__instance.state == 1 && CampfireRestrictionService.HasRevivablePlayers()) { __result = true; } } } [HarmonyPatch(typeof(Campfire), "IsConstantlyInteractable")] internal static class CampfireIsConstantlyInteractablePatch { private static void Postfix(Campfire __instance, Character interactor, ref bool __result) { //IL_0039: Unknown result type (might be due to invalid IL or missing references) //IL_003f: Invalid comparison between Unknown and I4 if (!__result && !((Object)(object)__instance == (Object)null) && !((Object)(object)interactor == (Object)null) && CampfireRestrictionService.HoldReviveActive && !CampfireHealingMod.CampfireHelper.IsPortableStove(__instance) && (int)__instance.state == 1 && CampfireRestrictionService.HasRevivablePlayers()) { __result = true; } } } [HarmonyPatch(typeof(Campfire), "ApplyCampfireProtection")] internal static class CampfirePendingUnlockPatch { private static void Postfix(Campfire __instance) { if (!((Object)(object)__instance == (Object)null) && __instance.Lit) { CampfireRestrictionService.TryCompleteUnlock(__instance); } } } [HarmonyPatch(typeof(Campfire), "Extinguish_Rpc")] internal static class CampfireExtinguishCleanupPatch { private static void Postfix(Campfire __instance) { CampfireRestrictionService.CleanupFire(__instance); } } [BepInPlugin("tony4twentys.Healing_Campfires", "Healing Campfires", "4.5.0")] [BepInDependency(/*Could not decode attribute arguments.*/)] [BepInDependency(/*Could not decode attribute arguments.*/)] public class CampfireHealingMod : BaseUnityPlugin { internal static class CampfireHelper { private const float NearCampfireGraceSeconds = 0.25f; private const float VanillaCampfireInjuryBurst = -0.2f; private const float HealIntervalSeconds = 1f; private static float localPlayerNearCampfireUntil; private static float _nextHealTime; public static bool IsPortableStove(Campfire fire) { if ((Object)(object)fire == (Object)null) { return false; } if (LooksLikePortableStoveToken(fire.nameOverride)) { return true; } Transform val = ((Component)fire).transform; int num = 0; while ((Object)(object)val != (Object)null && num < 8) { if (LooksLikePortableStoveToken(((Object)val).name)) { return true; } val = val.parent; num++; } string name = fire.GetName(); if (!string.IsNullOrEmpty(name)) { string text = name.Replace(" ", string.Empty); if (text.IndexOf("PORTABLESTOVE", StringComparison.OrdinalIgnoreCase) >= 0) { return true; } } return false; } private static bool LooksLikePortableStoveToken(string token) { if (string.IsNullOrEmpty(token)) { return false; } bool flag = token.IndexOf("PORTABLE", StringComparison.OrdinalIgnoreCase) >= 0 || token.IndexOf("Portable", StringComparison.Ordinal) >= 0; bool flag2 = token.IndexOf("STOVE", StringComparison.OrdinalIgnoreCase) >= 0 || token.IndexOf("Stove", StringComparison.Ordinal) >= 0; return (flag && flag2) || token.IndexOf("PortableStove", StringComparison.OrdinalIgnoreCase) >= 0; } public static bool IsNearLitProgressionCampfire(Vector3 position, float maxDistance) { //IL_003d: Unknown result type (might be due to invalid IL or missing references) //IL_0042: Unknown result type (might be due to invalid IL or missing references) //IL_0043: Unknown result type (might be due to invalid IL or missing references) //IL_0048: Unknown result type (might be due to invalid IL or missing references) Campfire[] array = Object.FindObjectsByType((FindObjectsSortMode)0); float num = maxDistance * maxDistance; foreach (Campfire val in array) { if (!((Object)(object)val == (Object)null) && val.Lit && !IsPortableStove(val)) { Vector3 val2 = ((Component)val).transform.position - position; if (((Vector3)(ref val2)).sqrMagnitude <= num) { return true; } } } return false; } public static float GetEffectiveRadius(Campfire fire) { return ((Object)(object)fire != (Object)null) ? fire.moraleBoostRadius : 0f; } public static void MarkLocalPlayerNearCampfire() { localPlayerNearCampfireUntil = Time.time + 0.25f; } public static bool IsLocalPlayerNearLitCampfire() { return Time.time < localPlayerNearCampfireUntil; } public static bool ShouldBlockStatus(STATUSTYPE statusType) { //IL_0011: Unknown result type (might be due to invalid IL or missing references) //IL_0012: Unknown result type (might be due to invalid IL or missing references) //IL_0013: Unknown result type (might be due to invalid IL or missing references) //IL_0014: Unknown result type (might be due to invalid IL or missing references) //IL_0015: Unknown result type (might be due to invalid IL or missing references) //IL_004b: Expected I4, but got Unknown if (syncedBlockAllStatuses) { return true; } return (int)statusType switch { 0 => syncedBlockInjury, 2 => syncedBlockCold, 3 => syncedBlockPoison, 4 => syncedBlockCrab, 5 => syncedBlockCurse, 6 => syncedBlockDrowsy, 8 => syncedBlockHot, 9 => syncedBlockThorns, 10 => syncedBlockSpores, 11 => syncedBlockWeb, 1 => false, _ => false, }; } public static bool IsVanillaCampfireInjuryBurst(STATUSTYPE statusType, float amount) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) return (int)statusType == 0 && Mathf.Approximately(amount, -0.2f); } public static void ApplyContinuousHealing(CharacterAfflictions afflictions) { if (!syncedHealAfflictionsEnabled || Time.time < _nextHealTime) { return; } _nextHealTime = Time.time + 1f; float syncedHealPerSecond = CampfireHealingMod.syncedHealPerSecond; if (!(syncedHealPerSecond <= 0f)) { bool syncedHealAllStatuses = CampfireHealingMod.syncedHealAllStatuses; if (syncedHealAllStatuses || syncedHealInjury) { TryHeal(afflictions, (STATUSTYPE)0, syncedHealPerSecond); } if (syncedHealAllStatuses || syncedHealDrowsy) { TryHeal(afflictions, (STATUSTYPE)6, syncedHealPerSecond); } if (syncedHealAllStatuses || syncedHealPoison) { TryHeal(afflictions, (STATUSTYPE)3, syncedHealPerSecond); } if (syncedHealAllStatuses || syncedHealCold) { TryHeal(afflictions, (STATUSTYPE)2, syncedHealPerSecond); } if (syncedHealAllStatuses || syncedHealCurse) { TryHeal(afflictions, (STATUSTYPE)5, syncedHealPerSecond); } if (syncedHealAllStatuses || syncedHealHunger) { TryHeal(afflictions, (STATUSTYPE)1, syncedHealPerSecond); } if (syncedHealAllStatuses || syncedHealHot) { TryHeal(afflictions, (STATUSTYPE)8, syncedHealPerSecond); } if (syncedHealAllStatuses || syncedHealThorns) { TryHeal(afflictions, (STATUSTYPE)9, syncedHealPerSecond); } if (syncedHealAllStatuses || syncedHealSpores) { TryHeal(afflictions, (STATUSTYPE)10, syncedHealPerSecond); } if (syncedHealAllStatuses || syncedHealWeb) { TryHeal(afflictions, (STATUSTYPE)11, syncedHealPerSecond); } if (syncedHealAllStatuses || syncedHealCrab) { TryHeal(afflictions, (STATUSTYPE)4, syncedHealPerSecond); } } } private static void TryHeal(CharacterAfflictions afflictions, STATUSTYPE statusType, float heal) { //IL_0002: 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 (!(afflictions.GetCurrentStatus(statusType) <= 0f)) { afflictions.SubtractStatus(statusType, heal, false, false); } } } [HarmonyPatch(typeof(Campfire), "ApplyCampfireProtection")] internal static class ApplyCampfireProtectionPatch { private static void Postfix(Campfire __instance) { if (!IsModActive || (Object)(object)__instance == (Object)null || !__instance.Lit || CampfireHelper.IsPortableStove(__instance)) { return; } Character localCharacter = Character.localCharacter; if (!((Object)(object)localCharacter == (Object)null) && !((Object)(object)localCharacter.data == (Object)null) && !localCharacter.data.dead && !((Object)(object)localCharacter.refs?.afflictions == (Object)null)) { float effectiveRadius = CampfireHelper.GetEffectiveRadius(__instance); if (__instance.PlayerCharactersInRadius(effectiveRadius).Contains(localCharacter)) { CampfireHelper.MarkLocalPlayerNearCampfire(); CampfireHelper.ApplyContinuousHealing(localCharacter.refs.afflictions); } } } } [HarmonyPatch(typeof(CharacterAfflictions), "AddStatus")] internal static class BlockAddStatusPatch { private static bool Prefix(STATUSTYPE statusType, float amount, bool fromRPC, CharacterAfflictions __instance) { //IL_0057: Unknown result type (might be due to invalid IL or missing references) if (!IsModActive || fromRPC || amount <= 0f) { return true; } if ((Object)(object)__instance?.character == (Object)null || !__instance.character.IsLocal) { return true; } if (!syncedBlockNegativeStatus || !CampfireHelper.ShouldBlockStatus(statusType)) { return true; } if (CampfireHelper.IsLocalPlayerNearLitCampfire()) { return false; } return true; } } [HarmonyPatch(typeof(CharacterAfflictions), "AdjustStatus")] internal static class BlockAdjustStatusPatch { private static bool Prefix(STATUSTYPE statusType, float amount, bool fromRPC, CharacterAfflictions __instance) { //IL_0040: Unknown result type (might be due to invalid IL or missing references) //IL_0069: Unknown result type (might be due to invalid IL or missing references) if (!IsModActive || fromRPC) { return true; } if ((Object)(object)__instance?.character == (Object)null || !__instance.character.IsLocal) { return true; } if (CampfireHelper.IsVanillaCampfireInjuryBurst(statusType, amount) && CampfireHelper.IsLocalPlayerNearLitCampfire()) { return false; } if (amount <= 0f || !syncedBlockNegativeStatus || !CampfireHelper.ShouldBlockStatus(statusType)) { return true; } if (CampfireHelper.IsLocalPlayerNearLitCampfire()) { return false; } return true; } } [HarmonyPatch(typeof(Character), "RPCA_ReviveAtPosition")] internal static class CampfireEnforceRevivePenaltyPatch { private static void Postfix(Character __instance, Vector3 position, bool applyStatus, int statueSegment) { //IL_0042: Unknown result type (might be due to invalid IL or missing references) if (IsModActive && syncedRespawnEnabled && syncedEnforceRevivePenalty && !applyStatus && statueSegment <= -1 && !((Object)(object)__instance == (Object)null) && __instance.refs != null && CampfireHelper.IsNearLitProgressionCampfire(position, 16f)) { ApplyForcedCampfireRevivePenalties(__instance.refs.afflictions); } } } public const string PluginGuid = "tony4twentys.Healing_Campfires"; public const string PluginName = "Healing Campfires"; public const string PluginVersion = "4.5.0"; public const string PhotonCustomPropsPluginGuid = "com.snosz.photoncustompropsutils"; private ConfigEntry _dynamicMallows; private ConfigEntry _mallowDecrease; private ConfigEntry _mallowMultiplierEnabled; private ConfigEntry _mallowMultiplier; private ConfigEntry _glizzyChance; private ConfigEntry _dynamicBackpacks; private ConfigEntry _backpackDecrease; private ConfigEntry _enableSoloMoraleBoost; private ConfigEntry _moraleBoostStamina; private ConfigEntry _moraleBoostRadius; private ConfigEntry _removeRestriction; private ConfigEntry _respawnEnabled; private ConfigEntry _revivePassedOut; private ConfigEntry _enforceRevivePenalty; private ConfigEntry _healAfflictionsEnabled; private ConfigEntry _healPerSecond; private ConfigEntry _healAllStatuses; private ConfigEntry _healInjury; private ConfigEntry _healDrowsy; private ConfigEntry _healPoison; private ConfigEntry _healCold; private ConfigEntry _healCurse; private ConfigEntry _healHunger; private ConfigEntry _healHot; private ConfigEntry _healThorns; private ConfigEntry _healSpores; private ConfigEntry _healWeb; private ConfigEntry _healCrab; private ConfigEntry _blockNegativeStatus; private ConfigEntry _blockAllStatuses; private ConfigEntry _blockInjury; private ConfigEntry _blockCold; private ConfigEntry _blockPoison; private ConfigEntry _blockCrab; private ConfigEntry _blockCurse; private ConfigEntry _blockDrowsy; private ConfigEntry _blockHot; private ConfigEntry _blockThorns; private ConfigEntry _blockSpores; private ConfigEntry _blockWeb; internal static bool syncedDynamicMallows; internal static bool syncedMallowDecrease; internal static bool syncedMallowMultiplierEnabled; internal static int syncedMallowMultiplier; internal static float syncedGlizzyChance; internal static bool syncedDynamicBackpacks; internal static bool syncedBackpackDecrease; internal static bool syncedEnableSoloMoraleBoost; internal static float syncedMoraleBoostStamina; internal static float syncedMoraleBoostRadius; internal static bool syncedHealAfflictionsEnabled; internal static float syncedHealPerSecond; internal static bool syncedHealAllStatuses; internal static bool syncedHealInjury; internal static bool syncedHealDrowsy; internal static bool syncedHealPoison; internal static bool syncedHealCold; internal static bool syncedHealCurse; internal static bool syncedHealHunger; internal static bool syncedHealHot; internal static bool syncedHealThorns; internal static bool syncedHealSpores; internal static bool syncedHealWeb; internal static bool syncedHealCrab; internal static bool syncedRespawnEnabled; internal static bool syncedBlockNegativeStatus; internal static bool syncedBlockAllStatuses; internal static bool syncedRemoveRestriction; internal static bool syncedRevivePassedOut; internal static bool syncedEnforceRevivePenalty; internal static bool syncedBlockInjury; internal static bool syncedBlockCold; internal static bool syncedBlockPoison; internal static bool syncedBlockCrab; internal static bool syncedBlockCurse; internal static bool syncedBlockDrowsy; internal static bool syncedBlockHot; internal static bool syncedBlockThorns; internal static bool syncedBlockSpores; internal static bool syncedBlockWeb; public static CampfireHealingMod Instance { get; private set; } public static ManualLogSource Log { get; private set; } public static bool IsModActive => CampfireModAuthorizationService.IsGameplayEnabled; private void Awake() { //IL_00cb: Unknown result type (might be due to invalid IL or missing references) //IL_00d5: Expected O, but got Unknown //IL_016c: Unknown result type (might be due to invalid IL or missing references) //IL_0176: Expected O, but got Unknown //IL_05a7: Unknown result type (might be due to invalid IL or missing references) //IL_05ad: Expected O, but got Unknown Instance = this; Log = ((BaseUnityPlugin)this).Logger; _dynamicMallows = ((BaseUnityPlugin)this).Config.Bind("Dynamic Mallows", "EnableDynamicMallows", true, "Match campfire marshmallow/glizzy pile size to current room player count (including late joiners)."); _mallowDecrease = ((BaseUnityPlugin)this).Config.Bind("Dynamic Mallows", "AllowMallowDecrease", true, "When players leave, remove unclaimed pile mallows down to the target count. Increase always happens while EnableDynamicMallows is on."); _mallowMultiplierEnabled = ((BaseUnityPlugin)this).Config.Bind("Dynamic Mallows", "EnableMallowMultiplier", false, "Multiply target mallow count by MallowMultiplier (players * multiplier). Works with or without AllowMallowDecrease."); _mallowMultiplier = ((BaseUnityPlugin)this).Config.Bind("Dynamic Mallows", "MallowMultiplier", "2", "Target mallows = players * this value when EnableMallowMultiplier is on. Type a whole number (minimum 1)."); _glizzyChance = ((BaseUnityPlugin)this).Config.Bind("Dynamic Mallows", "GlizzyChance", 0.25f, new ConfigDescription("Chance (0.00-1.00) each campfire food spawn is a Glizzy (vanilla spawners + extras). Remainder are Marshmallows. 0.01 = 1%, 0.5 = 50/50, 1.0 = always Glizzy.", (AcceptableValueBase)(object)new AcceptableValueRange(0f, 1f), Array.Empty())); _dynamicBackpacks = ((BaseUnityPlugin)this).Config.Bind("Dynamic Mallows", "EnableDynamicBackpacks", true, "Match campfire backpack count to current room player count (including late joiners). Spawns extras with slight X/Z scatter so they do not overlap."); _backpackDecrease = ((BaseUnityPlugin)this).Config.Bind("Dynamic Mallows", "AllowBackpackDecrease", true, "When players leave, remove unclaimed campfire backpacks down to the target count. Increase always happens while EnableDynamicBackpacks is on."); _enableSoloMoraleBoost = ((BaseUnityPlugin)this).Config.Bind("Morale Boosts", "EnableSoloMoraleBoost", true, "Allow campfire morale boosts with only 1 player in radius. Vanilla requires 2+ players."); _moraleBoostStamina = ((BaseUnityPlugin)this).Config.Bind("Morale Boosts", "MoraleBoostStamina", 0.25f, new ConfigDescription("Extra stamina granted by a campfire morale boost (0.00-1.00). Vanilla baseline is 0.25.", (AcceptableValueBase)(object)new AcceptableValueRange(0f, 1f), Array.Empty())); _moraleBoostRadius = ((BaseUnityPlugin)this).Config.Bind("Morale Boosts", "MoraleBoostRadius", 0f, "Override each progression campfire's moraleBoostRadius (0 = vanilla per-campfire). Affects NoHunger, injury burst, heal, block, lighting gather, morale, and unlock radius together."); _removeRestriction = ((BaseUnityPlugin)this).Config.Bind("Remove Restriction", "EnableRemoveRestriction", true, "Allow lighting a campfire without everyone nearby. Defers morale boost and next-segment unlock until every player is alive and inside moraleBoostRadius. When off, lighting uses vanilla everyone-in-range rules."); _respawnEnabled = ((BaseUnityPlugin)this).Config.Bind("Campfire Revives", "RespawnOnCampfire", true, "Revive eligible players when a progression campfire is lit, and allow hold-interact revive on already-lit campfires (dead always; passed-out only if RevivePassedOut is on)."); _revivePassedOut = ((BaseUnityPlugin)this).Config.Bind("Campfire Revives", "RevivePassedOut", false, "When on, campfire revives also include fully passed-out players. When off, only fully dead players are revived."); _enforceRevivePenalty = ((BaseUnityPlugin)this).Config.Bind("Campfire Revives", "EnforceRevivePenalty", false, "When off, campfire revives use vanilla Curse (0.05) + Hunger (0.3). When on, force both on campfire revives only (ignores Block; Heal can still clear them at the fire). Hunger uses SetStatus so campfire NoHunger cannot block it."); _healAfflictionsEnabled = ((BaseUnityPlugin)this).Config.Bind("Heal Afflictions", "HealAfflictions", true, "Master toggle for continuous campfire healing."); _healPerSecond = ((BaseUnityPlugin)this).Config.Bind("Heal Afflictions", "HealPerSecond", 0.025f, "Affliction removed once per second while inside moraleBoostRadius of a lit campfire"); _healAllStatuses = ((BaseUnityPlugin)this).Config.Bind("Heal Afflictions", "HealAllStatuses", true, "When on (default), continuously heal all supported statuses near campfires (ignores per-type Heal toggles). Turn off to customize with the individual Heal* options. Vanilla first-light Injury -0.2 still applies separately."); _healInjury = ((BaseUnityPlugin)this).Config.Bind("Heal Afflictions", "HealInjury", false, "Continuously heal Injury near campfires (only used when HealAllStatuses is off). Off = only vanilla first-light Injury -0.2."); _healDrowsy = ((BaseUnityPlugin)this).Config.Bind("Heal Afflictions", "HealDrowsy", false, "Continuously heal Drowsy near campfires (only used when HealAllStatuses is off)"); _healPoison = ((BaseUnityPlugin)this).Config.Bind("Heal Afflictions", "HealPoison", false, "Continuously heal Poison near campfires (only used when HealAllStatuses is off)"); _healCold = ((BaseUnityPlugin)this).Config.Bind("Heal Afflictions", "HealCold", false, "Continuously heal Cold near campfires (only used when HealAllStatuses is off)"); _healCurse = ((BaseUnityPlugin)this).Config.Bind("Heal Afflictions", "HealCurse", false, "Continuously heal Curse near campfires (only used when HealAllStatuses is off)"); _healHunger = ((BaseUnityPlugin)this).Config.Bind("Heal Afflictions", "HealHunger", false, "Continuously heal Hunger near campfires (only used when HealAllStatuses is off; vanilla NoHunger still prevents new hunger)"); _healHot = ((BaseUnityPlugin)this).Config.Bind("Heal Afflictions", "HealHot", false, "Continuously heal Hot near campfires (only used when HealAllStatuses is off)"); _healThorns = ((BaseUnityPlugin)this).Config.Bind("Heal Afflictions", "HealThorns", false, "Continuously heal Thorns near campfires (only used when HealAllStatuses is off)"); _healSpores = ((BaseUnityPlugin)this).Config.Bind("Heal Afflictions", "HealSpores", false, "Continuously heal Spores near campfires (only used when HealAllStatuses is off)"); _healWeb = ((BaseUnityPlugin)this).Config.Bind("Heal Afflictions", "HealWeb", false, "Continuously heal Web near campfires (only used when HealAllStatuses is off)"); _healCrab = ((BaseUnityPlugin)this).Config.Bind("Heal Afflictions", "HealCrab", false, "Continuously heal Crab near campfires (only used when HealAllStatuses is off)"); _blockNegativeStatus = ((BaseUnityPlugin)this).Config.Bind("Block Afflictions", "BlockNegativeStatus", true, "Master toggle for blocking configured afflictions near lit campfires (Harmony gate on AddStatus/AdjustStatus while in moraleBoostRadius)."); _blockAllStatuses = ((BaseUnityPlugin)this).Config.Bind("Block Afflictions", "BlockAllStatuses", true, "When on (default), block all positive status gains near campfires (ignores per-type Block toggles). Turn off to customize with the individual Block* options."); _blockInjury = ((BaseUnityPlugin)this).Config.Bind("Block Afflictions", "BlockInjury", false, "Block Injury near campfires (only used when BlockAllStatuses is off)"); _blockCold = ((BaseUnityPlugin)this).Config.Bind("Block Afflictions", "BlockCold", false, "Block Cold near campfires (only used when BlockAllStatuses is off)"); _blockPoison = ((BaseUnityPlugin)this).Config.Bind("Block Afflictions", "BlockPoison", false, "Block Poison near campfires (only used when BlockAllStatuses is off)"); _blockCrab = ((BaseUnityPlugin)this).Config.Bind("Block Afflictions", "BlockCrab", false, "Block Crab near campfires (only used when BlockAllStatuses is off)"); _blockCurse = ((BaseUnityPlugin)this).Config.Bind("Block Afflictions", "BlockCurse", false, "Block Curse near campfires (only used when BlockAllStatuses is off)"); _blockDrowsy = ((BaseUnityPlugin)this).Config.Bind("Block Afflictions", "BlockDrowsy", false, "Block Drowsy near campfires (only used when BlockAllStatuses is off)"); _blockHot = ((BaseUnityPlugin)this).Config.Bind("Block Afflictions", "BlockHot", false, "Block Hot near campfires (only used when BlockAllStatuses is off)"); _blockThorns = ((BaseUnityPlugin)this).Config.Bind("Block Afflictions", "BlockThorns", false, "Block Thorns near campfires (only used when BlockAllStatuses is off)"); _blockSpores = ((BaseUnityPlugin)this).Config.Bind("Block Afflictions", "BlockSpores", false, "Block Spores near campfires (only used when BlockAllStatuses is off)"); _blockWeb = ((BaseUnityPlugin)this).Config.Bind("Block Afflictions", "BlockWeb", false, "Block Web near campfires (only used when BlockAllStatuses is off)"); ApplySnapshot(BuildSnapshot()); BindConfigChangedHandlers(); CampfireModAuthorizationService.Initialize(); CampfireRestrictionService.RegisterPhotonEvents(); Harmony val = new Harmony("tony4twentys.Healing_Campfires"); Type[] types = typeof(CampfireHealingMod).Assembly.GetTypes(); foreach (Type type in types) { if (type.GetCustomAttributes(typeof(HarmonyPatch), inherit: true).Length != 0) { try { val.CreateClassProcessor(type).Patch(); } catch (Exception arg) { ((BaseUnityPlugin)this).Logger.LogError((object)$"Failed to apply Harmony patch class {type.Name}: {arg}"); } } } ((BaseUnityPlugin)this).Logger.LogInfo((object)string.Format("{0} v{1} loaded. DynamicMallows: {2}. Heal: {3} @ {4}/s.", "Healing Campfires", "4.5.0", _dynamicMallows.Value, _healAfflictionsEnabled.Value, _healPerSecond.Value)); } private void Update() { CampfireDynamicMallowService.Tick(); CampfireDynamicBackpackService.Tick(); } internal CampfireConfigSnapshot BuildSnapshot() { return new CampfireConfigSnapshot { HealPerSecond = _healPerSecond.Value, MoraleBoostRadius = Math.Max(0f, _moraleBoostRadius.Value), HealAfflictionsEnabled = _healAfflictionsEnabled.Value, HealAllStatuses = _healAllStatuses.Value, RespawnEnabled = _respawnEnabled.Value, BlockNegativeStatus = _blockNegativeStatus.Value, BlockAllStatuses = _blockAllStatuses.Value, RemoveRestriction = _removeRestriction.Value, RevivePassedOut = _revivePassedOut.Value, EnforceRevivePenalty = _enforceRevivePenalty.Value, DynamicMallows = _dynamicMallows.Value, MallowDecrease = _mallowDecrease.Value, MallowMultiplierEnabled = _mallowMultiplierEnabled.Value, MallowMultiplier = ParseMallowMultiplier(_mallowMultiplier.Value), GlizzyChance = Mathf.Clamp01(_glizzyChance.Value), DynamicBackpacks = _dynamicBackpacks.Value, BackpackDecrease = _backpackDecrease.Value, EnableSoloMoraleBoost = _enableSoloMoraleBoost.Value, MoraleBoostStamina = Mathf.Clamp01(_moraleBoostStamina.Value), HealInjury = _healInjury.Value, HealDrowsy = _healDrowsy.Value, HealPoison = _healPoison.Value, HealCold = _healCold.Value, HealCurse = _healCurse.Value, HealHunger = _healHunger.Value, HealHot = _healHot.Value, HealThorns = _healThorns.Value, HealSpores = _healSpores.Value, HealWeb = _healWeb.Value, HealCrab = _healCrab.Value, BlockInjury = _blockInjury.Value, BlockCold = _blockCold.Value, BlockPoison = _blockPoison.Value, BlockCrab = _blockCrab.Value, BlockCurse = _blockCurse.Value, BlockDrowsy = _blockDrowsy.Value, BlockHot = _blockHot.Value, BlockThorns = _blockThorns.Value, BlockSpores = _blockSpores.Value, BlockWeb = _blockWeb.Value }; } internal void ApplySnapshot(CampfireConfigSnapshot snapshot) { if (snapshot != null) { syncedHealPerSecond = snapshot.HealPerSecond; syncedMoraleBoostRadius = Math.Max(0f, snapshot.MoraleBoostRadius); syncedHealAfflictionsEnabled = snapshot.HealAfflictionsEnabled; syncedHealAllStatuses = snapshot.HealAllStatuses; syncedHealInjury = snapshot.HealInjury; syncedHealDrowsy = snapshot.HealDrowsy; syncedHealPoison = snapshot.HealPoison; syncedHealCold = snapshot.HealCold; syncedHealCurse = snapshot.HealCurse; syncedHealHunger = snapshot.HealHunger; syncedHealHot = snapshot.HealHot; syncedHealThorns = snapshot.HealThorns; syncedHealSpores = snapshot.HealSpores; syncedHealWeb = snapshot.HealWeb; syncedHealCrab = snapshot.HealCrab; syncedRespawnEnabled = snapshot.RespawnEnabled; syncedBlockNegativeStatus = snapshot.BlockNegativeStatus; syncedBlockAllStatuses = snapshot.BlockAllStatuses; syncedRemoveRestriction = snapshot.RemoveRestriction; syncedRevivePassedOut = snapshot.RevivePassedOut; syncedEnforceRevivePenalty = snapshot.EnforceRevivePenalty; syncedDynamicMallows = snapshot.DynamicMallows; syncedMallowDecrease = snapshot.MallowDecrease; syncedMallowMultiplierEnabled = snapshot.MallowMultiplierEnabled; syncedMallowMultiplier = Math.Max(1, snapshot.MallowMultiplier); syncedGlizzyChance = Mathf.Clamp01(snapshot.GlizzyChance); syncedDynamicBackpacks = snapshot.DynamicBackpacks; syncedBackpackDecrease = snapshot.BackpackDecrease; syncedEnableSoloMoraleBoost = snapshot.EnableSoloMoraleBoost; syncedMoraleBoostStamina = Mathf.Clamp01(snapshot.MoraleBoostStamina); syncedBlockInjury = snapshot.BlockInjury; syncedBlockCold = snapshot.BlockCold; syncedBlockPoison = snapshot.BlockPoison; syncedBlockCrab = snapshot.BlockCrab; syncedBlockCurse = snapshot.BlockCurse; syncedBlockDrowsy = snapshot.BlockDrowsy; syncedBlockHot = snapshot.BlockHot; syncedBlockThorns = snapshot.BlockThorns; syncedBlockSpores = snapshot.BlockSpores; syncedBlockWeb = snapshot.BlockWeb; CampfireRadiusService.ApplyToAllCampfires(); } } private void BindConfigChangedHandlers() { _dynamicMallows.SettingChanged += OnGameplayConfigChanged; _mallowDecrease.SettingChanged += OnGameplayConfigChanged; _mallowMultiplierEnabled.SettingChanged += OnGameplayConfigChanged; _mallowMultiplier.SettingChanged += OnGameplayConfigChanged; _glizzyChance.SettingChanged += OnGameplayConfigChanged; _dynamicBackpacks.SettingChanged += OnGameplayConfigChanged; _backpackDecrease.SettingChanged += OnGameplayConfigChanged; _enableSoloMoraleBoost.SettingChanged += OnGameplayConfigChanged; _moraleBoostStamina.SettingChanged += OnGameplayConfigChanged; _moraleBoostRadius.SettingChanged += OnGameplayConfigChanged; _removeRestriction.SettingChanged += OnGameplayConfigChanged; _respawnEnabled.SettingChanged += OnGameplayConfigChanged; _revivePassedOut.SettingChanged += OnGameplayConfigChanged; _enforceRevivePenalty.SettingChanged += OnGameplayConfigChanged; _healAfflictionsEnabled.SettingChanged += OnGameplayConfigChanged; _healPerSecond.SettingChanged += OnGameplayConfigChanged; _healAllStatuses.SettingChanged += OnGameplayConfigChanged; _healInjury.SettingChanged += OnGameplayConfigChanged; _healDrowsy.SettingChanged += OnGameplayConfigChanged; _healPoison.SettingChanged += OnGameplayConfigChanged; _healCold.SettingChanged += OnGameplayConfigChanged; _healCurse.SettingChanged += OnGameplayConfigChanged; _healHunger.SettingChanged += OnGameplayConfigChanged; _healHot.SettingChanged += OnGameplayConfigChanged; _healThorns.SettingChanged += OnGameplayConfigChanged; _healSpores.SettingChanged += OnGameplayConfigChanged; _healWeb.SettingChanged += OnGameplayConfigChanged; _healCrab.SettingChanged += OnGameplayConfigChanged; _blockNegativeStatus.SettingChanged += OnGameplayConfigChanged; _blockAllStatuses.SettingChanged += OnGameplayConfigChanged; _blockInjury.SettingChanged += OnGameplayConfigChanged; _blockCold.SettingChanged += OnGameplayConfigChanged; _blockPoison.SettingChanged += OnGameplayConfigChanged; _blockCrab.SettingChanged += OnGameplayConfigChanged; _blockCurse.SettingChanged += OnGameplayConfigChanged; _blockDrowsy.SettingChanged += OnGameplayConfigChanged; _blockHot.SettingChanged += OnGameplayConfigChanged; _blockThorns.SettingChanged += OnGameplayConfigChanged; _blockSpores.SettingChanged += OnGameplayConfigChanged; _blockWeb.SettingChanged += OnGameplayConfigChanged; } private void OnGameplayConfigChanged(object sender, EventArgs args) { CampfireModAuthorizationService.OnHostConfigChangedLocally(); if (PhotonNetwork.IsMasterClient) { CampfireDynamicMallowService.SyncAllCampfiresNow("config-changed"); CampfireDynamicBackpackService.SyncAllCampfiresNow("config-changed"); } } internal static int ParseMallowMultiplier(string raw) { if (string.IsNullOrWhiteSpace(raw)) { return 2; } if (int.TryParse(raw.Trim(), out var result)) { return Math.Max(1, result); } if (float.TryParse(raw.Trim(), NumberStyles.Float, CultureInfo.InvariantCulture, out var result2)) { return Math.Max(1, Mathf.RoundToInt(result2)); } return 2; } internal static void RespawnAllDeadPlayers(Vector3 position) { //IL_014c: Unknown result type (might be due to invalid IL or missing references) //IL_00bf: Unknown result type (might be due to invalid IL or missing references) //IL_00c0: Unknown result type (might be due to invalid IL or missing references) //IL_00ca: Unknown result type (might be due to invalid IL or missing references) //IL_00cf: Unknown result type (might be due to invalid IL or missing references) //IL_00d4: Unknown result type (might be due to invalid IL or missing references) //IL_00de: Unknown result type (might be due to invalid IL or missing references) //IL_00e3: Unknown result type (might be due to invalid IL or missing references) //IL_00e8: Unknown result type (might be due to invalid IL or missing references) //IL_00ff: Unknown result type (might be due to invalid IL or missing references) if (!PhotonNetwork.IsMasterClient) { return; } List allCharacters = Character.AllCharacters; if (allCharacters == null) { return; } bool flag = !syncedEnforceRevivePenalty; int num = 0; foreach (Character item in allCharacters) { if (!((Object)(object)item == (Object)null) && !((Object)(object)item.data == (Object)null) && !item.isBot && (item.data.dead || (syncedRevivePassedOut && item.data.fullyPassedOut)) && !((Object)(object)((MonoBehaviourPun)item).photonView == (Object)null)) { Vector3 val = position + Vector3.up * 1.5f + Random.insideUnitSphere * 2f; ((MonoBehaviourPun)item).photonView.RPC("RPCA_ReviveAtPosition", (RpcTarget)0, new object[3] { val, flag, -1 }); num++; } } if (num > 0) { CampfireRestrictionService.DelayUnlockAfterRevive(position); ManualLogSource log = Log; if (log != null) { log.LogInfo((object)$"Revived {num} dead/passed-out player(s) at campfire (enforcePenalty={syncedEnforceRevivePenalty})."); } } } internal static void ApplyForcedCampfireRevivePenalties(CharacterAfflictions afflictions) { if (!((Object)(object)afflictions == (Object)null)) { afflictions.AddStatus((STATUSTYPE)5, 0.05f, true, true, true); if ((Object)(object)afflictions.character != (Object)null && (Object)(object)((MonoBehaviourPun)afflictions.character).photonView != (Object)null && ((MonoBehaviourPun)afflictions.character).photonView.IsMine) { float currentStatus = afflictions.GetCurrentStatus((STATUSTYPE)1); afflictions.SetStatus((STATUSTYPE)1, currentStatus + 0.3f, true); } else { afflictions.AddStatus((STATUSTYPE)1, 0.3f, true, true, true); } } } } }