using System; using System.Collections; using System.Collections.Generic; using System.Diagnostics; using System.Globalization; using System.IO; using System.Linq; using System.Net; using System.Reflection; using System.Reflection.Emit; using System.Runtime.CompilerServices; using System.Runtime.Serialization; using System.Runtime.Serialization.Json; using System.Runtime.Versioning; using System.Security; using System.Security.Cryptography; using System.Security.Permissions; using System.Text; using System.Text.RegularExpressions; using System.Threading; using BepInEx; using BepInEx.Bootstrap; using BepInEx.Configuration; using BepInEx.Logging; using FortecaClanWarfare.Core; using FortecaClanWarfare.Data; using FortecaClanWarfare.Network; using FortecaClanWarfare.Patches; using FortecaClanWarfare.UI; using FortecaClanWarfare.Utils; using HarmonyLib; using Microsoft.CodeAnalysis; using Splatform; using UnityEngine; using UnityEngine.Rendering; using UnityEngine.UI; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)] [assembly: AssemblyCompany("forteca")] [assembly: AssemblyConfiguration("Debug")] [assembly: AssemblyDescription("Clan-based territory warfare mod for Valheim")] [assembly: AssemblyFileVersion("0.2.0.0")] [assembly: AssemblyInformationalVersion("0.2.0")] [assembly: AssemblyProduct("FortecaClanWarfare")] [assembly: AssemblyTitle("FortecaClanWarfare")] [assembly: TargetFramework(".NETFramework,Version=v4.7.2", FrameworkDisplayName = ".NET Framework 4.7.2")] [assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)] [assembly: AssemblyVersion("0.2.0.0")] [module: UnverifiableCode] [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 FortecaClanWarfare { [BepInPlugin("com.forteca.clanwarfare", "Forteca Clan Warfare", "0.2.0")] [BepInProcess("valheim.exe")] [BepInProcess("valheim_server.exe")] public class Plugin : BaseUnityPlugin { public const string PluginGUID = "com.forteca.clanwarfare"; public const string PluginName = "Forteca Clan Warfare"; public const string PluginVersion = "0.2.0"; public const string RuntimeBuildMarker = "PROFILE_IMAGE_FIX_20260728_020201"; private Harmony _harmony; private GameObject _managerObj; public static Plugin Instance { get; private set; } public static ManualLogSource Log { get; private set; } private void Awake() { //IL_00cb: Unknown result type (might be due to invalid IL or missing references) //IL_00d5: Expected O, but got Unknown //IL_0115: Unknown result type (might be due to invalid IL or missing references) //IL_011f: Expected O, but got Unknown //IL_0125: Unknown result type (might be due to invalid IL or missing references) //IL_012f: Expected O, but got Unknown //IL_0147: Unknown result type (might be due to invalid IL or missing references) //IL_014d: Invalid comparison between Unknown and I4 Instance = this; Log = ((BaseUnityPlugin)this).Logger; string location = Assembly.GetExecutingAssembly().Location; string text = Path.GetDirectoryName(location) ?? Paths.PluginPath; string fullPath = Path.GetFullPath(Path.Combine(text, "Profiles")); Log.LogInfo((object)("[FCW Runtime] Assembly location: " + location)); Log.LogInfo((object)("[FCW Runtime] Assembly version: " + Assembly.GetExecutingAssembly().GetName().Version)); Log.LogInfo((object)"[FCW Runtime] Build marker: PROFILE_IMAGE_FIX_20260728_020201"); Log.LogInfo((object)("[FCW Runtime] Base directory: " + text)); Log.LogInfo((object)("[FCW Runtime] Profiles directory: " + fullPath)); ClanConfig.Initialize(((BaseUnityPlugin)this).Config); _harmony = new Harmony("com.forteca.clanwarfare"); _harmony.PatchAll(typeof(Plugin).Assembly); TargetPortalFehuAdapter.Initialize(_harmony); ((MonoBehaviour)this).InvokeRepeating("RetryTargetPortalAdapter", 2f, 2f); _managerObj = new GameObject("FortecaClanWarfare"); Object.DontDestroyOnLoad((Object)_managerObj); _managerObj.AddComponent(); _managerObj.AddComponent(); if ((int)SystemInfo.graphicsDeviceType != 4) { _managerObj.AddComponent(); _managerObj.AddComponent(); _managerObj.AddComponent(); _managerObj.AddComponent(); _managerObj.AddComponent(); _managerObj.AddComponent(); } Log.LogMessage((object)""); Log.LogMessage((object)" ============================================"); Log.LogMessage((object)" FORTECA CLAN WARFARE"); Log.LogMessage((object)" Developed by Freyja and Black Viking"); Log.LogMessage((object)" https://tinyurl.com/DiscordFreyja"); Log.LogMessage((object)" ============================================"); Log.LogMessage((object)""); } private void OnDestroy() { //IL_0021: Unknown result type (might be due to invalid IL or missing references) //IL_002c: Expected O, but got Unknown //IL_0034: Unknown result type (might be due to invalid IL or missing references) //IL_003e: Expected O, but got Unknown ((MonoBehaviour)this).CancelInvoke("RetryTargetPortalAdapter"); Harmony harmony = _harmony; if (harmony != null) { harmony.UnpatchSelf(); } if ((Object)_managerObj != (Object)null) { Object.Destroy((Object)_managerObj); } } private void RetryTargetPortalAdapter() { TargetPortalFehuAdapter.RefreshIfNeeded(); if (TargetPortalFehuAdapter.AdapterEnabled) { ((MonoBehaviour)this).CancelInvoke("RetryTargetPortalAdapter"); } } } [HarmonyPatch] public static class TargetPortalDebugCommands { [Serializable] [CompilerGenerated] private sealed class <>c { public static readonly <>c <>9 = new <>c(); public static ConsoleEvent <>9__0_0; public static ConsoleEvent <>9__0_1; internal void b__0_0(ConsoleEventArgs args) { args.Context.AddString(TargetPortalFehuAdapter.DebugSetCurrentPortalClanMode()); } internal void b__0_1(ConsoleEventArgs args) { args.Context.AddString(TargetPortalFehuAdapter.DebugNearPortal()); } } [HarmonyPatch(typeof(Terminal), "InitTerminal")] [HarmonyPostfix] public static void RegisterCommands() { //IL_0032: Unknown result type (might be due to invalid IL or missing references) //IL_001e: 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_0029: Expected O, but got Unknown //IL_006a: Unknown result type (might be due to invalid IL or missing references) //IL_0056: Unknown result type (might be due to invalid IL or missing references) //IL_005b: Unknown result type (might be due to invalid IL or missing references) //IL_0061: Expected O, but got Unknown object obj = <>c.<>9__0_0; if (obj == null) { ConsoleEvent val = delegate(ConsoleEventArgs args) { args.Context.AddString(TargetPortalFehuAdapter.DebugSetCurrentPortalClanMode()); }; <>c.<>9__0_0 = val; obj = (object)val; } new ConsoleCommand("fcw_portal_clan", "Set the nearby TargetPortal portal to FCW Clan mode for the current clan.", (ConsoleEvent)obj, true, false, false, false, false, (ConsoleOptionsFetcher)null, false, false, false); object obj2 = <>c.<>9__0_1; if (obj2 == null) { ConsoleEvent val2 = delegate(ConsoleEventArgs args) { args.Context.AddString(TargetPortalFehuAdapter.DebugNearPortal()); }; <>c.<>9__0_1 = val2; obj2 = (object)val2; } new ConsoleCommand("fcw_portal_debug_near", "Print FCW TargetPortal permission and ownership data for the nearby portal.", (ConsoleEvent)obj2, true, false, false, false, false, (ConsoleOptionsFetcher)null, false, false, false); } } public static class TargetPortalFehuAdapter { private sealed class PortalAccessData { public string AccessType = "None"; public int ClanId; public ClanRank RequiredRank; public long FreebornId; public long OwnerPlayerId; public string ClanName = ""; public string ClanTag = ""; public string FreebornName = ""; public string OwnerName = ""; } private const string TargetPortalGuid = "org.bepinex.plugins.targetportal"; private const string TargetPortalModeKey = "Target Portal PortalMode"; private const string TargetPortalModeKeyAlt = "TargetPortal PortalMode"; private const string TargetPortalOwnerIdKey = "Target Portal PortalOwnerId"; private const string TargetPortalOwnerIdKeyAlt = "TargetPortal PortalOwnerId"; private const string TargetPortalOwnerNameKey = "Target Portal PortalOwnerName"; private const string TargetPortalOwnerNameKeyAlt = "TargetPortal PortalOwnerName"; private const string FcwTargetPortalModeKey = "FCW_TargetPortalMode"; private const string FcwTargetPortalClanIdKey = "FCW_TargetPortalClanId"; private const string FcwTargetPortalClanNameKey = "FCW_TargetPortalClanName"; private const string FcwTargetPortalClanTagKey = "FCW_TargetPortalClanTag"; private const string FcwTargetPortalOwnerPlayerIdKey = "FCW_TargetPortalOwnerPlayerId"; private const string FcwTargetPortalOwnerNameKey = "FCW_TargetPortalOwnerName"; private const string FcwTargetPortalCreatorIdKey = "FCW_TargetPortalCreatorId"; private const string FcwTargetPortalCreatorSteamIdKey = "FCW_TargetPortalCreatorSteamId"; private const string FcwTargetPortalCreatorPlayerIdKey = "FCW_TargetPortalCreatorPlayerId"; private const string FcwTargetPortalCreatorNameKey = "FCW_TargetPortalCreatorName"; private const string TargetPortalProtectionOwnerIdKey = "TargetPortalProtection OwnerId"; private const int PortalModePublic = 0; private const int PortalModePrivate = 1; private const int PortalModeGroup = 2; private const int PortalModeAdmin = 3; private const int PortalModeGuildCarrier = 4; private static readonly string[] BoolHookNames = new string[4] { "HandlePortalClick", "CanUsePortal", "CanTeleport", "FilterPortals" }; private static readonly string[] PortalListHookNames = new string[7] { "FilterPortals", "FetchPortals", "GetPortals", "AddPortalPins", "FillFavorites", "RefereshPortalPins", "HandlePortalClick" }; private static readonly HashSet Logged = new HashSet(); private static readonly Dictionary PreviousPortalModes = new Dictionary(); private static readonly HashSet MigratedClanCarriers = new HashSet(); private static readonly Dictionary AllowedPortalDestroyChain = new Dictionary(); private static bool _initialized; private static bool _enabled; private static bool _targetPortalFound; private static Harmony _harmony; private static float _nextDetectionTime; private static int _lastPatchedCount; private static float _nextRetryLogTime; private static bool _dumpedToggleDetails; private static Type _targetPortalType; private static Type _targetPortalMapType; private static FieldInfo _targetPortalKnownPortalsField; private static FieldInfo _targetPortalActivePinsField; private static FieldInfo _targetPortalMapPinTypeField; public static bool TargetPortalFound { get { RefreshIfNeeded(); return _targetPortalFound; } } public static bool AdapterEnabled { get { RefreshIfNeeded(); return _enabled; } } public static void Initialize(Harmony harmony) { _harmony = harmony; if (!_initialized) { _initialized = true; TryEnableAdapter(logMissing: true); } } public static void RefreshIfNeeded() { if (!_enabled) { float time = Time.time; if (!(time > 0f) || !(time < _nextDetectionTime)) { _nextDetectionTime = time + 3f; TryEnableAdapter(logMissing: false); } } } private static bool ShouldLogRetry(bool force) { if (force) { _nextRetryLogTime = Time.time + 3f; return true; } if (Time.time >= _nextRetryLogTime) { _nextRetryLogTime = Time.time + 10f; return true; } return false; } private static void TryEnableAdapter(bool logMissing) { //IL_007e: 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_00a0: Expected O, but got Unknown //IL_00a0: Expected O, but got Unknown //IL_017f: Unknown result type (might be due to invalid IL or missing references) //IL_018d: Expected O, but got Unknown //IL_02b2: Unknown result type (might be due to invalid IL or missing references) //IL_02bf: Expected O, but got Unknown //IL_03ea: Unknown result type (might be due to invalid IL or missing references) //IL_03ef: Unknown result type (might be due to invalid IL or missing references) //IL_03f8: Expected O, but got Unknown Assembly assembly = FindTargetPortalAssembly(); if (assembly == null) { _targetPortalFound = false; _enabled = false; _lastPatchedCount = 0; if (ShouldLogRetry(logMissing)) { Plugin.Log.LogInfo((object)"[FCW TargetPortal] TargetPortal not loaded yet, retrying..."); } return; } _targetPortalFound = true; if (_enabled || _harmony == null) { return; } int num = 0; foreach (MethodInfo item in FindHookMethods(assembly)) { try { _harmony.Patch((MethodBase)item, new HarmonyMethod(typeof(TargetPortalFehuAdapter), "TargetPortalBoolAccessPrefix", (Type[])null), new HarmonyMethod(typeof(TargetPortalFehuAdapter), "TargetPortalBoolAccessPostfix", (Type[])null), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null); num++; LogOnce("patched:" + item.DeclaringType?.FullName + "." + item.Name, "[FCW TargetPortal] Patched real TargetPortal method: " + item.DeclaringType?.FullName + "." + item.Name); } catch (Exception ex) { LogOnce("patchfail:" + item.Name, "[FCW TargetPortal] Patch failed for " + item.Name + ": " + ex.Message); } } num += PatchTargetPortalMapAccess(assembly); foreach (MethodInfo item2 in FindToggleModeMethods(assembly)) { try { _harmony.Patch((MethodBase)item2, new HarmonyMethod(typeof(TargetPortalFehuAdapter), "TargetPortalToggleModePrefix", (Type[])null), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null); num++; LogOnce("patched-toggle:" + item2.DeclaringType?.FullName + "." + item2.Name, "[FCW TargetPortal] Patched real TargetPortal method: " + item2.DeclaringType?.FullName + "." + item2.Name); } catch (Exception ex2) { LogOnce("patchtogglefail:" + item2.DeclaringType?.FullName + "." + item2.Name, "[FCW TargetPortal] Mode toggle patch failed for " + item2.DeclaringType?.FullName + "." + item2.Name + ": " + ex2.Message); } } foreach (MethodInfo item3 in FindHoverTextMethods(assembly)) { try { _harmony.Patch((MethodBase)item3, (HarmonyMethod)null, new HarmonyMethod(typeof(TargetPortalFehuAdapter), "TargetPortalHoverTextPostfix", (Type[])null), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null); num++; LogOnce("patched-hover:" + item3.DeclaringType?.FullName + "." + item3.Name, "[FCW TargetPortal] Patched real TargetPortal method: " + item3.DeclaringType?.FullName + "." + item3.Name); } catch (Exception ex3) { LogOnce("patchhoverfail:" + item3.DeclaringType?.FullName + "." + item3.Name, "[FCW TargetPortal] Hover patch failed for " + item3.DeclaringType?.FullName + "." + item3.Name + ": " + ex3.Message); } } try { MethodInfo methodInfo = AccessTools.Method(typeof(TeleportWorld), "GetHoverText", Type.EmptyTypes, (Type[])null); if (methodInfo != null) { HarmonyMethod val = new HarmonyMethod(typeof(TargetPortalFehuAdapter), "TargetPortalFinalHoverPostfix", (Type[])null) { priority = 0 }; _harmony.Patch((MethodBase)methodInfo, (HarmonyMethod)null, val, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null); num++; LogOnce("patched-final-hover", "[FCW TargetPortal] Patched final TeleportWorld.GetHoverText display path after TargetPortal."); } } catch (Exception ex4) { LogOnce("patch-final-hover-fail", "[FCW TargetPortal] Final hover display patch failed: " + ex4.Message); } num = (_lastPatchedCount = num + PatchPortalProtectionMethods()); if (num <= 0) { LogOnce("nohooks", "[FCW TargetPortal] TargetPortal found, but no safe boolean access hook was found. Adapter backend disabled."); return; } _enabled = true; LogOnce("enabled", "[FCW TargetPortal] Fehu portal adapter enabled. Assembly=" + assembly.GetName().Name + " hooks=" + num); } private static int PatchPortalProtectionMethods() { //IL_0038: Unknown result type (might be due to invalid IL or missing references) //IL_0046: Expected O, but got Unknown //IL_0090: Unknown result type (might be due to invalid IL or missing references) //IL_009e: Expected O, but got Unknown //IL_00e8: Unknown result type (might be due to invalid IL or missing references) //IL_00f6: Expected O, but got Unknown //IL_0143: Unknown result type (might be due to invalid IL or missing references) //IL_0151: Expected O, but got Unknown //IL_019e: Unknown result type (might be due to invalid IL or missing references) //IL_01ac: Expected O, but got Unknown int num = 0; try { MethodInfo methodInfo = AccessTools.Method(typeof(WearNTear), "Damage", (Type[])null, (Type[])null); if (methodInfo != null) { _harmony.Patch((MethodBase)methodInfo, new HarmonyMethod(typeof(TargetPortalFehuAdapter), "TargetPortalDamagePrefix", (Type[])null), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null); num++; LogOnce("patched-portal-damage", "[FCW TargetPortal] Patched local portal damage protection path."); } MethodInfo methodInfo2 = AccessTools.Method(typeof(WearNTear), "ApplyDamage", (Type[])null, (Type[])null); if (methodInfo2 != null) { _harmony.Patch((MethodBase)methodInfo2, new HarmonyMethod(typeof(TargetPortalFehuAdapter), "TargetPortalDamagePrefix", (Type[])null), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null); num++; LogOnce("patched-portal-apply-damage", "[FCW TargetPortal] Patched portal ApplyDamage protection path."); } MethodInfo methodInfo3 = AccessTools.Method(typeof(WearNTear), "RPC_Damage", (Type[])null, (Type[])null); if (methodInfo3 != null) { _harmony.Patch((MethodBase)methodInfo3, new HarmonyMethod(typeof(TargetPortalFehuAdapter), "TargetPortalRpcDamagePrefix", (Type[])null), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null); num++; LogOnce("patched-portal-rpc-damage", "[FCW TargetPortal] Patched server portal RPC_Damage protection path."); } MethodInfo methodInfo4 = AccessTools.Method(typeof(WearNTear), "Remove", (Type[])null, (Type[])null); if (methodInfo4 != null) { _harmony.Patch((MethodBase)methodInfo4, new HarmonyMethod(typeof(TargetPortalFehuAdapter), "TargetPortalRemovePrefix", (Type[])null), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null); num++; LogOnce("patched-portal-remove", "[FCW TargetPortal] Patched portal remove protection path."); } MethodInfo methodInfo5 = AccessTools.Method(typeof(WearNTear), "RPC_Remove", (Type[])null, (Type[])null); if (methodInfo5 != null) { _harmony.Patch((MethodBase)methodInfo5, new HarmonyMethod(typeof(TargetPortalFehuAdapter), "TargetPortalRpcRemovePrefix", (Type[])null), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null); num++; LogOnce("patched-portal-rpc-remove", "[FCW TargetPortal] Patched portal RPC_Remove protection path."); } } catch (Exception ex) { LogOnce("patch-portal-protection-fail", "[FCW TargetPortal] Portal damage/remove protection patch failed: " + ex.Message); } return num; } private static int PatchTargetPortalMapAccess(Assembly assembly) { //IL_016d: Unknown result type (might be due to invalid IL or missing references) //IL_0172: Unknown result type (might be due to invalid IL or missing references) //IL_0184: Expected O, but got Unknown //IL_01c4: Unknown result type (might be due to invalid IL or missing references) //IL_01c9: Unknown result type (might be due to invalid IL or missing references) //IL_01dd: Expected O, but got Unknown int num = 0; try { _targetPortalType = assembly.GetType("TargetPortal.TargetPortal", throwOnError: false); _targetPortalMapType = assembly.GetType("TargetPortal.Map", throwOnError: false); if (_targetPortalMapType == null) { LogOnce("targetportal-map-type-missing", "[FCW TargetPortal] TargetPortal.Map type was not found; clan map restore is disabled."); return 0; } _targetPortalKnownPortalsField = ((_targetPortalType != null) ? AccessTools.Field(_targetPortalType, "knownPortals") : null); _targetPortalActivePinsField = AccessTools.Field(_targetPortalMapType, "activePins"); Type type = assembly.GetType("TargetPortal.Map+AddMinimapPortalIcon", throwOnError: false); _targetPortalMapPinTypeField = ((type != null) ? AccessTools.Field(type, "pinType") : null); if (_targetPortalActivePinsField == null) { string text = string.Join(", ", (from field in _targetPortalMapType.GetFields(BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic) select field.Name).ToArray()); LogOnce("targetportal-active-pins-missing", "[FCW TargetPortal] TargetPortal.Map.activePins field was not found. Available fields: " + text); return 0; } if (_targetPortalMapPinTypeField == null) { LogOnce("targetportal-pin-type-missing", "[FCW TargetPortal] TargetPortal.Map+AddMinimapPortalIcon.pinType was not found; clan map restore is disabled."); return 0; } Type targetPortalMapType = _targetPortalMapType; MethodInfo methodInfo = AccessTools.Method(targetPortalMapType, "AddPortalPins", (Type[])null, (Type[])null); if (methodInfo != null) { _harmony.Patch((MethodBase)methodInfo, (HarmonyMethod)null, (HarmonyMethod)null, new HarmonyMethod(typeof(TargetPortalFehuAdapter), "TargetPortalMapAddPortalPinsTranspiler", (Type[])null) { priority = 800 }, (HarmonyMethod)null, (HarmonyMethod)null); num++; LogOnce("patched-map-add-pins", "[FCW TargetPortal] Transpiled TargetPortal.Map.AddPortalPins native Guild visibility gate for FCW Clan portals."); } MethodInfo methodInfo2 = AccessTools.Method(targetPortalMapType, "HandlePortalClick", (Type[])null, (Type[])null); if (methodInfo2 != null) { _harmony.Patch((MethodBase)methodInfo2, new HarmonyMethod(typeof(TargetPortalFehuAdapter), "TargetPortalMapHandlePortalClickPrefix", (Type[])null) { priority = 800 }, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null); num++; LogOnce("patched-map-handle-click", "[FCW TargetPortal] Patched TargetPortal.Map.HandlePortalClick destination access check."); } } catch (Exception ex) { LogOnce("patch-map-access-fail:" + ex.GetType().Name, "[FCW TargetPortal] Map visibility/access patch failed: " + ex.Message); } return num; } public static IEnumerable TargetPortalMapAddPortalPinsTranspiler(IEnumerable instructions, ILGenerator generator) { //IL_0246: Unknown result type (might be due to invalid IL or missing references) //IL_0250: Expected O, but got Unknown //IL_025d: Unknown result type (might be due to invalid IL or missing references) //IL_0267: Expected O, but got Unknown //IL_028f: Unknown result type (might be due to invalid IL or missing references) //IL_0299: Expected O, but got Unknown //IL_02a6: Unknown result type (might be due to invalid IL or missing references) //IL_02b0: Expected O, but got Unknown //IL_02bd: Unknown result type (might be due to invalid IL or missing references) //IL_02c7: Expected O, but got Unknown List list = instructions.ToList(); object obj = null; for (int i = 1; i < list.Count; i++) { if (IsLdloc(list[i - 1]) && list[i].opcode == OpCodes.Ldstr && object.Equals(list[i].operand, "TargetPortal PortalOwnerId")) { obj = list[i - 1].operand; break; } } if (obj == null) { LogOnce("map-add-pins-transpiler-no-portal-local", "[FCW TargetPortal] AddPortalPins transpiler could not find the current portal local."); return list; } for (int j = 0; j < list.Count - 4; j++) { if (!IsLdloc(list[j]) || !IsLdcI4(list[j + 1], 4) || !IsBranch(list[j + 2].opcode) || !(list[j + 3].operand is MethodInfo methodInfo) || methodInfo.DeclaringType?.FullName != "Guilds.API" || methodInfo.Name != "GetOwnGuild") { continue; } Label label = (Label)list[j + 2].operand; int num = -1; for (int k = j + 3; k < list.Count - 1; k++) { if (list[k].opcode == OpCodes.Callvirt && list[k].operand is MethodInfo { Name: "ContainsKey" } methodInfo2) { Type? declaringType = methodInfo2.DeclaringType; if ((object)declaringType != null && declaringType.FullName.StartsWith("System.Collections.Generic.Dictionary`2[[Guilds.PlayerReference,", StringComparison.Ordinal)) { num = k; break; } } } if (num < 0 || !IsBranch(list[num + 1].opcode)) { LogOnce("map-add-pins-transpiler-no-guild-contains", "[FCW TargetPortal] AddPortalPins transpiler could not find the native Guild ContainsKey decision."); return list; } Label label2 = generator.DefineLabel(); list[num + 2].labels.Add(label2); Label label3 = generator.DefineLabel(); list[j + 3].labels.Add(label3); List collection = new List { LoadLocal(obj), new CodeInstruction(OpCodes.Call, (object)AccessTools.Method(typeof(TargetPortalFehuAdapter), "IsFcwClanPortal", (Type[])null, (Type[])null)), new CodeInstruction(OpCodes.Brfalse_S, (object)label3), LoadLocal(obj), new CodeInstruction(OpCodes.Call, (object)AccessTools.Method(typeof(TargetPortalFehuAdapter), "AllowFcwClanPortalInDestinationList", (Type[])null, (Type[])null)), new CodeInstruction(OpCodes.Brtrue, (object)label2), new CodeInstruction(OpCodes.Br, (object)label) }; list.InsertRange(j + 3, collection); LogOnce("map-add-pins-transpiler-ok", "[FCW TargetPortal] AddPortalPins transpiler inserted FCW Clan branch at native Guild visibility decision."); return list; } LogOnce("map-add-pins-transpiler-no-guild-branch", "[FCW TargetPortal] AddPortalPins transpiler could not find the native Guild mode branch."); return list; } private static bool IsFcwClanPortal(ZDO portal) { if (portal != null) { return string.Equals(portal.GetString("FCW_TargetPortalMode", ""), "Clan", StringComparison.OrdinalIgnoreCase); } return false; } private static bool AllowFcwClanPortalInDestinationList(ZDO portal) { string reason; return CheckFcwClanDestinationAccess(portal, "DESTINATION", out reason); } private static bool CheckFcwClanDestinationAccess(ZDO portal, string action, out string reason) { //IL_0007: Unknown result type (might be due to invalid IL or missing references) //IL_0012: Expected O, but got Unknown Player localPlayer = Player.m_localPlayer; long num = (((Object)localPlayer != (Object)null) ? localPlayer.GetPlayerID() : 0); int num2 = ((num == 0L) ? null : WarfareManager.Instance?.Data?.GetPlayer(num))?.ClanId ?? 0; int num3 = ((portal != null) ? portal.GetInt("FCW_TargetPortalClanId", 0) : 0); bool flag; if (portal == null) { reason = "portal missing"; flag = false; } else if (!IsFcwClanPortal(portal)) { reason = "not FCW Clan"; flag = false; } else if (IsAdminAccess(num)) { reason = "admin"; flag = true; } else if (num3 <= 0) { reason = "assigned clan missing"; flag = false; } else if (num2 != 0 && num2 == num3) { reason = "matching clan"; flag = true; } else { reason = ((num2 == 0) ? "freeborn/no clan" : "different clan"); flag = false; } LogDestinationCheck(action, portal, num2, num3, flag, reason); return flag; } private static void LogDestinationCheck(string action, ZDO portal, int playerClanId, int assignedClanId, bool allowed, string reason) { string text = ModeName(ReadTargetPortalModeValue(portal)); string text2 = ((portal != null) ? portal.GetString("FCW_TargetPortalMode", "") : null) ?? ""; Plugin.Log.LogInfo((object)(action + " CHECK mode=" + text + " fcwMode=" + text2 + " playerClanId=" + playerClanId + " assignedClanId=" + assignedClanId + " allowed=" + allowed.ToString().ToLowerInvariant() + " reason=" + reason)); } private static bool IsLdloc(CodeInstruction instruction) { if (!(instruction.opcode == OpCodes.Ldloc) && !(instruction.opcode == OpCodes.Ldloc_S) && !(instruction.opcode == OpCodes.Ldloc_0) && !(instruction.opcode == OpCodes.Ldloc_1) && !(instruction.opcode == OpCodes.Ldloc_2)) { return instruction.opcode == OpCodes.Ldloc_3; } return true; } private static bool IsLdcI4(CodeInstruction instruction, int value) { if (instruction.opcode == OpCodes.Ldc_I4 && instruction.operand is int num) { return num == value; } if (instruction.opcode == OpCodes.Ldc_I4_S && instruction.operand is sbyte b) { return b == value; } if ((value != 0 || !(instruction.opcode == OpCodes.Ldc_I4_0)) && (value != 1 || !(instruction.opcode == OpCodes.Ldc_I4_1)) && (value != 2 || !(instruction.opcode == OpCodes.Ldc_I4_2)) && (value != 3 || !(instruction.opcode == OpCodes.Ldc_I4_3))) { if (value == 4) { return instruction.opcode == OpCodes.Ldc_I4_4; } return false; } return true; } private static bool IsBranch(OpCode opcode) { if (opcode.FlowControl != FlowControl.Branch) { return opcode.FlowControl == FlowControl.Cond_Branch; } return true; } private static CodeInstruction LoadLocal(object operand) { //IL_0015: Unknown result type (might be due to invalid IL or missing references) //IL_001b: Expected O, but got Unknown //IL_0009: Unknown result type (might be due to invalid IL or missing references) //IL_000f: Expected O, but got Unknown if (operand == null) { return new CodeInstruction(OpCodes.Ldloc_0, (object)null); } return new CodeInstruction(OpCodes.Ldloc_S, operand); } public static bool TargetPortalMapHandlePortalClickPrefix(object[] __args, ref bool __result) { //IL_005e: Unknown result type (might be due to invalid IL or missing references) //IL_0069: Expected O, but got Unknown try { Delegate obj = __args?.OfType().FirstOrDefault(); if ((object)obj == null) { return true; } object[] array = new object[2]; object obj2 = obj.DynamicInvoke(array); if (obj2 is bool && (bool)obj2) { object obj3 = array[1]; ZDO val = (ZDO)((obj3 is ZDO) ? obj3 : null); if (val != null) { Player localPlayer = Player.m_localPlayer; long playerId = (((Object)localPlayer != (Object)null) ? localPlayer.GetPlayerID() : 0); PlayerClanInfo player = WarfareManager.Instance?.Data?.GetPlayer(playerId); if (IsFcwClanPortal(val)) { if (CheckFcwClanDestinationAccess(val, "CLICK", out var _)) { return true; } WarfareHUD.ShowLeftNotification("You do not have permission for this portal."); __result = false; return false; } string reason2; bool flag = HasPortalAccess(val, playerId, player, out reason2); LogPortalDecision("USE", val, flag, reason2); if (flag) { return true; } WarfareHUD.ShowLeftNotification("Portal access denied: " + reason2 + "."); __result = false; return false; } } return true; } catch (Exception ex) { LogOnce("map-handle-click-prefix:" + ex.GetType().Name, "[FCW TargetPortal] Destination access check failed: " + ex.Message); return true; } } public static string AdapterStatusText() { RefreshIfNeeded(); if (!_targetPortalFound) { return "TargetPortal not detected yet"; } if (_enabled) { return "Active"; } if (_lastPatchedCount <= 0) { return "Detected / backend only"; } return "Detected / hook pending"; } public static bool TargetPortalBoolAccessPrefix(object __instance, object[] __args, MethodBase __originalMethod, ref bool __result) { if (!_enabled) { return true; } try { PortalAccessData portalAccessData = GetPortalAccessData(__instance, __args); if (portalAccessData.AccessType == "None") { return true; } bool flag = FehuAllowsPortalAccess(portalAccessData); LogPortalAccessCheck("TargetPortal." + (__originalMethod?.Name ?? "unknown"), portalAccessData, flag, flag ? "matching clan/admin" : "clan mismatch/freeborn/no clan"); if (!flag) { WarfareHUD.ShowLeftNotification("Portal access denied: " + DenialLabel(portalAccessData.AccessType) + "."); } __result = flag; return false; } catch (Exception ex) { LogOnce("access-prefix-ex:" + ex.GetType().Name, "[FCW TargetPortal] Fehu pre-access check failed: " + ex.Message); return true; } } public static void TargetPortalModeChangePrefix(object __instance, object[] __args, MethodBase __originalMethod) { //IL_0011: Unknown result type (might be due to invalid IL or missing references) try { ZDO val = FindPortalZdo(__instance, __args); if (val != null) { PreviousPortalModes[val.m_uid] = ReadTargetPortalModeValue(val); } } catch (Exception ex) { LogOnce("modechange-prefix-ex:" + ex.GetType().Name, "[FCW TargetPortal] Mode-change prefix failed: " + ex.Message); } } public static void TargetPortalModeChangePostfix(object __instance, object[] __args, MethodBase __originalMethod) { try { ZDO val = FindPortalZdo(__instance, __args); if (val != null) { int num = ReadTargetPortalModeValue(val); if (ReadPortalAccessData(val, __instance, __args).AccessType == "Clan" && num != 4) { DeactivateClanModeForCycle(val); LogPortalCycle("Clan", fcwClan: true, ModeName(num)); } } } catch (Exception ex) { LogOnce("modechange-postfix-ex:" + ex.GetType().Name, "[FCW TargetPortal] Mode-change postfix failed: " + ex.Message); } } public static void TargetPortalBoolAccessPostfix(object __instance, object[] __args, MethodBase __originalMethod, ref bool __result) { if (!_enabled) { return; } try { PortalAccessData portalAccessData = GetPortalAccessData(__instance, __args); if (!(portalAccessData.AccessType == "None")) { bool flag = FehuAllowsPortalAccess(portalAccessData); LogPortalAccessCheck("TargetPortal." + (__originalMethod?.Name ?? "unknown") + ".post", portalAccessData, flag, flag ? "matching clan/admin" : "clan mismatch/freeborn/no clan"); if (!flag) { __result = false; } else { __result = true; } } } catch (Exception ex) { LogOnce("access-ex:" + ex.GetType().Name, "[FCW TargetPortal] Fehu access check failed: " + ex.Message); } } public static bool TargetPortalDamagePrefix(WearNTear __instance) { //IL_0022: Unknown result type (might be due to invalid IL or missing references) //IL_002d: Expected O, but got Unknown if ((Object)(object)ZNet.instance != (Object)null && ZNet.instance.IsServer()) { return true; } Player localPlayer = Player.m_localPlayer; long playerId = (((Object)localPlayer != (Object)null) ? localPlayer.GetPlayerID() : 0); return !ShouldBlockPortalAction(__instance, playerId, showMessage: true, serverAuthoritative: false); } public static bool TargetPortalRpcDamagePrefix(WearNTear __instance, long sender, HitData hit) { long playerId = ResolvePortalActorId(sender, hit); return !ShouldBlockPortalAction(__instance, playerId, showMessage: false, serverAuthoritative: true); } public static bool TargetPortalRemovePrefix(WearNTear __instance) { //IL_0022: Unknown result type (might be due to invalid IL or missing references) //IL_002d: Expected O, but got Unknown if ((Object)(object)ZNet.instance != (Object)null && ZNet.instance.IsServer()) { return true; } Player localPlayer = Player.m_localPlayer; long playerId = (((Object)localPlayer != (Object)null) ? localPlayer.GetPlayerID() : 0); return !ShouldBlockPortalAction(__instance, playerId, showMessage: true, serverAuthoritative: false); } public static bool TargetPortalRpcRemovePrefix(WearNTear __instance, long sender) { long playerId = ResolvePortalActorId(sender, null); return !ShouldBlockPortalAction(__instance, playerId, showMessage: false, serverAuthoritative: true); } private static long ResolvePortalActorId(long sender, HitData hit) { //IL_0051: Unknown result type (might be due to invalid IL or missing references) //IL_005c: Expected O, but got Unknown //IL_0021: Unknown result type (might be due to invalid IL or missing references) //IL_002c: Expected O, but got Unknown //IL_00a2: Unknown result type (might be due to invalid IL or missing references) //IL_00a8: Unknown result type (might be due to invalid IL or missing references) //IL_00f3: Unknown result type (might be due to invalid IL or missing references) try { if (sender == 0L && (Object)(object)ZNet.instance != (Object)null && ZNet.instance.IsServer() && (Object)Player.m_localPlayer != (Object)null) { return Player.m_localPlayer.GetPlayerID(); } Character obj = ((hit != null) ? hit.GetAttacker() : null); Player val = (Player)(object)((obj is Player) ? obj : null); if ((Object)val != (Object)null) { return val.GetPlayerID(); } ZNet instance = ZNet.instance; ZNetPeer val2 = ((instance != null) ? instance.GetPeer(sender) : null); Player[] array = Object.FindObjectsOfType(); foreach (Player val3 in array) { ZDO val4 = TryGetZdo(val3); if (val4 != null) { if (val2 != null && val4.m_uid == val2.m_characterID) { return val3.GetPlayerID(); } if (GetZdoUserId(val4) == sender) { return val3.GetPlayerID(); } } } ZDO obj2 = ((val2 != null && ZDOMan.instance != null) ? ZDOMan.instance.GetZDO(val2.m_characterID) : null); long num = ((obj2 != null) ? obj2.GetLong(ZDOVars.s_playerID, 0L) : 0); if (num != 0L) { return num; } } catch { } return 0L; } private static long GetZdoUserId(ZDO zdo) { //IL_000f: Unknown result type (might be due to invalid IL or missing references) try { object obj = zdo?.m_uid; if (obj == null) { return 0L; } FieldInfo fieldInfo = obj.GetType().GetField("userID", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic) ?? obj.GetType().GetField("m_userID", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); return (fieldInfo != null) ? Convert.ToInt64(fieldInfo.GetValue(obj)) : 0; } catch { return 0L; } } private unsafe static bool ShouldBlockPortalAction(WearNTear wear, long playerId, bool showMessage, bool serverAuthoritative) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) //IL_000c: Expected O, but got Unknown //IL_0023: Unknown result type (might be due to invalid IL or missing references) //IL_002e: Expected O, but got Unknown //IL_007b: Unknown result type (might be due to invalid IL or missing references) //IL_0080: Unknown result type (might be due to invalid IL or missing references) //IL_00f0: Unknown result type (might be due to invalid IL or missing references) //IL_00fb: Expected O, but got Unknown try { if ((Object)wear == (Object)null) { return false; } TeleportWorld component = ((Component)wear).GetComponent(); ZDO val = TryGetZdo(component); if ((Object)component == (Object)null || val == null || !IsTargetPortalPortal(val)) { return false; } Player localPlayer = Player.m_localPlayer; if (playerId == 0L) { if (WasPortalDestroyChainAllowed(val)) { return false; } ManualLogSource log = Plugin.Log; string obj = (serverAuthoritative ? "server" : "client"); ZDOID uid = val.m_uid; log.LogWarning((object)("[FCW TargetPortal] Blocked unresolved " + obj + " portal action portal=" + ((object)(*(ZDOID*)(&uid))/*cast due to .constrained prefix*/).ToString())); return true; } SyncOwnerMetadata(val, localPlayer, playerId); PlayerClanInfo player = WarfareManager.Instance?.Data?.GetPlayer(playerId); if (HasPortalDestroyAccess(val, playerId, player, out var reason)) { LogPortalDecision("DESTROY", val, allowed: true, reason); RememberAllowedPortalDestroyChain(val); return false; } if (showMessage && (Object)localPlayer != (Object)null && localPlayer.GetPlayerID() == playerId) { WarfareHUD.ShowLeftNotification("You do not have permission for this portal."); } LogPortalDecision("DESTROY", val, allowed: false, reason); return true; } catch (Exception ex) { LogOnce("portal-action-prefix-ex:" + ex.GetType().Name, "[FCW TargetPortal] Portal damage/remove permission check failed: " + ex.Message); return false; } } private static void RememberAllowedPortalDestroyChain(ZDO zdo) { //IL_0009: Unknown result type (might be due to invalid IL or missing references) if (zdo != null) { AllowedPortalDestroyChain[zdo.m_uid] = Time.time + 30f; } } private static bool WasPortalDestroyChainAllowed(ZDO zdo) { //IL_000b: 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) if (zdo == null) { return false; } if (!AllowedPortalDestroyChain.TryGetValue(zdo.m_uid, out var value)) { return false; } if (Time.time <= value) { return true; } AllowedPortalDestroyChain.Remove(zdo.m_uid); return false; } public static void TargetPortalPortalListPrefix(object[] __args, MethodBase __originalMethod) { if (!_enabled || __args == null) { return; } for (int i = 0; i < __args.Length; i++) { if (__args[i] is IList portals) { FilterPortalListInPlace(portals, "TargetPortal." + (__originalMethod?.Name ?? "unknown") + ".args"); } } } public static void TargetPortalPortalListPostfix(MethodBase __originalMethod, ref List __result) { if (_enabled && __result != null) { FilterPortalListInPlace(__result, "TargetPortal." + (__originalMethod?.Name ?? "unknown") + ".result"); } } public static void TargetPortalPortalEnumerablePostfix(MethodBase __originalMethod, ref IEnumerable __result) { if (_enabled && __result != null) { List list = __result.ToList(); FilterPortalListInPlace(list, "TargetPortal." + (__originalMethod?.Name ?? "unknown") + ".result"); __result = list; } } public unsafe static bool TargetPortalToggleModePrefix(object __instance, object[] __args, MethodBase __originalMethod, ref bool __result) { //IL_0070: Unknown result type (might be due to invalid IL or missing references) //IL_007b: Expected O, but got Unknown //IL_008f: Unknown result type (might be due to invalid IL or missing references) //IL_009a: Expected O, but got Unknown //IL_0153: Unknown result type (might be due to invalid IL or missing references) //IL_0158: Unknown result type (might be due to invalid IL or missing references) try { if ((__args != null && __args.OfType().Any((bool value) => value)) || !IsShiftDown()) { TryInitializePortalCreator(__instance, __args); return true; } DumpToggleDetailsOnce(__instance, __args, __originalMethod); TeleportWorld val = FindTeleportWorldArg(__args); ZDO val2 = TryGetZdo(val) ?? FindPortalZdo(__instance, __args); if ((Object)val == (Object)null || val2 == null) { return true; } Player localPlayer = Player.m_localPlayer; if ((Object)localPlayer == (Object)null) { return true; } long playerID = localPlayer.GetPlayerID(); PlayerClanInfo playerClanInfo = WarfareManager.Instance?.Data?.GetPlayer(playerID); Clan clan = ((playerClanInfo == null || playerClanInfo.ClanId == 0) ? null : WarfareManager.Instance?.Data?.GetClan(playerClanInfo.ClanId)); EnsurePortalCreatorMetadata(val2, localPlayer, playerID); if (!CanChangeTargetPortalMode(val2, playerID, playerClanInfo, out var reason)) { ManualLogSource log = Plugin.Log; string[] obj = new string[8] { "[FCW TargetPortal] Toggle denied player=", playerID.ToString(), "/", localPlayer.GetPlayerName(), " portal=", null, null, null }; ZDOID uid = val2.m_uid; obj[5] = ((object)(*(ZDOID*)(&uid))/*cast due to .constrained prefix*/).ToString(); obj[6] = " reason="; obj[7] = reason; log.LogWarning((object)string.Concat(obj)); WarfareHUD.ShowLeftNotification("You do not have permission to change this portal."); __result = false; return false; } List list = BuildPortalCycle(val2, playerClanInfo, clan, playerID); PortalAccessData portalAccessData = ReadPortalAccessData(val2, val, null); string oldMode = ((portalAccessData.AccessType == "Clan") ? "Clan" : ModeName(ReadTargetPortalModeValue(val2))); int num = list.FindIndex((string mode) => string.Equals(mode, oldMode, StringComparison.OrdinalIgnoreCase)); if (num < 0) { num = 0; oldMode = list[0]; } string text = list[(num + 1) % list.Count]; Plugin.Log.LogInfo((object)("[FCW PortalModes] CYCLE old=" + oldMode + " next=" + text + " index=" + num + " list=" + string.Join(" -> ", list.ToArray()))); if (string.Equals(text, "Clan", StringComparison.OrdinalIgnoreCase)) { SetFehuClanSnapshot(val2, playerClanInfo, clan, localPlayer); WarfareHUD.ShowLeftNotification("Portal mode: Clan - " + clan.Name + " [" + clan.Tag + "]"); } else { DeactivateClanModeForCycle(val2); SetNormalTargetPortalMode(val2, ModeValue(text), playerID, localPlayer); } LogPortalModeChanged(oldMode, text, string.Equals(text, "Clan", StringComparison.OrdinalIgnoreCase) ? playerClanInfo.ClanId : 0, string.Equals(text, "Clan", StringComparison.OrdinalIgnoreCase) ? clan.Name : ""); __result = false; return false; } catch (Exception ex) { LogOnce("toggle-prefix-ex:" + ex.GetType().Name, "[FCW TargetPortal] Clan mode toggle failed: " + ex.Message); return true; } } public static void TargetPortalToggleModePostfix(object __instance, object[] __args, MethodBase __originalMethod) { try { ZDO val = FindPortalZdo(__instance, __args); if (val == null) { val = TryGetZdo(FindCurrentPortal()); } if (val != null) { int num = ReadTargetPortalModeValue(val); if (num != 4 && ReadPortalAccessData(val, __instance, __args).AccessType == "Clan") { DeactivateClanModeForCycle(val); LogPortalCycle("Clan", fcwClan: true, ModeName(num)); } } } catch (Exception ex) { LogOnce("toggle-postfix-ex:" + ex.GetType().Name, "[FCW TargetPortal] Clan mode toggle postfix failed: " + ex.Message); } } public static void TargetPortalTracePrefix(object __instance, object[] __args, MethodBase __originalMethod) { //IL_0022: Unknown result type (might be due to invalid IL or missing references) //IL_002d: Expected O, but got Unknown try { ZDO val = FindPortalZdo(__instance, __args) ?? TryGetZdo(FindCurrentPortal()); Player localPlayer = Player.m_localPlayer; long num = (((Object)localPlayer != (Object)null) ? localPlayer.GetPlayerID() : 0); PlayerClanInfo playerClanInfo = ((num == 0L) ? null : WarfareManager.Instance?.Data?.GetPlayer(num)); Clan clan = ((playerClanInfo == null || playerClanInfo.ClanId == 0) ? null : WarfareManager.Instance?.Data?.GetClan(playerClanInfo.ClanId)); Plugin.Log.LogInfo((object)("FCW TargetPortal TRACE: method=" + (__originalMethod?.DeclaringType?.FullName ?? "unknown") + "." + (__originalMethod?.Name ?? "unknown") + " currentMode=" + ReadTargetPortalMode(val, "") + " ownerName=" + ((val != null) ? val.GetString("Target Portal PortalOwnerName", "") : null) + " clanId=" + (playerClanInfo?.ClanId ?? 0) + " clanName=" + clan?.Name)); } catch (Exception ex) { LogOnce("trace-ex:" + ex.GetType().Name, "[FCW TargetPortal] Trace failed: " + ex.Message); } } private static void DumpToggleDetailsOnce(object instance, object[] args, MethodBase method) { if (_dumpedToggleDetails) { return; } _dumpedToggleDetails = true; try { Plugin.Log.LogInfo((object)("FCW TargetPortal TOGGLE INSPECT: method=" + (method?.DeclaringType?.FullName ?? "unknown") + "." + (method?.Name ?? "unknown"))); ParameterInfo[] array = method?.GetParameters() ?? Array.Empty(); for (int i = 0; i < array.Length; i++) { object value = ((args != null && i < args.Length) ? args[i] : null); Plugin.Log.LogInfo((object)("FCW TargetPortal TOGGLE PARAM: name=" + array[i].Name + " type=" + array[i].ParameterType.FullName + " value=" + FormatDebugValue(value))); } if (instance == null) { Plugin.Log.LogInfo((object)"FCW TargetPortal TOGGLE INSTANCE: null"); return; } Type type = instance.GetType(); Plugin.Log.LogInfo((object)("FCW TargetPortal TOGGLE INSTANCE: type=" + type.FullName)); FieldInfo[] fields = type.GetFields(BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic); foreach (FieldInfo fieldInfo in fields) { object obj = null; try { obj = fieldInfo.GetValue(fieldInfo.IsStatic ? null : instance); } catch (Exception ex) { obj = ""; } Plugin.Log.LogInfo((object)("FCW TargetPortal TOGGLE FIELD: " + fieldInfo.Name + " type=" + fieldInfo.FieldType.FullName + " value=" + FormatDebugValue(obj))); } PropertyInfo[] properties = type.GetProperties(BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic); foreach (PropertyInfo propertyInfo in properties) { if (propertyInfo.GetIndexParameters().Length == 0) { object obj2 = null; try { MethodInfo getMethod = propertyInfo.GetGetMethod(nonPublic: true); obj2 = ((!(getMethod == null)) ? propertyInfo.GetValue(getMethod.IsStatic ? null : instance, null) : ""); } catch (Exception ex2) { obj2 = ""; } Plugin.Log.LogInfo((object)("FCW TargetPortal TOGGLE PROPERTY: " + propertyInfo.Name + " type=" + propertyInfo.PropertyType.FullName + " value=" + FormatDebugValue(obj2))); } } } catch (Exception ex3) { Plugin.Log.LogInfo((object)("[FCW TargetPortal] Toggle inspect failed: " + ex3.Message)); } } private unsafe static string FormatDebugValue(object value) { //IL_001a: Unknown result type (might be due to invalid IL or missing references) //IL_001f: Unknown result type (might be due to invalid IL or missing references) //IL_003f: Unknown result type (might be due to invalid IL or missing references) //IL_0044: Unknown result type (might be due to invalid IL or missing references) //IL_004a: Unknown result type (might be due to invalid IL or missing references) //IL_004b: Unknown result type (might be due to invalid IL or missing references) if (value == null) { return "null"; } try { ZDO val = (ZDO)((value is ZDO) ? value : null); if (val != null) { ZDOID uid = val.m_uid; return "ZDO " + ((object)(*(ZDOID*)(&uid))/*cast due to .constrained prefix*/).ToString(); } if (value is ZDOID val2) { ZDOID uid = val2; return "ZDOID " + ((object)(*(ZDOID*)(&uid))/*cast due to .constrained prefix*/).ToString(); } Component val3 = (Component)((value is Component) ? value : null); if (val3 != null) { return ((object)val3).GetType().FullName + " name=" + ((Object)val3).name; } return value.ToString(); } catch { return ""; } } public static void TargetPortalHoverTextPostfix(object __instance, object[] __args, MethodBase __originalMethod, ref string __result) { try { ZDO val = FindPortalZdo(__instance, __args) ?? TryGetZdo(FindCurrentPortal()); PortalAccessData portalAccessData = ReadPortalAccessData(val, __instance, __args); string text = __result ?? ""; if (!(portalAccessData.AccessType != "Clan")) { LogOnce("hover-raw:" + (((object)Unsafe.As(ref val?.m_uid)/*cast due to .constrained prefix*/).ToString() ?? "unknown"), "[FCW TargetPortal] Hover raw=" + text); string text2 = ReplaceClanHoverMode(text, ClanLabel(portalAccessData)); if (!string.Equals(text, text2, StringComparison.Ordinal)) { __result = text2; LogOnce("hover-replaced:" + (((object)Unsafe.As(ref val?.m_uid)/*cast due to .constrained prefix*/).ToString() ?? "unknown"), "[FCW TargetPortal] Hover replaced=" + text2); } } } catch (Exception ex) { LogOnce("hover-postfix-ex:" + ex.GetType().Name, "[FCW TargetPortal] Hover rewrite failed: " + ex.Message); } } public static void TargetPortalFinalHoverPostfix(TeleportWorld __instance, ref string __result) { try { ZDO val = TryGetZdo(__instance); PortalAccessData portalAccessData = ReadPortalAccessData(val, __instance, null); if (!(portalAccessData.AccessType != "Clan")) { string obj = __result ?? ""; string text = ReplaceClanHoverMode(obj, ClanLabel(portalAccessData)); if (!string.Equals(obj, text, StringComparison.Ordinal)) { __result = text; LogOnce("final-hover-replaced:" + (((object)Unsafe.As(ref val?.m_uid)/*cast due to .constrained prefix*/).ToString() ?? "unknown"), "[FCW TargetPortal] Final hover replaced=" + text); } } } catch (Exception ex) { LogOnce("final-hover-ex:" + ex.GetType().Name, "[FCW TargetPortal] Final hover display failed: " + ex.Message); } } private static string ReplaceClanHoverMode(string text, string clanName) { if (string.IsNullOrEmpty(text)) { return text ?? ""; } string text2 = "Clan: " + clanName; int num = text.IndexOf("Portal Tag:", StringComparison.OrdinalIgnoreCase); int num2 = ((num >= 0) ? text.IndexOf('[', num) : text.IndexOf('[')); int num3 = ((num2 >= 0) ? text.IndexOf(']', num2) : (-1)); if (num2 >= 0 && num3 > num2) { return text.Substring(0, num2 + 1) + text2 + text.Substring(num3); } return RewriteClanHoverLabel(text, clanName); } private static string RewriteClanHoverLabel(string text, string clanName) { if (string.IsNullOrEmpty(text)) { return text ?? ""; } string[] array = text.Replace("\r\n", "\n").Split(new char[1] { '\n' }); bool flag = false; for (int i = 0; i < array.Length; i++) { string text2 = array[i]; if (TryExtractClanOwnerName(text2, out var clanName2)) { string text3 = "Clan: " + ((!string.IsNullOrWhiteSpace(clanName2)) ? clanName2 : clanName); int num = text2.IndexOf("(Owner: Clan:", StringComparison.OrdinalIgnoreCase); int num2 = text2.LastIndexOf('[', num); int num3 = ((num >= 0) ? text2.IndexOf(']', num) : (-1)); if (num2 >= 0 && num3 > num2) { array[i] = text2.Substring(0, num2 + 1) + text3 + text2.Substring(num3); } else { array[i] = text3; } flag = true; } } if (!flag) { return text; } string text4 = string.Join("\n", array); if (!text.Contains("\r\n")) { return text4; } return text4.Replace("\n", "\r\n"); } private static bool TryExtractClanOwnerName(string line, out string clanName) { clanName = ""; if (string.IsNullOrEmpty(line)) { return false; } string text = "(Owner: Clan:"; int num = line.IndexOf(text, StringComparison.OrdinalIgnoreCase); if (num < 0) { return false; } int num2 = num + text.Length; int num3 = line.IndexOf(')', num2); if (num3 < 0) { num3 = line.Length; } clanName = line.Substring(num2, num3 - num2).Trim().TrimEnd(new char[1] { ']' }); return true; } public static void UpdateFallbackHotkey() { } public unsafe static string DebugSetCurrentPortalClanMode() { //IL_0016: Unknown result type (might be due to invalid IL or missing references) //IL_0021: Expected O, but got Unknown //IL_0033: Unknown result type (might be due to invalid IL or missing references) //IL_003e: Expected O, but got Unknown //IL_00f7: Unknown result type (might be due to invalid IL or missing references) //IL_00fc: Unknown result type (might be due to invalid IL or missing references) RefreshIfNeeded(); TeleportWorld obj = FindCurrentPortal(); ZDO val = TryGetZdo(obj); if ((Object)obj == (Object)null || val == null) { return "No nearby portal found."; } Player localPlayer = Player.m_localPlayer; if ((Object)localPlayer == (Object)null) { return "No local player."; } long playerID = localPlayer.GetPlayerID(); PlayerClanInfo playerClanInfo = WarfareManager.Instance?.Data?.GetPlayer(playerID); Clan clan = ((playerClanInfo == null || playerClanInfo.ClanId == 0) ? null : WarfareManager.Instance?.Data?.GetClan(playerClanInfo.ClanId)); if (playerClanInfo == null || playerClanInfo.ClanId == 0 || clan == null) { return "You must be in a clan to set Clan portal mode."; } SetFehuClanSnapshot(val, playerClanInfo, clan, localPlayer); string[] obj2 = new string[6] { "FCW TargetPortal: DEBUG COMMAND set Clan mode clanId=", playerClanInfo.ClanId.ToString(), " clanName=", clan.Name, " zdo=", null }; ZDOID uid = val.m_uid; obj2[5] = ((object)(*(ZDOID*)(&uid))/*cast due to .constrained prefix*/).ToString(); string text = string.Concat(obj2); Plugin.Log.LogInfo((object)text); WarfareHUD.ShowLeftNotification("Portal mode set to Clan: " + clan.Name); return text; } public unsafe static string DebugNearPortal() { //IL_001e: Unknown result type (might be due to invalid IL or missing references) //IL_0029: Expected O, but got Unknown //IL_0035: Unknown result type (might be due to invalid IL or missing references) //IL_0040: Expected O, but got Unknown //IL_00c7: Unknown result type (might be due to invalid IL or missing references) //IL_00cc: Unknown result type (might be due to invalid IL or missing references) RefreshIfNeeded(); TeleportWorld val = FindCurrentPortal(); ZDO val2 = TryGetZdo(val); Player localPlayer = Player.m_localPlayer; if ((Object)val == (Object)null || val2 == null) { return "FCW Portal Debug: no nearby portal found."; } if ((Object)localPlayer == (Object)null) { return "FCW Portal Debug: no local player."; } long playerID = localPlayer.GetPlayerID(); long playerSteamId = GetPlayerSteamId(localPlayer, playerID); PlayerClanInfo playerClanInfo = WarfareManager.Instance?.Data?.GetPlayer(playerID); PortalAccessData portalAccessData = ReadPortalAccessData(val2, val, null); string reason; bool flag = HasPortalAccess(val2, playerID, playerClanInfo, out reason); PortalAccessData access; string reason2; bool flag2 = CanCurrentPlayerSeePortal(val2, out access, out reason2); string reason3; bool flag3 = HasPortalManagementAccess(val2, playerID, playerClanInfo, out reason3); string reason4; bool flag4 = HasPortalDestroyAccess(val2, playerID, playerClanInfo, out reason4); string[] array = new string[48]; array[0] = "FCW Portal Debug\nPortal:\n- ZDO id="; ZDOID uid = val2.m_uid; array[1] = ((object)(*(ZDOID*)(&uid))/*cast due to .constrained prefix*/).ToString(); array[2] = "\n- native TargetPortal mode="; array[3] = ModeName(ReadTargetPortalModeValue(val2)); array[4] = "\n- FCW mode="; array[5] = val2.GetString("FCW_TargetPortalMode", ""); array[6] = "\n- displayed mode="; array[7] = ((portalAccessData.AccessType == "Clan") ? ("Clan: " + ClanLabel(portalAccessData)) : ModeName(ReadTargetPortalModeValue(val2))); array[8] = "\n- nativeOwnerId="; array[9] = ReadStoredTargetPortalOwnerId(val2).ToString(); array[10] = "\n- nativeOwnerName="; array[11] = ReadPortalOwnerName(val2, ""); array[12] = "\n- creatorOldId="; array[13] = val2.GetLong("FCW_TargetPortalCreatorId", 0L).ToString(); array[14] = "\n- creatorName="; array[15] = val2.GetString("FCW_TargetPortalCreatorName", ""); array[16] = "\n- assignedClanId="; array[17] = portalAccessData.ClanId.ToString(); array[18] = "\n- assignedClanName="; array[19] = portalAccessData.ClanName; array[20] = "\n- assignedClanTag="; array[21] = portalAccessData.ClanTag; array[22] = "\nPlayer:\n- playerName="; array[23] = localPlayer.GetPlayerName(); array[24] = "\n- playerId="; array[25] = playerID.ToString(); array[26] = "\n- playerSteam="; array[27] = playerSteamId.ToString(); array[28] = "\n- playerClanId="; array[29] = (playerClanInfo?.ClanId ?? 0).ToString(); array[30] = "\n- isAdmin="; array[31] = IsAdminAccess(playerID).ToString().ToLowerInvariant(); array[32] = "\nResults:\n- HasUseAccess="; array[33] = flag.ToString().ToLowerInvariant(); array[34] = " reason="; array[35] = reason; array[36] = "\n- HasListAccess="; array[37] = flag2.ToString().ToLowerInvariant(); array[38] = " reason="; array[39] = reason2; array[40] = "\n- HasChangeAccess="; array[41] = flag3.ToString().ToLowerInvariant(); array[42] = " reason="; array[43] = reason3; array[44] = "\n- HasDestroyAccess="; array[45] = flag4.ToString().ToLowerInvariant(); array[46] = " reason="; array[47] = reason4; string text = string.Concat(array); Plugin.Log.LogInfo((object)("[FCW TargetPortal] " + text)); return text; } private static Assembly FindTargetPortalAssembly() { try { if (Chainloader.PluginInfos != null && Chainloader.PluginInfos.TryGetValue("org.bepinex.plugins.targetportal", out var value)) { Assembly assembly = ((value == null) ? null : ((object)value.Instance)?.GetType().Assembly); if (IsRealTargetPortalAssembly(assembly)) { LogTargetPortalPluginFound(value, assembly, "guid"); return assembly; } LogInvalidTargetPortalAssembly("guid", assembly); } foreach (KeyValuePair pluginInfo in Chainloader.PluginInfos) { string text = pluginInfo.Key ?? ""; PluginInfo value2 = pluginInfo.Value; object obj; if (value2 == null) { obj = null; } else { BepInPlugin metadata = value2.Metadata; obj = ((metadata != null) ? metadata.Name : null); } if (obj == null) { obj = ""; } string text2 = (string)obj; if (string.Equals(text2, "TargetPortal", StringComparison.OrdinalIgnoreCase) || text.IndexOf("targetportal", StringComparison.OrdinalIgnoreCase) >= 0 || text2.IndexOf("targetportal", StringComparison.OrdinalIgnoreCase) >= 0 || text2.IndexOf("target portal", StringComparison.OrdinalIgnoreCase) >= 0) { PluginInfo value3 = pluginInfo.Value; Assembly assembly2 = ((value3 == null) ? null : ((object)value3.Instance)?.GetType().Assembly); if (IsRealTargetPortalAssembly(assembly2)) { LogTargetPortalPluginFound(pluginInfo.Value, assembly2, "plugin:" + text); return assembly2; } LogInvalidTargetPortalAssembly("plugin:" + text, assembly2); } } } catch { } Assembly[] assemblies = AppDomain.CurrentDomain.GetAssemblies(); foreach (Assembly assembly3 in assemblies) { string text3 = assembly3.GetName().Name ?? ""; if (IsRealTargetPortalAssembly(assembly3)) { LogOnce("found-name", "[FCW TargetPortal] TargetPortal found by assembly name: " + text3); return assembly3; } } return null; } private static bool IsRealTargetPortalAssembly(Assembly assembly) { if (assembly == null) { return false; } string text = assembly.GetName().Name ?? ""; if (string.Equals(text, "FortecaClanWarfare", StringComparison.OrdinalIgnoreCase) || string.Equals(text, "FortecaClanWarfare.Core", StringComparison.OrdinalIgnoreCase) || string.Equals(text, "FortecaClanWarfare.UI", StringComparison.OrdinalIgnoreCase)) { return false; } if (!string.Equals(text, "TargetPortal", StringComparison.OrdinalIgnoreCase)) { return text.StartsWith("TargetPortal", StringComparison.OrdinalIgnoreCase); } return true; } private static void LogTargetPortalPluginFound(PluginInfo info, Assembly assembly, string source) { object obj; if (info == null) { obj = null; } else { BepInPlugin metadata = info.Metadata; obj = ((metadata != null) ? metadata.Name : null); } if (obj == null) { obj = assembly?.GetName().Name ?? "TargetPortal"; } string text = (string)obj; object obj2; if (info == null) { obj2 = null; } else { BepInPlugin metadata2 = info.Metadata; obj2 = ((metadata2 == null) ? null : metadata2.Version?.ToString()); } if (obj2 == null) { obj2 = assembly?.GetName().Version?.ToString() ?? ""; } string text2 = (string)obj2; LogOnce("real-targetportal:" + source, "[FCW TargetPortal] Real TargetPortal plugin found: " + text + (string.IsNullOrEmpty(text2) ? "" : (" " + text2))); } private static void LogInvalidTargetPortalAssembly(string source, Assembly assembly) { string text = assembly?.GetName().Name ?? "null"; LogOnce("invalid-targetportal:" + source + ":" + text, "[FCW TargetPortal] Ignored non-TargetPortal assembly from " + source + ": " + text); } private static IEnumerable FindHookMethods(Assembly assembly) { foreach (Type type in SafeGetTypes(assembly)) { MethodInfo[] methods; try { methods = type.GetMethods(BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic); } catch { continue; } MethodInfo[] array = methods; foreach (MethodInfo method in array) { if (!(method.ReturnType != typeof(bool)) && (!string.Equals(type.FullName, "TargetPortal.Map", StringComparison.Ordinal) || !string.Equals(method.Name, "HandlePortalClick", StringComparison.Ordinal)) && BoolHookNames.Any((string n) => string.Equals(method.Name, n, StringComparison.OrdinalIgnoreCase))) { yield return method; } } } } private static IEnumerable FindPortalListMethods(Assembly assembly) { foreach (Type item in SafeGetTypes(assembly)) { MethodInfo[] methods; try { methods = item.GetMethods(BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic); } catch { continue; } MethodInfo[] array = methods; foreach (MethodInfo method in array) { if (PortalListHookNames.Any((string n) => string.Equals(method.Name, n, StringComparison.OrdinalIgnoreCase)) && (HasMutablePortalListArg(method) || method.ReturnType == typeof(List) || method.ReturnType == typeof(IEnumerable))) { yield return method; } } } } private static IEnumerable FindToggleModeMethods(Assembly assembly) { foreach (Type item in SafeGetTypes(assembly)) { if ((item.FullName ?? "").IndexOf("TogglePortalMode", StringComparison.OrdinalIgnoreCase) < 0) { continue; } MethodInfo[] methods; try { methods = item.GetMethods(BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic); } catch { continue; } MethodInfo[] array = methods; foreach (MethodInfo methodInfo in array) { if (string.Equals(methodInfo.Name, "Prefix", StringComparison.OrdinalIgnoreCase)) { yield return methodInfo; } } } } private static IEnumerable FindModeChangeMethods(Assembly assembly) { foreach (Type item in SafeGetTypes(assembly)) { if ((item.FullName ?? "").IndexOf("TargetPortal", StringComparison.OrdinalIgnoreCase) < 0) { continue; } MethodInfo[] methods; try { methods = item.GetMethods(BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic); } catch { continue; } MethodInfo[] array = methods; foreach (MethodInfo methodInfo in array) { if (string.Equals(methodInfo.Name, "SetPortalMode", StringComparison.OrdinalIgnoreCase) || string.Equals(methodInfo.Name, "OnPortalModeChange", StringComparison.OrdinalIgnoreCase)) { yield return methodInfo; } } } } private static IEnumerable FindHoverTextMethods(Assembly assembly) { foreach (Type item in SafeGetTypes(assembly)) { if ((item.FullName ?? "").IndexOf("TargetPortal", StringComparison.OrdinalIgnoreCase) < 0) { continue; } MethodInfo[] methods; try { methods = item.GetMethods(BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic); } catch { continue; } MethodInfo[] array = methods; foreach (MethodInfo methodInfo in array) { if (!(methodInfo.ReturnType == typeof(string))) { continue; } if (!string.Equals(methodInfo.Name, "GetHoverText", StringComparison.OrdinalIgnoreCase) && !string.Equals(methodInfo.Name, "SquashPortalMode", StringComparison.OrdinalIgnoreCase) && methodInfo.Name.IndexOf("Hover", StringComparison.OrdinalIgnoreCase) < 0) { Type? declaringType = methodInfo.DeclaringType; if ((object)declaringType == null || !(declaringType.FullName?.IndexOf("OverrideHoverText", StringComparison.OrdinalIgnoreCase) >= 0)) { continue; } } yield return methodInfo; } } } private static IEnumerable FindTraceMethods(Assembly assembly) { string[] names = new string[8] { "TogglePortalMode", "SetPortalMode", "OnPortalModeChange", "SetInitialPortalMode", "UpdatePortalMode", "UpdatePortalText", "Interact", "GetHoverText" }; foreach (Type item in SafeGetTypes(assembly)) { if ((item.FullName ?? "").IndexOf("TargetPortal", StringComparison.OrdinalIgnoreCase) < 0) { continue; } MethodInfo[] methods; try { methods = item.GetMethods(BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic); } catch { continue; } MethodInfo[] array = methods; foreach (MethodInfo methodInfo in array) { string methodName = methodInfo.Name ?? ""; string declaring = methodInfo.DeclaringType?.FullName ?? ""; if (names.Any((string n) => string.Equals(methodName, n, StringComparison.OrdinalIgnoreCase) || declaring.IndexOf(n, StringComparison.OrdinalIgnoreCase) >= 0) || methodName.IndexOf("PortalMode", StringComparison.OrdinalIgnoreCase) >= 0 || methodName.IndexOf("PortalText", StringComparison.OrdinalIgnoreCase) >= 0 || methodName.IndexOf("Hover", StringComparison.OrdinalIgnoreCase) >= 0) { yield return methodInfo; } } } } private static bool HasMutablePortalListArg(MethodInfo method) { ParameterInfo[] parameters = method.GetParameters(); for (int i = 0; i < parameters.Length; i++) { Type parameterType = parameters[i].ParameterType; if (parameterType == typeof(List) || parameterType == typeof(IList)) { return true; } } return false; } private static TeleportWorld FindTeleportWorldArg(object[] args) { //IL_003b: Unknown result type (might be due to invalid IL or missing references) //IL_0046: Expected O, but got Unknown if (args == null) { return null; } foreach (object obj in args) { TeleportWorld val = (TeleportWorld)((obj is TeleportWorld) ? obj : null); if (val != null) { return val; } } foreach (object obj2 in args) { Component val2 = (Component)((obj2 is Component) ? obj2 : null); if (val2 != null) { TeleportWorld component = val2.GetComponent(); if ((Object)component != (Object)null) { return component; } } } return FindCurrentPortal(); } private static IEnumerable SafeGetTypes(Assembly assembly) { try { return assembly.GetTypes(); } catch (ReflectionTypeLoadException ex) { return ex.Types.Where((Type t) => t != null); } catch { return Array.Empty(); } } public static TeleportWorld FindCurrentPortal(float maxDistance = 6f) { //IL_0007: Unknown result type (might be due to invalid IL or missing references) //IL_0012: Expected O, but got Unknown //IL_002e: Unknown result type (might be due to invalid IL or missing references) //IL_0039: Unknown result type (might be due to invalid IL or missing references) Player localPlayer = Player.m_localPlayer; if ((Object)localPlayer == (Object)null) { return null; } TeleportWorld[] array = Object.FindObjectsOfType(); TeleportWorld result = null; float num = maxDistance; for (int i = 0; i < array.Length; i++) { float num2 = Vector3.Distance(((Component)array[i]).transform.position, ((Component)localPlayer).transform.position); if (num2 < num) { num = num2; result = array[i]; } } return result; } public static string GetPortalRestrictionText(TeleportWorld portal) { return FormatRestriction(ReadPortalAccessData(TryGetZdo(portal), portal, null)); } public static string RewriteHoverText(TeleportWorld portal, string original) { PortalAccessData portalAccessData = ReadPortalAccessData(TryGetZdo(portal), portal, null); string text = original ?? ""; if (portalAccessData.AccessType != "Clan") { return text; } return ReplaceClanHoverMode(text, ClanLabel(portalAccessData)); } public static bool IsFehuClansMode(TeleportWorld portal) { return ReadPortalAccessData(TryGetZdo(portal), portal, null).AccessType == "Clan"; } public static bool TryHandleModeToggle(TeleportWorld portal) { return false; } private static bool IsFehuCarrierMode(string currentTpMode, string hover) { return ModeMatches(currentTpMode, hover, "Admin"); } private static bool ModeMatches(string currentTpMode, string hover, string mode) { if (string.Equals(currentTpMode, mode, StringComparison.OrdinalIgnoreCase)) { return true; } return (hover ?? "").IndexOf("[" + mode, StringComparison.OrdinalIgnoreCase) >= 0; } private static string ReadTargetPortalMode(ZDO zdo, string hover) { if (zdo != null) { string text = ModeName(ReadTargetPortalModeValue(zdo)); if (!string.Equals(text, "Unknown", StringComparison.OrdinalIgnoreCase)) { return text; } } string text2 = hover ?? ""; int num = text2.IndexOf('['); int num2 = ((num >= 0) ? text2.IndexOf(']', num) : (-1)); if (num >= 0 && num2 > num) { string text3 = text2.Substring(num + 1, num2 - num - 1); string[] array = new string[5] { "Public", "Private", "Group", "Admin", "Guild" }; foreach (string text4 in array) { if (text3.StartsWith(text4, StringComparison.OrdinalIgnoreCase)) { return text4; } } } return "Unknown"; } private static int ReadTargetPortalModeValue(ZDO zdo) { if (zdo == null) { return -1; } int num = zdo.GetInt("Target Portal PortalMode", -1); if (num < 0) { num = zdo.GetInt("TargetPortal PortalMode", -1); } return num; } private static string ModeName(int mode) { return mode switch { 0 => "Public", 1 => "Private", 2 => "Group", 3 => "Admin", 4 => "Guild", _ => "Unknown", }; } private static List BuildPortalCycle(ZDO zdo, PlayerClanInfo player, Clan clan, long playerId) { List list = new List(); Plugin.Log.LogInfo((object)"[FCW PortalModes] REBUILD START"); list.Add("Public"); Plugin.Log.LogInfo((object)"[FCW PortalModes] Public add=true reason=always"); list.Add("Private"); Plugin.Log.LogInfo((object)"[FCW PortalModes] Private add=true reason=always"); bool flag = HasGroupsIntegration(); string text = (flag ? ReadPortalOwnerName(zdo, "Groups") : ""); Plugin.Log.LogInfo((object)("[FCW PortalModes] Group add=" + flag.ToString().ToLowerInvariant() + " reason=" + (flag ? "Groups integration active" : "Groups integration unavailable") + " groupName=" + text)); if (flag) { list.Add("Group"); } bool flag2 = player != null && player.ClanId != 0 && clan != null; Plugin.Log.LogInfo((object)("[FCW PortalModes] Clan add=" + flag2.ToString().ToLowerInvariant() + " reason=" + (flag2 ? "current FCW clan resolved" : "no current FCW clan") + " clanId=" + (player?.ClanId ?? 0) + " clanName=" + clan?.Name + " clanTag=" + clan?.Tag)); if (flag2) { list.Add("Clan"); } bool flag3 = IsAdminAccess(playerId); Plugin.Log.LogInfo((object)("[FCW PortalModes] Admin add=" + flag3.ToString().ToLowerInvariant() + " reason=" + (flag3 ? "FCW admin access" : "not an admin"))); if (flag3) { list.Add("Admin"); } Plugin.Log.LogInfo((object)"[FCW PortalModes] Guild add=false reason=reserved for FCW Clan mode guildName="); Plugin.Log.LogInfo((object)("[FCW PortalModes] FINAL LIST=" + string.Join(" -> ", list.ToArray()))); return list; } private static int ModeValue(string mode) { return mode switch { "Public" => 0, "Private" => 1, "Group" => 2, "Admin" => 3, "Guild" => 4, _ => 0, }; } private static string ReadPortalOwnerName(ZDO zdo, string fallback) { if (zdo == null) { return fallback; } string text = zdo.GetString("Target Portal PortalOwnerName", ""); if (string.IsNullOrWhiteSpace(text)) { text = zdo.GetString("TargetPortal PortalOwnerName", ""); } if (!string.IsNullOrWhiteSpace(text)) { return text; } return fallback; } private static void LogModeToggle(ZDO zdo, string currentTpMode, string currentFcwMode, string nextMode, PlayerClanInfo player, Clan clan) { string text = ""; if (zdo != null) { text = zdo.GetString("Target Portal PortalOwnerName", ""); if (string.IsNullOrWhiteSpace(text)) { text = zdo.GetString("TargetPortal PortalOwnerName", ""); } if (string.IsNullOrWhiteSpace(text)) { text = zdo.GetString("ownerName", ""); } } string text2 = ((clan != null) ? (clan.Name + " [" + clan.Tag + "]") : ((player != null && player.ClanId != 0) ? ("ClanId " + player.ClanId) : "None")); Plugin.Log.LogInfo((object)("[FCW TargetPortal]\ncurrent TP mode = " + currentTpMode + "\ncurrent FCW mode = " + currentFcwMode + "\nnext mode = " + nextMode + "\nportal zdo = " + ((zdo != null) ? ((object)Unsafe.As(ref zdo.m_uid)/*cast due to .constrained prefix*/).ToString() : "null") + "\nowner = " + text + "\nclan = " + text2)); } private static void LogPortalCycle(string currentMode, bool fcwClan, string nextMode) { //IL_0007: Unknown result type (might be due to invalid IL or missing references) //IL_0012: Expected O, but got Unknown Player localPlayer = Player.m_localPlayer; PlayerClanInfo playerClanInfo = ((!((Object)localPlayer != (Object)null)) ? null : WarfareManager.Instance?.Data?.GetPlayer(localPlayer.GetPlayerID())); Clan clan = ((playerClanInfo == null || playerClanInfo.ClanId == 0) ? null : WarfareManager.Instance?.Data?.GetClan(playerClanInfo.ClanId)); string text = ((clan != null) ? (clan.Name + " [" + clan.Tag + "]") : "none"); Plugin.Log.LogInfo((object)("[FCW TargetPortal] Cycle current=" + currentMode + " fcwClan=" + fcwClan.ToString().ToLowerInvariant() + " playerClan=" + (playerClanInfo?.ClanId ?? 0) + "/" + text + " next=" + nextMode)); if (fcwClan && !string.Equals(nextMode, "Clan", StringComparison.OrdinalIgnoreCase)) { Plugin.Log.LogInfo((object)("[FCW TargetPortal] Exit Clan mode next=" + nextMode)); } } private static bool IsShiftDown() { if (!Input.GetKey((KeyCode)304)) { return Input.GetKey((KeyCode)303); } return true; } private static bool HasGroupsIntegration() { return Chainloader.PluginInfos.Keys.Any((string guid) => guid.IndexOf("groups", StringComparison.OrdinalIgnoreCase) >= 0); } private static bool HasGuildsIntegration() { return Chainloader.PluginInfos.Keys.Any((string guid) => guid.IndexOf("guilds", StringComparison.OrdinalIgnoreCase) >= 0); } private static void SetNormalTargetPortalMode(ZDO zdo, int mode, long playerId, Player player) { //IL_0009: Unknown result type (might be due to invalid IL or missing references) //IL_0014: Expected O, but got Unknown EnsurePortalCreatorMetadata(zdo, player, playerId); string text = (((Object)player != (Object)null) ? player.GetPlayerName() : ""); zdo.Set("Target Portal PortalMode", mode); zdo.Set("TargetPortal PortalMode", mode); zdo.Set("Target Portal PortalOwnerId", playerId.ToString()); zdo.Set("TargetPortal PortalOwnerId", playerId.ToString()); zdo.Set("Target Portal PortalOwnerName", text); zdo.Set("TargetPortal PortalOwnerName", text); } private static void LogPortalModeChanged(string oldMode, string newMode, int clanId, string clanName) { Plugin.Log.LogInfo((object)("[FCW TargetPortal] Mode changed old=" + oldMode + " new=" + newMode + " clanId=" + clanId + " clanName=" + clanName)); } private static void ClearFehuMode(ZDO zdo) { zdo.Set("FCW_TargetPortalMode", ""); zdo.Set("FCW_TargetPortalClanId", 0); zdo.Set("FCW_TargetPortalClanName", ""); zdo.Set("FCW_TargetPortalClanTag", ""); zdo.Set("FCW_TargetPortalOwnerPlayerId", 0L); zdo.Set("FCW_TargetPortalOwnerName", ""); zdo.Set("FCW_PortalClanRank", ""); zdo.Set("FCW_PortalClanRankValue", 0); zdo.Set("FCW_PortalFreebornId", 0L); zdo.Set("FCW_PortalFreebornName", ""); } private static void DeactivateClanModeForCycle(ZDO zdo) { ClearFehuMode(zdo); } private static void ClearTargetPortalClanOwner(ZDO zdo) { zdo.Set("Target Portal PortalOwnerId", ""); zdo.Set("TargetPortal PortalOwnerId", ""); zdo.Set("Target Portal PortalOwnerName", ""); zdo.Set("TargetPortal PortalOwnerName", ""); } private static void SetFehuClanSnapshot(ZDO zdo, PlayerClanInfo player, Clan clan, Player localPlayer) { //IL_000e: Unknown result type (might be due to invalid IL or missing references) //IL_0019: Expected O, but got Unknown EnsurePortalCreatorMetadata(zdo, localPlayer, player.PlayerId); string text = (((Object)localPlayer != (Object)null) ? localPlayer.GetPlayerName() : (player.PlayerName ?? "")); if (!IsSameClanSnapshot(zdo, player, clan, text)) { zdo.Set("Target Portal PortalMode", 4); zdo.Set("TargetPortal PortalMode", 4); zdo.Set("Target Portal PortalOwnerId", player.PlayerId.ToString()); zdo.Set("TargetPortal PortalOwnerId", player.PlayerId.ToString()); zdo.Set("Target Portal PortalOwnerName", "Clan: " + clan.Name); zdo.Set("TargetPortal PortalOwnerName", "Clan: " + clan.Name); zdo.Set("FCW_TargetPortalMode", "Clan"); zdo.Set("FCW_TargetPortalClanId", player.ClanId); zdo.Set("FCW_TargetPortalClanTag", clan.Tag ?? ""); zdo.Set("FCW_TargetPortalClanName", clan.Name ?? ""); zdo.Set("FCW_TargetPortalOwnerPlayerId", player.PlayerId); zdo.Set("FCW_TargetPortalOwnerName", text ?? ""); zdo.Set("FCW_PortalClanRank", ""); zdo.Set("FCW_PortalClanRankValue", 0); zdo.Set("FCW_PortalFreebornId", 0L); } } private static bool IsSameClanSnapshot(ZDO zdo, PlayerClanInfo player, Clan clan, string ownerName) { if (string.Equals(zdo.GetString("FCW_TargetPortalMode", ""), "Clan", StringComparison.OrdinalIgnoreCase) && ReadTargetPortalModeValue(zdo) == 4 && zdo.GetInt("FCW_TargetPortalClanId", 0) == player.ClanId && string.Equals(zdo.GetString("FCW_TargetPortalClanName", ""), clan.Name ?? "", StringComparison.Ordinal) && zdo.GetLong("FCW_TargetPortalOwnerPlayerId", 0L) == player.PlayerId) { return string.Equals(zdo.GetString("FCW_TargetPortalOwnerName", ""), ownerName ?? "", StringComparison.Ordinal); } return false; } public static string SetPortalRestriction(TeleportWorld portal, string accessType, PlayerClanInfo player, Clan clan, ClanRank requiredRank = ClanRank.Member) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) //IL_000c: Expected O, but got Unknown //IL_0022: Unknown result type (might be due to invalid IL or missing references) //IL_002d: Expected O, but got Unknown if ((Object)portal == (Object)null) { return "No portal selected."; } Player localPlayer = Player.m_localPlayer; ZDO val = TryGetZdo(portal); if ((Object)localPlayer == (Object)null || val == null || player == null) { return "Portal data not available."; } long playerID = localPlayer.GetPlayerID(); if (!CanEditPortal(val, playerID)) { return "Only portal owner or admin can change Fehu portal access."; } string text = NormalizeAccessType(accessType); switch (text) { case "None": ClearFehuMode(val); return "Fehu portal access set to None."; case "Clan": case "ClanRank": if (player.ClanId == 0 || clan == null) { return "You must be in a clan to set clan portal access."; } break; } if (text == "Clan" || text == "ClanRank") { SetFehuClanSnapshot(val, player, clan, localPlayer); val.Set("FCW_PortalClanRank", requiredRank.ToString()); val.Set("FCW_PortalClanRankValue", (int)requiredRank); } return "Fehu portal access set to " + FormatRestriction(ReadPortalAccessData(val, portal, null)) + "."; } private static bool CanEditPortal(ZDO zdo, long playerId) { Player localPlayer = Player.m_localPlayer; EnsurePortalCreatorMetadata(zdo, localPlayer, playerId); PlayerClanInfo player = WarfareManager.Instance?.Data?.GetPlayer(playerId); string reason; return CanChangeTargetPortalMode(zdo, playerId, player, out reason); } private static void TryInitializePortalCreator(object instance, object[] args) { //IL_0007: Unknown result type (might be due to invalid IL or missing references) //IL_0012: Expected O, but got Unknown try { Player localPlayer = Player.m_localPlayer; if (!((Object)localPlayer == (Object)null)) { EnsurePortalCreatorMetadata(FindPortalZdo(instance, args) ?? TryGetZdo(FindTeleportWorldArg(args)), localPlayer, localPlayer.GetPlayerID()); } } catch (Exception ex) { LogOnce("creator-init-ex:" + ex.GetType().Name, "[FCW TargetPortal] Creator initialization failed: " + ex.Message); } } private static void EnsurePortalCreatorMetadata(ZDO zdo, Player localPlayer, long playerId) { SyncOwnerMetadata(zdo, localPlayer, playerId); } private unsafe static void SyncOwnerMetadata(ZDO zdo, Player localPlayer, long playerId) { //IL_000d: Unknown result type (might be due to invalid IL or missing references) //IL_0018: Expected O, but got Unknown //IL_0095: Unknown result type (might be due to invalid IL or missing references) //IL_00a0: Expected O, but got Unknown //IL_010a: Unknown result type (might be due to invalid IL or missing references) //IL_010f: Unknown result type (might be due to invalid IL or missing references) if (zdo == null) { return; } long playerSteamId = GetPlayerSteamId(localPlayer, playerId); string playerName = (((Object)localPlayer != (Object)null) ? localPlayer.GetPlayerName() : ""); long num = ReadStoredTargetPortalOwnerId(zdo); if (num != 0L) { zdo.Set("TargetPortalProtection OwnerId", num.ToString()); } long num2 = zdo.GetLong("FCW_TargetPortalCreatorId", 0L); string reason; if (num2 != 0L) { if (IsSamePortalOwner(localPlayer, zdo, playerId, playerSteamId, out reason)) { NormalizeOwnerMetadata(zdo, playerSteamId, playerId, playerName); } return; } num2 = ReadStoredTargetPortalOwnerId(zdo); string text = ReadPortalOwnerName(zdo, ""); string text2 = "TargetPortal owner"; if (num2 == 0L && IsAdminAccess(playerId)) { num2 = playerId; text = (((Object)localPlayer != (Object)null) ? localPlayer.GetPlayerName() : ""); text2 = "admin repair"; } if (num2 != 0L) { zdo.Set("FCW_TargetPortalCreatorId", num2); zdo.Set("FCW_TargetPortalCreatorName", text ?? ""); if (IsSamePortalOwner(localPlayer, zdo, playerId, playerSteamId, out reason)) { NormalizeOwnerMetadata(zdo, playerSteamId, playerId, playerName); } ManualLogSource log = Plugin.Log; string[] obj = new string[8] { "[FCW TargetPortal] Synced owner metadata portal=", null, null, null, null, null, null, null }; ZDOID uid = zdo.m_uid; obj[1] = ((object)(*(ZDOID*)(&uid))/*cast due to .constrained prefix*/).ToString(); obj[2] = " owner="; obj[3] = num2.ToString(); obj[4] = "/"; obj[5] = text; obj[6] = " source="; obj[7] = text2; log.LogInfo((object)string.Concat(obj)); } } private static bool IsTargetPortalPortal(ZDO zdo) { if (zdo != null) { if (ReadTargetPortalModeValue(zdo) < 0 && ReadStoredTargetPortalOwnerId(zdo) == 0L) { return !string.IsNullOrEmpty(zdo.GetString("FCW_TargetPortalMode", "")); } return true; } return false; } private static bool IsFcwVisibilityOverridePortal(ZDO zdo) { if (zdo == null) { return false; } if (string.Equals(zdo.GetString("FCW_TargetPortalMode", ""), "Clan", StringComparison.OrdinalIgnoreCase)) { return true; } if (ReadTargetPortalModeValue(zdo) != 1) { return false; } if (ReadStoredTargetPortalOwnerId(zdo) == 0L && string.IsNullOrWhiteSpace(ReadPortalOwnerName(zdo, ""))) { return zdo.GetLong("FCW_TargetPortalCreatorId", 0L) != 0; } return true; } private static object AddTargetPortalMapPin(ZDO portal, Type mapType) { //IL_001b: Unknown result type (might be due to invalid IL or missing references) //IL_0026: Expected O, but got Unknown //IL_00a5: Unknown result type (might be due to invalid IL or missing references) try { object obj = _targetPortalMapPinTypeField?.GetValue(null); if (obj == null || (Object)Minimap.instance == (Object)null) { LogOnce("add-fcw-map-pin-missing-dependency", "[FCW TargetPortal] Clan pin restore skipped: TargetPortal icon or Minimap is unavailable."); return null; } MethodInfo methodInfo = typeof(Minimap).GetMethods(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic).FirstOrDefault(delegate(MethodInfo method) { if (method.Name != "AddPin" || method.ReturnType == typeof(void)) { return false; } ParameterInfo[] parameters3 = method.GetParameters(); return parameters3.Length == 7 && parameters3[0].ParameterType == typeof(Vector3) && parameters3[1].ParameterType.IsEnum && parameters3[2].ParameterType == typeof(string) && parameters3[3].ParameterType == typeof(bool) && parameters3[4].ParameterType == typeof(bool) && parameters3[5].ParameterType == typeof(long); }); if (methodInfo == null) { LogOnce("add-fcw-map-pin-method-missing", "[FCW TargetPortal] Clan pin restore skipped: exact Minimap.AddPin overload was not found."); return null; } ParameterInfo[] parameters = methodInfo.GetParameters(); object[] parameters2 = new object[7] { GetPortalPosition(portal), Enum.ToObject(parameters[1].ParameterType, Convert.ToInt32(obj)), portal.GetString("tag", ""), false, false, 0L, Activator.CreateInstance(parameters[6].ParameterType) }; object? obj2 = methodInfo.Invoke(Minimap.instance, parameters2); if (obj2 == null) { LogOnce("add-fcw-map-pin-null", "[FCW TargetPortal] Exact Minimap.AddPin returned null while restoring an allowed portal pin."); } return obj2; } catch (Exception ex) { LogOnce("add-fcw-map-pin:" + ex.GetType().Name, "[FCW TargetPortal] Could not add restored map pin: " + ex.Message); return null; } } private static IEnumerable EnumerateDictionaryEntries(object dictionary) { if (!(dictionary is IEnumerable enumerable)) { yield break; } foreach (object item in enumerable) { yield return item; } } private static object GetDictionaryEntryValue(object entry) { return entry?.GetType().GetProperty("Value", BindingFlags.Instance | BindingFlags.Public)?.GetValue(entry, null); } private static object GetDictionaryEntryKey(object entry) { return entry?.GetType().GetProperty("Key", BindingFlags.Instance | BindingFlags.Public)?.GetValue(entry, null); } private static void AddDictionaryEntry(object dictionary, object key, object value) { MethodInfo? obj = dictionary?.GetType().GetMethod("Add", BindingFlags.Instance | BindingFlags.Public); if (obj == null) { throw new InvalidOperationException("TargetPortal active pin dictionary does not expose Add."); } obj.Invoke(dictionary, new object[2] { key, value }); } private static void RemoveDictionaryEntry(object dictionary, object key) { MethodInfo? obj = dictionary?.GetType().GetMethod("Remove", BindingFlags.Instance | BindingFlags.Public); if (obj == null) { throw new InvalidOperationException("TargetPortal active pin dictionary does not expose Remove."); } obj.Invoke(dictionary, new object[1] { key }); } private static Vector3? GetPinPosition(object pin) { //IL_003e: Unknown result type (might be due to invalid IL or missing references) //IL_0043: Unknown result type (might be due to invalid IL or missing references) //IL_0044: 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) //IL_0081: Unknown result type (might be due to invalid IL or missing references) //IL_0082: Unknown result type (might be due to invalid IL or missing references) if (pin == null) { return null; } FieldInfo field = pin.GetType().GetField("m_pos", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); if (field != null) { object value = field.GetValue(pin); if (value is Vector3) { return (Vector3)value; } } PropertyInfo property = pin.GetType().GetProperty("m_pos", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); if (property != null) { object value = property.GetValue(pin, null); if (value is Vector3) { return (Vector3)value; } } return null; } private static IEnumerable GetReceivedZdos() { object dictionary = AccessTools.Field(typeof(ZDOMan), "m_objectsByID")?.GetValue(ZDOMan.instance); foreach (object item in EnumerateDictionaryEntries(dictionary)) { object dictionaryEntryValue = GetDictionaryEntryValue(item); ZDO val = (ZDO)((dictionaryEntryValue is ZDO) ? dictionaryEntryValue : null); if (val != null) { yield return val; } } } private static bool IsLocalPortalMapClient() { //IL_0005: Unknown result type (might be due to invalid IL or missing references) //IL_0010: Expected O, but got Unknown return (Object)Player.m_localPlayer != (Object)null; } private static IEnumerable GetKnownTargetPortalPortals() { if (_targetPortalKnownPortalsField == null) { Plugin.Log.LogWarning((object)"[FCW TargetPortal] knownPortals field cache is unavailable; clan portal restore cannot inspect TargetPortal's raw list."); } else { if (!(_targetPortalKnownPortalsField.GetValue(null) is IEnumerable enumerable)) { yield break; } foreach (object item in enumerable) { ZDO val = (ZDO)((item is ZDO) ? item : null); if (val != null) { yield return val; } } } } private static bool ContainsPortal(object activePins, ZDOID portalId) { //IL_0007: Unknown result type (might be due to invalid IL or missing references) //IL_0008: Unknown result type (might be due to invalid IL or missing references) return EnumerateDictionaryEntries(activePins).Any(delegate(object entry) { //IL_0010: Unknown result type (might be due to invalid IL or missing references) //IL_0016: Unknown result type (might be due to invalid IL or missing references) object dictionaryEntryValue = GetDictionaryEntryValue(entry); ZDO val = (ZDO)((dictionaryEntryValue is ZDO) ? dictionaryEntryValue : null); return val != null && val.m_uid == portalId; }); } private static bool IsInMinimapPinRegistry(object activePins, ZDOID portalId) { //IL_0035: Unknown result type (might be due to invalid IL or missing references) //IL_003a: Unknown result type (might be due to invalid IL or missing references) object targetPin = null; foreach (object item in EnumerateDictionaryEntries(activePins)) { object dictionaryEntryValue = GetDictionaryEntryValue(item); ZDO val = (ZDO)((dictionaryEntryValue is ZDO) ? dictionaryEntryValue : null); if (val != null && val.m_uid == portalId) { targetPin = item.GetType().GetProperty("Key", BindingFlags.Instance | BindingFlags.Public)?.GetValue(item, null); break; } } if (targetPin == null) { return false; } if (AccessTools.Field(typeof(Minimap), "m_pins")?.GetValue(Minimap.instance) is IEnumerable source) { return source.Cast().Any((object pin) => pin == targetPin); } return false; } private static void LogMapStage(string stage, IEnumerable rawPortals, object activePins, string source) { List list = (rawPortals ?? EnumerateDictionaryEntries(activePins).Select(GetDictionaryEntryValue).OfType()).Where((ZDO portal) => portal != null).ToList(); bool flag = list.Any((ZDO portal) => string.Equals(portal.GetString("FCW_TargetPortalMode", ""), "Clan", StringComparison.OrdinalIgnoreCase)); Plugin.Log.LogInfo((object)("[FCW TargetPortal] LIST STAGE " + stage + " count=" + list.Count + " containsClanPortal=" + flag.ToString().ToLowerInvariant() + " source=" + source)); } private unsafe static void LogClanPortalTrace(ZDO portal, PortalAccessData access, bool finalVisible, string addedStage, string reason) { //IL_0007: Unknown result type (might be due to invalid IL or missing references) //IL_0012: Expected O, but got Unknown //IL_008a: Unknown result type (might be due to invalid IL or missing references) //IL_008f: Unknown result type (might be due to invalid IL or missing references) Player localPlayer = Player.m_localPlayer; long playerId = (((Object)localPlayer != (Object)null) ? localPlayer.GetPlayerID() : 0); int valueOrDefault = (WarfareManager.Instance?.Data?.GetPlayer(playerId)?.ClanId).GetValueOrDefault(); ManualLogSource log = Plugin.Log; string[] obj = new string[16] { "[FCW TargetPortal] CLAN PORTAL TRACE id=", null, null, null, null, null, null, null, null, null, null, null, null, null, null, null }; ZDOID uid = portal.m_uid; obj[1] = ((object)(*(ZDOID*)(&uid))/*cast due to .constrained prefix*/).ToString(); obj[2] = " tag="; obj[3] = portal.GetString("tag", ""); obj[4] = " assignedClanId="; obj[5] = (access?.ClanId ?? 0).ToString(); obj[6] = " playerClanId="; obj[7] = valueOrDefault.ToString(); obj[8] = " allowed="; obj[9] = (CanCurrentPlayerSeePortal(portal, out var _, out var _) ? "true" : "false"); obj[10] = " addedStage="; obj[11] = addedStage; obj[12] = " finalVisible="; obj[13] = finalVisible.ToString().ToLowerInvariant(); obj[14] = " reason="; obj[15] = reason; log.LogInfo((object)string.Concat(obj)); } private static Vector3 GetPortalPosition(ZDO portal) { //IL_007b: Unknown result type (might be due to invalid IL or missing references) //IL_0035: Unknown result type (might be due to invalid IL or missing references) //IL_003a: Unknown result type (might be due to invalid IL or missing references) //IL_003b: 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_0081: Unknown result type (might be due to invalid IL or missing references) //IL_006b: Unknown result type (might be due to invalid IL or missing references) //IL_0070: Unknown result type (might be due to invalid IL or missing references) //IL_0071: Unknown result type (might be due to invalid IL or missing references) //IL_0072: Unknown result type (might be due to invalid IL or missing references) try { MethodInfo methodInfo = AccessTools.Method(typeof(ZDO), "GetPosition", (Type[])null, (Type[])null); if (methodInfo != null && methodInfo.Invoke(portal, null) is Vector3 result) { return result; } if (AccessTools.Field(typeof(ZDO), "m_position")?.GetValue(portal) is Vector3 result2) { return result2; } } catch { } return Vector3.zero; } private static void LogPortalDecision(string action, ZDO zdo, bool allowed, string reason) { //IL_0007: Unknown result type (might be due to invalid IL or missing references) //IL_0012: Expected O, but got Unknown Player localPlayer = Player.m_localPlayer; long playerId = (((Object)localPlayer != (Object)null) ? localPlayer.GetPlayerID() : 0); PlayerClanInfo playerClanInfo = WarfareManager.Instance?.Data?.GetPlayer(playerId); PortalAccessData portalAccessData = ReadPortalAccessData(zdo, null, null); string text = ((portalAccessData.AccessType == "Clan") ? "Clan" : ModeName(ReadTargetPortalModeValue(zdo))); Plugin.Log.LogInfo((object)("[FCW TargetPortal] " + action + " CHECK mode=" + text + " fcwMode=" + ((zdo != null) ? zdo.GetString("FCW_TargetPortalMode", "") : null) + " player=" + ((localPlayer != null) ? localPlayer.GetPlayerName() : null) + " playerClanId=" + (playerClanInfo?.ClanId ?? 0) + " assignedClanId=" + (portalAccessData?.ClanId ?? 0) + " owner=" + ReadPortalOwnerName(zdo, "") + " allowed=" + allowed.ToString().ToLowerInvariant() + " reason=" + reason)); } private static bool IsPortalOwner(ZDO zdo, long playerId) { Player player = FindPlayerById(playerId); string reason; return IsSamePortalOwner(player, zdo, playerId, GetPlayerSteamId(player, playerId), out reason); } private static bool IsSamePortalOwner(Player player, ZDO zdo, long playerId, long playerSteamId, out string reason) { //IL_0023: Unknown result type (might be due to invalid IL or missing references) //IL_002e: Expected O, but got Unknown reason = "owner metadata missing"; if (zdo == null || playerId == 0L) { return false; } if (IsAdminAccess(playerId)) { reason = "admin bypass"; return true; } string text = (((Object)player != (Object)null) ? player.GetPlayerName() : ""); long num = zdo.GetLong("FCW_TargetPortalCreatorSteamId", 0L); long num2 = zdo.GetLong("FCW_TargetPortalCreatorPlayerId", 0L); long num3 = zdo.GetLong("FCW_TargetPortalCreatorId", 0L); long num4 = ReadStoredTargetPortalOwnerId(zdo); string text2 = zdo.GetString("FCW_TargetPortalCreatorName", ""); string text3 = ReadPortalOwnerName(zdo, ""); if (!string.IsNullOrWhiteSpace(text) && !string.IsNullOrWhiteSpace(text3) && string.Equals(text3, text, StringComparison.Ordinal)) { reason = "owner name match"; return true; } if (!string.IsNullOrWhiteSpace(text) && !string.IsNullOrWhiteSpace(text2) && string.Equals(text2, text, StringComparison.Ordinal)) { reason = "creator name match"; return true; } if ((num != 0L && num == playerSteamId) || (num2 != 0L && num2 == playerId)) { reason = "stored owner identity"; return true; } if (num3 != 0L && (num3 == playerSteamId || num3 == playerId)) { reason = "legacy creator identity"; return true; } if (num4 != 0L && (num4 == playerSteamId || num4 == playerId)) { reason = "native owner identity"; return true; } reason = "portal owner only"; return false; } private unsafe static void NormalizeOwnerMetadata(ZDO zdo, long steamId, long playerId, string playerName) { //IL_008c: Unknown result type (might be due to invalid IL or missing references) //IL_0091: Unknown result type (might be due to invalid IL or missing references) if (zdo != null) { bool flag = false; if (steamId != 0L && zdo.GetLong("FCW_TargetPortalCreatorSteamId", 0L) == 0L) { zdo.Set("FCW_TargetPortalCreatorSteamId", steamId); flag = true; } if (playerId != 0L && zdo.GetLong("FCW_TargetPortalCreatorPlayerId", 0L) == 0L) { zdo.Set("FCW_TargetPortalCreatorPlayerId", playerId); flag = true; } if (!string.IsNullOrWhiteSpace(playerName) && string.IsNullOrWhiteSpace(zdo.GetString("FCW_TargetPortalCreatorName", ""))) { zdo.Set("FCW_TargetPortalCreatorName", playerName); flag = true; } if (flag) { ManualLogSource log = Plugin.Log; string[] obj = new string[8] { "[FCW TargetPortal] Owner metadata normalized portal=", null, null, null, null, null, null, null }; ZDOID uid = zdo.m_uid; obj[1] = ((object)(*(ZDOID*)(&uid))/*cast due to .constrained prefix*/).ToString(); obj[2] = " steam="; obj[3] = steamId.ToString(); obj[4] = " playerId="; obj[5] = playerId.ToString(); obj[6] = " name="; obj[7] = playerName; log.LogInfo((object)string.Concat(obj)); } } } private static Player FindPlayerById(long playerId) { //IL_0014: Unknown result type (might be due to invalid IL or missing references) //IL_001f: Expected O, but got Unknown Player localPlayer = Player.m_localPlayer; if ((Object)localPlayer != (Object)null && localPlayer.GetPlayerID() == playerId) { return localPlayer; } try { return ((IEnumerable)Object.FindObjectsOfType()).FirstOrDefault((Func)((Player candidate) => (Object)candidate != (Object)null && candidate.GetPlayerID() == playerId)); } catch { return null; } } private static long GetPlayerSteamId(Player player, long playerId) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) //IL_000c: Expected O, but got Unknown try { if ((Object)player != (Object)null && (Object)(object)player == (Object)(object)Player.m_localPlayer) { return ZNet.GetUID(); } } catch { } return 0L; } private static bool LooksLikeSteamId(long value) { return value >= 10000000000000000L; } private static bool IsOwnerOrAdmin(ZDO zdo, long playerId) { if (!IsAdminAccess(playerId)) { return IsPortalOwner(zdo, playerId); } return true; } private unsafe static bool HasPortalAccess(ZDO zdo, long playerId, PlayerClanInfo player, out string reason) { //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) if (!IsTargetPortalPortal(zdo)) { reason = "not a TargetPortal portal"; return true; } if (IsAdminAccess(playerId)) { reason = "admin bypass"; return true; } PortalAccessData access = ReadPortalAccessData(zdo, null, null); if (access.AccessType == "Clan") { TryRepairMissingClanAssignment(zdo, playerId, player, out access); if (access.ClanId == 0) { reason = "broken clan portal: assigned clan missing"; ManualLogSource log = Plugin.Log; ZDOID uid = zdo.m_uid; log.LogWarning((object)("[FCW TargetPortal] Clan portal has no assigned clan portal=" + ((object)(*(ZDOID*)(&uid))/*cast due to .constrained prefix*/).ToString() + " player=" + playerId)); return false; } bool flag = player != null && player.ClanId != 0 && player.ClanId == access.ClanId; reason = (flag ? "assigned FCW clan" : ((player == null || player.ClanId == 0) ? "freeborn/no clan" : "different clan")); return flag; } switch (ReadTargetPortalModeValue(zdo)) { case 0: reason = "public"; return true; case 1: reason = (IsPortalOwner(zdo, playerId) ? "private owner" : "private owner only"); return IsPortalOwner(zdo, playerId); case 3: reason = "admin only"; return false; case 2: case 4: reason = "native group/guild validation"; return true; default: reason = "unowned"; return true; } } private static bool HasPortalManagementAccess(ZDO zdo, long playerId, PlayerClanInfo player, out string reason) { if (IsAdminAccess(playerId)) { reason = "admin bypass"; return true; } PortalAccessData access = ReadPortalAccessData(zdo, null, null); if (access.AccessType == "Clan") { TryRepairMissingClanAssignment(zdo, playerId, player, out access); if (access.ClanId == 0) { reason = "broken clan portal: assigned clan missing"; return false; } bool flag = player != null && player.ClanId != 0 && player.ClanId == access.ClanId; reason = (flag ? "assigned FCW clan" : ((player == null || player.ClanId == 0) ? "freeborn/no clan" : "different clan")); return flag; } if (ReadTargetPortalModeValue(zdo) == 3) { reason = "admin portal"; return false; } bool flag2 = IsPortalOwner(zdo, playerId); reason = (flag2 ? "portal owner" : "portal owner only"); return flag2; } private static bool HasPortalDestroyAccess(ZDO zdo, long playerId, PlayerClanInfo player, out string reason) { if (IsAdminAccess(playerId)) { reason = "admin bypass"; return true; } PortalAccessData portalAccessData = ReadPortalAccessData(zdo, null, null); if (portalAccessData.AccessType == "Clan") { if (portalAccessData.ClanId <= 0) { reason = "assigned clan missing"; return false; } bool flag = player != null && player.ClanId == portalAccessData.ClanId; reason = (flag ? "clan match" : "clan mismatch/freeborn"); return flag; } if (ReadTargetPortalModeValue(zdo) == 3) { reason = "admin portal"; return false; } Player localPlayer = Player.m_localPlayer; return IsSamePortalOwner(localPlayer, zdo, playerId, GetPlayerSteamId(localPlayer, playerId), out reason); } private unsafe static void TryRepairMissingClanAssignment(ZDO zdo, long playerId, PlayerClanInfo player, out PortalAccessData access) { //IL_0046: Unknown result type (might be due to invalid IL or missing references) //IL_004b: Unknown result type (might be due to invalid IL or missing references) //IL_00a2: Unknown result type (might be due to invalid IL or missing references) //IL_00a7: Unknown result type (might be due to invalid IL or missing references) //IL_0107: 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) access = ReadPortalAccessData(zdo, null, null); if (access.AccessType != "Clan" || access.ClanId != 0 || player == null || player.ClanId == 0) { return; } ZDOID uid; if (!IsOwnerOrAdmin(zdo, playerId)) { ManualLogSource log = Plugin.Log; uid = zdo.m_uid; log.LogWarning((object)("[FCW TargetPortal] Clan portal assignment missing; repair requires owner/admin portal=" + ((object)(*(ZDOID*)(&uid))/*cast due to .constrained prefix*/).ToString() + " player=" + playerId)); return; } Clan clan = WarfareManager.Instance?.Data?.GetClan(player.ClanId); if (clan == null) { ManualLogSource log2 = Plugin.Log; uid = zdo.m_uid; log2.LogWarning((object)("[FCW TargetPortal] Clan portal assignment missing and current clan could not be resolved portal=" + ((object)(*(ZDOID*)(&uid))/*cast due to .constrained prefix*/).ToString() + " clan=" + player.ClanId)); return; } SetFehuClanSnapshot(zdo, player, clan, FindPlayerById(playerId) ?? Player.m_localPlayer); access = ReadPortalAccessData(zdo, null, null); ManualLogSource log3 = Plugin.Log; string[] obj = new string[6] { "[FCW TargetPortal] Repaired missing ClanId portal=", null, null, null, null, null }; uid = zdo.m_uid; obj[1] = ((object)(*(ZDOID*)(&uid))/*cast due to .constrained prefix*/).ToString(); obj[2] = " clanId="; obj[3] = access.ClanId.ToString(); obj[4] = " clanName="; obj[5] = access.ClanName; log3.LogInfo((object)string.Concat(obj)); } private static bool CanChangeTargetPortalMode(ZDO zdo, long playerId, PlayerClanInfo player, out string reason) { //IL_0050: Unknown result type (might be due to invalid IL or missing references) //IL_005b: Expected O, but got Unknown string text = ((ReadPortalAccessData(zdo, null, null).AccessType == "Clan") ? "Clan" : ModeName(ReadTargetPortalModeValue(zdo))); int num = ((zdo != null) ? zdo.GetInt("FCW_TargetPortalClanId", 0) : 0); bool result = HasPortalManagementAccess(zdo, playerId, player, out reason); Player localPlayer = Player.m_localPlayer; string text2 = (((Object)localPlayer != (Object)null) ? localPlayer.GetPlayerName() : ""); long playerSteamId = GetPlayerSteamId(localPlayer, playerId); long num2 = ((zdo != null) ? zdo.GetLong("FCW_TargetPortalCreatorSteamId", 0L) : 0); long num3 = ((zdo != null) ? zdo.GetLong("FCW_TargetPortalCreatorPlayerId", 0L) : 0); long num4 = ((zdo != null) ? zdo.GetLong("FCW_TargetPortalCreatorId", 0L) : 0); string text3 = ((zdo != null) ? zdo.GetString("FCW_TargetPortalCreatorName", "") : null) ?? ""; long num5 = ReadStoredTargetPortalOwnerId(zdo); string text4 = ReadPortalOwnerName(zdo, ""); Plugin.Log.LogInfo((object)("[FCW TargetPortal] Owner check mode=" + text + "\nplayerSteam=" + playerSteamId + "\nplayerId=" + playerId + "\nplayerName=" + text2 + "\ncreatorSteam=" + num2 + "\ncreatorPlayerId=" + num3 + "\ncreatorOldId=" + num4 + "\ncreatorName=" + text3 + "\nnativeOwnerId=" + num5 + "\nnativeOwnerName=" + text4 + "\nplayerClanId=" + (player?.ClanId ?? 0) + "\nassignedClanId=" + num + "\nallowed=" + result.ToString().ToLowerInvariant() + "\nreason=" + reason)); return result; } private static PortalAccessData GetPortalAccessData(object instance, object[] args) { return ReadPortalAccessData(FindPortalZdo(instance, args), instance, args); } private static bool FehuAllowsPortalAccess(PortalAccessData access) { string reason; return AllowsCurrentPlayer(access, out reason); } private static bool AllowsCurrentPlayer(PortalAccessData access, out string reason) { //IL_0007: Unknown result type (might be due to invalid IL or missing references) //IL_0012: Expected O, but got Unknown Player localPlayer = Player.m_localPlayer; if ((Object)localPlayer == (Object)null) { reason = "no local player"; return false; } long playerID = localPlayer.GetPlayerID(); PlayerClanInfo playerClanInfo = WarfareManager.Instance?.Data?.GetPlayer(playerID); if (access == null || access.AccessType == "None") { reason = "allowed"; return true; } if (IsAdminAccess(playerID)) { LogAccess("admin", access); reason = "admin"; return true; } if (playerClanInfo == null) { reason = "no FCW player data"; return false; } switch (access.AccessType) { case "Clan": if (playerClanInfo.ClanId != 0 && playerClanInfo.ClanId == access.ClanId) { LogAccess("clan", access); reason = "allowed"; return true; } reason = ((playerClanInfo.ClanId == 0) ? "freeborn/no clan denied for clan portal" : "other clan denied"); break; case "ClanRank": if (playerClanInfo.ClanId != 0 && playerClanInfo.ClanId == access.ClanId && playerClanInfo.Rank >= access.RequiredRank) { LogAccess("clan-rank", access); reason = "allowed"; return true; } reason = "rank/clan mismatch"; break; case "Freeborn": if (playerClanInfo.IsFreeborn && ((access.FreebornId != 0L && playerClanInfo.PlayerId == access.FreebornId) || (access.OwnerPlayerId != 0L && playerClanInfo.PlayerId == access.OwnerPlayerId))) { LogAccess("freeborn", access); reason = "allowed"; return true; } reason = ((playerClanInfo.ClanId != 0) ? "clan denied for freeborn portal" : "different freeborn denied"); break; case "Self": if (access.OwnerPlayerId != 0L && playerClanInfo.PlayerId == access.OwnerPlayerId) { LogAccess("self", access); reason = "allowed"; return true; } reason = "not portal owner"; break; case "Admin": reason = "admin only"; return false; case "None": reason = "allowed"; return true; default: reason = "unowned"; return true; } return false; } private static void FilterPortalListInPlace(IList portals, string hook) { if ((Object)(object)ZNet.instance == (Object)null) { return; } if (ZNet.instance.IsServer()) { NormalizeLegacyClanCarriers(portals); } if (!IsLocalPortalMapClient()) { return; } for (int num = portals.Count - 1; num >= 0; num--) { ZDO val = portals[num]; if (val != null) { PortalAccessData access; string reason; bool flag = CanCurrentPlayerSeePortal(val, out access, out reason); LogPortalFilter(hook, val, access, flag, reason); if (!flag) { portals.RemoveAt(num); } } } } private unsafe static void RestoreAllowedClanPortals(List portals, string hook) { //IL_008e: 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_016f: Unknown result type (might be due to invalid IL or missing references) //IL_019b: Unknown result type (might be due to invalid IL or missing references) //IL_01a0: Unknown result type (might be due to invalid IL or missing references) //IL_00d5: 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_00eb: Unknown result type (might be due to invalid IL or missing references) //IL_0110: Unknown result type (might be due to invalid IL or missing references) //IL_0115: Unknown result type (might be due to invalid IL or missing references) if (portals == null || (Object)(object)ZNet.instance == (Object)null || !IsLocalPortalMapClient() || ZDOMan.instance == null) { return; } try { HashSet hashSet = new HashSet(from portal in portals where portal != null select portal.m_uid); foreach (ZDO receivedZdo in GetReceivedZdos()) { if (receivedZdo != null && !hashSet.Contains(receivedZdo.m_uid) && string.Equals(receivedZdo.GetString("FCW_TargetPortalMode", ""), "Clan", StringComparison.OrdinalIgnoreCase)) { if (CanCurrentPlayerSeePortal(receivedZdo, out var access, out var reason)) { portals.Add(receivedZdo); hashSet.Add(receivedZdo.m_uid); ZDOID uid = receivedZdo.m_uid; string key = "restore-clan-portal:" + ((object)(*(ZDOID*)(&uid))/*cast due to .constrained prefix*/).ToString(); string[] obj = new string[8] { "[FCW TargetPortal] Restored Clan portal to destination list portal=", null, null, null, null, null, null, null }; uid = receivedZdo.m_uid; obj[1] = ((object)(*(ZDOID*)(&uid))/*cast due to .constrained prefix*/).ToString(); obj[2] = " clan="; obj[3] = access.ClanId.ToString(); obj[4] = "/"; obj[5] = access.ClanName; obj[6] = " hook="; obj[7] = hook; LogOnce(key, string.Concat(obj)); } else { ZDOID uid = receivedZdo.m_uid; string key2 = "hide-clan-portal:" + ((object)(*(ZDOID*)(&uid))/*cast due to .constrained prefix*/).ToString() + ":" + reason; string[] obj2 = new string[6] { "[FCW TargetPortal] Hidden Clan portal from destination list portal=", null, null, null, null, null }; uid = receivedZdo.m_uid; obj2[1] = ((object)(*(ZDOID*)(&uid))/*cast due to .constrained prefix*/).ToString(); obj2[2] = " reason="; obj2[3] = reason; obj2[4] = " hook="; obj2[5] = hook; LogOnce(key2, string.Concat(obj2)); } } } } catch (Exception ex) { LogOnce("restore-clan-portals-fail:" + ex.GetType().Name, "[FCW TargetPortal] Clan portal restore filter failed: " + ex.Message); } } private unsafe static void NormalizeLegacyClanCarriers(IEnumerable portals) { //IL_0067: Unknown result type (might be due to invalid IL or missing references) //IL_0089: Unknown result type (might be due to invalid IL or missing references) //IL_008e: Unknown result type (might be due to invalid IL or missing references) if (portals == null) { return; } foreach (ZDO portal in portals) { if (portal != null && string.Equals(portal.GetString("FCW_TargetPortalMode", ""), "Clan", StringComparison.OrdinalIgnoreCase) && ReadTargetPortalModeValue(portal) != 4) { portal.Set("Target Portal PortalMode", 4); portal.Set("TargetPortal PortalMode", 4); if (MigratedClanCarriers.Add(portal.m_uid)) { ManualLogSource log = Plugin.Log; string[] obj = new string[6] { "[FCW TargetPortal] Migrated existing Clan portal to Guild-as-Clan mode portal=", null, null, null, null, null }; ZDOID uid = portal.m_uid; obj[1] = ((object)(*(ZDOID*)(&uid))/*cast due to .constrained prefix*/).ToString(); obj[2] = " clan="; obj[3] = portal.GetInt("FCW_TargetPortalClanId", 0).ToString(); obj[4] = "/"; obj[5] = portal.GetString("FCW_TargetPortalClanName", ""); log.LogInfo((object)string.Concat(obj)); } } } } private static bool CanCurrentPlayerSeePortal(ZDO zdo, out PortalAccessData access, out string reason) { //IL_0011: Unknown result type (might be due to invalid IL or missing references) //IL_001c: Expected O, but got Unknown access = ReadPortalAccessData(zdo, null, null); Player localPlayer = Player.m_localPlayer; if ((Object)localPlayer == (Object)null) { reason = "no local player"; return false; } long playerID = localPlayer.GetPlayerID(); if (IsAdminAccess(playerID)) { reason = "admin"; return true; } if (access.AccessType == "Clan") { PlayerClanInfo player = WarfareManager.Instance?.Data?.GetPlayer(playerID); return HasPortalAccess(zdo, playerID, player, out reason); } int num = ReadTargetPortalModeValue(zdo); if (num == 2 || num == 4) { reason = "native group/guild filter"; return true; } PlayerClanInfo player2 = WarfareManager.Instance?.Data?.GetPlayer(playerID); return HasPortalAccess(zdo, playerID, player2, out reason); } private static bool IsAdminAccess(long playerId) { try { return WarfareManager.Instance?.GetAdminAccessInfo(playerId, 0L).EventAdminAccess ?? false; } catch { return false; } } private static ZDO FindPortalZdo(object instance, object[] args) { ZDO val = TryGetZdo(instance); if (val != null) { return val; } if (args == null) { return null; } for (int i = 0; i < args.Length; i++) { val = TryGetZdo(args[i]); if (val != null) { return val; } } return null; } private static ZDO TryGetZdo(object value) { return TryGetZdo(value, 0, new HashSet()); } private static ZDO TryGetZdo(object value, int depth, HashSet visited) { //IL_003a: Unknown result type (might be due to invalid IL or missing references) //IL_003f: Unknown result type (might be due to invalid IL or missing references) //IL_0067: Unknown result type (might be due to invalid IL or missing references) //IL_0072: Expected O, but got Unknown //IL_004c: Unknown result type (might be due to invalid IL or missing references) //IL_0143: Unknown result type (might be due to invalid IL or missing references) //IL_0148: Unknown result type (might be due to invalid IL or missing references) //IL_0156: Unknown result type (might be due to invalid IL or missing references) if (value == null) { return null; } if (depth > 3) { return null; } Type type = value.GetType(); if (!type.IsValueType && !visited.Add(value)) { return null; } ZDO val = (ZDO)((value is ZDO) ? value : null); if (val != null) { return val; } if (value is ZDOID val2 && ZDOMan.instance != null) { return ZDOMan.instance.GetZDO(val2); } Component val3 = (Component)((value is Component) ? value : null); if (val3 != null) { ZNetView component = val3.GetComponent(); if ((Object)component != (Object)null) { return component.GetZDO(); } } object obj = GetMemberValue(value, "m_nview") ?? GetMemberValue(value, "nview") ?? GetMemberValue(value, "zNetView"); ZNetView val4 = (ZNetView)((obj is ZNetView) ? obj : null); if (val4 != null) { return val4.GetZDO(); } object obj2 = GetMemberValue(value, "m_zdo") ?? GetMemberValue(value, "zdo") ?? GetMemberValue(value, "ZDO"); ZDO val5 = (ZDO)((obj2 is ZDO) ? obj2 : null); if (val5 != null) { return val5; } if ((GetMemberValue(value, "m_zdoID") ?? GetMemberValue(value, "zdoID") ?? GetMemberValue(value, "ZDOID") ?? GetMemberValue(value, "m_uid") ?? GetMemberValue(value, "uid")) is ZDOID val6 && ZDOMan.instance != null) { ZDO zDO = ZDOMan.instance.GetZDO(val6); if (zDO != null) { return zDO; } } string[] array = new string[10] { "portal", "Portal", "targetPortal", "TargetPortal", "selectedPortal", "SelectedPortal", "m_portal", "m_targetPortal", "m_selectedPortal", "teleportWorld" }; foreach (string name in array) { ZDO val7 = TryGetZdo(GetMemberValue(value, name), depth + 1, visited); if (val7 != null) { return val7; } } if (!(value is string) && !(value is IEnumerable)) { FieldInfo[] fields = type.GetFields(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); for (int j = 0; j < fields.Length; j++) { if (!fields[j].FieldType.IsPrimitive && !(fields[j].FieldType == typeof(string))) { object value2 = null; try { value2 = fields[j].GetValue(value); } catch { } ZDO val8 = TryGetZdo(value2, depth + 1, visited); if (val8 != null) { return val8; } } } } return null; } private static PortalAccessData ReadPortalAccessData(ZDO zdo, object instance, object[] args) { PortalAccessData portalAccessData = new PortalAccessData(); if (zdo != null) { portalAccessData.AccessType = NormalizeAccessType(zdo.GetString("FCW_TargetPortalMode", "")); portalAccessData.ClanId = zdo.GetInt("FCW_TargetPortalClanId", 0); portalAccessData.RequiredRank = ParseRank(zdo.GetString("FCW_PortalClanRank", ""), (ClanRank)zdo.GetInt("FCW_PortalClanRankValue", 0)); portalAccessData.FreebornId = zdo.GetLong("FCW_PortalFreebornId", 0L); portalAccessData.OwnerPlayerId = ReadPortalOwnerPlayerId(zdo); portalAccessData.ClanName = zdo.GetString("FCW_TargetPortalClanName", ""); portalAccessData.ClanTag = zdo.GetString("FCW_TargetPortalClanTag", ""); portalAccessData.FreebornName = zdo.GetString("FCW_PortalFreebornName", ""); portalAccessData.OwnerName = zdo.GetString("Target Portal PortalOwnerName", ""); if (string.IsNullOrWhiteSpace(portalAccessData.OwnerName)) { portalAccessData.OwnerName = zdo.GetString("TargetPortal PortalOwnerName", ""); } string text = portalAccessData.OwnerName ?? ""; if (portalAccessData.AccessType == "None" && text.StartsWith("Clan:", StringComparison.OrdinalIgnoreCase)) { portalAccessData.AccessType = "Clan"; portalAccessData.ClanId = zdo.GetInt("FCW_TargetPortalClanId", 0); if (string.IsNullOrWhiteSpace(portalAccessData.ClanName)) { portalAccessData.ClanName = text.Substring("Clan:".Length).Trim(); } } } if (portalAccessData.AccessType != "None") { return portalAccessData; } string text2 = FindStringValue("ownerName", instance, args); if (string.IsNullOrWhiteSpace(text2)) { return portalAccessData; } if (text2.StartsWith("Clan:", StringComparison.OrdinalIgnoreCase)) { portalAccessData.AccessType = "Clan"; portalAccessData.ClanName = text2.Substring("Clan:".Length).Trim(); } return portalAccessData; } private static long ReadPortalOwnerPlayerId(ZDO zdo) { if (zdo == null) { return 0L; } long result = zdo.GetLong("FCW_TargetPortalOwnerPlayerId", 0L); if (result != 0L) { return result; } string s = zdo.GetString("TargetPortalProtection OwnerId", ""); if (!long.TryParse(s, out result)) { s = zdo.GetString("Target Portal PortalOwnerId", ""); } if (!long.TryParse(s, out result)) { s = zdo.GetString("TargetPortal PortalOwnerId", ""); long.TryParse(s, out result); } return result; } private static long ReadStoredTargetPortalOwnerId(ZDO zdo) { if (zdo == null) { return 0L; } string s = zdo.GetString("TargetPortalProtection OwnerId", ""); if (!long.TryParse(s, out var result)) { s = zdo.GetString("Target Portal PortalOwnerId", ""); } if (!long.TryParse(s, out result)) { s = zdo.GetString("TargetPortal PortalOwnerId", ""); long.TryParse(s, out result); } return result; } private static string FormatRestriction(PortalAccessData data) { if (data == null || data.AccessType == "None") { return "Fehu: None"; } return data.AccessType switch { "Clan" => "Fehu Clans: " + ClanLabel(data) + OwnerSuffix(data), "ClanRank" => "Fehu: Clan Rank - " + ClanLabel(data) + " / " + data.RequiredRank.ToString() + "+", "Freeborn" => "Fehu: Freeborn - " + ((!string.IsNullOrEmpty(data.FreebornName)) ? data.FreebornName : data.FreebornId.ToString()), "Self" => "Fehu: Self Only - " + ((!string.IsNullOrEmpty(data.OwnerName)) ? data.OwnerName : data.OwnerPlayerId.ToString()), "Admin" => "Fehu: Admin Only", _ => "Fehu: None", }; } private static string DenialLabel(string accessType) { switch (NormalizeAccessType(accessType)) { case "Clan": case "ClanRank": return "Fehu Clans only"; case "Freeborn": return "Freeborn only"; case "Self": return "Self only"; case "Admin": return "Admin only"; default: return "restricted"; } } private static string OwnerSuffix(PortalAccessData data) { if (!string.IsNullOrEmpty(data.OwnerName)) { return " (Owner: " + data.OwnerName + ")"; } return ""; } private static string ClanLabel(PortalAccessData data) { if (!string.IsNullOrEmpty(data.ClanName)) { return data.ClanName + (string.IsNullOrEmpty(data.ClanTag) ? "" : (" [" + data.ClanTag + "]")); } Clan clan = WarfareManager.Instance?.Data?.GetClan(data.ClanId); if (clan != null) { return clan.Name + " [" + clan.Tag + "]"; } return "Clan " + data.ClanId; } private static string FindStringValue(string name, object instance, object[] args) { string text = GetMemberValue(instance, name) as string; if (!string.IsNullOrWhiteSpace(text)) { return text; } if (args == null) { return ""; } for (int i = 0; i < args.Length; i++) { text = GetMemberValue(args[i], name) as string; if (!string.IsNullOrWhiteSpace(text)) { return text; } } return ""; } private static object GetMemberValue(object value, string name) { if (value == null) { return null; } Type type = value.GetType(); FieldInfo field = type.GetField(name, BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); if (field != null) { return field.GetValue(value); } return type.GetProperty(name, BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic)?.GetValue(value, null); } private static string NormalizeAccessType(string value) { string text = (value ?? "").Trim(); if (string.IsNullOrEmpty(text) || text.Equals("Public", StringComparison.OrdinalIgnoreCase)) { return "None"; } if (text.Equals("Clan", StringComparison.OrdinalIgnoreCase) || text.Equals("FCW Clan", StringComparison.OrdinalIgnoreCase)) { return "Clan"; } if (text.Equals("ClanRank", StringComparison.OrdinalIgnoreCase) || text.Equals("FCW Clan Rank", StringComparison.OrdinalIgnoreCase)) { return "ClanRank"; } if (text.Equals("Freeborn", StringComparison.OrdinalIgnoreCase) || text.Equals("FCW Freeborn", StringComparison.OrdinalIgnoreCase)) { return "Freeborn"; } if (text.Equals("Self", StringComparison.OrdinalIgnoreCase) || text.Equals("FCW Freeborn Self", StringComparison.OrdinalIgnoreCase)) { return "Self"; } if (text.Equals("Admin", StringComparison.OrdinalIgnoreCase)) { return "Admin"; } return "None"; } private static ClanRank ParseRank(string text, ClanRank fallback) { if (Enum.TryParse(text, ignoreCase: true, out var result)) { return result; } return fallback; } private static void LogAccess(string reason, PortalAccessData access) { ConfigEntry enableTaxDebugLogs = ClanConfig.EnableTaxDebugLogs; if (enableTaxDebugLogs == null || !enableTaxDebugLogs.Value) { ConfigEntry enableVaultDebugLogs = ClanConfig.EnableVaultDebugLogs; if (enableVaultDebugLogs == null || !enableVaultDebugLogs.Value) { return; } } Plugin.Log.LogInfo((object)("[FCW TargetPortal] access allowed by Fehu " + reason + " type=" + access.AccessType)); } private static void LogPortalAccessCheck(string hook, PortalAccessData access, bool allowed, string reason) { //IL_0007: Unknown result type (might be due to invalid IL or missing references) //IL_0012: Expected O, but got Unknown //IL_0020: Unknown result type (might be due to invalid IL or missing references) //IL_002b: Expected O, but got Unknown Player localPlayer = Player.m_localPlayer; long num = (((Object)localPlayer != (Object)null) ? localPlayer.GetPlayerID() : 0); string text = (((Object)localPlayer != (Object)null) ? localPlayer.GetPlayerName() : ""); int num2 = ((num == 0L) ? null : WarfareManager.Instance?.Data?.GetPlayer(num))?.ClanId ?? 0; Plugin.Log.LogInfo((object)("FCW TargetPortal: Access check portalClan=" + (access?.ClanId ?? 0) + " playerClan=" + num2 + " allowed=" + allowed.ToString().ToLowerInvariant())); Plugin.Log.LogInfo((object)("[FCW Portal Access Check]\nhook = " + hook + "\nplayer name = " + text + "\nplayer id = " + num + "\nplayer clan id = " + num2 + "\nsource portal FCW mode = checked-by-source-hook-if-present\ntarget portal FCW mode = " + (access?.AccessType ?? "None") + "\ntarget portal clan id = " + (access?.ClanId ?? 0) + "\nallowed = " + allowed.ToString().ToLowerInvariant() + "\nreason = " + reason)); } private static void LogPortalFilter(string hook, ZDO zdo, PortalAccessData access, bool visible, string reason) { //IL_000f: Unknown result type (might be due to invalid IL or missing references) //IL_001a: Expected O, but got Unknown if (IsDebugLoggingEnabled()) { Player localPlayer = Player.m_localPlayer; long num = (((Object)localPlayer != (Object)null) ? localPlayer.GetPlayerID() : 0); int num2 = ((num == 0L) ? null : WarfareManager.Instance?.Data?.GetPlayer(num))?.ClanId ?? 0; string text = ((zdo != null) ? zdo.GetString("tag", "") : null) ?? ""; Plugin.Log.LogInfo((object)("[FCW TargetPortal Filter]\nhook = " + hook + "\nportal name/id = " + text + " / " + ((zdo != null) ? ((object)Unsafe.As(ref zdo.m_uid)/*cast due to .constrained prefix*/).ToString() : "null") + "\nportal FCW mode = " + (access?.AccessType ?? "None") + "\nportal clan id = " + (access?.ClanId ?? 0) + "\nplayer clan id = " + num2 + "\nvisible true/false = " + visible.ToString().ToLowerInvariant() + "\nreason = " + reason)); } } private static bool IsDebugLoggingEnabled() { //IL_0007: Unknown result type (might be due to invalid IL or missing references) //IL_0012: Expected O, but got Unknown try { Player localPlayer = Player.m_localPlayer; long playerId = (((Object)localPlayer != (Object)null) ? localPlayer.GetPlayerID() : 0); ConfigEntry enableTaxDebugLogs = ClanConfig.EnableTaxDebugLogs; int result; if (enableTaxDebugLogs == null || !enableTaxDebugLogs.Value) { ConfigEntry enableVaultDebugLogs = ClanConfig.EnableVaultDebugLogs; if (enableVaultDebugLogs == null || !enableVaultDebugLogs.Value) { result = ((WarfareManager.Instance?.GetAdminAccessInfo(playerId, 0L).DebugMode ?? false) ? 1 : 0); goto IL_0061; } } result = 1; goto IL_0061; IL_0061: return (byte)result != 0; } catch { return false; } } private static void LogOnce(string key, string message) { if (Logged.Add(key)) { Plugin.Log.LogInfo((object)message); } } } } namespace FortecaClanWarfare.Utils { public static class BiomeUtils { private static Dictionary _merchantCache = new Dictionary(); private static float _merchantCacheTime = 0f; private const float MerchantCacheLifetime = 60f; public static Biome GetBiomeAt(Vector3 position) { //IL_000e: 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) if (WorldGenerator.instance == null) { return (Biome)1; } return WorldGenerator.instance.GetBiome(position); } public static Dictionary SampleBiomeComposition(Vector2i gridPos, int sampleCount = 100) { //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_0011: Unknown result type (might be due to invalid IL or missing references) //IL_0017: 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_007a: Unknown result type (might be due to invalid IL or missing references) //IL_0092: Unknown result type (might be due to invalid IL or missing references) //IL_00bf: Unknown result type (might be due to invalid IL or missing references) //IL_00c1: Unknown result type (might be due to invalid IL or missing references) //IL_00c6: Unknown result type (might be due to invalid IL or missing references) //IL_00c8: Unknown result type (might be due to invalid IL or missing references) //IL_00cf: Unknown result type (might be due to invalid IL or missing references) //IL_019f: Unknown result type (might be due to invalid IL or missing references) //IL_01a0: Unknown result type (might be due to invalid IL or missing references) //IL_01a5: Unknown result type (might be due to invalid IL or missing references) //IL_01a8: Unknown result type (might be due to invalid IL or missing references) //IL_01b4: Expected I4, but got Unknown //IL_00d7: Unknown result type (might be due to invalid IL or missing references) //IL_00de: Unknown result type (might be due to invalid IL or missing references) //IL_00e6: Unknown result type (might be due to invalid IL or missing references) //IL_00e8: Unknown result type (might be due to invalid IL or missing references) //IL_00ef: Expected I4, but got Unknown Dictionary dictionary = new Dictionary(); Vector3 regionCenter = VoronoiGrid.Instance.GetRegionCenter(gridPos); float num = VoronoiGrid.Instance.GetApproximateRegionRadius(gridPos); if (num < 200f) { num = 200f; } if (num > 2000f) { num = 2000f; } int num2 = (int)Mathf.Sqrt((float)sampleCount); if (num2 < 5) { num2 = 5; } float num3 = num * 2f; float num4 = num3 * 2f / (float)num2; int num5 = 0; Vector3 val = default(Vector3); for (int i = 0; i < num2; i++) { for (int j = 0; j < num2; j++) { float num6 = regionCenter.x - num3 + ((float)i + 0.5f) * num4; float num7 = regionCenter.z - num3 + ((float)j + 0.5f) * num4; ((Vector3)(ref val))..ctor(num6, 0f, num7); Vector2i region = VoronoiGrid.Instance.GetRegion(val); if (region.x == gridPos.x && region.y == gridPos.y) { int key = (int)GetBiomeAt(val); if (!dictionary.ContainsKey(key)) { dictionary[key] = 0f; } dictionary[key]++; num5++; } } } if (num5 > 0) { foreach (int item in new List(dictionary.Keys)) { dictionary[item] = dictionary[item] / (float)num5 * 100f; } } if (num5 == 0) { Biome biomeAt = GetBiomeAt(regionCenter); dictionary[(int)biomeAt] = 100f; } return dictionary; } public static bool CellHasMerchant(Vector2i gridPos) { //IL_0005: Unknown result type (might be due to invalid IL or missing references) //IL_0010: Expected O, but got Unknown //IL_003f: Unknown result type (might be due to invalid IL or missing references) //IL_004f: Unknown result type (might be due to invalid IL or missing references) //IL_0075: Unknown result type (might be due to invalid IL or missing references) //IL_007a: 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) //IL_0088: Unknown result type (might be due to invalid IL or missing references) //IL_013a: Unknown result type (might be due to invalid IL or missing references) //IL_00c7: Unknown result type (might be due to invalid IL or missing references) //IL_00c9: Unknown result type (might be due to invalid IL or missing references) //IL_00d8: Unknown result type (might be due to invalid IL or missing references) //IL_00da: Unknown result type (might be due to invalid IL or missing references) //IL_00e4: Unknown result type (might be due to invalid IL or missing references) //IL_00e9: Unknown result type (might be due to invalid IL or missing references) //IL_00ef: Unknown result type (might be due to invalid IL or missing references) //IL_00f0: Unknown result type (might be due to invalid IL or missing references) //IL_00f5: Unknown result type (might be due to invalid IL or missing references) //IL_00f7: Unknown result type (might be due to invalid IL or missing references) //IL_00fe: Unknown result type (might be due to invalid IL or missing references) //IL_0106: Unknown result type (might be due to invalid IL or missing references) //IL_010d: Unknown result type (might be due to invalid IL or missing references) if ((Object)ZoneSystem.instance == (Object)null) { return false; } if (Time.time - _merchantCacheTime > 60f) { _merchantCache.Clear(); _merchantCacheTime = Time.time; } if (_merchantCache.TryGetValue(gridPos, out var value)) { return value; } bool flag = false; Vector3 val = default(Vector3); foreach (KeyValuePair locationInstance in ZoneSystem.instance.m_locationInstances) { LocationInstance value2 = locationInstance.Value; if (value2.m_location == null) { continue; } string text = value2.m_location.m_prefabName?.ToLower() ?? ""; if (text.Contains("haldor") || text.Contains("hildir")) { val = new Vector3(value2.m_position.x, 0f, value2.m_position.z); Vector2i region = VoronoiGrid.Instance.GetRegion(val); if (region.x == gridPos.x && region.y == gridPos.y) { flag = true; break; } } } _merchantCache[gridPos] = flag; return flag; } public static Biome GetDominantBiome(Dictionary composition) { if (composition != null && composition.Count != 0) { int num = 1; float num2 = 0f; foreach (KeyValuePair item in composition) { if (item.Value > num2) { num2 = item.Value; num = item.Key; } } return (Biome)num; } return (Biome)1; } public static bool HasPointOfInterest(Vector2i gridPos) { //IL_0005: Unknown result type (might be due to invalid IL or missing references) //IL_0010: Expected O, but got Unknown //IL_0016: Unknown result type (might be due to invalid IL or missing references) //IL_003b: Unknown result type (might be due to invalid IL or missing references) //IL_0040: Unknown result type (might be due to invalid IL or missing references) //IL_0041: Unknown result type (might be due to invalid IL or missing references) //IL_0049: 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_0065: Unknown result type (might be due to invalid IL or missing references) //IL_0074: Unknown result type (might be due to invalid IL or missing references) //IL_0075: Unknown result type (might be due to invalid IL or missing references) //IL_007f: Unknown result type (might be due to invalid IL or missing references) //IL_0084: Unknown result type (might be due to invalid IL or missing references) //IL_008a: Unknown result type (might be due to invalid IL or missing references) //IL_008b: Unknown result type (might be due to invalid IL or missing references) //IL_0090: Unknown result type (might be due to invalid IL or missing references) //IL_0092: Unknown result type (might be due to invalid IL or missing references) //IL_0099: 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_00a8: Unknown result type (might be due to invalid IL or missing references) if ((Object)ZoneSystem.instance == (Object)null) { return false; } Vector3 val = default(Vector3); foreach (KeyValuePair locationInstance in ZoneSystem.instance.m_locationInstances) { LocationInstance value = locationInstance.Value; if (value.m_location != null && IsPOILocation(value.m_location.m_prefabName ?? "")) { val = new Vector3(value.m_position.x, 0f, value.m_position.z); Vector2i region = VoronoiGrid.Instance.GetRegion(val); if (region.x == gridPos.x && region.y == gridPos.y) { return true; } } } return false; } private static bool IsPOILocation(string prefabName) { if (string.IsNullOrEmpty(prefabName)) { return false; } string text = prefabName.ToLower(); if (text.Contains("altar") || text.Contains("boss")) { return true; } if (text.Contains("haldor") || text.Contains("hildir")) { return true; } if (text.Contains("infectedmine") || text.Contains("sunkencrypt") || text.Contains("mountaincave") || text.Contains("goblinking")) { return true; } return false; } public static Color GetBiomeColor(Biome biome) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_0003: Invalid comparison between Unknown and I4 //IL_0061: Unknown result type (might be due to invalid IL or missing references) //IL_0064: Invalid comparison between Unknown and I4 //IL_0005: Unknown result type (might be due to invalid IL or missing references) //IL_0007: Unknown result type (might be due to invalid IL or missing references) //IL_0009: Expected I4, but got Unknown //IL_00a3: Unknown result type (might be due to invalid IL or missing references) //IL_00a9: Invalid comparison between Unknown and I4 //IL_0066: Unknown result type (might be due to invalid IL or missing references) //IL_0069: Invalid comparison between Unknown and I4 //IL_0113: 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_001f: Unknown result type (might be due to invalid IL or missing references) //IL_0021: Invalid comparison between Unknown and I4 //IL_00ab: Unknown result type (might be due to invalid IL or missing references) //IL_00b1: Invalid comparison between Unknown and I4 //IL_006b: Unknown result type (might be due to invalid IL or missing references) //IL_006e: Invalid comparison between Unknown and I4 //IL_00e1: 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_0026: Invalid comparison between Unknown and I4 Color gray = default(Color); if ((int)biome <= 16) { switch (biome - 1) { case 1: goto IL_00e3; case 3: goto IL_00fb; case 2: goto IL_0113; case 0: goto IL_011b; } if ((int)biome != 8) { if ((int)biome != 16) { goto IL_0113; } ((Color)(ref gray))..ctor(0.9f, 0.85f, 0.4f); } else { ((Color)(ref gray))..ctor(0.2f, 0.4f, 0.2f); } } else if ((int)biome <= 64) { if ((int)biome != 32) { if ((int)biome != 64) { goto IL_0113; } ((Color)(ref gray))..ctor(0.7f, 0.9f, 1f); } else { ((Color)(ref gray))..ctor(0.8f, 0.3f, 0.2f); } } else if ((int)biome != 256) { if ((int)biome != 512) { goto IL_0113; } ((Color)(ref gray))..ctor(0.5f, 0.3f, 0.6f); } else { ((Color)(ref gray))..ctor(0.2f, 0.4f, 0.8f); } goto IL_00e1; IL_00e3: ((Color)(ref gray))..ctor(0.4f, 0.35f, 0.2f); goto IL_00e1; IL_00fb: ((Color)(ref gray))..ctor(0.8f, 0.8f, 0.9f); goto IL_00e1; IL_011b: ((Color)(ref gray))..ctor(0.4f, 0.8f, 0.3f); goto IL_00e1; IL_0113: gray = Color.gray; return gray; IL_00e1: return gray; } public static string GetBiomeName(Biome biome) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_0003: Invalid comparison between Unknown and I4 //IL_0046: Unknown result type (might be due to invalid IL or missing references) //IL_0049: Invalid comparison between Unknown and I4 //IL_0005: Unknown result type (might be due to invalid IL or missing references) //IL_0007: Unknown result type (might be due to invalid IL or missing references) //IL_0009: Expected I4, but got Unknown //IL_0061: Unknown result type (might be due to invalid IL or missing references) //IL_0067: Invalid comparison between Unknown and I4 //IL_004b: Unknown result type (might be due to invalid IL or missing references) //IL_004e: Invalid comparison between Unknown and I4 //IL_007d: Unknown result type (might be due to invalid IL or missing references) //IL_0080: Unknown result type (might be due to invalid IL or missing references) //IL_0082: Invalid comparison between Unknown and I4 //IL_001f: Unknown result type (might be due to invalid IL or missing references) //IL_0021: Invalid comparison between Unknown and I4 //IL_006f: Unknown result type (might be due to invalid IL or missing references) //IL_0075: Invalid comparison between Unknown and I4 //IL_0056: Unknown result type (might be due to invalid IL or missing references) //IL_0059: Invalid comparison between Unknown and I4 //IL_008a: Unknown result type (might be due to invalid IL or missing references) //IL_008d: Unknown result type (might be due to invalid IL or missing references) //IL_008f: Invalid comparison between Unknown and I4 //IL_0023: Unknown result type (might be due to invalid IL or missing references) //IL_0026: Invalid comparison between Unknown and I4 //IL_0097: Unknown result type (might be due to invalid IL or missing references) //IL_009d: Unknown result type (might be due to invalid IL or missing references) //IL_009f: Invalid comparison between Unknown and I4 //IL_00a7: Unknown result type (might be due to invalid IL or missing references) //IL_00aa: Unknown result type (might be due to invalid IL or missing references) //IL_00ac: Invalid comparison between Unknown and I4 //IL_00b4: Unknown result type (might be due to invalid IL or missing references) //IL_00b6: Unknown result type (might be due to invalid IL or missing references) //IL_00b8: Invalid comparison between Unknown and I4 //IL_00c0: Unknown result type (might be due to invalid IL or missing references) //IL_00c2: Unknown result type (might be due to invalid IL or missing references) //IL_00c4: Invalid comparison between Unknown and I4 //IL_00cc: Unknown result type (might be due to invalid IL or missing references) //IL_00ce: Unknown result type (might be due to invalid IL or missing references) //IL_00d0: Invalid comparison between Unknown and I4 //IL_00d8: Unknown result type (might be due to invalid IL or missing references) //IL_00da: Unknown result type (might be due to invalid IL or missing references) //IL_00dc: Invalid comparison between Unknown and I4 //IL_00e4: Unknown result type (might be due to invalid IL or missing references) //IL_00ea: Unknown result type (might be due to invalid IL or missing references) //IL_00ec: Invalid comparison between Unknown and I4 if ((int)biome <= 16) { switch (biome - 1) { default: if ((int)biome != 8) { if ((int)biome == 16) { return "Plains"; } break; } return "Black Forest"; case 0: return "Meadows"; case 1: return "Swamp"; case 3: return "Mountain"; case 2: break; } } else if ((int)biome <= 64) { if ((int)biome == 32) { return "Ashlands"; } if ((int)biome == 64) { return "Deep North"; } } else { if ((int)biome == 256) { return "Ocean"; } if ((int)biome == 512) { return "Mistlands"; } } if ((biome & 0x20) > 0) { return "Ashlands"; } if ((biome & 0x40) > 0) { return "Deep North"; } if ((biome & 0x200) > 0) { return "Mistlands"; } if ((biome & 0x10) > 0) { return "Plains"; } if ((biome & 4) > 0) { return "Mountain"; } if ((biome & 2) > 0) { return "Swamp"; } if ((biome & 8) > 0) { return "Black Forest"; } if ((biome & 1) > 0) { return "Meadows"; } if ((biome & 0x100) > 0) { return "Ocean"; } return "Wilds"; } public static float GetBiomeTollMultiplier(Biome biome) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //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_0007: Unknown result type (might be due to invalid IL or missing references) //IL_000a: Invalid comparison between Unknown and I4 //IL_003f: Unknown result type (might be due to invalid IL or missing references) //IL_0042: Invalid comparison between Unknown and I4 //IL_000c: Unknown result type (might be due to invalid IL or missing references) //IL_000e: Unknown result type (might be due to invalid IL or missing references) //IL_0010: Expected I4, but got Unknown //IL_005e: Unknown result type (might be due to invalid IL or missing references) //IL_0064: Invalid comparison between Unknown and I4 //IL_0044: Unknown result type (might be due to invalid IL or missing references) //IL_0047: Invalid comparison between Unknown and I4 //IL_0026: Unknown result type (might be due to invalid IL or missing references) //IL_0028: Invalid comparison between Unknown and I4 //IL_0066: Unknown result type (might be due to invalid IL or missing references) //IL_006c: Invalid comparison between Unknown and I4 //IL_0049: Unknown result type (might be due to invalid IL or missing references) //IL_004c: Invalid comparison between Unknown and I4 //IL_002a: Unknown result type (might be due to invalid IL or missing references) //IL_002d: Invalid comparison between Unknown and I4 Biome val = ResolveBiome(biome); if ((int)val <= 16) { switch (val - 1) { default: if ((int)val != 8) { if ((int)val == 16) { return 3f; } break; } return 1.5f; case 1: return 2f; case 3: return 2.5f; case 2: break; case 0: return 1f; } } else if ((int)val <= 64) { if ((int)val == 32) { return 5f; } if ((int)val == 64) { return 3f; } } else { if ((int)val == 256) { return 0.5f; } if ((int)val == 512) { return 4f; } } return 1f; } public static Biome ResolveBiome(Biome biome) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_0003: Invalid comparison between Unknown and I4 //IL_002c: Unknown result type (might be due to invalid IL or missing references) //IL_002f: Invalid comparison between Unknown and I4 //IL_0005: Unknown result type (might be due to invalid IL or missing references) //IL_0007: Invalid comparison between Unknown and I4 //IL_003d: Unknown result type (might be due to invalid IL or missing references) //IL_0043: Invalid comparison between Unknown and I4 //IL_0031: Unknown result type (might be due to invalid IL or missing references) //IL_0034: Invalid comparison between Unknown and I4 //IL_001b: Unknown result type (might be due to invalid IL or missing references) //IL_001d: Invalid comparison between Unknown and I4 //IL_0009: 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_000d: Invalid comparison between Unknown and I4 //IL_00ad: Unknown result type (might be due to invalid IL or missing references) //IL_0045: Unknown result type (might be due to invalid IL or missing references) //IL_004b: Invalid comparison between Unknown and I4 //IL_0036: Unknown result type (might be due to invalid IL or missing references) //IL_0039: Invalid comparison between Unknown and I4 //IL_0022: Unknown result type (might be due to invalid IL or missing references) //IL_0025: Invalid comparison between Unknown and I4 //IL_0012: Unknown result type (might be due to invalid IL or missing references) //IL_0014: Invalid comparison between Unknown and I4 //IL_004d: Unknown result type (might be due to invalid IL or missing references) //IL_0050: Unknown result type (might be due to invalid IL or missing references) //IL_0052: Invalid comparison between Unknown and I4 //IL_0057: Unknown result type (might be due to invalid IL or missing references) //IL_005a: Unknown result type (might be due to invalid IL or missing references) //IL_005c: Invalid comparison between Unknown and I4 //IL_0061: Unknown result type (might be due to invalid IL or missing references) //IL_0067: Unknown result type (might be due to invalid IL or missing references) //IL_0069: Invalid comparison between Unknown and I4 //IL_0071: Unknown result type (might be due to invalid IL or missing references) //IL_0074: Unknown result type (might be due to invalid IL or missing references) //IL_0076: Invalid comparison between Unknown and I4 //IL_007b: Unknown result type (might be due to invalid IL or missing references) //IL_007d: Unknown result type (might be due to invalid IL or missing references) //IL_007f: Invalid comparison between Unknown and I4 //IL_0083: Unknown result type (might be due to invalid IL or missing references) //IL_0085: Unknown result type (might be due to invalid IL or missing references) //IL_0087: Invalid comparison between Unknown and I4 //IL_008b: Unknown result type (might be due to invalid IL or missing references) //IL_008d: Unknown result type (might be due to invalid IL or missing references) //IL_008f: Invalid comparison between Unknown and I4 //IL_0093: Unknown result type (might be due to invalid IL or missing references) //IL_0095: Unknown result type (might be due to invalid IL or missing references) //IL_0097: Invalid comparison between Unknown and I4 //IL_009b: 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_00a3: Invalid comparison between Unknown and I4 if ((int)biome <= 16) { if ((int)biome <= 4) { if (biome - 1 > 1 && (int)biome != 4) { goto IL_004d; } } else if ((int)biome != 8 && (int)biome != 16) { goto IL_004d; } } else if ((int)biome <= 64) { if ((int)biome != 32 && (int)biome != 64) { goto IL_004d; } } else if ((int)biome != 256 && (int)biome != 512) { goto IL_004d; } return biome; IL_004d: if ((biome & 0x20) <= 0) { if ((biome & 0x40) <= 0) { if ((biome & 0x200) <= 0) { if ((biome & 0x10) <= 0) { if ((biome & 4) <= 0) { if ((biome & 2) <= 0) { if ((biome & 8) <= 0) { if ((biome & 1) <= 0) { if ((biome & 0x100) <= 0) { return (Biome)1; } return (Biome)256; } return (Biome)1; } return (Biome)8; } return (Biome)2; } return (Biome)4; } return (Biome)16; } return (Biome)512; } return (Biome)64; } return (Biome)32; } } public static class TerritoryNaming { private static readonly string[] NorthPrefixes = new string[5] { "Norðri", "Northern", "Frost", "Winter's", "Boreal" }; private static readonly string[] SouthPrefixes = new string[5] { "Suðri", "Southern", "Sun's", "Warm", "Golden" }; private static readonly string[] EastPrefixes = new string[5] { "Austri", "Eastern", "Dawn", "Rising", "Morning" }; private static readonly string[] WestPrefixes = new string[5] { "Vestri", "Western", "Twilight", "Setting", "Dusk" }; private static readonly string[] CenterPrefixes = new string[5] { "Miðgarð", "Central", "Heart of", "Ancient", "Old" }; private static readonly Dictionary BiomeNames = new Dictionary { { (Biome)1, new string[8] { "Meadow", "Grassland", "Green Vale", "Peaceful Fields", "Idavoll", "Folkvangr", "Verdant Lea", "Shepherd's Rest" } }, { (Biome)8, new string[8] { "Dark Woods", "Eerie Forest", "Shadow Grove", "Myrkviðr", "Ironwood", "Troll's Domain", "Whispering Pines", "Draugr's Reach" } }, { (Biome)2, new string[8] { "Murky Fen", "Poison Marsh", "Dead Man's Bog", "Hel's Mire", "Rotting Wetlands", "Drowned Vale", "Festering Swamp", "Corpse Marsh" } }, { (Biome)4, new string[8] { "Frost Peak", "Stone Heights", "Jötunheimr", "Eagle's Nest", "Ice Crown", "Thunder Summit", "Goat's Path", "Windswept Ridge" } }, { (Biome)16, new string[8] { "Golden Plains", "Fuling Lands", "Scorched Fields", "Barren Reach", "Vigriðr", "Blood Prairie", "Deathstalker's Domain", "Sun-bleached Expanse" } }, { (Biome)512, new string[8] { "Shrouded Vale", "Spider's Web", "Mist Hollow", "Gjöll", "Veiled Ruins", "Ancient's Rest", "Forgotten Realm", "Skuld's Domain" } }, { (Biome)32, new string[8] { "Scorched Waste", "Múspellheim", "Ember Fields", "Burning Reach", "Cinder Vale", "Charred Domain", "Flame's Edge", "Surt's Realm" } }, { (Biome)64, new string[8] { "Frozen Waste", "Niflheim", "Eternal Ice", "Frost Giant's Land", "Glacial Expanse", "Winter's End", "Permafrost Domain", "Hrimfaxi's Path" } }, { (Biome)256, new string[8] { "Ægir's Domain", "Depths", "Serpent's Sea", "Njord's Waters", "Kraken's Reach", "Wayward Waves", "Storm Channel", "Ran's Grasp" } } }; private static readonly string[] MixedSuffixes = new string[6] { "Borderlands", "Frontier", "Crossroads", "Threshold", "Edge", "Marches" }; private static readonly string[] MerchantModifiers = new string[4] { "Market", "Trade Post", "Merchant's Haven", "Trader's Rest" }; private static readonly string[] BossModifiers = new string[4] { "Altar", "Sacred Ground", "Ritual Site", "Offering Place" }; public static string GenerateTerritoryName(Vector2i gridPos, Dictionary biomeComposition, bool hasMerchant = false, bool hasBossAltar = false) { //IL_0010: 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_002c: Unknown result type (might be due to invalid IL or missing references) //IL_0038: Unknown result type (might be due to invalid IL or missing references) //IL_0046: Unknown result type (might be due to invalid IL or missing references) //IL_004b: Unknown result type (might be due to invalid IL or missing references) //IL_004d: Unknown result type (might be due to invalid IL or missing references) //IL_0053: Unknown result type (might be due to invalid IL or missing references) //IL_005b: Unknown result type (might be due to invalid IL or missing references) //IL_0074: Unknown result type (might be due to invalid IL or missing references) //IL_0075: Unknown result type (might be due to invalid IL or missing references) //IL_007a: 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) //IL_007e: Unknown result type (might be due to invalid IL or missing references) //IL_009e: Unknown result type (might be due to invalid IL or missing references) //IL_00af: Unknown result type (might be due to invalid IL or missing references) if (biomeComposition == null || biomeComposition.Count == 0) { return $"Unknown Land ({gridPos.x}, {gridPos.y})"; } Random random = new Random(gridPos.x * 10000 + gridPos.y); Biome dominantBiome = GetDominantBiome(biomeComposition); float biomePercent = GetBiomePercent(biomeComposition, dominantBiome); string directionalPrefix = GetDirectionalPrefix(gridPos, random); string text = GetBiomeName(dominantBiome, random); if (biomePercent < 70f && biomeComposition.Count > 1) { Biome secondaryBiome = GetSecondaryBiome(biomeComposition, dominantBiome); if (secondaryBiome != dominantBiome) { string text2 = MixedSuffixes[random.Next(MixedSuffixes.Length)]; text = GetShortBiomeName(dominantBiome) + "-" + GetShortBiomeName(secondaryBiome) + " " + text2; } } if (hasMerchant) { string text3 = MerchantModifiers[random.Next(MerchantModifiers.Length)]; return directionalPrefix + " " + text3; } if (hasBossAltar) { string text4 = BossModifiers[random.Next(BossModifiers.Length)]; return text + " " + text4; } return directionalPrefix + " " + text; } public static string GetSimpleName(Vector2i gridPos, Dictionary biomeComposition) { //IL_0011: 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_002b: Unknown result type (might be due to invalid IL or missing references) if (biomeComposition == null || biomeComposition.Count == 0) { return "Unknown"; } Random rng = new Random(gridPos.x * 10000 + gridPos.y); return GetBiomeName(GetDominantBiome(biomeComposition), rng); } private static string GetDirectionalPrefix(Vector2i gridPos, Random rng) { //IL_0005: 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_000b: Unknown result type (might be due to invalid IL or missing references) Vector3 regionCenter = VoronoiGrid.Instance.GetRegionCenter(gridPos); float x = regionCenter.x; float z = regionCenter.z; if (Mathf.Sqrt(x * x + z * z) < 2000f) { return CenterPrefixes[rng.Next(CenterPrefixes.Length)]; } float num = Mathf.Atan2(z, x) * 57.29578f; if (num >= 45f && num < 135f) { return NorthPrefixes[rng.Next(NorthPrefixes.Length)]; } if (num >= -45f && num < 45f) { return EastPrefixes[rng.Next(EastPrefixes.Length)]; } if (num >= -135f && num < -45f) { return SouthPrefixes[rng.Next(SouthPrefixes.Length)]; } return WestPrefixes[rng.Next(WestPrefixes.Length)]; } private static string GetBiomeName(Biome biome, Random rng) { //IL_0005: 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_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_0023: 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) if (BiomeNames.TryGetValue(biome, out var value)) { return value[rng.Next(value.Length)]; } Biome val = ResolveBiomeFlag(biome); if (val != biome && BiomeNames.TryGetValue(val, out var value2)) { return value2[rng.Next(value2.Length)]; } return "Wilderness"; } private static Biome ResolveBiomeFlag(Biome biome) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_0003: Unknown result type (might be due to invalid IL or missing references) //IL_0005: Invalid comparison between Unknown and I4 //IL_000a: Unknown result type (might be due to invalid IL or missing references) //IL_000d: Unknown result type (might be due to invalid IL or missing references) //IL_000f: Invalid comparison between Unknown and I4 //IL_0014: Unknown result type (might be due to invalid IL or missing references) //IL_001a: Unknown result type (might be due to invalid IL or missing references) //IL_001c: Invalid comparison between Unknown and I4 //IL_0024: Unknown result type (might be due to invalid IL or missing references) //IL_0027: Unknown result type (might be due to invalid IL or missing references) //IL_0029: Invalid comparison between Unknown and I4 //IL_002e: Unknown result type (might be due to invalid IL or missing references) //IL_0030: Unknown result type (might be due to invalid IL or missing references) //IL_0032: Invalid comparison between Unknown and I4 //IL_0036: Unknown result type (might be due to invalid IL or missing references) //IL_0038: Unknown result type (might be due to invalid IL or missing references) //IL_003a: Invalid comparison between Unknown and I4 //IL_003e: Unknown result type (might be due to invalid IL or missing references) //IL_0040: Unknown result type (might be due to invalid IL or missing references) //IL_0042: Invalid comparison between Unknown and I4 //IL_0046: Unknown result type (might be due to invalid IL or missing references) //IL_0048: Unknown result type (might be due to invalid IL or missing references) //IL_004a: Invalid comparison between Unknown and I4 //IL_004e: Unknown result type (might be due to invalid IL or missing references) //IL_0054: Unknown result type (might be due to invalid IL or missing references) //IL_0056: Invalid comparison between Unknown and I4 //IL_005e: Unknown result type (might be due to invalid IL or missing references) if ((biome & 0x20) > 0) { return (Biome)32; } if ((biome & 0x40) > 0) { return (Biome)64; } if ((biome & 0x200) > 0) { return (Biome)512; } if ((biome & 0x10) > 0) { return (Biome)16; } if ((biome & 4) > 0) { return (Biome)4; } if ((biome & 2) > 0) { return (Biome)2; } if ((biome & 8) > 0) { return (Biome)8; } if ((biome & 1) > 0) { return (Biome)1; } if ((biome & 0x100) > 0) { return (Biome)256; } return biome; } private static string GetShortBiomeName(Biome biome) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_0003: Unknown result type (might be due to invalid IL or missing references) //IL_0005: Invalid comparison between Unknown and I4 //IL_000d: Unknown result type (might be due to invalid IL or missing references) //IL_0010: Unknown result type (might be due to invalid IL or missing references) //IL_0012: Invalid comparison between Unknown and I4 //IL_001a: 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_0022: Invalid comparison between Unknown and I4 //IL_002a: Unknown result type (might be due to invalid IL or missing references) //IL_002d: Unknown result type (might be due to invalid IL or missing references) //IL_002f: Invalid comparison between Unknown and I4 //IL_0037: Unknown result type (might be due to invalid IL or missing references) //IL_0039: Unknown result type (might be due to invalid IL or missing references) //IL_003b: Invalid comparison between Unknown and I4 //IL_0043: Unknown result type (might be due to invalid IL or missing references) //IL_0045: Unknown result type (might be due to invalid IL or missing references) //IL_0047: Invalid comparison between Unknown and I4 //IL_004f: Unknown result type (might be due to invalid IL or missing references) //IL_0051: Unknown result type (might be due to invalid IL or missing references) //IL_0053: Invalid comparison between Unknown and I4 //IL_005b: Unknown result type (might be due to invalid IL or missing references) //IL_005d: Unknown result type (might be due to invalid IL or missing references) //IL_005f: Invalid comparison between Unknown and I4 //IL_0067: Unknown result type (might be due to invalid IL or missing references) //IL_006d: Unknown result type (might be due to invalid IL or missing references) //IL_006f: Invalid comparison between Unknown and I4 if ((biome & 0x20) > 0) { return "Ash"; } if ((biome & 0x40) > 0) { return "Frost"; } if ((biome & 0x200) > 0) { return "Mist"; } if ((biome & 0x10) > 0) { return "Plains"; } if ((biome & 4) > 0) { return "Mountain"; } if ((biome & 2) > 0) { return "Swamp"; } if ((biome & 8) > 0) { return "Forest"; } if ((biome & 1) > 0) { return "Meadow"; } if ((biome & 0x100) > 0) { return "Sea"; } return "Wilds"; } private static Biome GetDominantBiome(Dictionary composition) { int num = 1; float num2 = 0f; foreach (KeyValuePair item in composition) { if (item.Value > num2) { num2 = item.Value; num = item.Key; } } return (Biome)num; } private static Biome GetSecondaryBiome(Dictionary composition, Biome exclude) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_0002: Expected I4, but got Unknown //IL_0020: Unknown result type (might be due to invalid IL or missing references) //IL_0021: Invalid comparison between I4 and Unknown int num = (int)exclude; float num2 = 0f; foreach (KeyValuePair item in composition) { if (item.Key != (int)exclude && item.Value > num2) { num2 = item.Value; num = item.Key; } } return (Biome)num; } private static float GetBiomePercent(Dictionary composition, Biome biome) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) //IL_0009: Expected I4, but got Unknown if (!composition.TryGetValue((int)biome, out var value)) { return 0f; } return value; } public static string GetBiomeCompositionDescription(Dictionary composition) { if (composition == null || composition.Count == 0) { return "Unknown terrain"; } List list = new List(); List> list2 = new List>(composition); list2.Sort((KeyValuePair a, KeyValuePair b) => b.Value.CompareTo(a.Value)); foreach (KeyValuePair item in list2) { if (item.Value >= 5f) { string biomeName = BiomeUtils.GetBiomeName((Biome)item.Key); list.Add($"{biomeName} {item.Value:F0}%"); } } return string.Join(", ", list); } } public class VoronoiGrid { private static VoronoiGrid _instance; private static readonly object _lock = new object(); private List _seeds; private Dictionary _quantizedToIndex; private Dictionary> _spatialGrid; private const float SpatialCellSize = 2000f; private Dictionary _regionCache; private const float CacheQuantize = 8f; private float MapRadius = 10500f; private float _spawnProtectionRadius; public static VoronoiGrid Instance { get { if (_instance == null) { lock (_lock) { if (_instance == null) { _instance = new VoronoiGrid(); } } } return _instance; } } public bool IsInitialized { get; private set; } public int SpawnZoneSeedIndex => 0; public int RegionCount { get { EnsureInitialized(); return _seeds.Count; } } public static void Reset() { lock (_lock) { _instance = null; } } private VoronoiGrid() { _seeds = new List(); _quantizedToIndex = new Dictionary(); _spatialGrid = new Dictionary>(); _regionCache = new Dictionary(); IsInitialized = false; } public void Initialize(string worldName) { if (!IsInitialized) { _spawnProtectionRadius = ClanConfig.SpawnProtectionRadius?.Value ?? 2500; MapRadius = 10500f; int num = 180; int num2 = worldName?.GetHashCode() ?? 0; Plugin.Log.LogWarning((object)$"VoronoiGrid: MapRadius={MapRadius}, TerritoryCount={num}, SpawnProtection={_spawnProtectionRadius}, WorldSeed={num2}"); GenerateSeeds(num2, num); BuildSpatialGrid(); BuildQuantizedIndex(); IsInitialized = true; Plugin.Log.LogWarning((object)$"VoronoiGrid initialized with {_seeds.Count} regions for world '{worldName}' (radius={MapRadius})"); } } public void EnsureInitialized() { //IL_000f: Unknown result type (might be due to invalid IL or missing references) //IL_001a: Expected O, but got Unknown if (!IsInitialized) { string text = null; if ((Object)ZNet.instance != (Object)null) { text = ZNet.instance.GetWorldName(); } if (string.IsNullOrEmpty(text)) { text = "default_world"; } Initialize(text); } } private void GenerateSeeds(int worldSeed, int targetCount) { //IL_0022: Unknown result type (might be due to invalid IL or missing references) //IL_0116: Unknown result type (might be due to invalid IL or missing references) //IL_012b: Unknown result type (might be due to invalid IL or missing references) //IL_0171: Unknown result type (might be due to invalid IL or missing references) //IL_0234: Unknown result type (might be due to invalid IL or missing references) //IL_0249: Unknown result type (might be due to invalid IL or missing references) //IL_028f: Unknown result type (might be due to invalid IL or missing references) _seeds.Clear(); Random random = new Random(worldSeed); _seeds.Add(new Vector2(0f, 0f)); float num = MapRadius * 2f / Mathf.Sqrt((float)targetCount) * 0.6f; int num2 = targetCount * 80; int num3 = 0; while (_seeds.Count < targetCount && num3 < num2) { num3++; float num4 = (float)(random.NextDouble() * 2.0 * Math.PI); float num5 = (float)Math.Sqrt(random.NextDouble()) * (MapRadius - 200f); float num6 = num5 * Mathf.Cos(num4); float num7 = num5 * Mathf.Sin(num4); if (num6 * num6 + num7 * num7 < _spawnProtectionRadius * _spawnProtectionRadius) { continue; } float num8 = Mathf.Sqrt(num6 * num6 + num7 * num7) / MapRadius; float num9 = 0.3f + num8 * 1.5f; float num10 = 0.7f + (float)random.NextDouble() * 0.6f; float num11 = num9 * num10; float num12 = num * num11; bool flag = false; for (int i = 0; i < _seeds.Count; i++) { float num13 = _seeds[i].x - num6; float num14 = _seeds[i].y - num7; if (num13 * num13 + num14 * num14 < num12 * num12) { flag = true; break; } } if (!flag) { _seeds.Add(new Vector2(num6, num7)); } } float num15 = num * 0.4f; float[] array = new float[3] { MapRadius * 0.7f, MapRadius * 0.82f, MapRadius * 0.93f }; int num16 = 45; for (int j = 0; j < array.Length; j++) { float num17 = array[j]; float num18 = (float)j * 0.5f; for (int k = 0; k < num16; k++) { float num19 = ((float)k + num18) / (float)num16 * (float)Math.PI * 2f; float num20 = num17 * Mathf.Cos(num19); float num21 = num17 * Mathf.Sin(num19); bool flag2 = false; for (int l = 0; l < _seeds.Count; l++) { float num22 = _seeds[l].x - num20; float num23 = _seeds[l].y - num21; if (num22 * num22 + num23 * num23 < num15 * num15) { flag2 = true; break; } } if (!flag2) { _seeds.Add(new Vector2(num20, num21)); } } } for (int m = 0; m < 3; m++) { LloydRelaxation(); } Plugin.Log.LogInfo((object)$"Generated {_seeds.Count} Voronoi seeds (target: {targetCount}, attempts: {num3})"); } private void LloydRelaxation() { //IL_016d: Unknown result type (might be due to invalid IL or missing references) int num = 80; float num2 = MapRadius * 2f / (float)num; float[] array = new float[_seeds.Count]; float[] array2 = new float[_seeds.Count]; int[] array3 = new int[_seeds.Count]; for (int i = 0; i < num; i++) { for (int j = 0; j < num; j++) { float num3 = 0f - MapRadius + ((float)i + 0.5f) * num2; float num4 = 0f - MapRadius + ((float)j + 0.5f) * num2; if (!(num3 * num3 + num4 * num4 > MapRadius * MapRadius)) { int num5 = FindNearestSeedBruteForce(num3, num4); if (num5 >= 0) { array[num5] += num3; array2[num5] += num4; array3[num5]++; } } } } for (int k = 0; k < _seeds.Count; k++) { if (k != SpawnZoneSeedIndex && array3[k] > 0) { float num6 = array[k] / (float)array3[k]; float num7 = array2[k] / (float)array3[k]; if (num6 * num6 + num7 * num7 >= _spawnProtectionRadius * _spawnProtectionRadius && num6 * num6 + num7 * num7 < MapRadius * MapRadius) { _seeds[k] = new Vector2(num6, num7); } } } } private float SmoothNoise(float x, float z, int seed) { int num = Mathf.FloorToInt(x); int num2 = Mathf.FloorToInt(z); float num3 = x - (float)num; float num4 = z - (float)num2; num3 = num3 * num3 * (3f - 2f * num3); num4 = num4 * num4 * (3f - 2f * num4); float num5 = HashFloat(num, num2, seed); float num6 = HashFloat(num + 1, num2, seed); float num7 = HashFloat(num, num2 + 1, seed); float num8 = HashFloat(num + 1, num2 + 1, seed); float num9 = num5 + (num6 - num5) * num3; float num10 = num7 + (num8 - num7) * num3; return num9 + (num10 - num9) * num4; } private float HashFloat(int x, int z, int seed) { int num = x * 374761393 + z * 668265263 + seed * 1274126177; int num2 = (num ^ (num >> 13)) * 1103515245; return (float)((num2 ^ (num2 >> 16)) & 0x7FFFFFFF) / 2.1474836E+09f; } private float PerturbedDistanceSq(float x, float z, int seedIndex) { //IL_0007: Unknown result type (might be due to invalid IL or missing references) //IL_001a: Unknown result type (might be due to invalid IL or missing references) float num = _seeds[seedIndex].x - x; float num2 = _seeds[seedIndex].y - z; float num3 = Mathf.Sqrt(num * num + num2 * num2); float num4 = (float)seedIndex * 73.7f; float num5 = (float)seedIndex * 91.3f; float num6 = 0f; num6 += (Mathf.PerlinNoise((x + num4) * 0.006f, (z + num5) * 0.006f) - 0.5f) * 2f; num6 += (Mathf.PerlinNoise((x + num4) * 0.015f, (z + num5) * 0.015f) - 0.5f) * 1f; num6 += (Mathf.PerlinNoise((x + num4) * 0.03f, (z + num5) * 0.03f) - 0.5f) * 0.5f; num6 += (Mathf.PerlinNoise((x + num4) * 0.06f, (z + num5) * 0.06f) - 0.5f) * 0.25f; float num7 = num3 + num6 * 80f; return num7 * num7; } private int FindNearestSeedBruteForce(float x, float z) { int result = -1; float num = float.MaxValue; for (int i = 0; i < _seeds.Count; i++) { float num2 = PerturbedDistanceSq(x, z, i); if (num2 < num) { num = num2; result = i; } } return result; } private void BuildSpatialGrid() { //IL_0017: Unknown result type (might be due to invalid IL or missing references) //IL_0028: Unknown result type (might be due to invalid IL or missing references) _spatialGrid.Clear(); for (int i = 0; i < _seeds.Count; i++) { long key = SpatialKey(_seeds[i].x, _seeds[i].y); if (!_spatialGrid.TryGetValue(key, out var value)) { value = new List(); _spatialGrid[key] = value; } value.Add(i); } } private long SpatialKey(float x, float z) { int num = Mathf.FloorToInt(x / 2000f); int num2 = Mathf.FloorToInt(z / 2000f); return ((long)num << 32) | (uint)num2; } private void BuildQuantizedIndex() { //IL_0011: Unknown result type (might be due to invalid IL or missing references) //IL_0016: Unknown result type (might be due to invalid IL or missing references) //IL_0017: 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) _quantizedToIndex.Clear(); for (int i = 0; i < _seeds.Count; i++) { Vector2i val = QuantizeSeed(i); long key = ((long)val.x << 32) | (uint)val.y; _quantizedToIndex[key] = i; } } private Vector2i QuantizeSeed(int index) { //IL_0007: 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_0031: Unknown result type (might be due to invalid IL or missing references) Vector2 val = _seeds[index]; int num = Mathf.RoundToInt(val.x / 10f); int num2 = Mathf.RoundToInt(val.y / 10f); return new Vector2i(num, num2); } public Vector2i GetRegion(Vector3 worldPos) { //IL_0007: Unknown result type (might be due to invalid IL or missing references) //IL_000d: 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_0032: Unknown result type (might be due to invalid IL or missing references) //IL_0040: Unknown result type (might be due to invalid IL or missing references) //IL_0045: Unknown result type (might be due to invalid IL or missing references) //IL_0029: 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_0070: Unknown result type (might be due to invalid IL or missing references) EnsureInitialized(); long key = CacheKey(worldPos.x, worldPos.z); if (_regionCache.TryGetValue(key, out var value)) { return value; } int index = FindNearestSeed(worldPos.x, worldPos.z); Vector2i val = QuantizeSeed(index); if (_regionCache.Count > 500000) { _regionCache.Clear(); } _regionCache[key] = val; return val; } public Vector3 GetRegionCenter(Vector2i regionId) { //IL_0007: Unknown result type (might be due to invalid IL or missing references) //IL_0044: Unknown result type (might be due to invalid IL or missing references) //IL_0056: Unknown result type (might be due to invalid IL or missing references) //IL_0063: Unknown result type (might be due to invalid IL or missing references) //IL_0027: 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_002d: Unknown result type (might be due to invalid IL or missing references) //IL_0038: Unknown result type (might be due to invalid IL or missing references) //IL_003e: Unknown result type (might be due to invalid IL or missing references) EnsureInitialized(); int num = RegionIdToIndex(regionId); if (num >= 0 && num < _seeds.Count) { Vector2 val = _seeds[num]; return new Vector3(val.x, 0f, val.y); } return new Vector3((float)regionId.x * 10f, 0f, (float)regionId.y * 10f); } public bool IsSpawnZone(Vector2i regionId) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) return RegionIdToIndex(regionId) == SpawnZoneSeedIndex; } public string GetRegionKey(Vector2i regionId) { //IL_0005: Unknown result type (might be due to invalid IL or missing references) //IL_0010: Unknown result type (might be due to invalid IL or missing references) return $"{regionId.x}_{regionId.y}"; } public bool IsOnBorder(Vector3 worldPos, float threshold = 80f) { //IL_0006: Unknown result type (might be due to invalid IL or missing references) //IL_000d: Unknown result type (might be due to invalid IL or missing references) //IL_008d: Unknown result type (might be due to invalid IL or missing references) //IL_00a1: Unknown result type (might be due to invalid IL or missing references) EnsureInitialized(); float x = worldPos.x; float z = worldPos.z; int num = -1; int num2 = -1; float num3 = float.MaxValue; float num4 = float.MaxValue; int num5 = Mathf.FloorToInt(x / 2000f); int num6 = Mathf.FloorToInt(z / 2000f); for (int i = -1; i <= 1; i++) { for (int j = -1; j <= 1; j++) { long key = ((long)(num5 + i) << 32) | (uint)(num6 + j); if (!_spatialGrid.TryGetValue(key, out var value)) { continue; } for (int k = 0; k < value.Count; k++) { int num7 = value[k]; float num8 = _seeds[num7].x - x; float num9 = _seeds[num7].y - z; float num10 = num8 * num8 + num9 * num9; if (num10 < num3) { num4 = num3; num2 = num; num3 = num10; num = num7; } else if (num10 < num4) { num4 = num10; num2 = num7; } } } } if (num < 0 || num2 < 0) { return false; } float num11 = Mathf.Sqrt(num3); return Mathf.Sqrt(num4) - num11 < threshold; } public float GetApproximateRegionRadius(Vector2i regionId) { //IL_0007: Unknown result type (might be due to invalid IL or missing references) //IL_001f: Unknown result type (might be due to invalid IL or missing references) //IL_0024: Unknown result type (might be due to invalid IL or missing references) //IL_003a: Unknown result type (might be due to invalid IL or missing references) //IL_0044: Unknown result type (might be due to invalid IL or missing references) //IL_0052: 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) EnsureInitialized(); int num = RegionIdToIndex(regionId); if (num < 0) { return 500f; } Vector2 val = _seeds[num]; float num2 = float.MaxValue; for (int i = 0; i < _seeds.Count; i++) { if (i != num) { float num3 = _seeds[i].x - val.x; float num4 = _seeds[i].y - val.y; float num5 = num3 * num3 + num4 * num4; if (num5 < num2) { num2 = num5; } } } return Mathf.Sqrt(num2) * 0.5f; } public IReadOnlyList GetAllSeeds() { EnsureInitialized(); return _seeds; } public int RegionIdToIndex(Vector2i regionId) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_000a: Unknown result type (might be due to invalid IL or missing references) long key = ((long)regionId.x << 32) | (uint)regionId.y; if (_quantizedToIndex.TryGetValue(key, out var value)) { return value; } return -1; } public Vector2i IndexToRegionId(int index) { //IL_0014: Unknown result type (might be due to invalid IL or missing references) //IL_001c: Unknown result type (might be due to invalid IL or missing references) if (index < 0 || index >= _seeds.Count) { return new Vector2i(0, 0); } return QuantizeSeed(index); } private int FindNearestSeed(float x, float z) { int num = -1; float num2 = float.MaxValue; int num3 = Mathf.FloorToInt(x / 2000f); int num4 = Mathf.FloorToInt(z / 2000f); for (int i = -2; i <= 2; i++) { for (int j = -2; j <= 2; j++) { long key = ((long)(num3 + i) << 32) | (uint)(num4 + j); if (!_spatialGrid.TryGetValue(key, out var value)) { continue; } for (int k = 0; k < value.Count; k++) { int num5 = value[k]; float num6 = PerturbedDistanceSq(x, z, num5); if (num6 < num2) { num2 = num6; num = num5; } } } } if (num < 0) { num = FindNearestSeedBruteForce(x, z); } return num; } private long CacheKey(float x, float z) { int num = Mathf.FloorToInt(x / 8f); int num2 = Mathf.FloorToInt(z / 8f); return ((long)num << 32) | (uint)num2; } } } namespace FortecaClanWarfare.UI { public static class ClanMapDrawingUi { private static ClanMapDrawingSnapshot s; private static long world; private static int clan; private static bool edit; private static bool clear; private static bool show = true; private static bool names; private static bool mode; private static bool drag; private static bool busy; private static bool armed; private static float until; private static string selected = ""; private static string title = ""; private static string msg = ""; private static int type; private static int color; private static int thickness; private static bool important; private static readonly List preview = new List(); private static readonly List> strokes = new List>(); private static int pendingSaves; private static readonly Color[] C = (Color[])(object)new Color[8] { new Color(0.95f, 0.72f, 0.18f), new Color(0.9f, 0.2f, 0.15f), new Color(0.15f, 0.65f, 0.95f), new Color(0.2f, 0.8f, 0.45f), new Color(0.7f, 0.3f, 0.95f), new Color(1f, 0.48f, 0.1f), Color.white, new Color(0.15f, 0.85f, 0.8f) }; private static Texture2D px; public static bool DrawingMode => mode; public static bool LayerVisible => show; public static void Configure(ClanMapSnapshot x) { if (x == null) { ClearAll(); return; } bool num = world != x.WorldUID || clan != x.ClanID; world = x.WorldUID; clan = x.ClanID; edit = x.CanEdit; clear = x.CanClearDrawings; if (num) { ClearAll(identity: false); ClanMapRPC.RequestDrawingSnapshot(); } } public static void ClearAll(bool identity = true) { s = null; selected = ""; preview.Clear(); strokes.Clear(); pendingSaves = 0; mode = (drag = (busy = (armed = false))); msg = ""; if (identity) { world = 0L; clan = 0; edit = (clear = false); } } public static void Refresh() { if (clan != 0) { ClanMapRPC.RequestDrawingSnapshot(); } } public static void ReceiveSnapshot(ClanMapDrawingSnapshot x) { if (x == null || x.ProtocolVersion != 5 || x.WorldUID != world || x.ClanID != clan) { ReceiveDenied("Invalid drawing snapshot."); return; } s = x; busy = false; if (selected != "" && Find(selected) == null) { Cancel(); } } public static void ReceiveResult(string x) { msg = x; if (pendingSaves > 0) { pendingSaves--; } if (pendingSaves == 0) { busy = false; mode = (drag = false); preview.Clear(); strokes.Clear(); selected = ""; } } public static void ReceiveDenied(string x) { msg = x; busy = false; } public static void ReceiveConflict(string x) { msg = x; busy = false; Refresh(); } public static void ToggleLayer(GUIStyle b, bool enabled) { bool enabled2 = GUI.enabled; GUI.enabled = enabled; if (GUILayout.Button((show ? "[x] " : "[ ] ") + "Clan Drawings", b, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Height(25f) })) { show = !show; } if (!show) { Cancel(); } GUI.enabled = enabled2; } public static void DrawDisplay(GUIStyle g, GUIStyle b) { GUILayout.Label("DRAWING DISPLAY", g, Array.Empty()); if (GUILayout.Button((names ? "[x] " : "[ ] ") + "Show Drawing Names", b, Array.Empty())) { names = !names; } } public static void DrawToolbar(GUIStyle b) { bool enabled = GUI.enabled; GUI.enabled = edit && !busy; if (GUILayout.Button(mode ? "Cancel Draw" : "Draw", b, (GUILayoutOption[])(object)new GUILayoutOption[2] { GUILayout.Width(78f), GUILayout.Height(26f) })) { if (mode) { Cancel(); } else { mode = true; selected = ""; preview.Clear(); strokes.Clear(); title = ""; type = (color = (thickness = 0)); important = false; msg = "Choose style, then drag on the map."; ClanWorldMapPanel.OpenDrawingSection(); } } GUI.enabled = enabled; } public static void DrawEditor(GUIStyle l, GUIStyle g, GUIStyle b) { ClanMapDrawingDto clanMapDrawingDto = Find(selected); if (clanMapDrawingDto == null && strokes.Count == 0 && preview.Count == 0 && !mode) { GUILayout.Label(edit ? "Choose Draw or select a clan drawing." : "Select a clan drawing to view it.", l, Array.Empty()); if (msg != "") { GUILayout.Label(msg, l, Array.Empty()); } Admin(l, b); return; } GUILayout.Label((clanMapDrawingDto == null) ? "NEW CLAN DRAWING" : ((title == "") ? "CLAN DRAWING" : title), g, Array.Empty()); bool enabled = GUI.enabled; GUI.enabled = edit && !busy; ClanMapDrawingType clanMapDrawingType = (ClanMapDrawingType)type; if (GUILayout.Button("Tool: " + clanMapDrawingType, b, Array.Empty())) { type = 1 - type; } if (GUILayout.Button("Color: " + (color + 1), b, Array.Empty())) { color = (color + 1) % 8; } if (GUILayout.Button("Thickness: " + (thickness + 1), b, Array.Empty())) { thickness = (thickness + 1) % 3; } important = GUILayout.Toggle(important, "Important", l, Array.Empty()); GUILayout.Label("Title", l, Array.Empty()); title = GUILayout.TextField(title ?? "", 48, Array.Empty()); if (clanMapDrawingDto == null && !mode && GUILayout.Button("Start Drawing", b, Array.Empty())) { mode = true; preview.Clear(); strokes.Clear(); msg = "Drag on the map. Escape cancels. LMB draws; RMB pans."; } if ((drag || preview.Count > 0 || strokes.Count > 0) && GUILayout.Button("Undo Last Stroke", b, Array.Empty())) { if (drag || preview.Count > 0) { drag = false; preview.Clear(); } else if (strokes.Count > 0) { strokes.RemoveAt(strokes.Count - 1); } } if ((clanMapDrawingDto != null || strokes.Count > 0) && GUILayout.Button((clanMapDrawingDto == null) ? "Save Drawing" : "Save Changes", b, Array.Empty())) { Save(clanMapDrawingDto); } if (clanMapDrawingDto != null && GUILayout.Button("Delete", b, Array.Empty())) { busy = ClanMapRPC.RequestDrawingDelete(clanMapDrawingDto.DrawingID, clanMapDrawingDto.Revision); } GUI.enabled = enabled; if (clanMapDrawingDto != null) { GUILayout.Label("Created by: " + clanMapDrawingDto.CreatedByName, l, Array.Empty()); GUILayout.Label("Updated by: " + clanMapDrawingDto.UpdatedByName, l, Array.Empty()); } if (GUILayout.Button("Cancel", b, Array.Empty())) { Cancel(); } if (msg != "") { GUILayout.Label(msg, l, Array.Empty()); } Admin(l, b); } private static void Admin(GUIStyle l, GUIStyle b) { if (!clear || (s?.Drawings?.Count).GetValueOrDefault() == 0) { return; } if (armed && Time.unscaledTime > until) { armed = false; } GUILayout.Space(12f); GUILayout.Label("CLAN DRAWING ADMINISTRATION", l, Array.Empty()); if (!armed) { if (GUILayout.Button("Clear All Clan Drawings", b, Array.Empty())) { armed = true; until = Time.unscaledTime + 8f; } return; } GUILayout.Label("Permanently delete all drawings for this world.", l, Array.Empty()); if (GUILayout.Button("Confirm Clear All", b, Array.Empty())) { busy = ClanMapRPC.RequestDrawingClear(); armed = false; } if (GUILayout.Button("Cancel Clear", b, Array.Empty())) { armed = false; } } private static void Save(ClanMapDrawingDto d) { if (string.IsNullOrWhiteSpace(title)) { msg = "Drawing title is required."; return; } if (d != null) { busy = ClanMapRPC.RequestDrawingUpdate(new ClanMapDrawingMutationRequest { DrawingID = d.DrawingID, ExpectedRevision = d.Revision, DrawingType = type, ColorIndex = color, Thickness = thickness, IsImportant = important, Title = title, Points = new List(d.Points) }); return; } List> list = new List>(strokes); if (list.Count == 0) { return; } pendingSaves = list.Count; busy = true; mode = false; int num = 0; for (int i = 0; i < list.Count; i++) { if (ClanMapRPC.RequestDrawingCreate(new ClanMapDrawingMutationRequest { DrawingType = type, ColorIndex = color, Thickness = thickness, IsImportant = important, Title = ((list.Count == 1) ? title : (title + " " + (i + 1))), Points = new List(list[i]) })) { num++; } else { pendingSaves--; } } if (num == 0) { busy = false; mode = true; msg = "Drawing request unavailable."; } } private static ClanMapDrawingDto Find(string id) { return s?.Drawings?.Find((ClanMapDrawingDto x) => x.DrawingID == id); } private static void Load(ClanMapDrawingDto d) { selected = d.DrawingID; type = d.DrawingType; color = d.ColorIndex; thickness = d.Thickness; important = d.IsImportant; title = d.Title; preview.Clear(); strokes.Clear(); preview.AddRange(d.Points); } public static void Cancel() { selected = ""; preview.Clear(); strokes.Clear(); pendingSaves = 0; mode = (drag = (busy = (armed = false))); msg = ""; } public static void Render(Rect local, Vector2 center, float baseD, float zoom, GUIStyle style) { //IL_000f: Unknown result type (might be due to invalid IL or missing references) //IL_0019: Expected O, but got Unknown //IL_0020: 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_00e9: Unknown result type (might be due to invalid IL or missing references) //IL_012e: Unknown result type (might be due to invalid IL or missing references) //IL_0131: Unknown result type (might be due to invalid IL or missing references) //IL_0089: Unknown result type (might be due to invalid IL or missing references) //IL_008c: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)px == (Object)null) { px = new Texture2D(1, 1); px.SetPixel(0, 0, Color.white); px.Apply(); } if (show && s?.Drawings != null) { foreach (ClanMapDrawingDto drawing in s.Drawings) { Line(drawing.Points, drawing.ColorIndex, drawing.Thickness, drawing.IsImportant, drawing.DrawingType, center, baseD, zoom, local, style, drawing.Title, drawing.DrawingID == selected); } } for (int i = 0; i < strokes.Count; i++) { Line(strokes[i], color, thickness, important, type, center, baseD, zoom, local, style, title, sel: true); } if (preview.Count > 1) { Line(preview, color, thickness, important, type, center, baseD, zoom, local, style, title, sel: true); } } private static void Line(List p, int ci, int th, bool imp, int ty, Vector2 c, float bd, float z, Rect local, GUIStyle st, string n, bool sel) { //IL_0027: 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_003a: Unknown result type (might be due to invalid IL or missing references) //IL_0051: Unknown result type (might be due to invalid IL or missing references) //IL_0054: Unknown result type (might be due to invalid IL or missing references) //IL_0060: Unknown result type (might be due to invalid IL or missing references) //IL_0063: Unknown result type (might be due to invalid IL or missing references) //IL_0144: 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_0165: Unknown result type (might be due to invalid IL or missing references) //IL_0168: Unknown result type (might be due to invalid IL or missing references) //IL_0182: Unknown result type (might be due to invalid IL or missing references) //IL_00a6: 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_00ae: Unknown result type (might be due to invalid IL or missing references) //IL_00be: Unknown result type (might be due to invalid IL or missing references) //IL_00c1: Unknown result type (might be due to invalid IL or missing references) //IL_00c6: Unknown result type (might be due to invalid IL or missing references) //IL_00c8: Unknown result type (might be due to invalid IL or missing references) //IL_00cd: Unknown result type (might be due to invalid IL or missing references) //IL_00d1: Unknown result type (might be due to invalid IL or missing references) //IL_00d6: Unknown result type (might be due to invalid IL or missing references) //IL_00d8: Unknown result type (might be due to invalid IL or missing references) //IL_00da: Unknown result type (might be due to invalid IL or missing references) //IL_00dc: Unknown result type (might be due to invalid IL or missing references) //IL_00e3: 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_00fd: Unknown result type (might be due to invalid IL or missing references) //IL_00ff: Unknown result type (might be due to invalid IL or missing references) //IL_0101: Unknown result type (might be due to invalid IL or missing references) //IL_0108: Unknown result type (might be due to invalid IL or missing references) //IL_0112: Unknown result type (might be due to invalid IL or missing references) //IL_0117: Unknown result type (might be due to invalid IL or missing references) //IL_0190: Unknown result type (might be due to invalid IL or missing references) float r = bd * 0.5f * z; float num = (new float[3] { 2f, 4f, 7f })[Mathf.Clamp(th, 0, 2)]; Color val = GUI.color; GUI.color = C[Mathf.Clamp(ci, 0, 7)]; for (int i = 1; i < p.Count; i++) { Seg(P(p[i - 1], c, r), P(p[i], c, r), num + (float)(imp ? 2 : 0)); } if (ty == 1 && p.Count > 1) { Vector2 val2 = P(p[p.Count - 1], c, r); Vector2 val3 = P(p[p.Count - 2], c, r) - val2; Vector2 normalized = ((Vector2)(ref val3)).normalized; Seg(val2, val2 + Rot(normalized, 28f) * 18f, num); Seg(val2, val2 + Rot(normalized, -28f) * 18f, num); } if (sel || (names && z >= 2f)) { GUI.Label(new Rect(P(p[p.Count / 2], c, r).x - 70f, P(p[p.Count / 2], c, r).y + 8f, 140f, 20f), n, st); } GUI.color = val; } private static Vector2 Rot(Vector2 v, float d) { //IL_0008: Unknown result type (might be due to invalid IL or missing references) //IL_0015: Unknown result type (might be due to invalid IL or missing references) //IL_0023: Unknown result type (might be due to invalid IL or missing references) //IL_0030: Unknown result type (might be due to invalid IL or missing references) //IL_003e: Unknown result type (might be due to invalid IL or missing references) float num = d * ((float)Math.PI / 180f); return new Vector2(v.x * Mathf.Cos(num) - v.y * Mathf.Sin(num), v.x * Mathf.Sin(num) + v.y * Mathf.Cos(num)); } private static Vector2 P(ClanMapDrawingPoint p, Vector2 c, float r) { //IL_0000: 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_001e: Unknown result type (might be due to invalid IL or missing references) return new Vector2(c.x + p.NormalizedX * r, c.y - p.NormalizedY * r); } private static void Seg(Vector2 a, Vector2 b, float w) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) //IL_0002: 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_001e: Unknown result type (might be due to invalid IL or missing references) //IL_0024: Unknown result type (might be due to invalid IL or missing references) //IL_0029: Unknown result type (might be due to invalid IL or missing references) //IL_002a: Unknown result type (might be due to invalid IL or missing references) //IL_0038: Unknown result type (might be due to invalid IL or missing references) //IL_0048: Unknown result type (might be due to invalid IL or missing references) int num = Mathf.Max(1, Mathf.CeilToInt(Vector2.Distance(a, b) / 2f)); for (int i = 0; i <= num; i++) { Vector2 val = Vector2.Lerp(a, b, (float)i / (float)num); GUI.DrawTexture(new Rect(val.x - w / 2f, val.y - w / 2f, w, w), (Texture)(object)px); } } public static bool HandleInput(Rect local, Vector2 center, float bd, float zoom) { //IL_0010: Unknown result type (might be due to invalid IL or missing references) //IL_0016: Invalid comparison between Unknown and I4 //IL_0019: Unknown result type (might be due to invalid IL or missing references) //IL_0020: Invalid comparison between Unknown and I4 //IL_0042: Unknown result type (might be due to invalid IL or missing references) //IL_0096: Unknown result type (might be due to invalid IL or missing references) //IL_009c: Invalid comparison between Unknown and I4 //IL_004c: Unknown result type (might be due to invalid IL or missing references) //IL_00f4: Unknown result type (might be due to invalid IL or missing references) //IL_00fa: Invalid comparison between Unknown and I4 //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) //IL_0072: Unknown result type (might be due to invalid IL or missing references) //IL_0077: Unknown result type (might be due to invalid IL or missing references) //IL_010a: Unknown result type (might be due to invalid IL or missing references) //IL_010f: Unknown result type (might be due to invalid IL or missing references) //IL_00d6: Unknown result type (might be due to invalid IL or missing references) //IL_00db: Unknown result type (might be due to invalid IL or missing references) //IL_00c6: Unknown result type (might be due to invalid IL or missing references) //IL_00cb: Unknown result type (might be due to invalid IL or missing references) if (!mode) { return false; } Event current = Event.current; if ((int)current.type == 4 && (int)current.keyCode == 27) { Cancel(); current.Use(); return true; } float num = bd * 0.5f * zoom; if (current.button == 0 && (int)current.type == 0 && ((Rect)(ref local)).Contains(current.mousePosition) && Vector2.Distance(current.mousePosition, center) <= num) { preview.Clear(); Add(current.mousePosition, center, num); drag = true; current.Use(); return true; } if (current.button == 0 && (int)current.type == 3 && drag) { if (type == 1) { if (preview.Count > 1) { preview.RemoveAt(1); } Add(current.mousePosition, center, num, force: true); } else { Add(current.mousePosition, center, num); } current.Use(); return true; } if (current.button == 0 && (int)current.type == 1 && drag) { drag = false; Add(current.mousePosition, center, num, type == 1); Simplify(); if (preview.Count >= 2) { strokes.Add(new List(preview)); msg = "Stroke added. Continue drawing, Undo, Save or Cancel."; } preview.Clear(); current.Use(); return true; } return false; } private static void Add(Vector2 q, Vector2 c, float r, bool force = false) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_0001: Unknown result type (might be due to invalid IL or missing references) //IL_0002: Unknown result type (might be due to invalid IL or missing references) //IL_0007: Unknown result type (might be due to invalid IL or missing references) //IL_0022: Unknown result type (might be due to invalid IL or missing references) //IL_002a: Unknown result type (might be due to invalid IL or missing references) //IL_0016: Unknown result type (might be due to invalid IL or missing references) //IL_001c: Unknown result type (might be due to invalid IL or missing references) //IL_0021: 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) //IL_0064: 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_0070: Unknown result type (might be due to invalid IL or missing references) Vector2 val = q - c; if (((Vector2)(ref val)).sqrMagnitude > r * r) { val = ((Vector2)(ref val)).normalized * r; } ClanMapDrawingPoint clanMapDrawingPoint = ClanMapDrawingPoint.From(val.x / r, (0f - val.y) / r); if (force || preview.Count == 0 || Vector2.Distance(P(preview[preview.Count - 1], Vector2.zero, r), P(clanMapDrawingPoint, Vector2.zero, r)) >= 4f) { preview.Add(clanMapDrawingPoint); } } private static void Simplify() { if (preview.Count < 2) { return; } List list = new List { preview[0] }; for (int i = 1; i < preview.Count - 1; i++) { if (Mathf.Abs(preview[i].X - list[list.Count - 1].X) + Mathf.Abs(preview[i].Y - list[list.Count - 1].Y) >= 24) { list.Add(preview[i]); } } list.Add(preview[preview.Count - 1]); while (list.Count > 512) { List list2 = new List(); for (int j = 0; j < list.Count; j += 2) { list2.Add(list[j]); } if (list2[list2.Count - 1] != list[list.Count - 1]) { list2.Add(list[list.Count - 1]); } list = list2; } preview.Clear(); preview.AddRange(list); } public static bool TrySelect(Vector2 m, Vector2 c, float bd, float z) { //IL_004f: 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) //IL_0060: Unknown result type (might be due to invalid IL or missing references) //IL_0071: Unknown result type (might be due to invalid IL or missing references) //IL_0073: Unknown result type (might be due to invalid IL or missing references) if (!show || s?.Drawings == null) { return false; } float r = bd * 0.5f * z; for (int num = s.Drawings.Count - 1; num >= 0; num--) { ClanMapDrawingDto clanMapDrawingDto = s.Drawings[num]; for (int i = 1; i < clanMapDrawingDto.Points.Count; i++) { if (Dist(m, P(clanMapDrawingDto.Points[i - 1], c, r), P(clanMapDrawingDto.Points[i], c, r)) <= (float)(8 + thickness * 2)) { Load(clanMapDrawingDto); return true; } } } return false; } private static float Dist(Vector2 p, Vector2 a, Vector2 b) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_0001: Unknown result type (might be due to invalid IL or missing references) //IL_0002: Unknown result type (might be due to invalid IL or missing references) //IL_0007: Unknown result type (might be due to invalid IL or missing references) //IL_0016: Unknown result type (might be due to invalid IL or missing references) //IL_0017: 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_0038: Unknown result type (might be due to invalid IL or missing references) //IL_0039: Unknown result type (might be due to invalid IL or missing references) //IL_003a: Unknown result type (might be due to invalid IL or missing references) //IL_003c: Unknown result type (might be due to invalid IL or missing references) //IL_0041: Unknown result type (might be due to invalid IL or missing references) Vector2 val = b - a; float num = ((((Vector2)(ref val)).sqrMagnitude < 0.01f) ? 0f : Mathf.Clamp01(Vector2.Dot(p - a, val) / ((Vector2)(ref val)).sqrMagnitude)); return Vector2.Distance(p, a + val * num); } public static void DrawLegend(GUIStyle g, GUIStyle l) { GUILayout.Space(7f); GUILayout.Label("DRAWING TOOLS", g, Array.Empty()); GUILayout.Label("— Freehand route", l, Array.Empty()); GUILayout.Label("→ Arrow / direction", l, Array.Empty()); GUILayout.Label("Thickness: 1 / 2 / 3", l, Array.Empty()); } } public static class ClanMapPinUi { private sealed class PinHit { public ClanMapPinDto Pin; public Rect Rect; } private static ClanMapPinSnapshot _snapshot; private static readonly List Hits = new List(); private static long _world; private static int _clan; private static bool _canEdit; private static bool _canClear; private static bool _showLayer = true; private static bool _showNames = true; private static bool _importantOnly; private static bool _legendVisible = true; private static bool _placing; private static bool _creating; private static bool _busy; private static bool _clearArmed; private static float _clearUntil; private static string _selected = ""; private static string _title = ""; private static string _description = ""; private static string _message = ""; private static int _type; private static int _color; private static bool _important; private static float _nx; private static float _ny; private static readonly Color[] Colors = (Color[])(object)new Color[8] { new Color(0.92f, 0.72f, 0.18f), new Color(0.82f, 0.2f, 0.16f), new Color(0.18f, 0.65f, 0.9f), new Color(0.2f, 0.78f, 0.46f), new Color(0.72f, 0.3f, 0.9f), new Color(1f, 0.48f, 0.12f), new Color(0.85f, 0.85f, 0.85f), new Color(0.18f, 0.8f, 0.78f) }; public static bool LayerVisible => _showLayer; public static bool LegendVisible => _legendVisible; public static bool HasSelection { get { if (!_creating) { return !string.IsNullOrEmpty(_selected); } return true; } } public static bool PlacementMode => _placing; public static void Configure(ClanMapSnapshot s) { if (s == null) { ClearAll(); return; } bool num = _world != s.WorldUID || _clan != s.ClanID; _world = s.WorldUID; _clan = s.ClanID; _canEdit = s.CanEdit; _canClear = s.CanClearPins; if (num) { ClearAll(identity: false); ClanMapRPC.RequestPinSnapshot(); } } public static void ClearAll(bool identity = true) { _snapshot = null; _selected = ""; _placing = (_creating = (_busy = (_clearArmed = false))); _message = ""; Hits.Clear(); if (identity) { _world = 0L; _clan = 0; _canEdit = (_canClear = false); } } public static void Refresh() { if (_clan != 0) { ClanMapRPC.RequestPinSnapshot(); } } public static void ReceiveSnapshot(ClanMapPinSnapshot s) { if (s == null || s.ProtocolVersion != 5 || s.WorldUID != _world || s.ClanID != _clan) { ReceiveDenied("Invalid clan pin snapshot."); return; } _snapshot = s; _busy = false; if (!string.IsNullOrEmpty(_selected)) { ClanMapPinDto clanMapPinDto = Find(_selected); if (clanMapPinDto == null) { CancelSelection(); } else { Load(clanMapPinDto); } } } public static void ReceiveResult(string m) { _message = m ?? ""; _busy = false; _creating = false; _placing = false; } public static void ReceiveDenied(string m) { _message = m ?? "Clan pin access denied."; _busy = false; } public static void ReceiveConflict(string m) { _message = m ?? "This clan pin was changed by another officer. Refresh and try again."; _busy = false; Refresh(); } public static void DrawLayerControls(GUIStyle label, GUIStyle button, bool authorized) { Toggle("Territories", ClanWorldMapPanel.TerritoriesVisible, button, delegate(bool v) { ClanWorldMapPanel.TerritoriesVisible = v; }, authorized); Toggle("Clan Pins", _showLayer, button, delegate(bool v) { _showLayer = v; }, authorized); ClanMapDrawingUi.ToggleLayer(button, authorized); bool enabled = GUI.enabled; GUI.enabled = false; GUILayout.Button("[ ] Personal Pins", button, Array.Empty()); GUI.enabled = enabled; GUILayout.Label("Fogged strategic view active.", label, Array.Empty()); } public static void DrawDisplayControls(GUIStyle gold, GUIStyle button) { GUILayout.Space(6f); GUILayout.Label("PIN DISPLAY", gold, Array.Empty()); Toggle("Show Clan Pin Names", _showNames, button, delegate(bool v) { _showNames = v; }, enabled: true); Toggle("Important Only", _importantOnly, button, delegate(bool v) { _importantOnly = v; }, enabled: true); ClanMapDrawingUi.DrawDisplay(gold, button); } private static void Toggle(string n, bool value, GUIStyle style, Action set, bool enabled) { bool enabled2 = GUI.enabled; GUI.enabled = enabled; if (GUILayout.Button((value ? "[x] " : "[ ] ") + n, style, (GUILayoutOption[])(object)new GUILayoutOption[2] { GUILayout.ExpandWidth(true), GUILayout.Height(25f) })) { set(!value); } GUI.enabled = enabled2; } public static void DrawToolbar(GUIStyle button) { bool enabled = GUI.enabled; GUI.enabled = _canEdit && !_busy; if (GUILayout.Button(_placing ? "Cancel Pin" : "Add Clan Pin", button, (GUILayoutOption[])(object)new GUILayoutOption[2] { GUILayout.Width(108f), GUILayout.Height(26f) })) { if (_placing) { CancelSelection(); } else { _placing = true; _creating = false; _selected = ""; _message = "Click the map to place a clan pin."; ClanWorldMapPanel.OpenPinSection(); } } GUI.enabled = enabled; if (GUILayout.Button(_legendVisible ? "Hide Legend" : "Show Legend", button, (GUILayoutOption[])(object)new GUILayoutOption[2] { GUILayout.Width(96f), GUILayout.Height(26f) })) { _legendVisible = !_legendVisible; } } public static void DrawEditor(GUIStyle label, GUIStyle gold, GUIStyle button) { if (!_creating && string.IsNullOrEmpty(_selected)) { GUILayout.Label(_canEdit ? "Select a clan pin or choose Add Clan Pin." : "Select a clan pin to view it.", label, Array.Empty()); if (!string.IsNullOrEmpty(_message)) { GUILayout.Label(_message, label, Array.Empty()); } DrawClear(label, button); return; } ClanMapPinDto clanMapPinDto = Find(_selected); bool canEdit = _canEdit; GUILayout.Label(_creating ? "NEW CLAN PIN" : ((_title.Length > 0) ? _title : "CLAN PIN"), gold, Array.Empty()); GUILayout.Label("Coordinates: " + _nx.ToString("0.000") + ", " + _ny.ToString("0.000"), label, Array.Empty()); bool enabled = GUI.enabled; GUI.enabled = canEdit && !_busy; ClanMapPinType type = (ClanMapPinType)_type; if (GUILayout.Button("Type: " + type, button, Array.Empty())) { _type = NextType(_type); } if (GUILayout.Button("Color: " + (_color + 1), button, Array.Empty())) { _color = (_color + 1) % 8; } _important = GUILayout.Toggle(_important, "Important", label, Array.Empty()); GUILayout.Label("Title", label, Array.Empty()); _title = GUILayout.TextField(_title ?? "", 48, Array.Empty()); GUILayout.Label("Description", label, Array.Empty()); _description = GUILayout.TextArea(_description ?? "", 256, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Height(70f) }); if (canEdit) { if (GUILayout.Button(_creating ? "Save Pin" : "Save Changes", button, Array.Empty())) { Save(); } if (!_creating && GUILayout.Button("Delete", button, Array.Empty())) { Delete(); } } GUI.enabled = enabled; if (clanMapPinDto != null) { GUILayout.Label("Created by: " + clanMapPinDto.CreatedByName, label, Array.Empty()); GUILayout.Label("Updated by: " + clanMapPinDto.UpdatedByName, label, Array.Empty()); } if (GUILayout.Button(_creating ? "Cancel" : "Cancel Selection", button, Array.Empty())) { CancelSelection(); } if (!string.IsNullOrEmpty(_message)) { GUILayout.Label(_message, label, Array.Empty()); } DrawClear(label, button); } private static void DrawClear(GUIStyle label, GUIStyle button) { if (!_canClear || (_snapshot?.Pins?.Count).GetValueOrDefault() == 0) { return; } GUILayout.Space(18f); GUILayout.Label("CLAN PIN ADMINISTRATION", label, Array.Empty()); if (_clearArmed && Time.unscaledTime > _clearUntil) { _clearArmed = false; } GUILayout.Space(10f); if (!_clearArmed) { if (GUILayout.Button("Clear All Clan Pins", button, Array.Empty())) { _clearArmed = true; _clearUntil = Time.unscaledTime + 8f; } return; } GUILayout.Label("This will permanently delete all clan pins for this world.", label, Array.Empty()); if (GUILayout.Button("Confirm Clear All", button, Array.Empty())) { _busy = ClanMapRPC.RequestPinClear(); _clearArmed = false; } if (GUILayout.Button("Cancel Clear", button, Array.Empty())) { _clearArmed = false; } } private static void Save() { ClanMapPinMutationRequest r = new ClanMapPinMutationRequest { PinID = _selected, ExpectedRevision = (Find(_selected)?.Revision ?? 0), NormalizedX = _nx, NormalizedY = _ny, PinType = _type, Title = _title, Description = _description, ColorIndex = _color, IsImportant = _important }; _busy = (_creating ? ClanMapRPC.RequestPinCreate(r) : ClanMapRPC.RequestPinUpdate(r)); if (!_busy) { _message = "Clan pin request unavailable."; } } private static void Delete() { ClanMapPinDto clanMapPinDto = Find(_selected); if (clanMapPinDto != null) { _busy = ClanMapRPC.RequestPinDelete(clanMapPinDto.PinID, clanMapPinDto.Revision); if (!_busy) { _message = "Clan pin request unavailable."; } } } public static void Render(Rect local, Vector2 center, float baseD, float zoom, GUIStyle centerStyle) { //IL_0014: Unknown result type (might be due to invalid IL or missing references) //IL_001a: Invalid comparison between Unknown and I4 //IL_006a: 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_0024: Invalid comparison between Unknown and I4 //IL_00ba: Unknown result type (might be due to invalid IL or missing references) //IL_00b3: Unknown result type (might be due to invalid IL or missing references) //IL_00bf: Unknown result type (might be due to invalid IL or missing references) //IL_00f3: Unknown result type (might be due to invalid IL or missing references) //IL_0103: 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_011a: Unknown result type (might be due to invalid IL or missing references) //IL_013f: Unknown result type (might be due to invalid IL or missing references) //IL_014f: 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_0175: Unknown result type (might be due to invalid IL or missing references) //IL_0195: Unknown result type (might be due to invalid IL or missing references) //IL_019f: Unknown result type (might be due to invalid IL or missing references) //IL_023a: Unknown result type (might be due to invalid IL or missing references) //IL_0247: Unknown result type (might be due to invalid IL or missing references) //IL_025e: Unknown result type (might be due to invalid IL or missing references) //IL_0263: Unknown result type (might be due to invalid IL or missing references) //IL_026f: Unknown result type (might be due to invalid IL or missing references) //IL_01d5: Unknown result type (might be due to invalid IL or missing references) //IL_0213: Unknown result type (might be due to invalid IL or missing references) //IL_02a3: Unknown result type (might be due to invalid IL or missing references) //IL_02b0: 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_02f4: Unknown result type (might be due to invalid IL or missing references) //IL_0317: Unknown result type (might be due to invalid IL or missing references) //IL_0344: Unknown result type (might be due to invalid IL or missing references) //IL_02d6: Unknown result type (might be due to invalid IL or missing references) Hits.Clear(); Event current = Event.current; if (current != null && (int)current.type == 4 && (int)current.keyCode == 27 && (_placing || HasSelection)) { CancelSelection(); current.Use(); } if (_placing) { GUI.Label(new Rect(20f, ((Rect)(ref local)).height - 42f, ((Rect)(ref local)).width - 40f, 28f), "Click the map to place a clan pin. Escape cancels.", centerStyle); } if (!_showLayer || _snapshot?.Pins == null) { return; } float num = baseD * 0.5f * zoom; Event current2 = Event.current; Vector2 val = (Vector2)((current2 != null) ? current2.mousePosition : new Vector2(-999f, -999f)); Vector2 val2 = default(Vector2); Rect val3 = default(Rect); Rect val4 = default(Rect); foreach (ClanMapPinDto pin in _snapshot.Pins) { if (_importantOnly && !pin.IsImportant) { continue; } ((Vector2)(ref val2))..ctor(center.x + pin.NormalizedX * num, center.y - pin.NormalizedY * num); if (Vector2.Distance(val2, center) > num) { continue; } float num2 = (pin.IsImportant ? 24f : 19f); ((Rect)(ref val3))..ctor(val2.x - num2 * 0.5f, val2.y - num2 * 0.5f, num2, num2); if (!((Rect)(ref val3)).Overlaps(local)) { continue; } Color color = GUI.color; GUI.color = Colors[Mathf.Clamp(pin.ColorIndex, 0, Colors.Length - 1)]; GUI.Box(val3, pin.IsImportant ? "!" : GetPinSymbol(pin.PinType)); if (pin.PinID == _selected) { GUI.color = Color.white; GUI.Box(new Rect(((Rect)(ref val3)).x - 3f, ((Rect)(ref val3)).y - 3f, ((Rect)(ref val3)).width + 6f, ((Rect)(ref val3)).height + 6f), ""); } GUI.color = color; Hits.Add(new PinHit { Pin = pin, Rect = new Rect(val2.x - 12f, val2.y - 12f, 24f, 24f) }); bool flag = ((Rect)(ref val3)).Contains(val); if ((_showNames && (zoom >= 2f || flag)) || pin.PinID == _selected) { ((Rect)(ref val4))..ctor(val2.x - 75f, val2.y + 13f, 150f, 20f); if (((Rect)(ref val4)).Overlaps(local)) { GUI.Label(val4, Trim(pin.Title, 22), centerStyle); } } if (flag) { Rect val5 = new Rect(Mathf.Clamp(val.x + 12f, 0f, ((Rect)(ref local)).width - 210f), Mathf.Clamp(val.y + 12f, 0f, ((Rect)(ref local)).height - 52f), 210f, 48f); string[] obj = new string[5] { pin.Title, "\n", null, null, null }; ClanMapPinType pinType = (ClanMapPinType)pin.PinType; obj[2] = pinType.ToString(); obj[3] = " · "; obj[4] = (string.IsNullOrEmpty(pin.UpdatedByName) ? pin.CreatedByName : pin.UpdatedByName); GUI.Box(val5, string.Concat(obj)); } } } public static bool TryHandleClick(Vector2 mouse, Vector2 center, float baseD, float zoom) { //IL_001d: Unknown result type (might be due to invalid IL or missing references) //IL_001e: Unknown result type (might be due to invalid IL or missing references) //IL_001f: Unknown result type (might be due to invalid IL or missing references) //IL_0024: Unknown result type (might be due to invalid IL or missing references) //IL_0033: Unknown result type (might be due to invalid IL or missing references) //IL_0040: Unknown result type (might be due to invalid IL or missing references) //IL_00b6: Unknown result type (might be due to invalid IL or missing references) if (!_showLayer) { return false; } if (_placing) { float num = baseD * 0.5f * zoom; Vector2 val = mouse - center; if (((Vector2)(ref val)).sqrMagnitude > num * num) { return true; } _nx = val.x / num; _ny = (0f - val.y) / num; _creating = true; _placing = false; _selected = ""; _type = 9; _color = 0; _important = false; _title = ""; _description = ""; _message = ""; return true; } for (int num2 = Hits.Count - 1; num2 >= 0; num2--) { if (((Rect)(ref Hits[num2].Rect)).Contains(mouse)) { _selected = Hits[num2].Pin.PinID; _creating = false; Load(Hits[num2].Pin); return true; } } return false; } public static void CancelSelection() { _selected = ""; _creating = (_placing = (_busy = false)); _message = ""; _clearArmed = false; } private static ClanMapPinDto Find(string id) { return _snapshot?.Pins?.Find((ClanMapPinDto x) => x.PinID == id); } private static void Load(ClanMapPinDto p) { if (p != null) { _selected = p.PinID; _nx = p.NormalizedX; _ny = p.NormalizedY; _type = p.PinType; _title = p.Title; _description = p.Description; _color = p.ColorIndex; _important = p.IsImportant; } } public static string GetPinSymbol(int type) { return (ClanMapPinType)type switch { ClanMapPinType.Default => "●", ClanMapPinType.RallyPoint => "R", ClanMapPinType.Attack => "A", ClanMapPinType.Defend => "D", ClanMapPinType.Scout => "S", ClanMapPinType.Resource => "$", ClanMapPinType.Warning => "!", ClanMapPinType.Trade => "T", ClanMapPinType.Meeting => "M", ClanMapPinType.Custom => "C", _ => "●", }; } private static int NextType(int current) { int[] array = new int[10] { 9, 0, 1, 2, 3, 4, 5, 6, 7, 8 }; int num = Array.IndexOf(array, current); return array[(num + 1 + array.Length) % array.Length]; } private static string Trim(string s, int n) { s = s ?? ""; if (s.Length <= n) { return s; } return s.Substring(0, n - 1) + "…"; } public static void DrawLegend(float width, float height, GUIStyle panel, GUIStyle label, GUIStyle gold, GUIStyle button, GUIStyle small) { if (_legendVisible) { GUILayout.BeginVertical(panel, (GUILayoutOption[])(object)new GUILayoutOption[2] { GUILayout.Width(width), GUILayout.Height(height) }); GUILayout.BeginHorizontal(Array.Empty()); GUILayout.Label("MAP LEGEND", gold, Array.Empty()); if (GUILayout.Button("Hide", button, (GUILayoutOption[])(object)new GUILayoutOption[2] { GUILayout.Width(54f), GUILayout.Height(24f) })) { _legendVisible = false; } GUILayout.EndHorizontal(); GUILayout.Label("PIN TYPES", gold, Array.Empty()); LegendRow(ClanMapPinType.Default, "Default Pin", "general marker", label, gold, small); LegendRow(ClanMapPinType.RallyPoint, "Rally Point", "meeting or regroup position", label, gold, small); LegendRow(ClanMapPinType.Attack, "Attack", "planned offensive target", label, gold, small); LegendRow(ClanMapPinType.Defend, "Defend", "defensive position", label, gold, small); LegendRow(ClanMapPinType.Scout, "Scout", "reconnaissance location", label, gold, small); LegendRow(ClanMapPinType.Resource, "Resource", "important resource location", label, gold, small); LegendRow(ClanMapPinType.Warning, "Warning", "danger or hazard", label, gold, small); LegendRow(ClanMapPinType.Trade, "Trade", "trade or delivery point", label, gold, small); LegendRow(ClanMapPinType.Meeting, "Meeting", "planned clan meeting", label, gold, small); LegendRow(ClanMapPinType.Custom, "Custom", "other clan note", label, gold, small); ClanMapDrawingUi.DrawLegend(gold, label); GUILayout.FlexibleSpace(); GUILayout.Space(16f); GUILayout.EndVertical(); } } private static void LegendRow(ClanMapPinType type, string name, string description, GUIStyle label, GUIStyle gold, GUIStyle small) { GUILayout.BeginHorizontal(Array.Empty()); GUILayout.Label(GetPinSymbol((int)type), gold, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(24f) }); GUILayout.BeginVertical(Array.Empty()); GUILayout.Label(name, label, Array.Empty()); GUILayout.Label(description, small, Array.Empty()); GUILayout.EndVertical(); GUILayout.EndHorizontal(); GUILayout.Space(4f); } } public class ClanNameTagRenderer : MonoBehaviour { private GUIStyle _tagStyle; private GUIStyle _shadowStyle; private Dictionary _badgeTextures; private bool _styleInit; private const float MaxRenderDistance = 80f; private const float HeadOffsetY = 2.05f; public static ClanNameTagRenderer Instance { get; private set; } private void Awake() { Instance = this; } private void InitStyle() { //IL_001a: Unknown result type (might be due to invalid IL or missing references) //IL_001f: Unknown result type (might be due to invalid IL or missing references) //IL_0027: Unknown result type (might be due to invalid IL or missing references) //IL_002e: Unknown result type (might be due to invalid IL or missing references) //IL_0035: Unknown result type (might be due to invalid IL or missing references) //IL_003c: Unknown result type (might be due to invalid IL or missing references) //IL_0048: Expected O, but got Unknown //IL_004e: Unknown result type (might be due to invalid IL or missing references) //IL_0054: Expected O, but got Unknown //IL_006e: Unknown result type (might be due to invalid IL or missing references) if (!_styleInit) { _styleInit = true; _tagStyle = new GUIStyle(GUI.skin.label) { fontSize = 13, fontStyle = (FontStyle)1, alignment = (TextAnchor)4, richText = true, wordWrap = false }; GUIStyle val = new GUIStyle(_tagStyle); val.normal.textColor = new Color(0f, 0f, 0f, 0.85f); _shadowStyle = val; } } private void OnGUI() { //IL_0005: Unknown result type (might be due to invalid IL or missing references) //IL_000b: Invalid comparison between Unknown and I4 //IL_0012: Unknown result type (might be due to invalid IL or missing references) //IL_001d: Expected O, but got Unknown //IL_0027: Unknown result type (might be due to invalid IL or missing references) //IL_0032: Expected O, but got Unknown //IL_0065: 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_007f: Unknown result type (might be due to invalid IL or missing references) //IL_008a: Expected O, but got Unknown //IL_0091: Unknown result type (might be due to invalid IL or missing references) //IL_009b: Unknown result type (might be due to invalid IL or missing references) //IL_00a5: Expected O, but got Unknown //IL_00a5: Expected O, but got Unknown //IL_00bd: Unknown result type (might be due to invalid IL or missing references) //IL_00c2: Unknown result type (might be due to invalid IL or missing references) //IL_011f: Unknown result type (might be due to invalid IL or missing references) //IL_0124: Unknown result type (might be due to invalid IL or missing references) //IL_012e: Unknown result type (might be due to invalid IL or missing references) //IL_0133: Unknown result type (might be due to invalid IL or missing references) //IL_0138: Unknown result type (might be due to invalid IL or missing references) //IL_013b: Unknown result type (might be due to invalid IL or missing references) //IL_013d: Unknown result type (might be due to invalid IL or missing references) //IL_0142: 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_015b: Unknown result type (might be due to invalid IL or missing references) //IL_01e1: Unknown result type (might be due to invalid IL or missing references) //IL_01eb: Expected O, but got Unknown //IL_01e6: Unknown result type (might be due to invalid IL or missing references) //IL_01eb: Unknown result type (might be due to invalid IL or missing references) //IL_020d: Unknown result type (might be due to invalid IL or missing references) //IL_0219: 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_028e: Unknown result type (might be due to invalid IL or missing references) //IL_02a8: Unknown result type (might be due to invalid IL or missing references) //IL_02af: Unknown result type (might be due to invalid IL or missing references) //IL_02b6: Unknown result type (might be due to invalid IL or missing references) //IL_02d8: Unknown result type (might be due to invalid IL or missing references) //IL_02df: Unknown result type (might be due to invalid IL or missing references) //IL_02e6: Unknown result type (might be due to invalid IL or missing references) //IL_0308: Unknown result type (might be due to invalid IL or missing references) //IL_030f: Unknown result type (might be due to invalid IL or missing references) //IL_0316: Unknown result type (might be due to invalid IL or missing references) //IL_0338: Unknown result type (might be due to invalid IL or missing references) //IL_033f: Unknown result type (might be due to invalid IL or missing references) //IL_0346: Unknown result type (might be due to invalid IL or missing references) //IL_0365: Unknown result type (might be due to invalid IL or missing references) //IL_0371: Unknown result type (might be due to invalid IL or missing references) //IL_037d: Unknown result type (might be due to invalid IL or missing references) //IL_0389: Unknown result type (might be due to invalid IL or missing references) //IL_0397: Unknown result type (might be due to invalid IL or missing references) //IL_039e: Unknown result type (might be due to invalid IL or missing references) //IL_03a5: Unknown result type (might be due to invalid IL or missing references) //IL_03d2: Unknown result type (might be due to invalid IL or missing references) //IL_03dd: Expected O, but got Unknown //IL_03f0: Unknown result type (might be due to invalid IL or missing references) //IL_03fc: Unknown result type (might be due to invalid IL or missing references) //IL_0423: Unknown result type (might be due to invalid IL or missing references) //IL_042a: Unknown result type (might be due to invalid IL or missing references) //IL_0436: Expected O, but got Unknown //IL_0436: Unknown result type (might be due to invalid IL or missing references) if ((int)Event.current.type != 7 || (Object)Player.m_localPlayer == (Object)null) { return; } Camera main = Camera.main; if ((Object)main == (Object)null) { return; } WarfareData warfareData = WarfareManager.Instance?.Data; if (warfareData == null) { return; } InitStyle(); List allPlayers = Player.GetAllPlayers(); if (allPlayers == null) { return; } Vector3 position = ((Component)Player.m_localPlayer).transform.position; for (int i = 0; i < allPlayers.Count; i++) { Player val = allPlayers[i]; if ((Object)val == (Object)null || (Object)val == (Object)Player.m_localPlayer || ((Character)val).IsDead()) { continue; } float num = Vector3.Distance(((Component)val).transform.position, position); if (num > 80f) { continue; } long playerID = val.GetPlayerID(); if (playerID == 0L) { continue; } PlayerClanInfo player = warfareData.GetPlayer(playerID); if (player == null || player.ClanId == 0) { continue; } Clan clan = ClanConfig.GetClan(player.ClanId); if (clan == null) { continue; } Vector3 val2 = ((Component)val).transform.position + Vector3.up * 2.05f; Vector3 val3 = main.WorldToScreenPoint(val2); if (val3.z <= 0f) { continue; } float num2 = (float)Screen.height - val3.y; string text = ((!string.IsNullOrWhiteSpace(player.PlayerName)) ? player.PlayerName : val.GetPlayerName()); string text2 = text + " " + clan.Symbol + " " + clan.Name; List list = WarfareManager.Instance?.GetServerBadges(player) ?? new List(); Vector2 val4 = _tagStyle.CalcSize(new GUIContent(text2)); float num3 = 22f; float num4 = (float)Mathf.Min(4, list.Count) * (num3 + 3f); float num5 = val4.x + num4; float num6 = val3.x - num5 / 2f; float num7 = num2 - Mathf.Max(val4.y, num3) - 4f; float num8 = Mathf.Clamp01(1f - num / 80f); num8 = Mathf.Lerp(0.35f, 1f, num8); _shadowStyle.normal.textColor = new Color(0f, 0f, 0f, num8 * 0.85f); GUI.Label(new Rect(num6 + 1f, num7 + 1f, val4.x, val4.y), text2, _shadowStyle); GUI.Label(new Rect(num6 - 1f, num7 + 1f, val4.x, val4.y), text2, _shadowStyle); GUI.Label(new Rect(num6 + 1f, num7 - 1f, val4.x, val4.y), text2, _shadowStyle); GUI.Label(new Rect(num6 - 1f, num7 - 1f, val4.x, val4.y), text2, _shadowStyle); _tagStyle.normal.textColor = new Color(clan.Color.r, clan.Color.g, clan.Color.b, num8); GUI.Label(new Rect(num6, num7, val4.x, val4.y), text2, _tagStyle); for (int j = 0; j < list.Count && j < 4; j++) { Texture2D badgeTexture = GetBadgeTexture(list[j]); if ((Object)badgeTexture != (Object)null) { GUI.color = new Color(1f, 1f, 1f, num8); GUI.DrawTexture(new Rect(num6 + val4.x + 4f + (float)j * (num3 + 3f), num7 - 2f, num3, num3), (Texture)badgeTexture, (ScaleMode)2, true); GUI.color = Color.white; } } } } private Texture2D GetBadgeTexture(string badge) { if (_badgeTextures == null) { _badgeTextures = new Dictionary(StringComparer.OrdinalIgnoreCase); LoadBadgeFolder(Path.Combine(Paths.PluginPath, "BlackViking-FortecaClanWarfare", "FehuKingdoms", "Constellation")); LoadBadgeFolder(Path.Combine(Paths.PluginPath, "FehuKingdoms", "Constellation")); if (_badgeTextures.Count == 0) { string[] array = (Directory.Exists(Paths.PluginPath) ? Directory.GetDirectories(Paths.PluginPath, "Constellation", SearchOption.AllDirectories) : Array.Empty()); foreach (string folder in array) { LoadBadgeFolder(folder); } } } string text = BadgeIconKey(badge); if (!_badgeTextures.TryGetValue(text, out var value) && !_badgeTextures.TryGetValue(NormalizeAssetKey(text), out value)) { return null; } return value; } private void LoadBadgeFolder(string folder) { //IL_002d: Unknown result type (might be due to invalid IL or missing references) //IL_0033: Expected O, but got Unknown if (string.IsNullOrEmpty(folder) || !Directory.Exists(folder)) { return; } string[] files = Directory.GetFiles(folder, "*.png", SearchOption.TopDirectoryOnly); foreach (string text in files) { try { Texture2D val = new Texture2D(2, 2, (TextureFormat)4, false); if (!ImageConversion.LoadImage(val, File.ReadAllBytes(text))) { Object.Destroy((Object)(object)val); continue; } string text2 = (((Object)val).name = Path.GetFileNameWithoutExtension(text)); _badgeTextures[text2] = val; _badgeTextures[NormalizeAssetKey(text2)] = val; } catch (Exception ex) { Plugin.Log.LogWarning((object)("[FCW Constellation] Failed to load badge icon " + text + ": " + ex.Message)); } } } private static string BadgeIconKey(string badge) { switch (badge ?? "") { case "Contributor": case "Gold Contributor": return "contributor"; case "Silver Subscriber": case "Subscriber": return "subscriber"; case "Steel Supporter": case "Supporter": return "supporter"; case "Bronze Alpha Tester": case "Alpha Tester": return "alpha_tester"; case "Beta Tester": return "beta_tester"; case "Admin": case "Administrator": return "admin"; default: return NormalizeAssetKey(badge); } } private static string NormalizeAssetKey(string value) { if (string.IsNullOrEmpty(value)) { return string.Empty; } char[] array = value.ToLowerInvariant().ToCharArray(); int length = 0; for (int i = 0; i < array.Length; i++) { if (char.IsLetterOrDigit(array[i])) { array[length++] = array[i]; } } return new string(array, 0, length); } } public class ClanSelectionUI : MonoBehaviour { private enum Mode { Browse, Create, Role } private Mode _mode; private string _createName = ""; private string _createTag = ""; private int _colorIndex; private int _iconIndex; private string _createEmblemUrl = ""; private bool _previewCreateEmblem; private int _createEmblemIndex; private int _createEmblemCategoryIndex; private int _createEmblemPage; private Vector2 _browseScroll; private Vector2 _roleScroll; private long _nextChoiceReminderTimestamp; private bool _stylesInit; private bool _createWizardLogged; private bool _loggedDisconnectBlock; private bool _loggedWaitingSyncBlock; private bool _loggedConfirmedNoClan; private GUIStyle _titleStyle; private GUIStyle _subtitleStyle; private GUIStyle _bodyStyle; private GUIStyle _cardStyle; private GUIStyle _cardHoverStyle; private GUIStyle _btnStyle; private GUIStyle _fieldStyle; private GUIStyle _roleNameStyle; private Texture2D _backdropTex; private Texture2D _panelTex; private Texture2D _cardTex; private Texture2D _cardHoverTex; private Texture2D _pixelTex; private Texture2D _fieldTex; private Dictionary _roleTextures; private static readonly (ClanRole role, string title, string group, string lore, string perks, string iconName, bool selectable)[] RoleEntries = new(ClanRole, string, string, string, string, string, bool)[16] { (ClanRole.Explorer, "PATHFINDER", "Territory", "Explorer of the kingdom.", "Neutral claims; discovery cost reduction.", "explorer", true), (ClanRole.Warden, "WARDEN", "Territory", "Keeper of kingdom lands.", "Neutral claims; territory upgrade discount.", "warden", true), (ClanRole.Raider, "RAIDER", "Warfare", "Offensive warfare specialist.", "War damage and stamina efficiency.", "raider", true), (ClanRole.Guardian, "GUARDIAN", "Warfare", "Defender of kingdom lands.", "Home defense and block power.", "guardian", true), (ClanRole.Builder, "BUILDER", "Infrastructure", "Infrastructure specialist.", "Fehu build and upgrade discounts.", "builder", true), (ClanRole.Gatherer, "GATHERER", "Economy", "Resource supplier.", "Carry weight and basic resources.", "gatherer", true), (ClanRole.Merchant, "MERCHANT", "Economy", "Economy specialist.", "Economic rewards and trade fees.", "merchant", true), (ClanRole.Smuggler, "SMUGGLER", "Economy", "Illegal economy specialist.", "Personal tax reduction.", "smuggler", true), (ClanRole.Thief, "THIEF", "Intelligence", "Stealth and infiltration.", "Nearby player theft with cooldown.", "thief", true), (ClanRole.Shaman, "SHAMAN", "Spiritual", "Spiritual role.", "Rot Totems and placement discount.", "shaman", true), (ClanRole.Druid, "DRUID", "Spiritual", "Nature guardian.", "Rot Totem guidance and taming.", "druid", true), (ClanRole.Hunter, "HUNTER", "Wilderness", "Ranged wilderness specialist.", "Bow damage and stamina efficiency.", "hunter", true), (ClanRole.Prospector, "PROSPECTOR", "Economy", "Mining specialist.", "Mining yield and stamina efficiency.", "prospector", true), (ClanRole.Skipper, "SKIPPER", "Travel", "Sea specialist.", "Ship speed and durability.", "skipper", true), (ClanRole.Skald, "SKALD", "Support", "Culture and morale.", "Rested duration and regeneration.", "skald", true), (ClanRole.Lawspeaker, "LAWSPEAKER", "Politics", "Kingdom administration.", "Progressive upkeep reduction.", "lawspeaker", true) }; private const float PanelWidth = 1200f; private const float PanelHeight = 825f; public static ClanSelectionUI Instance { get; private set; } public static bool IsOpen { get; private set; } private static float UIScale => (float)Screen.height / 1080f; public static bool ShouldBlockInput() { if (!IsOpen) { return WarfareHUD.IsMenuOpen; } return true; } public void OpenBrowse() { if (!BlockOnboardingIfNeeded(close: true)) { if (GetCurrentPlayerClanId() != 0) { CloseClanSelectionState(); return; } Plugin.Log.LogInfo((object)"[FCW UI] ClanSelectionUI.OpenBrowse called."); _mode = Mode.Browse; IsOpen = true; } } public void OpenCreate() { if (!BlockOnboardingIfNeeded(close: true)) { int currentPlayerClanId = GetCurrentPlayerClanId(); if (currentPlayerClanId != 0) { Plugin.Log.LogInfo((object)("[FCW UI] Create screen blocked because player already has clanId=" + currentPlayerClanId)); CloseClanSelectionState(); return; } Plugin.Log.LogInfo((object)"[FCW UI] ClanSelectionUI.OpenCreate called."); _mode = Mode.Create; _createName = ""; _createTag = ""; _createEmblemUrl = ""; _previewCreateEmblem = false; _createEmblemIndex = 0; _createEmblemCategoryIndex = 0; _createEmblemPage = 0; _createWizardLogged = false; IsOpen = true; } } public void HandleClanSync(int clanId) { if (clanId <= 0) { _loggedWaitingSyncBlock = false; return; } Plugin.Log.LogInfo((object)("[FCW UI] Refresh after sync, playerClanId=" + clanId)); CloseClanSelectionState(); WarfareHUD.Instance?.HandleClanSync(clanId); } public void ResetClientState() { CloseClanSelectionState(); } public void CloseForDisconnect() { Plugin.Log.LogInfo((object)"[FCW UI] Closing clan onboarding due to disconnect"); CloseClanSelectionState(); _loggedDisconnectBlock = false; _loggedWaitingSyncBlock = false; _loggedConfirmedNoClan = false; } private void CloseClanSelectionState() { IsOpen = false; _mode = Mode.Browse; _createName = ""; _createTag = ""; _createEmblemUrl = ""; _previewCreateEmblem = false; _createEmblemIndex = 0; _createEmblemCategoryIndex = 0; _createEmblemPage = 0; _createWizardLogged = false; _nextChoiceReminderTimestamp = 0L; } private bool BlockOnboardingIfNeeded(bool close) { WarfareRPC instance = WarfareRPC.Instance; if ((Object)(object)instance != (Object)null && instance.IsClientDisconnecting) { if (!_loggedDisconnectBlock) { _loggedDisconnectBlock = true; Plugin.Log.LogInfo((object)"[FCW UI] Blocked onboarding: disconnecting=true"); } if (close) { CloseClanSelectionState(); } return true; } if ((Object)(object)instance != (Object)null && instance.IsWaitingForServerSync) { if (!_loggedWaitingSyncBlock) { _loggedWaitingSyncBlock = true; Plugin.Log.LogInfo((object)"[FCW UI] Blocked onboarding: waiting for server sync"); } if (close) { CloseClanSelectionState(); } return true; } _loggedDisconnectBlock = false; _loggedWaitingSyncBlock = false; return false; } private static int GetCurrentPlayerClanId() { //IL_0005: Unknown result type (might be due to invalid IL or missing references) //IL_0010: Expected O, but got Unknown if ((Object)Player.m_localPlayer == (Object)null) { return 0; } return (WarfareManager.Instance?.Data?.GetPlayer(Player.m_localPlayer.GetPlayerID())?.ClanId).GetValueOrDefault(); } private void Awake() { Instance = this; } private void Update() { //IL_000f: Unknown result type (might be due to invalid IL or missing references) //IL_001a: Expected O, but got Unknown //IL_0021: Unknown result type (might be due to invalid IL or missing references) //IL_002c: Expected O, but got Unknown //IL_0033: Unknown result type (might be due to invalid IL or missing references) //IL_003e: Expected O, but got Unknown if (BlockOnboardingIfNeeded(close: true)) { return; } if ((Object)Player.m_localPlayer == (Object)null || (Object)Game.instance == (Object)null || (Object)ZNet.instance == (Object)null) { IsOpen = false; _mode = Mode.Browse; return; } WarfareData warfareData = WarfareManager.Instance?.Data; if (warfareData == null) { IsOpen = false; return; } PlayerClanInfo player = warfareData.GetPlayer(Player.m_localPlayer.GetPlayerID()); if (player != null && player.ClanId != 0) { CloseClanSelectionState(); return; } if (player == null || (player.ClanId == 0 && !player.IsFreeborn)) { if (BlockOnboardingIfNeeded(close: true) || (!IsOpen && _nextChoiceReminderTimestamp > 0 && DateTimeOffset.UtcNow.ToUnixTimeSeconds() < _nextChoiceReminderTimestamp)) { return; } if (!_loggedConfirmedNoClan) { _loggedConfirmedNoClan = true; Plugin.Log.LogInfo((object)"[FCW UI] Showing onboarding only after confirmed no clan"); } IsOpen = true; if (_mode == Mode.Role) { _mode = Mode.Browse; } } else if (player.IsFreeborn && player.ClanId == 0) { IsOpen = false; _mode = Mode.Browse; _nextChoiceReminderTimestamp = 0L; } else if (player.PrimaryRole == ClanRole.None) { IsOpen = true; _mode = Mode.Role; } else { IsOpen = false; _nextChoiceReminderTimestamp = 0L; } if (IsOpen) { Cursor.lockState = (CursorLockMode)0; Cursor.visible = true; } } private Texture2D MakeTex(Color c) { //IL_0002: Unknown result type (might be due to invalid IL or missing references) //IL_0007: Unknown result type (might be due to invalid IL or missing references) //IL_000f: Unknown result type (might be due to invalid IL or missing references) //IL_0012: Unknown result type (might be due to invalid IL or missing references) //IL_0018: Unknown result type (might be due to invalid IL or missing references) //IL_001f: Expected O, but got Unknown Texture2D val = new Texture2D(1, 1) { hideFlags = (HideFlags)61 }; val.SetPixel(0, 0, c); val.Apply(); return val; } private Texture2D MakeGradientTex(int w, int h, Color top, Color bottom) { //IL_0002: Unknown result type (might be due to invalid IL or missing references) //IL_0007: Unknown result type (might be due to invalid IL or missing references) //IL_000f: Unknown result type (might be due to invalid IL or missing references) //IL_0017: Expected O, but got Unknown //IL_001b: 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_0028: Unknown result type (might be due to invalid IL or missing references) //IL_0030: Unknown result type (might be due to invalid IL or missing references) Texture2D val = new Texture2D(w, h) { hideFlags = (HideFlags)61, wrapMode = (TextureWrapMode)1 }; for (int i = 0; i < h; i++) { Color val2 = Color.Lerp(bottom, top, (float)i / (float)h); for (int j = 0; j < w; j++) { val.SetPixel(j, i, val2); } } val.Apply(); return val; } private void InitStyles() { //IL_0014: Unknown result type (might be due to invalid IL or missing references) //IL_0039: Unknown result type (might be due to invalid IL or missing references) //IL_0062: Unknown result type (might be due to invalid IL or missing references) //IL_007b: 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_00bd: 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_00ff: Unknown result type (might be due to invalid IL or missing references) //IL_0124: Unknown result type (might be due to invalid IL or missing references) //IL_0135: Unknown result type (might be due to invalid IL or missing references) //IL_0153: Unknown result type (might be due to invalid IL or missing references) //IL_0171: Unknown result type (might be due to invalid IL or missing references) //IL_0198: Unknown result type (might be due to invalid IL or missing references) //IL_019d: Unknown result type (might be due to invalid IL or missing references) //IL_01a5: Unknown result type (might be due to invalid IL or missing references) //IL_01ac: Unknown result type (might be due to invalid IL or missing references) //IL_01b3: Unknown result type (might be due to invalid IL or missing references) //IL_01bf: Expected O, but got Unknown //IL_01ca: Unknown result type (might be due to invalid IL or missing references) //IL_01db: Unknown result type (might be due to invalid IL or missing references) //IL_01e0: Unknown result type (might be due to invalid IL or missing references) //IL_01e8: Unknown result type (might be due to invalid IL or missing references) //IL_01ef: Unknown result type (might be due to invalid IL or missing references) //IL_01f6: Unknown result type (might be due to invalid IL or missing references) //IL_0202: Expected O, but got Unknown //IL_020d: Unknown result type (might be due to invalid IL or missing references) //IL_021e: Unknown result type (might be due to invalid IL or missing references) //IL_0223: Unknown result type (might be due to invalid IL or missing references) //IL_022b: Unknown result type (might be due to invalid IL or missing references) //IL_0232: Unknown result type (might be due to invalid IL or missing references) //IL_023e: Expected O, but got Unknown //IL_0249: Unknown result type (might be due to invalid IL or missing references) //IL_0259: Unknown result type (might be due to invalid IL or missing references) //IL_025f: Expected O, but got Unknown //IL_0275: Unknown result type (might be due to invalid IL or missing references) //IL_027f: Expected O, but got Unknown //IL_0288: Unknown result type (might be due to invalid IL or missing references) //IL_0292: Expected O, but got Unknown //IL_0297: Unknown result type (might be due to invalid IL or missing references) //IL_02a1: Expected O, but got Unknown //IL_02ae: Unknown result type (might be due to invalid IL or missing references) //IL_02b5: Expected O, but got Unknown //IL_02da: Unknown result type (might be due to invalid IL or missing references) //IL_02df: Unknown result type (might be due to invalid IL or missing references) //IL_02e7: Unknown result type (might be due to invalid IL or missing references) //IL_02ee: Unknown result type (might be due to invalid IL or missing references) //IL_02f5: Unknown result type (might be due to invalid IL or missing references) //IL_02ff: Expected O, but got Unknown //IL_02ff: Unknown result type (might be due to invalid IL or missing references) //IL_030f: Expected O, but got Unknown //IL_035c: Unknown result type (might be due to invalid IL or missing references) //IL_036d: Unknown result type (might be due to invalid IL or missing references) //IL_0382: Unknown result type (might be due to invalid IL or missing references) //IL_0387: Unknown result type (might be due to invalid IL or missing references) //IL_038f: Unknown result type (might be due to invalid IL or missing references) //IL_0394: Unknown result type (might be due to invalid IL or missing references) //IL_039e: Expected O, but got Unknown //IL_039e: Unknown result type (might be due to invalid IL or missing references) //IL_03ae: Expected O, but got Unknown //IL_03cf: Unknown result type (might be due to invalid IL or missing references) //IL_03e4: Unknown result type (might be due to invalid IL or missing references) //IL_03e9: Unknown result type (might be due to invalid IL or missing references) //IL_03f1: Unknown result type (might be due to invalid IL or missing references) //IL_03f8: Unknown result type (might be due to invalid IL or missing references) //IL_0404: Expected O, but got Unknown //IL_040f: Unknown result type (might be due to invalid IL or missing references) if (!_stylesInit) { _stylesInit = true; _pixelTex = MakeTex(Color.white); _backdropTex = MakeTex(new Color(0f, 0f, 0f, 0.8f)); _panelTex = MakeGradientTex(48, 48, new Color(0.055f, 0.085f, 0.09f, 0.99f), new Color(0.025f, 0.04f, 0.045f, 0.995f)); _cardTex = MakeGradientTex(32, 32, new Color(0.075f, 0.11f, 0.115f, 0.98f), new Color(0.04f, 0.065f, 0.07f, 0.99f)); _cardHoverTex = MakeGradientTex(32, 32, new Color(0.15f, 0.13f, 0.075f, 0.99f), new Color(0.075f, 0.105f, 0.105f, 0.99f)); _fieldTex = MakeTex(new Color(0.025f, 0.045f, 0.05f, 1f)); Color textColor = default(Color); ((Color)(ref textColor))..ctor(0.96f, 0.83f, 0.38f); Color textColor2 = default(Color); ((Color)(ref textColor2))..ctor(0.86f, 0.8f, 0.66f); Color textColor3 = default(Color); ((Color)(ref textColor3))..ctor(0.74f, 0.68f, 0.55f); _titleStyle = new GUIStyle(GUI.skin.label) { fontSize = 26, fontStyle = (FontStyle)1, alignment = (TextAnchor)4, richText = true }; _titleStyle.normal.textColor = textColor; _subtitleStyle = new GUIStyle(GUI.skin.label) { fontSize = 13, fontStyle = (FontStyle)2, alignment = (TextAnchor)4, richText = true }; _subtitleStyle.normal.textColor = textColor3; _bodyStyle = new GUIStyle(GUI.skin.label) { fontSize = 13, richText = true, wordWrap = true }; _bodyStyle.normal.textColor = textColor2; GUIStyle val = new GUIStyle(GUI.skin.box); val.normal.background = _cardTex; val.border = new RectOffset(6, 6, 6, 6); val.padding = new RectOffset(10, 10, 9, 14); val.margin = new RectOffset(2, 2, 3, 3); _cardStyle = val; GUIStyle val2 = new GUIStyle(_cardStyle); val2.normal.background = _cardHoverTex; _cardHoverStyle = val2; _btnStyle = new GUIStyle(GUI.skin.button) { fontSize = 14, fontStyle = (FontStyle)1, padding = new RectOffset(10, 10, 5, 5), fixedHeight = 30f }; _btnStyle.normal.background = _cardTex; _btnStyle.hover.background = _cardHoverTex; _btnStyle.active.background = _cardHoverTex; _btnStyle.normal.textColor = textColor; _btnStyle.hover.textColor = Color.white; _fieldStyle = new GUIStyle(GUI.skin.textField) { fontSize = 14, padding = new RectOffset(8, 8, 5, 5), fixedHeight = 30f }; _fieldStyle.normal.background = _fieldTex; _fieldStyle.normal.textColor = Color.white; _roleNameStyle = new GUIStyle(GUI.skin.label) { fontSize = 16, fontStyle = (FontStyle)1, richText = true }; _roleNameStyle.normal.textColor = textColor; } } private void OnGUI() { //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_0026: 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_0037: Unknown result type (might be due to invalid IL or missing references) //IL_003c: Unknown result type (might be due to invalid IL or missing references) //IL_0058: Unknown result type (might be due to invalid IL or missing references) //IL_006e: Unknown result type (might be due to invalid IL or missing references) //IL_0079: Unknown result type (might be due to invalid IL or missing references) //IL_0083: Expected O, but got Unknown //IL_00bc: Unknown result type (might be due to invalid IL or missing references) if (IsOpen && !BlockOnboardingIfNeeded(close: true)) { InitStyles(); float uIScale = UIScale; Matrix4x4 matrix = GUI.matrix; GUI.matrix = Matrix4x4.TRS(Vector3.zero, Quaternion.identity, new Vector3(uIScale, uIScale, 1f)); float num = (float)Screen.width / uIScale; float num2 = (float)Screen.height / uIScale; GUI.color = Color.white; GUI.DrawTexture(new Rect(0f, 0f, num, num2), (Texture)_backdropTex); switch (_mode) { case Mode.Browse: DrawBrowse(num, num2); break; case Mode.Create: DrawCreate(num, num2); break; case Mode.Role: DrawRole(num, num2); break; } GUI.matrix = matrix; } } private void DrawPanel(Rect r) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_0007: Unknown result type (might be due to invalid IL or missing references) //IL_0011: Expected O, but got Unknown //IL_0025: Unknown result type (might be due to invalid IL or missing references) //IL_0049: Unknown result type (might be due to invalid IL or missing references) //IL_0054: Unknown result type (might be due to invalid IL or missing references) //IL_005e: Expected O, but got Unknown //IL_007e: Unknown result type (might be due to invalid IL or missing references) //IL_0089: Unknown result type (might be due to invalid IL or missing references) //IL_0093: Expected O, but got Unknown //IL_00ad: Unknown result type (might be due to invalid IL or missing references) //IL_00b8: Unknown result type (might be due to invalid IL or missing references) //IL_00c2: Expected O, but got Unknown //IL_00e2: 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_00f7: Expected O, but got Unknown //IL_00f7: Unknown result type (might be due to invalid IL or missing references) GUI.DrawTexture(r, (Texture)_panelTex); GUI.color = new Color(0.65f, 0.5f, 0.2f, 0.85f); GUI.DrawTexture(new Rect(((Rect)(ref r)).x, ((Rect)(ref r)).y, ((Rect)(ref r)).width, 2f), (Texture)_pixelTex); GUI.DrawTexture(new Rect(((Rect)(ref r)).x, ((Rect)(ref r)).yMax - 2f, ((Rect)(ref r)).width, 2f), (Texture)_pixelTex); GUI.DrawTexture(new Rect(((Rect)(ref r)).x, ((Rect)(ref r)).y, 2f, ((Rect)(ref r)).height), (Texture)_pixelTex); GUI.DrawTexture(new Rect(((Rect)(ref r)).xMax - 2f, ((Rect)(ref r)).y, 2f, ((Rect)(ref r)).height), (Texture)_pixelTex); GUI.color = Color.white; } private void DrawSoftFrame(Rect r) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_0019: Unknown result type (might be due to invalid IL or missing references) //IL_004b: Unknown result type (might be due to invalid IL or missing references) //IL_0056: Unknown result type (might be due to invalid IL or missing references) //IL_0060: Expected O, but got Unknown //IL_0074: Unknown result type (might be due to invalid IL or missing references) //IL_007e: Unknown result type (might be due to invalid IL or missing references) //IL_0085: Unknown result type (might be due to invalid IL or missing references) //IL_008f: Expected O, but got Unknown //IL_00a3: Unknown result type (might be due to invalid IL or missing references) //IL_00c7: Unknown result type (might be due to invalid IL or missing references) //IL_00d2: Unknown result type (might be due to invalid IL or missing references) //IL_00dc: Expected O, but got Unknown //IL_00fc: Unknown result type (might be due to invalid IL or missing references) //IL_0107: Unknown result type (might be due to invalid IL or missing references) //IL_0111: Expected O, but got Unknown //IL_012b: Unknown result type (might be due to invalid IL or missing references) //IL_0136: Unknown result type (might be due to invalid IL or missing references) //IL_0140: Expected O, but got Unknown //IL_0160: Unknown result type (might be due to invalid IL or missing references) //IL_016b: Unknown result type (might be due to invalid IL or missing references) //IL_0175: Expected O, but got Unknown Color color = GUI.color; GUI.color = new Color(0f, 0f, 0f, 0.32f); GUI.DrawTexture(new Rect(((Rect)(ref r)).x + 5f, ((Rect)(ref r)).y + 6f, ((Rect)(ref r)).width, ((Rect)(ref r)).height), (Texture)_pixelTex); GUI.color = new Color(0.07f, 0.055f, 0.035f, 0.78f); GUI.DrawTexture(r, (Texture)_pixelTex); GUI.color = new Color(0.58f, 0.42f, 0.16f, 0.9f); GUI.DrawTexture(new Rect(((Rect)(ref r)).x, ((Rect)(ref r)).y, ((Rect)(ref r)).width, 1f), (Texture)_pixelTex); GUI.DrawTexture(new Rect(((Rect)(ref r)).x, ((Rect)(ref r)).yMax - 1f, ((Rect)(ref r)).width, 1f), (Texture)_pixelTex); GUI.DrawTexture(new Rect(((Rect)(ref r)).x, ((Rect)(ref r)).y, 1f, ((Rect)(ref r)).height), (Texture)_pixelTex); GUI.DrawTexture(new Rect(((Rect)(ref r)).xMax - 1f, ((Rect)(ref r)).y, 1f, ((Rect)(ref r)).height), (Texture)_pixelTex); GUI.color = color; } private void DrawBrowse(float refW, float refH) { //IL_0026: Unknown result type (might be due to invalid IL or missing references) //IL_0048: Unknown result type (might be due to invalid IL or missing references) //IL_0072: Unknown result type (might be due to invalid IL or missing references) //IL_00bf: Unknown result type (might be due to invalid IL or missing references) //IL_00f4: 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_0139: 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_01cf: Unknown result type (might be due to invalid IL or missing references) //IL_01d2: Unknown result type (might be due to invalid IL or missing references) //IL_01d7: Unknown result type (might be due to invalid IL or missing references) //IL_01d9: Unknown result type (might be due to invalid IL or missing references) //IL_01de: Unknown result type (might be due to invalid IL or missing references) //IL_0219: 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_025a: Unknown result type (might be due to invalid IL or missing references) //IL_0266: Unknown result type (might be due to invalid IL or missing references) //IL_04c9: Unknown result type (might be due to invalid IL or missing references) //IL_050a: Unknown result type (might be due to invalid IL or missing references) //IL_0551: 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_0284: Expected O, but got Unknown //IL_0286: Unknown result type (might be due to invalid IL or missing references) //IL_02aa: Unknown result type (might be due to invalid IL or missing references) //IL_02b5: Unknown result type (might be due to invalid IL or missing references) //IL_02bf: Expected O, but got Unknown //IL_02bf: Unknown result type (might be due to invalid IL or missing references) //IL_02cf: Unknown result type (might be due to invalid IL or missing references) //IL_02d4: Unknown result type (might be due to invalid IL or missing references) //IL_02de: Expected O, but got Unknown //IL_02e7: Unknown result type (might be due to invalid IL or missing references) //IL_0315: Unknown result type (might be due to invalid IL or missing references) //IL_0333: Unknown result type (might be due to invalid IL or missing references) //IL_0338: Unknown result type (might be due to invalid IL or missing references) //IL_0342: Expected O, but got Unknown //IL_035d: Unknown result type (might be due to invalid IL or missing references) //IL_0393: Unknown result type (might be due to invalid IL or missing references) //IL_0406: Unknown result type (might be due to invalid IL or missing references) //IL_045e: Unknown result type (might be due to invalid IL or missing references) //IL_05b6: Unknown result type (might be due to invalid IL or missing references) //IL_05dc: Unknown result type (might be due to invalid IL or missing references) //IL_05e7: Expected O, but got Unknown //IL_05fa: Unknown result type (might be due to invalid IL or missing references) //IL_0605: Expected O, but got Unknown float num = Mathf.Min(1200f, refW - 24f); float num2 = Mathf.Min(825f, refH - 24f); Rect r = default(Rect); ((Rect)(ref r))..ctor((refW - num) / 2f, (refH - num2) / 2f, num, num2); DrawPanel(r); if (GUI.Button(new Rect(((Rect)(ref r)).xMax - 42f, ((Rect)(ref r)).y + 10f, 30f, 28f), "X", _btnStyle)) { CloseChoiceTemporarily(); return; } if (ClanConfig.FreebornEnabled != null) { DrawBrowseRedesigned(r); return; } GUI.Label(new Rect(((Rect)(ref r)).x, ((Rect)(ref r)).y + 18f, ((Rect)(ref r)).width, 36f), "CHOOSE YOUR CLAN", _titleStyle); GUI.Label(new Rect(((Rect)(ref r)).x, ((Rect)(ref r)).y + 56f, ((Rect)(ref r)).width, 20f), "Join a clan, found your own, or walk free as Freeborn.", _subtitleStyle); WarfareData warfareData = WarfareManager.Instance?.Data; List list = ((warfareData != null) ? new List(warfareData.Clans.Values) : new List()); Rect val = default(Rect); ((Rect)(ref val))..ctor(((Rect)(ref r)).x + 24f, ((Rect)(ref r)).y + 90f, ((Rect)(ref r)).width - 48f, Mathf.Max(120f, ((Rect)(ref r)).height - 230f)); float num3 = 64f; Rect val2 = default(Rect); ((Rect)(ref val2))..ctor(0f, 0f, ((Rect)(ref val)).width - 20f, Mathf.Max(((Rect)(ref val)).height, (float)list.Count * (num3 + 6f))); _browseScroll = GUI.BeginScrollView(val, _browseScroll, val2); if (list.Count == 0) { GUI.Label(new Rect(0f, 10f, ((Rect)(ref val2)).width, 30f), "No clans exist yet — be the first to found one!", _subtitleStyle); } Rect val3 = default(Rect); for (int i = 0; i < list.Count; i++) { Clan clan = list[i]; ((Rect)(ref val3))..ctor(0f, (float)i * (num3 + 6f), ((Rect)(ref val2)).width, num3); bool flag = ((Rect)(ref val3)).Contains(Event.current.mousePosition); GUI.DrawTexture(val3, (Texture)(flag ? _cardHoverTex : _cardTex)); GUI.color = clan.Color; GUI.DrawTexture(new Rect(((Rect)(ref val3)).x, ((Rect)(ref val3)).y, 5f, ((Rect)(ref val3)).height), (Texture)_pixelTex); GUI.color = Color.white; GUIStyle val4 = new GUIStyle(_titleStyle) { fontSize = 30 }; val4.normal.textColor = clan.Color; GUI.Label(new Rect(((Rect)(ref val3)).x + 14f, ((Rect)(ref val3)).y + 14f, 44f, 40f), ClanConfig.GetIcon(clan.IconIndex), val4); GUIStyle val5 = new GUIStyle(_roleNameStyle) { fontSize = 17 }; val5.normal.textColor = new Color(1f, 0.82f, 0.28f, 1f); GUI.Label(new Rect(((Rect)(ref val3)).x + 66f, ((Rect)(ref val3)).y + 8f, ((Rect)(ref val3)).width - 200f, 24f), clan.Name + " [" + clan.Tag + "]", val5); int clanMemberCount = warfareData.GetClanMemberCount(clan.ClanId); int clanTerritoryCount = warfareData.GetClanTerritoryCount(clan.ClanId); GUI.Label(new Rect(((Rect)(ref val3)).x + 66f, ((Rect)(ref val3)).y + 32f, ((Rect)(ref val3)).width - 200f, 22f), $"{clanMemberCount} members {clanTerritoryCount} territories {clan.Treasury} coins", _bodyStyle); if (GUI.Button(new Rect(((Rect)(ref val3)).xMax - 110f, ((Rect)(ref val3)).y + 16f, 96f, 32f), "Join", _btnStyle)) { WarfareRPC.Instance?.RequestJoinClan(clan.ClanId); } } GUI.EndScrollView(); GUI.Label(new Rect(((Rect)(ref r)).x + 28f, ((Rect)(ref r)).yMax - 108f, 180f, 40f), "Free solo start. Claim a Homestead; later outposts and upgrades may cost resources.", _bodyStyle); GUI.Label(new Rect(((Rect)(ref r)).x + ((Rect)(ref r)).width - 208f, ((Rect)(ref r)).yMax - 62f, 180f, 40f), "Free clan founding. Growth, territory, vault and upgrades may cost resources.", _bodyStyle); if (GUI.Button(new Rect(((Rect)(ref r)).x + ((Rect)(ref r)).width / 2f - 140f, ((Rect)(ref r)).yMax - 64f, 280f, 40f), "âš’ Create a New Clan", _btnStyle)) { OpenCreate(); } ConfigEntry freebornEnabled = ClanConfig.FreebornEnabled; if (freebornEnabled != null && freebornEnabled.Value && GUI.Button(new Rect(((Rect)(ref r)).x + ((Rect)(ref r)).width / 2f - 140f, ((Rect)(ref r)).yMax - 110f, 280f, 40f), "Play As Freeborn", _btnStyle)) { Plugin.Log.LogInfo((object)("[FCW UI] Play As Freeborn clicked. rpc=" + ((Object)WarfareRPC.Instance != (Object)null) + " player=" + (((Object)Player.m_localPlayer != (Object)null) ? Player.m_localPlayer.GetPlayerID().ToString() : "none"))); WarfareRPC.Instance?.RequestPlayAsFreeborn(); } } private void DrawBrowseRedesigned(Rect r) { //IL_0020: Unknown result type (might be due to invalid IL or missing references) //IL_0055: Unknown result type (might be due to invalid IL or missing references) //IL_00d8: Unknown result type (might be due to invalid IL or missing references) //IL_00df: Unknown result type (might be due to invalid IL or missing references) //IL_00e9: Expected O, but got Unknown //IL_00e9: Unknown result type (might be due to invalid IL or missing references) //IL_00f1: Unknown result type (might be due to invalid IL or missing references) //IL_00fb: Expected O, but got Unknown //IL_01fb: Unknown result type (might be due to invalid IL or missing references) //IL_023c: Unknown result type (might be due to invalid IL or missing references) //IL_027d: Unknown result type (might be due to invalid IL or missing references) //IL_03d0: Unknown result type (might be due to invalid IL or missing references) //IL_0411: 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_0345: Unknown result type (might be due to invalid IL or missing references) //IL_0350: Expected O, but got Unknown //IL_0363: Unknown result type (might be due to invalid IL or missing references) //IL_036e: Expected O, but got Unknown //IL_04c8: Unknown result type (might be due to invalid IL or missing references) //IL_04cb: Unknown result type (might be due to invalid IL or missing references) //IL_04d0: Unknown result type (might be due to invalid IL or missing references) //IL_04d2: Unknown result type (might be due to invalid IL or missing references) //IL_04d7: Unknown result type (might be due to invalid IL or missing references) //IL_0501: Unknown result type (might be due to invalid IL or missing references) //IL_054a: Unknown result type (might be due to invalid IL or missing references) //IL_0552: Unknown result type (might be due to invalid IL or missing references) //IL_055c: Expected O, but got Unknown //IL_0580: Unknown result type (might be due to invalid IL or missing references) //IL_0591: Unknown result type (might be due to invalid IL or missing references) //IL_059b: Expected O, but got Unknown //IL_05a1: Unknown result type (might be due to invalid IL or missing references) //IL_05a6: Unknown result type (might be due to invalid IL or missing references) //IL_05b0: Expected O, but got Unknown //IL_05cb: Unknown result type (might be due to invalid IL or missing references) //IL_0601: Unknown result type (might be due to invalid IL or missing references) //IL_067f: Unknown result type (might be due to invalid IL or missing references) //IL_080e: Unknown result type (might be due to invalid IL or missing references) //IL_0857: Unknown result type (might be due to invalid IL or missing references) //IL_079b: Unknown result type (might be due to invalid IL or missing references) //IL_0720: Unknown result type (might be due to invalid IL or missing references) GUI.Label(new Rect(((Rect)(ref r)).x, ((Rect)(ref r)).y + 18f, ((Rect)(ref r)).width, 34f), "CHOOSE YOUR PATH", _titleStyle); GUI.Label(new Rect(((Rect)(ref r)).x, ((Rect)(ref r)).y + 52f, ((Rect)(ref r)).width, 22f), "Walk alone as Freeborn or found/join a clan.", _subtitleStyle); float num = 24f; float num2 = 18f; float num3 = (((Rect)(ref r)).width - num * 2f - num2) / 2f; Rect val = default(Rect); ((Rect)(ref val))..ctor(((Rect)(ref r)).x + num, ((Rect)(ref r)).y + 88f, num3, ((Rect)(ref r)).height - 112f); Rect val2 = default(Rect); ((Rect)(ref val2))..ctor(((Rect)(ref val)).xMax + num2, ((Rect)(ref val)).y, num3, ((Rect)(ref val)).height); GUI.DrawTexture(val, (Texture)_cardTex); GUI.DrawTexture(val2, (Texture)_cardTex); WarfareData warfareData = WarfareManager.Instance?.Data; int num4 = 0; int num5 = 0; int num6 = 0; if (warfareData != null) { foreach (PlayerClanInfo value in warfareData.Players.Values) { if (value.IsFreeborn && value.ClanId == 0) { num4++; } } foreach (ClanTerritory value2 in warfareData.Territories.Values) { if (value2.FreebornOwnerId != 0L) { if (value2.Type == TerritoryType.Homestead) { num5++; } else if (value2.Type == TerritoryType.Outpost) { num6++; } } } } GUI.Label(new Rect(((Rect)(ref val)).x + 16f, ((Rect)(ref val)).y + 14f, ((Rect)(ref val)).width - 32f, 26f), "FREEBORN", _roleNameStyle); GUI.Label(new Rect(((Rect)(ref val)).x + 16f, ((Rect)(ref val)).y + 48f, ((Rect)(ref val)).width - 32f, 108f), "Solo start with no clan. Your first claim becomes a Homestead protected by Odin's Peace. Later claims become Outposts. Outposts and upgrades can cost resources.", _bodyStyle); Rect val3 = new Rect(((Rect)(ref val)).x + 16f, ((Rect)(ref val)).y + 162f, ((Rect)(ref val)).width - 32f, 96f); object[] obj = new object[4] { num4, num5, num6, null }; ConfigEntry odinsPeaceEnabled = ClanConfig.OdinsPeaceEnabled; obj[3] = ((odinsPeaceEnabled != null && odinsPeaceEnabled.Value) ? "Enabled" : "Disabled"); GUI.Label(val3, string.Format("Freeborns: {0}\nHomesteads: {1}\nOutposts: {2}\nOdin's Peace: {3}", obj), _bodyStyle); ConfigEntry freebornEnabled = ClanConfig.FreebornEnabled; if (freebornEnabled != null && freebornEnabled.Value && GUI.Button(new Rect(((Rect)(ref val)).x + 24f, ((Rect)(ref val)).yMax - 88f, ((Rect)(ref val)).width - 48f, 40f), "Play As Freeborn", _btnStyle)) { Plugin.Log.LogInfo((object)("[FCW UI] Play As Freeborn clicked. rpc=" + ((Object)WarfareRPC.Instance != (Object)null) + " player=" + (((Object)Player.m_localPlayer != (Object)null) ? Player.m_localPlayer.GetPlayerID().ToString() : "none"))); WarfareRPC.Instance?.RequestPlayAsFreeborn(); } GUI.Label(new Rect(((Rect)(ref val)).x + 24f, ((Rect)(ref val)).yMax - 42f, ((Rect)(ref val)).width - 48f, 34f), "Free start. Claim a Homestead to begin.", _subtitleStyle); GUI.Label(new Rect(((Rect)(ref val2)).x + 16f, ((Rect)(ref val2)).y + 14f, ((Rect)(ref val2)).width - 32f, 26f), "CLANS", _roleNameStyle); List list = ((warfareData != null) ? new List(warfareData.Clans.Values) : new List()); Rect val4 = default(Rect); ((Rect)(ref val4))..ctor(((Rect)(ref val2)).x + 14f, ((Rect)(ref val2)).y + 48f, ((Rect)(ref val2)).width - 28f, ((Rect)(ref val2)).height - 152f); float num7 = 74f; Rect val5 = default(Rect); ((Rect)(ref val5))..ctor(0f, 0f, ((Rect)(ref val4)).width - 20f, Mathf.Max(((Rect)(ref val4)).height, (float)Mathf.Max(1, list.Count) * (num7 + 6f))); _browseScroll = GUI.BeginScrollView(val4, _browseScroll, val5); if (list.Count == 0) { GUI.Label(new Rect(8f, 12f, ((Rect)(ref val5)).width - 16f, 60f), "No clans exist yet. Create the first clan for free.", _bodyStyle); } Rect val6 = default(Rect); for (int i = 0; i < list.Count; i++) { Clan clan = list[i]; ((Rect)(ref val6))..ctor(0f, (float)i * (num7 + 6f), ((Rect)(ref val5)).width, num7); GUI.DrawTexture(val6, (Texture)_cardHoverTex); GUI.DrawTexture(new Rect(((Rect)(ref val6)).x + 8f, ((Rect)(ref val6)).y + 10f, 38f, 38f), (Texture)LocalEmblemCache.GetTexture(clan.ClanEmblemName)); GUIStyle val7 = new GUIStyle(_roleNameStyle) { fontSize = 15 }; val7.normal.textColor = new Color(1f, 0.82f, 0.28f, 1f); GUI.Label(new Rect(((Rect)(ref val6)).x + 54f, ((Rect)(ref val6)).y + 6f, ((Rect)(ref val6)).width - 142f, 22f), ClanConfig.GetIcon(clan.IconIndex) + " " + clan.Name + " [" + clan.Tag + "]", val7); GUI.Label(new Rect(((Rect)(ref val6)).x + 54f, ((Rect)(ref val6)).y + 30f, ((Rect)(ref val6)).width - 142f, 20f), $"{warfareData.GetClanMemberCount(clan.ClanId)} members {warfareData.GetClanTerritoryCount(clan.ClanId)} territories", _bodyStyle); ConfigEntry enableClanLinks = ClanConfig.EnableClanLinks; if (enableClanLinks != null && enableClanLinks.Value && (!string.IsNullOrEmpty(clan.ClanDiscordUrl) || !string.IsNullOrEmpty(clan.ClanWebsiteUrl))) { GUI.Label(new Rect(((Rect)(ref val6)).x + 54f, ((Rect)(ref val6)).y + 50f, ((Rect)(ref val6)).width - 142f, 20f), "Discord: " + (string.IsNullOrEmpty(clan.ClanDiscordUrl) ? "None" : clan.ClanDiscordUrl) + " Website: " + (string.IsNullOrEmpty(clan.ClanWebsiteUrl) ? "None" : clan.ClanWebsiteUrl), _bodyStyle); } if (GUI.Button(new Rect(((Rect)(ref val6)).xMax - 82f, ((Rect)(ref val6)).y + 13f, 72f, 30f), "Join", _btnStyle)) { WarfareRPC.Instance?.RequestJoinClan(clan.ClanId); } } GUI.EndScrollView(); if (GUI.Button(new Rect(((Rect)(ref val2)).x + 24f, ((Rect)(ref val2)).yMax - 88f, ((Rect)(ref val2)).width - 48f, 40f), "Create Free Clan", _btnStyle)) { OpenCreate(); } GUI.Label(new Rect(((Rect)(ref val2)).x + 24f, ((Rect)(ref val2)).yMax - 42f, ((Rect)(ref val2)).width - 48f, 34f), "Free founding. Growth and upgrades may cost resources.", _subtitleStyle); } private void DrawCreate(float refW, float refH) { //IL_006b: Unknown result type (might be due to invalid IL or missing references) //IL_00af: Unknown result type (might be due to invalid IL or missing references) //IL_010f: Unknown result type (might be due to invalid IL or missing references) //IL_0124: Unknown result type (might be due to invalid IL or missing references) //IL_014b: Unknown result type (might be due to invalid IL or missing references) //IL_0199: Unknown result type (might be due to invalid IL or missing references) //IL_01cf: Unknown result type (might be due to invalid IL or missing references) //IL_01f6: 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_0254: Unknown result type (might be due to invalid IL or missing references) //IL_0266: 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) //IL_02d5: Unknown result type (might be due to invalid IL or missing references) //IL_02e6: Unknown result type (might be due to invalid IL or missing references) //IL_02f0: Unknown result type (might be due to invalid IL or missing references) //IL_02f8: Unknown result type (might be due to invalid IL or missing references) //IL_0302: Expected O, but got Unknown //IL_043e: Unknown result type (might be due to invalid IL or missing references) //IL_0335: Unknown result type (might be due to invalid IL or missing references) //IL_0319: Unknown result type (might be due to invalid IL or missing references) //IL_0323: 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) //IL_0335: Expected O, but got Unknown //IL_046a: Unknown result type (might be due to invalid IL or missing references) //IL_034c: Unknown result type (might be due to invalid IL or missing references) //IL_0382: Unknown result type (might be due to invalid IL or missing references) //IL_038d: Unknown result type (might be due to invalid IL or missing references) //IL_0397: Expected O, but got Unknown //IL_03bd: Unknown result type (might be due to invalid IL or missing references) //IL_03c8: Unknown result type (might be due to invalid IL or missing references) //IL_03d2: Expected O, but got Unknown //IL_03d2: Unknown result type (might be due to invalid IL or missing references) //IL_03e3: Unknown result type (might be due to invalid IL or missing references) //IL_03f1: Unknown result type (might be due to invalid IL or missing references) //IL_05ed: Unknown result type (might be due to invalid IL or missing references) //IL_0611: Unknown result type (might be due to invalid IL or missing references) //IL_06ee: Unknown result type (might be due to invalid IL or missing references) //IL_0674: Unknown result type (might be due to invalid IL or missing references) //IL_07f6: Unknown result type (might be due to invalid IL or missing references) //IL_0863: Unknown result type (might be due to invalid IL or missing references) //IL_0778: Unknown result type (might be due to invalid IL or missing references) //IL_078b: Unknown result type (might be due to invalid IL or missing references) //IL_08cd: Unknown result type (might be due to invalid IL or missing references) //IL_08d9: Unknown result type (might be due to invalid IL or missing references) //IL_08e3: Expected O, but got Unknown //IL_08e9: Unknown result type (might be due to invalid IL or missing references) //IL_08ee: Unknown result type (might be due to invalid IL or missing references) //IL_08f5: Unknown result type (might be due to invalid IL or missing references) //IL_08ff: Expected O, but got Unknown //IL_091a: Unknown result type (might be due to invalid IL or missing references) //IL_07b1: Expected O, but got Unknown //IL_0986: Unknown result type (might be due to invalid IL or missing references) //IL_09b1: Unknown result type (might be due to invalid IL or missing references) //IL_09fc: Unknown result type (might be due to invalid IL or missing references) //IL_0a87: Unknown result type (might be due to invalid IL or missing references) //IL_0ac0: Unknown result type (might be due to invalid IL or missing references) //IL_0a48: Unknown result type (might be due to invalid IL or missing references) //IL_0b26: Unknown result type (might be due to invalid IL or missing references) //IL_0b7c: Unknown result type (might be due to invalid IL or missing references) //IL_0b87: Expected O, but got Unknown int currentPlayerClanId = GetCurrentPlayerClanId(); if (currentPlayerClanId != 0) { Plugin.Log.LogInfo((object)("[FCW UI] Create screen blocked because player already has clanId=" + currentPlayerClanId)); CloseClanSelectionState(); return; } float num = Mathf.Min(1200f, refW - 24f); float num2 = Mathf.Min(825f, refH - 24f); Rect r = default(Rect); ((Rect)(ref r))..ctor((refW - num) / 2f, (refH - num2) / 2f, num, num2); DrawPanel(r); if (!_createWizardLogged) { _createWizardLogged = true; Plugin.Log.LogInfo((object)"[FCW UI] Create clan wizard drawing."); } GUI.Label(new Rect(((Rect)(ref r)).x, ((Rect)(ref r)).y + 18f, ((Rect)(ref r)).width, 36f), "CREATE CLAN", _titleStyle); float num3 = ((Rect)(ref r)).x + 40f; float num4 = ((Rect)(ref r)).width - 80f; float num5 = ((Rect)(ref r)).y + 70f; DrawSoftFrame(new Rect(num3 - 12f, num5 - 12f, num4 + 24f, 78f)); GUI.Label(new Rect(num3, num5, num4, 20f), "Clan name (3-24 characters)", _bodyStyle); _createName = GUI.TextField(new Rect(num3, num5 + 22f, num4, 32f), _createName ?? "", 24, _fieldStyle); num5 += 68f; DrawSoftFrame(new Rect(num3 - 12f, num5 - 8f, num4 + 24f, 154f)); float num6 = 210f; float num7 = num3 + num6 + 28f; float num8 = num4 - num6 - 28f; GUI.Label(new Rect(num3, num5, num6, 20f), "Clan tag (1-6 characters)", _bodyStyle); _createTag = GUI.TextField(new Rect(num3, num5 + 22f, num6, 32f), _createTag ?? "", 6, _fieldStyle); GUI.Label(new Rect(num7, num5, num8, 20f), "Clan border color", _bodyStyle); int num9 = 14; float num10 = 30f; float num11 = 34f; bool flag = IsClanColorTaken(ClanConfig.GetColor(_colorIndex)); bool flag2 = ClanConfig.IsReservedClanColor(ClanConfig.GetColor(_colorIndex)); int num12 = 0; Rect val = default(Rect); for (int i = 0; i < ClanConfig.ClanColors.Length; i++) { if (!ClanConfig.IsReservedClanColor(ClanConfig.ClanColors[i])) { int num13 = num12 / num9; int num14 = num12 % num9; ((Rect)(ref val))..ctor(num7 + (float)num14 * (num10 + 6f), num5 + 24f + (float)num13 * 42f, num10, num11); bool num15 = IsClanColorTaken(ClanConfig.ClanColors[i]); GUI.color = ClanConfig.ClanColors[i]; GUI.DrawTexture(val, (Texture)_pixelTex); if (num15) { GUI.color = new Color(0f, 0f, 0f, 0.55f); GUI.DrawTexture(val, (Texture)_pixelTex); } GUI.color = Color.white; if (i == _colorIndex) { GUI.color = Color.white; GUI.DrawTexture(new Rect(((Rect)(ref val)).x - 2f, ((Rect)(ref val)).y - 2f, ((Rect)(ref val)).width + 4f, 2f), (Texture)_pixelTex); GUI.DrawTexture(new Rect(((Rect)(ref val)).x - 2f, ((Rect)(ref val)).yMax, ((Rect)(ref val)).width + 4f, 2f), (Texture)_pixelTex); GUI.color = Color.white; } if (!num15 && (int)Event.current.type == 0 && ((Rect)(ref val)).Contains(Event.current.mousePosition)) { _colorIndex = i; Event.current.Use(); } num12++; } } if (flag2) { GUI.Label(new Rect(num7, num5 + 112f, num8, 20f), "Pure white and silver are reserved for Homestead territories.", _bodyStyle); } else if (flag) { GUI.Label(new Rect(num7, num5 + 112f, num8, 20f), "This clan color is already taken.", _bodyStyle); } num5 += 154f; List list = new List(LocalEmblemCache.GetCategories()); if (list.Count > 1) { list.RemoveAll((string c) => string.Equals(c, "Root", StringComparison.OrdinalIgnoreCase)); } if (list.Count == 0) { list.Add("Root"); } _createEmblemCategoryIndex = Mathf.Clamp(_createEmblemCategoryIndex, 0, list.Count - 1); List list2 = new List(LocalEmblemCache.GetNames(list[_createEmblemCategoryIndex])); if (list2.Count == 0) { list2 = GetLocalEmblemNames(); } int num16 = 14; int num17 = Mathf.Max(1, Mathf.CeilToInt((float)list2.Count / (float)num16)); _createEmblemPage = Mathf.Clamp(_createEmblemPage, 0, num17 - 1); _createEmblemIndex = Mathf.Clamp(_createEmblemIndex, 0, Mathf.Max(0, list2.Count - 1)); string text = list2[Mathf.Clamp(_createEmblemIndex, 0, list2.Count - 1)]; bool flag3 = IsClanEmblemTaken(text); Rect val2 = default(Rect); ((Rect)(ref val2))..ctor(num3, num5, 300f, 250f); Rect val3 = default(Rect); ((Rect)(ref val3))..ctor(num3 + 322f, num5, num4 - 322f, 250f); DrawSoftFrame(new Rect(num3 - 12f, num5 - 10f, num4 + 24f, 270f)); GUI.Label(new Rect(((Rect)(ref val2)).x, ((Rect)(ref val2)).y, ((Rect)(ref val2)).width, 20f), "Clan Emblem", _bodyStyle); float num18 = ((Rect)(ref val2)).y + 26f; float num19 = num18; for (int num20 = 0; num20 < list.Count && num20 < 12; num20++) { int num21 = num20 / 6; int num22 = num20 % 6; if (GUI.Button(new Rect(((Rect)(ref val2)).x + (float)num22 * 48f, num19 + (float)num21 * 24f, 44f, 22f), list[num20], (num20 == _createEmblemCategoryIndex) ? _btnStyle : GUI.skin.button)) { _createEmblemCategoryIndex = num20; _createEmblemPage = 0; _createEmblemIndex = 0; } } GUI.Label(new Rect(((Rect)(ref val2)).x, num18 + 54f, 140f, 20f), "Page " + (_createEmblemPage + 1) + " / " + num17, _bodyStyle); int num23 = _createEmblemPage * num16; for (int num24 = 0; num24 < num16 && num23 + num24 < list2.Count; num24++) { int num25 = num23 + num24; int num26 = num24 / 7; int num27 = num24 % 7; if (GUI.Button(new Rect(((Rect)(ref val2)).x + (float)num27 * 42f, num18 + 80f + (float)num26 * 44f, 38f, 38f), (Texture)LocalEmblemCache.GetTexture(list2[num25]), (num25 == _createEmblemIndex) ? _btnStyle : GUI.skin.button)) { _createEmblemIndex = num25; } } if (GUI.Button(new Rect(((Rect)(ref val2)).x, ((Rect)(ref val2)).yMax - 34f, 136f, 30f), "Previous Page", _btnStyle)) { _createEmblemPage = (_createEmblemPage - 1 + num17) % num17; _createEmblemIndex = Mathf.Clamp(_createEmblemPage * num16, 0, list2.Count - 1); } if (GUI.Button(new Rect(((Rect)(ref val2)).x + 148f, ((Rect)(ref val2)).yMax - 34f, 136f, 30f), "Next Page", _btnStyle)) { _createEmblemPage = (_createEmblemPage + 1) % num17; _createEmblemIndex = Mathf.Clamp(_createEmblemPage * num16, 0, list2.Count - 1); } GUI.DrawTexture(new Rect(((Rect)(ref val3)).x + 38f, ((Rect)(ref val3)).y + 18f, 210f, 210f), (Texture)LocalEmblemCache.GetTexture(text)); GUIStyle val4 = new GUIStyle(_roleNameStyle) { alignment = (TextAnchor)3, fontSize = 22 }; val4.normal.textColor = new Color(1f, 0.82f, 0.28f, 1f); string text2 = (string.IsNullOrEmpty(_createName) ? "Your Clan" : _createName); string text3 = (string.IsNullOrEmpty(_createTag) ? "TAG" : _createTag); Rect r2 = default(Rect); ((Rect)(ref r2))..ctor(num3 - 12f, ((Rect)(ref r)).yMax - 112f, num4 + 24f, 48f); DrawSoftFrame(r2); GUI.Label(new Rect(((Rect)(ref r2)).x + 18f, ((Rect)(ref r2)).y + 8f, 280f, 34f), text2 + " [" + text3 + "]", val4); GUI.Label(new Rect(((Rect)(ref r2)).x + 316f, ((Rect)(ref r2)).y + 8f, ((Rect)(ref r2)).width - 334f, 34f), "Selected: " + text, _bodyStyle); if (flag3) { GUI.Label(new Rect(((Rect)(ref r2)).x + 316f, ((Rect)(ref r2)).y + 27f, ((Rect)(ref r2)).width - 334f, 18f), "This emblem is already used by another clan.", _bodyStyle); } Rect r3 = default(Rect); ((Rect)(ref r3))..ctor(num3 - 12f, ((Rect)(ref r)).yMax - 62f, num4 + 24f, 52f); DrawSoftFrame(r3); if (GUI.Button(new Rect(((Rect)(ref r)).x + ((Rect)(ref r)).width / 2f - 200f, ((Rect)(ref r)).yMax - 56f, 190f, 38f), "Back", _btnStyle)) { Plugin.Log.LogInfo((object)"[FCW UI] Create clan wizard back clicked."); _mode = Mode.Browse; _createWizardLogged = false; } if (GUI.Button(new Rect(((Rect)(ref r)).x + ((Rect)(ref r)).width / 2f + 10f, ((Rect)(ref r)).yMax - 56f, 190f, 38f), "Create Clan", _btnStyle)) { Plugin.Log.LogInfo((object)("[FCW UI] Create button clicked. name='" + _createName + "' tag='" + _createTag + "' rpc=" + ((Object)WarfareRPC.Instance != (Object)null))); WarfareRPC.Instance?.RequestCreateClan(_createName, _createTag, _colorIndex, _iconIndex, "", text); } } private void CloseChoiceTemporarily() { int num = Mathf.Max(1, ClanConfig.ChoiceReminderMinutes?.Value ?? 10); _nextChoiceReminderTimestamp = DateTimeOffset.UtcNow.ToUnixTimeSeconds() + (long)num * 60L; IsOpen = false; _mode = Mode.Browse; Plugin.Log.LogInfo((object)("[FCW UI] Choose Path closed. Reminder in " + num + " minutes.")); } private List GetLocalEmblemNames() { return new List(LocalEmblemCache.GetNames()); } private bool IsClanColorTaken(Color color) { //IL_0049: Unknown result type (might be due to invalid IL or missing references) //IL_004e: Unknown result type (might be due to invalid IL or missing references) ConfigEntry requireUniqueClanColors = ClanConfig.RequireUniqueClanColors; if (requireUniqueClanColors == null || !requireUniqueClanColors.Value) { return false; } WarfareData warfareData = WarfareManager.Instance?.Data; if (warfareData == null) { return false; } foreach (Clan value in warfareData.Clans.Values) { if (ClanConfig.ColorsClose(value.Color, color)) { return true; } } return false; } private bool IsClanEmblemTaken(string emblemName) { WarfareData warfareData = WarfareManager.Instance?.Data; if (warfareData == null) { return false; } emblemName = LocalEmblemCache.NormalizeName(emblemName); foreach (Clan value in warfareData.Clans.Values) { if (string.Equals(LocalEmblemCache.NormalizeName(value.ClanEmblemName), emblemName, StringComparison.OrdinalIgnoreCase)) { return true; } } return false; } private void DrawRole(float refW, float refH) { //IL_0026: Unknown result type (might be due to invalid IL or missing references) //IL_0048: Unknown result type (might be due to invalid IL or missing references) //IL_006e: Unknown result type (might be due to invalid IL or missing references) //IL_00a3: Unknown result type (might be due to invalid IL or missing references) //IL_01ac: Unknown result type (might be due to invalid IL or missing references) //IL_0105: Unknown result type (might be due to invalid IL or missing references) //IL_010a: Unknown result type (might be due to invalid IL or missing references) //IL_0114: Expected O, but got Unknown //IL_012f: Unknown result type (might be due to invalid IL or missing references) //IL_0159: Unknown result type (might be due to invalid IL or missing references) float num = Mathf.Min(1200f, refW - 24f); float num2 = Mathf.Min(825f, refH - 24f); Rect r = default(Rect); ((Rect)(ref r))..ctor((refW - num) / 2f, (refH - num2) / 2f, num, num2); DrawPanel(r); GUI.Label(new Rect(((Rect)(ref r)).x, ((Rect)(ref r)).y + 18f, ((Rect)(ref r)).width, 36f), "CHOOSE YOUR ROLE", _titleStyle); GUI.Label(new Rect(((Rect)(ref r)).x, ((Rect)(ref r)).y + 54f, ((Rect)(ref r)).width, 22f), "Twelve paths. Endless adventures. Your saga begins in Valheim.", _subtitleStyle); WarfareData warfareData = WarfareManager.Instance?.Data; PlayerClanInfo playerClanInfo = warfareData?.GetPlayer(Player.m_localPlayer.GetPlayerID()); Clan clan = ((playerClanInfo != null) ? warfareData.GetClan(playerClanInfo.ClanId) : null); if (clan != null) { GUIStyle val = new GUIStyle(_subtitleStyle) { fontSize = 14 }; val.normal.textColor = new Color(1f, 0.82f, 0.28f, 1f); GUI.Label(new Rect(((Rect)(ref r)).x, ((Rect)(ref r)).y + 76f, ((Rect)(ref r)).width, 22f), ClanConfig.GetIcon(clan.IconIndex) + " " + clan.Name + " [" + clan.Tag + "]", val); } DrawRoleTiles(r, clan); } private void DrawRoleTiles(Rect r, Clan clan) { //IL_0082: Unknown result type (might be due to invalid IL or missing references) //IL_008f: Unknown result type (might be due to invalid IL or missing references) //IL_00ca: Unknown result type (might be due to invalid IL or missing references) //IL_00e1: Unknown result type (might be due to invalid IL or missing references) //IL_00eb: Expected O, but got Unknown //IL_017e: Unknown result type (might be due to invalid IL or missing references) //IL_0185: Unknown result type (might be due to invalid IL or missing references) //IL_0191: Expected O, but got Unknown //IL_0197: Unknown result type (might be due to invalid IL or missing references) //IL_019c: Unknown result type (might be due to invalid IL or missing references) //IL_00ef: Unknown result type (might be due to invalid IL or missing references) //IL_0125: Unknown result type (might be due to invalid IL or missing references) //IL_0130: Unknown result type (might be due to invalid IL or missing references) //IL_013a: Expected O, but got Unknown //IL_013a: Unknown result type (might be due to invalid IL or missing references) //IL_01be: Expected O, but got Unknown //IL_01ea: Unknown result type (might be due to invalid IL or missing references) //IL_0203: Unknown result type (might be due to invalid IL or missing references) //IL_0208: Unknown result type (might be due to invalid IL or missing references) //IL_020f: Unknown result type (might be due to invalid IL or missing references) //IL_0219: Expected O, but got Unknown //IL_0245: Unknown result type (might be due to invalid IL or missing references) //IL_025e: Unknown result type (might be due to invalid IL or missing references) //IL_0263: Unknown result type (might be due to invalid IL or missing references) //IL_026a: Unknown result type (might be due to invalid IL or missing references) //IL_0274: Expected O, but got Unknown //IL_028f: Unknown result type (might be due to invalid IL or missing references) //IL_02c5: Unknown result type (might be due to invalid IL or missing references) //IL_02de: Unknown result type (might be due to invalid IL or missing references) //IL_02e3: Unknown result type (might be due to invalid IL or missing references) //IL_02ed: Expected O, but got Unknown //IL_0303: Unknown result type (might be due to invalid IL or missing references) //IL_0339: Unknown result type (might be due to invalid IL or missing references) //IL_0379: Unknown result type (might be due to invalid IL or missing references) float num = (((Rect)(ref r)).width - 110f) / 4f; float num2 = 162f; float num3 = ((Rect)(ref r)).x + 24f; float num4 = ((Rect)(ref r)).y + 104f; Rect r2 = default(Rect); for (int i = 0; i < RoleEntries.Length; i++) { (ClanRole, string, string, string, string, string, bool) tuple = RoleEntries[i]; int num5 = i % 4; int num6 = i / 4; ((Rect)(ref r2))..ctor(num3 + (float)num5 * (num + 18f), num4 + (float)num6 * (num2 + 8f), num, num2); bool flag = ((Rect)(ref r2)).Contains(Event.current.mousePosition); DrawSoftFrame(r2); GUI.DrawTexture(new Rect(((Rect)(ref r2)).x + 3f, ((Rect)(ref r2)).y + 3f, ((Rect)(ref r2)).width - 6f, ((Rect)(ref r2)).height - 6f), (Texture)(flag ? _cardHoverTex : _cardTex)); if (clan != null) { GUI.color = clan.Color; GUI.DrawTexture(new Rect(((Rect)(ref r2)).x + 1f, ((Rect)(ref r2)).y + 1f, 5f, ((Rect)(ref r2)).height - 2f), (Texture)_pixelTex); GUI.color = Color.white; } Texture2D roleTexture = GetRoleTexture(tuple.Item1, tuple.Item6); GUI.DrawTexture(new Rect(((Rect)(ref r2)).x + 10f, ((Rect)(ref r2)).y + 28f, 70f, 70f), (Texture)roleTexture, (ScaleMode)2, true); GUIStyle val = new GUIStyle(_roleNameStyle) { fontSize = (tuple.Item2.StartsWith("COMMUNITY", StringComparison.OrdinalIgnoreCase) ? 15 : 18) }; GUI.Label(new Rect(((Rect)(ref r2)).x + 88f, ((Rect)(ref r2)).y + 10f, ((Rect)(ref r2)).width - 102f, 22f), tuple.Item2, val); GUIStyle val2 = new GUIStyle(_subtitleStyle) { alignment = (TextAnchor)3, fontSize = 12 }; GUI.Label(new Rect(((Rect)(ref r2)).x + 88f, ((Rect)(ref r2)).y + 34f, ((Rect)(ref r2)).width - 102f, 16f), tuple.Item3, val2); GUIStyle val3 = new GUIStyle(_bodyStyle) { fontStyle = (FontStyle)2, fontSize = 12 }; val3.normal.textColor = new Color(0.78f, 0.68f, 0.47f, 1f); GUI.Label(new Rect(((Rect)(ref r2)).x + 88f, ((Rect)(ref r2)).y + 52f, ((Rect)(ref r2)).width - 102f, 44f), tuple.Item4, val3); GUIStyle val4 = new GUIStyle(_bodyStyle) { fontSize = 11 }; val4.normal.textColor = new Color(0.85f, 0.72f, 0.35f); GUI.Label(new Rect(((Rect)(ref r2)).x + 88f, ((Rect)(ref r2)).y + 98f, ((Rect)(ref r2)).width - 102f, 34f), tuple.Item5, val4); if (tuple.Item7 && GUI.Button(new Rect(((Rect)(ref r2)).xMax - 92f, ((Rect)(ref r2)).yMax - 32f, 78f, 24f), "Choose", _btnStyle)) { WarfareRPC.Instance?.RequestSetRole(tuple.Item1); } } } private Texture2D GetRoleTexture(ClanRole role, string iconName) { if (_roleTextures == null) { LoadRoleTextures(); } string[] array = RoleIconAliases(role, iconName); for (int i = 0; i < array.Length; i++) { if (_roleTextures.TryGetValue(array[i], out var value)) { return value; } } return _pixelTex; } private string[] RoleIconAliases(ClanRole role, string iconName) { string text = iconName ?? role.ToString(); if (role != ClanRole.Guardian) { return new string[1] { text }; } return new string[2] { text, "shieldbearer" }; } private void LoadRoleTextures() { //IL_005a: Unknown result type (might be due to invalid IL or missing references) //IL_0061: Expected O, but got Unknown _roleTextures = new Dictionary(StringComparer.OrdinalIgnoreCase); string path = Path.Combine(Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location) ?? Paths.PluginPath, "Roles"); if (!Directory.Exists(path)) { return; } string[] files = Directory.GetFiles(path, "*.png"); for (int i = 0; i < files.Length; i++) { try { byte[] array = File.ReadAllBytes(files[i]); Texture2D val = new Texture2D(2, 2, (TextureFormat)4, false); if (ImageConversion.LoadImage(val, array)) { ((Object)val).name = Path.GetFileNameWithoutExtension(files[i]); _roleTextures[((Object)val).name] = val; } } catch (Exception ex) { Plugin.Log.LogWarning((object)("[FCW Roles] Failed to load role artwork: " + files[i] + " " + ex.Message)); } } } } public static class ClanWorldMapPanel { private enum MapState { Idle, Invalid, Waiting, Authorized, Denied, TimedOut, Disconnected, ProtocolMismatch } private enum Accordion { None, Layers, Filters, Access, Pin, Drawing, Selected } private enum TypeFilter { AllTypes, Capitals, Colonies, Corrupted } private enum WhoFilter { AllClaimed, OwnClan, OtherClans } private sealed class PolyMask { public Texture2D Texture; public Texture2D Outline; public float MinX; public float MaxX; public float MinY; public float MaxY; public int Revision; } private sealed class Hit { public ClanMapPublicTerritoryDto Territory; public List Screen; public float Area; } private const float MinZoom = 1f; private const float MaxZoom = 6f; private const float RequestCooldown = 2f; private const float RequestTimeout = 8f; private const float SnapshotCacheSeconds = 30f; private const float DragThreshold = 5f; private static ClanMapSnapshot _snapshot; private static ClanMapTerritoryDetailDto _privateDetail; private static ClanMapPublicTerritoryDetailDto _publicDetail; private static MapState _state = MapState.Invalid; private static Accordion _accordion = Accordion.Layers; private static TypeFilter _typeFilter; private static WhoFilter _whoFilter; private static int _localClanId; private static float _lastRequest = -999f; private static float _received = -999f; private static float _requestStarted = -999f; private static int _retries; private static Vector2 _sectionScroll; private static Vector2 _pan; private static float _zoom = 1f; private static bool _pressed; private static bool _dragging; private static bool _showLabels = false; private static bool _showTerritories = true; private static Vector2 _press; private static Vector2 _panStart; private static string _selectedId = ""; private static string _pendingId = ""; private static string _message = ""; private static Texture2D _fog; private static Texture2D _worldRing; private static Texture2D _pixel; private static GUIStyle _center; private static GUIStyle _small; private static readonly Dictionary Masks = new Dictionary(); public static bool TerritoriesVisible { get { return _showTerritories; } set { _showTerritories = value; } } public static void OpenDrawingSection() { _accordion = Accordion.Drawing; } public static void OpenPinSection() { _accordion = Accordion.Pin; } public static void PrepareForClan(int id) { if (_localClanId != id) { _localClanId = id; ClanMapPinUi.ClearAll(); ClanMapDrawingUi.ClearAll(); ClearData(); _state = MapState.Invalid; _lastRequest = -999f; _requestStarted = -999f; _retries = 0; ResetView(); } } public static void Invalidate() { ClanMapPinUi.ClearAll(); ClanMapDrawingUi.ClearAll(); ClearData(); _state = MapState.Invalid; } private static void ClearData(bool preserveSelection = false) { _snapshot = null; if (!preserveSelection) { _privateDetail = null; _publicDetail = null; _selectedId = (_pendingId = (_message = "")); } _pressed = (_dragging = false); DisposeMasks(); } private static void DisposeMasks() { //IL_0023: Unknown result type (might be due to invalid IL or missing references) //IL_002e: Expected O, but got Unknown //IL_0046: Unknown result type (might be due to invalid IL or missing references) //IL_0051: Expected O, but got Unknown //IL_0036: Unknown result type (might be due to invalid IL or missing references) //IL_0040: Expected O, but got Unknown //IL_0059: Unknown result type (might be due to invalid IL or missing references) //IL_0063: Expected O, but got Unknown foreach (PolyMask value in Masks.Values) { if (value != null) { if ((Object)value.Texture != (Object)null) { Object.Destroy((Object)value.Texture); } if ((Object)value.Outline != (Object)null) { Object.Destroy((Object)value.Outline); } } } Masks.Clear(); } public static void ReceiveSnapshot(ClanMapSnapshot s) { if (s == null || s.ProtocolVersion != 5) { ClanMapPinUi.ClearAll(); ClanMapDrawingUi.ClearAll(); ClearData(); _state = MapState.ProtocolMismatch; return; } if (_localClanId == 0 || s.ClanID != _localClanId || s.Territories == null || s.PublicTerritories == null) { ClanMapPinUi.ClearAll(); ClanMapDrawingUi.ClearAll(); ClearData(); _state = MapState.Invalid; return; } for (int num = s.PublicTerritories.Count - 1; num >= 0; num--) { if (!Valid(s.PublicTerritories[num])) { s.PublicTerritories.RemoveAt(num); } } _snapshot = s; ClanMapPinUi.Configure(s); ClanMapDrawingUi.Configure(s); _received = Time.unscaledTime; _requestStarted = -999f; _retries = 0; _state = MapState.Authorized; Plugin.Log.LogInfo((object)("[FCW ClanMap] UI authorized public=" + s.PublicTerritories.Count)); if (!string.IsNullOrEmpty(_selectedId) && !s.PublicTerritories.Exists((ClanMapPublicTerritoryDto x) => x.TerritoryID == _selectedId)) { ClearSelection("Territory is no longer available."); } } private static bool Valid(ClanMapPublicTerritoryDto t) { if (t != null && !string.IsNullOrEmpty(t.TerritoryID) && t.OwnerClanID != 0 && t.GeometryPoints != null && t.GeometryPoints.Count >= 3) { return t.GeometryPoints.Count <= 256; } return false; } public static void ReceiveAccessDenied(string r) { ClanMapPinUi.ClearAll(); ClanMapDrawingUi.ClearAll(); ClearData(); _state = ((r != null && r.IndexOf("protocol", StringComparison.OrdinalIgnoreCase) >= 0) ? MapState.ProtocolMismatch : MapState.Denied); } public static void ReceiveInvalidSnapshot() { ClanMapPinUi.ClearAll(); ClanMapDrawingUi.ClearAll(); ClearData(); _state = MapState.Invalid; } public static void ReceiveTerritoryDetail(ClanMapTerritoryDetailDto d) { if (d == null || d.TerritoryID != _selectedId) { ReceiveTerritoryDetailDenied("Invalid territory detail."); return; } _privateDetail = d; _publicDetail = null; _pendingId = ""; _message = ""; _accordion = Accordion.Selected; } public static void ReceiveTerritoryDetailDenied(string r) { ClearSelection(string.IsNullOrEmpty(r) ? "Territory unavailable." : r); } public static void ReceivePublicTerritoryDetail(ClanMapPublicTerritoryDetailDto d) { if (d == null || d.TerritoryID != _selectedId) { ReceivePublicTerritoryDetailDenied("Invalid public territory detail."); return; } _publicDetail = d; _privateDetail = null; _pendingId = ""; _message = ""; _accordion = Accordion.Selected; } public static void ReceivePublicTerritoryDetailDenied(string r) { ClearSelection(string.IsNullOrEmpty(r) ? "Public territory unavailable." : r); } private static void ClearSelection(string message = "") { _selectedId = (_pendingId = ""); _privateDetail = null; _publicDetail = null; _message = message; } public static void Draw(PlayerClanInfo info, float width, float availableHeight, GUIStyle panel, GUIStyle label, GUIStyle gold, GUIStyle button, GUIStyle smallButton) { //IL_002e: Unknown result type (might be due to invalid IL or missing references) //IL_0039: Expected O, but got Unknown Ensure(label); int num = info?.ClanId ?? 0; PrepareForClan(num); if (num == 0) { Centered("Join a clan to access the World Map.", availableHeight, gold); return; } if ((Object)ZNet.instance == (Object)null || ZRoutedRpc.instance == null) { ClanMapPinUi.ClearAll(); ClanMapDrawingUi.ClearAll(); ClearData(); _state = MapState.Disconnected; } else if (_state == MapState.Disconnected || _state == MapState.Invalid || _state == MapState.Idle) { RequestSnapshot(); } else if (_state == MapState.Waiting) { CheckTimeout(); } else if (_state == MapState.Authorized && Time.unscaledTime - _received >= 30f) { RequestSnapshot(); } float num2 = Mathf.Max(330f, availableHeight - 44f); float num3 = Mathf.Clamp(width * 0.21f, 310f, 365f); bool flag = ClanMapPinUi.LegendVisible && width >= num3 + 796f; float num4 = (flag ? 220f : 0f); float w = Mathf.Max(560f, width - num3 - 8f - (flag ? (num4 + 8f) : 0f)); GUILayout.BeginHorizontal((GUILayoutOption[])(object)new GUILayoutOption[2] { GUILayout.Width(width), GUILayout.Height(num2) }); DrawLeft(num3, num2, panel, label, gold, smallButton); GUILayout.Space(8f); DrawMap(w, num2, panel, label, button, smallButton); if (flag) { GUILayout.Space(8f); ClanMapPinUi.DrawLegend(num4, num2, panel, label, gold, smallButton, _small); } GUILayout.EndHorizontal(); GUILayout.Space(28f); } private static bool Header(string title, Accordion section, GUIStyle style) { //IL_0009: Unknown result type (might be due to invalid IL or missing references) //IL_000e: 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_0014: Unknown result type (might be due to invalid IL or missing references) //IL_00c0: Unknown result type (might be due to invalid IL or missing references) //IL_00c6: Unknown result type (might be due to invalid IL or missing references) //IL_0047: Unknown result type (might be due to invalid IL or missing references) //IL_002c: Unknown result type (might be due to invalid IL or missing references) //IL_006f: Unknown result type (might be due to invalid IL or missing references) //IL_0054: Unknown result type (might be due to invalid IL or missing references) bool flag = _accordion == section; Color backgroundColor = GUI.backgroundColor; Color contentColor = GUI.contentColor; try { GUI.backgroundColor = (flag ? new Color(0.72f, 0.49f, 0.12f, 1f) : new Color(0.24f, 0.3f, 0.32f, 1f)); GUI.contentColor = (Color)(flag ? new Color(1f, 0.88f, 0.55f, 1f) : Color.white); if (GUILayout.Button((flag ? "▼ " : "▶ ") + title, style, (GUILayoutOption[])(object)new GUILayoutOption[2] { GUILayout.ExpandWidth(true), GUILayout.Height(30f) })) { _accordion = ((!flag) ? section : Accordion.None); } } finally { GUI.backgroundColor = backgroundColor; GUI.contentColor = contentColor; } return _accordion == section; } private static void DrawLeft(float w, float h, GUIStyle panel, GUIStyle label, GUIStyle gold, GUIStyle header) { //IL_00eb: 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_0106: Unknown result type (might be due to invalid IL or missing references) GUILayout.BeginVertical(panel, (GUILayoutOption[])(object)new GUILayoutOption[2] { GUILayout.Width(w), GUILayout.Height(h) }); if (Header("MAP LAYERS", Accordion.Layers, header)) { DrawLayersContent(label, header); } GUILayout.Space(4f); if (Header("TERRITORY FILTERS", Accordion.Filters, header)) { DrawFilters(label, gold, header); } GUILayout.Space(4f); if (Header("CLAN MAP ACCESS", Accordion.Access, header)) { DrawAccess(label, gold, panel); } GUILayout.Space(4f); if (Header("CLAN PIN", Accordion.Pin, header)) { ClanMapPinUi.DrawEditor(label, gold, header); } GUILayout.Space(4f); if (Header("CLAN DRAWING", Accordion.Drawing, header)) { ClanMapDrawingUi.DrawEditor(label, gold, header); } GUILayout.Space(4f); if (Header("SELECTED TERRITORY", Accordion.Selected, header)) { float num = Mathf.Max(100f, h - 220f); _sectionScroll = GUILayout.BeginScrollView(_sectionScroll, false, false, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Height(num) }); DrawSelected(label, gold); GUILayout.Space(16f); GUILayout.EndScrollView(); } GUILayout.FlexibleSpace(); GUILayout.Space(16f); GUILayout.EndVertical(); } private static void DrawLayersContent(GUIStyle label, GUIStyle button) { ClanMapPinUi.DrawLayerControls(_small, button, _state == MapState.Authorized); } private static void DrawFilters(GUIStyle label, GUIStyle gold, GUIStyle button) { GUILayout.Label("WHO", gold, Array.Empty()); FilterButton("All Claimed", _whoFilter == WhoFilter.AllClaimed, button, delegate { SetWho(WhoFilter.AllClaimed); }, enabled: true); FilterButton("Own Clan", _whoFilter == WhoFilter.OwnClan, button, delegate { SetWho(WhoFilter.OwnClan); }, enabled: true); FilterButton("Other Clans", _whoFilter == WhoFilter.OtherClans, button, delegate { SetWho(WhoFilter.OtherClans); }, enabled: true); GUILayout.Space(6f); GUILayout.Label("TYPE", gold, Array.Empty()); FilterButton("All Types", _typeFilter == TypeFilter.AllTypes, button, delegate { _typeFilter = TypeFilter.AllTypes; }, enabled: true); FilterButton("Capitals", _typeFilter == TypeFilter.Capitals, button, delegate { _typeFilter = TypeFilter.Capitals; }, enabled: true); FilterButton("Colonies", _typeFilter == TypeFilter.Colonies, button, delegate { _typeFilter = TypeFilter.Colonies; }, enabled: true); bool flag = _whoFilter != WhoFilter.OtherClans; FilterButton("Corrupted", _typeFilter == TypeFilter.Corrupted, button, delegate { _typeFilter = TypeFilter.Corrupted; }, flag); GUILayout.Space(5f); GUILayout.Label("WHO selects territory ownership.\nTYPE further limits the visible territory type.", _small, Array.Empty()); if (!flag) { GUILayout.Label("Foreign corruption is private.", _small, Array.Empty()); } GUILayout.Space(6f); GUILayout.Label("DISPLAY", gold, Array.Empty()); FilterButton("Show Territory Names", _showLabels, button, delegate { _showLabels = !_showLabels; }, enabled: true); ClanMapPinUi.DrawDisplayControls(gold, button); } private static void SetWho(WhoFilter who) { _whoFilter = who; if (who == WhoFilter.OtherClans && _typeFilter == TypeFilter.Corrupted) { _typeFilter = TypeFilter.AllTypes; } } private static void FilterButton(string text, bool active, GUIStyle style, Action action, bool enabled) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_0005: 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_000b: Unknown result type (might be due to invalid IL or missing references) //IL_00c5: Unknown result type (might be due to invalid IL or missing references) //IL_00cb: Unknown result type (might be due to invalid IL or missing references) //IL_004b: Unknown result type (might be due to invalid IL or missing references) //IL_0030: Unknown result type (might be due to invalid IL or missing references) //IL_0074: Unknown result type (might be due to invalid IL or missing references) //IL_006d: Unknown result type (might be due to invalid IL or missing references) Color backgroundColor = GUI.backgroundColor; Color contentColor = GUI.contentColor; bool enabled2 = GUI.enabled; try { GUI.enabled = enabled; GUI.backgroundColor = (active ? new Color(0.72f, 0.49f, 0.12f, 1f) : new Color(0.25f, 0.31f, 0.33f, 1f)); GUI.contentColor = (Color)(enabled ? Color.white : new Color(0.45f, 0.45f, 0.45f, 1f)); if (GUILayout.Button((active ? "[x] " : "[ ] ") + text, style, (GUILayoutOption[])(object)new GUILayoutOption[2] { GUILayout.ExpandWidth(true), GUILayout.Height(25f) })) { action(); } } finally { GUI.enabled = enabled2; GUI.backgroundColor = backgroundColor; GUI.contentColor = contentColor; } } private static void Disabled(string n, GUIStyle s) { bool enabled = GUI.enabled; GUI.enabled = false; GUILayout.Toggle(false, n, s, Array.Empty()); GUI.enabled = enabled; } private static void DrawAccess(GUIStyle label, GUIStyle gold, GUIStyle panel) { //IL_0031: Unknown result type (might be due to invalid IL or missing references) //IL_003c: Expected O, but got Unknown if (_state != MapState.Authorized || _snapshot == null) { GUILayout.Label(StateMessage(), label, Array.Empty()); return; } Texture2D texture = LocalEmblemCache.GetTexture(_snapshot.ClanLogoKey); if ((Object)texture != (Object)null) { GUILayout.Box((Texture)(object)texture, panel, (GUILayoutOption[])(object)new GUILayoutOption[2] { GUILayout.Width(64f), GUILayout.Height(64f) }); } GUILayout.Label(_snapshot.ClanName + " [" + _snapshot.ClanTag + "]", gold, Array.Empty()); Row("View", "Allowed", label); Row("Edit", _snapshot.CanEdit ? "Officer / Jarl" : "Read Only", label); GUILayout.Label("World UID: " + _snapshot.WorldUID, _small, Array.Empty()); GUILayout.Label("Own territories: " + _snapshot.Territories.Count, _small, Array.Empty()); GUILayout.Label("Visible claimed: " + _snapshot.PublicTerritories.Count, _small, Array.Empty()); } private static void DrawSelected(GUIStyle label, GUIStyle gold) { if (string.IsNullOrEmpty(_selectedId)) { GUILayout.Label(string.IsNullOrEmpty(_message) ? "Select a territory on the map." : _message, label, Array.Empty()); } else if (_privateDetail != null) { DrawPrivate(_privateDetail, label, gold); } else if (_publicDetail != null) { DrawPublic(_publicDetail, label, gold); } else { GUILayout.Label(_message, label, Array.Empty()); } } private static void DrawPrivate(ClanMapTerritoryDetailDto d, GUIStyle l, GUIStyle g) { GUILayout.Label(d.DisplayName, g, Array.Empty()); GUILayout.Label("IDENTITY", g, Array.Empty()); Row("Type", d.IsCapital ? "Capital" : "Colony", l); Row("Biome", d.BiomeName, l); Row("Level", d.SizeLevel.ToString(), l); GUILayout.Label("POSITION", g, Array.Empty()); GUILayout.Label("Grid " + d.GridX + "," + d.GridY + " | World " + d.WorldX.ToString("0") + "," + d.WorldZ.ToString("0"), l, Array.Empty()); GUILayout.Label("CONDITION", g, Array.Empty()); Row("HP", d.HP.ToString("0") + " / " + d.MaxHP.ToString("0"), l); string[] obj = new string[5] { "State: ", null, null, null, null }; TerritoryState state = (TerritoryState)d.State; obj[1] = state.ToString(); obj[2] = " | Rot: "; obj[3] = d.RotCount.ToString(); obj[4] = "/4"; GUILayout.Label(string.Concat(obj), l, Array.Empty()); GUILayout.Label("ECONOMY / TICK", g, Array.Empty()); Row("Base", d.BaseIncome.ToString(), l); Row("After rot", d.CorruptionAdjustedIncome.ToString(), l); Row("Upkeep", d.BaseUpkeep.ToString(), l); if (d.ProgressiveUpkeep != 0L) { Row("Progressive", d.ProgressiveUpkeep.ToString(), l); } if (d.GuardUpkeep != 0L) { Row("Guard", d.GuardUpkeep.ToString(), l); } Row("Net", d.NetResult.ToString(), l); GUILayout.Label("INFRASTRUCTURE", g, Array.Empty()); GUILayout.Label("Buildings " + d.BuildingCount + " | Active " + d.ActiveBuildingCount + " | Stored " + d.StoredProductionTicks + " | Ready " + d.ProductionReadyCount, l, Array.Empty()); } private static void DrawPublic(ClanMapPublicTerritoryDetailDto d, GUIStyle l, GUIStyle g) { GUILayout.Label(d.DisplayName, g, Array.Empty()); Row("Owner", d.OwnerClanName + " [" + d.OwnerClanTag + "]", l); Row("Type", d.IsCapital ? "Capital" : "Colony", l); Row("Biome", d.BiomeName, l); Row("World", d.WorldX.ToString("0") + ", " + d.WorldZ.ToString("0"), l); Row("Level", d.SizeLevel.ToString(), l); TerritoryState publicState = (TerritoryState)d.PublicState; Row("Public state", publicState.ToString(), l); GUILayout.Label("Private economy, HP and infrastructure are hidden.", _small, Array.Empty()); } private static void DrawMap(float w, float h, GUIStyle panel, GUIStyle label, GUIStyle button, GUIStyle smallButton) { //IL_018d: Unknown result type (might be due to invalid IL or missing references) GUILayout.BeginVertical(panel, (GUILayoutOption[])(object)new GUILayoutOption[2] { GUILayout.Width(w), GUILayout.Height(h) }); GUILayout.BeginHorizontal(Array.Empty()); if (GUILayout.Button("Zoom -", smallButton, (GUILayoutOption[])(object)new GUILayoutOption[2] { GUILayout.Width(72f), GUILayout.Height(26f) })) { SetZoom(_zoom - 0.25f); } if (GUILayout.Button("Zoom +", smallButton, (GUILayoutOption[])(object)new GUILayoutOption[2] { GUILayout.Width(72f), GUILayout.Height(26f) })) { SetZoom(_zoom + 0.25f); } if (GUILayout.Button("Reset View", button, (GUILayoutOption[])(object)new GUILayoutOption[2] { GUILayout.Width(104f), GUILayout.Height(26f) })) { ResetView(); } if (GUILayout.Button("Refresh", smallButton, (GUILayoutOption[])(object)new GUILayoutOption[2] { GUILayout.Width(78f), GUILayout.Height(26f) })) { ManualRefresh(); } ClanMapPinUi.DrawToolbar(smallButton); ClanMapDrawingUi.DrawToolbar(smallButton); GUILayout.FlexibleSpace(); GUILayout.Label("Zoom " + _zoom.ToString("0.00") + "x", _small, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(86f) }); GUILayout.EndHorizontal(); GUILayout.Space(5f); DrawCanvas(GUILayoutUtility.GetRect(w - 18f, h - 72f, (GUILayoutOption[])(object)new GUILayoutOption[2] { GUILayout.ExpandWidth(true), GUILayout.ExpandHeight(true) }), label); GUILayout.Space(12f); GUILayout.EndVertical(); } private static void DrawCanvas(Rect canvas, GUIStyle label) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_0005: 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_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_0011: Unknown result type (might be due to invalid IL or missing references) //IL_0012: Unknown result type (might be due to invalid IL or missing references) //IL_0017: Unknown result type (might be due to invalid IL or missing references) //IL_002f: Unknown result type (might be due to invalid IL or missing references) //IL_0039: Unknown result type (might be due to invalid IL or missing references) //IL_0058: Unknown result type (might be due to invalid IL or missing references) //IL_0062: Unknown result type (might be due to invalid IL or missing references) //IL_006d: Unknown result type (might be due to invalid IL or missing references) //IL_00de: Unknown result type (might be due to invalid IL or missing references) //IL_00e3: Unknown result type (might be due to invalid IL or missing references) //IL_00e8: Unknown result type (might be due to invalid IL or missing references) //IL_00ed: Unknown result type (might be due to invalid IL or missing references) //IL_00f1: Unknown result type (might be due to invalid IL or missing references) //IL_00fb: Unknown result type (might be due to invalid IL or missing references) //IL_010e: Unknown result type (might be due to invalid IL or missing references) //IL_011a: Unknown result type (might be due to invalid IL or missing references) //IL_0456: Unknown result type (might be due to invalid IL or missing references) //IL_045c: Unknown result type (might be due to invalid IL or missing references) //IL_0462: Unknown result type (might be due to invalid IL or missing references) //IL_0468: Unknown result type (might be due to invalid IL or missing references) //IL_0143: Unknown result type (might be due to invalid IL or missing references) //IL_0161: Unknown result type (might be due to invalid IL or missing references) //IL_0123: Unknown result type (might be due to invalid IL or missing references) //IL_0174: Unknown result type (might be due to invalid IL or missing references) //IL_020b: Unknown result type (might be due to invalid IL or missing references) //IL_01aa: Unknown result type (might be due to invalid IL or missing references) //IL_01c2: Unknown result type (might be due to invalid IL or missing references) //IL_01e7: Unknown result type (might be due to invalid IL or missing references) //IL_021e: Unknown result type (might be due to invalid IL or missing references) //IL_0254: Unknown result type (might be due to invalid IL or missing references) //IL_026c: Unknown result type (might be due to invalid IL or missing references) //IL_02c3: Unknown result type (might be due to invalid IL or missing references) //IL_02c5: Unknown result type (might be due to invalid IL or missing references) //IL_0291: Unknown result type (might be due to invalid IL or missing references) //IL_02d1: Unknown result type (might be due to invalid IL or missing references) //IL_02d3: Unknown result type (might be due to invalid IL or missing references) //IL_02e6: Unknown result type (might be due to invalid IL or missing references) //IL_02e8: Unknown result type (might be due to invalid IL or missing references) //IL_030f: Unknown result type (might be due to invalid IL or missing references) //IL_031b: Unknown result type (might be due to invalid IL or missing references) //IL_0330: 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_0373: Unknown result type (might be due to invalid IL or missing references) //IL_03da: Unknown result type (might be due to invalid IL or missing references) //IL_03ec: Unknown result type (might be due to invalid IL or missing references) //IL_0412: Unknown result type (might be due to invalid IL or missing references) //IL_042c: Unknown result type (might be due to invalid IL or missing references) //IL_042e: Unknown result type (might be due to invalid IL or missing references) //IL_041b: Unknown result type (might be due to invalid IL or missing references) //IL_03c4: Unknown result type (might be due to invalid IL or missing references) //IL_043e: Unknown result type (might be due to invalid IL or missing references) //IL_0442: Unknown result type (might be due to invalid IL or missing references) Color color = GUI.color; Color backgroundColor = GUI.backgroundColor; Color contentColor = GUI.contentColor; Matrix4x4 matrix = GUI.matrix; bool flag = false; try { GUI.color = new Color(0.035f, 0.045f, 0.055f, 1f); GUI.DrawTexture(canvas, (Texture)(object)_pixel); GUI.color = new Color(0.55f, 0.42f, 0.22f, 0.8f); Border(canvas, 2f); GUI.BeginGroup(canvas); flag = true; Rect val = default(Rect); ((Rect)(ref val))..ctor(0f, 0f, ((Rect)(ref canvas)).width, ((Rect)(ref canvas)).height); float num = Mathf.Min(((Rect)(ref val)).width, ((Rect)(ref val)).height) * 0.95f; float num2 = num * _zoom; float num3 = num2 * 0.5f; Vector2 val2 = new Vector2(((Rect)(ref val)).width * 0.5f, ((Rect)(ref val)).height * 0.5f) + _pan; Rect val3 = default(Rect); ((Rect)(ref val3))..ctor(val2.x - num3, val2.y - num3, num2, num2); GUI.color = Color.white; if (((Rect)(ref val3)).Overlaps(val)) { GUI.DrawTexture(val3, (Texture)(object)_fog); } GUI.color = new Color(0.63f, 0.56f, 0.39f, 0.13f); float num4 = Mathf.Max(24f, num2 / 10f); for (float num5 = val2.x % num4; num5 < ((Rect)(ref val)).width; num5 += num4) { float num6 = num5 - val2.x; if (!(Mathf.Abs(num6) > num3)) { float num7 = Mathf.Sqrt(Mathf.Max(0f, num3 * num3 - num6 * num6)); float num8 = Mathf.Max(0f, val2.y - num7); float num9 = Mathf.Min(((Rect)(ref val)).height, val2.y + num7); if (num9 > num8) { GUI.DrawTexture(new Rect(num5, num8, 1f, num9 - num8), (Texture)(object)_pixel); } } } for (float num10 = val2.y % num4; num10 < ((Rect)(ref val)).height; num10 += num4) { float num11 = num10 - val2.y; if (!(Mathf.Abs(num11) > num3)) { float num12 = Mathf.Sqrt(Mathf.Max(0f, num3 * num3 - num11 * num11)); float num13 = Mathf.Max(0f, val2.x - num12); float num14 = Mathf.Min(((Rect)(ref val)).width, val2.x + num12); if (num14 > num13) { GUI.DrawTexture(new Rect(num13, num10, num14 - num13, 1f), (Texture)(object)_pixel); } } } List hits = (_showTerritories ? DrawPolygons(val, val2, num, label) : new List()); ClanMapDrawingUi.Render(val, val2, num, _zoom, _center); ClanMapPinUi.Render(val, val2, num, _zoom, _center); GUI.color = new Color(0.65f, 0.47f, 0.18f, 0.72f); if (((Rect)(ref val3)).Overlaps(val)) { GUI.DrawTexture(val3, (Texture)(object)_worldRing); } GUI.color = Color.white; if (_state != MapState.Authorized || _snapshot == null) { GUI.Label(new Rect(24f, ((Rect)(ref val)).height / 2f - 24f, ((Rect)(ref val)).width - 48f, 48f), StateMessage(), _center); } else if (_snapshot.PublicTerritories.Count == 0) { GUI.Label(new Rect(24f, ((Rect)(ref val)).height / 2f - 24f, ((Rect)(ref val)).width - 48f, 48f), "No claimed territories are available.", _center); } Rect val4 = default(Rect); ((Rect)(ref val4))..ctor(val2.x - 15f, Mathf.Max(2f, val2.y - num3 + 8f), 30f, 24f); if (((Rect)(ref val4)).Overlaps(val)) { GUI.Label(val4, "N", _center); } if (!ClanMapDrawingUi.HandleInput(val, val2, num, _zoom)) { HandleInput(val, hits, val2, num); } } finally { if (flag) { GUI.EndGroup(); } GUI.matrix = matrix; GUI.color = color; GUI.backgroundColor = backgroundColor; GUI.contentColor = contentColor; } } private static List DrawPolygons(Rect local, Vector2 center, float baseD, GUIStyle label) { //IL_006a: Unknown result type (might be due to invalid IL or missing references) //IL_0073: Unknown result type (might be due to invalid IL or missing references) //IL_0078: Unknown result type (might be due to invalid IL or missing references) //IL_00f5: Unknown result type (might be due to invalid IL or missing references) //IL_00ff: Unknown result type (might be due to invalid IL or missing references) //IL_012c: Unknown result type (might be due to invalid IL or missing references) //IL_0136: 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_0147: Unknown result type (might be due to invalid IL or missing references) //IL_014c: Unknown result type (might be due to invalid IL or missing references) //IL_0150: Unknown result type (might be due to invalid IL or missing references) //IL_015d: Unknown result type (might be due to invalid IL or missing references) //IL_018a: Unknown result type (might be due to invalid IL or missing references) //IL_01d9: Unknown result type (might be due to invalid IL or missing references) //IL_01c1: 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_0192: Unknown result type (might be due to invalid IL or missing references) //IL_0194: Unknown result type (might be due to invalid IL or missing references) //IL_019e: Unknown result type (might be due to invalid IL or missing references) //IL_0205: Unknown result type (might be due to invalid IL or missing references) //IL_0207: Unknown result type (might be due to invalid IL or missing references) //IL_020c: Unknown result type (might be due to invalid IL or missing references) //IL_0210: Unknown result type (might be due to invalid IL or missing references) //IL_021d: Unknown result type (might be due to invalid IL or missing references) //IL_0248: Unknown result type (might be due to invalid IL or missing references) //IL_0259: Unknown result type (might be due to invalid IL or missing references) //IL_026c: Unknown result type (might be due to invalid IL or missing references) //IL_027d: Unknown result type (might be due to invalid IL or missing references) //IL_0290: Unknown result type (might be due to invalid IL or missing references) //IL_0292: 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) List list = new List(); if (_state != MapState.Authorized || _snapshot == null) { return list; } List list2 = new List(_snapshot.PublicTerritories); list2.Sort((ClanMapPublicTerritoryDto a, ClanMapPublicTerritoryDto b) => ((a.TerritoryID == _selectedId) ? 1 : 0).CompareTo((b.TerritoryID == _selectedId) ? 1 : 0)); Rect val3 = default(Rect); Rect val5 = default(Rect); foreach (ClanMapPublicTerritoryDto item in list2) { if (!Visible(item)) { continue; } List list3 = ScreenPoints(item, center, baseD); Rect val = Bounds(list3); if (((Rect)(ref val)).xMax < 0f || ((Rect)(ref val)).yMax < 0f || ((Rect)(ref val)).x > ((Rect)(ref local)).width || ((Rect)(ref val)).y > ((Rect)(ref local)).height) { continue; } PolyMask mask = GetMask(item); GUI.color = new Color(item.OwnerClanColorR, item.OwnerClanColorG, item.OwnerClanColorB, (item.OwnerClanID == _localClanId) ? 0.85f : 0.58f); GUI.DrawTexture(val, (Texture)(object)mask.Texture); if (item.IsCapital) { GUI.color = new Color(1f, 0.72f, 0.18f, 1f); GUI.DrawTexture(val, (Texture)(object)mask.Outline); Vector2 val2 = Center(item, center, baseD); ((Rect)(ref val3))..ctor(val2.x - 14f, val2.y - 14f, 28f, 28f); float num = baseD * 0.5f * _zoom; if (((Rect)(ref val3)).Overlaps(local) && Vector2.Distance(val2, center) <= num) { GUI.Label(val3, "★", _center); } } if (item.TerritoryID == _selectedId) { GUI.color = Color.white; GUI.DrawTexture(val, (Texture)(object)mask.Outline); } GUI.color = Color.white; if (_showLabels && (_zoom >= 2f || item.IsCapital)) { Vector2 val4 = Center(item, center, baseD); ((Rect)(ref val5))..ctor(val4.x - 80f, val4.y + 10f, 160f, 20f); float num2 = baseD * 0.5f * _zoom; if (val4.x >= 0f && val4.x <= ((Rect)(ref local)).width && val4.y >= 0f && val4.y <= ((Rect)(ref local)).height && Vector2.Distance(val4, center) <= num2 && ((Rect)(ref val5)).x >= 0f && ((Rect)(ref val5)).xMax <= ((Rect)(ref local)).width && ((Rect)(ref val5)).y >= 0f && ((Rect)(ref val5)).yMax <= ((Rect)(ref local)).height) { string text = item.DisplayName ?? ""; if (text.Length > 18) { text = text.Substring(0, 17) + "…"; } GUI.Label(val5, text, _center); } } list.Add(new Hit { Territory = item, Screen = list3, Area = Mathf.Abs(Area(list3)) }); } return list; } private static bool Visible(ClanMapPublicTerritoryDto t) { bool flag = t.OwnerClanID == _localClanId; if (_whoFilter == WhoFilter.OwnClan && !flag) { return false; } if (_whoFilter == WhoFilter.OtherClans && flag) { return false; } if (_typeFilter == TypeFilter.Capitals && !t.IsCapital) { return false; } if (_typeFilter == TypeFilter.Colonies && t.IsCapital) { return false; } if (_typeFilter == TypeFilter.Corrupted) { if (!flag) { return false; } ClanMapTerritoryMarkerDto clanMapTerritoryMarkerDto = _snapshot.Territories.Find((ClanMapTerritoryMarkerDto x) => x.TerritoryID == t.TerritoryID); if (clanMapTerritoryMarkerDto == null || (!clanMapTerritoryMarkerDto.IsRotted && clanMapTerritoryMarkerDto.RotCount <= 0)) { return false; } } return true; } private static Vector2 Center(ClanMapPublicTerritoryDto t, Vector2 center, float d) { //IL_0000: 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_0036: Unknown result type (might be due to invalid IL or missing references) return new Vector2(center.x + t.NormalizedCenterX * d * 0.5f * _zoom, center.y - t.NormalizedCenterY * d * 0.5f * _zoom); } private static List ScreenPoints(ClanMapPublicTerritoryDto t, Vector2 center, float d) { //IL_0028: Unknown result type (might be due to invalid IL or missing references) //IL_0043: Unknown result type (might be due to invalid IL or missing references) //IL_005e: Unknown result type (might be due to invalid IL or missing references) List list = new List(t.GeometryPoints.Count); foreach (ClanMapGeometryPoint geometryPoint in t.GeometryPoints) { list.Add(new Vector2(center.x + geometryPoint.NormalizedX * d * 0.5f * _zoom, center.y - geometryPoint.NormalizedY * d * 0.5f * _zoom)); } return list; } private static Rect Bounds(List p) { //IL_0024: Unknown result type (might be due to invalid IL or missing references) //IL_0029: 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_003a: Unknown result type (might be due to invalid IL or missing references) //IL_0048: Unknown result type (might be due to invalid IL or missing references) //IL_0056: 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) float num = float.MaxValue; float num2 = float.MinValue; float num3 = float.MaxValue; float num4 = float.MinValue; foreach (Vector2 item in p) { num = Mathf.Min(num, item.x); num2 = Mathf.Max(num2, item.x); num3 = Mathf.Min(num3, item.y); num4 = Mathf.Max(num4, item.y); } return new Rect(num, num3, Mathf.Max(1f, num2 - num), Mathf.Max(1f, num4 - num3)); } private static PolyMask GetMask(ClanMapPublicTerritoryDto t) { //IL_005f: 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_0081: Unknown result type (might be due to invalid IL or missing references) //IL_0086: Unknown result type (might be due to invalid IL or missing references) //IL_017d: Unknown result type (might be due to invalid IL or missing references) //IL_01bc: Unknown result type (might be due to invalid IL or missing references) //IL_0213: 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_01e6: Unknown result type (might be due to invalid IL or missing references) //IL_01eb: Unknown result type (might be due to invalid IL or missing references) //IL_02a8: Unknown result type (might be due to invalid IL or missing references) //IL_02ad: Unknown result type (might be due to invalid IL or missing references) //IL_02cf: Unknown result type (might be due to invalid IL or missing references) //IL_02d4: Unknown result type (might be due to invalid IL or missing references) string key = t.TerritoryID + ":" + t.GeometryRevision; if (Masks.TryGetValue(key, out var value)) { return value; } int num = 96; Color[] array = (Color[])(object)new Color[num * num]; Color[] array2 = (Color[])(object)new Color[num * num]; for (int i = 0; i < array.Length; i++) { array[i] = new Color(0f, 0f, 0f, 0f); array2[i] = new Color(0f, 0f, 0f, 0f); } List list = new List(); float num2 = 1f; float num3 = -1f; float num4 = 1f; float num5 = -1f; foreach (ClanMapGeometryPoint geometryPoint in t.GeometryPoints) { float normalizedX = geometryPoint.NormalizedX; float normalizedY = geometryPoint.NormalizedY; num2 = Mathf.Min(num2, normalizedX); num3 = Mathf.Max(num3, normalizedX); num4 = Mathf.Min(num4, normalizedY); num5 = Mathf.Max(num5, normalizedY); } foreach (ClanMapGeometryPoint geometryPoint2 in t.GeometryPoints) { list.Add(new Vector2((geometryPoint2.NormalizedX - num2) / Mathf.Max(0.0001f, num3 - num2) * (float)(num - 1), (geometryPoint2.NormalizedY - num4) / Mathf.Max(0.0001f, num5 - num4) * (float)(num - 1))); } for (int j = 0; j < num; j++) { for (int k = 0; k < num; k++) { if (PointIn(new Vector2((float)k + 0.5f, (float)j + 0.5f), list)) { array[j * num + k] = new Color(1f, 1f, 1f, 0.62f); } } } for (int l = 0; l < list.Count; l++) { RasterLine(array, array2, num, list[l], list[(l + 1) % list.Count]); } for (int m = 0; m < num; m++) { for (int n = 0; n < num; n++) { float num6 = Mathf.Lerp(num2, num3, ((float)n + 0.5f) / (float)num); float num7 = Mathf.Lerp(num4, num5, ((float)m + 0.5f) / (float)num); if (num6 * num6 + num7 * num7 > 1f) { array[m * num + n] = new Color(0f, 0f, 0f, 0f); array2[m * num + n] = new Color(0f, 0f, 0f, 0f); } } } Texture2D texture = MakeMaskTexture(num, array); Texture2D outline = MakeMaskTexture(num, array2); value = new PolyMask { Texture = texture, Outline = outline, MinX = num2, MaxX = num3, MinY = num4, MaxY = num5, Revision = t.GeometryRevision }; Masks[key] = value; return value; } private static Texture2D MakeMaskTexture(int size, Color[] pixels) { //IL_0004: Unknown result type (might be due to invalid IL or missing references) //IL_0009: Unknown result type (might be due to invalid IL or missing references) //IL_0010: Unknown result type (might be due to invalid IL or missing references) //IL_0017: Unknown result type (might be due to invalid IL or missing references) //IL_001e: Unknown result type (might be due to invalid IL or missing references) //IL_0027: Expected O, but got Unknown Texture2D val = new Texture2D(size, size, (TextureFormat)4, false) { wrapMode = (TextureWrapMode)1, filterMode = (FilterMode)1 }; val.SetPixels(pixels); val.Apply(false, true); return val; } private static void RasterLine(Color[] fill, Color[] outline, int size, Vector2 a, Vector2 b) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) //IL_0002: 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_001e: Unknown result type (might be due to invalid IL or missing references) //IL_003e: Unknown result type (might be due to invalid IL or missing references) //IL_0044: Unknown result type (might be due to invalid IL or missing references) //IL_006a: Unknown result type (might be due to invalid IL or missing references) //IL_006f: Unknown result type (might be due to invalid IL or missing references) //IL_007a: Unknown result type (might be due to invalid IL or missing references) //IL_007f: Unknown result type (might be due to invalid IL or missing references) int num = Mathf.Max(1, Mathf.CeilToInt(Vector2.Distance(a, b))); for (int i = 0; i <= num; i++) { int num2 = Mathf.Clamp(Mathf.RoundToInt(Mathf.Lerp(a.x, b.x, (float)i / (float)num)), 0, size - 1); int num3 = Mathf.Clamp(Mathf.RoundToInt(Mathf.Lerp(a.y, b.y, (float)i / (float)num)), 0, size - 1); fill[num3 * size + num2] = Color.white; outline[num3 * size + num2] = Color.white; } } private static void HandleInput(Rect canvas, List hits, Vector2 center, float baseD) { //IL_0014: Unknown result type (might be due to invalid IL or missing references) //IL_001a: Invalid comparison between Unknown and I4 //IL_004f: Unknown result type (might be due to invalid IL or missing references) //IL_001f: Unknown result type (might be due to invalid IL or missing references) //IL_0097: Unknown result type (might be due to invalid IL or missing references) //IL_009d: Invalid comparison between Unknown and I4 //IL_0031: Unknown result type (might be due to invalid IL or missing references) //IL_0107: Unknown result type (might be due to invalid IL or missing references) //IL_010d: Invalid comparison between Unknown and I4 //IL_0062: Unknown result type (might be due to invalid IL or missing references) //IL_007b: Unknown result type (might be due to invalid IL or missing references) //IL_0080: Unknown result type (might be due to invalid IL or missing references) //IL_0085: Unknown result type (might be due to invalid IL or missing references) //IL_008a: Unknown result type (might be due to invalid IL or missing references) //IL_00b7: 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) //IL_00da: 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) //IL_00e5: Unknown result type (might be due to invalid IL or missing references) //IL_00ea: Unknown result type (might be due to invalid IL or missing references) //IL_00ef: Unknown result type (might be due to invalid IL or missing references) //IL_00f4: Unknown result type (might be due to invalid IL or missing references) //IL_00f9: Unknown result type (might be due to invalid IL or missing references) //IL_013f: 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_016a: Unknown result type (might be due to invalid IL or missing references) //IL_016f: Unknown result type (might be due to invalid IL or missing references) //IL_019a: Unknown result type (might be due to invalid IL or missing references) Event current = Event.current; if (current == null) { return; } int num = (ClanMapDrawingUi.DrawingMode ? 1 : 0); if ((int)current.type == 6 && ((Rect)(ref canvas)).Contains(current.mousePosition)) { SetZoom(_zoom - current.delta.y * 0.12f); current.Use(); } else if ((int)current.type == 0 && current.button == num && ((Rect)(ref canvas)).Contains(current.mousePosition)) { _pressed = true; _dragging = false; _press = current.mousePosition; _panStart = _pan; current.Use(); } else if ((int)current.type == 3 && _pressed && current.button == num) { if (!_dragging && Vector2.Distance(current.mousePosition, _press) >= 5f) { _dragging = true; } if (_dragging) { _pan = _panStart + current.mousePosition - _press; ClampPan(canvas); } current.Use(); } else { if ((int)current.type != 1 || !_pressed || current.button != num) { return; } bool dragging = _dragging; _pressed = (_dragging = false); if (!dragging && num == 0) { if (ClanMapPinUi.TryHandleClick(current.mousePosition, center, baseD, _zoom)) { ClearSelection(); ClanMapDrawingUi.Cancel(); _accordion = Accordion.Pin; } else if (ClanMapDrawingUi.TrySelect(current.mousePosition, center, baseD, _zoom)) { ClearSelection(); ClanMapPinUi.CancelSelection(); _accordion = Accordion.Drawing; } else { ClanMapDrawingUi.Cancel(); SelectAt(current.mousePosition, hits); } } current.Use(); } } private static void SelectAt(Vector2 mouse, List hits) { //IL_0013: Unknown result type (might be due to invalid IL or missing references) Hit hit = null; foreach (Hit hit2 in hits) { if (PointIn(mouse, hit2.Screen) && (hit == null || hit2.Area < hit.Area || (Mathf.Approximately(hit2.Area, hit.Area) && string.CompareOrdinal(hit2.Territory.TerritoryID, hit.Territory.TerritoryID) < 0))) { hit = hit2; } } if (hit == null) { ClearSelection(); ClanMapPinUi.CancelSelection(); return; } ClanMapPinUi.CancelSelection(); ClanMapPublicTerritoryDto territory = hit.Territory; if (!(_selectedId == territory.TerritoryID) || (_privateDetail == null && _publicDetail == null && !(_pendingId == territory.TerritoryID))) { _selectedId = territory.TerritoryID; _pendingId = territory.TerritoryID; _privateDetail = null; _publicDetail = null; _accordion = Accordion.Selected; _message = "Waiting for authoritative territory details..."; if (!((territory.OwnerClanID == _localClanId) ? ClanMapRPC.RequestTerritoryDetail(territory.TerritoryID) : ClanMapRPC.RequestPublicTerritoryDetail(territory.TerritoryID))) { _pendingId = ""; _message = "Territory detail request unavailable."; } } } private static bool PointIn(Vector2 p, List polygon) { //IL_0011: Unknown result type (might be due to invalid IL or missing references) //IL_0016: Unknown result type (might be due to invalid IL or missing references) //IL_0019: Unknown result type (might be due to invalid IL or missing references) //IL_001e: 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_0026: Unknown result type (might be due to invalid IL or missing references) //IL_002e: Unknown result type (might be due to invalid IL or missing references) //IL_0035: Unknown result type (might be due to invalid IL or missing references) //IL_003f: Unknown result type (might be due to invalid IL or missing references) //IL_0045: Unknown result type (might be due to invalid IL or missing references) //IL_004c: Unknown result type (might be due to invalid IL or missing references) //IL_0053: Unknown result type (might be due to invalid IL or missing references) //IL_0059: Unknown result type (might be due to invalid IL or missing references) //IL_0061: Unknown result type (might be due to invalid IL or missing references) //IL_0068: Unknown result type (might be due to invalid IL or missing references) //IL_0070: Unknown result type (might be due to invalid IL or missing references) bool flag = false; int num = 0; int index = polygon.Count - 1; while (num < polygon.Count) { Vector2 val = polygon[num]; Vector2 val2 = polygon[index]; if (val.y > p.y != val2.y > p.y && p.x < (val2.x - val.x) * (p.y - val.y) / (val2.y - val.y) + val.x) { flag = !flag; } index = num++; } return flag; } private static float Area(List p) { //IL_000c: Unknown result type (might be due to invalid IL or missing references) //IL_0011: 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_0022: Unknown result type (might be due to invalid IL or missing references) //IL_0024: Unknown result type (might be due to invalid IL or missing references) //IL_002a: Unknown result type (might be due to invalid IL or missing references) //IL_0031: Unknown result type (might be due to invalid IL or missing references) //IL_0037: Unknown result type (might be due to invalid IL or missing references) float num = 0f; for (int i = 0; i < p.Count; i++) { Vector2 val = p[i]; Vector2 val2 = p[(i + 1) % p.Count]; num += val.x * val2.y - val2.x * val.y; } return num * 0.5f; } private static void Row(string k, string v, GUIStyle s) { GUILayout.BeginHorizontal(Array.Empty()); GUILayout.Label(k, s, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(125f) }); GUILayout.Label(v ?? "", s, Array.Empty()); GUILayout.EndHorizontal(); } private static string StateMessage() { return _state switch { MapState.Waiting => "Waiting for server authorization...", MapState.Denied => "Clan Map access denied.", MapState.TimedOut => "Clan Map authorization timed out.", MapState.Disconnected => "Clan Map unavailable while disconnected.", MapState.ProtocolMismatch => "Clan Map protocol mismatch.", MapState.Invalid => "Clan Map snapshot invalid. Refresh to try again.", _ => "", }; } private static void ClampPan(Rect c) { _pan.x = Mathf.Clamp(_pan.x, (0f - ((Rect)(ref c)).width) * 0.42f * _zoom, ((Rect)(ref c)).width * 0.42f * _zoom); _pan.y = Mathf.Clamp(_pan.y, (0f - ((Rect)(ref c)).height) * 0.42f * _zoom, ((Rect)(ref c)).height * 0.42f * _zoom); } private static void SetZoom(float z) { _zoom = Mathf.Clamp(z, 1f, 6f); } private static void ResetView() { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_0005: Unknown result type (might be due to invalid IL or missing references) _pan = Vector2.zero; _zoom = 1f; _pressed = (_dragging = false); } private static void RequestSnapshot() { if (_localClanId != 0 && _state != MapState.Waiting && !(Time.unscaledTime - _lastRequest < 2f)) { _lastRequest = Time.unscaledTime; _requestStarted = Time.unscaledTime; ClearData(preserveSelection: true); _state = MapState.Waiting; if (!ClanMapRPC.RequestOpen()) { _state = MapState.Disconnected; } } } private static void ManualRefresh() { ClanMapPinUi.Refresh(); ClanMapDrawingUi.Refresh(); _retries = 0; _lastRequest = -999f; if (_state == MapState.Waiting) { _state = MapState.Idle; } RequestSnapshot(); } private static void CheckTimeout() { if (!(Time.unscaledTime - _requestStarted < 8f)) { if (_retries < 1) { _retries++; _state = MapState.Idle; _lastRequest = -999f; RequestSnapshot(); } else { _state = MapState.TimedOut; _requestStarted = -999f; } } } private static void Centered(string m, float h, GUIStyle s) { GUILayout.Space(Mathf.Max(20f, h * 0.25f)); GUILayout.BeginHorizontal(Array.Empty()); GUILayout.FlexibleSpace(); GUILayout.Label(m, s, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Height(48f) }); GUILayout.FlexibleSpace(); GUILayout.EndHorizontal(); } private static void Border(Rect r, float t) { //IL_0016: Unknown result type (might be due to invalid IL or missing references) //IL_003d: Unknown result type (might be due to invalid IL or missing references) //IL_0062: Unknown result type (might be due to invalid IL or missing references) //IL_0089: Unknown result type (might be due to invalid IL or missing references) GUI.DrawTexture(new Rect(((Rect)(ref r)).x, ((Rect)(ref r)).y, ((Rect)(ref r)).width, t), (Texture)(object)_pixel); GUI.DrawTexture(new Rect(((Rect)(ref r)).x, ((Rect)(ref r)).yMax - t, ((Rect)(ref r)).width, t), (Texture)(object)_pixel); GUI.DrawTexture(new Rect(((Rect)(ref r)).x, ((Rect)(ref r)).y, t, ((Rect)(ref r)).height), (Texture)(object)_pixel); GUI.DrawTexture(new Rect(((Rect)(ref r)).xMax - t, ((Rect)(ref r)).y, t, ((Rect)(ref r)).height), (Texture)(object)_pixel); } private static void Ensure(GUIStyle label) { //IL_0005: Unknown result type (might be due to invalid IL or missing references) //IL_0010: Expected O, but got Unknown //IL_0056: Unknown result type (might be due to invalid IL or missing references) //IL_0061: Expected O, but got Unknown //IL_0016: Unknown result type (might be due to invalid IL or missing references) //IL_0020: Expected O, but got Unknown //IL_003d: Unknown result type (might be due to invalid IL or missing references) //IL_0174: Unknown result type (might be due to invalid IL or missing references) //IL_017f: Expected O, but got Unknown //IL_0070: Unknown result type (might be due to invalid IL or missing references) //IL_007a: Expected O, but got Unknown //IL_0191: Unknown result type (might be due to invalid IL or missing references) //IL_019b: Expected O, but got Unknown //IL_0293: Unknown result type (might be due to invalid IL or missing references) //IL_0298: Unknown result type (might be due to invalid IL or missing references) //IL_029f: Unknown result type (might be due to invalid IL or missing references) //IL_02a6: Unknown result type (might be due to invalid IL or missing references) //IL_02b3: Expected O, but got Unknown //IL_02b4: Unknown result type (might be due to invalid IL or missing references) //IL_02b9: Unknown result type (might be due to invalid IL or missing references) //IL_02c1: Unknown result type (might be due to invalid IL or missing references) //IL_02cd: Expected O, but got Unknown //IL_02eb: Unknown result type (might be due to invalid IL or missing references) //IL_0138: Unknown result type (might be due to invalid IL or missing references) //IL_013d: Unknown result type (might be due to invalid IL or missing references) //IL_024b: Unknown result type (might be due to invalid IL or missing references) //IL_0250: Unknown result type (might be due to invalid IL or missing references) if ((Object)_pixel == (Object)null) { _pixel = new Texture2D(1, 1, (TextureFormat)4, false); ((Texture)_pixel).wrapMode = (TextureWrapMode)1; ((Texture)_pixel).filterMode = (FilterMode)0; _pixel.SetPixel(0, 0, Color.white); _pixel.Apply(); } if ((Object)_fog == (Object)null) { int num = 128; _fog = new Texture2D(num, num, (TextureFormat)4, false); ((Texture)_fog).wrapMode = (TextureWrapMode)1; ((Texture)_fog).filterMode = (FilterMode)1; Color[] array = (Color[])(object)new Color[num * num]; for (int i = 0; i < num; i++) { for (int j = 0; j < num; j++) { float num2 = ((float)j + 0.5f) / (float)num * 2f - 1f; float num3 = ((float)i + 0.5f) / (float)num * 2f - 1f; float num4 = Mathf.Sqrt(num2 * num2 + num3 * num3); float num5 = Mathf.Clamp01((1.01f - num4) * 28f); float num6 = 0.075f + Mathf.PerlinNoise((float)j * 0.085f, (float)i * 0.085f) * 0.055f; array[i * num + j] = new Color(num6 * 0.72f, num6 * 0.83f, num6, num5); } } _fog.SetPixels(array); _fog.Apply(false, true); } if ((Object)_worldRing == (Object)null) { int num7 = 128; _worldRing = new Texture2D(num7, num7, (TextureFormat)4, false); ((Texture)_worldRing).wrapMode = (TextureWrapMode)1; ((Texture)_worldRing).filterMode = (FilterMode)1; Color[] array2 = (Color[])(object)new Color[num7 * num7]; for (int k = 0; k < num7; k++) { for (int l = 0; l < num7; l++) { float num8 = ((float)l + 0.5f) / (float)num7 * 2f - 1f; float num9 = ((float)k + 0.5f) / (float)num7 * 2f - 1f; float num10 = Mathf.Sqrt(num8 * num8 + num9 * num9); float num11 = Mathf.Clamp01(1f - Mathf.Abs(num10 - 0.985f) * 90f); array2[k * num7 + l] = new Color(1f, 1f, 1f, num11); } } _worldRing.SetPixels(array2); _worldRing.Apply(false, true); } if (_center == null) { _center = new GUIStyle(label) { alignment = (TextAnchor)4, wordWrap = true, fontSize = 13 }; _small = new GUIStyle(label) { fontSize = 10, wordWrap = true }; _small.normal.textColor = new Color(0.68f, 0.68f, 0.65f, 1f); } } } public static class LocalEmblemCache { private static readonly Dictionary Textures = new Dictionary(StringComparer.OrdinalIgnoreCase); private static readonly List Names = new List(); private static readonly Dictionary> Categories = new Dictionary>(StringComparer.OrdinalIgnoreCase); private static bool _loaded; private static Texture2D _placeholder; public static string RuntimeRoot => Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location) ?? Paths.PluginPath; public static string FolderPath => Path.Combine(RuntimeRoot, ClanConfig.EmblemFolder?.Value ?? "Emblems"); private static Texture2D Placeholder { get { //IL_0005: Unknown result type (might be due to invalid IL or missing references) //IL_0010: Expected O, but got Unknown //IL_0017: Unknown result type (might be due to invalid IL or missing references) //IL_001c: Unknown result type (might be due to invalid IL or missing references) //IL_0029: Expected O, but got Unknown //IL_0044: 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_008e: Unknown result type (might be due to invalid IL or missing references) //IL_00b3: Unknown result type (might be due to invalid IL or missing references) if ((Object)_placeholder == (Object)null) { _placeholder = new Texture2D(2, 2) { hideFlags = (HideFlags)61 }; _placeholder.SetPixel(0, 0, new Color(0.12f, 0.1f, 0.06f, 1f)); _placeholder.SetPixel(1, 0, new Color(0.55f, 0.42f, 0.16f, 1f)); _placeholder.SetPixel(0, 1, new Color(0.55f, 0.42f, 0.16f, 1f)); _placeholder.SetPixel(1, 1, new Color(0.12f, 0.1f, 0.06f, 1f)); _placeholder.Apply(); } return _placeholder; } } public static IReadOnlyList GetNames() { EnsureLoaded(); return Names; } public static IReadOnlyList GetCategories() { EnsureLoaded(); List list = new List(Categories.Keys); list.Sort(StringComparer.OrdinalIgnoreCase); return list; } public static IReadOnlyList GetNames(string category) { EnsureLoaded(); if (!Categories.TryGetValue(category ?? "Root", out var value)) { return Names; } return value; } public static Texture2D GetTexture(string name) { //IL_0025: Unknown result type (might be due to invalid IL or missing references) //IL_0030: Expected O, but got Unknown //IL_006e: Unknown result type (might be due to invalid IL or missing references) //IL_0079: Expected O, but got Unknown EnsureLoaded(); name = NormalizeName(name); if (!string.IsNullOrEmpty(name) && Textures.TryGetValue(name, out var value) && (Object)value != (Object)null) { return value; } string text = NormalizeName(ClanConfig.DefaultEmblem?.Value ?? "emblem_001"); if (!string.Equals(name, text, StringComparison.OrdinalIgnoreCase) && Textures.TryGetValue(text, out value) && (Object)value != (Object)null) { return value; } return Placeholder; } public static string NormalizeName(string name) { name = (name ?? "").Trim(); if (name.EndsWith(".png", StringComparison.OrdinalIgnoreCase)) { name = name.Substring(0, name.Length - 4); } return name; } private static void EnsureLoaded() { //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) //IL_0128: Expected O, but got Unknown if (_loaded) { return; } _loaded = true; Textures.Clear(); Names.Clear(); Categories.Clear(); string folderPath = FolderPath; Plugin.Log.LogInfo((object)("[FCW Emblems] Emblem folder path: " + folderPath)); try { if (!Directory.Exists(folderPath)) { Directory.CreateDirectory(folderPath); } string[] files = Directory.GetFiles(folderPath, "*.png", SearchOption.AllDirectories); Array.Sort(files, (IComparer?)StringComparer.OrdinalIgnoreCase); Plugin.Log.LogInfo((object)("[FCW Emblems] Found PNG count: " + files.Length)); string[] array = files; foreach (string text in array) { string fileName = Path.GetFileName(text); string text2 = NormalizeName(Path.ChangeExtension(text.Substring(folderPath.Length).TrimStart(Path.DirectorySeparatorChar, Path.AltDirectorySeparatorChar), null).Replace(Path.DirectorySeparatorChar, '/').Replace(Path.AltDirectorySeparatorChar, '/')); string key = "Root"; int num = text2.IndexOf('/'); if (num > 0) { key = text2.Substring(0, num); } try { Texture2D val = new Texture2D(2, 2, (TextureFormat)4, false) { hideFlags = (HideFlags)61 }; if (!ImageConversion.LoadImage(val, File.ReadAllBytes(text))) { throw new InvalidDataException("Texture2D.LoadImage returned false."); } Textures[text2] = val; Names.Add(text2); if (!Categories.ContainsKey(key)) { Categories[key] = new List(); } Categories[key].Add(text2); Plugin.Log.LogInfo((object)("[FCW Emblems] Loaded emblem: " + fileName)); } catch (Exception ex) { Plugin.Log.LogWarning((object)("[FCW Emblems] Failed to load emblem: " + fileName + " " + ex)); } } } catch (Exception ex2) { Plugin.Log.LogWarning((object)("[FCW Emblems] Failed to load emblem folder: " + folderPath + " " + ex2)); } if (Names.Count == 0) { string item = NormalizeName(ClanConfig.DefaultEmblem?.Value ?? "emblem_001"); Names.Add(item); Categories["Root"] = new List { item }; } foreach (List value in Categories.Values) { value.Sort(StringComparer.OrdinalIgnoreCase); } } } public class MapGridOverlay : MonoBehaviour { private Vector2i? _selectedCell; private bool _showClaimPanel; private Texture2D _pixelTex; private GUIStyle _panelStyle; private GUIStyle _headerStyle; private GUIStyle _labelStyle; private GUIStyle _buttonStyle; private GUIStyle _closeButtonStyle; private GUIStyle _tooltipTitleStyle; private GUIStyle _tooltipDetailStyle; private bool _stylesInit; private Vector2 _mouseDownPos; private bool _isDragging; private int _closeCooldown; private Vector2i? _cachedHighlightRegion; private Texture2D _highlightTex; private Rect _highlightWorldBounds; private float _lastZoom = -1f; private Vector2i? _stableHoveredCell; private int _hoverStableFrames; private int _frameSkip; private Vector2i? _lastLookupResult; private Dictionary> _claimedBorderCache = new Dictionary>(); private Dictionary> _claimedScreenCache = new Dictionary>(); private float _claimedPanX = float.MinValue; private float _claimedPanY = float.MinValue; private float _claimedZoom = float.MinValue; private int _claimedTerritoryCount = -1; private float _nextClaimedRebuild; private GUIStyle _territoryLabelStyle; private GUIStyle _eventMarkerStyle; private GUIStyle _eventMarkerLabelStyle; private Dictionary _eventTextures; private List _cachedScreenPoints = new List(); private float _lastMapPanX = float.MinValue; private float _lastMapPanY = float.MinValue; private float _lastMapZoom2 = float.MinValue; private List _cachedBorderWorldPoints = new List(); public static bool TerritoryOverlayEnabled = true; public static MapGridOverlay Instance { get; private set; } private float WorldRadius => 10500f; private static string RuntimeAssetRoot => Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location) ?? Paths.PluginPath; private void Awake() { //IL_0005: Unknown result type (might be due to invalid IL or missing references) //IL_0010: Expected O, but got Unknown //IL_0017: 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_0027: Expected O, but got Unknown //IL_0027: Expected O, but got Unknown //IL_002a: Unknown result type (might be due to invalid IL or missing references) //IL_0034: Expected O, but got Unknown if ((Object)Instance != (Object)null && (Object)Instance != (Object)this) { Object.Destroy((Object)this); return; } Instance = this; Plugin.Log.LogInfo((object)"Map overlay initialized (Voronoi mode)"); } private void Update() { //IL_0005: Unknown result type (might be due to invalid IL or missing references) //IL_0010: Expected O, but got Unknown //IL_0017: Unknown result type (might be due to invalid IL or missing references) //IL_001d: Invalid comparison between Unknown and I4 //IL_005a: 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_006e: Unknown result type (might be due to invalid IL or missing references) //IL_0073: Unknown result type (might be due to invalid IL or missing references) //IL_0088: Unknown result type (might be due to invalid IL or missing references) //IL_008d: Unknown result type (might be due to invalid IL or missing references) //IL_0097: 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) if ((Object)Minimap.instance == (Object)null || (int)Minimap.instance.m_mode != 2) { _showClaimPanel = false; _selectedCell = null; _isDragging = false; return; } if (_closeCooldown > 0) { _closeCooldown--; } if (Input.GetMouseButtonDown(0)) { _mouseDownPos = new Vector2(Input.mousePosition.x, Input.mousePosition.y); _isDragging = false; } if (Input.GetMouseButton(0) && Vector2.Distance(_mouseDownPos, new Vector2(Input.mousePosition.x, Input.mousePosition.y)) > 10f) { _isDragging = true; } if (!_isDragging && Input.GetMouseButtonUp(0) && _closeCooldown == 0) { Vector2i? cellUnderMouse = GetCellUnderMouse(); if (cellUnderMouse.HasValue) { _selectedCell = cellUnderMouse; _showClaimPanel = true; } } } private void ClosePanel() { _showClaimPanel = false; _selectedCell = null; _closeCooldown = 10; } private Vector2 WorldToMapPoint(Vector3 worldPos) { //IL_0017: Unknown result type (might be due to invalid IL or missing references) //IL_0029: Unknown result type (might be due to invalid IL or missing references) //IL_003d: Unknown result type (might be due to invalid IL or missing references) float num = Minimap.instance.m_textureSize; float pixelSize = Minimap.instance.m_pixelSize; float num2 = (worldPos.x / pixelSize + num / 2f) / num; float num3 = (worldPos.z / pixelSize + num / 2f) / num; return new Vector2(num2, num3); } private Vector3 MapPointToWorld(Vector2 mapPoint) { //IL_0017: Unknown result type (might be due to invalid IL or missing references) //IL_0029: Unknown result type (might be due to invalid IL or missing references) //IL_0042: Unknown result type (might be due to invalid IL or missing references) float num = Minimap.instance.m_textureSize; float pixelSize = Minimap.instance.m_pixelSize; float num2 = (mapPoint.x * num - num / 2f) * pixelSize; float num3 = (mapPoint.y * num - num / 2f) * pixelSize; return new Vector3(num2, 0f, num3); } private Vector2? WorldToScreenGUI(float worldX, float worldZ) { //IL_0005: Unknown result type (might be due to invalid IL or missing references) //IL_0010: Expected O, but got Unknown //IL_0029: Unknown result type (might be due to invalid IL or missing references) //IL_0034: Expected O, but got Unknown //IL_0049: Unknown result type (might be due to invalid IL or missing references) //IL_004e: Unknown result type (might be due to invalid IL or missing references) //IL_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) //IL_0061: Unknown result type (might be due to invalid IL or missing references) //IL_011d: Unknown result type (might be due to invalid IL or missing references) if ((Object)Minimap.instance == (Object)null) { return null; } RawImage mapImageLarge = Minimap.instance.m_mapImageLarge; if ((Object)mapImageLarge == (Object)null) { return null; } RectTransform component = ((Component)mapImageLarge).GetComponent(); Rect uvRect = mapImageLarge.uvRect; Vector2 val = WorldToMapPoint(new Vector3(worldX, 0f, worldZ)); float num = (val.x - ((Rect)(ref uvRect)).x) / ((Rect)(ref uvRect)).width; float num2 = (val.y - ((Rect)(ref uvRect)).y) / ((Rect)(ref uvRect)).height; if (num < -0.1f || num > 1.1f || num2 < -0.1f || num2 > 1.1f) { return null; } Vector3[] array = (Vector3[])(object)new Vector3[4]; component.GetWorldCorners(array); float num3 = Mathf.Lerp(array[0].x, array[2].x, num); float num4 = Mathf.Lerp(array[0].y, array[2].y, num2); return new Vector2(num3, (float)Screen.height - num4); } private Vector3? ScreenGUIToWorld(Vector2 guiPos) { //IL_0005: Unknown result type (might be due to invalid IL or missing references) //IL_0010: Expected O, but got Unknown //IL_0029: Unknown result type (might be due to invalid IL or missing references) //IL_0034: Expected O, but got Unknown //IL_0049: Unknown result type (might be due to invalid IL or missing references) //IL_004e: Unknown result type (might be due to invalid IL or missing references) //IL_004f: 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) //IL_014f: Unknown result type (might be due to invalid IL or missing references) //IL_0154: Unknown result type (might be due to invalid IL or missing references) if ((Object)Minimap.instance == (Object)null) { return null; } RawImage mapImageLarge = Minimap.instance.m_mapImageLarge; if ((Object)mapImageLarge == (Object)null) { return null; } RectTransform component = ((Component)mapImageLarge).GetComponent(); Rect uvRect = mapImageLarge.uvRect; float x = guiPos.x; float num = (float)Screen.height - guiPos.y; Vector3[] array = (Vector3[])(object)new Vector3[4]; component.GetWorldCorners(array); if (x < array[0].x || x > array[2].x || num < array[0].y || num > array[2].y) { return null; } float num2 = (x - array[0].x) / (array[2].x - array[0].x); float num3 = (num - array[0].y) / (array[2].y - array[0].y); float num4 = ((Rect)(ref uvRect)).x + num2 * ((Rect)(ref uvRect)).width; float num5 = ((Rect)(ref uvRect)).y + num3 * ((Rect)(ref uvRect)).height; return MapPointToWorld(new Vector2(num4, num5)); } private Vector2i WorldToGridCell(Vector3 worldPos) { //IL_0005: 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) return VoronoiGrid.Instance.GetRegion(worldPos); } private Vector2i? GetCellUnderMouse() { //IL_0002: Unknown result type (might be due to invalid IL or missing references) //IL_000a: Unknown result type (might be due to invalid IL or missing references) //IL_001a: 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_0047: Unknown result type (might be due to invalid IL or missing references) //IL_0053: Unknown result type (might be due to invalid IL or missing references) //IL_0060: Unknown result type (might be due to invalid IL or missing references) //IL_006c: Unknown result type (might be due to invalid IL or missing references) //IL_0092: Unknown result type (might be due to invalid IL or missing references) //IL_0097: Unknown result type (might be due to invalid IL or missing references) Vector2 guiPos = default(Vector2); ((Vector2)(ref guiPos))..ctor(Input.mousePosition.x, (float)Screen.height - Input.mousePosition.y); Vector3? val = ScreenGUIToWorld(guiPos); if (!val.HasValue) { return null; } if (Mathf.Sqrt(val.Value.x * val.Value.x + val.Value.z * val.Value.z) > WorldRadius) { return null; } return WorldToGridCell(val.Value); } private void OnGUI() { //IL_0005: Unknown result type (might be due to invalid IL or missing references) //IL_0010: Expected O, but got Unknown //IL_0017: Unknown result type (might be due to invalid IL or missing references) //IL_001d: Invalid comparison between Unknown and I4 //IL_0091: Unknown result type (might be due to invalid IL or missing references) //IL_009d: Unknown result type (might be due to invalid IL or missing references) //IL_00af: Unknown result type (might be due to invalid IL or missing references) //IL_00bb: Unknown result type (might be due to invalid IL or missing references) //IL_0111: Unknown result type (might be due to invalid IL or missing references) //IL_0108: Unknown result type (might be due to invalid IL or missing references) //IL_0216: Unknown result type (might be due to invalid IL or missing references) //IL_0116: Unknown result type (might be due to invalid IL or missing references) //IL_012f: Unknown result type (might be due to invalid IL or missing references) //IL_0138: Unknown result type (might be due to invalid IL or missing references) //IL_01c2: Unknown result type (might be due to invalid IL or missing references) //IL_01c3: 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_0192: Unknown result type (might be due to invalid IL or missing references) //IL_018b: Unknown result type (might be due to invalid IL or missing references) //IL_0197: Unknown result type (might be due to invalid IL or missing references) //IL_015d: Unknown result type (might be due to invalid IL or missing references) //IL_0162: Unknown result type (might be due to invalid IL or missing references) if ((Object)Minimap.instance == (Object)null || (int)Minimap.instance.m_mode != 2) { return; } InitStyles(); if (!TerritoryOverlayEnabled) { _showClaimPanel = false; _selectedCell = null; return; } _frameSkip++; if (_frameSkip >= 3) { _frameSkip = 0; _lastLookupResult = GetCellUnderMouse(); } Vector2i? lastLookupResult = _lastLookupResult; Vector2i val; Color color; if (lastLookupResult.HasValue) { if (!_stableHoveredCell.HasValue || _stableHoveredCell.Value.x != lastLookupResult.Value.x || _stableHoveredCell.Value.y != lastLookupResult.Value.y) { _hoverStableFrames++; if (_hoverStableFrames > 3) { _stableHoveredCell = lastLookupResult; _hoverStableFrames = 0; } } else { _hoverStableFrames = 0; } val = (Vector2i)(((??)_stableHoveredCell) ?? lastLookupResult.Value); ClanTerritory clanTerritory = WarfareManager.Instance?.Data?.GetTerritory(val); color = default(Color); if (clanTerritory != null && clanTerritory.FreebornOwnerId != 0L) { ConfigEntry showFreebornBorders = ClanConfig.ShowFreebornBorders; if (showFreebornBorders != null && showFreebornBorders.Value) { color = ClanConfig.GetFreebornBorderColor(clanTerritory); color.a = 0.95f; goto IL_01c1; } } if (clanTerritory != null && clanTerritory.OwnerClanId != 0) { color = ClanConfig.GetClan(clanTerritory.OwnerClanId)?.Color ?? Color.grey; color.a = 0.85f; } else { ((Color)(ref color))..ctor(0.5f, 0.5f, 0.5f, 0.6f); } goto IL_01c1; } _stableHoveredCell = null; _hoverStableFrames = 0; goto IL_01e6; IL_01e6: DrawAllClaimedBorders(); DrawProtectedWorldZoneMarkers(); DrawWorldEventMarkers(); if (!_showClaimPanel || !_selectedCell.HasValue) { return; } try { DrawClaimPanel(_selectedCell.Value); return; } catch { _stylesInit = false; return; } IL_01c1: DrawRegionBorder(val, color); DrawCellTooltip(val); goto IL_01e6; } private void DrawProtectedWorldZoneMarkers() { //IL_0005: Unknown result type (might be due to invalid IL or missing references) //IL_0010: Expected O, but got Unknown //IL_0018: Unknown result type (might be due to invalid IL or missing references) //IL_0023: Expected O, but got Unknown //IL_004b: Unknown result type (might be due to invalid IL or missing references) //IL_0055: Expected O, but got Unknown //IL_0091: Unknown result type (might be due to invalid IL or missing references) //IL_00cc: Unknown result type (might be due to invalid IL or missing references) //IL_00d1: Unknown result type (might be due to invalid IL or missing references) //IL_00e4: Unknown result type (might be due to invalid IL or missing references) //IL_00e9: Unknown result type (might be due to invalid IL or missing references) //IL_00eb: 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_0240: Unknown result type (might be due to invalid IL or missing references) //IL_0107: Unknown result type (might be due to invalid IL or missing references) //IL_010e: Unknown result type (might be due to invalid IL or missing references) //IL_00fa: Unknown result type (might be due to invalid IL or missing references) //IL_00ff: Unknown result type (might be due to invalid IL or missing references) //IL_0104: Unknown result type (might be due to invalid IL or missing references) //IL_012a: 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_0156: Unknown result type (might be due to invalid IL or missing references) //IL_016f: Unknown result type (might be due to invalid IL or missing references) //IL_017f: Unknown result type (might be due to invalid IL or missing references) //IL_0189: Expected O, but got Unknown //IL_01b0: Unknown result type (might be due to invalid IL or missing references) //IL_01c2: Unknown result type (might be due to invalid IL or missing references) //IL_01cc: Expected O, but got Unknown //IL_01c7: Unknown result type (might be due to invalid IL or missing references) //IL_01cc: Unknown result type (might be due to invalid IL or missing references) //IL_01ce: Unknown result type (might be due to invalid IL or missing references) //IL_01da: 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_01f4: Unknown result type (might be due to invalid IL or missing references) //IL_0204: Unknown result type (might be due to invalid IL or missing references) //IL_020b: Unknown result type (might be due to invalid IL or missing references) //IL_0212: Unknown result type (might be due to invalid IL or missing references) if ((Object)Minimap.instance == (Object)null || (Object)_pixelTex == (Object)null) { return; } List knownProtectedZones = ProtectedWorldZones.GetKnownProtectedZones(); if (knownProtectedZones == null || knownProtectedZones.Count == 0) { return; } if (_territoryLabelStyle == null) { _territoryLabelStyle = new GUIStyle(GUI.skin.label); _territoryLabelStyle.fontSize = 11; _territoryLabelStyle.fontStyle = (FontStyle)1; _territoryLabelStyle.alignment = (TextAnchor)4; _territoryLabelStyle.richText = true; _territoryLabelStyle.normal.textColor = Color.white; } Color color = default(Color); ((Color)(ref color))..ctor(0.55f, 0.7f, 1f, 0.8f); foreach (ProtectedZoneInfo item in knownProtectedZones) { DrawRegionBorder(item.Grid, color, drawCenterMarker: false); if (item.DrawMarker) { Vector3 val = item.Position; if (val == Vector3.zero) { val = item.Grid.ToWorldCenter(); } Vector2? val2 = WorldToScreenGUI(val.x, val.z); if (val2.HasValue) { Rect val3 = new Rect(val2.Value.x - 5f, val2.Value.y - 5f, 10f, 10f); GUI.color = new Color(0.15f, 0.35f, 0.95f, 0.85f); GUI.DrawTexture(val3, (Texture)_pixelTex); string name = item.Name; _territoryLabelStyle.normal.textColor = new Color(0.65f, 0.78f, 1f, 1f); Vector2 val4 = _territoryLabelStyle.CalcSize(new GUIContent(name)); GUI.color = Color.white; GUI.Label(new Rect(val2.Value.x - val4.x / 2f, val2.Value.y + 8f, val4.x, val4.y), name, _territoryLabelStyle); } } } GUI.color = Color.white; } private void DrawAllClaimedBorders() { //IL_00c5: Unknown result type (might be due to invalid IL or missing references) //IL_0195: Unknown result type (might be due to invalid IL or missing references) //IL_019a: 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_0137: Unknown result type (might be due to invalid IL or missing references) //IL_0345: Unknown result type (might be due to invalid IL or missing references) //IL_02f3: Unknown result type (might be due to invalid IL or missing references) //IL_02fd: Expected O, but got Unknown //IL_0339: Unknown result type (might be due to invalid IL or missing references) //IL_0252: Unknown result type (might be due to invalid IL or missing references) //IL_0257: Unknown result type (might be due to invalid IL or missing references) //IL_025c: Unknown result type (might be due to invalid IL or missing references) //IL_0263: Unknown result type (might be due to invalid IL or missing references) //IL_028d: 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) //IL_03bd: Unknown result type (might be due to invalid IL or missing references) //IL_03c2: Unknown result type (might be due to invalid IL or missing references) //IL_03b4: Unknown result type (might be due to invalid IL or missing references) //IL_03ab: Unknown result type (might be due to invalid IL or missing references) //IL_06ac: Unknown result type (might be due to invalid IL or missing references) //IL_06b1: Unknown result type (might be due to invalid IL or missing references) //IL_06b7: Unknown result type (might be due to invalid IL or missing references) //IL_06b8: Unknown result type (might be due to invalid IL or missing references) //IL_06bd: Unknown result type (might be due to invalid IL or missing references) //IL_06c0: Unknown result type (might be due to invalid IL or missing references) //IL_06c7: Unknown result type (might be due to invalid IL or missing references) //IL_04c1: Unknown result type (might be due to invalid IL or missing references) //IL_0446: Unknown result type (might be due to invalid IL or missing references) //IL_0448: Unknown result type (might be due to invalid IL or missing references) //IL_0456: Unknown result type (might be due to invalid IL or missing references) //IL_04d1: Unknown result type (might be due to invalid IL or missing references) //IL_04d6: Unknown result type (might be due to invalid IL or missing references) //IL_04e6: Unknown result type (might be due to invalid IL or missing references) //IL_04eb: Unknown result type (might be due to invalid IL or missing references) //IL_04ed: Unknown result type (might be due to invalid IL or missing references) //IL_04fb: Unknown result type (might be due to invalid IL or missing references) //IL_0694: Unknown result type (might be due to invalid IL or missing references) //IL_0466: Unknown result type (might be due to invalid IL or missing references) //IL_046b: Unknown result type (might be due to invalid IL or missing references) //IL_047b: Unknown result type (might be due to invalid IL or missing references) //IL_0480: Unknown result type (might be due to invalid IL or missing references) //IL_0482: Unknown result type (might be due to invalid IL or missing references) //IL_050a: Unknown result type (might be due to invalid IL or missing references) //IL_050c: Unknown result type (might be due to invalid IL or missing references) //IL_07d0: Unknown result type (might be due to invalid IL or missing references) //IL_0490: Unknown result type (might be due to invalid IL or missing references) //IL_07e2: Unknown result type (might be due to invalid IL or missing references) //IL_07ec: Expected O, but got Unknown //IL_07e7: Unknown result type (might be due to invalid IL or missing references) //IL_07ec: Unknown result type (might be due to invalid IL or missing references) //IL_07f0: Unknown result type (might be due to invalid IL or missing references) //IL_07fa: Unknown result type (might be due to invalid IL or missing references) //IL_080a: Unknown result type (might be due to invalid IL or missing references) //IL_0814: Unknown result type (might be due to invalid IL or missing references) //IL_0822: Unknown result type (might be due to invalid IL or missing references) //IL_0829: Unknown result type (might be due to invalid IL or missing references) //IL_0830: Unknown result type (might be due to invalid IL or missing references) //IL_07c7: Unknown result type (might be due to invalid IL or missing references) //IL_07be: Unknown result type (might be due to invalid IL or missing references) //IL_049f: Unknown result type (might be due to invalid IL or missing references) //IL_04a1: Unknown result type (might be due to invalid IL or missing references) //IL_0620: Unknown result type (might be due to invalid IL or missing references) //IL_0596: Unknown result type (might be due to invalid IL or missing references) //IL_0598: Unknown result type (might be due to invalid IL or missing references) //IL_05a6: Unknown result type (might be due to invalid IL or missing references) //IL_0630: Unknown result type (might be due to invalid IL or missing references) //IL_0635: Unknown result type (might be due to invalid IL or missing references) //IL_0645: Unknown result type (might be due to invalid IL or missing references) //IL_064a: Unknown result type (might be due to invalid IL or missing references) //IL_064c: Unknown result type (might be due to invalid IL or missing references) //IL_05b6: Unknown result type (might be due to invalid IL or missing references) //IL_05bb: Unknown result type (might be due to invalid IL or missing references) //IL_05cb: Unknown result type (might be due to invalid IL or missing references) //IL_05d0: Unknown result type (might be due to invalid IL or missing references) //IL_05d2: Unknown result type (might be due to invalid IL or missing references) //IL_065a: Unknown result type (might be due to invalid IL or missing references) //IL_05e0: Unknown result type (might be due to invalid IL or missing references) //IL_0678: Unknown result type (might be due to invalid IL or missing references) //IL_067a: Unknown result type (might be due to invalid IL or missing references) //IL_05fe: Unknown result type (might be due to invalid IL or missing references) //IL_0600: Unknown result type (might be due to invalid IL or missing references) WarfareData warfareData = WarfareManager.Instance?.Data; if (warfareData?.Territories == null || warfareData.Territories.Count == 0) { return; } int num = 0; foreach (KeyValuePair territory in warfareData.Territories) { if (territory.Value.OwnerClanId == 0) { if (territory.Value.FreebornOwnerId == 0L) { continue; } ConfigEntry showFreebornBorders = ClanConfig.ShowFreebornBorders; if (showFreebornBorders == null || !showFreebornBorders.Value) { continue; } } num++; } if (num != _claimedTerritoryCount || Time.time > _nextClaimedRebuild) { _claimedBorderCache.Clear(); _claimedScreenCache.Clear(); Vector2i val = default(Vector2i); foreach (KeyValuePair territory2 in warfareData.Territories) { ClanTerritory value = territory2.Value; if (value.OwnerClanId == 0) { if (value.FreebornOwnerId == 0L) { continue; } ConfigEntry showFreebornBorders2 = ClanConfig.ShowFreebornBorders; if (showFreebornBorders2 == null || !showFreebornBorders2.Value) { continue; } } string key = territory2.Key; val = new Vector2i(value.GridX, value.GridY); List list = new List(); BuildBorderWorldPointsFor(val, list); _claimedBorderCache[key] = list; } _claimedTerritoryCount = num; _nextClaimedRebuild = Time.time + 10f; _claimedPanX = float.MinValue; } Rect uvRect = Minimap.instance.m_mapImageLarge.uvRect; if (Mathf.Abs(((Rect)(ref uvRect)).x - _claimedPanX) > 0.0001f || Mathf.Abs(((Rect)(ref uvRect)).y - _claimedPanY) > 0.0001f || Mathf.Abs(((Rect)(ref uvRect)).width - _claimedZoom) > 0.0001f) { _claimedPanX = ((Rect)(ref uvRect)).x; _claimedPanY = ((Rect)(ref uvRect)).y; _claimedZoom = ((Rect)(ref uvRect)).width; _claimedScreenCache.Clear(); foreach (KeyValuePair> item in _claimedBorderCache) { List list2 = new List(); foreach (Vector2 item2 in item.Value) { list2.Add((Vector2)(((??)WorldToScreenGUI(item2.x, item2.y)) ?? new Vector2(-9999f, -9999f))); } _claimedScreenCache[item.Key] = list2; } } if (_territoryLabelStyle == null) { _territoryLabelStyle = new GUIStyle(GUI.skin.label); _territoryLabelStyle.fontSize = 11; _territoryLabelStyle.fontStyle = (FontStyle)1; _territoryLabelStyle.alignment = (TextAnchor)4; _territoryLabelStyle.richText = true; _territoryLabelStyle.normal.textColor = Color.white; } Vector2i val2 = default(Vector2i); foreach (KeyValuePair territory3 in warfareData.Territories) { ClanTerritory value2 = territory3.Value; if (value2.OwnerClanId == 0 && value2.FreebornOwnerId == 0L) { continue; } Clan clan = ((value2.OwnerClanId != 0) ? ClanConfig.GetClan(value2.OwnerClanId) : null); Color val3 = ((value2.FreebornOwnerId != 0L) ? ClanConfig.GetFreebornBorderColor(value2) : (clan?.Color ?? Color.grey)); float territoryBorderThickness = GetTerritoryBorderThickness(value2); val3.a = 0.55f; List value3; List worldPoints = (_claimedBorderCache.TryGetValue(territory3.Key, out value3) ? value3 : null); if (_claimedScreenCache.TryGetValue(territory3.Key, out var value4) && value4.Count >= 3) { if (value2.IsCapital || value2.Type == TerritoryType.Homestead) { ConfigEntry allianceBorderGlow = ClanConfig.AllianceBorderGlow; if (allianceBorderGlow != null && allianceBorderGlow.Value) { Color color = val3; color.a = 0.18f; GUI.color = color; for (int i = 0; i < value4.Count; i++) { Vector2 val4 = value4[i]; Vector2 val5 = value4[(i + 1) % value4.Count]; if (!(val4.x < -9000f) && !(val5.x < -9000f)) { DrawLineSimple(val4, val5, territoryBorderThickness + 3f); } } } } GUI.color = val3; for (int j = 0; j < value4.Count; j++) { Vector2 val6 = value4[j]; Vector2 val7 = value4[(j + 1) % value4.Count]; if (!(val6.x < -9000f) && !(val7.x < -9000f)) { DrawLineSimple(val6, val7, territoryBorderThickness); } } if (value2.OwnerClanId != 0) { ConfigEntry allianceBorderEnabled = ClanConfig.AllianceBorderEnabled; if (allianceBorderEnabled != null && allianceBorderEnabled.Value && TryGetAllianceBorderColor(warfareData, value2.OwnerClanId, out var color2)) { float num2 = ClanConfig.AllianceBorderThickness?.Value ?? 2f; color2.a = 0.85f; ConfigEntry allianceBorderGlow2 = ClanConfig.AllianceBorderGlow; if (allianceBorderGlow2 != null && allianceBorderGlow2.Value) { Color color3 = color2; color3.a = 0.22f; GUI.color = color3; for (int k = 0; k < value4.Count; k++) { Vector2 val8 = value4[k]; Vector2 val9 = value4[(k + 1) % value4.Count]; if (!(val8.x < -9000f) && !(val9.x < -9000f) && !HasSameOrAlliedNeighbor(warfareData, value2, worldPoints, k)) { DrawLineSimple(val8, val9, num2 + 3f); } } } GUI.color = color2; for (int l = 0; l < value4.Count; l++) { Vector2 val10 = value4[l]; Vector2 val11 = value4[(l + 1) % value4.Count]; if (!(val10.x < -9000f) && !(val11.x < -9000f) && !HasSameOrAlliedNeighbor(warfareData, value2, worldPoints, l)) { DrawLineSimple(val10, val11, num2); } } } } GUI.color = Color.white; } val2 = new Vector2i(value2.GridX, value2.GridY); Vector3 regionCenter = VoronoiGrid.Instance.GetRegionCenter(val2); Vector2? val12 = WorldToScreenGUI(regionCenter.x, regionCenter.z); if (!val12.HasValue) { continue; } string text = ((value2.FreebornOwnerId == 0L) ? (clan?.Symbol + " " + value2.DisplayName) : ((value2.Type == TerritoryType.Homestead) ? "Freeborn Homestead" : "Freeborn Outpost")); if (value2.FreebornOwnerId != 0L) { ConfigEntry showFreebornOwnerName = ClanConfig.ShowFreebornOwnerName; if (showFreebornOwnerName != null && showFreebornOwnerName.Value) { text = text + " of " + (value2.FreebornOwnerName ?? "Freeborn"); } } if (value2.OwnerClanId != 0 && value2.IsCapital) { text = "★ " + text; } if (value2.RotCount > 0) { text += $" [{value2.RotCount}/4 rot]"; } _territoryLabelStyle.normal.textColor = ((value2.FreebornOwnerId != 0L) ? ClanConfig.GetFreebornBorderColor(value2) : (clan?.Color ?? Color.white)); Vector2 val13 = _territoryLabelStyle.CalcSize(new GUIContent(text)); GUI.Label(new Rect(val12.Value.x - val13.x / 2f, val12.Value.y - val13.y / 2f, val13.x, val13.y), text, _territoryLabelStyle); } } private float GetTerritoryBorderThickness(ClanTerritory territory) { if (territory == null) { return ClanConfig.ColonyBorderThickness?.Value ?? 4f; } float num = ClanConfig.ColonyBorderThickness?.Value ?? 4f; if (territory.FreebornOwnerId != 0L) { if (territory.Type == TerritoryType.Homestead) { return num * (ClanConfig.HomesteadBorderThicknessMultiplier?.Value ?? 1.5f); } return 2f; } if (territory.Type == TerritoryType.Capital || territory.IsCapital) { return num * (ClanConfig.CapitalBorderThicknessMultiplier?.Value ?? 2f); } return num; } private bool TryGetAllianceBorderColor(WarfareData data, int clanId, out Color color) { //IL_0015: Unknown result type (might be due to invalid IL or missing references) //IL_001a: Unknown result type (might be due to invalid IL or missing references) //IL_0070: Unknown result type (might be due to invalid IL or missing references) //IL_0075: Unknown result type (might be due to invalid IL or missing references) color = new Color(0.2f, 0.45f, 1f, 1f); if (data == null || clanId == 0) { return false; } foreach (DiplomacyRelation item in data.Diplomacy) { if (item.State == DiplomacyState.Allied && (item.ClanA == clanId || item.ClanB == clanId)) { color = new Color(item.AllianceColorR, item.AllianceColorG, item.AllianceColorB, 1f); return true; } } return false; } private bool HasSameOrAlliedNeighbor(WarfareData data, ClanTerritory territory, List worldPoints, int index) { //IL_001f: Unknown result type (might be due to invalid IL or missing references) //IL_0030: Unknown result type (might be due to invalid IL or missing references) //IL_0035: Unknown result type (might be due to invalid IL or missing references) //IL_0036: Unknown result type (might be due to invalid IL or missing references) //IL_0037: Unknown result type (might be due to invalid IL or missing references) //IL_0041: Unknown result type (might be due to invalid IL or missing references) //IL_0046: Unknown result type (might be due to invalid IL or missing references) //IL_004d: Unknown result type (might be due to invalid IL or missing references) //IL_0052: Unknown result type (might be due to invalid IL or missing references) //IL_0057: Unknown result type (might be due to invalid IL or missing references) //IL_0058: Unknown result type (might be due to invalid IL or missing references) //IL_0059: Unknown result type (might be due to invalid IL or missing references) //IL_005f: Unknown result type (might be due to invalid IL or missing references) //IL_0065: Unknown result type (might be due to invalid IL or missing references) //IL_006a: Unknown result type (might be due to invalid IL or missing references) //IL_006f: Unknown result type (might be due to invalid IL or missing references) //IL_008f: Unknown result type (might be due to invalid IL or missing references) //IL_0090: Unknown result type (might be due to invalid IL or missing references) //IL_009a: Unknown result type (might be due to invalid IL or missing references) //IL_009f: Unknown result type (might be due to invalid IL or missing references) //IL_00a4: Unknown result type (might be due to invalid IL or missing references) //IL_00ab: Unknown result type (might be due to invalid IL or missing references) //IL_00b7: Unknown result type (might be due to invalid IL or missing references) //IL_00be: Unknown result type (might be due to invalid IL or missing references) //IL_00c3: Unknown result type (might be due to invalid IL or missing references) //IL_00c8: Unknown result type (might be due to invalid IL or missing references) //IL_00ca: Unknown result type (might be due to invalid IL or missing references) //IL_00e9: 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) if (data == null || territory == null || territory.OwnerClanId == 0 || worldPoints == null || worldPoints.Count < 3) { return false; } Vector2 val = worldPoints[index]; Vector2 val2 = worldPoints[(index + 1) % worldPoints.Count]; Vector2 val3 = (val + val2) * 0.5f; Vector3 regionCenter = VoronoiGrid.Instance.GetRegionCenter(territory.GridPosition); Vector2 val4 = val3 - new Vector2(regionCenter.x, regionCenter.z); if (((Vector2)(ref val4)).sqrMagnitude < 0.01f) { return false; } ((Vector2)(ref val4)).Normalize(); for (int i = 1; i <= 4; i++) { Vector2 val5 = val3 + val4 * (60f * (float)i); Vector2i region = VoronoiGrid.Instance.GetRegion(new Vector3(val5.x, 0f, val5.y)); if (region.x != territory.GridX || region.y != territory.GridY) { ClanTerritory territory2 = data.GetTerritory(region); if (territory2 == null || territory2.OwnerClanId == 0) { return false; } if (territory2.OwnerClanId == territory.OwnerClanId) { return true; } WarfareManager instance = WarfareManager.Instance; if (instance == null) { return false; } return instance.GetDiplomacyState(territory.OwnerClanId, territory2.OwnerClanId) == DiplomacyState.Allied; } } return false; } private void BuildBorderWorldPointsFor(Vector2i regionId, List result) { //IL_0005: 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_000b: Unknown result type (might be due to invalid IL or missing references) //IL_0011: Unknown result type (might be due to invalid IL or missing references) //IL_0067: Unknown result type (might be due to invalid IL or missing references) //IL_0075: Unknown result type (might be due to invalid IL or missing references) //IL_0091: Unknown result type (might be due to invalid IL or missing references) //IL_0096: Unknown result type (might be due to invalid IL or missing references) //IL_009b: Unknown result type (might be due to invalid IL or missing references) //IL_009d: Unknown result type (might be due to invalid IL or missing references) //IL_00a4: Unknown result type (might be due to invalid IL or missing references) //IL_015a: Unknown result type (might be due to invalid IL or missing references) //IL_0168: Unknown result type (might be due to invalid IL or missing references) //IL_00ac: Unknown result type (might be due to invalid IL or missing references) //IL_00b3: Unknown result type (might be due to invalid IL or missing references) //IL_01b3: 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) //IL_00f1: Unknown result type (might be due to invalid IL or missing references) //IL_00fd: Unknown result type (might be due to invalid IL or missing references) //IL_0102: Unknown result type (might be due to invalid IL or missing references) //IL_0107: Unknown result type (might be due to invalid IL or missing references) //IL_0109: Unknown result type (might be due to invalid IL or missing references) //IL_0110: 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_011f: Unknown result type (might be due to invalid IL or missing references) Vector3 regionCenter = VoronoiGrid.Instance.GetRegionCenter(regionId); float num = VoronoiGrid.Instance.GetApproximateRegionRadius(regionId) * 4f; if (num < 2000f) { num = 2000f; } int num2 = 60; float num3 = (float)Math.PI * 2f / (float)num2; for (int i = 0; i < num2; i++) { float num4 = (float)i * num3; float num5 = Mathf.Cos(num4); float num6 = Mathf.Sin(num4); float num7 = 50f; float num8 = num; for (float num9 = num7; num9 < num; num9 += num7) { float num10 = regionCenter.x + num5 * num9; float num11 = regionCenter.z + num6 * num9; Vector2i region = VoronoiGrid.Instance.GetRegion(new Vector3(num10, 0f, num11)); if (region.x == regionId.x && region.y == regionId.y) { continue; } float num12 = num9 - num7; float num13 = num9; for (int j = 0; j < 4; j++) { float num14 = (num12 + num13) * 0.5f; Vector2i region2 = VoronoiGrid.Instance.GetRegion(new Vector3(regionCenter.x + num5 * num14, 0f, regionCenter.z + num6 * num14)); if (region2.x == regionId.x && region2.y == regionId.y) { num12 = num14; } else { num13 = num14; } } num8 = (num12 + num13) * 0.5f; break; } float num15 = regionCenter.x + num5 * num8; float num16 = regionCenter.z + num6 * num8; float num17 = Mathf.Sqrt(num15 * num15 + num16 * num16); if (num17 > WorldRadius) { num15 *= WorldRadius / num17; num16 *= WorldRadius / num17; } result.Add(new Vector2(num15, num16)); } } private void InitStyles() { //IL_000e: Unknown result type (might be due to invalid IL or missing references) //IL_0019: Expected O, but got Unknown //IL_0060: Unknown result type (might be due to invalid IL or missing references) //IL_006a: Expected O, but got Unknown //IL_0072: Unknown result type (might be due to invalid IL or missing references) //IL_0092: Unknown result type (might be due to invalid IL or missing references) //IL_009c: Expected O, but got Unknown //IL_00be: Unknown result type (might be due to invalid IL or missing references) //IL_00db: Unknown result type (might be due to invalid IL or missing references) //IL_00e5: Expected O, but got Unknown //IL_00f0: Unknown result type (might be due to invalid IL or missing references) //IL_00fa: Expected O, but got Unknown //IL_012d: Unknown result type (might be due to invalid IL or missing references) //IL_014e: Unknown result type (might be due to invalid IL or missing references) //IL_0158: Expected O, but got Unknown //IL_0170: Unknown result type (might be due to invalid IL or missing references) //IL_019d: Unknown result type (might be due to invalid IL or missing references) //IL_01a7: Expected O, but got Unknown //IL_01bf: Unknown result type (might be due to invalid IL or missing references) //IL_01d5: Unknown result type (might be due to invalid IL or missing references) //IL_01df: Expected O, but got Unknown //IL_01ea: Unknown result type (might be due to invalid IL or missing references) //IL_01f4: Expected O, but got Unknown //IL_0218: 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_0237: Expected O, but got Unknown //IL_026a: 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_0289: Expected O, but got Unknown //IL_02a1: Unknown result type (might be due to invalid IL or missing references) //IL_003e: Unknown result type (might be due to invalid IL or missing references) //IL_0049: Expected O, but got Unknown if (_stylesInit) { if (!((Object)_pixelTex == (Object)null)) { GUIStyle panelStyle = _panelStyle; object obj; if (panelStyle == null) { obj = null; } else { GUIStyleState normal = panelStyle.normal; obj = ((normal != null) ? normal.background : null); } if (!((Object)obj == (Object)null)) { goto IL_0052; } } _stylesInit = false; } goto IL_0052; IL_0052: if (!_stylesInit) { _pixelTex = new Texture2D(1, 1); _pixelTex.SetPixel(0, 0, Color.white); _pixelTex.Apply(); _panelStyle = new GUIStyle(GUI.skin.box); _panelStyle.normal.background = MakeTex(2, 2, new Color(0.1f, 0.1f, 0.1f, 0.95f)); _panelStyle.padding = new RectOffset(15, 15, 10, 10); _headerStyle = new GUIStyle(GUI.skin.label); _headerStyle.fontSize = 18; _headerStyle.fontStyle = (FontStyle)1; _headerStyle.normal.textColor = new Color(1f, 0.85f, 0.4f); _headerStyle.alignment = (TextAnchor)4; _labelStyle = new GUIStyle(GUI.skin.label); _labelStyle.fontSize = 14; _labelStyle.normal.textColor = Color.white; _labelStyle.richText = true; _labelStyle.wordWrap = true; _buttonStyle = new GUIStyle(GUI.skin.button); _buttonStyle.fontSize = 14; _buttonStyle.normal.textColor = Color.white; _buttonStyle.padding = new RectOffset(10, 10, 6, 6); _closeButtonStyle = new GUIStyle(GUI.skin.button); _closeButtonStyle.fontSize = 16; _closeButtonStyle.fontStyle = (FontStyle)1; _closeButtonStyle.normal.textColor = Color.white; _tooltipTitleStyle = new GUIStyle(GUI.skin.label); _tooltipTitleStyle.fontSize = 14; _tooltipTitleStyle.fontStyle = (FontStyle)1; _tooltipTitleStyle.normal.textColor = new Color(1f, 0.85f, 0.4f); _tooltipDetailStyle = new GUIStyle(GUI.skin.label); _tooltipDetailStyle.fontSize = 12; _tooltipDetailStyle.normal.textColor = Color.white; _tooltipDetailStyle.richText = true; _stylesInit = true; } } private Texture2D MakeTex(int width, int height, Color col) { //IL_0014: Unknown result type (might be due to invalid IL or missing references) //IL_0015: Unknown result type (might be due to invalid IL or missing references) //IL_0026: 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_0032: Unknown result type (might be due to invalid IL or missing references) //IL_0039: Expected O, but got Unknown Color[] array = (Color[])(object)new Color[width * height]; for (int i = 0; i < array.Length; i++) { array[i] = col; } Texture2D val = new Texture2D(width, height); val.SetPixels(array); val.Apply(); return val; } private void DrawRegionMarker(Vector2i regionId, Color color, float size) { //IL_0005: 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_000b: Unknown result type (might be due to invalid IL or missing references) //IL_000d: Unknown result type (might be due to invalid IL or missing references) //IL_0013: Unknown result type (might be due to invalid IL or missing references) //IL_0028: Unknown result type (might be due to invalid IL or missing references) //IL_0038: Unknown result type (might be due to invalid IL or missing references) //IL_0046: Unknown result type (might be due to invalid IL or missing references) //IL_0054: Unknown result type (might be due to invalid IL or missing references) //IL_005f: Unknown result type (might be due to invalid IL or missing references) //IL_0069: Expected O, but got Unknown //IL_0069: Unknown result type (might be due to invalid IL or missing references) Vector3 regionCenter = VoronoiGrid.Instance.GetRegionCenter(regionId); Vector2? val = WorldToScreenGUI(regionCenter.x, regionCenter.z); if (val.HasValue) { GUI.color = color; float num = size / 2f; GUI.DrawTexture(new Rect(val.Value.x - num, val.Value.y - num, size, size), (Texture)_pixelTex); GUI.color = Color.white; } } private void DrawPlayerCellMarker() { //IL_0005: Unknown result type (might be due to invalid IL or missing references) //IL_0010: Expected O, but got Unknown //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_0023: Unknown result type (might be due to invalid IL or missing references) //IL_0024: Unknown result type (might be due to invalid IL or missing references) //IL_0029: 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_0040: Unknown result type (might be due to invalid IL or missing references) if (!((Object)Player.m_localPlayer == (Object)null)) { Vector3 position = ((Component)Player.m_localPlayer).transform.position; Vector2i regionId = WorldToGridCell(position); DrawRegionMarker(regionId, new Color(0.3f, 0.8f, 0.3f, 0.4f), 10f); } } private void DrawRegionBorder(Vector2i regionId, Color color, bool drawCenterMarker = true) { //IL_003e: Unknown result type (might be due to invalid IL or missing references) //IL_0045: Unknown result type (might be due to invalid IL or missing references) //IL_0013: 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_002b: Unknown result type (might be due to invalid IL or missing references) //IL_0035: Unknown result type (might be due to invalid IL or missing references) //IL_007c: Unknown result type (might be due to invalid IL or missing references) //IL_0081: Unknown result type (might be due to invalid IL or missing references) //IL_0110: Unknown result type (might be due to invalid IL or missing references) //IL_0115: Unknown result type (might be due to invalid IL or missing references) //IL_0117: Unknown result type (might be due to invalid IL or missing references) //IL_011d: Unknown result type (might be due to invalid IL or missing references) //IL_0150: 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_017b: 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_0189: Unknown result type (might be due to invalid IL or missing references) //IL_019c: Unknown result type (might be due to invalid IL or missing references) //IL_01a1: 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_01be: Unknown result type (might be due to invalid IL or missing references) //IL_01c0: Unknown result type (might be due to invalid IL or missing references) //IL_01ce: Unknown result type (might be due to invalid IL or missing references) //IL_0278: Unknown result type (might be due to invalid IL or missing references) //IL_0208: Unknown result type (might be due to invalid IL or missing references) //IL_0209: Unknown result type (might be due to invalid IL or missing references) //IL_020e: Unknown result type (might be due to invalid IL or missing references) //IL_0211: Unknown result type (might be due to invalid IL or missing references) //IL_0218: Unknown result type (might be due to invalid IL or missing references) //IL_01dd: Unknown result type (might be due to invalid IL or missing references) //IL_01df: Unknown result type (might be due to invalid IL or missing references) //IL_022f: Unknown result type (might be due to invalid IL or missing references) //IL_0237: Unknown result type (might be due to invalid IL or missing references) //IL_0249: Unknown result type (might be due to invalid IL or missing references) //IL_0263: Unknown result type (might be due to invalid IL or missing references) //IL_026e: Unknown result type (might be due to invalid IL or missing references) //IL_0278: Expected O, but got Unknown if (!_cachedHighlightRegion.HasValue || _cachedHighlightRegion.Value.x != regionId.x || _cachedHighlightRegion.Value.y != regionId.y) { BuildBorderWorldPoints(regionId); _cachedHighlightRegion = regionId; _lastMapPanX = float.MinValue; } if (_cachedBorderWorldPoints == null || _cachedBorderWorldPoints.Count < 3) { return; } Rect uvRect = Minimap.instance.m_mapImageLarge.uvRect; if (Mathf.Abs(((Rect)(ref uvRect)).x - _lastMapPanX) > 0.0001f || Mathf.Abs(((Rect)(ref uvRect)).y - _lastMapPanY) > 0.0001f || Mathf.Abs(((Rect)(ref uvRect)).width - _lastMapZoom2) > 0.0001f) { _lastMapPanX = ((Rect)(ref uvRect)).x; _lastMapPanY = ((Rect)(ref uvRect)).y; _lastMapZoom2 = ((Rect)(ref uvRect)).width; _cachedScreenPoints.Clear(); for (int i = 0; i < _cachedBorderWorldPoints.Count; i++) { Vector2 val = _cachedBorderWorldPoints[i]; Vector2? val2 = WorldToScreenGUI(val.x, val.y); _cachedScreenPoints.Add((Vector2)(((??)val2) ?? new Vector2(-9999f, -9999f))); } } if (_cachedScreenPoints.Count < 3) { return; } Color color2 = color; color2.a = 0.85f; GUI.color = color2; for (int j = 0; j < _cachedScreenPoints.Count; j++) { Vector2 val3 = _cachedScreenPoints[j]; Vector2 val4 = _cachedScreenPoints[(j + 1) % _cachedScreenPoints.Count]; if (!(val3.x < -9000f) && !(val4.x < -9000f)) { DrawLineSimple(val3, val4, 3f); } } if (drawCenterMarker) { Vector3 regionCenter = VoronoiGrid.Instance.GetRegionCenter(regionId); Vector2? val5 = WorldToScreenGUI(regionCenter.x, regionCenter.z); if (val5.HasValue) { GUI.color = color; GUI.DrawTexture(new Rect(val5.Value.x - 4f, val5.Value.y - 4f, 8f, 8f), (Texture)_pixelTex); } } GUI.color = Color.white; } private void DrawLineSimple(Vector2 a, Vector2 b, float thickness) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_0001: Unknown result type (might be due to invalid IL or missing references) //IL_0002: Unknown result type (might be due to invalid IL or missing references) //IL_0007: Unknown result type (might be due to invalid IL or missing references) //IL_0018: Unknown result type (might be due to invalid IL or missing references) //IL_001e: Unknown result type (might be due to invalid IL or missing references) //IL_002f: Unknown result type (might be due to invalid IL or missing references) //IL_0034: Unknown result type (might be due to invalid IL or missing references) //IL_0035: Unknown result type (might be due to invalid IL or missing references) //IL_003b: Unknown result type (might be due to invalid IL or missing references) //IL_0041: Unknown result type (might be due to invalid IL or missing references) //IL_0051: 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) //IL_0066: Expected O, but got Unknown //IL_0066: Unknown result type (might be due to invalid IL or missing references) Vector2 val = b - a; float magnitude = ((Vector2)(ref val)).magnitude; if (!(magnitude < 0.5f)) { float num = Mathf.Atan2(val.y, val.x) * 57.29578f; Matrix4x4 matrix = GUI.matrix; GUIUtility.RotateAroundPivot(num, a); GUI.DrawTexture(new Rect(a.x, a.y - thickness * 0.5f, magnitude, thickness), (Texture)_pixelTex); GUI.matrix = matrix; } } private void BuildBorderWorldPoints(Vector2i regionId) { //IL_0010: Unknown result type (might be due to invalid IL or missing references) //IL_0011: Unknown result type (might be due to invalid IL or missing references) //IL_0016: Unknown result type (might be due to invalid IL or missing references) //IL_001c: Unknown result type (might be due to invalid IL or missing references) //IL_0072: Unknown result type (might be due to invalid IL or missing references) //IL_0080: 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_00a6: Unknown result type (might be due to invalid IL or missing references) //IL_00a8: Unknown result type (might be due to invalid IL or missing references) //IL_00af: Unknown result type (might be due to invalid IL or missing references) //IL_0165: Unknown result type (might be due to invalid IL or missing references) //IL_0173: Unknown result type (might be due to invalid IL or missing references) //IL_00b7: Unknown result type (might be due to invalid IL or missing references) //IL_00be: Unknown result type (might be due to invalid IL or missing references) //IL_01c3: Unknown result type (might be due to invalid IL or missing references) //IL_00eb: Unknown result type (might be due to invalid IL or missing references) //IL_00fc: Unknown result type (might be due to invalid IL or missing references) //IL_0108: Unknown result type (might be due to invalid IL or missing references) //IL_010d: Unknown result type (might be due to invalid IL or missing references) //IL_0112: Unknown result type (might be due to invalid IL or missing references) //IL_0114: Unknown result type (might be due to invalid IL or missing references) //IL_011b: Unknown result type (might be due to invalid IL or missing references) //IL_0123: Unknown result type (might be due to invalid IL or missing references) //IL_012a: Unknown result type (might be due to invalid IL or missing references) _cachedBorderWorldPoints.Clear(); Vector3 regionCenter = VoronoiGrid.Instance.GetRegionCenter(regionId); float num = VoronoiGrid.Instance.GetApproximateRegionRadius(regionId) * 4f; if (num < 2000f) { num = 2000f; } int num2 = 90; float num3 = (float)Math.PI * 2f / (float)num2; for (int i = 0; i < num2; i++) { float num4 = (float)i * num3; float num5 = Mathf.Cos(num4); float num6 = Mathf.Sin(num4); float num7 = 30f; float num8 = num; for (float num9 = num7; num9 < num; num9 += num7) { float num10 = regionCenter.x + num5 * num9; float num11 = regionCenter.z + num6 * num9; Vector2i region = VoronoiGrid.Instance.GetRegion(new Vector3(num10, 0f, num11)); if (region.x == regionId.x && region.y == regionId.y) { continue; } float num12 = num9 - num7; float num13 = num9; for (int j = 0; j < 5; j++) { float num14 = (num12 + num13) * 0.5f; Vector2i region2 = VoronoiGrid.Instance.GetRegion(new Vector3(regionCenter.x + num5 * num14, 0f, regionCenter.z + num6 * num14)); if (region2.x == regionId.x && region2.y == regionId.y) { num12 = num14; } else { num13 = num14; } } num8 = (num12 + num13) * 0.5f; break; } float num15 = regionCenter.x + num5 * num8; float num16 = regionCenter.z + num6 * num8; float num17 = Mathf.Sqrt(num15 * num15 + num16 * num16); if (num17 > WorldRadius) { num15 *= WorldRadius / num17; num16 *= WorldRadius / num17; } _cachedBorderWorldPoints.Add(new Vector2(num15, num16)); } } private void DrawWorldEventMarkers() { //IL_0038: Unknown result type (might be due to invalid IL or missing references) //IL_0048: Unknown result type (might be due to invalid IL or missing references) //IL_00c4: 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_00fd: Unknown result type (might be due to invalid IL or missing references) //IL_0107: Unknown result type (might be due to invalid IL or missing references) //IL_010f: Unknown result type (might be due to invalid IL or missing references) //IL_0119: Expected O, but got Unknown //IL_0119: Unknown result type (might be due to invalid IL or missing references) //IL_0134: Unknown result type (might be due to invalid IL or missing references) //IL_013f: Expected O, but got Unknown //IL_027d: Unknown result type (might be due to invalid IL or missing references) //IL_0277: Unknown result type (might be due to invalid IL or missing references) //IL_0188: Unknown result type (might be due to invalid IL or missing references) //IL_0175: 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_0186: Expected O, but got Unknown //IL_01c5: Unknown result type (might be due to invalid IL or missing references) //IL_01d7: Unknown result type (might be due to invalid IL or missing references) //IL_020a: Unknown result type (might be due to invalid IL or missing references) //IL_0214: Unknown result type (might be due to invalid IL or missing references) //IL_021c: Unknown result type (might be due to invalid IL or missing references) //IL_0226: Expected O, but got Unknown //IL_0226: Unknown result type (might be due to invalid IL or missing references) //IL_0230: Unknown result type (might be due to invalid IL or missing references) //IL_0241: Unknown result type (might be due to invalid IL or missing references) //IL_024b: Unknown result type (might be due to invalid IL or missing references) WarfareData warfareData = WarfareManager.Instance?.Data; if (warfareData?.WorldEvents == null || warfareData.WorldEvents.Count == 0) { return; } EnsureEventMarkerStyles(); WorldEvent worldEvent = null; Vector2 val = default(Vector2); ((Vector2)(ref val))..ctor(Input.mousePosition.x, (float)Screen.height - Input.mousePosition.y); Rect val3 = default(Rect); Rect val4 = default(Rect); foreach (WorldEvent item in new List(warfareData.WorldEvents)) { if (item.Status != WorldEventStatus.Active && item.Status != WorldEventStatus.Pending) { continue; } Vector2? val2 = WorldToScreenGUI(item.Position.x, item.Position.z); if (val2.HasValue) { float num = 34f; ((Rect)(ref val3))..ctor(val2.Value.x - num * 0.5f, val2.Value.y - num * 0.5f, num, num); GUI.color = EventMarkerColor(item.EventType); GUI.DrawTexture(val3, (Texture)_pixelTex); GUI.color = Color.white; Texture2D worldEventTexture = GetWorldEventTexture(item.EventType); if ((Object)worldEventTexture != (Object)null) { GUI.DrawTexture(new Rect(((Rect)(ref val3)).x + 4f, ((Rect)(ref val3)).y + 4f, ((Rect)(ref val3)).width - 8f, ((Rect)(ref val3)).height - 8f), (Texture)worldEventTexture); } else { GUI.Label(val3, WorldEventMapSymbol(item.EventType), _eventMarkerStyle); } string text = ShortMarkerLabel(item.EventName ?? WarfareManager.WorldEventTypeName(item.EventType)); ((Rect)(ref val4))..ctor(val2.Value.x - 75f, val2.Value.y + 26f, 150f, 34f); GUI.color = new Color(0.04f, 0.03f, 0.02f, 0.72f); GUI.DrawTexture(val4, (Texture)_pixelTex); GUI.color = Color.white; GUI.Label(val4, text, _eventMarkerLabelStyle); if (((Rect)(ref val3)).Contains(val) || ((Rect)(ref val4)).Contains(val)) { worldEvent = item; } } } if (worldEvent != null) { DrawWorldEventHover(worldEvent, val); } GUI.color = Color.white; } private void DrawWorldEventHover(WorldEvent worldEvent, Vector2 mouse) { //IL_0007: Unknown result type (might be due to invalid IL or missing references) //IL_0012: Expected O, but got Unknown //IL_0021: Unknown result type (might be due to invalid IL or missing references) //IL_0027: 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) //IL_0088: 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_010b: Unknown result type (might be due to invalid IL or missing references) //IL_0115: Unknown result type (might be due to invalid IL or missing references) //IL_011d: Unknown result type (might be due to invalid IL or missing references) //IL_0127: Expected O, but got Unknown //IL_0127: Unknown result type (might be due to invalid IL or missing references) //IL_015d: Unknown result type (might be due to invalid IL or missing references) //IL_00de: Unknown result type (might be due to invalid IL or missing references) //IL_01ae: Unknown result type (might be due to invalid IL or missing references) //IL_01ff: Unknown result type (might be due to invalid IL or missing references) //IL_024c: Unknown result type (might be due to invalid IL or missing references) //IL_0299: Unknown result type (might be due to invalid IL or missing references) //IL_02e0: Unknown result type (might be due to invalid IL or missing references) Player localPlayer = Player.m_localPlayer; float distance = (((Object)localPlayer != (Object)null) ? Vector3.Distance(((Component)localPlayer).transform.position, worldEvent.Position) : (-1f)); long seconds = Math.Max(0L, worldEvent.EndTime - DateTimeOffset.UtcNow.ToUnixTimeSeconds()); string text = (string.IsNullOrWhiteSpace(worldEvent.RewardSummary) ? (worldEvent.RewardCoins + " coins") : worldEvent.RewardSummary); Rect val = default(Rect); ((Rect)(ref val))..ctor(mouse.x + 18f, mouse.y + 18f, 270f, 138f); if (((Rect)(ref val)).xMax > (float)Screen.width) { ((Rect)(ref val)).x = mouse.x - ((Rect)(ref val)).width - 18f; } if (((Rect)(ref val)).yMax > (float)Screen.height) { ((Rect)(ref val)).y = mouse.y - ((Rect)(ref val)).height - 18f; } GUI.color = new Color(0.04f, 0.03f, 0.02f, 0.9f); GUI.DrawTexture(val, (Texture)_pixelTex); GUI.color = Color.white; GUI.Label(new Rect(((Rect)(ref val)).x + 10f, ((Rect)(ref val)).y + 8f, ((Rect)(ref val)).width - 20f, 22f), worldEvent.EventName ?? WarfareManager.WorldEventTypeName(worldEvent.EventType), _tooltipTitleStyle); GUI.Label(new Rect(((Rect)(ref val)).x + 10f, ((Rect)(ref val)).y + 34f, ((Rect)(ref val)).width - 20f, 20f), "Type: " + WarfareManager.WorldEventTypeName(worldEvent.EventType), _tooltipDetailStyle); GUI.Label(new Rect(((Rect)(ref val)).x + 10f, ((Rect)(ref val)).y + 54f, ((Rect)(ref val)).width - 20f, 20f), "Distance: " + FormatMapDistance(distance), _tooltipDetailStyle); GUI.Label(new Rect(((Rect)(ref val)).x + 10f, ((Rect)(ref val)).y + 74f, ((Rect)(ref val)).width - 20f, 20f), "Time: " + FormatMapDuration(seconds), _tooltipDetailStyle); GUI.Label(new Rect(((Rect)(ref val)).x + 10f, ((Rect)(ref val)).y + 94f, ((Rect)(ref val)).width - 20f, 20f), "Reward: " + text, _tooltipDetailStyle); GUI.Label(new Rect(((Rect)(ref val)).x + 10f, ((Rect)(ref val)).y + 114f, ((Rect)(ref val)).width - 20f, 20f), "Status: " + worldEvent.Status, _tooltipDetailStyle); } private string FormatMapDistance(float distance) { if (distance < 0f) { return "Unknown"; } if (distance >= 1000f) { return (distance / 1000f).ToString("0.0") + " km"; } return Mathf.RoundToInt(distance) + " m"; } private string FormatMapDuration(long seconds) { seconds = Math.Max(0L, seconds); long num = seconds / 3600; long num2 = seconds % 3600 / 60; return num.ToString("00") + "h " + num2.ToString("00") + "m"; } private void EnsureEventMarkerStyles() { //IL_0013: Unknown result type (might be due to invalid IL or missing references) //IL_001d: Expected O, but got Unknown //IL_0059: 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_0080: Expected O, but got Unknown //IL_00d7: Unknown result type (might be due to invalid IL or missing references) if (_eventMarkerStyle == null) { _eventMarkerStyle = new GUIStyle(GUI.skin.label); _eventMarkerStyle.alignment = (TextAnchor)4; _eventMarkerStyle.fontStyle = (FontStyle)1; _eventMarkerStyle.fontSize = 15; _eventMarkerStyle.richText = true; _eventMarkerStyle.normal.textColor = Color.black; } if (_eventMarkerLabelStyle == null) { _eventMarkerLabelStyle = new GUIStyle(GUI.skin.label); _eventMarkerLabelStyle.alignment = (TextAnchor)4; _eventMarkerLabelStyle.fontStyle = (FontStyle)1; _eventMarkerLabelStyle.fontSize = 11; _eventMarkerLabelStyle.richText = true; _eventMarkerLabelStyle.wordWrap = true; _eventMarkerLabelStyle.normal.textColor = new Color(1f, 0.86f, 0.35f); } } private string ShortMarkerLabel(string label) { if (string.IsNullOrWhiteSpace(label)) { return "World Event"; } label = label.Trim(); if (label.Length > 24) { return label.Substring(0, 21) + "..."; } return label; } private Texture2D GetWorldEventTexture(WorldEventType type) { if (_eventTextures == null) { LoadWorldEventTextures(); } if (_eventTextures != null && _eventTextures.TryGetValue(WorldEventIconKey(type), out var value)) { return value; } return null; } private void LoadWorldEventTextures() { //IL_004a: Unknown result type (might be due to invalid IL or missing references) //IL_0051: Expected O, but got Unknown _eventTextures = new Dictionary(StringComparer.OrdinalIgnoreCase); string path = Path.Combine(RuntimeAssetRoot, "Events"); if (!Directory.Exists(path)) { return; } string[] files = Directory.GetFiles(path, "*.png", SearchOption.TopDirectoryOnly); foreach (string text in files) { try { byte[] array = File.ReadAllBytes(text); Texture2D val = new Texture2D(2, 2, (TextureFormat)4, false); if (ImageConversion.LoadImage(val, array)) { ((Object)val).name = Path.GetFileNameWithoutExtension(text); _eventTextures[((Object)val).name] = val; } } catch (Exception ex) { Plugin.Log.LogWarning((object)("[FCW Events] Failed to load event map image: " + text + " " + ex.Message)); } } } private string WorldEventIconKey(WorldEventType type) { return type switch { WorldEventType.BanditCamp => "bandit_camp", WorldEventType.AncientShrine => "ancient_shrine", WorldEventType.ResourceRush => "resource_rush", WorldEventType.CorruptedTerritory => "corrupted_territory", WorldEventType.LostCaravan => "lost_caravan", WorldEventType.WorldBoss => "world_boss", WorldEventType.OdinChallenge => "odin_challenge", WorldEventType.ClanChallenge => "clan_challenge", WorldEventType.FreebornOpportunity => "freeborn_opportunity", _ => "default", }; } private Color EventMarkerColor(WorldEventType type) { //IL_0043: Unknown result type (might be due to invalid IL or missing references) //IL_005d: Unknown result type (might be due to invalid IL or missing references) //IL_0077: Unknown result type (might be due to invalid IL or missing references) //IL_0091: Unknown result type (might be due to invalid IL or missing references) //IL_00ab: Unknown result type (might be due to invalid IL or missing references) //IL_00c5: Unknown result type (might be due to invalid IL or missing references) //IL_00df: Unknown result type (might be due to invalid IL or missing references) //IL_00f9: Unknown result type (might be due to invalid IL or missing references) //IL_0113: 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) return (Color)(type switch { WorldEventType.BanditCamp => new Color(1f, 0.45f, 0.15f, 0.9f), WorldEventType.AncientShrine => new Color(0.65f, 0.25f, 1f, 0.9f), WorldEventType.ResourceRush => new Color(0.3f, 1f, 0.45f, 0.9f), WorldEventType.CorruptedTerritory => new Color(1f, 0.15f, 0.15f, 0.9f), WorldEventType.LostCaravan => new Color(1f, 0.82f, 0.2f, 0.9f), WorldEventType.WorldBoss => new Color(0.45f, 0.02f, 0.02f, 0.95f), WorldEventType.OdinChallenge => new Color(0.15f, 0.45f, 1f, 0.9f), WorldEventType.ClanChallenge => new Color(1f, 0.7f, 0.05f, 0.9f), WorldEventType.FreebornOpportunity => new Color(1f, 1f, 1f, 0.9f), _ => new Color(1f, 1f, 1f, 0.9f), }); } private string WorldEventMapSymbol(WorldEventType type) { return type switch { WorldEventType.BanditCamp => "B", WorldEventType.AncientShrine => "A", WorldEventType.ResourceRush => "R", WorldEventType.CorruptedTerritory => "C", WorldEventType.LostCaravan => "L", WorldEventType.WorldBoss => "W", WorldEventType.OdinChallenge => "O", WorldEventType.ClanChallenge => "K", WorldEventType.FreebornOpportunity => "F", _ => "?", }; } private void DrawCellTooltip(Vector2i cell) { //IL_0002: Unknown result type (might be due to invalid IL or missing references) //IL_000a: Unknown result type (might be due to invalid IL or missing references) //IL_001a: 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_0033: Unknown result type (might be due to invalid IL or missing references) //IL_0052: Unknown result type (might be due to invalid IL or missing references) //IL_0060: Unknown result type (might be due to invalid IL or missing references) //IL_006b: Expected O, but got Unknown //IL_00a6: Unknown result type (might be due to invalid IL or missing references) //IL_0078: Unknown result type (might be due to invalid IL or missing references) //IL_007d: Unknown result type (might be due to invalid IL or missing references) //IL_0082: Unknown result type (might be due to invalid IL or missing references) //IL_0084: Unknown result type (might be due to invalid IL or missing references) //IL_008b: 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_009a: 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_03a3: Unknown result type (might be due to invalid IL or missing references) //IL_03b7: Unknown result type (might be due to invalid IL or missing references) //IL_0426: Unknown result type (might be due to invalid IL or missing references) //IL_0446: Unknown result type (might be due to invalid IL or missing references) //IL_0469: Unknown result type (might be due to invalid IL or missing references) //IL_01fc: Unknown result type (might be due to invalid IL or missing references) //IL_0495: Unknown result type (might be due to invalid IL or missing references) Vector2 value = default(Vector2); ((Vector2)(ref value))..ctor(Input.mousePosition.x, (float)Screen.height - Input.mousePosition.y); Vector2? val = value; IsCellExplored(cell); ClanTerritory clanTerritory = WarfareManager.Instance?.Data?.GetTerritory(cell); bool flag = false; if ((Object)Player.m_localPlayer != (Object)null) { Vector2i val2 = WorldToGridCell(((Component)Player.m_localPlayer).transform.position); flag = val2.x == cell.x && val2.y == cell.y; } string text; string text2; if (ProtectedWorldZones.TryGetProtectedZone(cell, out var info)) { text = "" + info.Name + ""; text2 = "Owner: WORLD\nProtected World Zone\nCannot be claimed."; } else if (clanTerritory != null && clanTerritory.FreebornOwnerId != 0L) { string text3 = ColorUtility.ToHtmlStringRGB(ClanConfig.GetFreebornBorderColor(clanTerritory)); string[] obj = new string[6] { "", (clanTerritory.Type == TerritoryType.Homestead) ? "Freeborn Homestead" : "Freeborn Outpost", null, null }; ConfigEntry showFreebornOwnerName = ClanConfig.ShowFreebornOwnerName; obj[4] = ((showFreebornOwnerName != null && showFreebornOwnerName.Value) ? (" of " + (clanTerritory.FreebornOwnerName ?? "Freeborn")) : ""); obj[5] = ""; text = string.Concat(obj); text2 = "" + clanTerritory.DisplayName + ""; ConfigEntry showOdinsPeaceOnTooltip = ClanConfig.ShowOdinsPeaceOnTooltip; if (showOdinsPeaceOnTooltip != null && showOdinsPeaceOnTooltip.Value && clanTerritory.OdinsPeaceActive) { text2 += "\nOdin's Peace: Active"; } } else if (clanTerritory != null && clanTerritory.OwnerClanId != 0) { Clan clan = ClanConfig.GetClan(clanTerritory.OwnerClanId); string text4 = ((clan != null) ? ColorUtility.ToHtmlStringRGB(clan.Color) : "888888"); string displayName = clanTerritory.DisplayName; text = "" + clan?.Symbol + " " + displayName + ""; WarfareManager instance = WarfareManager.Instance; object obj2; if ((Object)(object)instance == (Object)null) { obj2 = null; } else { WarfareData data = instance.Data; if (data == null) { obj2 = null; } else { Player localPlayer = Player.m_localPlayer; obj2 = data.GetPlayer(((Object)(object)localPlayer != (Object)null) ? localPlayer.GetPlayerID() : 0); } } PlayerClanInfo playerClanInfo = (PlayerClanInfo)obj2; text2 = ((playerClanInfo != null && playerClanInfo.ClanId == clanTerritory.OwnerClanId) ? ("HOME — " + clan?.Name + "\nClaimed by " + clanTerritory.ClaimedByPlayerName + "") : ("ENEMY — " + clan?.Name + "\nClaimed by " + clanTerritory.ClaimedByPlayerName + "")); if (clanTerritory.Type == TerritoryType.Capital) { text2 = "Capital of " + (clan?.Name ?? "Clan") + "\n" + text2; } } else { text = "Unclaimed Territory"; text2 = "Click to view / claim"; } string text5 = (flag ? "You are here" : ""); float num = 260f; float num2 = 115f; float num3 = val.Value.x + 20f; float num4 = val.Value.y - num2 / 2f; num3 = Mathf.Clamp(num3, 10f, (float)Screen.width - num - 10f); num4 = Mathf.Clamp(num4, 10f, (float)Screen.height - num2 - 10f); GUI.Box(new Rect(num3 - 5f, num4 - 5f, num + 10f, num2 + 10f), "", _panelStyle); GUI.Label(new Rect(num3, num4, num, 22f), text, _tooltipTitleStyle); GUI.Label(new Rect(num3, num4 + 22f, num, 58f), text2, _tooltipDetailStyle); if (!string.IsNullOrEmpty(text5)) { GUI.Label(new Rect(num3, num4 + 78f, num, 22f), text5, _tooltipDetailStyle); } } private bool IsCellExplored(Vector2i cell) { return true; } private void DrawOwnedTerritoryInfo(ClanTerritory territory, Vector2i cell) { //IL_0007: Unknown result type (might be due to invalid IL or missing references) //IL_0008: Unknown result type (might be due to invalid IL or missing references) //IL_003a: Unknown result type (might be due to invalid IL or missing references) //IL_02cc: Unknown result type (might be due to invalid IL or missing references) //IL_0346: Unknown result type (might be due to invalid IL or missing references) //IL_0381: Unknown result type (might be due to invalid IL or missing references) Clan clan = ClanConfig.GetClan(territory.OwnerClanId); string text = clan?.Name ?? "Unknown"; string text2 = ((clan != null) ? ColorUtility.ToHtmlStringRGB(clan.Color) : "888888"); string text3 = (territory.IsCapital ? "★ Capital" : "Colony"); GUILayout.Label("" + clan?.Symbol + " " + text + " — " + text3, _headerStyle, Array.Empty()); GUILayout.Space(4f); GUILayout.Label($"Size {territory.SizeLevel} {Economy.ValueTierName(territory.BiomeValueTier)}-value " + $"Income {Economy.TerritoryIncome(territory)}/tick", _labelStyle, Array.Empty()); GUILayout.Label($"State: {territory.State} HP: {Mathf.RoundToInt(territory.HP)}/{Mathf.RoundToInt(territory.MaxHP)}", _labelStyle, Array.Empty()); if (territory.RotCount > 0) { GUILayout.Label($"Corruption: {Mathf.Clamp(territory.RotCount, 0, 4)}/4 — Root income / material production reduction: {Mathf.Clamp(territory.RotCount, 0, 4) * 25}%", _labelStyle, Array.Empty()); } WarfareData warfareData = WarfareManager.Instance?.Data; object obj; if (warfareData == null) { obj = null; } else { Player localPlayer = Player.m_localPlayer; obj = warfareData.GetPlayer(((Object)(object)localPlayer != (Object)null) ? localPlayer.GetPlayerID() : 0); } PlayerClanInfo playerClanInfo = (PlayerClanInfo)obj; bool flag = playerClanInfo != null && playerClanInfo.ClanId == territory.OwnerClanId; GUILayout.Label(flag ? "Your clan's territory" : "Enemy territory", _labelStyle, Array.Empty()); CaptureSession captureSession = warfareData?.ActiveCaptures?.Find((CaptureSession c) => c.TerritoryGridX == cell.x && c.TerritoryGridY == cell.y); if (captureSession != null) { GUILayout.Label($"Being captured by {ClanConfig.GetClan(captureSession.AttackerClanId)?.Name} — {captureSession.Progress01 * 100f:F0}%", _labelStyle, Array.Empty()); } GUILayout.Space(6f); if (territory.IsRotted && playerClanInfo != null && playerClanInfo.ClanId != 0) { if (GUILayout.Button("Seize this rotted territory", _buttonStyle, Array.Empty())) { WarfareRPC.Instance?.RequestClaimTerritory(cell); ClosePanel(); } } else if (flag && playerClanInfo.Rank >= ClanRank.Officer) { int num = ClanConfig.MaxTerritorySizeLevel?.Value ?? 5; if (territory.SizeLevel < num && GUILayout.Button($"Upgrade Size ({Economy.TerritoryUpgradeCost(territory.SizeLevel)} coins)", _buttonStyle, Array.Empty())) { WarfareRPC.Instance?.RequestUpgradeTerritory(cell); ClosePanel(); } if (!territory.IsCapital && GUILayout.Button("Set as Capital", _buttonStyle, Array.Empty())) { WarfareRPC.Instance?.RequestSetCapital(cell); ClosePanel(); } } else if (!flag) { GUILayout.Label("Declare war from the F3 panel, then capture this land by standing in it.", _labelStyle, Array.Empty()); } } private void DrawClaimPanel(Vector2i cell) { //IL_002e: Unknown result type (might be due to invalid IL or missing references) //IL_005f: Unknown result type (might be due to invalid IL or missing references) //IL_0081: Unknown result type (might be due to invalid IL or missing references) //IL_0138: 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_012f: Unknown result type (might be due to invalid IL or missing references) float num = 380f; float num2 = 350f; float num3 = ((float)Screen.width - num) / 2f; float num4 = ((float)Screen.height - num2) / 2f; GUI.Box(new Rect(num3, num4, num, num2), "", _panelStyle); GUILayout.BeginArea(new Rect(num3 + 15f, num4 + 10f, num - 30f, num2 - 20f)); ClanTerritory clanTerritory = WarfareManager.Instance?.Data?.GetTerritory(cell); GUILayout.BeginHorizontal(Array.Empty()); GUILayout.FlexibleSpace(); GUILayout.Label((clanTerritory != null && clanTerritory.OwnerClanId != 0) ? clanTerritory.DisplayName : "Territory", _headerStyle, Array.Empty()); GUILayout.FlexibleSpace(); if (GUILayout.Button("X", _closeButtonStyle, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(30f) })) { ClosePanel(); } GUILayout.EndHorizontal(); GUILayout.Space(15f); if (clanTerritory != null && clanTerritory.FreebornOwnerId != 0L) { DrawFreebornTerritoryInfo(clanTerritory, cell); } else if (clanTerritory != null && clanTerritory.OwnerClanId != 0) { DrawOwnedTerritoryInfo(clanTerritory, cell); } else { DrawUnclaimedTerritoryInfo(cell); } GUILayout.EndArea(); } private void DrawUnclaimedTerritoryInfo(Vector2i cell) { //IL_0000: 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_014e: Unknown result type (might be due to invalid IL or missing references) //IL_0153: Unknown result type (might be due to invalid IL or missing references) //IL_0155: 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_0164: Unknown result type (might be due to invalid IL or missing references) //IL_016b: Unknown result type (might be due to invalid IL or missing references) //IL_01cc: Unknown result type (might be due to invalid IL or missing references) if (ProtectedWorldZones.TryGetProtectedZone(cell, out var info)) { GUILayout.Label("" + info.Name + "", _headerStyle, Array.Empty()); GUILayout.Space(8f); GUILayout.Label("Owner: WORLD", _labelStyle, Array.Empty()); GUILayout.Label("Protected World Zone", _labelStyle, Array.Empty()); GUILayout.Label("Cannot be claimed, captured, sold, transferred, or used as clan/freeborn territory.", _labelStyle, Array.Empty()); return; } GUILayout.Label("Unclaimed Territory", _headerStyle, Array.Empty()); GUILayout.Space(10f); WarfareManager instance = WarfareManager.Instance; object obj; if ((Object)(object)instance == (Object)null) { obj = null; } else { WarfareData data = instance.Data; if (data == null) { obj = null; } else { Player localPlayer = Player.m_localPlayer; obj = data.GetPlayer(((Object)(object)localPlayer != (Object)null) ? localPlayer.GetPlayerID() : 0); } } PlayerClanInfo playerClanInfo = (PlayerClanInfo)obj; if (playerClanInfo == null || (playerClanInfo.ClanId == 0 && !playerClanInfo.IsFreeborn)) { GUILayout.Label("Join a clan or choose Freeborn first to claim territories.", _labelStyle, Array.Empty()); return; } if (!playerClanInfo.IsFreeborn && !playerClanInfo.CanClaimTerritory && playerClanInfo.Rank != ClanRank.Jarl) { GUILayout.Label("You do not have permission to claim clan territory.", _labelStyle, Array.Empty()); return; } Vector2i val = WorldToGridCell(((Component)Player.m_localPlayer).transform.position); if (val.x != cell.x || val.y != cell.y) { GUILayout.Label("You must be standing in this territory to claim it.", _labelStyle, Array.Empty()); return; } GUILayout.Space(10f); if (GUILayout.Button((playerClanInfo.IsFreeborn && playerClanInfo.ClanId == 0) ? "Claim as Freeborn" : "Claim for your Clan", _buttonStyle, Array.Empty())) { WarfareRPC.Instance?.RequestClaimTerritory(cell); ClosePanel(); } } private void DrawFreebornTerritoryInfo(ClanTerritory territory, Vector2i cell) { //IL_0156: Unknown result type (might be due to invalid IL or missing references) //IL_0161: Expected O, but got Unknown //IL_01e8: Unknown result type (might be due to invalid IL or missing references) GUILayout.Label("Freeborn " + territory.Type.ToString() + " of " + (territory.FreebornOwnerName ?? "Freeborn") + " - " + territory.DisplayName, _headerStyle, Array.Empty()); GUILayout.Space(4f); GUILayout.Label(string.Format("Owner: {0} Size {1} {2}-value", territory.FreebornOwnerName ?? "Freeborn", territory.SizeLevel, Economy.ValueTierName(territory.BiomeValueTier)), _labelStyle, Array.Empty()); GUILayout.Label($"State: {territory.State} HP: {Mathf.RoundToInt(territory.HP)}/{Mathf.RoundToInt(territory.MaxHP)}", _labelStyle, Array.Empty()); if (territory.Type == TerritoryType.Homestead) { string text = ((!territory.OdinsPeaceActive) ? "Inactive" : ((territory.OdinsPeaceChallengeEndTimestamp > 0) ? "Challenged" : "Active")); GUILayout.Label("Odin's Peace: " + text, _labelStyle, Array.Empty()); } WarfareData warfareData = WarfareManager.Instance?.Data; Player localPlayer = Player.m_localPlayer; PlayerClanInfo playerClanInfo = ((warfareData != null && (Object)localPlayer != (Object)null) ? warfareData.GetPlayer(localPlayer.GetPlayerID()) : null); if (playerClanInfo != null && playerClanInfo.IsFreeborn && playerClanInfo.ClanId == 0 && territory.FreebornOwnerId == playerClanInfo.PlayerId) { int num = ClanConfig.MaxTerritorySizeLevel?.Value ?? 5; if (territory.SizeLevel < num && GUILayout.Button($"Upgrade Size ({Economy.TerritoryUpgradeCost(territory.SizeLevel)} coins)", _buttonStyle, Array.Empty())) { WarfareRPC.Instance?.RequestUpgradeTerritory(cell); ClosePanel(); } } } private void TryClaimTerritory(Vector2i cell) { //IL_000b: Unknown result type (might be due to invalid IL or missing references) WarfareRPC.Instance?.RequestClaimTerritory(cell); ClosePanel(); } } public class WarfareHUD : MonoBehaviour { private static class ForgedPalette { public static readonly Color WindowBackground = C(8, 11, 11); public static readonly Color ContentBackground = C(12, 18, 19); public static readonly Color PanelNeutral = C(20, 28, 30); public static readonly Color DarkSteel = C(24, 34, 37); public static readonly Color SteelHover = C(25, 37, 41); public static readonly Color SteelPressed = C(11, 17, 19); public static readonly Color SteelDisabled = C(21, 23, 25); public static readonly Color DisabledText = C(98, 105, 106); public static readonly Color BronzeDark = C(76, 59, 37); public static readonly Color Bronze = C(112, 85, 47); public static readonly Color BronzeBright = C(146, 113, 61); public static readonly Color GoldDark = C(140, 103, 42); public static readonly Color Gold = C(197, 154, 67); public static readonly Color GoldBright = C(226, 192, 107); public static readonly Color GoldSelected = C(75, 57, 27); public static readonly Color SilverDark = C(89, 100, 104); public static readonly Color Silver = C(130, 144, 149); public static readonly Color SilverBright = C(174, 184, 185); public static readonly Color MithrilDark = C(11, 29, 36); public static readonly Color Mithril = C(16, 38, 46); public static readonly Color MithrilBorder = C(102, 139, 151); public static readonly Color MithrilBright = C(131, 165, 174); public static readonly Color WarpstoneDark = C(10, 32, 25); public static readonly Color Warpstone = C(13, 42, 32); public static readonly Color WarpstoneBorder = C(66, 107, 84); public static readonly Color WarpstoneText = C(99, 198, 61); public static readonly Color WarpstoneMuted = C(79, 161, 59); public static readonly Color MeteoriteDark = C(33, 17, 18); public static readonly Color Meteorite = C(42, 21, 22); public static readonly Color MeteoriteBorder = C(147, 72, 66); public static readonly Color MeteoriteBright = C(180, 90, 80); public static readonly Color MeteoriteText = C(224, 75, 62); public static readonly Color VioletDark = C(29, 20, 33); public static readonly Color Violet = C(40, 26, 45); public static readonly Color VioletBorder = C(115, 80, 105); public static readonly Color VioletBright = C(149, 103, 131); public static readonly Color PinkDark = C(136, 62, 90); public static readonly Color PinkMuted = C(182, 83, 119); public static readonly Color PinkBright = C(207, 109, 145); public static readonly Color IvoryText = C(216, 200, 168); public static readonly Color ParchmentText = C(184, 165, 133); public static readonly Color DimText = C(129, 118, 101); public static readonly Color MajorHeading = C(197, 154, 67); public static readonly Color WindowTitle = C(226, 192, 107); public static readonly Color SpecialText = C(181, 138, 168); private static Color C(byte r, byte g, byte b) { //IL_0008: Unknown result type (might be due to invalid IL or missing references) //IL_000d: Unknown result type (might be due to invalid IL or missing references) return Color32.op_Implicit(new Color32(r, g, b, byte.MaxValue)); } } private enum UiTone { Neutral, Info, Positive, Cost, Special } private enum UiTextRole { WindowTitle, MajorSectionTitle, CardTitle, OrdinaryLabel, BodyText, SecondaryText, InformationalValue, PositiveValue, NegativeValue, WarningValue, SpecialValue, DisabledText, ActiveTabText, InactiveTabText, ButtonText, PrimaryButtonText } private sealed class LeftNotification { public string Message; public string Key; public float CreatedAt; public int RepeatCount = 1; } private sealed class ResolvedItemIcon { public readonly Texture2D Texture; public readonly Rect Uv; public readonly float Aspect; public ResolvedItemIcon(Texture2D texture, Rect uv, float aspect) { //IL_000e: 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) Texture = texture; Uv = uv; Aspect = ((aspect > 0f) ? aspect : 1f); } } private struct GameDashboardNumbers { public int ActivePlayers; public int ActiveGames; public int YourGames; public int YourWins; public int BestStreak; } private sealed class VaultInventoryEntry { public readonly string PrefabName; public readonly string DisplayName; public readonly int Amount; public readonly int SlotX; public readonly int SlotY; public readonly Sprite Icon; public VaultInventoryEntry(string prefabName, string displayName, int amount, int slotX, int slotY, Sprite icon) { PrefabName = prefabName; DisplayName = displayName; Amount = amount; SlotX = slotX; SlotY = slotY; Icon = icon; } } private sealed class RoleData { public ClanRole Role; public string DisplayName; public string Group; public string IconName; public string Lore; public string CoreAbility; public string PassiveBonuses; public string ClanValue; public bool Selectable; } private bool _isVisible; private Rect _windowRect; private int _windowId; private int _tab; private int _freebornTab; private bool _stylesInit; private Vector2 _scrollA; private Vector2 _scrollB; private Vector2 _scrollDirectoryFreeborn; private Vector2 _scrollDirectoryClans; private Vector2 _scrollIds; private Vector2 _scrollFreebornTerritory; private Vector2 _overviewScroll; private Vector2 _profileScroll; private Vector2 _overviewEventsScroll; private string _profileAboutDraft = ""; private int _profileImagePage; private int _profileImageFilter; private bool _profileImageExpanded; private bool _realmStatusExpanded; private int _profileTitleFilter; private Vector2 _profileTitlesScroll; private int _profileAchievementFilter; private Vector2 _profileAchievementsScroll; private PlayerClanInfo _viewedProfile; private Clan _viewedPublicClan; private string _profileBackLabel = ""; private int _profileBackTab = 1; private int _profileBackSubTab; private Vector2 _profileBackScroll; private string _allPlayersSearch = ""; private int _allPlayersFilter; private int _allPlayersSort; private Vector2 _allPlayersScroll; private readonly HashSet _allPlayersConflictLogs = new HashSet(StringComparer.Ordinal); private int _profileSagaFilter; private Vector2 _profileSagaScroll; private int _lastScreenWidth; private int _lastScreenHeight; private float _lastUiScale; private Dictionary _inventoryTextures; private List _profilePortraitKeys; private string _profilePortraitRoot = ""; private readonly Dictionary _profilePageTextures = new Dictionary(StringComparer.OrdinalIgnoreCase); private readonly HashSet _profilePortraitFailureLogs = new HashSet(StringComparer.OrdinalIgnoreCase); private string _profilePageCacheSignature = ""; private string _selectedProfileTextureKey = ""; private Texture2D _selectedProfileTexture; private int _profilePortraitCount; private string _lastProfileUiRecordLog = ""; private Dictionary _resolvedInventoryIcons = new Dictionary(StringComparer.OrdinalIgnoreCase); private Vector2 _scrollInventoryVault; private Vector2 _scrollVaultContents; private Vector2 _scrollFreebornVaultContents; private Vector2 _scrollRole; private Vector2 _scrollRolePreview; private Vector2 _adminScroll; private string _renameTarget; private string _renameInput = ""; private string _bountyInput = "100"; private int _bountyTargetClan; private Vector2 _bountyActiveScroll; private Vector2 _bountyTargetScroll; private Vector2 _warActiveScroll; private Vector2 _warClanScroll; private Vector2 _pendingTerritoryDealScroll; private int _expandedDiplomacyClanId; private int _warSubTab; private int _membersSubTab; private int _infrastructureSubTab; private int _portalRankIndex = 2; private Rect _controlsRect; private GUIStyle _controlsTextStyle; private bool _controlsDragging; private Vector2 _controlsDragOffset; private Rect _notificationsRect; private bool _notificationsDragging; private Vector2 _notificationsDragOffset; private float _nextElectionDebugLog; private int _kingdomsSubTab; private int _guardsSubTab; private int _selectedGuardTerritoryIndex; private int _selectedGuardTypeIndex; private Vector2 _guardTerritoryScroll; private Vector2 _guardLogScroll; private string _allianceNameInput = ""; private string _territoryDealPriceInput = "0"; private int _territoryDealTerritoryIndex; private int _territoryDealClanIndex; private Vector2 _buildingsTerritoryScroll; private Vector2 _buildingsDetailScroll; private int _selectedBuildingTerritoryIndex; private TerritoryBuildingType _selectedBuildType = TerritoryBuildingType.LumberCamp; private int _selectedBuildingCategory = 2; private bool _territoryDealSell; private bool _territoryDealTerritoryOpen; private bool _territoryDealClanOpen; private string _freebornFriendTarget = ""; private string _foundClanInviteTargetA = ""; private string _foundClanInviteTargetB = ""; private string _emblemUrlInput = ""; private int _emblemUrlClanId; private bool _editingClanEmblem; private bool _editingClanLinks; private bool _editingBannerColors; private int _selectedBannerColorIndex = -1; private int _inventoryVaultPage; private int _clanVaultPage; private bool _sortInventoryVaultDisplay; private bool _sortClanVaultDisplay; private bool _loggedAllIdsEmpty; private int _emblemCategoryIndex; private int _emblemPage; private string _pendingClanEmblemName = ""; private string _clanDiscordInput = ""; private string _clanWebsiteInput = ""; private string _clanSteamGroupInput = ""; private string _clanCustomUrlInput = ""; private string _clanCustomLabelInput = ""; private string _clanCustomUrl2Input = ""; private string _clanCustomLabel2Input = ""; private int _linksClanId; private int _allIdsSection; private string _vaultCoinAmountInput = "100"; private string _selectedInventoryVaultPrefab = ""; private string _selectedInventoryVaultName = ""; private int _selectedInventoryVaultStack; private int _selectedInventoryVaultSlotX = int.MinValue; private int _selectedInventoryVaultSlotY = int.MinValue; private string _vaultItemAmountInput = "1"; private int _vaultItemQuantityStep = 1; private string _selectedVaultPrefab = ""; private string _vaultWithdrawAmountInput = "1"; private int _vaultWithdrawQuantityStep = 1; private bool _confirmAbandonHomestead; private bool _confirmLeaveFreeborn; private bool _adminConfirmDanger; private bool _adminPasswordUnlocked; private string _adminPasswordInput = ""; private bool _adminPasswordDenied; private int _adminSubTab; private int _adminPvpSubTab; private string _pvpWebhookInput = ""; private string _pvpCombatDurationInput = ""; private HashSet _realmLawCollapsed = new HashSet(StringComparer.OrdinalIgnoreCase); private Dictionary _realmLawInputs = new Dictionary(StringComparer.OrdinalIgnoreCase); private string _adminLastMessage = ""; private float _adminLastMessageUntil; private bool _adminActionPending; private string _adminSteamIdInput = ""; private string _adminNicknameInput = ""; private string _adminRoleInput = ""; private string _constellationSteamIdInput = ""; private string _constellationNicknameInput = ""; private int _constellationBadgeIndex; private ClanRole _selectedRolePreview = ClanRole.Explorer; private int _selectedRolePreviewIndex; private int _contractsTab; private int _overviewSubTab; private int _profileSubTab; private int _territorySubTab; private int _economySubTab; private Vector2 _economyReportsScroll; private int _rankingsSubTab; private int _clansSubTab; private int _homesteadsSubTab; private int _historySubTab; private int _faqSubTab; private int _sagaSubTab; private int _gamesSubTab; private int _lastGamesSubTab; private int _gameHallSubTab; private int _gameRankingType; private int _gameRankingLimitIndex; private string _diceTableName = "Dice Table"; private string _diceStakeInput = "0"; private int _diceStakeTypeIndex; private int _diceStakeItemIndex; private int _gameHistoryFilter; private int _gameHistoryPage; private string _chessMatchName = "Chess Match"; private string _chessStakeInput = "0"; private string _chessStakePrefabInput = ""; private int _chessStakeTypeIndex; private int _chessStakeItemIndex; private int _chessStakeTerritoryIndex; private int _chessColorPreference; private int _chessTimerIndex; private string _chessCustomMinutes = "20"; private char _chessPromotion = 'Q'; private bool _chessPrivate; private bool _chessSpectators = true; private int _selectedChessGameId; private int _selectedChessSquare = -1; private int _selectedDiceTableId; private string _pokerTableName = "Texas Hold'em"; private string _pokerBuyInInput = "100"; private string _pokerSmallBlindInput = "1"; private string _pokerBigBlindInput = "2"; private string _pokerRaiseInput = "0"; private int _pokerHandLogPage; private int _pokerHandLogSeenCount; private int _pokerPlayerLimit = 6; private bool _pokerPrivate; private bool _pokerSpectators = true; private int _selectedPokerGameId; private int _diceModeIndex; private int _dicePlayerLimit = 2; private int _diceCount = 1; private bool _dicePrivate; private bool _diceSpectators = true; private string _adminConfirmAction = ""; private string _adminGamePlayerIdInput = ""; private string _adminGameIdInput = ""; private int _territoryDealMode; private string _exchangeYourTerritories = ""; private string _exchangeTheirTerritories = ""; private string _exchangeYourCoins = "0"; private string _exchangeTheirCoins = "0"; private string _exchangeYourResources = ""; private string _exchangeTheirResources = ""; private GUIStyle _windowStyle; private GUIStyle _labelStyle; private GUIStyle _labelGoldStyle; private GUIStyle _windowTitleStyle; private GUIStyle _majorSectionTitleStyle; private GUIStyle _cardTitleStyle; private GUIStyle _bodyTextStyle; private GUIStyle _secondaryTextStyle; private GUIStyle _informationalValueStyle; private GUIStyle _positiveValueStyle; private GUIStyle _negativeValueStyle; private GUIStyle _warningValueStyle; private GUIStyle _specialValueStyle; private GUIStyle _disabledTextStyle; private GUIStyle _buttonStyle; private GUIStyle _smallButtonStyle; private GUIStyle _inputFieldStyle; private GUIStyle _inputAreaStyle; private GUIStyle _toggleControlStyle; private GUIStyle _scrollbarStyle; private GUIStyle _scrollbarThumbStyle; private GUIStyle _tabStyle; private GUIStyle _tabActiveStyle; private GUIStyle _boxStyle; private GUIStyle _infoPanelStyle; private GUIStyle _positivePanelStyle; private GUIStyle _costPanelStyle; private GUIStyle _specialPanelStyle; private GUIStyle _primaryButtonStyle; private GUIStyle _infoButtonStyle; private GUIStyle _specialButtonStyle; private GUIStyle _pinkButtonStyle; private GUIStyle _dangerButtonStyle; private GUIStyle _disabledButtonStyle; private Texture2D _contentTex; private Texture2D _infoPanelTex; private Texture2D _positivePanelTex; private Texture2D _costPanelTex; private Texture2D _specialPanelTex; private Texture2D _pinkButtonTex; private Texture2D _pinkButtonHoverTex; private Texture2D _primaryButtonTex; private Texture2D _primaryButtonHoverTex; private Texture2D _dangerButtonTex; private Texture2D _dangerButtonHoverTex; private Texture2D _disabledButtonTex; private Texture2D _panelBg; private Texture2D _btnTex; private Texture2D _btnActiveTex; private Texture2D _boxTex; private Texture2D _pixelTex; private Texture2D _chessLightSquareTex; private Texture2D _chessDarkSquareTex; private Texture2D _chessSelectedOverlayTex; private Texture2D _chessLegalOverlayTex; private GUIStyle _chessLightSquareStyle; private GUIStyle _chessDarkSquareStyle; private Dictionary _chessPieceTextures; private Dictionary _pokerCardTextures; private Texture2D _pokerTableTexture; private Dictionary _pokerUiTextures; private Dictionary _gameIconTextures; private Dictionary _constellationBadgeTextures; private Dictionary _constellationSmallBadgeTextures; private Dictionary _biomeTextures; private Dictionary _roleTextures; private Dictionary _buildingTextures; private Dictionary _eventTextures; private readonly List _leftNotifications = new List(); private bool _notificationSubscribed; private bool _fcwMessagesEnabled = true; private bool _fcwMessagesSticky; private static readonly string[] Tabs = new string[20] { "Overview", "Profile", "Members", "Territories", "Economy", "Warfare", "Infrastructure", "Kingdoms", "Guards", "Contracts", "Rankings", "Clans", "Homesteads", "All IDs", "NPC", "History", "FAQ / Q&A", "Saga", "Games", "Admin" }; private static readonly string[] FreebornTabs = new string[13] { "Overview", "Profile", "Territories", "Economy", "Warfare", "Contracts", "Rankings", "Homesteads", "All IDs", "History", "FAQ / Q&A", "Saga", "Games" }; private static readonly WorldEventType[] AdminWorldEventTypes = new WorldEventType[9] { WorldEventType.BanditCamp, WorldEventType.AncientShrine, WorldEventType.ResourceRush, WorldEventType.CorruptedTerritory, WorldEventType.LostCaravan, WorldEventType.WorldBoss, WorldEventType.OdinChallenge, WorldEventType.ClanChallenge, WorldEventType.FreebornOpportunity }; private const float MainPanelWidth = 1380f; private const float MainPanelHeight = 980f; private static readonly RoleData[] RoleDefinitions = new RoleData[16] { new RoleData { Role = ClanRole.Explorer, DisplayName = "Pathfinder", Group = "Territory", IconName = "pathfinder", Lore = "Explorer of the kingdom.", CoreAbility = "Reveal a wider area of the map while exploring.", PassiveBonuses = "Map exploration radius is doubled at full effectiveness. Role effectiveness scales by slot.", ClanValue = "Scouting, discovery and kingdom knowledge.", Selectable = true }, new RoleData { Role = ClanRole.Warden, DisplayName = "Warden", Group = "Territory", IconName = "warden", Lore = "Keeper of kingdom lands.", CoreAbility = "Claim neutral territory and manage land.", PassiveBonuses = "Territory upgrade cost -15% at full effectiveness.", ClanValue = "Administration and stability.", Selectable = true }, new RoleData { Role = ClanRole.Raider, DisplayName = "Raider", Group = "Warfare", IconName = "raider", Lore = "Offensive warfare specialist.", CoreAbility = "Fight on enemy territory during official war.", PassiveBonuses = "Damage +8%; combat stamina cost -10%.", ClanValue = "Offensive pressure.", Selectable = true }, new RoleData { Role = ClanRole.Guardian, DisplayName = "Guardian", Group = "Warfare", IconName = "guardian", Lore = "Defender of kingdom lands.", CoreAbility = "Defend owned clan territory.", PassiveBonuses = "Damage reduction 8%; block power +10%.", ClanValue = "Homeland defense.", Selectable = true }, new RoleData { Role = ClanRole.Builder, DisplayName = "Builder", Group = "Infrastructure", IconName = "builder", Lore = "Infrastructure specialist.", CoreAbility = "Build and upgrade Fehu infrastructure efficiently.", PassiveBonuses = "Build cost -15%; upgrade cost -10%.", ClanValue = "Strongholds and development.", Selectable = true }, new RoleData { Role = ClanRole.Gatherer, DisplayName = "Gatherer", Group = "Economy", IconName = "gatherer", Lore = "Resource supplier.", CoreAbility = "Gather basic resources efficiently.", PassiveBonuses = "Carry weight +150; basic resource chance +10%.", ClanValue = "Feeds the kingdom economy.", Selectable = true }, new RoleData { Role = ClanRole.Merchant, DisplayName = "Merchant", Group = "Economy", IconName = "merchant", Lore = "Economy specialist.", CoreAbility = "Improve production and trade rewards.", PassiveBonuses = "Economic rewards +10%; trade fees -10%.", ClanValue = "Lawful prosperity.", Selectable = true }, new RoleData { Role = ClanRole.Smuggler, DisplayName = "Smuggler", Group = "Economy", IconName = "smuggler", Lore = "Illegal economy specialist.", CoreAbility = "Reduce personal clan tax.", PassiveBonuses = "Personal tax -15%.", ClanValue = "Covert value movement.", Selectable = true }, new RoleData { Role = ClanRole.Thief, DisplayName = "Thief", Group = "Intelligence", IconName = "thief", Lore = "Stealth and infiltration specialist.", CoreAbility = "Attempt theft from a nearby player.", PassiveBonuses = "Success chance scales by role slot; protected slots excluded.", ClanValue = "Espionage and pressure.", Selectable = true }, new RoleData { Role = ClanRole.Shaman, DisplayName = "Shaman", Group = "Spiritual", IconName = "shaman", Lore = "Spiritual role.", CoreAbility = "Place Rot Totems.", PassiveBonuses = "Rot Totem placement cost -15%.", ClanValue = "Corruption rites.", Selectable = true }, new RoleData { Role = ClanRole.Druid, DisplayName = "Druid", Group = "Spiritual", IconName = "druid", Lore = "Nature guardian.", CoreAbility = "Sense nearby Rot Totems through magical guidance.", PassiveBonuses = "Detection range 100m; taming speed +15%.", ClanValue = "Living-world support.", Selectable = true }, new RoleData { Role = ClanRole.Hunter, DisplayName = "Hunter", Group = "Wilderness", IconName = "hunter", Lore = "Ranged combat and wilderness specialist.", CoreAbility = "Master bows in the wild.", PassiveBonuses = "Bow damage +8%; bow stamina cost -10%.", ClanValue = "Ranged protection and hunting.", Selectable = true }, new RoleData { Role = ClanRole.Prospector, DisplayName = "Prospector", Group = "Economy", IconName = "prospector", Lore = "Mining specialist.", CoreAbility = "Extract more mining resources.", PassiveBonuses = "Ore/stone chance +12%; mining stamina cost -10%.", ClanValue = "Secures strategic materials.", Selectable = true }, new RoleData { Role = ClanRole.Skipper, DisplayName = "Skipper", Group = "Travel", IconName = "skipper", Lore = "Sea specialist.", CoreAbility = "Command ships efficiently.", PassiveBonuses = "Ship speed +15%; ship damage reduction 20% while steering.", ClanValue = "Maritime logistics.", Selectable = true }, new RoleData { Role = ClanRole.Skald, DisplayName = "Skald", Group = "Support", IconName = "skald", Lore = "Culture and morale specialist.", CoreAbility = "Strengthen rested warriors.", PassiveBonuses = "Rested duration +15%; rested stamina regeneration +5%.", ClanValue = "Morale and endurance.", Selectable = true }, new RoleData { Role = ClanRole.Lawspeaker, DisplayName = "Lawspeaker", Group = "Politics", IconName = "lawspeaker", Lore = "Politics and kingdom administration specialist.", CoreAbility = "Reduce empire administration pressure.", PassiveBonuses = "Progressive upkeep -20% at full effectiveness.", ClanValue = "Law, treaties and governance.", Selectable = true } }; public static WarfareHUD Instance { get; private set; } public static bool IsMenuOpen { get { if ((Object)(object)Instance != (Object)null) { return Instance._isVisible; } return false; } } private static float UIScale => (float)Screen.height / 1080f; private static string RuntimeAssetRoot => Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location) ?? Paths.PluginPath; private void Awake() { Instance = this; _windowId = ((Object)this).GetInstanceID(); RecalculateProfileWindow(center: true); } public void Toggle() { SetFehuMenuVisible(!_isVisible); } private void SetFehuMenuVisible(bool visible) { if (_isVisible != visible) { _isVisible = visible; if (visible) { _profileImageExpanded = false; ClearProfilePageTextures(); Cursor.lockState = (CursorLockMode)0; Cursor.visible = true; Plugin.Log.LogInfo((object)"[FCW UI] Gameplay input blocked: Fehu Kingdoms opened"); } else { _controlsDragging = false; Plugin.Log.LogInfo((object)"[FCW UI] Gameplay input restored: Fehu Kingdoms closed"); } } } private void Update() { EnsureNotificationSubscription(); if (_lastScreenWidth != Screen.width || _lastScreenHeight != Screen.height || !Mathf.Approximately(_lastUiScale, UIScale)) { RecalculateProfileWindow(center: false); } if (Input.GetKeyDown((KeyCode)284)) { SetFehuMenuVisible(!_isVisible); } } private void EnsureNotificationSubscription() { //IL_000d: Unknown result type (might be due to invalid IL or missing references) //IL_0018: Expected O, but got Unknown if (!_notificationSubscribed && !((Object)WarfareRPC.Instance == (Object)null)) { WarfareRPC.Instance.OnNotificationReceived += PushLeftNotification; _notificationSubscribed = true; } } private void OnDisable() { SetFehuMenuVisible(visible: false); } private void OnDestroy() { //IL_0013: Unknown result type (might be due to invalid IL or missing references) //IL_001e: Expected O, but got Unknown //IL_0026: Unknown result type (might be due to invalid IL or missing references) //IL_0030: Expected O, but got Unknown //IL_0044: Unknown result type (might be due to invalid IL or missing references) //IL_004f: Expected O, but got Unknown SetFehuMenuVisible(visible: false); ClearProfilePageTextures(); if ((Object)_selectedProfileTexture != (Object)null) { Object.Destroy((Object)_selectedProfileTexture); } _selectedProfileTexture = null; if (_notificationSubscribed && (Object)WarfareRPC.Instance != (Object)null) { WarfareRPC.Instance.OnNotificationReceived -= PushLeftNotification; } _notificationSubscribed = false; } private void PushLeftNotification(string message) { if (string.IsNullOrWhiteSpace(message)) { return; } float time = Time.time; string text = ClarifyNotificationText(message); if (_adminActionPending || IsAdminTabSelected()) { _adminLastMessage = text; _adminLastMessageUntil = time + 8f; _adminActionPending = false; } string text2 = NormalizeNotificationKey(text); for (int i = 0; i < _leftNotifications.Count; i++) { LeftNotification leftNotification = _leftNotifications[i]; if (leftNotification.Key == text2) { leftNotification.RepeatCount++; leftNotification.CreatedAt = time; if (i > 0) { _leftNotifications.RemoveAt(i); _leftNotifications.Insert(0, leftNotification); } return; } } _leftNotifications.Insert(0, new LeftNotification { Message = text, Key = text2, CreatedAt = time }); while (_leftNotifications.Count > 15) { _leftNotifications.RemoveAt(_leftNotifications.Count - 1); } } public static void ShowLeftNotification(string message) { Instance?.PushLeftNotification(message); } public void HandleClanSync(int clanId) { ClanWorldMapPanel.PrepareForClan(clanId); if (clanId > 0) { SetFehuMenuVisible(visible: true); if (IsAdminTabSelected()) { SetFehuMenuVisible(visible: true); Plugin.Log.LogInfo((object)("[FCW UI] Preserving Admin tab after sync, subtab=" + _adminSubTab)); } else if (_viewedProfile != null || _viewedPublicClan != null || _tab == 1 || _tab == 11 || _tab == 12 || _freebornTab == 1) { Plugin.Log.LogInfo((object)("[FCW UI] Preserving Profile after sync, subtab=" + _profileSubTab + " portraitPage=" + _profileImagePage)); } else if (_tab == 18) { RestoreGamesUiState(_lastGamesSubTab); Plugin.Log.LogInfo((object)("[FCW UI] Preserving Games tab after sync, subtab=" + _gamesSubTab)); } else { _tab = 0; Plugin.Log.LogInfo((object)("[FCW UI] Opening clan overview for ClanId=" + clanId)); } } } public void ResetClientState() { //IL_00bf: Unknown result type (might be due to invalid IL or missing references) //IL_00c4: Unknown result type (might be due to invalid IL or missing references) //IL_00ca: Unknown result type (might be due to invalid IL or missing references) //IL_00cf: Unknown result type (might be due to invalid IL or missing references) //IL_00d5: Unknown result type (might be due to invalid IL or missing references) //IL_00da: 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) //IL_00e5: Unknown result type (might be due to invalid IL or missing references) //IL_00eb: Unknown result type (might be due to invalid IL or missing references) //IL_00f0: Unknown result type (might be due to invalid IL or missing references) //IL_00f6: Unknown result type (might be due to invalid IL or missing references) //IL_00fb: 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_0106: 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: Unknown result type (might be due to invalid IL or missing references) //IL_011c: 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_012d: Unknown result type (might be due to invalid IL or missing references) //IL_0132: Unknown result type (might be due to invalid IL or missing references) //IL_0138: Unknown result type (might be due to invalid IL or missing references) //IL_013d: Unknown result type (might be due to invalid IL or missing references) //IL_0143: Unknown result type (might be due to invalid IL or missing references) //IL_0148: Unknown result type (might be due to invalid IL or missing references) //IL_014e: Unknown result type (might be due to invalid IL or missing references) //IL_0153: Unknown result type (might be due to invalid IL or missing references) //IL_0159: Unknown result type (might be due to invalid IL or missing references) //IL_015e: Unknown result type (might be due to invalid IL or missing references) //IL_0164: Unknown result type (might be due to invalid IL or missing references) //IL_0169: Unknown result type (might be due to invalid IL or missing references) //IL_016f: Unknown result type (might be due to invalid IL or missing references) //IL_0174: Unknown result type (might be due to invalid IL or missing references) //IL_017a: Unknown result type (might be due to invalid IL or missing references) //IL_017f: Unknown result type (might be due to invalid IL or missing references) //IL_0185: Unknown result type (might be due to invalid IL or missing references) //IL_018a: Unknown result type (might be due to invalid IL or missing references) //IL_0190: Unknown result type (might be due to invalid IL or missing references) //IL_0195: Unknown result type (might be due to invalid IL or missing references) //IL_019b: Unknown result type (might be due to invalid IL or missing references) //IL_01a0: Unknown result type (might be due to invalid IL or missing references) _tab = 0; _freebornTab = 0; _warSubTab = 0; _membersSubTab = 0; _infrastructureSubTab = 0; _kingdomsSubTab = 0; _guardsSubTab = 0; _allIdsSection = 0; _expandedDiplomacyClanId = 0; _bountyTargetClan = 0; _territoryDealTerritoryIndex = 0; _territoryDealClanIndex = 0; _selectedBuildingTerritoryIndex = 0; _selectedGuardTerritoryIndex = 0; _selectedGuardTypeIndex = 0; _renameTarget = null; _renameInput = ""; _pendingClanEmblemName = ""; _editingClanEmblem = false; _editingClanLinks = false; _editingBannerColors = false; _selectedBannerColorIndex = -1; _inventoryVaultPage = 0; _clanVaultPage = 0; _territoryDealTerritoryOpen = false; _territoryDealClanOpen = false; _scrollA = Vector2.zero; _scrollB = Vector2.zero; _scrollDirectoryFreeborn = Vector2.zero; _scrollDirectoryClans = Vector2.zero; _scrollIds = Vector2.zero; _scrollFreebornTerritory = Vector2.zero; _overviewScroll = Vector2.zero; _scrollVaultContents = Vector2.zero; _scrollFreebornVaultContents = Vector2.zero; _scrollRole = Vector2.zero; _scrollRolePreview = Vector2.zero; _adminScroll = Vector2.zero; _bountyActiveScroll = Vector2.zero; _bountyTargetScroll = Vector2.zero; _warActiveScroll = Vector2.zero; _warClanScroll = Vector2.zero; _pendingTerritoryDealScroll = Vector2.zero; _guardTerritoryScroll = Vector2.zero; _guardLogScroll = Vector2.zero; _buildingsTerritoryScroll = Vector2.zero; _buildingsDetailScroll = Vector2.zero; } private void InitStyles() { //IL_0014: 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_002a: Unknown result type (might be due to invalid IL or missing references) //IL_002f: Unknown result type (might be due to invalid IL or missing references) //IL_0040: Unknown result type (might be due to invalid IL or missing references) //IL_0045: Unknown result type (might be due to invalid IL or missing references) //IL_004a: Unknown result type (might be due to invalid IL or missing references) //IL_005b: Unknown result type (might be due to invalid IL or missing references) //IL_0060: Unknown result type (might be due to invalid IL or missing references) //IL_0065: 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_007b: Unknown result type (might be due to invalid IL or missing references) //IL_0080: Unknown result type (might be due to invalid IL or missing references) //IL_0091: Unknown result type (might be due to invalid IL or missing references) //IL_0096: Unknown result type (might be due to invalid IL or missing references) //IL_009b: Unknown result type (might be due to invalid IL or missing references) //IL_00ac: Unknown result type (might be due to invalid IL or missing references) //IL_00b1: Unknown result type (might be due to invalid IL or missing references) //IL_00b6: Unknown result type (might be due to invalid IL or missing references) //IL_00c7: Unknown result type (might be due to invalid IL or missing references) //IL_00cc: Unknown result type (might be due to invalid IL or missing references) //IL_00d1: Unknown result type (might be due to invalid IL or missing references) //IL_00e2: Unknown result type (might be due to invalid IL or missing references) //IL_00e7: 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_00fd: Unknown result type (might be due to invalid IL or missing references) //IL_0102: Unknown result type (might be due to invalid IL or missing references) //IL_0107: 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_011d: 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_0133: Unknown result type (might be due to invalid IL or missing references) //IL_0138: Unknown result type (might be due to invalid IL or missing references) //IL_013d: Unknown result type (might be due to invalid IL or missing references) //IL_014e: Unknown result type (might be due to invalid IL or missing references) //IL_0153: Unknown result type (might be due to invalid IL or missing references) //IL_0158: Unknown result type (might be due to invalid IL or missing references) //IL_0169: Unknown result type (might be due to invalid IL or missing references) //IL_016e: Unknown result type (might be due to invalid IL or missing references) //IL_0173: Unknown result type (might be due to invalid IL or missing references) //IL_0184: Unknown result type (might be due to invalid IL or missing references) //IL_0189: Unknown result type (might be due to invalid IL or missing references) //IL_018e: Unknown result type (might be due to invalid IL or missing references) //IL_019f: Unknown result type (might be due to invalid IL or missing references) //IL_01a4: Unknown result type (might be due to invalid IL or missing references) //IL_01a9: Unknown result type (might be due to invalid IL or missing references) //IL_01ba: Unknown result type (might be due to invalid IL or missing references) //IL_01e3: Unknown result type (might be due to invalid IL or missing references) //IL_01fc: Unknown result type (might be due to invalid IL or missing references) //IL_0225: Unknown result type (might be due to invalid IL or missing references) //IL_023e: Unknown result type (might be due to invalid IL or missing references) //IL_0263: Unknown result type (might be due to invalid IL or missing references) //IL_0288: Unknown result type (might be due to invalid IL or missing references) //IL_02a2: Unknown result type (might be due to invalid IL or missing references) //IL_02ac: Expected O, but got Unknown //IL_02e2: Unknown result type (might be due to invalid IL or missing references) //IL_02ec: Expected O, but got Unknown //IL_02fa: Unknown result type (might be due to invalid IL or missing references) //IL_0304: Expected O, but got Unknown //IL_030f: Unknown result type (might be due to invalid IL or missing references) //IL_0314: Unknown result type (might be due to invalid IL or missing references) //IL_031c: Unknown result type (might be due to invalid IL or missing references) //IL_0323: Unknown result type (might be due to invalid IL or missing references) //IL_032f: Expected O, but got Unknown //IL_033a: Unknown result type (might be due to invalid IL or missing references) //IL_0346: Unknown result type (might be due to invalid IL or missing references) //IL_0366: Unknown result type (might be due to invalid IL or missing references) //IL_037a: Unknown result type (might be due to invalid IL or missing references) //IL_038e: Unknown result type (might be due to invalid IL or missing references) //IL_03a2: Unknown result type (might be due to invalid IL or missing references) //IL_03b6: Unknown result type (might be due to invalid IL or missing references) //IL_03ca: Unknown result type (might be due to invalid IL or missing references) //IL_03de: Unknown result type (might be due to invalid IL or missing references) //IL_03f2: Unknown result type (might be due to invalid IL or missing references) //IL_0406: Unknown result type (might be due to invalid IL or missing references) //IL_041a: Unknown result type (might be due to invalid IL or missing references) //IL_0437: Unknown result type (might be due to invalid IL or missing references) //IL_0441: Expected O, but got Unknown //IL_048e: Unknown result type (might be due to invalid IL or missing references) //IL_04a3: Unknown result type (might be due to invalid IL or missing references) //IL_04d0: Unknown result type (might be due to invalid IL or missing references) //IL_04da: Expected O, but got Unknown //IL_04f4: Unknown result type (might be due to invalid IL or missing references) //IL_04fe: Expected O, but got Unknown //IL_0505: Unknown result type (might be due to invalid IL or missing references) //IL_050f: Expected O, but got Unknown //IL_0530: Unknown result type (might be due to invalid IL or missing references) //IL_0570: Unknown result type (might be due to invalid IL or missing references) //IL_057a: Expected O, but got Unknown //IL_0585: Unknown result type (might be due to invalid IL or missing references) //IL_058f: Expected O, but got Unknown //IL_05f2: Unknown result type (might be due to invalid IL or missing references) //IL_0607: Unknown result type (might be due to invalid IL or missing references) //IL_0642: Unknown result type (might be due to invalid IL or missing references) //IL_064c: Expected O, but got Unknown //IL_0656: Unknown result type (might be due to invalid IL or missing references) //IL_0660: Expected O, but got Unknown //IL_0677: Unknown result type (might be due to invalid IL or missing references) //IL_067c: Unknown result type (might be due to invalid IL or missing references) //IL_0684: Unknown result type (might be due to invalid IL or missing references) //IL_068b: Unknown result type (might be due to invalid IL or missing references) //IL_0692: Unknown result type (might be due to invalid IL or missing references) //IL_069c: Expected O, but got Unknown //IL_06a1: Expected O, but got Unknown //IL_06ac: Unknown result type (might be due to invalid IL or missing references) //IL_06b6: Expected O, but got Unknown //IL_06da: Unknown result type (might be due to invalid IL or missing references) //IL_06e4: Expected O, but got Unknown //IL_06ee: Unknown result type (might be due to invalid IL or missing references) //IL_06f8: Expected O, but got Unknown //IL_0702: Unknown result type (might be due to invalid IL or missing references) //IL_070c: Expected O, but got Unknown //IL_0768: Unknown result type (might be due to invalid IL or missing references) //IL_078b: Unknown result type (might be due to invalid IL or missing references) //IL_07ae: Unknown result type (might be due to invalid IL or missing references) //IL_07d1: Unknown result type (might be due to invalid IL or missing references) //IL_07f4: Unknown result type (might be due to invalid IL or missing references) //IL_0817: Unknown result type (might be due to invalid IL or missing references) //IL_0831: Unknown result type (might be due to invalid IL or missing references) //IL_083b: Expected O, but got Unknown //IL_0888: Unknown result type (might be due to invalid IL or missing references) //IL_089d: Unknown result type (might be due to invalid IL or missing references) //IL_08b1: Unknown result type (might be due to invalid IL or missing references) //IL_08bb: Expected O, but got Unknown //IL_08c5: Unknown result type (might be due to invalid IL or missing references) //IL_08cf: Expected O, but got Unknown //IL_08e7: Unknown result type (might be due to invalid IL or missing references) //IL_08f1: Expected O, but got Unknown //IL_0928: Unknown result type (might be due to invalid IL or missing references) //IL_093d: Unknown result type (might be due to invalid IL or missing references) //IL_0951: Unknown result type (might be due to invalid IL or missing references) //IL_095b: Expected O, but got Unknown //IL_0965: Unknown result type (might be due to invalid IL or missing references) //IL_096f: Expected O, but got Unknown //IL_0993: Unknown result type (might be due to invalid IL or missing references) //IL_099d: Expected O, but got Unknown //IL_09a8: Unknown result type (might be due to invalid IL or missing references) //IL_09bd: Unknown result type (might be due to invalid IL or missing references) //IL_09d2: Unknown result type (might be due to invalid IL or missing references) //IL_09dc: Expected O, but got Unknown //IL_09f7: Unknown result type (might be due to invalid IL or missing references) //IL_0a01: Expected O, but got Unknown //IL_0a32: Unknown result type (might be due to invalid IL or missing references) //IL_0a3c: Expected O, but got Unknown //IL_0aaf: Unknown result type (might be due to invalid IL or missing references) //IL_0af0: Unknown result type (might be due to invalid IL or missing references) //IL_0b05: Unknown result type (might be due to invalid IL or missing references) //IL_0b1a: Unknown result type (might be due to invalid IL or missing references) //IL_0b2f: Unknown result type (might be due to invalid IL or missing references) //IL_0b44: Unknown result type (might be due to invalid IL or missing references) //IL_0b4e: Expected O, but got Unknown //IL_0b9a: Unknown result type (might be due to invalid IL or missing references) //IL_0ba4: Expected O, but got Unknown //IL_0bae: Unknown result type (might be due to invalid IL or missing references) //IL_0bb8: Expected O, but got Unknown //IL_0bbf: Unknown result type (might be due to invalid IL or missing references) //IL_0bc9: Expected O, but got Unknown if (!_stylesInit) { _stylesInit = true; _panelBg = MakeTex(ForgedPalette.WindowBackground); _contentTex = MakeForgedTex(ForgedPalette.ContentBackground, ForgedPalette.BronzeDark, ForgedPalette.SilverDark); _boxTex = MakeForgedTex(ForgedPalette.PanelNeutral, ForgedPalette.BronzeDark, ForgedPalette.SilverDark); _btnTex = MakeForgedTex(ForgedPalette.DarkSteel, ForgedPalette.BronzeDark, ForgedPalette.SilverDark); _btnActiveTex = MakeForgedTex(ForgedPalette.GoldSelected, ForgedPalette.GoldDark, ForgedPalette.GoldBright); _infoPanelTex = MakeForgedTex(ForgedPalette.Mithril, ForgedPalette.SilverDark, ForgedPalette.MithrilBorder); _positivePanelTex = MakeForgedTex(ForgedPalette.Warpstone, ForgedPalette.SilverDark, ForgedPalette.WarpstoneBorder); _costPanelTex = MakeForgedTex(ForgedPalette.Meteorite, ForgedPalette.BronzeDark, ForgedPalette.MeteoriteBorder); _specialPanelTex = MakeForgedTex(ForgedPalette.Violet, ForgedPalette.PinkMuted, ForgedPalette.VioletBright); _pinkButtonTex = MakeForgedTex(ForgedPalette.PinkDark, ForgedPalette.PinkMuted, ForgedPalette.PinkBright); _pinkButtonHoverTex = MakeForgedTex(ForgedPalette.PinkMuted, ForgedPalette.PinkBright, ForgedPalette.VioletBright); _primaryButtonTex = MakeForgedTex(ForgedPalette.GoldSelected, ForgedPalette.GoldDark, ForgedPalette.GoldBright); _primaryButtonHoverTex = MakeForgedTex(ForgedPalette.GoldDark, ForgedPalette.BronzeBright, ForgedPalette.GoldBright); _dangerButtonTex = MakeForgedTex(ForgedPalette.Meteorite, ForgedPalette.BronzeDark, ForgedPalette.MeteoriteBorder); _dangerButtonHoverTex = MakeForgedTex(ForgedPalette.MeteoriteDark, ForgedPalette.MeteoriteBorder, ForgedPalette.MeteoriteBright); _disabledButtonTex = MakeForgedTex(ForgedPalette.SteelDisabled, ForgedPalette.SilverDark, ForgedPalette.BronzeDark); _pixelTex = MakeTex(Color.white); _chessLightSquareTex = MakeStoneTex(32, 32, new Color(0.49f, 0.44f, 0.34f, 1f), new Color(0.38f, 0.34f, 0.27f, 1f)); _chessDarkSquareTex = MakeStoneTex(32, 32, new Color(0.2f, 0.18f, 0.14f, 1f), new Color(0.12f, 0.11f, 0.09f, 1f)); _chessSelectedOverlayTex = MakeTex(new Color(1f, 0.74f, 0.18f, 0.32f)); _chessLegalOverlayTex = MakeTex(new Color(0.45f, 0.9f, 0.55f, 0.24f)); _windowStyle = new GUIStyle(GUI.skin.window); _windowStyle.normal.background = _panelBg; _windowStyle.onNormal.background = _panelBg; _windowStyle.border = new RectOffset(0, 0, 0, 0); _windowStyle.padding = new RectOffset(20, 20, 44, 18); _labelStyle = new GUIStyle(GUI.skin.label) { fontSize = 13, richText = true, wordWrap = true }; _labelStyle.normal.textColor = ForgedPalette.ParchmentText; _cardTitleStyle = MakeTextStyle(ForgedPalette.IvoryText, 14, (FontStyle)1); _labelGoldStyle = _cardTitleStyle; _windowTitleStyle = MakeTextStyle(ForgedPalette.WindowTitle, 17, (FontStyle)1); _majorSectionTitleStyle = MakeTextStyle(ForgedPalette.MajorHeading, 15, (FontStyle)1); _bodyTextStyle = MakeTextStyle(ForgedPalette.ParchmentText, 13, (FontStyle)0); _secondaryTextStyle = MakeTextStyle(ForgedPalette.DimText, 12, (FontStyle)0); _informationalValueStyle = MakeTextStyle(ForgedPalette.SilverBright, 13, (FontStyle)1); _positiveValueStyle = MakeTextStyle(ForgedPalette.WarpstoneText, 13, (FontStyle)1); _negativeValueStyle = MakeTextStyle(ForgedPalette.MeteoriteText, 13, (FontStyle)1); _warningValueStyle = MakeTextStyle(ForgedPalette.Gold, 13, (FontStyle)1); _specialValueStyle = MakeTextStyle(ForgedPalette.SpecialText, 13, (FontStyle)1); _disabledTextStyle = MakeTextStyle(ForgedPalette.DisabledText, 13, (FontStyle)0); _tabStyle = new GUIStyle(GUI.skin.button); _tabStyle.normal.background = _btnTex; _tabStyle.hover.background = _contentTex; _tabStyle.active.background = _disabledButtonTex; _tabStyle.normal.textColor = ForgedPalette.IvoryText; _tabStyle.hover.textColor = ForgedPalette.ParchmentText; _tabStyle.fontSize = 12; _tabStyle.fontStyle = (FontStyle)1; _tabStyle.padding = new RectOffset(8, 8, 5, 5); _tabStyle.fixedHeight = 30f; _tabStyle.margin = new RectOffset(2, 2, 0, 0); _tabActiveStyle = new GUIStyle(_tabStyle); _tabActiveStyle.normal.background = _btnActiveTex; _tabActiveStyle.normal.textColor = ForgedPalette.IvoryText; _tabActiveStyle.hover.background = _btnActiveTex; _tabActiveStyle.active.background = _btnActiveTex; _tabActiveStyle.border = new RectOffset(4, 4, 4, 4); _buttonStyle = new GUIStyle(GUI.skin.button); _buttonStyle.normal.background = _btnTex; _buttonStyle.hover.background = _contentTex; _buttonStyle.active.background = _disabledButtonTex; _buttonStyle.onNormal.background = _primaryButtonTex; _buttonStyle.normal.textColor = ForgedPalette.IvoryText; _buttonStyle.hover.textColor = ForgedPalette.ParchmentText; _buttonStyle.fontSize = 13; _buttonStyle.fontStyle = (FontStyle)1; _buttonStyle.richText = true; _buttonStyle.padding = new RectOffset(12, 12, 8, 8); _buttonStyle.margin = new RectOffset(0, 0, 2, 2); _buttonStyle.fixedHeight = 30f; _smallButtonStyle = new GUIStyle(_buttonStyle) { fontSize = 12, richText = true, padding = new RectOffset(9, 9, 6, 6) }; _boxStyle = new GUIStyle(GUI.skin.box); _boxStyle.normal.background = _boxTex; _boxStyle.padding = new RectOffset(10, 10, 9, 14); _boxStyle.border = new RectOffset(4, 4, 4, 4); _boxStyle.margin = new RectOffset(2, 2, 3, 3); _infoPanelStyle = MakeSemanticPanelStyle(_infoPanelTex); _positivePanelStyle = MakeSemanticPanelStyle(_positivePanelTex); _costPanelStyle = MakeSemanticPanelStyle(_costPanelTex); _specialPanelStyle = MakeSemanticPanelStyle(_specialPanelTex); _primaryButtonStyle = MakeSemanticButtonStyle(_primaryButtonTex, _primaryButtonHoverTex, _btnActiveTex, ForgedPalette.IvoryText); _infoButtonStyle = MakeSemanticButtonStyle(_infoPanelTex, _contentTex, _btnTex, ForgedPalette.IvoryText); _specialButtonStyle = MakeSemanticButtonStyle(_specialPanelTex, _contentTex, _btnTex, ForgedPalette.IvoryText); _pinkButtonStyle = MakeSemanticButtonStyle(_pinkButtonTex, _pinkButtonHoverTex, _specialPanelTex, ForgedPalette.IvoryText); _dangerButtonStyle = MakeSemanticButtonStyle(_dangerButtonTex, _dangerButtonHoverTex, _costPanelTex, ForgedPalette.IvoryText); _disabledButtonStyle = MakeSemanticButtonStyle(_disabledButtonTex, _disabledButtonTex, _disabledButtonTex, ForgedPalette.DisabledText); _inputFieldStyle = new GUIStyle(GUI.skin.textField); _inputFieldStyle.normal.background = _contentTex; _inputFieldStyle.focused.background = _boxTex; _inputFieldStyle.hover.background = _contentTex; _inputFieldStyle.normal.textColor = ForgedPalette.ParchmentText; _inputFieldStyle.focused.textColor = ForgedPalette.IvoryText; _inputFieldStyle.border = new RectOffset(4, 4, 4, 4); _inputFieldStyle.padding = new RectOffset(8, 8, 5, 5); _inputFieldStyle.fontSize = 13; _inputAreaStyle = new GUIStyle(GUI.skin.textArea); _inputAreaStyle.normal.background = _contentTex; _inputAreaStyle.focused.background = _boxTex; _inputAreaStyle.normal.textColor = ForgedPalette.ParchmentText; _inputAreaStyle.focused.textColor = ForgedPalette.IvoryText; _inputAreaStyle.border = new RectOffset(4, 4, 4, 4); _inputAreaStyle.padding = new RectOffset(8, 8, 7, 7); _inputAreaStyle.fontSize = 13; _inputAreaStyle.wordWrap = true; _toggleControlStyle = new GUIStyle(GUI.skin.toggle); _toggleControlStyle.normal.textColor = ForgedPalette.ParchmentText; _toggleControlStyle.hover.textColor = ForgedPalette.IvoryText; _toggleControlStyle.padding = new RectOffset(20, 6, 4, 4); _toggleControlStyle.fixedHeight = 26f; _scrollbarStyle = new GUIStyle(GUI.skin.verticalScrollbar); _scrollbarStyle.normal.background = _contentTex; _scrollbarStyle.fixedWidth = 14f; _scrollbarThumbStyle = new GUIStyle(GUI.skin.verticalScrollbarThumb); _scrollbarThumbStyle.normal.background = _btnTex; _scrollbarThumbStyle.hover.background = _btnActiveTex; _scrollbarThumbStyle.active.background = _primaryButtonHoverTex; _scrollbarThumbStyle.fixedWidth = 12f; _tabStyle.onNormal.background = _btnActiveTex; _tabStyle.onNormal.textColor = ForgedPalette.IvoryText; _tabStyle.onHover.background = _primaryButtonHoverTex; _tabStyle.onActive.background = _btnActiveTex; _tabStyle.normal.textColor = ForgedPalette.IvoryText; _tabStyle.hover.textColor = ForgedPalette.IvoryText; _tabStyle.active.textColor = ForgedPalette.ParchmentText; _tabStyle.focused.textColor = ForgedPalette.IvoryText; _chessLightSquareStyle = new GUIStyle(GUI.skin.button); _chessLightSquareStyle.normal.background = _chessLightSquareTex; _chessLightSquareStyle.hover.background = _chessLightSquareTex; _chessLightSquareStyle.active.background = _chessLightSquareTex; _chessLightSquareStyle.padding = new RectOffset(0, 0, 0, 0); _chessLightSquareStyle.margin = new RectOffset(0, 0, 0, 0); _chessDarkSquareStyle = new GUIStyle(_chessLightSquareStyle); _chessDarkSquareStyle.normal.background = _chessDarkSquareTex; _chessDarkSquareStyle.hover.background = _chessDarkSquareTex; _chessDarkSquareStyle.active.background = _chessDarkSquareTex; } } private Texture2D MakeTex(Color c) { //IL_0002: Unknown result type (might be due to invalid IL or missing references) //IL_0007: Unknown result type (might be due to invalid IL or missing references) //IL_000f: Unknown result type (might be due to invalid IL or missing references) //IL_0012: Unknown result type (might be due to invalid IL or missing references) //IL_0018: Unknown result type (might be due to invalid IL or missing references) //IL_001f: Expected O, but got Unknown Texture2D val = new Texture2D(1, 1) { hideFlags = (HideFlags)61 }; val.SetPixel(0, 0, c); val.Apply(); return val; } private Texture2D MakeForgedTex(Color fill, Color outerBorder, Color innerBorder) { //IL_0004: Unknown result type (might be due to invalid IL or missing references) //IL_0009: Unknown result type (might be due to invalid IL or missing references) //IL_0012: Expected O, but got Unknown //IL_006b: Unknown result type (might be due to invalid IL or missing references) //IL_006c: Unknown result type (might be due to invalid IL or missing references) //IL_0068: Unknown result type (might be due to invalid IL or missing references) //IL_0065: Unknown result type (might be due to invalid IL or missing references) Texture2D val = new Texture2D(32, 32) { hideFlags = (HideFlags)61 }; Color[] array = (Color[])(object)new Color[1024]; for (int i = 0; i < 32; i++) { for (int j = 0; j < 32; j++) { bool flag = j == 0 || i == 0 || j == 31 || i == 31; bool flag2 = j == 1 || i == 1 || j == 30 || i == 30; array[i * 32 + j] = (flag ? outerBorder : (flag2 ? innerBorder : fill)); } } val.SetPixels(array); val.Apply(); return val; } private GUIStyle MakeSemanticPanelStyle(Texture2D background) { //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_0017: 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_0028: Unknown result type (might be due to invalid IL or missing references) //IL_0032: Expected O, but got Unknown //IL_0033: Expected O, but got Unknown GUIStyle val = new GUIStyle(_boxStyle); val.normal.background = background; val.hover.background = background; val.border = new RectOffset(4, 4, 4, 4); return val; } private GUIStyle MakeSemanticButtonStyle(Texture2D normal, Texture2D hover, Texture2D pressed, Color textColor) { //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_0017: 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_002f: Unknown result type (might be due to invalid IL or missing references) //IL_003b: Unknown result type (might be due to invalid IL or missing references) //IL_0047: Unknown result type (might be due to invalid IL or missing references) //IL_0053: Unknown result type (might be due to invalid IL or missing references) //IL_005f: Unknown result type (might be due to invalid IL or missing references) //IL_0065: Unknown result type (might be due to invalid IL or missing references) //IL_006c: Unknown result type (might be due to invalid IL or missing references) //IL_0072: Unknown result type (might be due to invalid IL or missing references) //IL_0079: Unknown result type (might be due to invalid IL or missing references) //IL_007f: Unknown result type (might be due to invalid IL or missing references) //IL_0089: Unknown result type (might be due to invalid IL or missing references) //IL_008f: Unknown result type (might be due to invalid IL or missing references) //IL_0096: Unknown result type (might be due to invalid IL or missing references) //IL_009b: Unknown result type (might be due to invalid IL or missing references) //IL_00a5: Expected O, but got Unknown //IL_00a6: Expected O, but got Unknown GUIStyle val = new GUIStyle(_buttonStyle); val.normal.background = normal; val.hover.background = hover; val.active.background = pressed; val.focused.background = normal; val.onNormal.background = normal; val.onHover.background = hover; val.onActive.background = pressed; val.normal.textColor = textColor; val.hover.textColor = textColor; val.active.textColor = ForgedPalette.ParchmentText; val.focused.textColor = textColor; val.border = new RectOffset(4, 4, 4, 4); return val; } private GUIStyle MakeTextStyle(Color color, int fontSize, FontStyle fontStyle) { //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_0012: Unknown result type (might be due to invalid IL or missing references) //IL_0013: Unknown result type (might be due to invalid IL or missing references) //IL_0019: 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_0027: Unknown result type (might be due to invalid IL or missing references) //IL_002d: Unknown result type (might be due to invalid IL or missing references) //IL_0034: Expected O, but got Unknown GUIStyle val = new GUIStyle(_labelStyle) { fontSize = fontSize, fontStyle = fontStyle, richText = true, wordWrap = true }; val.normal.textColor = color; return val; } private GUIStyle TextStyle(UiTextRole role) { return (GUIStyle)(role switch { UiTextRole.WindowTitle => _windowTitleStyle, UiTextRole.MajorSectionTitle => _majorSectionTitleStyle, UiTextRole.CardTitle => _cardTitleStyle, UiTextRole.BodyText => _bodyTextStyle, UiTextRole.SecondaryText => _secondaryTextStyle, UiTextRole.InformationalValue => _informationalValueStyle, UiTextRole.PositiveValue => _positiveValueStyle, UiTextRole.NegativeValue => _negativeValueStyle, UiTextRole.WarningValue => _warningValueStyle, UiTextRole.SpecialValue => _specialValueStyle, UiTextRole.DisabledText => _disabledTextStyle, UiTextRole.ActiveTabText => _majorSectionTitleStyle, UiTextRole.InactiveTabText => _bodyTextStyle, UiTextRole.PrimaryButtonText => _windowTitleStyle, _ => _labelStyle, }); } private GUIStyle CardTitleStyle(UiTone tone) { return (GUIStyle)(tone switch { UiTone.Info => _informationalValueStyle, UiTone.Positive => _positiveValueStyle, UiTone.Cost => _negativeValueStyle, UiTone.Special => _specialValueStyle, _ => _cardTitleStyle, }); } private GUIStyle PanelStyle(UiTone tone) { return (GUIStyle)(tone switch { UiTone.Info => _infoPanelStyle, UiTone.Positive => _positivePanelStyle, UiTone.Cost => _costPanelStyle, UiTone.Special => _specialPanelStyle, _ => _boxStyle, }); } private Texture2D MakeBorderedTex(int width, int height, Color fill, Color border, int borderWidth) { //IL_0002: Unknown result type (might be due to invalid IL or missing references) //IL_0007: Unknown result type (might be due to invalid IL or missing references) //IL_0010: Expected O, but got Unknown //IL_0053: Unknown result type (might be due to invalid IL or missing references) //IL_0050: Unknown result type (might be due to invalid IL or missing references) //IL_0055: Unknown result type (might be due to invalid IL or missing references) Texture2D val = new Texture2D(width, height) { hideFlags = (HideFlags)61 }; Color[] array = (Color[])(object)new Color[width * height]; for (int i = 0; i < height; i++) { for (int j = 0; j < width; j++) { bool flag = j < borderWidth || i < borderWidth || j >= width - borderWidth || i >= height - borderWidth; array[i * width + j] = (flag ? border : fill); } } val.SetPixels(array); val.Apply(); return val; } private Texture2D MakeGradientTex(int w, int h, Color top, Color bottom) { //IL_0002: Unknown result type (might be due to invalid IL or missing references) //IL_0007: Unknown result type (might be due to invalid IL or missing references) //IL_000f: Unknown result type (might be due to invalid IL or missing references) //IL_0017: Expected O, but got Unknown //IL_001b: 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_0028: Unknown result type (might be due to invalid IL or missing references) //IL_0030: Unknown result type (might be due to invalid IL or missing references) Texture2D val = new Texture2D(w, h) { hideFlags = (HideFlags)61, wrapMode = (TextureWrapMode)1 }; for (int i = 0; i < h; i++) { Color val2 = Color.Lerp(bottom, top, (float)i / (float)h); for (int j = 0; j < w; j++) { val.SetPixel(j, i, val2); } } val.Apply(); return val; } private Texture2D MakeStoneTex(int w, int h, Color baseColor, Color veinColor) { //IL_0002: Unknown result type (might be due to invalid IL or missing references) //IL_0007: Unknown result type (might be due to invalid IL or missing references) //IL_000f: Unknown result type (might be due to invalid IL or missing references) //IL_0017: Expected O, but got Unknown //IL_0088: Unknown result type (might be due to invalid IL or missing references) //IL_0089: Unknown result type (might be due to invalid IL or missing references) //IL_00a3: Unknown result type (might be due to invalid IL or missing references) //IL_00a8: Unknown result type (might be due to invalid IL or missing references) //IL_00ad: Unknown result type (might be due to invalid IL or missing references) Texture2D val = new Texture2D(w, h) { hideFlags = (HideFlags)61, wrapMode = (TextureWrapMode)1 }; for (int i = 0; i < h; i++) { for (int j = 0; j < w; j++) { float num = Mathf.Sin((float)(j * 13 + i * 7) * 0.17f) * 0.5f + 0.5f; float num2 = Mathf.Sin((float)(j * 5 - i * 11) * 0.09f) * 0.5f + 0.5f; float num3 = ((j == 0 || i == 0 || j == w - 1 || i == h - 1) ? 0.14f : 0f); Color val2 = Color.Lerp(baseColor, veinColor, Mathf.Clamp01(num * 0.18f + num2 * 0.1f + num3)); val.SetPixel(j, i, val2); } } val.Apply(); return val; } private void OnGUI() { //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_0027: Unknown result type (might be due to invalid IL or missing references) //IL_0033: Unknown result type (might be due to invalid IL or missing references) //IL_0038: Unknown result type (might be due to invalid IL or missing references) //IL_0154: Unknown result type (might be due to invalid IL or missing references) //IL_00f1: Unknown result type (might be due to invalid IL or missing references) //IL_00fd: Unknown result type (might be due to invalid IL or missing references) //IL_0112: Expected O, but got Unknown //IL_010d: Unknown result type (might be due to invalid IL or missing references) //IL_0112: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)Player.m_localPlayer == (Object)null) { return; } InitStyles(); float uIScale = UIScale; Matrix4x4 matrix = GUI.matrix; GUI.matrix = Matrix4x4.TRS(Vector3.zero, Quaternion.identity, new Vector3(uIScale, uIScale, 1f)); DrawStatusBar(); DrawBottomLeftControls(); DrawLeftNotifications(); if (_isVisible) { GUIStyle textField = GUI.skin.textField; GUIStyle textArea = GUI.skin.textArea; GUIStyle toggle = GUI.skin.toggle; GUIStyle verticalScrollbar = GUI.skin.verticalScrollbar; GUIStyle verticalScrollbarThumb = GUI.skin.verticalScrollbarThumb; try { GUI.skin.textField = _inputFieldStyle; GUI.skin.textArea = _inputAreaStyle; GUI.skin.toggle = _toggleControlStyle; GUI.skin.verticalScrollbar = _scrollbarStyle; GUI.skin.verticalScrollbarThumb = _scrollbarThumbStyle; _windowRect = GUI.Window(_windowId, _windowRect, new WindowFunction(DrawWindow), "", _windowStyle); } finally { GUI.skin.textField = textField; GUI.skin.textArea = textArea; GUI.skin.toggle = toggle; GUI.skin.verticalScrollbar = verticalScrollbar; GUI.skin.verticalScrollbarThumb = verticalScrollbarThumb; } } GUI.matrix = matrix; } private void DrawLeftNotifications() { //IL_008c: Unknown result type (might be due to invalid IL or missing references) //IL_0091: Unknown result type (might be due to invalid IL or missing references) //IL_0130: Unknown result type (might be due to invalid IL or missing references) //IL_0141: Unknown result type (might be due to invalid IL or missing references) //IL_0146: Unknown result type (might be due to invalid IL or missing references) //IL_014e: Unknown result type (might be due to invalid IL or missing references) //IL_0155: 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_0165: Expected O, but got Unknown //IL_017b: Unknown result type (might be due to invalid IL or missing references) //IL_035a: Unknown result type (might be due to invalid IL or missing references) //IL_01e0: Unknown result type (might be due to invalid IL or missing references) //IL_01f1: Expected O, but got Unknown //IL_021f: Unknown result type (might be due to invalid IL or missing references) //IL_022f: Unknown result type (might be due to invalid IL or missing references) //IL_0256: Unknown result type (might be due to invalid IL or missing references) //IL_026a: Unknown result type (might be due to invalid IL or missing references) //IL_028b: Unknown result type (might be due to invalid IL or missing references) //IL_02b3: Unknown result type (might be due to invalid IL or missing references) //IL_02dc: Unknown result type (might be due to invalid IL or missing references) //IL_0300: Unknown result type (might be due to invalid IL or missing references) float time = Time.time; int num = _leftNotifications.Count - 1; while (num >= 0 && !_fcwMessagesSticky) { if (time - _leftNotifications[num].CreatedAt > 8f) { _leftNotifications.RemoveAt(num); } num--; } float num2 = 460f; if (((Rect)(ref _notificationsRect)).width <= 0f) { _notificationsRect = new Rect(30f, Mathf.Max(180f, (float)Screen.height / UIScale * 0.4f), num2, 40f); } ((Rect)(ref _notificationsRect)).width = num2; ((Rect)(ref _notificationsRect)).x = Mathf.Clamp(((Rect)(ref _notificationsRect)).x, 0f, (float)Screen.width / UIScale - num2); ((Rect)(ref _notificationsRect)).y = Mathf.Clamp(((Rect)(ref _notificationsRect)).y, 0f, (float)Screen.height / UIScale - 36f); float x = ((Rect)(ref _notificationsRect)).x; float num3 = ((Rect)(ref _notificationsRect)).y; if (!_fcwMessagesEnabled || _leftNotifications.Count == 0) { GUI.color = Color.white; return; } GUIStyle val = new GUIStyle(_labelStyle) { fontSize = 13, fontStyle = (FontStyle)1, wordWrap = true, richText = true }; val.normal.textColor = new Color(1f, 0.92f, 0.68f); for (int i = 0; i < _leftNotifications.Count; i++) { float num4 = time - _leftNotifications[i].CreatedAt; float num5 = (_fcwMessagesSticky ? 1f : Mathf.Clamp01((8f - num4) / 2f)); string text = FormatNotificationMessage(_leftNotifications[i]); float num6 = Mathf.Clamp(val.CalcHeight(new GUIContent(text), num2 - 54f) + 12f, 36f, 110f); GUI.color = new Color(0.04f, 0.03f, 0.02f, 0.82f * num5); GUI.DrawTexture(new Rect(x, num3, num2, num6), (Texture)(object)_pixelTex); GUI.color = new Color(0.85f, 0.62f, 0.18f, 0.95f * num5); GUI.DrawTexture(new Rect(x, num3, 3f, num6), (Texture)(object)_pixelTex); GUI.color = new Color(1f, 1f, 1f, num5); GUI.Label(new Rect(x + 10f, num3 + 6f, num2 - 44f, num6 - 10f), text, val); if (GUI.Button(new Rect(x + num2 - 30f, num3 + 6f, 22f, 22f), "X", _smallButtonStyle)) { _leftNotifications.RemoveAt(i); GUI.color = Color.white; } else { num3 += num6 + 6f; } } ((Rect)(ref _notificationsRect)).height = Mathf.Max(36f, num3 - ((Rect)(ref _notificationsRect)).y); HandleNotificationsDrag(); GUI.color = Color.white; } private void HandleNotificationsDrag() { //IL_001d: 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) //IL_0065: 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_00ba: Invalid comparison between Unknown and I4 //IL_0147: Unknown result type (might be due to invalid IL or missing references) //IL_014d: Invalid comparison between Unknown and I4 //IL_0076: Unknown result type (might be due to invalid IL or missing references) //IL_00cd: Unknown result type (might be due to invalid IL or missing references) //IL_010c: Unknown result type (might be due to invalid IL or missing references) //IL_0086: Unknown result type (might be due to invalid IL or missing references) //IL_009d: Unknown result type (might be due to invalid IL or missing references) //IL_00a2: Unknown result type (might be due to invalid IL or missing references) //IL_00a7: Unknown result type (might be due to invalid IL or missing references) Event current = Event.current; if (current != null && _leftNotifications.Count != 0) { Vector2 mousePosition = current.mousePosition; Rect val = default(Rect); ((Rect)(ref val))..ctor(((Rect)(ref _notificationsRect)).x, ((Rect)(ref _notificationsRect)).y, ((Rect)(ref _notificationsRect)).width - 36f, ((Rect)(ref _notificationsRect)).height); if ((int)current.type == 0 && current.button == 0 && ((Rect)(ref val)).Contains(mousePosition)) { _notificationsDragging = true; _notificationsDragOffset = mousePosition - new Vector2(((Rect)(ref _notificationsRect)).x, ((Rect)(ref _notificationsRect)).y); current.Use(); } else if ((int)current.type == 3 && _notificationsDragging) { ((Rect)(ref _notificationsRect)).x = Mathf.Clamp(mousePosition.x - _notificationsDragOffset.x, 0f, (float)Screen.width / UIScale - ((Rect)(ref _notificationsRect)).width); ((Rect)(ref _notificationsRect)).y = Mathf.Clamp(mousePosition.y - _notificationsDragOffset.y, 0f, (float)Screen.height / UIScale - 36f); current.Use(); } else if ((int)current.type == 1) { _notificationsDragging = false; } } } private void DrawBottomLeftControls() { //IL_002b: Unknown result type (might be due to invalid IL or missing references) //IL_0030: Unknown result type (might be due to invalid IL or missing references) //IL_017a: Unknown result type (might be due to invalid IL or missing references) //IL_018a: Unknown result type (might be due to invalid IL or missing references) //IL_019a: 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_0108: Unknown result type (might be due to invalid IL or missing references) //IL_0201: Unknown result type (might be due to invalid IL or missing references) //IL_0276: 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_0294: Unknown result type (might be due to invalid IL or missing references) //IL_02bd: Unknown result type (might be due to invalid IL or missing references) //IL_02e2: Unknown result type (might be due to invalid IL or missing references) //IL_02db: Unknown result type (might be due to invalid IL or missing references) float num = 386f; float num2 = 28f; if (((Rect)(ref _controlsRect)).width <= 0f) { _controlsRect = new Rect(2f, 2f, num, num2); } ((Rect)(ref _controlsRect)).width = num; ((Rect)(ref _controlsRect)).height = num2; float num3 = (float)Screen.width / UIScale; float num4 = (float)Screen.height / UIScale; ((Rect)(ref _controlsRect)).x = Mathf.Clamp(((Rect)(ref _controlsRect)).x, 2f, Mathf.Max(2f, num3 - num - 2f)); ((Rect)(ref _controlsRect)).y = Mathf.Clamp(((Rect)(ref _controlsRect)).y, 2f, Mathf.Max(2f, num4 - num2 - 2f)); Rect val = default(Rect); ((Rect)(ref val))..ctor(((Rect)(ref _windowRect)).x, ((Rect)(ref _windowRect)).y, ((Rect)(ref _windowRect)).width, 45f); if (_isVisible && ((Rect)(ref _controlsRect)).Overlaps(val)) { ((Rect)(ref _controlsRect)).y = Mathf.Clamp(((Rect)(ref val)).yMax + 8f, 2f, Mathf.Max(2f, num4 - num2 - 2f)); } HandleControlsDrag(); float x = ((Rect)(ref _controlsRect)).x; float y = ((Rect)(ref _controlsRect)).y; GUI.color = new Color(0.04f, 0.03f, 0.02f, 0.86f); GUI.DrawTexture(new Rect(x, y, num, num2), (Texture)(object)_pixelTex); GUI.color = Color.white; if (DrawStateButton(new Rect(x + 4f, y + 4f, 68f, 20f), "Msg", _fcwMessagesEnabled)) { _fcwMessagesEnabled = !_fcwMessagesEnabled; } if (DrawStateButton(new Rect(x + 76f, y + 4f, 98f, 20f), "Msg Keep", _fcwMessagesSticky, orangeWhenOff: true)) { _fcwMessagesSticky = !_fcwMessagesSticky; } bool flag = RealmRules.GetBool("force_pvp"); bool flag2 = RealmRules.GetBool("friendly_fire"); Color val2 = default(Color); ((Color)(ref val2))..ctor(0.95f, 0.18f, 0.12f, 1f); DrawStateIndicatorText(new Rect(x + 178f, y + 4f, 100f, 20f), "PvP", flag ? "FORCED ON" : "NORMAL", flag ? val2 : Color.gray); DrawStateIndicatorText(new Rect(x + 282f, y + 4f, 100f, 20f), "FF", flag2 ? "FORCED ON" : "NORMAL", flag2 ? val2 : Color.gray); } private void HandleControlsDrag() { //IL_000d: Unknown result type (might be due to invalid IL or missing references) //IL_0012: Unknown result type (might be due to invalid IL or missing references) //IL_0014: Unknown result type (might be due to invalid IL or missing references) //IL_0019: Unknown result type (might be due to invalid IL or missing references) //IL_001b: 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_006a: Invalid comparison between Unknown and I4 //IL_0126: Unknown result type (might be due to invalid IL or missing references) //IL_012c: Invalid comparison between Unknown and I4 //IL_002c: Unknown result type (might be due to invalid IL or missing references) //IL_009b: Unknown result type (might be due to invalid IL or missing references) //IL_00df: 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_0053: Unknown result type (might be due to invalid IL or missing references) //IL_0058: Unknown result type (might be due to invalid IL or missing references) //IL_005d: Unknown result type (might be due to invalid IL or missing references) Event current = Event.current; if (current != null) { Vector2 mousePosition = current.mousePosition; Rect controlsRect = _controlsRect; if ((int)current.type == 0 && current.button == 0 && ((Rect)(ref controlsRect)).Contains(mousePosition)) { _controlsDragging = true; _controlsDragOffset = mousePosition - new Vector2(((Rect)(ref _controlsRect)).x, ((Rect)(ref _controlsRect)).y); } else if ((int)current.type == 3 && _controlsDragging) { float num = (float)Screen.width / UIScale; float num2 = (float)Screen.height / UIScale; ((Rect)(ref _controlsRect)).x = Mathf.Clamp(mousePosition.x - _controlsDragOffset.x, 2f, Mathf.Max(2f, num - ((Rect)(ref _controlsRect)).width - 2f)); ((Rect)(ref _controlsRect)).y = Mathf.Clamp(mousePosition.y - _controlsDragOffset.y, 2f, Mathf.Max(2f, num2 - ((Rect)(ref _controlsRect)).height - 2f)); current.Use(); } else if ((int)current.type == 1) { _controlsDragging = false; } } } private bool DrawStateButton(Rect rect, string label, bool on, bool orangeWhenOff = false) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_0005: Unknown result type (might be due to invalid IL or missing references) //IL_0035: Unknown result type (might be due to invalid IL or missing references) //IL_003f: Expected O, but got Unknown //IL_0090: Unknown result type (might be due to invalid IL or missing references) //IL_0095: Unknown result type (might be due to invalid IL or missing references) //IL_0096: 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_00ad: 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_0075: Unknown result type (might be due to invalid IL or missing references) //IL_005a: 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) Color color = GUI.color; int fontSize = _smallButtonStyle.fontSize; RectOffset padding = _smallButtonStyle.padding; _smallButtonStyle.fontSize = 9; _smallButtonStyle.padding = new RectOffset(2, 2, 1, 1); Color tint = (GUI.color = (on ? new Color(0.2f, 0.9f, 0.25f, 1f) : (orangeWhenOff ? new Color(1f, 0.55f, 0.12f, 1f) : new Color(0.95f, 0.22f, 0.16f, 1f)))); bool result = GUI.Button(rect, "", _smallButtonStyle); GUI.color = color; DrawControlsText(rect, label + ": " + (on ? "ON" : "OFF"), tint); _smallButtonStyle.fontSize = fontSize; _smallButtonStyle.padding = padding; return result; } private void DrawStateIndicator(Rect rect, string label, bool on) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_0034: Unknown result type (might be due to invalid IL or missing references) //IL_003e: Expected O, but got Unknown //IL_0070: Unknown result type (might be due to invalid IL or missing references) //IL_0055: Unknown result type (might be due to invalid IL or missing references) //IL_007a: Unknown result type (might be due to invalid IL or missing references) Color color = GUI.color; int fontSize = _smallButtonStyle.fontSize; RectOffset padding = _smallButtonStyle.padding; _smallButtonStyle.fontSize = 9; _smallButtonStyle.padding = new RectOffset(2, 2, 1, 1); GUI.color = (on ? new Color(0.2f, 0.9f, 0.25f, 1f) : new Color(0.95f, 0.22f, 0.16f, 1f)); GUI.Label(rect, label + ": " + (on ? "ON" : "OFF"), _smallButtonStyle); GUI.color = color; _smallButtonStyle.fontSize = fontSize; _smallButtonStyle.padding = padding; } private void DrawStateIndicatorText(Rect rect, string label, string state) { //IL_0026: Unknown result type (might be due to invalid IL or missing references) //IL_0046: Unknown result type (might be due to invalid IL or missing references) //IL_0048: Unknown result type (might be due to invalid IL or missing references) //IL_004b: Unknown result type (might be due to invalid IL or missing references) //IL_0041: Unknown result type (might be due to invalid IL or missing references) Color tint = ((state != null && state.IndexOf("ON", StringComparison.OrdinalIgnoreCase) >= 0) ? new Color(0.2f, 0.9f, 0.25f, 1f) : new Color(0.95f, 0.22f, 0.16f, 1f)); DrawStateIndicatorText(rect, label, state, tint); } private void DrawStateIndicatorText(Rect rect, string label, string state, Color tint) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_0034: Unknown result type (might be due to invalid IL or missing references) //IL_003e: Expected O, but got Unknown //IL_003e: Unknown result type (might be due to invalid IL or missing references) //IL_0045: Unknown result type (might be due to invalid IL or missing references) //IL_005c: 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) Color color = GUI.color; int fontSize = _smallButtonStyle.fontSize; RectOffset padding = _smallButtonStyle.padding; _smallButtonStyle.fontSize = 9; _smallButtonStyle.padding = new RectOffset(2, 2, 1, 1); GUI.color = tint; GUI.Label(rect, "", _smallButtonStyle); GUI.color = color; DrawControlsText(rect, label + ": " + state, tint); _smallButtonStyle.fontSize = fontSize; _smallButtonStyle.padding = padding; } private void DrawControlsText(Rect rect, string text, Color tint) { //IL_00c5: Unknown result type (might be due to invalid IL or missing references) //IL_00ca: Unknown result type (might be due to invalid IL or missing references) //IL_00f3: Unknown result type (might be due to invalid IL or missing references) //IL_0125: Unknown result type (might be due to invalid IL or missing references) //IL_0141: Unknown result type (might be due to invalid IL or missing references) //IL_0142: Unknown result type (might be due to invalid IL or missing references) //IL_014c: Unknown result type (might be due to invalid IL or missing references) //IL_0156: Unknown result type (might be due to invalid IL or missing references) //IL_0012: Unknown result type (might be due to invalid IL or missing references) //IL_001c: Expected O, but got Unknown //IL_00bb: Unknown result type (might be due to invalid IL or missing references) //IL_00c5: Expected O, but got Unknown if (_controlsTextStyle == null) { _controlsTextStyle = new GUIStyle(_smallButtonStyle); _controlsTextStyle.normal.background = null; _controlsTextStyle.hover.background = null; _controlsTextStyle.active.background = null; _controlsTextStyle.focused.background = null; _controlsTextStyle.onNormal.background = null; _controlsTextStyle.onHover.background = null; _controlsTextStyle.onActive.background = null; _controlsTextStyle.onFocused.background = null; _controlsTextStyle.fontSize = 9; _controlsTextStyle.padding = new RectOffset(2, 2, 1, 1); } Color color = GUI.color; GUI.color = Color.white; _controlsTextStyle.normal.textColor = new Color(0f, 0f, 0f, 0.9f); GUI.Label(new Rect(((Rect)(ref rect)).x + 1f, ((Rect)(ref rect)).y + 1f, ((Rect)(ref rect)).width, ((Rect)(ref rect)).height), text, _controlsTextStyle); _controlsTextStyle.normal.textColor = Color.Lerp(tint, Color.white, 0.22f); GUI.Label(rect, text, _controlsTextStyle); GUI.color = color; } private bool IsLocalPvpEnabled() { Player localPlayer = Player.m_localPlayer; if ((Object)(object)localPlayer != (Object)null) { return ((Character)localPlayer).IsPVPEnabled(); } return false; } private void SetLocalPvp(bool enabled) { Player localPlayer = Player.m_localPlayer; if (!((Object)(object)localPlayer == (Object)null)) { GuardianPvPPatch.ForceLocalPvp = enabled; GuardianPvPPatch.SetPvpFromFehuPanel(localPlayer, enabled); } } private bool HasLocalAdminAccess() { Player localPlayer = Player.m_localPlayer; if ((Object)(object)localPlayer == (Object)null) { return false; } try { return WarfareManager.Instance?.GetAdminAccessInfo(localPlayer.GetPlayerID(), 0L).AuthorizedSteamId ?? false; } catch { return false; } } private string ClarifyNotificationText(string message) { return (message ?? "").Replace("Not enough coins.", "Clan Treasury does not have enough coins.").Replace("Blocked", "Cannot perform action").Replace("No active production", "No active production. Build or upgrade production buildings to generate resources."); } private string FormatNotificationMessage(LeftNotification notification) { if (notification == null) { return ""; } if (notification.RepeatCount <= 1) { return notification.Message ?? ""; } return notification.Message + " x" + notification.RepeatCount; } private string NormalizeNotificationKey(string message) { string text = message ?? ""; while (true) { int num = text.IndexOf("', num); if (num2 < 0) { break; } text = text.Remove(num, num2 - num + 1); } text = text.Replace("", ""); return text.Trim(); } private void DrawStatusBar() { //IL_00f0: Unknown result type (might be due to invalid IL or missing references) //IL_00f5: Unknown result type (might be due to invalid IL or missing references) //IL_00fa: Unknown result type (might be due to invalid IL or missing references) //IL_007b: Unknown result type (might be due to invalid IL or missing references) //IL_009b: Unknown result type (might be due to invalid IL or missing references) //IL_0166: Unknown result type (might be due to invalid IL or missing references) //IL_014b: Unknown result type (might be due to invalid IL or missing references) //IL_016b: Unknown result type (might be due to invalid IL or missing references) //IL_0174: Unknown result type (might be due to invalid IL or missing references) //IL_0180: Unknown result type (might be due to invalid IL or missing references) WarfareData warfareData = WarfareManager.Instance?.Data; if (warfareData != null) { long pid = Player.m_localPlayer.GetPlayerID(); float num = (float)Screen.width / UIScale; float num2 = 380f; float num3 = 26f; float num4 = (num - num2) / 2f; float num5 = 84f; VaultCrackSession vaultCrackSession = warfareData.ActiveCracks.Find((VaultCrackSession c) => c.PlayerId == pid); if (vaultCrackSession != null) { Bar(new Rect(num4, num5, num2, num3), vaultCrackSession.Progress01, new Color(0.8f, 0.55f, 0.15f, 0.9f), $"Cracking Vault Ä‚Ă\u02d8€žĂË\u0098Ă\u02d8‚¬ĹˇÄ‚ËÂ\u0098Ä‚Ë\u0098Ă\u02d8€šÂ¬ÄąÄľÄ‚Ă\u02d8€žĂ„Ă\u02d8€¦Ä‚Ă\u02d8€žĂË\u0098Ă\u02d8‚¬Â¦Ă„Ă\u02d8€šĂË\u0098Ă\u02d8‚¬ĹľÄ‚Ă\u02d8€žĂË\u0098Ă\u02d8‚¬Â¦Ă„Ă\u02d8€šĂË\u0098Ă\u02d8‚¬ĹľÄ‚Ă\u02d8€ąÄąÄ„Ä‚Ă\u02d8€žĂË\u0098Ă\u02d8‚¬ĹˇÄ‚ËÂ\u0098Ä‚Ë\u0098Ă\u02d8€šÂ¬Ă„Ă\u02d8€¦Ä‚Ă\u02d8€žĂ„Ă\u02d8€¦Ä‚Ă\u02d8€žĂË\u0098Ă\u02d8‚¬Ĺľ {vaultCrackSession.Progress01 * 100f:F0}% ({vaultCrackSession.MinutesElapsed:F0}/{vaultCrackSession.DurationMinutes} min)"); num5 += num3 + 4f; } Vector2i grid = GridExtensions.WorldToGrid(((Component)Player.m_localPlayer).transform.position); CaptureSession captureSession = warfareData.ActiveCaptures.Find((CaptureSession c) => c.TerritoryGridX == grid.x && c.TerritoryGridY == grid.y); if (captureSession != null) { Clan clan = warfareData.GetClan(captureSession.AttackerClanId); Color fill = (captureSession.Siege ? new Color(0.85f, 0.2f, 0.2f, 0.9f) : new Color(0.75f, 0.4f, 0.15f, 0.9f)); Bar(new Rect(num4, num5, num2, num3), captureSession.Progress01, fill, string.Format("{0}{1} capturing Ä‚Ă\u02d8€žĂË\u0098Ă\u02d8‚¬ĹˇÄ‚ËÂ\u0098Ä‚Ë\u0098Ă\u02d8€šÂ¬ÄąÄľÄ‚Ă\u02d8€žĂ„Ă\u02d8€¦Ä‚Ă\u02d8€žĂË\u0098Ă\u02d8‚¬Â¦Ă„Ă\u02d8€šĂË\u0098Ă\u02d8‚¬ĹľÄ‚Ă\u02d8€žĂË\u0098Ă\u02d8‚¬Â¦Ă„Ă\u02d8€šĂË\u0098Ă\u02d8‚¬ĹľÄ‚Ă\u02d8€ąÄąÄ„Ä‚Ă\u02d8€žĂË\u0098Ă\u02d8‚¬ĹˇÄ‚ËÂ\u0098Ä‚Ë\u0098Ă\u02d8€šÂ¬Ă„Ă\u02d8€¦Ä‚Ă\u02d8€žĂ„Ă\u02d8€¦Ä‚Ă\u02d8€žĂË\u0098Ă\u02d8‚¬Ĺľ {2:F0}%", captureSession.Siege ? "SIEGE Ä‚Ă\u02d8€žĂË\u0098Ă\u02d8‚¬ĹˇÄ‚ËÂ\u0098Ä‚Ë\u0098Ă\u02d8€šÂ¬ÄąÄľÄ‚Ă\u02d8€žĂ„Ă\u02d8€¦Ä‚Ă\u02d8€žĂË\u0098Ă\u02d8‚¬Â¦Ă„Ă\u02d8€šĂË\u0098Ă\u02d8‚¬ĹľÄ‚Ă\u02d8€žĂË\u0098Ă\u02d8‚¬Â¦Ă„Ă\u02d8€šĂË\u0098Ă\u02d8‚¬ĹľÄ‚Ă\u02d8€ąÄąÄ„Ä‚Ă\u02d8€žĂË\u0098Ă\u02d8‚¬ĹˇÄ‚ËÂ\u0098Ä‚Ë\u0098Ă\u02d8€šÂ¬Ă„Ă\u02d8€¦Ä‚Ă\u02d8€žĂ„Ă\u02d8€¦Ä‚Ă\u02d8€žĂË\u0098Ă\u02d8‚¬Ĺľ " : "", clan?.Name, captureSession.Progress01 * 100f)); } } } private void Bar(Rect r, float t, Color fill, string label) { //IL_0014: Unknown result type (might be due to invalid IL or missing references) //IL_001e: Unknown result type (might be due to invalid IL or missing references) //IL_002a: Unknown result type (might be due to invalid IL or missing references) //IL_006b: Unknown result type (might be due to invalid IL or missing references) //IL_008f: Unknown result type (might be due to invalid IL or missing references) //IL_00b3: Unknown result type (might be due to invalid IL or missing references) //IL_00e3: Unknown result type (might be due to invalid IL or missing references) //IL_00f3: Unknown result type (might be due to invalid IL or missing references) //IL_0103: Unknown result type (might be due to invalid IL or missing references) //IL_0108: Unknown result type (might be due to invalid IL or missing references) //IL_010f: Unknown result type (might be due to invalid IL or missing references) //IL_0116: Unknown result type (might be due to invalid IL or missing references) //IL_011f: Expected O, but got Unknown //IL_0125: Unknown result type (might be due to invalid IL or missing references) //IL_012f: Unknown result type (might be due to invalid IL or missing references) GUI.color = new Color(0f, 0f, 0f, 0.82f); GUI.DrawTexture(r, (Texture)(object)_pixelTex); GUI.color = fill; GUI.DrawTexture(new Rect(((Rect)(ref r)).x + 2f, ((Rect)(ref r)).y + 2f, (((Rect)(ref r)).width - 4f) * Mathf.Clamp01(t), ((Rect)(ref r)).height - 4f), (Texture)(object)_pixelTex); GUI.color = new Color(0.65f, 0.5f, 0.2f, 0.9f); GUI.DrawTexture(new Rect(((Rect)(ref r)).x, ((Rect)(ref r)).y, ((Rect)(ref r)).width, 1f), (Texture)(object)_pixelTex); GUI.DrawTexture(new Rect(((Rect)(ref r)).x, ((Rect)(ref r)).yMax - 1f, ((Rect)(ref r)).width, 1f), (Texture)(object)_pixelTex); GUI.color = Color.white; GUIStyle val = new GUIStyle(_labelStyle) { alignment = (TextAnchor)4, fontStyle = (FontStyle)1, fontSize = 12 }; val.normal.textColor = Color.white; GUI.Label(r, label, val); } private void DrawWindow(int id) { //IL_0045: Unknown result type (might be due to invalid IL or missing references) //IL_006f: Unknown result type (might be due to invalid IL or missing references) //IL_007f: 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_00b9: 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_00ce: Unknown result type (might be due to invalid IL or missing references) //IL_00d5: Unknown result type (might be due to invalid IL or missing references) //IL_00de: Expected O, but got Unknown //IL_00f8: 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_0181: Unknown result type (might be due to invalid IL or missing references) //IL_02c2: Unknown result type (might be due to invalid IL or missing references) //IL_02a3: Unknown result type (might be due to invalid IL or missing references) //IL_0850: Unknown result type (might be due to invalid IL or missing references) DrawWindowFrame(); WarfareData warfareData = WarfareManager.Instance?.Data; if (warfareData == null) { GUILayout.Label("Waiting for server data...", _labelStyle, Array.Empty()); return; } long playerID = Player.m_localPlayer.GetPlayerID(); PlayerClanInfo playerClanInfo = ResolveLocalProfileForUi(warfareData, playerID); GUI.color = ForgedPalette.WindowBackground; GUI.DrawTexture(new Rect(5f, 5f, ((Rect)(ref _windowRect)).width - 10f, 38f), (Texture)(object)_pixelTex); GUI.color = ForgedPalette.Bronze; GUI.DrawTexture(new Rect(5f, 42f, ((Rect)(ref _windowRect)).width - 10f, 1f), (Texture)(object)_pixelTex); GUI.color = Color.white; GUIStyle val = new GUIStyle(_windowTitleStyle) { alignment = (TextAnchor)4, fontSize = 17 }; GUI.Label(new Rect(0f, 7f, ((Rect)(ref _windowRect)).width, 30f), "FEHU KINGDOMS", val); if (GUI.Button(new Rect(((Rect)(ref _windowRect)).width - 38f, 10f, 28f, 26f), "X", _tabStyle)) { SetFehuMenuVisible(visible: false); } if (playerClanInfo != null && playerClanInfo.IsFreeborn && playerClanInfo.ClanId == 0) { DrawFreebornPanel(warfareData, playerClanInfo); GUI.DragWindow(new Rect(0f, 0f, ((Rect)(ref _windowRect)).width - 40f, 45f)); return; } if (playerClanInfo == null || playerClanInfo.ClanId == 0) { bool flag = IsKingdomAdminVisible(); GUILayout.Space(6f); if (flag) { GUILayout.BeginHorizontal(Array.Empty()); if (GUILayout.Button("Start", (_tab == 0) ? _tabActiveStyle : _tabStyle, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Height(30f) })) { _tab = 0; } if (GUILayout.Button("Admin", (_tab == 19) ? _tabActiveStyle : _tabStyle, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Height(30f) })) { _tab = 19; } GUILayout.EndHorizontal(); GUILayout.Space(6f); } if (flag && _tab == 19) { DrawKingdomAdmin(warfareData); } else { DrawStartChoiceActions(); GUILayout.Label("You have no clan. The clan menu opens automatically Ä‚Ă\u02d8€žĂË\u0098Ă\u02d8‚¬ĹˇÄ‚ËÂ\u0098Ä‚Ë\u0098Ă\u02d8€šÂ¬ÄąÄľÄ‚Ă\u02d8€žĂ„Ă\u02d8€¦Ä‚Ă\u02d8€žĂË\u0098Ă\u02d8‚¬Â¦Ă„Ă\u02d8€šĂË\u0098Ă\u02d8‚¬ĹľÄ‚Ă\u02d8€žĂË\u0098Ă\u02d8‚¬Â¦Ă„Ă\u02d8€šĂË\u0098Ă\u02d8‚¬ĹľÄ‚Ă\u02d8€ąÄąÄ„Ä‚Ă\u02d8€žĂË\u0098Ă\u02d8‚¬ĹˇÄ‚ËÂ\u0098Ä‚Ë\u0098Ă\u02d8€šÂ¬Ă„Ă\u02d8€¦Ä‚Ă\u02d8€žĂ„Ă\u02d8€¦Ä‚Ă\u02d8€žĂË\u0098Ă\u02d8‚¬Ĺľ found or join a clan to begin.", _labelStyle, Array.Empty()); } GUI.DragWindow(new Rect(0f, 0f, ((Rect)(ref _windowRect)).width - 40f, 45f)); return; } Clan clan = warfareData.GetClan(playerClanInfo.ClanId); if (clan != null) { WarfareManager.ColorHex(clan.Color); } GUILayout.BeginHorizontal(_boxStyle, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Height(72f) }); GUILayout.Box((Texture)(object)LocalEmblemCache.GetTexture(clan?.ClanEmblemName), _boxStyle, (GUILayoutOption[])(object)new GUILayoutOption[2] { GUILayout.Width(68f), GUILayout.Height(68f) }); long net = ((clan != null) ? Economy.CalculateClanEconomy(warfareData, playerClanInfo.ClanId) : new EconomyBreakdown()).Net; int clanTerritoryCount = warfareData.GetClanTerritoryCount(playerClanInfo.ClanId); int num = ((clan != null) ? Economy.TerritoryCap(clan) : 0); GUILayout.BeginVertical((GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.ExpandWidth(true) }); GUILayout.Space(8f); GUILayout.Label("" + clan?.Name + " [" + clan?.Tag + "] Rank: " + playerClanInfo.Rank.ToString() + "", _labelGoldStyle, Array.Empty()); GUILayout.Label("Primary: " + RoleName(playerClanInfo.PrimaryRole) + " Secondary: " + RoleName(playerClanInfo.SecondaryRole) + " Tertiary: " + RoleName(playerClanInfo.TertiaryRole) + "", _labelStyle, Array.Empty()); GUILayout.BeginHorizontal(Array.Empty()); GUILayout.Label($"Treasury: {TreasuryColored(clan?.Treasury ?? 0)} Net: {ColoredCoins(net)}/tick Territories: {clanTerritoryCount}/{num}", _labelStyle, Array.Empty()); GUILayout.EndHorizontal(); GUILayout.EndVertical(); GUILayout.BeginVertical((GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(96f) }); GUILayout.Space(5f); GUI.enabled = playerClanInfo.Rank >= ClanRank.Officer && HasClaimableBuildingProduction(warfareData, playerClanInfo); if (GUILayout.Button("Claim All", _specialButtonStyle, (GUILayoutOption[])(object)new GUILayoutOption[2] { GUILayout.Width(92f), GUILayout.Height(26f) })) { RequestClaimAllBuildingProduction(warfareData, playerClanInfo); } GUI.enabled = true; if (GUILayout.Button("Vault", _pinkButtonStyle, (GUILayoutOption[])(object)new GUILayoutOption[2] { GUILayout.Width(92f), GUILayout.Height(26f) })) { _tab = 2; _membersSubTab = 2; GUIUtility.ExitGUI(); } GUILayout.EndVertical(); GUILayout.EndHorizontal(); GUILayout.Space(4f); int num2 = 19; bool flag2 = IsKingdomAdminVisible(); float num3 = Mathf.Max(1f, (AvailableContentWidth() - 40f) / 10f); int num4 = -1; for (int i = 0; i < 2; i++) { GUILayout.BeginHorizontal(Array.Empty()); int num5 = i * 10; int num6 = Mathf.Min(num5 + 10, Tabs.Length); for (int j = num5; j < num6; j++) { if (j == num2 && !flag2) { GUILayout.Space(num3); } else if (GUILayout.Button(Tabs[j], (_tab == j) ? _tabActiveStyle : _tabStyle, (GUILayoutOption[])(object)new GUILayoutOption[2] { GUILayout.Width(num3), GUILayout.Height(30f) })) { num4 = j; } } GUILayout.EndHorizontal(); } if (num4 >= 0 && num4 != _tab) { _tab = num4; GUIUtility.ExitGUI(); } GUILayout.Space(6f); if (_tab == num2 && !flag2) { _tab = 0; } switch (_tab) { case 0: DrawOverviewHub(warfareData, playerClanInfo, clan); break; case 1: DrawProfileHub(warfareData, playerClanInfo, clan); break; case 2: DrawMembersHub(warfareData, playerClanInfo, clan); break; case 3: DrawTerritoriesHub(warfareData, playerClanInfo, clan); break; case 4: DrawEconomyHub(warfareData, playerClanInfo, clan); break; case 5: DrawWar(warfareData, playerClanInfo, clan); break; case 6: DrawInfrastructure(warfareData, playerClanInfo, clan); break; case 7: DrawKingdomsHub(warfareData, playerClanInfo, clan); break; case 8: DrawGuardsHub(warfareData, playerClanInfo, clan); break; case 9: DrawContractsHub(warfareData, playerClanInfo); break; case 10: DrawRankingsHub(warfareData, playerClanInfo, clan); break; case 11: DrawClansHub(warfareData, playerClanInfo); break; case 12: DrawHomesteadsHub(warfareData, playerClanInfo); break; case 13: DrawAllIdsHub(warfareData); break; case 14: DrawRoadmapInfoCard("NPC", "COMING SOON", "NPC profiles, service characters and world contacts will be collected here.", "Future NPC systems can use this panel without another top-level tab."); break; case 15: DrawHistoryHub(warfareData); break; case 16: DrawFaqHub(); break; case 17: DrawSagaHub(warfareData); break; case 18: DrawGamesHub(warfareData, playerClanInfo); break; case 19: DrawKingdomAdmin(warfareData); break; } GUI.DragWindow(new Rect(0f, 0f, ((Rect)(ref _windowRect)).width - 40f, 45f)); } private void DrawStartChoiceActions() { GUILayout.Space(8f); GUILayout.Label("Clan: free founding now. Growth, territory, vault and upgrades may cost resources later.", _labelStyle, Array.Empty()); GUILayout.Label("Freeborn: free solo start. Claim a Homestead; outposts and upgrades may cost resources later.", _labelStyle, Array.Empty()); GUILayout.Space(8f); GUILayout.BeginHorizontal(Array.Empty()); if (GUILayout.Button("Open Start Choice", _buttonStyle, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Height(38f) })) { Plugin.Log.LogInfo((object)"[FCW HUD] Open Start Choice clicked."); ClanSelectionUI.Instance?.OpenBrowse(); } if (GUILayout.Button("Create Free Clan", _buttonStyle, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Height(38f) })) { Plugin.Log.LogInfo((object)"[FCW HUD] Create Free Clan clicked."); ClanSelectionUI.Instance?.OpenCreate(); } ConfigEntry freebornEnabled = ClanConfig.FreebornEnabled; if (freebornEnabled != null && freebornEnabled.Value && GUILayout.Button("Play As Freeborn", _buttonStyle, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Height(38f) })) { Plugin.Log.LogInfo((object)"[FCW HUD] Play As Freeborn clicked."); WarfareRPC.Instance?.RequestPlayAsFreeborn(); } GUILayout.EndHorizontal(); } private void DrawFreebornPanel(WarfareData data, PlayerClanInfo info) { List freebornTerritories = GetFreebornTerritories(data, info.PlayerId, TerritoryType.Homestead); freebornTerritories.AddRange(GetFreebornTerritories(data, info.PlayerId, TerritoryType.Outpost)); long num = 0L; float globalMultiplier = Economy.GlobalUpkeepMultiplier(freebornTerritories.Count); foreach (ClanTerritory item in freebornTerritories) { num += Economy.TerritoryIncome(item) - Economy.ApplyBuildingUpkeepReduction(item, Economy.TerritoryUpkeep(item, globalMultiplier)); } GUILayout.BeginHorizontal(_boxStyle, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Height(72f) }); GUILayout.Box((Texture)(object)_pixelTex, _boxStyle, (GUILayoutOption[])(object)new GUILayoutOption[2] { GUILayout.Width(68f), GUILayout.Height(68f) }); GUILayout.BeginVertical((GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(Mathf.Max(380f, AvailableContentWidth() - 370f)) }); GUILayout.Space(8f); GUILayout.Label("Freeborn: " + info.PlayerName + "", _labelGoldStyle, Array.Empty()); GUILayout.Label($"Treasury: {info.FreebornTreasury} Bounty: {info.Bounty} Net: {ColoredCoins(num)}/tick Territories: {freebornTerritories.Count}", _labelStyle, Array.Empty()); GUILayout.Label("Independent homestead path", _labelStyle, Array.Empty()); GUILayout.EndVertical(); GUILayout.FlexibleSpace(); GUILayout.BeginVertical(_specialPanelStyle, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(276f) }); GUILayout.Space(10f); GUILayout.Label("STATUS SPACE", CardTitleStyle(UiTone.Special), Array.Empty()); GUILayout.Label("Future: alerts, friends, challenges", _labelStyle, Array.Empty()); GUILayout.EndVertical(); GUILayout.EndHorizontal(); GUILayout.BeginHorizontal(Array.Empty()); int num2 = -1; for (int i = 0; i < FreebornTabs.Length; i++) { if (GUILayout.Button(FreebornTabs[i], (_freebornTab == i) ? _tabActiveStyle : _tabStyle, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Height(30f) })) { num2 = i; } } bool flag = IsKingdomAdminVisible(); if (flag && GUILayout.Button("Admin", (_freebornTab == 13) ? _tabActiveStyle : _tabStyle, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Height(30f) })) { num2 = 13; } GUILayout.EndHorizontal(); if (num2 >= 0 && num2 != _freebornTab) { _freebornTab = num2; GUIUtility.ExitGUI(); } if (_freebornTab == 13 && !flag) { _freebornTab = 0; } switch (_freebornTab) { case 0: DrawFreebornOverview(data, info); break; case 1: DrawProfileHub(data, info, null); break; case 2: DrawFreebornTerritory(data, info); break; case 3: DrawFreebornEconomy(data, info); break; case 4: DrawFreebornWarfareHub(data, info); break; case 5: DrawContractsHub(data, info); break; case 6: DrawFreebornRankingStats(data, info); break; case 7: DrawHomesteadsDirectory(data, info); break; case 8: DrawAllIds(data); break; case 9: DrawHistoryHub(data); break; case 10: DrawFaqHub(); break; case 11: DrawSagaHub(data); break; case 12: DrawGamesHub(data, info); break; case 13: DrawKingdomAdmin(data); break; } } private void DrawComingSoon(string label) { DrawRoadmapInfoCard(label, "COMING SOON", (label == "Guards") ? "Future territory defense and guard management." : "Future kingdom management systems.", "This tab is reserved now so future roadmap systems can be added internally without creating new top-level tabs."); } private float AvailableContentWidth(bool reserveScrollbar = false) { return Mathf.Max(280f, ((Rect)(ref _windowRect)).width - 76f - (reserveScrollbar ? 18f : 0f)); } private float AvailableContentHeight(bool hasSecondaryTabs = true) { float num = (hasSecondaryTabs ? 230f : 196f); return Mathf.Max(260f, ((Rect)(ref _windowRect)).height - num); } private float BottomSafePadding() { return 64f; } private bool HasClaimableBuildingProduction(WarfareData data, PlayerClanInfo player) { if (data == null || player == null || player.ClanId == 0) { return false; } foreach (ClanTerritory clanTerritory in data.GetClanTerritories(player.ClanId)) { foreach (TerritoryBuilding item in clanTerritory.Buildings ?? new List()) { if (item != null && item.StoredAmount > 0) { return true; } } } foreach (PendingRootLoot item2 in data.PendingRootLoot) { if (item2.RootClanId == player.ClanId && item2.Amount > 0) { return true; } } return false; } private void RecalculateProfileWindow(bool center) { //IL_00ab: Unknown result type (might be due to invalid IL or missing references) //IL_00b0: Unknown result type (might be due to invalid IL or missing references) float uIScale = UIScale; float num = (float)Screen.width / uIScale; float num2 = (float)Screen.height / uIScale; float num3 = Mathf.Min(1380f, num - 24f); float num4 = Mathf.Min(980f, num2 - 24f); float num5 = (center ? ((num - num3) / 2f) : Mathf.Clamp(((Rect)(ref _windowRect)).x, 0f, Mathf.Max(0f, num - num3))); float num6 = (center ? ((num2 - num4) / 2f) : Mathf.Clamp(((Rect)(ref _windowRect)).y, 0f, Mathf.Max(0f, num2 - num4))); _windowRect = new Rect(num5, num6, num3, num4); _lastScreenWidth = Screen.width; _lastScreenHeight = Screen.height; _lastUiScale = uIScale; } private void RequestClaimAllBuildingProduction(WarfareData data, PlayerClanInfo player) { //IL_005b: Unknown result type (might be due to invalid IL or missing references) if (data == null || player == null || player.ClanId == 0) { return; } List clanTerritories = data.GetClanTerritories(player.ClanId); if (clanTerritories.Count != 0) { clanTerritories.Sort(delegate(ClanTerritory a, ClanTerritory b) { int num = a.GridX.CompareTo(b.GridX); return (num == 0) ? a.GridY.CompareTo(b.GridY) : num; }); WarfareRPC.Instance?.RequestTerritoryBuildingAction(clanTerritories[0].GridPosition, 5, TerritoryBuildingType.None, 0); } } private int ResponsiveColumns(float availableWidth, float minimumCardWidth, int maximumColumns, float gap = 8f) { for (int num = maximumColumns; num > 1; num--) { if ((availableWidth - gap * (float)(num - 1)) / (float)num >= minimumCardWidth) { return num; } } return 1; } private float ResponsiveCardWidth(float availableWidth, int columns, float gap = 8f) { return Mathf.Max(1f, (availableWidth - gap * (float)(columns - 1)) / (float)columns); } private float InfrastructureSelectorWidth() { return Mathf.Clamp(AvailableContentWidth() * 0.25f, 240f, 320f); } private float InfrastructureDetailWidth() { return Mathf.Max(320f, AvailableContentWidth() - InfrastructureSelectorWidth() - 8f); } private void DrawWindowFrame() { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_0005: Unknown result type (might be due to invalid IL or missing references) //IL_0029: Unknown result type (might be due to invalid IL or missing references) //IL_005f: Unknown result type (might be due to invalid IL or missing references) //IL_0089: Unknown result type (might be due to invalid IL or missing references) //IL_00bf: Unknown result type (might be due to invalid IL or missing references) Color color = GUI.color; GUI.color = ForgedPalette.BronzeDark; GUI.DrawTexture(new Rect(0f, 0f, ((Rect)(ref _windowRect)).width, 1f), (Texture)(object)_pixelTex); GUI.DrawTexture(new Rect(0f, ((Rect)(ref _windowRect)).height - 1f, ((Rect)(ref _windowRect)).width, 1f), (Texture)(object)_pixelTex); GUI.DrawTexture(new Rect(0f, 0f, 1f, ((Rect)(ref _windowRect)).height), (Texture)(object)_pixelTex); GUI.DrawTexture(new Rect(((Rect)(ref _windowRect)).width - 1f, 0f, 1f, ((Rect)(ref _windowRect)).height), (Texture)(object)_pixelTex); GUI.color = color; } private void DrawRoadmapInfoCard(string title, string status, string description, string roadmapUsage) { //IL_0054: Unknown result type (might be due to invalid IL or missing references) //IL_0059: Unknown result type (might be due to invalid IL or missing references) //IL_0060: Unknown result type (might be due to invalid IL or missing references) //IL_0069: Expected O, but got Unknown //IL_006f: Unknown result type (might be due to invalid IL or missing references) //IL_0074: Unknown result type (might be due to invalid IL or missing references) //IL_007c: Expected O, but got Unknown GUILayout.Space(10f); GUILayout.BeginHorizontal(Array.Empty()); GUILayout.FlexibleSpace(); GUILayout.BeginVertical(_specialPanelStyle, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(Mathf.Min(560f, AvailableContentWidth() - 24f)) }); GUIStyle val = new GUIStyle(_majorSectionTitleStyle) { alignment = (TextAnchor)4, fontSize = 18 }; GUIStyle val2 = new GUIStyle(_labelStyle) { alignment = (TextAnchor)4 }; GUILayout.Label("" + title + "", val, Array.Empty()); GUILayout.Label(status, val, Array.Empty()); GUILayout.Label(description, val2, Array.Empty()); GUILayout.Space(8f); GUILayout.Label("Roadmap usage", val, Array.Empty()); GUILayout.Label(roadmapUsage, val2, Array.Empty()); GUILayout.EndVertical(); GUILayout.FlexibleSpace(); GUILayout.EndHorizontal(); } private void DrawSectionTabs(string[] labels, ref int selected, float width) { selected = Mathf.Clamp(selected, 0, labels.Length - 1); float num = Mathf.Min(width, Mathf.Max(72f, (AvailableContentWidth() - (float)(labels.Length - 1) * 4f) / (float)labels.Length)); GUILayout.BeginHorizontal(Array.Empty()); for (int i = 0; i < labels.Length; i++) { if (GUILayout.Button(labels[i], (selected == i) ? _tabActiveStyle : _tabStyle, (GUILayoutOption[])(object)new GUILayoutOption[2] { GUILayout.Width(num), GUILayout.Height(38f) })) { selected = i; GUIUtility.ExitGUI(); } } GUILayout.EndHorizontal(); GUILayout.Space(6f); } private void DrawMembersHub(WarfareData data, PlayerClanInfo info, Clan clan) { string[] labels = new string[3] { "Roster", "Roles", "Vault" }; DrawSectionTabs(labels, ref _membersSubTab, 180f); switch (_membersSubTab) { case 0: DrawMembers(data, info, clan); break; case 1: DrawRole(data, info); break; case 2: DrawVaultClarity(data, info, clan); break; } } private void DrawFreebornMembersHub(WarfareData data, PlayerClanInfo info) { string[] labels = new string[3] { "Identity", "Friends", "Bounty" }; DrawSectionTabs(labels, ref _membersSubTab, 180f); switch (_membersSubTab) { case 0: DrawRoadmapInfoCard("Freeborn Identity", "ACTIVE", "Independent player status, homestead ownership and personal progression are shown across Freeborn panels.", "Future personal titles and identity tools can live here without adding another top-level tab."); break; case 1: DrawFreebornFriendships(data, info); break; case 2: DrawFreebornBounty(data, info); break; } } private void DrawInfrastructure(WarfareData data, PlayerClanInfo info, Clan clan) { string[] labels = new string[4] { "Buildings", "Production", "Resources", "Future Expansion" }; DrawSectionTabs(labels, ref _infrastructureSubTab, 180f); switch (_infrastructureSubTab) { case 0: DrawBuildings(data, info); break; case 1: DrawProductionOverview(data, info); break; case 2: DrawResourceOverview(data, info, clan); break; case 3: DrawRoadmapInfoCard("Infrastructure Expansion", "COMING SOON", "Future roads, logistics, trade routes and advanced infrastructure.", "This section will hold non-building kingdom development systems after the current Buildings and Production foundations are stable."); break; } } private void DrawFehuPortalEditor(WarfareData data, PlayerClanInfo info, Clan clan) { GUILayout.BeginHorizontal(Array.Empty()); GUILayout.BeginVertical(_boxStyle, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(520f) }); GUILayout.Label("Fehu Portal Access", _labelGoldStyle, Array.Empty()); if (!TargetPortalFehuAdapter.TargetPortalFound) { DrawEmptyState("TargetPortal not installed.", "Fehu portal controls are disabled until TargetPortal is loaded."); GUILayout.EndVertical(); GUILayout.BeginVertical(_boxStyle, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.ExpandWidth(true) }); GUILayout.Label("Soft Adapter", _labelGoldStyle, Array.Empty()); GUILayout.Label("Fehu Kingdoms still works normally without TargetPortal.", _labelStyle, Array.Empty()); GUILayout.EndVertical(); GUILayout.EndHorizontal(); return; } TeleportWorld val = TargetPortalFehuAdapter.FindCurrentPortal(); if ((Object)(object)val == (Object)null) { DrawEmptyState("No portal selected.", "Look at a portal or stand close to one to edit Fehu access."); GUILayout.EndVertical(); GUILayout.BeginVertical(_boxStyle, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.ExpandWidth(true) }); GUILayout.Label("Available Modes", _labelGoldStyle, Array.Empty()); GUILayout.Label("None, Clan, Freeborn.", _labelStyle, Array.Empty()); GUILayout.EndVertical(); GUILayout.EndHorizontal(); return; } DrawMiniStat("Selected Portal", val.GetText()); DrawMiniStat("Current Fehu Access", TargetPortalFehuAdapter.GetPortalRestrictionText(val)); DrawMiniStat("Adapter", TargetPortalFehuAdapter.AdapterStatusText()); GUILayout.Space(6f); if (GUILayout.Button("Set None", _buttonStyle, Array.Empty())) { ShowLeftNotification(TargetPortalFehuAdapter.SetPortalRestriction(val, "None", info, clan)); } if (GUILayout.Button("Set Clan Only", _buttonStyle, Array.Empty())) { ShowLeftNotification(TargetPortalFehuAdapter.SetPortalRestriction(val, "Clan", info, clan)); } if (GUILayout.Button("Set Freeborn Only", _buttonStyle, Array.Empty())) { ShowLeftNotification(TargetPortalFehuAdapter.SetPortalRestriction(val, "Freeborn", info, clan)); } GUILayout.EndVertical(); GUILayout.BeginVertical(_boxStyle, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.ExpandWidth(true) }); GUILayout.Label("Portal Rules", _labelGoldStyle, Array.Empty()); GUILayout.Label("Only the portal owner or an admin can change Fehu portal access.", _labelStyle, Array.Empty()); GUILayout.Label("Fehu restrictions are stored in portal ZDO data and do not modify TargetPortal DLL or enum modes.", _labelStyle, Array.Empty()); GUILayout.Label("If Fehu access is None, TargetPortal works normally.", _labelStyle, Array.Empty()); GUILayout.Label("If Fehu access is set, Fehu access must match for non-admin players.", _labelStyle, Array.Empty()); GUILayout.EndVertical(); GUILayout.EndHorizontal(); } private void DrawProductionOverview(WarfareData data, PlayerClanInfo info) { List buildingTerritories = GetBuildingTerritories(data, info); GUILayout.BeginVertical(_infoPanelStyle, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.MaxWidth(920f) }); GUILayout.Label("Production Overview", CardTitleStyle(UiTone.Info), Array.Empty()); GUILayout.Label("Production runs through existing territory buildings. Claim stored output when capacity is full; warnings appear only on affected territories.", _bodyTextStyle, Array.Empty()); GUILayout.EndVertical(); GUILayout.Space(6f); if (buildingTerritories.Count == 0) { DrawEmptyState("No production territories.", "Claim or select a territory with building slots to begin production."); } else { float availableWidth = AvailableContentWidth(reserveScrollbar: true); int num = ResponsiveColumns(availableWidth, 360f, 2); float num2 = ResponsiveCardWidth(availableWidth, num); for (int i = 0; i < buildingTerritories.Count; i += num) { GUILayout.BeginHorizontal(Array.Empty()); for (int j = 0; j < num; j++) { int num3 = i + j; if (num3 >= buildingTerritories.Count) { GUILayout.Space(num2 + 8f); continue; } ClanTerritory clanTerritory = buildingTerritories[num3]; GUILayout.BeginVertical(_positivePanelStyle, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(num2) }); GUILayout.Label("" + clanTerritory.DisplayName + "", _cardTitleStyle, Array.Empty()); DrawMiniStat("Status", BuildingProductionStatusText(clanTerritory)); string value = NextBuildingProductionText(clanTerritory); if (BuildingProductionStatusText(clanTerritory) != "Idle" && !string.IsNullOrEmpty(value)) { DrawMiniStat("Next", value); } GUILayout.EndVertical(); } GUILayout.EndHorizontal(); } } GUILayout.Space(6f); GUILayout.BeginVertical(_specialPanelStyle, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.MaxWidth(920f) }); DrawBuildingProductionLog(data); GUILayout.EndVertical(); } private void DrawResourceOverview(WarfareData data, PlayerClanInfo info, Clan clan) { List buildingTerritories = GetBuildingTerritories(data, info); EconomyBreakdown economyBreakdown = Economy.CalculateTerritoriesEconomy(data, info.ClanId, buildingTerritories); GUILayout.BeginHorizontal(Array.Empty()); DrawSummaryCard("Gross Resource Value", "+" + economyBreakdown.Gross + "/tick", 300f, UiTone.Info); DrawSummaryCard("Infrastructure Upkeep", "-" + (economyBreakdown.TerritoryUpkeep + economyBreakdown.ProgressiveUpkeep) + "/tick", 300f, UiTone.Cost); DrawSummaryCard("Net Resource Value", ColoredCoins(economyBreakdown.Net) + "/tick", 300f, UiTone.Positive); GUILayout.EndHorizontal(); GUILayout.BeginHorizontal(Array.Empty()); GUILayout.BeginVertical(_boxStyle, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(520f) }); GUILayout.Label("Resource Overview", _labelGoldStyle, Array.Empty()); DrawMiniStat("Territories", buildingTerritories.Count.ToString()); DrawMiniStat("Treasury", TreasuryColored(clan?.Treasury ?? info.FreebornTreasury) + " coins"); DrawMiniStat("Production", "Uses existing building production data only."); GUILayout.EndVertical(); GUILayout.BeginVertical(_boxStyle, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.ExpandWidth(true) }); GUILayout.Label("Future Resource Planning", _labelGoldStyle, Array.Empty()); GUILayout.Label("Advanced stockpiles, logistics, routes and trade planning are not active yet.", _labelStyle, Array.Empty()); GUILayout.Label("This panel is reserved for roadmap systems and does not change current economy logic.", _labelStyle, Array.Empty()); GUILayout.EndVertical(); GUILayout.EndHorizontal(); } private void DrawKingdomsHub(WarfareData data, PlayerClanInfo info, Clan clan) { string[] labels = new string[4] { "World Map", "Politics", "Laws", "Future" }; DrawSectionTabs(labels, ref _kingdomsSubTab, 180f); switch (_kingdomsSubTab) { case 0: if (info == null || info.ClanId == 0) { GUILayout.Space(AvailableContentHeight() * 0.25f); GUILayout.BeginHorizontal(Array.Empty()); GUILayout.FlexibleSpace(); GUILayout.Label("Join a clan to access the World Map.", _labelGoldStyle, Array.Empty()); GUILayout.FlexibleSpace(); GUILayout.EndHorizontal(); } else { ClanWorldMapPanel.Draw(info, AvailableContentWidth(), AvailableContentHeight(), _boxStyle, _labelStyle, _labelGoldStyle, _buttonStyle, _smallButtonStyle); } break; case 1: DrawRoadmapInfoCard("Kingdom Politics", "COMING SOON", "Future kingdom hierarchy, succession and internal politics.", "Kingdoms will become the long-term political layer above clans after its foundation is complete."); break; case 2: DrawRoadmapInfoCard("Kingdom Laws", "COMING SOON", "Future law and policy controls for kingdom-level governance.", "Planned for kingdom rules, permissions, policies and server-safe administration."); break; case 3: DrawRoadmapInfoCard("Kingdom Expansion", "COMING SOON", "Future kingdom administration, succession, hierarchy and strategic management.", "No gameplay is active here yet. The tab exists now to prevent future top-tab churn."); break; } } private void DrawGuardsHub(WarfareData data, PlayerClanInfo info, Clan clan) { string[] labels = new string[4] { "Guards", "Patrols", "Defense", "Reports" }; DrawSectionTabs(labels, ref _guardsSubTab, 180f); switch (_guardsSubTab) { case 0: DrawTerritoryGuards(data, info); break; case 1: DrawRoadmapInfoCard("Patrols", "COMING SOON", "Future roaming patrol system.", "Patrols will support road and border security without heavy constant scanning or complex AI in the first version."); break; case 2: DrawGuardDefenseSummary(data, info); break; case 3: DrawGuardLog(data); break; } } private void DrawContractsHub(WarfareData data, PlayerClanInfo info) { string[] labels = new string[4] { "Public Jobs", "Clan Jobs", "Kingdom Orders", "Completed" }; DrawSectionTabs(labels, ref _contractsTab, 180f); GUILayout.BeginVertical(_boxStyle, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.ExpandHeight(true) }); DrawContractList(data, info, _contractsTab); DrawContractLog(data); GUILayout.EndVertical(); } private void DrawContractList(WarfareData data, PlayerClanInfo info, int mode) { //IL_00d0: Unknown result type (might be due to invalid IL or missing references) //IL_00dc: Unknown result type (might be due to invalid IL or missing references) //IL_00e1: Unknown result type (might be due to invalid IL or missing references) List list = new List(); foreach (Contract item in data.Contracts ?? new List()) { if (mode == 0 && item.Status == ContractStatus.Available) { list.Add(item); } else if (mode == 1 && info.ClanId != 0 && item.Status != ContractStatus.Cancelled && item.IssuerType != ContractIssuerType.Freeborn) { list.Add(item); } else if (mode == 2 && info.IsFreeborn && item.Status != ContractStatus.Cancelled) { list.Add(item); } else if (mode == 3 && item.Status == ContractStatus.Completed) { list.Add(item); } } if (list.Count == 0) { DrawRoadmapInfoCard("Contracts", "No contracts available.", "Clan and Freeborn jobs will appear here as the task board expands.", "Future support: NPC boards, Haldor/Hildir jobs, player-created contracts, recurring work, reputation and material escrow."); return; } _scrollA = GUILayout.BeginScrollView(_scrollA, false, true, Array.Empty()); foreach (Contract item2 in list) { DrawContractCard(item2, info); } GUILayout.EndScrollView(); } private void DrawContractCard(Contract contract, PlayerClanInfo info) { GUILayout.BeginVertical(_boxStyle, Array.Empty()); GUILayout.Label("#" + contract.ContractId + " " + contract.Title + " " + contract.ContractType.ToString() + "", _labelGoldStyle, Array.Empty()); GUILayout.Label(contract.Description, _labelStyle, Array.Empty()); GUILayout.Label("Requirement: " + (string.IsNullOrEmpty(contract.RequiredItem) ? contract.TargetType.ToString() : (contract.RequiredItem + " x" + contract.RequiredAmount)) + " Reward: " + contract.RewardCoins + " coins Status: " + contract.Status, _labelStyle, Array.Empty()); if (contract.ExpiresAt > 0) { GUILayout.Label("Time left: " + FormatDuration(Math.Max(0L, contract.ExpiresAt - WarfareManager.Now())), _labelStyle, Array.Empty()); } GUILayout.BeginHorizontal(Array.Empty()); GUI.enabled = contract.Status == ContractStatus.Available; if (GUILayout.Button("Accept", _smallButtonStyle, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(110f) })) { WarfareRPC.Instance?.RequestContractAction(1, contract.ContractId); } GUI.enabled = contract.Status == ContractStatus.Accepted && (contract.AcceptedBy == 0L || contract.AcceptedBy == info.PlayerId); if (GUILayout.Button("Complete", _smallButtonStyle, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(110f) })) { WarfareRPC.Instance?.RequestContractAction(2, contract.ContractId); } GUI.enabled = true; GUILayout.EndHorizontal(); GUILayout.EndVertical(); } private void DrawContractAdminTools(WarfareData data) { if (!IsKingdomAdminVisible()) { DrawRoadmapInfoCard("Contracts Admin", "ADMIN ONLY", "Contract admin tools are hidden unless debug, local host or server admin access is active.", "Normal players can use public contracts only."); return; } GUILayout.Label("Admin Contract Tools", _labelGoldStyle, Array.Empty()); GUILayout.BeginHorizontal(Array.Empty()); if (GUILayout.Button("Create Test Gathering Contract", _smallButtonStyle, Array.Empty())) { WarfareRPC.Instance?.RequestContractAction(10); } if (GUILayout.Button("Create Test Exploration Contract", _smallButtonStyle, Array.Empty())) { WarfareRPC.Instance?.RequestContractAction(10, 0, ContractType.Exploration); } GUILayout.EndHorizontal(); GUILayout.BeginHorizontal(Array.Empty()); if (GUILayout.Button("Deliver Wood", _smallButtonStyle, Array.Empty())) { WarfareRPC.Instance?.RequestContractAction(20); } if (GUILayout.Button("Deliver Stone", _smallButtonStyle, Array.Empty())) { WarfareRPC.Instance?.RequestContractAction(21); } if (GUILayout.Button("Discover Territory", _smallButtonStyle, Array.Empty())) { WarfareRPC.Instance?.RequestContractAction(22); } if (GUILayout.Button("Visit Event Area", _smallButtonStyle, Array.Empty())) { WarfareRPC.Instance?.RequestContractAction(23); } GUILayout.EndHorizontal(); GUILayout.BeginHorizontal(Array.Empty()); if (GUILayout.Button("Complete World Event", _smallButtonStyle, Array.Empty())) { WarfareRPC.Instance?.RequestContractAction(24); } if (GUILayout.Button("Treasury Contribution", _smallButtonStyle, Array.Empty())) { WarfareRPC.Instance?.RequestContractAction(25); } GUILayout.EndHorizontal(); foreach (Contract item in new List(data.Contracts ?? new List())) { GUILayout.BeginHorizontal(_boxStyle, Array.Empty()); GUILayout.Label("#" + item.ContractId + " " + item.Title + " | " + item.Status, _labelStyle, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(420f) }); if (GUILayout.Button("Expire Contract", _smallButtonStyle, (GUILayoutOption[])(object)new GUILayoutOption[2] { GUILayout.Width(150f), GUILayout.Height(34f) })) { WarfareRPC.Instance?.RequestContractAction(11, item.ContractId); } if (GUILayout.Button("Remove Contract", _smallButtonStyle, (GUILayoutOption[])(object)new GUILayoutOption[2] { GUILayout.Width(150f), GUILayout.Height(34f) })) { WarfareRPC.Instance?.RequestContractAction(12, item.ContractId); } GUILayout.EndHorizontal(); } DrawContractLog(data); } private void DrawContractLog(WarfareData data) { GUILayout.Space(8f); GUILayout.Label("Contract Log", _labelGoldStyle, Array.Empty()); List list = data.ContractLog ?? new List(); if (list.Count == 0) { GUILayout.Label("No contract log entries yet.", _labelStyle, Array.Empty()); return; } int num = Math.Min(10, list.Count); for (int i = 0; i < num; i++) { GUILayout.Label(list[i], _labelStyle, Array.Empty()); } } private void DrawTerritoryGuards(WarfareData data, PlayerClanInfo info) { //IL_010b: Unknown result type (might be due to invalid IL or missing references) //IL_0142: 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) if (info == null || info.ClanId == 0 || info.IsFreeborn) { DrawRoadmapInfoCard("Territory Guards", "CLAN ONLY", "Freeborn players cannot hire clan guards in v1.", "Freeborn defense remains a future system. Clan guards require clan-owned territory."); return; } List clanTerritories = data.GetClanTerritories(info.ClanId); if (clanTerritories.Count == 0) { DrawRoadmapInfoCard("Territory Guards", "NO TERRITORIES", "Claim clan territory before hiring guards.", "Guards can only be placed on owned clan territory."); return; } _selectedGuardTerritoryIndex = Mathf.Clamp(_selectedGuardTerritoryIndex, 0, clanTerritories.Count - 1); ClanTerritory clanTerritory = clanTerritories[_selectedGuardTerritoryIndex]; string territoryId = WarfareData.GridToKey(clanTerritory.GridX, clanTerritory.GridY); List list = data.Guards.FindAll((TerritoryGuard g) => g.TerritoryId == territoryId && g.Status != GuardStatus.Dead); int num = WarfareManager.GuardSlotLimit(clanTerritory.Type); GUILayout.BeginHorizontal(Array.Empty()); GUILayout.BeginVertical(_boxStyle, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(390f) }); GUILayout.Label("Territory Selector", _labelGoldStyle, Array.Empty()); _guardTerritoryScroll = GUILayout.BeginScrollView(_guardTerritoryScroll, false, false, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Height(Mathf.Min(360f, 54f + (float)clanTerritories.Count * 42f)) }); for (int num2 = 0; num2 < clanTerritories.Count; num2++) { ClanTerritory clanTerritory2 = clanTerritories[num2]; string key = WarfareData.GridToKey(clanTerritory2.GridX, clanTerritory2.GridY); int count = data.Guards.FindAll((TerritoryGuard g) => g.TerritoryId == key && g.Status != GuardStatus.Dead).Count; if (GUILayout.Button(clanTerritory2.DisplayName + " | " + clanTerritory2.Type.ToString() + " | " + count + "/" + WarfareManager.GuardSlotLimit(clanTerritory2.Type), (num2 == _selectedGuardTerritoryIndex) ? _tabActiveStyle : _smallButtonStyle, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Height(34f) })) { _selectedGuardTerritoryIndex = num2; GUIUtility.ExitGUI(); } } GUILayout.EndScrollView(); GUILayout.EndVertical(); GUILayout.BeginVertical(_boxStyle, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.ExpandWidth(true) }); GUILayout.Label("" + clanTerritory.DisplayName + "", _labelGoldStyle, Array.Empty()); GUILayout.BeginHorizontal(Array.Empty()); DrawSummaryCard("Slots", list.Count + "/" + num, 170f); DrawSummaryCard("Defense Score", WarfareManager.Instance?.TerritoryDefenseScore(territoryId).ToString() ?? "0", 170f); DrawSummaryCard("Upkeep", GuardTerritoryUpkeep(list) + "c/tick", 170f); GUILayout.EndHorizontal(); GUILayout.Space(6f); GUILayout.Label("Current Guards", _labelGoldStyle, Array.Empty()); for (int num3 = 0; num3 < num; num3++) { TerritoryGuard guard = ((num3 < list.Count) ? list[num3] : null); DrawGuardSlot(territoryId, guard, num3, info); } GUILayout.Space(6f); DrawHireGuardPanel(territoryId, list.Count < num, info); GUILayout.EndVertical(); GUILayout.EndHorizontal(); } private long GuardTerritoryUpkeep(List guards) { long num = 0L; foreach (TerritoryGuard guard in guards) { if (guard.Status == GuardStatus.Active) { num += guard.UpkeepCost; } } return num; } private void DrawGuardSlot(string territoryId, TerritoryGuard guard, int slot, PlayerClanInfo info) { GUILayout.BeginHorizontal(_boxStyle, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.MinHeight(58f) }); if (guard == null) { GUILayout.Label("Slot " + (slot + 1) + " - Empty", _labelGoldStyle, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(220f) }); GUILayout.EndHorizontal(); return; } GUILayout.Label("" + WarfareManager.GuardName(guard.GuardType) + "", _labelGoldStyle, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(190f) }); GUILayout.Label("Lv. " + guard.Level, _labelStyle, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(58f) }); GUILayout.Label("Status: " + guard.Status, _labelStyle, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(130f) }); GUILayout.Label("Defense +" + WarfareManager.GuardDefenseScore(guard.GuardType), _labelStyle, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(110f) }); GUILayout.Label("Upkeep " + guard.UpkeepCost + "c", _labelStyle, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(110f) }); bool flag = info != null && info.Rank >= ClanRank.Officer; GUI.enabled = flag && guard.Level < 5; if (GUILayout.Button("Upgrade", _smallButtonStyle, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(82f) })) { WarfareRPC.Instance?.RequestGuardAction(territoryId, 1, guard.GuardType, guard.GuardId); } GUI.enabled = flag; if (GUILayout.Button("Dismiss", _smallButtonStyle, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(82f) })) { WarfareRPC.Instance?.RequestGuardAction(territoryId, 2, guard.GuardType, guard.GuardId); } GUI.enabled = true; GUILayout.EndHorizontal(); } private void DrawHireGuardPanel(string territoryId, bool hasSlot, PlayerClanInfo info) { GUILayout.BeginVertical(_boxStyle, Array.Empty()); GUILayout.Label("Hire Guard", _labelGoldStyle, Array.Empty()); GuardType[] guardTypes = WarfareManager.GetGuardTypes(); _selectedGuardTypeIndex = Mathf.Clamp(_selectedGuardTypeIndex, 0, guardTypes.Length - 1); GUILayout.BeginHorizontal(Array.Empty()); for (int i = 0; i < guardTypes.Length; i++) { GuardType type = guardTypes[i]; if (GUILayout.Button(((_selectedGuardTypeIndex == i) ? "[" : "") + WarfareManager.GuardName(type) + ((_selectedGuardTypeIndex == i) ? "]" : ""), (_selectedGuardTypeIndex == i) ? _tabActiveStyle : _smallButtonStyle, (GUILayoutOption[])(object)new GUILayoutOption[2] { GUILayout.Width(150f), GUILayout.Height(34f) })) { _selectedGuardTypeIndex = i; } } GUILayout.EndHorizontal(); GuardType guardType = guardTypes[_selectedGuardTypeIndex]; GUILayout.Label("Cost: " + WarfareManager.GuardHireCost(guardType) + "c Upkeep: " + WarfareManager.GuardUpkeepCost(guardType) + "c/tick Defense: +" + WarfareManager.GuardDefenseScore(guardType) + "", _labelStyle, Array.Empty()); GUI.enabled = hasSlot && info != null && info.Rank >= ClanRank.Officer; if (GUILayout.Button("Hire " + WarfareManager.GuardName(guardType), _primaryButtonStyle, (GUILayoutOption[])(object)new GUILayoutOption[2] { GUILayout.Width(360f), GUILayout.Height(40f) })) { WarfareRPC.Instance?.RequestGuardAction(territoryId, 0, guardType, 0); } GUI.enabled = true; if (!hasSlot) { GUILayout.Label("No empty guard slots in this territory.", _labelStyle, Array.Empty()); } GUILayout.EndVertical(); } private void DrawGuardDefenseSummary(WarfareData data, PlayerClanInfo info) { int clanId = info?.ClanId ?? 0; List list = data.Guards.FindAll((TerritoryGuard g) => clanId == 0 || g.OwnerClanId == clanId); int count = list.FindAll((TerritoryGuard g) => g.Status == GuardStatus.Active).Count; int count2 = list.FindAll((TerritoryGuard g) => g.Status == GuardStatus.Disabled).Count; long num = 0L; Dictionary dictionary = new Dictionary(); foreach (TerritoryGuard item in list) { if (item.Status == GuardStatus.Active) { num += item.UpkeepCost; dictionary[item.TerritoryId ?? ""] = true; } } GUILayout.BeginHorizontal(Array.Empty()); DrawSummaryCard("Total Guards", list.Count.ToString(), 180f); DrawSummaryCard("Active Guards", count.ToString(), 180f); DrawSummaryCard("Disabled Guards", count2.ToString(), 180f); DrawSummaryCard("Guard Upkeep", num + "c/tick", 180f); DrawSummaryCard("Defended Territories", dictionary.Count.ToString(), 220f); GUILayout.EndHorizontal(); GUILayout.BeginVertical(_boxStyle, Array.Empty()); GUILayout.Label("Defense By Territory", _labelGoldStyle, Array.Empty()); bool flag = false; foreach (ClanTerritory value in data.Territories.Values) { if (clanId == 0 || value.OwnerClanId == clanId) { string territoryId = WarfareData.GridToKey(value.GridX, value.GridY); int num2 = WarfareManager.Instance?.TerritoryDefenseScore(territoryId) ?? 0; if (num2 > 0) { flag = true; GUILayout.Label(value.DisplayName + " | " + value.Type.ToString() + " | Defense Score: " + num2 + "", _labelStyle, Array.Empty()); } } } if (!flag) { DrawEmptyState("No defended territories.", "Hire active guards to add displayed defense score."); } GUILayout.EndVertical(); } private void DrawGuardLog(WarfareData data) { //IL_0055: Unknown result type (might be due to invalid IL or missing references) //IL_0074: Unknown result type (might be due to invalid IL or missing references) //IL_0079: Unknown result type (might be due to invalid IL or missing references) GUILayout.BeginVertical(_boxStyle, Array.Empty()); GUILayout.Label("Guard Log", _labelGoldStyle, Array.Empty()); List list = data.GuardLog ?? new List(); if (list.Count == 0) { DrawEmptyState("No guard events yet.", "Guard hire, upgrade, dismiss and upkeep warnings will appear here."); GUILayout.EndVertical(); return; } _guardLogScroll = GUILayout.BeginScrollView(_guardLogScroll, false, false, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Height(360f) }); foreach (string item in list) { GUILayout.Label(item, _labelStyle, Array.Empty()); } GUILayout.EndScrollView(); GUILayout.EndVertical(); } private void DrawFreebornWarfareHub(WarfareData data, PlayerClanInfo info) { string[] labels = new string[5] { "Wars", "Diplomacy", "Bounties", "Events", "Defense" }; DrawSectionTabs(labels, ref _warSubTab, 180f); switch (_warSubTab) { case 0: DrawFreebornWar(data, info); break; case 1: DrawFreebornFriendships(data, info); break; case 2: DrawFreebornBounty(data, info); break; case 3: DrawWorldEventsPanel(data, info); break; case 4: DrawRoadmapInfoCard("Freeborn Defense", "COMING SOON", "Future homestead defense visibility and protection tools.", "This is reserved for later defense systems without changing current Freeborn gameplay."); break; } } private int FreebornOutpostCapLabel() { return 4; } private void DrawFreebornOverview(WarfareData data, PlayerClanInfo info) { //IL_0085: Unknown result type (might be due to invalid IL or missing references) //IL_008a: Unknown result type (might be due to invalid IL or missing references) //IL_008f: Unknown result type (might be due to invalid IL or missing references) //IL_0092: Unknown result type (might be due to invalid IL or missing references) //IL_03d8: Unknown result type (might be due to invalid IL or missing references) List freebornTerritories = GetFreebornTerritories(data, info.PlayerId, TerritoryType.Homestead); List freebornTerritories2 = GetFreebornTerritories(data, info.PlayerId, TerritoryType.Outpost); List list = new List(freebornTerritories); list.AddRange(freebornTerritories2); ClanTerritory clanTerritory = ((freebornTerritories.Count > 0) ? freebornTerritories[0] : null); EconomyBreakdown economyBreakdown = Economy.CalculateTerritoriesEconomy(data, 0, list); string text = "None"; if (clanTerritory != null) { text = ((!clanTerritory.OdinsPeaceActive) ? "Inactive" : ((clanTerritory.OdinsPeaceChallengeEndTimestamp > 0) ? "Challenged" : "Active")); } Vector2i grid = GridExtensions.WorldToGrid(((Component)Player.m_localPlayer).transform.position); ClanTerritory territory = data.GetTerritory(grid); GUILayout.BeginHorizontal(Array.Empty()); GUILayout.BeginVertical(_infoPanelStyle, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(330f) }); GUILayout.Label("FREEBORN STATUS", _labelGoldStyle, Array.Empty()); DrawMiniStat("Name", info.PlayerName); DrawMiniStat("Homestead", (clanTerritory != null) ? clanTerritory.DisplayName : "Not claimed"); DrawMiniStat("Additional Territories", freebornTerritories2.Count + "/" + FreebornOutpostCapLabel()); DrawMiniStat("Odin's Peace", (text == "Active") ? "Active" : text); GUILayout.EndVertical(); GUILayout.BeginVertical(_positivePanelStyle, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(330f) }); GUILayout.Label("ECONOMY", _labelGoldStyle, Array.Empty()); DrawMiniStat("Treasury", "" + info.FreebornTreasury + " coins"); DrawMiniStat("Bounty", "" + info.Bounty + " coins"); DrawMiniStat("Gross", "+" + economyBreakdown.Gross + "/tick"); DrawMiniStat("Upkeep", "-" + (economyBreakdown.TerritoryUpkeep + economyBreakdown.ProgressiveUpkeep) + "/tick"); DrawMiniStat("Net", ColoredCoins(economyBreakdown.Net) + "/tick"); GUILayout.EndVertical(); GUILayout.BeginVertical(_infoPanelStyle, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.ExpandWidth(true) }); GUILayout.Label("CURRENT STATUS", _labelGoldStyle, Array.Empty()); if (territory == null || (territory.OwnerClanId == 0 && territory.FreebornOwnerId == 0L)) { DrawMiniStat("Current Zone", "Unclaimed wilderness"); } else if (territory.FreebornOwnerId == info.PlayerId) { DrawMiniStat("Current Zone", "Your " + territory.Type.ToString() + " " + territory.DisplayName); DrawMiniStat("Current Biome", BiomeDisplayName(territory)); DrawMiniStat("Income", "+" + Economy.TerritoryIncome(territory) + "/tick"); } else { DrawMiniStat("Current Zone", "" + territory.DisplayName + ""); DrawMiniStat("Current Biome", BiomeDisplayName(territory)); } DrawMiniStat("Next Tick", "every " + (ClanConfig.UpkeepTickMinutes?.Value ?? 30) + " minutes"); DrawInactivityPreview(info.FreebornLastActiveTimestamp); GUILayout.EndVertical(); GUILayout.EndHorizontal(); GUILayout.BeginVertical(_boxStyle, Array.Empty()); DrawZoneActions(data, info, territory, grid); GUILayout.EndVertical(); } private void DrawStatusFeed(WarfareData data, PlayerClanInfo viewer, int maxLines) { List list = BuildStatusFeed(data, viewer, maxLines); if (list.Count == 0) { GUILayout.Label("No recent events.", _labelStyle, Array.Empty()); return; } foreach (string item in list) { GUILayout.Label(item, _labelStyle, Array.Empty()); } } private List BuildStatusFeed(WarfareData data, PlayerClanInfo viewer, int maxLines) { List list = new List(); if (data == null) { return list; } if (viewer != null && viewer.ClanId != 0) { foreach (War war in data.Wars) { if (list.Count >= maxLines) { return list; } if (war.IsActive && war.Involves(viewer.ClanId)) { Clan clan = data.GetClan(war.AttackerClanId); Clan clan2 = data.GetClan(war.DefenderClanId); list.Add("War: " + (clan?.Name ?? "Unknown") + " vs " + (clan2?.Name ?? "Unknown") + $" ({war.MinutesRemaining:F0}m)"); } } foreach (CaptureSession activeCapture in data.ActiveCaptures) { if (list.Count >= maxLines) { return list; } if (activeCapture.AttackerClanId == viewer.ClanId || activeCapture.DefenderClanId == viewer.ClanId) { list.Add($"Capture: {activeCapture.TerritoryGridX},{activeCapture.TerritoryGridY} {activeCapture.Progress01 * 100f:F0}%"); } } foreach (TerritoryTransferOffer territoryTransferOffer in data.TerritoryTransferOffers) { if (list.Count >= maxLines) { return list; } if (territoryTransferOffer.Status == TerritoryTransferStatus.Pending && (territoryTransferOffer.FromClanId == viewer.ClanId || territoryTransferOffer.ToClanId == viewer.ClanId)) { list.Add(((territoryTransferOffer.Price > 0) ? "Sale: " : "Gift: ") + territoryTransferOffer.TerritoryId + $" ({territoryTransferOffer.Price}c)"); } } foreach (NonAggressionPact nonAggressionPact in data.NonAggressionPacts) { if (list.Count >= maxLines) { return list; } if (nonAggressionPact.Status == PactStatus.Active && (nonAggressionPact.ClanAId == viewer.ClanId || nonAggressionPact.ClanBId == viewer.ClanId)) { list.Add("NAP: " + (data.GetClan((nonAggressionPact.ClanAId == viewer.ClanId) ? nonAggressionPact.ClanBId : nonAggressionPact.ClanAId)?.Name ?? "Unknown")); } } Alliance alliance = WarfareManager.Instance?.GetAllianceForClan(viewer.ClanId); if (alliance != null && list.Count < maxLines) { list.Add("Alliance: " + alliance.AllianceName); } } List list2 = data.BuildingProductionLog ?? new List(); for (int i = 0; i < list2.Count; i++) { if (list.Count >= maxLines) { break; } list.Add(list2[i]); } foreach (WorldEvent item in new List(data.WorldEvents)) { if (list.Count >= maxLines) { break; } if (item.Status == WorldEventStatus.Active) { list.Add("World Event: " + item.EventName); } } return list; } private bool IsKingdomAdminVisible() { return true; } private long LocalPlayerId() { //IL_0007: Unknown result type (might be due to invalid IL or missing references) //IL_0012: Expected O, but got Unknown Player localPlayer = Player.m_localPlayer; if (!((Object)localPlayer != (Object)null)) { return 0L; } return localPlayer.GetPlayerID(); } private WarfareManager.AdminAccessInfo GetLocalAdminAccessInfo() { return WarfareManager.Instance?.GetAdminAccessInfo(LocalPlayerId(), 0L) ?? new WarfareManager.AdminAccessInfo(); } private string YesNo(bool value) { if (!value) { return "NO"; } return "YES"; } private void DrawInactivityPreview(long lastActive) { DrawMiniStat("Last Active", FormatTimestamp(lastActive)); DrawMiniStat("Extra Territories Loss In", InactivityCountdown(lastActive, 10)); DrawMiniStat("Capital Vulnerable In", InactivityCountdown(lastActive, 15)); DrawMiniStat("Inactivity Status", InactivityStatus(lastActive)); GUILayout.Label("Tracking only: inactivity enforcement is not active yet.", _labelStyle, Array.Empty()); } private string FormatTimestamp(long timestamp) { if (timestamp <= 0) { return "Unknown / not tracked yet"; } return DateTimeOffset.FromUnixTimeSeconds(timestamp).ToLocalTime().ToString("yyyy-MM-dd HH:mm"); } private string InactivityCountdown(long lastActive, int days) { if (lastActive <= 0) { return "Unknown / waiting for next login"; } long num = lastActive + (long)days * 86400L - DateTimeOffset.UtcNow.ToUnixTimeSeconds(); if (num <= 0) { return "Expired preview"; } long num2 = num / 86400; long num3 = num % 86400 / 3600; return num2 + "d " + num3 + "h"; } private string InactivityStatus(long lastActive) { if (lastActive <= 0) { return "Unknown"; } long num = DateTimeOffset.UtcNow.ToUnixTimeSeconds() - lastActive; if (num >= 1296000) { return "Expired Preview"; } if (num >= 864000) { return "Warning"; } return "Protected"; } private bool IsAdminPanelUnlocked() { if (!string.IsNullOrEmpty(ClanConfig.AdminPanelPassword?.Value ?? "")) { return _adminPasswordUnlocked; } return true; } private void DrawAdminPasswordDialog() { GUILayout.FlexibleSpace(); GUILayout.BeginHorizontal(Array.Empty()); GUILayout.FlexibleSpace(); GUILayout.BeginVertical(_boxStyle, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(360f) }); GUILayout.Label("Administrator Password", _labelGoldStyle, Array.Empty()); _adminPasswordInput = GUILayout.PasswordField(_adminPasswordInput ?? "", '*', (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(320f) }); if (_adminPasswordDenied) { GUILayout.Label("Access denied.", _labelStyle, Array.Empty()); } GUILayout.BeginHorizontal(Array.Empty()); if (GUILayout.Button("Confirm", _buttonStyle, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(150f) })) { if ((_adminPasswordInput ?? "") == (ClanConfig.AdminPanelPassword?.Value ?? "")) { _adminPasswordUnlocked = true; _adminPasswordDenied = false; _adminPasswordInput = ""; } else { _adminPasswordDenied = true; _adminPasswordInput = ""; } } if (GUILayout.Button("Cancel", _buttonStyle, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(150f) })) { _adminPasswordInput = ""; _adminPasswordDenied = false; _tab = 0; _freebornTab = 0; } GUILayout.EndHorizontal(); GUILayout.EndVertical(); GUILayout.FlexibleSpace(); GUILayout.EndHorizontal(); GUILayout.FlexibleSpace(); } private void DrawKingdomAdmin(WarfareData data) { //IL_0067: Unknown result type (might be due to invalid IL or missing references) //IL_0088: Unknown result type (might be due to invalid IL or missing references) //IL_008d: Unknown result type (might be due to invalid IL or missing references) WarfareManager.AdminAccessInfo localAdminAccessInfo = GetLocalAdminAccessInfo(); bool authorizedSteamId = localAdminAccessInfo.AuthorizedSteamId; string[] labels = new string[7] { "Main", "Server Settings", "PvP Settings", "Realm Laws", "Notifications", "Admin Crew", "Constellation Members" }; DrawSectionTabs(labels, ref _adminSubTab, 190f); DrawAdminActionMessage(); _adminScroll = GUILayout.BeginScrollView(_adminScroll, false, true, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Height(AvailableContentHeight()) }); if (_adminSubTab == 0) { if (!authorizedSteamId) { DrawAdminLocked(localAdminAccessInfo); } else { DrawAdminMain(data, localAdminAccessInfo); } } else if (_adminSubTab == 1) { DrawAdminGameSettings(authorizedSteamId); } else if (_adminSubTab == 2) { DrawAdminPvpSettings(authorizedSteamId); } else if (_adminSubTab == 3) { DrawAdminRealmLaws(authorizedSteamId); } else if (_adminSubTab == 4) { DrawAdminNotifications(authorizedSteamId); } else if (_adminSubTab == 5) { DrawAdminCrew(authorizedSteamId); } else { DrawConstellationMembers(authorizedSteamId); } GUILayout.Space(BottomSafePadding()); GUILayout.EndScrollView(); } private bool IsAdminTabSelected() { if (_tab != 19) { return _freebornTab == 13; } return true; } private void PreserveAdminTabBeforeAction(string message) { SetFehuMenuVisible(visible: true); _tab = 19; _freebornTab = 13; _adminActionPending = true; _adminLastMessage = message ?? "Admin action sent."; _adminLastMessageUntil = Time.time + 8f; } private void DrawAdminActionMessage() { if (!string.IsNullOrWhiteSpace(_adminLastMessage) && !(Time.time > _adminLastMessageUntil)) { GUILayout.BeginVertical(_boxStyle, Array.Empty()); GUILayout.Label(_adminLastMessage, _labelGoldStyle, Array.Empty()); GUILayout.EndVertical(); } } private void RequestAdminTextConfigPreserve(int action, string id, string nickname, string note) { PreserveAdminTabBeforeAction(AdminActionPendingMessage(action)); WarfareRPC.Instance?.RequestAdminTextConfig(action, id, nickname, note); } private void RequestKingdomAdminActionPreserve(int action, long targetId) { PreserveAdminTabBeforeAction(AdminActionPendingMessage(action)); WarfareRPC.Instance?.RequestKingdomAdminAction(action, targetId); } private void RequestWorldEventAdminPreserve(int action, int eventId) { PreserveAdminTabBeforeAction("World event admin action sent."); WarfareRPC.Instance?.RequestWorldEventAdmin(action, eventId); } private string AdminActionPendingMessage(int action) { switch (action) { case 1: return "Admin add/update sent."; case 2: return "Admin remove sent."; case 10: return "Constellation member add/update sent."; case 11: return "Constellation member remove sent."; case 20: case 21: case 22: case 30: case 31: case 32: case 33: case 34: case 35: case 36: case 37: case 38: case 39: case 40: case 41: case 42: case 43: case 44: case 55: case 56: case 60: case 61: case 62: case 63: case 64: case 65: case 66: case 67: case 68: case 69: case 70: case 71: return "Notification setting update sent."; case 80: return "Realm law update sent."; case 102: return "Protected zone repair sent."; case 306: return "Territory check sent."; default: return "Admin action sent."; } } private void DrawAdminLocked(WarfareManager.AdminAccessInfo access) { GUILayout.BeginVertical(_boxStyle, Array.Empty()); GUILayout.Label("Admin access required.", _labelGoldStyle, Array.Empty()); GUILayout.Label("Your ID: " + LocalPlayerId(), _labelStyle, Array.Empty()); GUILayout.EndVertical(); } private void DrawAdminMain(WarfareData data, WarfareManager.AdminAccessInfo access) { GUILayout.Label("ADMINS", _labelGoldStyle, Array.Empty()); GUILayout.BeginVertical(_boxStyle, Array.Empty()); GUILayout.Label("Admin Access", _labelGoldStyle, Array.Empty()); GUILayout.Label("Permissions use SteamID64 only. PlayerID and SessionID are diagnostics only.", _labelStyle, Array.Empty()); GUILayout.BeginHorizontal(Array.Empty()); DrawMiniStat("Authorized", YesNo(access.AuthorizedSteamId)); DrawMiniStat("Matched Admin ID", string.IsNullOrEmpty(access.MatchedSteamId) ? "-" : access.MatchedSteamId); DrawMiniStat("Your ID", LocalPlayerId().ToString()); GUILayout.EndHorizontal(); GUILayout.EndVertical(); DrawAdminGameTools(data); GUILayout.BeginHorizontal(Array.Empty()); DrawAdminTerritoryTools(data); DrawAdminClanTools(data); GUILayout.EndHorizontal(); GUILayout.BeginHorizontal(Array.Empty()); DrawAdminHomesteadTools(data); DrawAdminDiplomacyTools(data); GUILayout.EndHorizontal(); GUILayout.BeginHorizontal(Array.Empty()); GUILayout.BeginVertical(_boxStyle, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(420f) }); GUILayout.Label("Events Tools", _labelGoldStyle, Array.Empty()); DrawAdminWorldEventTools(data); GUILayout.EndVertical(); GUILayout.BeginVertical(_boxStyle, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(420f) }); GUILayout.Label("Guards Tools", _labelGoldStyle, Array.Empty()); DrawAdminGuardOverview(data); GUILayout.EndVertical(); GUILayout.EndHorizontal(); DrawAdminDangerTools(data); } private void DrawAdminGameSettings(bool canEdit) { GUILayout.BeginVertical(_boxStyle, Array.Empty()); GUILayout.Label("Server Settings", _labelGoldStyle, Array.Empty()); GUILayout.Label("These are global server rules synchronized by the server.\nChanges apply immediately to all connected players.\nOnly administrators can modify these settings.", _labelStyle, Array.Empty()); GUILayout.Space(4f); GUILayout.Label("Server Sync: Connected", _labelStyle, Array.Empty()); if (!canEdit) { GUILayout.Label("Read-only. Admin SteamID64 is required to modify settings.", _labelStyle, Array.Empty()); } GUILayout.Space(8f); GUILayout.BeginHorizontal(Array.Empty()); GUILayout.BeginVertical(_boxStyle, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(520f) }); DrawServerSettingSection("General"); GUILayout.Label("PvP and territory combat rules now live under PvP Settings.", _labelStyle, Array.Empty()); GUILayout.EndVertical(); GUILayout.BeginVertical(_boxStyle, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(520f) }); DrawServerSettingSection("Protection"); DrawGameSettingRow("Freeborn Protection Enabled", ClanConfig.FreebornProtectionEnabled?.Value ?? false, canEdit, 36, future: true); DrawGameSettingRow("Odin's Peace Enabled", ClanConfig.OdinsPeaceEnabled?.Value ?? false, canEdit, 37, future: false); DrawServerSettingSection("Economy"); DrawGameSettingRow("Taxes Enabled", ClanConfig.EnableClanMemberTax?.Value ?? false, canEdit, 38, future: false); DrawGameSettingRow("Upkeep Enabled", ClanConfig.EnableProgressiveTerritoryUpkeep?.Value ?? false, canEdit, 39, future: false); DrawServerSettingSection("World"); DrawGameSettingRow("World Events Enabled", ClanConfig.WorldEventsEnabled?.Value ?? false, canEdit, 40, future: true); DrawGameSettingRow("Games Enabled", ClanConfig.GamesEnabled?.Value ?? false, canEdit, 41, future: true); DrawServerSettingSection("Debug"); ConfigEntry enableTaxDebugLogs = ClanConfig.EnableTaxDebugLogs; DrawGameSettingRow("Debug Logging", (enableTaxDebugLogs != null && enableTaxDebugLogs.Value) || (ClanConfig.EnableVaultDebugLogs?.Value ?? false), canEdit, 42, future: false); GUILayout.EndVertical(); GUILayout.EndHorizontal(); GUILayout.EndVertical(); } private void DrawAdminRealmLaws(bool canEdit) { GUILayout.BeginVertical(_boxStyle, Array.Empty()); GUILayout.Label("Realm Laws", _labelGoldStyle, Array.Empty()); GUILayout.Label("A server-authored law book for global rules. Existing laws are wired to ClanConfig where gameplay support exists; future systems can query RealmRules by ID.", _labelStyle, Array.Empty()); GUILayout.Label("Server Sync: Connected", _labelStyle, Array.Empty()); if (!canEdit) { GUILayout.Label("Read-only. Admin SteamID64 is required to amend realm laws.", _labelStyle, Array.Empty()); } GUILayout.Space(8f); foreach (RealmRuleCategory category in RealmRules.Categories) { DrawRealmLawCategory(category, canEdit); } GUILayout.EndVertical(); } private void DrawRealmLawCategory(RealmRuleCategory category, bool canEdit) { bool flag = _realmLawCollapsed.Contains(category.Id); GUILayout.BeginVertical(_boxStyle, Array.Empty()); GUILayout.BeginHorizontal(Array.Empty()); if (GUILayout.Button(flag ? "+" : "-", _smallButtonStyle, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(34f) })) { if (flag) { _realmLawCollapsed.Remove(category.Id); } else { _realmLawCollapsed.Add(category.Id); } flag = !flag; } GUILayout.Label("" + category.Title + "", _labelGoldStyle, Array.Empty()); GUILayout.EndHorizontal(); GUILayout.Label(category.Description, _labelStyle, Array.Empty()); if (!flag) { foreach (RealmRule rule in RealmRules.Rules) { if (rule.VisibleInUi && string.Equals(rule.Category, category.Id, StringComparison.OrdinalIgnoreCase)) { DrawRealmLawRow(rule, canEdit); } } } GUILayout.EndVertical(); } private void DrawRealmLawRow(RealmRule rule, bool canEdit) { string value = RealmRules.GetValue(rule.Id); GUILayout.BeginVertical(_boxStyle, Array.Empty()); GUILayout.BeginHorizontal(Array.Empty()); GUILayout.Label("" + rule.DisplayName + "", _labelStyle, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(260f) }); if (rule.ValueType == RealmRuleValueType.Bool) { bool flag = WarfareManager.IsTruthy(value); GUILayout.Label("" + (flag ? "ON" : "OFF") + "", _labelStyle, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(80f) }); GUI.enabled = canEdit; if (GUILayout.Button(flag ? "Turn OFF" : "Turn ON", _smallButtonStyle, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(96f) })) { RequestAdminTextConfigPreserve(80, rule.Id, "", (!flag).ToString()); } GUI.enabled = true; } else { GUILayout.Label("" + RealmRules.DisplayValue(rule, value) + "", _labelStyle, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(120f) }); if (!_realmLawInputs.TryGetValue(rule.Id, out var value2) || string.IsNullOrEmpty(value2)) { value2 = value ?? ""; } GUI.enabled = canEdit; _realmLawInputs[rule.Id] = GUILayout.TextField(value2, 64, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(180f) }); if (GUILayout.Button("Save", _smallButtonStyle, (GUILayoutOption[])(object)new GUILayoutOption[2] { GUILayout.Width(92f), GUILayout.Height(36f) })) { RequestAdminTextConfigPreserve(80, rule.Id, "", _realmLawInputs[rule.Id] ?? ""); } GUI.enabled = true; } GUILayout.EndHorizontal(); GUILayout.Label(rule.Description, _labelStyle, Array.Empty()); GUILayout.Label("ID: " + rule.Id + " Default: " + RealmRules.DisplayValue(rule, rule.DefaultValue) + (rule.RequiresRestart ? " Requires restart" : " Applies immediately"), _labelStyle, Array.Empty()); GUILayout.EndVertical(); } private void DrawAdminNotifications(bool canEdit) { if (string.IsNullOrEmpty(_pvpWebhookInput) && !string.IsNullOrEmpty(ClanConfig.DiscordWebhookUrl?.Value)) { _pvpWebhookInput = ClanConfig.DiscordWebhookUrl.Value; } GUILayout.BeginVertical(_boxStyle, Array.Empty()); GUILayout.Label("Notifications", _labelGoldStyle, Array.Empty()); GUILayout.Label("Global notification routing uses the existing left-side info/message panel, Recent Events, WorldEventLog and optional Discord forwarding.", _labelStyle, Array.Empty()); if (!canEdit) { GUILayout.Label("Read-only. Admin SteamID64 is required to modify notification settings.", _labelStyle, Array.Empty()); } DrawServerSettingSection("Discord Bridge"); DrawGameSettingRow("Discord Enabled", ClanConfig.DiscordEnabled?.Value ?? false, canEdit, 44, future: false); DrawGameSettingRow("Debug Notifications", ClanConfig.DebugNotifications?.Value ?? false, canEdit, 56, future: false); GUILayout.BeginVertical(_boxStyle, Array.Empty()); GUILayout.Label("Discord Webhook URL", _labelStyle, Array.Empty()); GUILayout.Label("Standard Discord webhook endpoint. It is saved on the server and should only be visible to admins.", _labelStyle, Array.Empty()); if (canEdit) { GUI.enabled = true; _pvpWebhookInput = GUILayout.TextArea(_pvpWebhookInput ?? "", 3, (GUILayoutOption[])(object)new GUILayoutOption[2] { GUILayout.Width(720f), GUILayout.Height(64f) }); GUILayout.BeginHorizontal(Array.Empty()); if (GUILayout.Button("Save Webhook", _smallButtonStyle, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(140f) })) { RequestAdminTextConfigPreserve(43, "", "", _pvpWebhookInput ?? ""); } if (GUILayout.Button("Test Webhook", _smallButtonStyle, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(140f) })) { RequestAdminTextConfigPreserve(55, "", "", "test"); } GUILayout.EndHorizontal(); } else { GUILayout.Label("Hidden for non-admin users.", _labelStyle, Array.Empty()); } GUI.enabled = true; GUILayout.EndVertical(); DrawServerSettingSection("Discord Category Forwarding"); DrawGameSettingRow("PvP", ClanConfig.NotifyDiscordPvP?.Value ?? false, canEdit, 60, future: false); DrawGameSettingRow("War", ClanConfig.NotifyDiscordWar?.Value ?? false, canEdit, 61, future: false); DrawGameSettingRow("Territory", ClanConfig.NotifyDiscordTerritory?.Value ?? false, canEdit, 62, future: false); DrawGameSettingRow("Economy", ClanConfig.NotifyDiscordEconomy?.Value ?? false, canEdit, 63, future: false); DrawGameSettingRow("Building", ClanConfig.NotifyDiscordBuilding?.Value ?? false, canEdit, 64, future: false); DrawGameSettingRow("Clan", ClanConfig.NotifyDiscordClan?.Value ?? false, canEdit, 65, future: false); DrawGameSettingRow("Election", ClanConfig.NotifyDiscordElection?.Value ?? false, canEdit, 66, future: false); DrawGameSettingRow("Event", ClanConfig.NotifyDiscordEvent?.Value ?? false, canEdit, 67, future: false); DrawGameSettingRow("Server", ClanConfig.NotifyDiscordServer?.Value ?? false, canEdit, 68, future: false); DrawGameSettingRow("Supporter", ClanConfig.NotifyDiscordSupporter?.Value ?? false, canEdit, 69, future: false); DrawGameSettingRow("Admin", ClanConfig.NotifyDiscordAdmin?.Value ?? false, canEdit, 70, future: false); DrawGameSettingRow("System", ClanConfig.NotifyDiscordSystem?.Value ?? false, canEdit, 71, future: false); GUILayout.EndVertical(); } private void DrawAdminPvpSettings(bool canEdit) { GUILayout.BeginVertical(_boxStyle, Array.Empty()); GUILayout.Label("PvP Settings", _labelGoldStyle, Array.Empty()); GUILayout.Label("These global combat rules are synchronized by the server. Only administrators can modify live rules.", _labelStyle, Array.Empty()); GUILayout.Space(4f); GUILayout.Label("Server Sync: Connected", _labelStyle, Array.Empty()); if (!canEdit) { GUILayout.Label("Read-only. Admin SteamID64 is required to modify PvP settings.", _labelStyle, Array.Empty()); } GUILayout.Space(8f); string[] labels = new string[9] { "Core PvP", "Combat Lock", "Death Rules", "Kill Feed / Discord", "Territory PvP", "Tamed / Guards", "Map / Pings", "Boss Powers", "Admin / Debug" }; DrawSectionTabs(labels, ref _adminPvpSubTab, 156f); GUILayout.Space(8f); switch (_adminPvpSubTab) { case 0: DrawAdminPvpCore(canEdit); break; case 1: DrawAdminPvpCombatLock(canEdit); break; case 2: DrawAdminPvpDeathRules(); break; case 3: DrawAdminPvpKillFeed(canEdit); break; case 4: DrawAdminPvpTerritory(canEdit); break; case 5: DrawAdminPvpTamedGuards(); break; case 6: DrawAdminPvpMapPings(); break; case 7: DrawAdminPvpBossPowers(); break; default: DrawAdminPvpDebug(); break; } GUILayout.EndVertical(); } private void DrawAdminPvpCore(bool canEdit) { GUILayout.BeginVertical(_boxStyle, Array.Empty()); DrawServerSettingSection("Core PvP"); DrawGameSettingRow("Force PvP", RealmRules.GetBool("force_pvp"), canEdit, 20, future: false); DrawGameSettingRow("Force Friendly Fire", RealmRules.GetBool("friendly_fire"), canEdit, 21, future: false); DrawGameSettingRow("Allow Player PvP Toggle", ClanConfig.AllowPlayerPvpToggle?.Value ?? false, canEdit, 22, future: false); DrawPlaceholderSettingRow("Admin PvP Bypass", "Reserved for admin-only combat override rules. No gameplay hook exists yet."); GUILayout.EndVertical(); } private void DrawAdminPvpCombatLock(bool canEdit) { GUILayout.BeginVertical(_boxStyle, Array.Empty()); DrawServerSettingSection("Combat Lock"); DrawGameSettingRow("Battle Status Enabled", ClanConfig.BattleStatusEnabled?.Value ?? false, canEdit, 23, future: false); DrawIntegerSettingRow("Combat Duration Seconds", ClanConfig.CombatDurationSeconds?.Value ?? 30, canEdit, 24, "How long PvP combat state lasts after the latest player hit."); DrawGameSettingRow("Block Teleport While In Combat", !RealmRules.GetBool("teleport_in_combat"), canEdit, 25, future: false); DrawPlaceholderSettingRow("Block Equipment Change While In Combat", "Coming in future update. Equipment hooks are not wired yet."); DrawGameSettingRow("Combat Logout Handling", RealmRules.GetBool("combat_logout"), canEdit, 27, future: false); GUILayout.EndVertical(); } private void DrawAdminPvpDeathRules() { GUILayout.BeginVertical(_boxStyle, Array.Empty()); DrawServerSettingSection("Death Rules"); DrawPlaceholderSettingRow("Skill Loss On Any Death", "Future override for death skill loss outside PvP-specific handling."); DrawPlaceholderSettingRow("Skill Loss On PvP Death", "Future PvP-specific death penalty rule."); DrawPlaceholderSettingRow("Teleport On Any Death", "Future death teleport rule."); DrawPlaceholderSettingRow("Teleport On PvP Death", "Future PvP death teleport rule."); DrawPlaceholderSettingRow("Death Grace Period Seconds", "Future protection window after respawn or death processing."); DrawPlaceholderSettingRow("Death Ping Enabled", "Future death-location ping broadcast."); DrawPlaceholderSettingRow("Death Ping Radius", "Future radius for death-location visibility."); GUILayout.EndVertical(); } private void DrawAdminPvpKillFeed(bool canEdit) { GUILayout.BeginVertical(_boxStyle, Array.Empty()); DrawServerSettingSection("Kill Feed"); DrawGameSettingRow("Kill Feed Enabled", ClanConfig.KillFeedEnabled?.Value ?? false, canEdit, 26, future: false); DrawPlaceholderSettingRow("Kill Feed Server Name", "Coming in future update. Global notification routing is handled in Admin > Notifications."); DrawPlaceholderSettingRow("PvP Kill Message Format", "Coming in future update. Current PvP feed uses the shared NotificationManager format."); DrawPlaceholderSettingRow("Combat Logout Message Format", "Coming in future update. Current combat logout notices use the shared NotificationManager format."); GUILayout.EndVertical(); } private void DrawAdminPvpTerritory(bool canEdit) { GUILayout.BeginVertical(_boxStyle, Array.Empty()); DrawServerSettingSection("Territory PvP"); DrawGameSettingRow("Territory Capture Enabled", RealmRules.GetBool("territory_capture"), canEdit, 34, future: false); DrawGameSettingRow("Clan Wars Enabled", RealmRules.GetBool("clan_wars"), canEdit, 35, future: false); DrawGameSettingRow("Resource Gathering Restriction", RealmRules.GetBool("resource_gathering_restriction"), canEdit, 30, future: false); DrawPlaceholderSettingRow("Mining Restriction", "Config entry exists, but no active mining enforcement hook was found."); DrawPlaceholderSettingRow("Woodcutting Restriction", "Config entry exists, but no active woodcutting enforcement hook was found."); DrawPlaceholderSettingRow("Building Restriction", "Config entry exists, but no active build restriction hook was found."); GUILayout.EndVertical(); } private void DrawAdminPvpTamedGuards() { GUILayout.BeginVertical(_boxStyle, Array.Empty()); DrawServerSettingSection("Tamed / Guards"); DrawPlaceholderSettingRow("Tamed Creatures Defend Territory", "Future territory-defense behavior for tamed creatures."); DrawPlaceholderSettingRow("Tamed Health Multiplier", "Future health modifier for tamed territory defenders."); DrawPlaceholderSettingRow("Tamed Damage Multiplier", "Future damage modifier for tamed territory defenders."); DrawPlaceholderSettingRow("Ward / Territory Defense Mode", "Future ward and territory-defense mode selector."); GUILayout.EndVertical(); } private void DrawAdminPvpMapPings() { GUILayout.BeginVertical(_boxStyle, Array.Empty()); DrawServerSettingSection("Map / Pings"); DrawPlaceholderSettingRow("Allow Map Pings", "Future control for map ping permissions."); DrawPlaceholderSettingRow("Allow Shout Pings", "Future control for shout-linked pings."); DrawPlaceholderSettingRow("Force Player Map Pins", "Future control for forced player map pin visibility."); DrawPlaceholderSettingRow("Arrival Messages", "Future travel arrival announcement rule."); DrawPlaceholderSettingRow("Trader / Temple Pins", "Future special-location pin visibility rule."); DrawPlaceholderSettingRow("Hide Platform Tags", "Future display privacy rule for platform tags."); GUILayout.EndVertical(); } private void DrawAdminPvpBossPowers() { GUILayout.BeginVertical(_boxStyle, Array.Empty()); DrawServerSettingSection("Boss Powers / Balance"); DrawPlaceholderSettingRow("Reset Boss Power On Change", "Future boss-power reset behavior."); DrawPlaceholderSettingRow("Boss Power Duration Seconds", "Future boss-power duration override."); DrawPlaceholderSettingRow("Boss Power Cooldown Seconds", "Future boss-power cooldown override."); GUILayout.EndVertical(); } private void DrawAdminPvpDebug() { GUILayout.BeginVertical(_boxStyle, Array.Empty()); DrawServerSettingSection("Admin / Debug"); DrawPlaceholderSettingRow("Console Mode: Disabled / Admin Only / Everyone", "Future console access mode selector."); DrawPlaceholderSettingRow("Debug PvP Logs", "Future PvP-specific debug log switch."); GUILayout.EndVertical(); } private void DrawPlaceholderSettingRow(string label, string description) { GUILayout.BeginVertical(_boxStyle, Array.Empty()); GUILayout.BeginHorizontal(Array.Empty()); GUILayout.Label("" + label + "", _labelStyle, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(250f) }); GUILayout.Label("Coming in future update", _labelStyle, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(220f) }); GUILayout.EndHorizontal(); GUILayout.Label(description, _labelStyle, Array.Empty()); GUILayout.EndVertical(); } private void DrawServerSettingSection(string title) { GUILayout.Space(6f); GUILayout.Label("" + title + "", _labelGoldStyle, Array.Empty()); } private void DrawIntegerSettingRow(string label, int value, bool canEdit, int action, string description) { GUILayout.BeginVertical(_boxStyle, Array.Empty()); GUILayout.BeginHorizontal(Array.Empty()); GUILayout.Label("" + label + "", _labelStyle, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(280f) }); GUILayout.Label("" + value + "", _labelStyle, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(90f) }); if (string.IsNullOrEmpty(_pvpCombatDurationInput)) { _pvpCombatDurationInput = value.ToString(); } GUI.enabled = canEdit; _pvpCombatDurationInput = GUILayout.TextField(_pvpCombatDurationInput ?? value.ToString(), 4, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(70f) }); if (GUILayout.Button("Save", _smallButtonStyle, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(80f) })) { RequestAdminTextConfigPreserve(action, "", "", _pvpCombatDurationInput ?? value.ToString()); } GUI.enabled = true; GUILayout.EndHorizontal(); GUILayout.Label(description, _labelStyle, Array.Empty()); GUILayout.EndVertical(); } private void DrawGameSettingRow(string label, bool enabled, bool canEdit, int action, bool future) { GUILayout.BeginVertical(_boxStyle, Array.Empty()); GUILayout.BeginHorizontal(Array.Empty()); GUILayout.Label("" + label + "", _labelStyle, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(250f) }); string text = (future ? "Coming in future update" : (enabled ? "ON" : "OFF")); string text2 = (future ? "#d0a43a" : (enabled ? "#44ff44" : "#ff4444")); GUILayout.Label("" + text + "", _labelStyle, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(165f) }); if (canEdit && !future && GUILayout.Button(enabled ? "Turn OFF" : "Turn ON", _smallButtonStyle, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(90f) })) { RequestAdminTextConfigPreserve(action, "", "", (!enabled).ToString()); } GUILayout.EndHorizontal(); GUILayout.Label(GameSettingDescription(label), _labelStyle, Array.Empty()); GUILayout.EndVertical(); } private string GameSettingDescription(string label) { return label switch { "Force PvP" => "Forces PvP mode for every connected player. Players cannot disable PvP manually.", "Force Friendly Fire" => "Forces friendly-fire rules for every connected player. Players cannot override this manually.", "Allow Player PvP Toggle" => "Allows players to use the vanilla PvP toggle when server-wide Force PvP is off.", "Battle Status Enabled" => "Tracks players who recently dealt or received player-vs-player damage.", "Block Teleport While In Combat" => "Blocks portal travel while battle status is active. Admins bypass this restriction.", "Combat Logout Handling" => "Records a Recent Events entry when a player disconnects while in PvP combat.", "Resource Gathering Restriction" => "Players can gather resources only in territories where gathering is permitted.", "Mining Restriction" => "Players can mine only in territories where mining is permitted.", "Woodcutting Restriction" => "Players can cut trees only in territories where woodcutting is permitted.", "Building Restriction" => "Players can build only in territories where building is permitted.", "Territory Capture Enabled" => "Allows territories to be captured during valid wars.", "Clan Wars Enabled" => "Allows clans to declare and fight wars.", "Freeborn Protection Enabled" => "Enables protection rules for eligible Freeborn settlements.", "Odin's Peace Enabled" => "Allows Freeborn homesteads and outposts to receive Odin's Peace protection.", "Taxes Enabled" => "Enables clan resource and coin taxes.", "Upkeep Enabled" => "Enables territory upkeep costs and pressure.", "World Events Enabled" => "Allows Forteca world events to run.", "Games Enabled" => "Allows Forteca games such as Chess, Dice and Poker.", "Debug Logging" => "Enables additional Forteca debug logs.", "Discord Enabled" => "Forwards enabled notification categories to the configured Discord webhook. In-game left-side notifications still work when this is off.", "Debug Notifications" => "Logs Discord bridge errors and notification diagnostics without exposing the webhook to players.", "PvP" => "Forward PvP notifications to Discord.", "War" => "Forward war notifications to Discord.", "Territory" => "Forward territory notifications to Discord.", "Economy" => "Forward economy notifications to Discord.", "Building" => "Forward building notifications to Discord.", "Clan" => "Forward clan notifications to Discord.", "Election" => "Forward election notifications to Discord.", "Event" => "Forward event notifications to Discord.", "Server" => "Forward server announcements to Discord.", "Supporter" => "Forward supporter notifications to Discord.", "Admin" => "Forward admin notifications to Discord.", "System" => "Forward system notifications to Discord.", "Webhook Enabled" => "Allows Forteca to post enabled notification categories to the configured Discord webhook.", "Kill Feed Enabled" => "Enables the PvP kill feed for Recent Events and notification routing.", "Send Kill Feed" => "Sends PvP kill feed messages to Discord when the webhook is enabled.", "Send Combat Logout" => "Sends combat logout notices to Discord when the webhook is enabled.", "Send War Started" => "Allows future war-start notifications to use Discord without new webhook code.", "Send War Ended" => "Allows future war-ended notifications to use Discord without new webhook code.", "Send Territory Captured" => "Allows future territory capture notifications to use Discord.", "Send Territory Lost" => "Allows future territory loss notifications to use Discord.", "Send Clan Created" => "Allows future clan creation notifications to use Discord.", "Send Clan Disbanded" => "Allows future clan disband notifications to use Discord.", "Send Elections" => "Allows future election notices to use Discord.", "Send Server Announcements" => "Allows future server announcement notices to use Discord.", _ => "Global Forteca server rule.", }; } private void DrawAdminCrew(bool canEdit) { GUILayout.BeginVertical(_boxStyle, Array.Empty()); GUILayout.Label("Admin Crew", _labelGoldStyle, Array.Empty()); GUILayout.Label("Administrators have permission to manage the server and use all administration tools. Only the Owner can add or remove administrators.", _labelStyle, Array.Empty()); GUILayout.Space(6f); GUILayout.Label("Current Administrators", _labelGoldStyle, Array.Empty()); List obj = WarfareManager.Instance?.GetAdminCrewEntries() ?? new List(); if (obj.Count == 0) { GUILayout.Label("No additional administrators.", _labelStyle, Array.Empty()); } foreach (string item in obj) { string[] array = item.Split(new char[1] { '|' }); string text = ((array.Length != 0) ? array[0] : ""); string text2 = ((array.Length > 1 && !string.IsNullOrWhiteSpace(array[1])) ? array[1] : "Admin"); string text3 = ((array.Length > 2 && !string.IsNullOrWhiteSpace(array[2])) ? array[2] : "Administrator"); bool flag = text == "76561198007625196" || text == (ClanConfig.OwnerSteamId?.Value ?? ""); GUILayout.BeginVertical(_boxStyle, Array.Empty()); GUILayout.Label("" + text2 + (flag ? " [OWNER]" : "") + "", _labelGoldStyle, Array.Empty()); DrawProfileLine("SteamID64", PublicSteamIdText(text)); DrawProfileLine("Role", text3); DrawProfileLine("Description", "No description saved."); DrawProfileLine("Date Added", "Existing entry"); GUILayout.BeginHorizontal(Array.Empty()); DrawSupporterBadgeIcons(text); if (canEdit && !flag) { GUILayout.FlexibleSpace(); if (GUILayout.Button("Edit", _smallButtonStyle, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(70f) })) { _adminSteamIdInput = text; _adminNicknameInput = text2; _adminRoleInput = text3; } if (GUILayout.Button("Remove", _smallButtonStyle, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(90f) })) { RequestAdminTextConfigPreserve(2, text, "", ""); } } GUILayout.EndHorizontal(); GUILayout.EndVertical(); } if (canEdit) { GUILayout.Space(10f); GUILayout.Label("Add Admin", _labelGoldStyle, Array.Empty()); GUILayout.Label("Player Name", _labelStyle, Array.Empty()); _adminNicknameInput = GUILayout.TextField(_adminNicknameInput ?? "", (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(260f) }); GUILayout.Label("SteamID64", _labelStyle, Array.Empty()); _adminSteamIdInput = GUILayout.TextField(_adminSteamIdInput ?? "", (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(260f) }); GUILayout.Label("Role / Description", _labelStyle, Array.Empty()); _adminRoleInput = GUILayout.TextField(_adminRoleInput ?? "", (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(420f) }); GUILayout.BeginHorizontal(Array.Empty()); if (GUILayout.Button("Add Administrator", _smallButtonStyle, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(160f) })) { RequestAdminTextConfigPreserve(1, _adminSteamIdInput, _adminNicknameInput, _adminRoleInput); } if (GUILayout.Button("Clear", _smallButtonStyle, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(90f) })) { _adminSteamIdInput = ""; _adminNicknameInput = ""; _adminRoleInput = ""; } GUILayout.EndHorizontal(); } GUILayout.EndVertical(); } private void DrawConstellationMembers(bool canEdit) { //IL_0182: Unknown result type (might be due to invalid IL or missing references) //IL_018d: Expected O, but got Unknown string[] array = new string[6] { "Admin", "Alpha Tester", "Beta Tester", "Contributor", "Subscriber", "Supporter" }; GUILayout.BeginVertical(_boxStyle, Array.Empty()); GUILayout.Label("Constellation Members", _labelGoldStyle, Array.Empty()); GUILayout.Label("Constellation Members are project supporters and contributors. Badges are cosmetic and may grant optional future bonuses.", _labelStyle, Array.Empty()); GUILayout.Space(6f); GUILayout.Label("Current Members", _labelGoldStyle, Array.Empty()); List obj = WarfareManager.Instance?.GetConstellationMemberEntries() ?? new List(); if (obj.Count == 0) { GUILayout.Label("No Constellation Members yet.", _labelStyle, Array.Empty()); } foreach (string item in obj) { string[] array2 = item.Split(new char[1] { '|' }); string text = ((array2.Length != 0) ? array2[0] : ""); string text2 = ((array2.Length > 1 && !string.IsNullOrWhiteSpace(array2[1])) ? array2[1] : "Member"); string text3 = ((array2.Length > 2) ? array2[2] : "Gold Contributor"); GUILayout.BeginVertical(_boxStyle, Array.Empty()); GUILayout.BeginHorizontal(Array.Empty()); GUILayout.BeginVertical(Array.Empty()); GUILayout.BeginHorizontal(Array.Empty()); GUILayout.Label("" + text2 + "", _labelGoldStyle, Array.Empty()); Texture2D constellationSmallBadgeTexture = GetConstellationSmallBadgeTexture(text3); if ((Object)constellationSmallBadgeTexture != (Object)null) { GUILayout.Box((Texture)(object)constellationSmallBadgeTexture, _boxStyle, (GUILayoutOption[])(object)new GUILayoutOption[2] { GUILayout.Width(32f), GUILayout.Height(32f) }); } else { GUILayout.Label("" + text3 + "", _labelStyle, Array.Empty()); } GUILayout.EndHorizontal(); DrawProfileLine("SteamID64", PublicSteamIdText(text)); DrawProfileLine("Badge", text3); DrawProfileLine("Description", "No description saved."); DrawProfileLine("Date Added", "Existing entry"); GUILayout.EndVertical(); if (canEdit) { GUILayout.FlexibleSpace(); if (GUILayout.Button("Edit", _smallButtonStyle, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(70f) })) { _constellationSteamIdInput = text; _constellationNicknameInput = text2; string value = CanonicalBadgeName(text3); _constellationBadgeIndex = ((Array.IndexOf(array, value) >= 0) ? Array.IndexOf(array, value) : 0); } if (GUILayout.Button("Remove", _smallButtonStyle, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(90f) })) { RequestAdminTextConfigPreserve(11, text, "", ""); } } GUILayout.EndHorizontal(); GUILayout.EndVertical(); } if (canEdit) { GUILayout.Space(10f); GUILayout.Label("Add Member", _labelGoldStyle, Array.Empty()); GUILayout.Label("Player Name", _labelStyle, Array.Empty()); _constellationNicknameInput = GUILayout.TextField(_constellationNicknameInput ?? "", (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(260f) }); GUILayout.Label("SteamID64", _labelStyle, Array.Empty()); _constellationSteamIdInput = GUILayout.TextField(_constellationSteamIdInput ?? "", (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(260f) }); GUILayout.Label("Badge", _labelStyle, Array.Empty()); _constellationBadgeIndex = Mathf.Clamp(_constellationBadgeIndex, 0, array.Length - 1); GUILayout.BeginHorizontal(Array.Empty()); for (int i = 0; i < array.Length; i++) { if (DrawBadgeChoiceButton(array[i], _constellationBadgeIndex == i)) { _constellationBadgeIndex = i; } } GUILayout.EndHorizontal(); GUILayout.BeginHorizontal(Array.Empty()); if (GUILayout.Button("Add Member", _smallButtonStyle, (GUILayoutOption[])(object)new GUILayoutOption[2] { GUILayout.Width(150f), GUILayout.Height(34f) })) { RequestAdminTextConfigPreserve(10, _constellationSteamIdInput, _constellationNicknameInput, array[_constellationBadgeIndex]); } if (GUILayout.Button("Clear", _smallButtonStyle, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(90f) })) { _constellationSteamIdInput = ""; _constellationNicknameInput = ""; _constellationBadgeIndex = 0; } GUILayout.EndHorizontal(); } GUILayout.EndVertical(); } private static string CanonicalBadgeName(string badge) { return (badge ?? "") switch { "Gold Contributor" => "Contributor", "Silver Subscriber" => "Subscriber", "Steel Supporter" => "Supporter", "Bronze Alpha Tester" => "Alpha Tester", _ => badge ?? "Admin", }; } private bool DrawBadgeChoiceButton(string badge, bool selected) { //IL_0046: Unknown result type (might be due to invalid IL or missing references) //IL_0051: Expected O, but got Unknown GUILayout.BeginVertical(selected ? _tabActiveStyle : _boxStyle, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(170f) }); Texture2D constellationBadgeTexture = GetConstellationBadgeTexture(badge); GUILayout.BeginHorizontal(Array.Empty()); GUILayout.FlexibleSpace(); if ((Object)constellationBadgeTexture != (Object)null) { GUILayout.Box((Texture)(object)constellationBadgeTexture, _boxStyle, (GUILayoutOption[])(object)new GUILayoutOption[2] { GUILayout.Width(150f), GUILayout.Height(150f) }); } else { GUILayout.Box(badge, _boxStyle, (GUILayoutOption[])(object)new GUILayoutOption[2] { GUILayout.Width(150f), GUILayout.Height(150f) }); } GUILayout.FlexibleSpace(); GUILayout.EndHorizontal(); bool result = GUILayout.Button(badge, selected ? _tabActiveStyle : _smallButtonStyle, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(160f) }); GUILayout.EndVertical(); return result; } private string PublicSteamIdText(string steamId) { if (WarfareManager.IsTruthy(ClanConfig.ShowPublicSteamIds?.Value)) { return "(" + steamId + ")"; } if (string.IsNullOrEmpty(steamId) || steamId.Length < 6) { return ""; } return "(" + steamId.Substring(0, 3) + "..." + steamId.Substring(steamId.Length - 3) + ")"; } private void DrawAdminGameTools(WarfareData data) { GUILayout.BeginVertical(_boxStyle, Array.Empty()); GUILayout.Label("Games / System Admin", _labelGoldStyle, Array.Empty()); int num = data.Games.Count((FehuGameTable g) => g.GameType == FehuGameType.Chess && IsActiveGameTable(g)); int num2 = data.Games.Count((FehuGameTable g) => g.GameType == FehuGameType.Dice && IsActiveGameTable(g)); int num3 = data.Games.Count((FehuGameTable g) => g.GameType == FehuGameType.Poker && IsActiveGameTable(g)); GUILayout.BeginHorizontal(Array.Empty()); DrawMiniStat("Active Chess", num.ToString()); DrawMiniStat("Active Dice", num2.ToString()); DrawMiniStat("Active Poker", num3.ToString()); DrawMiniStat("History", data.GameHistory.Count.ToString()); DrawMiniStat("Game Stats", data.GameStats.Count.ToString()); GUILayout.EndHorizontal(); GUILayout.BeginHorizontal(Array.Empty()); GUILayout.Label("Player ID", _labelStyle, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(80f) }); _adminGamePlayerIdInput = GUILayout.TextField(_adminGamePlayerIdInput ?? "", (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(180f) }); GUILayout.Label("Game/Table ID", _labelStyle, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(105f) }); _adminGameIdInput = GUILayout.TextField(_adminGameIdInput ?? "", (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(140f) }); GUILayout.FlexibleSpace(); GUILayout.EndHorizontal(); GUILayout.BeginHorizontal(Array.Empty()); DrawAdminConfirmButton("games-reset-all", "Reset All Game Stats", "Confirm Reset All Game Stats", delegate { RequestKingdomAdminActionPreserve(200, 0L); }); DrawAdminConfirmButton("games-reset-player", "Reset Player Game Stats", "Confirm Reset Player Game Stats", delegate { RequestKingdomAdminActionPreserve(201, ParseLongOrZero(_adminGamePlayerIdInput)); }); DrawAdminConfirmButton("games-history-all", "Delete All Game History", "Confirm Delete All Game History", delegate { RequestKingdomAdminActionPreserve(202, 0L); }); DrawAdminConfirmButton("games-history-selected", "Delete Selected History", "Confirm Delete Selected History", delegate { RequestKingdomAdminActionPreserve(203, ParseLongOrZero(_adminGameIdInput)); }); GUILayout.EndHorizontal(); GUILayout.BeginHorizontal(Array.Empty()); DrawAdminConfirmButton("games-chess-close", "Delete Active Chess", "Confirm Delete Active Chess", delegate { RequestKingdomAdminActionPreserve(204, 0L); }); DrawAdminConfirmButton("games-dice-close", "Delete Active Dice", "Confirm Delete Active Dice", delegate { RequestKingdomAdminActionPreserve(205, 0L); }); DrawAdminConfirmButton("games-poker-close", "Delete Active Poker", "Confirm Delete Active Poker", delegate { RequestKingdomAdminActionPreserve(206, 0L); }); DrawAdminConfirmButton("games-force-close", "Force Close Selected", "Confirm Force Close Selected", delegate { RequestKingdomAdminActionPreserve(207, ParseLongOrZero(_adminGameIdInput)); }); GUILayout.EndHorizontal(); GUILayout.BeginHorizontal(Array.Empty()); DrawAdminConfirmButton("games-refund", "Refund Stuck Escrow", "Confirm Refund Escrow", delegate { RequestKingdomAdminActionPreserve(208, ParseLongOrZero(_adminGameIdInput)); }); if (GUILayout.Button("Debug Active Game State", _smallButtonStyle, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(190f) })) { RequestKingdomAdminActionPreserve(209, 0L); } DrawAdminConfirmButton("system-clear-escrow", "Clear Stuck Escrow", "Confirm Clear Stuck Escrow", delegate { RequestKingdomAdminActionPreserve(303, 0L); }); DrawAdminConfirmButton("territory-stake-locks", "Clear Territory Stake Locks", "Confirm Clear Stake Locks", delegate { RequestKingdomAdminActionPreserve(305, 0L); }); GUILayout.EndHorizontal(); GUILayout.BeginHorizontal(Array.Empty()); if (GUILayout.Button("Force Save", _smallButtonStyle, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(140f) })) { RequestKingdomAdminActionPreserve(300, 0L); } if (GUILayout.Button("Backup Data", _smallButtonStyle, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(140f) })) { RequestKingdomAdminActionPreserve(304, 0L); } if (GUILayout.Button("Print Data Version", _smallButtonStyle, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(160f) })) { RequestKingdomAdminActionPreserve(301, 0L); } if (GUILayout.Button("Debug Sync State", _smallButtonStyle, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(160f) })) { RequestKingdomAdminActionPreserve(302, 0L); } GUILayout.EndHorizontal(); GUILayout.Label("Existing admin sections below handle clans, protected territory conflicts, homesteads, diplomacy, contracts, events and guards.", _labelStyle, Array.Empty()); GUILayout.EndVertical(); } private void DrawAdminConfirmButton(string key, string label, string confirmLabel, Action action) { bool flag = _adminConfirmAction == key; if (GUILayout.Button(flag ? confirmLabel : label, flag ? _tabActiveStyle : _smallButtonStyle, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(190f) })) { if (flag) { _adminConfirmAction = ""; action?.Invoke(); } else { _adminConfirmAction = key; } } } private static long ParseLongOrZero(string text) { if (!long.TryParse(text, out var result)) { return 0L; } return result; } private void DrawAdminTerritoryTools(WarfareData data) { GUILayout.BeginVertical(_boxStyle, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(500f) }); GUILayout.Label("Territory Tools", _labelGoldStyle, Array.Empty()); int num = 0; foreach (ClanTerritory value in data.Territories.Values) { if (num++ >= 8) { GUILayout.Label("More territories hidden in overview.", _labelStyle, Array.Empty()); break; } Clan clan = data.GetClan(value.OwnerClanId); long lastActive = 0L; if (clan != null) { lastActive = clan.LastActiveTimestamp; } else if (value.FreebornOwnerId != 0L) { lastActive = data.GetPlayer(value.FreebornOwnerId)?.FreebornLastActiveTimestamp ?? 0; } float globalMultiplier = Economy.GlobalUpkeepMultiplier((value.OwnerClanId == 0) ? 1 : data.GetClanTerritoryCount(value.OwnerClanId)); long num2 = Economy.ApplyBuildingUpkeepReduction(value, Economy.TerritoryUpkeep(value, globalMultiplier)); GUILayout.Label(string.Format("{0} [{1},{2}] | owner: {3} | {4} | income +{5} / upkeep -{6} | inactivity {7}", value.DisplayName, value.GridX, value.GridY, clan?.Name ?? value.FreebornOwnerName ?? "None", value.Type, Economy.TerritoryIncome(value), num2, InactivityStatus(lastActive)), _labelStyle, Array.Empty()); } DrawTerritoryCheckerTools(data); DrawProtectedZoneConflictTools(data); GUILayout.EndVertical(); } private void DrawTerritoryCheckerTools(WarfareData data) { //IL_0214: Unknown result type (might be due to invalid IL or missing references) GUILayout.Space(8f); GUILayout.Label("Territory Checker", _labelGoldStyle, Array.Empty()); GUILayout.BeginHorizontal(Array.Empty()); if (GUILayout.Button("Check All Territories", _smallButtonStyle, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(190f) })) { RequestKingdomAdminActionPreserve(306, 0L); } if (GUILayout.Button("Refresh Territories", _smallButtonStyle, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(210f) })) { RequestKingdomAdminActionPreserve(306, 0L); } GUILayout.EndHorizontal(); GUILayout.BeginHorizontal(Array.Empty()); if (GUILayout.Button("Refresh Territory Borders", _smallButtonStyle, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(190f) })) { RequestKingdomAdminActionPreserve(306, 0L); } if (GUILayout.Button("Refresh Labels", _smallButtonStyle, (GUILayoutOption[])(object)new GUILayoutOption[2] { GUILayout.Width(150f), GUILayout.Height(34f) })) { RequestKingdomAdminActionPreserve(306, 0L); } if (GUILayout.Button("Refresh Capitals", _smallButtonStyle, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(140f) })) { RequestKingdomAdminActionPreserve(306, 0L); } GUILayout.EndHorizontal(); GUILayout.BeginHorizontal(Array.Empty()); if (GUILayout.Button("Refresh Homesteads", _smallButtonStyle, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(170f) })) { RequestKingdomAdminActionPreserve(306, 0L); } if (GUILayout.Button("Refresh Outposts", _smallButtonStyle, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(150f) })) { RequestKingdomAdminActionPreserve(306, 0L); } GUILayout.EndHorizontal(); int num = 0; foreach (ClanTerritory value in data.Territories.Values) { if (value != null && (value.OwnerClanId != 0 || value.FreebornOwnerId != 0L) && ProtectedWorldZones.TryGetProtectedZone(value.GridPosition, out var info)) { num++; string text = data.GetClan(value.OwnerClanId)?.Name ?? value.FreebornOwnerName ?? "Unknown"; GUILayout.Label("Conflict detected | Territory: " + value.DisplayName + " | Current Owner: " + text + " | Conflict: " + (info?.Name ?? "Protected World Zone") + " | Action Required: Repair Ownership", _labelStyle, Array.Empty()); } } if (num == 0) { GUILayout.Label("No protected-zone ownership conflicts detected.", _labelStyle, Array.Empty()); } } private void DrawProtectedZoneConflictTools(WarfareData data) { //IL_00f9: Unknown result type (might be due to invalid IL or missing references) List list = new List(); foreach (ClanTerritory value in data.Territories.Values) { if (value != null && (value.OwnerClanId != 0 || value.FreebornOwnerId != 0L) && ProtectedWorldZones.IsProtected(value)) { list.Add(value); } } GUILayout.Space(8f); GUILayout.Label("Protected Zone Conflicts", _labelGoldStyle, Array.Empty()); if (GUILayout.Button("Detect Protected Zone Conflicts", _smallButtonStyle, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(240f) })) { PushLeftNotification("Protected Zone Conflicts: " + list.Count); } if (list.Count == 0) { GUILayout.Label("No protected zone conflicts detected.", _labelStyle, Array.Empty()); return; } foreach (ClanTerritory item in list) { ProtectedWorldZones.TryGetProtectedZone(item.GridPosition, out var info); string text = data.GetClan(item.OwnerClanId)?.Name ?? item.FreebornOwnerName ?? "Unknown"; GUILayout.Label(item.DisplayName + " [" + item.GridX + "," + item.GridY + "] | owner: " + text + " | " + (info?.Name ?? "Protected World Zone") + " | reason: protected WORLD zone cannot be owned", _labelStyle, Array.Empty()); } if (GUILayout.Button("Release Protected Zone Conflicts", _smallButtonStyle, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(240f) })) { RequestKingdomAdminActionPreserve(102, 0L); } } private void DrawAdminClanTools(WarfareData data) { GUILayout.BeginVertical(_boxStyle, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.ExpandWidth(true) }); GUILayout.Label("Clan Tools", _labelGoldStyle, Array.Empty()); foreach (Clan clan in new List(data.Clans.Values)) { int count = data.VassalRelations.FindAll((VassalRelation v) => v.Status == VassalStatus.Active && v.OverlordClanId == clan.ClanId).Count; string text = "None"; VassalRelation vassalRelation = data.VassalRelations.Find((VassalRelation v) => v.Status == VassalStatus.Active && v.VassalClanId == clan.ClanId); if (vassalRelation != null) { text = data.GetClan(vassalRelation.OverlordClanId)?.Name ?? vassalRelation.OverlordClanName ?? "Unknown"; } GUILayout.Label($"{clan.Name} [{clan.Tag}] | treasury {clan.Treasury} | territories {data.GetClanTerritoryCount(clan.ClanId)} | vassals {count} | overlord {text}", _labelStyle, Array.Empty()); } GUILayout.EndVertical(); } private void DrawAdminHomesteadTools(WarfareData data) { GUILayout.BeginVertical(_boxStyle, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(500f) }); GUILayout.Label("Homestead Tools", _labelGoldStyle, Array.Empty()); foreach (PlayerClanInfo item in new List(data.Players.Values)) { if (item.IsFreeborn && item.ClanId == 0) { int num = GetFreebornTerritories(data, item.PlayerId, TerritoryType.Homestead).Count + GetFreebornTerritories(data, item.PlayerId, TerritoryType.Outpost).Count; GUILayout.Label($"{item.PlayerName} | territories {num} | last active {FormatTimestamp(item.FreebornLastActiveTimestamp)} | status {InactivityStatus(item.FreebornLastActiveTimestamp)}", _labelStyle, Array.Empty()); } } GUILayout.EndVertical(); } private void DrawAdminDiplomacyTools(WarfareData data) { GUILayout.BeginVertical(_boxStyle, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.ExpandWidth(true) }); GUILayout.Label("Diplomacy Tools", _labelGoldStyle, Array.Empty()); GUILayout.Label($"Alliances: {data.Alliances.Count} | NAPs: {data.NonAggressionPacts.Count} | Vassals: {data.VassalRelations.Count} | Pending deals: {data.TerritoryTransferOffers.Count}", _labelStyle, Array.Empty()); foreach (VassalRelation vassalRelation in data.VassalRelations) { if (vassalRelation.Status == VassalStatus.Active || vassalRelation.Status == VassalStatus.Pending) { Clan clan = data.GetClan(vassalRelation.OverlordClanId); Clan clan2 = data.GetClan(vassalRelation.VassalClanId); GUILayout.Label(string.Format("{0}: {1} -> {2}", vassalRelation.Status, clan2?.Name ?? vassalRelation.VassalClanName ?? "Unknown", clan?.Name ?? vassalRelation.OverlordClanName ?? "Unknown"), _labelStyle, Array.Empty()); } } GUILayout.EndVertical(); } private void DrawAdminWorldEventTools(WarfareData data) { int count = data.WorldEvents.FindAll((WorldEvent e) => e.Status == WorldEventStatus.Active).Count; GUILayout.Label($"Active events: {count} | Total events: {data.WorldEvents.Count}", _labelStyle, Array.Empty()); GUILayout.Label("Create Test Event", _labelGoldStyle, Array.Empty()); for (int num = 0; num < AdminWorldEventTypes.Length; num += 2) { GUILayout.BeginHorizontal(Array.Empty()); for (int num2 = num; num2 < num + 2 && num2 < AdminWorldEventTypes.Length; num2++) { WorldEventType worldEventType = AdminWorldEventTypes[num2]; if (GUILayout.Button("Create " + WarfareManager.WorldEventTypeName(worldEventType), _smallButtonStyle, (GUILayoutOption[])(object)new GUILayoutOption[2] { GUILayout.Width(190f), GUILayout.Height(30f) })) { RequestWorldEventAdminPreserve(10, (int)worldEventType); } } GUILayout.EndHorizontal(); } int num3 = 0; foreach (WorldEvent item in new List(data.WorldEvents)) { if (item.Status == WorldEventStatus.Active || item.Status == WorldEventStatus.Pending) { long seconds = Math.Max(0L, item.EndTime - DateTimeOffset.UtcNow.ToUnixTimeSeconds()); GUILayout.BeginVertical(_boxStyle, Array.Empty()); GUILayout.Label("#" + item.EventId + " " + item.EventName + " | " + WarfareManager.WorldEventTypeName(item.EventType), _labelGoldStyle, Array.Empty()); GUILayout.Label("Remaining: " + FormatDuration(seconds) + " | Status: " + item.Status, _labelStyle, Array.Empty()); GUILayout.BeginHorizontal(Array.Empty()); if (GUILayout.Button("Expire Event", _smallButtonStyle, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(110f) })) { RequestWorldEventAdminPreserve(2, item.EventId); } if (GUILayout.Button("Remove Event", _smallButtonStyle, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(110f) })) { RequestWorldEventAdminPreserve(1, item.EventId); } GUILayout.EndHorizontal(); GUILayout.EndVertical(); if (++num3 >= 4) { GUILayout.Label("More events hidden.", _labelStyle, Array.Empty()); break; } } } if (count == 0) { GUILayout.Label("No active world events.", _labelStyle, Array.Empty()); } DrawWorldEventLog(data, 6); } private void DrawAdminGuardOverview(WarfareData data) { int count = data.Guards.FindAll((TerritoryGuard g) => g.Status == GuardStatus.Active).Count; int count2 = data.Guards.FindAll((TerritoryGuard g) => g.Status == GuardStatus.Disabled).Count; long num = 0L; foreach (TerritoryGuard guard in data.Guards) { if (guard.Status == GuardStatus.Active) { num += guard.UpkeepCost; } } GUILayout.Label($"Total: {data.Guards.Count} | Active: {count} | Disabled: {count2} | Upkeep: {num}c/tick", _labelStyle, Array.Empty()); int num2 = 0; foreach (ClanTerritory value in data.Territories.Values) { string key = WarfareData.GridToKey(value.GridX, value.GridY); int count3 = data.Guards.FindAll((TerritoryGuard g) => g.TerritoryId == key && g.Status != GuardStatus.Dead).Count; if (count3 > 0) { GUILayout.Label(value.DisplayName + " | guards " + count3 + " | defense " + (WarfareManager.Instance?.TerritoryDefenseScore(key) ?? 0), _labelStyle, Array.Empty()); if (++num2 >= 6) { GUILayout.Label("More guarded territories hidden.", _labelStyle, Array.Empty()); break; } } } if (data.Guards.Count == 0) { GUILayout.Label("No guards created yet.", _labelStyle, Array.Empty()); } } private void DrawAdminDangerTools(WarfareData data) { GUILayout.Space(8f); GUILayout.BeginVertical(_boxStyle, Array.Empty()); GUILayout.Label("Danger Zone", _labelGoldStyle, Array.Empty()); GUILayout.Label("Admin-only destructive tools. Use only during server testing or cleanup.", _labelStyle, Array.Empty()); _adminConfirmDanger = GUILayout.Toggle(_adminConfirmDanger, "I understand: enable destructive admin actions", _labelStyle, Array.Empty()); GUILayout.BeginHorizontal(Array.Empty()); GUILayout.BeginVertical(_boxStyle, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(650f) }); GUILayout.Label("Delete Clan", _labelGoldStyle, Array.Empty()); foreach (Clan item in new List(data.Clans.Values)) { GUILayout.BeginHorizontal(Array.Empty()); GUILayout.Label($"{item.Name} [{item.Tag}] | members {data.GetClanMemberCount(item.ClanId)} | territories {data.GetClanTerritoryCount(item.ClanId)}", _labelStyle, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(470f) }); if (_adminConfirmDanger && GUILayout.Button("Delete Clan", _smallButtonStyle, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(140f) })) { RequestKingdomAdminActionPreserve(100, item.ClanId); } else if (!_adminConfirmDanger) { GUILayout.Label("Confirm required", _labelStyle, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(140f) }); } GUILayout.EndHorizontal(); } if (data.Clans.Count == 0) { GUILayout.Label("No clans found.", _labelStyle, Array.Empty()); } GUILayout.EndVertical(); GUILayout.BeginVertical(_boxStyle, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.ExpandWidth(true) }); GUILayout.Label("Delete Homestead", _labelGoldStyle, Array.Empty()); int num = 0; foreach (PlayerClanInfo item2 in new List(data.Players.Values)) { if (!item2.IsFreeborn || item2.ClanId != 0) { continue; } int num2 = GetFreebornTerritories(data, item2.PlayerId, TerritoryType.Homestead).Count + GetFreebornTerritories(data, item2.PlayerId, TerritoryType.Outpost).Count; if (num2 > 0) { GUILayout.BeginHorizontal(Array.Empty()); GUILayout.Label($"{item2.PlayerName} | homestead/outposts {num2}", _labelStyle, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(360f) }); if (_adminConfirmDanger && GUILayout.Button("Delete Homestead", _smallButtonStyle, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(160f) })) { RequestKingdomAdminActionPreserve(101, item2.PlayerId); } else if (!_adminConfirmDanger) { GUILayout.Label("Confirm required", _labelStyle, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(160f) }); } GUILayout.EndHorizontal(); num++; } } if (num == 0) { GUILayout.Label("No Freeborn homesteads found.", _labelStyle, Array.Empty()); } GUILayout.EndVertical(); GUILayout.EndHorizontal(); GUILayout.EndVertical(); } private void DrawFreebornTerritory(WarfareData data, PlayerClanInfo info) { //IL_004d: Unknown result type (might be due to invalid IL or missing references) //IL_0066: Unknown result type (might be due to invalid IL or missing references) //IL_006b: Unknown result type (might be due to invalid IL or missing references) List list = new List(); list.AddRange(GetFreebornTerritories(data, info.PlayerId, TerritoryType.Homestead)); list.AddRange(GetFreebornTerritories(data, info.PlayerId, TerritoryType.Outpost)); if (list.Count == 0) { DrawEmptyState("No Homestead", "You hold no Freeborn territory. Claim a Homestead from the map."); } else { _scrollFreebornTerritory = GUILayout.BeginScrollView(_scrollFreebornTerritory, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.ExpandHeight(true) }); float num = (((Rect)(ref _windowRect)).width - 92f) / 2f; for (int i = 0; i < list.Count; i += 2) { GUILayout.BeginHorizontal(Array.Empty()); DrawFreebornTerritoryCard(list[i], num); if (i + 1 < list.Count) { DrawFreebornTerritoryCard(list[i + 1], num); } else { GUILayout.Space(num + 8f); } GUILayout.EndHorizontal(); } GUILayout.EndScrollView(); } ConfigEntry allowFreebornAbandonHomestead = ClanConfig.AllowFreebornAbandonHomestead; if (allowFreebornAbandonHomestead != null && allowFreebornAbandonHomestead.Value && GetFreebornTerritories(data, info.PlayerId, TerritoryType.Homestead).Count > 0) { GUILayout.BeginHorizontal(_boxStyle, Array.Empty()); if (!_confirmAbandonHomestead) { if (GUILayout.Button("Abandon Homestead / Find New Home", _dangerButtonStyle, Array.Empty())) { _confirmAbandonHomestead = true; } } else { GUILayout.Label("Confirm abandon?", _labelStyle, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(150f) }); if (GUILayout.Button("Confirm", _smallButtonStyle, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(90f) })) { WarfareRPC.Instance?.RequestAbandonFreebornHomestead(); _confirmAbandonHomestead = false; } if (GUILayout.Button("Cancel", _smallButtonStyle, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(80f) })) { _confirmAbandonHomestead = false; } } GUILayout.EndHorizontal(); } GUILayout.BeginVertical(_boxStyle, Array.Empty()); if (!_confirmLeaveFreeborn) { if (GUILayout.Button("Leave Freeborn", _dangerButtonStyle, Array.Empty())) { _confirmLeaveFreeborn = true; } } else { GUILayout.Label("Leave Freeborn?\nYou will no longer be treated as an independent Freeborn.\nAll Freeborn homestead and territory data will be removed.\nYou may join or create a clan after leaving this path.", _labelStyle, Array.Empty()); GUILayout.BeginHorizontal(Array.Empty()); if (GUILayout.Button("Confirm", _smallButtonStyle, (GUILayoutOption[])(object)new GUILayoutOption[2] { GUILayout.Width(120f), GUILayout.Height(34f) })) { WarfareRPC.Instance?.RequestLeaveFreebornLife(); _confirmLeaveFreeborn = false; } if (GUILayout.Button("Cancel", _smallButtonStyle, (GUILayoutOption[])(object)new GUILayoutOption[2] { GUILayout.Width(120f), GUILayout.Height(34f) })) { _confirmLeaveFreeborn = false; } GUILayout.EndHorizontal(); } GUILayout.EndVertical(); } private void DrawFreebornTerritoryCard(ClanTerritory item, float width) { //IL_01c8: Unknown result type (might be due to invalid IL or missing references) //IL_020e: Unknown result type (might be due to invalid IL or missing references) GUILayout.BeginVertical(_boxStyle, (GUILayoutOption[])(object)new GUILayoutOption[2] { GUILayout.Width(width), GUILayout.MinHeight(118f) }); GUILayout.Label($"{item.DisplayName} ({item.GridX},{item.GridY})", _labelGoldStyle, Array.Empty()); GUILayout.Label($"{item.Type} Size: {item.SizeLevel} Value: {Economy.ValueTierName(item.BiomeValueTier)}", _labelStyle, Array.Empty()); GUILayout.Label($"State: {item.State} HP: {Mathf.RoundToInt(item.HP)}/{Mathf.RoundToInt(item.MaxHP)} Income: {Economy.TerritoryIncome(item)}/tick", _labelStyle, Array.Empty()); GUILayout.BeginHorizontal(Array.Empty()); GUILayout.Label("Odin's Peace: " + ((!item.OdinsPeaceActive) ? "Inactive" : ((item.OdinsPeaceChallengeEndTimestamp > 0) ? "Challenged" : "Active")), _labelStyle, Array.Empty()); GUILayout.FlexibleSpace(); int num = ClanConfig.MaxTerritorySizeLevel?.Value ?? 5; GUI.enabled = item.SizeLevel < num; if (GUILayout.Button((item.SizeLevel < num) ? $"Upgrade ({Economy.TerritoryUpgradeCost(item.SizeLevel)}c)" : "Max Tier", _smallButtonStyle, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(140f) })) { WarfareRPC.Instance?.RequestUpgradeTerritory(item.GridPosition); } GUI.enabled = true; if (GUILayout.Button("Discover (3000c)", _infoButtonStyle, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(140f) })) { WarfareRPC.Instance?.RequestDiscoverTerritory(item.GridPosition); } GUILayout.EndHorizontal(); GUILayout.EndVertical(); } private void DrawFreebornEconomy(WarfareData data, PlayerClanInfo info) { List freebornTerritories = GetFreebornTerritories(data, info.PlayerId, TerritoryType.Homestead); List freebornTerritories2 = GetFreebornTerritories(data, info.PlayerId, TerritoryType.Outpost); List list = new List(freebornTerritories); list.AddRange(freebornTerritories2); float globalMultiplier = Economy.GlobalUpkeepMultiplier(list.Count); long num = 0L; long num2 = 0L; long num3 = 0L; foreach (ClanTerritory item in freebornTerritories) { num += Economy.TerritoryIncome(item); num3 += Economy.ApplyBuildingUpkeepReduction(item, Economy.TerritoryUpkeep(item, globalMultiplier)); } foreach (ClanTerritory item2 in freebornTerritories2) { num2 += Economy.TerritoryIncome(item2); num3 += Economy.ApplyBuildingUpkeepReduction(item2, Economy.TerritoryUpkeep(item2, globalMultiplier)); } long num4 = num + num2; GUILayout.BeginHorizontal(Array.Empty()); DrawSummaryCard("Gross", "+" + num4 + "/tick", 220f, UiTone.Info); DrawSummaryCard("Upkeep", "-" + num3 + "/tick", 220f, UiTone.Cost); DrawSummaryCard("Net", ColoredCoins(num4 - num3) + "/tick", 220f, UiTone.Info); GUILayout.EndHorizontal(); GUILayout.BeginHorizontal(Array.Empty()); GUILayout.BeginVertical(_infoPanelStyle, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(360f) }); GUILayout.Label("Income Sources", CardTitleStyle(UiTone.Info), Array.Empty()); DrawMiniStat("Homestead", "+" + num + "/tick"); DrawMiniStat("Outposts", "+" + num2 + "/tick"); DrawMiniStat("Total", "+" + num4 + "/tick"); GUILayout.EndVertical(); GUILayout.BeginVertical(_costPanelStyle, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(360f) }); GUILayout.Label("Upkeep", CardTitleStyle(UiTone.Cost), Array.Empty()); DrawMiniStat("Territories", "-" + num3 + "/tick"); DrawMiniStat("Multiplier", "x" + globalMultiplier.ToString("F2") + " (" + EconomyBandColored(list.Count) + ")"); DrawMiniStat("Tick", "every " + (ClanConfig.UpkeepTickMinutes?.Value ?? 30) + " minutes"); GUILayout.EndVertical(); GUILayout.BeginVertical(_positivePanelStyle, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.ExpandWidth(true) }); GUILayout.Label("Summary", CardTitleStyle(UiTone.Positive), Array.Empty()); DrawMiniStat("Treasury", "" + info.FreebornTreasury + " coins"); DrawMiniStat("Territories", list.Count.ToString()); DrawMiniStat("Economy", EconomyBandColored(list.Count)); if (list.Count == 0) { GUILayout.Label("No Freeborn territory yet. Claim a Homestead to start income and upkeep tracking.", _labelStyle, Array.Empty()); } GUILayout.EndVertical(); GUILayout.EndHorizontal(); } private void DrawFreebornVault(WarfareData data, PlayerClanInfo info) { ConfigEntry enableFreebornVault = ClanConfig.EnableFreebornVault; if (enableFreebornVault == null || !enableFreebornVault.Value) { return; } bool flag = WarfareManager.Instance.IsVaultOpen(); string message; bool flag2 = IsVaultLocationValid(info, coinDeposit: false, out message); string message2; bool flag3 = IsVaultLocationValid(info, coinDeposit: true, out message2); List freebornVault = data.GetFreebornVault(info.PlayerId); int num = WarfareManager.Instance?.FreebornVaultCapacity(info) ?? 20; GUILayout.BeginVertical(_boxStyle, Array.Empty()); GUILayout.BeginVertical(_boxStyle, Array.Empty()); GUILayout.Label("Homestead Vault Summary", _labelGoldStyle, Array.Empty()); GUILayout.Label("Status: " + (flag ? "Open" : "Closed") + $" Tier: {info.FreebornVaultTier} Capacity: {num} stacks", _labelStyle, Array.Empty()); GUILayout.Label($"Vault Usage: {freebornVault.Count}/{num} stacks", _labelStyle, Array.Empty()); DrawCapacityBar(freebornVault.Count, num, 520f); GUILayout.EndVertical(); DrawVaultPermissionSummary(depositCoins: true, depositItems: true, flag3, flag2); if (!flag2) { GUILayout.Label(message, _labelStyle, Array.Empty()); } if (!flag3 && message2 != message) { GUILayout.Label(message2, _labelStyle, Array.Empty()); } GUILayout.BeginHorizontal(Array.Empty()); GUILayout.BeginVertical(_boxStyle, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(540f) }); GUILayout.Label("Player Inventory", _labelGoldStyle, Array.Empty()); GUI.enabled = true; DrawInventoryVaultPicker(); GUILayout.Label("Selected: " + (string.IsNullOrEmpty(_selectedInventoryVaultName) ? "None" : _selectedInventoryVaultName) + "", _labelStyle, Array.Empty()); GUILayout.BeginHorizontal(Array.Empty()); GUILayout.Label("Qty", _labelStyle, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(36f) }); _vaultItemAmountInput = GUILayout.TextField(_vaultItemAmountInput, 4, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(54f) }); int result; int num2 = (int.TryParse(_vaultItemAmountInput, out result) ? result : 0); GUI.enabled = flag && flag2 && !string.IsNullOrEmpty(_selectedInventoryVaultPrefab) && num2 > 0 && num2 <= _selectedInventoryVaultStack && (freebornVault.Count < num || freebornVault.Find((ClanVaultItem v) => v.PrefabName == _selectedInventoryVaultPrefab) != null); if (GUILayout.Button("Deposit Selected Item", _buttonStyle, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Height(38f) })) { WarfareRPC.Instance?.DepositSpecificInventoryItemToClanVault(0, _selectedInventoryVaultPrefab, _selectedInventoryVaultName, num2, _selectedInventoryVaultSlotX, _selectedInventoryVaultSlotY); ClearInventoryVaultSelection(); } GUI.enabled = true; GUILayout.EndHorizontal(); GUILayout.EndVertical(); GUILayout.BeginVertical(_boxStyle, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(540f) }); GUILayout.Label("Vault Contents", _labelGoldStyle, Array.Empty()); DrawVaultContentsGrid(freebornVault, freeborn: true); GUILayout.Label("Selected: " + (string.IsNullOrEmpty(_selectedVaultPrefab) ? "None" : WarfareRPC.CleanItemDisplayName(_selectedVaultPrefab)) + "", _labelStyle, Array.Empty()); GUILayout.BeginHorizontal(Array.Empty()); GUILayout.Label("Qty", _labelStyle, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(36f) }); _vaultWithdrawAmountInput = GUILayout.TextField(_vaultWithdrawAmountInput, 4, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(54f) }); int result2; int num3 = (int.TryParse(_vaultWithdrawAmountInput, out result2) ? result2 : 0); GUI.enabled = flag && flag2 && !string.IsNullOrEmpty(_selectedVaultPrefab) && num3 > 0; if (GUILayout.Button("Withdraw Selected Item", _buttonStyle, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Height(38f) })) { WarfareRPC.Instance?.RequestWithdrawVaultItem(0, _selectedVaultPrefab, num3); _selectedVaultPrefab = ""; } GUI.enabled = true; GUILayout.EndHorizontal(); GUILayout.EndVertical(); GUILayout.EndHorizontal(); GUILayout.BeginVertical(_boxStyle, Array.Empty()); GUILayout.Label("Coins", _labelGoldStyle, Array.Empty()); GUILayout.Label("Your coins: " + CountInventoryCoins() + "", _labelStyle, Array.Empty()); GUILayout.BeginHorizontal(Array.Empty()); GUILayout.Label("Amount:", _labelStyle, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(64f) }); _vaultCoinAmountInput = GUILayout.TextField(_vaultCoinAmountInput, 9, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(90f) }); int result3; int num4 = (int.TryParse(_vaultCoinAmountInput, out result3) ? result3 : 0); int num5; if (flag3) { if (!flag) { ConfigEntry allowCoinDepositWhenVaultClosed = ClanConfig.AllowCoinDepositWhenVaultClosed; if (allowCoinDepositWhenVaultClosed == null || !allowCoinDepositWhenVaultClosed.Value) { goto IL_0651; } } num5 = ((num4 > 0) ? 1 : 0); goto IL_057d; } goto IL_0651; IL_057d: GUI.enabled = (byte)num5 != 0; if (GUILayout.Button("Deposit Coins", _buttonStyle, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Height(38f) })) { WarfareRPC.Instance?.RequestDepositVaultCoins(0, num4); } GUI.enabled = flag && flag3 && num4 > 0 && info.FreebornTreasury >= num4; if (GUILayout.Button("Withdraw Coins", _buttonStyle, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Height(38f) })) { WarfareRPC.Instance?.RequestWithdrawVaultCoins(0, num4); } GUI.enabled = true; GUILayout.EndHorizontal(); GUILayout.EndVertical(); if (!flag) { GUILayout.Label("Vault is currently closed.", _labelStyle, Array.Empty()); } GUILayout.EndVertical(); return; IL_0651: num5 = 0; goto IL_057d; } private void DrawFreebornWar(WarfareData data, PlayerClanInfo info) { List freebornTerritories = GetFreebornTerritories(data, info.PlayerId, TerritoryType.Homestead); freebornTerritories.AddRange(GetFreebornTerritories(data, info.PlayerId, TerritoryType.Outpost)); GUILayout.BeginVertical(_boxStyle, Array.Empty()); GUILayout.Label("Warfare Status", _labelGoldStyle, Array.Empty()); GUILayout.Label("Freeborn territory can be challenged through Odin's Peace before capture. Watch Homestead and Outpost timers here.", _labelStyle, Array.Empty()); GUILayout.EndVertical(); if (freebornTerritories.Count == 0) { GUILayout.Label("No Freeborn territory to defend yet.", _labelStyle, Array.Empty()); return; } foreach (ClanTerritory item in freebornTerritories) { GUILayout.BeginHorizontal(_boxStyle, Array.Empty()); GUILayout.BeginVertical((GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(360f) }); GUILayout.Label("" + item.DisplayName + "", _labelGoldStyle, Array.Empty()); DrawMiniStat("Type", item.Type.ToString()); DrawMiniStat("State", StateColored(item.State)); GUILayout.EndVertical(); GUILayout.BeginVertical((GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(360f) }); DrawMiniStat("Odin's Peace", (!item.OdinsPeaceActive) ? "Inactive" : ((item.OdinsPeaceChallengeEndTimestamp > 0) ? "Challenged" : "Active")); if (item.OdinsPeaceChallengeEndTimestamp > 0) { long num = Math.Max(0L, item.OdinsPeaceChallengeEndTimestamp - WarfareManager.Now()); DrawMiniStat("Challenge Timer", num / 3600 + "h " + num % 3600 / 60 + "m"); } else { DrawMiniStat("Challenge Timer", "None"); } GUILayout.EndVertical(); GUILayout.FlexibleSpace(); GUILayout.EndHorizontal(); } } private void DrawFreebornBounty(WarfareData data, PlayerClanInfo info) { //IL_00c3: Unknown result type (might be due to invalid IL or missing references) //IL_00dc: Unknown result type (might be due to invalid IL or missing references) //IL_00e1: Unknown result type (might be due to invalid IL or missing references) GUILayout.BeginVertical(_boxStyle, Array.Empty()); GUILayout.Label($"Your bounty: {info.Bounty} coins", _labelGoldStyle, Array.Empty()); GUILayout.Label($"Bounty funds use Freeborn Treasury: {info.FreebornTreasury} coins", _labelStyle, Array.Empty()); GUILayout.BeginHorizontal(Array.Empty()); GUILayout.Label("Amount:", _labelStyle, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(70f) }); _bountyInput = GUILayout.TextField(_bountyInput, 9, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(90f) }); GUILayout.EndHorizontal(); GUILayout.EndVertical(); _scrollB = GUILayout.BeginScrollView(_scrollB, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.ExpandHeight(true) }); foreach (PlayerClanInfo value in data.Players.Values) { if (value.PlayerId != info.PlayerId) { GUILayout.BeginHorizontal(_boxStyle, Array.Empty()); GUILayout.Label("" + value.PlayerName + "", _labelGoldStyle, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(260f) }); GUILayout.Label($"Bounty: {value.Bounty}c", _labelStyle, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(160f) }); GUILayout.FlexibleSpace(); if (GUILayout.Button("Place", _smallButtonStyle, (GUILayoutOption[])(object)new GUILayoutOption[2] { GUILayout.Width(90f), GUILayout.Height(38f) })) { long result; long amount = (long.TryParse(_bountyInput, out result) ? result : 0); WarfareRPC.Instance?.RequestPlaceBounty(value.PlayerId, 0, amount); } GUILayout.EndHorizontal(); } } GUILayout.EndScrollView(); } private void DrawFreebornFriendships(WarfareData data, PlayerClanInfo info) { GUILayout.BeginVertical(_boxStyle, Array.Empty()); GUILayout.Label("Freeborn Friendships", _labelGoldStyle, Array.Empty()); GUILayout.Label($"Friends: {info.FreebornFriends?.Count ?? 0}/{ClanConfig.FreebornMaxFriends?.Value ?? 2}", _labelStyle, Array.Empty()); GUILayout.BeginHorizontal(Array.Empty()); GUILayout.Label("Player ID / SteamID:", _labelStyle, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(130f) }); _freebornFriendTarget = GUILayout.TextField(_freebornFriendTarget ?? "", 20, GUI.skin.textField, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(220f) }); if (GUILayout.Button("Add Friend", _smallButtonStyle, (GUILayoutOption[])(object)new GUILayoutOption[2] { GUILayout.Width(120f), GUILayout.Height(34f) }) && long.TryParse(_freebornFriendTarget, out var result)) { WarfareRPC.Instance?.RequestFreebornFriend(result, 1); } GUILayout.EndHorizontal(); GUILayout.Space(8f); List freebornTerritories = GetFreebornTerritories(data, info.PlayerId, TerritoryType.Homestead); List freebornTerritories2 = GetFreebornTerritories(data, info.PlayerId, TerritoryType.Outpost); int num = 0; foreach (long item in info.AcceptedClanFounderInvites ?? new List()) { PlayerClanInfo player = data.GetPlayer(item); if (player != null && player.IsFreeborn && player.ClanId == 0) { num++; } } GUILayout.Label("Found Clan", _labelGoldStyle, Array.Empty()); GUILayout.Label("Requires: Homestead, one additional territory, and two accepted Freeborn founding invitations.", _labelStyle, Array.Empty()); GUILayout.Label($"Status: Homestead {freebornTerritories.Count}/1 | Additional territories {freebornTerritories2.Count}/1 | Accepted founders {num}/2", _labelStyle, Array.Empty()); GUILayout.BeginHorizontal(Array.Empty()); GUILayout.Label("Invite Freeborn IDs:", _labelStyle, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(120f) }); _foundClanInviteTargetA = GUILayout.TextField(_foundClanInviteTargetA ?? "", 20, GUI.skin.textField, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(160f) }); if (GUILayout.Button("Invite", _smallButtonStyle, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(70f) }) && long.TryParse(_foundClanInviteTargetA, out var result2)) { WarfareRPC.Instance?.RequestFreebornFriend(result2, 10); } _foundClanInviteTargetB = GUILayout.TextField(_foundClanInviteTargetB ?? "", 20, GUI.skin.textField, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(160f) }); if (GUILayout.Button("Invite", _smallButtonStyle, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(70f) }) && long.TryParse(_foundClanInviteTargetB, out var result3)) { WarfareRPC.Instance?.RequestFreebornFriend(result3, 10); } GUILayout.EndHorizontal(); if (info.PendingClanFounderInvites != null && info.PendingClanFounderInvites.Count > 0) { GUILayout.Label("Founding Invitations", _labelGoldStyle, Array.Empty()); foreach (long pendingClanFounderInvite in info.PendingClanFounderInvites) { PlayerClanInfo player2 = data.GetPlayer(pendingClanFounderInvite); GUILayout.BeginHorizontal(_boxStyle, Array.Empty()); GUILayout.Label((player2?.PlayerName ?? "Freeborn") + " wants to found a Clan. Join founding members?", _labelStyle, Array.Empty()); if (GUILayout.Button("YES", _smallButtonStyle, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(70f) })) { WarfareRPC.Instance?.RequestFreebornFriend(pendingClanFounderInvite, 11); } if (GUILayout.Button("NO", _smallButtonStyle, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(70f) })) { WarfareRPC.Instance?.RequestFreebornFriend(pendingClanFounderInvite, 12); } GUILayout.EndHorizontal(); } } GUI.enabled = freebornTerritories.Count > 0 && freebornTerritories2.Count > 0 && num >= 2; if (GUILayout.Button("Found Clan", _buttonStyle, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Height(38f) })) { ClanSelectionUI.Instance?.OpenCreate(); } GUI.enabled = true; GUILayout.EndVertical(); GUILayout.BeginHorizontal(Array.Empty()); GUILayout.BeginVertical(_boxStyle, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(520f) }); GUILayout.Label("Friends", _labelGoldStyle, Array.Empty()); if (info.FreebornFriends == null || info.FreebornFriends.Count == 0) { GUILayout.Label("No Freeborn friends.", _labelStyle, Array.Empty()); } else { foreach (long freebornFriend in info.FreebornFriends) { PlayerClanInfo player3 = data.GetPlayer(freebornFriend); GUILayout.BeginHorizontal(_boxStyle, Array.Empty()); GUILayout.Label((player3?.PlayerName ?? "Player") + $" ({freebornFriend})", _labelStyle, Array.Empty()); if (GUILayout.Button("Remove", _smallButtonStyle, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(80f) })) { WarfareRPC.Instance?.RequestFreebornFriend(freebornFriend, 3); } GUILayout.EndHorizontal(); } } GUILayout.EndVertical(); GUILayout.BeginVertical(_boxStyle, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(520f) }); GUILayout.Label("Permissions", _labelGoldStyle, Array.Empty()); GUILayout.Label("Gathering: " + PermissionColored(ClanConfig.AllowFriendGathering?.Value ?? false), _labelStyle, Array.Empty()); GUILayout.Label("Building: " + PermissionColored(ClanConfig.AllowFriendBuilding?.Value ?? false), _labelStyle, Array.Empty()); GUILayout.Label("Containers: " + PermissionColored(ClanConfig.AllowFriendContainers?.Value ?? false), _labelStyle, Array.Empty()); if (info.PendingFreebornFriendRequests != null && info.PendingFreebornFriendRequests.Count > 0) { GUILayout.Space(6f); GUILayout.Label("Requests", _labelGoldStyle, Array.Empty()); foreach (long pendingFreebornFriendRequest in info.PendingFreebornFriendRequests) { PlayerClanInfo player4 = data.GetPlayer(pendingFreebornFriendRequest); GUILayout.BeginHorizontal(_boxStyle, Array.Empty()); GUILayout.Label((player4?.PlayerName ?? "Player") + $" ({pendingFreebornFriendRequest})", _labelStyle, Array.Empty()); if (GUILayout.Button("Accept", _smallButtonStyle, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(80f) })) { WarfareRPC.Instance?.RequestFreebornFriend(pendingFreebornFriendRequest, 2); } if (GUILayout.Button("Remove", _smallButtonStyle, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(80f) })) { WarfareRPC.Instance?.RequestFreebornFriend(pendingFreebornFriendRequest, 3); } GUILayout.EndHorizontal(); } } GUILayout.EndVertical(); GUILayout.EndHorizontal(); } private string PermissionColored(bool allowed) { if (!allowed) { return "Blocked"; } return "Allowed"; } private string StateColored(TerritoryState state) { string text = state.ToString(); return state switch { TerritoryState.Normal => "" + text + "", TerritoryState.Attacked => "" + text + "", _ => "" + text + "", }; } private string CorruptionColored(int value, int max) { string text = value + "/" + max; if (value <= 0) { return "" + text + ""; } if (value < max) { return "" + text + ""; } return "" + text + ""; } private void DrawClanLinksEditor(Clan clan) { if (_linksClanId != clan.ClanId) { _linksClanId = clan.ClanId; _clanDiscordInput = clan.ClanDiscordUrl ?? ""; _clanWebsiteInput = clan.ClanWebsiteUrl ?? ""; _clanSteamGroupInput = clan.ClanSteamGroupUrl ?? ""; _clanCustomUrlInput = clan.ClanCustomUrl ?? ""; _clanCustomLabelInput = clan.ClanCustomLabel ?? ""; _clanCustomUrl2Input = clan.ClanCustomUrl2 ?? ""; _clanCustomLabel2Input = clan.ClanCustomLabel2 ?? ""; } GUILayout.BeginVertical(_boxStyle, Array.Empty()); GUILayout.Label("Clan Links", _labelGoldStyle, Array.Empty()); DrawLinkInputRow("Discord:", ref _clanDiscordInput, ClanConfig.ClanDiscordUrlMaxLength?.Value ?? 200); DrawLinkInputRow("Website:", ref _clanWebsiteInput, ClanConfig.ClanWebsiteUrlMaxLength?.Value ?? 200); DrawLinkInputRow("Steam group:", ref _clanSteamGroupInput, ClanConfig.ClanSteamGroupUrlMaxLength?.Value ?? 200); DrawLinkInputRow("Custom label 1:", ref _clanCustomLabelInput, ClanConfig.ClanCustomLabelMaxLength?.Value ?? 40); DrawLinkInputRow("Custom link 1:", ref _clanCustomUrlInput, ClanConfig.ClanCustomUrlMaxLength?.Value ?? 200); DrawLinkInputRow("Custom label 2:", ref _clanCustomLabel2Input, ClanConfig.ClanCustomLabelMaxLength?.Value ?? 40); DrawLinkInputRow("Custom link 2:", ref _clanCustomUrl2Input, ClanConfig.ClanCustomUrlMaxLength?.Value ?? 200); if (GUILayout.Button("Save Links", _smallButtonStyle, (GUILayoutOption[])(object)new GUILayoutOption[2] { GUILayout.Width(120f), GUILayout.Height(34f) })) { clan.ClanDiscordUrl = _clanDiscordInput ?? ""; clan.ClanWebsiteUrl = _clanWebsiteInput ?? ""; clan.ClanSteamGroupUrl = _clanSteamGroupInput ?? ""; clan.ClanCustomUrl = _clanCustomUrlInput ?? ""; clan.ClanCustomLabel = _clanCustomLabelInput ?? ""; clan.ClanCustomUrl2 = _clanCustomUrl2Input ?? ""; clan.ClanCustomLabel2 = _clanCustomLabel2Input ?? ""; WarfareRPC.Instance?.RequestSetClanLinks(_clanDiscordInput, _clanWebsiteInput, _clanSteamGroupInput, _clanCustomUrlInput, _clanCustomLabelInput, _clanCustomUrl2Input, _clanCustomLabel2Input); } GUILayout.EndVertical(); } private void DrawLinkInputRow(string label, ref string value, int maxLength) { GUILayout.BeginHorizontal(Array.Empty()); GUILayout.Label(label, _labelStyle, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(116f) }); value = GUILayout.TextField(value ?? "", maxLength, GUI.skin.textField, (GUILayoutOption[])(object)new GUILayoutOption[2] { GUILayout.Height(22f), GUILayout.ExpandWidth(true) }); GUILayout.EndHorizontal(); } private void DrawClanEmblemBrowser(Clan clan) { IReadOnlyList categories = LocalEmblemCache.GetCategories(); if (categories.Count == 0) { GUILayout.Label("No local emblems found.", _labelStyle, Array.Empty()); return; } if (_emblemCategoryIndex < 0 || _emblemCategoryIndex >= categories.Count) { _emblemCategoryIndex = 0; _emblemPage = 0; } if (categories.Count > 1 && string.Equals(categories[_emblemCategoryIndex], "Root", StringComparison.OrdinalIgnoreCase)) { for (int i = 0; i < categories.Count; i++) { if (!string.Equals(categories[i], "Root", StringComparison.OrdinalIgnoreCase)) { _emblemCategoryIndex = i; _emblemPage = 0; break; } } } IReadOnlyList names = LocalEmblemCache.GetNames(categories[_emblemCategoryIndex]); int num = 10; int num2 = 4; int num3 = num * num2; int num4 = Mathf.Max(1, Mathf.CeilToInt((float)names.Count / (float)num3)); if (_emblemPage >= num4) { _emblemPage = num4 - 1; } GUILayout.BeginVertical(_boxStyle, Array.Empty()); GUILayout.BeginHorizontal(_boxStyle, Array.Empty()); GUILayout.Label("Category", _labelGoldStyle, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(64f) }); int num5 = 0; foreach (string item in categories) { if (!string.Equals(item, "Root", StringComparison.OrdinalIgnoreCase) || categories.Count <= 1) { int num6 = IndexOfCategory(categories, item); if (GUILayout.Button(item, (num6 == _emblemCategoryIndex) ? _tabActiveStyle : _smallButtonStyle, (GUILayoutOption[])(object)new GUILayoutOption[2] { GUILayout.Width(82f), GUILayout.Height(30f) })) { _emblemCategoryIndex = num6; _emblemPage = 0; } num5++; if (num5 >= 8) { break; } } } GUILayout.FlexibleSpace(); GUILayout.Label($"Page {_emblemPage + 1} / {num4}", _labelStyle, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(90f) }); GUI.enabled = _emblemPage > 0; if (GUILayout.Button("Previous", _smallButtonStyle, (GUILayoutOption[])(object)new GUILayoutOption[2] { GUILayout.Width(82f), GUILayout.Height(30f) })) { _emblemPage--; } GUI.enabled = _emblemPage + 1 < num4; if (GUILayout.Button("Next", _smallButtonStyle, (GUILayoutOption[])(object)new GUILayoutOption[2] { GUILayout.Width(82f), GUILayout.Height(30f) })) { _emblemPage++; } GUI.enabled = true; if (GUILayout.Button("Apply Emblem", _buttonStyle, (GUILayoutOption[])(object)new GUILayoutOption[2] { GUILayout.Width(130f), GUILayout.Height(38f) })) { clan.ClanEmblemName = _pendingClanEmblemName; WarfareRPC.Instance?.RequestSetClanEmblemName(_pendingClanEmblemName); _editingClanEmblem = false; } if (GUILayout.Button("Cancel", _smallButtonStyle, (GUILayoutOption[])(object)new GUILayoutOption[2] { GUILayout.Width(82f), GUILayout.Height(30f) })) { _editingClanEmblem = false; } GUILayout.EndHorizontal(); GUILayout.BeginHorizontal(Array.Empty()); GUILayout.BeginVertical(_boxStyle, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(760f) }); int num7 = _emblemPage * num3; int num8 = Mathf.Min(num7 + num3, names.Count); for (int j = 0; j < num2; j++) { GUILayout.BeginHorizontal(Array.Empty()); for (int k = 0; k < num; k++) { int num9 = num7 + j * num + k; if (num9 < num8) { string text = names[num9]; if (GUILayout.Button((Texture)(object)LocalEmblemCache.GetTexture(text), string.Equals(_pendingClanEmblemName, text, StringComparison.OrdinalIgnoreCase) ? _tabActiveStyle : _smallButtonStyle, (GUILayoutOption[])(object)new GUILayoutOption[2] { GUILayout.Width(68f), GUILayout.Height(68f) })) { _pendingClanEmblemName = text; } } else { GUILayout.Space(68f); } } GUILayout.EndHorizontal(); } GUILayout.Label("Icon grid: 10 columns x 4 rows, 68x68 clickable placeholders.", _labelStyle, Array.Empty()); GUILayout.EndVertical(); GUILayout.BeginVertical(_boxStyle, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(330f) }); GUILayout.Label("Selected Emblem", _labelGoldStyle, Array.Empty()); GUILayout.Box((Texture)(object)LocalEmblemCache.GetTexture(_pendingClanEmblemName), _boxStyle, (GUILayoutOption[])(object)new GUILayoutOption[2] { GUILayout.Width(256f), GUILayout.Height(256f) }); GUILayout.Label("" + clan.Name + " [" + clan.Tag + "]", _labelGoldStyle, Array.Empty()); GUILayout.Label(EmblemDisplayName(_pendingClanEmblemName), _labelStyle, Array.Empty()); GUILayout.Space(8f); GUILayout.Label("Banner placeholder sizes:", _labelGoldStyle, Array.Empty()); GUILayout.Label("Overview banner: 60x180", _labelStyle, Array.Empty()); GUILayout.Label("List/card banner: 44x124", _labelStyle, Array.Empty()); GUILayout.EndVertical(); GUILayout.EndHorizontal(); GUILayout.EndVertical(); } private int IndexOfCategory(IReadOnlyList categories, string category) { for (int i = 0; i < categories.Count; i++) { if (string.Equals(categories[i], category, StringComparison.OrdinalIgnoreCase)) { return i; } } return 0; } private string EmblemDisplayName(string emblemName) { emblemName = LocalEmblemCache.NormalizeName(emblemName); if (string.IsNullOrEmpty(emblemName)) { return ClanConfig.DefaultEmblem?.Value ?? "emblem_001"; } int num = emblemName.LastIndexOf('/'); if (num < 0 || num + 1 >= emblemName.Length) { return emblemName; } return emblemName.Substring(num + 1); } private List GetFreebornTerritories(WarfareData data, long playerId, TerritoryType type) { return WarfareManager.Instance?.GetFreebornTerritories(playerId, type) ?? new List(); } private Texture2D GetBiomeTexture(ClanTerritory territory) { if (_biomeTextures == null) { LoadBiomeTextures(); } string key = BiomeImageKey(territory); if (_biomeTextures != null && _biomeTextures.TryGetValue(key, out var value)) { return value; } return _boxTex ?? _pixelTex; } private void LoadBiomeTextures() { //IL_004a: Unknown result type (might be due to invalid IL or missing references) //IL_0051: Expected O, but got Unknown _biomeTextures = new Dictionary(StringComparer.OrdinalIgnoreCase); string path = Path.Combine(RuntimeAssetRoot, "Biomes"); if (!Directory.Exists(path)) { return; } string[] files = Directory.GetFiles(path, "*.png", SearchOption.TopDirectoryOnly); foreach (string text in files) { try { byte[] array = File.ReadAllBytes(text); Texture2D val = new Texture2D(2, 2, (TextureFormat)4, false); if (ImageConversion.LoadImage(val, array)) { ((Object)val).name = Path.GetFileNameWithoutExtension(text); _biomeTextures[((Object)val).name] = val; } } catch (Exception ex) { Plugin.Log.LogWarning((object)("[FCW Biomes] Failed to load biome image: " + text + " " + ex.Message)); } } } private Texture2D GetProfileTexture(PlayerClanInfo info) { //IL_0121: Unknown result type (might be due to invalid IL or missing references) //IL_012c: Expected O, but got Unknown //IL_0134: Unknown result type (might be due to invalid IL or missing references) //IL_013e: Expected O, but got Unknown EnsureProfilePortraitIndex(); string key = info?.ProfileImageKey ?? ""; if (!string.IsNullOrWhiteSpace(key) && !_profilePortraitKeys.Contains(key, StringComparer.OrdinalIgnoreCase)) { key = _profilePortraitKeys.FirstOrDefault((string candidate) => string.Equals(Path.GetFileNameWithoutExtension(candidate), key, StringComparison.OrdinalIgnoreCase)) ?? ""; } if (string.IsNullOrEmpty(key) && info != null) { key = _profilePortraitKeys.FirstOrDefault((string candidate) => string.Equals(Path.GetFileNameWithoutExtension(candidate), info.PlayerId.ToString(), StringComparison.OrdinalIgnoreCase)) ?? ""; } if (string.IsNullOrEmpty(key)) { key = _profilePortraitKeys.FirstOrDefault((string candidate) => string.Equals(Path.GetFileNameWithoutExtension(candidate), "default", StringComparison.OrdinalIgnoreCase)) ?? ""; } if (string.Equals(_selectedProfileTextureKey, key, StringComparison.OrdinalIgnoreCase)) { return _selectedProfileTexture; } if ((Object)_selectedProfileTexture != (Object)null) { Object.Destroy((Object)_selectedProfileTexture); } _selectedProfileTexture = null; _selectedProfileTextureKey = key; if (!string.IsNullOrEmpty(key)) { _selectedProfileTexture = LoadProfilePngTexture(key); } return _selectedProfileTexture; } private void EnsureProfilePortraitIndex() { if (_profilePortraitKeys != null) { return; } _profilePortraitKeys = new List(); List list = new List(); string[] obj = new string[2] { Path.GetFullPath(Path.Combine(RuntimeAssetRoot, "Profiles")), Path.GetFullPath(Path.Combine(RuntimeAssetRoot, "FehuKingdoms", "Profiles")) }; string text = obj[0]; foreach (string candidate in obj.Distinct(StringComparer.OrdinalIgnoreCase)) { list.Add(candidate); if (!Directory.Exists(candidate)) { continue; } string[] array = Directory.GetFiles(candidate, "*.png", SearchOption.AllDirectories).OrderBy((string file) => Path.GetDirectoryName(file.Substring(candidate.TrimEnd(Path.DirectorySeparatorChar, Path.AltDirectorySeparatorChar).Length).TrimStart(Path.DirectorySeparatorChar, Path.AltDirectorySeparatorChar)), StringComparer.OrdinalIgnoreCase).ThenBy((string file) => Path.GetFileName(file), StringComparer.OrdinalIgnoreCase) .ToArray(); if (array.Length == 0) { text = candidate; continue; } text = candidate; string[] array2 = array; foreach (string path in array2) { try { string fullPath = Path.GetFullPath(path); string text2 = candidate.TrimEnd(Path.DirectorySeparatorChar, Path.AltDirectorySeparatorChar); char directorySeparatorChar = Path.DirectorySeparatorChar; string text3 = text2 + directorySeparatorChar; if (fullPath.StartsWith(text3, StringComparison.OrdinalIgnoreCase) && string.Equals(Path.GetExtension(fullPath), ".png", StringComparison.OrdinalIgnoreCase)) { string item = fullPath.Substring(text3.Length).Replace('\\', '/'); _profilePortraitKeys.Add(item); } } catch (Exception) { } } if (_profilePortraitKeys.Count <= 0) { continue; } break; } _profilePortraitRoot = text; _profilePortraitCount = _profilePortraitKeys.Count; Plugin.Log.LogInfo((object)("[FCW Profile] Portrait directory: " + text)); Plugin.Log.LogInfo((object)("[FCW Profile] Indexed " + _profilePortraitCount + " portrait PNG files.")); if (_profilePortraitCount == 0) { Plugin.Log.LogWarning((object)("[FCW Profile] No profile portraits found. Checked paths: " + string.Join("; ", list.ToArray()))); } } private Texture2D LoadProfilePngTexture(string key) { //IL_0092: Unknown result type (might be due to invalid IL or missing references) //IL_0098: Expected O, but got Unknown //IL_00a7: Unknown result type (might be due to invalid IL or missing references) //IL_00b1: Expected O, but got Unknown try { string fullPath = Path.GetFullPath(_profilePortraitRoot); string text = fullPath.TrimEnd(Path.DirectorySeparatorChar, Path.AltDirectorySeparatorChar); char directorySeparatorChar = Path.DirectorySeparatorChar; string value = text + directorySeparatorChar; string fullPath2 = Path.GetFullPath(Path.Combine(fullPath, (key ?? "").Replace('/', Path.DirectorySeparatorChar))); if (!fullPath2.StartsWith(value, StringComparison.OrdinalIgnoreCase) || !string.Equals(Path.GetExtension(fullPath2), ".png", StringComparison.OrdinalIgnoreCase) || !File.Exists(fullPath2)) { throw new IOException("invalid or missing PNG"); } Texture2D val = new Texture2D(2, 2, (TextureFormat)4, false); if (!ImageConversion.LoadImage(val, File.ReadAllBytes(fullPath2))) { Object.Destroy((Object)val); throw new IOException("PNG decode failed"); } ((Object)val).name = key; return val; } catch (Exception ex) { if (_profilePortraitFailureLogs.Add(key ?? "")) { Plugin.Log.LogWarning((object)("[FCW Profile] Failed portrait PNG " + key + ": " + ex.Message)); } return null; } } private void EnsureProfilePageTextures(List keys, int page, int pageCount, int pageSize) { //IL_00a6: Unknown result type (might be due to invalid IL or missing references) //IL_00b1: Expected O, but got Unknown int count = page * pageSize; List list = keys.Skip(count).Take(pageSize).ToList(); string text = _profileImageFilter + "|" + page + "|" + pageSize + "|" + string.Join("|", list.ToArray()); if (string.Equals(text, _profilePageCacheSignature, StringComparison.Ordinal)) { return; } ClearProfilePageTextures(); foreach (string item in list) { Texture2D val = LoadProfilePngTexture(item); if ((Object)val != (Object)null) { _profilePageTextures[item] = val; } } _profilePageCacheSignature = text; } private void ClearProfilePageTextures() { //IL_001c: Unknown result type (might be due to invalid IL or missing references) //IL_0027: Expected O, but got Unknown //IL_002a: Unknown result type (might be due to invalid IL or missing references) //IL_0034: Expected O, but got Unknown foreach (Texture2D value in _profilePageTextures.Values) { if ((Object)value != (Object)null) { Object.Destroy((Object)value); } } _profilePageTextures.Clear(); _profilePageCacheSignature = ""; } private PlayerClanInfo ResolveLocalProfileForUi(WarfareData data, long playerId) { if (data?.Players == null) { return null; } string localSteamId64ForDiagnostics = WarfareManager.GetLocalSteamId64ForDiagnostics(); PlayerClanInfo playerClanInfo = null; if (!string.IsNullOrEmpty(localSteamId64ForDiagnostics)) { foreach (PlayerClanInfo value in data.Players.Values) { if (string.Equals(value.SteamId64, localSteamId64ForDiagnostics, StringComparison.Ordinal)) { if (playerClanInfo != null && playerClanInfo != value) { playerClanInfo = null; break; } playerClanInfo = value; } } } PlayerClanInfo profile = playerClanInfo ?? data.GetPlayer(playerId); if (profile != null) { long key = data.Players.FirstOrDefault((KeyValuePair entry) => entry.Value == profile).Key; string text = playerId + "|" + localSteamId64ForDiagnostics + "|" + key; if (!string.Equals(_lastProfileUiRecordLog, text, StringComparison.Ordinal)) { _lastProfileUiRecordLog = text; Plugin.Log.LogInfo((object)("[FCW Profile UI] playerId=" + playerId + " steamId=" + localSteamId64ForDiagnostics + " profileKey=" + key)); } } return profile; } private ResolvedItemIcon GetInventoryItemTexture(string prefabName, string displayName, Sprite itemDropIcon = null) { string text = (prefabName ?? string.Empty).Replace("(Clone)", string.Empty).Trim(); if (string.IsNullOrEmpty(text)) { return null; } if (_resolvedInventoryIcons.TryGetValue(text, out var value)) { return value; } value = ResolveSpriteIcon(itemDropIcon ?? GetPrefabItemIcon(text)); _resolvedInventoryIcons[text] = value; return value; } private static ResolvedItemIcon ResolveSpriteIcon(Sprite sprite) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) //IL_000c: Expected O, but got Unknown //IL_0014: Unknown result type (might be due to invalid IL or missing references) //IL_001f: Expected O, but got Unknown //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_0066: Unknown result type (might be due to invalid IL or missing references) //IL_00b0: Unknown result type (might be due to invalid IL or missing references) if ((Object)sprite == (Object)null || (Object)sprite.texture == (Object)null) { return null; } try { Texture2D texture = sprite.texture; Rect textureRect = sprite.textureRect; if (((Rect)(ref textureRect)).width <= 0f || ((Rect)(ref textureRect)).height <= 0f || ((Texture)texture).width <= 0 || ((Texture)texture).height <= 0) { return null; } Rect uv = default(Rect); ((Rect)(ref uv))..ctor(((Rect)(ref textureRect)).x / (float)((Texture)texture).width, ((Rect)(ref textureRect)).y / (float)((Texture)texture).height, ((Rect)(ref textureRect)).width / (float)((Texture)texture).width, ((Rect)(ref textureRect)).height / (float)((Texture)texture).height); return new ResolvedItemIcon(texture, uv, ((Rect)(ref textureRect)).width / ((Rect)(ref textureRect)).height); } catch { return null; } } private static Sprite GetItemDataIcon(object itemData) { try { object obj = itemData?.GetType().GetField("m_shared")?.GetValue(itemData); return (Sprite)((obj?.GetType().GetField("m_icons")?.GetValue(obj) is Array { Length: >0 } array) ? /*isinst with value type is only supported in some contexts*/: null); } catch { return null; } } private static Sprite GetPrefabItemIcon(string prefabName) { //IL_0077: Unknown result type (might be due to invalid IL or missing references) //IL_007d: Expected O, but got Unknown //IL_008b: Unknown result type (might be due to invalid IL or missing references) //IL_0096: Expected O, but got Unknown //IL_00b8: Unknown result type (might be due to invalid IL or missing references) //IL_00c3: Expected O, but got Unknown if (string.IsNullOrEmpty(prefabName)) { return null; } try { string text = prefabName.Replace("(Clone)", string.Empty).Trim(); object obj = ((object)ObjectDB.instance)?.GetType().GetMethod("GetItemPrefab", new Type[1] { typeof(string) })?.Invoke(ObjectDB.instance, new object[1] { text }); GameObject val = (GameObject)((obj is GameObject) ? obj : null); if ((Object)(object)val == (Object)null && (Object)ZNetScene.instance != (Object)null) { val = ZNetScene.instance.GetPrefab(text); } ItemDrop val2 = (((Object)(object)val != (Object)null) ? val.GetComponent() : null); return ((Object)val2 != (Object)null) ? GetItemDataIcon(val2.m_itemData) : null; } catch { return null; } } private bool DrawVaultIconButton(ResolvedItemIcon icon, string fallbackLabel, int amount, bool selected, float size = 58f, bool empty = false) { //IL_0006: Unknown result type (might be due to invalid IL or missing references) //IL_0035: Expected O, but got Unknown //IL_0035: Unknown result type (might be due to invalid IL or missing references) //IL_003a: Unknown result type (might be due to invalid IL or missing references) //IL_0047: Unknown result type (might be due to invalid IL or missing references) //IL_0052: Expected O, but got Unknown //IL_0105: Unknown result type (might be due to invalid IL or missing references) //IL_010a: 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_011b: Expected O, but got Unknown //IL_014f: Unknown result type (might be due to invalid IL or missing references) //IL_0281: Unknown result type (might be due to invalid IL or missing references) //IL_0286: Unknown result type (might be due to invalid IL or missing references) //IL_028d: Unknown result type (might be due to invalid IL or missing references) //IL_0296: Expected O, but got Unknown //IL_0167: Unknown result type (might be due to invalid IL or missing references) //IL_0180: Unknown result type (might be due to invalid IL or missing references) //IL_01be: Unknown result type (might be due to invalid IL or missing references) //IL_01f7: 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_0266: Unknown result type (might be due to invalid IL or missing references) //IL_00ca: Unknown result type (might be due to invalid IL or missing references) //IL_00cf: Unknown result type (might be due to invalid IL or missing references) //IL_00d4: Unknown result type (might be due to invalid IL or missing references) //IL_00d6: Unknown result type (might be due to invalid IL or missing references) //IL_00e7: 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_02d2: Unknown result type (might be due to invalid IL or missing references) bool result = GUILayout.Button(new GUIContent(string.Empty, fallbackLabel), _smallButtonStyle, (GUILayoutOption[])(object)new GUILayoutOption[2] { GUILayout.Width(size), GUILayout.Height(size) }); Rect lastRect = GUILayoutUtility.GetLastRect(); if (icon != null && (Object)icon.Texture != (Object)null) { float num = Mathf.Min(((Rect)(ref lastRect)).width, ((Rect)(ref lastRect)).height) * 0.9f; float num2 = num; float num3 = num; if (icon.Aspect > 1f) { num3 = num / icon.Aspect; } else { num2 = num * icon.Aspect; } Rect val = new Rect(((Rect)(ref lastRect)).x + (((Rect)(ref lastRect)).width - num2) * 0.5f, ((Rect)(ref lastRect)).y + (((Rect)(ref lastRect)).height - num3) * 0.5f, num2, num3); Color color = GUI.color; GUI.color = Color.white; GUI.DrawTextureWithTexCoords(val, (Texture)(object)icon.Texture, icon.Uv, true); GUI.color = color; } else if (!empty) { GUIStyle val2 = new GUIStyle(_secondaryTextStyle) { alignment = (TextAnchor)4, fontSize = 20 }; GUI.Label(new Rect(((Rect)(ref lastRect)).x + 4f, ((Rect)(ref lastRect)).y + 4f, ((Rect)(ref lastRect)).width - 8f, ((Rect)(ref lastRect)).height - 8f), "?", val2); } if (selected) { Color color2 = GUI.color; GUI.color = new Color(0.86f, 0.35f, 0.68f, 1f); float num4 = 2f; float num5 = 2f; GUI.DrawTexture(new Rect(((Rect)(ref lastRect)).x + num4, ((Rect)(ref lastRect)).y + num4, ((Rect)(ref lastRect)).width - num4 * 2f, num5), (Texture)(object)_pixelTex); GUI.DrawTexture(new Rect(((Rect)(ref lastRect)).x + num4, ((Rect)(ref lastRect)).yMax - num4 - num5, ((Rect)(ref lastRect)).width - num4 * 2f, num5), (Texture)(object)_pixelTex); GUI.DrawTexture(new Rect(((Rect)(ref lastRect)).x + num4, ((Rect)(ref lastRect)).y + num4, num5, ((Rect)(ref lastRect)).height - num4 * 2f), (Texture)(object)_pixelTex); GUI.DrawTexture(new Rect(((Rect)(ref lastRect)).xMax - num4 - num5, ((Rect)(ref lastRect)).y + num4, num5, ((Rect)(ref lastRect)).height - num4 * 2f), (Texture)(object)_pixelTex); GUI.color = color2; } GUIStyle val3 = new GUIStyle(_labelGoldStyle) { alignment = (TextAnchor)8, fontSize = 11 }; if (!empty && amount > 0) { GUI.Label(new Rect(((Rect)(ref lastRect)).x + 3f, ((Rect)(ref lastRect)).y + 3f, ((Rect)(ref lastRect)).width - 6f, ((Rect)(ref lastRect)).height - 6f), amount.ToString(), val3); } return result; } private Dictionary LoadRuntimePngTextures(string folderName, bool recursive) { //IL_003f: Unknown result type (might be due to invalid IL or missing references) //IL_0046: Expected O, but got Unknown //IL_0058: Unknown result type (might be due to invalid IL or missing references) //IL_0062: Expected O, but got Unknown Dictionary dictionary = new Dictionary(StringComparer.OrdinalIgnoreCase); string path = ResolveRuntimeAssetFolder(folderName); if (!Directory.Exists(path)) { return dictionary; } string[] files = Directory.GetFiles(path, "*.png", recursive ? SearchOption.AllDirectories : SearchOption.TopDirectoryOnly); foreach (string text in files) { try { Texture2D val = new Texture2D(2, 2, (TextureFormat)4, false); if (!ImageConversion.LoadImage(val, File.ReadAllBytes(text))) { Object.Destroy((Object)val); continue; } string text2 = (((Object)val).name = Path.GetFileNameWithoutExtension(text)); string text3 = text2; dictionary[text3] = val; dictionary[NormalizeAssetKey(text3)] = val; } catch (Exception ex) { Plugin.Log.LogWarning((object)("[FCW Assets] Failed to load " + text + ": " + ex.Message)); } } return dictionary; } private static string ResolveRuntimeAssetFolder(string folderName) { string[] array = (string.Equals(folderName, "Inventory-icons", StringComparison.OrdinalIgnoreCase) ? new string[4] { "Inventory-icons", "inventory-icons", "Icons", Path.Combine("Assets", "Icons") } : new string[1] { folderName }); foreach (string path in array) { string text = Path.Combine(RuntimeAssetRoot, path); if (Directory.Exists(text)) { return text; } } return Path.Combine(RuntimeAssetRoot, folderName); } private static string NormalizeAssetKey(string value) { if (string.IsNullOrEmpty(value)) { return string.Empty; } char[] array = value.ToLowerInvariant().ToCharArray(); int length = 0; for (int i = 0; i < array.Length; i++) { if (char.IsLetterOrDigit(array[i])) { array[length++] = array[i]; } } return new string(array, 0, length); } private string BiomeImageKey(ClanTerritory territory) { if (territory == null) { return "Meadows"; } if (!string.IsNullOrEmpty(territory.BiomeName)) { return NormalizeBiomeImageKey(territory.BiomeName); } if (territory.BiomeValueTier >= 3) { return "Plains"; } if (territory.BiomeValueTier == 2) { return "Swamp"; } return "Meadows"; } private string BiomeDisplayName(ClanTerritory territory) { if (territory != null && !string.IsNullOrEmpty(territory.BiomeName)) { return territory.BiomeName; } return BiomeImageKey(territory); } private string NormalizeBiomeImageKey(string biomeName) { string a = (biomeName ?? "").Replace(" ", "").Replace("-", ""); if (string.Equals(a, "BlackForest", StringComparison.OrdinalIgnoreCase)) { return "BlackForest"; } if (string.Equals(a, "DeepNorth", StringComparison.OrdinalIgnoreCase)) { return "DeepNorth"; } if (string.Equals(a, "Jungle", StringComparison.OrdinalIgnoreCase)) { return "Jungle"; } if (string.Equals(a, "Mistlands", StringComparison.OrdinalIgnoreCase)) { return "Mistlands"; } if (string.Equals(a, "Mountain", StringComparison.OrdinalIgnoreCase)) { return "Mountain"; } if (string.Equals(a, "Ocean", StringComparison.OrdinalIgnoreCase)) { return "Ocean"; } if (string.Equals(a, "Plains", StringComparison.OrdinalIgnoreCase)) { return "Plains"; } if (string.Equals(a, "Swamp", StringComparison.OrdinalIgnoreCase)) { return "Swamp"; } if (string.Equals(a, "Ashlands", StringComparison.OrdinalIgnoreCase)) { return "Ashlands"; } return "Meadows"; } private ClanTerritory GetCapitalTerritory(WarfareData data, int clanId) { foreach (ClanTerritory clanTerritory in data.GetClanTerritories(clanId)) { if (clanTerritory.Type == TerritoryType.Capital) { return clanTerritory; } } return null; } private ClanTerritory GetCurrentTerritory(WarfareData data) { //IL_0019: Unknown result type (might be due to invalid IL or missing references) //IL_001e: 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_0025: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)Player.m_localPlayer == (Object)null) { return null; } Vector2i grid = GridExtensions.WorldToGrid(((Component)Player.m_localPlayer).transform.position); return data.GetTerritory(grid); } private void DrawMiniStat(string label, string value) { GUILayout.Label("" + label + ": " + value + "", _labelStyle, Array.Empty()); } private void DrawEmptyState(string title, string detail) { GUILayout.BeginVertical(_infoPanelStyle, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.MaxWidth(560f) }); GUILayout.Label("" + title + "", _labelGoldStyle, Array.Empty()); if (!string.IsNullOrEmpty(detail)) { GUILayout.Label(detail, _labelStyle, Array.Empty()); } GUILayout.EndVertical(); } private void DrawCapacityBar(int used, int capacity, float width = 280f, float height = 18f) { //IL_0037: Unknown result type (might be due to invalid IL or missing references) //IL_003c: Unknown result type (might be due to invalid IL or missing references) //IL_003d: Unknown result type (might be due to invalid IL or missing references) //IL_0056: Unknown result type (might be due to invalid IL or missing references) //IL_0060: Unknown result type (might be due to invalid IL or missing references) //IL_0080: Unknown result type (might be due to invalid IL or missing references) //IL_00a8: Unknown result type (might be due to invalid IL or missing references) //IL_00cc: Unknown result type (might be due to invalid IL or missing references) //IL_00f0: Unknown result type (might be due to invalid IL or missing references) //IL_0120: Unknown result type (might be due to invalid IL or missing references) float num = ((capacity > 0) ? Mathf.Clamp01((float)used / (float)capacity) : 0f); Rect rect = GUILayoutUtility.GetRect(width, height, (GUILayoutOption[])(object)new GUILayoutOption[2] { GUILayout.Width(width), GUILayout.Height(height) }); Color color = GUI.color; GUI.color = new Color(0.12f, 0.09f, 0.04f, 0.95f); GUI.DrawTexture(rect, (Texture)(object)_pixelTex); GUI.color = new Color(0.86f, 0.62f, 0.24f, 0.95f); GUI.DrawTexture(new Rect(((Rect)(ref rect)).x, ((Rect)(ref rect)).y, ((Rect)(ref rect)).width * num, ((Rect)(ref rect)).height), (Texture)(object)_pixelTex); GUI.color = new Color(0.58f, 0.42f, 0.16f, 1f); GUI.DrawTexture(new Rect(((Rect)(ref rect)).x, ((Rect)(ref rect)).y, ((Rect)(ref rect)).width, 1f), (Texture)(object)_pixelTex); GUI.DrawTexture(new Rect(((Rect)(ref rect)).x, ((Rect)(ref rect)).yMax - 1f, ((Rect)(ref rect)).width, 1f), (Texture)(object)_pixelTex); GUI.color = color; } private Texture2D GetRoleTexture(ClanRole role) { if (_roleTextures == null) { LoadRoleTextures(); } string key = GetRoleData(role)?.IconName ?? RoleName(role); if (_roleTextures != null && _roleTextures.TryGetValue(key, out var value)) { return value; } string[] array = RoleIconAliases(role); foreach (string key2 in array) { if (_roleTextures != null && _roleTextures.TryGetValue(key2, out value)) { return value; } } return _boxTex ?? _pixelTex; } private string[] RoleIconAliases(ClanRole role) { if (PlayerClanInfo.NormalizeRole(role) == ClanRole.Guardian) { return new string[2] { "guardian", "shieldbearer" }; } return Array.Empty(); } private void LoadRoleTextures() { //IL_004a: Unknown result type (might be due to invalid IL or missing references) //IL_0051: Expected O, but got Unknown _roleTextures = new Dictionary(StringComparer.OrdinalIgnoreCase); string path = Path.Combine(RuntimeAssetRoot, "Roles"); if (!Directory.Exists(path)) { return; } string[] files = Directory.GetFiles(path, "*.png", SearchOption.TopDirectoryOnly); foreach (string text in files) { try { byte[] array = File.ReadAllBytes(text); Texture2D val = new Texture2D(2, 2, (TextureFormat)4, false); if (ImageConversion.LoadImage(val, array)) { ((Object)val).name = Path.GetFileNameWithoutExtension(text); _roleTextures[((Object)val).name] = val; } } catch (Exception ex) { Plugin.Log.LogWarning((object)("[FCW Roles] Failed to load role image: " + text + " " + ex.Message)); } } } private Texture2D GetBuildingTexture(TerritoryBuildingType type) { if (_buildingTextures == null) { LoadBuildingTextures(); } if (type == TerritoryBuildingType.None && _buildingTextures != null && _buildingTextures.TryGetValue("empty_slot", out var value)) { return value; } string key = WarfareManager.BuildingIconName(type); if (_buildingTextures != null && _buildingTextures.TryGetValue(key, out var value2)) { return value2; } if (_buildingTextures != null && _buildingTextures.TryGetValue("empty_slot", out value2)) { return value2; } return _boxTex ?? _pixelTex; } private Texture2D GetBuildingTexture(TerritoryBuilding building) { if (building != null && building.Damaged) { if (_buildingTextures == null) { LoadBuildingTextures(); } if (_buildingTextures != null && _buildingTextures.TryGetValue("damaged_building", out var value)) { return value; } return GetBuildingTexture(TerritoryBuildingType.None); } return GetBuildingTexture(building?.Type ?? TerritoryBuildingType.None); } private void LoadBuildingTextures() { //IL_0080: Unknown result type (might be due to invalid IL or missing references) //IL_0087: Expected O, but got Unknown //IL_0098: Unknown result type (might be due to invalid IL or missing references) //IL_00a2: Expected O, but got Unknown _buildingTextures = LoadRuntimePngTextures(Path.Combine("FehuKingdoms", "Buildings"), recursive: false); string path = Path.Combine(RuntimeAssetRoot, "Buildings"); if (!Directory.Exists(path)) { return; } string[] files = Directory.GetFiles(path, "*.png", SearchOption.TopDirectoryOnly); foreach (string text in files) { string fileNameWithoutExtension = Path.GetFileNameWithoutExtension(text); if (_buildingTextures.ContainsKey(fileNameWithoutExtension) || _buildingTextures.ContainsKey(NormalizeAssetKey(fileNameWithoutExtension))) { continue; } try { Texture2D val = new Texture2D(2, 2, (TextureFormat)4, false); if (!ImageConversion.LoadImage(val, File.ReadAllBytes(text))) { Object.Destroy((Object)val); continue; } ((Object)val).name = fileNameWithoutExtension; _buildingTextures[fileNameWithoutExtension] = val; _buildingTextures[NormalizeAssetKey(fileNameWithoutExtension)] = val; } catch (Exception ex) { Plugin.Log.LogWarning((object)("[FCW Buildings] Failed to load building image: " + text + " " + ex.Message)); } } } private Texture2D GetWorldEventTexture(WorldEventType type) { if (_eventTextures == null) { LoadWorldEventTextures(); } if (_eventTextures != null && _eventTextures.TryGetValue(WorldEventIconKey(type), out var value)) { return value; } return null; } private void LoadWorldEventTextures() { //IL_004a: Unknown result type (might be due to invalid IL or missing references) //IL_0051: Expected O, but got Unknown _eventTextures = new Dictionary(StringComparer.OrdinalIgnoreCase); string path = Path.Combine(RuntimeAssetRoot, "Events"); if (!Directory.Exists(path)) { return; } string[] files = Directory.GetFiles(path, "*.png", SearchOption.TopDirectoryOnly); foreach (string text in files) { try { byte[] array = File.ReadAllBytes(text); Texture2D val = new Texture2D(2, 2, (TextureFormat)4, false); if (ImageConversion.LoadImage(val, array)) { ((Object)val).name = Path.GetFileNameWithoutExtension(text); _eventTextures[((Object)val).name] = val; } } catch (Exception ex) { Plugin.Log.LogWarning((object)("[FCW Events] Failed to load event image: " + text + " " + ex.Message)); } } } private string WorldEventIconKey(WorldEventType type) { return type switch { WorldEventType.BanditCamp => "bandit_camp", WorldEventType.AncientShrine => "ancient_shrine", WorldEventType.ResourceRush => "resource_rush", WorldEventType.CorruptedTerritory => "corrupted_territory", WorldEventType.LostCaravan => "lost_caravan", WorldEventType.WorldBoss => "world_boss", WorldEventType.OdinChallenge => "odin_challenge", WorldEventType.ClanChallenge => "clan_challenge", WorldEventType.FreebornOpportunity => "freeborn_opportunity", _ => "default", }; } private string WorldEventSymbol(WorldEventType type) { if (SupportsUnicodeEventSymbols()) { return type switch { WorldEventType.BanditCamp => "?", WorldEventType.AncientShrine => "?", WorldEventType.ResourceRush => "?", WorldEventType.CorruptedTerritory => "?", WorldEventType.LostCaravan => "??", WorldEventType.WorldBoss => "?", WorldEventType.OdinChallenge => "?", WorldEventType.ClanChallenge => "??", WorldEventType.FreebornOpportunity => "??", _ => "?", }; } return type switch { WorldEventType.BanditCamp => "[B]", WorldEventType.AncientShrine => "[A]", WorldEventType.ResourceRush => "[R]", WorldEventType.CorruptedTerritory => "[C]", WorldEventType.LostCaravan => "[L]", WorldEventType.WorldBoss => "[W]", WorldEventType.OdinChallenge => "[O]", WorldEventType.ClanChallenge => "[K]", WorldEventType.FreebornOpportunity => "[F]", _ => "[?]", }; } private bool SupportsUnicodeEventSymbols() { //IL_001a: Unknown result type (might be due to invalid IL or missing references) //IL_0025: Expected O, but got Unknown GUISkin skin = GUI.skin; Font val = (((Object)(object)skin != (Object)null) ? skin.font : null); if ((Object)val != (Object)null && val.HasCharacter('?') && val.HasCharacter('?')) { return val.HasCharacter('?'); } return false; } private void DrawOverviewHub(WarfareData data, PlayerClanInfo info, Clan clan) { //IL_0107: 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_006e: Unknown result type (might be due to invalid IL or missing references) //IL_008f: Unknown result type (might be due to invalid IL or missing references) //IL_0094: Unknown result type (might be due to invalid IL or missing references) string[] labels = new string[3] { "Overview", "Elections", "Events" }; if (_overviewSubTab > 2) { _overviewSubTab = 0; } DrawSectionTabs(labels, ref _overviewSubTab, 180f); if (_overviewSubTab == 0) { DrawRealmStatusHeader(data, info, clan); bool flag = _editingClanEmblem || _editingClanLinks || _editingBannerColors; _overviewScroll = GUILayout.BeginScrollView(_overviewScroll, false, true, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Height(AvailableContentHeight()) }); DrawOverview(data, info, clan); if (flag) { GUILayout.Space(28f); } GUILayout.EndScrollView(); } else if (_overviewSubTab == 1) { DrawJarlElectionPanel(data, info, clan); } else if (_overviewSubTab == 2) { GUILayout.BeginVertical(_boxStyle, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.ExpandHeight(true) }); GUILayout.Label("EVENTS", _labelGoldStyle, Array.Empty()); _overviewEventsScroll = GUILayout.BeginScrollView(_overviewEventsScroll, false, true, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.ExpandHeight(true) }); DrawStatusFeed(data, info, int.MaxValue); GUILayout.EndScrollView(); GUILayout.EndVertical(); } else { DrawGlobalRankingStats(data); } } private void DrawProfileHub(WarfareData data, PlayerClanInfo info, Clan clan) { PlayerClanInfo playerClanInfo = _viewedProfile ?? info; Clan clan2 = ((playerClanInfo != null) ? data.GetClan(playerClanInfo.ClanId) : null); if (_viewedProfile != null) { if (GUILayout.Button("← Back to " + _profileBackLabel, _smallButtonStyle, (GUILayoutOption[])(object)new GUILayoutOption[2] { GUILayout.Width(210f), GUILayout.Height(26f) })) { ClosePublicProfile(); GUIUtility.ExitGUI(); } string[] labels = new string[4] { "Character", "Achievements", "Titles", "Personal Saga" }; _profileSubTab = Mathf.Clamp(_profileSubTab, 0, 3); DrawSectionTabs(labels, ref _profileSubTab, 180f); } else { string[] labels2 = new string[5] { "Character", "All Players", "Achievements", "Titles", "Personal Saga" }; _profileSubTab = Mathf.Clamp(_profileSubTab, 0, 4); DrawSectionTabs(labels2, ref _profileSubTab, 180f); if (_profileSubTab == 1) { DrawAllPlayers(data, info); return; } } switch ((_viewedProfile != null) ? _profileSubTab : ((_profileSubTab > 1) ? (_profileSubTab - 1) : _profileSubTab)) { case 0: DrawProfileCharacter(data, playerClanInfo, clan2); break; case 1: DrawProfileAchievements(playerClanInfo); break; case 2: DrawProfileTitles(playerClanInfo, clan2); break; case 3: DrawProfileSaga(data, playerClanInfo); break; } } private void OpenPublicProfile(PlayerClanInfo target, string source, int sourceTab, int sourceSubTab, Vector2 sourceScroll) { //IL_0036: Unknown result type (might be due to invalid IL or missing references) //IL_0038: Unknown result type (might be due to invalid IL or missing references) if (target != null) { _viewedProfile = target; if (source != "Clans") { _viewedPublicClan = null; } _profileBackLabel = source; _profileBackTab = sourceTab; _profileBackSubTab = sourceSubTab; _profileBackScroll = sourceScroll; _profileSubTab = 0; _profileAboutDraft = ""; _profileImageExpanded = false; _tab = 1; } } private void ClosePublicProfile() { //IL_0052: Unknown result type (might be due to invalid IL or missing references) //IL_0057: Unknown result type (might be due to invalid IL or missing references) //IL_007d: Unknown result type (might be due to invalid IL or missing references) //IL_0082: Unknown result type (might be due to invalid IL or missing references) //IL_00a8: Unknown result type (might be due to invalid IL or missing references) //IL_00ad: 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_00d8: Unknown result type (might be due to invalid IL or missing references) _viewedProfile = null; if (_profileBackLabel != "Clans") { _viewedPublicClan = null; } _profileAboutDraft = ""; _tab = _profileBackTab; if (_profileBackLabel == "All Players") { _profileSubTab = 1; _allPlayersScroll = _profileBackScroll; } else if (_profileBackLabel == "Members") { _membersSubTab = _profileBackSubTab; _scrollA = _profileBackScroll; } else if (_profileBackLabel == "Clans") { _clansSubTab = _profileBackSubTab; _scrollDirectoryClans = _profileBackScroll; } else if (_profileBackLabel == "Homesteads") { _homesteadsSubTab = _profileBackSubTab; _scrollDirectoryFreeborn = _profileBackScroll; } } private bool IsRemoteProfile(PlayerClanInfo info) { if (_viewedProfile != null && info != null) { if (!((Object)(object)Player.m_localPlayer == (Object)null)) { return info.PlayerId != Player.m_localPlayer.GetPlayerID(); } return true; } return false; } private List CanonicalPublicProfiles(WarfareData data) { List list = new List(); Dictionary dictionary = new Dictionary(StringComparer.Ordinal); foreach (PlayerClanInfo candidate in from p in data.Players.Values where p != null orderby p.PlayerId select p) { if (!string.IsNullOrWhiteSpace(candidate.SteamId64)) { if (!dictionary.ContainsKey(candidate.SteamId64)) { dictionary[candidate.SteamId64] = candidate; list.Add(candidate); } continue; } PlayerClanInfo playerClanInfo = list.FirstOrDefault((PlayerClanInfo p) => p.PlayerId == candidate.PlayerId || (p.LinkedPlayerIds ?? new List()).Contains(candidate.PlayerId) || (candidate.LinkedPlayerIds ?? new List()).Contains(p.PlayerId)); if (playerClanInfo == null) { list.Add(candidate); } else if (!string.IsNullOrWhiteSpace(playerClanInfo.SteamId64)) { string item = playerClanInfo.PlayerId + ":" + candidate.PlayerId; if (_allPlayersConflictLogs.Add(item)) { Plugin.Log.LogWarning((object)("[FCW Profile] Ambiguous saved identity records retained separately: " + playerClanInfo.PlayerId + " / " + candidate.PlayerId)); } list.Add(candidate); } } return list; } private HashSet ConnectedPlayerNames() { //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_0033: Unknown result type (might be due to invalid IL or missing references) HashSet hashSet = new HashSet(StringComparer.Ordinal); if ((Object)(object)ZNet.instance != (Object)null) { foreach (PlayerInfo player in ZNet.instance.GetPlayerList()) { hashSet.Add(player.m_name ?? ""); } } return hashSet; } private bool IsPublicProfileOnline(PlayerClanInfo info, HashSet names) { if (info != null) { return names.Contains(info.PlayerName ?? ""); } return false; } private long PublicLastOnline(PlayerClanInfo info) { if (info == null) { return 0L; } if (info.LastSeenTimestamp > 0) { return info.LastSeenTimestamp; } if (info.LastLoginTimestamp > 0) { return info.LastLoginTimestamp; } return info.FreebornLastActiveTimestamp; } private string PublicLastOnlineText(PlayerClanInfo info, bool online) { if (online) { return "Online"; } long num = PublicLastOnline(info); if (num <= 0) { return "Last online: Unknown"; } long num2 = Math.Max(0L, WarfareManager.Now() - num); if (num2 < 3600) { return "Last online: " + Math.Max(1L, num2 / 60) + "m ago"; } if (num2 < 86400) { return "Last online: " + num2 / 3600 + "h ago"; } return "Last online: " + num2 / 86400 + "d ago"; } private int PublicLegacyScore(PlayerClanInfo info, Clan clan) { return info.TerritoriesClaimed * 100 + info.TerritoriesCaptured * 200 + info.CapitalsFounded * 250 + info.WarsParticipated * 100 + info.WarsWon * 200 + info.PvPKills * 10 + info.BountiesClaimed * 75 + (int)Math.Min(100000L, info.CoinsDonatedToTreasury + info.ResourceTaxPaid + info.CoinTaxPaid) / 10 + info.BuildingsContributed * 25 + info.OutpostsFounded * 100 + info.HomesteadsFounded * 100 + info.TerritoriesDiscovered * 75 + info.WorldEventsCompleted * 80 + (int)Math.Min(100000L, info.ItemsDonatedToVault) / 10 + ((clan != null && clan.JarlId == info.PlayerId) ? 100 : 0) + WarfareManager.PersonalSagaLegacyScore(info); } private void DrawAllPlayers(WarfareData data, PlayerClanInfo viewer) { //IL_043f: Unknown result type (might be due to invalid IL or missing references) //IL_0456: Unknown result type (might be due to invalid IL or missing references) //IL_045b: Unknown result type (might be due to invalid IL or missing references) List list = CanonicalPublicProfiles(data); HashSet onlineNames = ConnectedPlayerNames(); int num = list.Count((PlayerClanInfo p) => IsPublicProfileOnline(p, onlineNames)); int num2 = list.Count((PlayerClanInfo p) => p.ClanId != 0); int num3 = list.Count((PlayerClanInfo p) => p.IsFreeborn && p.ClanId == 0); int num4 = list.Count((PlayerClanInfo p) => p.ClanId == 0 && !p.IsFreeborn); GUILayout.Label("ALL PLAYERS Players: " + list.Count + " Online: " + num + " Clan Members: " + num2 + " Freeborn: " + num3 + " No Clan: " + num4, _labelGoldStyle, Array.Empty()); GUILayout.BeginHorizontal(Array.Empty()); GUILayout.Label("Search", _labelStyle, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(58f) }); _allPlayersSearch = GUILayout.TextField(_allPlayersSearch ?? "", 80, (GUILayoutOption[])(object)new GUILayoutOption[2] { GUILayout.ExpandWidth(true), GUILayout.Height(28f) }); GUILayout.EndHorizontal(); DrawProfileFilterRow(new string[5] { "All", "Online", "Clan Members", "Freeborn", "No Clan" }, ref _allPlayersFilter); DrawProfileFilterRow(new string[4] { "Online First", "Last Online", "Name", "Legacy Score" }, ref _allPlayersSort); IEnumerable source = list; if (_allPlayersFilter == 1) { source = source.Where((PlayerClanInfo p) => IsPublicProfileOnline(p, onlineNames)); } else if (_allPlayersFilter == 2) { source = source.Where((PlayerClanInfo p) => viewer != null && viewer.ClanId != 0 && p.ClanId == viewer.ClanId); } else if (_allPlayersFilter == 3) { source = source.Where((PlayerClanInfo p) => p.IsFreeborn && p.ClanId == 0); } else if (_allPlayersFilter == 4) { source = source.Where((PlayerClanInfo p) => p.ClanId == 0 && !p.IsFreeborn); } if (!string.IsNullOrWhiteSpace(_allPlayersSearch)) { string search = _allPlayersSearch.Trim(); source = source.Where((PlayerClanInfo p) => (p.PlayerName ?? "").IndexOf(search, StringComparison.OrdinalIgnoreCase) >= 0 || (data.GetClan(p.ClanId)?.Name ?? "").IndexOf(search, StringComparison.OrdinalIgnoreCase) >= 0 || (data.GetClan(p.ClanId)?.Tag ?? "").IndexOf(search, StringComparison.OrdinalIgnoreCase) >= 0 || ProfileTitle(p, data.GetClan(p.ClanId)).IndexOf(search, StringComparison.OrdinalIgnoreCase) >= 0); } source = ((_allPlayersSort == 1) ? source.OrderByDescending(PublicLastOnline).ThenBy((PlayerClanInfo p) => p.PlayerName, StringComparer.OrdinalIgnoreCase) : ((_allPlayersSort == 2) ? source.OrderBy((PlayerClanInfo p) => p.PlayerName, StringComparer.OrdinalIgnoreCase) : ((_allPlayersSort != 3) ? source.OrderByDescending((PlayerClanInfo p) => IsPublicProfileOnline(p, onlineNames)).ThenByDescending(PublicLastOnline).ThenBy((PlayerClanInfo p) => p.PlayerName, StringComparer.OrdinalIgnoreCase) : source.OrderByDescending((PlayerClanInfo p) => PublicLegacyScore(p, data.GetClan(p.ClanId))).ThenBy((PlayerClanInfo p) => p.PlayerName, StringComparer.OrdinalIgnoreCase)))); float num5 = Mathf.Max(220f, AvailableContentHeight() - 122f - BottomSafePadding()); _allPlayersScroll = GUILayout.BeginScrollView(_allPlayersScroll, false, true, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Height(num5) }); foreach (PlayerClanInfo item in source) { DrawAllPlayersRow(data, item, onlineNames); } GUILayout.Space(64f); GUILayout.EndScrollView(); } private void DrawAllPlayersRow(WarfareData data, PlayerClanInfo player, HashSet onlineNames) { //IL_027d: Unknown result type (might be due to invalid IL or missing references) //IL_0288: Expected O, but got Unknown //IL_038f: Unknown result type (might be due to invalid IL or missing references) //IL_0394: Unknown result type (might be due to invalid IL or missing references) //IL_0376: Unknown result type (might be due to invalid IL or missing references) //IL_039f: Unknown result type (might be due to invalid IL or missing references) //IL_03b1: Unknown result type (might be due to invalid IL or missing references) //IL_03cd: Unknown result type (might be due to invalid IL or missing references) Clan clan = data.GetClan(player.ClanId); bool online = IsPublicProfileOnline(player, onlineNames); GUILayout.BeginHorizontal(_boxStyle, (GUILayoutOption[])(object)new GUILayoutOption[2] { GUILayout.ExpandWidth(true), GUILayout.Height(116f) }); GUILayout.Box((Texture)(object)(GetProfileTexture(player) ?? LocalEmblemCache.GetTexture(clan?.ClanEmblemName)), _boxStyle, (GUILayoutOption[])(object)new GUILayoutOption[2] { GUILayout.Width(92f), GUILayout.Height(92f) }); GUILayout.BeginVertical((GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(250f) }); GUILayout.Label("" + (player.PlayerName ?? "Unknown") + "", _labelGoldStyle, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.MaxWidth(240f) }); GUILayout.Label(ProfileTitle(player, clan), _labelStyle, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.MaxWidth(240f) }); GUILayout.Label((clan != null) ? ("Clan: " + clan.Name + " [" + clan.Tag + "]") : (player.IsFreeborn ? "Freeborn" : "No Clan"), _labelStyle, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.MaxWidth(240f) }); if (clan != null) { GUILayout.Label("Rank: " + player.Rank, _labelStyle, Array.Empty()); } GUILayout.EndVertical(); GUILayout.BeginVertical((GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(310f) }); GUILayout.Label("Roles: " + RoleName(player.PrimaryRole) + " / " + RoleName(player.SecondaryRole) + " / " + RoleName(player.TertiaryRole), _labelStyle, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.MaxWidth(300f) }); List list = (WarfareManager.Instance?.GetServerBadges(player) ?? new List()).Take(4).ToList(); GUILayout.BeginHorizontal(Array.Empty()); foreach (string item in list) { Texture2D val = GetConstellationSmallBadgeTexture(item) ?? GetConstellationBadgeTexture(item); if ((Object)val != (Object)null) { GUILayout.Box((Texture)(object)val, _boxStyle, (GUILayoutOption[])(object)new GUILayoutOption[2] { GUILayout.Width(30f), GUILayout.Height(30f) }); } } GUILayout.EndHorizontal(); GUILayout.EndVertical(); GUILayout.FlexibleSpace(); GUILayout.BeginVertical((GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(190f) }); GUILayout.Label(PublicLastOnlineText(player, online), _labelStyle, Array.Empty()); GUILayout.Label("Legacy: " + PublicLegacyScore(player, clan), _labelGoldStyle, Array.Empty()); if (GUILayout.Button("View Profile", _smallButtonStyle, (GUILayoutOption[])(object)new GUILayoutOption[2] { GUILayout.Width(120f), GUILayout.Height(28f) })) { OpenPublicProfile(player, "All Players", 1, 1, _allPlayersScroll); GUIUtility.ExitGUI(); } GUILayout.EndVertical(); GUILayout.EndHorizontal(); Rect lastRect = GUILayoutUtility.GetLastRect(); Event current2 = Event.current; if (current2 != null && (int)current2.type == 0 && current2.button == 0 && ((Rect)(ref lastRect)).Contains(current2.mousePosition)) { current2.Use(); OpenPublicProfile(player, "All Players", 1, 1, _allPlayersScroll); GUIUtility.ExitGUI(); } } private void DrawTerritoriesHub(WarfareData data, PlayerClanInfo info, Clan clan) { string[] labels = new string[3] { "Territories", "Capital", "Colonies" }; DrawSectionTabs(labels, ref _territorySubTab, 180f); if (_territorySubTab == 0) { DrawTerritory(data, info, clan); return; } TerritoryType required = ((_territorySubTab == 1) ? TerritoryType.Capital : TerritoryType.Colony); List list = data.GetClanTerritories(info.ClanId).FindAll((ClanTerritory t) => t.Type == required); GUILayout.BeginVertical(_boxStyle, Array.Empty()); GUILayout.Label("" + required.ToString() + " Territories", _labelGoldStyle, Array.Empty()); if (list.Count == 0) { GUILayout.Label((required == TerritoryType.Capital) ? "No capital is assigned." : "No colonies are owned.", _labelStyle, Array.Empty()); } else { foreach (ClanTerritory item in list) { GUILayout.Label("" + item.DisplayName + " | " + BiomeDisplayName(item) + " | " + item.GridX + "," + item.GridY, _labelStyle, Array.Empty()); } } GUILayout.EndVertical(); } private void DrawEconomyHub(WarfareData data, PlayerClanInfo info, Clan clan) { string[] labels = new string[3] { "Treasury", "Taxes", "Reports" }; DrawSectionTabs(labels, ref _economySubTab, 180f); if (_economySubTab == 0) { DrawEconomy(data, info, clan); } else if (clan != null) { if (_economySubTab == 1) { GUILayout.BeginVertical(_infoPanelStyle, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(Mathf.Min(560f, AvailableContentWidth())) }); GUILayout.Label("Clan Taxes", _labelGoldStyle, Array.Empty()); DrawMiniStat("Resource Tax", clan.ResourceTaxPercent + "%"); DrawMiniStat("Coin Tax", clan.CoinTaxPercent + "%"); DrawMiniStat("This Session", clan.SessionResourceTaxCollected + " items / " + clan.SessionCoinTaxCollected + " coins"); DrawMiniStat("Total Collected", clan.TotalResourceTaxCollected + " items / " + clan.TotalCoinTaxCollected + " coins"); GUILayout.EndVertical(); } else { DrawEconomyReports(clan); } } } private void DrawEconomyReports(Clan clan) { //IL_005f: Unknown result type (might be due to invalid IL or missing references) //IL_007b: Unknown result type (might be due to invalid IL or missing references) //IL_0080: Unknown result type (might be due to invalid IL or missing references) List list = (from transaction in clan.TreasuryHistory ?? new List() where transaction != null orderby transaction.Timestamp descending select transaction).ToList(); _economyReportsScroll = GUILayout.BeginScrollView(_economyReportsScroll, false, true, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Height(AvailableContentHeight()) }); GUILayout.Label("ROYAL TREASURY JOURNAL", _majorSectionTitleStyle, Array.Empty()); GUILayout.Label("A read-only record of saved treasury activity.", _secondaryTextStyle, Array.Empty()); if (list.Count == 0) { DrawEmptyState("No treasury entries", "Treasury activity will appear here after recorded transactions occur."); GUILayout.Space(BottomSafePadding()); GUILayout.EndScrollView(); return; } long num = 0L; long num2 = 0L; Dictionary dictionary = new Dictionary(StringComparer.OrdinalIgnoreCase); Dictionary dictionary2 = new Dictionary(StringComparer.OrdinalIgnoreCase); Dictionary dictionary3 = new Dictionary(StringComparer.OrdinalIgnoreCase); foreach (EconomyTransaction item in list) { string key = (string.IsNullOrWhiteSpace(item.Type) ? "Other" : item.Type.Trim()); if (item.Amount > 0) { num += item.Amount; dictionary[key] = (dictionary.TryGetValue(key, out var value) ? (value + item.Amount) : item.Amount); string text = EconomyReportSource(item.Source); if (!string.IsNullOrEmpty(text)) { dictionary3[text] = (dictionary3.TryGetValue(text, out var value2) ? (value2 + item.Amount) : item.Amount); } } else if (item.Amount < 0) { long num3 = -item.Amount; num2 += num3; dictionary2[key] = (dictionary2.TryGetValue(key, out var value3) ? (value3 + num3) : num3); } } float width = ResponsiveCardWidth(AvailableContentWidth(reserveScrollbar: true) - 8f, 3); GUILayout.BeginHorizontal(Array.Empty()); DrawEconomyReportSummaryCard("RECORDED INCOME", ColoredCoins(num), UiTone.Positive, width); DrawEconomyReportSummaryCard("RECORDED EXPENSES", ColoredCoins(-num2), UiTone.Cost, width); DrawEconomyReportSummaryCard("NET RESULT", ColoredCoins(num - num2), (num >= num2) ? UiTone.Positive : UiTone.Cost, width); GUILayout.EndHorizontal(); float width2 = ResponsiveCardWidth(AvailableContentWidth(reserveScrollbar: true) - 6f, 2); GUILayout.BeginHorizontal(Array.Empty()); DrawEconomyReportBreakdown("INCOME BREAKDOWN", dictionary, UiTone.Positive, width2, expense: false); DrawEconomyReportBreakdown("EXPENSE BREAKDOWN", dictionary2, UiTone.Cost, width2, expense: true); GUILayout.EndHorizontal(); if (dictionary3.Count > 0) { GUILayout.BeginVertical(_infoPanelStyle, Array.Empty()); GUILayout.Label("TOP RECORDED INCOME SOURCES", CardTitleStyle(UiTone.Info), Array.Empty()); foreach (KeyValuePair item2 in dictionary3.OrderByDescending((KeyValuePair pair) => pair.Value).Take(3)) { DrawMiniStat(item2.Key, ColoredCoins(item2.Value) + " coins"); } GUILayout.EndVertical(); } GUILayout.Space(6f); GUILayout.Label("TREASURY LEDGER", _majorSectionTitleStyle, Array.Empty()); foreach (EconomyTransaction item3 in list) { DrawEconomyLedgerEntry(item3); } GUILayout.Space(BottomSafePadding()); GUILayout.EndScrollView(); } private void DrawEconomyReportSummaryCard(string title, string amount, UiTone tone, float width) { GUILayout.BeginVertical(PanelStyle(tone), (GUILayoutOption[])(object)new GUILayoutOption[2] { GUILayout.Width(width), GUILayout.Height(72f) }); GUILayout.Label("" + title + "", CardTitleStyle(tone), Array.Empty()); GUILayout.Label("" + amount + " Coins", _labelStyle, Array.Empty()); GUILayout.EndVertical(); } private void DrawEconomyReportBreakdown(string title, Dictionary values, UiTone tone, float width, bool expense) { GUILayout.BeginVertical(PanelStyle(tone), (GUILayoutOption[])(object)new GUILayoutOption[2] { GUILayout.Width(width), GUILayout.MinHeight(104f) }); GUILayout.Label("" + title + "", CardTitleStyle(tone), Array.Empty()); if (values.Count == 0) { GUILayout.Label("No recorded entries.", _secondaryTextStyle, Array.Empty()); } else { foreach (KeyValuePair item in values.OrderByDescending((KeyValuePair pair) => pair.Value)) { DrawMiniStat(item.Key, ColoredCoins(expense ? (-item.Value) : item.Value) + " coins"); } } GUILayout.EndVertical(); } private void DrawEconomyLedgerEntry(EconomyTransaction transaction) { GUILayout.BeginHorizontal((transaction.Amount >= 0) ? _positivePanelStyle : _boxStyle, (GUILayoutOption[])(object)new GUILayoutOption[2] { GUILayout.ExpandWidth(true), GUILayout.MinHeight(62f) }); GUILayout.BeginVertical((GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(190f) }); GUILayout.Label((transaction.Timestamp > 0) ? FormatTimestamp(transaction.Timestamp) : "Date unavailable", _secondaryTextStyle, Array.Empty()); GUILayout.Label("" + (string.IsNullOrWhiteSpace(transaction.Type) ? "Treasury Activity" : transaction.Type) + "", _labelGoldStyle, Array.Empty()); GUILayout.EndVertical(); GUILayout.BeginVertical((GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.ExpandWidth(true) }); string text = EconomyReportSource(transaction.Source); GUILayout.Label(string.IsNullOrEmpty(text) ? "Royal Treasury" : text, _bodyTextStyle, Array.Empty()); GUILayout.Label("Balance: " + TreasuryColored(transaction.BalanceBefore) + " → " + TreasuryColored(transaction.BalanceAfter), _secondaryTextStyle, Array.Empty()); GUILayout.EndVertical(); GUILayout.Label("" + ColoredCoins(transaction.Amount) + " Coins", (transaction.Amount >= 0) ? _positiveValueStyle : _negativeValueStyle, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(150f) }); GUILayout.EndHorizontal(); } private static string EconomyReportSource(string source) { if (string.IsNullOrWhiteSpace(source)) { return ""; } if (!string.Equals(source, "clan_givecoins", StringComparison.OrdinalIgnoreCase)) { return source.Trim(); } return "Realm administration"; } private void DrawRankingsHub(WarfareData data, PlayerClanInfo info, Clan clan) { string[] labels = new string[4] { "Players", "Clans", "Territories", "Economy" }; DrawSectionTabs(labels, ref _rankingsSubTab, 180f); DrawGlobalRankingStats(data, _rankingsSubTab); } private void DrawClansHub(WarfareData data, PlayerClanInfo info) { if (_viewedPublicClan != null) { DrawPublicClanProfile(data, info, _viewedPublicClan); return; } string[] labels = new string[3] { "Browse", "Recruitment", "Search" }; DrawSectionTabs(labels, ref _clansSubTab, 180f); if (_clansSubTab == 0) { DrawClansDirectory(data, info); } else { DrawRoadmapInfoCard((_clansSubTab == 1) ? "Clan Recruitment" : "Clan Search", "COMING SOON", (_clansSubTab == 1) ? "Clan recruitment notices and applications will appear here." : "Clan search and filters will appear here.", "These directory tools will expand without another top-level tab."); } } private void DrawHomesteadsHub(WarfareData data, PlayerClanInfo info) { string[] labels = new string[3] { "Freeborn", "Browse", "Top Homesteads" }; DrawSectionTabs(labels, ref _homesteadsSubTab, 180f); DrawHomesteadsDirectory(data, info); } private void DrawAllIdsHub(WarfareData data) { string[] array = new string[8] { "Players", "Clans", "Territories", "Objects", "NPC", "Citizens", "Hirelings", "Vendors" }; DrawSectionTabs(array, ref _allIdsSection, 160f); if (_allIdsSection == 0) { DrawPlayerIdsSection(data); } else if (_allIdsSection == 1) { DrawClanIdsSection(data); } else if (_allIdsSection == 2) { DrawHomesteadIdsSection(data); } else { DrawRoadmapInfoCard(array[_allIdsSection], "ADMIN DIRECTORY", "This identifier directory will be populated when the related system becomes active.", "All IDs is reserved for technical reference and admin support, not gameplay."); } } private void DrawHistoryHub(WarfareData data) { string[] array = new string[4] { "Wars", "Territories", "Economy", "Diplomacy" }; DrawSectionTabs(array, ref _historySubTab, 180f); DrawRoadmapInfoCard("History: " + array[_historySubTab], "READ ONLY", "Historical records will be displayed here without active controls or duplicated mechanics.", "Wars, territory changes, economy and diplomacy history remain archival views only."); } private void DrawFaqHub() { string[] array = new string[5] { "Getting Started", "Territories", "Economy", "Warfare", "Rules" }; DrawSectionTabs(array, ref _faqSubTab, 160f); DrawRoadmapInfoCard("FAQ / Q&A: " + array[_faqSubTab], "COMING SOON", "Server guidance and player answers will be curated here.", "The FAQ keeps help separate from mechanics and can grow internally."); } private void DrawSagaHub(WarfareData data) { string[] array = new string[4] { "Hall of Fame", "Great Wars", "Legendary Clans", "World Chronicle" }; DrawSectionTabs(array, ref _sagaSubTab, 180f); DrawRoadmapInfoCard("Saga: " + array[_sagaSubTab], "LORE ARCHIVE", "This is a lore-only record of the world and its people.", "Saga entries never create active gameplay mechanics."); } private void DrawGamesHub(WarfareData data, PlayerClanInfo info) { _tab = 18; if (_gamesSubTab > 4) { _gamesSubTab = 4; } string[] labels = new string[5] { "Overview", "Chess", "Dice", "Poker", "Game Hall" }; DrawSectionTabs(labels, ref _gamesSubTab, 180f); _lastGamesSubTab = _gamesSubTab; if (_gamesSubTab == 0) { DrawGamesLobby(data, info); } else if (_gamesSubTab == 1) { DrawChessGames(data, info); } else if (_gamesSubTab == 2) { DrawDiceGames(data, info); } else if (_gamesSubTab == 3) { DrawPokerGames(data, info); } else if (_gamesSubTab == 4) { DrawGameHall(data, info); } } private static bool IsActiveGameTable(FehuGameTable table) { if (table != null) { if (table.Status != FehuGameStatus.Waiting) { return table.Status == FehuGameStatus.Active; } return true; } return false; } private void DrawGameHall(WarfareData data, PlayerClanInfo info) { if (_gameHallSubTab > 3) { _gameHallSubTab = 0; } string[] labels = new string[4] { "Overview", "Statistics", "Rankings", "History" }; DrawSectionTabs(labels, ref _gameHallSubTab, 150f); GUILayout.BeginHorizontal(Array.Empty()); GUILayout.BeginVertical((GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.MaxWidth(Mathf.Max(900f, (float)Screen.width - 120f)) }); if (_gameHallSubTab == 0) { DrawGameHallOverview(data, info); } else if (_gameHallSubTab == 1) { DrawMyGameStats(data, info); } else if (_gameHallSubTab == 2) { DrawGamesLeaderboards(data); } else { DrawGamesHistory(data); } GUILayout.EndVertical(); GUILayout.Space(28f); GUILayout.EndHorizontal(); } private void DrawGameHallOverview(WarfareData data, PlayerClanInfo info) { GUILayout.BeginVertical(_boxStyle, Array.Empty()); GUILayout.Label("Game Hall Overview", _labelGoldStyle, Array.Empty()); FehuGamePlayerStats value = null; data?.GameStats?.TryGetValue(info.PlayerId, out value); if (value == null) { value = new FehuGamePlayerStats(); } GUILayout.BeginVertical(_boxStyle, Array.Empty()); GUILayout.Label("Global Summary", _labelGoldStyle, Array.Empty()); GUILayout.BeginHorizontal(Array.Empty()); float num = ((value.GamesPlayed > 0) ? ((float)value.Wins * 100f / (float)value.GamesPlayed) : 0f); DrawStatPill("Games", value.GamesPlayed.ToString(), 150f); DrawStatPill("Wins", value.Wins.ToString(), 150f); DrawStatPill("Losses", value.Losses.ToString(), 150f); DrawStatPill("Profit", value.Profit.ToString(), 150f); DrawStatPill("Favorite Game", value.FavoriteGame.ToString(), 190f); DrawStatPill("Win Rate", num.ToString("0.0") + "%", 150f); GUILayout.FlexibleSpace(); GUILayout.EndHorizontal(); GUILayout.EndVertical(); GUILayout.BeginHorizontal(Array.Empty()); DrawGameOverviewCard(FehuGameType.Chess, "Chess", "Games Played: " + value.ChessGamesPlayed + "\nWins: " + value.ChessWins + "\nLosses: " + value.ChessLosses + "\nDraws: " + value.ChessDraws + "\nWin Rate: " + ChessWinRate(value).ToString("0.0") + "%\nCurrent Rating: " + value.ChessRating + "\nHighest Rating: " + FehuGameProfileStats.FromStats(value).HighestRating + "\nCurrent / Best Streak: " + value.ChessCurrentStreak + " / " + value.ChessBestStreak); DrawGameOverviewCard(FehuGameType.Dice, "Dice", "Games Played: " + DiceGamesPlayed(value) + "\nWins: " + DiceWins(value) + "\nLosses: " + DiceLosses(value) + "\nAverage Bet: tracked from new tables\nLargest Win: " + value.BiggestWin + "\nHighest Roll: " + value.HighestDiceRoll + "\nCurrent / Best Streak: " + value.DiceCurrentStreak + " / " + value.DiceBestStreak + "\nProfit: " + value.Profit); DrawGameOverviewCard(FehuGameType.Poker, "Poker", "Games Played: " + value.PokerGamesPlayed + "\nWins: " + value.PokerHandsWon + "\nLosses: " + Math.Max(0, value.PokerGamesPlayed - value.PokerHandsWon) + "\nCoins Won/Lost: net " + value.PokerProfit + "\nLargest Pot Won: " + value.PokerBiggestPot + "\nTables Won: " + value.PokerTablesWon + "\nBiggest Win: " + value.PokerBiggestWin); GUILayout.EndHorizontal(); GUILayout.EndVertical(); } private void DrawGameOverviewCard(FehuGameType gameType, string title, string body) { GUILayout.BeginVertical(_boxStyle, (GUILayoutOption[])(object)new GUILayoutOption[2] { GUILayout.Width(ResponsiveCardWidth(AvailableContentWidth() - 20f, 3)), GUILayout.MinHeight(250f) }); DrawGameIconHeader(gameType, title); GUILayout.Label(body, _labelStyle, Array.Empty()); GUILayout.EndVertical(); } private void DrawStatPill(string label, string value, float width) { GUILayout.BeginVertical(_boxStyle, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(width) }); GUILayout.Label("" + label + "", _labelGoldStyle, Array.Empty()); GUILayout.Label(value, _labelStyle, Array.Empty()); GUILayout.EndVertical(); } private void DrawGameIconHeader(FehuGameType gameType, string title) { //IL_0013: Unknown result type (might be due to invalid IL or missing references) //IL_001e: Expected O, but got Unknown GUILayout.BeginHorizontal(Array.Empty()); Texture2D gameIconTexture = GetGameIconTexture(gameType); if ((Object)gameIconTexture != (Object)null) { GUILayout.Box((Texture)(object)gameIconTexture, GUIStyle.none, (GUILayoutOption[])(object)new GUILayoutOption[2] { GUILayout.Width(42f), GUILayout.Height(42f) }); } GUILayout.Label("" + title + "", _labelGoldStyle, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Height(42f) }); GUILayout.EndHorizontal(); } private Texture2D GetGameIconTexture(FehuGameType gameType) { if (_gameIconTextures == null) { _gameIconTextures = LoadRuntimePngTextures("Games", recursive: false); if (_gameIconTextures.Count == 0) { _gameIconTextures = LoadRuntimePngTextures(Path.Combine("FehuKingdoms", "Games"), recursive: false); } } string text = gameType switch { FehuGameType.Dice => "dice_icon", FehuGameType.Chess => "chess_icon", _ => "poker_icon", }; if (!_gameIconTextures.TryGetValue(text, out var value) && !_gameIconTextures.TryGetValue(NormalizeAssetKey(text), out value)) { return null; } return value; } private void DrawGamesLobby(WarfareData data, PlayerClanInfo info) { GUILayout.BeginVertical(_boxStyle, Array.Empty()); GUILayout.Label("Games Overview", _labelGoldStyle, Array.Empty()); GUILayout.Label("Chess, Dice and Poker tables share saved server history, escrow recovery and player statistics.", _labelStyle, Array.Empty()); DrawGamesInvites(data, info); GUILayout.Space(8f); GUILayout.BeginHorizontal(Array.Empty()); DrawGameDashboardCard(data, info, FehuGameType.Chess, "Chess", "Classic two-player board duel with rating, resign, draw and time-control support.", 1); DrawGameDashboardCard(data, info, FehuGameType.Dice, "Dice", "Fast wager tables with Highest Roll, Best of 3 and Lucky Seven modes.", 2); DrawGameDashboardCard(data, info, FehuGameType.Poker, "Poker", "Texas Hold'em with blinds, check/call/raise/fold, all-in and side-pot payout support.", 3); GUILayout.EndHorizontal(); GUILayout.Space(10f); GUILayout.Label("Active Tables", _labelGoldStyle, Array.Empty()); List list = (data.Games ?? new List()).FindAll(IsActiveGameTable); if (list.Count == 0) { GUILayout.Label("No active game tables. Create one from Chess, Dice or Poker.", _labelStyle, Array.Empty()); } else { foreach (FehuGameTable item in list) { GUILayout.BeginVertical(_boxStyle, Array.Empty()); DrawGameIconHeader(item.GameType, item.GameType.ToString() + ": " + item.TableName); GUILayout.Label("Status: " + FormatGameStatus(item) + " Host: " + item.HostPlayerName + " Players: " + item.PlayerIds.Count + "/" + item.PlayerLimit + " Stake: " + FormatGameStake(item.StakeType, item.StakeAmount, item.StakePrefabName) + " Spectators: " + item.SpectatorIds.Count, _labelStyle, Array.Empty()); GUILayout.BeginHorizontal(Array.Empty()); if (GUILayout.Button("Open", _infoButtonStyle, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(90f) })) { OpenGameSubTab(item.GameType, item.GameId); } if (item.Status == FehuGameStatus.Waiting && !item.PlayerIds.Contains(info.PlayerId) && !item.IsPrivate && GUILayout.Button("Join", _smallButtonStyle, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(90f) })) { RequestLobbyAction(item, 2); } if (item.AllowSpectators && !item.PlayerIds.Contains(info.PlayerId) && !item.SpectatorIds.Contains(info.PlayerId) && GUILayout.Button("Spectate", _smallButtonStyle, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(90f) })) { RequestLobbyAction(item, 5); } if (item.Status == FehuGameStatus.Waiting && item.HostPlayerId == info.PlayerId && GUILayout.Button("Start", _smallButtonStyle, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(90f) })) { RequestLobbyAction(item, 3); } if (item.Status == FehuGameStatus.Waiting && item.HostPlayerId == info.PlayerId && GUILayout.Button("Cancel", _smallButtonStyle, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(90f) })) { RequestLobbyAction(item, 4); } if (item.Status == FehuGameStatus.Waiting && item.HostPlayerId == info.PlayerId && GUILayout.Button("Delete", _smallButtonStyle, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(90f) })) { RequestLobbyDelete(item); } GUILayout.EndHorizontal(); GUILayout.EndVertical(); } } GUILayout.EndVertical(); } private void OpenGameSubTab(FehuGameType gameType, int gameId) { _gamesSubTab = gameType switch { FehuGameType.Dice => 2, FehuGameType.Chess => 1, _ => 3, }; _lastGamesSubTab = _gamesSubTab; switch (gameType) { case FehuGameType.Chess: _selectedChessGameId = gameId; _selectedChessSquare = -1; break; case FehuGameType.Dice: _selectedDiceTableId = gameId; break; case FehuGameType.Poker: _selectedPokerGameId = gameId; break; } } private void DrawGameDashboardCard(WarfareData data, PlayerClanInfo info, FehuGameType gameType, string title, string description, int openTab) { List active = (data.Games ?? new List()).FindAll((FehuGameTable game) => game.GameType == gameType && IsActiveGameTable(game)); List list = (data.GameHistory ?? new List()).FindAll((FehuGameHistoryEntry entry) => entry.GameType == gameType); FehuGamePlayerStats value = null; data.GameStats?.TryGetValue(info.PlayerId, out value); GameDashboardNumbers gameDashboardNumbers = GetGameDashboardNumbers(gameType, value, active, list, info.PlayerId); GUILayout.BeginVertical(_boxStyle, (GUILayoutOption[])(object)new GUILayoutOption[2] { GUILayout.Width(360f), GUILayout.Height(300f) }); DrawGameIconHeader(gameType, title); GUILayout.Label(description, _labelStyle, Array.Empty()); GUILayout.Space(6f); DrawMiniStat("Players currently playing", gameDashboardNumbers.ActivePlayers.ToString()); DrawMiniStat("Games in progress", gameDashboardNumbers.ActiveGames.ToString()); DrawMiniStat("Total games played", list.Count.ToString()); DrawMiniStat("Your games played", gameDashboardNumbers.YourGames.ToString()); DrawMiniStat("Your wins", gameDashboardNumbers.YourWins.ToString()); DrawMiniStat("Average duration", "Tracked in future matches"); DrawMiniStat("Best streak", gameDashboardNumbers.BestStreak.ToString()); GUILayout.FlexibleSpace(); if (GUILayout.Button("Open", _buttonStyle, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Height(32f) })) { _gamesSubTab = openTab; _lastGamesSubTab = openTab; } GUILayout.EndVertical(); } private static GameDashboardNumbers GetGameDashboardNumbers(FehuGameType gameType, FehuGamePlayerStats stats, List active, List history, long playerId) { GameDashboardNumbers result = new GameDashboardNumbers { ActiveGames = active.Count, ActivePlayers = active.Sum((FehuGameTable game) => game.PlayerIds?.Count ?? 0) }; if (stats != null) { switch (gameType) { case FehuGameType.Chess: result.YourGames = stats.ChessGamesPlayed; result.YourWins = stats.ChessWins; result.BestStreak = stats.ChessBestStreak; break; case FehuGameType.Poker: result.YourGames = stats.PokerGamesPlayed; result.YourWins = stats.PokerHandsWon; result.BestStreak = stats.PokerTablesWon; break; default: result.YourGames = DiceGamesPlayed(stats); result.YourWins = DiceWins(stats); result.BestStreak = stats.DiceBestStreak; break; } } return result; } private static string FormatGameStatus(FehuGameTable table) { if (table == null) { return "Unknown"; } if (table.EscrowCollecting) { return "Collecting escrow"; } if (table.Status == FehuGameStatus.Waiting) { return "Waiting for players"; } if (table.Status == FehuGameStatus.Active) { return "In Progress"; } if (table.Status == FehuGameStatus.Cancelled) { return "Cancelled"; } return "Finished"; } private void RequestLobbyAction(FehuGameTable table, int action) { if (table.GameType == FehuGameType.Dice) { RequestDiceActionKeepTab(action, table.GameId); } else if (table.GameType == FehuGameType.Chess) { RequestChessActionKeepTab(action, table.GameId); } else if (table.GameType == FehuGameType.Poker) { RequestPokerActionKeepTab(action switch { 4 => 11, 5 => 4, _ => action, }, table.GameId, "", 2, 100L, 1L, 2L, isPrivate: false, allowSpectators: true, 0L); } } private void RequestLobbyDelete(FehuGameTable table) { if (table.GameType == FehuGameType.Dice) { RequestDiceActionKeepTab(6, table.GameId); } else if (table.GameType == FehuGameType.Chess) { RequestChessActionKeepTab(14, table.GameId); } else if (table.GameType == FehuGameType.Poker) { RequestPokerActionKeepTab(12, table.GameId, "", 2, 100L, 1L, 2L, isPrivate: false, allowSpectators: true, 0L); } } private void DrawChessGames(WarfareData data, PlayerClanInfo info) { RestoreChessGamesTab(); List diceStakeItems = GetDiceStakeItems(); if (_chessStakeItemIndex >= diceStakeItems.Count) { _chessStakeItemIndex = 0; } List availableGameStakeTerritories = GetAvailableGameStakeTerritories(data, info); if (_chessStakeTerritoryIndex >= availableGameStakeTerritories.Count) { _chessStakeTerritoryIndex = 0; } List list = (data.Games ?? new List()).FindAll((FehuGameTable game) => game.GameType == FehuGameType.Chess && IsActiveGameTable(game) && IsChessMatchVisibleToPlayer(game, info)); if (_selectedChessGameId == 0 && list.Count > 0) { _selectedChessGameId = list[0].GameId; } FehuGameTable fehuGameTable = list.Find((FehuGameTable game) => game.GameId == _selectedChessGameId); if (fehuGameTable == null && list.Count > 0) { _selectedChessGameId = list[0].GameId; fehuGameTable = list[0]; } GUILayout.BeginHorizontal(Array.Empty()); GUILayout.BeginVertical(_boxStyle, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(360f) }); if (fehuGameTable != null) { DrawCurrentGameSidePanel("Current Chess Match", fehuGameTable, data, info, delegate { _selectedChessGameId = 0; _selectedChessSquare = -1; }); } else { GUILayout.Label("Create Chess Match", _labelGoldStyle, Array.Empty()); GUILayout.Label("Match Name", _labelStyle, Array.Empty()); _chessMatchName = GUILayout.TextField(_chessMatchName, 32, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(320f) }); GUILayout.Label("Color Preference", _labelStyle, Array.Empty()); string[] array = new string[3] { "Random", "White", "Black" }; GUILayout.BeginHorizontal(Array.Empty()); for (int num = 0; num < array.Length; num++) { if (GUILayout.Button(array[num], (_chessColorPreference == num) ? _tabActiveStyle : _tabStyle, (GUILayoutOption[])(object)new GUILayoutOption[2] { GUILayout.Width(104f), GUILayout.Height(34f) })) { _chessColorPreference = num; } } GUILayout.EndHorizontal(); GUILayout.Label("Time Control", _labelStyle, Array.Empty()); if (_chessTimerIndex > 4) { _chessTimerIndex = 4; } string[] array2 = new string[5] { "No Limit", "5 min", "10 min", "15 min", "Custom" }; GUILayout.BeginHorizontal(Array.Empty()); for (int num2 = 0; num2 < array2.Length; num2++) { if (GUILayout.Button(array2[num2], (_chessTimerIndex == num2) ? _tabActiveStyle : _tabStyle, (GUILayoutOption[])(object)new GUILayoutOption[2] { GUILayout.Width(64f), GUILayout.Height(34f) })) { _chessTimerIndex = num2; } } GUILayout.EndHorizontal(); if (_chessTimerIndex == 4) { GUILayout.BeginHorizontal(Array.Empty()); GUILayout.Label("Custom minutes", _labelStyle, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(136f) }); _chessCustomMinutes = GUILayout.TextField(_chessCustomMinutes, 4, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(86f) }); GUILayout.EndHorizontal(); } GUILayout.Label("Stake", _labelStyle, Array.Empty()); string[] array3 = new string[4] { "None", "Coins", "Item", "Territory" }; GUILayout.BeginHorizontal(Array.Empty()); for (int num3 = 0; num3 < array3.Length; num3++) { if (GUILayout.Button(array3[num3], (_chessStakeTypeIndex == num3) ? _tabActiveStyle : _tabStyle, (GUILayoutOption[])(object)new GUILayoutOption[2] { GUILayout.Width(80f), GUILayout.Height(34f) })) { _chessStakeTypeIndex = num3; } } GUILayout.EndHorizontal(); if (_chessStakeTypeIndex == 1) { GUILayout.BeginHorizontal(Array.Empty()); GUILayout.Label("Coin Stake", _labelStyle, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(110f) }); _chessStakeInput = GUILayout.TextField(_chessStakeInput, 10, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(110f) }); GUILayout.EndHorizontal(); } else if (_chessStakeTypeIndex == 2) { GUILayout.BeginHorizontal(Array.Empty()); GUILayout.Label("Item prefab", _labelStyle, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(92f) }); _chessStakePrefabInput = GUILayout.TextField(_chessStakePrefabInput, 40, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(200f) }); GUILayout.EndHorizontal(); GUILayout.BeginHorizontal(Array.Empty()); GUILayout.Label("Amount", _labelStyle, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(92f) }); _chessStakeInput = GUILayout.TextField(_chessStakeInput, 10, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(110f) }); GUILayout.EndHorizontal(); GUILayout.Label("Opponent must deposit the same item stake before start.", _labelStyle, Array.Empty()); } else if (_chessStakeTypeIndex == 3) { GUILayout.Label((info.Rank == ClanRank.Jarl) ? "Territory stake: Jarl only. Territory locking/transfer is disabled until server-safe territory escrow is available." : "Territory stakes are Jarl only.", _labelStyle, Array.Empty()); } if (DrawToggleButton("Private Match", _chessPrivate, 320f)) { _chessPrivate = !_chessPrivate; } if (DrawToggleButton("Allow Spectators", _chessSpectators, 320f)) { _chessSpectators = !_chessSpectators; } int result = 0; int.TryParse(_chessStakeInput, out result); bool flag = _chessStakeTypeIndex != 3 && (_chessStakeTypeIndex != 2 || (result > 0 && !string.IsNullOrWhiteSpace(_chessStakePrefabInput))); if (_chessStakeTypeIndex == 2 && !flag) { GUILayout.Label("Item stake needs a prefab and amount greater than zero.", _labelStyle, Array.Empty()); } GUI.enabled = flag; if (GUILayout.Button("Create Chess Match", _buttonStyle, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(320f) })) { int result2 = 0; if (_chessStakeTypeIndex == 1 || _chessStakeTypeIndex == 2) { int.TryParse(_chessStakeInput, out result2); } int.TryParse(_chessCustomMinutes, out var result3); int[] array4 = new int[4] { 0, 300, 600, 900 }; int timeControlSeconds = ((_chessTimerIndex == 4) ? Mathf.Clamp((result3 <= 0) ? 1200 : (result3 * 60), 60, 7200) : array4[_chessTimerIndex]); FehuStakeType stakeType = ((_chessStakeTypeIndex == 2) ? FehuStakeType.Item : ((_chessStakeTypeIndex == 1) ? FehuStakeType.Coins : FehuStakeType.None)); RequestChessActionKeepTab(1, 0, _chessMatchName, _chessColorPreference, timeControlSeconds, Mathf.Max(0, result2), _chessPrivate, _chessSpectators, -1, -1, 'Q', stakeType, _chessStakePrefabInput); } GUI.enabled = true; GUILayout.Space(8f); GUILayout.Label("Active Matches", _labelGoldStyle, Array.Empty()); foreach (FehuGameTable item in list) { if (GUILayout.Button("#" + item.GameId + " " + item.TableName + "\n" + item.PlayerIds.Count + "/2 | " + item.Status.ToString() + " | " + FormatGameStake(item.StakeType, item.StakeAmount, item.StakePrefabName), (item.GameId == _selectedChessGameId) ? _tabActiveStyle : _tabStyle, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(320f) })) { _selectedChessGameId = item.GameId; _selectedChessSquare = -1; } } } GUILayout.EndVertical(); GUILayout.BeginVertical(_boxStyle, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(540f) }); if (fehuGameTable == null) { GUILayout.Label("Create or select a chess match.", _labelStyle, Array.Empty()); } else if (fehuGameTable.Status != FehuGameStatus.Active || fehuGameTable.Chess == null) { DrawChessWaiting(fehuGameTable, info); } else { DrawChessBoard(fehuGameTable, info); } GUILayout.EndVertical(); GUILayout.BeginVertical(_boxStyle, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(330f) }); DrawChessInfo(fehuGameTable, data, info); GUILayout.EndVertical(); GUILayout.EndHorizontal(); } private void DrawCurrentGameSidePanel(string title, FehuGameTable table, WarfareData data, PlayerClanInfo info, Action backAction) { DrawGameIconHeader(table.GameType, title); GUILayout.Label("" + table.TableName + "", _labelGoldStyle, Array.Empty()); GUILayout.Label("Players: " + table.PlayerIds.Count + "/" + table.PlayerLimit + "\nStake: " + FormatGameStake(table.StakeType, table.StakeAmount, table.StakePrefabName) + "\nStatus: " + table.Status.ToString() + "\nSpectators: " + table.SpectatorIds.Count, _labelStyle, Array.Empty()); if (GUILayout.Button("Back to Tables", _buttonStyle, Array.Empty())) { backAction?.Invoke(); } if (table.GameType == FehuGameType.Chess) { if (table.Status == FehuGameStatus.Active && table.PlayerIds.Contains(info.PlayerId) && GUILayout.Button("Resign", _buttonStyle, Array.Empty())) { RequestChessActionKeepTab(7, table.GameId); } if (table.Status == FehuGameStatus.Finished && table.HostPlayerId == info.PlayerId && GUILayout.Button("Close Match", _buttonStyle, Array.Empty())) { RequestChessActionKeepTab(14, table.GameId); } } else if (table.GameType == FehuGameType.Dice) { if ((table.Status == FehuGameStatus.Waiting || table.Status == FehuGameStatus.Active) && table.PlayerIds.Contains(info.PlayerId) && GUILayout.Button("Leave Table", _buttonStyle, Array.Empty())) { RequestDiceActionKeepTab(8, table.GameId); } if (table.Status == FehuGameStatus.Finished && table.HostPlayerId == info.PlayerId && GUILayout.Button("Close Table", _buttonStyle, Array.Empty())) { RequestDiceActionKeepTab(6, table.GameId); } } else if (table.GameType == FehuGameType.Poker) { if (table.Poker == null && table.PlayerIds.Contains(info.PlayerId) && GUILayout.Button("Leave Table", _buttonStyle, Array.Empty())) { RequestPokerActionKeepTab(9, table.GameId, "", 2, 100L, 1L, 2L, isPrivate: false, allowSpectators: true, 0L); } if (table.Status == FehuGameStatus.Finished && table.HostPlayerId == info.PlayerId && GUILayout.Button("Close Table", _buttonStyle, Array.Empty())) { RequestPokerActionKeepTab(12, table.GameId, "", 2, 100L, 1L, 2L, isPrivate: false, allowSpectators: true, 0L); } } } private void DrawChessWaiting(FehuGameTable match, PlayerClanInfo info) { GUILayout.Label("" + match.TableName + "", _labelGoldStyle, Array.Empty()); GUILayout.Label("Host: " + match.HostPlayerName + " | Players: " + match.PlayerIds.Count + "/2", _labelStyle, Array.Empty()); GUILayout.Label("Stake: " + FormatGameStake(match.StakeType, match.StakeAmount, match.StakePrefabName) + " | Spectators: " + match.SpectatorIds.Count, _labelStyle, Array.Empty()); if (match.Status == FehuGameStatus.Waiting && !match.PlayerIds.Contains(info.PlayerId) && !match.IsPrivate && GUILayout.Button("Join Match", _buttonStyle, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(220f) })) { RequestChessActionKeepTab(2, match.GameId); } if (match.Status == FehuGameStatus.Waiting && match.HostPlayerId == info.PlayerId && GUILayout.Button("Start Match", _buttonStyle, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(220f) })) { RequestChessActionKeepTab(3, match.GameId); } if (match.Status == FehuGameStatus.Waiting && match.HostPlayerId == info.PlayerId && GUILayout.Button("Cancel Match", _buttonStyle, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(220f) })) { RequestChessActionKeepTab(4, match.GameId); } if (match.Status == FehuGameStatus.Waiting && match.HostPlayerId == info.PlayerId && GUILayout.Button("Delete Match", _buttonStyle, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(220f) })) { RequestChessActionKeepTab(14, match.GameId); } if (match.AllowSpectators && !match.PlayerIds.Contains(info.PlayerId) && !match.SpectatorIds.Contains(info.PlayerId) && GUILayout.Button("Spectate", _buttonStyle, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(220f) })) { RequestChessActionKeepTab(5, match.GameId); } GUILayout.Label("Board appears when both players have joined and the host starts the match.", _labelStyle, Array.Empty()); } private void DrawChessBoard(FehuGameTable match, PlayerClanInfo info) { //IL_01e0: Unknown result type (might be due to invalid IL or missing references) //IL_01e5: Unknown result type (might be due to invalid IL or missing references) //IL_01f1: Unknown result type (might be due to invalid IL or missing references) //IL_021a: Unknown result type (might be due to invalid IL or missing references) //IL_020a: Unknown result type (might be due to invalid IL or missing references) ChessGameState chess = match.Chess; bool flag = chess.WhitePlayerId == info.PlayerId || chess.BlackPlayerId == info.PlayerId; List list = ((_selectedChessSquare >= 0) ? ChessRuleEngine.GetLegalMoves(chess, _selectedChessSquare) : new List()); GUILayout.Label("" + match.TableName + " | " + (chess.WhiteToMove ? "White to move" : "Black to move"), _labelGoldStyle, Array.Empty()); float num = 56f; GUILayout.BeginHorizontal(Array.Empty()); GUILayout.FlexibleSpace(); GUILayout.BeginVertical(Array.Empty()); for (int num2 = 7; num2 >= 0; num2--) { GUILayout.BeginHorizontal(Array.Empty()); GUILayout.Label((num2 + 1).ToString(), _labelStyle, (GUILayoutOption[])(object)new GUILayoutOption[2] { GUILayout.Width(22f), GUILayout.Height(num) }); for (int i = 0; i < 8; i++) { int num3 = num2 * 8 + i; char c = chess.Board[num3]; GUIStyle val = ((((num2 + i) & 1) == 0) ? _chessLightSquareStyle : _chessDarkSquareStyle); if (GUILayout.Button(GUIContent.none, val, (GUILayoutOption[])(object)new GUILayoutOption[2] { GUILayout.Width(num), GUILayout.Height(num) }) && flag) { if (_selectedChessSquare >= 0 && list.Contains(num3)) { RequestChessActionKeepTab(6, match.GameId, "", 0, 0, 0, isPrivate: false, allowSpectators: false, _selectedChessSquare, num3, _chessPromotion); _selectedChessSquare = -1; } else if (c != '.' && ChessRuleEngine.IsWhite(c) == chess.WhiteToMove && ((chess.WhiteToMove && chess.WhitePlayerId == info.PlayerId) || (!chess.WhiteToMove && chess.BlackPlayerId == info.PlayerId))) { _selectedChessSquare = num3; } } Rect lastRect = GUILayoutUtility.GetLastRect(); if (num3 == _selectedChessSquare) { GUI.DrawTexture(lastRect, (Texture)(object)_chessSelectedOverlayTex); } else if (list.Contains(num3)) { GUI.DrawTexture(lastRect, (Texture)(object)_chessLegalOverlayTex); } DrawChessPieceIcon(c, lastRect); } GUILayout.EndHorizontal(); } GUILayout.BeginHorizontal(Array.Empty()); GUILayout.Label(" ", _labelStyle, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(22f) }); for (int j = 0; j < 8; j++) { GUILayout.Label(((char)(65 + j)).ToString(), _labelStyle, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(num) }); } GUILayout.EndHorizontal(); GUILayout.EndVertical(); GUILayout.FlexibleSpace(); GUILayout.EndHorizontal(); GUILayout.BeginHorizontal(Array.Empty()); GUILayout.Label("Promotion", _labelStyle, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(82f) }); char[] array = new char[4] { 'Q', 'R', 'B', 'N' }; foreach (char c2 in array) { if (DrawPromotionButton(c2)) { _chessPromotion = c2; } } GUILayout.EndHorizontal(); } private void DrawChessInfo(FehuGameTable match, WarfareData data, PlayerClanInfo info) { GUILayout.Label("Match Info", _labelGoldStyle, Array.Empty()); if (match == null) { GUILayout.Label("No match selected.", _labelStyle, Array.Empty()); return; } GUILayout.Label("Status: " + match.Status.ToString() + "\nStake: " + FormatGameStake(match.StakeType, match.StakeAmount, match.StakePrefabName) + "\nSpectators: " + match.SpectatorIds.Count, _labelStyle, Array.Empty()); ChessGameState chess = match.Chess; if (chess == null) { return; } GUILayout.Label("White: " + (data.GetPlayer(chess.WhitePlayerId)?.PlayerName ?? chess.WhitePlayerId.ToString()) + "\nBlack: " + (data.GetPlayer(chess.BlackPlayerId)?.PlayerName ?? chess.BlackPlayerId.ToString()), _labelStyle, Array.Empty()); if (chess.TimeControlSeconds > 0) { GUILayout.Label("Clock: White " + FormatChessClock(chess.WhiteSeconds) + " | Black " + FormatChessClock(chess.BlackSeconds), _labelStyle, Array.Empty()); } GUILayout.Label("Captured: White " + GetCapturedChessPieces(chess, whitePieces: true) + " | Black " + GetCapturedChessPieces(chess, whitePieces: false), _labelStyle, Array.Empty()); GUILayout.Label("Move History", _labelGoldStyle, Array.Empty()); for (int i = Math.Max(0, chess.Moves.Count - 12); i < chess.Moves.Count; i += 2) { string text = i / 2 + 1 + ". " + chess.Moves[i].Notation; if (i + 1 < chess.Moves.Count) { text = text + " ... " + chess.Moves[i + 1].Notation; } GUILayout.Label(text, _labelStyle, Array.Empty()); } if (match.Status != FehuGameStatus.Active || (chess.WhitePlayerId != info.PlayerId && chess.BlackPlayerId != info.PlayerId)) { return; } if (GUILayout.Button("Resign", _buttonStyle, Array.Empty())) { RequestChessActionKeepTab(7, match.GameId); } if (chess.DrawOfferedBy == 0L && GUILayout.Button("Offer Draw", _buttonStyle, Array.Empty())) { RequestChessActionKeepTab(8, match.GameId); } if (chess.DrawOfferedBy != 0L && chess.DrawOfferedBy != info.PlayerId) { if (GUILayout.Button("Accept Draw", _buttonStyle, Array.Empty())) { RequestChessActionKeepTab(9, match.GameId); } if (GUILayout.Button("Decline Draw", _buttonStyle, Array.Empty())) { RequestChessActionKeepTab(10, match.GameId); } } if (chess.UndoRequestedBy == 0L && chess.Moves.Count > 0 && GUILayout.Button("Request Undo", _buttonStyle, Array.Empty())) { RequestChessActionKeepTab(11, match.GameId); } if (chess.UndoRequestedBy != 0L && chess.UndoRequestedBy != info.PlayerId) { if (GUILayout.Button("Accept Undo", _buttonStyle, Array.Empty())) { RequestChessActionKeepTab(12, match.GameId); } if (GUILayout.Button("Decline Undo", _buttonStyle, Array.Empty())) { RequestChessActionKeepTab(13, match.GameId); } } } private bool DrawToggleButton(string label, bool enabled, float width) { return GUILayout.Button(label + ": " + (enabled ? "ON" : "OFF"), enabled ? _tabActiveStyle : _tabStyle, (GUILayoutOption[])(object)new GUILayoutOption[2] { GUILayout.Width(width), GUILayout.Height(34f) }); } private static bool IsChessMatchVisibleToPlayer(FehuGameTable match, PlayerClanInfo info) { if (match == null || info == null) { return false; } if (!match.IsPrivate) { return true; } if (match.HostPlayerId != info.PlayerId && !match.PlayerIds.Contains(info.PlayerId)) { return match.SpectatorIds.Contains(info.PlayerId); } return true; } private void RestoreChessGamesTab() { RestoreGamesUiState(1); } private void RestoreGamesUiState(int gamesSubTab = -1) { _tab = 18; if (gamesSubTab >= 0) { _gamesSubTab = Mathf.Clamp(gamesSubTab, 0, 4); } else { _gamesSubTab = Mathf.Clamp(_lastGamesSubTab, 0, 4); } _lastGamesSubTab = _gamesSubTab; } private void RequestChessActionKeepTab(int action, int gameId = 0, string matchName = "", int colorPreference = 0, int timeControlSeconds = 0, int stake = 0, bool isPrivate = false, bool allowSpectators = true, int from = -1, int to = -1, char promotion = 'Q', FehuStakeType stakeType = FehuStakeType.None, string stakePrefab = "") { RestoreChessGamesTab(); if (gameId > 0) { _selectedChessGameId = gameId; } WarfareRPC.Instance?.RequestChessAction(action, gameId, matchName, colorPreference, timeControlSeconds, stake, isPrivate, allowSpectators, from, to, promotion, stakeType, stakePrefab); RestoreChessGamesTab(); } private void RequestDiceActionKeepTab(int action, int gameId = 0, string tableName = "", DiceGameMode diceMode = DiceGameMode.HighestRoll, int playerLimit = 2, int diceCount = 1, int roundCount = 1, int coinStake = 0, bool isPrivate = false, bool allowSpectators = true, FehuStakeType stakeType = FehuStakeType.None, string stakePrefab = "") { RestoreGamesUiState(2); if (gameId > 0) { _selectedDiceTableId = gameId; } WarfareRPC.Instance?.RequestGameAction(action, gameId, tableName, diceMode, playerLimit, diceCount, roundCount, coinStake, isPrivate, allowSpectators, stakeType, stakePrefab); RestoreGamesUiState(2); } private void RequestPokerActionKeepTab(int action, int gameId = 0, string tableName = "", int playerLimit = 2, long buyIn = 100L, long smallBlind = 1L, long bigBlind = 2L, bool isPrivate = false, bool allowSpectators = true, long raiseTo = 0L) { RestoreGamesUiState(3); if (gameId > 0) { _selectedPokerGameId = gameId; } WarfareRPC.Instance?.RequestPokerAction(action, gameId, tableName, playerLimit, buyIn, smallBlind, bigBlind, isPrivate, allowSpectators, raiseTo); RestoreGamesUiState(3); } private Texture2D GetChessPieceTexture(char piece) { if (piece == '.') { return null; } if (_chessPieceTextures == null) { _chessPieceTextures = LoadRuntimePngTextures(Path.Combine("Games", "Chess"), recursive: false); if (_chessPieceTextures.Count == 0) { _chessPieceTextures = LoadRuntimePngTextures(Path.Combine("FehuKingdoms", "Games", "Chess"), recursive: false); } } string obj = (ChessRuleEngine.IsWhite(piece) ? "white" : "black"); char c = char.ToUpperInvariant(piece); Dictionary chessPieceTextures = _chessPieceTextures; string text = obj; if (!chessPieceTextures.TryGetValue(text + "-" + c switch { 'B' => "bishop", 'K' => "king", 'N' => "knight", 'P' => "pawn", 'Q' => "queen", 'R' => "rook", _ => "", }, out var value)) { return null; } return value; } private void DrawChessPieceIcon(char piece, Rect square) { //IL_0012: Unknown result type (might be due to invalid IL or missing references) //IL_0054: Unknown result type (might be due to invalid IL or missing references) //IL_005f: Expected O, but got Unknown //IL_0071: 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_007d: Unknown result type (might be due to invalid IL or missing references) //IL_0086: Expected O, but got Unknown //IL_0086: Unknown result type (might be due to invalid IL or missing references) //IL_0061: Unknown result type (might be due to invalid IL or missing references) if (piece != '.') { Texture2D chessPieceTexture = GetChessPieceTexture(piece); Rect val = default(Rect); ((Rect)(ref val))..ctor(((Rect)(ref square)).x + 5f, ((Rect)(ref square)).y + 5f, ((Rect)(ref square)).width - 10f, ((Rect)(ref square)).height - 10f); if ((Object)chessPieceTexture != (Object)null) { GUI.DrawTexture(val, (Texture)(object)chessPieceTexture, (ScaleMode)2, true); return; } GUIStyle val2 = new GUIStyle(_labelGoldStyle) { alignment = (TextAnchor)4, fontSize = 24 }; GUI.Label(square, ChessPieceText(piece), val2); } } private bool DrawPromotionButton(char promotion) { //IL_0010: Unknown result type (might be due to invalid IL or missing references) //IL_0016: Expected O, but got Unknown //IL_001f: Unknown result type (might be due to invalid IL or missing references) //IL_002a: Expected O, but got Unknown //IL_0034: Unknown result type (might be due to invalid IL or missing references) //IL_003a: Expected O, but got Unknown ChessRuleEngine.IsPlayerWhite(null, 0L); GUIContent val = new GUIContent(promotion.ToString()); Texture2D chessPieceTexture = GetChessPieceTexture(promotion); if ((Object)chessPieceTexture != (Object)null) { val = new GUIContent((Texture)(object)chessPieceTexture, promotion.ToString()); } return GUILayout.Button(val, (_chessPromotion == promotion) ? _tabActiveStyle : _tabStyle, (GUILayoutOption[])(object)new GUILayoutOption[2] { GUILayout.Width(48f), GUILayout.Height(36f) }); } private static string ChessPieceText(char piece) { if (piece != '.') { if (!ChessRuleEngine.IsWhite(piece)) { return char.ToLowerInvariant(piece).ToString(); } return char.ToUpperInvariant(piece).ToString(); } return ""; } private static string FormatChessClock(long seconds) { return Math.Max(0L, seconds / 60).ToString("00") + ":" + Math.Max(0L, seconds % 60).ToString("00"); } private static string GetCapturedChessPieces(ChessGameState state, bool whitePieces) { if (state == null || state.Board == null) { return "-"; } char[] array = new char[5] { 'P', 'N', 'B', 'R', 'Q' }; int[] array2 = new int[5] { 8, 2, 2, 2, 1 }; List list = new List(); for (int i = 0; i < array.Length; i++) { char c = (whitePieces ? array[i] : char.ToLowerInvariant(array[i])); int num = 0; for (int j = 0; j < state.Board.Length; j++) { if (state.Board[j] == c) { num++; } } int num2 = Math.Max(0, array2[i] - num); if (num2 > 0) { list.Add(array[i] + "x" + num2); } } if (list.Count != 0) { return string.Join(" ", list.ToArray()); } return "none"; } private void DrawPokerGames(WarfareData data, PlayerClanInfo info) { RestoreGamesUiState(3); List list = (data.Games ?? new List()).FindAll((FehuGameTable game) => game.GameType == FehuGameType.Poker && IsActiveGameTable(game)); if (_selectedPokerGameId == 0 && list.Count > 0) { _selectedPokerGameId = list[0].GameId; } FehuGameTable fehuGameTable = list.Find((FehuGameTable game) => game.GameId == _selectedPokerGameId); GUILayout.BeginHorizontal(Array.Empty()); GUILayout.BeginVertical(_boxStyle, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(330f) }); if (fehuGameTable != null) { DrawCurrentGameSidePanel("Current Poker Table", fehuGameTable, data, info, delegate { _selectedPokerGameId = 0; }); DrawPokerInfo(fehuGameTable, data, info); } else { GUILayout.Label("Create Texas Hold'em Table", _labelGoldStyle, Array.Empty()); GUILayout.Label("Table Name", _labelStyle, Array.Empty()); _pokerTableName = GUILayout.TextField(_pokerTableName, 32, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(260f) }); if ((_pokerPlayerLimit & 1) != 0) { _pokerPlayerLimit++; } GUILayout.BeginHorizontal(Array.Empty()); GUILayout.Label("Seats", _labelStyle, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(58f) }); if (GUILayout.Button("-", _smallButtonStyle, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(36f) })) { _pokerPlayerLimit = Mathf.Max(2, _pokerPlayerLimit - 2); } GUILayout.Label(_pokerPlayerLimit.ToString(), _labelGoldStyle, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(36f) }); if (GUILayout.Button("+", _smallButtonStyle, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(36f) })) { _pokerPlayerLimit = Mathf.Min(10, _pokerPlayerLimit + 2); } GUILayout.EndHorizontal(); DrawPokerNumberInput("Buy-in", ref _pokerBuyInInput); DrawPokerNumberInput("Small Blind", ref _pokerSmallBlindInput); DrawPokerNumberInput("Big Blind", ref _pokerBigBlindInput); if (DrawToggleButton("Private Table", _pokerPrivate, 260f)) { _pokerPrivate = !_pokerPrivate; } if (DrawToggleButton("Allow Spectators", _pokerSpectators, 260f)) { _pokerSpectators = !_pokerSpectators; } if (GUILayout.Button("Create Poker Table", _buttonStyle, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(260f) })) { long.TryParse(_pokerBuyInInput, out var result); long.TryParse(_pokerSmallBlindInput, out var result2); long.TryParse(_pokerBigBlindInput, out var result3); RequestPokerActionKeepTab(1, 0, _pokerTableName, _pokerPlayerLimit, result, result2, result3, _pokerPrivate, _pokerSpectators, 0L); } GUILayout.Space(8f); GUILayout.Label("Poker Tables", _labelGoldStyle, Array.Empty()); foreach (FehuGameTable item in list) { if (GUILayout.Button("#" + item.GameId + " " + item.TableName + "\n" + item.PlayerIds.Count + "/" + item.PlayerLimit + " | Buy-in " + item.PokerBuyIn + " | " + item.Status, (item.GameId == _selectedPokerGameId) ? _tabActiveStyle : _tabStyle, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(260f) })) { _selectedPokerGameId = item.GameId; } } } GUILayout.EndVertical(); GUILayout.Space(10f); GUILayout.BeginVertical(_boxStyle, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(Mathf.Min(1040f, Mathf.Max(860f, (float)Screen.width - 430f))) }); if (fehuGameTable == null) { GUILayout.Label("Create or select a Texas Hold'em table.", _labelStyle, Array.Empty()); } else { DrawPokerTable(fehuGameTable, data, info); } GUILayout.EndVertical(); GUILayout.EndHorizontal(); } private void DrawPokerNumberInput(string label, ref string value) { GUILayout.BeginHorizontal(Array.Empty()); GUILayout.Label(label, _labelStyle, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(90f) }); value = GUILayout.TextField(value, 10, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(110f) }); GUILayout.EndHorizontal(); } private void DrawPokerTable(FehuGameTable table, WarfareData data, PlayerClanInfo info) { PokerGameState poker = table.Poker; GUILayout.Label("" + table.TableName + "", _labelGoldStyle, Array.Empty()); if (poker == null) { GUILayout.Label("Players: " + table.PlayerIds.Count + "/" + table.PlayerLimit + " | Buy-in: " + table.PokerBuyIn + " coins", _labelStyle, Array.Empty()); GUILayout.Label("Seats: " + FormatPokerWaitingSeats(table, data), _labelStyle, Array.Empty()); if (table.Status == FehuGameStatus.Waiting && !table.PlayerIds.Contains(info.PlayerId) && !table.IsPrivate && GUILayout.Button("Join Table", _buttonStyle, Array.Empty())) { RequestPokerActionKeepTab(2, table.GameId, "", 2, 100L, 1L, 2L, isPrivate: false, allowSpectators: true, 0L); } if (table.Status == FehuGameStatus.Waiting && table.PlayerIds.Contains(info.PlayerId) && GUILayout.Button("Leave Table", _buttonStyle, Array.Empty())) { RequestPokerActionKeepTab(9, table.GameId, "", 2, 100L, 1L, 2L, isPrivate: false, allowSpectators: true, 0L); } if (table.Status == FehuGameStatus.Waiting && table.HostPlayerId == info.PlayerId && GUILayout.Button("Start Table", _buttonStyle, Array.Empty())) { RequestPokerActionKeepTab(3, table.GameId, "", 2, 100L, 1L, 2L, isPrivate: false, allowSpectators: true, 0L); } if (table.Status == FehuGameStatus.Waiting && table.HostPlayerId == info.PlayerId && GUILayout.Button("Cancel Table", _buttonStyle, Array.Empty())) { RequestPokerActionKeepTab(11, table.GameId, "", 2, 100L, 1L, 2L, isPrivate: false, allowSpectators: true, 0L); } if (table.Status == FehuGameStatus.Waiting && table.HostPlayerId == info.PlayerId && GUILayout.Button("Delete Table", _buttonStyle, Array.Empty())) { RequestPokerActionKeepTab(12, table.GameId, "", 2, 100L, 1L, 2L, isPrivate: false, allowSpectators: true, 0L); } if (table.AllowSpectators && !table.PlayerIds.Contains(info.PlayerId) && !table.SpectatorIds.Contains(info.PlayerId) && GUILayout.Button("Spectate", _buttonStyle, Array.Empty())) { RequestPokerActionKeepTab(4, table.GameId, "", 2, 100L, 1L, 2L, isPrivate: false, allowSpectators: true, 0L); } } else { GUILayout.Label("Hand " + poker.HandNumber + " | " + poker.Phase.ToString() + " | Pot: " + PokerEngine.CurrentPot(poker) + " | Current bet: " + poker.CurrentBet, _labelStyle, Array.Empty()); GUILayout.Space(6f); DrawPokerTableSurface(table, poker, data, info); DrawPokerTableLegend(); if (table.AllowSpectators && !table.PlayerIds.Contains(info.PlayerId) && !table.SpectatorIds.Contains(info.PlayerId) && GUILayout.Button("Spectate", _smallButtonStyle, Array.Empty())) { RequestPokerActionKeepTab(4, table.GameId, "", 2, 100L, 1L, 2L, isPrivate: false, allowSpectators: true, 0L); } } } private void DrawPokerTableSurface(FehuGameTable table, PokerGameState state, WarfareData data, PlayerClanInfo info) { //IL_002b: Unknown result type (might be due to invalid IL or missing references) //IL_0030: Unknown result type (might be due to invalid IL or missing references) //IL_0033: Unknown result type (might be due to invalid IL or missing references) //IL_0090: Unknown result type (might be due to invalid IL or missing references) //IL_009b: Expected O, but got Unknown //IL_00a8: Unknown result type (might be due to invalid IL or missing references) //IL_009d: Unknown result type (might be due to invalid IL or missing references) //IL_00bb: 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_01bc: Unknown result type (might be due to invalid IL or missing references) //IL_01c1: Unknown result type (might be due to invalid IL or missing references) //IL_01c8: Unknown result type (might be due to invalid IL or missing references) //IL_01d2: Expected O, but got Unknown //IL_01f2: Unknown result type (might be due to invalid IL or missing references) //IL_0249: Unknown result type (might be due to invalid IL or missing references) //IL_019e: Unknown result type (might be due to invalid IL or missing references) //IL_0280: Unknown result type (might be due to invalid IL or missing references) //IL_0283: Unknown result type (might be due to invalid IL or missing references) //IL_0288: Unknown result type (might be due to invalid IL or missing references) //IL_0373: Unknown result type (might be due to invalid IL or missing references) //IL_02e9: Unknown result type (might be due to invalid IL or missing references) //IL_02ee: Unknown result type (might be due to invalid IL or missing references) //IL_02f0: Unknown result type (might be due to invalid IL or missing references) //IL_02f7: Unknown result type (might be due to invalid IL or missing references) //IL_02fe: 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_0314: Unknown result type (might be due to invalid IL or missing references) //IL_0352: Unknown result type (might be due to invalid IL or missing references) //IL_0367: Unknown result type (might be due to invalid IL or missing references) //IL_03e6: Unknown result type (might be due to invalid IL or missing references) //IL_044c: Unknown result type (might be due to invalid IL or missing references) //IL_04c9: Unknown result type (might be due to invalid IL or missing references) //IL_0522: Unknown result type (might be due to invalid IL or missing references) Rect rect = GUILayoutUtility.GetRect(900f, 580f, (GUILayoutOption[])(object)new GUILayoutOption[2] { GUILayout.ExpandWidth(true), GUILayout.Height(580f) }); Rect val = default(Rect); ((Rect)(ref val))..ctor(((Rect)(ref rect)).x + 120f, ((Rect)(ref rect)).y + 96f, Mathf.Max(560f, ((Rect)(ref rect)).width - 240f), Mathf.Max(300f, ((Rect)(ref rect)).height - 192f)); Texture2D pokerTableTexture = GetPokerTableTexture(); if ((Object)pokerTableTexture != (Object)null) { GUI.DrawTexture(val, (Texture)(object)pokerTableTexture, (ScaleMode)2, true); } else { GUI.Box(val, "", _boxStyle); } Rect val2 = default(Rect); ((Rect)(ref val2))..ctor(((Rect)(ref val)).x + ((Rect)(ref val)).width * 0.5f - 305f, ((Rect)(ref val)).y + ((Rect)(ref val)).height * 0.5f - 96f, 610f, 205f); GUI.Label(new Rect(((Rect)(ref val2)).x, ((Rect)(ref val2)).y - 34f, ((Rect)(ref val2)).width, 26f), "Community Cards - " + FormatPokerPhase(state.Phase), _labelGoldStyle); for (int i = 0; i < 5; i++) { int card = ((state.CommunityCards != null && i < state.CommunityCards.Count) ? state.CommunityCards[i] : (-1)); DrawPokerCardAt(new Rect(((Rect)(ref val2)).x + (float)i * 112f, ((Rect)(ref val2)).y, 99f, 138f), card, hidden: false); } GUIStyle val3 = new GUIStyle(_labelGoldStyle) { alignment = (TextAnchor)4, fontSize = 22 }; GUI.Label(new Rect(((Rect)(ref val2)).x, ((Rect)(ref val2)).y + 146f, ((Rect)(ref val2)).width, 50f), "Pot\n" + PokerEngine.CurrentPot(state) + " Coins", val3); DrawPokerPhaseStrip(new Rect(((Rect)(ref val2)).x - 22f, ((Rect)(ref val2)).y - 64f, ((Rect)(ref val2)).width + 44f, 24f), state.Phase); int num = FindNextPokerSeatIndex(state, state.DealerSeatIndex + 1); int bigBlindSeat = FindNextPokerSeatIndex(state, num + 1); for (int j = 0; j < 10; j++) { Rect pokerSeatRect = GetPokerSeatRect(rect, j); PokerPlayerState pokerPlayerState = ((j < state.Seats.Count) ? state.Seats[j] : null); bool hidden = pokerPlayerState != null && pokerPlayerState.PlayerId != info.PlayerId && table.Status == FehuGameStatus.Active && state.Phase != PokerPhase.Showdown && state.Phase != PokerPhase.Finished; if (pokerPlayerState == null) { Color color = GUI.color; GUI.color = new Color(color.r, color.g, color.b, 0.45f); GUI.Box(pokerSeatRect, "", _boxStyle); GUI.Label(new Rect(((Rect)(ref pokerSeatRect)).x + 6f, ((Rect)(ref pokerSeatRect)).y + 8f, ((Rect)(ref pokerSeatRect)).width - 12f, 24f), "Empty Seat", _labelStyle); GUI.color = color; continue; } GUI.Box(pokerSeatRect, "", (pokerPlayerState.PlayerId == state.CurrentActorId) ? _tabActiveStyle : _boxStyle); List pokerSeatMarkerKeys = GetPokerSeatMarkerKeys(table, state, pokerPlayerState, j, num, bigBlindSeat); bool num2 = HasPokerMarkerIcon(pokerSeatMarkerKeys); if (num2) { DrawPokerMarkerIcons(new Rect(((Rect)(ref pokerSeatRect)).x + 6f, ((Rect)(ref pokerSeatRect)).y + 4f, ((Rect)(ref pokerSeatRect)).width - 12f, 27f), pokerSeatMarkerKeys, 27f); } string text = (num2 ? "" : BuildPokerSeatMarker(pokerSeatMarkerKeys)); float num3 = (num2 ? (((Rect)(ref pokerSeatRect)).y + 32f) : (((Rect)(ref pokerSeatRect)).y + 4f)); GUI.Label(new Rect(((Rect)(ref pokerSeatRect)).x + 6f, num3, ((Rect)(ref pokerSeatRect)).width - 12f, 38f), text + pokerPlayerState.PlayerName + "\nChips " + pokerPlayerState.Chips + " | Bet " + pokerPlayerState.RoundBet, _labelStyle); DrawPokerCardAt(new Rect(((Rect)(ref pokerSeatRect)).x + 12f, ((Rect)(ref pokerSeatRect)).y + 58f, 66f, 93f), (pokerPlayerState.HoleCards != null && pokerPlayerState.HoleCards.Count > 0) ? pokerPlayerState.HoleCards[0] : (-1), hidden); DrawPokerCardAt(new Rect(((Rect)(ref pokerSeatRect)).x + 86f, ((Rect)(ref pokerSeatRect)).y + 58f, 66f, 93f), (pokerPlayerState.HoleCards != null && pokerPlayerState.HoleCards.Count > 1) ? pokerPlayerState.HoleCards[1] : (-1), hidden); } } private void DrawPokerPhaseStrip(Rect rect, PokerPhase phase) { //IL_006d: Unknown result type (might be due to invalid IL or missing references) string[] array = new string[5] { "Pre-Flop", "Flop", "Turn", "River", "Showdown" }; PokerPhase[] array2 = new PokerPhase[5] { PokerPhase.PreFlop, PokerPhase.Flop, PokerPhase.Turn, PokerPhase.River, PokerPhase.Showdown }; float num = ((Rect)(ref rect)).width / (float)array.Length; for (int i = 0; i < array.Length; i++) { GUI.Label(new Rect(((Rect)(ref rect)).x + (float)i * num, ((Rect)(ref rect)).y, num, ((Rect)(ref rect)).height), (array2[i] == phase) ? ("" + array[i] + "") : array[i], (array2[i] == phase) ? _labelGoldStyle : _labelStyle); } } private Rect GetPokerSeatRect(Rect tableRect, int index) { //IL_00fb: Unknown result type (might be due to invalid IL or missing references) //IL_0100: Unknown result type (might be due to invalid IL or missing references) //IL_0120: Unknown result type (might be due to invalid IL or missing references) //IL_0125: Unknown result type (might be due to invalid IL or missing references) //IL_0131: Unknown result type (might be due to invalid IL or missing references) //IL_0136: Unknown result type (might be due to invalid IL or missing references) //IL_013f: 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_014d: 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_0161: Unknown result type (might be due to invalid IL or missing references) //IL_017e: Unknown result type (might be due to invalid IL or missing references) //IL_0183: 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_0191: Unknown result type (might be due to invalid IL or missing references) //IL_019a: Unknown result type (might be due to invalid IL or missing references) //IL_019f: Unknown result type (might be due to invalid IL or missing references) //IL_01af: Unknown result type (might be due to invalid IL or missing references) //IL_01a8: Unknown result type (might be due to invalid IL or missing references) //IL_01ad: Unknown result type (might be due to invalid IL or missing references) float num = 196f; float num2 = 156f; float num3 = ((Rect)(ref tableRect)).x + 10f; float num4 = ((Rect)(ref tableRect)).xMax - num - 10f; float num5 = ((Rect)(ref tableRect)).x + ((Rect)(ref tableRect)).width * 0.5f - num * 0.5f; float num6 = ((Rect)(ref tableRect)).y + 8f; float num7 = ((Rect)(ref tableRect)).yMax - num2 - 8f; float num8 = ((Rect)(ref tableRect)).y + ((Rect)(ref tableRect)).height * 0.25f - num2 * 0.5f; float num9 = ((Rect)(ref tableRect)).y + ((Rect)(ref tableRect)).height * 0.5f - num2 * 0.5f; float num10 = ((Rect)(ref tableRect)).y + ((Rect)(ref tableRect)).height * 0.75f - num2 * 0.5f; return (Rect)(index switch { 0 => new Rect(num5, num6, num, num2), 1 => new Rect(((Rect)(ref tableRect)).xMax - num * 1.55f, num6 + 18f, num, num2), 2 => new Rect(num4, num8, num, num2), 3 => new Rect(num4, num9, num, num2), 4 => new Rect(num4, num10, num, num2), 5 => new Rect(num5, num7, num, num2), 6 => new Rect(((Rect)(ref tableRect)).x + num * 0.55f, num7 - 18f, num, num2), 7 => new Rect(num3, num10, num, num2), 8 => new Rect(num3, num9, num, num2), _ => new Rect(num3, num8, num, num2), }); } private List GetPokerSeatMarkerKeys(FehuGameTable table, PokerGameState state, PokerPlayerState seat, int seatIndex, int smallBlindSeat, int bigBlindSeat) { List list = new List(); if (seat.PlayerId == state.CurrentActorId) { list.Add("current_turn"); } if (seatIndex == state.DealerSeatIndex) { list.Add("dealer_button"); } if (seatIndex == smallBlindSeat) { list.Add("small_blind"); } if (seatIndex == bigBlindSeat) { list.Add("big_blind"); } if (seat.Folded) { list.Add("folded"); } if (seat.AllIn) { list.Add("all_in"); } if (seat.PlayerId == table.WinnerPlayerId) { list.Add("winner"); } return list; } private string BuildPokerSeatMarker(List markers) { if (markers == null || markers.Count == 0) { return ""; } return string.Join(" ", markers.Select(PokerMarkerFallback).ToArray()) + " "; } private static string PokerMarkerFallback(string key) { return key switch { "current_turn" => "[>]", "dealer_button" => "[D]", "small_blind" => "[SB]", "big_blind" => "[BB]", "folded" => "[F]", "all_in" => "[AI]", "winner" => "[W]", _ => "", }; } private bool HasPokerMarkerIcon(List markers) { return markers?.Exists((string marker) => (Object)GetPokerMarkerTexture(marker) != (Object)null) ?? false; } private void DrawPokerMarkerIcons(Rect rect, List markers, float size) { //IL_0026: Unknown result type (might be due to invalid IL or missing references) //IL_0031: Expected O, but got Unknown //IL_005c: Unknown result type (might be due to invalid IL or missing references) //IL_003d: Unknown result type (might be due to invalid IL or missing references) if (markers == null) { return; } float num = ((Rect)(ref rect)).x; foreach (string marker in markers) { Texture2D pokerMarkerTexture = GetPokerMarkerTexture(marker); if ((Object)pokerMarkerTexture != (Object)null) { GUI.DrawTexture(new Rect(num, ((Rect)(ref rect)).y, size, size), (Texture)(object)pokerMarkerTexture, (ScaleMode)2, true); } else { GUI.Label(new Rect(num, ((Rect)(ref rect)).y, size + 20f, size), PokerMarkerFallback(marker), _labelGoldStyle); } num += size + 4f; } } private Texture2D GetPokerMarkerTexture(string key) { string text = key switch { "dealer_button" => "dealer", "small_blind" => "smallblind", "big_blind" => "bigblind", "all_in" => "allin", "current_turn" => "turn", _ => key, }; if (_pokerUiTextures == null) { _pokerUiTextures = LoadRuntimePngTextures(Path.Combine("Games", "Poker", "ui"), recursive: false); if (_pokerUiTextures.Count == 0) { _pokerUiTextures = LoadRuntimePngTextures(Path.Combine("FehuKingdoms", "Games", "Poker", "ui"), recursive: false); } } if (_pokerUiTextures.TryGetValue(text, out var value) || _pokerUiTextures.TryGetValue(NormalizeAssetKey(text), out value)) { return value; } return GetPokerSpecialCardTexture(key); } private void DrawPokerTableLegend() { GUILayout.BeginHorizontal(_boxStyle, Array.Empty()); DrawPokerLegendItem("dealer_button", "[D] Dealer"); DrawPokerLegendItem("small_blind", "[SB] Small Blind"); DrawPokerLegendItem("big_blind", "[BB] Big Blind"); DrawPokerLegendItem("all_in", "[AI] All-In"); DrawPokerLegendItem("folded", "[F] Folded"); DrawPokerLegendItem("winner", "[W] Winner"); DrawPokerLegendItem("current_turn", "[>] Turn"); GUILayout.EndHorizontal(); } private void DrawPokerLegendItem(string iconKey, string fallback) { //IL_0009: Unknown result type (might be due to invalid IL or missing references) //IL_0014: Expected O, but got Unknown //IL_0047: Unknown result type (might be due to invalid IL or missing references) //IL_0052: Expected O, but got Unknown //IL_0076: Unknown result type (might be due to invalid IL or missing references) //IL_0081: Expected O, but got Unknown Texture2D pokerMarkerTexture = GetPokerMarkerTexture(iconKey); if ((Object)pokerMarkerTexture != (Object)null) { GUILayout.Box((Texture)(object)pokerMarkerTexture, GUIStyle.none, (GUILayoutOption[])(object)new GUILayoutOption[2] { GUILayout.Width(33f), GUILayout.Height(33f) }); } GUILayout.Label(((Object)pokerMarkerTexture != (Object)null) ? fallback.Substring(fallback.IndexOf(' ') + 1) : fallback, _labelStyle, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(((Object)pokerMarkerTexture != (Object)null) ? 94f : 128f) }); } private static string FormatPokerPhase(PokerPhase phase) { if (phase != PokerPhase.PreFlop) { return phase.ToString(); } return "Pre-Flop"; } private int FindNextPokerSeatIndex(PokerGameState state, int start) { if (state == null || state.Seats == null || state.Seats.Count == 0) { return -1; } for (int i = 0; i < state.Seats.Count; i++) { int num = (start + i) % state.Seats.Count; if (state.Seats[num].Chips > 0 || state.Seats[num].TotalContribution > 0) { return num; } } return -1; } private void DrawPokerCardAt(Rect rect, int card, bool hidden) { //IL_002a: Unknown result type (might be due to invalid IL or missing references) //IL_0035: Expected O, but got Unknown //IL_0041: Unknown result type (might be due to invalid IL or missing references) //IL_0037: Unknown result type (might be due to invalid IL or missing references) Texture2D val = (hidden ? GetPokerSpecialCardTexture("card_back") : ((card >= 0) ? GetPokerCardTexture(card) : GetPokerSpecialCardTexture("card_empty"))); if ((Object)val != (Object)null) { GUI.DrawTexture(rect, (Texture)(object)val, (ScaleMode)2, true); } else { GUI.Box(rect, hidden ? "" : ((card >= 0) ? PokerEngine.CardName(card) : ""), _boxStyle); } } private Texture2D GetPokerTableTexture() { //IL_0006: Unknown result type (might be due to invalid IL or missing references) //IL_0011: Expected O, but got Unknown //IL_0031: Unknown result type (might be due to invalid IL or missing references) //IL_003c: Expected O, but got Unknown if ((Object)_pokerTableTexture != (Object)null) { return _pokerTableTexture; } _pokerTableTexture = GetPokerSpecialCardTexture("table1"); if ((Object)_pokerTableTexture == (Object)null) { LoadRuntimePngTextures(Path.Combine("FehuKingdoms", "Games"), recursive: false).TryGetValue("table1", out _pokerTableTexture); } return _pokerTableTexture; } private void DrawPokerCardRow(List cards, int slots, bool hidden, float width, float height) { //IL_0073: Unknown result type (might be due to invalid IL or missing references) //IL_007e: Expected O, but got Unknown GUILayout.BeginHorizontal(Array.Empty()); for (int i = 0; i < slots; i++) { Texture2D val = (hidden ? GetPokerSpecialCardTexture("card_back") : ((cards != null && i < cards.Count) ? GetPokerCardTexture(cards[i]) : GetPokerSpecialCardTexture("card_empty"))); string text = (hidden ? "" : ((cards != null && i < cards.Count) ? PokerEngine.CardName(cards[i]) : "")); if ((Object)val != (Object)null) { GUILayout.Box((Texture)(object)val, GUIStyle.none, (GUILayoutOption[])(object)new GUILayoutOption[2] { GUILayout.Width(width), GUILayout.Height(height) }); } else { GUILayout.Box(text, _boxStyle, (GUILayoutOption[])(object)new GUILayoutOption[2] { GUILayout.Width(width), GUILayout.Height(height) }); } } GUILayout.EndHorizontal(); } private Texture2D GetPokerCardTexture(int card) { if (card < 0 || card >= 52) { return GetPokerSpecialCardTexture("card_empty"); } string[] array = new string[4] { "clubs", "diamonds", "hearts", "spades" }; string[] array2 = new string[13] { "02", "03", "04", "05", "06", "07", "08", "09", "10", "J", "Q", "K", "A" }; return GetPokerSpecialCardTexture("card_" + array[card / 13] + "_" + array2[card % 13]); } private Texture2D GetPokerSpecialCardTexture(string key) { if (_pokerCardTextures == null) { _pokerCardTextures = LoadRuntimePngTextures(Path.Combine("Games", "Poker"), recursive: false); if (_pokerCardTextures.Count == 0) { _pokerCardTextures = LoadRuntimePngTextures(Path.Combine("FehuKingdoms", "Games", "Poker"), recursive: false); } } if (!_pokerCardTextures.TryGetValue(key, out var value) && !_pokerCardTextures.TryGetValue(NormalizeAssetKey(key), out value)) { return null; } return value; } private static string FormatPokerWaitingSeats(FehuGameTable table, WarfareData data) { List list = new List(); for (int i = 0; i < table.PlayerLimit; i++) { if (i < table.PlayerIds.Count) { long playerId = table.PlayerIds[i]; list.Add(data.GetPlayer(playerId)?.PlayerName ?? playerId.ToString()); } else { list.Add("Empty"); } } return string.Join(" | ", list.ToArray()); } private void DrawPokerInfo(FehuGameTable table, WarfareData data, PlayerClanInfo info) { GUILayout.Label("Poker Actions", _labelGoldStyle, Array.Empty()); if (table == null) { GUILayout.Label("No table selected.", _labelStyle, Array.Empty()); return; } PokerGameState poker = table.Poker; GUILayout.Label("Buy-in: " + table.PokerBuyIn + " | Blinds: " + table.PokerSmallBlind + "/" + table.PokerBigBlind + "\nSpectators: " + table.SpectatorIds.Count, _labelStyle, Array.Empty()); if (poker == null) { return; } PokerPlayerState player = PokerEngine.GetPlayer(poker, info.PlayerId); if (player != null && PokerEngine.IsHandActive(poker) && poker.CurrentActorId == info.PlayerId) { long num = Math.Max(0L, poker.CurrentBet - player.RoundBet); string obj = ((num == 0L) ? "Check" : ((num >= player.Chips) ? ("Call All-In " + player.Chips) : ("Call " + num))); GUILayout.Label("Your turn. To call: " + num + " | Your chips: " + player.Chips, _labelGoldStyle, Array.Empty()); GUILayout.BeginHorizontal(Array.Empty()); if (GUILayout.Button("Fold", _smallButtonStyle, Array.Empty())) { RequestPokerActionKeepTab(5, table.GameId, "", 2, 100L, 1L, 2L, isPrivate: false, allowSpectators: true, 0L); } if (GUILayout.Button(obj, _smallButtonStyle, Array.Empty())) { RequestPokerActionKeepTab(6, table.GameId, "", 2, 100L, 1L, 2L, isPrivate: false, allowSpectators: true, 0L); } if (GUILayout.Button("All-In", _smallButtonStyle, Array.Empty())) { RequestPokerActionKeepTab(8, table.GameId, "", 2, 100L, 1L, 2L, isPrivate: false, allowSpectators: true, 0L); } GUILayout.EndHorizontal(); GUILayout.BeginHorizontal(Array.Empty()); GUILayout.Label((poker.CurrentBet == 0L) ? "Bet" : "Raise to", _labelStyle, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(64f) }); _pokerRaiseInput = GUILayout.TextField(_pokerRaiseInput, 10, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(90f) }); long.TryParse(_pokerRaiseInput, out var result); GUI.enabled = result > poker.CurrentBet && result <= player.RoundBet + player.Chips; if (GUILayout.Button((poker.CurrentBet == 0L) ? "Bet" : "Raise", _smallButtonStyle, Array.Empty())) { RequestPokerActionKeepTab(7, table.GameId, "", 2, 100L, 1L, 2L, isPrivate: false, allowSpectators: true, result); } GUI.enabled = true; GUILayout.EndHorizontal(); } if (!PokerEngine.IsHandActive(poker)) { if (table.HostPlayerId == info.PlayerId && GUILayout.Button("Deal Next Hand", _buttonStyle, Array.Empty())) { RequestPokerActionKeepTab(10, table.GameId, "", 2, 100L, 1L, 2L, isPrivate: false, allowSpectators: true, 0L); } if (player != null && GUILayout.Button("Cash Out / Leave " + player.Chips + " Coins", _buttonStyle, Array.Empty())) { RequestPokerActionKeepTab(9, table.GameId, "", 2, 100L, 1L, 2L, isPrivate: false, allowSpectators: true, 0L); } if (table.HostPlayerId == info.PlayerId && GUILayout.Button("Cancel / Refund Table", _buttonStyle, Array.Empty())) { RequestPokerActionKeepTab(11, table.GameId, "", 2, 100L, 1L, 2L, isPrivate: false, allowSpectators: true, 0L); } } GUILayout.Space(8f); GUILayout.Label("Hand Result", _labelGoldStyle, Array.Empty()); GUILayout.BeginVertical(_tabActiveStyle, Array.Empty()); GUILayout.Label("Winner: " + ((table.WinnerPlayerId != 0L) ? FormatPlayerName(data, table.WinnerPlayerId) : "Waiting"), _labelStyle, Array.Empty()); GUILayout.Label("Hand: " + ExtractPokerWinningHand(poker.LastResult), _labelStyle, Array.Empty()); GUILayout.Label("Pot: " + ExtractPokerPot(poker.LastResult, PokerEngine.CurrentPot(poker)), _labelStyle, Array.Empty()); GUILayout.Label("Payout: " + ExtractPokerPayout(poker), _labelStyle, Array.Empty()); GUILayout.EndVertical(); GUILayout.Space(8f); GUILayout.Label("Current Hand Log", _labelGoldStyle, Array.Empty()); List handLog = poker.HandLog ?? new List(); DrawPokerHandLogPager(handLog); } private void DrawPokerHandLogPager(List handLog) { if (handLog == null || handLog.Count == 0) { GUILayout.Label("Waiting.", _labelStyle, Array.Empty()); return; } int num = Mathf.Max(1, Mathf.CeilToInt((float)handLog.Count / 9f)); if (_pokerHandLogSeenCount != handLog.Count) { _pokerHandLogSeenCount = handLog.Count; _pokerHandLogPage = num - 1; } _pokerHandLogPage = Mathf.Clamp(_pokerHandLogPage, 0, num - 1); GUILayout.BeginHorizontal(Array.Empty()); if (GUILayout.Button("<", _smallButtonStyle, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(42f) })) { _pokerHandLogPage = Mathf.Max(0, _pokerHandLogPage - 1); } GUILayout.Label("Page " + (_pokerHandLogPage + 1) + " / " + num, _labelGoldStyle, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(110f) }); if (GUILayout.Button(">", _smallButtonStyle, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(42f) })) { _pokerHandLogPage = Mathf.Min(num - 1, _pokerHandLogPage + 1); } GUILayout.EndHorizontal(); int num2 = _pokerHandLogPage * 9; int num3 = Math.Min(handLog.Count, num2 + 9); for (int i = num2; i < num3; i++) { string line = ExpandPokerCardNames(handLog[i]); GUILayout.Label(HighlightPokerLogLine(line), IsImportantPokerLogLine(line) ? _labelGoldStyle : _labelStyle, Array.Empty()); } } private static string ExtractPokerWinningHand(string result) { if (string.IsNullOrWhiteSpace(result)) { return "Waiting"; } string text = result; int num = text.IndexOf(". Pot:", StringComparison.OrdinalIgnoreCase); if (num >= 0) { text = text.Substring(0, num); } if (text.StartsWith("Showdown ", StringComparison.OrdinalIgnoreCase)) { text = text.Substring("Showdown ".Length); } if (text.StartsWith("Won by folds", StringComparison.OrdinalIgnoreCase)) { return "Won by folds"; } if (!string.IsNullOrWhiteSpace(text)) { return text.Trim(' ', '.'); } return "Waiting"; } private static string ExtractPokerPayout(PokerGameState state) { if (state?.HandLog == null) { return "Waiting"; } for (int num = state.HandLog.Count - 1; num >= 0; num--) { string text = state.HandLog[num] ?? ""; if (text.StartsWith("Payout:", StringComparison.OrdinalIgnoreCase)) { return text.Substring("Payout:".Length).Trim(' ', '.'); } } return "Waiting"; } private static long ExtractPokerPot(string result, long fallback) { if (string.IsNullOrWhiteSpace(result)) { return fallback; } Match match = Regex.Match(result, "Pot:\\s*(\\d+)", RegexOptions.IgnoreCase); if (!match.Success || !long.TryParse(match.Groups[1].Value, out var result2)) { return fallback; } return result2; } private static bool IsImportantPokerLogLine(string line) { if (string.IsNullOrWhiteSpace(line)) { return false; } if (line.IndexOf("dealer", StringComparison.OrdinalIgnoreCase) < 0 && line.IndexOf("blind", StringComparison.OrdinalIgnoreCase) < 0 && line.IndexOf("check", StringComparison.OrdinalIgnoreCase) < 0 && line.IndexOf("call", StringComparison.OrdinalIgnoreCase) < 0 && line.IndexOf("raise", StringComparison.OrdinalIgnoreCase) < 0 && line.IndexOf("fold", StringComparison.OrdinalIgnoreCase) < 0 && line.IndexOf("all-in", StringComparison.OrdinalIgnoreCase) < 0 && line.IndexOf("flop", StringComparison.OrdinalIgnoreCase) < 0 && line.IndexOf("turn", StringComparison.OrdinalIgnoreCase) < 0 && line.IndexOf("river", StringComparison.OrdinalIgnoreCase) < 0 && line.IndexOf("showdown", StringComparison.OrdinalIgnoreCase) < 0 && line.IndexOf("winner", StringComparison.OrdinalIgnoreCase) < 0 && line.IndexOf("main pot", StringComparison.OrdinalIgnoreCase) < 0 && line.IndexOf("side pot", StringComparison.OrdinalIgnoreCase) < 0) { return line.IndexOf("payout", StringComparison.OrdinalIgnoreCase) >= 0; } return true; } private static string HighlightPokerLogLine(string line) { if (!IsImportantPokerLogLine(line)) { return line; } return "" + line + ""; } private static string ExpandPokerCardNames(string line) { if (string.IsNullOrWhiteSpace(line)) { return line; } return Regex.Replace(line, "\\b(10|[2-9TJQKA])([CDHS])\\b", (Match match) => FullPokerCardTokenName(match.Groups[1].Value, match.Groups[2].Value)); } private static string FullPokerCardTokenName(string rank, string suit) { if (rank == null) { goto IL_00b8; } int length = rank.Length; string text; if (length != 1) { if (length != 2 || !(rank == "10")) { goto IL_00b8; } text = "Ten"; } else { switch (rank[0]) { case 'T': break; case '2': goto IL_00b0; default: goto IL_00b8; case 'J': goto IL_0127; case 'Q': goto IL_012f; case 'K': goto IL_0137; case 'A': goto IL_0142; case '9': goto IL_014d; case '8': goto IL_0158; case '7': goto IL_0163; case '6': goto IL_016e; case '5': goto IL_0179; case '4': goto IL_0184; case '3': goto IL_018f; } text = "Ten"; } goto IL_00ba; IL_018f: text = "Three"; goto IL_00ba; IL_00b8: text = rank; goto IL_00ba; IL_00ba: string text2 = text; return text2 + " of " + suit switch { "C" => "Clubs", "D" => "Diamonds", "H" => "Hearts", "S" => "Spades", _ => suit, }; IL_00b0: text = "Two"; goto IL_00ba; IL_0127: text = "Jack"; goto IL_00ba; IL_012f: text = "Queen"; goto IL_00ba; IL_0137: text = "King"; goto IL_00ba; IL_0142: text = "Ace"; goto IL_00ba; IL_014d: text = "Nine"; goto IL_00ba; IL_0158: text = "Eight"; goto IL_00ba; IL_0163: text = "Seven"; goto IL_00ba; IL_016e: text = "Six"; goto IL_00ba; IL_0179: text = "Five"; goto IL_00ba; IL_0184: text = "Four"; goto IL_00ba; } private void DrawDiceGames(WarfareData data, PlayerClanInfo info) { RestoreGamesUiState(2); List diceStakeItems = GetDiceStakeItems(); if (_diceStakeItemIndex >= diceStakeItems.Count) { _diceStakeItemIndex = 0; } List list = (data.Games ?? new List()).FindAll((FehuGameTable game) => game.GameType == FehuGameType.Dice && IsDiceTableVisibleInList(game) && IsDiceTableVisibleToPlayer(game, info)); if (_selectedDiceTableId == 0 && list.Count > 0) { _selectedDiceTableId = list[0].GameId; } FehuGameTable fehuGameTable = list.Find((FehuGameTable game) => game.GameId == _selectedDiceTableId); if (fehuGameTable == null && list.Count > 0) { _selectedDiceTableId = list[0].GameId; fehuGameTable = list[0]; } GUILayout.BeginHorizontal(Array.Empty()); GUILayout.BeginVertical(_boxStyle, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(370f) }); if (fehuGameTable != null) { DrawCurrentGameSidePanel("Current Dice Table", fehuGameTable, data, info, delegate { _selectedDiceTableId = 0; }); } else { GUILayout.Label("Create Dice Table", _labelGoldStyle, Array.Empty()); GUILayout.Label("Table Name", _labelStyle, Array.Empty()); _diceTableName = GUILayout.TextField(_diceTableName, 32, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(320f) }); GUILayout.Label("Mode", _labelStyle, Array.Empty()); GUILayout.BeginHorizontal(Array.Empty()); string[] array = new string[3] { "Highest Roll", "Best of 3", "Lucky Seven" }; for (int num = 0; num < array.Length; num++) { if (GUILayout.Button(array[num], (_diceModeIndex == num) ? _tabActiveStyle : _tabStyle, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(104f) })) { _diceModeIndex = num; } } GUILayout.EndHorizontal(); GUILayout.BeginHorizontal(Array.Empty()); GUILayout.BeginVertical(_boxStyle, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(150f) }); GUILayout.Label("PLAYERS", _labelGoldStyle, Array.Empty()); GUILayout.BeginHorizontal(Array.Empty()); if (GUILayout.Button("-", _smallButtonStyle, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(30f) })) { _dicePlayerLimit = Mathf.Max(2, _dicePlayerLimit - 1); } GUILayout.Label(_dicePlayerLimit + " players", _labelStyle, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(76f) }); if (GUILayout.Button("+", _smallButtonStyle, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(30f) })) { _dicePlayerLimit = Mathf.Min(8, _dicePlayerLimit + 1); } GUILayout.EndHorizontal(); GUILayout.EndVertical(); GUILayout.BeginVertical(_boxStyle, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(150f) }); GUILayout.Label("DICE PER ROLL", _labelGoldStyle, Array.Empty()); GUILayout.BeginHorizontal(Array.Empty()); if (GUILayout.Button("-", _smallButtonStyle, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(30f) })) { _diceCount = Mathf.Max(1, _diceCount - 1); } GUILayout.Label(_diceCount + " dice", _labelStyle, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(76f) }); if (GUILayout.Button("+", _smallButtonStyle, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(30f) })) { _diceCount = Mathf.Min(6, _diceCount + 1); } GUILayout.EndHorizontal(); GUILayout.EndVertical(); GUILayout.EndHorizontal(); GUILayout.Label("Stake", _labelStyle, Array.Empty()); GUILayout.BeginHorizontal(Array.Empty()); string[] array2 = new string[3] { "None", "Coins", "Item" }; for (int num2 = 0; num2 < array2.Length; num2++) { if (GUILayout.Button(array2[num2], (_diceStakeTypeIndex == num2) ? _tabActiveStyle : _tabStyle, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(104f) })) { _diceStakeTypeIndex = num2; } } GUILayout.EndHorizontal(); if (_diceStakeTypeIndex > 0) { GUILayout.BeginHorizontal(Array.Empty()); GUILayout.Label((_diceStakeTypeIndex == 1) ? "Coin amount" : "Item quantity", _labelStyle, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(100f) }); _diceStakeInput = GUILayout.TextField(_diceStakeInput, 10, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(100f) }); GUILayout.EndHorizontal(); } if (_diceStakeTypeIndex == 2) { GUILayout.BeginHorizontal(Array.Empty()); if (GUILayout.Button("<", _smallButtonStyle, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(30f) })) { _diceStakeItemIndex = ((diceStakeItems.Count != 0) ? ((_diceStakeItemIndex + diceStakeItems.Count - 1) % diceStakeItems.Count) : 0); } object obj; if (diceStakeItems.Count != 0) { string displayName = diceStakeItems[_diceStakeItemIndex].DisplayName; int amount = diceStakeItems[_diceStakeItemIndex].Amount; obj = displayName + " x" + amount; } else { obj = "No stake item available"; } GUILayout.Label((string)obj, _labelStyle, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(220f) }); if (GUILayout.Button(">", _smallButtonStyle, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(30f) })) { _diceStakeItemIndex = ((diceStakeItems.Count != 0) ? ((_diceStakeItemIndex + 1) % diceStakeItems.Count) : 0); } GUILayout.EndHorizontal(); } if (DrawToggleButton("Private Table", _dicePrivate, 320f)) { _dicePrivate = !_dicePrivate; } if (DrawToggleButton("Allow Spectators", _diceSpectators, 320f)) { _diceSpectators = !_diceSpectators; } int result = 0; int.TryParse(_diceStakeInput, out result); bool flag = _diceStakeTypeIndex != 2 || (result > 0 && diceStakeItems.Count > 0); if (_diceStakeTypeIndex == 2 && !flag) { GUILayout.Label("Item stake needs an item and quantity greater than zero.", _labelStyle, Array.Empty()); } GUI.enabled = flag; if (GUILayout.Button("Create Dice Table", _buttonStyle, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(320f) })) { int.TryParse(_diceStakeInput, out var result2); FehuStakeType diceStakeTypeIndex = (FehuStakeType)_diceStakeTypeIndex; string stakePrefab = ((diceStakeTypeIndex == FehuStakeType.Item && diceStakeItems.Count > 0) ? diceStakeItems[_diceStakeItemIndex].PrefabName : ""); RequestDiceActionKeepTab(1, 0, _diceTableName, (DiceGameMode)_diceModeIndex, _dicePlayerLimit, _diceCount, (_diceModeIndex != 1) ? 1 : 3, Mathf.Max(0, result2), _dicePrivate, _diceSpectators, diceStakeTypeIndex, stakePrefab); } GUI.enabled = true; GUILayout.Label("Dice Tables", _labelGoldStyle, Array.Empty()); if (list.Count == 0) { GUILayout.Label("No Dice tables available.", _labelStyle, Array.Empty()); } foreach (FehuGameTable item in list) { if (GUILayout.Button("#" + item.GameId + " " + item.TableName + "\n" + FormatDiceMode(item.DiceMode) + " | " + item.PlayerIds.Count + "/" + item.PlayerLimit + " | " + item.Status, (item.GameId == _selectedDiceTableId) ? _tabActiveStyle : _tabStyle, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(320f) })) { _selectedDiceTableId = item.GameId; } } } GUILayout.EndVertical(); GUILayout.BeginVertical(_boxStyle, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(520f) }); DrawDiceTablePanel(fehuGameTable, data, info); GUILayout.EndVertical(); GUILayout.BeginVertical(_boxStyle, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(360f) }); DrawDiceSidePanel(fehuGameTable, data, info); GUILayout.EndVertical(); GUILayout.EndHorizontal(); } private void DrawDiceTablePanel(FehuGameTable table, WarfareData data, PlayerClanInfo info) { if (table == null) { GUILayout.Label("Create or select a Dice table.", _labelStyle, Array.Empty()); return; } GUILayout.Label("" + table.TableName + "", _labelGoldStyle, Array.Empty()); GUILayout.Label(FormatDiceMode(table.DiceMode) + " | " + table.Status.ToString() + " | Stake: " + FormatGameStake(table.StakeType, table.StakeAmount, table.StakePrefabName), _labelStyle, Array.Empty()); GUILayout.Label("Pot: " + ((table.EscrowTotal > 0) ? (table.EscrowTotal + " " + ((table.StakeType == FehuStakeType.Item) ? table.StakePrefabName : "coins")) : "None"), _labelStyle, Array.Empty()); if (table.Status == FehuGameStatus.Waiting) { GUILayout.Label("Waiting for players. Host starts when ready.", _labelStyle, Array.Empty()); GUILayout.BeginHorizontal(Array.Empty()); if (!table.PlayerIds.Contains(info.PlayerId) && !table.IsPrivate && GUILayout.Button("Join Table", _buttonStyle, Array.Empty())) { RequestDiceActionKeepTab(2, table.GameId); } if (table.PlayerIds.Contains(info.PlayerId) && GUILayout.Button("Leave Table", _buttonStyle, Array.Empty())) { RequestDiceActionKeepTab(8, table.GameId); } GUILayout.EndHorizontal(); if (table.HostPlayerId == info.PlayerId && GUILayout.Button("Start Game", _buttonStyle, Array.Empty())) { RequestDiceActionKeepTab(3, table.GameId); } if (table.HostPlayerId == info.PlayerId && GUILayout.Button("Cancel Table", _buttonStyle, Array.Empty())) { RequestDiceActionKeepTab(4, table.GameId); } if (table.HostPlayerId == info.PlayerId && GUILayout.Button("Delete Table", _buttonStyle, Array.Empty())) { RequestDiceActionKeepTab(6, table.GameId); } if (table.AllowSpectators && !table.PlayerIds.Contains(info.PlayerId) && !table.SpectatorIds.Contains(info.PlayerId) && GUILayout.Button("Spectate", _buttonStyle, Array.Empty())) { RequestDiceActionKeepTab(5, table.GameId); } return; } DiceGameState dice = table.Dice; if (dice == null) { GUILayout.Label(string.IsNullOrEmpty(table.Result) ? "Dice state is waiting for server sync." : table.Result, _labelStyle, Array.Empty()); return; } long diceCurrentPlayer = GetDiceCurrentPlayer(table); GUILayout.Label("Round: " + dice.CurrentRound + " | Current turn: " + FormatPlayerName(data, diceCurrentPlayer), _labelGoldStyle, Array.Empty()); GUILayout.Space(8f); GUILayout.Label("Dice Result", _labelGoldStyle, Array.Empty()); GUILayout.Label(string.IsNullOrEmpty(dice.LastResult) ? "No rolls yet." : dice.LastResult, _labelStyle, Array.Empty()); GUILayout.Space(8f); if (table.Status == FehuGameStatus.Active && diceCurrentPlayer == info.PlayerId && GUILayout.Button("ROLL", _buttonStyle, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Height(44f) })) { RequestDiceActionKeepTab(7, table.GameId); } else if (table.Status == FehuGameStatus.Active) { GUILayout.Label("Waiting for " + FormatPlayerName(data, diceCurrentPlayer) + " to roll.", _labelStyle, Array.Empty()); } if (table.Status == FehuGameStatus.Active && table.PlayerIds.Contains(info.PlayerId) && GUILayout.Button("Leave Table", _buttonStyle, Array.Empty())) { RequestDiceActionKeepTab(8, table.GameId); } if (table.Status == FehuGameStatus.Finished) { GUILayout.Label("Winner", _labelGoldStyle, Array.Empty()); GUILayout.Label(FormatPlayerName(data, table.WinnerPlayerId), _labelStyle, Array.Empty()); GUILayout.Label(table.Result ?? "", _labelStyle, Array.Empty()); if (table.HostPlayerId == info.PlayerId && GUILayout.Button("Close Table", _buttonStyle, Array.Empty())) { RequestDiceActionKeepTab(6, table.GameId); } } } private void DrawDiceSidePanel(FehuGameTable table, WarfareData data, PlayerClanInfo info) { GUILayout.Label("Dice Table Info", _labelGoldStyle, Array.Empty()); if (table == null) { GUILayout.Label("No table selected.", _labelStyle, Array.Empty()); return; } GUILayout.Label("Host: " + table.HostPlayerName + "\nPlayers: " + table.PlayerIds.Count + "/" + table.PlayerLimit + "\nSpectators: " + table.SpectatorIds.Count + "\nSpectator access: " + (table.AllowSpectators ? "ON" : "OFF"), _labelStyle, Array.Empty()); GUILayout.Label("Players", _labelGoldStyle, Array.Empty()); foreach (long playerId in table.PlayerIds) { int value; int num = ((table.Dice != null && table.Dice.Scores.TryGetValue(playerId, out value)) ? value : 0); GUILayout.Label(FormatPlayerName(data, playerId) + ((GetDiceCurrentPlayer(table) == playerId) ? " <- turn" : "") + " | Score: " + num, _labelStyle, Array.Empty()); } if (table.SpectatorIds.Count > 0) { GUILayout.Label("Spectators", _labelGoldStyle, Array.Empty()); foreach (long spectatorId in table.SpectatorIds) { GUILayout.Label(FormatPlayerName(data, spectatorId), _labelStyle, Array.Empty()); } } GUILayout.Label("Round History", _labelGoldStyle, Array.Empty()); List list = table.Dice?.Rolls ?? new List(); if (list.Count == 0) { GUILayout.Label("No rolls yet.", _labelStyle, Array.Empty()); } for (int i = Math.Max(0, list.Count - 12); i < list.Count; i++) { DiceRollRecord diceRollRecord = list[i]; GUILayout.Label("R" + diceRollRecord.Round + " " + FormatPlayerName(data, diceRollRecord.PlayerId) + ": " + string.Join("+", diceRollRecord.Rolls) + " = " + diceRollRecord.Total, _labelStyle, Array.Empty()); } } private static long GetDiceCurrentPlayer(FehuGameTable table) { if (table?.Dice == null) { return 0L; } List list = ((table.Dice.ActivePlayerIds != null && table.Dice.ActivePlayerIds.Count > 0) ? table.Dice.ActivePlayerIds : table.PlayerIds); if (list == null || list.Count == 0) { return 0L; } int index = Mathf.Clamp(table.Dice.CurrentPlayerIndex, 0, list.Count - 1); return list[index]; } private static string FormatDiceMode(DiceGameMode mode) { return mode switch { DiceGameMode.BestOf3 => "Best of 3", DiceGameMode.LuckySeven => "Lucky Seven", _ => "Highest Roll", }; } private static string FormatPlayerName(WarfareData data, long playerId) { if (playerId == 0L) { return "None"; } return data?.GetPlayer(playerId)?.PlayerName ?? playerId.ToString(); } private static bool IsDiceTableVisibleToPlayer(FehuGameTable table, PlayerClanInfo info) { if (table == null || info == null) { return false; } if (!table.IsPrivate) { return true; } if (table.HostPlayerId != info.PlayerId && !table.PlayerIds.Contains(info.PlayerId)) { return table.SpectatorIds.Contains(info.PlayerId); } return true; } private static bool IsDiceTableVisibleInList(FehuGameTable table) { if (table == null || table.Status == FehuGameStatus.Cancelled) { return false; } if (table.Status == FehuGameStatus.Waiting || table.Status == FehuGameStatus.Active) { return true; } if (table.Status != FehuGameStatus.Finished) { return false; } long num = ((table.FinishedAt > 0) ? table.FinishedAt : table.StartedAt); if (num > 0) { return WarfareManager.Now() - num <= 300; } return true; } private List GetDiceStakeItems() { //IL_00cb: Unknown result type (might be due to invalid IL or missing references) //IL_00d2: Expected O, but got Unknown List list = new List(); Player localPlayer = Player.m_localPlayer; Inventory val = (((Object)(object)localPlayer != (Object)null) ? ((Humanoid)localPlayer).GetInventory() : null); if (!(((object)val)?.GetType().GetMethod("GetAllItems", Type.EmptyTypes)?.Invoke(val, null) is IEnumerable enumerable)) { return list; } foreach (object item in enumerable) { if (item != null) { Type type = item.GetType(); int num = (int)(type.GetField("m_stack")?.GetValue(item) ?? ((object)0)); object obj = type.GetField("m_dropPrefab")?.GetValue(item); GameObject val2 = (GameObject)((obj is GameObject) ? obj : null); string text = (((Object)(object)val2 != (Object)null) ? ((Object)val2).name : ""); object obj2 = type.GetField("m_shared")?.GetValue(item); string text2 = obj2?.GetType().GetField("m_name")?.GetValue(obj2) as string; if (num > 0 && !string.IsNullOrEmpty(text)) { GetInventorySlot(item, out var x, out var y); list.Add(new VaultInventoryEntry(text, WarfareRPC.CleanItemDisplayName(string.IsNullOrEmpty(text2) ? text : text2), num, x, y, GetItemDataIcon(item))); } } } return list; } private List GetAvailableGameStakeTerritories(WarfareData data, PlayerClanInfo info) { if (data == null || info == null || info.ClanId == 0 || info.Rank != ClanRank.Jarl) { return new List(); } List list = data.GetClanTerritories(info.ClanId).FindAll((ClanTerritory t) => t != null && !t.IsCapital && t.FreebornOwnerId == 0); if (list.Count <= 1) { return new List(); } return (from t in list.FindAll((ClanTerritory t) => t.TerritoryStakeGameId == 0) orderby t.DisplayName select t).ToList(); } private static string FormatGameStake(FehuStakeType type, long amount, string prefab) { if (type == FehuStakeType.None || amount <= 0) { return "None"; } return amount + " " + ((type == FehuStakeType.Coins) ? "Coins" : WarfareRPC.CleanItemDisplayName(prefab)); } private void DrawGamesLeaderboards(WarfareData data) { List collection = new List((data.GameStats ?? new Dictionary()).Values); string[] array = new string[3] { "Chess", "Poker", "Dice" }; GUILayout.BeginHorizontal(Array.Empty()); for (int i = 0; i < array.Length; i++) { if (GUILayout.Button(array[i], (_gameRankingType == i) ? _tabActiveStyle : _tabStyle, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(120f) })) { _gameRankingType = i; } } GUILayout.Space(16f); string[] array2 = new string[2] { "Top 10", "Top 100" }; for (int j = 0; j < array2.Length; j++) { if (GUILayout.Button(array2[j], (_gameRankingLimitIndex == j) ? _tabActiveStyle : _tabStyle, (GUILayoutOption[])(object)new GUILayoutOption[2] { GUILayout.Width(120f), GUILayout.Height(34f) })) { _gameRankingLimitIndex = j; } } GUILayout.FlexibleSpace(); GUILayout.EndHorizontal(); FehuGameType type = ((_gameRankingType != 0) ? ((_gameRankingType != 1) ? FehuGameType.Dice : FehuGameType.Poker) : FehuGameType.Chess); Comparison gameRankingComparison = GetGameRankingComparison(type); Func gameRankingSummary = GetGameRankingSummary(type); DrawGameLeaderboardColumn(data, type, new List(collection), gameRankingComparison, gameRankingSummary, (_gameRankingLimitIndex == 0) ? 10 : 100); } private static float ChessWinRate(FehuGamePlayerStats stats) { if (stats.ChessGamesPlayed > 0) { return (float)stats.ChessWins * 100f / (float)stats.ChessGamesPlayed; } return 0f; } private static float GlobalWinRate(FehuGamePlayerStats stats) { if (stats.GamesPlayed > 0) { return (float)stats.Wins * 100f / (float)stats.GamesPlayed; } return 0f; } private static float PokerWinRate(FehuGamePlayerStats stats) { if (stats.PokerGamesPlayed > 0) { return (float)stats.PokerHandsWon * 100f / (float)stats.PokerGamesPlayed; } return 0f; } private static int DiceGamesPlayed(FehuGamePlayerStats stats) { if (stats != null) { return Math.Max(0, stats.GamesPlayed - stats.ChessGamesPlayed - stats.PokerGamesPlayed); } return 0; } private static int DiceWins(FehuGamePlayerStats stats) { if (stats != null) { return Math.Max(0, stats.Wins - stats.ChessWins - stats.PokerHandsWon); } return 0; } private static int DiceLosses(FehuGamePlayerStats stats) { if (stats != null) { return Math.Max(0, stats.Losses - stats.ChessLosses - Math.Max(0, stats.PokerGamesPlayed - stats.PokerHandsWon)); } return 0; } private static float DiceWinRate(FehuGamePlayerStats stats) { if (DiceGamesPlayed(stats) > 0) { return (float)DiceWins(stats) * 100f / (float)DiceGamesPlayed(stats); } return 0f; } private static Comparison GetGameRankingComparison(FehuGameType type) { return type switch { FehuGameType.Chess => (FehuGamePlayerStats a, FehuGamePlayerStats b) => (b.ChessRating != a.ChessRating) ? b.ChessRating.CompareTo(a.ChessRating) : b.ChessWins.CompareTo(a.ChessWins), FehuGameType.Poker => (FehuGamePlayerStats a, FehuGamePlayerStats b) => (b.PokerProfit != a.PokerProfit) ? b.PokerProfit.CompareTo(a.PokerProfit) : b.PokerBiggestPot.CompareTo(a.PokerBiggestPot), _ => (FehuGamePlayerStats a, FehuGamePlayerStats b) => (b.Profit != a.Profit) ? b.Profit.CompareTo(a.Profit) : b.Wins.CompareTo(a.Wins), }; } private static Func GetGameRankingSummary(FehuGameType type) { return type switch { FehuGameType.Chess => (FehuGamePlayerStats s) => "Rating " + s.ChessRating + " | Wins " + s.ChessWins + " | Win Rate " + ChessWinRate(s).ToString("0.0") + "% | Streak " + s.ChessBestStreak, FehuGameType.Poker => (FehuGamePlayerStats s) => "Profit " + s.PokerProfit + " | Hands Won " + s.PokerHandsWon + " | Biggest Pot " + s.PokerBiggestPot + " | Win Rate " + PokerWinRate(s).ToString("0.0") + "%", _ => (FehuGamePlayerStats s) => "Profit " + s.Profit + " | Wins " + DiceWins(s) + " | Highest Roll " + s.HighestDiceRoll + " | Streak " + s.DiceBestStreak, }; } private void DrawGameLeaderboardColumn(WarfareData data, FehuGameType type, List stats, Comparison comparison, Func summary, int limit) { GUILayout.BeginVertical(_boxStyle, Array.Empty()); DrawGameIconHeader(type, type.ToString() + " Rankings"); stats.Sort(comparison); long localId = LocalPlayerId(); int num = stats.FindIndex((FehuGamePlayerStats stat) => stat.PlayerId == localId) + 1; GUILayout.Label("Your Position: " + ((num > 0) ? num.ToString() : "Unranked") + "", _labelGoldStyle, Array.Empty()); if (stats.Count == 0) { GUILayout.Label("No ranked results yet. Play games to appear on the leaderboard.", _labelStyle, Array.Empty()); } for (int num2 = 0; num2 < stats.Count && num2 < limit; num2++) { bool flag = stats[num2].PlayerId == localId; object obj = num2 switch { 2 => "Bronze ", 1 => "Silver ", 0 => "Gold ", _ => "", }; GUILayout.BeginHorizontal(flag ? _tabActiveStyle : _boxStyle, Array.Empty()); GUILayout.Label((string)obj + (num2 + 1) + ".", flag ? _labelGoldStyle : _labelStyle, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(55f) }); GUILayout.Label(data.GetPlayer(stats[num2].PlayerId)?.PlayerName ?? stats[num2].PlayerId.ToString(), flag ? _labelGoldStyle : _labelStyle, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(220f) }); GUILayout.Label(summary(stats[num2]), flag ? _labelGoldStyle : _labelStyle, Array.Empty()); GUILayout.EndHorizontal(); } GUILayout.EndVertical(); } private static FehuGameType GameTypeFromTitle(string title) { if (!string.IsNullOrEmpty(title) && title.StartsWith("Chess", StringComparison.OrdinalIgnoreCase)) { return FehuGameType.Chess; } if (!string.IsNullOrEmpty(title) && title.StartsWith("Dice", StringComparison.OrdinalIgnoreCase)) { return FehuGameType.Dice; } if (!string.IsNullOrEmpty(title) && title.StartsWith("Poker", StringComparison.OrdinalIgnoreCase)) { return FehuGameType.Poker; } return FehuGameType.Chess; } private void DrawGamesHistory(WarfareData data) { //IL_038e: Unknown result type (might be due to invalid IL or missing references) //IL_0399: Expected O, but got Unknown //IL_03dc: Unknown result type (might be due to invalid IL or missing references) //IL_03e1: Unknown result type (might be due to invalid IL or missing references) //IL_03f6: Expected O, but got Unknown GUILayout.BeginVertical(_boxStyle, Array.Empty()); GUILayout.Label("Game History", _labelGoldStyle, Array.Empty()); string[] array = new string[4] { "All", "Dice", "Chess", "Poker" }; GUILayout.BeginHorizontal(Array.Empty()); for (int i = 0; i < array.Length; i++) { if (GUILayout.Button(array[i], (_gameHistoryFilter == i) ? _tabActiveStyle : _tabStyle, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(110f) })) { _gameHistoryFilter = i; _gameHistoryPage = 0; } } GUILayout.EndHorizontal(); FehuGameType selectedType = ((_gameHistoryFilter == 1) ? FehuGameType.Dice : ((_gameHistoryFilter != 2) ? FehuGameType.Poker : FehuGameType.Chess)); List list = (data.GameHistory ?? new List()).FindAll((FehuGameHistoryEntry entry) => _gameHistoryFilter == 0 || entry.GameType == selectedType); if (list.Count == 0) { GUILayout.Label("No game history entries yet.", _labelStyle, Array.Empty()); } int num = Math.Max(1, (list.Count + 5 - 1) / 5); _gameHistoryPage = Mathf.Clamp(_gameHistoryPage, 0, num - 1); GUILayout.BeginHorizontal(Array.Empty()); GUI.enabled = _gameHistoryPage > 0; if (GUILayout.Button("Previous", _smallButtonStyle, (GUILayoutOption[])(object)new GUILayoutOption[2] { GUILayout.Width(120f), GUILayout.Height(34f) })) { _gameHistoryPage--; } GUI.enabled = true; GUILayout.Label("Page " + (_gameHistoryPage + 1) + " / " + num, _labelGoldStyle, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(140f) }); GUI.enabled = _gameHistoryPage < num - 1; if (GUILayout.Button("Next", _smallButtonStyle, (GUILayoutOption[])(object)new GUILayoutOption[2] { GUILayout.Width(120f), GUILayout.Height(34f) })) { _gameHistoryPage++; } GUI.enabled = true; GUILayout.FlexibleSpace(); GUILayout.EndHorizontal(); GUILayout.BeginVertical(_boxStyle, Array.Empty()); int num2 = list.Count - 1 - _gameHistoryPage * 5; for (int num3 = 0; num3 < 5; num3++) { int num4 = num2 - num3; if (num4 < 0 || num4 >= list.Count) { break; } FehuGameHistoryEntry fehuGameHistoryEntry = list[num4]; string text = string.Join(", ", fehuGameHistoryEntry.PlayerIds.ConvertAll((long id) => data.GetPlayer(id)?.PlayerName ?? id.ToString()).ToArray()); string text2 = data.GetPlayer(fehuGameHistoryEntry.WinnerPlayerId)?.PlayerName ?? ((fehuGameHistoryEntry.WinnerPlayerId == 0L) ? "Draw / refund" : fehuGameHistoryEntry.WinnerPlayerId.ToString()); string text3 = ((fehuGameHistoryEntry.CompletedAt > 0) ? DateTimeOffset.FromUnixTimeSeconds(fehuGameHistoryEntry.CompletedAt).ToLocalTime().ToString("yyyy-MM-dd HH:mm") : "-"); GUILayout.BeginHorizontal(_boxStyle, Array.Empty()); Texture2D gameIconTexture = GetGameIconTexture(fehuGameHistoryEntry.GameType); if ((Object)gameIconTexture != (Object)null) { GUILayout.Box((Texture)(object)gameIconTexture, GUIStyle.none, (GUILayoutOption[])(object)new GUILayoutOption[2] { GUILayout.Width(58f), GUILayout.Height(58f) }); } GUILayout.BeginVertical(Array.Empty()); GUIStyle val = new GUIStyle(_labelGoldStyle) { fontSize = _labelGoldStyle.fontSize + 1 }; GUILayout.Label("" + fehuGameHistoryEntry.GameType.ToString() + " - " + fehuGameHistoryEntry.TableName + "", val, Array.Empty()); GUILayout.Label("Players: " + text + " | Winner: " + text2 + " | Stake: " + FormatGameStake(fehuGameHistoryEntry.StakeType, fehuGameHistoryEntry.StakeAmount, fehuGameHistoryEntry.StakePrefabName) + " | Reward: " + fehuGameHistoryEntry.StakeAmount * fehuGameHistoryEntry.PlayerIds.Count + "", _labelStyle, Array.Empty()); GUILayout.Label("" + FormatHistoryResultLine(fehuGameHistoryEntry) + " | Date: " + text3, _labelStyle, Array.Empty()); GUILayout.EndVertical(); GUILayout.EndHorizontal(); } GUILayout.EndVertical(); GUILayout.EndVertical(); } private static string FormatHistoryResultLine(FehuGameHistoryEntry entry) { string text = entry.Result ?? ""; if (entry.GameType == FehuGameType.Poker) { return "Poker: " + text.Replace(" | ", " | "); } if (entry.GameType == FehuGameType.Chess) { return "Chess: " + text.Replace(" | ", " | "); } if (entry.GameType == FehuGameType.Dice) { return "Dice: " + text.Replace(" | ", " | "); } return text; } private void DrawMyGameStats(WarfareData data, PlayerClanInfo info) { (data.GameStats ?? new Dictionary()).TryGetValue(info.PlayerId, out var value); GUILayout.BeginVertical(_boxStyle, Array.Empty()); GUILayout.Label("My Game Stats", _labelGoldStyle, Array.Empty()); if (value == null) { GUILayout.Label("No games played yet.", _labelStyle, Array.Empty()); } else { float num = ((value.GamesPlayed > 0) ? ((float)value.Wins * 100f / (float)value.GamesPlayed) : 0f); GUILayout.BeginVertical(_boxStyle, Array.Empty()); GUILayout.Label("Global Summary", _labelGoldStyle, Array.Empty()); GUILayout.BeginHorizontal(Array.Empty()); DrawStatPill("Games Played", value.GamesPlayed.ToString(), 180f); DrawStatPill("Wins", value.Wins.ToString(), 160f); DrawStatPill("Losses", value.Losses.ToString(), 160f); DrawStatPill("Win Rate", num.ToString("0.0") + "%", 160f); DrawStatPill("Profit", value.Profit.ToString(), 160f); DrawStatPill("Favorite", value.FavoriteGame.ToString(), 180f); GUILayout.FlexibleSpace(); GUILayout.EndHorizontal(); GUILayout.EndVertical(); GUILayout.BeginHorizontal(Array.Empty()); DrawGameOverviewCard(FehuGameType.Chess, "Chess", "Games Played: " + value.ChessGamesPlayed + "\nWins: " + value.ChessWins + "\nLosses: " + value.ChessLosses + "\nDraws: " + value.ChessDraws + "\nWin Rate: " + ChessWinRate(value).ToString("0.0") + "%\nCurrent / Best Streak: " + value.ChessCurrentStreak + " / " + value.ChessBestStreak + "\nRating: " + value.ChessRating); DrawGameOverviewCard(FehuGameType.Dice, "Dice", "Games Played: " + DiceGamesPlayed(value) + "\nWins: " + DiceWins(value) + "\nLosses: " + DiceLosses(value) + "\nWin Rate: " + DiceWinRate(value).ToString("0.0") + "%\nAverage Bet: tracked from new tables\nLargest Win: " + value.BiggestWin + "\nCurrent / Best Streak: " + value.DiceCurrentStreak + " / " + value.DiceBestStreak + "\nProfit: " + value.Profit); float num2 = ((value.PokerGamesPlayed > 0) ? ((float)value.PokerHandsWon * 100f / (float)value.PokerGamesPlayed) : 0f); DrawGameOverviewCard(FehuGameType.Poker, "Poker", "Hands Played: " + value.PokerGamesPlayed + "\nHands Won: " + value.PokerHandsWon + "\nLosses: " + Math.Max(0, value.PokerGamesPlayed - value.PokerHandsWon) + "\nWin Rate: " + num2.ToString("0.0") + "%\nCoins Won/Lost: net " + value.PokerProfit + "\nLargest Pot Won: " + value.PokerBiggestPot + "\nBiggest Win: " + value.PokerBiggestWin); GUILayout.EndHorizontal(); } GUILayout.EndVertical(); } private void DrawGamesInvites(WarfareData data, PlayerClanInfo info) { GUILayout.BeginVertical(_boxStyle, Array.Empty()); GUILayout.Label("Game Invites", _labelGoldStyle, Array.Empty()); int num = 0; foreach (FehuGameInvite item in data.GameInvites ?? new List()) { if (item.ToPlayerId == info.PlayerId) { num++; GUILayout.Label(item.GameType.ToString() + " invitation from " + item.FromPlayerId + " expires " + item.ExpiresAt, _labelStyle, Array.Empty()); } } if (num == 0) { GUILayout.Label("No pending game invites.", _labelStyle, Array.Empty()); } GUILayout.EndVertical(); } private void DrawSupportHub() { } private void DrawOverviewSupportSections() { GUILayout.BeginVertical(_boxStyle, Array.Empty()); GUILayout.Label("Support / Roadmap", _labelGoldStyle, Array.Empty()); GUILayout.BeginHorizontal(Array.Empty()); DrawCompactOverviewInfo("Support Forteca", "Dedicated servers, domains, backups, website hosting and community infrastructure."); DrawCompactOverviewInfo("Support Fehu", "Mod development, art assets, icons, UI, models, testing servers and future systems."); DrawCompactOverviewInfo("Recognition", "Contributor, Supporter, Subscriber and Constellation recognition tiers."); DrawCompactOverviewInfo("Roadmap", "Upcoming systems move here from foundation to playable content."); GUILayout.EndHorizontal(); if (GUILayout.Button("Open forteca.tip4serv.com", _smallButtonStyle, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(220f) })) { Application.OpenURL("https://forteca.tip4serv.com/"); } GUILayout.EndVertical(); } private void DrawCompactOverviewInfo(string title, string body) { GUILayout.BeginVertical(_boxStyle, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(300f) }); GUILayout.Label("" + title + "", _labelGoldStyle, Array.Empty()); GUILayout.Label(body, _labelStyle, Array.Empty()); GUILayout.EndVertical(); } private void DrawRealmStatusHeader(WarfareData data, PlayerClanInfo info, Clan clan) { //IL_000b: Unknown result type (might be due to invalid IL or missing references) //IL_0016: Expected O, but got Unknown long num = WarfareManager.Now(); string text = (((Object)ZNet.instance != (Object)null) ? (ZNet.instance.GetWorldName() ?? "Unknown") : "Unknown"); string text2 = RealmStatusServerName(text); string text3 = RealmStatusWorldDay(); string text4 = (((ClanConfig.SeasonLengthDays?.Value ?? 60) <= 0) ? "ETERNAL" : ("Cycle " + data.SeasonNumber)); List clanTerritories = data.GetClanTerritories(info.ClanId); int num2 = clanTerritories.Count((ClanTerritory t) => t != null && t.State == TerritoryState.Attacked); int num3 = clanTerritories.Count((ClanTerritory t) => t != null && t.RotCount > 0); int num4 = clanTerritories.Count((ClanTerritory t) => t?.OdinsPeaceActive ?? false); int count = data.GetActiveWarsInvolving(info.ClanId).Count; long num5 = long.MaxValue; int num6 = Mathf.Max(60, (ClanConfig.UpkeepTickMinutes?.Value ?? 30) * 60); long num7 = long.MaxValue; int num8 = Mathf.Max(1, ClanConfig.BuildingProductionHours?.Value ?? 12) * 3600; int num9 = 0; foreach (ClanTerritory item in clanTerritories) { if (item.LastIncomeTimestamp > 0) { num5 = Math.Min(num5, Math.Max(0L, item.LastIncomeTimestamp + num6 - num)); } foreach (TerritoryBuilding item2 in item.Buildings ?? new List()) { if (item2 == null || item2.Damaged || item2.Type == TerritoryBuildingType.None) { continue; } WarfareManager.GetBuildingOutput(item, item2, out var prefab, out var _, out var amount, out var coins); if ((amount > 0 && !string.IsNullOrEmpty(prefab)) || coins > 0) { if (item2.StoredAmount > 0) { num9++; } if (item2.LastProductionTimestamp > 0 && (coins > 0 || item2.StoredProductionTicks < 4)) { num7 = Math.Min(num7, Math.Max(0L, item2.LastProductionTimestamp + num8 - num)); } } } } string text5 = ((num5 == long.MaxValue) ? "Starting" : FormatDuration(num5)); string text6 = ((num7 == long.MaxValue) ? "No active production" : FormatDuration(num7)); string text7 = "Scheduling"; string text8 = "-"; if (clan != null && clan.NextElectionTimestamp > 0) { bool flag = IsElectionActive(clan, num); long num10 = (long)Math.Max(1, ClanConfig.JarlElectionDurationHours?.Value ?? 72) * 3600L; text7 = (flag ? "Voting" : "Intermission"); text8 = FormatDuration(Math.Max(0L, (flag ? clan.NextElectionTimestamp : (clan.NextElectionTimestamp - num10)) - num)); } if (GUILayout.Button("FEHU REALM STATUS " + (_realmStatusExpanded ? "▲" : "▼"), _realmStatusExpanded ? _tabActiveStyle : _smallButtonStyle, (GUILayoutOption[])(object)new GUILayoutOption[2] { GUILayout.ExpandWidth(true), GUILayout.Height(32f) })) { _realmStatusExpanded = !_realmStatusExpanded; } if (!_realmStatusExpanded) { GUILayout.Space(4f); return; } float num11 = ResponsiveCardWidth(AvailableContentWidth(reserveScrollbar: true) - 8f, 3); GUILayout.BeginVertical(_specialPanelStyle, Array.Empty()); GUILayout.BeginHorizontal(_boxStyle, (GUILayoutOption[])(object)new GUILayoutOption[2] { GUILayout.ExpandWidth(true), GUILayout.Height(42f) }); GUILayout.Label("FEHU · THE 10TH REALM", _labelGoldStyle, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(num11) }); GUILayout.FlexibleSpace(); GUILayout.Label("One Save To Rule Them All · Forteca endures since 1999.", _secondaryTextStyle, Array.Empty()); GUILayout.EndHorizontal(); GUILayout.BeginHorizontal(Array.Empty()); DrawRealmStatusCard("WORLD", UiTone.Neutral, num11, "Realm Name", text2, "World Name", text, "World Day", text3, "Cycle", text4); DrawRealmStatusCard("ECONOMY", UiTone.Info, num11, "Treasury", (clan != null) ? (TreasuryColored(clan.Treasury) + " coins") : "-", "Next Royal Income", text5); DrawRealmStatusCard("PRODUCTION", UiTone.Special, num11, "Next Production Cycle", text6, "Buildings Ready", num9.ToString()); GUILayout.EndHorizontal(); GUILayout.BeginHorizontal(Array.Empty()); DrawRealmStatusCard("JARL ELECTION", UiTone.Special, num11, "Current Phase", text7, "Time Remaining", text8); DrawRealmStatusCard("TERRITORY", UiTone.Positive, num11, "Owned Territories", clanTerritories.Count.ToString(), "Under Attack", num2.ToString(), "Rot Corrupted", num3.ToString(), "Territory Cap", (clan != null) ? Economy.TerritoryCap(clan).ToString() : "-"); DrawRealmStatusCard("WAR STATUS", UiTone.Cost, num11, "Active Wars", count.ToString(), "Odin's Peace", (num4 > 0) ? (num4 + " protected") : "Inactive"); GUILayout.EndHorizontal(); GUILayout.EndVertical(); GUILayout.Space(6f); } private void DrawRealmStatusCard(string title, UiTone tone, float width, params string[] rows) { GUILayout.BeginVertical(PanelStyle(tone), (GUILayoutOption[])(object)new GUILayoutOption[2] { GUILayout.Width(width), GUILayout.Height(116f) }); GUILayout.Label("" + title + "", CardTitleStyle(tone), Array.Empty()); for (int i = 0; i + 1 < rows.Length; i += 2) { DrawMiniStat(rows[i], rows[i + 1]); } GUILayout.EndVertical(); } private static string RealmStatusServerName(string fallback) { //IL_0005: Unknown result type (might be due to invalid IL or missing references) //IL_0010: Expected O, but got Unknown if ((Object)ZNet.instance == (Object)null) { return fallback; } string text = typeof(ZNet).GetMethod("GetServerName", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic)?.Invoke(ZNet.instance, null) as string; if (!string.IsNullOrWhiteSpace(text)) { return text; } return fallback; } private static string RealmStatusWorldDay() { //IL_0005: Unknown result type (might be due to invalid IL or missing references) //IL_0010: Expected O, but got Unknown if ((Object)EnvMan.instance == (Object)null) { return "-"; } object obj = typeof(EnvMan).GetMethod("GetCurrentDay", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic)?.Invoke(EnvMan.instance, null); if (obj != null) { return Convert.ToInt32(obj).ToString(); } return "-"; } private void DrawOverview(WarfareData data, PlayerClanInfo info, Clan clan) { //IL_0068: Unknown result type (might be due to invalid IL or missing references) //IL_006d: Unknown result type (might be due to invalid IL or missing references) //IL_0072: Unknown result type (might be due to invalid IL or missing references) //IL_0075: Unknown result type (might be due to invalid IL or missing references) //IL_0160: Unknown result type (might be due to invalid IL or missing references) //IL_0165: Unknown result type (might be due to invalid IL or missing references) //IL_0191: Unknown result type (might be due to invalid IL or missing references) //IL_01d7: Unknown result type (might be due to invalid IL or missing references) //IL_0221: 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_0256: Unknown result type (might be due to invalid IL or missing references) //IL_0259: Unknown result type (might be due to invalid IL or missing references) //IL_0697: Unknown result type (might be due to invalid IL or missing references) //IL_091c: Unknown result type (might be due to invalid IL or missing references) int clanTerritoryCount = data.GetClanTerritoryCount(info.ClanId); int num = ((clan != null) ? Economy.TerritoryCap(clan) : 0); int clanMemberCount = data.GetClanMemberCount(info.ClanId); EconomyBreakdown economyBreakdown = ((clan != null) ? Economy.CalculateClanEconomy(data, info.ClanId) : new EconomyBreakdown()); long net = economyBreakdown.Net; ClanTerritory capitalTerritory = GetCapitalTerritory(data, info.ClanId); ClanTerritory currentTerritory = GetCurrentTerritory(data); Vector2i grid = GridExtensions.WorldToGrid(((Component)Player.m_localPlayer).transform.position); ClanTerritory territory = data.GetTerritory(grid); List vault = data.GetVault(info.ClanId); int num2 = ((clan != null) ? Economy.VaultCapacity(clan, clanTerritoryCount) : 0); GUILayout.BeginVertical(_boxStyle, Array.Empty()); if (clan != null) { float num3 = ResponsiveCardWidth(AvailableContentWidth(reserveScrollbar: true) - 8f, 3); GUILayout.BeginHorizontal(Array.Empty()); GUILayout.BeginVertical(_boxStyle, (GUILayoutOption[])(object)new GUILayoutOption[3] { GUILayout.Width(num3), GUILayout.Height(465f), GUILayout.ExpandWidth(false) }); float num4 = Mathf.Max(320f, num3 - 28f); float num5 = Mathf.Clamp(Mathf.Min(num4, 392f), 256f, 512f); Rect rect = GUILayoutUtility.GetRect(num4, num5, (GUILayoutOption[])(object)new GUILayoutOption[2] { GUILayout.Width(num4), GUILayout.Height(num5) }); Rect val = default(Rect); ((Rect)(ref val))..ctor(((Rect)(ref rect)).x + (((Rect)(ref rect)).width - num5) * 0.5f, ((Rect)(ref rect)).y, num5, num5); GUI.Box(val, GUIContent.none, _boxStyle); GUI.DrawTexture(new Rect(((Rect)(ref val)).x + 3f, ((Rect)(ref val)).y + 3f, ((Rect)(ref val)).width - 6f, ((Rect)(ref val)).height - 6f), (Texture)(object)LocalEmblemCache.GetTexture(clan.ClanEmblemName), (ScaleMode)2, true); GUILayout.Space(5f); Rect rect2 = GUILayoutUtility.GetRect(num4, 28f, (GUILayoutOption[])(object)new GUILayoutOption[2] { GUILayout.Width(num4), GUILayout.Height(28f) }); Rect rect3 = default(Rect); ((Rect)(ref rect3))..ctor(((Rect)(ref rect2)).x + (((Rect)(ref rect2)).width - num5) * 0.5f, ((Rect)(ref rect2)).y, num5, 28f); DrawClanColorBanner(rect3, clan.Color); GUILayout.Space(4f); GUILayout.Label("Clan: " + clan.Name + " Tag: " + clan.Tag, _labelStyle, Array.Empty()); GUILayout.EndVertical(); GUILayout.BeginVertical(_boxStyle, (GUILayoutOption[])(object)new GUILayoutOption[3] { GUILayout.Width(num3), GUILayout.Height(465f), GUILayout.ExpandWidth(false) }); GUILayout.Label("CLAN MANAGEMENT", _labelGoldStyle, Array.Empty()); GUILayout.Label($"Members: {clanMemberCount}", _labelStyle, Array.Empty()); GUILayout.Label($"Territories: {clanTerritoryCount}/{num}", _labelStyle, Array.Empty()); GUILayout.Label("Economy: " + EconomyBandColored(clanTerritoryCount), _labelStyle, Array.Empty()); GUILayout.Label("Gross: +" + economyBreakdown.Gross + " / tick", _labelStyle, Array.Empty()); GUILayout.Label("Base Upkeep: -" + economyBreakdown.TerritoryUpkeep + " / tick", _labelStyle, Array.Empty()); GUILayout.Label("Progressive Upkeep: -" + economyBreakdown.ProgressiveUpkeepBeforeLawspeaker + " / tick", _labelStyle, Array.Empty()); if (economyBreakdown.LawspeakerReduction > 0) { GUILayout.Label("Lawspeaker Reduction: -" + economyBreakdown.LawspeakerReduction + " / tick", _labelStyle, Array.Empty()); } GUILayout.Label("Guard Upkeep: -" + economyBreakdown.GuardUpkeep + " / tick", _labelStyle, Array.Empty()); GUILayout.Label("Final Upkeep: -" + (economyBreakdown.TerritoryUpkeep + economyBreakdown.ProgressiveUpkeep + economyBreakdown.GuardUpkeep) + " / tick", _labelStyle, Array.Empty()); GUILayout.Label("Net Income: " + ColoredCoins(net) + " / tick", _labelStyle, Array.Empty()); GUILayout.Label("Treasury: " + TreasuryColored(clan.Treasury) + " coins", _labelStyle, Array.Empty()); GUILayout.Label($"Vault Tier: {clan.VaultTier}", _labelStyle, Array.Empty()); GUILayout.Label($"Territory Cap: {num}", _labelStyle, Array.Empty()); int nextTerritoryNumber = clanTerritoryCount + 1; GUILayout.Label("Current Acquisition Tier: " + Economy.TerritoryAcquisitionTierName(nextTerritoryNumber), _labelStyle, Array.Empty()); GUILayout.Label("Next Territory Cost: " + Economy.TerritoryAcquisitionCost(nextTerritoryNumber, info) + " coins", _labelStyle, Array.Empty()); if (info.Rank == ClanRank.Jarl) { GUILayout.BeginHorizontal(Array.Empty()); if (GUILayout.Button(_editingClanEmblem ? "[Emblem]" : "Emblem", _smallButtonStyle, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(112f) })) { _editingClanEmblem = !_editingClanEmblem; _editingClanLinks = false; _editingBannerColors = false; _pendingClanEmblemName = EmblemName(clan); _emblemPage = 0; } ConfigEntry enableClanLinks = ClanConfig.EnableClanLinks; if (enableClanLinks != null && enableClanLinks.Value && GUILayout.Button(_editingClanLinks ? "[Clan Links]" : "Clan Links", _smallButtonStyle, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(112f) })) { _editingClanLinks = !_editingClanLinks; _editingClanEmblem = false; _editingBannerColors = false; } if (GUILayout.Button(_editingBannerColors ? "[Banner Colors]" : "Banner Colors", _smallButtonStyle, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(125f) })) { _editingBannerColors = !_editingBannerColors; _editingClanEmblem = false; _editingClanLinks = false; _selectedBannerColorIndex = FindClanColorIndex(clan.Color); } GUILayout.EndHorizontal(); } GUILayout.EndVertical(); GUILayout.BeginVertical(_boxStyle, (GUILayoutOption[])(object)new GUILayoutOption[3] { GUILayout.Width(num3), GUILayout.Height(465f), GUILayout.ExpandWidth(false) }); GUILayout.Label("CURRENT STATUS", _labelGoldStyle, Array.Empty()); DrawMiniStat("Capital", (capitalTerritory != null) ? (capitalTerritory.DisplayName + $" ({capitalTerritory.GridX},{capitalTerritory.GridY})") : "None"); DrawMiniStat("Current Territory", (currentTerritory != null) ? (currentTerritory.DisplayName + $" ({currentTerritory.GridX},{currentTerritory.GridY})") : "Unclaimed wilderness"); DrawMiniStat("Current Biome", (currentTerritory != null) ? BiomeDisplayName(currentTerritory) : "Unknown"); DrawMiniStat("Active Wars", data.GetActiveWarsInvolving(info.ClanId).Count.ToString()); DrawMiniStat("Vault Usage", vault.Count + "/" + num2 + " stacks"); DrawMiniStat("Corruption", (currentTerritory != null) ? CorruptionColored(currentTerritory.RotCount, 4) : "0/4"); DrawMiniStat("Next Tick", "every " + (ClanConfig.UpkeepTickMinutes?.Value ?? 30) + " minutes"); GUILayout.Space(4f); GUILayout.Label("Activity Protection", _labelGoldStyle, Array.Empty()); DrawInactivityPreview(clan.LastActiveTimestamp); GUILayout.EndVertical(); GUILayout.EndHorizontal(); GUILayout.Space(10f); GUILayout.BeginHorizontal((GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.ExpandHeight(false) }); GUILayout.BeginVertical((GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(ResponsiveCardWidth(AvailableContentWidth(reserveScrollbar: true) - 8f, 2)) }); DrawClanTaxSettings(info, clan); GUILayout.EndVertical(); GUILayout.BeginVertical((GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(ResponsiveCardWidth(AvailableContentWidth(reserveScrollbar: true) - 8f, 2)) }); DrawCurrentZoneClaimPanel(data, info, territory, grid); GUILayout.EndVertical(); GUILayout.EndHorizontal(); ConfigEntry enableClanLinks2 = ClanConfig.EnableClanLinks; if (enableClanLinks2 != null && enableClanLinks2.Value && _editingClanLinks && info.Rank == ClanRank.Jarl) { DrawClanLinksEditor(clan); } if (_editingClanEmblem && info.Rank == ClanRank.Jarl) { DrawClanEmblemBrowser(clan); } if (_editingBannerColors && info.Rank == ClanRank.Jarl) { DrawBannerColorsEditor(clan); } } GUILayout.EndVertical(); } private int FindClanColorIndex(Color color) { //IL_000a: Unknown result type (might be due to invalid IL or missing references) //IL_000f: Unknown result type (might be due to invalid IL or missing references) for (int i = 0; i < ClanConfig.ClanColors.Length; i++) { if (ClanConfig.ColorsClose(ClanConfig.ClanColors[i], color)) { return i; } } return 0; } private bool IsBannerColorTaken(Clan clan, Color color) { //IL_0054: Unknown result type (might be due to invalid IL or missing references) //IL_0059: Unknown result type (might be due to invalid IL or missing references) ConfigEntry requireUniqueClanColors = ClanConfig.RequireUniqueClanColors; if (requireUniqueClanColors == null || !requireUniqueClanColors.Value) { return false; } WarfareData warfareData = WarfareManager.Instance?.Data; if (warfareData == null) { return false; } foreach (Clan value in warfareData.Clans.Values) { if (value.ClanId != clan.ClanId && ClanConfig.ColorsClose(value.Color, color)) { return true; } } return false; } private void DrawBannerColorsEditor(Clan clan) { //IL_0173: Unknown result type (might be due to invalid IL or missing references) //IL_0178: Unknown result type (might be due to invalid IL or missing references) //IL_01bb: Unknown result type (might be due to invalid IL or missing references) //IL_0077: 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) //IL_007e: Unknown result type (might be due to invalid IL or missing references) //IL_0089: 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_00a1: Unknown result type (might be due to invalid IL or missing references) //IL_00a8: Unknown result type (might be due to invalid IL or missing references) //IL_00af: Unknown result type (might be due to invalid IL or missing references) //IL_00bb: Unknown result type (might be due to invalid IL or missing references) //IL_009d: Unknown result type (might be due to invalid IL or missing references) //IL_0132: Unknown result type (might be due to invalid IL or missing references) _selectedBannerColorIndex = Mathf.Clamp(_selectedBannerColorIndex, 0, ClanConfig.ClanColors.Length - 1); GUILayout.BeginVertical(_boxStyle, Array.Empty()); GUILayout.Label("Banner Colors", _labelGoldStyle, Array.Empty()); GUILayout.Label("Choose an available existing clan colour.", _labelStyle, Array.Empty()); for (int i = 0; i < ClanConfig.ClanColors.Length; i += 14) { GUILayout.BeginHorizontal(Array.Empty()); for (int j = 0; j < 14 && i + j < ClanConfig.ClanColors.Length; j++) { int num = i + j; Color val = ClanConfig.ClanColors[num]; bool num2 = ClanConfig.IsReservedClanColor(val) || IsBannerColorTaken(clan, val); Color color = GUI.color; GUI.color = (Color)(num2 ? new Color(val.r, val.g, val.b, 0.35f) : val); GUI.enabled = !num2; if (GUILayout.Button((num == _selectedBannerColorIndex) ? "*" : string.Empty, (num == _selectedBannerColorIndex) ? _tabActiveStyle : _smallButtonStyle, (GUILayoutOption[])(object)new GUILayoutOption[2] { GUILayout.Width(42f), GUILayout.Height(36f) })) { _selectedBannerColorIndex = num; } GUI.enabled = true; GUI.color = color; } GUILayout.EndHorizontal(); } Color color2 = ClanConfig.ClanColors[_selectedBannerColorIndex]; GUILayout.BeginHorizontal(Array.Empty()); if (GUILayout.Button("Apply Banner Color", _buttonStyle, (GUILayoutOption[])(object)new GUILayoutOption[2] { GUILayout.Width(170f), GUILayout.Height(36f) })) { clan.SetColor(color2); WarfareManager.Instance?.SaveData(); _editingBannerColors = false; } if (GUILayout.Button("Cancel", _smallButtonStyle, (GUILayoutOption[])(object)new GUILayoutOption[2] { GUILayout.Width(90f), GUILayout.Height(36f) })) { _editingBannerColors = false; } GUILayout.EndHorizontal(); GUILayout.EndVertical(); } private void DrawCurrentZoneClaimPanel(WarfareData data, PlayerClanInfo info, ClanTerritory territory, Vector2i grid) { //IL_019f: Unknown result type (might be due to invalid IL or missing references) //IL_005d: Unknown result type (might be due to invalid IL or missing references) GUILayout.BeginVertical(_boxStyle, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.ExpandHeight(false) }); GUILayout.Label("CURRENT ZONE / CLAIM TERRITORY", _labelGoldStyle, Array.Empty()); if (territory != null && territory.OwnerClanId != 0) { Clan clan = data.GetClan(territory.OwnerClanId); string text = ((clan != null) ? WarfareManager.ColorHex(clan.Color) : "888888"); bool flag = territory.OwnerClanId == info.ClanId; GUILayout.Label("Current zone: " + territory.DisplayName + " (" + (flag ? "HOME" : "ENEMY") + ") - " + territory.Type.ToString() + ", " + Economy.ValueTierName(territory.BiomeValueTier) + "-value, size " + territory.SizeLevel, _labelStyle, Array.Empty()); if (territory.RotCount > 0) { GUILayout.Label("Corruption: " + Mathf.Clamp(territory.RotCount, 0, 4) + "/4 - Root income / material production reduction: " + Mathf.Clamp(territory.RotCount, 0, 4) * 25 + "%", _labelStyle, Array.Empty()); } } else { GUILayout.Label("Current zone: Unclaimed wilderness", _labelStyle, Array.Empty()); } DrawZoneActions(data, info, territory, grid); GUILayout.EndVertical(); } private void DrawProfileCharacter(WarfareData data, PlayerClanInfo info, Clan clan) { //IL_076f: Unknown result type (might be due to invalid IL or missing references) //IL_0774: Unknown result type (might be due to invalid IL or missing references) //IL_077c: Unknown result type (might be due to invalid IL or missing references) //IL_0785: Expected O, but got Unknown bool flag = IsRemoteProfile(info); ClanTerritory clanTerritory = (flag ? null : GetCurrentTerritory(data)); ClanTerritory capitalTerritory = GetCapitalTerritory(data, info.ClanId); Texture2D val = GetProfileTexture(info) ?? LocalEmblemCache.GetTexture(clan?.ClanEmblemName); GUILayout.BeginHorizontal(Array.Empty()); GUILayout.BeginVertical(_boxStyle, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(270f) }); GUILayout.Label("IDENTITY", _labelGoldStyle, Array.Empty()); GUILayout.Box((Texture)(object)val, _boxStyle, (GUILayoutOption[])(object)new GUILayoutOption[2] { GUILayout.Width(250f), GUILayout.Height(250f) }); GUILayout.Label("" + (info.PlayerName ?? "Unknown") + "", _labelGoldStyle, Array.Empty()); DrawProfileLine("Player ID", info.PlayerId.ToString()); DrawProfileLine("Clan", (clan != null) ? clan.Name : (info.IsFreeborn ? "Freeborn" : "No Clan")); DrawProfileLine("Tag", (clan != null) ? clan.Tag : "-"); DrawProfileLine("Rank", info.Rank.ToString()); if (info.IsFreeborn && info.ClanId == 0) { DrawProfileLine("Homestead", GetFreebornTerritories(data, info.PlayerId, TerritoryType.Homestead).FirstOrDefault()?.DisplayName ?? "None"); DrawProfileLine("Outposts", GetFreebornTerritories(data, info.PlayerId, TerritoryType.Outpost).Count.ToString()); } DrawProfileLine("Title", ProfileTitle(info, clan)); DrawProfileLine("Primary", RoleName(info.PrimaryRole)); DrawProfileLine("Secondary", RoleName(info.SecondaryRole)); DrawProfileLine("Tertiary", RoleName(info.TertiaryRole)); GUILayout.EndVertical(); GUILayout.BeginVertical((GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.ExpandWidth(true) }); if (!flag) { DrawProfileImagePicker(info); if (_profileImageExpanded) { GUILayout.EndVertical(); GUILayout.EndHorizontal(); return; } } GUILayout.Space(6f); GUILayout.BeginVertical(_boxStyle, (GUILayoutOption[])(object)new GUILayoutOption[2] { GUILayout.ExpandWidth(true), GUILayout.Height(145f) }); GUILayout.Label("ABOUT ME", _labelGoldStyle, Array.Empty()); int num = Mathf.Clamp(ClanConfig.ProfileAboutMaxLength?.Value ?? 500, 0, 2000); if (flag) { GUILayout.Label(string.IsNullOrWhiteSpace(info.AboutMe) ? "No About Me text." : info.AboutMe, _labelStyle, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Height(92f) }); } else { if (string.IsNullOrEmpty(_profileAboutDraft)) { _profileAboutDraft = info.AboutMe ?? ""; } _profileAboutDraft = GUILayout.TextArea(_profileAboutDraft, num, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Height(70f) }); GUILayout.BeginHorizontal(Array.Empty()); GUILayout.Label("Plain text, up to " + num + " characters.", _labelStyle, Array.Empty()); GUILayout.FlexibleSpace(); if (GUILayout.Button("Save About Me", _smallButtonStyle, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(150f) })) { WarfareRPC.Instance?.RequestUpdateAboutMe(_profileAboutDraft); } GUILayout.EndHorizontal(); } GUILayout.EndVertical(); GUILayout.Space(6f); GUILayout.BeginHorizontal(Array.Empty()); DrawProfileStatsColumn("GENERAL STATS", new string[7] { "Playtime: " + FormatProfileDuration(info.TotalPlayMinutes), "First login: " + FormatProfileTime(info.FirstLoginTimestamp), "Last login: " + FormatProfileTime(info.LastLoginTimestamp), "Total logins: " + info.TotalLogins, "Account age: " + ((info.FirstLoginTimestamp > 0) ? (Math.Max(0L, (WarfareManager.Now() - info.FirstLoginTimestamp) / 86400) + " days") : "Unknown"), "Current biome: " + ((clanTerritory != null) ? BiomeDisplayName(clanTerritory) : "Unknown"), "Home territory: " + ((capitalTerritory != null) ? capitalTerritory.DisplayName : "None") }); DrawProfileStatsColumn("CLAN / ECONOMY", new string[7] { "Clan join: " + FormatProfileTime(info.JoinedClanTimestamp), "Time in clan: " + FormatProfileDuration((WarfareManager.Now() - info.JoinedClanTimestamp) / 60), "Coins donated: " + info.CoinsDonatedToTreasury, "Items donated: " + info.ItemsDonatedToVault, "Resource tax paid: " + info.ResourceTaxPaid, "Coin tax paid: " + info.CoinTaxPaid, "Highest donation: " + info.HighestDonation }); DrawProfileStatsColumn("TERRITORY / WARFARE", new string[7] { "Claimed: " + info.TerritoriesClaimed, "Captured: " + info.TerritoriesCaptured, "Lost: " + info.TerritoriesLost, "Capitals founded: " + info.CapitalsFounded, "Homesteads founded: " + info.HomesteadsFounded, "Wars: " + info.WarsParticipated, "PvP K/D: " + info.PvPKills + "/" + info.PvPDeaths }); GUILayout.EndHorizontal(); GUILayout.Space(6f); int num2 = info.TerritoriesClaimed * 100 + info.TerritoriesCaptured * 200 + info.CapitalsFounded * 250; int num3 = info.WarsParticipated * 100 + info.WarsWon * 200 + info.PvPKills * 10 + info.BountiesClaimed * 75; int num4 = (int)Mathf.Min(100000f, (float)(info.CoinsDonatedToTreasury + info.ResourceTaxPaid + info.CoinTaxPaid)) / 10; int num5 = info.BuildingsContributed * 25 + info.OutpostsFounded * 100 + info.HomesteadsFounded * 100; int num6 = info.TerritoriesDiscovered * 75 + info.WorldEventsCompleted * 80; int num7 = (int)Mathf.Min(100000f, (float)info.ItemsDonatedToVault) / 10; int num8 = ((clan != null && clan.JarlId == info.PlayerId) ? 100 : 0); int num9 = WarfareManager.PersonalSagaLegacyScore(info); int num10 = num2 + num3 + num4 + num5 + num6 + num7 + num8 + num9; GUIStyle val2 = new GUIStyle(_labelGoldStyle) { fontSize = 22, alignment = (TextAnchor)5 }; GUILayout.BeginHorizontal((GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Height(30f) }); GUILayout.Label("FEHU LEGACY SCORE", _labelGoldStyle, Array.Empty()); GUILayout.FlexibleSpace(); GUILayout.Label("" + num10 + "", val2, (GUILayoutOption[])(object)new GUILayoutOption[2] { GUILayout.Width(90f), GUILayout.Height(30f) }); GUILayout.EndHorizontal(); GUILayout.Label("Calculated from gameplay achievements and activity. Profile edits do not grant Legacy points.", _labelStyle, Array.Empty()); float num11 = Mathf.Max(800f, AvailableContentWidth() - 292f); float width = Mathf.Max(92f, (num11 - 28f) / 8f); GUILayout.BeginHorizontal(Array.Empty()); DrawProfileMetric("Territory", num2, width); DrawProfileMetric("Warfare", num3, width); DrawProfileMetric("Economy", num4, width); DrawProfileMetric("Building", num5, width); DrawProfileMetric("Exploration", num6, width); DrawProfileMetric("Clan Support", num7, width); DrawProfileMetric("Diplomacy", num8, width); DrawProfileMetric("Saga", num9, width); GUILayout.EndHorizontal(); GUILayout.Space(6f); GUILayout.BeginVertical(_boxStyle, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.ExpandWidth(true) }); GUILayout.Label("ACHIEVEMENTS / BADGES", _labelGoldStyle, Array.Empty()); GUILayout.Label("Constellation badges are assigned by SteamID64.", _labelStyle, Array.Empty()); DrawServerBadgeBoxes(info); GUILayout.EndVertical(); GUILayout.EndVertical(); GUILayout.EndHorizontal(); GUILayout.Space(12f); } private string ServerBadgeText(PlayerClanInfo info) { List list = WarfareManager.Instance?.GetServerBadges(info) ?? new List(); if (list.Count != 0) { return string.Join(", ", list.ToArray()); } return "None"; } private void DrawServerBadgeBoxes(PlayerClanInfo info) { //IL_006c: Unknown result type (might be due to invalid IL or missing references) //IL_0077: Expected O, but got Unknown List list = WarfareManager.Instance?.GetServerBadges(info) ?? new List(); if (list.Count == 0) { GUILayout.Label("No supporter badges assigned.", _labelStyle, Array.Empty()); return; } GUILayout.BeginHorizontal(Array.Empty()); foreach (string item in list) { Texture2D val = GetConstellationSmallBadgeTexture(item) ?? GetConstellationBadgeTexture(item); if ((Object)val != (Object)null) { GUILayout.Box((Texture)(object)val, _boxStyle, (GUILayoutOption[])(object)new GUILayoutOption[2] { GUILayout.Width(40f), GUILayout.Height(40f) }); } else { GUILayout.Box(item, _boxStyle, (GUILayoutOption[])(object)new GUILayoutOption[2] { GUILayout.Width(150f), GUILayout.Height(46f) }); } } GUILayout.EndHorizontal(); } private void DrawSupporterBadgeIcons(string playerOrSteamId) { //IL_005a: Unknown result type (might be due to invalid IL or missing references) //IL_0065: Expected O, but got Unknown List list = WarfareManager.Instance?.GetSupporterBadges(playerOrSteamId) ?? new List(); GUILayout.BeginHorizontal((GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.ExpandWidth(false) }); foreach (string item in list) { Texture2D val = GetConstellationSmallBadgeTexture(item) ?? GetConstellationBadgeTexture(item); if ((Object)val != (Object)null) { GUILayout.Box((Texture)(object)val, _boxStyle, (GUILayoutOption[])(object)new GUILayoutOption[2] { GUILayout.Width(32f), GUILayout.Height(32f) }); } } GUILayout.EndHorizontal(); } private Texture2D GetConstellationBadgeTexture(string badge) { if (_constellationBadgeTextures == null) { _constellationBadgeTextures = LoadRuntimePngTextures(Path.Combine("FehuKingdoms", "Constellation"), recursive: false); if (_constellationBadgeTextures.Count == 0) { _constellationBadgeTextures = LoadRuntimePngTextures("Constellation", recursive: false); } } string text = BadgeIconKey(badge); if (!_constellationBadgeTextures.TryGetValue(text, out var value) && !_constellationBadgeTextures.TryGetValue(NormalizeAssetKey(text), out value)) { return null; } return value; } private Texture2D GetConstellationSmallBadgeTexture(string badge) { if (_constellationSmallBadgeTextures == null) { _constellationSmallBadgeTextures = LoadRuntimePngTextures(Path.Combine("FehuKingdoms", "Constellation"), recursive: false); if (_constellationSmallBadgeTextures.Count == 0) { _constellationSmallBadgeTextures = LoadRuntimePngTextures("Constellation", recursive: false); } } string text = SmallBadgeIconKey(badge); if (!_constellationSmallBadgeTextures.TryGetValue(text, out var value) && !_constellationSmallBadgeTextures.TryGetValue(NormalizeAssetKey(text), out value)) { return null; } return value; } private static string SmallBadgeIconKey(string badge) { switch (badge ?? "") { case "Contributor": case "Gold Contributor": return "contributor"; case "Silver Subscriber": case "Subscriber": return "subscriber"; case "Steel Supporter": case "Supporter": return "supporter"; case "Bronze Alpha Tester": case "Alpha Tester": return "alpha_tester"; case "Beta Tester": return "beta_tester"; case "Admin": case "Administrator": return "admin"; default: return NormalizeAssetKey(badge); } } private static string BadgeIconKey(string badge) { switch (badge ?? "") { case "Contributor": case "Gold Contributor": return "contributor"; case "Silver Subscriber": case "Subscriber": return "subscriber"; case "Steel Supporter": case "Supporter": return "supporter"; case "Bronze Alpha Tester": case "Alpha Tester": return "alpha_tester"; case "Beta Tester": return "beta_tester"; case "Admin": case "Administrator": return "admin"; default: return NormalizeAssetKey(badge); } } private void DrawProfileSaga(WarfareData data, PlayerClanInfo info) { //IL_0257: Unknown result type (might be due to invalid IL or missing references) //IL_026e: Unknown result type (might be due to invalid IL or missing references) //IL_0273: Unknown result type (might be due to invalid IL or missing references) List list = (from entry in info.PersonalSaga ?? new List() where entry != null orderby entry.TimestampUtc descending select entry).ToList(); int num = list.Count((PlayerSagaEntry entry) => entry.Importance == "Major" || entry.Importance == "Legendary"); GUILayout.BeginVertical(_boxStyle, Array.Empty()); GUILayout.BeginHorizontal(Array.Empty()); GUILayout.Label("PERSONAL SAGA", _labelGoldStyle, Array.Empty()); GUILayout.FlexibleSpace(); GUILayout.Label("Entries: " + list.Count, _labelStyle, Array.Empty()); GUILayout.Space(24f); GUILayout.Label("Major milestones: " + num, _labelStyle, Array.Empty()); GUILayout.EndHorizontal(); string[] categories = new string[7] { "All", "Clan", "Territory", "Building", "Economy", "Achievements", "Events" }; string[] array = new string[categories.Length]; int i; for (i = 0; i < categories.Length; i++) { int num2 = ((i == 0) ? list.Count : list.Count((PlayerSagaEntry entry) => PersonalSagaCategory(entry.EventType) == categories[i])); array[i] = categories[i] + " (" + num2 + ")"; } DrawProfileFilterRow(array, ref _profileSagaFilter); List list2 = list.Where((PlayerSagaEntry entry) => _profileSagaFilter == 0 || PersonalSagaCategory(entry.EventType) == categories[_profileSagaFilter]).ToList(); float num3 = Mathf.Max(220f, AvailableContentHeight() - 90f - BottomSafePadding()); _profileSagaScroll = GUILayout.BeginScrollView(_profileSagaScroll, false, true, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Height(num3) }); if (list2.Count == 0) { GUILayout.BeginVertical(_boxStyle, Array.Empty()); GUILayout.Label("No personal saga entries yet.", _labelStyle, Array.Empty()); GUILayout.EndVertical(); } foreach (PlayerSagaEntry item in list2) { DrawPersonalSagaCard(data, item); } GUILayout.Space(BottomSafePadding()); GUILayout.EndScrollView(); GUILayout.EndVertical(); } private static string PersonalSagaCategory(string eventType) { switch (eventType) { case "ClanJoined": case "ClanFounded": case "BecameJarl": return "Clan"; case "CapitalFounded": case "HomesteadFounded": case "OutpostFounded": case "TerritoryClaimed": return "Territory"; case "BuildingMilestone": return "Building"; case "DonationMilestone": case "BountyClaimed": return "Economy"; case "AchievementUnlocked": case "TitleUnlocked": return "Achievements"; default: return "Events"; } } private void DrawPersonalSagaCard(WarfareData data, PlayerSagaEntry entry) { GUILayout.BeginVertical((entry.Importance == "Legendary") ? _tabActiveStyle : _boxStyle, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.ExpandWidth(true) }); GUILayout.BeginHorizontal(Array.Empty()); GUILayout.Label("" + entry.Title + "", _labelGoldStyle, Array.Empty()); GUILayout.FlexibleSpace(); GUILayout.Label(entry.Importance, (entry.Importance == "Major" || entry.Importance == "Legendary") ? _labelGoldStyle : _labelStyle, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(90f) }); GUILayout.EndHorizontal(); if (!string.IsNullOrWhiteSpace(entry.Description)) { GUILayout.Label(entry.Description, _labelStyle, Array.Empty()); } string text = ""; if (entry.RelatedClanId != 0) { text = data.GetClan(entry.RelatedClanId)?.Name ?? ("Clan " + entry.RelatedClanId); } if (entry.RelatedTerritoryId != 0) { ClanTerritory clanTerritory = data.Territories.Values.FirstOrDefault((ClanTerritory value) => WarfareManager.TerritorySagaId(value) == entry.RelatedTerritoryId); if (clanTerritory != null) { text = (string.IsNullOrEmpty(text) ? clanTerritory.DisplayName : (text + " — " + clanTerritory.DisplayName)); } } if (!string.IsNullOrEmpty(text)) { GUILayout.Label(text, _labelGoldStyle, Array.Empty()); } GUILayout.Label((entry.TimestampUtc > 0) ? DateTimeOffset.FromUnixTimeSeconds(entry.TimestampUtc).ToLocalTime().ToString("yyyy-MM-dd HH:mm") : "Date unknown", _labelStyle, Array.Empty()); GUILayout.EndVertical(); GUILayout.Space((entry.Importance == "Minor") ? 3f : 6f); } private void DrawProfileLine(string label, string value) { GUILayout.Label("" + label + ": " + (value ?? "Unknown") + "", _labelStyle, Array.Empty()); } private void DrawProfileMetric(string label, int value, float width) { GUILayout.BeginVertical(_boxStyle, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(width) }); GUILayout.Label(label, _labelStyle, Array.Empty()); GUILayout.Label("" + value + "", _labelGoldStyle, Array.Empty()); GUILayout.EndVertical(); } private void DrawProfileStatsColumn(string title, string[] rows) { GUILayout.BeginVertical(_boxStyle, (GUILayoutOption[])(object)new GUILayoutOption[2] { GUILayout.ExpandWidth(true), GUILayout.Height(150f) }); GUILayout.Label("" + title + "", _labelGoldStyle, Array.Empty()); for (int i = 0; i < rows.Length; i++) { GUILayout.Label(rows[i], _labelStyle, Array.Empty()); } GUILayout.EndVertical(); } private string ProfileTitle(PlayerClanInfo info, Clan clan) { PlayerTitleDefinition active = PlayerTitleCatalog.Get(info.ActiveTitleId); if (active != null && (info.UnlockedTitles ?? new List()).Any((string id) => string.Equals(id, active.Id, StringComparison.OrdinalIgnoreCase))) { return active.DisplayName; } if (!string.IsNullOrWhiteSpace(info.ActiveTitle)) { return info.ActiveTitle; } if (clan != null && clan.FounderId == info.PlayerId) { return "Founder"; } if (info.Rank == ClanRank.Jarl) { return "Jarl"; } return "None"; } private void DrawProfileTitles(PlayerClanInfo info, Clan clan) { //IL_017d: Unknown result type (might be due to invalid IL or missing references) //IL_0193: Unknown result type (might be due to invalid IL or missing references) //IL_0198: Unknown result type (might be due to invalid IL or missing references) GUILayout.BeginVertical(_boxStyle, Array.Empty()); GUILayout.BeginHorizontal(Array.Empty()); GUILayout.Label("CURRENT ACTIVE TITLE", _labelGoldStyle, Array.Empty()); GUILayout.FlexibleSpace(); GUILayout.Label("" + ProfileTitle(info, clan) + "", _labelGoldStyle, Array.Empty()); if (!IsRemoteProfile(info) && GUILayout.Button("Clear Title", _smallButtonStyle, (GUILayoutOption[])(object)new GUILayoutOption[2] { GUILayout.Width(110f), GUILayout.Height(26f) })) { WarfareRPC.Instance?.RequestSetActiveTitle(""); } GUILayout.EndHorizontal(); GUILayout.Space(4f); string[] array = new string[6] { "All", "Special", "Activity", "Clan", "Territory", "Economy" }; DrawProfileFilterRow(array, ref _profileTitleFilter); string category = ((_profileTitleFilter == 0) ? "" : array[_profileTitleFilter]); List list = PlayerTitleCatalog.All.Where((PlayerTitleDefinition title) => string.IsNullOrEmpty(category) || string.Equals(title.Category, category, StringComparison.OrdinalIgnoreCase)).ToList(); float num = Mathf.Max(220f, AvailableContentHeight() - 90f - BottomSafePadding()); float width = Mathf.Max(360f, (AvailableContentWidth(reserveScrollbar: true) - 28f) / 2f); _profileTitlesScroll = GUILayout.BeginScrollView(_profileTitlesScroll, false, true, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Height(num) }); for (int num2 = 0; num2 < list.Count; num2 += 2) { GUILayout.BeginHorizontal(Array.Empty()); DrawProfileTitleCard(info, list[num2], width); if (num2 + 1 < list.Count) { DrawProfileTitleCard(info, list[num2 + 1], width); } else { GUILayout.FlexibleSpace(); } GUILayout.EndHorizontal(); } GUILayout.Space(BottomSafePadding()); GUILayout.EndScrollView(); GUILayout.EndVertical(); } private void DrawProfileAchievements(PlayerClanInfo info) { //IL_01c9: Unknown result type (might be due to invalid IL or missing references) //IL_01e0: Unknown result type (might be due to invalid IL or missing references) //IL_01e5: Unknown result type (might be due to invalid IL or missing references) GUILayout.BeginVertical(_boxStyle, Array.Empty()); int num = (info.UnlockedAchievements ?? new List()).Count((string id) => PlayerAchievementCatalog.Get(id) != null); GUILayout.BeginHorizontal(Array.Empty()); GUILayout.Label("ACHIEVEMENTS", _labelGoldStyle, Array.Empty()); GUILayout.FlexibleSpace(); GUILayout.Label("Unlocked: " + num + " / " + PlayerAchievementCatalog.All.Count, _labelStyle, Array.Empty()); GUILayout.Space(24f); GUILayout.Label("Coins earned from achievements: " + Math.Max(0L, info.AchievementCoinsEarned), _labelStyle, Array.Empty()); GUILayout.EndHorizontal(); string[] array = new string[7] { "All", "Activity", "Clan", "Territory", "Building", "Economy", "Events" }; DrawProfileFilterRow(array, ref _profileAchievementFilter); string category = ((_profileAchievementFilter == 0) ? "" : array[_profileAchievementFilter]); List list = PlayerAchievementCatalog.All.Where((PlayerAchievementDefinition a) => !a.Hidden && (string.IsNullOrEmpty(category) || string.Equals(a.Category, category, StringComparison.OrdinalIgnoreCase))).ToList(); float num2 = AvailableContentWidth(reserveScrollbar: true); int num3 = ((num2 >= 1050f) ? 3 : 2); float num4 = Mathf.Max(300f, (num2 - (float)(num3 - 1) * 8f - 20f) / (float)num3); float num5 = Mathf.Max(220f, AvailableContentHeight() - 90f - BottomSafePadding()); _profileAchievementsScroll = GUILayout.BeginScrollView(_profileAchievementsScroll, false, true, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Height(num5) }); for (int num6 = 0; num6 < list.Count; num6 += num3) { GUILayout.BeginHorizontal(Array.Empty()); for (int num7 = 0; num7 < num3; num7++) { int num8 = num6 + num7; if (num8 < list.Count) { DrawProfileAchievementCard(info, list[num8], num4); } else { GUILayout.Space(num4); } } GUILayout.EndHorizontal(); } GUILayout.Space(BottomSafePadding()); GUILayout.EndScrollView(); GUILayout.EndVertical(); } private void DrawProfileFilterRow(string[] filters, ref int activeFilter) { GUILayout.BeginHorizontal(Array.Empty()); for (int i = 0; i < filters.Length; i++) { if (GUILayout.Button(filters[i], (activeFilter == i) ? _tabActiveStyle : _smallButtonStyle, (GUILayoutOption[])(object)new GUILayoutOption[2] { GUILayout.ExpandWidth(true), GUILayout.Height(26f) })) { activeFilter = i; } } GUILayout.EndHorizontal(); } private void DrawProfileAchievementCard(PlayerClanInfo info, PlayerAchievementDefinition achievement, float width) { bool flag = (info.UnlockedAchievements ?? new List()).Any((string id) => string.Equals(id, achievement.Id, StringComparison.OrdinalIgnoreCase)); long val = WarfareManager.Instance?.AchievementProgress(info, achievement) ?? 0; long progress = Math.Min(Math.Max(0L, val), achievement.RequiredValue); int num = ((achievement.RewardAmount > 0) ? achievement.RewardAmount : Math.Max(0, ClanConfig.AchievementDefaultCoinReward?.Value ?? 10)); GUILayout.BeginVertical(_boxStyle, (GUILayoutOption[])(object)new GUILayoutOption[2] { GUILayout.Width(width), GUILayout.Height(128f) }); GUILayout.BeginHorizontal(Array.Empty()); GUILayout.Label("" + achievement.DisplayName + "", _labelGoldStyle, Array.Empty()); GUILayout.FlexibleSpace(); GUILayout.Label(flag ? "UNLOCKED" : "LOCKED", _labelStyle, Array.Empty()); GUILayout.EndHorizontal(); GUILayout.Label(achievement.Description, _labelStyle, Array.Empty()); GUILayout.Label(AchievementProgressText(achievement, progress), _labelStyle, Array.Empty()); GUILayout.Label("Reward: " + num + " Coins", _labelStyle, Array.Empty()); if (flag && info.AchievementUnlockTimestamps != null && info.AchievementUnlockTimestamps.TryGetValue(achievement.Id, out var value)) { GUILayout.Label("Unlocked — " + FormatProfileTime(value), _labelStyle, Array.Empty()); } GUILayout.EndVertical(); } private static string AchievementProgressText(PlayerAchievementDefinition achievement, long progress) { return achievement.ProgressType switch { AchievementProgressType.FirstLogin => "First login: " + progress + " / 1", AchievementProgressType.PlayMinutes => "Playtime: " + progress + " / " + achievement.RequiredValue + " minutes", AchievementProgressType.ClanDays => "Time in clan: " + progress + " / " + achievement.RequiredValue + " days", AchievementProgressType.CoinsDonated => "Coins donated: " + progress + " / " + achievement.RequiredValue, AchievementProgressType.ItemsDonated => "Items donated: " + progress + " / " + achievement.RequiredValue, AchievementProgressType.TaxesPaid => "Tax paid: " + progress + " / " + achievement.RequiredValue, _ => achievement.RequirementText + " (" + progress + " / " + achievement.RequiredValue + ")", }; } private void DrawProfileTitleCard(PlayerClanInfo info, PlayerTitleDefinition title, float width) { bool flag = (info.UnlockedTitles ?? new List()).Any((string id) => string.Equals(id, title.Id, StringComparison.OrdinalIgnoreCase)); bool flag2 = flag && string.Equals(info.ActiveTitleId, title.Id, StringComparison.OrdinalIgnoreCase); GUILayout.BeginVertical(_boxStyle, (GUILayoutOption[])(object)new GUILayoutOption[2] { GUILayout.Width(width), GUILayout.Height(124f) }); GUILayout.BeginHorizontal(Array.Empty()); GUILayout.Label("" + title.DisplayName + "", _labelGoldStyle, Array.Empty()); GUILayout.FlexibleSpace(); GUILayout.Label(flag2 ? "ACTIVE" : (flag ? "UNLOCKED" : "LOCKED"), _labelStyle, Array.Empty()); GUILayout.EndHorizontal(); GUILayout.Label(title.Description, _labelStyle, Array.Empty()); GUILayout.Label(TitleProgressText(info, title), _labelStyle, Array.Empty()); GUILayout.FlexibleSpace(); if (!IsRemoteProfile(info)) { GUI.enabled = flag && !flag2; if (GUILayout.Button(flag2 ? "Active" : "Select", _smallButtonStyle, (GUILayoutOption[])(object)new GUILayoutOption[2] { GUILayout.Width(90f), GUILayout.Height(24f) })) { WarfareRPC.Instance?.RequestSetActiveTitle(title.Id); } GUI.enabled = true; } GUILayout.EndVertical(); } private static string TitleProgressText(PlayerClanInfo info, PlayerTitleDefinition title) { switch (title.Id) { case "veteran": return "Playtime: " + Math.Min(info.TotalPlayMinutes, 600L) + " / 600 minutes"; case "landclaimer": return "Territories claimed: " + Math.Min(info.TerritoriesClaimed, 1) + " / 1"; case "realm_builder": return "Buildings contributed: " + Math.Min(info.BuildingsContributed, 10) + " / 10"; case "benefactor": return "Coins donated: " + Math.Min(info.CoinsDonatedToTreasury, 1000L) + " / 1000"; case "great_benefactor": return "Coins donated: " + Math.Min(info.CoinsDonatedToTreasury, 10000L) + " / 10000"; case "loyal_member": { long val = ((info.ClanId != 0 && info.JoinedClanTimestamp > 0) ? Math.Max(0L, (WarfareManager.Now() - info.JoinedClanTimestamp) / 86400) : 0); return "Time in clan: " + Math.Min(val, 30L) + " / 30 days"; } default: return title.Requirement; } } private static string FormatProfileTime(long timestamp) { if (timestamp > 0) { return DateTimeOffset.FromUnixTimeSeconds(timestamp).ToLocalTime().ToString("yyyy-MM-dd HH:mm"); } return "Unknown"; } private static string FormatProfileDuration(long minutes) { if (minutes <= 0) { return "0m"; } return minutes / 1440 + "d " + minutes % 1440 / 60 + "h"; } private void DrawJarlElectionSummary(WarfareData data, PlayerClanInfo info, Clan clan) { if (data != null && info != null && clan != null) { long num = WarfareManager.Now(); if (!IsElectionActive(clan, num)) { GUILayout.Label("No active Jarl election.", _labelStyle, Array.Empty()); return; } int clanMemberCount = data.GetClanMemberCount(clan.ClanId); int num2 = CountValidElectionVotes(data, clan); int votes; bool tied; PlayerClanInfo electionLeader = GetElectionLeader(data, clan, out votes, out tied); GUILayout.Label("Election active: " + FormatDuration(Math.Max(0L, clan.NextElectionTimestamp - num)), _labelStyle, Array.Empty()); GUILayout.Label($"Votes: {num2}/{clanMemberCount}", _labelStyle, Array.Empty()); GUILayout.Label("Leader: " + ((electionLeader != null) ? (electionLeader.PlayerName + " (" + votes + " votes" + (tied ? ", tied" : "") + ")") : "None"), _labelStyle, Array.Empty()); } } private void DrawJarlElectionPanel(WarfareData data, PlayerClanInfo info, Clan clan) { if (data == null || info == null || clan == null) { return; } long num = WarfareManager.Now(); int num2 = Math.Max(1, ClanConfig.ElectionIntervalDays?.Value ?? 14); int num3 = Math.Max(1, ClanConfig.JarlElectionDurationHours?.Value ?? 72); int num4 = Math.Max(0, Math.Min(100, ClanConfig.JarlElectionMinimumTurnoutPercent?.Value ?? 50)); int num5 = Math.Max(1, ClanConfig.JarlElectionTieExtensionHours?.Value ?? 24); long num6 = clan.NextElectionTimestamp - (long)num3 * 3600L; bool flag = IsElectionActive(clan, num); int clanMemberCount = data.GetClanMemberCount(clan.ClanId); int votes = CountValidElectionVotes(data, clan); int required = RequiredElectionTurnout(clanMemberCount); int votes2; bool tied; PlayerClanInfo electionLeader = GetElectionLeader(data, clan, out votes2, out tied); bool flag2 = clan.ElectionVotes != null && clan.ElectionVotes.ContainsKey(info.PlayerId); GUILayout.BeginVertical(_boxStyle, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.ExpandWidth(true) }); GUILayout.Label("JARL ELECTIONS", _majorSectionTitleStyle, Array.Empty()); GUILayout.Label("Clan elections provide a regular, member-controlled way to confirm the current Jarl or choose a new leader.", _bodyTextStyle, Array.Empty()); GUILayout.BeginHorizontal(Array.Empty()); GUILayout.BeginVertical(_infoPanelStyle, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.ExpandWidth(true) }); GUILayout.Label("WHEN DOES VOTING HAPPEN?", _cardTitleStyle, Array.Empty()); DrawMiniStat("Current status", (!flag) ? "Scheduled" : (tied ? "Active - current lead tied" : "Voting open")); DrawMiniStat("Election deadline", (clan.NextElectionTimestamp > 0) ? FormatTimestamp(clan.NextElectionTimestamp) : "Being scheduled"); DrawMiniStat("Voting window", num3 + " hours before the deadline"); DrawMiniStat(flag ? "Time remaining" : "Voting opens in", (clan.NextElectionTimestamp <= 0) ? "-" : FormatDuration(Math.Max(0L, (flag ? clan.NextElectionTimestamp : num6) - num))); DrawMiniStat("Election cycle", "Every " + num2 + " real days"); GUILayout.EndVertical(); GUILayout.BeginVertical(_infoPanelStyle, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.ExpandWidth(true) }); GUILayout.Label("WHO CAN VOTE AND RUN?", _cardTitleStyle, Array.Empty()); GUILayout.Label("Every current clan member is eligible to vote and is also an eligible candidate, including the current Jarl.", _bodyTextStyle, Array.Empty()); GUILayout.Label("Each member has one current vote. You may vote for yourself and may change your vote at any time while voting is open.", _bodyTextStyle, Array.Empty()); GUILayout.Label("Votes are validated at the deadline. A vote is ignored if either the voter or candidate is no longer in this clan.", _bodyTextStyle, Array.Empty()); GUILayout.EndVertical(); GUILayout.BeginVertical(_infoPanelStyle, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.ExpandWidth(true) }); GUILayout.Label("HOW IS THE RESULT DECIDED?", _cardTitleStyle, Array.Empty()); GUILayout.Label("At least " + required + " of " + clanMemberCount + " eligible members must vote (" + num4 + "% turnout, rounded up). Without enough valid votes, the election fails and leadership does not change.", _bodyTextStyle, Array.Empty()); GUILayout.Label("The candidate with the most valid votes becomes Jarl. If leadership changes, the previous Jarl becomes an Officer.", _bodyTextStyle, Array.Empty()); GUILayout.Label("A tie for first place extends the deadline by " + num5 + " hours and keeps the existing votes. After a final result, the next election is scheduled for the next cycle.", _bodyTextStyle, Array.Empty()); GUILayout.EndVertical(); GUILayout.EndHorizontal(); GUILayout.Space(8f); GUILayout.BeginVertical(flag ? _specialPanelStyle : _infoPanelStyle, Array.Empty()); GUILayout.Label(flag ? "VOTING IS OPEN" : "NEXT ELECTION", flag ? _specialValueStyle : _cardTitleStyle, Array.Empty()); if (!flag) { GUILayout.Label((clan.NextElectionTimestamp > 0) ? ("Voting opens " + FormatTimestamp(num6) + ". Return here during the voting window to choose or change your candidate.") : "The server is preparing the first election schedule.", _bodyTextStyle, Array.Empty()); GUILayout.EndVertical(); GUILayout.EndVertical(); return; } GUILayout.BeginHorizontal(Array.Empty()); DrawMiniStat("Eligible voters", clanMemberCount.ToString()); DrawMiniStat("Votes cast", votes + " / " + required + " required"); DrawMiniStat("Current leader", (electionLeader != null) ? (electionLeader.PlayerName + " (" + votes2 + " votes" + (tied ? ", tied" : "") + ")") : "None"); DrawMiniStat("Your vote", flag2 ? "Recorded - you may change it" : "Not cast"); GUILayout.EndHorizontal(); GUILayout.Label("CURRENT RESULTS", _labelGoldStyle, Array.Empty()); List> electionVoteTotals = GetElectionVoteTotals(data, clan); if (electionVoteTotals.Count == 0) { GUILayout.Label("No votes cast yet.", _labelStyle, Array.Empty()); } else { for (int i = 0; i < electionVoteTotals.Count; i++) { PlayerClanInfo player = data.GetPlayer(electionVoteTotals[i].Key); GUILayout.Label(i + 1 + ". " + (player?.PlayerName ?? ("Player " + electionVoteTotals[i].Key)) + " - " + electionVoteTotals[i].Value + " votes", _labelStyle, Array.Empty()); } } GUILayout.Space(6f); GUILayout.Label("" + (flag2 ? "CHANGE YOUR VOTE" : "CAST YOUR VOTE") + "", _labelGoldStyle, Array.Empty()); List clanMembers = data.GetClanMembers(clan.ClanId); GUILayout.BeginHorizontal(Array.Empty()); for (int j = 0; j < clanMembers.Count; j++) { PlayerClanInfo playerClanInfo = clanMembers[j]; if (GUILayout.Button((flag2 ? "Change: " : "Vote: ") + playerClanInfo.PlayerName, _smallButtonStyle, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(150f) })) { Plugin.Log.LogInfo((object)("FCW Election Vote: player=" + info.PlayerName + "/" + info.PlayerId + " candidate=" + playerClanInfo.PlayerName + "/" + playerClanInfo.PlayerId)); WarfareRPC.Instance?.RequestCastVote(playerClanInfo.PlayerId); } if ((j + 1) % 6 == 0 && j + 1 < clanMembers.Count) { GUILayout.EndHorizontal(); GUILayout.BeginHorizontal(Array.Empty()); } } GUILayout.EndHorizontal(); LogElectionUi(data, clan, flag, clanMemberCount, votes, required, electionVoteTotals); GUILayout.EndVertical(); GUILayout.EndVertical(); } private bool IsElectionActive(Clan clan, long now) { if (clan == null || clan.NextElectionTimestamp <= 0) { return false; } long num = (long)Math.Max(1, ClanConfig.JarlElectionDurationHours?.Value ?? 72) * 3600L; if (now >= clan.NextElectionTimestamp - num) { return now < clan.NextElectionTimestamp; } return false; } private int RequiredElectionTurnout(int eligible) { if (eligible <= 0) { return 0; } int num = Math.Max(0, Math.Min(100, ClanConfig.JarlElectionMinimumTurnoutPercent?.Value ?? 50)); return Math.Max(1, (int)Math.Ceiling((double)eligible * (double)num / 100.0)); } private int CountValidElectionVotes(WarfareData data, Clan clan) { int num = 0; if (data == null || clan == null || clan.ElectionVotes == null) { return num; } foreach (KeyValuePair electionVote in clan.ElectionVotes) { PlayerClanInfo player = data.GetPlayer(electionVote.Key); PlayerClanInfo player2 = data.GetPlayer(electionVote.Value); if (player != null && player.ClanId == clan.ClanId && player2 != null && player2.ClanId == clan.ClanId) { num++; } } return num; } private List> GetElectionVoteTotals(WarfareData data, Clan clan) { Dictionary dictionary = new Dictionary(); if (data == null || clan == null || clan.ElectionVotes == null) { return new List>(); } foreach (KeyValuePair electionVote in clan.ElectionVotes) { PlayerClanInfo player = data.GetPlayer(electionVote.Key); PlayerClanInfo player2 = data.GetPlayer(electionVote.Value); if (player != null && player.ClanId == clan.ClanId && player2 != null && player2.ClanId == clan.ClanId) { dictionary[electionVote.Value] = (dictionary.TryGetValue(electionVote.Value, out var value) ? value : 0) + 1; } } List> list = new List>(dictionary); list.Sort((KeyValuePair a, KeyValuePair b) => b.Value.CompareTo(a.Value)); return list; } private PlayerClanInfo GetElectionLeader(WarfareData data, Clan clan, out int votes, out bool tied) { votes = 0; tied = false; List> electionVoteTotals = GetElectionVoteTotals(data, clan); if (electionVoteTotals.Count == 0) { return null; } votes = electionVoteTotals[0].Value; PlayerClanInfo player = data.GetPlayer(electionVoteTotals[0].Key); tied = electionVoteTotals.Count > 1 && electionVoteTotals[1].Value == votes; return player; } private void LogElectionUi(WarfareData data, Clan clan, bool active, int eligible, int votes, int required, List> totals) { if (!(Time.time < _nextElectionDebugLog)) { _nextElectionDebugLog = Time.time + 10f; Plugin.Log.LogInfo((object)("FCW Election UI: active=" + active + " eligible=" + eligible + " votes=" + votes + " required=" + required)); for (int i = 0; i < totals.Count; i++) { PlayerClanInfo player = data.GetPlayer(totals[i].Key); Plugin.Log.LogInfo((object)("FCW Election UI: candidate=" + (player?.PlayerName ?? totals[i].Key.ToString()) + " votes=" + totals[i].Value)); } } } private void DrawClanTaxSettings(PlayerClanInfo info, Clan clan) { if (clan == null) { return; } ConfigEntry enableClanMemberTax = ClanConfig.EnableClanMemberTax; if (enableClanMemberTax != null && enableClanMemberTax.Value) { GUILayout.BeginVertical(_boxStyle, Array.Empty()); GUILayout.Label("CLAN TAX SETTINGS", _labelGoldStyle, Array.Empty()); bool flag = info != null && info.Rank == ClanRank.Jarl; DrawTaxRow("Resource Tax", clan.ResourceTaxPercent, clan.CoinTaxPercent, resource: true, flag); DrawTaxRow("Coin Tax", clan.CoinTaxPercent, clan.ResourceTaxPercent, resource: false, flag); if (!flag) { GUILayout.Label("Only the Jarl can edit clan taxes.", _labelStyle, Array.Empty()); } GUILayout.EndVertical(); } } private void DrawTaxRow(string label, int current, int other, bool resource, bool editable) { GUILayout.BeginHorizontal(Array.Empty()); GUILayout.Label($"{label}: {current}%", _labelStyle, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(180f) }); if (!editable) { GUILayout.EndHorizontal(); return; } int num = ClanConfig.MaxClanTaxPercent?.Value ?? 50; int[] array = new int[6] { 0, 10, 20, 30, 40, 50 }; for (int i = 0; i < array.Length; i++) { int num2 = array[i]; if (num2 > num) { continue; } bool flag = num2 == current; if (GUILayout.Button(flag ? $"[{num2}]" : num2.ToString(), flag ? _tabActiveStyle : _smallButtonStyle, (GUILayoutOption[])(object)new GUILayoutOption[2] { GUILayout.Width(52f), GUILayout.Height(34f) })) { if (resource) { WarfareRPC.Instance?.RequestSetClanTax(num2, other); } else { WarfareRPC.Instance?.RequestSetClanTax(other, num2); } } } GUILayout.EndHorizontal(); } private void DrawZoneActions(WarfareData data, PlayerClanInfo info, ClanTerritory t, Vector2i grid) { //IL_0087: Unknown result type (might be due to invalid IL or missing references) //IL_018a: 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_0214: Unknown result type (might be due to invalid IL or missing references) bool flag = t == null || (t.OwnerClanId == 0 && t.FreebornOwnerId == 0); bool flag2 = t?.IsRotted ?? false; bool flag3 = t != null && t.OwnerClanId != 0 && t.OwnerClanId != info.ClanId; bool flag4 = t != null && t.FreebornOwnerId != 0L && t.OdinsPeaceActive; bool flag5 = info != null && info.IsFreeborn && info.ClanId == 0; bool flag6 = flag5 || RoleUtils.HasRole(info, ClanRole.Explorer); if (ProtectedWorldZones.TryGetProtectedZone(grid, out var info2)) { GUILayout.Label("" + info2.Name + " - Protected World Zone", _labelStyle, Array.Empty()); GUILayout.Label("Owner: WORLD. Cannot be claimed, captured, sold, or transferred.", _labelStyle, Array.Empty()); return; } if (flag && !flag5 && info != null && info.ClanId != 0) { GUILayout.Label("Next Territory Cost: " + Economy.TerritoryAcquisitionCost(data.GetClanTerritoryCount(info.ClanId) + 1, info) + " coins from Clan Treasury", _labelStyle, Array.Empty()); } if (((flag && flag6) || flag2) && GUILayout.Button(flag5 ? ((GetFreebornTerritories(data, info.PlayerId, TerritoryType.Homestead).Count == 0) ? "Claim Homestead" : "Claim Outpost") : (flag2 ? "Seize this rotted territory" : "Claim this territory"), _buttonStyle, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Height(38f) })) { WarfareRPC.Instance?.RequestClaimTerritory(grid); } if (flag3 && !flag2 && RoleUtils.HasRole(info, ClanRole.Raider)) { if (info.CanCrackVault(ClanConfig.VaultCrackCooldownHours?.Value ?? 6)) { if (GUILayout.Button($"Start Vault Crack ({ClanConfig.VaultCrackDurationMinutes?.Value ?? 30} min)", _buttonStyle, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Height(38f) })) { WarfareRPC.Instance?.RequestStartVaultCrack(grid); } } else { GUILayout.Label("Vault crack on cooldown.", _labelStyle, Array.Empty()); } } if (flag4 && info.ClanId != 0 && info.Rank >= ClanRank.Officer && GUILayout.Button("Challenge Odin's Peace", _buttonStyle, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Height(38f) })) { WarfareRPC.Instance?.RequestChallengeFreeborn(grid); } } private void DrawMembers(WarfareData data, PlayerClanInfo info, Clan clan) { //IL_0033: Unknown result type (might be due to invalid IL or missing references) //IL_0038: Unknown result type (might be due to invalid IL or missing references) //IL_0040: Unknown result type (might be due to invalid IL or missing references) //IL_02f2: Unknown result type (might be due to invalid IL or missing references) //IL_030d: Unknown result type (might be due to invalid IL or missing references) //IL_0312: Unknown result type (might be due to invalid IL or missing references) //IL_0416: Unknown result type (might be due to invalid IL or missing references) //IL_069f: Unknown result type (might be due to invalid IL or missing references) HashSet online = new HashSet(); if ((Object)(object)ZNet.instance != (Object)null) { foreach (PlayerInfo player in ZNet.instance.GetPlayerList()) { online.Add(player.m_name); } } List clanMembers = data.GetClanMembers(info.ClanId); clanMembers.Sort(delegate(PlayerClanInfo a, PlayerClanInfo b) { bool flag4 = online.Contains(a.PlayerName); bool flag5 = online.Contains(b.PlayerName); if (flag4 != flag5) { return flag5.CompareTo(flag4); } return (a.Rank != b.Rank) ? b.Rank.CompareTo(a.Rank) : string.Compare(a.PlayerName, b.PlayerName, StringComparison.OrdinalIgnoreCase); }); long num = ((clan != null) ? (clan.NextElectionTimestamp - WarfareManager.Now()) : 0); GUILayout.Label($"Roster - {clanMembers.Count} members Next election: " + ((num > 0) ? $"{num / 86400}d {num % 86400 / 3600}h" : "soon"), _labelGoldStyle, Array.Empty()); bool flag = info.Rank >= ClanRank.Officer; bool flag2 = info.Rank == ClanRank.Jarl; int num2 = 0; int num3 = 0; foreach (PlayerClanInfo item in clanMembers) { if (online.Contains(item.PlayerName)) { num2++; } if (item.Rank >= ClanRank.Officer) { num3++; } } GUILayout.BeginHorizontal(Array.Empty()); DrawSummaryCard("Online", "" + num2 + "/" + clanMembers.Count, 180f); DrawSummaryCard("Officers", num3.ToString(), 180f); DrawSummaryCard("Jarl", (clan == null) ? "Unknown" : (data.GetPlayer(clan.JarlId)?.PlayerName ?? "Unknown"), 260f); DrawSummaryCard("Vault Access", flag2 ? "Editable" : "View only", 200f); GUILayout.EndHorizontal(); GUILayout.BeginHorizontal(_boxStyle, Array.Empty()); DrawRosterHeaderCell("Status", "", 54f); DrawRosterHeaderCell("Name", "", 140f); DrawRosterHeaderCell("Rank", "", 52f); DrawRosterHeaderCell("Primary", "", 70f); DrawRosterHeaderCell("Secondary", "", 82f); DrawRosterHeaderCell("Tertiary", "", 70f); DrawRosterHeaderCell("Claim", "Territory", 64f); DrawRosterVaultHeader(240f); DrawRosterHeaderCell("Vote / Actions", "", 240f); GUILayout.EndHorizontal(); _scrollA = GUILayout.BeginScrollView(_scrollA, false, false, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.ExpandHeight(true) }); foreach (PlayerClanInfo item2 in clanMembers) { bool num4 = online.Contains(item2.PlayerName); bool flag3 = clan != null && clan.JarlId == item2.PlayerId; GUILayout.BeginHorizontal(_boxStyle, Array.Empty()); GUILayout.Label(num4 ? "Online" : "Offline", _labelStyle, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(54f) }); if (GUILayout.Button((item2.PlayerName ?? "Unknown") + ((item2.Bounty > 0) ? (" [" + item2.Bounty + "c]") : ""), _smallButtonStyle, (GUILayoutOption[])(object)new GUILayoutOption[2] { GUILayout.Width(140f), GUILayout.Height(26f) })) { OpenPublicProfile(item2, "Members", 2, _membersSubTab, _scrollA); GUIUtility.ExitGUI(); } GUILayout.Label((flag3 ? "Jarl" : item2.Rank.ToString()) ?? "", _labelStyle, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(52f) }); GUILayout.Label(RoleName(item2.PrimaryRole), _labelStyle, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(70f) }); GUILayout.Label(RoleName(item2.SecondaryRole), _labelStyle, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(82f) }); GUILayout.Label(RoleName(item2.TertiaryRole), _labelStyle, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(70f) }); ClaimPermissionButton(item2, flag2); if (flag2) { WarfareManager.Instance?.EnsureVaultPermissions(item2); GUILayout.BeginHorizontal((GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(240f) }); GUILayout.Label("D:", _labelStyle, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(20f) }); VaultPermissionButton(item2, 3, "C", item2.CanDepositVaultCoins || item2.Rank == ClanRank.Jarl); VaultPermissionButton(item2, 1, "I", item2.CanDepositVaultItems || item2.Rank == ClanRank.Jarl); GUILayout.Label("W:", _labelStyle, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(22f) }); VaultPermissionButton(item2, 4, "C", item2.CanWithdrawVaultCoins || item2.Rank == ClanRank.Jarl); VaultPermissionButton(item2, 2, "I", item2.CanWithdrawVaultItems || item2.Rank == ClanRank.Jarl); GUILayout.EndHorizontal(); } else { GUILayout.Label("-", _labelStyle, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(240f) }); } GUILayout.BeginHorizontal((GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(240f) }); if (GUILayout.Button("Profile", _smallButtonStyle, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(72f) })) { OpenPublicProfile(item2, "Members", 2, _membersSubTab, _scrollA); GUIUtility.ExitGUI(); } if (GUILayout.Button("Vote", _smallButtonStyle, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(44f) })) { WarfareRPC.Instance?.RequestCastVote(item2.PlayerId); } if (flag2 && item2.PlayerId != info.PlayerId) { if (item2.Rank == ClanRank.Member && GUILayout.Button("Promote", _smallButtonStyle, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(64f) })) { WarfareRPC.Instance?.RequestSetRank(item2.PlayerId, ClanRank.Officer); } else if (item2.Rank == ClanRank.Officer && GUILayout.Button("Demote", _smallButtonStyle, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(64f) })) { WarfareRPC.Instance?.RequestSetRank(item2.PlayerId, ClanRank.Member); } } if (flag && item2.PlayerId != info.PlayerId && item2.Rank < info.Rank && GUILayout.Button("Kick", _smallButtonStyle, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(44f) })) { WarfareRPC.Instance?.RequestKickMember(item2.PlayerId); } GUILayout.FlexibleSpace(); GUILayout.EndHorizontal(); GUILayout.EndHorizontal(); } GUILayout.EndScrollView(); GUILayout.BeginHorizontal(Array.Empty()); if (GUILayout.Button("Leave Clan", _dangerButtonStyle, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(140f) })) { WarfareRPC.Instance?.RequestLeaveClan(); } if (flag2 && GUILayout.Button("Disband Clan", _dangerButtonStyle, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(150f) })) { WarfareRPC.Instance?.RequestDisbandClan(); } GUILayout.EndHorizontal(); } private void DrawRosterHeaderCell(string title, string subtitle, float width) { //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_0013: Expected O, but got Unknown GUIStyle val = new GUIStyle(_labelGoldStyle) { wordWrap = false }; GUILayout.BeginVertical((GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(width) }); GUILayout.Label(title, val, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Height(22f) }); GUILayout.Label(subtitle ?? "", _labelStyle, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Height(21f) }); GUILayout.EndVertical(); } private void DrawRosterVaultHeader(float width) { //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_0012: Unknown result type (might be due to invalid IL or missing references) //IL_001a: Expected O, but got Unknown //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_002c: Unknown result type (might be due to invalid IL or missing references) //IL_0034: Expected O, but got Unknown GUIStyle val = new GUIStyle(_labelGoldStyle) { wordWrap = false, alignment = (TextAnchor)4 }; GUIStyle val2 = new GUIStyle(_labelStyle) { wordWrap = false, alignment = (TextAnchor)4 }; GUILayout.BeginVertical((GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(width) }); GUILayout.Label("Vault Rights", val, (GUILayoutOption[])(object)new GUILayoutOption[2] { GUILayout.Width(218f), GUILayout.Height(22f) }); GUILayout.BeginHorizontal(Array.Empty()); GUILayout.Space(20f); GUILayout.Label("Deposit", val, (GUILayoutOption[])(object)new GUILayoutOption[2] { GUILayout.Width(88f), GUILayout.Height(21f) }); GUILayout.Space(22f); GUILayout.Label("Withdraw", val, (GUILayoutOption[])(object)new GUILayoutOption[2] { GUILayout.Width(88f), GUILayout.Height(21f) }); GUILayout.EndHorizontal(); GUILayout.BeginHorizontal(Array.Empty()); GUILayout.Space(20f); GUILayout.Label("Coins", val2, (GUILayoutOption[])(object)new GUILayoutOption[2] { GUILayout.Width(44f), GUILayout.Height(17f) }); GUILayout.Label("Items", val2, (GUILayoutOption[])(object)new GUILayoutOption[2] { GUILayout.Width(44f), GUILayout.Height(17f) }); GUILayout.Space(22f); GUILayout.Label("Coins", val2, (GUILayoutOption[])(object)new GUILayoutOption[2] { GUILayout.Width(44f), GUILayout.Height(17f) }); GUILayout.Label("Items", val2, (GUILayoutOption[])(object)new GUILayoutOption[2] { GUILayout.Width(44f), GUILayout.Height(17f) }); GUILayout.EndHorizontal(); GUILayout.EndVertical(); } private void ClaimPermissionButton(PlayerClanInfo member, bool editable) { string text = ((member.Rank == ClanRank.Jarl || member.CanClaimTerritory) ? "Y" : "N"); if (editable && member.Rank != ClanRank.Jarl) { if (GUILayout.Button(text, _smallButtonStyle, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(64f) })) { WarfareRPC.Instance?.RequestSetVaultPermission(member.PlayerId, 5, !member.CanClaimTerritory); } } else { GUILayout.Label(text, _labelStyle, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(64f) }); } } private void VaultPermissionButton(PlayerClanInfo member, int permission, string label, bool enabled) { GUI.enabled = member.Rank != ClanRank.Jarl; string text = (enabled ? "Y" : "N"); if (GUILayout.Button(label + " " + text, _smallButtonStyle, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(44f) })) { WarfareRPC.Instance?.RequestSetVaultPermission(member.PlayerId, permission, !enabled); } GUI.enabled = true; } private void DrawBuildings(WarfareData data, PlayerClanInfo info) { //IL_00b0: 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_00d0: Unknown result type (might be due to invalid IL or missing references) //IL_0139: Unknown result type (might be due to invalid IL or missing references) //IL_0154: Unknown result type (might be due to invalid IL or missing references) //IL_0159: Unknown result type (might be due to invalid IL or missing references) List buildingTerritories = GetBuildingTerritories(data, info); if (buildingTerritories.Count == 0) { DrawEmptyState("No Buildings", "No owned territories are available for kingdom buildings."); return; } if (_selectedBuildingTerritoryIndex < 0 || _selectedBuildingTerritoryIndex >= buildingTerritories.Count) { _selectedBuildingTerritoryIndex = 0; } ClanTerritory territory = buildingTerritories[_selectedBuildingTerritoryIndex]; float num = InfrastructureSelectorWidth(); float num2 = Mathf.Max(420f, ((Rect)(ref _windowRect)).height - 365f); GUILayout.BeginHorizontal(Array.Empty()); GUILayout.BeginVertical(_boxStyle, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(num) }); GUILayout.Label("Territory Selection", _labelGoldStyle, Array.Empty()); _buildingsTerritoryScroll = GUILayout.BeginScrollView(_buildingsTerritoryScroll, false, true, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Height(num2) }); for (int i = 0; i < buildingTerritories.Count; i++) { DrawBuildingTerritoryTile(buildingTerritories[i], i, i == _selectedBuildingTerritoryIndex); } GUILayout.EndScrollView(); GUILayout.EndVertical(); GUILayout.Space(8f); GUILayout.BeginVertical(_boxStyle, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.ExpandWidth(true) }); _buildingsDetailScroll = GUILayout.BeginScrollView(_buildingsDetailScroll, false, true, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Height(num2) }); DrawSelectedBuildingTerritory(data, info, territory); GUILayout.EndScrollView(); GUILayout.EndVertical(); GUILayout.EndHorizontal(); } private List GetBuildingTerritories(WarfareData data, PlayerClanInfo info) { List list = new List(); if (info != null && info.IsFreeborn && info.ClanId == 0) { foreach (ClanTerritory value in data.Territories.Values) { if (value.FreebornOwnerId == info.PlayerId) { list.Add(value); } } } else if (info != null && info.ClanId != 0) { list.AddRange(data.GetClanTerritories(info.ClanId)); } list.Sort((ClanTerritory a, ClanTerritory b) => string.Compare(a.DisplayName, b.DisplayName, StringComparison.OrdinalIgnoreCase)); return list; } private void DrawBuildingTerritoryTile(ClanTerritory territory, int index, bool selected) { List list = WarfareManager.EnsureBuildings(territory); GUILayout.BeginHorizontal(selected ? _tabActiveStyle : _boxStyle, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.MinHeight(62f) }); GUILayout.Box((Texture)(object)GetBiomeTexture(territory), _boxStyle, (GUILayoutOption[])(object)new GUILayoutOption[2] { GUILayout.Width(36f), GUILayout.Height(36f) }); GUILayout.BeginVertical((GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.ExpandWidth(true) }); GUILayout.Label("" + territory.DisplayName + "", selected ? _labelGoldStyle : _labelStyle, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.MinHeight(22f) }); GUILayout.Label(BiomeDisplayName(territory) + " | " + territory.Type, _labelStyle, Array.Empty()); GUILayout.Label("Slots: " + list.Count + "/" + WarfareManager.GetBuildingSlots(territory) + "", _labelStyle, Array.Empty()); GUILayout.EndVertical(); if (GUILayout.Button(selected ? "Selected" : "Select", _smallButtonStyle, (GUILayoutOption[])(object)new GUILayoutOption[2] { GUILayout.Width(60f), GUILayout.Height(38f) })) { _selectedBuildingTerritoryIndex = index; GUIUtility.ExitGUI(); } GUILayout.EndHorizontal(); } private void DrawSelectedBuildingTerritory(WarfareData data, PlayerClanInfo info, ClanTerritory territory) { List list = WarfareManager.EnsureBuildings(territory); int buildingSlots = WarfareManager.GetBuildingSlots(territory); int count = GetBuildingTerritories(data, info).Count; EconomyBreakdown economyBreakdown = Economy.CalculateTerritoryEconomy(data, info.ClanId, territory, count); GUILayout.BeginHorizontal((GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.ExpandHeight(false) }); GUILayout.Box((Texture)(object)GetBiomeTexture(territory), _boxStyle, (GUILayoutOption[])(object)new GUILayoutOption[2] { GUILayout.Width(72f), GUILayout.Height(72f) }); GUILayout.BeginVertical((GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.ExpandHeight(false) }); GUILayout.Label("" + territory.DisplayName + "", _labelGoldStyle, Array.Empty()); GUILayout.Label("Biome: " + BiomeDisplayName(territory) + " Type: " + territory.Type.ToString() + "", _labelStyle, Array.Empty()); GUILayout.Label("Gross: +" + economyBreakdown.Gross + "/tick Territory Upkeep: -" + economyBreakdown.TerritoryUpkeep + "/tick Progressive: -" + economyBreakdown.ProgressiveUpkeep + "/tick Net: " + ColoredCoins(economyBreakdown.Net) + "/tick", _labelStyle, Array.Empty()); GUILayout.Label("Territory Level: " + territory.SizeLevel + " Territory Size: " + territory.SizeLevel + " Slots: " + list.Count + "/" + buildingSlots + "", _labelStyle, Array.Empty()); GUILayout.Label("Strategic Value: " + Economy.ValueTierName(territory.BiomeValueTier), _labelStyle, Array.Empty()); GUILayout.Label("Next Production: " + NextBuildingProductionText(territory) + "", _labelStyle, Array.Empty()); GUILayout.Label("Production Status: " + BuildingProductionStatusText(territory) + "", _labelStyle, Array.Empty()); GUILayout.EndVertical(); GUILayout.EndHorizontal(); GUILayout.Space(16f); GUILayout.Label("Building Catalog", _labelGoldStyle, Array.Empty()); DrawBuildTypeSelector(territory); GUILayout.Space(16f); DrawSelectedBuildingPanel(territory, _selectedBuildType); GUILayout.Space(16f); GUILayout.Label("Building Slots", _labelGoldStyle, Array.Empty()); DrawBuildingSlotsGrid(territory, list, buildingSlots); GUILayout.Space(28f); } private void DrawSelectedBuildingPanel(ClanTerritory territory, TerritoryBuildingType type) { //IL_0014: Unknown result type (might be due to invalid IL or missing references) //IL_0019: Unknown result type (might be due to invalid IL or missing references) //IL_0021: Expected O, but got Unknown //IL_0078: Unknown result type (might be due to invalid IL or missing references) //IL_0083: Expected O, but got Unknown if (type != TerritoryBuildingType.None) { bool flag = WarfareManager.IsBuildingAllowedInBiome(territory, type); GUIStyle val = new GUIStyle(_labelStyle) { wordWrap = true }; GUILayout.BeginVertical(_specialPanelStyle, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.ExpandHeight(false) }); GUILayout.Label("Selected Building", CardTitleStyle(UiTone.Special), Array.Empty()); GUILayout.BeginHorizontal((GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.ExpandHeight(false) }); Texture2D buildingTexture = GetBuildingTexture(type); if ((Object)buildingTexture != (Object)null) { GUILayout.Box((Texture)(object)buildingTexture, _boxStyle, (GUILayoutOption[])(object)new GUILayoutOption[2] { GUILayout.Width(250f), GUILayout.Height(140f) }); } GUILayout.BeginVertical((GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.ExpandHeight(false) }); GUILayout.Label("" + WarfareManager.BuildingName(type) + "", CardTitleStyle(UiTone.Special), Array.Empty()); GUILayout.Label("Purpose: " + BuildingPurposeText(type), val, Array.Empty()); GUILayout.Label("Benefit: " + BuildingImplementedEffectText(territory, type), val, Array.Empty()); GUILayout.Label(string.Concat("Cost: " + WarfareManager.BuildingBuildCost(type) + "c Requirement: " + WarfareManager.BuildingRequirementText(type) + " ", flag ? "Available" : "Unavailable"), val, Array.Empty()); GUILayout.EndVertical(); GUILayout.EndHorizontal(); GUILayout.EndVertical(); } } private void DrawBuildingSlotsGrid(ClanTerritory territory, List buildings, int slots) { float availableWidth = InfrastructureDetailWidth() - 34f; int num = ResponsiveColumns(availableWidth, 260f, 3); float num2 = ResponsiveCardWidth(availableWidth, num); for (int i = 0; i < slots; i += num) { GUILayout.BeginHorizontal(Array.Empty()); for (int j = 0; j < num; j++) { int num3 = i + j; if (num3 < slots) { TerritoryBuilding building = ((num3 < buildings.Count) ? buildings[num3] : null); DrawBuildingSlotTile(territory, buildings, building, num3, num2); } else { GUILayout.Space(num2 + 8f); } } GUILayout.EndHorizontal(); GUILayout.Space(6f); } } private void DrawBuildingSlotTile(ClanTerritory territory, List buildings, TerritoryBuilding building, int slot, float tileWidth) { //IL_0335: Unknown result type (might be due to invalid IL or missing references) //IL_0289: Unknown result type (might be due to invalid IL or missing references) //IL_0391: Unknown result type (might be due to invalid IL or missing references) //IL_03ed: Unknown result type (might be due to invalid IL or missing references) //IL_0444: Unknown result type (might be due to invalid IL or missing references) GUILayout.BeginVertical(PanelStyle((building == null) ? UiTone.Info : UiTone.Positive), (GUILayoutOption[])(object)new GUILayoutOption[2] { GUILayout.Width(tileWidth), GUILayout.MinHeight(132f) }); GUILayout.BeginHorizontal(Array.Empty()); GUILayout.Box((Texture)(object)GetBuildingTexture(building), _boxStyle, (GUILayoutOption[])(object)new GUILayoutOption[2] { GUILayout.Width(72f), GUILayout.Height(72f) }); GUILayout.BeginVertical(Array.Empty()); if (building == null) { bool num = WarfareManager.IsBuildingAllowedInBiome(territory, _selectedBuildType); bool flag = CountBuildingsOfType(buildings, _selectedBuildType) >= WarfareManager.BuildingMaxCopies(_selectedBuildType); GUILayout.Label("Slot " + (slot + 1) + " - Empty", _labelGoldStyle, Array.Empty()); GUILayout.Label((!num) ? "Requirement not met" : (flag ? "Copy limit reached" : "Available"), _labelStyle, Array.Empty()); } else { GUILayout.Label("" + WarfareManager.BuildingName(building.Type) + " " + RomanLevel(building.Level) + "", _labelGoldStyle, Array.Empty()); GUILayout.Label("Status: " + BuildingStatusColored(territory, building), _labelStyle, Array.Empty()); GUILayout.Label(BuildingProductionLine(territory, building), _labelStyle, Array.Empty()); GUILayout.Label("Stored: " + BuildingStoredLine(territory, building), _labelStyle, Array.Empty()); GUILayout.Label("Next Production: " + BuildingNextProductionColored(territory, building), _labelStyle, Array.Empty()); } GUILayout.EndVertical(); GUILayout.EndHorizontal(); GUILayout.Space(6f); if (building == null) { bool num2 = WarfareManager.IsBuildingAllowedInBiome(territory, _selectedBuildType); bool flag2 = CountBuildingsOfType(buildings, _selectedBuildType) >= WarfareManager.BuildingMaxCopies(_selectedBuildType); GUI.enabled = num2 && !flag2; if (GUILayout.Button("Build (" + WarfareManager.BuildingBuildCost(_selectedBuildType) + "c)", GUI.enabled ? _primaryButtonStyle : _disabledButtonStyle, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Height(38f) })) { WarfareRPC.Instance?.RequestTerritoryBuildingAction(territory.GridPosition, 0, _selectedBuildType, slot); } GUI.enabled = true; } else { GUILayout.BeginHorizontal(Array.Empty()); GUI.enabled = building.Level < WarfareManager.BuildingMaxLevel(building.Type); if (GUILayout.Button((building.Level < WarfareManager.BuildingMaxLevel(building.Type)) ? "Upgrade" : "Max", GUI.enabled ? _primaryButtonStyle : _disabledButtonStyle, (GUILayoutOption[])(object)new GUILayoutOption[2] { GUILayout.Width(82f), GUILayout.Height(38f) })) { WarfareRPC.Instance?.RequestTerritoryBuildingAction(territory.GridPosition, 1, TerritoryBuildingType.None, slot); } GUI.enabled = building.Damaged; if (GUILayout.Button("Repair", _smallButtonStyle, (GUILayoutOption[])(object)new GUILayoutOption[2] { GUILayout.Width(82f), GUILayout.Height(38f) })) { WarfareRPC.Instance?.RequestTerritoryBuildingAction(territory.GridPosition, 2, TerritoryBuildingType.None, slot); } GUI.enabled = WarfareManager.HasStoredBuildingOutput(building); if (GUILayout.Button("Claim", _smallButtonStyle, (GUILayoutOption[])(object)new GUILayoutOption[2] { GUILayout.Width(82f), GUILayout.Height(38f) })) { WarfareRPC.Instance?.RequestTerritoryBuildingAction(territory.GridPosition, 4, TerritoryBuildingType.None, slot); } GUI.enabled = true; if (GUILayout.Button("Destroy", _dangerButtonStyle, (GUILayoutOption[])(object)new GUILayoutOption[2] { GUILayout.Width(82f), GUILayout.Height(38f) })) { WarfareRPC.Instance?.RequestTerritoryBuildingAction(territory.GridPosition, 3, TerritoryBuildingType.None, slot); } GUILayout.EndHorizontal(); GUILayout.Label(BuildingUpgradePreviewText(building), _labelStyle, Array.Empty()); } GUILayout.EndVertical(); } private void DrawBuildTypeSelector(ClanTerritory territory) { string[] buildingCategoryNames = WarfareManager.GetBuildingCategoryNames(); float availableWidth = InfrastructureDetailWidth() - 90f; int num = 4; float num2 = ResponsiveCardWidth(availableWidth, num); for (int i = 0; i < 2; i++) { GUILayout.BeginHorizontal((GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.ExpandHeight(false) }); for (int j = 0; j < num; j++) { int num3 = i * num + j; if (num3 >= buildingCategoryNames.Length) { GUILayout.Space(num2 + 8f); } else if (GUILayout.Button(((_selectedBuildingCategory == num3) ? "[" : "") + buildingCategoryNames[num3] + ((_selectedBuildingCategory == num3) ? "]" : ""), _smallButtonStyle, (GUILayoutOption[])(object)new GUILayoutOption[2] { GUILayout.Width(num2), GUILayout.Height(34f) })) { _selectedBuildingCategory = num3; TerritoryBuildingType[] buildingCategoryTypes = WarfareManager.GetBuildingCategoryTypes(num3); _selectedBuildType = ((buildingCategoryTypes.Length != 0) ? buildingCategoryTypes[0] : TerritoryBuildingType.None); } } GUILayout.EndHorizontal(); if (i == 0) { GUILayout.Space(5f); } } GUILayout.Space(8f); TerritoryBuildingType[] buildingCategoryTypes2 = WarfareManager.GetBuildingCategoryTypes(_selectedBuildingCategory); if (buildingCategoryTypes2.Length == 0) { GUILayout.BeginVertical(_infoPanelStyle, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.ExpandHeight(false) }); GUILayout.Label("No buildings are available in this category.", _labelStyle, Array.Empty()); GUILayout.EndVertical(); return; } int num4 = ResponsiveColumns(availableWidth, 230f, 3); float num5 = ResponsiveCardWidth(availableWidth, num4); int num6 = Mathf.CeilToInt((float)buildingCategoryTypes2.Length / (float)num4); for (int k = 0; k < num6; k++) { float num7 = BuildingCatalogRowHeight(territory, buildingCategoryTypes2, k * num4, num4, num5); GUILayout.BeginHorizontal((GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Height(num7) }); for (int l = 0; l < num4; l++) { int num8 = k * num4 + l; if (num8 >= buildingCategoryTypes2.Length) { GUILayout.Space(num5 + 8f); continue; } TerritoryBuildingType territoryBuildingType = buildingCategoryTypes2[num8]; if (DrawBuildTypeIconButton(territory, territoryBuildingType, num5, num7)) { _selectedBuildType = territoryBuildingType; } } GUILayout.EndHorizontal(); if (k + 1 < num6) { GUILayout.Space(8f); } } } private float BuildingCatalogRowHeight(ClanTerritory territory, TerritoryBuildingType[] types, int startIndex, int columns, float width) { //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_0012: Unknown result type (might be due to invalid IL or missing references) //IL_001b: Expected O, but got Unknown //IL_00e4: Unknown result type (might be due to invalid IL or missing references) //IL_00ef: Expected O, but got Unknown //IL_00fa: Unknown result type (might be due to invalid IL or missing references) //IL_0105: Expected O, but got Unknown //IL_0109: Unknown result type (might be due to invalid IL or missing references) //IL_0114: Expected O, but got Unknown GUIStyle val = new GUIStyle(_labelStyle) { wordWrap = true, fontSize = 11 }; float num = Mathf.Max(120f, width - 18f); float num2 = 300f; string text = WarfareManager.GetBuildingCategoryNames()[Mathf.Clamp(_selectedBuildingCategory, 0, WarfareManager.GetBuildingCategoryNames().Length - 1)]; for (int i = 0; i < columns; i++) { int num3 = startIndex + i; if (num3 >= types.Length) { break; } TerritoryBuildingType type = types[num3]; bool flag = WarfareManager.IsBuildingAllowedInBiome(territory, type); string text2 = "Category: " + text + " Cost: " + WarfareManager.BuildingBuildCost(type) + "c"; string text3 = "Requirement: " + WarfareManager.BuildingRequirementText(type) + " " + (flag ? "Available" : "Unavailable: requirement not met"); float num4 = 226f + val.CalcHeight(new GUIContent(text2), num) + val.CalcHeight(new GUIContent(BuildingCatalogBenefitText(territory, type)), num) + val.CalcHeight(new GUIContent(text3), num); num2 = Mathf.Max(num2, num4); } return Mathf.Ceil(num2); } private bool DrawBuildTypeIconButton(ClanTerritory territory, TerritoryBuildingType type, float width, float height) { //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_0024: Unknown result type (might be due to invalid IL or missing references) //IL_002d: Expected O, but got Unknown //IL_006a: Unknown result type (might be due to invalid IL or missing references) //IL_0075: Expected O, but got Unknown bool flag = _selectedBuildType == type; bool flag2 = WarfareManager.IsBuildingAllowedInBiome(territory, type); GUIStyle val = new GUIStyle(_labelStyle) { wordWrap = true, fontSize = 11 }; GUILayout.BeginVertical(flag ? _specialPanelStyle : _boxStyle, (GUILayoutOption[])(object)new GUILayoutOption[2] { GUILayout.Width(width), GUILayout.Height(height) }); Texture2D buildingTexture = GetBuildingTexture(type); if ((Object)buildingTexture != (Object)null) { GUILayout.Box((Texture)(object)buildingTexture, _boxStyle, (GUILayoutOption[])(object)new GUILayoutOption[2] { GUILayout.Width(Mathf.Max(120f, width - 10f)), GUILayout.Height(150f) }); } GUILayout.Label("" + WarfareManager.BuildingName(type) + "", flag ? CardTitleStyle(UiTone.Special) : _labelGoldStyle, Array.Empty()); string text = WarfareManager.GetBuildingCategoryNames()[Mathf.Clamp(_selectedBuildingCategory, 0, WarfareManager.GetBuildingCategoryNames().Length - 1)]; GUILayout.Label("Category: " + text + " Cost: " + WarfareManager.BuildingBuildCost(type) + "c", val, Array.Empty()); GUILayout.Label(BuildingCatalogBenefitText(territory, type), val, Array.Empty()); GUILayout.Label("Requirement: " + WarfareManager.BuildingRequirementText(type) + " " + (flag2 ? "Available" : "Unavailable: requirement not met"), val, Array.Empty()); bool result = GUILayout.Button(flag ? "Selected" : "Select", flag ? _specialPanelStyle : _smallButtonStyle, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Height(34f) }); GUILayout.EndVertical(); return result; } private string BuildingPurposeText(TerritoryBuildingType type) { if (WarfareManager.IsCoinBuilding(type)) { return "Periodic coin production."; } if (WarfareManager.IsResourceBuilding(type)) { return "Periodic resource production."; } return type switch { TerritoryBuildingType.TownCenter => "Settlement prerequisite for advanced buildings.", TerritoryBuildingType.ClanHall => "Capital building-slot support.", TerritoryBuildingType.Longhouse => "Local building-slot support from level 3.", TerritoryBuildingType.Barracks => "Passive territory-upkeep reduction.", TerritoryBuildingType.Watchtower => "Passive territory-upkeep reduction.", _ => "Specialized territory building.", }; } private string BuildingImplementedEffectText(ClanTerritory territory, TerritoryBuildingType type) { TerritoryBuilding building = new TerritoryBuilding { Type = type, Level = 1 }; if (WarfareManager.IsCoinBuilding(type) || WarfareManager.IsResourceBuilding(type)) { return WarfareManager.BuildingProductionText(territory, building) + "; stored output must be claimed when storage fills."; } return type switch { TerritoryBuildingType.TownCenter => "Unlocks building types whose existing requirement is Town Center.", TerritoryBuildingType.ClanHall => "Adds one building slot when present in a Capital.", TerritoryBuildingType.Longhouse => "Adds one local building slot at level 3 or above.", TerritoryBuildingType.Barracks => "Reduces this territory's upkeep by 2% per level, within the existing 25% combined cap.", TerritoryBuildingType.Watchtower => "Reduces this territory's upkeep by 3% per level, within the existing 25% combined cap.", _ => "No active gameplay effect is currently assigned to this building.", }; } private string BuildingCatalogBenefitText(ClanTerritory territory, TerritoryBuildingType type) { TerritoryBuilding building = new TerritoryBuilding { Type = type, Level = 1 }; if (WarfareManager.IsCoinBuilding(type) || WarfareManager.IsResourceBuilding(type)) { return "Benefit: " + WarfareManager.BuildingProductionText(territory, building); } return "Benefit: " + BuildingImplementedEffectText(territory, type); } private string BuildingFullDescriptionText(ClanTerritory territory, TerritoryBuildingType type) { string text = BuildingImplementedEffectText(territory, type); if (text.StartsWith("No active", StringComparison.Ordinal)) { return text + " It can be constructed under the existing requirement, but no planned benefit is presented as active."; } return text; } private string BuildingUnavailableReason(ClanTerritory territory, TerritoryBuildingType type) { return "requires " + WarfareManager.BuildingRequirementText(type) + "; current territory is " + territory.Type.ToString() + " in " + BiomeDisplayName(territory) + "."; } private int FindBuildingCategory(TerritoryBuildingType type) { string[] buildingCategoryNames = WarfareManager.GetBuildingCategoryNames(); for (int i = 0; i < buildingCategoryNames.Length; i++) { TerritoryBuildingType[] buildingCategoryTypes = WarfareManager.GetBuildingCategoryTypes(i); for (int j = 0; j < buildingCategoryTypes.Length; j++) { if (buildingCategoryTypes[j] == type) { return i; } } } return 0; } private void DrawBuildingSlot(ClanTerritory territory, TerritoryBuilding building, int slot) { //IL_02d9: Unknown result type (might be due to invalid IL or missing references) //IL_0247: Unknown result type (might be due to invalid IL or missing references) //IL_0334: Unknown result type (might be due to invalid IL or missing references) //IL_038a: Unknown result type (might be due to invalid IL or missing references) List buildings = WarfareManager.EnsureBuildings(territory); GUILayout.BeginHorizontal(_boxStyle, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Height(58f) }); GUILayout.Box((Texture)(object)GetBuildingTexture(building), _boxStyle, (GUILayoutOption[])(object)new GUILayoutOption[2] { GUILayout.Width(36f), GUILayout.Height(36f) }); GUILayout.BeginVertical((GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(230f) }); if (building == null) { bool num = WarfareManager.IsBuildingAllowedInBiome(territory, _selectedBuildType); bool flag = CountBuildingsOfType(buildings, _selectedBuildType) >= WarfareManager.BuildingMaxCopies(_selectedBuildType); GUILayout.Label("EMPTY SLOT", _labelGoldStyle, Array.Empty()); GUILayout.Label("No building constructed.", _labelStyle, Array.Empty()); GUILayout.Label((!num) ? "Biome requirement not met" : (flag ? "Copy limit reached" : ("Ready to build " + WarfareManager.BuildingName(_selectedBuildType))), _labelStyle, Array.Empty()); } else { GUILayout.Label("" + WarfareManager.BuildingName(building.Type) + " Lv. " + building.Level, _labelGoldStyle, Array.Empty()); GUILayout.Label(building.Damaged ? "Damaged" : "Operational", _labelStyle, Array.Empty()); GUILayout.Label(WarfareManager.BuildingProductionText(territory, building), _labelStyle, Array.Empty()); } GUILayout.EndVertical(); GUILayout.FlexibleSpace(); GUILayout.BeginVertical((GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(270f) }); if (building == null) { bool num2 = WarfareManager.IsBuildingAllowedInBiome(territory, _selectedBuildType); bool flag2 = CountBuildingsOfType(buildings, _selectedBuildType) >= WarfareManager.BuildingMaxCopies(_selectedBuildType); GUI.enabled = num2 && !flag2; if (GUILayout.Button("Build (" + WarfareManager.BuildingBuildCost(_selectedBuildType) + "c)", _smallButtonStyle, (GUILayoutOption[])(object)new GUILayoutOption[2] { GUILayout.Width(250f), GUILayout.Height(38f) })) { WarfareRPC.Instance?.RequestTerritoryBuildingAction(territory.GridPosition, 0, _selectedBuildType, slot); } GUI.enabled = true; } else { GUILayout.BeginHorizontal(Array.Empty()); GUI.enabled = building.Level < 5; if (GUILayout.Button((building.Level < WarfareManager.BuildingMaxLevel(building.Type)) ? "Upgrade" : "Max", _smallButtonStyle, (GUILayoutOption[])(object)new GUILayoutOption[2] { GUILayout.Width(82f), GUILayout.Height(38f) })) { WarfareRPC.Instance?.RequestTerritoryBuildingAction(territory.GridPosition, 1, TerritoryBuildingType.None, slot); } GUI.enabled = building.Damaged; if (GUILayout.Button("Repair", _smallButtonStyle, (GUILayoutOption[])(object)new GUILayoutOption[2] { GUILayout.Width(82f), GUILayout.Height(38f) })) { WarfareRPC.Instance?.RequestTerritoryBuildingAction(territory.GridPosition, 2, TerritoryBuildingType.None, slot); } GUI.enabled = true; if (GUILayout.Button("Destroy", _smallButtonStyle, (GUILayoutOption[])(object)new GUILayoutOption[2] { GUILayout.Width(82f), GUILayout.Height(38f) })) { WarfareRPC.Instance?.RequestTerritoryBuildingAction(territory.GridPosition, 3, TerritoryBuildingType.None, slot); } GUILayout.EndHorizontal(); GUILayout.Label(BuildingUpgradePreviewText(building), _labelStyle, Array.Empty()); } GUILayout.EndVertical(); GUILayout.EndHorizontal(); } private string RomanLevel(int level) { return Mathf.Clamp(level, 1, 5) switch { 1 => "I", 2 => "II", 3 => "III", 4 => "IV", 5 => "V", _ => "I", }; } private bool BuildingProducesOutput(ClanTerritory territory, TerritoryBuilding building, out string prefab, out string display, out int amount, out long coins) { WarfareManager.GetBuildingOutput(territory, building, out prefab, out display, out amount, out coins); if (coins <= 0) { return amount > 0; } return true; } private string BuildingStatusColored(ClanTerritory territory, TerritoryBuilding building) { if (building == null) { return "No Production"; } if (building.Damaged) { return "Damaged"; } if (!BuildingProducesOutput(territory, building, out var _, out var _, out var _, out var _)) { return "Passive Effect"; } if (building.StoredProductionTicks >= 4) { return "Storage Full"; } return "Producing"; } private string BuildingProductionLine(ClanTerritory territory, TerritoryBuilding building) { if (building != null && building.Damaged) { return "Production: stopped"; } if (BuildingProducesOutput(territory, building, out var _, out var display, out var amount, out var coins)) { string text = ((coins > 0) ? (coins + " Coins") : (amount + " " + display)); return "Production: " + text + " / 12h"; } string text2 = WarfareManager.BuildingProductionValueText(territory, building); if (text2 == "No output") { text2 = "No Production"; } return "Passive Effect: " + text2 + ""; } private string BuildingStoredLine(ClanTerritory territory, TerritoryBuilding building) { if (!BuildingProducesOutput(territory, building, out var _, out var display, out var amount, out var coins)) { return "-"; } if (coins > 0) { return "Automatic: paid directly to Treasury each production tick."; } long num = building.StoredAmount; long num2 = (long)amount * 4L; string text = ((building.StoredProductionTicks >= 4) ? "#ff9900" : ((building.StoredProductionTicks > 0) ? "#ffd24a" : "#999999")); return "" + num + " / " + num2 + " " + display + " (" + building.StoredProductionTicks + " / 4 ticks)"; } private void DrawProfileImagePicker(PlayerClanInfo info) { EnsureProfilePortraitIndex(); if (info == null || (info.PlayerId != LocalPlayerId() && (info.LinkedPlayerIds == null || !info.LinkedPlayerIds.Contains(LocalPlayerId())) && !string.Equals(info.SteamId64, WarfareManager.GetLocalSteamId64ForDiagnostics(), StringComparison.Ordinal))) { _profileImageExpanded = false; ClearProfilePageTextures(); GUILayout.BeginVertical(_boxStyle, (GUILayoutOption[])(object)new GUILayoutOption[2] { GUILayout.ExpandWidth(true), GUILayout.Height(32f) }); GUILayout.Label("PROFILE IMAGE", _labelGoldStyle, Array.Empty()); GUILayout.EndVertical(); return; } if (GUILayout.Button("PROFILE IMAGE — " + _profilePortraitCount + " PORTRAITS " + (_profileImageExpanded ? "▲" : "▼"), _profileImageExpanded ? _tabActiveStyle : _smallButtonStyle, (GUILayoutOption[])(object)new GUILayoutOption[2] { GUILayout.ExpandWidth(true), GUILayout.Height(32f) })) { _profileImageExpanded = !_profileImageExpanded; if (_profileImageExpanded) { Plugin.Log.LogInfo((object)("[FCW Profile UI] Portrait gallery opened count=" + _profilePortraitCount + " page=" + (_profileImagePage + 1))); } else { ClearProfilePageTextures(); Plugin.Log.LogInfo((object)"[FCW Profile UI] Portrait gallery closed"); } } if (!_profileImageExpanded) { ClearProfilePageTextures(); return; } List list = _profilePortraitKeys.Where((string key) => !string.Equals(Path.GetFileNameWithoutExtension(key), "default", StringComparison.OrdinalIgnoreCase) && (_profileImageFilter == 0 || (_profileImageFilter == 1 && key.StartsWith("men/", StringComparison.OrdinalIgnoreCase)) || (_profileImageFilter == 2 && key.StartsWith("women/", StringComparison.OrdinalIgnoreCase)))).ToList(); float num = Mathf.Max(780f, AvailableContentWidth() - 292f); float num2 = Mathf.Max(300f, AvailableContentHeight() - 190f); int num3 = Mathf.Clamp(Mathf.FloorToInt((num - 16f) / 78f), 8, 12); int num4 = Mathf.Clamp(Mathf.FloorToInt((num2 - 70f) / 76f), 3, 4); float num5 = Mathf.Clamp((num - 16f - 6f * (float)(num3 - 1)) / (float)num3, 58f, 72f); float num6 = 70f + (float)num4 * (num5 + 6f); GUILayout.BeginVertical(_boxStyle, (GUILayoutOption[])(object)new GUILayoutOption[2] { GUILayout.ExpandWidth(true), GUILayout.Height(num6) }); if (list.Count == 0) { GUILayout.Label("No profile portraits found", _labelStyle, Array.Empty()); GUILayout.EndVertical(); return; } GUILayout.BeginHorizontal(Array.Empty()); string[] array = new string[3] { "All", "Men", "Women" }; for (int num7 = 0; num7 < array.Length; num7++) { if (GUILayout.Button(array[num7], (_profileImageFilter == num7) ? _tabActiveStyle : _smallButtonStyle, (GUILayoutOption[])(object)new GUILayoutOption[2] { GUILayout.Width(72f), GUILayout.Height(24f) })) { _profileImageFilter = num7; _profileImagePage = 0; } } GUILayout.EndHorizontal(); int num8 = num3 * num4; int num9 = Mathf.Max(1, Mathf.CeilToInt((float)list.Count / (float)num8)); _profileImagePage = Mathf.Clamp(_profileImagePage, 0, num9 - 1); EnsureProfilePageTextures(list, _profileImagePage, num9, num8); int num10 = _profileImagePage * num8; for (int num11 = 0; num11 < num4; num11++) { GUILayout.BeginHorizontal((GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.ExpandWidth(true) }); for (int num12 = 0; num12 < num3; num12++) { int num13 = num10 + num11 * num3 + num12; if (num13 >= list.Count) { GUILayout.Space(num5 + 4f); continue; } string text = list[num13]; GUIStyle val = (string.Equals(info.ProfileImageKey, text, StringComparison.OrdinalIgnoreCase) ? _tabActiveStyle : _boxStyle); if (GUILayout.Button((Texture)(object)(_profilePageTextures.TryGetValue(text, out var value) ? value : (_boxTex ?? _pixelTex)), val, (GUILayoutOption[])(object)new GUILayoutOption[2] { GUILayout.Width(num5), GUILayout.Height(num5) })) { WarfareRPC.Instance?.RequestUpdateProfile(info.AboutMe ?? "", info.ActiveTitle ?? "", text); } if (num12 < num3 - 1) { GUILayout.FlexibleSpace(); } } GUILayout.EndHorizontal(); } GUILayout.BeginHorizontal(Array.Empty()); GUI.enabled = _profileImagePage > 0; if (GUILayout.Button("<", _smallButtonStyle, (GUILayoutOption[])(object)new GUILayoutOption[2] { GUILayout.Width(36f), GUILayout.Height(24f) })) { _profileImagePage--; } GUI.enabled = true; GUILayout.Label(_profileImagePage + 1 + " / " + num9, _labelStyle, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(70f) }); GUI.enabled = _profileImagePage < num9 - 1; if (GUILayout.Button(">", _smallButtonStyle, (GUILayoutOption[])(object)new GUILayoutOption[2] { GUILayout.Width(36f), GUILayout.Height(24f) })) { _profileImagePage++; } GUI.enabled = true; GUILayout.EndHorizontal(); GUILayout.EndVertical(); } private void DrawServerBadgeIcons(PlayerClanInfo info) { //IL_005a: Unknown result type (might be due to invalid IL or missing references) //IL_0065: Expected O, but got Unknown List list = WarfareManager.Instance?.GetServerBadges(info) ?? new List(); GUILayout.BeginHorizontal((GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.ExpandWidth(false) }); foreach (string item in list) { Texture2D val = GetConstellationSmallBadgeTexture(item) ?? GetConstellationBadgeTexture(item); if ((Object)val != (Object)null) { GUILayout.Box((Texture)(object)val, _boxStyle, (GUILayoutOption[])(object)new GUILayoutOption[2] { GUILayout.Width(32f), GUILayout.Height(32f) }); } } GUILayout.EndHorizontal(); } private string BuildingNextProductionColored(ClanTerritory territory, TerritoryBuilding building) { string text = BuildingNextProductionText(territory, building); if (building != null && building.Damaged) { return "Production stopped"; } if (text.StartsWith("Storage Full", StringComparison.OrdinalIgnoreCase)) { return "Claim required"; } if (text == "No output") { return "-"; } return "" + text + ""; } private string BuildingTooltipText(TerritoryBuildingType type) { return type switch { TerritoryBuildingType.TownCenter => "Unlocks advanced settlement buildings.", TerritoryBuildingType.ClanHall => "Provides capital administration and slot support.", TerritoryBuildingType.Longhouse => "Provides upkeep and local slot support.", TerritoryBuildingType.Warehouse => "Storage support building.", TerritoryBuildingType.Treasury => "Generates coins every production cycle. Coins must be manually claimed.", TerritoryBuildingType.TradePost => "Generates trade coins every production cycle. Coins must be manually claimed.", TerritoryBuildingType.Marketplace => "Generates coins every production cycle. Coins must be manually claimed.", TerritoryBuildingType.TaxOffice => "Provides passive tax efficiency bonuses.", TerritoryBuildingType.Embassy => "Provides passive diplomacy support.", TerritoryBuildingType.LogisticsPost => "Provides passive upkeep, transfer, and claim support.", TerritoryBuildingType.LumberCamp => "Produces wood every production cycle. Production stops when storage is full.", TerritoryBuildingType.Quarry => "Produces stone every production cycle. Production stops when storage is full.", TerritoryBuildingType.CopperMine => "Produces copper ore every production cycle. Production stops when storage is full.", TerritoryBuildingType.IronMine => "Produces scrap iron every production cycle. Production stops when storage is full.", TerritoryBuildingType.SilverMine => "Produces silver ore every production cycle. Production stops when storage is full.", TerritoryBuildingType.Farm => "Produces biome crops every production cycle. Production stops when storage is full.", TerritoryBuildingType.FishingPort => "Produces fish every production cycle. Production stops when storage is full.", TerritoryBuildingType.HunterLodge => "Produces biome meat or hides every production cycle. Production stops when storage is full.", TerritoryBuildingType.Apiary => "Produces honey every production cycle. Production stops when storage is full.", TerritoryBuildingType.Tannery => "Produces leather materials every production cycle. Production stops when storage is full.", TerritoryBuildingType.Workshop => "Provides passive repair discount.", TerritoryBuildingType.Smithy => "Provides passive metal and military repair discount.", TerritoryBuildingType.Shipyard => "Produces shipbuilding materials every production cycle. Production stops when storage is full.", TerritoryBuildingType.Barracks => "Provides passive military bonuses. Does not produce resources.", TerritoryBuildingType.Watchtower => "Provides passive defense bonuses. Does not produce resources.", TerritoryBuildingType.Shrine => "Provides passive Odin protection bonuses.", TerritoryBuildingType.HealerHut => "Provides passive recovery support.", _ => "No building selected.", }; } private string NextBuildingProductionText(ClanTerritory territory) { List list = WarfareManager.EnsureBuildings(territory); long num = DateTimeOffset.UtcNow.ToUnixTimeSeconds(); long num2 = 0L; int num3 = Mathf.Max(1, ClanConfig.BuildingProductionHours?.Value ?? 12) * 3600; foreach (TerritoryBuilding item in list) { if (item == null || item.Damaged || item.Type == TerritoryBuildingType.None || item.StoredProductionTicks >= 4) { continue; } string text = BuildingNextProductionText(territory, item); if (!(text == "No output") && !text.StartsWith("Storage Full", StringComparison.OrdinalIgnoreCase) && !text.StartsWith("Damaged", StringComparison.OrdinalIgnoreCase)) { long num4 = ((item.LastProductionTimestamp > 0) ? item.LastProductionTimestamp : num) + num3; if (num2 == 0L || num4 < num2) { num2 = num4; } } } if (num2 == 0L) { return "No active production. Claim full storage or build production buildings."; } return FormatDuration(Math.Max(0L, num2 - num)) + " remaining"; } private string BuildingNextProductionText(ClanTerritory territory, TerritoryBuilding building) { if (building == null) { return "No output"; } if (building.Damaged) { return "Damaged - production stopped"; } string text = WarfareManager.BuildingProductionValueText(territory, building); if (text == "No output" || text == "Coming in future update" || text.Contains("support") || text.Contains("bonuses") || text.Contains("discount") || text.Contains("efficiency") || text.StartsWith("Defense", StringComparison.OrdinalIgnoreCase) || text.StartsWith("Unlocks", StringComparison.OrdinalIgnoreCase)) { return "No output"; } if (building.StoredProductionTicks >= 4) { return "Storage Full - Claim required"; } int num = Mathf.Max(1, ClanConfig.BuildingProductionHours?.Value ?? 12) * 3600; long num2 = DateTimeOffset.UtcNow.ToUnixTimeSeconds(); long num3 = ((building.LastProductionTimestamp > 0) ? building.LastProductionTimestamp : num2) + num; return FormatDuration(Math.Max(0L, num3 - num2)); } private string BuildingProductionStatusText(ClanTerritory territory) { List list = WarfareManager.EnsureBuildings(territory); int num = 0; int num2 = 0; List list2 = new List(); foreach (TerritoryBuilding item in list) { if (item == null || item.Type == TerritoryBuildingType.None || item.Damaged) { continue; } string text = BuildingNextProductionText(territory, item); if (text.StartsWith("Storage Full", StringComparison.OrdinalIgnoreCase)) { num2++; } else if (!(text == "No output")) { num++; if (list2.Count < 2) { list2.Add(WarfareManager.BuildingName(item.Type) + " " + WarfareManager.BuildingProductionValueText(territory, item)); } } } if (num == 0 && num2 == 0) { return "Idle"; } if (num == 0) { return "Storage Full - Claim required"; } return "Producing: " + string.Join(", ", list2.ToArray()) + ((num > list2.Count) ? $" +{num - list2.Count} more" : "") + ((num2 > 0) ? $" | {num2} full" : ""); } private string FormatDuration(long seconds) { seconds = Math.Max(0L, seconds); long num = seconds / 3600; long num2 = seconds % 3600 / 60; long num3 = seconds % 60; return num.ToString("00") + ":" + num2.ToString("00") + ":" + num3.ToString("00"); } private string BuildingUpgradePreviewText(TerritoryBuilding building) { if (building == null) { return ""; } if (building.Level >= WarfareManager.BuildingMaxLevel(building.Type)) { return "Building already max level."; } TerritoryBuilding territoryBuilding = new TerritoryBuilding { Type = building.Type, Level = building.Level + 1 }; return WarfareManager.BuildingName(building.Type) + " Lv" + building.Level + " -> Lv" + territoryBuilding.Level + " Cost: " + WarfareManager.BuildingUpgradeCost(building.Type, building.Level) + " Coins\nProduction: " + WarfareManager.BuildingProductionValueText(null, building) + " -> " + WarfareManager.BuildingProductionValueText(null, territoryBuilding); } private void DrawBuildingProductionLog(WarfareData data) { GUILayout.Space(6f); GUILayout.BeginVertical(_boxStyle, Array.Empty()); GUILayout.Label("Production Log", CardTitleStyle(UiTone.Special), Array.Empty()); List list = data.BuildingProductionLog ?? new List(); if (list.Count == 0) { DrawEmptyState("No building production yet.", "Completed production events will appear here."); GUILayout.EndVertical(); return; } int num = Mathf.Min(3, list.Count); for (int i = 0; i < num; i++) { GUILayout.Label(list[i], _labelStyle, Array.Empty()); } GUILayout.EndVertical(); } private int CountBuildingsOfType(List buildings, TerritoryBuildingType type) { int num = 0; foreach (TerritoryBuilding building in buildings) { if (building != null && building.Type == type) { num++; } } return num; } private string BuildingPreviewText(TerritoryBuildingType type) { TerritoryBuilding building = new TerritoryBuilding { Type = type, Level = 1 }; return "Cost: " + WarfareManager.BuildingBuildCost(type) + "c Upgrade: " + WarfareManager.BuildingUpgradeCost(type, 1) + "c\n" + WarfareManager.BuildingProductionText(null, building) + "\nRequirement: " + WarfareManager.BuildingRequirementText(type) + "\nMax level: " + WarfareManager.BuildingMaxLevel(type) + " Max copies: " + WarfareManager.BuildingMaxCopies(type); } private void DrawTerritory(WarfareData data, PlayerClanInfo info, Clan clan) { //IL_0130: Unknown result type (might be due to invalid IL or missing references) //IL_0157: 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) List clanTerritories = data.GetClanTerritories(info.ClanId); if (clanTerritories.Count == 0) { DrawEmptyState("No Territories", "Your clan holds no territory. Explorers can claim land from the map."); return; } bool canManage = info.Rank >= ClanRank.Officer; float num = Economy.GlobalUpkeepMultiplier(clanTerritories.Count); long num2 = 0L; long num3 = 0L; foreach (ClanTerritory item in clanTerritories) { num2 += Economy.TerritoryIncome(item); num3 += Economy.ApplyBuildingUpkeepReduction(item, Economy.ApplyWardenUpkeepReduction(data, info.ClanId, Economy.TerritoryUpkeep(item, num))); } float availableWidth = AvailableContentWidth(); float width = ResponsiveCardWidth(availableWidth, 3); GUILayout.BeginHorizontal(Array.Empty()); DrawSummaryCard("Total Territory Income", "+" + num2 + "/tick", width, UiTone.Info); DrawSummaryCard("Total Territory Upkeep", "-" + num3 + "/tick", width, UiTone.Cost); DrawSummaryCard("Net Territory Value", ColoredCoins(num2 - num3) + "/tick", width, UiTone.Info); GUILayout.EndHorizontal(); _scrollB = GUILayout.BeginScrollView(_scrollB, false, true, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Height(AvailableContentHeight() - 82f) }); int num4 = ResponsiveColumns(availableWidth, 340f, 3); float num5 = ResponsiveCardWidth(availableWidth, num4); for (int i = 0; i < clanTerritories.Count; i += num4) { GUILayout.BeginHorizontal(Array.Empty()); for (int j = 0; j < num4; j++) { int num6 = i + j; if (num6 < clanTerritories.Count) { DrawTerritoryCard(data, info, clanTerritories[num6], canManage, num, num5); } else { GUILayout.Space(num5 + 8f); } } GUILayout.EndHorizontal(); } GUILayout.Space(BottomSafePadding()); GUILayout.EndScrollView(); } private void DrawTerritoryCard(WarfareData data, PlayerClanInfo info, ClanTerritory item, bool canManage, float upkeepMultiplier, float width) { //IL_02b7: Unknown result type (might be due to invalid IL or missing references) //IL_03fd: Unknown result type (might be due to invalid IL or missing references) //IL_046b: Unknown result type (might be due to invalid IL or missing references) //IL_04bb: Unknown result type (might be due to invalid IL or missing references) string text = WarfareData.GridToKey(item.GridX, item.GridY); string text2 = item.Type.ToString(); long num = Economy.TerritoryIncome(item); long num2 = Economy.ApplyBuildingUpkeepReduction(item, Economy.ApplyWardenUpkeepReduction(data, info.ClanId, Economy.TerritoryUpkeep(item, upkeepMultiplier))); long v = num - num2; GUILayout.BeginVertical(_boxStyle, (GUILayoutOption[])(object)new GUILayoutOption[2] { GUILayout.Width(width), GUILayout.MinHeight(220f) }); GUILayout.BeginHorizontal(Array.Empty()); GUILayout.Box((Texture)(object)GetBiomeTexture(item), _boxStyle, (GUILayoutOption[])(object)new GUILayoutOption[2] { GUILayout.Width(76f), GUILayout.Height(76f) }); GUILayout.BeginVertical((GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.ExpandWidth(true) }); GUILayout.Label("" + item.DisplayName + "", _labelGoldStyle, Array.Empty()); GUILayout.Label($"{text2} | {BiomeDisplayName(item)} | {Economy.ValueTierName(item.BiomeValueTier)} value | Size {item.SizeLevel}", _labelStyle, Array.Empty()); GUILayout.Label($"HP: {Mathf.RoundToInt(item.HP)}/{Mathf.RoundToInt(item.MaxHP)} Corruption: {CorruptionColored(item.RotCount, 4)}", _labelStyle, Array.Empty()); GUILayout.Label("Defense Score: " + (WarfareManager.Instance?.TerritoryDefenseScore(text) ?? 0) + "", _labelStyle, Array.Empty()); GUILayout.Label($"Income +{num} Upkeep -{num2} Net {ColoredCoins(v)}", _labelStyle, Array.Empty()); GUILayout.EndVertical(); GUILayout.EndHorizontal(); float num3 = Mathf.Max(96f, (width - 38f) / 2f); GUILayout.BeginHorizontal(Array.Empty()); if (_renameTarget == text) { _renameInput = GUILayout.TextField(_renameInput, 32, (GUILayoutOption[])(object)new GUILayoutOption[2] { GUILayout.Width(Mathf.Max(130f, width - 188f)), GUILayout.Height(34f) }); if (GUILayout.Button("Save", _smallButtonStyle, (GUILayoutOption[])(object)new GUILayoutOption[2] { GUILayout.Width(82f), GUILayout.Height(38f) })) { WarfareRPC.Instance?.RequestRenameTerritory(item.GridPosition, _renameInput); _renameTarget = null; } if (GUILayout.Button("Cancel", _smallButtonStyle, (GUILayoutOption[])(object)new GUILayoutOption[2] { GUILayout.Width(82f), GUILayout.Height(38f) })) { _renameTarget = null; } } else { if (GUILayout.Button("Rename", _smallButtonStyle, (GUILayoutOption[])(object)new GUILayoutOption[2] { GUILayout.Width(num3), GUILayout.Height(38f) })) { _renameTarget = text; _renameInput = item.CustomName ?? ""; } GUI.enabled = canManage; int num4 = ClanConfig.MaxTerritorySizeLevel?.Value ?? 5; if (GUILayout.Button((item.SizeLevel < num4) ? $"Upgrade ({Economy.TerritoryUpgradeCost(item.SizeLevel)}c)" : "Max Size", GUI.enabled ? _primaryButtonStyle : _disabledButtonStyle, (GUILayoutOption[])(object)new GUILayoutOption[2] { GUILayout.Width(num3), GUILayout.Height(38f) }) && item.SizeLevel < num4) { WarfareRPC.Instance?.RequestUpgradeTerritory(item.GridPosition); } GUILayout.EndHorizontal(); GUILayout.BeginHorizontal(Array.Empty()); GUI.enabled = canManage && !item.IsCapital; if (GUILayout.Button("Make Capital", _specialButtonStyle, (GUILayoutOption[])(object)new GUILayoutOption[2] { GUILayout.Width(num3), GUILayout.Height(38f) })) { WarfareRPC.Instance?.RequestSetCapital(item.GridPosition); } GUI.enabled = true; if (GUILayout.Button("Discover (3000c)", _infoButtonStyle, (GUILayoutOption[])(object)new GUILayoutOption[2] { GUILayout.Width(num3), GUILayout.Height(38f) })) { WarfareRPC.Instance?.RequestDiscoverTerritory(item.GridPosition); } GUI.enabled = true; } GUILayout.EndHorizontal(); GUILayout.EndVertical(); } private void DrawEconomy(WarfareData data, PlayerClanInfo info, Clan clan) { if (clan == null) { return; } float availableWidth = AvailableContentWidth(); float width = ResponsiveCardWidth(availableWidth, 3); float num = ResponsiveCardWidth(availableWidth, 3); float num2 = num; List clanTerritories = data.GetClanTerritories(info.ClanId); int count = clanTerritories.Count; float num3 = Economy.GlobalUpkeepMultiplier(count); long num4 = 0L; long num5 = 0L; long num6 = 0L; long num7 = 0L; foreach (ClanTerritory item in clanTerritories) { long num8 = Economy.TerritoryIncome(item); if (item.Type == TerritoryType.Capital) { num4 += num8; } else if (item.Type == TerritoryType.Outpost) { num6 += num8; } else { num5 += num8; } num7 += Economy.ApplyBuildingUpkeepReduction(item, Economy.ApplyWardenUpkeepReduction(data, info.ClanId, Economy.TerritoryUpkeep(item, num3))); } long num9 = num4 + num5 + num6; long num10 = 0L; long num11 = num7 + num10; GUILayout.BeginHorizontal(Array.Empty()); DrawSummaryCard("Gross", "+" + num9 + "/tick", width, UiTone.Info); DrawSummaryCard("Upkeep", "-" + num11 + "/tick", width, UiTone.Cost); DrawSummaryCard("Net", ColoredCoins(num9 - num11) + "/tick", width, UiTone.Info); GUILayout.EndHorizontal(); GUILayout.BeginHorizontal(Array.Empty()); GUILayout.BeginVertical(_infoPanelStyle, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(num) }); GUILayout.Label("Income Sources", CardTitleStyle(UiTone.Info), Array.Empty()); DrawMiniStat("Capital", "+" + num4 + "/tick"); DrawMiniStat("Colonies", "+" + num5 + "/tick"); DrawMiniStat("Outposts", "+" + num6 + "/tick"); DrawMiniStat("Total", "+" + num9 + "/tick"); GUILayout.EndVertical(); GUILayout.BeginVertical(_costPanelStyle, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(num) }); GUILayout.Label("Upkeep", CardTitleStyle(UiTone.Cost), Array.Empty()); DrawMiniStat("Territories", "-" + num7 + "/tick"); DrawMiniStat("Vault", "-" + num10 + "/tick"); DrawMiniStat("Total", "-" + num11 + "/tick"); GUILayout.Label($"Multiplier: x{num3:F2} ({EconomyBandColored(count)})", _labelStyle, Array.Empty()); GUILayout.EndVertical(); GUILayout.BeginVertical(_positivePanelStyle, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(num2) }); GUILayout.Label("Summary", CardTitleStyle(UiTone.Positive), Array.Empty()); DrawMiniStat("Gross income", "+" + num9 + "/tick"); DrawMiniStat("Upkeep", "-" + num11 + "/tick"); DrawMiniStat("Net income", ColoredCoins(num9 - num11) + "/tick"); DrawMiniStat("Treasury", TreasuryColored(clan.Treasury) + " coins"); DrawMiniStat("Next tick", "every " + (ClanConfig.UpkeepTickMinutes?.Value ?? 30) + " minutes"); DrawMiniStat("Resource Tax", clan.ResourceTaxPercent + "%"); DrawMiniStat("Coin Tax", clan.CoinTaxPercent + "%"); DrawMiniStat("Tax this session", clan.SessionResourceTaxCollected + " items / " + clan.SessionCoinTaxCollected + " coins"); DrawMiniStat("Tax collected total", clan.TotalResourceTaxCollected + " items / " + clan.TotalCoinTaxCollected + " coins"); GUILayout.EndVertical(); GUILayout.EndHorizontal(); bool flag = info.Rank >= ClanRank.Officer; GUILayout.BeginHorizontal(Array.Empty()); GUILayout.BeginVertical(_costPanelStyle, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(num2) }); int num12 = Economy.VaultCapacity(clan, count); GUILayout.Label($"Vault: tier {clan.VaultTier}, capacity {num12} stacks ({data.GetVaultStackCount(info.ClanId)} used)", _labelStyle, Array.Empty()); int num13 = ClanConfig.MaxVaultTier?.Value ?? 5; GUI.enabled = flag && clan.VaultTier < num13; if (GUILayout.Button((clan.VaultTier < num13) ? $"Upgrade Vault to tier {clan.VaultTier + 1} ({Economy.VaultUpgradeCost(clan.VaultTier)}c)" : "Vault at maximum tier", GUI.enabled ? _primaryButtonStyle : _disabledButtonStyle, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Height(40f) })) { WarfareRPC.Instance?.RequestUpgradeVault(); } GUI.enabled = true; GUILayout.EndVertical(); GUILayout.BeginVertical(_infoPanelStyle, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(num2) }); GUILayout.Label($"Territory cap: {Economy.TerritoryCap(clan)} (base {ClanConfig.BaseTerritoryCap?.Value ?? 10} + {clan.ExtraTerritorySlots} expansion(s))", _labelStyle, Array.Empty()); GUI.enabled = flag && Economy.TerritoryCap(clan) < (ClanConfig.MaxTerritoryCap?.Value ?? 100); if (GUILayout.Button($"Buy Expansion (+{ClanConfig.TerritoryExpansionBlock?.Value ?? 10} slots, {Economy.TerritoryExpansionCost(clan.ExtraTerritorySlots)}c)", GUI.enabled ? _primaryButtonStyle : _disabledButtonStyle, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Height(40f) })) { WarfareRPC.Instance?.RequestBuyExpansion(); } GUI.enabled = true; GUILayout.EndVertical(); GUILayout.EndHorizontal(); } private void DrawWar(WarfareData data, PlayerClanInfo info, Clan clan) { float num = AvailableContentHeight(); string[] array = new string[6] { "Wars", "Diplomacy", "Bounties", "Territory Deals", "Vassals", "Events" }; GUILayout.BeginHorizontal(Array.Empty()); for (int i = 0; i < array.Length; i++) { if (GUILayout.Button(array[i], (_warSubTab == i) ? _tabActiveStyle : _tabStyle, (GUILayoutOption[])(object)new GUILayoutOption[2] { GUILayout.Width(150f), GUILayout.Height(38f) })) { _warSubTab = i; GUIUtility.ExitGUI(); } } GUILayout.EndHorizontal(); GUILayout.Space(6f); float panelHeight = Mathf.Max(300f, num - 138f); float panelWidth = AvailableContentWidth(); switch (_warSubTab) { case 0: DrawWarsHubPanel(data, info, panelHeight, panelWidth); break; case 1: DrawClanDiplomacyPanel(data, info, panelHeight, panelWidth); break; case 2: DrawBountiesPanel(data, info, panelHeight); break; case 3: DrawTerritoryDeals(data, info, panelHeight); break; case 4: DrawVassalsPanel(data, info, panelHeight); break; case 5: DrawWorldEventsPanel(data, info); break; } } private void DrawWarsHubPanel(WarfareData data, PlayerClanInfo info, float panelHeight, float panelWidth) { float num = Mathf.Max(240f, panelHeight - 48f); float num2 = Mathf.Max(180f, num * 0.46f); float num3 = Mathf.Max(150f, num - num2 - 8f); float num4 = Mathf.Max(620f, panelWidth - 470f); GUILayout.BeginVertical(_boxStyle, (GUILayoutOption[])(object)new GUILayoutOption[2] { GUILayout.Width(panelWidth), GUILayout.Height(panelHeight) }); GUILayout.Label("Wars", _labelGoldStyle, Array.Empty()); GUILayout.BeginHorizontal((GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Height(num) }); DrawDeclareWarPanel(data, info, num); GUILayout.BeginVertical((GUILayoutOption[])(object)new GUILayoutOption[2] { GUILayout.Width(num4), GUILayout.Height(num) }); DrawActiveWarsPanel(data, info, num2, num4); GUILayout.Space(8f); GUILayout.BeginHorizontal((GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Height(num3) }); GUILayout.BeginVertical(_boxStyle, (GUILayoutOption[])(object)new GUILayoutOption[2] { GUILayout.ExpandWidth(true), GUILayout.Height(num3) }); GUILayout.Label("War Targets", _labelGoldStyle, Array.Empty()); GUILayout.Label("Select an enemy clan on the left, then choose Raid, Declare War or Rage War.", _labelStyle, Array.Empty()); GUILayout.Label("Territory capture targets are handled by the existing war/capture flow.", _labelStyle, Array.Empty()); GUILayout.EndVertical(); GUILayout.BeginVertical(_boxStyle, (GUILayoutOption[])(object)new GUILayoutOption[2] { GUILayout.ExpandWidth(true), GUILayout.Height(num3) }); GUILayout.Label("War Status", _labelGoldStyle, Array.Empty()); DrawMiniStat("Active Wars", data.GetActiveWarsInvolving(info.ClanId).Count.ToString()); DrawMiniStat("Active Captures", data.ActiveCaptures.FindAll((CaptureSession c) => c.AttackerClanId == info.ClanId || c.DefenderClanId == info.ClanId).Count.ToString()); GUILayout.EndVertical(); GUILayout.BeginVertical(_boxStyle, (GUILayoutOption[])(object)new GUILayoutOption[2] { GUILayout.ExpandWidth(true), GUILayout.Height(num3) }); GUILayout.Label("War History", _labelGoldStyle, Array.Empty()); GUILayout.Label("Coming Soon: completed wars, captures, kills and peace outcomes will be summarized here.", _labelStyle, Array.Empty()); GUILayout.EndVertical(); GUILayout.EndHorizontal(); GUILayout.EndVertical(); GUILayout.EndHorizontal(); GUILayout.EndVertical(); } private void DrawWorldEventsPanel(WarfareData data, PlayerClanInfo info) { GUILayout.BeginVertical(_boxStyle, Array.Empty()); GUILayout.Label("ACTIVE EVENTS", _labelGoldStyle, Array.Empty()); List list = data.WorldEvents.FindAll((WorldEvent e) => e.Status == WorldEventStatus.Active || e.Status == WorldEventStatus.Pending || e.Status == WorldEventStatus.Completed || e.Status == WorldEventStatus.Expired); if (list.Count == 0) { DrawEmptyState("No active world events.", "World Events will appear here when they spawn."); DrawWorldEventLog(data, 8); GUILayout.EndVertical(); return; } foreach (WorldEvent item in list) { DrawWorldEventCard(item); } DrawWorldEventLog(data, 8); GUILayout.EndVertical(); } private void DrawWorldEventLog(WarfareData data, int maxLines) { if (data.WorldEventLog != null && data.WorldEventLog.Count != 0) { GUILayout.Space(6f); GUILayout.BeginVertical(_boxStyle, Array.Empty()); GUILayout.Label("Event Log", _labelGoldStyle, Array.Empty()); for (int i = 0; i < data.WorldEventLog.Count && i < maxLines; i++) { GUILayout.Label(data.WorldEventLog[i], _labelStyle, Array.Empty()); } GUILayout.EndVertical(); } } private void DrawWorldEventCard(WorldEvent worldEvent) { //IL_003b: Unknown result type (might be due to invalid IL or missing references) //IL_0041: Unknown result type (might be due to invalid IL or missing references) //IL_016d: Unknown result type (might be due to invalid IL or missing references) //IL_0172: Unknown result type (might be due to invalid IL or missing references) //IL_0179: Unknown result type (might be due to invalid IL or missing references) //IL_0183: Expected O, but got Unknown //IL_02f3: Unknown result type (might be due to invalid IL or missing references) long num = DateTimeOffset.UtcNow.ToUnixTimeSeconds(); long seconds = Math.Max(0L, worldEvent.EndTime - num); Player localPlayer = Player.m_localPlayer; float num2 = (((Object)(object)localPlayer != (Object)null) ? Vector3.Distance(((Component)localPlayer).transform.position, worldEvent.Position) : (-1f)); bool flag = num2 >= 0f && num2 <= worldEvent.Radius; bool enabled = worldEvent.Status == WorldEventStatus.Active && flag; string text = worldEvent.Status switch { WorldEventStatus.Completed => "Completed", WorldEventStatus.Expired => "Expired", WorldEventStatus.Failed => "Failed", _ => flag ? "Claim Event" : "Go to event area to claim", }; GUILayout.BeginHorizontal(_boxStyle, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.MinHeight(96f) }); Texture2D worldEventTexture = GetWorldEventTexture(worldEvent.EventType); if ((Object)(object)worldEventTexture != (Object)null) { GUILayout.Box((Texture)(object)worldEventTexture, _boxStyle, (GUILayoutOption[])(object)new GUILayoutOption[2] { GUILayout.Width(64f), GUILayout.Height(64f) }); } else { GUILayout.BeginVertical(_boxStyle, (GUILayoutOption[])(object)new GUILayoutOption[2] { GUILayout.Width(64f), GUILayout.Height(64f) }); GUILayout.FlexibleSpace(); GUIStyle val = new GUIStyle(_labelGoldStyle) { alignment = (TextAnchor)4, fontSize = 22 }; GUILayout.Label(WorldEventSymbol(worldEvent.EventType), val, Array.Empty()); GUILayout.FlexibleSpace(); GUILayout.EndVertical(); } GUILayout.BeginVertical((GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(360f) }); GUILayout.Label("" + (worldEvent.EventName ?? WarfareManager.WorldEventTypeName(worldEvent.EventType)) + "", _labelGoldStyle, Array.Empty()); GUILayout.Label("Type: " + WarfareManager.WorldEventTypeName(worldEvent.EventType) + "", _labelStyle, Array.Empty()); GUILayout.Label(worldEvent.Description ?? "", _labelStyle, Array.Empty()); GUILayout.EndVertical(); GUILayout.BeginVertical((GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(320f) }); GUILayout.Label("Location: " + Mathf.RoundToInt(worldEvent.Position.x) + ", " + Mathf.RoundToInt(worldEvent.Position.z) + "", _labelStyle, Array.Empty()); GUILayout.Label("Distance: " + FormatEventDistance(num2) + "" + ((num2 >= 0f) ? (" | " + EventDirection(worldEvent.Position)) : ""), _labelStyle, Array.Empty()); GUILayout.Label("Radius: " + Mathf.RoundToInt(worldEvent.Radius) + "m", _labelStyle, Array.Empty()); GUILayout.Label("Remaining: " + FormatDuration(seconds) + "", _labelStyle, Array.Empty()); GUILayout.EndVertical(); GUILayout.BeginVertical((GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(250f) }); GUILayout.Label("Status: " + worldEvent.Status.ToString() + "", _labelStyle, Array.Empty()); GUILayout.Label("Reward: " + (worldEvent.RewardSummary ?? "Future reward") + "", _labelStyle, Array.Empty()); GUILayout.Label(ClaimStatusText(worldEvent, flag), _labelStyle, Array.Empty()); if (GUILayout.Button("Track Event", _smallButtonStyle, (GUILayoutOption[])(object)new GUILayoutOption[2] { GUILayout.Width(120f), GUILayout.Height(34f) })) { PushLeftNotification("Track Event: " + worldEvent.EventName + "\nLocation: " + Mathf.RoundToInt(worldEvent.Position.x) + ", " + Mathf.RoundToInt(worldEvent.Position.z) + ((num2 >= 0f) ? ("\nDistance: " + FormatEventDistance(num2)) : "")); } GUILayout.EndVertical(); GUILayout.FlexibleSpace(); GUI.enabled = enabled; if (GUILayout.Button(text, _buttonStyle, (GUILayoutOption[])(object)new GUILayoutOption[2] { GUILayout.Width(190f), GUILayout.Height(32f) })) { WarfareRPC.Instance?.RequestWorldEventClaim(worldEvent.EventId); } GUI.enabled = true; GUILayout.EndHorizontal(); } private string FormatEventDistance(float distance) { if (distance < 0f) { return "Unknown"; } if (distance >= 1000f) { return (distance / 1000f).ToString("0.0") + " km"; } return Mathf.RoundToInt(distance) + " m"; } private string EventDirection(Vector3 target) { //IL_0015: Unknown result type (might be due to invalid IL or missing references) //IL_001c: Unknown result type (might be due to invalid IL or missing references) //IL_0021: Unknown result type (might be due to invalid IL or missing references) //IL_0026: Unknown result type (might be due to invalid IL or missing references) //IL_003b: Unknown result type (might be due to invalid IL or missing references) //IL_0041: Unknown result type (might be due to invalid IL or missing references) Player localPlayer = Player.m_localPlayer; if ((Object)(object)localPlayer == (Object)null) { return ""; } Vector3 val = target - ((Component)localPlayer).transform.position; if (((Vector3)(ref val)).sqrMagnitude < 1f) { return "Here"; } float num = Mathf.Atan2(val.x, val.z) * 57.29578f; if (num < 0f) { num += 360f; } return (new string[8] { "N", "NE", "E", "SE", "S", "SW", "W", "NW" })[Mathf.RoundToInt(num / 45f) % 8]; } private string ClaimStatusText(WorldEvent worldEvent, bool inRange) { if (worldEvent.Status == WorldEventStatus.Completed) { return "Claim Status: Completed"; } if (worldEvent.Status == WorldEventStatus.Expired) { return "Claim Status: Expired"; } if (worldEvent.Status != WorldEventStatus.Active) { return "Claim Status: " + worldEvent.Status; } if (!inRange) { return "Claim Status: Outside radius"; } return "Claim Status: In range"; } private void DrawDeclareWarPanel(WarfareData data, PlayerClanInfo info, float panelHeight) { //IL_0083: Unknown result type (might be due to invalid IL or missing references) //IL_009e: Unknown result type (might be due to invalid IL or missing references) //IL_00a3: Unknown result type (might be due to invalid IL or missing references) //IL_00fe: Unknown result type (might be due to invalid IL or missing references) //IL_0109: Expected O, but got Unknown //IL_0144: Unknown result type (might be due to invalid IL or missing references) GUILayout.BeginVertical(_boxStyle, (GUILayoutOption[])(object)new GUILayoutOption[2] { GUILayout.Width(440f), GUILayout.Height(panelHeight) }); GUILayout.Label("Declare War", _labelGoldStyle, Array.Empty()); GUILayout.Label("Choose a target clan and war type. This uses the existing war declaration logic.", _labelStyle, Array.Empty()); bool flag = info != null && info.Rank >= ClanRank.Officer; if (!flag) { GUILayout.Label("Only Officers and the Jarl may declare war.", _labelStyle, Array.Empty()); } _warClanScroll = GUILayout.BeginScrollView(_warClanScroll, false, true, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.ExpandHeight(true) }); bool flag2 = false; foreach (Clan value in data.Clans.Values) { if (value.ClanId != info.ClanId) { flag2 = true; bool flag3 = data.GetActiveWarBetween(info.ClanId, value.ClanId) != null; string message = ""; bool flag4 = (Object)WarfareManager.Instance != (Object)null && WarfareManager.Instance.IsHostileActionBlocked(info.ClanId, value.ClanId, out message); GUILayout.BeginVertical(_boxStyle, Array.Empty()); GUILayout.BeginHorizontal(Array.Empty()); DrawClanColorSquare(value.Color, 22f); GUILayout.Label("" + value.Name + " [" + value.Tag + "]", _labelGoldStyle, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(220f) }); GUILayout.Label(flag3 ? "At war" : (flag4 ? ("" + message + "") : "Available"), _labelStyle, Array.Empty()); GUILayout.EndHorizontal(); GUILayout.BeginHorizontal(Array.Empty()); GUI.enabled = flag && !flag3 && !flag4; if (GUILayout.Button("Raid", _smallButtonStyle, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(92f) })) { WarfareRPC.Instance?.RequestDeclareWar(value.ClanId, WarType.Raid); } if (GUILayout.Button("Declare War", _smallButtonStyle, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(124f) })) { WarfareRPC.Instance?.RequestDeclareWar(value.ClanId, WarType.War); } if (GUILayout.Button("Rage War", _smallButtonStyle, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(104f) })) { WarfareRPC.Instance?.RequestDeclareWar(value.ClanId, WarType.RageWar); } GUI.enabled = true; GUILayout.EndHorizontal(); GUILayout.EndVertical(); } } if (!flag2) { DrawEmptyState("No target clans.", "There are no other clans available for war declarations."); } GUILayout.EndScrollView(); GUILayout.EndVertical(); } private void DrawActiveWarsPanel(WarfareData data, PlayerClanInfo info, float panelHeight, float panelWidth) { //IL_0134: Unknown result type (might be due to invalid IL or missing references) //IL_015f: Unknown result type (might be due to invalid IL or missing references) //IL_0164: Unknown result type (might be due to invalid IL or missing references) GUILayout.BeginVertical(_boxStyle, (GUILayoutOption[])(object)new GUILayoutOption[2] { GUILayout.Width(panelWidth), GUILayout.Height(panelHeight) }); GUILayout.Label("Active Wars", _labelGoldStyle, Array.Empty()); List activeWarsInvolving = data.GetActiveWarsInvolving(info.ClanId); if (activeWarsInvolving.Count == 0) { DrawEmptyState("No active wars.", "Your clan is currently at peace."); } else { GUILayout.BeginHorizontal(_boxStyle, Array.Empty()); GUILayout.Label("Enemy Clan", _labelGoldStyle, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(260f) }); GUILayout.Label("War Type", _labelGoldStyle, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(140f) }); GUILayout.Label("Target Territory", _labelGoldStyle, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(260f) }); GUILayout.Label("Status", _labelGoldStyle, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(180f) }); GUILayout.Label("Kills / Captures", _labelGoldStyle, Array.Empty()); GUILayout.EndHorizontal(); _warActiveScroll = GUILayout.BeginScrollView(_warActiveScroll, false, true, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Height(Mathf.Max(90f, panelHeight - 76f)) }); foreach (War item in activeWarsInvolving) { Clan clan = data.GetClan(item.AttackerClanId); Clan clan2 = data.GetClan(item.DefenderClanId); Clan clan3 = ((item.AttackerClanId == info.ClanId) ? clan2 : clan); int count = data.ActiveCaptures.FindAll((CaptureSession c) => (c.AttackerClanId == item.AttackerClanId && c.DefenderClanId == item.DefenderClanId) || (c.AttackerClanId == item.DefenderClanId && c.DefenderClanId == item.AttackerClanId)).Count; GUILayout.BeginHorizontal(_boxStyle, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.MinHeight(34f) }); GUILayout.Label(clan3?.Name ?? "Unknown", _labelGoldStyle, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(260f) }); GUILayout.Label("" + item.Type.ToString() + "", _labelStyle, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(140f) }); GUILayout.Label((count > 0) ? "Active contested territory" : "No active target", _labelStyle, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(260f) }); GUILayout.Label($"{item.MinutesRemaining:F0} min remaining", _labelStyle, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(180f) }); GUILayout.Label("Kills: n/a Captures: " + count, _labelStyle, Array.Empty()); GUILayout.EndHorizontal(); } GUILayout.EndScrollView(); } GUILayout.EndVertical(); } private void DrawBountiesPanel(WarfareData data, PlayerClanInfo info, float panelHeight) { //IL_0079: 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_009d: Unknown result type (might be due to invalid IL or missing references) //IL_0263: Unknown result type (might be due to invalid IL or missing references) //IL_0282: 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) GUILayout.BeginVertical(_boxStyle, Array.Empty()); GUILayout.Label("Bounties", _labelGoldStyle, Array.Empty()); GUILayout.BeginHorizontal(Array.Empty()); float num = ResponsiveCardWidth(AvailableContentWidth(reserveScrollbar: true), 2); GUILayout.BeginVertical(_boxStyle, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(num) }); GUILayout.Label("Active Bounties", _labelGoldStyle, Array.Empty()); _bountyActiveScroll = GUILayout.BeginScrollView(_bountyActiveScroll, false, false, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Height(180f) }); bool flag = false; foreach (Clan value in data.Clans.Values) { if (value.Bounty > 0) { flag = true; DrawBountyRow(value.Name + " [" + value.Tag + "]", "Clan", value.Bounty, "Treasury-funded", "Visible"); } } foreach (PlayerClanInfo value2 in data.Players.Values) { if (value2.Bounty > 0) { flag = true; DrawBountyRow(value2.PlayerName, (value2.IsFreeborn && value2.ClanId == 0) ? "Freeborn" : "Player", value2.Bounty, "Treasury-funded", "Auto-claim on kill"); } } if (!flag) { DrawEmptyState("No active bounties.", "No player, freeborn, or clan bounty is currently posted."); } GUILayout.EndScrollView(); GUILayout.EndVertical(); GUILayout.BeginVertical(_boxStyle, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.ExpandWidth(true) }); GUILayout.Label("Place Bounty", _labelGoldStyle, Array.Empty()); GUILayout.BeginHorizontal(Array.Empty()); GUILayout.Label("Amount:", _labelStyle, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(64f) }); _bountyInput = GUILayout.TextField(_bountyInput, 9, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(100f) }); GUILayout.Label("Reward is paid from the issuer's available treasury/wallet source.", _labelStyle, Array.Empty()); GUILayout.EndHorizontal(); _bountyTargetScroll = GUILayout.BeginScrollView(_bountyTargetScroll, false, false, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Height(220f) }); DrawBountyTargets(data, info); GUILayout.EndScrollView(); GUILayout.EndVertical(); GUILayout.EndHorizontal(); GUILayout.EndVertical(); } private void DrawBountyRow(string target, string targetType, long reward, string issuer, string status) { float num = ResponsiveCardWidth(AvailableContentWidth(reserveScrollbar: true), 2) - 28f; GUILayout.BeginHorizontal(_boxStyle, Array.Empty()); GUILayout.Label(target, _labelGoldStyle, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(num * 0.34f) }); GUILayout.Label(targetType, _labelStyle, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(num * 0.14f) }); GUILayout.Label("" + reward + "c", _labelStyle, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(num * 0.14f) }); GUILayout.Label(issuer, _labelStyle, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(num * 0.18f) }); GUILayout.Label(status, _labelStyle, Array.Empty()); GUILayout.EndHorizontal(); } private void DrawBountyTargets(WarfareData data, PlayerClanInfo info) { bool flag = info != null && (info.IsFreeborn || info.Rank >= ClanRank.Officer); long num = ParseBountyAmount(); foreach (Clan value in data.Clans.Values) { if (info == null || value.ClanId != info.ClanId) { GUILayout.BeginHorizontal(_boxStyle, Array.Empty()); GUILayout.Label(value.Name + " [" + value.Tag + "]", _labelStyle, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(190f) }); GUILayout.Label("Clan", _labelStyle, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(90f) }); GUILayout.Label("Current: " + value.Bounty + "c", _labelStyle, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(110f) }); GUI.enabled = flag && num > 0; if (GUILayout.Button("Place", _smallButtonStyle, (GUILayoutOption[])(object)new GUILayoutOption[2] { GUILayout.Width(92f), GUILayout.Height(36f) })) { WarfareRPC.Instance?.RequestPlaceBounty(0L, value.ClanId, num); } GUI.enabled = true; GUILayout.EndHorizontal(); } } foreach (PlayerClanInfo value2 in data.Players.Values) { if (info == null || value2.PlayerId != info.PlayerId) { GUILayout.BeginHorizontal(_boxStyle, Array.Empty()); GUILayout.Label(value2.PlayerName, _labelStyle, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(190f) }); GUILayout.Label((value2.IsFreeborn && value2.ClanId == 0) ? "Freeborn" : "Player", _labelStyle, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(90f) }); GUILayout.Label("Current: " + value2.Bounty + "c", _labelStyle, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(110f) }); GUI.enabled = flag && num > 0; if (GUILayout.Button("Place", _smallButtonStyle, (GUILayoutOption[])(object)new GUILayoutOption[2] { GUILayout.Width(92f), GUILayout.Height(36f) })) { WarfareRPC.Instance?.RequestPlaceBounty(value2.PlayerId, 0, num); } GUI.enabled = true; GUILayout.EndHorizontal(); } } if (!flag) { GUILayout.Label("Only clan Officers/Jarls or Freeborn players may place bounties.", _labelStyle, Array.Empty()); } } private long ParseBountyAmount() { if (!long.TryParse(_bountyInput, out var result)) { return 0L; } return Math.Max(0L, result); } private void DrawClanDiplomacyPanel(WarfareData data, PlayerClanInfo info, float panelHeight, float panelWidth) { //IL_00dc: Unknown result type (might be due to invalid IL or missing references) //IL_0107: 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) GUILayout.BeginVertical(_boxStyle, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(panelWidth) }); GUILayout.BeginHorizontal(Array.Empty()); GUILayout.Label("Clan Diplomacy", _labelGoldStyle, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(170f) }); ConfigEntry allianceBorderEnabled = ClanConfig.AllianceBorderEnabled; GUILayout.Label("Alliance Borders: " + ((allianceBorderEnabled != null && allianceBorderEnabled.Value) ? "Enabled" : "Disabled") + $" Thickness: {ClanConfig.AllianceBorderThickness?.Value ?? 2f:0.0}", _labelStyle, Array.Empty()); GUILayout.EndHorizontal(); if (info.Rank < ClanRank.Officer) { GUILayout.Label("Only Officers and the Jarl may declare war or set bounties.", _labelStyle, Array.Empty()); } else { DrawAlliancePanel(data, info); } _warClanScroll = GUILayout.BeginScrollView(_warClanScroll, false, false, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Height(Mathf.Max(170f, panelHeight - 118f)) }); foreach (Clan value in data.Clans.Values) { if (value.ClanId != info.ClanId) { DrawClanDiplomacyRow(data, info, value); } } GUILayout.EndScrollView(); DrawBountyEditor(data); GUILayout.EndVertical(); } private void DrawClanDiplomacyRow(WarfareData data, PlayerClanInfo info, Clan target) { //IL_0085: Unknown result type (might be due to invalid IL or missing references) //IL_0090: Expected O, but got Unknown //IL_00de: Unknown result type (might be due to invalid IL or missing references) bool flag = info.Rank >= ClanRank.Officer; bool flag2 = _expandedDiplomacyClanId == target.ClanId; bool flag3 = data.GetActiveWarBetween(info.ClanId, target.ClanId) != null; DiplomacyState diplomacyState = WarfareManager.Instance?.GetDiplomacyState(info.ClanId, target.ClanId) ?? DiplomacyState.Neutral; string text = WarfareManager.Instance?.GetRelationLabel(info.ClanId, target.ClanId) ?? diplomacyState.ToString(); string message; bool flag4 = (Object)WarfareManager.Instance != (Object)null && WarfareManager.Instance.IsHostileActionBlocked(info.ClanId, target.ClanId, out message); GUILayout.BeginVertical(_boxStyle, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.MinHeight(52f) }); GUILayout.BeginHorizontal(Array.Empty()); DrawClanColorSquare(target.Color, 24f); GUILayout.BeginVertical((GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(220f) }); GUILayout.Label("" + target.Name + " [" + target.Tag + "]", _labelGoldStyle, Array.Empty()); GUILayout.Label("Relation: " + text + (flag3 ? " At war" : ""), _labelStyle, Array.Empty()); GUILayout.EndVertical(); GUI.enabled = flag; if (GUILayout.Button((diplomacyState == DiplomacyState.Friendly) ? "Accept Ally" : "Ally", _smallButtonStyle, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(78f) })) { WarfareRPC.Instance?.RequestDiplomacy(target.ClanId, (diplomacyState != DiplomacyState.Friendly) ? 1 : 2); } if (GUILayout.Button("NAP", _smallButtonStyle, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(58f) })) { WarfareRPC.Instance?.RequestDiplomacyV1(20, target.ClanId, 7, "", 0L); } GUI.enabled = flag && !flag3 && !flag4; if (GUILayout.Button("Declare War", _smallButtonStyle, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(94f) })) { WarfareRPC.Instance?.RequestDeclareWar(target.ClanId, WarType.War); } GUI.enabled = flag; if (GUILayout.Button("Bounty", _smallButtonStyle, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(70f) })) { _bountyTargetClan = target.ClanId; } GUI.enabled = true; GUILayout.FlexibleSpace(); if (GUILayout.Button(flag2 ? "Collapse" : "Expand", _smallButtonStyle, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(82f) })) { _expandedDiplomacyClanId = ((!flag2) ? target.ClanId : 0); } GUILayout.EndHorizontal(); if (flag2) { DrawExpandedDiplomacyActions(data, info, target, diplomacyState, flag3, flag4, flag); } GUILayout.EndVertical(); } private void DrawExpandedDiplomacyActions(WarfareData data, PlayerClanInfo info, Clan target, DiplomacyState diplomacyState, bool atWar, bool blocked, bool canManage) { GUI.enabled = canManage; GUILayout.BeginHorizontal(Array.Empty()); GUILayout.BeginVertical(_boxStyle, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(250f) }); GUILayout.Label("Diplomacy", _labelGoldStyle, Array.Empty()); GUILayout.BeginHorizontal(Array.Empty()); if (GUILayout.Button((diplomacyState == DiplomacyState.Friendly) ? "Accept Ally" : "Ally", _smallButtonStyle, (GUILayoutOption[])(object)new GUILayoutOption[2] { GUILayout.Width(92f), GUILayout.Height(36f) })) { WarfareRPC.Instance?.RequestDiplomacy(target.ClanId, (diplomacyState != DiplomacyState.Friendly) ? 1 : 2); } if (GUILayout.Button("Neutral", _smallButtonStyle, (GUILayoutOption[])(object)new GUILayoutOption[2] { GUILayout.Width(92f), GUILayout.Height(36f) })) { WarfareRPC.Instance?.RequestDiplomacy(target.ClanId, 3); } if (GUILayout.Button("Hostile", _smallButtonStyle, (GUILayoutOption[])(object)new GUILayoutOption[2] { GUILayout.Width(92f), GUILayout.Height(36f) })) { WarfareRPC.Instance?.RequestDiplomacy(target.ClanId, 4); } GUILayout.EndHorizontal(); GUILayout.BeginHorizontal(Array.Empty()); if (GUILayout.Button("Invite", _smallButtonStyle, (GUILayoutOption[])(object)new GUILayoutOption[2] { GUILayout.Width(92f), GUILayout.Height(36f) })) { WarfareRPC.Instance?.RequestDiplomacyV1(11, target.ClanId, 0, "", 0L); } if (GUILayout.Button("Accept", _smallButtonStyle, (GUILayoutOption[])(object)new GUILayoutOption[2] { GUILayout.Width(92f), GUILayout.Height(36f) })) { WarfareRPC.Instance?.RequestDiplomacyV1(12, target.ClanId, 0, "", 0L); } if (GUILayout.Button("Kick", _smallButtonStyle, (GUILayoutOption[])(object)new GUILayoutOption[2] { GUILayout.Width(92f), GUILayout.Height(36f) })) { WarfareRPC.Instance?.RequestDiplomacyV1(15, target.ClanId, 0, "", 0L); } GUILayout.EndHorizontal(); GUILayout.EndVertical(); GUILayout.BeginVertical(_boxStyle, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(245f) }); GUILayout.Label("NAP / Vassal", _labelGoldStyle, Array.Empty()); GUILayout.BeginHorizontal(Array.Empty()); if (GUILayout.Button("NAP 7", _smallButtonStyle, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(58f) })) { WarfareRPC.Instance?.RequestDiplomacyV1(20, target.ClanId, 7, "", 0L); } if (GUILayout.Button("14", _smallButtonStyle, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(42f) })) { WarfareRPC.Instance?.RequestDiplomacyV1(20, target.ClanId, 14, "", 0L); } if (GUILayout.Button("30", _smallButtonStyle, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(42f) })) { WarfareRPC.Instance?.RequestDiplomacyV1(20, target.ClanId, 30, "", 0L); } if (GUILayout.Button("Accept", _smallButtonStyle, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(70f) })) { WarfareRPC.Instance?.RequestDiplomacyV1(21, target.ClanId, 0, "", 0L); } GUILayout.EndHorizontal(); GUILayout.BeginHorizontal(Array.Empty()); if (GUILayout.Button("Break NAP", _smallButtonStyle, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(86f) })) { WarfareRPC.Instance?.RequestDiplomacyV1(23, target.ClanId, 0, "", 0L); } GUILayout.EndHorizontal(); DrawVassalControls(data, info, target, atWar); GUILayout.EndVertical(); GUILayout.BeginVertical(_boxStyle, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(270f) }); GUILayout.Label("Military", _labelGoldStyle, Array.Empty()); GUILayout.BeginHorizontal(Array.Empty()); GUI.enabled = canManage && !atWar && !blocked; if (GUILayout.Button("Raid", _smallButtonStyle, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(70f) })) { WarfareRPC.Instance?.RequestDeclareWar(target.ClanId, WarType.Raid); } if (GUILayout.Button("Declare War", _smallButtonStyle, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(94f) })) { WarfareRPC.Instance?.RequestDeclareWar(target.ClanId, WarType.War); } if (GUILayout.Button("Rage War", _smallButtonStyle, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(82f) })) { WarfareRPC.Instance?.RequestDeclareWar(target.ClanId, WarType.RageWar); } GUI.enabled = canManage; GUILayout.EndHorizontal(); GUILayout.EndVertical(); GUILayout.BeginVertical(_boxStyle, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(130f) }); GUILayout.Label("Economy", _labelGoldStyle, Array.Empty()); if (GUILayout.Button("Place Bounty", _smallButtonStyle, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(112f) })) { _bountyTargetClan = target.ClanId; } GUILayout.EndVertical(); GUI.enabled = true; GUILayout.EndHorizontal(); } private void DrawVassalControls(WarfareData data, PlayerClanInfo info, Clan target, bool atWar) { bool flag = info != null && info.Rank == ClanRank.Jarl && (ClanConfig.EnableVassals?.Value ?? false); VassalRelation vassalRelation = data.VassalRelations.Find((VassalRelation v) => v.Status == VassalStatus.Active && v.IsBetween(info.ClanId, target.ClanId)); VassalRelation vassalRelation2 = data.VassalRelations.Find((VassalRelation v) => v.Status == VassalStatus.Pending && v.IsBetween(info.ClanId, target.ClanId)); GUILayout.Space(4f); GUILayout.Label("Vassalage", _labelGoldStyle, Array.Empty()); if (vassalRelation != null) { if (vassalRelation.OverlordClanId == info.ClanId) { GUI.enabled = flag; if (GUILayout.Button("Release Vassal", _smallButtonStyle, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(118f) })) { WarfareRPC.Instance?.RequestDiplomacyV1(34, target.ClanId, 0, "", 0L); } GUI.enabled = true; } else { GUI.enabled = flag; if (GUILayout.Button("Break Vassalage", _smallButtonStyle, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(128f) })) { WarfareRPC.Instance?.RequestDiplomacyV1(35, 0, 0, "", 0L); } GUI.enabled = true; } } else if (vassalRelation2 != null) { if (vassalRelation2.PendingRequestByClanId == info.ClanId) { GUILayout.Label("Pending response", _labelStyle, Array.Empty()); return; } GUILayout.BeginHorizontal(Array.Empty()); GUI.enabled = flag; if (GUILayout.Button("Accept Vassalage", _smallButtonStyle, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(122f) })) { WarfareRPC.Instance?.RequestDiplomacyV1(32, target.ClanId, 0, "", 0L); } if (GUILayout.Button("Reject", _smallButtonStyle, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(62f) })) { WarfareRPC.Instance?.RequestDiplomacyV1(33, target.ClanId, 0, "", 0L); } GUI.enabled = true; GUILayout.EndHorizontal(); } else { GUI.enabled = flag && !atWar; if (GUILayout.Button("Offer Vassalage", _smallButtonStyle, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(128f) })) { WarfareRPC.Instance?.RequestDiplomacyV1(30, target.ClanId, 0, "", 0L); } GUI.enabled = true; } } private void DrawVassalsPanel(WarfareData data, PlayerClanInfo info, float panelHeight) { GUILayout.BeginVertical(_boxStyle, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Height(panelHeight) }); GUILayout.Label("Vassals", _labelGoldStyle, Array.Empty()); GUILayout.Label("Overview only. Manage vassalage from expanded clan rows in Diplomacy.", _labelStyle, Array.Empty()); GUILayout.BeginHorizontal(Array.Empty()); GUILayout.BeginVertical(_boxStyle, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(360f) }); GUILayout.Label("Your Overlord", _labelGoldStyle, Array.Empty()); VassalRelation vassalRelation = data.VassalRelations.Find((VassalRelation v) => v.Status == VassalStatus.Active && v.VassalClanId == info.ClanId); if (vassalRelation == null) { DrawEmptyState("None", "Your clan is not a vassal."); } else { DrawMiniStat("Overlord", data.GetClan(vassalRelation.OverlordClanId)?.Name ?? vassalRelation.OverlordClanName ?? "Unknown"); DrawMiniStat("Status", "Active"); } GUILayout.EndVertical(); GUILayout.BeginVertical(_boxStyle, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(440f) }); GUILayout.Label("Your Vassals", _labelGoldStyle, Array.Empty()); bool flag = false; foreach (VassalRelation vassalRelation2 in data.VassalRelations) { if (vassalRelation2.Status == VassalStatus.Active && vassalRelation2.OverlordClanId == info.ClanId) { flag = true; Clan clan = data.GetClan(vassalRelation2.VassalClanId); GUILayout.BeginHorizontal(_boxStyle, Array.Empty()); GUILayout.Label(clan?.Name ?? vassalRelation2.VassalClanName ?? "Unknown", _labelGoldStyle, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(260f) }); if (GUILayout.Button("Open Row", _smallButtonStyle, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(92f) })) { _warSubTab = 1; _expandedDiplomacyClanId = vassalRelation2.VassalClanId; GUIUtility.ExitGUI(); } GUILayout.EndHorizontal(); } } if (!flag) { DrawEmptyState("No vassals.", "Accepted vassal states will appear here."); } GUILayout.EndVertical(); GUILayout.BeginVertical(_boxStyle, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.ExpandWidth(true) }); GUILayout.Label("Pending Vassal Offers", _labelGoldStyle, Array.Empty()); bool flag2 = false; foreach (VassalRelation vassalRelation3 in data.VassalRelations) { if (vassalRelation3.Status == VassalStatus.Pending && (vassalRelation3.OverlordClanId == info.ClanId || vassalRelation3.VassalClanId == info.ClanId)) { flag2 = true; Clan clan2 = data.GetClan(vassalRelation3.OverlordClanId); Clan clan3 = data.GetClan(vassalRelation3.VassalClanId); GUILayout.BeginHorizontal(_boxStyle, Array.Empty()); GUILayout.Label("Pending", _labelGoldStyle, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(78f) }); GUILayout.Label("Overlord: " + (clan2?.Name ?? vassalRelation3.OverlordClanName ?? "Unknown"), _labelStyle, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(230f) }); GUILayout.Label("Vassal: " + (clan3?.Name ?? vassalRelation3.VassalClanName ?? "Unknown"), _labelStyle, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(230f) }); int clanId = ((vassalRelation3.OverlordClanId == info.ClanId) ? vassalRelation3.VassalClanId : vassalRelation3.OverlordClanId); Clan clan4 = data.GetClan(clanId); if (clan4 != null && GUILayout.Button("Open Row", _smallButtonStyle, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(92f) })) { _warSubTab = 1; _expandedDiplomacyClanId = clan4.ClanId; GUIUtility.ExitGUI(); } GUILayout.EndHorizontal(); } } if (!flag2) { DrawEmptyState("No pending offers.", "Offer, accept, or reject vassalage from Diplomacy."); } GUILayout.EndVertical(); GUILayout.EndHorizontal(); GUILayout.Space(6f); GUILayout.BeginVertical(_boxStyle, Array.Empty()); GUILayout.Label("Rules", _labelGoldStyle, Array.Empty()); GUILayout.Label("Vassals keep their territories and income. No tribute, rebellion, guards, or forced military support is active in this version.", _labelStyle, Array.Empty()); GUILayout.EndVertical(); GUILayout.EndVertical(); } private void DrawBountyEditor(WarfareData data) { if (_bountyTargetClan != 0) { Clan clan = data.GetClan(_bountyTargetClan); GUILayout.BeginHorizontal(_boxStyle, Array.Empty()); GUILayout.Label("Bounty on " + clan?.Name + ":", _labelStyle, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(160f) }); _bountyInput = GUILayout.TextField(_bountyInput, 9, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(80f) }); if (GUILayout.Button("Place", _smallButtonStyle, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(70f) })) { long result; long amount = (long.TryParse(_bountyInput, out result) ? result : 0); WarfareRPC.Instance?.RequestPlaceBounty(0L, _bountyTargetClan, amount); _bountyTargetClan = 0; } if (GUILayout.Button("Cancel", _smallButtonStyle, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(70f) })) { _bountyTargetClan = 0; } GUILayout.EndHorizontal(); } } private void DrawAlliancePanel(WarfareData data, PlayerClanInfo info) { Alliance alliance = WarfareManager.Instance?.GetAllianceForClan(info.ClanId); GUILayout.BeginVertical(_boxStyle, Array.Empty()); GUILayout.Label("Alliance", _labelGoldStyle, Array.Empty()); if (alliance == null) { GUILayout.BeginHorizontal(Array.Empty()); _allianceNameInput = GUILayout.TextField(_allianceNameInput, 32, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(220f) }); GUI.enabled = info.Rank == ClanRank.Jarl; if (GUILayout.Button("Create Alliance", _smallButtonStyle, (GUILayoutOption[])(object)new GUILayoutOption[2] { GUILayout.Width(150f), GUILayout.Height(34f) })) { WarfareRPC.Instance?.RequestDiplomacyV1(10, 0, 0, _allianceNameInput, 0L); } GUI.enabled = true; GUILayout.EndHorizontal(); foreach (Alliance alliance2 in data.Alliances) { if (alliance2.PendingInvites.Contains(info.ClanId)) { Clan clan = data.GetClan(alliance2.AllianceLeaderClanId); GUILayout.BeginHorizontal(Array.Empty()); GUILayout.Label("Invite from " + (clan?.Name ?? alliance2.AllianceName), _labelStyle, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(220f) }); if (GUILayout.Button("Accept", _smallButtonStyle, (GUILayoutOption[])(object)new GUILayoutOption[2] { GUILayout.Width(92f), GUILayout.Height(36f) })) { WarfareRPC.Instance?.RequestDiplomacyV1(12, alliance2.AllianceLeaderClanId, 0, "", 0L); } if (GUILayout.Button("Decline", _smallButtonStyle, (GUILayoutOption[])(object)new GUILayoutOption[2] { GUILayout.Width(92f), GUILayout.Height(36f) })) { WarfareRPC.Instance?.RequestDiplomacyV1(13, alliance2.AllianceLeaderClanId, 0, "", 0L); } GUILayout.EndHorizontal(); } } } else { Clan clan2 = data.GetClan(alliance.AllianceLeaderClanId); GUILayout.Label("Current Alliance: " + alliance.AllianceName + " | Leader: " + (clan2?.Name ?? "Unknown"), _labelStyle, Array.Empty()); GUILayout.BeginHorizontal(Array.Empty()); GUILayout.Label("Members:", _labelStyle, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(70f) }); foreach (int memberClanId in alliance.MemberClanIds) { GUILayout.Label(data.GetClan(memberClanId)?.Name ?? memberClanId.ToString(), _labelStyle, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(120f) }); } GUILayout.EndHorizontal(); GUI.enabled = info.Rank == ClanRank.Jarl; GUILayout.BeginHorizontal(Array.Empty()); if (GUILayout.Button("Change Color", _smallButtonStyle, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(110f) })) { WarfareRPC.Instance?.RequestDiplomacyV1(17, 0, 0, "", 0L); } if (GUILayout.Button("Leave", _smallButtonStyle, (GUILayoutOption[])(object)new GUILayoutOption[2] { GUILayout.Width(92f), GUILayout.Height(36f) })) { WarfareRPC.Instance?.RequestDiplomacyV1(14, 0, 0, "", 0L); } if (GUILayout.Button("Disband", _smallButtonStyle, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(86f) })) { WarfareRPC.Instance?.RequestDiplomacyV1(16, 0, 0, "", 0L); } GUILayout.EndHorizontal(); GUI.enabled = true; } GUILayout.EndVertical(); } private void DrawTerritoryDeals(WarfareData data, PlayerClanInfo info, float panelHeight) { ConfigEntry enableTerritoryTransfers = ClanConfig.EnableTerritoryTransfers; if (enableTerritoryTransfers == null || !enableTerritoryTransfers.Value) { return; } GUILayout.BeginVertical(_boxStyle, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.MaxWidth(980f) }); GUILayout.Label("Territory Diplomacy", _labelGoldStyle, Array.Empty()); GUILayout.BeginHorizontal(Array.Empty()); if (GUILayout.Button((_territoryDealMode == 0) ? "[Gift]" : "Gift", (_territoryDealMode == 0) ? _tabActiveStyle : _smallButtonStyle, (GUILayoutOption[])(object)new GUILayoutOption[2] { GUILayout.Width(120f), GUILayout.Height(34f) })) { _territoryDealMode = 0; } if (GUILayout.Button((_territoryDealMode == 1) ? "[Sell]" : "Sell", (_territoryDealMode == 1) ? _tabActiveStyle : _smallButtonStyle, (GUILayoutOption[])(object)new GUILayoutOption[2] { GUILayout.Width(120f), GUILayout.Height(34f) })) { _territoryDealMode = 1; } if (GUILayout.Button((_territoryDealMode == 2) ? "[Exchange]" : "Exchange", (_territoryDealMode == 2) ? _tabActiveStyle : _smallButtonStyle, (GUILayoutOption[])(object)new GUILayoutOption[2] { GUILayout.Width(150f), GUILayout.Height(34f) })) { _territoryDealMode = 2; } GUILayout.EndHorizontal(); List clanTerritories = data.GetClanTerritories(info.ClanId); clanTerritories.RemoveAll(delegate(ClanTerritory t) { if (t.FreebornOwnerId == 0L && !ProtectedWorldZones.IsProtected(t)) { if (t.Type == TerritoryType.Capital) { ConfigEntry allowCapitalTransfer = ClanConfig.AllowCapitalTransfer; if (allowCapitalTransfer == null) { return true; } return !allowCapitalTransfer.Value; } return false; } return true; }); List list = new List(); foreach (Clan value in data.Clans.Values) { if (value.ClanId != info.ClanId) { list.Add(value); } } if ((_territoryDealMode != 2 && clanTerritories.Count == 0) || list.Count == 0) { GUILayout.Label("No eligible clan territories or target clans available.", _labelStyle, Array.Empty()); DrawPendingTerritoryDeals(data, info); GUILayout.EndVertical(); return; } if (clanTerritories.Count > 0) { _territoryDealTerritoryIndex = Mathf.Clamp(_territoryDealTerritoryIndex, 0, clanTerritories.Count - 1); } _territoryDealClanIndex = Mathf.Clamp(_territoryDealClanIndex, 0, list.Count - 1); ClanTerritory clanTerritory = ((clanTerritories.Count > 0) ? clanTerritories[_territoryDealTerritoryIndex] : null); Clan clan = list[_territoryDealClanIndex]; if (_territoryDealMode == 2) { DrawTerritoryExchangeDeal(data, info, clan, panelHeight); GUILayout.EndVertical(); return; } GUILayout.BeginHorizontal(Array.Empty()); GUILayout.BeginVertical(_boxStyle, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(560f) }); GUILayout.Label("Deal Controls", _labelGoldStyle, Array.Empty()); GUILayout.Label("Territory:", _labelStyle, Array.Empty()); _territoryDealTerritoryIndex = DrawDropdown("territory", _territoryDealTerritoryIndex, clanTerritories.ConvertAll((ClanTerritory t) => t.DisplayName + " (" + t.Type.ToString() + ")"), ref _territoryDealTerritoryOpen, 530f); GUILayout.Space(4f); GUILayout.Label("Target Clan:", _labelStyle, Array.Empty()); _territoryDealClanIndex = DrawDropdown("target clan", _territoryDealClanIndex, list.ConvertAll((Clan c) => c.Name + " [" + c.Tag + "]"), ref _territoryDealClanOpen, 530f); GUILayout.Space(6f); GUILayout.Label("Action:", _labelStyle, Array.Empty()); GUILayout.BeginHorizontal(Array.Empty()); if (GUILayout.Button((_territoryDealMode == 1) ? "Gift" : "[Gift]", (_territoryDealMode == 1) ? _smallButtonStyle : _tabActiveStyle, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(90f) })) { _territoryDealMode = 0; } if (GUILayout.Button((_territoryDealMode == 1) ? "[Sell]" : "Sell", (_territoryDealMode == 1) ? _tabActiveStyle : _smallButtonStyle, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(90f) })) { _territoryDealMode = 1; } GUILayout.EndHorizontal(); if (_territoryDealMode == 1) { GUILayout.BeginHorizontal(Array.Empty()); GUILayout.Label("Price:", _labelStyle, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(60f) }); _territoryDealPriceInput = GUILayout.TextField(_territoryDealPriceInput, 10, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(120f) }); GUILayout.Label("coins", _labelStyle, Array.Empty()); GUILayout.EndHorizontal(); } long result; long amount = (long.TryParse(_territoryDealPriceInput, out result) ? Math.Max(0L, result) : 0); GUI.enabled = info.Rank == ClanRank.Jarl; if (GUILayout.Button("Execute Deal", _buttonStyle, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Height(38f) })) { string text = WarfareData.GridToKey(clanTerritory.GridX, clanTerritory.GridY); WarfareRPC.Instance?.RequestDiplomacyV1((_territoryDealMode == 1) ? 41 : 40, clan.ClanId, 0, text, amount); } GUI.enabled = true; GUILayout.EndVertical(); GUILayout.Space(6f); GUILayout.BeginVertical(_boxStyle, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.ExpandWidth(true) }); GUILayout.Label("Selected Territory", _labelGoldStyle, Array.Empty()); float globalMultiplier = Economy.GlobalUpkeepMultiplier(data.GetClanTerritoryCount(info.ClanId)); Clan clan2 = data.GetClan(clanTerritory.OwnerClanId); DrawMiniStat("Territory name", clanTerritory.DisplayName); DrawMiniStat("Current owner", clan2?.Name ?? "Unknown"); DrawMiniStat("Income", "+" + Economy.TerritoryIncome(clanTerritory) + "/tick"); DrawMiniStat("Upkeep", "-" + Economy.ApplyBuildingUpkeepReduction(clanTerritory, Economy.ApplyWardenUpkeepReduction(data, info.ClanId, Economy.TerritoryUpkeep(clanTerritory, globalMultiplier))) + "/tick"); DrawMiniStat("Connected Status", clanTerritory.Type.ToString()); DrawMiniStat("Strategic Value", Economy.ValueTierName(clanTerritory.BiomeValueTier) + " value | Size " + clanTerritory.SizeLevel); GUILayout.Space(4f); GUILayout.Label((_territoryDealMode == 1) ? "Transfer ownership of the selected territory in exchange for coins. Coins are deposited into the Clan Treasury." : "Transfer ownership of the selected territory to another clan for free. This action cannot be reversed.", _labelStyle, Array.Empty()); DrawPendingTerritoryDeals(data, info); GUILayout.EndVertical(); GUILayout.EndHorizontal(); GUILayout.EndVertical(); } private void DrawTerritoryExchangeDeal(WarfareData data, PlayerClanInfo info, Clan selectedTarget, float panelHeight) { List clanTerritories = data.GetClanTerritories(info.ClanId); clanTerritories.RemoveAll(delegate(ClanTerritory t) { if (t.FreebornOwnerId == 0L && !ProtectedWorldZones.IsProtected(t)) { if (t.Type == TerritoryType.Capital) { ConfigEntry allowCapitalTransfer = ClanConfig.AllowCapitalTransfer; if (allowCapitalTransfer == null) { return true; } return !allowCapitalTransfer.Value; } return false; } return true; }); List clanTerritories2 = data.GetClanTerritories(selectedTarget.ClanId); clanTerritories2.RemoveAll(delegate(ClanTerritory t) { if (t.FreebornOwnerId == 0L && !ProtectedWorldZones.IsProtected(t)) { if (t.Type == TerritoryType.Capital) { ConfigEntry allowCapitalTransfer = ClanConfig.AllowCapitalTransfer; if (allowCapitalTransfer == null) { return true; } return !allowCapitalTransfer.Value; } return false; } return true; }); GUILayout.BeginHorizontal(Array.Empty()); GUILayout.BeginVertical(_boxStyle, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(520f) }); GUILayout.Label("Your Offer", _labelGoldStyle, Array.Empty()); GUILayout.Label("Territory IDs, separated by space or semicolon. Keep each ID as x,y:", _labelStyle, Array.Empty()); _exchangeYourTerritories = GUILayout.TextField(_exchangeYourTerritories, 160, Array.Empty()); GUILayout.Label("Coins from your Clan Treasury:", _labelStyle, Array.Empty()); _exchangeYourCoins = GUILayout.TextField(_exchangeYourCoins, 12, Array.Empty()); GUILayout.Label("Resources from your Clan Vault, example Wood:200,Stone:100", _labelStyle, Array.Empty()); _exchangeYourResources = GUILayout.TextField(_exchangeYourResources, 160, Array.Empty()); GUILayout.Label("Your eligible territories", _labelGoldStyle, Array.Empty()); foreach (ClanTerritory item in clanTerritories) { GUILayout.Label(WarfareData.GridToKey(item.GridX, item.GridY) + " | " + item.DisplayName + " | " + item.Type, _labelStyle, Array.Empty()); } GUILayout.EndVertical(); GUILayout.BeginVertical(_boxStyle, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(520f) }); GUILayout.Label("Their Offer - " + selectedTarget.Name + "", _labelGoldStyle, Array.Empty()); GUILayout.Label("Territory IDs requested from target, separated by space or semicolon:", _labelStyle, Array.Empty()); _exchangeTheirTerritories = GUILayout.TextField(_exchangeTheirTerritories, 160, Array.Empty()); GUILayout.Label("Coins requested from target Clan Treasury:", _labelStyle, Array.Empty()); _exchangeTheirCoins = GUILayout.TextField(_exchangeTheirCoins, 12, Array.Empty()); GUILayout.Label("Resources requested from target Clan Vault:", _labelStyle, Array.Empty()); _exchangeTheirResources = GUILayout.TextField(_exchangeTheirResources, 160, Array.Empty()); GUILayout.Label("Their eligible territories", _labelGoldStyle, Array.Empty()); foreach (ClanTerritory item2 in clanTerritories2) { GUILayout.Label(WarfareData.GridToKey(item2.GridX, item2.GridY) + " | " + item2.DisplayName + " | " + item2.Type, _labelStyle, Array.Empty()); } GUILayout.EndVertical(); GUILayout.EndHorizontal(); GUI.enabled = info.Rank == ClanRank.Jarl; if (GUILayout.Button("Offer Exchange Deal", _buttonStyle, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Height(38f) })) { string payload = string.Join("|", _exchangeYourTerritories, _exchangeTheirTerritories, _exchangeYourCoins, _exchangeTheirCoins, _exchangeYourResources, _exchangeTheirResources); WarfareRPC.Instance?.RequestTerritoryExchange(selectedTarget.ClanId, payload); } GUI.enabled = true; DrawPendingTerritoryDeals(data, info); } private int DrawDropdown(string label, int selected, List options, ref bool open, float width) { if (options == null || options.Count == 0) { GUILayout.Label("No " + label + " available", _labelStyle, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(width) }); return 0; } selected = Mathf.Clamp(selected, 0, options.Count - 1); if (GUILayout.Button(options[selected] + (open ? " ^" : " v"), _smallButtonStyle, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(width) })) { open = !open; } if (open) { GUILayout.BeginVertical(_boxStyle, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(width) }); for (int i = 0; i < options.Count; i++) { if (GUILayout.Button((i == selected) ? ("[" + options[i] + "]") : options[i], (i == selected) ? _tabActiveStyle : _smallButtonStyle, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(width - 8f) })) { selected = i; open = false; } } GUILayout.EndVertical(); } return selected; } private void DrawPendingTerritoryDeals(WarfareData data, PlayerClanInfo info) { //IL_0021: Unknown result type (might be due to invalid IL or missing references) //IL_0040: Unknown result type (might be due to invalid IL or missing references) //IL_0045: Unknown result type (might be due to invalid IL or missing references) GUILayout.Space(6f); GUILayout.Label("Pending Territory Deals", _labelGoldStyle, Array.Empty()); _pendingTerritoryDealScroll = GUILayout.BeginScrollView(_pendingTerritoryDealScroll, false, false, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Height(96f) }); bool flag = false; foreach (TerritoryTransferOffer territoryTransferOffer in data.TerritoryTransferOffers) { if (territoryTransferOffer.Status != TerritoryTransferStatus.Pending || (territoryTransferOffer.FromClanId != info.ClanId && territoryTransferOffer.ToClanId != info.ClanId)) { continue; } flag = true; Clan clan = data.GetClan(territoryTransferOffer.FromClanId); Clan clan2 = data.GetClan(territoryTransferOffer.ToClanId); ClanTerritory value; ClanTerritory clanTerritory = (data.Territories.TryGetValue(territoryTransferOffer.TerritoryId ?? "", out value) ? value : null); bool flag2 = ProtectedWorldZones.IsProtected(clanTerritory); GUILayout.BeginHorizontal(_boxStyle, Array.Empty()); GUILayout.Label(DealSummary(territoryTransferOffer, clan, clan2, clanTerritory) + (flag2 ? " | Protected World Zone" : ""), _labelStyle, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(760f) }); if (flag2) { GUILayout.Label("Blocked", _labelStyle, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(76f) }); } else if (territoryTransferOffer.ToClanId == info.ClanId) { if (GUILayout.Button("Accept", _smallButtonStyle, (GUILayoutOption[])(object)new GUILayoutOption[2] { GUILayout.Width(92f), GUILayout.Height(36f) })) { WarfareRPC.Instance?.RequestDiplomacyV1(42, 0, territoryTransferOffer.OfferId, "", 0L); } if (GUILayout.Button("Decline", _smallButtonStyle, (GUILayoutOption[])(object)new GUILayoutOption[2] { GUILayout.Width(92f), GUILayout.Height(36f) })) { WarfareRPC.Instance?.RequestDiplomacyV1(43, 0, territoryTransferOffer.OfferId, "", 0L); } } else if (GUILayout.Button("Cancel", _smallButtonStyle, (GUILayoutOption[])(object)new GUILayoutOption[2] { GUILayout.Width(92f), GUILayout.Height(36f) })) { WarfareRPC.Instance?.RequestDiplomacyV1(44, 0, territoryTransferOffer.OfferId, "", 0L); } GUILayout.EndHorizontal(); } if (!flag) { DrawEmptyState("No pending territory deals.", "Gift and sale offers will appear here."); } GUILayout.EndScrollView(); } private string DealSummary(TerritoryTransferOffer offer, Clan from, Clan to, ClanTerritory legacyTerritory) { if (offer.DealType != TerritoryDealType.Exchange) { return (legacyTerritory?.DisplayName ?? offer.TerritoryId) + " | " + (from?.Name ?? "?") + " -> " + (to?.Name ?? "?") + " | " + offer.Price + " coins"; } return "Exchange | " + (from?.Name ?? "?") + " offers [" + string.Join(",", (offer.FromTerritoryIds ?? new List()).ToArray()) + "] +" + offer.FromCoins + "c " + ResourceSummary(offer.FromResources) + " -> " + (to?.Name ?? "?") + " offers [" + string.Join(",", (offer.ToTerritoryIds ?? new List()).ToArray()) + "] +" + offer.ToCoins + "c " + ResourceSummary(offer.ToResources); } private string ResourceSummary(List resources) { if (resources == null || resources.Count == 0) { return ""; } List list = new List(); foreach (ClanVaultItem resource in resources) { if (resource != null && resource.Amount > 0 && !string.IsNullOrEmpty(resource.PrefabName)) { list.Add(resource.PrefabName + "x" + resource.Amount); } } if (list.Count != 0) { return "[" + string.Join(",", list.ToArray()) + "]"; } return ""; } private void DrawVault(WarfareData data, PlayerClanInfo info, Clan clan) { //IL_0087: Unknown result type (might be due to invalid IL or missing references) //IL_00a0: Unknown result type (might be due to invalid IL or missing references) //IL_00a5: Unknown result type (might be due to invalid IL or missing references) bool flag = WarfareManager.Instance.IsVaultOpen(); GUILayout.Label(flag ? "VAULT OPEN Ä‚Ă\u02d8€žĂË\u0098Ă\u02d8‚¬ĹˇÄ‚ËÂ\u0098Ä‚Ë\u0098Ă\u02d8€šÂ¬ÄąÄľÄ‚Ă\u02d8€žĂ„Ă\u02d8€¦Ä‚Ă\u02d8€žĂË\u0098Ă\u02d8‚¬Â¦Ă„Ă\u02d8€šĂË\u0098Ă\u02d8‚¬ĹľÄ‚Ă\u02d8€žĂË\u0098Ă\u02d8‚¬Â¦Ă„Ă\u02d8€šĂË\u0098Ă\u02d8‚¬ĹľÄ‚Ă\u02d8€ąÄąÄ„Ä‚Ă\u02d8€žĂË\u0098Ă\u02d8‚¬ĹˇÄ‚ËÂ\u0098Ä‚Ë\u0098Ă\u02d8€šÂ¬Ă„Ă\u02d8€¦Ä‚Ă\u02d8€žĂ„Ă\u02d8€¦Ä‚Ă\u02d8€žĂË\u0098Ă\u02d8‚¬Ĺľ withdraw now!" : "Vault closed. Opens on the scheduled day/hour.", _labelGoldStyle, Array.Empty()); List vault = data.GetVault(info.ClanId); int num = ((clan != null) ? Economy.VaultCapacity(clan, data.GetClanTerritoryCount(info.ClanId)) : 0); GUILayout.Label($"{vault.Count}/{num} stacks stored.", _labelStyle, Array.Empty()); GUILayout.Space(4f); _scrollA = GUILayout.BeginScrollView(_scrollA, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.ExpandHeight(true) }); if (vault.Count == 0) { GUILayout.Label("The vault is empty. Raiders and Gatherers fill it by farming enemy land.", _labelStyle, Array.Empty()); } foreach (ClanVaultItem item in vault) { GUILayout.Label(" " + WarfareRPC.CleanItemDisplayName(item.DisplayName) + $" x{item.Amount}", _labelStyle, Array.Empty()); } GUILayout.EndScrollView(); if (flag && vault.Count > 0 && GUILayout.Button("Withdraw All Items", _buttonStyle, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Height(38f) })) { WarfareRPC.Instance?.RequestWithdrawVault(info.ClanId); } } private void DrawVaultClarity(WarfareData data, PlayerClanInfo info, Clan clan) { bool flag = WarfareManager.Instance.IsVaultOpen(); List vault = data.GetVault(info.ClanId); int num = ((clan != null) ? Economy.VaultCapacity(clan, data.GetClanTerritoryCount(info.ClanId)) : 0); WarfareManager.Instance?.EnsureVaultPermissions(info); string message; bool flag2 = IsVaultLocationValid(info, coinDeposit: false, out message); bool num2 = HasRankUI(info, ClanConfig.VaultViewMinRank?.Value ?? "Member"); bool flag3 = (WarfareManager.Instance?.CanDepositVaultItems(info) ?? false) && flag && flag2 && (vault.Count < num || vault.Find((ClanVaultItem v) => v.PrefabName == _selectedInventoryVaultPrefab) != null); bool flag4 = (WarfareManager.Instance?.CanWithdrawVaultItems(info) ?? false) && flag && flag2; bool flag5 = WarfareManager.Instance?.CanDepositVaultCoins(info) ?? false; bool flag6 = (WarfareManager.Instance?.CanWithdrawVaultCoins(info) ?? false) && flag && flag2; if (!num2) { GUILayout.Label("You do not have permission to view the vault.", _labelStyle, Array.Empty()); return; } float num3 = AvailableContentWidth(); float num4 = Mathf.Clamp(num3 * 0.29f, 240f, 360f); float num5 = Mathf.Clamp(num3 * 0.22f, 210f, 280f); float num6 = Mathf.Max(300f, num3 - num4 - num5 - 20f); float num7 = Mathf.Max(560f, ((Rect)(ref _windowRect)).height - 370f); float num8 = Mathf.Max(336f, num7 - 190f); float slotSize = Mathf.Clamp(Mathf.Floor(Mathf.Min((num4 - 44f) / 7f, (num8 - 32f) / 9f)), 38f, 58f); float slotSize2 = Mathf.Clamp(Mathf.Floor(Mathf.Min((num6 - 62f) / 12f, (num8 - 32f) / 9f)), 38f, 58f); GUILayout.BeginVertical((GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Height(num7) }); GUILayout.BeginHorizontal((GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Height(num7) }); GUILayout.BeginVertical(_boxStyle, (GUILayoutOption[])(object)new GUILayoutOption[2] { GUILayout.Width(num4), GUILayout.Height(num7) }); GUILayout.Label("Player Inventory", _labelGoldStyle, Array.Empty()); GUI.enabled = true; DrawInventoryVaultPicker(compact: true, slotSize); GUILayout.FlexibleSpace(); DrawSelectedVaultItem("SELECTED INVENTORY ITEM", _selectedInventoryVaultName, _selectedInventoryVaultStack); GUILayout.BeginHorizontal(Array.Empty()); GUILayout.Label("Qty", _labelStyle, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(36f) }); _vaultItemAmountInput = GUILayout.TextField(_vaultItemAmountInput, 4, (GUILayoutOption[])(object)new GUILayoutOption[2] { GUILayout.Width(54f), GUILayout.Height(30f) }); DrawVaultQuantityButtons(ref _vaultItemAmountInput, _selectedInventoryVaultStack, ref _vaultItemQuantityStep); GUILayout.EndHorizontal(); int result; int num9 = (int.TryParse(_vaultItemAmountInput, out result) ? result : 0); if (_selectedInventoryVaultStack > 0) { num9 = Mathf.Clamp(num9, 1, _selectedInventoryVaultStack); _vaultItemAmountInput = num9.ToString(); } GUILayout.BeginHorizontal(Array.Empty()); GUILayout.FlexibleSpace(); GUI.enabled = flag3 && !string.IsNullOrEmpty(_selectedInventoryVaultPrefab) && num9 > 0 && num9 <= _selectedInventoryVaultStack; if (GUILayout.Button("Deposit Item", _buttonStyle, (GUILayoutOption[])(object)new GUILayoutOption[2] { GUILayout.Width(126f), GUILayout.Height(30f) })) { WarfareRPC.Instance?.DepositSpecificInventoryItemToClanVault(info.ClanId, _selectedInventoryVaultPrefab, _selectedInventoryVaultName, num9, _selectedInventoryVaultSlotX, _selectedInventoryVaultSlotY); ClearInventoryVaultSelection(); } GUI.enabled = true; if (GUILayout.Button("Sort", _smallButtonStyle, (GUILayoutOption[])(object)new GUILayoutOption[2] { GUILayout.Width(72f), GUILayout.Height(30f) })) { _sortInventoryVaultDisplay = true; _inventoryVaultPage = 0; } GUILayout.FlexibleSpace(); GUILayout.EndHorizontal(); GUILayout.EndVertical(); GUILayout.Space(8f); GUILayout.BeginVertical(_boxStyle, (GUILayoutOption[])(object)new GUILayoutOption[3] { GUILayout.Width(num6), GUILayout.Height(num7), GUILayout.ExpandWidth(false) }); GUILayout.Label("Clan Vault Contents", _labelGoldStyle, Array.Empty()); DrawVaultContentsGrid(vault, freeborn: false, wideLayout: true, slotSize2); GUILayout.FlexibleSpace(); DrawSelectedVaultItem("SELECTED VAULT ITEM", string.IsNullOrEmpty(_selectedVaultPrefab) ? "" : WarfareRPC.CleanItemDisplayName(_selectedVaultPrefab), GetSelectedVaultAmount(vault)); GUILayout.BeginHorizontal(Array.Empty()); GUILayout.Label("Qty", _labelStyle, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(36f) }); _vaultWithdrawAmountInput = GUILayout.TextField(_vaultWithdrawAmountInput, 4, (GUILayoutOption[])(object)new GUILayoutOption[2] { GUILayout.Width(54f), GUILayout.Height(30f) }); DrawVaultQuantityButtons(ref _vaultWithdrawAmountInput, GetSelectedVaultAmount(vault), ref _vaultWithdrawQuantityStep); GUILayout.EndHorizontal(); int result2; int num10 = (int.TryParse(_vaultWithdrawAmountInput, out result2) ? result2 : 0); int selectedVaultAmount = GetSelectedVaultAmount(vault); if (selectedVaultAmount > 0) { num10 = Mathf.Clamp(num10, 1, selectedVaultAmount); _vaultWithdrawAmountInput = num10.ToString(); } GUILayout.BeginHorizontal(Array.Empty()); GUILayout.FlexibleSpace(); GUI.enabled = flag4 && !string.IsNullOrEmpty(_selectedVaultPrefab) && num10 > 0; if (GUILayout.Button("Withdraw Item", _buttonStyle, (GUILayoutOption[])(object)new GUILayoutOption[2] { GUILayout.Width(126f), GUILayout.Height(30f) })) { WarfareRPC.Instance?.RequestWithdrawVaultItem(info.ClanId, _selectedVaultPrefab, num10); _selectedVaultPrefab = ""; } GUI.enabled = true; if (GUILayout.Button("Sort", _smallButtonStyle, (GUILayoutOption[])(object)new GUILayoutOption[2] { GUILayout.Width(72f), GUILayout.Height(30f) })) { _sortClanVaultDisplay = true; _clanVaultPage = 0; } GUILayout.FlexibleSpace(); GUILayout.EndHorizontal(); GUILayout.EndVertical(); GUILayout.Space(8f); GUILayout.BeginVertical((GUILayoutOption[])(object)new GUILayoutOption[2] { GUILayout.Width(num5), GUILayout.Height(num7) }); DrawVaultSummary(flag, vault, num, clan, Mathf.Clamp(num5 - 34f, 150f, 220f)); GUILayout.Space(8f); GUILayout.BeginVertical(_boxStyle, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.ExpandHeight(true) }); GUILayout.Label("Permissions / Summary", _labelGoldStyle, Array.Empty()); DrawVaultPermissionSummary(flag5, flag3, flag6, flag4, compact: true); if (!flag2) { GUILayout.Label(message, _labelStyle, Array.Empty()); } if (!flag) { GUILayout.Label("Vault is closed. Withdrawals are blocked until it opens.", _labelStyle, Array.Empty()); } else if (!flag4) { GUILayout.Label("You do not have permission to withdraw from the vault.", _labelStyle, Array.Empty()); } if (vault.Count >= num) { GUILayout.Label("Vault is full.", _labelStyle, Array.Empty()); } GUILayout.Space(6f); GUILayout.Label("Coins", _labelGoldStyle, Array.Empty()); GUILayout.Label("Your coins: " + CountInventoryCoins() + "", _labelStyle, Array.Empty()); GUILayout.BeginHorizontal(Array.Empty()); GUILayout.Label("Amount:", _labelStyle, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(64f) }); _vaultCoinAmountInput = GUILayout.TextField(_vaultCoinAmountInput, 9, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(90f) }); GUILayout.EndHorizontal(); int result3; int num11 = (int.TryParse(_vaultCoinAmountInput, out result3) ? result3 : 0); string message2; bool flag7 = IsVaultLocationValid(info, coinDeposit: true, out message2); if (!flag7) { GUILayout.Label(message2, _labelStyle, Array.Empty()); } if (!(flag5 && flag7)) { goto IL_0887; } if (!flag) { ConfigEntry allowCoinDepositWhenVaultClosed = ClanConfig.AllowCoinDepositWhenVaultClosed; if (allowCoinDepositWhenVaultClosed == null || !allowCoinDepositWhenVaultClosed.Value) { goto IL_0887; } } int num12 = ((num11 > 0) ? 1 : 0); goto IL_088a; IL_0887: num12 = 0; goto IL_088a; IL_088a: GUI.enabled = (byte)num12 != 0; if (GUILayout.Button("Deposit Coins", _buttonStyle, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Height(38f) })) { WarfareRPC.Instance?.RequestDepositVaultCoins(info.ClanId, num11); } GUI.enabled = flag6 && num11 > 0 && clan != null && clan.Treasury >= num11; if (GUILayout.Button("Withdraw Coins", _buttonStyle, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Height(38f) })) { WarfareRPC.Instance?.RequestWithdrawVaultCoins(info.ClanId, num11); } GUI.enabled = true; GUILayout.EndVertical(); GUILayout.EndVertical(); GUILayout.EndHorizontal(); GUILayout.EndVertical(); } private void DrawVaultSummary(bool isOpen, List vault, int capacity, Clan clan, float progressBarWidth) { //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_00b2: Expected O, but got Unknown GUILayout.BeginVertical(_boxStyle, Array.Empty()); GUILayout.Label("Vault Summary", _labelGoldStyle, Array.Empty()); GUILayout.Label("Status: " + (isOpen ? "Open" : "Closed"), _labelStyle, Array.Empty()); GUILayout.Label("Schedule: " + VaultScheduleMode() + " Tier: " + (clan?.VaultTier ?? 0), _labelStyle, Array.Empty()); GUILayout.Label("TREASURY", _labelGoldStyle, Array.Empty()); GUIStyle val = new GUIStyle(_labelGoldStyle) { fontSize = 18 }; GUILayout.Label("" + TreasuryColored(clan?.Treasury ?? 0) + " Coins", val, Array.Empty()); GUILayout.Label("Vault Capacity", _labelGoldStyle, Array.Empty()); int count = vault.Count; int num = ((capacity > 0) ? Mathf.Clamp(Mathf.RoundToInt((float)count * 100f / (float)capacity), 0, 100) : 0); GUILayout.Label("Used slots: " + count + " / " + capacity + "", _labelStyle, Array.Empty()); GUILayout.Label(num + "% full", _labelStyle, Array.Empty()); DrawVaultCapacityBar(count, capacity, Mathf.Clamp(progressBarWidth, 150f, 220f), 14f); GUILayout.EndVertical(); } private void DrawVaultCapacityBar(int used, int capacity, float width, float height) { //IL_0037: Unknown result type (might be due to invalid IL or missing references) //IL_003c: Unknown result type (might be due to invalid IL or missing references) //IL_003d: Unknown result type (might be due to invalid IL or missing references) //IL_0042: Unknown result type (might be due to invalid IL or missing references) //IL_004c: 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_009e: Unknown result type (might be due to invalid IL or missing references) //IL_00ae: Unknown result type (might be due to invalid IL or missing references) //IL_00d2: Unknown result type (might be due to invalid IL or missing references) //IL_0102: Unknown result type (might be due to invalid IL or missing references) //IL_006f: Unknown result type (might be due to invalid IL or missing references) //IL_0068: Unknown result type (might be due to invalid IL or missing references) float num = ((capacity > 0) ? Mathf.Clamp01((float)used / (float)capacity) : 0f); Rect rect = GUILayoutUtility.GetRect(width, height, (GUILayoutOption[])(object)new GUILayoutOption[2] { GUILayout.Width(width), GUILayout.Height(height) }); Color color = GUI.color; GUI.color = ForgedPalette.MithrilDark; GUI.DrawTexture(rect, (Texture)(object)_pixelTex); GUI.color = ((num >= 1f) ? ForgedPalette.MeteoriteText : ((num >= 0.8f) ? ForgedPalette.MeteoriteBright : ForgedPalette.MithrilBorder)); GUI.DrawTexture(new Rect(((Rect)(ref rect)).x, ((Rect)(ref rect)).y, ((Rect)(ref rect)).width * num, ((Rect)(ref rect)).height), (Texture)(object)_pixelTex); GUI.color = ForgedPalette.SilverDark; GUI.DrawTexture(new Rect(((Rect)(ref rect)).x, ((Rect)(ref rect)).y, ((Rect)(ref rect)).width, 1f), (Texture)(object)_pixelTex); GUI.DrawTexture(new Rect(((Rect)(ref rect)).x, ((Rect)(ref rect)).yMax - 1f, ((Rect)(ref rect)).width, 1f), (Texture)(object)_pixelTex); GUI.color = color; } private void DrawSelectedVaultItem(string title, string itemName, int amount) { //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_0013: Unknown result type (might be due to invalid IL or missing references) //IL_001b: Expected O, but got Unknown GUIStyle val = new GUIStyle(_labelStyle) { fontSize = 14, wordWrap = true }; GUILayout.Space(4f); GUILayout.Label("" + title + "", _labelGoldStyle, Array.Empty()); GUILayout.Label(string.IsNullOrEmpty(itemName) ? "None" : ("" + itemName + " x" + amount + ""), val, Array.Empty()); } private void DrawVaultPermissionSummary(bool depositCoins, bool depositItems, bool withdrawCoins, bool withdrawItems, bool compact = false) { if (compact) { GUILayout.BeginVertical(_boxStyle, Array.Empty()); } else { GUILayout.BeginHorizontal(_boxStyle, Array.Empty()); } GUILayout.Label("Deposit: Coins " + VaultYesNo(depositCoins) + " | Items " + VaultYesNo(depositItems), _labelStyle, Array.Empty()); GUILayout.Label("Withdraw: Coins " + VaultYesNo(withdrawCoins) + " | Items " + VaultYesNo(withdrawItems), _labelStyle, Array.Empty()); if (compact) { GUILayout.EndVertical(); } else { GUILayout.EndHorizontal(); } } private static int VaultDisplayItemCategory(string prefabName) { //IL_00b0: Unknown result type (might be due to invalid IL or missing references) try { string text = (prefabName ?? string.Empty).Replace("(Clone)", string.Empty).Trim(); object? obj = ((object)ObjectDB.instance)?.GetType().GetMethod("GetItemPrefab", new Type[1] { typeof(string) })?.Invoke(ObjectDB.instance, new object[1] { text }); GameObject val = (GameObject)((obj is GameObject) ? obj : null); ItemDrop val2 = (((Object)(object)val != (Object)null) ? val.GetComponent() : null); return (!((Object)(object)val2 != (Object)null) || val2.m_itemData?.m_shared == null) ? int.MaxValue : ((int)val2.m_itemData.m_shared.m_itemType); } catch { return int.MaxValue; } } private static string VaultDisplayLocalizedName(string displayName) { string result = displayName ?? string.Empty; try { return result; } catch { return result; } } private void DrawVaultQuantityButtons(ref string amountInput, int availableAmount, ref int step) { int num = Mathf.Max(0, availableAmount); GUI.enabled = num > 0; int result; int num2 = ((!int.TryParse(amountInput, out result)) ? 1 : Mathf.Clamp(result, 1, Mathf.Max(1, num))); if (GUILayout.Button("[-]", _smallButtonStyle, (GUILayoutOption[])(object)new GUILayoutOption[2] { GUILayout.Width(34f), GUILayout.Height(30f) })) { num2 = Mathf.Max(1, num2 - step); amountInput = num2.ToString(); } if (GUILayout.Button("[+]", _smallButtonStyle, (GUILayoutOption[])(object)new GUILayoutOption[2] { GUILayout.Width(34f), GUILayout.Height(30f) })) { amountInput = Mathf.Min(num, num2 + step).ToString(); } if (GUILayout.Button("[1]", _smallButtonStyle, (GUILayoutOption[])(object)new GUILayoutOption[2] { GUILayout.Width(36f), GUILayout.Height(30f) })) { step = 1; amountInput = "1"; } if (GUILayout.Button("[10]", _smallButtonStyle, (GUILayoutOption[])(object)new GUILayoutOption[2] { GUILayout.Width(42f), GUILayout.Height(30f) })) { step = 10; amountInput = Mathf.Min(10, num).ToString(); } if (GUILayout.Button("[MAX]", _smallButtonStyle, (GUILayoutOption[])(object)new GUILayoutOption[2] { GUILayout.Width(54f), GUILayout.Height(30f) })) { amountInput = num.ToString(); } GUI.enabled = true; } private int GetSelectedVaultAmount(List vault) { if (vault == null || string.IsNullOrEmpty(_selectedVaultPrefab)) { return 0; } foreach (ClanVaultItem item in vault) { if (item != null && string.Equals(item.PrefabName, _selectedVaultPrefab, StringComparison.OrdinalIgnoreCase)) { return item.Amount; } } return 0; } private string VaultYesNo(bool value) { if (!value) { return "NO"; } return "YES"; } private void DrawInventoryVaultPicker(bool compact = false, float slotSize = 58f) { //IL_00fa: Unknown result type (might be due to invalid IL or missing references) //IL_0101: Expected O, but got Unknown //IL_0502: Unknown result type (might be due to invalid IL or missing references) //IL_0507: Unknown result type (might be due to invalid IL or missing references) //IL_050e: Unknown result type (might be due to invalid IL or missing references) //IL_0522: Unknown result type (might be due to invalid IL or missing references) //IL_0527: Unknown result type (might be due to invalid IL or missing references) //IL_052e: Unknown result type (might be due to invalid IL or missing references) //IL_0554: Unknown result type (might be due to invalid IL or missing references) //IL_0573: Unknown result type (might be due to invalid IL or missing references) //IL_0578: Unknown result type (might be due to invalid IL or missing references) Player localPlayer = Player.m_localPlayer; Inventory val = (((Object)(object)localPlayer != (Object)null) ? ((Humanoid)localPlayer).GetInventory() : null); if ((Object)(object)localPlayer == (Object)null || val == null) { GUILayout.Label("Inventory unavailable.", _labelStyle, Array.Empty()); return; } if (!(((object)val).GetType().GetMethod("GetAllItems", Type.EmptyTypes)?.Invoke(val, null) is IEnumerable enumerable)) { GUILayout.Label("Inventory list unavailable.", _labelStyle, Array.Empty()); return; } List list = new List(); foreach (object item in enumerable) { if (item == null) { continue; } Type type = item.GetType(); int num = (int)(type.GetField("m_stack")?.GetValue(item) ?? ((object)0)); object obj = type.GetField("m_dropPrefab")?.GetValue(item); GameObject val2 = (GameObject)((obj is GameObject) ? obj : null); string text = (((Object)(object)val2 != (Object)null) ? ((Object)val2).name : null); object obj2 = type.GetField("m_shared")?.GetValue(item); string text2 = obj2?.GetType().GetField("m_name")?.GetValue(obj2) as string; GetInventorySlot(item, out var x, out var y); if (!string.IsNullOrEmpty(text) && num > 0) { string text3 = WarfareRPC.CleanItemDisplayName(string.IsNullOrEmpty(text2) ? text : text2); if (!IsCoinInventoryEntry(text, text3, text2)) { VaultDebug("[FCW Vault] Inventory entry: prefab=" + text + " name=" + text3 + " stack=" + num + " slot=" + SlotText(x, y)); list.Add(new VaultInventoryEntry(text, text3, num, x, y, null)); } } } if (list.Count == 0) { GUILayout.Label("No inventory items found.", _labelStyle, Array.Empty()); return; } if (_sortInventoryVaultDisplay) { list = list.OrderBy((VaultInventoryEntry entry) => VaultDisplayItemCategory(entry.PrefabName)).ThenBy((VaultInventoryEntry entry) => VaultDisplayLocalizedName(entry.DisplayName), StringComparer.CurrentCultureIgnoreCase).ThenBy((VaultInventoryEntry entry) => entry.PrefabName ?? string.Empty, StringComparer.OrdinalIgnoreCase) .ThenBy((VaultInventoryEntry entry) => entry.Amount) .ToList(); } VaultInventoryEntry vaultInventoryEntry = list.Find((VaultInventoryEntry entry) => entry.PrefabName == _selectedInventoryVaultPrefab && entry.SlotX == _selectedInventoryVaultSlotX && entry.SlotY == _selectedInventoryVaultSlotY); if (vaultInventoryEntry == null) { vaultInventoryEntry = list[0]; _selectedInventoryVaultPrefab = vaultInventoryEntry.PrefabName; _selectedInventoryVaultName = vaultInventoryEntry.DisplayName; _selectedInventoryVaultStack = vaultInventoryEntry.Amount; _selectedInventoryVaultSlotX = vaultInventoryEntry.SlotX; _selectedInventoryVaultSlotY = vaultInventoryEntry.SlotY; } else { _selectedInventoryVaultStack = vaultInventoryEntry.Amount; } if (compact) { int num2 = Mathf.Max(1, Mathf.CeilToInt((float)list.Count / 63f)); _inventoryVaultPage = Mathf.Clamp(_inventoryVaultPage, 0, num2 - 1); int num3 = _inventoryVaultPage * 63; for (int num4 = 0; num4 < 9; num4++) { GUILayout.BeginHorizontal(Array.Empty()); GUILayout.FlexibleSpace(); for (int num5 = 0; num5 < 7; num5++) { int num6 = num3 + num4 * 7 + num5; if (num6 < list.Count) { VaultInventoryEntry vaultInventoryEntry2 = list[num6]; bool selected = vaultInventoryEntry2.PrefabName == _selectedInventoryVaultPrefab && vaultInventoryEntry2.SlotX == _selectedInventoryVaultSlotX && vaultInventoryEntry2.SlotY == _selectedInventoryVaultSlotY; ResolvedItemIcon inventoryItemTexture = GetInventoryItemTexture(vaultInventoryEntry2.PrefabName, vaultInventoryEntry2.DisplayName, vaultInventoryEntry2.Icon); if (DrawVaultIconButton(inventoryItemTexture, vaultInventoryEntry2.DisplayName, vaultInventoryEntry2.Amount, selected, slotSize)) { _selectedInventoryVaultPrefab = vaultInventoryEntry2.PrefabName; _selectedInventoryVaultName = vaultInventoryEntry2.DisplayName; _selectedInventoryVaultStack = vaultInventoryEntry2.Amount; _selectedInventoryVaultSlotX = vaultInventoryEntry2.SlotX; _selectedInventoryVaultSlotY = vaultInventoryEntry2.SlotY; } } else { DrawVaultIconButton(null, string.Empty, 0, selected: false, slotSize, empty: true); } } GUILayout.FlexibleSpace(); GUILayout.EndHorizontal(); } DrawVaultPager(ref _inventoryVaultPage, num2); return; } int num7 = ((!compact) ? 1 : 3); int num8 = Mathf.CeilToInt((float)list.Count / (float)num7); new GUIStyle(_smallButtonStyle) { wordWrap = true, alignment = (TextAnchor)4, fontSize = 12 }; new GUIStyle(_tabActiveStyle) { wordWrap = true, alignment = (TextAnchor)4, fontSize = 12 }; bool flag = compact && list.Count > 15; if (flag) { _scrollInventoryVault = GUILayout.BeginScrollView(_scrollInventoryVault, false, false, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Height(300f) }); } GUILayout.BeginHorizontal(Array.Empty()); for (int num9 = 0; num9 < num7; num9++) { GUILayout.BeginVertical(Array.Empty()); for (int num10 = 0; num10 < num8; num10++) { int num11 = num9 * num8 + num10; if (num11 >= list.Count) { break; } VaultInventoryEntry vaultInventoryEntry3 = list[num11]; bool selected2 = vaultInventoryEntry3.PrefabName == _selectedInventoryVaultPrefab && vaultInventoryEntry3.SlotX == _selectedInventoryVaultSlotX && vaultInventoryEntry3.SlotY == _selectedInventoryVaultSlotY; ResolvedItemIcon inventoryItemTexture2 = GetInventoryItemTexture(vaultInventoryEntry3.PrefabName, vaultInventoryEntry3.DisplayName, vaultInventoryEntry3.Icon); if (DrawVaultIconButton(inventoryItemTexture2, CompactVaultItemLabel(vaultInventoryEntry3.DisplayName, vaultInventoryEntry3.Amount), vaultInventoryEntry3.Amount, selected2)) { _selectedInventoryVaultPrefab = vaultInventoryEntry3.PrefabName; _selectedInventoryVaultName = vaultInventoryEntry3.DisplayName; _selectedInventoryVaultStack = vaultInventoryEntry3.Amount; _selectedInventoryVaultSlotX = vaultInventoryEntry3.SlotX; _selectedInventoryVaultSlotY = vaultInventoryEntry3.SlotY; string[] obj3 = new string[8] { "[FCW Vault] selected item prefab/name/stack prefab=", vaultInventoryEntry3.PrefabName, " name=", vaultInventoryEntry3.DisplayName, " stack=", null, null, null }; int amount = vaultInventoryEntry3.Amount; obj3[5] = amount.ToString(); obj3[6] = " slot="; obj3[7] = SlotText(vaultInventoryEntry3.SlotX, vaultInventoryEntry3.SlotY); VaultDebug(string.Concat(obj3)); } } GUILayout.EndVertical(); } GUILayout.EndHorizontal(); if (flag) { GUILayout.EndScrollView(); } } private static string CompactVaultItemLabel(string itemName, int amount) { return (itemName ?? "Item") + " x" + amount; } private void DrawVaultPager(ref int page, int pageCount) { if (pageCount > 1) { GUILayout.BeginHorizontal(Array.Empty()); GUI.enabled = page > 0; if (GUILayout.Button("Previous", _smallButtonStyle, (GUILayoutOption[])(object)new GUILayoutOption[2] { GUILayout.Width(82f), GUILayout.Height(30f) })) { page--; } GUI.enabled = true; GUILayout.Label("Page " + (page + 1) + " / " + pageCount, _labelStyle, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(88f) }); GUI.enabled = page + 1 < pageCount; if (GUILayout.Button("Next", _smallButtonStyle, (GUILayoutOption[])(object)new GUILayoutOption[2] { GUILayout.Width(82f), GUILayout.Height(30f) })) { page++; } GUI.enabled = true; GUILayout.EndHorizontal(); } } private void DrawFixedClanVaultGrid(List vault, float slotSize) { List list = (_sortClanVaultDisplay ? vault.OrderBy((ClanVaultItem item) => VaultDisplayItemCategory(item?.PrefabName)).ThenBy((ClanVaultItem item) => VaultDisplayLocalizedName(item?.DisplayName), StringComparer.CurrentCultureIgnoreCase).ThenBy((ClanVaultItem item) => item?.PrefabName ?? string.Empty, StringComparer.OrdinalIgnoreCase) .ThenBy((ClanVaultItem item) => item?.Amount ?? 0) .ToList() : vault); int num = Mathf.Max(1, Mathf.CeilToInt((float)list.Count / 108f)); _clanVaultPage = Mathf.Clamp(_clanVaultPage, 0, num - 1); int num2 = _clanVaultPage * 108; for (int num3 = 0; num3 < 9; num3++) { GUILayout.BeginHorizontal(Array.Empty()); GUILayout.FlexibleSpace(); for (int num4 = 0; num4 < 12; num4++) { int num5 = num2 + num3 * 12 + num4; if (num5 < list.Count) { ClanVaultItem clanVaultItem = list[num5]; string text = WarfareRPC.CleanItemDisplayName(string.IsNullOrEmpty(clanVaultItem.DisplayName) ? clanVaultItem.PrefabName : clanVaultItem.DisplayName); bool selected = string.Equals(_selectedVaultPrefab, clanVaultItem.PrefabName, StringComparison.OrdinalIgnoreCase); ResolvedItemIcon inventoryItemTexture = GetInventoryItemTexture(clanVaultItem.PrefabName, text); if (DrawVaultIconButton(inventoryItemTexture, text, clanVaultItem.Amount, selected, slotSize)) { _selectedVaultPrefab = clanVaultItem.PrefabName; } } else { DrawVaultIconButton(null, string.Empty, 0, selected: false, slotSize, empty: true); } } GUILayout.FlexibleSpace(); GUILayout.EndHorizontal(); } DrawVaultPager(ref _clanVaultPage, num); } private void DrawVaultContentsGrid(List vault, bool freeborn, bool wideLayout = false, float slotSize = 58f) { //IL_004b: Unknown result type (might be due to invalid IL or missing references) //IL_0043: Unknown result type (might be due to invalid IL or missing references) //IL_0050: Unknown result type (might be due to invalid IL or missing references) //IL_010e: Unknown result type (might be due to invalid IL or missing references) //IL_0127: Unknown result type (might be due to invalid IL or missing references) //IL_012c: Unknown result type (might be due to invalid IL or missing references) //IL_014e: Unknown result type (might be due to invalid IL or missing references) //IL_0153: Unknown result type (might be due to invalid IL or missing references) //IL_015a: Unknown result type (might be due to invalid IL or missing references) //IL_016e: Unknown result type (might be due to invalid IL or missing references) //IL_0173: Unknown result type (might be due to invalid IL or missing references) //IL_017a: Unknown result type (might be due to invalid IL or missing references) //IL_037e: Unknown result type (might be due to invalid IL or missing references) //IL_037f: Unknown result type (might be due to invalid IL or missing references) //IL_0376: Unknown result type (might be due to invalid IL or missing references) //IL_0377: Unknown result type (might be due to invalid IL or missing references) //IL_0288: Unknown result type (might be due to invalid IL or missing references) //IL_0289: 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_0103: Unknown result type (might be due to invalid IL or missing references) //IL_0108: Unknown result type (might be due to invalid IL or missing references) if (vault == null) { vault = new List(); } if (wideLayout) { DrawFixedClanVaultGrid(vault, slotSize); return; } if (vault.Count == 0) { GUILayout.Label(freeborn ? "The Homestead vault is empty." : "The vault is empty.", _labelStyle, Array.Empty()); return; } Vector2 val = (freeborn ? _scrollFreebornVaultContents : _scrollVaultContents); bool flag = wideLayout && vault.Count > 15; if (flag) { bool flag2 = false; string text = ""; foreach (ClanVaultItem item in vault) { if (item != null && !string.IsNullOrEmpty(item.PrefabName)) { if (string.Equals(item.PrefabName, _selectedVaultPrefab, StringComparison.OrdinalIgnoreCase)) { flag2 = true; } if (string.IsNullOrEmpty(text)) { text = item.PrefabName; } } } if (!flag2 && !string.IsNullOrEmpty(text)) { _selectedVaultPrefab = text; } val = GUILayout.BeginScrollView(val, false, false, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.ExpandHeight(true) }); } else if (!wideLayout) { val = GUILayout.BeginScrollView(val, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Height(132f) }); } if (wideLayout) { int num = 3; int num2 = Mathf.CeilToInt((float)vault.Count / (float)num); new GUIStyle(_smallButtonStyle) { wordWrap = true, alignment = (TextAnchor)4, fontSize = 12 }; new GUIStyle(_tabActiveStyle) { wordWrap = true, alignment = (TextAnchor)4, fontSize = 12 }; GUILayout.BeginHorizontal(Array.Empty()); for (int i = 0; i < num; i++) { GUILayout.BeginVertical(Array.Empty()); for (int j = 0; j < num2; j++) { int num3 = i * num2 + j; if (num3 >= vault.Count) { break; } ClanVaultItem clanVaultItem = vault[num3]; string text2 = WarfareRPC.CleanItemDisplayName(string.IsNullOrEmpty(clanVaultItem.DisplayName) ? clanVaultItem.PrefabName : clanVaultItem.DisplayName); bool selected = string.Equals(_selectedVaultPrefab, clanVaultItem.PrefabName, StringComparison.OrdinalIgnoreCase); ResolvedItemIcon inventoryItemTexture = GetInventoryItemTexture(clanVaultItem.PrefabName, text2); if (DrawVaultIconButton(inventoryItemTexture, text2 + " x" + clanVaultItem.Amount, clanVaultItem.Amount, selected)) { _selectedVaultPrefab = clanVaultItem.PrefabName; } } GUILayout.EndVertical(); } GUILayout.EndHorizontal(); if (flag) { GUILayout.EndScrollView(); _scrollVaultContents = val; } return; } int num4 = 3; for (int k = 0; k < vault.Count; k++) { if (!wideLayout && k % num4 == 0) { GUILayout.BeginHorizontal(Array.Empty()); } ClanVaultItem clanVaultItem2 = vault[k]; string text3 = WarfareRPC.CleanItemDisplayName(string.IsNullOrEmpty(clanVaultItem2.DisplayName) ? clanVaultItem2.PrefabName : clanVaultItem2.DisplayName); bool selected2 = string.Equals(_selectedVaultPrefab, clanVaultItem2.PrefabName, StringComparison.OrdinalIgnoreCase); ResolvedItemIcon inventoryItemTexture2 = GetInventoryItemTexture(clanVaultItem2.PrefabName, text3); if (DrawVaultIconButton(inventoryItemTexture2, text3 + " x" + clanVaultItem2.Amount, clanVaultItem2.Amount, selected2)) { _selectedVaultPrefab = clanVaultItem2.PrefabName; } if (k % num4 == num4 - 1 || k == vault.Count - 1) { GUILayout.EndHorizontal(); } } GUILayout.EndScrollView(); if (freeborn) { _scrollFreebornVaultContents = val; } else { _scrollVaultContents = val; } } private int CountInventoryCoins() { //IL_00ce: Unknown result type (might be due to invalid IL or missing references) //IL_00d5: Expected O, but got Unknown Player localPlayer = Player.m_localPlayer; Inventory val = (((Object)(object)localPlayer != (Object)null) ? ((Humanoid)localPlayer).GetInventory() : null); if ((Object)(object)localPlayer == (Object)null || val == null) { return 0; } if (!(((object)val).GetType().GetMethod("GetAllItems", Type.EmptyTypes)?.Invoke(val, null) is IEnumerable enumerable)) { return 0; } int num = 0; foreach (object item in enumerable) { if (item != null) { Type type = item.GetType(); int num2 = (int)(type.GetField("m_stack")?.GetValue(item) ?? ((object)0)); object obj = type.GetField("m_dropPrefab")?.GetValue(item); GameObject val2 = (GameObject)((obj is GameObject) ? obj : null); string text = (((Object)(object)val2 != (Object)null) ? ((Object)val2).name : ""); object obj2 = type.GetField("m_shared")?.GetValue(item); string text2 = obj2?.GetType().GetField("m_name")?.GetValue(obj2) as string; string displayName = WarfareRPC.CleanItemDisplayName(string.IsNullOrEmpty(text2) ? text : text2); if (num2 > 0 && IsCoinInventoryEntry(text, displayName, text2)) { num += num2; } } } return num; } private static bool IsCoinInventoryEntry(string prefabName, string displayName, string sharedName) { string b = (ClanConfig.CoinItemPrefab?.Value ?? "Coins").Trim(); if (!string.Equals(prefabName, b, StringComparison.OrdinalIgnoreCase) && !string.Equals(displayName, b, StringComparison.OrdinalIgnoreCase) && !string.Equals(sharedName, "$item_coins", StringComparison.OrdinalIgnoreCase)) { return string.Equals(displayName, "Coins", StringComparison.OrdinalIgnoreCase); } return true; } private static void GetInventorySlot(object item, out int x, out int y) { x = int.MinValue; y = int.MinValue; object obj = item?.GetType().GetField("m_gridPos", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic)?.GetValue(item); if (obj != null) { Type type = obj.GetType(); object obj2 = type.GetField("x", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic)?.GetValue(obj) ?? type.GetProperty("x", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic)?.GetValue(obj, null); object obj3 = type.GetField("y", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic)?.GetValue(obj) ?? type.GetProperty("y", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic)?.GetValue(obj, null); if (obj2 is int) { x = (int)obj2; } if (obj3 is int) { y = (int)obj3; } } } private static string SlotText(int x, int y) { if (x != int.MinValue && y != int.MinValue) { return x + "," + y; } return "unknown"; } private void ClearInventoryVaultSelection() { _selectedInventoryVaultPrefab = ""; _selectedInventoryVaultName = ""; _selectedInventoryVaultStack = 0; _selectedInventoryVaultSlotX = int.MinValue; _selectedInventoryVaultSlotY = int.MinValue; } private static void VaultDebug(string message) { ConfigEntry enableVaultDebugLogs = ClanConfig.EnableVaultDebugLogs; if (enableVaultDebugLogs != null && enableVaultDebugLogs.Value) { Plugin.Log.LogInfo((object)message); } } private bool IsVaultLocationValid(PlayerClanInfo info, bool coinDeposit, out string message) { //IL_0019: Unknown result type (might be due to invalid IL or missing references) //IL_0024: Expected O, but got Unknown //IL_0039: Unknown result type (might be due to invalid IL or missing references) //IL_003e: Unknown result type (might be due to invalid IL or missing references) //IL_0043: Unknown result type (might be due to invalid IL or missing references) //IL_004a: Unknown result type (might be due to invalid IL or missing references) message = ""; if ((Object)(object)Player.m_localPlayer == (Object)null || (Object)WarfareManager.Instance == (Object)null) { message = "Player position unavailable."; return false; } Vector2i grid = GridExtensions.WorldToGrid(((Component)Player.m_localPlayer).transform.position); return WarfareManager.Instance.CanUseVaultAt(info, grid, coinDeposit, out message); } private string VaultScheduleMode() { ConfigEntry vaultAlwaysOpen = ClanConfig.VaultAlwaysOpen; if (vaultAlwaysOpen != null && vaultAlwaysOpen.Value) { return "Always Open"; } ConfigEntry vaultUseIntervalSchedule = ClanConfig.VaultUseIntervalSchedule; if (vaultUseIntervalSchedule != null && vaultUseIntervalSchedule.Value) { return "Interval Schedule"; } return "Weekly Schedule"; } private string EmblemName(Clan clan) { if (clan == null || string.IsNullOrEmpty(clan.ClanEmblemName)) { return ClanConfig.DefaultEmblem?.Value ?? "emblem_001"; } return clan.ClanEmblemName; } private bool HasRankUI(PlayerClanInfo info, string minRank) { if (info == null) { return false; } if (info.Rank == ClanRank.Jarl) { return true; } ClanRank clanRank; try { clanRank = (ClanRank)Enum.Parse(typeof(ClanRank), minRank ?? "Member", ignoreCase: true); } catch { clanRank = ClanRank.Member; } return info.Rank >= clanRank; } private void DrawRankingStats(WarfareData data, PlayerClanInfo info, Clan clan) { DrawGlobalRankingStats(data); } private void DrawFreebornRankingStats(WarfareData data, PlayerClanInfo info) { DrawGlobalRankingStats(data); } private void DrawGlobalRankingStats(WarfareData data, int section) { List list = new List(data.Clans.Values); List list2 = new List(data.Players.Values); float num = ResponsiveCardWidth(AvailableContentWidth(), 3); GUILayout.Label("Rankings", _labelGoldStyle, Array.Empty()); GUILayout.BeginHorizontal(Array.Empty()); switch (section) { case 0: { list2.Sort((PlayerClanInfo a, PlayerClanInfo b) => b.PvPKills.CompareTo(a.PvPKills)); GUILayout.BeginVertical(_boxStyle, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(num) }); GUILayout.Label("Players by PvP Kills", _labelStyle, Array.Empty()); for (int num2 = 0; num2 < list2.Count && num2 < 10; num2++) { GUILayout.Label(num2 + 1 + ". " + list2[num2].PlayerName + " - " + list2[num2].PvPKills, _labelStyle, Array.Empty()); } if (list2.Count == 0) { GUILayout.Label("No player data.", _labelStyle, Array.Empty()); } GUILayout.EndVertical(); break; } case 1: list.Sort((Clan a, Clan b) => data.GetClanTerritoryCount(b.ClanId).CompareTo(data.GetClanTerritoryCount(a.ClanId))); GUILayout.BeginVertical(_boxStyle, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(num) }); DrawClanRanking("Clans by Territories", list, (Clan c) => data.GetClanTerritoryCount(c.ClanId)); GUILayout.EndVertical(); break; case 2: list.Sort((Clan a, Clan b) => data.GetClanTerritoryCount(b.ClanId).CompareTo(data.GetClanTerritoryCount(a.ClanId))); GUILayout.BeginVertical(_boxStyle, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(num) }); DrawClanRanking("Territories Held", list, (Clan c) => data.GetClanTerritoryCount(c.ClanId)); GUILayout.EndVertical(); break; default: list.Sort((Clan a, Clan b) => b.Treasury.CompareTo(a.Treasury)); GUILayout.BeginVertical(_boxStyle, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(num) }); DrawClanRanking("Clan Treasury", list, (Clan c) => c.Treasury); GUILayout.EndVertical(); list.Sort((Clan a, Clan b) => Economy.NetClanTick(data, b.ClanId).CompareTo(Economy.NetClanTick(data, a.ClanId))); GUILayout.BeginVertical(_boxStyle, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(num) }); DrawClanRanking("Net Income", list, (Clan c) => Economy.NetClanTick(data, c.ClanId)); GUILayout.EndVertical(); break; } GUILayout.FlexibleSpace(); GUILayout.EndHorizontal(); } private void DrawGlobalRankingStats(WarfareData data) { List list = new List(data.Clans.Values); list.Sort((Clan a, Clan b) => data.GetClanTerritoryCount(b.ClanId).CompareTo(data.GetClanTerritoryCount(a.ClanId))); List list2 = new List(data.Clans.Values); list2.Sort((Clan a, Clan b) => b.Treasury.CompareTo(a.Treasury)); List list3 = new List(data.Clans.Values); list3.Sort((Clan a, Clan b) => Economy.NetClanTick(data, b.ClanId).CompareTo(Economy.NetClanTick(data, a.ClanId))); List freebornPlayers = GetFreebornPlayers(data); freebornPlayers.Sort((PlayerClanInfo a, PlayerClanInfo b) => GetFreebornTerritories(data, b.PlayerId, TerritoryType.Outpost).Count.CompareTo(GetFreebornTerritories(data, a.PlayerId, TerritoryType.Outpost).Count)); List freebornPlayers2 = GetFreebornPlayers(data); freebornPlayers2.Sort((PlayerClanInfo a, PlayerClanInfo b) => b.FreebornTreasury.CompareTo(a.FreebornTreasury)); List freebornPlayers3 = GetFreebornPlayers(data); freebornPlayers3.Sort((PlayerClanInfo a, PlayerClanInfo b) => b.Bounty.CompareTo(a.Bounty)); int num = 0; int num2 = 0; int num3 = 0; foreach (ClanTerritory value in data.Territories.Values) { if (value.OwnerClanId != 0 || value.FreebornOwnerId != 0L) { num3++; } if (value.Type == TerritoryType.Homestead && value.FreebornOwnerId != 0L) { num++; } if (value.Type == TerritoryType.Outpost && value.FreebornOwnerId != 0L) { num2++; } } long num4 = 0L; long num5 = 0L; foreach (Clan value2 in data.Clans.Values) { num4 += value2.Treasury; num5 += Economy.NetClanTick(data, value2.ClanId); } foreach (PlayerClanInfo item in freebornPlayers2) { num4 += item.FreebornTreasury; List freebornTerritories = GetFreebornTerritories(data, item.PlayerId, TerritoryType.Homestead); freebornTerritories.AddRange(GetFreebornTerritories(data, item.PlayerId, TerritoryType.Outpost)); float globalMultiplier = Economy.GlobalUpkeepMultiplier(freebornTerritories.Count); foreach (ClanTerritory item2 in freebornTerritories) { num5 += Economy.TerritoryIncome(item2) - Economy.ApplyBuildingUpkeepReduction(item2, Economy.TerritoryUpkeep(item2, globalMultiplier)); } } GUILayout.Label("Global Ranking / Stats", _labelGoldStyle, Array.Empty()); GUILayout.BeginHorizontal(Array.Empty()); DrawSummaryCard("Total Clans", data.Clans.Count.ToString(), 170f); DrawSummaryCard("Total Freeborns", freebornPlayers2.Count.ToString(), 170f); DrawSummaryCard("Total Territories", num3.ToString(), 170f); DrawSummaryCard("Active Wars", CountActiveWars(data).ToString(), 170f); DrawSummaryCard("Total Treasury", num4 + "c", 170f); DrawSummaryCard("Server Net", ColoredCoins(num5) + "/tick", 170f); GUILayout.EndHorizontal(); GUILayout.BeginHorizontal(Array.Empty()); GUILayout.BeginVertical(_boxStyle, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(370f) }); GUILayout.Label("CLAN RANKINGS", _labelGoldStyle, Array.Empty()); DrawClanRanking("Top Clans by Territories", list, (Clan c) => data.GetClanTerritoryCount(c.ClanId)); DrawClanRanking("Top Clans by Treasury", list2, (Clan c) => c.Treasury); DrawClanRanking("Top Clans by Net Income", list3, (Clan c) => Economy.NetClanTick(data, c.ClanId)); GUILayout.EndVertical(); GUILayout.BeginVertical(_boxStyle, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(370f) }); GUILayout.Label("FREEBORN RANKINGS", _labelGoldStyle, Array.Empty()); DrawFreebornRanking("Top Freeborns by Outposts", freebornPlayers, (PlayerClanInfo p) => GetFreebornTerritories(data, p.PlayerId, TerritoryType.Outpost).Count); DrawFreebornRanking("Top Freeborns by Treasury", freebornPlayers2, (PlayerClanInfo p) => p.FreebornTreasury); DrawFreebornRanking("Top Freeborns by Bounties", freebornPlayers3, (PlayerClanInfo p) => p.Bounty); GUILayout.EndVertical(); GUILayout.BeginVertical(_boxStyle, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.ExpandWidth(true) }); GUILayout.Label("SERVER WARFARE STATS", _labelGoldStyle, Array.Empty()); DrawMiniStat("Active Wars", CountActiveWars(data).ToString()); DrawMiniStat("Wars Won/Lost", "0"); DrawMiniStat("Kills", "0"); DrawMiniStat("Deaths", "0"); DrawMiniStat("Captured Territories", "0"); GUILayout.Space(8f); GUILayout.Label("SERVER TOTALS", _labelGoldStyle, Array.Empty()); DrawMiniStat("Homesteads", num.ToString()); DrawMiniStat("Outposts", num2.ToString()); DrawMiniStat("Tribute Paid", "0"); DrawMiniStat("Upkeep Paid", "0"); GUILayout.EndVertical(); GUILayout.EndHorizontal(); } private void DrawSummaryCard(string title, string value, float width, UiTone tone = UiTone.Neutral) { GUILayout.BeginVertical(PanelStyle(tone), (GUILayoutOption[])(object)new GUILayoutOption[2] { GUILayout.Width(width), GUILayout.Height(68f) }); GUILayout.Label("" + title + "", CardTitleStyle(tone), Array.Empty()); GUILayout.Label("" + value + "", CardTitleStyle(tone), Array.Empty()); GUILayout.EndVertical(); } private void DrawClanRanking(string title, List clans, Func score) { GUILayout.Label("" + title + "", _labelStyle, Array.Empty()); for (int i = 0; i < clans.Count && i < 5; i++) { Clan clan = clans[i]; GUILayout.Label($"{i + 1}. {clan.Name} [{clan.Tag}] - {score(clan)}", _labelStyle, Array.Empty()); } if (clans.Count == 0) { GUILayout.Label("0", _labelStyle, Array.Empty()); } } private int CountActiveWars(WarfareData data) { int num = 0; foreach (War war in data.Wars) { if (war.IsActive) { num++; } } return num; } private void DrawFreebornRanking(string title, List players, Func score) { GUILayout.Label("" + title + "", _labelStyle, Array.Empty()); for (int i = 0; i < players.Count && i < 5; i++) { PlayerClanInfo playerClanInfo = players[i]; GUILayout.Label($"{i + 1}. {playerClanInfo.PlayerName} - {score(playerClanInfo)}", _labelStyle, Array.Empty()); } if (players.Count == 0) { GUILayout.Label("0", _labelStyle, Array.Empty()); } } private List GetFreebornPlayers(WarfareData data) { List list = new List(); foreach (PlayerClanInfo value in data.Players.Values) { if (value.IsFreeborn && value.ClanId == 0) { list.Add(value); } } return list; } private void DrawPublicClanProfile(WarfareData data, PlayerClanInfo viewer, Clan clan) { //IL_0048: Unknown result type (might be due to invalid IL or missing references) //IL_004d: Unknown result type (might be due to invalid IL or missing references) //IL_0416: Unknown result type (might be due to invalid IL or missing references) if (GUILayout.Button("← Back to Clans", _smallButtonStyle, (GUILayoutOption[])(object)new GUILayoutOption[2] { GUILayout.Width(180f), GUILayout.Height(26f) })) { _viewedPublicClan = null; _scrollDirectoryClans = _profileBackScroll; GUIUtility.ExitGUI(); } GUILayout.BeginHorizontal(_boxStyle, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.ExpandWidth(true) }); DrawDirectoryEmblem(clan.ClanEmblemName, 132f); GUILayout.BeginVertical(Array.Empty()); GUILayout.Label("" + clan.Name + " [" + clan.Tag + "]", _labelGoldStyle, Array.Empty()); PlayerClanInfo player = data.GetPlayer(clan.FounderId); PlayerClanInfo player2 = data.GetPlayer(clan.JarlId); ClanTerritory clanTerritory = data.GetClanTerritories(clan.ClanId).FirstOrDefault((ClanTerritory t) => t.Type == TerritoryType.Capital); DrawProfileLine("Founder", player?.PlayerName ?? "Unknown"); DrawProfileLine("Jarl", player2?.PlayerName ?? "Unknown"); DrawProfileLine("Members", data.GetClanMemberCount(clan.ClanId).ToString()); DrawProfileLine("Territories", data.GetClanTerritoryCount(clan.ClanId).ToString()); DrawProfileLine("Capital", clanTerritory?.DisplayName ?? "None"); DrawProfileLine("Last activity", PublicLastOnlineText(new PlayerClanInfo { LastSeenTimestamp = clan.LastActiveTimestamp }, online: false).Replace("Last online", "Last activity")); GUILayout.EndVertical(); GUILayout.EndHorizontal(); GUILayout.Label("PUBLIC RELATIONS", _labelGoldStyle, Array.Empty()); foreach (Clan item in from c in data.Clans.Values where c.ClanId != clan.ClanId orderby c.Name select c) { GUILayout.Label(item.Name + ": " + (WarfareManager.Instance?.GetDiplomacyState(clan.ClanId, item.ClanId) ?? DiplomacyState.Neutral), _labelStyle, Array.Empty()); } GUILayout.Label("MEMBERS", _labelGoldStyle, Array.Empty()); foreach (PlayerClanInfo item2 in from m in data.GetClanMembers(clan.ClanId) orderby m.Rank descending, m.PlayerName select m) { GUILayout.BeginHorizontal(_boxStyle, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.ExpandWidth(true) }); GUILayout.Label(item2.PlayerName + " — " + item2.Rank, _labelStyle, Array.Empty()); GUILayout.FlexibleSpace(); if (GUILayout.Button("View Profile", _smallButtonStyle, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(120f) })) { OpenPublicProfile(item2, "Clans", 11, _clansSubTab, _scrollDirectoryClans); GUIUtility.ExitGUI(); } GUILayout.EndHorizontal(); } } private void DrawClansDirectory(WarfareData data, PlayerClanInfo viewer) { //IL_003a: Unknown result type (might be due to invalid IL or missing references) //IL_0053: Unknown result type (might be due to invalid IL or missing references) //IL_0058: Unknown result type (might be due to invalid IL or missing references) GUILayout.Label("Clans", _labelGoldStyle, Array.Empty()); if (data.Clans.Count == 0) { GUILayout.Label("No clans founded.", _labelStyle, Array.Empty()); return; } _scrollDirectoryClans = GUILayout.BeginScrollView(_scrollDirectoryClans, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.ExpandHeight(true) }); List list = new List(data.Clans.Values); list.Sort((Clan a, Clan b) => string.Compare(a.Name, b.Name, StringComparison.OrdinalIgnoreCase)); float num = (((Rect)(ref _windowRect)).width - 96f) / 3f; for (int num2 = 0; num2 < list.Count; num2 += 3) { GUILayout.BeginHorizontal(Array.Empty()); DrawClanDirectoryCard(data, viewer, list[num2], num); if (num2 + 1 < list.Count) { DrawClanDirectoryCard(data, viewer, list[num2 + 1], num); } else { GUILayout.Space(num + 8f); } if (num2 + 2 < list.Count) { DrawClanDirectoryCard(data, viewer, list[num2 + 2], num); } else { GUILayout.Space(num + 8f); } GUILayout.EndHorizontal(); } GUILayout.EndScrollView(); } private void DrawClanDirectoryCard(WarfareData data, PlayerClanInfo viewer, Clan clan, float width) { //IL_0085: Unknown result type (might be due to invalid IL or missing references) //IL_01ef: Unknown result type (might be due to invalid IL or missing references) //IL_01f4: Unknown result type (might be due to invalid IL or missing references) string text = "Neutral"; if (viewer != null && viewer.ClanId == clan.ClanId) { text = "Your clan"; } else if (viewer != null && viewer.ClanId != 0) { text = (WarfareManager.Instance?.GetDiplomacyState(viewer.ClanId, clan.ClanId) ?? DiplomacyState.Neutral).ToString(); } GUILayout.BeginHorizontal(_boxStyle, (GUILayoutOption[])(object)new GUILayoutOption[2] { GUILayout.Width(width), GUILayout.MinHeight(162f) }); DrawClanColorBanner(clan.Color, 38f, 152f); DrawDirectoryEmblem(clan.ClanEmblemName, 132f); GUILayout.BeginVertical((GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(116f) }); GUILayout.Label("" + clan.Name + " [" + clan.Tag + "]", _labelGoldStyle, Array.Empty()); GUILayout.Label("Tag: " + clan.Tag, _labelStyle, Array.Empty()); GUILayout.Label($"Members: {data.GetClanMemberCount(clan.ClanId)}", _labelStyle, Array.Empty()); GUILayout.Label($"Territories: {data.GetClanTerritoryCount(clan.ClanId)}", _labelStyle, Array.Empty()); GUILayout.Label("Relation: " + text, _labelStyle, Array.Empty()); if (GUILayout.Button("View Clan", _smallButtonStyle, (GUILayoutOption[])(object)new GUILayoutOption[2] { GUILayout.Width(104f), GUILayout.Height(26f) })) { _viewedPublicClan = clan; _profileBackLabel = "Clans"; _profileBackSubTab = _clansSubTab; _profileBackScroll = _scrollDirectoryClans; GUIUtility.ExitGUI(); } GUILayout.EndVertical(); ConfigEntry enableClanLinks = ClanConfig.EnableClanLinks; if (enableClanLinks != null && enableClanLinks.Value) { DrawLinkSquareColumn(("Discord", clan.ClanDiscordUrl), ("Steam", clan.ClanSteamGroupUrl), ("Website", clan.ClanWebsiteUrl), ("Fehu Codex", clan.ClanCustomUrl), ("Custom Link", clan.ClanCustomUrl2)); } else { GUILayout.Space(50f); } GUILayout.EndHorizontal(); } private void DrawHomesteadsDirectory(WarfareData data, PlayerClanInfo viewer) { //IL_0090: Unknown result type (might be due to invalid IL or missing references) //IL_00a6: Unknown result type (might be due to invalid IL or missing references) //IL_00ab: Unknown result type (might be due to invalid IL or missing references) GUILayout.Label("Freeborn Homesteads", _labelGoldStyle, Array.Empty()); List list = (from t in data.Territories.Values where t.Type == TerritoryType.Homestead && t.FreebornOwnerId != 0 orderby t.DisplayName select t).ToList(); if (list.Count == 0) { DrawEmptyState("No Homesteads", "No Freeborn Homesteads have been claimed yet."); return; } HashSet onlineNames = ConnectedPlayerNames(); _scrollDirectoryFreeborn = GUILayout.BeginScrollView(_scrollDirectoryFreeborn, false, true, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.ExpandHeight(true) }); foreach (ClanTerritory item in list) { DrawHomesteadDirectoryCard(data, viewer, item, onlineNames); } GUILayout.Space(BottomSafePadding()); GUILayout.EndScrollView(); } private void DrawHomesteadDirectoryCard(WarfareData data, PlayerClanInfo viewer, ClanTerritory territory, HashSet onlineNames) { //IL_0235: Unknown result type (might be due to invalid IL or missing references) //IL_023a: Unknown result type (might be due to invalid IL or missing references) //IL_021c: Unknown result type (might be due to invalid IL or missing references) //IL_0248: Unknown result type (might be due to invalid IL or missing references) //IL_025a: Unknown result type (might be due to invalid IL or missing references) //IL_027c: Unknown result type (might be due to invalid IL or missing references) PlayerClanInfo player = data.GetPlayer(territory.FreebornOwnerId); int count = GetFreebornTerritories(data, territory.FreebornOwnerId, TerritoryType.Outpost).Count; GUILayout.BeginHorizontal(_boxStyle, (GUILayoutOption[])(object)new GUILayoutOption[2] { GUILayout.ExpandWidth(true), GUILayout.Height(108f) }); GUILayout.Box((Texture)(object)((player != null) ? GetProfileTexture(player) : null), _boxStyle, (GUILayoutOption[])(object)new GUILayoutOption[2] { GUILayout.Width(84f), GUILayout.Height(84f) }); GUILayout.BeginVertical((GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(300f) }); GUILayout.Label("" + (player?.PlayerName ?? territory.FreebornOwnerName ?? "Unknown") + "", _labelGoldStyle, Array.Empty()); GUILayout.Label((player != null) ? ProfileTitle(player, null) : "None", _labelStyle, Array.Empty()); GUILayout.Label("Homestead: " + (territory.DisplayName ?? "Unknown"), _labelStyle, Array.Empty()); GUILayout.EndVertical(); GUILayout.BeginVertical((GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(260f) }); GUILayout.Label("Biome: " + BiomeDisplayName(territory), _labelStyle, Array.Empty()); GUILayout.Label("Outposts: " + count, _labelStyle, Array.Empty()); if (player != null) { DrawServerBadgeBoxesCompact(player, 4); } GUILayout.EndVertical(); GUILayout.FlexibleSpace(); GUILayout.BeginVertical((GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(190f) }); GUILayout.Label((player != null) ? PublicLastOnlineText(player, IsPublicProfileOnline(player, onlineNames)) : "Last online: Unknown", _labelStyle, Array.Empty()); if (player != null && GUILayout.Button("View Profile", _smallButtonStyle, (GUILayoutOption[])(object)new GUILayoutOption[2] { GUILayout.Width(120f), GUILayout.Height(28f) })) { OpenPublicProfile(player, "Homesteads", 12, _homesteadsSubTab, _scrollDirectoryFreeborn); GUIUtility.ExitGUI(); } GUILayout.EndVertical(); GUILayout.EndHorizontal(); Rect lastRect = GUILayoutUtility.GetLastRect(); Event current = Event.current; if (player != null && current != null && (int)current.type == 0 && current.button == 0 && ((Rect)(ref lastRect)).Contains(current.mousePosition)) { current.Use(); OpenPublicProfile(player, "Homesteads", 12, _homesteadsSubTab, _scrollDirectoryFreeborn); GUIUtility.ExitGUI(); } } private void DrawServerBadgeBoxesCompact(PlayerClanInfo info, int maximum) { //IL_004e: Unknown result type (might be due to invalid IL or missing references) //IL_0059: Expected O, but got Unknown GUILayout.BeginHorizontal(Array.Empty()); foreach (string item in (WarfareManager.Instance?.GetServerBadges(info) ?? new List()).Take(maximum)) { Texture2D val = GetConstellationSmallBadgeTexture(item) ?? GetConstellationBadgeTexture(item); if ((Object)val != (Object)null) { GUILayout.Box((Texture)(object)val, _boxStyle, (GUILayoutOption[])(object)new GUILayoutOption[2] { GUILayout.Width(28f), GUILayout.Height(28f) }); } } GUILayout.EndHorizontal(); } private string GetFreebornRelationLabel(PlayerClanInfo viewer, ClanTerritory territory) { if (viewer == null || territory == null || territory.FreebornOwnerId == 0L) { return "None"; } if (viewer.PlayerId == territory.FreebornOwnerId) { return "Friendly"; } if (viewer.FreebornFriends != null && viewer.FreebornFriends.Contains(territory.FreebornOwnerId)) { return "Friendly"; } return "None"; } private string LinkLabel(string label, string fallback) { if (!string.IsNullOrEmpty(label)) { return label; } return fallback; } private void DrawClanColorBanner(Rect rect, Color color) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_0019: Unknown result type (might be due to invalid IL or missing references) //IL_0023: Unknown result type (might be due to invalid IL or missing references) //IL_002f: Unknown result type (might be due to invalid IL or missing references) //IL_0069: Unknown result type (might be due to invalid IL or missing references) Color color2 = GUI.color; GUI.color = new Color(0.86f, 0.62f, 0.24f, 1f); GUI.DrawTexture(rect, (Texture)(object)_pixelTex); GUI.color = color; GUI.DrawTexture(new Rect(((Rect)(ref rect)).x + 2f, ((Rect)(ref rect)).y + 2f, ((Rect)(ref rect)).width - 4f, ((Rect)(ref rect)).height - 4f), (Texture)(object)_pixelTex); GUI.color = color2; } private void DrawClanColorBanner(Color color, float width, float height) { //IL_001f: Unknown result type (might be due to invalid IL or missing references) //IL_0024: 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_003e: Unknown result type (might be due to invalid IL or missing references) //IL_0048: Unknown result type (might be due to invalid IL or missing references) //IL_0054: Unknown result type (might be due to invalid IL or missing references) //IL_008e: Unknown result type (might be due to invalid IL or missing references) Rect rect = GUILayoutUtility.GetRect(width, height, (GUILayoutOption[])(object)new GUILayoutOption[2] { GUILayout.Width(width), GUILayout.Height(height) }); Color color2 = GUI.color; GUI.color = new Color(0.86f, 0.62f, 0.24f, 1f); GUI.DrawTexture(rect, (Texture)(object)_pixelTex); GUI.color = color; GUI.DrawTexture(new Rect(((Rect)(ref rect)).x + 2f, ((Rect)(ref rect)).y + 2f, ((Rect)(ref rect)).width - 4f, ((Rect)(ref rect)).height - 4f), (Texture)(object)_pixelTex); GUI.color = color2; } private void DrawDirectoryEmblem(string emblemName, float size) { //IL_001f: Unknown result type (might be due to invalid IL or missing references) //IL_0024: Unknown result type (might be due to invalid IL or missing references) //IL_0029: Unknown result type (might be due to invalid IL or missing references) //IL_002a: Unknown result type (might be due to invalid IL or missing references) //IL_0041: Unknown result type (might be due to invalid IL or missing references) Rect rect = GUILayoutUtility.GetRect(size, size, (GUILayoutOption[])(object)new GUILayoutOption[2] { GUILayout.Width(size), GUILayout.Height(size) }); Color color = GUI.color; GUI.color = Color.white; GUI.DrawTexture(rect, (Texture)(object)LocalEmblemCache.GetTexture(emblemName), (ScaleMode)2, true); GUI.color = color; } private void DrawClanColorSquare(Color color, float size) { //IL_001f: Unknown result type (might be due to invalid IL or missing references) //IL_0024: 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_003e: Unknown result type (might be due to invalid IL or missing references) //IL_0048: Unknown result type (might be due to invalid IL or missing references) //IL_0054: Unknown result type (might be due to invalid IL or missing references) //IL_008e: Unknown result type (might be due to invalid IL or missing references) Rect rect = GUILayoutUtility.GetRect(size, size, (GUILayoutOption[])(object)new GUILayoutOption[2] { GUILayout.Width(size), GUILayout.Height(size) }); Color color2 = GUI.color; GUI.color = new Color(0.86f, 0.62f, 0.24f, 1f); GUI.DrawTexture(rect, (Texture)(object)_pixelTex); GUI.color = color; GUI.DrawTexture(new Rect(((Rect)(ref rect)).x + 2f, ((Rect)(ref rect)).y + 2f, ((Rect)(ref rect)).width - 4f, ((Rect)(ref rect)).height - 4f), (Texture)(object)_pixelTex); GUI.color = color2; } private void DrawLinkIcon(string label, string url) { if (GUILayout.Button((GUI.enabled = !string.IsNullOrEmpty(url)) ? ("" + label + "") : ("" + label + ""), _smallButtonStyle, (GUILayoutOption[])(object)new GUILayoutOption[2] { GUILayout.Width(50f), GUILayout.Height(50f) })) { Application.OpenURL(url); } GUI.enabled = true; } private void DrawFlatLinkButton(string label, string url) { if (GUILayout.Button((GUI.enabled = !string.IsNullOrEmpty(url)) ? label : ("" + label + ""), _smallButtonStyle, (GUILayoutOption[])(object)new GUILayoutOption[2] { GUILayout.Width(72f), GUILayout.Height(22f) })) { Application.OpenURL(url); } GUI.enabled = true; } private void DrawLinkSquareColumn(params (string label, string url)[] links) { GUILayout.BeginVertical((GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(50f) }); for (int i = 0; i < links.Length; i++) { DrawSquareLinkButton(links[i].label, links[i].url); } GUILayout.EndVertical(); } private void DrawLinkSquareGrid(params (string label, string url)[] links) { GUILayout.BeginVertical((GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(74f) }); for (int i = 0; i < links.Length; i += 2) { GUILayout.BeginHorizontal(Array.Empty()); DrawSquareLinkButton(links[i].label, links[i].url); if (i + 1 < links.Length) { DrawSquareLinkButton(links[i + 1].label, links[i + 1].url); } GUILayout.EndHorizontal(); } GUILayout.EndVertical(); } private void DrawSquareLinkButton(string label, string url) { if (GUILayout.Button((GUI.enabled = !string.IsNullOrEmpty(url)) ? ShortLinkLabel(label) : ("" + ShortLinkLabel(label) + ""), _smallButtonStyle, (GUILayoutOption[])(object)new GUILayoutOption[2] { GUILayout.Width(46f), GUILayout.Height(20f) })) { Application.OpenURL(url); } GUI.enabled = true; } private string ShortLinkLabel(string label) { switch (label) { case "Discord": return "Disc"; case "Steam": return "Stm"; case "Website": return "Web"; case "Fehu Hub": case "Fehu": case "Fehu Codex": return "Fehu"; case "Custom": case "Custom Link": return "Cust"; default: return label; } } private void DrawLinkButtonRow((string label, string url) a, (string label, string url) b, (string label, string url) c) { GUILayout.BeginHorizontal(Array.Empty()); if (!string.IsNullOrEmpty(a.label)) { DrawFlatLinkButton(a.label, a.url); } if (!string.IsNullOrEmpty(b.label)) { DrawFlatLinkButton(b.label, b.url); } if (!string.IsNullOrEmpty(c.label)) { DrawFlatLinkButton(c.label, c.url); } GUILayout.EndHorizontal(); } private void DrawAllIds(WarfareData data) { //IL_016f: Unknown result type (might be due to invalid IL or missing references) //IL_0188: 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) if (data == null || (data.Clans.Count == 0 && data.Players.Count == 0 && data.Territories.Count == 0)) { if (!_loggedAllIdsEmpty) { _loggedAllIdsEmpty = true; Plugin.Log.LogInfo((object)("[FCW AllIDs] clans=" + (data?.Clans.Count ?? 0) + " players=" + (data?.Players.Count ?? 0) + " territories=" + (data?.Territories.Count ?? 0))); } GUILayout.BeginVertical(_boxStyle, Array.Empty()); GUILayout.Label("All IDs", _labelGoldStyle, Array.Empty()); GUILayout.Label("No data available", _labelStyle, Array.Empty()); GUILayout.EndVertical(); return; } _loggedAllIdsEmpty = false; GUILayout.Label("All IDs", _labelGoldStyle, Array.Empty()); GUILayout.BeginHorizontal(Array.Empty()); AllIdsSectionButton(0, "Clan IDs"); AllIdsSectionButton(1, "Clan Member IDs"); AllIdsSectionButton(2, "Homestead IDs"); AllIdsSectionButton(3, "Freeborn IDs"); AllIdsSectionButton(4, "Player IDs / Steam IDs"); GUILayout.EndHorizontal(); _scrollIds = GUILayout.BeginScrollView(_scrollIds, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.ExpandHeight(true) }); GUILayout.BeginVertical(_boxStyle, Array.Empty()); switch (_allIdsSection) { case 0: DrawClanIdsSection(data); break; case 1: DrawClanMemberIdsSection(data); break; case 2: DrawHomesteadIdsSection(data); break; case 3: DrawFreebornIdsSection(data); break; case 4: DrawPlayerIdsSection(data); break; } GUILayout.EndVertical(); GUILayout.EndScrollView(); } private void AllIdsSectionButton(int section, string label) { //IL_003f: Unknown result type (might be due to invalid IL or missing references) //IL_0044: Unknown result type (might be due to invalid IL or missing references) if (GUILayout.Button(label, (_allIdsSection == section) ? _tabActiveStyle : _tabStyle, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Height(30f) })) { _allIdsSection = section; _scrollIds = Vector2.zero; } } private void DrawClanIdsSection(WarfareData data) { GUILayout.Label("Clan IDs", _labelGoldStyle, Array.Empty()); if (data.Clans.Count == 0) { GUILayout.Label("No clan IDs", _labelStyle, Array.Empty()); return; } foreach (Clan value in data.Clans.Values) { PlayerClanInfo player = data.GetPlayer(value.JarlId); GUILayout.BeginVertical(_boxStyle, Array.Empty()); GUILayout.Label("Clan Box", _labelGoldStyle, Array.Empty()); DrawMiniStat("Clan ID", value.ClanId.ToString()); DrawMiniStat("Name", value.Name); DrawMiniStat("Tag", value.Tag); DrawMiniStat("Jarl", (player != null) ? player.PlayerName : "Unknown"); DrawMiniStat("Jarl SteamID", value.JarlId.ToString()); foreach (ClanTerritory value2 in data.Territories.Values) { if (value2.OwnerClanId == value.ClanId) { DrawMiniStat("Territory", $"{value2.GridX},{value2.GridY} | {value2.DisplayName} | {value2.Type}"); } } GUILayout.EndVertical(); } } private void DrawClanMemberIdsSection(WarfareData data) { GUILayout.Label("Clan Member IDs", _labelGoldStyle, Array.Empty()); bool flag = false; foreach (PlayerClanInfo value in data.Players.Values) { if (value.ClanId != 0) { flag = true; GUILayout.BeginVertical(_boxStyle, Array.Empty()); GUILayout.Label("Member Box", _labelGoldStyle, Array.Empty()); DrawMiniStat("Name", value.PlayerName); DrawMiniStat("SteamID", value.PlayerId.ToString()); DrawMiniStat("Clan ID", value.ClanId.ToString()); DrawMiniStat("Role", RoleName(value.PrimaryRole) + "/" + RoleName(value.SecondaryRole)); DrawMiniStat("Rank", value.Rank.ToString()); GUILayout.EndVertical(); } } if (!flag) { GUILayout.Label("No clan member IDs", _labelStyle, Array.Empty()); } } private void DrawHomesteadIdsSection(WarfareData data) { GUILayout.Label("Homestead IDs", _labelGoldStyle, Array.Empty()); bool flag = false; foreach (ClanTerritory value in data.Territories.Values) { if (value.Type == TerritoryType.Homestead && value.FreebornOwnerId != 0L) { flag = true; PlayerClanInfo player = data.GetPlayer(value.FreebornOwnerId); GUILayout.BeginVertical(_boxStyle, Array.Empty()); GUILayout.Label("Homestead Box", _labelGoldStyle, Array.Empty()); DrawMiniStat("Homestead ID", $"{value.GridX},{value.GridY}"); DrawMiniStat("Name", value.DisplayName); DrawMiniStat("Owner", (player != null) ? player.PlayerName : value.FreebornOwnerName); DrawMiniStat("Owner SteamID", value.FreebornOwnerId.ToString()); GUILayout.EndVertical(); } } if (!flag) { GUILayout.Label("No Homestead IDs", _labelStyle, Array.Empty()); } } private void DrawFreebornIdsSection(WarfareData data) { GUILayout.Label("Freeborn IDs", _labelGoldStyle, Array.Empty()); bool flag = false; foreach (PlayerClanInfo value in data.Players.Values) { if (!value.IsFreeborn || value.ClanId != 0) { continue; } flag = true; GUILayout.BeginVertical(_boxStyle, Array.Empty()); GUILayout.Label("Freeborn Box", _labelGoldStyle, Array.Empty()); DrawMiniStat("Name", value.PlayerName); DrawMiniStat("SteamID", value.PlayerId.ToString()); foreach (ClanTerritory value2 in data.Territories.Values) { if (value2.FreebornOwnerId == value.PlayerId) { string label = ((value2.Type == TerritoryType.Homestead) ? "Homestead ID" : "Outpost ID"); DrawMiniStat(label, $"{value2.GridX},{value2.GridY} | {value2.DisplayName}"); } } GUILayout.EndVertical(); } if (!flag) { GUILayout.Label("No Freeborn IDs", _labelStyle, Array.Empty()); } } private void DrawPlayerIdsSection(WarfareData data) { GUILayout.Label("Player IDs / Steam IDs", _labelGoldStyle, Array.Empty()); if (data.Players.Count == 0) { GUILayout.Label("No player IDs", _labelStyle, Array.Empty()); return; } foreach (PlayerClanInfo value2 in data.Players.Values) { string value = ((value2.IsFreeborn && value2.ClanId == 0) ? "Freeborn" : ((value2.ClanId != 0) ? ("Clan " + value2.ClanId) : "No clan")); GUILayout.BeginVertical(_boxStyle, Array.Empty()); GUILayout.Label("Player Box", _labelGoldStyle, Array.Empty()); DrawMiniStat("Name", value2.PlayerName); DrawMiniStat("SteamID", value2.PlayerId.ToString()); DrawMiniStat("State", value); GUILayout.EndVertical(); } } private void DrawStatLine(string label, long value) { GUILayout.Label("" + label + ": " + value + "", _labelStyle, Array.Empty()); } private void DrawRole(WarfareData data, PlayerClanInfo info) { //IL_006c: Unknown result type (might be due to invalid IL or missing references) //IL_0071: Unknown result type (might be due to invalid IL or missing references) //IL_0074: Unknown result type (might be due to invalid IL or missing references) bool canPrimary = CanChangePrimaryUI(info); bool canSecondary = CanChangeSecondaryUI(info); bool canTertiary = CanChangeTertiaryUI(info); RoleData[] array = VisibleRoleData(); if (_selectedRolePreviewIndex < 0 || _selectedRolePreviewIndex >= array.Length) { _selectedRolePreviewIndex = 0; } Rect rect = GUILayoutUtility.GetRect(((Rect)(ref _windowRect)).width - 60f, 650f, (GUILayoutOption[])(object)new GUILayoutOption[2] { GUILayout.ExpandWidth(true), GUILayout.Height(650f) }); DrawRoleRect(rect, info, array, canPrimary, canSecondary, canTertiary); } private void DrawRoleRect(Rect area, PlayerClanInfo info, RoleData[] roles, bool canPrimary, bool canSecondary, bool canTertiary) { //IL_0002: Unknown result type (might be due to invalid IL or missing references) //IL_0050: Unknown result type (might be due to invalid IL or missing references) //IL_00e9: 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) bool flag = false; try { GUI.BeginGroup(area); flag = true; float num = 0f; float num2 = ((Rect)(ref area)).height - 10f; float num3 = Mathf.Floor((((Rect)(ref area)).width - 8f) * 0.64f); float num4 = ((Rect)(ref area)).width - num3 - 8f; GUI.Box(new Rect(0f, num, num3, num2), GUIContent.none, _boxStyle); int num5 = 4; int num6 = Mathf.CeilToInt((float)roles.Length / (float)num5); float num7 = 6f; float num8 = (num3 - 16f - num7 * (float)(num5 - 1)) / (float)num5; float num9 = (num2 - 16f - num7 * (float)(num6 - 1)) / (float)num6; Rect rect = default(Rect); for (int i = 0; i < roles.Length; i++) { int num10 = i % num5; int num11 = i / num5; ((Rect)(ref rect))..ctor(8f + (float)num10 * (num8 + num7), num + 8f + (float)num11 * (num9 + num7), num8, num9); DrawRoleCardRect(rect, info, roles[i], i); } Rect rect2 = default(Rect); ((Rect)(ref rect2))..ctor(num3 + 8f, num, num4, num2); DrawRolePreviewRect(rect2, info, roles[_selectedRolePreviewIndex], canPrimary, canSecondary, canTertiary); } finally { if (flag) { GUI.EndGroup(); } } } private void DrawRoleAccentBorder(Rect rect, Color accent, float thickness) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_0005: 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_0049: Unknown result type (might be due to invalid IL or missing references) //IL_006f: Unknown result type (might be due to invalid IL or missing references) //IL_0097: Unknown result type (might be due to invalid IL or missing references) Color color = GUI.color; GUI.color = accent; GUI.DrawTexture(new Rect(((Rect)(ref rect)).x, ((Rect)(ref rect)).y, ((Rect)(ref rect)).width, thickness), (Texture)(object)_pixelTex); GUI.DrawTexture(new Rect(((Rect)(ref rect)).x, ((Rect)(ref rect)).yMax - thickness, ((Rect)(ref rect)).width, thickness), (Texture)(object)_pixelTex); GUI.DrawTexture(new Rect(((Rect)(ref rect)).x, ((Rect)(ref rect)).y, thickness, ((Rect)(ref rect)).height), (Texture)(object)_pixelTex); GUI.DrawTexture(new Rect(((Rect)(ref rect)).xMax - thickness, ((Rect)(ref rect)).y, thickness, ((Rect)(ref rect)).height), (Texture)(object)_pixelTex); GUI.color = color; } private RoleData[] VisibleRoleData() { return RoleDefinitions; } private RoleData GetRoleData(ClanRole role) { ClanRole clanRole = PlayerClanInfo.NormalizeRole(role); for (int i = 0; i < RoleDefinitions.Length; i++) { if (RoleDefinitions[i].Role == clanRole) { return RoleDefinitions[i]; } } return null; } private bool IsSelectableRole(ClanRole role) { return role != ClanRole.None; } private void RoleCard(PlayerClanInfo info, RoleData roleData, float width, int index) { ClanRole role = roleData.Role; bool flag = roleData.Selectable && IsSelectableRole(role); bool flag2 = _selectedRolePreviewIndex == index; GUILayout.BeginVertical(flag2 ? _tabActiveStyle : _boxStyle, (GUILayoutOption[])(object)new GUILayoutOption[2] { GUILayout.Width(width), GUILayout.Height(102f) }); GUILayout.BeginHorizontal(Array.Empty()); if (GUILayout.Button((Texture)(object)GetRoleTexture(role), flag2 ? _tabActiveStyle : _boxStyle, (GUILayoutOption[])(object)new GUILayoutOption[2] { GUILayout.Width(72f), GUILayout.Height(72f) })) { _selectedRolePreviewIndex = index; _selectedRolePreview = role; } GUILayout.BeginVertical((GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(width - 88f) }); GUILayout.BeginHorizontal(Array.Empty()); GUILayout.Label("" + roleData.DisplayName + "", (flag2 || flag) ? _labelGoldStyle : _labelStyle, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(width - 166f) }); if (GUILayout.Button("Preview", _smallButtonStyle, (GUILayoutOption[])(object)new GUILayoutOption[2] { GUILayout.Width(72f), GUILayout.Height(20f) })) { _selectedRolePreviewIndex = index; _selectedRolePreview = role; } GUILayout.EndHorizontal(); GUILayout.Label(roleData.Group, _labelStyle, Array.Empty()); string status = RoleSlotName(info, role); GUILayout.Label(RoleStatusColored(status), _labelStyle, Array.Empty()); GUILayout.Label(roleData.Lore, _labelStyle, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Height(38f) }); GUILayout.EndVertical(); GUILayout.EndHorizontal(); GUILayout.EndVertical(); } private void DrawRoleCardRect(Rect rect, PlayerClanInfo info, RoleData roleData, int index) { //IL_001b: Unknown result type (might be due to invalid IL or missing references) //IL_00c3: Unknown result type (might be due to invalid IL or missing references) //IL_00c8: Unknown result type (might be due to invalid IL or missing references) //IL_00a8: Unknown result type (might be due to invalid IL or missing references) //IL_00cd: Unknown result type (might be due to invalid IL or missing references) //IL_00e2: Unknown result type (might be due to invalid IL or missing references) //IL_008d: Unknown result type (might be due to invalid IL or missing references) //IL_0072: Unknown result type (might be due to invalid IL or missing references) //IL_0148: Unknown result type (might be due to invalid IL or missing references) //IL_018e: Unknown result type (might be due to invalid IL or missing references) //IL_01a7: Unknown result type (might be due to invalid IL or missing references) //IL_01ac: Unknown result type (might be due to invalid IL or missing references) //IL_01b3: Unknown result type (might be due to invalid IL or missing references) //IL_01cd: Unknown result type (might be due to invalid IL or missing references) //IL_01d4: Unknown result type (might be due to invalid IL or missing references) //IL_01dd: Expected O, but got Unknown //IL_0209: Unknown result type (might be due to invalid IL or missing references) //IL_0248: 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_0101: Unknown result type (might be due to invalid IL or missing references) //IL_0102: Unknown result type (might be due to invalid IL or missing references) ClanRole role = roleData.Role; bool flag = _selectedRolePreviewIndex == index; string text = RoleSlotName(info, role); GUI.Box(rect, GUIContent.none, flag ? _tabActiveStyle : _boxStyle); Color accent = (Color)(text switch { "Tertiary" => new Color(0.48f, 0.78f, 0.62f, 1f), "Secondary" => new Color(0.3f, 0.68f, 0.9f, 1f), "Primary" => new Color(0.95f, 0.68f, 0.2f, 1f), _ => new Color(0.32f, 0.36f, 0.37f, 1f), }); if (flag) { DrawRoleAccentBorder(rect, new Color(1f, 0.78f, 0.3f, 1f), 3f); } else if (text != "No Role Selected") { DrawRoleAccentBorder(rect, accent, 2f); } Rect val = default(Rect); ((Rect)(ref val))..ctor(((Rect)(ref rect)).x + 5f, ((Rect)(ref rect)).y + 5f, ((Rect)(ref rect)).width - 10f, ((Rect)(ref rect)).height - 46f); GUI.Box(val, GUIContent.none, _boxStyle); GUI.DrawTexture(new Rect(((Rect)(ref val)).x + 2f, ((Rect)(ref val)).y + 2f, ((Rect)(ref val)).width - 4f, ((Rect)(ref val)).height - 4f), (Texture)(object)GetRoleTexture(role), (ScaleMode)2, true); GUIStyle val2 = new GUIStyle(_labelStyle) { alignment = (TextAnchor)4, fontSize = Mathf.Max(10, _labelStyle.fontSize - 1), wordWrap = false, clipping = (TextClipping)1 }; GUI.Label(new Rect(((Rect)(ref rect)).x + 5f, ((Rect)(ref val)).yMax + 3f, ((Rect)(ref rect)).width - 10f, 17f), RoleFunction(role), val2); GUI.Label(new Rect(((Rect)(ref rect)).x + 5f, ((Rect)(ref rect)).yMax - 19f, ((Rect)(ref rect)).width - 10f, 16f), RoleStatusColored(text), val2); if (GUI.Button(rect, "", GUIStyle.none)) { _selectedRolePreviewIndex = index; _selectedRolePreview = role; } } private string RoleFunction(ClanRole role) { return PlayerClanInfo.NormalizeRole(role) switch { ClanRole.Explorer => "Exploration & Discovery", ClanRole.Warden => "Protection & Vigilance", ClanRole.Raider => "Offensive Warfare", ClanRole.Guardian => "Homeland Defense", ClanRole.Builder => "Kingdom Infrastructure", ClanRole.Gatherer => "Resource Supply", ClanRole.Merchant => "Trade & Wealth", ClanRole.Smuggler => "Hidden Economy", ClanRole.Thief => "Espionage & Infiltration", ClanRole.Shaman => "Spiritual Control", ClanRole.Druid => "Nature & Corruption Sense", ClanRole.Hunter => "Tracking & Ranged Combat", ClanRole.Prospector => "Mining & Discovery", ClanRole.Skipper => "Naval Operations", ClanRole.Skald => "Culture & Inspiration", ClanRole.Lawspeaker => "Politics & Diplomacy", _ => "Unassigned", }; } private string SelectedRoleCooldown(PlayerClanInfo info, ClanRole role, bool canPrimary, bool canSecondary, bool canTertiary) { switch (RoleSlotName(info, role)) { case "Primary": if (!canPrimary) { return RoleCooldownRemainingUI(info, ClanConfig.PrimaryRoleChangeCooldownHours?.Value ?? ClanConfig.RoleChangeCooldownHours?.Value ?? 24); } return "Ready"; case "Secondary": { ConfigEntry enableSecondaryRole = ClanConfig.EnableSecondaryRole; if (enableSecondaryRole != null && enableSecondaryRole.Value) { if (!canSecondary) { return RoleCooldownRemainingUI(info, ClanConfig.SecondaryRoleChangeCooldownHours?.Value ?? ClanConfig.RoleChangeCooldownHours?.Value ?? 24); } return "Ready"; } return "Disabled"; } case "Tertiary": { ConfigEntry enableTertiaryRole = ClanConfig.EnableTertiaryRole; if (enableTertiaryRole != null && enableTertiaryRole.Value) { if (!canTertiary) { return RoleCooldownRemainingUI(info, ClanConfig.TertiaryRoleChangeCooldownHours?.Value ?? ClanConfig.RoleChangeCooldownHours?.Value ?? 24); } return "Ready"; } return "Disabled"; } default: return "Choose assignment below"; } } private string RoleSlotName(PlayerClanInfo info, ClanRole role) { if (info.PrimaryRole == role) { return "Primary"; } if (info.SecondaryRole == role) { return "Secondary"; } if (info.TertiaryRole == role) { return "Tertiary"; } return "No Role Selected"; } private string RoleStatusColored(string status) { return status switch { "Primary" => "Primary", "Secondary" => "Secondary", "Tertiary" => "Tertiary", _ => "No Role Selected", }; } private string RoleSlotColored(string slot, string roleName, string percent) { if (roleName == "None") { return "No Role Selected"; } string text = ((slot == "Primary") ? "#ffd24a" : ((slot == "Secondary") ? "#c0c0c0" : "#40e0d0")); return "" + roleName + " (" + percent + ")"; } private string RoleEffectPercent(string slot) { if (slot == "Primary") { return "100%"; } if (slot == "Secondary") { return (ClanConfig.SecondaryRoleEffectPercent?.Value ?? 60f).ToString("0") + "%"; } return (ClanConfig.TertiaryRoleEffectPercent?.Value ?? 40f).ToString("0") + "%"; } private string RoleComingSoon(string text) { return "COMING SOON"; } private void DrawRolePreviewRect(Rect rect, PlayerClanInfo info, RoleData roleData, bool canPrimary, bool canSecondary, bool canTertiary) { //IL_0022: Unknown result type (might be due to invalid IL or missing references) //IL_0034: Unknown result type (might be due to invalid IL or missing references) //IL_0049: Unknown result type (might be due to invalid IL or missing references) //IL_008b: Unknown result type (might be due to invalid IL or missing references) //IL_00d0: Unknown result type (might be due to invalid IL or missing references) //IL_00e9: Unknown result type (might be due to invalid IL or missing references) //IL_00ee: Unknown result type (might be due to invalid IL or missing references) //IL_00f5: Unknown result type (might be due to invalid IL or missing references) //IL_00fd: Expected O, but got Unknown //IL_0162: 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_01c9: Expected O, but got Unknown //IL_01d7: Unknown result type (might be due to invalid IL or missing references) //IL_0221: Unknown result type (might be due to invalid IL or missing references) //IL_022d: Expected O, but got Unknown //IL_023b: Unknown result type (might be due to invalid IL or missing references) //IL_0276: Unknown result type (might be due to invalid IL or missing references) //IL_0282: Expected O, but got Unknown //IL_028f: Unknown result type (might be due to invalid IL or missing references) //IL_044f: Unknown result type (might be due to invalid IL or missing references) //IL_045b: Unknown result type (might be due to invalid IL or missing references) //IL_047e: Unknown result type (might be due to invalid IL or missing references) //IL_0491: Unknown result type (might be due to invalid IL or missing references) //IL_04fb: Unknown result type (might be due to invalid IL or missing references) //IL_0517: Unknown result type (might be due to invalid IL or missing references) //IL_053a: Unknown result type (might be due to invalid IL or missing references) //IL_054d: Unknown result type (might be due to invalid IL or missing references) //IL_05b6: Unknown result type (might be due to invalid IL or missing references) //IL_05d2: Unknown result type (might be due to invalid IL or missing references) //IL_05f5: Unknown result type (might be due to invalid IL or missing references) //IL_0608: Unknown result type (might be due to invalid IL or missing references) ClanRole clanRole = roleData?.Role ?? ClanRole.Explorer; bool num = roleData != null && roleData.Selectable && IsSelectableRole(clanRole); GUI.Box(rect, GUIContent.none, _boxStyle); DrawRoleAccentBorder(rect, new Color(0.72f, 0.49f, 0.18f, 1f), 2f); float num2 = 8f; Rect val = default(Rect); ((Rect)(ref val))..ctor(((Rect)(ref rect)).x + num2, ((Rect)(ref rect)).y + num2, ((Rect)(ref rect)).width - num2 * 2f, 175f); GUI.Box(val, GUIContent.none, _tabActiveStyle); GUI.DrawTexture(new Rect(((Rect)(ref val)).x + 2f, ((Rect)(ref val)).y + 2f, ((Rect)(ref val)).width - 4f, ((Rect)(ref val)).height - 4f), (Texture)(object)GetRoleTexture(clanRole), (ScaleMode)2, true); GUIStyle val2 = new GUIStyle(_labelStyle) { wordWrap = true, clipping = (TextClipping)0 }; float num3 = ((Rect)(ref val)).yMax + 10f; float num4 = ((Rect)(ref rect)).width - num2 * 2f; float num5 = 12f; float num6 = Mathf.Min(155f, num4 * 0.42f); float num7 = num4 - num6 - num5; float num8 = ((Rect)(ref rect)).x + num2; float x = num8 + num7 + num5; float num9 = num3; GUI.Label(new Rect(num8, num9, num7, 24f), "" + (roleData?.DisplayName ?? RoleName(clanRole)) + "", _labelGoldStyle); num9 += 26f; string text = "Function: " + RoleFunction(clanRole); float num10 = Mathf.Max(22f, val2.CalcHeight(new GUIContent(text), num7)); GUI.Label(new Rect(num8, num9, num7, num10), text, val2); num9 += num10 + 3f; string text2 = RoleStatusColored(RoleSlotName(info, clanRole)) + " Cooldown: " + SelectedRoleCooldown(info, clanRole, canPrimary, canSecondary, canTertiary); float num11 = Mathf.Max(22f, val2.CalcHeight(new GUIContent(text2), num7)); GUI.Label(new Rect(num8, num9, num7, num11), text2, val2); num9 += num11 + 4f; string text3 = roleData?.Lore ?? RoleDescription(clanRole); float num12 = Mathf.Max(34f, val2.CalcHeight(new GUIContent(text3), num7)); GUI.Label(new Rect(num8, num9, num7, num12), text3, val2); float num13 = num9 + num12; float num14 = DrawMyRoles(x, num3, num6, info); float num15 = DrawRolePlainDetail(y: DrawRolePlainDetail(y: DrawRolePlainDetail(y: Mathf.Max(num13, num14) + 10f, x: ((Rect)(ref rect)).x + num2, width: num4, title: "CORE ABILITY", body: roleData?.CoreAbility ?? "None", bodyStyle: val2), x: ((Rect)(ref rect)).x + num2, width: num4, title: "PASSIVE BONUSES", body: roleData?.PassiveBonuses ?? "None", bodyStyle: val2), x: ((Rect)(ref rect)).x + num2, width: num4, title: "CLAN VALUE", body: roleData?.ClanValue ?? RolePlaystyle(clanRole), bodyStyle: val2); float num16 = ((Rect)(ref rect)).yMax - 110f; float num17 = 6f; float num18 = ((Rect)(ref rect)).width - num2 * 2f; float num19 = Mathf.Floor((num18 - num17) * 0.58f); float num20 = num18 - num17 - num19; float num21 = ((Rect)(ref rect)).x + num2 + num19 + num17; float num22 = 30f; float num23 = 5f; Color tint = default(Color); ((Color)(ref tint))..ctor(0.88f, 0.63f, 0.24f, 1f); Color tint2 = default(Color); ((Color)(ref tint2))..ctor(0.32f, 0.62f, 0.82f, 1f); Color tint3 = default(Color); ((Color)(ref tint3))..ctor(0.42f, 0.68f, 0.5f, 1f); Color tint4 = default(Color); ((Color)(ref tint4))..ctor(0.58f, 0.24f, 0.22f, 1f); ConfigEntry enableSecondaryRole = ClanConfig.EnableSecondaryRole; ConfigEntry enableTertiaryRole = ClanConfig.EnableTertiaryRole; if (DrawRoleActionButton(enabled: num && canPrimary && info.PrimaryRole != clanRole, rect: new Rect(((Rect)(ref rect)).x + num2, num16, num19, num22), label: "Assign Primary - 100%", tint: tint)) { WarfareRPC.Instance?.RequestSetPrimaryRole(clanRole); } if (DrawRoleActionButton(new Rect(num21, num16, num20, num22), "Resign Primary", info.PrimaryRole != ClanRole.None, tint4)) { WarfareRPC.Instance?.RequestSetPrimaryRole(ClanRole.None); } num16 += num22 + num23; if (DrawRoleActionButton(enabled: num && canSecondary && enableSecondaryRole != null && enableSecondaryRole.Value && info.SecondaryRole != clanRole && info.PrimaryRole != clanRole && info.TertiaryRole != clanRole, rect: new Rect(((Rect)(ref rect)).x + num2, num16, num19, num22), label: "Assign Secondary - " + RoleEffectPercent("Secondary"), tint: tint2)) { WarfareRPC.Instance?.RequestSetSecondaryRole(clanRole); } if (DrawRoleActionButton(new Rect(num21, num16, num20, num22), "Resign Secondary", info.SecondaryRole != ClanRole.None, tint4)) { WarfareRPC.Instance?.RequestSetSecondaryRole(ClanRole.None); } num16 += num22 + num23; bool enabled = num && canTertiary && enableTertiaryRole != null && enableTertiaryRole.Value && info.TertiaryRole != clanRole && info.PrimaryRole != clanRole && info.SecondaryRole != clanRole; if (DrawRoleActionButton(new Rect(((Rect)(ref rect)).x + num2, num16, num19, num22), "Assign Tertiary - " + RoleEffectPercent("Tertiary"), enabled, tint3)) { WarfareRPC.Instance?.RequestSetTertiaryRole(clanRole); } if (DrawRoleActionButton(new Rect(num21, num16, num20, num22), "Resign Tertiary", info.TertiaryRole != ClanRole.None, tint4)) { WarfareRPC.Instance?.RequestSetTertiaryRole(ClanRole.None); } } private bool DrawRoleActionButton(Rect rect, string label, bool enabled, Color tint) { //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_003c: Unknown result type (might be due to invalid IL or missing references) //IL_0015: Unknown result type (might be due to invalid IL or missing references) //IL_002b: Unknown result type (might be due to invalid IL or missing references) //IL_0035: Unknown result type (might be due to invalid IL or missing references) //IL_0043: Unknown result type (might be due to invalid IL or missing references) //IL_0050: Unknown result type (might be due to invalid IL or missing references) bool enabled2 = GUI.enabled; Color color = GUI.color; GUI.enabled = enabled; GUI.color = (enabled ? tint : Color.Lerp(tint, new Color(0.66f, 0.68f, 0.68f, 1f), 0.55f)); bool result = GUI.Button(rect, label, _smallButtonStyle); GUI.color = color; GUI.enabled = enabled2; return result; } private float DrawRolePlainDetail(float x, float y, float width, string title, string body, GUIStyle bodyStyle) { //IL_0018: Unknown result type (might be due to invalid IL or missing references) //IL_0023: Expected O, but got Unknown //IL_0033: Unknown result type (might be due to invalid IL or missing references) //IL_0060: Unknown result type (might be due to invalid IL or missing references) float num = 22f; float num2 = Mathf.Max(20f, bodyStyle.CalcHeight(new GUIContent(body ?? ""), width)); GUI.Label(new Rect(x, y + 2f, width, num), "" + title + "", _labelGoldStyle); GUI.Label(new Rect(x, y + num + 3f, width, num2), body ?? "", bodyStyle); return y + num + 3f + num2 + 5f; } private float DrawMyRoles(float x, float y, float width, PlayerClanInfo info) { //IL_000e: Unknown result type (might be due to invalid IL or missing references) //IL_0056: Unknown result type (might be due to invalid IL or missing references) //IL_008c: Unknown result type (might be due to invalid IL or missing references) //IL_00c2: Unknown result type (might be due to invalid IL or missing references) GUI.Label(new Rect(x, y + 2f, width, 22f), "MY ROLES", _labelGoldStyle); y += 27f; y = DrawMyRoleLine(x, y, width, "PRIMARY", RoleName(info.PrimaryRole), new Color(0.95f, 0.68f, 0.2f, 1f)); y = DrawMyRoleLine(x, y, width, "SECONDARY", RoleName(info.SecondaryRole), new Color(0.3f, 0.68f, 0.9f, 1f)); y = DrawMyRoleLine(x, y, width, "TERTIARY", RoleName(info.TertiaryRole), new Color(0.48f, 0.78f, 0.62f, 1f)); return y; } private float DrawMyRoleLine(float x, float y, float width, string slot, string roleName, Color accent) { //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_0025: 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_0034: Expected O, but got Unknown //IL_003a: Unknown result type (might be due to invalid IL or missing references) //IL_004f: Unknown result type (might be due to invalid IL or missing references) //IL_0079: Unknown result type (might be due to invalid IL or missing references) GUIStyle val = new GUIStyle(_labelStyle) { fontSize = Mathf.Max(10, _labelStyle.fontSize - 1), wordWrap = false, clipping = (TextClipping)0 }; val.normal.textColor = accent; GUI.Label(new Rect(x, y + 1f, width, 17f), "" + slot + "", val); GUI.Label(new Rect(x, y + 19f, width, 19f), (string.IsNullOrEmpty(roleName) || roleName == "None") ? "None" : roleName, _labelStyle); return y + 40f; } private void DrawRolePreviewColumn(float x, float y, float width, float height, string title, string body, GUIStyle bodyStyle) { //IL_0008: Unknown result type (might be due to invalid IL or missing references) //IL_0034: Unknown result type (might be due to invalid IL or missing references) GUI.Label(new Rect(x, y, width, 18f), "" + title + "", _labelGoldStyle); GUI.Label(new Rect(x, y + 24f, width, height), body, bodyStyle); } private string RoleName(ClanRole role) { RoleData roleData = GetRoleData(role); if (roleData != null) { return roleData.DisplayName; } return PlayerClanInfo.NormalizeRole(role) switch { ClanRole.Explorer => "Explorer", ClanRole.Warden => "Warden", ClanRole.Raider => "Raider", ClanRole.Guardian => "Guardian", ClanRole.Builder => "Builder", ClanRole.Gatherer => "Gatherer", ClanRole.Merchant => "Merchant", ClanRole.Smuggler => "Smuggler", ClanRole.Thief => "Thief", ClanRole.Shaman => "Shaman", ClanRole.Druid => "Druid", _ => "None", }; } private string RoleDescription(ClanRole role) { RoleData roleData = GetRoleData(role); if (roleData != null) { return roleData.Lore; } return PlayerClanInfo.NormalizeRole(role) switch { ClanRole.Explorer => "Discovers and claims wilderness territory.", ClanRole.Warden => "Manages annexation, territory administration and stability.", ClanRole.Raider => "Offensive warfare specialist.", ClanRole.Guardian => "Defensive warfare specialist, formerly Shieldbearer.", ClanRole.Builder => "Construction and infrastructure specialist.", ClanRole.Gatherer => "Resource generation specialist.", ClanRole.Merchant => "Legal economy and trade specialist.", ClanRole.Smuggler => "Illegal economy and hidden trade specialist.", ClanRole.Thief => "Espionage and intelligence specialist.", ClanRole.Shaman => "Totems, rituals and clan support.", ClanRole.Druid => "Healing, survival and animal management.", _ => "", }; } private string RoleBonusText(ClanRole role) { RoleData roleData = GetRoleData(role); if (roleData != null) { return roleData.CoreAbility + " | " + roleData.PassiveBonuses; } return PlayerClanInfo.NormalizeRole(role) switch { ClanRole.Explorer => "Claiming: enabled | Wilderness bonus: active", ClanRole.Warden => "Annexation: planned | Stability: planned", ClanRole.Raider => "Enemy loot: x2 | Vault cracking: enabled", ClanRole.Guardian => "Home defense: increased | Capture resistance: increased", ClanRole.Builder => "Build discount: 50% | Upgrade discount: 50%", ClanRole.Gatherer => "Enemy resources: x2 | Carry bonus: +500", ClanRole.Merchant => "Trade income: planned | Legal economy: planned", ClanRole.Smuggler => "Hidden trade: planned | Black market: planned", ClanRole.Thief => "Espionage: planned | Intelligence: planned", ClanRole.Shaman => "Corruption rites: enabled | Cleansing rituals: enabled", ClanRole.Druid => "Healing: planned | Animal management: planned", _ => "", }; } private string RolePlaystyle(ClanRole role) { RoleData roleData = GetRoleData(role); if (roleData != null) { return roleData.ClanValue; } return PlayerClanInfo.NormalizeRole(role) switch { ClanRole.Explorer => "Scout, claim, expand.", ClanRole.Warden => "Administer and stabilize territory.", ClanRole.Raider => "Attack, loot, disrupt.", ClanRole.Guardian => "Defend home ground.", ClanRole.Builder => "Build, upgrade, fortify.", ClanRole.Gatherer => "Harvest and feed the economy.", ClanRole.Merchant => "Trade and finance growth.", ClanRole.Smuggler => "Move goods in the shadows.", ClanRole.Thief => "Intel, secrets, pressure.", ClanRole.Shaman => "Ritual support and corruption control.", ClanRole.Druid => "Survival and living-world support.", _ => "", }; } private string RolePlaceholderText(ClanRole role) { return RoleName(role) + " image placeholder"; } private bool CanChangePrimaryUI(PlayerClanInfo info) { return info.CanChangeRole(ClanConfig.PrimaryRoleChangeCooldownHours?.Value ?? ClanConfig.RoleChangeCooldownHours?.Value ?? 24); } private bool CanChangeSecondaryUI(PlayerClanInfo info) { if (info.SecondaryRole == ClanRole.None) { ConfigEntry ignoreCooldownIfSecondaryRoleIsNone = ClanConfig.IgnoreCooldownIfSecondaryRoleIsNone; if (ignoreCooldownIfSecondaryRoleIsNone != null && ignoreCooldownIfSecondaryRoleIsNone.Value) { return true; } } return info.CanChangeRole(ClanConfig.SecondaryRoleChangeCooldownHours?.Value ?? ClanConfig.RoleChangeCooldownHours?.Value ?? 24); } private bool CanChangeTertiaryUI(PlayerClanInfo info) { if (info.TertiaryRole == ClanRole.None) { ConfigEntry ignoreCooldownIfSecondaryRoleIsNone = ClanConfig.IgnoreCooldownIfSecondaryRoleIsNone; if (ignoreCooldownIfSecondaryRoleIsNone != null && ignoreCooldownIfSecondaryRoleIsNone.Value) { return true; } } return info.CanChangeRole(ClanConfig.TertiaryRoleChangeCooldownHours?.Value ?? ClanConfig.RoleChangeCooldownHours?.Value ?? 24); } private string RoleCooldownRemainingUI(PlayerClanInfo info, int cooldownHours) { long num = info.LastRoleChangeTimestamp + (long)cooldownHours * 3600L - WarfareManager.Now(); if (num < 0) { num = 0L; } long num2 = num / 3600; long num3 = num % 3600 / 60; if (num2 <= 0) { return $"{num3}m"; } return $"{num2}h {num3}m"; } private string EconomyBandColored(int territoryCount) { string text = Economy.EconomyBand(territoryCount); string text2 = ((text == "Profit") ? "88ff88" : ((text == "Balanced") ? "ffd24a" : "ff6666")); return "" + text + ""; } private string TreasuryColored(long value) { if (value > 0) { return "" + value + ""; } if (value < 0) { return "" + value + ""; } return "0"; } private string ColoredCoins(long v) { if (v < 0) { return $"{v}"; } return $"+{v}"; } } } namespace FortecaClanWarfare.Patches { [HarmonyPatch] public static class ClanTaxPatches { private static bool _suppressNestedTax; private static int _taxSourceDepth; private static float _taxSourceUntil; private static string _taxSourceReason = ""; private const float TaxSourceGraceSeconds = 0.75f; [HarmonyPatch(typeof(Pickable), "Interact")] [HarmonyPrefix] public static void Pickable_Interact_Prefix(Pickable __instance) { BeginTaxSource(__instance, "natural pickable interact"); } [HarmonyPatch(typeof(Pickable), "Interact")] [HarmonyPostfix] public static void Pickable_Interact_Postfix() { EndTaxSource(); } [HarmonyPatch(typeof(Pickable), "RPC_Pick")] [HarmonyPrefix] public static void Pickable_RPC_Pick_Prefix(Pickable __instance) { BeginTaxSource(__instance, "natural pickable rpc"); } [HarmonyPatch(typeof(Pickable), "RPC_Pick")] [HarmonyPostfix] public static void Pickable_RPC_Pick_Postfix() { EndTaxSource(); } [HarmonyPatch(typeof(Inventory), "AddItem", new Type[] { typeof(GameObject), typeof(int) })] [HarmonyPrefix] public static void AddItem_GameObject_Prefix(GameObject prefab, ref int amount) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) //IL_000c: Expected O, but got Unknown if (!((Object)prefab == (Object)null) && amount > 1) { ItemDrop component = prefab.GetComponent(); string text = ((Object)prefab).name.Replace("(Clone)", "").Trim(); string displayName = component?.m_itemData?.m_shared?.m_name ?? text; if (ApplyTax(text, displayName, ref amount)) { _suppressNestedTax = true; } } } [HarmonyPatch(typeof(Inventory), "AddItem", new Type[] { typeof(GameObject), typeof(int) })] [HarmonyPostfix] public static void AddItem_GameObject_Postfix() { _suppressNestedTax = false; } [HarmonyPatch(typeof(Inventory), "AddItem", new Type[] { typeof(ItemData) })] [HarmonyPrefix] public static void AddItem_ItemData_Prefix(ItemData item) { if (!_suppressNestedTax && item != null && item.m_stack > 1) { object? obj = typeof(ItemData).GetField("m_dropPrefab", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic)?.GetValue(item); GameObject val = (GameObject)((obj is GameObject) ? obj : null); string text = ((val != null) ? ((Object)val).name.Replace("(Clone)", "").Trim() : (item.m_shared?.m_name ?? "")); string displayName = item.m_shared?.m_name ?? text; int amount = item.m_stack; if (ApplyTax(text, displayName, ref amount)) { item.m_stack = amount; } } } private static bool ApplyTax(string prefabName, string displayName, ref int amount) { //IL_007d: Unknown result type (might be due to invalid IL or missing references) //IL_0088: Expected O, but got Unknown if (!WarfareRPC.SuppressTax) { ConfigEntry enableClanMemberTax = ClanConfig.EnableClanMemberTax; if (enableClanMemberTax != null && enableClanMemberTax.Value && amount > 1) { if (!IsConfirmedTaxableSource(out var reason)) { TaxDebug("[FCW Tax] skipped item=" + prefabName + " amount=" + amount + " source=unconfirmed"); return false; } Player localPlayer = Player.m_localPlayer; WarfareData warfareData = WarfareManager.Instance?.Data; if ((Object)localPlayer == (Object)null || warfareData == null) { return false; } PlayerClanInfo player = warfareData.GetPlayer(localPlayer.GetPlayerID()); if (player == null || player.ClanId == 0 || player.IsFreeborn) { return false; } Clan clan = warfareData.GetClan(player.ClanId); if (clan == null) { return false; } bool flag = IsCoin(prefabName, displayName); if (!flag && !IsSafeResource(prefabName, displayName)) { return false; } int taxPercent = (flag ? clan.CoinTaxPercent : clan.ResourceTaxPercent); int num = ApplySmugglerReduction(player, taxPercent); int num2 = Mathf.FloorToInt((float)amount * ((float)num / 100f)); if (num2 < 1) { return false; } if (!flag && IsClanVaultFullForTax(warfareData, clan, prefabName)) { ShowTaxMessage("Clan Vault is full. Resource tax skipped."); TaxDebug("[FCW Tax] vault full tax skipped player=" + player.PlayerName + " item=" + prefabName + " tax=" + num2); return false; } amount -= num2; if (flag) { WarfareRPC.Instance?.RequestClanCoinTax(num2); TaxDebug("[FCW Tax] taxed coins amount=" + (amount + num2) + " tax=" + num2 + " source=" + reason); } else { WarfareRPC.Instance?.RequestClanResourceTax(prefabName, CleanDisplayName(displayName), num2); ShowTaxMessage("Resource Tax: " + num2 + " item sent to Clan Vault."); TaxDebug("[FCW Tax] taxed player=" + player.PlayerName + " item=" + prefabName + " amount=" + (amount + num2) + " tax=" + num2 + " source=" + reason); } return true; } } return false; } private static void BeginTaxSource(Pickable pickable, string reason) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) //IL_000c: Expected O, but got Unknown if (!((Object)pickable == (Object)null)) { _taxSourceDepth++; _taxSourceUntil = Time.time + 0.75f; string text = ((Object)pickable).name ?? "Pickable"; _taxSourceReason = reason + " " + text.Replace("(Clone)", "").Trim(); } } private static void EndTaxSource() { if (_taxSourceDepth > 0) { _taxSourceDepth--; } _taxSourceUntil = Time.time + 0.75f; } private static bool IsConfirmedTaxableSource(out string reason) { reason = _taxSourceReason; if (_taxSourceDepth > 0) { return true; } if (Time.time <= _taxSourceUntil && !string.IsNullOrEmpty(_taxSourceReason)) { return true; } reason = "unconfirmed"; return false; } private static int ApplySmugglerReduction(PlayerClanInfo player, int taxPercent) { float num = Mathf.Clamp01((ClanConfig.SmugglerTaxReduction?.Value ?? 15f) / 100f * RoleUtils.GetRoleFactor(player, ClanRole.Smuggler)); return Mathf.FloorToInt((float)Mathf.Max(0, taxPercent) * (1f - num)); } private static bool IsClanVaultFullForTax(WarfareData data, Clan clan, string prefabName) { if (data == null || clan == null) { return true; } if (data.GetVault(clan.ClanId).Find((ClanVaultItem v) => v.PrefabName == prefabName) != null) { return false; } int num = Economy.VaultCapacity(clan, data.GetClanTerritoryCount(clan.ClanId)); return data.GetVaultStackCount(clan.ClanId) >= num; } private static bool IsCoin(string prefabName, string displayName) { string b = (ClanConfig.CoinItemPrefab?.Value ?? "Coins").Trim(); if (!string.Equals(prefabName, b, StringComparison.OrdinalIgnoreCase) && !string.Equals(displayName, b, StringComparison.OrdinalIgnoreCase)) { return string.Equals(CleanDisplayName(displayName), b, StringComparison.OrdinalIgnoreCase); } return true; } private static bool IsSafeResource(string prefabName, string displayName) { ConfigEntry taxOnlySafeResources = ClanConfig.TaxOnlySafeResources; if (taxOnlySafeResources == null || !taxOnlySafeResources.Value) { return true; } string text = (prefabName + " " + displayName).ToLowerInvariant(); string[] array = new string[18] { "trophy", "sword", "axe", "mace", "knife", "atgeir", "spear", "bow", "arrow", "shield", "helmet", "armor", "cape", "belt", "key", "cryptkey", "wishbone", "food" }; foreach (string value in array) { if (text.Contains(value)) { return false; } } array = new string[31] { "wood", "stone", "ore", "metal", "coal", "resin", "hide", "leather", "fiber", "fibre", "flax", "linen", "scrap", "copper", "tin", "iron", "silver", "blackmetal", "bronze", "chitin", "surtlingcore", "core", "barley", "wolfpelt", "deerhide", "loxpelt", "jute", "tar", "crystal", "needle", "bonefragment" }; foreach (string value2 in array) { if (text.Contains(value2)) { return true; } } return false; } private static string CleanDisplayName(string displayName) { if (string.IsNullOrWhiteSpace(displayName)) { return ""; } return displayName.Replace("$item_", "").Replace("_", " ").Trim(); } private static void ShowTaxMessage(string message) { WarfareHUD.ShowLeftNotification(message); } private static void TaxDebug(string message) { ConfigEntry enableTaxDebugLogs = ClanConfig.EnableTaxDebugLogs; if (enableTaxDebugLogs != null && enableTaxDebugLogs.Value) { Plugin.Log.LogInfo((object)message); } } } [HarmonyPatch] public static class CombatPatch { private static readonly Dictionary> _lastAttacker = new Dictionary>(); private static bool IsServer { get { //IL_0005: Unknown result type (might be due to invalid IL or missing references) //IL_0010: Expected O, but got Unknown if ((Object)ZNet.instance != (Object)null) { return ZNet.instance.IsServer(); } return false; } } private static long Now() { return DateTimeOffset.UtcNow.ToUnixTimeSeconds(); } [HarmonyPatch(typeof(Character), "Damage")] [HarmonyPostfix] public static void RecordAttacker(Character __instance, HitData hit) { //IL_0008: Unknown result type (might be due to invalid IL or missing references) //IL_0013: Expected O, but got Unknown //IL_0019: Unknown result type (might be due to invalid IL or missing references) //IL_001e: Unknown result type (might be due to invalid IL or missing references) //IL_003f: Unknown result type (might be due to invalid IL or missing references) //IL_005e: Unknown result type (might be due to invalid IL or missing references) //IL_0069: Expected O, but got Unknown //IL_006c: Unknown result type (might be due to invalid IL or missing references) //IL_0077: Expected O, but got Unknown //IL_007f: Unknown result type (might be due to invalid IL or missing references) //IL_0085: Unknown result type (might be due to invalid IL or missing references) //IL_00a5: Unknown result type (might be due to invalid IL or missing references) //IL_00ab: Expected O, but got Unknown //IL_00ac: Unknown result type (might be due to invalid IL or missing references) //IL_00b7: Expected O, but got Unknown //IL_00ba: Unknown result type (might be due to invalid IL or missing references) //IL_00c0: Unknown result type (might be due to invalid IL or missing references) //IL_00ca: Expected O, but got Unknown //IL_00ca: Expected O, but got Unknown if (!IsServer || (Object)__instance == (Object)null || hit == null || hit.m_attacker == ZDOID.None) { return; } ZNetScene instance = ZNetScene.instance; GameObject val = (((Object)(object)instance != (Object)null) ? instance.FindInstance(hit.m_attacker) : null); Player val2 = (((Object)(object)val != (Object)null) ? val.GetComponent() : null); if (!((Object)val == (Object)null) && !((Object)val2 == (Object)null)) { _lastAttacker[__instance.GetZDOID()] = new KeyValuePair(hit.m_attacker, Now()); Player val3 = (Player)((__instance is Player) ? __instance : null); if ((Object)val3 != (Object)null && (Object)val3 != (Object)val2) { PvPManager.RegisterPvPDamage(val2, val3); } if (_lastAttacker.Count > 256) { PruneAttackers(); } } } [HarmonyPatch(typeof(Character), "OnDeath")] [HarmonyPostfix] public static void OnCharacterDeath(Character __instance) { //IL_0008: Unknown result type (might be due to invalid IL or missing references) //IL_0013: Expected O, but got Unknown //IL_0032: Unknown result type (might be due to invalid IL or missing references) //IL_0047: Unknown result type (might be due to invalid IL or missing references) //IL_007a: Unknown result type (might be due to invalid IL or missing references) //IL_009e: Unknown result type (might be due to invalid IL or missing references) //IL_00a9: Expected O, but got Unknown //IL_0140: Unknown result type (might be due to invalid IL or missing references) //IL_0163: Unknown result type (might be due to invalid IL or missing references) //IL_016a: Expected O, but got Unknown //IL_016c: Unknown result type (might be due to invalid IL or missing references) //IL_0177: Expected O, but got Unknown //IL_017b: Unknown result type (might be due to invalid IL or missing references) //IL_0182: Unknown result type (might be due to invalid IL or missing references) //IL_018c: Expected O, but got Unknown //IL_018c: Expected O, but got Unknown if (!IsServer || (Object)__instance == (Object)null) { return; } WarfareManager instance = WarfareManager.Instance; WarfareData warfareData = instance?.Data; if (warfareData == null || !_lastAttacker.TryGetValue(__instance.GetZDOID(), out var value)) { return; } _lastAttacker.Remove(__instance.GetZDOID()); if (Now() - value.Value > 30) { return; } ZNetScene instance2 = ZNetScene.instance; GameObject val = (((Object)(object)instance2 != (Object)null) ? instance2.FindInstance(value.Key) : null); Player val2 = (((Object)(object)val != (Object)null) ? val.GetComponent() : null); if ((Object)val2 == (Object)null) { return; } long playerID = val2.GetPlayerID(); int num = warfareData.GetPlayer(playerID)?.ClanId ?? 0; int num2 = 0; if (__instance.IsPlayer()) { num2 = ClanConfig.CombatPointsKill?.Value ?? 500; } else if (__instance.IsBoss()) { num2 = ClanConfig.CombatPointsBoss?.Value ?? 5000; } else if (__instance.GetLevel() > 1) { num2 = ClanConfig.CombatPointsElite?.Value ?? 1000; } if (num2 > 0 && num != 0) { instance.RegisterCombatKill(((Component)__instance).transform.position, num, num2); } if (!__instance.IsPlayer()) { return; } Player val3 = (Player)((__instance is Player) ? __instance : null); if (!((Object)val3 == (Object)null) && !((Object)val3 == (Object)val2)) { PvPManager.RegisterPlayerKilledPlayer(val2, val3); long playerID2 = val3.GetPlayerID(); PlayerClanInfo player = warfareData.GetPlayer(playerID2); int num3 = player?.ClanId ?? 0; if (num == 0 || num3 == 0 || !instance.AreAtWar(num, num3)) { instance.RegisterIllegalKill(playerID); } if (player != null && player.Bounty > 0) { instance.CollectBounty(playerID, playerID2); } } } private static void PruneAttackers() { //IL_003b: Unknown result type (might be due to invalid IL or missing references) //IL_006a: Unknown result type (might be due to invalid IL or missing references) //IL_006f: Unknown result type (might be due to invalid IL or missing references) //IL_0076: Unknown result type (might be due to invalid IL or missing references) long num = Now() - 60; List list = new List(); foreach (KeyValuePair> item in _lastAttacker) { if (item.Value.Value < num) { list.Add(item.Key); } } foreach (ZDOID item2 in list) { _lastAttacker.Remove(item2); } } } [HarmonyPatch] public static class CursorPatches { [HarmonyPatch(typeof(GameCamera), "UpdateMouseCapture")] [HarmonyPrefix] public static bool GameCamera_UpdateMouseCapture_Prefix() { if (ClanSelectionUI.ShouldBlockInput()) { Cursor.lockState = (CursorLockMode)0; Cursor.visible = true; return false; } return true; } } public sealed class DruidRotTotemTracker : MonoBehaviour { private const float ScanInterval = 0.75f; private const float PrimaryRange = 100f; private const int TrailNodeCount = 9; private const float MaximumTrailLength = 15f; private Player _player; private RotTotem _target; private float _range; private float _nextScanTime; private GameObject _visualRoot; private readonly GameObject[] _nodes = (GameObject[])(object)new GameObject[9]; private Material _trailMaterial; public void Initialize(Player player) { _player = player; _nextScanTime = 0f; } private void Update() { //IL_0006: Unknown result type (might be due to invalid IL or missing references) //IL_0011: Expected O, but got Unknown //IL_0019: 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_002d: Expected O, but got Unknown //IL_002d: Expected O, but got Unknown //IL_0052: Unknown result type (might be due to invalid IL or missing references) if ((Object)_player == (Object)null || (Object)_player != (Object)Player.m_localPlayer) { ClearTarget(); return; } if (_target != null && (_target.State != TotemState.Active || !IsFinite(_target.WorldPosition))) { ClearTarget(); _nextScanTime = 0f; } if (Time.time >= _nextScanTime) { ScanForTarget(); _nextScanTime = Time.time + 0.75f; } if (_target == null) { HideNodes(); } else { UpdateTrail(); } } private void ScanForTarget() { //IL_006b: Unknown result type (might be due to invalid IL or missing references) //IL_0070: Unknown result type (might be due to invalid IL or missing references) //IL_00aa: Unknown result type (might be due to invalid IL or missing references) //IL_00b8: Unknown result type (might be due to invalid IL or missing references) //IL_00bd: Unknown result type (might be due to invalid IL or missing references) //IL_00be: Unknown result type (might be due to invalid IL or missing references) //IL_00c3: Unknown result type (might be due to invalid IL or missing references) WarfareData warfareData = WarfareManager.Instance?.Data; PlayerClanInfo player = warfareData?.GetPlayer(_player.GetPlayerID()); _range = 100f * RoleUtils.GetRoleFactor(player, ClanRole.Druid); if (_range <= 0f || warfareData?.RotTotems == null) { ClearTarget(); return; } Vector3 position = ((Component)_player).transform.position; float num = _range * _range; RotTotem target = null; foreach (RotTotem rotTotem in warfareData.RotTotems) { if (rotTotem != null && rotTotem.State == TotemState.Active && IsFinite(rotTotem.WorldPosition)) { Vector3 val = rotTotem.WorldPosition - position; float sqrMagnitude = ((Vector3)(ref val)).sqrMagnitude; if (sqrMagnitude <= num) { num = sqrMagnitude; target = rotTotem; } } } _target = target; if (_target == null) { HideNodes(); } else { EnsureVisualPool(); } } private void EnsureVisualPool() { //IL_0006: Unknown result type (might be due to invalid IL or missing references) //IL_0011: Expected O, but got Unknown //IL_001a: Unknown result type (might be due to invalid IL or missing references) //IL_0024: Expected O, but got Unknown //IL_0082: Unknown result type (might be due to invalid IL or missing references) //IL_008d: Expected O, but got Unknown //IL_0090: Unknown result type (might be due to invalid IL or missing references) //IL_009a: Expected O, but got Unknown //IL_0165: Unknown result type (might be due to invalid IL or missing references) //IL_0170: Expected O, but got Unknown //IL_00a8: Unknown result type (might be due to invalid IL or missing references) //IL_00b3: Expected O, but got Unknown //IL_0178: Unknown result type (might be due to invalid IL or missing references) //IL_0183: Expected O, but got Unknown //IL_00be: Unknown result type (might be due to invalid IL or missing references) //IL_00c9: Expected O, but got Unknown //IL_00d5: Unknown result type (might be due to invalid IL or missing references) //IL_00df: Expected O, but got Unknown //IL_012f: Unknown result type (might be due to invalid IL or missing references) //IL_0153: Unknown result type (might be due to invalid IL or missing references) //IL_015a: Unknown result type (might be due to invalid IL or missing references) if ((Object)_visualRoot != (Object)null) { return; } _visualRoot = new GameObject("FCW_DruidRotGuidance"); ((Object)_visualRoot).hideFlags = (HideFlags)52; Color val2 = default(Color); for (int i = 0; i < 9; i++) { GameObject val = GameObject.CreatePrimitive((PrimitiveType)0); ((Object)val).name = "GuidanceWisp_" + i; ((Object)val).hideFlags = (HideFlags)52; val.transform.SetParent(_visualRoot.transform, true); Component component = val.GetComponent("SphereCollider"); if ((Object)component != (Object)null) { Object.Destroy((Object)component); } Renderer component2 = val.GetComponent(); if (i == 0 && (Object)component2 != (Object)null && (Object)component2.sharedMaterial != (Object)null) { _trailMaterial = new Material(component2.sharedMaterial); ((Object)_trailMaterial).name = "FCW_DruidGuidanceMaterial"; ((Object)_trailMaterial).hideFlags = (HideFlags)52; ((Color)(ref val2))..ctor(0.42f, 0.95f, 0.68f, 0.78f); if (_trailMaterial.HasProperty("_Color")) { _trailMaterial.color = val2; } if (_trailMaterial.HasProperty("_EmissionColor")) { _trailMaterial.SetColor("_EmissionColor", val2 * 0.55f); } } if ((Object)component2 != (Object)null && (Object)_trailMaterial != (Object)null) { component2.sharedMaterial = _trailMaterial; component2.shadowCastingMode = (ShadowCastingMode)0; component2.receiveShadows = false; } _nodes[i] = val; } } private void UpdateTrail() { //IL_000b: Unknown result type (might be due to invalid IL or missing references) //IL_0010: Unknown result type (might be due to invalid IL or missing references) //IL_0017: Unknown result type (might be due to invalid IL or missing references) //IL_001c: Unknown result type (might be due to invalid IL or missing references) //IL_001d: 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_0049: Unknown result type (might be due to invalid IL or missing references) //IL_004b: Unknown result type (might be due to invalid IL or missing references) //IL_0050: Unknown result type (might be due to invalid IL or missing references) //IL_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_00aa: Unknown result type (might be due to invalid IL or missing references) //IL_00af: Unknown result type (might be due to invalid IL or missing references) //IL_00b3: Unknown result type (might be due to invalid IL or missing references) //IL_00b8: Unknown result type (might be due to invalid IL or missing references) //IL_00cf: Unknown result type (might be due to invalid IL or missing references) //IL_00da: Expected O, but got Unknown //IL_012f: Unknown result type (might be due to invalid IL or missing references) //IL_0130: Unknown result type (might be due to invalid IL or missing references) //IL_0139: 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) //IL_0143: Unknown result type (might be due to invalid IL or missing references) //IL_0156: Unknown result type (might be due to invalid IL or missing references) //IL_015b: Unknown result type (might be due to invalid IL or missing references) //IL_0160: Unknown result type (might be due to invalid IL or missing references) //IL_0164: Unknown result type (might be due to invalid IL or missing references) //IL_0169: Unknown result type (might be due to invalid IL or missing references) //IL_017a: Unknown result type (might be due to invalid IL or missing references) //IL_019c: Unknown result type (might be due to invalid IL or missing references) Vector3 position = ((Component)_player).transform.position; Vector3 val = _target.WorldPosition - position; float magnitude = ((Vector3)(ref val)).magnitude; if (magnitude > _range || magnitude < 0.1f) { ClearTarget(); return; } EnsureVisualPool(); Vector3 val2 = val / magnitude; float num = 1f - Mathf.Clamp01(magnitude / _range); float num2 = Mathf.Min(2f, magnitude * 0.2f); float num3 = Mathf.Min(15f, Mathf.Max(num2, magnitude - 0.75f)); float num4 = Mathf.Max(0.1f, num3 - num2); Vector3 val3 = Vector3.Cross(Vector3.up, val2); Vector3 normalized = ((Vector3)(ref val3)).normalized; for (int i = 0; i < 9; i++) { GameObject val4 = _nodes[i]; if (!((Object)val4 == (Object)null)) { float num5 = (float)i / 8f; float num6 = Mathf.Sin(Time.time * 2.2f + (float)i * 0.85f); float num7 = Mathf.Sin(Time.time * 1.35f + (float)i * 1.7f) * 0.08f; val4.transform.position = position + val2 * (num2 + num4 * num5) + Vector3.up * (1.05f + num6 * 0.12f) + normalized * num7; val4.transform.localScale = Vector3.one * (0.075f + num * 0.055f + (num6 + 1f) * 0.012f); if (!val4.activeSelf) { val4.SetActive(true); } } } } private void ClearTarget() { _target = null; _range = 0f; HideNodes(); } private void HideNodes() { //IL_000c: Unknown result type (might be due to invalid IL or missing references) //IL_0017: Expected O, but got Unknown for (int i = 0; i < _nodes.Length; i++) { if ((Object)_nodes[i] != (Object)null && _nodes[i].activeSelf) { _nodes[i].SetActive(false); } } } private static bool IsFinite(Vector3 p) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_000d: Unknown result type (might be due to invalid IL or missing references) //IL_001a: Unknown result type (might be due to invalid IL or missing references) //IL_0027: Unknown result type (might be due to invalid IL or missing references) //IL_0034: Unknown result type (might be due to invalid IL or missing references) //IL_0041: Unknown result type (might be due to invalid IL or missing references) if (!float.IsNaN(p.x) && !float.IsInfinity(p.x) && !float.IsNaN(p.y) && !float.IsInfinity(p.y) && !float.IsNaN(p.z)) { return !float.IsInfinity(p.z); } return false; } private void OnDisable() { ClearTarget(); } private void OnDestroy() { //IL_000c: Unknown result type (might be due to invalid IL or missing references) //IL_0017: Expected O, but got Unknown //IL_002f: Unknown result type (might be due to invalid IL or missing references) //IL_003a: Expected O, but got Unknown //IL_001f: Unknown result type (might be due to invalid IL or missing references) //IL_0029: Expected O, but got Unknown //IL_0042: Unknown result type (might be due to invalid IL or missing references) //IL_004c: Expected O, but got Unknown ClearTarget(); if ((Object)_visualRoot != (Object)null) { Object.Destroy((Object)_visualRoot); } if ((Object)_trailMaterial != (Object)null) { Object.Destroy((Object)_trailMaterial); } } } [HarmonyPatch(typeof(Player), "Update")] public static class DruidRotTotemTrackerPatch { private static Player _trackedPlayer; private static DruidRotTotemTracker _tracker; [HarmonyPostfix] public static void AttachToLocalPlayer(Player __instance) { //IL_0001: 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_0015: Expected O, but got Unknown //IL_0015: Expected O, but got Unknown //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_002d: Expected O, but got Unknown //IL_002d: Expected O, but got Unknown //IL_0058: Unknown result type (might be due to invalid IL or missing references) //IL_0063: Expected O, but got Unknown //IL_0034: Unknown result type (might be due to invalid IL or missing references) //IL_003f: Expected O, but got Unknown if (!((Object)__instance != (Object)Player.m_localPlayer) && (!((Object)_trackedPlayer == (Object)__instance) || !((Object)_tracker != (Object)null))) { _trackedPlayer = __instance; _tracker = ((Component)__instance).GetComponent(); if ((Object)_tracker == (Object)null) { _tracker = ((Component)__instance).gameObject.AddComponent(); } _tracker.Initialize(__instance); } } } [HarmonyPatch(typeof(Tameable), "DecreaseRemainingTime", new Type[] { typeof(float) })] public static class DruidTamingSpeedPatch { private const float NearbyDruidFallbackDistance = 10f; [HarmonyPrefix] private static void ApplyNearbyDruidBonus(Tameable __instance, ref float __0) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) //IL_000c: Expected O, but got Unknown //IL_003c: Unknown result type (might be due to invalid IL or missing references) //IL_0041: Unknown result type (might be due to invalid IL or missing references) //IL_0061: Unknown result type (might be due to invalid IL or missing references) //IL_006c: Expected O, but got Unknown //IL_007e: Unknown result type (might be due to invalid IL or missing references) //IL_0083: Unknown result type (might be due to invalid IL or missing references) //IL_0084: Unknown result type (might be due to invalid IL or missing references) //IL_0089: Unknown result type (might be due to invalid IL or missing references) if ((Object)__instance == (Object)null || __0 <= 0f) { return; } WarfareData warfareData = WarfareManager.Instance?.Data; if (warfareData == null) { return; } float num = 10f * 10f; Vector3 position = ((Component)__instance).transform.position; float num2 = 0f; List allPlayers = Player.GetAllPlayers(); for (int i = 0; i < allPlayers.Count; i++) { Player val = allPlayers[i]; if (!((Object)val == (Object)null) && !((Character)val).IsDead()) { Vector3 val2 = ((Component)val).transform.position - position; if (!(((Vector3)(ref val2)).sqrMagnitude > num)) { PlayerClanInfo player = warfareData.GetPlayer(val.GetPlayerID()); num2 = Mathf.Max(num2, RoleUtils.GetRoleFactor(player, ClanRole.Druid)); } } } if (!(num2 <= 0f)) { float num3 = Mathf.Max(0f, ClanConfig.DruidTamingSpeedBonus?.Value ?? 15f) / 100f; __0 *= 1f + num3 * num2; } } } [HarmonyPatch(typeof(Pickable), "RPC_Pick", new Type[] { typeof(long), typeof(int) })] public static class GathererPickableBonusPatch { private const float MaximumInteractionDistance = 6f; private static readonly RNGCryptoServiceProvider Random = new RNGCryptoServiceProvider(); [HarmonyPrefix] private static void AddGathererYield(Pickable __instance, long sender, ref int __1) { //IL_001a: Unknown result type (might be due to invalid IL or missing references) //IL_0025: Expected O, but got Unknown //IL_006f: Unknown result type (might be due to invalid IL or missing references) //IL_007a: Expected O, but got Unknown //IL_009b: Unknown result type (might be due to invalid IL or missing references) //IL_00a6: Unknown result type (might be due to invalid IL or missing references) //IL_00ab: Unknown result type (might be due to invalid IL or missing references) //IL_00b0: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)ZNet.instance == (Object)null || !ZNet.instance.IsServer() || (Object)__instance == (Object)null || __instance.GetPicked() || !__instance.CanBePicked()) { return; } long num = ResolvePeerPlayerId(sender); Player val = FindPlayer(num); float roleFactor = RoleUtils.GetRoleFactor((WarfareManager.Instance?.Data)?.GetPlayer(num), ClanRole.Gatherer); if (num != 0L && !((Object)val == (Object)null) && !((Character)val).IsDead() && !(roleFactor <= 0f) && IsValidPickable(__instance)) { Vector3 val2 = ((Component)val).transform.position - ((Component)__instance).transform.position; if (!(((Vector3)(ref val2)).sqrMagnitude > 36f) && NextInt(10000) < Mathf.RoundToInt(1000f * roleFactor)) { __1++; } } } private static bool IsValidPickable(Pickable pickable) { //IL_0008: Unknown result type (might be due to invalid IL or missing references) //IL_0013: Expected O, but got Unknown GameObject itemPrefab = pickable.m_itemPrefab; ItemDrop val = (((Object)itemPrefab == (Object)null) ? null : itemPrefab.GetComponent()); if (val?.m_itemData?.m_shared != null) { return !val.m_itemData.m_shared.m_questItem; } return false; } private static Player FindPlayer(long playerId) { //IL_001b: Unknown result type (might be due to invalid IL or missing references) //IL_0026: Expected O, but got Unknown if (playerId == 0L) { return null; } foreach (Player allPlayer in Player.GetAllPlayers()) { if ((Object)allPlayer != (Object)null && allPlayer.GetPlayerID() == playerId) { return allPlayer; } } return null; } private static long ResolvePeerPlayerId(long peerId) { //IL_000d: Unknown result type (might be due to invalid IL or missing references) //IL_0018: Expected O, but got Unknown //IL_009e: Unknown result type (might be due to invalid IL or missing references) if (peerId == ZNet.GetUID() && (Object)Player.m_localPlayer != (Object)null) { return Player.m_localPlayer.GetPlayerID(); } ZNet instance = ZNet.instance; ZNetPeer val = ((instance != null) ? instance.GetPeer(peerId) : null); if (val == null && (Object)(object)ZNet.instance != (Object)null) { foreach (ZNetPeer peer in ZNet.instance.GetPeers()) { if (peer != null && peer.m_uid == peerId) { val = peer; break; } } } ZDO obj = ((val != null && ZDOMan.instance != null) ? ZDOMan.instance.GetZDO(val.m_characterID) : null); if (obj == null) { return 0L; } return obj.GetLong(ZDOVars.s_playerID, 0L); } private static int NextInt(int maximum) { if (maximum <= 1) { return 0; } byte[] array = new byte[4]; uint num = uint.MaxValue - uint.MaxValue % (uint)maximum; uint num2; do { Random.GetBytes(array); num2 = BitConverter.ToUInt32(array, 0); } while (num2 >= num); return (int)(num2 % (uint)maximum); } } [HarmonyPatch] public static class GuardianBuildPatch { [HarmonyPatch(typeof(Player), "ConsumeResources")] [HarmonyPrefix] public static void ConsumeResources_Prefix(Player __instance, Requirement[] requirements, ref int[] __state, PieceTable ___m_buildPieces) { //IL_0001: 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_0015: Expected O, but got Unknown //IL_0015: Expected O, but got Unknown //IL_0087: Unknown result type (might be due to invalid IL or missing references) //IL_008c: Unknown result type (might be due to invalid IL or missing references) //IL_0091: Unknown result type (might be due to invalid IL or missing references) //IL_0094: Unknown result type (might be due to invalid IL or missing references) if ((Object)__instance != (Object)Player.m_localPlayer) { return; } Piece val = ((___m_buildPieces != null) ? ___m_buildPieces.GetSelectedPiece() : null); if ((Object)(object)val != (Object)null && string.Equals(((Object)val).name, "Fehu_RotTotem", StringComparison.Ordinal)) { return; } WarfareData warfareData = WarfareManager.Instance?.Data; if (warfareData == null) { return; } PlayerClanInfo player = warfareData.GetPlayer(__instance.GetPlayerID()); float roleFactor = RoleUtils.GetRoleFactor(player, ClanRole.Builder); if (player == null || roleFactor <= 0f || player.ClanId == 0) { return; } Vector2i grid = GridExtensions.WorldToGrid(((Component)__instance).transform.position); ClanTerritory territory = warfareData.GetTerritory(grid); if (territory == null || territory.OwnerClanId != player.ClanId) { return; } float num = (ClanConfig.BuilderBuildCostReduction?.Value ?? ClanConfig.GuardianBuildCostReduction?.Value ?? 50f) / 100f * roleFactor; __state = new int[requirements.Length]; for (int i = 0; i < requirements.Length; i++) { __state[i] = requirements[i]?.m_amount ?? 0; } foreach (Requirement val2 in requirements) { if (val2 != null && val2.m_amount > 1) { val2.m_amount = Math.Max(1, (int)((float)val2.m_amount * (1f - num))); } } } [HarmonyPatch(typeof(Player), "ConsumeResources")] [HarmonyPostfix] public static void ConsumeResources_Postfix(Requirement[] requirements, int[] __state) { RestoreRequirements(requirements, __state); } [HarmonyPatch(typeof(Player), "ConsumeResources")] [HarmonyFinalizer] public static Exception ConsumeResources_Finalizer(Requirement[] requirements, int[] __state, Exception __exception) { RestoreRequirements(requirements, __state); return __exception; } private static void RestoreRequirements(Requirement[] requirements, int[] originalAmounts) { if (requirements == null || originalAmounts == null) { return; } int num = Math.Min(requirements.Length, originalAmounts.Length); for (int i = 0; i < num; i++) { if (requirements[i] != null) { requirements[i].m_amount = originalAmounts[i]; } } } } [HarmonyPatch] public static class GuardianPvPPatch { public static bool ForceLocalPvp; public static bool FriendlyFireEnabled; private static bool _allowPvpMutation; public static void SetPvpFromFehuPanel(Player player, bool enabled) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) //IL_000c: Expected O, but got Unknown if (!((Object)player == (Object)null)) { ForceLocalPvp = enabled; SetPvpInternal(player, enabled); } } private static void SetPvpInternal(Player player, bool enabled) { _allowPvpMutation = true; try { player.SetPVP(enabled); } finally { _allowPvpMutation = false; } } [HarmonyPatch(typeof(Player), "SetPVP")] [HarmonyPrefix] public static bool Player_SetPVP_Prefix(Player __instance, bool enabled) { //IL_0008: Unknown result type (might be due to invalid IL or missing references) //IL_0012: Unknown result type (might be due to invalid IL or missing references) //IL_001c: Expected O, but got Unknown //IL_001c: Expected O, but got Unknown if (_allowPvpMutation || (Object)__instance != (Object)Player.m_localPlayer) { return true; } PlayerClanInfo playerClanInfo = WarfareManager.Instance?.Data?.GetPlayer(__instance.GetPlayerID()); bool flag = playerClanInfo != null && playerClanInfo.ForcedPvpUntilUtc > DateTimeOffset.UtcNow.ToUnixTimeSeconds(); if ((RealmRules.GetBool("force_pvp") || flag) && !enabled) { return false; } return ClanConfig.AllowPlayerPvpToggle?.Value ?? false; } [HarmonyPatch(typeof(Player), "Update")] [HarmonyPostfix] public static void Player_Update(Player __instance) { //IL_0001: 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_0015: Expected O, but got Unknown //IL_0015: Expected O, but got Unknown //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_00ae: 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) if ((Object)__instance != (Object)Player.m_localPlayer) { return; } WarfareData warfareData = WarfareManager.Instance?.Data; if (warfareData == null) { return; } PlayerClanInfo player = warfareData.GetPlayer(__instance.GetPlayerID()); bool flag = player != null && player.ForcedPvpUntilUtc > DateTimeOffset.UtcNow.ToUnixTimeSeconds(); ForceLocalPvp = RealmRules.GetBool("force_pvp") || flag; FriendlyFireEnabled = RealmRules.GetBool("friendly_fire"); if (ForceLocalPvp && !((Character)__instance).IsPVPEnabled()) { SetPvpInternal(__instance, enabled: true); } if (player != null && player.ClanId != 0) { Vector2i grid = GridExtensions.WorldToGrid(((Component)__instance).transform.position); ClanTerritory territory = warfareData.GetTerritory(grid); bool flag2 = false; if (RoleUtils.HasRole(player, ClanRole.Guardian) && territory != null && territory.OwnerClanId == player.ClanId) { flag2 = true; } ConfigEntry fullPvPDuringWar = ClanConfig.FullPvPDuringWar; if (fullPvPDuringWar != null && fullPvPDuringWar.Value && territory != null && territory.OwnerClanId != 0 && warfareData.GetActiveWarsInvolving(territory.OwnerClanId).Count > 0) { flag2 = true; } if (flag2 && !((Character)__instance).IsPVPEnabled()) { SetPvpInternal(__instance, enabled: true); } } } [HarmonyPatch(typeof(Character), "Damage")] [HarmonyPrefix] public static bool PreventFriendlyFire(Character __instance, HitData hit) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) //IL_000c: Expected O, but got Unknown //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_0043: Unknown result type (might be due to invalid IL or missing references) //IL_006c: Unknown result type (might be due to invalid IL or missing references) //IL_0072: Expected O, but got Unknown if ((Object)__instance == (Object)null || !__instance.IsPlayer()) { return true; } if (hit == null || hit.m_attacker == ZDOID.None) { return true; } ZNetScene instance = ZNetScene.instance; GameObject val = (((Object)(object)instance != (Object)null) ? instance.FindInstance(hit.m_attacker) : null); Player attacker = (((Object)(object)val != (Object)null) ? val.GetComponent() : null); Player victim = (Player)((__instance is Player) ? __instance : null); return PvPManager.CanDamage(attacker, victim); } } [HarmonyPatch] public static class InputBlockingPatches { [HarmonyPatch(typeof(InventoryGui), "Update")] [HarmonyPrefix] public static bool InventoryGui_Update_Prefix() { return !WarfareHUD.IsMenuOpen; } [HarmonyPatch(typeof(Player), "TakeInput")] [HarmonyPrefix] public static bool Player_TakeInput_Prefix(ref bool __result) { if (ClanSelectionUI.ShouldBlockInput()) { __result = false; return false; } return true; } [HarmonyPatch(typeof(PlayerController), "TakeInput")] [HarmonyPrefix] public static bool PlayerController_TakeInput_Prefix(ref bool __result) { if (ClanSelectionUI.ShouldBlockInput()) { __result = false; return false; } return true; } } [HarmonyPatch(typeof(RolePatches), "DoubleLoot")] public static class LegacyDoubleLootDisablePatch { [HarmonyPrefix] private static bool DisableLegacyCreatureLootDuplication() { return false; } } [HarmonyPatch] public static class MerchantTraderPricePatch { private static readonly FieldInfo PurchasePriceField = AccessTools.Field(typeof(TradeItem), "m_price"); private static readonly FieldInfo SaleValueField = AccessTools.Field(typeof(SharedData), "m_value"); [HarmonyPatch(typeof(StoreGui), "CanAfford")] [HarmonyTranspiler] private static IEnumerable AdjustAffordability(IEnumerable instructions) { return ReplacePurchasePrice(instructions); } [HarmonyPatch(typeof(StoreGui), "BuySelectedItem")] [HarmonyTranspiler] private static IEnumerable AdjustPurchaseTransaction(IEnumerable instructions) { return ReplacePurchasePrice(instructions); } [HarmonyPatch(typeof(StoreGui), "SellItem")] [HarmonyTranspiler] private static IEnumerable AdjustSaleTransaction(IEnumerable instructions) { MethodInfo replacement = AccessTools.Method(typeof(MerchantTraderPricePatch), "GetMerchantSaleValue", (Type[])null, (Type[])null); foreach (CodeInstruction instruction in instructions) { yield return (CodeInstruction)((!(instruction.opcode == OpCodes.Ldfld) || !object.Equals(instruction.operand, SaleValueField)) ? ((object)instruction) : ((object)new CodeInstruction(OpCodes.Call, (object)replacement))); } } private static IEnumerable ReplacePurchasePrice(IEnumerable instructions) { MethodInfo replacement = AccessTools.Method(typeof(MerchantTraderPricePatch), "GetMerchantPurchasePrice", (Type[])null, (Type[])null); foreach (CodeInstruction instruction in instructions) { yield return (CodeInstruction)((!(instruction.opcode == OpCodes.Ldfld) || !object.Equals(instruction.operand, PurchasePriceField)) ? ((object)instruction) : ((object)new CodeInstruction(OpCodes.Call, (object)replacement))); } } private static int GetMerchantPurchasePrice(TradeItem item) { int num = item?.m_price ?? 0; if (num <= 0) { return num; } return Mathf.Max(1, Mathf.RoundToInt((float)num * (1f - 0.1f * GetLocalMerchantFactor()))); } private static int GetMerchantSaleValue(SharedData shared) { int num = shared?.m_value ?? 0; if (num <= 0) { return num; } return Mathf.Max(1, Mathf.RoundToInt((float)num * (1f + 0.1f * GetLocalMerchantFactor()))); } private static float GetLocalMerchantFactor() { //IL_0019: Unknown result type (might be due to invalid IL or missing references) //IL_0024: Expected O, but got Unknown Player localPlayer = Player.m_localPlayer; WarfareData warfareData = WarfareManager.Instance?.Data; return RoleUtils.GetRoleFactor(((Object)localPlayer == (Object)null) ? null : warfareData?.GetPlayer(localPlayer.GetPlayerID()), ClanRole.Merchant); } } [HarmonyPatch] public static class ParasitePatches { [HarmonyPatch(typeof(CharacterDrop), "GenerateDropList")] [HarmonyPostfix] public static void ReduceLootFromPump(CharacterDrop __instance, ref List> __result) { //IL_002a: Unknown result type (might be due to invalid IL or missing references) //IL_002f: Unknown result type (might be due to invalid IL or missing references) //IL_0034: Unknown result type (might be due to invalid IL or missing references) //IL_0036: Unknown result type (might be due to invalid IL or missing references) if (__result == null || __result.Count == 0) { return; } WarfareData warfareData = WarfareManager.Instance?.Data; if (warfareData == null) { return; } Vector2i grid = GridExtensions.WorldToGrid(((Component)__instance).transform.position); int lootReductionPercent = warfareData.GetLootReductionPercent(grid); if (lootReductionPercent <= 0) { return; } float num = 1f - (float)lootReductionPercent / 100f; if (num <= 0f) { __result.Clear(); return; } for (int i = 0; i < __result.Count; i++) { KeyValuePair keyValuePair = __result[i]; int value = Math.Max(1, (int)((float)keyValuePair.Value * num)); __result[i] = new KeyValuePair(keyValuePair.Key, value); } } private static bool IsRaiderInEnemyTerritory(Player player) { //IL_0040: Unknown result type (might be due to invalid IL or missing references) //IL_0045: Unknown result type (might be due to invalid IL or missing references) //IL_004a: 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) WarfareData warfareData = WarfareManager.Instance?.Data; if (warfareData == null) { return false; } PlayerClanInfo player2 = warfareData.GetPlayer(player.GetPlayerID()); if (player2 == null || !RoleUtils.HasRole(player2, ClanRole.Raider) || player2.ClanId == 0) { return false; } Vector2i grid = GridExtensions.WorldToGrid(((Component)player).transform.position); ClanTerritory territory = warfareData.GetTerritory(grid); if (territory != null && territory.OwnerClanId != 0) { return territory.OwnerClanId != player2.ClanId; } return false; } } [HarmonyPatch(typeof(Minimap), "Explore", new Type[] { typeof(Vector3), typeof(float) })] public static class PathfinderMapExplorationPatch { [HarmonyPrefix] private static void ScaleLocalExplorationRadius(ref float radius) { //IL_0007: Unknown result type (might be due to invalid IL or missing references) //IL_0012: Expected O, but got Unknown Player localPlayer = Player.m_localPlayer; if (!((Object)localPlayer == (Object)null)) { float roleFactor = RoleUtils.GetRoleFactor((WarfareManager.Instance?.Data)?.GetPlayer(localPlayer.GetPlayerID()), ClanRole.Explorer); if (roleFactor > 0f) { radius *= 1f + roleFactor; } } } } [HarmonyPatch] public static class ProspectorMiningBonusPatch { [ThreadStatic] private static float _bonusChance; [ThreadStatic] private static bool _bonusResolved; [HarmonyPatch(typeof(MineRock), "RPC_Hit")] [HarmonyPrefix] private static void MineRockHitPrefix(HitData __1) { BeginMiningDrop(__1); } [HarmonyPatch(typeof(MineRock), "RPC_Hit")] [HarmonyPostfix] private static void MineRockHitPostfix() { EndMiningDrop(); } [HarmonyPatch(typeof(MineRock), "RPC_Hit")] [HarmonyFinalizer] private static Exception MineRockHitFinalizer(Exception __exception) { EndMiningDrop(); return __exception; } [HarmonyPatch(typeof(MineRock5), "RPC_Damage")] [HarmonyPrefix] private static void MineRock5DamagePrefix(HitData __1) { BeginMiningDrop(__1); } [HarmonyPatch(typeof(MineRock5), "RPC_Damage")] [HarmonyPostfix] private static void MineRock5DamagePostfix() { EndMiningDrop(); } [HarmonyPatch(typeof(MineRock5), "RPC_Damage")] [HarmonyFinalizer] private static Exception MineRock5DamageFinalizer(Exception __exception) { EndMiningDrop(); return __exception; } [HarmonyPatch(typeof(DropTable), "GetDropList", new Type[] { })] [HarmonyPostfix] private static void AddOneMiningBonusDrop(ref List __result) { //IL_0036: Unknown result type (might be due to invalid IL or missing references) //IL_0041: Expected O, but got Unknown //IL_007b: Unknown result type (might be due to invalid IL or missing references) //IL_0088: Unknown result type (might be due to invalid IL or missing references) //IL_008e: Invalid comparison between Unknown and I4 if (_bonusChance <= 0f || _bonusResolved || __result == null || __result.Count == 0) { return; } List list = null; foreach (GameObject item in __result) { ItemDrop obj = (((Object)item == (Object)null) ? null : item.GetComponent()); if (obj != null && (int)(obj.m_itemData?.m_shared?.m_itemType).GetValueOrDefault() == 1) { if (list == null) { list = new List(); } list.Add(item); } } if (list != null && list.Count != 0) { _bonusResolved = true; if (Random.value < _bonusChance) { __result.Add(list[Random.Range(0, list.Count)]); } } } private static void BeginMiningDrop(HitData hit) { //IL_0009: Unknown result type (might be due to invalid IL or missing references) //IL_0010: Invalid comparison between Unknown and I4 //IL_0020: Unknown result type (might be due to invalid IL or missing references) //IL_002b: Expected O, but got Unknown EndMiningDrop(); if (hit != null && (int)hit.m_skill == 12) { Character attacker = hit.GetAttacker(); Player val = (Player)(object)((attacker is Player) ? attacker : null); if (!((Object)val == (Object)null)) { float roleFactor = RoleUtils.GetRoleFactor((WarfareManager.Instance?.Data)?.GetPlayer(val.GetPlayerID()), ClanRole.Prospector); _bonusChance = Mathf.Clamp01((ClanConfig.ProspectorResourceChance?.Value ?? 12f) / 100f * roleFactor); } } } private static void EndMiningDrop() { _bonusChance = 0f; _bonusResolved = false; } } [HarmonyPatch] public static class PvPFoundationPatches { [HarmonyPatch(typeof(Player), "Update")] [HarmonyPrefix] public static void Player_Update_Postfix(Player __instance) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) //IL_000c: Expected O, but got Unknown if (!((Object)__instance == (Object)null)) { PvPManager.Tick(); } } [HarmonyPatch(typeof(TeleportWorld), "Interact", new Type[] { typeof(Humanoid), typeof(bool), typeof(bool) })] [HarmonyPrefix] public static bool TeleportWorld_Interact_Prefix(TeleportWorld __instance, Humanoid human, ref bool __result) { //IL_000c: Unknown result type (might be due to invalid IL or missing references) //IL_0012: Expected O, but got Unknown //IL_0013: Unknown result type (might be due to invalid IL or missing references) //IL_001e: Expected O, but got Unknown Player val = (Player)((human is Player) ? human : null); if ((Object)val == (Object)null) { return true; } if (!TerritoryRules.CanUsePortal(val, __instance, out var message)) { ((Character)val).Message((MessageType)2, message, 0, (Sprite)null); __result = false; return false; } ConfigEntry battleStatusEnabled = ClanConfig.BattleStatusEnabled; if (battleStatusEnabled == null || !battleStatusEnabled.Value || RealmRules.GetBool("teleport_in_combat")) { return true; } if (!PvPManager.IsPlayerInCombat(val) || PvPManager.HasAdminPvpBypass(val)) { return true; } ((Character)val).Message((MessageType)2, "The battle still grips you. Teleport is blocked.", 0, (Sprite)null); __result = false; return false; } [HarmonyPatch(typeof(ZNet), "Disconnect", new Type[] { typeof(ZNetPeer) })] [HarmonyPrefix] public static void ZNet_Disconnect_Prefix(ZNetPeer peer) { //IL_0005: Unknown result type (might be due to invalid IL or missing references) //IL_0010: Expected O, but got Unknown //IL_004d: Unknown result type (might be due to invalid IL or missing references) //IL_0058: Expected O, but got Unknown try { if ((Object)ZNet.instance == (Object)null || !ZNet.instance.IsServer() || peer == null) { return; } string playerName = peer.m_playerName; if (string.IsNullOrEmpty(playerName)) { return; } foreach (Player allPlayer in Player.GetAllPlayers()) { if ((Object)allPlayer != (Object)null && string.Equals(allPlayer.GetPlayerName(), playerName, StringComparison.Ordinal)) { PvPManager.RegisterCombatLogout(allPlayer); break; } } } catch (Exception ex) { Plugin.Log.LogWarning((object)("[FCW PvP] Combat logout hook failed safely: " + ex.Message)); } } } [HarmonyPatch(typeof(Economy), "ClaimSpeed")] public static class RaiderClaimSpeedPatch { [HarmonyPostfix] private static void ApplyEnemyTerritoryRaiderBonus(int attackers, ClanTerritory t, ref float __result) { //IL_0055: Unknown result type (might be due to invalid IL or missing references) //IL_0060: Expected O, but got Unknown //IL_0078: Unknown result type (might be due to invalid IL or missing references) //IL_007d: Unknown result type (might be due to invalid IL or missing references) //IL_0082: Unknown result type (might be due to invalid IL or missing references) //IL_0084: 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) WarfareData warfareData = WarfareManager.Instance?.Data; if (warfareData == null || t == null || t.OwnerClanId == 0 || t.FreebornOwnerId != 0L || __result <= 0f) { return; } int num = 0; float num2 = 0f; foreach (Player allPlayer in Player.GetAllPlayers()) { if ((Object)allPlayer == (Object)null || ((Character)allPlayer).IsDead()) { continue; } Vector2i val = GridExtensions.WorldToGrid(((Component)allPlayer).transform.position); if (val.x == t.GridX && val.y == t.GridY) { PlayerClanInfo player = warfareData.GetPlayer(allPlayer.GetPlayerID()); if (player != null && player.ClanId != 0 && player.ClanId != t.OwnerClanId && warfareData.GetActiveWarBetween(player.ClanId, t.OwnerClanId) != null) { num++; num2 = Mathf.Max(num2, RoleUtils.GetRoleFactor(player, ClanRole.Raider)); } } } if (num == attackers && num2 > 0f) { __result *= 1f + 0.2f * num2; } } } [HarmonyPatch] public static class RaiderVaultRaidSystem { private sealed class PendingRaid { public long Id; public long RaiderId; public long RaiderPeer; public int TargetClanId; public long Created; public bool Removed; public readonly List Items = new List(); } private const string AttemptRpc = "FCW_RaiderVaultAttempt"; private const string CapacityRpc = "FCW_RaiderVaultCapacity"; private const string CapacityReplyRpc = "FCW_RaiderVaultCapacityReply"; private const string AddRpc = "FCW_RaiderVaultAdd"; private const string AddReplyRpc = "FCW_RaiderVaultAddReply"; private const string ForcePvpRpc = "FCW_RaiderVaultForcePvp"; private const long SuccessCooldown = 86400L; private const long FailureCooldown = 1800L; private const long ForcedPvpSeconds = 600L; private static readonly RNGCryptoServiceProvider Random = new RNGCryptoServiceProvider(); private static readonly Dictionary Pending = new Dictionary(); private static int _rpcInstance; private static long _nextTransaction = 1L; private static bool IsServer { get { if ((Object)(object)ZNet.instance != (Object)null) { return ZNet.instance.IsServer(); } return false; } } [HarmonyPatch(typeof(Player), "Update")] [HarmonyPostfix] private static void PlayerUpdate(Player __instance) { //IL_0001: 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_0015: Expected O, but got Unknown //IL_0015: Expected O, but got Unknown if ((Object)__instance == (Object)Player.m_localPlayer) { RegisterRpcs(); } } [HarmonyPatch(typeof(WarfareRPC), "RequestStartVaultCrack")] [HarmonyPrefix] private static bool ReplaceLegacyVaultCrack(Vector2i grid) { //IL_0005: Unknown result type (might be due to invalid IL or missing references) //IL_000b: Expected O, but got Unknown //IL_000c: 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) RegisterRpcs(); ZPackage val = new ZPackage(); val.Write(grid.x); val.Write(grid.y); ToServer("FCW_RaiderVaultAttempt", val); return false; } [HarmonyPatch(typeof(PlayerClanInfo), "CanCrackVault")] [HarmonyPostfix] private static void UseRaidCooldown(PlayerClanInfo __instance, ref bool __result) { long lastVaultCrackTimestamp = __instance.LastVaultCrackTimestamp; if (lastVaultCrackTimestamp == 0L) { __result = true; return; } long num = ((lastVaultCrackTimestamp > 0) ? 86400 : 1800); __result = WarfareManager.Now() - Math.Abs(lastVaultCrackTimestamp) >= num; } private static void RegisterRpcs() { ZRoutedRpc instance = ZRoutedRpc.instance; if (instance != null) { int hashCode = RuntimeHelpers.GetHashCode(instance); if (hashCode != _rpcInstance) { instance.Register("FCW_RaiderVaultAttempt", (Action)RpcAttempt); instance.Register("FCW_RaiderVaultCapacity", (Action)RpcCapacity); instance.Register("FCW_RaiderVaultCapacityReply", (Action)RpcCapacityReply); instance.Register("FCW_RaiderVaultAdd", (Action)RpcAdd); instance.Register("FCW_RaiderVaultAddReply", (Action)RpcAddReply); instance.Register("FCW_RaiderVaultForcePvp", (Action)RpcForcePvp); _rpcInstance = hashCode; } } } private static void RpcAttempt(long sender, ZPackage package) { //IL_005a: Unknown result type (might be due to invalid IL or missing references) //IL_0088: Unknown result type (might be due to invalid IL or missing references) //IL_0093: Expected O, but got Unknown //IL_00b1: Unknown result type (might be due to invalid IL or missing references) //IL_00b6: Unknown result type (might be due to invalid IL or missing references) //IL_00bb: Unknown result type (might be due to invalid IL or missing references) //IL_00bd: Unknown result type (might be due to invalid IL or missing references) //IL_00c7: Unknown result type (might be due to invalid IL or missing references) //IL_01f8: Unknown result type (might be due to invalid IL or missing references) //IL_01ff: Expected O, but got Unknown if (!IsServer) { return; } Cleanup(); long num = ResolvePeerPlayerId(sender); int num2 = package.ReadInt(); int num3 = package.ReadInt(); WarfareManager instance = WarfareManager.Instance; WarfareData warfareData = instance?.Data; PlayerClanInfo playerClanInfo = warfareData?.GetPlayer(num); Player val = FindPlayer(num); ClanTerritory clanTerritory = warfareData?.GetTerritory(new Vector2i(num2, num3)); float roleFactor = RoleUtils.GetRoleFactor(playerClanInfo, ClanRole.Raider); if (playerClanInfo == null || playerClanInfo.ClanId == 0 || roleFactor <= 0f || (Object)val == (Object)null || ((Character)val).IsDead()) { Notify(sender, "Only a living Raider may raid an enemy Vault."); return; } Vector2i val2 = GridExtensions.WorldToGrid(((Component)val).transform.position); if (val2.x != num2 || val2.y != num3 || clanTerritory == null || clanTerritory.OwnerClanId == 0 || clanTerritory.OwnerClanId == playerClanInfo.ClanId || warfareData.GetActiveWarBetween(playerClanInfo.ClanId, clanTerritory.OwnerClanId) == null) { Notify(sender, "You must interact with an official enemy clan Vault."); return; } long lastVaultCrackTimestamp = playerClanInfo.LastVaultCrackTimestamp; long num4 = ((lastVaultCrackTimestamp > 0) ? 86400 : 1800); if (((lastVaultCrackTimestamp == 0L) ? 0 : (num4 - (WarfareManager.Now() - Math.Abs(lastVaultCrackTimestamp)))) > 0) { Notify(sender, "Vault raid is on cooldown."); return; } List list = EligibleVaultItems(warfareData, clanTerritory.OwnerClanId); if (list.Count == 0) { Notify(sender, "The enemy Vault has no eligible stacks."); return; } if (NextInt(10000) >= Mathf.RoundToInt(3500f * roleFactor)) { long num5 = WarfareManager.Now(); playerClanInfo.LastVaultCrackTimestamp = -num5; playerClanInfo.ForcedPvpUntilUtc = Math.Max(playerClanInfo.ForcedPvpUntilUtc, num5 + 600); instance.SaveData(); WarfareRPC.Instance?.SyncAllDataToClients(); Notify(sender, "Vault raid failed. You have been exposed."); NotifyClan(clanTerritory.OwnerClanId, "A Raider attempted to breach your clan Vault."); ZPackage val3 = new ZPackage(); val3.Write(playerClanInfo.ForcedPvpUntilUtc); Send(sender, "FCW_RaiderVaultForcePvp", val3); return; } PendingRaid pendingRaid = new PendingRaid { Id = _nextTransaction++, RaiderId = num, RaiderPeer = sender, TargetClanId = clanTerritory.OwnerClanId, Created = WarfareManager.Now() }; int num6 = Math.Min(list.Count, 1 + NextInt(3)); for (int i = 0; i < num6; i++) { int index = NextInt(list.Count); ClanVaultItem clanVaultItem = list[index]; list.RemoveAt(index); pendingRaid.Items.Add(new ClanVaultItem { PrefabName = clanVaultItem.PrefabName, DisplayName = clanVaultItem.DisplayName, Amount = clanVaultItem.Amount }); } Pending[pendingRaid.Id] = pendingRaid; ZPackage package2 = WriteItems(pendingRaid.Id, pendingRaid.Items); Send(sender, "FCW_RaiderVaultCapacity", package2); } private static void RpcCapacity(long sender, ZPackage package) { //IL_0026: Unknown result type (might be due to invalid IL or missing references) //IL_002c: Expected O, but got Unknown long num = package.ReadLong(); List items = ReadItems(package); Player localPlayer = Player.m_localPlayer; bool flag = CanReceiveAll((localPlayer != null) ? ((Humanoid)localPlayer).GetInventory() : null, items); ZPackage val = new ZPackage(); val.Write(num); val.Write(flag); ToServer("FCW_RaiderVaultCapacityReply", val); } private static void RpcCapacityReply(long sender, ZPackage package) { if (!IsServer) { return; } long num = package.ReadLong(); bool flag = package.ReadBool(); if (!Pending.TryGetValue(num, out var value) || value.RaiderPeer != sender) { return; } if (!flag) { Notify(sender, "Vault raid cancelled: your inventory cannot receive every selected stack."); Pending.Remove(num); return; } List list = (WarfareManager.Instance?.Data)?.GetVault(value.TargetClanId); if (list == null || !RemoveExact(list, value.Items)) { Notify(sender, "Vault raid cancelled: the Vault contents changed."); Pending.Remove(num); } else { value.Removed = true; WarfareManager.Instance.SaveData(); Send(sender, "FCW_RaiderVaultAdd", WriteItems(num, value.Items)); } } private static void RpcAdd(long sender, ZPackage package) { //IL_0095: Unknown result type (might be due to invalid IL or missing references) //IL_009c: Expected O, but got Unknown //IL_005a: Unknown result type (might be due to invalid IL or missing references) //IL_0065: Expected O, but got Unknown long num = package.ReadLong(); List list = ReadItems(package); Player localPlayer = Player.m_localPlayer; Inventory val = ((localPlayer != null) ? ((Humanoid)localPlayer).GetInventory() : null); bool flag = CanReceiveAll(val, list); if (flag) { foreach (ClanVaultItem item in list) { ObjectDB instance = ObjectDB.instance; GameObject val2 = ((instance != null) ? instance.GetItemPrefab(item.PrefabName) : null); if ((Object)val2 == (Object)null || !val.AddItem(val2, item.Amount)) { flag = false; break; } } } ZPackage val3 = new ZPackage(); val3.Write(num); val3.Write(flag); ToServer("FCW_RaiderVaultAddReply", val3); } private static void RpcAddReply(long sender, ZPackage package) { if (!IsServer) { return; } long key = package.ReadLong(); bool flag = package.ReadBool(); if (!Pending.TryGetValue(key, out var value) || value.RaiderPeer != sender) { return; } PlayerClanInfo playerClanInfo = WarfareManager.Instance?.Data?.GetPlayer(value.RaiderId); if (!flag) { Rollback(value); Notify(sender, "Vault raid transfer failed and was rolled back."); Pending.Remove(key); return; } if (playerClanInfo != null) { playerClanInfo.LastVaultCrackTimestamp = WarfareManager.Now(); } WarfareManager.Instance.SaveData(); WarfareRPC.Instance?.SyncAllDataToClients(); Notify(sender, "Vault raid succeeded. You stole " + value.Items.Count + " stack(s)."); NotifyClan(value.TargetClanId, "An enemy Raider has breached your clan Vault."); Pending.Remove(key); } private static bool CanReceiveAll(Inventory inventory, List items) { //IL_0024: Unknown result type (might be due to invalid IL or missing references) //IL_002a: Expected O, but got Unknown //IL_002a: Unknown result type (might be due to invalid IL or missing references) //IL_0030: Expected O, but got Unknown //IL_003e: Unknown result type (might be due to invalid IL or missing references) //IL_0048: Expected O, but got Unknown //IL_006d: Unknown result type (might be due to invalid IL or missing references) //IL_0078: Expected O, but got Unknown if (inventory == null || (Object)(object)ObjectDB.instance == (Object)null) { return false; } Inventory val = new Inventory("FCW Raider Capacity", (Sprite)null, inventory.GetWidth(), inventory.GetHeight()); ZPackage val2 = new ZPackage(); inventory.Save(val2); val.Load(new ZPackage(val2.GetArray())); foreach (ClanVaultItem item in items) { GameObject itemPrefab = ObjectDB.instance.GetItemPrefab(item.PrefabName); if ((Object)itemPrefab == (Object)null || !val.CanAddItem(itemPrefab, item.Amount) || !val.AddItem(itemPrefab, item.Amount)) { return false; } } return true; } private static List EligibleVaultItems(WarfareData data, int clanId) { //IL_0036: Unknown result type (might be due to invalid IL or missing references) //IL_0041: Expected O, but got Unknown List list = new List(); foreach (ClanVaultItem item in data.GetVault(clanId)) { ObjectDB instance = ObjectDB.instance; GameObject val = ((instance != null) ? instance.GetItemPrefab(item.PrefabName) : null); ItemDrop val2 = (((Object)val == (Object)null) ? null : val.GetComponent()); if (item.Amount > 0 && val2?.m_itemData?.m_shared != null && !val2.m_itemData.m_shared.m_questItem) { list.Add(item); } } return list; } private static bool RemoveExact(List vault, List selected) { foreach (ClanVaultItem wanted in selected) { if (vault.Find((ClanVaultItem item) => string.Equals(item.PrefabName, wanted.PrefabName, StringComparison.OrdinalIgnoreCase) && item.Amount == wanted.Amount) == null) { return false; } } foreach (ClanVaultItem wanted2 in selected) { vault.Remove(vault.Find((ClanVaultItem item) => string.Equals(item.PrefabName, wanted2.PrefabName, StringComparison.OrdinalIgnoreCase) && item.Amount == wanted2.Amount)); } return true; } private static void Rollback(PendingRaid pending) { if (!pending.Removed) { return; } WarfareData warfareData = WarfareManager.Instance?.Data; if (warfareData == null) { return; } foreach (ClanVaultItem item in pending.Items) { warfareData.AddToVault(pending.TargetClanId, item.PrefabName, item.DisplayName, item.Amount); } WarfareManager.Instance.SaveData(); pending.Removed = false; } private static ZPackage WriteItems(long transaction, List items) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_0006: Expected O, but got Unknown ZPackage val = new ZPackage(); val.Write(transaction); val.Write(items.Count); foreach (ClanVaultItem item in items) { val.Write(item.PrefabName ?? ""); val.Write(item.DisplayName ?? ""); val.Write(item.Amount); } return val; } private static List ReadItems(ZPackage package) { int num = package.ReadInt(); List list = new List(); for (int i = 0; i < num; i++) { list.Add(new ClanVaultItem { PrefabName = package.ReadString(), DisplayName = package.ReadString(), Amount = package.ReadInt() }); } return list; } private static void Cleanup() { long num = WarfareManager.Now(); List list = new List(); foreach (KeyValuePair item in Pending) { if (num - item.Value.Created > 30) { if (item.Value.Removed) { Rollback(item.Value); } list.Add(item.Key); } } foreach (long item2 in list) { Pending.Remove(item2); } } private static void NotifyClan(int clanId, string message) { if ((Object)(object)ZNet.instance == (Object)null) { return; } foreach (ZNetPeer peer in ZNet.instance.GetPeers()) { long playerId = ResolvePeerPlayerId(peer.m_uid); WarfareManager instance = WarfareManager.Instance; if (instance != null && instance.Data?.GetPlayer(playerId)?.ClanId == clanId) { Notify(peer.m_uid, message); } } } private static void RpcForcePvp(long sender, ZPackage package) { //IL_000e: Unknown result type (might be due to invalid IL or missing references) //IL_0019: Expected O, but got Unknown package.ReadLong(); Player localPlayer = Player.m_localPlayer; if ((Object)localPlayer != (Object)null) { GuardianPvPPatch.SetPvpFromFehuPanel(localPlayer, enabled: true); } } private static Player FindPlayer(long playerId) { //IL_0016: Unknown result type (might be due to invalid IL or missing references) //IL_0021: Expected O, but got Unknown foreach (Player allPlayer in Player.GetAllPlayers()) { if ((Object)allPlayer != (Object)null && allPlayer.GetPlayerID() == playerId) { return allPlayer; } } return null; } private static long ResolvePeerPlayerId(long peerId) { //IL_0014: Unknown result type (might be due to invalid IL or missing references) //IL_001f: Expected O, but got Unknown //IL_00a5: Unknown result type (might be due to invalid IL or missing references) if (IsServer && peerId == ZNet.GetUID() && (Object)Player.m_localPlayer != (Object)null) { return Player.m_localPlayer.GetPlayerID(); } ZNet instance = ZNet.instance; ZNetPeer val = ((instance != null) ? instance.GetPeer(peerId) : null); if (val == null && (Object)(object)ZNet.instance != (Object)null) { foreach (ZNetPeer peer in ZNet.instance.GetPeers()) { if (peer != null && peer.m_uid == peerId) { val = peer; break; } } } ZDO obj = ((val != null && ZDOMan.instance != null) ? ZDOMan.instance.GetZDO(val.m_characterID) : null); if (obj == null) { return 0L; } return obj.GetLong(ZDOVars.s_playerID, 0L); } private static int NextInt(int maximum) { if (maximum <= 1) { return 0; } byte[] array = new byte[4]; uint num = uint.MaxValue - uint.MaxValue % (uint)maximum; uint num2; do { Random.GetBytes(array); num2 = BitConverter.ToUInt32(array, 0); } while (num2 >= num); return (int)(num2 % (uint)maximum); } private static void ToServer(string name, ZPackage package) { long peer; if (!IsServer) { ZNet instance = ZNet.instance; peer = ((instance == null) ? ((long?)null) : instance.GetServerPeer()?.m_uid).GetValueOrDefault(); } else { peer = ZNet.GetUID(); } Send(peer, name, package); } private static void Send(long peer, string name, ZPackage package) { ZRoutedRpc instance = ZRoutedRpc.instance; if (instance != null) { instance.InvokeRoutedRPC(peer, name, new object[1] { package }); } } private static void Notify(long peer, string message) { WarfareRPC.Instance?.SendNotification(peer, message); } } [HarmonyPatch] public static class RolePatches { [HarmonyPatch] private static class ResourceDamagePermissionPatch { private static IEnumerable TargetMethods() { string[] array = new string[5] { "MineRock", "MineRock5", "TreeBase", "TreeLog", "Destructible" }; string[] array2 = array; for (int i = 0; i < array2.Length; i++) { Type type = AccessTools.TypeByName(array2[i]); MethodInfo methodInfo = ((type != null) ? AccessTools.Method(type, "Damage", (Type[])null, (Type[])null) : null); if (methodInfo != null) { yield return methodInfo; } } } private static bool Prefix(Component __instance) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) //IL_000c: Expected O, but got Unknown //IL_0016: Unknown result type (might be due to invalid IL or missing references) if ((Object)__instance == (Object)null) { return true; } return CheckGatherPermission(__instance.transform.position); } } [HarmonyPatch(typeof(SEMan), "ModifyAttack")] [HarmonyPostfix] public static void ModifyAttack(SEMan __instance, SkillType skill, ref HitData hitData) { //IL_0019: Unknown result type (might be due to invalid IL or missing references) //IL_0024: Expected O, but got Unknown //IL_004a: Unknown result type (might be due to invalid IL or missing references) //IL_004c: Invalid comparison between Unknown and I4 //IL_0081: Unknown result type (might be due to invalid IL or missing references) Player localPlayer = Player.m_localPlayer; WarfareData warfareData = WarfareManager.Instance?.Data; if ((Object)localPlayer == (Object)null || warfareData == null || __instance != ((Character)localPlayer).GetSEMan()) { return; } PlayerClanInfo player = warfareData.GetPlayer(localPlayer.GetPlayerID()); if (player != null) { float num = 0f; if ((int)skill == 8) { num = Mathf.Max(num, (ClanConfig.HunterBowDamageBonus?.Value ?? 8f) / 100f * RoleUtils.GetRoleFactor(player, ClanRole.Hunter)); } if (IsOfficialWarEnemyTerritory(warfareData, player, ((Component)localPlayer).transform.position)) { num = Mathf.Max(num, (ClanConfig.RaiderDamageBonus?.Value ?? 8f) / 100f * RoleUtils.GetRoleFactor(player, ClanRole.Raider)); } if (num > 0f) { ((DamageTypes)(ref hitData.m_damage)).Modify(1f + num); } } } [HarmonyPatch(typeof(Character), "RPC_Damage")] [HarmonyPrefix] public static void ModifyDefense(Character __instance, HitData hit) { //IL_0020: Unknown result type (might be due to invalid IL or missing references) //IL_002b: Expected O, but got Unknown //IL_002e: Unknown result type (might be due to invalid IL or missing references) //IL_0034: Unknown result type (might be due to invalid IL or missing references) //IL_003e: Expected O, but got Unknown //IL_003e: Expected O, but got Unknown //IL_0067: Unknown result type (might be due to invalid IL or missing references) //IL_006c: Unknown result type (might be due to invalid IL or missing references) Player val = (Player)(object)((__instance is Player) ? __instance : null); Player localPlayer = Player.m_localPlayer; WarfareData warfareData = WarfareManager.Instance?.Data; if ((Object)val == (Object)null || (Object)val != (Object)localPlayer || warfareData == null || hit == null) { return; } PlayerClanInfo player = warfareData.GetPlayer(val.GetPlayerID()); if (player == null || player.ClanId == 0) { return; } ClanTerritory territory = warfareData.GetTerritory(GridExtensions.WorldToGrid(((Component)val).transform.position)); if (territory != null && territory.OwnerClanId == player.ClanId) { float num = Mathf.Clamp01((ClanConfig.GuardianDamageReduction?.Value ?? 8f) / 100f * RoleUtils.GetRoleFactor(player, ClanRole.Guardian)); if (num > 0f) { ((DamageTypes)(ref hit.m_damage)).Modify(1f - num); } } } [HarmonyPatch(typeof(Player), "UseStamina")] [HarmonyPrefix] public static void ReduceRoleStaminaCost(Player __instance, ref float v) { //IL_0001: 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_0015: Expected O, but got Unknown //IL_0015: Expected O, but got Unknown //IL_0073: Unknown result type (might be due to invalid IL or missing references) //IL_0080: Unknown result type (might be due to invalid IL or missing references) //IL_008b: Unknown result type (might be due to invalid IL or missing references) //IL_008d: Invalid comparison between Unknown and I4 //IL_00ba: Unknown result type (might be due to invalid IL or missing references) //IL_00bd: Invalid comparison between Unknown and I4 //IL_00f4: Unknown result type (might be due to invalid IL or missing references) if ((Object)__instance != (Object)Player.m_localPlayer || v <= 0f) { return; } WarfareData warfareData = WarfareManager.Instance?.Data; PlayerClanInfo playerClanInfo = warfareData?.GetPlayer(__instance.GetPlayerID()); if (playerClanInfo != null) { SkillType valueOrDefault = (((Humanoid)__instance).GetCurrentWeapon()?.m_shared?.m_skillType).GetValueOrDefault(); float num = 0f; if ((int)valueOrDefault == 8) { num = Mathf.Max(num, (ClanConfig.HunterBowStaminaReduction?.Value ?? 10f) / 100f * RoleUtils.GetRoleFactor(playerClanInfo, ClanRole.Hunter)); } if ((int)valueOrDefault == 12) { num = Mathf.Max(num, (ClanConfig.ProspectorMiningStaminaReduction?.Value ?? 10f) / 100f * RoleUtils.GetRoleFactor(playerClanInfo, ClanRole.Prospector)); } if ((int)valueOrDefault != 0 && IsOfficialWarEnemyTerritory(warfareData, playerClanInfo, ((Component)__instance).transform.position)) { num = Mathf.Max(num, (ClanConfig.RaiderStaminaReduction?.Value ?? 10f) / 100f * RoleUtils.GetRoleFactor(playerClanInfo, ClanRole.Raider)); } v *= 1f - Mathf.Clamp01(num); } } [HarmonyPatch(typeof(ItemData), "GetBlockPower", new Type[] { typeof(float) })] [HarmonyPostfix] public static void GuardianBlockPower(ref float __result) { //IL_0019: Unknown result type (might be due to invalid IL or missing references) //IL_0024: Expected O, but got Unknown //IL_0041: Unknown result type (might be due to invalid IL or missing references) //IL_0046: Unknown result type (might be due to invalid IL or missing references) Player localPlayer = Player.m_localPlayer; WarfareData warfareData = WarfareManager.Instance?.Data; if ((Object)localPlayer == (Object)null || warfareData == null) { return; } PlayerClanInfo player = warfareData.GetPlayer(localPlayer.GetPlayerID()); ClanTerritory clanTerritory = ((player == null) ? null : warfareData.GetTerritory(GridExtensions.WorldToGrid(((Component)localPlayer).transform.position))); if (player != null && clanTerritory != null && clanTerritory.OwnerClanId == player.ClanId) { float num = (ClanConfig.GuardianBlockPowerBonus?.Value ?? 10f) / 100f * RoleUtils.GetRoleFactor(player, ClanRole.Guardian); if (num > 0f) { __result *= 1f + num; } } } private static bool IsOfficialWarEnemyTerritory(WarfareData data, PlayerClanInfo player, Vector3 position) { //IL_0011: Unknown result type (might be due to invalid IL or missing references) //IL_0012: Unknown result type (might be due to invalid IL or missing references) if (data == null || player == null || player.ClanId == 0) { return false; } ClanTerritory territory = data.GetTerritory(GridExtensions.WorldToGrid(position)); if (territory == null || territory.OwnerClanId == 0 || territory.OwnerClanId == player.ClanId) { return false; } foreach (War item in data.GetActiveWarsInvolving(player.ClanId)) { if ((item.AttackerClanId == player.ClanId && item.DefenderClanId == territory.OwnerClanId) || (item.DefenderClanId == player.ClanId && item.AttackerClanId == territory.OwnerClanId)) { return true; } } return false; } [HarmonyPatch(typeof(Player), "GetMaxCarryWeight")] [HarmonyPostfix] public static void GathererCarryWeight(Player __instance, ref float __result) { //IL_0001: 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_0015: Expected O, but got Unknown //IL_0015: Expected O, but got Unknown if ((Object)__instance != (Object)Player.m_localPlayer) { return; } WarfareData warfareData = WarfareManager.Instance?.Data; if (warfareData != null) { float roleFactor = RoleUtils.GetRoleFactor(warfareData.GetPlayer(__instance.GetPlayerID()), ClanRole.Gatherer); if (roleFactor > 0f) { __result += (float)(ClanConfig.GathererCarryWeightBonus?.Value ?? 500) * roleFactor; } } } [HarmonyPatch(typeof(CharacterDrop), "GenerateDropList")] [HarmonyPostfix] public static void DoubleLoot(CharacterDrop __instance, ref List> __result) { //IL_0015: Unknown result type (might be due to invalid IL or missing references) //IL_0020: Expected O, but got Unknown //IL_006a: Unknown result type (might be due to invalid IL or missing references) //IL_006f: Unknown result type (might be due to invalid IL or missing references) //IL_0074: 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_00cb: Unknown result type (might be due to invalid IL or missing references) //IL_00d6: Expected O, but got Unknown if (__result == null || __result.Count == 0) { return; } Player localPlayer = Player.m_localPlayer; if ((Object)localPlayer == (Object)null) { return; } WarfareData warfareData = WarfareManager.Instance?.Data; if (warfareData == null) { return; } PlayerClanInfo player = warfareData.GetPlayer(localPlayer.GetPlayerID()); if (player == null || player.ClanId == 0 || (!RoleUtils.HasRole(player, ClanRole.Raider) && !RoleUtils.HasRole(player, ClanRole.Gatherer))) { return; } Vector2i grid = GridExtensions.WorldToGrid(((Component)__instance).transform.position); ClanTerritory territory = warfareData.GetTerritory(grid); if (territory == null || territory.OwnerClanId == 0 || territory.OwnerClanId == player.ClanId) { return; } if (!CanGatherFromTerritory(player, territory)) { __result.Clear(); return; } foreach (KeyValuePair item in __result) { if (!((Object)item.Key == (Object)null)) { ItemDrop component = item.Key.GetComponent(); if (component?.m_itemData?.m_shared != null) { string name = ((Object)item.Key).name; string name2 = component.m_itemData.m_shared.m_name; int value = item.Value; WarfareRPC.Instance?.SendItemToClanVault(player.ClanId, name, name2, value); } } } } public static bool CanGatherFromTerritory(PlayerClanInfo player, ClanTerritory territory) { string message; if (!((Object)(object)WarfareManager.Instance == (Object)null)) { return WarfareManager.Instance.CanGatherInTerritory(player, territory, out message); } return true; } [HarmonyPatch(typeof(Pickable), "Interact")] [HarmonyPrefix] public static bool Pickable_Interact(Pickable __instance) { //IL_0006: Unknown result type (might be due to invalid IL or missing references) return CheckGatherPermission(((Component)__instance).transform.position); } private static bool CheckGatherPermission(Vector3 position) { //IL_0019: Unknown result type (might be due to invalid IL or missing references) //IL_0024: Expected O, but got Unknown //IL_0038: Unknown result type (might be due to invalid IL or missing references) //IL_0039: Unknown result type (might be due to invalid IL or missing references) //IL_003e: Unknown result type (might be due to invalid IL or missing references) //IL_0040: Unknown result type (might be due to invalid IL or missing references) //IL_005d: Unknown result type (might be due to invalid IL or missing references) Player localPlayer = Player.m_localPlayer; WarfareData warfareData = WarfareManager.Instance?.Data; if ((Object)localPlayer == (Object)null || warfareData == null) { return true; } PlayerClanInfo player = warfareData.GetPlayer(localPlayer.GetPlayerID()); Vector2i val = GridExtensions.WorldToGrid(position); ClanTerritory territory = warfareData.GetTerritory(val); if ((Object)(object)WarfareManager.Instance == (Object)null || WarfareManager.Instance.CanGatherInTerritory(player, territory, val, out var message)) { return true; } WarfareHUD.ShowLeftNotification(message); return false; } [HarmonyPatch(typeof(Player), "OnSpawned")] [HarmonyPostfix] public static void OnPlayerSpawned(Player __instance) { //IL_0001: 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_0015: Expected O, but got Unknown //IL_0015: Expected O, but got Unknown if ((Object)__instance != (Object)Player.m_localPlayer) { return; } WarfareRPC.Instance?.RequestDataFromServer(); WarfareData warfareData = WarfareManager.Instance?.Data; if (warfareData != null) { PlayerClanInfo player = warfareData.GetPlayer(__instance.GetPlayerID()); if (player == null || player.ClanId == 0) { Plugin.Log.LogInfo((object)"Player needs clan selection."); } } } [HarmonyPatch(typeof(ZNet), "Awake")] [HarmonyPostfix] public static void ZNet_Awake(ZNet __instance) { if (__instance.IsServer()) { WarfareManager.Instance?.InitServer(); } else { WarfareRPC.Instance?.MarkClientServerSessionChanged(); } } [HarmonyPatch(typeof(ZNet), "Shutdown")] [HarmonyPrefix] public static void ZNet_Shutdown() { ZNet instance = ZNet.instance; if ((Object)(object)instance != (Object)null && instance.IsServer()) { WarfareManager.Instance?.SaveData(); } else { WarfareRPC.Instance?.ClearClientClanCacheOnDisconnect(); } } [HarmonyPatch(typeof(Game), "SavePlayerProfile")] [HarmonyPrefix] public static void Game_Save() { ZNet instance = ZNet.instance; if ((Object)(object)instance != (Object)null && instance.IsServer()) { WarfareManager.Instance?.SaveData(); } } } [HarmonyPatch] public static class RotTotemPrefabPatches { public const string PrefabName = "Fehu_RotTotem"; private static GameObject _prefab; private static GameObject _prefabRoot; private static bool _missingFehuCategoryLogged; private static bool _pieceManagerReflectionResolved; private static Type _pieceManagerType; private static PropertyInfo _pieceManagerInstanceProperty; private static MethodInfo _getPieceCategoryMethod; private static float _nextVisibilityUpdate; [HarmonyPatch(typeof(ZNetScene), "Awake")] [HarmonyPostfix] private static void ZNetSceneAwakePostfix(ZNetScene __instance) { RegisterNetworkPrefab(__instance); RegisterHammerPiece(ObjectDB.instance); } [HarmonyPatch(typeof(ObjectDB), "Awake")] [HarmonyPostfix] private static void ObjectDBAwakePostfix(ObjectDB __instance) { RegisterHammerPiece(__instance); } [HarmonyPatch(typeof(ObjectDB), "CopyOtherDB")] [HarmonyPostfix] private static void ObjectDBCopyOtherDBPostfix(ObjectDB __instance) { RegisterHammerPiece(__instance); } [HarmonyPatch(typeof(Player), "Update")] [HarmonyPostfix] private static void PlayerUpdatePostfix(Player __instance) { if (!((Object)(object)__instance != (Object)(object)Player.m_localPlayer) && !(Time.time < _nextVisibilityUpdate)) { _nextVisibilityUpdate = Time.time + 1f; UpdateHammerVisibility(__instance); } } private static void RegisterNetworkPrefab(ZNetScene scene) { if ((Object)(object)scene == (Object)null || (Object)(object)_prefab != (Object)null) { return; } GameObject prefab = scene.GetPrefab("dverger_guardstone"); if ((Object)(object)prefab == (Object)null) { ManualLogSource log = Plugin.Log; if (log != null) { log.LogWarning((object)"Rot Totem base prefab dverger_guardstone was not found."); } return; } GameObject val = null; bool activeSelf = prefab.activeSelf; try { prefab.SetActive(false); val = Object.Instantiate(prefab); ((Object)val).name = "Fehu_RotTotem"; val.transform.SetParent(GetPrefabRoot().transform, false); val.SetActive(true); } finally { prefab.SetActive(activeSelf); } PrivateArea[] componentsInChildren = val.GetComponentsInChildren(true); for (int i = 0; i < componentsInChildren.Length; i++) { Object.DestroyImmediate((Object)(object)componentsInChildren[i]); } MonoBehaviour[] componentsInChildren2 = val.GetComponentsInChildren(true); foreach (MonoBehaviour val2 in componentsInChildren2) { if (!((Object)(object)val2 == (Object)null) && !(val2 is ZNetView) && !(val2 is WearNTear) && !(val2 is Piece) && !(val2 is FehuRotTotemNetwork)) { string name = ((object)val2).GetType().Name; if (name.IndexOf("Dvergr", StringComparison.OrdinalIgnoreCase) >= 0 || name.IndexOf("GuardStone", StringComparison.OrdinalIgnoreCase) >= 0 || name.IndexOf("Ward", StringComparison.OrdinalIgnoreCase) >= 0) { Object.DestroyImmediate((Object)(object)val2); } } } Piece obj = val.GetComponent() ?? val.AddComponent(); obj.m_name = "Rot Totem"; obj.m_description = "A corrupted ritual stone used by Shamans to spread rot within enemy territory. Only Shamans may place Rot Totems."; obj.m_craftingStation = null; obj.m_resources = BuildRequirements(); obj.m_destroyedLootPrefab = null; val.AddComponent(); val.AddComponent(); _prefab = val; if (!scene.m_prefabs.Contains(val)) { scene.m_prefabs.Add(val); } ((Dictionary)AccessTools.Field(typeof(ZNetScene), "m_namedPrefabs").GetValue(scene))[StringExtensionMethods.GetStableHashCode("Fehu_RotTotem")] = val; ManualLogSource log2 = Plugin.Log; if (log2 != null) { log2.LogInfo((object)"Registered Fehu_RotTotem from dverger_guardstone."); } } private static Requirement[] BuildRequirements() { return (Requirement[])(object)new Requirement[4] { Requirement("SurtlingCore", 3), Requirement("Stone", 10), Requirement("Wood", 10), Requirement("Coins", 100) }; } private static Requirement Requirement(string prefabName, int amount) { //IL_0026: 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_0032: Unknown result type (might be due to invalid IL or missing references) //IL_0039: Unknown result type (might be due to invalid IL or missing references) //IL_0041: Expected O, but got Unknown ObjectDB instance = ObjectDB.instance; GameObject val = ((instance != null) ? instance.GetItemPrefab(prefabName) : null); ItemDrop resItem = (((Object)(object)val != (Object)null) ? val.GetComponent() : null); return new Requirement { m_resItem = resItem, m_amount = amount, m_recover = false }; } private static void RegisterHammerPiece(ObjectDB objectDb) { //IL_00ba: Unknown result type (might be due to invalid IL or missing references) //IL_00bf: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)objectDb == (Object)null || (Object)(object)_prefab == (Object)null) { return; } PieceCategory? val = ResolveExistingFehuCategory(); GameObject itemPrefab = objectDb.GetItemPrefab("Hammer"); PieceTable val2 = ((!((Object)(object)itemPrefab != (Object)null)) ? null : itemPrefab.GetComponent()?.m_itemData?.m_shared?.m_buildPieces); Piece component = _prefab.GetComponent(); if (!val.HasValue || (Object)(object)component == (Object)null || (Object)(object)val2 == (Object)null) { if (!val.HasValue && !_missingFehuCategoryLogged) { _missingFehuCategoryLogged = true; ManualLogSource log = Plugin.Log; if (log != null) { log.LogWarning((object)"Existing Hammer category Fehu is not available yet; Rot Totem registration deferred."); } } } else { _missingFehuCategoryLogged = false; component.m_category = val.Value; component.m_resources = BuildRequirements(); component.m_destroyedLootPrefab = null; if (!val2.m_pieces.Contains(_prefab)) { val2.m_pieces.Add(_prefab); } } } private static void UpdateHammerVisibility(Player player) { //IL_00a6: Unknown result type (might be due to invalid IL or missing references) //IL_00ab: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)_prefab == (Object)null || (Object)(object)ObjectDB.instance == (Object)null) { return; } GameObject itemPrefab = ObjectDB.instance.GetItemPrefab("Hammer"); PieceTable val = ((!((Object)(object)itemPrefab != (Object)null)) ? null : itemPrefab.GetComponent()?.m_itemData?.m_shared?.m_buildPieces); Piece component = _prefab.GetComponent(); PieceCategory? val2 = ResolveExistingFehuCategory(); if ((Object)(object)val == (Object)null || (Object)(object)component == (Object)null || !val2.HasValue) { val?.m_pieces.Remove(_prefab); return; } component.m_category = val2.Value; PlayerClanInfo playerClanInfo = WarfareManager.Instance?.Data?.GetPlayer(player.GetPlayerID()); bool flag = RoleUtils.HasRole(playerClanInfo, ClanRole.Shaman) || (playerClanInfo != null && playerClanInfo.Rank == ClanRank.Jarl); if (flag && !val.m_pieces.Contains(_prefab)) { val.m_pieces.Add(_prefab); } else if (!flag) { val.m_pieces.Remove(_prefab); } } private static GameObject GetPrefabRoot() { //IL_0012: Unknown result type (might be due to invalid IL or missing references) //IL_001c: Expected O, but got Unknown if ((Object)(object)_prefabRoot == (Object)null) { _prefabRoot = new GameObject("FehuKingdoms_PrefabRoot"); _prefabRoot.SetActive(false); Object.DontDestroyOnLoad((Object)(object)_prefabRoot); } return _prefabRoot; } private static PieceCategory? ResolveExistingFehuCategory() { //IL_006d: Unknown result type (might be due to invalid IL or missing references) //IL_0072: Unknown result type (might be due to invalid IL or missing references) //IL_0080: Unknown result type (might be due to invalid IL or missing references) ResolvePieceManagerReflectionOnce(); if (_pieceManagerType == null || _pieceManagerInstanceProperty == null || _getPieceCategoryMethod == null) { return null; } try { object value = _pieceManagerInstanceProperty.GetValue(null, null); return (((value == null) ? null : _getPieceCategoryMethod.Invoke(value, new object[1] { "Fehu" })) is PieceCategory value2) ? new PieceCategory?(value2) : ((PieceCategory?)null); } catch (Exception ex) { if (!_missingFehuCategoryLogged) { _missingFehuCategoryLogged = true; ManualLogSource log = Plugin.Log; if (log != null) { log.LogWarning((object)("Existing Hammer category Fehu could not be resolved; registration deferred. " + ex.Message)); } } return null; } } private static void ResolvePieceManagerReflectionOnce() { if (_pieceManagerReflectionResolved) { return; } _pieceManagerReflectionResolved = true; try { Assembly[] assemblies = AppDomain.CurrentDomain.GetAssemblies(); foreach (Assembly assembly in assemblies) { if (string.Equals(assembly.GetName().Name, "Jotunn", StringComparison.OrdinalIgnoreCase)) { _pieceManagerType = assembly.GetType("Jotunn.Managers.PieceManager", throwOnError: false); break; } } if (!(_pieceManagerType == null)) { BindingFlags bindingAttr = BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.FlattenHierarchy; _pieceManagerInstanceProperty = _pieceManagerType.GetProperty("Instance", bindingAttr); _getPieceCategoryMethod = _pieceManagerType.GetMethod("GetPieceCategory", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic, null, new Type[1] { typeof(string) }, null); } } catch (Exception ex) { ManualLogSource log = Plugin.Log; if (log != null) { log.LogWarning((object)("Optional Jotunn PieceManager could not be inspected; Rot Totem category registration was deferred. " + ex.Message)); } } } } public sealed class FehuRotTotemNetwork : MonoBehaviour { private ZNetView _view; private bool _resolved; private float _nextAttempt; private void Awake() { _view = ((Component)this).GetComponent(); } private void Update() { //IL_00c8: Unknown result type (might be due to invalid IL or missing references) if (!((Component)this).gameObject.activeInHierarchy || _resolved || Time.time < _nextAttempt || (Object)(object)ZNet.instance == (Object)null || !ZNet.instance.IsServer() || (Object)(object)_view == (Object)null || _view.GetZDO() == null) { return; } _nextAttempt = Time.time + 0.5f; ZDO zDO = _view.GetZDO(); if (zDO.GetInt("Fehu_RotTotem_TotemId", 0) > 0) { _resolved = true; return; } Piece component = ((Component)this).GetComponent(); long num = (((Object)(object)component != (Object)null) ? component.GetCreator() : zDO.GetLong("creator", 0L)); if (num == 0L) { return; } string text = WarfareManager.Instance?.RegisterPhysicalRotTotem(num, ((Component)this).transform.position, _view) ?? "Rot Totem manager unavailable."; _resolved = true; if (!text.Contains("green")) { ManualLogSource log = Plugin.Log; if (log != null) { log.LogWarning((object)("Rejected Fehu_RotTotem placement: " + text)); } ZNetScene instance = ZNetScene.instance; if (instance != null) { instance.Destroy(((Component)this).gameObject); } } } } public sealed class FehuRotTotemVisual : MonoBehaviour { private void Start() { if (((Component)this).gameObject.activeInHierarchy) { ZNetView component = ((Component)this).GetComponent(); if (!((Object)(object)component == (Object)null)) { component.GetZDO(); } ((Behaviour)this).enabled = false; } } } [HarmonyPatch] public static class ScrollBlockingPatches { [HarmonyPatch(typeof(ZInput), "GetMouseScrollWheel")] [HarmonyPrefix] public static bool ZInput_GetMouseScrollWheel_Prefix(ref float __result) { if (ClanSelectionUI.ShouldBlockInput()) { __result = 0f; return false; } return true; } } [HarmonyPatch] public static class ShamanRotTotemCostPatch { private const string RotTotemPrefabName = "Fehu_RotTotem"; [HarmonyPatch(typeof(Player), "HaveRequirements", new Type[] { typeof(Piece), typeof(RequirementMode) })] [HarmonyPrefix] private static void HaveRequirementsPrefix(Player __instance, Piece __0, ref int[] __state) { if (IsDiscountedRotTotem(__instance, __0, out var discount)) { ApplyTemporaryDiscount(__0.m_resources, discount, ref __state); } } [HarmonyPatch(typeof(Player), "HaveRequirements", new Type[] { typeof(Piece), typeof(RequirementMode) })] [HarmonyPostfix] private static void HaveRequirementsPostfix(Piece __0, int[] __state) { RestoreRequirements(__0?.m_resources, __state); } [HarmonyPatch(typeof(Player), "HaveRequirements", new Type[] { typeof(Piece), typeof(RequirementMode) })] [HarmonyFinalizer] private static Exception HaveRequirementsFinalizer(Piece __0, int[] __state, Exception __exception) { RestoreRequirements(__0?.m_resources, __state); return __exception; } [HarmonyPatch(typeof(Player), "ConsumeResources")] [HarmonyPrefix] private static void ConsumeResourcesPrefix(Player __instance, Requirement[] __0, ref int[] __state, PieceTable ___m_buildPieces) { Piece piece = ((___m_buildPieces != null) ? ___m_buildPieces.GetSelectedPiece() : null); if (IsDiscountedRotTotem(__instance, piece, out var discount)) { ApplyTemporaryDiscount(__0, discount, ref __state); } } [HarmonyPatch(typeof(Player), "ConsumeResources")] [HarmonyPostfix] private static void ConsumeResourcesPostfix(Requirement[] __0, int[] __state) { RestoreRequirements(__0, __state); } [HarmonyPatch(typeof(Player), "ConsumeResources")] [HarmonyFinalizer] private static Exception ConsumeResourcesFinalizer(Requirement[] __0, int[] __state, Exception __exception) { RestoreRequirements(__0, __state); return __exception; } private static bool IsDiscountedRotTotem(Player playerObject, Piece piece, out float discount) { //IL_0008: Unknown result type (might be due to invalid IL or missing references) //IL_0013: Expected O, but got Unknown discount = 0f; if ((Object)playerObject == (Object)null || (Object)(object)piece == (Object)null || !string.Equals(((Object)piece).name, "Fehu_RotTotem", StringComparison.Ordinal)) { return false; } float roleFactor = RoleUtils.GetRoleFactor((WarfareManager.Instance?.Data)?.GetPlayer(playerObject.GetPlayerID()), ClanRole.Shaman); if (roleFactor <= 0f) { return false; } discount = Mathf.Clamp01((ClanConfig.ShamanTotemCostReduction?.Value ?? 15f) / 100f * roleFactor); return discount > 0f; } private static void ApplyTemporaryDiscount(Requirement[] requirements, float discount, ref int[] originalAmounts) { if (requirements == null || requirements.Length == 0) { return; } originalAmounts = new int[requirements.Length]; for (int i = 0; i < requirements.Length; i++) { Requirement val = requirements[i]; originalAmounts[i] = val?.m_amount ?? 0; if (val != null && val.m_amount > 0) { val.m_amount = Math.Max(1, Mathf.FloorToInt((float)val.m_amount * (1f - discount))); } } } private static void RestoreRequirements(Requirement[] requirements, int[] originalAmounts) { if (requirements == null || originalAmounts == null) { return; } int num = Math.Min(requirements.Length, originalAmounts.Length); for (int i = 0; i < num; i++) { if (requirements[i] != null) { requirements[i].m_amount = originalAmounts[i]; } } } } [HarmonyPatch] public static class SkaldRolePatches { [HarmonyPatch(typeof(SE_Rested), "UpdateTTL")] [HarmonyPostfix] private static void ExtendRestedDuration(SE_Rested __instance, ref float ___m_time, Character ___m_character) { Player val = (Player)(object)((___m_character is Player) ? ___m_character : null); float skaldFactor = GetSkaldFactor(val); if (!(skaldFactor <= 0f)) { float num = Mathf.Max(0f, ClanConfig.SkaldRestedDurationBonus?.Value ?? 15f) / 100f; float num2 = (__instance.m_baseTTL + (float)(val.GetComfortLevel() - 1) * __instance.m_TTLPerComfortLevel) * (1f + num * skaldFactor); float num3 = ((StatusEffect)__instance).m_ttl - ___m_time; if (num2 > num3) { ((StatusEffect)__instance).m_ttl = num2; ___m_time = 0f; } } } [HarmonyPatch(typeof(SEMan), "ModifyStaminaRegen")] [HarmonyPostfix] private static void IncreaseRestedStaminaRegeneration(SEMan __instance, ref float __0, Character ___m_character) { //IL_0008: Unknown result type (might be due to invalid IL or missing references) //IL_0013: Expected O, but got Unknown Player val = (Player)(object)((___m_character is Player) ? ___m_character : null); if (!((Object)val == (Object)null) && __instance.HaveStatusEffect(SEMan.s_statusEffectRested)) { float skaldFactor = GetSkaldFactor(val); if (!(skaldFactor <= 0f)) { float num = Mathf.Max(0f, ClanConfig.SkaldRestedStaminaRegenBonus?.Value ?? 5f) / 100f; __0 *= 1f + num * skaldFactor; } } } private static float GetSkaldFactor(Player playerObject) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) //IL_000c: Expected O, but got Unknown if ((Object)playerObject == (Object)null) { return 0f; } return RoleUtils.GetRoleFactor((WarfareManager.Instance?.Data)?.GetPlayer(playerObject.GetPlayerID()), ClanRole.Skald); } } [HarmonyPatch] public static class SkipperRolePatches { private const float PrimaryShipSpeedBonus = 0.15f; private const float PrimaryShipDamageReduction = 0.2f; [HarmonyPatch(typeof(Ship), "GetSailForce", new Type[] { typeof(float), typeof(float) })] [HarmonyPostfix] private static void IncreaseControlledShipSailForce(Ship __instance, ref Vector3 __result) { //IL_0011: 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_0028: Unknown result type (might be due to invalid IL or missing references) float controllingSkipperFactor = GetControllingSkipperFactor(__instance); if (controllingSkipperFactor > 0f) { __result *= 1f + 0.15f * controllingSkipperFactor; } } [HarmonyPatch(typeof(WearNTear), "RPC_Damage", new Type[] { typeof(long), typeof(HitData) })] [HarmonyPrefix] private static void ReduceControlledShipDamage(WearNTear __instance, HitData hit) { //IL_000c: Unknown result type (might be due to invalid IL or missing references) //IL_0017: Expected O, but got Unknown if (hit != null) { Ship val = ((Component)__instance).GetComponent(); if ((Object)val == (Object)null) { val = ((Component)__instance).GetComponentInParent(); } float controllingSkipperFactor = GetControllingSkipperFactor(val); if (controllingSkipperFactor > 0f) { ((DamageTypes)(ref hit.m_damage)).Modify(1f - 0.2f * controllingSkipperFactor); } } } [HarmonyReversePatch(/*Could not decode attribute arguments.*/)] [HarmonyPatch(typeof(ShipControlls), "GetUser")] private static long GetControllingPlayerId(ShipControlls controls) { throw new NotImplementedException("Harmony reverse patch stub"); } private static float GetControllingSkipperFactor(Ship ship) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) //IL_000c: Expected O, but got Unknown //IL_0014: Unknown result type (might be due to invalid IL or missing references) //IL_001f: Expected O, but got Unknown if ((Object)ship == (Object)null || (Object)ship.m_shipControlls == (Object)null || !ship.m_shipControlls.HaveValidUser()) { return 0f; } long controllingPlayerId = GetControllingPlayerId(ship.m_shipControlls); if (controllingPlayerId == 0L) { return 0f; } return RoleUtils.GetRoleFactor((WarfareManager.Instance?.Data)?.GetPlayer(controllingPlayerId), ClanRole.Skipper); } } public class TabMenuButtonRenderer : MonoBehaviour { private void OnGUI() { TabMenuPatch.DrawButton(); } } [HarmonyPatch] public static class TabMenuPatch { private static GameObject _clanButton; private static bool _created; private static bool _showButton; private static bool _guiInit; private static GUIStyle _btnStyle; [HarmonyPatch(typeof(InventoryGui), "Show")] [HarmonyPostfix] public static void InventoryGui_Show(InventoryGui __instance) { //IL_000c: Unknown result type (might be due to invalid IL or missing references) //IL_0017: Expected O, but got Unknown //IL_0044: Unknown result type (might be due to invalid IL or missing references) //IL_004a: Expected O, but got Unknown if (_created && (Object)_clanButton != (Object)null) { _clanButton.SetActive(true); return; } try { foreach (Transform item in ((Component)__instance.m_player).transform.parent) { Transform val = item; if (((Object)val).name.Contains("Trophie") || ((Object)val).name.Contains("Trophy")) { break; } } _created = true; } catch { _created = true; } } [HarmonyPatch(typeof(InventoryGui), "Hide")] [HarmonyPostfix] public static void InventoryGui_Hide() { //IL_0005: Unknown result type (might be due to invalid IL or missing references) //IL_0010: Expected O, but got Unknown if ((Object)_clanButton != (Object)null) { _clanButton.SetActive(false); } } [HarmonyPatch(typeof(InventoryGui), "Update")] [HarmonyPostfix] public static void InventoryGui_Update(InventoryGui __instance) { _showButton = true; } public static void DrawButton() { //IL_00b2: Unknown result type (might be due to invalid IL or missing references) //IL_002d: Unknown result type (might be due to invalid IL or missing references) //IL_0032: Unknown result type (might be due to invalid IL or missing references) //IL_003a: Unknown result type (might be due to invalid IL or missing references) //IL_0041: Unknown result type (might be due to invalid IL or missing references) //IL_0048: Unknown result type (might be due to invalid IL or missing references) //IL_0052: Expected O, but got Unknown //IL_0057: Expected O, but got Unknown //IL_0070: Unknown result type (might be due to invalid IL or missing references) //IL_00cd: Unknown result type (might be due to invalid IL or missing references) //IL_00d8: Expected O, but got Unknown if (!_showButton) { return; } if (!InventoryGui.IsVisible()) { _showButton = false; return; } if (!_guiInit) { _guiInit = true; _btnStyle = new GUIStyle(GUI.skin.button) { fontSize = 14, fontStyle = (FontStyle)1, padding = new RectOffset(10, 10, 6, 6) }; _btnStyle.normal.textColor = new Color(1f, 0.85f, 0.4f); } float num = (float)Screen.height / 1080f; float num2 = 160f * num; float num3 = 32f * num; float num4 = (float)(Screen.width / 2) - num2 / 2f; float num5 = 8f * num; if (GUI.Button(new Rect(num4, num5, num2, num3), "Clan Warfare [F3]", _btnStyle) && (Object)WarfareHUD.Instance != (Object)null) { WarfareHUD.Instance.Toggle(); } } } [HarmonyPatch] public static class TerritoryRulePatches { [HarmonyPatch(typeof(WearNTear), "Damage")] [HarmonyPrefix] public static bool WearNTear_Damage_Prefix(WearNTear __instance, HitData hit) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) //IL_000c: Expected O, but got Unknown //IL_0012: Unknown result type (might be due to invalid IL or missing references) //IL_0017: Unknown result type (might be due to invalid IL or missing references) //IL_0067: Unknown result type (might be due to invalid IL or missing references) //IL_0088: Unknown result type (might be due to invalid IL or missing references) //IL_0093: Expected O, but got Unknown //IL_00a1: Unknown result type (might be due to invalid IL or missing references) //IL_00ac: Expected O, but got Unknown if ((Object)__instance == (Object)null || hit == null || hit.m_attacker == ZDOID.None) { return true; } ZNetView component = ((Component)__instance).GetComponent(); ZDO val = (((Object)(object)component != (Object)null) ? component.GetZDO() : null); if (val != null && val.GetInt("Fehu_RotTotem_TotemId", 0) > 0) { return true; } ZNetScene instance = ZNetScene.instance; GameObject val2 = (((Object)(object)instance != (Object)null) ? instance.FindInstance(hit.m_attacker) : null); Player val3 = (((Object)(object)val2 != (Object)null) ? val2.GetComponent() : null); if ((Object)val3 == (Object)null) { return true; } Piece component2 = ((Component)__instance).GetComponent(); if ((Object)component2 == (Object)null) { return true; } bool num = TerritoryRules.CanDamageBuilding(val3, component2); if (!num) { ((Character)val3).Message((MessageType)2, "The realm's border laws protect this structure.", 0, (Sprite)null); } return num; } [HarmonyPatch(typeof(WearNTear), "Destroy")] [HarmonyPrefix] public static void WearNTear_Destroy_Prefix(WearNTear __instance) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) //IL_000c: Expected O, but got Unknown if (!((Object)__instance == (Object)null) && !((Object)(object)ZNet.instance == (Object)null) && ZNet.instance.IsServer()) { ZNetView component = ((Component)__instance).GetComponent(); ZDO obj = (((Object)(object)component != (Object)null) ? component.GetZDO() : null); int num = ((obj != null) ? obj.GetInt("Fehu_RotTotem_TotemId", 0) : 0); if (num > 0) { WarfareManager.Instance?.DestroyRootTotem(num); } } } } [HarmonyPatch] public static class ThiefTheftSystem { private sealed class PendingTheft { public long Id; public long ThiefId; public long TargetId; public long ThiefPeer; public long TargetPeer; public long Created; public byte[] Item; public string Prefab; public string Display; public int Amount; } private const string AttemptRpc = "FCW_TheftAttempt"; private const string SnapshotRequestRpc = "FCW_TheftSnapshotRequest"; private const string SnapshotReplyRpc = "FCW_TheftSnapshotReply"; private const string CapacityRequestRpc = "FCW_TheftCapacityRequest"; private const string CapacityReplyRpc = "FCW_TheftCapacityReply"; private const string RemoveRequestRpc = "FCW_TheftRemoveRequest"; private const string RemoveReplyRpc = "FCW_TheftRemoveReply"; private const string AddRequestRpc = "FCW_TheftAddRequest"; private const string AddReplyRpc = "FCW_TheftAddReply"; private const string RestoreRpc = "FCW_TheftRestore"; private const string ForcePvpRpc = "FCW_TheftForcePvp"; private const float MaximumDistance = 3f; private const long TheftCooldownSeconds = 300L; private const long ForcedPvpSeconds = 300L; private static readonly Dictionary Pending = new Dictionary(); private static readonly HashSet BusyPlayers = new HashSet(); private static readonly RNGCryptoServiceProvider Rng = new RNGCryptoServiceProvider(); private static readonly FieldInfo DragItemField = typeof(InventoryGui).GetField("m_dragItem", BindingFlags.Instance | BindingFlags.NonPublic); private static int _rpcInstance; private static long _nextTransaction = 1L; private static float _lastLocalAttempt; private static bool IsServer { get { if ((Object)(object)ZNet.instance != (Object)null) { return ZNet.instance.IsServer(); } return false; } } [HarmonyPatch(typeof(Player), "Update")] [HarmonyPostfix] private static void PlayerUpdate(Player __instance) { //IL_0001: 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_0015: Expected O, but got Unknown //IL_0015: Expected O, but got Unknown if ((Object)__instance == (Object)Player.m_localPlayer) { RegisterRpcs(); } } private static void RegisterRpcs() { ZRoutedRpc instance = ZRoutedRpc.instance; if (instance != null) { int hashCode = RuntimeHelpers.GetHashCode(instance); if (hashCode != _rpcInstance) { instance.Register("FCW_TheftAttempt", (Action)RpcAttempt); instance.Register("FCW_TheftSnapshotRequest", (Action)RpcSnapshotRequest); instance.Register("FCW_TheftSnapshotReply", (Action)RpcSnapshotReply); instance.Register("FCW_TheftCapacityRequest", (Action)RpcCapacityRequest); instance.Register("FCW_TheftCapacityReply", (Action)RpcCapacityReply); instance.Register("FCW_TheftRemoveRequest", (Action)RpcRemoveRequest); instance.Register("FCW_TheftRemoveReply", (Action)RpcRemoveReply); instance.Register("FCW_TheftAddRequest", (Action)RpcAddRequest); instance.Register("FCW_TheftAddReply", (Action)RpcAddReply); instance.Register("FCW_TheftRestore", (Action)RpcRestore); instance.Register("FCW_TheftForcePvp", (Action)RpcForcePvp); _rpcInstance = hashCode; } } } [HarmonyPatch(typeof(Player), "GetHoverText")] [HarmonyPostfix] private static void HoverText(Player __instance, ref string __result) { if (CanLocalAttempt(__instance)) { __result += "\n[$KEY_Use] Steal"; } } [HarmonyPatch(typeof(Player), "Interact")] [HarmonyPostfix] private static void Interact(Player __instance, GameObject go, bool hold, bool alt) { //IL_0018: Unknown result type (might be due to invalid IL or missing references) //IL_0023: Expected O, but got Unknown //IL_002e: Unknown result type (might be due to invalid IL or missing references) //IL_0039: Expected O, but got Unknown //IL_004e: Unknown result type (might be due to invalid IL or missing references) //IL_0054: Expected O, but got Unknown if (!(hold || alt) && !(Time.time < _lastLocalAttempt + 0.35f) && !((Object)go == (Object)null)) { Player component = go.GetComponent(); if (!((Object)component == (Object)null) && CanLocalAttempt(component)) { _lastLocalAttempt = Time.time; ZPackage val = new ZPackage(); val.Write(component.GetPlayerID()); ToServer("FCW_TheftAttempt", val); } } } private static bool CanLocalAttempt(Player target) { //IL_0019: Unknown result type (might be due to invalid IL or missing references) //IL_0024: Expected O, but got Unknown //IL_0027: Unknown result type (might be due to invalid IL or missing references) //IL_0032: Expected O, but got Unknown //IL_0035: Unknown result type (might be due to invalid IL or missing references) //IL_003b: Unknown result type (might be due to invalid IL or missing references) //IL_0045: Expected O, but got Unknown //IL_0045: Expected O, but got Unknown //IL_0094: Unknown result type (might be due to invalid IL or missing references) //IL_009f: Unknown result type (might be due to invalid IL or missing references) //IL_00a4: Unknown result type (might be due to invalid IL or missing references) Player localPlayer = Player.m_localPlayer; WarfareData warfareData = WarfareManager.Instance?.Data; if ((Object)localPlayer == (Object)null || (Object)target == (Object)null || (Object)localPlayer == (Object)target || warfareData == null || ((Character)target).IsDead() || ((Character)target).IsTeleporting()) { return false; } PlayerClanInfo player = warfareData.GetPlayer(localPlayer.GetPlayerID()); if (RoleUtils.GetRoleFactor(player, ClanRole.Thief) <= 0f || WarfareManager.Now() - player.LastTheftAttemptTimestamp < 300) { return false; } return Vector3.SqrMagnitude(((Component)localPlayer).transform.position - ((Component)target).transform.position) <= 9f; } private static void ToServer(string name, ZPackage p) { RegisterRpcs(); long num; if (!((Object)(object)ZNet.instance != (Object)null) || !ZNet.instance.IsServer()) { ZNet instance = ZNet.instance; num = ((instance == null) ? ((long?)null) : instance.GetServerPeer()?.m_uid).GetValueOrDefault(); } else { num = ZNet.GetUID(); } long num2 = num; ZRoutedRpc instance2 = ZRoutedRpc.instance; if (instance2 != null) { instance2.InvokeRoutedRPC(num2, name, new object[1] { p }); } } private static void Send(long peer, string name, ZPackage p) { ZRoutedRpc instance = ZRoutedRpc.instance; if (instance != null) { instance.InvokeRoutedRPC(peer, name, new object[1] { p }); } } private static void RpcAttempt(long sender, ZPackage p) { //IL_0086: Unknown result type (might be due to invalid IL or missing references) //IL_0091: Expected O, but got Unknown //IL_0094: Unknown result type (might be due to invalid IL or missing references) //IL_009f: Expected O, but got Unknown //IL_00d7: Unknown result type (might be due to invalid IL or missing references) //IL_00e2: Unknown result type (might be due to invalid IL or missing references) //IL_00e7: Unknown result type (might be due to invalid IL or missing references) //IL_020b: Unknown result type (might be due to invalid IL or missing references) //IL_0212: Expected O, but got Unknown if (!IsServer) { return; } Cleanup(); long num = ResolvePeerPlayerId(sender); long num2 = p.ReadLong(); Player val = FindPlayer(num); Player val2 = FindPlayer(num2); PlayerClanInfo playerClanInfo = WarfareManager.Instance?.Data?.GetPlayer(num); PlayerClanInfo playerClanInfo2 = WarfareManager.Instance?.Data?.GetPlayer(num2); long num3 = WarfareManager.Now(); if (num == 0L || num2 == 0L || num == num2 || playerClanInfo == null || playerClanInfo2 == null || (Object)val == (Object)null || (Object)val2 == (Object)null || ((Character)val).IsDead() || ((Character)val2).IsDead() || ((Character)val).IsTeleporting() || ((Character)val2).IsTeleporting() || RoleUtils.GetRoleFactor(playerClanInfo, ClanRole.Thief) <= 0f || Vector3.SqrMagnitude(((Component)val).transform.position - ((Component)val2).transform.position) > 9f) { Notify(sender, "Theft target is invalid or out of range."); return; } long num4 = 300 - (num3 - playerClanInfo.LastTheftAttemptTimestamp); if (num4 > 0) { Notify(sender, "Theft is on cooldown for " + num4 + "s."); return; } if (!CanTargetByRealmRules(val, val2, playerClanInfo, playerClanInfo2, out var reason)) { Notify(sender, reason); return; } if (!HasLineOfSight(val, val2)) { Notify(sender, "You do not have line of sight to that target."); return; } if (BusyPlayers.Contains(num) || BusyPlayers.Contains(num2)) { Notify(sender, "Theft is already being resolved."); return; } long num5 = ResolvePlayerPeer(num2); if (num5 == 0L) { Notify(sender, "Target connection is unavailable."); return; } long num6 = _nextTransaction++; Pending[num6] = new PendingTheft { Id = num6, ThiefId = num, TargetId = num2, ThiefPeer = sender, TargetPeer = num5, Created = num3 }; BusyPlayers.Add(num); BusyPlayers.Add(num2); ZPackage val3 = new ZPackage(); val3.Write(num6); val3.Write(num2); Send(num5, "FCW_TheftSnapshotRequest", val3); } private static void RpcSnapshotRequest(long sender, ZPackage p) { //IL_0015: Unknown result type (might be due to invalid IL or missing references) //IL_0020: Expected O, but got Unknown //IL_0036: Unknown result type (might be due to invalid IL or missing references) //IL_003c: Expected O, but got Unknown //IL_0063: 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_008e: Expected O, but got Unknown //IL_0096: Unknown result type (might be due to invalid IL or missing references) //IL_009d: Expected O, but got Unknown long num = p.ReadLong(); long num2 = p.ReadLong(); Player localPlayer = Player.m_localPlayer; if ((Object)localPlayer == (Object)null || localPlayer.GetPlayerID() != num2) { return; } Inventory val = new Inventory("FCW Theft Snapshot", (Sprite)null, 20, 20); foreach (ItemData item in Eligible(((Humanoid)localPlayer).GetInventory())) { ItemData val2 = item.Clone(); val.AddItem(val2, val2.m_gridPos); } ZPackage val3 = new ZPackage(); val.Save(val3); ZPackage val4 = new ZPackage(); val4.Write(num); val4.Write(val3.GetArray()); ToServer("FCW_TheftSnapshotReply", val4); } private static void RpcSnapshotReply(long sender, ZPackage p) { //IL_00c5: Unknown result type (might be due to invalid IL or missing references) //IL_00cc: Expected O, but got Unknown if (!IsServer) { return; } long num = p.ReadLong(); if (Pending.TryGetValue(num, out var value) && sender == value.TargetPeer) { List list = Eligible(LoadInventory(p.ReadByteArray())); if (list.Count == 0) { Notify(value.ThiefPeer, "Target has no eligible inventory stack."); Finish(value); return; } ItemData val = list[NextInt(list.Count)]; value.Item = SerializeItem(val); PendingTheft pendingTheft = value; GameObject dropPrefab = val.m_dropPrefab; pendingTheft.Prefab = ((dropPrefab != null) ? ((Object)dropPrefab).name : null) ?? ""; value.Display = val.m_shared?.m_name ?? value.Prefab; value.Amount = val.m_stack; ZPackage val2 = new ZPackage(); val2.Write(num); val2.Write(value.Item); Send(value.ThiefPeer, "FCW_TheftCapacityRequest", val2); } } private static void RpcCapacityRequest(long sender, ZPackage p) { //IL_003c: Unknown result type (might be due to invalid IL or missing references) //IL_0043: Expected O, but got Unknown long num = p.ReadLong(); ItemData val = DeserializeItem(p.ReadByteArray()); Player localPlayer = Player.m_localPlayer; Inventory val2 = ((localPlayer != null) ? ((Humanoid)localPlayer).GetInventory() : null); bool flag = val != null && val2 != null && val2.CanAddItem(val, val.m_stack); ZPackage val3 = new ZPackage(); val3.Write(num); val3.Write(flag); ToServer("FCW_TheftCapacityReply", val3); } private static void RpcCapacityReply(long sender, ZPackage p) { //IL_01c3: Unknown result type (might be due to invalid IL or missing references) //IL_01ca: Expected O, but got Unknown //IL_0147: Unknown result type (might be due to invalid IL or missing references) //IL_014e: Expected O, but got Unknown if (!IsServer) { return; } long num = p.ReadLong(); bool flag = p.ReadBool(); if (!Pending.TryGetValue(num, out var value) || sender != value.ThiefPeer) { return; } if (!flag) { Notify(value.ThiefPeer, "Your inventory cannot hold the complete stolen stack."); Finish(value); return; } PlayerClanInfo player = WarfareManager.Instance.Data.GetPlayer(value.ThiefId); PlayerClanInfo player2 = WarfareManager.Instance.Data.GetPlayer(value.TargetId); long lastTheftTargetedTimestamp = (player.LastTheftAttemptTimestamp = WarfareManager.Now()); if (player2 != null) { player2.LastTheftTargetedTimestamp = lastTheftTargetedTimestamp; } WarfareManager.Instance.SaveData(); WarfareRPC.Instance?.SyncAllDataToClients(); float num2 = 65f * RoleUtils.GetRoleFactor(player, ClanRole.Thief); if (NextInt(10000) >= Mathf.RoundToInt(num2 * 100f)) { long num3 = WarfareManager.Now() + 300; player.ForcedPvpUntilUtc = Math.Max(player.ForcedPvpUntilUtc, num3); WarfareManager.Instance.SaveData(); WarfareRPC.Instance?.SyncAllDataToClients(); Notify(value.TargetPeer, (player.PlayerName ?? "A thief") + " tried to steal from you!"); Notify(value.ThiefPeer, "Theft failed. You have been exposed."); ZPackage val = new ZPackage(); val.Write(num3); Send(value.ThiefPeer, "FCW_TheftForcePvp", val); Plugin.Log.LogInfo((object)("Theft failed: thief=" + value.ThiefId + " target=" + value.TargetId + " forcedPvpUntil=" + num3)); Finish(value); } else { ZPackage val2 = new ZPackage(); val2.Write(num); val2.Write(value.Item); Send(value.TargetPeer, "FCW_TheftRemoveRequest", val2); } } private static void RpcRemoveRequest(long sender, ZPackage p) { //IL_007c: Unknown result type (might be due to invalid IL or missing references) //IL_0083: Expected O, but got Unknown long num = p.ReadLong(); ItemData val = DeserializeItem(p.ReadByteArray()); Player localPlayer = Player.m_localPlayer; Inventory val2 = ((localPlayer != null) ? ((Humanoid)localPlayer).GetInventory() : null); ItemData val3 = null; if (val != null && val2 != null) { foreach (ItemData item in Eligible(val2)) { if (SameItem(item, val)) { val3 = item; break; } } } bool flag = val3 != null && val2.RemoveItem(val3); ZPackage val4 = new ZPackage(); val4.Write(num); val4.Write(flag); val4.Write(flag ? SerializeItem(val3) : new byte[0]); ToServer("FCW_TheftRemoveReply", val4); } private static void RpcRemoveReply(long sender, ZPackage p) { //IL_005b: Unknown result type (might be due to invalid IL or missing references) //IL_0062: Expected O, but got Unknown if (!IsServer) { return; } long num = p.ReadLong(); bool flag = p.ReadBool(); byte[] array = p.ReadByteArray(); if (Pending.TryGetValue(num, out var value) && sender == value.TargetPeer) { if (!flag || array.Length == 0) { Notify(value.ThiefPeer, "The selected stack changed before transfer."); Finish(value); return; } value.Item = array; ZPackage val = new ZPackage(); val.Write(num); val.Write(array); Send(value.ThiefPeer, "FCW_TheftAddRequest", val); } } private static void RpcAddRequest(long sender, ZPackage p) { //IL_0045: Unknown result type (might be due to invalid IL or missing references) //IL_004c: Expected O, but got Unknown long num = p.ReadLong(); ItemData val = DeserializeItem(p.ReadByteArray()); Player localPlayer = Player.m_localPlayer; Inventory val2 = ((localPlayer != null) ? ((Humanoid)localPlayer).GetInventory() : null); bool flag = val != null && val2 != null && val2.CanAddItem(val, val.m_stack) && val2.AddItem(val); ZPackage val3 = new ZPackage(); val3.Write(num); val3.Write(flag); ToServer("FCW_TheftAddReply", val3); } private static void RpcAddReply(long sender, ZPackage p) { //IL_0032: Unknown result type (might be due to invalid IL or missing references) //IL_0038: Expected O, but got Unknown if (!IsServer) { return; } long num = p.ReadLong(); bool flag = p.ReadBool(); if (Pending.TryGetValue(num, out var value) && sender == value.ThiefPeer) { if (!flag) { ZPackage val = new ZPackage(); val.Write(num); val.Write(value.Item); Send(value.TargetPeer, "FCW_TheftRestore", val); Notify(value.ThiefPeer, "Theft transfer was rolled back."); Finish(value); return; } Notify(value.ThiefPeer, "Theft successful: stole " + value.Amount + " × " + value.Display + "."); Plugin.Log.LogInfo((object)("Theft success: thief=" + value.ThiefId + " target=" + value.TargetId + " item=" + value.Prefab + " amount=" + value.Amount)); Finish(value); } } private static void RpcRestore(long sender, ZPackage p) { //IL_002e: Unknown result type (might be due to invalid IL or missing references) p.ReadLong(); ItemData val = DeserializeItem(p.ReadByteArray()); Player localPlayer = Player.m_localPlayer; Inventory val2 = ((localPlayer != null) ? ((Humanoid)localPlayer).GetInventory() : null); if (val != null && val2 != null && !val2.AddItem(val, val.m_gridPos)) { val2.AddItem(val); } } private static void RpcForcePvp(long sender, ZPackage p) { //IL_000e: Unknown result type (might be due to invalid IL or missing references) //IL_0019: Expected O, but got Unknown p.ReadLong(); Player localPlayer = Player.m_localPlayer; if ((Object)localPlayer != (Object)null) { GuardianPvPPatch.SetPvpFromFehuPanel(localPlayer, enabled: true); } } private static List Eligible(Inventory inv) { //IL_00d8: Unknown result type (might be due to invalid IL or missing references) List list = new List(); if (inv == null) { return list; } object obj = (((Object)(object)InventoryGui.instance == (Object)null) ? null : DragItemField?.GetValue(InventoryGui.instance)); foreach (ItemData allItem in inv.GetAllItems()) { if (allItem != null && allItem.m_stack > 0 && !allItem.m_equipped && allItem.m_gridPos.y != 0 && allItem != obj && allItem.m_shared != null && !allItem.m_shared.m_questItem && !((Object)(object)allItem.m_dropPrefab == (Object)null)) { string text = (((Object)allItem.m_dropPrefab).name + " " + allItem.m_shared.m_name).ToLowerInvariant(); if (!IsProtectedItemType(allItem.m_shared.m_itemType) && !(allItem.m_shared.m_food > 0f) && !(allItem.m_shared.m_foodStamina > 0f) && !(allItem.m_shared.m_foodEitr > 0f) && !((Object)(object)allItem.m_shared.m_consumeStatusEffect != (Object)null) && !text.Contains("potion") && !text.Contains("mead") && !HasTransferProtection(allItem)) { list.Add(allItem); } } } return list; } private static bool CanTargetByRealmRules(Player thief, Player target, PlayerClanInfo thiefInfo, PlayerClanInfo targetInfo, out string reason) { //IL_0050: Unknown result type (might be due to invalid IL or missing references) //IL_0055: Unknown result type (might be due to invalid IL or missing references) reason = "That target is protected."; if (thiefInfo.ClanId != 0 && thiefInfo.ClanId == targetInfo.ClanId) { return false; } if (PvPManager.IsPlayerInSafeZone(thief) || PvPManager.IsPlayerInSafeZone(target)) { return false; } WarfareData warfareData = WarfareManager.Instance?.Data; if (warfareData == null) { return false; } ClanTerritory territory = warfareData.GetTerritory(GridExtensions.WorldToGrid(((Component)target).transform.position)); WarfareManager instance = WarfareManager.Instance; if (instance != null && instance.IsFreebornProtectedTerritory(territory)) { return false; } if (thiefInfo.ClanId != 0 && targetInfo.ClanId != 0) { WarfareManager instance2 = WarfareManager.Instance; if (instance2 != null && instance2.GetDiplomacyState(thiefInfo.ClanId, targetInfo.ClanId) == DiplomacyState.Allied) { return false; } if (WarfareManager.Instance?.GetActiveNap(thiefInfo.ClanId, targetInfo.ClanId) != null) { return false; } } if (!PvPManager.CanDamage(thief, target)) { return false; } reason = ""; return true; } private static bool HasLineOfSight(Player thief, Player target) { //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_0008: Unknown result type (might be due to invalid IL or missing references) //IL_000d: Unknown result type (might be due to invalid IL or missing references) //IL_000e: Unknown result type (might be due to invalid IL or missing references) //IL_0013: Unknown result type (might be due to invalid IL or missing references) //IL_0026: Unknown result type (might be due to invalid IL or missing references) //IL_0027: Unknown result type (might be due to invalid IL or missing references) //IL_0029: Unknown result type (might be due to invalid IL or missing references) //IL_005b: Unknown result type (might be due to invalid IL or missing references) //IL_0061: Unknown result type (might be due to invalid IL or missing references) //IL_006b: Expected O, but got Unknown //IL_006b: Expected O, but got Unknown Vector3 eyePoint = ((Character)thief).GetEyePoint(); Vector3 val = ((Character)target).GetCenterPoint() - eyePoint; float magnitude = ((Vector3)(ref val)).magnitude; if (magnitude <= 0.01f) { return true; } RaycastHit val2 = default(RaycastHit); if (!Physics.Raycast(eyePoint, val / magnitude, ref val2, magnitude, -5, (QueryTriggerInteraction)1)) { return true; } return (Object)(((Object)(object)((RaycastHit)(ref val2)).collider == (Object)null) ? null : ((Component)((RaycastHit)(ref val2)).collider).GetComponentInParent()) == (Object)target; } private static bool IsProtectedItemType(ItemType type) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_0002: Unknown result type (might be due to invalid IL or missing references) //IL_005c: Expected I4, but got Unknown switch (type - 2) { case 0: case 1: case 2: case 3: case 4: case 5: case 9: case 10: case 12: case 13: case 15: case 16: case 17: case 20: return true; default: return false; } } private static bool HasTransferProtection(ItemData item) { if (item.m_customData == null) { return false; } foreach (string key in item.m_customData.Keys) { string text = (key ?? "").ToLowerInvariant(); if (text.Contains("bound") || text.Contains("protected") || text.Contains("notransfer") || text.Contains("no_transfer")) { return true; } } return false; } private static byte[] SerializeItem(ItemData item) { //IL_000a: Unknown result type (might be due to invalid IL or missing references) //IL_0016: Unknown result type (might be due to invalid IL or missing references) //IL_0019: Unknown result type (might be due to invalid IL or missing references) //IL_0024: Unknown result type (might be due to invalid IL or missing references) //IL_002a: Expected O, but got Unknown Inventory val = new Inventory("FCW Theft Item", (Sprite)null, 20, 20); ItemData val2 = item.Clone(); val.AddItem(val2, val2.m_gridPos); ZPackage val3 = new ZPackage(); val.Save(val3); return val3.GetArray(); } private static ItemData DeserializeItem(byte[] bytes) { List allItems = LoadInventory(bytes).GetAllItems(); if (allItems.Count != 1) { return null; } return allItems[0]; } private static Inventory LoadInventory(byte[] bytes) { //IL_000a: Unknown result type (might be due to invalid IL or missing references) //IL_0010: Expected O, but got Unknown //IL_0019: Unknown result type (might be due to invalid IL or missing references) //IL_0023: Expected O, but got Unknown Inventory val = new Inventory("FCW Theft", (Sprite)null, 20, 20); if (bytes != null && bytes.Length != 0) { val.Load(new ZPackage(bytes)); } return val; } private static bool SameItem(ItemData a, ItemData b) { if (a != null && b != null && a.m_stack == b.m_stack && a.m_quality == b.m_quality && !(Math.Abs(a.m_durability - b.m_durability) > 0.001f) && a.m_variant == b.m_variant && a.m_crafterID == b.m_crafterID && !(a.m_crafterName != b.m_crafterName)) { GameObject dropPrefab = a.m_dropPrefab; string obj = ((dropPrefab != null) ? ((Object)dropPrefab).name : null) ?? ""; GameObject dropPrefab2 = b.m_dropPrefab; if (!(obj != (((dropPrefab2 != null) ? ((Object)dropPrefab2).name : null) ?? ""))) { if ((a.m_customData?.Count ?? 0) != (b.m_customData?.Count ?? 0)) { return false; } if (a.m_customData != null) { foreach (KeyValuePair customDatum in a.m_customData) { if (b.m_customData == null || !b.m_customData.TryGetValue(customDatum.Key, out var value) || value != customDatum.Value) { return false; } } } return true; } } return false; } private static Player FindPlayer(long id) { //IL_0016: Unknown result type (might be due to invalid IL or missing references) //IL_0021: Expected O, but got Unknown foreach (Player allPlayer in Player.GetAllPlayers()) { if ((Object)allPlayer != (Object)null && allPlayer.GetPlayerID() == id) { return allPlayer; } } return null; } private static long ResolvePeerPlayerId(long peer) { //IL_0014: Unknown result type (might be due to invalid IL or missing references) //IL_001f: Expected O, but got Unknown //IL_00a5: Unknown result type (might be due to invalid IL or missing references) if (IsServer && peer == ZNet.GetUID() && (Object)Player.m_localPlayer != (Object)null) { return Player.m_localPlayer.GetPlayerID(); } ZNet instance = ZNet.instance; ZNetPeer val = ((instance != null) ? instance.GetPeer(peer) : null); if (val == null && (Object)(object)ZNet.instance != (Object)null) { foreach (ZNetPeer peer2 in ZNet.instance.GetPeers()) { if (peer2 != null && peer2.m_uid == peer) { val = peer2; break; } } } ZDO obj = ((val != null && ZDOMan.instance != null) ? ZDOMan.instance.GetZDO(val.m_characterID) : null); if (obj == null) { return 0L; } return obj.GetLong(ZDOVars.s_playerID, 0L); } private static long ResolvePlayerPeer(long playerId) { //IL_000c: Unknown result type (might be due to invalid IL or missing references) //IL_0017: Expected O, but got Unknown //IL_0063: Unknown result type (might be due to invalid IL or missing references) if (IsServer && (Object)Player.m_localPlayer != (Object)null && Player.m_localPlayer.GetPlayerID() == playerId) { return ZNet.GetUID(); } if ((Object)(object)ZNet.instance != (Object)null && ZDOMan.instance != null) { foreach (ZNetPeer peer in ZNet.instance.GetPeers()) { ZDO obj = ((peer == null) ? null : ZDOMan.instance.GetZDO(peer.m_characterID)); if (((obj != null) ? obj.GetLong(ZDOVars.s_playerID, 0L) : 0) == playerId) { return peer.m_uid; } } } return 0L; } private static int NextInt(int max) { if (max <= 1) { return 0; } uint num = uint.MaxValue - uint.MaxValue % (uint)max; byte[] array = new byte[4]; uint num2; do { Rng.GetBytes(array); num2 = BitConverter.ToUInt32(array, 0); } while (num2 >= num); return (int)(num2 % (uint)max); } private static void Notify(long peer, string message) { WarfareRPC.Instance?.SendNotification(peer, message); } private static void Finish(PendingTheft x) { Pending.Remove(x.Id); BusyPlayers.Remove(x.ThiefId); BusyPlayers.Remove(x.TargetId); } private static void Cleanup() { long num = WarfareManager.Now(); List list = new List(); foreach (PendingTheft value in Pending.Values) { if (num - value.Created > 20) { list.Add(value); } } foreach (PendingTheft item in list) { Finish(item); } } } [HarmonyPatch] public static class VanillaCoinsPatches { private static bool _loggedSuccess; private static bool _loggedMissing; [HarmonyPatch(typeof(ObjectDB), "Awake")] [HarmonyPostfix] private static void ObjectDBAwakePostfix(ObjectDB __instance) { ApplyConfiguredCoins(__instance); } [HarmonyPatch(typeof(ObjectDB), "CopyOtherDB")] [HarmonyPostfix] private static void ObjectDBCopyOtherDBPostfix(ObjectDB __instance) { ApplyConfiguredCoins(__instance); } public static void ApplyConfiguredCoins(ObjectDB objectDb) { if ((Object)(object)objectDb == (Object)null) { return; } GameObject val = null; try { object? obj = typeof(ObjectDB).GetMethod("GetItemPrefab", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic, null, new Type[1] { typeof(string) }, null)?.Invoke(objectDb, new object[1] { "Coins" }); val = (GameObject)((obj is GameObject) ? obj : null); } catch { val = null; } ItemDrop val2 = (((Object)(object)val != (Object)null) ? val.GetComponent() : null); if ((Object)(object)val2 == (Object)null || val2.m_itemData?.m_shared == null) { if (!_loggedMissing) { ManualLogSource log = Plugin.Log; if (log != null) { log.LogWarning((object)"Vanilla Coins prefab could not be found; no item definitions were modified."); } _loggedMissing = true; } return; } float num = Math.Max(0f, ClanConfig.CoinsWeight?.Value ?? 0f); int num2 = Math.Max(1, ClanConfig.CoinsMaxStack?.Value ?? 100000); val2.m_itemData.m_shared.m_weight = num; val2.m_itemData.m_shared.m_maxStackSize = num2; if (!_loggedSuccess) { ManualLogSource log2 = Plugin.Log; if (log2 != null) { log2.LogInfo((object)$"Vanilla Coins configured: Weight={num}, MaxStack={num2}"); } _loggedSuccess = true; } } } [HarmonyPatch] public static class VaultCrackMonitor { [HarmonyPatch(typeof(Character), "OnDeath")] [HarmonyPostfix] public static void OnDeath(Character __instance) { //IL_0033: Unknown result type (might be due to invalid IL or missing references) //IL_0039: Expected O, but got Unknown //IL_003a: Unknown result type (might be due to invalid IL or missing references) //IL_0045: Expected O, but got Unknown if (!__instance.IsPlayer()) { return; } ZNet instance = ZNet.instance; if ((Object)(object)instance == (Object)null || !instance.IsServer()) { return; } Player val = (Player)((__instance is Player) ? __instance : null); if ((Object)val == (Object)null) { return; } long playerId = val.GetPlayerID(); WarfareData warfareData = WarfareManager.Instance?.Data; if (warfareData != null) { VaultCrackSession vaultCrackSession = warfareData.ActiveCracks.Find((VaultCrackSession c) => c.PlayerId == playerId); if (vaultCrackSession != null) { warfareData.ActiveCracks.Remove(vaultCrackSession); string message = "" + vaultCrackSession.PlayerName + " died while cracking a vault! Attempt failed."; WarfareRPC.Instance?.BroadcastGlobalMessage(message); Plugin.Log.LogInfo((object)("Vault crack cancelled (death): " + vaultCrackSession.PlayerName)); } } } public static void CheckActiveCracks() { //IL_008a: Unknown result type (might be due to invalid IL or missing references) //IL_0095: Expected O, but got Unknown //IL_00d6: Unknown result type (might be due to invalid IL or missing references) //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) //IL_00e2: Unknown result type (might be due to invalid IL or missing references) //IL_00f1: Unknown result type (might be due to invalid IL or missing references) WarfareData warfareData = WarfareManager.Instance?.Data; if (warfareData == null || warfareData.ActiveCracks.Count == 0) { return; } for (int num = warfareData.ActiveCracks.Count - 1; num >= 0; num--) { VaultCrackSession vaultCrackSession = warfareData.ActiveCracks[num]; Player val = null; foreach (Player allPlayer in Player.GetAllPlayers()) { if (allPlayer.GetPlayerID() == vaultCrackSession.PlayerId) { val = allPlayer; break; } } if ((Object)val == (Object)null) { warfareData.ActiveCracks.RemoveAt(num); WarfareRPC.Instance?.BroadcastGlobalMessage("" + vaultCrackSession.PlayerName + " disconnected during vault crack! Attempt failed."); } else { Vector2i val2 = GridExtensions.WorldToGrid(((Component)val).transform.position); if (val2.x != vaultCrackSession.TerritoryGridX || val2.y != vaultCrackSession.TerritoryGridY) { warfareData.ActiveCracks.RemoveAt(num); WarfareRPC.Instance?.BroadcastGlobalMessage("" + vaultCrackSession.PlayerName + " left the territory! Vault crack failed."); } else if (vaultCrackSession.IsComplete) { Plugin.Log.LogInfo((object)$"Vault crack in progress: {vaultCrackSession.PlayerName} ({vaultCrackSession.MinutesElapsed:F1}min)"); } } } } } } namespace FortecaClanWarfare.Network { public static class ClanMapRPC { private sealed class R { public Queue q = new Queue(); } private sealed class RateState { public readonly Queue Times = new Queue(); } private const string DS = "FCW_ClanMapDrawingSnapshot"; private const string DSR = "FCW_ClanMapDrawingSnapshotRequest"; private const string DC = "FCW_ClanMapDrawingCreateRequest"; private const string DU = "FCW_ClanMapDrawingUpdateRequest"; private const string DD = "FCW_ClanMapDrawingDeleteRequest"; private const string DX = "FCW_ClanMapDrawingClearRequest"; private const string DR = "FCW_ClanMapDrawingResult"; private const string DN = "FCW_ClanMapDrawingAccessDenied"; private const string DF = "FCW_ClanMapDrawingConflict"; private static Dictionary rs = new Dictionary(); public const string PinSnapshotRequestRpc = "FCW_ClanMapPinSnapshotRequest"; public const string PinSnapshotRpc = "FCW_ClanMapPinSnapshot"; public const string PinCreateRequestRpc = "FCW_ClanMapPinCreateRequest"; public const string PinCreateResultRpc = "FCW_ClanMapPinCreateResult"; public const string PinUpdateRequestRpc = "FCW_ClanMapPinUpdateRequest"; public const string PinUpdateResultRpc = "FCW_ClanMapPinUpdateResult"; public const string PinDeleteRequestRpc = "FCW_ClanMapPinDeleteRequest"; public const string PinDeleteResultRpc = "FCW_ClanMapPinDeleteResult"; public const string PinClearRequestRpc = "FCW_ClanMapPinClearRequest"; public const string PinClearResultRpc = "FCW_ClanMapPinClearResult"; public const string PinAccessDeniedRpc = "FCW_ClanMapPinAccessDenied"; public const string PinConflictRpc = "FCW_ClanMapPinConflict"; private static readonly Dictionary PinRates = new Dictionary(); public const string OpenRequestRpc = "FCW_ClanMapOpenRequest"; public const string SnapshotRpc = "FCW_ClanMapSnapshot"; public const string AccessDeniedRpc = "FCW_ClanMapAccessDenied"; public const string DetailRequestRpc = "FCW_ClanMapTerritoryDetailRequest"; public const string DetailResponseRpc = "FCW_ClanMapTerritoryDetailResponse"; public const string DetailDeniedRpc = "FCW_ClanMapTerritoryDetailDenied"; public const string PublicDetailRequestRpc = "FCW_ClanMapPublicTerritoryDetailRequest"; public const string PublicDetailResponseRpc = "FCW_ClanMapPublicTerritoryDetailResponse"; public const string PublicDetailDeniedRpc = "FCW_ClanMapPublicTerritoryDetailDenied"; private static int _registeredInstance; private static void TryRegisterDrawings(ZRoutedRpc r) { r.Register("FCW_ClanMapDrawingSnapshotRequest", (Action)delegate(long s, ZPackage p) { Wrap(delegate { Snap(s, ClanMapOpenRequest.Read(p)); }, s); }); r.Register("FCW_ClanMapDrawingCreateRequest", (Action)delegate(long s, ZPackage p) { Wrap(delegate { Create(s, ClanMapDrawingMutationRequest.Read(p)); }, s); }); r.Register("FCW_ClanMapDrawingUpdateRequest", (Action)delegate(long s, ZPackage p) { Wrap(delegate { UpdateD(s, ClanMapDrawingMutationRequest.Read(p)); }, s); }); r.Register("FCW_ClanMapDrawingDeleteRequest", (Action)delegate(long s, ZPackage p) { Wrap(delegate { DeleteD(s, ClanMapDrawingDeleteRequest.Read(p)); }, s); }); r.Register("FCW_ClanMapDrawingClearRequest", (Action)delegate(long s, ZPackage p) { Wrap(delegate { ClearD(s, ClanMapOpenRequest.Read(p)); }, s); }); r.Register("FCW_ClanMapDrawingSnapshot", (Action)RPC_DS); r.Register("FCW_ClanMapDrawingResult", (Action)delegate(long s, string m) { if (Trusted(s)) { ClanMapDrawingUi.ReceiveResult(m); } }); r.Register("FCW_ClanMapDrawingAccessDenied", (Action)delegate(long s, string m) { if (Trusted(s)) { ClanMapDrawingUi.ReceiveDenied(m); } }); r.Register("FCW_ClanMapDrawingConflict", (Action)delegate(long s, string m) { if (Trusted(s)) { ClanMapDrawingUi.ReceiveConflict(m); } }); } public static bool RequestDrawingSnapshot() { return SendD("FCW_ClanMapDrawingSnapshotRequest", delegate(ZPackage p) { new ClanMapOpenRequest().Write(p); }, delegate { Snap(ServerPeer(), new ClanMapOpenRequest()); }); } public static bool RequestDrawingCreate(ClanMapDrawingMutationRequest r) { return SendD("FCW_ClanMapDrawingCreateRequest", delegate(ZPackage p) { r.Write(p); }, delegate { Create(ServerPeer(), r); }); } public static bool RequestDrawingUpdate(ClanMapDrawingMutationRequest r) { return SendD("FCW_ClanMapDrawingUpdateRequest", delegate(ZPackage p) { r.Write(p); }, delegate { UpdateD(ServerPeer(), r); }); } public static bool RequestDrawingDelete(string id, int rev) { ClanMapDrawingDeleteRequest r = new ClanMapDrawingDeleteRequest { DrawingID = id, ExpectedRevision = rev }; return SendD("FCW_ClanMapDrawingDeleteRequest", delegate(ZPackage p) { r.Write(p); }, delegate { DeleteD(ServerPeer(), r); }); } public static bool RequestDrawingClear() { return SendD("FCW_ClanMapDrawingClearRequest", delegate(ZPackage p) { new ClanMapOpenRequest().Write(p); }, delegate { ClearD(ServerPeer(), new ClanMapOpenRequest()); }); } private static bool SendD(string n, Action w, Action l) { //IL_002b: Unknown result type (might be due to invalid IL or missing references) //IL_0031: Expected O, but got Unknown TryRegister(); long num = ServerPeer(); if (num == 0L || ZRoutedRpc.instance == null) { return false; } if (ZNet.instance.IsServer()) { l(); return true; } ZPackage val = new ZPackage(); w(val); ZRoutedRpc.instance.InvokeRoutedRPC(num, n, new object[1] { val }); return true; } private static void Wrap(Action a, long s) { //IL_0005: Unknown result type (might be due to invalid IL or missing references) //IL_0010: Expected O, but got Unknown if ((Object)ZNet.instance == (Object)null || !ZNet.instance.IsServer()) { return; } try { a(); } catch (Exception ex) { Plugin.Log.LogWarning((object)("[FCW ClanDrawings] malformed sender=" + s + " " + ex.Message)); DenyD(s, "Invalid drawing request."); } } private static bool AccessD(long s, Func f, out ClanMapAccessContext a) { if (!ClanMapPermissions.TryResolve(s, out a, out var denialReason) || !f(a, a.Player.ClanId)) { DenyD(s, denialReason); return false; } return true; } private static bool RateD(long s, string o, int m, float sec) { string key = s + o; float realtimeSinceStartup = Time.realtimeSinceStartup; if (!rs.TryGetValue(key, out var value)) { value = new R(); rs[key] = value; } while (value.q.Count > 0 && realtimeSinceStartup - value.q.Peek() > sec) { value.q.Dequeue(); } if (value.q.Count >= m) { DenyD(s, "Drawing rate limit exceeded."); return false; } value.q.Enqueue(realtimeSinceStartup); return true; } private static bool V(ClanMapOpenRequest r, long s) { if (r != null) { return r.ProtocolVersion == 5; } return false; } private static void Snap(long s, ClanMapOpenRequest r) { ClanMapAccessContext a; if (!V(r, s)) { DenyD(s, "Protocol mismatch."); } else if (RateD(s, "s", 10, 10f) && AccessD(s, ClanMapPermissions.CanViewClanDrawings, out a)) { DeliverD(s, ClanMapDrawingStore.Snapshot(a.WorldUID, a.Player.ClanId)); } } private static void Create(long s, ClanMapDrawingMutationRequest r) { if (RateD(s, "c", 5, 10f) && AccessD(s, ClanMapPermissions.CanCreateClanDrawing, out var a)) { if (!ClanMapDrawingStore.Create(a.WorldUID, a.Player.ClanId, a.PlayerID, a.Player.PlayerName, r, out var result)) { DenyD(s, result); } else { Done(a, s, "create", result, r.Points.Count); } } } private static void UpdateD(long s, ClanMapDrawingMutationRequest r) { if (!RateD(s, "u", 10, 10f) || !AccessD(s, ClanMapPermissions.CanEditClanDrawing, out var a)) { return; } if (!ClanMapDrawingStore.Update(a.WorldUID, a.Player.ClanId, a.PlayerID, a.Player.PlayerName, r, out var result, out var conflict)) { if (conflict) { ConflictD(s, result); } else { DenyD(s, result); } } else { Done(a, s, "update", result, r.Points.Count); } } private static void DeleteD(long s, ClanMapDrawingDeleteRequest r) { if (r == null || r.ProtocolVersion != 5 || !RateD(s, "d", 10, 10f) || !AccessD(s, ClanMapPermissions.CanDeleteClanDrawing, out var a)) { return; } if (!ClanMapDrawingStore.Delete(a.WorldUID, a.Player.ClanId, r.DrawingID, r.ExpectedRevision, out var result, out var conflict)) { if (conflict) { ConflictD(s, result); } else { DenyD(s, result); } } else { Done(a, s, "delete", result, 0); } } private static void ClearD(long s, ClanMapOpenRequest r) { if (V(r, s) && RateD(s, "x", 1, 30f) && AccessD(s, ClanMapPermissions.CanClearClanDrawings, out var a)) { if (!ClanMapDrawingStore.Clear(a.WorldUID, a.Player.ClanId, out var error)) { DenyD(s, error); } else { Done(a, s, "clear", "all", 0); } } } private static void Done(ClanMapAccessContext a, long s, string op, string id, int pts) { Plugin.Log.LogInfo((object)("[FCW ClanDrawings] clan=" + a.Player.ClanId + " action=" + op + " drawing=" + id + " points=" + pts + " actor=" + a.PlayerID + " world=" + a.WorldUID)); ResultD(s, "Drawing " + op + " complete."); PushD(a); } private static void PushD(ClanMapAccessContext a) { long uID = ZNet.GetUID(); HashSet hashSet = new HashSet(); if (ClanMapPermissions.TryResolve(uID, out var context, out var denialReason) && context.Player.ClanId == a.Player.ClanId) { DeliverD(uID, ClanMapDrawingStore.Snapshot(a.WorldUID, a.Player.ClanId)); hashSet.Add(uID); } foreach (ZNetPeer peer in ZNet.instance.GetPeers()) { if (peer != null && !hashSet.Contains(peer.m_uid) && ClanMapPermissions.TryResolve(peer.m_uid, out var context2, out denialReason) && context2.Player.ClanId == a.Player.ClanId) { DeliverD(peer.m_uid, ClanMapDrawingStore.Snapshot(a.WorldUID, a.Player.ClanId)); } } } private static void DeliverD(long r, ClanMapDrawingSnapshot s) { //IL_000f: Unknown result type (might be due to invalid IL or missing references) //IL_0015: Expected O, but got Unknown if (IsLocalHostRecipient(r)) { ClanMapDrawingUi.ReceiveSnapshot(s); return; } ZPackage val = new ZPackage(); s.Write(val); if (val.Size() > 524288) { DenyD(r, "Snapshot too large."); return; } ZRoutedRpc.instance.InvokeRoutedRPC(r, "FCW_ClanMapDrawingSnapshot", new object[1] { val }); } private static void RPC_DS(long s, ZPackage p) { if (!Trusted(s)) { return; } try { if (p.Size() > 524288) { throw new Exception(); } ClanMapDrawingUi.ReceiveSnapshot(ClanMapDrawingSnapshot.Read(p)); } catch { ClanMapDrawingUi.ReceiveDenied("Invalid drawing snapshot."); } } private static void ResultD(long r, string m) { if (IsLocalHostRecipient(r)) { ClanMapDrawingUi.ReceiveResult(m); return; } ZRoutedRpc.instance.InvokeRoutedRPC(r, "FCW_ClanMapDrawingResult", new object[1] { m }); } private static void DenyD(long r, string m) { if (IsLocalHostRecipient(r)) { ClanMapDrawingUi.ReceiveDenied(m); return; } ZRoutedRpc instance = ZRoutedRpc.instance; if (instance != null) { instance.InvokeRoutedRPC(r, "FCW_ClanMapDrawingAccessDenied", new object[1] { m ?? "Denied." }); } } private static void ConflictD(long r, string m) { if (IsLocalHostRecipient(r)) { ClanMapDrawingUi.ReceiveConflict(m); return; } ZRoutedRpc instance = ZRoutedRpc.instance; if (instance != null) { instance.InvokeRoutedRPC(r, "FCW_ClanMapDrawingConflict", new object[1] { m }); } } private static void TryRegisterPins(ZRoutedRpc rpc) { rpc.Register("FCW_ClanMapPinSnapshotRequest", (Action)RPC_PinSnapshotRequest); rpc.Register("FCW_ClanMapPinSnapshot", (Action)RPC_PinSnapshot); rpc.Register("FCW_ClanMapPinCreateRequest", (Action)RPC_PinCreate); rpc.Register("FCW_ClanMapPinCreateResult", (Action)RPC_PinCreateResult); rpc.Register("FCW_ClanMapPinUpdateRequest", (Action)RPC_PinUpdate); rpc.Register("FCW_ClanMapPinUpdateResult", (Action)RPC_PinUpdateResult); rpc.Register("FCW_ClanMapPinDeleteRequest", (Action)RPC_PinDelete); rpc.Register("FCW_ClanMapPinDeleteResult", (Action)RPC_PinDeleteResult); rpc.Register("FCW_ClanMapPinClearRequest", (Action)RPC_PinClear); rpc.Register("FCW_ClanMapPinClearResult", (Action)RPC_PinClearResult); rpc.Register("FCW_ClanMapPinAccessDenied", (Action)RPC_PinDenied); rpc.Register("FCW_ClanMapPinConflict", (Action)RPC_PinConflict); } public static bool RequestPinSnapshot() { return SendSimple("FCW_ClanMapPinSnapshotRequest"); } public static bool RequestPinCreate(ClanMapPinMutationRequest r) { return SendPackage("FCW_ClanMapPinCreateRequest", delegate(ZPackage p) { r.Write(p); }, delegate { ProcessCreate(ServerPeer(), r); }); } public static bool RequestPinUpdate(ClanMapPinMutationRequest r) { return SendPackage("FCW_ClanMapPinUpdateRequest", delegate(ZPackage p) { r.Write(p); }, delegate { ProcessUpdate(ServerPeer(), r); }); } public static bool RequestPinDelete(string id, int revision) { ClanMapPinDeleteRequest r = new ClanMapPinDeleteRequest { PinID = id, ExpectedRevision = revision }; return SendPackage("FCW_ClanMapPinDeleteRequest", delegate(ZPackage p) { r.Write(p); }, delegate { ProcessDelete(ServerPeer(), r); }); } public static bool RequestPinClear() { return SendSimple("FCW_ClanMapPinClearRequest"); } private static bool SendSimple(string rpc) { ClanMapPinSimpleRequest r = new ClanMapPinSimpleRequest(); return SendPackage(rpc, delegate(ZPackage p) { r.Write(p); }, delegate { long sender = ServerPeer(); if (rpc == "FCW_ClanMapPinSnapshotRequest") { ProcessPinSnapshot(sender, r); } else { ProcessClear(sender, r); } }); } private static bool SendPackage(string rpc, Action write, Action local) { //IL_002b: Unknown result type (might be due to invalid IL or missing references) //IL_0031: Expected O, but got Unknown TryRegister(); long num = ServerPeer(); if (num == 0L || ZRoutedRpc.instance == null) { return false; } if (ZNet.instance.IsServer()) { local(); return true; } ZPackage val = new ZPackage(); write(val); ZRoutedRpc.instance.InvokeRoutedRPC(num, rpc, new object[1] { val }); return true; } private static void RPC_PinSnapshotRequest(long s, ZPackage p) { if (IsServer()) { Try(delegate { ProcessPinSnapshot(s, ClanMapPinSimpleRequest.Read(p)); }, s, "snapshot"); } } private static void RPC_PinCreate(long s, ZPackage p) { if (IsServer()) { Try(delegate { ProcessCreate(s, ClanMapPinMutationRequest.Read(p)); }, s, "create"); } } private static void RPC_PinUpdate(long s, ZPackage p) { if (IsServer()) { Try(delegate { ProcessUpdate(s, ClanMapPinMutationRequest.Read(p)); }, s, "update"); } } private static void RPC_PinDelete(long s, ZPackage p) { if (IsServer()) { Try(delegate { ProcessDelete(s, ClanMapPinDeleteRequest.Read(p)); }, s, "delete"); } } private static void RPC_PinClear(long s, ZPackage p) { if (IsServer()) { Try(delegate { ProcessClear(s, ClanMapPinSimpleRequest.Read(p)); }, s, "clear"); } } private static bool IsServer() { //IL_0005: Unknown result type (might be due to invalid IL or missing references) //IL_0010: Expected O, but got Unknown if ((Object)ZNet.instance != (Object)null) { return ZNet.instance.IsServer(); } return false; } private static void Try(Action action, long sender, string op) { try { action(); } catch (Exception ex) { Plugin.Log.LogWarning((object)("[FCW ClanPins] Malformed " + op + " sender=" + sender + " error=" + ex.Message)); PinDeny(sender, "Invalid clan pin request."); } } private static bool Access(long sender, Func permission, out ClanMapAccessContext a) { if (!ClanMapPermissions.TryResolve(sender, out a, out var denialReason) || !permission(a, a.Player.ClanId)) { PinDeny(sender, string.IsNullOrEmpty(denialReason) ? "Clan pin access denied." : denialReason); return false; } return true; } private static void ProcessPinSnapshot(long sender, ClanMapPinSimpleRequest r) { ClanMapAccessContext a; if (r == null || r.ProtocolVersion != 5) { PinDeny(sender, "Clan Map protocol mismatch."); } else if (Rate(sender, "snapshot", 10, 10f) && Access(sender, ClanMapPermissions.CanViewClanPins, out a)) { DeliverPinSnapshot(sender, ClanMapPinStore.Snapshot(a.WorldUID, a.Player.ClanId)); } } private static void ProcessCreate(long sender, ClanMapPinMutationRequest r) { if (Rate(sender, "create", 5, 10f) && Access(sender, ClanMapPermissions.CanCreateClanPin, out var a)) { if (!ClanMapPinStore.Create(a.WorldUID, a.Player.ClanId, a.PlayerID, a.Player.PlayerName, r, out var error)) { PinDeny(sender, error); return; } Plugin.Log.LogInfo((object)("[FCW ClanPins] clan=" + a.Player.ClanId + " action=create pin=" + error + " actor=" + a.PlayerID + " world=" + a.WorldUID)); PinResult(sender, "FCW_ClanMapPinCreateResult", "Clan pin created."); PushClan(a); } } private static void ProcessUpdate(long sender, ClanMapPinMutationRequest r) { if (!Rate(sender, "update", 10, 10f) || !Access(sender, ClanMapPermissions.CanEditClanPin, out var a)) { return; } if (!ClanMapPinStore.Update(a.WorldUID, a.Player.ClanId, a.PlayerID, a.Player.PlayerName, r, out var error, out var conflict)) { if (conflict) { PinConflict(sender, error); } else { PinDeny(sender, error); } return; } Plugin.Log.LogInfo((object)("[FCW ClanPins] clan=" + a.Player.ClanId + " action=update pin=" + error + " actor=" + a.PlayerID + " world=" + a.WorldUID)); PinResult(sender, "FCW_ClanMapPinUpdateResult", "Clan pin updated."); PushClan(a); } private static void ProcessDelete(long sender, ClanMapPinDeleteRequest r) { if (r == null || r.ProtocolVersion != 5 || r.PinID.Length > 64) { PinDeny(sender, "Invalid clan pin request."); } else { if (!Rate(sender, "delete", 10, 10f) || !Access(sender, ClanMapPermissions.CanDeleteClanPin, out var a)) { return; } if (!ClanMapPinStore.Delete(a.WorldUID, a.Player.ClanId, r.PinID, r.ExpectedRevision, out var error, out var conflict)) { if (conflict) { PinConflict(sender, error); } else { PinDeny(sender, error); } return; } Plugin.Log.LogInfo((object)("[FCW ClanPins] clan=" + a.Player.ClanId + " action=delete pin=" + error + " actor=" + a.PlayerID + " world=" + a.WorldUID)); PinResult(sender, "FCW_ClanMapPinDeleteResult", "Clan pin deleted."); PushClan(a); } } private static void ProcessClear(long sender, ClanMapPinSimpleRequest r) { ClanMapAccessContext a; if (r == null || r.ProtocolVersion != 5) { PinDeny(sender, "Clan Map protocol mismatch."); } else if (Rate(sender, "clear", 1, 30f) && Access(sender, ClanMapPermissions.CanClearClanPins, out a)) { if (!ClanMapPinStore.Clear(a.WorldUID, a.Player.ClanId, out var error)) { PinDeny(sender, error); return; } Plugin.Log.LogInfo((object)("[FCW ClanPins] clan=" + a.Player.ClanId + " action=clear actor=" + a.PlayerID + " world=" + a.WorldUID)); PinResult(sender, "FCW_ClanMapPinClearResult", "All clan pins cleared."); PushClan(a); } } private static bool Rate(long sender, string action, int max, float seconds) { string key = sender + ":" + action; float realtimeSinceStartup = Time.realtimeSinceStartup; if (!PinRates.TryGetValue(key, out var value)) { value = new RateState(); PinRates[key] = value; } while (value.Times.Count > 0 && realtimeSinceStartup - value.Times.Peek() > seconds) { value.Times.Dequeue(); } if (value.Times.Count >= max) { PinDeny(sender, "Clan pin request rate limit exceeded."); return false; } value.Times.Enqueue(realtimeSinceStartup); return true; } private static void PushClan(ClanMapAccessContext a) { HashSet hashSet = new HashSet(); long uID = ZNet.GetUID(); if (ClanMapPermissions.TryResolve(uID, out var context, out var denialReason) && context.WorldUID == a.WorldUID && context.Player.ClanId == a.Player.ClanId) { DeliverPinSnapshot(uID, ClanMapPinStore.Snapshot(a.WorldUID, a.Player.ClanId)); hashSet.Add(uID); } foreach (ZNetPeer peer in ZNet.instance.GetPeers()) { if (peer != null && !hashSet.Contains(peer.m_uid) && ClanMapPermissions.TryResolve(peer.m_uid, out var context2, out denialReason) && context2.WorldUID == a.WorldUID && context2.Player.ClanId == a.Player.ClanId) { DeliverPinSnapshot(peer.m_uid, ClanMapPinStore.Snapshot(a.WorldUID, a.Player.ClanId)); } } } private static void DeliverPinSnapshot(long recipient, ClanMapPinSnapshot s) { //IL_000f: Unknown result type (might be due to invalid IL or missing references) //IL_0015: Expected O, but got Unknown if (IsLocalHostRecipient(recipient)) { ClanMapPinUi.ReceiveSnapshot(s); return; } ZPackage val = new ZPackage(); s.Write(val); ZRoutedRpc.instance.InvokeRoutedRPC(recipient, "FCW_ClanMapPinSnapshot", new object[1] { val }); } private static void RPC_PinSnapshot(long sender, ZPackage p) { if (!Trusted(sender)) { return; } try { ClanMapPinUi.ReceiveSnapshot(ClanMapPinSnapshot.Read(p)); } catch (Exception ex) { Plugin.Log.LogWarning((object)("[FCW ClanPins] Invalid snapshot: " + ex.Message)); ClanMapPinUi.ReceiveDenied("Invalid clan pin snapshot."); } } private static void RPC_PinCreateResult(long s, string m) { if (Trusted(s)) { ClanMapPinUi.ReceiveResult(m); } } private static void RPC_PinUpdateResult(long s, string m) { if (Trusted(s)) { ClanMapPinUi.ReceiveResult(m); } } private static void RPC_PinDeleteResult(long s, string m) { if (Trusted(s)) { ClanMapPinUi.ReceiveResult(m); } } private static void RPC_PinClearResult(long s, string m) { if (Trusted(s)) { ClanMapPinUi.ReceiveResult(m); } } private static void RPC_PinDenied(long s, string m) { if (Trusted(s)) { ClanMapPinUi.ReceiveDenied(m); } } private static void RPC_PinConflict(long s, string m) { if (Trusted(s)) { ClanMapPinUi.ReceiveConflict(m); } } private static void PinResult(long recipient, string rpc, string message) { if (IsLocalHostRecipient(recipient)) { ClanMapPinUi.ReceiveResult(message); return; } ZRoutedRpc.instance.InvokeRoutedRPC(recipient, rpc, new object[1] { message }); } private static void PinDeny(long recipient, string message) { if (IsLocalHostRecipient(recipient)) { ClanMapPinUi.ReceiveDenied(message); return; } ZRoutedRpc instance = ZRoutedRpc.instance; if (instance != null) { instance.InvokeRoutedRPC(recipient, "FCW_ClanMapPinAccessDenied", new object[1] { message ?? "Clan pin access denied." }); } } private static void PinConflict(long recipient, string message) { if (IsLocalHostRecipient(recipient)) { ClanMapPinUi.ReceiveConflict(message); return; } ZRoutedRpc instance = ZRoutedRpc.instance; if (instance != null) { instance.InvokeRoutedRPC(recipient, "FCW_ClanMapPinConflict", new object[1] { message }); } } public static void TryRegister() { ZRoutedRpc instance = ZRoutedRpc.instance; if (instance == null) { return; } int hashCode = RuntimeHelpers.GetHashCode(instance); if (_registeredInstance == hashCode) { return; } try { instance.Register("FCW_ClanMapOpenRequest", (Action)RPC_OnOpenRequest); instance.Register("FCW_ClanMapSnapshot", (Action)RPC_OnSnapshot); instance.Register("FCW_ClanMapAccessDenied", (Action)RPC_OnAccessDenied); instance.Register("FCW_ClanMapTerritoryDetailRequest", (Action)RPC_OnDetailRequest); instance.Register("FCW_ClanMapTerritoryDetailResponse", (Action)RPC_OnDetailResponse); instance.Register("FCW_ClanMapTerritoryDetailDenied", (Action)RPC_OnDetailDenied); instance.Register("FCW_ClanMapPublicTerritoryDetailRequest", (Action)RPC_OnPublicDetailRequest); instance.Register("FCW_ClanMapPublicTerritoryDetailResponse", (Action)RPC_OnPublicDetailResponse); instance.Register("FCW_ClanMapPublicTerritoryDetailDenied", (Action)RPC_OnPublicDetailDenied); TryRegisterPins(instance); TryRegisterDrawings(instance); _registeredInstance = hashCode; Plugin.Log.LogInfo((object)("[FCW ClanMap] RPC registered protocol=" + 5 + ".")); } catch (ArgumentException) { _registeredInstance = hashCode; Plugin.Log.LogWarning((object)"[FCW ClanMap] RPC registration already exists."); } } private static long ServerPeer() { //IL_0005: Unknown result type (might be due to invalid IL or missing references) //IL_0010: Expected O, but got Unknown if (!((Object)ZNet.instance == (Object)null)) { if (!ZNet.instance.IsServer()) { return ZNet.instance.GetServerPeer()?.m_uid ?? 0; } return ZNet.GetUID(); } return 0L; } public static bool RequestOpen() { //IL_0062: Unknown result type (might be due to invalid IL or missing references) //IL_0068: Expected O, but got Unknown TryRegister(); long num = ServerPeer(); if (num == 0L || ZRoutedRpc.instance == null) { return false; } Plugin.Log.LogInfo((object)("[FCW ClanMap] Open request sent target=" + num + " host=" + ZNet.instance.IsServer())); if (ZNet.instance.IsServer()) { ProcessOpen(num, new ClanMapOpenRequest()); return true; } ZPackage val = new ZPackage(); new ClanMapOpenRequest().Write(val); ZRoutedRpc.instance.InvokeRoutedRPC(num, "FCW_ClanMapOpenRequest", new object[1] { val }); return true; } public static bool RequestTerritoryDetail(string territoryId) { //IL_004b: Unknown result type (might be due to invalid IL or missing references) //IL_0051: Expected O, but got Unknown TryRegister(); long num = ServerPeer(); if (num == 0L || ZRoutedRpc.instance == null || string.IsNullOrEmpty(territoryId) || territoryId.Length > 80) { return false; } ClanMapTerritoryDetailRequest clanMapTerritoryDetailRequest = new ClanMapTerritoryDetailRequest { TerritoryID = territoryId }; if (ZNet.instance.IsServer()) { ProcessDetail(num, clanMapTerritoryDetailRequest); return true; } ZPackage val = new ZPackage(); clanMapTerritoryDetailRequest.Write(val); ZRoutedRpc.instance.InvokeRoutedRPC(num, "FCW_ClanMapTerritoryDetailRequest", new object[1] { val }); return true; } public static bool RequestPublicTerritoryDetail(string territoryId) { //IL_004b: Unknown result type (might be due to invalid IL or missing references) //IL_0051: Expected O, but got Unknown TryRegister(); long num = ServerPeer(); if (num == 0L || ZRoutedRpc.instance == null || string.IsNullOrEmpty(territoryId) || territoryId.Length > 80) { return false; } ClanMapTerritoryDetailRequest clanMapTerritoryDetailRequest = new ClanMapTerritoryDetailRequest { TerritoryID = territoryId }; if (ZNet.instance.IsServer()) { ProcessPublicDetail(num, clanMapTerritoryDetailRequest); return true; } ZPackage val = new ZPackage(); clanMapTerritoryDetailRequest.Write(val); ZRoutedRpc.instance.InvokeRoutedRPC(num, "FCW_ClanMapPublicTerritoryDetailRequest", new object[1] { val }); return true; } private static void RPC_OnOpenRequest(long sender, ZPackage package) { //IL_0005: Unknown result type (might be due to invalid IL or missing references) //IL_0010: Expected O, but got Unknown if ((Object)ZNet.instance == (Object)null || !ZNet.instance.IsServer()) { return; } Plugin.Log.LogInfo((object)("[FCW ClanMap] Open request received sender=" + sender)); try { ProcessOpen(sender, ClanMapOpenRequest.Read(package)); } catch (Exception ex) { Plugin.Log.LogWarning((object)("[FCW ClanMap] Open request malformed sender=" + sender + " error=" + ex.Message)); Deny(sender, "Clan Map access denied."); } } private static void ProcessOpen(long sender, ClanMapOpenRequest request) { try { if (request == null || request.ProtocolVersion != 5) { Plugin.Log.LogWarning((object)("[FCW ClanMap] Authorization denied protocol sender=" + sender)); Deny(sender, "Clan Map protocol mismatch."); return; } if (!ClanMapPermissions.TryResolve(sender, out var context, out var denialReason) || !ClanMapPermissions.CanViewClanMap(context, context.Player.ClanId)) { Plugin.Log.LogWarning((object)("[FCW ClanMap] Authorization denied sender=" + sender)); Deny(sender, denialReason); return; } Plugin.Log.LogInfo((object)("[FCW ClanMap] Sender resolved player=" + context.PlayerID + " clan=" + context.Player.ClanId)); int clanId = context.Player.ClanId; ClanMapSnapshot clanMapSnapshot = new ClanMapSnapshot { ProtocolVersion = 5, WorldUID = context.WorldUID, ClanID = clanId, ClanName = (context.Clan.Name ?? ""), ClanTag = (context.Clan.Tag ?? ""), ClanColorR = context.Clan.ColorR, ClanColorG = context.Clan.ColorG, ClanColorB = context.Clan.ColorB, ClanLogoKey = (context.Clan.ClanEmblemName ?? ""), CanEdit = ClanMapPermissions.CanEditClanMap(context, clanId), CanClear = ClanMapPermissions.CanClearClanMap(context, clanId), CanClearPins = ClanMapPermissions.CanClearClanPins(context, clanId), CanClearDrawings = ClanMapPermissions.CanClearClanDrawings(context, clanId), SnapshotRevision = WarfareManager.Instance.Data.Version, ServerTimestampUtc = DateTimeOffset.UtcNow.ToUnixTimeSeconds(), Territories = ClanMapProjectionService.ProjectMarkers(context), PublicTerritories = ClanMapProjectionService.ProjectPublicTerritories(context) }; Plugin.Log.LogInfo((object)("[FCW ClanMap] Authorization accepted; snapshot markers=" + clanMapSnapshot.Territories.Count + " recipient=" + sender)); DeliverSnapshot(sender, clanMapSnapshot); } catch (Exception ex) { Plugin.Log.LogWarning((object)("[FCW ClanMap] Open rejected sender=" + sender + " error=" + ex.Message)); Deny(sender, "Clan Map access denied."); } } private static void DeliverSnapshot(long recipient, ClanMapSnapshot snapshot) { //IL_001e: Unknown result type (might be due to invalid IL or missing references) //IL_0024: Expected O, but got Unknown if (IsLocalHostRecipient(recipient)) { Plugin.Log.LogInfo((object)"[FCW ClanMap] Snapshot delivered through local host path."); ClanWorldMapPanel.ReceiveSnapshot(snapshot); return; } ZPackage val = new ZPackage(); snapshot.Write(val); ZRoutedRpc.instance.InvokeRoutedRPC(recipient, "FCW_ClanMapSnapshot", new object[1] { val }); Plugin.Log.LogInfo((object)("[FCW ClanMap] Snapshot sent recipient=" + recipient)); } private static void RPC_OnDetailRequest(long sender, ZPackage package) { //IL_0005: Unknown result type (might be due to invalid IL or missing references) //IL_0010: Expected O, but got Unknown if ((Object)ZNet.instance == (Object)null || !ZNet.instance.IsServer()) { return; } try { ProcessDetail(sender, ClanMapTerritoryDetailRequest.Read(package)); } catch (Exception ex) { Plugin.Log.LogWarning((object)("[FCW ClanMap] Detail malformed sender=" + sender + " error=" + ex.Message)); DenyDetail(sender, "Territory detail denied."); } } private static void ProcessDetail(long sender, ClanMapTerritoryDetailRequest request) { //IL_0068: Unknown result type (might be due to invalid IL or missing references) //IL_006e: Expected O, but got Unknown try { if (request == null || request.ProtocolVersion != 5) { DenyDetail(sender, "Clan Map protocol mismatch."); return; } if (!ClanMapPermissions.TryResolve(sender, out var context, out var _) || !ClanMapPermissions.CanViewClanMap(context, context.Player.ClanId) || !ClanMapProjectionService.TryProjectDetail(context, request.TerritoryID, out var detail)) { DenyDetail(sender, "Territory unavailable or no longer owned."); return; } if (IsLocalHostRecipient(sender)) { ClanWorldMapPanel.ReceiveTerritoryDetail(detail); return; } ZPackage val = new ZPackage(); detail.Write(val); ZRoutedRpc.instance.InvokeRoutedRPC(sender, "FCW_ClanMapTerritoryDetailResponse", new object[1] { val }); } catch (Exception ex) { Plugin.Log.LogWarning((object)("[FCW ClanMap] Detail rejected sender=" + sender + " error=" + ex.Message)); DenyDetail(sender, "Territory detail denied."); } } private static void RPC_OnPublicDetailRequest(long sender, ZPackage package) { //IL_0005: Unknown result type (might be due to invalid IL or missing references) //IL_0010: Expected O, but got Unknown if ((Object)ZNet.instance == (Object)null || !ZNet.instance.IsServer()) { return; } try { ProcessPublicDetail(sender, ClanMapTerritoryDetailRequest.Read(package)); } catch (Exception ex) { Plugin.Log.LogWarning((object)("[FCW ClanMap] Public detail malformed sender=" + sender + " error=" + ex.Message)); DenyPublicDetail(sender, "Public territory detail denied."); } } private static void ProcessPublicDetail(long sender, ClanMapTerritoryDetailRequest request) { //IL_0062: Unknown result type (might be due to invalid IL or missing references) //IL_0068: Expected O, but got Unknown if (request == null || request.ProtocolVersion != 5) { DenyPublicDetail(sender, "Clan Map protocol mismatch."); return; } if (!ClanMapPermissions.TryResolve(sender, out var context, out var _) || !ClanMapPermissions.CanViewClanMap(context, context.Player.ClanId) || !ClanMapProjectionService.TryProjectPublicDetail(context, request.TerritoryID, out var detail)) { DenyPublicDetail(sender, "Public territory unavailable."); return; } if (IsLocalHostRecipient(sender)) { ClanWorldMapPanel.ReceivePublicTerritoryDetail(detail); return; } ZPackage val = new ZPackage(); detail.Write(val); ZRoutedRpc.instance.InvokeRoutedRPC(sender, "FCW_ClanMapPublicTerritoryDetailResponse", new object[1] { val }); } private static bool IsLocalHostRecipient(long recipient) { //IL_0005: Unknown result type (might be due to invalid IL or missing references) //IL_0010: Expected O, but got Unknown //IL_002b: Unknown result type (might be due to invalid IL or missing references) //IL_0036: Expected O, but got Unknown if ((Object)ZNet.instance != (Object)null && ZNet.instance.IsServer() && recipient == ZNet.GetUID()) { return (Object)Player.m_localPlayer != (Object)null; } return false; } private static void RPC_OnSnapshot(long sender, ZPackage p) { if (!Trusted(sender)) { return; } try { ClanWorldMapPanel.ReceiveSnapshot(ClanMapSnapshot.Read(p)); Plugin.Log.LogInfo((object)"[FCW ClanMap] Snapshot received."); } catch (Exception ex) { Plugin.Log.LogWarning((object)("[FCW ClanMap] Invalid snapshot: " + ex.Message)); ClanWorldMapPanel.ReceiveInvalidSnapshot(); } } private static void RPC_OnDetailResponse(long sender, ZPackage p) { if (!Trusted(sender)) { return; } try { ClanWorldMapPanel.ReceiveTerritoryDetail(ClanMapTerritoryDetailDto.Read(p)); } catch (Exception ex) { Plugin.Log.LogWarning((object)("[FCW ClanMap] Invalid detail: " + ex.Message)); ClanWorldMapPanel.ReceiveTerritoryDetailDenied("Invalid territory detail."); } } private static void RPC_OnPublicDetailResponse(long sender, ZPackage p) { if (!Trusted(sender)) { return; } try { ClanWorldMapPanel.ReceivePublicTerritoryDetail(ClanMapPublicTerritoryDetailDto.Read(p)); } catch (Exception) { ClanWorldMapPanel.ReceivePublicTerritoryDetailDenied("Invalid public territory detail."); } } private static void RPC_OnAccessDenied(long sender, string reason) { if (Trusted(sender)) { ClanWorldMapPanel.ReceiveAccessDenied(reason); } } private static void RPC_OnDetailDenied(long sender, string reason) { if (Trusted(sender)) { ClanWorldMapPanel.ReceiveTerritoryDetailDenied(reason); } } private static void RPC_OnPublicDetailDenied(long sender, string reason) { if (Trusted(sender)) { ClanWorldMapPanel.ReceivePublicTerritoryDetailDenied(reason); } } private static void DenyPublicDetail(long sender, string reason) { if (IsLocalHostRecipient(sender)) { ClanWorldMapPanel.ReceivePublicTerritoryDetailDenied(reason); return; } ZRoutedRpc instance = ZRoutedRpc.instance; if (instance != null) { instance.InvokeRoutedRPC(sender, "FCW_ClanMapPublicTerritoryDetailDenied", new object[1] { reason ?? "Public territory detail denied." }); } } private static bool Trusted(long sender) { long num = ServerPeer(); if (num != 0L) { return sender == num; } return false; } private static void Deny(long sender, string reason) { if (IsLocalHostRecipient(sender)) { ClanWorldMapPanel.ReceiveAccessDenied(reason); return; } ZRoutedRpc instance = ZRoutedRpc.instance; if (instance != null) { instance.InvokeRoutedRPC(sender, "FCW_ClanMapAccessDenied", new object[1] { reason ?? "Clan Map access denied." }); } } private static void DenyDetail(long sender, string reason) { if (IsLocalHostRecipient(sender)) { ClanWorldMapPanel.ReceiveTerritoryDetailDenied(reason); return; } ZRoutedRpc instance = ZRoutedRpc.instance; if (instance != null) { instance.InvokeRoutedRPC(sender, "FCW_ClanMapTerritoryDetailDenied", new object[1] { reason ?? "Territory detail denied." }); } } } public class WarfareRPC : MonoBehaviour { private class CoinStackInfo { public object Item; public string Prefab; public string Name; public int Stack; public int SlotX = int.MinValue; public int SlotY = int.MinValue; } private class InventoryEntryInfo { public object Item; public string Prefab; public string SharedName; public string DisplayName; public int Stack; public int SlotX = int.MinValue; public int SlotY = int.MinValue; } private class PendingVaultDeposit { public long PlayerId; public int ClanId; public bool IsCoin; public string Prefab; public string DisplayName; public int Amount; public int SlotX; public int SlotY; public long CreatedTimestamp; } private const string RPC_SyncData = "FCW_SyncData"; private const string RPC_RequestData = "FCW_RequestData"; private const string RPC_CreateClan = "FCW_CreateClan"; private const string RPC_JoinClan = "FCW_JoinClan"; private const string RPC_PlayAsFreeborn = "FCW_PlayAsFreeborn"; private const string RPC_LeaveClan = "FCW_LeaveClan"; private const string RPC_DisbandClan = "FCW_DisbandClan"; private const string RPC_KickMember = "FCW_KickMember"; private const string RPC_SetRank = "FCW_SetRank"; private const string RPC_SetClanEmblemUrl = "FCW_SetClanEmblemUrl"; private const string RPC_SetClanEmblemName = "FCW_SetClanEmblemName"; private const string RPC_SetClanLinks = "FCW_SetClanLinks"; private const string RPC_SetRole = "FCW_SetRole"; private const string RPC_SetPrimaryRole = "FCW_SetPrimaryRole"; private const string RPC_SetSecondaryRole = "FCW_SetSecondaryRole"; private const string RPC_SetTertiaryRole = "FCW_SetTertiaryRole"; private const string RPC_CastVote = "FCW_CastVote"; private const string RPC_UpdateProfile = "FCW_UpdateProfile"; private const string RPC_UpdateAboutMe = "FCW_UpdateAboutMe"; private const string RPC_SetActiveTitle = "FCW_SetActiveTitle"; private const string RPC_AchievementCoinDelivery = "FCW_AchievementCoinDelivery"; private const string RPC_ConfirmAchievementCoins = "FCW_ConfirmAchievementCoins"; private const string RPC_ClaimTerritory = "FCW_ClaimTerritory"; private const string RPC_ClaimResult = "FCW_ClaimResult"; private const string RPC_RenameTerritory = "FCW_RenameTerritory"; private const string RPC_UpgradeTerritory = "FCW_UpgradeTerritory"; private const string RPC_TerritoryBuildingAction = "FCW_TerritoryBuildingAction"; private const string RPC_GuardAction = "FCW_GuardAction"; private const string RPC_WorldEventClaim = "FCW_WorldEventClaim"; private const string RPC_WorldEventAdmin = "FCW_WorldEventAdmin"; private const string RPC_KingdomAdminAction = "FCW_KingdomAdminAction"; private const string RPC_SetCapital = "FCW_SetCapital"; private const string RPC_BuyExpansion = "FCW_BuyExpansion"; private const string RPC_UpgradeVault = "FCW_UpgradeVault"; private const string RPC_WithdrawVault = "FCW_WithdrawVault"; private const string RPC_WithdrawVaultItem = "FCW_WithdrawVaultItem"; private const string RPC_AddToVault = "FCW_AddToVault"; private const string RPC_ConfirmVaultWithdraw = "FCW_ConfirmVaultWithdraw"; private const string RPC_SetVaultPermission = "FCW_SetVaultPermission"; private const string RPC_SetClanTax = "FCW_SetClanTax"; private const string RPC_ClanResourceTax = "FCW_ClanResourceTax"; private const string RPC_ClanCoinTax = "FCW_ClanCoinTax"; private const string RPC_DepositVaultCoins = "FCW_DepositVaultCoins"; private const string RPC_WithdrawVaultCoins = "FCW_WithdrawVaultCoins"; private const string RPC_ConfirmVaultCoinsWithdraw = "FCW_ConfirmVaultCoinsWithdraw"; private const string RPC_ApproveVaultDeposit = "FCW_ApproveVaultDeposit"; private const string RPC_ConfirmVaultDeposit = "FCW_ConfirmVaultDeposit"; private const string RPC_GameAction = "FCW_GameAction"; private const string RPC_GameEscrow = "FCW_GameEscrow"; private const string RPC_GameEscrowConfirm = "FCW_GameEscrowConfirm"; private const string RPC_GamePayout = "FCW_GamePayout"; private const string RPC_ChessAction = "FCW_ChessAction"; private const string RPC_PokerAction = "FCW_PokerAction"; private const string RPC_VaultState = "FCW_VaultState"; private const string RPC_StartVaultCrack = "FCW_StartVaultCrack"; private const string RPC_CancelVaultCrack = "FCW_CancelVaultCrack"; private const string RPC_DeclareWar = "FCW_DeclareWar"; private const string RPC_Diplomacy = "FCW_Diplomacy"; private const string RPC_DiplomacyV1 = "FCW_DiplomacyV1"; private const string RPC_FreebornFriend = "FCW_FreebornFriend"; private const string RPC_AdminTextConfig = "FCW_AdminTextConfig"; private const string RPC_ServerSettings = "FCW_ServerSettings"; private const string RPC_AbandonFreebornHomestead = "FCW_AbandonFreebornHomestead"; private const string RPC_LeaveFreebornLife = "FCW_LeaveFreebornLife"; private const string RPC_PlaceRotTotem = "FCW_PlaceRotTotem"; private const string RPC_PlaceBounty = "FCW_PlaceBounty"; private const string RPC_ChallengeFreeborn = "FCW_ChallengeFreeborn"; private const string RPC_Notification = "FCW_Notification"; private const string RPC_GlobalMessage = "FCW_GlobalMessage"; private bool _registered; private int _registeredRpcInstanceId; private readonly HashSet _registeredRpcNames = new HashSet(); private readonly HashSet _duplicateRegisterLogs = new HashSet(); private readonly HashSet _alreadyRegisteredTryLogs = new HashSet(); private readonly HashSet _profileSpoofLogs = new HashSet(); private readonly HashSet _invalidTitleSelectionLogs = new HashSet(); private readonly HashSet _processedAchievementDeliveryTokens = new HashSet(); private readonly Dictionary _profilePeerByPlayerId = new Dictionary(); private static readonly HashSet _profileIdentityConflictLogs = new HashSet(); private readonly Dictionary _pendingVaultDeposits = new Dictionary(); private long _lastLocalPlayerId; private string _lastLocalPlayerName = ""; private long _lastServerPeerId; private bool _pendingFreshSync; private bool _clientDisconnecting; private bool _clientSyncReceived; private float _lastSyncRequestTime; private int _freshSyncAttempts; private int _lastZNetInstanceId; private string _lastServerSessionKey = ""; private float _chessTickTimer; private bool _chessValidationLogged; public static bool SuppressTax; public static WarfareRPC Instance { get; private set; } private static bool IsServer { get { //IL_0005: Unknown result type (might be due to invalid IL or missing references) //IL_0010: Expected O, but got Unknown if ((Object)ZNet.instance != (Object)null) { return ZNet.instance.IsServer(); } return false; } } public bool IsClientDisconnecting { get { if (!IsServer) { return _clientDisconnecting; } return false; } } public bool IsWaitingForServerSync { get { if (!IsServer) { return !_clientSyncReceived; } return false; } } public bool HasReceivedServerSync { get { if (!IsServer) { return _clientSyncReceived; } return true; } } private static long LocalId { get { Player localPlayer = Player.m_localPlayer; if (!((Object)(object)localPlayer != (Object)null)) { return 0L; } return localPlayer.GetPlayerID(); } } private static string LocalName { get { Player localPlayer = Player.m_localPlayer; return (((Object)(object)localPlayer != (Object)null) ? localPlayer.GetPlayerName() : null) ?? ""; } } public event Action OnNotificationReceived; private void Awake() { //IL_0005: Unknown result type (might be due to invalid IL or missing references) //IL_0010: Expected O, but got Unknown //IL_0017: 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_0027: Expected O, but got Unknown //IL_0027: Expected O, but got Unknown //IL_002a: Unknown result type (might be due to invalid IL or missing references) //IL_0034: Expected O, but got Unknown if ((Object)Instance != (Object)null && (Object)Instance != (Object)this) { Object.Destroy((Object)this); } else { Instance = this; } } private void Update() { TryRegister(); ClanMapRPC.TryRegister(); CheckLocalSession(); if (IsServer) { _chessTickTimer += Time.deltaTime; if (_chessTickTimer >= 1f) { _chessTickTimer = 0f; TickChessClocks(); TickPokerTables(); TickGameTableCleanup(); } } } private void CheckLocalSession() { //IL_0005: Unknown result type (might be due to invalid IL or missing references) //IL_0010: Expected O, but got Unknown //IL_0047: Unknown result type (might be due to invalid IL or missing references) //IL_0052: Expected O, but got Unknown int num = (((Object)ZNet.instance != (Object)null) ? ((Object)ZNet.instance).GetInstanceID() : 0); if (!IsServer && num != 0 && num != _lastZNetInstanceId) { _lastZNetInstanceId = num; MarkClientServerSessionChanged(); } Player localPlayer = Player.m_localPlayer; if ((Object)localPlayer == (Object)null) { if (_lastLocalPlayerId != 0L || !string.IsNullOrEmpty(_lastLocalPlayerName)) { Plugin.Log.LogInfo((object)"[FCW Session] Clearing local player cache"); ClearClientClanCacheOnDisconnect(); } return; } long playerID = localPlayer.GetPlayerID(); string text = localPlayer.GetPlayerName() ?? ""; long serverPeerID = GetServerPeerID(); string serverSessionKey = GetServerSessionKey(serverPeerID); if (!IsServer && serverPeerID != 0L && _clientDisconnecting) { _clientDisconnecting = false; Plugin.Log.LogInfo((object)("[FCW Client] Joined server=" + serverSessionKey + ", waiting for fresh clan sync")); } if (!IsServer && serverPeerID != 0L && !string.IsNullOrEmpty(_lastServerSessionKey) && !string.Equals(serverSessionKey, _lastServerSessionKey, StringComparison.Ordinal)) { Plugin.Log.LogInfo((object)("[FCW Session] Server changed session=" + _lastServerSessionKey + " -> " + serverSessionKey + "; clearing local clan cache")); ResetClientClanState(); _clientSyncReceived = false; _pendingFreshSync = true; _freshSyncAttempts = 0; } if (!IsServer && serverPeerID != 0L) { _lastServerSessionKey = serverSessionKey; } if (!IsServer && serverPeerID != 0L && !_pendingFreshSync && !HasLocalPlayerState(playerID)) { _pendingFreshSync = true; _freshSyncAttempts = 0; Plugin.Log.LogInfo((object)("[FCW Client] Local clan state missing after connect; requesting clan sync for player=" + playerID)); } if (!IsServer && _pendingFreshSync && serverPeerID != 0L && Time.time - _lastSyncRequestTime >= 2f) { _lastServerPeerId = serverPeerID; Plugin.Log.LogInfo((object)("[FCW Client] Joined server=peer:" + serverPeerID + ", requesting clan sync")); RequestDataFromServer(); _lastSyncRequestTime = Time.time; _freshSyncAttempts++; if (_freshSyncAttempts >= 30 && _freshSyncAttempts % 30 == 0) { Plugin.Log.LogWarning((object)("[FCW Client] Fresh clan sync still pending after " + _freshSyncAttempts + " attempts; continuing until FCW_SyncData is received.")); } } if (playerID == _lastLocalPlayerId && string.Equals(text, _lastLocalPlayerName, StringComparison.Ordinal)) { if (!IsServer && serverPeerID != 0L && serverPeerID != _lastServerPeerId) { Plugin.Log.LogInfo((object)("[FCW Session] Server changed peer=" + _lastServerPeerId + " -> " + serverPeerID + "; clearing local clan cache")); _lastServerPeerId = serverPeerID; ResetClientClanState(); _clientSyncReceived = false; Plugin.Log.LogInfo((object)("[FCW Client] Joined server=peer:" + serverPeerID + ", requesting clan sync")); RequestDataFromServer(); _lastSyncRequestTime = Time.time; _pendingFreshSync = true; _freshSyncAttempts = 1; } return; } Plugin.Log.LogInfo((object)("[FCW Session] Player loaded steamId=" + playerID + " characterId=" + text)); Plugin.Log.LogInfo((object)"[FCW Session] Clearing local player cache"); _lastLocalPlayerId = playerID; _lastLocalPlayerName = text; _lastServerPeerId = serverPeerID; if (!IsServer) { ResetClientClanState(); _clientSyncReceived = false; } if (!IsServer && serverPeerID == 0L) { _pendingFreshSync = true; _freshSyncAttempts = 0; Plugin.Log.LogInfo((object)"[FCW Session] Waiting for server peer before clan sync request"); } else { Plugin.Log.LogInfo((object)"[FCW Session] Requesting fresh warfare data"); if (!IsServer) { Plugin.Log.LogInfo((object)("[FCW Client] Joined server=peer:" + serverPeerID + ", requesting clan sync")); } RequestDataFromServer(); _lastSyncRequestTime = Time.time; _pendingFreshSync = !IsServer; _freshSyncAttempts = ((!IsServer) ? 1 : 0); } if (IsServer) { LogLocalPlayerState(); } } public void ClearClientClanCacheOnDisconnect() { if (!IsServer) { _clientDisconnecting = true; _clientSyncReceived = false; Plugin.Log.LogInfo((object)"[FCW Client] Clearing clan cache on disconnect/server change"); _lastLocalPlayerId = 0L; _lastLocalPlayerName = ""; _lastServerPeerId = 0L; _lastServerSessionKey = ""; _pendingFreshSync = true; _lastSyncRequestTime = 0f; _freshSyncAttempts = 0; ClanSelectionUI.Instance?.CloseForDisconnect(); ResetClientClanState(); } } public void MarkClientServerSessionChanged() { if (!IsServer) { ClearClientClanCacheOnDisconnect(); } } private void ResetClientClanState() { WarfareManager.Instance?.ClearServerVaultOpenState(); WarfareManager.Instance?.ApplyServerData(new WarfareData()); ClanSelectionUI.Instance?.ResetClientState(); WarfareHUD.Instance?.ResetClientState(); } private static bool HasLocalPlayerState(long playerId) { if (playerId == 0L) { return false; } return WarfareManager.Instance?.Data?.GetPlayer(playerId) != null; } private string GetServerSessionKey(long serverPeerID) { //IL_000b: Unknown result type (might be due to invalid IL or missing references) //IL_0016: Expected O, but got Unknown string text = ""; try { if ((Object)ZNet.instance != (Object)null) { text = ZNet.instance.GetWorldName() ?? ""; } } catch { text = ""; } return serverPeerID + "|" + text; } private void LogLocalPlayerState() { PlayerClanInfo playerClanInfo = WarfareManager.Instance?.Data?.GetPlayer(LocalId); string text = ((playerClanInfo == null) ? "none" : ((playerClanInfo.IsFreeborn && playerClanInfo.ClanId == 0) ? "freeborn" : ((playerClanInfo.ClanId != 0) ? ("clan:" + playerClanInfo.ClanId) : "none"))); Plugin.Log.LogInfo((object)("[FCW Session] Received player state clan/freeborn=" + text)); } private void TryRegister() { ZRoutedRpc instance = ZRoutedRpc.instance; int num = ((instance != null) ? RuntimeHelpers.GetHashCode(instance) : 0); if (instance == null) { return; } bool flag = _registered && _registeredRpcInstanceId == num; if (!flag || _alreadyRegisteredTryLogs.Add(num)) { Plugin.Log.LogInfo((object)("[FCW RPC] TryRegister rpcInstance=" + num + " alreadyRegistered=" + flag)); } if (!flag) { if (_registeredRpcInstanceId != num) { _registeredRpcInstanceId = num; _registeredRpcNames.Clear(); _duplicateRegisterLogs.Clear(); _alreadyRegisteredTryLogs.Clear(); _registered = false; Plugin.Log.LogInfo((object)("[FCW RPC] Registering RPC handlers for ZRoutedRpc instance=" + num)); } SafeRegister(instance, num, "FCW_SyncData", RPC_OnSyncData); SafeRegister(instance, num, "FCW_RequestData", RPC_OnRequestData); SafeRegister(instance, num, "FCW_CreateClan", RPC_OnCreateClan); SafeRegister(instance, num, "FCW_JoinClan", RPC_OnJoinClan); SafeRegister(instance, num, "FCW_PlayAsFreeborn", RPC_OnPlayAsFreeborn); SafeRegister(instance, num, "FCW_LeaveClan", RPC_OnLeaveClan); SafeRegister(instance, num, "FCW_DisbandClan", RPC_OnDisbandClan); SafeRegister(instance, num, "FCW_KickMember", RPC_OnKickMember); SafeRegister(instance, num, "FCW_SetRank", RPC_OnSetRank); SafeRegister(instance, num, "FCW_SetClanEmblemUrl", RPC_OnSetClanEmblemUrl); SafeRegister(instance, num, "FCW_SetClanEmblemName", RPC_OnSetClanEmblemName); SafeRegister(instance, num, "FCW_SetClanLinks", RPC_OnSetClanLinks); SafeRegister(instance, num, "FCW_SetRole", RPC_OnSetRole); SafeRegister(instance, num, "FCW_SetPrimaryRole", RPC_OnSetPrimaryRole); SafeRegister(instance, num, "FCW_SetSecondaryRole", RPC_OnSetSecondaryRole); SafeRegister(instance, num, "FCW_SetTertiaryRole", RPC_OnSetTertiaryRole); SafeRegister(instance, num, "FCW_CastVote", RPC_OnCastVote); SafeRegister(instance, num, "FCW_UpdateProfile", RPC_OnUpdateProfile); SafeRegister(instance, num, "FCW_UpdateAboutMe", RPC_OnUpdateAboutMe); SafeRegister(instance, num, "FCW_SetActiveTitle", RPC_OnSetActiveTitle); SafeRegister(instance, num, "FCW_AchievementCoinDelivery", RPC_OnAchievementCoinDelivery); SafeRegister(instance, num, "FCW_ConfirmAchievementCoins", RPC_OnConfirmAchievementCoins); SafeRegister(instance, num, "FCW_ClaimTerritory", RPC_OnClaimTerritory); SafeRegister(instance, num, "FCW_ClaimResult", RPC_OnClaimResult); SafeRegister(instance, num, "FCW_DiscoverTerritory", RPC_OnDiscoverTerritory); SafeRegister(instance, num, "FCW_DiscoverTerritoryClient", RPC_OnDiscoverTerritoryClient); SafeRegister(instance, num, "FCW_RenameTerritory", RPC_OnRenameTerritory); SafeRegister(instance, num, "FCW_UpgradeTerritory", RPC_OnUpgradeTerritory); SafeRegister(instance, num, "FCW_TerritoryBuildingAction", RPC_OnTerritoryBuildingAction); SafeRegister(instance, num, "FCW_GuardAction", RPC_OnGuardAction); SafeRegister(instance, num, "FCW_WorldEventClaim", RPC_OnWorldEventClaim); SafeRegister(instance, num, "FCW_WorldEventAdmin", RPC_OnWorldEventAdmin); SafeRegister(instance, num, "FCW_KingdomAdminAction", RPC_OnKingdomAdminAction); SafeRegister(instance, num, "FCW_AdminTextConfig", RPC_OnAdminTextConfig); SafeRegister(instance, num, "FCW_ServerSettings", RPC_OnServerSettings); SafeRegister(instance, num, "FCW_SetCapital", RPC_OnSetCapital); SafeRegister(instance, num, "FCW_BuyExpansion", RPC_OnBuyExpansion); SafeRegister(instance, num, "FCW_UpgradeVault", RPC_OnUpgradeVault); SafeRegister(instance, num, "FCW_WithdrawVault", RPC_OnWithdrawVault); SafeRegister(instance, num, "FCW_WithdrawVaultItem", RPC_OnWithdrawVaultItem); SafeRegister(instance, num, "FCW_AddToVault", RPC_OnAddToVault); SafeRegister(instance, num, "FCW_ConfirmVaultWithdraw", RPC_OnConfirmVaultWithdraw); SafeRegister(instance, num, "FCW_SetVaultPermission", RPC_OnSetVaultPermission); SafeRegister(instance, num, "FCW_SetClanTax", RPC_OnSetClanTax); SafeRegister(instance, num, "FCW_ClanResourceTax", RPC_OnClanResourceTax); SafeRegister(instance, num, "FCW_ClanCoinTax", RPC_OnClanCoinTax); SafeRegister(instance, num, "FCW_DepositVaultCoins", RPC_OnDepositVaultCoins); SafeRegister(instance, num, "FCW_WithdrawVaultCoins", RPC_OnWithdrawVaultCoins); SafeRegister(instance, num, "FCW_ConfirmVaultCoinsWithdraw", RPC_OnConfirmVaultCoinsWithdraw); SafeRegister(instance, num, "FCW_ApproveVaultDeposit", RPC_OnApproveVaultDeposit); SafeRegister(instance, num, "FCW_ConfirmVaultDeposit", RPC_OnConfirmVaultDeposit); SafeRegister(instance, num, "FCW_VaultState", RPC_OnVaultState); SafeRegister(instance, num, "FCW_StartVaultCrack", RPC_OnStartVaultCrack); SafeRegister(instance, num, "FCW_CancelVaultCrack", RPC_OnCancelVaultCrack); SafeRegister(instance, num, "FCW_DeclareWar", RPC_OnDeclareWar); SafeRegister(instance, num, "FCW_Diplomacy", RPC_OnDiplomacy); SafeRegister(instance, num, "FCW_DiplomacyV1", RPC_OnDiplomacyV1); SafeRegister(instance, num, "FCW_TerritoryExchange", RPC_OnTerritoryExchange); SafeRegister(instance, num, "FCW_ContractAction", RPC_OnContractAction); SafeRegister(instance, num, "FCW_GameAction", RPC_OnGameAction); SafeRegister(instance, num, "FCW_GameEscrow", RPC_OnGameEscrow); SafeRegister(instance, num, "FCW_GameEscrowConfirm", RPC_OnGameEscrowConfirm); SafeRegister(instance, num, "FCW_GamePayout", RPC_OnGamePayout); SafeRegister(instance, num, "FCW_ChessAction", RPC_OnChessAction); SafeRegister(instance, num, "FCW_PokerAction", RPC_OnPokerAction); SafeRegister(instance, num, "FCW_FreebornFriend", RPC_OnFreebornFriend); SafeRegister(instance, num, "FCW_AbandonFreebornHomestead", RPC_OnAbandonFreebornHomestead); SafeRegister(instance, num, "FCW_LeaveFreebornLife", RPC_OnLeaveFreebornLife); SafeRegister(instance, num, "FCW_PlaceRotTotem", RPC_OnPlaceRotTotem); SafeRegister(instance, num, "FCW_PlaceBounty", RPC_OnPlaceBounty); SafeRegister(instance, num, "FCW_ChallengeFreeborn", RPC_OnChallengeFreeborn); SafeRegister(instance, num, "FCW_Notification", RPC_OnNotification); SafeRegister(instance, num, "FCW_GlobalMessage", RPC_OnGlobalMessage); _registered = true; Plugin.Log.LogInfo((object)"[FCW RPC] Warfare RPCs registered, including FCW_CreateClan and FCW_PlayAsFreeborn."); } } private void SafeRegister(ZRoutedRpc rpc, int rpcInstance, string name, Action handler) { if (_registeredRpcNames.Contains(name)) { Plugin.Log.LogInfo((object)("[FCW RPC] Skipping already registered RPC=" + name)); LogDuplicateRegistration(name); return; } try { Plugin.Log.LogInfo((object)("[FCW RPC] Registering RPC=" + name)); rpc.Register(name, handler); _registeredRpcNames.Add(name); if (name == "FCW_SyncData") { Plugin.Log.LogInfo((object)("[FCW RPC] Registered FCW_SyncData on rpcInstance=" + rpcInstance)); } } catch (ArgumentException) { _registeredRpcNames.Add(name); Plugin.Log.LogWarning((object)("[FCW RPC] Duplicate registration blocked RPC=" + name)); LogDuplicateRegistration(name); } } private void LogDuplicateRegistration(string name) { if (_duplicateRegisterLogs.Add(name)) { Plugin.Log.LogInfo((object)("[FCW RPC] Skipped duplicate registration " + name)); } } private long GetServerPeerID() { //IL_0005: Unknown result type (might be due to invalid IL or missing references) //IL_0010: Expected O, but got Unknown if ((Object)ZNet.instance == (Object)null) { return 0L; } if (ZNet.instance.IsServer()) { return ZNet.GetUID(); } return ZNet.instance.GetServerPeer()?.m_uid ?? 0; } private void ToServer(string rpc, ZPackage pkg) { if (!_registered) { TryRegister(); } ZRoutedRpc instance = ZRoutedRpc.instance; if (instance != null) { long serverPeerID = GetServerPeerID(); Plugin.Log.LogInfo((object)("[FCW RPC] Sending " + rpc + " to serverPeer=" + serverPeerID + " localPlayer=" + LocalId + ".")); instance.InvokeRoutedRPC(serverPeerID, rpc, new object[1] { pkg }); } else { Plugin.Log.LogWarning((object)("[FCW RPC] Cannot send " + rpc + ": ZRoutedRpc.instance is null.")); } } public void RequestDataFromServer() { //IL_0008: Unknown result type (might be due to invalid IL or missing references) //IL_000e: Expected O, but got Unknown if (!IsServer) { ZPackage val = new ZPackage(); val.Write(LocalId); ToServer("FCW_RequestData", val); } } private void RPC_OnRequestData(long sender, ZPackage pkg) { if (!IsServer) { return; } long num = pkg.ReadLong(); long num2 = ResolveSenderPlayerId(sender); string text = ResolveSenderSteamId64(sender); if (num2 == 0L) { Plugin.Log.LogWarning((object)("[FCW Profile] Data request identity unresolved sender=" + sender + " requestedPlayerId=" + num + " steamId64=" + text)); return; } WarfareManager.Instance?.EnsureSharedDataForPlayerSync(num2); PlayerClanInfo playerClanInfo = WarfareManager.Instance?.AssociatePlayerIdentity(num2, ResolveSenderPlayerName(sender), text); AchievementEvaluationResult achievementEvaluationResult = WarfareManager.Instance?.MarkPlayerActive(num2, playerClanInfo, text) ?? new AchievementEvaluationResult(); if (achievementEvaluationResult.Changed) { WarfareManager.Instance?.SaveData(); } _profilePeerByPlayerId[playerClanInfo?.PlayerId ?? num2] = sender; int valueOrDefault = (WarfareManager.Instance?.Data?.GetPlayer(num2)?.ClanId).GetValueOrDefault(); int valueOrDefault2 = (WarfareManager.Instance?.Data?.Clans?.Count).GetValueOrDefault(); int num3 = CountClanMembers(WarfareManager.Instance?.Data); Plugin.Log.LogInfo((object)("[FCW Server] Sending clan sync playerId=" + num2 + " clanId=" + valueOrDefault + " clans=" + valueOrDefault2 + " members=" + num3 + " peer=" + sender)); SyncDataToClient(sender); SendAchievementUnlockNotification(sender, achievementEvaluationResult); TryDeliverPendingAchievementCoins(sender, playerClanInfo); } private static int CountClanMembers(WarfareData data) { int num = 0; if (data?.Players == null) { return 0; } foreach (PlayerClanInfo value in data.Players.Values) { if (value.ClanId != 0) { num++; } } return num; } public void SyncAllDataToClients() { //IL_005c: Unknown result type (might be due to invalid IL or missing references) //IL_0062: Expected O, but got Unknown if (!IsServer) { return; } WarfareManager.Instance?.RefreshServerBadgesFromConfig(); byte[] array = WarfareManager.Instance?.Data?.Serialize(); if (array == null) { return; } ZRoutedRpc instance = ZRoutedRpc.instance; if (instance == null) { return; } instance.InvokeRoutedRPC(ZRoutedRpc.Everybody, "FCW_SyncData", new object[1] { (object)new ZPackage(array) }); SyncVaultStateToClient(ZRoutedRpc.Everybody); SyncServerSettingsToClient(ZRoutedRpc.Everybody); foreach (KeyValuePair item in _profilePeerByPlayerId.ToList()) { SendAchievementUnlockNotification(item.Value, WarfareManager.Instance?.ConsumeAchievementNotification(item.Key)); TryDeliverPendingAchievementCoins(item.Value, WarfareManager.Instance?.Data?.GetPlayer(item.Key)); } } private void SyncDataToClient(long peerUID) { //IL_0075: Unknown result type (might be due to invalid IL or missing references) //IL_007b: Expected O, but got Unknown WarfareManager.Instance?.RefreshServerBadgesFromConfig(); byte[] array = WarfareManager.Instance?.Data?.Serialize(); if (array != null) { ZRoutedRpc instance = ZRoutedRpc.instance; if (instance != null) { Plugin.Log.LogInfo((object)("[FCW Server] Invoking FCW_SyncData peer=" + peerUID + " bytes=" + array.Length)); instance.InvokeRoutedRPC(peerUID, "FCW_SyncData", new object[1] { (object)new ZPackage(array) }); SyncVaultStateToClient(peerUID); SyncServerSettingsToClient(peerUID); } } } private void SyncVaultStateToClient(long peerUID) { //IL_000f: Unknown result type (might be due to invalid IL or missing references) //IL_0015: Expected O, but got Unknown if (IsServer && ZRoutedRpc.instance != null) { ZPackage val = new ZPackage(); val.Write(WarfareManager.Instance?.IsVaultOpen() ?? false); ZRoutedRpc.instance.InvokeRoutedRPC(peerUID, "FCW_VaultState", new object[1] { val }); } } private void RPC_OnVaultState(long sender, ZPackage pkg) { if (!IsServer) { WarfareManager.Instance?.SetServerVaultOpenState(pkg.ReadBool()); } } private void SyncServerSettingsToClient(long peerUID) { //IL_000f: Unknown result type (might be due to invalid IL or missing references) //IL_0015: Expected O, but got Unknown if (IsServer && ZRoutedRpc.instance != null) { ZPackage val = new ZPackage(); val.Write(RealmRules.GetBool("force_pvp")); val.Write(RealmRules.GetBool("friendly_fire")); val.Write(RealmRules.GetBool("resource_gathering_restriction")); val.Write(ClanConfig.MiningRestrictionEnabled?.Value ?? false); val.Write(ClanConfig.WoodcuttingRestrictionEnabled?.Value ?? false); val.Write(ClanConfig.BuildingRestrictionEnabled?.Value ?? false); val.Write(RealmRules.GetBool("territory_capture")); val.Write(RealmRules.GetBool("clan_wars")); val.Write(ClanConfig.FreebornProtectionEnabled?.Value ?? false); val.Write(ClanConfig.OdinsPeaceEnabled?.Value ?? false); val.Write(ClanConfig.EnableClanMemberTax?.Value ?? false); val.Write(ClanConfig.EnableProgressiveTerritoryUpkeep?.Value ?? false); val.Write(ClanConfig.WorldEventsEnabled?.Value ?? false); val.Write(ClanConfig.GamesEnabled?.Value ?? false); ConfigEntry enableTaxDebugLogs = ClanConfig.EnableTaxDebugLogs; val.Write((enableTaxDebugLogs != null && enableTaxDebugLogs.Value) || (ClanConfig.EnableVaultDebugLogs?.Value ?? false)); val.Write(ClanConfig.AllowPlayerPvpToggle?.Value ?? false); val.Write(ClanConfig.BattleStatusEnabled?.Value ?? false); val.Write(ClanConfig.CombatDurationSeconds?.Value ?? 30); val.Write(!RealmRules.GetBool("teleport_in_combat")); val.Write(ClanConfig.KillFeedEnabled?.Value ?? false); val.Write(RealmRules.GetBool("combat_logout")); val.Write(ClanConfig.DiscordWebhookUrl?.Value ?? ""); val.Write(ClanConfig.DiscordWebhookEnabled?.Value ?? false); val.Write(ClanConfig.NotifyKillFeed?.Value ?? false); val.Write(ClanConfig.NotifyCombatLogout?.Value ?? false); val.Write(ClanConfig.NotifyWarStarted?.Value ?? false); val.Write(ClanConfig.NotifyWarEnded?.Value ?? false); val.Write(ClanConfig.NotifyTerritoryCaptured?.Value ?? false); val.Write(ClanConfig.NotifyTerritoryLost?.Value ?? false); val.Write(ClanConfig.NotifyClanCreated?.Value ?? false); val.Write(ClanConfig.NotifyClanDisbanded?.Value ?? false); val.Write(ClanConfig.NotifyElections?.Value ?? false); val.Write(ClanConfig.NotifyServerAnnouncements?.Value ?? false); val.Write(ClanConfig.DiscordEnabled?.Value ?? false); val.Write(ClanConfig.DebugNotifications?.Value ?? false); val.Write(ClanConfig.NotifyDiscordPvP?.Value ?? false); val.Write(ClanConfig.NotifyDiscordWar?.Value ?? false); val.Write(ClanConfig.NotifyDiscordTerritory?.Value ?? false); val.Write(ClanConfig.NotifyDiscordEconomy?.Value ?? false); val.Write(ClanConfig.NotifyDiscordBuilding?.Value ?? false); val.Write(ClanConfig.NotifyDiscordClan?.Value ?? false); val.Write(ClanConfig.NotifyDiscordElection?.Value ?? false); val.Write(ClanConfig.NotifyDiscordEvent?.Value ?? false); val.Write(ClanConfig.NotifyDiscordServer?.Value ?? false); val.Write(ClanConfig.NotifyDiscordSupporter?.Value ?? false); val.Write(ClanConfig.NotifyDiscordAdmin?.Value ?? false); val.Write(ClanConfig.NotifyDiscordSystem?.Value ?? false); val.Write(ClanConfig.RealmLawOverrides?.Value ?? ""); val.Write(RealmRules.GetBool("raid_protection")); val.Write(Math.Max(0f, ClanConfig.CoinsWeight?.Value ?? 0f)); val.Write(Math.Max(1, ClanConfig.CoinsMaxStack?.Value ?? 100000)); ZRoutedRpc.instance.InvokeRoutedRPC(peerUID, "FCW_ServerSettings", new object[1] { val }); } } private void RPC_OnServerSettings(long sender, ZPackage pkg) { if (!IsServer) { bool flag = pkg.ReadBool(); bool flag2 = pkg.ReadBool(); bool value = pkg.ReadBool(); bool value2 = pkg.ReadBool(); bool value3 = pkg.ReadBool(); bool value4 = pkg.ReadBool(); bool value5 = pkg.ReadBool(); bool value6 = pkg.ReadBool(); bool value7 = pkg.ReadBool(); bool value8 = pkg.ReadBool(); bool value9 = pkg.ReadBool(); bool value10 = pkg.ReadBool(); bool value11 = pkg.ReadBool(); bool value12 = pkg.ReadBool(); bool value13 = pkg.ReadBool(); bool value14 = pkg.ReadBool(); bool value15 = pkg.ReadBool(); int value16 = pkg.ReadInt(); bool value17 = pkg.ReadBool(); bool value18 = pkg.ReadBool(); bool value19 = pkg.ReadBool(); string text = pkg.ReadString(); bool value20 = pkg.ReadBool(); bool value21 = pkg.ReadBool(); bool value22 = pkg.ReadBool(); bool value23 = pkg.ReadBool(); bool value24 = pkg.ReadBool(); bool value25 = pkg.ReadBool(); bool value26 = pkg.ReadBool(); bool value27 = pkg.ReadBool(); bool value28 = pkg.ReadBool(); bool value29 = pkg.ReadBool(); bool value30 = pkg.ReadBool(); bool value31 = pkg.ReadBool(); bool value32 = pkg.ReadBool(); bool value33 = pkg.ReadBool(); bool value34 = pkg.ReadBool(); bool value35 = pkg.ReadBool(); bool value36 = pkg.ReadBool(); bool value37 = pkg.ReadBool(); bool value38 = pkg.ReadBool(); bool value39 = pkg.ReadBool(); bool value40 = pkg.ReadBool(); bool value41 = pkg.ReadBool(); bool value42 = pkg.ReadBool(); bool value43 = pkg.ReadBool(); bool value44 = pkg.ReadBool(); string text2 = pkg.ReadString(); bool value45 = pkg.ReadBool(); float val = pkg.ReadSingle(); int val2 = pkg.ReadInt(); if (ClanConfig.ForcePvpOn != null) { ClanConfig.ForcePvpOn.Value = (flag ? "true" : "false"); } if (ClanConfig.ForceFriendlyFireOn != null) { ClanConfig.ForceFriendlyFireOn.Value = (flag2 ? "true" : "false"); } if (ClanConfig.EnableTerritoryResourceOwnership != null) { ClanConfig.EnableTerritoryResourceOwnership.Value = value; } if (ClanConfig.MiningRestrictionEnabled != null) { ClanConfig.MiningRestrictionEnabled.Value = value2; } if (ClanConfig.WoodcuttingRestrictionEnabled != null) { ClanConfig.WoodcuttingRestrictionEnabled.Value = value3; } if (ClanConfig.BuildingRestrictionEnabled != null) { ClanConfig.BuildingRestrictionEnabled.Value = value4; } if (ClanConfig.TerritoryCaptureEnabled != null) { ClanConfig.TerritoryCaptureEnabled.Value = value5; } if (ClanConfig.ClanWarsEnabled != null) { ClanConfig.ClanWarsEnabled.Value = value6; } if (ClanConfig.FreebornProtectionEnabled != null) { ClanConfig.FreebornProtectionEnabled.Value = value7; } if (ClanConfig.OdinsPeaceEnabled != null) { ClanConfig.OdinsPeaceEnabled.Value = value8; } if (ClanConfig.EnableClanMemberTax != null) { ClanConfig.EnableClanMemberTax.Value = value9; } if (ClanConfig.EnableProgressiveTerritoryUpkeep != null) { ClanConfig.EnableProgressiveTerritoryUpkeep.Value = value10; } if (ClanConfig.WorldEventsEnabled != null) { ClanConfig.WorldEventsEnabled.Value = value11; } if (ClanConfig.GamesEnabled != null) { ClanConfig.GamesEnabled.Value = value12; } if (ClanConfig.EnableTaxDebugLogs != null) { ClanConfig.EnableTaxDebugLogs.Value = value13; } if (ClanConfig.EnableVaultDebugLogs != null) { ClanConfig.EnableVaultDebugLogs.Value = value13; } if (ClanConfig.AllowPlayerPvpToggle != null) { ClanConfig.AllowPlayerPvpToggle.Value = value14; } if (ClanConfig.BattleStatusEnabled != null) { ClanConfig.BattleStatusEnabled.Value = value15; } if (ClanConfig.CombatDurationSeconds != null) { ClanConfig.CombatDurationSeconds.Value = value16; } if (ClanConfig.BlockTeleportWhileInCombat != null) { ClanConfig.BlockTeleportWhileInCombat.Value = value17; } if (ClanConfig.KillFeedEnabled != null) { ClanConfig.KillFeedEnabled.Value = value18; } if (ClanConfig.CombatLogoutHandling != null) { ClanConfig.CombatLogoutHandling.Value = value19; } if (ClanConfig.DiscordWebhookUrl != null) { ClanConfig.DiscordWebhookUrl.Value = text ?? ""; } if (ClanConfig.DiscordWebhookEnabled != null) { ClanConfig.DiscordWebhookEnabled.Value = value20; } if (ClanConfig.NotifyKillFeed != null) { ClanConfig.NotifyKillFeed.Value = value21; } if (ClanConfig.NotifyCombatLogout != null) { ClanConfig.NotifyCombatLogout.Value = value22; } if (ClanConfig.NotifyWarStarted != null) { ClanConfig.NotifyWarStarted.Value = value23; } if (ClanConfig.NotifyWarEnded != null) { ClanConfig.NotifyWarEnded.Value = value24; } if (ClanConfig.NotifyTerritoryCaptured != null) { ClanConfig.NotifyTerritoryCaptured.Value = value25; } if (ClanConfig.NotifyTerritoryLost != null) { ClanConfig.NotifyTerritoryLost.Value = value26; } if (ClanConfig.NotifyClanCreated != null) { ClanConfig.NotifyClanCreated.Value = value27; } if (ClanConfig.NotifyClanDisbanded != null) { ClanConfig.NotifyClanDisbanded.Value = value28; } if (ClanConfig.NotifyElections != null) { ClanConfig.NotifyElections.Value = value29; } if (ClanConfig.NotifyServerAnnouncements != null) { ClanConfig.NotifyServerAnnouncements.Value = value30; } if (ClanConfig.DiscordEnabled != null) { ClanConfig.DiscordEnabled.Value = value31; } if (ClanConfig.DebugNotifications != null) { ClanConfig.DebugNotifications.Value = value32; } if (ClanConfig.NotifyDiscordPvP != null) { ClanConfig.NotifyDiscordPvP.Value = value33; } if (ClanConfig.NotifyDiscordWar != null) { ClanConfig.NotifyDiscordWar.Value = value34; } if (ClanConfig.NotifyDiscordTerritory != null) { ClanConfig.NotifyDiscordTerritory.Value = value35; } if (ClanConfig.NotifyDiscordEconomy != null) { ClanConfig.NotifyDiscordEconomy.Value = value36; } if (ClanConfig.NotifyDiscordBuilding != null) { ClanConfig.NotifyDiscordBuilding.Value = value37; } if (ClanConfig.NotifyDiscordClan != null) { ClanConfig.NotifyDiscordClan.Value = value38; } if (ClanConfig.NotifyDiscordElection != null) { ClanConfig.NotifyDiscordElection.Value = value39; } if (ClanConfig.NotifyDiscordEvent != null) { ClanConfig.NotifyDiscordEvent.Value = value40; } if (ClanConfig.NotifyDiscordServer != null) { ClanConfig.NotifyDiscordServer.Value = value41; } if (ClanConfig.NotifyDiscordSupporter != null) { ClanConfig.NotifyDiscordSupporter.Value = value42; } if (ClanConfig.NotifyDiscordAdmin != null) { ClanConfig.NotifyDiscordAdmin.Value = value43; } if (ClanConfig.NotifyDiscordSystem != null) { ClanConfig.NotifyDiscordSystem.Value = value44; } if (ClanConfig.RealmLawOverrides != null) { ClanConfig.RealmLawOverrides.Value = text2 ?? ""; } if (ClanConfig.OfflineRaidProtection != null) { ClanConfig.OfflineRaidProtection.Value = value45; } if (ClanConfig.CoinsWeight != null) { ClanConfig.CoinsWeight.Value = Math.Max(0f, val); } if (ClanConfig.CoinsMaxStack != null) { ClanConfig.CoinsMaxStack.Value = Math.Max(1, val2); } VanillaCoinsPatches.ApplyConfiguredCoins(ObjectDB.instance); } } private void RPC_OnSyncData(long sender, ZPackage pkg) { if (!IsServer) { Plugin.Log.LogInfo((object)("[FCW Client] FCW_SyncData RPC received sender=" + sender + " localPlayer=" + LocalId)); WarfareData warfareData = WarfareData.Deserialize(pkg.GetArray()); string localSteamId64ForDiagnostics = WarfareManager.GetLocalSteamId64ForDiagnostics(); EnsureLocalPlayerAlias(warfareData, LocalId, localSteamId64ForDiagnostics); int valueOrDefault = (warfareData?.Clans?.Count).GetValueOrDefault(); int num = CountClanMembers(warfareData); int num2 = ResolvePlayerClanId(warfareData, LocalId, localSteamId64ForDiagnostics); Plugin.Log.LogInfo((object)("[FCW Client] Payload clans=" + valueOrDefault + " members=" + num)); Plugin.Log.LogInfo((object)("[FCW Client] Player lookup playerId=" + LocalId + " clanId=" + num2)); Plugin.Log.LogInfo((object)("[FCW Client] Received FCW_SyncData clans=" + valueOrDefault + " members=" + num + " playerClanId=" + num2)); Plugin.Log.LogInfo((object)"[FCW Client] Rebuilding local clan state"); WarfareManager.Instance?.ApplyServerData(warfareData); _clientSyncReceived = true; _clientDisconnecting = false; num2 = WarfareManager.Instance?.Data?.GetPlayer(LocalId)?.ClanId ?? num2; Plugin.Log.LogInfo((object)("[FCW Client] PlayerClan assigned clanId=" + num2)); Plugin.Log.LogInfo((object)("[FCW Client] Received clan sync clanId=" + num2 + " clans=" + valueOrDefault + " members=" + num)); if (num2 != 0 || WarfareManager.Instance?.Data?.GetPlayer(LocalId) != null) { _pendingFreshSync = false; _freshSyncAttempts = 0; } else { _pendingFreshSync = true; Plugin.Log.LogWarning((object)("[FCW Client] FCW_SyncData applied but player clan is unresolved; retrying clan sync for player=" + LocalId)); } ClanSelectionUI.Instance?.HandleClanSync(num2); WarfareHUD.Instance?.HandleClanSync(num2); Plugin.Log.LogInfo((object)"[FCW Client] UI refresh requested"); Plugin.Log.LogInfo((object)"[FCW UI] Refreshing clan UI after sync"); LogLocalPlayerState(); } } private static int ResolvePlayerClanId(WarfareData data, long playerId, string steamId64) { if (data?.Players != null && data.Players.TryGetValue(playerId, out var value)) { return value.ClanId; } if (data?.Players == null || string.IsNullOrEmpty(steamId64)) { return 0; } PlayerClanInfo playerClanInfo = null; foreach (PlayerClanInfo value2 in data.Players.Values) { if (string.Equals(value2.SteamId64, steamId64, StringComparison.Ordinal)) { if (playerClanInfo != null && playerClanInfo != value2) { return 0; } playerClanInfo = value2; } } return playerClanInfo?.ClanId ?? 0; } private static void EnsureLocalPlayerAlias(WarfareData data, long playerId, string steamId64) { if (data?.Players == null || playerId == 0L || data.Players.ContainsKey(playerId) || string.IsNullOrEmpty(steamId64)) { return; } PlayerClanInfo playerClanInfo = null; foreach (PlayerClanInfo value in data.Players.Values) { if (!string.Equals(value.SteamId64, steamId64, StringComparison.Ordinal)) { continue; } if (playerClanInfo != null && playerClanInfo != value) { string item = playerId + "|" + steamId64; if (_profileIdentityConflictLogs.Add(item)) { Plugin.Log.LogWarning((object)("[FCW Profile] Ambiguous SteamID64 identity preserved without alias: playerId=" + playerId + " steamId64=" + steamId64)); } return; } playerClanInfo = value; } if (playerClanInfo != null) { PlayerClanInfo playerClanInfo2 = playerClanInfo; if (playerClanInfo2.LinkedPlayerIds == null) { playerClanInfo2.LinkedPlayerIds = new List(); } if (!playerClanInfo.LinkedPlayerIds.Contains(playerId)) { playerClanInfo.LinkedPlayerIds.Add(playerId); } } } public void RequestCreateClan(string name, string tag, int colorIndex, int iconIndex) { RequestCreateClan(name, tag, colorIndex, iconIndex, ""); } public void RequestCreateClan(string name, string tag, int colorIndex, int iconIndex, string emblemUrl) { RequestCreateClan(name, tag, colorIndex, iconIndex, emblemUrl, ""); } public void RequestCreateClan(string name, string tag, int colorIndex, int iconIndex, string emblemUrl, string emblemName) { //IL_0005: Unknown result type (might be due to invalid IL or missing references) //IL_0010: Expected O, but got Unknown //IL_0089: Unknown result type (might be due to invalid IL or missing references) //IL_008f: Expected O, but got Unknown if (!((Object)Player.m_localPlayer == (Object)null)) { Plugin.Log.LogInfo((object)("[FCW RPC] RequestCreateClan localId=" + LocalId + " name='" + name + "' tag='" + tag + "' color=" + colorIndex + " icon=" + iconIndex + ".")); ZPackage val = new ZPackage(); val.Write(LocalId); val.Write(LocalName); val.Write(name ?? ""); val.Write(tag ?? ""); val.Write(colorIndex); val.Write(iconIndex); val.Write(emblemUrl ?? ""); val.Write(emblemName ?? ""); ToServer("FCW_CreateClan", val); } else { Plugin.Log.LogWarning((object)"[FCW RPC] RequestCreateClan ignored: Player.m_localPlayer is null."); } } private void RPC_OnCreateClan(long sender, ZPackage pkg) { if (IsServer) { try { long playerId = pkg.ReadLong(); string playerName = pkg.ReadString(); string text = pkg.ReadString(); string text2 = pkg.ReadString(); int colorIndex = pkg.ReadInt(); int iconIndex = pkg.ReadInt(); string emblemUrl = pkg.ReadString(); string emblemName = pkg.ReadString(); Plugin.Log.LogInfo((object)("[FCW RPC] Server received FCW_CreateClan sender=" + sender + " playerId=" + playerId + " name='" + text + "' tag='" + text2 + "'.")); string text3 = WarfareManager.Instance?.CreateClan(playerId, playerName, text, text2, colorIndex, iconIndex, emblemUrl, emblemName) ?? "Failed"; Plugin.Log.LogInfo((object)("[FCW RPC] FCW_CreateClan response to sender=" + sender + ": " + text3)); SendNotification(sender, text3); SyncAllDataToClients(); } catch (Exception ex) { Plugin.Log.LogError((object)("[FCW RPC] FCW_CreateClan failed: " + ex)); SendNotification(sender, "Create clan failed. See server log."); } } } public void RequestJoinClan(int clanId) { //IL_0005: Unknown result type (might be due to invalid IL or missing references) //IL_0010: Expected O, but got Unknown //IL_0012: Unknown result type (might be due to invalid IL or missing references) //IL_0018: Expected O, but got Unknown if (!((Object)Player.m_localPlayer == (Object)null)) { ZPackage val = new ZPackage(); val.Write(LocalId); val.Write(LocalName); val.Write(clanId); ToServer("FCW_JoinClan", val); } } private void RPC_OnJoinClan(long sender, ZPackage pkg) { if (IsServer) { long playerId = pkg.ReadLong(); string playerName = pkg.ReadString(); int clanId = pkg.ReadInt(); string message = WarfareManager.Instance?.JoinClan(playerId, playerName, clanId) ?? "Failed"; SendNotification(sender, message); SyncAllDataToClients(); } } public void RequestPlayAsFreeborn() { //IL_0005: Unknown result type (might be due to invalid IL or missing references) //IL_0010: Expected O, but got Unknown //IL_0057: Unknown result type (might be due to invalid IL or missing references) //IL_005d: Expected O, but got Unknown if (!((Object)Player.m_localPlayer == (Object)null)) { Plugin.Log.LogInfo((object)("[FCW RPC] RequestPlayAsFreeborn localId=" + LocalId + " name='" + LocalName + "'.")); ZPackage val = new ZPackage(); val.Write(LocalId); val.Write(LocalName); ToServer("FCW_PlayAsFreeborn", val); } else { Plugin.Log.LogWarning((object)"[FCW RPC] RequestPlayAsFreeborn ignored: Player.m_localPlayer is null."); } } private void RPC_OnPlayAsFreeborn(long sender, ZPackage pkg) { if (IsServer) { try { long playerId = pkg.ReadLong(); string text = pkg.ReadString(); Plugin.Log.LogInfo((object)("[FCW RPC] Server received FCW_PlayAsFreeborn sender=" + sender + " playerId=" + playerId + " name='" + text + "'.")); string text2 = WarfareManager.Instance?.PlayAsFreeborn(playerId, text) ?? "Failed"; Plugin.Log.LogInfo((object)("[FCW RPC] FCW_PlayAsFreeborn response to sender=" + sender + ": " + text2)); SendNotification(sender, text2); SyncAllDataToClients(); } catch (Exception ex) { Plugin.Log.LogError((object)("[FCW RPC] FCW_PlayAsFreeborn failed: " + ex)); SendNotification(sender, "Freeborn selection failed. See server log."); } } } public void RequestLeaveClan() { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_0006: Expected O, but got Unknown ZPackage val = new ZPackage(); val.Write(LocalId); ToServer("FCW_LeaveClan", val); } private void RPC_OnLeaveClan(long sender, ZPackage pkg) { if (IsServer) { string message = WarfareManager.Instance?.LeaveClan(pkg.ReadLong()) ?? "Failed"; SendNotification(sender, message); SyncAllDataToClients(); } } public void RequestDisbandClan() { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_0006: Expected O, but got Unknown ZPackage val = new ZPackage(); val.Write(LocalId); ToServer("FCW_DisbandClan", val); } private void RPC_OnDisbandClan(long sender, ZPackage pkg) { if (IsServer) { string message = WarfareManager.Instance?.DisbandClan(pkg.ReadLong()) ?? "Failed"; SendNotification(sender, message); SyncAllDataToClients(); } } public void RequestKickMember(long targetId) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_0006: Expected O, but got Unknown ZPackage val = new ZPackage(); val.Write(LocalId); val.Write(targetId); ToServer("FCW_KickMember", val); } private void RPC_OnKickMember(long sender, ZPackage pkg) { if (IsServer) { string message = WarfareManager.Instance?.KickMember(pkg.ReadLong(), pkg.ReadLong()) ?? "Failed"; SendNotification(sender, message); SyncAllDataToClients(); } } public void RequestSetRank(long targetId, ClanRank rank) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_0006: Expected O, but got Unknown ZPackage val = new ZPackage(); val.Write(LocalId); val.Write(targetId); val.Write((int)rank); ToServer("FCW_SetRank", val); } private void RPC_OnSetRank(long sender, ZPackage pkg) { if (IsServer) { string message = WarfareManager.Instance?.SetRank(pkg.ReadLong(), pkg.ReadLong(), (ClanRank)pkg.ReadInt()) ?? "Failed"; SendNotification(sender, message); SyncAllDataToClients(); } } public void RequestSetClanEmblemUrl(string emblemUrl) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_0006: Expected O, but got Unknown ZPackage val = new ZPackage(); val.Write(LocalId); val.Write(emblemUrl ?? ""); ToServer("FCW_SetClanEmblemUrl", val); } private void RPC_OnSetClanEmblemUrl(long sender, ZPackage pkg) { if (IsServer) { string message = WarfareManager.Instance?.SetClanEmblemUrl(pkg.ReadLong(), pkg.ReadString()) ?? "Failed"; SendNotification(sender, message); SyncAllDataToClients(); } } public void RequestSetClanEmblemName(string emblemName) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_0006: Expected O, but got Unknown ZPackage val = new ZPackage(); val.Write(LocalId); val.Write(emblemName ?? ""); ToServer("FCW_SetClanEmblemName", val); } private void RPC_OnSetClanEmblemName(long sender, ZPackage pkg) { if (IsServer) { string message = WarfareManager.Instance?.SetClanEmblemName(pkg.ReadLong(), pkg.ReadString()) ?? "Failed"; SendNotification(sender, message); SyncAllDataToClients(); } } public void RequestSetClanLinks(string discordUrl, string websiteUrl) { RequestSetClanLinks(discordUrl, websiteUrl, ""); } public void RequestSetClanLinks(string discordUrl, string websiteUrl, string steamGroupUrl) { RequestSetClanLinks(discordUrl, websiteUrl, steamGroupUrl, "", ""); } public void RequestSetClanLinks(string discordUrl, string websiteUrl, string steamGroupUrl, string customUrl, string customLabel) { RequestSetClanLinks(discordUrl, websiteUrl, steamGroupUrl, customUrl, customLabel, "", ""); } public void RequestSetClanLinks(string discordUrl, string websiteUrl, string steamGroupUrl, string customUrl, string customLabel, string customUrl2, string customLabel2) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_0006: Expected O, but got Unknown ZPackage val = new ZPackage(); val.Write(LocalId); val.Write(discordUrl ?? ""); val.Write(websiteUrl ?? ""); val.Write(steamGroupUrl ?? ""); val.Write(customUrl ?? ""); val.Write(customLabel ?? ""); val.Write(customUrl2 ?? ""); val.Write(customLabel2 ?? ""); ToServer("FCW_SetClanLinks", val); } private void RPC_OnSetClanLinks(long sender, ZPackage pkg) { if (IsServer) { long actorId = pkg.ReadLong(); string discordUrl = pkg.ReadString(); string websiteUrl = pkg.ReadString(); string text = ""; string text2 = ""; string text3 = ""; string text4 = ""; string text5 = ""; try { text = pkg.ReadString(); } catch { text = ""; } try { text2 = pkg.ReadString(); text3 = pkg.ReadString(); } catch { text2 = ""; text3 = ""; } try { text4 = pkg.ReadString(); text5 = pkg.ReadString(); } catch { text4 = ""; text5 = ""; } string message = WarfareManager.Instance?.SetClanLinks(actorId, discordUrl, websiteUrl, text, text2, text3, text4, text5) ?? "Failed"; SendNotification(sender, message); SyncAllDataToClients(); } } public void RequestSetRole(ClanRole role) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_0006: Expected O, but got Unknown ZPackage val = new ZPackage(); val.Write(LocalId); val.Write((int)role); ToServer("FCW_SetRole", val); } private void RPC_OnSetRole(long sender, ZPackage pkg) { if (IsServer) { string message = WarfareManager.Instance?.SetPrimaryRole(pkg.ReadLong(), (ClanRole)pkg.ReadInt()) ?? "Failed"; SendNotification(sender, message); SyncAllDataToClients(); } } public void RequestSetPrimaryRole(ClanRole role) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_0006: Expected O, but got Unknown ZPackage val = new ZPackage(); val.Write(LocalId); val.Write((int)role); ToServer("FCW_SetPrimaryRole", val); } private void RPC_OnSetPrimaryRole(long sender, ZPackage pkg) { if (IsServer) { string message = WarfareManager.Instance?.SetPrimaryRole(pkg.ReadLong(), (ClanRole)pkg.ReadInt()) ?? "Failed"; SendNotification(sender, message); SyncAllDataToClients(); } } public void RequestSetSecondaryRole(ClanRole role) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_0006: Expected O, but got Unknown ZPackage val = new ZPackage(); val.Write(LocalId); val.Write((int)role); ToServer("FCW_SetSecondaryRole", val); } private void RPC_OnSetSecondaryRole(long sender, ZPackage pkg) { if (IsServer) { string message = WarfareManager.Instance?.SetSecondaryRole(pkg.ReadLong(), (ClanRole)pkg.ReadInt()) ?? "Failed"; SendNotification(sender, message); SyncAllDataToClients(); } } public void RequestSetTertiaryRole(ClanRole role) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_0006: Expected O, but got Unknown ZPackage val = new ZPackage(); val.Write(LocalId); val.Write((int)role); ToServer("FCW_SetTertiaryRole", val); } private void RPC_OnSetTertiaryRole(long sender, ZPackage pkg) { if (IsServer) { string message = WarfareManager.Instance?.SetTertiaryRole(pkg.ReadLong(), (ClanRole)pkg.ReadInt()) ?? "Failed"; SendNotification(sender, message); SyncAllDataToClients(); } } public void RequestCastVote(long candidateId) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_0006: Expected O, but got Unknown ZPackage val = new ZPackage(); val.Write(LocalId); val.Write(candidateId); ToServer("FCW_CastVote", val); } private void RPC_OnCastVote(long sender, ZPackage pkg) { if (IsServer) { string message = WarfareManager.Instance?.CastElectionVote(pkg.ReadLong(), pkg.ReadLong()) ?? "Failed"; SendNotification(sender, message); SyncAllDataToClients(); } } public void RequestClaimTerritory(Vector2i grid) { //IL_0005: Unknown result type (might be due to invalid IL or missing references) //IL_0010: Expected O, but got Unknown //IL_0012: Unknown result type (might be due to invalid IL or missing references) //IL_0018: Expected O, but got Unknown //IL_002f: Unknown result type (might be due to invalid IL or missing references) //IL_003b: Unknown result type (might be due to invalid IL or missing references) if (!((Object)Player.m_localPlayer == (Object)null)) { ZPackage val = new ZPackage(); val.Write(LocalId); val.Write(LocalName); val.Write(grid.x); val.Write(grid.y); ToServer("FCW_ClaimTerritory", val); } } private void RPC_OnClaimTerritory(long sender, ZPackage pkg) { //IL_0044: Unknown result type (might be due to invalid IL or missing references) //IL_0050: Unknown result type (might be due to invalid IL or missing references) //IL_0057: Expected O, but got Unknown if (IsServer) { pkg.ReadLong(); pkg.ReadString(); int num = pkg.ReadInt(); int num2 = pkg.ReadInt(); long playerId = ResolveSenderPlayerId(sender); string playerName = ResolveSenderPlayerName(sender); WarfareManager.ClaimResult claimResult = WarfareManager.Instance?.TryClaimTerritory(playerId, playerName, new Vector2i(num, num2)); ZPackage val = new ZPackage(); val.Write(claimResult?.Success ?? false); val.Write(claimResult?.Message ?? "Unknown error"); ZRoutedRpc instance = ZRoutedRpc.instance; if (instance != null) { instance.InvokeRoutedRPC(sender, "FCW_ClaimResult", new object[1] { val }); } if (claimResult != null && claimResult.Success) { SyncAllDataToClients(); } } } private void RPC_OnClaimResult(long sender, ZPackage pkg) { bool flag = pkg.ReadBool(); string text = pkg.ReadString(); this.OnNotificationReceived?.Invoke(flag ? ("" + text + "") : ("" + text + "")); } public void RequestUpdateProfile(string aboutMe, string activeTitle, string profileImageKey = "") { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_0006: Expected O, but got Unknown ZPackage val = new ZPackage(); val.Write(LocalId); val.Write(aboutMe ?? string.Empty); val.Write(activeTitle ?? string.Empty); val.Write(profileImageKey ?? string.Empty); ToServer("FCW_UpdateProfile", val); } public void RequestUpdateAboutMe(string aboutMe) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_0006: Expected O, but got Unknown ZPackage val = new ZPackage(); val.Write(LocalId); val.Write(aboutMe ?? string.Empty); ToServer("FCW_UpdateAboutMe", val); } public void RequestSetActiveTitle(string titleId) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_0006: Expected O, but got Unknown ZPackage val = new ZPackage(); val.Write(titleId ?? string.Empty); ToServer("FCW_SetActiveTitle", val); } private void RPC_OnSetActiveTitle(long sender, ZPackage pkg) { if (!IsServer) { return; } string text = pkg.ReadString(); long num = ResolveSenderPlayerId(sender); string steamId = ResolveSenderSteamId64(sender); PlayerClanInfo playerClanInfo = WarfareManager.Instance?.AssociatePlayerIdentity(num, ResolveSenderPlayerName(sender), steamId); if (num == 0L || playerClanInfo == null) { LogProfileSpoofOnce(sender, 0L, num, steamId); SendNotification(sender, "Profile ownership could not be verified."); return; } string message; bool flag = WarfareManager.Instance.SetActiveTitle(playerClanInfo.PlayerId, text, out message); if (!flag && !string.IsNullOrEmpty(text) && message.Contains("not unlocked")) { string item = sender + "|" + text; if (_invalidTitleSelectionLogs.Add(item)) { Plugin.Log.LogWarning((object)("[FCW Titles] Rejected title selection sender=" + sender + " player=" + playerClanInfo.PlayerId + " title=" + text)); } } SendNotification(sender, message); if (flag) { SyncAllDataToClients(); } } private void SendAchievementUnlockNotification(long peer, AchievementEvaluationResult result) { if (result != null && result.UnlockedIds.Count != 0) { if (result.UnlockedIds.Count == 1) { PlayerAchievementDefinition playerAchievementDefinition = PlayerAchievementCatalog.Get(result.UnlockedIds[0]); SendNotification(peer, "Achievement Unlocked: " + (playerAchievementDefinition?.DisplayName ?? result.UnlockedIds[0]) + "\nReward: " + result.RewardCoins + " Coins"); } else { SendNotification(peer, "Achievements Unlocked: " + result.UnlockedIds.Count + "\nRewards received: " + result.RewardCoins + " Coins"); } } } private static string AchievementDeliveryToken(PlayerClanInfo profile) { if (profile?.PendingAchievementCoinRewards == null || profile.PendingAchievementCoinRewards.Count == 0) { return ""; } return string.Join(",", (from entry in profile.PendingAchievementCoinRewards.OrderBy, string>((KeyValuePair entry) => entry.Key, StringComparer.Ordinal) select entry.Key + "=" + entry.Value).ToArray()); } private void TryDeliverPendingAchievementCoins(long peer, PlayerClanInfo profile) { //IL_0045: Unknown result type (might be due to invalid IL or missing references) //IL_004b: Expected O, but got Unknown string text = AchievementDeliveryToken(profile); if (string.IsNullOrEmpty(text)) { return; } int num = profile.PendingAchievementCoinRewards.Values.Sum((int value) => Math.Max(0, value)); if (num > 0) { ZPackage val = new ZPackage(); val.Write(text); val.Write(num); ZRoutedRpc instance = ZRoutedRpc.instance; if (instance != null) { instance.InvokeRoutedRPC(peer, "FCW_AchievementCoinDelivery", new object[1] { val }); } } } private void RPC_OnAchievementCoinDelivery(long sender, ZPackage pkg) { //IL_0136: Unknown result type (might be due to invalid IL or missing references) //IL_013c: Expected O, but got Unknown //IL_006d: Unknown result type (might be due to invalid IL or missing references) //IL_0078: Expected O, but got Unknown //IL_007b: Unknown result type (might be due to invalid IL or missing references) //IL_0086: Expected O, but got Unknown if (IsServer) { return; } string text = pkg.ReadString(); int num = pkg.ReadInt(); if (string.IsNullOrEmpty(text) || num <= 0) { return; } if (!_processedAchievementDeliveryTokens.Contains(text)) { Player localPlayer = Player.m_localPlayer; GameObject itemPrefabSafe = GetItemPrefabSafe(ObjectDB.instance, ClanConfig.CoinItemPrefab?.Value ?? "Coins"); Inventory val = ((localPlayer != null) ? ((Humanoid)localPlayer).GetInventory() : null); if ((Object)localPlayer == (Object)null || (Object)itemPrefabSafe == (Object)null || val == null || !CanInventoryAcceptItem(val, itemPrefabSafe, num, out var _)) { SendNotification(GetServerPeerID(), "Achievement Coins pending: inventory full."); return; } SuppressTax = true; object obj; try { obj = ((object)val).GetType().GetMethod("AddItem", new Type[2] { typeof(GameObject), typeof(int) })?.Invoke(val, new object[2] { itemPrefabSafe, num }); } finally { SuppressTax = false; } if (obj == null || (obj is bool && !(bool)obj)) { return; } _processedAchievementDeliveryTokens.Add(text); } ZPackage val2 = new ZPackage(); val2.Write(text); val2.Write(num); ToServer("FCW_ConfirmAchievementCoins", val2); } private void RPC_OnConfirmAchievementCoins(long sender, ZPackage pkg) { if (IsServer) { string a = pkg.ReadString(); int num = pkg.ReadInt(); long playerId = ResolveSenderPlayerId(sender); PlayerClanInfo playerClanInfo = WarfareManager.Instance?.Data?.GetPlayer(playerId); string b = AchievementDeliveryToken(playerClanInfo); int valueOrDefault = (playerClanInfo?.PendingAchievementCoinRewards?.Values.Sum((int value) => Math.Max(0, value))).GetValueOrDefault(); if (playerClanInfo != null && string.Equals(a, b, StringComparison.Ordinal) && num == valueOrDefault && num > 0) { playerClanInfo.PendingAchievementCoinRewards.Clear(); WarfareManager.Instance.MarkDirty(); WarfareManager.Instance.SaveData(); SendNotification(sender, "Achievement reward delivered: " + num + " Coins."); SyncAllDataToClients(); } } } private void RPC_OnUpdateAboutMe(long sender, ZPackage pkg) { if (!IsServer) { return; } long requestedPlayerId = pkg.ReadLong(); string aboutMe = pkg.ReadString(); long num = ResolveSenderPlayerId(sender); string steamId = ResolveSenderSteamId64(sender); PlayerClanInfo playerClanInfo = WarfareManager.Instance?.AssociatePlayerIdentity(num, ResolveSenderPlayerName(sender), steamId); if (num == 0L || playerClanInfo == null) { LogProfileSpoofOnce(sender, requestedPlayerId, num, steamId); SendNotification(sender, "Profile ownership could not be verified."); return; } string message; bool num2 = WarfareManager.Instance.UpdateAboutMe(playerClanInfo.PlayerId, aboutMe, out message); SendNotification(sender, message); if (num2) { SyncAllDataToClients(); } } private void RPC_OnUpdateProfile(long sender, ZPackage pkg) { if (!IsServer) { return; } long num = pkg.ReadLong(); string aboutMe = pkg.ReadString(); string activeTitle = pkg.ReadString(); string profileImageKey = pkg.ReadString(); long num2 = ResolveSenderPlayerId(sender); string steamId = ResolveSenderSteamId64(sender); PlayerClanInfo playerClanInfo = WarfareManager.Instance?.AssociatePlayerIdentity(num2, ResolveSenderPlayerName(sender), steamId); if (num2 == 0L || playerClanInfo == null) { LogProfileSpoofOnce(sender, num, num2, steamId); SendNotification(sender, "Profile ownership could not be verified."); return; } if (num != num2 && num != playerClanInfo.PlayerId && (playerClanInfo.LinkedPlayerIds == null || !playerClanInfo.LinkedPlayerIds.Contains(num))) { LogProfileSpoofOnce(sender, num, num2, steamId); } string message = WarfareManager.Instance?.UpdateProfile(playerClanInfo.PlayerId, aboutMe, activeTitle, profileImageKey) ?? "Profile update failed."; SendNotification(sender, message); SyncAllDataToClients(); } public void RequestDiscoverTerritory(Vector2i grid) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_0006: Expected O, but got Unknown //IL_0012: Unknown result type (might be due to invalid IL or missing references) //IL_001e: Unknown result type (might be due to invalid IL or missing references) ZPackage val = new ZPackage(); val.Write(LocalId); val.Write(grid.x); val.Write(grid.y); ToServer("FCW_DiscoverTerritory", val); } private void RPC_OnDiscoverTerritory(long sender, ZPackage pkg) { //IL_0022: Unknown result type (might be due to invalid IL or missing references) //IL_0027: Unknown result type (might be due to invalid IL or missing references) //IL_003b: Unknown result type (might be due to invalid IL or missing references) //IL_0067: Unknown result type (might be due to invalid IL or missing references) //IL_006e: Expected O, but got Unknown //IL_0070: Unknown result type (might be due to invalid IL or missing references) if (!IsServer) { return; } long playerId = pkg.ReadLong(); Vector2i grid = default(Vector2i); ((Vector2i)(ref grid))..ctor(pkg.ReadInt(), pkg.ReadInt()); Vector3 center = Vector3.zero; float radius = 0f; string text = WarfareManager.Instance?.DiscoverTerritory(playerId, grid, out center, out radius) ?? "Failed"; SendNotification(sender, text); if (text.Contains("")) { ZPackage val = new ZPackage(); val.Write(center); val.Write(radius); ZRoutedRpc instance = ZRoutedRpc.instance; if (instance != null) { instance.InvokeRoutedRPC(sender, "FCW_DiscoverTerritoryClient", new object[1] { val }); } SyncAllDataToClients(); } } private void RPC_OnDiscoverTerritoryClient(long sender, ZPackage pkg) { //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_0048: Unknown result type (might be due to invalid IL or missing references) //IL_0053: Expected O, but got Unknown //IL_006c: Unknown result type (might be due to invalid IL or missing references) Vector3 val = pkg.ReadVector3(); float num = pkg.ReadSingle(); try { MethodInfo method = typeof(Minimap).GetMethod("Explore", new Type[2] { typeof(Vector3), typeof(float) }); if ((Object)Minimap.instance != (Object)null && method != null) { method.Invoke(Minimap.instance, new object[2] { val, num }); } } catch (Exception ex) { Plugin.Log.LogWarning((object)("[FCW Discovery] Could not reveal minimap area: " + ex.Message)); } } public void RequestRenameTerritory(Vector2i grid, string newName) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_0006: Expected O, but got Unknown //IL_0012: Unknown result type (might be due to invalid IL or missing references) //IL_001e: Unknown result type (might be due to invalid IL or missing references) ZPackage val = new ZPackage(); val.Write(LocalId); val.Write(grid.x); val.Write(grid.y); val.Write(newName ?? ""); ToServer("FCW_RenameTerritory", val); } private void RPC_OnRenameTerritory(long sender, ZPackage pkg) { //IL_0032: Unknown result type (might be due to invalid IL or missing references) if (IsServer) { long playerId = pkg.ReadLong(); int num = pkg.ReadInt(); int num2 = pkg.ReadInt(); string newName = pkg.ReadString(); string message = WarfareManager.Instance?.TryRenameTerritory(playerId, new Vector2i(num, num2), newName) ?? "Failed"; SendNotification(sender, message); SyncAllDataToClients(); } } public void RequestUpgradeTerritory(Vector2i grid) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_0006: Expected O, but got Unknown //IL_0012: Unknown result type (might be due to invalid IL or missing references) //IL_001e: Unknown result type (might be due to invalid IL or missing references) ZPackage val = new ZPackage(); val.Write(LocalId); val.Write(grid.x); val.Write(grid.y); ToServer("FCW_UpgradeTerritory", val); } private void RPC_OnUpgradeTerritory(long sender, ZPackage pkg) { //IL_0025: Unknown result type (might be due to invalid IL or missing references) if (IsServer) { string message = WarfareManager.Instance?.UpgradeTerritory(pkg.ReadLong(), new Vector2i(pkg.ReadInt(), pkg.ReadInt())) ?? "Failed"; SendNotification(sender, message); SyncAllDataToClients(); } } public void RequestTerritoryBuildingAction(Vector2i grid, int action, TerritoryBuildingType buildingType, int slot) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_0006: Expected O, but got Unknown //IL_0012: Unknown result type (might be due to invalid IL or missing references) //IL_001e: Unknown result type (might be due to invalid IL or missing references) ZPackage val = new ZPackage(); val.Write(LocalId); val.Write(grid.x); val.Write(grid.y); val.Write(action); val.Write((int)buildingType); val.Write(slot); ToServer("FCW_TerritoryBuildingAction", val); } private void RPC_OnTerritoryBuildingAction(long sender, ZPackage pkg) { //IL_004a: Unknown result type (might be due to invalid IL or missing references) if (IsServer) { pkg.ReadLong(); int num = pkg.ReadInt(); int num2 = pkg.ReadInt(); int action = pkg.ReadInt(); TerritoryBuildingType type = (TerritoryBuildingType)pkg.ReadInt(); int slot = pkg.ReadInt(); long playerId = ResolveSenderPlayerId(sender); string message = WarfareManager.Instance?.ManageTerritoryBuilding(playerId, new Vector2i(num, num2), action, type, slot) ?? "Failed"; SendNotification(sender, message); SyncAllDataToClients(); } } public void RequestGuardAction(string territoryId, int action, GuardType guardType, int guardId) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_0006: Expected O, but got Unknown ZPackage val = new ZPackage(); val.Write(LocalId); val.Write(territoryId ?? ""); val.Write(action); val.Write((int)guardType); val.Write(guardId); ToServer("FCW_GuardAction", val); } private void RPC_OnGuardAction(long sender, ZPackage pkg) { if (IsServer) { long playerId = pkg.ReadLong(); string territoryId = pkg.ReadString(); int action = pkg.ReadInt(); GuardType type = (GuardType)pkg.ReadInt(); int guardId = pkg.ReadInt(); string message = WarfareManager.Instance?.ManageGuard(playerId, territoryId, action, type, guardId) ?? "Failed"; SendNotification(sender, message); SyncAllDataToClients(); } } public void RequestWorldEventClaim(int eventId) { //IL_0007: Unknown result type (might be due to invalid IL or missing references) //IL_0012: Expected O, but got Unknown //IL_0015: Unknown result type (might be due to invalid IL or missing references) //IL_001b: Expected O, but got Unknown //IL_003f: Unknown result type (might be due to invalid IL or missing references) Player localPlayer = Player.m_localPlayer; if (!((Object)localPlayer == (Object)null)) { ZPackage val = new ZPackage(); val.Write(LocalId); val.Write(LocalName); val.Write(eventId); val.Write(((Component)localPlayer).transform.position); ToServer("FCW_WorldEventClaim", val); } } private void RPC_OnWorldEventClaim(long sender, ZPackage pkg) { //IL_001d: 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_0040: Unknown result type (might be due to invalid IL or missing references) if (IsServer) { pkg.ReadLong(); pkg.ReadString(); int eventId = pkg.ReadInt(); Vector3 position = pkg.ReadVector3(); long playerId = ResolveSenderPlayerId(sender); string playerName = ResolveSenderPlayerName(sender); string message = WarfareManager.Instance?.ClaimWorldEvent(playerId, playerName, eventId, position) ?? "Failed"; SendNotification(sender, message); SyncAllDataToClients(); } } private static long ResolveSenderPlayerId(long sender) { //IL_0014: Unknown result type (might be due to invalid IL or missing references) //IL_001f: Expected O, but got Unknown //IL_00a7: Unknown result type (might be due to invalid IL or missing references) //IL_0104: Unknown result type (might be due to invalid IL or missing references) //IL_010a: Unknown result type (might be due to invalid IL or missing references) try { if (IsServer && sender == ZNet.GetUID() && (Object)Player.m_localPlayer != (Object)null) { return Player.m_localPlayer.GetPlayerID(); } ZNet instance = ZNet.instance; ZNetPeer val = ((instance != null) ? instance.GetPeer(sender) : null); if (val == null && (Object)(object)ZNet.instance != (Object)null) { foreach (ZNetPeer peer in ZNet.instance.GetPeers()) { if (peer != null && peer.m_uid == sender) { val = peer; break; } } } if (val != null && ZDOMan.instance != null) { ZDO zDO = ZDOMan.instance.GetZDO(val.m_characterID); long num = ((zDO != null) ? zDO.GetLong(ZDOVars.s_playerID, 0L) : 0); if (num != 0L) { return num; } } Player[] array = Object.FindObjectsOfType(); foreach (Player val2 in array) { ZNetView component = ((Component)val2).GetComponent(); ZDO val3 = ((component != null) ? component.GetZDO() : null); if (val != null && val3 != null && val3.m_uid == val.m_characterID) { return val2.GetPlayerID(); } } } catch (Exception ex) { Plugin.Log.LogWarning((object)("[FCW Profile] Failed to resolve sender PlayerID sender=" + sender + " error=" + ex.Message)); } return 0L; } private static string ResolveSenderPlayerName(long sender) { long num = ResolveSenderPlayerId(sender); if (num == 0L) { return ""; } Player[] array = Object.FindObjectsOfType(); foreach (Player val in array) { if (val.GetPlayerID() == num) { return val.GetPlayerName() ?? ""; } } return WarfareManager.Instance?.Data?.GetPlayer(num)?.PlayerName ?? ""; } private void LogProfileSpoofOnce(long sender, long requestedPlayerId, long resolvedPlayerId, string steamId64) { string item = sender + "|" + requestedPlayerId + "|" + resolvedPlayerId + "|" + steamId64; if (_profileSpoofLogs.Add(item)) { Plugin.Log.LogWarning((object)("[FCW Profile] Rejected profile ownership mismatch sender=" + sender + " requestedPlayerId=" + requestedPlayerId + " resolvedPlayerId=" + resolvedPlayerId + " steamId64=" + steamId64)); } } private static string ResolveSenderSteamId64(long sender) { //IL_0007: Unknown result type (might be due to invalid IL or missing references) //IL_0012: Expected O, but got Unknown try { ZNet instance = ZNet.instance; if ((Object)instance == (Object)null) { return ""; } ZNetPeer val = instance.GetPeer(sender); if (val == null) { foreach (ZNetPeer peer in instance.GetPeers()) { if (peer != null && peer.m_uid == sender) { val = peer; break; } } } object obj; if (val == null) { obj = null; } else { ISocket socket = val.m_socket; obj = ((socket != null) ? socket.GetHostName() : null); } if (obj == null) { obj = ""; } string text = (string)obj; string text2 = NormalizeSteamId64(text); if (string.IsNullOrEmpty(text2)) { Plugin.Log.LogWarning((object)("[FCW Admin] Could not resolve SteamID64 from ZNet peer. sender=" + sender + " hostName='" + text + "'")); } return text2; } catch { return ""; } } private static string ResolveAdminSteamId64(long sender, string claimedSteamId64) { string text = ResolveSenderSteamId64(sender); if (!string.IsNullOrEmpty(text)) { return text; } string text2 = NormalizeSteamId64(claimedSteamId64); if (!string.IsNullOrEmpty(text2)) { Plugin.Log.LogWarning((object)("[FCW Admin] Falling back to claimed SteamID64 for admin RPC. sender=" + sender + " claimed=" + text2)); return text2; } return ""; } private static string NormalizeSteamId64(string value) { string text = value ?? ""; string text2 = text.Trim(); if (text2.StartsWith("Steam_", StringComparison.OrdinalIgnoreCase)) { text2 = text2.Substring(6).Trim(); } int num = text2.LastIndexOf('/'); if (num >= 0) { text2 = text2.Substring(num + 1).Trim(); } int num2 = text2.LastIndexOf('_'); if (num2 >= 0) { text2 = text2.Substring(num2 + 1).Trim(); } if (IsSteamId64Text(text2)) { return text2; } for (int i = 0; i <= text.Length - 17; i++) { string text3 = text.Substring(i, 17); if (IsSteamId64Text(text3)) { return text3; } } return ""; } private static bool IsSteamId64Text(string value) { if (string.IsNullOrWhiteSpace(value) || value.Length != 17 || !value.StartsWith("765", StringComparison.Ordinal)) { return false; } for (int i = 0; i < value.Length; i++) { if (!char.IsDigit(value[i])) { return false; } } return true; } public void RequestWorldEventAdmin(int action, int eventId) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_0005: Unknown result type (might be due to invalid IL or missing references) //IL_000d: Unknown result type (might be due to invalid IL or missing references) //IL_0018: Expected O, but got Unknown //IL_0026: Unknown result type (might be due to invalid IL or missing references) //IL_002c: Expected O, but got Unknown //IL_003b: 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) Vector3 val = Vector3.zero; Player localPlayer = Player.m_localPlayer; if ((Object)localPlayer != (Object)null) { val = ((Component)localPlayer).transform.position; } ZPackage val2 = new ZPackage(); val2.Write(action); val2.Write(eventId); val2.Write(val); val2.Write(WarfareManager.GetLocalSteamId64ForDiagnostics()); ToServer("FCW_WorldEventAdmin", val2); } private void RPC_OnWorldEventAdmin(long sender, ZPackage pkg) { //IL_0019: Unknown result type (might be due to invalid IL or missing references) //IL_001e: Unknown result type (might be due to invalid IL or missing references) //IL_0056: Unknown result type (might be due to invalid IL or missing references) //IL_00ae: Unknown result type (might be due to invalid IL or missing references) if (IsServer) { int num = pkg.ReadInt(); int num2 = pkg.ReadInt(); Vector3 position = pkg.ReadVector3(); string adminSteamId = ResolveAdminSteamId64(sender, pkg.ReadString()); WarfareManager instance = WarfareManager.Instance; SendNotification(sender, num switch { 0 => instance?.CreateTestWorldEvent(adminSteamId, position) ?? "Failed", 1 => instance?.RemoveWorldEvent(adminSteamId, num2) ?? "Failed", 2 => instance?.ExpireWorldEvent(adminSteamId, num2) ?? "Failed", 10 => instance?.CreateWorldEvent(adminSteamId, (WorldEventType)num2, position) ?? "Failed", _ => "Unknown world event admin action.", }); SyncAllDataToClients(); } } public void RequestKingdomAdminAction(int action, long targetId) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_0006: Expected O, but got Unknown ZPackage val = new ZPackage(); val.Write(action); val.Write(targetId); val.Write(WarfareManager.GetLocalSteamId64ForDiagnostics()); ToServer("FCW_KingdomAdminAction", val); } public void RequestAdminTextConfig(int action, string steamId, string nickname, string note) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_0006: Expected O, but got Unknown ZPackage val = new ZPackage(); val.Write(action); val.Write(steamId ?? ""); val.Write(nickname ?? ""); val.Write(note ?? ""); val.Write(WarfareManager.GetLocalSteamId64ForDiagnostics()); ToServer("FCW_AdminTextConfig", val); } private void RPC_OnAdminTextConfig(long sender, ZPackage pkg) { if (IsServer) { int action = pkg.ReadInt(); string steamId = pkg.ReadString(); string nickname = pkg.ReadString(); string note = pkg.ReadString(); string adminSteamId = ResolveAdminSteamId64(sender, pkg.ReadString()); string message = WarfareManager.Instance?.AdminTextConfigAction(adminSteamId, action, steamId, nickname, note) ?? "Failed"; SendNotification(sender, message); SyncAllDataToClients(); } } private void RPC_OnKingdomAdminAction(long sender, ZPackage pkg) { if (IsServer) { int num = pkg.ReadInt(); long num2 = pkg.ReadLong(); string adminSteamId = ResolveAdminSteamId64(sender, pkg.ReadString()); WarfareManager instance = WarfareManager.Instance; string text = ((num < 200) ? (num switch { 100 => instance?.AdminDeleteClan(adminSteamId, (int)num2) ?? "Failed", 101 => instance?.AdminDeleteHomestead(adminSteamId, num2) ?? "Failed", 102 => instance?.AdminReleaseProtectedZoneConflicts(adminSteamId) ?? "Failed", _ => "Unknown admin action.", }) : HandleKingdomGamesAdminAction(adminSteamId, num, num2)); string message = text; SendNotification(sender, message); SyncAllDataToClients(); } } private string HandleKingdomGamesAdminAction(string adminSteamId64, int action, long targetId) { WarfareManager instance = WarfareManager.Instance; if ((Object)(object)instance == (Object)null || !instance.HasEventAdminAccess(adminSteamId64)) { return "Admin access required."; } WarfareData data = instance.Data; if (data == null) { return "No warfare data loaded."; } string text; switch (action) { case 200: data.GameStats.Clear(); text = "Reset all game stats."; break; case 201: if (targetId == 0L) { return "Player ID required."; } data.GameStats.Remove(targetId); text = "Reset game stats for player " + targetId + "."; break; case 202: data.GameHistory.Clear(); text = "Deleted all game history."; break; case 203: { if (targetId == 0L) { return "Game history ID required."; } int num = data.GameHistory.RemoveAll((FehuGameHistoryEntry h) => h.GameId == (int)targetId); text = "Deleted " + num + " history entries for game " + targetId + "."; break; } case 204: text = "Closed and deleted " + AdminCloseActiveGames(data, FehuGameType.Chess) + " active Chess matches."; break; case 205: text = "Closed and deleted " + AdminCloseActiveGames(data, FehuGameType.Dice) + " active Dice tables."; break; case 206: text = "Closed and deleted " + AdminCloseActiveGames(data, FehuGameType.Poker) + " active Poker tables."; break; case 207: { if (targetId == 0L) { return "Game/Table ID required."; } FehuGameTable fehuGameTable2 = data.Games.Find((FehuGameTable g) => g.GameId == (int)targetId); if (fehuGameTable2 == null) { return "Game/table not found."; } AdminCloseGame(fehuGameTable2, "Admin force closed table."); data.Games.Remove(fehuGameTable2); text = "Force closed table " + targetId + "."; break; } case 208: { if (targetId == 0L) { return "Game/Table ID required."; } FehuGameTable fehuGameTable = data.Games.Find((FehuGameTable g) => g.GameId == (int)targetId); if (fehuGameTable == null) { return "Game/table not found."; } CancelGameEscrow(fehuGameTable, "Admin refunded stuck game escrow."); text = "Refunded stuck escrow for table " + targetId + "."; break; } case 209: text = BuildActiveGameDebug(data); break; case 300: instance.MarkDirty(); instance.SaveData(); text = "Forced save completed."; break; case 301: text = "Data version: " + 49 + " | Loaded: " + data.Version + "."; break; case 302: text = "Sync debug: clans=" + data.Clans.Count + ", players=" + data.Players.Count + ", games=" + data.Games.Count + ", history=" + data.GameHistory.Count + "."; break; case 303: text = "Cleared " + AdminClearStuckEscrow(data) + " stuck escrow games."; break; case 304: instance.MarkDirty(); instance.SaveData(); text = "Manual data backup/save completed."; break; case 305: text = "Cleared " + ClearTerritoryStakeLocks(data) + " territory stake locks."; break; case 306: text = instance.AdminCheckAllTerritories(adminSteamId64); break; default: return "Unknown admin action."; } Plugin.Log.LogWarning((object)("[FCW Admin] steamId64=" + adminSteamId64 + " action=" + action + " target=" + targetId + " result=" + text)); instance.MarkDirty(); instance.SaveData(); return "Admin: " + text; } private int AdminCloseActiveGames(WarfareData data, FehuGameType gameType) { List list = data.Games.FindAll((FehuGameTable g) => g.GameType == gameType && (g.Status == FehuGameStatus.Waiting || g.Status == FehuGameStatus.Active)); foreach (FehuGameTable item in list) { AdminCloseGame(item, "Admin closed active " + gameType.ToString() + " table."); } foreach (FehuGameTable item2 in list) { data.Games.Remove(item2); } return list.Count; } private void AdminCloseGame(FehuGameTable game, string result) { if (game != null) { if (!game.PayoutIssued) { CancelGameEscrow(game, result); return; } game.Status = FehuGameStatus.Cancelled; game.Result = result; game.FinishedAt = WarfareManager.Now(); } } private int AdminClearStuckEscrow(WarfareData data) { List list = data.Games.FindAll((FehuGameTable g) => g.EscrowCollecting || (g.EscrowPlayerIds != null && g.EscrowPlayerIds.Count > 0)); foreach (FehuGameTable item in list) { CancelGameEscrow(item, "Admin cleared stuck game escrow."); } return list.Count; } private string BuildActiveGameDebug(WarfareData data) { int num = data.Games.Count((FehuGameTable g) => g.Status == FehuGameStatus.Waiting); int num2 = data.Games.Count((FehuGameTable g) => g.Status == FehuGameStatus.Active); int num3 = data.Games.Count((FehuGameTable g) => g.EscrowCollecting || (g.EscrowPlayerIds != null && g.EscrowPlayerIds.Count > 0)); string text = string.Join(", ", (from g in data.Games.Where((FehuGameTable g) => g.Status == FehuGameStatus.Waiting || g.Status == FehuGameStatus.Active).Take(8) select "#" + g.GameId + " " + g.GameType.ToString() + " " + g.Status).ToArray()); return "Active game state: waiting=" + num + ", active=" + num2 + ", escrow=" + num3 + (string.IsNullOrWhiteSpace(text) ? "." : (" | " + text)); } public void RequestSetCapital(Vector2i grid) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_0006: Expected O, but got Unknown //IL_0012: Unknown result type (might be due to invalid IL or missing references) //IL_001e: Unknown result type (might be due to invalid IL or missing references) ZPackage val = new ZPackage(); val.Write(LocalId); val.Write(grid.x); val.Write(grid.y); ToServer("FCW_SetCapital", val); } private void RPC_OnSetCapital(long sender, ZPackage pkg) { //IL_0025: Unknown result type (might be due to invalid IL or missing references) if (IsServer) { string message = WarfareManager.Instance?.SetCapital(pkg.ReadLong(), new Vector2i(pkg.ReadInt(), pkg.ReadInt())) ?? "Failed"; SendNotification(sender, message); SyncAllDataToClients(); } } public void RequestBuyExpansion() { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_0006: Expected O, but got Unknown ZPackage val = new ZPackage(); val.Write(LocalId); ToServer("FCW_BuyExpansion", val); } private void RPC_OnBuyExpansion(long sender, ZPackage pkg) { if (IsServer) { string message = WarfareManager.Instance?.BuyTerritoryExpansion(pkg.ReadLong()) ?? "Failed"; SendNotification(sender, message); SyncAllDataToClients(); } } public void RequestUpgradeVault() { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_0006: Expected O, but got Unknown ZPackage val = new ZPackage(); val.Write(LocalId); ToServer("FCW_UpgradeVault", val); } private void RPC_OnUpgradeVault(long sender, ZPackage pkg) { if (IsServer) { string message = WarfareManager.Instance?.UpgradeVault(pkg.ReadLong()) ?? "Failed"; SendNotification(sender, message); SyncAllDataToClients(); } } public void RequestWithdrawVault(int clanId) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_0006: Expected O, but got Unknown //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_001f: 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) ZPackage val = new ZPackage(); val.Write(LocalId); val.Write(clanId); Vector2i val2 = CurrentGrid(); val.Write(val2.x); val.Write(val2.y); ToServer("FCW_WithdrawVault", val); } private void RPC_OnWithdrawVault(long sender, ZPackage pkg) { //IL_0030: Unknown result type (might be due to invalid IL or missing references) //IL_003b: Expected O, but got Unknown //IL_008a: 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) //IL_00e7: Expected O, but got Unknown if (!IsServer) { return; } long playerId = pkg.ReadLong(); int num = pkg.ReadInt(); Vector2i grid = default(Vector2i); ((Vector2i)(ref grid))..ctor(pkg.ReadInt(), pkg.ReadInt()); WarfareManager instance = WarfareManager.Instance; if ((Object)instance == (Object)null) { return; } if (!instance.IsVaultOpen()) { SendNotification(sender, "Vault is currently closed."); return; } WarfareData data = instance.Data; PlayerClanInfo player = data.GetPlayer(playerId); if (player == null || player.ClanId != num) { SendNotification(sender, "That is not your clan's vault."); return; } string message = ""; if (!instance.CanUseVaultAt(player, grid, coinDeposit: false, out message)) { SendNotification(sender, message); return; } if (!instance.CanWithdrawVaultItems(player)) { SendNotification(sender, "You do not have permission to withdraw from vault."); return; } List vault = data.GetVault(num); if (vault.Count == 0) { SendNotification(sender, "Vault is empty."); return; } ClanVaultItem clanVaultItem = vault[0]; ZPackage val = new ZPackage(); val.Write(false); val.Write(num); val.Write(clanVaultItem.PrefabName); val.Write(1); ZRoutedRpc instance2 = ZRoutedRpc.instance; if (instance2 != null) { instance2.InvokeRoutedRPC(sender, "FCW_AddToVault", new object[1] { val }); } } public void RequestWithdrawVaultItem(int clanId, string prefab, int amount) { //IL_001c: Unknown result type (might be due to invalid IL or missing references) //IL_0022: Expected O, but got Unknown //IL_004b: Unknown result type (might be due to invalid IL or missing references) //IL_0050: Unknown result type (might be due to invalid IL or missing references) //IL_0052: 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) VaultDebug("[FCW Vault] withdraw request item=" + prefab + " amount=" + amount); ZPackage val = new ZPackage(); val.Write(LocalId); val.Write(clanId); val.Write(prefab ?? ""); val.Write(amount); Vector2i val2 = CurrentGrid(); val.Write(val2.x); val.Write(val2.y); ToServer("FCW_WithdrawVaultItem", val); } private void RPC_OnWithdrawVaultItem(long sender, ZPackage pkg) { //IL_00b2: Unknown result type (might be due to invalid IL or missing references) //IL_0161: Unknown result type (might be due to invalid IL or missing references) //IL_0168: Expected O, but got Unknown if (!IsServer) { return; } long playerId = pkg.ReadLong(); int num = pkg.ReadInt(); string prefab = pkg.ReadString(); int num2 = pkg.ReadInt(); Vector2i grid = default(Vector2i); ((Vector2i)(ref grid))..ctor(pkg.ReadInt(), pkg.ReadInt()); WarfareManager instance = WarfareManager.Instance; WarfareData warfareData = instance?.Data; PlayerClanInfo playerClanInfo = warfareData?.GetPlayer(playerId); if (playerClanInfo == null || (num != 0 && playerClanInfo.ClanId != num) || (num == 0 && (!playerClanInfo.IsFreeborn || playerClanInfo.ClanId != 0))) { SendNotification(sender, "That is not your clan's vault."); return; } string message = ""; if ((Object)(object)instance == (Object)null || !instance.CanUseVaultAt(playerClanInfo, grid, coinDeposit: false, out message)) { SendNotification(sender, message); return; } if (!instance.IsVaultOpen()) { SendNotification(sender, "Vault is currently closed."); return; } if (num != 0 && !instance.CanWithdrawVaultItems(playerClanInfo)) { SendNotification(sender, "You do not have permission to withdraw from vault."); return; } ClanVaultItem clanVaultItem = ((num == 0) ? warfareData.GetFreebornVault(playerId) : warfareData.GetVault(num)).Find((ClanVaultItem v) => v.PrefabName == prefab); if (clanVaultItem == null || num2 <= 0 || clanVaultItem.Amount < num2) { SendNotification(sender, "Vault item is no longer available."); VaultDebug("[FCW Vault] withdraw result=fail reason=vault item unavailable item=" + prefab + " amount=" + num2); return; } ZPackage val = new ZPackage(); val.Write(false); val.Write(num); val.Write(prefab); val.Write(num2); ZRoutedRpc instance2 = ZRoutedRpc.instance; if (instance2 != null) { instance2.InvokeRoutedRPC(sender, "FCW_AddToVault", new object[1] { val }); } } private void RPC_OnConfirmVaultWithdraw(long sender, ZPackage pkg) { if (!IsServer) { return; } long playerId = pkg.ReadLong(); int num = pkg.ReadInt(); string text = pkg.ReadString(); int amount = pkg.ReadInt(); WarfareManager instance = WarfareManager.Instance; PlayerClanInfo playerClanInfo = instance?.Data?.GetPlayer(playerId); string text2; if (num == 0 && playerClanInfo != null && playerClanInfo.IsFreeborn && playerClanInfo.ClanId == 0) { if (instance.ConsumeFreebornVaultItem(playerId, text, amount)) { instance.MarkDirty(); text2 = "Item withdrawn from vault."; VaultDebug("[FCW Vault] withdraw result=success item=" + text + " amount=" + amount); } else { text2 = "Vault item is no longer available."; VaultDebug("[FCW Vault] withdraw result=fail reason=freeborn vault consume failed item=" + text + " amount=" + amount); } } else { text2 = instance?.ConfirmVaultWithdraw(playerId, num, text, amount) ?? "Failed"; VaultDebug("[FCW Vault] withdraw result=" + (text2.Contains("green") ? "success" : "fail reason=clan vault consume failed") + " item=" + text + " amount=" + amount); } SendNotification(sender, text2); SyncAllDataToClients(); } public void RequestSetVaultPermission(long targetPlayerId, int permission, bool allowed) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_0006: Expected O, but got Unknown ZPackage val = new ZPackage(); val.Write(LocalId); val.Write(targetPlayerId); val.Write(permission); val.Write(allowed); ToServer("FCW_SetVaultPermission", val); } private void RPC_OnSetVaultPermission(long sender, ZPackage pkg) { if (IsServer) { string message = WarfareManager.Instance?.SetVaultPermission(pkg.ReadLong(), pkg.ReadLong(), pkg.ReadInt(), pkg.ReadBool()) ?? "Failed"; SendNotification(sender, message); SyncAllDataToClients(); } } public void RequestSetClanTax(int resourceTaxPercent, int coinTaxPercent) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_0006: Expected O, but got Unknown ZPackage val = new ZPackage(); val.Write(LocalId); val.Write(resourceTaxPercent); val.Write(coinTaxPercent); ToServer("FCW_SetClanTax", val); } private void RPC_OnSetClanTax(long sender, ZPackage pkg) { if (IsServer) { string message = WarfareManager.Instance?.SetClanTax(pkg.ReadLong(), pkg.ReadInt(), pkg.ReadInt()) ?? "Failed"; SendNotification(sender, message); SyncAllDataToClients(); } } public void RequestClanResourceTax(string prefabName, string displayName, int taxAmount) { //IL_000d: Unknown result type (might be due to invalid IL or missing references) //IL_0013: Expected O, but got Unknown if (taxAmount > 0 && !string.IsNullOrEmpty(prefabName)) { ZPackage val = new ZPackage(); val.Write(LocalId); val.Write(prefabName); val.Write(displayName ?? prefabName); val.Write(taxAmount); ToServer("FCW_ClanResourceTax", val); } } private void RPC_OnClanResourceTax(long sender, ZPackage pkg) { if (IsServer) { pkg.ReadLong(); string prefabName = pkg.ReadString(); string displayName = pkg.ReadString(); int taxAmount = pkg.ReadInt(); long playerId = ResolveSenderPlayerId(sender); string message = null; WarfareManager instance = WarfareManager.Instance; if (instance != null && instance.TryRecordResourceTax(playerId, prefabName, displayName, taxAmount, out message)) { SyncAllDataToClients(); } else if (!string.IsNullOrEmpty(message)) { SendNotification(sender, "" + message + ""); } } } public void RequestClanCoinTax(int taxAmount) { //IL_0005: Unknown result type (might be due to invalid IL or missing references) //IL_000b: Expected O, but got Unknown if (taxAmount > 0) { ZPackage val = new ZPackage(); val.Write(LocalId); val.Write(taxAmount); ToServer("FCW_ClanCoinTax", val); } } private void RPC_OnClanCoinTax(long sender, ZPackage pkg) { if (IsServer) { pkg.ReadLong(); int baseTaxAmount = pkg.ReadInt(); long playerId = ResolveSenderPlayerId(sender); WarfareManager instance = WarfareManager.Instance; if (instance != null && instance.TryRecordCoinTax(playerId, baseTaxAmount)) { SyncAllDataToClients(); } } } public void RequestDepositVaultCoins(int clanId, int amount) { //IL_0013: Unknown result type (might be due to invalid IL or missing references) //IL_001e: Expected O, but got Unknown //IL_019a: Unknown result type (might be due to invalid IL or missing references) //IL_01f2: Unknown result type (might be due to invalid IL or missing references) //IL_01f9: Expected O, but got Unknown //IL_0215: Unknown result type (might be due to invalid IL or missing references) //IL_021a: Unknown result type (might be due to invalid IL or missing references) //IL_021e: Unknown result type (might be due to invalid IL or missing references) //IL_022c: Unknown result type (might be due to invalid IL or missing references) Player localPlayer = Player.m_localPlayer; Inventory val = ((localPlayer != null) ? ((Humanoid)localPlayer).GetInventory() : null); if ((Object)localPlayer == (Object)null || val == null) { return; } VaultDebug("[FCW Vault] Deposit coins requested: amount=" + amount); CoinStackInfo coinStackInfo = FindCoinStack(val); if (coinStackInfo != null) { VaultDebug("[FCW Vault] Coin entry found: prefab=" + coinStackInfo.Prefab + " name=" + coinStackInfo.Name + " stack=" + coinStackInfo.Stack + " slot=" + SlotText(coinStackInfo.SlotX, coinStackInfo.SlotY)); } if (amount <= 0 || coinStackInfo == null || coinStackInfo.Stack < amount) { SendNotification(GetServerPeerID(), "Your inventory does not have enough coins."); VaultDebug("[FCW Vault] Coin removal result: fail reason=" + ((amount <= 0) ? "invalid amount" : ((coinStackInfo == null) ? "coin stack not detected" : "insufficient stack"))); return; } VaultDebug("[FCW Vault] Coin deposit requested: qty=" + amount); WarfareManager instance = WarfareManager.Instance; PlayerClanInfo playerClanInfo = instance?.Data?.GetPlayer(LocalId); if (playerClanInfo == null || (clanId != 0 && playerClanInfo.ClanId != clanId) || (clanId == 0 && (!playerClanInfo.IsFreeborn || playerClanInfo.ClanId != 0))) { SendNotification(GetServerPeerID(), "That is not your clan's vault."); return; } if (clanId != 0 && (instance == null || !instance.CanDepositVaultCoins(playerClanInfo))) { SendNotification(GetServerPeerID(), "You do not have permission to deposit coins."); return; } string message = ""; if ((Object)(object)instance == (Object)null || !instance.CanUseVaultAt(playerClanInfo, CurrentGrid(), coinDeposit: true, out message)) { SendNotification(GetServerPeerID(), message); return; } if (!instance.IsVaultOpen()) { ConfigEntry allowCoinDepositWhenVaultClosed = ClanConfig.AllowCoinDepositWhenVaultClosed; if (allowCoinDepositWhenVaultClosed == null || !allowCoinDepositWhenVaultClosed.Value) { SendNotification(GetServerPeerID(), "Vault is currently closed."); return; } } VaultDebug("[FCW Vault] DepositCoins waiting for server approval; coins remain in inventory until approval."); ZPackage val2 = new ZPackage(); val2.Write(LocalId); val2.Write(clanId); val2.Write(amount); Vector2i val3 = CurrentGrid(); val2.Write(val3.x); val2.Write(val3.y); val2.Write(coinStackInfo.Stack); ToServer("FCW_DepositVaultCoins", val2); } private void RPC_OnDepositVaultCoins(long sender, ZPackage pkg) { //IL_016f: Unknown result type (might be due to invalid IL or missing references) if (!IsServer) { return; } long num = pkg.ReadLong(); long num2 = ResolveSenderPlayerId(sender); int num3 = pkg.ReadInt(); int num4 = pkg.ReadInt(); Vector2i grid = default(Vector2i); ((Vector2i)(ref grid))..ctor(pkg.ReadInt(), pkg.ReadInt()); int num5 = pkg.ReadInt(); Plugin.Log.LogInfo((object)("[FCW Vault] SERVER received coin deposit request player=" + num + " amount=" + num4)); if (num2 == 0L || num2 != num) { SendNotification(sender, "Vault deposit ownership could not be verified."); return; } WarfareData obj = WarfareManager.Instance?.Data; PlayerClanInfo playerClanInfo = obj?.GetPlayer(num); Clan clan = obj?.GetClan(num3); if (playerClanInfo == null || (num3 != 0 && (clan == null || playerClanInfo.ClanId != num3)) || (num3 == 0 && (!playerClanInfo.IsFreeborn || playerClanInfo.ClanId != 0))) { VaultDebug("[FCW Vault] DepositCoins failed reason=invalid clan membership"); SendNotification(sender, "That is not your clan's vault."); return; } if (num3 != 0) { WarfareManager instance = WarfareManager.Instance; if (instance == null || !instance.CanDepositVaultCoins(playerClanInfo)) { VaultDebug("[FCW Vault] DepositCoins failed reason=permission denied"); SendNotification(sender, "You do not have permission to deposit coins."); return; } } if (num4 <= 0) { VaultDebug("[FCW Vault] DepositCoins failed reason=invalid amount"); SendNotification(sender, "Coin amount must be greater than zero."); return; } if (num5 < num4) { VaultDebug("[FCW Vault] DepositCoins failed reason=not enough coins"); SendNotification(sender, "Your inventory does not have enough coins."); return; } string message = ""; if ((Object)(object)WarfareManager.Instance == (Object)null || !WarfareManager.Instance.CanUseVaultAt(playerClanInfo, grid, coinDeposit: true, out message)) { VaultDebug("[FCW Vault] DepositCoins failed reason=location " + message); SendNotification(sender, message); return; } if (!WarfareManager.Instance.IsVaultOpen()) { ConfigEntry allowCoinDepositWhenVaultClosed = ClanConfig.AllowCoinDepositWhenVaultClosed; if (allowCoinDepositWhenVaultClosed == null || !allowCoinDepositWhenVaultClosed.Value) { VaultDebug("[FCW Vault] DepositCoins failed reason=vault closed"); SendNotification(sender, "Vault is currently closed."); return; } } long num6 = ((num3 == 0) ? playerClanInfo.FreebornTreasury : clan.Treasury); VaultDebug("[FCW Vault] DepositCoins start playerCoins=" + num5 + " amount=" + num4 + " treasuryBefore=" + num6); QueueVaultDeposit(sender, num, num3, isCoin: true, ClanConfig.CoinItemPrefab?.Value ?? "Coins", "Coins", num4, int.MinValue, int.MinValue); } private void QueueVaultDeposit(long peerId, long playerId, int clanId, bool isCoin, string prefab, string displayName, int amount, int slotX, int slotY) { //IL_0074: Unknown result type (might be due to invalid IL or missing references) //IL_007a: Expected O, but got Unknown _pendingVaultDeposits[peerId] = new PendingVaultDeposit { PlayerId = playerId, ClanId = clanId, IsCoin = isCoin, Prefab = (prefab ?? ""), DisplayName = (displayName ?? ""), Amount = amount, SlotX = slotX, SlotY = slotY, CreatedTimestamp = DateTimeOffset.UtcNow.ToUnixTimeSeconds() }; ZPackage val = new ZPackage(); val.Write(isCoin); val.Write(playerId); val.Write(clanId); val.Write(prefab ?? ""); val.Write(displayName ?? ""); val.Write(amount); val.Write(slotX); val.Write(slotY); ZRoutedRpc instance = ZRoutedRpc.instance; if (instance != null) { instance.InvokeRoutedRPC(peerId, "FCW_ApproveVaultDeposit", new object[1] { val }); } } private void RPC_OnApproveVaultDeposit(long sender, ZPackage pkg) { //IL_00ba: Unknown result type (might be due to invalid IL or missing references) //IL_00c5: Expected O, but got Unknown //IL_01f3: Unknown result type (might be due to invalid IL or missing references) //IL_01fa: Expected O, but got Unknown bool flag = pkg.ReadBool(); long num = pkg.ReadLong(); int num2 = pkg.ReadInt(); string text = pkg.ReadString(); string text2 = pkg.ReadString(); int num3 = pkg.ReadInt(); int slotX = pkg.ReadInt(); int slotY = pkg.ReadInt(); if (IsServer && LocalId != num) { return; } Plugin.Log.LogInfo((object)("[FCW Vault] CLIENT " + (flag ? "coin" : "item") + " deposit approved " + (flag ? ("amount=" + num3) : ("item=" + text + " qty=" + num3)))); Player localPlayer = Player.m_localPlayer; Inventory val = ((localPlayer != null) ? ((Humanoid)localPlayer).GetInventory() : null); if ((Object)localPlayer == (Object)null || val == null || localPlayer.GetPlayerID() != num) { VaultDebug("[FCW Vault] Deposit" + (flag ? "Coins" : "Item") + " failed reason=inventory unavailable after server approval"); NotifyVaultClient("Vault deposit cancelled: inventory unavailable."); return; } bool flag2 = false; string reason = ""; if (flag) { CoinStackInfo coinStackInfo = FindCoinStack(val); flag2 = coinStackInfo != null && coinStackInfo.Stack >= num3 && TryRemoveExactInventoryEntry(val, coinStackInfo.Item, num3, out reason); } else { InventoryEntryInfo inventoryEntryInfo = FindInventoryEntry(val, text, slotX, slotY); if (inventoryEntryInfo != null && inventoryEntryInfo.Stack >= num3) { flag2 = TryRemoveExactInventoryEntry(val, inventoryEntryInfo.Item, num3, out reason); if (!flag2) { flag2 = TryRemoveInventoryByPrefab(val, inventoryEntryInfo.Prefab, num3, out reason); } } else { reason = "item no longer in inventory"; } } if (!flag2) { VaultDebug("[FCW Vault] Deposit" + (flag ? "Coins" : "Item") + " failed reason=" + reason); Plugin.Log.LogInfo((object)("[FCW Vault] CLIENT " + (flag ? "coin" : "item") + " deposit denied reason=" + reason)); NotifyVaultClient("Vault deposit cancelled: item is no longer available."); return; } ZPackage val2 = new ZPackage(); val2.Write(num); val2.Write(num2); val2.Write(flag); val2.Write(text ?? ""); val2.Write(text2 ?? ""); val2.Write(num3); ToServer("FCW_ConfirmVaultDeposit", val2); } private void RPC_OnConfirmVaultDeposit(long sender, ZPackage pkg) { if (!IsServer) { return; } long num = pkg.ReadLong(); long num2 = ResolveSenderPlayerId(sender); int num3 = pkg.ReadInt(); bool flag = pkg.ReadBool(); string text = pkg.ReadString(); string text2 = pkg.ReadString(); int num4 = pkg.ReadInt(); Plugin.Log.LogInfo((object)("[FCW Vault] SERVER received " + (flag ? "coin" : "item") + " deposit confirmation player=" + num + " " + (flag ? ("amount=" + num4) : ("item=" + text + " qty=" + num4)))); if (num2 == 0L || num2 != num || !_pendingVaultDeposits.TryGetValue(sender, out var value) || value.PlayerId != num || value.ClanId != num3 || value.IsCoin != flag || value.Amount != num4 || !string.Equals(value.Prefab, text ?? "", StringComparison.OrdinalIgnoreCase)) { VaultDebug("[FCW Vault] Deposit" + (flag ? "Coins" : "Item") + " failed reason=no matching server approval"); SendNotification(sender, "Vault deposit approval expired."); return; } _pendingVaultDeposits.Remove(sender); if (DateTimeOffset.UtcNow.ToUnixTimeSeconds() - value.CreatedTimestamp > 20) { VaultDebug("[FCW Vault] Deposit" + (flag ? "Coins" : "Item") + " failed reason=approval expired"); SendNotification(sender, "Vault deposit approval expired."); return; } WarfareManager instance = WarfareManager.Instance; WarfareData warfareData = instance?.Data; PlayerClanInfo playerClanInfo = warfareData?.GetPlayer(num); Clan clan = warfareData?.GetClan(num3); if ((Object)(object)instance == (Object)null || warfareData == null || playerClanInfo == null || (num3 != 0 && (clan == null || playerClanInfo.ClanId != num3)) || (num3 == 0 && (!playerClanInfo.IsFreeborn || playerClanInfo.ClanId != 0))) { VaultDebug("[FCW Vault] Deposit" + (flag ? "Coins" : "Item") + " failed reason=state changed after approval"); SendNotification(sender, "Vault deposit cancelled."); return; } if (flag) { long balanceBefore = ((num3 == 0) ? playerClanInfo.FreebornTreasury : clan.Treasury); if (num3 == 0) { playerClanInfo.FreebornTreasury += num4; } else { clan.Treasury += num4; Economy.RecordTreasuryTransaction(clan, DateTimeOffset.UtcNow.ToUnixTimeSeconds(), "Manual Deposit", num4, balanceBefore, clan.Treasury, playerClanInfo.PlayerName); playerClanInfo.CoinsDonatedToTreasury += num4; if (num4 > playerClanInfo.HighestDonation) { playerClanInfo.HighestDonation = num4; } instance.AddDonationMilestones(playerClanInfo); } VaultDebug("[FCW Vault] DepositCoins success treasuryAfter=" + ((num3 == 0) ? playerClanInfo.FreebornTreasury : clan.Treasury) + " treasuryBefore=" + balanceBefore); SendNotification(sender, "Coins deposited."); } else { playerClanInfo.ItemsDonatedToVault += num4; if (num3 == 0) { warfareData.AddToFreebornVault(num, text, CleanItemDisplayName(string.IsNullOrEmpty(text2) ? text : text2), num4); } else { warfareData.AddToVault(num3, text, CleanItemDisplayName(string.IsNullOrEmpty(text2) ? text : text2), num4); } SendNotification(sender, "Item deposited into clan vault."); } instance.MarkDirty(); instance.SaveData(); SyncAllDataToClients(); } public void RequestWithdrawVaultCoins(int clanId, int amount) { //IL_0035: Unknown result type (might be due to invalid IL or missing references) //IL_003b: Expected O, but got Unknown //IL_0054: Unknown result type (might be due to invalid IL or missing references) //IL_0059: Unknown result type (might be due to invalid IL or missing references) //IL_005b: Unknown result type (might be due to invalid IL or missing references) //IL_0067: Unknown result type (might be due to invalid IL or missing references) VaultDebug("[FCW Vault] withdraw request item=" + (ClanConfig.CoinItemPrefab?.Value ?? "Coins") + " amount=" + amount); ZPackage val = new ZPackage(); val.Write(LocalId); val.Write(clanId); val.Write(amount); Vector2i val2 = CurrentGrid(); val.Write(val2.x); val.Write(val2.y); ToServer("FCW_WithdrawVaultCoins", val); } private void RPC_OnWithdrawVaultCoins(long sender, ZPackage pkg) { //IL_011c: Unknown result type (might be due to invalid IL or missing references) //IL_0176: Unknown result type (might be due to invalid IL or missing references) //IL_017d: Expected O, but got Unknown if (!IsServer) { return; } long playerId = pkg.ReadLong(); int num = pkg.ReadInt(); int num2 = pkg.ReadInt(); Vector2i grid = default(Vector2i); ((Vector2i)(ref grid))..ctor(pkg.ReadInt(), pkg.ReadInt()); WarfareData obj = WarfareManager.Instance?.Data; PlayerClanInfo playerClanInfo = obj?.GetPlayer(playerId); Clan clan = obj?.GetClan(num); if (playerClanInfo == null || (num != 0 && (clan == null || playerClanInfo.ClanId != num)) || (num == 0 && (!playerClanInfo.IsFreeborn || playerClanInfo.ClanId != 0))) { SendNotification(sender, "That is not your clan's vault."); return; } if (num != 0) { WarfareManager instance = WarfareManager.Instance; if (instance == null || !instance.CanWithdrawVaultCoins(playerClanInfo)) { SendNotification(sender, "You do not have permission to withdraw coins."); return; } } long num3 = ((num == 0) ? playerClanInfo.FreebornTreasury : clan.Treasury); if (num2 <= 0 || num3 < num2) { SendNotification(sender, "Treasury does not have enough coins."); VaultDebug("[FCW Vault] withdraw result=fail reason=not enough treasury coins amount=" + num2); return; } string message = ""; if ((Object)(object)WarfareManager.Instance == (Object)null || !WarfareManager.Instance.CanUseVaultAt(playerClanInfo, grid, coinDeposit: false, out message)) { SendNotification(sender, message); VaultDebug("[FCW Vault] withdraw result=fail reason=location blocked amount=" + num2); return; } if (!WarfareManager.Instance.IsVaultOpen()) { SendNotification(sender, "Vault is currently closed."); VaultDebug("[FCW Vault] withdraw result=fail reason=vault closed amount=" + num2); return; } ZPackage val = new ZPackage(); val.Write(false); val.Write(num); val.Write(ClanConfig.CoinItemPrefab?.Value ?? "Coins"); val.Write(num2); ZRoutedRpc instance2 = ZRoutedRpc.instance; if (instance2 != null) { instance2.InvokeRoutedRPC(sender, "FCW_AddToVault", new object[1] { val }); } } private void RPC_OnConfirmVaultCoinsWithdraw(long sender, ZPackage pkg) { if (!IsServer) { return; } long playerId = pkg.ReadLong(); int num = pkg.ReadInt(); int num2 = pkg.ReadInt(); WarfareData obj = WarfareManager.Instance?.Data; PlayerClanInfo playerClanInfo = obj?.GetPlayer(playerId); Clan clan = obj?.GetClan(num); if (playerClanInfo == null || (num != 0 && (clan == null || playerClanInfo.ClanId != num)) || (num == 0 && (!playerClanInfo.IsFreeborn || playerClanInfo.ClanId != 0))) { SendNotification(sender, "That is not your clan's vault."); return; } if (num != 0) { WarfareManager instance = WarfareManager.Instance; if (instance == null || !instance.CanWithdrawVaultCoins(playerClanInfo)) { SendNotification(sender, "You do not have permission to withdraw coins."); return; } } long num3 = ((num == 0) ? playerClanInfo.FreebornTreasury : clan.Treasury); if (num2 <= 0 || num3 < num2) { SendNotification(sender, "Treasury does not have enough coins."); return; } if (num == 0) { playerClanInfo.FreebornTreasury -= num2; } else { long treasury = clan.Treasury; clan.Treasury -= num2; Economy.RecordTreasuryTransaction(clan, DateTimeOffset.UtcNow.ToUnixTimeSeconds(), "Manual Withdrawal", -num2, treasury, clan.Treasury, playerClanInfo.PlayerName); } WarfareManager.Instance.MarkDirty(); SendNotification(sender, "Coins withdrawn."); VaultDebug("[FCW Vault] withdraw result=success item=" + (ClanConfig.CoinItemPrefab?.Value ?? "Coins") + " amount=" + num2); SyncAllDataToClients(); } private void RPC_OnAddToVault(long sender, ZPackage pkg) { //IL_02e2: Unknown result type (might be due to invalid IL or missing references) //IL_02ed: Expected O, but got Unknown //IL_0300: Unknown result type (might be due to invalid IL or missing references) //IL_030b: Expected O, but got Unknown //IL_014a: Unknown result type (might be due to invalid IL or missing references) //IL_041e: Unknown result type (might be due to invalid IL or missing references) //IL_0425: Expected O, but got Unknown bool flag = pkg.ReadBool(); if (flag && IsServer) { long num = pkg.ReadLong(); int num2 = pkg.ReadInt(); string prefab = pkg.ReadString(); string text = pkg.ReadString(); int amount = pkg.ReadInt(); Vector2i grid = default(Vector2i); ((Vector2i)(ref grid))..ctor(pkg.ReadInt(), pkg.ReadInt()); bool flag2 = pkg.ReadBool(); int slotX = pkg.ReadInt(); int slotY = pkg.ReadInt(); Plugin.Log.LogInfo((object)("[FCW Vault] SERVER received item deposit request player=" + num + " item=" + prefab + " qty=" + amount)); long num3 = ResolveSenderPlayerId(sender); if (num3 == 0L || num3 != num) { SendNotification(sender, "Vault deposit ownership could not be verified."); return; } WarfareManager instance = WarfareManager.Instance; WarfareData warfareData = instance?.Data; if (warfareData == null) { return; } PlayerClanInfo player = warfareData.GetPlayer(num); if (player == null || (num2 != 0 && player.ClanId != num2) || (num2 == 0 && (!player.IsFreeborn || player.ClanId != 0))) { SendNotification(sender, "That is not your clan's vault."); return; } string message = ""; if ((Object)(object)instance == (Object)null || !instance.CanUseVaultAt(player, grid, coinDeposit: false, out message)) { SendNotification(sender, message); return; } if (num2 != 0 && (instance == null || !instance.CanDepositVaultItems(player))) { SendNotification(sender, "You do not have permission to deposit into the vault."); return; } if ((Object)(object)instance != (Object)null && !instance.IsVaultOpen()) { VaultDebug("[FCW Vault] DepositItem failed reason=vault closed"); SendNotification(sender, "Vault is currently closed."); return; } Clan clan = warfareData.GetClan(num2); int num4 = ((num2 == 0) ? instance.FreebornVaultCapacity(player) : Economy.VaultCapacity(clan, warfareData.GetClanTerritoryCount(num2))); int num5 = ((num2 == 0) ? warfareData.GetFreebornVaultStackCount(num) : warfareData.GetVaultStackCount(num2)); bool flag3 = ((num2 == 0) ? warfareData.GetFreebornVault(num) : warfareData.GetVault(num2)).Find((ClanVaultItem v) => v.PrefabName == prefab) != null; if (num5 < num4 || flag3) { if (flag2) { QueueVaultDeposit(sender, num, num2, isCoin: false, prefab, text, amount, slotX, slotY); return; } if (num2 == 0) { warfareData.AddToFreebornVault(num, prefab, CleanItemDisplayName(string.IsNullOrEmpty(text) ? prefab : text), amount); } else { warfareData.AddToVault(num2, prefab, CleanItemDisplayName(string.IsNullOrEmpty(text) ? prefab : text), amount); } instance.MarkDirty(); SendNotification(sender, "Item deposited into clan vault."); SyncAllDataToClients(); } else { SendNotification(sender, "Vault is full."); } } else { if (flag) { return; } int num6 = pkg.ReadInt(); string text2 = pkg.ReadString(); int num7 = pkg.ReadInt(); Player localPlayer = Player.m_localPlayer; if ((Object)localPlayer == (Object)null) { return; } GameObject itemPrefabSafe = GetItemPrefabSafe(ObjectDB.instance, text2); if ((Object)itemPrefabSafe != (Object)null) { Inventory inventory = ((Humanoid)localPlayer).GetInventory(); if (inventory == null) { return; } if (!CanInventoryAcceptItem(inventory, itemPrefabSafe, num7, out var reason)) { SendNotification(GetServerPeerID(), "Inventory full."); VaultDebug("[FCW Vault] withdraw result=fail reason=" + reason + " item=" + text2 + " amount=" + num7); return; } SuppressTax = true; object obj; try { obj = ((object)inventory).GetType().GetMethod("AddItem", new Type[2] { typeof(GameObject), typeof(int) })?.Invoke(inventory, new object[2] { itemPrefabSafe, num7 }); } finally { SuppressTax = false; } if (obj != null && (!(obj is int num8) || num8 != 0)) { VaultDebug("[FCW Vault] withdraw result=success item=" + text2 + " amount=" + num7); ZPackage val = new ZPackage(); val.Write(LocalId); val.Write(num6); if (text2 == (ClanConfig.CoinItemPrefab?.Value ?? "Coins")) { val.Write(num7); ToServer("FCW_ConfirmVaultCoinsWithdraw", val); } else { val.Write(text2); val.Write(num7); ToServer("FCW_ConfirmVaultWithdraw", val); } } else { SendNotification(GetServerPeerID(), "Inventory full."); VaultDebug("[FCW Vault] withdraw result=fail reason=AddItem returned false item=" + text2 + " amount=" + num7); } } else { SendNotification(GetServerPeerID(), "Item prefab not found: " + text2 + ""); VaultDebug("[FCW Vault] withdraw result=fail reason=prefab not found item=" + text2 + " amount=" + num7); } } } private static bool CanInventoryAcceptItem(Inventory inventory, GameObject prefab, int amount, out string reason) { //IL_000b: Unknown result type (might be due to invalid IL or missing references) //IL_0016: Expected O, but got Unknown reason = ""; if (inventory == null || (Object)prefab == (Object)null || amount <= 0) { reason = "invalid inventory add check"; return false; } MethodInfo method = ((object)inventory).GetType().GetMethod("CanAddItem", new Type[2] { typeof(GameObject), typeof(int) }); if (method != null) { try { object obj = method.Invoke(inventory, new object[2] { prefab, amount }); if (obj is bool && !(bool)obj) { reason = "CanAddItem returned false"; return false; } } catch (Exception ex) { reason = "CanAddItem failed: " + ex.GetBaseException().Message; return true; } } reason = "ok"; return true; } private static GameObject GetItemPrefabSafe(ObjectDB objectDb, string prefab) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) //IL_000c: Expected O, but got Unknown //IL_0021: Unknown result type (might be due to invalid IL or missing references) //IL_002c: Expected O, but got Unknown //IL_0059: Unknown result type (might be due to invalid IL or missing references) //IL_0064: Expected O, but got Unknown if ((Object)objectDb == (Object)null || string.IsNullOrEmpty(prefab)) { return null; } GameObject itemPrefab = objectDb.GetItemPrefab(prefab); if ((Object)itemPrefab != (Object)null) { return itemPrefab; } string text = prefab.Replace("(Clone)", "").Trim(); if (!string.Equals(text, prefab, StringComparison.OrdinalIgnoreCase)) { itemPrefab = objectDb.GetItemPrefab(text); if ((Object)itemPrefab != (Object)null) { return itemPrefab; } } text = text.Replace(" ", "_"); return objectDb.GetItemPrefab(text); } public void SendItemToClanVault(int clanId, string prefab, string display, int amount, bool manualInventoryDeposit = false, int slotX = int.MinValue, int slotY = int.MinValue) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_0006: Expected O, but got Unknown //IL_0047: Unknown result type (might be due to invalid IL or missing references) //IL_004c: Unknown result type (might be due to invalid IL or missing references) //IL_004e: Unknown result type (might be due to invalid IL or missing references) //IL_005a: Unknown result type (might be due to invalid IL or missing references) ZPackage val = new ZPackage(); val.Write(true); val.Write(LocalId); val.Write(clanId); val.Write(prefab ?? ""); val.Write(display ?? ""); val.Write(amount); Vector2i val2 = CurrentGrid(); val.Write(val2.x); val.Write(val2.y); val.Write(manualInventoryDeposit); val.Write(slotX); val.Write(slotY); ToServer("FCW_AddToVault", val); } public void DepositSpecificInventoryItemToClanVault(int clanId, string prefab, string display, int amount, int slotX = int.MinValue, int slotY = int.MinValue) { //IL_0020: Unknown result type (might be due to invalid IL or missing references) //IL_002b: Expected O, but got Unknown //IL_016c: Unknown result type (might be due to invalid IL or missing references) Player localPlayer = Player.m_localPlayer; Inventory val = ((localPlayer != null) ? ((Humanoid)localPlayer).GetInventory() : null); if ((Object)localPlayer == (Object)null || val == null) { return; } if (string.IsNullOrEmpty(prefab) || amount <= 0) { SendNotification(GetServerPeerID(), "Select an item first."); VaultDebug("[FCW Vault] Remove item result: fail reason=invalid selection prefab=" + prefab + " qty=" + amount + " slot=" + SlotText(slotX, slotY)); return; } VaultDebug("[FCW Vault] Selected deposit: prefab=" + prefab + " qty=" + amount + " slot=" + SlotText(slotX, slotY)); WarfareManager instance = WarfareManager.Instance; PlayerClanInfo playerClanInfo = instance?.Data?.GetPlayer(LocalId); Clan clan = instance?.Data?.GetClan(clanId); if (playerClanInfo == null || (clanId != 0 && (playerClanInfo.ClanId != clanId || clan == null)) || (clanId == 0 && (!playerClanInfo.IsFreeborn || playerClanInfo.ClanId != 0))) { SendNotification(GetServerPeerID(), "That is not your clan's vault."); return; } string message = ""; if ((Object)(object)instance == (Object)null || !instance.CanUseVaultAt(playerClanInfo, CurrentGrid(), coinDeposit: false, out message)) { SendNotification(GetServerPeerID(), message); return; } if (!instance.IsVaultOpen()) { SendNotification(GetServerPeerID(), "Vault is currently closed."); return; } if (clanId != 0 && !instance.CanDepositVaultItems(playerClanInfo)) { SendNotification(GetServerPeerID(), "You do not have permission to deposit into the vault."); return; } int num = ((clanId == 0) ? instance.FreebornVaultCapacity(playerClanInfo) : Economy.VaultCapacity(clan, instance.Data.GetClanTerritoryCount(clanId))); int num2 = ((clanId == 0) ? instance.Data.GetFreebornVaultStackCount(playerClanInfo.PlayerId) : instance.Data.GetVaultStackCount(clanId)); bool flag = ((clanId == 0) ? instance.Data.GetFreebornVault(playerClanInfo.PlayerId) : instance.Data.GetVault(clanId)).Find((ClanVaultItem v) => v.PrefabName == prefab) != null; if (num2 >= num && !flag) { SendNotification(GetServerPeerID(), "Vault is full."); return; } InventoryEntryInfo inventoryEntryInfo = FindInventoryEntry(val, prefab, slotX, slotY); if (inventoryEntryInfo == null) { SendNotification(GetServerPeerID(), "Item is no longer in inventory."); VaultDebug("[FCW Vault] Remove item result: fail reason=selected item disappeared prefab=" + prefab + " qty=" + amount + " slot=" + SlotText(slotX, slotY)); } else if (inventoryEntryInfo.Stack < amount) { SendNotification(GetServerPeerID(), "Item is no longer in inventory."); VaultDebug("[FCW Vault] Remove item result: fail reason=insufficient stack prefab=" + prefab + " stack=" + inventoryEntryInfo.Stack + " qty=" + amount + " slot=" + SlotText(slotX, slotY)); } else { VaultDebug("[FCW Vault] DepositItem waiting for server approval; item remains in inventory until approval."); SendItemToClanVault(clanId, inventoryEntryInfo.Prefab, CleanItemDisplayName((!string.IsNullOrEmpty(inventoryEntryInfo.DisplayName)) ? inventoryEntryInfo.DisplayName : (string.IsNullOrEmpty(display) ? prefab : display)), amount, manualInventoryDeposit: true, inventoryEntryInfo.SlotX, inventoryEntryInfo.SlotY); } } public void DepositInventoryToClanVault(int clanId, bool wholeStack) { //IL_000d: Unknown result type (might be due to invalid IL or missing references) //IL_0018: Expected O, but got Unknown //IL_00c2: Unknown result type (might be due to invalid IL or missing references) //IL_018f: Unknown result type (might be due to invalid IL or missing references) //IL_019a: Expected O, but got Unknown Player localPlayer = Player.m_localPlayer; if ((Object)localPlayer == (Object)null) { return; } Inventory inventory = ((Humanoid)localPlayer).GetInventory(); if (inventory == null) { return; } WarfareManager instance = WarfareManager.Instance; PlayerClanInfo playerClanInfo = instance?.Data?.GetPlayer(LocalId); Clan clan = instance?.Data?.GetClan(clanId); if (playerClanInfo == null || playerClanInfo.ClanId != clanId || clan == null) { SendNotification(GetServerPeerID(), "That is not your clan's vault."); return; } if (instance == null || !instance.CanDepositVaultItems(playerClanInfo)) { SendNotification(GetServerPeerID(), "You do not have permission to deposit into the vault."); return; } string message = ""; if ((Object)(object)instance == (Object)null || !instance.CanUseVaultAt(playerClanInfo, CurrentGrid(), coinDeposit: false, out message)) { SendNotification(GetServerPeerID(), message); return; } if ((Object)(object)instance != (Object)null && !instance.IsVaultOpen()) { SendNotification(GetServerPeerID(), "Vault is currently closed."); return; } object selectedInventoryItem = GetSelectedInventoryItem(); if (selectedInventoryItem == null) { SendNotification(GetServerPeerID(), "Select an item first."); return; } Type type = selectedInventoryItem.GetType(); int num = (int)(type.GetField("m_stack")?.GetValue(selectedInventoryItem) ?? ((object)0)); if (num <= 0) { SendNotification(GetServerPeerID(), "Select an item first."); return; } object? obj = type.GetField("m_dropPrefab")?.GetValue(selectedInventoryItem); GameObject val = (GameObject)((obj is GameObject) ? obj : null); string text = (((Object)val != (Object)null) ? ((Object)val).name : null); object obj2 = type.GetField("m_shared")?.GetValue(selectedInventoryItem); string text2 = obj2?.GetType().GetField("m_name")?.GetValue(obj2) as string; if (string.IsNullOrEmpty(text)) { SendNotification(GetServerPeerID(), "Select an item first."); return; } int num2 = Economy.VaultCapacity(clan, instance.Data.GetClanTerritoryCount(clanId)); if (instance.Data.GetVaultStackCount(clanId) >= num2 && instance.Data.GetVault(clanId).Find((ClanVaultItem v) => v.PrefabName == text) == null) { SendNotification(GetServerPeerID(), "Vault is full."); return; } int num3 = ((!wholeStack) ? 1 : num); if (inventory.CountItems(text, -1, true) < num3) { SendNotification(GetServerPeerID(), "Item is no longer in inventory."); return; } VaultDebug("[FCW Vault] DepositItem waiting for server approval; item remains in inventory until approval."); SendItemToClanVault(clanId, text, CleanItemDisplayName(string.IsNullOrEmpty(text2) ? text : text2), num3, manualInventoryDeposit: true); } private static object GetSelectedInventoryItem() { object instance = InventoryGui.instance; if (instance == null) { return null; } Type type = instance.GetType(); string[] array = new string[5] { "m_selectedItem", "m_dragItem", "m_selectedItemData", "m_currentSelectedItem", "m_selected" }; foreach (string name in array) { object obj = type.GetField(name, BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic)?.GetValue(instance); if (obj != null) { return obj; } } return type.GetProperty("SelectedItem", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic)?.GetValue(instance, null); } private static Vector2i CurrentGrid() { //IL_0005: Unknown result type (might be due to invalid IL or missing references) //IL_0010: Expected O, but got Unknown //IL_0024: Unknown result type (might be due to invalid IL or missing references) //IL_0029: Unknown result type (might be due to invalid IL or missing references) //IL_0014: Unknown result type (might be due to invalid IL or missing references) if ((Object)Player.m_localPlayer == (Object)null) { return new Vector2i(0, 0); } return GridExtensions.WorldToGrid(((Component)Player.m_localPlayer).transform.position); } private static List ScanInventoryEntries(Inventory inventory) { //IL_00a6: Unknown result type (might be due to invalid IL or missing references) //IL_00b1: Expected O, but got Unknown List list = new List(); if (inventory == null) { return list; } if (!(((object)inventory).GetType().GetMethod("GetAllItems", Type.EmptyTypes)?.Invoke(inventory, null) is IEnumerable enumerable)) { return list; } foreach (object item in enumerable) { if (item != null) { Type type = item.GetType(); int num = (int)(type.GetField("m_stack", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic)?.GetValue(item) ?? ((object)0)); object? obj = type.GetField("m_dropPrefab", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic)?.GetValue(item); GameObject val = (GameObject)((obj is GameObject) ? obj : null); string text = (((Object)val != (Object)null) ? ((Object)val).name : ""); object obj2 = type.GetField("m_shared", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic)?.GetValue(item); string text2 = obj2?.GetType().GetField("m_name", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic)?.GetValue(obj2) as string; if (num > 0 && !string.IsNullOrEmpty(text)) { GetInventorySlot(item, out var x, out var y); string text3 = CleanItemDisplayName(string.IsNullOrEmpty(text2) ? text : text2); VaultDebug("[FCW Vault] Inventory entry: prefab=" + text + " name=" + text3 + " stack=" + num + " slot=" + SlotText(x, y)); list.Add(new InventoryEntryInfo { Item = item, Prefab = text, SharedName = text2, DisplayName = text3, Stack = num, SlotX = x, SlotY = y }); } } } return list; } private static InventoryEntryInfo FindInventoryEntry(Inventory inventory, string prefab, int slotX, int slotY) { foreach (InventoryEntryInfo item in ScanInventoryEntries(inventory)) { if (string.Equals(item.Prefab, prefab, StringComparison.OrdinalIgnoreCase) && (slotX == int.MinValue || slotY == int.MinValue || (item.SlotX == slotX && item.SlotY == slotY))) { return item; } } return null; } private static bool TryRemoveExactInventoryEntry(Inventory inventory, object item, int amount, out string reason) { reason = ""; if (inventory == null || item == null) { reason = "inventory or item missing"; return false; } if (amount <= 0) { reason = "invalid amount"; return false; } Type type = item.GetType(); int itemStack = GetItemStack(item); if (itemStack < amount) { reason = "insufficient stack before remove"; return false; } MethodInfo methodInfo = null; MethodInfo[] methods = ((object)inventory).GetType().GetMethods(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); foreach (MethodInfo methodInfo2 in methods) { if (!(methodInfo2.Name != "RemoveItem")) { ParameterInfo[] parameters = methodInfo2.GetParameters(); if (parameters.Length == 2 && parameters[1].ParameterType == typeof(int) && parameters[0].ParameterType.IsAssignableFrom(type)) { methodInfo = methodInfo2; break; } } } if (methodInfo == null) { reason = "exact RemoveItem(item, amount) overload not found"; return false; } try { methodInfo.Invoke(inventory, new object[2] { item, amount }); int itemStack2 = GetItemStack(item); bool flag = InventoryContainsReference(inventory, item); if (itemStack2 > itemStack - amount && flag) { reason = "stack did not decrease before=" + itemStack + " after=" + itemStack2; return false; } reason = "success"; return true; } catch (Exception ex) { reason = ex.GetBaseException().Message; return false; } } private static bool TryRemoveInventoryByPrefab(Inventory inventory, string prefab, int amount, out string reason) { reason = ""; if (inventory == null || string.IsNullOrEmpty(prefab) || amount <= 0) { reason = "invalid fallback arguments"; return false; } int num = inventory.CountItems(prefab, -1, true); if (num < amount) { reason = "insufficient prefab count before=" + num; return false; } inventory.RemoveItem(prefab, amount, -1, true); int num2 = inventory.CountItems(prefab, -1, true); if (num2 > num - amount) { reason = "prefab count did not decrease before=" + num + " after=" + num2; return false; } reason = "success"; return true; } private static bool InventoryContainsReference(Inventory inventory, object item) { if (inventory == null || item == null) { return false; } if (!(((object)inventory).GetType().GetMethod("GetAllItems", Type.EmptyTypes)?.Invoke(inventory, null) is IEnumerable enumerable)) { return false; } foreach (object item2 in enumerable) { if (item2 == item) { return true; } } return false; } private static int GetItemStack(object item) { return (int)(item?.GetType().GetField("m_stack", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic)?.GetValue(item) ?? ((object)0)); } private static void GetInventorySlot(object item, out int x, out int y) { x = int.MinValue; y = int.MinValue; object obj = item?.GetType().GetField("m_gridPos", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic)?.GetValue(item); if (obj != null) { Type type = obj.GetType(); object obj2 = type.GetField("x", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic)?.GetValue(obj) ?? type.GetProperty("x", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic)?.GetValue(obj, null); object obj3 = type.GetField("y", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic)?.GetValue(obj) ?? type.GetProperty("y", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic)?.GetValue(obj, null); if (obj2 is int) { x = (int)obj2; } if (obj3 is int) { y = (int)obj3; } } } private static string SlotText(int x, int y) { if (x != int.MinValue && y != int.MinValue) { return x + "," + y; } return "unknown"; } private static void VaultDebug(string message) { ConfigEntry enableVaultDebugLogs = ClanConfig.EnableVaultDebugLogs; if (enableVaultDebugLogs != null && enableVaultDebugLogs.Value) { Plugin.Log.LogInfo((object)message); } } private void NotifyVaultClient(string message) { Plugin.Log.LogInfo((object)("[FCW Vault] CLIENT result " + message)); this.OnNotificationReceived?.Invoke(message); } private static CoinStackInfo FindCoinStack(Inventory inventory) { if (inventory == null) { return null; } string b = (ClanConfig.CoinItemPrefab?.Value ?? "Coins").Trim(); CoinStackInfo coinStackInfo = null; foreach (InventoryEntryInfo item in ScanInventoryEntries(inventory)) { bool flag = string.Equals(item.Prefab, b, StringComparison.OrdinalIgnoreCase) || string.Equals(item.DisplayName, b, StringComparison.OrdinalIgnoreCase); bool flag2 = item.DisplayName.IndexOf("coin", StringComparison.OrdinalIgnoreCase) >= 0 || item.Prefab.IndexOf("coin", StringComparison.OrdinalIgnoreCase) >= 0; if (flag || flag2) { CoinStackInfo coinStackInfo2 = new CoinStackInfo { Item = item.Item, Prefab = item.Prefab, Name = item.DisplayName, Stack = item.Stack, SlotX = item.SlotX, SlotY = item.SlotY }; if (flag) { return coinStackInfo2; } if (coinStackInfo == null) { coinStackInfo = coinStackInfo2; } } } return coinStackInfo; } public static string CleanItemDisplayName(string raw) { raw = (raw ?? "").Trim(); if (raw.StartsWith("$")) { raw = raw.TrimStart(new char[1] { '$' }); if (raw.StartsWith("item_")) { raw = raw.Substring(5); } } raw = raw.Replace("_", " "); if (raw.Length == 0) { return "Item"; } return char.ToUpperInvariant(raw[0]) + raw.Substring(1); } private static bool HasVaultRank(PlayerClanInfo player, string minRank) { if (player == null) { return false; } if (player.Rank == ClanRank.Jarl) { return true; } ClanRank clanRank = ParseRank(minRank, ClanRank.Member); return player.Rank >= clanRank; } private static ClanRank ParseRank(string rank, ClanRank fallback) { try { return (ClanRank)Enum.Parse(typeof(ClanRank), rank ?? fallback.ToString(), ignoreCase: true); } catch { return fallback; } } public void RequestStartVaultCrack(Vector2i grid) { //IL_0007: Unknown result type (might be due to invalid IL or missing references) //IL_0012: Expected O, but got Unknown //IL_0014: Unknown result type (might be due to invalid IL or missing references) //IL_001a: Expected O, but got Unknown //IL_0031: Unknown result type (might be due to invalid IL or missing references) //IL_003d: Unknown result type (might be due to invalid IL or missing references) //IL_004f: Unknown result type (might be due to invalid IL or missing references) Player localPlayer = Player.m_localPlayer; if (!((Object)localPlayer == (Object)null)) { ZPackage val = new ZPackage(); val.Write(LocalId); val.Write(LocalName); val.Write(grid.x); val.Write(grid.y); val.Write(((Component)localPlayer).transform.position); ToServer("FCW_StartVaultCrack", val); } } private void RPC_OnStartVaultCrack(long sender, ZPackage pkg) { //IL_0024: Unknown result type (might be due to invalid IL or missing references) //IL_0029: Unknown result type (might be due to invalid IL or missing references) //IL_003b: Unknown result type (might be due to invalid IL or missing references) //IL_0040: Unknown result type (might be due to invalid IL or missing references) if (IsServer) { long playerId = pkg.ReadLong(); string playerName = pkg.ReadString(); int num = pkg.ReadInt(); int num2 = pkg.ReadInt(); Vector3 pos = pkg.ReadVector3(); string message = WarfareManager.Instance?.StartVaultCrack(playerId, playerName, new Vector2i(num, num2), pos) ?? "Failed"; SendNotification(sender, message); } } public void RequestCancelVaultCrack() { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_0006: Expected O, but got Unknown ZPackage val = new ZPackage(); val.Write(LocalId); ToServer("FCW_CancelVaultCrack", val); } private void RPC_OnCancelVaultCrack(long sender, ZPackage pkg) { if (IsServer) { long playerId = pkg.ReadLong(); WarfareManager.Instance?.Data?.ActiveCracks.RemoveAll((VaultCrackSession c) => c.PlayerId == playerId); SendNotification(sender, "Vault crack cancelled."); } } public void RequestDeclareWar(int targetClanId, WarType type) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_0006: Expected O, but got Unknown ZPackage val = new ZPackage(); val.Write(LocalId); val.Write(targetClanId); val.Write((int)type); ToServer("FCW_DeclareWar", val); } private void RPC_OnDeclareWar(long sender, ZPackage pkg) { if (IsServer) { string message = WarfareManager.Instance?.DeclareWar(pkg.ReadLong(), pkg.ReadInt(), (WarType)pkg.ReadInt()) ?? "Failed"; SendNotification(sender, message); SyncAllDataToClients(); } } public void RequestPlaceRotTotem() { //IL_0007: Unknown result type (might be due to invalid IL or missing references) //IL_0012: Expected O, but got Unknown //IL_0014: Unknown result type (might be due to invalid IL or missing references) //IL_001a: Expected O, but got Unknown //IL_0037: Unknown result type (might be due to invalid IL or missing references) Player localPlayer = Player.m_localPlayer; if (!((Object)localPlayer == (Object)null)) { ZPackage val = new ZPackage(); val.Write(LocalId); val.Write(LocalName); val.Write(((Component)localPlayer).transform.position); ToServer("FCW_PlaceRotTotem", val); } } private void RPC_OnPlaceRotTotem(long sender, ZPackage pkg) { //IL_0016: 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_002a: Unknown result type (might be due to invalid IL or missing references) if (IsServer) { long playerId = pkg.ReadLong(); string playerName = pkg.ReadString(); Vector3 worldPos = pkg.ReadVector3(); string message = WarfareManager.Instance?.PlaceRotTotem(playerId, playerName, worldPos) ?? "Failed"; SendNotification(sender, message); SyncAllDataToClients(); } } public void RequestPlaceBounty(long targetPlayerId, int targetClanId, long amount) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_0006: Expected O, but got Unknown ZPackage val = new ZPackage(); val.Write(LocalId); val.Write(targetPlayerId); val.Write(targetClanId); val.Write(amount); ToServer("FCW_PlaceBounty", val); } private void RPC_OnPlaceBounty(long sender, ZPackage pkg) { if (IsServer) { string message = WarfareManager.Instance?.PlaceBounty(pkg.ReadLong(), pkg.ReadLong(), pkg.ReadInt(), pkg.ReadLong()) ?? "Failed"; SendNotification(sender, message); SyncAllDataToClients(); } } public void RequestDiplomacy(int targetClanId, int action) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_0006: Expected O, but got Unknown ZPackage val = new ZPackage(); val.Write(LocalId); val.Write(targetClanId); val.Write(action); ToServer("FCW_Diplomacy", val); } public void RequestFreebornFriend(long targetPlayerId, int action) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_0006: Expected O, but got Unknown ZPackage val = new ZPackage(); val.Write(LocalId); val.Write(targetPlayerId); val.Write(action); ToServer("FCW_FreebornFriend", val); } private void RPC_OnDiplomacy(long sender, ZPackage pkg) { if (IsServer) { long playerId = pkg.ReadLong(); int num = pkg.ReadInt(); int num2 = pkg.ReadInt(); WarfareManager instance = WarfareManager.Instance; SendNotification(sender, num2 switch { 1 => instance?.SendAllianceRequest(playerId, num) ?? "Failed", 2 => instance?.AcceptAlliance(playerId, num) ?? "Failed", 3 => instance?.BreakAlliance(playerId, num) ?? "Failed", 4 => instance?.DeclareHostile(playerId, num) ?? "Failed", 5 => instance?.CycleAllianceColor(playerId, num) ?? "Failed", _ => "Unknown diplomacy action.", }); SyncAllDataToClients(); } } public void RequestDiplomacyV1(int action, int targetClanId = 0, int value = 0, string text = "", long amount = 0L) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_0006: Expected O, but got Unknown ZPackage val = new ZPackage(); val.Write(LocalId); val.Write(action); val.Write(targetClanId); val.Write(value); val.Write(text ?? ""); val.Write(amount); ToServer("FCW_DiplomacyV1", val); } private void RPC_OnDiplomacyV1(long sender, ZPackage pkg) { if (IsServer) { long playerId = pkg.ReadLong(); int num = pkg.ReadInt(); int num2 = pkg.ReadInt(); int num3 = pkg.ReadInt(); string text = pkg.ReadString(); long price = pkg.ReadLong(); WarfareManager instance = WarfareManager.Instance; SendNotification(sender, num switch { 10 => instance?.CreateAlliance(playerId, text) ?? "Failed", 11 => instance?.InviteToAlliance(playerId, num2) ?? "Failed", 12 => instance?.AcceptAllianceInvite(playerId, num2) ?? "Failed", 13 => instance?.DeclineAllianceInvite(playerId, num2) ?? "Failed", 14 => instance?.LeaveAlliance(playerId) ?? "Failed", 15 => instance?.KickFromAlliance(playerId, num2) ?? "Failed", 16 => instance?.DisbandAlliance(playerId) ?? "Failed", 17 => instance?.ChangeAllianceColor(playerId) ?? "Failed", 20 => instance?.OfferNAP(playerId, num2, num3) ?? "Failed", 21 => instance?.AcceptNAP(playerId, num2) ?? "Failed", 22 => instance?.DeclineNAP(playerId, num2) ?? "Failed", 23 => instance?.BreakNAP(playerId, num2) ?? "Failed", 30 => instance?.OfferVassalage(playerId, num2) ?? "Failed", 31 => instance?.RequestProtection(playerId, num2) ?? "Failed", 32 => instance?.AcceptVassalage(playerId, num2) ?? "Failed", 33 => instance?.DeclineVassalage(playerId, num2) ?? "Failed", 34 => instance?.ReleaseVassal(playerId, num2) ?? "Failed", 35 => instance?.LeaveOverlord(playerId) ?? "Failed", 40 => instance?.OfferTerritoryDeal(playerId, text, num2, 0L) ?? "Failed", 41 => instance?.OfferTerritoryDeal(playerId, text, num2, price) ?? "Failed", 42 => instance?.AcceptTerritoryDeal(playerId, num3) ?? "Failed", 43 => instance?.DeclineTerritoryDeal(playerId, num3) ?? "Failed", 44 => instance?.CancelTerritoryDeal(playerId, num3) ?? "Failed", _ => "Unknown diplomacy action.", }); SyncAllDataToClients(); } } public void RequestTerritoryExchange(int targetClanId, string payload) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_0006: Expected O, but got Unknown ZPackage val = new ZPackage(); val.Write(LocalId); val.Write(targetClanId); val.Write(payload ?? ""); ToServer("FCW_TerritoryExchange", val); } private void RPC_OnTerritoryExchange(long sender, ZPackage pkg) { if (IsServer) { long playerId = pkg.ReadLong(); int targetClanId = pkg.ReadInt(); string payload = pkg.ReadString(); string message = WarfareManager.Instance?.OfferTerritoryExchange(playerId, targetClanId, payload) ?? "Failed"; SendNotification(sender, message); SyncAllDataToClients(); } } public void RequestGameAction(int action, int gameId = 0, string tableName = "", DiceGameMode diceMode = DiceGameMode.HighestRoll, int playerLimit = 2, int diceCount = 1, int roundCount = 1, int coinStake = 0, bool isPrivate = false, bool allowSpectators = true, FehuStakeType stakeType = FehuStakeType.None, string stakePrefab = "") { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_0006: Expected O, but got Unknown ZPackage val = new ZPackage(); val.Write(LocalId); val.Write(action); val.Write(gameId); val.Write(tableName ?? ""); val.Write((int)diceMode); val.Write(playerLimit); val.Write(diceCount); val.Write(roundCount); val.Write(coinStake); val.Write(isPrivate); val.Write(allowSpectators); val.Write((int)stakeType); val.Write(stakePrefab ?? ""); ToServer("FCW_GameAction", val); } private void RPC_OnGameAction(long sender, ZPackage pkg) { //IL_0112: Unknown result type (might be due to invalid IL or missing references) //IL_011d: Expected O, but got Unknown if (!IsServer) { return; } long num = pkg.ReadLong(); int num2 = pkg.ReadInt(); int gameId = pkg.ReadInt(); string text = pkg.ReadString(); DiceGameMode diceGameMode = (DiceGameMode)pkg.ReadInt(); int num3 = pkg.ReadInt(); int num4 = pkg.ReadInt(); int num5 = pkg.ReadInt(); int num6 = pkg.ReadInt(); bool isPrivate = pkg.ReadBool(); bool allowSpectators = pkg.ReadBool(); FehuStakeType fehuStakeType = (FehuStakeType)pkg.ReadInt(); string text2 = pkg.ReadString(); WarfareData warfareData = WarfareManager.Instance?.Data; if (warfareData == null) { return; } if (num2 == 1) { if (num6 < 0 || num3 < 2 || num3 > 8 || num4 < 1 || num4 > 6 || num5 < 1 || num5 > 3 || (fehuStakeType != FehuStakeType.None && fehuStakeType != FehuStakeType.Coins && fehuStakeType != FehuStakeType.Item)) { SendNotification(sender, "Invalid Dice table settings."); return; } if (fehuStakeType == FehuStakeType.Item && num6 <= 0) { SendNotification(sender, "Item stake quantity must be greater than zero."); return; } if (fehuStakeType == FehuStakeType.Coins && num6 <= 0) { fehuStakeType = FehuStakeType.None; } if (fehuStakeType == FehuStakeType.Item && (string.IsNullOrWhiteSpace(text2) || (Object)GetItemPrefabSafe(ObjectDB.instance, text2) == (Object)null)) { SendNotification(sender, "Invalid item stake."); return; } if (fehuStakeType == FehuStakeType.Coins) { text2 = ClanConfig.CoinItemPrefab?.Value ?? "Coins"; } FehuGameTable fehuGameTable = new FehuGameTable { GameId = warfareData.NextGameId++, GameType = FehuGameType.Dice, TableName = (string.IsNullOrWhiteSpace(text) ? "Dice Table" : text.Trim()), HostPlayerId = num, HostPlayerName = (warfareData.GetPlayer(num)?.PlayerName ?? "Player"), PlayerLimit = num3, DiceCount = ((diceGameMode == DiceGameMode.LuckySeven) ? 2 : num4), RoundCount = ((diceGameMode == DiceGameMode.BestOf3) ? 3 : num5), DiceMode = diceGameMode, StakeType = fehuStakeType, StakeAmount = num6, StakePrefabName = (text2 ?? ""), IsPrivate = isPrivate, AllowSpectators = allowSpectators, Status = FehuGameStatus.Waiting, CreatedAt = WarfareManager.Now() }; fehuGameTable.PlayerIds.Add(num); warfareData.Games.Add(fehuGameTable); PersistGames(); Plugin.Log.LogInfo((object)("[FCW Games] Create Dice table id=" + fehuGameTable.GameId)); SendNotification(sender, "Dice table created. Waiting for players."); return; } FehuGameTable fehuGameTable2 = warfareData.Games.Find((FehuGameTable t) => t.GameId == gameId && t.GameType == FehuGameType.Dice); if (fehuGameTable2 == null) { SendNotification(sender, "Dice table not found."); return; } switch (num2) { case 2: if (fehuGameTable2.Status != FehuGameStatus.Waiting || fehuGameTable2.PlayerIds.Contains(num) || fehuGameTable2.PlayerIds.Count >= fehuGameTable2.PlayerLimit) { SendNotification(sender, "Cannot join this Dice table."); break; } fehuGameTable2.PlayerIds.Add(num); PersistGames(); Plugin.Log.LogInfo((object)("[FCW Games] Join Dice table id=" + gameId + " player=" + num)); SendNotification(sender, "Joined Dice table. Waiting for host to start."); break; case 8: if (!fehuGameTable2.PlayerIds.Contains(num)) { SendNotification(sender, "You are not seated at this Dice table."); } else if (fehuGameTable2.Status == FehuGameStatus.Waiting) { fehuGameTable2.PlayerIds.Remove(num); fehuGameTable2.SpectatorIds.Remove(num); if (fehuGameTable2.HostPlayerId == num && fehuGameTable2.PlayerIds.Count > 0) { fehuGameTable2.HostPlayerId = fehuGameTable2.PlayerIds[0]; fehuGameTable2.HostPlayerName = warfareData.GetPlayer(fehuGameTable2.HostPlayerId)?.PlayerName ?? "Player"; } if (fehuGameTable2.PlayerIds.Count == 0) { warfareData.Games.Remove(fehuGameTable2); SendNotification(sender, "Left and removed empty Dice table."); } else { SendNotification(sender, "Left Dice table."); } PersistGames(); } else if (fehuGameTable2.Status == FehuGameStatus.Active) { CancelDiceGame(fehuGameTable2, "Player left active Dice table. Stakes refunded."); SendNotification(sender, "Dice table cancelled."); } else { SendNotification(sender, "Cannot leave this Dice table now."); } break; case 5: if (!fehuGameTable2.AllowSpectators || fehuGameTable2.SpectatorIds.Contains(num)) { SendNotification(sender, "Spectating is unavailable."); break; } fehuGameTable2.SpectatorIds.Add(num); PersistGames(); SendNotification(sender, "You are now spectating this Dice table."); break; case 4: if (fehuGameTable2.HostPlayerId != num || fehuGameTable2.Status == FehuGameStatus.Finished || fehuGameTable2.PayoutIssued) { SendNotification(sender, "Dice table cannot be cancelled."); break; } CancelDiceGame(fehuGameTable2, "Cancelled by host."); SendNotification(sender, "Dice table cancelled."); break; case 6: if (fehuGameTable2.HostPlayerId != num || (fehuGameTable2.Status != FehuGameStatus.Waiting && fehuGameTable2.Status != FehuGameStatus.Finished && fehuGameTable2.Status != FehuGameStatus.Cancelled)) { SendNotification(sender, "Only the host can delete a waiting, finished or cancelled Dice table."); break; } warfareData.Games.Remove(fehuGameTable2); PersistGames(); Plugin.Log.LogInfo((object)("[FCW Games] Delete Dice table id=" + gameId)); SendNotification(sender, "Dice table deleted. Its history is preserved."); break; case 3: if (fehuGameTable2.HostPlayerId != num || fehuGameTable2.Status != FehuGameStatus.Waiting || fehuGameTable2.PlayerIds.Count < 2) { SendNotification(sender, "Dice table needs at least two players and its host to start."); break; } Plugin.Log.LogInfo((object)("[FCW Games] Start Dice table id=" + gameId)); if (fehuGameTable2.StakeType == FehuStakeType.None || fehuGameTable2.StakeAmount <= 0) { StartDiceGame(fehuGameTable2); break; } fehuGameTable2.EscrowCollecting = true; fehuGameTable2.Status = FehuGameStatus.Active; fehuGameTable2.StartedAt = WarfareManager.Now(); fehuGameTable2.EscrowPlayerIds.Clear(); fehuGameTable2.EscrowTotal = 0L; PersistGames(); { foreach (long playerId in fehuGameTable2.PlayerIds) { RequestGameEscrow(playerId, fehuGameTable2.GameId, fehuGameTable2.StakeAmount, fehuGameTable2.StakeType, fehuGameTable2.StakePrefabName); } break; } case 7: RollDiceTurn(fehuGameTable2, num, sender); break; } } private void RequestGameEscrow(long playerId, int gameId, long amount, FehuStakeType stakeType, string stakePrefab) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_0006: Expected O, but got Unknown ZPackage val = new ZPackage(); val.Write(playerId); val.Write(gameId); val.Write(amount); val.Write((int)stakeType); val.Write(stakePrefab ?? ""); ZRoutedRpc instance = ZRoutedRpc.instance; if (instance != null) { instance.InvokeRoutedRPC(ZRoutedRpc.Everybody, "FCW_GameEscrow", new object[1] { val }); } } private void RPC_OnGameEscrow(long sender, ZPackage pkg) { //IL_00b3: Unknown result type (might be due to invalid IL or missing references) //IL_00ba: Expected O, but got Unknown long num = pkg.ReadLong(); int num2 = pkg.ReadInt(); long num3 = pkg.ReadLong(); FehuStakeType fehuStakeType = (FehuStakeType)pkg.ReadInt(); string text = pkg.ReadString(); if (!((Object)(object)Player.m_localPlayer == (Object)null) && Player.m_localPlayer.GetPlayerID() == num) { Inventory inventory = ((Humanoid)Player.m_localPlayer).GetInventory(); string reason = ""; bool flag = false; switch (fehuStakeType) { case FehuStakeType.Coins: { CoinStackInfo coinStackInfo = FindCoinStack(inventory); flag = num3 > 0 && coinStackInfo != null && coinStackInfo.Stack >= num3 && TryRemoveExactInventoryEntry(inventory, coinStackInfo.Item, (int)num3, out reason); break; } case FehuStakeType.Item: flag = num3 > 0 && TryRemoveInventoryByPrefab(inventory, text, (int)num3, out reason); break; default: reason = "invalid stake type"; break; } ZPackage val = new ZPackage(); val.Write(num); val.Write(num2); val.Write(flag); val.Write(reason ?? ""); val.Write((int)fehuStakeType); val.Write(text ?? ""); ToServer("FCW_GameEscrowConfirm", val); } } private void RPC_OnGameEscrowConfirm(long sender, ZPackage pkg) { if (!IsServer) { return; } long item = pkg.ReadLong(); int gameId = pkg.ReadInt(); bool flag = pkg.ReadBool(); string text = pkg.ReadString(); FehuStakeType fehuStakeType = (FehuStakeType)pkg.ReadInt(); string text2 = pkg.ReadString(); FehuGameTable fehuGameTable = WarfareManager.Instance?.Data?.Games.Find((FehuGameTable t) => t.GameId == gameId); if (fehuGameTable == null) { return; } bool flag2 = fehuGameTable.StakeType != FehuStakeType.Item || string.Equals(fehuGameTable.StakePrefabName ?? "", text2 ?? "", StringComparison.OrdinalIgnoreCase); if (!fehuGameTable.EscrowCollecting || !fehuGameTable.PlayerIds.Contains(item) || fehuGameTable.EscrowPlayerIds.Contains(item) || fehuGameTable.StakeType != fehuStakeType || !flag2) { return; } Plugin.Log.LogInfo((object)("[FCW Games] Stake validation player=" + item + " amount=" + fehuGameTable.StakeAmount + " ok=" + flag + " reason=" + text)); if (!flag) { SendNotification(sender, "Stake collection failed" + (string.IsNullOrWhiteSpace(text) ? "." : (": " + text)) + ""); CancelGameEscrow(fehuGameTable, "Stake collection failed."); return; } fehuGameTable.EscrowPlayerIds.Add(item); fehuGameTable.EscrowTotal += fehuGameTable.StakeAmount; if (fehuGameTable.EscrowPlayerIds.Count == fehuGameTable.PlayerIds.Count) { Plugin.Log.LogInfo((object)("[FCW Games] Escrow collected table=" + fehuGameTable.GameId + " total=" + fehuGameTable.EscrowTotal)); if (fehuGameTable.GameType == FehuGameType.Dice) { StartDiceGame(fehuGameTable); } else if (fehuGameTable.GameType == FehuGameType.Chess) { StartChessMatch(fehuGameTable); } else if (fehuGameTable.GameType == FehuGameType.Poker) { StartPokerTable(fehuGameTable); } } else { PersistGames(); } } private void StartDiceGame(FehuGameTable game) { if (game == null) { return; } game.Status = FehuGameStatus.Active; game.EscrowCollecting = false; game.StartedAt = ((game.StartedAt > 0) ? game.StartedAt : WarfareManager.Now()); game.FinishedAt = 0L; game.WinnerPlayerId = 0L; game.PayoutIssued = false; game.Dice = new DiceGameState { CurrentPlayerIndex = 0, CurrentRound = 1, CurrentTurnStartedAt = WarfareManager.Now(), LastResult = "Dice game started. Waiting for first roll." }; game.Dice.ActivePlayerIds.AddRange(game.PlayerIds); foreach (long playerId in game.PlayerIds) { game.Dice.Scores[playerId] = 0; } game.Result = game.Dice.LastResult; PersistGames(); } private void RollDiceTurn(FehuGameTable game, long playerId, long sender) { if (game == null || game.Status != FehuGameStatus.Active || game.Dice == null) { SendNotification(sender, "Dice table is not active."); return; } List list = game.Dice.ActivePlayerIds ?? new List(); if (list.Count == 0) { list = game.PlayerIds; } if (game.Dice.CurrentPlayerIndex < 0 || game.Dice.CurrentPlayerIndex >= list.Count) { game.Dice.CurrentPlayerIndex = 0; } if (list[game.Dice.CurrentPlayerIndex] != playerId) { SendNotification(sender, "It is not your Dice turn."); return; } List list2 = new List(); int num = 0; int num2 = ((game.DiceMode == DiceGameMode.LuckySeven) ? 2 : Math.Max(1, game.DiceCount)); for (int i = 0; i < num2; i++) { int num3 = ServerRandomInt(1, 7); list2.Add(num3); num += num3; } DiceRollRecord item = new DiceRollRecord { PlayerId = playerId, Round = game.Dice.CurrentRound, Rolls = list2, Total = num, Timestamp = WarfareManager.Now() }; game.Dice.Rolls.Add(item); game.Dice.LastResult = (WarfareManager.Instance?.Data?.GetPlayer(playerId)?.PlayerName ?? playerId.ToString()) + " rolled " + string.Join(" + ", list2) + " = " + num; game.Result = game.Dice.LastResult; AdvanceDiceAfterRoll(game, list); PersistGames(); } private void AdvanceDiceAfterRoll(FehuGameTable game, List active) { game.Dice.CurrentPlayerIndex++; if (game.Dice.CurrentPlayerIndex < active.Count) { game.Dice.CurrentTurnStartedAt = WarfareManager.Now(); } else { EvaluateDiceRound(game, active); } } private void EvaluateDiceRound(FehuGameTable game, List active) { List list = game.Dice.Rolls.FindAll((DiceRollRecord roll) => roll.Round == game.Dice.CurrentRound && active.Contains(roll.PlayerId)); if (list.Count == 0) { return; } if (game.DiceMode == DiceGameMode.LuckySeven) { int bestDistance = list.Min((DiceRollRecord roll) => Math.Abs(roll.Total - 7)); List list2 = (from roll in list.FindAll((DiceRollRecord roll) => Math.Abs(roll.Total - 7) == bestDistance) select roll.PlayerId).Distinct().ToList(); if (list2.Count == 1) { FinishDiceGame(game, list2[0]); return; } game.Dice.ActivePlayerIds = list2; game.Dice.CurrentRound++; game.Dice.CurrentPlayerIndex = 0; game.Dice.CurrentTurnStartedAt = WarfareManager.Now(); game.Dice.LastResult = "Lucky Seven tie. Closest players roll again."; game.Result = game.Dice.LastResult; return; } int high = list.Max((DiceRollRecord roll) => roll.Total); List list3 = (from roll in list.FindAll((DiceRollRecord roll) => roll.Total == high) select roll.PlayerId).Distinct().ToList(); if (list3.Count == 1) { game.Dice.Scores[list3[0]] = ((!game.Dice.Scores.TryGetValue(list3[0], out var value)) ? 1 : (value + 1)); } if (game.DiceMode == DiceGameMode.HighestRoll) { if (list3.Count == 1) { FinishDiceGame(game, list3[0]); return; } game.Dice.ActivePlayerIds = list3; game.Dice.CurrentRound++; game.Dice.CurrentPlayerIndex = 0; game.Dice.CurrentTurnStartedAt = WarfareManager.Now(); game.Dice.LastResult = "Highest Roll tie. Tied players roll again."; game.Result = game.Dice.LastResult; return; } if (game.DiceMode == DiceGameMode.BestOf3 && game.Dice.Scores.TryGetValue((list3.Count == 1) ? list3[0] : 0, out var value2) && value2 >= 2) { FinishDiceGame(game, list3[0]); return; } if (game.DiceMode == DiceGameMode.BestOf3 && game.Dice.CurrentRound >= 3) { int best = ((game.Dice.Scores.Count != 0) ? game.Dice.Scores.Values.Max() : 0); List list4 = (from pair in game.Dice.Scores where pair.Value == best select pair.Key).ToList(); if (list4.Count == 1) { FinishDiceGame(game, list4[0]); return; } game.Dice.ActivePlayerIds = list4; game.Dice.LastResult = "Best of 3 tied. Leaders roll sudden death."; } else { game.Dice.ActivePlayerIds = new List(game.PlayerIds); game.Dice.LastResult = ((list3.Count == 1) ? ("Round " + game.Dice.CurrentRound + " won. Next round.") : "Round tied. Next round."); } game.Dice.CurrentRound++; game.Dice.CurrentPlayerIndex = 0; game.Dice.CurrentTurnStartedAt = WarfareManager.Now(); game.Result = game.Dice.LastResult; } private void FinishDiceGame(FehuGameTable game, long winner) { game.WinnerPlayerId = winner; game.Status = FehuGameStatus.Finished; game.EscrowCollecting = false; game.FinishedAt = WarfareManager.Now(); Dictionary diceBestTotals = GetDiceBestTotals(game); int value; int num = (diceBestTotals.TryGetValue(winner, out value) ? value : 0); game.Result = game.DiceMode.ToString() + " winner: " + (WarfareManager.Instance?.Data?.GetPlayer(winner)?.PlayerName ?? winner.ToString()) + " (" + num + ")"; long escrowTotal = game.EscrowTotal; if (escrowTotal > 0) { SendGamePayout(winner, game.GameId, escrowTotal, game.StakeType, game.StakePrefabName, "Dice winnings"); } game.PayoutIssued = true; AddDiceHistoryAndStats(game, diceBestTotals, winner, escrowTotal); } private static Dictionary GetDiceBestTotals(FehuGameTable game) { Dictionary dictionary = new Dictionary(); foreach (long playerId in game.PlayerIds) { dictionary[playerId] = 0; } foreach (DiceRollRecord item in game.Dice?.Rolls ?? new List()) { dictionary[item.PlayerId] = (dictionary.TryGetValue(item.PlayerId, out var value) ? Math.Max(value, item.Total) : item.Total); } return dictionary; } private void ResolveDiceGame(FehuGameTable game) { Dictionary dictionary = new Dictionary(); List list = new List(game.PlayerIds); int num = 0; while (true) { dictionary.Clear(); foreach (long item in list) { int num2 = 0; int num3 = ((game.DiceMode == DiceGameMode.BestOf3) ? (game.DiceCount * 3) : game.DiceCount); for (int i = 0; i < num3; i++) { num2 += ServerRandomInt(1, 7); } dictionary[item] = num2; Plugin.Log.LogInfo((object)("[FCW Games] Dice rolled table=" + game.GameId + " player=" + item + " total=" + num2)); } int num4 = int.MinValue; foreach (int value in dictionary.Values) { num4 = Math.Max(num4, value); } List list2 = new List(); foreach (KeyValuePair item2 in dictionary) { if ((game.DiceMode == DiceGameMode.LuckySeven) ? (item2.Value == 7) : (item2.Value == num4)) { list2.Add(item2.Key); } } if (list2.Count == 1) { long num5 = (game.WinnerPlayerId = list2[0]); game.Status = FehuGameStatus.Finished; game.EscrowCollecting = false; game.FinishedAt = WarfareManager.Now(); game.Result = game.DiceMode.ToString() + " winner: " + (WarfareManager.Instance?.Data?.GetPlayer(num5)?.PlayerName ?? num5.ToString()) + " (" + dictionary[num5] + ")" + ((num > 0) ? (" after tie replay " + num) : ""); long escrowTotal = game.EscrowTotal; if (escrowTotal > 0) { SendGamePayout(num5, game.GameId, escrowTotal, game.StakeType, game.StakePrefabName, "Dice winnings"); } game.PayoutIssued = true; AddDiceHistoryAndStats(game, dictionary, num5, escrowTotal); PersistGames(); Plugin.Log.LogInfo((object)("[FCW Games] Dice finished table=" + game.GameId + " winner=" + num5 + " reward=" + escrowTotal + " " + game.StakePrefabName)); return; } if (game.DiceMode == DiceGameMode.LuckySeven || num >= 5) { break; } list = list2; num++; } game.Result = ((game.DiceMode == DiceGameMode.LuckySeven) ? "No single Lucky Seven winner. Stakes refunded." : "Tie after replay. Stakes refunded."); RefundDiceGame(game); } private static int ServerRandomInt(int minInclusive, int maxExclusive) { if (maxExclusive <= minInclusive) { return minInclusive; } byte[] array = new byte[4]; using (RandomNumberGenerator randomNumberGenerator = RandomNumberGenerator.Create()) { randomNumberGenerator.GetBytes(array); } uint num = BitConverter.ToUInt32(array, 0); return minInclusive + (int)(num % (uint)(maxExclusive - minInclusive)); } private void CancelGameEscrow(FehuGameTable game, string result) { if (game != null) { if (game.GameType == FehuGameType.Dice) { CancelDiceGame(game, result); } else if (game.GameType == FehuGameType.Chess) { CancelChessGame(game, result); } else if (game.GameType == FehuGameType.Poker) { RefundPokerTable(game, result); } } } private void CancelDiceGame(FehuGameTable game, string result) { game.Result = result; if (game.EscrowTotal > 0) { RefundDiceGame(game); } else { game.Status = FehuGameStatus.Cancelled; game.FinishedAt = WarfareManager.Now(); AddDiceCancellationHistory(game); PersistGames(); } Plugin.Log.LogInfo((object)("[FCW Games] Dice cancelled table=" + game.GameId + " refund=" + game.EscrowTotal)); } private void RefundDiceGame(FehuGameTable game) { foreach (long escrowPlayerId in game.EscrowPlayerIds) { SendGamePayout(escrowPlayerId, game.GameId, game.StakeAmount, game.StakeType, game.StakePrefabName, "Dice stake refund"); } game.Status = FehuGameStatus.Cancelled; game.EscrowCollecting = false; game.FinishedAt = WarfareManager.Now(); game.PayoutIssued = true; AddDiceCancellationHistory(game); PersistGames(); } private void SendGamePayout(long playerId, int gameId, long amount, FehuStakeType stakeType, string stakePrefab, string reason) { //IL_0006: Unknown result type (might be due to invalid IL or missing references) //IL_000c: Expected O, but got Unknown if (amount > 0) { ZPackage val = new ZPackage(); val.Write(playerId); val.Write(gameId); val.Write(amount); val.Write((int)stakeType); val.Write(stakePrefab ?? ""); val.Write(reason ?? ""); ZRoutedRpc instance = ZRoutedRpc.instance; if (instance != null) { instance.InvokeRoutedRPC(ZRoutedRpc.Everybody, "FCW_GamePayout", new object[1] { val }); } } } private bool TryPrepareTerritoryStake(WarfareData data, FehuGameTable game, long playerId, string territoryPayload, bool hostSide, out string error) { error = ""; PlayerClanInfo playerClanInfo = data?.GetPlayer(playerId); if (playerClanInfo == null || playerClanInfo.ClanId == 0 || playerClanInfo.Rank != ClanRank.Jarl) { error = "Territory stakes are Jarl-only."; return false; } List list = ParseTerritoryStakeIds(territoryPayload); if (list.Count == 0) { error = "Select at least one owned territory."; return false; } if (!ValidateTerritoryStakeIds(data, list, playerClanInfo.ClanId, game.GameId, out error)) { return false; } if (hostSide) { game.HostStakeClanId = playerClanInfo.ClanId; game.HostStakeTerritoryIds = list; game.HostStakeAccepted = true; } else { if (game.HostStakeClanId == playerClanInfo.ClanId) { error = "Territory stake opponent must be from another clan."; return false; } game.ChallengerStakeClanId = playerClanInfo.ClanId; game.ChallengerStakeTerritoryIds = list; game.ChallengerStakeAccepted = true; } LockTerritoryStake(data, list, playerClanInfo.ClanId, game.GameId); game.StakeAmount = Math.Max(1, (game.HostStakeTerritoryIds?.Count ?? 0) + (game.ChallengerStakeTerritoryIds?.Count ?? 0)); game.StakePrefabName = FormatTerritoryStake(game); Plugin.Log.LogInfo((object)("[FCW Games] Territory stake locked game=" + game.GameId + " clan=" + playerClanInfo.ClanId + " territories=" + string.Join(",", list.ToArray()))); return true; } private static List ParseTerritoryStakeIds(string payload) { return (from id in (payload ?? "").Split(new char[3] { '|', ',', ';' }, StringSplitOptions.RemoveEmptyEntries) select id.Trim() into id where id.Length > 0 select id).Distinct(StringComparer.OrdinalIgnoreCase).ToList(); } private bool ValidateTerritoryStakeIds(WarfareData data, List ids, int clanId, int gameId, out string error) { //IL_00ab: Unknown result type (might be due to invalid IL or missing references) error = ""; foreach (string id in ids) { if (!data.Territories.TryGetValue(id, out var value) || value.OwnerClanId != clanId || value.FreebornOwnerId != 0L) { error = "Territory stake contains territory not owned by your clan."; return false; } if (value.TerritoryStakeGameId != 0 && value.TerritoryStakeGameId != gameId) { error = value.DisplayName + " is already locked in another territory stake."; return false; } if (value.Type == TerritoryType.Capital) { ConfigEntry allowCapitalTransfer = ClanConfig.AllowCapitalTransfer; if (allowCapitalTransfer == null || !allowCapitalTransfer.Value) { error = "Capital transfer is disabled."; return false; } } if (ProtectedWorldZones.IsProtected(value)) { error = ProtectedWorldZones.GetBlockedActionMessage(value.GridPosition); return false; } } return true; } private static void LockTerritoryStake(WarfareData data, List ids, int clanId, int gameId) { foreach (string item in ids ?? new List()) { if (data.Territories.TryGetValue(item, out var value)) { value.TerritoryStakeGameId = gameId; value.TerritoryStakeClanId = clanId; } } } private static int ClearTerritoryStakeLocks(WarfareData data, int gameId = 0) { if (data?.Territories == null) { return 0; } int num = 0; foreach (ClanTerritory value in data.Territories.Values) { if (value.TerritoryStakeGameId != 0 && (gameId == 0 || value.TerritoryStakeGameId == gameId)) { value.TerritoryStakeGameId = 0; value.TerritoryStakeClanId = 0; num++; } } return num; } private string ResolveTerritoryStakeGame(FehuGameTable game, long winner) { WarfareData warfareData = WarfareManager.Instance?.Data; if (warfareData == null || game == null || game.StakeType != FehuStakeType.Territory) { return ""; } if (winner == 0L) { return "Draw/cancel: no territory transfer. Locks cleared: " + ClearTerritoryStakeLocks(warfareData, game.GameId) + "."; } PlayerClanInfo player = warfareData.GetPlayer(winner); if (player == null || player.ClanId == 0) { return "Territory transfer cancelled: winner clan missing. Locks cleared: " + ClearTerritoryStakeLocks(warfareData, game.GameId) + "."; } int num = ((player.ClanId == game.HostStakeClanId) ? game.ChallengerStakeClanId : game.HostStakeClanId); List list = ((player.ClanId == game.HostStakeClanId) ? game.ChallengerStakeTerritoryIds : game.HostStakeTerritoryIds); if (num == 0 || list == null || list.Count == 0) { return "Territory transfer cancelled: missing loser stake. Locks cleared: " + ClearTerritoryStakeLocks(warfareData, game.GameId) + "."; } int num2 = 0; foreach (string item in list) { if (!warfareData.Territories.TryGetValue(item, out var value) || value.OwnerClanId != num || value.FreebornOwnerId != 0L) { Plugin.Log.LogWarning((object)("[FCW Games] Territory stake payout skipped game=" + game.GameId + " territory=" + item + " expectedOwner=" + num)); continue; } if (value.Type == TerritoryType.Capital) { ConfigEntry allowCapitalTransfer = ClanConfig.AllowCapitalTransfer; if (allowCapitalTransfer == null || !allowCapitalTransfer.Value) { Plugin.Log.LogWarning((object)("[FCW Games] Territory stake capital skipped game=" + game.GameId + " territory=" + item)); continue; } } value.OwnerClanId = player.ClanId; value.ClaimedByPlayerId = winner; value.ClaimedByPlayerName = player.PlayerName; value.ClaimTimestamp = WarfareManager.Now(); num2++; } int num3 = ClearTerritoryStakeLocks(warfareData, game.GameId); string text = "Territory transfer: " + num2 + " territory(s) to " + (warfareData.GetClan(player.ClanId)?.Name ?? player.ClanId.ToString()) + ". Locks cleared: " + num3 + "."; Plugin.Log.LogInfo((object)("[FCW Games] " + text + " game=" + game.GameId)); return text; } private static string FormatTerritoryStake(FehuGameTable game) { string text = string.Join(",", (game.HostStakeTerritoryIds ?? new List()).ToArray()); string text2 = string.Join(",", (game.ChallengerStakeTerritoryIds ?? new List()).ToArray()); return "Host[" + text + "] vs Challenger[" + text2 + "]"; } private void RPC_OnGamePayout(long sender, ZPackage pkg) { //IL_008a: Unknown result type (might be due to invalid IL or missing references) //IL_0095: Expected O, but got Unknown long num = pkg.ReadLong(); pkg.ReadInt(); long num2 = pkg.ReadLong(); FehuStakeType fehuStakeType = (FehuStakeType)pkg.ReadInt(); string text = pkg.ReadString(); string text2 = pkg.ReadString(); if (!((Object)(object)Player.m_localPlayer == (Object)null) && Player.m_localPlayer.GetPlayerID() == num && num2 > 0) { string text3 = ((fehuStakeType != FehuStakeType.Coins) ? text : (ClanConfig.CoinItemPrefab?.Value ?? "Coins")); GameObject itemPrefabSafe = GetItemPrefabSafe(ObjectDB.instance, text3); Inventory inventory = ((Humanoid)Player.m_localPlayer).GetInventory(); if ((Object)itemPrefabSafe == (Object)null || !CanInventoryAcceptItem(inventory, itemPrefabSafe, (int)num2, out var _)) { NotifyVaultClient("Game payout pending: inventory cannot receive " + num2 + " " + text3 + "."); } else { inventory.AddItem(itemPrefabSafe, (int)num2); NotifyVaultClient("Game " + text2 + ": " + num2 + " " + ((fehuStakeType == FehuStakeType.Coins) ? "coins" : text3) + "."); } } } private void AddDiceHistoryAndStats(FehuGameTable game, Dictionary totals, long winner, long reward) { WarfareData data = WarfareManager.Instance.Data; data.GameHistory.Add(new FehuGameHistoryEntry { GameId = game.GameId, GameType = FehuGameType.Dice, TableName = game.TableName, PlayerIds = new List(game.PlayerIds), WinnerPlayerId = winner, Result = game.Result, StakeType = game.StakeType, StakeAmount = game.StakeAmount, StakePrefabName = game.StakePrefabName, CompletedAt = game.FinishedAt }); if (data.GameHistory.Count > 100) { data.GameHistory.RemoveAt(0); } foreach (long playerId in game.PlayerIds) { if (!data.GameStats.TryGetValue(playerId, out var value)) { Dictionary gameStats = data.GameStats; FehuGamePlayerStats obj = new FehuGamePlayerStats { PlayerId = playerId, FavoriteGame = FehuGameType.Dice }; value = obj; gameStats[playerId] = obj; } value.GamesPlayed++; value.HighestDiceRoll = Math.Max(value.HighestDiceRoll, totals[playerId]); if (playerId == winner) { value.Wins++; value.DiceCurrentStreak++; value.DiceBestStreak = Math.Max(value.DiceBestStreak, value.DiceCurrentStreak); value.Profit += reward - game.StakeAmount; value.BiggestWin = Math.Max(value.BiggestWin, reward); } else { value.Losses++; value.DiceCurrentStreak = 0; value.Profit -= game.StakeAmount; } value.FavoriteGame = FehuGameType.Dice; } } private void AddDiceCancellationHistory(FehuGameTable game) { WarfareData warfareData = WarfareManager.Instance?.Data; if (warfareData != null && !warfareData.GameHistory.Exists((FehuGameHistoryEntry entry) => entry.GameId == game.GameId && entry.GameType == FehuGameType.Dice)) { warfareData.GameHistory.Add(new FehuGameHistoryEntry { GameId = game.GameId, GameType = FehuGameType.Dice, TableName = game.TableName, PlayerIds = new List(game.PlayerIds), WinnerPlayerId = 0L, Result = (string.IsNullOrEmpty(game.Result) ? "Cancelled. Stakes refunded." : game.Result), StakeType = game.StakeType, StakeAmount = game.StakeAmount, StakePrefabName = game.StakePrefabName, CompletedAt = game.FinishedAt }); if (warfareData.GameHistory.Count > 100) { warfareData.GameHistory.RemoveAt(0); } } } private void PersistGames() { WarfareManager.Instance?.MarkDirty(); WarfareManager.Instance?.SaveData(); SyncAllDataToClients(); } public void RequestChessAction(int action, int gameId = 0, string matchName = "", int colorPreference = 0, int timeControlSeconds = 0, int stake = 0, bool isPrivate = false, bool allowSpectators = true, int from = -1, int to = -1, char promotion = 'Q', FehuStakeType stakeType = FehuStakeType.None, string stakePrefab = "") { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_0006: Expected O, but got Unknown ZPackage val = new ZPackage(); val.Write(LocalId); val.Write(action); val.Write(gameId); val.Write(matchName ?? ""); val.Write(colorPreference); val.Write(timeControlSeconds); val.Write(stake); val.Write(isPrivate); val.Write(allowSpectators); val.Write(from); val.Write(to); val.Write((int)promotion); val.Write((int)stakeType); val.Write(stakePrefab ?? ""); ToServer("FCW_ChessAction", val); } private void RPC_OnChessAction(long sender, ZPackage pkg) { //IL_0132: Unknown result type (might be due to invalid IL or missing references) //IL_013d: Expected O, but got Unknown if (!IsServer) { return; } long num = pkg.ReadLong(); int num2 = pkg.ReadInt(); int gameId = pkg.ReadInt(); string text = pkg.ReadString(); int num3 = pkg.ReadInt(); int num4 = pkg.ReadInt(); int num5 = pkg.ReadInt(); bool isPrivate = pkg.ReadBool(); bool allowSpectators = pkg.ReadBool(); int num6 = pkg.ReadInt(); int to = pkg.ReadInt(); char promotion = (char)pkg.ReadInt(); FehuStakeType fehuStakeType = (FehuStakeType)pkg.ReadInt(); string text2 = pkg.ReadString(); WarfareData warfareData = WarfareManager.Instance?.Data; if (warfareData == null) { return; } if (num2 == 1) { if (num5 < 0 || num3 < 0 || num3 > 2 || num4 < 0 || num4 > 7200 || (num4 > 0 && num4 < 60)) { SendNotification(sender, "Invalid chess match settings."); return; } if (fehuStakeType != FehuStakeType.None && fehuStakeType != FehuStakeType.Coins && fehuStakeType != FehuStakeType.Item && fehuStakeType != FehuStakeType.Territory) { SendNotification(sender, "Invalid chess stake type."); return; } if (fehuStakeType == FehuStakeType.Item && num5 <= 0) { SendNotification(sender, "Chess item stake amount must be greater than zero."); return; } if (fehuStakeType == FehuStakeType.Coins && num5 <= 0) { fehuStakeType = FehuStakeType.None; } if (fehuStakeType == FehuStakeType.Item && (string.IsNullOrWhiteSpace(text2) || (Object)GetItemPrefabSafe(ObjectDB.instance, text2) == (Object)null)) { SendNotification(sender, "Invalid chess item stake."); return; } if (fehuStakeType == FehuStakeType.Coins) { text2 = ClanConfig.CoinItemPrefab?.Value ?? "Coins"; } if (fehuStakeType == FehuStakeType.None) { text2 = ""; } FehuGameTable fehuGameTable = new FehuGameTable { GameId = warfareData.NextGameId++, GameType = FehuGameType.Chess, TableName = (string.IsNullOrWhiteSpace(text) ? "Chess Match" : text.Trim()), HostPlayerId = num, HostPlayerName = (warfareData.GetPlayer(num)?.PlayerName ?? "Player"), PlayerLimit = 2, IsPrivate = isPrivate, AllowSpectators = allowSpectators, StakeType = fehuStakeType, StakeAmount = ((fehuStakeType == FehuStakeType.None) ? 0 : num5), StakePrefabName = (text2 ?? ""), Status = FehuGameStatus.Waiting, CreatedAt = WarfareManager.Now(), ChessHostColorPreference = num3, ChessTimeControlSeconds = num4 }; if (fehuStakeType == FehuStakeType.Territory) { if (!TryPrepareTerritoryStake(warfareData, fehuGameTable, num, text2, hostSide: true, out var error)) { SendNotification(sender, "" + error + ""); return; } fehuGameTable.StakeAmount = fehuGameTable.HostStakeTerritoryIds.Count; } fehuGameTable.PlayerIds.Add(num); warfareData.Games.Add(fehuGameTable); PersistGames(); Plugin.Log.LogInfo((object)("[FCW Chess] Create match=" + fehuGameTable.GameId)); SendNotification(sender, "Chess match created. Waiting for opponent."); return; } FehuGameTable fehuGameTable2 = warfareData.Games.Find((FehuGameTable value) => value.GameId == gameId && value.GameType == FehuGameType.Chess); if (fehuGameTable2 == null) { SendNotification(sender, "Chess match not found."); return; } switch (num2) { case 2: { if (fehuGameTable2.Status != FehuGameStatus.Waiting || fehuGameTable2.IsPrivate || fehuGameTable2.PlayerIds.Contains(num) || fehuGameTable2.PlayerIds.Count >= 2) { SendNotification(sender, "Cannot join this chess match."); return; } if (fehuGameTable2.StakeType == FehuStakeType.Territory && !TryPrepareTerritoryStake(warfareData, fehuGameTable2, num, text2, hostSide: false, out var error2)) { SendNotification(sender, "" + error2 + ""); return; } fehuGameTable2.PlayerIds.Add(num); PersistGames(); SendNotification(sender, "Opponent joined. Waiting for host to start."); return; } case 5: if (!fehuGameTable2.AllowSpectators || fehuGameTable2.SpectatorIds.Contains(num)) { SendNotification(sender, "Spectating is unavailable."); return; } fehuGameTable2.SpectatorIds.Add(num); PersistGames(); return; case 4: if (fehuGameTable2.HostPlayerId != num || fehuGameTable2.Status != FehuGameStatus.Waiting) { SendNotification(sender, "Only the host can cancel a waiting chess match."); } else { CancelChessGame(fehuGameTable2, "Cancelled by host."); } return; case 14: if (fehuGameTable2.HostPlayerId != num || (fehuGameTable2.Status != FehuGameStatus.Waiting && fehuGameTable2.Status != FehuGameStatus.Finished && fehuGameTable2.Status != FehuGameStatus.Cancelled)) { SendNotification(sender, "Only the host can delete a waiting, finished or cancelled chess match."); return; } warfareData.Games.Remove(fehuGameTable2); PersistGames(); SendNotification(sender, "Chess match deleted. Its history is preserved."); return; case 3: if (fehuGameTable2.HostPlayerId != num || fehuGameTable2.Status != FehuGameStatus.Waiting || fehuGameTable2.PlayerIds.Count != 2) { SendNotification(sender, "Chess needs two players and its host to start."); return; } if (fehuGameTable2.StakeType == FehuStakeType.Territory) { if (!fehuGameTable2.HostStakeAccepted || !fehuGameTable2.ChallengerStakeAccepted || fehuGameTable2.HostStakeClanId == 0 || fehuGameTable2.ChallengerStakeClanId == 0) { SendNotification(sender, "Both Jarls must lock territory stakes before start."); } else { StartChessMatch(fehuGameTable2); } return; } if (fehuGameTable2.StakeType == FehuStakeType.None || fehuGameTable2.StakeAmount <= 0) { StartChessMatch(fehuGameTable2); return; } fehuGameTable2.Status = FehuGameStatus.Active; fehuGameTable2.StartedAt = WarfareManager.Now(); fehuGameTable2.EscrowCollecting = true; fehuGameTable2.EscrowPlayerIds.Clear(); fehuGameTable2.EscrowTotal = 0L; PersistGames(); { foreach (long playerId in fehuGameTable2.PlayerIds) { RequestGameEscrow(playerId, fehuGameTable2.GameId, fehuGameTable2.StakeAmount, fehuGameTable2.StakeType, fehuGameTable2.StakePrefabName); } return; } } if (fehuGameTable2.Status != FehuGameStatus.Active || fehuGameTable2.Chess == null) { SendNotification(sender, "Chess match is not active."); return; } if (fehuGameTable2.Chess.WhitePlayerId != num && fehuGameTable2.Chess.BlackPlayerId != num) { SendNotification(sender, "Spectators cannot control a chess match."); return; } switch (num2) { case 6: { if (!ChessRuleEngine.TryMove(fehuGameTable2.Chess, num, num6, to, promotion, out var reason, out var result)) { SendNotification(sender, "" + reason + ""); } else if (result != ChessMatchResult.Active) { FinishChessGame(fehuGameTable2, result); } else { PersistGames(); } break; } case 7: FinishChessGame(fehuGameTable2, (fehuGameTable2.Chess.WhitePlayerId == num) ? ChessMatchResult.BlackWinResign : ChessMatchResult.WhiteWinResign); break; case 8: if (fehuGameTable2.Chess.DrawOfferedBy != 0L) { SendNotification(sender, "A draw offer is already pending."); break; } fehuGameTable2.Chess.DrawOfferedBy = num; PersistGames(); break; case 9: if (fehuGameTable2.Chess.DrawOfferedBy == 0L || fehuGameTable2.Chess.DrawOfferedBy == num) { SendNotification(sender, "No opponent draw offer is pending."); } else { FinishChessGame(fehuGameTable2, ChessMatchResult.DrawAgreement); } break; case 10: if (fehuGameTable2.Chess.DrawOfferedBy != 0L && fehuGameTable2.Chess.DrawOfferedBy != num) { fehuGameTable2.Chess.DrawOfferedBy = 0L; PersistGames(); } break; case 11: if (fehuGameTable2.Chess.Moves.Count == 0 || fehuGameTable2.Chess.UndoRequestedBy != 0L) { SendNotification(sender, "Undo is unavailable."); break; } fehuGameTable2.Chess.UndoRequestedBy = num; PersistGames(); break; case 12: if (fehuGameTable2.Chess.UndoRequestedBy == 0L || fehuGameTable2.Chess.UndoRequestedBy == num) { SendNotification(sender, "No opponent undo request is pending."); } else if (!ChessRuleEngine.UndoLastMove(fehuGameTable2.Chess)) { SendNotification(sender, "Unable to undo the last move."); } else { PersistGames(); } break; case 13: if (fehuGameTable2.Chess.UndoRequestedBy != 0L && fehuGameTable2.Chess.UndoRequestedBy != num) { fehuGameTable2.Chess.UndoRequestedBy = 0L; PersistGames(); } break; } } private void StartChessMatch(FehuGameTable game) { if (!_chessValidationLogged) { _chessValidationLogged = true; if (ChessRuleEngine.RunDebugValidation(out var reason)) { Plugin.Log.LogInfo((object)"[FCW Chess] Rule validation passed."); } else { Plugin.Log.LogError((object)("[FCW Chess] Rule validation failed: " + reason)); } } long host = game.HostPlayerId; long num = game.PlayerIds.Find((long id) => id != host); bool flag = game.ChessHostColorPreference == 1 || (game.ChessHostColorPreference == 0 && (game.GameId & 1) == 0); if (game.ChessHostColorPreference == 2) { flag = false; } game.Chess = ChessRuleEngine.Create(flag ? host : num, flag ? num : host, game.ChessTimeControlSeconds); game.Status = FehuGameStatus.Active; game.EscrowCollecting = false; game.StartedAt = WarfareManager.Now(); game.Result = "Chess match in progress."; PersistGames(); Plugin.Log.LogInfo((object)("[FCW Chess] Started match=" + game.GameId + " white=" + game.Chess.WhitePlayerId)); BroadcastGlobalMessage("Chess match started: " + game.TableName + " | " + (WarfareManager.Instance?.Data?.GetPlayer(game.Chess.WhitePlayerId)?.PlayerName ?? "White") + " vs " + (WarfareManager.Instance?.Data?.GetPlayer(game.Chess.BlackPlayerId)?.PlayerName ?? "Black")); } private void TickChessClocks() { WarfareData warfareData = WarfareManager.Instance?.Data; if (warfareData == null) { return; } long num = WarfareManager.Now(); bool flag = false; foreach (FehuGameTable item in warfareData.Games.FindAll((FehuGameTable value) => value.GameType == FehuGameType.Chess && value.Status == FehuGameStatus.Active && value.Chess != null)) { ChessGameState chess = item.Chess; if (IsChessPlayerOnline(warfareData, chess.WhitePlayerId)) { chess.WhiteDisconnectedAt = 0L; } else if (chess.WhiteDisconnectedAt == 0L) { chess.WhiteDisconnectedAt = num; flag = true; } if (IsChessPlayerOnline(warfareData, chess.BlackPlayerId)) { chess.BlackDisconnectedAt = 0L; } else if (chess.BlackDisconnectedAt == 0L) { chess.BlackDisconnectedAt = num; flag = true; } if (chess.WhiteDisconnectedAt > 0 && num - chess.WhiteDisconnectedAt >= 300) { FinishChessGame(item, ChessMatchResult.BlackWinTimeout); return; } if (chess.BlackDisconnectedAt > 0 && num - chess.BlackDisconnectedAt >= 300) { FinishChessGame(item, ChessMatchResult.WhiteWinTimeout); return; } if (chess.WhiteDisconnectedAt > 0 || chess.BlackDisconnectedAt > 0) { chess.LastClockTick = num; flag = true; } else { if (chess.TimeControlSeconds <= 0) { continue; } long num2 = Math.Max(0L, num - chess.LastClockTick); if (num2 != 0L) { if (chess.WhiteToMove) { chess.WhiteSeconds = Math.Max(0L, chess.WhiteSeconds - num2); } else { chess.BlackSeconds = Math.Max(0L, chess.BlackSeconds - num2); } chess.LastClockTick = num; flag = true; if (chess.WhiteSeconds <= 0) { FinishChessGame(item, ChessMatchResult.BlackWinTimeout); return; } if (chess.BlackSeconds <= 0) { FinishChessGame(item, ChessMatchResult.WhiteWinTimeout); return; } } } } if (flag) { PersistGames(); } } private static bool IsChessPlayerOnline(WarfareData data, long playerId) { //IL_0023: Unknown result type (might be due to invalid IL or missing references) //IL_002e: Expected O, but got Unknown //IL_0046: Unknown result type (might be due to invalid IL or missing references) PlayerClanInfo playerClanInfo = data?.GetPlayer(playerId); if (playerClanInfo == null || string.IsNullOrWhiteSpace(playerClanInfo.PlayerName) || (Object)ZNet.instance == (Object)null) { return false; } foreach (PlayerInfo player in ZNet.instance.GetPlayerList()) { if (string.Equals(player.m_name, playerClanInfo.PlayerName, StringComparison.Ordinal)) { return true; } } return false; } private void FinishChessGame(FehuGameTable game, ChessMatchResult result) { ChessGameState chess = game.Chess; if (chess == null || game.PayoutIssued) { return; } chess.Result = result; game.FinishedAt = WarfareManager.Now(); game.Status = FehuGameStatus.Finished; game.EscrowCollecting = false; long num = 0L; if (result == ChessMatchResult.WhiteWinCheckmate || result == ChessMatchResult.WhiteWinResign || result == ChessMatchResult.WhiteWinTimeout) { num = chess.WhitePlayerId; } if (result == ChessMatchResult.BlackWinCheckmate || result == ChessMatchResult.BlackWinResign || result == ChessMatchResult.BlackWinTimeout) { num = chess.BlackPlayerId; } game.WinnerPlayerId = num; game.Result = result.ToString(); if (game.StakeType == FehuStakeType.Territory) { game.Result = game.Result + " | " + ResolveTerritoryStakeGame(game, num); } else if (num != 0L && game.EscrowTotal > 0) { SendGamePayout(num, game.GameId, game.EscrowTotal, game.StakeType, game.StakePrefabName, "Chess winnings"); } else if (num == 0L && game.EscrowTotal > 0) { foreach (long escrowPlayerId in game.EscrowPlayerIds) { SendGamePayout(escrowPlayerId, game.GameId, game.StakeAmount, game.StakeType, game.StakePrefabName, "Chess draw refund"); } } game.PayoutIssued = true; AddChessHistoryAndStats(game, num); PersistGames(); Plugin.Log.LogInfo((object)("[FCW Chess] Finished match=" + game.GameId + " result=" + result)); } private void CancelChessGame(FehuGameTable game, string result) { if (game.PayoutIssued) { return; } if (game.StakeType == FehuStakeType.Territory) { game.Result = result + " | " + ResolveTerritoryStakeGame(game, 0L); } else { foreach (long escrowPlayerId in game.EscrowPlayerIds) { SendGamePayout(escrowPlayerId, game.GameId, game.StakeAmount, game.StakeType, game.StakePrefabName, "Chess stake refund"); } } if (game.StakeType != FehuStakeType.Territory) { game.Result = result; } game.Status = FehuGameStatus.Cancelled; game.FinishedAt = WarfareManager.Now(); game.EscrowCollecting = false; game.PayoutIssued = true; AddChessHistoryAndStats(game, 0L, countStats: false); PersistGames(); } private void AddChessHistoryAndStats(FehuGameTable game, long winner, bool countStats = true) { WarfareData data = WarfareManager.Instance.Data; if (!data.GameHistory.Exists((FehuGameHistoryEntry entry) => entry.GameId == game.GameId && entry.GameType == FehuGameType.Chess)) { data.GameHistory.Add(new FehuGameHistoryEntry { GameId = game.GameId, GameType = FehuGameType.Chess, TableName = game.TableName, PlayerIds = new List(game.PlayerIds), WinnerPlayerId = winner, Result = game.Result + " | Moves: " + (game.Chess?.Moves.Count ?? 0), StakeType = game.StakeType, StakeAmount = game.StakeAmount, StakePrefabName = game.StakePrefabName, CompletedAt = game.FinishedAt, HostStakeTerritoryIds = new List(game.HostStakeTerritoryIds ?? new List()), ChallengerStakeTerritoryIds = new List(game.ChallengerStakeTerritoryIds ?? new List()), HostStakeClanId = game.HostStakeClanId, ChallengerStakeClanId = game.ChallengerStakeClanId, TerritoryTransferResult = ((game.StakeType == FehuStakeType.Territory) ? game.Result : "") }); } if (!countStats) { if (data.GameHistory.Count > 100) { data.GameHistory.RemoveAt(0); } return; } foreach (long playerId in game.PlayerIds) { if (!data.GameStats.TryGetValue(playerId, out var value)) { Dictionary gameStats = data.GameStats; FehuGamePlayerStats obj = new FehuGamePlayerStats { PlayerId = playerId, ChessRating = 1000 }; value = obj; gameStats[playerId] = obj; } value.GamesPlayed++; value.ChessGamesPlayed++; if (winner == 0L) { value.ChessDraws++; value.ChessCurrentStreak = 0; } else if (playerId == winner) { value.Wins++; value.ChessWins++; value.ChessRating += 10; value.ChessCurrentStreak++; value.ChessBestStreak = Math.Max(value.ChessBestStreak, value.ChessCurrentStreak); value.Profit += game.EscrowTotal - game.StakeAmount; value.BiggestWin = Math.Max(value.BiggestWin, game.EscrowTotal); } else { value.Losses++; value.ChessLosses++; value.ChessRating = Math.Max(0, value.ChessRating - 10); value.ChessCurrentStreak = 0; value.Profit -= game.StakeAmount; } value.FavoriteGame = FehuGameType.Chess; } if (data.GameHistory.Count > 100) { data.GameHistory.RemoveAt(0); } } public void RequestPokerAction(int action, int gameId = 0, string tableName = "", int playerLimit = 2, long buyIn = 100L, long smallBlind = 1L, long bigBlind = 2L, bool isPrivate = false, bool allowSpectators = true, long raiseTo = 0L, FehuStakeType stakeType = FehuStakeType.Coins, string stakePrefab = "") { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_0006: Expected O, but got Unknown ZPackage val = new ZPackage(); val.Write(LocalId); val.Write(action); val.Write(gameId); val.Write(tableName ?? ""); val.Write(playerLimit); val.Write(buyIn); val.Write(smallBlind); val.Write(bigBlind); val.Write(isPrivate); val.Write(allowSpectators); val.Write(raiseTo); val.Write((int)stakeType); val.Write(stakePrefab ?? ""); ToServer("FCW_PokerAction", val); } private void RPC_OnPokerAction(long sender, ZPackage pkg) { if (!IsServer) { return; } long num = pkg.ReadLong(); int num2 = pkg.ReadInt(); int gameId = pkg.ReadInt(); string text = pkg.ReadString(); int num3 = pkg.ReadInt(); long num4 = pkg.ReadLong(); long num5 = pkg.ReadLong(); long num6 = pkg.ReadLong(); bool isPrivate = pkg.ReadBool(); bool allowSpectators = pkg.ReadBool(); long raiseTo = pkg.ReadLong(); FehuStakeType fehuStakeType = ((pkg.ReadInt() != 3) ? FehuStakeType.Coins : FehuStakeType.Territory); string text2 = pkg.ReadString(); WarfareData warfareData = WarfareManager.Instance?.Data; if (warfareData == null) { return; } if (num2 == 1) { if (num3 < 2 || num3 > 10 || num4 <= 0 || num5 <= 0 || num6 < num5 || num6 > num4) { SendNotification(sender, "Invalid poker table settings."); return; } FehuGameTable fehuGameTable = new FehuGameTable { GameId = warfareData.NextGameId++, GameType = FehuGameType.Poker, TableName = (string.IsNullOrWhiteSpace(text) ? "Texas Hold'em" : text.Trim()), HostPlayerId = num, HostPlayerName = (warfareData.GetPlayer(num)?.PlayerName ?? "Player"), PlayerLimit = num3, IsPrivate = isPrivate, AllowSpectators = allowSpectators, StakeType = fehuStakeType, StakeAmount = num4, StakePrefabName = ((fehuStakeType == FehuStakeType.Territory) ? text2 : (ClanConfig.CoinItemPrefab?.Value ?? "Coins")), Status = FehuGameStatus.Waiting, CreatedAt = WarfareManager.Now(), PokerBuyIn = num4, PokerSmallBlind = num5, PokerBigBlind = num6 }; if (fehuStakeType == FehuStakeType.Territory && !TryPrepareTerritoryStake(warfareData, fehuGameTable, num, text2, hostSide: true, out var error)) { SendNotification(sender, "" + error + ""); return; } fehuGameTable.PlayerIds.Add(num); warfareData.Games.Add(fehuGameTable); PersistGames(); Plugin.Log.LogInfo((object)("[FCW Poker] Create table=" + fehuGameTable.GameId)); BroadcastGlobalMessage("Texas Hold'em table created: " + fehuGameTable.TableName + " | Buy-in: " + num4 + " Coins | Seats: " + num3); SendNotification(sender, "Poker table created. Waiting for players."); return; } FehuGameTable fehuGameTable2 = warfareData.Games.Find((FehuGameTable t) => t.GameId == gameId && t.GameType == FehuGameType.Poker); if (fehuGameTable2 == null) { SendNotification(sender, "Poker table not found."); return; } switch (num2) { case 2: { if (fehuGameTable2.Status != FehuGameStatus.Waiting || fehuGameTable2.IsPrivate || fehuGameTable2.PlayerIds.Contains(num) || fehuGameTable2.PlayerIds.Count >= fehuGameTable2.PlayerLimit) { SendNotification(sender, "Cannot join this poker table."); return; } if (fehuGameTable2.StakeType == FehuStakeType.Territory && !TryPrepareTerritoryStake(warfareData, fehuGameTable2, num, text2, hostSide: false, out var error2)) { SendNotification(sender, "" + error2 + ""); return; } fehuGameTable2.PlayerIds.Add(num); PersistGames(); Plugin.Log.LogInfo((object)("[FCW Poker] Join table=" + fehuGameTable2.GameId + " player=" + num)); SendNotification(sender, "Joined poker table. Waiting for host to start."); return; } case 3: if (fehuGameTable2.HostPlayerId != num || fehuGameTable2.Status != FehuGameStatus.Waiting || fehuGameTable2.PlayerIds.Count < 2) { SendNotification(sender, "Poker needs two players and its host to start."); return; } if (fehuGameTable2.StakeType == FehuStakeType.Territory) { if (!fehuGameTable2.HostStakeAccepted || !fehuGameTable2.ChallengerStakeAccepted || fehuGameTable2.HostStakeClanId == 0 || fehuGameTable2.ChallengerStakeClanId == 0) { SendNotification(sender, "Both Jarls must lock territory stakes before start."); return; } StartPokerTable(fehuGameTable2); SendNotification(sender, "Poker game started."); return; } fehuGameTable2.Status = FehuGameStatus.Active; fehuGameTable2.StartedAt = WarfareManager.Now(); fehuGameTable2.EscrowCollecting = true; fehuGameTable2.EscrowPlayerIds.Clear(); fehuGameTable2.EscrowTotal = 0L; PersistGames(); { foreach (long playerId in fehuGameTable2.PlayerIds) { RequestGameEscrow(playerId, fehuGameTable2.GameId, fehuGameTable2.PokerBuyIn, FehuStakeType.Coins, fehuGameTable2.StakePrefabName); } return; } case 4: if (!fehuGameTable2.AllowSpectators || fehuGameTable2.SpectatorIds.Contains(num)) { SendNotification(sender, "Spectating is unavailable."); return; } fehuGameTable2.SpectatorIds.Add(num); PersistGames(); return; case 11: if (fehuGameTable2.HostPlayerId != num || (fehuGameTable2.Poker != null && PokerEngine.IsHandActive(fehuGameTable2.Poker))) { SendNotification(sender, "Only a waiting or between-hands poker table can be cancelled."); } else { RefundPokerTable(fehuGameTable2, "Cancelled by host."); } return; case 12: if (fehuGameTable2.HostPlayerId != num || (fehuGameTable2.Status != FehuGameStatus.Waiting && fehuGameTable2.Status != FehuGameStatus.Finished && fehuGameTable2.Status != FehuGameStatus.Cancelled)) { SendNotification(sender, "Only the host can delete a waiting, finished or cancelled poker table."); return; } warfareData.Games.Remove(fehuGameTable2); PersistGames(); SendNotification(sender, "Poker table deleted. Its history is preserved."); return; case 9: if (fehuGameTable2.Status == FehuGameStatus.Waiting && fehuGameTable2.Poker == null) { LeaveWaitingPokerTable(fehuGameTable2, num, sender); return; } break; } PokerGameState poker = fehuGameTable2.Poker; if (poker == null) { SendNotification(sender, "Poker table is still collecting buy-ins."); return; } switch (num2) { case 9: CashOutPokerPlayer(fehuGameTable2, num, sender); return; case 10: if (fehuGameTable2.HostPlayerId != num || PokerEngine.IsHandActive(poker)) { SendNotification(sender, "Only the host can deal the next hand between hands."); return; } PokerEngine.StartHand(poker); if (PokerEngine.HasDuplicateCards(poker)) { RefundPokerTable(fehuGameTable2, "Poker deck validation failed."); return; } fehuGameTable2.Result = poker.LastResult; PersistGames(); return; } PokerPlayerState player = PokerEngine.GetPlayer(poker, num); if (player == null || poker.CurrentActorId != num) { SendNotification(sender, "It is not your poker turn."); return; } if (num2 < 5 || num2 > 8) { SendNotification(sender, "Unknown poker action."); return; } long num7 = PokerEngine.CurrentPot(poker); int num8 = num2 switch { 7 => 4, 6 => 3, 5 => 1, _ => 5, }; if (num2 == 6 && poker.CurrentBet <= player.RoundBet) { num8 = 2; } if (!PokerEngine.ApplyAction(poker, num, num8, raiseTo, out var reason)) { SendNotification(sender, "" + reason + ""); return; } long num9 = PokerEngine.CurrentPot(poker); if (player.AllIn && (num8 == 5 || player.Chips == 0L)) { BroadcastGlobalMessage("Poker All-In: " + player.PlayerName + " is All-In."); } if (num7 < 500 && num9 >= 500) { BroadcastGlobalMessage("Main Pot reaches " + num9 + " Coins."); } Plugin.Log.LogInfo((object)("[FCW Poker] Action table=" + fehuGameTable2.GameId + " player=" + num + " action=" + num2 + " pot=" + PokerEngine.CurrentPot(poker) + " chips=" + player.Chips + " allIn=" + player.AllIn)); AdvancePokerAfterAction(fehuGameTable2); PersistGames(); } private void StartPokerTable(FehuGameTable game) { PokerGameState pokerGameState = PokerEngine.Create(game.PokerSmallBlind, game.PokerBigBlind); foreach (long playerId in game.PlayerIds) { pokerGameState.Seats.Add(new PokerPlayerState { PlayerId = playerId, PlayerName = (WarfareManager.Instance.Data.GetPlayer(playerId)?.PlayerName ?? playerId.ToString()), Chips = game.PokerBuyIn }); } game.Poker = pokerGameState; game.Status = FehuGameStatus.Active; game.EscrowCollecting = false; game.StartedAt = WarfareManager.Now(); PokerEngine.StartHand(pokerGameState); if (PokerEngine.HasDuplicateCards(pokerGameState)) { RefundPokerTable(game, "Poker deck validation failed."); return; } PokerEngine.AddHandLog(pokerGameState, "Texas Hold'em started. Players: " + string.Join(", ", pokerGameState.Seats.Select((PokerPlayerState seat) => seat.PlayerName).ToArray()) + ". Buy-in: " + game.PokerBuyIn + " Coins. Blinds: " + game.PokerSmallBlind + " / " + game.PokerBigBlind + "."); game.Result = pokerGameState.LastResult; PersistGames(); Plugin.Log.LogInfo((object)("[FCW Poker] Started table=" + game.GameId + " players=" + pokerGameState.Seats.Count)); BroadcastGlobalMessage("Texas Hold'em started: " + game.TableName + " | Players: " + string.Join(", ", pokerGameState.Seats.Select((PokerPlayerState seat) => seat.PlayerName).ToArray()) + " | Buy-in: " + game.PokerBuyIn + " Coins | Blinds: " + game.PokerSmallBlind + "/" + game.PokerBigBlind); } private void AdvancePokerAfterAction(FehuGameTable game) { PokerGameState state = game.Poker; if (PokerEngine.HasSingleLivePlayer(state)) { PokerPlayerState singleLivePlayer = PokerEngine.GetSingleLivePlayer(state); long value = PokerEngine.CurrentPot(state); FinishPokerHand(game, new Dictionary { { singleLivePlayer?.PlayerId ?? 0, value } }, "Won by folds"); return; } while (PokerEngine.IsBettingRoundComplete(state)) { if (state.Phase == PokerPhase.River) { FinishPokerHand(game, PokerEngine.ResolvePots(state), "Showdown"); return; } PokerEngine.AdvanceStreet(state); if (state.Phase == PokerPhase.Showdown) { FinishPokerHand(game, PokerEngine.ResolvePots(state), "Showdown"); return; } if (state.CurrentActorId == 0L && state.Seats.Count((PokerPlayerState p) => !p.Folded) <= 1) { FinishPokerHand(game, PokerEngine.ResolvePots(state), "Showdown"); return; } if (state.Seats.Count((PokerPlayerState p) => !p.Folded && !p.AllIn && p.Chips > 0) > 1) { return; } } int num = state.Seats.FindIndex((PokerPlayerState p) => p.PlayerId == state.CurrentActorId); int num2 = PokerEngine.FindNextActionSeat(state, num + 1); if (num2 >= 0) { state.CurrentActorId = state.Seats[num2].PlayerId; } state.LastActionAt = WarfareManager.Now(); } private void LeaveWaitingPokerTable(FehuGameTable game, long playerId, long sender) { if (!game.PlayerIds.Remove(playerId) && !game.SpectatorIds.Remove(playerId)) { SendNotification(sender, "You are not seated at this poker table."); return; } if (game.PlayerIds.Count == 0) { game.Status = FehuGameStatus.Finished; game.FinishedAt = WarfareManager.Now(); game.Result = "Empty poker table removed."; } else if (game.HostPlayerId == playerId) { game.HostPlayerId = game.PlayerIds[0]; game.HostPlayerName = WarfareManager.Instance?.Data?.GetPlayer(game.HostPlayerId)?.PlayerName ?? game.HostPlayerId.ToString(); } PersistGames(); SendNotification(sender, "Left poker table."); } private void FinishPokerHand(FehuGameTable game, Dictionary payouts, string result) { PokerGameState poker = game.Poker; long pot = PokerEngine.CurrentPot(poker); Dictionary contributions = poker.Seats.ToDictionary((PokerPlayerState p) => p.PlayerId, (PokerPlayerState p) => Math.Max(0L, p.TotalContribution)); Dictionary chipsBefore = poker.Seats.ToDictionary((PokerPlayerState p) => p.PlayerId, (PokerPlayerState p) => p.Chips); long num = (from p in payouts orderby p.Value descending select p.Key).FirstOrDefault(); string text = ""; PokerPlayerState player = PokerEngine.GetPlayer(poker, num); if (player != null && result == "Showdown") { text = " " + PokerEngine.HandName(PokerEngine.EvaluateBest(player.HoleCards.Concat(poker.CommunityCards).ToList())); } foreach (KeyValuePair payout in payouts) { PokerPlayerState player2 = PokerEngine.GetPlayer(poker, payout.Key); if (player2 != null && payout.Value > 0) { player2.Chips = Math.Max(0L, player2.Chips + payout.Value); } } poker.Phase = PokerPhase.Showdown; poker.CurrentActorId = 0L; poker.LastResult = result + text + ". Pot: " + pot + "."; PokerEngine.AddHandLog(poker, "Showdown."); PokerEngine.AddHandLog(poker, "Winner: " + (player?.PlayerName ?? num.ToString()) + (string.IsNullOrWhiteSpace(text) ? "" : (" - " + text.Trim())) + "."); PokerEngine.AddHandLog(poker, (player?.PlayerName ?? num.ToString()) + " wins " + payouts.Values.Sum() + " Coins" + (string.IsNullOrWhiteSpace(text) ? "." : (" with " + text.Trim() + "."))); PokerEngine.AddHandLog(poker, "Payout: " + string.Join(", ", payouts.Select((KeyValuePair pair) => (WarfareManager.Instance?.Data?.GetPlayer(pair.Key)?.PlayerName ?? pair.Key.ToString()) + " " + pair.Value + " Coins").ToArray()) + "."); game.Result = poker.LastResult; game.WinnerPlayerId = num; if (game.StakeType == FehuStakeType.Territory) { game.Result = game.Result + " | " + ResolveTerritoryStakeGame(game, num); } BroadcastGlobalMessage("Showdown."); BroadcastGlobalMessage("" + (player?.PlayerName ?? num.ToString()) + " wins " + payouts.Values.Sum() + " Coins" + (string.IsNullOrWhiteSpace(text) ? "." : (" with " + text.Trim() + ".")) + ""); LogPokerPayoutDebug(game, poker, contributions, chipsBefore, payouts, pot, num); AddPokerHistoryAndStats(game, payouts, pot); foreach (PokerPlayerState seat in poker.Seats) { seat.RoundBet = 0L; seat.TotalContribution = 0L; } poker.CurrentBet = 0L; Plugin.Log.LogInfo((object)("[FCW Poker] Hand finished table=" + game.GameId + " pot=" + pot + " payouts=" + string.Join(",", payouts.Select((KeyValuePair pair) => pair.Key + ":" + pair.Value).ToArray()))); } private void LogPokerPayoutDebug(FehuGameTable game, PokerGameState state, Dictionary contributions, Dictionary chipsBefore, Dictionary payouts, long pot, long winner) { long value; string text = string.Join(", ", state.Seats.Select((PokerPlayerState p) => p.PlayerName + "=" + (contributions.TryGetValue(p.PlayerId, out value) ? value : 0)).ToArray()); string text2 = string.Join(", ", (from p in state.Seats where !p.Folded select p.PlayerName).ToArray()); string text3 = string.Join(", ", payouts.Select((KeyValuePair pair) => (WarfareManager.Instance?.Data?.GetPlayer(pair.Key)?.PlayerName ?? pair.Key.ToString()) + "=" + pair.Value).ToArray()); string text4 = string.Join(", ", state.Seats.Select((PokerPlayerState p) => p.PlayerName + " " + (chipsBefore.TryGetValue(p.PlayerId, out value) ? value : 0) + "->" + p.Chips).ToArray()); Plugin.Log.LogInfo((object)("[Poker Payout] table=" + game.GameId + " player contributions: " + text)); Plugin.Log.LogInfo((object)("[Poker Payout] main pot=" + pot + " side pots/log=" + string.Join(" | ", (state.HandLog ?? new List()).Where((string line) => line.StartsWith("Main Pot") || line.StartsWith("Side Pot")).ToArray()))); Plugin.Log.LogInfo((object)("[Poker Payout] eligible players: " + text2)); Plugin.Log.LogInfo((object)("[Poker Payout] winner=" + (WarfareManager.Instance?.Data?.GetPlayer(winner)?.PlayerName ?? winner.ToString()) + " payout amount: " + text3)); Plugin.Log.LogInfo((object)("[Poker Payout] chips before/after: " + text4)); } private void CashOutPokerPlayer(FehuGameTable game, long playerId, long sender) { PokerGameState poker = game.Poker; PokerPlayerState player = PokerEngine.GetPlayer(poker, playerId); if (player == null || PokerEngine.IsHandActive(poker)) { SendNotification(sender, "Cash out is available between hands only."); return; } long chips = player.Chips; poker.Seats.Remove(player); game.PlayerIds.Remove(playerId); game.SpectatorIds.Remove(playerId); game.EscrowTotal = Math.Max(0L, game.EscrowTotal - chips); if (chips > 0) { SendGamePayout(playerId, game.GameId, chips, FehuStakeType.Coins, game.StakePrefabName, "Poker cash out"); } if (game.HostPlayerId == playerId && poker.Seats.Count > 0) { game.HostPlayerId = poker.Seats[0].PlayerId; game.HostPlayerName = poker.Seats[0].PlayerName; } if (poker.Seats.Count == 0) { game.Status = FehuGameStatus.Finished; } PersistGames(); } private void RefundPokerTable(FehuGameTable game, string result) { if (game.PayoutIssued) { return; } if (game.StakeType == FehuStakeType.Territory) { game.Result = result + " | " + ResolveTerritoryStakeGame(game, 0L); } else if (game.Poker != null) { foreach (PokerPlayerState seat in game.Poker.Seats) { if (seat.Chips > 0) { SendGamePayout(seat.PlayerId, game.GameId, seat.Chips, FehuStakeType.Coins, game.StakePrefabName, "Poker table refund"); } } } else { foreach (long escrowPlayerId in game.EscrowPlayerIds) { SendGamePayout(escrowPlayerId, game.GameId, game.PokerBuyIn, FehuStakeType.Coins, game.StakePrefabName, "Poker buy-in refund"); } } if (game.StakeType != FehuStakeType.Territory) { game.Result = result; } game.Status = FehuGameStatus.Cancelled; game.FinishedAt = WarfareManager.Now(); game.EscrowCollecting = false; game.PayoutIssued = true; AddPokerCancellationHistory(game); PersistGames(); } private void AddPokerHistoryAndStats(FehuGameTable game, Dictionary payouts, long pot) { WarfareData data = WarfareManager.Instance.Data; PokerGameState poker = game.Poker; long winnerPlayerId = (from p in payouts orderby p.Value descending select p.Key).FirstOrDefault(); data.GameHistory.Add(new FehuGameHistoryEntry { GameId = game.GameId, GameType = FehuGameType.Poker, TableName = game.TableName, PlayerIds = new List(game.PlayerIds), WinnerPlayerId = winnerPlayerId, Result = game.Result + " | Hand " + poker.HandNumber, StakeType = game.StakeType, StakeAmount = game.PokerBuyIn, StakePrefabName = game.StakePrefabName, CompletedAt = WarfareManager.Now(), HostStakeTerritoryIds = new List(game.HostStakeTerritoryIds ?? new List()), ChallengerStakeTerritoryIds = new List(game.ChallengerStakeTerritoryIds ?? new List()), HostStakeClanId = game.HostStakeClanId, ChallengerStakeClanId = game.ChallengerStakeClanId, TerritoryTransferResult = ((game.StakeType == FehuStakeType.Territory) ? game.Result : "") }); foreach (PokerPlayerState seat in poker.Seats) { if (!data.GameStats.TryGetValue(seat.PlayerId, out var value)) { Dictionary gameStats = data.GameStats; long playerId = seat.PlayerId; FehuGamePlayerStats obj = new FehuGamePlayerStats { PlayerId = seat.PlayerId }; value = obj; gameStats[playerId] = obj; } value.GamesPlayed++; value.PokerGamesPlayed++; value.PokerBiggestPot = Math.Max(value.PokerBiggestPot, pot); if (payouts.TryGetValue(seat.PlayerId, out var value2) && value2 > 0) { value.Wins++; value.PokerHandsWon++; value.PokerBiggestWin = Math.Max(value.PokerBiggestWin, value2); value.PokerProfit += value2 - seat.TotalContribution; value.Profit += value2 - seat.TotalContribution; if (payouts.Count == 1) { value.PokerTablesWon++; } } else { value.Losses++; value.PokerProfit -= seat.TotalContribution; value.Profit -= seat.TotalContribution; } value.FavoriteGame = FehuGameType.Poker; } if (data.GameHistory.Count > 100) { data.GameHistory.RemoveAt(0); } } private void AddPokerCancellationHistory(FehuGameTable game) { WarfareData warfareData = WarfareManager.Instance?.Data; if (warfareData != null && !warfareData.GameHistory.Exists((FehuGameHistoryEntry entry) => entry.GameId == game.GameId && entry.GameType == FehuGameType.Poker)) { warfareData.GameHistory.Add(new FehuGameHistoryEntry { GameId = game.GameId, GameType = FehuGameType.Poker, TableName = game.TableName, PlayerIds = new List(game.PlayerIds), Result = game.Result, StakeType = game.StakeType, StakeAmount = game.PokerBuyIn, StakePrefabName = game.StakePrefabName, CompletedAt = game.FinishedAt, HostStakeTerritoryIds = new List(game.HostStakeTerritoryIds ?? new List()), ChallengerStakeTerritoryIds = new List(game.ChallengerStakeTerritoryIds ?? new List()), HostStakeClanId = game.HostStakeClanId, ChallengerStakeClanId = game.ChallengerStakeClanId, TerritoryTransferResult = ((game.StakeType == FehuStakeType.Territory) ? game.Result : "") }); } } private void TickPokerTables() { WarfareData warfareData = WarfareManager.Instance?.Data; if (warfareData == null) { return; } long num = WarfareManager.Now(); bool flag = false; foreach (FehuGameTable item in warfareData.Games.FindAll((FehuGameTable g) => g.GameType == FehuGameType.Poker && g.Poker != null && PokerEngine.IsHandActive(g.Poker))) { PokerGameState poker = item.Poker; foreach (PokerPlayerState item2 in poker.Seats.Where((PokerPlayerState p) => !p.Folded)) { if (IsChessPlayerOnline(warfareData, item2.PlayerId)) { item2.DisconnectedAt = 0L; } else if (item2.DisconnectedAt == 0L) { item2.DisconnectedAt = num; flag = true; } if (item2.DisconnectedAt > 0 && num - item2.DisconnectedAt >= 300) { item2.Folded = true; flag = true; } } if (poker.CurrentActorId != 0L && poker.LastActionAt > 0 && num - poker.LastActionAt >= 90) { PokerPlayerState player = PokerEngine.GetPlayer(poker, poker.CurrentActorId); if (player != null && !player.Folded && !player.AllIn) { player.Folded = true; poker.LastActionAt = num; flag = true; Plugin.Log.LogInfo((object)("[FCW Poker] Auto-fold timeout table=" + item.GameId + " player=" + player.PlayerId)); } } if (poker.CurrentActorId != 0L) { PokerPlayerState player2 = PokerEngine.GetPlayer(poker, poker.CurrentActorId); if (player2 != null && player2.Folded) { AdvancePokerAfterAction(item); } } } if (flag) { PersistGames(); } } private void TickGameTableCleanup() { WarfareData warfareData = WarfareManager.Instance?.Data; if (warfareData?.Games == null || warfareData.Games.Count == 0) { return; } long now = WarfareManager.Now(); using (List.Enumerator enumerator = warfareData.Games.FindAll((FehuGameTable g) => g.EscrowCollecting && g.Status == FehuGameStatus.Active && ((g.StartedAt > 0) ? (now - g.StartedAt) : (now - g.CreatedAt)) >= 180).GetEnumerator()) { if (enumerator.MoveNext()) { FehuGameTable current = enumerator.Current; CancelGameEscrow(current, "Stake collection timed out."); return; } } int num = warfareData.Games.RemoveAll(delegate(FehuGameTable game) { long num2 = ((game.FinishedAt > 0) ? game.FinishedAt : ((game.StartedAt > 0) ? game.StartedAt : game.CreatedAt)); if (num2 <= 0) { return false; } if (game.GameType == FehuGameType.Poker && game.PlayerIds.Count == 0) { return true; } if (game.Status == FehuGameStatus.Waiting) { return now - num2 >= 86400; } if (game.Status == FehuGameStatus.Cancelled) { return now - num2 >= 3600; } if (game.GameType == FehuGameType.Dice && game.Status == FehuGameStatus.Finished) { return now - num2 >= 300; } return game.Status == FehuGameStatus.Finished && now - num2 >= 86400; }); if (num > 0) { Plugin.Log.LogInfo((object)("[FCW Games] Archived " + num + " expired game table(s). History and stats were preserved.")); PersistGames(); } } public void RequestContractAction(int action, int contractId = 0, ContractType type = ContractType.Gathering) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_0006: Expected O, but got Unknown ZPackage val = new ZPackage(); val.Write(LocalId); val.Write(action); val.Write(contractId); val.Write((int)type); ToServer("FCW_ContractAction", val); } private void RPC_OnContractAction(long sender, ZPackage pkg) { if (IsServer) { long playerId = pkg.ReadLong(); int num = pkg.ReadInt(); int contractId = pkg.ReadInt(); ContractType type = (ContractType)pkg.ReadInt(); WarfareManager instance = WarfareManager.Instance; SendNotification(sender, num switch { 1 => instance?.AcceptContract(playerId, contractId) ?? "Failed", 2 => instance?.CompleteContract(playerId, contractId) ?? "Failed", 10 => instance?.CreateTestContract(playerId, type) ?? "Failed", 11 => instance?.AdminContractAction(playerId, 1, contractId) ?? "Failed", 12 => instance?.AdminContractAction(playerId, 2, contractId) ?? "Failed", 20 => instance?.CreateTestContractPreset(playerId, 1) ?? "Failed", 21 => instance?.CreateTestContractPreset(playerId, 2) ?? "Failed", 22 => instance?.CreateTestContractPreset(playerId, 3) ?? "Failed", 23 => instance?.CreateTestContractPreset(playerId, 4) ?? "Failed", 24 => instance?.CreateTestContractPreset(playerId, 5) ?? "Failed", 25 => instance?.CreateTestContractPreset(playerId, 6) ?? "Failed", _ => "Unknown contract action.", }); SyncAllDataToClients(); } } private void RPC_OnFreebornFriend(long sender, ZPackage pkg) { if (IsServer) { long playerId = pkg.ReadLong(); long num = pkg.ReadLong(); int num2 = pkg.ReadInt(); WarfareManager instance = WarfareManager.Instance; SendNotification(sender, num2 switch { 1 => instance?.SendFreebornFriendRequest(playerId, num) ?? "Failed", 2 => instance?.AcceptFreebornFriendRequest(playerId, num) ?? "Failed", 3 => instance?.RemoveFreebornFriend(playerId, num) ?? "Failed", 10 => instance?.SendClanFoundingInvite(playerId, num) ?? "Failed", 11 => instance?.AcceptClanFoundingInvite(playerId, num) ?? "Failed", 12 => instance?.DeclineClanFoundingInvite(playerId, num) ?? "Failed", _ => "Unknown Freeborn action.", }); SyncAllDataToClients(); } } public void RequestAbandonFreebornHomestead() { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_0006: Expected O, but got Unknown ZPackage val = new ZPackage(); val.Write(LocalId); ToServer("FCW_AbandonFreebornHomestead", val); } private void RPC_OnAbandonFreebornHomestead(long sender, ZPackage pkg) { if (IsServer) { string message = WarfareManager.Instance?.AbandonFreebornHomestead(pkg.ReadLong()) ?? "Failed"; SendNotification(sender, message); SyncAllDataToClients(); } } public void RequestLeaveFreebornLife() { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_0006: Expected O, but got Unknown ZPackage val = new ZPackage(); val.Write(LocalId); ToServer("FCW_LeaveFreebornLife", val); } private void RPC_OnLeaveFreebornLife(long sender, ZPackage pkg) { if (IsServer) { long playerId = pkg.ReadLong(); string message = WarfareManager.Instance?.LeaveFreebornLife(playerId) ?? "Unable to leave Freeborn life."; SendNotification(sender, message); SyncAllDataToClients(); } } public void RequestChallengeFreeborn(Vector2i grid) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_0006: Expected O, but got Unknown //IL_0012: Unknown result type (might be due to invalid IL or missing references) //IL_001e: Unknown result type (might be due to invalid IL or missing references) ZPackage val = new ZPackage(); val.Write(LocalId); val.Write(grid.x); val.Write(grid.y); ToServer("FCW_ChallengeFreeborn", val); } private void RPC_OnChallengeFreeborn(long sender, ZPackage pkg) { //IL_002b: Unknown result type (might be due to invalid IL or missing references) if (IsServer) { long playerId = pkg.ReadLong(); int num = pkg.ReadInt(); int num2 = pkg.ReadInt(); string message = WarfareManager.Instance?.ChallengeFreeborn(playerId, new Vector2i(num, num2)) ?? "Failed"; SendNotification(sender, message); SyncAllDataToClients(); } } public void SendNotification(long peerUID, string message) { ZRoutedRpc instance = ZRoutedRpc.instance; if (instance != null) { Plugin.Log.LogInfo((object)("[FCW RPC] Sending notification to peer=" + peerUID + ": " + message)); instance.InvokeRoutedRPC(peerUID, "FCW_Notification", new object[1] { message }); } else { Plugin.Log.LogWarning((object)("[FCW RPC] Cannot send notification to peer=" + peerUID + ": ZRoutedRpc.instance is null.")); } } public void BroadcastGlobalMessage(string message) { ZRoutedRpc instance = ZRoutedRpc.instance; if (instance != null) { instance.InvokeRoutedRPC(ZRoutedRpc.Everybody, "FCW_GlobalMessage", new object[1] { message }); } } private void RPC_OnNotification(long sender, string message) { Plugin.Log.LogInfo((object)("[FCW RPC] Notification received from sender=" + sender + ": " + message)); if (!string.IsNullOrEmpty(message) && message.IndexOf("vault", StringComparison.OrdinalIgnoreCase) >= 0) { Plugin.Log.LogInfo((object)("[FCW Vault] CLIENT deposit denied reason=" + message)); } this.OnNotificationReceived?.Invoke(message); } private void RPC_OnGlobalMessage(long sender, string message) { this.OnNotificationReceived?.Invoke(message); } } } namespace FortecaClanWarfare.Data { [Serializable] public class Alliance { public int AllianceId; public string AllianceName = ""; public int AllianceLeaderClanId; public List MemberClanIds = new List(); public float AllianceColorR = 0.2f; public float AllianceColorG = 0.45f; public float AllianceColorB = 1f; public long CreatedAt; public List PendingInvites = new List(); public bool HasMember(int clanId) { if (MemberClanIds != null) { return MemberClanIds.Contains(clanId); } return false; } } [Serializable] public class CaptureSession { public int TerritoryGridX; public int TerritoryGridY; public int AttackerClanId; public int DefenderClanId; public float Progress; public float Threshold = 100f; public int CombatPoints; public long StartTimestamp; public long LastAttackerSeenTime; public long LastDefenderMajorityTime; public bool Siege; public Vector2i GridPosition => new Vector2i(TerritoryGridX, TerritoryGridY); public float Progress01 { get { if (!(Threshold <= 0f)) { return Mathf.Clamp01(Progress / Threshold); } return 0f; } } } public enum ChessMatchResult { Active, WhiteWinCheckmate, BlackWinCheckmate, WhiteWinResign, BlackWinResign, WhiteWinTimeout, BlackWinTimeout, DrawAgreement, DrawStalemate, DrawRepetition, DrawFiftyMove, DrawInsufficientMaterial } [Serializable] public class ChessMoveRecord { public int From; public int To; public char Piece; public char Captured; public string Notation = ""; public string PreviousBoard = ""; public bool PreviousWhiteToMove; public bool PreviousWhiteKingSide; public bool PreviousWhiteQueenSide; public bool PreviousBlackKingSide; public bool PreviousBlackQueenSide; public int PreviousEnPassant = -1; public int PreviousHalfMove; public long PreviousWhiteSeconds; public long PreviousBlackSeconds; } [Serializable] public class ChessGameState { public string Board = "RNBQKBNRPPPPPPPP................................pppppppprnbqkbnr"; public long WhitePlayerId; public long BlackPlayerId; public bool WhiteToMove = true; public bool WhiteKingSide = true; public bool WhiteQueenSide = true; public bool BlackKingSide = true; public bool BlackQueenSide = true; public int EnPassantSquare = -1; public int HalfMove; public int FullMove = 1; public int TimeControlSeconds; public long WhiteSeconds; public long BlackSeconds; public long LastClockTick; public long DrawOfferedBy; public long UndoRequestedBy; public long WhiteDisconnectedAt; public long BlackDisconnectedAt; public ChessMatchResult Result; public List Moves = new List(); } public static class ChessRuleEngine { private static bool _debugValidationRan; public static bool RunDebugValidation(out string reason) { reason = "OK"; if (_debugValidationRan) { return true; } try { ChessGameState chessGameState = Create(1L, 2L, 0); if (!GetLegalMoves(chessGameState, 12).Contains(20) || !GetLegalMoves(chessGameState, 12).Contains(28) || !GetLegalMoves(chessGameState, 6).Contains(21)) { reason = "Opening pawn or knight move failed."; return false; } if (TryMove(Create(1L, 2L, 0), 1L, 12, 36, 'Q', out var _, out var _)) { reason = "Illegal pawn move accepted."; return false; } if (!TryMove(chessGameState, 1L, 12, 28, 'Q', out var reason3, out var result2) || !TryMove(chessGameState, 2L, 48, 40, 'Q', out reason3, out result2) || !TryMove(chessGameState, 1L, 28, 36, 'Q', out reason3, out result2) || !TryMove(chessGameState, 2L, 51, 35, 'Q', out reason3, out result2) || !TryMove(chessGameState, 1L, 36, 43, 'Q', out reason3, out result2) || chessGameState.Board[35] != '.') { reason = "En passant failed."; return false; } char[] array = new string('.', 64).ToCharArray(); array[4] = 'K'; array[60] = 'k'; array[2] = 'B'; array[0] = 'R'; array[3] = 'Q'; ChessGameState chessGameState2 = Create(1L, 2L, 0); chessGameState2.Board = new string(array); if (!GetLegalMoves(chessGameState2, 2).Contains(11) || !GetLegalMoves(chessGameState2, 0).Contains(8) || !GetLegalMoves(chessGameState2, 3).Contains(11) || !GetLegalMoves(chessGameState2, 4).Contains(5)) { reason = "Bishop, rook, queen, or king move failed."; return false; } char[] array2 = new string('.', 64).ToCharArray(); array2[0] = 'R'; array2[4] = 'K'; array2[7] = 'R'; array2[60] = 'k'; ChessGameState chessGameState3 = Create(1L, 2L, 0); chessGameState3.Board = new string(array2); if (!GetLegalMoves(chessGameState3, 4).Contains(6) || !GetLegalMoves(chessGameState3, 4).Contains(2)) { reason = "Castling failed."; return false; } char[] array3 = new string('.', 64).ToCharArray(); array3[0] = 'K'; array3[63] = 'k'; array3[48] = 'P'; ChessGameState chessGameState4 = Create(1L, 2L, 0); chessGameState4.Board = new string(array3); if (!TryMove(chessGameState4, 1L, 48, 56, 'N', out reason3, out result2) || chessGameState4.Board[56] != 'N') { reason = "Promotion failed."; return false; } ChessGameState state = Create(1L, 2L, 0); if (!TryMove(state, 1L, 13, 21, 'Q', out reason3, out result2) || !TryMove(state, 2L, 52, 36, 'Q', out reason3, out result2) || !TryMove(state, 1L, 14, 30, 'Q', out reason3, out result2) || !TryMove(state, 2L, 59, 31, 'Q', out reason3, out result2) || result2 != ChessMatchResult.BlackWinCheckmate) { reason = "Checkmate detection failed."; return false; } char[] array4 = new string('.', 64).ToCharArray(); array4[56] = 'k'; array4[42] = 'K'; array4[50] = 'Q'; ChessGameState chessGameState5 = Create(1L, 2L, 0); chessGameState5.Board = new string(array4); chessGameState5.WhiteToMove = false; if (IsInCheck(chessGameState5, white: false) || HasAnyLegalMove(chessGameState5)) { reason = "Stalemate detection failed."; return false; } char[] array5 = new string('.', 64).ToCharArray(); array5[4] = 'K'; array5[12] = 'R'; array5[56] = 'k'; array5[60] = 'r'; ChessGameState chessGameState6 = Create(1L, 2L, 0); chessGameState6.Board = new string(array5); if (GetLegalMoves(chessGameState6, 12).Contains(13)) { reason = "Move into check was accepted."; return false; } _debugValidationRan = true; return true; } catch (Exception ex) { reason = ex.Message; return false; } } public static ChessGameState Create(long whitePlayerId, long blackPlayerId, int timeControlSeconds) { return new ChessGameState { WhitePlayerId = whitePlayerId, BlackPlayerId = blackPlayerId, TimeControlSeconds = timeControlSeconds, WhiteSeconds = timeControlSeconds, BlackSeconds = timeControlSeconds, LastClockTick = DateTimeOffset.UtcNow.ToUnixTimeSeconds() }; } public static bool IsWhite(char piece) { if (piece >= 'A') { return piece <= 'Z'; } return false; } public static bool IsBlack(char piece) { if (piece >= 'a') { return piece <= 'z'; } return false; } public static bool IsPlayerWhite(ChessGameState state, long playerId) { if (state != null) { return state.WhitePlayerId == playerId; } return false; } public static string SquareName(int square) { if (square >= 0 && square < 64) { return ((char)(97 + square % 8)).ToString() + (square / 8 + 1); } return "?"; } public static List GetLegalMoves(ChessGameState state, int from) { List list = new List(); if (state == null || from < 0 || from >= 64 || state.Board == null || state.Board.Length != 64) { return list; } char c = state.Board[from]; if (c == '.' || IsWhite(c) != state.WhiteToMove) { return list; } foreach (int pseudoMove in GetPseudoMoves(state, from, attacks: false)) { ChessGameState chessGameState = Clone(state); ApplyUnchecked(chessGameState, from, pseudoMove, 'Q', null); if (!IsInCheck(chessGameState, !chessGameState.WhiteToMove)) { list.Add(pseudoMove); } } return list; } public static bool TryMove(ChessGameState state, long playerId, int from, int to, char promotion, out string reason, out ChessMatchResult result) { reason = ""; result = ChessMatchResult.Active; if (state == null || state.Result != ChessMatchResult.Active) { reason = "Match is not active."; return false; } bool flag = IsPlayerWhite(state, playerId); if ((state.WhiteToMove && !flag) || (!state.WhiteToMove && flag)) { reason = "It is not your turn."; return false; } if (from < 0 || to < 0 || from >= 64 || to >= 64 || !GetLegalMoves(state, from).Contains(to)) { reason = "Illegal chess move."; return false; } char c = state.Board[from]; if (char.ToUpperInvariant(c) == 'P' && (to / 8 == 0 || to / 8 == 7) && "QRBNqrbn".IndexOf(promotion) < 0) { promotion = (state.WhiteToMove ? 'Q' : 'q'); } ChessMoveRecord chessMoveRecord = CaptureRecord(state, from, to); ApplyUnchecked(state, from, to, promotion, chessMoveRecord); state.Moves.Add(chessMoveRecord); bool whiteToMove = state.WhiteToMove; bool flag2 = IsInCheck(state, whiteToMove); if (!HasAnyLegalMove(state)) { state.Result = ((!flag2) ? ChessMatchResult.DrawStalemate : ((!whiteToMove) ? ChessMatchResult.WhiteWinCheckmate : ChessMatchResult.BlackWinCheckmate)); } else if (state.HalfMove >= 100) { state.Result = ChessMatchResult.DrawFiftyMove; } else if (IsInsufficientMaterial(state)) { state.Result = ChessMatchResult.DrawInsufficientMaterial; } else if (IsThreefoldRepetition(state)) { state.Result = ChessMatchResult.DrawRepetition; } chessMoveRecord.Notation = BuildNotation(c, from, to, chessMoveRecord.Captured != '.', promotion, flag2, state.Result); result = state.Result; return true; } public static bool UndoLastMove(ChessGameState state) { if (state == null || state.Moves == null || state.Moves.Count == 0 || state.Result != ChessMatchResult.Active) { return false; } ChessMoveRecord chessMoveRecord = state.Moves[state.Moves.Count - 1]; state.Board = chessMoveRecord.PreviousBoard; state.WhiteToMove = chessMoveRecord.PreviousWhiteToMove; state.WhiteKingSide = chessMoveRecord.PreviousWhiteKingSide; state.WhiteQueenSide = chessMoveRecord.PreviousWhiteQueenSide; state.BlackKingSide = chessMoveRecord.PreviousBlackKingSide; state.BlackQueenSide = chessMoveRecord.PreviousBlackQueenSide; state.EnPassantSquare = chessMoveRecord.PreviousEnPassant; state.HalfMove = chessMoveRecord.PreviousHalfMove; state.WhiteSeconds = chessMoveRecord.PreviousWhiteSeconds; state.BlackSeconds = chessMoveRecord.PreviousBlackSeconds; state.LastClockTick = DateTimeOffset.UtcNow.ToUnixTimeSeconds(); state.Moves.RemoveAt(state.Moves.Count - 1); return true; } public static bool IsInCheck(ChessGameState state, bool white) { if (state == null || string.IsNullOrEmpty(state.Board)) { return false; } char value = (white ? 'K' : 'k'); int num = state.Board.IndexOf(value); if (num >= 0) { return IsSquareAttacked(state, num, !white); } return false; } public static bool HasAnyLegalMove(ChessGameState state) { for (int i = 0; i < 64; i++) { if (state.Board[i] != '.' && IsWhite(state.Board[i]) == state.WhiteToMove && GetLegalMoves(state, i).Count > 0) { return true; } } return false; } private static ChessMoveRecord CaptureRecord(ChessGameState state, int from, int to) { return new ChessMoveRecord { From = from, To = to, Piece = state.Board[from], Captured = state.Board[to], PreviousBoard = state.Board, PreviousWhiteToMove = state.WhiteToMove, PreviousWhiteKingSide = state.WhiteKingSide, PreviousWhiteQueenSide = state.WhiteQueenSide, PreviousBlackKingSide = state.BlackKingSide, PreviousBlackQueenSide = state.BlackQueenSide, PreviousEnPassant = state.EnPassantSquare, PreviousHalfMove = state.HalfMove, PreviousWhiteSeconds = state.WhiteSeconds, PreviousBlackSeconds = state.BlackSeconds }; } private static void ApplyUnchecked(ChessGameState state, int from, int to, char promotion, ChessMoveRecord record) { char[] array = state.Board.ToCharArray(); char c = array[from]; char c2 = array[to]; bool flag = IsWhite(c); int value = to % 8 - from % 8; if (char.ToUpperInvariant(c) == 'P' && to == state.EnPassantSquare && c2 == '.' && Math.Abs(value) == 1) { int num = to + (flag ? (-8) : 8); c2 = array[num]; array[num] = '.'; if (record != null) { record.Captured = c2; } } array[from] = '.'; array[to] = c; if (char.ToUpperInvariant(c) == 'K' && Math.Abs(to - from) == 2) { int num2 = ((to > from) ? (from + 3) : (from - 4)); int num3 = ((to > from) ? (from + 1) : (from - 1)); array[num3] = array[num2]; array[num2] = '.'; } if (char.ToUpperInvariant(c) == 'P' && (to / 8 == 0 || to / 8 == 7)) { array[to] = (flag ? char.ToUpperInvariant(promotion) : char.ToLowerInvariant(promotion)); } if (c == 'K') { state.WhiteKingSide = false; state.WhiteQueenSide = false; } if (c == 'k') { state.BlackKingSide = false; state.BlackQueenSide = false; } if (from == 0 || to == 0) { state.WhiteQueenSide = false; } if (from == 7 || to == 7) { state.WhiteKingSide = false; } if (from == 56 || to == 56) { state.BlackQueenSide = false; } if (from == 63 || to == 63) { state.BlackKingSide = false; } state.EnPassantSquare = ((char.ToUpperInvariant(c) == 'P' && Math.Abs(to - from) == 16) ? ((from + to) / 2) : (-1)); state.HalfMove = ((char.ToUpperInvariant(c) != 'P' && c2 == '.') ? (state.HalfMove + 1) : 0); if (!flag) { state.FullMove++; } state.WhiteToMove = !state.WhiteToMove; state.DrawOfferedBy = 0L; state.UndoRequestedBy = 0L; state.Board = new string(array); state.LastClockTick = DateTimeOffset.UtcNow.ToUnixTimeSeconds(); } private static List GetPseudoMoves(ChessGameState state, int from, bool attacks) { List moves = new List(); char c = state.Board[from]; bool white = IsWhite(c); char c2 = char.ToUpperInvariant(c); int x = from % 8; int y = from / 8; Action action = delegate(int dx, int dy, bool repeat) { int num12 = x + dx; int num13 = y + dy; while (num12 >= 0 && num12 < 8 && num13 >= 0 && num13 < 8) { int num14 = num13 * 8 + num12; char c4 = state.Board[num14]; if (c4 != '.') { if (IsWhite(c4) != white && (attacks || char.ToUpperInvariant(c4) != 'K')) { moves.Add(num14); } break; } moves.Add(num14); if (!repeat) { break; } num12 += dx; num13 += dy; } }; switch (c2) { case 'P': { int num2 = (white ? 1 : (-1)); int num3 = (white ? 1 : 6); int num4 = (y + num2) * 8 + x; if (!attacks && y + num2 >= 0 && y + num2 < 8 && state.Board[num4] == '.') { moves.Add(num4); int num5 = (y + 2 * num2) * 8 + x; if (y == num3 && state.Board[num5] == '.') { moves.Add(num5); } } for (int num6 = -1; num6 <= 1; num6 += 2) { int num7 = x + num6; int num8 = y + num2; if (num7 >= 0 && num7 <= 7 && num8 >= 0 && num8 <= 7) { int num9 = num8 * 8 + num7; char c3 = state.Board[num9]; if (attacks || (c3 != '.' && IsWhite(c3) != white && char.ToUpperInvariant(c3) != 'K') || num9 == state.EnPassantSquare) { moves.Add(num9); } } } break; } case 'N': { int[] array = new int[8] { -2, -1, 1, 2, 2, 1, -1, -2 }; int[] array2 = new int[8] { 1, 2, 2, 1, -1, -2, -2, -1 }; for (int num = 0; num < 8; num++) { action(array[num], array2[num], arg3: false); } break; } case 'B': case 'Q': action(1, 1, arg3: true); action(1, -1, arg3: true); action(-1, 1, arg3: true); action(-1, -1, arg3: true); break; } if (c2 == 'R' || c2 == 'Q') { action(1, 0, arg3: true); action(-1, 0, arg3: true); action(0, 1, arg3: true); action(0, -1, arg3: true); } if (c2 == 'K') { for (int num10 = -1; num10 <= 1; num10++) { for (int num11 = -1; num11 <= 1; num11++) { if (num10 != 0 || num11 != 0) { action(num10, num11, arg3: false); } } } if (!attacks && !IsInCheck(state, white)) { if (white && from == 4 && state.WhiteKingSide && state.Board[5] == '.' && state.Board[6] == '.' && state.Board[7] == 'R' && !IsSquareAttacked(state, 5, byWhite: false) && !IsSquareAttacked(state, 6, byWhite: false)) { moves.Add(6); } if (white && from == 4 && state.WhiteQueenSide && state.Board[1] == '.' && state.Board[2] == '.' && state.Board[3] == '.' && state.Board[0] == 'R' && !IsSquareAttacked(state, 3, byWhite: false) && !IsSquareAttacked(state, 2, byWhite: false)) { moves.Add(2); } if (!white && from == 60 && state.BlackKingSide && state.Board[61] == '.' && state.Board[62] == '.' && state.Board[63] == 'r' && !IsSquareAttacked(state, 61, byWhite: true) && !IsSquareAttacked(state, 62, byWhite: true)) { moves.Add(62); } if (!white && from == 60 && state.BlackQueenSide && state.Board[57] == '.' && state.Board[58] == '.' && state.Board[59] == '.' && state.Board[56] == 'r' && !IsSquareAttacked(state, 59, byWhite: true) && !IsSquareAttacked(state, 58, byWhite: true)) { moves.Add(58); } } } return moves; } private static bool IsSquareAttacked(ChessGameState state, int square, bool byWhite) { for (int i = 0; i < 64; i++) { if (state.Board[i] != '.' && IsWhite(state.Board[i]) == byWhite && GetPseudoMoves(state, i, attacks: true).Contains(square)) { return true; } } return false; } private static ChessGameState Clone(ChessGameState source) { return new ChessGameState { Board = source.Board, WhitePlayerId = source.WhitePlayerId, BlackPlayerId = source.BlackPlayerId, WhiteToMove = source.WhiteToMove, WhiteKingSide = source.WhiteKingSide, WhiteQueenSide = source.WhiteQueenSide, BlackKingSide = source.BlackKingSide, BlackQueenSide = source.BlackQueenSide, EnPassantSquare = source.EnPassantSquare, HalfMove = source.HalfMove, FullMove = source.FullMove, WhiteSeconds = source.WhiteSeconds, BlackSeconds = source.BlackSeconds, LastClockTick = source.LastClockTick, Result = source.Result }; } private static bool IsThreefoldRepetition(ChessGameState state) { if (state?.Moves == null) { return false; } string text = PositionKey(state.Board, state.WhiteToMove, state.WhiteKingSide, state.WhiteQueenSide, state.BlackKingSide, state.BlackQueenSide, state.EnPassantSquare); int num = 1; foreach (ChessMoveRecord move in state.Moves) { if (PositionKey(move.PreviousBoard, move.PreviousWhiteToMove, move.PreviousWhiteKingSide, move.PreviousWhiteQueenSide, move.PreviousBlackKingSide, move.PreviousBlackQueenSide, move.PreviousEnPassant) == text && ++num >= 3) { return true; } } return false; } private static string PositionKey(string board, bool whiteToMove, bool whiteKingSide, bool whiteQueenSide, bool blackKingSide, bool blackQueenSide, int enPassant) { return board + "|" + whiteToMove + "|" + whiteKingSide + whiteQueenSide + blackKingSide + blackQueenSide + "|" + enPassant; } private static bool IsInsufficientMaterial(ChessGameState state) { if (state == null || state.Board == null || state.Board.Length != 64) { return false; } List list = new List(); List list2 = new List(); for (int i = 0; i < state.Board.Length; i++) { char c = state.Board[i]; if (c != '.' && char.ToUpperInvariant(c) != 'K') { char c2 = char.ToUpperInvariant(c); if (c2 != 'B' && c2 != 'N') { return false; } if (IsWhite(c)) { list.Add(i); } else { list2.Add(i); } } } switch (list.Count + list2.Count) { case 0: return true; case 1: return true; case 2: if (list.Count == 1 && list2.Count == 1) { char c3 = state.Board[list[0]]; char c4 = state.Board[list2[0]]; if (char.ToUpperInvariant(c3) == 'B' && char.ToUpperInvariant(c4) == 'B') { return SquareColor(list[0]) == SquareColor(list2[0]); } } break; } return false; } private static bool SquareColor(int square) { return (square % 8 + square / 8) % 2 == 0; } private static string BuildNotation(char piece, int from, int to, bool capture, char promotion, bool check, ChessMatchResult result) { if (char.ToUpperInvariant(piece) == 'K' && Math.Abs(to - from) == 2) { if (to <= from) { return "O-O-O"; } return "O-O"; } string text = ((char.ToUpperInvariant(piece) == 'P') ? "" : char.ToUpperInvariant(piece).ToString()) + SquareName(from) + (capture ? "x" : "-") + SquareName(to); if (char.ToUpperInvariant(piece) == 'P' && (to / 8 == 0 || to / 8 == 7)) { text = text + "=" + char.ToUpperInvariant(promotion); } if (result == ChessMatchResult.WhiteWinCheckmate || result == ChessMatchResult.BlackWinCheckmate) { text += "#"; } else if (check) { text += "+"; } return text; } } [Serializable] public class Clan { public int ClanId; public string Name; public string Tag; public int IconIndex; public float ColorR = 0.7f; public float ColorG = 0.5f; public float ColorB = 0.2f; public long FounderId; public long JarlId; public long Treasury; public long DebtStartTimestamp; public List TreasuryHistory = new List(); public int VaultTier = 1; public int ExtraTerritorySlots; public long CreatedTimestamp; public long NextElectionTimestamp; public long Bounty; public long LastActiveTimestamp; public string ClanEmblemUrl; public string ClanEmblemName; public string ClanDiscordUrl; public string ClanWebsiteUrl; public string ClanSteamGroupUrl; public string ClanCustomUrl; public string ClanCustomLabel; public string ClanCustomUrl2; public string ClanCustomLabel2; public int ResourceTaxPercent = 10; public int CoinTaxPercent = 10; public long TotalResourceTaxCollected; public long TotalCoinTaxCollected; [NonSerialized] public long SessionResourceTaxCollected; [NonSerialized] public long SessionCoinTaxCollected; public Dictionary ElectionVotes = new Dictionary(); public Color Color => new Color(ColorR, ColorG, ColorB, 1f); public string Symbol => Tag; public void SetColor(Color c) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) //IL_000d: Unknown result type (might be due to invalid IL or missing references) //IL_0019: Unknown result type (might be due to invalid IL or missing references) ColorR = c.r; ColorG = c.g; ColorB = c.b; } } public enum ClanMapDrawingType { Freehand, Arrow } [Serializable] public sealed class ClanMapDrawingPoint { public short X; public short Y; public float NormalizedX => (float)X / 32767f; public float NormalizedY => (float)Y / 32767f; public static ClanMapDrawingPoint From(float x, float y) { return new ClanMapDrawingPoint { X = (short)Mathf.Clamp(Mathf.RoundToInt(x * 32767f), -32767, 32767), Y = (short)Mathf.Clamp(Mathf.RoundToInt(y * 32767f), -32767, 32767) }; } public void Write(ZPackage p) { p.Write((int)X); p.Write((int)Y); } public static ClanMapDrawingPoint Read(ZPackage p) { int num = p.ReadInt(); int num2 = p.ReadInt(); if (num < -32767 || num > 32767 || num2 < -32767 || num2 > 32767) { throw new InvalidOperationException("Invalid drawing point."); } return new ClanMapDrawingPoint { X = (short)num, Y = (short)num2 }; } } [Serializable] public sealed class ClanMapDrawing { public string DrawingID = ""; public long WorldUID; public int ClanID; public long CreatedByPlayerID; public string CreatedByName = ""; public long CreatedUtc; public long UpdatedByPlayerID; public string UpdatedByName = ""; public long UpdatedUtc; public int Revision = 1; public ClanMapDrawingType DrawingType; public int ColorIndex; public int Thickness; public bool IsImportant; public string Title = ""; public List Points = new List(); } public static class ClanMapDrawingProtocol { public const int MaxDrawingsPerClan = 100; public const int MaxPointsPerDrawing = 512; public const int MaxTotalPointsPerClan = 20000; public const int MaxTitleLength = 48; public const int ColorCount = 8; public const int ThicknessCount = 3; public const int MaxSnapshotBytes = 524288; public static bool ValidType(int x) { if (x != 0) { return x == 1; } return true; } } [Serializable] public sealed class ClanMapDrawingDto { public string DrawingID = ""; public int DrawingType; public int ColorIndex; public int Thickness; public bool IsImportant; public string Title = ""; public string CreatedByName = ""; public string UpdatedByName = ""; public long CreatedUtc; public long UpdatedUtc; public int Revision; public List Points = new List(); public void Write(ZPackage p) { p.Write(DrawingID ?? ""); p.Write(DrawingType); p.Write(ColorIndex); p.Write(Thickness); p.Write(IsImportant); p.Write(Title ?? ""); p.Write(CreatedByName ?? ""); p.Write(UpdatedByName ?? ""); p.Write(CreatedUtc); p.Write(UpdatedUtc); p.Write(Revision); int num = Math.Min(Points?.Count ?? 0, 512); p.Write(num); for (int i = 0; i < num; i++) { Points[i].Write(p); } } public static ClanMapDrawingDto Read(ZPackage p, ref int total) { ClanMapDrawingDto clanMapDrawingDto = new ClanMapDrawingDto { DrawingID = p.ReadString(), DrawingType = p.ReadInt(), ColorIndex = p.ReadInt(), Thickness = p.ReadInt(), IsImportant = p.ReadBool(), Title = p.ReadString(), CreatedByName = p.ReadString(), UpdatedByName = p.ReadString(), CreatedUtc = p.ReadLong(), UpdatedUtc = p.ReadLong(), Revision = p.ReadInt() }; int num = p.ReadInt(); if (num < 2 || num > 512 || total + num > 20000) { throw new InvalidOperationException("Invalid drawing point count."); } total += num; for (int i = 0; i < num; i++) { clanMapDrawingDto.Points.Add(ClanMapDrawingPoint.Read(p)); } return clanMapDrawingDto; } public static ClanMapDrawingDto From(ClanMapDrawing d) { return new ClanMapDrawingDto { DrawingID = d.DrawingID, DrawingType = (int)d.DrawingType, ColorIndex = d.ColorIndex, Thickness = d.Thickness, IsImportant = d.IsImportant, Title = d.Title, CreatedByName = d.CreatedByName, UpdatedByName = d.UpdatedByName, CreatedUtc = d.CreatedUtc, UpdatedUtc = d.UpdatedUtc, Revision = d.Revision, Points = new List(d.Points) }; } } [Serializable] public sealed class ClanMapDrawingSnapshot { public int ProtocolVersion = 5; public long WorldUID; public int ClanID; public long DrawingRevision; public long ServerTimestampUtc; public List Drawings = new List(); public void Write(ZPackage p) { p.Write(ProtocolVersion); p.Write(WorldUID); p.Write(ClanID); p.Write(DrawingRevision); p.Write(ServerTimestampUtc); int num = Math.Min(Drawings?.Count ?? 0, 100); p.Write(num); for (int i = 0; i < num; i++) { Drawings[i].Write(p); } } public static ClanMapDrawingSnapshot Read(ZPackage p) { ClanMapDrawingSnapshot clanMapDrawingSnapshot = new ClanMapDrawingSnapshot { ProtocolVersion = p.ReadInt(), WorldUID = p.ReadLong(), ClanID = p.ReadInt(), DrawingRevision = p.ReadLong(), ServerTimestampUtc = p.ReadLong() }; int num = p.ReadInt(); if (num < 0 || num > 100) { throw new InvalidOperationException("Invalid drawing count."); } int total = 0; for (int i = 0; i < num; i++) { clanMapDrawingSnapshot.Drawings.Add(ClanMapDrawingDto.Read(p, ref total)); } return clanMapDrawingSnapshot; } } [Serializable] public sealed class ClanMapDrawingMutationRequest { public int ProtocolVersion = 5; public string DrawingID = ""; public int ExpectedRevision; public int DrawingType; public int ColorIndex; public int Thickness; public bool IsImportant; public string Title = ""; public List Points = new List(); public void Write(ZPackage p) { p.Write(ProtocolVersion); p.Write(DrawingID ?? ""); p.Write(ExpectedRevision); p.Write(DrawingType); p.Write(ColorIndex); p.Write(Thickness); p.Write(IsImportant); p.Write(Title ?? ""); int num = Math.Min(Points?.Count ?? 0, 512); p.Write(num); for (int i = 0; i < num; i++) { Points[i].Write(p); } } public static ClanMapDrawingMutationRequest Read(ZPackage p) { ClanMapDrawingMutationRequest clanMapDrawingMutationRequest = new ClanMapDrawingMutationRequest { ProtocolVersion = p.ReadInt(), DrawingID = p.ReadString(), ExpectedRevision = p.ReadInt(), DrawingType = p.ReadInt(), ColorIndex = p.ReadInt(), Thickness = p.ReadInt(), IsImportant = p.ReadBool(), Title = p.ReadString() }; int num = p.ReadInt(); if (num < 2 || num > 512) { throw new InvalidOperationException("Invalid drawing points."); } for (int i = 0; i < num; i++) { clanMapDrawingMutationRequest.Points.Add(ClanMapDrawingPoint.Read(p)); } return clanMapDrawingMutationRequest; } } [Serializable] public sealed class ClanMapDrawingDeleteRequest { public int ProtocolVersion = 5; public string DrawingID = ""; public int ExpectedRevision; public void Write(ZPackage p) { p.Write(ProtocolVersion); p.Write(DrawingID ?? ""); p.Write(ExpectedRevision); } public static ClanMapDrawingDeleteRequest Read(ZPackage p) { return new ClanMapDrawingDeleteRequest { ProtocolVersion = p.ReadInt(), DrawingID = p.ReadString(), ExpectedRevision = p.ReadInt() }; } } public static class ClanMapProtocol { public const int Version = 5; public const int MaxTerritories = 512; public const int MaxPointsPerPolygon = 256; public const int MaxTotalGeometryPoints = 50000; public const int MaxSnapshotBytes = 524288; public const int GeometryQuantization = 32767; } [Serializable] public sealed class ClanMapOpenRequest { public int ProtocolVersion = 5; public void Write(ZPackage p) { p.Write(ProtocolVersion); } public static ClanMapOpenRequest Read(ZPackage p) { return new ClanMapOpenRequest { ProtocolVersion = p.ReadInt() }; } } [Serializable] public sealed class ClanMapTerritoryDetailRequest { public int ProtocolVersion = 5; public string TerritoryID = ""; public void Write(ZPackage p) { p.Write(ProtocolVersion); p.Write(TerritoryID ?? ""); } public static ClanMapTerritoryDetailRequest Read(ZPackage p) { return new ClanMapTerritoryDetailRequest { ProtocolVersion = p.ReadInt(), TerritoryID = p.ReadString() }; } } [Serializable] public sealed class ClanMapGeometryPoint { public int X; public int Y; public float NormalizedX => (float)X / 32767f; public float NormalizedY => (float)Y / 32767f; public static ClanMapGeometryPoint FromNormalized(float x, float y) { return new ClanMapGeometryPoint { X = Mathf.Clamp(Mathf.RoundToInt(x * 32767f), -32767, 32767), Y = Mathf.Clamp(Mathf.RoundToInt(y * 32767f), -32767, 32767) }; } public void Write(ZPackage p) { p.Write(X); p.Write(Y); } public static ClanMapGeometryPoint Read(ZPackage p) { int num = p.ReadInt(); int num2 = p.ReadInt(); if (num < -32767 || num > 32767 || num2 < -32767 || num2 > 32767) { throw new InvalidOperationException("Invalid geometry point."); } return new ClanMapGeometryPoint { X = num, Y = num2 }; } } [Serializable] public sealed class ClanMapTerritoryMarkerDto { public string TerritoryID = ""; public string DisplayName = ""; public string BiomeName = ""; public float WorldX; public float WorldZ; public float NormalizedX; public float NormalizedY; public float ApproximateRadiusWorld; public int TerritoryType; public int SizeLevel; public int State; public int RotCount; public bool IsCapital; public bool IsRotted; public void Write(ZPackage p) { p.Write(TerritoryID); p.Write(DisplayName); p.Write(WorldX); p.Write(WorldZ); p.Write(NormalizedX); p.Write(NormalizedY); p.Write(ApproximateRadiusWorld); p.Write(TerritoryType); p.Write(IsCapital); p.Write(BiomeName); p.Write(SizeLevel); p.Write(State); p.Write(RotCount); p.Write(IsRotted); } public static ClanMapTerritoryMarkerDto Read(ZPackage p) { return new ClanMapTerritoryMarkerDto { TerritoryID = p.ReadString(), DisplayName = p.ReadString(), WorldX = p.ReadSingle(), WorldZ = p.ReadSingle(), NormalizedX = p.ReadSingle(), NormalizedY = p.ReadSingle(), ApproximateRadiusWorld = p.ReadSingle(), TerritoryType = p.ReadInt(), IsCapital = p.ReadBool(), BiomeName = p.ReadString(), SizeLevel = p.ReadInt(), State = p.ReadInt(), RotCount = p.ReadInt(), IsRotted = p.ReadBool() }; } } [Serializable] public sealed class ClanMapPublicTerritoryDto { public string TerritoryID = ""; public string DisplayName = ""; public string OwnerClanName = ""; public string OwnerClanTag = ""; public string OwnerClanLogoKey = ""; public string BiomeName = ""; public int OwnerClanID; public int TerritoryType; public int SizeLevel; public int PublicState; public int GeometryRevision; public float OwnerClanColorR; public float OwnerClanColorG; public float OwnerClanColorB; public float WorldCenterX; public float WorldCenterZ; public float NormalizedCenterX; public float NormalizedCenterY; public bool IsCapital; public List GeometryPoints = new List(); public Color OwnerColor => new Color(OwnerClanColorR, OwnerClanColorG, OwnerClanColorB, 1f); public void Write(ZPackage p) { p.Write(TerritoryID); p.Write(DisplayName); p.Write(OwnerClanID); p.Write(OwnerClanName); p.Write(OwnerClanTag); p.Write(OwnerClanColorR); p.Write(OwnerClanColorG); p.Write(OwnerClanColorB); p.Write(OwnerClanLogoKey); p.Write(TerritoryType); p.Write(IsCapital); p.Write(BiomeName); p.Write(SizeLevel); p.Write(WorldCenterX); p.Write(WorldCenterZ); p.Write(NormalizedCenterX); p.Write(NormalizedCenterY); p.Write(PublicState); p.Write(GeometryRevision); int num = Math.Min(GeometryPoints?.Count ?? 0, 256); p.Write(num); for (int i = 0; i < num; i++) { GeometryPoints[i].Write(p); } } public static ClanMapPublicTerritoryDto Read(ZPackage p, ref int total) { ClanMapPublicTerritoryDto clanMapPublicTerritoryDto = new ClanMapPublicTerritoryDto { TerritoryID = p.ReadString(), DisplayName = p.ReadString(), OwnerClanID = p.ReadInt(), OwnerClanName = p.ReadString(), OwnerClanTag = p.ReadString(), OwnerClanColorR = p.ReadSingle(), OwnerClanColorG = p.ReadSingle(), OwnerClanColorB = p.ReadSingle(), OwnerClanLogoKey = p.ReadString(), TerritoryType = p.ReadInt(), IsCapital = p.ReadBool(), BiomeName = p.ReadString(), SizeLevel = p.ReadInt(), WorldCenterX = p.ReadSingle(), WorldCenterZ = p.ReadSingle(), NormalizedCenterX = p.ReadSingle(), NormalizedCenterY = p.ReadSingle(), PublicState = p.ReadInt(), GeometryRevision = p.ReadInt() }; int num = p.ReadInt(); if (num < 3 || num > 256 || total + num > 50000) { throw new InvalidOperationException("Invalid polygon count."); } total += num; for (int i = 0; i < num; i++) { clanMapPublicTerritoryDto.GeometryPoints.Add(ClanMapGeometryPoint.Read(p)); } return clanMapPublicTerritoryDto; } } [Serializable] public sealed class ClanMapSnapshot { public int ProtocolVersion; public long WorldUID; public int ClanID; public string ClanName = ""; public string ClanTag = ""; public float ClanColorR; public float ClanColorG; public float ClanColorB; public string ClanLogoKey = ""; public bool CanEdit; public bool CanClear; public bool CanClearPins; public bool CanClearDrawings; public long SnapshotRevision; public long ServerTimestampUtc; public List Territories = new List(); public List PublicTerritories = new List(); public Color ClanColor => new Color(ClanColorR, ClanColorG, ClanColorB, 1f); public void Write(ZPackage p) { p.Write(ProtocolVersion); p.Write(WorldUID); p.Write(ClanID); p.Write(ClanName ?? ""); p.Write(ClanTag ?? ""); p.Write(ClanColorR); p.Write(ClanColorG); p.Write(ClanColorB); p.Write(ClanLogoKey ?? ""); p.Write(CanEdit); p.Write(CanClear); p.Write(CanClearPins); p.Write(CanClearDrawings); p.Write(SnapshotRevision); p.Write(ServerTimestampUtc); int num = Math.Min(Territories?.Count ?? 0, 512); p.Write(num); for (int i = 0; i < num; i++) { Territories[i].Write(p); } int num2 = Math.Min(PublicTerritories?.Count ?? 0, 512); p.Write(num2); int num3 = 0; for (int j = 0; j < num2; j++) { if (PublicTerritories[j].GeometryPoints.Count + num3 > 50000) { throw new InvalidOperationException("Geometry payload limit exceeded."); } num3 += PublicTerritories[j].GeometryPoints.Count; PublicTerritories[j].Write(p); } } public static ClanMapSnapshot Read(ZPackage p) { ClanMapSnapshot clanMapSnapshot = new ClanMapSnapshot { ProtocolVersion = p.ReadInt(), WorldUID = p.ReadLong(), ClanID = p.ReadInt(), ClanName = p.ReadString(), ClanTag = p.ReadString(), ClanColorR = p.ReadSingle(), ClanColorG = p.ReadSingle(), ClanColorB = p.ReadSingle(), ClanLogoKey = p.ReadString(), CanEdit = p.ReadBool(), CanClear = p.ReadBool(), CanClearPins = p.ReadBool(), CanClearDrawings = p.ReadBool(), SnapshotRevision = p.ReadLong(), ServerTimestampUtc = p.ReadLong() }; int num = p.ReadInt(); if (num < 0 || num > 512) { throw new InvalidOperationException("Invalid own territory count."); } for (int i = 0; i < num; i++) { clanMapSnapshot.Territories.Add(ClanMapTerritoryMarkerDto.Read(p)); } int num2 = p.ReadInt(); if (num2 < 0 || num2 > 512) { throw new InvalidOperationException("Invalid public territory count."); } int total = 0; for (int j = 0; j < num2; j++) { clanMapSnapshot.PublicTerritories.Add(ClanMapPublicTerritoryDto.Read(p, ref total)); } return clanMapSnapshot; } } [Serializable] public sealed class ClanMapPublicTerritoryDetailDto { public string TerritoryID = ""; public string DisplayName = ""; public string OwnerClanName = ""; public string OwnerClanTag = ""; public string OwnerClanLogoKey = ""; public string BiomeName = ""; public int OwnerClanID; public int TerritoryType; public int SizeLevel; public int PublicState; public float OwnerClanColorR; public float OwnerClanColorG; public float OwnerClanColorB; public float WorldX; public float WorldZ; public bool IsCapital; public long ServerTimestampUtc; public long DetailRevision; public void Write(ZPackage p) { p.Write(TerritoryID); p.Write(DisplayName); p.Write(OwnerClanID); p.Write(OwnerClanName); p.Write(OwnerClanTag); p.Write(OwnerClanColorR); p.Write(OwnerClanColorG); p.Write(OwnerClanColorB); p.Write(OwnerClanLogoKey); p.Write(TerritoryType); p.Write(IsCapital); p.Write(BiomeName); p.Write(SizeLevel); p.Write(WorldX); p.Write(WorldZ); p.Write(PublicState); p.Write(ServerTimestampUtc); p.Write(DetailRevision); } public static ClanMapPublicTerritoryDetailDto Read(ZPackage p) { return new ClanMapPublicTerritoryDetailDto { TerritoryID = p.ReadString(), DisplayName = p.ReadString(), OwnerClanID = p.ReadInt(), OwnerClanName = p.ReadString(), OwnerClanTag = p.ReadString(), OwnerClanColorR = p.ReadSingle(), OwnerClanColorG = p.ReadSingle(), OwnerClanColorB = p.ReadSingle(), OwnerClanLogoKey = p.ReadString(), TerritoryType = p.ReadInt(), IsCapital = p.ReadBool(), BiomeName = p.ReadString(), SizeLevel = p.ReadInt(), WorldX = p.ReadSingle(), WorldZ = p.ReadSingle(), PublicState = p.ReadInt(), ServerTimestampUtc = p.ReadLong(), DetailRevision = p.ReadLong() }; } } [Serializable] public sealed class ClanMapTerritoryDetailDto { public string TerritoryID = ""; public string DisplayName = ""; public string CustomName = ""; public string BiomeName = ""; public int TerritoryType; public bool IsCapital; public int GridX; public int GridY; public float WorldX; public float WorldZ; public float ApproximateRadiusWorld; public int SizeLevel; public float HP; public float MaxHP; public int RotCount; public bool IsRotted; public int State; public long BaseIncome; public long CorruptionAdjustedIncome; public long BaseUpkeep; public long ProgressiveUpkeep; public long GuardUpkeep; public long NetResult; public int BuildingCount; public int ActiveBuildingCount; public int StoredProductionTicks; public int ProductionReadyCount; public long ServerTimestampUtc; public long DetailRevision; public void Write(ZPackage p) { p.Write(TerritoryID); p.Write(DisplayName); p.Write(CustomName); p.Write(TerritoryType); p.Write(IsCapital); p.Write(BiomeName); p.Write(GridX); p.Write(GridY); p.Write(WorldX); p.Write(WorldZ); p.Write(ApproximateRadiusWorld); p.Write(SizeLevel); p.Write(HP); p.Write(MaxHP); p.Write(RotCount); p.Write(IsRotted); p.Write(State); p.Write(BaseIncome); p.Write(CorruptionAdjustedIncome); p.Write(BaseUpkeep); p.Write(ProgressiveUpkeep); p.Write(GuardUpkeep); p.Write(NetResult); p.Write(BuildingCount); p.Write(ActiveBuildingCount); p.Write(StoredProductionTicks); p.Write(ProductionReadyCount); p.Write(ServerTimestampUtc); p.Write(DetailRevision); } public static ClanMapTerritoryDetailDto Read(ZPackage p) { return new ClanMapTerritoryDetailDto { TerritoryID = p.ReadString(), DisplayName = p.ReadString(), CustomName = p.ReadString(), TerritoryType = p.ReadInt(), IsCapital = p.ReadBool(), BiomeName = p.ReadString(), GridX = p.ReadInt(), GridY = p.ReadInt(), WorldX = p.ReadSingle(), WorldZ = p.ReadSingle(), ApproximateRadiusWorld = p.ReadSingle(), SizeLevel = p.ReadInt(), HP = p.ReadSingle(), MaxHP = p.ReadSingle(), RotCount = p.ReadInt(), IsRotted = p.ReadBool(), State = p.ReadInt(), BaseIncome = p.ReadLong(), CorruptionAdjustedIncome = p.ReadLong(), BaseUpkeep = p.ReadLong(), ProgressiveUpkeep = p.ReadLong(), GuardUpkeep = p.ReadLong(), NetResult = p.ReadLong(), BuildingCount = p.ReadInt(), ActiveBuildingCount = p.ReadInt(), StoredProductionTicks = p.ReadInt(), ProductionReadyCount = p.ReadInt(), ServerTimestampUtc = p.ReadLong(), DetailRevision = p.ReadLong() }; } } public enum ClanMapPinType { RallyPoint, Attack, Defend, Scout, Resource, Warning, Trade, Meeting, Custom, Default } [Serializable] public sealed class ClanMapPin { public string PinID = ""; public long WorldUID; public int ClanID; public long CreatedByPlayerID; public string CreatedByName = ""; public long CreatedUtc; public long UpdatedByPlayerID; public string UpdatedByName = ""; public long UpdatedUtc; public int Revision = 1; public float NormalizedX; public float NormalizedY; public ClanMapPinType PinType; public string Title = ""; public string Description = ""; public int ColorIndex; public bool IsImportant; } [Serializable] public sealed class ClanMapPinDto { public string PinID = ""; public float NormalizedX; public float NormalizedY; public int PinType; public string Title = ""; public string Description = ""; public int ColorIndex; public bool IsImportant; public string CreatedByName = ""; public string UpdatedByName = ""; public long CreatedUtc; public long UpdatedUtc; public int Revision; public void Write(ZPackage p) { p.Write(PinID ?? ""); p.Write(NormalizedX); p.Write(NormalizedY); p.Write(PinType); p.Write(Title ?? ""); p.Write(Description ?? ""); p.Write(ColorIndex); p.Write(IsImportant); p.Write(CreatedByName ?? ""); p.Write(UpdatedByName ?? ""); p.Write(CreatedUtc); p.Write(UpdatedUtc); p.Write(Revision); } public static ClanMapPinDto Read(ZPackage p) { ClanMapPinDto clanMapPinDto = new ClanMapPinDto { PinID = p.ReadString(), NormalizedX = p.ReadSingle(), NormalizedY = p.ReadSingle(), PinType = p.ReadInt(), Title = p.ReadString(), Description = p.ReadString(), ColorIndex = p.ReadInt(), IsImportant = p.ReadBool(), CreatedByName = p.ReadString(), UpdatedByName = p.ReadString(), CreatedUtc = p.ReadLong(), UpdatedUtc = p.ReadLong(), Revision = p.ReadInt() }; if (clanMapPinDto.PinID.Length > 64 || clanMapPinDto.Title.Length > 48 || clanMapPinDto.Description.Length > 256 || clanMapPinDto.CreatedByName.Length > 64 || clanMapPinDto.UpdatedByName.Length > 64) { throw new InvalidOperationException("Invalid pin payload."); } return clanMapPinDto; } public static ClanMapPinDto FromModel(ClanMapPin p) { return new ClanMapPinDto { PinID = p.PinID, NormalizedX = p.NormalizedX, NormalizedY = p.NormalizedY, PinType = (int)p.PinType, Title = p.Title, Description = p.Description, ColorIndex = p.ColorIndex, IsImportant = p.IsImportant, CreatedByName = p.CreatedByName, UpdatedByName = p.UpdatedByName, CreatedUtc = p.CreatedUtc, UpdatedUtc = p.UpdatedUtc, Revision = p.Revision }; } } public static class ClanMapPinProtocol { public const int MaxPinsPerClan = 200; public const int MaxTitleLength = 48; public const int MaxDescriptionLength = 256; public const int MaxSnapshotBytes = 131072; public const int ColorCount = 8; public static bool IsValidType(int value) { if (value >= 0) { return value <= 9; } return false; } } [Serializable] public sealed class ClanMapPinSnapshot { public int ProtocolVersion = 5; public long WorldUID; public int ClanID; public long PinRevision; public long ServerTimestampUtc; public List Pins = new List(); public void Write(ZPackage p) { p.Write(ProtocolVersion); p.Write(WorldUID); p.Write(ClanID); p.Write(PinRevision); p.Write(ServerTimestampUtc); int num = Math.Min(Pins?.Count ?? 0, 200); p.Write(num); for (int i = 0; i < num; i++) { Pins[i].Write(p); } } public static ClanMapPinSnapshot Read(ZPackage p) { ClanMapPinSnapshot clanMapPinSnapshot = new ClanMapPinSnapshot { ProtocolVersion = p.ReadInt(), WorldUID = p.ReadLong(), ClanID = p.ReadInt(), PinRevision = p.ReadLong(), ServerTimestampUtc = p.ReadLong() }; int num = p.ReadInt(); if (num < 0 || num > 200) { throw new InvalidOperationException("Invalid pin count."); } for (int i = 0; i < num; i++) { clanMapPinSnapshot.Pins.Add(ClanMapPinDto.Read(p)); } return clanMapPinSnapshot; } } [Serializable] public sealed class ClanMapPinMutationRequest { public int ProtocolVersion = 5; public string PinID = ""; public int ExpectedRevision; public float NormalizedX; public float NormalizedY; public int PinType; public string Title = ""; public string Description = ""; public int ColorIndex; public bool IsImportant; public void Write(ZPackage p) { p.Write(ProtocolVersion); p.Write(PinID ?? ""); p.Write(ExpectedRevision); p.Write(NormalizedX); p.Write(NormalizedY); p.Write(PinType); p.Write(Title ?? ""); p.Write(Description ?? ""); p.Write(ColorIndex); p.Write(IsImportant); } public static ClanMapPinMutationRequest Read(ZPackage p) { return new ClanMapPinMutationRequest { ProtocolVersion = p.ReadInt(), PinID = p.ReadString(), ExpectedRevision = p.ReadInt(), NormalizedX = p.ReadSingle(), NormalizedY = p.ReadSingle(), PinType = p.ReadInt(), Title = p.ReadString(), Description = p.ReadString(), ColorIndex = p.ReadInt(), IsImportant = p.ReadBool() }; } } [Serializable] public sealed class ClanMapPinDeleteRequest { public int ProtocolVersion = 5; public string PinID = ""; public int ExpectedRevision; public void Write(ZPackage p) { p.Write(ProtocolVersion); p.Write(PinID ?? ""); p.Write(ExpectedRevision); } public static ClanMapPinDeleteRequest Read(ZPackage p) { return new ClanMapPinDeleteRequest { ProtocolVersion = p.ReadInt(), PinID = p.ReadString(), ExpectedRevision = p.ReadInt() }; } } [Serializable] public sealed class ClanMapPinSimpleRequest { public int ProtocolVersion = 5; public void Write(ZPackage p) { p.Write(ProtocolVersion); } public static ClanMapPinSimpleRequest Read(ZPackage p) { return new ClanMapPinSimpleRequest { ProtocolVersion = p.ReadInt() }; } } public enum ClanRank { Member, Officer, Jarl } public enum ClanRole { None = 0, Explorer = 1, Pathfinder = 1, Warden = 2, Raider = 3, Guardian = 4, Builder = 5, Gatherer = 6, Merchant = 7, Smuggler = 8, Thief = 9, Shaman = 10, Druid = 11, Hunter = 12, Prospector = 13, Skipper = 14, Skald = 15, Lawspeaker = 16, Shieldbearer = 4, Invader = 3, Parasite = 3 } [Serializable] public class ClanTerritory { public int GridX; public int GridY; public int OwnerClanId; public long ClaimedByPlayerId; public string ClaimedByPlayerName; public long ClaimTimestamp; public string CustomName; public long LastRenameTimestamp; public TerritoryType Type; public TerritoryState State; public int SizeLevel = 1; public int BiomeValueTier = 1; public string BiomeName = ""; public int RotCount; public long LastRotTickTimestamp; public long LastIncomeTimestamp; public float HP; public float MaxHP; public long FreebornOwnerId; public string FreebornOwnerName; public bool OdinsPeaceActive; public long OdinsPeaceChallengeEndTimestamp; public long LastChallengeTimestamp; public bool IsAbandoned; public int TerritoryStakeGameId; public int TerritoryStakeClanId; public List Buildings = new List(); public List TerritoryStorage = new List(); [NonSerialized] public float CaptureProgress; [NonSerialized] public int CaptureAttackerClanId; public Vector2i GridPosition => new Vector2i(GridX, GridY); public string DisplayName { get { if (string.IsNullOrEmpty(CustomName)) { return $"({GridX}, {GridY})"; } return CustomName; } } public bool IsRotted => RotCount >= 4; public bool IsCapital { get { if (Type != TerritoryType.Capital) { return Type == TerritoryType.Homestead; } return true; } } public bool IsFreebornTerritory { get { if (FreebornOwnerId == 0L && Type != TerritoryType.Homestead) { return Type == TerritoryType.Outpost; } return true; } } public bool CanRename(double cooldownHours = 24.0) { if (LastRenameTimestamp == 0L) { return true; } return (double)(DateTimeOffset.UtcNow.ToUnixTimeSeconds() - LastRenameTimestamp) / 3600.0 >= cooldownHours; } } [Serializable] public class ClanVaultItem { public string PrefabName; public string DisplayName; public int Amount; } public enum ContractType { Gathering, Exploration, Warfare, Economy, WorldEvents, Building, Freeborn } public enum ContractIssuerType { System, Clan, Freeborn, Admin } public enum ContractTargetType { None, Territory, Clan, Freeborn, Player, WorldEvent, Item } public enum ContractStatus { Available, Accepted, Completed, Expired, Cancelled } [Serializable] public class Contract { public int ContractId; public ContractType ContractType; public string Title = ""; public string Description = ""; public ContractIssuerType IssuerType; public long IssuerId; public ContractTargetType TargetType; public string TargetId = ""; public string RequiredItem = ""; public int RequiredAmount; public long RewardCoins; public string RewardMaterials = ""; public ContractStatus Status; public long CreatedAt; public long ExpiresAt; public long AcceptedBy; } [Serializable] public class DiplomacyRelation { public int ClanA; public int ClanB; public DiplomacyState State; public int RequestedByClanId; public long UpdatedTimestamp; public int AllianceLeaderClanId; public float AllianceColorR = 0.2f; public float AllianceColorG = 0.45f; public float AllianceColorB = 1f; public bool IsBetween(int a, int b) { if (ClanA != a || ClanB != b) { if (ClanA == b) { return ClanB == a; } return false; } return true; } } public enum DiplomacyState { Neutral, Friendly, Allied, Hostile, AtWar } [Serializable] public class EconomyTransaction { public long Timestamp; public string Type = ""; public long Amount; public long BalanceBefore; public long BalanceAfter; public string Source = ""; public string TerritoryId = ""; public string BuildingId = ""; } public enum FehuGameType { Chess, Dice, Poker } public enum FehuGameStatus { Waiting, Active, Finished, Cancelled } public enum FehuStakeType { None, Coins, Item, Territory } public enum DiceGameMode { HighestRoll, BestOf3, LuckySeven } [Serializable] public class DiceRollRecord { public long PlayerId; public int Round; public List Rolls = new List(); public int Total; public long Timestamp; } [Serializable] public class DiceGameState { public int CurrentPlayerIndex; public int CurrentRound = 1; public long CurrentTurnStartedAt; public List ActivePlayerIds = new List(); public Dictionary Scores = new Dictionary(); public List Rolls = new List(); public string LastResult = ""; } [Serializable] public class FehuGameTable { public int GameId; public FehuGameType GameType; public string TableName = ""; public long HostPlayerId; public string HostPlayerName = ""; public List PlayerIds = new List(); public List SpectatorIds = new List(); public int PlayerLimit = 2; public bool IsPrivate; public bool AllowSpectators = true; public FehuStakeType StakeType; public long StakeAmount; public string StakePrefabName = ""; public FehuGameStatus Status; public long CreatedAt; public long StartedAt; public long FinishedAt; public long WinnerPlayerId; public string Result = ""; public int DiceCount = 1; public int RoundCount = 1; public DiceGameMode DiceMode; public DiceGameState Dice; public List EscrowPlayerIds = new List(); public long EscrowTotal; public bool EscrowCollecting; public bool PayoutIssued; public ChessGameState Chess; public int ChessHostColorPreference; public int ChessTimeControlSeconds; public PokerGameState Poker; public long PokerBuyIn; public long PokerSmallBlind; public long PokerBigBlind; public List HostStakeTerritoryIds = new List(); public List ChallengerStakeTerritoryIds = new List(); public int HostStakeClanId; public int ChallengerStakeClanId; public bool HostStakeAccepted; public bool ChallengerStakeAccepted; } [Serializable] public class FehuGameInvite { public int GameId; public FehuGameType GameType; public long FromPlayerId; public long ToPlayerId; public long CreatedAt; public long ExpiresAt; } [Serializable] public class FehuGameHistoryEntry { public int GameId; public FehuGameType GameType; public string TableName = ""; public List PlayerIds = new List(); public long WinnerPlayerId; public string Result = ""; public FehuStakeType StakeType; public long StakeAmount; public string StakePrefabName = ""; public long CompletedAt; public List HostStakeTerritoryIds = new List(); public List ChallengerStakeTerritoryIds = new List(); public int HostStakeClanId; public int ChallengerStakeClanId; public string TerritoryTransferResult = ""; } [Serializable] public class FehuGamePlayerStats { public long PlayerId; public int GamesPlayed; public int Wins; public int Losses; public long Profit; public int HighestDiceRoll; public long BiggestWin; public int DiceCurrentStreak; public int DiceBestStreak; public FehuGameType FavoriteGame; public int ChessGamesPlayed; public int ChessWins; public int ChessLosses; public int ChessDraws; public int ChessRating = 1000; public int ChessCurrentStreak; public int ChessBestStreak; public int PokerGamesPlayed; public int PokerHandsWon; public int PokerTablesWon; public long PokerBiggestPot; public long PokerBiggestWin; public long PokerProfit; } [Serializable] public class FehuGameProfileSummary { public long PlayerId; public int GamesPlayed; public int Wins; public int Losses; public int CurrentRating; public int HighestRating; public FehuGameType FavoriteGame; } public static class FehuGameProfileStats { public static FehuGameProfileSummary FromStats(FehuGamePlayerStats stats) { if (stats == null) { return new FehuGameProfileSummary(); } int highestRating = Math.Max(1000, stats.ChessRating); return new FehuGameProfileSummary { PlayerId = stats.PlayerId, GamesPlayed = stats.GamesPlayed, Wins = stats.Wins, Losses = stats.Losses, CurrentRating = stats.ChessRating, HighestRating = highestRating, FavoriteGame = stats.FavoriteGame }; } public static string FavoriteGameName(FehuGamePlayerStats stats) { if (stats != null && stats.GamesPlayed > 0) { return stats.FavoriteGame.ToString(); } return "None"; } } public enum GuardStatus { Active, Injured, Dead, Disabled } public enum GuardType { BasicGuard, ArcherGuard, ShieldGuard, PatrolGuard, TowerGuard, EliteGuard } public enum PactStatus { Pending, Active, Broken, Expired } [Serializable] public class NonAggressionPact { public int ClanAId; public int ClanBId; public long StartTime; public long EndTime; public long BrokenAt; public long BreakCooldownEndTime; public PactStatus Status; public int RequestedByClanId; public bool IsBetween(int a, int b) { if (ClanAId != a || ClanBId != b) { if (ClanAId == b) { return ClanBId == a; } return false; } return true; } } public enum AchievementProgressType { FirstLogin, PlayMinutes, ClanMember, ClanDays, ClanFounder, TerritoriesClaimed, CapitalsFounded, HomesteadsFounded, OutpostsFounded, BuildingsContributed, CoinsDonated, ItemsDonated, TaxesPaid, BountiesClaimed, WorldEventsCompleted } public enum AchievementRewardType { None, Coins, Title } public sealed class AchievementEvaluationResult { public List UnlockedIds { get; } = new List(); public int RewardCoins { get; set; } public bool StateChanged { get; set; } public bool Changed => StateChanged; } public sealed class PlayerAchievementDefinition { public string Id { get; } public string DisplayName { get; } public string Description { get; } public string Category { get; } public string RequirementText { get; } public long RequiredValue { get; } public AchievementProgressType ProgressType { get; } public bool Hidden { get; } public AchievementRewardType RewardType { get; } public int RewardAmount { get; } public string RewardTitleId { get; } public string IconKey { get; } public PlayerAchievementDefinition(string id, string name, string description, string category, string requirement, long required, AchievementProgressType progress, bool hidden = false, AchievementRewardType rewardType = AchievementRewardType.Coins, int rewardAmount = 0, string rewardTitleId = "", string iconKey = "") { Id = id; DisplayName = name; Description = description; Category = category; RequirementText = requirement; RequiredValue = required; ProgressType = progress; Hidden = hidden; RewardType = rewardType; RewardAmount = rewardAmount; RewardTitleId = rewardTitleId; IconKey = iconKey; } } public static class PlayerAchievementCatalog { public static readonly IReadOnlyList All = new List { new PlayerAchievementDefinition("first_steps", "First Steps", "Begin your journey in Fehu.", "Activity", "Complete the first successful profile login.", 1L, AchievementProgressType.FirstLogin), new PlayerAchievementDefinition("first_hour", "First Hour", "Spend your first hour in the realm.", "Activity", "Reach 60 minutes of playtime.", 60L, AchievementProgressType.PlayMinutes), new PlayerAchievementDefinition("veteran", "Veteran", "Become a seasoned resident.", "Activity", "Reach 600 minutes of playtime.", 600L, AchievementProgressType.PlayMinutes), new PlayerAchievementDefinition("clan_member", "Sworn to a Clan", "Join a recognized clan.", "Clan", "Belong to a valid clan.", 1L, AchievementProgressType.ClanMember), new PlayerAchievementDefinition("loyal_member", "Loyal Member", "Remain loyal to your clan.", "Clan", "Stay in the current clan for 30 full days.", 30L, AchievementProgressType.ClanDays), new PlayerAchievementDefinition("clan_founder", "Clan Founder", "Establish a clan or its capital.", "Clan", "Be the authoritative founder or found a capital.", 1L, AchievementProgressType.ClanFounder), new PlayerAchievementDefinition("first_claim", "First Claim", "Claim your first territory.", "Territory", "Claim 1 territory.", 1L, AchievementProgressType.TerritoriesClaimed), new PlayerAchievementDefinition("landholder", "Landholder", "Hold a growing domain.", "Territory", "Claim 5 territories.", 5L, AchievementProgressType.TerritoriesClaimed), new PlayerAchievementDefinition("capital_founder", "Seat of Power", "Establish a capital.", "Territory", "Found 1 capital.", 1L, AchievementProgressType.CapitalsFounded), new PlayerAchievementDefinition("homestead_founder", "A Place to Call Home", "Establish a homestead.", "Territory", "Found 1 homestead.", 1L, AchievementProgressType.HomesteadsFounded), new PlayerAchievementDefinition("outpost_founder", "Frontier Outpost", "Establish a frontier outpost.", "Territory", "Found 1 outpost.", 1L, AchievementProgressType.OutpostsFounded), new PlayerAchievementDefinition("first_builder", "First Foundation", "Contribute your first building.", "Building", "Contribute 1 building.", 1L, AchievementProgressType.BuildingsContributed), new PlayerAchievementDefinition("realm_builder", "Realm Builder", "Help construct the realm.", "Building", "Contribute 10 buildings.", 10L, AchievementProgressType.BuildingsContributed), new PlayerAchievementDefinition("first_donation", "For the Treasury", "Make a meaningful treasury donation.", "Economy", "Donate 100 Coins.", 100L, AchievementProgressType.CoinsDonated), new PlayerAchievementDefinition("benefactor", "Benefactor", "Become a generous benefactor.", "Economy", "Donate 1,000 Coins.", 1000L, AchievementProgressType.CoinsDonated), new PlayerAchievementDefinition("great_benefactor", "Great Benefactor", "Provide extraordinary support.", "Economy", "Donate 10,000 Coins.", 10000L, AchievementProgressType.CoinsDonated), new PlayerAchievementDefinition("vault_contributor", "Keeper of Stores", "Stock the clan vault.", "Economy", "Donate 100 items to the vault.", 100L, AchievementProgressType.ItemsDonated), new PlayerAchievementDefinition("taxpayer", "Duty Paid", "Contribute through taxation.", "Economy", "Pay 100 total resource and Coin tax units.", 100L, AchievementProgressType.TaxesPaid), new PlayerAchievementDefinition("bounty_hunter", "Bounty Hunter", "Complete a bounty hunt.", "Events", "Claim 1 bounty.", 1L, AchievementProgressType.BountiesClaimed), new PlayerAchievementDefinition("event_participant", "Realm Defender", "Complete a realm event.", "Events", "Complete 1 world event.", 1L, AchievementProgressType.WorldEventsCompleted) }; public static PlayerAchievementDefinition Get(string id) { return All.FirstOrDefault((PlayerAchievementDefinition a) => string.Equals(a.Id, id, StringComparison.OrdinalIgnoreCase)); } } [Serializable] public class PlayerSagaEntry { public string EntryId = ""; public long TimestampUtc; public string EventType = ""; public string Title = ""; public string Description = ""; public string Importance = "Normal"; public int RelatedClanId; public int RelatedTerritoryId; public long RelatedPlayerId; public string RelatedAchievementId = ""; public string RelatedTitleId = ""; public string SourceKey = ""; } public static class SagaEventType { public const string ClanJoined = "ClanJoined"; public const string ClanFounded = "ClanFounded"; public const string BecameJarl = "BecameJarl"; public const string CapitalFounded = "CapitalFounded"; public const string HomesteadFounded = "HomesteadFounded"; public const string OutpostFounded = "OutpostFounded"; public const string TerritoryClaimed = "TerritoryClaimed"; public const string BuildingMilestone = "BuildingMilestone"; public const string DonationMilestone = "DonationMilestone"; public const string AchievementUnlocked = "AchievementUnlocked"; public const string TitleUnlocked = "TitleUnlocked"; public const string BountyClaimed = "BountyClaimed"; public const string WorldEventCompleted = "WorldEventCompleted"; public const string RootTotemPlaced = "RootTotemPlaced"; public const string RootTotemDestroyed = "RootTotemDestroyed"; public const string WarStarted = "WarStarted"; public const string WarWon = "WarWon"; public const string WarLost = "WarLost"; public const string Legacy = "Legacy"; public static readonly HashSet All = new HashSet(StringComparer.Ordinal) { "ClanJoined", "ClanFounded", "BecameJarl", "CapitalFounded", "HomesteadFounded", "OutpostFounded", "TerritoryClaimed", "BuildingMilestone", "DonationMilestone", "AchievementUnlocked", "TitleUnlocked", "BountyClaimed", "WorldEventCompleted", "RootTotemPlaced", "RootTotemDestroyed", "WarStarted", "WarWon", "WarLost", "Legacy" }; } public static class SagaImportance { public const string Minor = "Minor"; public const string Normal = "Normal"; public const string Major = "Major"; public const string Legendary = "Legendary"; public static bool IsValid(string value) { switch (value) { default: return value == "Legendary"; case "Minor": case "Normal": case "Major": return true; } } } [Serializable] public class PlayerClanInfo { public long PlayerId; public string SteamId64 = ""; public List LinkedPlayerIds = new List(); public string PlayerName; public int ClanId; public ClanRole Role; public ClanRole PrimaryRole; public ClanRole SecondaryRole; public ClanRole TertiaryRole; public bool IsFreeborn; public long FreebornSinceTimestamp; public long FreebornLastActiveTimestamp; public long LastFreebornChallengeTimestamp; public List FreebornFriends = new List(); public List PendingFreebornFriendRequests = new List(); public List PendingClanFounderInvites = new List(); public List AcceptedClanFounderInvites = new List(); public ClanRank Rank; public long LastRoleChangeTimestamp; public long LastVaultCrackTimestamp; public long LastTheftAttemptTimestamp; public long LastTheftTargetedTimestamp; public long ForcedPvpUntilUtc; public long JoinedClanTimestamp; public long LastClanLeaveTimestamp; public long LastSeenTimestamp; public long Bounty; public long FreebornTreasury; public int FreebornVaultTier = 1; public long LastFreebornAbandonTimestamp; public bool VaultPermissionsInitialized; public bool CanDepositVaultItems; public bool CanWithdrawVaultItems; public bool CanDepositVaultCoins; public bool CanWithdrawVaultCoins; public bool CanClaimTerritory; public string AboutMe = ""; public string ActiveTitle = ""; public List UnlockedTitles = new List(); public string ActiveTitleId = ""; public Dictionary TitleUnlockTimestamps = new Dictionary(); public List UnlockedAchievements = new List(); public Dictionary AchievementUnlockTimestamps = new Dictionary(); public List RewardedAchievements = new List(); public Dictionary PendingAchievementCoinRewards = new Dictionary(); public long AchievementCoinsEarned; public string ProfileImageKey = ""; public long FirstLoginTimestamp; public long LastLoginTimestamp; public int TotalLogins; public long TotalPlayMinutes; public long LastProfileMinuteTimestamp; public long CoinsDonatedToTreasury; public long ItemsDonatedToVault; public long ResourceTaxPaid; public long CoinTaxPaid; public int TerritoriesClaimed; public int TerritoriesCaptured; public int TerritoriesLost; public int CapitalsFounded; public int HomesteadsFounded; public int OutpostsFounded; public int WarsParticipated; public int WarsWon; public int WarsLost; public int TerritoriesDefended; public int BountiesClaimed; public int BountiesSurvived; public int WorldEventsCompleted; public int TerritoriesDiscovered; public int BuildingsContributed; public int PvPKills; public int PvPDeaths; public long HighestDonation; public List SagaEntries = new List(); public List PersonalSaga = new List(); public Dictionary RoleMinutes = new Dictionary(); public List ServerBadges = new List(); public bool CanChangeRole(double cooldownHours = 24.0) { if (LastRoleChangeTimestamp == 0L) { return true; } return (double)(Now() - LastRoleChangeTimestamp) / 3600.0 >= cooldownHours; } public void NormalizeRoles(bool legacySave = false) { Role = NormalizeRole(Role, legacySave); PrimaryRole = NormalizeRole(PrimaryRole, legacySave); SecondaryRole = NormalizeRole(SecondaryRole, legacySave); TertiaryRole = NormalizeRole(TertiaryRole, legacySave); if (PrimaryRole == ClanRole.None && Role != ClanRole.None) { PrimaryRole = Role; } if (SecondaryRole == PrimaryRole) { SecondaryRole = ClanRole.None; } if (TertiaryRole == PrimaryRole || TertiaryRole == SecondaryRole) { TertiaryRole = ClanRole.None; } Role = PrimaryRole; } public static ClanRole NormalizeRole(ClanRole role, bool legacySave = false) { if (legacySave) { switch (role) { case ClanRole.Explorer: return ClanRole.Explorer; case ClanRole.Warden: return ClanRole.Raider; case ClanRole.Raider: return ClanRole.Gatherer; case ClanRole.Guardian: return ClanRole.Builder; case ClanRole.Builder: return ClanRole.Guardian; case ClanRole.Gatherer: return ClanRole.Shaman; } } if (role >= ClanRole.None && role <= ClanRole.Lawspeaker) { return role; } return ClanRole.None; } public bool CanCrackVault(double cooldownHours = 6.0) { if (LastVaultCrackTimestamp == 0L) { return true; } return (double)(Now() - LastVaultCrackTimestamp) / 3600.0 >= cooldownHours; } public bool CanJoinClan(double cooldownHours) { if (LastClanLeaveTimestamp == 0L) { return true; } return (double)(Now() - LastClanLeaveTimestamp) / 3600.0 >= cooldownHours; } private static long Now() { return DateTimeOffset.UtcNow.ToUnixTimeSeconds(); } } public sealed class PlayerTitleDefinition { public string Id { get; } public string DisplayName { get; } public string Category { get; } public string Description { get; } public string Requirement { get; } public PlayerTitleDefinition(string id, string displayName, string category, string description, string requirement) { Id = id; DisplayName = displayName; Category = category; Description = description; Requirement = requirement; } } public static class PlayerTitleCatalog { public static readonly IReadOnlyList All = new List { new PlayerTitleDefinition("founder", "Founder", "Special", "Founder of the current clan.", "Be the confirmed clan founder."), new PlayerTitleDefinition("jarl", "Jarl", "Special", "Recognized service as a clan Jarl.", "Hold Jarl rank."), new PlayerTitleDefinition("alpha_tester", "Alpha Tester", "Special", "Recognized Fehu alpha tester.", "Receive the Alpha Tester server badge."), new PlayerTitleDefinition("beta_tester", "Beta Tester", "Special", "Recognized Fehu beta tester.", "Receive the Beta Tester server badge."), new PlayerTitleDefinition("contributor", "Contributor", "Special", "Recognized Fehu contributor.", "Receive the Contributor server badge."), new PlayerTitleDefinition("supporter", "Supporter", "Special", "Recognized Fehu supporter.", "Receive the Supporter server badge."), new PlayerTitleDefinition("newcomer", "Newcomer", "Activity", "Your journey in Fehu has begun.", "Complete the first successful profile login."), new PlayerTitleDefinition("veteran", "Veteran", "Activity", "A seasoned resident of the realm.", "Reach 600 minutes of playtime."), new PlayerTitleDefinition("clan_founder", "Clan Founder", "Clan", "Established a clan capital.", "Found a capital or be the confirmed clan founder."), new PlayerTitleDefinition("loyal_member", "Loyal Member", "Clan", "Long-standing clan membership.", "Remain in a clan for at least 30 days."), new PlayerTitleDefinition("landclaimer", "Landclaimer", "Territory", "Claimed land for the realm.", "Claim at least 1 territory."), new PlayerTitleDefinition("realm_builder", "Realm Builder", "Territory", "Helped build the realm.", "Contribute at least 10 buildings."), new PlayerTitleDefinition("benefactor", "Benefactor", "Economy", "A generous clan benefactor.", "Donate at least 1,000 coins to the treasury."), new PlayerTitleDefinition("great_benefactor", "Great Benefactor", "Economy", "An exceptional clan benefactor.", "Donate at least 10,000 coins to the treasury.") }; public static PlayerTitleDefinition Get(string id) { return All.FirstOrDefault((PlayerTitleDefinition title) => string.Equals(title.Id, id, StringComparison.OrdinalIgnoreCase)); } public static PlayerTitleDefinition FindByDisplayName(string displayName) { return All.FirstOrDefault((PlayerTitleDefinition title) => string.Equals(title.DisplayName, (displayName ?? string.Empty).Trim(), StringComparison.OrdinalIgnoreCase)); } } public enum PokerPhase { Waiting, PreFlop, Flop, Turn, River, Showdown, Finished } [Serializable] public class PokerPlayerState { public long PlayerId; public string PlayerName = ""; public long Chips; public long RoundBet; public long TotalContribution; public bool Folded; public bool AllIn; public bool HasActedThisRound; public List HoleCards = new List(); public long DisconnectedAt; } [Serializable] public class PokerGameState { public List Seats = new List(); public List Deck = new List(); public List CommunityCards = new List(); public PokerPhase Phase; public int DealerSeatIndex; public long CurrentActorId; public long CurrentBet; public long MinRaise; public long SmallBlind; public long BigBlind; public long HandNumber; public string LastResult = ""; public List HandLog = new List(); public long LastActionAt; } public static class PokerEngine { public static PokerGameState Create(long smallBlind, long bigBlind) { return new PokerGameState { Phase = PokerPhase.Waiting, SmallBlind = Math.Max(1L, smallBlind), BigBlind = Math.Max(2L, bigBlind), MinRaise = Math.Max(2L, bigBlind) }; } public static string CardName(int card) { if (card < 0 || card >= 52) { return "?"; } char c = "CDHS"[card / 13]; return "23456789TJQKA"[card % 13].ToString() + c; } public static string FullCardName(int card) { if (card < 0 || card >= 52) { return "Unknown Card"; } string[] array = new string[13] { "Two", "Three", "Four", "Five", "Six", "Seven", "Eight", "Nine", "Ten", "Jack", "Queen", "King", "Ace" }; string[] array2 = new string[4] { "Clubs", "Diamonds", "Hearts", "Spades" }; return array[card % 13] + " of " + array2[card / 13]; } public static bool IsHandActive(PokerGameState state) { if (state != null && state.Phase >= PokerPhase.PreFlop) { return state.Phase <= PokerPhase.River; } return false; } public static PokerPlayerState GetPlayer(PokerGameState state, long playerId) { return state?.Seats?.Find((PokerPlayerState p) => p.PlayerId == playerId); } public static void StartHand(PokerGameState state) { if (state.Seats.Where((PokerPlayerState p) => p.Chips > 0).ToList().Count < 2) { state.Phase = PokerPhase.Finished; state.LastResult = "Waiting for at least two funded players."; return; } state.HandNumber++; state.HandLog.Clear(); state.DealerSeatIndex = FindNextFundedSeat(state, (state.HandNumber == 1) ? state.DealerSeatIndex : (state.DealerSeatIndex + 1)); state.Deck = Enumerable.Range(0, 52).ToList(); Random random = new Random((int)(DateTimeOffset.UtcNow.ToUnixTimeMilliseconds() ^ state.HandNumber)); for (int num = state.Deck.Count - 1; num > 0; num--) { int index = random.Next(num + 1); int value = state.Deck[num]; state.Deck[num] = state.Deck[index]; state.Deck[index] = value; } state.CommunityCards.Clear(); state.CurrentBet = 0L; state.MinRaise = state.BigBlind; state.Phase = PokerPhase.PreFlop; state.LastResult = "Pre-flop."; AddHandLog(state, "Hand " + state.HandNumber + " started."); foreach (PokerPlayerState seat in state.Seats) { seat.RoundBet = 0L; seat.TotalContribution = 0L; seat.Folded = seat.Chips <= 0; seat.AllIn = seat.Chips <= 0; seat.HasActedThisRound = false; seat.HoleCards.Clear(); if (!seat.Folded) { seat.HoleCards.Add(Draw(state)); seat.HoleCards.Add(Draw(state)); } } int num2 = FindNextFundedSeat(state, state.DealerSeatIndex + 1); int num3 = FindNextFundedSeat(state, num2 + 1); AddHandLog(state, "Dealer: " + state.Seats[state.DealerSeatIndex].PlayerName + "."); PostBlind(state, num2, state.SmallBlind); AddHandLog(state, "Small Blind: " + state.Seats[num2].PlayerName + " " + state.Seats[num2].RoundBet + "."); PostBlind(state, num3, state.BigBlind); AddHandLog(state, "Big Blind: " + state.Seats[num3].PlayerName + " " + state.Seats[num3].RoundBet + "."); int num4 = FindNextActionSeat(state, num3 + 1); state.CurrentActorId = ((num4 >= 0) ? state.Seats[num4].PlayerId : 0); state.LastActionAt = DateTimeOffset.UtcNow.ToUnixTimeSeconds(); } public static bool ApplyAction(PokerGameState state, long playerId, int action, long raiseTo, out string reason) { reason = ""; PokerPlayerState player = GetPlayer(state, playerId); if (!IsHandActive(state) || player == null || state.CurrentActorId != playerId || player.Folded || player.AllIn) { reason = "It is not your poker turn."; return false; } long num = Math.Max(0L, state.CurrentBet - player.RoundBet); switch (action) { case 1: player.Folded = true; AddHandLog(state, player.PlayerName + " folds."); break; case 2: if (num > 0) { reason = "You must call, raise, fold, or go all-in."; return false; } AddHandLog(state, player.PlayerName + " checks."); break; case 3: { long roundBet = player.RoundBet; Commit(state, player, num); long num3 = player.RoundBet - roundBet; AddHandLog(state, player.PlayerName + " calls " + num3 + (player.AllIn ? " and is All-In." : ".")); break; } case 4: if (raiseTo <= state.CurrentBet || raiseTo - state.CurrentBet < state.MinRaise || raiseTo > player.RoundBet + player.Chips) { reason = "Invalid raise amount."; return false; } Commit(state, player, raiseTo - player.RoundBet); AddHandLog(state, player.PlayerName + " raises to " + raiseTo + (player.AllIn ? " and is All-In." : ".")); state.MinRaise = raiseTo - state.CurrentBet; state.CurrentBet = raiseTo; foreach (PokerPlayerState seat in state.Seats) { if (seat.PlayerId != player.PlayerId && !seat.Folded && !seat.AllIn) { seat.HasActedThisRound = false; } } break; case 5: { long chips = player.Chips; Commit(state, player, player.Chips); AddHandLog(state, player.PlayerName + " goes All-In for " + chips + "."); if (player.RoundBet <= state.CurrentBet) { break; } long num2 = player.RoundBet - state.CurrentBet; if (num2 >= state.MinRaise) { state.MinRaise = num2; foreach (PokerPlayerState seat2 in state.Seats) { if (seat2.PlayerId != player.PlayerId && !seat2.Folded && !seat2.AllIn) { seat2.HasActedThisRound = false; } } } state.CurrentBet = player.RoundBet; break; } default: reason = "Unknown poker action."; return false; } player.HasActedThisRound = true; state.LastActionAt = DateTimeOffset.UtcNow.ToUnixTimeSeconds(); return true; } public static bool IsBettingRoundComplete(PokerGameState state) { if (state == null) { return true; } foreach (PokerPlayerState seat in state.Seats) { if (!seat.Folded && !seat.AllIn && (!seat.HasActedThisRound || seat.RoundBet != state.CurrentBet)) { return false; } } state.Seats.Count((PokerPlayerState p) => !p.Folded && !p.AllIn); _ = 1; return true; } public static bool HasSingleLivePlayer(PokerGameState state) { return state.Seats.Count((PokerPlayerState p) => !p.Folded) <= 1; } public static PokerPlayerState GetSingleLivePlayer(PokerGameState state) { return state.Seats.Find((PokerPlayerState p) => !p.Folded); } public static void AdvanceStreet(PokerGameState state) { foreach (PokerPlayerState seat in state.Seats) { seat.RoundBet = 0L; seat.HasActedThisRound = false; } state.CurrentBet = 0L; state.MinRaise = state.BigBlind; if (state.Phase == PokerPhase.PreFlop) { state.Phase = PokerPhase.Flop; Draw(state); state.CommunityCards.Add(Draw(state)); state.CommunityCards.Add(Draw(state)); state.CommunityCards.Add(Draw(state)); AddHandLog(state, "Flop:\n" + string.Join("\n", state.CommunityCards.Select(FullCardName).ToArray())); } else if (state.Phase == PokerPhase.Flop) { state.Phase = PokerPhase.Turn; Draw(state); state.CommunityCards.Add(Draw(state)); AddHandLog(state, "Turn:\n" + FullCardName(state.CommunityCards[state.CommunityCards.Count - 1])); } else { if (state.Phase != PokerPhase.Turn) { state.Phase = PokerPhase.Showdown; AddHandLog(state, "Showdown."); return; } state.Phase = PokerPhase.River; Draw(state); state.CommunityCards.Add(Draw(state)); AddHandLog(state, "River:\n" + FullCardName(state.CommunityCards[state.CommunityCards.Count - 1])); } int num = FindNextActionSeat(state, state.DealerSeatIndex + 1); state.CurrentActorId = ((num >= 0) ? state.Seats[num].PlayerId : 0); state.LastActionAt = DateTimeOffset.UtcNow.ToUnixTimeSeconds(); } public static Dictionary ResolvePots(PokerGameState state) { Dictionary dictionary = new Dictionary(); List list = (from v in (from p in state.Seats where p.TotalContribution > 0 select p.TotalContribution).Distinct() orderby v select v).ToList(); long num = 0L; int num2 = 0; foreach (long level in list) { num2++; List list2 = state.Seats.Where((PokerPlayerState p) => p.TotalContribution >= level).ToList(); long num3 = (level - num) * list2.Count; num = level; List list3 = list2.Where((PokerPlayerState p) => !p.Folded).ToList(); if (num3 > 0 && list3.Count != 0) { long best = list3.Max((PokerPlayerState p) => EvaluateBest(p.HoleCards.Concat(state.CommunityCards).ToList())); List list4 = (from p in list3 where EvaluateBest(p.HoleCards.Concat(state.CommunityCards).ToList()) == best orderby p.PlayerId select p).ToList(); long num4 = num3 / list4.Count; long num5 = num3 % list4.Count; for (int num6 = 0; num6 < list4.Count; num6++) { long num7 = num4 + ((num6 < num5) ? 1 : 0); dictionary[list4[num6].PlayerId] = (dictionary.TryGetValue(list4[num6].PlayerId, out var value) ? (value + num7) : num7); } string text = ((num2 == 1) ? "Main Pot" : "Side Pot"); string text2 = HandName(best); AddHandLog(state, text + " " + num3 + " won by " + string.Join(", ", list4.Select((PokerPlayerState w) => w.PlayerName).ToArray()) + " with " + text2 + "."); } } return dictionary; } public static long CurrentPot(PokerGameState state) { return (state?.Seats?.Sum((PokerPlayerState p) => p.TotalContribution)).GetValueOrDefault(); } public static int FindNextActionSeat(PokerGameState state, int start) { for (int i = 0; i < state.Seats.Count; i++) { int num = (start + i) % state.Seats.Count; PokerPlayerState pokerPlayerState = state.Seats[num]; if (!pokerPlayerState.Folded && !pokerPlayerState.AllIn && pokerPlayerState.Chips > 0) { return num; } } return -1; } private static int FindNextFundedSeat(PokerGameState state, int start) { for (int i = 0; i < state.Seats.Count; i++) { int num = (start + i) % state.Seats.Count; if (state.Seats[num].Chips > 0) { return num; } } return 0; } private static int Draw(PokerGameState state) { int result = state.Deck[0]; state.Deck.RemoveAt(0); return result; } private static void PostBlind(PokerGameState state, int seatIndex, long amount) { if (seatIndex >= 0 && seatIndex < state.Seats.Count) { PokerPlayerState pokerPlayerState = state.Seats[seatIndex]; Commit(state, pokerPlayerState, amount); state.CurrentBet = Math.Max(state.CurrentBet, pokerPlayerState.RoundBet); } } private static void Commit(PokerGameState state, PokerPlayerState player, long amount) { amount = Math.Max(0L, Math.Min(amount, player.Chips)); player.Chips -= amount; player.RoundBet += amount; player.TotalContribution += amount; if (player.Chips == 0L) { player.AllIn = true; } } public static long EvaluateBest(List cards) { if (cards == null || cards.Count < 5) { return 0L; } long num = 0L; for (int i = 0; i < cards.Count - 4; i++) { for (int j = i + 1; j < cards.Count - 3; j++) { for (int k = j + 1; k < cards.Count - 2; k++) { for (int l = k + 1; l < cards.Count - 1; l++) { for (int m = l + 1; m < cards.Count; m++) { num = Math.Max(num, EvaluateFive(new int[5] { cards[i], cards[j], cards[k], cards[l], cards[m] })); } } } } } return num; } public static string HandName(long score) { return (int)(score / 100000000) switch { 9 => "Royal Flush", 8 => "Straight Flush", 7 => "Four of a Kind", 6 => "Full House", 5 => "Flush", 4 => "Straight", 3 => "Three of a Kind", 2 => "Two Pair", 1 => "Pair", _ => "High Card", }; } private static long EvaluateFive(int[] cards) { int[] source = (from c in cards select c % 13 + 2 into v orderby v descending select v).ToArray(); bool flag = cards.All((int c) => c / 13 == cards[0] / 13); List list = source.Distinct().ToList(); bool flag2 = list.Count == 5 && (list[0] - list[4] == 4 || list.SequenceEqual(new List { 14, 5, 4, 3, 2 })); int num = (list.SequenceEqual(new List { 14, 5, 4, 3, 2 }) ? 5 : list[0]); List> list2 = (from v in source group v by v into g orderby g.Count() descending, g.Key descending select g).ToList(); int num2 = ((!(flag && flag2)) ? ((list2[0].Count() == 4) ? 7 : ((list2[0].Count() == 3 && list2[1].Count() == 2) ? 6 : (flag ? 5 : (flag2 ? 4 : ((list2[0].Count() == 3) ? 3 : ((list2[0].Count() == 2 && list2[1].Count() == 2) ? 2 : ((list2[0].Count() == 2) ? 1 : 0))))))) : ((num == 14) ? 9 : 8)); List list3 = new List(); foreach (IGrouping item in list2) { for (int num3 = 0; num3 < item.Count(); num3++) { list3.Add(item.Key); } } if (flag2) { list3.Clear(); list3.Add(num); } long num4 = (long)num2 * 100000000L; for (int num5 = 0; num5 < list3.Count; num5++) { num4 += list3[num5] * (long)Math.Pow(15.0, 4 - num5); } return num4; } public static bool HasDuplicateCards(PokerGameState state) { if (state == null) { return false; } HashSet hashSet = new HashSet(); foreach (int item in state.Deck.Concat(state.CommunityCards ?? new List())) { if (item < 0 || item >= 52 || !hashSet.Add(item)) { return true; } } foreach (PokerPlayerState item2 in state.Seats ?? new List()) { foreach (int item3 in item2.HoleCards ?? new List()) { if (item3 < 0 || item3 >= 52 || !hashSet.Add(item3)) { return true; } } } return false; } public static void AddHandLog(PokerGameState state, string line) { if (state != null && !string.IsNullOrWhiteSpace(line)) { state.HandLog.Add(line); while (state.HandLog.Count > 24) { state.HandLog.RemoveAt(0); } } } } public enum ProtectedZoneType { Spawn, Trader, Marketplace, EventHub, Custom } [Serializable] public class RotTotem { public int TotemId; public string PrefabName = "guard_stone"; public int TerritoryGridX; public int TerritoryGridY; public long PlacedByPlayerId; public string PlacedByPlayerName; public int PlacerClanId; public long PlacedTimestamp; public Vector3 WorldPosition; public TotemState State; public int Level = 1; public long LastDecayTimestamp; public int DaysActive => (int)((DateTimeOffset.UtcNow.ToUnixTimeSeconds() - PlacedTimestamp) / 86400); public int LootReductionPercent => Math.Min(DaysActive * 10, 100); public Vector2i GridPosition => new Vector2i(TerritoryGridX, TerritoryGridY); } [Serializable] public class PendingRootLoot { public int RootClanId; public int TerritoryGridX; public int TerritoryGridY; public int TotemId; public string PrefabName = ""; public string DisplayName = ""; public int Amount; } [Serializable] public class RootProductionRemainder { public int ReceivingClanId; public int TerritoryGridX; public int TerritoryGridY; public long BuildingBuiltAt; public string PrefabName = ""; public string DisplayName = ""; public bool OwnerShare; public int QuarterUnits; } [Serializable] public class SharedClansFile { public int Version = 1; public int NextClanId = 1; public List Clans = new List(); } [Serializable] public class SharedMembersFile { public int Version = 1; public List Members = new List(); } [Serializable] public class SharedRanksFile { public int Version = 1; public List Ranks = new List(); } [Serializable] public class SharedElectionsFile { public int Version = 1; public List Elections = new List(); } [Serializable] public class SharedDiplomacyFile { public int Version = 1; public int NextAllianceId = 1; public List Diplomacy = new List(); public List Alliances = new List(); public List NonAggressionPacts = new List(); public List VassalRelations = new List(); } [Serializable] public class SharedClanDto { public int ClanId; public string Name = ""; public string Tag = ""; public int IconIndex; public float ColorR; public float ColorG; public float ColorB; public long FounderId; public long JarlId; public long CreatedTimestamp; } [Serializable] public class SharedMemberDto { public long PlayerId; public string PlayerName = ""; public int ClanId; public ClanRank Rank; public ClanRole Role; public ClanRole PrimaryRole; public ClanRole SecondaryRole; public ClanRole TertiaryRole; public long JoinedClanTimestamp; public long LastClanLeaveTimestamp; } [Serializable] public class SharedRankDto { public long PlayerId; public int ClanId; public ClanRank Rank; } [Serializable] public class SharedElectionDto { public int ClanId; public long JarlId; public long NextElectionTimestamp; public List Votes = new List(); } [Serializable] public class SharedElectionVoteDto { public long VoterId; public long CandidateId; } [Serializable] public class TerritoryBuilding { public const int MaxStoredProductionTicks = 4; public TerritoryBuildingType Type; public int Level = 1; public bool Damaged; public long BuiltAt; public long LastProductionTimestamp; public int StoredProductionTicks; public string StoredPrefabName = ""; public string StoredDisplayName = ""; public int StoredAmount; public long StoredCoins; } public enum TerritoryBuildingType { None, ClanHall, Treasury, Marketplace, TradePost, LumberCamp, Quarry, CopperMine, IronMine, SilverMine, Farm, FishingPort, Barracks, Watchtower, TownCenter, Longhouse, Warehouse, TaxOffice, Embassy, LogisticsPost, HunterLodge, Apiary, Tannery, Workshop, Smithy, Shipyard, Shrine, HealerHut } [Serializable] public class TerritoryGuard { public int GuardId; public int OwnerClanId; public string TerritoryId = ""; public GuardType GuardType; public int Level = 1; public GuardStatus Status; public long UpkeepCost; public long CreatedAt; public long LastPaidAt; public bool HasPosition; public Vector3 Position; } public enum TerritoryState { Normal, Contested, Attacked, Rooted, Captured } public enum TerritoryTransferStatus { Pending, Accepted, Declined, Expired } public enum TerritoryDealType { Gift, Sale, Exchange } [Serializable] public class TerritoryTransferOffer { public int OfferId; public int FromClanId; public int ToClanId; public string TerritoryId = ""; public long Price; public long CreatedAt; public long ExpiresAt; public TerritoryTransferStatus Status; public TerritoryDealType DealType; public List FromTerritoryIds = new List(); public List ToTerritoryIds = new List(); public long FromCoins; public long ToCoins; public List FromResources = new List(); public List ToResources = new List(); public bool FromAccepted = true; public bool ToAccepted; } public enum TerritoryType { Colony, Capital, Homestead, Outpost } public enum TotemState { Placed, Active, Decay, Destroyed } public enum VassalStatus { Pending, Active, Broken } [Serializable] public class VassalRelation { public int OverlordClanId; public string OverlordClanName = ""; public string OverlordClanTag = ""; public int VassalClanId; public string VassalClanName = ""; public string VassalClanTag = ""; public long StartTime; public VassalStatus Status; public int PendingRequestByClanId; public bool IsBetween(int a, int b) { if (OverlordClanId != a || VassalClanId != b) { if (OverlordClanId == b) { return VassalClanId == a; } return false; } return true; } } [Serializable] public class VaultCrackSession { public long PlayerId; public string PlayerName; public int PlayerClanId; public int TargetClanId; public int TerritoryGridX; public int TerritoryGridY; public long StartTimestamp; public Vector3 StartPosition; public int DurationMinutes = 30; public double MinutesElapsed => (double)(DateTimeOffset.UtcNow.ToUnixTimeSeconds() - StartTimestamp) / 60.0; public bool IsComplete => MinutesElapsed >= (double)DurationMinutes; public float Progress01 => Mathf.Clamp01((float)(MinutesElapsed / (double)Math.Max(1, DurationMinutes))); } [Serializable] public class War { public int WarId; public int AttackerClanId; public int DefenderClanId; public WarType Type = WarType.War; public long StartTimestamp; public long EndTimestamp; public bool IsActive => DateTimeOffset.UtcNow.ToUnixTimeSeconds() < EndTimestamp; public double MinutesRemaining => Math.Max(0.0, (double)(EndTimestamp - DateTimeOffset.UtcNow.ToUnixTimeSeconds()) / 60.0); public bool Involves(int clanId) { if (AttackerClanId != clanId) { return DefenderClanId == clanId; } return true; } public bool IsBetween(int a, int b) { if (AttackerClanId != a || DefenderClanId != b) { if (AttackerClanId == b) { return DefenderClanId == a; } return false; } return true; } } [Serializable] public class WarfareData { public const int DataVersion = 49; public int SeasonNumber = 1; public long SeasonStartTimestamp; public int NextClanId = 1; public int NextWarId = 1; public int NextAllianceId = 1; public int NextTerritoryDealId = 1; public int NextGuardId = 1; public int NextWorldEventId = 1; public int NextContractId = 1; public int NextGameId = 1; public int NextRotTotemId = 1; public List ActiveCracks = new List(); public List ActiveCaptures = new List(); public int Version { get; set; } = 49; public Dictionary Clans { get; set; } = new Dictionary(); public Dictionary Players { get; set; } = new Dictionary(); public Dictionary Territories { get; set; } = new Dictionary(); public Dictionary> ClanVaults { get; set; } = new Dictionary>(); public Dictionary> FreebornVaults { get; set; } = new Dictionary>(); public List RotTotems { get; set; } = new List(); public List PendingRootLoot { get; set; } = new List(); public List RootProductionRemainders { get; set; } = new List(); public List Wars { get; set; } = new List(); public List Diplomacy { get; set; } = new List(); public List Alliances { get; set; } = new List(); public List NonAggressionPacts { get; set; } = new List(); public List VassalRelations { get; set; } = new List(); public List TerritoryTransferOffers { get; set; } = new List(); public List BuildingProductionLog { get; set; } = new List(); public List Guards { get; set; } = new List(); public List GuardLog { get; set; } = new List(); public List WorldEvents { get; set; } = new List(); public List WorldEventLog { get; set; } = new List(); public List Contracts { get; set; } = new List(); public List ContractLog { get; set; } = new List(); public List Games { get; set; } = new List(); public List GameInvites { get; set; } = new List(); public List GameHistory { get; set; } = new List(); public Dictionary GameStats { get; set; } = new Dictionary(); public static string GridToKey(Vector2i grid) { //IL_0005: Unknown result type (might be due to invalid IL or missing references) //IL_0010: Unknown result type (might be due to invalid IL or missing references) return $"{grid.x},{grid.y}"; } public static string GridToKey(int x, int y) { return $"{x},{y}"; } public PlayerClanInfo GetPlayer(long playerId) { if (Players.TryGetValue(playerId, out var value)) { value.NormalizeRoles(); return value; } foreach (PlayerClanInfo value2 in Players.Values) { if (value2.LinkedPlayerIds != null && value2.LinkedPlayerIds.Contains(playerId)) { value2.NormalizeRoles(); return value2; } } return null; } public PlayerClanInfo AssociatePlayerIdentity(long playerId, string playerName, string steamId64, out bool conflict) { conflict = false; PlayerClanInfo player = GetPlayer(playerId); if (player != null) { if (!string.IsNullOrEmpty(steamId64)) { if (string.IsNullOrEmpty(player.SteamId64)) { player.SteamId64 = steamId64; } else if (!string.Equals(player.SteamId64, steamId64, StringComparison.Ordinal)) { conflict = true; } } if (!string.IsNullOrEmpty(playerName)) { player.PlayerName = playerName; } return player; } if (!string.IsNullOrEmpty(steamId64)) { List list = new List(); foreach (PlayerClanInfo value in Players.Values) { if (string.Equals(value.SteamId64, steamId64, StringComparison.Ordinal)) { list.Add(value); } } if (list.Count == 1) { PlayerClanInfo playerClanInfo = list[0]; PlayerClanInfo playerClanInfo2 = playerClanInfo; if (playerClanInfo2.LinkedPlayerIds == null) { playerClanInfo2.LinkedPlayerIds = new List(); } if (playerId != 0L && playerId != playerClanInfo.PlayerId && !playerClanInfo.LinkedPlayerIds.Contains(playerId)) { playerClanInfo.LinkedPlayerIds.Add(playerId); } if (!string.IsNullOrEmpty(playerName)) { playerClanInfo.PlayerName = playerName; } return playerClanInfo; } if (list.Count > 1) { conflict = true; } } return null; } public PlayerClanInfo GetOrCreatePlayer(long playerId, string playerName) { if (!Players.TryGetValue(playerId, out var value)) { long num = DateTimeOffset.UtcNow.ToUnixTimeSeconds(); value = new PlayerClanInfo { PlayerId = playerId, PlayerName = playerName, ClanId = 0, Role = ClanRole.None, PrimaryRole = ClanRole.None, SecondaryRole = ClanRole.None, TertiaryRole = ClanRole.None, FirstLoginTimestamp = num, LastLoginTimestamp = num, LastProfileMinuteTimestamp = num, TotalLogins = 1 }; Players[playerId] = value; } if (!string.IsNullOrEmpty(playerName)) { value.PlayerName = playerName; } DateTimeOffset utcNow = DateTimeOffset.UtcNow; long num2 = (value.LastSeenTimestamp = utcNow.ToUnixTimeSeconds()); if (value.FirstLoginTimestamp == 0L) { value.FirstLoginTimestamp = num2; value.TotalLogins = Math.Max(1, value.TotalLogins); } if (value.ClanId != 0 && Clans.TryGetValue(value.ClanId, out var value2)) { value2.LastActiveTimestamp = num2; } if (value.IsFreeborn && value.ClanId == 0) { value.FreebornLastActiveTimestamp = num2; } return value; } public Clan GetClan(int clanId) { if (!Clans.TryGetValue(clanId, out var value)) { return null; } return value; } public List GetClanMembers(int clanId) { List list = new List(); foreach (PlayerClanInfo value in Players.Values) { if (value.ClanId == clanId) { list.Add(value); } } return list; } public int GetClanMemberCount(int clanId) { int num = 0; foreach (PlayerClanInfo value in Players.Values) { if (value.ClanId == clanId) { num++; } } return num; } public ClanTerritory GetTerritory(Vector2i grid) { //IL_0006: Unknown result type (might be due to invalid IL or missing references) if (!Territories.TryGetValue(GridToKey(grid), out var value)) { return null; } return value; } public ClanTerritory GetTerritory(int x, int y) { if (!Territories.TryGetValue(GridToKey(x, y), out var value)) { return null; } return value; } public int GetClanTerritoryCount(int clanId) { int num = 0; foreach (ClanTerritory value in Territories.Values) { if (value.OwnerClanId == clanId) { num++; } } return num; } public List GetClanTerritories(int clanId) { List list = new List(); foreach (ClanTerritory value in Territories.Values) { if (value.OwnerClanId == clanId) { list.Add(value); } } return list; } public bool ClanHasCapital(int clanId) { foreach (ClanTerritory value in Territories.Values) { if (value.OwnerClanId == clanId && value.Type == TerritoryType.Capital) { return true; } } return false; } public void AddToVault(int clanId, string prefabName, string displayName, int amount) { if (!ClanVaults.ContainsKey(clanId)) { ClanVaults[clanId] = new List(); } List list = ClanVaults[clanId]; ClanVaultItem clanVaultItem = list.Find((ClanVaultItem v) => v.PrefabName == prefabName); if (clanVaultItem != null) { clanVaultItem.Amount += amount; return; } list.Add(new ClanVaultItem { PrefabName = prefabName, DisplayName = displayName, Amount = amount }); } public List GetVault(int clanId) { if (!ClanVaults.TryGetValue(clanId, out var value)) { return new List(); } return value; } public int GetVaultStackCount(int clanId) { if (!ClanVaults.TryGetValue(clanId, out var value)) { return 0; } return value.Count; } public void AddToFreebornVault(long playerId, string prefabName, string displayName, int amount) { if (!FreebornVaults.ContainsKey(playerId)) { FreebornVaults[playerId] = new List(); } List list = FreebornVaults[playerId]; ClanVaultItem clanVaultItem = list.Find((ClanVaultItem v) => v.PrefabName == prefabName); if (clanVaultItem != null) { clanVaultItem.Amount += amount; return; } list.Add(new ClanVaultItem { PrefabName = prefabName, DisplayName = displayName, Amount = amount }); } public List GetFreebornVault(long playerId) { if (!FreebornVaults.TryGetValue(playerId, out var value)) { return new List(); } return value; } public int GetFreebornVaultStackCount(long playerId) { if (!FreebornVaults.TryGetValue(playerId, out var value)) { return 0; } return value.Count; } public int GetLootReductionPercent(Vector2i grid) { //IL_0027: 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) int num = 0; foreach (RotTotem rotTotem in RotTotems) { if (rotTotem.State != TotemState.Destroyed && rotTotem.TerritoryGridX == grid.x && rotTotem.TerritoryGridY == grid.y) { num += rotTotem.LootReductionPercent; } } return Math.Min(num, 100); } public War GetActiveWarBetween(int clanA, int clanB) { foreach (War war in Wars) { if (war.IsActive && war.IsBetween(clanA, clanB)) { return war; } } return null; } public DiplomacyRelation GetDiplomacy(int clanA, int clanB) { return Diplomacy.Find((DiplomacyRelation d) => d.IsBetween(clanA, clanB)); } public List GetActiveWarsInvolving(int clanId) { List list = new List(); foreach (War war in Wars) { if (war.IsActive && war.Involves(clanId)) { list.Add(war); } } return list; } private static void WriteStringList(ZPackage pkg, List list) { if (list == null) { list = new List(); } pkg.Write(list.Count); foreach (string item in list) { pkg.Write(item ?? ""); } } private static List ReadStringList(ZPackage pkg) { int num = pkg.ReadInt(); List list = new List(); for (int i = 0; i < num; i++) { list.Add(pkg.ReadString()); } return list; } private static void WriteVaultItems(ZPackage pkg, List list) { if (list == null) { list = new List(); } pkg.Write(list.Count); foreach (ClanVaultItem item in list) { pkg.Write(item.PrefabName ?? ""); pkg.Write(item.DisplayName ?? ""); pkg.Write(item.Amount); } } private static List ReadVaultItems(ZPackage pkg) { int num = pkg.ReadInt(); List list = new List(); for (int i = 0; i < num; i++) { list.Add(new ClanVaultItem { PrefabName = pkg.ReadString(), DisplayName = pkg.ReadString(), Amount = pkg.ReadInt() }); } return list; } private static void WriteChessState(ZPackage pkg, ChessGameState state) { pkg.Write(state != null); if (state == null) { return; } pkg.Write(state.Board ?? ""); pkg.Write(state.WhitePlayerId); pkg.Write(state.BlackPlayerId); pkg.Write(state.WhiteToMove); pkg.Write(state.WhiteKingSide); pkg.Write(state.WhiteQueenSide); pkg.Write(state.BlackKingSide); pkg.Write(state.BlackQueenSide); pkg.Write(state.EnPassantSquare); pkg.Write(state.HalfMove); pkg.Write(state.FullMove); pkg.Write(state.TimeControlSeconds); pkg.Write(state.WhiteSeconds); pkg.Write(state.BlackSeconds); pkg.Write(state.LastClockTick); pkg.Write(state.DrawOfferedBy); pkg.Write(state.UndoRequestedBy); pkg.Write(state.WhiteDisconnectedAt); pkg.Write(state.BlackDisconnectedAt); pkg.Write((int)state.Result); pkg.Write(state.Moves?.Count ?? 0); foreach (ChessMoveRecord item in state.Moves ?? new List()) { pkg.Write(item.From); pkg.Write(item.To); pkg.Write((int)item.Piece); pkg.Write((int)item.Captured); pkg.Write(item.Notation ?? ""); pkg.Write(item.PreviousBoard ?? ""); pkg.Write(item.PreviousWhiteToMove); pkg.Write(item.PreviousWhiteKingSide); pkg.Write(item.PreviousWhiteQueenSide); pkg.Write(item.PreviousBlackKingSide); pkg.Write(item.PreviousBlackQueenSide); pkg.Write(item.PreviousEnPassant); pkg.Write(item.PreviousHalfMove); pkg.Write(item.PreviousWhiteSeconds); pkg.Write(item.PreviousBlackSeconds); } } private static ChessGameState ReadChessState(ZPackage pkg, bool includesDisconnectState) { if (!pkg.ReadBool()) { return null; } ChessGameState chessGameState = new ChessGameState { Board = pkg.ReadString(), WhitePlayerId = pkg.ReadLong(), BlackPlayerId = pkg.ReadLong(), WhiteToMove = pkg.ReadBool(), WhiteKingSide = pkg.ReadBool(), WhiteQueenSide = pkg.ReadBool(), BlackKingSide = pkg.ReadBool(), BlackQueenSide = pkg.ReadBool(), EnPassantSquare = pkg.ReadInt(), HalfMove = pkg.ReadInt(), FullMove = pkg.ReadInt(), TimeControlSeconds = pkg.ReadInt(), WhiteSeconds = pkg.ReadLong(), BlackSeconds = pkg.ReadLong(), LastClockTick = pkg.ReadLong(), DrawOfferedBy = pkg.ReadLong(), UndoRequestedBy = pkg.ReadLong() }; if (includesDisconnectState) { chessGameState.WhiteDisconnectedAt = pkg.ReadLong(); chessGameState.BlackDisconnectedAt = pkg.ReadLong(); } chessGameState.Result = (ChessMatchResult)pkg.ReadInt(); int num = pkg.ReadInt(); for (int i = 0; i < num; i++) { chessGameState.Moves.Add(new ChessMoveRecord { From = pkg.ReadInt(), To = pkg.ReadInt(), Piece = (char)pkg.ReadInt(), Captured = (char)pkg.ReadInt(), Notation = pkg.ReadString(), PreviousBoard = pkg.ReadString(), PreviousWhiteToMove = pkg.ReadBool(), PreviousWhiteKingSide = pkg.ReadBool(), PreviousWhiteQueenSide = pkg.ReadBool(), PreviousBlackKingSide = pkg.ReadBool(), PreviousBlackQueenSide = pkg.ReadBool(), PreviousEnPassant = pkg.ReadInt(), PreviousHalfMove = pkg.ReadInt(), PreviousWhiteSeconds = pkg.ReadLong(), PreviousBlackSeconds = pkg.ReadLong() }); } return chessGameState; } private static void WritePokerState(ZPackage pkg, PokerGameState state) { pkg.Write(state != null); if (state == null) { return; } pkg.Write((int)state.Phase); pkg.Write(state.DealerSeatIndex); pkg.Write(state.CurrentActorId); pkg.Write(state.CurrentBet); pkg.Write(state.MinRaise); pkg.Write(state.SmallBlind); pkg.Write(state.BigBlind); pkg.Write(state.HandNumber); pkg.Write(state.LastResult ?? ""); pkg.Write(state.LastActionAt); pkg.Write(state.HandLog?.Count ?? 0); foreach (string item in state.HandLog ?? new List()) { pkg.Write(item ?? ""); } pkg.Write(state.Deck?.Count ?? 0); foreach (int item2 in state.Deck ?? new List()) { pkg.Write(item2); } pkg.Write(state.CommunityCards?.Count ?? 0); foreach (int item3 in state.CommunityCards ?? new List()) { pkg.Write(item3); } pkg.Write(state.Seats?.Count ?? 0); foreach (PokerPlayerState item4 in state.Seats ?? new List()) { pkg.Write(item4.PlayerId); pkg.Write(item4.PlayerName ?? ""); pkg.Write(item4.Chips); pkg.Write(item4.RoundBet); pkg.Write(item4.TotalContribution); pkg.Write(item4.Folded); pkg.Write(item4.AllIn); pkg.Write(item4.HasActedThisRound); pkg.Write(item4.DisconnectedAt); pkg.Write(item4.HoleCards?.Count ?? 0); foreach (int item5 in item4.HoleCards ?? new List()) { pkg.Write(item5); } } } private static PokerGameState ReadPokerState(ZPackage pkg, bool includesActionState, bool includesHandLog) { if (!pkg.ReadBool()) { return null; } PokerGameState pokerGameState = new PokerGameState { Phase = (PokerPhase)pkg.ReadInt(), DealerSeatIndex = pkg.ReadInt(), CurrentActorId = pkg.ReadLong(), CurrentBet = pkg.ReadLong(), MinRaise = pkg.ReadLong(), SmallBlind = pkg.ReadLong(), BigBlind = pkg.ReadLong(), HandNumber = pkg.ReadLong(), LastResult = pkg.ReadString(), LastActionAt = pkg.ReadLong() }; if (includesHandLog) { int num = pkg.ReadInt(); for (int i = 0; i < num; i++) { pokerGameState.HandLog.Add(pkg.ReadString()); } } int num2 = pkg.ReadInt(); for (int j = 0; j < num2; j++) { pokerGameState.Deck.Add(pkg.ReadInt()); } int num3 = pkg.ReadInt(); for (int k = 0; k < num3; k++) { pokerGameState.CommunityCards.Add(pkg.ReadInt()); } int num4 = pkg.ReadInt(); for (int l = 0; l < num4; l++) { PokerPlayerState pokerPlayerState = new PokerPlayerState { PlayerId = pkg.ReadLong(), PlayerName = pkg.ReadString(), Chips = pkg.ReadLong(), RoundBet = pkg.ReadLong(), TotalContribution = pkg.ReadLong(), Folded = pkg.ReadBool(), AllIn = pkg.ReadBool() }; if (includesActionState) { pokerPlayerState.HasActedThisRound = pkg.ReadBool(); } pokerPlayerState.DisconnectedAt = pkg.ReadLong(); int num5 = pkg.ReadInt(); for (int m = 0; m < num5; m++) { pokerPlayerState.HoleCards.Add(pkg.ReadInt()); } pokerGameState.Seats.Add(pokerPlayerState); } return pokerGameState; } private static void WriteDiceState(ZPackage pkg, DiceGameState state) { pkg.Write(state != null); if (state == null) { return; } pkg.Write(state.CurrentPlayerIndex); pkg.Write(state.CurrentRound); pkg.Write(state.CurrentTurnStartedAt); pkg.Write(state.LastResult ?? ""); pkg.Write(state.ActivePlayerIds?.Count ?? 0); foreach (long item in state.ActivePlayerIds ?? new List()) { pkg.Write(item); } pkg.Write(state.Scores?.Count ?? 0); foreach (KeyValuePair item2 in state.Scores ?? new Dictionary()) { pkg.Write(item2.Key); pkg.Write(item2.Value); } pkg.Write(state.Rolls?.Count ?? 0); foreach (DiceRollRecord item3 in state.Rolls ?? new List()) { pkg.Write(item3.PlayerId); pkg.Write(item3.Round); pkg.Write(item3.Total); pkg.Write(item3.Timestamp); pkg.Write(item3.Rolls?.Count ?? 0); foreach (int item4 in item3.Rolls ?? new List()) { pkg.Write(item4); } } } private static DiceGameState ReadDiceState(ZPackage pkg) { if (!pkg.ReadBool()) { return null; } DiceGameState diceGameState = new DiceGameState { CurrentPlayerIndex = pkg.ReadInt(), CurrentRound = pkg.ReadInt(), CurrentTurnStartedAt = pkg.ReadLong(), LastResult = pkg.ReadString() }; int num = pkg.ReadInt(); for (int i = 0; i < num; i++) { diceGameState.ActivePlayerIds.Add(pkg.ReadLong()); } int num2 = pkg.ReadInt(); for (int j = 0; j < num2; j++) { diceGameState.Scores[pkg.ReadLong()] = pkg.ReadInt(); } int num3 = pkg.ReadInt(); for (int k = 0; k < num3; k++) { DiceRollRecord diceRollRecord = new DiceRollRecord { PlayerId = pkg.ReadLong(), Round = pkg.ReadInt(), Total = pkg.ReadInt(), Timestamp = pkg.ReadLong() }; int num4 = pkg.ReadInt(); for (int l = 0; l < num4; l++) { diceRollRecord.Rolls.Add(pkg.ReadInt()); } diceGameState.Rolls.Add(diceRollRecord); } return diceGameState; } public byte[] Serialize() { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_0006: Expected O, but got Unknown //IL_144f: Unknown result type (might be due to invalid IL or missing references) //IL_1b02: Unknown result type (might be due to invalid IL or missing references) //IL_1ccc: Unknown result type (might be due to invalid IL or missing references) //IL_1df0: Unknown result type (might be due to invalid IL or missing references) ZPackage val = new ZPackage(); val.Write(49); val.Write(SeasonNumber); val.Write(SeasonStartTimestamp); val.Write(NextClanId); val.Write(NextWarId); val.Write(NextAllianceId); val.Write(NextTerritoryDealId); val.Write(NextGuardId); val.Write(NextWorldEventId); val.Write(NextContractId); val.Write(NextGameId); val.Write(NextRotTotemId); val.Write(Clans.Count); foreach (KeyValuePair clan in Clans) { Clan value = clan.Value; val.Write(value.ClanId); val.Write(value.Name ?? ""); val.Write(value.Tag ?? ""); val.Write(value.IconIndex); val.Write(value.ColorR); val.Write(value.ColorG); val.Write(value.ColorB); val.Write(value.FounderId); val.Write(value.JarlId); val.Write(value.Treasury); val.Write(value.VaultTier); val.Write(value.ExtraTerritorySlots); val.Write(value.CreatedTimestamp); val.Write(value.NextElectionTimestamp); val.Write(value.Bounty); val.Write(value.DebtStartTimestamp); val.Write(value.ClanEmblemUrl ?? ""); val.Write(value.ClanEmblemName ?? ""); val.Write(value.ClanDiscordUrl ?? ""); val.Write(value.ClanWebsiteUrl ?? ""); val.Write(value.ClanSteamGroupUrl ?? ""); val.Write(value.ClanCustomUrl ?? ""); val.Write(value.ClanCustomLabel ?? ""); val.Write(value.ClanCustomUrl2 ?? ""); val.Write(value.ClanCustomLabel2 ?? ""); val.Write(value.ResourceTaxPercent); val.Write(value.CoinTaxPercent); val.Write(value.TotalResourceTaxCollected); val.Write(value.TotalCoinTaxCollected); val.Write(value.SessionResourceTaxCollected); val.Write(value.SessionCoinTaxCollected); val.Write(value.LastActiveTimestamp); Dictionary dictionary = value.ElectionVotes ?? new Dictionary(); val.Write(dictionary.Count); foreach (KeyValuePair item in dictionary) { val.Write(item.Key); val.Write(item.Value); } List list = value.TreasuryHistory ?? new List(); int num = Math.Min(100, list.Count); val.Write(num); for (int i = 0; i < num; i++) { EconomyTransaction economyTransaction = list[i]; val.Write(economyTransaction?.Timestamp ?? 0); val.Write(economyTransaction?.Type ?? ""); val.Write(economyTransaction?.Amount ?? 0); val.Write(economyTransaction?.BalanceBefore ?? 0); val.Write(economyTransaction?.BalanceAfter ?? 0); val.Write(economyTransaction?.Source ?? ""); val.Write(economyTransaction?.TerritoryId ?? ""); val.Write(economyTransaction?.BuildingId ?? ""); } } val.Write(Players.Count); foreach (KeyValuePair player in Players) { PlayerClanInfo value2 = player.Value; val.Write(value2.PlayerId); val.Write(value2.PlayerName ?? ""); val.Write(value2.ClanId); val.Write((int)value2.Role); val.Write((int)value2.Rank); val.Write(value2.LastRoleChangeTimestamp); val.Write(value2.LastVaultCrackTimestamp); val.Write(value2.JoinedClanTimestamp); val.Write(value2.LastClanLeaveTimestamp); val.Write(value2.LastSeenTimestamp); val.Write(value2.Bounty); value2.NormalizeRoles(); val.Write((int)value2.PrimaryRole); val.Write((int)value2.SecondaryRole); val.Write((int)value2.TertiaryRole); val.Write(value2.IsFreeborn); val.Write(value2.FreebornSinceTimestamp); val.Write(value2.LastFreebornChallengeTimestamp); List list2 = value2.FreebornFriends ?? new List(); val.Write(list2.Count); foreach (long item2 in list2) { val.Write(item2); } List list3 = value2.PendingFreebornFriendRequests ?? new List(); val.Write(list3.Count); foreach (long item3 in list3) { val.Write(item3); } List list4 = value2.PendingClanFounderInvites ?? new List(); val.Write(list4.Count); foreach (long item4 in list4) { val.Write(item4); } List list5 = value2.AcceptedClanFounderInvites ?? new List(); val.Write(list5.Count); foreach (long item5 in list5) { val.Write(item5); } val.Write(value2.FreebornTreasury); val.Write(value2.FreebornVaultTier); val.Write(value2.LastFreebornAbandonTimestamp); val.Write(value2.VaultPermissionsInitialized); val.Write(value2.CanDepositVaultItems); val.Write(value2.CanWithdrawVaultItems); val.Write(value2.CanDepositVaultCoins); val.Write(value2.CanWithdrawVaultCoins); val.Write(value2.FreebornLastActiveTimestamp); val.Write(value2.AboutMe ?? ""); val.Write(value2.ActiveTitle ?? ""); val.Write(value2.ProfileImageKey ?? ""); val.Write(value2.FirstLoginTimestamp); val.Write(value2.LastLoginTimestamp); val.Write(value2.TotalLogins); val.Write(value2.TotalPlayMinutes); val.Write(value2.LastProfileMinuteTimestamp); val.Write(value2.CoinsDonatedToTreasury); val.Write(value2.ItemsDonatedToVault); val.Write(value2.ResourceTaxPaid); val.Write(value2.CoinTaxPaid); val.Write(value2.TerritoriesClaimed); val.Write(value2.TerritoriesCaptured); val.Write(value2.TerritoriesLost); val.Write(value2.CapitalsFounded); val.Write(value2.HomesteadsFounded); val.Write(value2.OutpostsFounded); val.Write(value2.WarsParticipated); val.Write(value2.WarsWon); val.Write(value2.WarsLost); val.Write(value2.TerritoriesDefended); val.Write(value2.BountiesClaimed); val.Write(value2.BountiesSurvived); val.Write(value2.WorldEventsCompleted); val.Write(value2.TerritoriesDiscovered); val.Write(value2.BuildingsContributed); val.Write(value2.PvPKills); val.Write(value2.PvPDeaths); val.Write(value2.HighestDonation); List list6 = value2.SagaEntries ?? new List(); val.Write(list6.Count); foreach (string item6 in list6) { val.Write(item6 ?? ""); } Dictionary dictionary2 = value2.RoleMinutes ?? new Dictionary(); val.Write(dictionary2.Count); foreach (KeyValuePair item7 in dictionary2) { val.Write(item7.Key); val.Write(item7.Value); } val.Write(value2.SteamId64 ?? ""); List list7 = value2.LinkedPlayerIds ?? new List(); val.Write(list7.Count); foreach (long item8 in list7) { val.Write(item8); } List list8 = value2.ServerBadges ?? new List(); val.Write(list8.Count); foreach (string item9 in list8) { val.Write(item9 ?? ""); } List list9 = value2.UnlockedTitles ?? new List(); val.Write(list9.Count); foreach (string item10 in list9) { val.Write(item10 ?? ""); } val.Write(value2.ActiveTitleId ?? ""); Dictionary dictionary3 = value2.TitleUnlockTimestamps ?? new Dictionary(); val.Write(dictionary3.Count); foreach (KeyValuePair item11 in dictionary3) { val.Write(item11.Key ?? ""); val.Write(item11.Value); } List list10 = value2.UnlockedAchievements ?? new List(); val.Write(list10.Count); foreach (string item12 in list10) { val.Write(item12 ?? ""); } Dictionary dictionary4 = value2.AchievementUnlockTimestamps ?? new Dictionary(); val.Write(dictionary4.Count); foreach (KeyValuePair item13 in dictionary4) { val.Write(item13.Key ?? ""); val.Write(item13.Value); } List list11 = value2.RewardedAchievements ?? new List(); val.Write(list11.Count); foreach (string item14 in list11) { val.Write(item14 ?? ""); } Dictionary dictionary5 = value2.PendingAchievementCoinRewards ?? new Dictionary(); val.Write(dictionary5.Count); foreach (KeyValuePair item15 in dictionary5) { val.Write(item15.Key ?? ""); val.Write(item15.Value); } val.Write(value2.AchievementCoinsEarned); List list12 = value2.PersonalSaga ?? new List(); val.Write(list12.Count); foreach (PlayerSagaEntry item16 in list12) { val.Write(item16?.EntryId ?? ""); val.Write(item16?.TimestampUtc ?? 0); val.Write(item16?.EventType ?? ""); val.Write(item16?.Title ?? ""); val.Write(item16?.Description ?? ""); val.Write(item16?.Importance ?? "Normal"); val.Write(item16?.RelatedClanId ?? 0); val.Write(item16?.RelatedTerritoryId ?? 0); val.Write(item16?.RelatedPlayerId ?? 0); val.Write(item16?.RelatedAchievementId ?? ""); val.Write(item16?.RelatedTitleId ?? ""); val.Write(item16?.SourceKey ?? ""); } val.Write(value2.CanClaimTerritory); val.Write(value2.LastTheftAttemptTimestamp); val.Write(value2.LastTheftTargetedTimestamp); val.Write(value2.ForcedPvpUntilUtc); } val.Write(Territories.Count); foreach (KeyValuePair territory in Territories) { ClanTerritory value3 = territory.Value; val.Write(territory.Key); val.Write(value3.GridX); val.Write(value3.GridY); val.Write(value3.OwnerClanId); val.Write(value3.ClaimedByPlayerId); val.Write(value3.ClaimedByPlayerName ?? ""); val.Write(value3.ClaimTimestamp); val.Write(value3.CustomName ?? ""); val.Write(value3.LastRenameTimestamp); val.Write((int)value3.Type); val.Write((int)value3.State); val.Write(value3.SizeLevel); val.Write(value3.BiomeValueTier); val.Write(value3.BiomeName ?? ""); val.Write(value3.RotCount); val.Write(value3.LastRotTickTimestamp); val.Write(value3.LastIncomeTimestamp); val.Write(value3.HP); val.Write(value3.MaxHP); val.Write(value3.FreebornOwnerId); val.Write(value3.FreebornOwnerName ?? ""); val.Write(value3.OdinsPeaceActive); val.Write(value3.OdinsPeaceChallengeEndTimestamp); val.Write(value3.LastChallengeTimestamp); val.Write(value3.IsAbandoned); val.Write(value3.TerritoryStakeGameId); val.Write(value3.TerritoryStakeClanId); List list13 = value3.Buildings ?? new List(); val.Write(list13.Count); foreach (TerritoryBuilding item17 in list13) { val.Write((int)item17.Type); val.Write(item17.Level); val.Write(item17.Damaged); val.Write(item17.BuiltAt); val.Write(item17.LastProductionTimestamp); val.Write(item17.StoredProductionTicks); val.Write(item17.StoredPrefabName ?? ""); val.Write(item17.StoredDisplayName ?? ""); val.Write(item17.StoredAmount); val.Write(item17.StoredCoins); } List list14 = value3.TerritoryStorage ?? new List(); val.Write(list14.Count); foreach (ClanVaultItem item18 in list14) { val.Write(item18.PrefabName ?? ""); val.Write(item18.DisplayName ?? ""); val.Write(item18.Amount); } } val.Write(ClanVaults.Count); foreach (KeyValuePair> clanVault in ClanVaults) { val.Write(clanVault.Key); List list15 = clanVault.Value ?? new List(); val.Write(list15.Count); foreach (ClanVaultItem item19 in list15) { val.Write(item19.PrefabName ?? ""); val.Write(item19.DisplayName ?? ""); val.Write(item19.Amount); } } val.Write(FreebornVaults.Count); foreach (KeyValuePair> freebornVault in FreebornVaults) { val.Write(freebornVault.Key); List list16 = freebornVault.Value ?? new List(); val.Write(list16.Count); foreach (ClanVaultItem item20 in list16) { val.Write(item20.PrefabName ?? ""); val.Write(item20.DisplayName ?? ""); val.Write(item20.Amount); } } val.Write(RotTotems.Count); foreach (RotTotem rotTotem in RotTotems) { val.Write(rotTotem.TerritoryGridX); val.Write(rotTotem.TerritoryGridY); val.Write(rotTotem.PlacedByPlayerId); val.Write(rotTotem.PlacedByPlayerName ?? ""); val.Write(rotTotem.PlacerClanId); val.Write(rotTotem.PlacedTimestamp); val.Write(rotTotem.WorldPosition); val.Write((int)rotTotem.State); val.Write(rotTotem.Level); val.Write(rotTotem.LastDecayTimestamp); val.Write(rotTotem.TotemId); val.Write(rotTotem.PrefabName ?? "guard_stone"); } val.Write(Wars.Count); foreach (War war in Wars) { val.Write(war.WarId); val.Write(war.AttackerClanId); val.Write(war.DefenderClanId); val.Write((int)war.Type); val.Write(war.StartTimestamp); val.Write(war.EndTimestamp); } val.Write(Diplomacy.Count); foreach (DiplomacyRelation item21 in Diplomacy) { val.Write(item21.ClanA); val.Write(item21.ClanB); val.Write((int)item21.State); val.Write(item21.RequestedByClanId); val.Write(item21.UpdatedTimestamp); val.Write(item21.AllianceLeaderClanId); val.Write(item21.AllianceColorR); val.Write(item21.AllianceColorG); val.Write(item21.AllianceColorB); } val.Write(Alliances.Count); foreach (Alliance alliance in Alliances) { val.Write(alliance.AllianceId); val.Write(alliance.AllianceName ?? ""); val.Write(alliance.AllianceLeaderClanId); List list17 = alliance.MemberClanIds ?? new List(); val.Write(list17.Count); foreach (int item22 in list17) { val.Write(item22); } val.Write(alliance.AllianceColorR); val.Write(alliance.AllianceColorG); val.Write(alliance.AllianceColorB); val.Write(alliance.CreatedAt); List list18 = alliance.PendingInvites ?? new List(); val.Write(list18.Count); foreach (int item23 in list18) { val.Write(item23); } } val.Write(NonAggressionPacts.Count); foreach (NonAggressionPact nonAggressionPact in NonAggressionPacts) { val.Write(nonAggressionPact.ClanAId); val.Write(nonAggressionPact.ClanBId); val.Write(nonAggressionPact.StartTime); val.Write(nonAggressionPact.EndTime); val.Write(nonAggressionPact.BrokenAt); val.Write(nonAggressionPact.BreakCooldownEndTime); val.Write((int)nonAggressionPact.Status); val.Write(nonAggressionPact.RequestedByClanId); } val.Write(VassalRelations.Count); foreach (VassalRelation vassalRelation in VassalRelations) { val.Write(vassalRelation.OverlordClanId); val.Write(vassalRelation.VassalClanId); val.Write(vassalRelation.StartTime); val.Write((int)vassalRelation.Status); val.Write(vassalRelation.PendingRequestByClanId); val.Write(vassalRelation.OverlordClanName ?? ""); val.Write(vassalRelation.OverlordClanTag ?? ""); val.Write(vassalRelation.VassalClanName ?? ""); val.Write(vassalRelation.VassalClanTag ?? ""); } val.Write(TerritoryTransferOffers.Count); foreach (TerritoryTransferOffer territoryTransferOffer in TerritoryTransferOffers) { val.Write(territoryTransferOffer.OfferId); val.Write(territoryTransferOffer.FromClanId); val.Write(territoryTransferOffer.ToClanId); val.Write(territoryTransferOffer.TerritoryId ?? ""); val.Write(territoryTransferOffer.Price); val.Write(territoryTransferOffer.CreatedAt); val.Write(territoryTransferOffer.ExpiresAt); val.Write((int)territoryTransferOffer.Status); val.Write((int)territoryTransferOffer.DealType); WriteStringList(val, territoryTransferOffer.FromTerritoryIds); WriteStringList(val, territoryTransferOffer.ToTerritoryIds); val.Write(territoryTransferOffer.FromCoins); val.Write(territoryTransferOffer.ToCoins); WriteVaultItems(val, territoryTransferOffer.FromResources); WriteVaultItems(val, territoryTransferOffer.ToResources); val.Write(territoryTransferOffer.FromAccepted); val.Write(territoryTransferOffer.ToAccepted); } List list19 = BuildingProductionLog ?? new List(); val.Write(list19.Count); foreach (string item24 in list19) { val.Write(item24 ?? ""); } val.Write(ActiveCracks.Count); foreach (VaultCrackSession activeCrack in ActiveCracks) { val.Write(activeCrack.PlayerId); val.Write(activeCrack.PlayerName ?? ""); val.Write(activeCrack.PlayerClanId); val.Write(activeCrack.TargetClanId); val.Write(activeCrack.TerritoryGridX); val.Write(activeCrack.TerritoryGridY); val.Write(activeCrack.StartTimestamp); val.Write(activeCrack.StartPosition); val.Write(activeCrack.DurationMinutes); } val.Write(ActiveCaptures.Count); foreach (CaptureSession activeCapture in ActiveCaptures) { val.Write(activeCapture.TerritoryGridX); val.Write(activeCapture.TerritoryGridY); val.Write(activeCapture.AttackerClanId); val.Write(activeCapture.DefenderClanId); val.Write(activeCapture.Progress); val.Write(activeCapture.Threshold); val.Write(activeCapture.CombatPoints); val.Write(activeCapture.StartTimestamp); val.Write(activeCapture.LastAttackerSeenTime); val.Write(activeCapture.LastDefenderMajorityTime); val.Write(activeCapture.Siege); } List list20 = Guards ?? new List(); val.Write(list20.Count); foreach (TerritoryGuard item25 in list20) { val.Write(item25.GuardId); val.Write(item25.OwnerClanId); val.Write(item25.TerritoryId ?? ""); val.Write((int)item25.GuardType); val.Write(item25.Level); val.Write((int)item25.Status); val.Write(item25.UpkeepCost); val.Write(item25.CreatedAt); val.Write(item25.LastPaidAt); val.Write(item25.HasPosition); val.Write(item25.Position); } List list21 = GuardLog ?? new List(); val.Write(list21.Count); foreach (string item26 in list21) { val.Write(item26 ?? ""); } List list22 = WorldEvents ?? new List(); val.Write(list22.Count); foreach (WorldEvent item27 in list22) { val.Write(item27.EventId); val.Write((int)item27.EventType); val.Write(item27.EventName ?? ""); val.Write(item27.Description ?? ""); val.Write(item27.StartTime); val.Write(item27.EndTime); val.Write(item27.Duration); val.Write(item27.Position); val.Write(item27.Radius); val.Write((int)item27.Status); val.Write(item27.RewardCoins); val.Write(item27.RewardSummary ?? ""); val.Write(item27.ClaimedByPlayerId); val.Write(item27.ClaimedByPlayerName ?? ""); } List list23 = WorldEventLog ?? new List(); val.Write(list23.Count); foreach (string item28 in list23) { val.Write(item28 ?? ""); } List list24 = Contracts ?? new List(); val.Write(list24.Count); foreach (Contract item29 in list24) { val.Write(item29.ContractId); val.Write((int)item29.ContractType); val.Write(item29.Title ?? ""); val.Write(item29.Description ?? ""); val.Write((int)item29.IssuerType); val.Write(item29.IssuerId); val.Write((int)item29.TargetType); val.Write(item29.TargetId ?? ""); val.Write(item29.RequiredItem ?? ""); val.Write(item29.RequiredAmount); val.Write(item29.RewardCoins); val.Write(item29.RewardMaterials ?? ""); val.Write((int)item29.Status); val.Write(item29.CreatedAt); val.Write(item29.ExpiresAt); val.Write(item29.AcceptedBy); } List list25 = ContractLog ?? new List(); val.Write(list25.Count); foreach (string item30 in list25) { val.Write(item30 ?? ""); } List list26 = Games ?? new List(); val.Write(list26.Count); foreach (FehuGameTable item31 in list26) { val.Write(item31.GameId); val.Write((int)item31.GameType); val.Write(item31.TableName ?? ""); val.Write(item31.HostPlayerId); val.Write(item31.HostPlayerName ?? ""); val.Write(item31.PlayerIds?.Count ?? 0); foreach (long item32 in item31.PlayerIds ?? new List()) { val.Write(item32); } val.Write(item31.SpectatorIds?.Count ?? 0); foreach (long item33 in item31.SpectatorIds ?? new List()) { val.Write(item33); } val.Write(item31.PlayerLimit); val.Write(item31.IsPrivate); val.Write(item31.AllowSpectators); val.Write((int)item31.StakeType); val.Write(item31.StakeAmount); val.Write(item31.StakePrefabName ?? ""); val.Write((int)item31.Status); val.Write(item31.CreatedAt); val.Write(item31.StartedAt); val.Write(item31.FinishedAt); val.Write(item31.WinnerPlayerId); val.Write(item31.Result ?? ""); val.Write(item31.DiceCount); val.Write(item31.RoundCount); val.Write((int)item31.DiceMode); val.Write(item31.EscrowPlayerIds?.Count ?? 0); foreach (long item34 in item31.EscrowPlayerIds ?? new List()) { val.Write(item34); } val.Write(item31.EscrowTotal); val.Write(item31.EscrowCollecting); val.Write(item31.PayoutIssued); WriteChessState(val, item31.Chess); val.Write(item31.ChessHostColorPreference); val.Write(item31.ChessTimeControlSeconds); WritePokerState(val, item31.Poker); val.Write(item31.PokerBuyIn); val.Write(item31.PokerSmallBlind); val.Write(item31.PokerBigBlind); WriteDiceState(val, item31.Dice); WriteStringList(val, item31.HostStakeTerritoryIds); WriteStringList(val, item31.ChallengerStakeTerritoryIds); val.Write(item31.HostStakeClanId); val.Write(item31.ChallengerStakeClanId); val.Write(item31.HostStakeAccepted); val.Write(item31.ChallengerStakeAccepted); } List list27 = GameInvites ?? new List(); val.Write(list27.Count); foreach (FehuGameInvite item35 in list27) { val.Write(item35.GameId); val.Write((int)item35.GameType); val.Write(item35.FromPlayerId); val.Write(item35.ToPlayerId); val.Write(item35.CreatedAt); val.Write(item35.ExpiresAt); } List list28 = GameHistory ?? new List(); val.Write(list28.Count); foreach (FehuGameHistoryEntry item36 in list28) { val.Write(item36.GameId); val.Write((int)item36.GameType); val.Write(item36.TableName ?? ""); val.Write(item36.PlayerIds?.Count ?? 0); foreach (long item37 in item36.PlayerIds ?? new List()) { val.Write(item37); } val.Write(item36.WinnerPlayerId); val.Write(item36.Result ?? ""); val.Write((int)item36.StakeType); val.Write(item36.StakeAmount); val.Write(item36.StakePrefabName ?? ""); val.Write(item36.CompletedAt); WriteStringList(val, item36.HostStakeTerritoryIds); WriteStringList(val, item36.ChallengerStakeTerritoryIds); val.Write(item36.HostStakeClanId); val.Write(item36.ChallengerStakeClanId); val.Write(item36.TerritoryTransferResult ?? ""); } Dictionary dictionary6 = GameStats ?? new Dictionary(); val.Write(dictionary6.Count); foreach (KeyValuePair item38 in dictionary6) { FehuGamePlayerStats value4 = item38.Value; val.Write(item38.Key); val.Write(value4.GamesPlayed); val.Write(value4.Wins); val.Write(value4.Losses); val.Write(value4.Profit); val.Write(value4.HighestDiceRoll); val.Write(value4.BiggestWin); val.Write((int)value4.FavoriteGame); val.Write(value4.ChessGamesPlayed); val.Write(value4.ChessWins); val.Write(value4.ChessLosses); val.Write(value4.ChessDraws); val.Write(value4.ChessRating); val.Write(value4.ChessCurrentStreak); val.Write(value4.ChessBestStreak); val.Write(value4.PokerGamesPlayed); val.Write(value4.PokerHandsWon); val.Write(value4.PokerTablesWon); val.Write(value4.PokerBiggestPot); val.Write(value4.PokerBiggestWin); val.Write(value4.PokerProfit); val.Write(value4.DiceCurrentStreak); val.Write(value4.DiceBestStreak); } List list29 = PendingRootLoot ?? new List(); val.Write(list29.Count); foreach (PendingRootLoot item39 in list29) { val.Write(item39.RootClanId); val.Write(item39.TerritoryGridX); val.Write(item39.TerritoryGridY); val.Write(item39.TotemId); val.Write(item39.PrefabName ?? ""); val.Write(item39.DisplayName ?? ""); val.Write(item39.Amount); } List list30 = RootProductionRemainders ?? new List(); val.Write(list30.Count); foreach (RootProductionRemainder item40 in list30) { val.Write(item40.ReceivingClanId); val.Write(item40.TerritoryGridX); val.Write(item40.TerritoryGridY); val.Write(item40.BuildingBuiltAt); val.Write(item40.PrefabName ?? ""); val.Write(item40.DisplayName ?? ""); val.Write(item40.OwnerShare); val.Write(item40.QuarterUnits); } return val.GetArray(); } public static WarfareData Deserialize(byte[] data) { //IL_0011: Unknown result type (might be due to invalid IL or missing references) //IL_0017: Expected O, but got Unknown //IL_0f42: Unknown result type (might be due to invalid IL or missing references) //IL_0f47: Unknown result type (might be due to invalid IL or missing references) //IL_158e: Unknown result type (might be due to invalid IL or missing references) //IL_1593: Unknown result type (might be due to invalid IL or missing references) //IL_1708: Unknown result type (might be due to invalid IL or missing references) //IL_170d: Unknown result type (might be due to invalid IL or missing references) //IL_17c9: Unknown result type (might be due to invalid IL or missing references) //IL_17ce: Unknown result type (might be due to invalid IL or missing references) WarfareData warfareData = new WarfareData(); if (data == null || data.Length == 0) { return warfareData; } try { ZPackage val = new ZPackage(data); int num = val.ReadInt(); if (num < 3) { Debug.LogWarning((object)($"[ClanWarfare] Save format v{num} predates the warfare redesign. " + "Starting a fresh season (Cycle of Odin).")); warfareData.SeasonStartTimestamp = DateTimeOffset.UtcNow.ToUnixTimeSeconds(); return warfareData; } warfareData.Version = num; warfareData.SeasonNumber = val.ReadInt(); warfareData.SeasonStartTimestamp = val.ReadLong(); warfareData.NextClanId = val.ReadInt(); warfareData.NextWarId = val.ReadInt(); if (num >= 18) { warfareData.NextAllianceId = val.ReadInt(); warfareData.NextTerritoryDealId = val.ReadInt(); } if (num >= 23) { warfareData.NextGuardId = val.ReadInt(); } if (num >= 24) { warfareData.NextWorldEventId = val.ReadInt(); } if (num >= 25) { warfareData.NextContractId = val.ReadInt(); } if (num >= 28) { warfareData.NextGameId = val.ReadInt(); } if (num >= 40) { warfareData.NextRotTotemId = val.ReadInt(); } int num2 = val.ReadInt(); for (int i = 0; i < num2; i++) { Clan clan = new Clan { ClanId = val.ReadInt(), Name = val.ReadString(), Tag = val.ReadString(), IconIndex = val.ReadInt(), ColorR = val.ReadSingle(), ColorG = val.ReadSingle(), ColorB = val.ReadSingle(), FounderId = val.ReadLong(), JarlId = val.ReadLong(), Treasury = val.ReadLong(), VaultTier = val.ReadInt(), ExtraTerritorySlots = val.ReadInt(), CreatedTimestamp = val.ReadLong(), NextElectionTimestamp = val.ReadLong(), Bounty = val.ReadLong() }; if (num >= 40) { clan.DebtStartTimestamp = val.ReadLong(); } if (num >= 5) { clan.ClanEmblemUrl = val.ReadString(); } if (num >= 10) { clan.ClanEmblemName = val.ReadString(); } if (num >= 10) { clan.ClanDiscordUrl = val.ReadString(); clan.ClanWebsiteUrl = val.ReadString(); } if (num >= 12) { clan.ClanSteamGroupUrl = val.ReadString(); } if (num >= 13) { clan.ClanCustomUrl = val.ReadString(); clan.ClanCustomLabel = val.ReadString(); } if (num >= 14) { clan.ClanCustomUrl2 = val.ReadString(); clan.ClanCustomLabel2 = val.ReadString(); } if (num >= 17) { clan.ResourceTaxPercent = val.ReadInt(); clan.CoinTaxPercent = val.ReadInt(); clan.TotalResourceTaxCollected = val.ReadLong(); clan.TotalCoinTaxCollected = val.ReadLong(); clan.SessionResourceTaxCollected = val.ReadLong(); clan.SessionCoinTaxCollected = val.ReadLong(); } if (num >= 22) { clan.LastActiveTimestamp = val.ReadLong(); } int num3 = val.ReadInt(); for (int j = 0; j < num3; j++) { long key = val.ReadLong(); long value = val.ReadLong(); clan.ElectionVotes[key] = value; } if (num >= 47) { int num4 = val.ReadInt(); for (int k = 0; k < num4; k++) { EconomyTransaction item = new EconomyTransaction { Timestamp = val.ReadLong(), Type = val.ReadString(), Amount = val.ReadLong(), BalanceBefore = val.ReadLong(), BalanceAfter = val.ReadLong(), Source = val.ReadString(), TerritoryId = val.ReadString(), BuildingId = val.ReadString() }; if (clan.TreasuryHistory.Count < 100) { clan.TreasuryHistory.Add(item); } } } warfareData.Clans[clan.ClanId] = clan; } int num5 = val.ReadInt(); for (int l = 0; l < num5; l++) { PlayerClanInfo playerClanInfo = new PlayerClanInfo { PlayerId = val.ReadLong(), PlayerName = val.ReadString(), ClanId = val.ReadInt(), Role = (ClanRole)val.ReadInt(), Rank = (ClanRank)val.ReadInt(), LastRoleChangeTimestamp = val.ReadLong(), LastVaultCrackTimestamp = val.ReadLong(), JoinedClanTimestamp = val.ReadLong(), LastClanLeaveTimestamp = val.ReadLong(), LastSeenTimestamp = val.ReadLong(), Bounty = val.ReadLong() }; playerClanInfo.NormalizeRoles(num < 16); if (num >= 4) { playerClanInfo.PrimaryRole = PlayerClanInfo.NormalizeRole((ClanRole)val.ReadInt(), num < 16); playerClanInfo.SecondaryRole = PlayerClanInfo.NormalizeRole((ClanRole)val.ReadInt(), num < 16); if (num >= 16) { playerClanInfo.TertiaryRole = PlayerClanInfo.NormalizeRole((ClanRole)val.ReadInt()); } playerClanInfo.IsFreeborn = val.ReadBool(); playerClanInfo.FreebornSinceTimestamp = val.ReadLong(); playerClanInfo.LastFreebornChallengeTimestamp = val.ReadLong(); playerClanInfo.NormalizeRoles(num < 16); } if (num >= 6) { int num6 = val.ReadInt(); for (int m = 0; m < num6; m++) { playerClanInfo.FreebornFriends.Add(val.ReadLong()); } int num7 = val.ReadInt(); for (int n = 0; n < num7; n++) { playerClanInfo.PendingFreebornFriendRequests.Add(val.ReadLong()); } if (num >= 38) { int num8 = val.ReadInt(); for (int num9 = 0; num9 < num8; num9++) { playerClanInfo.PendingClanFounderInvites.Add(val.ReadLong()); } int num10 = val.ReadInt(); for (int num11 = 0; num11 < num10; num11++) { playerClanInfo.AcceptedClanFounderInvites.Add(val.ReadLong()); } } } if (num >= 7) { playerClanInfo.FreebornTreasury = val.ReadLong(); } if (num >= 10) { playerClanInfo.FreebornVaultTier = val.ReadInt(); } if (num >= 9) { playerClanInfo.LastFreebornAbandonTimestamp = val.ReadLong(); } if (num >= 8) { playerClanInfo.VaultPermissionsInitialized = val.ReadBool(); playerClanInfo.CanDepositVaultItems = val.ReadBool(); playerClanInfo.CanWithdrawVaultItems = val.ReadBool(); playerClanInfo.CanDepositVaultCoins = val.ReadBool(); playerClanInfo.CanWithdrawVaultCoins = val.ReadBool(); } if (num >= 22) { playerClanInfo.FreebornLastActiveTimestamp = val.ReadLong(); } if (num >= 27) { playerClanInfo.AboutMe = val.ReadString(); playerClanInfo.ActiveTitle = val.ReadString(); if (num >= 43) { playerClanInfo.ProfileImageKey = val.ReadString(); } playerClanInfo.FirstLoginTimestamp = val.ReadLong(); playerClanInfo.LastLoginTimestamp = val.ReadLong(); playerClanInfo.TotalLogins = val.ReadInt(); playerClanInfo.TotalPlayMinutes = val.ReadLong(); playerClanInfo.LastProfileMinuteTimestamp = val.ReadLong(); playerClanInfo.CoinsDonatedToTreasury = val.ReadLong(); playerClanInfo.ItemsDonatedToVault = val.ReadLong(); playerClanInfo.ResourceTaxPaid = val.ReadLong(); playerClanInfo.CoinTaxPaid = val.ReadLong(); playerClanInfo.TerritoriesClaimed = val.ReadInt(); playerClanInfo.TerritoriesCaptured = val.ReadInt(); playerClanInfo.TerritoriesLost = val.ReadInt(); playerClanInfo.CapitalsFounded = val.ReadInt(); playerClanInfo.HomesteadsFounded = val.ReadInt(); playerClanInfo.OutpostsFounded = val.ReadInt(); playerClanInfo.WarsParticipated = val.ReadInt(); playerClanInfo.WarsWon = val.ReadInt(); playerClanInfo.WarsLost = val.ReadInt(); playerClanInfo.TerritoriesDefended = val.ReadInt(); playerClanInfo.BountiesClaimed = val.ReadInt(); playerClanInfo.BountiesSurvived = val.ReadInt(); playerClanInfo.WorldEventsCompleted = val.ReadInt(); playerClanInfo.TerritoriesDiscovered = val.ReadInt(); playerClanInfo.BuildingsContributed = val.ReadInt(); playerClanInfo.PvPKills = val.ReadInt(); playerClanInfo.PvPDeaths = val.ReadInt(); playerClanInfo.HighestDonation = val.ReadLong(); int num12 = val.ReadInt(); for (int num13 = 0; num13 < num12; num13++) { playerClanInfo.SagaEntries.Add(val.ReadString()); } int num14 = val.ReadInt(); for (int num15 = 0; num15 < num14; num15++) { playerClanInfo.RoleMinutes[val.ReadInt()] = val.ReadLong(); } } if (num >= 42) { playerClanInfo.SteamId64 = val.ReadString(); int num16 = val.ReadInt(); for (int num17 = 0; num17 < num16; num17++) { playerClanInfo.LinkedPlayerIds.Add(val.ReadLong()); } int num18 = val.ReadInt(); for (int num19 = 0; num19 < num18; num19++) { playerClanInfo.ServerBadges.Add(val.ReadString()); } } if (num >= 44) { int num20 = val.ReadInt(); for (int num21 = 0; num21 < num20; num21++) { playerClanInfo.UnlockedTitles.Add(val.ReadString()); } playerClanInfo.ActiveTitleId = val.ReadString(); int num22 = val.ReadInt(); for (int num23 = 0; num23 < num22; num23++) { playerClanInfo.TitleUnlockTimestamps[val.ReadString()] = val.ReadLong(); } } if (num >= 45) { int num24 = val.ReadInt(); for (int num25 = 0; num25 < num24; num25++) { playerClanInfo.UnlockedAchievements.Add(val.ReadString()); } num24 = val.ReadInt(); for (int num26 = 0; num26 < num24; num26++) { playerClanInfo.AchievementUnlockTimestamps[val.ReadString()] = val.ReadLong(); } num24 = val.ReadInt(); for (int num27 = 0; num27 < num24; num27++) { playerClanInfo.RewardedAchievements.Add(val.ReadString()); } num24 = val.ReadInt(); for (int num28 = 0; num28 < num24; num28++) { playerClanInfo.PendingAchievementCoinRewards[val.ReadString()] = val.ReadInt(); } playerClanInfo.AchievementCoinsEarned = val.ReadLong(); } if (num >= 46) { int num29 = val.ReadInt(); for (int num30 = 0; num30 < num29; num30++) { playerClanInfo.PersonalSaga.Add(new PlayerSagaEntry { EntryId = val.ReadString(), TimestampUtc = val.ReadLong(), EventType = val.ReadString(), Title = val.ReadString(), Description = val.ReadString(), Importance = val.ReadString(), RelatedClanId = val.ReadInt(), RelatedTerritoryId = val.ReadInt(), RelatedPlayerId = val.ReadLong(), RelatedAchievementId = val.ReadString(), RelatedTitleId = val.ReadString(), SourceKey = val.ReadString() }); } } playerClanInfo.CanClaimTerritory = ((num >= 48) ? val.ReadBool() : (playerClanInfo.Rank == ClanRank.Jarl)); if (num >= 49) { playerClanInfo.LastTheftAttemptTimestamp = val.ReadLong(); playerClanInfo.LastTheftTargetedTimestamp = val.ReadLong(); playerClanInfo.ForcedPvpUntilUtc = val.ReadLong(); } warfareData.Players[playerClanInfo.PlayerId] = playerClanInfo; } int num31 = val.ReadInt(); for (int num32 = 0; num32 < num31; num32++) { string key2 = val.ReadString(); ClanTerritory clanTerritory = new ClanTerritory { GridX = val.ReadInt(), GridY = val.ReadInt(), OwnerClanId = val.ReadInt(), ClaimedByPlayerId = val.ReadLong(), ClaimedByPlayerName = val.ReadString(), ClaimTimestamp = val.ReadLong(), CustomName = val.ReadString(), LastRenameTimestamp = val.ReadLong(), Type = (TerritoryType)val.ReadInt(), State = (TerritoryState)val.ReadInt(), SizeLevel = val.ReadInt(), BiomeValueTier = val.ReadInt(), BiomeName = ((num >= 15) ? val.ReadString() : ""), RotCount = val.ReadInt(), LastRotTickTimestamp = val.ReadLong(), LastIncomeTimestamp = val.ReadLong(), HP = val.ReadSingle(), MaxHP = val.ReadSingle() }; if (num >= 4) { clanTerritory.FreebornOwnerId = val.ReadLong(); clanTerritory.FreebornOwnerName = val.ReadString(); clanTerritory.OdinsPeaceActive = val.ReadBool(); clanTerritory.OdinsPeaceChallengeEndTimestamp = val.ReadLong(); clanTerritory.LastChallengeTimestamp = val.ReadLong(); clanTerritory.IsAbandoned = val.ReadBool(); if (num >= 37) { clanTerritory.TerritoryStakeGameId = val.ReadInt(); clanTerritory.TerritoryStakeClanId = val.ReadInt(); } } if (num >= 19) { int num33 = val.ReadInt(); clanTerritory.Buildings = new List(); for (int num34 = 0; num34 < num33; num34++) { TerritoryBuilding territoryBuilding = new TerritoryBuilding { Type = (TerritoryBuildingType)val.ReadInt(), Level = val.ReadInt(), Damaged = val.ReadBool(), BuiltAt = val.ReadLong(), LastProductionTimestamp = val.ReadLong() }; if (num >= 39) { territoryBuilding.StoredProductionTicks = val.ReadInt(); territoryBuilding.StoredPrefabName = val.ReadString(); territoryBuilding.StoredDisplayName = val.ReadString(); territoryBuilding.StoredAmount = val.ReadInt(); territoryBuilding.StoredCoins = val.ReadLong(); } clanTerritory.Buildings.Add(territoryBuilding); } int num35 = val.ReadInt(); clanTerritory.TerritoryStorage = new List(); for (int num36 = 0; num36 < num35; num36++) { clanTerritory.TerritoryStorage.Add(new ClanVaultItem { PrefabName = val.ReadString(), DisplayName = val.ReadString(), Amount = val.ReadInt() }); } } if (string.IsNullOrEmpty(clanTerritory.CustomName)) { clanTerritory.CustomName = null; } warfareData.Territories[key2] = clanTerritory; } int num37 = val.ReadInt(); for (int num38 = 0; num38 < num37; num38++) { int key3 = val.ReadInt(); int num39 = val.ReadInt(); List list = new List(); for (int num40 = 0; num40 < num39; num40++) { list.Add(new ClanVaultItem { PrefabName = val.ReadString(), DisplayName = val.ReadString(), Amount = val.ReadInt() }); } warfareData.ClanVaults[key3] = list; } if (num >= 10) { int num41 = val.ReadInt(); for (int num42 = 0; num42 < num41; num42++) { long key4 = val.ReadLong(); int num43 = val.ReadInt(); List list2 = new List(); for (int num44 = 0; num44 < num43; num44++) { list2.Add(new ClanVaultItem { PrefabName = val.ReadString(), DisplayName = val.ReadString(), Amount = val.ReadInt() }); } warfareData.FreebornVaults[key4] = list2; } } int num45 = val.ReadInt(); for (int num46 = 0; num46 < num45; num46++) { RotTotem rotTotem = new RotTotem { TerritoryGridX = val.ReadInt(), TerritoryGridY = val.ReadInt(), PlacedByPlayerId = val.ReadLong(), PlacedByPlayerName = val.ReadString(), PlacerClanId = val.ReadInt(), PlacedTimestamp = val.ReadLong(), WorldPosition = val.ReadVector3(), State = (TotemState)val.ReadInt(), Level = val.ReadInt(), LastDecayTimestamp = val.ReadLong() }; if (num >= 40) { rotTotem.TotemId = val.ReadInt(); rotTotem.PrefabName = val.ReadString(); } warfareData.RotTotems.Add(rotTotem); } int num47 = val.ReadInt(); for (int num48 = 0; num48 < num47; num48++) { warfareData.Wars.Add(new War { WarId = val.ReadInt(), AttackerClanId = val.ReadInt(), DefenderClanId = val.ReadInt(), Type = (WarType)val.ReadInt(), StartTimestamp = val.ReadLong(), EndTimestamp = val.ReadLong() }); } if (num >= 4) { int num49 = val.ReadInt(); for (int num50 = 0; num50 < num49; num50++) { DiplomacyRelation diplomacyRelation = new DiplomacyRelation { ClanA = val.ReadInt(), ClanB = val.ReadInt(), State = (DiplomacyState)val.ReadInt(), RequestedByClanId = val.ReadInt(), UpdatedTimestamp = val.ReadLong() }; if (num >= 6) { diplomacyRelation.AllianceLeaderClanId = val.ReadInt(); diplomacyRelation.AllianceColorR = val.ReadSingle(); diplomacyRelation.AllianceColorG = val.ReadSingle(); diplomacyRelation.AllianceColorB = val.ReadSingle(); } warfareData.Diplomacy.Add(diplomacyRelation); } } if (num >= 18) { int num51 = val.ReadInt(); for (int num52 = 0; num52 < num51; num52++) { Alliance alliance = new Alliance { AllianceId = val.ReadInt(), AllianceName = val.ReadString(), AllianceLeaderClanId = val.ReadInt() }; int num53 = val.ReadInt(); for (int num54 = 0; num54 < num53; num54++) { alliance.MemberClanIds.Add(val.ReadInt()); } alliance.AllianceColorR = val.ReadSingle(); alliance.AllianceColorG = val.ReadSingle(); alliance.AllianceColorB = val.ReadSingle(); alliance.CreatedAt = val.ReadLong(); int num55 = val.ReadInt(); for (int num56 = 0; num56 < num55; num56++) { alliance.PendingInvites.Add(val.ReadInt()); } warfareData.Alliances.Add(alliance); } int num57 = val.ReadInt(); for (int num58 = 0; num58 < num57; num58++) { warfareData.NonAggressionPacts.Add(new NonAggressionPact { ClanAId = val.ReadInt(), ClanBId = val.ReadInt(), StartTime = val.ReadLong(), EndTime = val.ReadLong(), BrokenAt = val.ReadLong(), BreakCooldownEndTime = val.ReadLong(), Status = (PactStatus)val.ReadInt(), RequestedByClanId = val.ReadInt() }); } int num59 = val.ReadInt(); for (int num60 = 0; num60 < num59; num60++) { VassalRelation vassalRelation = new VassalRelation { OverlordClanId = val.ReadInt(), VassalClanId = val.ReadInt(), StartTime = val.ReadLong(), Status = (VassalStatus)val.ReadInt(), PendingRequestByClanId = val.ReadInt() }; if (num >= 21) { vassalRelation.OverlordClanName = val.ReadString(); vassalRelation.OverlordClanTag = val.ReadString(); vassalRelation.VassalClanName = val.ReadString(); vassalRelation.VassalClanTag = val.ReadString(); } else { Clan clan2 = warfareData.GetClan(vassalRelation.OverlordClanId); Clan clan3 = warfareData.GetClan(vassalRelation.VassalClanId); vassalRelation.OverlordClanName = clan2?.Name ?? ""; vassalRelation.OverlordClanTag = clan2?.Tag ?? ""; vassalRelation.VassalClanName = clan3?.Name ?? ""; vassalRelation.VassalClanTag = clan3?.Tag ?? ""; } warfareData.VassalRelations.Add(vassalRelation); } int num61 = val.ReadInt(); for (int num62 = 0; num62 < num61; num62++) { TerritoryTransferOffer territoryTransferOffer = new TerritoryTransferOffer { OfferId = val.ReadInt(), FromClanId = val.ReadInt(), ToClanId = val.ReadInt(), TerritoryId = val.ReadString(), Price = val.ReadLong(), CreatedAt = val.ReadLong(), ExpiresAt = val.ReadLong(), Status = (TerritoryTransferStatus)val.ReadInt() }; if (num >= 25) { territoryTransferOffer.DealType = (TerritoryDealType)val.ReadInt(); territoryTransferOffer.FromTerritoryIds = ReadStringList(val); territoryTransferOffer.ToTerritoryIds = ReadStringList(val); territoryTransferOffer.FromCoins = val.ReadLong(); territoryTransferOffer.ToCoins = val.ReadLong(); territoryTransferOffer.FromResources = ReadVaultItems(val); territoryTransferOffer.ToResources = ReadVaultItems(val); territoryTransferOffer.FromAccepted = val.ReadBool(); territoryTransferOffer.ToAccepted = val.ReadBool(); } else { territoryTransferOffer.DealType = ((territoryTransferOffer.Price > 0) ? TerritoryDealType.Sale : TerritoryDealType.Gift); if (!string.IsNullOrEmpty(territoryTransferOffer.TerritoryId)) { territoryTransferOffer.FromTerritoryIds.Add(territoryTransferOffer.TerritoryId); } territoryTransferOffer.ToCoins = territoryTransferOffer.Price; territoryTransferOffer.FromAccepted = true; territoryTransferOffer.ToAccepted = territoryTransferOffer.Status == TerritoryTransferStatus.Accepted; } warfareData.TerritoryTransferOffers.Add(territoryTransferOffer); } } if (num >= 20) { int num63 = val.ReadInt(); for (int num64 = 0; num64 < num63; num64++) { warfareData.BuildingProductionLog.Add(val.ReadString()); } } int num65 = val.ReadInt(); for (int num66 = 0; num66 < num65; num66++) { warfareData.ActiveCracks.Add(new VaultCrackSession { PlayerId = val.ReadLong(), PlayerName = val.ReadString(), PlayerClanId = val.ReadInt(), TargetClanId = val.ReadInt(), TerritoryGridX = val.ReadInt(), TerritoryGridY = val.ReadInt(), StartTimestamp = val.ReadLong(), StartPosition = val.ReadVector3(), DurationMinutes = val.ReadInt() }); } int num67 = val.ReadInt(); for (int num68 = 0; num68 < num67; num68++) { warfareData.ActiveCaptures.Add(new CaptureSession { TerritoryGridX = val.ReadInt(), TerritoryGridY = val.ReadInt(), AttackerClanId = val.ReadInt(), DefenderClanId = val.ReadInt(), Progress = val.ReadSingle(), Threshold = val.ReadSingle(), CombatPoints = val.ReadInt(), StartTimestamp = val.ReadLong(), LastAttackerSeenTime = val.ReadLong(), LastDefenderMajorityTime = val.ReadLong(), Siege = val.ReadBool() }); } if (num >= 23) { int num69 = val.ReadInt(); for (int num70 = 0; num70 < num69; num70++) { warfareData.Guards.Add(new TerritoryGuard { GuardId = val.ReadInt(), OwnerClanId = val.ReadInt(), TerritoryId = val.ReadString(), GuardType = (GuardType)val.ReadInt(), Level = val.ReadInt(), Status = (GuardStatus)val.ReadInt(), UpkeepCost = val.ReadLong(), CreatedAt = val.ReadLong(), LastPaidAt = val.ReadLong(), HasPosition = val.ReadBool(), Position = val.ReadVector3() }); } int num71 = val.ReadInt(); for (int num72 = 0; num72 < num71; num72++) { warfareData.GuardLog.Add(val.ReadString()); } } if (num >= 24) { int num73 = val.ReadInt(); for (int num74 = 0; num74 < num73; num74++) { warfareData.WorldEvents.Add(new WorldEvent { EventId = val.ReadInt(), EventType = (WorldEventType)val.ReadInt(), EventName = val.ReadString(), Description = val.ReadString(), StartTime = val.ReadLong(), EndTime = val.ReadLong(), Duration = val.ReadLong(), Position = val.ReadVector3(), Radius = val.ReadSingle(), Status = (WorldEventStatus)val.ReadInt(), RewardCoins = val.ReadLong(), RewardSummary = val.ReadString(), ClaimedByPlayerId = val.ReadLong(), ClaimedByPlayerName = val.ReadString() }); } int num75 = val.ReadInt(); for (int num76 = 0; num76 < num75; num76++) { warfareData.WorldEventLog.Add(val.ReadString()); } } if (num >= 25) { int num77 = val.ReadInt(); for (int num78 = 0; num78 < num77; num78++) { warfareData.Contracts.Add(new Contract { ContractId = val.ReadInt(), ContractType = (ContractType)val.ReadInt(), Title = val.ReadString(), Description = val.ReadString(), IssuerType = (ContractIssuerType)val.ReadInt(), IssuerId = val.ReadLong(), TargetType = (ContractTargetType)val.ReadInt(), TargetId = val.ReadString(), RequiredItem = val.ReadString(), RequiredAmount = val.ReadInt(), RewardCoins = val.ReadLong(), RewardMaterials = val.ReadString(), Status = (ContractStatus)val.ReadInt(), CreatedAt = val.ReadLong(), ExpiresAt = val.ReadLong(), AcceptedBy = val.ReadLong() }); } } if (num >= 26) { int num79 = val.ReadInt(); for (int num80 = 0; num80 < num79; num80++) { warfareData.ContractLog.Add(val.ReadString()); } } if (num >= 28) { int num81 = val.ReadInt(); for (int num82 = 0; num82 < num81; num82++) { FehuGameTable fehuGameTable = new FehuGameTable { GameId = val.ReadInt(), GameType = (FehuGameType)val.ReadInt(), TableName = val.ReadString(), HostPlayerId = val.ReadLong(), HostPlayerName = val.ReadString() }; int num83 = val.ReadInt(); for (int num84 = 0; num84 < num83; num84++) { fehuGameTable.PlayerIds.Add(val.ReadLong()); } int num85 = val.ReadInt(); for (int num86 = 0; num86 < num85; num86++) { fehuGameTable.SpectatorIds.Add(val.ReadLong()); } fehuGameTable.PlayerLimit = val.ReadInt(); fehuGameTable.IsPrivate = val.ReadBool(); fehuGameTable.AllowSpectators = val.ReadBool(); fehuGameTable.StakeType = (FehuStakeType)val.ReadInt(); fehuGameTable.StakeAmount = val.ReadLong(); fehuGameTable.StakePrefabName = val.ReadString(); fehuGameTable.Status = (FehuGameStatus)val.ReadInt(); fehuGameTable.CreatedAt = val.ReadLong(); fehuGameTable.StartedAt = val.ReadLong(); fehuGameTable.FinishedAt = val.ReadLong(); fehuGameTable.WinnerPlayerId = val.ReadLong(); fehuGameTable.Result = val.ReadString(); fehuGameTable.DiceCount = val.ReadInt(); fehuGameTable.RoundCount = val.ReadInt(); fehuGameTable.DiceMode = (DiceGameMode)val.ReadInt(); int num87 = val.ReadInt(); for (int num88 = 0; num88 < num87; num88++) { fehuGameTable.EscrowPlayerIds.Add(val.ReadLong()); } fehuGameTable.EscrowTotal = val.ReadLong(); fehuGameTable.EscrowCollecting = val.ReadBool(); fehuGameTable.PayoutIssued = val.ReadBool(); if (num >= 29) { fehuGameTable.Chess = ReadChessState(val, num >= 31); fehuGameTable.ChessHostColorPreference = val.ReadInt(); fehuGameTable.ChessTimeControlSeconds = val.ReadInt(); } if (num >= 32) { fehuGameTable.Poker = ReadPokerState(val, num >= 33, num >= 35); fehuGameTable.PokerBuyIn = val.ReadLong(); fehuGameTable.PokerSmallBlind = val.ReadLong(); fehuGameTable.PokerBigBlind = val.ReadLong(); } if (num >= 34) { fehuGameTable.Dice = ReadDiceState(val); } if (num >= 37) { fehuGameTable.HostStakeTerritoryIds = ReadStringList(val); fehuGameTable.ChallengerStakeTerritoryIds = ReadStringList(val); fehuGameTable.HostStakeClanId = val.ReadInt(); fehuGameTable.ChallengerStakeClanId = val.ReadInt(); fehuGameTable.HostStakeAccepted = val.ReadBool(); fehuGameTable.ChallengerStakeAccepted = val.ReadBool(); } warfareData.Games.Add(fehuGameTable); } int num89 = val.ReadInt(); for (int num90 = 0; num90 < num89; num90++) { warfareData.GameInvites.Add(new FehuGameInvite { GameId = val.ReadInt(), GameType = (FehuGameType)val.ReadInt(), FromPlayerId = val.ReadLong(), ToPlayerId = val.ReadLong(), CreatedAt = val.ReadLong(), ExpiresAt = val.ReadLong() }); } int num91 = val.ReadInt(); for (int num92 = 0; num92 < num91; num92++) { FehuGameHistoryEntry fehuGameHistoryEntry = new FehuGameHistoryEntry { GameId = val.ReadInt(), GameType = (FehuGameType)val.ReadInt(), TableName = val.ReadString() }; int num93 = val.ReadInt(); for (int num94 = 0; num94 < num93; num94++) { fehuGameHistoryEntry.PlayerIds.Add(val.ReadLong()); } fehuGameHistoryEntry.WinnerPlayerId = val.ReadLong(); fehuGameHistoryEntry.Result = val.ReadString(); fehuGameHistoryEntry.StakeType = (FehuStakeType)val.ReadInt(); fehuGameHistoryEntry.StakeAmount = val.ReadLong(); fehuGameHistoryEntry.StakePrefabName = val.ReadString(); fehuGameHistoryEntry.CompletedAt = val.ReadLong(); if (num >= 37) { fehuGameHistoryEntry.HostStakeTerritoryIds = ReadStringList(val); fehuGameHistoryEntry.ChallengerStakeTerritoryIds = ReadStringList(val); fehuGameHistoryEntry.HostStakeClanId = val.ReadInt(); fehuGameHistoryEntry.ChallengerStakeClanId = val.ReadInt(); fehuGameHistoryEntry.TerritoryTransferResult = val.ReadString(); } warfareData.GameHistory.Add(fehuGameHistoryEntry); } int num95 = val.ReadInt(); for (int num96 = 0; num96 < num95; num96++) { long num97 = val.ReadLong(); FehuGamePlayerStats fehuGamePlayerStats = new FehuGamePlayerStats { PlayerId = num97, GamesPlayed = val.ReadInt(), Wins = val.ReadInt(), Losses = val.ReadInt(), Profit = val.ReadLong(), HighestDiceRoll = val.ReadInt(), BiggestWin = val.ReadLong(), FavoriteGame = (FehuGameType)val.ReadInt() }; if (num >= 30) { fehuGamePlayerStats.ChessGamesPlayed = val.ReadInt(); fehuGamePlayerStats.ChessWins = val.ReadInt(); fehuGamePlayerStats.ChessLosses = val.ReadInt(); fehuGamePlayerStats.ChessDraws = val.ReadInt(); fehuGamePlayerStats.ChessRating = val.ReadInt(); fehuGamePlayerStats.ChessCurrentStreak = val.ReadInt(); fehuGamePlayerStats.ChessBestStreak = val.ReadInt(); } if (num >= 32) { fehuGamePlayerStats.PokerGamesPlayed = val.ReadInt(); fehuGamePlayerStats.PokerHandsWon = val.ReadInt(); fehuGamePlayerStats.PokerTablesWon = val.ReadInt(); fehuGamePlayerStats.PokerBiggestPot = val.ReadLong(); fehuGamePlayerStats.PokerBiggestWin = val.ReadLong(); fehuGamePlayerStats.PokerProfit = val.ReadLong(); } if (num >= 36) { fehuGamePlayerStats.DiceCurrentStreak = val.ReadInt(); fehuGamePlayerStats.DiceBestStreak = val.ReadInt(); } warfareData.GameStats[num97] = fehuGamePlayerStats; } } if (num >= 40) { int num98 = val.ReadInt(); for (int num99 = 0; num99 < num98; num99++) { warfareData.PendingRootLoot.Add(new PendingRootLoot { RootClanId = val.ReadInt(), TerritoryGridX = val.ReadInt(), TerritoryGridY = val.ReadInt(), TotemId = val.ReadInt(), PrefabName = val.ReadString(), DisplayName = val.ReadString(), Amount = val.ReadInt() }); } } if (num >= 41) { int num100 = val.ReadInt(); for (int num101 = 0; num101 < num100; num101++) { warfareData.RootProductionRemainders.Add(new RootProductionRemainder { ReceivingClanId = val.ReadInt(), TerritoryGridX = val.ReadInt(), TerritoryGridY = val.ReadInt(), BuildingBuiltAt = val.ReadLong(), PrefabName = val.ReadString(), DisplayName = val.ReadString(), OwnerShare = val.ReadBool(), QuarterUnits = val.ReadInt() }); } } return warfareData; } catch (Exception ex) { Debug.LogError((object)("[ClanWarfare] Deserialize failed: " + ex.Message + "\n" + ex.StackTrace)); return new WarfareData(); } } } public enum WarType { None, Raid, War, RageWar } [Serializable] public class WorldEvent { public int EventId; public WorldEventType EventType; public string EventName = ""; public string Description = ""; public long StartTime; public long EndTime; public long Duration; public Vector3 Position; public float Radius = 60f; public WorldEventStatus Status; public long RewardCoins; public string RewardSummary = ""; public long ClaimedByPlayerId; public string ClaimedByPlayerName = ""; } public enum WorldEventStatus { Pending, Active, Completed, Expired, Failed } public enum WorldEventType { BanditCamp, AncientShrine, ResourceRush, CorruptedTerritory, LostCaravan, WorldBoss, OdinChallenge, ClanChallenge, FreebornOpportunity } } namespace FortecaClanWarfare.Core { [HarmonyPatch] public static class BuildingDebugCommands { [Serializable] [CompilerGenerated] private sealed class <>c { public static readonly <>c <>9 = new <>c(); public static ConsoleEvent <>9__0_0; public static ConsoleEvent <>9__0_1; public static ConsoleEvent <>9__0_2; public static ConsoleEvent <>9__0_3; internal void b__0_0(ConsoleEventArgs args) { if (!IsServer()) { args.Context.AddString("Server only."); } else { args.Context.AddString(WarfareManager.Instance?.ForceBuildingProductionTick() ?? "WarfareManager missing."); } } internal void b__0_1(ConsoleEventArgs args) { //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_0030: Unknown result type (might be due to invalid IL or missing references) if (!IsServer()) { args.Context.AddString("Server only."); return; } Vector3 worldPosition = CurrentPlayerPosition(); args.Context.AddString(WarfareManager.Instance?.DebugTerritoryBuildings(worldPosition) ?? "WarfareManager missing."); } internal void b__0_2(ConsoleEventArgs args) { //IL_002a: Unknown result type (might be due to invalid IL or missing references) if (!IsServer()) { args.Context.AddString("Server only."); } else { args.Context.AddString(WarfareManager.Instance?.DamageTerritoryBuildings(CurrentPlayerPosition()) ?? "WarfareManager missing."); } } internal void b__0_3(ConsoleEventArgs args) { //IL_002a: Unknown result type (might be due to invalid IL or missing references) if (!IsServer()) { args.Context.AddString("Server only."); } else { args.Context.AddString(WarfareManager.Instance?.RepairTerritoryBuildings(CurrentPlayerPosition()) ?? "WarfareManager missing."); } } } [HarmonyPatch(typeof(Terminal), "InitTerminal")] [HarmonyPostfix] public static void RegisterCommands() { //IL_0032: Unknown result type (might be due to invalid IL or missing references) //IL_001e: 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_0029: Expected O, but got Unknown //IL_006a: Unknown result type (might be due to invalid IL or missing references) //IL_0056: Unknown result type (might be due to invalid IL or missing references) //IL_005b: Unknown result type (might be due to invalid IL or missing references) //IL_0061: Expected O, but got Unknown //IL_00a2: Unknown result type (might be due to invalid IL or missing references) //IL_008e: Unknown result type (might be due to invalid IL or missing references) //IL_0093: Unknown result type (might be due to invalid IL or missing references) //IL_0099: Expected O, but got Unknown //IL_00da: Unknown result type (might be due to invalid IL or missing references) //IL_00c6: Unknown result type (might be due to invalid IL or missing references) //IL_00cb: Unknown result type (might be due to invalid IL or missing references) //IL_00d1: Expected O, but got Unknown object obj = <>c.<>9__0_0; if (obj == null) { ConsoleEvent val = delegate(ConsoleEventArgs args) { if (!IsServer()) { args.Context.AddString("Server only."); } else { args.Context.AddString(WarfareManager.Instance?.ForceBuildingProductionTick() ?? "WarfareManager missing."); } }; <>c.<>9__0_0 = val; obj = (object)val; } new ConsoleCommand("fcw_building_tick", "Force Kingdom Building production tick (admin)", (ConsoleEvent)obj, true, false, false, false, false, (ConsoleOptionsFetcher)null, false, false, false); object obj2 = <>c.<>9__0_1; if (obj2 == null) { ConsoleEvent val2 = delegate(ConsoleEventArgs args) { //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_0030: Unknown result type (might be due to invalid IL or missing references) if (!IsServer()) { args.Context.AddString("Server only."); } else { Vector3 worldPosition = CurrentPlayerPosition(); args.Context.AddString(WarfareManager.Instance?.DebugTerritoryBuildings(worldPosition) ?? "WarfareManager missing."); } }; <>c.<>9__0_1 = val2; obj2 = (object)val2; } new ConsoleCommand("fcw_building_debug", "Show selected territory building data (admin)", (ConsoleEvent)obj2, true, false, false, false, false, (ConsoleOptionsFetcher)null, false, false, false); object obj3 = <>c.<>9__0_2; if (obj3 == null) { ConsoleEvent val3 = delegate(ConsoleEventArgs args) { //IL_002a: Unknown result type (might be due to invalid IL or missing references) if (!IsServer()) { args.Context.AddString("Server only."); } else { args.Context.AddString(WarfareManager.Instance?.DamageTerritoryBuildings(CurrentPlayerPosition()) ?? "WarfareManager missing."); } }; <>c.<>9__0_2 = val3; obj3 = (object)val3; } new ConsoleCommand("fcw_building_damage", "Damage buildings in current territory (admin)", (ConsoleEvent)obj3, true, false, false, false, false, (ConsoleOptionsFetcher)null, false, false, false); object obj4 = <>c.<>9__0_3; if (obj4 == null) { ConsoleEvent val4 = delegate(ConsoleEventArgs args) { //IL_002a: Unknown result type (might be due to invalid IL or missing references) if (!IsServer()) { args.Context.AddString("Server only."); } else { args.Context.AddString(WarfareManager.Instance?.RepairTerritoryBuildings(CurrentPlayerPosition()) ?? "WarfareManager missing."); } }; <>c.<>9__0_3 = val4; obj4 = (object)val4; } new ConsoleCommand("fcw_building_repair", "Repair buildings in current territory (admin)", (ConsoleEvent)obj4, true, false, false, false, false, (ConsoleOptionsFetcher)null, false, false, false); } private static bool IsServer() { if ((Object)(object)ZNet.instance != (Object)null) { return ZNet.instance.IsServer(); } return false; } private static Vector3 CurrentPlayerPosition() { //IL_0007: Unknown result type (might be due to invalid IL or missing references) //IL_0012: Expected O, but got Unknown //IL_0020: Unknown result type (might be due to invalid IL or missing references) //IL_0014: Unknown result type (might be due to invalid IL or missing references) Player localPlayer = Player.m_localPlayer; if (!((Object)localPlayer != (Object)null)) { return Vector3.zero; } return ((Component)localPlayer).transform.position; } } public static class ClanConfig { public static ConfigEntry GridCellSize; public static ConfigEntry SpawnProtectionRadius; public static ConfigEntry ClanFreeMemberLimit; public static ConfigEntry ClanMemberCostBase; public static ConfigEntry ClanMaxMembers; public static ConfigEntry ClanCreationCost; public static ConfigEntry ClanCreationCoinCost; public static ConfigEntry ClanCreationSurtlingCoreCost; public static ConfigEntry RequireUniqueClanColors; public static ConfigEntry ReservedClanColors; public static ConfigEntry ChoiceReminderMinutes; public static ConfigEntry ClanLeaveCooldownHours; public static ConfigEntry MaxClans; public static ConfigEntry ElectionIntervalDays; public static ConfigEntry JarlElectionDurationHours; public static ConfigEntry JarlElectionMinimumTurnoutPercent; public static ConfigEntry JarlElectionTieExtensionHours; public static ConfigEntry ProfileAboutMaxLength; public static ConfigEntry PersonalSagaMaxEntries; public static ConfigEntry PersonalSagaLegacyScoreCap; public static ConfigEntry AchievementCoinRewardsEnabled; public static ConfigEntry AchievementDefaultCoinReward; public static ConfigEntry EnableSharedClans; public static ConfigEntry SharedClansPath; public static ConfigEntry ServerId; public static ConfigEntry BaseTerritoryCap; public static ConfigEntry MaxTerritoryCap; public static ConfigEntry TerritoryExpansionBlock; public static ConfigEntry TerritoryExpansionCost; public static ConfigEntry TerritoryExpansionSurtlingCoreCost; public static ConfigEntry TerritoryUpgradeCostBase; public static ConfigEntry TerritoryUpgradeSurtlingCoreCostBase; public static ConfigEntry MaxTerritorySizeLevel; public static ConfigEntry ProfitThreshold; public static ConfigEntry BalancedThreshold; public static ConfigEntry IncomeBiomeLow; public static ConfigEntry IncomeBiomeMid; public static ConfigEntry IncomeBiomeHigh; public static ConfigEntry UpkeepPerSizeLevel; public static ConfigEntry GlobalUpkeepBase; public static ConfigEntry GlobalUpkeepPerTerritory; public static ConfigEntry GlobalUpkeepFreeTerritories; public static ConfigEntry UpkeepTickMinutes; public static ConfigEntry StartingTreasury; public static ConfigEntry BuildingProductionHours; public static ConfigEntry BuildingBuildCostBase; public static ConfigEntry BuildingUpgradeCostBase; public static ConfigEntry BuildingRepairCostBase; public static ConfigEntry BuildingCaptureMode; public static ConfigEntry BuildingBuildCostByType; public static ConfigEntry BuildingUpgradeCostByType; public static ConfigEntry BuildingRepairCostByType; public static ConfigEntry BuildingProductionByType; public static ConfigEntry BuildingMaxLevelByType; public static ConfigEntry BuildingMaxCopiesByType; public static ConfigEntry BuildingBiomeRequirementByType; public static ConfigEntry EnableClanMemberTax; public static ConfigEntry DefaultClanResourceTaxPercent; public static ConfigEntry DefaultClanCoinTaxPercent; public static ConfigEntry MaxClanTaxPercent; public static ConfigEntry TaxOnlySafeResources; public static ConfigEntry EnableTaxDebugLogs; public static ConfigEntry VaultCapacityBase; public static ConfigEntry ClanVaultTier1CapacityStacks; public static ConfigEntry VaultUpgradeCostBase; public static ConfigEntry VaultUpgradeSurtlingCoreCostBase; public static ConfigEntry MaxVaultTier; public static ConfigEntry VaultOpenDayOfWeek; public static ConfigEntry VaultOpenHourUTC; public static ConfigEntry VaultOpenDurationMinutes; public static ConfigEntry VaultAlwaysOpen; public static ConfigEntry VaultUseIntervalSchedule; public static ConfigEntry VaultOpenEveryMinutes; public static ConfigEntry VaultDepositMinRank; public static ConfigEntry VaultWithdrawMinRank; public static ConfigEntry VaultViewMinRank; public static ConfigEntry VaultAllowDepositWhenClosed; public static ConfigEntry EnableTerritoryIncomeToVault; public static ConfigEntry VaultRequireCapitalTerritory; public static ConfigEntry FreebornVaultRequireHomestead; public static ConfigEntry EnableFreebornVault; public static ConfigEntry AllowVaultInClanColonies; public static ConfigEntry AllowVaultInFreebornOutposts; public static ConfigEntry AllowFreebornVaultInOutposts; public static ConfigEntry AllowCoinDepositOutsideCapital; public static ConfigEntry AllowCoinDepositWhenVaultClosed; public static ConfigEntry CoinItemPrefab; public static ConfigEntry CoinsWeight; public static ConfigEntry CoinsMaxStack; public static ConfigEntry VaultDefaultMemberCanDepositItems; public static ConfigEntry VaultDefaultMemberCanWithdrawItems; public static ConfigEntry VaultDefaultMemberCanDepositCoins; public static ConfigEntry VaultDefaultMemberCanWithdrawCoins; public static ConfigEntry VaultDefaultOfficerCanWithdrawItems; public static ConfigEntry VaultDefaultOfficerCanWithdrawCoins; public static ConfigEntry RoleBuffPercent; public static ConfigEntry RoleDebuffPercent; public static ConfigEntry EnableSecondaryRole; public static ConfigEntry SecondaryRoleEffectPercent; public static ConfigEntry EnableTertiaryRole; public static ConfigEntry TertiaryRoleEffectPercent; public static ConfigEntry RequireExplorerForClaim; public static ConfigEntry GuardianBuildCostReduction; public static ConfigEntry ParasiteCraftCostReduction; public static ConfigEntry BuilderBuildCostReduction; public static ConfigEntry GathererCarryWeightBonus; public static ConfigEntry PathfinderDiscoveryCostReduction; public static ConfigEntry WardenUpgradeCostReduction; public static ConfigEntry RaiderDamageBonus; public static ConfigEntry RaiderStaminaReduction; public static ConfigEntry GuardianDamageReduction; public static ConfigEntry GuardianBlockPowerBonus; public static ConfigEntry BuilderUpgradeCostReduction; public static ConfigEntry GathererBasicResourceChance; public static ConfigEntry MerchantEconomicRewardBonus; public static ConfigEntry MerchantTradeFeeReduction; public static ConfigEntry SmugglerTaxReduction; public static ConfigEntry ThiefSuccessChance; public static ConfigEntry ThiefCooldownMinutes; public static ConfigEntry ShamanTotemCostReduction; public static ConfigEntry DruidTotemDetectionRange; public static ConfigEntry DruidTamingSpeedBonus; public static ConfigEntry HunterBowDamageBonus; public static ConfigEntry HunterBowStaminaReduction; public static ConfigEntry ProspectorResourceChance; public static ConfigEntry ProspectorMiningStaminaReduction; public static ConfigEntry SkipperShipSpeedBonus; public static ConfigEntry SkipperShipDamageReduction; public static ConfigEntry SkaldRestedDurationBonus; public static ConfigEntry SkaldRestedStaminaRegenBonus; public static ConfigEntry LawspeakerProgressiveUpkeepReduction; public static ConfigEntry LawspeakerDiplomacyReduction; public static ConfigEntry RoleChangeCooldownHours; public static ConfigEntry PrimaryRoleChangeCooldownHours; public static ConfigEntry SecondaryRoleChangeCooldownHours; public static ConfigEntry TertiaryRoleChangeCooldownHours; public static ConfigEntry IgnoreCooldownIfSecondaryRoleIsNone; public static ConfigEntry RolePreviewImagePathExplorer; public static ConfigEntry RolePreviewImagePathRaider; public static ConfigEntry RolePreviewImagePathGatherer; public static ConfigEntry RolePreviewImagePathBuilder; public static ConfigEntry RolePreviewImagePathShieldbearer; public static ConfigEntry RolePreviewImagePathShaman; public static ConfigEntry FreebornEnabled; public static ConfigEntry FreebornMaxHomesteads; public static ConfigEntry FreebornBaseOutposts; public static ConfigEntry FreebornMaxOutposts; public static ConfigEntry FreebornOutpostUpgradeCoinCostBase; public static ConfigEntry FreebornOutpostUpgradeSurtlingCoreCostBase; public static ConfigEntry ShowFreebornBorders; public static ConfigEntry FreebornHomesteadBorderColor; public static ConfigEntry FreebornOutpostBorderColor; public static ConfigEntry ShowFreebornOwnerName; public static ConfigEntry ShowOdinsPeaceOnTooltip; public static ConfigEntry EnableClanEmblemUrl; public static ConfigEntry ClanEmblemUrlMaxLength; public static ConfigEntry AllowOfficerEditClanEmblem; public static ConfigEntry ClanEmblemRecommendedSizeText; public static ConfigEntry EnableLocalEmblems; public static ConfigEntry RequireUniqueClanEmblems; public static ConfigEntry EnableClanLinks; public static ConfigEntry ClanDiscordUrlMaxLength; public static ConfigEntry ClanWebsiteUrlMaxLength; public static ConfigEntry ClanSteamGroupUrlMaxLength; public static ConfigEntry ClanCustomUrlMaxLength; public static ConfigEntry ClanCustomLabelMaxLength; public static ConfigEntry EnableVaultDebugLogs; public static ConfigEntry EmblemFolder; public static ConfigEntry DefaultEmblem; public static ConfigEntry OdinsPeaceEnabled; public static ConfigEntry OdinsPeaceProtectHomestead; public static ConfigEntry OdinsPeaceProtectOutposts; public static ConfigEntry ChallengeFreebornEnabled; public static ConfigEntry ChallengeWarningHours; public static ConfigEntry ChallengeTributeCoins; public static ConfigEntry ChallengeTributeSurtlingCores; public static ConfigEntry ChallengeTributeTrophyItem; public static ConfigEntry ChallengeCooldownHours; public static ConfigEntry FreebornAbandonDays; public static ConfigEntry FreebornAbandonGraceHours; public static ConfigEntry AllowFreebornAbandonHomestead; public static ConfigEntry AbandonHomesteadAlsoAbandonsOutposts; public static ConfigEntry AbandonHomesteadCooldownHours; public static ConfigEntry EnableProgressiveTerritoryUpkeep; public static ConfigEntry FreeTerritoriesBeforeUpkeep; public static ConfigEntry TerritoryUpkeepTier1Start; public static ConfigEntry TerritoryUpkeepTier1End; public static ConfigEntry TerritoryUpkeepTier1Cost; public static ConfigEntry TerritoryUpkeepTier2Start; public static ConfigEntry TerritoryUpkeepTier2End; public static ConfigEntry TerritoryUpkeepTier2Cost; public static ConfigEntry TerritoryUpkeepTier3Start; public static ConfigEntry TerritoryUpkeepTier3End; public static ConfigEntry TerritoryUpkeepTier3Cost; public static ConfigEntry PayUpkeepFromTreasuryFirst; public static ConfigEntry AllowPlayerWalletUpkeepFallback; public static ConfigEntry EnableTerritoryResourceOwnership; public static ConfigEntry ClanGatherOnlyOwnTerritory; public static ConfigEntry AllowAllianceGathering; public static ConfigEntry AllowGatherInAlliedTerritory; public static ConfigEntry FreebornGatherOnlyOwnTerritory; public static ConfigEntry MiningRestrictionEnabled; public static ConfigEntry WoodcuttingRestrictionEnabled; public static ConfigEntry BuildingRestrictionEnabled; public static ConfigEntry TerritoryCaptureEnabled; public static ConfigEntry ClanWarsEnabled; public static ConfigEntry FreebornProtectionEnabled; public static ConfigEntry WorldEventsEnabled; public static ConfigEntry GamesEnabled; public static ConfigEntry FreebornIgnoreResourceOwnership; public static ConfigEntry FreebornMaxFriends; public static ConfigEntry AllowFriendGathering; public static ConfigEntry AllowFriendBuilding; public static ConfigEntry AllowFriendContainers; public static ConfigEntry AllowFriendTerritoryManagement; public static ConfigEntry AllianceBorderEnabled; public static ConfigEntry AllianceBorderThickness; public static ConfigEntry AllianceBorderGlow; public static ConfigEntry EnableVassals; public static ConfigEntry AllowVassalJoinOverlordWars; public static ConfigEntry MaxVassalsPerClan; public static ConfigEntry EnableTerritoryTransfers; public static ConfigEntry AllowCapitalTransfer; public static ConfigEntry MaxPendingTerritoryDealsPerClan; public static ConfigEntry TerritoryDealExpirationHours; public static ConfigEntry ColonyBorderThickness; public static ConfigEntry CapitalBorderThicknessMultiplier; public static ConfigEntry HomesteadBorderThicknessMultiplier; public static ConfigEntry BlockGatheringInEnemyTerritory; public static ConfigEntry EnemyGatheringAllowedForRoles; public static ConfigEntry ClanUpgradeSurtlingCoreCostBase; public static ConfigEntry VaultCrackDurationMinutes; public static ConfigEntry VaultCrackCooldownHours; public static ConfigEntry VaultCrackMinStacks; public static ConfigEntry VaultCrackMaxStacks; public static ConfigEntry WarDurationMinutes; public static ConfigEntry RaidPlayersRequired; public static ConfigEntry WarPlayersRequired; public static ConfigEntry RageWarPlayersRequired; public static ConfigEntry WarDeclareCost; public static ConfigEntry CaptureThresholdPerSize; public static ConfigEntry ClaimSpeedBase; public static ConfigEntry CombatPointsKill; public static ConfigEntry CombatPointsElite; public static ConfigEntry CombatPointsBoss; public static ConfigEntry InterruptNoAttackerSeconds; public static ConfigEntry InterruptDefenderMajoritySeconds; public static ConfigEntry SiegeAttackerCount; public static ConfigEntry SiegeDefenseReduction; public static ConfigEntry SiegeClaimSpeedBonus; public static ConfigEntry TerritoryHPBase; public static ConfigEntry CapitalHPModifier; public static ConfigEntry ColonyHPModifier; public static ConfigEntry RotDecayHours; public static ConfigEntry RotTotemCostBase; public static ConfigEntry MaxRotTotemsPerTerritory; public static ConfigEntry AutoBountyIllegalKill; public static ConfigEntry BountyRevealThreshold; public static ConfigEntry BountySystemEnabled; public static ConfigEntry FullPvPDuringWar; public static ConfigEntry ProtectHotbarOnDeath; public static ConfigEntry AntiAltEnabled; public static ConfigEntry OfflineRaidProtection; public static ConfigEntry OfflineRaidEffectReduction; public static ConfigEntry SeasonLengthDays; public static ConfigEntry AdminPanelPassword; public static ConfigEntry OwnerSteamId; public static ConfigEntry AdminSteamIds; public static ConfigEntry AdminSteamIdNames; public static ConfigEntry ConstellationMembers; public static ConfigEntry SupporterBadgeSteamIds; public static ConfigEntry ForcePvpOn; public static ConfigEntry ForceFriendlyFireOn; public static ConfigEntry AllowPlayerPvpToggle; public static ConfigEntry BattleStatusEnabled; public static ConfigEntry CombatDurationSeconds; public static ConfigEntry BlockTeleportWhileInCombat; public static ConfigEntry KillFeedEnabled; public static ConfigEntry CombatLogoutHandling; public static ConfigEntry DiscordEnabled; public static ConfigEntry DebugNotifications; public static ConfigEntry NotifyDiscordPvP; public static ConfigEntry NotifyDiscordWar; public static ConfigEntry NotifyDiscordTerritory; public static ConfigEntry NotifyDiscordEconomy; public static ConfigEntry NotifyDiscordBuilding; public static ConfigEntry NotifyDiscordClan; public static ConfigEntry NotifyDiscordElection; public static ConfigEntry NotifyDiscordEvent; public static ConfigEntry NotifyDiscordServer; public static ConfigEntry NotifyDiscordSupporter; public static ConfigEntry NotifyDiscordAdmin; public static ConfigEntry NotifyDiscordSystem; public static ConfigEntry DiscordWebhookEnabled; public static ConfigEntry NotifyKillFeed; public static ConfigEntry NotifyCombatLogout; public static ConfigEntry NotifyWarStarted; public static ConfigEntry NotifyWarEnded; public static ConfigEntry NotifyTerritoryCaptured; public static ConfigEntry NotifyTerritoryLost; public static ConfigEntry NotifyClanCreated; public static ConfigEntry NotifyClanDisbanded; public static ConfigEntry NotifyElections; public static ConfigEntry NotifyServerAnnouncements; public static ConfigEntry DiscordWebhookUrl; public static ConfigEntry RealmLawOverrides; public static ConfigEntry ShowPublicSteamIds; public static readonly Color[] ClanColors = (Color[])(object)new Color[28] { new Color(0.01f, 0.01f, 0.012f, 1f), new Color(0.55f, 0.28f, 0.08f, 1f), new Color(0.28f, 0.13f, 0.04f, 1f), new Color(0.58f, 0.24f, 0.07f, 1f), new Color(0.48f, 0.22f, 0.04f, 1f), new Color(1f, 0.96f, 0.48f, 1f), new Color(1f, 0.74f, 0.02f, 1f), new Color(1f, 0.58f, 0.02f, 1f), new Color(0.75f, 0.92f, 0.12f, 1f), new Color(0.2f, 0.72f, 0.02f, 1f), new Color(0.55f, 0.95f, 0f, 1f), new Color(0.38f, 0.7f, 0.96f, 1f), new Color(0.04f, 0.22f, 0.72f, 1f), new Color(0.02f, 0.68f, 0.68f, 1f), new Color(0.02f, 0.86f, 0.88f, 1f), new Color(0.72f, 0.54f, 0.95f, 1f), new Color(0.38f, 0.02f, 0.62f, 1f), new Color(0.22f, 0.04f, 0.42f, 1f), new Color(0.44f, 0.05f, 0.62f, 1f), new Color(0.96f, 0.42f, 0.72f, 1f), new Color(1f, 0.15f, 0.42f, 1f), new Color(0.95f, 0f, 0.86f, 1f), new Color(1f, 0.24f, 0.22f, 1f), new Color(0.96f, 0f, 0f, 1f), new Color(0.55f, 0.02f, 0.02f, 1f), new Color(0.42f, 0f, 0.08f, 1f), new Color(1f, 0.5f, 0f, 1f), new Color(0.85f, 0.36f, 0f, 1f) }; public static readonly string[] ClanIcons = new string[12] { "?", "?", "?", "?", "?", "\ufffd", "?", "?", "\ufffd", "?", "?", "?" }; public static Color GoldColor => new Color(1f, 0.78f, 0.18f, 1f); public static Color SilverColor => new Color(0.74f, 0.76f, 0.78f, 1f); public static Color WhiteColor => Color.white; public static Color CreamColor => new Color(0.96f, 0.9f, 0.78f, 1f); public static Color SteelColor => new Color(0.54f, 0.56f, 0.6f, 1f); public static Color GraphiteColor => new Color(0.25f, 0.25f, 0.27f, 1f); public static Color AnthraciteColor => new Color(0.12f, 0.12f, 0.12f, 1f); public static Color OrangeFallbackColor => new Color(0.92f, 0.55f, 0.12f, 1f); public static Color GrayFallbackColor => new Color(0.36f, 0.38f, 0.4f, 1f); public static void Initialize(ConfigFile config) { //IL_0ed1: Unknown result type (might be due to invalid IL or missing references) //IL_0edb: Expected O, but got Unknown //IL_0f05: Unknown result type (might be due to invalid IL or missing references) //IL_0f0f: Expected O, but got Unknown GridCellSize = config.Bind("Grid", "CellSize", 500, "Fallback grid cell size in meters (Voronoi territories are used in-game)."); SpawnProtectionRadius = config.Bind("Grid", "SpawnProtectionRadius", 2500, "No-claim radius around world spawn."); AdminPanelPassword = config.Bind("Admin", "AdminPanelPassword", "", "Legacy UI password. Ignored by the SteamID admin gate."); OwnerSteamId = config.Bind("Admin", "OwnerSteamId", "76561198007625196", "Permanent Forteca owner SteamID. This SteamID always has full admin access."); AdminSteamIds = config.Bind("Admin", "AdminSteamIds", "76561198007625196", "Comma separated SteamIDs allowed to use the Forteca Admin panel."); AdminSteamIdNames = config.Bind("Admin", "AdminSteamIdNames", "76561198007625196|Black Viking|Owner", "Semicolon separated admin metadata entries: steamid|nickname|role-note."); ConstellationMembers = config.Bind("Constellation", "Members", "", "Semicolon separated member entries: steamid|nickname|badge. Badges: Gold Contributor, Silver Subscriber, Steel Supporter, Bronze Alpha Tester."); SupporterBadgeSteamIds = config.Bind("Constellation", "SupporterBadgeSteamIds", "76561198007625196|Gold Contributor,Silver Subscriber,Steel Supporter,Bronze Alpha Tester", "Semicolon separated SteamID64 badge entries: steamid64|badge,badge. Icon names: gold_contributor.png, silver_subscriber.png, steel_supporter.png, bronze_alpha_tester.png."); ForcePvpOn = config.Bind("Game Settings", "ForcePvpOn", "false", "Force local PvP ON for players while this mod is loaded."); ForceFriendlyFireOn = config.Bind("Game Settings", "ForceFriendlyFireOn", "false", "Force Forteca friendly-fire damage ON."); AllowPlayerPvpToggle = config.Bind("PvP", "AllowPlayerPvpToggle", false, "Allow players to manually change their local PvP toggle when ForcePvpOn is false."); BattleStatusEnabled = config.Bind("PvP", "BattleStatusEnabled", false, "Enable the runtime PvP combat-state tracker for recent player-vs-player damage."); CombatDurationSeconds = config.Bind("PvP", "CombatDurationSeconds", 30, "Seconds before PvP combat state expires after the latest player-vs-player hit."); BlockTeleportWhileInCombat = config.Bind("PvP", "BlockTeleportWhileInCombat", false, "Block portal teleport attempts while a player is in PvP combat state."); KillFeedEnabled = config.Bind("PvP", "KillFeedEnabled", false, "Add a Recent Events entry when one player kills another player."); CombatLogoutHandling = config.Bind("PvP", "CombatLogoutHandling", false, "Add a Recent Events entry when a player disconnects while in PvP combat state."); DiscordWebhookUrl = config.Bind("PvP", "DiscordWebhookUrl", "", "Discord webhook URL reserved for future kill-feed delivery."); DiscordEnabled = config.Bind("Notifications", "DiscordEnabled", false, "Enable sending Forteca notifications to Discord through the configured webhook."); DebugNotifications = config.Bind("Notifications", "DebugNotifications", false, "Log detailed notification and Discord bridge diagnostics."); NotifyDiscordPvP = config.Bind("Notifications", "NotifyDiscordPvP", true, "Forward PvP notifications to Discord."); NotifyDiscordWar = config.Bind("Notifications", "NotifyDiscordWar", true, "Forward war notifications to Discord."); NotifyDiscordTerritory = config.Bind("Notifications", "NotifyDiscordTerritory", true, "Forward territory notifications to Discord."); NotifyDiscordEconomy = config.Bind("Notifications", "NotifyDiscordEconomy", true, "Forward economy notifications to Discord."); NotifyDiscordBuilding = config.Bind("Notifications", "NotifyDiscordBuilding", true, "Forward building notifications to Discord."); NotifyDiscordClan = config.Bind("Notifications", "NotifyDiscordClan", true, "Forward clan notifications to Discord."); NotifyDiscordElection = config.Bind("Notifications", "NotifyDiscordElection", true, "Forward election notifications to Discord."); NotifyDiscordEvent = config.Bind("Notifications", "NotifyDiscordEvent", true, "Forward event notifications to Discord."); NotifyDiscordServer = config.Bind("Notifications", "NotifyDiscordServer", true, "Forward server notifications to Discord."); NotifyDiscordSupporter = config.Bind("Notifications", "NotifyDiscordSupporter", true, "Forward supporter notifications to Discord."); NotifyDiscordAdmin = config.Bind("Notifications", "NotifyDiscordAdmin", true, "Forward admin notifications to Discord."); NotifyDiscordSystem = config.Bind("Notifications", "NotifyDiscordSystem", true, "Forward system notifications to Discord."); DiscordWebhookEnabled = config.Bind("Notifications", "DiscordWebhookEnabled", false, "Enable sending Forteca notifications to Discord through the configured webhook."); NotifyKillFeed = config.Bind("Notifications", "SendKillFeed", true, "Send PvP kill feed notifications to Discord."); NotifyCombatLogout = config.Bind("Notifications", "SendCombatLogout", true, "Send combat logout notifications to Discord."); NotifyWarStarted = config.Bind("Notifications", "SendWarStarted", true, "Send war started notifications to Discord."); NotifyWarEnded = config.Bind("Notifications", "SendWarEnded", true, "Send war ended notifications to Discord."); NotifyTerritoryCaptured = config.Bind("Notifications", "SendTerritoryCaptured", true, "Send territory captured notifications to Discord."); NotifyTerritoryLost = config.Bind("Notifications", "SendTerritoryLost", true, "Send territory lost notifications to Discord."); NotifyClanCreated = config.Bind("Notifications", "SendClanCreated", true, "Send clan created notifications to Discord."); NotifyClanDisbanded = config.Bind("Notifications", "SendClanDisbanded", true, "Send clan disbanded notifications to Discord."); NotifyElections = config.Bind("Notifications", "SendElections", true, "Send election notifications to Discord."); NotifyServerAnnouncements = config.Bind("Notifications", "SendServerAnnouncements", true, "Send server announcement notifications to Discord."); RealmLawOverrides = config.Bind("Realm Laws", "RealmLawOverrides", "", "Server-authored overrides for RealmRules laws that do not yet map to dedicated config entries."); ShowPublicSteamIds = config.Bind("Admin", "ShowPublicSteamIds", "false", "If true, public Admin Crew and Constellation lists show full SteamIDs. If false, SteamIDs are masked."); ClanFreeMemberLimit = config.Bind("Clan", "FreeMemberLimit", 3, "How many members a clan can hold for free."); ClanMemberCostBase = config.Bind("Clan", "MemberCostBase", 100, "Coin cost for the first paid member slot. Doubles for each additional slot (100, 200, 400, ...)."); ClanMaxMembers = config.Bind("Clan", "MaxMembers", 20, "Hard cap on clan members."); ClanCreationCost = config.Bind("Clan", "CreationCost", 0, "Legacy coin cost to found a clan. ClanCreationCoinCost overrides when greater than 0."); ClanCreationCoinCost = config.Bind("Clan", "ClanCreationCoinCost", 0, "Legacy coin cost to found a clan. Clan founding is free; development and upgrades may still cost resources."); ClanCreationSurtlingCoreCost = config.Bind("Clan", "ClanCreationSurtlingCoreCost", 0, "Legacy Surtling Core cost to found a clan. Clan founding is free; development and upgrades may still cost resources."); RequireUniqueClanColors = config.Bind("Clan", "RequireUniqueClanColors", true, "If true each clan must use a unique non-reserved color."); ReservedClanColors = config.Bind("Clan", "ReservedClanColors", "White,Silver", "Comma separated clan color names reserved for systems such as Freeborn/Homestead territories."); if (string.Equals(ReservedClanColors.Value, "Gold,Silver", StringComparison.OrdinalIgnoreCase)) { ReservedClanColors.Value = "White,Silver"; Plugin.Log.LogInfo((object)"[FCW Config] Migrated ReservedClanColors from Gold,Silver to White,Silver."); } ChoiceReminderMinutes = config.Bind("Clan", "ChoiceReminderMinutes", 10, "Minutes before the choose path window is shown again after closing it without choosing."); ClanUpgradeSurtlingCoreCostBase = config.Bind("Clan", "ClanUpgradeSurtlingCoreCostBase", 10, "Base Surtling Core cost for future clan upgrades."); ClanLeaveCooldownHours = config.Bind("Clan", "LeaveCooldownHours", 24, "Hours before a player who left a clan may join another."); MaxClans = config.Bind("Clan", "MaxClans", 8, "Maximum number of clans that can exist at once. 0 = unlimited."); ElectionIntervalDays = config.Bind("Clan", "ElectionIntervalDays", 14, "Real days between Jarl elections."); JarlElectionDurationHours = config.Bind("Clan", "JarlElectionDurationHours", 72, "Hours before the election deadline that voting is open."); JarlElectionMinimumTurnoutPercent = config.Bind("Clan", "JarlElectionMinimumTurnoutPercent", 50, "Percent of eligible clan members required for a valid Jarl election."); JarlElectionTieExtensionHours = config.Bind("Clan", "JarlElectionTieExtensionHours", 24, "Hours added to a Jarl election if the leading result is tied."); ProfileAboutMaxLength = config.Bind("Profile", "AboutMaxLength", 500, "Maximum characters in a player's Fehu profile About Me text."); PersonalSagaMaxEntries = config.Bind("Personal Saga", "PersonalSagaMaxEntries", 200, "Maximum structured Personal Saga entries retained per player; values below 200 retain the newest 200."); PersonalSagaLegacyScoreCap = config.Bind("Personal Saga", "PersonalSagaLegacyScoreCap", 1000, "Maximum Legacy Score contribution from valid Personal Saga milestones."); AchievementCoinRewardsEnabled = config.Bind("Achievements", "AchievementCoinRewardsEnabled", true, "Grant personal Coin rewards when achievements unlock."); AchievementDefaultCoinReward = config.Bind("Achievements", "AchievementDefaultCoinReward", 10, "Default personal Coin reward for future achievement unlocks."); EnableSharedClans = config.Bind("SharedClans", "EnableSharedClans", true, "Enable global shared clan identity/membership/diplomacy across servers."); SharedClansPath = config.Bind("SharedClans", "SharedClansPath", "auto", "Absolute path to shared_clans folder. auto = current Valheim Saves/shared_clans."); ServerId = config.Bind("SharedClans", "ServerId", "auto", "Server identifier used in shared clan status/logs. auto = world name or machine name."); BaseTerritoryCap = config.Bind("Territory", "BaseCap", 10, "Territory cap for a fresh clan."); MaxTerritoryCap = config.Bind("Territory", "MaxCap", 100, "Absolute maximum territories a clan can ever hold."); TerritoryExpansionBlock = config.Bind("Territory", "ExpansionBlock", 10, "Extra territory slots granted per expansion purchase."); TerritoryExpansionCost = config.Bind("Territory", "ExpansionCost", 1000, "Coin cost to buy one expansion block of territory slots."); TerritoryExpansionSurtlingCoreCost = config.Bind("Territory", "TerritoryExpansionSurtlingCoreCost", 10, "Surtling Core cost to buy one expansion block of territory slots."); TerritoryUpgradeCostBase = config.Bind("Territory", "UpgradeCostBase", 500, "Base coin cost to upgrade a territory's size level (scales with level)."); TerritoryUpgradeSurtlingCoreCostBase = config.Bind("Territory", "TerritoryUpgradeSurtlingCoreCostBase", 5, "Base Surtling Core cost to upgrade a territory's size level (scales with level)."); MaxTerritorySizeLevel = config.Bind("Territory", "MaxSizeLevel", 5, "Maximum size/upgrade level per territory."); ProfitThreshold = config.Bind("Territory", "ProfitThreshold", 20, "At or below this many territories a clan runs at a profit."); BalancedThreshold = config.Bind("Territory", "BalancedThreshold", 40, "Above this many territories a clan runs at a loss."); IncomeBiomeLow = config.Bind("Economy", "IncomeBiomeLow", 5, "Coins per upkeep tick per size level for low-value biomes (Meadows, Black Forest, Ocean)."); IncomeBiomeMid = config.Bind("Economy", "IncomeBiomeMid", 10, "Coins per upkeep tick per size level for mid-value biomes (Swamp, Mountain, Ashlands, Deep North)."); IncomeBiomeHigh = config.Bind("Economy", "IncomeBiomeHigh", 20, "Coins per upkeep tick per size level for high-value biomes (Plains, Mistlands)."); UpkeepPerSizeLevel = config.Bind("Economy", "UpkeepPerSizeLevel", 4f, "Base coin upkeep per size level, before the global multiplier."); GlobalUpkeepBase = config.Bind("Economy", "GlobalUpkeepBase", 1f, "Base global upkeep multiplier."); GlobalUpkeepPerTerritory = config.Bind("Economy", "GlobalUpkeepPerTerritory", 0.1f, "Added to the global upkeep multiplier per territory beyond the free count."); GlobalUpkeepFreeTerritories = config.Bind("Economy", "GlobalUpkeepFreeTerritories", 5, "Territories that do not add to the global upkeep multiplier."); UpkeepTickMinutes = config.Bind("Economy", "UpkeepTickMinutes", 30, "Real minutes between economy ticks (income + upkeep)."); StartingTreasury = config.Bind("Economy", "StartingTreasury", 0, "Coins a clan starts with on creation."); BuildingProductionHours = config.Bind("Buildings", "BuildingProductionHours", 12, "Real hours between virtual kingdom building production ticks."); BuildingBuildCostBase = config.Bind("Buildings", "BuildingBuildCostBase", 500, "Base coin cost to build a kingdom building."); BuildingUpgradeCostBase = config.Bind("Buildings", "BuildingUpgradeCostBase", 500, "Base coin cost to upgrade a kingdom building."); BuildingRepairCostBase = config.Bind("Buildings", "BuildingRepairCostBase", 250, "Base coin cost to repair a damaged kingdom building."); BuildingCaptureMode = config.Bind("Buildings", "BuildingCaptureMode", "Transfer", "What happens to buildings when a territory is captured: Transfer, Damaged, or Destroy."); BuildingBuildCostByType = config.Bind("Buildings", "BuildingBuildCostByType", "TownCenter=500,ClanHall=1500,Longhouse=800,Warehouse=500,Treasury=750,TradePost=750,Marketplace=1000,TaxOffice=900,Embassy=900,LogisticsPost=650,LumberCamp=500,Quarry=500,CopperMine=750,IronMine=750,SilverMine=750,Farm=500,FishingPort=500,HunterLodge=500,Apiary=500,Tannery=600,Workshop=650,Smithy=750,Shipyard=900,Barracks=1000,Watchtower=750,Shrine=700,HealerHut=650", "Per-building build coin costs."); BuildingUpgradeCostByType = config.Bind("Buildings", "BuildingUpgradeCostByType", "Default=500,ClanHall=1000,Marketplace=750,Barracks=750,Shipyard=700", "Per-building upgrade coin cost per next level."); BuildingRepairCostByType = config.Bind("Buildings", "BuildingRepairCostByType", "Default=250,ClanHall=500,Barracks=350,Shipyard=350", "Per-building repair coin cost per current level."); BuildingProductionByType = config.Bind("Buildings", "BuildingProductionByType", "Treasury=2,TradePost=3,Marketplace=5,LumberCamp=1,Quarry=1,CopperMine=1,IronMine=1,SilverMine=1,Farm=1,FishingPort=1,HunterLodge=1,Apiary=1,Tannery=1,Shipyard=1", "Legacy display map only. Final v1 production is enforced in code, stored locally, and manually claimed."); BuildingMaxLevelByType = config.Bind("Buildings", "BuildingMaxLevelByType", "Default=5", "Per-building max level. Use Default=5 or entries such as Marketplace=5."); BuildingMaxCopiesByType = config.Bind("Buildings", "BuildingMaxCopiesByType", "Default=1", "Per-building max copies per territory. Final v1 uses one copy of each building type per territory."); BuildingBiomeRequirementByType = config.Bind("Buildings", "BuildingBiomeRequirementByType", "FinalV1=CodeControlled", "Legacy display map only. Final v1 building unlocks are enforced in code."); EnableClanMemberTax = config.Bind("ClanTax", "EnableClanMemberTax", true, "Enable clan member resource and coin taxes."); DefaultClanResourceTaxPercent = config.Bind("ClanTax", "DefaultClanResourceTaxPercent", 10, "Default resource tax percent for new and migrated clans."); DefaultClanCoinTaxPercent = config.Bind("ClanTax", "DefaultClanCoinTaxPercent", 10, "Default coin tax percent for new and migrated clans."); MaxClanTaxPercent = config.Bind("ClanTax", "MaxClanTaxPercent", 50, "Maximum clan tax percent Jarls may set."); TaxOnlySafeResources = config.Bind("ClanTax", "TaxOnlySafeResources", true, "If true, only safe basic resource items are taxed."); EnableTaxDebugLogs = config.Bind("ClanTax", "EnableTaxDebugLogs", false, "Enable clan tax debug logs."); VaultCapacityBase = config.Bind("Vault", "CapacityBase", 100, "Legacy clan vault tier-1 capacity fallback."); ClanVaultTier1CapacityStacks = config.Bind("Vault", "ClanVaultTier1CapacityStacks", 100, "Clan vault tier 1 capacity in item stacks. Higher tiers multiply this value."); VaultUpgradeCostBase = config.Bind("Vault", "UpgradeCostBase", 1000, "Base coin cost to upgrade vault tier (scales with tier)."); VaultUpgradeSurtlingCoreCostBase = config.Bind("Vault", "VaultUpgradeSurtlingCoreCostBase", 5, "Base Surtling Core cost to upgrade vault tier (scales with tier)."); MaxVaultTier = config.Bind("Vault", "MaxTier", 5, "Maximum vault tier."); VaultOpenDayOfWeek = config.Bind("Vault", "OpenDayOfWeek", 0, "Day of week the vault opens (0=Sunday, 6=Saturday)."); VaultOpenHourUTC = config.Bind("Vault", "OpenHourUTC", 16, "Hour (UTC) when the vault opens."); VaultOpenDurationMinutes = config.Bind("Vault", "OpenDurationMinutes", 30, "How long the vault stays open."); VaultAlwaysOpen = config.Bind("Vault", "AlwaysOpen", false, "If true the vault is always open (ignores the schedule)."); VaultUseIntervalSchedule = config.Bind("Vault", "VaultUseIntervalSchedule", false, "If true the vault opens every VaultOpenEveryMinutes for VaultOpenDurationMinutes."); VaultOpenEveryMinutes = config.Bind("Vault", "VaultOpenEveryMinutes", 60, "Interval schedule period in minutes."); VaultDepositMinRank = config.Bind("Vault", "VaultDepositMinRank", "Member", "Minimum clan rank allowed to deposit into the vault."); VaultWithdrawMinRank = config.Bind("Vault", "VaultWithdrawMinRank", "Officer", "Minimum clan rank allowed to withdraw from the vault."); VaultViewMinRank = config.Bind("Vault", "VaultViewMinRank", "Member", "Minimum clan rank allowed to view vault contents."); VaultAllowDepositWhenClosed = config.Bind("Vault", "VaultAllowDepositWhenClosed", false, "Legacy setting. New vault schedule rules block item deposits while the vault is closed by default."); EnableTerritoryIncomeToVault = config.Bind("Vault", "EnableTerritoryIncomeToVault", false, "Future placeholder: route territory income items to clan vault. Not implemented yet."); VaultRequireCapitalTerritory = config.Bind("Vault", "VaultRequireCapitalTerritory", true, "Require clan vault access from your Capital territory."); EnableFreebornVault = config.Bind("Vault", "EnableFreebornVault", true, "Enable Freeborn Homestead vaults."); FreebornVaultRequireHomestead = config.Bind("Vault", "FreebornVaultRequireHomestead", true, "Require Freeborn vault/treasury access from your Homestead."); AllowVaultInClanColonies = config.Bind("Vault", "AllowVaultInClanColonies", false, "Allow clan vault access from owned colony territories."); AllowVaultInFreebornOutposts = config.Bind("Vault", "AllowVaultInFreebornOutposts", false, "Allow Freeborn vault/treasury access from owned Outposts. Current Freeborn rules require Homestead access."); AllowFreebornVaultInOutposts = config.Bind("Vault", "AllowFreebornVaultInOutposts", false, "Allow Freeborn vault access from owned Outposts."); AllowCoinDepositOutsideCapital = config.Bind("Vault", "AllowCoinDepositOutsideCapital", false, "Allow coin deposits outside the required vault territory."); AllowCoinDepositWhenVaultClosed = config.Bind("Vault", "AllowCoinDepositWhenVaultClosed", false, "Allow coin deposits while the vault schedule is closed."); CoinItemPrefab = config.Bind("Vault", "CoinItemPrefab", "Coins", "Prefab name used as the coin item."); CoinsWeight = config.Bind("Vanilla Coins", "CoinsWeight", 0f, new ConfigDescription("Sets the weight of the vanilla Coins item.", (AcceptableValueBase)(object)new AcceptableValueRange(0f, float.MaxValue), Array.Empty())); CoinsMaxStack = config.Bind("Vanilla Coins", "CoinsMaxStack", 100000, new ConfigDescription("Sets the maximum stack size of the vanilla Coins item.", (AcceptableValueBase)(object)new AcceptableValueRange(1, int.MaxValue), Array.Empty())); VaultDefaultMemberCanDepositItems = config.Bind("VaultPermissions", "VaultDefaultMemberCanDepositItems", true, "Default member permission to deposit items."); VaultDefaultMemberCanWithdrawItems = config.Bind("VaultPermissions", "VaultDefaultMemberCanWithdrawItems", false, "Default member permission to withdraw items."); VaultDefaultMemberCanDepositCoins = config.Bind("VaultPermissions", "VaultDefaultMemberCanDepositCoins", true, "Default member permission to deposit coins."); VaultDefaultMemberCanWithdrawCoins = config.Bind("VaultPermissions", "VaultDefaultMemberCanWithdrawCoins", false, "Default member permission to withdraw coins."); VaultDefaultOfficerCanWithdrawItems = config.Bind("VaultPermissions", "VaultDefaultOfficerCanWithdrawItems", true, "Default officer permission to withdraw items."); VaultDefaultOfficerCanWithdrawCoins = config.Bind("VaultPermissions", "VaultDefaultOfficerCanWithdrawCoins", true, "Default officer permission to withdraw coins."); RoleBuffPercent = config.Bind("Roles", "BuffPercent", 20f, "Stat buff percentage in a role's preferred territory."); RoleDebuffPercent = config.Bind("Roles", "DebuffPercent", 20f, "Stat debuff percentage outside a role's preferred territory."); EnableSecondaryRole = config.Bind("Roles", "EnableSecondaryRole", true, "Allow players to select a secondary role."); SecondaryRoleEffectPercent = config.Bind("Roles", "SecondaryRoleEffectPercent", 60f, "Secondary role effect percentage."); EnableTertiaryRole = config.Bind("Roles", "EnableTertiaryRole", true, "Allow players to select a tertiary role."); TertiaryRoleEffectPercent = config.Bind("Roles", "TertiaryRoleEffectPercent", 40f, "Tertiary role effect percentage."); if (Math.Abs(SecondaryRoleEffectPercent.Value - 50f) < 0.01f) { SecondaryRoleEffectPercent.Value = 60f; Plugin.Log.LogInfo((object)"[FCW Config] Migrated SecondaryRoleEffectPercent from 50 to 60."); } if (Math.Abs(TertiaryRoleEffectPercent.Value - 25f) < 0.01f) { TertiaryRoleEffectPercent.Value = 40f; Plugin.Log.LogInfo((object)"[FCW Config] Migrated TertiaryRoleEffectPercent from 25 to 40."); } RequireExplorerForClaim = config.Bind("Roles", "RequireExplorerForClaim", true, "Only Explorers may claim unclaimed clan territory."); GuardianBuildCostReduction = config.Bind("Roles", "GuardianBuildCostReduction", 50f, "Build cost reduction (%) for Guardians in clan territory."); ParasiteCraftCostReduction = config.Bind("Roles", "ParasiteCraftCostReduction", 50f, "Craft cost reduction (%) for Parasites in enemy territory."); BuilderBuildCostReduction = config.Bind("Roles", "BuilderBuildCostReduction", 15f, "Fehu infrastructure build cost reduction (%) for Builders at primary effectiveness."); GathererCarryWeightBonus = config.Bind("Roles", "GathererCarryWeightBonus", 150, "Extra carry weight for Gatherers at primary effectiveness."); PathfinderDiscoveryCostReduction = config.Bind("RoleBonuses", "PathfinderDiscoveryCostReduction", 20f, "Territory discovery cost reduction (%)."); WardenUpgradeCostReduction = config.Bind("RoleBonuses", "WardenUpgradeCostReduction", 15f, "Territory infrastructure upgrade cost reduction (%)."); RaiderDamageBonus = config.Bind("RoleBonuses", "RaiderDamageBonus", 8f, "Damage bonus during official war on enemy territory (%)."); RaiderStaminaReduction = config.Bind("RoleBonuses", "RaiderStaminaReduction", 10f, "Combat stamina reduction during official war on enemy territory (%)."); GuardianDamageReduction = config.Bind("RoleBonuses", "GuardianDamageReduction", 8f, "Damage reduction on owned clan territory (%)."); GuardianBlockPowerBonus = config.Bind("RoleBonuses", "GuardianBlockPowerBonus", 10f, "Block power bonus on owned clan territory (%)."); BuilderUpgradeCostReduction = config.Bind("RoleBonuses", "BuilderUpgradeCostReduction", 10f, "Fehu infrastructure upgrade cost reduction (%)."); GathererBasicResourceChance = config.Bind("RoleBonuses", "GathererBasicResourceChance", 10f, "Chance for an extra basic non-ore resource (%)."); MerchantEconomicRewardBonus = config.Bind("RoleBonuses", "MerchantEconomicRewardBonus", 10f, "Personal Production Coins and economy reward bonus (%)."); MerchantTradeFeeReduction = config.Bind("RoleBonuses", "MerchantTradeFeeReduction", 10f, "Trade fee reduction (%)."); SmugglerTaxReduction = config.Bind("RoleBonuses", "SmugglerTaxReduction", 15f, "Personal clan tax reduction (%)."); ThiefSuccessChance = config.Bind("RoleBonuses", "ThiefSuccessChance", 65f, "Primary theft success chance (%)."); ThiefCooldownMinutes = config.Bind("RoleBonuses", "ThiefCooldownMinutes", 30, "Minutes between theft attempts."); ShamanTotemCostReduction = config.Bind("RoleBonuses", "ShamanTotemCostReduction", 15f, "Rot Totem placement cost reduction (%)."); DruidTotemDetectionRange = config.Bind("RoleBonuses", "DruidTotemDetectionRange", 100f, "Primary Rot Totem guidance range in metres."); DruidTamingSpeedBonus = config.Bind("RoleBonuses", "DruidTamingSpeedBonus", 15f, "Animal taming speed bonus (%)."); HunterBowDamageBonus = config.Bind("RoleBonuses", "HunterBowDamageBonus", 8f, "Bow damage bonus (%)."); HunterBowStaminaReduction = config.Bind("RoleBonuses", "HunterBowStaminaReduction", 10f, "Bow stamina reduction (%)."); ProspectorResourceChance = config.Bind("RoleBonuses", "ProspectorResourceChance", 12f, "Chance for extra ore or stone (%)."); ProspectorMiningStaminaReduction = config.Bind("RoleBonuses", "ProspectorMiningStaminaReduction", 10f, "Mining stamina reduction (%)."); SkipperShipSpeedBonus = config.Bind("RoleBonuses", "SkipperShipSpeedBonus", 15f, "Primary ship speed bonus while steering (%)."); SkipperShipDamageReduction = config.Bind("RoleBonuses", "SkipperShipDamageReduction", 20f, "Primary ship damage reduction while steering (%)."); SkaldRestedDurationBonus = config.Bind("RoleBonuses", "SkaldRestedDurationBonus", 15f, "Rested duration bonus (%)."); SkaldRestedStaminaRegenBonus = config.Bind("RoleBonuses", "SkaldRestedStaminaRegenBonus", 5f, "Stamina regeneration bonus while rested (%)."); LawspeakerProgressiveUpkeepReduction = config.Bind("RoleBonuses", "LawspeakerProgressiveUpkeepReduction", 15f, "Maximum clan Progressive Upkeep reduction from the strongest Lawspeaker (%)."); LawspeakerDiplomacyReduction = config.Bind("RoleBonuses", "LawspeakerDiplomacyReduction", 15f, "Diplomacy action cost reduction (%)."); RoleChangeCooldownHours = config.Bind("Roles", "ChangeCooldownHours", 24, "Hours between role changes."); PrimaryRoleChangeCooldownHours = config.Bind("Roles", "PrimaryRoleChangeCooldownHours", 24, "Hours between primary role changes."); SecondaryRoleChangeCooldownHours = config.Bind("Roles", "SecondaryRoleChangeCooldownHours", 24, "Hours between secondary role changes."); TertiaryRoleChangeCooldownHours = config.Bind("Roles", "TertiaryRoleChangeCooldownHours", 24, "Hours between tertiary role changes."); IgnoreCooldownIfSecondaryRoleIsNone = config.Bind("Roles", "IgnoreCooldownIfSecondaryRoleIsNone", true, "Allow initial secondary role selection without waiting for cooldown."); RolePreviewImagePathExplorer = config.Bind("Roles", "RolePreviewImagePath_Explorer", "", "Optional future image path for Explorer role preview."); RolePreviewImagePathRaider = config.Bind("Roles", "RolePreviewImagePath_Raider", "", "Optional future image path for Raider role preview."); RolePreviewImagePathGatherer = config.Bind("Roles", "RolePreviewImagePath_Gatherer", "", "Optional future image path for Gatherer role preview."); RolePreviewImagePathBuilder = config.Bind("Roles", "RolePreviewImagePath_Builder", "", "Optional future image path for Builder role preview."); RolePreviewImagePathShieldbearer = config.Bind("Roles", "RolePreviewImagePath_Shieldbearer", "", "Optional future image path for Shieldbearer role preview."); RolePreviewImagePathShaman = config.Bind("Roles", "RolePreviewImagePath_Shaman", "", "Optional future image path for Shaman role preview."); FreebornEnabled = config.Bind("Freeborn", "FreebornEnabled", true, "Enable independent Freeborn status."); FreebornMaxHomesteads = config.Bind("Freeborn", "FreebornMaxHomesteads", 1, "Maximum Homesteads per Freeborn player."); FreebornBaseOutposts = config.Bind("Freeborn", "FreebornBaseOutposts", 4, "Legacy config retained for compatibility; Freeborn design is fixed at 4 additional territories."); FreebornMaxOutposts = config.Bind("Freeborn", "FreebornMaxOutposts", 4, "Legacy config retained for compatibility; Freeborn design is fixed at 4 additional territories."); FreebornOutpostUpgradeCoinCostBase = config.Bind("Freeborn", "FreebornOutpostUpgradeCoinCostBase", 500, "Base coin cost for Freeborn outpost allowance upgrades."); FreebornOutpostUpgradeSurtlingCoreCostBase = config.Bind("Freeborn", "FreebornOutpostUpgradeSurtlingCoreCostBase", 5, "Base Surtling Core cost for Freeborn outpost allowance upgrades."); ShowFreebornBorders = config.Bind("Freeborn", "ShowFreebornBorders", true, "Show special borders for Freeborn Homesteads and Outposts."); FreebornHomesteadBorderColor = config.Bind("Freeborn", "FreebornHomesteadBorderColor", "Gold", "Border color for Freeborn Homesteads."); FreebornOutpostBorderColor = config.Bind("Freeborn", "FreebornOutpostBorderColor", "Silver", "Border color for Freeborn Outposts."); ShowFreebornOwnerName = config.Bind("Freeborn", "ShowFreebornOwnerName", true, "Show Freeborn owner names on map labels and tooltips."); ShowOdinsPeaceOnTooltip = config.Bind("Freeborn", "ShowOdinsPeaceOnTooltip", true, "Show Odin's Peace status on Freeborn territory tooltips."); EnableClanEmblemUrl = config.Bind("ClanProfile", "EnableClanEmblemUrl", true, "Enable a URL field for clan emblems."); ClanEmblemUrlMaxLength = config.Bind("ClanProfile", "ClanEmblemUrlMaxLength", 300, "Maximum length for clan emblem URLs."); AllowOfficerEditClanEmblem = config.Bind("ClanProfile", "AllowOfficerEditClanEmblem", false, "Allow Officers to edit ClanEmblemUrl. Jarl can always edit."); ClanEmblemRecommendedSizeText = config.Bind("ClanProfile", "ClanEmblemRecommendedSizeText", "Recommended: 256x256 PNG/JPG direct image URL.", "Text shown beside emblem URL fields."); EnableLocalEmblems = config.Bind("ClanProfile", "EnableLocalEmblems", true, "Use local emblem PNG files from the configured Emblems folder."); RequireUniqueClanEmblems = config.Bind("ClanProfile", "RequireUniqueClanEmblems", false, "If true each clan must use a unique local emblem."); EmblemFolder = config.Bind("ClanProfile", "EmblemFolder", "Emblems", "Folder under FortecaClanWarfare config data containing local emblem PNG files."); DefaultEmblem = config.Bind("ClanProfile", "DefaultEmblem", "emblem_001", "Default local emblem name without .png."); EnableClanLinks = config.Bind("ClanProfile", "EnableClanLinks", true, "Allow Jarls to set Discord and website links for their clan."); ClanDiscordUrlMaxLength = config.Bind("ClanProfile", "ClanDiscordUrlMaxLength", 200, "Maximum length for clan Discord links."); ClanWebsiteUrlMaxLength = config.Bind("ClanProfile", "ClanWebsiteUrlMaxLength", 200, "Maximum length for clan website links."); ClanSteamGroupUrlMaxLength = config.Bind("ClanProfile", "ClanSteamGroupUrlMaxLength", 200, "Maximum length for clan Steam group links."); ClanCustomUrlMaxLength = config.Bind("ClanProfile", "ClanCustomUrlMaxLength", 200, "Maximum length for clan custom links."); ClanCustomLabelMaxLength = config.Bind("ClanProfile", "ClanCustomLabelMaxLength", 40, "Maximum length for clan custom link labels."); EnableVaultDebugLogs = config.Bind("Vault", "EnableVaultDebugLogs", false, "Enable verbose vault inventory debug logs."); FreebornAbandonDays = config.Bind("Freeborn", "FreebornAbandonDays", 30, "Inactive days before a Homestead becomes abandoned."); FreebornAbandonGraceHours = config.Bind("Freeborn", "FreebornAbandonGraceHours", 24, "Grace hours after abandonment before anyone may claim."); AllowFreebornAbandonHomestead = config.Bind("Freeborn", "AllowFreebornAbandonHomestead", true, "Allow Freeborn players to abandon their Homestead and choose a new home."); AbandonHomesteadAlsoAbandonsOutposts = config.Bind("Freeborn", "AbandonHomesteadAlsoAbandonsOutposts", true, "If true, abandoning a Homestead also removes owned Outposts."); AbandonHomesteadCooldownHours = config.Bind("Freeborn", "AbandonHomesteadCooldownHours", 24, "Hours between Homestead abandon actions."); EnableProgressiveTerritoryUpkeep = config.Bind("ProgressiveUpkeep", "EnableProgressiveTerritoryUpkeep", true, "Enable extra upkeep pressure for large clan and Freeborn territory counts."); FreeTerritoriesBeforeUpkeep = config.Bind("ProgressiveUpkeep", "FreeTerritoriesBeforeUpkeep", 3, "Territory count at or below this value has no progressive upkeep."); TerritoryUpkeepTier1Start = config.Bind("ProgressiveUpkeep", "TerritoryUpkeepTier1Start", 6, "First territory count for tier 1 progressive upkeep."); TerritoryUpkeepTier1End = config.Bind("ProgressiveUpkeep", "TerritoryUpkeepTier1End", 10, "Last territory count for tier 1 progressive upkeep."); TerritoryUpkeepTier1Cost = config.Bind("ProgressiveUpkeep", "TerritoryUpkeepTier1Cost", 500, "Progressive upkeep per territory in tier 1."); TerritoryUpkeepTier2Start = config.Bind("ProgressiveUpkeep", "TerritoryUpkeepTier2Start", 11, "First territory count for tier 2 progressive upkeep."); TerritoryUpkeepTier2End = config.Bind("ProgressiveUpkeep", "TerritoryUpkeepTier2End", 20, "Last territory count for tier 2 progressive upkeep."); TerritoryUpkeepTier2Cost = config.Bind("ProgressiveUpkeep", "TerritoryUpkeepTier2Cost", 1000, "Progressive upkeep per territory in tier 2."); TerritoryUpkeepTier3Start = config.Bind("ProgressiveUpkeep", "TerritoryUpkeepTier3Start", 21, "First territory count for tier 3 progressive upkeep."); TerritoryUpkeepTier3End = config.Bind("ProgressiveUpkeep", "TerritoryUpkeepTier3End", 100, "Last territory count for tier 3 progressive upkeep."); TerritoryUpkeepTier3Cost = config.Bind("ProgressiveUpkeep", "TerritoryUpkeepTier3Cost", 2000, "Progressive upkeep per territory in tier 3."); PayUpkeepFromTreasuryFirst = config.Bind("ProgressiveUpkeep", "PayUpkeepFromTreasuryFirst", true, "Pay progressive upkeep from clan or Freeborn treasury first."); AllowPlayerWalletUpkeepFallback = config.Bind("ProgressiveUpkeep", "AllowPlayerWalletUpkeepFallback", true, "Reserved for online wallet fallback when player inventory access is available."); OdinsPeaceEnabled = config.Bind("OdinsPeace", "OdinsPeaceEnabled", true, "Enable Odin's Peace protection."); OdinsPeaceProtectHomestead = config.Bind("OdinsPeace", "OdinsPeaceProtectHomestead", true, "Protect Freeborn Homesteads."); OdinsPeaceProtectOutposts = config.Bind("OdinsPeace", "OdinsPeaceProtectOutposts", true, "Protect Freeborn Outposts."); ChallengeFreebornEnabled = config.Bind("OdinsPeace", "ChallengeFreebornEnabled", true, "Allow clans to challenge protected Freeborn Homesteads."); ChallengeWarningHours = config.Bind("OdinsPeace", "ChallengeWarningHours", 48, "Hours before Odin's Peace drops after a challenge."); ChallengeTributeCoins = config.Bind("OdinsPeace", "ChallengeTributeCoins", 1000, "Coins consumed to challenge Odin's Peace."); ChallengeTributeSurtlingCores = config.Bind("OdinsPeace", "ChallengeTributeSurtlingCores", 10, "Surtling Cores consumed to challenge Odin's Peace."); ChallengeTributeTrophyItem = config.Bind("OdinsPeace", "ChallengeTributeTrophyItem", "", "Optional trophy prefab consumed to challenge Odin's Peace."); ChallengeCooldownHours = config.Bind("OdinsPeace", "ChallengeCooldownHours", 72, "Hours between challenges against the same Homestead."); EnableTerritoryResourceOwnership = config.Bind("ResourceOwnership", "EnableTerritoryResourceOwnership", false, "Restrict gathering by territory ownership."); ClanGatherOnlyOwnTerritory = config.Bind("ResourceOwnership", "ClanGatherOnlyOwnTerritory", false, "If true clan members may gather only in own territory, plus allied territory when allowed."); AllowAllianceGathering = config.Bind("ResourceOwnership", "AllowAllianceGathering", true, "Allow allied clans to gather from each other's territory."); AllowGatherInAlliedTerritory = config.Bind("ResourceOwnership", "AllowGatherInAlliedTerritory", true, "Allow gathering in allied territory."); FreebornGatherOnlyOwnTerritory = config.Bind("ResourceOwnership", "FreebornGatherOnlyOwnTerritory", false, "If true Freeborn may gather only in their Homestead and Outposts."); MiningRestrictionEnabled = config.Bind("Main Game Settings", "MiningRestrictionEnabled", false, "If true, mining is restricted to allowed territory where supported by active hooks."); WoodcuttingRestrictionEnabled = config.Bind("Main Game Settings", "WoodcuttingRestrictionEnabled", false, "If true, woodcutting is restricted to allowed territory where supported by active hooks."); BuildingRestrictionEnabled = config.Bind("Main Game Settings", "BuildingRestrictionEnabled", false, "If true, building is restricted to allowed territory where supported by active hooks."); TerritoryCaptureEnabled = config.Bind("Main Game Settings", "TerritoryCaptureEnabled", true, "Master flag for territory capture rules where supported by active hooks."); ClanWarsEnabled = config.Bind("Main Game Settings", "ClanWarsEnabled", true, "Master flag for clan war rules where supported by active hooks."); FreebornProtectionEnabled = config.Bind("Main Game Settings", "FreebornProtectionEnabled", true, "Master flag for Freeborn protection rules where supported by active hooks."); WorldEventsEnabled = config.Bind("Main Game Settings", "WorldEventsEnabled", true, "Master flag for Forteca world events where supported by active hooks."); GamesEnabled = config.Bind("Main Game Settings", "GamesEnabled", true, "Master flag for Forteca games where supported by active hooks."); FreebornIgnoreResourceOwnership = config.Bind("ResourceOwnership", "FreebornIgnoreResourceOwnership", true, "Freeborn bypass resource ownership restrictions."); FreebornMaxFriends = config.Bind("FreebornFriendship", "FreebornMaxFriends", 2, "Maximum Freeborn friendship agreements per Freeborn player."); AllowFriendGathering = config.Bind("FreebornFriendship", "AllowFriendGathering", true, "Allow Freeborn friends to gather in each other's territory."); AllowFriendBuilding = config.Bind("FreebornFriendship", "AllowFriendBuilding", false, "Allow Freeborn friends to build in each other's territory."); AllowFriendContainers = config.Bind("FreebornFriendship", "AllowFriendContainers", false, "Allow Freeborn friends to use containers in each other's territory."); AllowFriendTerritoryManagement = config.Bind("FreebornFriendship", "AllowFriendTerritoryManagement", false, "Allow Freeborn friends limited territory management."); AllianceBorderEnabled = config.Bind("Alliance", "AllianceBorderEnabled", true, "Show alliance border identity for allied clan territories."); AllianceBorderThickness = config.Bind("Alliance", "AllianceBorderThickness", 2f, "Alliance border thickness."); AllianceBorderGlow = config.Bind("Alliance", "AllianceBorderGlow", true, "Draw a subtle glow under alliance borders."); EnableVassals = config.Bind("Diplomacy", "EnableVassals", true, "Enable vassal and overlord diplomacy."); AllowVassalJoinOverlordWars = config.Bind("Diplomacy", "AllowVassalJoinOverlordWars", false, "Allow vassals to automatically join overlord wars."); MaxVassalsPerClan = config.Bind("Diplomacy", "MaxVassalsPerClan", 10, "Maximum active vassals per overlord clan."); EnableTerritoryTransfers = config.Bind("Diplomacy", "EnableTerritoryTransfers", true, "Enable diplomatic territory gift and sale offers."); AllowCapitalTransfer = config.Bind("Diplomacy", "AllowCapitalTransfer", false, "Allow capitals to be transferred through territory deals."); MaxPendingTerritoryDealsPerClan = config.Bind("Diplomacy", "MaxPendingTerritoryDealsPerClan", 5, "Maximum pending incoming and outgoing territory deals per clan."); TerritoryDealExpirationHours = config.Bind("Diplomacy", "TerritoryDealExpirationHours", 48, "Hours before pending territory deals expire."); ColonyBorderThickness = config.Bind("Territory", "ColonyBorderThickness", 4f, "Border thickness used for clan colonies."); CapitalBorderThicknessMultiplier = config.Bind("Territory", "CapitalBorderThicknessMultiplier", 2f, "Multiplier applied to colony thickness for clan capitals."); HomesteadBorderThicknessMultiplier = config.Bind("Freeborn", "HomesteadBorderThicknessMultiplier", 1.5f, "Multiplier applied to colony thickness for Freeborn Homesteads."); BlockGatheringInEnemyTerritory = config.Bind("ResourceOwnership", "BlockGatheringInEnemyTerritory", true, "Block enemy territory gathering unless allowed by role/config."); EnemyGatheringAllowedForRoles = config.Bind("ResourceOwnership", "EnemyGatheringAllowedForRoles", "Raider,Gatherer", "Comma separated roles allowed to gather in enemy territory."); VaultCrackDurationMinutes = config.Bind("VaultCrack", "DurationMinutes", 30, "Minutes an Invader must hold an enemy territory to crack the vault."); VaultCrackCooldownHours = config.Bind("VaultCrack", "CooldownHours", 6, "Hours between vault crack attempts per player."); VaultCrackMinStacks = config.Bind("VaultCrack", "MinStacks", 1, "Minimum item stacks stolen on a successful crack."); VaultCrackMaxStacks = config.Bind("VaultCrack", "MaxStacks", 5, "Maximum item stacks stolen on a successful crack."); WarDurationMinutes = config.Bind("War", "DurationMinutes", 120, "How long a declared war stays active."); RaidPlayersRequired = config.Bind("War", "RaidPlayersRequired", 1, "Attackers required to sustain a Raid."); WarPlayersRequired = config.Bind("War", "WarPlayersRequired", 3, "Attackers required to sustain a War."); RageWarPlayersRequired = config.Bind("War", "RageWarPlayersRequired", 5, "Attackers required to sustain a Rage War."); WarDeclareCost = config.Bind("War", "DeclareCost", 200, "Coin cost to declare a war."); CaptureThresholdPerSize = config.Bind("Capture", "ThresholdPerSize", 100, "Capture threshold = ThresholdPerSize x territory size level."); ClaimSpeedBase = config.Bind("Capture", "ClaimSpeedBase", 1f, "Base capture progress per attacker per claim tick (1s)."); CombatPointsKill = config.Bind("Capture", "CombatPointsKill", 500, "Capture progress from a player kill inside the contested territory."); CombatPointsElite = config.Bind("Capture", "CombatPointsElite", 1000, "Capture progress from an elite kill."); CombatPointsBoss = config.Bind("Capture", "CombatPointsBoss", 5000, "Capture progress from a boss kill."); InterruptNoAttackerSeconds = config.Bind("Capture", "InterruptNoAttackerSeconds", 60, "Seconds with no attackers present before capture progress resets."); InterruptDefenderMajoritySeconds = config.Bind("Capture", "InterruptDefenderMajoritySeconds", 30, "Seconds of defender majority before capture progress reverses."); SiegeAttackerCount = config.Bind("Capture", "SiegeAttackerCount", 5, "Attackers present to trigger Siege state."); SiegeDefenseReduction = config.Bind("Capture", "SiegeDefenseReduction", 30f, "Defense reduction (%) while a territory is under Siege."); SiegeClaimSpeedBonus = config.Bind("Capture", "SiegeClaimSpeedBonus", 50f, "Capture speed bonus (%) while a territory is under Siege."); TerritoryHPBase = config.Bind("Defense", "TerritoryHPBase", 1000f, "Territory HP = Base x SizeLevel x type modifier."); CapitalHPModifier = config.Bind("Defense", "CapitalHPModifier", 2f, "HP modifier for Capital territories."); ColonyHPModifier = config.Bind("Defense", "ColonyHPModifier", 1f, "HP modifier for Colony territories."); RotDecayHours = config.Bind("Rot", "DecayHours", 6, "Legacy compatibility setting. Physical active Root Totems now determine Corruption; this value no longer adds automatic decay."); RotTotemCostBase = config.Bind("Rot", "TotemCostBase", 100, "Totem cost = Base x totem level x territory value tier."); MaxRotTotemsPerTerritory = config.Bind("Rot", "MaxTotemsPerTerritory", 4, "Maximum rot totems on a single territory."); BountySystemEnabled = config.Bind("Bounty", "Enabled", true, "Enable the bounty system."); AutoBountyIllegalKill = config.Bind("Bounty", "AutoBountyIllegalKill", 250, "Bounty added for a kill made outside a war (an 'illegal' kill)."); BountyRevealThreshold = config.Bind("Bounty", "RevealThreshold", 1000, "Bounty above which a target's territory is revealed on the map."); FullPvPDuringWar = config.Bind("PvP", "FullPvPDuringWar", true, "Force PvP on for players inside a territory involved in an active war."); ProtectHotbarOnDeath = config.Bind("PvP", "ProtectHotbarOnDeath", false, "If true the hotbar is kept on death (purely informational \ufffd drop behaviour is vanilla)."); AntiAltEnabled = config.Bind("Security", "AntiAltEnabled", true, "Enable basic anti-alt protections (clan-hop cooldown)."); OfflineRaidProtection = config.Bind("Security", "OfflineRaidProtection", true, "Reduce capture/crack effectiveness against clans with no members online."); OfflineRaidEffectReduction = config.Bind("Security", "OfflineRaidEffectReduction", 50f, "Effectiveness reduction (%) for offline raids."); SeasonLengthDays = config.Bind("Season", "LengthDays", 60, "Real days per season before the Cycle of Odin resets territories/vaults/rot. 0 = never auto-reset."); } public static string GetIcon(int index) { if (ClanIcons.Length == 0) { return "*"; } return ClanIcons[(index % ClanIcons.Length + ClanIcons.Length) % ClanIcons.Length]; } public static Color GetColor(int index) { //IL_002c: Unknown result type (might be due to invalid IL or missing references) //IL_0008: Unknown result type (might be due to invalid IL or missing references) if (ClanColors.Length == 0) { return Color.white; } return ClanColors[(index % ClanColors.Length + ClanColors.Length) % ClanColors.Length]; } public static Color GetNamedColor(string name, Color fallback) { //IL_000e: Unknown result type (might be due to invalid IL or missing references) //IL_0036: Unknown result type (might be due to invalid IL or missing references) //IL_007a: Unknown result type (might be due to invalid IL or missing references) //IL_008e: Unknown result type (might be due to invalid IL or missing references) //IL_00a2: Unknown result type (might be due to invalid IL or missing references) //IL_00b6: Unknown result type (might be due to invalid IL or missing references) //IL_00ca: Unknown result type (might be due to invalid IL or missing references) //IL_00de: Unknown result type (might be due to invalid IL or missing references) //IL_00f2: Unknown result type (might be due to invalid IL or missing references) //IL_0106: 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_012e: Unknown result type (might be due to invalid IL or missing references) if (string.Equals(name, "Gold", StringComparison.OrdinalIgnoreCase)) { return GoldColor; } if (string.Equals(name, "Yellow", StringComparison.OrdinalIgnoreCase)) { return new Color(1f, 0.96f, 0.48f, 1f); } if (string.Equals(name, "Ochre", StringComparison.OrdinalIgnoreCase) || string.Equals(name, "DarkYellow", StringComparison.OrdinalIgnoreCase) || string.Equals(name, "Dark Yellow", StringComparison.OrdinalIgnoreCase)) { return new Color(0.55f, 0.42f, 0.05f, 1f); } if (string.Equals(name, "Silver", StringComparison.OrdinalIgnoreCase)) { return SilverColor; } if (string.Equals(name, "White", StringComparison.OrdinalIgnoreCase)) { return WhiteColor; } if (string.Equals(name, "Cream", StringComparison.OrdinalIgnoreCase)) { return CreamColor; } if (string.Equals(name, "Steel", StringComparison.OrdinalIgnoreCase)) { return SteelColor; } if (string.Equals(name, "Graphite", StringComparison.OrdinalIgnoreCase)) { return GraphiteColor; } if (string.Equals(name, "Anthracite", StringComparison.OrdinalIgnoreCase)) { return AnthraciteColor; } if (string.Equals(name, "Orange", StringComparison.OrdinalIgnoreCase)) { return OrangeFallbackColor; } if (string.Equals(name, "Gray", StringComparison.OrdinalIgnoreCase) || string.Equals(name, "Grey", StringComparison.OrdinalIgnoreCase)) { return GrayFallbackColor; } return fallback; } public static Color GetFreebornBorderColor(TerritoryType type) { //IL_000b: Unknown result type (might be due to invalid IL or missing references) //IL_0004: Unknown result type (might be due to invalid IL or missing references) if (type == TerritoryType.Homestead) { return WhiteColor; } return GetNeutralHomesteadBorderColor(type); } public static Color GetFreebornBorderColor(ClanTerritory territory) { //IL_000c: Unknown result type (might be due to invalid IL or missing references) //IL_001e: Unknown result type (might be due to invalid IL or missing references) if (territory == null || territory.Type == TerritoryType.Homestead) { return WhiteColor; } return GetNeutralHomesteadBorderColor(territory.GridX, territory.GridY); } public static Color GetNeutralHomesteadBorderColor(TerritoryType type) { //IL_0008: Unknown result type (might be due to invalid IL or missing references) //IL_000d: Unknown result type (might be due to invalid IL or missing references) //IL_0014: Unknown result type (might be due to invalid IL or missing references) //IL_0019: 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_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_0038: Unknown result type (might be due to invalid IL or missing references) //IL_003d: Unknown result type (might be due to invalid IL or missing references) //IL_0044: Unknown result type (might be due to invalid IL or missing references) //IL_0049: Unknown result type (might be due to invalid IL or missing references) //IL_005f: Unknown result type (might be due to invalid IL or missing references) Color[] array = (Color[])(object)new Color[6] { CreamColor, SilverColor, SteelColor, GrayFallbackColor, GraphiteColor, AnthraciteColor }; int num = Mathf.Abs((int)type * 37); return array[num % array.Length]; } public static Color GetNeutralHomesteadBorderColor(int gridX, int gridY) { //IL_0008: Unknown result type (might be due to invalid IL or missing references) //IL_000d: Unknown result type (might be due to invalid IL or missing references) //IL_0014: Unknown result type (might be due to invalid IL or missing references) //IL_0019: 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_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_0038: Unknown result type (might be due to invalid IL or missing references) //IL_003d: Unknown result type (might be due to invalid IL or missing references) //IL_0044: Unknown result type (might be due to invalid IL or missing references) //IL_0049: 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) Color[] array = (Color[])(object)new Color[6] { CreamColor, SilverColor, SteelColor, GrayFallbackColor, GraphiteColor, AnthraciteColor }; int num = Mathf.Abs((gridX * 73856093) ^ (gridY * 19349663)); return array[num % array.Length]; } public static bool IsReservedClanColor(Color color) { //IL_0047: Unknown result type (might be due to invalid IL or missing references) //IL_0048: Unknown result type (might be due to invalid IL or missing references) //IL_0064: Unknown result type (might be due to invalid IL or missing references) //IL_0065: Unknown result type (might be due to invalid IL or missing references) //IL_0081: Unknown result type (might be due to invalid IL or missing references) //IL_0082: Unknown result type (might be due to invalid IL or missing references) string[] array = (ReservedClanColors?.Value ?? "White,Silver").Split(new char[1] { ',' }, StringSplitOptions.RemoveEmptyEntries); for (int i = 0; i < array.Length; i++) { string a = array[i].Trim(); if (string.Equals(a, "Gold", StringComparison.OrdinalIgnoreCase) && ColorsClose(color, GoldColor)) { return true; } if (string.Equals(a, "Silver", StringComparison.OrdinalIgnoreCase) && ColorsClose(color, SilverColor)) { return true; } if (string.Equals(a, "White", StringComparison.OrdinalIgnoreCase) && ColorsClose(color, WhiteColor)) { return true; } } return false; } public static bool ColorsClose(Color a, Color b) { //IL_0000: 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_0019: Unknown result type (might be due to invalid IL or missing references) //IL_001f: Unknown result type (might be due to invalid IL or missing references) //IL_0032: Unknown result type (might be due to invalid IL or missing references) //IL_0038: Unknown result type (might be due to invalid IL or missing references) if (Mathf.Abs(a.r - b.r) < 0.04f && Mathf.Abs(a.g - b.g) < 0.04f) { return Mathf.Abs(a.b - b.b) < 0.04f; } return false; } public static Clan GetClan(int clanId) { return WarfareManager.Instance?.Data?.GetClan(clanId); } } public static class ClanExporter { private static string ExportPath => Path.Combine(GetExportDir(), "clan_export.json"); private static string GetExportDir() { string text = Path.Combine(Paths.ConfigPath, "FortecaClanWarfare"); if (!Directory.Exists(text)) { Directory.CreateDirectory(text); } return text; } public static string ExportClansToJson() { WarfareData warfareData = WarfareManager.Instance?.Data; if (warfareData == null) { return "No warfare data loaded."; } StringBuilder stringBuilder = new StringBuilder(); stringBuilder.AppendLine("{"); stringBuilder.AppendLine($" \"season\": {warfareData.SeasonNumber},"); stringBuilder.AppendLine(" \"clans\": ["); int num = 0; foreach (Clan value in warfareData.Clans.Values) { if (num > 0) { stringBuilder.AppendLine(","); } stringBuilder.Append(" { \"name\": " + JsonString(value.Name) + ", \"tag\": " + JsonString(value.Tag) + ", " + $"\"icon\": {value.IconIndex}, \"colorR\": {To255(value.ColorR)}, \"colorG\": {To255(value.ColorG)}, " + $"\"colorB\": {To255(value.ColorB)}, \"treasury\": {value.Treasury}, \"vaultTier\": {value.VaultTier} }}"); num++; } stringBuilder.AppendLine(); stringBuilder.AppendLine(" ],"); stringBuilder.AppendLine(" \"players\": ["); int num2 = 0; foreach (PlayerClanInfo value2 in warfareData.Players.Values) { if (value2.ClanId == 0) { continue; } Clan clan = warfareData.GetClan(value2.ClanId); if (clan != null) { if (num2 > 0) { stringBuilder.AppendLine(","); } stringBuilder.Append($" {{ \"playerId\": {value2.PlayerId}, \"playerName\": {JsonString(value2.PlayerName)}, " + $"\"clanName\": {JsonString(clan.Name)}, \"role\": {(int)value2.Role}, \"rank\": {(int)value2.Rank} }}"); num2++; } } stringBuilder.AppendLine(); stringBuilder.AppendLine(" ],"); stringBuilder.AppendLine(" \"vaults\": {"); int num3 = 0; foreach (KeyValuePair> clanVault in warfareData.ClanVaults) { if (clanVault.Value == null || clanVault.Value.Count == 0) { continue; } Clan clan2 = warfareData.GetClan(clanVault.Key); if (clan2 != null) { if (num3 > 0) { stringBuilder.AppendLine(","); } stringBuilder.AppendLine(" " + JsonString(clan2.Name) + ": ["); for (int i = 0; i < clanVault.Value.Count; i++) { ClanVaultItem clanVaultItem = clanVault.Value[i]; stringBuilder.Append(" { \"prefab\": " + JsonString(clanVaultItem.PrefabName) + ", \"name\": " + JsonString(clanVaultItem.DisplayName) + ", " + $"\"amount\": {clanVaultItem.Amount} }}"); stringBuilder.AppendLine((i < clanVault.Value.Count - 1) ? "," : ""); } stringBuilder.Append(" ]"); num3++; } } stringBuilder.AppendLine(); stringBuilder.AppendLine(" }"); stringBuilder.AppendLine("}"); File.WriteAllText(ExportPath, stringBuilder.ToString()); return $"Exported {num} clans, {num2} players and {num3} vaults to {ExportPath}"; } public static string ImportClansFromJson() { WarfareData warfareData = WarfareManager.Instance?.Data; if (warfareData == null) { return "No warfare data loaded."; } if (!File.Exists(ExportPath)) { return "File not found: " + ExportPath; } try { string json = File.ReadAllText(ExportPath); int num = 0; int num2 = 0; int num3 = 0; Dictionary dictionary = new Dictionary(StringComparer.OrdinalIgnoreCase); foreach (Clan value3 in warfareData.Clans.Values) { if (!string.IsNullOrEmpty(value3.Name)) { dictionary[value3.Name] = value3.ClanId; } } foreach (string item in ExtractArray(json, "clans")) { string text = ExtractString(item, "name"); if (!string.IsNullOrEmpty(text) && !dictionary.ContainsKey(text)) { int num4 = warfareData.NextClanId++; Clan value = new Clan { ClanId = num4, Name = text, Tag = ExtractString(item, "tag"), IconIndex = ExtractInt(item, "icon"), ColorR = (float)ExtractInt(item, "colorR") / 255f, ColorG = (float)ExtractInt(item, "colorG") / 255f, ColorB = (float)ExtractInt(item, "colorB") / 255f, Treasury = ExtractLong(item, "treasury"), VaultTier = Math.Max(1, ExtractInt(item, "vaultTier")), CreatedTimestamp = WarfareManager.Now(), NextElectionTimestamp = WarfareManager.Now() + 1209600 }; warfareData.Clans[num4] = value; dictionary[text] = num4; num++; } } foreach (string item2 in ExtractArray(json, "players")) { long num5 = ExtractLong(item2, "playerId"); string text2 = ExtractString(item2, "clanName"); if (num5 != 0L && !string.IsNullOrEmpty(text2) && dictionary.TryGetValue(text2, out var value2) && !warfareData.Players.ContainsKey(num5)) { warfareData.Players[num5] = new PlayerClanInfo { PlayerId = num5, PlayerName = ExtractString(item2, "playerName"), ClanId = value2, Role = (ClanRole)ExtractInt(item2, "role"), Rank = (ClanRank)ExtractInt(item2, "rank"), JoinedClanTimestamp = WarfareManager.Now() }; num2++; } } foreach (Clan value4 in warfareData.Clans.Values) { if (warfareData.GetPlayer(value4.JarlId) != null) { continue; } foreach (PlayerClanInfo value5 in warfareData.Players.Values) { if (value5.ClanId == value4.ClanId) { value4.JarlId = value5.PlayerId; value5.Rank = ClanRank.Jarl; break; } } } string text3 = ExtractObject(json, "vaults"); if (!string.IsNullOrEmpty(text3)) { foreach (KeyValuePair item3 in dictionary) { List list = ExtractArray(text3, item3.Key); if (list.Count == 0) { continue; } foreach (string item4 in list) { string text4 = ExtractString(item4, "prefab"); int num6 = ExtractInt(item4, "amount"); if (!string.IsNullOrEmpty(text4) && num6 > 0) { warfareData.AddToVault(item3.Value, text4, ExtractString(item4, "name"), num6); num3++; } } } } WarfareManager.Instance?.MarkDirty(); WarfareRPC.Instance?.SyncAllDataToClients(); return $"Imported {num} clans, {num2} players, {num3} vault items."; } catch (Exception ex) { return "Import failed: " + ex.Message; } } private static int To255(float f) { return Mathf.Clamp(Mathf.RoundToInt(f * 255f), 0, 255); } private static string JsonString(string s) { if (s == null) { return "\"\""; } return "\"" + s.Replace("\\", "\\\\").Replace("\"", "\\\"") + "\""; } private static List ExtractArray(string json, string key) { List list = new List(); int num = json.IndexOf("\"" + key + "\""); if (num < 0) { return list; } int num2 = json.IndexOf('[', num); if (num2 < 0) { return list; } int num3 = 0; int num4 = -1; for (int i = num2; i < json.Length; i++) { char c = json[i]; if (c == '[') { num3++; if (num3 == 1) { continue; } } if (c == ']') { num3--; if (num3 == 0) { break; } } if (c == '{' && num3 == 1) { num4 = i; } if (c == '}' && num3 == 1 && num4 >= 0) { list.Add(json.Substring(num4, i - num4 + 1)); num4 = -1; } } return list; } private static string ExtractObject(string json, string key) { int num = json.IndexOf("\"" + key + "\""); if (num < 0) { return null; } int num2 = json.IndexOf('{', num + key.Length + 2); if (num2 < 0) { return null; } int num3 = 0; for (int i = num2; i < json.Length; i++) { if (json[i] == '{') { num3++; } if (json[i] == '}') { num3--; if (num3 == 0) { return json.Substring(num2, i - num2 + 1); } } } return null; } private static string ExtractString(string json, string key) { string text = "\"" + key + "\":"; int num = json.IndexOf(text); if (num < 0) { text = "\"" + key + "\" :"; num = json.IndexOf(text); } if (num < 0) { return ""; } int num2 = json.IndexOf('"', num + text.Length); if (num2 < 0) { return ""; } int num3 = json.IndexOf('"', num2 + 1); if (num3 < 0) { return ""; } return json.Substring(num2 + 1, num3 - num2 - 1); } private static int ExtractInt(string json, string key) { return (int)ExtractLong(json, key); } private static long ExtractLong(string json, string key) { string text = "\"" + key + "\":"; int num = json.IndexOf(text); if (num < 0) { text = "\"" + key + "\" :"; num = json.IndexOf(text); } if (num < 0) { return 0L; } int i; for (i = num + text.Length; i < json.Length && (json[i] == ' ' || json[i] == '\t'); i++) { } int j; for (j = i; j < json.Length && (char.IsDigit(json[j]) || json[j] == '-'); j++) { } if (!long.TryParse(json.Substring(i, j - i), out var result)) { return 0L; } return result; } } [HarmonyPatch] public static class ClanExporterCommands { [Serializable] [CompilerGenerated] private sealed class _003C_003Ec { public static readonly _003C_003Ec _003C_003E9 = new _003C_003Ec(); public static ConsoleEvent _003C_003E9__0_0; public static ConsoleEvent _003C_003E9__0_1; public static ConsoleEvent _003C_003E9__0_2; public static ConsoleEvent _003C_003E9__0_3; internal void _003CRegisterCommands_003Eb__0_0(ConsoleEventArgs args) { args.Context.AddString(ClanExporter.ExportClansToJson()); } internal void _003CRegisterCommands_003Eb__0_1(ConsoleEventArgs args) { args.Context.AddString(ClanExporter.ImportClansFromJson()); } internal void _003CRegisterCommands_003Eb__0_2(ConsoleEventArgs args) { ZNet instance = ZNet.instance; if ((Object)(object)instance != (Object)null && instance.IsServer()) { WarfareManager.Instance?.RunCycleOfOdin(); args.Context.AddString("Cycle of Odin triggered."); } else { args.Context.AddString("Server only."); } } internal void _003CRegisterCommands_003Eb__0_3(ConsoleEventArgs args) { ZNet instance = ZNet.instance; if ((Object)(object)instance == (Object)null || !instance.IsServer()) { args.Context.AddString("Server only."); return; } if (args.Length < 3 || !int.TryParse(args[1], out var result) || !long.TryParse(args[2], out var result2)) { args.Context.AddString("Usage: clan_givecoins "); return; } Clan clan = WarfareManager.Instance?.Data?.GetClan(result); if (clan == null) { args.Context.AddString("Clan not found."); return; } long treasury = clan.Treasury; clan.Treasury += result2; Economy.RecordTreasuryTransaction(clan, DateTimeOffset.UtcNow.ToUnixTimeSeconds(), "Admin Adjustment", result2, treasury, clan.Treasury, "clan_givecoins"); WarfareManager.Instance.MarkDirty(); WarfareRPC.Instance?.SyncAllDataToClients(); args.Context.AddString($"{clan.Name} treasury is now {clan.Treasury}."); } } [Serializable] [CompilerGenerated] private sealed class <>c { public static readonly <>c <>9 = new <>c(); public static ConsoleEvent <>9__1_0; public static ConsoleEvent <>9__1_1; public static ConsoleEvent <>9__1_2; public static ConsoleEvent <>9__1_3; internal void b__1_0(ConsoleEventArgs args) { args.Context.AddString(ClanExporter.ExportClansToJson()); } internal void b__1_1(ConsoleEventArgs args) { args.Context.AddString(ClanExporter.ImportClansFromJson()); } internal void b__1_2(ConsoleEventArgs args) { ZNet instance = ZNet.instance; if ((Object)(object)instance != (Object)null && instance.IsServer()) { WarfareManager.Instance?.RunCycleOfOdin(); args.Context.AddString("Cycle of Odin triggered."); } else { args.Context.AddString("Server only."); } } internal void b__1_3(ConsoleEventArgs args) { ZNet instance = ZNet.instance; if ((Object)(object)instance == (Object)null || !instance.IsServer()) { args.Context.AddString("Server only."); return; } if (args.Length < 3 || !int.TryParse(args[1], out var result) || !long.TryParse(args[2], out var result2)) { args.Context.AddString("Usage: clan_givecoins "); return; } Clan clan = WarfareManager.Instance?.Data?.GetClan(result); if (clan == null) { args.Context.AddString("Clan not found."); return; } long treasury = clan.Treasury; clan.Treasury += result2; Economy.RecordTreasuryTransaction(clan, DateTimeOffset.UtcNow.ToUnixTimeSeconds(), "Admin Adjustment", result2, treasury, clan.Treasury, "clan_givecoins"); WarfareManager.Instance.MarkDirty(); WarfareRPC.Instance?.SyncAllDataToClients(); args.Context.AddString($"{clan.Name} treasury is now {clan.Treasury}."); } } [HarmonyPatch(typeof(Terminal), "InitTerminal")] [HarmonyPostfix] public static void RegisterCommands() { //IL_003a: Unknown result type (might be due to invalid IL or missing references) //IL_004d: Expected O, but got Unknown //IL_0048: Unknown result type (might be due to invalid IL or missing references) //IL_0088: Unknown result type (might be due to invalid IL or missing references) //IL_009b: Expected O, but got Unknown //IL_0096: 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_0022: Unknown result type (might be due to invalid IL or missing references) //IL_0028: Expected O, but got Unknown //IL_00d6: Unknown result type (might be due to invalid IL or missing references) //IL_00e9: Expected O, but got Unknown //IL_00e4: Unknown result type (might be due to invalid IL or missing references) //IL_006b: Unknown result type (might be due to invalid IL or missing references) //IL_0070: Unknown result type (might be due to invalid IL or missing references) //IL_0076: Expected O, but got Unknown //IL_0124: Unknown result type (might be due to invalid IL or missing references) //IL_0137: Expected O, but got Unknown //IL_0132: Unknown result type (might be due to invalid IL or missing references) //IL_00b9: Unknown result type (might be due to invalid IL or missing references) //IL_00be: Unknown result type (might be due to invalid IL or missing references) //IL_00c4: Expected O, but got Unknown //IL_0107: 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_0112: Expected O, but got Unknown object obj = _003C_003Ec._003C_003E9__0_0; if (obj == null) { object obj2 = <>c.<>9__1_0; if (obj2 == null) { ConsoleEvent val = delegate(ConsoleEventArgs args) { args.Context.AddString(ClanExporter.ExportClansToJson()); }; <>c.<>9__1_0 = val; obj2 = (object)val; } _003C_003Ec._003C_003E9__0_0 = (ConsoleEvent)obj2; obj = obj2; } new ConsoleCommand("clan_export", "Export clans, rosters and vaults to JSON for cross-server sharing (admin)", (ConsoleEvent)obj, true, false, false, false, false, (ConsoleOptionsFetcher)null, false, false, false); object obj3 = _003C_003Ec._003C_003E9__0_1; if (obj3 == null) { object obj4 = <>c.<>9__1_1; if (obj4 == null) { ConsoleEvent val2 = delegate(ConsoleEventArgs args) { args.Context.AddString(ClanExporter.ImportClansFromJson()); }; <>c.<>9__1_1 = val2; obj4 = (object)val2; } _003C_003Ec._003C_003E9__0_1 = (ConsoleEvent)obj4; obj3 = obj4; } new ConsoleCommand("clan_import", "Import clans, rosters and vaults from clan_export.json (admin)", (ConsoleEvent)obj3, true, false, false, false, false, (ConsoleOptionsFetcher)null, false, false, false); object obj5 = _003C_003Ec._003C_003E9__0_2; if (obj5 == null) { object obj6 = <>c.<>9__1_2; if (obj6 == null) { ConsoleEvent val3 = delegate(ConsoleEventArgs args) { ZNet instance = ZNet.instance; if ((Object)(object)instance != (Object)null && instance.IsServer()) { WarfareManager.Instance?.RunCycleOfOdin(); args.Context.AddString("Cycle of Odin triggered."); } else { args.Context.AddString("Server only."); } }; <>c.<>9__1_2 = val3; obj6 = (object)val3; } _003C_003Ec._003C_003E9__0_2 = (ConsoleEvent)obj6; obj5 = obj6; } new ConsoleCommand("clan_season_reset", "Force the Cycle of Odin — wipe territories/vaults/rot, keep clans (admin)", (ConsoleEvent)obj5, true, false, false, false, false, (ConsoleOptionsFetcher)null, false, false, false); object obj7 = _003C_003Ec._003C_003E9__0_3; if (obj7 == null) { object obj8 = <>c.<>9__1_3; if (obj8 == null) { ConsoleEvent val4 = delegate(ConsoleEventArgs args) { ZNet instance = ZNet.instance; int result; long result2; if ((Object)(object)instance == (Object)null || !instance.IsServer()) { args.Context.AddString("Server only."); } else if (args.Length < 3 || !int.TryParse(args[1], out result) || !long.TryParse(args[2], out result2)) { args.Context.AddString("Usage: clan_givecoins "); } else { Clan clan = WarfareManager.Instance?.Data?.GetClan(result); if (clan == null) { args.Context.AddString("Clan not found."); } else { long treasury = clan.Treasury; clan.Treasury += result2; Economy.RecordTreasuryTransaction(clan, DateTimeOffset.UtcNow.ToUnixTimeSeconds(), "Admin Adjustment", result2, treasury, clan.Treasury, "clan_givecoins"); WarfareManager.Instance.MarkDirty(); WarfareRPC.Instance?.SyncAllDataToClients(); args.Context.AddString($"{clan.Name} treasury is now {clan.Treasury}."); } } }; <>c.<>9__1_3 = val4; obj8 = (object)val4; } _003C_003Ec._003C_003E9__0_3 = (ConsoleEvent)obj8; obj7 = obj8; } new ConsoleCommand("clan_givecoins", "clan_givecoins — add coins to a clan treasury (admin)", (ConsoleEvent)obj7, true, false, false, false, false, (ConsoleOptionsFetcher)null, false, false, false); } } [Serializable] public sealed class ClanMapDrawingFile { public int FormatVersion = 1; public long WorldUID; public List Clans = new List(); } [Serializable] public sealed class ClanMapDrawingBucket { public int ClanID; public long Revision; public List Drawings = new List(); } public static class ClanMapDrawingStore { private static readonly object Gate = new object(); private static ClanMapDrawingFile _data; private static long _world = long.MinValue; private static string Dir => Path.Combine(Paths.ConfigPath, "FortecaClanWarfare", "ClanMapDrawings"); private static string PathFor(long w) { return Path.Combine(Dir, "ClanMapDrawings_" + w + ".json"); } private static long Now() { return DateTimeOffset.UtcNow.ToUnixTimeSeconds(); } public static ClanMapDrawingSnapshot Snapshot(long w, int c) { lock (Gate) { Ensure(w); ClanMapDrawingBucket clanMapDrawingBucket = Bucket(c); return new ClanMapDrawingSnapshot { WorldUID = w, ClanID = c, DrawingRevision = clanMapDrawingBucket.Revision, ServerTimestampUtc = Now(), Drawings = clanMapDrawingBucket.Drawings.Select(ClanMapDrawingDto.From).ToList() }; } } public static bool Create(long w, int c, long actor, string name, ClanMapDrawingMutationRequest r, out string result) { lock (Gate) { Ensure(w); result = Validate(r, id: false); if (result != null) { return false; } ClanMapDrawingBucket clanMapDrawingBucket = Bucket(c); if (clanMapDrawingBucket.Drawings.Count >= 100) { result = "Clan drawing limit reached."; return false; } if (Total(clanMapDrawingBucket) + r.Points.Count > 20000) { result = "Clan drawing point limit reached."; return false; } long num = Now(); ClanMapDrawing clanMapDrawing = new ClanMapDrawing { DrawingID = Guid.NewGuid().ToString("N"), WorldUID = w, ClanID = c, CreatedByPlayerID = actor, CreatedByName = Clean(name, 64), CreatedUtc = num, UpdatedByPlayerID = actor, UpdatedByName = Clean(name, 64), UpdatedUtc = num, Revision = 1, DrawingType = (ClanMapDrawingType)r.DrawingType, ColorIndex = r.ColorIndex, Thickness = r.Thickness, IsImportant = r.IsImportant, Title = Clean(r.Title, 48), Points = new List(r.Points) }; clanMapDrawingBucket.Drawings.Add(clanMapDrawing); clanMapDrawingBucket.Revision++; if (!Save(w)) { clanMapDrawingBucket.Drawings.Remove(clanMapDrawing); clanMapDrawingBucket.Revision--; result = "Clan drawing storage save failed."; return false; } result = clanMapDrawing.DrawingID; return true; } } public static bool Update(long w, int c, long actor, string name, ClanMapDrawingMutationRequest r, out string result, out bool conflict) { lock (Gate) { Ensure(w); conflict = false; result = Validate(r, id: true); if (result != null) { return false; } ClanMapDrawingBucket clanMapDrawingBucket = Bucket(c); ClanMapDrawing clanMapDrawing = clanMapDrawingBucket.Drawings.FirstOrDefault((ClanMapDrawing x) => x.DrawingID == r.DrawingID); if (clanMapDrawing == null || clanMapDrawing.WorldUID != w || clanMapDrawing.ClanID != c) { result = "Clan drawing not found."; return false; } if (clanMapDrawing.Revision != r.ExpectedRevision) { conflict = true; result = "This clan drawing was changed by another officer. Refresh and try again."; return false; } if (Total(clanMapDrawingBucket) - clanMapDrawing.Points.Count + r.Points.Count > 20000) { result = "Clan drawing point limit reached."; return false; } ClanMapDrawing a = Clone(clanMapDrawing); clanMapDrawing.DrawingType = (ClanMapDrawingType)r.DrawingType; clanMapDrawing.ColorIndex = r.ColorIndex; clanMapDrawing.Thickness = r.Thickness; clanMapDrawing.IsImportant = r.IsImportant; clanMapDrawing.Title = Clean(r.Title, 48); clanMapDrawing.Points = new List(r.Points); clanMapDrawing.UpdatedByPlayerID = actor; clanMapDrawing.UpdatedByName = Clean(name, 64); clanMapDrawing.UpdatedUtc = Now(); clanMapDrawing.Revision++; clanMapDrawingBucket.Revision++; if (!Save(w)) { Copy(a, clanMapDrawing); clanMapDrawingBucket.Revision--; result = "Clan drawing storage save failed."; return false; } result = clanMapDrawing.DrawingID; return true; } } public static bool Delete(long w, int c, string id, int rev, out string result, out bool conflict) { lock (Gate) { Ensure(w); conflict = false; ClanMapDrawingBucket clanMapDrawingBucket = Bucket(c); ClanMapDrawing clanMapDrawing = clanMapDrawingBucket.Drawings.FirstOrDefault((ClanMapDrawing x) => x.DrawingID == id); if (clanMapDrawing == null || clanMapDrawing.WorldUID != w || clanMapDrawing.ClanID != c) { result = "Clan drawing not found."; return false; } if (clanMapDrawing.Revision != rev) { conflict = true; result = "This clan drawing was changed by another officer. Refresh and try again."; return false; } int index = clanMapDrawingBucket.Drawings.IndexOf(clanMapDrawing); clanMapDrawingBucket.Drawings.RemoveAt(index); clanMapDrawingBucket.Revision++; if (!Save(w)) { clanMapDrawingBucket.Drawings.Insert(index, clanMapDrawing); clanMapDrawingBucket.Revision--; result = "Clan drawing storage save failed."; return false; } result = id; return true; } } public static bool Clear(long w, int c, out string error) { lock (Gate) { Ensure(w); ClanMapDrawingBucket clanMapDrawingBucket = Bucket(c); List drawings = clanMapDrawingBucket.Drawings; long revision = clanMapDrawingBucket.Revision; clanMapDrawingBucket.Drawings = new List(); clanMapDrawingBucket.Revision++; if (!Save(w)) { clanMapDrawingBucket.Drawings = drawings; clanMapDrawingBucket.Revision = revision; error = "Clan drawing storage save failed."; return false; } error = ""; return true; } } private static string Validate(ClanMapDrawingMutationRequest r, bool id) { if (r == null || r.ProtocolVersion != 5) { return "Clan Map protocol mismatch."; } if (id && (string.IsNullOrEmpty(r.DrawingID) || r.DrawingID.Length > 64)) { return "Invalid drawing ID."; } if (!ClanMapDrawingProtocol.ValidType(r.DrawingType) || r.ColorIndex < 0 || r.ColorIndex >= 8 || r.Thickness < 0 || r.Thickness >= 3) { return "Invalid drawing style."; } if (string.IsNullOrWhiteSpace(r.Title) || r.Title.Length > 48) { return "Drawing title is required and must be at most 48 characters."; } int num = r.Points?.Count ?? 0; if (num < 2 || num > 512) { return "Invalid drawing point count."; } foreach (ClanMapDrawingPoint point in r.Points) { if (point == null) { return "Invalid drawing point."; } float normalizedX = point.NormalizedX; float normalizedY = point.NormalizedY; if (normalizedX * normalizedX + normalizedY * normalizedY > 1.0001f) { return "Drawing point outside world disc."; } } return null; } private static string Clean(string s, int n) { s = (s ?? "").Replace('\r', ' ').Replace('\n', ' ').Trim(); if (s.Length <= n) { return s; } return s.Substring(0, n); } private static int Total(ClanMapDrawingBucket b) { return b.Drawings.Sum((ClanMapDrawing x) => x.Points?.Count ?? 0); } private static ClanMapDrawingBucket Bucket(int c) { ClanMapDrawingBucket clanMapDrawingBucket = _data.Clans.FirstOrDefault((ClanMapDrawingBucket x) => x.ClanID == c); if (clanMapDrawingBucket == null) { clanMapDrawingBucket = new ClanMapDrawingBucket { ClanID = c }; _data.Clans.Add(clanMapDrawingBucket); } return clanMapDrawingBucket; } private static void Ensure(long w) { //IL_0049: Unknown result type (might be due to invalid IL or missing references) if (_data != null && _world == w) { return; } _world = w; _data = new ClanMapDrawingFile { WorldUID = w }; string path = PathFor(w); if (!File.Exists(path)) { return; } try { using (FileStream fileStream = File.OpenRead(path)) { ClanMapDrawingFile clanMapDrawingFile = (ClanMapDrawingFile)((XmlObjectSerializer)new DataContractJsonSerializer(typeof(ClanMapDrawingFile))).ReadObject((Stream)fileStream); if (clanMapDrawingFile != null && clanMapDrawingFile.WorldUID == w) { _data = clanMapDrawingFile; } } Sanitize(w); Plugin.Log.LogInfo((object)("[FCW ClanDrawings] Loaded world=" + w + " clans=" + _data.Clans.Count)); } catch (Exception ex) { Plugin.Log.LogWarning((object)("[FCW ClanDrawings] Load failed world=" + w + " error=" + ex.Message)); _data = new ClanMapDrawingFile { WorldUID = w }; } } private static void Sanitize(long w) { if (_data.Clans == null) { _data.Clans = new List(); } foreach (ClanMapDrawingBucket item in _data.Clans.ToList()) { if (item == null || item.ClanID <= 0) { _data.Clans.Remove(item); continue; } if (item.Drawings == null) { item.Drawings = new List(); } List list = new List(); int num = 0; foreach (ClanMapDrawing drawing in item.Drawings) { ClanMapDrawingMutationRequest r = ((drawing == null) ? null : new ClanMapDrawingMutationRequest { ProtocolVersion = 5, DrawingID = drawing.DrawingID, ExpectedRevision = drawing.Revision, DrawingType = (int)drawing.DrawingType, ColorIndex = drawing.ColorIndex, Thickness = drawing.Thickness, IsImportant = drawing.IsImportant, Title = drawing.Title, Points = drawing.Points }); if (drawing != null && drawing.WorldUID == w && drawing.ClanID == item.ClanID && Validate(r, id: true) == null && list.Count < 100 && num + drawing.Points.Count <= 20000) { list.Add(drawing); num += drawing.Points.Count; } else { Plugin.Log.LogWarning((object)("[FCW ClanDrawings] Skipped malformed drawing clan=" + item.ClanID)); } } item.Drawings = list; } } private static bool Save(long w) { //IL_0032: Unknown result type (might be due to invalid IL or missing references) try { Directory.CreateDirectory(Dir); string text = PathFor(w); string text2 = text + ".tmp"; using (FileStream fileStream = new FileStream(text2, FileMode.Create, FileAccess.Write, FileShare.None)) { ((XmlObjectSerializer)new DataContractJsonSerializer(typeof(ClanMapDrawingFile))).WriteObject((Stream)fileStream, (object)_data); fileStream.Flush(flushToDisk: true); } if (File.Exists(text)) { try { File.Replace(text2, text, null); } catch (PlatformNotSupportedException) { File.Delete(text); File.Move(text2, text); } } else { File.Move(text2, text); } return true; } catch (Exception ex2) { Plugin.Log.LogError((object)("[FCW ClanDrawings] Atomic save failed world=" + w + " error=" + ex2.Message)); return false; } } private static ClanMapDrawing Clone(ClanMapDrawing d) { ClanMapDrawing clanMapDrawing = new ClanMapDrawing(); Copy(d, clanMapDrawing); return clanMapDrawing; } private static void Copy(ClanMapDrawing a, ClanMapDrawing b) { b.DrawingID = a.DrawingID; b.WorldUID = a.WorldUID; b.ClanID = a.ClanID; b.CreatedByPlayerID = a.CreatedByPlayerID; b.CreatedByName = a.CreatedByName; b.CreatedUtc = a.CreatedUtc; b.UpdatedByPlayerID = a.UpdatedByPlayerID; b.UpdatedByName = a.UpdatedByName; b.UpdatedUtc = a.UpdatedUtc; b.Revision = a.Revision; b.DrawingType = a.DrawingType; b.ColorIndex = a.ColorIndex; b.Thickness = a.Thickness; b.IsImportant = a.IsImportant; b.Title = a.Title; b.Points = new List(a.Points); } } public static class ClanMapGeometryService { public const int AlgorithmVersion = 1; public const int Samples = 90; private static readonly Dictionary> Cache = new Dictionary>(); private static long _cachedWorld; public static bool TryGet(long worldUid, string territoryId, Vector2i regionId, out List points) { //IL_0084: Unknown result type (might be due to invalid IL or missing references) points = null; if (VoronoiGrid.Instance == null || string.IsNullOrEmpty(territoryId)) { return false; } if (_cachedWorld != worldUid) { Cache.Clear(); _cachedWorld = worldUid; } string key = worldUid + ":" + 1 + ":" + territoryId; if (Cache.TryGetValue(key, out points)) { if (points != null) { return points.Count >= 3; } return false; } List list = Build(regionId); if (list.Count < 3) { return false; } Cache[key] = list; points = list; return true; } private static List Build(Vector2i regionId) { //IL_000d: Unknown result type (might be due to invalid IL or missing references) //IL_000e: Unknown result type (might be due to invalid IL or missing references) //IL_0013: Unknown result type (might be due to invalid IL or missing references) //IL_001e: Unknown result type (might be due to invalid IL or missing references) //IL_006f: Unknown result type (might be due to invalid IL or missing references) //IL_0080: Unknown result type (might be due to invalid IL or missing references) //IL_008c: Unknown result type (might be due to invalid IL or missing references) //IL_0091: Unknown result type (might be due to invalid IL or missing references) //IL_0096: 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_009f: 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_015e: Unknown result type (might be due to invalid IL or missing references) //IL_00a7: Unknown result type (might be due to invalid IL or missing references) //IL_00ae: Unknown result type (might be due to invalid IL or missing references) //IL_00d7: Unknown result type (might be due to invalid IL or missing references) //IL_00e8: Unknown result type (might be due to invalid IL or missing references) //IL_00f4: Unknown result type (might be due to invalid IL or missing references) //IL_00f9: Unknown result type (might be due to invalid IL or missing references) //IL_00fe: Unknown result type (might be due to invalid IL or missing references) //IL_0100: Unknown result type (might be due to invalid IL or missing references) //IL_0107: Unknown result type (might be due to invalid IL or missing references) //IL_010f: Unknown result type (might be due to invalid IL or missing references) //IL_0116: Unknown result type (might be due to invalid IL or missing references) List list = new List(90); Vector3 regionCenter = VoronoiGrid.Instance.GetRegionCenter(regionId); float num = Mathf.Max(2000f, VoronoiGrid.Instance.GetApproximateRegionRadius(regionId) * 4f); for (int i = 0; i < 90; i++) { float num2 = (float)Math.PI * 2f * (float)i / 90f; float num3 = Mathf.Cos(num2); float num4 = Mathf.Sin(num2); float num5 = 0f; float num6 = num; for (float num7 = 30f; num7 < num; num7 += 30f) { Vector2i region = VoronoiGrid.Instance.GetRegion(new Vector3(regionCenter.x + num3 * num7, 0f, regionCenter.z + num4 * num7)); if (region.x == regionId.x && region.y == regionId.y) { num5 = num7; continue; } num6 = num7; for (int j = 0; j < 6; j++) { float num8 = (num5 + num6) * 0.5f; Vector2i region2 = VoronoiGrid.Instance.GetRegion(new Vector3(regionCenter.x + num3 * num8, 0f, regionCenter.z + num4 * num8)); if (region2.x == regionId.x && region2.y == regionId.y) { num5 = num8; } else { num6 = num8; } } break; } float num9 = regionCenter.x + num3 * ((num5 + num6) * 0.5f); float num10 = regionCenter.z + num4 * ((num5 + num6) * 0.5f); float num11 = Mathf.Sqrt(num9 * num9 + num10 * num10); if (num11 > 10500f) { float num12 = 10500f / num11; num9 *= num12; num10 *= num12; } list.Add(ClanMapGeometryPoint.FromNormalized(num9 / 10500f, num10 / 10500f)); } return list; } } public sealed class ClanMapAccessContext { public long SenderPeerID; public long PlayerID; public PlayerClanInfo Player; public Clan Clan; public long WorldUID; } public static class ClanMapPermissions { public static bool TryResolve(long senderPeerId, out ClanMapAccessContext context, out string denialReason) { //IL_000f: Unknown result type (might be due to invalid IL or missing references) //IL_001a: Expected O, but got Unknown context = null; denialReason = "Clan Map access denied."; try { if ((Object)ZNet.instance == (Object)null || !ZNet.instance.IsServer() || WarfareManager.Instance?.Data == null) { return false; } long num = ResolveSenderPlayerId(senderPeerId); if (num == 0L) { return false; } PlayerClanInfo player = WarfareManager.Instance.Data.GetPlayer(num); if (player == null || player.ClanId == 0) { return false; } Clan clan = WarfareManager.Instance.Data.GetClan(player.ClanId); if (clan == null) { return false; } context = new ClanMapAccessContext { SenderPeerID = senderPeerId, PlayerID = num, Player = player, Clan = clan, WorldUID = ZNet.instance.GetWorldUID() }; return true; } catch (Exception ex) { Plugin.Log.LogWarning((object)("[FCW ClanMap] Sender authorization failed peer=" + senderPeerId + " error=" + ex.Message)); return false; } } public static bool CanViewClanMap(ClanMapAccessContext context, int requestedClanId) { if (context?.Player != null && context.Clan != null && context.Player.ClanId != 0 && context.Player.ClanId == requestedClanId) { return context.Clan.ClanId == requestedClanId; } return false; } public static bool CanEditClanMap(ClanMapAccessContext context, int requestedClanId) { if (CanViewClanMap(context, requestedClanId)) { return context.Player.Rank >= ClanRank.Officer; } return false; } public static bool CanClearClanMap(ClanMapAccessContext context, int requestedClanId) { return CanEditClanMap(context, requestedClanId); } public static bool CanViewClanPins(ClanMapAccessContext context, int clanId) { return CanViewClanMap(context, clanId); } public static bool CanCreateClanPin(ClanMapAccessContext context, int clanId) { return CanEditClanMap(context, clanId); } public static bool CanEditClanPin(ClanMapAccessContext context, int clanId) { return CanEditClanMap(context, clanId); } public static bool CanDeleteClanPin(ClanMapAccessContext context, int clanId) { return CanEditClanMap(context, clanId); } public static bool CanClearClanPins(ClanMapAccessContext context, int clanId) { if (CanViewClanMap(context, clanId)) { return context.Player.Rank == ClanRank.Jarl; } return false; } public static bool CanViewClanDrawings(ClanMapAccessContext context, int clanId) { return CanViewClanMap(context, clanId); } public static bool CanCreateClanDrawing(ClanMapAccessContext context, int clanId) { return CanEditClanMap(context, clanId); } public static bool CanEditClanDrawing(ClanMapAccessContext context, int clanId) { return CanEditClanMap(context, clanId); } public static bool CanDeleteClanDrawing(ClanMapAccessContext context, int clanId) { return CanEditClanMap(context, clanId); } public static bool CanClearClanDrawings(ClanMapAccessContext context, int clanId) { if (CanViewClanMap(context, clanId)) { return context.Player.Rank == ClanRank.Jarl; } return false; } private static long ResolveSenderPlayerId(long sender) { //IL_0019: Unknown result type (might be due to invalid IL or missing references) //IL_0024: Expected O, but got Unknown //IL_0093: Unknown result type (might be due to invalid IL or missing references) //IL_00e8: Unknown result type (might be due to invalid IL or missing references) //IL_00ee: Unknown result type (might be due to invalid IL or missing references) if (ZNet.instance.IsServer() && sender == ZNet.GetUID() && (Object)Player.m_localPlayer != (Object)null) { return Player.m_localPlayer.GetPlayerID(); } ZNetPeer val = ZNet.instance.GetPeer(sender); if (val == null) { foreach (ZNetPeer peer in ZNet.instance.GetPeers()) { if (peer != null && peer.m_uid == sender) { val = peer; break; } } } if (val != null && ZDOMan.instance != null) { ZDO zDO = ZDOMan.instance.GetZDO(val.m_characterID); long num = ((zDO != null) ? zDO.GetLong(ZDOVars.s_playerID, 0L) : 0); if (num != 0L) { return num; } } Player[] array = Object.FindObjectsOfType(); foreach (Player val2 in array) { ZNetView component = ((Component)val2).GetComponent(); ZDO val3 = ((component != null) ? component.GetZDO() : null); if (val != null && val3 != null && val3.m_uid == val.m_characterID) { return val2.GetPlayerID(); } } return 0L; } } [Serializable] public sealed class ClanMapPinFile { public int FormatVersion = 1; public long WorldUID; public List Clans = new List(); } [Serializable] public sealed class ClanMapPinClanBucket { public int ClanID; public long Revision; public List Pins = new List(); } public static class ClanMapPinStore { private static readonly object Gate = new object(); private static ClanMapPinFile _data; private static long _world = long.MinValue; private static string DirectoryPath => Path.Combine(Paths.ConfigPath, "FortecaClanWarfare", "ClanMapPins"); private static string PathFor(long world) { return Path.Combine(DirectoryPath, "ClanMapPins_" + world + ".json"); } public static ClanMapPinSnapshot Snapshot(long world, int clan) { lock (Gate) { Ensure(world); ClanMapPinClanBucket clanMapPinClanBucket = Bucket(clan, create: true); return new ClanMapPinSnapshot { WorldUID = world, ClanID = clan, PinRevision = clanMapPinClanBucket.Revision, ServerTimestampUtc = Now(), Pins = clanMapPinClanBucket.Pins.Select(ClanMapPinDto.FromModel).Take(200).ToList() }; } } public static bool Create(long world, int clan, long actor, string name, ClanMapPinMutationRequest r, out string error) { lock (Gate) { Ensure(world); error = Validate(r, requireId: false); if (error != null) { return false; } ClanMapPinClanBucket clanMapPinClanBucket = Bucket(clan, create: true); if (clanMapPinClanBucket.Pins.Count >= 200) { error = "Clan pin limit reached."; return false; } long num = Now(); ClanMapPin clanMapPin = new ClanMapPin { PinID = Guid.NewGuid().ToString("N"), WorldUID = world, ClanID = clan, CreatedByPlayerID = actor, CreatedByName = SafeName(name), CreatedUtc = num, UpdatedByPlayerID = actor, UpdatedByName = SafeName(name), UpdatedUtc = num, Revision = 1, NormalizedX = r.NormalizedX, NormalizedY = r.NormalizedY, PinType = (ClanMapPinType)r.PinType, Title = Clean(r.Title, 48), Description = Clean(r.Description, 256), ColorIndex = r.ColorIndex, IsImportant = r.IsImportant }; clanMapPinClanBucket.Pins.Add(clanMapPin); clanMapPinClanBucket.Revision++; if (!Save(world)) { clanMapPinClanBucket.Pins.Remove(clanMapPin); clanMapPinClanBucket.Revision--; error = "Clan pin storage save failed."; return false; } error = clanMapPin.PinID; return true; } } public static bool Update(long world, int clan, long actor, string name, ClanMapPinMutationRequest r, out string error, out bool conflict) { lock (Gate) { Ensure(world); conflict = false; error = Validate(r, requireId: true); if (error != null) { return false; } ClanMapPinClanBucket clanMapPinClanBucket = Bucket(clan, create: true); ClanMapPin clanMapPin = clanMapPinClanBucket.Pins.FirstOrDefault((ClanMapPin x) => x.PinID == r.PinID); if (clanMapPin == null) { error = "Clan pin not found."; return false; } if (clanMapPin.WorldUID != world || clanMapPin.ClanID != clan) { error = "Clan pin ownership mismatch."; return false; } if (clanMapPin.Revision != r.ExpectedRevision) { conflict = true; error = "This clan pin was changed by another officer. Refresh and try again."; return false; } ClanMapPin a = Clone(clanMapPin); clanMapPin.NormalizedX = r.NormalizedX; clanMapPin.NormalizedY = r.NormalizedY; clanMapPin.PinType = (ClanMapPinType)r.PinType; clanMapPin.Title = Clean(r.Title, 48); clanMapPin.Description = Clean(r.Description, 256); clanMapPin.ColorIndex = r.ColorIndex; clanMapPin.IsImportant = r.IsImportant; clanMapPin.UpdatedByPlayerID = actor; clanMapPin.UpdatedByName = SafeName(name); clanMapPin.UpdatedUtc = Now(); clanMapPin.Revision++; clanMapPinClanBucket.Revision++; if (!Save(world)) { Copy(a, clanMapPin); clanMapPinClanBucket.Revision--; error = "Clan pin storage save failed."; return false; } error = clanMapPin.PinID; return true; } } public static bool Delete(long world, int clan, string id, int expected, out string error, out bool conflict) { lock (Gate) { Ensure(world); conflict = false; ClanMapPinClanBucket clanMapPinClanBucket = Bucket(clan, create: true); ClanMapPin clanMapPin = clanMapPinClanBucket.Pins.FirstOrDefault((ClanMapPin x) => x.PinID == id); if (clanMapPin == null) { error = "Clan pin not found."; return false; } if (clanMapPin.WorldUID != world || clanMapPin.ClanID != clan) { error = "Clan pin ownership mismatch."; return false; } if (clanMapPin.Revision != expected) { conflict = true; error = "This clan pin was changed by another officer. Refresh and try again."; return false; } int index = clanMapPinClanBucket.Pins.IndexOf(clanMapPin); clanMapPinClanBucket.Pins.RemoveAt(index); clanMapPinClanBucket.Revision++; if (!Save(world)) { clanMapPinClanBucket.Pins.Insert(index, clanMapPin); clanMapPinClanBucket.Revision--; error = "Clan pin storage save failed."; return false; } error = id; return true; } } public static bool Clear(long world, int clan, out string error) { lock (Gate) { Ensure(world); ClanMapPinClanBucket clanMapPinClanBucket = Bucket(clan, create: true); List pins = new List(clanMapPinClanBucket.Pins); long revision = clanMapPinClanBucket.Revision; clanMapPinClanBucket.Pins.Clear(); clanMapPinClanBucket.Revision++; if (!Save(world)) { clanMapPinClanBucket.Pins = pins; clanMapPinClanBucket.Revision = revision; error = "Clan pin storage save failed."; return false; } error = ""; return true; } } private static string Validate(ClanMapPinMutationRequest r, bool requireId) { if (r == null || r.ProtocolVersion != 5) { return "Clan Map protocol mismatch."; } if (requireId && (string.IsNullOrEmpty(r.PinID) || r.PinID.Length > 64)) { return "Invalid clan pin ID."; } if (float.IsNaN(r.NormalizedX) || float.IsNaN(r.NormalizedY) || float.IsInfinity(r.NormalizedX) || float.IsInfinity(r.NormalizedY) || r.NormalizedX * r.NormalizedX + r.NormalizedY * r.NormalizedY > 1f) { return "Invalid clan pin position."; } if (!ClanMapPinProtocol.IsValidType(r.PinType)) { return "Invalid clan pin type."; } if (r.ColorIndex < 0 || r.ColorIndex >= 8) { return "Invalid clan pin color."; } if (string.IsNullOrWhiteSpace(Clean(r.Title, 48))) { return "Clan pin title is required."; } if ((r.Title ?? "").Length > 48 || (r.Description ?? "").Length > 256) { return "Clan pin text is too long."; } return null; } private static string Clean(string s, int max) { s = (s ?? "").Replace('\r', ' ').Replace('\n', ' ').Trim(); if (s.Length <= max) { return s; } return s.Substring(0, max); } private static string SafeName(string s) { return Clean(s, 64); } private static long Now() { return DateTimeOffset.UtcNow.ToUnixTimeSeconds(); } private static ClanMapPinClanBucket Bucket(int clan, bool create) { ClanMapPinClanBucket clanMapPinClanBucket = _data.Clans.FirstOrDefault((ClanMapPinClanBucket x) => x.ClanID == clan); if (clanMapPinClanBucket == null && create) { clanMapPinClanBucket = new ClanMapPinClanBucket { ClanID = clan }; _data.Clans.Add(clanMapPinClanBucket); } return clanMapPinClanBucket; } private static void Ensure(long world) { //IL_0049: Unknown result type (might be due to invalid IL or missing references) if (_data != null && _world == world) { return; } _world = world; _data = new ClanMapPinFile { WorldUID = world }; string path = PathFor(world); if (!File.Exists(path)) { return; } try { using (FileStream fileStream = File.OpenRead(path)) { ClanMapPinFile clanMapPinFile = (ClanMapPinFile)((XmlObjectSerializer)new DataContractJsonSerializer(typeof(ClanMapPinFile))).ReadObject((Stream)fileStream); if (clanMapPinFile != null && clanMapPinFile.WorldUID == world) { _data = clanMapPinFile; } } Sanitize(world); Plugin.Log.LogInfo((object)("[FCW ClanPins] Loaded world=" + world + " clans=" + _data.Clans.Count)); } catch (Exception ex) { Plugin.Log.LogWarning((object)("[FCW ClanPins] Storage load failed world=" + world + " error=" + ex.Message)); _data = new ClanMapPinFile { WorldUID = world }; } } private static void Sanitize(long world) { if (_data.Clans == null) { _data.Clans = new List(); } foreach (ClanMapPinClanBucket item in _data.Clans.ToList()) { if (item == null || item.ClanID <= 0) { _data.Clans.Remove(item); continue; } if (item.Pins == null) { item.Pins = new List(); } List list = new List(); foreach (ClanMapPin pin in item.Pins) { ClanMapPinMutationRequest clanMapPinMutationRequest = ((pin == null) ? null : new ClanMapPinMutationRequest { ProtocolVersion = 5, PinID = pin.PinID, ExpectedRevision = pin.Revision, NormalizedX = pin.NormalizedX, NormalizedY = pin.NormalizedY, PinType = (int)pin.PinType, Title = pin.Title, Description = pin.Description, ColorIndex = pin.ColorIndex, IsImportant = pin.IsImportant }); if (pin != null && !ClanMapPinProtocol.IsValidType((int)pin.PinType)) { pin.PinType = ClanMapPinType.Default; clanMapPinMutationRequest.PinType = 9; } if (pin != null && pin.WorldUID == world && pin.ClanID == item.ClanID && Validate(clanMapPinMutationRequest, requireId: true) == null && list.Count < 200) { list.Add(pin); } else { Plugin.Log.LogWarning((object)("[FCW ClanPins] Skipped malformed persisted pin clan=" + item.ClanID)); } } item.Pins = list; } } private static bool Save(long world) { //IL_0032: Unknown result type (might be due to invalid IL or missing references) try { Directory.CreateDirectory(DirectoryPath); string text = PathFor(world); string text2 = text + ".tmp"; using (FileStream fileStream = new FileStream(text2, FileMode.Create, FileAccess.Write, FileShare.None)) { ((XmlObjectSerializer)new DataContractJsonSerializer(typeof(ClanMapPinFile))).WriteObject((Stream)fileStream, (object)_data); fileStream.Flush(flushToDisk: true); } if (File.Exists(text)) { try { File.Replace(text2, text, null); } catch (PlatformNotSupportedException) { File.Delete(text); File.Move(text2, text); } } else { File.Move(text2, text); } return true; } catch (Exception ex2) { Plugin.Log.LogError((object)("[FCW ClanPins] Atomic save failed world=" + world + " error=" + ex2.Message)); return false; } } private static ClanMapPin Clone(ClanMapPin p) { ClanMapPin clanMapPin = new ClanMapPin(); Copy(p, clanMapPin); return clanMapPin; } private static void Copy(ClanMapPin a, ClanMapPin b) { b.PinID = a.PinID; b.WorldUID = a.WorldUID; b.ClanID = a.ClanID; b.CreatedByPlayerID = a.CreatedByPlayerID; b.CreatedByName = a.CreatedByName; b.CreatedUtc = a.CreatedUtc; b.UpdatedByPlayerID = a.UpdatedByPlayerID; b.UpdatedByName = a.UpdatedByName; b.UpdatedUtc = a.UpdatedUtc; b.Revision = a.Revision; b.NormalizedX = a.NormalizedX; b.NormalizedY = a.NormalizedY; b.PinType = a.PinType; b.Title = a.Title; b.Description = a.Description; b.ColorIndex = a.ColorIndex; b.IsImportant = a.IsImportant; } } public static class ClanMapProjectionService { public const float WorldRadius = 10500f; public static List ProjectMarkers(ClanMapAccessContext access) { //IL_00b7: Unknown result type (might be due to invalid IL or missing references) //IL_00c4: Unknown result type (might be due to invalid IL or missing references) //IL_00d1: Unknown result type (might be due to invalid IL or missing references) //IL_00f3: Unknown result type (might be due to invalid IL or missing references) List list = new List(); WarfareData warfareData = WarfareManager.Instance?.Data; if (access?.Player == null || warfareData?.Territories == null) { return list; } foreach (KeyValuePair territory in warfareData.Territories) { ClanTerritory value = territory.Value; if (value != null && value.OwnerClanId == access.Player.ClanId && TryGeometry(value, out var center, out var radius)) { list.Add(new ClanMapTerritoryMarkerDto { TerritoryID = (territory.Key ?? ""), DisplayName = Safe(value.DisplayName, 64), WorldX = center.x, WorldZ = center.z, NormalizedX = Mathf.Clamp(center.x / 10500f, -1f, 1f), NormalizedY = Mathf.Clamp(center.z / 10500f, -1f, 1f), ApproximateRadiusWorld = radius, TerritoryType = (int)value.Type, IsCapital = value.IsCapital, BiomeName = Safe(value.BiomeName, 40), SizeLevel = Mathf.Max(1, value.SizeLevel), State = (int)value.State, RotCount = Mathf.Clamp(value.RotCount, 0, 4), IsRotted = value.IsRotted }); } } return list; } public static List ProjectPublicTerritories(ClanMapAccessContext access) { //IL_00aa: Unknown result type (might be due to invalid IL or missing references) //IL_0256: Unknown result type (might be due to invalid IL or missing references) //IL_0263: 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_0283: Unknown result type (might be due to invalid IL or missing references) List list = new List(); WarfareData warfareData = WarfareManager.Instance?.Data; if (access?.Player == null || warfareData?.Territories == null) { return list; } int num = 0; int num2 = 65536; foreach (KeyValuePair territory in warfareData.Territories) { ClanTerritory value = territory.Value; if (value == null || value.OwnerClanId == 0) { continue; } Clan clan = warfareData.GetClan(value.OwnerClanId); if (clan == null || !TryGeometry(value, out var center, out var _)) { continue; } if (!ClanMapGeometryService.TryGet(access.WorldUID, territory.Key, value.GridPosition, out var points)) { Plugin.Log.LogWarning((object)("[FCW ClanMap] Geometry skipped territory=" + territory.Key)); continue; } int num3 = points.Count * 8 + 256 + (territory.Key?.Length ?? 0) * 4 + (value.DisplayName?.Length ?? 0) * 4 + (clan.Name?.Length ?? 0) * 4; if (points.Count > 256 || num + points.Count > 50000 || num2 + num3 > 524288) { break; } num += points.Count; num2 += num3; list.Add(new ClanMapPublicTerritoryDto { TerritoryID = territory.Key, DisplayName = Safe(value.DisplayName, 64), OwnerClanID = clan.ClanId, OwnerClanName = Safe(clan.Name, 64), OwnerClanTag = Safe(clan.Tag, 20), OwnerClanLogoKey = Safe(clan.ClanEmblemName, 80), OwnerClanColorR = Mathf.Clamp01(clan.ColorR), OwnerClanColorG = Mathf.Clamp01(clan.ColorG), OwnerClanColorB = Mathf.Clamp01(clan.ColorB), TerritoryType = (int)value.Type, IsCapital = value.IsCapital, BiomeName = Safe(value.BiomeName, 40), SizeLevel = Mathf.Max(1, value.SizeLevel), WorldCenterX = center.x, WorldCenterZ = center.z, NormalizedCenterX = center.x / 10500f, NormalizedCenterY = center.z / 10500f, PublicState = (int)value.State, GeometryRevision = 1, GeometryPoints = points }); if (list.Count < 512) { continue; } break; } return list; } public static bool TryProjectPublicDetail(ClanMapAccessContext access, string territoryId, out ClanMapPublicTerritoryDetailDto detail) { //IL_0156: Unknown result type (might be due to invalid IL or missing references) //IL_0162: Unknown result type (might be due to invalid IL or missing references) detail = null; WarfareData warfareData = WarfareManager.Instance?.Data; if (access?.Player == null || warfareData?.Territories == null || string.IsNullOrEmpty(territoryId) || territoryId.Length > 80) { return false; } if (!warfareData.Territories.TryGetValue(territoryId, out var value) || value == null || value.OwnerClanId == 0) { return false; } Clan clan = warfareData.GetClan(value.OwnerClanId); if (clan == null || !TryGeometry(value, out var center, out var _)) { return false; } detail = new ClanMapPublicTerritoryDetailDto { TerritoryID = territoryId, DisplayName = Safe(value.DisplayName, 64), OwnerClanID = clan.ClanId, OwnerClanName = Safe(clan.Name, 64), OwnerClanTag = Safe(clan.Tag, 20), OwnerClanLogoKey = Safe(clan.ClanEmblemName, 80), OwnerClanColorR = Mathf.Clamp01(clan.ColorR), OwnerClanColorG = Mathf.Clamp01(clan.ColorG), OwnerClanColorB = Mathf.Clamp01(clan.ColorB), TerritoryType = (int)value.Type, IsCapital = value.IsCapital, BiomeName = Safe(value.BiomeName, 40), SizeLevel = Mathf.Max(1, value.SizeLevel), WorldX = center.x, WorldZ = center.z, PublicState = (int)value.State, ServerTimestampUtc = DateTimeOffset.UtcNow.ToUnixTimeSeconds(), DetailRevision = warfareData.Version }; return true; } public static bool TryProjectDetail(ClanMapAccessContext access, string territoryId, out ClanMapTerritoryDetailDto detail) { //IL_024d: Unknown result type (might be due to invalid IL or missing references) //IL_0259: Unknown result type (might be due to invalid IL or missing references) detail = null; WarfareData warfareData = WarfareManager.Instance?.Data; if (access?.Player == null || warfareData?.Territories == null || string.IsNullOrEmpty(territoryId) || territoryId.Length > 80) { return false; } if (!warfareData.Territories.TryGetValue(territoryId, out var value) || value == null || value.OwnerClanId != access.Player.ClanId) { return false; } if (!TryGeometry(value, out var center, out var radius)) { return false; } List clanTerritories = warfareData.GetClanTerritories(access.Player.ClanId); EconomyBreakdown economyBreakdown = Economy.CalculateTerritoryEconomy(warfareData, access.Player.ClanId, value, clanTerritories.Count); long num = 0L; if (warfareData.Guards != null) { foreach (TerritoryGuard guard in warfareData.Guards) { if (guard != null && guard.OwnerClanId == access.Player.ClanId && guard.Status == GuardStatus.Active && guard.TerritoryId == territoryId) { num += Math.Max(0L, guard.UpkeepCost); } } } int num2 = 0; int num3 = 0; int num4 = 0; int num5 = 0; if (value.Buildings != null) { foreach (TerritoryBuilding building in value.Buildings) { if (building != null && building.Type != TerritoryBuildingType.None) { num2++; if (!building.Damaged) { num3++; } int num6 = Mathf.Clamp(building.StoredProductionTicks, 0, 4); num4 += num6; if (num6 > 0) { num5++; } } } } long progressiveUpkeep = Economy.CalculateClanEconomy(warfareData, access.Player.ClanId).ProgressiveUpkeep; detail = new ClanMapTerritoryDetailDto { TerritoryID = territoryId, DisplayName = Safe(value.DisplayName, 64), CustomName = Safe(value.CustomName, 64), TerritoryType = (int)value.Type, IsCapital = value.IsCapital, BiomeName = Safe(value.BiomeName, 40), GridX = value.GridX, GridY = value.GridY, WorldX = center.x, WorldZ = center.z, ApproximateRadiusWorld = radius, SizeLevel = Mathf.Max(1, value.SizeLevel), HP = Mathf.Max(0f, value.HP), MaxHP = Mathf.Max(0f, value.MaxHP), RotCount = Mathf.Clamp(value.RotCount, 0, 4), IsRotted = value.IsRotted, State = (int)value.State, BaseIncome = (long)Economy.IncomeBiomeRate(value.BiomeValueTier) * (long)Mathf.Max(1, value.SizeLevel), CorruptionAdjustedIncome = economyBreakdown.Gross, BaseUpkeep = economyBreakdown.TerritoryUpkeep, ProgressiveUpkeep = progressiveUpkeep, GuardUpkeep = num, NetResult = economyBreakdown.Gross - economyBreakdown.TerritoryUpkeep - num, BuildingCount = num2, ActiveBuildingCount = num3, StoredProductionTicks = num4, ProductionReadyCount = num5, ServerTimestampUtc = DateTimeOffset.UtcNow.ToUnixTimeSeconds(), DetailRevision = warfareData.Version }; return true; } private static bool TryGeometry(ClanTerritory territory, out Vector3 center, out float radius) { //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_0025: Unknown result type (might be due to invalid IL or missing references) //IL_002a: Unknown result type (might be due to invalid IL or missing references) //IL_002f: Unknown result type (might be due to invalid IL or missing references) //IL_003b: Unknown result type (might be due to invalid IL or missing references) center = Vector3.zero; radius = 0f; if (territory == null || VoronoiGrid.Instance == null) { return false; } center = VoronoiGrid.Instance.GetRegionCenter(territory.GridPosition); radius = VoronoiGrid.Instance.GetApproximateRegionRadius(territory.GridPosition); if (IsFinite(center.x) && IsFinite(center.z) && IsFinite(radius) && radius > 0f) { return center.x * center.x + center.z * center.z <= 110250000f; } return false; } private static bool IsFinite(float value) { if (!float.IsNaN(value)) { return !float.IsInfinity(value); } return false; } private static string Safe(string value, int max) { if (!string.IsNullOrEmpty(value)) { if (value.Length > max) { return value.Substring(0, max); } return value; } return ""; } } public sealed class EconomyBreakdown { public long Gross; public long TerritoryUpkeep; public long ProgressiveUpkeepBeforeLawspeaker; public long LawspeakerReduction; public long ProgressiveUpkeep; public long GuardUpkeep; public long Net => Gross - TerritoryUpkeep - ProgressiveUpkeep - GuardUpkeep; public void Add(EconomyBreakdown value) { if (value != null) { Gross += value.Gross; TerritoryUpkeep += value.TerritoryUpkeep; ProgressiveUpkeepBeforeLawspeaker += value.ProgressiveUpkeepBeforeLawspeaker; LawspeakerReduction += value.LawspeakerReduction; ProgressiveUpkeep += value.ProgressiveUpkeep; GuardUpkeep += value.GuardUpkeep; } } } public static class Economy { public static void RecordTreasuryTransaction(Clan clan, long timestamp, string type, long amount, long balanceBefore, long balanceAfter, string source, string territoryId = "", string buildingId = "") { if (clan != null && amount != 0L) { if (clan.TreasuryHistory == null) { clan.TreasuryHistory = new List(); } clan.TreasuryHistory.Insert(0, new EconomyTransaction { Timestamp = timestamp, Type = (type ?? ""), Amount = amount, BalanceBefore = balanceBefore, BalanceAfter = balanceAfter, Source = (source ?? ""), TerritoryId = (territoryId ?? ""), BuildingId = (buildingId ?? "") }); if (clan.TreasuryHistory.Count > 100) { clan.TreasuryHistory.RemoveRange(100, clan.TreasuryHistory.Count - 100); } } } public static int BiomeValueTier(Biome biome) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //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_0007: Unknown result type (might be due to invalid IL or missing references) //IL_000a: Invalid comparison between Unknown and I4 //IL_001b: Unknown result type (might be due to invalid IL or missing references) //IL_001e: Invalid comparison between Unknown and I4 //IL_000c: Unknown result type (might be due to invalid IL or missing references) //IL_000e: Invalid comparison between Unknown and I4 //IL_0020: Unknown result type (might be due to invalid IL or missing references) //IL_0023: Invalid comparison between Unknown and I4 //IL_0010: Unknown result type (might be due to invalid IL or missing references) //IL_0012: Invalid comparison between Unknown and I4 //IL_0025: Unknown result type (might be due to invalid IL or missing references) //IL_002b: Invalid comparison between Unknown and I4 //IL_0014: Unknown result type (might be due to invalid IL or missing references) //IL_0017: Invalid comparison between Unknown and I4 Biome val = BiomeUtils.ResolveBiome(biome); if ((int)val <= 16) { if ((int)val == 2 || (int)val == 4) { goto IL_002f; } if ((int)val != 16) { goto IL_002d; } } else { if ((int)val == 32 || (int)val == 64) { goto IL_002f; } if ((int)val != 512) { goto IL_002d; } } return 3; IL_002f: return 2; IL_002d: return 1; } public static string ValueTierName(int tier) { return tier switch { 3 => "High", 2 => "Mid", _ => "Low", }; } public static int IncomeBiomeRate(int valueTier) { return valueTier switch { 3 => ClanConfig.IncomeBiomeHigh?.Value ?? 20, 2 => ClanConfig.IncomeBiomeMid?.Value ?? 10, _ => ClanConfig.IncomeBiomeLow?.Value ?? 5, }; } public static long TerritoryIncome(ClanTerritory t) { if (t == null) { return 0L; } return (long)Mathf.Round((float)(IncomeBiomeRate(t.BiomeValueTier) * Mathf.Max(1, t.SizeLevel)) * RotIncomeMultiplier(t.RotCount)); } public static float RotIncomeMultiplier(int rotCount) { return Mathf.Clamp01(1f - (float)Mathf.Clamp(rotCount, 0, 4) * 0.25f); } public static float GlobalUpkeepMultiplier(int territoryCount) { float num = ClanConfig.GlobalUpkeepBase?.Value ?? 1f; float num2 = ClanConfig.GlobalUpkeepPerTerritory?.Value ?? 0.1f; int num3 = ClanConfig.GlobalUpkeepFreeTerritories?.Value ?? 5; int num4 = Mathf.Max(0, territoryCount - num3); return num + (float)num4 * num2; } public static long TerritoryUpkeep(ClanTerritory t, float globalMultiplier) { if (t == null) { return 0L; } float num = ClanConfig.UpkeepPerSizeLevel?.Value ?? 4f; return (long)Mathf.Round((float)Mathf.Max(1, t.SizeLevel) * num * globalMultiplier); } public static long ApplyBuildingUpkeepReduction(ClanTerritory territory, long upkeep) { if (territory == null || upkeep <= 0 || territory.Buildings == null) { return upkeep; } float num = 0f; foreach (TerritoryBuilding building in territory.Buildings) { if (building != null && !building.Damaged) { if (building.Type == TerritoryBuildingType.Barracks) { num += 0.02f * (float)Mathf.Clamp(building.Level, 1, 5); } else if (building.Type == TerritoryBuildingType.Watchtower) { num += 0.03f * (float)Mathf.Clamp(building.Level, 1, 5); } } } num = Mathf.Clamp(num, 0f, 0.25f); return (long)Mathf.Round((float)upkeep * (1f - num)); } public static long ApplyWardenUpkeepReduction(WarfareData data, int clanId, long upkeep) { if (upkeep <= 0 || data == null || clanId == 0) { return upkeep; } float bestWardenReduction = GetBestWardenReduction(data, clanId); if (bestWardenReduction <= 0f) { return upkeep; } return (long)Mathf.Round((float)upkeep * (1f - bestWardenReduction)); } public static float GetBestWardenReduction(WarfareData data, int clanId) { if (data == null || clanId == 0) { return 0f; } float num = 0f; foreach (PlayerClanInfo value in data.Players.Values) { if (value != null && value.ClanId == clanId) { switch (RoleUtils.GetRoleSlot(value, ClanRole.Warden)) { case RoleSlot.Primary: num = Mathf.Max(num, 0.2f); break; case RoleSlot.Secondary: num = Mathf.Max(num, 0.12f); break; case RoleSlot.Tertiary: num = Mathf.Max(num, 0.08f); break; } } } return num; } public static long NetClanTick(WarfareData data, int clanId) { return CalculateClanEconomy(data, clanId).Net; } public static int ProgressiveTerritoryUpkeepCost(int territoryCount) { ConfigEntry enableProgressiveTerritoryUpkeep = ClanConfig.EnableProgressiveTerritoryUpkeep; if (enableProgressiveTerritoryUpkeep == null || !enableProgressiveTerritoryUpkeep.Value || territoryCount <= 25) { return 0; } int num = Mathf.Clamp(territoryCount - 25, 0, 25); int num2 = Mathf.Clamp(territoryCount - 50, 0, 50); int num3 = Mathf.Max(0, territoryCount - 100); return num + num2 * 2 + num3 * 5; } public static long ApplyLawspeakerProgressiveReduction(WarfareData data, int clanId, long upkeep) { upkeep = Math.Max(0L, upkeep); if (data == null || clanId == 0 || upkeep == 0L) { return upkeep; } float num = 0f; foreach (PlayerClanInfo clanMember in data.GetClanMembers(clanId)) { num = Mathf.Max(num, RoleUtils.GetRoleFactor(clanMember, ClanRole.Lawspeaker)); } float num2 = Mathf.Clamp01(0.2f * num); return Math.Max(0L, (long)Mathf.Round((float)upkeep * (1f - num2))); } private static void SetProgressiveUpkeep(EconomyBreakdown result, WarfareData data, int clanId, long rawUpkeep) { result.ProgressiveUpkeepBeforeLawspeaker = Math.Max(0L, rawUpkeep); result.ProgressiveUpkeep = ApplyLawspeakerProgressiveReduction(data, clanId, result.ProgressiveUpkeepBeforeLawspeaker); result.LawspeakerReduction = result.ProgressiveUpkeepBeforeLawspeaker - result.ProgressiveUpkeep; } public static long OwnerIncomeAfterCorruption(long gross, int rotCount) { int num = Mathf.Clamp(rotCount, 0, 4); long num2 = gross / 4; long num3 = gross % 4; int num4 = 4 - num; return num2 * num4 + Mathf.Min((int)num3, num4); } public static EconomyBreakdown CalculateTerritoryEconomy(WarfareData data, int clanId, ClanTerritory territory, int territoryCount, int ticks = 1, long? confirmedGross = null, bool includeProgressiveUpkeep = false) { EconomyBreakdown economyBreakdown = new EconomyBreakdown(); if (territory == null) { return economyBreakdown; } int num = Mathf.Max(1, ticks); long gross = (long)IncomeBiomeRate(territory.BiomeValueTier) * (long)Mathf.Max(1, territory.SizeLevel) * num; economyBreakdown.Gross = confirmedGross ?? OwnerIncomeAfterCorruption(gross, territory.RotCount); long upkeep = TerritoryUpkeep(territory, GlobalUpkeepMultiplier(territoryCount)); upkeep = ApplyWardenUpkeepReduction(data, clanId, upkeep); upkeep = ApplyBuildingUpkeepReduction(territory, upkeep); economyBreakdown.TerritoryUpkeep = upkeep * num; if (includeProgressiveUpkeep) { SetProgressiveUpkeep(economyBreakdown, data, clanId, (long)ProgressiveTerritoryUpkeepCost(territoryCount) * (long)num); } return economyBreakdown; } public static EconomyBreakdown CalculateTerritoriesEconomy(WarfareData data, int clanId, IList territories) { EconomyBreakdown economyBreakdown = new EconomyBreakdown(); if (territories == null) { return economyBreakdown; } foreach (ClanTerritory territory in territories) { economyBreakdown.Add(CalculateTerritoryEconomy(data, clanId, territory, territories.Count)); } SetProgressiveUpkeep(economyBreakdown, data, clanId, ProgressiveTerritoryUpkeepCost(territories.Count)); return economyBreakdown; } public static EconomyBreakdown CalculateClanEconomy(WarfareData data, int clanId) { EconomyBreakdown economyBreakdown = new EconomyBreakdown(); if (data == null || clanId == 0) { return economyBreakdown; } economyBreakdown.Add(CalculateTerritoriesEconomy(data, clanId, data.GetClanTerritories(clanId))); foreach (TerritoryGuard guard in data.Guards) { if (guard != null && guard.OwnerClanId == clanId && guard.Status == GuardStatus.Active) { economyBreakdown.GuardUpkeep += guard.UpkeepCost; } } return economyBreakdown; } public static string EconomyBand(int territoryCount) { int num = ClanConfig.ProfitThreshold?.Value ?? 20; int num2 = ClanConfig.BalancedThreshold?.Value ?? 40; if (territoryCount <= num) { return "Profit"; } if (territoryCount <= num2) { return "Balanced"; } return "Loss"; } public static long ClanMemberSlotCost(int newMemberCount) { int num = ClanConfig.ClanFreeMemberLimit?.Value ?? 3; if (newMemberCount <= num) { return 0L; } long num2 = ClanConfig.ClanMemberCostBase?.Value ?? 100; int num3 = newMemberCount - num - 1; return num2 * (long)Mathf.Pow(2f, (float)Mathf.Max(0, num3)); } public static long TerritoryUpgradeCost(int currentSizeLevel) { return (long)(ClanConfig.TerritoryUpgradeCostBase?.Value ?? 500) * (long)Mathf.Max(1, currentSizeLevel); } public static int TerritoryUpgradeCoreCost(int currentSizeLevel) { return (ClanConfig.TerritoryUpgradeSurtlingCoreCostBase?.Value ?? 5) * Mathf.Max(1, currentSizeLevel); } public static long VaultUpgradeCost(int currentTier) { return (long)(ClanConfig.VaultUpgradeCostBase?.Value ?? 1000) * (long)Mathf.Max(1, currentTier); } public static int VaultUpgradeCoreCost(int currentTier) { return (ClanConfig.VaultUpgradeSurtlingCoreCostBase?.Value ?? 5) * Mathf.Max(1, currentTier); } public static long TerritoryExpansionCost(int expansionsBought) { return (long)(ClanConfig.TerritoryExpansionCost?.Value ?? 1000) * (long)(expansionsBought + 1); } public static long TerritoryAcquisitionBaseCost(int nextTerritoryNumber) { if (nextTerritoryNumber <= 5) { return 0L; } if (nextTerritoryNumber <= 10) { return 500L; } return (long)Mathf.Clamp((nextTerritoryNumber - 1) / 10 + 1, 2, 10) * 500L; } public static string TerritoryAcquisitionTierName(int nextTerritoryNumber) { if (nextTerritoryNumber <= 5) { return "1-5 Territories"; } if (nextTerritoryNumber <= 10) { return "6-10 Territories"; } int num = (nextTerritoryNumber - 1) / 10 * 10 + 1; return num + "-" + (num + 9) + " Territories"; } public static float ExplorerAcquisitionDiscount(PlayerClanInfo player) { return Mathf.Clamp01((ClanConfig.PathfinderDiscoveryCostReduction?.Value ?? 20f) / 100f * RoleUtils.GetRoleFactor(player, ClanRole.Explorer)); } public static long TerritoryAcquisitionCost(int nextTerritoryNumber, PlayerClanInfo player) { long num = TerritoryAcquisitionBaseCost(nextTerritoryNumber); if (num <= 0) { return 0L; } float num2 = Mathf.Clamp01(ExplorerAcquisitionDiscount(player)); return (long)Mathf.Floor((float)num * (1f - num2)); } public static int TerritoryExpansionCoreCost(int expansionsBought) { return (ClanConfig.TerritoryExpansionSurtlingCoreCost?.Value ?? 10) * (expansionsBought + 1); } public static long RotTotemCost(int totemLevel, int biomeValueTier) { return (long)(ClanConfig.RotTotemCostBase?.Value ?? 100) * (long)Mathf.Max(1, totemLevel) * Mathf.Max(1, biomeValueTier); } public static int TerritoryCap(Clan clan) { int num = ClanConfig.BaseTerritoryCap?.Value ?? 10; int num2 = ClanConfig.TerritoryExpansionBlock?.Value ?? 10; int num3 = ClanConfig.MaxTerritoryCap?.Value ?? 100; return Mathf.Clamp(num + (clan?.ExtraTerritorySlots ?? 0) * num2, num, num3); } public static int VaultCapacity(Clan clan, int territoryCount) { int num = ClanConfig.ClanVaultTier1CapacityStacks?.Value ?? ClanConfig.VaultCapacityBase?.Value ?? 100; int num2 = Mathf.Max(1, clan?.VaultTier ?? 1); return num * num2; } public static float TerritoryMaxHP(ClanTerritory t) { if (t == null) { return 0f; } float num = ClanConfig.TerritoryHPBase?.Value ?? 1000f; float num2 = ((t.Type == TerritoryType.Capital) ? (ClanConfig.CapitalHPModifier?.Value ?? 2f) : (ClanConfig.ColonyHPModifier?.Value ?? 1f)); return num * (float)Mathf.Max(1, t.SizeLevel) * num2; } public static float CaptureThreshold(ClanTerritory t) { return (ClanConfig.CaptureThresholdPerSize?.Value ?? 100) * Mathf.Max(1, t?.SizeLevel ?? 1); } public static float ClaimSpeed(int attackers, float roleFactor, ClanTerritory t, bool siege) { float num = ClanConfig.ClaimSpeedBase?.Value ?? 1f; float num2 = 1f / (float)Mathf.Max(1, t?.SizeLevel ?? 1); float num3 = (siege ? (1f + (ClanConfig.SiegeClaimSpeedBonus?.Value ?? 50f) / 100f) : 1f); return num * (float)Mathf.Max(1, attackers) * Mathf.Max(0.1f, roleFactor) * num2 * num3; } public static int PlayersRequired(WarType type) { return type switch { WarType.RageWar => ClanConfig.RageWarPlayersRequired?.Value ?? 5, WarType.War => ClanConfig.WarPlayersRequired?.Value ?? 3, _ => ClanConfig.RaidPlayersRequired?.Value ?? 1, }; } } public static class GridExtensions { public static Vector3 ToWorldCenter(this Vector2i grid) { //IL_000c: Unknown result type (might be due to invalid IL or missing references) //IL_000d: Unknown result type (might be due to invalid IL or missing references) //IL_0029: Unknown result type (might be due to invalid IL or missing references) //IL_0040: Unknown result type (might be due to invalid IL or missing references) //IL_0052: Unknown result type (might be due to invalid IL or missing references) if (VoronoiGrid.Instance != null) { return VoronoiGrid.Instance.GetRegionCenter(grid); } int num = ClanConfig.GridCellSize?.Value ?? 500; return new Vector3((float)(grid.x * num) + (float)num / 2f, 0f, (float)(grid.y * num) + (float)num / 2f); } public static Vector2i WorldToGrid(Vector3 worldPos) { //IL_000c: Unknown result type (might be due to invalid IL or missing references) //IL_000d: Unknown result type (might be due to invalid IL or missing references) //IL_0029: Unknown result type (might be due to invalid IL or missing references) //IL_0037: Unknown result type (might be due to invalid IL or missing references) //IL_0045: Unknown result type (might be due to invalid IL or missing references) if (VoronoiGrid.Instance != null) { return VoronoiGrid.Instance.GetRegion(worldPos); } int num = ClanConfig.GridCellSize?.Value ?? 500; return new Vector2i(Mathf.FloorToInt(worldPos.x / (float)num), Mathf.FloorToInt(worldPos.z / (float)num)); } } public class GridManager : MonoBehaviour { private Vector2i? _currentGrid; private int _lastAnnouncedClanId = -1; private float _checkTimer; public static GridManager Instance { get; private set; } private void Awake() { //IL_0005: Unknown result type (might be due to invalid IL or missing references) //IL_0010: Expected O, but got Unknown //IL_0017: 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_0027: Expected O, but got Unknown //IL_0027: Expected O, but got Unknown //IL_002a: Unknown result type (might be due to invalid IL or missing references) //IL_0034: Expected O, but got Unknown if ((Object)Instance != (Object)null && (Object)Instance != (Object)this) { Object.Destroy((Object)this); } else { Instance = this; } } private void Update() { //IL_0005: Unknown result type (might be due to invalid IL or missing references) //IL_0010: Expected O, but got Unknown if (!((Object)Player.m_localPlayer == (Object)null)) { _checkTimer += Time.deltaTime; if (!(_checkTimer < 0.5f)) { _checkTimer = 0f; CheckTerritoryChange(); } } } private void CheckTerritoryChange() { //IL_0007: Unknown result type (might be due to invalid IL or missing references) //IL_0012: Expected O, but got Unknown //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_0065: Unknown result type (might be due to invalid IL or missing references) //IL_0039: Unknown result type (might be due to invalid IL or missing references) //IL_0043: Unknown result type (might be due to invalid IL or missing references) //IL_0051: Unknown result type (might be due to invalid IL or missing references) //IL_005b: 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_00ff: Unknown result type (might be due to invalid IL or missing references) Player localPlayer = Player.m_localPlayer; if ((Object)localPlayer == (Object)null) { return; } Vector2i val = GridExtensions.WorldToGrid(((Component)localPlayer).transform.position); if (_currentGrid.HasValue && _currentGrid.Value.x == val.x && _currentGrid.Value.y == val.y) { return; } _currentGrid = val; WarfareData warfareData = WarfareManager.Instance?.Data; if (warfareData == null) { return; } int num = warfareData.GetTerritory(val)?.OwnerClanId ?? 0; if (num == _lastAnnouncedClanId) { return; } _lastAnnouncedClanId = num; int num2 = warfareData.GetPlayer(localPlayer.GetPlayerID())?.ClanId ?? 0; if (num == 0) { MessageHud instance = MessageHud.instance; if ((Object)(object)instance != (Object)null) { instance.ShowMessage((MessageType)2, "Unclaimed Territory", 0, (Sprite)null, false); } return; } Clan clan = ClanConfig.GetClan(num); string text = ((clan != null) ? ColorUtility.ToHtmlStringRGB(clan.Color) : "ffffff"); bool flag = num == num2; string text2 = (flag ? "HOME TERRITORY" : "ENEMY TERRITORY"); MessageHud instance2 = MessageHud.instance; if ((Object)(object)instance2 != (Object)null) { instance2.ShowMessage((MessageType)2, "" + clan?.Symbol + " " + clan?.Name + "\n" + text2 + "", 0, (Sprite)null, false); } } public Vector2i? GetCurrentGrid() { return _currentGrid; } public ClanTerritory GetCurrentTerritory() { //IL_002b: Unknown result type (might be due to invalid IL or missing references) if (!_currentGrid.HasValue) { return null; } return WarfareManager.Instance?.Data?.GetTerritory(_currentGrid.Value); } } public enum NotificationCategory { PvP, Clan, War, Territory, Economy, Building, Admin, Server, Election, Event, Supporter, System } public sealed class FortecaNotification { public NotificationCategory Category; public string Title; public string Description; public string Player; public string Clan; public string Territory; public int Priority; public DateTimeOffset Timestamp; } public static class NotificationManager { private const int TimeoutMs = 5000; public static void Send(NotificationCategory category, string title, string description) { Send(category, title, description, null, null, null, 0); } public static void Send(NotificationCategory category, string title, string description, string player = null, string clan = null, string territory = null, int priority = 0) { Send(new FortecaNotification { Category = category, Title = Clean(title), Description = Clean(description), Player = Clean(player), Clan = Clean(clan), Territory = Clean(territory), Priority = priority, Timestamp = DateTimeOffset.Now }); } public static void Send(FortecaNotification notification) { if (notification == null) { return; } if (notification.Timestamp == default(DateTimeOffset)) { notification.Timestamp = DateTimeOffset.Now; } string text = FormatEventMessage(notification); if (!string.IsNullOrWhiteSpace(text)) { WarfareManager.Instance?.AddWorldEventLog(text); WarfareRPC.Instance?.BroadcastGlobalMessage(FormatLeftPanelMessage(notification)); if (ShouldSendDiscord(notification.Category)) { QueueDiscord(FormatDiscordMessage(notification)); } } } public static bool TestDiscordWebhook(out string result) { string error; bool flag = TrySendDiscord("Fehu Kingdoms\nNotification System Online\n\nRealm:\n" + GetRealmName() + "\n\nTime:\n" + DateTimeOffset.Now.ToString("yyyy-MM-dd HH:mm:ss zzz"), out error); result = (flag ? "Webhook test successful." : ("Webhook failed. " + error)); return flag; } private static void QueueDiscord(string content) { ThreadPool.QueueUserWorkItem(delegate { if (!TrySendDiscord(content, out var error)) { ConfigEntry debugNotifications = ClanConfig.DebugNotifications; if (debugNotifications != null && debugNotifications.Value) { Plugin.Log.LogWarning((object)("[FCW Notifications] Discord webhook send failed: " + error)); } } }); } private static bool ShouldSendDiscord(NotificationCategory category) { ConfigEntry discordEnabled = ClanConfig.DiscordEnabled; if (discordEnabled == null || !discordEnabled.Value || string.IsNullOrWhiteSpace(ClanConfig.DiscordWebhookUrl?.Value)) { return false; } return category switch { NotificationCategory.PvP => ClanConfig.NotifyDiscordPvP?.Value ?? false, NotificationCategory.Clan => ClanConfig.NotifyDiscordClan?.Value ?? false, NotificationCategory.War => ClanConfig.NotifyDiscordWar?.Value ?? false, NotificationCategory.Territory => ClanConfig.NotifyDiscordTerritory?.Value ?? false, NotificationCategory.Economy => ClanConfig.NotifyDiscordEconomy?.Value ?? false, NotificationCategory.Building => ClanConfig.NotifyDiscordBuilding?.Value ?? false, NotificationCategory.Admin => ClanConfig.NotifyDiscordAdmin?.Value ?? false, NotificationCategory.Server => ClanConfig.NotifyDiscordServer?.Value ?? false, NotificationCategory.Election => ClanConfig.NotifyDiscordElection?.Value ?? false, NotificationCategory.Event => ClanConfig.NotifyDiscordEvent?.Value ?? false, NotificationCategory.Supporter => ClanConfig.NotifyDiscordSupporter?.Value ?? false, NotificationCategory.System => ClanConfig.NotifyDiscordSystem?.Value ?? false, _ => false, }; } private static bool TrySendDiscord(string content, out string error) { error = ""; string text = ClanConfig.DiscordWebhookUrl?.Value ?? ""; if (string.IsNullOrWhiteSpace(text)) { error = "Webhook URL is empty."; return false; } for (int i = 0; i < 2; i++) { try { SendDiscordOnce(text, content); return true; } catch (Exception ex) { error = ex.GetBaseException().Message; } } return false; } private static void SendDiscordOnce(string url, string content) { HttpWebRequest httpWebRequest = (HttpWebRequest)WebRequest.Create(url); httpWebRequest.Method = "POST"; httpWebRequest.ContentType = "application/json"; httpWebRequest.Timeout = 5000; httpWebRequest.ReadWriteTimeout = 5000; byte[] bytes = Encoding.UTF8.GetBytes("{\"content\":\"" + JsonEscape(content) + "\"}"); httpWebRequest.ContentLength = bytes.Length; using (Stream stream = httpWebRequest.GetRequestStream()) { stream.Write(bytes, 0, bytes.Length); } using HttpWebResponse httpWebResponse = (HttpWebResponse)httpWebRequest.GetResponse(); int statusCode = (int)httpWebResponse.StatusCode; if (statusCode < 200 || statusCode >= 300) { throw new WebException("Discord returned HTTP " + statusCode + "."); } } private static string FormatEventMessage(FortecaNotification notification) { if (string.IsNullOrWhiteSpace(notification.Title)) { return notification.Description ?? ""; } if (string.IsNullOrWhiteSpace(notification.Description)) { return notification.Title; } return notification.Title + ": " + notification.Description; } private static string FormatLeftPanelMessage(FortecaNotification notification) { return "[" + notification.Category.ToString() + "] " + FormatEventMessage(notification); } private static string FormatDiscordMessage(FortecaNotification notification) { StringBuilder stringBuilder = new StringBuilder(); stringBuilder.Append("**").Append(notification.Title ?? notification.Category.ToString()).Append("**"); if (!string.IsNullOrWhiteSpace(notification.Description)) { stringBuilder.Append("\n").Append(notification.Description); } if (!string.IsNullOrWhiteSpace(notification.Clan)) { stringBuilder.Append("\nClan: ").Append(notification.Clan); } if (!string.IsNullOrWhiteSpace(notification.Territory)) { stringBuilder.Append("\nTerritory: ").Append(notification.Territory); } if (!string.IsNullOrWhiteSpace(notification.Player)) { stringBuilder.Append("\nPlayer: ").Append(notification.Player); } stringBuilder.Append("\nTime: ").Append(notification.Timestamp.ToString("yyyy-MM-dd HH:mm:ss zzz")); return stringBuilder.ToString(); } private static string JsonEscape(string value) { if (string.IsNullOrEmpty(value)) { return ""; } return value.Replace("\\", "\\\\").Replace("\"", "\\\"").Replace("\r", "") .Replace("\n", "\\n"); } private static string Clean(string value) { if (!string.IsNullOrWhiteSpace(value)) { return value.Replace("<", "").Replace(">", "").Trim(); } return ""; } private static string GetRealmName() { try { ZNet instance = ZNet.instance; string text = ((instance != null) ? instance.GetWorldName() : null); if (!string.IsNullOrWhiteSpace(text)) { return text; } } catch { } return SystemInfo.deviceName ?? "Unknown Realm"; } } public sealed class ProtectedZoneInfo { public ProtectedZoneType Type; public string Name = "Protected World Zone"; public string OwnerName = "WORLD"; public Vector2i Grid; public Vector3 Position; public bool DrawMarker = true; } public static class ProtectedWorldZones { private const float TraderRefreshSeconds = 30f; private static readonly List TraderZones = new List(); private static float _lastTraderRefresh = -9999f; public static bool IsProtected(Vector2i grid) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) ProtectedZoneInfo info; return TryGetProtectedZone(grid, out info); } public static bool IsProtected(ClanTerritory territory) { //IL_0004: Unknown result type (might be due to invalid IL or missing references) if (territory != null) { return IsProtected(territory.GridPosition); } return false; } public static bool TryGetProtectedZone(Vector2i grid, out ProtectedZoneInfo info) { //IL_0000: 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_0028: Unknown result type (might be due to invalid IL or missing references) //IL_0029: Unknown result type (might be due to invalid IL or missing references) //IL_002e: Unknown result type (might be due to invalid IL or missing references) //IL_0056: Unknown result type (might be due to invalid IL or missing references) //IL_0069: Unknown result type (might be due to invalid IL or missing references) if (IsSpawnSanctuary(grid)) { info = new ProtectedZoneInfo { Type = ProtectedZoneType.Spawn, Name = "Spawn Sanctuary", Grid = grid, Position = grid.ToWorldCenter() }; return true; } RefreshTraderZonesIfNeeded(); for (int i = 0; i < TraderZones.Count; i++) { ProtectedZoneInfo protectedZoneInfo = TraderZones[i]; if (protectedZoneInfo.Grid.x == grid.x && protectedZoneInfo.Grid.y == grid.y) { info = protectedZoneInfo; return true; } } info = null; return false; } public static string GetBlockedActionMessage(Vector2i grid) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) if (TryGetProtectedZone(grid, out var info)) { if (info.Type == ProtectedZoneType.Trader) { return "Protected World Zone: Trader territory cannot be claimed."; } return info.Name + " is a Protected World Zone and cannot be claimed, captured, sold, or transferred."; } return "Protected World Zone: action blocked."; } public static List GetKnownProtectedZones() { //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_0010: Unknown result type (might be due to invalid IL or missing references) //IL_002a: Unknown result type (might be due to invalid IL or missing references) //IL_002b: 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_0032: Unknown result type (might be due to invalid IL or missing references) //IL_0037: Unknown result type (might be due to invalid IL or missing references) //IL_0052: Unknown result type (might be due to invalid IL or missing references) //IL_005a: Unknown result type (might be due to invalid IL or missing references) //IL_0067: Unknown result type (might be due to invalid IL or missing references) //IL_006e: Unknown result type (might be due to invalid IL or missing references) //IL_009e: Unknown result type (might be due to invalid IL or missing references) //IL_00a0: Unknown result type (might be due to invalid IL or missing references) //IL_00a6: Unknown result type (might be due to invalid IL or missing references) //IL_00a8: Unknown result type (might be due to invalid IL or missing references) //IL_00ad: 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_007d: Unknown result type (might be due to invalid IL or missing references) List list = new List(); Vector2i val = GridExtensions.WorldToGrid(Vector3.zero); list.Add(new ProtectedZoneInfo { Type = ProtectedZoneType.Spawn, Name = "Spawn Sanctuary", Grid = val, Position = val.ToWorldCenter(), DrawMarker = true }); Vector2i val2 = default(Vector2i); for (int i = -1; i <= 1; i++) { for (int j = -1; j <= 1; j++) { ((Vector2i)(ref val2))..ctor(val.x + i, val.y + j); if (val2.x != val.x || val2.y != val.y) { list.Add(new ProtectedZoneInfo { Type = ProtectedZoneType.Spawn, Name = "Spawn Sanctuary", Grid = val2, Position = val2.ToWorldCenter(), DrawMarker = false }); } } } RefreshTraderZonesIfNeeded(); for (int k = 0; k < TraderZones.Count; k++) { list.Add(TraderZones[k]); } return list; } public static List GetKnownTraderZones(bool forceRefresh = false) { if (forceRefresh) { _lastTraderRefresh = -9999f; } RefreshTraderZonesIfNeeded(); return new List(TraderZones); } private static bool IsSpawnSanctuary(Vector2i grid) { //IL_0016: 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_0021: Unknown result type (might be due to invalid IL or missing references) //IL_0027: 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_0036: 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) if (VoronoiGrid.Instance != null && VoronoiGrid.Instance.IsSpawnZone(grid)) { return true; } Vector2i val = GridExtensions.WorldToGrid(Vector3.zero); if (Math.Abs(grid.x - val.x) <= 1) { return Math.Abs(grid.y - val.y) <= 1; } return false; } private static void RefreshTraderZonesIfNeeded() { if (!Application.isPlaying || !(Time.time - _lastTraderRefresh < 30f)) { _lastTraderRefresh = (Application.isPlaying ? Time.time : 0f); TraderZones.Clear(); RefreshTraderZonesFromLoadedTraders(); RefreshTraderZonesFromMinimapPins(); RefreshTraderZonesFromZoneSystem(); } } private static void RefreshTraderZonesFromLoadedTraders() { //IL_004b: Unknown result type (might be due to invalid IL or missing references) //IL_0056: Expected O, but got Unknown //IL_0082: Unknown result type (might be due to invalid IL or missing references) Type type = typeof(Player).Assembly.GetType("Trader") ?? Type.GetType("Trader"); if (type == null) { return; } Object[] array = Object.FindObjectsOfType(type); if (array == null) { return; } foreach (Object obj in array) { Component val = (Component)(object)((obj is Component) ? obj : null); if (!((Object)val == (Object)null)) { string text = ((Object)val).name ?? ""; string text2 = ((Object)val.gameObject).name ?? text; AddTraderZone(val.transform.position, text + " " + text2); } } } private static void RefreshTraderZonesFromMinimapPins() { //IL_0005: Unknown result type (might be due to invalid IL or missing references) //IL_0010: Expected O, but got Unknown //IL_0063: Unknown result type (might be due to invalid IL or missing references) try { if ((Object)Minimap.instance == (Object)null || !(typeof(Minimap).GetField("m_pins", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic)?.GetValue(Minimap.instance) is IEnumerable enumerable)) { return; } foreach (object item in enumerable) { if (TryReadTraderPin(item, out var position, out var label)) { AddTraderZone(position, label); } } } catch (Exception ex) { Plugin.Log.LogWarning((object)("[FCW ProtectedZones] Trader minimap pin scan failed: " + ex.Message)); } } private static bool TryReadTraderPin(object pin, out Vector3 position, out string label) { //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_0044: Unknown result type (might be due to invalid IL or missing references) //IL_0049: Unknown result type (might be due to invalid IL or missing references) //IL_00eb: 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) position = Vector3.zero; label = ""; if (pin == null) { return false; } Type type = pin.GetType(); if (!((GetMemberValue(type, pin, "m_pos") ?? GetMemberValue(type, pin, "m_position")) is Vector3 val)) { return false; } string text = (GetMemberValue(type, pin, "m_name") as string) ?? ""; string text2 = GetMemberValue(type, pin, "m_type")?.ToString() ?? ""; string text3 = GetMemberValue(type, pin, "m_icon")?.ToString() ?? ""; string text4 = (text + " " + text2 + " " + text3).ToLowerInvariant(); if (!IsTraderText(text4)) { return false; } position = val; label = GetTraderZoneName(text4); return true; } private static void RefreshTraderZonesFromZoneSystem() { try { object staticInstance = GetStaticInstance("ZoneSystem"); if (staticInstance == null) { return; } object memberValue = GetMemberValue(staticInstance.GetType(), staticInstance, "m_locationInstances"); if (memberValue is IDictionary dictionary) { { foreach (object value in dictionary.Values) { TryAddTraderZoneFromLocationObject(value); } return; } } if (!(memberValue is IEnumerable enumerable)) { return; } foreach (object item in enumerable) { TryAddTraderZoneFromLocationObject(item); } } catch (Exception ex) { Plugin.Log.LogWarning((object)("[FCW ProtectedZones] ZoneSystem trader scan failed: " + ex.Message)); } } private static object GetStaticInstance(string typeName) { Type type = typeof(Player).Assembly.GetType(typeName) ?? Type.GetType(typeName); if (type == null) { return null; } return type.GetField("instance", BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic)?.GetValue(null); } private static void TryAddTraderZoneFromLocationObject(object locationObject) { //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) //IL_005d: Unknown result type (might be due to invalid IL or missing references) if (locationObject != null) { Type type = locationObject.GetType(); string text = CollectLocationText(locationObject, 0).ToLowerInvariant(); if (IsTraderText(text) && (GetMemberValue(type, locationObject, "m_position") ?? GetMemberValue(type, locationObject, "m_pos") ?? GetMemberValue(type, locationObject, "Position")) is Vector3 position) { AddTraderZone(position, text); } } } private static string CollectLocationText(object value, int depth) { if (value == null || depth > 2) { return ""; } if (value is string text) { return text + " "; } Type type = value.GetType(); if (type.IsPrimitive || type.IsEnum || type == typeof(Vector3) || type == typeof(Vector2i)) { return value.ToString() + " "; } string text2 = type.Name + " "; FieldInfo[] fields = type.GetFields(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); foreach (FieldInfo fieldInfo in fields) { if (fieldInfo.FieldType == typeof(string) || fieldInfo.Name.ToLowerInvariant().Contains("name") || fieldInfo.Name.ToLowerInvariant().Contains("prefab") || fieldInfo.Name.ToLowerInvariant().Contains("location")) { try { text2 += CollectLocationText(fieldInfo.GetValue(value), depth + 1); } catch { } } } return text2; } private static object GetMemberValue(Type type, object instance, string name) { FieldInfo field = type.GetField(name, BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); if (field != null) { return field.GetValue(instance); } return type.GetProperty(name, BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic)?.GetValue(instance, null); } private static void AddTraderZone(Vector3 position, string sourceName) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //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_0022: Unknown result type (might be due to invalid IL or missing references) //IL_006d: Unknown result type (might be due to invalid IL or missing references) //IL_006e: Unknown result type (might be due to invalid IL or missing references) //IL_0074: Unknown result type (might be due to invalid IL or missing references) //IL_0075: 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) Vector2i val = GridExtensions.WorldToGrid(position); for (int i = 0; i < TraderZones.Count; i++) { ProtectedZoneInfo protectedZoneInfo = TraderZones[i]; if (protectedZoneInfo.Grid.x == val.x && protectedZoneInfo.Grid.y == val.y) { return; } } TraderZones.Add(new ProtectedZoneInfo { Type = ProtectedZoneType.Trader, Name = GetTraderZoneName(sourceName), Grid = val, Position = position }); } private static string GetTraderZoneName(string sourceName) { string text = (sourceName ?? "").ToLowerInvariant(); if (text.Contains("bogwitch") || text.Contains("bog_witch") || text.Contains("bog witch")) { return "Bog Witch Hut"; } if (text.Contains("hildir")) { return "Hildir Camp"; } if (text.Contains("haldor")) { return "Haldor Trading Post"; } return "Trader Protected Zone"; } private static bool IsTraderText(string text) { if (string.IsNullOrEmpty(text)) { return false; } if (!text.Contains("trader") && !text.Contains("vendor") && !text.Contains("haldor") && !text.Contains("hildir") && !text.Contains("bogwitch") && !text.Contains("bog_witch")) { return text.Contains("bog witch"); } return true; } } public static class PvPManager { private struct CombatState { public float EndsAt; public long LastAttackerId; public string LastAttackerName; } private static readonly Dictionary _combatStates = new Dictionary(); private static readonly List> _safeZoneProviders = new List>(); private static readonly List> _arenaZoneProviders = new List>(); private static float _nextPruneAt; public static bool IsBattleStatusEnabled => ClanConfig.BattleStatusEnabled?.Value ?? false; public static bool IsCombatLogoutHandlingEnabled => RealmRules.GetBool("combat_logout"); public static bool IsKillFeedEnabled => ClanConfig.KillFeedEnabled?.Value ?? false; public static int CombatDurationSeconds => Mathf.Clamp(ClanConfig.CombatDurationSeconds?.Value ?? 30, 1, 3600); public static event Action OnPlayerKilledPlayer; public static bool IsPlayerInCombat(Player player) { return GetRemainingCombatTime(player) > 0f; } public static void EnterCombat(Player player) { EnterCombat(player, null); } public static void EnterCombat(Player player, Player attacker) { //IL_0008: Unknown result type (might be due to invalid IL or missing references) //IL_0013: Expected O, but got Unknown //IL_003d: Unknown result type (might be due to invalid IL or missing references) //IL_0048: Expected O, but got Unknown //IL_004b: Unknown result type (might be due to invalid IL or missing references) //IL_0051: Unknown result type (might be due to invalid IL or missing references) //IL_005b: Expected O, but got Unknown //IL_005b: Expected O, but got Unknown if (!IsBattleStatusEnabled || (Object)player == (Object)null) { return; } long playerID = player.GetPlayerID(); if (playerID != 0L) { CombatState value = new CombatState { EndsAt = Time.time + (float)CombatDurationSeconds }; CombatState value2; if ((Object)attacker != (Object)null && (Object)attacker != (Object)player) { value.LastAttackerId = attacker.GetPlayerID(); value.LastAttackerName = SafePlayerName(attacker); } else if (_combatStates.TryGetValue(playerID, out value2)) { value.LastAttackerId = value2.LastAttackerId; value.LastAttackerName = value2.LastAttackerName; } _combatStates[playerID] = value; } } public static void LeaveCombat(Player player) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) //IL_000c: Expected O, but got Unknown if (!((Object)player == (Object)null)) { _combatStates.Remove(player.GetPlayerID()); } } public static float GetRemainingCombatTime(Player player) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) //IL_000c: Expected O, but got Unknown if ((Object)player == (Object)null) { return 0f; } long playerID = player.GetPlayerID(); if (!_combatStates.TryGetValue(playerID, out var value)) { return 0f; } float num = value.EndsAt - Time.time; if (num <= 0f) { _combatStates.Remove(playerID); return 0f; } return num; } public static float GetCombatTimer(Player player) { return GetRemainingCombatTime(player); } public static long GetLastAttacker(Player player) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) //IL_000c: Expected O, but got Unknown if ((Object)player == (Object)null) { return 0L; } if (!_combatStates.TryGetValue(player.GetPlayerID(), out var value)) { return 0L; } return value.LastAttackerId; } public static bool IsPvPKill(Player killer, Player victim) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) //IL_000c: Expected O, but got Unknown //IL_000f: Unknown result type (might be due to invalid IL or missing references) //IL_001a: Expected O, but got Unknown //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_002d: Expected O, but got Unknown //IL_002d: Expected O, but got Unknown if ((Object)killer != (Object)null && (Object)victim != (Object)null) { return (Object)killer != (Object)victim; } return false; } public static void RegisterSafeZoneProvider(Func provider) { if (provider != null && !_safeZoneProviders.Contains(provider)) { _safeZoneProviders.Add(provider); } } public static void RegisterArenaZoneProvider(Func provider) { if (provider != null && !_arenaZoneProviders.Contains(provider)) { _arenaZoneProviders.Add(provider); } } public static bool CanDamage(Player attacker, Player victim) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) //IL_000c: Expected O, but got Unknown //IL_000f: Unknown result type (might be due to invalid IL or missing references) //IL_001a: Expected O, but got Unknown //IL_001f: 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_002f: Expected O, but got Unknown //IL_002f: Expected O, but got Unknown //IL_00ce: 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) if ((Object)attacker == (Object)null || (Object)victim == (Object)null) { return true; } if ((Object)attacker == (Object)victim) { return true; } if (HasAdminPvpBypass(attacker)) { return true; } bool flag = RealmRules.GetBool("arena_pvp") && IsPlayerInArena(attacker) && IsPlayerInArena(victim); if (RealmRules.GetBool("safe_zones") && (IsPlayerInSafeZone(attacker) || IsPlayerInSafeZone(victim)) && !flag) { return false; } if (flag) { return true; } WarfareData warfareData = WarfareManager.Instance?.Data; PlayerClanInfo playerClanInfo = warfareData?.GetPlayer(attacker.GetPlayerID()); PlayerClanInfo playerClanInfo2 = warfareData?.GetPlayer(victim.GetPlayerID()); if (playerClanInfo == null || playerClanInfo2 == null) { return true; } ClanTerritory territory = warfareData.GetTerritory(GridExtensions.WorldToGrid(((Component)victim).transform.position)); if (RealmRules.GetBool("safe_zones")) { WarfareManager instance = WarfareManager.Instance; if (instance != null && instance.IsFreebornProtectedTerritory(territory) && territory.FreebornOwnerId != playerClanInfo.PlayerId) { return false; } } if (playerClanInfo.ClanId != 0 && playerClanInfo2.ClanId != 0) { if (playerClanInfo.ClanId == playerClanInfo2.ClanId) { return RealmRules.GetBool("friendly_fire"); } if (!RealmRules.GetBool("alliance_friendly_fire")) { WarfareManager instance2 = WarfareManager.Instance; if (instance2 != null && instance2.GetDiplomacyState(playerClanInfo.ClanId, playerClanInfo2.ClanId) == DiplomacyState.Allied) { return false; } } } return true; } public static bool IsPlayerInSafeZone(Player player) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) //IL_000c: Expected O, but got Unknown //IL_0016: 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) if ((Object)player == (Object)null) { return false; } if (ProtectedWorldZones.IsProtected(GridExtensions.WorldToGrid(((Component)player).transform.position))) { return true; } for (int i = 0; i < _safeZoneProviders.Count; i++) { try { if (_safeZoneProviders[i](player)) { return true; } } catch (Exception ex) { Plugin.Log.LogWarning((object)("[FCW PvP] Safe zone provider failed safely: " + ex.Message)); } } return false; } public static bool IsPlayerInArena(Player player) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) //IL_000c: Expected O, but got Unknown if ((Object)player == (Object)null) { return false; } for (int i = 0; i < _arenaZoneProviders.Count; i++) { try { if (_arenaZoneProviders[i](player)) { return true; } } catch (Exception ex) { Plugin.Log.LogWarning((object)("[FCW PvP] Arena provider failed safely: " + ex.Message)); } } return false; } public static void RegisterPvPDamage(Player attacker, Player victim) { if (IsPvPKill(attacker, victim)) { EnterCombat(attacker, victim); EnterCombat(victim, attacker); } } public static void RegisterPlayerKilledPlayer(Player killer, Player victim) { if (IsPvPKill(killer, victim)) { PvPManager.OnPlayerKilledPlayer?.Invoke(killer, victim); if (IsKillFeedEnabled) { NotificationManager.Send(NotificationCategory.PvP, "Blood has been spilled", SafePlayerName(killer) + " defeated " + SafePlayerName(victim) + ".", SafePlayerName(killer)); } LeaveCombat(victim); } } public static void RegisterCombatLogout(Player player) { if (IsCombatLogoutHandlingEnabled && IsPlayerInCombat(player)) { NotificationManager.Send(NotificationCategory.PvP, "A warrior abandoned the battlefield", "Combat logout detected.", SafePlayerName(player)); LeaveCombat(player); } } public static void Tick() { if (Time.time < _nextPruneAt) { return; } _nextPruneAt = Time.time + 1f; if (_combatStates.Count == 0) { return; } List list = null; foreach (KeyValuePair combatState in _combatStates) { if (combatState.Value.EndsAt <= Time.time) { if (list == null) { list = new List(); } list.Add(combatState.Key); } } if (list != null) { for (int i = 0; i < list.Count; i++) { _combatStates.Remove(list[i]); } } } public static bool HasAdminPvpBypass(Player player) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) //IL_000c: Expected O, but got Unknown if ((Object)player == (Object)null) { return false; } return WarfareManager.Instance?.HasEventAdminAccess(player.GetPlayerID().ToString()) ?? false; } private static string SafePlayerName(Player player) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) //IL_000c: Expected O, but got Unknown if ((Object)player == (Object)null) { return "Unknown"; } string playerName = player.GetPlayerName(); if (!string.IsNullOrWhiteSpace(playerName)) { return playerName.Replace("<", "").Replace(">", "").Trim(); } return "Unknown"; } } public enum RealmRuleValueType { Bool, Int, Float, String } public sealed class RealmRule { public string Id; public string DisplayName; public string Description; public string Category; public string DefaultValue; public bool RequiresRestart; public bool VisibleInUi = true; public RealmRuleValueType ValueType; public Func GetValue; public Action SetValue; } public sealed class RealmRuleCategory { public string Id; public string Title; public string Description; } public static class RealmRules { private static readonly List _categories = new List(); private static readonly List _rules = new List(); private static readonly Dictionary _byId = new Dictionary(StringComparer.OrdinalIgnoreCase); private static bool _registered; public static IReadOnlyList Categories { get { EnsureRegistered(); return _categories; } } public static IReadOnlyList Rules { get { EnsureRegistered(); return _rules; } } public static bool IsEnabled(string ruleId) { return GetBool(ruleId); } public static bool GetBool(string ruleId) { return WarfareManager.IsTruthy(GetValue(ruleId)); } public static int GetInt(string ruleId) { if (!int.TryParse(GetValue(ruleId), out var result)) { return 0; } return result; } public static float GetFloat(string ruleId) { if (!float.TryParse(GetValue(ruleId), out var result)) { return 0f; } return result; } public static string GetValue(string ruleId) { EnsureRegistered(); if (string.IsNullOrWhiteSpace(ruleId) || !_byId.TryGetValue(ruleId, out var value)) { return ""; } if (value.GetValue == null) { return GetOverride(value.Id, value.DefaultValue); } return value.GetValue(); } public static RealmRule GetRule(string ruleId) { EnsureRegistered(); if (string.IsNullOrWhiteSpace(ruleId) || !_byId.TryGetValue(ruleId, out var value)) { return null; } return value; } public static bool TrySetValue(string ruleId, string value, out string message) { EnsureRegistered(); RealmRule rule = GetRule(ruleId); if (rule == null) { message = "Unknown realm law."; return false; } value = NormalizeValue(rule, value); string value2 = GetValue(rule.Id); if (rule.SetValue != null) { rule.SetValue(value); } else { SetOverride(rule.Id, value); } if (!string.Equals(value2, value, StringComparison.OrdinalIgnoreCase)) { NotificationManager.Send(NotificationCategory.Admin, GetLawChangeTitle(rule.Id, WarfareManager.IsTruthy(value)), rule.DisplayName + " is now " + DisplayValue(rule, value) + "."); } message = "✔ Realm law updated\n" + rule.DisplayName + " = " + DisplayValue(rule, value) + ""; return true; } private static string GetLawChangeTitle(string ruleId, bool enabled) { switch (ruleId) { case "safe_zones": if (!enabled) { return "The sacred protections have been lifted"; } return "The realm now protects sacred lands"; case "arena_pvp": if (!enabled) { return "The sacred arenas have fallen silent"; } return "The sacred arenas have been opened"; case "alliance_friendly_fire": return "The bonds between allied clans have changed"; case "building_damage": return "The laws protecting the realm's settlements have changed"; case "portal_restrictions": return "The borders of the realm are now more secure"; case "claim_restrictions": return "The Jarls have revised the laws of conquest"; case "raid_protection": return "The ancient ward stones have been strengthened"; default: return "The Jarl has proclaimed a new law"; } } public static string DisplayValue(RealmRule rule, string value) { if (rule != null && rule.ValueType == RealmRuleValueType.Bool) { if (!WarfareManager.IsTruthy(value)) { return "OFF"; } return "ON"; } return value ?? ""; } private static void EnsureRegistered() { if (!_registered) { _registered = true; RegisterCategories(); RegisterRules(); } } private static void RegisterCategories() { AddCategory("warfare", "WARFARE", "Laws that govern conflict, combat, wars and protected combat states."); AddCategory("territories", "TERRITORIES", "Laws that govern claims, land use, gathering and raids."); AddCategory("freeborn", "FREEBORN", "Laws that govern independent Freeborn rights and restrictions."); AddCategory("economy", "ECONOMY", "Laws that govern taxes, trade, markets, treasury and vault access."); AddCategory("death", "RESPAWN & DEATH", "Laws that govern death penalties, inventory protection and grace periods."); AddCategory("server", "SERVER", "Laws that govern broad realm-wide server behavior."); } private static void RegisterRules() { AddBool("force_pvp", "Force PvP", "Force local PvP ON for every player.", "warfare", defaultValue: false, () => WarfareManager.IsTruthy(ClanConfig.ForcePvpOn?.Value), delegate(bool v) { ClanConfig.ForcePvpOn.Value = (v ? "true" : "false"); }); AddBool("friendly_fire", "Friendly Fire", "Allow Forteca friendly-fire damage.", "warfare", defaultValue: false, () => WarfareManager.IsTruthy(ClanConfig.ForceFriendlyFireOn?.Value), delegate(bool v) { ClanConfig.ForceFriendlyFireOn.Value = (v ? "true" : "false"); }); AddBool("clan_wars", "Clan Wars", "Allow clans to declare and fight wars.", "warfare", defaultValue: true, () => ClanConfig.ClanWarsEnabled?.Value ?? false, delegate(bool v) { ClanConfig.ClanWarsEnabled.Value = v; }); AddBool("territory_capture", "Territory Capture", "Allow valid wars to capture territories.", "warfare", defaultValue: true, () => ClanConfig.TerritoryCaptureEnabled?.Value ?? false, delegate(bool v) { ClanConfig.TerritoryCaptureEnabled.Value = v; }); AddBool("safe_zones", "Safe Zones", "Respect protected safe zones.", "warfare", defaultValue: true); AddBool("arena_pvp", "Arena PvP", "Allow future arena-specific PvP laws.", "warfare", defaultValue: false); AddBool("alliance_friendly_fire", "Alliance Friendly Fire", "Allow future alliance friendly-fire overrides.", "warfare", defaultValue: false); AddBool("combat_logout", "Combat Logout", "Record combat logout notifications.", "warfare", defaultValue: false, () => ClanConfig.CombatLogoutHandling?.Value ?? false, delegate(bool v) { ClanConfig.CombatLogoutHandling.Value = v; }); AddBool("teleport_in_combat", "Teleport In Combat", "Allow teleporting while in combat.", "warfare", defaultValue: true, delegate { ConfigEntry blockTeleportWhileInCombat = ClanConfig.BlockTeleportWhileInCombat; return blockTeleportWhileInCombat == null || !blockTeleportWhileInCombat.Value; }, delegate(bool v) { ClanConfig.BlockTeleportWhileInCombat.Value = !v; }); AddBool("building_damage", "Building Damage", "Allow future building damage laws.", "territories", defaultValue: true); AddBool("mining_restriction", "Mining Restriction", "Restrict mining by territory ownership rules.", "territories", defaultValue: false, () => ClanConfig.MiningRestrictionEnabled?.Value ?? false, delegate(bool v) { ClanConfig.MiningRestrictionEnabled.Value = v; }); AddBool("woodcutting_restriction", "Woodcutting Restriction", "Restrict woodcutting by territory ownership rules.", "territories", defaultValue: false, () => ClanConfig.WoodcuttingRestrictionEnabled?.Value ?? false, delegate(bool v) { ClanConfig.WoodcuttingRestrictionEnabled.Value = v; }); AddBool("resource_gathering_restriction", "Resource Gathering Restriction", "Restrict gathering by territory ownership rules.", "territories", defaultValue: false, () => ClanConfig.EnableTerritoryResourceOwnership?.Value ?? false, delegate(bool v) { ClanConfig.EnableTerritoryResourceOwnership.Value = v; }); AddBool("portal_restrictions", "Portal Restrictions", "Allow future portal access restriction laws.", "territories", defaultValue: true); AddBool("claim_restrictions", "Claim Restrictions", "Respect claim restrictions and protected zones.", "territories", defaultValue: true); AddBool("raid_protection", "Raid Protection", "Reduce raid effectiveness against offline clans.", "territories", defaultValue: true, () => ClanConfig.OfflineRaidProtection?.Value ?? false, delegate(bool v) { ClanConfig.OfflineRaidProtection.Value = v; }); AddBool("freeborn_claim", "Can Claim Territories", "Allow Freeborn claim rules.", "freeborn", defaultValue: true); AddBool("freeborn_build", "Can Build", "Allow Freeborn building rights.", "freeborn", defaultValue: true); AddBool("freeborn_vote", "Can Vote", "Allow Freeborn voting rights.", "freeborn", defaultValue: true); AddBool("freeborn_join_wars", "Can Join Wars", "Allow Freeborn war participation rules.", "freeborn", defaultValue: false); AddBool("freeborn_homesteads", "Can Create Homesteads", "Allow Freeborn homestead creation.", "freeborn", defaultValue: true, () => ClanConfig.FreebornEnabled?.Value ?? false, delegate(bool v) { ClanConfig.FreebornEnabled.Value = v; }); AddBool("freeborn_join_clans", "Can Join Clans", "Allow Freeborn to join clans.", "freeborn", defaultValue: true); AddBool("taxes_enabled", "Taxes Enabled", "Enable clan resource and coin taxes.", "economy", defaultValue: true, () => ClanConfig.EnableClanMemberTax?.Value ?? false, delegate(bool v) { ClanConfig.EnableClanMemberTax.Value = v; }); AddBool("marketplace_enabled", "Marketplace Enabled", "Allow future marketplace systems.", "economy", defaultValue: false); AddBool("black_market_enabled", "Black Market Enabled", "Allow future black market systems.", "economy", defaultValue: false); AddBool("trade_restrictions", "Trade Restrictions", "Allow future trade restriction laws.", "economy", defaultValue: false); AddBool("treasury_rules", "Treasury Rules", "Enable treasury law enforcement.", "economy", defaultValue: true); AddBool("vault_rules", "Vault Rules", "Enable vault law enforcement.", "economy", defaultValue: true); AddBool("keep_inventory", "Keep Inventory", "Protect configured inventory slots on death where supported.", "death", defaultValue: false, () => ClanConfig.ProtectHotbarOnDeath?.Value ?? false, delegate(bool v) { ClanConfig.ProtectHotbarOnDeath.Value = v; }); AddBool("skill_loss", "Skill Loss", "Allow future skill-loss laws.", "death", defaultValue: true); AddBool("death_penalty", "Death Penalty", "Allow future death penalty laws.", "death", defaultValue: true); AddBool("respawn_protection", "Respawn Protection", "Allow future respawn protection laws.", "death", defaultValue: true); AddBool("death_grace", "Death Grace", "Allow future death grace period laws.", "death", defaultValue: true); AddBool("hardcore_mode", "Hardcore Mode", "Allow future hardcore-mode laws.", "server", defaultValue: false, null, null, requiresRestart: true); AddBool("boss_buffs", "Boss Buffs", "Allow future boss buff laws.", "server", defaultValue: true); AddBool("map_sharing", "Map Sharing", "Allow future map sharing laws.", "server", defaultValue: true); AddBool("portals_enabled", "Portals Enabled", "Allow future global portal laws.", "server", defaultValue: true); AddBool("sleep_skip", "Sleep Skip", "Allow future sleep skip laws.", "server", defaultValue: true); AddBool("console_mode", "Console Mode", "Allow future console mode laws.", "server", defaultValue: false); } private static void AddCategory(string id, string title, string description) { _categories.Add(new RealmRuleCategory { Id = id, Title = title, Description = description }); } private static void AddBool(string id, string name, string description, string category, bool defaultValue, Func getter = null, Action setter = null, bool requiresRestart = false) { AddRule(new RealmRule { Id = id, DisplayName = name, Description = description, Category = category, DefaultValue = (defaultValue ? "true" : "false"), RequiresRestart = requiresRestart, ValueType = RealmRuleValueType.Bool, GetValue = ((getter == null) ? null : ((Func)(() => (!getter()) ? "false" : "true"))), SetValue = ((setter == null) ? null : ((Action)delegate(string value) { setter(WarfareManager.IsTruthy(value)); })) }); } private static void AddRule(RealmRule rule) { _rules.Add(rule); _byId[rule.Id] = rule; } private static string NormalizeValue(RealmRule rule, string value) { if (rule.ValueType == RealmRuleValueType.Bool) { if (!WarfareManager.IsTruthy(value)) { return "false"; } return "true"; } return value ?? ""; } private static string GetOverride(string id, string fallback) { if (!ParseOverrides().TryGetValue(id, out var value)) { return fallback; } return value; } private static void SetOverride(string id, string value) { Dictionary dictionary = ParseOverrides(); dictionary[id] = value ?? ""; List list = new List(); foreach (KeyValuePair item in dictionary) { list.Add(item.Key + "=" + item.Value.Replace(";", "").Replace("=", "")); } ClanConfig.RealmLawOverrides.Value = string.Join(";", list.ToArray()); } private static Dictionary ParseOverrides() { Dictionary dictionary = new Dictionary(StringComparer.OrdinalIgnoreCase); string[] array = (ClanConfig.RealmLawOverrides?.Value ?? "").Split(new char[1] { ';' }, StringSplitOptions.RemoveEmptyEntries); foreach (string text in array) { int num = text.IndexOf('='); if (num > 0) { dictionary[text.Substring(0, num).Trim()] = text.Substring(num + 1).Trim(); } } return dictionary; } } public enum RoleSlot { None, Primary, Secondary, Tertiary } public enum RoleModifier { None, MapDiscoveryBonus, UpgradeCostReduction, TreasuryIncomeBonus, UpkeepReduction, DefenseBonus } public static class RoleUtils { public static bool HasRole(PlayerClanInfo player, ClanRole role) { return GetRoleFactor(player, role) > 0f; } public static float GetRoleFactor(PlayerClanInfo player, ClanRole role) { return GetRoleSlotEffectiveness(GetRoleSlot(player, role)); } public static RoleSlot GetRoleSlot(PlayerClanInfo player, ClanRole role) { if (player == null || role == ClanRole.None) { return RoleSlot.None; } player.NormalizeRoles(); if (player.PrimaryRole == role) { return RoleSlot.Primary; } ConfigEntry enableSecondaryRole = ClanConfig.EnableSecondaryRole; if (enableSecondaryRole != null && enableSecondaryRole.Value && player.SecondaryRole == role) { return RoleSlot.Secondary; } ConfigEntry enableTertiaryRole = ClanConfig.EnableTertiaryRole; if (enableTertiaryRole != null && enableTertiaryRole.Value && player.TertiaryRole == role) { return RoleSlot.Tertiary; } return RoleSlot.None; } public static float GetRoleSlotEffectiveness(RoleSlot slot) { return slot switch { RoleSlot.Primary => 1f, RoleSlot.Secondary => (ClanConfig.SecondaryRoleEffectPercent?.Value ?? 60f) / 100f, RoleSlot.Tertiary => (ClanConfig.TertiaryRoleEffectPercent?.Value ?? 40f) / 100f, _ => 0f, }; } public static float GetRoleModifier(PlayerClanInfo player, ClanRole role, RoleModifier modifier, float baseValue) { return baseValue * GetRoleFactor(player, role); } public static float GetRoleModifier(PlayerClanInfo player, RoleModifier modifier, float baseValue) { return modifier switch { RoleModifier.MapDiscoveryBonus => GetRoleModifier(player, ClanRole.Explorer, modifier, baseValue), RoleModifier.UpgradeCostReduction => GetRoleModifier(player, ClanRole.Builder, modifier, baseValue), RoleModifier.TreasuryIncomeBonus => GetRoleModifier(player, ClanRole.Merchant, modifier, baseValue), RoleModifier.UpkeepReduction => GetRoleModifier(player, ClanRole.Warden, modifier, baseValue), RoleModifier.DefenseBonus => GetRoleModifier(player, ClanRole.Guardian, modifier, baseValue), _ => 0f, }; } } [HarmonyPatch] public static class SharedClanCommands { [Serializable] [CompilerGenerated] private sealed class <>c { public static readonly <>c <>9 = new <>c(); public static ConsoleEvent <>9__0_0; public static ConsoleEvent <>9__0_1; public static ConsoleEvent <>9__0_2; internal void b__0_0(ConsoleEventArgs args) { args.Context.AddString(((Object)(object)WarfareManager.Instance != (Object)null) ? WarfareManager.Instance.SharedStatus() : "Warfare manager not ready."); } internal void b__0_1(ConsoleEventArgs args) { args.Context.AddString(((Object)(object)WarfareManager.Instance != (Object)null) ? WarfareManager.Instance.ReloadSharedClans() : "Warfare manager not ready."); } internal void b__0_2(ConsoleEventArgs args) { args.Context.AddString(((Object)(object)WarfareManager.Instance != (Object)null) ? WarfareManager.Instance.BackupSharedClans() : "Warfare manager not ready."); } } [HarmonyPatch(typeof(Terminal), "InitTerminal")] [HarmonyPostfix] public static void RegisterCommands() { //IL_0032: Unknown result type (might be due to invalid IL or missing references) //IL_001e: 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_0029: Expected O, but got Unknown //IL_006a: Unknown result type (might be due to invalid IL or missing references) //IL_0056: Unknown result type (might be due to invalid IL or missing references) //IL_005b: Unknown result type (might be due to invalid IL or missing references) //IL_0061: Expected O, but got Unknown //IL_00a2: Unknown result type (might be due to invalid IL or missing references) //IL_008e: Unknown result type (might be due to invalid IL or missing references) //IL_0093: Unknown result type (might be due to invalid IL or missing references) //IL_0099: Expected O, but got Unknown object obj = <>c.<>9__0_0; if (obj == null) { ConsoleEvent val = delegate(ConsoleEventArgs args) { args.Context.AddString(((Object)(object)WarfareManager.Instance != (Object)null) ? WarfareManager.Instance.SharedStatus() : "Warfare manager not ready."); }; <>c.<>9__0_0 = val; obj = (object)val; } new ConsoleCommand("fcw_shared_status", "Show shared clan storage status.", (ConsoleEvent)obj, true, false, false, false, false, (ConsoleOptionsFetcher)null, false, false, false); object obj2 = <>c.<>9__0_1; if (obj2 == null) { ConsoleEvent val2 = delegate(ConsoleEventArgs args) { args.Context.AddString(((Object)(object)WarfareManager.Instance != (Object)null) ? WarfareManager.Instance.ReloadSharedClans() : "Warfare manager not ready."); }; <>c.<>9__0_1 = val2; obj2 = (object)val2; } new ConsoleCommand("fcw_shared_reload", "Reload shared clan JSON files and sync clients.", (ConsoleEvent)obj2, true, false, false, false, false, (ConsoleOptionsFetcher)null, false, false, false); object obj3 = <>c.<>9__0_2; if (obj3 == null) { ConsoleEvent val3 = delegate(ConsoleEventArgs args) { args.Context.AddString(((Object)(object)WarfareManager.Instance != (Object)null) ? WarfareManager.Instance.BackupSharedClans() : "Warfare manager not ready."); }; <>c.<>9__0_2 = val3; obj3 = (object)val3; } new ConsoleCommand("fcw_shared_backup", "Create backups of shared clan JSON files.", (ConsoleEvent)obj3, true, false, false, false, false, (ConsoleOptionsFetcher)null, false, false, false); } } public static class SharedClanJson { private sealed class Parser { private readonly string _json; private int _index; public Parser(string json) { _json = json; } public object ParseValue() { SkipWhite(); if (_index >= _json.Length) { return null; } char c = _json[_index]; switch (c) { case '{': return ParseObject(); case '[': return ParseArray(); case '"': return ParseString(); default: if (char.IsDigit(c) || c == '-') { return ParseNumber(); } if (Match("true")) { return true; } if (Match("false")) { return false; } if (Match("null")) { return null; } throw new FormatException("Invalid JSON value at " + _index); } } private Dictionary ParseObject() { Dictionary dictionary = new Dictionary(); _index++; SkipWhite(); if (Peek('}')) { _index++; return dictionary; } while (true) { SkipWhite(); string key = ParseString(); SkipWhite(); Expect(':'); dictionary[key] = ParseValue(); SkipWhite(); if (Peek('}')) { break; } Expect(','); } _index++; return dictionary; } private List ParseArray() { List list = new List(); _index++; SkipWhite(); if (Peek(']')) { _index++; return list; } while (true) { list.Add(ParseValue()); SkipWhite(); if (Peek(']')) { break; } Expect(','); } _index++; return list; } private string ParseString() { Expect('"'); StringBuilder stringBuilder = new StringBuilder(); while (_index < _json.Length) { char c = _json[_index++]; switch (c) { case '"': return stringBuilder.ToString(); default: stringBuilder.Append(c); continue; case '\\': break; } if (_index >= _json.Length) { break; } char c2 = _json[_index++]; switch (c2) { case '"': case '/': case '\\': stringBuilder.Append(c2); break; case 'b': stringBuilder.Append('\b'); break; case 'f': stringBuilder.Append('\f'); break; case 'n': stringBuilder.Append('\n'); break; case 'r': stringBuilder.Append('\r'); break; case 't': stringBuilder.Append('\t'); break; case 'u': { string s = _json.Substring(_index, 4); stringBuilder.Append((char)int.Parse(s, NumberStyles.HexNumber, CultureInfo.InvariantCulture)); _index += 4; break; } default: stringBuilder.Append(c2); break; } } throw new FormatException("Unterminated JSON string."); } private object ParseNumber() { int index = _index; if (Peek('-')) { _index++; } while (_index < _json.Length && char.IsDigit(_json[_index])) { _index++; } bool flag = false; if (Peek('.')) { flag = true; _index++; while (_index < _json.Length && char.IsDigit(_json[_index])) { _index++; } } if (_index < _json.Length && (_json[_index] == 'e' || _json[_index] == 'E')) { flag = true; _index++; if (_index < _json.Length && (_json[_index] == '+' || _json[_index] == '-')) { _index++; } while (_index < _json.Length && char.IsDigit(_json[_index])) { _index++; } } string s = _json.Substring(index, _index - index); if (!flag) { return long.Parse(s, CultureInfo.InvariantCulture); } return double.Parse(s, CultureInfo.InvariantCulture); } private void SkipWhite() { while (_index < _json.Length && char.IsWhiteSpace(_json[_index])) { _index++; } } private bool Match(string text) { if (_index + text.Length > _json.Length || string.Compare(_json, _index, text, 0, text.Length, StringComparison.Ordinal) != 0) { return false; } _index += text.Length; return true; } private bool Peek(char c) { if (_index < _json.Length) { return _json[_index] == c; } return false; } private void Expect(char c) { SkipWhite(); if (_index >= _json.Length || _json[_index] != c) { throw new FormatException("Expected '" + c + "' at " + _index); } _index++; } } public static string Serialize(T value) { StringBuilder stringBuilder = new StringBuilder(4096); WriteValue(stringBuilder, value); return stringBuilder.ToString(); } public static T Deserialize(string json) { if (string.IsNullOrWhiteSpace(json)) { return default(T); } object value = new Parser(json).ParseValue(); return (T)ConvertTo(typeof(T), value); } private static void WriteValue(StringBuilder sb, object value) { if (value == null) { sb.Append("null"); return; } Type type = value.GetType(); if (type == typeof(string)) { WriteString(sb, (string)value); return; } if (type == typeof(bool)) { sb.Append(((bool)value) ? "true" : "false"); return; } if (type.IsEnum) { WriteString(sb, value.ToString()); return; } if (IsNumber(type)) { sb.Append(Convert.ToString(value, CultureInfo.InvariantCulture)); return; } if (value is IList list) { sb.Append('['); for (int i = 0; i < list.Count; i++) { if (i > 0) { sb.Append(','); } WriteValue(sb, list[i]); } sb.Append(']'); return; } sb.Append('{'); bool flag = true; FieldInfo[] fields = type.GetFields(BindingFlags.Instance | BindingFlags.Public); foreach (FieldInfo fieldInfo in fields) { if (!flag) { sb.Append(','); } flag = false; WriteString(sb, fieldInfo.Name); sb.Append(':'); WriteValue(sb, fieldInfo.GetValue(value)); } sb.Append('}'); } private static object ConvertTo(Type targetType, object value) { if (value == null) { if (!targetType.IsValueType) { return null; } return Activator.CreateInstance(targetType); } if (targetType == typeof(string)) { return Convert.ToString(value, CultureInfo.InvariantCulture) ?? ""; } if (targetType == typeof(bool)) { return Convert.ToBoolean(value, CultureInfo.InvariantCulture); } if (targetType.IsEnum) { if (!(value is string value2)) { return Enum.ToObject(targetType, Convert.ToInt32(value, CultureInfo.InvariantCulture)); } return Enum.Parse(targetType, value2); } if (targetType == typeof(int)) { return Convert.ToInt32(value, CultureInfo.InvariantCulture); } if (targetType == typeof(long)) { return Convert.ToInt64(value, CultureInfo.InvariantCulture); } if (targetType == typeof(float)) { return Convert.ToSingle(value, CultureInfo.InvariantCulture); } if (targetType == typeof(double)) { return Convert.ToDouble(value, CultureInfo.InvariantCulture); } if (typeof(IList).IsAssignableFrom(targetType)) { IList list = (IList)Activator.CreateInstance(targetType); Type targetType2 = (targetType.IsGenericType ? targetType.GetGenericArguments()[0] : typeof(object)); if (value is List list2) { foreach (object item in list2) { list.Add(ConvertTo(targetType2, item)); } } return list; } object obj = Activator.CreateInstance(targetType); if (value is Dictionary dictionary) { FieldInfo[] fields = targetType.GetFields(BindingFlags.Instance | BindingFlags.Public); foreach (FieldInfo fieldInfo in fields) { if (dictionary.TryGetValue(fieldInfo.Name, out var value3)) { fieldInfo.SetValue(obj, ConvertTo(fieldInfo.FieldType, value3)); } } } return obj; } private static bool IsNumber(Type type) { if (!(type == typeof(byte)) && !(type == typeof(short)) && !(type == typeof(ushort)) && !(type == typeof(int)) && !(type == typeof(uint)) && !(type == typeof(long)) && !(type == typeof(ulong)) && !(type == typeof(float)) && !(type == typeof(double))) { return type == typeof(decimal); } return true; } private static void WriteString(StringBuilder sb, string value) { sb.Append('"'); string text = value ?? ""; foreach (char c in text) { switch (c) { case '"': sb.Append("\\\""); continue; case '\\': sb.Append("\\\\"); continue; case '\n': sb.Append("\\n"); continue; case '\r': sb.Append("\\r"); continue; case '\t': sb.Append("\\t"); continue; } if (char.IsControl(c)) { sb.Append("\\u"); int num = c; sb.Append(num.ToString("x4", CultureInfo.InvariantCulture)); } else { sb.Append(c); } } sb.Append('"'); } } public class SharedClanStore { private const string ClansFileName = "clans_global.json"; private const string MembersFileName = "members_global.json"; private const string RanksFileName = "ranks_global.json"; private const string ElectionsFileName = "elections_global.json"; private const string DiplomacyFileName = "diplomacy_global.json"; private readonly Dictionary _lastWriteTimes = new Dictionary(); private string _path = ""; private string _serverId = ""; private DateTime _lastLoadUtc = DateTime.MinValue; private DateTime _lastSaveUtc = DateTime.MinValue; private int _lastClanCount; private int _lastMemberCount; private bool _loaded; private bool _filesCreatedThisRun; private bool _initialized; private bool _clientSkipLogged; public bool Enabled => ClanConfig.EnableSharedClans?.Value ?? false; public string PathRoot => _path; public string ServerId => _serverId; public DateTime LastLoadUtc => _lastLoadUtc; public DateTime LastSaveUtc => _lastSaveUtc; public int LastClanCount => _lastClanCount; public int LastMemberCount => _lastMemberCount; public bool Loaded => _loaded; private bool IsServerRuntime { get { WarfareManager instance = WarfareManager.Instance; if (instance == null || !instance.IsServer) { if ((Object)(object)ZNet.instance != (Object)null) { return ZNet.instance.IsServer(); } return false; } return true; } } public void Initialize() { if (_initialized) { return; } if (!Enabled || !IsServerRuntime) { if (!_clientSkipLogged) { Plugin.Log.LogInfo((object)("[FCW Shared] IsServer=" + IsServerRuntime)); _clientSkipLogged = true; } return; } Plugin.Log.LogInfo((object)("[FCW Shared] IsServer=" + IsServerRuntime)); _path = ResolvePath(); _serverId = ResolveServerId(); Plugin.Log.LogInfo((object)("[FCW] SharedClans enabled: " + Enabled)); Plugin.Log.LogInfo((object)("[FCW] SharedClans configured path: " + (ClanConfig.SharedClansPath?.Value ?? "auto"))); Plugin.Log.LogInfo((object)("[FCW] SharedClans resolved path: " + _path)); Plugin.Log.LogInfo((object)("[FCW Shared] ResolvedPath=" + _path)); Directory.CreateDirectory(_path); Directory.CreateDirectory(Path.Combine(_path, "locks")); Directory.CreateDirectory(Path.Combine(_path, "backups")); _filesCreatedThisRun |= EnsureFile("clans_global.json", new SharedClansFile()); _filesCreatedThisRun |= EnsureFile("members_global.json", new SharedMembersFile()); _filesCreatedThisRun |= EnsureFile("ranks_global.json", new SharedRanksFile()); _filesCreatedThisRun |= EnsureFile("elections_global.json", new SharedElectionsFile()); _filesCreatedThisRun |= EnsureFile("diplomacy_global.json", new SharedDiplomacyFile()); if (_filesCreatedThisRun && _lastSaveUtc == DateTime.MinValue) { _lastSaveUtc = DateTime.UtcNow; } RefreshWriteTimes(); if (_lastSaveUtc == DateTime.MinValue && _lastWriteTimes.Count > 0) { DateTime dateTime = _lastWriteTimes.Values.DefaultIfEmpty(DateTime.MinValue).Max(); if (dateTime != DateTime.MinValue) { _lastSaveUtc = dateTime; } } Plugin.Log.LogInfo((object)("[FCW Shared] Initialized path=" + _path + " serverId=" + _serverId)); Plugin.Log.LogInfo((object)("[FCW] SharedClans files found: " + CountFilesFound() + "/5")); _initialized = true; } public void LoadInto(WarfareData data) { if (Enabled && data != null && IsServerRuntime) { Initialize(); SharedClansFile clans = ReadFile("clans_global.json", new SharedClansFile()); SharedMembersFile members = ReadFile("members_global.json", new SharedMembersFile()); SharedRanksFile ranks = ReadFile("ranks_global.json", new SharedRanksFile()); SharedElectionsFile elections = ReadFile("elections_global.json", new SharedElectionsFile()); SharedDiplomacyFile diplomacy = ReadFile("diplomacy_global.json", new SharedDiplomacyFile()); Apply(data, clans, members, ranks, elections, diplomacy); _lastLoadUtc = DateTime.UtcNow; _lastClanCount = data.Clans.Count; _lastMemberCount = data.Players.Values.Count((PlayerClanInfo p) => p.ClanId != 0); _loaded = true; RefreshWriteTimes(); Plugin.Log.LogInfo((object)("[FCW Shared] Loaded global clans=" + _lastClanCount + " members=" + _lastMemberCount)); Plugin.Log.LogInfo((object)("[FCW Shared] Loaded clans=" + _lastClanCount + " members=" + _lastMemberCount)); } } public void SaveFrom(WarfareData data) { if (Enabled && data != null && IsServerRuntime) { Initialize(); WriteFile("clans_global.json", BuildClans(data)); WriteFile("members_global.json", BuildMembers(data)); WriteFile("ranks_global.json", BuildRanks(data)); WriteFile("elections_global.json", BuildElections(data)); WriteFile("diplomacy_global.json", BuildDiplomacy(data)); _lastSaveUtc = DateTime.UtcNow; _lastClanCount = data.Clans.Count; _lastMemberCount = data.Players.Values.Count((PlayerClanInfo p) => p.ClanId != 0); _loaded = true; RefreshWriteTimes(); Plugin.Log.LogInfo((object)("[FCW Shared] Saved global clans=" + _lastClanCount + " members=" + _lastMemberCount)); Plugin.Log.LogInfo((object)"[FCW Shared] Saved successfully"); } } public bool ReloadIfChanged(WarfareData data) { if (!Enabled || data == null || !IsServerRuntime) { return false; } Initialize(); foreach (string item in FileNames()) { string path = FullPath(item); DateTime dateTime = (File.Exists(path) ? File.GetLastWriteTimeUtc(path) : DateTime.MinValue); if (!_lastWriteTimes.TryGetValue(item, out var value) || dateTime > value) { LoadInto(data); return true; } } return false; } public string Status() { if (!Enabled) { return "Shared clans disabled."; } if (!IsServerRuntime) { return "Shared clans\nIsServer: false\nResolvedPath: server only\nFilesFound: 0/5\nClansCount: 0\nMembersCount: 0\nLastLoad: never\nLastSave: never"; } Initialize(); return "Shared clans: enabled\nIsServer: true\nResolvedPath: " + _path + "\nServerId: " + _serverId + "\nFilesFound: " + CountFilesFound() + "/5\nClansCount: " + _lastClanCount + "\nMembersCount: " + _lastMemberCount + "\nLastLoad: " + FormatTime(_lastLoadUtc) + "\nLastSave: " + FormatTime(_lastSaveUtc); } public string BackupNow() { if (!Enabled) { return "Shared clans disabled."; } if (!IsServerRuntime) { return "Shared clans backup must run on server."; } Initialize(); foreach (string item in FileNames()) { BackupFile(item); } return "Shared clan backup created in " + Path.Combine(_path, "backups"); } private void Apply(WarfareData data, SharedClansFile clans, SharedMembersFile members, SharedRanksFile ranks, SharedElectionsFile elections, SharedDiplomacyFile diplomacy) { HashSet hashSet = new HashSet((clans.Clans ?? new List()).Select((SharedClanDto c) => c.ClanId)); foreach (int item in data.Clans.Keys.ToList()) { if (!hashSet.Contains(item)) { ReleaseLocalClanData(data, item); data.Clans.Remove(item); } } foreach (SharedClanDto item2 in clans.Clans ?? new List()) { if (!data.Clans.TryGetValue(item2.ClanId, out var value)) { value = new Clan { ClanId = item2.ClanId, Treasury = 0L, VaultTier = 1, ResourceTaxPercent = (ClanConfig.DefaultClanResourceTaxPercent?.Value ?? 10), CoinTaxPercent = (ClanConfig.DefaultClanCoinTaxPercent?.Value ?? 10) }; data.Clans[item2.ClanId] = value; } ApplyClanDto(value, item2); } data.NextClanId = Math.Max(clans.NextClanId, data.Clans.Keys.DefaultIfEmpty(0).Max() + 1); ApplyMembers(data, members, ranks); ApplyElections(data, elections); data.NextAllianceId = Math.Max(diplomacy.NextAllianceId, 1); data.Diplomacy = diplomacy.Diplomacy ?? new List(); data.Alliances = diplomacy.Alliances ?? new List(); data.NonAggressionPacts = diplomacy.NonAggressionPacts ?? new List(); data.VassalRelations = diplomacy.VassalRelations ?? new List(); } private void ReleaseLocalClanData(WarfareData data, int clanId) { foreach (string item in (from pair in data.Territories where pair.Value.OwnerClanId == clanId select pair.Key).ToList()) { data.Territories.Remove(item); } data.ClanVaults.Remove(clanId); data.Wars.RemoveAll((War w) => w.Involves(clanId)); data.ActiveCaptures.RemoveAll((CaptureSession c) => c.AttackerClanId == clanId || c.DefenderClanId == clanId); data.ActiveCracks.RemoveAll((VaultCrackSession c) => c.PlayerClanId == clanId || c.TargetClanId == clanId); } private void ApplyClanDto(Clan clan, SharedClanDto dto) { clan.ClanId = dto.ClanId; clan.Name = dto.Name ?? ""; clan.Tag = dto.Tag ?? ""; clan.IconIndex = dto.IconIndex; clan.ColorR = dto.ColorR; clan.ColorG = dto.ColorG; clan.ColorB = dto.ColorB; clan.FounderId = dto.FounderId; clan.JarlId = dto.JarlId; clan.CreatedTimestamp = dto.CreatedTimestamp; } private void ApplyMembers(WarfareData data, SharedMembersFile members, SharedRanksFile ranks) { Dictionary dictionary = (members.Members ?? new List()).ToDictionary((SharedMemberDto m) => m.PlayerId, (SharedMemberDto m) => m); Dictionary dictionary2 = (ranks.Ranks ?? new List()).ToDictionary((SharedRankDto r) => r.PlayerId, (SharedRankDto r) => r); foreach (PlayerClanInfo value3 in data.Players.Values) { if (!dictionary.ContainsKey(value3.PlayerId) && value3.ClanId != 0) { value3.ClanId = 0; value3.Rank = ClanRank.Member; value3.Role = ClanRole.None; value3.PrimaryRole = ClanRole.None; value3.SecondaryRole = ClanRole.None; value3.TertiaryRole = ClanRole.None; } } foreach (SharedMemberDto value4 in dictionary.Values) { if (!data.Players.TryGetValue(value4.PlayerId, out var value)) { value = new PlayerClanInfo { PlayerId = value4.PlayerId }; data.Players[value4.PlayerId] = value; } value.PlayerName = value4.PlayerName ?? value.PlayerName ?? ""; value.ClanId = value4.ClanId; value.Rank = (dictionary2.TryGetValue(value4.PlayerId, out var value2) ? value2.Rank : value4.Rank); value.Role = value4.Role; value.PrimaryRole = value4.PrimaryRole; value.SecondaryRole = value4.SecondaryRole; value.TertiaryRole = value4.TertiaryRole; value.JoinedClanTimestamp = value4.JoinedClanTimestamp; value.LastClanLeaveTimestamp = value4.LastClanLeaveTimestamp; value.NormalizeRoles(); } } private void ApplyElections(WarfareData data, SharedElectionsFile elections) { foreach (Clan value2 in data.Clans.Values) { value2.ElectionVotes = new Dictionary(); } foreach (SharedElectionDto item in elections.Elections ?? new List()) { if (!data.Clans.TryGetValue(item.ClanId, out var value)) { continue; } value.JarlId = item.JarlId; value.NextElectionTimestamp = item.NextElectionTimestamp; value.ElectionVotes = new Dictionary(); foreach (SharedElectionVoteDto item2 in item.Votes ?? new List()) { value.ElectionVotes[item2.VoterId] = item2.CandidateId; } } } private SharedClansFile BuildClans(WarfareData data) { return new SharedClansFile { NextClanId = data.NextClanId, Clans = (from c in data.Clans.Values orderby c.ClanId select new SharedClanDto { ClanId = c.ClanId, Name = (c.Name ?? ""), Tag = (c.Tag ?? ""), IconIndex = c.IconIndex, ColorR = c.ColorR, ColorG = c.ColorG, ColorB = c.ColorB, FounderId = c.FounderId, JarlId = c.JarlId, CreatedTimestamp = c.CreatedTimestamp }).ToList() }; } private SharedMembersFile BuildMembers(WarfareData data) { return new SharedMembersFile { Members = (from p in data.Players.Values where p.ClanId != 0 orderby p.PlayerId select new SharedMemberDto { PlayerId = p.PlayerId, PlayerName = (p.PlayerName ?? ""), ClanId = p.ClanId, Rank = p.Rank, Role = p.Role, PrimaryRole = p.PrimaryRole, SecondaryRole = p.SecondaryRole, TertiaryRole = p.TertiaryRole, JoinedClanTimestamp = p.JoinedClanTimestamp, LastClanLeaveTimestamp = p.LastClanLeaveTimestamp }).ToList() }; } private SharedRanksFile BuildRanks(WarfareData data) { return new SharedRanksFile { Ranks = (from p in data.Players.Values where p.ClanId != 0 orderby p.PlayerId select new SharedRankDto { PlayerId = p.PlayerId, ClanId = p.ClanId, Rank = p.Rank }).ToList() }; } private SharedElectionsFile BuildElections(WarfareData data) { return new SharedElectionsFile { Elections = (from c in data.Clans.Values orderby c.ClanId select new SharedElectionDto { ClanId = c.ClanId, JarlId = c.JarlId, NextElectionTimestamp = c.NextElectionTimestamp, Votes = (c.ElectionVotes ?? new Dictionary()).Select((KeyValuePair v) => new SharedElectionVoteDto { VoterId = v.Key, CandidateId = v.Value }).ToList() }).ToList() }; } private SharedDiplomacyFile BuildDiplomacy(WarfareData data) { return new SharedDiplomacyFile { NextAllianceId = data.NextAllianceId, Diplomacy = (data.Diplomacy ?? new List()), Alliances = (data.Alliances ?? new List()), NonAggressionPacts = (data.NonAggressionPacts ?? new List()), VassalRelations = (data.VassalRelations ?? new List()) }; } private string ResolvePath() { string text = ClanConfig.SharedClansPath?.Value ?? "auto"; if (!string.IsNullOrWhiteSpace(text) && !string.Equals(text.Trim(), "auto", StringComparison.OrdinalIgnoreCase)) { return text.Trim(); } return Path.Combine(Paths.GameRootPath, "Saves", "shared_clans"); } private string ResolveServerId() { string text = ClanConfig.ServerId?.Value ?? "auto"; if (!string.IsNullOrWhiteSpace(text) && !string.Equals(text, "auto", StringComparison.OrdinalIgnoreCase)) { return text.Trim(); } return Environment.MachineName; } private bool EnsureFile(string name, T empty) { string path = FullPath(name); if (!File.Exists(path)) { WriteJson(path, empty); Plugin.Log.LogInfo((object)("[FCW Shared] Created file=" + name)); return true; } return false; } private T ReadFile(string name, T fallback) { string path = FullPath(name); try { T val = ReadJson(path); return (val == null) ? fallback : val; } catch (Exception ex) { LogException("Read failed for " + name, ex); return fallback; } } private void WriteFile(string name, T data) { string text = FullPath(name); string text2 = text + ".tmp"; using (AcquireLock(name)) { BackupFile(name); WriteJson(text2, data); if (File.Exists(text)) { File.Delete(text); } File.Move(text2, text); } } private void WriteJson(string path, T data) { string contents = SharedClanJson.Serialize(data); File.WriteAllText(path, contents, Encoding.UTF8); } private T ReadJson(string path) { string text = File.ReadAllText(path, Encoding.UTF8); if (string.IsNullOrWhiteSpace(text)) { return default(T); } return SharedClanJson.Deserialize(text); } private IDisposable AcquireLock(string name) { string path = Path.Combine(_path, "locks", name + ".lock"); DateTime dateTime = DateTime.UtcNow.AddSeconds(5.0); while (true) { try { return new FileStream(path, FileMode.OpenOrCreate, FileAccess.ReadWrite, FileShare.None); } catch (IOException) { if (DateTime.UtcNow >= dateTime) { throw; } Thread.Sleep(50); } } } private void BackupFile(string name) { string text = FullPath(name); if (File.Exists(text)) { string text2 = DateTime.UtcNow.ToString("yyyyMMdd_HHmmss"); string destFileName = Path.Combine(_path, "backups", name + "." + text2 + ".bak"); File.Copy(text, destFileName, overwrite: true); } } private string FullPath(string name) { return Path.Combine(_path, name); } private void RefreshWriteTimes() { foreach (string item in FileNames()) { string path = FullPath(item); _lastWriteTimes[item] = (File.Exists(path) ? File.GetLastWriteTimeUtc(path) : DateTime.MinValue); } } private static IEnumerable FileNames() { yield return "clans_global.json"; yield return "members_global.json"; yield return "ranks_global.json"; yield return "elections_global.json"; yield return "diplomacy_global.json"; } private static string FormatTime(DateTime time) { if (!(time == DateTime.MinValue)) { return time.ToLocalTime().ToString("yyyy-MM-dd HH:mm:ss"); } return "never"; } private int CountFilesFound() { return FileNames().Count((string file) => File.Exists(FullPath(file))); } private void LogException(string context, Exception ex) { Plugin.Log.LogError((object)("[FCW Shared] " + context + "\nType: " + ex.GetType().FullName + "\nMessage: " + ex.Message + "\nStack:\n" + ex)); } } public static class TerritoryRules { public static bool CanBuild(PlayerClanInfo player, ClanTerritory territory) { if (!RealmRules.GetBool("building_restriction")) { return true; } if (player == null) { return false; } if (territory == null) { if (!player.IsFreeborn) { return player.ClanId != 0; } return true; } if (player.ClanId != 0 && territory.OwnerClanId == player.ClanId) { return true; } if (player.IsFreeborn && territory.FreebornOwnerId == player.PlayerId) { return true; } return false; } public static bool CanDamageBuilding(Player attacker, Piece piece) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) //IL_000c: Expected O, but got Unknown //IL_000f: Unknown result type (might be due to invalid IL or missing references) //IL_001a: Expected O, but got Unknown //IL_0047: Unknown result type (might be due to invalid IL or missing references) //IL_004c: Unknown result type (might be due to invalid IL or missing references) if ((Object)attacker == (Object)null || (Object)piece == (Object)null) { return true; } if (PvPManager.HasAdminPvpBypass(attacker)) { return true; } WarfareData warfareData = WarfareManager.Instance?.Data; ClanTerritory clanTerritory = warfareData?.GetTerritory(GridExtensions.WorldToGrid(((Component)piece).transform.position)); if (!CanRaid(attacker, clanTerritory)) { return false; } if (RealmRules.GetBool("building_damage")) { return true; } PlayerClanInfo playerClanInfo = warfareData?.GetPlayer(attacker.GetPlayerID()); if (playerClanInfo == null || playerClanInfo.ClanId == 0 || clanTerritory == null || clanTerritory.OwnerClanId == 0) { return false; } return WarfareManager.Instance?.AreAtWar(playerClanInfo.ClanId, clanTerritory.OwnerClanId) ?? false; } public static bool CanClaim(PlayerClanInfo player, ClanTerritory territory, Vector2i grid, out string message) { //IL_001d: Unknown result type (might be due to invalid IL or missing references) //IL_0028: Unknown result type (might be due to invalid IL or missing references) message = null; if (!RealmRules.GetBool("claim_restrictions")) { return true; } if (player == null) { message = "Join a clan or choose the Freeborn path first!"; return false; } if (ProtectedWorldZones.TryGetProtectedZone(grid, out var _)) { message = ProtectedWorldZones.GetBlockedActionMessage(grid); return false; } if (territory != null && RealmRules.GetBool("raid_protection")) { WarfareManager instance = WarfareManager.Instance; if (instance != null && instance.IsFreebornProtectedTerritory(territory)) { message = "Odin's Peace protects this Freeborn territory. Challenge it first."; return false; } } return true; } public static bool CanRaid(Player attacker, ClanTerritory territory) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) //IL_000c: Expected O, but got Unknown if ((Object)attacker == (Object)null || territory == null || !RealmRules.GetBool("raid_protection")) { return true; } if (PvPManager.HasAdminPvpBypass(attacker)) { return true; } if (ProtectedWorldZones.IsProtected(territory)) { return false; } WarfareManager instance = WarfareManager.Instance; if (instance != null && instance.IsFreebornProtectedTerritory(territory)) { return false; } int ownerClanId = territory.OwnerClanId; PlayerClanInfo playerClanInfo = WarfareManager.Instance?.Data?.GetPlayer(attacker.GetPlayerID()); if (ownerClanId != 0 && playerClanInfo != null && playerClanInfo.ClanId != 0) { WarfareManager instance2 = WarfareManager.Instance; if (instance2 != null && instance2.AreAtWar(playerClanInfo.ClanId, ownerClanId)) { return true; } } if (ownerClanId != 0) { WarfareManager instance3 = WarfareManager.Instance; if (instance3 != null && instance3.ShouldReduceForOffline(ownerClanId)) { return false; } } return true; } public static bool CanUsePortal(Player player, TeleportWorld portal, out string message) { //IL_0004: Unknown result type (might be due to invalid IL or missing references) //IL_000f: Expected O, but got Unknown //IL_005b: Unknown result type (might be due to invalid IL or missing references) //IL_0060: Unknown result type (might be due to invalid IL or missing references) message = null; if ((Object)player == (Object)null || !RealmRules.GetBool("portal_restrictions")) { return true; } if (PvPManager.HasAdminPvpBypass(player)) { return true; } WarfareData obj = WarfareManager.Instance?.Data; PlayerClanInfo playerClanInfo = obj?.GetPlayer(player.GetPlayerID()); ClanTerritory clanTerritory = obj?.GetTerritory(GridExtensions.WorldToGrid(((Component)player).transform.position)); if (clanTerritory == null || playerClanInfo == null) { return true; } if (clanTerritory.OwnerClanId == 0 && clanTerritory.FreebornOwnerId == 0L) { return true; } if (playerClanInfo.ClanId != 0 && clanTerritory.OwnerClanId == playerClanInfo.ClanId) { return true; } if (playerClanInfo.IsFreeborn && clanTerritory.FreebornOwnerId == playerClanInfo.PlayerId) { return true; } if (playerClanInfo.ClanId != 0 && clanTerritory.OwnerClanId != 0) { WarfareManager instance = WarfareManager.Instance; if (instance != null && instance.GetDiplomacyState(playerClanInfo.ClanId, clanTerritory.OwnerClanId) == DiplomacyState.Allied) { return true; } } message = "The border laws forbid portal travel from this territory."; return false; } } public class WarfareManager : MonoBehaviour { public sealed class AdminAccessInfo { public bool DebugMode; public bool LocalHost; public bool ServerAdmin; public bool ExistingFcwDebug; public bool AuthorizedSteamId; public string MatchedSteamId = ""; public bool EventAdminAccess => AuthorizedSteamId; } public class ClaimResult { public bool Success; public string Message; public ClaimResult(bool s, string m) { Success = s; Message = m; } } private const string HardcodedOwnerSteamId = "76561198007625196"; public const string RootTotemPrefabName = "Fehu_RotTotem"; public const string RootTotemZdoKey = "Fehu_RotTotem_TotemId"; private static readonly TerritoryBuildingType[] BuildableBuildingTypes = new TerritoryBuildingType[27] { TerritoryBuildingType.TownCenter, TerritoryBuildingType.ClanHall, TerritoryBuildingType.Longhouse, TerritoryBuildingType.Warehouse, TerritoryBuildingType.Treasury, TerritoryBuildingType.TradePost, TerritoryBuildingType.Marketplace, TerritoryBuildingType.TaxOffice, TerritoryBuildingType.Embassy, TerritoryBuildingType.LogisticsPost, TerritoryBuildingType.LumberCamp, TerritoryBuildingType.Quarry, TerritoryBuildingType.CopperMine, TerritoryBuildingType.IronMine, TerritoryBuildingType.SilverMine, TerritoryBuildingType.Farm, TerritoryBuildingType.FishingPort, TerritoryBuildingType.HunterLodge, TerritoryBuildingType.Apiary, TerritoryBuildingType.Tannery, TerritoryBuildingType.Workshop, TerritoryBuildingType.Smithy, TerritoryBuildingType.Shipyard, TerritoryBuildingType.Barracks, TerritoryBuildingType.Watchtower, TerritoryBuildingType.Shrine, TerritoryBuildingType.HealerHut }; private static readonly string[] BuildingCategoryNames = new string[8] { "Admin", "Economy", "Diplomacy", "Infrastructure", "Resources", "Food", "Craft", "Military" }; private static readonly TerritoryBuildingType[][] BuildingCategoryTypes = new TerritoryBuildingType[8][] { new TerritoryBuildingType[4] { TerritoryBuildingType.TownCenter, TerritoryBuildingType.ClanHall, TerritoryBuildingType.Longhouse, TerritoryBuildingType.TaxOffice }, new TerritoryBuildingType[3] { TerritoryBuildingType.Treasury, TerritoryBuildingType.TradePost, TerritoryBuildingType.Marketplace }, new TerritoryBuildingType[1] { TerritoryBuildingType.Embassy }, new TerritoryBuildingType[2] { TerritoryBuildingType.Warehouse, TerritoryBuildingType.LogisticsPost }, new TerritoryBuildingType[5] { TerritoryBuildingType.LumberCamp, TerritoryBuildingType.Quarry, TerritoryBuildingType.CopperMine, TerritoryBuildingType.IronMine, TerritoryBuildingType.SilverMine }, new TerritoryBuildingType[4] { TerritoryBuildingType.Farm, TerritoryBuildingType.FishingPort, TerritoryBuildingType.HunterLodge, TerritoryBuildingType.Apiary }, new TerritoryBuildingType[5] { TerritoryBuildingType.Tannery, TerritoryBuildingType.Workshop, TerritoryBuildingType.Smithy, TerritoryBuildingType.Shipyard, TerritoryBuildingType.HealerHut }, new TerritoryBuildingType[3] { TerritoryBuildingType.Barracks, TerritoryBuildingType.Watchtower, TerritoryBuildingType.Shrine } }; private static readonly GuardType[] HireableGuardTypes = new GuardType[6] { GuardType.BasicGuard, GuardType.ArcherGuard, GuardType.ShieldGuard, GuardType.PatrolGuard, GuardType.TowerGuard, GuardType.EliteGuard }; private bool _isServer; private bool _dirty; private readonly HashSet _identityConflictLogs = new HashSet(StringComparer.Ordinal); private float _saveTimer; private float _captureTimer; private float _miscTimer; private float _slowTimer; private readonly HashSet _activeProfileSessions = new HashSet(); private readonly Dictionary _activeSessionProfiles = new Dictionary(); private readonly SharedClanStore _sharedClanStore = new SharedClanStore(); private bool _sharedDirty; private bool? _serverVaultOpenState; private readonly Dictionary _pendingAchievementNotifications = new Dictionary(); public static WarfareManager Instance { get; private set; } public WarfareData Data { get; private set; } = new WarfareData(); public bool IsServer => _isServer; public void SetServerVaultOpenState(bool isOpen) { _serverVaultOpenState = isOpen; } public void ClearServerVaultOpenState() { _serverVaultOpenState = null; } private void Awake() { //IL_0005: Unknown result type (might be due to invalid IL or missing references) //IL_0010: Expected O, but got Unknown //IL_0017: 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_0027: Expected O, but got Unknown //IL_0027: Expected O, but got Unknown //IL_002a: Unknown result type (might be due to invalid IL or missing references) //IL_0034: Expected O, but got Unknown if ((Object)Instance != (Object)null && (Object)Instance != (Object)this) { Object.Destroy((Object)this); } else { Instance = this; } } private void Update() { if (!_isServer) { return; } float deltaTime = Time.deltaTime; if (_dirty) { _saveTimer += deltaTime; if (_saveTimer >= 60f) { _saveTimer = 0f; SaveData(); SaveSharedDataIfDirty(); _dirty = false; } } _captureTimer += deltaTime; if (_captureTimer >= 1f) { _captureTimer = 0f; try { CaptureTick(); } catch (Exception ex) { Plugin.Log.LogError((object)("CaptureTick: " + ex.Message)); } } _miscTimer += deltaTime; if (_miscTimer >= 5f) { _miscTimer = 0f; try { ProcessVaultCracks(); VaultCrackMonitor.CheckActiveCracks(); WarExpiryTick(); WorldEventTick(); TrackConnectedPlayerActivity(); SaveSharedDataIfDirty(); if (_sharedClanStore.Enabled && _sharedClanStore.ReloadIfChanged(Data)) { LogSharedPlayerResolutions(); WarfareRPC.Instance?.SyncAllDataToClients(); } } catch (Exception ex2) { Plugin.Log.LogError((object)("MiscTick: " + ex2.Message)); } } _slowTimer += deltaTime; if (!(_slowTimer >= 60f)) { return; } _slowTimer = 0f; try { EconomyTick(); RotTick(); ElectionTick(); SeasonTick(); } catch (Exception ex3) { Plugin.Log.LogError((object)("SlowTick: " + ex3.Message)); } } public void InitServer() { _isServer = true; LoadData(); if (Data.SeasonStartTimestamp == 0L) { Data.SeasonStartTimestamp = Now(); MarkDirty(); } } public void MarkDirty() { _dirty = true; } public PlayerClanInfo AssociatePlayerIdentity(long playerId, string playerName, string steamId64) { if (Data?.Players == null || playerId == 0L) { return null; } steamId64 = (steamId64 ?? string.Empty).Trim(); bool flag = false; if (Data.Players.TryGetValue(playerId, out var value)) { if (!string.IsNullOrEmpty(steamId64)) { if (string.IsNullOrEmpty(value.SteamId64)) { value.SteamId64 = steamId64; flag = true; } else if (!string.Equals(value.SteamId64, steamId64, StringComparison.Ordinal)) { LogIdentityConflictOnce(playerId, steamId64, "PlayerID profile already has SteamID64 " + value.SteamId64); } } } else { if (string.IsNullOrEmpty(steamId64)) { return null; } value = null; foreach (PlayerClanInfo value2 in Data.Players.Values) { if (string.Equals(value2.SteamId64, steamId64, StringComparison.Ordinal)) { if (value != null && value != value2) { LogIdentityConflictOnce(playerId, steamId64, "multiple profiles have the same SteamID64"); return null; } value = value2; } } if (value == null) { return null; } PlayerClanInfo playerClanInfo = value; if (playerClanInfo.LinkedPlayerIds == null) { playerClanInfo.LinkedPlayerIds = new List(); } if (!value.LinkedPlayerIds.Contains(playerId)) { value.LinkedPlayerIds.Add(playerId); flag = true; } } if (!string.IsNullOrEmpty(playerName) && !string.Equals(value.PlayerName, playerName, StringComparison.Ordinal)) { value.PlayerName = playerName; flag = true; } if (flag) { MarkDirty(); } return value; } private void LogIdentityConflictOnce(long playerId, string steamId64, string reason) { string item = playerId + "|" + steamId64 + "|" + reason; if (_identityConflictLogs.Add(item)) { Plugin.Log.LogWarning((object)("[FCW Profile] Identity conflict preserved without merge: playerId=" + playerId + " steamId64=" + steamId64 + " reason=" + reason)); } } public string UpdateProfile(long playerId, string aboutMe, string activeTitle, string profileImageKey = "") { PlayerClanInfo player = Data.GetPlayer(playerId); if (player == null) { return "Player profile was not found."; } int num = Mathf.Clamp(ClanConfig.ProfileAboutMaxLength?.Value ?? 500, 0, 2000); aboutMe = (aboutMe ?? string.Empty).Replace("<", string.Empty).Replace(">", string.Empty).Trim(); activeTitle = (activeTitle ?? string.Empty).Replace("<", string.Empty).Replace(">", string.Empty).Trim(); profileImageKey = ValidateProfileImageKey(profileImageKey); if (aboutMe.Length > num) { aboutMe = aboutMe.Substring(0, num); } if (activeTitle.Length > 80) { activeTitle = activeTitle.Substring(0, 80); } int num2 = player.SagaEntries?.Count ?? 0; bool flag = false; if (!string.Equals(player.AboutMe, aboutMe, StringComparison.Ordinal)) { player.AboutMe = aboutMe; flag = true; } if (!string.Equals(player.ProfileImageKey, profileImageKey, StringComparison.OrdinalIgnoreCase)) { player.ProfileImageKey = profileImageKey; flag = true; } if (flag) { MarkDirty(); } Plugin.Log.LogInfo((object)("[FCW Profile] Updated profile fields for player=" + playerId + "; saga entries unchanged=" + num2)); return "Profile updated."; } public bool UpdateAboutMe(long playerId, string aboutMe, out string message) { PlayerClanInfo player = Data.GetPlayer(playerId); if (player == null) { message = "Player profile was not found."; return false; } List list = new List(player.SagaEntries ?? new List()); int count = list.Count; int num = CalculateProfileLegacyScore(player); Plugin.Log.LogInfo((object)("[FCW AboutMe] before player=" + playerId + " saga=" + count + " legacy=" + num)); int num2 = Mathf.Clamp(ClanConfig.ProfileAboutMaxLength?.Value ?? 500, 0, 2000); aboutMe = (aboutMe ?? string.Empty).Replace("<", string.Empty).Replace(">", string.Empty).Trim(); if (aboutMe.Length > num2) { aboutMe = aboutMe.Substring(0, num2); } bool flag = !string.Equals(player.AboutMe, aboutMe, StringComparison.Ordinal); if (!flag) { Plugin.Log.LogInfo((object)("[FCW AboutMe] after player=" + playerId + " saga=" + count + " legacy=" + num)); message = "About Me unchanged."; return false; } player.AboutMe = aboutMe; if (player.SagaEntries == null || !list.SequenceEqual(player.SagaEntries)) { player.SagaEntries = new List(list); Plugin.Log.LogError((object)"[FCW AboutMe] ERROR Saga mutated during AboutMe update; restored original entries"); } int num3 = player.SagaEntries?.Count ?? 0; int num4 = CalculateProfileLegacyScore(player); Plugin.Log.LogInfo((object)("[FCW AboutMe] after player=" + playerId + " saga=" + num3 + " legacy=" + num4)); MarkDirty(); message = (flag ? "About Me updated." : "About Me unchanged."); return flag; } public bool SetActiveTitle(long playerId, string requestedTitleId, out string message) { PlayerClanInfo player = Data.GetPlayer(playerId); if (player == null) { message = "Player profile was not found."; return false; } requestedTitleId = (requestedTitleId ?? string.Empty).Trim(); if (string.IsNullOrEmpty(requestedTitleId)) { if (string.IsNullOrEmpty(player.ActiveTitleId) && string.IsNullOrEmpty(player.ActiveTitle)) { message = "Title already clear."; return false; } player.ActiveTitleId = ""; player.ActiveTitle = ""; MarkDirty(); message = "Active title cleared."; return true; } PlayerTitleDefinition title = PlayerTitleCatalog.Get(requestedTitleId); if (title == null || !(player.UnlockedTitles ?? new List()).Any((string id) => string.Equals(id, title.Id, StringComparison.OrdinalIgnoreCase))) { message = "That title is not unlocked."; return false; } if (string.Equals(player.ActiveTitleId, title.Id, StringComparison.OrdinalIgnoreCase) && string.Equals(player.ActiveTitle, title.DisplayName, StringComparison.Ordinal)) { message = "Title already active."; return false; } player.ActiveTitleId = title.Id; player.ActiveTitle = title.DisplayName; MarkDirty(); message = "Active title set to " + title.DisplayName + "."; return true; } public bool EvaluateAllTitleUnlocks(bool markDirty = true) { if (!_isServer || Data?.Players == null) { return false; } bool flag = false; foreach (PlayerClanInfo value in Data.Players.Values) { flag |= EvaluatePlayerTitleUnlocks(value); } if (flag && markDirty) { MarkDirty(); } return flag; } private bool EvaluatePlayerTitleUnlocks(PlayerClanInfo player) { if (player == null) { return false; } PlayerClanInfo playerClanInfo = player; if (playerClanInfo.UnlockedTitles == null) { playerClanInfo.UnlockedTitles = new List(); } playerClanInfo = player; if (playerClanInfo.TitleUnlockTimestamps == null) { playerClanInfo.TitleUnlockTimestamps = new Dictionary(); } Clan clan = ((player.ClanId == 0) ? null : Data.GetClan(player.ClanId)); bool flag = clan != null && clan.FounderId == player.PlayerId; bool condition = player.Rank == ClanRank.Jarl || (clan != null && clan.JarlId == player.PlayerId); List serverBadges = GetServerBadges(player); bool flag2 = false; flag2 |= UnlockTitle(player, "founder", flag); flag2 |= UnlockTitle(player, "jarl", condition); flag2 |= UnlockTitle(player, "alpha_tester", serverBadges.Any((string b) => string.Equals(b, "Alpha Tester", StringComparison.OrdinalIgnoreCase))); flag2 |= UnlockTitle(player, "beta_tester", serverBadges.Any((string b) => string.Equals(b, "Beta Tester", StringComparison.OrdinalIgnoreCase))); flag2 |= UnlockTitle(player, "contributor", serverBadges.Any((string b) => string.Equals(b, "Contributor", StringComparison.OrdinalIgnoreCase))); flag2 |= UnlockTitle(player, "supporter", serverBadges.Any((string b) => string.Equals(b, "Supporter", StringComparison.OrdinalIgnoreCase))); flag2 |= UnlockTitle(player, "newcomer", player.TotalLogins > 0 || player.FirstLoginTimestamp > 0); flag2 |= UnlockTitle(player, "veteran", player.TotalPlayMinutes >= 600); flag2 |= UnlockTitle(player, "clan_founder", player.CapitalsFounded >= 1 || flag); flag2 |= UnlockTitle(player, "loyal_member", player.ClanId != 0 && player.JoinedClanTimestamp > 0 && Now() - player.JoinedClanTimestamp >= 2592000); flag2 |= UnlockTitle(player, "landclaimer", player.TerritoriesClaimed >= 1); flag2 |= UnlockTitle(player, "realm_builder", player.BuildingsContributed >= 10); flag2 |= UnlockTitle(player, "benefactor", player.CoinsDonatedToTreasury >= 1000); flag2 |= UnlockTitle(player, "great_benefactor", player.CoinsDonatedToTreasury >= 10000); PlayerTitleDefinition legacy = PlayerTitleCatalog.FindByDisplayName(player.ActiveTitle); if (string.IsNullOrEmpty(player.ActiveTitleId) && legacy != null && player.UnlockedTitles.Any((string id) => string.Equals(id, legacy.Id, StringComparison.OrdinalIgnoreCase))) { player.ActiveTitleId = legacy.Id; flag2 = true; } return flag2; } private bool UnlockTitle(PlayerClanInfo player, string titleId, bool condition) { if (!condition || player.UnlockedTitles.Any((string id) => string.Equals(id, titleId, StringComparison.OrdinalIgnoreCase))) { return false; } player.UnlockedTitles.Add(titleId); long num = Now(); player.TitleUnlockTimestamps[titleId] = num; PlayerTitleDefinition playerTitleDefinition = PlayerTitleCatalog.Get(titleId); if (playerTitleDefinition != null) { AddPersonalSagaEntry(player, "TitleUnlocked", "Title Earned", "Earned the title " + playerTitleDefinition.DisplayName + ".", "Normal", "title:" + playerTitleDefinition.Id, 0, 0, 0L, "", playerTitleDefinition.Id, num, markDirty: false); } return true; } public static bool IsLegacyProfileEditSagaEntry(string entry) { string text = (entry ?? string.Empty).Trim(); if (text.StartsWith("[", StringComparison.Ordinal)) { int num = text.IndexOf(']'); if (num >= 0 && num + 1 < text.Length) { text = text.Substring(num + 1).TrimStart(Array.Empty()); } } return new string[6] { "Profile updated", "About Me updated", "Title updated", "Portrait updated", "Profile image updated", "Saved profile" }.Any((string prefix) => text.StartsWith(prefix, StringComparison.OrdinalIgnoreCase)); } private static bool IsGenuineLegacySagaEntry(string entry) { if (string.IsNullOrWhiteSpace(entry) || IsLegacyProfileEditSagaEntry(entry)) { return false; } string text = entry.Trim(); if (text.StartsWith("[", StringComparison.Ordinal)) { int num = text.IndexOf(']'); if (num >= 0 && num + 1 < text.Length) { text = text.Substring(num + 1).TrimStart(Array.Empty()); } } return new string[18] { "clan", "jarl", "capital", "homestead", "outpost", "territor", "building", "donat", "achievement", "title earned", "bounty", "world event", "war", "totem", "founded", "joined", "captured", "milestone" }.Any((string marker) => text.IndexOf(marker, StringComparison.OrdinalIgnoreCase) >= 0); } private static long LegacySagaTimestamp(string entry, long fallback) { string text = (entry ?? string.Empty).Trim(); if (!text.StartsWith("[", StringComparison.Ordinal)) { return fallback; } int num = text.IndexOf(']'); if (num <= 1) { return fallback; } if (!DateTimeOffset.TryParse(text.Substring(1, num - 1), out var result)) { return fallback; } return result.ToUniversalTime().ToUnixTimeSeconds(); } private static string NormalizeSagaText(string value, int maxLength) { string text = (value ?? string.Empty).Replace("<", string.Empty).Replace(">", string.Empty).Trim(); if (text.Length > maxLength) { return text.Substring(0, maxLength); } return text; } private static string StableSagaToken(string value) { ulong num = 1469598103934665603uL; string text = value ?? string.Empty; foreach (char c in text) { num ^= c; num *= 1099511628211L; } return num.ToString("x16"); } public static int TerritorySagaId(ClanTerritory territory) { if (territory == null) { return 0; } return (territory.GridX * 397) ^ territory.GridY; } public bool AddPersonalSagaEntry(PlayerClanInfo profile, string eventType, string title, string description, string importance, string sourceKey, int relatedClanId = 0, int relatedTerritoryId = 0, long relatedPlayerId = 0L, string relatedAchievementId = "", string relatedTitleId = "", long timestampUtc = 0L, bool markDirty = true) { if (!_isServer || profile == null) { return false; } if (!SagaEventType.All.Contains(eventType)) { Plugin.Log.LogError((object)("[FCW Personal Saga] Invalid event type rejected: " + (eventType ?? ""))); return false; } title = NormalizeSagaText(title, 120); description = NormalizeSagaText(description, 800); importance = (SagaImportance.IsValid(importance) ? importance : "Normal"); sourceKey = NormalizeSagaText(sourceKey, 240).ToLowerInvariant(); if (string.IsNullOrEmpty(title) || string.IsNullOrEmpty(sourceKey)) { return false; } string entryId = "saga-" + StableSagaToken(sourceKey); if (profile.PersonalSaga == null) { profile.PersonalSaga = new List(); } if (profile.PersonalSaga.Any((PlayerSagaEntry entry) => entry != null && (string.Equals(entry.SourceKey, sourceKey, StringComparison.OrdinalIgnoreCase) || string.Equals(entry.EntryId, entryId, StringComparison.OrdinalIgnoreCase)))) { return false; } profile.PersonalSaga.Insert(0, new PlayerSagaEntry { EntryId = entryId, TimestampUtc = ((timestampUtc > 0) ? timestampUtc : Now()), EventType = eventType, Title = title, Description = description, Importance = importance, RelatedClanId = relatedClanId, RelatedTerritoryId = relatedTerritoryId, RelatedPlayerId = relatedPlayerId, RelatedAchievementId = (relatedAchievementId ?? ""), RelatedTitleId = (relatedTitleId ?? ""), SourceKey = sourceKey }); TrimPersonalSaga(profile); if (markDirty) { MarkDirty(); } return true; } private static bool TrimPersonalSaga(PlayerClanInfo profile) { if (profile.PersonalSaga == null) { profile.PersonalSaga = new List(); } int num = Math.Max(200, ClanConfig.PersonalSagaMaxEntries?.Value ?? 200); if (profile.PersonalSaga.Count <= num) { return false; } profile.PersonalSaga.RemoveRange(num, profile.PersonalSaga.Count - num); return true; } private bool MigratePersonalSaga() { if (!_isServer || Data?.Players == null) { return false; } bool flag = false; long num = Now(); foreach (PlayerClanInfo value3 in Data.Players.Values) { PlayerClanInfo playerClanInfo = value3; if (playerClanInfo.PersonalSaga == null) { playerClanInfo.PersonalSaga = new List(); } foreach (string item in value3.SagaEntries ?? new List()) { if (IsGenuineLegacySagaEntry(item)) { string sourceKey = "legacy:" + StableSagaToken(item.Trim()); flag |= AddPersonalSagaEntry(value3, "Legacy", "Legacy Milestone", item, "Normal", sourceKey, 0, 0, 0L, "", "", LegacySagaTimestamp(item, num), markDirty: false); } } foreach (string item2 in value3.UnlockedAchievements ?? new List()) { PlayerAchievementDefinition playerAchievementDefinition = PlayerAchievementCatalog.Get(item2); if (playerAchievementDefinition != null) { flag |= AddPersonalSagaEntry(value3, "AchievementUnlocked", "Achievement Unlocked", "Unlocked " + playerAchievementDefinition.DisplayName + ".", "Normal", "achievement:" + playerAchievementDefinition.Id, 0, 0, 0L, playerAchievementDefinition.Id, "", (value3.AchievementUnlockTimestamps != null && value3.AchievementUnlockTimestamps.TryGetValue(playerAchievementDefinition.Id, out var value)) ? value : num, markDirty: false); } } foreach (string item3 in value3.UnlockedTitles ?? new List()) { PlayerTitleDefinition playerTitleDefinition = PlayerTitleCatalog.Get(item3); if (playerTitleDefinition != null) { flag |= AddPersonalSagaEntry(value3, "TitleUnlocked", "Title Earned", "Earned the title " + playerTitleDefinition.DisplayName + ".", "Normal", "title:" + playerTitleDefinition.Id, 0, 0, 0L, "", playerTitleDefinition.Id, (value3.TitleUnlockTimestamps != null && value3.TitleUnlockTimestamps.TryGetValue(playerTitleDefinition.Id, out var value2)) ? value2 : num, markDirty: false); } } Clan clan = ((value3.ClanId == 0) ? null : Data.GetClan(value3.ClanId)); if (clan != null && clan.FounderId == value3.PlayerId) { flag |= AddPersonalSagaEntry(value3, "ClanFounded", "Founded a Clan", "Founded " + clan.Name + ".", "Major", "clanfounded:" + clan.ClanId, clan.ClanId, 0, 0L, "", "", (clan.CreatedTimestamp > 0) ? clan.CreatedTimestamp : num, markDirty: false); } flag |= BackfillStatisticMilestones(value3, num); value3.PersonalSaga.Sort((PlayerSagaEntry left, PlayerSagaEntry right) => (right?.TimestampUtc ?? 0).CompareTo(left?.TimestampUtc ?? 0)); flag |= TrimPersonalSaga(value3); } return flag; } private bool BackfillStatisticMilestones(PlayerClanInfo profile, long timestamp) { bool flag = false; if (profile.TerritoriesClaimed >= 1) { flag |= AddPersonalSagaEntry(profile, "TerritoryClaimed", "First Territory Claimed", "Historical milestone restored from profile statistics.", "Minor", "territoryclaim:milestone:1", 0, 0, 0L, "", "", timestamp, markDirty: false); } if (profile.TerritoriesClaimed >= 5) { flag |= AddPersonalSagaEntry(profile, "TerritoryClaimed", "Five Territories Claimed", "Historical milestone restored from profile statistics.", "Normal", "territoryclaim:milestone:5", 0, 0, 0L, "", "", timestamp, markDirty: false); } if (profile.TerritoriesClaimed >= 10) { flag |= AddPersonalSagaEntry(profile, "TerritoryClaimed", "Ten Territories Claimed", "Historical milestone restored from profile statistics.", "Major", "territoryclaim:milestone:10", 0, 0, 0L, "", "", timestamp, markDirty: false); } if (profile.BuildingsContributed >= 1) { flag |= AddPersonalSagaEntry(profile, "BuildingMilestone", "First Building Raised", "Historical milestone restored from profile statistics.", "Minor", "building:milestone:1", 0, 0, 0L, "", "", timestamp, markDirty: false); } if (profile.BuildingsContributed >= 10) { flag |= AddPersonalSagaEntry(profile, "BuildingMilestone", "Realm Builder", "Historical milestone restored from profile statistics.", "Normal", "building:milestone:10", 0, 0, 0L, "", "", timestamp, markDirty: false); } if (profile.CoinsDonatedToTreasury >= 1000) { flag |= AddPersonalSagaEntry(profile, "DonationMilestone", "Benefactor of the Clan", "Historical milestone restored from profile statistics.", "Normal", "donation:coins:1000", 0, 0, 0L, "", "", timestamp, markDirty: false); } if (profile.CoinsDonatedToTreasury >= 10000) { flag |= AddPersonalSagaEntry(profile, "DonationMilestone", "Great Benefactor", "Historical milestone restored from profile statistics.", "Major", "donation:coins:10000", 0, 0, 0L, "", "", timestamp, markDirty: false); } if (profile.CapitalsFounded >= 1 && !profile.PersonalSaga.Any((PlayerSagaEntry entry) => entry != null && entry.EventType == "CapitalFounded")) { flag |= AddPersonalSagaEntry(profile, "CapitalFounded", "Founded a Capital", "Historical milestone restored from profile statistics.", "Major", "capital:historical:1", 0, 0, 0L, "", "", timestamp, markDirty: false); } if (profile.HomesteadsFounded >= 1 && !profile.PersonalSaga.Any((PlayerSagaEntry entry) => entry != null && entry.EventType == "HomesteadFounded")) { flag |= AddPersonalSagaEntry(profile, "HomesteadFounded", "Founded a Homestead", "Historical milestone restored from profile statistics.", "Normal", "homestead:historical:1", 0, 0, 0L, "", "", timestamp, markDirty: false); } if (profile.OutpostsFounded >= 1 && !profile.PersonalSaga.Any((PlayerSagaEntry entry) => entry != null && entry.EventType == "OutpostFounded")) { flag |= AddPersonalSagaEntry(profile, "OutpostFounded", "Founded an Outpost", "Historical milestone restored from profile statistics.", "Normal", "outpost:historical:1", 0, 0, 0L, "", "", timestamp, markDirty: false); } return flag; } private void AddTerritoryClaimMilestone(PlayerClanInfo profile, ClanTerritory territory) { if (profile != null && territory != null) { int num = ((profile.TerritoriesClaimed == 1) ? 1 : ((profile.TerritoriesClaimed == 5) ? 5 : ((profile.TerritoriesClaimed == 10) ? 10 : 0))); object obj; switch (num) { case 0: return; default: obj = "Ten Territories Claimed"; break; case 5: obj = "Five Territories Claimed"; break; case 1: obj = "First Territory Claimed"; break; } string title = (string)obj; string importance = num switch { 5 => "Normal", 1 => "Minor", _ => "Major", }; AddPersonalSagaEntry(profile, "TerritoryClaimed", title, "Reached the " + num + " territory milestone at " + territory.DisplayName + ".", importance, "territoryclaim:milestone:" + num, profile.ClanId, TerritorySagaId(territory), 0L, "", "", territory.ClaimTimestamp, markDirty: false); } } private void AddBuildingMilestone(PlayerClanInfo profile, ClanTerritory territory, TerritoryBuildingType type) { if (profile != null && (profile.BuildingsContributed == 1 || profile.BuildingsContributed == 10)) { int buildingsContributed = profile.BuildingsContributed; AddPersonalSagaEntry(profile, "BuildingMilestone", (buildingsContributed == 1) ? "First Building Raised" : "Realm Builder", "Raised " + BuildingName(type) + " at " + (territory?.DisplayName ?? "a realm territory") + ".", (buildingsContributed == 1) ? "Minor" : "Normal", "building:milestone:" + buildingsContributed, profile.ClanId, TerritorySagaId(territory), 0L, "", "", Now(), markDirty: false); } } public void AddDonationMilestones(PlayerClanInfo profile) { if (_isServer && profile != null) { if (profile.CoinsDonatedToTreasury >= 1000) { AddPersonalSagaEntry(profile, "DonationMilestone", "Benefactor of the Clan", "Donated 1,000 Coins to the clan.", "Normal", "donation:coins:1000", profile.ClanId, 0, 0L, "", "", 0L, markDirty: false); } if (profile.CoinsDonatedToTreasury >= 10000) { AddPersonalSagaEntry(profile, "DonationMilestone", "Great Benefactor", "Donated 10,000 Coins to the clan.", "Major", "donation:coins:10000", profile.ClanId, 0, 0L, "", "", 0L, markDirty: false); } } } public static int CountScoredPersonalSagaEntries(PlayerClanInfo player) { HashSet hashSet = new HashSet(StringComparer.OrdinalIgnoreCase); HashSet hashSet2 = new HashSet(StringComparer.OrdinalIgnoreCase); int num = 0; foreach (PlayerSagaEntry item in player?.PersonalSaga ?? new List()) { if (item != null && !string.IsNullOrWhiteSpace(item.EntryId) && !string.IsNullOrWhiteSpace(item.SourceKey) && !string.IsNullOrWhiteSpace(item.Title) && SagaEventType.All.Contains(item.EventType) && hashSet.Add(item.SourceKey) && hashSet2.Add(item.EntryId)) { num++; } } return num; } public static int PersonalSagaLegacyScore(PlayerClanInfo player) { return Math.Min(Math.Max(0, ClanConfig.PersonalSagaLegacyScoreCap?.Value ?? 1000), CountScoredPersonalSagaEntries(player) * 10); } private int CalculateProfileLegacyScore(PlayerClanInfo player) { Clan clan = Data.GetClan(player.ClanId); return player.TerritoriesClaimed * 100 + player.TerritoriesCaptured * 200 + player.CapitalsFounded * 250 + player.WarsParticipated * 100 + player.WarsWon * 200 + player.PvPKills * 10 + player.BountiesClaimed * 75 + (int)Math.Min(100000L, player.CoinsDonatedToTreasury + player.ResourceTaxPaid + player.CoinTaxPaid) / 10 + player.BuildingsContributed * 25 + player.OutpostsFounded * 100 + player.HomesteadsFounded * 100 + player.TerritoriesDiscovered * 75 + player.WorldEventsCompleted * 80 + (int)Math.Min(100000L, player.ItemsDonatedToVault) / 10 + ((clan != null && clan.JarlId == player.PlayerId) ? 100 : 0) + PersonalSagaLegacyScore(player); } private static string ValidateProfileImageKey(string profileImageKey) { string text = (profileImageKey ?? string.Empty).Trim().Replace('\\', '/'); if (string.IsNullOrEmpty(text)) { return ""; } if (text.Length > 260 || Path.IsPathRooted(text) || text.StartsWith("/", StringComparison.Ordinal) || text.Split(new char[1] { '/' }).Any((string part) => part == "..") || !string.Equals(Path.GetExtension(text), ".png", StringComparison.OrdinalIgnoreCase)) { return ""; } string path = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location) ?? Paths.PluginPath; foreach (string item in new string[2] { Path.GetFullPath(Path.Combine(path, "Profiles")), Path.GetFullPath(Path.Combine(path, "FehuKingdoms", "Profiles")) }.Distinct(StringComparer.OrdinalIgnoreCase)) { try { string fullPath = Path.GetFullPath(Path.Combine(item, text.Replace('/', Path.DirectorySeparatorChar))); string text2 = item.TrimEnd(Path.DirectorySeparatorChar, Path.AltDirectorySeparatorChar); char directorySeparatorChar = Path.DirectorySeparatorChar; string value = text2 + directorySeparatorChar; if (fullPath.StartsWith(value, StringComparison.OrdinalIgnoreCase) && File.Exists(fullPath) && string.Equals(Path.GetExtension(fullPath), ".png", StringComparison.OrdinalIgnoreCase)) { return text; } } catch (ArgumentException) { } catch (NotSupportedException) { } } return ""; } public static long Now() { return DateTimeOffset.UtcNow.ToUnixTimeSeconds(); } public void LoadData() { if (!_isServer) { return; } try { byte[] worldCustomData = GetWorldCustomData(); if (worldCustomData != null && worldCustomData.Length != 0) { Data = WarfareData.Deserialize(worldCustomData); } if (_sharedClanStore.Enabled) { _sharedClanStore.LoadInto(Data); LogSharedDataLoaded(); } MigrateReservedClanColors(); NormalizeClanTaxSettings(); ResetClanTaxSessionCounters(); NormalizeFreebornTerritories(); NormalizeVaultPermissions(); NormalizeGuardData(); NormalizeWorldEventData(); if (MigratePersonalSaga()) { MarkDirty(); } Plugin.Log.LogInfo((object)($"Loaded warfare data: season {Data.SeasonNumber}, " + $"{Data.Clans.Count} clans, {Data.Players.Count} players, " + $"{Data.Territories.Count} territories, {Data.Wars.Count} wars.")); } catch (Exception ex) { Plugin.Log.LogError((object)("LoadData failed: " + ex.Message)); } } private void MigrateReservedClanColors() { //IL_0021: Unknown result type (might be due to invalid IL or missing references) //IL_0026: Unknown result type (might be due to invalid IL or missing references) //IL_0027: Unknown result type (might be due to invalid IL or missing references) //IL_0028: Unknown result type (might be due to invalid IL or missing references) //IL_0056: Unknown result type (might be due to invalid IL or missing references) //IL_0057: Unknown result type (might be due to invalid IL or missing references) //IL_0035: Unknown result type (might be due to invalid IL or missing references) //IL_0064: Unknown result type (might be due to invalid IL or missing references) foreach (Clan value in Data.Clans.Values) { Color color = value.Color; if (ClanConfig.ColorsClose(color, ClanConfig.SilverColor)) { value.SetColor(ClanConfig.GrayFallbackColor); Plugin.Log.LogInfo((object)"Migrated reserved clan color Silver to Gray."); MarkDirty(); } else if (ClanConfig.ColorsClose(color, ClanConfig.WhiteColor)) { value.SetColor(ClanConfig.GrayFallbackColor); Plugin.Log.LogInfo((object)"Migrated reserved clan color White to Gray."); MarkDirty(); } } } private void NormalizeClanTaxSettings() { int num = Mathf.Clamp(ClanConfig.MaxClanTaxPercent?.Value ?? 50, 0, 100); int resourceTaxPercent = Mathf.Clamp(ClanConfig.DefaultClanResourceTaxPercent?.Value ?? 10, 0, num); int coinTaxPercent = Mathf.Clamp(ClanConfig.DefaultClanCoinTaxPercent?.Value ?? 10, 0, num); foreach (Clan value in Data.Clans.Values) { bool flag = false; if (value.ResourceTaxPercent < 0 || value.ResourceTaxPercent > num) { value.ResourceTaxPercent = resourceTaxPercent; flag = true; } if (value.CoinTaxPercent < 0 || value.CoinTaxPercent > num) { value.CoinTaxPercent = coinTaxPercent; flag = true; } if (flag) { MarkDirty(); } } } private void ResetClanTaxSessionCounters() { foreach (Clan value in Data.Clans.Values) { value.SessionResourceTaxCollected = 0L; value.SessionCoinTaxCollected = 0L; } } private void NormalizeFreebornTerritories() { Dictionary dictionary = new Dictionary(); foreach (ClanTerritory value2 in Data.Territories.Values) { if (value2.FreebornOwnerId == 0L) { continue; } bool flag = false; if (value2.OwnerClanId != 0) { value2.OwnerClanId = 0; flag = true; } bool flag2 = value2.Type == TerritoryType.Homestead || value2.Type == TerritoryType.Outpost; if (!dictionary.TryGetValue(value2.FreebornOwnerId, out var value)) { dictionary[value2.FreebornOwnerId] = value2.Type == TerritoryType.Homestead || !flag2; if (!flag2) { value2.Type = TerritoryType.Homestead; flag = true; } } else if (!flag2 || (value2.Type == TerritoryType.Homestead && value)) { value2.Type = TerritoryType.Outpost; flag = true; } if (value2.Type == TerritoryType.Homestead) { dictionary[value2.FreebornOwnerId] = true; } ConfigEntry odinsPeaceEnabled = ClanConfig.OdinsPeaceEnabled; if (odinsPeaceEnabled != null && odinsPeaceEnabled.Value && !value2.OdinsPeaceActive && value2.OdinsPeaceChallengeEndTimestamp == 0L) { value2.OdinsPeaceActive = true; flag = true; } if (flag) { Plugin.Log.LogInfo((object)("Normalized Freeborn territory " + value2.DisplayName + " for " + (value2.FreebornOwnerName ?? value2.FreebornOwnerId.ToString()) + ".")); MarkDirty(); } } } private void NormalizeVaultPermissions() { foreach (PlayerClanInfo value in Data.Players.Values) { EnsureVaultPermissions(value); } } private void NormalizeGuardData() { int num = 0; foreach (TerritoryGuard guard in Data.Guards) { if (guard.GuardId > num) { num = guard.GuardId; } if (guard.Level <= 0) { guard.Level = 1; } if (guard.UpkeepCost <= 0) { guard.UpkeepCost = GuardUpkeepCost(guard.GuardType); } } if (Data.NextGuardId <= num) { Data.NextGuardId = num + 1; MarkDirty(); } } private void NormalizeWorldEventData() { int num = 0; foreach (WorldEvent worldEvent in Data.WorldEvents) { if (worldEvent.EventId > num) { num = worldEvent.EventId; } if (worldEvent.Duration <= 0 && worldEvent.StartTime > 0 && worldEvent.EndTime > worldEvent.StartTime) { worldEvent.Duration = worldEvent.EndTime - worldEvent.StartTime; } if (worldEvent.Radius <= 0f) { worldEvent.Radius = 60f; } if (string.IsNullOrEmpty(worldEvent.RewardSummary) && worldEvent.RewardCoins > 0) { worldEvent.RewardSummary = worldEvent.RewardCoins + " coins"; } } if (Data.NextWorldEventId <= num) { Data.NextWorldEventId = num + 1; MarkDirty(); } } public void EnsureVaultPermissions(PlayerClanInfo player) { if (player != null && !player.VaultPermissionsInitialized) { bool flag = player.Rank == ClanRank.Officer || player.Rank == ClanRank.Jarl; player.CanDepositVaultItems = flag || (ClanConfig.VaultDefaultMemberCanDepositItems?.Value ?? true); player.CanWithdrawVaultItems = player.Rank == ClanRank.Jarl || ((!flag) ? (ClanConfig.VaultDefaultMemberCanWithdrawItems?.Value ?? false) : (ClanConfig.VaultDefaultOfficerCanWithdrawItems?.Value ?? true)); player.CanDepositVaultCoins = flag || (ClanConfig.VaultDefaultMemberCanDepositCoins?.Value ?? true); player.CanWithdrawVaultCoins = player.Rank == ClanRank.Jarl || ((!flag) ? (ClanConfig.VaultDefaultMemberCanWithdrawCoins?.Value ?? false) : (ClanConfig.VaultDefaultOfficerCanWithdrawCoins?.Value ?? true)); player.VaultPermissionsInitialized = true; MarkDirty(); } } public bool CanDepositVaultItems(PlayerClanInfo player) { EnsureVaultPermissions(player); if (player != null) { if (player.Rank != ClanRank.Jarl) { return player.CanDepositVaultItems; } return true; } return false; } public bool CanWithdrawVaultItems(PlayerClanInfo player) { EnsureVaultPermissions(player); if (player != null) { if (player.Rank != ClanRank.Jarl) { return player.CanWithdrawVaultItems; } return true; } return false; } public bool CanDepositVaultCoins(PlayerClanInfo player) { EnsureVaultPermissions(player); if (player != null) { if (player.Rank != ClanRank.Jarl) { return player.CanDepositVaultCoins; } return true; } return false; } public bool CanWithdrawVaultCoins(PlayerClanInfo player) { EnsureVaultPermissions(player); if (player != null) { if (player.Rank != ClanRank.Jarl) { return player.CanWithdrawVaultCoins; } return true; } return false; } public bool CanUseVaultAt(PlayerClanInfo player, Vector2i grid, bool coinDeposit, out string message) { //IL_0029: Unknown result type (might be due to invalid IL or missing references) //IL_0015: Unknown result type (might be due to invalid IL or missing references) //IL_00fd: Unknown result type (might be due to invalid IL or missing references) //IL_0066: Unknown result type (might be due to invalid IL or missing references) //IL_0149: Unknown result type (might be due to invalid IL or missing references) //IL_0123: Unknown result type (might be due to invalid IL or missing references) //IL_008c: Unknown result type (might be due to invalid IL or missing references) //IL_018e: Unknown result type (might be due to invalid IL or missing references) //IL_00dd: Unknown result type (might be due to invalid IL or missing references) //IL_01df: Unknown result type (might be due to invalid IL or missing references) //IL_00c5: Unknown result type (might be due to invalid IL or missing references) message = ""; if (player == null) { message = "Join a clan or choose Freeborn first."; LogVaultAccess(player, grid, allowed: false, "no player"); return false; } ClanTerritory territory = Data.GetTerritory(grid); if (player.IsFreeborn && player.ClanId == 0) { ConfigEntry enableFreebornVault = ClanConfig.EnableFreebornVault; if (enableFreebornVault == null || !enableFreebornVault.Value) { message = "Freeborn Vault is disabled."; LogVaultAccess(player, grid, allowed: false, "freeborn vault disabled"); return false; } ConfigEntry freebornVaultRequireHomestead = ClanConfig.FreebornVaultRequireHomestead; if (freebornVaultRequireHomestead == null || !freebornVaultRequireHomestead.Value) { LogVaultAccess(player, grid, allowed: true, "freeborn vault unrestricted"); return true; } bool flag = false; if (territory != null && territory.FreebornOwnerId == player.PlayerId && (territory.Type == TerritoryType.Homestead || (territory.Type == TerritoryType.Outpost && flag))) { LogVaultAccess(player, grid, allowed: true, "owned freeborn territory"); return true; } message = "Freeborn Vault can only be accessed from your Homestead."; LogVaultAccess(player, grid, allowed: false, "freeborn territory required"); return false; } if (player.ClanId == 0) { message = "Join a clan first."; LogVaultAccess(player, grid, allowed: false, "no clan"); return false; } if (coinDeposit) { ConfigEntry allowCoinDepositOutsideCapital = ClanConfig.AllowCoinDepositOutsideCapital; if (allowCoinDepositOutsideCapital != null && allowCoinDepositOutsideCapital.Value) { LogVaultAccess(player, grid, allowed: true, "coin deposit outside capital allowed"); return true; } } ConfigEntry vaultRequireCapitalTerritory = ClanConfig.VaultRequireCapitalTerritory; if (vaultRequireCapitalTerritory == null || !vaultRequireCapitalTerritory.Value) { LogVaultAccess(player, grid, allowed: true, "capital territory requirement disabled"); return true; } if (territory != null && territory.OwnerClanId == player.ClanId) { if (territory.Type == TerritoryType.Capital) { goto IL_018c; } if (territory.Type == TerritoryType.Colony) { ConfigEntry allowVaultInClanColonies = ClanConfig.AllowVaultInClanColonies; if (allowVaultInClanColonies != null && allowVaultInClanColonies.Value) { goto IL_018c; } } } ConfigEntry allowVaultInClanColonies2 = ClanConfig.AllowVaultInClanColonies; message = "Clan Vault can only be accessed from your Capital" + ((allowVaultInClanColonies2 != null && allowVaultInClanColonies2.Value) ? " or owned Colony" : "") + "."; LogVaultAccess(player, grid, allowed: false, "capital or allowed colony required"); return false; IL_018c: LogVaultAccess(player, grid, allowed: true, (territory.Type == TerritoryType.Capital) ? "owned capital" : "owned colony allowed"); return true; } private void LogVaultAccess(PlayerClanInfo player, Vector2i grid, bool allowed, string reason) { //IL_0027: Unknown result type (might be due to invalid IL or missing references) if (_isServer) { ConfigEntry enableVaultDebugLogs = ClanConfig.EnableVaultDebugLogs; if (enableVaultDebugLogs != null && enableVaultDebugLogs.Value) { ClanTerritory territory = Data.GetTerritory(grid); Plugin.Log.LogInfo((object)("[FCW Vault] CanUseVaultAt clan=" + (player?.ClanId ?? 0) + " territory=" + (territory?.DisplayName ?? "none") + " isCapital=" + (territory != null && territory.Type == TerritoryType.Capital) + " owned=" + (territory != null && territory.OwnerClanId == (player?.ClanId ?? 0)) + " allowed=" + allowed + " reason=" + reason)); } } } public string SetVaultPermission(long actorId, long targetId, int permission, bool allowed) { PlayerClanInfo player = Data.GetPlayer(actorId); PlayerClanInfo player2 = Data.GetPlayer(targetId); if (player == null || player2 == null || player.ClanId == 0 || player.ClanId != player2.ClanId) { return "That player is not in your clan."; } Clan clan = Data.GetClan(player.ClanId); if (clan == null || clan.JarlId != actorId) { return "Only the Jarl may manage vault permissions."; } EnsureVaultPermissions(player2); switch (permission) { case 1: player2.CanDepositVaultItems = allowed; break; case 2: player2.CanWithdrawVaultItems = allowed; break; case 3: player2.CanDepositVaultCoins = allowed; break; case 4: player2.CanWithdrawVaultCoins = allowed; break; case 5: player2.CanClaimTerritory = allowed; break; default: return "Unknown vault permission."; } MarkDirty(); if (permission != 5) { return "Vault permission updated."; } return "Claim permission updated."; } public string ConfirmVaultWithdraw(long playerId, int clanId, string prefab, int amount) { PlayerClanInfo player = Data.GetPlayer(playerId); if (player == null || player.ClanId != clanId) { return "That is not your clan's vault."; } if (!CanWithdrawVaultItems(player)) { return "You do not have permission to withdraw from vault."; } if (!ConsumeVaultItem(clanId, prefab, amount)) { return "Vault item is no longer available."; } MarkDirty(); return "Item withdrawn from vault."; } public void SaveData() { if (!_isServer) { return; } try { SetWorldCustomData(Data.Serialize()); } catch (Exception ex) { Plugin.Log.LogError((object)("SaveData failed: " + ex.Message)); } } private void MarkSharedDirty() { if (_isServer && _sharedClanStore.Enabled) { _sharedDirty = true; } } private void MarkSharedDirtyAndSave() { MarkSharedDirty(); SaveSharedDataIfDirty(); } private void SaveSharedDataIfDirty() { if (!_isServer || !_sharedDirty || !_sharedClanStore.Enabled) { return; } try { _sharedClanStore.SaveFrom(Data); _sharedDirty = false; } catch (Exception ex) { Plugin.Log.LogError((object)("[FCW Shared] Save failed\nType: " + ex.GetType().FullName + "\nMessage: " + ex.Message + "\nStack:\n" + ex)); } } public void EnsureSharedDataForPlayerSync(long playerId) { if (_isServer && _sharedClanStore.Enabled) { _sharedClanStore.LoadInto(Data); LogSharedDataLoaded(); LogSharedPlayerResolved(playerId); } } private void LogSharedDataLoaded() { Plugin.Log.LogInfo((object)"[FCW Shared] Shared data loaded"); Plugin.Log.LogInfo((object)("[FCW Shared] Global clans=" + Data.Clans.Count)); int num = 0; foreach (PlayerClanInfo value in Data.Players.Values) { if (value.ClanId != 0) { num++; } } Plugin.Log.LogInfo((object)("[FCW Shared] Global members=" + num)); } private void LogSharedPlayerResolutions() { foreach (PlayerClanInfo value in Data.Players.Values) { if (value.ClanId != 0) { LogSharedPlayerResolved(value.PlayerId); } } } private void LogSharedPlayerResolved(long playerId) { PlayerClanInfo player = Data.GetPlayer(playerId); string text = player?.PlayerName ?? playerId.ToString(); int num = player?.ClanId ?? 0; Plugin.Log.LogInfo((object)("[FCW Shared] Player clan resolved " + text + " clan=" + num)); } public string SharedStatus() { return _sharedClanStore.Status(); } public string ReloadSharedClans() { if (!_isServer) { return "Shared clans reload must run on server."; } try { _sharedClanStore.LoadInto(Data); WarfareRPC.Instance?.SyncAllDataToClients(); return "Shared clans reloaded.\n" + _sharedClanStore.Status(); } catch (Exception ex) { Plugin.Log.LogError((object)("[FCW Shared] Reload failed\nType: " + ex.GetType().FullName + "\nMessage: " + ex.Message + "\nStack:\n" + ex)); return "Shared clans reload failed: " + ex.Message; } } public string BackupSharedClans() { try { return _sharedClanStore.BackupNow(); } catch (Exception ex) { Plugin.Log.LogError((object)("[FCW Shared] Backup failed\nType: " + ex.GetType().FullName + "\nMessage: " + ex.Message + "\nStack:\n" + ex)); return "Shared clans backup failed: " + ex.Message; } } private string GetSavePath() { string text = Path.Combine(Paths.ConfigPath, "FortecaClanWarfare"); if (!Directory.Exists(text)) { Directory.CreateDirectory(text); } return Path.Combine(text, "warfare_data.bin"); } private byte[] GetWorldCustomData() { string savePath = GetSavePath(); if (!File.Exists(savePath)) { return null; } return File.ReadAllBytes(savePath); } private void SetWorldCustomData(byte[] data) { File.WriteAllBytes(GetSavePath(), data); } public void ApplyServerData(WarfareData data) { //IL_0042: Unknown result type (might be due to invalid IL or missing references) //IL_004d: Expected O, but got Unknown if (!_isServer) { Plugin.Log.LogInfo((object)"[FCW Shared Sync] Client clearing cached clan state before ApplyServerData"); Data = new WarfareData(); } Data = data ?? new WarfareData(); if (!_isServer) { long playerId = 0L; if ((Object)Player.m_localPlayer != (Object)null) { playerId = Player.m_localPlayer.GetPlayerID(); } int num = Data.GetPlayer(playerId)?.ClanId ?? 0; Plugin.Log.LogInfo((object)("[FCW Shared Sync] Client rebuilt player clan: player=" + playerId + " clan=" + num)); } } public AchievementEvaluationResult MarkPlayerActive(long playerId, PlayerClanInfo resolvedProfile = null, string steamId64 = "") { PlayerClanInfo player = resolvedProfile ?? Data.GetPlayer(playerId); if (player == null) { return new AchievementEvaluationResult(); } long num = Now(); if (resolvedProfile != null) { _activeSessionProfiles[playerId] = player; } if (_activeProfileSessions.Add(playerId)) { _activeSessionProfiles[playerId] = player; bool num2 = player.TotalLogins == 1 && player.FirstLoginTimestamp != 0L && player.FirstLoginTimestamp == player.LastLoginTimestamp && num - player.LastLoginTimestamp < 30; if (player.FirstLoginTimestamp == 0L) { player.FirstLoginTimestamp = num; } if (!num2) { player.TotalLogins = Math.Max(0, player.TotalLogins) + 1; } player.LastLoginTimestamp = num; player.LastProfileMinuteTimestamp = num; long key = Data.Players.FirstOrDefault((KeyValuePair entry) => entry.Value == player).Key; Plugin.Log.LogInfo((object)("[FCW Profile Activity] session playerId=" + playerId + " steamId=" + (steamId64 ?? player.SteamId64 ?? "") + " profileKey=" + key + " logins=" + player.TotalLogins)); } player.LastSeenTimestamp = num; if (player.ClanId != 0) { Clan clan = Data.GetClan(player.ClanId); if (clan != null) { clan.LastActiveTimestamp = num; } } if (player.IsFreeborn && player.ClanId == 0) { player.FreebornLastActiveTimestamp = num; } EvaluatePlayerTitleUnlocks(player); AchievementEvaluationResult result = EvaluatePlayerAchievements(player, markDirty: false); MarkDirty(); return result; } private void TrackConnectedPlayerActivity() { long now = Now(); bool flag = false; HashSet hashSet = new HashSet(); Player[] array = Object.FindObjectsOfType(); for (int i = 0; i < array.Length; i++) { long playerID = array[i].GetPlayerID(); if (playerID == 0L) { continue; } hashSet.Add(playerID); if (_activeProfileSessions.Contains(playerID)) { PlayerClanInfo value; PlayerClanInfo playerClanInfo = (_activeSessionProfiles.TryGetValue(playerID, out value) ? value : Data.GetPlayer(playerID)); if (playerClanInfo != null) { flag |= FlushProfileMinutes(playerClanInfo, now); } } } foreach (long item in new List(_activeProfileSessions)) { if (!hashSet.Contains(item)) { PlayerClanInfo value2; PlayerClanInfo playerClanInfo2 = (_activeSessionProfiles.TryGetValue(item, out value2) ? value2 : Data.GetPlayer(item)); if (playerClanInfo2 != null) { flag |= FlushProfileMinutes(playerClanInfo2, now); } _activeProfileSessions.Remove(item); _activeSessionProfiles.Remove(item); } } if (flag) { WarfareRPC.Instance?.SyncAllDataToClients(); } } private bool FlushProfileMinutes(PlayerClanInfo player, long now) { if (player.LastProfileMinuteTimestamp <= 0 || now <= player.LastProfileMinuteTimestamp) { player.LastProfileMinuteTimestamp = now; return false; } long num = (now - player.LastProfileMinuteTimestamp) / 60; player.LastSeenTimestamp = now; if (num <= 0) { return false; } player.TotalPlayMinutes += num; player.LastProfileMinuteTimestamp += num * 60; if (player.RoleMinutes == null) { player.RoleMinutes = new Dictionary(); } HashSet hashSet = new HashSet(); if (player.PrimaryRole != ClanRole.None) { hashSet.Add(PlayerClanInfo.NormalizeRole(player.PrimaryRole)); } if (player.SecondaryRole != ClanRole.None) { hashSet.Add(PlayerClanInfo.NormalizeRole(player.SecondaryRole)); } if (player.TertiaryRole != ClanRole.None) { hashSet.Add(PlayerClanInfo.NormalizeRole(player.TertiaryRole)); } foreach (ClanRole item in hashSet) { player.RoleMinutes.TryGetValue((int)item, out var value); player.RoleMinutes[(int)item] = value + num; } EvaluatePlayerTitleUnlocks(player); EvaluatePlayerAchievements(player, markDirty: false); MarkDirty(); return true; } public string CreateClan(long playerId, string playerName, string name, string tag, int colorIndex, int iconIndex) { return CreateClan(playerId, playerName, name, tag, colorIndex, iconIndex, ""); } public string CreateClan(long playerId, string playerName, string name, string tag, int colorIndex, int iconIndex, string emblemUrl) { return CreateClan(playerId, playerName, name, tag, colorIndex, iconIndex, emblemUrl, ""); } public string CreateClan(long playerId, string playerName, string name, string tag, int colorIndex, int iconIndex, string emblemUrl, string emblemName) { //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_030b: Unknown result type (might be due to invalid IL or missing references) //IL_0310: Unknown result type (might be due to invalid IL or missing references) //IL_04ea: Unknown result type (might be due to invalid IL or missing references) //IL_0a52: Unknown result type (might be due to invalid IL or missing references) Plugin.Log.LogInfo((object)("[FCW Manager] CreateClan requested playerId=" + playerId + " playerName='" + playerName + "' name='" + name + "' tag='" + tag + "'.")); PlayerClanInfo orCreatePlayer = Data.GetOrCreatePlayer(playerId, playerName); if (orCreatePlayer.ClanId != 0) { Plugin.Log.LogWarning((object)("[FCW Manager] CreateClan rejected: player already in clan " + orCreatePlayer.ClanId + ".")); return "You are already in a clan."; } ConfigEntry antiAltEnabled = ClanConfig.AntiAltEnabled; if (antiAltEnabled != null && antiAltEnabled.Value && !orCreatePlayer.CanJoinClan(ClanConfig.ClanLeaveCooldownHours?.Value ?? 24)) { Plugin.Log.LogWarning((object)"[FCW Manager] CreateClan rejected: anti-alt cooldown."); return "You left a clan too recently. Try again later."; } name = (name ?? "").Trim(); tag = (tag ?? "").Trim(); if (name.Length < 3 || name.Length > 24) { Plugin.Log.LogWarning((object)("[FCW Manager] CreateClan rejected: invalid name length " + name.Length + ".")); return "Clan name must be 3-24 characters."; } if (tag.Length < 1 || tag.Length > 6) { Plugin.Log.LogWarning((object)("[FCW Manager] CreateClan rejected: invalid tag length " + tag.Length + ".")); return "Clan tag must be 1-6 characters."; } foreach (Clan value2 in Data.Clans.Values) { if (string.Equals(value2.Name, name, StringComparison.OrdinalIgnoreCase)) { Plugin.Log.LogWarning((object)("[FCW Manager] CreateClan rejected: duplicate name '" + name + "'.")); return "A clan with that name already exists."; } if (string.Equals(value2.Tag, tag, StringComparison.OrdinalIgnoreCase)) { Plugin.Log.LogWarning((object)("[FCW Manager] CreateClan rejected: duplicate tag '" + tag + "'.")); return "A clan with that tag already exists."; } } int num = ClanConfig.MaxClans?.Value ?? 8; if (num > 0 && Data.Clans.Count >= num) { Plugin.Log.LogWarning((object)("[FCW Manager] CreateClan rejected: max clans reached " + Data.Clans.Count + "/" + num + ".")); return $"The world is full \ufffd {num} clans already exist."; } Color color = ClanConfig.GetColor(colorIndex); if (ClanConfig.IsReservedClanColor(color)) { Plugin.Log.LogWarning((object)"[FCW Manager] CreateClan rejected: reserved clan color."); return "Pure white and silver are reserved for Homestead territories."; } ConfigEntry requireUniqueClanColors = ClanConfig.RequireUniqueClanColors; if (requireUniqueClanColors != null && requireUniqueClanColors.Value) { foreach (Clan value3 in Data.Clans.Values) { if (ClanConfig.ColorsClose(value3.Color, color)) { Plugin.Log.LogWarning((object)"[FCW Manager] CreateClan rejected: duplicate clan color."); return "This clan color is already taken."; } } } string text = NormalizeEmblemName(emblemName); ConfigEntry requireUniqueClanEmblems = ClanConfig.RequireUniqueClanEmblems; if (requireUniqueClanEmblems != null && requireUniqueClanEmblems.Value) { foreach (Clan value4 in Data.Clans.Values) { if (string.Equals(NormalizeEmblemName(value4.ClanEmblemName), text, StringComparison.OrdinalIgnoreCase)) { Plugin.Log.LogWarning((object)"[FCW Manager] CreateClan rejected: duplicate clan emblem."); return "This emblem is already used by another clan."; } } } Plugin.Log.LogInfo((object)"[FCW Manager] CreateClan founding cost skipped: clan founding is free. Development and upgrades may still cost resources."); int num2 = Data.NextClanId++; Clan clan = new Clan { ClanId = num2, Name = name, Tag = tag, IconIndex = iconIndex, FounderId = playerId, JarlId = playerId, Treasury = (ClanConfig.StartingTreasury?.Value ?? 0), ResourceTaxPercent = Mathf.Clamp(ClanConfig.DefaultClanResourceTaxPercent?.Value ?? 10, 0, ClanConfig.MaxClanTaxPercent?.Value ?? 50), CoinTaxPercent = Mathf.Clamp(ClanConfig.DefaultClanCoinTaxPercent?.Value ?? 10, 0, ClanConfig.MaxClanTaxPercent?.Value ?? 50), CreatedTimestamp = Now(), LastActiveTimestamp = Now(), NextElectionTimestamp = Now() + (long)(ClanConfig.ElectionIntervalDays?.Value ?? 14) * 86400L }; clan.SetColor(color); if (!string.IsNullOrWhiteSpace(emblemUrl)) { string text2 = ValidateClanEmblemUrl(emblemUrl); if (text2 != null) { return text2; } clan.ClanEmblemUrl = emblemUrl.Trim(); } clan.ClanEmblemName = text; bool num3 = orCreatePlayer.IsFreeborn && orCreatePlayer.ClanId == 0; List list = new List { playerId }; if (num3) { List freebornTerritories = GetFreebornTerritories(playerId, TerritoryType.Homestead); List freebornTerritories2 = GetFreebornTerritories(playerId, TerritoryType.Outpost); if (freebornTerritories.Count == 0 || freebornTerritories2.Count == 0) { return "Founding a clan from Freeborn requires a Homestead and at least one additional territory."; } EnsureFreebornLists(orCreatePlayer); foreach (long item in orCreatePlayer.AcceptedClanFounderInvites ?? new List()) { PlayerClanInfo player = Data.GetPlayer(item); if (player != null && player.IsFreeborn && player.ClanId == 0 && !list.Contains(item)) { list.Add(item); if (list.Count >= 3) { break; } } } if (list.Count < 3) { return "Founding a clan from Freeborn requires two accepted Freeborn founding invitations."; } } Data.Clans[num2] = clan; ClanTerritory clanTerritory = null; foreach (long item2 in list) { PlayerClanInfo player2 = Data.GetPlayer(item2); if (player2 == null) { continue; } ChangeClanTreasury(clan, player2.FreebornTreasury, "Transfer", "Freeborn Treasury joined clan"); player2.FreebornTreasury = 0L; if (Data.FreebornVaults.TryGetValue(item2, out var value)) { foreach (ClanVaultItem item3 in value) { Data.AddToVault(num2, item3.PrefabName, item3.DisplayName, item3.Amount); } Data.FreebornVaults.Remove(item2); } player2.ClanId = num2; player2.IsFreeborn = false; player2.Rank = ((item2 == playerId) ? ClanRank.Jarl : ClanRank.Member); player2.CanClaimTerritory = item2 == playerId; player2.JoinedClanTimestamp = Now(); player2.FreebornSinceTimestamp = 0L; player2.FreebornLastActiveTimestamp = 0L; player2.LastFreebornChallengeTimestamp = 0L; player2.LastFreebornAbandonTimestamp = 0L; player2.PendingClanFounderInvites.Clear(); player2.AcceptedClanFounderInvites.Clear(); } foreach (ClanTerritory value5 in Data.Territories.Values) { if (list.Contains(value5.FreebornOwnerId)) { long freebornOwnerId = value5.FreebornOwnerId; value5.OwnerClanId = num2; value5.FreebornOwnerId = 0L; value5.FreebornOwnerName = null; value5.OdinsPeaceActive = false; value5.OdinsPeaceChallengeEndTimestamp = 0L; if (freebornOwnerId == playerId && value5.Type == TerritoryType.Homestead && clanTerritory == null) { value5.Type = TerritoryType.Capital; clanTerritory = value5; } else { value5.Type = TerritoryType.Colony; } } } foreach (long item4 in list) { PlayerClanInfo player3 = Data.GetPlayer(item4); if (player3 != null && item4 != playerId) { AddPersonalSagaEntry(player3, "ClanJoined", "Joined a Clan", "Joined " + clan.Name + ".", "Minor", "clanjoin:" + clan.ClanId + ":" + player3.JoinedClanTimestamp, clan.ClanId, 0, 0L, "", "", player3.JoinedClanTimestamp, markDirty: false); } } AddPersonalSagaEntry(orCreatePlayer, "ClanFounded", "Founded a Clan", "Founded " + clan.Name + ".", "Major", "clanfounded:" + clan.ClanId, clan.ClanId, 0, 0L, "", "", clan.CreatedTimestamp, markDirty: false); AddPersonalSagaEntry(orCreatePlayer, "BecameJarl", "Became Jarl", "Became Jarl of " + clan.Name + ".", "Major", "jarl:" + clan.ClanId + ":" + clan.CreatedTimestamp, clan.ClanId, 0, 0L, "", "", clan.CreatedTimestamp, markDirty: false); if (clanTerritory != null) { AddPersonalSagaEntry(orCreatePlayer, "CapitalFounded", "Founded a Capital", "Established the Capital at " + clanTerritory.DisplayName + ".", "Major", "capital:" + clanTerritory.GridX + ":" + clanTerritory.GridY, clan.ClanId, TerritorySagaId(clanTerritory), 0L, "", "", clan.CreatedTimestamp, markDirty: false); } MarkDirty(); MarkSharedDirtyAndSave(); WarfareRPC.Instance?.BroadcastGlobalMessage("" + ClanConfig.GetIcon(iconIndex) + " Clan " + name + " [" + tag + "] has been founded by " + playerName + "!"); Plugin.Log.LogInfo((object)("[FCW Manager] CreateClan success clanId=" + num2 + " name='" + name + "' playerId=" + playerId + ".")); return "Clan " + name + " founded! You are its first Jarl."; } public string DisbandClan(long playerId) { PlayerClanInfo player = Data.GetPlayer(playerId); if (player == null || player.ClanId == 0) { return "You are not in a clan."; } Clan clan = Data.GetClan(player.ClanId); if (clan == null) { return "Clan not found."; } if (clan.JarlId != playerId) { return "Only the Jarl may disband the clan."; } int clanId = clan.ClanId; DoDisbandClan(clanId); MarkDirty(); MarkSharedDirtyAndSave(); WarfareRPC.Instance?.BroadcastGlobalMessage("Clan " + clan.Name + " has been disbanded."); return "Clan disbanded."; } private void DoDisbandClan(int clanId) { RemoveContractsReferencingClan(clanId); foreach (PlayerClanInfo value in Data.Players.Values) { if (value.ClanId == clanId) { value.ClanId = 0; value.Rank = ClanRank.Member; value.CanClaimTerritory = false; value.Role = ClanRole.None; value.PrimaryRole = ClanRole.None; value.SecondaryRole = ClanRole.None; value.TertiaryRole = ClanRole.None; value.LastClanLeaveTimestamp = Now(); } } List list = new List(); foreach (KeyValuePair territory in Data.Territories) { if (territory.Value.OwnerClanId == clanId) { list.Add(territory.Key); } } foreach (string item in list) { Data.Territories.Remove(item); } Data.ClanVaults.Remove(clanId); Data.Clans.Remove(clanId); Data.Wars.RemoveAll((War w) => w.Involves(clanId)); Data.Diplomacy.RemoveAll((DiplomacyRelation d) => d.ClanA == clanId || d.ClanB == clanId || d.RequestedByClanId == clanId || d.AllianceLeaderClanId == clanId); Data.NonAggressionPacts.RemoveAll((NonAggressionPact p) => p.ClanAId == clanId || p.ClanBId == clanId || p.RequestedByClanId == clanId); Data.VassalRelations.RemoveAll((VassalRelation v) => v.OverlordClanId == clanId || v.VassalClanId == clanId || v.PendingRequestByClanId == clanId); Data.TerritoryTransferOffers.RemoveAll((TerritoryTransferOffer o) => o.FromClanId == clanId || o.ToClanId == clanId); for (int num = Data.Alliances.Count - 1; num >= 0; num--) { Alliance alliance = Data.Alliances[num]; alliance.MemberClanIds.Remove(clanId); alliance.PendingInvites.Remove(clanId); if (alliance.MemberClanIds.Count == 0) { Data.Alliances.RemoveAt(num); } else if (alliance.AllianceLeaderClanId == clanId) { alliance.AllianceLeaderClanId = alliance.MemberClanIds[0]; } } Data.Guards.RemoveAll((TerritoryGuard g) => g.OwnerClanId == clanId); Data.RotTotems.RemoveAll((RotTotem t) => t.PlacerClanId == clanId); Data.PendingRootLoot.RemoveAll((PendingRootLoot l) => l.RootClanId == clanId); Data.RootProductionRemainders.RemoveAll((RootProductionRemainder r) => r.ReceivingClanId == clanId); Data.ActiveCaptures.RemoveAll((CaptureSession c) => c.AttackerClanId == clanId || c.DefenderClanId == clanId); Data.ActiveCracks.RemoveAll((VaultCrackSession c) => c.PlayerClanId == clanId || c.TargetClanId == clanId); } private void RemoveContractsReferencingClan(int clanId) { HashSet hashSet = new HashSet(); foreach (PlayerClanInfo value2 in Data.Players.Values) { if (value2.ClanId == clanId) { hashSet.Add(value2.PlayerId); } } List list = new List(); int num = 0; int num2 = 0; int num3 = 0; foreach (Contract item in Data.Contracts ?? new List()) { bool num4 = item.IssuerType == ContractIssuerType.Clan && item.IssuerId == clanId; long result; bool flag = item.TargetType == ContractTargetType.Clan && long.TryParse(item.TargetId, out result) && result == clanId; bool flag2 = item.AcceptedBy != 0L && hashSet.Contains(item.AcceptedBy); bool flag3 = false; if (item.TargetType == ContractTargetType.Territory && !string.IsNullOrEmpty(item.TargetId) && Data.Territories.TryGetValue(item.TargetId, out var value)) { flag3 = value.OwnerClanId == clanId; } if (num4 || flag || flag2 || flag3) { list.Add(item.ContractId); if (item.Status == ContractStatus.Accepted) { num++; } if (item.Status == ContractStatus.Available || item.Status == ContractStatus.Accepted) { num2++; } if (item.Status == ContractStatus.Completed) { num3++; } } } HashSet affectedIdSet = new HashSet(list); int num5 = Data.Contracts.RemoveAll((Contract contract) => affectedIdSet.Contains(contract.ContractId)); Plugin.Log.LogInfo((object)("[FCW Contract Cleanup] dissolvedClanId=" + clanId + " removed=" + num5 + " active=" + num + " pendingOrAccepted=" + num2 + " completedRecords=" + num3 + " cachedOrIndexed=" + 0)); MarkDirty(); MarkSharedDirtyAndSave(); } public string JoinClan(long playerId, string playerName, int clanId) { //IL_01d9: Unknown result type (might be due to invalid IL or missing references) PlayerClanInfo orCreatePlayer = Data.GetOrCreatePlayer(playerId, playerName); if (orCreatePlayer.ClanId != 0) { return "You are already in a clan."; } Clan clan = Data.GetClan(clanId); if (clan == null) { return "That clan no longer exists."; } ConfigEntry antiAltEnabled = ClanConfig.AntiAltEnabled; if (antiAltEnabled != null && antiAltEnabled.Value && !orCreatePlayer.CanJoinClan(ClanConfig.ClanLeaveCooldownHours?.Value ?? 24)) { return "You left a clan too recently. Try again later."; } int clanMemberCount = Data.GetClanMemberCount(clanId); int num = ClanConfig.ClanMaxMembers?.Value ?? 20; if (clanMemberCount >= num) { return $"{clan.Name} is full ({clanMemberCount}/{num})."; } long num2 = Economy.ClanMemberSlotCost(clanMemberCount + 1); if (num2 > 0) { if (clan.Treasury < num2) { return $"{clan.Name} cannot afford the member slot ({num2} coins needed)."; } ChangeClanTreasury(clan, -num2, "Purchase", "Clan purchase"); } orCreatePlayer.ClanId = clanId; orCreatePlayer.IsFreeborn = false; orCreatePlayer.Rank = ClanRank.Member; orCreatePlayer.CanClaimTerritory = false; orCreatePlayer.Role = ClanRole.None; orCreatePlayer.PrimaryRole = ClanRole.None; orCreatePlayer.SecondaryRole = ClanRole.None; orCreatePlayer.TertiaryRole = ClanRole.None; orCreatePlayer.JoinedClanTimestamp = Now(); clan.LastActiveTimestamp = Now(); AddPersonalSagaEntry(orCreatePlayer, "ClanJoined", "Joined a Clan", "Joined " + clan.Name + ".", "Minor", "clanjoin:" + clan.ClanId + ":" + orCreatePlayer.JoinedClanTimestamp, clan.ClanId, 0, 0L, "", "", orCreatePlayer.JoinedClanTimestamp, markDirty: false); MarkDirty(); MarkSharedDirtyAndSave(); WarfareRPC.Instance?.BroadcastGlobalMessage("" + playerName + " joined " + clan.Name + "!"); return "Joined " + clan.Name + "."; } public string LeaveClan(long playerId) { //IL_00c6: Unknown result type (might be due to invalid IL or missing references) PlayerClanInfo player = Data.GetPlayer(playerId); if (player == null || player.ClanId == 0) { return "You are not in a clan."; } Clan clan = Data.GetClan(player.ClanId); int clanId = player.ClanId; player.ClanId = 0; player.Rank = ClanRank.Member; player.CanClaimTerritory = false; player.Role = ClanRole.None; player.PrimaryRole = ClanRole.None; player.SecondaryRole = ClanRole.None; player.TertiaryRole = ClanRole.None; player.LastClanLeaveTimestamp = Now(); if (clan != null && clan.JarlId == playerId) { PlayerClanInfo playerClanInfo = PickSuccessor(clanId); if (playerClanInfo != null) { clan.JarlId = playerClanInfo.PlayerId; playerClanInfo.Rank = ClanRank.Jarl; playerClanInfo.CanClaimTerritory = true; WarfareRPC.Instance?.BroadcastGlobalMessage("" + playerClanInfo.PlayerName + " is the new Jarl of " + clan.Name + "."); } else { DoDisbandClan(clanId); } } MarkDirty(); MarkSharedDirtyAndSave(); return "You have left the clan."; } public string KickMember(long actorId, long targetId) { PlayerClanInfo player = Data.GetPlayer(actorId); PlayerClanInfo player2 = Data.GetPlayer(targetId); if (player == null || player.ClanId == 0) { return "You are not in a clan."; } if (player2 == null || player2.ClanId != player.ClanId) { return "That player is not in your clan."; } if (player.Rank < ClanRank.Officer) { return "Only Officers and the Jarl may kick members."; } if (targetId == actorId) { return "You cannot kick yourself \ufffd use Leave Clan."; } if (player2.Rank >= player.Rank) { return "You cannot kick someone of equal or higher rank."; } player2.ClanId = 0; player2.Rank = ClanRank.Member; player2.CanClaimTerritory = false; player2.Role = ClanRole.None; player2.PrimaryRole = ClanRole.None; player2.SecondaryRole = ClanRole.None; player2.TertiaryRole = ClanRole.None; player2.LastClanLeaveTimestamp = Now(); MarkDirty(); MarkSharedDirtyAndSave(); return "" + player2.PlayerName + " was removed from the clan."; } public string SetRank(long actorId, long targetId, ClanRank rank) { PlayerClanInfo player = Data.GetPlayer(actorId); PlayerClanInfo player2 = Data.GetPlayer(targetId); if (player == null || player.ClanId == 0) { return "You are not in a clan."; } Clan clan = Data.GetClan(player.ClanId); if (clan == null || clan.JarlId != actorId) { return "Only the Jarl may change ranks."; } if (player2 == null || player2.ClanId != player.ClanId) { return "That player is not in your clan."; } if (rank == ClanRank.Jarl) { clan.JarlId = targetId; player2.Rank = ClanRank.Jarl; player2.CanClaimTerritory = true; player.Rank = ClanRank.Officer; MarkDirty(); MarkSharedDirtyAndSave(); return "" + player2.PlayerName + " is now the Jarl."; } player2.Rank = rank; MarkDirty(); MarkSharedDirtyAndSave(); return $"{player2.PlayerName} is now {rank}."; } public string SetClanEmblemUrl(long actorId, string emblemUrl) { ConfigEntry enableClanEmblemUrl = ClanConfig.EnableClanEmblemUrl; if (enableClanEmblemUrl == null || !enableClanEmblemUrl.Value) { return "Clan emblem URLs are disabled."; } PlayerClanInfo player = Data.GetPlayer(actorId); if (player == null || player.ClanId == 0) { return "You are not in a clan."; } if (player.Rank != ClanRank.Jarl) { if (player.Rank == ClanRank.Officer) { ConfigEntry allowOfficerEditClanEmblem = ClanConfig.AllowOfficerEditClanEmblem; if (allowOfficerEditClanEmblem != null && allowOfficerEditClanEmblem.Value) { goto IL_006b; } } return "Only the clan leader can change the clan emblem."; } goto IL_006b; IL_006b: Clan clan = Data.GetClan(player.ClanId); if (clan == null) { return "Clan not found."; } emblemUrl = (emblemUrl ?? "").Trim(); string text = ValidateClanEmblemUrl(emblemUrl); if (text != null) { return text; } clan.ClanEmblemUrl = emblemUrl; MarkDirty(); if (!string.IsNullOrEmpty(emblemUrl)) { return "Clan emblem URL updated."; } return "Clan emblem URL cleared."; } public string SetClanEmblemName(long actorId, string emblemName) { PlayerClanInfo player = Data.GetPlayer(actorId); if (player == null || player.ClanId == 0) { return "You are not in a clan."; } if (player.Rank != ClanRank.Jarl) { return "Only the Jarl can change the clan emblem."; } Clan clan = Data.GetClan(player.ClanId); if (clan == null) { return "Clan not found."; } clan.ClanEmblemName = NormalizeEmblemName(emblemName); MarkDirty(); return "Clan emblem changed."; } public string SetClanLinks(long actorId, string discordUrl, string websiteUrl) { return SetClanLinks(actorId, discordUrl, websiteUrl, ""); } public string SetClanLinks(long actorId, string discordUrl, string websiteUrl, string steamGroupUrl) { return SetClanLinks(actorId, discordUrl, websiteUrl, steamGroupUrl, "", ""); } public string SetClanLinks(long actorId, string discordUrl, string websiteUrl, string steamGroupUrl, string customUrl, string customLabel) { return SetClanLinks(actorId, discordUrl, websiteUrl, steamGroupUrl, customUrl, customLabel, "", ""); } public string SetClanLinks(long actorId, string discordUrl, string websiteUrl, string steamGroupUrl, string customUrl, string customLabel, string customUrl2, string customLabel2) { ConfigEntry enableClanLinks = ClanConfig.EnableClanLinks; if (enableClanLinks == null || !enableClanLinks.Value) { return "Clan links are disabled."; } PlayerClanInfo player = Data.GetPlayer(actorId); if (player == null || player.ClanId == 0) { return "You are not in a clan."; } if (player.Rank != ClanRank.Jarl) { return "Only the Jarl can edit clan links."; } Clan clan = Data.GetClan(player.ClanId); if (clan == null) { return "Clan not found."; } discordUrl = (discordUrl ?? "").Trim(); websiteUrl = (websiteUrl ?? "").Trim(); steamGroupUrl = (steamGroupUrl ?? "").Trim(); customUrl = (customUrl ?? "").Trim(); customLabel = (customLabel ?? "").Trim(); customUrl2 = (customUrl2 ?? "").Trim(); customLabel2 = (customLabel2 ?? "").Trim(); string text = ValidateOptionalUrl(discordUrl, ClanConfig.ClanDiscordUrlMaxLength?.Value ?? 200, "Discord"); if (text != null) { return text; } text = ValidateOptionalUrl(websiteUrl, ClanConfig.ClanWebsiteUrlMaxLength?.Value ?? 200, "Website"); if (text != null) { return text; } text = ValidateOptionalUrl(steamGroupUrl, ClanConfig.ClanSteamGroupUrlMaxLength?.Value ?? 200, "Steam group"); if (text != null) { return text; } text = ValidateOptionalUrl(customUrl, ClanConfig.ClanCustomUrlMaxLength?.Value ?? 200, "Custom"); if (text != null) { return text; } text = ValidateOptionalUrl(customUrl2, ClanConfig.ClanCustomUrlMaxLength?.Value ?? 200, "Custom 2"); if (text != null) { return text; } if (customLabel.Length > (ClanConfig.ClanCustomLabelMaxLength?.Value ?? 40)) { return "Custom link label is too long."; } if (customLabel2.Length > (ClanConfig.ClanCustomLabelMaxLength?.Value ?? 40)) { return "Custom link 2 label is too long."; } clan.ClanDiscordUrl = discordUrl; clan.ClanWebsiteUrl = websiteUrl; clan.ClanSteamGroupUrl = steamGroupUrl; clan.ClanCustomUrl = customUrl; clan.ClanCustomLabel = customLabel; clan.ClanCustomUrl2 = customUrl2; clan.ClanCustomLabel2 = customLabel2; MarkDirty(); return "Clan links updated."; } private string ValidateOptionalUrl(string url, int maxLength, string label) { if (url.Length > maxLength) { return $"{label} link is too long. Max length is {maxLength}."; } if (url.Length > 0 && (!Uri.TryCreate(url, UriKind.Absolute, out Uri result) || (result.Scheme != Uri.UriSchemeHttp && result.Scheme != Uri.UriSchemeHttps))) { return "Invalid " + label + " link."; } return null; } private string ValidateClanEmblemUrl(string emblemUrl) { emblemUrl = (emblemUrl ?? "").Trim(); int num = ClanConfig.ClanEmblemUrlMaxLength?.Value ?? 300; if (emblemUrl.Length > num) { return $"Invalid emblem URL. Max length is {num}."; } if (emblemUrl.Length > 0 && (!Uri.TryCreate(emblemUrl, UriKind.Absolute, out Uri result) || (result.Scheme != Uri.UriSchemeHttp && result.Scheme != Uri.UriSchemeHttps))) { return "Invalid emblem URL."; } return null; } private string NormalizeEmblemName(string emblemName) { emblemName = (emblemName ?? "").Trim(); emblemName = emblemName.Replace('\\', '/').Replace("..", ""); if (emblemName.EndsWith(".png", StringComparison.OrdinalIgnoreCase)) { emblemName = emblemName.Substring(0, emblemName.Length - 4); } if (string.IsNullOrWhiteSpace(emblemName)) { emblemName = ClanConfig.DefaultEmblem?.Value ?? "emblem_001"; } char[] invalidFileNameChars = Path.GetInvalidFileNameChars(); for (int i = 0; i < invalidFileNameChars.Length; i++) { char c = invalidFileNameChars[i]; if (c != '/' && c != '\\') { emblemName = emblemName.Replace(c.ToString(), ""); } } if (!string.IsNullOrWhiteSpace(emblemName)) { return emblemName; } return "emblem_001"; } private PlayerClanInfo PickSuccessor(int clanId) { PlayerClanInfo playerClanInfo = null; foreach (PlayerClanInfo value in Data.Players.Values) { if (value.ClanId == clanId && (playerClanInfo == null || value.Rank > playerClanInfo.Rank || (value.Rank == playerClanInfo.Rank && value.JoinedClanTimestamp < playerClanInfo.JoinedClanTimestamp))) { playerClanInfo = value; } } return playerClanInfo; } public string SetPlayerRole(long playerId, ClanRole role) { return SetPrimaryRole(playerId, role); } public string SetPrimaryRole(long playerId, ClanRole role) { PlayerClanInfo player = Data.GetPlayer(playerId); string text = ValidateRoleChange(player, role, primary: true); if (text != null) { return text; } role = PlayerClanInfo.NormalizeRole(role); if (_activeProfileSessions.Contains(playerId)) { FlushProfileMinutes(player, Now()); } player.NormalizeRoles(); if (player.SecondaryRole == role) { player.SecondaryRole = ClanRole.None; } if (player.TertiaryRole == role) { player.TertiaryRole = ClanRole.None; } player.PrimaryRole = role; player.Role = role; player.LastRoleChangeTimestamp = Now(); MarkDirty(); MarkSharedDirtyAndSave(); if (role != ClanRole.None) { return $"Primary role changed to {role}."; } return "Primary role resigned."; } public string SetSecondaryRole(long playerId, ClanRole role) { PlayerClanInfo player = Data.GetPlayer(playerId); string text = ValidateRoleChange(player, role, primary: false); if (text != null) { return text; } role = PlayerClanInfo.NormalizeRole(role); if (_activeProfileSessions.Contains(playerId)) { FlushProfileMinutes(player, Now()); } player.NormalizeRoles(); if (role != ClanRole.None && player.PrimaryRole == role) { return "Cannot use the same role as Primary and Secondary."; } if (player.TertiaryRole == role) { player.TertiaryRole = ClanRole.None; } player.SecondaryRole = role; player.LastRoleChangeTimestamp = Now(); MarkDirty(); MarkSharedDirtyAndSave(); if (role != ClanRole.None) { return $"Secondary role changed to {role}."; } return "Secondary role resigned."; } public string SetTertiaryRole(long playerId, ClanRole role) { PlayerClanInfo player = Data.GetPlayer(playerId); string text = ValidateRoleChange(player, role, primary: false, tertiary: true); if (text != null) { return text; } role = PlayerClanInfo.NormalizeRole(role); if (_activeProfileSessions.Contains(playerId)) { FlushProfileMinutes(player, Now()); } player.NormalizeRoles(); if (role != ClanRole.None && (player.PrimaryRole == role || player.SecondaryRole == role)) { return "Cannot use the same role in multiple slots."; } player.TertiaryRole = role; player.LastRoleChangeTimestamp = Now(); MarkDirty(); MarkSharedDirtyAndSave(); if (role != ClanRole.None) { return $"Tertiary role changed to {role}."; } return "Tertiary role resigned."; } private string ValidateRoleChange(PlayerClanInfo player, ClanRole role, bool primary) { return ValidateRoleChange(player, role, primary, tertiary: false); } private string ValidateRoleChange(PlayerClanInfo player, ClanRole role, bool primary, bool tertiary) { if (player == null || player.ClanId == 0) { return "Join a clan first."; } role = PlayerClanInfo.NormalizeRole(role); player.NormalizeRoles(); if (role != ClanRole.None && !primary && !tertiary) { ConfigEntry enableSecondaryRole = ClanConfig.EnableSecondaryRole; if (enableSecondaryRole == null || !enableSecondaryRole.Value) { return "Secondary roles are disabled."; } } if (role != ClanRole.None && tertiary) { ConfigEntry enableTertiaryRole = ClanConfig.EnableTertiaryRole; if (enableTertiaryRole == null || !enableTertiaryRole.Value) { return "Tertiary roles are disabled."; } } int num = ((!primary) ? ((!tertiary) ? (ClanConfig.SecondaryRoleChangeCooldownHours?.Value ?? ClanConfig.RoleChangeCooldownHours?.Value ?? 24) : (ClanConfig.TertiaryRoleChangeCooldownHours?.Value ?? ClanConfig.RoleChangeCooldownHours?.Value ?? 24)) : (ClanConfig.PrimaryRoleChangeCooldownHours?.Value ?? ClanConfig.RoleChangeCooldownHours?.Value ?? 24)); bool flag = !primary && !tertiary && player.SecondaryRole == ClanRole.None && (ClanConfig.IgnoreCooldownIfSecondaryRoleIsNone?.Value ?? false); if (tertiary && player.TertiaryRole == ClanRole.None) { ConfigEntry ignoreCooldownIfSecondaryRoleIsNone = ClanConfig.IgnoreCooldownIfSecondaryRoleIsNone; if (ignoreCooldownIfSecondaryRoleIsNone != null && ignoreCooldownIfSecondaryRoleIsNone.Value) { flag = true; } } if (!flag && !player.CanChangeRole(num)) { return "Role change on cooldown: " + RoleCooldownRemaining(player, num) + " remaining."; } if (role != ClanRole.None && !primary && !tertiary && (player.PrimaryRole == role || player.TertiaryRole == role)) { return "Cannot use the same role in multiple slots."; } if (role != ClanRole.None && tertiary && (player.PrimaryRole == role || player.SecondaryRole == role)) { return "Cannot use the same role in multiple slots."; } return null; } private string RoleCooldownRemaining(PlayerClanInfo player, int cooldownHours) { long num = player.LastRoleChangeTimestamp + (long)cooldownHours * 3600L - Now(); if (num < 0) { num = 0L; } long num2 = num / 3600; long num3 = num % 3600 / 60; if (num2 <= 0) { return $"{num3}m"; } return $"{num2}h {num3}m"; } public string CastElectionVote(long voterId, long candidateId) { PlayerClanInfo player = Data.GetPlayer(voterId); if (player == null || player.ClanId == 0) { return "You are not in a clan."; } Clan clan = Data.GetClan(player.ClanId); if (clan == null) { return "Clan not found."; } if (!IsElectionActive(clan, Now())) { return "No active Jarl election."; } PlayerClanInfo player2 = Data.GetPlayer(candidateId); if (player2 == null || player2.ClanId != player.ClanId) { return "That candidate is not in your clan."; } clan.ElectionVotes[voterId] = candidateId; Plugin.Log.LogInfo((object)("FCW Election Vote: player=" + player.PlayerName + "/" + voterId + " candidate=" + player2.PlayerName + "/" + candidateId)); MarkDirty(); MarkSharedDirtyAndSave(); return "Vote cast for " + player2.PlayerName + "."; } private void ElectionTick() { long num = Now(); foreach (Clan value in Data.Clans.Values) { if (value.NextElectionTimestamp == 0L) { value.NextElectionTimestamp = num + (long)(ClanConfig.ElectionIntervalDays?.Value ?? 14) * 86400L; } else if (num >= value.NextElectionTimestamp) { if (ResolveElection(value)) { value.NextElectionTimestamp = num + (long)(ClanConfig.ElectionIntervalDays?.Value ?? 14) * 86400L; } MarkDirty(); MarkSharedDirtyAndSave(); } } } private bool ResolveElection(Clan clan) { //IL_0105: Unknown result type (might be due to invalid IL or missing references) //IL_038a: Unknown result type (might be due to invalid IL or missing references) //IL_01ef: Unknown result type (might be due to invalid IL or missing references) //IL_0319: Unknown result type (might be due to invalid IL or missing references) Dictionary dictionary = new Dictionary(); int num = 0; foreach (KeyValuePair electionVote in clan.ElectionVotes) { PlayerClanInfo player = Data.GetPlayer(electionVote.Key); PlayerClanInfo player2 = Data.GetPlayer(electionVote.Value); if (player != null && player.ClanId == clan.ClanId && player2 != null && player2.ClanId == clan.ClanId) { num++; dictionary[electionVote.Value] = ((!dictionary.TryGetValue(electionVote.Value, out var value)) ? 1 : (value + 1)); } } int num2 = Math.Max(0, Data.GetClanMemberCount(clan.ClanId)); int num3 = RequiredElectionTurnout(num2); if (num2 <= 0 || num < num3) { clan.ElectionVotes.Clear(); WarfareRPC.Instance?.BroadcastGlobalMessage("" + clan.Name + "'s Jarl election failed turnout."); return true; } long num4 = clan.JarlId; int num5 = -1; bool flag = false; foreach (KeyValuePair item in dictionary) { if (item.Value > num5) { num5 = item.Value; num4 = item.Key; flag = false; } else if (item.Value == num5) { flag = true; } } if (flag) { long num6 = (long)Math.Max(1, ClanConfig.JarlElectionTieExtensionHours?.Value ?? 24) * 3600L; clan.NextElectionTimestamp = Now() + num6; WarfareRPC.Instance?.BroadcastGlobalMessage("" + clan.Name + "'s Jarl election is tied and has been extended."); return false; } clan.ElectionVotes.Clear(); if (num4 != clan.JarlId && Data.GetPlayer(num4) != null) { PlayerClanInfo player3 = Data.GetPlayer(clan.JarlId); if (player3 != null) { player3.Rank = ClanRank.Officer; } clan.JarlId = num4; PlayerClanInfo player4 = Data.GetPlayer(num4); player4.Rank = ClanRank.Jarl; player4.CanClaimTerritory = true; AddPersonalSagaEntry(player4, "BecameJarl", "Became Jarl", "Became Jarl of " + clan.Name + ".", "Major", "jarl:" + clan.ClanId + ":" + clan.NextElectionTimestamp, clan.ClanId, 0, 0L, "", "", Now(), markDirty: false); WarfareRPC.Instance?.BroadcastGlobalMessage("" + clan.Name + " has elected " + player4.PlayerName + " as Jarl!"); } else { PlayerClanInfo player5 = Data.GetPlayer(clan.JarlId); WarfareRPC.Instance?.BroadcastGlobalMessage("" + clan.Name + "'s election kept " + (player5?.PlayerName ?? "the Jarl") + " in power."); } return true; } private bool IsElectionActive(Clan clan, long now) { if (clan == null || clan.NextElectionTimestamp <= 0) { return false; } long num = (long)Math.Max(1, ClanConfig.JarlElectionDurationHours?.Value ?? 72) * 3600L; if (now >= clan.NextElectionTimestamp - num) { return now < clan.NextElectionTimestamp; } return false; } private int RequiredElectionTurnout(int eligible) { if (eligible <= 0) { return 0; } int num = Math.Max(0, Math.Min(100, ClanConfig.JarlElectionMinimumTurnoutPercent?.Value ?? 50)); return Math.Max(1, (int)Math.Ceiling((double)eligible * (double)num / 100.0)); } public bool IsVaultOpen() { if (!_isServer && _serverVaultOpenState.HasValue) { return _serverVaultOpenState.Value; } ConfigEntry vaultAlwaysOpen = ClanConfig.VaultAlwaysOpen; if (vaultAlwaysOpen != null && vaultAlwaysOpen.Value) { if (_isServer) { ConfigEntry enableVaultDebugLogs = ClanConfig.EnableVaultDebugLogs; if (enableVaultDebugLogs != null && enableVaultDebugLogs.Value) { Plugin.Log.LogInfo((object)"[FCW Vault] IsVaultOpen alwaysOpen=true result=true"); } } return true; } DateTime utcNow = DateTime.UtcNow; ConfigEntry vaultUseIntervalSchedule = ClanConfig.VaultUseIntervalSchedule; if (vaultUseIntervalSchedule != null && vaultUseIntervalSchedule.Value) { int num = Mathf.Max(1, ClanConfig.VaultOpenEveryMinutes?.Value ?? 60); int num2 = Mathf.Clamp(ClanConfig.VaultOpenDurationMinutes?.Value ?? 5, 1, num); bool result = DateTimeOffset.UtcNow.ToUnixTimeSeconds() / 60 % num < num2; if (_isServer) { ConfigEntry enableVaultDebugLogs2 = ClanConfig.EnableVaultDebugLogs; if (enableVaultDebugLogs2 != null && enableVaultDebugLogs2.Value) { Plugin.Log.LogInfo((object)("[FCW Vault] IsVaultOpen alwaysOpen=false result=" + result)); } } return result; } int num3 = ClanConfig.VaultOpenDayOfWeek?.Value ?? 0; int num4 = ClanConfig.VaultOpenHourUTC?.Value ?? 16; int num5 = ClanConfig.VaultOpenDurationMinutes?.Value ?? 30; if (utcNow.DayOfWeek != (DayOfWeek)num3) { return false; } if (utcNow.Hour != num4) { return false; } if (utcNow.Minute >= num5) { return false; } return true; } public string UpgradeVault(long playerId) { PlayerClanInfo player = Data.GetPlayer(playerId); if (player == null || player.ClanId == 0) { return "You are not in a clan."; } if (player.Rank < ClanRank.Officer) { return "Only Officers and the Jarl may upgrade the vault."; } Clan clan = Data.GetClan(player.ClanId); if (clan == null) { return "Clan not found."; } int num = ClanConfig.MaxVaultTier?.Value ?? 5; if (clan.VaultTier >= num) { return "Vault is already at maximum tier."; } long num2 = Economy.VaultUpgradeCost(clan.VaultTier); int num3 = Economy.VaultUpgradeCoreCost(clan.VaultTier); if (clan.Treasury < num2) { return $"Treasury needs {num2} coins (has {clan.Treasury})."; } if (!ConsumeVaultItem(clan.ClanId, "SurtlingCore", num3)) { return $"Clan vault needs {num3} Surtling Cores."; } ChangeClanTreasury(clan, -num2, "Purchase", "Clan purchase"); clan.VaultTier++; MarkDirty(); return $"Vault upgraded to tier {clan.VaultTier}."; } private void ProcessVaultCracks() { for (int num = Data.ActiveCracks.Count - 1; num >= 0; num--) { VaultCrackSession vaultCrackSession = Data.ActiveCracks[num]; if (vaultCrackSession.IsComplete) { int num2 = Random.Range(ClanConfig.VaultCrackMinStacks?.Value ?? 1, (ClanConfig.VaultCrackMaxStacks?.Value ?? 5) + 1); if (ShouldReduceForOffline(vaultCrackSession.TargetClanId)) { num2 = Mathf.Max(1, Mathf.RoundToInt((float)num2 * (1f - (ClanConfig.OfflineRaidEffectReduction?.Value ?? 50f) / 100f))); } List vault = Data.GetVault(vaultCrackSession.TargetClanId); int num3 = 0; for (int i = 0; i < num2; i++) { if (vault.Count <= 0) { break; } int index = Random.Range(0, vault.Count); ClanVaultItem clanVaultItem = vault[index]; Data.AddToVault(vaultCrackSession.PlayerClanId, clanVaultItem.PrefabName, clanVaultItem.DisplayName, clanVaultItem.Amount); vault.RemoveAt(index); num3++; } Clan clan = Data.GetClan(vaultCrackSession.TargetClanId); Clan clan2 = Data.GetClan(vaultCrackSession.PlayerClanId); WarfareRPC.Instance?.BroadcastGlobalMessage(string.Format("{0} cracked {1} vault \ufffd {2} stacks taken by {3}!", vaultCrackSession.PlayerName, clan?.Name ?? "a", num3, clan2?.Name ?? "the raiders")); Data.ActiveCracks.RemoveAt(num); MarkDirty(); } } } public bool ShouldReduceForOffline(int clanId) { //IL_001e: Unknown result type (might be due to invalid IL or missing references) //IL_0029: Expected O, but got Unknown //IL_003f: Unknown result type (might be due to invalid IL or missing references) //IL_0044: Unknown result type (might be due to invalid IL or missing references) //IL_0046: Unknown result type (might be due to invalid IL or missing references) ConfigEntry offlineRaidProtection = ClanConfig.OfflineRaidProtection; if (offlineRaidProtection == null || !offlineRaidProtection.Value) { return false; } HashSet hashSet = new HashSet(); if ((Object)ZNet.instance != (Object)null) { foreach (PlayerInfo player in ZNet.instance.GetPlayerList()) { hashSet.Add(player.m_name); } } foreach (PlayerClanInfo value in Data.Players.Values) { if (value.ClanId == clanId && hashSet.Contains(value.PlayerName)) { return false; } } return true; } public static string ColorHex(Color c) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) return ColorUtility.ToHtmlStringRGB(c); } private static int CountInventoryItem(Player player, string prefabName) { Inventory obj = ((player != null) ? ((Humanoid)player).GetInventory() : null); if (obj == null) { return 0; } return obj.CountItems(prefabName, -1, true); } private static bool ConsumeInventoryItem(Player player, string prefabName, int amount) { if (amount <= 0) { return true; } Inventory val = ((player != null) ? ((Humanoid)player).GetInventory() : null); if (val == null || val.CountItems(prefabName, -1, true) < amount) { return false; } val.RemoveItem(prefabName, amount, -1, true); return true; } private int CountVaultItem(int clanId, string prefabName) { int num = 0; foreach (ClanVaultItem item in Data.GetVault(clanId)) { if (item.PrefabName == prefabName) { num += item.Amount; } } return num; } private bool ConsumeVaultItem(int clanId, string prefabName, int amount) { if (amount <= 0) { return true; } if (CountVaultItem(clanId, prefabName) < amount) { return false; } List vault = Data.GetVault(clanId); int num = vault.Count - 1; while (num >= 0 && amount > 0) { ClanVaultItem clanVaultItem = vault[num]; if (!(clanVaultItem.PrefabName != prefabName)) { int num2 = Mathf.Min(amount, clanVaultItem.Amount); clanVaultItem.Amount -= num2; amount -= num2; if (clanVaultItem.Amount <= 0) { vault.RemoveAt(num); } } num--; } return true; } public int FreebornVaultCapacity(PlayerClanInfo player) { return Mathf.Clamp(player?.FreebornVaultTier ?? 1, 1, 5) * 20; } public bool ConsumeFreebornVaultItem(long playerId, string prefabName, int amount) { if (amount <= 0) { return true; } List freebornVault = Data.GetFreebornVault(playerId); int num = 0; foreach (ClanVaultItem item in freebornVault) { if (item.PrefabName == prefabName) { num += item.Amount; } } if (num < amount) { return false; } int num2 = freebornVault.Count - 1; while (num2 >= 0 && amount > 0) { ClanVaultItem clanVaultItem = freebornVault[num2]; if (!(clanVaultItem.PrefabName != prefabName)) { int num3 = Mathf.Min(amount, clanVaultItem.Amount); clanVaultItem.Amount -= num3; amount -= num3; if (clanVaultItem.Amount <= 0) { freebornVault.RemoveAt(num2); } } num2--; } return true; } private void ChangeClanTreasury(Clan clan, long amount, string type, string source, string territoryId = "", string buildingId = "") { if (clan != null && amount != 0L) { long treasury = clan.Treasury; clan.Treasury += amount; Economy.RecordTreasuryTransaction(clan, Now(), type, amount, treasury, clan.Treasury, source, territoryId, buildingId); } } private void EconomyTick() { long num = Now(); int num2 = Mathf.Max(60, (ClanConfig.UpkeepTickMinutes?.Value ?? 30) * 60); Dictionary dictionary = new Dictionary(); Dictionary dictionary2 = new Dictionary(); HashSet hashSet = new HashSet(); HashSet hashSet2 = new HashSet(); foreach (ClanTerritory value7 in Data.Territories.Values) { if (value7.OwnerClanId != 0) { dictionary[value7.OwnerClanId] = ((!dictionary.TryGetValue(value7.OwnerClanId, out var value)) ? 1 : (value + 1)); } else if (value7.FreebornOwnerId != 0L) { dictionary2[value7.FreebornOwnerId] = ((!dictionary2.TryGetValue(value7.FreebornOwnerId, out var value2)) ? 1 : (value2 + 1)); } } bool flag = false; foreach (ClanTerritory value8 in Data.Territories.Values) { if (value8.OwnerClanId == 0 && value8.FreebornOwnerId == 0L) { continue; } if (value8.LastIncomeTimestamp == 0L) { value8.LastIncomeTimestamp = num; flag = true; } else { if (num - value8.LastIncomeTimestamp < num2) { continue; } int num3 = (int)((num - value8.LastIncomeTimestamp) / num2); num3 = Mathf.Clamp(num3, 1, 48); Clan clan = Data.GetClan(value8.OwnerClanId); if (clan != null) { int value3; int territoryCount = ((!dictionary.TryGetValue(value8.OwnerClanId, out value3)) ? 1 : value3); long grossIncome = (long)Economy.IncomeBiomeRate(value8.BiomeValueTier) * (long)Mathf.Max(1, value8.SizeLevel) * num3; long value4 = DistributeRootedCoinIncome(value8, grossIncome); bool includeProgressiveUpkeep = hashSet.Add(value8.OwnerClanId); EconomyBreakdown economyBreakdown = Economy.CalculateTerritoryEconomy(Data, value8.OwnerClanId, value8, territoryCount, num3, value4, includeProgressiveUpkeep); long treasury = clan.Treasury; clan.Treasury += economyBreakdown.Net; long num4 = treasury; string territoryId = value8.GridX + "," + value8.GridY; Economy.RecordTreasuryTransaction(clan, num, "Territory Income", economyBreakdown.Gross, num4, num4 + economyBreakdown.Gross, value8.DisplayName, territoryId); num4 += economyBreakdown.Gross; Economy.RecordTreasuryTransaction(clan, num, "Territory Upkeep", -economyBreakdown.TerritoryUpkeep, num4, num4 - economyBreakdown.TerritoryUpkeep, value8.DisplayName, territoryId); num4 -= economyBreakdown.TerritoryUpkeep; Economy.RecordTreasuryTransaction(clan, num, "Progressive Upkeep", -economyBreakdown.ProgressiveUpkeep, num4, clan.Treasury, value8.DisplayName, territoryId); value8.LastIncomeTimestamp = num; flag = true; } else if (value8.FreebornOwnerId != 0L) { PlayerClanInfo player = Data.GetPlayer(value8.FreebornOwnerId); if (player != null) { int value5; int territoryCount2 = ((!dictionary2.TryGetValue(value8.FreebornOwnerId, out value5)) ? 1 : value5); long value6 = Economy.TerritoryIncome(value8) * num3; bool includeProgressiveUpkeep2 = hashSet2.Add(value8.FreebornOwnerId); EconomyBreakdown economyBreakdown2 = Economy.CalculateTerritoryEconomy(Data, 0, value8, territoryCount2, num3, value6, includeProgressiveUpkeep2); player.FreebornTreasury += economyBreakdown2.Net; value8.LastIncomeTimestamp = num; flag = true; } } } } if (ProcessDebtRebellions(num)) { flag = true; } bool flag2 = BuildingProductionTick(num); bool flag3 = GuardUpkeepTick(num, num2); if (flag || flag2 || flag3) { MarkDirty(); WarfareRPC.Instance?.SyncAllDataToClients(); } } private List GetActiveRootTotems(ClanTerritory territory) { List list = new List(); if (territory == null || territory.OwnerClanId == 0) { return list; } foreach (RotTotem rotTotem in Data.RotTotems) { if (rotTotem != null && rotTotem.State != TotemState.Destroyed && rotTotem.TerritoryGridX == territory.GridX && rotTotem.TerritoryGridY == territory.GridY && rotTotem.PlacerClanId != 0 && rotTotem.PlacerClanId != territory.OwnerClanId && Data.GetClan(rotTotem.PlacerClanId) != null) { rotTotem.State = TotemState.Active; list.Add(rotTotem); } } list.Sort((RotTotem a, RotTotem b) => a.TotemId.CompareTo(b.TotemId)); if (list.Count > 4) { list.RemoveRange(4, list.Count - 4); } return list; } private long DistributeRootedCoinIncome(ClanTerritory territory, long grossIncome) { List activeRootTotems = GetActiveRootTotems(territory); if (grossIncome <= 0 || activeRootTotems.Count == 0) { return grossIncome; } long num = grossIncome / 4; int num2 = (int)(grossIncome % 4); int num3 = 4 - activeRootTotems.Count; long result = num * num3 + Math.Min(num2, num3); for (int i = 0; i < activeRootTotems.Count; i++) { long num4 = num + ((num2 > num3 + i) ? 1 : 0); Clan clan = Data.GetClan(activeRootTotems[i].PlacerClanId); if (clan != null && num4 > 0) { long treasury = clan.Treasury; clan.Treasury += num4; Economy.RecordTreasuryTransaction(clan, Now(), "Territory Income", num4, treasury, clan.Treasury, "Rooted income from " + territory.DisplayName, territory.GridX + "," + territory.GridY); } } return result; } private bool ProcessDebtRebellions(long now) { bool result = false; foreach (Clan item in new List(Data.Clans.Values)) { if (item.Treasury >= -1000) { if (item.DebtStartTimestamp != 0L) { item.DebtStartTimestamp = 0L; result = true; } } else if (item.DebtStartTimestamp == 0L) { item.DebtStartTimestamp = now; Plugin.Log.LogWarning((object)("[FCW Debt] clan=" + item.ClanId + " treasury=" + item.Treasury + " debtStart=" + item.DebtStartTimestamp)); result = true; } else { if (now - item.DebtStartTimestamp < 86400) { continue; } List list = new List(); ClanTerritory capital = null; foreach (ClanTerritory value in Data.Territories.Values) { if (value.OwnerClanId == item.ClanId) { if (value.IsCapital) { capital = value; } else { list.Add(value); } } } long debtStartTimestamp = item.DebtStartTimestamp; item.DebtStartTimestamp = 0L; result = true; if (list.Count > 0) { ClanTerritory lost = list[Random.Range(0, list.Count)]; lost.OwnerClanId = 0; lost.ClaimedByPlayerId = 0L; lost.ClaimedByPlayerName = ""; lost.ClaimTimestamp = 0L; lost.State = TerritoryState.Normal; Data.RotTotems.RemoveAll((RotTotem t) => t.TerritoryGridX == lost.GridX && t.TerritoryGridY == lost.GridY); Data.RootProductionRemainders.RemoveAll((RootProductionRemainder r) => r.OwnerShare && r.TerritoryGridX == lost.GridX && r.TerritoryGridY == lost.GridY); Plugin.Log.LogWarning((object)("[FCW Debt] clan=" + item.ClanId + " treasury=" + item.Treasury + " debtStart=" + debtStartTimestamp + " lostTerritory=" + lost.DisplayName + " capitalExcluded=true")); WarfareRPC.Instance?.BroadcastGlobalMessage("A territory has rebelled against " + item.Name + " because the Treasury remained deeply in debt for 24 hours. Lost territory: " + lost.DisplayName + ". Another 24-hour debt period will begin if the Treasury remains below -1000."); } else if (capital != null) { string displayName = capital.DisplayName; capital.OwnerClanId = 0; capital.ClaimedByPlayerId = 0L; capital.ClaimedByPlayerName = ""; capital.ClaimTimestamp = 0L; capital.State = TerritoryState.Normal; Data.RotTotems.RemoveAll((RotTotem t) => t.TerritoryGridX == capital.GridX && t.TerritoryGridY == capital.GridY); Data.RootProductionRemainders.RemoveAll((RootProductionRemainder r) => r.OwnerShare && r.TerritoryGridX == capital.GridX && r.TerritoryGridY == capital.GridY); WarfareRPC.Instance?.BroadcastGlobalMessage("The final territory of " + item.Name + " rebelled after another full 24 hours below -1000 Treasury. Capital lost: " + displayName + ". The clan has collapsed."); Plugin.Log.LogWarning((object)("[FCW Debt] clan=" + item.ClanId + " treasury=" + item.Treasury + " debtStart=" + debtStartTimestamp + " finalCapital=" + displayName + " clanDissolved=true")); DoDisbandClan(item.ClanId); MarkSharedDirtyAndSave(); } else { Plugin.Log.LogWarning((object)("[FCW Debt] clan=" + item.ClanId + " treasury=" + item.Treasury + " debtStart=" + debtStartTimestamp + " no owned territory; dissolving orphan clan")); DoDisbandClan(item.ClanId); MarkSharedDirtyAndSave(); } } } return result; } public static GuardType[] GetGuardTypes() { return HireableGuardTypes; } public static string GuardName(GuardType type) { return type switch { GuardType.BasicGuard => "Basic Guard", GuardType.ArcherGuard => "Archer Guard", GuardType.ShieldGuard => "Shield Guard", GuardType.PatrolGuard => "Patrol Guard", GuardType.TowerGuard => "Tower Guard", GuardType.EliteGuard => "Elite Guard", _ => type.ToString(), }; } public static long GuardHireCost(GuardType type) { return type switch { GuardType.BasicGuard => 100L, GuardType.ArcherGuard => 150L, GuardType.ShieldGuard => 200L, GuardType.PatrolGuard => 250L, GuardType.TowerGuard => 300L, GuardType.EliteGuard => 500L, _ => 100L, }; } public static long GuardUpkeepCost(GuardType type) { return type switch { GuardType.BasicGuard => 2L, GuardType.ArcherGuard => 3L, GuardType.ShieldGuard => 4L, GuardType.PatrolGuard => 5L, GuardType.TowerGuard => 6L, GuardType.EliteGuard => 10L, _ => 2L, }; } public static int GuardDefenseScore(GuardType type) { return type switch { GuardType.BasicGuard => 1, GuardType.ArcherGuard => 2, GuardType.ShieldGuard => 3, GuardType.PatrolGuard => 2, GuardType.TowerGuard => 4, GuardType.EliteGuard => 6, _ => 0, }; } public static int GuardSlotLimit(TerritoryType type) { return type switch { TerritoryType.Capital => 5, TerritoryType.Outpost => 3, TerritoryType.Colony => 2, _ => 2, }; } public int TerritoryDefenseScore(string territoryId) { int num = 0; foreach (TerritoryGuard guard in Data.Guards) { if (guard.TerritoryId == territoryId && guard.Status == GuardStatus.Active) { num += GuardDefenseScore(guard.GuardType); } } return num; } public long TotalGuardUpkeep(int clanId) { long num = 0L; foreach (TerritoryGuard guard in Data.Guards) { if (guard.OwnerClanId == clanId && guard.Status == GuardStatus.Active) { num += guard.UpkeepCost; } } return num; } public string ManageGuard(long playerId, string territoryId, int action, GuardType type, int guardId) { PlayerClanInfo player = Data.GetPlayer(playerId); if (player == null || player.ClanId == 0 || player.IsFreeborn) { return "Only clan members can manage clan guards."; } if (player.Rank < ClanRank.Officer) { return "Only Officers and the Jarl may manage guards."; } return action switch { 0 => HireGuard(player, territoryId, type), 1 => UpgradeGuard(player, guardId), 2 => DismissGuard(player, guardId), _ => "Unknown guard action.", }; } private string HireGuard(PlayerClanInfo player, string territoryId, GuardType type) { if (string.IsNullOrEmpty(territoryId) || !Data.Territories.TryGetValue(territoryId, out var value)) { return "Territory not found."; } if (value.OwnerClanId != player.ClanId) { return "Guards require your clan territory."; } int num = GuardSlotLimit(value.Type); if (Data.Guards.FindAll((TerritoryGuard g) => g.TerritoryId == territoryId && g.Status != GuardStatus.Dead).Count >= num) { return "No empty guard slots in this territory."; } Clan clan = Data.GetClan(player.ClanId); long num2 = GuardHireCost(type); if (clan == null || clan.Treasury < num2) { return "Clan Treasury does not have enough coins."; } long num3 = Now(); ChangeClanTreasury(clan, -num2, "Purchase", "Clan purchase"); TerritoryGuard item = new TerritoryGuard { GuardId = Data.NextGuardId++, OwnerClanId = player.ClanId, TerritoryId = territoryId, GuardType = type, Level = 1, Status = GuardStatus.Active, UpkeepCost = GuardUpkeepCost(type), CreatedAt = num3, LastPaidAt = num3 }; Data.Guards.Add(item); AddGuardLog(clan.Name + " hired " + GuardName(type) + " at " + value.DisplayName + "."); MarkDirty(); return "Hired " + GuardName(type) + " for " + value.DisplayName + "."; } private string UpgradeGuard(PlayerClanInfo player, int guardId) { TerritoryGuard territoryGuard = Data.Guards.Find((TerritoryGuard g) => g.GuardId == guardId); if (territoryGuard == null || territoryGuard.OwnerClanId != player.ClanId) { return "Guard not found."; } if (territoryGuard.Level >= 5) { return "Guard is already max level."; } Clan clan = Data.GetClan(player.ClanId); long num = GuardHireCost(territoryGuard.GuardType) * (territoryGuard.Level + 1); if (clan == null || clan.Treasury < num) { return "Clan Treasury does not have enough coins."; } ChangeClanTreasury(clan, -num, "Purchase", "Clan purchase"); territoryGuard.Level++; if (territoryGuard.Status == GuardStatus.Disabled) { territoryGuard.Status = GuardStatus.Active; territoryGuard.LastPaidAt = Now(); } AddGuardLog(clan.Name + " upgraded " + GuardName(territoryGuard.GuardType) + " to level " + territoryGuard.Level + "."); MarkDirty(); return "Guard upgraded to level " + territoryGuard.Level + "."; } private string DismissGuard(PlayerClanInfo player, int guardId) { TerritoryGuard territoryGuard = Data.Guards.Find((TerritoryGuard g) => g.GuardId == guardId); if (territoryGuard == null || territoryGuard.OwnerClanId != player.ClanId) { return "Guard not found."; } Clan clan = Data.GetClan(player.ClanId); territoryGuard.Status = GuardStatus.Dead; AddGuardLog((clan?.Name ?? "Clan") + " dismissed " + GuardName(territoryGuard.GuardType) + "."); MarkDirty(); return "Guard dismissed."; } private bool GuardUpkeepTick(long now, int tickSeconds) { bool result = false; foreach (TerritoryGuard guard in Data.Guards) { if (guard.Status != GuardStatus.Active) { continue; } if (guard.LastPaidAt == 0L) { guard.LastPaidAt = now; result = true; } else if (now - guard.LastPaidAt >= tickSeconds) { int num = Mathf.Clamp((int)((now - guard.LastPaidAt) / tickSeconds), 1, 48); long num2 = guard.UpkeepCost * num; Clan clan = Data.GetClan(guard.OwnerClanId); if (clan == null || clan.Treasury < num2) { guard.Status = GuardStatus.Disabled; AddGuardLog(GuardName(guard.GuardType) + " disabled: Clan Treasury could not pay upkeep."); result = true; } else { long treasury = clan.Treasury; clan.Treasury -= num2; Economy.RecordTreasuryTransaction(clan, now, "Guard Upkeep", -num2, treasury, clan.Treasury, GuardName(guard.GuardType), guard.TerritoryId, guard.GuardId.ToString()); guard.LastPaidAt = now; result = true; } } } return result; } private void AddGuardLog(string message) { if (Data.GuardLog == null) { Data.GuardLog = new List(); } Data.GuardLog.Insert(0, DateTimeOffset.UtcNow.ToLocalTime().ToString("yyyy-MM-dd HH:mm") + " - " + message); if (Data.GuardLog.Count > 30) { Data.GuardLog.RemoveRange(30, Data.GuardLog.Count - 30); } } public static string WorldEventTypeName(WorldEventType type) { return type switch { WorldEventType.BanditCamp => "Bandit Camp", WorldEventType.AncientShrine => "Ancient Shrine", WorldEventType.ResourceRush => "Resource Rush", WorldEventType.CorruptedTerritory => "Corrupted Territory", WorldEventType.LostCaravan => "Lost Caravan", WorldEventType.WorldBoss => "World Boss", WorldEventType.OdinChallenge => "Odin Challenge", WorldEventType.ClanChallenge => "Clan Challenge", WorldEventType.FreebornOpportunity => "Freeborn Opportunity", _ => type.ToString(), }; } public string CreateTestWorldEvent(string adminSteamId64, Vector3 position) { //IL_0003: Unknown result type (might be due to invalid IL or missing references) return CreateWorldEvent(adminSteamId64, WorldEventType.BanditCamp, position); } public string CreateWorldEvent(string adminSteamId64, WorldEventType eventType, Vector3 position) { //IL_000f: Unknown result type (might be due to invalid IL or missing references) //IL_0010: 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_002c: Expected O, but got Unknown //IL_00c3: Unknown result type (might be due to invalid IL or missing references) //IL_00c4: Unknown result type (might be due to invalid IL or missing references) //IL_0038: Unknown result type (might be due to invalid IL or missing references) //IL_003d: Unknown result type (might be due to invalid IL or missing references) if (!HasEventAdminAccess(adminSteamId64)) { return "Admin/debug mode required."; } if (position == Vector3.zero && (Object)Player.m_localPlayer != (Object)null) { position = ((Component)Player.m_localPlayer).transform.position; } if (!Enum.IsDefined(typeof(WorldEventType), eventType)) { eventType = WorldEventType.BanditCamp; } long num = Now(); string eventName = WorldEventTypeName(eventType); WorldEvent worldEvent = new WorldEvent { EventId = Data.NextWorldEventId++, EventType = eventType, EventName = eventName, Description = WorldEventDescription(eventType), StartTime = num, Duration = WorldEventDefaultDuration(eventType), EndTime = num + WorldEventDefaultDuration(eventType), Position = position, Radius = WorldEventDefaultRadius(eventType), Status = WorldEventStatus.Active, RewardCoins = WorldEventDefaultReward(eventType), RewardSummary = WorldEventDefaultReward(eventType) + " coins" }; Data.WorldEvents.Add(worldEvent); AddWorldEventLog("World Event Created: " + worldEvent.EventName + " at " + Mathf.RoundToInt(worldEvent.Position.x) + ", " + Mathf.RoundToInt(worldEvent.Position.z)); AddWorldEventLog("World Event Started: " + worldEvent.EventName); MarkDirty(); WarfareRPC.Instance?.BroadcastGlobalMessage(FormatWorldEventStartedMessage(worldEvent)); return "World Event Created: " + worldEvent.EventName + "\nLocation: " + Mathf.RoundToInt(worldEvent.Position.x) + ", " + Mathf.RoundToInt(worldEvent.Position.z) + ""; } public static string WorldEventDescription(WorldEventType type) { return type switch { WorldEventType.BanditCamp => "A small hostile camp has been spotted. Reach the area and claim the event.", WorldEventType.AncientShrine => "An old shrine is active. Travel there to secure its blessing.", WorldEventType.ResourceRush => "Valuable resources have surfaced. Reach the hotspot before others.", WorldEventType.CorruptedTerritory => "Corruption gathers in the wilds. Enter the area and stabilize it.", WorldEventType.LostCaravan => "A caravan is stranded with salvageable supplies.", WorldEventType.WorldBoss => "A major threat has been sighted. V1 claim-only verification event.", WorldEventType.OdinChallenge => "A trial of Odin awaits challengers at this location.", WorldEventType.ClanChallenge => "A clan challenge site is open for competition.", WorldEventType.FreebornOpportunity => "A Freeborn opportunity has appeared in the world.", _ => "A world event has appeared. Reach the area and claim it.", }; } public static float WorldEventDefaultRadius(WorldEventType type) { switch (type) { case WorldEventType.WorldBoss: return 120f; case WorldEventType.CorruptedTerritory: case WorldEventType.OdinChallenge: case WorldEventType.ClanChallenge: return 100f; default: return 80f; } } public static long WorldEventDefaultDuration(WorldEventType type) { if ((uint)(type - 5) <= 2u) { return 7200L; } return 3600L; } public static long WorldEventDefaultReward(WorldEventType type) { return type switch { WorldEventType.BanditCamp => 500L, WorldEventType.AncientShrine => 750L, WorldEventType.ResourceRush => 400L, WorldEventType.CorruptedTerritory => 900L, WorldEventType.LostCaravan => 600L, WorldEventType.WorldBoss => 1500L, WorldEventType.OdinChallenge => 1200L, WorldEventType.ClanChallenge => 1000L, WorldEventType.FreebornOpportunity => 350L, _ => 500L, }; } public string RemoveWorldEvent(string adminSteamId64, int eventId) { if (!HasEventAdminAccess(adminSteamId64)) { return "Admin/debug mode required."; } WorldEvent worldEvent = Data.WorldEvents.Find((WorldEvent e) => e.EventId == eventId); if (worldEvent == null) { return "World event not found."; } Data.WorldEvents.Remove(worldEvent); AddWorldEventLog("World Event Removed: " + worldEvent.EventName); MarkDirty(); return "World Event Removed."; } public string ExpireWorldEvent(string adminSteamId64, int eventId) { if (!HasEventAdminAccess(adminSteamId64)) { return "Admin/debug mode required."; } WorldEvent worldEvent = Data.WorldEvents.Find((WorldEvent e) => e.EventId == eventId); if (worldEvent == null) { return "World event not found."; } worldEvent.Status = WorldEventStatus.Expired; worldEvent.EndTime = Now(); AddWorldEventLog("World Event Expired: " + worldEvent.EventName); MarkDirty(); WarfareRPC.Instance?.BroadcastGlobalMessage("World Event Expired:\n" + worldEvent.EventName + ""); return "World Event Expired."; } public string ClaimWorldEvent(long playerId, string playerName, int eventId, Vector3 position) { //IL_0078: Unknown result type (might be due to invalid IL or missing references) //IL_007b: Unknown result type (might be due to invalid IL or missing references) WorldEvent worldEvent = Data.WorldEvents.Find((WorldEvent e) => e.EventId == eventId); if (worldEvent == null) { return "World event not found."; } if (worldEvent.Status != WorldEventStatus.Active) { return "World event is not active."; } if (Now() >= worldEvent.EndTime) { worldEvent.Status = WorldEventStatus.Expired; AddWorldEventLog("World Event Expired: " + worldEvent.EventName); MarkDirty(); return "World event has expired."; } if (Vector3.Distance(position, worldEvent.Position) > worldEvent.Radius) { return "You are outside the event area."; } PlayerClanInfo player = Data.GetPlayer(playerId); if (player == null) { return "Player data not found."; } if (player.ClanId != 0) { Clan clan = Data.GetClan(player.ClanId); if (clan != null) { ChangeClanTreasury(clan, worldEvent.RewardCoins, "Reward", "World event"); } } else { player.FreebornTreasury += worldEvent.RewardCoins; } worldEvent.Status = WorldEventStatus.Completed; worldEvent.ClaimedByPlayerId = playerId; worldEvent.ClaimedByPlayerName = playerName ?? player.PlayerName ?? ""; player.WorldEventsCompleted++; AddPersonalSagaEntry(player, "WorldEventCompleted", "World Event Completed", "Completed " + worldEvent.EventName + ".", "Major", "worldevent:" + worldEvent.EventId, player.ClanId, 0, player.PlayerId, "", "", Now(), markDirty: false); worldEvent.EndTime = Now(); AddWorldEventLog("World Event Claimed: " + worldEvent.EventName + " by " + (worldEvent.ClaimedByPlayerName ?? "Unknown")); AddWorldEventLog("World Event Completed: " + worldEvent.EventName + " by " + (worldEvent.ClaimedByPlayerName ?? "Unknown")); MarkDirty(); WarfareRPC.Instance?.BroadcastGlobalMessage("World Event Completed:\n" + worldEvent.EventName + ""); return "World event claimed. Reward: " + worldEvent.RewardSummary + "."; } public string AdminDeleteClan(string adminSteamId64, int clanId) { if (!HasEventAdminAccess(adminSteamId64)) { return "Admin/debug mode required."; } Clan clan = Data.GetClan(clanId); if (clan == null) { return "Clan not found."; } string name = clan.Name; DoDisbandClan(clanId); AddWorldEventLog("Admin removed clan: " + name); MarkDirty(); return "Admin removed clan " + name + "."; } public string AdminDeleteHomestead(string adminSteamId64, long freebornPlayerId) { if (!HasEventAdminAccess(adminSteamId64)) { return "Admin/debug mode required."; } PlayerClanInfo player = Data.GetPlayer(freebornPlayerId); if (player == null || !player.IsFreeborn || player.ClanId != 0) { return "Freeborn homestead owner not found."; } int num = 0; foreach (ClanTerritory item in new List(Data.Territories.Values)) { if (item.FreebornOwnerId == freebornPlayerId && (item.Type == TerritoryType.Homestead || item.Type == TerritoryType.Outpost)) { item.FreebornOwnerId = 0L; item.FreebornOwnerName = null; item.OdinsPeaceActive = false; item.OdinsPeaceChallengeEndTimestamp = 0L; item.LastChallengeTimestamp = 0L; item.Type = TerritoryType.Colony; item.State = TerritoryState.Normal; item.IsAbandoned = false; Data.ActiveCaptures.RemoveAll((CaptureSession c) => c.TerritoryGridX == item.GridX && c.TerritoryGridY == item.GridY); Data.RotTotems.RemoveAll((RotTotem t) => t.TerritoryGridX == item.GridX && t.TerritoryGridY == item.GridY); num++; } } if (num == 0) { return "No homestead or outposts found for that Freeborn."; } AddWorldEventLog("Admin removed homestead/outposts for: " + (player.PlayerName ?? freebornPlayerId.ToString())); MarkDirty(); return "Admin removed " + num + " homestead/outpost territory records."; } public string AdminReleaseProtectedZoneConflicts(string adminSteamId64) { if (!HasEventAdminAccess(adminSteamId64)) { return "Admin/debug mode required."; } return AdminCheckAllTerritories(adminSteamId64) + " Ownership preserved; no territories were deleted."; } private int ReleaseDetectedProtectedZoneConflicts(bool autoNotify) { //IL_0042: Unknown result type (might be due to invalid IL or missing references) int num = 0; ProtectedWorldZones.GetKnownTraderZones(forceRefresh: true); foreach (ClanTerritory item in new List(Data.Territories.Values)) { if (item != null && (item.OwnerClanId != 0 || item.FreebornOwnerId != 0L) && ProtectedWorldZones.TryGetProtectedZone(item.GridPosition, out var info)) { num++; AddWorldEventLog("Protected zone conflict detected: " + item.DisplayName + " overlaps " + (info?.Name ?? "Protected World Zone") + ". Ownership preserved for admin repair."); } } return num; } public string AdminCheckAllTerritories(string adminSteamId64) { //IL_01f3: Unknown result type (might be due to invalid IL or missing references) if (!HasEventAdminAccess(adminSteamId64)) { return "Admin/debug mode required."; } ProtectedWorldZones.GetKnownTraderZones(forceRefresh: true); int num = 0; int num2 = 0; HashSet hashSet = new HashSet(); HashSet hashSet2 = new HashSet(); foreach (ClanTerritory item in new List(Data.Territories.Values)) { if (item == null) { continue; } bool flag = false; if (item.OwnerClanId != 0 && item.FreebornOwnerId != 0L) { item.FreebornOwnerId = 0L; item.FreebornOwnerName = null; flag = true; } if (item.OwnerClanId != 0 && Data.GetClan(item.OwnerClanId) == null) { AddWorldEventLog("Territory checker found missing clan owner for " + item.DisplayName + ". Ownership left unchanged for manual repair."); } if (item.FreebornOwnerId != 0L) { if (Data.GetPlayer(item.FreebornOwnerId) == null) { AddWorldEventLog("Territory checker found missing Freeborn owner for " + item.DisplayName + ". Ownership left unchanged for manual repair."); } if (item.OwnerClanId != 0) { item.OwnerClanId = 0; flag = true; } if (item.Type != TerritoryType.Homestead && item.Type != TerritoryType.Outpost) { item.Type = (hashSet2.Contains(item.FreebornOwnerId) ? TerritoryType.Outpost : TerritoryType.Homestead); flag = true; } if (item.Type == TerritoryType.Homestead) { if (hashSet2.Contains(item.FreebornOwnerId)) { item.Type = TerritoryType.Outpost; flag = true; } else { hashSet2.Add(item.FreebornOwnerId); } } } else if (item.OwnerClanId != 0) { if (item.Type == TerritoryType.Capital) { if (hashSet.Contains(item.OwnerClanId)) { item.Type = TerritoryType.Colony; flag = true; } else { hashSet.Add(item.OwnerClanId); } } else if (!hashSet.Contains(item.OwnerClanId) && !Data.ClanHasCapital(item.OwnerClanId)) { item.Type = TerritoryType.Capital; hashSet.Add(item.OwnerClanId); flag = true; } } if (ProtectedWorldZones.TryGetProtectedZone(item.GridPosition, out var info) && (item.OwnerClanId != 0 || item.FreebornOwnerId != 0L)) { num2++; AddWorldEventLog("Conflict detected: " + item.DisplayName + " overlaps " + (info?.Name ?? "Protected World Zone") + ". Ownership preserved for admin repair."); } if (flag) { num++; ValueTerritory(item); InitTerritoryStats(item); } } if (num > 0) { MarkDirty(); } return "Territory checker: scanned " + Data.Territories.Count + " territories, repaired " + num + ", conflicts " + num2 + ". Borders/labels refresh on sync."; } public AdminAccessInfo GetAdminAccessInfo(string steamId64) { AdminAccessInfo adminAccessInfo = new AdminAccessInfo(); ConfigEntry enableVaultDebugLogs = ClanConfig.EnableVaultDebugLogs; adminAccessInfo.ExistingFcwDebug = (enableVaultDebugLogs != null && enableVaultDebugLogs.Value) || (ClanConfig.EnableTaxDebugLogs?.Value ?? false); adminAccessInfo.DebugMode = adminAccessInfo.ExistingFcwDebug || IsValheimDebugModeEnabled(); adminAccessInfo.LocalHost = false; adminAccessInfo.ServerAdmin = false; adminAccessInfo.AuthorizedSteamId = IsAuthorizedSteamId(steamId64); adminAccessInfo.MatchedSteamId = (adminAccessInfo.AuthorizedSteamId ? NormalizeSteamId(steamId64) : ""); return adminAccessInfo; } public AdminAccessInfo GetAdminAccessInfo(long playerId, long senderPeerId = 0L) { return GetAdminAccessInfo(GetLocalSteamId64ForDiagnostics()); } public bool HasEventAdminAccess(string steamId64) { return IsAuthorizedSteamId(steamId64); } private bool IsAuthorizedSteamId(string steamId) { steamId = NormalizeSteamId(steamId); if (string.IsNullOrEmpty(steamId)) { return false; } if (steamId == "76561198007625196" || steamId == NormalizeSteamId(ClanConfig.OwnerSteamId?.Value)) { return true; } foreach (string item in SplitConfigList(ClanConfig.AdminSteamIds?.Value)) { if (steamId == NormalizeSteamId(item)) { return true; } } return false; } private static string NormalizeSteamId(string value) { string text = value ?? ""; string text2 = text.Trim(); if (text2.StartsWith("Steam_", StringComparison.OrdinalIgnoreCase)) { text2 = text2.Substring(6).Trim(); } int num = text2.LastIndexOf('/'); if (num >= 0) { text2 = text2.Substring(num + 1).Trim(); } int num2 = text2.LastIndexOf('_'); if (num2 >= 0) { text2 = text2.Substring(num2 + 1).Trim(); } if (IsSteamId64(text2)) { return text2; } for (int i = 0; i <= text.Length - 17; i++) { string text3 = text.Substring(i, 17); if (IsSteamId64(text3)) { return text3; } } return ""; } private static bool IsSteamId64(string value) { if (string.IsNullOrWhiteSpace(value) || value.Length != 17) { return false; } for (int i = 0; i < value.Length; i++) { if (!char.IsDigit(value[i])) { return false; } } return value.StartsWith("765", StringComparison.Ordinal); } public static string GetLocalSteamId64ForDiagnostics() { try { return NormalizeSteamId(((object)Unsafe.As(ref UserInfo.GetLocalUser().UserId)/*cast due to .constrained prefix*/).ToString()); } catch { return ""; } } private static List SplitConfigList(string value) { List list = new List(); string[] array = (value ?? "").Split(new char[4] { ',', ';', '\n', '\r' }, StringSplitOptions.RemoveEmptyEntries); for (int i = 0; i < array.Length; i++) { string text = NormalizeSteamId(array[i]); if (!string.IsNullOrEmpty(text) && !list.Contains(text)) { list.Add(text); } } return list; } private static List SplitEntries(string value) { List list = new List(); string[] array = (value ?? "").Split(new char[1] { ';' }, StringSplitOptions.RemoveEmptyEntries); for (int i = 0; i < array.Length; i++) { string text = array[i].Trim(); if (!string.IsNullOrEmpty(text)) { list.Add(text); } } return list; } private static string CleanEntryPart(string value) { return (value ?? "").Replace("|", " ").Replace(";", " ").Trim(); } public List GetAdminCrewEntries() { List list = SplitEntries(ClanConfig.AdminSteamIdNames?.Value); bool flag = false; for (int i = 0; i < list.Count; i++) { if (list[i].StartsWith("76561198007625196|", StringComparison.Ordinal) || list[i] == "76561198007625196") { flag = true; } } if (!flag) { list.Insert(0, "76561198007625196|Black Viking|Owner"); } return list; } public List GetConstellationMemberEntries() { return SplitEntries(ClanConfig.ConstellationMembers?.Value); } public List GetSupporterBadges(string steamId) { steamId = NormalizeSteamId(steamId); List list = new List(); if (string.IsNullOrEmpty(steamId)) { return list; } foreach (string constellationMemberEntry in GetConstellationMemberEntries()) { string[] array = constellationMemberEntry.Split(new char[1] { '|' }); if (array.Length >= 3 && !(NormalizeSteamId(array[0]) != steamId)) { string[] array2 = array[2].Split(new char[1] { ',' }, StringSplitOptions.RemoveEmptyEntries); foreach (string badge in array2) { AddBadgeIfMissing(list, CanonicalCosmeticBadgeName(badge)); } } } SortServerBadges(list); return list; } public List GetServerBadges(PlayerClanInfo player) { List list = new List(); if (player?.ServerBadges != null) { foreach (string serverBadge in player.ServerBadges) { AddBadgeIfMissing(list, CanonicalCosmeticBadgeName(serverBadge)); } } SortServerBadges(list); return list; } public void RefreshServerBadgesFromConfig() { if (!_isServer || Data?.Players == null) { return; } bool flag = false; foreach (PlayerClanInfo value in Data.Players.Values) { List supporterBadges = GetSupporterBadges(value.SteamId64); bool flag2 = value.ServerBadges != null && value.ServerBadges.Count == supporterBadges.Count; int num = 0; while (flag2 && num < supporterBadges.Count) { flag2 = string.Equals(value.ServerBadges[num], supporterBadges[num], StringComparison.Ordinal); num++; } if (!flag2) { value.ServerBadges = supporterBadges; flag = true; } } flag |= EvaluateAllTitleUnlocks(markDirty: false); bool flag3 = EvaluateAllAchievements(markDirty: false); if (flag || flag3) { MarkDirty(); } if (flag3) { SaveData(); } } public bool EvaluateAllAchievements(bool markDirty = true) { if (!_isServer || Data?.Players == null) { return false; } bool flag = false; foreach (PlayerClanInfo value in Data.Players.Values) { AchievementEvaluationResult achievementEvaluationResult = EvaluatePlayerAchievements(value, markDirty: false); flag |= achievementEvaluationResult.Changed; if (achievementEvaluationResult.UnlockedIds.Count > 0) { _pendingAchievementNotifications[value.PlayerId] = achievementEvaluationResult; } } if (flag && markDirty) { MarkDirty(); } return flag; } public AchievementEvaluationResult ConsumeAchievementNotification(long playerId) { if (_pendingAchievementNotifications.TryGetValue(playerId, out var value)) { _pendingAchievementNotifications.Remove(playerId); return value; } return new AchievementEvaluationResult(); } public AchievementEvaluationResult EvaluatePlayerAchievements(PlayerClanInfo player, bool markDirty = true) { AchievementEvaluationResult achievementEvaluationResult = new AchievementEvaluationResult(); if (!_isServer || player == null) { return achievementEvaluationResult; } PlayerClanInfo playerClanInfo = player; if (playerClanInfo.UnlockedAchievements == null) { playerClanInfo.UnlockedAchievements = new List(); } playerClanInfo = player; if (playerClanInfo.AchievementUnlockTimestamps == null) { playerClanInfo.AchievementUnlockTimestamps = new Dictionary(); } playerClanInfo = player; if (playerClanInfo.RewardedAchievements == null) { playerClanInfo.RewardedAchievements = new List(); } playerClanInfo = player; if (playerClanInfo.PendingAchievementCoinRewards == null) { playerClanInfo.PendingAchievementCoinRewards = new Dictionary(); } foreach (PlayerAchievementDefinition achievement in PlayerAchievementCatalog.All) { if (AchievementProgress(player, achievement) < achievement.RequiredValue) { continue; } if (!player.UnlockedAchievements.Any((string id) => string.Equals(id, achievement.Id, StringComparison.OrdinalIgnoreCase))) { player.UnlockedAchievements.Add(achievement.Id); long num = Now(); player.AchievementUnlockTimestamps[achievement.Id] = num; AddPersonalSagaEntry(player, "AchievementUnlocked", "Achievement Unlocked", "Unlocked " + achievement.DisplayName + ".", "Normal", "achievement:" + achievement.Id, 0, 0, 0L, achievement.Id, "", num, markDirty: false); achievementEvaluationResult.UnlockedIds.Add(achievement.Id); achievementEvaluationResult.StateChanged = true; } if (player.RewardedAchievements.Any((string id) => string.Equals(id, achievement.Id, StringComparison.OrdinalIgnoreCase))) { continue; } int num2 = 0; if (achievement.RewardType == AchievementRewardType.Coins) { ConfigEntry achievementCoinRewardsEnabled = ClanConfig.AchievementCoinRewardsEnabled; if (achievementCoinRewardsEnabled == null || achievementCoinRewardsEnabled.Value) { num2 = Math.Max(0, (achievement.RewardAmount > 0) ? achievement.RewardAmount : (ClanConfig.AchievementDefaultCoinReward?.Value ?? 10)); if (num2 > 0) { player.PendingAchievementCoinRewards[achievement.Id] = num2; player.AchievementCoinsEarned += num2; achievementEvaluationResult.RewardCoins += num2; } } } player.RewardedAchievements.Add(achievement.Id); achievementEvaluationResult.StateChanged = true; } if (achievementEvaluationResult.Changed && markDirty) { MarkDirty(); } return achievementEvaluationResult; } public long AchievementProgress(PlayerClanInfo player, PlayerAchievementDefinition achievement) { if (player == null || achievement == null) { return 0L; } Clan clan = ((player.ClanId == 0) ? null : Data.GetClan(player.ClanId)); switch (achievement.ProgressType) { case AchievementProgressType.FirstLogin: return (player.FirstLoginTimestamp > 0) ? 1 : 0; case AchievementProgressType.PlayMinutes: return Math.Max(0L, player.TotalPlayMinutes); case AchievementProgressType.ClanMember: return (clan != null) ? 1 : 0; case AchievementProgressType.ClanDays: if (clan == null || player.JoinedClanTimestamp <= 0) { return 0L; } return Math.Max(0L, (Now() - player.JoinedClanTimestamp) / 86400); case AchievementProgressType.ClanFounder: if (player.CapitalsFounded < 1 && (clan == null || clan.FounderId != player.PlayerId)) { return 0L; } return 1L; case AchievementProgressType.TerritoriesClaimed: return Math.Max(0, player.TerritoriesClaimed); case AchievementProgressType.CapitalsFounded: return Math.Max(0, player.CapitalsFounded); case AchievementProgressType.HomesteadsFounded: return Math.Max(0, player.HomesteadsFounded); case AchievementProgressType.OutpostsFounded: return Math.Max(0, player.OutpostsFounded); case AchievementProgressType.BuildingsContributed: return Math.Max(0, player.BuildingsContributed); case AchievementProgressType.CoinsDonated: return Math.Max(0L, player.CoinsDonatedToTreasury); case AchievementProgressType.ItemsDonated: return Math.Max(0L, player.ItemsDonatedToVault); case AchievementProgressType.TaxesPaid: return Math.Max(0L, player.ResourceTaxPaid) + Math.Max(0L, player.CoinTaxPaid); case AchievementProgressType.BountiesClaimed: return Math.Max(0, player.BountiesClaimed); case AchievementProgressType.WorldEventsCompleted: return Math.Max(0, player.WorldEventsCompleted); default: return 0L; } } private static void SortServerBadges(List badges) { string[] order = new string[6] { "Admin", "Alpha Tester", "Beta Tester", "Contributor", "Subscriber", "Supporter" }; badges.RemoveAll((string badge) => Array.IndexOf(order, CanonicalCosmeticBadgeName(badge)) < 0); badges.Sort((string a, string b) => Array.IndexOf(order, CanonicalCosmeticBadgeName(a)).CompareTo(Array.IndexOf(order, CanonicalCosmeticBadgeName(b)))); } private static string CanonicalCosmeticBadgeName(string badge) { return (badge ?? string.Empty).Trim() switch { "Administrator" => "Admin", "Gold Contributor" => "Contributor", "Silver Subscriber" => "Subscriber", "Steel Supporter" => "Supporter", "Bronze Alpha Tester" => "Alpha Tester", _ => CleanBadgeName(badge), }; } public string FormatSupporterBadges(string steamId) { List supporterBadges = GetSupporterBadges(steamId); if (supporterBadges.Count != 0) { return string.Join(", ", supporterBadges.ToArray()); } return "None"; } private static void AddBadgeIfMissing(List badges, string badge) { badge = CleanBadgeName(badge); if (!string.IsNullOrEmpty(badge) && !badges.Exists((string existing) => string.Equals(existing, badge, StringComparison.OrdinalIgnoreCase))) { badges.Add(badge); } } private static string CleanBadgeName(string value) { return (value ?? "").Replace("|", " ").Replace(";", " ").Replace("<", "") .Replace(">", "") .Trim(); } public string AdminTextConfigAction(string adminSteamId64, int action, string steamId, string nickname, string note) { if (!HasEventAdminAccess(adminSteamId64)) { return "Admin access required."; } steamId = NormalizeSteamId(steamId); nickname = CleanEntryPart(nickname); note = CleanEntryPart(note); switch (action) { case 1: return AddAdminSteamId(steamId, nickname, note); case 2: return RemoveAdminSteamId(steamId); case 10: return AddConstellationMember(steamId, nickname, note); case 11: return RemoveConstellationMember(steamId); case 20: return SetRealmBoolToggle("Force PvP", "force_pvp", IsTruthy(note)); case 21: return SetRealmBoolToggle("Force Friendly Fire", "friendly_fire", IsTruthy(note)); case 22: { bool flag3 = IsTruthy(note); ConfigEntry allowPlayerPvpToggle = ClanConfig.AllowPlayerPvpToggle; bool num4 = allowPlayerPvpToggle == null || allowPlayerPvpToggle.Value != flag3; ClanConfig.AllowPlayerPvpToggle.Value = flag3; if (num4) { AddWorldEventLog(PvpSettingEventMessage("Player PvP Toggle", flag3)); } return SettingSyncMessage("Allow Player PvP Toggle", flag3); } case 23: { bool flag4 = IsTruthy(note); ConfigEntry battleStatusEnabled = ClanConfig.BattleStatusEnabled; bool num5 = battleStatusEnabled == null || battleStatusEnabled.Value != flag4; ClanConfig.BattleStatusEnabled.Value = flag4; if (num5) { AddWorldEventLog(PvpSettingEventMessage("Battle Status", flag4)); } return SettingSyncMessage("Battle Status", flag4); } case 24: { int num = ParsePositiveInt(note, 30, 1, 3600); ConfigEntry combatDurationSeconds = ClanConfig.CombatDurationSeconds; bool num2 = combatDurationSeconds == null || combatDurationSeconds.Value != num; ClanConfig.CombatDurationSeconds.Value = num; if (num2) { AddWorldEventLog("A new decree has been carved into the war-stone."); } return "✔ Server synchronized\nCombat Duration Seconds set to " + num + "."; } case 25: return SetRealmBoolToggle("Block Teleport While In Combat", "teleport_in_combat", IsTruthy(note), invertRuleValue: true); case 26: { bool flag2 = IsTruthy(note); ConfigEntry killFeedEnabled = ClanConfig.KillFeedEnabled; bool num3 = killFeedEnabled == null || killFeedEnabled.Value != flag2; ClanConfig.KillFeedEnabled.Value = flag2; if (num3) { AddWorldEventLog(PvpSettingEventMessage("Kill Feed", flag2)); } return SettingSyncMessage("Kill Feed", flag2); } case 27: return SetRealmBoolToggle("Combat Logout Handling", "combat_logout", IsTruthy(note)); case 30: return SetRealmBoolToggle("Resource Gathering Restriction", "resource_gathering_restriction", IsTruthy(note)); case 31: return SetRealmBoolToggle("Mining Restriction", "mining_restriction", IsTruthy(note)); case 32: return SetRealmBoolToggle("Woodcutting Restriction", "woodcutting_restriction", IsTruthy(note)); case 33: ClanConfig.BuildingRestrictionEnabled.Value = IsTruthy(note); return SettingSyncMessage("Building Restriction", IsTruthy(note)); case 34: return SetRealmBoolToggle("Territory Capture", "territory_capture", IsTruthy(note)); case 35: return SetRealmBoolToggle("Clan Wars", "clan_wars", IsTruthy(note)); case 36: ClanConfig.FreebornProtectionEnabled.Value = IsTruthy(note); return SettingSyncMessage("Freeborn Protection", IsTruthy(note)); case 37: ClanConfig.OdinsPeaceEnabled.Value = IsTruthy(note); return SettingSyncMessage("Odin's Peace", IsTruthy(note)); case 38: ClanConfig.EnableClanMemberTax.Value = IsTruthy(note); return SettingSyncMessage("Taxes", IsTruthy(note)); case 39: ClanConfig.EnableProgressiveTerritoryUpkeep.Value = IsTruthy(note); return SettingSyncMessage("Upkeep", IsTruthy(note)); case 40: ClanConfig.WorldEventsEnabled.Value = IsTruthy(note); return SettingSyncMessage("World Events", IsTruthy(note)); case 41: ClanConfig.GamesEnabled.Value = IsTruthy(note); return SettingSyncMessage("Games", IsTruthy(note)); case 42: { bool flag = IsTruthy(note); ClanConfig.EnableTaxDebugLogs.Value = flag; ClanConfig.EnableVaultDebugLogs.Value = flag; return SettingSyncMessage("Debug Logging", flag); } case 43: ClanConfig.DiscordWebhookUrl.Value = note ?? ""; return "✔ Server synchronized\nDiscord webhook URL saved."; case 44: return SetNotificationToggle("Discord Enabled", ClanConfig.DiscordEnabled, IsTruthy(note)); case 45: return SetNotificationToggle("Send Kill Feed", ClanConfig.NotifyKillFeed, IsTruthy(note)); case 46: return SetNotificationToggle("Send Combat Logout", ClanConfig.NotifyCombatLogout, IsTruthy(note)); case 47: return SetNotificationToggle("Send War Started", ClanConfig.NotifyWarStarted, IsTruthy(note)); case 48: return SetNotificationToggle("Send War Ended", ClanConfig.NotifyWarEnded, IsTruthy(note)); case 49: return SetNotificationToggle("Send Territory Captured", ClanConfig.NotifyTerritoryCaptured, IsTruthy(note)); case 50: return SetNotificationToggle("Send Territory Lost", ClanConfig.NotifyTerritoryLost, IsTruthy(note)); case 51: return SetNotificationToggle("Send Clan Created", ClanConfig.NotifyClanCreated, IsTruthy(note)); case 52: return SetNotificationToggle("Send Clan Disbanded", ClanConfig.NotifyClanDisbanded, IsTruthy(note)); case 53: return SetNotificationToggle("Send Elections", ClanConfig.NotifyElections, IsTruthy(note)); case 54: return SetNotificationToggle("Send Server Announcements", ClanConfig.NotifyServerAnnouncements, IsTruthy(note)); case 55: { if (!NotificationManager.TestDiscordWebhook(out var result)) { return "" + result + ""; } return "✔ " + result + ""; } case 56: return SetNotificationToggle("Debug Notifications", ClanConfig.DebugNotifications, IsTruthy(note)); case 60: return SetNotificationToggle("Notify Discord PvP", ClanConfig.NotifyDiscordPvP, IsTruthy(note)); case 61: return SetNotificationToggle("Notify Discord War", ClanConfig.NotifyDiscordWar, IsTruthy(note)); case 62: return SetNotificationToggle("Notify Discord Territory", ClanConfig.NotifyDiscordTerritory, IsTruthy(note)); case 63: return SetNotificationToggle("Notify Discord Economy", ClanConfig.NotifyDiscordEconomy, IsTruthy(note)); case 64: return SetNotificationToggle("Notify Discord Building", ClanConfig.NotifyDiscordBuilding, IsTruthy(note)); case 65: return SetNotificationToggle("Notify Discord Clan", ClanConfig.NotifyDiscordClan, IsTruthy(note)); case 66: return SetNotificationToggle("Notify Discord Election", ClanConfig.NotifyDiscordElection, IsTruthy(note)); case 67: return SetNotificationToggle("Notify Discord Event", ClanConfig.NotifyDiscordEvent, IsTruthy(note)); case 68: return SetNotificationToggle("Notify Discord Server", ClanConfig.NotifyDiscordServer, IsTruthy(note)); case 69: return SetNotificationToggle("Notify Discord Supporter", ClanConfig.NotifyDiscordSupporter, IsTruthy(note)); case 70: return SetNotificationToggle("Notify Discord Admin", ClanConfig.NotifyDiscordAdmin, IsTruthy(note)); case 71: return SetNotificationToggle("Notify Discord System", ClanConfig.NotifyDiscordSystem, IsTruthy(note)); case 80: { RealmRules.TrySetValue(steamId, note, out var message); return message; } default: return "Unknown admin config action."; } } private string SetRealmBoolToggle(string label, string ruleId, bool enabled, bool invertRuleValue = false) { RealmRules.TrySetValue(ruleId, (invertRuleValue ? (!enabled) : enabled).ToString(), out var _); return SettingSyncMessage(label, enabled); } private string SetNotificationToggle(string label, ConfigEntry entry, bool enabled) { if (entry == null) { return "Notification setting is unavailable."; } bool num = entry.Value != enabled; entry.Value = enabled; if (num) { AddWorldEventLog("The realm has accepted a new law of messages."); } return SettingSyncMessage(label, enabled); } private static int ParsePositiveInt(string value, int fallback, int min, int max) { if (!int.TryParse(value, out var result)) { return fallback; } return Mathf.Clamp(result, min, max); } private string PvpSettingEventMessage(string label, bool enabled) { if (label == "PvP") { if (!enabled) { return "The Thing has ruled: open combat returns to local custom."; } return "The Thing has ruled: open combat is now sanctioned across the realm."; } if (!enabled) { return "The Thing has ruled: kin-strife is restrained once more."; } return "The Thing has ruled: shield-brothers may now test steel in earnest."; } private string SettingSyncMessage(string label, bool enabled) { return "✔ Server synchronized\n" + label + " " + (enabled ? "enabled." : "disabled.") + ""; } private string AddAdminSteamId(string steamId, string nickname, string note) { if (string.IsNullOrEmpty(steamId)) { return "SteamID64 required."; } List list = SplitConfigList(ClanConfig.AdminSteamIds?.Value); if (!list.Contains(steamId)) { list.Add(steamId); } if (!list.Contains("76561198007625196")) { list.Insert(0, "76561198007625196"); } ClanConfig.AdminSteamIds.Value = string.Join(",", list.ToArray()); List adminCrewEntries = GetAdminCrewEntries(); adminCrewEntries.RemoveAll((string e) => e == steamId || e.StartsWith(steamId + "|", StringComparison.Ordinal)); adminCrewEntries.Add(steamId + "|" + nickname + "|" + note); ClanConfig.AdminSteamIdNames.Value = string.Join(";", adminCrewEntries.ToArray()); return "✔ Admin saved\n" + (string.IsNullOrWhiteSpace(nickname) ? steamId : nickname) + " added as Administrator."; } private string RemoveAdminSteamId(string steamId) { if (steamId == "76561198007625196" || steamId == NormalizeSteamId(ClanConfig.OwnerSteamId?.Value)) { return "Owner SteamID64 cannot be removed."; } List list = SplitConfigList(ClanConfig.AdminSteamIds?.Value); list.RemoveAll((string id) => id == steamId); if (!list.Contains("76561198007625196")) { list.Insert(0, "76561198007625196"); } ClanConfig.AdminSteamIds.Value = string.Join(",", list.ToArray()); List adminCrewEntries = GetAdminCrewEntries(); adminCrewEntries.RemoveAll((string e) => e == steamId || e.StartsWith(steamId + "|", StringComparison.Ordinal)); ClanConfig.AdminSteamIdNames.Value = string.Join(";", adminCrewEntries.ToArray()); return "✔ Admin removed\nAdministrator removed."; } private string AddConstellationMember(string steamId, string nickname, string badge) { if (string.IsNullOrEmpty(steamId)) { return "SteamID64 required."; } List constellationMemberEntries = GetConstellationMemberEntries(); constellationMemberEntries.RemoveAll((string e) => e == steamId || e.StartsWith(steamId + "|", StringComparison.Ordinal)); constellationMemberEntries.Add(steamId + "|" + nickname + "|" + badge); ClanConfig.ConstellationMembers.Value = string.Join(";", constellationMemberEntries.ToArray()); RefreshServerBadgesFromConfig(); return "✔ Constellation member saved\n" + (string.IsNullOrWhiteSpace(nickname) ? steamId : nickname) + " added as " + badge + "."; } private string RemoveConstellationMember(string steamId) { List constellationMemberEntries = GetConstellationMemberEntries(); constellationMemberEntries.RemoveAll((string e) => e == steamId || e.StartsWith(steamId + "|", StringComparison.Ordinal)); ClanConfig.ConstellationMembers.Value = string.Join(";", constellationMemberEntries.ToArray()); RefreshServerBadgesFromConfig(); return "✔ Constellation member removed\nMember removed."; } public static bool IsTruthy(string value) { if (!string.Equals(value, "true", StringComparison.OrdinalIgnoreCase) && !string.Equals(value, "1", StringComparison.OrdinalIgnoreCase) && !string.Equals(value, "yes", StringComparison.OrdinalIgnoreCase)) { return string.Equals(value, "on", StringComparison.OrdinalIgnoreCase); } return true; } private bool IsValheimDebugModeEnabled() { Assembly[] assemblies = AppDomain.CurrentDomain.GetAssemblies(); foreach (Assembly assembly in assemblies) { Type type = assembly.GetType("Console") ?? assembly.GetType("Terminal"); if (type != null && (ReadDebugFlag(type, null) || ReadDebugFlag(type, ReadStaticInstance(type)))) { return true; } } return false; } private object ReadStaticInstance(Type type) { FieldInfo field = type.GetField("instance", BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic); if (field != null) { return field.GetValue(null); } return type.GetProperty("instance", BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic)?.GetValue(null, null); } private bool ReadDebugFlag(Type type, object instance) { BindingFlags bindingAttr = (BindingFlags)(0x30 | ((instance == null) ? 8 : 4)); string[] array = new string[6] { "m_cheat", "m_cheats", "m_debugMode", "m_debugModeEnabled", "CheatsEnabled", "DebugMode" }; foreach (string name in array) { FieldInfo field = type.GetField(name, bindingAttr); if (field != null && field.FieldType == typeof(bool) && (bool)field.GetValue(instance)) { return true; } PropertyInfo property = type.GetProperty(name, bindingAttr); if (property != null && property.PropertyType == typeof(bool) && (bool)property.GetValue(instance, null)) { return true; } } return false; } private void WorldEventTick() { long num = Now(); bool flag = false; foreach (WorldEvent worldEvent in Data.WorldEvents) { if (worldEvent.Status == WorldEventStatus.Pending && worldEvent.StartTime <= num) { worldEvent.Status = WorldEventStatus.Active; AddWorldEventLog("World Event Started: " + worldEvent.EventName); WarfareRPC.Instance?.BroadcastGlobalMessage(FormatWorldEventStartedMessage(worldEvent)); flag = true; } if (worldEvent.Status == WorldEventStatus.Active && worldEvent.EndTime > 0 && num >= worldEvent.EndTime) { worldEvent.Status = WorldEventStatus.Expired; AddWorldEventLog("World Event Expired: " + worldEvent.EventName); WarfareRPC.Instance?.BroadcastGlobalMessage("World Event Expired:\n" + worldEvent.EventName + ""); flag = true; } } if (flag) { MarkDirty(); WarfareRPC.Instance?.SyncAllDataToClients(); } } public void AddWorldEventLog(string message) { if (Data.WorldEventLog == null) { Data.WorldEventLog = new List(); } Data.WorldEventLog.Insert(0, DateTimeOffset.UtcNow.ToLocalTime().ToString("yyyy-MM-dd HH:mm") + " - " + message); if (Data.WorldEventLog.Count > 40) { Data.WorldEventLog.RemoveRange(40, Data.WorldEventLog.Count - 40); } } private string FormatWorldEventStartedMessage(WorldEvent worldEvent) { return "World Event Started:\n" + WorldEventAsciiSymbol(worldEvent.EventType) + " " + (worldEvent.EventName ?? WorldEventTypeName(worldEvent.EventType)) + "\nLocation: " + Mathf.RoundToInt(worldEvent.Position.x) + ", " + Mathf.RoundToInt(worldEvent.Position.z) + ""; } private static string WorldEventAsciiSymbol(WorldEventType type) { return type switch { WorldEventType.BanditCamp => "[B]", WorldEventType.AncientShrine => "[A]", WorldEventType.ResourceRush => "[R]", WorldEventType.CorruptedTerritory => "[C]", WorldEventType.LostCaravan => "[L]", WorldEventType.WorldBoss => "[W]", WorldEventType.OdinChallenge => "[O]", WorldEventType.ClanChallenge => "[K]", WorldEventType.FreebornOpportunity => "[F]", _ => "[?]", }; } private bool BuildingProductionTick(long now) { int num = Mathf.Max(1, ClanConfig.BuildingProductionHours?.Value ?? 12) * 3600; bool result = false; foreach (ClanTerritory value in Data.Territories.Values) { List list = EnsureBuildings(value); if (list.Count == 0) { continue; } foreach (TerritoryBuilding item in list) { if (item != null && item.Type != TerritoryBuildingType.None && !item.Damaged) { if (item.LastProductionTimestamp == 0L) { item.LastProductionTimestamp = now; result = true; } else if (now - item.LastProductionTimestamp >= num) { int ticks = Mathf.Clamp((int)((now - item.LastProductionTimestamp) / num), 1, 8); ProduceBuildingOutput(value, item, ticks); item.LastProductionTimestamp = now; result = true; } } } } return result; } public string ForceBuildingProductionTick() { long num = Now(); int num2 = Mathf.Max(1, ClanConfig.BuildingProductionHours?.Value ?? 12) * 3600; foreach (ClanTerritory value in Data.Territories.Values) { foreach (TerritoryBuilding item in EnsureBuildings(value)) { if (item != null && item.Type != TerritoryBuildingType.None && !item.Damaged) { item.LastProductionTimestamp = num - num2; } } } bool num3 = BuildingProductionTick(num); if (num3) { MarkDirty(); WarfareRPC.Instance?.SyncAllDataToClients(); } if (!num3) { return "No active building production found."; } return "Building production tick forced."; } public string DebugTerritoryBuildings(Vector3 worldPosition) { //IL_0006: Unknown result type (might be due to invalid IL or missing references) //IL_0007: Unknown result type (might be due to invalid IL or missing references) ClanTerritory territory = Data.GetTerritory(GridExtensions.WorldToGrid(worldPosition)); if (territory == null) { return "No territory at current position."; } List list = EnsureBuildings(territory); string text = territory.DisplayName + " slots " + list.Count + "/" + GetBuildingSlots(territory); for (int i = 0; i < list.Count; i++) { TerritoryBuilding territoryBuilding = list[i]; text = text + "\n#" + (i + 1) + " " + BuildingName(territoryBuilding.Type) + " Lv" + territoryBuilding.Level + " damaged=" + territoryBuilding.Damaged + " next=" + territoryBuilding.LastProductionTimestamp; } return text; } public string DamageTerritoryBuildings(Vector3 worldPosition) { //IL_0006: Unknown result type (might be due to invalid IL or missing references) //IL_0007: Unknown result type (might be due to invalid IL or missing references) ClanTerritory territory = Data.GetTerritory(GridExtensions.WorldToGrid(worldPosition)); if (territory == null) { return "No territory at current position."; } int num = 0; foreach (TerritoryBuilding item in EnsureBuildings(territory)) { if (item != null) { item.Damaged = true; num++; } } MarkDirty(); WarfareRPC.Instance?.SyncAllDataToClients(); return "Damaged " + num + " buildings at " + territory.DisplayName + "."; } public string RepairTerritoryBuildings(Vector3 worldPosition) { //IL_0006: Unknown result type (might be due to invalid IL or missing references) //IL_0007: Unknown result type (might be due to invalid IL or missing references) ClanTerritory territory = Data.GetTerritory(GridExtensions.WorldToGrid(worldPosition)); if (territory == null) { return "No territory at current position."; } int num = 0; foreach (TerritoryBuilding item in EnsureBuildings(territory)) { if (item != null && item.Damaged) { item.Damaged = false; item.LastProductionTimestamp = Now(); num++; } } MarkDirty(); WarfareRPC.Instance?.SyncAllDataToClients(); return "Repaired " + num + " buildings at " + territory.DisplayName + "."; } private void ProduceBuildingOutput(ClanTerritory territory, TerritoryBuilding building, int ticks) { if (!IsProductionBuilding(building)) { return; } GetBuildingOutput(territory, building, out var prefab, out var display, out var amount, out var coins); int num = Mathf.Clamp(ticks, 1, 8); if (coins > 0) { long num2 = coins * num + Math.Max(0L, building.StoredCoins); building.StoredCoins = 0L; building.StoredProductionTicks = 0; building.StoredPrefabName = ""; building.StoredDisplayName = ""; building.StoredAmount = 0; long balanceBefore = 0L; long num3 = 0L; if (territory.OwnerClanId != 0) { Clan clan = Data.GetClan(territory.OwnerClanId); if (clan != null) { balanceBefore = clan.Treasury; clan.Treasury += num2; Economy.RecordTreasuryTransaction(clan, Now(), "Building Production Coins", num2, balanceBefore, clan.Treasury, BuildingName(building.Type), territory.GridX + "," + territory.GridY, building.Type.ToString()); num3 = clan.Treasury; } } else if (territory.FreebornOwnerId != 0L) { PlayerClanInfo player = Data.GetPlayer(territory.FreebornOwnerId); if (player != null) { balanceBefore = player.FreebornTreasury; player.FreebornTreasury += num2; num3 = player.FreebornTreasury; } } long num4 = BaseCoinBuildingIncome(building.Type); float num5 = 1f + (float)(Mathf.Max(1, building.Level) - 1) * 0.25f; AddBuildingProductionLog("Coin production: territory=" + territory.DisplayName + ", building=" + BuildingName(building.Type) + ", level=" + building.Level + ", base=" + num4 + ", multiplier=" + num5.ToString("0.00") + ", final=" + num2 + ", treasuryBefore=" + balanceBefore + ", treasuryAfter=" + num3 + "."); } else if (building.StoredProductionTicks >= 4) { AddBuildingProductionLog(BuildingName(building.Type) + " Lv" + building.Level + " production storage full in " + territory.DisplayName + " (4/4 ticks); existing production preserved."); } else { int num6 = Mathf.Clamp(num, 1, 4 - building.StoredProductionTicks); if (!string.IsNullOrEmpty(prefab) && amount > 0) { int normalAmount = amount * num6; int num7 = DistributeRootedMaterialOutput(territory, building, prefab, display, normalAmount); building.StoredPrefabName = prefab; building.StoredDisplayName = display; building.StoredAmount += num7; } building.StoredProductionTicks = Mathf.Clamp(building.StoredProductionTicks + num6, 0, 4); AddBuildingProductionLog(BuildingName(building.Type) + " Lv" + building.Level + " stored " + BuildingStoredOutputText(building) + " in " + territory.DisplayName + " (" + building.StoredProductionTicks + "/4 ticks)."); } } private static long BaseCoinBuildingIncome(TerritoryBuildingType type) { return type switch { TerritoryBuildingType.Treasury => 2L, TerritoryBuildingType.TradePost => 3L, TerritoryBuildingType.Marketplace => 5L, _ => 0L, }; } private int DistributeRootedMaterialOutput(ClanTerritory territory, TerritoryBuilding building, string prefab, string display, int normalAmount) { List activeRootTotems = GetActiveRootTotems(territory); if (normalAmount <= 0 || activeRootTotems.Count == 0) { return normalAmount; } int addedQuarterUnits = normalAmount * (4 - activeRootTotems.Count); int result = AccumulateRootQuarterUnits(territory.OwnerClanId, territory, building, prefab, display, ownerShare: true, addedQuarterUnits); foreach (RotTotem item in activeRootTotems) { int num = AccumulateRootQuarterUnits(item.PlacerClanId, territory, building, prefab, display, ownerShare: false, normalAmount); if (num > 0) { StoreRootLoot(item, territory, prefab, display, num); } } return result; } private int AccumulateRootQuarterUnits(int receivingClanId, ClanTerritory territory, TerritoryBuilding building, string prefab, string display, bool ownerShare, int addedQuarterUnits) { if (receivingClanId == 0 || addedQuarterUnits <= 0) { return 0; } int territoryX = (ownerShare ? territory.GridX : 0); int territoryY = (ownerShare ? territory.GridY : 0); long builtAt = (ownerShare ? building.BuiltAt : 0); RootProductionRemainder rootProductionRemainder = Data.RootProductionRemainders.Find((RootProductionRemainder r) => r.ReceivingClanId == receivingClanId && r.OwnerShare == ownerShare && r.TerritoryGridX == territoryX && r.TerritoryGridY == territoryY && r.BuildingBuiltAt == builtAt && r.PrefabName == prefab); if (rootProductionRemainder == null) { rootProductionRemainder = new RootProductionRemainder { ReceivingClanId = receivingClanId, TerritoryGridX = territoryX, TerritoryGridY = territoryY, BuildingBuiltAt = builtAt, PrefabName = prefab, DisplayName = display, OwnerShare = ownerShare }; Data.RootProductionRemainders.Add(rootProductionRemainder); } int num = rootProductionRemainder.QuarterUnits + addedQuarterUnits; int result = num / 4; rootProductionRemainder.QuarterUnits = num % 4; if (rootProductionRemainder.QuarterUnits == 0) { Data.RootProductionRemainders.Remove(rootProductionRemainder); } return result; } private void StoreRootLoot(RotTotem root, ClanTerritory territory, string prefab, string display, int amount) { Clan clan = Data.GetClan(root.PlacerClanId); if (clan == null || amount <= 0) { return; } List vault = Data.GetVault(root.PlacerClanId); bool num = vault.Find((ClanVaultItem v) => v.PrefabName == prefab) != null; int num2 = Economy.VaultCapacity(clan, Data.GetClanTerritoryCount(root.PlacerClanId)); if (num || vault.Count < num2) { Data.AddToVault(root.PlacerClanId, prefab, display, amount); return; } PendingRootLoot pendingRootLoot = Data.PendingRootLoot.Find((PendingRootLoot v) => v.RootClanId == root.PlacerClanId && v.TerritoryGridX == territory.GridX && v.TerritoryGridY == territory.GridY && v.TotemId == root.TotemId && v.PrefabName == prefab); if (pendingRootLoot == null) { pendingRootLoot = new PendingRootLoot { RootClanId = root.PlacerClanId, TerritoryGridX = territory.GridX, TerritoryGridY = territory.GridY, TotemId = root.TotemId, PrefabName = prefab, DisplayName = display }; Data.PendingRootLoot.Add(pendingRootLoot); } pendingRootLoot.Amount += amount; AddBuildingProductionLog("Pending Root loot: clan=" + root.PlacerClanId + ", territory=" + territory.DisplayName + ", totem=" + root.TotemId + ", " + amount + " " + display + " retained because Vault is full."); } private void FlushPendingRootLoot(int clanId, out int transferredStacks, out int transferredItems) { transferredStacks = 0; transferredItems = 0; Clan clan = Data.GetClan(clanId); if (clan == null) { return; } List list = Data.PendingRootLoot.FindAll((PendingRootLoot pendingRootLoot) => pendingRootLoot.RootClanId == clanId && pendingRootLoot.Amount > 0); list.Sort(delegate(PendingRootLoot a, PendingRootLoot b) { int num3 = a.TerritoryGridX.CompareTo(b.TerritoryGridX); if (num3 != 0) { return num3; } int num4 = a.TerritoryGridY.CompareTo(b.TerritoryGridY); if (num4 != 0) { return num4; } int num5 = a.TotemId.CompareTo(b.TotemId); return (num5 == 0) ? string.CompareOrdinal(a.PrefabName, b.PrefabName) : num5; }); foreach (PendingRootLoot loot in list) { List vault = Data.GetVault(clanId); bool num = vault.Find((ClanVaultItem item) => item.PrefabName == loot.PrefabName) != null; int num2 = Economy.VaultCapacity(clan, Data.GetClanTerritoryCount(clanId)); if (num || vault.Count < num2) { Data.AddToVault(clanId, loot.PrefabName, loot.DisplayName, loot.Amount); transferredStacks++; transferredItems += loot.Amount; Data.PendingRootLoot.Remove(loot); } } } private static bool IsProductionBuilding(TerritoryBuilding building) { if (building == null || building.Type == TerritoryBuildingType.None || building.Damaged) { return false; } GetBuildingOutput(null, building, out var prefab, out var _, out var amount, out var coins); if (coins <= 0) { if (!string.IsNullOrEmpty(prefab)) { return amount > 0; } return false; } return true; } public static bool HasStoredBuildingOutput(TerritoryBuilding building) { if (building != null) { return building.StoredAmount > 0; } return false; } public static string BuildingStoredOutputText(TerritoryBuilding building) { if (building == null) { return "No output"; } if (building.StoredCoins > 0) { return building.StoredCoins + " Coins"; } if (building.StoredAmount > 0) { return building.StoredAmount + " " + (string.IsNullOrEmpty(building.StoredDisplayName) ? "Items" : building.StoredDisplayName); } return "No output"; } private void AddBuildingProductionLog(string message) { if (Data.BuildingProductionLog == null) { Data.BuildingProductionLog = new List(); } Data.BuildingProductionLog.Insert(0, message); while (Data.BuildingProductionLog.Count > 20) { Data.BuildingProductionLog.RemoveAt(Data.BuildingProductionLog.Count - 1); } } private void AddToTerritoryStorage(ClanTerritory territory, string prefab, string display, int amount) { if (territory.TerritoryStorage == null) { territory.TerritoryStorage = new List(); } ClanVaultItem clanVaultItem = territory.TerritoryStorage.Find((ClanVaultItem v) => v.PrefabName == prefab); if (clanVaultItem != null) { clanVaultItem.Amount += amount; return; } territory.TerritoryStorage.Add(new ClanVaultItem { PrefabName = prefab, DisplayName = display, Amount = amount }); } private PlayerClanInfo GetBestClanRolePlayer(int clanId, ClanRole role) { PlayerClanInfo result = null; float num = 0f; foreach (PlayerClanInfo clanMember in Data.GetClanMembers(clanId)) { float roleFactor = RoleUtils.GetRoleFactor(clanMember, role); if (roleFactor > num) { num = roleFactor; result = clanMember; } } return result; } private float GetBuildingProductionMultiplier(PlayerClanInfo player, TerritoryBuildingType type) { if (IsCoinBuilding(type)) { return 1f + RoleUtils.GetRoleFactor(player, ClanRole.Merchant) * ((ClanConfig.MerchantEconomicRewardBonus?.Value ?? 10f) / 100f); } if (IsResourceBuilding(type)) { return 1f + RoleUtils.GetRoleFactor(player, ClanRole.Gatherer) * 0.25f; } return 1f; } public static TerritoryBuildingType[] GetBuildableBuildingTypes() { return BuildableBuildingTypes; } public static string[] GetBuildingCategoryNames() { return BuildingCategoryNames; } public static TerritoryBuildingType[] GetBuildingCategoryTypes(int categoryIndex) { if (categoryIndex < 0 || categoryIndex >= BuildingCategoryTypes.Length) { return new TerritoryBuildingType[0]; } return BuildingCategoryTypes[categoryIndex]; } public static int BuildingMaxLevel(TerritoryBuildingType type) { if (type != TerritoryBuildingType.None) { return GetBuildingIntConfig(ClanConfig.BuildingMaxLevelByType?.Value, type, 5); } return 0; } public static int BuildingMaxCopies(TerritoryBuildingType type) { return (type != TerritoryBuildingType.None) ? 1 : 0; } public static long BuildingBuildCost(TerritoryBuildingType type) { return GetBuildingIntConfig(ClanConfig.BuildingBuildCostByType?.Value, type, ClanConfig.BuildingBuildCostBase?.Value ?? 500); } public static long BuildingUpgradeCost(TerritoryBuildingType type, int currentLevel) { long num = GetBuildingIntConfig(ClanConfig.BuildingUpgradeCostByType?.Value, type, ClanConfig.BuildingUpgradeCostBase?.Value ?? 500); int num2 = Mathf.Clamp(currentLevel + 1, 2, BuildingMaxLevel(type)); return Mathf.Max(1, num2) * num; } public static long BuildingRepairCost(TerritoryBuildingType type, int level) { return (long)GetBuildingIntConfig(ClanConfig.BuildingRepairCostByType?.Value, type, ClanConfig.BuildingRepairCostBase?.Value ?? 250) * (long)Mathf.Clamp(level, 1, BuildingMaxLevel(type)); } public static string BuildingRequirementText(TerritoryBuildingType type) { switch (type) { case TerritoryBuildingType.LumberCamp: case TerritoryBuildingType.Quarry: case TerritoryBuildingType.Farm: case TerritoryBuildingType.Watchtower: case TerritoryBuildingType.HunterLodge: return "Owned land territory"; case TerritoryBuildingType.FishingPort: return "Coastal or Ocean territory"; case TerritoryBuildingType.TownCenter: return "Owned land territory"; case TerritoryBuildingType.ClanHall: return "Capital only"; case TerritoryBuildingType.TaxOffice: case TerritoryBuildingType.Embassy: return "Capital or large Colony"; case TerritoryBuildingType.Marketplace: return "Town Center; Capital or Colony; not Outpost"; case TerritoryBuildingType.Longhouse: return "Capital, Homestead, or Colony"; case TerritoryBuildingType.Shipyard: return "Town Center and coastal territory"; default: return "Town Center required"; } } private static int GetBuildingIntConfig(string map, TerritoryBuildingType type, int fallback) { string buildingStringConfig = GetBuildingStringConfig(map, type, null); if (!string.IsNullOrEmpty(buildingStringConfig) && int.TryParse(buildingStringConfig, out var result)) { return result; } buildingStringConfig = GetMapValue(map, "Default"); if (!string.IsNullOrEmpty(buildingStringConfig) && int.TryParse(buildingStringConfig, out result)) { return result; } return fallback; } private static string GetBuildingStringConfig(string map, TerritoryBuildingType type, string fallback) { string mapValue = GetMapValue(map, type.ToString()); if (!string.IsNullOrEmpty(mapValue)) { return mapValue; } return fallback; } private static string GetMapValue(string map, string key) { if (string.IsNullOrEmpty(map) || string.IsNullOrEmpty(key)) { return ""; } string[] array = map.Split(new char[1] { ',' }, StringSplitOptions.RemoveEmptyEntries); for (int i = 0; i < array.Length; i++) { string[] array2 = array[i].Split(new char[1] { '=' }, 2); if (array2.Length == 2 && string.Equals(array2[0].Trim(), key, StringComparison.OrdinalIgnoreCase)) { return array2[1].Trim(); } } return ""; } public static int GetBuildingSlots(ClanTerritory territory) { if (territory == null) { return 0; } int num; int num2; switch (territory.Type) { case TerritoryType.Capital: num = 6; num2 = 8; break; case TerritoryType.Homestead: num = 4; num2 = 6; break; default: num = 3; num2 = 4; break; } int num3 = num + Mathf.Max(0, territory.SizeLevel - 1); if (territory.Type == TerritoryType.Capital && HasBuilding(territory, TerritoryBuildingType.ClanHall)) { num3++; } TerritoryBuilding territoryBuilding = FindBuilding(territory, TerritoryBuildingType.Longhouse); if (territoryBuilding != null && territoryBuilding.Level >= 3) { num3++; } return Mathf.Clamp(num3, num, num2); } private static TerritoryBuilding FindBuilding(ClanTerritory territory, TerritoryBuildingType type) { if (territory?.Buildings == null) { return null; } foreach (TerritoryBuilding building in territory.Buildings) { if (building != null && building.Type == type) { return building; } } return null; } private static bool HasBuilding(ClanTerritory territory, TerritoryBuildingType type) { return FindBuilding(territory, type) != null; } private static bool IsLargeColony(ClanTerritory territory) { if (territory != null && territory.Type == TerritoryType.Colony) { return territory.SizeLevel >= 2; } return false; } public static List EnsureBuildings(ClanTerritory territory) { if (territory.Buildings == null) { territory.Buildings = new List(); } return territory.Buildings; } public static string BuildingName(TerritoryBuildingType type) { return type switch { TerritoryBuildingType.TownCenter => "Town Center", TerritoryBuildingType.ClanHall => "Clan Hall", TerritoryBuildingType.Longhouse => "Longhouse", TerritoryBuildingType.Warehouse => "Warehouse", TerritoryBuildingType.Treasury => "Treasury", TerritoryBuildingType.TradePost => "Trade Post", TerritoryBuildingType.Marketplace => "Marketplace", TerritoryBuildingType.TaxOffice => "Tax Office", TerritoryBuildingType.Embassy => "Embassy", TerritoryBuildingType.LogisticsPost => "Logistics Post", TerritoryBuildingType.LumberCamp => "Lumber Camp", TerritoryBuildingType.Quarry => "Quarry", TerritoryBuildingType.CopperMine => "Copper Mine", TerritoryBuildingType.IronMine => "Iron Mine", TerritoryBuildingType.SilverMine => "Silver Mine", TerritoryBuildingType.Farm => "Farm", TerritoryBuildingType.FishingPort => "Fishing Port", TerritoryBuildingType.HunterLodge => "Hunter Lodge", TerritoryBuildingType.Apiary => "Apiary", TerritoryBuildingType.Tannery => "Tannery", TerritoryBuildingType.Workshop => "Workshop", TerritoryBuildingType.Smithy => "Smithy", TerritoryBuildingType.Shipyard => "Shipyard", TerritoryBuildingType.Barracks => "Barracks", TerritoryBuildingType.Watchtower => "Watchtower", TerritoryBuildingType.Shrine => "Shrine", TerritoryBuildingType.HealerHut => "Healer Hut", _ => "Empty", }; } public static string BuildingIconName(TerritoryBuildingType type) { return type switch { TerritoryBuildingType.TownCenter => "town_center", TerritoryBuildingType.ClanHall => "clan_hall", TerritoryBuildingType.Longhouse => "longhouse", TerritoryBuildingType.Warehouse => "warehouse", TerritoryBuildingType.Treasury => "treasury", TerritoryBuildingType.TradePost => "trade_post", TerritoryBuildingType.Marketplace => "marketplace", TerritoryBuildingType.TaxOffice => "tax_office", TerritoryBuildingType.Embassy => "embassy", TerritoryBuildingType.LogisticsPost => "logistics_post", TerritoryBuildingType.LumberCamp => "lumber_camp", TerritoryBuildingType.Quarry => "quarry", TerritoryBuildingType.CopperMine => "copper_mine", TerritoryBuildingType.IronMine => "iron_mine", TerritoryBuildingType.SilverMine => "silver_mine", TerritoryBuildingType.Farm => "farm", TerritoryBuildingType.FishingPort => "fishing_port", TerritoryBuildingType.HunterLodge => "hunter_lodge", TerritoryBuildingType.Apiary => "apiary", TerritoryBuildingType.Tannery => "tannery", TerritoryBuildingType.Workshop => "workshop", TerritoryBuildingType.Smithy => "smithy", TerritoryBuildingType.Shipyard => "shipyard", TerritoryBuildingType.Barracks => "barracks", TerritoryBuildingType.Watchtower => "watchtower", TerritoryBuildingType.Shrine => "shrine", TerritoryBuildingType.HealerHut => "healer_hut", _ => "empty_slot", }; } public static bool IsCoinBuilding(TerritoryBuildingType type) { if (type != TerritoryBuildingType.Treasury && type != TerritoryBuildingType.Marketplace) { return type == TerritoryBuildingType.TradePost; } return true; } public static bool IsResourceBuilding(TerritoryBuildingType type) { switch (type) { case TerritoryBuildingType.LumberCamp: case TerritoryBuildingType.Quarry: case TerritoryBuildingType.CopperMine: case TerritoryBuildingType.IronMine: case TerritoryBuildingType.SilverMine: case TerritoryBuildingType.Farm: case TerritoryBuildingType.FishingPort: case TerritoryBuildingType.HunterLodge: case TerritoryBuildingType.Apiary: case TerritoryBuildingType.Tannery: case TerritoryBuildingType.Shipyard: return true; default: return false; } } private static int LevelValue(TerritoryBuilding building) { return Mathf.Clamp(building?.Level ?? 1, 1, 5); } public static void GetBuildingOutput(ClanTerritory territory, TerritoryBuilding building, out string prefab, out string display, out int amount, out long coins) { prefab = ""; display = ""; amount = 0; coins = 0L; int num = LevelValue(building); switch (building?.Type ?? TerritoryBuildingType.None) { case TerritoryBuildingType.Treasury: coins = (long)Mathf.Round(2f * (1f + (float)(num - 1) * 0.25f)); break; case TerritoryBuildingType.TradePost: coins = (long)Mathf.Round(3f * (1f + (float)(num - 1) * 0.25f)); break; case TerritoryBuildingType.Marketplace: coins = (long)Mathf.Round(5f * (1f + (float)(num - 1) * 0.25f)); break; case TerritoryBuildingType.LumberCamp: prefab = "Wood"; display = "Wood"; amount = num; break; case TerritoryBuildingType.Quarry: prefab = "Stone"; display = "Stone"; amount = num; break; case TerritoryBuildingType.CopperMine: prefab = "CopperOre"; display = "Copper Ore"; amount = num; break; case TerritoryBuildingType.IronMine: prefab = "IronScrap"; display = "Scrap Iron"; amount = num; break; case TerritoryBuildingType.SilverMine: prefab = "SilverOre"; display = "Silver Ore"; amount = num; break; case TerritoryBuildingType.Farm: GetFarmOutput(territory, out prefab, out display); amount = num; break; case TerritoryBuildingType.FishingPort: prefab = "FishRaw"; display = "Raw Fish"; amount = num; break; case TerritoryBuildingType.HunterLodge: GetHunterOutput(territory, out prefab, out display); amount = num; break; case TerritoryBuildingType.Apiary: prefab = "Honey"; display = "Honey"; amount = num; break; case TerritoryBuildingType.Tannery: prefab = "LeatherScraps"; display = "Leather Scraps"; amount = num; break; case TerritoryBuildingType.Shipyard: prefab = "FineWood"; display = "Fine Wood"; amount = num; break; case TerritoryBuildingType.Barracks: case TerritoryBuildingType.Watchtower: case TerritoryBuildingType.TownCenter: case TerritoryBuildingType.Longhouse: case TerritoryBuildingType.Warehouse: case TerritoryBuildingType.TaxOffice: case TerritoryBuildingType.Embassy: case TerritoryBuildingType.LogisticsPost: case TerritoryBuildingType.Workshop: case TerritoryBuildingType.Smithy: break; } } private static void GetFarmOutput(ClanTerritory territory, out string prefab, out string display) { string text = (territory?.BiomeName ?? "").Replace(" ", "").ToLowerInvariant(); if (text.Contains("black")) { prefab = "Blueberries"; display = "Blueberries"; } else if (text.Contains("swamp")) { prefab = "Turnip"; display = "Turnip"; } else if (text.Contains("mountain")) { prefab = "Onion"; display = "Onion"; } else if (text.Contains("plains")) { prefab = "Barley"; display = "Barley"; } else if (text.Contains("mist")) { prefab = "JotunPuffs"; display = "Jotun Puffs"; } else if (text.Contains("ash")) { prefab = "Vineberry"; display = "Vineberry"; } else { prefab = "Carrot"; display = "Carrot"; } } private static void GetHunterOutput(ClanTerritory territory, out string prefab, out string display) { string text = (territory?.BiomeName ?? "").Replace(" ", "").ToLowerInvariant(); if (text.Contains("black")) { prefab = "RawMeat"; display = "Raw Meat"; } else if (text.Contains("swamp")) { prefab = "Entrails"; display = "Entrails"; } else if (text.Contains("mountain")) { prefab = "WolfMeat"; display = "Wolf Meat"; } else if (text.Contains("plains")) { prefab = "LoxMeat"; display = "Lox Meat"; } else if (text.Contains("mist")) { prefab = "HareMeat"; display = "Hare Meat"; } else if (text.Contains("ash")) { prefab = "RawMeat"; display = "Raw Meat"; } else { prefab = "DeerMeat"; display = "Deer Meat"; } } public static string BuildingProductionText(ClanTerritory territory, TerritoryBuilding building) { string text = BuildingProductionValueText(territory, building); GetBuildingOutput(territory, building, out var _, out var _, out var amount, out var coins); if (coins > 0 || amount > 0) { return "Production per 12h: " + text; } return text; } public static string BuildingProductionValueText(ClanTerritory territory, TerritoryBuilding building) { GetBuildingOutput(territory, building, out var _, out var display, out var amount, out var coins); if (coins > 0) { return coins + " Coins"; } if (amount > 0) { return amount + " " + display; } if (building == null || building.Type == TerritoryBuildingType.None) { return "No output"; } return building.Type switch { TerritoryBuildingType.TaxOffice => "Tax efficiency " + LevelValue(building) + "%", TerritoryBuildingType.Workshop => "Build/repair discount " + LevelValue(building) * 2 + "%", TerritoryBuildingType.Smithy => "Metal/military repair discount " + LevelValue(building) * 2 + "%", TerritoryBuildingType.Watchtower => "Defense " + LevelValue(building), TerritoryBuildingType.ClanHall => "Capital bonuses and slot support", TerritoryBuildingType.Longhouse => "Upkeep reduction and local slot support", TerritoryBuildingType.Warehouse => "Storage support only", TerritoryBuildingType.TownCenter => "Unlocks advanced buildings", _ => "Coming in future update", }; } public static bool IsBuildingAllowedInBiome(ClanTerritory territory, TerritoryBuildingType type) { if (territory == null || type == TerritoryBuildingType.None) { return false; } string text = (territory.BiomeName ?? "").Replace(" ", "").ToLowerInvariant(); bool flag = IsCoastalTerritory(territory) || text.Contains("ocean") || text.Contains("coast"); bool flag2 = HasBuilding(territory, TerritoryBuildingType.TownCenter); switch (type) { case TerritoryBuildingType.LumberCamp: case TerritoryBuildingType.Quarry: case TerritoryBuildingType.Farm: case TerritoryBuildingType.Watchtower: case TerritoryBuildingType.HunterLodge: return !text.Contains("ocean"); case TerritoryBuildingType.FishingPort: return flag; case TerritoryBuildingType.TownCenter: return !text.Contains("ocean"); case TerritoryBuildingType.ClanHall: return territory.Type == TerritoryType.Capital; case TerritoryBuildingType.TaxOffice: case TerritoryBuildingType.Embassy: if (territory.Type != TerritoryType.Capital) { return IsLargeColony(territory); } return true; case TerritoryBuildingType.Marketplace: if (flag2) { if (territory.Type != TerritoryType.Capital) { return territory.Type == TerritoryType.Colony; } return true; } return false; case TerritoryBuildingType.Longhouse: if (territory.Type != TerritoryType.Capital && territory.Type != TerritoryType.Homestead) { return territory.Type == TerritoryType.Colony; } return true; case TerritoryBuildingType.Treasury: case TerritoryBuildingType.TradePost: case TerritoryBuildingType.CopperMine: case TerritoryBuildingType.IronMine: case TerritoryBuildingType.SilverMine: case TerritoryBuildingType.Barracks: case TerritoryBuildingType.Warehouse: case TerritoryBuildingType.LogisticsPost: case TerritoryBuildingType.Apiary: case TerritoryBuildingType.Tannery: case TerritoryBuildingType.Workshop: case TerritoryBuildingType.Shrine: case TerritoryBuildingType.HealerHut: if (flag2) { return !text.Contains("ocean"); } return false; case TerritoryBuildingType.Shipyard: return flag2 && flag; default: return flag2; } } private static bool BuildingRequirementMatches(ClanTerritory territory, string requirement) { if (string.IsNullOrEmpty(requirement) || string.Equals(requirement, "Any", StringComparison.OrdinalIgnoreCase)) { return true; } string[] array = requirement.Split(new char[1] { '|' }, StringSplitOptions.RemoveEmptyEntries); for (int i = 0; i < array.Length; i++) { string a = array[i].Trim().Replace(" ", ""); string text = (territory?.BiomeName ?? "").Replace(" ", ""); if (string.Equals(a, "Capital", StringComparison.OrdinalIgnoreCase) && territory != null && territory.IsCapital) { return true; } if (string.Equals(a, "Land", StringComparison.OrdinalIgnoreCase) && !text.ToLowerInvariant().Contains("ocean")) { return true; } if (string.Equals(a, "Coastal", StringComparison.OrdinalIgnoreCase) && IsCoastalTerritory(territory)) { return true; } if (string.Equals(a, text, StringComparison.OrdinalIgnoreCase)) { return true; } } return false; } private static bool IsCoastalTerritory(ClanTerritory territory) { string text = (territory?.DisplayName ?? "").ToLowerInvariant(); if (!(territory?.BiomeName ?? "").ToLowerInvariant().Contains("coast") && !text.Contains("coast")) { return text.Contains("sea"); } return true; } public string BuildTerritoryBuilding(long playerId, Vector2i grid, TerritoryBuildingType type) { //IL_0002: Unknown result type (might be due to invalid IL or missing references) return ManageTerritoryBuilding(playerId, grid, 0, type, 0); } public string UpgradeTerritoryBuilding(long playerId, Vector2i grid, int slot) { //IL_0002: Unknown result type (might be due to invalid IL or missing references) return ManageTerritoryBuilding(playerId, grid, 1, TerritoryBuildingType.None, slot); } public string RepairTerritoryBuilding(long playerId, Vector2i grid, int slot) { //IL_0002: Unknown result type (might be due to invalid IL or missing references) return ManageTerritoryBuilding(playerId, grid, 2, TerritoryBuildingType.None, slot); } public string DestroyTerritoryBuilding(long playerId, Vector2i grid, int slot) { //IL_0002: Unknown result type (might be due to invalid IL or missing references) return ManageTerritoryBuilding(playerId, grid, 3, TerritoryBuildingType.None, slot); } public string ManageTerritoryBuilding(long playerId, Vector2i grid, int action, TerritoryBuildingType type, int slot) { //IL_0038: Unknown result type (might be due to invalid IL or missing references) PlayerClanInfo player = Data.GetPlayer(playerId); if (player == null || (player.ClanId == 0 && !player.IsFreeborn)) { return "You are not in a clan or Freeborn."; } if (action == 5) { return ClaimAllBuildingOutput(player); } ClanTerritory territory = Data.GetTerritory(grid); if (territory == null) { return "Territory not found."; } if (!CanManageTerritoryBuildings(player, territory)) { return "You cannot manage buildings here."; } List list = EnsureBuildings(territory); int buildingSlots = GetBuildingSlots(territory); if (action == 0) { if (type == TerritoryBuildingType.None) { return "Select a building type."; } if (list.Count >= buildingSlots) { return "No empty building slots."; } if (!IsBuildingAllowedInBiome(territory, type)) { return "This building cannot be built in this biome or territory type."; } int num = 0; foreach (TerritoryBuilding item in list) { if (item.Type == type) { num++; } } if (num >= BuildingMaxCopies(type)) { return "Maximum one copy of each building type per territory."; } long cost = ApplyBuilderDiscount(player, BuildingBuildCost(type), upgrade: false); if (!PayBuildingCost(player, cost)) { return "" + ((player.IsFreeborn && player.ClanId == 0) ? "Freeborn Treasury" : "Clan Treasury") + " does not have enough coins."; } list.Add(new TerritoryBuilding { Type = type, Level = 1, Damaged = false, BuiltAt = Now(), LastProductionTimestamp = Now() }); player.BuildingsContributed++; AddBuildingMilestone(player, territory, type); MarkDirty(); return "Built " + BuildingName(type) + "."; } if (slot < 0 || slot >= list.Count) { return "Building slot not found."; } TerritoryBuilding territoryBuilding = list[slot]; switch (action) { case 1: { if (territoryBuilding.Level >= BuildingMaxLevel(territoryBuilding.Type)) { return "Building is already at maximum level."; } long cost3 = ApplyBuilderDiscount(player, BuildingUpgradeCost(territoryBuilding.Type, territoryBuilding.Level), upgrade: true); if (!PayBuildingCost(player, cost3)) { return "" + ((player.IsFreeborn && player.ClanId == 0) ? "Freeborn Treasury" : "Clan Treasury") + " does not have enough coins."; } territoryBuilding.Level++; territoryBuilding.Damaged = false; MarkDirty(); return "Upgraded " + BuildingName(territoryBuilding.Type) + " to level " + territoryBuilding.Level + "."; } case 2: { if (!territoryBuilding.Damaged) { return "Building is not damaged."; } long cost2 = ApplyBuilderDiscount(player, BuildingRepairCost(territoryBuilding.Type, territoryBuilding.Level), upgrade: false); if (!PayBuildingCost(player, cost2)) { return "" + ((player.IsFreeborn && player.ClanId == 0) ? "Freeborn Treasury" : "Clan Treasury") + " does not have enough coins."; } territoryBuilding.Damaged = false; MarkDirty(); return "Repaired " + BuildingName(territoryBuilding.Type) + "."; } case 3: { string text = BuildingName(territoryBuilding.Type); list.RemoveAt(slot); MarkDirty(); return "Destroyed " + text + "."; } case 4: return ClaimTerritoryBuildingOutput(player, territory, territoryBuilding); default: return "Unknown building action."; } } private string ClaimTerritoryBuildingOutput(PlayerClanInfo player, ClanTerritory territory, TerritoryBuilding building) { if (building == null || building.StoredAmount <= 0 || string.IsNullOrEmpty(building.StoredPrefabName)) { return "No stored material production to claim."; } int num = TransferBuildingMaterialToOwnerVault(player, territory, building); if (num <= 0) { return "Clan Vault is full. Stored production remains on the building."; } string text = (string.IsNullOrEmpty(building.StoredDisplayName) ? building.StoredPrefabName : building.StoredDisplayName); building.StoredAmount -= num; if (building.StoredAmount <= 0) { building.StoredProductionTicks = 0; building.StoredPrefabName = ""; building.StoredDisplayName = ""; building.StoredAmount = 0; building.LastProductionTimestamp = Now(); } MarkDirty(); AddBuildingProductionLog(BuildingName(building.Type) + " claimed " + num + " " + text + "; remaining stored=" + building.StoredAmount + "."); return "Claimed " + num + " " + text + "."; } private int TransferBuildingMaterialToOwnerVault(PlayerClanInfo player, ClanTerritory territory, TerritoryBuilding building) { int storedAmount = building.StoredAmount; if (storedAmount <= 0) { return 0; } string displayName = (string.IsNullOrEmpty(building.StoredDisplayName) ? building.StoredPrefabName : building.StoredDisplayName); if (player.IsFreeborn && player.ClanId == 0) { List freebornVault = Data.GetFreebornVault(player.PlayerId); if (freebornVault.Find((ClanVaultItem v) => v.PrefabName == building.StoredPrefabName) == null && freebornVault.Count >= FreebornVaultCapacity(player)) { return 0; } Data.AddToFreebornVault(player.PlayerId, building.StoredPrefabName, displayName, storedAmount); return storedAmount; } Clan clan = Data.GetClan(territory.OwnerClanId); if (clan == null) { return 0; } List vault = Data.GetVault(territory.OwnerClanId); if (vault.Find((ClanVaultItem v) => v.PrefabName == building.StoredPrefabName) == null && vault.Count >= Economy.VaultCapacity(clan, Data.GetClanTerritoryCount(territory.OwnerClanId))) { return 0; } Data.AddToVault(territory.OwnerClanId, building.StoredPrefabName, displayName, storedAmount); return storedAmount; } private string ClaimAllBuildingOutput(PlayerClanInfo player) { if (player == null || player.ClanId == 0 || player.Rank < ClanRank.Officer) { return "Only Officers and the Jarl may use Claim All."; } List clanTerritories = Data.GetClanTerritories(player.ClanId); clanTerritories.Sort(delegate(ClanTerritory a, ClanTerritory b) { int num4 = a.GridX.CompareTo(b.GridX); return (num4 == 0) ? a.GridY.CompareTo(b.GridY) : num4; }); FlushPendingRootLoot(player.ClanId, out var transferredStacks, out var transferredItems); int num = 0; foreach (ClanTerritory item in clanTerritories) { List list = EnsureBuildings(item); for (int num2 = 0; num2 < list.Count; num2++) { TerritoryBuilding territoryBuilding = list[num2]; if (territoryBuilding == null || territoryBuilding.StoredAmount <= 0 || string.IsNullOrEmpty(territoryBuilding.StoredPrefabName)) { continue; } int num3 = TransferBuildingMaterialToOwnerVault(player, item, territoryBuilding); if (num3 > 0) { transferredItems += num3; transferredStacks++; territoryBuilding.StoredAmount -= num3; if (territoryBuilding.StoredAmount <= 0) { territoryBuilding.StoredProductionTicks = 0; territoryBuilding.StoredPrefabName = ""; territoryBuilding.StoredDisplayName = ""; territoryBuilding.StoredAmount = 0; territoryBuilding.LastProductionTimestamp = Now(); } } num += territoryBuilding.StoredAmount; } } foreach (PendingRootLoot item2 in Data.PendingRootLoot) { if (item2.RootClanId == player.ClanId && item2.Amount > 0) { num += item2.Amount; } } MarkDirty(); AddBuildingProductionLog("Claim All clan=" + player.ClanId + ": transferredStacks=" + transferredStacks + ", transferredItems=" + transferredItems + ", remaining=" + num + ". Pending Root loot is claimed here after Vault space becomes available."); return "Claim All completed:\nTransferred: " + transferredStacks + " item stacks / " + transferredItems + " total items\nRemaining stored because Vault is full: " + num + " items"; } private bool CanManageTerritoryBuildings(PlayerClanInfo player, ClanTerritory territory) { if (player.IsFreeborn && player.ClanId == 0) { return territory.FreebornOwnerId == player.PlayerId; } if (territory.OwnerClanId == player.ClanId) { return player.Rank >= ClanRank.Officer; } return false; } private long ApplyBuilderDiscount(PlayerClanInfo player, long cost, bool upgrade) { float num = Mathf.Clamp01(((!upgrade) ? (ClanConfig.BuilderBuildCostReduction?.Value ?? 15f) : (ClanConfig.BuilderUpgradeCostReduction?.Value ?? 10f)) / 100f * RoleUtils.GetRoleFactor(player, ClanRole.Builder)); if (upgrade) { num = Mathf.Max(num, Mathf.Clamp01((ClanConfig.WardenUpgradeCostReduction?.Value ?? 15f) / 100f * RoleUtils.GetRoleFactor(player, ClanRole.Warden))); } return Math.Max(0L, (long)Mathf.Floor((float)cost * (1f - num))); } private bool PayBuildingCost(PlayerClanInfo player, long cost) { if (player.IsFreeborn && player.ClanId == 0) { if (player.FreebornTreasury < cost) { return false; } player.FreebornTreasury -= cost; return true; } Clan clan = Data.GetClan(player.ClanId); if (clan == null || clan.Treasury < cost) { return false; } ChangeClanTreasury(clan, -cost, "Purchase", "Clan purchase"); return true; } private void RotTick() { //IL_0036: Unknown result type (might be due to invalid IL or missing references) bool flag = false; for (int num = Data.RotTotems.Count - 1; num >= 0; num--) { RotTotem rotTotem = Data.RotTotems[num]; ClanTerritory clanTerritory = ((rotTotem == null) ? null : Data.GetTerritory(rotTotem.GridPosition)); if (rotTotem == null || rotTotem.State == TotemState.Destroyed || clanTerritory == null || clanTerritory.OwnerClanId == 0 || clanTerritory.OwnerClanId == rotTotem.PlacerClanId) { Data.RotTotems.RemoveAt(num); flag = true; } else { if (rotTotem.TotemId <= 0) { rotTotem.TotemId = Data.NextRotTotemId++; flag = true; } if (string.IsNullOrEmpty(rotTotem.PrefabName)) { rotTotem.PrefabName = "guard_stone"; flag = true; } if (rotTotem.State != TotemState.Active) { rotTotem.State = TotemState.Active; flag = true; } } } foreach (ClanTerritory value in Data.Territories.Values) { int count = GetActiveRootTotems(value).Count; if (value.RotCount != count) { value.RotCount = count; flag = true; } if (count >= 4 && value.State != TerritoryState.Rooted) { value.State = TerritoryState.Rooted; flag = true; } else if (count > 0 && value.State == TerritoryState.Normal) { value.State = TerritoryState.Contested; flag = true; } else if (count == 0 && (value.State == TerritoryState.Rooted || value.State == TerritoryState.Contested)) { value.State = TerritoryState.Normal; flag = true; } } if (flag) { MarkDirty(); } } private void SeasonTick() { int num = ClanConfig.SeasonLengthDays?.Value ?? 60; if (num > 0) { if (Data.SeasonStartTimestamp == 0L) { Data.SeasonStartTimestamp = Now(); } else if (Now() - Data.SeasonStartTimestamp >= (long)num * 86400L) { RunCycleOfOdin(); } } } public void RunCycleOfOdin() { Data.SeasonNumber++; Data.SeasonStartTimestamp = Now(); Data.Territories.Clear(); Data.ClanVaults.Clear(); Data.RotTotems.Clear(); Data.Wars.Clear(); Data.ActiveCaptures.Clear(); Data.ActiveCracks.Clear(); int num = ClanConfig.StartingTreasury?.Value ?? 0; foreach (Clan value in Data.Clans.Values) { long treasury = value.Treasury; value.Treasury = num; Economy.RecordTreasuryTransaction(value, Now(), "Season Reset", value.Treasury - treasury, treasury, value.Treasury, "Cycle of Odin"); value.VaultTier = 1; value.ExtraTerritorySlots = 0; } MarkDirty(); SaveData(); WarfareRPC.Instance?.BroadcastGlobalMessage($"== THE CYCLE OF ODIN ==\nSeason {Data.SeasonNumber} begins. The map is reborn \ufffd claim it anew!"); WarfareRPC.Instance?.SyncAllDataToClients(); Plugin.Log.LogWarning((object)$"Cycle of Odin: season {Data.SeasonNumber} started."); } public string PlayAsFreeborn(long playerId, string playerName) { Plugin.Log.LogInfo((object)("[FCW Manager] PlayAsFreeborn requested playerId=" + playerId + " playerName='" + playerName + "'.")); ConfigEntry freebornEnabled = ClanConfig.FreebornEnabled; if (freebornEnabled == null || !freebornEnabled.Value) { Plugin.Log.LogWarning((object)"[FCW Manager] PlayAsFreeborn rejected: FreebornEnabled is false."); return "Freeborn play is disabled."; } PlayerClanInfo orCreatePlayer = Data.GetOrCreatePlayer(playerId, playerName); if (orCreatePlayer.ClanId != 0) { Plugin.Log.LogWarning((object)("[FCW Manager] PlayAsFreeborn rejected: player already in clan " + orCreatePlayer.ClanId + ".")); return "You are already in a clan."; } orCreatePlayer.IsFreeborn = true; if (orCreatePlayer.FreebornSinceTimestamp == 0L) { orCreatePlayer.FreebornSinceTimestamp = Now(); } orCreatePlayer.FreebornLastActiveTimestamp = Now(); MarkDirty(); Plugin.Log.LogInfo((object)("[FCW Manager] PlayAsFreeborn success playerId=" + playerId + ".")); return "You walk the Freeborn path. Claim a Homestead to begin."; } private bool IsProtectedByOdinsPeace(ClanTerritory territory) { if (territory != null && territory.FreebornOwnerId != 0L && territory.OdinsPeaceActive) { ConfigEntry odinsPeaceEnabled = ClanConfig.OdinsPeaceEnabled; if (odinsPeaceEnabled != null && odinsPeaceEnabled.Value) { if (territory.OdinsPeaceChallengeEndTimestamp > 0 && Now() >= territory.OdinsPeaceChallengeEndTimestamp) { territory.OdinsPeaceActive = false; territory.OdinsPeaceChallengeEndTimestamp = 0L; MarkDirty(); return false; } return true; } } return false; } public bool IsFreebornProtectedTerritory(ClanTerritory territory) { return IsProtectedByOdinsPeace(territory); } public string ChallengeFreeborn(long playerId, Vector2i grid) { //IL_004f: Unknown result type (might be due to invalid IL or missing references) ConfigEntry challengeFreebornEnabled = ClanConfig.ChallengeFreebornEnabled; if (challengeFreebornEnabled == null || !challengeFreebornEnabled.Value) { return "Freeborn challenges are disabled."; } PlayerClanInfo player = Data.GetPlayer(playerId); if (player == null || player.ClanId == 0) { return "You are not in a clan."; } if (player.Rank < ClanRank.Officer) { return "Only Officers and the Jarl may challenge Odin's Peace."; } ClanTerritory territory = Data.GetTerritory(grid); if (territory == null || territory.FreebornOwnerId == 0L) { return "That is not a Freeborn territory."; } if (!territory.OdinsPeaceActive) { return "Odin's Peace is not active there."; } long num = Now(); int num2 = ClanConfig.ChallengeCooldownHours?.Value ?? 72; if (territory.LastChallengeTimestamp > 0 && num - territory.LastChallengeTimestamp < (long)num2 * 3600L) { return "This Homestead was challenged too recently."; } Clan clan = Data.GetClan(player.ClanId); long num3 = ClanConfig.ChallengeTributeCoins?.Value ?? 1000; int num4 = ClanConfig.ChallengeTributeSurtlingCores?.Value ?? 10; if (clan.Treasury < num3) { return $"Treasury needs {num3} coins (has {clan.Treasury})."; } if (!ConsumeVaultItem(clan.ClanId, "SurtlingCore", num4)) { return $"Clan vault needs {num4} Surtling Cores."; } ChangeClanTreasury(clan, -num3, "Purchase", "Clan purchase"); territory.LastChallengeTimestamp = num; territory.OdinsPeaceChallengeEndTimestamp = num + (long)(ClanConfig.ChallengeWarningHours?.Value ?? 48) * 3600L; MarkDirty(); WarfareRPC.Instance?.BroadcastGlobalMessage($"{clan.Name} has paid tribute to challenge Odin's Peace at {territory.DisplayName}. Protection falls in {ClanConfig.ChallengeWarningHours?.Value ?? 48} hours."); return "Challenge sent. Odin's warning has begun."; } public ClaimResult TryClaimTerritory(long playerId, string playerName, Vector2i grid) { //IL_0007: Unknown result type (might be due to invalid IL or missing references) //IL_0008: Unknown result type (might be due to invalid IL or missing references) //IL_0040: Unknown result type (might be due to invalid IL or missing references) //IL_004e: Unknown result type (might be due to invalid IL or missing references) //IL_00a7: Unknown result type (might be due to invalid IL or missing references) //IL_0078: Unknown result type (might be due to invalid IL or missing references) //IL_00b7: 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_00f7: 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_012c: Unknown result type (might be due to invalid IL or missing references) //IL_0354: Unknown result type (might be due to invalid IL or missing references) PlayerClanInfo player = Data.GetPlayer(playerId); if (player == null || (player.ClanId == 0 && !player.IsFreeborn)) { return new ClaimResult(s: false, "Join a clan or choose the Freeborn path first!"); } ClanTerritory territory = Data.GetTerritory(grid); if (!TerritoryRules.CanClaim(player, territory, grid, out var message)) { return new ClaimResult(s: false, message); } if (player.IsFreeborn && player.ClanId == 0) { return TryClaimFreebornTerritory(player, playerName, grid); } Clan clan = Data.GetClan(player.ClanId); if (clan == null) { return new ClaimResult(s: false, "Your clan no longer exists."); } if (ProtectedWorldZones.TryGetProtectedZone(grid, out var _)) { return new ClaimResult(s: false, ProtectedWorldZones.GetBlockedActionMessage(grid)); } if (VoronoiGrid.Instance != null && VoronoiGrid.Instance.IsSpawnZone(grid)) { return new ClaimResult(s: false, "The spawn zone is neutral ground \ufffd no claims allowed."); } if (Vector3.Distance(grid.ToWorldCenter(), Vector3.zero) < (float)(ClanConfig.SpawnProtectionRadius?.Value ?? 2500)) { return new ClaimResult(s: false, "Too close to world spawn!"); } ClanTerritory territory2 = Data.GetTerritory(grid); bool flag = territory2?.IsRotted ?? false; if (territory2 != null && territory2.FreebornOwnerId != 0L && !flag) { if (IsProtectedByOdinsPeace(territory2)) { return new ClaimResult(s: false, "Odin's Peace protects this Freeborn territory. Challenge it first."); } return new ClaimResult(s: false, "Freeborn land \ufffd capture it through warfare instead."); } if (territory2 != null && territory2.OwnerClanId != 0 && !flag) { if (territory2.OwnerClanId == player.ClanId) { return new ClaimResult(s: false, "Your clan already owns this territory."); } return new ClaimResult(s: false, "Enemy land \ufffd declare war and capture it instead."); } if (!flag && !player.CanClaimTerritory && player.Rank != ClanRank.Jarl) { return new ClaimResult(s: false, "You do not have permission to claim clan territory."); } int clanTerritoryCount = Data.GetClanTerritoryCount(player.ClanId); int num = Economy.TerritoryCap(clan); if (clanTerritoryCount >= num) { return new ClaimResult(s: false, $"Territory cap reached ({clanTerritoryCount}/{num}). Buy an expansion."); } long num2 = ((!flag && (territory2 == null || (territory2.OwnerClanId == 0 && territory2.FreebornOwnerId == 0))) ? Economy.TerritoryAcquisitionCost(clanTerritoryCount + 1, player) : 0); if (num2 > 0) { if (clan.Treasury < num2) { return new ClaimResult(s: false, "Not enough Clan Treasury."); } ChangeClanTreasury(clan, -num2, "Purchase", "Clan purchase"); } ClanTerritory clanTerritory = territory2 ?? new ClanTerritory { GridX = grid.x, GridY = grid.y }; clanTerritory.OwnerClanId = player.ClanId; clanTerritory.ClaimedByPlayerId = playerId; clanTerritory.ClaimedByPlayerName = playerName; clanTerritory.ClaimTimestamp = Now(); clanTerritory.RotCount = 0; clanTerritory.State = TerritoryState.Normal; clanTerritory.LastIncomeTimestamp = Now(); clanTerritory.LastRotTickTimestamp = Now(); clanTerritory.Type = ((!Data.ClanHasCapital(player.ClanId)) ? TerritoryType.Capital : TerritoryType.Colony); if (clanTerritory.SizeLevel < 1) { clanTerritory.SizeLevel = 1; } ValueTerritory(clanTerritory); InitTerritoryStats(clanTerritory); Data.Territories[WarfareData.GridToKey(grid)] = clanTerritory; Data.RotTotems.RemoveAll((RotTotem t) => t.TerritoryGridX == grid.x && t.TerritoryGridY == grid.y); Data.ActiveCaptures.RemoveAll((CaptureSession c) => c.TerritoryGridX == grid.x && c.TerritoryGridY == grid.y); player.TerritoriesClaimed++; if (clanTerritory.Type == TerritoryType.Capital) { player.CapitalsFounded++; AddPersonalSagaEntry(player, "CapitalFounded", "Founded a Capital", "Established the Capital at " + clanTerritory.DisplayName + ".", "Major", "capital:" + clanTerritory.GridX + ":" + clanTerritory.GridY, player.ClanId, TerritorySagaId(clanTerritory), 0L, "", "", clanTerritory.ClaimTimestamp, markDirty: false); } AddTerritoryClaimMilestone(player, clanTerritory); MarkDirty(); string text = (clanTerritory.IsCapital ? "Capital" : "Colony"); string text2 = ((num2 > 0) ? (" Paid " + num2 + " coins from Clan Treasury.") : ""); return new ClaimResult(s: true, "Claimed " + clanTerritory.DisplayName + " as a " + text + " for " + clan.Name + "!" + text2); } private ClaimResult TryClaimFreebornTerritory(PlayerClanInfo player, string playerName, Vector2i grid) { //IL_0007: Unknown result type (might be due to invalid IL or missing references) //IL_0008: Unknown result type (might be due to invalid IL or missing references) //IL_000e: Unknown result type (might be due to invalid IL or missing references) //IL_001e: Unknown result type (might be due to invalid IL or missing references) //IL_005a: Unknown result type (might be due to invalid IL or missing references) //IL_003b: Unknown result type (might be due to invalid IL or missing references) //IL_023e: Unknown result type (might be due to invalid IL or missing references) if (ProtectedWorldZones.TryGetProtectedZone(grid, out var _)) { return new ClaimResult(s: false, ProtectedWorldZones.GetBlockedActionMessage(grid)); } if (VoronoiGrid.Instance != null && VoronoiGrid.Instance.IsSpawnZone(grid)) { return new ClaimResult(s: false, "The spawn zone is neutral ground \ufffd no claims allowed."); } ClanTerritory territory = Data.GetTerritory(grid); if (territory != null && (territory.OwnerClanId != 0 || territory.FreebornOwnerId != 0L) && !territory.IsRotted) { return new ClaimResult(s: false, "This territory is already claimed."); } int num = CountFreebornTerritories(player.PlayerId, TerritoryType.Homestead); int num2 = CountFreebornTerritories(player.PlayerId, TerritoryType.Outpost); bool flag = num <= 0; if (flag && num >= (ClanConfig.FreebornMaxHomesteads?.Value ?? 1)) { return new ClaimResult(s: false, "You already own a Homestead."); } if (!flag && num2 >= FreebornOutpostCap(player)) { int num3 = FreebornOutpostCap(player); return new ClaimResult(s: false, $"Freeborn territory cap reached ({num2}/{num3} additional territories, max total 5)."); } ClanTerritory clanTerritory = territory ?? new ClanTerritory { GridX = grid.x, GridY = grid.y }; clanTerritory.OwnerClanId = 0; clanTerritory.FreebornOwnerId = player.PlayerId; clanTerritory.FreebornOwnerName = playerName; clanTerritory.ClaimedByPlayerId = player.PlayerId; clanTerritory.ClaimedByPlayerName = playerName; clanTerritory.ClaimTimestamp = Now(); clanTerritory.RotCount = 0; clanTerritory.State = TerritoryState.Normal; clanTerritory.LastIncomeTimestamp = Now(); clanTerritory.LastRotTickTimestamp = Now(); clanTerritory.Type = (flag ? TerritoryType.Homestead : TerritoryType.Outpost); ConfigEntry odinsPeaceEnabled = ClanConfig.OdinsPeaceEnabled; int odinsPeaceActive; if (odinsPeaceEnabled != null && odinsPeaceEnabled.Value) { if (flag) { ConfigEntry odinsPeaceProtectHomestead = ClanConfig.OdinsPeaceProtectHomestead; if (odinsPeaceProtectHomestead != null && odinsPeaceProtectHomestead.Value) { odinsPeaceActive = 1; goto IL_01fa; } } odinsPeaceActive = ((!flag && (ClanConfig.OdinsPeaceProtectOutposts?.Value ?? false)) ? 1 : 0); } else { odinsPeaceActive = 0; } goto IL_01fa; IL_01fa: clanTerritory.OdinsPeaceActive = (byte)odinsPeaceActive != 0; clanTerritory.OdinsPeaceChallengeEndTimestamp = 0L; clanTerritory.IsAbandoned = false; if (clanTerritory.SizeLevel < 1) { clanTerritory.SizeLevel = 1; } ValueTerritory(clanTerritory); InitTerritoryStats(clanTerritory); Data.Territories[WarfareData.GridToKey(grid)] = clanTerritory; Data.RotTotems.RemoveAll((RotTotem t) => t.TerritoryGridX == grid.x && t.TerritoryGridY == grid.y); Data.ActiveCaptures.RemoveAll((CaptureSession c) => c.TerritoryGridX == grid.x && c.TerritoryGridY == grid.y); player.TerritoriesClaimed++; if (flag) { player.HomesteadsFounded++; AddPersonalSagaEntry(player, "HomesteadFounded", "Founded a Homestead", "Established a Homestead at " + clanTerritory.DisplayName + ".", "Normal", "homestead:" + clanTerritory.GridX + ":" + clanTerritory.GridY, 0, TerritorySagaId(clanTerritory), 0L, "", "", clanTerritory.ClaimTimestamp, markDirty: false); } else { player.OutpostsFounded++; AddPersonalSagaEntry(player, "OutpostFounded", "Founded an Outpost", "Established an Outpost at " + clanTerritory.DisplayName + ".", "Normal", "outpost:" + clanTerritory.GridX + ":" + clanTerritory.GridY, 0, TerritorySagaId(clanTerritory), 0L, "", "", clanTerritory.ClaimTimestamp, markDirty: false); } AddTerritoryClaimMilestone(player, clanTerritory); MarkDirty(); return new ClaimResult(s: true, "Claimed " + clanTerritory.DisplayName + " as a Freeborn " + clanTerritory.Type.ToString() + "!"); } private int FreebornOutpostCap(PlayerClanInfo player) { return 4; } public List GetFreebornTerritories(long playerId, TerritoryType type) { List list = new List(); foreach (ClanTerritory value in Data.Territories.Values) { if (value.FreebornOwnerId == playerId && value.Type == type) { list.Add(value); } } return list; } public int CountFreebornTerritories(long playerId, TerritoryType type) { return GetFreebornTerritories(playerId, type).Count; } public string AbandonFreebornHomestead(long playerId) { ConfigEntry allowFreebornAbandonHomestead = ClanConfig.AllowFreebornAbandonHomestead; if (allowFreebornAbandonHomestead == null || !allowFreebornAbandonHomestead.Value) { return "Abandoning Homesteads is disabled."; } PlayerClanInfo player = Data.GetPlayer(playerId); if (player == null || !player.IsFreeborn || player.ClanId != 0) { return "You are not Freeborn."; } long num = Now(); long num2 = player.LastFreebornAbandonTimestamp + (long)(ClanConfig.AbandonHomesteadCooldownHours?.Value ?? 24) * 3600L - num; if (num2 > 0) { return "Abandon Homestead cooldown: " + FormatDuration(num2) + " remaining."; } if (GetFreebornTerritories(playerId, TerritoryType.Homestead).Count == 0) { return "You have no Homestead to abandon."; } foreach (ClanTerritory value in Data.Territories.Values) { if (value.FreebornOwnerId == playerId) { if (value.OdinsPeaceChallengeEndTimestamp > num) { return "You cannot abandon your Homestead during war or challenge."; } if (Data.ActiveCaptures.Find((CaptureSession c) => c.TerritoryGridX == value.GridX && c.TerritoryGridY == value.GridY) != null) { return "You cannot abandon your Homestead during war or challenge."; } } } bool flag = ClanConfig.AbandonHomesteadAlsoAbandonsOutposts?.Value ?? false; foreach (ClanTerritory item in new List(Data.Territories.Values)) { if (item.FreebornOwnerId == playerId && (item.Type == TerritoryType.Homestead || (flag && item.Type == TerritoryType.Outpost))) { item.FreebornOwnerId = 0L; item.FreebornOwnerName = null; item.OdinsPeaceActive = false; item.OdinsPeaceChallengeEndTimestamp = 0L; item.LastChallengeTimestamp = 0L; item.Type = TerritoryType.Colony; item.State = TerritoryState.Normal; item.IsAbandoned = false; Data.ActiveCaptures.RemoveAll((CaptureSession c) => c.TerritoryGridX == item.GridX && c.TerritoryGridY == item.GridY); Data.RotTotems.RemoveAll((RotTotem t) => t.TerritoryGridX == item.GridX && t.TerritoryGridY == item.GridY); } } player.LastFreebornAbandonTimestamp = num; MarkDirty(); return "You abandoned your Homestead. You may claim a new home."; } public string LeaveFreebornLife(long playerId) { PlayerClanInfo player = Data.GetPlayer(playerId); if (player == null || !player.IsFreeborn || player.ClanId != 0) { return "You are not Freeborn."; } foreach (ClanTerritory item in new List(Data.Territories.Values)) { if (item.FreebornOwnerId == playerId) { item.FreebornOwnerId = 0L; item.FreebornOwnerName = null; item.OdinsPeaceActive = false; item.OdinsPeaceChallengeEndTimestamp = 0L; item.LastChallengeTimestamp = 0L; item.Type = TerritoryType.Colony; item.State = TerritoryState.Normal; item.IsAbandoned = false; Data.ActiveCaptures.RemoveAll((CaptureSession c) => c.TerritoryGridX == item.GridX && c.TerritoryGridY == item.GridY); Data.RotTotems.RemoveAll((RotTotem t) => t.TerritoryGridX == item.GridX && t.TerritoryGridY == item.GridY); } } Data.FreebornVaults.Remove(playerId); player.IsFreeborn = false; player.FreebornSinceTimestamp = 0L; player.FreebornLastActiveTimestamp = 0L; player.LastFreebornChallengeTimestamp = 0L; player.LastFreebornAbandonTimestamp = 0L; player.FreebornTreasury = 0L; player.FreebornVaultTier = 1; player.FreebornFriends.Clear(); player.PendingFreebornFriendRequests.Clear(); player.VaultPermissionsInitialized = false; player.CanDepositVaultItems = false; player.CanWithdrawVaultItems = false; player.CanDepositVaultCoins = false; player.CanWithdrawVaultCoins = false; MarkDirty(); return "You are no longer Freeborn.\nYou may now join or create a clan."; } private string FormatDuration(long seconds) { if (seconds < 0) { seconds = 0L; } long num = seconds / 3600; long num2 = seconds % 3600 / 60; if (num <= 0) { return $"{num2}m"; } return $"{num}h {num2}m"; } public void ValueTerritory(ClanTerritory t) { //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_0007: Unknown result type (might be due to invalid IL or missing references) //IL_0011: Unknown result type (might be due to invalid IL or missing references) //IL_0016: Unknown result type (might be due to invalid IL or missing references) //IL_0018: Unknown result type (might be due to invalid IL or missing references) //IL_0024: 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_0043: 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) try { Vector2i gridPosition = t.GridPosition; Dictionary dictionary = BiomeUtils.SampleBiomeComposition(gridPosition, 64); Biome dominantBiome = BiomeUtils.GetDominantBiome(dictionary); t.BiomeValueTier = Economy.BiomeValueTier(dominantBiome); t.BiomeName = BiomeUtils.GetBiomeName(dominantBiome); if (string.IsNullOrEmpty(t.CustomName)) { bool hasMerchant = BiomeUtils.CellHasMerchant(gridPosition); bool hasBossAltar = BiomeUtils.HasPointOfInterest(gridPosition); t.CustomName = TerritoryNaming.GenerateTerritoryName(gridPosition, dictionary, hasMerchant, hasBossAltar); } } catch (Exception ex) { Plugin.Log.LogWarning((object)$"ValueTerritory failed for ({t.GridX},{t.GridY}): {ex.Message}"); if (t.BiomeValueTier < 1) { t.BiomeValueTier = 1; } } } public void InitTerritoryStats(ClanTerritory t) { t.MaxHP = Economy.TerritoryMaxHP(t); if (t.HP <= 0f || t.HP > t.MaxHP) { t.HP = t.MaxHP; } } public string TryRenameTerritory(long playerId, Vector2i grid, string newName) { //IL_0024: Unknown result type (might be due to invalid IL or missing references) PlayerClanInfo player = Data.GetPlayer(playerId); if (player == null || player.ClanId == 0) { return "You must join a clan first!"; } ClanTerritory territory = Data.GetTerritory(grid); if (territory == null || territory.OwnerClanId == 0) { return "This territory is not claimed!"; } if (territory.OwnerClanId != player.ClanId) { return "You can only rename your own clan's territories!"; } if (!territory.CanRename()) { return "Territory rename on cooldown (24 hours)."; } newName = newName?.Trim(); if (string.IsNullOrEmpty(newName) || newName.Length > 32) { return "Name must be 1-32 characters!"; } territory.CustomName = newName; territory.LastRenameTimestamp = Now(); MarkDirty(); return "Territory renamed to " + newName + "!"; } public string UpgradeTerritory(long playerId, Vector2i grid) { //IL_002c: Unknown result type (might be due to invalid IL or missing references) PlayerClanInfo player = Data.GetPlayer(playerId); if (player == null || (player.ClanId == 0 && !player.IsFreeborn)) { return "You are not in a clan or Freeborn."; } ClanTerritory territory = Data.GetTerritory(grid); if (player.IsFreeborn && player.ClanId == 0) { if (territory == null || territory.FreebornOwnerId != player.PlayerId) { return "That is not your Freeborn territory."; } int num = ClanConfig.MaxTerritorySizeLevel?.Value ?? 5; if (territory.SizeLevel >= num) { return "Territory is already at maximum size."; } long num2 = Economy.TerritoryUpgradeCost(territory.SizeLevel); if (player.FreebornTreasury < num2) { return $"Freeborn treasury needs {num2} coins (has {player.FreebornTreasury})."; } player.FreebornTreasury -= num2; territory.SizeLevel++; float num3 = ((territory.MaxHP > 0f) ? (territory.HP / territory.MaxHP) : 1f); territory.MaxHP = Economy.TerritoryMaxHP(territory); territory.HP = territory.MaxHP * num3; MarkDirty(); return $"{territory.DisplayName} upgraded to size {territory.SizeLevel}."; } if (player.Rank < ClanRank.Officer) { return "Only Officers and the Jarl may upgrade territory."; } if (territory == null || territory.OwnerClanId != player.ClanId) { return "That is not your clan's territory."; } int num4 = ClanConfig.MaxTerritorySizeLevel?.Value ?? 5; if (territory.SizeLevel >= num4) { return "Territory is already at maximum size."; } Clan clan = Data.GetClan(player.ClanId); long num5 = Economy.TerritoryUpgradeCost(territory.SizeLevel); int num6 = Economy.TerritoryUpgradeCoreCost(territory.SizeLevel); if (clan.Treasury < num5) { return $"Treasury needs {num5} coins (has {clan.Treasury})."; } if (!ConsumeVaultItem(clan.ClanId, "SurtlingCore", num6)) { return $"Clan vault needs {num6} Surtling Cores."; } ChangeClanTreasury(clan, -num5, "Purchase", "Clan purchase"); territory.SizeLevel++; float num7 = ((territory.MaxHP > 0f) ? (territory.HP / territory.MaxHP) : 1f); territory.MaxHP = Economy.TerritoryMaxHP(territory); territory.HP = territory.MaxHP * num7; MarkDirty(); return $"{territory.DisplayName} upgraded to size {territory.SizeLevel}."; } public string SetCapital(long playerId, Vector2i grid) { //IL_0033: Unknown result type (might be due to invalid IL or missing references) //IL_005f: Unknown result type (might be due to invalid IL or missing references) PlayerClanInfo player = Data.GetPlayer(playerId); if (player == null || player.ClanId == 0) { return "You are not in a clan."; } if (player.Rank < ClanRank.Officer) { return "Only Officers and the Jarl may move the Capital."; } ClanTerritory territory = Data.GetTerritory(grid); if (territory == null || territory.OwnerClanId != player.ClanId) { return "That is not your clan's territory."; } if (ProtectedWorldZones.IsProtected(territory)) { return "" + ProtectedWorldZones.GetBlockedActionMessage(territory.GridPosition) + ""; } if (territory.IsCapital) { return "That is already your Capital."; } foreach (ClanTerritory clanTerritory in Data.GetClanTerritories(player.ClanId)) { if (clanTerritory.Type == TerritoryType.Capital) { clanTerritory.Type = TerritoryType.Colony; clanTerritory.MaxHP = Economy.TerritoryMaxHP(clanTerritory); clanTerritory.HP = Mathf.Min(clanTerritory.HP, clanTerritory.MaxHP); } } territory.Type = TerritoryType.Capital; territory.MaxHP = Economy.TerritoryMaxHP(territory); territory.HP = territory.MaxHP; MarkDirty(); return "" + territory.DisplayName + " is now your Capital."; } public string BuyTerritoryExpansion(long playerId) { PlayerClanInfo player = Data.GetPlayer(playerId); if (player == null || player.ClanId == 0) { return "You are not in a clan."; } if (player.Rank < ClanRank.Officer) { return "Only Officers and the Jarl may buy expansions."; } Clan clan = Data.GetClan(player.ClanId); if (clan == null) { return "Clan not found."; } if (Economy.TerritoryCap(clan) >= (ClanConfig.MaxTerritoryCap?.Value ?? 100)) { return "Territory cap is already at the maximum."; } long num = Economy.TerritoryExpansionCost(clan.ExtraTerritorySlots); int num2 = Economy.TerritoryExpansionCoreCost(clan.ExtraTerritorySlots); if (clan.Treasury < num) { return $"Treasury needs {num} coins (has {clan.Treasury})."; } if (!ConsumeVaultItem(clan.ClanId, "SurtlingCore", num2)) { return $"Clan vault needs {num2} Surtling Cores."; } ChangeClanTreasury(clan, -num, "Purchase", "Clan purchase"); clan.ExtraTerritorySlots++; MarkDirty(); return $"Territory cap raised to {Economy.TerritoryCap(clan)}."; } private void CaptureTick() { //IL_00fe: Unknown result type (might be due to invalid IL or missing references) //IL_0103: Unknown result type (might be due to invalid IL or missing references) //IL_0110: Unknown result type (might be due to invalid IL or missing references) //IL_002f: Unknown result type (might be due to invalid IL or missing references) //IL_003a: Expected O, but got Unknown //IL_0154: Unknown result type (might be due to invalid IL or missing references) //IL_006d: Unknown result type (might be due to invalid IL or missing references) //IL_0072: Unknown result type (might be due to invalid IL or missing references) //IL_0077: Unknown result type (might be due to invalid IL or missing references) //IL_0079: Unknown result type (might be due to invalid IL or missing references) //IL_00a5: Unknown result type (might be due to invalid IL or missing references) //IL_02a8: Unknown result type (might be due to invalid IL or missing references) Dictionary> dictionary = new Dictionary>(); Dictionary dictionary2 = new Dictionary(); List allPlayers = Player.GetAllPlayers(); if (allPlayers != null) { foreach (Player item in allPlayers) { if ((Object)item == (Object)null || ((Character)item).IsDead()) { continue; } PlayerClanInfo player = Data.GetPlayer(item.GetPlayerID()); if (player != null && player.ClanId != 0) { Vector2i val = GridExtensions.WorldToGrid(((Component)item).transform.position); string key = WarfareData.GridToKey(val); if (!dictionary.TryGetValue(key, out var value)) { List list = (dictionary[key] = new List()); value = list; dictionary2[key] = val; } value.Add(player); } } } RemoveProtectedFreebornCaptures(); foreach (KeyValuePair> item2 in dictionary) { Vector2i grid = dictionary2[item2.Key]; ClanTerritory territory = Data.GetTerritory(grid); if (territory == null || (territory.OwnerClanId == 0 && territory.FreebornOwnerId == 0L) || territory.Type == TerritoryType.Capital || IsProtectedByOdinsPeace(territory) || ProtectedWorldZones.IsProtected(grid) || territory.IsRotted) { continue; } int ownerClanId = territory.OwnerClanId; bool flag = ownerClanId == 0 && territory.FreebornOwnerId != 0; Dictionary dictionary3 = new Dictionary(); int num = 0; foreach (PlayerClanInfo item3 in item2.Value) { string message; if (!flag && item3.ClanId == ownerClanId) { num++; } else if (flag || (Data.GetActiveWarBetween(item3.ClanId, ownerClanId) != null && !IsHostileActionBlocked(item3.ClanId, ownerClanId, out message))) { dictionary3[item3.ClanId] = ((!dictionary3.TryGetValue(item3.ClanId, out var value2)) ? 1 : (value2 + 1)); } } int num2 = 0; int num3 = 0; foreach (KeyValuePair item4 in dictionary3) { if (item4.Value > num3) { num3 = item4.Value; num2 = item4.Key; } } CaptureSession captureSession = Data.ActiveCaptures.Find((CaptureSession c) => c.TerritoryGridX == grid.x && c.TerritoryGridY == grid.y); if (captureSession != null && ProtectedWorldZones.IsProtected(grid)) { Data.ActiveCaptures.Remove(captureSession); territory.State = TerritoryState.Normal; MarkDirty(); } else if (captureSession != null && IsProtectedByOdinsPeace(territory)) { Data.ActiveCaptures.Remove(captureSession); territory.State = TerritoryState.Normal; MarkDirty(); } else { if (num2 == 0) { continue; } if (captureSession == null) { captureSession = new CaptureSession { TerritoryGridX = grid.x, TerritoryGridY = grid.y, AttackerClanId = num2, DefenderClanId = ownerClanId, Threshold = Economy.CaptureThreshold(territory), StartTimestamp = Now() }; Data.ActiveCaptures.Add(captureSession); territory.State = TerritoryState.Contested; Clan clan = Data.GetClan(num2); WarfareRPC.Instance?.BroadcastGlobalMessage("" + clan?.Name + " is capturing " + territory.DisplayName + "!"); } captureSession.AttackerClanId = num2; captureSession.DefenderClanId = ownerClanId; captureSession.LastAttackerSeenTime = Now(); bool flag2 = (captureSession.Siege = num3 >= (ClanConfig.SiegeAttackerCount?.Value ?? 5)); territory.State = ((!flag2) ? TerritoryState.Contested : TerritoryState.Attacked); if (num > num3) { if (captureSession.LastDefenderMajorityTime == 0L) { captureSession.LastDefenderMajorityTime = Now(); } if (Now() - captureSession.LastDefenderMajorityTime >= (ClanConfig.InterruptDefenderMajoritySeconds?.Value ?? 30)) { captureSession.Progress = Mathf.Max(0f, captureSession.Progress - Economy.ClaimSpeed(num, 1f, territory, siege: false)); } } else { captureSession.LastDefenderMajorityTime = 0L; float num4 = Economy.ClaimSpeed(num3, 1f, territory, flag2); if (ShouldReduceForOffline(ownerClanId)) { num4 *= 1f - (ClanConfig.OfflineRaidEffectReduction?.Value ?? 50f) / 100f; } captureSession.Progress += num4; } if (captureSession.CombatPoints > 0) { captureSession.Progress += captureSession.CombatPoints; captureSession.CombatPoints = 0; } if (captureSession.Progress >= captureSession.Threshold) { if (IsProtectedByOdinsPeace(territory)) { Data.ActiveCaptures.Remove(captureSession); territory.State = TerritoryState.Normal; MarkDirty(); } else { CompleteCapture(territory, captureSession); } } } } CaptureExpiryScan(); } private void RemoveProtectedFreebornCaptures() { //IL_0031: Unknown result type (might be due to invalid IL or missing references) //IL_003d: Unknown result type (might be due to invalid IL or missing references) for (int num = Data.ActiveCaptures.Count - 1; num >= 0; num--) { CaptureSession captureSession = Data.ActiveCaptures[num]; ClanTerritory territory = Data.GetTerritory(captureSession.GridPosition); if (ProtectedWorldZones.IsProtected(captureSession.GridPosition)) { if (territory != null) { territory.State = TerritoryState.Normal; } Data.ActiveCaptures.RemoveAt(num); MarkDirty(); } else if (IsProtectedByOdinsPeace(territory)) { if (territory != null) { territory.State = TerritoryState.Normal; } Data.ActiveCaptures.RemoveAt(num); MarkDirty(); WarfareRPC.Instance?.BroadcastGlobalMessage("Odin's Peace stopped a capture at " + (territory?.DisplayName ?? "Freeborn territory") + "."); } } } private void CaptureExpiryScan() { //IL_0051: Unknown result type (might be due to invalid IL or missing references) int num = ClanConfig.InterruptNoAttackerSeconds?.Value ?? 60; for (int num2 = Data.ActiveCaptures.Count - 1; num2 >= 0; num2--) { CaptureSession captureSession = Data.ActiveCaptures[num2]; if (Now() - captureSession.LastAttackerSeenTime >= num) { ClanTerritory territory = Data.GetTerritory(captureSession.GridPosition); if (territory != null && !territory.IsRotted) { territory.State = TerritoryState.Normal; } Data.ActiveCaptures.RemoveAt(num2); } } } private void CompleteCapture(ClanTerritory territory, CaptureSession session) { if (ProtectedWorldZones.IsProtected(territory)) { Data.ActiveCaptures.Remove(session); territory.State = TerritoryState.Normal; MarkDirty(); WarfareRPC.Instance?.BroadcastGlobalMessage("" + territory.DisplayName + " is a Protected World Zone and cannot be captured."); return; } if (IsProtectedByOdinsPeace(territory)) { Data.ActiveCaptures.Remove(session); territory.State = TerritoryState.Normal; MarkDirty(); WarfareRPC.Instance?.BroadcastGlobalMessage("Odin's Peace protected " + territory.DisplayName + " from capture."); return; } Clan clan = Data.GetClan(territory.OwnerClanId); Clan clan2 = Data.GetClan(session.AttackerClanId); string text = ((territory.FreebornOwnerId != 0L) ? ("Freeborn " + (territory.FreebornOwnerName ?? "owner")) : (clan?.Name ?? "unknown")); territory.OwnerClanId = session.AttackerClanId; territory.FreebornOwnerId = 0L; territory.FreebornOwnerName = null; territory.OdinsPeaceActive = false; territory.OdinsPeaceChallengeEndTimestamp = 0L; territory.Type = TerritoryType.Colony; territory.State = TerritoryState.Normal; territory.ClaimTimestamp = Now(); territory.ClaimedByPlayerName = clan2?.Name ?? "raiders"; territory.RotCount = 0; territory.HP = territory.MaxHP; territory.LastIncomeTimestamp = Now(); ApplyBuildingCaptureMode(territory); Data.ActiveCaptures.Remove(session); Data.RotTotems.RemoveAll((RotTotem t) => t.TerritoryGridX == territory.GridX && t.TerritoryGridY == territory.GridY); MarkDirty(); WarfareRPC.Instance?.BroadcastGlobalMessage("" + clan2?.Name + " captured " + territory.DisplayName + " from " + text + "!"); } private void ApplyBuildingCaptureMode(ClanTerritory territory) { string a = (ClanConfig.BuildingCaptureMode?.Value ?? "Transfer").Trim(); if (string.Equals(a, "Destroy", StringComparison.OrdinalIgnoreCase)) { EnsureBuildings(territory).Clear(); if (territory.TerritoryStorage != null) { territory.TerritoryStorage.Clear(); } } else { if (!string.Equals(a, "Damaged", StringComparison.OrdinalIgnoreCase)) { return; } foreach (TerritoryBuilding item in EnsureBuildings(territory)) { item.Damaged = true; } } } public void RegisterCombatKill(Vector3 deathPosition, int attackerClanId, int points) { //IL_0007: Unknown result type (might be due to invalid IL or missing references) //IL_0008: Unknown result type (might be due to invalid IL or missing references) //IL_000d: Unknown result type (might be due to invalid IL or missing references) Vector2i grid = GridExtensions.WorldToGrid(deathPosition); CaptureSession captureSession = Data.ActiveCaptures.Find((CaptureSession c) => c.TerritoryGridX == grid.x && c.TerritoryGridY == grid.y); if (captureSession != null && captureSession.AttackerClanId == attackerClanId) { captureSession.CombatPoints += points; } } public string DeclareWar(long playerId, int targetClanId, WarType type) { PlayerClanInfo player = Data.GetPlayer(playerId); if (player == null || player.ClanId == 0) { return "You are not in a clan."; } if (player.Rank < ClanRank.Officer) { return "Only Officers and the Jarl may declare war."; } if (type == WarType.None) { type = WarType.War; } Clan clan = Data.GetClan(player.ClanId); Clan clan2 = Data.GetClan(targetClanId); if (clan2 == null) { return "That clan no longer exists."; } if (targetClanId == player.ClanId) { return "You cannot declare war on yourself."; } if (Data.GetActiveWarBetween(player.ClanId, targetClanId) != null) { return "You are already at war with " + clan2.Name + "."; } if (IsHostileActionBlocked(player.ClanId, targetClanId, out var message)) { return "" + message + ""; } long num = ClanConfig.WarDeclareCost?.Value ?? 200; if (clan.Treasury < num) { return $"Treasury needs {num} coins to declare war (has {clan.Treasury})."; } ChangeClanTreasury(clan, -num, "Purchase", "Clan purchase"); long num2 = Now(); War war = new War { WarId = Data.NextWarId++, AttackerClanId = player.ClanId, DefenderClanId = targetClanId, Type = type, StartTimestamp = num2, EndTimestamp = num2 + (long)(ClanConfig.WarDurationMinutes?.Value ?? 120) * 60L }; Data.Wars.Add(war); AddPersonalSagaEntry(player, "WarStarted", "War Started", "Declared " + type.ToString() + " on " + clan2.Name + ".", "Major", "warstarted:" + war.WarId, player.ClanId, 0, player.PlayerId, "", "", war.StartTimestamp, markDirty: false); SetDiplomacyState(player.ClanId, targetClanId, DiplomacyState.AtWar); MarkDirty(); WarfareRPC.Instance?.BroadcastGlobalMessage($"? {clan.Name} has declared {type} on {clan2.Name}! ?"); return $"{type} declared on {clan2.Name}."; } private void WarExpiryTick() { bool flag = false; for (int num = Data.Wars.Count - 1; num >= 0; num--) { War war = Data.Wars[num]; if (!war.IsActive) { Clan clan = Data.GetClan(war.AttackerClanId); Clan clan2 = Data.GetClan(war.DefenderClanId); Data.ActiveCaptures.RemoveAll((CaptureSession c) => c.TerritoryGridX != int.MinValue && ((c.AttackerClanId == war.AttackerClanId && c.DefenderClanId == war.DefenderClanId) || (c.AttackerClanId == war.DefenderClanId && c.DefenderClanId == war.AttackerClanId))); Data.Wars.RemoveAt(num); flag = true; WarfareRPC.Instance?.BroadcastGlobalMessage(string.Format("The {0} between {1} and {2} has ended.", war.Type, clan?.Name ?? "?", clan2?.Name ?? "?")); } } if (flag) { MarkDirty(); } } public bool AreAtWar(int clanA, int clanB) { if (clanA != 0 && clanB != 0) { return Data.GetActiveWarBetween(clanA, clanB) != null; } return false; } public DiplomacyState GetDiplomacyState(int clanA, int clanB) { if (clanA == clanB) { return DiplomacyState.Allied; } if (AreAtWar(clanA, clanB)) { return DiplomacyState.AtWar; } if (GetActiveVassalRelation(clanA, clanB) != null || AreInSameAlliance(clanA, clanB) || GetActiveNap(clanA, clanB) != null) { return DiplomacyState.Allied; } DiplomacyState? diplomacyState = Data.GetDiplomacy(clanA, clanB)?.State; if (diplomacyState == DiplomacyState.Friendly || diplomacyState == DiplomacyState.Hostile) { return diplomacyState.Value; } return DiplomacyState.Neutral; } public string GetRelationLabel(int clanA, int clanB) { VassalRelation activeVassalRelation = GetActiveVassalRelation(clanA, clanB); if (activeVassalRelation != null) { Clan clan = Data.GetClan(activeVassalRelation.OverlordClanId); Clan clan2 = Data.GetClan(activeVassalRelation.VassalClanId); if (activeVassalRelation.OverlordClanId != clanA) { return "Vassal of " + (clan?.Name ?? activeVassalRelation.OverlordClanName ?? "Unknown"); } return "Overlord of " + (clan2?.Name ?? activeVassalRelation.VassalClanName ?? "Unknown"); } if (AreInSameAlliance(clanA, clanB)) { return "Allied"; } if (GetActiveNap(clanA, clanB) != null) { return "NAP"; } return GetDiplomacyState(clanA, clanB).ToString(); } public Alliance GetAllianceForClan(int clanId) { return Data.Alliances.Find((Alliance a) => a.HasMember(clanId)); } public bool AreInSameAlliance(int clanA, int clanB) { return GetAllianceForClan(clanA)?.HasMember(clanB) ?? false; } public NonAggressionPact GetActiveNap(int clanA, int clanB) { long num = Now(); foreach (NonAggressionPact nonAggressionPact in Data.NonAggressionPacts) { if (nonAggressionPact.IsBetween(clanA, clanB)) { if (nonAggressionPact.Status == PactStatus.Active && nonAggressionPact.EndTime > num) { return nonAggressionPact; } if (nonAggressionPact.Status == PactStatus.Broken && nonAggressionPact.BreakCooldownEndTime > num) { return nonAggressionPact; } } } return null; } public VassalRelation GetActiveVassalRelation(int clanA, int clanB) { return Data.VassalRelations.Find((VassalRelation v) => v.Status == VassalStatus.Active && v.IsBetween(clanA, clanB)); } private VassalRelation GetActiveOverlordRelation(int vassalClanId) { return Data.VassalRelations.Find((VassalRelation v) => v.Status == VassalStatus.Active && v.VassalClanId == vassalClanId); } private int GetActiveOverlordClanId(int vassalClanId) { return GetActiveOverlordRelation(vassalClanId)?.OverlordClanId ?? 0; } private bool WouldCreateVassalCycle(int overlordClanId, int vassalClanId) { int num = overlordClanId; HashSet hashSet = new HashSet(); while (num != 0 && hashSet.Add(num)) { if (num == vassalClanId) { return true; } num = GetActiveOverlordClanId(num); } return false; } private void FillVassalClanSnapshots(VassalRelation relation) { Clan clan = Data.GetClan(relation.OverlordClanId); Clan clan2 = Data.GetClan(relation.VassalClanId); relation.OverlordClanName = clan?.Name ?? relation.OverlordClanName ?? ""; relation.OverlordClanTag = clan?.Tag ?? relation.OverlordClanTag ?? ""; relation.VassalClanName = clan2?.Name ?? relation.VassalClanName ?? ""; relation.VassalClanTag = clan2?.Tag ?? relation.VassalClanTag ?? ""; } private void AddDiplomacyStatusEvent(string message) { if (Data.BuildingProductionLog == null) { Data.BuildingProductionLog = new List(); } Data.BuildingProductionLog.Insert(0, "Diplomacy: " + message); while (Data.BuildingProductionLog.Count > 20) { Data.BuildingProductionLog.RemoveAt(Data.BuildingProductionLog.Count - 1); } } public bool IsHostileActionBlocked(int attackerClanId, int targetClanId, out string message) { message = ""; if (attackerClanId == 0 || targetClanId == 0 || attackerClanId == targetClanId) { return false; } if (AreInSameAlliance(attackerClanId, targetClanId)) { message = "Cannot attack alliance member."; return true; } if (GetActiveNap(attackerClanId, targetClanId) != null) { message = "Non-Aggression Pact is active."; return true; } if (GetActiveVassalRelation(attackerClanId, targetClanId) != null) { message = "Cannot declare war: target is your vassal or overlord."; return true; } return false; } private DiplomacyRelation SetDiplomacyState(int clanA, int clanB, DiplomacyState state, int requestedBy = 0) { DiplomacyRelation diplomacyRelation = Data.GetDiplomacy(clanA, clanB); if (diplomacyRelation == null) { diplomacyRelation = new DiplomacyRelation { ClanA = Mathf.Min(clanA, clanB), ClanB = Mathf.Max(clanA, clanB) }; Data.Diplomacy.Add(diplomacyRelation); } diplomacyRelation.State = state; diplomacyRelation.RequestedByClanId = requestedBy; if (state == DiplomacyState.Allied && diplomacyRelation.AllianceLeaderClanId == 0) { diplomacyRelation.AllianceLeaderClanId = ((requestedBy != 0) ? requestedBy : clanA); } diplomacyRelation.UpdatedTimestamp = Now(); MarkDirty(); MarkSharedDirty(); return diplomacyRelation; } public Color GetAllianceColor(int clanA, int clanB) { //IL_007c: 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_0062: Unknown result type (might be due to invalid IL or missing references) Alliance allianceForClan = GetAllianceForClan(clanA); if (allianceForClan != null && allianceForClan.HasMember(clanB)) { return new Color(allianceForClan.AllianceColorR, allianceForClan.AllianceColorG, allianceForClan.AllianceColorB, 1f); } DiplomacyRelation diplomacy = Data.GetDiplomacy(clanA, clanB); if (diplomacy != null && diplomacy.State == DiplomacyState.Allied) { return new Color(diplomacy.AllianceColorR, diplomacy.AllianceColorG, diplomacy.AllianceColorB, 1f); } return new Color(0.2f, 0.45f, 1f, 1f); } public string CycleAllianceColor(long playerId, int otherClanId) { //IL_007f: Unknown result type (might be due to invalid IL or missing references) //IL_0084: Unknown result type (might be due to invalid IL or missing references) //IL_009a: Unknown result type (might be due to invalid IL or missing references) //IL_009f: Unknown result type (might be due to invalid IL or missing references) //IL_00b5: Unknown result type (might be due to invalid IL or missing references) //IL_00ba: Unknown result type (might be due to invalid IL or missing references) //IL_00d0: Unknown result type (might be due to invalid IL or missing references) //IL_00d5: Unknown result type (might be due to invalid IL or missing references) //IL_00dc: Unknown result type (might be due to invalid IL or missing references) //IL_00e1: Unknown result type (might be due to invalid IL or missing references) //IL_0108: 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) PlayerClanInfo player = Data.GetPlayer(playerId); if (player == null || player.ClanId == 0 || player.Rank < ClanRank.Officer) { return "Only clan Officers and Jarls may change alliance color."; } DiplomacyRelation diplomacy = Data.GetDiplomacy(player.ClanId, otherClanId); if (diplomacy == null || diplomacy.State != DiplomacyState.Allied) { return "No alliance with that clan."; } if (diplomacy.AllianceLeaderClanId != 0 && diplomacy.AllianceLeaderClanId != player.ClanId) { return "Only alliance leader can change alliance color."; } Color[] array = (Color[])(object)new Color[5] { new Color(0.2f, 0.45f, 1f), new Color(0.2f, 0.8f, 0.35f), new Color(0.55f, 0.3f, 0.9f), new Color(0.95f, 0.45f, 0.12f), Color.white }; Color a = default(Color); ((Color)(ref a))..ctor(diplomacy.AllianceColorR, diplomacy.AllianceColorG, diplomacy.AllianceColorB); int num = 0; for (int i = 0; i < array.Length; i++) { if (ClanConfig.ColorsClose(a, array[i])) { num = (i + 1) % array.Length; break; } } diplomacy.AllianceColorR = array[num].r; diplomacy.AllianceColorG = array[num].g; diplomacy.AllianceColorB = array[num].b; MarkDirty(); MarkSharedDirty(); return "Alliance border color changed."; } public string SendAllianceRequest(long playerId, int targetClanId) { return InviteToAlliance(playerId, targetClanId); } public string AcceptAlliance(long playerId, int otherClanId) { return AcceptAllianceInvite(playerId, otherClanId); } public string BreakAlliance(long playerId, int otherClanId) { PlayerClanInfo player = Data.GetPlayer(playerId); if (player == null || player.ClanId == 0 || player.Rank != ClanRank.Jarl) { return "Only the Jarl may reset diplomacy."; } SetDiplomacyState(player.ClanId, otherClanId, DiplomacyState.Neutral); return "Diplomacy reset to Neutral."; } public string DeclareHostile(long playerId, int otherClanId) { PlayerClanInfo player = Data.GetPlayer(playerId); if (player == null || player.ClanId == 0 || player.Rank < ClanRank.Officer) { return "Only clan Officers and Jarls may declare hostility."; } SetDiplomacyState(player.ClanId, otherClanId, DiplomacyState.Hostile); return "Hostility declared."; } private bool IsJarl(PlayerClanInfo player) { if (player != null && player.ClanId != 0) { return player.Rank == ClanRank.Jarl; } return false; } public string CreateAlliance(long playerId, string name) { PlayerClanInfo player = Data.GetPlayer(playerId); if (!IsJarl(player)) { return "Only the Jarl can create an alliance."; } if (GetAllianceForClan(player.ClanId) != null) { return "Your clan already belongs to an alliance."; } Clan clan = Data.GetClan(player.ClanId); Alliance alliance = new Alliance { AllianceId = Data.NextAllianceId++, AllianceName = (string.IsNullOrWhiteSpace(name) ? ((clan?.Name ?? "Clan") + " Alliance") : name.Trim()), AllianceLeaderClanId = player.ClanId, CreatedAt = Now() }; alliance.MemberClanIds.Add(player.ClanId); Data.Alliances.Add(alliance); MarkDirty(); MarkSharedDirty(); return "Alliance created."; } public string InviteToAlliance(long playerId, int targetClanId) { PlayerClanInfo player = Data.GetPlayer(playerId); if (!IsJarl(player)) { return "Only the Jarl can invite clans to an alliance."; } if (Data.GetClan(targetClanId) == null || targetClanId == player.ClanId) { return "Invalid diplomacy target."; } Alliance allianceForClan = GetAllianceForClan(player.ClanId); if (allianceForClan == null) { CreateAlliance(playerId, null); allianceForClan = GetAllianceForClan(player.ClanId); } if (allianceForClan.AllianceLeaderClanId != player.ClanId) { return "Only alliance leader Jarl can invite."; } if (GetAllianceForClan(targetClanId) != null) { return "Target clan already belongs to an alliance."; } if (!allianceForClan.PendingInvites.Contains(targetClanId)) { allianceForClan.PendingInvites.Add(targetClanId); } SetDiplomacyState(player.ClanId, targetClanId, DiplomacyState.Friendly, player.ClanId); MarkDirty(); MarkSharedDirty(); return "Alliance invite sent."; } public string AcceptAllianceInvite(long playerId, int leaderClanId) { PlayerClanInfo player = Data.GetPlayer(playerId); if (!IsJarl(player)) { return "Only the Jarl can accept alliance invites."; } if (GetAllianceForClan(player.ClanId) != null) { return "Your clan already belongs to an alliance."; } Alliance alliance = Data.Alliances.Find((Alliance a) => a.AllianceLeaderClanId == leaderClanId && a.PendingInvites.Contains(player.ClanId)); if (alliance == null) { return "No pending alliance invite from that clan."; } alliance.PendingInvites.Remove(player.ClanId); alliance.MemberClanIds.Add(player.ClanId); foreach (int memberClanId in alliance.MemberClanIds) { if (memberClanId != player.ClanId) { SetDiplomacyState(player.ClanId, memberClanId, DiplomacyState.Allied, alliance.AllianceLeaderClanId); } } MarkDirty(); MarkSharedDirty(); return "Alliance joined."; } public string DeclineAllianceInvite(long playerId, int leaderClanId) { PlayerClanInfo player = Data.GetPlayer(playerId); if (!IsJarl(player)) { return "Only the Jarl can decline alliance invites."; } Alliance alliance = Data.Alliances.Find((Alliance a) => a.AllianceLeaderClanId == leaderClanId && a.PendingInvites.Contains(player.ClanId)); if (alliance == null) { return "No pending invite."; } alliance.PendingInvites.Remove(player.ClanId); MarkDirty(); MarkSharedDirty(); return "Alliance invite declined."; } public string LeaveAlliance(long playerId) { PlayerClanInfo player = Data.GetPlayer(playerId); if (!IsJarl(player)) { return "Only the Jarl can leave an alliance."; } Alliance allianceForClan = GetAllianceForClan(player.ClanId); if (allianceForClan == null) { return "Your clan is not in an alliance."; } if (allianceForClan.AllianceLeaderClanId == player.ClanId) { return DisbandAlliance(playerId); } allianceForClan.MemberClanIds.Remove(player.ClanId); foreach (int memberClanId in allianceForClan.MemberClanIds) { SetDiplomacyState(player.ClanId, memberClanId, DiplomacyState.Neutral); } MarkDirty(); MarkSharedDirty(); return "Alliance left."; } public string KickFromAlliance(long playerId, int targetClanId) { PlayerClanInfo player = Data.GetPlayer(playerId); if (!IsJarl(player)) { return "Only the Jarl can kick alliance members."; } Alliance allianceForClan = GetAllianceForClan(player.ClanId); if (allianceForClan == null || allianceForClan.AllianceLeaderClanId != player.ClanId) { return "Only alliance leader Jarl can kick."; } if (!allianceForClan.MemberClanIds.Remove(targetClanId)) { return "Target is not an alliance member."; } foreach (int memberClanId in allianceForClan.MemberClanIds) { SetDiplomacyState(targetClanId, memberClanId, DiplomacyState.Neutral); } MarkDirty(); MarkSharedDirty(); return "Alliance member removed."; } public string DisbandAlliance(long playerId) { PlayerClanInfo player = Data.GetPlayer(playerId); if (!IsJarl(player)) { return "Only the Jarl can disband an alliance."; } Alliance allianceForClan = GetAllianceForClan(player.ClanId); if (allianceForClan == null || allianceForClan.AllianceLeaderClanId != player.ClanId) { return "Only alliance leader Jarl can disband."; } foreach (int memberClanId in allianceForClan.MemberClanIds) { foreach (int memberClanId2 in allianceForClan.MemberClanIds) { if (memberClanId != memberClanId2) { SetDiplomacyState(memberClanId, memberClanId2, DiplomacyState.Neutral); } } } Data.Alliances.Remove(allianceForClan); MarkDirty(); MarkSharedDirty(); return "Alliance disbanded."; } public string ChangeAllianceColor(long playerId) { //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) //IL_0072: Unknown result type (might be due to invalid IL or missing references) //IL_0077: Unknown result type (might be due to invalid IL or missing references) //IL_008d: Unknown result type (might be due to invalid IL or missing references) //IL_0092: Unknown result type (might be due to invalid IL or missing references) //IL_00a8: Unknown result type (might be due to invalid IL or missing references) //IL_00ad: 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_00b9: 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) //IL_00e4: Unknown result type (might be due to invalid IL or missing references) PlayerClanInfo player = Data.GetPlayer(playerId); if (!IsJarl(player)) { return "Only the Jarl can change alliance color."; } Alliance allianceForClan = GetAllianceForClan(player.ClanId); if (allianceForClan == null || allianceForClan.AllianceLeaderClanId != player.ClanId) { return "Only alliance leader Jarl can change alliance color."; } Color[] array = (Color[])(object)new Color[5] { new Color(0.2f, 0.45f, 1f), new Color(0.2f, 0.8f, 0.35f), new Color(0.55f, 0.3f, 0.9f), new Color(0.95f, 0.45f, 0.12f), Color.white }; Color a = default(Color); ((Color)(ref a))..ctor(allianceForClan.AllianceColorR, allianceForClan.AllianceColorG, allianceForClan.AllianceColorB); int num = 0; for (int i = 0; i < array.Length; i++) { if (ClanConfig.ColorsClose(a, array[i])) { num = (i + 1) % array.Length; break; } } allianceForClan.AllianceColorR = array[num].r; allianceForClan.AllianceColorG = array[num].g; allianceForClan.AllianceColorB = array[num].b; MarkDirty(); MarkSharedDirty(); return "Alliance color changed."; } public string OfferNAP(long playerId, int targetClanId, int days) { PlayerClanInfo player = Data.GetPlayer(playerId); if (!IsJarl(player)) { return "Only the Jarl can offer a NAP."; } if (Data.GetClan(targetClanId) == null || targetClanId == player.ClanId) { return "Invalid diplomacy target."; } days = ((days == 14 || days == 30) ? days : 7); if (Data.NonAggressionPacts.Find((NonAggressionPact p) => p.IsBetween(player.ClanId, targetClanId) && (p.Status == PactStatus.Pending || p.Status == PactStatus.Active)) != null) { return "NAP already pending or active."; } long num = Now(); Data.NonAggressionPacts.Add(new NonAggressionPact { ClanAId = Mathf.Min(player.ClanId, targetClanId), ClanBId = Mathf.Max(player.ClanId, targetClanId), StartTime = num, EndTime = num + (long)days * 86400L, Status = PactStatus.Pending, RequestedByClanId = player.ClanId }); MarkDirty(); MarkSharedDirty(); return "NAP offer sent."; } public string AcceptNAP(long playerId, int otherClanId) { PlayerClanInfo player = Data.GetPlayer(playerId); if (!IsJarl(player)) { return "Only the Jarl can accept a NAP."; } NonAggressionPact nonAggressionPact = Data.NonAggressionPacts.Find((NonAggressionPact p) => p.IsBetween(player.ClanId, otherClanId) && p.Status == PactStatus.Pending && p.RequestedByClanId != player.ClanId); if (nonAggressionPact == null) { return "No incoming NAP offer."; } nonAggressionPact.Status = PactStatus.Active; nonAggressionPact.StartTime = Now(); MarkDirty(); MarkSharedDirty(); return "Non-Aggression Pact accepted."; } public string DeclineNAP(long playerId, int otherClanId) { PlayerClanInfo player = Data.GetPlayer(playerId); if (!IsJarl(player)) { return "Only the Jarl can decline a NAP."; } NonAggressionPact nonAggressionPact = Data.NonAggressionPacts.Find((NonAggressionPact p) => p.IsBetween(player.ClanId, otherClanId) && p.Status == PactStatus.Pending); if (nonAggressionPact == null) { return "No pending NAP."; } Data.NonAggressionPacts.Remove(nonAggressionPact); MarkDirty(); MarkSharedDirty(); return "NAP declined."; } public string BreakNAP(long playerId, int otherClanId) { PlayerClanInfo player = Data.GetPlayer(playerId); if (!IsJarl(player)) { return "Only the Jarl can break a NAP."; } NonAggressionPact nonAggressionPact = Data.NonAggressionPacts.Find((NonAggressionPact p) => p.IsBetween(player.ClanId, otherClanId) && p.Status == PactStatus.Active); if (nonAggressionPact == null) { return "No active NAP."; } nonAggressionPact.Status = PactStatus.Broken; nonAggressionPact.BrokenAt = Now(); nonAggressionPact.BreakCooldownEndTime = nonAggressionPact.BrokenAt + 86400; MarkDirty(); MarkSharedDirty(); return "NAP broken. Hostile actions unlock after 24 hours."; } public string OfferVassalage(long playerId, int targetClanId) { PlayerClanInfo player = Data.GetPlayer(playerId); if (IsJarl(player)) { ConfigEntry enableVassals = ClanConfig.EnableVassals; if (enableVassals != null && enableVassals.Value) { if (Data.GetClan(targetClanId) == null || targetClanId == player.ClanId) { return "Invalid diplomacy target."; } if (Data.GetActiveWarBetween(player.ClanId, targetClanId) != null) { return "End the active war before offering vassalage."; } if (GetActiveOverlordRelation(targetClanId) != null) { return "Target already has an overlord."; } if (WouldCreateVassalCycle(player.ClanId, targetClanId)) { return "That vassalage would create a circular chain."; } if (Data.VassalRelations.FindAll((VassalRelation v) => v.OverlordClanId == player.ClanId && v.Status == VassalStatus.Active).Count >= (ClanConfig.MaxVassalsPerClan?.Value ?? 10)) { return "Vassal limit reached."; } Data.VassalRelations.RemoveAll((VassalRelation v) => v.IsBetween(player.ClanId, targetClanId) && v.Status == VassalStatus.Pending); VassalRelation vassalRelation = new VassalRelation { OverlordClanId = player.ClanId, VassalClanId = targetClanId, StartTime = Now(), Status = VassalStatus.Pending, PendingRequestByClanId = player.ClanId }; FillVassalClanSnapshots(vassalRelation); Data.VassalRelations.Add(vassalRelation); MarkDirty(); MarkSharedDirty(); return "Vassalage offer sent."; } } return "Only the Jarl can offer vassalage."; } public string RequestProtection(long playerId, int targetOverlordClanId) { PlayerClanInfo player = Data.GetPlayer(playerId); if (IsJarl(player)) { ConfigEntry enableVassals = ClanConfig.EnableVassals; if (enableVassals != null && enableVassals.Value) { if (Data.GetClan(targetOverlordClanId) == null || targetOverlordClanId == player.ClanId) { return "Invalid diplomacy target."; } if (Data.GetActiveWarBetween(player.ClanId, targetOverlordClanId) != null) { return "End the active war before requesting protection."; } if (GetActiveOverlordRelation(player.ClanId) != null) { return "Your clan already has an overlord."; } if (WouldCreateVassalCycle(targetOverlordClanId, player.ClanId)) { return "That vassalage would create a circular chain."; } if (Data.VassalRelations.FindAll((VassalRelation v) => v.OverlordClanId == targetOverlordClanId && v.Status == VassalStatus.Active).Count >= (ClanConfig.MaxVassalsPerClan?.Value ?? 10)) { return "Target overlord has reached the vassal limit."; } Data.VassalRelations.RemoveAll((VassalRelation v) => v.IsBetween(player.ClanId, targetOverlordClanId) && v.Status == VassalStatus.Pending); VassalRelation vassalRelation = new VassalRelation { OverlordClanId = targetOverlordClanId, VassalClanId = player.ClanId, StartTime = Now(), Status = VassalStatus.Pending, PendingRequestByClanId = player.ClanId }; FillVassalClanSnapshots(vassalRelation); Data.VassalRelations.Add(vassalRelation); MarkDirty(); MarkSharedDirty(); return "Protection request sent."; } } return "Only the Jarl can request protection."; } public string AcceptVassalage(long playerId, int otherClanId) { PlayerClanInfo player = Data.GetPlayer(playerId); if (IsJarl(player)) { ConfigEntry enableVassals = ClanConfig.EnableVassals; if (enableVassals != null && enableVassals.Value) { VassalRelation relation = Data.VassalRelations.Find((VassalRelation v) => v.IsBetween(player.ClanId, otherClanId) && v.Status == VassalStatus.Pending && v.PendingRequestByClanId != player.ClanId); if (relation == null) { return "No incoming vassal request."; } if (Data.GetActiveWarBetween(relation.OverlordClanId, relation.VassalClanId) != null) { return "End the active war before accepting vassalage."; } if (GetActiveOverlordRelation(relation.VassalClanId) != null) { return "Vassal clan already has an overlord."; } if (WouldCreateVassalCycle(relation.OverlordClanId, relation.VassalClanId)) { return "That vassalage would create a circular chain."; } if (Data.VassalRelations.FindAll((VassalRelation v) => v.OverlordClanId == relation.OverlordClanId && v.Status == VassalStatus.Active).Count >= (ClanConfig.MaxVassalsPerClan?.Value ?? 10)) { return "Vassal limit reached."; } relation.Status = VassalStatus.Active; relation.StartTime = Now(); FillVassalClanSnapshots(relation); Clan clan = Data.GetClan(relation.OverlordClanId); AddDiplomacyStatusEvent((Data.GetClan(relation.VassalClanId)?.Name ?? "A clan") + " became vassal of " + (clan?.Name ?? "another clan") + "."); MarkDirty(); MarkSharedDirty(); return "Vassal relation accepted."; } } return "Only the Jarl can accept vassalage."; } public string DeclineVassalage(long playerId, int otherClanId) { PlayerClanInfo player = Data.GetPlayer(playerId); if (!IsJarl(player)) { return "Only the Jarl can decline vassalage."; } int num = Data.VassalRelations.RemoveAll((VassalRelation v) => v.IsBetween(player.ClanId, otherClanId) && v.Status == VassalStatus.Pending); MarkDirty(); MarkSharedDirty(); if (num <= 0) { return "No pending vassal request."; } return "Vassal request declined."; } public string ReleaseVassal(long playerId, int vassalClanId) { PlayerClanInfo player = Data.GetPlayer(playerId); if (!IsJarl(player)) { return "Only the Jarl can release a vassal."; } VassalRelation vassalRelation = Data.VassalRelations.Find((VassalRelation v) => v.OverlordClanId == player.ClanId && v.VassalClanId == vassalClanId && v.Status == VassalStatus.Active); if (vassalRelation == null) { return "No active vassal relation."; } vassalRelation.Status = VassalStatus.Broken; SetDiplomacyState(player.ClanId, vassalClanId, DiplomacyState.Neutral); AddDiplomacyStatusEvent((Data.GetClan(vassalClanId)?.Name ?? "Vassal") + " was released by " + (Data.GetClan(player.ClanId)?.Name ?? "overlord") + "."); MarkDirty(); MarkSharedDirty(); return "Vassal released."; } public string LeaveOverlord(long playerId) { PlayerClanInfo player = Data.GetPlayer(playerId); if (!IsJarl(player)) { return "Only the Jarl can leave an overlord."; } VassalRelation vassalRelation = Data.VassalRelations.Find((VassalRelation v) => v.VassalClanId == player.ClanId && v.Status == VassalStatus.Active); if (vassalRelation == null) { return "No active overlord."; } vassalRelation.Status = VassalStatus.Broken; SetDiplomacyState(player.ClanId, vassalRelation.OverlordClanId, DiplomacyState.Neutral); Clan clan = Data.GetClan(vassalRelation.OverlordClanId); AddDiplomacyStatusEvent((Data.GetClan(player.ClanId)?.Name ?? "Vassal") + " broke vassalage with " + (clan?.Name ?? "overlord") + "."); MarkDirty(); MarkSharedDirty(); return "Overlord relation ended."; } public string OfferTerritoryDeal(long playerId, string territoryId, int targetClanId, long price) { //IL_00c9: Unknown result type (might be due to invalid IL or missing references) PlayerClanInfo player = Data.GetPlayer(playerId); if (IsJarl(player)) { ConfigEntry enableTerritoryTransfers = ClanConfig.EnableTerritoryTransfers; if (enableTerritoryTransfers != null && enableTerritoryTransfers.Value) { if (Data.GetClan(targetClanId) == null || targetClanId == player.ClanId) { return "Invalid target clan."; } if (!Data.Territories.TryGetValue(territoryId ?? "", out var territory) || territory.OwnerClanId != player.ClanId || territory.FreebornOwnerId != 0L) { return "You can transfer only clan territory you own."; } if (ProtectedWorldZones.IsProtected(territory)) { return "" + ProtectedWorldZones.GetBlockedActionMessage(territory.GridPosition) + ""; } if (territory.Type == TerritoryType.Capital) { ConfigEntry allowCapitalTransfer = ClanConfig.AllowCapitalTransfer; if (allowCapitalTransfer == null || !allowCapitalTransfer.Value) { return "Capital transfer is disabled."; } } if (Data.ActiveCaptures.Exists((CaptureSession c) => c.TerritoryGridX == territory.GridX && c.TerritoryGridY == territory.GridY)) { return "Territory is under active capture."; } if (Data.TerritoryTransferOffers.FindAll((TerritoryTransferOffer o) => o.Status == TerritoryTransferStatus.Pending && (o.FromClanId == player.ClanId || o.ToClanId == player.ClanId)).Count >= (ClanConfig.MaxPendingTerritoryDealsPerClan?.Value ?? 5)) { return "Pending territory deal limit reached."; } long num = Now(); Data.TerritoryTransferOffers.Add(new TerritoryTransferOffer { OfferId = Data.NextTerritoryDealId++, FromClanId = player.ClanId, ToClanId = targetClanId, TerritoryId = territoryId, Price = Math.Max(0L, price), DealType = ((price > 0) ? TerritoryDealType.Sale : TerritoryDealType.Gift), FromTerritoryIds = new List { territoryId }, ToCoins = Math.Max(0L, price), FromAccepted = true, ToAccepted = false, CreatedAt = num, ExpiresAt = num + (long)(ClanConfig.TerritoryDealExpirationHours?.Value ?? 48) * 3600L, Status = TerritoryTransferStatus.Pending }); MarkDirty(); if (price <= 0) { return "Territory gift offered."; } return "Territory sale offered."; } } return "Only the Jarl can offer territory deals."; } public string OfferTerritoryExchange(long playerId, int targetClanId, string payload) { PlayerClanInfo player = Data.GetPlayer(playerId); if (IsJarl(player)) { ConfigEntry enableTerritoryTransfers = ClanConfig.EnableTerritoryTransfers; if (enableTerritoryTransfers != null && enableTerritoryTransfers.Value) { if (Data.GetClan(targetClanId) == null || targetClanId == player.ClanId) { return "Invalid target clan."; } TerritoryTransferOffer territoryTransferOffer = ParseExchangePayload(payload); territoryTransferOffer.OfferId = Data.NextTerritoryDealId++; territoryTransferOffer.FromClanId = player.ClanId; territoryTransferOffer.ToClanId = targetClanId; territoryTransferOffer.DealType = TerritoryDealType.Exchange; territoryTransferOffer.CreatedAt = Now(); territoryTransferOffer.ExpiresAt = territoryTransferOffer.CreatedAt + (long)(ClanConfig.TerritoryDealExpirationHours?.Value ?? 48) * 3600L; territoryTransferOffer.Status = TerritoryTransferStatus.Pending; territoryTransferOffer.FromAccepted = true; territoryTransferOffer.ToAccepted = false; string text = ValidateExchangeOffer(territoryTransferOffer, creating: true); if (!string.IsNullOrEmpty(text)) { return text; } if (Data.TerritoryTransferOffers.FindAll((TerritoryTransferOffer o) => o.Status == TerritoryTransferStatus.Pending && (o.FromClanId == player.ClanId || o.ToClanId == player.ClanId)).Count >= (ClanConfig.MaxPendingTerritoryDealsPerClan?.Value ?? 5)) { return "Pending territory deal limit reached."; } Data.TerritoryTransferOffers.Add(territoryTransferOffer); MarkDirty(); return "Territory exchange offered."; } } return "Only the Jarl can offer territory exchange deals."; } public string DiscoverTerritory(long playerId, Vector2i grid, out Vector3 center, out float radius) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) //IL_0002: Unknown result type (might be due to invalid IL or missing references) //IL_0007: Unknown result type (might be due to invalid IL or missing references) //IL_0051: Unknown result type (might be due to invalid IL or missing references) //IL_006c: Unknown result type (might be due to invalid IL or missing references) center = grid.ToWorldCenter(); radius = (float)(ClanConfig.GridCellSize?.Value ?? 500) * 0.65f; PlayerClanInfo player = Data.GetPlayer(playerId); if (player == null || (player.ClanId == 0 && !player.IsFreeborn)) { return "Join a clan or choose Freeborn first."; } if (ProtectedWorldZones.IsProtected(grid)) { return "Protected World Zones do not need paid discovery."; } long num = 3000L; string text = Data.GetTerritory(grid)?.DisplayName ?? ("Territory " + grid.x + "," + grid.y); if (player.IsFreeborn && player.ClanId == 0) { if (player.FreebornTreasury < num) { return "Freeborn Treasury does not have enough coins."; } player.FreebornTreasury -= num; } else { Clan clan = Data.GetClan(player.ClanId); if (clan == null) { return "Clan not found."; } if (clan.Treasury < num) { return "Clan Treasury does not have enough coins."; } ChangeClanTreasury(clan, -num, "Purchase", "Clan purchase"); } AddDiplomacyStatusEvent("Territory discovered: " + text); MarkDirty(); return "Territory discovered: " + text + ""; } public string AcceptTerritoryDeal(long playerId, int offerId) { //IL_00ed: Unknown result type (might be due to invalid IL or missing references) PlayerClanInfo player = Data.GetPlayer(playerId); if (!IsJarl(player)) { return "Only the Jarl can accept territory deals."; } TerritoryTransferOffer territoryTransferOffer = Data.TerritoryTransferOffers.Find((TerritoryTransferOffer o) => o.OfferId == offerId && o.Status == TerritoryTransferStatus.Pending); if (territoryTransferOffer == null || territoryTransferOffer.ToClanId != player.ClanId) { return "No incoming territory deal."; } if (territoryTransferOffer.ExpiresAt <= Now()) { territoryTransferOffer.Status = TerritoryTransferStatus.Expired; MarkDirty(); return "Territory deal expired."; } if (territoryTransferOffer.DealType == TerritoryDealType.Exchange) { return AcceptTerritoryExchange(player, territoryTransferOffer); } if (!Data.ClanHasCapital(player.ClanId)) { return "Buyer needs a capital before receiving territory."; } if (!Data.Territories.TryGetValue(territoryTransferOffer.TerritoryId ?? "", out var value) || value.OwnerClanId != territoryTransferOffer.FromClanId) { return "Territory is no longer available."; } if (ProtectedWorldZones.IsProtected(value)) { return "" + ProtectedWorldZones.GetBlockedActionMessage(value.GridPosition) + ""; } Clan clan = Data.GetClan(territoryTransferOffer.ToClanId); Clan clan2 = Data.GetClan(territoryTransferOffer.FromClanId); if (clan == null || clan2 == null) { return "Clan not found."; } if (territoryTransferOffer.Price > 0 && clan.Treasury < territoryTransferOffer.Price) { return "Not enough Clan Treasury."; } ChangeClanTreasury(clan, -territoryTransferOffer.Price, "Purchase", "Territory trade"); ChangeClanTreasury(clan2, territoryTransferOffer.Price, "Sale", "Territory trade"); value.OwnerClanId = territoryTransferOffer.ToClanId; value.Type = TerritoryType.Colony; value.State = TerritoryState.Normal; value.ClaimTimestamp = Now(); value.ClaimedByPlayerName = clan.Name; territoryTransferOffer.Status = TerritoryTransferStatus.Accepted; MarkDirty(); return "Territory deal accepted."; } private string AcceptTerritoryExchange(PlayerClanInfo player, TerritoryTransferOffer offer) { if (!Data.ClanHasCapital(offer.ToClanId) || !Data.ClanHasCapital(offer.FromClanId)) { return "Both clans need a capital before exchanging territory."; } string text = ValidateExchangeOffer(offer, creating: false); if (!string.IsNullOrEmpty(text)) { return text; } Clan clan = Data.GetClan(offer.FromClanId); Clan clan2 = Data.GetClan(offer.ToClanId); if (clan == null || clan2 == null) { return "Clan not found."; } ChangeClanTreasury(clan, -Math.Max(0L, offer.FromCoins), "Trade", "Clan trade offer"); ChangeClanTreasury(clan2, Math.Max(0L, offer.FromCoins), "Trade", "Clan trade offer"); ChangeClanTreasury(clan2, -Math.Max(0L, offer.ToCoins), "Trade", "Clan trade offer"); ChangeClanTreasury(clan, Math.Max(0L, offer.ToCoins), "Trade", "Clan trade offer"); MoveVaultItems(offer.FromClanId, offer.ToClanId, offer.FromResources); MoveVaultItems(offer.ToClanId, offer.FromClanId, offer.ToResources); foreach (string item in offer.FromTerritoryIds ?? new List()) { if (Data.Territories.TryGetValue(item ?? "", out var value)) { value.OwnerClanId = offer.ToClanId; value.Type = TerritoryType.Colony; value.State = TerritoryState.Normal; value.ClaimTimestamp = Now(); value.ClaimedByPlayerName = clan2.Name; } } foreach (string item2 in offer.ToTerritoryIds ?? new List()) { if (Data.Territories.TryGetValue(item2 ?? "", out var value2)) { value2.OwnerClanId = offer.FromClanId; value2.Type = TerritoryType.Colony; value2.State = TerritoryState.Normal; value2.ClaimTimestamp = Now(); value2.ClaimedByPlayerName = clan.Name; } } offer.ToAccepted = true; offer.Status = TerritoryTransferStatus.Accepted; MarkDirty(); return "Territory exchange accepted."; } private string ValidateExchangeOffer(TerritoryTransferOffer offer, bool creating) { Clan clan = Data.GetClan(offer.FromClanId); Clan clan2 = Data.GetClan(offer.ToClanId); if (clan == null || clan2 == null) { return "Clan not found."; } if ((offer.FromTerritoryIds == null || offer.FromTerritoryIds.Count == 0) && (offer.ToTerritoryIds == null || offer.ToTerritoryIds.Count == 0) && offer.FromCoins <= 0 && offer.ToCoins <= 0 && IsResourceListEmpty(offer.FromResources) && IsResourceListEmpty(offer.ToResources)) { return "Exchange offer is empty."; } string text = ValidateOfferedTerritories(offer.FromTerritoryIds, offer.FromClanId); if (!string.IsNullOrEmpty(text)) { return text; } string text2 = ValidateOfferedTerritories(offer.ToTerritoryIds, offer.ToClanId); if (!string.IsNullOrEmpty(text2)) { return text2; } if (!creating) { if (clan.Treasury < Math.Max(0L, offer.FromCoins)) { return "Offering clan lacks treasury coins."; } if (clan2.Treasury < Math.Max(0L, offer.ToCoins)) { return "Target clan lacks treasury coins."; } if (!HasVaultItems(offer.FromClanId, offer.FromResources)) { return "Offering clan lacks required vault materials."; } if (!HasVaultItems(offer.ToClanId, offer.ToResources)) { return "Target clan lacks required vault materials."; } } return ""; } private string ValidateOfferedTerritories(List ids, int ownerClanId) { //IL_008a: Unknown result type (might be due to invalid IL or missing references) foreach (string item in ids ?? new List()) { if (string.IsNullOrWhiteSpace(item)) { continue; } if (!Data.Territories.TryGetValue(item.Trim(), out var territory) || territory.OwnerClanId != ownerClanId || territory.FreebornOwnerId != 0L) { return "Exchange contains territory not owned by the offering clan."; } if (ProtectedWorldZones.IsProtected(territory)) { return "" + ProtectedWorldZones.GetBlockedActionMessage(territory.GridPosition) + ""; } if (territory.Type == TerritoryType.Capital) { ConfigEntry allowCapitalTransfer = ClanConfig.AllowCapitalTransfer; if (allowCapitalTransfer == null || !allowCapitalTransfer.Value) { return "Capital transfer is disabled."; } } if (Data.ActiveCaptures.Exists((CaptureSession c) => c.TerritoryGridX == territory.GridX && c.TerritoryGridY == territory.GridY)) { return "Exchange contains territory under active capture."; } } return ""; } private static bool IsResourceListEmpty(List items) { if (items == null || items.Count == 0) { return true; } return !items.Exists((ClanVaultItem i) => i != null && i.Amount > 0 && !string.IsNullOrWhiteSpace(i.PrefabName)); } private bool HasVaultItems(int clanId, List items) { foreach (ClanVaultItem item in items ?? new List()) { if (item != null && item.Amount > 0 && !string.IsNullOrWhiteSpace(item.PrefabName)) { ClanVaultItem clanVaultItem = Data.GetVault(clanId).Find((ClanVaultItem v) => string.Equals(v.PrefabName, item.PrefabName, StringComparison.OrdinalIgnoreCase)); if (clanVaultItem == null || clanVaultItem.Amount < item.Amount) { return false; } } } return true; } private void MoveVaultItems(int fromClanId, int toClanId, List items) { foreach (ClanVaultItem item in items ?? new List()) { if (item == null || item.Amount <= 0 || string.IsNullOrWhiteSpace(item.PrefabName)) { continue; } List vault = Data.GetVault(fromClanId); ClanVaultItem clanVaultItem = vault.Find((ClanVaultItem v) => string.Equals(v.PrefabName, item.PrefabName, StringComparison.OrdinalIgnoreCase)); if (clanVaultItem != null) { clanVaultItem.Amount -= item.Amount; if (clanVaultItem.Amount <= 0) { vault.Remove(clanVaultItem); } Data.AddToVault(toClanId, item.PrefabName, string.IsNullOrEmpty(item.DisplayName) ? item.PrefabName : item.DisplayName, item.Amount); } } } private TerritoryTransferOffer ParseExchangePayload(string payload) { string[] parts = (payload ?? "").Split(new char[1] { '|' }); return new TerritoryTransferOffer { FromTerritoryIds = ParseIdList(GetPart(parts, 0)), ToTerritoryIds = ParseIdList(GetPart(parts, 1)), FromCoins = ParseLong(GetPart(parts, 2)), ToCoins = ParseLong(GetPart(parts, 3)), FromResources = ParseResourceList(GetPart(parts, 4)), ToResources = ParseResourceList(GetPart(parts, 5)) }; } private static string GetPart(string[] parts, int index) { if (parts == null || index < 0 || index >= parts.Length) { return ""; } return parts[index]; } private static long ParseLong(string value) { if (!long.TryParse((value ?? "").Trim(), out var result)) { return 0L; } return Math.Max(0L, result); } private static List ParseIdList(string value) { List list = new List(); string[] array = (value ?? "").Split(new char[3] { ';', ' ', '\n' }, StringSplitOptions.RemoveEmptyEntries); for (int i = 0; i < array.Length; i++) { string item = array[i].Trim(); if (!list.Contains(item)) { list.Add(item); } } return list; } private static List ParseResourceList(string value) { List list = new List(); string[] array = (value ?? "").Split(new char[2] { ',', ';' }, StringSplitOptions.RemoveEmptyEntries); for (int i = 0; i < array.Length; i++) { string[] array2 = array[i].Split(new char[1] { ':' }); if (array2.Length >= 2) { string text = array2[0].Trim(); if (int.TryParse(array2[1].Trim(), out var result) && result > 0 && !string.IsNullOrWhiteSpace(text)) { list.Add(new ClanVaultItem { PrefabName = text, DisplayName = text, Amount = result }); } } } return list; } public string CreateTestContract(long playerId, ContractType type) { if (!HasAdminAccess(playerId)) { return "Admin/debug mode required."; } long num = Now(); Contract contract = new Contract { ContractId = Data.NextContractId++, ContractType = type, Title = TestContractTitle(type), Description = TestContractDescription(type), IssuerType = ContractIssuerType.Admin, IssuerId = playerId, TargetType = ContractTargetType.None, RequiredItem = ((type == ContractType.Gathering) ? "Wood" : ""), RequiredAmount = ((type != ContractType.Gathering) ? 1 : 50), RewardCoins = TestContractReward(type), Status = ContractStatus.Available, CreatedAt = num, ExpiresAt = num + 86400 }; Data.Contracts.Add(contract); AddContractLog("Created: " + contract.Title); MarkDirty(); return "Contract created: " + contract.Title + ""; } public string CreateTestContractPreset(long playerId, int preset) { if (!HasAdminAccess(playerId)) { return "Admin/debug mode required."; } Contract contract = CreateContractPreset(playerId, preset); if (contract == null) { return "Unknown contract preset."; } Data.Contracts.Add(contract); AddContractLog("Created: " + contract.Title); MarkDirty(); return "Contract created: " + contract.Title + ""; } public string AcceptContract(long playerId, int contractId) { PlayerClanInfo player = Data.GetPlayer(playerId); if (player == null) { return "Player not registered."; } Contract contract = Data.Contracts.Find((Contract c) => c.ContractId == contractId); if (contract == null || contract.Status != ContractStatus.Available) { return "Contract is not available."; } if (contract.ExpiresAt > 0 && contract.ExpiresAt <= Now()) { contract.Status = ContractStatus.Expired; AddContractLog("Expired: " + contract.Title); MarkDirty(); return "Contract expired."; } contract.AcceptedBy = playerId; contract.Status = ContractStatus.Accepted; AddContractLog("Accepted: " + contract.Title + " by " + (player.PlayerName ?? playerId.ToString())); MarkDirty(); return "Contract accepted: " + contract.Title + ""; } public string CompleteContract(long playerId, int contractId) { PlayerClanInfo player = Data.GetPlayer(playerId); if (player == null) { return "Player not registered."; } Contract contract = Data.Contracts.Find((Contract c) => c.ContractId == contractId); if (contract == null || (contract.Status != ContractStatus.Accepted && contract.Status != ContractStatus.Available)) { return "Contract cannot be completed."; } if (contract.AcceptedBy != 0L && contract.AcceptedBy != playerId) { return "Contract accepted by another player."; } if (contract.ExpiresAt > 0 && contract.ExpiresAt <= Now()) { contract.Status = ContractStatus.Expired; AddContractLog("Expired: " + contract.Title); MarkDirty(); return "Contract expired."; } if (player.IsFreeborn && player.ClanId == 0) { player.FreebornTreasury += Math.Max(0L, contract.RewardCoins); } else { Clan clan = Data.GetClan(player.ClanId); if (clan == null) { return "Clan not found."; } ChangeClanTreasury(clan, Math.Max(0L, contract.RewardCoins), "Reward", "Contract reward"); } contract.AcceptedBy = playerId; contract.Status = ContractStatus.Completed; AddContractLog("Completed: " + contract.Title + " by " + (player.PlayerName ?? playerId.ToString())); MarkDirty(); return "Contract completed: " + contract.Title + ""; } public string AdminContractAction(long playerId, int action, int contractId) { if (!HasAdminAccess(playerId)) { return "Admin/debug mode required."; } Contract contract = Data.Contracts.Find((Contract c) => c.ContractId == contractId); if (contract == null) { return "Contract not found."; } switch (action) { case 1: contract.Status = ContractStatus.Expired; AddContractLog("Expired: " + contract.Title); MarkDirty(); return "Contract expired."; case 2: AddContractLog("Removed: " + contract.Title); Data.Contracts.Remove(contract); MarkDirty(); return "Contract removed."; default: return "Unknown contract admin action."; } } private bool HasAdminAccess(long playerId) { return GetAdminAccessInfo(playerId, 0L).EventAdminAccess; } private Contract CreateContractPreset(long playerId, int preset) { long now = Now(); return preset switch { 1 => NewContract(playerId, ContractType.Gathering, "Deliver Wood", "Deliver 50 Wood for a simple gathering reward.", ContractTargetType.Item, "Wood", "Wood", 50, 250L, now), 2 => NewContract(playerId, ContractType.Gathering, "Deliver Stone", "Deliver 50 Stone for a simple gathering reward.", ContractTargetType.Item, "Stone", "Stone", 50, 250L, now), 3 => NewContract(playerId, ContractType.Exploration, "Discover Territory", "Discover a selected territory area for the issuer.", ContractTargetType.Territory, "", "Discovery", 1, 400L, now), 4 => NewContract(playerId, ContractType.WorldEvents, "Visit Event Area", "Travel to an active world event area.", ContractTargetType.WorldEvent, "", "Visit", 1, 350L, now), 5 => NewContract(playerId, ContractType.WorldEvents, "Complete World Event", "Complete one active world event objective.", ContractTargetType.WorldEvent, "", "World Event", 1, 600L, now), 6 => NewContract(playerId, ContractType.Economy, "Treasury Contribution", "Contribute coins to a clan or Freeborn treasury.", ContractTargetType.None, "", "Coins", 1, 300L, now), _ => null, }; } private Contract NewContract(long playerId, ContractType type, string title, string description, ContractTargetType targetType, string targetId, string requiredItem, int requiredAmount, long rewardCoins, long now) { return new Contract { ContractId = Data.NextContractId++, ContractType = type, Title = title, Description = description, IssuerType = ContractIssuerType.Admin, IssuerId = playerId, TargetType = targetType, TargetId = (targetId ?? ""), RequiredItem = (requiredItem ?? ""), RequiredAmount = requiredAmount, RewardCoins = rewardCoins, Status = ContractStatus.Available, CreatedAt = now, ExpiresAt = now + 86400 }; } private void AddContractLog(string entry) { if (Data.ContractLog == null) { Data.ContractLog = new List(); } Data.ContractLog.Insert(0, DateTimeOffset.UtcNow.ToString("yyyy-MM-dd HH:mm") + " UTC - " + entry); if (Data.ContractLog.Count > 80) { Data.ContractLog.RemoveRange(80, Data.ContractLog.Count - 80); } AddDiplomacyStatusEvent("Contract " + entry); } private static string TestContractTitle(ContractType type) { return type switch { ContractType.Gathering => "Deliver Wood", ContractType.Exploration => "Discover Territory", ContractType.Warfare => "Place Bounty", ContractType.Economy => "Treasury Contribution", ContractType.WorldEvents => "Complete World Event", ContractType.Building => "Supply Building Materials", ContractType.Freeborn => "Homestead Supply", _ => "Test Contract", }; } private static string TestContractDescription(ContractType type) { return type switch { ContractType.Gathering => "Foundation gathering contract. Delivery hooks will expand later.", ContractType.Exploration => "Foundation exploration contract for discovery/scouting tasks.", ContractType.WorldEvents => "Foundation world-event participation contract.", _ => "Foundation contract used for testing the task board.", }; } private static long TestContractReward(ContractType type) { return type switch { ContractType.Gathering => 250L, ContractType.Exploration => 400L, ContractType.WorldEvents => 600L, ContractType.Warfare => 500L, ContractType.Building => 350L, ContractType.Freeborn => 200L, _ => 300L, }; } public string DeclineTerritoryDeal(long playerId, int offerId) { PlayerClanInfo player = Data.GetPlayer(playerId); if (!IsJarl(player)) { return "Only the Jarl can decline territory deals."; } TerritoryTransferOffer territoryTransferOffer = Data.TerritoryTransferOffers.Find((TerritoryTransferOffer o) => o.OfferId == offerId && o.Status == TerritoryTransferStatus.Pending && o.ToClanId == player.ClanId); if (territoryTransferOffer == null) { return "No incoming territory deal."; } territoryTransferOffer.Status = TerritoryTransferStatus.Declined; MarkDirty(); return "Territory deal declined."; } public string CancelTerritoryDeal(long playerId, int offerId) { PlayerClanInfo player = Data.GetPlayer(playerId); if (!IsJarl(player)) { return "Only the Jarl can cancel territory deals."; } TerritoryTransferOffer territoryTransferOffer = Data.TerritoryTransferOffers.Find((TerritoryTransferOffer o) => o.OfferId == offerId && o.Status == TerritoryTransferStatus.Pending && o.FromClanId == player.ClanId); if (territoryTransferOffer == null) { return "No outgoing territory deal."; } territoryTransferOffer.Status = TerritoryTransferStatus.Declined; MarkDirty(); return "Territory deal canceled."; } public string SendFreebornFriendRequest(long playerId, long targetId) { PlayerClanInfo player = Data.GetPlayer(playerId); PlayerClanInfo player2 = Data.GetPlayer(targetId); if (player == null || player2 == null || !player.IsFreeborn || !player2.IsFreeborn || player.ClanId != 0 || player2.ClanId != 0) { return "Both players must be Freeborn."; } EnsureFreebornLists(player); EnsureFreebornLists(player2); if (player.FreebornFriends.Contains(targetId)) { return "You are already friends."; } if (player.FreebornFriends.Count >= (ClanConfig.FreebornMaxFriends?.Value ?? 2)) { return "Freeborn friend limit reached."; } if (!player2.PendingFreebornFriendRequests.Contains(playerId)) { player2.PendingFreebornFriendRequests.Add(playerId); } MarkDirty(); return "Friendship request sent."; } public string AcceptFreebornFriendRequest(long playerId, long fromId) { PlayerClanInfo player = Data.GetPlayer(playerId); PlayerClanInfo player2 = Data.GetPlayer(fromId); if (player == null || player2 == null || !player.IsFreeborn || !player2.IsFreeborn) { return "Both players must be Freeborn."; } EnsureFreebornLists(player); EnsureFreebornLists(player2); if (!player.PendingFreebornFriendRequests.Remove(fromId)) { return "No friendship request from that player."; } int num = ClanConfig.FreebornMaxFriends?.Value ?? 2; if (player.FreebornFriends.Count >= num || player2.FreebornFriends.Count >= num) { return "Freeborn friend limit reached."; } if (!player.FreebornFriends.Contains(fromId)) { player.FreebornFriends.Add(fromId); } if (!player2.FreebornFriends.Contains(playerId)) { player2.FreebornFriends.Add(playerId); } MarkDirty(); return "Friendship accepted."; } public string RemoveFreebornFriend(long playerId, long friendId) { PlayerClanInfo player = Data.GetPlayer(playerId); PlayerClanInfo player2 = Data.GetPlayer(friendId); if (player == null) { return "Unknown player."; } EnsureFreebornLists(player); player.FreebornFriends.Remove(friendId); player.PendingFreebornFriendRequests.Remove(friendId); if (player2 != null) { EnsureFreebornLists(player2); player2.FreebornFriends.Remove(playerId); player2.PendingFreebornFriendRequests.Remove(playerId); } MarkDirty(); return "Friendship removed."; } public string SendClanFoundingInvite(long playerId, long targetId) { PlayerClanInfo player = Data.GetPlayer(playerId); PlayerClanInfo player2 = Data.GetPlayer(targetId); if (player == null || player2 == null || !player.IsFreeborn || !player2.IsFreeborn || player.ClanId != 0 || player2.ClanId != 0) { return "Both founding players must be Freeborn."; } EnsureFreebornLists(player); EnsureFreebornLists(player2); if (player2.PendingClanFounderInvites.Contains(playerId) || player.AcceptedClanFounderInvites.Contains(targetId)) { return "Founding invitation already exists."; } player2.PendingClanFounderInvites.Add(playerId); MarkDirty(); return "Clan founding invitation sent."; } public string AcceptClanFoundingInvite(long playerId, long founderId) { PlayerClanInfo player = Data.GetPlayer(playerId); PlayerClanInfo player2 = Data.GetPlayer(founderId); if (player == null || player2 == null || !player.IsFreeborn || !player2.IsFreeborn || player.ClanId != 0 || player2.ClanId != 0) { return "Both founding players must be Freeborn."; } EnsureFreebornLists(player); EnsureFreebornLists(player2); if (!player.PendingClanFounderInvites.Remove(founderId)) { return "No clan founding invitation from that player."; } if (!player2.AcceptedClanFounderInvites.Contains(playerId)) { player2.AcceptedClanFounderInvites.Add(playerId); } MarkDirty(); return "Clan founding invitation accepted."; } public string DeclineClanFoundingInvite(long playerId, long founderId) { PlayerClanInfo player = Data.GetPlayer(playerId); PlayerClanInfo player2 = Data.GetPlayer(founderId); if (player == null) { return "Unknown player."; } EnsureFreebornLists(player); player.PendingClanFounderInvites.Remove(founderId); if (player2 != null) { EnsureFreebornLists(player2); player2.AcceptedClanFounderInvites.Remove(playerId); } MarkDirty(); return "Clan founding invitation declined."; } public bool AreFreebornFriends(long a, long b) { PlayerClanInfo player = Data.GetPlayer(a); EnsureFreebornLists(player); return player?.FreebornFriends.Contains(b) ?? false; } private void EnsureFreebornLists(PlayerClanInfo player) { if (player != null) { if (player.FreebornFriends == null) { player.FreebornFriends = new List(); } if (player.PendingFreebornFriendRequests == null) { player.PendingFreebornFriendRequests = new List(); } if (player.PendingClanFounderInvites == null) { player.PendingClanFounderInvites = new List(); } if (player.AcceptedClanFounderInvites == null) { player.AcceptedClanFounderInvites = new List(); } } } public bool CanGatherInTerritory(PlayerClanInfo player, ClanTerritory territory, out string message) { //IL_0012: Unknown result type (might be due to invalid IL or missing references) return CanGatherInTerritory(player, territory, territory?.GridPosition, out message); } public bool CanGatherInTerritory(PlayerClanInfo player, ClanTerritory territory, Vector2i? grid, out string message) { //IL_0020: Unknown result type (might be due to invalid IL or missing references) message = null; if (!RealmRules.GetBool("resource_gathering_restriction") || player == null) { return true; } if (grid.HasValue && ProtectedWorldZones.IsProtected(grid.Value)) { return true; } if (territory == null) { if (player.IsFreeborn && player.ClanId == 0) { ConfigEntry freebornGatherOnlyOwnTerritory = ClanConfig.FreebornGatherOnlyOwnTerritory; if (freebornGatherOnlyOwnTerritory != null && freebornGatherOnlyOwnTerritory.Value) { message = "This territory does not belong to you."; return false; } } if (player.ClanId != 0) { ConfigEntry clanGatherOnlyOwnTerritory = ClanConfig.ClanGatherOnlyOwnTerritory; if (clanGatherOnlyOwnTerritory != null && clanGatherOnlyOwnTerritory.Value) { message = "You do not have gathering rights in this territory."; return false; } } return true; } if (player.IsFreeborn && player.ClanId == 0) { ConfigEntry freebornGatherOnlyOwnTerritory2 = ClanConfig.FreebornGatherOnlyOwnTerritory; if (freebornGatherOnlyOwnTerritory2 == null || !freebornGatherOnlyOwnTerritory2.Value) { ConfigEntry freebornIgnoreResourceOwnership = ClanConfig.FreebornIgnoreResourceOwnership; if (freebornIgnoreResourceOwnership != null && freebornIgnoreResourceOwnership.Value) { return true; } } if (territory.FreebornOwnerId == player.PlayerId) { return true; } ConfigEntry allowFriendGathering = ClanConfig.AllowFriendGathering; if (allowFriendGathering != null && allowFriendGathering.Value && territory.FreebornOwnerId != 0L && AreFreebornFriends(player.PlayerId, territory.FreebornOwnerId)) { return true; } message = "This territory does not belong to you."; return false; } if (player.ClanId == 0) { message = "You do not have gathering rights in this territory."; return false; } if (territory.OwnerClanId == player.ClanId) { return true; } ConfigEntry allowAllianceGathering = ClanConfig.AllowAllianceGathering; if (allowAllianceGathering == null || !allowAllianceGathering.Value) { ConfigEntry allowGatherInAlliedTerritory = ClanConfig.AllowGatherInAlliedTerritory; if (allowGatherInAlliedTerritory == null || !allowGatherInAlliedTerritory.Value) { goto IL_0175; } } if (territory.OwnerClanId != 0 && GetDiplomacyState(player.ClanId, territory.OwnerClanId) == DiplomacyState.Allied) { return true; } goto IL_0175; IL_0175: ConfigEntry clanGatherOnlyOwnTerritory2 = ClanConfig.ClanGatherOnlyOwnTerritory; if (clanGatherOnlyOwnTerritory2 == null || !clanGatherOnlyOwnTerritory2.Value) { ConfigEntry blockGatheringInEnemyTerritory = ClanConfig.BlockGatheringInEnemyTerritory; if (blockGatheringInEnemyTerritory == null || !blockGatheringInEnemyTerritory.Value) { return true; } } message = "You do not have gathering rights in this territory."; return false; } public string SetClanTax(long playerId, int resourceTaxPercent, int coinTaxPercent) { ConfigEntry enableClanMemberTax = ClanConfig.EnableClanMemberTax; if (enableClanMemberTax == null || !enableClanMemberTax.Value) { return "Clan taxes are disabled."; } PlayerClanInfo player = Data.GetPlayer(playerId); if (player == null || player.ClanId == 0) { return "You are not in a clan."; } Clan clan = Data.GetClan(player.ClanId); if (clan == null) { return "Clan not found."; } if (clan.JarlId != playerId && player.Rank != ClanRank.Jarl) { return "Only the Jarl may change clan taxes."; } int num = Mathf.Clamp(ClanConfig.MaxClanTaxPercent?.Value ?? 50, 0, 100); resourceTaxPercent = Mathf.Clamp(resourceTaxPercent, 0, num); coinTaxPercent = Mathf.Clamp(coinTaxPercent, 0, num); clan.ResourceTaxPercent = SnapTaxPercent(resourceTaxPercent, num); clan.CoinTaxPercent = SnapTaxPercent(coinTaxPercent, num); MarkDirty(); return $"Clan taxes updated: resources {clan.ResourceTaxPercent}%, coins {clan.CoinTaxPercent}%."; } private int SnapTaxPercent(int value, int max) { int[] obj = new int[6] { 0, 10, 20, 30, 40, 50 }; int num = 0; int[] array = obj; foreach (int num2 in array) { if (num2 <= max && Mathf.Abs(value - num2) < Mathf.Abs(value - num)) { num = num2; } } return num; } public bool TryRecordResourceTax(long playerId, string prefabName, string displayName, int taxAmount, out string message) { message = null; ConfigEntry enableClanMemberTax = ClanConfig.EnableClanMemberTax; if (enableClanMemberTax == null || !enableClanMemberTax.Value || taxAmount <= 0) { return false; } PlayerClanInfo player = Data.GetPlayer(playerId); if (player == null || player.ClanId == 0 || player.IsFreeborn) { return false; } Clan clan = Data.GetClan(player.ClanId); if (clan == null) { return false; } List vault = Data.GetVault(player.ClanId); int num = Economy.VaultCapacity(clan, Data.GetClanTerritoryCount(player.ClanId)); bool flag = vault.Find((ClanVaultItem v) => v.PrefabName == prefabName) != null; if (Data.GetVaultStackCount(player.ClanId) >= num && !flag) { message = "Clan Vault is full. Resource tax skipped."; TaxDebug("[FCW Tax] vault full tax skipped player=" + player.PlayerName + " item=" + prefabName + " tax=" + taxAmount); return false; } Data.AddToVault(player.ClanId, prefabName, string.IsNullOrEmpty(displayName) ? prefabName : displayName, taxAmount); player.ResourceTaxPaid += taxAmount; clan.TotalResourceTaxCollected += taxAmount; clan.SessionResourceTaxCollected += taxAmount; TaxDebug("[FCW Tax] player=" + player.PlayerName + " item=" + prefabName + " amount=? tax=" + taxAmount); MarkDirty(); return true; } public bool TryRecordCoinTax(long playerId, int baseTaxAmount) { ConfigEntry enableClanMemberTax = ClanConfig.EnableClanMemberTax; if (enableClanMemberTax == null || !enableClanMemberTax.Value || baseTaxAmount <= 0) { return false; } PlayerClanInfo player = Data.GetPlayer(playerId); if (player == null || player.ClanId == 0 || player.IsFreeborn) { return false; } Clan clan = Data.GetClan(player.ClanId); if (clan == null) { return false; } long num = Mathf.FloorToInt((float)baseTaxAmount * (1f + GetMerchantTaxEfficiency(player))); player.CoinTaxPaid += baseTaxAmount; ChangeClanTreasury(clan, num, "Income", "Territory tax"); clan.TotalCoinTaxCollected += num; clan.SessionCoinTaxCollected += num; TaxDebug("[FCW Tax] coins amount=? tax=" + baseTaxAmount + " treasury=" + num + " player=" + player.PlayerName); MarkDirty(); return true; } private float GetMerchantTaxEfficiency(PlayerClanInfo player) { return RoleUtils.GetRoleFactor(player, ClanRole.Merchant) * ((ClanConfig.MerchantEconomicRewardBonus?.Value ?? 10f) / 100f); } private void TaxDebug(string message) { ConfigEntry enableTaxDebugLogs = ClanConfig.EnableTaxDebugLogs; if (enableTaxDebugLogs != null && enableTaxDebugLogs.Value) { Plugin.Log.LogInfo((object)message); } } private bool SpawnRootTotemWorldObject(RotTotem totem) { //IL_0023: Unknown result type (might be due to invalid IL or missing references) //IL_002e: Expected O, but got Unknown //IL_0043: Unknown result type (might be due to invalid IL or missing references) //IL_0048: Unknown result type (might be due to invalid IL or missing references) if (totem == null || (Object)(object)ZNetScene.instance == (Object)null) { return false; } GameObject prefab = ZNetScene.instance.GetPrefab("Fehu_RotTotem"); if ((Object)prefab == (Object)null) { Plugin.Log.LogError((object)"Root Totem placeholder prefab is missing: Fehu_RotTotem"); return false; } GameObject val = Object.Instantiate(prefab, totem.WorldPosition, Quaternion.identity); ZNetView component = val.GetComponent(); ZDO val2 = (((Object)(object)component != (Object)null) ? component.GetZDO() : null); if (val2 == null) { Object.Destroy((Object)(object)val); Plugin.Log.LogError((object)"Root Totem placeholder has no valid ZNetView/ZDO: Fehu_RotTotem"); return false; } val2.Set("Fehu_RotTotem_TotemId", totem.TotemId); val2.Set("FCW_RootTotemClanId", totem.PlacerClanId); val2.Set("FCW_RootTotemGridX", totem.TerritoryGridX); val2.Set("FCW_RootTotemGridY", totem.TerritoryGridY); return true; } public void DestroyRootTotem(int totemId) { //IL_003f: Unknown result type (might be due to invalid IL or missing references) if (totemId <= 0) { return; } RotTotem rotTotem = Data.RotTotems.Find((RotTotem t) => t != null && t.TotemId == totemId); if (rotTotem != null) { ClanTerritory territory = Data.GetTerritory(rotTotem.GridPosition); Data.RotTotems.Remove(rotTotem); Data.PendingRootLoot.RemoveAll((PendingRootLoot loot) => loot.TotemId == totemId && loot.Amount <= 0); int num = 0; if (territory != null) { num = (territory.RotCount = GetActiveRootTotems(territory).Count); territory.State = ((num >= 4) ? TerritoryState.Rooted : ((num > 0) ? TerritoryState.Contested : TerritoryState.Normal)); } MarkDirty(); WarfareRPC.Instance?.SyncAllDataToClients(); string text = (string.IsNullOrWhiteSpace(territory?.DisplayName) ? "the affected territory" : territory.DisplayName); string description = "The Rot Totem in " + text + " has been destroyed. The land begins to recover."; NotificationManager.Send(NotificationCategory.Territory, "", description, null, null, text); Plugin.Log.LogInfo((object)("Root Totem destroyed: id=" + totemId + ", rootClan=" + rotTotem.PlacerClanId + ", territory=" + text + ", corruption=" + num + "/4.")); } } public string RegisterPhysicalRotTotem(long playerId, Vector3 worldPos, ZNetView view) { //IL_006c: Unknown result type (might be due to invalid IL or missing references) //IL_006d: Unknown result type (might be due to invalid IL or missing references) //IL_0072: Unknown result type (might be due to invalid IL or missing references) //IL_0079: Unknown result type (might be due to invalid IL or missing references) //IL_00d2: 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_00d8: Unknown result type (might be due to invalid IL or missing references) //IL_00dc: Unknown result type (might be due to invalid IL or missing references) //IL_00e7: Unknown result type (might be due to invalid IL or missing references) //IL_00e8: Unknown result type (might be due to invalid IL or missing references) //IL_017b: 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_01c2: Unknown result type (might be due to invalid IL or missing references) //IL_01c3: 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_0208: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)view == (Object)null || view.GetZDO() == null) { return "Rot Totem network state is unavailable."; } ZDO zDO = view.GetZDO(); if (zDO.GetInt("Fehu_RotTotem_TotemId", 0) > 0) { return "Rot Totem already registered."; } PlayerClanInfo player = Data.GetPlayer(playerId); if (player == null || player.ClanId == 0) { return "You are not in a clan."; } bool num = RoleUtils.HasRole(player, ClanRole.Shaman); bool flag = IsJarl(player); if (!num && !flag) { return "Only Shamans or Jarls may place Rot Totems."; } Vector2i val = GridExtensions.WorldToGrid(worldPos); ClanTerritory territory = Data.GetTerritory(val); if (territory == null || territory.OwnerClanId == 0) { return "Rot Totems require a valid enemy territory."; } if (territory.OwnerClanId == player.ClanId) { return "You cannot root your own clan's land."; } if (IsHostileActionBlocked(player.ClanId, territory.OwnerClanId, out var message)) { return "" + message + ""; } Vector3 val2 = val.ToWorldCenter(); val2.y = worldPos.y; if (Vector3.Distance(worldPos, val2) > 100f) { return "A Rot Totem must be placed within 100 metres of the territory centre."; } int num2 = Mathf.Clamp(ClanConfig.MaxRotTotemsPerTerritory?.Value ?? 4, 1, 4); int count = GetActiveRootTotems(territory).Count; if (count >= num2) { return $"Rot Totem limit reached ({count}/{num2})."; } long num3 = Now(); RotTotem rotTotem = new RotTotem { TotemId = Data.NextRotTotemId++, PrefabName = "Fehu_RotTotem", TerritoryGridX = val.x, TerritoryGridY = val.y, PlacedByPlayerId = playerId, PlacedByPlayerName = player.PlayerName, PlacerClanId = player.ClanId, PlacedTimestamp = num3, LastDecayTimestamp = num3, WorldPosition = worldPos, State = TotemState.Active, Level = 1 }; Data.RotTotems.Add(rotTotem); zDO.Set("Fehu_RotTotem_TotemId", rotTotem.TotemId); zDO.Set("Fehu_RotTotem_TerritoryId", WarfareData.GridToKey(val.x, val.y)); zDO.Set("Fehu_RotTotem_OwnerClanId", player.ClanId); zDO.Set("Fehu_RotTotem_PlacedByPlayerId", playerId); zDO.Set("Fehu_RotTotem_PlacedTimestamp", rotTotem.PlacedTimestamp); zDO.Set("Fehu_RotTotem_Active", true); territory.RotCount = GetActiveRootTotems(territory).Count; territory.State = ((territory.RotCount < 4) ? TerritoryState.Contested : TerritoryState.Rooted); string text = (string.IsNullOrWhiteSpace(territory.DisplayName) ? "the affected territory" : territory.DisplayName); string text2 = (string.IsNullOrWhiteSpace(player.PlayerName) ? "A warrior" : player.PlayerName); Clan clan = Data.GetClan(player.ClanId); string text3 = (string.IsNullOrWhiteSpace(clan?.Name) ? "" : clan.Name); AddPersonalSagaEntry(player, "RootTotemPlaced", "Rot Totem Placed", "Raised a Rot Totem at " + text + ".", "Normal", "rottotemplaced:" + rotTotem.TotemId, player.ClanId, TerritorySagaId(territory), player.PlayerId, "", "", rotTotem.PlacedTimestamp, markDirty: false); MarkDirty(); MarkSharedDirtyAndSave(); WarfareRPC.Instance?.SyncAllDataToClients(); string description = (string.IsNullOrEmpty(text3) ? ("A Rot Totem has been raised by " + text2 + " in " + text + ". Corruption begins to spread.") : ("A Rot Totem has been raised by " + text2 + " of " + text3 + " in " + text + ". Corruption begins to spread.")); NotificationManager.Send(NotificationCategory.Territory, "", description, text2, text3, text); return "Rot Totem raised. Corruption is now " + territory.RotCount + "/4."; } public string PlaceRotTotem(long playerId, string playerName, Vector3 worldPos) { return "Place the Rot Totem through the Hammer build menu."; } public string PlaceBounty(long actorId, long targetPlayerId, int targetClanId, long amount) { ConfigEntry bountySystemEnabled = ClanConfig.BountySystemEnabled; if (bountySystemEnabled == null || !bountySystemEnabled.Value) { return "The bounty system is disabled."; } PlayerClanInfo player = Data.GetPlayer(actorId); if (player == null || (player.ClanId == 0 && !player.IsFreeborn)) { return "You are not in a clan or Freeborn."; } if (player.ClanId != 0 && player.Rank < ClanRank.Officer) { return "Only Officers and the Jarl may place bounties."; } if (amount <= 0) { return "Bounty must be positive."; } Clan clan = Data.GetClan(player.ClanId); if (player.IsFreeborn && player.ClanId == 0) { if (player.FreebornTreasury < amount) { return $"Freeborn treasury needs {amount} coins (has {player.FreebornTreasury})."; } } else if (clan == null || clan.Treasury < amount) { return $"Treasury needs {amount} coins (has {clan?.Treasury ?? 0})."; } if (targetPlayerId != 0L) { PlayerClanInfo player2 = Data.GetPlayer(targetPlayerId); if (player2 == null) { return "Unknown target player."; } if (player.IsFreeborn && player.ClanId == 0) { player.FreebornTreasury -= amount; } else { ChangeClanTreasury(clan, -amount, "Purchase", "Bounty placement"); } player2.Bounty += amount; MarkDirty(); WarfareRPC.Instance?.BroadcastGlobalMessage($"A {player2.Bounty}-coin bounty hangs over {player2.PlayerName}!"); return "Bounty placed on " + player2.PlayerName + "."; } if (targetClanId != 0) { Clan clan2 = Data.GetClan(targetClanId); if (clan2 == null) { return "Unknown target clan."; } if (player.IsFreeborn && player.ClanId == 0) { player.FreebornTreasury -= amount; } else { ChangeClanTreasury(clan, -amount, "Purchase", "Bounty placement"); } clan2.Bounty += amount; MarkDirty(); WarfareRPC.Instance?.BroadcastGlobalMessage($"A {clan2.Bounty}-coin bounty hangs over clan {clan2.Name}!"); return "Bounty placed on " + clan2.Name + "."; } return "No bounty target given."; } public void RegisterIllegalKill(long killerId) { ConfigEntry bountySystemEnabled = ClanConfig.BountySystemEnabled; if (bountySystemEnabled != null && bountySystemEnabled.Value) { PlayerClanInfo player = Data.GetPlayer(killerId); if (player != null) { player.Bounty += ClanConfig.AutoBountyIllegalKill?.Value ?? 250; MarkDirty(); } } } public void CollectBounty(long killerId, long victimId) { ConfigEntry bountySystemEnabled = ClanConfig.BountySystemEnabled; if (bountySystemEnabled == null || !bountySystemEnabled.Value) { return; } PlayerClanInfo player = Data.GetPlayer(killerId); PlayerClanInfo player2 = Data.GetPlayer(victimId); if (player == null || player2 == null || player2.Bounty <= 0) { return; } if (player.ClanId != 0) { Clan clan = Data.GetClan(player.ClanId); if (clan != null) { ChangeClanTreasury(clan, player2.Bounty, "Reward", "Bounty claimed"); } } else if (player.IsFreeborn) { player.FreebornTreasury += player2.Bounty; } WarfareRPC.Instance?.BroadcastGlobalMessage($"{player.PlayerName} claimed the {player2.Bounty}-coin bounty on {player2.PlayerName}!"); player.BountiesClaimed++; AddPersonalSagaEntry(player, "BountyClaimed", "Bounty Claimed", "Claimed a bounty on " + (player2.PlayerName ?? "a target") + ".", "Normal", "bounty:" + victimId + ":" + player.BountiesClaimed, player.ClanId, 0, victimId, "", "", Now(), markDirty: false); player2.Bounty = 0L; MarkDirty(); } public string StartVaultCrack(long playerId, string playerName, Vector2i grid, Vector3 pos) { //IL_0065: Unknown result type (might be due to invalid IL or missing references) //IL_012a: Unknown result type (might be due to invalid IL or missing references) //IL_0136: 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) //IL_014f: Unknown result type (might be due to invalid IL or missing references) PlayerClanInfo player = Data.GetPlayer(playerId); if (player == null || player.ClanId == 0) { return "You are not in a clan."; } if (!RoleUtils.HasRole(player, ClanRole.Raider)) { return "Only Raiders can crack vaults!"; } if (!player.CanCrackVault(ClanConfig.VaultCrackCooldownHours?.Value ?? 6)) { return "Vault crack is on cooldown."; } ClanTerritory territory = Data.GetTerritory(grid); if (territory == null || territory.OwnerClanId == 0 || territory.OwnerClanId == player.ClanId) { return "You must be in enemy territory."; } if (IsHostileActionBlocked(player.ClanId, territory.OwnerClanId, out var message)) { return "" + message + ""; } if (IsProtectedByOdinsPeace(territory)) { return "Odin's Peace protects this Homestead."; } if (Data.ActiveCracks.Exists((VaultCrackSession c) => c.PlayerId == playerId)) { return "You are already cracking a vault."; } int num = ClanConfig.VaultCrackDurationMinutes?.Value ?? 30; VaultCrackSession item = new VaultCrackSession { PlayerId = playerId, PlayerName = playerName, PlayerClanId = player.ClanId, TargetClanId = territory.OwnerClanId, TerritoryGridX = grid.x, TerritoryGridY = grid.y, StartTimestamp = Now(), StartPosition = pos, DurationMinutes = num }; Data.ActiveCracks.Add(item); player.LastVaultCrackTimestamp = Now(); MarkDirty(); Clan clan = Data.GetClan(territory.OwnerClanId); WarfareRPC.Instance?.BroadcastGlobalMessage("" + playerName + " is cracking the vault in " + clan?.Name + "'s " + territory.DisplayName + "!"); return $"Vault crack started \ufffd hold the ground for {num} minutes!"; } } }