Decompiled source of Marriage v0.2.6

Pix.Marriage.dll

Decompiled 3 hours ago
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Globalization;
using System.IO;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
using BepInEx;
using BepInEx.Configuration;
using HarmonyLib;
using UnityEngine;

[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: AssemblyVersion("0.0.0.0")]
namespace Pix.Marriage;

[BepInPlugin("Pix.Marriage", "Marriage", "0.2.6")]
public sealed class MarriagePlugin : BaseUnityPlugin
{
	private struct Proposal
	{
		public long FromUid;

		public string FromName;

		public string FromHouseholdId;

		public float LastNotifyTime;
	}

	private sealed class Household
	{
		public string id;

		public Dictionary<long, Member> members = new Dictionary<long, Member>();
	}

	private sealed class Member
	{
		public long uid;

		public string name;

		public double bondSeconds;
	}

	[HarmonyPatch(typeof(ZRoutedRpc))]
	private static class Patch_ZRoutedRpc_Ctor
	{
		[HarmonyPostfix]
		[HarmonyPatch(/*Could not decode attribute arguments.*/)]
		private static void Postfix(ZRoutedRpc __instance, bool server)
		{
			TryRegisterRpcsLate();
		}
	}

	[HarmonyPatch(typeof(Terminal), "Awake")]
	private static class Patch_Terminal_Awake
	{
		private static void Postfix()
		{
			TryRegisterConsoleCommands();
		}
	}

	[HarmonyPatch(typeof(Character), "OnDeath")]
	private static class Patch_Character_OnDeath
	{
		private static void Postfix(Character __instance)
		{
			if ((Object)(object)__instance == (Object)null || (Object)(object)ZNet.instance == (Object)null || !ZNet.instance.IsServer() || !_enableMourning.Value)
			{
				return;
			}
			Player val = (Player)(object)((__instance is Player) ? __instance : null);
			if ((Object)(object)val == (Object)null)
			{
				return;
			}
			try
			{
				long playerID = val.GetPlayerID();
				if (playerID == 0L || !UidToHousehold.TryGetValue(playerID, out var value) || string.IsNullOrEmpty(value) || !Households.TryGetValue(value, out var value2))
				{
					return;
				}
				int tier = 1;
				if (value2.members.TryGetValue(playerID, out var value3))
				{
					tier = Math.Max(1, GetTierFromBondSeconds(value3.bondSeconds));
				}
				float durationSeconds = Mathf.Max(1f, _mourningDurationSeconds.Value);
				foreach (KeyValuePair<long, Member> member in value2.members)
				{
					long key = member.Key;
					if (key != playerID && ZNet.instance.GetPeer(key) != null)
					{
						Server_ToClientMourning(key, tier, durationSeconds);
					}
				}
			}
			catch
			{
			}
		}
	}

	[HarmonyPatch(typeof(ObjectDB), "Awake")]
	private static class Patch_ObjectDB_Awake
	{
		private static void Postfix(ObjectDB __instance)
		{
			TryRegisterStatusEffects(__instance);
		}
	}

	[HarmonyPatch(typeof(ObjectDB), "CopyOtherDB")]
	private static class Patch_ObjectDB_CopyOtherDB
	{
		private static void Postfix(ObjectDB __instance, ObjectDB other)
		{
			TryRegisterStatusEffects(__instance);
		}
	}

	[Serializable]
	[CompilerGenerated]
	private sealed class <>c
	{
		public static readonly <>c <>9 = new <>c();

		public static ConsoleEvent <>9__108_0;

		public static ConsoleEvent <>9__108_1;

		public static ConsoleEvent <>9__108_2;

		public static ConsoleEvent <>9__108_3;

		public static ConsoleEvent <>9__108_4;

		public static ConsoleEvent <>9__108_5;

		public static ConsoleEvent <>9__108_6;

		internal void <TryRegisterConsoleCommands>b__108_0(ConsoleEventArgs args)
		{
			try
			{
				if (args?.Args == null || args.Args.Length < 2)
				{
					MessageCenter("Usage: /marry <playerName>");
					return;
				}
				string s = string.Join(" ", args.Args, 1, args.Args.Length - 1);
				s = NormalizePlayerNameInput(s).Trim(new char[1] { '"' });
				if (string.IsNullOrEmpty(s))
				{
					MessageCenter("Usage: /marry <playerName>");
				}
				else
				{
					Client_RequestMarriage(s);
				}
			}
			catch
			{
			}
		}

		internal void <TryRegisterConsoleCommands>b__108_1(ConsoleEventArgs args)
		{
			try
			{
				Client_RespondMarriage(accept: true);
			}
			catch
			{
			}
		}

		internal void <TryRegisterConsoleCommands>b__108_2(ConsoleEventArgs args)
		{
			try
			{
				Client_RespondMarriage(accept: false);
			}
			catch
			{
			}
		}

		internal void <TryRegisterConsoleCommands>b__108_3(ConsoleEventArgs args)
		{
			try
			{
				Client_RequestDivorce();
			}
			catch
			{
			}
		}

		internal void <TryRegisterConsoleCommands>b__108_4(ConsoleEventArgs args)
		{
			int result = 1;
			if (args.Args != null && args.Args.Length >= 2)
			{
				int.TryParse(args.Args[1], out result);
			}
			result = Mathf.Clamp(result, 1, MaxTierClamped());
			if (ZRoutedRpc.instance != null)
			{
				ZRoutedRpc.instance.InvokeRoutedRPC(0L, "PX_Debug_MarrySelf", new object[1] { result });
			}
		}

		internal void <TryRegisterConsoleCommands>b__108_5(ConsoleEventArgs args)
		{
			if (ZRoutedRpc.instance != null)
			{
				ZRoutedRpc.instance.InvokeRoutedRPC(0L, "PX_Debug_ClearMarriage", Array.Empty<object>());
			}
		}

		internal void <TryRegisterConsoleCommands>b__108_6(ConsoleEventArgs args)
		{
			if (ZRoutedRpc.instance != null)
			{
				ZRoutedRpc.instance.InvokeRoutedRPC(0L, "PX_Debug_State", Array.Empty<object>());
			}
		}
	}

	public const string PluginGuid = "Pix.Marriage";

	public const string PluginName = "Marriage";

	public const string PluginVersion = "0.2.6";

	private static MarriagePlugin _self;

	private Harmony _harmony;

	private const string RpcRequestMarriage = "PX_Marry_Request";

	private const string RpcRespondMarriage = "PX_Marry_Response";

	private const string RpcNotifyProposal = "PX_Marry_NotifyProposal";

	private const string RpcNotifyToast = "PX_Marry_NotifyToast";

	private const string RpcDebugMarrySelf = "PX_Debug_MarrySelf";

	private const string RpcDebugClear = "PX_Debug_ClearMarriage";

	private const string RpcDebugState = "PX_Debug_State";

	private const string RpcClientSetFamilyTier = "PX_Family_SetTier";

	private const string RpcClientSetFamilyProgress = "PX_Family_SetProgress";

	private const string RpcClientPlayFamilyVfx = "PX_Family_PlayVfx";

	private const string RpcClientMourning = "PX_Mourning_Apply";

	private const string FamilySeBase = "SE_PX_Marriage_Family_";

	private const string MourningSeBase = "SE_PX_Marriage_Mourning_";

	private static string[] FamilySeNames;

	private static int[] _familySeHashes;

	private static string[] MourningSeNames;

	private static int[] _mourningSeHashes;

	private static Sprite _familyIcon;

	private static Sprite _mourningIcon;

	private static bool _iconsTried;

	private static bool _pngLoaderTried;

	private static MethodInfo _miTexture2D_LoadImage_Bytes;

	private static MethodInfo _miTexture2D_LoadImage_BytesBool;

	private static MethodInfo _miImageConversion_LoadImage;

	private static ConfigEntry<float> _rangeMeters;

	private static ConfigEntry<float> _saveIntervalSeconds;

	private static ConfigEntry<int> _maxTier;

	private static ConfigEntry<bool> _startAtTier1;

	private static ConfigEntry<int> _tier1Days;

	private static ConfigEntry<int> _tier2Days;

	private static ConfigEntry<int> _tier3Days;

	private static ConfigEntry<int> _tier4Days;

	private static ConfigEntry<int> _tier5Days;

	private static ConfigEntry<int> _tier6Days;

	private static ConfigEntry<int> _tier7Days;

	private static ConfigEntry<int> _tier8Days;

	private static ConfigEntry<int> _tier9Days;

	private static ConfigEntry<int> _tier10Days;

	private static ConfigEntry<float> _tierBaseBonus;

	private static ConfigEntry<bool> _tierDoubleEachTier;

	private static ConfigEntry<float> _tierMaxBonus;

	private static ConfigEntry<bool> _buffMove;

	private static ConfigEntry<bool> _buffDamage;

	private static ConfigEntry<bool> _buffStaminaRegen;

	private static ConfigEntry<bool> _buffHealthRegen;

	private static ConfigEntry<bool> _buffEitrRegen;

	private static ConfigEntry<bool> _enableEnterVfx;

	private static ConfigEntry<string> _enterVfxItemPrefab;

	private static ConfigEntry<float> _enterVfxCooldownSeconds;

	private static ConfigEntry<bool> _enableMourning;

	private static ConfigEntry<float> _mourningDurationSeconds;

	private static ConfigEntry<float> _mourningBasePenalty;

	private static ConfigEntry<float> _mourningMaxPenalty;

	private static ConfigEntry<bool> _mourningDoublesWithTier;

	private static ConfigEntry<float> _mourningCarryWeightPenalty;

	private static ConfigEntry<bool> _announce;

	private static ConfigEntry<bool> _familyProgressInTooltip;

	private static ConfigEntry<bool> _familyProgressToasts;

	private static ConfigEntry<bool> _familyTierUpToasts;

	private static ConfigEntry<float> _progressSendIntervalSeconds;

	private static ConfigEntry<bool> _debugEnabledServer;

	private static ConfigEntry<bool> _diagnosticsVerbose;

	private static bool _rpcsRegistered;

	private static bool _loggedRpcRegister;

	private static bool _consoleCommandsRegistered;

	private float _serverTickTimer;

	private const float ServerTickInterval = 1f;

	private const float ProposalRenotifyInterval = 5f;

	private const float InGameDaySeconds = 1800f;

	private static readonly Dictionary<long, Proposal> Pending = new Dictionary<long, Proposal>();

	private static readonly Dictionary<string, Household> Households = new Dictionary<string, Household>();

	private static readonly Dictionary<long, string> UidToHousehold = new Dictionary<long, string>();

	private static string _householdsPath;

	private static bool _householdsLoaded;

	private static bool _householdsDirty;

	private static float _lastSaveRealtime;

