using System; using System.Collections; using System.Collections.Generic; using System.Diagnostics; using System.Globalization; using System.IO; using System.Linq; using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.Versioning; using System.Security; using System.Security.Permissions; using System.Text; 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 UnityEngine; 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"; private Harmony _harmony; private GameObject _managerObj; public static Plugin Instance { get; private set; } public static ManualLogSource Log { get; private set; } private void Awake() { //IL_0022: Unknown result type (might be due to invalid IL or missing references) //IL_002c: Expected O, but got Unknown //IL_006c: Unknown result type (might be due to invalid IL or missing references) //IL_0076: Expected O, but got Unknown //IL_007c: Unknown result type (might be due to invalid IL or missing references) //IL_0086: Expected O, but got Unknown //IL_009e: Unknown result type (might be due to invalid IL or missing references) //IL_00a4: Invalid comparison between Unknown and I4 Instance = this; Log = ((BaseUnityPlugin)this).Logger; 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 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; 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_0332: Unknown result type (might be due to invalid IL or missing references) //IL_0340: Expected O, but got Unknown //IL_0465: Unknown result type (might be due to invalid IL or missing references) //IL_0472: Expected O, but got Unknown //IL_0184: Unknown result type (might be due to invalid IL or missing references) //IL_059d: Unknown result type (might be due to invalid IL or missing references) //IL_05a2: Unknown result type (might be due to invalid IL or missing references) //IL_05ab: Expected O, but got Unknown //IL_01b6: Unknown result type (might be due to invalid IL or missing references) //IL_01bd: Expected O, but got Unknown //IL_01e7: Unknown result type (might be due to invalid IL or missing references) //IL_01ee: 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 FindPortalListMethods(assembly)) { try { HarmonyMethod val = ((!HasMutablePortalListArg(item2)) ? ((HarmonyMethod)null) : new HarmonyMethod(typeof(TargetPortalFehuAdapter), "TargetPortalPortalListPrefix", (Type[])null)); HarmonyMethod val2 = null; if (item2.ReturnType == typeof(List)) { val2 = new HarmonyMethod(typeof(TargetPortalFehuAdapter), "TargetPortalPortalListPostfix", (Type[])null); } else if (item2.ReturnType == typeof(IEnumerable)) { val2 = new HarmonyMethod(typeof(TargetPortalFehuAdapter), "TargetPortalPortalEnumerablePostfix", (Type[])null); } if (val != null || val2 != null) { _harmony.Patch((MethodBase)item2, val, val2, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null); num++; LogOnce("patched-list:" + item2.DeclaringType?.FullName + "." + item2.Name, "[FCW TargetPortal] Patched real TargetPortal method: " + item2.DeclaringType?.FullName + "." + item2.Name); } } catch (Exception ex2) { LogOnce("patchlistfail:" + item2.DeclaringType?.FullName + "." + item2.Name, "[FCW TargetPortal] Portal list patch failed for " + item2.DeclaringType?.FullName + "." + item2.Name + ": " + ex2.Message); } } foreach (MethodInfo item3 in FindToggleModeMethods(assembly)) { try { _harmony.Patch((MethodBase)item3, new HarmonyMethod(typeof(TargetPortalFehuAdapter), "TargetPortalToggleModePrefix", (Type[])null), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null); num++; LogOnce("patched-toggle:" + item3.DeclaringType?.FullName + "." + item3.Name, "[FCW TargetPortal] Patched real TargetPortal method: " + item3.DeclaringType?.FullName + "." + item3.Name); } catch (Exception ex3) { LogOnce("patchtogglefail:" + item3.DeclaringType?.FullName + "." + item3.Name, "[FCW TargetPortal] Mode toggle patch failed for " + item3.DeclaringType?.FullName + "." + item3.Name + ": " + ex3.Message); } } foreach (MethodInfo item4 in FindHoverTextMethods(assembly)) { try { _harmony.Patch((MethodBase)item4, (HarmonyMethod)null, new HarmonyMethod(typeof(TargetPortalFehuAdapter), "TargetPortalHoverTextPostfix", (Type[])null), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null); num++; LogOnce("patched-hover:" + item4.DeclaringType?.FullName + "." + item4.Name, "[FCW TargetPortal] Patched real TargetPortal method: " + item4.DeclaringType?.FullName + "." + item4.Name); } catch (Exception ex4) { LogOnce("patchhoverfail:" + item4.DeclaringType?.FullName + "." + item4.Name, "[FCW TargetPortal] Hover patch failed for " + item4.DeclaringType?.FullName + "." + item4.Name + ": " + ex4.Message); } } try { MethodInfo methodInfo = AccessTools.Method(typeof(TeleportWorld), "GetHoverText", Type.EmptyTypes, (Type[])null); if (methodInfo != null) { HarmonyMethod val3 = new HarmonyMethod(typeof(TargetPortalFehuAdapter), "TargetPortalFinalHoverPostfix", (Type[])null) { priority = 0 }; _harmony.Patch((MethodBase)methodInfo, (HarmonyMethod)null, val3, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null); num++; LogOnce("patched-final-hover", "[FCW TargetPortal] Patched final TeleportWorld.GetHoverText display path after TargetPortal."); } } catch (Exception ex5) { LogOnce("patch-final-hover-fail", "[FCW TargetPortal] Final hover display patch failed: " + ex5.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_0214: 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_0222: 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); } try { MethodInfo methodInfo6 = AccessTools.Method(typeof(ZDOMan), "GetPortals", (Type[])null, (Type[])null); if (methodInfo6 != null) { HarmonyMethod val = new HarmonyMethod(typeof(TargetPortalFehuAdapter), "TargetPortalPortalListPostfix", (Type[])null) { priority = 0 }; _harmony.Patch((MethodBase)methodInfo6, (HarmonyMethod)null, val, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null); num++; LogOnce("patched-zdoman-portals", "[FCW TargetPortal] Patched ZDOMan.GetPortals for FCW Clan visibility filtering."); } } catch (Exception ex2) { LogOnce("patch-zdoman-portals-fail", "[FCW TargetPortal] Portal list visibility patch failed: " + ex2.Message); } return num; } private static int PatchTargetPortalMapAccess(Assembly assembly) { //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_0054: Expected O, but got Unknown //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_0080: Expected O, but got Unknown //IL_00c0: Unknown result type (might be due to invalid IL or missing references) //IL_00c5: Unknown result type (might be due to invalid IL or missing references) //IL_00d9: Expected O, but got Unknown int num = 0; try { Type? type = assembly.GetType("TargetPortal.Map", throwOnError: false); MethodInfo methodInfo = AccessTools.Method(type, "AddPortalPins", (Type[])null, (Type[])null); if (methodInfo != null) { _harmony.Patch((MethodBase)methodInfo, new HarmonyMethod(typeof(TargetPortalFehuAdapter), "TargetPortalMapAddPortalPinsPrefix", (Type[])null) { priority = 800 }, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null); _harmony.Patch((MethodBase)methodInfo, (HarmonyMethod)null, new HarmonyMethod(typeof(TargetPortalFehuAdapter), "TargetPortalMapAddPortalPinsPostfix", (Type[])null) { priority = 0 }, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null); num++; LogOnce("patched-map-add-pins", "[FCW TargetPortal] Patched TargetPortal.Map.AddPortalPins after native visibility filtering."); } MethodInfo methodInfo2 = AccessTools.Method(type, "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 void TargetPortalMapAddPortalPinsPrefix() { if (_enabled && IsLocalPortalMapClient()) { LogMapStage("raw", GetKnownTargetPortalPortals(), null, "before native TargetPortal filtering"); } } public unsafe static void TargetPortalMapAddPortalPinsPostfix() { //IL_00bc: 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_016d: 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_01bf: Unknown result type (might be due to invalid IL or missing references) //IL_027e: 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) try { if (!_enabled || (Object)(object)ZNet.instance == (Object)null || !IsLocalPortalMapClient() || ZDOMan.instance == null || (Object)(object)Minimap.instance == (Object)null) { return; } Type type = FindTargetPortalAssembly()?.GetType("TargetPortal.Map", throwOnError: false); object obj = AccessTools.Field(type, "activePins")?.GetValue(null); if (obj == null) { return; } LogMapStage("afterNative", null, obj, "after TargetPortal native Group/Guild gate"); LogMapStage("afterGuild", null, obj, "native Group/Guild gate shares AddPortalPins collection"); HashSet hashSet = new HashSet(); foreach (object item in EnumerateDictionaryEntries(obj)) { object dictionaryEntryValue = GetDictionaryEntryValue(item); ZDO val = (ZDO)((dictionaryEntryValue is ZDO) ? dictionaryEntryValue : null); if (val != null) { hashSet.Add(val.m_uid); } } foreach (ZDO receivedZdo in GetReceivedZdos()) { if (!IsFcwVisibilityOverridePortal(receivedZdo) || hashSet.Contains(receivedZdo.m_uid)) { continue; } if (!CanCurrentPlayerSeePortal(receivedZdo, out var access, out var reason)) { LogPortalDecision("LIST", receivedZdo, allowed: false, reason + " finalAdded=false"); LogClanPortalTrace(receivedZdo, access, finalVisible: false, "afterFCWRestore", reason); continue; } object obj2 = AddTargetPortalMapPin(receivedZdo, type); if (obj2 != null) { AddDictionaryEntry(obj, obj2, receivedZdo); hashSet.Add(receivedZdo.m_uid); LogPortalDecision("LIST", receivedZdo, allowed: true, reason + " finalAdded=true"); ManualLogSource log = Plugin.Log; string[] obj3 = new string[6] { "[FCW TargetPortal] Restored allowed ", access.AccessType, " portal pin portal=", null, null, null }; ZDOID uid = receivedZdo.m_uid; obj3[3] = ((object)(*(ZDOID*)(&uid))/*cast due to .constrained prefix*/).ToString(); obj3[4] = " reason="; obj3[5] = reason; log.LogInfo((object)string.Concat(obj3)); } else { LogPortalDecision("LIST", receivedZdo, allowed: true, reason + " finalAdded=false"); } } LogMapStage("afterFCWRestore", null, obj, "after FCW Clan/private restoration"); LogMapStage("finalUI", null, obj, "TargetPortal.Map.activePins plus Minimap pin registry"); foreach (ZDO item2 in from portal in GetReceivedZdos() where portal != null && string.Equals(portal.GetString("FCW_TargetPortalMode", ""), "Clan", StringComparison.OrdinalIgnoreCase) select portal) { bool finalVisible = obj != null && ContainsPortal(obj, item2.m_uid) && IsInMinimapPinRegistry(obj, item2.m_uid); CanCurrentPlayerSeePortal(item2, out var access2, out var reason2); LogClanPortalTrace(item2, access2, finalVisible, "finalUI", reason2); } } catch (Exception ex) { LogOnce("map-add-pins-postfix:" + ex.GetType().Name, "[FCW TargetPortal] Map pin restore failed: " + ex.Message); } } 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); string reason; bool flag = HasPortalAccess(val, playerId, player, out reason); LogPortalDecision("USE", val, flag, reason); if (flag) { return true; } WarfareHUD.ShowLeftNotification("Portal access denied: " + reason + "."); __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_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); 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_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); 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_0013: Unknown result type (might be due to invalid IL or missing references) //IL_001e: Expected O, but got Unknown //IL_0094: Unknown result type (might be due to invalid IL or missing references) try { Character obj = ((hit != null) ? hit.GetAttacker() : null); Player val = (Player)(object)((obj is Player) ? obj : null); if ((Object)val != (Object)null) { return val.GetPlayerID(); } Player[] array = Object.FindObjectsOfType(); foreach (Player val2 in array) { ZDO val3 = TryGetZdo(val2); if (val3 != null && GetZdoUserId(val3) == sender) { return val2.GetPlayerID(); } } ZNet instance = ZNet.instance; ZNetPeer val4 = ((instance != null) ? instance.GetPeer(sender) : null); ZDO obj2 = ((val4 != null && ZDOMan.instance != null) ? ZDOMan.instance.GetZDO(val4.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_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_00da: Unknown result type (might be due to invalid IL or missing references) //IL_00e5: 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) { 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); 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; } } 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) { RestoreAllowedClanPortals(__result, "TargetPortal." + (__originalMethod?.Name ?? "unknown") + ".restore"); 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_00b7: Unknown result type (might be due to invalid IL or missing references) try { object obj = AccessTools.Field(mapType?.Assembly.GetType("TargetPortal.AddMinimapPortalIcon", throwOnError: false), "pinType")?.GetValue(null); MethodInfo methodInfo = typeof(Minimap).GetMethods(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic).FirstOrDefault((MethodInfo method) => method.Name == "AddPin" && method.GetParameters().Length == 7 && method.ReturnType != typeof(void)); if (methodInfo == null || obj == null) { return null; } ParameterInfo[] parameters = methodInfo.GetParameters(); object[] array = new object[parameters.Length]; for (int num = 0; num < parameters.Length; num++) { Type parameterType = parameters[num].ParameterType; if (num == 0 && parameterType == typeof(Vector3)) { array[num] = GetPortalPosition(portal); } else if (num == 1 && parameterType.IsEnum) { array[num] = Enum.ToObject(parameterType, Convert.ToInt32(obj)); } else if (parameterType == typeof(string)) { array[num] = portal.GetString("tag", ""); } else if (parameterType == typeof(bool)) { array[num] = false; } else if (parameterType == typeof(long)) { array[num] = 0L; } else if (parameters[num].HasDefaultValue) { array[num] = parameters[num].DefaultValue; } else { array[num] = (parameterType.IsValueType ? Activator.CreateInstance(parameterType) : null); } } return methodInfo.Invoke(Minimap.instance, array); } 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 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 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 (!(AccessTools.Field(FindTargetPortalAssembly()?.GetType("TargetPortal.TargetPortal", throwOnError: false), "knownPortals")?.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 class ClanNameTagRenderer : MonoBehaviour { private GUIStyle _tagStyle; private GUIStyle _shadowStyle; 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_0185: Unknown result type (might be due to invalid IL or missing references) //IL_018f: Expected O, but got Unknown //IL_018a: 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_0191: 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_01a8: 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_021c: 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_022a: 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_0253: 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) //IL_0283: Unknown result type (might be due to invalid IL or missing references) //IL_028a: Unknown result type (might be due to invalid IL or missing references) //IL_02ac: 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_02ba: Unknown result type (might be due to invalid IL or missing references) //IL_02d9: Unknown result type (might be due to invalid IL or missing references) //IL_02e5: Unknown result type (might be due to invalid IL or missing references) //IL_02f1: Unknown result type (might be due to invalid IL or missing references) //IL_02fd: 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_0312: 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) 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) { Vector3 val2 = ((Component)val).transform.position + Vector3.up * 2.05f; Vector3 val3 = main.WorldToScreenPoint(val2); if (!(val3.z <= 0f)) { float num2 = (float)Screen.height - val3.y; string text = clan.Symbol + " " + clan.Name; Vector2 val4 = _tagStyle.CalcSize(new GUIContent(text)); float num3 = val3.x - val4.x / 2f; float num4 = num2 - val4.y - 4f; float num5 = Mathf.Clamp01(1f - num / 80f); num5 = Mathf.Lerp(0.35f, 1f, num5); _shadowStyle.normal.textColor = new Color(0f, 0f, 0f, num5 * 0.85f); GUI.Label(new Rect(num3 + 1f, num4 + 1f, val4.x, val4.y), text, _shadowStyle); GUI.Label(new Rect(num3 - 1f, num4 + 1f, val4.x, val4.y), text, _shadowStyle); GUI.Label(new Rect(num3 + 1f, num4 - 1f, val4.x, val4.y), text, _shadowStyle); GUI.Label(new Rect(num3 - 1f, num4 - 1f, val4.x, val4.y), text, _shadowStyle); _tagStyle.normal.textColor = new Color(clan.Color.r, clan.Color.g, clan.Color.b, num5); GUI.Label(new Rect(num3, num4, val4.x, val4.y), text, _tagStyle); } } } } } 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, "EXPLORER", "Territory", "Discovers and claims wilderness territory.", "Claim unowned wilderness land.", "explorer", true), (ClanRole.Warden, "WARDEN", "Territory", "Manages and expands existing territory.", "Administration and stability.", "warden", true), (ClanRole.Raider, "RAIDER", "Warfare", "Offensive warfare specialist.", "Raid enemy lands and pressure vaults.", "raider", true), (ClanRole.Guardian, "GUARDIAN", "Warfare", "Defensive warfare specialist.", "Defend owned land and resist capture.", "guardian", true), (ClanRole.Builder, "BUILDER", "Infrastructure", "Construction and infrastructure specialist.", "Build, repair and upgrade efficiently.", "builder", true), (ClanRole.Gatherer, "GATHERER", "Economy", "Resource generation specialist.", "Gather more from useful land.", "gatherer", true), (ClanRole.Merchant, "MERCHANT", "Economy", "Legal economy and trade specialist.", "Strengthen lawful trade and coin flow.", "merchant", true), (ClanRole.Smuggler, "SMUGGLER", "Economy", "Illegal economy and hidden trade specialist.", "Move value through hidden routes.", "smuggler", true), (ClanRole.Thief, "THIEF", "Intelligence", "Espionage and intelligence specialist.", "Find secrets and pressure rivals.", "thief", true), (ClanRole.Shaman, "SHAMAN", "Spiritual", "Totems, rituals and clan support.", "Rituals against corruption and instability.", "shaman", true), (ClanRole.Druid, "DRUID", "Spiritual", "Healing, survival and animal management.", "Support survival and living-world systems.", "druid", true), (ClanRole.TwelfthPath, "THE TWELFTH PATH", "Community", "You can now hold up to three roles.", "Choose primary now; other paths later.", "TwelfthPath", false), (ClanRole.TwelfthPath, "COMMUNITY WARFARE ROLE", "Community", "Help choose a future Warfare role.", "", "TwelfthPath", false), (ClanRole.TwelfthPath, "COMMUNITY ECONOMY ROLE", "Community", "Help choose a future Economy role.", "", "TwelfthPath", false), (ClanRole.TwelfthPath, "COMMUNITY SUPPORT ROLE", "Community", "Help choose a future Support role.", "", "TwelfthPath", false), (ClanRole.TwelfthPath, "EMPTY FUTURE SLOT", "Community", "Reserved for future content.", "", "TwelfthPath", false) }; 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() { return IsOpen; } 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 + ", showing overview")); CloseClanSelectionState(); } 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_0304: Expected O, but got Unknown //IL_0351: Unknown result type (might be due to invalid IL or missing references) //IL_0362: 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_037c: Unknown result type (might be due to invalid IL or missing references) //IL_0384: 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_0393: Expected O, but got Unknown //IL_0398: Expected O, but got Unknown //IL_03b9: Unknown result type (might be due to invalid IL or missing references) //IL_03ce: Unknown result type (might be due to invalid IL or missing references) //IL_03d3: Unknown result type (might be due to invalid IL or missing references) //IL_03db: Unknown result type (might be due to invalid IL or missing references) //IL_03e2: Unknown result type (might be due to invalid IL or missing references) //IL_03ee: Expected O, but got Unknown //IL_03f9: 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.09f, 0.07f, 0.045f, 0.98f), new Color(0.05f, 0.04f, 0.025f, 0.99f)); _cardTex = MakeGradientTex(32, 32, new Color(0.16f, 0.12f, 0.07f, 0.96f), new Color(0.1f, 0.08f, 0.04f, 0.97f)); _cardHoverTex = MakeGradientTex(32, 32, new Color(0.26f, 0.19f, 0.09f, 0.97f), new Color(0.16f, 0.12f, 0.05f, 0.98f)); _fieldTex = MakeTex(new Color(0.03f, 0.025f, 0.018f, 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(12, 12, 10, 10); val.margin = new RectOffset(4, 4, 4, 4); _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(14, 14, 8, 8) }; _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 = 16, padding = new RectOffset(8, 8, 6, 6) }; _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(); return role switch { ClanRole.Guardian => new string[2] { text, "shieldbearer" }, ClanRole.TwelfthPath => new string[2] { text, "new" }, _ => new string[1] { text }, }; } 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 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_02be: 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_0373: 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($"Rot: {territory.RotCount}/4 — {territory.RotCount * 25}% income lost", _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_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) //IL_015e: 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_0167: 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_0176: 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) 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; } ConfigEntry requireExplorerForClaim = ClanConfig.RequireExplorerForClaim; if (requireExplorerForClaim != null && requireExplorerForClaim.Value && !RoleUtils.HasRole(playerClanInfo, ClanRole.Explorer) && !playerClanInfo.IsFreeborn) { GUILayout.Label("Only Explorers can claim 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 sealed class LeftNotification { public string Message; public string Key; public float CreatedAt; public int RepeatCount = 1; } 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 Texture2D Icon; public VaultInventoryEntry(string prefabName, string displayName, int amount, int slotX, int slotY, Texture2D 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 string _profileAboutDraft = ""; private Dictionary _inventoryTextures; private Dictionary _profileTextures; 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 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 _emblemUrlInput = ""; private int _emblemUrlClanId; private bool _editingClanEmblem; private bool _editingClanLinks; 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 ClanRole _selectedRolePreview = ClanRole.Explorer; private int _selectedRolePreviewIndex; private int _contractsTab; private int _overviewSubTab; private int _profileSubTab; private int _territorySubTab; private int _economySubTab; private int _rankingsSubTab; private int _clansSubTab; private int _homesteadsSubTab; private int _historySubTab; private int _faqSubTab; private int _sagaSubTab; private int _gamesSubTab; private string _diceTableName = "Dice Table"; private string _diceStakeInput = "0"; private int _diceStakeTypeIndex; private int _diceStakeItemIndex; private int _gameHistoryFilter; private string _chessMatchName = "Chess Match"; private string _chessStakeInput = "0"; 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 string _pokerTableName = "Texas Hold'em"; private string _pokerBuyInInput = "100"; private string _pokerSmallBlindInput = "1"; private string _pokerBigBlindInput = "2"; private string _pokerRaiseInput = "0"; 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 int _supportSubTab; 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 _buttonStyle; private GUIStyle _smallButtonStyle; private GUIStyle _tabStyle; private GUIStyle _tabActiveStyle; private GUIStyle _boxStyle; private Texture2D _panelBg; private Texture2D _btnTex; private Texture2D _btnActiveTex; private Texture2D _boxTex; private Texture2D _pixelTex; 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", "Support" }; private static readonly string[] FreebornTabs = new string[13] { "Overview", "Territories", "Economy", "Warfare", "Infrastructure", "Kingdoms", "Guards", "Contracts", "Rankings", "Clans", "Homesteads", "All IDs", "Members" }; 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 = "Explorer", Group = "Territory", IconName = "explorer", Lore = "Discovers and claims wilderness territory.", CoreAbility = "Claim unowned wilderness land.", PassiveBonuses = "Wilderness claim/support bonuses scale by role slot.", ClanValue = "Fast expansion and map control.", Selectable = true }, new RoleData { Role = ClanRole.Warden, DisplayName = "Warden", Group = "Territory", IconName = "warden", Lore = "Manages and expands existing territory.", CoreAbility = "Annexation, administration and stability.", PassiveBonuses = "Territory upkeep and stability tools scale by role slot.", ClanValue = "Keeps borders organized and resilient.", Selectable = true }, new RoleData { Role = ClanRole.Raider, DisplayName = "Raider", Group = "Warfare", IconName = "raider", Lore = "Offensive warfare specialist.", CoreAbility = "Raid enemy lands and pressure vaults.", PassiveBonuses = "Loot and offensive pressure scale by role slot.", ClanValue = "Turns wars into resources.", Selectable = true }, new RoleData { Role = ClanRole.Guardian, DisplayName = "Guardian", Group = "Warfare", IconName = "guardian", Lore = "Defensive warfare specialist, formerly Shieldbearer.", CoreAbility = "Defend owned land and resist capture.", PassiveBonuses = "Defense and capture resistance scale by role slot.", ClanValue = "Protects capitals, homesteads and frontlines.", Selectable = true }, new RoleData { Role = ClanRole.Builder, DisplayName = "Builder", Group = "Infrastructure", IconName = "builder", Lore = "Construction and infrastructure specialist.", CoreAbility = "Build, repair and upgrade more efficiently.", PassiveBonuses = "Build and upgrade discounts scale by role slot.", ClanValue = "Turns territory into strongholds.", Selectable = true }, new RoleData { Role = ClanRole.Gatherer, DisplayName = "Gatherer", Group = "Economy", IconName = "gatherer", Lore = "Resource generation specialist.", CoreAbility = "Gather more from useful land.", PassiveBonuses = "Resource and carry bonuses scale by role slot.", ClanValue = "Feeds treasury, vault and war supply.", Selectable = true }, new RoleData { Role = ClanRole.Merchant, DisplayName = "Merchant", Group = "Economy", IconName = "merchant", Lore = "Legal economy and trade specialist.", CoreAbility = "Strengthen lawful trade and coin flow.", PassiveBonuses = "Trade income bonuses scale by role slot.", ClanValue = "Stabilizes clan economy.", Selectable = true }, new RoleData { Role = ClanRole.Smuggler, DisplayName = "Smuggler", Group = "Economy", IconName = "smuggler", Lore = "Illegal economy and hidden trade specialist.", CoreAbility = "Move value through hidden routes.", PassiveBonuses = "Black-market and covert trade bonuses scale by role slot.", ClanValue = "Creates risky alternate income.", Selectable = true }, new RoleData { Role = ClanRole.Thief, DisplayName = "Thief", Group = "Intelligence", IconName = "thief", Lore = "Espionage and intelligence specialist.", CoreAbility = "Find secrets and pressure rivals.", PassiveBonuses = "Intel and infiltration bonuses scale by role slot.", ClanValue = "Turns knowledge into leverage.", Selectable = true }, new RoleData { Role = ClanRole.Shaman, DisplayName = "Shaman", Group = "Spiritual", IconName = "shaman", Lore = "Totems, rituals and clan support.", CoreAbility = "Use rituals against corruption and instability.", PassiveBonuses = "Ritual and corruption control bonuses scale by role slot.", ClanValue = "Supports long-term survival.", Selectable = true }, new RoleData { Role = ClanRole.Druid, DisplayName = "Druid", Group = "Spiritual", IconName = "druid", Lore = "Healing, survival and animal management.", CoreAbility = "Support survival and living-world systems.", PassiveBonuses = "Healing and animal bonuses scale by role slot.", ClanValue = "Keeps people alive away from home.", Selectable = true }, new RoleData { Role = ClanRole.TwelfthPath, DisplayName = "The Twelfth Path", Group = "Community", IconName = "TwelfthPath", Lore = "You can now hold up to three roles. Choose your primary path now.", CoreAbility = "Secondary and tertiary paths can be chosen later.", PassiveBonuses = "Community placeholder.", ClanValue = "Future community role.", Selectable = false }, new RoleData { Role = ClanRole.TwelfthPath, DisplayName = "Community Warfare Role", Group = "Community", IconName = "TwelfthPath", Lore = "Help choose a future Warfare role.", CoreAbility = "Future slot.", PassiveBonuses = "Community placeholder.", ClanValue = "Future community role.", Selectable = false }, new RoleData { Role = ClanRole.TwelfthPath, DisplayName = "Community Economy Role", Group = "Community", IconName = "TwelfthPath", Lore = "Help choose a future Economy role.", CoreAbility = "Future slot.", PassiveBonuses = "Community placeholder.", ClanValue = "Future community role.", Selectable = false }, new RoleData { Role = ClanRole.TwelfthPath, DisplayName = "Community Support Role", Group = "Community", IconName = "TwelfthPath", Lore = "Help choose a future Support role.", CoreAbility = "Future slot.", PassiveBonuses = "Community placeholder.", ClanValue = "Future community role.", Selectable = false }, new RoleData { Role = ClanRole.TwelfthPath, DisplayName = "Empty Future Slot", Group = "Community", IconName = "TwelfthPath", Lore = "Reserved for future content.", CoreAbility = "Future slot.", PassiveBonuses = "Community placeholder.", ClanValue = "Reserved.", Selectable = false } }; public static WarfareHUD Instance { get; private set; } private static float UIScale => (float)Screen.height / 1080f; private static string RuntimeAssetRoot => Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location) ?? Paths.PluginPath; private void Awake() { //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) Instance = this; _windowId = ((Object)this).GetInstanceID(); 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); _windowRect = new Rect((num - num3) / 2f, (num2 - num4) / 2f, num3, num4); } public void Toggle() { _isVisible = !_isVisible; } private void Update() { EnsureNotificationSubscription(); if (Input.GetKeyDown((KeyCode)284)) { _isVisible = !_isVisible; } } private void EnsureNotificationSubscription() { if (!_notificationSubscribed && !((Object)(object)WarfareRPC.Instance == (Object)null)) { WarfareRPC.Instance.OnNotificationReceived += PushLeftNotification; _notificationSubscribed = true; } } private void OnDestroy() { if (_notificationSubscribed && (Object)(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 message2 = ClarifyNotificationText(message); string text = NormalizeNotificationKey(message2); for (int i = 0; i < _leftNotifications.Count; i++) { LeftNotification leftNotification = _leftNotifications[i]; if (leftNotification.Key == text) { leftNotification.RepeatCount++; leftNotification.CreatedAt = time; if (i > 0) { _leftNotifications.RemoveAt(i); _leftNotifications.Insert(0, leftNotification); } return; } } _leftNotifications.Insert(0, new LeftNotification { Message = message2, Key = text, 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) { if (clanId > 0) { _isVisible = true; _tab = 0; Plugin.Log.LogInfo((object)("[FCW UI] Opening clan overview for ClanId=" + clanId)); } } public void ResetClientState() { //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_00ae: 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_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: 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_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_00da: 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_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_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_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_0106: 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_0111: 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_011c: Unknown result type (might be due to invalid IL or missing references) //IL_0121: Unknown result type (might be due to invalid IL or missing references) //IL_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_0132: 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_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_0148: 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_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_015e: 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_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_0174: 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_017f: 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) _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; _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_002c: 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_006e: 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_00b0: 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_00ee: 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_0119: Unknown result type (might be due to invalid IL or missing references) //IL_0123: Expected O, but got Unknown //IL_015d: Unknown result type (might be due to invalid IL or missing references) //IL_0167: Expected O, but got Unknown //IL_0175: Unknown result type (might be due to invalid IL or missing references) //IL_017f: Expected O, but got Unknown //IL_018a: 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_0197: 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_01aa: Expected O, but got Unknown //IL_01c4: 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_01da: Unknown result type (might be due to invalid IL or missing references) //IL_01e2: Unknown result type (might be due to invalid IL or missing references) //IL_01ee: Expected O, but got Unknown //IL_0208: 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_0227: Expected O, but got Unknown //IL_0283: Unknown result type (might be due to invalid IL or missing references) //IL_02a7: 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_02e8: Unknown result type (might be due to invalid IL or missing references) //IL_02f2: Expected O, but got Unknown //IL_02f9: Unknown result type (might be due to invalid IL or missing references) //IL_0303: Expected O, but got Unknown //IL_0333: Unknown result type (might be due to invalid IL or missing references) //IL_0348: Unknown result type (might be due to invalid IL or missing references) //IL_0352: Expected O, but got Unknown //IL_03ae: 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_040d: Unknown result type (might be due to invalid IL or missing references) //IL_0417: Expected O, but got Unknown //IL_0421: Unknown result type (might be due to invalid IL or missing references) //IL_042b: Expected O, but got Unknown //IL_0432: 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_043f: 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_044b: Unknown result type (might be due to invalid IL or missing references) //IL_0455: Expected O, but got Unknown //IL_045a: Expected O, but got Unknown //IL_0465: Unknown result type (might be due to invalid IL or missing references) //IL_046f: Expected O, but got Unknown //IL_048f: Unknown result type (might be due to invalid IL or missing references) //IL_0499: Expected O, but got Unknown //IL_04a3: Unknown result type (might be due to invalid IL or missing references) //IL_04ad: Expected O, but got Unknown if (!_stylesInit) { _stylesInit = true; _panelBg = MakeGradientTex(32, 32, new Color(0.08f, 0.06f, 0.04f, 0.95f), new Color(0.05f, 0.04f, 0.03f, 0.97f)); _btnTex = MakeGradientTex(16, 16, new Color(0.18f, 0.14f, 0.08f, 0.9f), new Color(0.12f, 0.09f, 0.05f, 0.92f)); _btnActiveTex = MakeGradientTex(16, 16, new Color(0.32f, 0.23f, 0.1f, 0.95f), new Color(0.2f, 0.15f, 0.06f, 0.97f)); _boxTex = MakeTex(new Color(0.1f, 0.08f, 0.05f, 0.7f)); _pixelTex = MakeTex(Color.white); _windowStyle = new GUIStyle(GUI.skin.window); _windowStyle.normal.background = _panelBg; _windowStyle.onNormal.background = _panelBg; _windowStyle.border = new RectOffset(12, 12, 45, 12); _windowStyle.padding = new RectOffset(18, 18, 50, 16); _labelStyle = new GUIStyle(GUI.skin.label) { fontSize = 13, richText = true, wordWrap = true }; _labelStyle.normal.textColor = new Color(0.82f, 0.78f, 0.68f); _labelGoldStyle = new GUIStyle(_labelStyle) { fontSize = 14, fontStyle = (FontStyle)1 }; _labelGoldStyle.normal.textColor = new Color(0.92f, 0.8f, 0.36f); _tabStyle = new GUIStyle(GUI.skin.button); _tabStyle.normal.background = _btnTex; _tabStyle.hover.background = _btnActiveTex; _tabStyle.active.background = _btnActiveTex; _tabStyle.normal.textColor = new Color(0.75f, 0.68f, 0.52f); _tabStyle.hover.textColor = new Color(0.97f, 0.87f, 0.6f); _tabStyle.fontSize = 12; _tabStyle.fontStyle = (FontStyle)1; _tabStyle.padding = new RectOffset(6, 6, 7, 7); _tabStyle.margin = new RectOffset(2, 2, 0, 0); _tabActiveStyle = new GUIStyle(_tabStyle); _tabActiveStyle.normal.background = _btnActiveTex; _tabActiveStyle.normal.textColor = new Color(1f, 0.9f, 0.46f); _buttonStyle = new GUIStyle(GUI.skin.button); _buttonStyle.normal.background = _btnTex; _buttonStyle.hover.background = _btnActiveTex; _buttonStyle.active.background = _btnActiveTex; _buttonStyle.normal.textColor = new Color(0.92f, 0.84f, 0.62f); _buttonStyle.hover.textColor = new Color(1f, 0.96f, 0.82f); _buttonStyle.fontSize = 13; _buttonStyle.fontStyle = (FontStyle)1; _buttonStyle.richText = true; _buttonStyle.padding = new RectOffset(10, 10, 6, 6); _buttonStyle.margin = new RectOffset(0, 0, 3, 3); _smallButtonStyle = new GUIStyle(_buttonStyle) { fontSize = 11, richText = true, padding = new RectOffset(7, 7, 4, 4) }; _boxStyle = new GUIStyle(GUI.skin.box); _boxStyle.normal.background = _boxTex; _boxStyle.padding = new RectOffset(8, 8, 6, 6); _boxStyle.margin = new RectOffset(0, 0, 3, 3); } } 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 OnGUI() { //IL_0005: Unknown result type (might be due to invalid IL or missing references) //IL_0010: Expected O, but got Unknown //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_0028: 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_0039: 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_0071: Unknown result type (might be due to invalid IL or missing references) //IL_0086: Expected O, but got Unknown //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) if (!((Object)Player.m_localPlayer == (Object)null)) { 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) { _windowRect = GUI.Window(_windowId, _windowRect, new WindowFunction(DrawWindow), "", _windowStyle); } GUI.matrix = matrix; } } private void DrawLeftNotifications() { //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) //IL_0135: 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_014b: 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_0161: Unknown result type (might be due to invalid IL or missing references) //IL_016a: Expected O, but got Unknown //IL_0180: Unknown result type (might be due to invalid IL or missing references) //IL_0362: 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_01f6: Expected O, but got Unknown //IL_0224: Unknown result type (might be due to invalid IL or missing references) //IL_0233: 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_0248: Expected O, but got Unknown //IL_025f: Unknown result type (might be due to invalid IL or missing references) //IL_0272: 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_0287: Expected O, but got Unknown //IL_0298: Unknown result type (might be due to invalid IL or missing references) //IL_02bf: 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_030b: 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)_pixelTex); GUI.color = new Color(0.85f, 0.62f, 0.18f, 0.95f * num5); GUI.DrawTexture(new Rect(x, num3, 3f, num6), (Texture)_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_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_0058: 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_00ad: Invalid comparison between Unknown and I4 //IL_013a: Unknown result type (might be due to invalid IL or missing references) //IL_0140: Invalid comparison between Unknown and I4 //IL_0069: 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_00ff: 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_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_009a: 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_00e4: 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_0107: Expected O, but got Unknown //IL_0107: 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_0034: 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_0169: Unknown result type (might be due to invalid IL or missing references) //IL_01b0: Unknown result type (might be due to invalid IL or missing references) //IL_01fb: Unknown result type (might be due to invalid IL or missing references) float num = 398f; float num2 = 28f; if (((Rect)(ref _controlsRect)).width <= 0f) { _controlsRect = new Rect(0f, (float)Screen.height / UIScale - num2, num, num2); } ((Rect)(ref _controlsRect)).width = num; ((Rect)(ref _controlsRect)).height = num2; ((Rect)(ref _controlsRect)).x = Mathf.Clamp(((Rect)(ref _controlsRect)).x, 0f, (float)Screen.width / UIScale - num); ((Rect)(ref _controlsRect)).y = Mathf.Clamp(((Rect)(ref _controlsRect)).y, 0f, (float)Screen.height / UIScale - num2); 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)_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)) { _fcwMessagesSticky = !_fcwMessagesSticky; } bool flag = IsLocalPvpEnabled(); if (DrawStateButton(new Rect(x + 178f, y + 4f, 70f, 20f), "PvP", flag)) { if (HasLocalAdminAccess()) { SetLocalPvp(!flag); } else { ShowLeftNotification("Admin only."); } } if (DrawStateButton(new Rect(x + 252f, y + 4f, 132f, 20f), "Friendly Fire", GuardianPvPPatch.FriendlyFireEnabled)) { if (HasLocalAdminAccess()) { GuardianPvPPatch.FriendlyFireEnabled = !GuardianPvPPatch.FriendlyFireEnabled; } else { ShowLeftNotification("Admin only."); } } } private void HandleControlsDrag() { //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_0051: 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: Invalid comparison between Unknown and I4 //IL_00f2: Unknown result type (might be due to invalid IL or missing references) //IL_00f8: Invalid comparison between Unknown and I4 //IL_0062: 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_00d3: 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_0089: Unknown result type (might be due to invalid IL or missing references) //IL_008e: Unknown result type (might be due to invalid IL or missing references) //IL_0093: Unknown result type (might be due to invalid IL or missing references) Event current = Event.current; if (current != null) { Vector2 mousePosition = current.mousePosition; Rect val = default(Rect); ((Rect)(ref val))..ctor(((Rect)(ref _controlsRect)).x + ((Rect)(ref _controlsRect)).width - 14f, ((Rect)(ref _controlsRect)).y, 14f, ((Rect)(ref _controlsRect)).height); if ((int)current.type == 0 && current.button == 0 && ((Rect)(ref val)).Contains(mousePosition)) { _controlsDragging = true; _controlsDragOffset = mousePosition - new Vector2(((Rect)(ref _controlsRect)).x, ((Rect)(ref _controlsRect)).y); current.Use(); } else if ((int)current.type == 3 && _controlsDragging) { ((Rect)(ref _controlsRect)).x = mousePosition.x - _controlsDragOffset.x; ((Rect)(ref _controlsRect)).y = mousePosition.y - _controlsDragOffset.y; current.Use(); } else if ((int)current.type == 1) { _controlsDragging = false; } } } private bool DrawStateButton(Rect rect, string label, bool on) { //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_0071: 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_007b: 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) 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)); bool result = GUI.Button(rect, label + ": " + (on ? "ON" : "OFF"), _smallButtonStyle); GUI.color = color; _smallButtonStyle.fontSize = fontSize; _smallButtonStyle.padding = padding; return result; } private bool IsLocalPvpEnabled() { //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 ((Character)localPlayer).IsPVPEnabled(); } return false; } private void SetLocalPvp(bool enabled) { //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)) { GuardianPvPPatch.ForceLocalPvp = enabled; GuardianPvPPatch.SetPvpFromFehuPanel(localPlayer, enabled); } } private bool HasLocalAdminAccess() { //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 false; } try { return WarfareManager.Instance?.GetAdminAccessInfo(localPlayer.GetPlayerID(), 0L).EventAdminAccess ?? 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 — {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 — {2:F0}%", captureSession.Siege ? "SIEGE — " : "", 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_0025: Unknown result type (might be due to invalid IL or missing references) //IL_002f: Expected O, but got Unknown //IL_002f: 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_007b: Unknown result type (might be due to invalid IL or missing references) //IL_0085: Expected O, but got Unknown //IL_0099: 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_00c8: Unknown result type (might be due to invalid IL or missing references) //IL_00d2: Expected O, but got Unknown //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_0107: Expected O, but got Unknown //IL_0107: 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_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) //IL_0133: Expected O, but got Unknown //IL_0139: 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) GUI.color = new Color(0f, 0f, 0f, 0.82f); GUI.DrawTexture(r, (Texture)_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)_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)_pixelTex); GUI.DrawTexture(new Rect(((Rect)(ref r)).x, ((Rect)(ref r)).yMax - 1f, ((Rect)(ref r)).width, 1f), (Texture)_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_0052: 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_0087: Unknown result type (might be due to invalid IL or missing references) //IL_0091: Expected O, but got Unknown //IL_00a5: 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: Unknown result type (might be due to invalid IL or missing references) //IL_00e4: Expected O, but got Unknown //IL_00e4: 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_0100: Unknown result type (might be due to invalid IL or missing references) //IL_0109: Expected O, but got Unknown //IL_0123: 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_0207: 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_0226: Unknown result type (might be due to invalid IL or missing references) //IL_0267: Unknown result type (might be due to invalid IL or missing references) //IL_029c: Expected O, but got Unknown //IL_077f: Unknown result type (might be due to invalid IL or missing references) 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 player = warfareData.GetPlayer(playerID); GUI.color = new Color(0.06f, 0.04f, 0.03f, 0.95f); GUI.DrawTexture(new Rect(5f, 5f, ((Rect)(ref _windowRect)).width - 10f, 38f), (Texture)_pixelTex); GUI.color = new Color(0.65f, 0.5f, 0.2f, 0.7f); GUI.DrawTexture(new Rect(5f, 42f, ((Rect)(ref _windowRect)).width - 10f, 1f), (Texture)_pixelTex); GUI.color = Color.white; GUIStyle val = new GUIStyle(_labelGoldStyle) { alignment = (TextAnchor)4, fontSize = 17 }; GUI.Label(new Rect(0f, 8f, ((Rect)(ref _windowRect)).width, 32f), "FORTECA CLAN WARFARE", val); if (GUI.Button(new Rect(((Rect)(ref _windowRect)).width - 38f, 10f, 28f, 26f), "X", _tabStyle)) { _isVisible = false; } if (player != null && player.IsFreeborn && player.ClanId == 0) { DrawFreebornPanel(warfareData, player); GUI.DragWindow(new Rect(0f, 0f, ((Rect)(ref _windowRect)).width - 40f, 45f)); return; } if (player == null || player.ClanId == 0) { GUILayout.Space(6f); DrawStartChoiceActions(); GUILayout.Label("You have no clan. The clan menu opens automatically — 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(player.ClanId); if (clan != null) { WarfareManager.ColorHex(clan.Color); } GUILayout.BeginHorizontal(_boxStyle, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Height(112f) }); GUILayout.Box((Texture)LocalEmblemCache.GetTexture(clan?.ClanEmblemName), _boxStyle, (GUILayoutOption[])(object)new GUILayoutOption[2] { GUILayout.Width(96f), GUILayout.Height(96f) }); long v = ((clan != null) ? Economy.NetClanTick(warfareData, player.ClanId) : 0); int clanTerritoryCount = warfareData.GetClanTerritoryCount(player.ClanId); int num = ((clan != null) ? Economy.TerritoryCap(clan) : 0); GUILayout.BeginVertical((GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(690f) }); GUILayout.Space(8f); GUILayout.Label("" + clan?.Name + " [" + clan?.Tag + "] Rank: " + player.Rank.ToString() + "", _labelGoldStyle, Array.Empty()); GUILayout.Label("Primary: " + RoleName(player.PrimaryRole) + " Secondary: " + RoleName(player.SecondaryRole) + " Tertiary: " + RoleName(player.TertiaryRole) + "", _labelStyle, Array.Empty()); GUILayout.Label($"Treasury: {clan?.Treasury ?? 0} Net: {ColoredCoins(v)}/tick Territories: {clanTerritoryCount}/{num}", _labelStyle, Array.Empty()); GUILayout.EndVertical(); GUILayout.FlexibleSpace(); GUILayout.BeginVertical(_boxStyle, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(300f) }); GUILayout.Space(10f); GUILayout.Label("RECENT EVENTS", _labelGoldStyle, Array.Empty()); DrawStatusFeed(warfareData, player, 3); GUILayout.EndVertical(); GUILayout.EndHorizontal(); GUILayout.Space(4f); int num2 = Tabs.Length; bool flag = IsKingdomAdminVisible(); float num3 = Mathf.Max(92f, (((Rect)(ref _windowRect)).width - 24f) / 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 (GUILayout.Button(Tabs[j], (_tab == j) ? _tabActiveStyle : _tabStyle, (GUILayoutOption[])(object)new GUILayoutOption[2] { GUILayout.Width(num3), GUILayout.Height(26f) })) { num4 = j; } } if (i == 1 && flag && GUILayout.Button("Admins", (_tab == num2) ? _tabActiveStyle : _tabStyle, (GUILayoutOption[])(object)new GUILayoutOption[2] { GUILayout.Width(num3), GUILayout.Height(26f) })) { num4 = num2; } GUILayout.EndHorizontal(); } if (num4 >= 0 && num4 != _tab) { _tab = num4; GUIUtility.ExitGUI(); } GUILayout.Space(6f); if (_tab == num2 && !flag) { _tab = 0; } switch (_tab) { case 0: DrawOverviewHub(warfareData, player, clan); break; case 1: DrawProfileHub(warfareData, player, clan); break; case 2: DrawMembersHub(warfareData, player, clan); break; case 3: DrawTerritoriesHub(warfareData, player, clan); break; case 4: DrawEconomyHub(warfareData, player, clan); break; case 5: DrawWar(warfareData, player, clan); break; case 6: DrawInfrastructure(warfareData, player, clan); break; case 7: DrawKingdomsHub(warfareData, player, clan); break; case 8: DrawGuardsHub(warfareData, player, clan); break; case 9: DrawContractsHub(warfareData, player); break; case 10: DrawRankingsHub(warfareData, player, clan); break; case 11: DrawClansHub(warfareData, player); break; case 12: DrawHomesteadsHub(warfareData, player); 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, player); break; case 19: DrawSupportHub(); break; case 20: 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(30f) })) { 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(30f) })) { 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(30f) })) { Plugin.Log.LogInfo((object)"[FCW HUD] Play As Freeborn clicked."); WarfareRPC.Instance?.RequestPlayAsFreeborn(); } GUILayout.EndHorizontal(); } private void DrawFreebornPanel(WarfareData data, PlayerClanInfo info) { //IL_00a1: Unknown result type (might be due to invalid IL or missing references) //IL_00d6: Expected O, but got Unknown 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(112f) }); GUILayout.Box((Texture)_pixelTex, _boxStyle, (GUILayoutOption[])(object)new GUILayoutOption[2] { GUILayout.Width(96f), GUILayout.Height(96f) }); GUILayout.BeginVertical((GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(690f) }); 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(_boxStyle, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(300f) }); GUILayout.Space(10f); GUILayout.Label("STATUS SPACE", _labelGoldStyle, 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(26f) })) { num2 = i; } } bool flag = IsKingdomAdminVisible(); if (flag && GUILayout.Button("Admins", (_freebornTab == 13) ? _tabActiveStyle : _tabStyle, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Height(26f) })) { 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: DrawFreebornTerritory(data, info); break; case 2: DrawFreebornEconomy(data, info); break; case 3: DrawFreebornWarfareHub(data, info); break; case 4: DrawInfrastructure(data, info, null); break; case 5: DrawKingdomsHub(data, info, null); break; case 6: DrawGuardsHub(data, info, null); break; case 7: DrawContractsHub(data, info); break; case 8: DrawFreebornRankingStats(data, info); break; case 9: DrawClansDirectory(data, info); break; case 10: DrawHomesteadsDirectory(data, info); break; case 11: DrawAllIds(data); break; case 12: DrawFreebornMembersHub(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 void DrawRoadmapInfoCard(string title, string status, string description, string roadmapUsage) { //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_0062: Unknown result type (might be due to invalid IL or missing references) //IL_006b: 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_007e: Expected O, but got Unknown GUILayout.BeginVertical((GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.ExpandHeight(true) }); GUILayout.FlexibleSpace(); GUILayout.BeginHorizontal(Array.Empty()); GUILayout.FlexibleSpace(); GUILayout.BeginVertical(_boxStyle, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(640f) }); GUIStyle val = new GUIStyle(_labelGoldStyle) { 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(); GUILayout.FlexibleSpace(); GUILayout.EndVertical(); } 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(96f, (((Rect)(ref _windowRect)).width - 24f) / (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(30f) })) { selected = i; GUIUtility.ExitGUI(); } } GUILayout.EndHorizontal(); GUILayout.Space(6f); } private void DrawMembersHub(WarfareData data, PlayerClanInfo info, Clan clan) { //IL_0057: 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) string[] labels = new string[3] { "Roster", "Roles", "Vault" }; DrawSectionTabs(labels, ref _membersSubTab, 180f); switch (_membersSubTab) { case 0: DrawMembers(data, info, clan); break; case 1: _scrollRole = GUILayout.BeginScrollView(_scrollRole, false, true, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.ExpandHeight(true) }); DrawRole(data, info); GUILayout.Space(36f); GUILayout.EndScrollView(); 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) { //IL_00be: Unknown result type (might be due to invalid IL or missing references) //IL_00c9: Expected O, but got Unknown 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)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) { GUILayout.BeginHorizontal(Array.Empty()); GUILayout.BeginVertical(_boxStyle, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(560f) }); GUILayout.Label("Production Timers", _labelGoldStyle, Array.Empty()); List buildingTerritories = GetBuildingTerritories(data, info); if (buildingTerritories.Count == 0) { DrawEmptyState("No production territories.", "Claim or select a territory with building slots to begin production."); } else { foreach (ClanTerritory item in buildingTerritories) { GUILayout.BeginVertical(_boxStyle, Array.Empty()); GUILayout.Label("" + item.DisplayName + "", _labelGoldStyle, Array.Empty()); DrawMiniStat("Next Production", NextBuildingProductionText(item)); DrawMiniStat("Production Status", BuildingProductionStatusText(item)); GUILayout.EndVertical(); } } GUILayout.EndVertical(); GUILayout.BeginVertical(_boxStyle, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.ExpandWidth(true) }); DrawBuildingProductionLog(data); GUILayout.EndVertical(); GUILayout.EndHorizontal(); } private void DrawResourceOverview(WarfareData data, PlayerClanInfo info, Clan clan) { List buildingTerritories = GetBuildingTerritories(data, info); long num = 0L; long num2 = 0L; float globalMultiplier = Economy.GlobalUpkeepMultiplier(buildingTerritories.Count); foreach (ClanTerritory item in buildingTerritories) { num += Economy.TerritoryIncome(item); num2 += Economy.ApplyBuildingUpkeepReduction(item, Economy.ApplyWardenUpkeepReduction(data, info.ClanId, Economy.TerritoryUpkeep(item, globalMultiplier))); } GUILayout.BeginHorizontal(Array.Empty()); DrawSummaryCard("Gross Resource Value", "+" + num + "/tick", 300f); DrawSummaryCard("Infrastructure Upkeep", "-" + num2 + "/tick", 300f); DrawSummaryCard("Net Resource Value", ColoredCoins(num - num2) + "/tick", 300f); 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", "" + (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[3] { "Politics", "Laws", "Future" }; DrawSectionTabs(labels, ref _kingdomsSubTab, 180f); switch (_kingdomsSubTab) { case 0: 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 1: 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 2: 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[1] { GUILayout.Width(130f) })) { WarfareRPC.Instance?.RequestContractAction(11, item.ContractId); } if (GUILayout.Button("Remove Contract", _smallButtonStyle, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(130f) })) { 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_012a: 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) 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(520f) }); 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.Label("Ready for a guard.", _labelStyle, Array.Empty()); 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[1] { GUILayout.Width(132f) })) { _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), _buttonStyle, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Height(30f) })) { 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(520f) }); 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[4] { "Wars", "Bounties", "Events", "Defense" }; DrawSectionTabs(labels, ref _warSubTab, 180f); switch (_warSubTab) { case 0: DrawFreebornWar(data, info); break; case 1: DrawFreebornBounty(data, info); break; case 2: DrawWorldEventsPanel(data, info); break; case 3: 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 void DrawFreebornOverview(WarfareData data, PlayerClanInfo info) { //IL_00d8: 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_00e2: 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_041d: 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); long num = 0L; long num2 = 0L; float globalMultiplier = Economy.GlobalUpkeepMultiplier(list.Count); foreach (ClanTerritory item in list) { num += Economy.TerritoryIncome(item); num2 += Economy.ApplyBuildingUpkeepReduction(item, Economy.TerritoryUpkeep(item, globalMultiplier)); } 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(_boxStyle, (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("Outposts", freebornTerritories2.Count + "/" + (ClanConfig.FreebornBaseOutposts?.Value ?? 2)); DrawMiniStat("Odin's Peace", (text == "Active") ? "Active" : text); GUILayout.EndVertical(); GUILayout.BeginVertical(_boxStyle, (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", "+" + num + "/tick"); DrawMiniStat("Upkeep", "-" + num2 + "/tick"); DrawMiniStat("Net", ColoredCoins(num - num2) + "/tick"); GUILayout.EndVertical(); GUILayout.BeginVertical(_boxStyle, (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 GetLocalAdminAccessInfo().EventAdminAccess; } private long LocalPlayerId() { Player localPlayer = Player.m_localPlayer; if (!((Object)(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 void DrawKingdomAdmin(WarfareData data) { //IL_0002: 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) _adminScroll = GUILayout.BeginScrollView(_adminScroll, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.ExpandHeight(true) }); GUILayout.Label("ADMINS", _labelGoldStyle, Array.Empty()); WarfareManager.AdminAccessInfo localAdminAccessInfo = GetLocalAdminAccessInfo(); GUILayout.BeginVertical(_boxStyle, Array.Empty()); GUILayout.Label("Admin Access", _labelGoldStyle, Array.Empty()); GUILayout.BeginHorizontal(Array.Empty()); DrawMiniStat("Debug Mode", YesNo(localAdminAccessInfo.DebugMode)); DrawMiniStat("Local Host", YesNo(localAdminAccessInfo.LocalHost)); DrawMiniStat("Server Admin", YesNo(localAdminAccessInfo.ServerAdmin)); DrawMiniStat("Event Admin Access", YesNo(localAdminAccessInfo.EventAdminAccess)); GUILayout.EndHorizontal(); GUILayout.EndVertical(); GUILayout.Label("Development overview and admin-only tools. Dangerous actions require confirmation.", _labelStyle, Array.Empty()); 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); GUILayout.EndScrollView(); } private void DrawAdminTerritoryTools(WarfareData data) { GUILayout.BeginVertical(_boxStyle, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(640f) }); 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()); } DrawProtectedZoneConflictTools(data); GUILayout.EndVertical(); } 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) })) { WarfareRPC.Instance?.RequestKingdomAdminAction(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(640f) }); 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(26f) })) { WarfareRPC.Instance?.RequestWorldEventAdmin(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) })) { WarfareRPC.Instance?.RequestWorldEventAdmin(2, item.EventId); } if (GUILayout.Button("Remove Event", _smallButtonStyle, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(110f) })) { WarfareRPC.Instance?.RequestWorldEventAdmin(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) })) { WarfareRPC.Instance?.RequestKingdomAdminAction(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) })) { WarfareRPC.Instance?.RequestKingdomAdminAction(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_0052: 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) List list = new List(); list.AddRange(GetFreebornTerritories(data, info.PlayerId, TerritoryType.Homestead)); list.AddRange(GetFreebornTerritories(data, info.PlayerId, TerritoryType.Outpost)); if (list.Count == 0) { GUILayout.Label("You hold no Freeborn territory. Claim a Homestead from the map.", _labelStyle, Array.Empty()); } 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", _buttonStyle, 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", _buttonStyle, 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[1] { GUILayout.Width(100f) })) { WarfareRPC.Instance?.RequestLeaveFreebornLife(); _confirmLeaveFreeborn = false; } if (GUILayout.Button("Cancel", _smallButtonStyle, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(100f) })) { _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)", _smallButtonStyle, (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); DrawSummaryCard("Upkeep", "-" + num3 + "/tick", 220f); DrawSummaryCard("Net", ColoredCoins(num4 - num3) + "/tick", 220f); GUILayout.EndHorizontal(); GUILayout.BeginHorizontal(Array.Empty()); GUILayout.BeginVertical(_boxStyle, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(360f) }); GUILayout.Label("Income Sources", _labelGoldStyle, Array.Empty()); DrawMiniStat("Homestead", "+" + num + "/tick"); DrawMiniStat("Outposts", "+" + num2 + "/tick"); DrawMiniStat("Total", "+" + num4 + "/tick"); GUILayout.EndVertical(); GUILayout.BeginVertical(_boxStyle, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(360f) }); GUILayout.Label("Upkeep", _labelGoldStyle, 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(_boxStyle, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.ExpandWidth(true) }); GUILayout.Label("Summary", _labelGoldStyle, 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(30f) })) { 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(30f) })) { 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); if (!flag3) { goto IL_056c; } if (!flag) { ConfigEntry allowCoinDepositWhenVaultClosed = ClanConfig.AllowCoinDepositWhenVaultClosed; if (allowCoinDepositWhenVaultClosed == null || !allowCoinDepositWhenVaultClosed.Value) { goto IL_056c; } } int enabled = ((num4 > 0) ? 1 : 0); goto IL_056d; IL_056d: GUI.enabled = (byte)enabled != 0; if (GUILayout.Button("Deposit Coins", _buttonStyle, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Height(30f) })) { 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(30f) })) { 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_056c: enabled = 0; goto IL_056d; } 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(28f) })) { 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[1] { GUILayout.Width(100f) }) && long.TryParse(_freebornFriendTarget, out var result)) { WarfareRPC.Instance?.RequestFreebornFriend(result, 1); } GUILayout.EndHorizontal(); 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 player = data.GetPlayer(freebornFriend); GUILayout.BeginHorizontal(_boxStyle, Array.Empty()); GUILayout.Label((player?.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 player2 = data.GetPlayer(pendingFreebornFriendRequest); GUILayout.BeginHorizontal(_boxStyle, Array.Empty()); GUILayout.Label((player2?.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(24f) })) { 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) { //IL_04ee: Unknown result type (might be due to invalid IL or missing references) //IL_0523: Expected O, but got Unknown //IL_040d: Unknown result type (might be due to invalid IL or missing references) //IL_045a: Expected O, but got Unknown 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(72f) }); 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(26f) })) { _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(26f) })) { _emblemPage--; } GUI.enabled = _emblemPage + 1 < num4; if (GUILayout.Button("Next", _smallButtonStyle, (GUILayoutOption[])(object)new GUILayoutOption[2] { GUILayout.Width(82f), GUILayout.Height(26f) })) { _emblemPage++; } GUI.enabled = true; if (GUILayout.Button("Apply Emblem", _buttonStyle, (GUILayoutOption[])(object)new GUILayoutOption[2] { GUILayout.Width(130f), GUILayout.Height(28f) })) { clan.ClanEmblemName = _pendingClanEmblemName; WarfareRPC.Instance?.RequestSetClanEmblemName(_pendingClanEmblemName); _editingClanEmblem = false; } if (GUILayout.Button("Cancel", _smallButtonStyle, (GUILayoutOption[])(object)new GUILayoutOption[2] { GUILayout.Width(82f), GUILayout.Height(26f) })) { _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)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)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) { if (_profileTextures == null) { _profileTextures = LoadRuntimePngTextures("Profiles", recursive: false); } if (info != null) { string key = info.PlayerId.ToString(); if (_profileTextures.TryGetValue(key, out var value)) { return value; } } if (!_profileTextures.TryGetValue("default", out var value2)) { return null; } return value2; } private Texture2D GetInventoryItemTexture(string prefabName, string displayName, Texture2D itemDropIcon = null) { if ((Object)(object)itemDropIcon != (Object)null) { return itemDropIcon; } string key = (prefabName ?? string.Empty) + "|" + (displayName ?? string.Empty); if (_resolvedInventoryIcons.TryGetValue(key, out var value)) { return value; } Texture2D prefabItemIcon = GetPrefabItemIcon(prefabName); if ((Object)(object)prefabItemIcon != (Object)null) { _resolvedInventoryIcons[key] = prefabItemIcon; return prefabItemIcon; } if (_inventoryTextures == null) { _inventoryTextures = LoadRuntimePngTextures("Inventory-icons", recursive: true); } string[] array = new string[4] { prefabName ?? string.Empty, NormalizeAssetKey(prefabName), displayName ?? string.Empty, NormalizeAssetKey(displayName) }; foreach (string text in array) { if (!string.IsNullOrEmpty(text) && _inventoryTextures.TryGetValue(text, out var value2)) { _resolvedInventoryIcons[key] = value2; return value2; } } _resolvedInventoryIcons[key] = null; return null; } private static Texture2D GetItemDataIcon(object itemData) { try { object obj = itemData?.GetType().GetField("m_shared")?.GetValue(itemData); if (obj?.GetType().GetField("m_icons")?.GetValue(obj) is Array { Length: >0 } array) { object? value = array.GetValue(0); Sprite val = (Sprite)((value is Sprite) ? value : null); if (val != null) { return val.texture; } } } catch { } return null; } private static Texture2D GetPrefabItemIcon(string prefabName) { if (string.IsNullOrEmpty(prefabName) || (Object)(object)ObjectDB.instance == (Object)null) { return null; } try { object? obj = ((object)ObjectDB.instance).GetType().GetMethod("GetItemPrefab", new Type[1] { typeof(string) })?.Invoke(ObjectDB.instance, new object[1] { prefabName }); object? obj2 = ((obj is GameObject) ? obj : null); ItemDrop val = ((obj2 != null) ? ((GameObject)obj2).GetComponent() : null); return ((Object)(object)val != (Object)null) ? GetItemDataIcon(val.m_itemData) : null; } catch { return null; } } private bool DrawVaultIconButton(Texture2D icon, string fallbackLabel, int amount, bool selected) { //IL_0012: Unknown result type (might be due to invalid IL or missing references) //IL_001d: Expected O, but got Unknown //IL_0018: 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_0059: Expected O, but got Unknown //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_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_0071: Unknown result type (might be due to invalid IL or missing references) //IL_007a: Expected O, but got Unknown //IL_00ae: Unknown result type (might be due to invalid IL or missing references) bool result = GUILayout.Button(((Object)(object)icon != (Object)null) ? new GUIContent((Texture)icon, fallbackLabel) : new GUIContent(fallbackLabel), selected ? _tabActiveStyle : _smallButtonStyle, (GUILayoutOption[])(object)new GUILayoutOption[2] { GUILayout.Width(54f), GUILayout.Height(54f) }); Rect lastRect = GUILayoutUtility.GetLastRect(); GUIStyle val = new GUIStyle(_labelGoldStyle) { alignment = (TextAnchor)8, fontSize = 11 }; GUI.Label(new Rect(((Rect)(ref lastRect)).x + 3f, ((Rect)(ref lastRect)).y + 3f, ((Rect)(ref lastRect)).width - 6f, ((Rect)(ref lastRect)).height - 6f), "x" + amount, val); 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 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)(object)val); continue; } string text2 = (((Object)val).name = Path.GetFileNameWithoutExtension(text)); dictionary[text2] = val; dictionary[NormalizeAssetKey(text2)] = 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[1] { folderName } : new string[4] { "Inventory-icons", "inventory-icons", "Icons", Path.Combine("Assets", "Icons") }); 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_0005: Unknown result type (might be due to invalid IL or missing references) //IL_0010: Expected O, but got Unknown //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_0028: 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 ((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(_boxStyle, Array.Empty()); 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_0067: Unknown result type (might be due to invalid IL or missing references) //IL_0071: Expected O, but got Unknown //IL_0085: 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_00b8: Unknown result type (might be due to invalid IL or missing references) //IL_00c2: Expected O, but got Unknown //IL_00d6: 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_0105: Unknown result type (might be due to invalid IL or missing references) //IL_010f: Expected O, but got Unknown //IL_012f: 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_0144: Expected O, but got Unknown 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)_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)_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)_pixelTex); GUI.DrawTexture(new Rect(((Rect)(ref rect)).x, ((Rect)(ref rect)).yMax - 1f, ((Rect)(ref rect)).width, 1f), (Texture)_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) { return PlayerClanInfo.NormalizeRole(role) switch { ClanRole.Guardian => new string[2] { "guardian", "shieldbearer" }, ClanRole.TwelfthPath => new string[2] { "TwelfthPath", "new" }, _ => 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_004a: Unknown result type (might be due to invalid IL or missing references) //IL_0051: Expected O, but got Unknown _buildingTextures = new Dictionary(StringComparer.OrdinalIgnoreCase); string path = Path.Combine(RuntimeAssetRoot, "Buildings"); 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); _buildingTextures[((Object)val).name] = 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 => "\ud83d\uded2", WorldEventType.WorldBoss => "☠", WorldEventType.OdinChallenge => "ᛟ", WorldEventType.ClanChallenge => "\ud83d\udc51", WorldEventType.FreebornOpportunity => "\ud83c\udfe0", _ => "?", }; } 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() { GUISkin skin = GUI.skin; Font val = ((skin != null) ? skin.font : null); if ((Object)(object)val != (Object)null && val.HasCharacter('⚔') && val.HasCharacter('ᚠ')) { return val.HasCharacter('☠'); } return false; } private void DrawOverviewHub(WarfareData data, PlayerClanInfo info, Clan clan) { //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) //IL_0063: Unknown result type (might be due to invalid IL or missing references) string[] labels = new string[4] { "Overview", "Elections", "Activity", "Statistics" }; DrawSectionTabs(labels, ref _overviewSubTab, 180f); if (_overviewSubTab == 0) { _overviewScroll = GUILayout.BeginScrollView(_overviewScroll, false, true, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.ExpandHeight(true) }); DrawOverview(data, info, clan); GUILayout.Space(24f); GUILayout.EndScrollView(); } else if (_overviewSubTab == 1) { DrawJarlElectionPanel(data, info, clan); } else if (_overviewSubTab == 2) { DrawRoadmapInfoCard("Clan Activity", "TRACKING FOUNDATION", "Recent clan activity, member participation and inactivity protection status will be collected here.", "This view uses existing activity tracking only and does not enable inactivity enforcement."); } else { DrawGlobalRankingStats(data); } } private void DrawProfileHub(WarfareData data, PlayerClanInfo info, Clan clan) { //IL_003b: 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) string[] labels = new string[4] { "Character", "Achievements", "Titles", "Personal Saga" }; DrawSectionTabs(labels, ref _profileSubTab, 180f); _profileScroll = GUILayout.BeginScrollView(_profileScroll, false, true, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.ExpandHeight(true) }); switch (_profileSubTab) { case 0: DrawProfileCharacter(data, info, clan); break; case 1: DrawRoadmapInfoCard("Achievements", "FOUNDATION", "Personal achievements will record real Fehu accomplishments as systems become available.", "Future achievements cover territory, economy, warfare, events and community milestones."); break; case 2: GUILayout.BeginVertical(_boxStyle, Array.Empty()); GUILayout.Label("Titles", _labelGoldStyle, Array.Empty()); GUILayout.Label("Active title: " + ProfileTitle(info, clan) + "", _labelStyle, Array.Empty()); GUILayout.Label("Titles are earned from future achievements, saga milestones and supporter recognition.", _labelStyle, Array.Empty()); GUILayout.EndVertical(); break; case 3: DrawProfileSaga(info); break; } GUILayout.Space(16f); GUILayout.EndScrollView(); } 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(_boxStyle, Array.Empty()); 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 { DrawRoadmapInfoCard("Economy Reports", "COMING SOON", "Daily income, weekly income, treasury transactions and top contributors will appear here.", "Reports are read-only summaries built from existing economy activity."); } } } 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); } private void DrawClansHub(WarfareData data, PlayerClanInfo info) { 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) { string[] labels = new string[8] { "Lobby", "Chess", "Dice", "Poker", "Leaderboards", "History", "My Stats", "Invites" }; DrawSectionTabs(labels, ref _gamesSubTab, 180f); 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) { DrawGamesLeaderboards(data); } else if (_gamesSubTab == 5) { DrawGamesHistory(data); } else if (_gamesSubTab == 6) { DrawMyGameStats(data, info); } else { DrawGamesInvites(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 DrawGamesLobby(WarfareData data, PlayerClanInfo info) { GUILayout.BeginVertical(_boxStyle, Array.Empty()); GUILayout.Label("Games Lobby", _labelGoldStyle, Array.Empty()); List list = (data.Games ?? new List()).FindAll(IsActiveGameTable); if (list.Count == 0) { GUILayout.Label("No active game tables.", _labelStyle, Array.Empty()); GUILayout.Label("Public and private tables will appear here. Every table has separate players, spectators, escrow and history.", _labelStyle, Array.Empty()); } else { foreach (FehuGameTable item in list) { GUILayout.BeginVertical(_boxStyle, Array.Empty()); GUILayout.Label("" + item.GameType.ToString() + ": " + item.TableName + "", _labelGoldStyle, Array.Empty()); GUILayout.Label("Host: " + item.HostPlayerName + " Players: " + item.PlayerIds.Count + "/" + item.PlayerLimit + " Stake: " + FormatGameStake(item.StakeType, item.StakeAmount, item.StakePrefabName) + " Status: " + item.Status.ToString() + " Spectators: " + item.SpectatorIds.Count, _labelStyle, Array.Empty()); GUILayout.BeginHorizontal(Array.Empty()); 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 static void RequestLobbyAction(FehuGameTable table, int action) { if (table.GameType == FehuGameType.Dice) { WarfareRPC.Instance?.RequestGameAction(action, table.GameId); } else if (table.GameType == FehuGameType.Chess) { WarfareRPC.Instance?.RequestChessAction(action, table.GameId); } else if (table.GameType == FehuGameType.Poker) { WarfareRPC.Instance?.RequestPokerAction(action switch { 4 => 11, 5 => 4, _ => action, }, table.GameId, "", 2, 100L, 1L, 2L, isPrivate: false, allowSpectators: true, 0L); } } private static void RequestLobbyDelete(FehuGameTable table) { if (table.GameType == FehuGameType.Dice) { WarfareRPC.Instance?.RequestGameAction(6, table.GameId); } else if (table.GameType == FehuGameType.Chess) { WarfareRPC.Instance?.RequestChessAction(14, table.GameId); } else if (table.GameType == FehuGameType.Poker) { WarfareRPC.Instance?.RequestPokerAction(12, table.GameId, "", 2, 100L, 1L, 2L, isPrivate: false, allowSpectators: true, 0L); } } private void DrawChessGames(WarfareData data, PlayerClanInfo info) { List list = (data.Games ?? new List()).FindAll((FehuGameTable game) => game.GameType == FehuGameType.Chess && IsActiveGameTable(game)); if (_selectedChessGameId == 0 && list.Count > 0) { _selectedChessGameId = list[0].GameId; } FehuGameTable fehuGameTable = list.Find((FehuGameTable game) => game.GameId == _selectedChessGameId); GUILayout.BeginHorizontal(Array.Empty()); GUILayout.BeginVertical(_boxStyle, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(300f) }); 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(260f) }); 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[1] { GUILayout.Width(84f) })) { _chessColorPreference = num; } } GUILayout.EndHorizontal(); GUILayout.Label("Time Control", _labelStyle, Array.Empty()); string[] array2 = new string[6] { "No Timer", "5 min", "10 min", "15 min", "30 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[1] { GUILayout.Width((num2 == 5) ? 62f : 52f) })) { _chessTimerIndex = num2; } } GUILayout.EndHorizontal(); if (_chessTimerIndex == 5) { GUILayout.BeginHorizontal(Array.Empty()); GUILayout.Label("Custom minutes", _labelStyle, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(105f) }); _chessCustomMinutes = GUILayout.TextField(_chessCustomMinutes, 4, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(60f) }); GUILayout.EndHorizontal(); } GUILayout.BeginHorizontal(Array.Empty()); GUILayout.Label("Coin Stake", _labelStyle, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(90f) }); _chessStakeInput = GUILayout.TextField(_chessStakeInput, 10, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(100f) }); GUILayout.EndHorizontal(); _chessPrivate = GUILayout.Toggle(_chessPrivate, "Private Match", _labelStyle, Array.Empty()); _chessSpectators = GUILayout.Toggle(_chessSpectators, "Allow Spectators", _labelStyle, Array.Empty()); if (GUILayout.Button("Create Chess Match", _buttonStyle, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(260f) })) { int.TryParse(_chessStakeInput, out var result); int.TryParse(_chessCustomMinutes, out var result2); int[] array3 = new int[5] { 0, 300, 600, 900, 1800 }; int timeControlSeconds = ((_chessTimerIndex == 5) ? Mathf.Clamp((result2 <= 0) ? 1200 : (result2 * 60), 60, 7200) : array3[_chessTimerIndex]); WarfareRPC.Instance?.RequestChessAction(1, 0, _chessMatchName, _chessColorPreference, timeControlSeconds, Mathf.Max(0, result), _chessPrivate, _chessSpectators); } 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(260f) })) { _selectedChessGameId = item.GameId; _selectedChessSquare = -1; } } GUILayout.EndVertical(); GUILayout.BeginVertical(_boxStyle, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(470f) }); 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(310f) }); DrawChessInfo(fehuGameTable, data, info); GUILayout.EndVertical(); GUILayout.EndHorizontal(); } 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) })) { WarfareRPC.Instance?.RequestChessAction(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) })) { WarfareRPC.Instance?.RequestChessAction(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) })) { WarfareRPC.Instance?.RequestChessAction(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) })) { WarfareRPC.Instance?.RequestChessAction(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) })) { WarfareRPC.Instance?.RequestChessAction(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) { 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()); for (int num = 7; num >= 0; num--) { GUILayout.BeginHorizontal(Array.Empty()); GUILayout.Label((num + 1).ToString(), _labelStyle, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(18f) }); for (int i = 0; i < 8; i++) { int num2 = num * 8 + i; char c = chess.Board[num2]; string text = ChessPieceText(c); GUIStyle val = ((num2 == _selectedChessSquare) ? _tabActiveStyle : (list.Contains(num2) ? _buttonStyle : _tabStyle)); if (GUILayout.Button(text, val, (GUILayoutOption[])(object)new GUILayoutOption[2] { GUILayout.Width(48f), GUILayout.Height(42f) }) && flag) { if (_selectedChessSquare >= 0 && list.Contains(num2)) { WarfareRPC.Instance?.RequestChessAction(6, match.GameId, "", 0, 0, 0, isPrivate: false, allowSpectators: false, _selectedChessSquare, num2, _chessPromotion); _selectedChessSquare = -1; } else if (c != '.' && ChessRuleEngine.IsWhite(c) == chess.WhiteToMove && ((chess.WhiteToMove && chess.WhitePlayerId == info.PlayerId) || (!chess.WhiteToMove && chess.BlackPlayerId == info.PlayerId))) { _selectedChessSquare = num2; } } } GUILayout.EndHorizontal(); } GUILayout.BeginHorizontal(Array.Empty()); GUILayout.Label(" ", _labelStyle, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(18f) }); for (int j = 0; j < 8; j++) { GUILayout.Label(((char)(65 + j)).ToString(), _labelStyle, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(48f) }); } GUILayout.EndHorizontal(); GUILayout.BeginHorizontal(Array.Empty()); GUILayout.Label("Promotion", _labelStyle, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(76f) }); char[] array = new char[4] { 'Q', 'R', 'B', 'N' }; for (int k = 0; k < array.Length; k++) { char c2 = array[k]; if (GUILayout.Button(c2.ToString(), (_chessPromotion == c2) ? _tabActiveStyle : _tabStyle, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(42f) })) { _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())) { WarfareRPC.Instance?.RequestChessAction(7, match.GameId); } if (chess.DrawOfferedBy == 0L && GUILayout.Button("Offer Draw", _buttonStyle, Array.Empty())) { WarfareRPC.Instance?.RequestChessAction(8, match.GameId); } if (chess.DrawOfferedBy != 0L && chess.DrawOfferedBy != info.PlayerId) { if (GUILayout.Button("Accept Draw", _buttonStyle, Array.Empty())) { WarfareRPC.Instance?.RequestChessAction(9, match.GameId); } if (GUILayout.Button("Decline Draw", _buttonStyle, Array.Empty())) { WarfareRPC.Instance?.RequestChessAction(10, match.GameId); } } if (chess.UndoRequestedBy == 0L && chess.Moves.Count > 0 && GUILayout.Button("Request Undo", _buttonStyle, Array.Empty())) { WarfareRPC.Instance?.RequestChessAction(11, match.GameId); } if (chess.UndoRequestedBy != 0L && chess.UndoRequestedBy != info.PlayerId) { if (GUILayout.Button("Accept Undo", _buttonStyle, Array.Empty())) { WarfareRPC.Instance?.RequestChessAction(12, match.GameId); } if (GUILayout.Button("Decline Undo", _buttonStyle, Array.Empty())) { WarfareRPC.Instance?.RequestChessAction(13, match.GameId); } } } 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) { 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(300f) }); 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) }); 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(26f) })) { _pokerPlayerLimit = Mathf.Max(2, _pokerPlayerLimit - 1); } GUILayout.Label(_pokerPlayerLimit.ToString(), _labelGoldStyle, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(25f) }); if (GUILayout.Button("+", _smallButtonStyle, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(26f) })) { _pokerPlayerLimit = Mathf.Min(10, _pokerPlayerLimit + 1); } GUILayout.EndHorizontal(); DrawPokerNumberInput("Buy-in", ref _pokerBuyInInput); DrawPokerNumberInput("Small Blind", ref _pokerSmallBlindInput); DrawPokerNumberInput("Big Blind", ref _pokerBigBlindInput); _pokerPrivate = GUILayout.Toggle(_pokerPrivate, "Private Table", _labelStyle, Array.Empty()); _pokerSpectators = GUILayout.Toggle(_pokerSpectators, "Allow Spectators", _labelStyle, Array.Empty()); 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); WarfareRPC.Instance?.RequestPokerAction(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.BeginVertical(_boxStyle, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(560f) }); if (fehuGameTable == null) { GUILayout.Label("Create or select a Texas Hold'em table.", _labelStyle, Array.Empty()); } else { DrawPokerTable(fehuGameTable, data, info); } GUILayout.EndVertical(); GUILayout.BeginVertical(_boxStyle, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(290f) }); DrawPokerInfo(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()); if (table.Status == FehuGameStatus.Waiting && !table.PlayerIds.Contains(info.PlayerId) && !table.IsPrivate && GUILayout.Button("Join Table", _buttonStyle, Array.Empty())) { WarfareRPC.Instance?.RequestPokerAction(2, 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())) { WarfareRPC.Instance?.RequestPokerAction(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())) { WarfareRPC.Instance?.RequestPokerAction(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())) { WarfareRPC.Instance?.RequestPokerAction(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())) { WarfareRPC.Instance?.RequestPokerAction(4, table.GameId, "", 2, 100L, 1L, 2L, isPrivate: false, allowSpectators: true, 0L); } return; } GUILayout.Label("Hand " + poker.HandNumber + " | " + poker.Phase.ToString() + " | Pot: " + PokerEngine.CurrentPot(poker) + " | Current bet: " + poker.CurrentBet, _labelStyle, Array.Empty()); GUILayout.Label("Community: " + ((poker.CommunityCards.Count == 0) ? "Hidden" : string.Join(" ", poker.CommunityCards.ConvertAll(PokerEngine.CardName).ToArray())), _labelGoldStyle, Array.Empty()); GUILayout.Space(6f); GUILayout.Label("Seats", _labelGoldStyle, Array.Empty()); foreach (PokerPlayerState seat in poker.Seats) { string text = ((seat.PlayerId == info.PlayerId) ? string.Join(" ", seat.HoleCards.ConvertAll(PokerEngine.CardName).ToArray()) : "[hidden]"); GUILayout.Label(((seat.PlayerId == poker.CurrentActorId) ? "> " : "") + seat.PlayerName + " | Chips " + seat.Chips + " | Bet " + seat.RoundBet + (seat.Folded ? " | Folded" : (seat.AllIn ? " | All-in" : "")) + " | " + text, _labelStyle, Array.Empty()); } if (table.AllowSpectators && !table.PlayerIds.Contains(info.PlayerId) && !table.SpectatorIds.Contains(info.PlayerId) && GUILayout.Button("Spectate", _smallButtonStyle, Array.Empty())) { WarfareRPC.Instance?.RequestPokerAction(4, table.GameId, "", 2, 100L, 1L, 2L, isPrivate: false, allowSpectators: true, 0L); } } 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); GUILayout.Label("Your turn. Call: " + num, _labelGoldStyle, Array.Empty()); GUILayout.BeginHorizontal(Array.Empty()); if (GUILayout.Button("Fold", _smallButtonStyle, Array.Empty())) { WarfareRPC.Instance?.RequestPokerAction(5, table.GameId, "", 2, 100L, 1L, 2L, isPrivate: false, allowSpectators: true, 0L); } if (GUILayout.Button((num == 0L) ? "Check" : ("Call " + num), _smallButtonStyle, Array.Empty())) { WarfareRPC.Instance?.RequestPokerAction(6, table.GameId, "", 2, 100L, 1L, 2L, isPrivate: false, allowSpectators: true, 0L); } if (GUILayout.Button("All-In", _smallButtonStyle, Array.Empty())) { WarfareRPC.Instance?.RequestPokerAction(8, table.GameId, "", 2, 100L, 1L, 2L, isPrivate: false, allowSpectators: true, 0L); } GUILayout.EndHorizontal(); GUILayout.BeginHorizontal(Array.Empty()); GUILayout.Label("Raise to", _labelStyle, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(56f) }); _pokerRaiseInput = GUILayout.TextField(_pokerRaiseInput, 10, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(90f) }); if (GUILayout.Button("Raise", _smallButtonStyle, Array.Empty())) { long.TryParse(_pokerRaiseInput, out var result); WarfareRPC.Instance?.RequestPokerAction(7, table.GameId, "", 2, 100L, 1L, 2L, isPrivate: false, allowSpectators: true, result); } GUILayout.EndHorizontal(); } if (!PokerEngine.IsHandActive(poker)) { if (table.HostPlayerId == info.PlayerId && GUILayout.Button("Deal Next Hand", _buttonStyle, Array.Empty())) { WarfareRPC.Instance?.RequestPokerAction(10, table.GameId, "", 2, 100L, 1L, 2L, isPrivate: false, allowSpectators: true, 0L); } if (player != null && GUILayout.Button("Cash Out " + player.Chips + " Coins", _buttonStyle, Array.Empty())) { WarfareRPC.Instance?.RequestPokerAction(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())) { WarfareRPC.Instance?.RequestPokerAction(11, table.GameId, "", 2, 100L, 1L, 2L, isPrivate: false, allowSpectators: true, 0L); } } GUILayout.Space(8f); GUILayout.Label("Hand Result", _labelGoldStyle, Array.Empty()); GUILayout.Label(poker.LastResult ?? "Waiting.", _labelStyle, Array.Empty()); } private void DrawDiceGames(WarfareData data, PlayerClanInfo info) { List diceStakeItems = GetDiceStakeItems(); if (_diceStakeItemIndex >= diceStakeItems.Count) { _diceStakeItemIndex = 0; } GUILayout.BeginHorizontal(Array.Empty()); GUILayout.BeginVertical(_boxStyle, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(370f) }); 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 i = 0; i < array.Length; i++) { if (GUILayout.Button(array[i], (_diceModeIndex == i) ? _tabActiveStyle : _tabStyle, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(104f) })) { _diceModeIndex = i; } } 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 j = 0; j < array2.Length; j++) { if (GUILayout.Button(array2[j], (_diceStakeTypeIndex == j) ? _tabActiveStyle : _tabStyle, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(104f) })) { _diceStakeTypeIndex = j; } } 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(); } _dicePrivate = GUILayout.Toggle(_dicePrivate, "Private Table", _labelStyle, Array.Empty()); _diceSpectators = GUILayout.Toggle(_diceSpectators, "Allow Spectators", _labelStyle, Array.Empty()); if (GUILayout.Button("Create Dice Table", _buttonStyle, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(320f) })) { int.TryParse(_diceStakeInput, out var result); FehuStakeType diceStakeTypeIndex = (FehuStakeType)_diceStakeTypeIndex; string stakePrefab = ((diceStakeTypeIndex == FehuStakeType.Item && diceStakeItems.Count > 0) ? diceStakeItems[_diceStakeItemIndex].PrefabName : ""); WarfareRPC.Instance?.RequestGameAction(1, 0, _diceTableName, (DiceGameMode)_diceModeIndex, _dicePlayerLimit, _diceCount, (_diceModeIndex != 1) ? 1 : 3, Mathf.Max(0, result), _dicePrivate, _diceSpectators, diceStakeTypeIndex, stakePrefab); } GUILayout.EndVertical(); GUILayout.BeginVertical(_boxStyle, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.ExpandWidth(true) }); GUILayout.Label("Dice Tables", _labelGoldStyle, Array.Empty()); List list = (data.Games ?? new List()).FindAll((FehuGameTable game) => game.GameType == FehuGameType.Dice && IsActiveGameTable(game)); if (list.Count == 0) { GUILayout.Label("No Dice tables available.", _labelStyle, Array.Empty()); } foreach (FehuGameTable item in list) { GUILayout.BeginVertical(_boxStyle, Array.Empty()); GUILayout.Label("#" + item.GameId + " " + item.TableName + " | " + item.DiceMode.ToString() + " | " + item.Status, _labelGoldStyle, Array.Empty()); GUILayout.Label("Host: " + item.HostPlayerName + " | Players: " + item.PlayerIds.Count + "/" + item.PlayerLimit + " | Spectators: " + item.SpectatorIds.Count + " | Stake: " + FormatGameStake(item.StakeType, item.StakeAmount, item.StakePrefabName) + " | Pot: " + ((item.EscrowTotal > 0) ? (item.EscrowTotal + " " + ((item.StakeType == FehuStakeType.Item) ? item.StakePrefabName : "coins")) : "-"), _labelStyle, Array.Empty()); GUILayout.Label("Result: " + (string.IsNullOrEmpty(item.Result) ? "Waiting for game start." : item.Result), _labelStyle, Array.Empty()); GUILayout.BeginHorizontal(Array.Empty()); if (item.Status == FehuGameStatus.Waiting && !item.PlayerIds.Contains(info.PlayerId) && !item.IsPrivate && GUILayout.Button("Join", _smallButtonStyle, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(100f) })) { WarfareRPC.Instance?.RequestGameAction(2, item.GameId); } if (item.Status == FehuGameStatus.Waiting && item.HostPlayerId == info.PlayerId && GUILayout.Button("Start Game", _smallButtonStyle, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(110f) })) { WarfareRPC.Instance?.RequestGameAction(3, item.GameId); } if (item.Status == FehuGameStatus.Waiting && item.HostPlayerId == info.PlayerId && GUILayout.Button("Cancel", _smallButtonStyle, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(90f) })) { WarfareRPC.Instance?.RequestGameAction(4, item.GameId); } if (item.Status == FehuGameStatus.Waiting && item.HostPlayerId == info.PlayerId && GUILayout.Button("Delete Table", _smallButtonStyle, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(110f) })) { WarfareRPC.Instance?.RequestGameAction(6, item.GameId); } if (item.AllowSpectators && !item.PlayerIds.Contains(info.PlayerId) && !item.SpectatorIds.Contains(info.PlayerId) && GUILayout.Button("Spectate", _smallButtonStyle, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(100f) })) { WarfareRPC.Instance?.RequestGameAction(5, item.GameId); } GUILayout.EndHorizontal(); GUILayout.EndVertical(); } GUILayout.EndVertical(); GUILayout.EndHorizontal(); } private List GetDiceStakeItems() { //IL_00ba: Unknown result type (might be due to invalid IL or missing references) //IL_00c5: Expected O, but got Unknown List list = new List(); Player localPlayer = Player.m_localPlayer; Inventory val = ((localPlayer != 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)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 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); GUILayout.BeginHorizontal(Array.Empty()); DrawGameLeaderboardColumn(data, "Global: Profit", new List(collection), (FehuGamePlayerStats a, FehuGamePlayerStats b) => b.Profit.CompareTo(a.Profit), (FehuGamePlayerStats s) => "Profit " + s.Profit); DrawGameLeaderboardColumn(data, "Global: Wins", new List(collection), (FehuGamePlayerStats a, FehuGamePlayerStats b) => b.Wins.CompareTo(a.Wins), (FehuGamePlayerStats s) => s.Wins + " wins"); DrawGameLeaderboardColumn(data, "Global: Games", new List(collection), (FehuGamePlayerStats a, FehuGamePlayerStats b) => b.GamesPlayed.CompareTo(a.GamesPlayed), (FehuGamePlayerStats s) => s.GamesPlayed + " games"); DrawGameLeaderboardColumn(data, "Global: Win Rate", new List(collection), (FehuGamePlayerStats a, FehuGamePlayerStats b) => GlobalWinRate(b).CompareTo(GlobalWinRate(a)), (FehuGamePlayerStats s) => GlobalWinRate(s).ToString("0.0") + "%"); GUILayout.EndHorizontal(); GUILayout.BeginHorizontal(Array.Empty()); DrawGameLeaderboardColumn(data, "Dice: Wins", new List(collection), (FehuGamePlayerStats a, FehuGamePlayerStats b) => b.Wins.CompareTo(a.Wins), (FehuGamePlayerStats s) => s.Wins + " wins"); DrawGameLeaderboardColumn(data, "Dice: Highest Roll", new List(collection), (FehuGamePlayerStats a, FehuGamePlayerStats b) => b.HighestDiceRoll.CompareTo(a.HighestDiceRoll), (FehuGamePlayerStats s) => "Roll " + s.HighestDiceRoll); DrawGameLeaderboardColumn(data, "Dice: Profit", new List(collection), (FehuGamePlayerStats a, FehuGamePlayerStats b) => b.Profit.CompareTo(a.Profit), (FehuGamePlayerStats s) => "Profit " + s.Profit); GUILayout.EndHorizontal(); GUILayout.BeginHorizontal(Array.Empty()); DrawGameLeaderboardColumn(data, "Chess: Rating", new List(collection), (FehuGamePlayerStats a, FehuGamePlayerStats b) => b.ChessRating.CompareTo(a.ChessRating), (FehuGamePlayerStats s) => "Rating " + s.ChessRating); DrawGameLeaderboardColumn(data, "Chess: Wins", new List(collection), (FehuGamePlayerStats a, FehuGamePlayerStats b) => b.ChessWins.CompareTo(a.ChessWins), (FehuGamePlayerStats s) => s.ChessWins + " wins"); DrawGameLeaderboardColumn(data, "Chess: Win Rate", new List(collection), (FehuGamePlayerStats a, FehuGamePlayerStats b) => ChessWinRate(b).CompareTo(ChessWinRate(a)), (FehuGamePlayerStats s) => ChessWinRate(s).ToString("0.0") + "%"); DrawGameLeaderboardColumn(data, "Chess: Best Streak", new List(collection), (FehuGamePlayerStats a, FehuGamePlayerStats b) => b.ChessBestStreak.CompareTo(a.ChessBestStreak), (FehuGamePlayerStats s) => s.ChessBestStreak + " streak"); GUILayout.EndHorizontal(); GUILayout.BeginHorizontal(Array.Empty()); DrawGameLeaderboardColumn(data, "Poker: Profit", new List(collection), (FehuGamePlayerStats a, FehuGamePlayerStats b) => b.PokerProfit.CompareTo(a.PokerProfit), (FehuGamePlayerStats s) => "Profit " + s.PokerProfit); DrawGameLeaderboardColumn(data, "Poker: Hands Won", new List(collection), (FehuGamePlayerStats a, FehuGamePlayerStats b) => b.PokerHandsWon.CompareTo(a.PokerHandsWon), (FehuGamePlayerStats s) => s.PokerHandsWon + " hands"); DrawGameLeaderboardColumn(data, "Poker: Biggest Pot", new List(collection), (FehuGamePlayerStats a, FehuGamePlayerStats b) => b.PokerBiggestPot.CompareTo(a.PokerBiggestPot), (FehuGamePlayerStats s) => "Pot " + s.PokerBiggestPot); DrawGameLeaderboardColumn(data, "Poker: Tables Won", new List(collection), (FehuGamePlayerStats a, FehuGamePlayerStats b) => b.PokerTablesWon.CompareTo(a.PokerTablesWon), (FehuGamePlayerStats s) => s.PokerTablesWon + " tables"); GUILayout.EndHorizontal(); } 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 void DrawGameLeaderboardColumn(WarfareData data, string title, List stats, Comparison comparison, Func summary) { GUILayout.BeginVertical(_boxStyle, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(360f) }); GUILayout.Label("" + title + "", _labelGoldStyle, Array.Empty()); stats.Sort(comparison); if (stats.Count == 0) { GUILayout.Label("No ranked results yet. Play games to appear on the leaderboard.", _labelStyle, Array.Empty()); } for (int i = 0; i < stats.Count && i < 5; i++) { GUILayout.Label(i + 1 + ". " + (data.GetPlayer(stats[i].PlayerId)?.PlayerName ?? stats[i].PlayerId.ToString()) + " - " + summary(stats[i]), _labelStyle, Array.Empty()); } GUILayout.EndVertical(); } private void DrawGamesHistory(WarfareData data) { 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; } } 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 = list.Count - 1; while (num >= 0 && num >= list.Count - 20) { FehuGameHistoryEntry fehuGameHistoryEntry = list[num]; 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.BeginVertical(_boxStyle, Array.Empty()); GUILayout.Label("" + fehuGameHistoryEntry.GameType.ToString() + " - " + fehuGameHistoryEntry.TableName + "", _labelGoldStyle, Array.Empty()); GUILayout.Label("Players: " + text + "\nWinner: " + text2 + "\nStake: " + FormatGameStake(fehuGameHistoryEntry.StakeType, fehuGameHistoryEntry.StakeAmount, fehuGameHistoryEntry.StakePrefabName) + "\nReward: " + fehuGameHistoryEntry.StakeAmount * fehuGameHistoryEntry.PlayerIds.Count + "\nResult: " + fehuGameHistoryEntry.Result + "\nDate: " + text3, _labelStyle, Array.Empty()); GUILayout.EndVertical(); num--; } GUILayout.EndVertical(); } 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.Label("Global", _labelGoldStyle, Array.Empty()); DrawMiniStat("Games Played", value.GamesPlayed.ToString()); DrawMiniStat("Wins", value.Wins.ToString()); DrawMiniStat("Losses", value.Losses.ToString()); DrawMiniStat("Draws", value.ChessDraws.ToString()); DrawMiniStat("Profit", value.Profit.ToString()); DrawMiniStat("Favorite Game", value.FavoriteGame.ToString()); GUILayout.Label("Dice", _labelGoldStyle, Array.Empty()); DrawMiniStat("Games Played", value.GamesPlayed.ToString()); DrawMiniStat("Wins", value.Wins.ToString()); DrawMiniStat("Losses", value.Losses.ToString()); DrawMiniStat("Win Rate", num.ToString("0.0") + "%"); DrawMiniStat("Highest Roll", value.HighestDiceRoll.ToString()); DrawMiniStat("Biggest Win", value.BiggestWin.ToString()); DrawMiniStat("Profit", value.Profit.ToString()); GUILayout.Label("Chess", _labelGoldStyle, Array.Empty()); DrawMiniStat("Games Played", value.ChessGamesPlayed.ToString()); DrawMiniStat("Wins", value.ChessWins.ToString()); DrawMiniStat("Losses", value.ChessLosses.ToString()); DrawMiniStat("Draws", value.ChessDraws.ToString()); DrawMiniStat("Win Rate", ChessWinRate(value).ToString("0.0") + "%"); DrawMiniStat("Current / Best Streak", value.ChessCurrentStreak + " / " + value.ChessBestStreak); DrawMiniStat("Rating", value.ChessRating.ToString()); float num2 = ((value.PokerGamesPlayed > 0) ? ((float)value.PokerHandsWon * 100f / (float)value.PokerGamesPlayed) : 0f); GUILayout.Label("Poker", _labelGoldStyle, Array.Empty()); DrawMiniStat("Hands Played", value.PokerGamesPlayed.ToString()); DrawMiniStat("Hands Won", value.PokerHandsWon.ToString()); DrawMiniStat("Winning Hands", value.PokerTablesWon.ToString()); DrawMiniStat("Win Rate", num2.ToString("0.0") + "%"); DrawMiniStat("Biggest Pot", value.PokerBiggestPot.ToString()); DrawMiniStat("Biggest Win", value.PokerBiggestWin.ToString()); DrawMiniStat("Profit", value.PokerProfit.ToString()); } 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() { string[] labels = new string[4] { "Support Forteca", "Support Fehu", "Recognition", "Roadmap" }; DrawSectionTabs(labels, ref _supportSubTab, 180f); GUILayout.BeginVertical(_boxStyle, Array.Empty()); if (_supportSubTab == 0) { GUILayout.Label("Support Forteca", _labelGoldStyle, Array.Empty()); GUILayout.Label("Support helps fund dedicated servers, domains, backups, website hosting and community infrastructure.", _labelStyle, Array.Empty()); if (GUILayout.Button("Open forteca.tip4serv.com", _buttonStyle, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(300f) })) { Application.OpenURL("https://forteca.tip4serv.com/"); } } else if (_supportSubTab == 1) { GUILayout.Label("Support Fehu", _labelGoldStyle, Array.Empty()); GUILayout.Label("Support helps fund mod development, art assets, icons, UI, models, testing servers and future systems.", _labelStyle, Array.Empty()); } else if (_supportSubTab == 2) { GUILayout.Label("Recognition", _labelGoldStyle, Array.Empty()); GUILayout.Label("Contributor - Helped build Fehu Kingdoms.\nSupporter - One-time supporter.\nSubscriber - Recurring monthly supporter.\nConstellation Member - Highest supporter tier with unique recognition.", _labelStyle, Array.Empty()); } else { GUILayout.Label("Roadmap", _labelGoldStyle, Array.Empty()); GUILayout.Label("Follow the Fehu Kingdoms roadmap as new systems move from foundation to playable content.", _labelStyle, Array.Empty()); } GUILayout.EndVertical(); } private void DrawOverview(WarfareData data, PlayerClanInfo info, Clan clan) { //IL_0606: Unknown result type (might be due to invalid IL or missing references) //IL_060b: Unknown result type (might be due to invalid IL or missing references) //IL_0610: Unknown result type (might be due to invalid IL or missing references) //IL_0613: 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_00fd: Expected O, but got Unknown //IL_00ff: 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_065b: 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); long v = ((clan != null) ? Economy.NetClanTick(data, info.ClanId) : 0); ClanTerritory capitalTerritory = GetCapitalTerritory(data, info.ClanId); ClanTerritory currentTerritory = GetCurrentTerritory(data); List vault = data.GetVault(info.ClanId); int num2 = ((clan != null) ? Economy.VaultCapacity(clan, clanTerritoryCount) : 0); GUILayout.BeginVertical(_boxStyle, Array.Empty()); if (clan != null) { GUILayout.BeginHorizontal(Array.Empty()); GUILayout.BeginVertical(_boxStyle, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(330f) }); GUILayout.BeginHorizontal(Array.Empty()); GUILayout.Box((Texture)LocalEmblemCache.GetTexture(clan.ClanEmblemName), _boxStyle, (GUILayoutOption[])(object)new GUILayoutOption[2] { GUILayout.Width(180f), GUILayout.Height(180f) }); DrawClanColorBanner(clan.Color, 60f, 180f); GUILayout.EndHorizontal(); GUILayout.Label("Clan: " + clan.Name, _labelStyle, Array.Empty()); GUILayout.Label("Tag: " + clan.Tag, _labelStyle, Array.Empty()); GUILayout.EndVertical(); GUILayout.BeginVertical(_boxStyle, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(320f) }); 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("Net Income: " + ColoredCoins(v) + " / tick", _labelStyle, Array.Empty()); GUILayout.Label("Treasury: " + 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()); DrawJarlElectionSummary(data, info, clan); if (info.Rank == ClanRank.Jarl) { GUILayout.BeginHorizontal(Array.Empty()); if (GUILayout.Button(_editingClanEmblem ? "Close Emblem Browser" : "Open Emblem Browser", _smallButtonStyle, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(160f) })) { if (_editingClanEmblem = !_editingClanEmblem) { _editingClanLinks = false; } _pendingClanEmblemName = EmblemName(clan); _emblemPage = 0; } ConfigEntry enableClanLinks = ClanConfig.EnableClanLinks; if (enableClanLinks != null && enableClanLinks.Value && GUILayout.Button(_editingClanLinks ? "Close Clan Links" : "Open Clan Links", _smallButtonStyle, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(145f) }) && (_editingClanLinks = !_editingClanLinks)) { _editingClanEmblem = false; } GUILayout.EndHorizontal(); } GUILayout.EndVertical(); GUILayout.BeginVertical(_boxStyle, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.ExpandWidth(true) }); 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(); DrawJarlElectionPanel(data, info, clan); DrawClanTaxSettings(info, clan); ConfigEntry enableClanLinks2 = ClanConfig.EnableClanLinks; if (enableClanLinks2 != null && enableClanLinks2.Value && _editingClanLinks && info.Rank == ClanRank.Jarl) { DrawClanLinksEditor(clan); } if (_editingClanEmblem && info.Rank == ClanRank.Jarl) { DrawClanEmblemBrowser(clan); } } GUILayout.EndVertical(); Vector2i grid = GridExtensions.WorldToGrid(((Component)Player.m_localPlayer).transform.position); ClanTerritory territory = data.GetTerritory(grid); GUILayout.BeginVertical(_boxStyle, Array.Empty()); if (territory != null && territory.OwnerClanId != 0) { Clan clan2 = data.GetClan(territory.OwnerClanId); string text = ((clan2 != null) ? WarfareManager.ColorHex(clan2.Color) : "888888"); bool flag = territory.OwnerClanId == info.ClanId; GUILayout.Label("Current zone: " + territory.DisplayName + " " + string.Format("({0}) - {1}, {2}-value, ", flag ? "HOME" : "ENEMY", territory.Type, Economy.ValueTierName(territory.BiomeValueTier)) + $"size {territory.SizeLevel}", _labelStyle, Array.Empty()); if (territory.RotCount > 0) { GUILayout.Label($"Corruption: {territory.RotCount}/4 - {territory.RotCount * 25}% income lost", _labelStyle, Array.Empty()); } } else { GUILayout.Label("Current zone: Unclaimed wilderness", _labelStyle, Array.Empty()); } GUILayout.Label("Claim Current Territory", _labelGoldStyle, Array.Empty()); DrawZoneActions(data, info, territory, grid); GUILayout.EndVertical(); } private void DrawProfileCharacter(WarfareData data, PlayerClanInfo info, Clan clan) { //IL_0076: Unknown result type (might be due to invalid IL or missing references) //IL_00ab: Expected O, but got Unknown //IL_042e: Unknown result type (might be due to invalid IL or missing references) //IL_0433: Unknown result type (might be due to invalid IL or missing references) //IL_043b: Unknown result type (might be due to invalid IL or missing references) //IL_0444: Expected O, but got Unknown ClanTerritory currentTerritory = 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(320f) }); GUILayout.Label("IDENTITY", _labelGoldStyle, Array.Empty()); GUILayout.Box((Texture)val, _boxStyle, (GUILayoutOption[])(object)new GUILayoutOption[2] { GUILayout.Width(180f), GUILayout.Height(180f) }); GUILayout.Label("" + (info.PlayerName ?? "Unknown") + "", _labelGoldStyle, Array.Empty()); DrawProfileLine("Player ID", info.PlayerId.ToString()); DrawProfileLine("Clan", (clan != null) ? clan.Name : "None"); DrawProfileLine("Tag", (clan != null) ? clan.Tag : "-"); DrawProfileLine("Rank", info.Rank.ToString()); DrawProfileLine("Title", ProfileTitle(info, clan)); DrawProfileLine("Primary", RoleName(info.PrimaryRole)); DrawProfileLine("Secondary", RoleName(info.SecondaryRole)); DrawProfileLine("Tertiary", RoleName(info.TertiaryRole)); DrawProfileLine("Home", (capitalTerritory != null) ? capitalTerritory.DisplayName : "None"); DrawProfileLine("Current", (currentTerritory != null) ? currentTerritory.DisplayName : "Unclaimed wilderness"); GUILayout.EndVertical(); GUILayout.BeginVertical(_boxStyle, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.ExpandWidth(true) }); GUILayout.Label("ABOUT ME", _labelGoldStyle, Array.Empty()); if (string.IsNullOrEmpty(_profileAboutDraft)) { _profileAboutDraft = info.AboutMe ?? ""; } int num = Mathf.Clamp(ClanConfig.ProfileAboutMaxLength?.Value ?? 500, 0, 2000); _profileAboutDraft = GUILayout.TextArea(_profileAboutDraft, num, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Height(92f) }); 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?.RequestUpdateProfile(_profileAboutDraft, info.ActiveTitle ?? ""); } GUILayout.EndHorizontal(); GUILayout.Space(8f); GUILayout.Label("FEHU LEGACY SCORE", _labelGoldStyle, Array.Empty()); 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 = (info.SagaEntries ?? new List()).Count * 10; int num10 = num2 + num3 + num4 + num5 + num6 + num7 + num8 + num9; GUIStyle val2 = new GUIStyle(_labelGoldStyle) { fontSize = 30, alignment = (TextAnchor)4 }; GUILayout.Label("" + num10 + "", val2, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Height(42f) }); GUILayout.BeginHorizontal(Array.Empty()); DrawProfileMetric("Territory", num2); DrawProfileMetric("Warfare", num3); DrawProfileMetric("Economy", num4); DrawProfileMetric("Building", num5); GUILayout.EndHorizontal(); GUILayout.BeginHorizontal(Array.Empty()); DrawProfileMetric("Exploration", num6); DrawProfileMetric("Clan Support", num7); DrawProfileMetric("Diplomacy", num8); DrawProfileMetric("Saga", num9); GUILayout.EndHorizontal(); GUILayout.EndVertical(); GUILayout.EndHorizontal(); GUILayout.Space(8f); 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, "Days active: " + info.TotalPlayMinutes / 1440, "Current biome: " + ((currentTerritory != null) ? BiomeDisplayName(currentTerritory) : "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(8f); GUILayout.BeginHorizontal(Array.Empty()); GUILayout.BeginVertical(_boxStyle, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(430f) }); GUILayout.Label("ROLE CAREER", _labelGoldStyle, Array.Empty()); RoleData[] roleDefinitions = RoleDefinitions; foreach (RoleData roleData in roleDefinitions) { if (roleData.Selectable && info.RoleMinutes.TryGetValue((int)roleData.Role, out var value)) { GUILayout.Label(roleData.DisplayName + ": " + FormatProfileDuration(value), _labelStyle, Array.Empty()); } } GUILayout.Label("Primary 100% | Secondary 60% | Tertiary 40%", _labelStyle, Array.Empty()); GUILayout.EndVertical(); GUILayout.BeginVertical(_boxStyle, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.ExpandWidth(true) }); GUILayout.Label("ACHIEVEMENTS / BADGES", _labelGoldStyle, Array.Empty()); GUILayout.Label("Featured achievements, trophies and medals will unlock from real Fehu events.", _labelStyle, Array.Empty()); GUILayout.BeginHorizontal(Array.Empty()); for (int j = 0; j < 9; j++) { GUILayout.Box((j < 3) ? "Achievement" : ((j < 6) ? "Trophy" : "Medal"), _boxStyle, (GUILayoutOption[])(object)new GUILayoutOption[2] { GUILayout.Width(92f), GUILayout.Height(46f) }); } GUILayout.EndHorizontal(); GUILayout.EndVertical(); GUILayout.EndHorizontal(); DrawProfileSaga(info); } private void DrawProfileHistory(PlayerClanInfo info) { DrawRoadmapInfoCard("History", "PROFILE HISTORY", "Personal milestones are recorded only when real Fehu actions occur.", "The profile timeline below is cached and does not scan the world."); DrawProfileSaga(info); } private void DrawProfileSaga(PlayerClanInfo info) { GUILayout.BeginVertical(_boxStyle, Array.Empty()); GUILayout.Label("PERSONAL SAGA", _labelGoldStyle, Array.Empty()); List list = info.SagaEntries ?? new List(); if (list.Count == 0) { GUILayout.Label("No personal saga entries yet.", _labelStyle, Array.Empty()); } else { for (int i = Mathf.Max(0, list.Count - 8); i < list.Count; i++) { GUILayout.Label(list[i], _labelStyle, Array.Empty()); } } GUILayout.EndVertical(); } private void DrawProfileLine(string label, string value) { GUILayout.Label("" + label + ": " + (value ?? "Unknown"), _labelStyle, Array.Empty()); } private void DrawProfileMetric(string label, int value) { GUILayout.BeginVertical(_boxStyle, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(145f) }); 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[1] { GUILayout.Width(420f) }); 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) { 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 "No Title Selected"; } 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(); 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, Array.Empty()); GUILayout.Label("Jarl Election", _labelGoldStyle, Array.Empty()); GUILayout.BeginHorizontal(Array.Empty()); GUILayout.BeginVertical((GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(360f) }); DrawMiniStat("Status", (!flag) ? "No election" : (tied ? "Tie extended" : "Active")); DrawMiniStat("Voting ends", flag ? FormatTimestamp(clan.NextElectionTimestamp) : "Not scheduled"); DrawMiniStat("Time remaining", flag ? FormatDuration(Math.Max(0L, clan.NextElectionTimestamp - num)) : "-"); DrawMiniStat("Eligible voters", clanMemberCount.ToString()); DrawMiniStat("Votes cast", votes.ToString()); DrawMiniStat("Required turnout", required + " (" + (ClanConfig.JarlElectionMinimumTurnoutPercent?.Value ?? 50) + "%)"); DrawMiniStat("Current leader", (electionLeader != null) ? (electionLeader.PlayerName + " (" + votes2 + " votes" + (tied ? ", tied" : "") + ")") : "None"); DrawMiniStat("Your vote", flag2 ? "Voted" : "Not voted"); GUILayout.EndVertical(); GUILayout.BeginVertical((GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.ExpandWidth(true) }); GUILayout.Label("Candidates voted for:", _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); if (!flag) { GUILayout.Label("Voting opens during the configured election window.", _labelStyle, Array.Empty()); } else if (info.ClanId != clan.ClanId || info.ClanId == 0) { GUILayout.Label("Only current clan members may vote.", _labelStyle, Array.Empty()); } else { GUILayout.Label("" + (flag2 ? "Change Vote" : "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) % 3 == 0) { GUILayout.EndHorizontal(); GUILayout.BeginHorizontal(Array.Empty()); } } GUILayout.EndHorizontal(); } GUILayout.EndVertical(); GUILayout.EndHorizontal(); LogElectionUi(data, clan, flag, clanMemberCount, votes, required, electionVoteTotals); 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[1] { GUILayout.Width(44f) })) { 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_02c4: 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) ? (flag2 ? "Seize this rotted territory" : "Claim this territory") : ((GetFreebornTerritories(data, info.PlayerId, TerritoryType.Homestead).Count == 0) ? "Claim Homestead" : "Claim Outpost"), _buttonStyle, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Height(28f) })) { 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(28f) })) { WarfareRPC.Instance?.RequestStartVaultCrack(grid); } } else { GUILayout.Label("Vault crack on cooldown.", _labelStyle, Array.Empty()); } } if (flag3 && !flag2 && RoleUtils.HasRole(info, ClanRole.Shaman) && GUILayout.Button("Raise Rot Totem here", _buttonStyle, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Height(28f) })) { WarfareRPC.Instance?.RequestPlaceRotTotem(); } if (flag4 && info.ClanId != 0 && info.Rank >= ClanRank.Officer && GUILayout.Button("Challenge Odin's Peace", _buttonStyle, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Height(28f) })) { WarfareRPC.Instance?.RequestChallengeFreeborn(grid); } } private void DrawMembers(WarfareData data, PlayerClanInfo info, Clan clan) { //IL_0016: Unknown result type (might be due to invalid IL or missing references) //IL_0021: Expected O, but got Unknown //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_0045: 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_0244: 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) HashSet online = new HashSet(); if ((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) ? string.Compare(a.PlayerName, b.PlayerName, StringComparison.OrdinalIgnoreCase) : b.Rank.CompareTo(a.Rank); }); 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(); _scrollA = GUILayout.BeginScrollView(_scrollA, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.ExpandHeight(true) }); GUILayout.BeginHorizontal(_boxStyle, Array.Empty()); GUILayout.Label("Status", _labelGoldStyle, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(70f) }); GUILayout.Label("Name", _labelGoldStyle, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(180f) }); GUILayout.Label("Rank", _labelGoldStyle, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(90f) }); GUILayout.Label("Primary", _labelGoldStyle, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(120f) }); GUILayout.Label("Secondary", _labelGoldStyle, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(110f) }); GUILayout.Label("Tertiary", _labelGoldStyle, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(100f) }); GUILayout.Label("Vault Rights", _labelGoldStyle, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(340f) }); GUILayout.FlexibleSpace(); GUILayout.Label("Vote", _labelGoldStyle, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(54f) }); GUILayout.EndHorizontal(); 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(70f) }); GUILayout.Label("" + item2.PlayerName + "", _labelStyle, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(180f) }); GUILayout.Label((flag3 ? "Jarl" : item2.Rank.ToString()) ?? "", _labelStyle, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(90f) }); GUILayout.Label(RoleName(item2.PrimaryRole), _labelStyle, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(120f) }); GUILayout.Label(RoleName(item2.SecondaryRole), _labelStyle, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(110f) }); GUILayout.Label(RoleName(item2.TertiaryRole), _labelStyle, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(100f) }); if (flag2) { WarfareManager.Instance?.EnsureVaultPermissions(item2); GUILayout.BeginHorizontal((GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(340f) }); GUILayout.Label("Deposit:", _labelStyle, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(58f) }); VaultPermissionButton(item2, 3, "Coins", item2.CanDepositVaultCoins || item2.Rank == ClanRank.Jarl); VaultPermissionButton(item2, 1, "Items", item2.CanDepositVaultItems || item2.Rank == ClanRank.Jarl); GUILayout.Label("Withdraw:", _labelStyle, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(74f) }); VaultPermissionButton(item2, 4, "Coins", item2.CanWithdrawVaultCoins || item2.Rank == ClanRank.Jarl); VaultPermissionButton(item2, 2, "Items", item2.CanWithdrawVaultItems || item2.Rank == ClanRank.Jarl); GUILayout.EndHorizontal(); } else { GUILayout.Label("-", _labelStyle, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(340f) }); } if (item2.Bounty > 0) { GUILayout.Label($"{item2.Bounty}c bounty", _labelStyle, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(110f) }); } GUILayout.FlexibleSpace(); if (GUILayout.Button("Vote", _smallButtonStyle, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(54f) })) { 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(74f) })) { WarfareRPC.Instance?.RequestSetRank(item2.PlayerId, ClanRank.Officer); } else if (item2.Rank == ClanRank.Officer && GUILayout.Button("Demote", _smallButtonStyle, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(74f) })) { 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(54f) })) { WarfareRPC.Instance?.RequestKickMember(item2.PlayerId); } GUILayout.EndHorizontal(); } GUILayout.EndScrollView(); GUILayout.BeginHorizontal(Array.Empty()); if (GUILayout.Button("Leave Clan", _buttonStyle, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(140f) })) { WarfareRPC.Instance?.RequestLeaveClan(); } if (flag2 && GUILayout.Button("Disband Clan", _buttonStyle, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(150f) })) { WarfareRPC.Instance?.RequestDisbandClan(); } GUILayout.EndHorizontal(); } 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(62f) })) { WarfareRPC.Instance?.RequestSetVaultPermission(member.PlayerId, permission, !enabled); } GUI.enabled = true; } private void DrawBuildings(WarfareData data, PlayerClanInfo info) { //IL_0096: 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_00b4: 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_0134: Unknown result type (might be due to invalid IL or missing references) List buildingTerritories = GetBuildingTerritories(data, info); if (buildingTerritories.Count == 0) { GUILayout.Label("No owned territories available for kingdom buildings.", _labelStyle, Array.Empty()); return; } if (_selectedBuildingTerritoryIndex < 0 || _selectedBuildingTerritoryIndex >= buildingTerritories.Count) { _selectedBuildingTerritoryIndex = 0; } ClanTerritory territory = buildingTerritories[_selectedBuildingTerritoryIndex]; GUILayout.BeginHorizontal(Array.Empty()); GUILayout.BeginVertical(_boxStyle, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(360f) }); GUILayout.Label("Territory Selection", _labelGoldStyle, Array.Empty()); _buildingsTerritoryScroll = GUILayout.BeginScrollView(_buildingsTerritoryScroll, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.ExpandHeight(true) }); 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, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.ExpandHeight(true) }); 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) { //IL_003c: Unknown result type (might be due to invalid IL or missing references) //IL_0071: Expected O, but got Unknown List list = WarfareManager.EnsureBuildings(territory); GUILayout.BeginHorizontal(selected ? _tabActiveStyle : _boxStyle, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.MinHeight(96f) }); GUILayout.Box((Texture)GetBiomeTexture(territory), _boxStyle, (GUILayoutOption[])(object)new GUILayoutOption[2] { GUILayout.Width(60f), GUILayout.Height(60f) }); GUILayout.BeginVertical((GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(205f) }); GUILayout.Label("" + territory.DisplayName + "", selected ? _labelGoldStyle : _labelStyle, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.MinHeight(34f) }); 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(68f), GUILayout.Height(28f) })) { _selectedBuildingTerritoryIndex = index; GUIUtility.ExitGUI(); } GUILayout.EndHorizontal(); } private void DrawSelectedBuildingTerritory(WarfareData data, PlayerClanInfo info, ClanTerritory territory) { //IL_0032: Unknown result type (might be due to invalid IL or missing references) //IL_0067: Expected O, but got Unknown List list = WarfareManager.EnsureBuildings(territory); int buildingSlots = WarfareManager.GetBuildingSlots(territory); float globalMultiplier = Economy.GlobalUpkeepMultiplier(GetBuildingTerritories(data, info).Count); GUILayout.BeginHorizontal(Array.Empty()); GUILayout.Box((Texture)GetBiomeTexture(territory), _boxStyle, (GUILayoutOption[])(object)new GUILayoutOption[2] { GUILayout.Width(86f), GUILayout.Height(86f) }); GUILayout.BeginVertical(Array.Empty()); GUILayout.Label("" + territory.DisplayName + "", _labelGoldStyle, Array.Empty()); GUILayout.Label("Biome: " + BiomeDisplayName(territory) + " Type: " + territory.Type.ToString() + "", _labelStyle, Array.Empty()); long num = Economy.ApplyBuildingUpkeepReduction(territory, Economy.TerritoryUpkeep(territory, globalMultiplier)); GUILayout.Label("Income: +" + Economy.TerritoryIncome(territory) + "/tick Upkeep: -" + num + "/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(8f); GUILayout.BeginHorizontal(Array.Empty()); GUILayout.BeginVertical((GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(500f) }); GUILayout.Label("Build Type", _labelGoldStyle, Array.Empty()); DrawBuildTypeSelector(territory); GUILayout.EndVertical(); GUILayout.BeginVertical(_boxStyle, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.ExpandWidth(true) }); GUILayout.Label("" + WarfareManager.BuildingName(_selectedBuildType) + "", _labelGoldStyle, Array.Empty()); GUILayout.Label(WarfareManager.IsBuildingAllowedInBiome(territory, _selectedBuildType) ? "Available here" : "Biome or territory requirement not met", _labelStyle, Array.Empty()); GUILayout.Label(BuildingPreviewText(_selectedBuildType), _labelStyle, Array.Empty()); GUILayout.EndVertical(); GUILayout.EndHorizontal(); GUILayout.Space(8f); GUILayout.Label("Building Slots", _labelGoldStyle, Array.Empty()); DrawBuildingSlotsGrid(territory, list, buildingSlots); DrawBuildingProductionLog(data); } private void DrawBuildingSlotsGrid(ClanTerritory territory, List buildings, int slots) { float num = Mathf.Max(420f, (((Rect)(ref _windowRect)).width - 560f) / 2f); for (int i = 0; i < slots; i += 2) { GUILayout.BeginHorizontal(Array.Empty()); TerritoryBuilding building = ((i < buildings.Count) ? buildings[i] : null); DrawBuildingSlotTile(territory, buildings, building, i, num); if (i + 1 < slots) { TerritoryBuilding building2 = ((i + 1 < buildings.Count) ? buildings[i + 1] : null); DrawBuildingSlotTile(territory, buildings, building2, i + 1, num); } else { GUILayout.Space(num + 8f); } GUILayout.EndHorizontal(); GUILayout.Space(6f); } } private void DrawBuildingSlotTile(ClanTerritory territory, List buildings, TerritoryBuilding building, int slot, float tileWidth) { //IL_003e: Unknown result type (might be due to invalid IL or missing references) //IL_0073: Expected O, but got Unknown //IL_02e3: Unknown result type (might be due to invalid IL or missing references) //IL_0246: Unknown result type (might be due to invalid IL or missing references) //IL_033f: Unknown result type (might be due to invalid IL or missing references) //IL_0396: Unknown result type (might be due to invalid IL or missing references) GUILayout.BeginVertical(_boxStyle, (GUILayoutOption[])(object)new GUILayoutOption[2] { GUILayout.Width(tileWidth), GUILayout.MinHeight(128f) }); GUILayout.BeginHorizontal(Array.Empty()); GUILayout.Box((Texture)GetBuildingTexture(building), _boxStyle, (GUILayoutOption[])(object)new GUILayoutOption[2] { GUILayout.Width(48f), GUILayout.Height(48f) }); 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("Ready: " + WarfareManager.BuildingName(_selectedBuildType), _labelStyle, Array.Empty()); GUILayout.Label((!num) ? "Biome requirement not met" : (flag ? "Copy limit reached" : "Production: none"), _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("Production: " + WarfareManager.BuildingProductionText(territory, building), _labelStyle, Array.Empty()); } GUILayout.EndVertical(); GUILayout.EndHorizontal(); GUILayout.Space(4f); 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[1] { GUILayout.Height(28f) })) { 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", _smallButtonStyle, (GUILayoutOption[])(object)new GUILayoutOption[2] { GUILayout.Width(82f), GUILayout.Height(28f) })) { 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(28f) })) { 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(28f) })) { 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(); GUILayout.BeginHorizontal(Array.Empty()); for (int i = 0; i < buildingCategoryNames.Length; i++) { if (GUILayout.Button(((_selectedBuildingCategory == i) ? "[" : "") + buildingCategoryNames[i] + ((_selectedBuildingCategory == i) ? "]" : ""), _smallButtonStyle, (GUILayoutOption[])(object)new GUILayoutOption[2] { GUILayout.Width(96f), GUILayout.Height(24f) })) { _selectedBuildingCategory = i; TerritoryBuildingType[] buildingCategoryTypes = WarfareManager.GetBuildingCategoryTypes(i); if (buildingCategoryTypes.Length != 0) { _selectedBuildType = buildingCategoryTypes[0]; } } } GUILayout.EndHorizontal(); TerritoryBuildingType[] buildingCategoryTypes2 = WarfareManager.GetBuildingCategoryTypes(_selectedBuildingCategory); GUILayout.BeginHorizontal(Array.Empty()); TerritoryBuildingType[] array = buildingCategoryTypes2; foreach (TerritoryBuildingType territoryBuildingType in array) { bool flag = WarfareManager.IsBuildingAllowedInBiome(territory, territoryBuildingType); if (GUILayout.Button(((_selectedBuildType == territoryBuildingType) ? "[" : "") + WarfareManager.BuildingName(territoryBuildingType) + ((_selectedBuildType == territoryBuildingType) ? "]" : ""), flag ? _smallButtonStyle : _tabStyle, (GUILayoutOption[])(object)new GUILayoutOption[2] { GUILayout.Width(126f), GUILayout.Height(26f) })) { _selectedBuildType = territoryBuildingType; } } GUILayout.EndHorizontal(); } 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_0031: Unknown result type (might be due to invalid IL or missing references) //IL_0066: Expected O, but got Unknown //IL_02de: 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_0339: 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) List buildings = WarfareManager.EnsureBuildings(territory); GUILayout.BeginHorizontal(_boxStyle, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Height(58f) }); GUILayout.Box((Texture)GetBuildingTexture(building), _boxStyle, (GUILayoutOption[])(object)new GUILayoutOption[2] { GUILayout.Width(44f), GUILayout.Height(44f) }); 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(28f) })) { 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(28f) })) { 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(28f) })) { 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(28f) })) { WarfareRPC.Instance?.RequestTerritoryBuildingAction(territory.GridPosition, 3, TerritoryBuildingType.None, slot); } GUILayout.EndHorizontal(); GUILayout.Label(BuildingUpgradePreviewText(building), _labelStyle, Array.Empty()); } GUILayout.EndVertical(); GUILayout.EndHorizontal(); } private string NextBuildingProductionText(ClanTerritory territory) { List list = WarfareManager.EnsureBuildings(territory); int num = Mathf.Max(1, ClanConfig.BuildingProductionHours?.Value ?? 12) * 3600; long num2 = DateTimeOffset.UtcNow.ToUnixTimeSeconds(); long num3 = 0L; foreach (TerritoryBuilding item in list) { if (item == null || item.Damaged || item.Type == TerritoryBuildingType.None) { continue; } string text = WarfareManager.BuildingProductionValueText(territory, item); if (!(text == "No production") && !text.StartsWith("Defense", StringComparison.OrdinalIgnoreCase) && !text.StartsWith("Capital", StringComparison.OrdinalIgnoreCase)) { long num4 = ((item.LastProductionTimestamp > 0) ? item.LastProductionTimestamp : num2) + num; if (num3 == 0L || num4 < num3) { num3 = num4; } } } if (num3 == 0L) { return "No active production. Build or upgrade production buildings to generate resources."; } return FormatDuration(Math.Max(0L, num3 - num2)) + " remaining"; } private string BuildingProductionStatusText(ClanTerritory territory) { List list = WarfareManager.EnsureBuildings(territory); int num = 0; List list2 = new List(); foreach (TerritoryBuilding item in list) { if (item == null || item.Damaged || item.Type == TerritoryBuildingType.None) { continue; } string text = WarfareManager.BuildingProductionValueText(territory, item); if (!(text == "No production") && !text.StartsWith("Defense", StringComparison.OrdinalIgnoreCase) && !text.StartsWith("Capital", StringComparison.OrdinalIgnoreCase)) { num++; if (list2.Count < 2) { list2.Add(WarfareManager.BuildingName(item.Type) + " " + text); } } } if (num == 0) { return "Idle"; } return "Active: " + string.Join(", ", list2.ToArray()) + ((num > list2.Count) ? $" +{num - list2.Count} more" : ""); } private string FormatDuration(long seconds) { long num = seconds / 3600; long num2 = seconds % 3600 / 60; return num.ToString("00") + "h " + num2.ToString("00") + "m"; } 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", _labelGoldStyle, 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_0122: 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_0140: Unknown result type (might be due to invalid IL or missing references) List clanTerritories = data.GetClanTerritories(info.ClanId); if (clanTerritories.Count == 0) { GUILayout.Label("Your clan holds no territory. Explorers claim land from the map.", _labelStyle, Array.Empty()); 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))); } GUILayout.BeginHorizontal(Array.Empty()); DrawSummaryCard("Total Territory Income", "+" + num2 + "/tick", 250f); DrawSummaryCard("Total Territory Upkeep", "-" + num3 + "/tick", 250f); DrawSummaryCard("Net Territory Value", ColoredCoins(num2 - num3) + "/tick", 250f); GUILayout.EndHorizontal(); _scrollB = GUILayout.BeginScrollView(_scrollB, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.ExpandHeight(true) }); int num4 = ((((Rect)(ref _windowRect)).width >= 1180f) ? 3 : 2); float num5 = Mathf.Max(360f, (((Rect)(ref _windowRect)).width - 76f - (float)(num4 - 1) * 8f) / (float)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.EndScrollView(); } private void DrawTerritoryCard(WarfareData data, PlayerClanInfo info, ClanTerritory item, bool canManage, float upkeepMultiplier, float width) { //IL_0089: Unknown result type (might be due to invalid IL or missing references) //IL_00be: Expected O, but got Unknown //IL_02a2: Unknown result type (might be due to invalid IL or missing references) //IL_03df: Unknown result type (might be due to invalid IL or missing references) //IL_0441: Unknown result type (might be due to invalid IL or missing references) //IL_0494: 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(188f) }); GUILayout.BeginHorizontal(Array.Empty()); GUILayout.Box((Texture)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(); 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(24f) }); if (GUILayout.Button("Save", _smallButtonStyle, (GUILayoutOption[])(object)new GUILayoutOption[2] { GUILayout.Width(82f), GUILayout.Height(30f) })) { WarfareRPC.Instance?.RequestRenameTerritory(item.GridPosition, _renameInput); _renameTarget = null; } if (GUILayout.Button("Cancel", _smallButtonStyle, (GUILayoutOption[])(object)new GUILayoutOption[2] { GUILayout.Width(82f), GUILayout.Height(30f) })) { _renameTarget = null; } } else { if (GUILayout.Button("Rename", _smallButtonStyle, (GUILayoutOption[])(object)new GUILayoutOption[2] { GUILayout.Width(82f), GUILayout.Height(30f) })) { _renameTarget = text; _renameInput = item.CustomName ?? ""; } GUI.enabled = canManage; int num3 = ClanConfig.MaxTerritorySizeLevel?.Value ?? 5; if (GUILayout.Button((item.SizeLevel < num3) ? $"Upgrade ({Economy.TerritoryUpgradeCost(item.SizeLevel)}c)" : "Max Size", _smallButtonStyle, (GUILayoutOption[])(object)new GUILayoutOption[2] { GUILayout.Width(124f), GUILayout.Height(30f) }) && item.SizeLevel < num3) { WarfareRPC.Instance?.RequestUpgradeTerritory(item.GridPosition); } GUI.enabled = canManage && !item.IsCapital; if (GUILayout.Button("Make Capital", _smallButtonStyle, (GUILayoutOption[])(object)new GUILayoutOption[2] { GUILayout.Width(106f), GUILayout.Height(30f) })) { WarfareRPC.Instance?.RequestSetCapital(item.GridPosition); } GUI.enabled = true; if (GUILayout.Button("Discover (3000c)", _smallButtonStyle, (GUILayoutOption[])(object)new GUILayoutOption[2] { GUILayout.Width(132f), GUILayout.Height(30f) })) { 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; } List clanTerritories = data.GetClanTerritories(info.ClanId); int count = clanTerritories.Count; float num = Economy.GlobalUpkeepMultiplier(count); long num2 = 0L; long num3 = 0L; long num4 = 0L; long num5 = 0L; foreach (ClanTerritory item in clanTerritories) { long num6 = Economy.TerritoryIncome(item); if (item.Type == TerritoryType.Capital) { num2 += num6; } else if (item.Type == TerritoryType.Outpost) { num4 += num6; } else { num3 += num6; } num5 += Economy.ApplyBuildingUpkeepReduction(item, Economy.ApplyWardenUpkeepReduction(data, info.ClanId, Economy.TerritoryUpkeep(item, num))); } long num7 = num2 + num3 + num4; long num8 = 0L; long num9 = num5 + num8; GUILayout.BeginHorizontal(Array.Empty()); DrawSummaryCard("Gross", "+" + num7 + "/tick", 220f); DrawSummaryCard("Upkeep", "-" + num9 + "/tick", 220f); DrawSummaryCard("Net", ColoredCoins(num7 - num9) + "/tick", 220f); GUILayout.EndHorizontal(); GUILayout.BeginHorizontal(Array.Empty()); GUILayout.BeginVertical(_boxStyle, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(360f) }); GUILayout.Label("Income Sources", _labelGoldStyle, Array.Empty()); DrawMiniStat("Capital", "+" + num2 + "/tick"); DrawMiniStat("Colonies", "+" + num3 + "/tick"); DrawMiniStat("Outposts", "+" + num4 + "/tick"); DrawMiniStat("Total", "+" + num7 + "/tick"); GUILayout.EndVertical(); GUILayout.BeginVertical(_boxStyle, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(360f) }); GUILayout.Label("Upkeep", _labelGoldStyle, Array.Empty()); DrawMiniStat("Territories", "-" + num5 + "/tick"); DrawMiniStat("Vault", "-" + num8 + "/tick"); DrawMiniStat("Total", "-" + num9 + "/tick"); GUILayout.Label($"Multiplier: x{num:F2} ({EconomyBandColored(count)})", _labelStyle, Array.Empty()); GUILayout.EndVertical(); GUILayout.BeginVertical(_boxStyle, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.ExpandWidth(true) }); GUILayout.Label("Summary", _labelGoldStyle, Array.Empty()); DrawMiniStat("Gross income", "+" + num7 + "/tick"); DrawMiniStat("Upkeep", "-" + num9 + "/tick"); DrawMiniStat("Net income", ColoredCoins(num7 - num9) + "/tick"); DrawMiniStat("Treasury", "" + 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(_boxStyle, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(520f) }); int num10 = Economy.VaultCapacity(clan, count); GUILayout.Label($"Vault: tier {clan.VaultTier}, capacity {num10} stacks ({data.GetVaultStackCount(info.ClanId)} used)", _labelStyle, Array.Empty()); int num11 = ClanConfig.MaxVaultTier?.Value ?? 5; GUI.enabled = flag && clan.VaultTier < num11; if (GUILayout.Button((clan.VaultTier < num11) ? $"Upgrade Vault to tier {clan.VaultTier + 1} ({Economy.VaultUpgradeCost(clan.VaultTier)}c)" : "Vault at maximum tier", _buttonStyle, Array.Empty())) { WarfareRPC.Instance?.RequestUpgradeVault(); } GUI.enabled = true; GUILayout.EndVertical(); GUILayout.BeginVertical(_boxStyle, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.ExpandWidth(true) }); 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)", _buttonStyle, Array.Empty())) { WarfareRPC.Instance?.RequestBuyExpansion(); } GUI.enabled = true; GUILayout.EndVertical(); GUILayout.EndHorizontal(); } private void DrawWar(WarfareData data, PlayerClanInfo info, Clan clan) { float num = Mathf.Max(690f, ((Rect)(ref _windowRect)).height - 225f); 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(30f) })) { _warSubTab = i; GUIUtility.ExitGUI(); } } GUILayout.EndHorizontal(); GUILayout.Space(6f); float panelHeight = Mathf.Max(620f, num - 44f); float panelWidth = Mathf.Max(1120f, ((Rect)(ref _windowRect)).width - 54f); 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) { GUILayout.BeginVertical(_boxStyle, (GUILayoutOption[])(object)new GUILayoutOption[2] { GUILayout.Height(panelHeight), GUILayout.Width(panelWidth) }); GUILayout.Label("Wars", _labelGoldStyle, Array.Empty()); GUILayout.BeginHorizontal(Array.Empty()); DrawDeclareWarPanel(data, info, panelHeight); GUILayout.BeginVertical((GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.ExpandWidth(true) }); DrawActiveWarsPanel(data, info, Mathf.Max(250f, panelHeight * 0.48f), Mathf.Max(620f, panelWidth - 470f)); GUILayout.BeginHorizontal(Array.Empty()); GUILayout.BeginVertical(_boxStyle, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(300f) }); 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[1] { GUILayout.Width(300f) }); 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[1] { GUILayout.ExpandWidth(true) }); 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_0026: Unknown result type (might be due to invalid IL or missing references) //IL_0031: Expected O, but got Unknown //IL_0040: 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_00fb: Unknown result type (might be due to invalid IL or missing references) //IL_0106: Expected O, but got Unknown //IL_017c: Unknown result type (might be due to invalid IL or missing references) //IL_0181: Unknown result type (might be due to invalid IL or missing references) //IL_0188: Unknown result type (might be due to invalid IL or missing references) //IL_0192: Expected O, but got Unknown //IL_010a: Unknown result type (might be due to invalid IL or missing references) //IL_013f: Expected O, but got Unknown //IL_0302: 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)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)worldEventTexture != (Object)null) { GUILayout.Box((Texture)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(24f) })) { 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_0007: Unknown result type (might be due to invalid IL or missing references) //IL_0012: Expected O, but got Unknown //IL_001a: 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_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) //IL_0046: Unknown result type (might be due to invalid IL or missing references) Player localPlayer = Player.m_localPlayer; if ((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_0089: 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_0155: 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 - 24f) }); 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, false, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Height(Mathf.Max(460f, panelHeight - 118f)) }); 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)(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.Height(panelHeight), GUILayout.Width(panelWidth) }); 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, false, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Height(Mathf.Max(440f, panelHeight - 78f)) }); 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_0096: 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_028c: Unknown result type (might be due to invalid IL or missing references) //IL_02b7: Unknown result type (might be due to invalid IL or missing references) //IL_02bc: Unknown result type (might be due to invalid IL or missing references) GUILayout.BeginVertical(_boxStyle, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Height(panelHeight) }); GUILayout.Label("Bounties", _labelGoldStyle, Array.Empty()); GUILayout.BeginHorizontal(Array.Empty()); float num = Mathf.Max(560f, (((Rect)(ref _windowRect)).width - 72f) * 0.5f); 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(Mathf.Max(420f, panelHeight - 82f)) }); 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(Mathf.Max(420f, panelHeight - 118f)) }); DrawBountyTargets(data, info); GUILayout.EndScrollView(); GUILayout.EndVertical(); GUILayout.EndHorizontal(); GUILayout.EndVertical(); } private void DrawBountyRow(string target, string targetType, long reward, string issuer, string status) { GUILayout.BeginHorizontal(_boxStyle, Array.Empty()); GUILayout.Label(target, _labelGoldStyle, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(270f) }); GUILayout.Label(targetType, _labelStyle, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(96f) }); GUILayout.Label("" + reward + "c", _labelStyle, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(92f) }); GUILayout.Label(issuer, _labelStyle, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(130f) }); 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(300f) }); 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(145f) }); GUI.enabled = flag && num > 0; if (GUILayout.Button("Place", _smallButtonStyle, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(76f) })) { 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(300f) }); 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(145f) }); GUI.enabled = flag && num > 0; if (GUILayout.Button("Place", _smallButtonStyle, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(76f) })) { 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_00e7: 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) GUILayout.BeginVertical(_boxStyle, (GUILayoutOption[])(object)new GUILayoutOption[2] { GUILayout.Height(panelHeight), 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_00d9: 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)(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[1] { GUILayout.Width(76f) })) { WarfareRPC.Instance?.RequestDiplomacy(target.ClanId, (diplomacyState != DiplomacyState.Friendly) ? 1 : 2); } if (GUILayout.Button("Neutral", _smallButtonStyle, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(76f) })) { WarfareRPC.Instance?.RequestDiplomacy(target.ClanId, 3); } if (GUILayout.Button("Hostile", _smallButtonStyle, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(76f) })) { WarfareRPC.Instance?.RequestDiplomacy(target.ClanId, 4); } GUILayout.EndHorizontal(); GUILayout.BeginHorizontal(Array.Empty()); if (GUILayout.Button("Invite", _smallButtonStyle, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(76f) })) { WarfareRPC.Instance?.RequestDiplomacyV1(11, target.ClanId, 0, "", 0L); } if (GUILayout.Button("Accept", _smallButtonStyle, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(76f) })) { WarfareRPC.Instance?.RequestDiplomacyV1(12, target.ClanId, 0, "", 0L); } if (GUILayout.Button("Kick", _smallButtonStyle, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(76f) })) { 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[1] { GUILayout.Width(130f) })) { 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[1] { GUILayout.Width(76f) })) { WarfareRPC.Instance?.RequestDiplomacyV1(12, alliance2.AllianceLeaderClanId, 0, "", 0L); } if (GUILayout.Button("Decline", _smallButtonStyle, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(76f) })) { 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[1] { GUILayout.Width(76f) })) { 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.Height(panelHeight) }); 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[1] { GUILayout.Width(100f) })) { _territoryDealMode = 0; } if (GUILayout.Button((_territoryDealMode == 1) ? "[Sell]" : "Sell", (_territoryDealMode == 1) ? _tabActiveStyle : _smallButtonStyle, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(100f) })) { _territoryDealMode = 1; } if (GUILayout.Button((_territoryDealMode == 2) ? "[Exchange]" : "Exchange", (_territoryDealMode == 2) ? _tabActiveStyle : _smallButtonStyle, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(130f) })) { _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(28f) })) { 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(30f) })) { 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[1] { GUILayout.Width(76f) })) { WarfareRPC.Instance?.RequestDiplomacyV1(42, 0, territoryTransferOffer.OfferId, "", 0L); } if (GUILayout.Button("Decline", _smallButtonStyle, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(76f) })) { WarfareRPC.Instance?.RequestDiplomacyV1(43, 0, territoryTransferOffer.OfferId, "", 0L); } } else if (GUILayout.Button("Cancel", _smallButtonStyle, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(76f) })) { 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 — 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(30f) })) { 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 = Mathf.Max(920f, ((Rect)(ref _windowRect)).width - 48f); float num4 = Mathf.Clamp(num3 * 0.31f, 300f, 390f); float num5 = Mathf.Clamp(num3 * 0.22f, 240f, 290f); float num6 = Mathf.Max(360f, num3 - num4 - num5 - 16f); float num7 = Mathf.Clamp(((Rect)(ref _windowRect)).height - 390f, 400f, 560f); 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); 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[1] { GUILayout.Width(54f) }); DrawVaultQuantityButtons(ref _vaultItemAmountInput, _selectedInventoryVaultStack, ref _vaultItemQuantityStep); int result; int num8 = (int.TryParse(_vaultItemAmountInput, out result) ? result : 0); if (_selectedInventoryVaultStack > 0) { num8 = Mathf.Clamp(num8, 1, _selectedInventoryVaultStack); _vaultItemAmountInput = num8.ToString(); } GUI.enabled = flag3 && !string.IsNullOrEmpty(_selectedInventoryVaultPrefab) && num8 > 0 && num8 <= _selectedInventoryVaultStack; if (GUILayout.Button("Deposit Item", _buttonStyle, (GUILayoutOption[])(object)new GUILayoutOption[2] { GUILayout.Width(108f), GUILayout.Height(26f) })) { WarfareRPC.Instance?.DepositSpecificInventoryItemToClanVault(info.ClanId, _selectedInventoryVaultPrefab, _selectedInventoryVaultName, num8, _selectedInventoryVaultSlotX, _selectedInventoryVaultSlotY); ClearInventoryVaultSelection(); } GUI.enabled = true; 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); 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[1] { GUILayout.Width(54f) }); DrawVaultQuantityButtons(ref _vaultWithdrawAmountInput, GetSelectedVaultAmount(vault), ref _vaultWithdrawQuantityStep); int result2; int num9 = (int.TryParse(_vaultWithdrawAmountInput, out result2) ? result2 : 0); int selectedVaultAmount = GetSelectedVaultAmount(vault); if (selectedVaultAmount > 0) { num9 = Mathf.Clamp(num9, 1, selectedVaultAmount); _vaultWithdrawAmountInput = num9.ToString(); } GUI.enabled = flag4 && !string.IsNullOrEmpty(_selectedVaultPrefab) && num9 > 0; if (GUILayout.Button("Withdraw Item", _buttonStyle, (GUILayoutOption[])(object)new GUILayoutOption[2] { GUILayout.Width(112f), GUILayout.Height(26f) })) { WarfareRPC.Instance?.RequestWithdrawVaultItem(info.ClanId, _selectedVaultPrefab, num9); _selectedVaultPrefab = ""; } GUI.enabled = true; 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 num10 = (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_0769; } if (!flag) { ConfigEntry allowCoinDepositWhenVaultClosed = ClanConfig.AllowCoinDepositWhenVaultClosed; if (allowCoinDepositWhenVaultClosed == null || !allowCoinDepositWhenVaultClosed.Value) { goto IL_0769; } } int enabled = ((num10 > 0) ? 1 : 0); goto IL_076a; IL_0769: enabled = 0; goto IL_076a; IL_076a: GUI.enabled = (byte)enabled != 0; if (GUILayout.Button("Deposit Coins", _buttonStyle, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Height(30f) })) { WarfareRPC.Instance?.RequestDepositVaultCoins(info.ClanId, num10); } GUI.enabled = flag6 && num10 > 0 && clan != null && clan.Treasury >= num10; if (GUILayout.Button("Withdraw Coins", _buttonStyle, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Height(30f) })) { WarfareRPC.Instance?.RequestWithdrawVaultCoins(info.ClanId, num10); } GUI.enabled = true; DrawVaultGuide(); 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("" + (clan?.Treasury ?? 0) + " Coins", val, Array.Empty()); GUILayout.Label("VAULT STORAGE", _labelGoldStyle, Array.Empty()); GUILayout.Label($"{vault.Count} / {capacity} stacks", _labelStyle, Array.Empty()); DrawCapacityBar(vault.Count, capacity, Mathf.Clamp(progressBarWidth, 150f, 220f), 16f); GUILayout.EndVertical(); } 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 void DrawVaultGuide() { //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 GUIStyle val = new GUIStyle(_labelStyle) { wordWrap = true, fontSize = 11 }; GUILayout.Space(6f); GUILayout.Label("Clan Vault Guide", _labelGoldStyle, Array.Empty()); GUILayout.Label("Local: Vault, Treasury, Territories, Buildings.", val, Array.Empty()); GUILayout.Label("Shared: Clan identity, members, ranks, Jarl, diplomacy.", val, Array.Empty()); GUILayout.Label("Resource tax -> Vault. Coin tax -> Treasury. Access depends on capital, colonies, rank and schedule. War vault cracking may apply if enabled.", val, Array.Empty()); } 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[1] { GUILayout.Width(28f) })) { num2 = Mathf.Max(1, num2 - step); amountInput = num2.ToString(); } if (GUILayout.Button("[+]", _smallButtonStyle, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(28f) })) { amountInput = Mathf.Min(num, num2 + step).ToString(); } if (GUILayout.Button("[1]", _smallButtonStyle, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(28f) })) { step = 1; amountInput = "1"; } if (GUILayout.Button("[10]", _smallButtonStyle, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(34f) })) { step = 10; amountInput = Mathf.Min(10, num).ToString(); } if (GUILayout.Button("[MAX]", _smallButtonStyle, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(44f) })) { 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) { //IL_0013: Unknown result type (might be due to invalid IL or missing references) //IL_001e: Expected O, but got Unknown //IL_00f1: Unknown result type (might be due to invalid IL or missing references) //IL_00fc: Expected O, but got Unknown //IL_02c9: 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_02d5: 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_02eb: 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_031f: Unknown result type (might be due to invalid IL or missing references) //IL_033e: Unknown result type (might be due to invalid IL or missing references) //IL_0343: 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) { 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)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, GetItemDataIcon(item))); } } } if (list.Count == 0) { GUILayout.Label("No inventory items found.", _labelStyle, Array.Empty()); return; } 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; } int num2 = ((!compact) ? 1 : 3); int num3 = Mathf.CeilToInt((float)list.Count / (float)num2); 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 num4 = 0; num4 < num2; num4++) { GUILayout.BeginVertical(Array.Empty()); for (int num5 = 0; num5 < num3; num5++) { int num6 = num4 * num3 + num5; if (num6 >= list.Count) { break; } VaultInventoryEntry vaultInventoryEntry2 = list[num6]; bool selected = vaultInventoryEntry2.PrefabName == _selectedInventoryVaultPrefab && vaultInventoryEntry2.SlotX == _selectedInventoryVaultSlotX && vaultInventoryEntry2.SlotY == _selectedInventoryVaultSlotY; Texture2D inventoryItemTexture = GetInventoryItemTexture(vaultInventoryEntry2.PrefabName, vaultInventoryEntry2.DisplayName, vaultInventoryEntry2.Icon); if (DrawVaultIconButton(inventoryItemTexture, CompactVaultItemLabel(vaultInventoryEntry2.DisplayName, vaultInventoryEntry2.Amount), vaultInventoryEntry2.Amount, selected)) { _selectedInventoryVaultPrefab = vaultInventoryEntry2.PrefabName; _selectedInventoryVaultName = vaultInventoryEntry2.DisplayName; _selectedInventoryVaultStack = vaultInventoryEntry2.Amount; _selectedInventoryVaultSlotX = vaultInventoryEntry2.SlotX; _selectedInventoryVaultSlotY = vaultInventoryEntry2.SlotY; string[] obj3 = new string[8] { "[FCW Vault] selected item prefab/name/stack prefab=", vaultInventoryEntry2.PrefabName, " name=", vaultInventoryEntry2.DisplayName, " stack=", null, null, null }; int amount = vaultInventoryEntry2.Amount; obj3[5] = amount.ToString(); obj3[6] = " slot="; obj3[7] = SlotText(vaultInventoryEntry2.SlotX, vaultInventoryEntry2.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 DrawVaultContentsGrid(List vault, bool freeborn, bool wideLayout = false) { //IL_0037: 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_003c: 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_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_013a: 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_0146: 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_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_0168: 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_0362: Unknown result type (might be due to invalid IL or missing references) //IL_0363: 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_035b: Unknown result type (might be due to invalid IL or missing references) //IL_0272: 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) //IL_00d8: 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) if (vault == null || 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); Texture2D 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); Texture2D 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_0013: Unknown result type (might be due to invalid IL or missing references) //IL_001e: Expected O, but got Unknown //IL_00c3: Unknown result type (might be due to invalid IL or missing references) //IL_00ce: Expected O, but got Unknown Player localPlayer = Player.m_localPlayer; Inventory val = ((localPlayer != null) ? ((Humanoid)localPlayer).GetInventory() : null); if ((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)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_000c: Unknown result type (might be due to invalid IL or missing references) //IL_0017: 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)Player.m_localPlayer == (Object)null || (Object)(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) { 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) { GUILayout.BeginVertical(_boxStyle, (GUILayoutOption[])(object)new GUILayoutOption[2] { GUILayout.Width(width), GUILayout.Height(68f) }); GUILayout.Label("" + title + "", _labelStyle, Array.Empty()); GUILayout.Label("" + value + "", _labelGoldStyle, 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 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) 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()); 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_00ac: 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_00ca: Unknown result type (might be due to invalid IL or missing references) GUILayout.Label("Freeborn Homesteads", _labelGoldStyle, Array.Empty()); List list = new List(); foreach (ClanTerritory value in data.Territories.Values) { if (value.Type == TerritoryType.Homestead && value.FreebornOwnerId != 0L) { list.Add(value); } } if (list.Count == 0) { GUILayout.Label("No Freeborn Homesteads claimed.", _labelStyle, Array.Empty()); return; } list.Sort((ClanTerritory a, ClanTerritory b) => string.Compare(a.DisplayName, b.DisplayName, StringComparison.OrdinalIgnoreCase)); _scrollDirectoryFreeborn = GUILayout.BeginScrollView(_scrollDirectoryFreeborn, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.ExpandHeight(true) }); float num = (((Rect)(ref _windowRect)).width - 96f) / 3f; for (int num2 = 0; num2 < list.Count; num2 += 3) { GUILayout.BeginHorizontal(Array.Empty()); DrawHomesteadDirectoryCard(data, viewer, list[num2], num); if (num2 + 1 < list.Count) { DrawHomesteadDirectoryCard(data, viewer, list[num2 + 1], num); } else { GUILayout.Space(num + 8f); } if (num2 + 2 < list.Count) { DrawHomesteadDirectoryCard(data, viewer, list[num2 + 2], num); } else { GUILayout.Space(num + 8f); } GUILayout.EndHorizontal(); } GUILayout.EndScrollView(); } private void DrawHomesteadDirectoryCard(WarfareData data, PlayerClanInfo viewer, ClanTerritory territory, float width) { PlayerClanInfo player = data.GetPlayer(territory.FreebornOwnerId); int count = GetFreebornTerritories(data, territory.FreebornOwnerId, TerritoryType.Outpost).Count; string text = ((!territory.OdinsPeaceActive) ? "Inactive" : ((territory.OdinsPeaceChallengeEndTimestamp > 0) ? "Challenged" : "Active")); string freebornRelationLabel = GetFreebornRelationLabel(viewer, territory); GUILayout.BeginHorizontal(_boxStyle, (GUILayoutOption[])(object)new GUILayoutOption[2] { GUILayout.Width(width), GUILayout.MinHeight(122f) }); GUILayout.BeginVertical((GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(148f) }); GUILayout.Label("" + ((player != null) ? player.PlayerName : territory.FreebornOwnerName) + "", _labelGoldStyle, Array.Empty()); DrawMiniStat("Homestead", territory.DisplayName); DrawMiniStat("Outposts", count.ToString()); DrawMiniStat("Odin's Peace", (text == "Active") ? "Active" : text); DrawMiniStat("Relation", freebornRelationLabel); GUILayout.EndVertical(); GUILayout.BeginVertical((GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(92f) }); DrawMiniStat("Treasury", (player?.FreebornTreasury ?? 0) + " coins"); DrawMiniStat("Bounty", (player?.Bounty ?? 0) + " coins"); DrawMiniStat("War Status", "0"); GUILayout.EndVertical(); GUILayout.FlexibleSpace(); DrawLinkSquareColumn(("Discord", ""), ("Steam", ""), ("Custom", "")); 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(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_004f: Unknown result type (might be due to invalid IL or missing references) //IL_0059: Expected O, but got Unknown //IL_0059: 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_009e: Unknown result type (might be due to invalid IL or missing references) //IL_00a8: Expected O, but got Unknown 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)_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)_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_003a: Unknown result type (might be due to invalid IL or missing references) //IL_0046: Expected O, but got Unknown //IL_0046: 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)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_004f: Unknown result type (might be due to invalid IL or missing references) //IL_0059: Expected O, but got Unknown //IL_0059: 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_009e: Unknown result type (might be due to invalid IL or missing references) //IL_00a8: Expected O, but got Unknown 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)_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)_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(26f) })) { _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_0224: Unknown result type (might be due to invalid IL or missing references) //IL_0229: 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) GUILayout.Label("Primary Role: " + RoleSlotColored("Primary", RoleName(info.PrimaryRole), RoleEffectPercent("Primary")) + " Secondary Role: " + RoleSlotColored("Secondary", RoleName(info.SecondaryRole), RoleEffectPercent("Secondary")) + " Tertiary Role: " + RoleSlotColored("Tertiary", RoleName(info.TertiaryRole), RoleEffectPercent("Tertiary")), _labelGoldStyle, Array.Empty()); bool flag = CanChangePrimaryUI(info); bool flag2 = CanChangeSecondaryUI(info); bool flag3 = CanChangeTertiaryUI(info); string[] obj = new string[7] { "Primary Cooldown: ", flag ? "ready" : RoleCooldownRemainingUI(info, ClanConfig.PrimaryRoleChangeCooldownHours?.Value ?? ClanConfig.RoleChangeCooldownHours?.Value ?? 24), " | Secondary Cooldown: ", null, null, null, null }; ConfigEntry enableSecondaryRole = ClanConfig.EnableSecondaryRole; obj[3] = ((enableSecondaryRole == null || !enableSecondaryRole.Value) ? "disabled" : (flag2 ? "ready" : RoleCooldownRemainingUI(info, ClanConfig.SecondaryRoleChangeCooldownHours?.Value ?? ClanConfig.RoleChangeCooldownHours?.Value ?? 24))); obj[4] = " | Tertiary Cooldown: "; ConfigEntry enableTertiaryRole = ClanConfig.EnableTertiaryRole; obj[5] = ((enableTertiaryRole == null || !enableTertiaryRole.Value) ? "disabled" : (flag3 ? "ready" : RoleCooldownRemainingUI(info, ClanConfig.TertiaryRoleChangeCooldownHours?.Value ?? ClanConfig.RoleChangeCooldownHours?.Value ?? 24))); obj[6] = ""; GUILayout.Label(string.Concat(obj), _labelStyle, Array.Empty()); RoleData[] array = VisibleRoleData(); if (_selectedRolePreviewIndex < 0 || _selectedRolePreviewIndex >= array.Length) { _selectedRolePreviewIndex = 0; } Rect rect = GUILayoutUtility.GetRect(((Rect)(ref _windowRect)).width - 60f, 590f, (GUILayoutOption[])(object)new GUILayoutOption[2] { GUILayout.ExpandWidth(true), GUILayout.Height(590f) }); DrawRoleRect(rect, info, array, flag, flag2, flag3); } 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_0077: 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) bool flag = false; try { GUI.BeginGroup(area); flag = true; float num = 8f; float num2 = 210f; float num3 = ((Rect)(ref area)).height - num2 - num; float num4 = (((Rect)(ref area)).width - num * 3f) / 4f; float num5 = (num3 - num * 3f) / 4f; Rect rect = default(Rect); for (int i = 0; i < roles.Length; i++) { int num6 = i % 4; int num7 = i / 4; ((Rect)(ref rect))..ctor((float)num6 * (num4 + num), (float)num7 * (num5 + num), num4, num5); DrawRoleCardRect(rect, info, roles[i], i); } Rect rect2 = default(Rect); ((Rect)(ref rect2))..ctor(0f, num3 + num, ((Rect)(ref area)).width, num2); DrawRolePreviewRect(rect2, info, roles[_selectedRolePreviewIndex], canPrimary, canSecondary, canTertiary); } finally { if (flag) { GUI.EndGroup(); } } } 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) { if (role != ClanRole.None) { return role != ClanRole.TwelfthPath; } return false; } private void RoleCard(PlayerClanInfo info, RoleData roleData, float width, int index) { //IL_006d: Unknown result type (might be due to invalid IL or missing references) //IL_00ad: Expected O, but got Unknown 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)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(30f) }); GUILayout.EndVertical(); GUILayout.EndHorizontal(); GUILayout.EndVertical(); } private void DrawRoleCardRect(Rect rect, PlayerClanInfo info, RoleData roleData, int index) { //IL_0025: 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_0073: Unknown result type (might be due to invalid IL or missing references) //IL_008e: Expected O, but got Unknown //IL_00bf: 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_0167: 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_01e8: Unknown result type (might be due to invalid IL or missing references) //IL_01ed: Unknown result type (might be due to invalid IL or missing references) //IL_0209: Expected O, but got Unknown //IL_0245: Unknown result type (might be due to invalid IL or missing references) ClanRole role = roleData.Role; bool flag = roleData.Selectable && IsSelectableRole(role); bool flag2 = _selectedRolePreviewIndex == index; GUI.Box(rect, GUIContent.none, flag2 ? _tabActiveStyle : _boxStyle); float num = 6f; Rect val = default(Rect); ((Rect)(ref val))..ctor(((Rect)(ref rect)).x + num, ((Rect)(ref rect)).y + num, 48f, 48f); if (GUI.Button(val, (Texture)GetRoleTexture(role), flag2 ? _tabActiveStyle : _boxStyle)) { _selectedRolePreviewIndex = index; _selectedRolePreview = role; } if (GUI.Button(new Rect(((Rect)(ref rect)).xMax - 68f, ((Rect)(ref rect)).y + num, 60f, 20f), "Preview", _smallButtonStyle)) { _selectedRolePreviewIndex = index; _selectedRolePreview = role; } Rect val2 = default(Rect); ((Rect)(ref val2))..ctor(((Rect)(ref val)).xMax + 8f, ((Rect)(ref rect)).y + 6f, ((Rect)(ref rect)).width - 138f, 20f); GUI.Label(val2, "" + roleData.DisplayName + "", (flag2 || flag) ? _labelGoldStyle : _labelStyle); GUI.Label(new Rect(((Rect)(ref val2)).x, ((Rect)(ref rect)).y + 30f, ((Rect)(ref val2)).width, 18f), roleData.Group, _labelStyle); GUI.Label(new Rect(((Rect)(ref val2)).x, ((Rect)(ref rect)).y + 50f, ((Rect)(ref val2)).width, 18f), RoleStatusColored(RoleSlotName(info, role)), _labelStyle); string text = (roleData.Selectable ? roleData.Lore : roleData.Lore.Replace("Forteca Clan Warfare", "FCW")); GUIStyle val3 = new GUIStyle(_labelStyle) { fontSize = Mathf.Max(10, _labelStyle.fontSize - 1) }; GUI.Label(new Rect(((Rect)(ref rect)).x + num, ((Rect)(ref rect)).y + 70f, ((Rect)(ref rect)).width - num * 2f, Mathf.Max(22f, ((Rect)(ref rect)).height - 76f)), text, val3); } 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_003b: 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_0088: Unknown result type (might be due to invalid IL or missing references) //IL_0098: Expected O, but got Unknown //IL_00c1: 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_0145: 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_01f4: Unknown result type (might be due to invalid IL or missing references) //IL_01f9: Unknown result type (might be due to invalid IL or missing references) //IL_0215: Expected O, but got Unknown //IL_0373: Unknown result type (might be due to invalid IL or missing references) //IL_039d: 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_0469: Unknown result type (might be due to invalid IL or missing references) //IL_04df: Unknown result type (might be due to invalid IL or missing references) ClanRole clanRole = roleData?.Role ?? ClanRole.Explorer; string text = roleData?.DisplayName ?? RoleName(clanRole); bool flag = roleData != null && roleData.Selectable && IsSelectableRole(clanRole); GUI.Box(rect, GUIContent.none, _boxStyle); float num = 8f; Rect val = default(Rect); ((Rect)(ref val))..ctor(((Rect)(ref rect)).x + num + 4f, ((Rect)(ref rect)).y + 50f, 150f, 150f); GUI.Box(val, (Texture)GetRoleTexture(clanRole), _boxStyle); float num2 = ((Rect)(ref val)).xMax + 14f; float num3 = 230f; GUI.Label(new Rect(num2, ((Rect)(ref rect)).y + 18f, num3, 30f), "" + text + "", flag ? _labelGoldStyle : _labelStyle); GUI.Label(new Rect(num2, ((Rect)(ref rect)).y + 54f, num3, 24f), "Group: " + (roleData?.Group ?? "None"), _labelStyle); GUI.Label(new Rect(num2, ((Rect)(ref rect)).y + 84f, num3, 44f), RoleStatusColored(RoleSlotName(info, clanRole)), _labelStyle); GUI.Label(new Rect(num2, ((Rect)(ref rect)).y + 136f, num3, 74f), flag ? ("Primary 100%\nSecondary " + RoleEffectPercent("Secondary") + "\nTertiary " + RoleEffectPercent("Tertiary")) : "Not selectable", _labelStyle); float num4 = num2 + num3 + 14f; float num5 = 300f; float num6 = Mathf.Max(118f, (((Rect)(ref rect)).width - num4 - num5 - 28f) / 4f); GUIStyle val2 = new GUIStyle(_labelStyle) { fontSize = Mathf.Max(10, _labelStyle.fontSize - 1) }; DrawRolePreviewColumn(num4, ((Rect)(ref rect)).y + 18f, num6, 204f, "Lore", roleData?.Lore ?? RoleDescription(clanRole), val2); DrawRolePreviewColumn(num4 + num6 + 8f, ((Rect)(ref rect)).y + 18f, num6, 204f, "Core Ability", flag ? RoleComingSoon(roleData?.CoreAbility) : (roleData?.CoreAbility ?? "Future slot."), val2); DrawRolePreviewColumn(num4 + (num6 + 8f) * 2f, ((Rect)(ref rect)).y + 18f, num6, 204f, "Passive", flag ? RoleComingSoon(roleData?.PassiveBonuses) : (roleData?.PassiveBonuses ?? "Community placeholder."), val2); DrawRolePreviewColumn(num4 + (num6 + 8f) * 3f, ((Rect)(ref rect)).y + 18f, num6, 204f, "Clan Value", roleData?.ClanValue ?? RolePlaystyle(clanRole), val2); float num7 = ((Rect)(ref rect)).xMax - num5 - 8f; GUI.Label(new Rect(num7, ((Rect)(ref rect)).y + 18f, num5, 24f), "Assign Role", _labelGoldStyle); GUI.Label(new Rect(num7, ((Rect)(ref rect)).y + 50f, num5, 72f), flag ? "Use the buttons below to assign this role to an available slot." : "This role is reserved for future/community use.", val2); bool enabled = GUI.enabled; float num8 = ((Rect)(ref rect)).yMax - 98f; GUI.enabled = flag && canPrimary && info.PrimaryRole != clanRole; if (GUI.Button(new Rect(num7, num8, num5, 28f), "Set Primary", _smallButtonStyle)) { WarfareRPC.Instance?.RequestSetPrimaryRole(clanRole); } int enabled2; if (flag && canSecondary) { ConfigEntry enableSecondaryRole = ClanConfig.EnableSecondaryRole; if (enableSecondaryRole != null && enableSecondaryRole.Value && info.SecondaryRole != clanRole && info.PrimaryRole != clanRole) { enabled2 = ((info.TertiaryRole != clanRole) ? 1 : 0); goto IL_0454; } } enabled2 = 0; goto IL_0454; IL_04cb: int enabled3; GUI.enabled = (byte)enabled3 != 0; if (GUI.Button(new Rect(num7, num8 + 68f, num5, 28f), "Set Tertiary", _smallButtonStyle)) { WarfareRPC.Instance?.RequestSetTertiaryRole(clanRole); } GUI.enabled = enabled; return; IL_0454: GUI.enabled = (byte)enabled2 != 0; if (GUI.Button(new Rect(num7, num8 + 34f, num5, 28f), "Set Secondary", _smallButtonStyle)) { WarfareRPC.Instance?.RequestSetSecondaryRole(clanRole); } if (flag && canTertiary) { ConfigEntry enableTertiaryRole = ClanConfig.EnableTertiaryRole; if (enableTertiaryRole != null && enableTertiaryRole.Value && info.TertiaryRole != clanRole && info.PrimaryRole != clanRole) { enabled3 = ((info.SecondaryRole != clanRole) ? 1 : 0); goto IL_04cb; } } enabled3 = 0; goto IL_04cb; } 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", ClanRole.TwelfthPath => "The Twelfth Path", _ => "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.", ClanRole.TwelfthPath => "Help shape the future of Forteca Clan Warfare.", _ => "", }; } 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", ClanRole.TwelfthPath => "Placeholder role | Not implemented | Community vote", _ => "", }; } 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.", ClanRole.TwelfthPath => "Community destiny pending.", _ => "", }; } 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 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 = 0f; switch (RoleUtils.GetRoleSlot(player, ClanRole.Smuggler)) { case RoleSlot.Primary: num = 0.5f; break; case RoleSlot.Secondary: num = 0.3f; break; case RoleSlot.Tertiary: num = 0.2f; break; } 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_004b: Unknown result type (might be due to invalid IL or missing references) //IL_0056: Expected O, but got Unknown //IL_005e: Unknown result type (might be due to invalid IL or missing references) //IL_0069: Expected O, but got Unknown //IL_0071: 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) 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); if (!((Object)val == (Object)null) && !((Object)val.GetComponent() == (Object)null)) { _lastAttacker[__instance.GetZDOID()] = new KeyValuePair(hit.m_attacker, Now()); 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)) { 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; } } [HarmonyPatch] public static class GuardianBuildPatch { [HarmonyPatch(typeof(Player), "ConsumeResources")] [HarmonyPrefix] public static void ConsumeResources_Prefix(Player __instance, Requirement[] requirements) { //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_005d: Unknown result type (might be due to invalid IL or missing references) //IL_0062: Unknown result type (might be due to invalid IL or missing references) //IL_0067: 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 ((Object)__instance != (Object)Player.m_localPlayer) { 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; foreach (Requirement val in requirements) { if (val != null && val.m_amount > 1) { val.m_amount = Math.Max(1, (int)((float)val.m_amount * (1f - num))); } } } } [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) { //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; } return 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_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_0074: 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()); 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 flag = false; if (RoleUtils.HasRole(player, ClanRole.Guardian) && territory != null && territory.OwnerClanId == player.ClanId) { flag = true; } ConfigEntry fullPvPDuringWar = ClanConfig.FullPvPDuringWar; if (fullPvPDuringWar != null && fullPvPDuringWar.Value && territory != null && territory.OwnerClanId != 0 && warfareData.GetActiveWarsInvolving(territory.OwnerClanId).Count > 0) { flag = true; } if (flag && !((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_0062: Unknown result type (might be due to invalid IL or missing references) //IL_006d: Expected O, but got Unknown //IL_007d: Unknown result type (might be due to invalid IL or missing references) //IL_0083: Expected O, but got Unknown //IL_0084: Unknown result type (might be due to invalid IL or missing references) //IL_008f: Expected O, but got Unknown //IL_0092: 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_00a2: Expected O, but got Unknown //IL_00a2: Expected O, but got Unknown //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_0100: 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) 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 val2 = (((Object)(object)val != (Object)null) ? val.GetComponent() : null); if ((Object)val2 == (Object)null) { return true; } Player val3 = (Player)((__instance is Player) ? __instance : null); if ((Object)val3 == (Object)null || (Object)val3 == (Object)val2) { return true; } if (FriendlyFireEnabled) { return true; } WarfareData warfareData = WarfareManager.Instance?.Data; if (warfareData == null) { return true; } PlayerClanInfo player = warfareData.GetPlayer(val2.GetPlayerID()); PlayerClanInfo player2 = warfareData.GetPlayer(val3.GetPlayerID()); if (player == null || player2 == null) { return true; } Vector2i grid = GridExtensions.WorldToGrid(((Component)val3).transform.position); ClanTerritory territory = warfareData.GetTerritory(grid); WarfareManager instance2 = WarfareManager.Instance; if (instance2 != null && instance2.IsFreebornProtectedTerritory(territory) && territory.FreebornOwnerId != player.PlayerId) { return false; } if (player.ClanId != 0 && player.ClanId == player2.ClanId) { return false; } return true; } } [HarmonyPatch] public static class InputBlockingPatches { [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] 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] 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) { float statMultiplier = GetStatMultiplier(__instance); if (statMultiplier != 1f) { ((DamageTypes)(ref hitData.m_damage)).Modify(statMultiplier); } } [HarmonyPatch(typeof(Character), "RPC_Damage")] [HarmonyPrefix] public static void ModifyDefense(Character __instance, HitData hit) { if (__instance.IsPlayer()) { float statMultiplier = GetStatMultiplier(__instance.GetSEMan()); if (statMultiplier != 1f && statMultiplier < 1f) { ((DamageTypes)(ref hit.m_damage)).Modify(1f / statMultiplier); } else if (statMultiplier > 1f) { ((DamageTypes)(ref hit.m_damage)).Modify(1f / statMultiplier); } } } private static float GetStatMultiplier(SEMan seMan) { //IL_0010: Unknown result type (might be due to invalid IL or missing references) //IL_001b: Expected O, but got Unknown //IL_001e: 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_0032: Expected O, but got Unknown //IL_0079: Unknown result type (might be due to invalid IL or missing references) //IL_007e: Unknown result type (might be due to invalid IL or missing references) //IL_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) if (seMan == null) { return 1f; } Player localPlayer = Player.m_localPlayer; if ((Object)localPlayer == (Object)null || (Object)localPlayer != (Object)Player.m_localPlayer) { return 1f; } WarfareData warfareData = WarfareManager.Instance?.Data; if (warfareData == null) { return 1f; } PlayerClanInfo player = warfareData.GetPlayer(localPlayer.GetPlayerID()); if (player == null || player.ClanId == 0) { return 1f; } Vector2i grid = GridExtensions.WorldToGrid(((Component)localPlayer).transform.position); ClanTerritory territory = warfareData.GetTerritory(grid); float num = 1f + (ClanConfig.RoleBuffPercent?.Value ?? 20f) / 100f; float result = 1f - (ClanConfig.RoleDebuffPercent?.Value ?? 20f) / 100f; bool flag = territory != null && territory.OwnerClanId == player.ClanId; bool flag2 = territory != null && territory.OwnerClanId != 0 && territory.OwnerClanId != player.ClanId; bool flag3 = territory == null || territory.OwnerClanId == 0; float roleFactor = RoleUtils.GetRoleFactor(player, ClanRole.Explorer); if (roleFactor > 0f && flag3) { return 1f + (num - 1f) * roleFactor; } float roleFactor2 = RoleUtils.GetRoleFactor(player, ClanRole.Guardian); if (roleFactor2 > 0f && flag) { return 1f + (num - 1f) * roleFactor2; } float roleFactor3 = RoleUtils.GetRoleFactor(player, ClanRole.Raider); if (roleFactor3 > 0f && flag2) { return 1f + (num - 1f) * roleFactor3; } if ((roleFactor > 0f || roleFactor2 > 0f) && (flag || flag2)) { return result; } return 1f; } [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 ScrollBlockingPatches { [HarmonyPatch(typeof(ZInput), "GetMouseScrollWheel")] [HarmonyPrefix] public static bool ZInput_GetMouseScrollWheel_Prefix(ref float __result) { if (ClanSelectionUI.ShouldBlockInput()) { __result = 0f; return false; } return true; } } 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 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 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_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_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 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(); 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_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_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) { long playerId = pkg.ReadLong(); Plugin.Log.LogInfo((object)("[FCW Server] Received clan sync request playerId=" + playerId)); WarfareManager.Instance?.EnsureSharedDataForPlayerSync(playerId); WarfareManager.Instance?.MarkPlayerActive(playerId); int valueOrDefault = (WarfareManager.Instance?.Data?.GetPlayer(playerId)?.ClanId).GetValueOrDefault(); Plugin.Log.LogInfo((object)("[FCW Server] Shared clan lookup playerId=" + playerId + " clanId=" + ((valueOrDefault != 0) ? valueOrDefault.ToString() : "none"))); int valueOrDefault2 = (WarfareManager.Instance?.Data?.Clans?.Count).GetValueOrDefault(); int num = CountClanMembers(WarfareManager.Instance?.Data); Plugin.Log.LogInfo((object)("[FCW Server] Sending clan sync playerId=" + playerId + " clanId=" + valueOrDefault + " clans=" + valueOrDefault2 + " members=" + num + " peer=" + sender)); SyncDataToClient(sender); SyncAllDataToClients(); Plugin.Log.LogInfo((object)("[FCW Shared Sync] Server sent merged shared clan data to player=" + playerId)); } } 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_0046: Unknown result type (might be due to invalid IL or missing references) //IL_004c: Expected O, but got Unknown if (!IsServer) { return; } byte[] array = WarfareManager.Instance?.Data?.Serialize(); if (array != null) { ZRoutedRpc instance = ZRoutedRpc.instance; if (instance != null) { instance.InvokeRoutedRPC(ZRoutedRpc.Everybody, "FCW_SyncData", new object[1] { (object)new ZPackage(array) }); SyncVaultStateToClient(ZRoutedRpc.Everybody); } } } private void SyncDataToClient(long peerUID) { //IL_0065: Unknown result type (might be due to invalid IL or missing references) //IL_006b: Expected O, but got Unknown 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); } } } 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 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()); EnsureLocalPlayerAlias(warfareData, LocalId, LocalName); int valueOrDefault = (warfareData?.Clans?.Count).GetValueOrDefault(); int num = CountClanMembers(warfareData); int num2 = ResolvePlayerClanId(warfareData, LocalId, LocalName); 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 playerName) { PlayerClanInfo playerClanInfo = data?.GetPlayer(playerId); if (playerClanInfo != null) { return playerClanInfo.ClanId; } if (data?.Players == null || string.IsNullOrEmpty(playerName)) { return 0; } foreach (PlayerClanInfo value in data.Players.Values) { if (string.Equals(value.PlayerName, playerName, StringComparison.OrdinalIgnoreCase)) { return value.ClanId; } } return 0; } private static void EnsureLocalPlayerAlias(WarfareData data, long playerId, string playerName) { if (data?.Players == null || playerId == 0L || data.GetPlayer(playerId) != null || string.IsNullOrEmpty(playerName)) { return; } foreach (PlayerClanInfo value in data.Players.Values) { if (string.Equals(value.PlayerName, playerName, StringComparison.OrdinalIgnoreCase)) { data.Players[playerId] = value; Plugin.Log.LogInfo((object)("[FCW Client] Aliased synced clan player by name: localPlayerId=" + playerId + " sourcePlayerId=" + value.PlayerId + " clanId=" + value.ClanId)); break; } } } 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_0036: 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_0049: Expected O, but got Unknown if (IsServer) { long playerId = pkg.ReadLong(); string playerName = pkg.ReadString(); int num = pkg.ReadInt(); int num2 = pkg.ReadInt(); 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) { //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); ToServer("FCW_UpdateProfile", val); } private void RPC_OnUpdateProfile(long sender, ZPackage pkg) { if (IsServer) { long playerId = pkg.ReadLong(); string aboutMe = pkg.ReadString(); string activeTitle = pkg.ReadString(); string message = WarfareManager.Instance?.UpdateProfile(playerId, aboutMe, activeTitle) ?? "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_0042: 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(); int action = pkg.ReadInt(); TerritoryBuildingType type = (TerritoryBuildingType)pkg.ReadInt(); int slot = pkg.ReadInt(); 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_0032: Unknown result type (might be due to invalid IL or missing references) if (IsServer) { long playerId = pkg.ReadLong(); string playerName = pkg.ReadString(); int eventId = pkg.ReadInt(); Vector3 position = pkg.ReadVector3(); string message = WarfareManager.Instance?.ClaimWorldEvent(playerId, playerName, eventId, position) ?? "Failed"; SendNotification(sender, message); SyncAllDataToClients(); } } 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_0046: 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(LocalId); val2.Write(action); val2.Write(eventId); val2.Write(val); ToServer("FCW_WorldEventAdmin", val2); } private void RPC_OnWorldEventAdmin(long sender, ZPackage pkg) { //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_0051: 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) if (IsServer) { long playerId = pkg.ReadLong(); int num = pkg.ReadInt(); int num2 = pkg.ReadInt(); Vector3 position = pkg.ReadVector3(); WarfareManager instance = WarfareManager.Instance; SendNotification(sender, num switch { 0 => instance?.CreateTestWorldEvent(playerId, sender, position) ?? "Failed", 1 => instance?.RemoveWorldEvent(playerId, sender, num2) ?? "Failed", 2 => instance?.ExpireWorldEvent(playerId, sender, num2) ?? "Failed", 10 => instance?.CreateWorldEvent(playerId, sender, (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(LocalId); val.Write(action); val.Write(targetId); ToServer("FCW_KingdomAdminAction", val); } private void RPC_OnKingdomAdminAction(long sender, ZPackage pkg) { if (IsServer) { long playerId = pkg.ReadLong(); int num = pkg.ReadInt(); long num2 = pkg.ReadLong(); WarfareManager instance = WarfareManager.Instance; SendNotification(sender, num switch { 100 => instance?.AdminDeleteClan(playerId, sender, (int)num2) ?? "Failed", 101 => instance?.AdminDeleteHomestead(playerId, sender, num2) ?? "Failed", 102 => instance?.AdminReleaseProtectedZoneConflicts(playerId, sender) ?? "Failed", _ => "Unknown admin action.", }); SyncAllDataToClients(); } } 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) { long playerId = pkg.ReadLong(); string prefabName = pkg.ReadString(); string displayName = pkg.ReadString(); int taxAmount = pkg.ReadInt(); 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) { WarfareManager instance = WarfareManager.Instance; if (instance != null && instance.TryRecordCoinTax(pkg.ReadLong(), pkg.ReadInt())) { 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_0154: Unknown result type (might be due to invalid IL or missing references) 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()); int num3 = pkg.ReadInt(); Plugin.Log.LogInfo((object)("[FCW Vault] SERVER received coin deposit request player=" + playerId + " amount=" + num2)); 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))) { VaultDebug("[FCW Vault] DepositCoins failed reason=invalid clan membership"); SendNotification(sender, "That is not your clan's vault."); return; } if (num != 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 (num2 <= 0) { VaultDebug("[FCW Vault] DepositCoins failed reason=invalid amount"); SendNotification(sender, "Coin amount must be greater than zero."); return; } if (num3 < num2) { 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 num4 = ((num == 0) ? playerClanInfo.FreebornTreasury : clan.Treasury); VaultDebug("[FCW Vault] DepositCoins start playerCoins=" + num3 + " amount=" + num2 + " treasuryBefore=" + num4); QueueVaultDeposit(sender, playerId, num, isCoin: true, ClanConfig.CoinItemPrefab?.Value ?? "Coins", "Coins", num2, 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(); int num2 = pkg.ReadInt(); bool flag = pkg.ReadBool(); string text = pkg.ReadString(); string text2 = pkg.ReadString(); int num3 = pkg.ReadInt(); Plugin.Log.LogInfo((object)("[FCW Vault] SERVER received " + (flag ? "coin" : "item") + " deposit confirmation player=" + num + " " + (flag ? ("amount=" + num3) : ("item=" + text + " qty=" + num3)))); if (!_pendingVaultDeposits.TryGetValue(sender, out var value) || value.PlayerId != num || value.ClanId != num2 || value.IsCoin != flag || value.Amount != num3 || !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(num2); if ((Object)(object)instance == (Object)null || warfareData == null || playerClanInfo == null || (num2 != 0 && (clan == null || playerClanInfo.ClanId != num2)) || (num2 == 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 num4 = ((num2 == 0) ? playerClanInfo.FreebornTreasury : clan.Treasury); if (num2 == 0) { playerClanInfo.FreebornTreasury += num3; } else { clan.Treasury += num3; } VaultDebug("[FCW Vault] DepositCoins success treasuryAfter=" + ((num2 == 0) ? playerClanInfo.FreebornTreasury : clan.Treasury) + " treasuryBefore=" + num4); SendNotification(sender, "Coins deposited."); } else { if (num2 == 0) { warfareData.AddToFreebornVault(num, text, CleanItemDisplayName(string.IsNullOrEmpty(text2) ? text : text2), num3); } else { warfareData.AddToVault(num2, text, CleanItemDisplayName(string.IsNullOrEmpty(text2) ? text : text2), num3); } 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 { clan.Treasury -= num2; } 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_02c4: Unknown result type (might be due to invalid IL or missing references) //IL_02cf: Expected O, but got Unknown //IL_02e2: Unknown result type (might be due to invalid IL or missing references) //IL_02ed: Expected O, but got Unknown //IL_012c: Unknown result type (might be due to invalid IL or missing references) //IL_0400: Unknown result type (might be due to invalid IL or missing references) //IL_0407: Expected O, but got Unknown bool flag = pkg.ReadBool(); if (flag && IsServer) { long playerId = pkg.ReadLong(); int num = 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=" + playerId + " item=" + prefab + " qty=" + amount)); WarfareManager instance = WarfareManager.Instance; WarfareData warfareData = instance?.Data; if (warfareData == null) { return; } PlayerClanInfo player = warfareData.GetPlayer(playerId); if (player == null || (num != 0 && player.ClanId != num) || (num == 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 (num != 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(num); int num2 = ((num == 0) ? instance.FreebornVaultCapacity(player) : Economy.VaultCapacity(clan, warfareData.GetClanTerritoryCount(num))); int num3 = ((num == 0) ? warfareData.GetFreebornVaultStackCount(playerId) : warfareData.GetVaultStackCount(num)); bool flag3 = ((num == 0) ? warfareData.GetFreebornVault(playerId) : warfareData.GetVault(num)).Find((ClanVaultItem v) => v.PrefabName == prefab) != null; if (num3 < num2 || flag3) { if (flag2) { QueueVaultDeposit(sender, playerId, num, isCoin: false, prefab, text, amount, slotX, slotY); return; } if (num == 0) { warfareData.AddToFreebornVault(playerId, prefab, CleanItemDisplayName(string.IsNullOrEmpty(text) ? prefab : text), amount); } else { warfareData.AddToVault(num, 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 num4 = pkg.ReadInt(); string text2 = pkg.ReadString(); int num5 = 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, num5, out var reason)) { SendNotification(GetServerPeerID(), "Inventory full."); VaultDebug("[FCW Vault] withdraw result=fail reason=" + reason + " item=" + text2 + " amount=" + num5); 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, num5 }); } finally { SuppressTax = false; } if (obj != null && (!(obj is int num6) || num6 != 0)) { VaultDebug("[FCW Vault] withdraw result=success item=" + text2 + " amount=" + num5); ZPackage val = new ZPackage(); val.Write(LocalId); val.Write(num4); if (text2 == (ClanConfig.CoinItemPrefab?.Value ?? "Coins")) { val.Write(num5); ToServer("FCW_ConfirmVaultCoinsWithdraw", val); } else { val.Write(text2); val.Write(num5); ToServer("FCW_ConfirmVaultWithdraw", val); } } else { SendNotification(GetServerPeerID(), "Inventory full."); VaultDebug("[FCW Vault] withdraw result=fail reason=AddItem returned false item=" + text2 + " amount=" + num5); } } else { SendNotification(GetServerPeerID(), "Item prefab not found: " + text2 + ""); VaultDebug("[FCW Vault] withdraw result=fail reason=prefab not found item=" + text2 + " amount=" + num5); } } } 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_00f6: Unknown result type (might be due to invalid IL or missing references) //IL_0101: 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 (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."); 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."); 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) { ResolveDiceGame(fehuGameTable2); break; } fehuGameTable2.EscrowCollecting = true; fehuGameTable2.Status = FehuGameStatus.Active; fehuGameTable2.EscrowPlayerIds.Clear(); fehuGameTable2.EscrowTotal = 0L; PersistGames(); { foreach (long playerId in fehuGameTable2.PlayerIds) { RequestGameEscrow(playerId, fehuGameTable2.GameId, fehuGameTable2.StakeAmount, fehuGameTable2.StakeType, fehuGameTable2.StakePrefabName); } 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 || !fehuGameTable.EscrowCollecting || !fehuGameTable.PlayerIds.Contains(item) || fehuGameTable.EscrowPlayerIds.Contains(item) || fehuGameTable.StakeType != fehuStakeType || !string.Equals(fehuGameTable.StakePrefabName ?? "", text2 ?? "", StringComparison.OrdinalIgnoreCase)) { return; } Plugin.Log.LogInfo((object)("[FCW Games] Stake validation player=" + item + " amount=" + fehuGameTable.StakeAmount + " ok=" + flag + " reason=" + text)); if (!flag) { CancelDiceGame(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) { ResolveDiceGame(fehuGameTable); } else if (fehuGameTable.GameType == FehuGameType.Chess) { StartChessMatch(fehuGameTable); } else if (fehuGameTable.GameType == FehuGameType.Poker) { StartPokerTable(fehuGameTable); } } else { PersistGames(); } } private void ResolveDiceGame(FehuGameTable game) { Random random = new Random((int)(WarfareManager.Now() ^ game.GameId ^ game.PlayerIds.Count)); Dictionary dictionary = new Dictionary(); foreach (long playerId in game.PlayerIds) { int num = 0; int num2 = ((game.DiceMode == DiceGameMode.BestOf3) ? (game.DiceCount * 3) : game.DiceCount); for (int i = 0; i < num2; i++) { num += random.Next(1, 7); } dictionary[playerId] = num; Plugin.Log.LogInfo((object)("[FCW Games] Dice rolled table=" + game.GameId + " player=" + playerId + " total=" + num)); } int num3 = int.MinValue; foreach (int value in dictionary.Values) { num3 = Math.Max(num3, value); } List list = new List(); foreach (KeyValuePair item in dictionary) { if ((game.DiceMode == DiceGameMode.LuckySeven) ? (item.Value == 7) : (item.Value == num3)) { list.Add(item.Key); } } if (list.Count != 1) { game.Result = ((game.DiceMode == DiceGameMode.LuckySeven) ? "No single Lucky Seven winner. Stakes refunded." : "Tie. Stakes refunded."); RefundDiceGame(game); return; } long num4 = (game.WinnerPlayerId = list[0]); game.Status = FehuGameStatus.Finished; game.EscrowCollecting = false; game.FinishedAt = WarfareManager.Now(); game.Result = game.DiceMode.ToString() + " winner: " + (WarfareManager.Instance?.Data?.GetPlayer(num4)?.PlayerName ?? num4.ToString()) + " (" + dictionary[num4] + ")"; long escrowTotal = game.EscrowTotal; if (escrowTotal > 0) { SendGamePayout(num4, game.GameId, escrowTotal, game.StakeType, game.StakePrefabName, "Dice winnings"); } game.PayoutIssued = true; AddDiceHistoryAndStats(game, dictionary, num4, escrowTotal); PersistGames(); Plugin.Log.LogInfo((object)("[FCW Games] Dice finished table=" + game.GameId + " winner=" + num4 + " reward=" + escrowTotal + " " + game.StakePrefabName)); } 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 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("Dice payout pending: inventory cannot receive " + num2 + " " + text3 + "."); } else { inventory.AddItem(itemPrefabSafe, (int)num2); NotifyVaultClient("Dice " + 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.Profit += reward - game.StakeAmount; value.BiggestWin = Math.Max(value.BiggestWin, reward); } else { value.Losses++; value.Profit -= game.StakeAmount; } } } 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') { //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); ToServer("FCW_ChessAction", val); } private void RPC_OnChessAction(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(); 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(); 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; } 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 = ((num5 > 0) ? FehuStakeType.Coins : FehuStakeType.None), StakeAmount = num5, StakePrefabName = ((num5 <= 0) ? "" : (ClanConfig.CoinItemPrefab?.Value ?? "Coins")), Status = FehuGameStatus.Waiting, CreatedAt = WarfareManager.Now(), ChessHostColorPreference = num3, ChessTimeControlSeconds = num4 }; fehuGameTable.PlayerIds.Add(num); warfareData.Games.Add(fehuGameTable); PersistGames(); Plugin.Log.LogInfo((object)("[FCW Chess] Create match=" + fehuGameTable.GameId)); SendNotification(sender, "Chess match created."); 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; } fehuGameTable2.PlayerIds.Add(num); PersistGames(); SendNotification(sender, "Joined chess match."); 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.None || fehuGameTable2.StakeAmount <= 0) { StartChessMatch(fehuGameTable2); return; } fehuGameTable2.Status = FehuGameStatus.Active; 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)); } 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 (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) { game.Status = FehuGameStatus.Cancelled; game.Result = result; game.FinishedAt = WarfareManager.Now(); 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 }); } 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) { //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); 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(); 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.Coins, StakeAmount = num4, StakePrefabName = (ClanConfig.CoinItemPrefab?.Value ?? "Coins"), Status = FehuGameStatus.Waiting, CreatedAt = WarfareManager.Now(), PokerBuyIn = num4, PokerSmallBlind = num5, PokerBigBlind = num6 }; fehuGameTable.PlayerIds.Add(num); warfareData.Games.Add(fehuGameTable); PersistGames(); Plugin.Log.LogInfo((object)("[FCW Poker] Create table=" + fehuGameTable.GameId)); SendNotification(sender, "Poker table created."); 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; } fehuGameTable2.PlayerIds.Add(num); PersistGames(); Plugin.Log.LogInfo((object)("[FCW Poker] Join table=" + fehuGameTable2.GameId + " player=" + num)); SendNotification(sender, "Joined poker table."); 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; } fehuGameTable2.Status = FehuGameStatus.Active; 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; } 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); fehuGameTable2.Result = poker.LastResult; PersistGames(); return; } if (PokerEngine.GetPlayer(poker, num) == null || poker.CurrentActorId != num) { SendNotification(sender, "It is not your poker turn."); return; } if (num2 < 5 || num2 > 8) { SendNotification(sender, "Unknown poker action."); return; } if (!PokerEngine.ApplyAction(poker, num, num2 - 4, raiseTo, out var reason)) { SendNotification(sender, "" + reason + ""); return; } Plugin.Log.LogInfo((object)("[FCW Poker] Action table=" + fehuGameTable2.GameId + " player=" + num + " action=" + num2)); 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); game.Result = pokerGameState.LastResult; PersistGames(); Plugin.Log.LogInfo((object)("[FCW Poker] Started table=" + game.GameId + " players=" + pokerGameState.Seats.Count)); } private void AdvancePokerAfterAction(FehuGameTable game) { PokerGameState state = game.Poker; if (PokerEngine.HasSingleLivePlayer(state)) { PokerPlayerState singleLivePlayer = PokerEngine.GetSingleLivePlayer(state); long num = PokerEngine.CurrentPot(state); if (singleLivePlayer != null) { singleLivePlayer.Chips += num; } FinishPokerHand(game, new Dictionary { { singleLivePlayer?.PlayerId ?? 0, num } }, "Won by folds"); } else if (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"); } } else { int num2 = state.Seats.FindIndex((PokerPlayerState p) => p.PlayerId == state.CurrentActorId); int num3 = PokerEngine.FindNextActionSeat(state, num2 + 1); if (num3 >= 0) { state.CurrentActorId = state.Seats[num3].PlayerId; } } } private void FinishPokerHand(FehuGameTable game, Dictionary payouts, string result) { PokerGameState poker = game.Poker; long pot = PokerEngine.CurrentPot(poker); 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())); } poker.Phase = PokerPhase.Showdown; poker.CurrentActorId = 0L; poker.LastResult = result + text + ". Pot: " + pot + "."; game.Result = poker.LastResult; game.WinnerPlayerId = num; AddPokerHistoryAndStats(game, payouts, pot); Plugin.Log.LogInfo((object)("[FCW Poker] Hand finished table=" + game.GameId + " pot=" + pot)); } 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(); SendNotification(sender, "Poker cash out: " + chips + " coins."); } private void RefundPokerTable(FehuGameTable game, string result) { if (game.PayoutIssued) { return; } 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"); continue; } foreach (long escrowPlayerId in game.EscrowPlayerIds) { SendGamePayout(escrowPlayerId, game.GameId, game.PokerBuyIn, FehuStakeType.Coins, game.StakePrefabName, "Poker buy-in refund"); } } } game.Status = FehuGameStatus.Cancelled; game.Result = result; game.FinishedAt = WarfareManager.Now(); 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 = FehuStakeType.Coins, StakeAmount = game.PokerBuyIn, StakePrefabName = game.StakePrefabName, CompletedAt = WarfareManager.Now() }); 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 = FehuStakeType.Coins, StakeAmount = game.PokerBuyIn, StakePrefabName = game.StakePrefabName, CompletedAt = game.FinishedAt }); } } 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) { PokerPlayerState player = PokerEngine.GetPlayer(poker, poker.CurrentActorId); if (player != null && player.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(); 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.Status == FehuGameStatus.Waiting) { return now - num2 >= 86400; } if (game.Status == FehuGameStatus.Cancelled) { return now - num2 >= 3600; } 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", _ => "Unknown friendship 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 } [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)); } 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 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 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 ClanRank { Member, Officer, Jarl } public enum ClanRole { None = 0, Explorer = 1, Warden = 2, Raider = 3, Guardian = 4, Builder = 5, Gatherer = 6, Merchant = 7, Smuggler = 8, Thief = 9, Shaman = 10, Druid = 11, TwelfthPath = 12, 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 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 } public enum FehuGameType { Chess, Dice, Poker } public enum FehuGameStatus { Waiting, Active, Finished, Cancelled } public enum FehuStakeType { None, Coins, Item } public enum DiceGameMode { HighestRoll, BestOf3, LuckySeven } [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 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; } [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; } [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 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; } 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; } } [Serializable] public class PlayerClanInfo { public long PlayerId; 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 ClanRank Rank; public long LastRoleChangeTimestamp; public long LastVaultCrackTimestamp; 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 string AboutMe = ""; public string ActiveTitle = ""; 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 Dictionary RoleMinutes = new Dictionary(); 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.TwelfthPath) { 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 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 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 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.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."; 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); PostBlind(state, num2, state.SmallBlind); PostBlind(state, num3, state.BigBlind); 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; break; case 2: if (num > 0) { reason = "You must call, raise, fold, or go all-in."; return false; } break; case 3: Commit(state, player, num); 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); 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: Commit(state, player, player.Chips); if (player.RoundBet > state.CurrentBet) { state.MinRaise = player.RoundBet - state.CurrentBet; 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) { foreach (PokerPlayerState seat in state.Seats) { if (!seat.Folded && !seat.AllIn && (!seat.HasActedThisRound || seat.RoundBet != state.CurrentBet)) { return false; } } 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)); } else if (state.Phase == PokerPhase.Flop) { state.Phase = PokerPhase.Turn; Draw(state); state.CommunityCards.Add(Draw(state)); } else { if (state.Phase != PokerPhase.Turn) { state.Phase = PokerPhase.Showdown; return; } state.Phase = PokerPhase.River; Draw(state); state.CommunityCards.Add(Draw(state)); } int num = FindNextActionSeat(state, state.DealerSeatIndex + 1); state.CurrentActorId = ((num >= 0) ? state.Seats[num].PlayerId : 0); } 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; foreach (long level in list) { List list2 = state.Seats.Where((PokerPlayerState p) => p.TotalContribution >= level).ToList(); long num2 = (level - num) * list2.Count; num = level; List list3 = list2.Where((PokerPlayerState p) => !p.Folded).ToList(); if (num2 > 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 num3 = num2 / list4.Count; long num4 = num2 % list4.Count; for (int num5 = 0; num5 < list4.Count; num5++) { long num6 = num3 + ((num5 < num4) ? 1 : 0); dictionary[list4[num5].PlayerId] = (dictionary.TryGetValue(list4[num5].PlayerId, out var value) ? (value + num6) : num6); } } } foreach (KeyValuePair item in dictionary) { GetPlayer(state, item.Key).Chips += item.Value; } 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 enum ProtectedZoneType { Spawn, Trader, Marketplace, EventHub, Custom } [Serializable] public class RotTotem { 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 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 TerritoryBuildingType Type; public int Level = 1; public bool Damaged; public long BuiltAt; public long LastProductionTimestamp; } public enum TerritoryBuildingType { None, ClanHall, Treasury, Marketplace, TradePost, LumberCamp, Quarry, CopperMine, IronMine, SilverMine, Farm, FishingPort, Barracks, Watchtower } [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 = 33; 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 List ActiveCracks = new List(); public List ActiveCaptures = new List(); public int Version { get; set; } = 33; 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 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; } 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.LastLoginTimestamp = (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.Deck?.Count ?? 0); foreach (int item in state.Deck ?? new List()) { pkg.Write(item); } pkg.Write(state.CommunityCards?.Count ?? 0); foreach (int item2 in state.CommunityCards ?? new List()) { pkg.Write(item2); } pkg.Write(state.Seats?.Count ?? 0); foreach (PokerPlayerState item3 in state.Seats ?? new List()) { pkg.Write(item3.PlayerId); pkg.Write(item3.PlayerName ?? ""); pkg.Write(item3.Chips); pkg.Write(item3.RoundBet); pkg.Write(item3.TotalContribution); pkg.Write(item3.Folded); pkg.Write(item3.AllIn); pkg.Write(item3.HasActedThisRound); pkg.Write(item3.DisconnectedAt); pkg.Write(item3.HoleCards?.Count ?? 0); foreach (int item4 in item3.HoleCards ?? new List()) { pkg.Write(item4); } } } private static PokerGameState ReadPokerState(ZPackage pkg, bool includesActionState) { 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() }; int num = pkg.ReadInt(); for (int i = 0; i < num; i++) { pokerGameState.Deck.Add(pkg.ReadInt()); } int num2 = pkg.ReadInt(); for (int j = 0; j < num2; j++) { pokerGameState.CommunityCards.Add(pkg.ReadInt()); } int num3 = pkg.ReadInt(); for (int k = 0; k < num3; k++) { 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 num4 = pkg.ReadInt(); for (int l = 0; l < num4; l++) { pokerPlayerState.HoleCards.Add(pkg.ReadInt()); } pokerGameState.Seats.Add(pokerPlayerState); } return pokerGameState; } 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_0ce3: Unknown result type (might be due to invalid IL or missing references) //IL_1373: Unknown result type (might be due to invalid IL or missing references) //IL_153d: Unknown result type (might be due to invalid IL or missing references) //IL_1661: Unknown result type (might be due to invalid IL or missing references) ZPackage val = new ZPackage(); val.Write(33); 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(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.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); } } 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 list = value2.FreebornFriends ?? new List(); val.Write(list.Count); foreach (long item2 in list) { val.Write(item2); } List list2 = value2.PendingFreebornFriendRequests ?? new List(); val.Write(list2.Count); foreach (long item3 in list2) { val.Write(item3); } 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.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 list3 = value2.SagaEntries ?? new List(); val.Write(list3.Count); foreach (string item4 in list3) { val.Write(item4 ?? ""); } Dictionary dictionary2 = value2.RoleMinutes ?? new Dictionary(); val.Write(dictionary2.Count); foreach (KeyValuePair item5 in dictionary2) { val.Write(item5.Key); val.Write(item5.Value); } } 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); List list4 = value3.Buildings ?? new List(); val.Write(list4.Count); foreach (TerritoryBuilding item6 in list4) { val.Write((int)item6.Type); val.Write(item6.Level); val.Write(item6.Damaged); val.Write(item6.BuiltAt); val.Write(item6.LastProductionTimestamp); } List list5 = value3.TerritoryStorage ?? new List(); val.Write(list5.Count); foreach (ClanVaultItem item7 in list5) { val.Write(item7.PrefabName ?? ""); val.Write(item7.DisplayName ?? ""); val.Write(item7.Amount); } } val.Write(ClanVaults.Count); foreach (KeyValuePair> clanVault in ClanVaults) { val.Write(clanVault.Key); List list6 = clanVault.Value ?? new List(); val.Write(list6.Count); foreach (ClanVaultItem item8 in list6) { val.Write(item8.PrefabName ?? ""); val.Write(item8.DisplayName ?? ""); val.Write(item8.Amount); } } val.Write(FreebornVaults.Count); foreach (KeyValuePair> freebornVault in FreebornVaults) { val.Write(freebornVault.Key); List list7 = freebornVault.Value ?? new List(); val.Write(list7.Count); foreach (ClanVaultItem item9 in list7) { val.Write(item9.PrefabName ?? ""); val.Write(item9.DisplayName ?? ""); val.Write(item9.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(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 item10 in Diplomacy) { val.Write(item10.ClanA); val.Write(item10.ClanB); val.Write((int)item10.State); val.Write(item10.RequestedByClanId); val.Write(item10.UpdatedTimestamp); val.Write(item10.AllianceLeaderClanId); val.Write(item10.AllianceColorR); val.Write(item10.AllianceColorG); val.Write(item10.AllianceColorB); } val.Write(Alliances.Count); foreach (Alliance alliance in Alliances) { val.Write(alliance.AllianceId); val.Write(alliance.AllianceName ?? ""); val.Write(alliance.AllianceLeaderClanId); List list8 = alliance.MemberClanIds ?? new List(); val.Write(list8.Count); foreach (int item11 in list8) { val.Write(item11); } val.Write(alliance.AllianceColorR); val.Write(alliance.AllianceColorG); val.Write(alliance.AllianceColorB); val.Write(alliance.CreatedAt); List list9 = alliance.PendingInvites ?? new List(); val.Write(list9.Count); foreach (int item12 in list9) { val.Write(item12); } } 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 list10 = BuildingProductionLog ?? new List(); val.Write(list10.Count); foreach (string item13 in list10) { val.Write(item13 ?? ""); } 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 list11 = Guards ?? new List(); val.Write(list11.Count); foreach (TerritoryGuard item14 in list11) { val.Write(item14.GuardId); val.Write(item14.OwnerClanId); val.Write(item14.TerritoryId ?? ""); val.Write((int)item14.GuardType); val.Write(item14.Level); val.Write((int)item14.Status); val.Write(item14.UpkeepCost); val.Write(item14.CreatedAt); val.Write(item14.LastPaidAt); val.Write(item14.HasPosition); val.Write(item14.Position); } List list12 = GuardLog ?? new List(); val.Write(list12.Count); foreach (string item15 in list12) { val.Write(item15 ?? ""); } List list13 = WorldEvents ?? new List(); val.Write(list13.Count); foreach (WorldEvent item16 in list13) { val.Write(item16.EventId); val.Write((int)item16.EventType); val.Write(item16.EventName ?? ""); val.Write(item16.Description ?? ""); val.Write(item16.StartTime); val.Write(item16.EndTime); val.Write(item16.Duration); val.Write(item16.Position); val.Write(item16.Radius); val.Write((int)item16.Status); val.Write(item16.RewardCoins); val.Write(item16.RewardSummary ?? ""); val.Write(item16.ClaimedByPlayerId); val.Write(item16.ClaimedByPlayerName ?? ""); } List list14 = WorldEventLog ?? new List(); val.Write(list14.Count); foreach (string item17 in list14) { val.Write(item17 ?? ""); } List list15 = Contracts ?? new List(); val.Write(list15.Count); foreach (Contract item18 in list15) { val.Write(item18.ContractId); val.Write((int)item18.ContractType); val.Write(item18.Title ?? ""); val.Write(item18.Description ?? ""); val.Write((int)item18.IssuerType); val.Write(item18.IssuerId); val.Write((int)item18.TargetType); val.Write(item18.TargetId ?? ""); val.Write(item18.RequiredItem ?? ""); val.Write(item18.RequiredAmount); val.Write(item18.RewardCoins); val.Write(item18.RewardMaterials ?? ""); val.Write((int)item18.Status); val.Write(item18.CreatedAt); val.Write(item18.ExpiresAt); val.Write(item18.AcceptedBy); } List list16 = ContractLog ?? new List(); val.Write(list16.Count); foreach (string item19 in list16) { val.Write(item19 ?? ""); } List list17 = Games ?? new List(); val.Write(list17.Count); foreach (FehuGameTable item20 in list17) { val.Write(item20.GameId); val.Write((int)item20.GameType); val.Write(item20.TableName ?? ""); val.Write(item20.HostPlayerId); val.Write(item20.HostPlayerName ?? ""); val.Write(item20.PlayerIds?.Count ?? 0); foreach (long item21 in item20.PlayerIds ?? new List()) { val.Write(item21); } val.Write(item20.SpectatorIds?.Count ?? 0); foreach (long item22 in item20.SpectatorIds ?? new List()) { val.Write(item22); } val.Write(item20.PlayerLimit); val.Write(item20.IsPrivate); val.Write(item20.AllowSpectators); val.Write((int)item20.StakeType); val.Write(item20.StakeAmount); val.Write(item20.StakePrefabName ?? ""); val.Write((int)item20.Status); val.Write(item20.CreatedAt); val.Write(item20.StartedAt); val.Write(item20.FinishedAt); val.Write(item20.WinnerPlayerId); val.Write(item20.Result ?? ""); val.Write(item20.DiceCount); val.Write(item20.RoundCount); val.Write((int)item20.DiceMode); val.Write(item20.EscrowPlayerIds?.Count ?? 0); foreach (long item23 in item20.EscrowPlayerIds ?? new List()) { val.Write(item23); } val.Write(item20.EscrowTotal); val.Write(item20.EscrowCollecting); val.Write(item20.PayoutIssued); WriteChessState(val, item20.Chess); val.Write(item20.ChessHostColorPreference); val.Write(item20.ChessTimeControlSeconds); WritePokerState(val, item20.Poker); val.Write(item20.PokerBuyIn); val.Write(item20.PokerSmallBlind); val.Write(item20.PokerBigBlind); } List list18 = GameInvites ?? new List(); val.Write(list18.Count); foreach (FehuGameInvite item24 in list18) { val.Write(item24.GameId); val.Write((int)item24.GameType); val.Write(item24.FromPlayerId); val.Write(item24.ToPlayerId); val.Write(item24.CreatedAt); val.Write(item24.ExpiresAt); } List list19 = GameHistory ?? new List(); val.Write(list19.Count); foreach (FehuGameHistoryEntry item25 in list19) { val.Write(item25.GameId); val.Write((int)item25.GameType); val.Write(item25.TableName ?? ""); val.Write(item25.PlayerIds?.Count ?? 0); foreach (long item26 in item25.PlayerIds ?? new List()) { val.Write(item26); } val.Write(item25.WinnerPlayerId); val.Write(item25.Result ?? ""); val.Write((int)item25.StakeType); val.Write(item25.StakeAmount); val.Write(item25.StakePrefabName ?? ""); val.Write(item25.CompletedAt); } Dictionary dictionary3 = GameStats ?? new Dictionary(); val.Write(dictionary3.Count); foreach (KeyValuePair item27 in dictionary3) { FehuGamePlayerStats value4 = item27.Value; val.Write(item27.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); } 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_0ae5: Unknown result type (might be due to invalid IL or missing references) //IL_0aea: Unknown result type (might be due to invalid IL or missing references) //IL_1108: Unknown result type (might be due to invalid IL or missing references) //IL_110d: Unknown result type (might be due to invalid IL or missing references) //IL_1282: Unknown result type (might be due to invalid IL or missing references) //IL_1287: Unknown result type (might be due to invalid IL or missing references) //IL_1343: Unknown result type (might be due to invalid IL or missing references) //IL_1348: 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(); } 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 >= 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; } warfareData.Clans[clan.ClanId] = clan; } int num4 = val.ReadInt(); for (int k = 0; k < num4; k++) { 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 num5 = val.ReadInt(); for (int l = 0; l < num5; l++) { playerClanInfo.FreebornFriends.Add(val.ReadLong()); } int num6 = val.ReadInt(); for (int m = 0; m < num6; m++) { playerClanInfo.PendingFreebornFriendRequests.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(); 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 num7 = val.ReadInt(); for (int n = 0; n < num7; n++) { playerClanInfo.SagaEntries.Add(val.ReadString()); } int num8 = val.ReadInt(); for (int num9 = 0; num9 < num8; num9++) { playerClanInfo.RoleMinutes[val.ReadInt()] = val.ReadLong(); } } warfareData.Players[playerClanInfo.PlayerId] = playerClanInfo; } int num10 = val.ReadInt(); for (int num11 = 0; num11 < num10; num11++) { 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 >= 19) { int num12 = val.ReadInt(); clanTerritory.Buildings = new List(); for (int num13 = 0; num13 < num12; num13++) { clanTerritory.Buildings.Add(new TerritoryBuilding { Type = (TerritoryBuildingType)val.ReadInt(), Level = val.ReadInt(), Damaged = val.ReadBool(), BuiltAt = val.ReadLong(), LastProductionTimestamp = val.ReadLong() }); } int num14 = val.ReadInt(); clanTerritory.TerritoryStorage = new List(); for (int num15 = 0; num15 < num14; num15++) { 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 num16 = val.ReadInt(); for (int num17 = 0; num17 < num16; num17++) { int key3 = val.ReadInt(); int num18 = val.ReadInt(); List list = new List(); for (int num19 = 0; num19 < num18; num19++) { list.Add(new ClanVaultItem { PrefabName = val.ReadString(), DisplayName = val.ReadString(), Amount = val.ReadInt() }); } warfareData.ClanVaults[key3] = list; } if (num >= 10) { int num20 = val.ReadInt(); for (int num21 = 0; num21 < num20; num21++) { long key4 = val.ReadLong(); int num22 = val.ReadInt(); List list2 = new List(); for (int num23 = 0; num23 < num22; num23++) { list2.Add(new ClanVaultItem { PrefabName = val.ReadString(), DisplayName = val.ReadString(), Amount = val.ReadInt() }); } warfareData.FreebornVaults[key4] = list2; } } int num24 = val.ReadInt(); for (int num25 = 0; num25 < num24; num25++) { warfareData.RotTotems.Add(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() }); } int num26 = val.ReadInt(); for (int num27 = 0; num27 < num26; num27++) { 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 num28 = val.ReadInt(); for (int num29 = 0; num29 < num28; num29++) { 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 num30 = val.ReadInt(); for (int num31 = 0; num31 < num30; num31++) { Alliance alliance = new Alliance { AllianceId = val.ReadInt(), AllianceName = val.ReadString(), AllianceLeaderClanId = val.ReadInt() }; int num32 = val.ReadInt(); for (int num33 = 0; num33 < num32; num33++) { alliance.MemberClanIds.Add(val.ReadInt()); } alliance.AllianceColorR = val.ReadSingle(); alliance.AllianceColorG = val.ReadSingle(); alliance.AllianceColorB = val.ReadSingle(); alliance.CreatedAt = val.ReadLong(); int num34 = val.ReadInt(); for (int num35 = 0; num35 < num34; num35++) { alliance.PendingInvites.Add(val.ReadInt()); } warfareData.Alliances.Add(alliance); } int num36 = val.ReadInt(); for (int num37 = 0; num37 < num36; num37++) { 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 num38 = val.ReadInt(); for (int num39 = 0; num39 < num38; num39++) { 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 num40 = val.ReadInt(); for (int num41 = 0; num41 < num40; num41++) { 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 num42 = val.ReadInt(); for (int num43 = 0; num43 < num42; num43++) { warfareData.BuildingProductionLog.Add(val.ReadString()); } } int num44 = val.ReadInt(); for (int num45 = 0; num45 < num44; num45++) { 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 num46 = val.ReadInt(); for (int num47 = 0; num47 < num46; num47++) { 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 num48 = val.ReadInt(); for (int num49 = 0; num49 < num48; num49++) { 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 num50 = val.ReadInt(); for (int num51 = 0; num51 < num50; num51++) { warfareData.GuardLog.Add(val.ReadString()); } } if (num >= 24) { int num52 = val.ReadInt(); for (int num53 = 0; num53 < num52; num53++) { 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 num54 = val.ReadInt(); for (int num55 = 0; num55 < num54; num55++) { warfareData.WorldEventLog.Add(val.ReadString()); } } if (num >= 25) { int num56 = val.ReadInt(); for (int num57 = 0; num57 < num56; num57++) { 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 num58 = val.ReadInt(); for (int num59 = 0; num59 < num58; num59++) { warfareData.ContractLog.Add(val.ReadString()); } } if (num >= 28) { int num60 = val.ReadInt(); for (int num61 = 0; num61 < num60; num61++) { FehuGameTable fehuGameTable = new FehuGameTable { GameId = val.ReadInt(), GameType = (FehuGameType)val.ReadInt(), TableName = val.ReadString(), HostPlayerId = val.ReadLong(), HostPlayerName = val.ReadString() }; int num62 = val.ReadInt(); for (int num63 = 0; num63 < num62; num63++) { fehuGameTable.PlayerIds.Add(val.ReadLong()); } int num64 = val.ReadInt(); for (int num65 = 0; num65 < num64; num65++) { 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 num66 = val.ReadInt(); for (int num67 = 0; num67 < num66; num67++) { 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); fehuGameTable.PokerBuyIn = val.ReadLong(); fehuGameTable.PokerSmallBlind = val.ReadLong(); fehuGameTable.PokerBigBlind = val.ReadLong(); } warfareData.Games.Add(fehuGameTable); } int num68 = val.ReadInt(); for (int num69 = 0; num69 < num68; num69++) { 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 num70 = val.ReadInt(); for (int num71 = 0; num71 < num70; num71++) { FehuGameHistoryEntry fehuGameHistoryEntry = new FehuGameHistoryEntry { GameId = val.ReadInt(), GameType = (FehuGameType)val.ReadInt(), TableName = val.ReadString() }; int num72 = val.ReadInt(); for (int num73 = 0; num73 < num72; num73++) { 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(); warfareData.GameHistory.Add(fehuGameHistoryEntry); } int num74 = val.ReadInt(); for (int num75 = 0; num75 < num74; num75++) { long num76 = val.ReadLong(); FehuGamePlayerStats fehuGamePlayerStats = new FehuGamePlayerStats { PlayerId = num76, 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(); } warfareData.GameStats[num76] = fehuGamePlayerStats; } } 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 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 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 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 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 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] { "⚔", "☠", "⚓", "⚑", "⚜", "♦", "★", "⚡", "♠", "☸", "✪", "◈" }; 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) { 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."); 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."); 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", "ClanHall=1500,Treasury=750,Marketplace=1000,TradePost=750,LumberCamp=500,Quarry=500,CopperMine=750,IronMine=750,SilverMine=750,Farm=500,FishingPort=500,Barracks=1000,Watchtower=750", "Per-building build coin costs."); BuildingUpgradeCostByType = config.Bind("Buildings", "BuildingUpgradeCostByType", "ClanHall=1000,Treasury=750,Marketplace=750,TradePost=650,LumberCamp=500,Quarry=500,CopperMine=650,IronMine=650,SilverMine=650,Farm=500,FishingPort=500,Barracks=750,Watchtower=650", "Per-building upgrade coin cost per next level."); BuildingRepairCostByType = config.Bind("Buildings", "BuildingRepairCostByType", "ClanHall=500,Treasury=300,Marketplace=300,TradePost=300,LumberCamp=250,Quarry=250,CopperMine=300,IronMine=300,SilverMine=300,Farm=250,FishingPort=250,Barracks=350,Watchtower=300", "Per-building repair coin cost per current level."); BuildingProductionByType = config.Bind("Buildings", "BuildingProductionByType", "Treasury=30,Marketplace=100,TradePost=60,LumberCamp=20,Quarry=20,CopperMine=10,IronMine=10,SilverMine=8,Farm=15,FishingPort=10", "Per-building base production per level and production tick."); 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=2", "Per-building max copies per territory. Use Default=2 or entries such as ClanHall=1."); BuildingBiomeRequirementByType = config.Bind("Buildings", "BuildingBiomeRequirementByType", "ClanHall=Capital,LumberCamp=Meadows|BlackForest,Quarry=Land,CopperMine=BlackForest,IronMine=Swamp,SilverMine=Mountain,Farm=Meadows|Plains,FishingPort=Ocean|Coastal", "Per-building biome requirements. Supported values include Any, Capital, Land, Coastal, Meadows, BlackForest, Swamp, Mountain, Plains, Ocean."); 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."); 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."); 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", 50f, "Build cost reduction (%) for Builders in owned territory."); GathererCarryWeightBonus = config.Bind("Roles", "GathererCarryWeightBonus", 500, "Extra carry weight for Gatherers."); 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", 2, "Base Outpost allowance for Freeborn players."); FreebornMaxOutposts = config.Bind("Freeborn", "FreebornMaxOutposts", 5, "Maximum Outposts per Freeborn player."); 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."); 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, "Real hours per rot decay tick (each active totem adds 1 rot)."); 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 — 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; } clan.Treasury += result2; 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; } clan.Treasury += result2; 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 { clan.Treasury += result2; 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); } } public static class Economy { 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) { if (data == null) { return 0L; } List clanTerritories = data.GetClanTerritories(clanId); float globalMultiplier = GlobalUpkeepMultiplier(clanTerritories.Count); long num = 0L; foreach (ClanTerritory item in clanTerritories) { num += TerritoryIncome(item) - ApplyBuildingUpkeepReduction(item, ApplyWardenUpkeepReduction(data, clanId, TerritoryUpkeep(item, globalMultiplier))); } return num; } 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 RoleUtils.GetRoleSlot(player, ClanRole.Explorer) switch { RoleSlot.Primary => 0.5f, RoleSlot.Secondary => 0.3f, RoleSlot.Tertiary => 0.2f, _ => 0f, }; } 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 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 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 class WarfareManager : MonoBehaviour { public sealed class AdminAccessInfo { public bool DebugMode; public bool LocalHost; public bool ServerAdmin; public bool ExistingFcwDebug; public bool EventAdminAccess { get { if (!DebugMode && !LocalHost && !ServerAdmin) { return ExistingFcwDebug; } return true; } } } public class ClaimResult { public bool Success; public string Message; public ClaimResult(bool s, string m) { Success = s; Message = m; } } private static readonly TerritoryBuildingType[] BuildableBuildingTypes = new TerritoryBuildingType[13] { TerritoryBuildingType.ClanHall, TerritoryBuildingType.Treasury, TerritoryBuildingType.Marketplace, TerritoryBuildingType.TradePost, TerritoryBuildingType.LumberCamp, TerritoryBuildingType.Quarry, TerritoryBuildingType.CopperMine, TerritoryBuildingType.IronMine, TerritoryBuildingType.SilverMine, TerritoryBuildingType.Farm, TerritoryBuildingType.FishingPort, TerritoryBuildingType.Barracks, TerritoryBuildingType.Watchtower }; private static readonly string[] BuildingCategoryNames = new string[5] { "Administration", "Economy", "Resources", "Food", "Military" }; private static readonly TerritoryBuildingType[][] BuildingCategoryTypes = new TerritoryBuildingType[5][] { new TerritoryBuildingType[1] { TerritoryBuildingType.ClanHall }, new TerritoryBuildingType[3] { TerritoryBuildingType.Treasury, TerritoryBuildingType.Marketplace, TerritoryBuildingType.TradePost }, new TerritoryBuildingType[5] { TerritoryBuildingType.LumberCamp, TerritoryBuildingType.Quarry, TerritoryBuildingType.CopperMine, TerritoryBuildingType.IronMine, TerritoryBuildingType.SilverMine }, new TerritoryBuildingType[2] { TerritoryBuildingType.Farm, TerritoryBuildingType.FishingPort }, new TerritoryBuildingType[2] { TerritoryBuildingType.Barracks, TerritoryBuildingType.Watchtower } }; 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 float _saveTimer; private float _captureTimer; private float _miscTimer; private float _slowTimer; private readonly SharedClanStore _sharedClanStore = new SharedClanStore(); private bool _sharedDirty; private bool? _serverVaultOpenState; 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(); ReleaseDetectedProtectedZoneConflicts(autoNotify: true); 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 string UpdateProfile(long playerId, string aboutMe, string activeTitle) { 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(); if (aboutMe.Length > num) { aboutMe = aboutMe.Substring(0, num); } if (activeTitle.Length > 80) { activeTitle = activeTitle.Substring(0, 80); } player.AboutMe = aboutMe; player.ActiveTitle = activeTitle; PlayerClanInfo playerClanInfo = player; if (playerClanInfo.SagaEntries == null) { playerClanInfo.SagaEntries = new List(); } player.SagaEntries.RemoveAll((string entry) => string.IsNullOrWhiteSpace(entry)); player.SagaEntries.Insert(0, "Profile updated - " + DateTime.UtcNow.ToString("yyyy-MM-dd")); if (player.SagaEntries.Count > 30) { player.SagaEntries.RemoveRange(30, player.SagaEntries.Count - 30); } MarkDirty(); return "Profile updated."; } 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(); 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_0123: 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_016f: 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_008c: Unknown result type (might be due to invalid IL or missing references) //IL_01b4: Unknown result type (might be due to invalid IL or missing references) //IL_0205: 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_00eb: 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; } ConfigEntry allowFreebornVaultInOutposts = ClanConfig.AllowFreebornVaultInOutposts; bool flag = (allowFreebornVaultInOutposts != null && allowFreebornVaultInOutposts.Value) || (ClanConfig.AllowVaultInFreebornOutposts?.Value ?? 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_01b2; } if (territory.Type == TerritoryType.Colony) { ConfigEntry allowVaultInClanColonies = ClanConfig.AllowVaultInClanColonies; if (allowVaultInClanColonies != null && allowVaultInClanColonies.Value) { goto IL_01b2; } } } 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_01b2: 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; default: return "Unknown vault permission."; } MarkDirty(); return "Vault 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 void MarkPlayerActive(long playerId) { PlayerClanInfo player = Data.GetPlayer(playerId); if (player == null) { return; } long num = (player.LastSeenTimestamp = Now()); 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; } MarkDirty(); } 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_05fc: 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 value in Data.Clans.Values) { if (string.Equals(value.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(value.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 — {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 value2 in Data.Clans.Values) { if (ClanConfig.ColorsClose(value2.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 value3 in Data.Clans.Values) { if (string.Equals(NormalizeEmblemName(value3.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; Data.Clans[num2] = clan; orCreatePlayer.ClanId = num2; orCreatePlayer.IsFreeborn = false; orCreatePlayer.Rank = ClanRank.Jarl; orCreatePlayer.JoinedClanTimestamp = Now(); foreach (ClanTerritory value4 in Data.Territories.Values) { if (value4.FreebornOwnerId == playerId) { value4.OwnerClanId = num2; value4.FreebornOwnerId = 0L; value4.FreebornOwnerName = null; value4.OdinsPeaceActive = false; value4.Type = ((value4.Type == TerritoryType.Homestead) ? TerritoryType.Capital : TerritoryType.Colony); } } 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) { foreach (PlayerClanInfo value in Data.Players.Values) { if (value.ClanId == clanId) { value.ClanId = 0; value.Rank = ClanRank.Member; 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.RotTotems.RemoveAll((RotTotem t) => t.PlacerClanId == clanId); Data.ActiveCaptures.RemoveAll((CaptureSession c) => c.AttackerClanId == clanId || c.DefenderClanId == clanId); Data.ActiveCracks.RemoveAll((VaultCrackSession c) => c.PlayerClanId == clanId || c.TargetClanId == clanId); } public string JoinClan(long playerId, string playerName, int clanId) { //IL_0158: 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)."; } clan.Treasury -= num2; } orCreatePlayer.ClanId = clanId; orCreatePlayer.IsFreeborn = false; orCreatePlayer.Rank = ClanRank.Member; orCreatePlayer.Role = ClanRole.None; orCreatePlayer.PrimaryRole = ClanRole.None; orCreatePlayer.SecondaryRole = ClanRole.None; orCreatePlayer.TertiaryRole = ClanRole.None; orCreatePlayer.JoinedClanTimestamp = Now(); clan.LastActiveTimestamp = Now(); MarkDirty(); MarkSharedDirtyAndSave(); WarfareRPC.Instance?.BroadcastGlobalMessage("" + playerName + " joined " + clan.Name + "!"); return "Joined " + clan.Name + "."; } public string LeaveClan(long playerId) { //IL_00b8: 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.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; 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 — 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.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; 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); 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(); return $"Primary role changed to {role}."; } 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); player.NormalizeRoles(); if (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(); return $"Secondary role changed to {role}."; } 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); player.NormalizeRoles(); if (player.PrimaryRole == role || player.SecondaryRole == role) { return "Cannot use the same role in multiple slots."; } player.TertiaryRole = role; player.LastRoleChangeTimestamp = Now(); MarkDirty(); MarkSharedDirtyAndSave(); return $"Tertiary role changed to {role}."; } 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(); switch (role) { case ClanRole.None: return "Select a valid role."; case ClanRole.TwelfthPath: return "The Twelfth Path is coming soon."; default: { if (!primary && !tertiary) { ConfigEntry enableSecondaryRole = ClanConfig.EnableSecondaryRole; if (enableSecondaryRole == null || !enableSecondaryRole.Value) { return "Secondary roles are disabled."; } } if (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 (!primary && !tertiary && (player.PrimaryRole == role || player.TertiaryRole == role)) { return "Cannot use the same role in multiple slots."; } if (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_0317: 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_02a6: 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; 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."; } clan.Treasury -= num2; 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 — {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 EconomyTick() { long num = Now(); int num2 = Mathf.Max(60, (ClanConfig.UpkeepTickMinutes?.Value ?? 30) * 60); Dictionary dictionary = new Dictionary(); Dictionary dictionary2 = new Dictionary(); foreach (ClanTerritory value5 in Data.Territories.Values) { if (value5.OwnerClanId != 0) { dictionary[value5.OwnerClanId] = ((!dictionary.TryGetValue(value5.OwnerClanId, out var value)) ? 1 : (value + 1)); } else if (value5.FreebornOwnerId != 0L) { dictionary2[value5.FreebornOwnerId] = ((!dictionary2.TryGetValue(value5.FreebornOwnerId, out var value2)) ? 1 : (value2 + 1)); } } bool flag = false; foreach (ClanTerritory value6 in Data.Territories.Values) { if (value6.OwnerClanId == 0 && value6.FreebornOwnerId == 0L) { continue; } if (value6.LastIncomeTimestamp == 0L) { value6.LastIncomeTimestamp = num; flag = true; } else { if (num - value6.LastIncomeTimestamp < num2) { continue; } int num3 = (int)((num - value6.LastIncomeTimestamp) / num2); num3 = Mathf.Clamp(num3, 1, 48); Clan clan = Data.GetClan(value6.OwnerClanId); if (clan != null) { int value3; int territoryCount = ((!dictionary.TryGetValue(value6.OwnerClanId, out value3)) ? 1 : value3); float globalMultiplier = Economy.GlobalUpkeepMultiplier(territoryCount); long upkeep = Economy.TerritoryUpkeep(value6, globalMultiplier); long num4 = Economy.ApplyBuildingUpkeepReduction(value6, Economy.ApplyWardenUpkeepReduction(Data, value6.OwnerClanId, upkeep)); long num5 = (Economy.TerritoryIncome(value6) - num4 - ProgressiveTerritoryUpkeepCost(territoryCount)) * num3; clan.Treasury += num5; if (clan.Treasury < 0) { clan.Treasury = 0L; value6.State = TerritoryState.Attacked; } value6.LastIncomeTimestamp = num; flag = true; } else { if (value6.FreebornOwnerId == 0L) { continue; } PlayerClanInfo player = Data.GetPlayer(value6.FreebornOwnerId); if (player != null) { int value4; int territoryCount2 = ((!dictionary2.TryGetValue(value6.FreebornOwnerId, out value4)) ? 1 : value4); float globalMultiplier2 = Economy.GlobalUpkeepMultiplier(territoryCount2); long num6 = Economy.ApplyBuildingUpkeepReduction(value6, Economy.TerritoryUpkeep(value6, globalMultiplier2)); long num7 = (Economy.TerritoryIncome(value6) - num6 - ProgressiveTerritoryUpkeepCost(territoryCount2)) * num3; player.FreebornTreasury += num7; if (player.FreebornTreasury < 0) { player.FreebornTreasury = 0L; value6.State = TerritoryState.Attacked; } value6.LastIncomeTimestamp = num; flag = true; } } } } if (flag) { MarkDirty(); } if (BuildingProductionTick(num)) { MarkDirty(); } if (GuardUpkeepTick(num, num2)) { MarkDirty(); } } 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(); clan.Treasury -= num2; 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."; } clan.Treasury -= num; 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 { clan.Treasury -= num2; 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(long playerId, long senderPeerId, Vector3 position) { //IL_0004: Unknown result type (might be due to invalid IL or missing references) return CreateWorldEvent(playerId, senderPeerId, WorldEventType.BanditCamp, position); } public string CreateWorldEvent(long playerId, long senderPeerId, WorldEventType eventType, Vector3 position) { //IL_0010: 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_0023: Unknown result type (might be due to invalid IL or missing references) //IL_002e: Expected O, but got Unknown //IL_00c5: 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_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) if (!HasEventAdminAccess(playerId, senderPeerId)) { 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(long playerId, long senderPeerId, int eventId) { if (!HasEventAdminAccess(playerId, senderPeerId)) { 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(long playerId, long senderPeerId, int eventId) { if (!HasEventAdminAccess(playerId, senderPeerId)) { 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) { clan.Treasury += worldEvent.RewardCoins; } } else { player.FreebornTreasury += worldEvent.RewardCoins; } worldEvent.Status = WorldEventStatus.Completed; worldEvent.ClaimedByPlayerId = playerId; worldEvent.ClaimedByPlayerName = playerName ?? player.PlayerName ?? ""; 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(long playerId, long senderPeerId, int clanId) { if (!HasEventAdminAccess(playerId, senderPeerId)) { 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(long playerId, long senderPeerId, long freebornPlayerId) { if (!HasEventAdminAccess(playerId, senderPeerId)) { 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(long playerId, long senderPeerId) { if (!HasEventAdminAccess(playerId, senderPeerId)) { return "Admin/debug mode required."; } int num = ReleaseDetectedProtectedZoneConflicts(autoNotify: false); if (num == 0) { return "No protected zone conflicts found."; } return "Released " + num + " protected zone conflict(s)."; } private int ReleaseDetectedProtectedZoneConflicts(bool autoNotify) { //IL_0046: Unknown result type (might be due to invalid IL or missing references) List list = new List(); 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)) { list.Add(WarfareData.GridToKey(item.GridX, item.GridY)); AddWorldEventLog("Released protected zone conflict: " + item.DisplayName + " (" + info.Name + ")"); } } foreach (string item2 in list) { Data.Territories.Remove(item2); string[] array = item2.Split(new char[1] { ',' }); if (array.Length == 2 && int.TryParse(array[0], out var x) && int.TryParse(array[1], out var y)) { Data.ActiveCaptures.RemoveAll((CaptureSession c) => c.TerritoryGridX == x && c.TerritoryGridY == y); Data.RotTotems.RemoveAll((RotTotem t) => t.TerritoryGridX == x && t.TerritoryGridY == y); } } if (list.Count > 0) { MarkDirty(); if (autoNotify) { WarfareRPC.Instance?.BroadcastGlobalMessage("Protected World Zone repaired: trader territory returned to WORLD."); } } return list.Count; } public AdminAccessInfo GetAdminAccessInfo(long playerId, long senderPeerId = 0L) { 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 = IsLocalHostAdmin(playerId, senderPeerId); adminAccessInfo.ServerAdmin = IsServerAdmin(playerId, senderPeerId); return adminAccessInfo; } public bool HasEventAdminAccess(long playerId, long senderPeerId = 0L) { return GetAdminAccessInfo(playerId, senderPeerId).EventAdminAccess; } private bool IsLocalHostAdmin(long playerId, long senderPeerId) { //IL_0005: Unknown result type (might be due to invalid IL or missing references) //IL_0010: Expected O, but got Unknown try { if ((Object)ZNet.instance == (Object)null || !ZNet.instance.IsServer()) { return false; } if (senderPeerId != 0L && senderPeerId == ZNet.GetUID()) { return true; } Player localPlayer = Player.m_localPlayer; return (Object)(object)localPlayer != (Object)null && playerId != 0L && localPlayer.GetPlayerID() == playerId; } catch { return false; } } private bool IsServerAdmin(long playerId, long senderPeerId) { //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 false; } object instance = ZNet.instance; if (InvokeAdminCheck(instance, playerId, senderPeerId)) { return true; } return AdminCollectionContains(instance, playerId, senderPeerId); } private bool InvokeAdminCheck(object target, long playerId, long senderPeerId) { MethodInfo[] methods = target.GetType().GetMethods(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); foreach (MethodInfo methodInfo in methods) { if (methodInfo.ReturnType != typeof(bool) || !methodInfo.Name.ToLowerInvariant().Contains("admin")) { continue; } ParameterInfo[] parameters = methodInfo.GetParameters(); if (parameters.Length != 1) { continue; } foreach (object item in AdminCheckArgs(parameters[0].ParameterType, playerId, senderPeerId)) { try { if ((bool)methodInfo.Invoke(target, new object[1] { item })) { return true; } } catch { } } } return false; } private IEnumerable AdminCheckArgs(Type parameterType, long playerId, long senderPeerId) { if (parameterType == typeof(long)) { yield return playerId; if (senderPeerId != 0L) { yield return senderPeerId; } } else if (parameterType == typeof(ulong)) { yield return (ulong)playerId; if (senderPeerId != 0L) { yield return (ulong)senderPeerId; } } else if (parameterType == typeof(string)) { yield return playerId.ToString(); if (senderPeerId != 0L) { yield return senderPeerId.ToString(); } } } private bool AdminCollectionContains(object target, long playerId, long senderPeerId) { string playerText = playerId.ToString(); string peerText = senderPeerId.ToString(); FieldInfo[] fields = target.GetType().GetFields(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); foreach (FieldInfo fieldInfo in fields) { if (!fieldInfo.Name.ToLowerInvariant().Contains("admin")) { continue; } try { if (CollectionContainsAdminId(fieldInfo.GetValue(target), playerText, peerText, senderPeerId != 0)) { return true; } } catch { } } return false; } private bool CollectionContainsAdminId(object value, string playerText, string peerText, bool hasPeer) { if (value is IEnumerable enumerable && !(value is string)) { foreach (object item in enumerable) { string text = item?.ToString() ?? ""; if (text == playerText || (hasPeer && text == peerText) || text.Contains(playerText) || (hasPeer && text.Contains(peerText))) { return true; } } } return false; } 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(); } } private 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) { GetBuildingOutput(territory, building, out var prefab, out var display, out var amount, out var coins); PlayerClanInfo playerClanInfo = null; if (territory.FreebornOwnerId != 0L) { playerClanInfo = Data.GetPlayer(territory.FreebornOwnerId); } else if (territory.OwnerClanId != 0) { playerClanInfo = GetBestClanRolePlayer(territory.OwnerClanId, IsCoinBuilding(building.Type) ? ClanRole.Merchant : ClanRole.Gatherer); } float buildingProductionMultiplier = GetBuildingProductionMultiplier(playerClanInfo, building.Type); if (coins > 0) { long num = Mathf.FloorToInt((float)(coins * ticks) * buildingProductionMultiplier); if (territory.OwnerClanId != 0) { Clan clan = Data.GetClan(territory.OwnerClanId); if (clan != null) { clan.Treasury += num; AddBuildingProductionLog(BuildingName(building.Type) + " Lv" + building.Level + " produced " + num + " Coins to Clan Treasury."); } } else if (playerClanInfo != null) { playerClanInfo.FreebornTreasury += num; AddBuildingProductionLog(BuildingName(building.Type) + " Lv" + building.Level + " produced " + num + " Coins to Freeborn Treasury."); } } if (!string.IsNullOrEmpty(prefab) && amount > 0) { int amount2 = Mathf.Max(1, Mathf.FloorToInt((float)(amount * ticks) * buildingProductionMultiplier)); AddToTerritoryStorage(territory, prefab, display, amount2); if (territory.OwnerClanId != 0) { Data.AddToVault(territory.OwnerClanId, prefab, display, amount2); AddBuildingProductionLog(BuildingName(building.Type) + " Lv" + building.Level + " produced " + amount2 + " " + display + " to Clan Vault."); } else if (territory.FreebornOwnerId != 0L) { Data.AddToFreebornVault(territory.FreebornOwnerId, prefab, display, amount2); AddBuildingProductionLog(BuildingName(building.Type) + " Lv" + building.Level + " produced " + amount2 + " " + display + " to Freeborn Vault."); } } } 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) * 0.25f; } if (IsResourceBuilding(type)) { return 1f + RoleUtils.GetRoleFactor(player, ClanRole.Gatherer) * 0.25f; } return 1f; } private int ProgressiveTerritoryUpkeepCost(int territoryCount) { ConfigEntry enableProgressiveTerritoryUpkeep = ClanConfig.EnableProgressiveTerritoryUpkeep; if (enableProgressiveTerritoryUpkeep == null || !enableProgressiveTerritoryUpkeep.Value || territoryCount <= (ClanConfig.FreeTerritoriesBeforeUpkeep?.Value ?? 3)) { return 0; } if (territoryCount >= (ClanConfig.TerritoryUpkeepTier1Start?.Value ?? 6) && territoryCount <= (ClanConfig.TerritoryUpkeepTier1End?.Value ?? 10)) { return ClanConfig.TerritoryUpkeepTier1Cost?.Value ?? 500; } if (territoryCount >= (ClanConfig.TerritoryUpkeepTier2Start?.Value ?? 11) && territoryCount <= (ClanConfig.TerritoryUpkeepTier2End?.Value ?? 20)) { return ClanConfig.TerritoryUpkeepTier2Cost?.Value ?? 1000; } if (territoryCount >= (ClanConfig.TerritoryUpkeepTier3Start?.Value ?? 21) && territoryCount <= (ClanConfig.TerritoryUpkeepTier3End?.Value ?? 100)) { return ClanConfig.TerritoryUpkeepTier3Cost?.Value ?? 2000; } return 0; } 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) { if (type != TerritoryBuildingType.None) { return GetBuildingIntConfig(ClanConfig.BuildingMaxCopiesByType?.Value, type, 2); } return 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) { string buildingStringConfig = GetBuildingStringConfig(ClanConfig.BuildingBiomeRequirementByType?.Value, type, ""); if (!string.IsNullOrEmpty(buildingStringConfig)) { return buildingStringConfig.Replace("|", " or ").Replace("BlackForest", "Black Forest"); } return type switch { TerritoryBuildingType.ClanHall => "Capital only", TerritoryBuildingType.LumberCamp => "Meadows or Black Forest", TerritoryBuildingType.Quarry => "Most land territories", TerritoryBuildingType.CopperMine => "Black Forest", TerritoryBuildingType.IronMine => "Swamp", TerritoryBuildingType.SilverMine => "Mountain", TerritoryBuildingType.Farm => "Meadows or Plains", TerritoryBuildingType.FishingPort => "Ocean or coastal territory", _ => "Any owned territory", }; } 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; } if (!territory.IsCapital) { return 3; } return 5; } 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.ClanHall => "Clan Hall", TerritoryBuildingType.TradePost => "Trade Post", TerritoryBuildingType.LumberCamp => "Lumber Camp", TerritoryBuildingType.CopperMine => "Copper Mine", TerritoryBuildingType.IronMine => "Iron Mine", TerritoryBuildingType.SilverMine => "Silver Mine", TerritoryBuildingType.FishingPort => "Fishing Port", TerritoryBuildingType.Marketplace => "Marketplace", TerritoryBuildingType.Treasury => "Treasury", TerritoryBuildingType.Quarry => "Quarry", TerritoryBuildingType.Farm => "Farm", TerritoryBuildingType.Barracks => "Barracks", TerritoryBuildingType.Watchtower => "Watchtower", _ => "Empty", }; } public static string BuildingIconName(TerritoryBuildingType type) { return BuildingName(type).Replace(" ", "_").ToLowerInvariant(); } 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) { if (type != TerritoryBuildingType.LumberCamp && type != TerritoryBuildingType.Quarry && type != TerritoryBuildingType.CopperMine && type != TerritoryBuildingType.IronMine && type != TerritoryBuildingType.SilverMine && type != TerritoryBuildingType.Farm) { return type == TerritoryBuildingType.FishingPort; } return true; } 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 = Mathf.Clamp(building?.Level ?? 1, 1, 5); int buildingIntConfig = GetBuildingIntConfig(ClanConfig.BuildingProductionByType?.Value, building?.Type ?? TerritoryBuildingType.None, 0); switch (building?.Type ?? TerritoryBuildingType.None) { case TerritoryBuildingType.Treasury: coins = ((buildingIntConfig > 0) ? buildingIntConfig : 30) * num; break; case TerritoryBuildingType.Marketplace: coins = ((buildingIntConfig > 0) ? buildingIntConfig : 100) * num; break; case TerritoryBuildingType.TradePost: coins = ((buildingIntConfig > 0) ? buildingIntConfig : 60) * num; break; case TerritoryBuildingType.LumberCamp: prefab = "Wood"; display = "Wood"; amount = ((buildingIntConfig > 0) ? buildingIntConfig : 20) * num; break; case TerritoryBuildingType.Quarry: prefab = "Stone"; display = "Stone"; amount = ((buildingIntConfig > 0) ? buildingIntConfig : 20) * num; break; case TerritoryBuildingType.CopperMine: prefab = "CopperOre"; display = "Copper Ore"; amount = ((buildingIntConfig > 0) ? buildingIntConfig : 10) * num; break; case TerritoryBuildingType.IronMine: prefab = "IronScrap"; display = "Scrap Iron"; amount = ((buildingIntConfig > 0) ? buildingIntConfig : 10) * num; break; case TerritoryBuildingType.SilverMine: prefab = "SilverOre"; display = "Silver Ore"; amount = ((buildingIntConfig > 0) ? buildingIntConfig : 8) * num; break; case TerritoryBuildingType.Farm: prefab = "Barley"; display = "Barley"; amount = ((buildingIntConfig > 0) ? buildingIntConfig : 15) * num; break; case TerritoryBuildingType.FishingPort: prefab = "FishRaw"; display = "Raw Fish"; amount = ((buildingIntConfig > 0) ? buildingIntConfig : 10) * num; break; } } public static string BuildingProductionText(ClanTerritory territory, TerritoryBuilding building) { string text = BuildingProductionValueText(territory, building); if (text == "No production") { return text; } if (building != null && (building.Type == TerritoryBuildingType.Barracks || building.Type == TerritoryBuildingType.Watchtower || building.Type == TerritoryBuildingType.ClanHall)) { return "Bonus: " + text; } return "Produces: " + text + " / 12h"; } 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.Barracks || building.Type == TerritoryBuildingType.Watchtower)) { return "Defense and upkeep support"; } if (building != null && building.Type == TerritoryBuildingType.ClanHall) { return "Capital administration"; } return "No production"; } public static bool IsBuildingAllowedInBiome(ClanTerritory territory, TerritoryBuildingType type) { if (type == TerritoryBuildingType.None) { return false; } string buildingStringConfig = GetBuildingStringConfig(ClanConfig.BuildingBiomeRequirementByType?.Value, type, ""); if (!string.IsNullOrEmpty(buildingStringConfig)) { return BuildingRequirementMatches(territory, buildingStringConfig); } string text = (territory?.BiomeName ?? "").ToLowerInvariant(); switch (type) { case TerritoryBuildingType.ClanHall: return territory?.IsCapital ?? false; case TerritoryBuildingType.LumberCamp: if (!text.Contains("meadows")) { return text.Contains("black"); } return true; case TerritoryBuildingType.CopperMine: return text.Contains("black"); case TerritoryBuildingType.IronMine: return text.Contains("swamp"); case TerritoryBuildingType.SilverMine: return text.Contains("mountain"); case TerritoryBuildingType.Farm: if (!text.Contains("meadows")) { return text.Contains("plains"); } return true; case TerritoryBuildingType.FishingPort: if (!text.Contains("ocean")) { return IsCoastalTerritory(territory); } return true; case TerritoryBuildingType.Quarry: return !text.Contains("ocean"); default: return true; } } 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_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 (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 two copies of the same building per territory."; } long cost = ApplyBuilderDiscount(player, BuildingBuildCost(type)); 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() }); 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)); 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)); 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 + "."; } default: return "Unknown building action."; } } 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) { float num = 0f; switch (RoleUtils.GetRoleSlot(player, ClanRole.Builder)) { case RoleSlot.Primary: num = 0.5f; break; case RoleSlot.Secondary: num = 0.3f; break; case RoleSlot.Tertiary: num = 0.2f; break; } return Math.Max(0L, Mathf.FloorToInt((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; } clan.Treasury -= cost; return true; } private void RotTick() { //IL_0083: Unknown result type (might be due to invalid IL or missing references) long num = Now(); int num2 = Mathf.Max(60, (ClanConfig.RotDecayHours?.Value ?? 6) * 3600); bool flag = false; for (int num3 = Data.RotTotems.Count - 1; num3 >= 0; num3--) { RotTotem rotTotem = Data.RotTotems[num3]; if (rotTotem.State == TotemState.Destroyed) { Data.RotTotems.RemoveAt(num3); } else { ClanTerritory territory = Data.GetTerritory(rotTotem.GridPosition); if (territory == null || territory.OwnerClanId == 0) { Data.RotTotems.RemoveAt(num3); flag = true; } else if (rotTotem.LastDecayTimestamp == 0L) { rotTotem.LastDecayTimestamp = num; flag = true; } else if (num - rotTotem.LastDecayTimestamp >= num2) { int num4 = Mathf.Clamp((int)((num - rotTotem.LastDecayTimestamp) / num2), 1, 8); rotTotem.LastDecayTimestamp = num; rotTotem.State = TotemState.Active; int rotCount = territory.RotCount; territory.RotCount = Mathf.Clamp(territory.RotCount + num4, 0, 4); flag = true; if (rotCount < 4 && territory.RotCount >= 4) { territory.State = TerritoryState.Rooted; Data.ActiveCaptures.RemoveAll((CaptureSession c) => c.TerritoryGridX == territory.GridX && c.TerritoryGridY == territory.GridY); Clan clan = Data.GetClan(territory.OwnerClanId); WarfareRPC.Instance?.BroadcastGlobalMessage("" + territory.DisplayName + " (" + clan?.Name + ") has fully rotted — any clan may now seize it!"); } else if (territory.RotCount > 0 && territory.State == TerritoryState.Normal) { territory.State = TerritoryState.Contested; } } } } 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) { value.Treasury = num; value.VaultTier = 1; value.ExtraTerritorySlots = 0; } MarkDirty(); SaveData(); WarfareRPC.Instance?.BroadcastGlobalMessage($"══ THE CYCLE OF ODIN ══\nSeason {Data.SeasonNumber} begins. The map is reborn — 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."; } clan.Treasury -= num3; 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_007a: 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_008a: Unknown result type (might be due to invalid IL or missing references) //IL_00c0: Unknown result type (might be due to invalid IL or missing references) //IL_00c5: Unknown result type (might be due to invalid IL or missing references) //IL_00ca: 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_00ff: Unknown result type (might be due to invalid IL or missing references) //IL_0320: 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!"); } 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 — 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 territory = Data.GetTerritory(grid); bool flag = territory?.IsRotted ?? false; if (territory != null && territory.FreebornOwnerId != 0L && !flag) { if (IsProtectedByOdinsPeace(territory)) { return new ClaimResult(s: false, "Odin's Peace protects this Freeborn territory. Challenge it first."); } return new ClaimResult(s: false, "Freeborn land — capture it through warfare instead."); } if (territory != null && territory.OwnerClanId != 0 && !flag) { if (territory.OwnerClanId == player.ClanId) { return new ClaimResult(s: false, "Your clan already owns this territory."); } return new ClaimResult(s: false, "Enemy land — declare war and capture it instead."); } if (!flag) { ConfigEntry requireExplorerForClaim = ClanConfig.RequireExplorerForClaim; if (requireExplorerForClaim != null && requireExplorerForClaim.Value && !RoleUtils.HasRole(player, ClanRole.Explorer)) { return new ClaimResult(s: false, "Only Explorers can claim unclaimed 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 && (territory == null || (territory.OwnerClanId == 0 && territory.FreebornOwnerId == 0))) ? Economy.TerritoryAcquisitionCost(clanTerritoryCount + 1, player) : 0); if (num2 > 0) { if (clan.Treasury < num2) { return new ClaimResult(s: false, "Not enough Clan Treasury."); } clan.Treasury -= num2; } ClanTerritory clanTerritory = territory ?? 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); 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_024e: 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 — 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 >= (ClanConfig.FreebornBaseOutposts?.Value ?? 2)) { return new ClaimResult(s: false, $"Freeborn Outpost cap reached ({num2}/{ClanConfig.FreebornBaseOutposts?.Value ?? 2})."); } 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_020a; } } odinsPeaceActive = ((!flag && (ClanConfig.OdinsPeaceProtectOutposts?.Value ?? false)) ? 1 : 0); } else { odinsPeaceActive = 0; } goto IL_020a; IL_020a: 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); MarkDirty(); return new ClaimResult(s: true, "Claimed " + clanTerritory.DisplayName + " as a Freeborn " + clanTerritory.Type.ToString() + "!"); } 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."; } clan.Treasury -= num5; 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."; } clan.Treasury -= num; 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})."; } clan.Treasury -= num; long num2 = Now(); War item = 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(item); 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."; } clan.Treasury -= num; } 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."; } clan.Treasury -= territoryTransferOffer.Price; clan2.Treasury += territoryTransferOffer.Price; 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."; } clan.Treasury -= Math.Max(0L, offer.FromCoins); clan2.Treasury += Math.Max(0L, offer.FromCoins); clan2.Treasury -= Math.Max(0L, offer.ToCoins); clan.Treasury += Math.Max(0L, offer.ToCoins); 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."; } clan.Treasury += Math.Max(0L, contract.RewardCoins); } 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 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(); } } } 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_002a: Unknown result type (might be due to invalid IL or missing references) message = null; ConfigEntry enableTerritoryResourceOwnership = ClanConfig.EnableTerritoryResourceOwnership; if (enableTerritoryResourceOwnership == null || !enableTerritoryResourceOwnership.Value || 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_017f; } } if (territory.OwnerClanId != 0 && GetDiplomacyState(player.ClanId, territory.OwnerClanId) == DiplomacyState.Allied) { return true; } goto IL_017f; IL_017f: 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); 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))); clan.Treasury += num; 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.GetRoleSlot(player, ClanRole.Merchant) switch { RoleSlot.Primary => 0.25f, RoleSlot.Secondary => 0.15f, RoleSlot.Tertiary => 0.1f, _ => 0f, }; } private void TaxDebug(string message) { ConfigEntry enableTaxDebugLogs = ClanConfig.EnableTaxDebugLogs; if (enableTaxDebugLogs != null && enableTaxDebugLogs.Value) { Plugin.Log.LogInfo((object)message); } } public string PlaceRotTotem(long playerId, string playerName, Vector3 worldPos) { //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_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_01be: 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) PlayerClanInfo player = Data.GetPlayer(playerId); if (player == null || player.ClanId == 0) { return "You are not in a clan."; } if (!RoleUtils.HasRole(player, ClanRole.Shaman)) { return "Only Shamans can raise rot totems."; } Vector2i grid = GridExtensions.WorldToGrid(worldPos); ClanTerritory territory = Data.GetTerritory(grid); if (territory == null || territory.OwnerClanId == 0) { return "Rot totems can only be raised on enemy territory."; } if (territory.OwnerClanId == player.ClanId) { return "You cannot rot your own clan's land."; } if (IsHostileActionBlocked(player.ClanId, territory.OwnerClanId, out var message)) { return "" + message + ""; } if (territory.IsRotted) { return "That territory is already fully rotted."; } int count = Data.RotTotems.FindAll((RotTotem t) => t.TerritoryGridX == grid.x && t.TerritoryGridY == grid.y && t.State != TotemState.Destroyed).Count; int num = ClanConfig.MaxRotTotemsPerTerritory?.Value ?? 4; if (count >= num) { return $"That territory already holds {count}/{num} rot totems."; } Clan clan = Data.GetClan(player.ClanId); long num2 = Economy.RotTotemCost(1, territory.BiomeValueTier); if (clan.Treasury < num2) { return $"Treasury needs {num2} coins to raise a totem (has {clan.Treasury})."; } clan.Treasury -= num2; Data.RotTotems.Add(new RotTotem { TerritoryGridX = grid.x, TerritoryGridY = grid.y, PlacedByPlayerId = playerId, PlacedByPlayerName = playerName, PlacerClanId = player.ClanId, PlacedTimestamp = Now(), LastDecayTimestamp = Now(), WorldPosition = worldPos, State = TotemState.Placed, Level = 1 }); MarkDirty(); Clan clan2 = Data.GetClan(territory.OwnerClanId); WarfareRPC.Instance?.BroadcastGlobalMessage("A rot totem festers in " + clan2?.Name + "'s land at " + territory.DisplayName + "!"); return "Rot totem raised. The land will wither."; } 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 { clan.Treasury -= amount; } 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 { clan.Treasury -= amount; } 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) { clan.Treasury += player2.Bounty; } } else if (player.IsFreeborn) { player.FreebornTreasury += player2.Bounty; } WarfareRPC.Instance?.BroadcastGlobalMessage($"{player.PlayerName} claimed the {player2.Bounty}-coin bounty on {player2.PlayerName}!"); 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 — hold the ground for {num} minutes!"; } } }