Decompiled source of Healing Campfires v4.5.0
tony4twentys-Healing Campfires.dll
Decompiled a week ago
The result has been truncated due to the large size, download it to view full contents!
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<Transform> SpawnPoints = new List<Transform>(); public readonly List<PhotonView> TrackedViews = new List<PhotonView>(); 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<int, CampfireBackpackState> StatesByRootId = new Dictionary<int, CampfireBackpackState>(); 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<SingleItemSpawner>((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<PhotonView> 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<int> list = new List<int>(); foreach (KeyValuePair<int, CampfireBackpackState> 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<Campfire>(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<SingleItemSpawner>((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<Campfire>() != (Object)null) { return ((Component)val).gameObject; } val = val.parent; } Campfire[] array = Object.FindObjectsByType<Campfire>((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<SingleItemSpawner>(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<SingleItemSpawner>(); 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<Component>(); 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<int> hashSet = new HashSet<int>(); 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<Item>((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<Item>(); 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<Campfire>(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<PhotonView>(); if ((Object)(object)component == (Object)null) { return false; } Item component2 = val3.GetComponent<Item>(); 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<Item>((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<PhotonView> __result) { CampfireDynamicBackpackService.TrackVanillaSpawnedViews(__instance, __result); } } internal static class CampfireDynamicMallowService { private sealed class CampfireMallowState { public GameObject Root; public string PrefabName = "Marshmallow"; public readonly List<Transform> SpawnPoints = new List<Transform>(); public readonly List<PhotonView> TrackedViews = new List<PhotonView>(); 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<int, CampfireMallowState> StatesByRootId = new Dictionary<int, CampfireMallowState>(); 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<PhotonView> 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<PhotonView>(); return true; } if (spawner.playersInRoomRequirement > PhotonNetwork.PlayerList.Length) { result = new List<PhotonView>(); 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<PhotonView>(); } } 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<PhotonView>(); } } 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<PhotonView>(); return true; } spawner.InitializePhysics(val4); result = new List<PhotonView> { 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<PhotonView> 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<int> list = new List<int>(); foreach (KeyValuePair<int, CampfireMallowState> 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<Campfire>(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<Transform>((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<Campfire>(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<Transform>(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<SingleItemSpawner>(); 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<Component>(); 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<int> hashSet = new HashSet<int>(); 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<Item>((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<Item>(); 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<PhotonView>(); if ((Object)(object)component == (Object)null) { return false; } Item component2 = val.GetComponent<Item>(); 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<Campfire>(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<Item>((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<PhotonView> __result) { if (!CampfireDynamicMallowService.TrySpawnCampfireFoodWithGlizzyChance(__instance, out var result)) { return true; } __result = result ?? new List<PhotonView>(); return false; } private static void Postfix(SingleItemSpawner __instance, List<PhotonView> __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<Player>)delegate { if (PhotonNetwork.InRoom && PhotonNetwork.IsMasterClient && _hostHasHealingCampfiresMod) { CampfireConfigSyncService.ApplyLocalAuthoritativeConfig(); PublishHostConfigIfMaster(); } }); _photonManager.RegisterRoomProperty<string>("healingCampfiresConfigV2", (RoomEventType)2, (Action<string>)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<int, float> OriginalRadiusById = new Dictionary<int, float>(); 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<Campfire>((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<int, PendingCampfireState> PendingByInstanceId = new Dictionary<int, PendingCampfireState>(); 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<Character> 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<AchievementManager>.Instance != (Object)null) { Singleton<AchievementManager>.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<Character> 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<Character> allCharacters = Character.AllCharacters; if (allCharacters == null) { return; } List<Character> list = new List<Character>(); 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 CampfireHelp