	private static double _lastWorldTimeSeconds;

	private static readonly Dictionary<long, int> _lastSentTier = new Dictionary<long, int>();

	private static readonly Dictionary<long, float> _lastSentProgressRealtime = new Dictionary<long, float>();

	private static readonly HashSet<long> _debugSoloFamily = new HashSet<long>();

	private static float _clientNextVfxTime;

	private static float _clientMourningEndTime;

	private static int _clientMourningTier;

	private static int _clientFamilyTier;

	private static float _clientFamilyBondDays;

	private static int _clientFamilyMilestoneBucket;

	private void Awake()
	{
		//IL_0524: Unknown result type (might be due to invalid IL or missing references)
		//IL_052e: Expected O, but got Unknown
		_self = this;
		_rangeMeters = ((BaseUnityPlugin)this).Config.Bind<float>("Family", "RangeMeters", 25f, "Distance required to activate Family buff.");
		_saveIntervalSeconds = ((BaseUnityPlugin)this).Config.Bind<float>("Persistence", "SaveIntervalSeconds", 60f, "Minimum seconds between household file writes when dirty.");
		_maxTier = ((BaseUnityPlugin)this).Config.Bind<int>("Tiers", "MaxTier", 10, "Maximum tier supported (5 to 10).");
		_startAtTier1 = ((BaseUnityPlugin)this).Config.Bind<bool>("Tiers", "StartAtTier1", true, "If true, new household members start at Tier 1 so Family can activate immediately.");
		_tier1Days = ((BaseUnityPlugin)this).Config.Bind<int>("Tiers", "Tier1Days", 6, "In-game days alive in household to reach tier 1.");
		_tier2Days = ((BaseUnityPlugin)this).Config.Bind<int>("Tiers", "Tier2Days", 12, "In-game days alive in household to reach tier 2.");
		_tier3Days = ((BaseUnityPlugin)this).Config.Bind<int>("Tiers", "Tier3Days", 24, "In-game days alive in household to reach tier 3.");
		_tier4Days = ((BaseUnityPlugin)this).Config.Bind<int>("Tiers", "Tier4Days", 48, "In-game days alive in household to reach tier 4.");
		_tier5Days = ((BaseUnityPlugin)this).Config.Bind<int>("Tiers", "Tier5Days", 60, "In-game days alive in household to reach tier 5.");
		_tier6Days = ((BaseUnityPlugin)this).Config.Bind<int>("Tiers", "Tier6Days", 90, "In-game days alive in household to reach tier 6.");
		_tier7Days = ((BaseUnityPlugin)this).Config.Bind<int>("Tiers", "Tier7Days", 120, "In-game days alive in household to reach tier 7.");
		_tier8Days = ((BaseUnityPlugin)this).Config.Bind<int>("Tiers", "Tier8Days", 180, "In-game days alive in household to reach tier 8.");
		_tier9Days = ((BaseUnityPlugin)this).Config.Bind<int>("Tiers", "Tier9Days", 240, "In-game days alive in household to reach tier 9.");
		_tier10Days = ((BaseUnityPlugin)this).Config.Bind<int>("Tiers", "Tier10Days", 300, "In-game days alive in household to reach tier 10.");
		_tierBaseBonus = ((BaseUnityPlugin)this).Config.Bind<float>("Tiers", "TierBaseBonus", 0.02f, "Tier 1 additive bonus (0.02=2%).");
		_tierDoubleEachTier = ((BaseUnityPlugin)this).Config.Bind<bool>("Tiers", "TierDoubleEachTier", true, "If true, the base bonus doubles each tier.");
		_tierMaxBonus = ((BaseUnityPlugin)this).Config.Bind<float>("Tiers", "TierMaxBonus", 0.32f, "Max tier bonus cap (0.32=32%).");
		_buffMove = ((BaseUnityPlugin)this).Config.Bind<bool>("FamilyBuff", "BuffMoveSpeed", true, "Family buff affects move speed.");
		_buffDamage = ((BaseUnityPlugin)this).Config.Bind<bool>("FamilyBuff", "BuffDamage", true, "Family buff affects damage.");
		_buffStaminaRegen = ((BaseUnityPlugin)this).Config.Bind<bool>("FamilyBuff", "BuffStaminaRegen", true, "Family buff affects stamina regen.");
		_buffHealthRegen = ((BaseUnityPlugin)this).Config.Bind<bool>("FamilyBuff", "BuffHealthRegen", true, "Family buff affects health regen.");
		_buffEitrRegen = ((BaseUnityPlugin)this).Config.Bind<bool>("FamilyBuff", "BuffEitrRegen", true, "Family buff affects eitr regen.");
		_enableEnterVfx = ((BaseUnityPlugin)this).Config.Bind<bool>("VFX", "EnableEnterRangeVfx", true, "Play a VFX when entering Family range.");
		_enterVfxItemPrefab = ((BaseUnityPlugin)this).Config.Bind<string>("VFX", "EnterRangeVfxItemPrefab", "MeadHasty", "Item prefab to copy consume VFX from.");
		_enterVfxCooldownSeconds = ((BaseUnityPlugin)this).Config.Bind<float>("VFX", "EnterRangeVfxCooldownSeconds", 30f, "Client cooldown to prevent VFX spam.");
		_enableMourning = ((BaseUnityPlugin)this).Config.Bind<bool>("Mourning", "EnableMourning", true, "Apply Mourning when a household member dies.");
		_mourningDurationSeconds = ((BaseUnityPlugin)this).Config.Bind<float>("Mourning", "DurationSeconds", 900f, "Mourning duration in seconds.");
		_mourningBasePenalty = ((BaseUnityPlugin)this).Config.Bind<float>("Mourning", "BasePenalty", 0.05f, "Tier 1 additive penalty (0.05=5%).");
		_mourningMaxPenalty = ((BaseUnityPlugin)this).Config.Bind<float>("Mourning", "MaxPenalty", 0.32f, "Max penalty cap (0.32=32%).");
		_mourningDoublesWithTier = ((BaseUnityPlugin)this).Config.Bind<bool>("Mourning", "DoublesWithTier", true, "If true, mourning penalty doubles each tier.");
		_mourningCarryWeightPenalty = ((BaseUnityPlugin)this).Config.Bind<float>("Mourning", "CarryWeightPenalty", -100f, "Carry weight penalty.");
		_announce = ((BaseUnityPlugin)this).Config.Bind<bool>("Social", "AnnounceHouseholds", true, "Announce household events.");
		_familyProgressInTooltip = ((BaseUnityPlugin)this).Config.Bind<bool>("UI", "FamilyProgressInTooltip", true, "Show tier progress-to-next in the Family StatusEffect tooltip.");
		_familyProgressToasts = ((BaseUnityPlugin)this).Config.Bind<bool>("UI", "FamilyProgressToasts", true, "Toast at 25/50/75/100% progress toward next tier (while Family is active).");
		_familyTierUpToasts = ((BaseUnityPlugin)this).Config.Bind<bool>("UI", "FamilyTierUpToasts", true, "Toast when Family tier increases.");
		_progressSendIntervalSeconds = ((BaseUnityPlugin)this).Config.Bind<float>("UI", "ProgressSendIntervalSeconds", 6f, "Server -> client progress update interval (seconds) while Family is active.");
		_debugEnabledServer = ((BaseUnityPlugin)this).Config.Bind<bool>("Debug", "EnableDebugCommands", false, "Enable server-authoritative debug commands.");
		_diagnosticsVerbose = ((BaseUnityPlugin)this).Config.Bind<bool>("Debug", "VerboseDiagnostics", true, "Extra logs for troubleshooting.");
		BuildSeArrays();
		_householdsPath = Path.Combine(Paths.ConfigPath, "pix.marriage.households.txt");
		_harmony = new Harmony("Pix.Marriage");
		_harmony.PatchAll();
		((BaseUnityPlugin)this).Logger.LogInfo((object)"[Marriage] Loaded v0.2.6.");
	}

	private void OnDestroy()
	{
		try
		{
			Harmony harmony = _harmony;
			if (harmony != null)
			{
				harmony.UnpatchSelf();
			}
		}
		catch
		{
		}
		if ((Object)(object)ZNet.instance != (Object)null && ZNet.instance.IsServer())
		{
			SaveHouseholdsIfDirty(force: true);
		}
	}

	private void Update()
	{
		TryRegisterRpcsLate();
		Client_UpdateMourning();
		if (!((Object)(object)ZNet.instance == (Object)null) && ZNet.instance.IsServer())
		{
			if (!_householdsLoaded)
			{
				LoadHouseholdsOnce();
			}
			ServerTick_AccrueBondSeconds();
			_serverTickTimer += Time.deltaTime;
			if (!(_serverTickTimer < 1f))
			{
				_serverTickTimer = 0f;
				ServerTick_RenotifyProposals();
				ServerTick_ProximityFamily();
				ServerTick_CleanupStaleProposals();
				ServerTick_CleanupDisconnectedCaches();
				SaveHouseholdsIfDirty(force: false);
			}
		}
	}

	private static int MaxTierClamped()
	{
		int num = ((_maxTier != null) ? _maxTier.Value : 5);
		return Mathf.Clamp(num, 5, 10);
	}

	private static void BuildSeArrays()
	{
		int num = MaxTierClamped();
		FamilySeNames = new string[num];
		_familySeHashes = new int[num];
		for (int i = 0; i < num; i++)
		{
			FamilySeNames[i] = "SE_PX_Marriage_Family_" + (i + 1).ToString(CultureInfo.InvariantCulture);
			_familySeHashes[i] = FamilySeNames[i].GetStableHashCode();
		}
		MourningSeNames = new string[num];
		_mourningSeHashes = new int[num];
		for (int j = 0; j < num; j++)
		{
			MourningSeNames[j] = "SE_PX_Marriage_Mourning_" + (j + 1).ToString(CultureInfo.InvariantCulture);
			_mourningSeHashes[j] = MourningSeNames[j].GetStableHashCode();
		}
	}

	private static float GetTierThresholdDays(int tier)
	{
		tier = Mathf.Clamp(tier, 1, MaxTierClamped());
		return tier switch
		{
			1 => _tier1Days.Value, 
			2 => _tier2Days.Value, 
			3 => _tier3Days.Value, 
			4 => _tier4Days.Value, 
			5 => _tier5Days.Value, 
			6 => _tier6Days.Value, 
			7 => _tier7Days.Value, 
			8 => _tier8Days.Value, 
			9 => _tier9Days.Value, 
			_ => _tier10Days.Value, 
		};
	}

	private static int GetTierFromBondSeconds(double bondSeconds)
	{
		double num = bondSeconds / 1800.0;
		int num2 = MaxTierClamped();
		int result = 0;
		for (int i = 1; i <= num2 && num >= (double)GetTierThresholdDays(i); i++)
		{
			result = i;
		}
		return result;
	}

