Please disclose if any significant portion of your mod was created using AI tools by adding the 'AI Generated' category. Failing to do so may result in the mod being removed from Thunderstore.
Decompiled source of ReefClient v0.6.5
ReefClient.dll
Decompiled a day 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.IO; using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.Versioning; using BepInEx; using BepInEx.Bootstrap; using BepInEx.Configuration; using BepInEx.Logging; using HarmonyLib; using Microsoft.CodeAnalysis; using TMPro; using UnityEngine; using UnityEngine.UI; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)] [assembly: TargetFramework(".NETFramework,Version=v4.7.2", FrameworkDisplayName = ".NET Framework 4.7.2")] [assembly: AssemblyCompany("ReefClient")] [assembly: AssemblyConfiguration("Release")] [assembly: AssemblyFileVersion("0.6.5.0")] [assembly: AssemblyInformationalVersion("0.6.5+1610e87379a7e5606a8e54743b3a1516f4c67255")] [assembly: AssemblyProduct("ReefClient")] [assembly: AssemblyTitle("ReefClient")] [assembly: AssemblyVersion("0.6.5.0")] [module: RefSafetyRules(11)] namespace Microsoft.CodeAnalysis { [CompilerGenerated] [Embedded] internal sealed class EmbeddedAttribute : Attribute { } } namespace System.Runtime.CompilerServices { [CompilerGenerated] [Embedded] [AttributeUsage(AttributeTargets.Module, AllowMultiple = false, Inherited = false)] internal sealed class RefSafetyRulesAttribute : Attribute { public readonly int Version; public RefSafetyRulesAttribute(int P_0) { Version = P_0; } } } namespace ReefClient { internal static class Deposit { internal static ConfigEntry<bool> Enabled; internal static ConfigEntry<KeyboardShortcut> Key; internal static ConfigEntry<float> Range; internal static ConfigEntry<bool> AnyChest; internal static ConfigEntry<int> FirstRow; private static bool wasOpen; private static readonly List<Container> cache = new List<Container>(); private static readonly MethodInfo checkAccess = AccessTools.Method(typeof(Container), "CheckAccess", new Type[1] { typeof(long) }, (Type[])null); private static readonly FieldRef<Container, bool> checkGuardStone = AccessTools.FieldRefAccess<Container, bool>("m_checkGuardStone"); private static int keptInSlots; private static MethodInfo eaqsIsItemInSlot; private static bool eaqsLooked; private static bool PrivacyAllows(Container c, long pid) { if (checkAccess == null) { return true; } return (bool)checkAccess.Invoke(c, new object[1] { pid }); } internal static void Bind(ConfigFile cfg) { //IL_002d: 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_0074: Expected O, but got Unknown //IL_00b1: Unknown result type (might be due to invalid IL or missing references) //IL_00bb: Expected O, but got Unknown Enabled = cfg.Bind<bool>("Deposit", "Enabled", true, "Quick deposit: with the inventory open, press the key to move the rows from 'First row' down into nearby chests (only chests already holding the item, unless 'Any chest')."); Key = cfg.Bind<KeyboardShortcut>("Deposit", "Key", new KeyboardShortcut((KeyCode)114, Array.Empty<KeyCode>()), "Key pressed while the inventory is open."); Range = cfg.Bind<float>("Deposit", "Range", 20f, new ConfigDescription("Radius in metres around you (chests are listed when the inventory opens).", (AcceptableValueBase)(object)new AcceptableValueRange<float>(5f, 60f), Array.Empty<object>())); AnyChest = cfg.Bind<bool>("Deposit", "Any chest", false, "true = what no chest already holds goes to the nearest chest with a free slot. The Reef: false — only matching chests receive, the rest stays with you."); FirstRow = cfg.Bind<int>("Deposit", "First row", 3, new ConfigDescription("Rows from this one down are deposited (1 = hotbar, 4 = last row of the vanilla grid); rows above stay with you. The Reef: 3 = rows 3 and 4 go, hotbar and row 2 stay.", (AcceptableValueBase)(object)new AcceptableValueRange<int>(1, 4), Array.Empty<object>())); } internal static void Tick() { //IL_00ad: 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) if (Enabled == null || !Enabled.Value) { return; } Player localPlayer = Player.m_localPlayer; if ((Object)(object)localPlayer == (Object)null || (Object)(object)InventoryGui.instance == (Object)null) { wasOpen = false; return; } bool flag = InventoryGui.IsVisible(); if (flag && !wasOpen) { try { Scan(localPlayer); } catch (Exception ex) { ReefClientPlugin.Log.LogWarning((object)("deposit scan: " + ex.GetType().Name + ": " + ex.Message)); } } wasOpen = flag; if (!flag || ((Object)(object)Chat.instance != (Object)null && Chat.instance.HasFocus()) || Console.IsVisible()) { return; } KeyboardShortcut value = Key.Value; if (!((KeyboardShortcut)(ref value)).IsDown()) { return; } try { Run(localPlayer); } catch (Exception ex2) { ReefClientPlugin.Log.LogWarning((object)("deposit: " + ex2.GetType().Name + ": " + ex2.Message)); } } private static void Scan(Player player) { //IL_0010: Unknown result type (might be due to invalid IL or missing references) cache.Clear(); Collider[] array = Physics.OverlapSphere(((Component)player).transform.position, Range.Value); for (int i = 0; i < array.Length; i++) { Container componentInParent = ((Component)array[i]).GetComponentInParent<Container>(); if (!((Object)(object)componentInParent == (Object)null) && !cache.Contains(componentInParent) && !((Object)(object)((Component)componentInParent).GetComponentInParent<Character>() != (Object)null)) { ZNetView component = ((Component)componentInParent).GetComponent<ZNetView>(); if (!((Object)(object)component == (Object)null) && component.IsValid()) { cache.Add(componentInParent); } } } } private static bool Eligible(ItemData it, Player player) { if (it == null || it.m_equipped || ((Humanoid)player).IsItemEquiped(it)) { return false; } if (it.m_gridPos.y < FirstRow.Value - 1) { return false; } if (it.m_customData != null && it.m_customData.ContainsKey("eaqs_slot")) { return false; } if (InEaqsSlot(it)) { keptInSlots++; return false; } return true; } private static bool InEaqsSlot(ItemData it) { if (!eaqsLooked) { eaqsLooked = true; try { Type type = Type.GetType("EquipmentAndQuickSlots.Slots, EquipmentAndQuickSlots"); eaqsIsItemInSlot = ((type == null) ? null : type.GetMethod("IsItemInSlot", new Type[1] { typeof(ItemData) })); ReefClientPlugin.Log.LogInfo((object)("deposit: EAQS " + ((eaqsIsItemInSlot == null) ? "not found (no slot filter)" : "Slots.IsItemInSlot bound"))); } catch (Exception ex) { ReefClientPlugin.Log.LogWarning((object)("deposit: EAQS lookup failed: " + ex.Message)); } } if (eaqsIsItemInSlot == null) { return false; } try { return (bool)eaqsIsItemInSlot.Invoke(null, new object[1] { it }); } catch { return false; } } private static Inventory Usable(Container c, long pid, ref int skipped) { //IL_0044: Unknown result type (might be due to invalid IL or missing references) ZNetView component = ((Component)c).GetComponent<ZNetView>(); ZDO zDO = component.GetZDO(); if (c.IsInUse() || (zDO != null && zDO.GetInt(ZDOVars.s_inUse, 0) == 1)) { skipped++; return null; } if (checkGuardStone.Invoke(c) && !PrivateArea.CheckAccess(((Component)c).transform.position, 0f, false, false)) { skipped++; return null; } if (!PrivacyAllows(c, pid)) { skipped++; return null; } Inventory inventory = c.GetInventory(); if (inventory == null) { return null; } if (!component.IsOwner()) { component.ClaimOwnership(); } if (!component.IsOwner()) { skipped++; ReefClientPlugin.Log.LogInfo((object)("deposit: could not own " + ((Object)c).name + " — skipped")); return null; } return inventory; } private static int Move(Inventory inv, Inventory target, ItemData it) { int stack = it.m_stack; int num = target.FindFreeStackSpace(it.m_shared.m_name, (float)it.m_worldLevel); if (num >= stack || target.HaveEmptySlot()) { if (target.AddItem(it)) { inv.RemoveItem(it); return stack; } if (it.m_stack < stack) { return stack - it.m_stack; } return 0; } if (num > 0) { ItemData val = it.Clone(); val.m_stack = num; if (target.AddItem(val)) { inv.RemoveItem(it, num); return num; } } return 0; } private static void Run(Player player) { //IL_0057: Unknown result type (might be due to invalid IL or missing references) //IL_005c: Unknown result type (might be due to invalid IL or missing references) cache.RemoveAll((Container c) => (Object)(object)c == (Object)null || (Object)(object)((Component)c).GetComponent<ZNetView>() == (Object)null || !((Component)c).GetComponent<ZNetView>().IsValid()); Inventory inventory = ((Humanoid)player).GetInventory(); long playerID = player.GetPlayerID(); int num = 0; int skipped = 0; keptInSlots = 0; HashSet<Container> hashSet = new HashSet<Container>(); Vector3 pos = ((Component)player).transform.position; List<Container> list = new List<Container>(cache); list.Sort((Container a, Container b) => Vector3.Distance(((Component)a).transform.position, pos).CompareTo(Vector3.Distance(((Component)b).transform.position, pos))); foreach (Container item in list) { bool flag = false; foreach (ItemData allItem in inventory.GetAllItems()) { if (Eligible(allItem, player) && item.GetInventory() != null && item.GetInventory().ContainsItemByName(allItem.m_shared.m_name)) { flag = true; break; } } if (!flag) { continue; } Inventory val = Usable(item, playerID, ref skipped); if (val == null) { continue; } int num2 = 0; foreach (ItemData item2 in new List<ItemData>(inventory.GetAllItems())) { if (Eligible(item2, player) && val.ContainsItemByName(item2.m_shared.m_name)) { num2 += Move(inventory, val, item2); } } if (num2 > 0) { hashSet.Add(item); num += num2; } } List<ItemData> list2 = new List<ItemData>(); if (AnyChest.Value) { foreach (ItemData allItem2 in inventory.GetAllItems()) { if (Eligible(allItem2, player)) { list2.Add(allItem2); } } } foreach (Container item3 in list) { if (list2.Count == 0) { break; } Inventory val2 = Usable(item3, playerID, ref skipped); if (val2 == null) { continue; } int num3 = 0; foreach (ItemData item4 in new List<ItemData>(list2)) { if (val2.HaveEmptySlot() || val2.FindFreeStackSpace(item4.m_shared.m_name, (float)item4.m_worldLevel) > 0) { int num4 = Move(inventory, val2, item4); num3 += num4; if (num4 > 0 && !inventory.ContainsItem(item4)) { list2.Remove(item4); } } } if (num3 > 0) { hashSet.Add(item3); num += num3; } } int count = hashSet.Count; if (num > 0) { ((Character)player).Message((MessageType)2, num + " items deposited into " + count + ((count == 1) ? " chest" : " chests"), 0, (Sprite)null, false); } else { ((Character)player).Message((MessageType)2, (cache.Count == 0) ? "No chest in range" : "Nothing to deposit", 0, (Sprite)null, false); } ReefClientPlugin.Log.LogInfo((object)("deposit: " + num + " items → " + count + " chest(s), " + skipped + " skipped, " + cache.Count + " in range, " + list2.Count + " stack(s) left, " + keptInSlots + " kept in slots (grid " + inventory.GetWidth() + "x" + inventory.GetHeight() + ")")); } } internal static class FavorHud { private static int count = -1; private static GameObject root; private static Image icon; private static TMP_Text label; private static Sprite sprite; private static bool spriteTried; private static float builtSize = -1f; internal static bool EverReceived => count >= 0; internal static int Count => count; internal static void Set(int n) { count = Math.Max(0, n); if ((Object)(object)label != (Object)null) { label.text = count.ToString(); } } internal static void Tick() { Minimap instance = Minimap.instance; if ((Object)(object)instance == (Object)null || (Object)(object)instance.m_smallRoot == (Object)null) { root = null; icon = null; label = null; return; } if ((Object)(object)root == (Object)null || builtSize != ReefClientPlugin.HudSize.Value) { try { Build(instance); } catch (Exception ex) { ReefClientPlugin.Log.LogWarning((object)("favor hud: " + ex.GetType().Name + ": " + ex.Message)); return; } } bool flag = EverReceived || !ReefClientPlugin.HiddenUntilFirstFavor.Value; if (root.activeSelf != flag) { root.SetActive(flag); } } private static void Build(Minimap mm) { //IL_0058: Unknown result type (might be due to invalid IL or missing references) //IL_0062: Expected O, but got Unknown //IL_008a: 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_00b4: Unknown result type (might be due to invalid IL or missing references) //IL_00d3: Unknown result type (might be due to invalid IL or missing references) //IL_00e6: Unknown result type (might be due to invalid IL or missing references) //IL_0122: 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_0140: Unknown result type (might be due to invalid IL or missing references) //IL_0155: Unknown result type (might be due to invalid IL or missing references) //IL_016a: Unknown result type (might be due to invalid IL or missing references) //IL_0181: Unknown result type (might be due to invalid IL or missing references) //IL_018d: Unknown result type (might be due to invalid IL or missing references) //IL_01ec: Unknown result type (might be due to invalid IL or missing references) //IL_0228: Unknown result type (might be due to invalid IL or missing references) //IL_022d: Unknown result type (might be due to invalid IL or missing references) //IL_0246: Unknown result type (might be due to invalid IL or missing references) //IL_025b: Unknown result type (might be due to invalid IL or missing references) //IL_0270: Unknown result type (might be due to invalid IL or missing references) //IL_0287: Unknown result type (might be due to invalid IL or missing references) //IL_029b: Unknown result type (might be due to invalid IL or missing references) //IL_030a: Unknown result type (might be due to invalid IL or missing references) //IL_02e5: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)root != (Object)null) { Object.Destroy((Object)(object)root); } float num = Mathf.Clamp(ReefClientPlugin.HudSize.Value, 8f, 128f); builtSize = ReefClientPlugin.HudSize.Value; root = new GameObject("ReefFavors", new Type[1] { typeof(RectTransform) }); RectTransform component = root.GetComponent<RectTransform>(); ((Transform)component).SetParent(mm.m_smallRoot.transform, false); component.anchorMin = new Vector2(0.5f, 0f); component.anchorMax = new Vector2(0.5f, 0f); component.pivot = new Vector2(0.5f, 1f); component.anchoredPosition = new Vector2(ReefClientPlugin.HudX.Value, ReefClientPlugin.HudY.Value); component.sizeDelta = new Vector2(num * 3f, num); GameObject val = new GameObject("Icon", new Type[3] { typeof(RectTransform), typeof(CanvasRenderer), typeof(Image) }); RectTransform component2 = val.GetComponent<RectTransform>(); ((Transform)component2).SetParent((Transform)(object)component, false); component2.anchorMin = new Vector2(0f, 0.5f); component2.anchorMax = new Vector2(0f, 0.5f); component2.pivot = new Vector2(0f, 0.5f); component2.anchoredPosition = new Vector2(num * 0.5f, 0f); component2.sizeDelta = new Vector2(num, num); icon = val.GetComponent<Image>(); ((Graphic)icon).raycastTarget = false; icon.preserveAspect = true; Sprite val2 = LoadSprite(); if ((Object)(object)val2 != (Object)null) { icon.sprite = val2; } else { ((Graphic)icon).color = new Color(0.95f, 0.8f, 0.35f, 0.9f); } GameObject val3 = new GameObject("Count", new Type[3] { typeof(RectTransform), typeof(CanvasRenderer), typeof(TextMeshProUGUI) }); RectTransform component3 = val3.GetComponent<RectTransform>(); ((Transform)component3).SetParent((Transform)(object)component, false); component3.anchorMin = new Vector2(0f, 0f); component3.anchorMax = new Vector2(1f, 1f); component3.pivot = new Vector2(0f, 0.5f); component3.offsetMin = new Vector2(num * 1.65f, 0f); component3.offsetMax = new Vector2(0f, 0f); label = (TMP_Text)(object)val3.GetComponent<TextMeshProUGUI>(); TMP_Text biomeNameSmall = mm.m_biomeNameSmall; if ((Object)(object)biomeNameSmall != (Object)null) { label.font = biomeNameSmall.font; label.fontSharedMaterial = biomeNameSmall.fontSharedMaterial; ((Graphic)label).color = ((Graphic)biomeNameSmall).color; } else { ((Graphic)label).color = new Color(0.95f, 0.85f, 0.5f, 1f); } label.fontSize = num * 0.85f; label.alignment = (TextAlignmentOptions)4097; label.textWrappingMode = (TextWrappingModes)0; ((Graphic)label).raycastTarget = false; label.text = ((count >= 0) ? count.ToString() : ""); ReefClientPlugin.Log.LogInfo((object)("favor hud built under the minimap (size " + num + ", offset " + ReefClientPlugin.HudX.Value + "," + ReefClientPlugin.HudY.Value + ", sprite " + (((Object)(object)val2 != (Object)null) ? "eye.png" : "fallback") + ")")); } private static Sprite LoadSprite() { //IL_007e: Unknown result type (might be due to invalid IL or missing references) //IL_0084: Expected O, but got Unknown //IL_012f: Unknown result type (might be due to invalid IL or missing references) //IL_013e: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)sprite != (Object)null || spriteTried) { return sprite; } spriteTried = true; try { using Stream stream = Assembly.GetExecutingAssembly().GetManifestResourceStream("ReefClient.Resources.eye.png"); if (stream == null) { ReefClientPlugin.Log.LogWarning((object)"favor hud: embedded eye.png not found"); return null; } byte[] array = new byte[stream.Length]; int num; for (int i = 0; i < array.Length; i += num) { num = stream.Read(array, i, array.Length - i); if (num <= 0) { break; } } Texture2D val = new Texture2D(2, 2, (TextureFormat)4, false); MethodInfo method = typeof(ImageConversion).GetMethod("LoadImage", new Type[2] { typeof(Texture2D), typeof(byte[]) }); if (method == null) { ReefClientPlugin.Log.LogWarning((object)"favor hud: ImageConversion.LoadImage(Texture2D, byte[]) not found"); return null; } if (!(bool)method.Invoke(null, new object[2] { val, array })) { ReefClientPlugin.Log.LogWarning((object)"favor hud: eye.png did not decode"); return null; } ((Texture)val).filterMode = (FilterMode)1; sprite = Sprite.Create(val, new Rect(0f, 0f, (float)((Texture)val).width, (float)((Texture)val).height), new Vector2(0.5f, 0.5f), 100f); return sprite; } catch (Exception ex) { ReefClientPlugin.Log.LogWarning((object)("favor hud sprite: " + ex.GetType().Name + ": " + ex.Message)); return null; } } } [HarmonyPatch(typeof(ZNetView), "InvokeRPC", new Type[] { typeof(string), typeof(object[]) })] internal static class FermenterPull { private static int fixedCount; private static void Prefix(ZNetView __instance, string method, ref object[] parameters) { try { if (method != "RPC_AddItem" || parameters == null || parameters.Length == 0 || !(parameters[0] is string text) || (Object)(object)__instance == (Object)null || (Object)(object)((Component)__instance).GetComponent<Fermenter>() == (Object)null) { return; } bool flag = default(bool); int num; if (parameters.Length > 1) { object obj = parameters[1]; if (obj is bool) { flag = (bool)obj; num = 1; } else { num = 0; } } else { num = 0; } bool flag2 = (byte)((uint)num & (flag ? 1u : 0u)) != 0; parameters = new object[2] { StringExtensionMethods.GetStableHashCode(text), flag2 }; fixedCount++; if (fixedCount <= 3 || fixedCount % 50 == 0) { ReefClientPlugin.Log.LogInfo((object)("fermenter pull: RPC_AddItem(" + text + ") rewritten to the 1.0 shape (hash + cheated) [" + fixedCount + "]")); } } catch (Exception ex) { ReefClientPlugin.Log.LogWarning((object)("fermenter pull: " + ex.GetType().Name + ": " + ex.Message)); } } } [BepInPlugin("reef.client", "Reef Client", "0.6.5")] [BepInDependency(/*Could not decode attribute arguments.*/)] public class ReefClientPlugin : BaseUnityPlugin { public const string GUID = "reef.client"; public const string NAME = "Reef Client"; public const string VERSION = "0.6.5"; public const string BUILD = "reef"; internal static ManualLogSource Log; internal static ConfigEntry<float> HudX; internal static ConfigEntry<float> HudY; internal static ConfigEntry<float> HudSize; internal static ConfigEntry<bool> HiddenUntilFirstFavor; internal static ConfigEntry<float> MaxEnvMinutes; internal static ConfigEntry<bool> SendChatCopy; private Harmony harmony; private void Awake() { //IL_017b: Unknown result type (might be due to invalid IL or missing references) //IL_0185: Expected O, but got Unknown Log = ((BaseUnityPlugin)this).Logger; HudX = ((BaseUnityPlugin)this).Config.Bind<float>("Favors", "HudX", 0f, "Horizontal offset (px) of the favor counter from the bottom-centre of the small minimap."); HudY = ((BaseUnityPlugin)this).Config.Bind<float>("Favors", "HudY", -6f, "Vertical offset (px) of the favor counter from the bottom edge of the small minimap (negative = below)."); HudSize = ((BaseUnityPlugin)this).Config.Bind<float>("Favors", "HudSize", 28f, "Icon height in px; the number scales with it."); HiddenUntilFirstFavor = ((BaseUnityPlugin)this).Config.Bind<bool>("Favors", "HiddenUntilFirstFavor", true, "true = nothing is drawn until the server has sent a favor count at least once this game session."); MaxEnvMinutes = ((BaseUnityPlugin)this).Config.Bind<float>("Weather", "MaxEnvMinutes", 60f, "Safety: a forced sky (Reef_SetEnv) clears itself after this many real minutes even if the server never sends the reset. 0 = never."); SendChatCopy = ((BaseUnityPlugin)this).Config.Bind<bool>("Chat", "SendCopyToServer", true, "Send a copy of every chat line you type to the server (Reef_Chat) so the director hears you even when you play alone. Chat is never READ by this plugin."); Deposit.Bind(((BaseUnityPlugin)this).Config); ReefFood.Bind(((BaseUnityPlugin)this).Config); ReefTorches.Bind(((BaseUnityPlugin)this).Config); ReefStumps.Bind(((BaseUnityPlugin)this).Config); ReefFarming.Bind(((BaseUnityPlugin)this).Config); ReefHarvest.Bind(((BaseUnityPlugin)this).Config); ReefPortals.Bind(((BaseUnityPlugin)this).Config); ReefForgeGate.Bind(((BaseUnityPlugin)this).Config); ReefClock.Bind(((BaseUnityPlugin)this).Config); ReefAdrenaline.Bind(((BaseUnityPlugin)this).Config); ReefRevive.Bind(((BaseUnityPlugin)this).Config); ReefFenrir.Bind(((BaseUnityPlugin)this).Config); ReefSeedBed.Bind(((BaseUnityPlugin)this).Config); ReefCompost.Bind(((BaseUnityPlugin)this).Config); harmony = new Harmony("reef.client"); harmony.PatchAll(typeof(ZNetAwakePatch)); harmony.PatchAll(typeof(ChatSendPatch)); harmony.PatchAll(typeof(FermenterPull)); harmony.PatchAll(typeof(ReefFood.GetTotalFoodValuePatch)); harmony.PatchAll(typeof(ReefTorches.UpdateFireplacePatch)); harmony.PatchAll(typeof(ReefStumps.DamagePatch)); harmony.PatchAll(typeof(ReefStumps.DropPatch)); harmony.PatchAll(typeof(ReefHarvest.InteractPatch)); harmony.PatchAll(typeof(ReefPortals.UpdatePortalPatch)); harmony.PatchAll(typeof(ReefForgeGate.InteractPatch)); harmony.PatchAll(typeof(ReefAdrenaline.AwakePatch)); harmony.PatchAll(typeof(ReefRevive.OnDeathPatch)); harmony.PatchAll(typeof(ReefRevive.AwakeRegisterPatch)); harmony.PatchAll(typeof(ReefRevive.HoverTextPatch)); harmony.PatchAll(typeof(ReefRevive.GuardianPowerPatch)); harmony.PatchAll(typeof(ReefRevive.OnSpawnedPatch)); harmony.PatchAll(typeof(ReefFenrir.ObjectDBAwakePatch)); harmony.PatchAll(typeof(ReefFenrir.ObjectDBCopyPatch)); harmony.PatchAll(typeof(ReefFenrir.OnSpawnedPatch)); harmony.PatchAll(typeof(ReefCompost.MessagePatch)); ReefSeedBed.Patch(harmony); Log.LogInfo((object)"Reef Client 0.6.5 loaded (build: reef)"); } private void OnDestroy() { Harmony obj = harmony; if (obj != null) { obj.UnpatchSelf(); } } private void Update() { ReefFarming.TickOnce(); Receivers.Tick(); FavorHud.Tick(); ReefClock.Tick(); ReefRevive.Tick(); Deposit.Tick(); } } [HarmonyPatch(typeof(ZNet), "Awake")] internal static class ZNetAwakePatch { private static void Postfix(ZNet __instance) { try { if (__instance.IsDedicated()) { ReefClientPlugin.Log.LogInfo((object)"dedicated server: ReefClient registers nothing"); return; } ZRoutedRpc instance = ZRoutedRpc.instance; if (instance == null) { ReefClientPlugin.Log.LogWarning((object)"ZRoutedRpc.instance is null after ZNet.Awake — receivers not registered"); return; } instance.Register<int>("Reef_FavorCount", (Action<long, int>)Receivers.OnFavorCount); instance.Register<string>("Reef_SetEnv", (Action<long, string>)Receivers.OnSetEnv); instance.Register<float, float, float>("Reef_SetWind", (Action<long, float, float, float>)Receivers.OnSetWind); ReefClientPlugin.Log.LogInfo((object)"receivers registered: Reef_FavorCount, Reef_SetEnv, Reef_SetWind"); } catch (Exception ex) { ReefClientPlugin.Log.LogWarning((object)("register: " + ex.GetType().Name + ": " + ex.Message)); } } } [HarmonyPatch(typeof(Chat), "SendText", new Type[] { typeof(Type), typeof(string) })] internal static class ChatSendPatch { private static void Postfix(Type type, string text) { //IL_000e: Unknown result type (might be due to invalid IL or missing references) //IL_0010: Invalid comparison between Unknown and I4 //IL_004e: Unknown result type (might be due to invalid IL or missing references) //IL_0054: Expected I4, but got Unknown try { if (ReefClientPlugin.SendChatCopy.Value && (int)type != 3 && !string.IsNullOrEmpty(text)) { ZNet instance = ZNet.instance; if (!((Object)(object)instance == (Object)null) && !instance.IsServer() && ZRoutedRpc.instance != null) { ZRoutedRpc.instance.InvokeRoutedRPC("Reef_Chat", new object[2] { (int)type, text }); } } } catch (Exception ex) { ReefClientPlugin.Log.LogWarning((object)("Reef_Chat: " + ex.GetType().Name + ": " + ex.Message)); } } } internal static class Receivers { private static float windDeadline = -1f; private static float envDeadline = -1f; private static string heldEnv = ""; internal static void OnFavorCount(long sender, int n) { ReefClientPlugin.Log.LogInfo((object)("Reef_FavorCount " + n + " (from " + sender + ")")); FavorHud.Set(n); } internal static void OnSetEnv(long sender, string name) { name = name ?? ""; EnvMan instance = EnvMan.instance; if ((Object)(object)instance == (Object)null) { ReefClientPlugin.Log.LogWarning((object)("Reef_SetEnv '" + name + "': no EnvMan")); return; } instance.m_debugEnv = name; heldEnv = name; float value = ReefClientPlugin.MaxEnvMinutes.Value; envDeadline = ((name.Length > 0 && value > 0f) ? (Time.realtimeSinceStartup + value * 60f) : (-1f)); ReefClientPlugin.Log.LogInfo((object)("Reef_SetEnv '" + name + "' (from " + sender + ")" + ((name.Length == 0) ? " → natural sky" : ""))); } internal static void OnSetWind(long sender, float angle, float intensity, float minutes) { EnvMan instance = EnvMan.instance; if ((Object)(object)instance == (Object)null) { ReefClientPlugin.Log.LogWarning((object)"Reef_SetWind: no EnvMan"); return; } if (minutes <= 0f) { instance.ResetDebugWind(); windDeadline = -1f; ReefClientPlugin.Log.LogInfo((object)("Reef_SetWind reset (from " + sender + ")")); return; } instance.SetDebugWind(angle, intensity); windDeadline = Time.realtimeSinceStartup + Mathf.Min(minutes, 1440f) * 60f; ReefClientPlugin.Log.LogInfo((object)("Reef_SetWind angle=" + angle + " intensity=" + intensity + " for " + minutes + " min (from " + sender + ")")); } internal static void Tick() { EnvMan instance = EnvMan.instance; if ((Object)(object)instance == (Object)null) { return; } float realtimeSinceStartup = Time.realtimeSinceStartup; if (windDeadline > 0f && realtimeSinceStartup >= windDeadline) { windDeadline = -1f; instance.ResetDebugWind(); ReefClientPlugin.Log.LogInfo((object)"wind hold expired → natural wind"); } if (envDeadline > 0f && realtimeSinceStartup >= envDeadline) { envDeadline = -1f; if (instance.m_debugEnv == heldEnv) { instance.m_debugEnv = ""; } ReefClientPlugin.Log.LogInfo((object)"sky hold expired (MaxEnvMinutes) → natural sky"); } } } internal static class ReefAdrenaline { [HarmonyPatch(typeof(Player), "Awake")] internal static class AwakePatch { private static void Postfix(Player __instance) { try { if (Enabled.Value) { if (!loggedOriginal) { loggedOriginal = true; ReefClientPlugin.Log.LogInfo((object)("adrenaline: prefab decay curve " + Describe(__instance.m_adrenalineDegen) + " ; delay curve " + Describe(__instance.m_adrenalineDegenDelay) + " ; max " + __instance.m_maxAdrenaline)); } float num = Mathf.Clamp(DrainEmpty.Value, 0f, 100f); float num2 = Mathf.Clamp(DrainFull.Value, 0f, 100f); float num3 = Mathf.Clamp(PauseEmpty.Value, 0f, 600f); float num4 = Mathf.Clamp(PauseFull.Value, 0f, 600f); __instance.m_adrenalineDegen = AnimationCurve.Linear(0f, num, 1f, num2); __instance.m_adrenalineDegenDelay = AnimationCurve.Linear(0f, num3, 1f, num4); ReefClientPlugin.Log.LogInfo((object)("adrenaline: drain " + num + "→" + num2 + " /s, pause " + num3 + "→" + num4 + " s (empty→full)")); } } catch (Exception ex) { ReefClientPlugin.Log.LogWarning((object)("adrenaline: " + ex.GetType().Name + ": " + ex.Message)); } } } internal static ConfigEntry<bool> Enabled; internal static ConfigEntry<float> DrainEmpty; internal static ConfigEntry<float> DrainFull; internal static ConfigEntry<float> PauseEmpty; internal static ConfigEntry<float> PauseFull; internal static ConfigEntry<int> ConfigVersion; private const int CurrentVersion = 2; private static bool loggedOriginal; internal static void Bind(ConfigFile cfg) { Enabled = cfg.Bind<bool>("Adrenaline", "Enabled", true, "Change how the adrenaline gauge drains once you stop fighting. Vanilla: 1 to 4 points per second after a 10 to 6 s pause."); DrainEmpty = cfg.Bind<float>("Adrenaline", "DrainPerSecondWhenEmpty", 0.3f, "Points lost per second when the gauge is nearly empty. Vanilla 1."); DrainFull = cfg.Bind<float>("Adrenaline", "DrainPerSecondWhenFull", 1f, "Points lost per second when the gauge is full. Vanilla 4."); PauseEmpty = cfg.Bind<float>("Adrenaline", "PauseSecondsWhenEmpty", 30f, "Seconds without a gain before the gauge starts draining, when it is nearly empty. Vanilla 10."); PauseFull = cfg.Bind<float>("Adrenaline", "PauseSecondsWhenFull", 18f, "Seconds without a gain before the gauge starts draining, when it is full. Vanilla 6."); ConfigVersion = cfg.Bind<int>("Adrenaline", "ConfigVersion", 1, "Do not edit. Records which set of Reef values this section holds. A section older than the current set is rewritten once at startup, so a change of realm numbers reaches a config file that was already written."); Migrate(); } private static void Migrate() { try { int value = ConfigVersion.Value; if (value < 2) { string text = Show(); DrainEmpty.Value = (float)((ConfigEntryBase)DrainEmpty).DefaultValue; DrainFull.Value = (float)((ConfigEntryBase)DrainFull).DefaultValue; PauseEmpty.Value = (float)((ConfigEntryBase)PauseEmpty).DefaultValue; PauseFull.Value = (float)((ConfigEntryBase)PauseFull).DefaultValue; ConfigVersion.Value = 2; ReefClientPlugin.Log.LogInfo((object)("adrenaline: config v" + value + " -> v" + 2 + " (was " + text + ", now " + Show() + ")")); } } catch (Exception ex) { ReefClientPlugin.Log.LogWarning((object)("adrenaline migrate: " + ex.GetType().Name + ": " + ex.Message)); } } private static string Show() { return "drain " + DrainEmpty.Value + "/" + DrainFull.Value + " per s, pause " + PauseEmpty.Value + "/" + PauseFull.Value + " s"; } private static string Describe(AnimationCurve curve) { if (curve == null) { return "null"; } Keyframe[] keys = curve.keys; string[] array = new string[keys.Length]; for (int i = 0; i < keys.Length; i++) { array[i] = ((Keyframe)(ref keys[i])).time.ToString("0.##") + "→" + ((Keyframe)(ref keys[i])).value.ToString("0.##"); } return "[" + string.Join(", ", array) + "]"; } } internal static class ReefClock { internal static ConfigEntry<bool> Enabled; internal static ConfigEntry<bool> ShowCountdown; internal static ConfigEntry<float> X; internal static ConfigEntry<float> Y; internal static ConfigEntry<float> Size; private const float NightStart = 0.85f; private const float MorningStart = 0.15f; private static GameObject root; private static TMP_Text label; private static float builtSize = -1f; private static float nextRefresh; private static bool loggedDayLength; internal static void Bind(ConfigFile cfg) { Enabled = cfg.Bind<bool>("Clock", "Enabled", true, "Show a small day/night clock under the minimap."); ShowCountdown = cfg.Bind<bool>("Clock", "ShowCountdown", true, "Append how long until night falls (or until dawn, at night)."); X = cfg.Bind<float>("Clock", "HudX", 0f, "Horizontal offset (px) from the bottom-centre of the small minimap."); Y = cfg.Bind<float>("Clock", "HudY", -38f, "Vertical offset (px) from the bottom edge of the small minimap (negative = below). Default sits just under the favor counter."); Size = cfg.Bind<float>("Clock", "HudSize", 20f, "Text height in px."); } internal static void Tick() { if (!Enabled.Value) { if ((Object)(object)root != (Object)null && root.activeSelf) { root.SetActive(false); } return; } Minimap instance = Minimap.instance; if ((Object)(object)instance == (Object)null || (Object)(object)instance.m_smallRoot == (Object)null) { root = null; label = null; return; } if ((Object)(object)root == (Object)null || builtSize != Size.Value) { try { Build(instance); } catch (Exception ex) { ReefClientPlugin.Log.LogWarning((object)("clock: " + ex.GetType().Name + ": " + ex.Message)); return; } } if (!root.activeSelf) { root.SetActive(true); } float realtimeSinceStartup = Time.realtimeSinceStartup; if (realtimeSinceStartup < nextRefresh) { return; } nextRefresh = realtimeSinceStartup + 0.5f; try { label.text = Compose(); } catch (Exception ex2) { label.text = ""; ReefClientPlugin.Log.LogWarning((object)("clock text: " + ex2.GetType().Name + ": " + ex2.Message)); } } internal static string Compose() { EnvMan instance = EnvMan.instance; ZNet instance2 = ZNet.instance; if ((Object)(object)instance == (Object)null || (Object)(object)instance2 == (Object)null) { return ""; } double num = instance.m_dayLengthSec; if (num <= 0.0) { return ""; } if (!loggedDayLength) { loggedDayLength = true; ReefClientPlugin.Log.LogInfo((object)("clock: day length " + num + " s (prefab value)")); } double timeSeconds = instance2.GetTimeSeconds(); int num2 = (int)(timeSeconds / num); double num3 = timeSeconds % num / num; int num4 = (int)(Mathf.Repeat(instance.GetDayFraction(), 1f) * 24f * 60f); string text = (num4 / 60).ToString("00") + ":" + (num4 % 60).ToString("00"); string text2 = "Day " + num2 + " · " + text; if (!ShowCountdown.Value) { return text2; } double seconds; string text3; if (num3 >= 0.8500000238418579 || num3 < 0.15000000596046448) { seconds = (((num3 >= 0.8500000238418579) ? 1.1500000059604645 : 0.15000000596046448) - num3) * num; text3 = "dawn"; } else { seconds = (0.8500000238418579 - num3) * num; text3 = "night"; } return text2 + " · " + text3 + " in " + Span(seconds); } private static string Span(double seconds) { if (seconds < 60.0) { return Math.Max(1, (int)Math.Ceiling(seconds)) + " s"; } return (int)Math.Ceiling(seconds / 60.0) + " min"; } private static void Build(Minimap mm) { //IL_0058: Unknown result type (might be due to invalid IL or missing references) //IL_0062: Expected O, but got Unknown //IL_008a: 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_00b4: Unknown result type (might be due to invalid IL or missing references) //IL_00d3: Unknown result type (might be due to invalid IL or missing references) //IL_00ec: Unknown result type (might be due to invalid IL or missing references) //IL_0128: 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_013c: Unknown result type (might be due to invalid IL or missing references) //IL_0147: Unknown result type (might be due to invalid IL or missing references) //IL_0152: Unknown result type (might be due to invalid IL or missing references) //IL_015c: Unknown result type (might be due to invalid IL or missing references) //IL_01cb: Unknown result type (might be due to invalid IL or missing references) //IL_01a6: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)root != (Object)null) { Object.Destroy((Object)(object)root); } float num = Mathf.Clamp(Size.Value, 8f, 64f); builtSize = Size.Value; root = new GameObject("ReefClock", new Type[1] { typeof(RectTransform) }); RectTransform component = root.GetComponent<RectTransform>(); ((Transform)component).SetParent(mm.m_smallRoot.transform, false); component.anchorMin = new Vector2(0.5f, 0f); component.anchorMax = new Vector2(0.5f, 0f); component.pivot = new Vector2(0.5f, 1f); component.anchoredPosition = new Vector2(X.Value, Y.Value); component.sizeDelta = new Vector2(num * 14f, num * 1.2f); GameObject val = new GameObject("Text", new Type[3] { typeof(RectTransform), typeof(CanvasRenderer), typeof(TextMeshProUGUI) }); RectTransform component2 = val.GetComponent<RectTransform>(); ((Transform)component2).SetParent((Transform)(object)component, false); component2.anchorMin = Vector2.zero; component2.anchorMax = Vector2.one; component2.offsetMin = Vector2.zero; component2.offsetMax = Vector2.zero; label = (TMP_Text)(object)val.GetComponent<TextMeshProUGUI>(); TMP_Text biomeNameSmall = mm.m_biomeNameSmall; if ((Object)(object)biomeNameSmall != (Object)null) { label.font = biomeNameSmall.font; label.fontSharedMaterial = biomeNameSmall.fontSharedMaterial; ((Graphic)label).color = ((Graphic)biomeNameSmall).color; } else { ((Graphic)label).color = new Color(0.95f, 0.85f, 0.5f, 1f); } label.fontSize = num * 0.85f; label.alignment = (TextAlignmentOptions)4098; label.textWrappingMode = (TextWrappingModes)0; ((Graphic)label).raycastTarget = false; label.text = ""; nextRefresh = 0f; ReefClientPlugin.Log.LogInfo((object)("clock built under the minimap (size " + num + ", offset " + X.Value + "," + Y.Value + ")")); } } internal static class ReefCompost { [HarmonyPatch(typeof(Player), "Message", new Type[] { typeof(MessageType), typeof(string), typeof(int), typeof(Sprite), typeof(bool) })] internal static class MessagePatch { private static void Prefix(ref string msg) { try { if (Enabled.Value && !(msg != "Item Deleted")) { msg = "Item Composted"; if (!loggedOnce) { loggedOnce = true; ReefClientPlugin.Log.LogInfo((object)"compost: \"Item Deleted\" shown as \"Item Composted\""); } } } catch (Exception ex) { ReefClientPlugin.Log.LogWarning((object)("compost: " + ex.GetType().Name + ": " + ex.Message)); } } } internal static ConfigEntry<bool> Enabled; private const string Deleted = "Item Deleted"; private const string Composted = "Item Composted"; private static bool loggedOnce; internal static void Bind(ConfigFile config) { Enabled = config.Bind<bool>("Compost", "Enabled", true, "true = RecyclePlus's \"Item Deleted\" message reads \"Item Composted\" (the realm's trash button is the compost). false = the mod's own text."); } } internal static class ReefFarming { private const string PlantEasilyGuid = "advize.PlantEasily"; internal static ConfigEntry<bool> StandDownPlantEasily; private static bool done; internal static void Bind(ConfigFile config) { StandDownPlantEasily = config.Bind<bool>("Farming", "StandDownPlantEasily", true, "true = if Advize-PlantEasily is still installed, switch it off at startup ([General] ModActive). The realm plants vanilla and harvests through Reef Client instead. Press F8 in game to wake it for your session if you really want its grid back."); } internal static void TickOnce() { //IL_0076: Unknown result type (might be due to invalid IL or missing references) //IL_0082: Expected O, but got Unknown if (done) { return; } done = true; try { if (StandDownPlantEasily != null && StandDownPlantEasily.Value) { if (!Chainloader.PluginInfos.TryGetValue("advize.PlantEasily", out var value) || (Object)(object)((value != null) ? value.Instance : null) == (Object)null) { ReefClientPlugin.Log.LogInfo((object)"farming: PlantEasily not installed — vanilla planting, Reef harvest"); return; } ConfigEntry<bool> val = default(ConfigEntry<bool>); if (!value.Instance.Config.TryGetEntry<bool>(new ConfigDefinition("General", "ModActive"), ref val)) { ReefClientPlugin.Log.LogWarning((object)"farming: PlantEasily has no [General] ModActive any more — not switching it off (the mod was reorganised; this file needs a look)"); return; } if (!val.Value) { ReefClientPlugin.Log.LogInfo((object)"farming: PlantEasily already off"); return; } val.Value = false; ReefClientPlugin.Log.LogInfo((object)"farming: PlantEasily switched off (ModActive=false) — vanilla planting, Reef harvest. F8 wakes it for this session if you want its grid back."); } } catch (Exception ex) { ReefClientPlugin.Log.LogWarning((object)("farming: " + ex.GetType().Name + ": " + ex.Message)); } } } internal static class ReefFenrir { [HarmonyPatch(typeof(ObjectDB), "Awake")] internal static class ObjectDBAwakePatch { [HarmonyPriority(0)] private static void Postfix(ObjectDB __instance) { try { Apply(__instance, "ObjectDB.Awake"); } catch (Exception ex) { ReefClientPlugin.Log.LogWarning((object)("fenrir: " + ex.GetType().Name + ": " + ex.Message)); } } } [HarmonyPatch(typeof(ObjectDB), "CopyOtherDB")] internal static class ObjectDBCopyPatch { [HarmonyPriority(0)] private static void Postfix(ObjectDB __instance) { try { Apply(__instance, "ObjectDB.CopyOtherDB"); } catch (Exception ex) { ReefClientPlugin.Log.LogWarning((object)("fenrir: " + ex.GetType().Name + ": " + ex.Message)); } } } [HarmonyPatch(typeof(Player), "OnSpawned", new Type[] { typeof(bool) })] internal static class OnSpawnedPatch { private static void Postfix(Player __instance) { try { if ((Object)(object)__instance == (Object)(object)Player.m_localPlayer) { Apply(ObjectDB.instance, "spawn"); } } catch (Exception ex) { ReefClientPlugin.Log.LogWarning((object)("fenrir: " + ex.GetType().Name + ": " + ex.Message)); } } } internal static ConfigEntry<bool> Enabled; private const float BodyPiece = 0.05f; private const float Helmet = 0.02f; private static readonly string[] Pieces = new string[9] { "HelmetFenrir_TW", "ArmorFenrirChest_TW", "ArmorFenrirLegs_TW", "HelmetFenrirBM_TW", "ArmorFenrirBMChest_TW", "ArmorFenrirBMLegs_TW", "HelmetFenrirCarapace_TW", "ArmorFenrirCarapaceChest_TW", "ArmorFenrirCarapaceLegs_TW" }; internal static void Bind(ConfigFile config) { Enabled = config.Bind<bool>("Fenrir", "Enabled", true, "true = Therzie Fenrir chest and legs give +5 % movement and the helmet +2 % (+12 % for a full set), so the upgrade keeps the Fenris speed it was made from. false = Therzie's own values (+2 % or 0 %)."); } private static void Apply(ObjectDB db, string where) { if (!Enabled.Value || (Object)(object)db == (Object)null) { return; } int num = 0; int num2 = 0; string[] pieces = Pieces; foreach (string text in pieces) { GameObject itemPrefab = db.GetItemPrefab(text); ItemDrop val = (((Object)(object)itemPrefab != (Object)null) ? itemPrefab.GetComponent<ItemDrop>() : null); if ((Object)(object)val == (Object)null) { num2++; continue; } SharedData shared = val.m_itemData.m_shared; float num3 = (text.StartsWith("Helmet") ? 0.02f : 0.05f); if (shared.m_movementModifier != num3) { shared.m_movementModifier = num3; num++; } } if (num > 0 || num2 > 0) { ReefClientPlugin.Log.LogInfo((object)("fenrir (" + where + "): " + num + " piece(s) set (chest/legs +" + 5f + " %, helmet +" + 2f + " % movement), " + num2 + " not found")); } } } internal static class ReefFood { [HarmonyPatch(typeof(Player), "GetTotalFoodValue")] internal static class GetTotalFoodValuePatch { private static bool Prefix(Player __instance, ref float hp, ref float stamina, ref float eitr) { hp = 0f; stamina = 0f; eitr = 0f; if (Enabled == null || !Enabled.Value) { return true; } try { hp = __instance.m_baseHP; stamina = __instance.m_baseStamina; float num = hp; List<Food> foods = __instance.GetFoods(); if (foods != null) { foreach (Food item in foods) { if (item != null && item.m_item != null && item.m_item.m_shared != null) { SharedData shared = item.m_item.m_shared; float elapsed = Elapsed(item, shared); hp += Curve(elapsed, HealthExponent) * shared.m_food; stamina += Curve(elapsed, StaminaExponent) * shared.m_foodStamina; eitr += Curve(elapsed, EitrExponent) * shared.m_foodEitr; num += item.m_health; } } Proof(foods, hp, num); } return false; } catch (Exception ex) { ReefClientPlugin.Log.LogWarning((object)("food curve: " + ex.GetType().Name + ": " + ex.Message + " — vanilla value used")); hp = 0f; stamina = 0f; eitr = 0f; return true; } } } internal static ConfigEntry<bool> Enabled; internal static ConfigEntry<float> HealthExponent; internal static ConfigEntry<float> StaminaExponent; internal static ConfigEntry<float> EitrExponent; private const float MinExponent = 0.1f; private const float MaxExponent = 100f; private const string ExponentHelp = "Exponent 'e' in the food curve 'value = (1 - x^e) * base', x = fraction of the buff already elapsed. Higher = full longer then a sharper drop; 1 = linear; below 1 = fast at first, then slower. Range 0.1 - 100."; private static int lastFoodCount = -1; private static float nextProofAt; internal static void Bind(ConfigFile config) { Enabled = config.Bind<bool>("Food", "Enabled", true, "true = The Reef's food curve (absorbed from Sated). false = vanilla decay. Takes effect immediately."); HealthExponent = config.Bind<float>("Food", "HealthCurveExponent", 8f, "Added health. Exponent 'e' in the food curve 'value = (1 - x^e) * base', x = fraction of the buff already elapsed. Higher = full longer then a sharper drop; 1 = linear; below 1 = fast at first, then slower. Range 0.1 - 100."); StaminaExponent = config.Bind<float>("Food", "StaminaCurveExponent", 8f, "Added stamina. Exponent 'e' in the food curve 'value = (1 - x^e) * base', x = fraction of the buff already elapsed. Higher = full longer then a sharper drop; 1 = linear; below 1 = fast at first, then slower. Range 0.1 - 100."); EitrExponent = config.Bind<float>("Food", "EitrCurveExponent", 8f, "Added eitr. Exponent 'e' in the food curve 'value = (1 - x^e) * base', x = fraction of the buff already elapsed. Higher = full longer then a sharper drop; 1 = linear; below 1 = fast at first, then slower. Range 0.1 - 100."); } private static void Proof(List<Food> foods, float hp, float vanillaHp) { float realtimeSinceStartup = Time.realtimeSinceStartup; if (foods.Count == lastFoodCount && realtimeSinceStartup < nextProofAt) { return; } lastFoodCount = foods.Count; nextProofAt = realtimeSinceStartup + 300f; if (foods.Count == 0) { return; } List<string> list = new List<string>(); foreach (Food food in foods) { if (food != null && food.m_item != null && food.m_item.m_shared != null) { SharedData shared = food.m_item.m_shared; list.Add(food.m_name + " " + Mathf.RoundToInt(Elapsed(food, shared) * 100f) + "% eaten"); } } ReefClientPlugin.Log.LogInfo((object)("food curve: max hp " + Mathf.RoundToInt(hp) + " (vanilla would be " + Mathf.RoundToInt(vanillaHp) + ") — " + string.Join(", ", list.ToArray()))); } private static float Elapsed(Food food, SharedData shared) { float foodBurnTime = shared.m_foodBurnTime; if (foodBurnTime <= 0f) { return 0f; } return Mathf.Clamp01(1f - food.m_time / foodBurnTime); } private static float Curve(float elapsed, ConfigEntry<float> exponent) { float num = Mathf.Clamp(exponent.Value, 0.1f, 100f); return 1f - Mathf.Pow(elapsed, num); } } internal static class ReefForgeGate { [HarmonyPatch(typeof(Player), "Interact", new Type[] { typeof(GameObject), typeof(bool), typeof(bool) })] internal static class InteractPatch { [HarmonyPriority(800)] private static bool Prefix(Player __instance, GameObject go, bool hold, bool alt) { try { if ((Object)(object)__instance == (Object)null || __instance != Player.m_localPlayer) { return true; } if (!IsSealed(go)) { return true; } if (Time.time - lastLineAt >= (LineEverySeconds?.Value ?? 3f)) { lastLineAt = Time.time; string text = Line?.Value ?? ""; if (text.Length > 0) { ((Character)__instance).Message((MessageType)2, text, 0, (Sprite)null, false); } ManualLogSource log = ReefClientPlugin.Log; ZNetView componentInParent = go.GetComponentInParent<ZNetView>(); log.LogInfo((object)("forge gate: refused " + Utils.GetPrefabName(((componentInParent != null) ? ((Component)componentInParent).gameObject : null) ?? ((Component)go.transform.root).gameObject))); } return false; } catch (Exception ex) { ReefClientPlugin.Log.LogWarning((object)("forge gate: " + ex.Message + " (letting the interaction through)")); return true; } } } internal static ConfigEntry<bool> Sealed; internal static ConfigEntry<string> Prefabs; internal static ConfigEntry<string> Line; internal static ConfigEntry<float> LineEverySeconds; private static HashSet<string> sealedPrefabs; private static float lastLineAt = -999f; internal static void Bind(ConfigFile config) { Sealed = config.Bind<bool>("ForgeOfPotential", "Sealed", true, "true = the Forge of Potential (and every prefab listed below) refuses interaction: no menu, no upgrade, no idol spent. The realm sealed it on 2026-09-16 while a 1.0 bug (every recipe unlocked after use) is investigated. false = vanilla behaviour."); Prefabs = config.Bind<string>("ForgeOfPotential", "Prefabs", "UpgradeStation", "Comma-separated prefab names refused while Sealed is true (the station's piece prefab is UpgradeStation)."); Line = config.Bind<string>("ForgeOfPotential", "Line", "Odin has sealed this forge. Not yet.", "What the player reads when refused (empty = silent)."); LineEverySeconds = config.Bind<float>("ForgeOfPotential", "LineEverySeconds", 3f, "The line is shown at most once per this many seconds (a held key would spam it)."); sealedPrefabs = null; Prefabs.SettingChanged += delegate { sealedPrefabs = null; }; } private static HashSet<string> Wanted() { if (sealedPrefabs != null) { return sealedPrefabs; } HashSet<string> hashSet = new HashSet<string>(StringComparer.Ordinal); string[] array = (Prefabs?.Value ?? "").Split(new char[1] { ',' }); for (int i = 0; i < array.Length; i++) { string text = array[i].Trim(); if (text.Length > 0) { hashSet.Add(text); } } sealedPrefabs = hashSet; return hashSet; } internal static bool IsSealed(GameObject go) { if ((Object)(object)go == (Object)null || Sealed == null || !Sealed.Value) { return false; } ZNetView componentInParent = go.GetComponentInParent<ZNetView>(); string prefabName = Utils.GetPrefabName(((Object)(object)componentInParent != (Object)null) ? ((Component)componentInParent).gameObject : ((Component)go.transform.root).gameObject); return Wanted().Contains(prefabName); } } internal static class ReefHarvest { [HarmonyPatch(typeof(Player), "Interact")] internal static class InteractPatch { private static void Prefix(Player __instance, GameObject go, bool hold, bool alt) { //IL_0059: Unknown result type (might be due to invalid IL or missing references) try { if (BulkHarvest == null || !BulkHarvest.Value || (Object)(object)go == (Object)null || (Object)(object)__instance == (Object)null || __instance != Player.m_localPlayer || hold || ((Character)__instance).InAttack() || ((Character)__instance).InDodge() || !ZInput.GetKey(ModifierKey.Value, false)) { return; } Transform root = go.transform.root; if (!((Object)(object)root == (Object)null)) { Pickable componentInParent = go.GetComponentInParent<Pickable>(); Beehive val = (((Object)(object)componentInParent != (Object)null) ? null : go.GetComponentInParent<Beehive>()); if (!((Object)(object)componentInParent == (Object)null) || !((Object)(object)val == (Object)null)) { Sweep(__instance, root, hold, alt); } } } catch (Exception ex) { ReefClientPlugin.Log.LogWarning((object)("harvest: " + ex.GetType().Name + ": " + ex.Message)); } } } internal static ConfigEntry<bool> BulkHarvest; internal static ConfigEntry<KeyCode> ModifierKey; internal static ConfigEntry<float> Radius; internal static ConfigEntry<bool> SameKindOnly; internal static ConfigEntry<int> MaxPerHarvest; internal static void Bind(ConfigFile config) { //IL_0063: Unknown result type (might be due to invalid IL or missing references) //IL_006d: Expected O, but got Unknown //IL_00af: Unknown result type (might be due to invalid IL or missing references) //IL_00b9: Expected O, but got Unknown BulkHarvest = config.Bind<bool>("Harvest", "BulkHarvest", true, "true = holding the modifier key while you pick also picks everything else within the radius. Berries, mushrooms, crops, flint, stone, branches and beehives."); ModifierKey = config.Bind<KeyCode>("Harvest", "ModifierKey", (KeyCode)304, "Hold this while you pick to harvest in bulk. Same key PlantEasily used, so nothing to relearn."); Radius = config.Bind<float>("Harvest", "Radius", 3f, new ConfigDescription("How far around the thing you picked to sweep, in metres.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(1f, 20f), Array.Empty<object>())); SameKindOnly = config.Bind<bool>("Harvest", "SameKindOnly", false, "false = take everything harvestable in range. true = take only the same kind as the one you clicked (a carrot patch grown through raspberries comes out as carrots alone)."); MaxPerHarvest = config.Bind<int>("Harvest", "MaxPerHarvest", 50, new ConfigDescription("Ceiling on one gesture, so a big radius in a berry field cannot hang the game.", (AcceptableValueBase)(object)new AcceptableValueRange<int>(1, 500), Array.Empty<object>())); } private static void Sweep(Player player, Transform root, bool hold, bool alt) { //IL_0007: Unknown result type (might be due to invalid IL or missing references) HashSet<Interactable> hashSet = new HashSet<Interactable>(); Collider[] array = Physics.OverlapSphere(root.position, Radius.Value); foreach (Collider val in array) { Pickable componentInParent = ((Component)val).GetComponentInParent<Pickable>(); Beehive val2 = (((Object)(object)componentInParent != (Object)null) ? null : ((Component)val).GetComponentInParent<Beehive>()); if ((Object)(object)componentInParent == (Object)null && (Object)(object)val2 == (Object)null) { continue; } Transform root2 = (((Object)(object)componentInParent != (Object)null) ? ((Component)componentInParent).gameObject : ((Component)val2).gameObject).transform.root; if (!((Object)(object)root2 == (Object)(object)root) && (!SameKindOnly.Value || !(((Object)root2).name != ((Object)root).name)) && (!((Object)(object)val2 != (Object)null) || val2.GetHoneyLevel() >= 1)) { Interactable item; if (!((Object)(object)componentInParent != (Object)null)) { Interactable val3 = (Interactable)(object)val2; item = val3; } else { Interactable val3 = (Interactable)(object)componentInParent; item = val3; } hashSet.Add(item); } } int num = 0; foreach (Interactable item2 in hashSet) { if (num >= MaxPerHarvest.Value) { break; } item2.Interact((Humanoid)(object)player, hold, alt); num++; } if (num > 0) { ReefClientPlugin.Log.LogInfo((object)("harvest: " + Utils.GetPrefabName(((Component)root).gameObject) + " + " + num + " more within " + Radius.Value.ToString("0.#") + " m" + (SameKindOnly.Value ? " (same kind only)" : ""))); } } } internal static class ReefPortals { [HarmonyPatch(typeof(TeleportWorld), "UpdatePortal")] internal static class UpdatePortalPatch { [HarmonyPriority(800)] private static void Prefix(TeleportWorld __instance) { try { if (AlignStandingPortals == null || !AlignStandingPortals.Value) { return; } if (!looked) { Look(); } if (apType == null) { return; } Component component = ((Component)__instance).GetComponent(apType); if ((Object)(object)component == (Object)null) { return; } ZNetScene instance = ZNetScene.instance; if ((Object)(object)instance == (Object)null) { return; } string prefabName = Utils.GetPrefabName(((Component)__instance).gameObject); GameObject prefab = instance.GetPrefab(prefabName); if ((Object)(object)prefab == (Object)null) { return; } Component component2 = prefab.GetComponent(apType); if ((Object)(object)component2 == (Object)null || component2 == component) { return; } List<string> list = fAllowedItems.GetValue(component2) as List<string>; bool flag = (bool)fAllowEverything.GetValue(component2); List<string> list2 = fAllowedItems.GetValue(component) as List<string>; bool flag2 = (bool)fAllowEverything.GetValue(component); if (flag != flag2 || !Same(list, list2)) { fAllowedItems.SetValue(component, (list == null) ? new List<string>() : new List<string>(list)); fAllowEverything.SetValue(component, flag); if (said.Add(prefabName)) { ReefClientPlugin.Log.LogInfo((object)("portals: " + prefabName + " realigned -> " + (flag ? "everything" : string.Join(", ", (list ?? new List<string>()).ToArray())) + " (was " + (flag2 ? "everything" : string.Join(", ", (list2 ?? new List<string>()).ToArray())) + ")")); } } } catch (Exception ex) { ReefClientPlugin.Log.LogWarning((object)("portals: " + ex.GetType().Name + ": " + ex.Message)); apType = null; } } } internal static ConfigEntry<bool> AlignStandingPortals; private static bool looked; private static Type apType; private static FieldInfo fAllowedItems; private static FieldInfo fAllowEverything; private static readonly HashSet<string> said = new HashSet<string>(); internal static void Bind(ConfigFile config) { AlignStandingPortals = config.Bind<bool>("Portals", "AlignStandingPortals", true, "true = a portal already standing in the world is put back on the allowlist the server currently sends (AdvancedPortals only applies a config change to the prefab, never to portals already placed). false = AdvancedPortals' own behaviour, which is how iron kept crossing while the server said no."); } private static void Look() { looked = true; apType = AccessTools.TypeByName("AdvancedPortals.AdvancedPortal"); if (apType == null) { ReefClientPlugin.Log.LogInfo((object)"portals: AdvancedPortals not installed, nothing to align"); return; } fAllowedItems = AccessTools.Field(apType, "AllowedItems"); fAllowEverything = AccessTools.Field(apType, "AllowEverything"); if (fAllowedItems == null || fAllowEverything == null) { apType = null; ReefClientPlugin.Log.LogWarning((object)"portals: AdvancedPortal changed shape (AllowedItems/AllowEverything not found) — not aligning"); } else { ReefClientPlugin.Log.LogInfo((object)"portals: aligning standing portals on the server's allowlist"); } } private static bool Same(List<string> a, List<string> b) { if (a == null || b == null) { return a == b; } if (a.Count != b.Count) { return false; } for (int i = 0; i < a.Count; i++) { if (!string.Equals(a[i], b[i], StringComparison.OrdinalIgnoreCase)) { return false; } } return true; } } internal static class ReefRevive { [HarmonyPatch(typeof(Player), "OnDeath")] internal static class OnDeathPatch { private static void Prefix(Player __instance) { //IL_0135: Unknown result type (might be due to invalid IL or missing references) try { if ((Object)(object)__instance == (Object)null || __instance != Player.m_localPlayer || Enabled == null || !Enabled.Value || !OnAServer()) { return; } ZNetView component = ((Component)__instance).GetComponent<ZNetView>(); if ((Object)(object)component != (Object)null && component.IsValid() && !component.IsOwner()) { ReefClientPlugin.Log.LogError((object)"revive: DYING ON A VIEW WE DO NOT OWN — vanilla will skip CreateTombStone, the inventory transfer and the skill cost (no grave will appear)"); } snapshot = null; Skills skills = ((Character)__instance).GetSkills(); if ((Object)(object)skills == (Object)null) { return; } if (!loggedDeathFactor) { loggedDeathFactor = true; ReefClientPlugin.Log.LogInfo((object)("revive: vanilla death costs " + (skills.m_DeathLowerFactor * Game.m_skillReductionRate).ToString("0.###") + " of every skill (prefab factor " + skills.m_DeathLowerFactor.ToString("0.###") + " x SkillReductionRate " + Game.m_skillReductionRate.ToString("0.###") + ")")); } Dictionary<SkillType, float> dictionary = new Dictionary<SkillType, float>(); foreach (Skill skill in skills.GetSkillList()) { if (skill != null && skill.m_info != null) { dictionary[skill.m_info.m_skill] = skill.m_level; } } snapshot = dictionary; } catch (Exception ex) { snapshot = null; ReefClientPlugin.Log.LogWarning((object)("revive snapshot: " + ex.GetType().Name + ": " + ex.Message)); } } private static void Postfix(Player __instance) { //IL_0119: 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) try { if ((Object)(object)__instance == (Object)null || __instance != Player.m_localPlayer || Enabled == null || !Enabled.Value) { return; } if (!OnAServer()) { ReefClientPlugin.Log.LogInfo((object)"revive: local host / solo — vanilla death screen"); return; } Game instance = Game.instance; if ((Object)(object)instance == (Object)null) { return; } float num = Window(); deadline = Time.realtimeSinceStartup + num; keyWasDown = true; pendingRestore = false; pendingReviver = ""; waiting = true; closing = false; ((MonoBehaviour)instance).CancelInvoke("_RequestRespawn"); try { PlayerProfile playerProfile = instance.GetPlayerProfile(); if (playerProfile != null) { playerProfile.SavePlayerData(__instance); } ReefClientPlugin.Log.LogInfo((object)"revive: corpse written to the profile (no live snapshot can outlive the wait)"); } catch (Exception ex) { ReefClientPlugin.Log.LogWarning((object)("revive: could not stamp the corpse into the profile — " + ex.GetType().Name + ": " + ex.Message)); } ManualLogSource log = ReefClientPlugin.Log; string[] obj = new string[5] { "revive: waiting room open for ", num.ToString(), " s (grave at ", null, null }; Vector3 position = ((Component)__instance).transform.position; obj[3] = ((Vector3)(ref position)).ToString("0.0"); obj[4] = ")"; log.LogInfo((object)string.Concat(obj)); } catch (Exception ex2) { waiting = false; ReefClientPlugin.Log.LogWarning((object)("revive: " + ex2.GetType().Name + ": " + ex2.Message + " — falling back to the vanilla respawn")); try { Game instance2 = Game.instance; if (instance2 != null) { instance2.RequestRespawn(10f, true); } } catch { } } } } [HarmonyPatch(typeof(Player), "Awake")] internal static class AwakeRegisterPatch { private static void Postfix(Player __instance) { try { ZNetView val = (((Object)(object)__instance != (Object)null) ? ((Component)__instance).GetComponent<ZNetView>() : null); if (!((Object)(object)val == (Object)null) && val.IsValid()) { val.Register<Vector3, string>("Reef_Revive", (Action<long, Vector3, string>)delegate(long sender, Vector3 pos, string who) { //IL_0007: Unknown result type (might be due to invalid IL or missing references) OnRevive(__instance, sender, pos, who); }); val.Register<string>("Reef_ReviveStart", (Action<long, string>)delegate(long sender, string who) { OnReviveStart(__instance, sender, who); }); if (!loggedRegistered) { loggedRegistered = true; ReefClientPlugin.Log.LogInfo((object)"revive: Reef_Revive registered on a Player view"); } } } catch (Exception ex) { ReefClientPlugin.Log.LogWarning((object)("revive register: " + ex.GetType().Name + ": " + ex.Message)); } } } [HarmonyPatch(typeof(TombStone), "GetHoverText")] internal static class HoverTextPatch { private static void Postfix(TombStone __instance, ref string __result) { //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) try { if (!Active() || (Object)(object)__instance == (Object)null) { return; } string text = ((HoverLine != null) ? HoverLine.Value : ""); if (!string.IsNullOrEmpty(text) && RevivableGrave(Player.m_localPlayer, ((Component)__instance).gameObject, out var _, out var dead)) { string text2 = text.Replace("{key}", ((object)Key()/*cast due to .constrained prefix*/).ToString()).Replace("{name}", dead.GetPlayerName()); if (string.IsNullOrEmpty(__result)) { __result = text2; } else { __result = __result + " · " + text2; } } } catch (Exception ex) { ReefClientPlugin.Log.LogWarning((object)("revive hover: " + ex.GetType().Name + ": " + ex.Message)); } } } [HarmonyPatch(typeof(Player), "StartGuardianPower")] internal static class GuardianPowerPatch { private static bool Prefix(Player __instance, ref bool __result) { //IL_001c: Unknown result type (might be due to invalid IL or missing references) //IL_0021: Unknown result type (might be due to invalid IL or missing references) //IL_0022: Unknown result type (might be due to invalid IL or missing references) //IL_0025: Unknown result type (might be due to invalid IL or missing references) try { if (!Active() || (Object)(object)__instance == (Object)null || __instance != Player.m_localPlayer) { return true; } KeyCode val = Key(); ZNetView graveView; Player dead; bool flag = (int)val != 0 && ZInput.GetKey(val, false) && RevivableGrave(__instance, ((Humanoid)__instance).GetHoverObject(), out graveView, out dead); if (!channeling && !flag) { return true; } __result = false; return false; } catch (Exception ex) { ReefClientPlugin.Log.LogWarning((object)("revive power guard: " + ex.GetType().Name + ": " + ex.Message)); return true; } } } [HarmonyPatch(typeof(Player), "OnSpawned", new Type[] { typeof(bool) })] internal static class OnSpawnedPatch { private static void Postfix(Player __instance) { //IL_02b5: Unknown result type (might be due to invalid IL or missing references) //IL_02ba: Unknown result type (might be due to invalid IL or missing references) //IL_02bb: Unknown result type (might be due to invalid IL or missing references) //IL_02bc: Unknown result type (might be due to invalid IL or missing references) //IL_014d: Unknown result type (might be due to invalid IL or missing references) try { if (!pendingRestore || (Object)(object)__instance == (Object)null || __instance != Player.m_localPlayer) { return; } if (pendingSince < 0f || Time.realtimeSinceStartup - pendingSince > 60f) { pendingRestore = false; snapshot = null; ReefClientPlugin.Log.LogWarning((object)"revive: stale pending respawn — nothing restored"); return; } pendingRestore = false; try { ZNetView component = ((Component)__instance).GetComponent<ZNetView>(); if ((Object)(object)component != (Object)null && component.IsValid() && !component.IsOwner()) { component.ClaimOwnership(); ReefClientPlugin.Log.LogWarning((object)"revive: respawned player view was NOT ours — ownership claimed (without this, the next death creates no grave and costs nothing)"); } else { ReefClientPlugin.Log.LogInfo((object)"revive: respawned player view is ours"); } } catch (Exception ex) { ReefClientPlugin.Log.LogWarning((object)("revive ownership: " + ex.GetType().Name + ": " + ex.Message)); } if (RestoreSkills != null && RestoreSkills.Value && snapshot != null) { Skills skills = ((Character)__instance).GetSkills(); int num = 0; if ((Object)(object)skills != (Object)null) { foreach (Skill skill in skills.GetSkillList()) { if (skill != null && skill.m_info != null && snapshot.TryGetValue(skill.m_info.m_skill, out var value) && value > skill.m_level) { skill.m_level = value; num++; } } } ReefClientPlugin.Log.LogInfo((object)("revive: " + num + " skill(s) given back")); } snapshot = null; string text = ((BuffName != null) ? (BuffName.Value ?? "").Trim() : ""); if (text.Length > 0) { SEMan sEMan = ((Character)__instance).GetSEMan(); int stableHashCode = StringExtensionMethods.GetStableHashCode(text); if (sEMan == null || (Object)(object)ObjectDB.instance == (Object)null || (Object)(object)ObjectDB.instance.GetStatusEffect(stableHashCode) == (Object)null) { ReefClientPlugin.Log.LogWarning((object)("revive: no status effect named '" + text + "' — no buff given")); } else { sEMan.AddStatusEffect(stableHashCode, true, 0, 0f, (short)(-1)); ReefClientPlugin.Log.LogInfo((object)("revive: buff '" + text + "' granted")); } } string text2 = ((RevivedLine != null) ? RevivedLine.Value : ""); if (!string.IsNullOrEmpty(text2)) { ((Character)__instance).Message((MessageType)2, text2.Replace("{name}", string.IsNullOrEmpty(pendingReviver) ? "?" : pendingReviver), 0, (Sprite)null, false); } Vector3 position = ((Component)__instance).transform.position; float num2 = Vector3.Distance(position, pendingPos); if (num2 > 8f) { ReefClientPlugin.Log.LogWarning((object)("revive: logout point rejected — spawned at " + ((Vector3)(ref position)).ToString("0.0") + ", " + num2.ToString("0") + " m from the grave at " + ((Vector3)(ref pendingPos)).ToString("0.0") + " (vanilla bed spawn)")); } else { ReefClientPlugin.Log.LogInfo((object)("revive: back on the grave at " + ((Vector3)(ref position)).ToString("0.0") + " (" + num2.ToString("0.0") + " m from it)")); } pendingReviver = ""; } catch (Exception ex2) { pendingRestore = false; snapshot = null; ReefClientPlugin.Log.LogWarning((object)("revive spawn: " + ex2.GetType().Name + ": " + ex2.Message)); } } } internal static ConfigEntry<bool> Enabled; internal static ConfigEntry<float> WindowSeconds; internal static ConfigEntry<KeyCode> RespawnKey; internal static ConfigEntry<bool> RestoreSkills; internal static ConfigEntry<string> BuffName; internal static ConfigEntry<string> TitleLine; internal static ConfigEntry<string> WaitLine; internal static ConfigEntry<string> RespawnLine; internal static ConfigEntry<string> RevivedLine; internal static ConfigEntry<string> ReviverLine; internal static ConfigEntry<float> HudSize; internal static ConfigEntry<KeyCode> ReviveKey; internal static ConfigEntry<float> ChannelSeconds; internal static ConfigEntry<string> HoverLine; internal static ConfigEntry<string> ChannelLine; internal static ConfigEntry<string> BeingRaisedLine; internal static ConfigEntry<string> CancelLine; internal const string RpcName = "Reef_Revive"; internal const string StartRpcName = "Reef_ReviveStart"; private const string OldWaitLine = "A friend can raise you by touching your grave."; private static bool waiting; private static bool closing; private static float deadline; private static bool keyWasDown; private static bool loggedDeathFactor; private static bool loggedRegistered; private static bool pendingRestore; private static float pendingSince = -1f; private static string pendingReviver = ""; private static Vector3 pendingPos; private static Dictionary<SkillType, float> snapshot; private static readonly Dictionary<long, float> lastSendAt = new Dictionary<long, float>(); private static bool channeling; private static float channelStart; private static Vector3 channelFrom; private static ZDOID channelGrave; private static ZNetView channelGraveView; private static Player channelTarget; private static float channelLastAimed; private const float AimGrace = 0.5f; private static long channelOwner; private static string channelName = ""; private static bool channelSat; private static bool reviveKeyWasDown; private static GameObject bar; private static RectTransform barFill; private static TMP_Text barLabel; private static bool barFailed; private const float BarWidth = 180f; private static string raisedBy = ""; private static float raisedShownUntil = -1f; private static Func<Player, bool> takeInput; private static Action<Player> stopEmote; private static GameObject panel; private static TMP_Text label; private static float builtSize = -1f; private static bool buildFailed; internal static void Bind(ConfigFile cfg) { Enabled = cfg.Bind<bool>("Revive", "Enabled", true, "true = the death screen becomes a waiting room: a friend who holds the revive key on your grave sends you back on top of it. false = the grave, its hover text, the death screen and the F key (Forsaken power) are all 100 % vanilla. Disabled on a solo / local-host world, where vanilla owns the death screen."); WindowSeconds = cfg.Bind<float>("Revive", "WindowSeconds", 60f, "How long you can wait for a friend before the vanilla respawn happens anyway. Clamped to 10-180 s."); RespawnKey = cfg.Bind<KeyCode>("Revive", "RespawnKey", (KeyCode)32, "Pressed on the death screen = respawn now, exactly as vanilla would have after its own 10 s."); RestoreSkills = cfg.Bind<bool>("Revive", "RestoreSkills", true, "true = being raised by a friend also gives you back the skill levels the death took (measured at 5 % of every skill on this realm). Dying alone and respawning keeps the full vanilla cost either way — only a revive restores. false = a revive saves you the walk and nothing else."); BuffName = cfg.Bind<string>("Revive", "BuffName", "Rested", "Status effect granted when you are revived (a vanilla effect name; empty = no buff). Looked up in ObjectDB; a name that does not exist is logged as a warning and simply skipped."); TitleLine = cfg.Bind<string>("Revive", "TitleLine", "You have fallen.", "First line of the waiting room (empty = hidden)."); WaitLine = cfg.Bind<string>("Revive", "WaitLine", "A friend can raise you: they hold F on your grave.", "Second line of the waiting room (empty = hidden)."); if (WaitLine.Value == "A friend can raise you by touching your grave.") { WaitLine.Value = (string)((ConfigEntryBase)WaitLine).DefaultValue; ReefClientPlugin.Log.LogInfo((object)"revive: WaitLine still held the 0.6.1 default — updated to the 0.6.2 one"); } RespawnLine = cfg.Bind<string>("Revive", "RespawnLine", "[{key}] Respawn now", "Last line of the waiting room. {key} is replaced by RespawnKey (empty = hidden)."); RevivedLine = cfg.Bind<string>("Revive", "RevivedLine", "Raised by {name}. Your grave is beside you.", "Centre message when you come back on your grave. {name} = who raised you (empty = silent). The belongings stay IN the grave — being raised puts you next to it, it does not hand the bag back."); ReviverLine = cfg.Bind<string>("Revive", "ReviverLine", "You raise {name}.", "Centre message for the one who touches the grave. {name} = the dead player (empty = silent)."); HudSize = cfg.Bind<float>("Revive", "HudSize", 28f, "Text height in px on the death screen."); ReviveKey = cfg.Bind<KeyCode>("Revive", "ReviveKey", (KeyCode)102, "Hold it on a dead friend's grave to raise them. F is also vanilla's Forsaken power key: while you aim at a grave you can revive with this key down, the power does not fire (anywhere else it does)."); ChannelSeconds = cfg.Bind<float>("Revive", "ChannelSeconds", 5f, "How long the revive key must be held. Clamped to 1-15 s. Letting go, moving or looking away cancels."); HoverLine = cfg.Bind<string>("Revive", "HoverLine", "[<color=yellow><b>Hold {key}</b></color>] Revive {name}", "Added to the grave's hover text when its owner can be raised. {key} = ReviveKey, {name} = the dead player (empty = no hint, the revive still works)."); ChannelLine = cfg.Bind<string>("Revive", "ChannelLine", "Raising {name}…", "Above the bar while you hold the key. {name} = the dead player."); BeingRaisedLine = cfg.Bind<string>("Revive", "BeingRaisedLine", "{name} is raising you…", "On YOUR death screen when a friend starts to raise you (your countdown starts over at the same moment). {name} = the friend (empty = hidden)."); CancelLine = cfg.Bind<string>("Revive", "CancelLine", "Revive interrupted.", "Message when you let go, move or look away before the end (empty = silent)."); takeInput = TryDelegate<Func<Player, bool>>("TakeInput"); stopEmote = TryDelegate<Action<Player>>("StopEmote"); } private static T TryDelegate<T>(string method) where T : Delegate { try { MethodInfo methodInfo = AccessTools.Method(typeof(Player), method, Type.EmptyTypes, (Type[])null); if (methodInfo != null) { return AccessTools.MethodDelegate<T>(methodInfo, (object)null, true); } } catch (Exception ex) { ReefClientPlugin.Log.LogWarning((object)("revive: Player." + method + " not reachable — " + ex.GetType().Name + ": " + ex.Message)); return null; } ReefClientPlugin.Log.LogWarning((object)("revive: Player." + method + " not found")); return null; } private static float Channel() { return Mathf.Clamp((ChannelSeconds != null) ? ChannelSeconds.Value : 5f, 1f, 15f); } private static KeyCode Key() { //IL_000f: Unknown result type (might be due to invalid IL or missing references) if (ReviveKey == null) { return (KeyCode)102; } return ReviveKey.Value; } private static float Window() { return Mathf.Clamp((WindowSeconds != null) ? WindowSeconds.Value : 60f, 10f, 180f); } private static bool OnAServer() { ZNet instance = ZNet.instance; if ((Object)(object)instance != (Object)null && !instance.IsServer() && !instance.IsDedicated()) { return ZRoutedRpc.instance != null; } return false; } private static void OnReviveStart(Player me, long sender, string who) { try { ReefClientPlugin.Log.LogInfo((object)("revive: Reef_ReviveStart received from " + (string.IsNullOrEmpty(who) ? "?" : who) + " (peer " + sender + ")")); if (!waiting || closing) { ReefClientPlugin.Log.LogInfo((object)"revive: not waiting — channel start ignored"); } else if (!((Object)(object)me == (Object)null) && me == Player.m_localPlayer) { float realtimeSinceStartup = Time.realtimeSinceStartup; deadline = Mathf.Max(deadline, realtimeSinceStartup + Window()); raisedBy = who ?? ""; raisedShownUntil = realtimeSinceStartup + Channel() + 2f; ReefClientPlugin.Log.LogInfo((object)("revive: window restarted at " + Window() + " s — " + raisedBy + " is channelling")); } } catch (Exception ex) { ReefClientPlugin.Log.LogWarning((object)("revive start receive: " + ex.GetType().Name + ": " + ex.Message)); } } private static void OnRevive(Player me, long sender, Vector3 pos, string who) { //IL_0117: Unknown result type (might be due to invalid IL or missing references) //IL_0118: 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) try { ReefClientPlugin.Log.LogInfo((object)("revive: Reef_Revive received from " + (string.IsNullOrEmpty(who) ? "?" : who) + " (peer " + sender + ") at " + ((Vector3)(ref pos)).ToString("0.0"))); if (!waiting) { ReefClientPlugin.Log.LogInfo((object)"revive: not waiting — ignored"); return; } if ((Object)(object)me == (Object)null || me != Player.m_localPlayer) { ReefClientPlugin.Log.LogInfo((object)"revive: not the local player — ignored"); return; } Game instance = Game.instance; PlayerProfile val = (((Object)(object)instance != (Object)null) ? instance.GetPlayerProfile() : null); if ((Object)(object)instance == (Object)null || val == null) { ReefClientPlugin.Log.LogWarning((object)"revive: no profile — ignored"); return; } if (closing) { ReefClientPlugin.Log.LogInfo((object)"revive: a respawn is already under way — revive ignored"); return; } closing = true; CloseWaitingRoom(); pendingRestore = true; pendingSince = Time.realtimeSinceStartup; pendingReviver = who ?? ""; pendingPos = pos; val.SetLogoutPoint(pos); instance.RequestRespawn(0f, false); ReefClientPlugin.Log.LogInfo((object)("revive: respawning on the grave at " + ((Vector3)(ref pos)).ToString("0.0"))); } catch (Exception ex) { ReefClientPlugin.Log.LogWarning((object)("revive receive: " + ex.GetType().Name + ": " + ex.Message)); try { CloseWaitingRoom(); Game instance2 = Game.instance; if (instance2 != null) { instance2.RequestRespawn(0f, true); } } catch { } } } private static bool RevivableGrave(Player me, GameObject go, out ZNetView graveView, out Player dead) { graveView = null; dead = null; if ((Object)(object)me == (Object)null || (Object)(object)go == (Object)null) { return false; } ZNetView componentInParent = go.GetComponentInParent<ZNetView>(); if ((Object)(object)componentInParent == (Object)null || !componentInParent.IsValid()) { return false; } if ((Object)(object)((Component)componentInParent).gameObject.GetComponent<TombStone>() == (Object)null) { return false; } long num = componentInParent.GetZDO().GetLong(ZDOVars.s_owner, 0L); if (num == 0L || num == me.GetPlayerID()) { return false; } Player player = Player.GetPlayer(num); if ((Object)(object)player == (Object)null || !((Character)player).IsDead()) { return false; } ZNetView component = ((Component)player).GetComponent<ZNetView>(); if ((Object)(object)component == (Object)null || !component.IsValid()) { return false; } graveView = componentInParent; dead = player; return true; } private static bool Active() { if (Enabled != null && Enabled.Value) { return OnAServer(); } return false; } private static void ChannelTick() { //IL_0006: Unknown result type (might be due to invalid IL or missing references) //IL_000b: Unknown result type (might be due to invalid IL or missing references) //IL_000c: Unknown result type (might be due to invalid IL or missing references) //IL_000f: Unknown result type (might be due to invalid IL or missing references) //IL_01b4: Unknown result type (might be due to invalid IL or missing references) //IL_01b9: Unknown result type (might be due to invalid IL or missing references) //IL_0093: Unknown result type (might be due to invalid IL or missing references) //IL_0098: 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_00a9: Unknown result type (might be due to invalid IL or missing references) //IL_031f: Unknown result type (might be due to invalid IL or missing references) //IL_0324: Unknown result type (might be due to invalid IL or missing references) //IL_033e: Unknown result type (might be due to invalid IL or missing references) //IL_0275: Unknown result type (might be due to invalid IL or missing references) //IL_027a: Unknown result type (might be due to invalid IL or missing references) //IL_027f: Unknown result type (might be due to invalid IL or missing references) //IL_0284: Unknown result type (might be due to invalid IL or missing references) Player localPlayer = Player.m_localPlayer; KeyCode val = Key(); bool flag = (int)val != 0 && ZInput.GetKey(val, false); bool flag2 = flag && !reviveKeyWasDown; reviveKeyWasDown = flag; if (!channeling) { if (flag2 && Active() && !((Object)(object)localPlayer == (Object)null) && !((Character)localPlayer).IsDead() && (takeInput == null || takeInput(localPlayer)) && RevivableGrave(localPlayer, ((Humanoid)localPlayer).GetHoverObject(), out var graveView, out var dead)) { channeling = true; channelStart = Time.realtimeSinceStartup; channelFrom = ((Component)localPlayer).transform.position; channelGrave = graveView.GetZDO().m_uid; channelGraveView = graveView; channelTarget = dead; channelLastAimed = channelStart; channelOwner = graveView.GetZDO().GetLong(ZDOVars.s_owner, 0L); channelName = dead.GetPlayerName(); channelSat = localPlayer.StartEmote("sit", false); ((Component)dead).GetComponent<ZNetView>().InvokeRPC("Reef_ReviveStart", new object[1] { localPlayer.GetPlayerName() }); ReefClientPlugin.Log.LogInfo((object)("revive: channel started on the grave of " + channelName + " (" + Channel() + " s, sat: " + channelSat + "), Reef_ReviveStart sent")); } return; } string text = null; ZNetView val2 = channelGraveView; Player val3 = channelTarget; float realtimeSinceStartup = Time.realtimeSinceStartup; if (RevivableGrave(localPlayer, ((Object)(object)localPlayer != (Object)null) ? ((Humanoid)localPlayer).GetHoverObject() : null, out var graveView2, out var _) && graveView2.GetZDO().m_uid == channelGrave) { channelLastAimed = realtimeSinceStartup; } if (!Active() || (Object)(object)localPlayer == (Object)null) { text = "disabled"; } else if (((Character)localPlayer).IsDead()) { text = "reviver died"; } else if (!flag) { text = "key released"; } else if (takeInput != null && !takeInput(localPlayer)) { text = "a window opened"; } else if ((Object)(object)val2 == (Object)null || !val2.IsValid() || (Object)(object)val3 == (Object)null || !((Character)val3).IsDead()) { text = "the dead player is gone or already up"; } else if (realtimeSinceStartup - channelLastAimed > 0.5f) { text = "grave no longer aimed at"; } else { Vector3 val4 = ((Component)localPlayer).transform.position - channelFrom; val4.y = 0f; if (((Vector3)(ref val4)).magnitude > 0.5f) { text = "moved"; } } if (text != null) { EndChannel(localPlayer, "cancelled (" + text + ")", cancelled: true); return; } float num = (realtimeSinceStartup - channelStart) / Channel(); if (num < 1f) { DrawBar(num); return; } if (!lastSendAt.TryGetValue(channelOwner, out var value) || !(realtimeSinceStartup - value < 2f)) { lastSendAt[channelOwner] = realtimeSinceStartup; Vector3 position = ((Component)val2).transform.position; ZNetView component = ((Component)val3).GetComponent<ZNetView>(); component.InvokeRPC("Reef_Revive", new object[2] { position, localPlayer.GetPlayerName() }); ReefClientPlugin.Log.LogInfo((object)("revive: Reef_Revive sent to " + channelName + " (playerID " + channelOwner + ", zdo owner " + component.GetZDO().GetOwner() + ") at " + ((Vector3)(ref position)).ToString("0.0"))); string text2 = ((ReviverLine != null) ? ReviverLine.Value : ""); if (!string.IsNullOrEmpty(text2)) { ((Character)localPlayer).Message((MessageType)2, text2.Replace("{name}", channelName), 0, (Sprite)null, false); } } EndChannel(localPlayer, "complete", cancelled: false); } private static void EndChannel(Player me, string how, bool cancelled) { channeling = false; HideBar(); try { if (channelSat && (Object)(object)me != (Object)null && !((Character)me).IsDead() && stopEmote != null) { stopEmote(me); } } catch (Exception ex) { ReefClientPlugin.Log.LogWarning((object)("revive stand up: " + ex.GetType().Name + ": " + ex.Message)); } channelSat = false; channelGraveView = null; channelTarget = null; ReefClientPlugin.Log.LogInfo((object)("revive: channel on the grave of " + channelName + " " + how)); if (cancelled && (Object)(object)me != (Object)null && !((Character)me).IsDead()) { string text = ((CancelLine != null) ? CancelLine.Value : ""); if (!string.IsNullOrEmpty(text)) { ((Character)me).Message((MessageType)2, text, 0, (Sprite)null, false); } } } private static void DrawBar(float progress) { //IL_005a: Unknown result type (might be due to invalid IL or missing references) if (barFailed) { return; } try { if ((Object)(object)bar == (Object)null) { BuildBar(); } if (!((Object)(object)bar == (Object)null)) { if (!bar.activeSelf) { bar.SetActive(true); } barFill.sizeDelta = new Vector2(180f * Mathf.Clamp01(progress), 0f); string text = ((ChannelLine != null) ? ChannelLine.Value : ""); barLabel.text = (string.IsNullOrEmpty(text) ? "" : text.Replace("{name}", channelName)); } } catch (Exception ex) { barFailed = true; ReefClientPlugin.Log.LogWarning((object)("revive bar: " + ex.GetType().Name + ": " + ex.Message + " — the channel runs without a bar")); } } private static void HideBar() { if ((Object)(object)bar != (Object)null) { bar.SetActive(false); } } private static void BuildBar() { //IL_0036: Unknown result type (might be due to invalid IL or missing references) //IL_0040: Expected O, but got Unknown //IL_0070: Unknown result type (might be due to invalid IL or missing references) //IL_0076: 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_009c: 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_00ed: Unknown result type (might be due to invalid IL or missing references) //IL_00f2: Unknown result type (might be due to invalid IL or missing references) //IL_0101: Unknown result type (might be due to invalid IL or missing references) //IL_010c: Unknown result type (might be due to invalid IL or missing references) //IL_0117: Unknown result type (might be due to invalid IL or missing references) //IL_0121: Unknown result type (might be due to invalid IL or missing references) //IL_0145: 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_018c: Unknown result type (might be due to invalid IL or missing references) //IL_01b2: Unknown result type (might be due to invalid IL or missing references) //IL_01cb: Unknown result type (might be due to invalid IL or missing references) //IL_01e4: Unknown result type (might be due to invalid IL or missing references) //IL_01f3: Unknown result type (might be due to invalid IL or missing references) //IL_0202: Unknown result type (might be due to invalid IL or missing references) //IL_0226: Unknown result type (might be due to invalid IL or missing references) //IL_0268: Unknown result type (might be due to invalid IL or missing references) //IL_026d: Unknown result type (might be due to invalid IL or missing references) //IL_028e: Unknown result type (might be due to invalid IL or missing references) //IL_0294: Unknown result type (might be due to invalid IL or missing references) //IL_02a5: Unknown result type (might be due to invalid IL or missing references) //IL_02ba: Unknown result type (might be due to invalid IL or missing references) //IL_02ce: Unknown result type (might be due to invalid IL or missing references) //IL_032b: Unknown result type (might be due to invalid IL or missing references) Hud instance = Hud.instance; if (!((Object)(object)instance == (Object)null) && !((Object)(object)instance.m_crosshair == (Object)null)) { bar = new GameObject("ReefReviveBar", new Type[1] { typeof(RectTransform) }); RectTransform component = bar.GetComponent<RectTransform>(); ((Transform)component).SetParent(((Component)instance.m_crosshair).transform, false); Vector2 val = default(Vector2); ((Vector2)(ref val))..ctor(0.5f, 0.5f); component.anchorMax = val; component.anchorMin = val; component.pivot = new Vector2(0.5f, 0.5f); component.anchoredPosition = new Vector2(0f, -48f); component.sizeDelta = new Vector2(180f, 8f); GameObject val2 = new GameObject("Back", new Type[3] { typeof(RectTransform), typeof(CanvasRenderer), typeof(Image) }); RectTransform component2 = val2.GetComponent<RectTransform>(); ((Transform)component2).SetParent((Transform)(object)component, false); component2.anchorMin = Vector2.zero; component2.anchorMax = Vector2.one; component2.offsetMin = Vector2.zero; component2.offsetMax = Vector2.zero; Image component3 = val2.GetComponent<Image>(); ((Graphic)component3).color = new Color(0f, 0f, 0f, 0.6f); ((Graphic)component3).raycastTarget = false; GameObject val3 = new GameObject("Fill", new Type[3] { typeof(RectTransform), typeof(CanvasRenderer), typeof(Image) }); barFill = val3.GetComponent<RectTransform>(); ((Transform)barFill).SetParent((Transform)(object)component, false); barFill.anchorMin = new Vector2(0f, 0f); barFill.anchorMax = new Vector2(0f, 1f); barFill.pivot = new Vector2(0f, 0.5f); barFill.anchoredPosition = Vector2.zero; barFill.sizeDelta = Vector2.zero; Image component4 = val3.GetComponent<Image>(); ((Graphic)component4).color = new Color(0.92f, 0.8f, 0.45f, 0.95f); ((Graphic)component4).raycastTarget = false; GameObject val4 = new GameObject("Text", new Type[3] { typeof(RectTransform), typeof(CanvasRenderer), typeof(TextMeshProUGUI) }); RectTransform component5 = val4.GetComponent<RectTransform>(); ((Transform)component5).SetParent((Transform)(object)component, false); ((Vector2)(ref val))..ctor(0.5f, 1f); component5.anchorMax = val; component5.anchorMin = val; component5.pivot = new Vector2(0.5f, 0f); component5.anchoredPosition = new Vector2(0f, 4f); component5.sizeDelta = new Vector2(360f, 24f); barLabel = (TMP_Text)(object)val4.GetComponent<TextMeshProUGUI>(); TextMeshProUGUI hoverName = instance.m_hoverName; if ((Object)(object)hoverName != (Object)null) { barLabel.font = ((TMP_Text)hoverName).font; barLabel.fontSharedMaterial = ((TMP_Text)hoverName).fontSharedMaterial; } ((Graphic)barLabel).color = new Color(0.92f, 0.88f, 0.78f, 1f); barLabel.fontSize = 18f; barLabel.alignment = (TextAlignmentOptions)1026; ((Graphic)barLabel).raycastTarget = false; barLabel.text = ""; ReefClientPlugin.Log.LogInfo((object)"revive: channel bar built under the reticle"); } } internal unsafe static void Tick() { //IL_00c4: Unknown result type (might be due to invalid IL or missing references) //IL_00c9: Unknown result type (might be due to invalid IL or missing references) //IL_00cb: Unknown result type (might be due to invalid IL or missing references) //IL_00cf: Unknown result type (might be due to invalid IL or missing references) try { ChannelTick(); } catch (Exception ex) { ReefClientPlugin.Log.LogWarning((object)("revive channel: " + ex.GetType().Name + ": " + ex.Message)); if (channeling) { try { EndChannel(Player.m_localPlayer, "dropped (error)", cancelled: false); } catch { channeling = false; } } } if (!waiting) { return; } try { Player localPlayer = Player.m_localPlayer; if ((Object)(object)localPlayer == (Object)null || !((Character)localPlayer).IsDead()) { CloseWaitingRoom(); return; } float realtimeSinceStartup = Time.realtimeSinceStartup; float num = deadline - realtimeSinceStartup; if (num < -30f) { ReefClientPlugin.Log.LogError((object)"revive: waiting room overran by 30 s — forcing the vanilla respawn"); RespawnNow("failsafe"); return; } KeyCode val = (KeyCode)((RespawnKey == null) ? 32 : ((int)RespawnKey.Value)); bool flag = (int)val !=