	private static float GetTierBonus(int tier)
	{
		if (tier <= 0)
		{
			return 0f;
		}
		float num = Mathf.Clamp(_tierBaseBonus.Value, 0f, 5f);
		float num2 = num;
		if (_tierDoubleEachTier.Value)
		{
			int num3 = Mathf.Clamp(tier - 1, 0, 20);
			num2 = num * (float)(1 << num3);
		}
		float num4 = Mathf.Clamp(_tierMaxBonus.Value, 0f, 5f);
		return Mathf.Min(num2, num4);
	}

	private static float GetMourningPenalty(int tier)
	{
		tier = Mathf.Clamp(tier, 1, MaxTierClamped());
		float num = Mathf.Clamp(_mourningBasePenalty.Value, 0f, 5f);
		float num2 = num;
		if (_mourningDoublesWithTier.Value)
		{
			int num3 = Mathf.Clamp(tier - 1, 0, 20);
			num2 = num * (float)(1 << num3);
		}
		float num4 = Mathf.Clamp(_mourningMaxPenalty.Value, 0f, 5f);
		return Mathf.Min(num2, num4);
	}

	private static double TierToMinBondSeconds(int tier)
	{
		int tier2 = Mathf.Clamp(tier, 1, MaxTierClamped());
		return (double)GetTierThresholdDays(tier2) * 1800.0;
	}

	private static void TryRegisterRpcsLate()
	{
		if (!_rpcsRegistered && ZRoutedRpc.instance != null)
		{
			RegisterRpcs(ZRoutedRpc.instance);
			_rpcsRegistered = true;
			if (!_loggedRpcRegister && (Object)(object)_self != (Object)null)
			{
				_loggedRpcRegister = true;
				((BaseUnityPlugin)_self).Logger.LogInfo((object)"[Marriage] RPCs registered.");
			}
		}
	}

	private static void RegisterRpcs(ZRoutedRpc rpc)
	{
		rpc.Register<string>("PX_Marry_Request", (Action<long, string>)RPC_Server_RequestMarriage);
		rpc.Register<bool>("PX_Marry_Response", (Action<long, bool>)RPC_Server_RespondMarriage);
		rpc.Register<int>("PX_Debug_MarrySelf", (Action<long, int>)RPC_Server_DebugMarrySelf);
		rpc.Register("PX_Debug_ClearMarriage", (Action<long>)RPC_Server_DebugClearMarriage);
		rpc.Register("PX_Debug_State", (Action<long>)RPC_Server_DebugState);
		rpc.Register<string, long>("PX_Marry_NotifyProposal", (Action<long, string, long>)RPC_Client_NotifyProposal);
		rpc.Register<string>("PX_Marry_NotifyToast", (Action<long, string>)RPC_Client_NotifyToast);
		rpc.Register<int>("PX_Family_SetTier", (Action<long, int>)RPC_Client_SetFamilyTier);
		rpc.Register<int, float>("PX_Family_SetProgress", (Action<long, int, float>)RPC_Client_SetFamilyProgress);
		rpc.Register<string>("PX_Family_PlayVfx", (Action<long, string>)RPC_Client_PlayFamilyVfx);
		rpc.Register<int, float>("PX_Mourning_Apply", (Action<long, int, float>)RPC_Client_ApplyMourning);
	}

	private static void TryRegisterConsoleCommands()
	{
		//IL_0040: Unknown result type (might be due to invalid IL or missing references)
		//IL_002c: Unknown result type (might be due to invalid IL or missing references)
		//IL_0031: Unknown result type (might be due to invalid IL or missing references)
		//IL_0037: Expected O, but got Unknown
		//IL_0078: Unknown result type (might be due to invalid IL or missing references)
		//IL_0064: Unknown result type (might be due to invalid IL or missing references)
		//IL_0069: Unknown result type (might be due to invalid IL or missing references)
		//IL_006f: Expected O, but got Unknown
		//IL_00b0: 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_00a1: Unknown result type (might be due to invalid IL or missing references)
		//IL_00a7: Expected O, but got Unknown
		//IL_00e8: Unknown result type (might be due to invalid IL or missing references)
		//IL_00d4: Unknown result type (might be due to invalid IL or missing references)
		//IL_00d9: Unknown result type (might be due to invalid IL or missing references)
		//IL_00df: Expected O, but got Unknown
		//IL_0120: 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_0111: Unknown result type (might be due to invalid IL or missing references)
		//IL_0117: Expected O, but got Unknown
		//IL_0158: Unknown result type (might be due to invalid IL or missing references)
		//IL_0144: Unknown result type (might be due to invalid IL or missing references)
		//IL_0149: Unknown result type (might be due to invalid IL or missing references)
		//IL_014f: Expected O, but got Unknown
		//IL_0190: Unknown result type (might be due to invalid IL or missing references)
		//IL_017c: 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_0187: Expected O, but got Unknown
		if (_consoleCommandsRegistered)
		{
			return;
		}
		_consoleCommandsRegistered = true;
		object obj = <>c.<>9__108_0;
		if (obj == null)
		{
			ConsoleEvent val = delegate(ConsoleEventArgs args)
			{
				try
				{
					if (args?.Args == null || args.Args.Length < 2)
					{
						MessageCenter("Usage: /marry <playerName>");
					}
					else
					{
						string s = string.Join(" ", args.Args, 1, args.Args.Length - 1);
						s = NormalizePlayerNameInput(s).Trim(new char[1] { '"' });
						if (string.IsNullOrEmpty(s))
						{
							MessageCenter("Usage: /marry <playerName>");
						}
						else
						{
							Client_RequestMarriage(s);
						}
					}
				}
				catch
				{
				}
			};
			<>c.<>9__108_0 = val;
			obj = (object)val;
		}
		new ConsoleCommand("marry", "Invite a player to your household. Usage: /marry <playerName>", (ConsoleEvent)obj, false, false, false, false, false, (ConsoleOptionsFetcher)null, false, false, false);
		object obj2 = <>c.<>9__108_1;
		if (obj2 == null)
		{
			ConsoleEvent val2 = delegate
			{
				try
				{
					Client_RespondMarriage(accept: true);
				}
				catch
				{
				}
			};
			<>c.<>9__108_1 = val2;
			obj2 = (object)val2;
		}
		new ConsoleCommand("ido", "Accept the most recent household invite. Usage: /ido", (ConsoleEvent)obj2, false, false, false, false, false, (ConsoleOptionsFetcher)null, false, false, false);
		object obj3 = <>c.<>9__108_2;
		if (obj3 == null)
		{
			ConsoleEvent val3 = delegate
			{
				try
				{
					Client_RespondMarriage(accept: false);
				}
				catch
				{
				}
			};
			<>c.<>9__108_2 = val3;
			obj3 = (object)val3;
		}
		new ConsoleCommand("idont", "Decline the most recent household invite. Usage: /idont", (ConsoleEvent)obj3, false, false, false, false, false, (ConsoleOptionsFetcher)null, false, false, false);
		object obj4 = <>c.<>9__108_3;
		if (obj4 == null)
		{
			ConsoleEvent val4 = delegate
			{
				try
				{
					Client_RequestDivorce();
				}
				catch
				{
				}
			};
			<>c.<>9__108_3 = val4;
			obj4 = (object)val4;
		}
		new ConsoleCommand("divorce", "Leave your current household. Usage: /divorce", (ConsoleEvent)obj4, false, false, false, false, false, (ConsoleOptionsFetcher)null, false, false, false);
		object obj5 = <>c.<>9__108_4;
		if (obj5 == null)
		{
			ConsoleEvent val5 = delegate(ConsoleEventArgs args)
			{
				int result = 1;
				if (args.Args != null && args.Args.Length >= 2)
				{
					int.TryParse(args.Args[1], out result);
				}
				result = Mathf.Clamp(result, 1, MaxTierClamped());
				if (ZRoutedRpc.instance != null)
				{
					ZRoutedRpc.instance.InvokeRoutedRPC(0L, "PX_Debug_MarrySelf", new object[1] { result });
				}
			};
			<>c.<>9__108_4 = val5;
			obj5 = (object)val5;
		}
		new ConsoleCommand("px_marryself", "Debug: set yourself in a solo household, force bond to tier. Usage: px_marryself [tier(1-max)]", (ConsoleEvent)obj5, false, false, false, false, false, (ConsoleOptionsFetcher)null, false, false, false);
		object obj6 = <>c.<>9__108_5;
		if (obj6 == null)
		{
			ConsoleEvent val6 = delegate
			{
				if (ZRoutedRpc.instance != null)
				{
					ZRoutedRpc.instance.InvokeRoutedRPC(0L, "PX_Debug_ClearMarriage", Array.Empty<object>());
				}
			};
			<>c.<>9__108_5 = val6;
			obj6 = (object)val6;
		}
		new ConsoleCommand("px_marryclear", "Debug: leave household. Usage: px_marryclear", (ConsoleEvent)obj6, false, false, false, false, false, (ConsoleOptionsFetcher)null, false, false, false);
		object obj7 = <>c.<>9__108_6;
		if (obj7 == null)
		{
			ConsoleEvent val7 = delegate
			{
				if (ZRoutedRpc.instance != null)
				{
					ZRoutedRpc.instance.InvokeRoutedRPC(0L, "PX_Debug_State", Array.Empty<object>());
				}
			};
			<>c.<>9__108_6 = val7;
			obj7 = (object)val7;
		}
		new ConsoleCommand("px_marrydebug", "Debug: print state. Usage: px_marrydebug", (ConsoleEvent)obj7, false, false, false, false, false, (ConsoleOptionsFetcher)null, false, false, false);
	}

	private static void MessageCenter(string msg)
	{
		if (!((Object)(object)MessageHud.instance == (Object)null))
		{
			MessageHud.instance.ShowMessage((MessageType)2, msg, 0, (Sprite)null, false);
		}
	}

	private static void Client_RequestMarriage(string targetName)
	{
		if (ZRoutedRpc.instance == null)
		{
			MessageCenter("RPC not ready yet.");
			return;
		}
		ZRoutedRpc.instance.InvokeRoutedRPC(0L, "PX_Marry_Request", new object[1] { targetName });
		MessageCenter("Invite sent to " + targetName + "…");
	}

	private static void Client_RespondMarriage(bool accept)
	{
		if (ZRoutedRpc.instance == null)
		{
			MessageCenter("RPC not ready yet.");
			return;
		}
		ZRoutedRpc.instance.InvokeRoutedRPC(0L, "PX_Marry_Response", new object[1] { accept });
	}

	private static void Client_RequestDivorce()
	{
		if (ZRoutedRpc.instance == null)
		{
			MessageCenter("RPC not ready yet.");
			return;
		}
		ZRoutedRpc.instance.InvokeRoutedRPC(0L, "PX_Marry_Response", new object[1] { false });
	}

	private static void Client_DebugMarrySelf(int tier)
	{
		if (ZRoutedRpc.instance == null)
		{
			MessageCenter("RPC not ready yet.");
			return;
		}
		tier = Mathf.Clamp(tier, 1, MaxTierClamped());
		ZRoutedRpc.instance.InvokeRoutedRPC(0L, "PX_Debug_MarrySelf", new object[1] { tier });
	}

	private static void Client_DebugClearMarriage()
	{
		if (ZRoutedRpc.instance == null)
		{
			MessageCenter("RPC not ready yet.");
		}
		else
		{
			ZRoutedRpc.instance.InvokeRoutedRPC(0L, "PX_Debug_ClearMarriage", Array.Empty<object>());
		}
	}

	private static void Client_DebugState()
	{
		if (ZRoutedRpc.instance == null)
		{
			MessageCenter("RPC not ready yet.");
		}
		else
		{
			ZRoutedRpc.instance.InvokeRoutedRPC(0L, "PX_Debug_State", Array.Empty<object>());
		}
	}

	private static void RPC_Client_NotifyProposal(long sender, string fromName, long fromUid)
	{
		MessageCenter("\ud83c\udfe0 " + fromName + " invited you to their household! Type /ido or /idont");
	}

	private static void RPC_Client_NotifyToast(long sender, string msg)
	{
		MessageCenter(msg);
	}

	private static void RPC_Client_SetFamilyTier(long sender, int tier)
	{
		int num = MaxTierClamped();
		int num2 = Mathf.Clamp(tier, 0, num);
		if (num2 > 0 && _familyTierUpToasts.Value && num2 > _clientFamilyTier)
		{
			MessageCenter($"\ud83c\udfe0 Family reached Tier {num2}!");
		}
		if (num2 != _clientFamilyTier)
		{
			_clientFamilyTier = num2;
			_clientFamilyMilestoneBucket = 0;
		}
		Client_ApplyFamilyTierLocal(num2);
		if (num2 <= 0)
		{
			_clientFamilyBondDays = 0f;
			_clientFamilyMilestoneBucket = 0;
			Client_RefreshAllFamilyTooltips(0f);
		}
	}

	private static void RPC_Client_SetFamilyProgress(long sender, int tier, float bondDays)
	{
		int num = MaxTierClamped();
		int num2 = Mathf.Clamp(tier, 0, num);
		float bondDays2 = (_clientFamilyBondDays = Mathf.Max(0f, bondDays));
		if (_familyProgressInTooltip.Value)
		{
			Client_RefreshFamilyTooltipForTier(num2, bondDays2);
		}
		if (!_familyProgressToasts.Value || num2 <= 0 || num2 >= num)
		{
			return;
		}
		int progressMilestoneBucket = GetProgressMilestoneBucket(num2, bondDays2);
		if (progressMilestoneBucket > 0 && progressMilestoneBucket > _clientFamilyMilestoneBucket)
		{
			_clientFamilyMilestoneBucket = progressMilestoneBucket;
			if (progressMilestoneBucket >= 100)
			{
				MessageCenter($"\ud83c\udfe0 Family Tier {num2} complete — next tier ready!");
			}
			else
			{
				MessageCenter($"\ud83c\udfe0 Family Tier {num2}: {progressMilestoneBucket}% to Tier {num2 + 1}");
			}
		}
	}

	private static int GetProgressMilestoneBucket(int tier, float bondDays)
	{
		int num = MaxTierClamped();
		if (tier <= 0 || tier >= num)
		{
			return 0;
		}
		float tierThresholdDays = GetTierThresholdDays(tier);
		float tierThresholdDays2 = GetTierThresholdDays(tier + 1);
		float num2 = Mathf.Max(0.0001f, tierThresholdDays2 - tierThresholdDays);
		float num3 = Mathf.Clamp(bondDays - tierThresholdDays, 0f, num2);
		float num4 = num3 / num2 * 100f;
		if (num4 >= 100f)
		{
			return 100;
		}
		if (num4 >= 75f)
		{
			return 75;
		}
		if (num4 >= 50f)
		{
			return 50;
		}
		if (num4 >= 25f)
		{
			return 25;
		}
		return 0;
	}

	private static void RPC_Client_PlayFamilyVfx(long sender, string itemPrefabName)
	{
		//IL_00db: Unknown result type (might be due to invalid IL or missing references)
		//IL_00e0: Unknown result type (might be due to invalid IL or missing references)
		if (!_enableEnterVfx.Value)
		{
			return;
		}
		float time = Time.time;
		float num = Mathf.Max(0f, _enterVfxCooldownSeconds.Value);
		if (time < _clientNextVfxTime)
		{
			return;
		}
		_clientNextVfxTime = time + num;
		try
		{
			Player localPlayer = Player.m_localPlayer;
			if (!Object.op_Implicit((Object)(object)localPlayer) || (Object)(object)ObjectDB.instance == (Object)null || string.IsNullOrEmpty(itemPrefabName))
			{
				return;
			}
			GameObject itemPrefab = ObjectDB.instance.GetItemPrefab(itemPrefabName);
			if ((Object)(object)itemPrefab == (Object)null)
			{
				return;
			}
			ItemDrop component = itemPrefab.GetComponent<ItemDrop>();
			if ((Object)(object)component == (Object)null)
			{
				return;
			}
			SharedData val = ((component.m_itemData != null) ? component.m_itemData.m_shared : null);
			if (val != null && (Object)(object)val.m_consumeStatusEffect != (Object)null)
			{
				EffectList startEffects = val.m_consumeStatusEffect.m_startEffects;
				if (startEffects != null)
				{
					startEffects.Create(((Component)localPlayer).transform.position, Quaternion.identity, ((Component)localPlayer).transform, 1f, -1);
				}
			}
		}
		catch
		{
		}
	}

	private static void RPC_Client_ApplyMourning(long sender, int tier, float durationSeconds)
	{
		if (_enableMourning.Value)
		{
			int num = MaxTierClamped();
			int num2 = Mathf.Clamp(tier, 1, num);
			float num3 = Mathf.Max(1f, durationSeconds);
			if (num2 > _clientMourningTier)
			{
				_clientMourningTier = num2;
			}
			float time = Time.time;
			float num4 = Mathf.Max(0f, _clientMourningEndTime - time);
			_clientMourningEndTime = time + num4 + num3;
			Client_EnsureMourningTierSe();
		}
	}

	private static void Client_UpdateMourning()
	{
		if (!(_clientMourningEndTime <= 0f))
		{
			if (Time.time >= _clientMourningEndTime)
			{
				_clientMourningEndTime = 0f;
				_clientMourningTier = 0;
				Client_ClearMourning();
			}
			else
			{
				Client_EnsureMourningTierSe();
			}
		}
	}

	private static void Client_ClearMourning()
	{
		Player localPlayer = Player.m_localPlayer;
		if (!Object.op_Implicit((Object)(object)localPlayer))
		{
			return;
		}
		SEMan sEMan = ((Character)localPlayer).GetSEMan();
		if (sEMan == null)
		{
			return;
		}
		int num = MaxTierClamped();
		for (int i = 0; i < num; i++)
		{
			if (sEMan.HaveStatusEffect(_mourningSeHashes[i]))
			{
				sEMan.RemoveStatusEffect(_mourningSeHashes[i], true);
			}
		}
	}

	private static void Client_EnsureMourningTierSe()
	{
		Player localPlayer = Player.m_localPlayer;
		if (!Object.op_Implicit((Object)(object)localPlayer))
		{
			return;
		}
		SEMan sEMan = ((Character)localPlayer).GetSEMan();
		if (sEMan == null)
		{
			return;
		}
		int num = MaxTierClamped();
		for (int i = 0; i < num; i++)
		{
			if (sEMan.HaveStatusEffect(_mourningSeHashes[i]))
			{
				sEMan.RemoveStatusEffect(_mourningSeHashes[i], true);
			}
		}
		int num2 = Mathf.Clamp(_clientMourningTier, 1, num);
		int num3 = num2 - 1;
		if (!sEMan.HaveStatusEffect(_mourningSeHashes[num3]))
		{
			sEMan.AddStatusEffect(_mourningSeHashes[num3], true, 0, 0f);
		}
	}

	private static void Client_ApplyFamilyTierLocal(int tier)
	{
		Player localPlayer = Player.m_localPlayer;
		if (!Object.op_Implicit((Object)(object)localPlayer))
		{
			return;
		}
		SEMan sEMan = ((Character)localPlayer).GetSEMan();
		if (sEMan == null)
		{
			return;
		}
		int num = MaxTierClamped();
		for (int i = 0; i < num; i++)
		{
			if (sEMan.HaveStatusEffect(_familySeHashes[i]))
			{
				sEMan.RemoveStatusEffect(_familySeHashes[i], true);
			}
		}
		if (tier >= 1 && tier <= num)
		{
			int num2 = tier - 1;
			if (!sEMan.HaveStatusEffect(_familySeHashes[num2]))
			{
				sEMan.AddStatusEffect(_familySeHashes[num2], true, 0, 0f);
			}
		}
	}

	private static void RPC_Server_DebugMarrySelf(long senderUid, int tier)
	{
		if ((Object)(object)ZNet.instance == (Object)null || !ZNet.instance.IsServer())
		{
			return;
		}
		EnsureLoadedOnServer();
		if (!_debugEnabledServer.Value)
		{
			Server_ToClientToast(senderUid, "Debug commands are disabled on this server.");
			return;
		}
		tier = Mathf.Clamp(tier, 1, MaxTierClamped());
		ZNetPeer peer = ZNet.instance.GetPeer(senderUid);
		string name = ((peer != null) ? (peer.m_playerName ?? "Player") : "Player");
		_debugSoloFamily.Add(senderUid);
		if (!UidToHousehold.TryGetValue(senderUid, out var value))
		{
			value = "H" + Guid.NewGuid().ToString("N");
			Household household = new Household
			{
				id = value
			};
			Households[value] = household;
			AddMemberToHousehold(household, senderUid, name);
			UidToHousehold[senderUid] = value;
			_householdsDirty = true;
		}
		if (Households.TryGetValue(UidToHousehold[senderUid], out var value2) && value2.members.TryGetValue(senderUid, out var value3))
		{
			value3.bondSeconds = TierToMinBondSeconds(tier);
			_householdsDirty = true;
		}
		_lastSentTier[senderUid] = 0;
		_lastSentProgressRealtime.Remove(senderUid);
		Server_ToClientToast(senderUid, $"Debug: set bond to Tier {tier}.");
	}

	private static void RPC_Server_RequestMarriage(long senderUid, string targetName)
	{
		if ((Object)(object)ZNet.instance == (Object)null || !ZNet.instance.IsServer())
		{
			return;
		}
		EnsureLoadedOnServer();
		targetName = NormalizePlayerNameInput(targetName);
		if (string.IsNullOrEmpty(targetName))
		{
			Server_ToClientToast(senderUid, "Usage: /marry <playerName>");
			return;
		}
		ZNetPeer peer = ZNet.instance.GetPeer(senderUid);
		if (peer == null)
		{
			return;
		}
		ZNetPeer val = FindPeerByNameSmart(targetName);
		if (val == null)
		{
			Server_ToClientToast(senderUid, "Could not find player '" + targetName + "'.");
			return;
		}
		if (val.m_uid == senderUid)
		{
			Server_ToClientToast(senderUid, "You can’t invite yourself.");
			return;
		}
		if (UidToHousehold.ContainsKey(val.m_uid))
		{
			Server_ToClientToast(senderUid, val.m_playerName + " is already in a household.");
			return;
		}
		string fromHouseholdId = null;
		if (UidToHousehold.TryGetValue(senderUid, out var value))
		{
			fromHouseholdId = value;
		}
		float realtimeSinceStartup = Time.realtimeSinceStartup;
		Pending[val.m_uid] = new Proposal
		{
			FromUid = senderUid,
			FromName = (peer.m_playerName ?? "Someone"),
			FromHouseholdId = fromHouseholdId,
			LastNotifyTime = realtimeSinceStartup
		};
		ZRoutedRpc.instance.InvokeRoutedRPC(val.m_uid, "PX_Marry_NotifyProposal", new object[2]
		{
			peer.m_playerName ?? "Someone",
			senderUid
		});
		Server_ToClientToast(senderUid, "Invite sent to " + val.m_playerName + ".");
	}

	private static void RPC_Server_RespondMarriage(long senderUid, bool accept)
	{
		if ((Object)(object)ZNet.instance == (Object)null || !ZNet.instance.IsServer())
		{
			return;
		}
		EnsureLoadedOnServer();
		if (!accept && UidToHousehold.ContainsKey(senderUid) && !Pending.ContainsKey(senderUid))
		{
			Server_LeaveHousehold(senderUid);
			return;
		}
		if (!Pending.TryGetValue(senderUid, out var value))
		{
			Server_ToClientToast(senderUid, "No pending household invite.");
			return;
		}
		Pending.Remove(senderUid);
		ZNetPeer peer = ZNet.instance.GetPeer(senderUid);
		ZNetPeer peer2 = ZNet.instance.GetPeer(value.FromUid);
		if (peer == null || peer2 == null)
		{
			Server_ToClientToast(senderUid, "Invite no longer valid.");
			return;
		}
		if (!accept)
		{
			Server_ToClientToast(value.FromUid, peer.m_playerName + " declined your invite.");
			Server_ToClientToast(senderUid, "You declined.");
			return;
		}
		if (UidToHousehold.ContainsKey(senderUid))
		{
			Server_ToClientToast(senderUid, "You are already in a household.");
			return;
		}
		string fromHouseholdId = value.FromHouseholdId;
		Household value2;
		if (string.IsNullOrEmpty(fromHouseholdId))
		{
			fromHouseholdId = "H" + Guid.NewGuid().ToString("N");
			Household household = new Household
			{
				id = fromHouseholdId
			};
			Households[fromHouseholdId] = household;
			AddMemberToHousehold(household, value.FromUid, peer2.m_playerName ?? "Player");
			AddMemberToHousehold(household, senderUid, peer.m_playerName ?? "Player");
			UidToHousehold[value.FromUid] = fromHouseholdId;
			UidToHousehold[senderUid] = fromHouseholdId;
			_householdsDirty = true;
			if (_announce.Value)
			{
				ZRoutedRpc.instance.InvokeRoutedRPC(ZRoutedRpc.Everybody, "PX_Marry_NotifyToast", new object[1] { "\ud83c\udfe0 " + peer2.m_playerName + " and " + peer.m_playerName + " formed a Household!" });
			}
			else
			{
				Server_ToClientToast(value.FromUid, "Household formed with " + peer.m_playerName + ".");
				Server_ToClientToast(senderUid, "Household formed with " + peer2.m_playerName + ".");
			}
			_lastSentTier[value.FromUid] = 0;
			_lastSentTier[senderUid] = 0;
			_lastSentProgressRealtime.Remove(value.FromUid);
			_lastSentProgressRealtime.Remove(senderUid);
		}
		else if (!Households.TryGetValue(fromHouseholdId, out value2))
		{
			Server_ToClientToast(senderUid, "Household missing (try again).");
		}
		else
		{
			AddMemberToHousehold(value2, senderUid, peer.m_playerName ?? "Player");
			UidToHousehold[senderUid] = fromHouseholdId;
			_householdsDirty = true;
			if (_announce.Value)
			{
				ZRoutedRpc.instance.InvokeRoutedRPC(ZRoutedRpc.Everybody, "PX_Marry_NotifyToast", new object[1] { "\ud83c\udfe0 " + peer.m_playerName + " joined " + peer2.m_playerName + "'s Household!" });
			}
			else
			{
				Server_ToClientToast(senderUid, "You joined " + peer2.m_playerName + "'s Household.");
				Server_ToClientToast(value.FromUid, peer.m_playerName + " joined your Household.");
			}
			_lastSentTier[senderUid] = 0;
			_lastSentProgressRealtime.Remove(senderUid);
		}
	}

	private static void RPC_Server_DebugClearMarriage(long senderUid)
	{
		if (!((Object)(object)ZNet.instance == (Object)null) && ZNet.instance.IsServer())
		{
			EnsureLoadedOnServer();
			if (!_debugEnabledServer.Value)
			{
				Server_ToClientToast(senderUid, "Debug commands are disabled on this server.");
				return;
			}
			_debugSoloFamily.Remove(senderUid);
			Server_LeaveHousehold(senderUid);
			Server_ToClientFamily(senderUid, 0);
			Server_ToClientFamilyProgress(senderUid, 0, 0f);
		}
	}

	private static void RPC_Server_DebugState(long senderUid)
	{
		if ((Object)(object)ZNet.instance == (Object)null || !ZNet.instance.IsServer())
		{
			return;
		}
		EnsureLoadedOnServer();
		if (!_debugEnabledServer.Value)
		{
			Server_ToClientToast(senderUid, "Debug commands are disabled on this server.");
			return;
		}
		if (!UidToHousehold.TryGetValue(senderUid, out var value) || string.IsNullOrEmpty(value))
		{
			Server_ToClientToast(senderUid, "Not in a household.");
			return;
		}
		if (!Households.TryGetValue(value, out var value2))
		{
			Server_ToClientToast(senderUid, "Household missing.");
			return;
		}
		double num = 0.0;
		if (value2.members.TryGetValue(senderUid, out var value3))
		{
			num = value3.bondSeconds;
		}
		int tierFromBondSeconds = GetTierFromBondSeconds(num);
		Server_ToClientToast(senderUid, $"household={value} members={value2.members.Count} bondDays={num / 1800.0:0.0} tier={tierFromBondSeconds}");
	}

	private static void AddMemberToHousehold(Household hh, long uid, string name)
	{
		if (hh == null)
		{
			return;
		}
		if (!hh.members.TryGetValue(uid, out var value))
		{
			double bondSeconds = 0.0;
			if (_startAtTier1 != null && _startAtTier1.Value)
			{
				bondSeconds = TierToMinBondSeconds(1);
			}
			value = new Member
			{
				uid = uid,
				name = (name ?? "Player"),
				bondSeconds = bondSeconds
			};
			hh.members[uid] = value;
		}
		else
		{
			value.name = name ?? value.name;
		}
	}

	private static void Server_LeaveHousehold(long uid)
	{
		if (!UidToHousehold.TryGetValue(uid, out var value) || string.IsNullOrEmpty(value))
		{
			Server_ToClientToast(uid, "You are not in a household.");
			return;
		}
		if (!Households.TryGetValue(value, out var value2))
		{
			UidToHousehold.Remove(uid);
			_householdsDirty = true;
			Server_ToClientToast(uid, "Household missing; you were removed.");
			return;
		}
		value2.members.Remove(uid);
		UidToHousehold.Remove(uid);
		_householdsDirty = true;
		_lastSentTier.Remove(uid);
		_lastSentProgressRealtime.Remove(uid);
		Server_ToClientFamily(uid, 0);
		Server_ToClientFamilyProgress(uid, 0, 0f);
		Server_ToClientToast(uid, "You left the household.");
		if (value2.members.Count > 1)
		{
			return;
		}
		foreach (KeyValuePair<long, Member> member in value2.members)
		{
			long key = member.Key;
			UidToHousehold.Remove(key);
			_lastSentTier.Remove(key);
			_lastSentProgressRealtime.Remove(key);
			Server_ToClientFamily(key, 0);
			Server_ToClientFamilyProgress(key, 0, 0f);
			Server_ToClientToast(key, "Your household dissolved.");
		}
		Households.Remove(value);
		_householdsDirty = true;
		if (_announce.Value && ZRoutedRpc.instance != null)
		{
			ZRoutedRpc.instance.InvokeRoutedRPC(ZRoutedRpc.Everybody, "PX_Marry_NotifyToast", new object[1] { "\ud83c\udfda Household dissolved." });
		}
	}

	private void ServerTick_AccrueBondSeconds()
	{
		double num = 0.0;
		try
		{
			num = ZNet.instance.GetTimeSeconds();
		}
		catch
		{
			num = Time.timeAsDouble;
		}
		if (_lastWorldTimeSeconds <= 0.0)
		{
			_lastWorldTimeSeconds = num;
			return;
		}
		double num2 = num - _lastWorldTimeSeconds;
		if (num2 <= 0.0 || num2 > 10.0)
		{
			_lastWorldTimeSeconds = num;
			return;
		}
		_lastWorldTimeSeconds = num;
		List<ZNetPeer> peers = ZNet.instance.GetPeers();
		if (peers == null)
		{
			return;
		}
		for (int i = 0; i < peers.Count; i++)
		{
			ZNetPeer val = peers[i];
			if (val != null && val.m_uid != 0L)
			{
				long uid = val.m_uid;
				if (UidToHousehold.TryGetValue(uid, out var value) && !string.IsNullOrEmpty(value) && Households.TryGetValue(value, out var value2) && value2.members.TryGetValue(uid, out var value3) && TryGetPeerAlive(val, out var alive) && alive)
				{
					value3.bondSeconds += num2;
					_householdsDirty = true;
				}
			}
		}
	}

	private void ServerTick_ProximityFamily()
	{
		//IL_01bb: Unknown result type (might be due to invalid IL or missing references)
		//IL_01bd: Unknown result type (might be due to invalid IL or missing references)
		//IL_01bf: Unknown result type (might be due to invalid IL or missing references)
		//IL_01c4: Unknown result type (might be due to invalid IL or missing references)
		List<ZNetPeer> peers = ZNet.instance.GetPeers();
		if (peers == null || peers.Count == 0)
		{
			return;
		}
		float num = Mathf.Max(1f, _rangeMeters.Value);
		float num2 = num * num;
		int num3 = MaxTierClamped();
		for (int i = 0; i < peers.Count; i++)
		{
			ZNetPeer val = peers[i];
			if (val == null || val.m_uid == 0L)
			{
				continue;
			}
			long uid = val.m_uid;
			if (TryGetPeerAlive(val, out var alive) && !alive)
			{
				continue;
			}
			if (!UidToHousehold.TryGetValue(uid, out var value) || string.IsNullOrEmpty(value) || !Households.TryGetValue(value, out var value2))
			{
				MaybeSendTier(uid, 0, 0f);
				continue;
			}
			int num4 = 0;
			float num5 = 0f;
			if (value2.members.TryGetValue(uid, out var value3))
			{
				num4 = Mathf.Clamp(GetTierFromBondSeconds(value3.bondSeconds), 0, num3);
				num5 = (float)(value3.bondSeconds / 1800.0);
			}
			if (num4 <= 0)
			{
				MaybeSendTier(uid, 0, 0f);
				continue;
			}
			if (value2.members.Count <= 1 && _debugSoloFamily.Contains(uid))
			{
				MaybeSendTier(uid, num4, num5);
				continue;
			}
			if (!TryGetPeerPosition(val, out var pos))
			{
				MaybeSendTier(uid, 0, 0f);
				continue;
			}
			bool flag = false;
			foreach (KeyValuePair<long, Member> member in value2.members)
			{
				long key = member.Key;
				if (key == uid)
				{
					continue;
				}
				ZNetPeer peer = ZNet.instance.GetPeer(key);
				if (peer != null && peer.m_uid != 0L && (!TryGetPeerAlive(peer, out var alive2) || alive2) && TryGetPeerPosition(peer, out var pos2))
				{
					Vector3 val2 = pos - pos2;
					float sqrMagnitude = ((Vector3)(ref val2)).sqrMagnitude;
					if (sqrMagnitude <= num2)
					{
						flag = true;
						break;
					}
				}
			}
			MaybeSendTier(uid, flag ? num4 : 0, flag ? num5 : 0f);
		}
	}

	private void MaybeSendTier(long uid, int tier, float bondDays)
	{
		if (!_lastSentTier.TryGetValue(uid, out var value))
		{
			value = -999;
		}
		bool flag = value != tier;
		if (flag)
		{
			bool flag2 = value <= 0 && tier > 0;
			_lastSentTier[uid] = tier;
			Server_ToClientFamily(uid, tier);
			if (tier <= 0)
			{
				_lastSentProgressRealtime.Remove(uid);
				Server_ToClientFamilyProgress(uid, 0, 0f);
			}
			if (flag2 && _enableEnterVfx.Value)
			{
				Server_ToClientPlayVfx(uid, _enterVfxItemPrefab.Value ?? "MeadHasty");
			}
			if (_diagnosticsVerbose.Value && (Object)(object)_self != (Object)null)
			{
				((BaseUnityPlugin)_self).Logger.LogInfo((object)string.Format("[{0}] set-family uid={1} tier={2} entering={3}", "Marriage", uid, tier, flag2));
			}
		}
		if (tier > 0)
		{
			float realtimeSinceStartup = Time.realtimeSinceStartup;
			float num = Mathf.Clamp(_progressSendIntervalSeconds.Value, 1f, 60f);
			if (!_lastSentProgressRealtime.TryGetValue(uid, out var value2) || realtimeSinceStartup - value2 >= num || flag)
			{
				_lastSentProgressRealtime[uid] = realtimeSinceStartup;
				Server_ToClientFamilyProgress(uid, tier, Mathf.Max(0f, bondDays));
			}
		}
	}

	private void ServerTick_RenotifyProposals()
	{
		if (Pending.Count == 0 || ZRoutedRpc.instance == null)
		{
			return;
		}
		float realtimeSinceStartup = Time.realtimeSinceStartup;
		List<long> list = new List<long>(Pending.Keys);
		for (int i = 0; i < list.Count; i++)
		{
			long num = list[i];
			if (Pending.TryGetValue(num, out var value))
			{
				ZNetPeer peer = ZNet.instance.GetPeer(num);
				if (peer != null && !(realtimeSinceStartup - value.LastNotifyTime < 5f))
				{
					ZRoutedRpc.instance.InvokeRoutedRPC(num, "PX_Marry_NotifyProposal", new object[2] { value.FromName, value.FromUid });
					value.LastNotifyTime = realtimeSinceStartup;
					Pending[num] = value;
				}
			}
		}
	}

	private void ServerTick_CleanupStaleProposals()
	{
		if (Pending.Count == 0)
		{
			return;
		}
		List<ZNetPeer> peers = ZNet.instance.GetPeers();
		HashSet<long> hashSet = new HashSet<long>();
		if (peers != null)
		{
			for (int i = 0; i < peers.Count; i++)
			{
				if (peers[i] != null)
				{
					hashSet.Add(peers[i].m_uid);
				}
			}
		}
		List<long> list = new List<long>();
		foreach (KeyValuePair<long, Proposal> item in Pending)
		{
			if (!hashSet.Contains(item.Key))
			{
				list.Add(item.Key);
			}
		}
		for (int j = 0; j < list.Count; j++)
		{
			Pending.Remove(list[j]);
		}
	}

	private static void ServerTick_CleanupDisconnectedCaches()
	{
		if ((Object)(object)ZNet.instance == (Object)null || !ZNet.instance.IsServer())
		{
			return;
		}
		List<ZNetPeer> peers = ZNet.instance.GetPeers();
		HashSet<long> hashSet = new HashSet<long>();
		if (peers != null)
		{
			for (int i = 0; i < peers.Count; i++)
			{
				ZNetPeer val = peers[i];
				if (val != null && val.m_uid != 0L)
				{
					hashSet.Add(val.m_uid);
				}
			}
		}
		List<long> list = new List<long>();
		foreach (KeyValuePair<long, int> item in _lastSentTier)
		{
			if (!hashSet.Contains(item.Key))
			{
				list.Add(item.Key);
			}
		}
		for (int j = 0; j < list.Count; j++)
		{
			long key = list[j];
			_lastSentTier.Remove(key);
			_lastSentProgressRealtime.Remove(key);
		}
	}

	private static void Server_ToClientToast(long targetUid, string msg)
	{
		if (ZRoutedRpc.instance != null)
		{
			ZRoutedRpc.instance.InvokeRoutedRPC(targetUid, "PX_Marry_NotifyToast", new object[1] { msg });
		}
	}

	private static void Server_ToClientFamily(long targetUid, int tier)
	{
		if (ZRoutedRpc.instance != null)
		{
			ZRoutedRpc.instance.InvokeRoutedRPC(targetUid, "PX_Family_SetTier", new object[1] { tier });
		}
	}

	private static void Server_ToClientFamilyProgress(long targetUid, int tier, float bondDays)
	{
		if (ZRoutedRpc.instance != null)
		{
			ZRoutedRpc.instance.InvokeRoutedRPC(targetUid, "PX_Family_SetProgress", new object[2] { tier, bondDays });
		}
	}

	private static void Server_ToClientPlayVfx(long targetUid, string itemPrefabName)
	{
		if (ZRoutedRpc.instance != null)
		{
			ZRoutedRpc.instance.InvokeRoutedRPC(targetUid, "PX_Family_PlayVfx", new object[1] { itemPrefabName ?? "" });
		}
	}

	private static void Server_ToClientMourning(long targetUid, int tier, float durationSeconds)
	{
		if (ZRoutedRpc.instance != null)
		{
			ZRoutedRpc.instance.InvokeRoutedRPC(targetUid, "PX_Mourning_Apply", new object[2] { tier, durationSeconds });
		}
	}

	private static bool TryGetPeerPosition(ZNetPeer peer, out Vector3 pos)
	{
		//IL_0001: Unknown result type (might be due to invalid IL or missing references)
		//IL_0006: Unknown result type (might be due to invalid IL or missing references)
		//IL_001b: Unknown result type (might be due to invalid IL or missing references)
		//IL_0020: 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)
		//IL_002b: Unknown result type (might be due to invalid IL or missing references)
		//IL_003c: Unknown result type (might be due to invalid IL or missing references)
		//IL_004c: Unknown result type (might be due to invalid IL or missing references)
		//IL_0051: Unknown result type (might be due to invalid IL or missing references)
		pos = Vector3.zero;
		if (peer == null)
		{
			return false;
		}
		if (ZDOMan.instance == null)
		{
			return false;
		}
		try
		{
			ZDOID characterID = peer.m_characterID;
			if (((ZDOID)(ref characterID)).Equals(default(ZDOID)))
			{
				return false;
			}
			ZDO zDO = ZDOMan.instance.GetZDO(characterID);
			if (zDO == null)
			{
				return false;
			}
			pos = zDO.GetPosition();
			return true;
		}
		catch
		{
			return false;
		}
	}

	private static bool TryGetPeerAlive(ZNetPeer peer, out bool alive)
	{
		//IL_0013: Unknown result type (might be due to invalid IL or missing references)
		//IL_0018: Unknown result type (might be due to invalid IL or missing references)
		//IL_001d: Unknown result type (might be due to invalid IL or missing references)
		//IL_0023: Unknown result type (might be due to invalid IL or missing references)
		//IL_0035: Unknown result type (might be due to invalid IL or missing references)
		alive = false;
		if (peer == null)
		{
			return false;
		}
		if (ZDOMan.instance == null)
		{
			return false;
		}
		try
		{
			ZDOID characterID = peer.m_characterID;
			if (((ZDOID)(ref characterID)).Equals(default(ZDOID)))
			{
				return false;
			}
			ZDO zDO = ZDOMan.instance.GetZDO(characterID);
			if (zDO == null)
			{
				return false;
			}
			float @float = zDO.GetFloat("health", 0f);
			alive = @float > 0.01f;
			return true;
		}
		catch
		{
			return false;
		}
	}

	private static void EnsureLoadedOnServer()
	{
		if (!_householdsLoaded)
		{
			LoadHouseholdsOnce();
		}
	}

	private static void LoadHouseholdsOnce()
	{
		_householdsLoaded = true;
		_lastWorldTimeSeconds = 0.0;
		_lastSaveRealtime = Time.realtimeSinceStartup;
		try
		{
			Households.Clear();
			UidToHousehold.Clear();
			_lastSentTier.Clear();
			_lastSentProgressRealtime.Clear();
			if (!File.Exists(_householdsPath))
			{
				return;
			}
			string[] array = File.ReadAllLines(_householdsPath);
			Household household = null;
			foreach (string text in array)
			{
				if (string.IsNullOrEmpty(text) || text.StartsWith("#"))
				{
					continue;
				}
				if (text.StartsWith("H\t"))
				{
					string[] array2 = text.Split(new char[1] { '\t' });
					if (array2.Length >= 2)
					{
						string text2 = array2[1];
						household = new Household
						{
							id = text2
						};
						Households[text2] = household;
					}
				}
				else
				{
					if (!text.StartsWith("M\t") || household == null)
					{
						continue;
					}
					string[] array3 = text.Split(new char[1] { '\t' });
					if (array3.Length >= 4 && long.TryParse(array3[1], NumberStyles.Integer, CultureInfo.InvariantCulture, out var result))
					{
						string text3 = Unescape(array3[2]);
						if (!double.TryParse(array3[3], NumberStyles.Float, CultureInfo.InvariantCulture, out var result2))
						{
							result2 = 0.0;
						}
						Member value = new Member
						{
							uid = result,
							name = (text3 ?? "Player"),
							bondSeconds = Math.Max(0.0, result2)
						};
						household.members[result] = value;
						UidToHousehold[result] = household.id;
					}
				}
			}
		}
		catch (Exception ex)
		{
			MarriagePlugin self = _self;
			if (self != null)
			{
				((BaseUnityPlugin)self).Logger.LogWarning((object)("[Marriage] Failed to load households: " + ex.Message));
			}
		}
	}

	private static void SaveHouseholdsIfDirty(bool force)
	{
		if (!_householdsDirty && !force)
		{
			return;
		}
		float realtimeSinceStartup = Time.realtimeSinceStartup;
		float num = Mathf.Max(5f, _saveIntervalSeconds.Value);
		if (!force && realtimeSinceStartup - _lastSaveRealtime < num)
		{
			return;
		}
		_lastSaveRealtime = realtimeSinceStartup;
		try
		{
			List<string> list = new List<string> { "# pix.marriage.households.txt", "# H<TAB>householdId", "# M<TAB>uid<TAB>escapedName<TAB>bondSeconds" };
			foreach (KeyValuePair<string, Household> household in Households)
			{
				Household value = household.Value;
				if (value == null)
				{
					continue;
				}
				list.Add("H\t" + value.id);
				foreach (KeyValuePair<long, Member> member in value.members)
				{
					Member value2 = member.Value;
					if (value2 != null)
					{
						list.Add("M\t" + value2.uid.ToString(CultureInfo.InvariantCulture) + "\t" + Escape(value2.name ?? "Player") + "\t" + value2.bondSeconds.ToString("R", CultureInfo.InvariantCulture));
					}
				}
			}
			string text = _householdsPath + ".tmp";
			File.WriteAllLines(text, list.ToArray());
			File.Copy(text, _householdsPath, overwrite: true);
			File.Delete(text);
			_householdsDirty = false;
		}
		catch (Exception ex)
		{
			MarriagePlugin self = _self;
			if (self != null)
			{
				((BaseUnityPlugin)self).Logger.LogWarning((object)("[Marriage] Failed to save households: " + ex.Message));
			}
		}
	}

	private static string Escape(string s)
	{
		try
		{
			return Uri.EscapeDataString(s ?? "");
		}
		catch
		{
			return "";
		}
	}

	private static string Unescape(string s)
	{
		try
		{
			return Uri.UnescapeDataString(s ?? "");
		}
		catch
		{
			return s ?? "";
		}
	}

	private static void TryRegisterStatusEffects(ObjectDB odb)
	{
		if (!((Object)(object)odb == (Object)null) && odb.m_StatusEffects != null)
		{
			TryLoadIconsOnce();
			int num = MaxTierClamped();
			if (FamilySeNames == null || FamilySeNames.Length != num || MourningSeNames == null || MourningSeNames.Length != num)
			{
				BuildSeArrays();
			}
			for (int i = 1; i <= num; i++)
			{
				EnsureFamilySe(odb, i);
				EnsureMourningSe(odb, i);
			}
			Client_RefreshAllFamilyTooltips(_clientFamilyBondDays);
		}
	}

	private static void EnsureFamilySe(ObjectDB odb, int tier)
	{
		//IL_00a8: Unknown result type (might be due to invalid IL or missing references)
		//IL_00bc: Unknown result type (might be due to invalid IL or missing references)
		int num = MaxTierClamped();
		if (tier < 1 || tier > num)
		{
			return;
		}
		string name = FamilySeNames[tier - 1];
		int hash = _familySeHashes[tier - 1];
		StatusEffect val = FindStatusEffectByNameOrHash(odb, name, hash);
		if ((Object)(object)val != (Object)null)
		{
			if ((Object)(object)_familyIcon != (Object)null)
			{
				val.m_icon = _familyIcon;
			}
			return;
		}
		float tierBonus = GetTierBonus(tier);
		SE_Stats val2 = ScriptableObject.CreateInstance<SE_Stats>();
		((Object)val2).name = name;
		((StatusEffect)val2).m_name = $"Family (Tier {tier})";
		((StatusEffect)val2).m_tooltip = BuildFamilyTooltip(tier, tierBonus, 0f);
		((StatusEffect)val2).m_icon = _familyIcon ?? GetFallbackIcon();
		((StatusEffect)val2).m_startMessageType = (MessageType)1;
		((StatusEffect)val2).m_startMessage = "";
		((StatusEffect)val2).m_stopMessageType = (MessageType)1;
		((StatusEffect)val2).m_stopMessage = "";
		ApplyFamilyStats(val2, tierBonus);
		odb.m_StatusEffects.Add((StatusEffect)(object)val2);
	}

	private static void EnsureMourningSe(ObjectDB odb, int tier)
	{
		//IL_0098: Unknown result type (might be due to invalid IL or missing references)
		//IL_00ac: Unknown result type (might be due to invalid IL or missing references)
		int num = MaxTierClamped();
		if (tier < 1 || tier > num)
		{
			return;
		}
		string name = MourningSeNames[tier - 1];
		int hash = _mourningSeHashes[tier - 1];
		StatusEffect val = FindStatusEffectByNameOrHash(odb, name, hash);
		if ((Object)(object)val != (Object)null)
		{
			if ((Object)(object)_mourningIcon != (Object)null)
			{
				val.m_icon = _mourningIcon;
			}
			return;
		}
		float mourningPenalty = GetMourningPenalty(tier);
		SE_Stats val2 = ScriptableObject.CreateInstance<SE_Stats>();
		((Object)val2).name = name;
		((StatusEffect)val2).m_name = "Mourning";
		((StatusEffect)val2).m_tooltip = BuildMourningTooltip(tier, mourningPenalty);
		((StatusEffect)val2).m_icon = _mourningIcon ?? GetFallbackIcon();
		((StatusEffect)val2).m_startMessageType = (MessageType)1;
		((StatusEffect)val2).m_startMessage = "";
		((StatusEffect)val2).m_stopMessageType = (MessageType)1;
		((StatusEffect)val2).m_stopMessage = "";
		ApplyMourningStats(val2, mourningPenalty);
		odb.m_StatusEffects.Add((StatusEffect)(object)val2);
	}

	private static StatusEffect FindStatusEffectByNameOrHash(ObjectDB odb, string name, int hash)
	{
		try
		{
			List<StatusEffect> statusEffects = odb.m_StatusEffects;
			for (int i = 0; i < statusEffects.Count; i++)
			{
				StatusEffect val = statusEffects[i];
				if (!((Object)(object)val == (Object)null))
				{
					if (!string.IsNullOrEmpty(((Object)val).name) && string.Equals(((Object)val).name, name, StringComparison.Ordinal))
					{
						return val;
					}
					if (!string.IsNullOrEmpty(((Object)val).name) && ((Object)val).name.GetStableHashCode() == hash)
					{
						return val;
					}
				}
			}
		}
		catch
		{
		}
		return null;
	}

	private static string BuildFamilyTooltip(int tier, float bonus, float bondDays)
	{
		int num = Mathf.RoundToInt(bonus * 100f);
		List<string> list = new List<string>();
		if (_buffMove.Value)
		{
			list.Add($"+{num}% move speed");
		}
		if (_buffDamage.Value)
		{
			list.Add($"+{num}% damage");
		}
		if (_buffStaminaRegen.Value)
		{
			list.Add($"+{num}% stamina regen");
		}
		if (_buffHealthRegen.Value)
		{
			list.Add($"+{num}% health regen");
		}
		if (_buffEitrRegen.Value)
		{
			list.Add($"+{num}% eitr regen");
		}
		if (list.Count == 0)
		{
			list.Add("No bonuses enabled (check config).");
		}
		string text = string.Format("Being near your Household grants bonuses.\nTier {0}: {1}", tier, string.Join(", ", list));
		if (!_familyProgressInTooltip.Value)
		{
			return text;
		}
		string text2 = BuildTierProgressLine(tier, bondDays);
		if (!string.IsNullOrEmpty(text2))
		{
			return text + "\n" + text2;
		}
		return text;
	}

	private static string BuildTierProgressLine(int tier, float bondDays)
	{
		int num = MaxTierClamped();
		if (tier <= 0)
		{
			return "";
		}
		if (tier >= num)
		{
			return $"Tier {num}: MAX ({bondDays:0.0} days)";
		}
		float tierThresholdDays = GetTierThresholdDays(tier);
		float tierThresholdDays2 = GetTierThresholdDays(tier + 1);
		float num2 = Mathf.Max(0.0001f, tierThresholdDays2 - tierThresholdDays);
		float num3 = Mathf.Clamp(bondDays - tierThresholdDays, 0f, num2);
		float num4 = num3 / num2 * 100f;
		return $"Tier {tier}: {num4:0}% ({num3:0.0} / {num2:0.0} days)";
	}

	private static string BuildMourningTooltip(int tier, float penalty)
	{
		int num = Mathf.RoundToInt(penalty * 100f);
		float value = _mourningCarryWeightPenalty.Value;
		List<string> list = new List<string>
		{
			$"-{num}% move speed",
			$"-{num}% damage",
			$"-{num}% stamina regen",
			$"-{num}% health regen",
			$"-{num}% eitr regen"
		};
		if (Mathf.Abs(value) > 0.01f)
		{
			list.Add($"{value:0} carry weight");
		}
		return string.Format("A Household member has died.\nTier {0}: {1}", tier, string.Join(", ", list));
	}

	private static void ApplyFamilyStats(SE_Stats se, float bonus)
	{
		try
		{
			if (_buffMove.Value)
			{
				SetFieldIfExists(se, "m_speedModifier", bonus);
			}
			else
			{
				SetFieldIfExists(se, "m_speedModifier", 0f);
			}
			if (_buffDamage.Value)
			{
				SetFieldIfExists(se, "m_damageModifier", bonus);
			}
			else
			{
				SetFieldIfExists(se, "m_damageModifier", 0f);
			}
			if (_buffStaminaRegen.Value)
			{
				SetFieldIfExists(se, "m_staminaRegenMultiplier", 1f + bonus);
			}
			else
			{
				SetFieldIfExists(se, "m_staminaRegenMultiplier", 1f);
			}
			if (_buffHealthRegen.Value)
			{
				SetFieldIfExists(se, "m_healthRegenMultiplier", 1f + bonus);
			}
			else
			{
				SetFieldIfExists(se, "m_healthRegenMultiplier", 1f);
			}
			if (_buffEitrRegen.Value)
			{
				SetFieldIfExists(se, "m_eitrRegenMultiplier", 1f + bonus);
			}
			else
			{
				SetFieldIfExists(se, "m_eitrRegenMultiplier", 1f);
			}
		}
		catch
		{
		}
	}

	private static void ApplyMourningStats(SE_Stats se, float penalty)
	{
		try
		{
			SetFieldIfExists(se, "m_speedModifier", 0f - penalty);
			SetFieldIfExists(se, "m_damageModifier", 0f - penalty);
			float num = Mathf.Clamp(1f - penalty, 0.05f, 10f);
			SetFieldIfExists(se, "m_staminaRegenMultiplier", num);
			SetFieldIfExists(se, "m_healthRegenMultiplier", num);
			SetFieldIfExists(se, "m_eitrRegenMultiplier", num);
			float value = _mourningCarryWeightPenalty.Value;
			SetFieldIfExists(se, "m_addMaxCarryWeight", value);
		}
		catch
		{
		}
	}

	private static void SetFieldIfExists(object obj, string fieldName, object value)
	{
		if (obj == null)
		{
			return;
		}
		try
		{
			Type type = obj.GetType();
			FieldInfo field = type.GetField(fieldName, BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
			if (!(field == null))
			{
				if (value != null && field.FieldType.IsAssignableFrom(value.GetType()))
				{
					field.SetValue(obj, value);
				}
				else if (field.FieldType == typeof(float) && value is double num)
				{
					field.SetValue(obj, (float)num);
				}
				else if (field.FieldType == typeof(float) && value is int num2)
				{
					field.SetValue(obj, (float)num2);
				}
				else if (field.FieldType == typeof(int) && value is float num3)
				{
					field.SetValue(obj, (int)num3);
				}
				else if (field.FieldType == typeof(int) && value is double)
				{
					double num4 = (double)value;
					field.SetValue(obj, (int)num4);
				}
			}
		}
		catch
		{
		}
	}

	private static void Client_RefreshAllFamilyTooltips(float bondDays)
	{
		if (!((Object)(object)ObjectDB.instance == (Object)null) && ObjectDB.instance.m_StatusEffects != null)
		{
			int num = MaxTierClamped();
			for (int i = 1; i <= num; i++)
			{
				Client_RefreshFamilyTooltipForTier(i, bondDays);
			}
		}
	}

	private static void Client_RefreshFamilyTooltipForTier(int tier, float bondDays)
	{
		int num = MaxTierClamped();
		if (tier >= 1 && tier <= num && !((Object)(object)ObjectDB.instance == (Object)null))
		{
			StatusEffect val = FindStatusEffectByNameOrHash(ObjectDB.instance, FamilySeNames[tier - 1], _familySeHashes[tier - 1]);
			if (!((Object)(object)val == (Object)null))
			{
				float tierBonus = GetTierBonus(tier);
				val.m_tooltip = BuildFamilyTooltip(tier, tierBonus, bondDays);
			}
		}
	}

	private static Sprite GetFallbackIcon()
	{
		try
		{
			ObjectDB instance = ObjectDB.instance;
			if ((Object)(object)instance != (Object)null && instance.m_StatusEffects != null)
			{
				for (int i = 0; i < instance.m_StatusEffects.Count; i++)
				{
					StatusEffect val = instance.m_StatusEffects[i];
					if ((Object)(object)val != (Object)null && (Object)(object)val.m_icon != (Object)null)
					{
						return val.m_icon;
					}
				}
			}
		}
		catch
		{
		}
		return null;
	}

	private static void TryLoadIconsOnce()
	{
		if (_iconsTried)
		{
			return;
		}
		_iconsTried = true;
		try
		{
			string text = "";
			try
			{
				text = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location);
			}
			catch
			{
			}
			if (!string.IsNullOrEmpty(text))
			{
				string path = Path.Combine(text, "family.png");
				string path2 = Path.Combine(text, "mourning.png");
				_familyIcon = LoadSpriteFromPngPath(path);
				_mourningIcon = LoadSpriteFromPngPath(path2);
			}
			if ((Object)(object)_familyIcon == (Object)null)
			{
				_familyIcon = GetFallbackIcon();
			}
			if ((Object)(object)_mourningIcon == (Object)null)
			{
				_mourningIcon = GetFallbackIcon();
			}
		}
		catch
		{
		}
	}

	private static void EnsurePngLoader()
	{
		if (_pngLoaderTried)
		{
			return;
		}
		_pngLoaderTried = true;
		try
		{
			Type typeFromHandle = typeof(Texture2D);
			_miTexture2D_LoadImage_Bytes = typeFromHandle.GetMethod("LoadImage", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic, null, new Type[1] { typeof(byte[]) }, null);
			_miTexture2D_LoadImage_BytesBool = typeFromHandle.GetMethod("LoadImage", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic, null, new Type[2]
			{
				typeof(byte[]),
				typeof(bool)
			}, null);
			Type type = Type.GetType("UnityEngine.ImageConversion, UnityEngine.ImageConversionModule", throwOnError: false) ?? Type.GetType("UnityEngine.ImageConversion, UnityEngine", throwOnError: false);
			if (type != null)
			{
				_miImageConversion_LoadImage = type.GetMethod("LoadImage", BindingFlags.Static | BindingFlags.Public, null, new Type[3]
				{
					typeof(Texture2D),
					typeof(byte[]),
					typeof(bool)
				}, null);
			}
		}
		catch
		{
		}
	}

	private static bool TryLoadPngIntoTexture(Texture2D tex, byte[] data)
	{
		if ((Object)(object)tex == (Object)null || data == null || data.Length < 8)
		{
			return false;
		}
		EnsurePngLoader();
		try
		{
			if (_miTexture2D_LoadImage_BytesBool != null)
			{
				if (_miTexture2D_LoadImage_BytesBool.Invoke(tex, new object[2] { data, true }) is bool result)
				{
					return result;
				}
				return true;
			}
			if (_miTexture2D_LoadImage_Bytes != null)
			{
				if (_miTexture2D_LoadImage_Bytes.Invoke(tex, new object[1] { data }) is bool result2)
				{
					return result2;
				}
				return true;
			}
			if (_miImageConversion_LoadImage != null)
			{
				if (_miImageConversion_LoadImage.Invoke(null, new object[3] { tex, data, true }) is bool result3)
				{
					return result3;
				}
				return true;
			}
		}
		catch
		{
		}
		return false;
	}

	private static Sprite LoadSpriteFromPngPath(string path)
	{
		//IL_0033: Unknown result type (might be due to invalid IL or missing references)
		//IL_0039: Expected O, but got Unknown
		//IL_006d: Unknown result type (might be due to invalid IL or missing references)
		//IL_007c: Unknown result type (might be due to invalid IL or missing references)
		try
		{
			if (string.IsNullOrEmpty(path))
			{
				return null;
			}
			if (!File.Exists(path))
			{
				return null;
			}
			byte[] array = File.ReadAllBytes(path);
			if (array == null || array.Length < 8)
			{
				return null;
			}
			Texture2D val = new Texture2D(2, 2, (TextureFormat)4, false);
			if (!TryLoadPngIntoTexture(val, array))
			{
				return null;
			}
			((Texture)val).wrapMode = (TextureWrapMode)1;
			((Texture)val).filterMode = (FilterMode)1;
			return Sprite.Create(val, new Rect(0f, 0f, (float)((Texture)val).width, (float)((Texture)val).height), new Vector2(0.5f, 0.5f), 100f);
		}
		catch
		{
			return null;
		}
	}

	private static ZNetPeer FindPeerByNameSmart(string input)
	{
		input = NormalizePlayerNameInput(input);
		if (string.IsNullOrEmpty(input))
		{
			return null;
		}
		List<ZNetPeer> list = (((Object)(object)ZNet.instance != (Object)null) ? ZNet.instance.GetPeers() : null);
		if (list == null)
		{
			return null;
		}
		for (int i = 0; i < list.Count; i++)
		{
			ZNetPeer val = list[i];
			if (val != null && string.Equals(val.m_playerName ?? "", input, StringComparison.OrdinalIgnoreCase))
			{
				return val;
			}
		}
		for (int j = 0; j < list.Count; j++)
		{
			ZNetPeer val2 = list[j];
			if (val2 != null)
			{
				string text = val2.m_playerName ?? "";
				if (text.StartsWith(input, StringComparison.OrdinalIgnoreCase))
				{
					return val2;
				}
			}
		}
		ZNetPeer val3 = null;
		for (int k = 0; k < list.Count; k++)
		{
			ZNetPeer val4 = list[k];
			if (val4 == null)
			{
				continue;
			}
			string text2 = val4.m_playerName ?? "";
			if (text2.IndexOf(input, StringComparison.OrdinalIgnoreCase) >= 0)
			{
				if (val3 != null)
				{
					return null;
				}
				val3 = val4;
			}
		}
		return val3;
	}

	private static string NormalizePlayerNameInput(string s)
	{
		if (string.IsNullOrEmpty(s))
		{
			return "";
		}
		s = s.Trim();
		while (s.Contains("  "))
		{
			s = s.Replace("  ", " ");
		}
		return s;
	}
}
internal static class StableHashExtensions
{
	public static int GetStableHashCode(this string str)
	{
		if (str == null)
		{
			return 0;
		}
		int num = 5381;
		int num2 = num;
		for (int i = 0; i < str.Length && str[i] != 0; i += 2)
		{
			num = ((num << 5) + num) ^ str[i];
			if (i == str.Length - 1 || str[i + 1] == '\0')
			{
				break;
			}
			num2 = ((num2 << 5) + num2) ^ str[i + 1];
		}
		return num + num2 * 1566083941;
	}
}