Please disclose if any significant portion of your mod was created using AI tools by adding the 'AI Generated' category. Failing to do so may result in the mod being removed from Thunderstore.
Decompiled source of ValheimHouseholds v1.0.8
BepInEx\plugins\ValheimHouseholds.dll
Decompiled 14 hours ago
The result has been truncated due to the large size, download it to view full contents!
using System; using System.Collections; using System.Collections.Generic; using System.Diagnostics; using System.Globalization; using System.IO; using System.Linq; using System.Net.Http; using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.Versioning; using System.Text; using System.Threading.Tasks; using BepInEx; using BepInEx.Configuration; using BepInEx.Logging; using HarmonyLib; using Jotunn.Configs; using Jotunn.Entities; using Jotunn.Managers; using Microsoft.CodeAnalysis; using UnityEngine; using ValheimHouseholds.Chest; using ValheimHouseholds.Portal; using ValheimHouseholds.Utils; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)] [assembly: TargetFramework(".NETFramework,Version=v4.8", FrameworkDisplayName = ".NET Framework 4.8")] [assembly: AssemblyCompany("ValheimHouseholds")] [assembly: AssemblyConfiguration("Release")] [assembly: AssemblyFileVersion("1.0.8.0")] [assembly: AssemblyInformationalVersion("1.0.8+aafae9dcf99121e9c58b917308a110b352be81ae")] [assembly: AssemblyProduct("ValheimHouseholds")] [assembly: AssemblyTitle("ValheimHouseholds")] [assembly: AssemblyVersion("1.0.8.0")] [module: RefSafetyRules(11)] namespace Microsoft.CodeAnalysis { [CompilerGenerated] [Embedded] internal sealed class EmbeddedAttribute : Attribute { } } namespace System.Runtime.CompilerServices { [CompilerGenerated] [Embedded] [AttributeUsage(AttributeTargets.Module, AllowMultiple = false, Inherited = false)] internal sealed class RefSafetyRulesAttribute : Attribute { public readonly int Version; public RefSafetyRulesAttribute(int P_0) { Version = P_0; } } } namespace ValheimHouseholds { public static class ConfigValues { public static ConfigEntry<bool> AdminBypassEnabled; public static ConfigEntry<bool> AllowBuildingInWilds; public static ConfigEntry<string> Faction1Name; public static ConfigEntry<string> Faction2Name; public static ConfigEntry<string> Faction3Name; public static ConfigEntry<bool> FreezeAdmins; public static ConfigEntry<string> FreezeBannerText; public static ConfigEntry<string> FreezeBannerColor; public static ConfigEntry<string> DiscordWebhookUrl; public static ConfigEntry<string> DiscordServerLabel; public static ConfigEntry<bool> DiscordPostFreezeToggle; public static ConfigEntry<string> ChestDetectedItems; public static ConfigEntry<string> ChestMatchMode; public static ConfigEntry<bool> ChestIgnoreNonFactionPlayers; public static ConfigEntry<bool> ChestWebhookOnDeposit; public static ConfigEntry<string> ChestWebhookMessageTemplate; public static ConfigEntry<string> ChestFreezeMode; public static ConfigEntry<string> ChestFreezeMessage; public static ConfigEntry<bool> ChestFreezeOnlyOnce; public static ConfigEntry<bool> ChestBroadcastInGame; public static ConfigEntry<string> ChestBroadcastTemplate; public static ConfigEntry<bool> ChestBroadcastCompletion; public static ConfigEntry<string> ChestBroadcastCompletionTemplate; public static ConfigEntry<float> ChestBroadcastCompletionDuration; public static ConfigEntry<bool> ChestConsumeOnDeposit; public static bool LocalCanBypass; public static int LocalFactionId; public static bool LocalIsFrozen; public static bool WorldIsFrozen; public static string LocalBannerSubtitle; public static float LocalDamageImmunityUntilTime; public static string GetFactionName(int factionId) { return factionId switch { 1 => Faction1Name.Value, 2 => Faction2Name.Value, 3 => Faction3Name.Value, _ => "Unknown", }; } } public enum PvpState { Peace, Skirmish, War } [Serializable] public class PlayerAssignment { public string name; public int factionId; } [Serializable] public class HouseholdData { private static readonly string DataFilePath = Path.Combine(Paths.ConfigPath, "ValheimHouseholds.json"); private static readonly object _lock = new object(); private static FileSystemWatcher _watcher; private static HouseholdData _instance; private static DateTime _lastReload = DateTime.MinValue; public PvpState pvpState; public List<PlayerAssignment> playerAssignments = new List<PlayerAssignment>(); public static void Load() { lock (_lock) { try { if (File.Exists(DataFilePath)) { _instance = DeserializeManual(File.ReadAllText(DataFilePath)) ?? new HouseholdData(); NormalizeAfterLoad(_instance); } else { _instance = new HouseholdData(); SaveLocked(); } } catch (Exception ex) { Plugin.Log.LogError((object)("Failed to load HouseholdData: " + ex.Message)); _instance = new HouseholdData(); } InitWatcher(); } } public static void Save() { lock (_lock) { SaveLocked(); } } private static void SaveLocked() { try { string contents = SerializeManual(_instance); File.WriteAllText(DataFilePath, contents); } catch (Exception ex) { Plugin.Log.LogError((object)("Failed to save HouseholdData: " + ex.Message)); } } private static string SerializeManual(HouseholdData d) { StringBuilder stringBuilder = new StringBuilder(256); stringBuilder.Append("{\n"); stringBuilder.Append(" \"pvpState\": ").Append((int)d.pvpState).Append(",\n"); stringBuilder.Append(" \"playerAssignments\": ["); for (int i = 0; i < d.playerAssignments.Count; i++) { PlayerAssignment playerAssignment = d.playerAssignments[i]; if (i > 0) { stringBuilder.Append(","); } stringBuilder.Append("\n { \"name\": ").Append(Quote(playerAssignment?.name)).Append(", \"factionId\": ") .Append(playerAssignment?.factionId ?? 0) .Append(" }"); } if (d.playerAssignments.Count > 0) { stringBuilder.Append("\n "); } stringBuilder.Append("]\n"); stringBuilder.Append("}\n"); return stringBuilder.ToString(); } private static HouseholdData DeserializeManual(string json) { HouseholdData householdData = new HouseholdData(); if (string.IsNullOrWhiteSpace(json)) { return householdData; } int startIndex = 0; int num = json.IndexOf("\"pvpState\"", startIndex, StringComparison.Ordinal); if (num >= 0) { int num2 = json.IndexOf(':', num); if (num2 > 0) { int i; for (i = num2 + 1; i < json.Length && (char.IsDigit(json[i]) || json[i] == '-' || char.IsWhiteSpace(json[i])); i++) { } if (int.TryParse(json.Substring(num2 + 1, i - num2 - 1).Trim(), NumberStyles.Integer, CultureInfo.InvariantCulture, out var result)) { householdData.pvpState = (PvpState)result; } } } foreach (string item in EnumerateObjects(ExtractArray(json, "playerAssignments"))) { string text = ExtractString(item, "name"); int factionId = ExtractInt(item, "factionId"); if (!string.IsNullOrEmpty(text)) { householdData.playerAssignments.Add(new PlayerAssignment { name = text, factionId = factionId }); } } return householdData; } private static string Quote(string s) { if (s == null) { return "\"\""; } StringBuilder stringBuilder = new StringBuilder(s.Length + 2); stringBuilder.Append('"'); foreach (char c in s) { switch (c) { case '\\': stringBuilder.Append("\\\\"); continue; case '"': stringBuilder.Append("\\\""); continue; case '\n': stringBuilder.Append("\\n"); continue; case '\r': stringBuilder.Append("\\r"); continue; case '\t': stringBuilder.Append("\\t"); continue; } if (c < ' ') { StringBuilder stringBuilder2 = stringBuilder.Append("\\u"); int num = c; stringBuilder2.Append(num.ToString("X4")); } else { stringBuilder.Append(c); } } stringBuilder.Append('"'); return stringBuilder.ToString(); } private static string ExtractArray(string json, string key) { int num = json.IndexOf("\"" + key + "\"", StringComparison.Ordinal); if (num < 0) { return ""; } int num2 = json.IndexOf(':', num); if (num2 < 0) { return ""; } int num3 = json.IndexOf('[', num2); if (num3 < 0) { return ""; } int num4 = 0; for (int i = num3; i < json.Length; i++) { switch (json[i]) { case '[': num4++; break; case ']': num4--; if (num4 == 0) { return json.Substring(num3 + 1, i - num3 - 1); } break; } } return ""; } private static IEnumerable<string> EnumerateObjects(string arrayBody) { if (string.IsNullOrEmpty(arrayBody)) { yield break; } int depth = 0; int num = -1; bool inString = false; bool escape = false; for (int i = 0; i < arrayBody.Length; i++) { char c = arrayBody[i]; if (escape) { escape = false; continue; } switch (c) { case '\\': escape = true; continue; case '"': inString = !inString; continue; } if (inString) { continue; } switch (c) { case '{': if (depth == 0) { num = i; } depth++; break; case '}': depth--; if (depth == 0 && num >= 0) { yield return arrayBody.Substring(num, i - num + 1); num = -1; } break; } } } private static string ExtractString(string obj, string key) { int num = obj.IndexOf("\"" + key + "\"", StringComparison.Ordinal); if (num < 0) { return null; } int num2 = obj.IndexOf(':', num); if (num2 < 0) { return null; } int num3 = obj.IndexOf('"', num2 + 1); if (num3 < 0) { return null; } StringBuilder stringBuilder = new StringBuilder(); for (int i = num3 + 1; i < obj.Length; i++) { char c = obj[i]; if (c == '\\' && i + 1 < obj.Length) { char c2 = obj[i + 1]; switch (c2) { case '\\': stringBuilder.Append('\\'); break; case '"': stringBuilder.Append('"'); break; case 'n': stringBuilder.Append('\n'); break; case 'r': stringBuilder.Append('\r'); break; case 't': stringBuilder.Append('\t'); break; default: stringBuilder.Append(c2); break; } i++; } else { if (c == '"') { return stringBuilder.ToString(); } stringBuilder.Append(c); } } return null; } private static int ExtractInt(string obj, string key) { int num = obj.IndexOf("\"" + key + "\"", StringComparison.Ordinal); if (num < 0) { return 0; } int num2 = obj.IndexOf(':', num); if (num2 < 0) { return 0; } int i; for (i = num2 + 1; i < obj.Length && char.IsWhiteSpace(obj[i]); i++) { } int num3 = i; if (i < obj.Length && (obj[i] == '-' || obj[i] == '+')) { i++; } for (; i < obj.Length && char.IsDigit(obj[i]); i++) { } if (i == num3) { return 0; } if (int.TryParse(obj.Substring(num3, i - num3), NumberStyles.Integer, CultureInfo.InvariantCulture, out var result)) { return result; } return 0; } public static HouseholdData Get() { lock (_lock) { return _instance; } } private static void NormalizeAfterLoad(HouseholdData data) { if (data.playerAssignments == null) { data.playerAssignments = new List<PlayerAssignment>(); } data.playerAssignments.RemoveAll((PlayerAssignment e) => e == null || string.IsNullOrEmpty(e.name)); } public static int GetFactionId(string playerName) { if (string.IsNullOrEmpty(playerName)) { return 0; } lock (_lock) { return FindAssignmentLocked(playerName)?.factionId ?? 0; } } public static (bool ok, string storedName) SetFaction(string playerName, int factionId, string canonicalCaseHint = null) { if (string.IsNullOrWhiteSpace(playerName)) { return (ok: false, storedName: null); } if (factionId < 1 || factionId > 3) { return (ok: false, storedName: null); } lock (_lock) { string text = ((!string.IsNullOrEmpty(canonicalCaseHint)) ? canonicalCaseHint : playerName.Trim()); PlayerAssignment playerAssignment = FindAssignmentLocked(playerName); if (playerAssignment == null) { _instance.playerAssignments.Add(new PlayerAssignment { name = text, factionId = factionId }); } else { playerAssignment.factionId = factionId; if (!string.IsNullOrEmpty(canonicalCaseHint)) { playerAssignment.name = canonicalCaseHint; } text = playerAssignment.name; } SaveLocked(); return (ok: true, storedName: text); } } public static bool RemoveFaction(string playerName) { if (string.IsNullOrEmpty(playerName)) { return false; } lock (_lock) { int num = _instance.playerAssignments.RemoveAll((PlayerAssignment e) => e != null && string.Equals(e.name, playerName, StringComparison.OrdinalIgnoreCase)); if (num > 0) { SaveLocked(); } return num > 0; } } public static List<PlayerAssignment> ListAssignments() { lock (_lock) { return (from e in _instance.playerAssignments.OrderBy((PlayerAssignment e) => e.factionId).ThenBy<PlayerAssignment, string>((PlayerAssignment e) => e.name, StringComparer.OrdinalIgnoreCase) select new PlayerAssignment { name = e.name, factionId = e.factionId }).ToList(); } } private static PlayerAssignment FindAssignmentLocked(string playerName) { for (int i = 0; i < _instance.playerAssignments.Count; i++) { PlayerAssignment playerAssignment = _instance.playerAssignments[i]; if (playerAssignment != null && string.Equals(playerAssignment.name, playerName, StringComparison.OrdinalIgnoreCase)) { return playerAssignment; } } return null; } private static void InitWatcher() { try { if (_watcher == null) { string? directoryName = Path.GetDirectoryName(DataFilePath); string fileName = Path.GetFileName(DataFilePath); _watcher = new FileSystemWatcher(directoryName, fileName) { NotifyFilter = (NotifyFilters.Size | NotifyFilters.LastWrite), EnableRaisingEvents = true }; _watcher.Changed += OnFileChanged; _watcher.Created += OnFileChanged; } } catch (Exception ex) { Plugin.Log.LogWarning((object)("FileSystemWatcher failed to start: " + ex.Message)); } } private static void OnFileChanged(object sender, FileSystemEventArgs e) { DateTime utcNow = DateTime.UtcNow; if ((utcNow - _lastReload).TotalMilliseconds < 500.0) { return; } _lastReload = utcNow; lock (_lock) { try { HouseholdData obj = DeserializeManual(File.ReadAllText(DataFilePath)) ?? new HouseholdData(); NormalizeAfterLoad(obj); _instance = obj; Plugin.Log.LogInfo((object)"HouseholdData hot-reloaded from file"); Patches.BroadcastFactionAssignmentsToAll(); } catch (Exception ex) { Plugin.Log.LogWarning((object)("HouseholdData hot-reload failed: " + ex.Message)); } } } } internal static class FactionArmorSets { private sealed class BundleItem { internal string AssetName; internal string PrefabName; internal string DisplayName; internal string Slot; internal int[] Cost; } private sealed class VanillaArmorPiece { internal string SourcePrefab; internal string IdSuffix; internal string DisplayName; internal string Slot; internal bool TrollSet; } private sealed class HouseholdColor { internal string Name; internal Color32 Tint; } private sealed class ArmorBonusTarget { internal ItemDrop Drop; internal float BaseArmor; internal bool TrollSet; } private static readonly string[] AshlandsMaterials = new string[5] { "FlametalNew", "AskHide", "CharredBone", "Eitr", "YmirRemains" }; private static readonly string[] FactionColorTextureSlots = new string[6] { "_MainTex", "_BaseMap", "_ChestTex", "_LegsTex", "_HelmetTex", "_ArmorTex" }; private static readonly HouseholdColor[] HouseholdColors = new HouseholdColor[3] { new HouseholdColor { Name = "Ironforge", Tint = new Color32((byte)178, (byte)52, (byte)47, byte.MaxValue) }, new HouseholdColor { Name = "Stormwind", Tint = new Color32((byte)60, (byte)112, (byte)180, byte.MaxValue) }, new HouseholdColor { Name = "Frosthelm", Tint = new Color32((byte)218, (byte)226, (byte)238, byte.MaxValue) } }; private static readonly VanillaArmorPiece[] TrollPieces = new VanillaArmorPiece[4] { new VanillaArmorPiece { SourcePrefab = "HelmetTrollLeather", IdSuffix = "TrollHood", DisplayName = "Troll Hide Hood", Slot = "Helmet", TrollSet = true }, new VanillaArmorPiece { SourcePrefab = "ArmorTrollLeatherChest", IdSuffix = "TrollChest", DisplayName = "Troll Hide Tunic", Slot = "Chest", TrollSet = true }, new VanillaArmorPiece { SourcePrefab = "ArmorTrollLeatherLegs", IdSuffix = "TrollLegs", DisplayName = "Troll Hide Pants", Slot = "Legs", TrollSet = true }, new VanillaArmorPiece { SourcePrefab = "CapeTrollHide", IdSuffix = "TrollCape", DisplayName = "Troll Hide Cape", Slot = "Cape", TrollSet = true } }; private static readonly VanillaArmorPiece[] BronzePieces = new VanillaArmorPiece[3] { new VanillaArmorPiece { SourcePrefab = "HelmetBronze", IdSuffix = "BronzeHelmet", DisplayName = "Bronze Helmet", Slot = "Helmet" }, new VanillaArmorPiece { SourcePrefab = "ArmorBronzeChest", IdSuffix = "BronzeChest", DisplayName = "Bronze Cuirass", Slot = "Chest" }, new VanillaArmorPiece { SourcePrefab = "ArmorBronzeLegs", IdSuffix = "BronzeLegs", DisplayName = "Bronze Leggings", Slot = "Legs" } }; private static readonly BundleItem[] BundleItems = new BundleItem[8] { new BundleItem { AssetName = "HelmetHounds", PrefabName = "IronforgeHelmet", DisplayName = "Ironforge Helmet", Slot = "Helmet", Cost = new int[5] { 16, 3, 2, 4, 0 } }, new BundleItem { AssetName = "HelmetHoundsKing", PrefabName = "IronforgeKingHelmet", DisplayName = "Ironforge King Helmet", Slot = "Helmet", Cost = new int[5] { 16, 3, 5, 4, 0 } }, new BundleItem { AssetName = "ArmorHoundsChest", PrefabName = "IronforgeChest", DisplayName = "Ironforge Chest", Slot = "Chest", Cost = new int[5] { 20, 3, 5, 0, 10 } }, new BundleItem { AssetName = "ArmorHoundsLegs", PrefabName = "IronforgeLegs", DisplayName = "Ironforge Legs", Slot = "Legs", Cost = new int[5] { 20, 3, 5, 0, 0 } }, new BundleItem { AssetName = "ArmorFreyrChest", PrefabName = "StormwindChest", DisplayName = "Stormwind Chest", Slot = "Chest", Cost = new int[5] { 16, 3, 5, 0, 10 } }, new BundleItem { AssetName = "ArmorFreyrLegs", PrefabName = "StormwindLegs", DisplayName = "Stormwind Legs", Slot = "Legs", Cost = new int[5] { 16, 30, 5, 0, 10 } }, new BundleItem { AssetName = "ArmorFenrirChest", PrefabName = "FrosthelmChest", DisplayName = "Frosthelm Chest", Slot = "Chest", Cost = new int[5] { 16, 3, 5, 0, 10 } }, new BundleItem { AssetName = "ArmorFenrirLegs", PrefabName = "FrosthelmLegs", DisplayName = "Frosthelm Legs", Slot = "Legs", Cost = new int[5] { 16, 30, 5, 0, 10 } } }; private static readonly List<Material> OwnedMaterials = new List<Material>(); private static readonly List<Texture2D> OwnedTextures = new List<Texture2D>(); private static readonly List<Sprite> OwnedSprites = new List<Sprite>(); private static readonly Dictionary<string, Texture2D> RecoloredTextures = new Dictionary<string, Texture2D>(StringComparer.Ordinal); private static readonly List<ArmorBonusTarget> ArmorBonusTargets = new List<ArmorBonusTarget>(); private static AssetBundle _bundle; private static MethodInfo _loadImage; private static bool _registered; internal static void Register() { //IL_0255: 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_026c: 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_0298: Unknown result type (might be due to invalid IL or missing references) //IL_02a3: Unknown result type (might be due to invalid IL or missing references) //IL_02ac: Expected O, but got Unknown //IL_0304: Unknown result type (might be due to invalid IL or missing references) //IL_030e: Expected O, but got Unknown if (_registered) { return; } _registered = true; PrefabManager.OnVanillaPrefabsAvailable -= Register; _bundle = LoadBundle(); if ((Object)(object)_bundle == (Object)null) { Plugin.Log.LogError((object)"Faction armor asset bundle could not be loaded."); return; } Dictionary<string, GameObject> dictionary = (from prefab in _bundle.LoadAllAssets<GameObject>() where (Object)(object)prefab != (Object)null select prefab).ToDictionary<GameObject, string>((GameObject prefab) => ((Object)prefab).name, StringComparer.OrdinalIgnoreCase); int num = 0; BundleItem[] bundleItems = BundleItems; ItemDrop drop = default(ItemDrop); foreach (BundleItem item in bundleItems) { if (!dictionary.TryGetValue(item.AssetName, out var value)) { Plugin.Log.LogError((object)("Faction armor bundle is missing '" + item.AssetName + "'.")); continue; } GameObject val = value; ((Object)val).name = item.PrefabName; if (!val.TryGetComponent<ItemDrop>(ref drop)) { Plugin.Log.LogError((object)("Faction armor '" + item.AssetName + "' has no ItemDrop component.")); Object.Destroy((Object)(object)val); continue; } ApplyStats(drop, item.Slot); FactionEquipmentConfig.RegisterStatRefresher(delegate { ApplyStats(drop, item.Slot); }); if (item.AssetName == "ArmorHoundsChest") { ApplyBodyMaterial(drop, "ArmorMageChest_Ashlands", "_ChestTex", coal: false); } else if (item.AssetName == "ArmorHoundsLegs") { ApplyBodyMaterial(drop, "ArmorMageLegs_Ashlands", "_LegsTex", coal: false); } else if (item.AssetName == "ArmorFenrirChest") { ApplyBodyMaterial(drop, "ArmorMageChest_Ashlands", "_ChestTex", coal: true); } else if (item.AssetName == "ArmorFenrirLegs") { ApplyBodyMaterial(drop, "ArmorMageLegs_Ashlands", "_LegsTex", coal: true); } ItemConfig val2 = new ItemConfig { Name = item.DisplayName, Description = "Faction armor: " + item.DisplayName + ".", CraftingStation = "forge", RepairStation = "forge", MinStationLevel = 2 }; for (int num3 = 0; num3 < item.Cost.Length; num3++) { if (item.Cost[num3] > 0) { val2.AddRequirement(AshlandsMaterials[num3], item.Cost[num3], 0); } } ItemManager.Instance.AddItem(new CustomItem(val, true, val2)); num++; } num += RegisterFactionMageHelmets(); num += RegisterFactionCapes(); num += RegisterHouseholdTierArmor(); Plugin.Log.LogInfo((object)$"FactionArmorSets: registered {num} Ironforge, Stormwind, and Frosthelm armor pieces."); } private static AssetBundle LoadBundle() { Assembly assembly = typeof(FactionArmorSets).Assembly; string text = assembly.GetManifestResourceNames().FirstOrDefault((string name) => name.EndsWith(".Assets.FactionEquipment.factionarmors", StringComparison.OrdinalIgnoreCase)); if (text == null) { return null; } using Stream stream = assembly.GetManifestResourceStream(text); return (stream == null) ? null : AssetBundle.LoadFromStream(stream); } private static int RegisterFactionMageHelmets() { GameObject prefab = Cache.GetPrefab<GameObject>("HelmetMage_Ashlands"); ItemDrop sourceDrop = default(ItemDrop); if ((Object)(object)prefab == (Object)null || !prefab.TryGetComponent<ItemDrop>(ref sourceDrop)) { Plugin.Log.LogError((object)"Could not find HelmetMage_Ashlands; Stormwind and Frosthelm helmets were skipped."); return 0; } return 0 + RegisterMageHelmet(prefab, sourceDrop, "Freyr", "Stormwind") + RegisterMageHelmet(prefab, sourceDrop, "Fenrir", "Frosthelm"); } private static int RegisterMageHelmet(GameObject source, ItemDrop sourceDrop, string assetFaction, string faction) { //IL_00c4: Unknown result type (might be due to invalid IL or missing references) //IL_00ce: Expected O, but got Unknown Texture2D val = LoadTexture("AshMage", assetFaction + "Tex.png"); Sprite val2 = LoadSprite("AshMage", assetFaction + "Helmet_Icon.png"); if ((Object)(object)val == (Object)null || (Object)(object)val2 == (Object)null) { return 0; } string text = faction + "Helmet"; GameObject val3 = Object.Instantiate<GameObject>(source, Plugin.PrefabRoot.transform, false); ((Object)val3).name = text; SetArmorTexture(val3, (Texture)(object)val); ApplyStats(val3.GetComponent<ItemDrop>(), "Helmet", clearDamageModifiers: false); ItemDrop cloneDrop = val3.GetComponent<ItemDrop>(); FactionEquipmentConfig.RegisterStatRefresher(delegate { ApplyStats(cloneDrop, "Helmet", clearDamageModifiers: false); }); ItemConfig val4 = CreateClonedConfig(sourceDrop, text, faction + " Helmet", val2); ItemManager.Instance.AddItem(new CustomItem(val3, true, val4)); return 1; } private static int RegisterFactionCapes() { GameObject prefab = Cache.GetPrefab<GameObject>("CapeLox"); ItemDrop sourceDrop = default(ItemDrop); if ((Object)(object)prefab == (Object)null || !prefab.TryGetComponent<ItemDrop>(ref sourceDrop)) { Plugin.Log.LogError((object)"Could not find vanilla CapeLox; faction capes were skipped."); return 0; } return 0 + RegisterCape(prefab, sourceDrop, "Hounds", "Ironforge") + RegisterCape(prefab, sourceDrop, "Freyr", "Stormwind") + RegisterCape(prefab, sourceDrop, "Fenrir", "Frosthelm"); } private static int RegisterCape(GameObject source, ItemDrop sourceDrop, string assetFaction, string faction) { //IL_00d3: Unknown result type (might be due to invalid IL or missing references) //IL_00dd: Expected O, but got Unknown string text = "CapeLox" + assetFaction; Texture2D val = LoadTexture("CapeLox", text + ".png"); Sprite val2 = LoadSprite("CapeLox", text + "_Icon.png"); if ((Object)(object)val == (Object)null || (Object)(object)val2 == (Object)null) { return 0; } string text2 = faction + "Cape"; GameObject val3 = Object.Instantiate<GameObject>(source, Plugin.PrefabRoot.transform, false); ((Object)val3).name = text2; SetCapeTexture(val3, (Texture)(object)val); ApplyStats(val3.GetComponent<ItemDrop>(), "Cape", clearDamageModifiers: false); ItemDrop cloneDrop = val3.GetComponent<ItemDrop>(); FactionEquipmentConfig.RegisterStatRefresher(delegate { ApplyStats(cloneDrop, "Cape", clearDamageModifiers: false); }); ItemConfig val4 = CreateClonedConfig(sourceDrop, text2, faction + " Cape", val2); ItemManager.Instance.AddItem(new CustomItem(val3, true, val4)); return 1; } private static ItemConfig CreateClonedConfig(ItemDrop sourceDrop, string id, string displayName, Sprite icon) { //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_000c: Unknown result type (might be due to invalid IL or missing references) //IL_0022: Unknown result type (might be due to invalid IL or missing references) //IL_002a: Expected O, but got Unknown ItemConfig val = new ItemConfig { Name = displayName, Description = "Faction gear: " + displayName + ".", Icon = icon }; ObjectDB menuObjectDatabase = GetMenuObjectDatabase(); Recipe val2 = (((Object)(object)menuObjectDatabase == (Object)null) ? null : menuObjectDatabase.GetRecipe(sourceDrop.m_itemData)); if ((Object)(object)val2 == (Object)null) { val.Enabled = false; Plugin.Log.LogWarning((object)("No vanilla crafting recipe found for '" + id + "'; it will be spawn-only until a recipe is available.")); return val; } val.CraftingStation = (((Object)(object)val2.m_craftingStation == (Object)null) ? null : ((Object)val2.m_craftingStation).name); val.RepairStation = val.CraftingStation; val.MinStationLevel = val2.m_minStationLevel; val.Requirements = (val2.m_resources ?? Array.Empty<Requirement>()).Where((Requirement requirement) => requirement != null && (Object)(object)requirement.m_resItem != (Object)null).Select((Func<Requirement, RequirementConfig>)((Requirement requirement) => new RequirementConfig(((Object)requirement.m_resItem).name, requirement.m_amount, requirement.m_amountPerLevel, requirement.m_recover))).ToArray(); return val; } private static int RegisterHouseholdTierArmor() { int num = 0; HouseholdColor[] householdColors = HouseholdColors; foreach (HouseholdColor household in householdColors) { num += RegisterHouseholdArmorSet(household, TrollPieces, trollSet: true); num += RegisterHouseholdArmorSet(household, BronzePieces, trollSet: false); } return num; } private static int RegisterHouseholdArmorSet(HouseholdColor household, VanillaArmorPiece[] pieces, bool trollSet) { //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_015b: Unknown result type (might be due to invalid IL or missing references) //IL_0160: Unknown result type (might be due to invalid IL or missing references) //IL_0210: Unknown result type (might be due to invalid IL or missing references) //IL_021a: Expected O, but got Unknown int num = 0; ItemDrop sourceDrop = default(ItemDrop); ItemDrop val2 = default(ItemDrop); foreach (VanillaArmorPiece vanillaArmorPiece in pieces) { GameObject prefab = Cache.GetPrefab<GameObject>(vanillaArmorPiece.SourcePrefab); if ((Object)(object)prefab == (Object)null || !prefab.TryGetComponent<ItemDrop>(ref sourceDrop)) { Plugin.Log.LogError((object)("Could not find vanilla armor prefab '" + vanillaArmorPiece.SourcePrefab + "'; skipped " + household.Name + " " + vanillaArmorPiece.DisplayName + ".")); continue; } string text = household.Name + vanillaArmorPiece.IdSuffix; string text2 = household.Name + " " + vanillaArmorPiece.DisplayName; GameObject val = Object.Instantiate<GameObject>(prefab, Plugin.PrefabRoot.transform, false); ((Object)val).name = text; if (!val.TryGetComponent<ItemDrop>(ref val2)) { Plugin.Log.LogError((object)("Cloned armor prefab '" + text + "' has no ItemDrop component.")); Object.Destroy((Object)(object)val); continue; } float armor = val2.m_itemData.m_shared.m_armor; if (trollSet) { val2.m_itemData.m_shared.m_setName = household.Name + "TrollArmorSet"; } Sprite icon = CreateFactionIcon(sourceDrop, val2, Color32.op_Implicit(household.Tint), text); int num2 = ApplyFactionRecolor(val, val2, Color32.op_Implicit(household.Tint), text); if (num2 == 0) { Plugin.Log.LogWarning((object)("Faction armor '" + text + "' had no supported diffuse texture slots to recolor.")); } else { Plugin.Log.LogInfo((object)$"Faction armor '{text}' recolored {num2} diffuse texture slot(s)."); } ApplyArmorBonus(val2, armor, trollSet); ItemConfig val3 = CreateClonedConfig(sourceDrop, text, text2, icon); if (trollSet) { val3.Description = text2 + ", bearing the " + household.Name + " colors. A strengthened Troll hide set piece."; } else { val3.Description = text2 + ", forged in " + household.Name + " colors. A strengthened Bronze set piece."; } ItemManager.Instance.AddItem(new CustomItem(val, true, val3)); ArmorBonusTargets.Add(new ArmorBonusTarget { Drop = val2, BaseArmor = armor, TrollSet = trollSet }); ArmorBonusTarget bonusTarget = ArmorBonusTargets[ArmorBonusTargets.Count - 1]; FactionEquipmentConfig.RegisterStatRefresher(delegate { ApplyArmorBonus(bonusTarget.Drop, bonusTarget.BaseArmor, bonusTarget.TrollSet); }); num++; } return num; } private static void ApplyArmorBonus(ItemDrop drop, float baseArmor, bool trollSet) { if (!((Object)(object)drop == (Object)null)) { float num = (trollSet ? FactionEquipmentConfig.TrollArmorBonus.Value : FactionEquipmentConfig.BronzeArmorBonus.Value); drop.m_itemData.m_shared.m_armor = baseArmor + num; } } private static int ApplyFactionRecolor(GameObject prefab, ItemDrop drop, Color tint, string itemId) { //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_00cc: Expected O, but got Unknown //IL_00cf: Unknown result type (might be due to invalid IL or missing references) //IL_0033: Unknown result type (might be due to invalid IL or missing references) //IL_0038: Unknown result type (might be due to invalid IL or missing references) //IL_0052: Expected O, but got Unknown //IL_0055: Unknown result type (might be due to invalid IL or missing references) int num = 0; Renderer[] componentsInChildren = prefab.GetComponentsInChildren<Renderer>(true); foreach (Renderer val in componentsInChildren) { Material[] sharedMaterials = val.sharedMaterials; for (int j = 0; j < sharedMaterials.Length; j++) { if (!((Object)(object)sharedMaterials[j] == (Object)null)) { Material val2 = new Material(sharedMaterials[j]) { name = itemId + "_Material_" + j }; num += RecolorMaterial(val2, tint); sharedMaterials[j] = val2; OwnedMaterials.Add(val2); } } val.materials = sharedMaterials; } SharedData shared = drop.m_itemData.m_shared; if ((Object)(object)shared.m_armorMaterial != (Object)null) { Material val3 = new Material(shared.m_armorMaterial) { name = itemId + "_EquippedMaterial" }; num += RecolorMaterial(val3, tint); shared.m_armorMaterial = val3; OwnedMaterials.Add(val3); } return num; } private static int RecolorMaterial(Material material, Color tint) { //IL_0033: 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_0090: Unknown result type (might be due to invalid IL or missing references) int num = 0; string[] factionColorTextureSlots = FactionColorTextureSlots; foreach (string text in factionColorTextureSlots) { if (!material.HasProperty(text)) { continue; } Texture texture = material.GetTexture(text); Texture2D val = (Texture2D)(object)((texture is Texture2D) ? texture : null); if (!((Object)(object)val == (Object)null)) { Texture2D recoloredTexture = GetRecoloredTexture(val, tint); if (!((Object)(object)recoloredTexture == (Object)null)) { material.SetTexture(text, (Texture)(object)recoloredTexture); num++; } } } if (num > 0) { if (material.HasProperty("_Color")) { material.SetColor("_Color", Color.white); } if (material.HasProperty("_BaseColor")) { material.SetColor("_BaseColor", Color.white); } } return num; } private static Texture2D GetRecoloredTexture(Texture2D source, Color tint) { //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_0103: Unknown result type (might be due to invalid IL or missing references) //IL_010a: Expected O, but got Unknown //IL_011c: 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_014a: Unknown result type (might be due to invalid IL or missing references) //IL_0157: Unknown result type (might be due to invalid IL or missing references) //IL_0165: 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_0192: 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_01a4: Unknown result type (might be due to invalid IL or missing references) //IL_01ab: 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) Color32 val = Color32.op_Implicit(tint); string text = ((Object)source).GetInstanceID() + "_" + val.r + "_" + val.g + "_" + val.b; if (RecoloredTextures.TryGetValue(text, out var value)) { return value; } int num = Math.Max(((Texture)source).width, ((Texture)source).height); float num2 = ((num > 1024) ? (1024f / (float)num) : 1f); int num3 = Math.Max(1, Mathf.RoundToInt((float)((Texture)source).width * num2)); int num4 = Math.Max(1, Mathf.RoundToInt((float)((Texture)source).height * num2)); RenderTexture active = RenderTexture.active; RenderTexture val2 = null; Texture2D val3 = null; try { val2 = RenderTexture.GetTemporary(num3, num4, 0, (RenderTextureFormat)0); Graphics.Blit((Texture)(object)source, val2); RenderTexture.active = val2; val3 = new Texture2D(num3, num4, (TextureFormat)4, false); val3.ReadPixels(new Rect(0f, 0f, (float)num3, (float)num4), 0, 0); val3.Apply(false, false); Color[] pixels = val3.GetPixels(); for (int i = 0; i < pixels.Length; i++) { Color val4 = pixels[i]; float num5 = val4.r * 0.2126f + val4.g * 0.7152f + val4.b * 0.0722f; float num6 = 0.22f + num5 * 0.98f; pixels[i] = new Color(tint.r * num6, tint.g * num6, tint.b * num6, val4.a); } val3.SetPixels(pixels); val3.Apply(false, false); ((Object)val3).name = "HouseholdFactionTint_" + text; RecoloredTextures.Add(text, val3); OwnedTextures.Add(val3); return val3; } catch (Exception ex) { if ((Object)(object)val3 != (Object)null) { Object.Destroy((Object)(object)val3); } Plugin.Log.LogWarning((object)("Could not bake faction tint for texture '" + ((Object)source).name + "': " + ex.Message)); return null; } finally { RenderTexture.active = active; if ((Object)(object)val2 != (Object)null) { RenderTexture.ReleaseTemporary(val2); } } } private static Sprite CreateFactionIcon(ItemDrop sourceDrop, ItemDrop targetDrop, Color tint, string itemId) { //IL_005f: Unknown result type (might be due to invalid IL or missing references) //IL_0064: Unknown result type (might be due to invalid IL or missing references) //IL_0122: Unknown result type (might be due to invalid IL or missing references) //IL_0128: Expected O, but got Unknown //IL_0135: 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_0166: 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_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_0197: Unknown result type (might be due to invalid IL or missing references) //IL_01bb: Unknown result type (might be due to invalid IL or missing references) //IL_01c4: Unknown result type (might be due to invalid IL or missing references) //IL_01cd: Unknown result type (might be due to invalid IL or missing references) //IL_01d6: 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_01e2: Unknown result type (might be due to invalid IL or missing references) //IL_0258: 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) Sprite val = ((sourceDrop.m_itemData.m_shared.m_icons == null || sourceDrop.m_itemData.m_shared.m_icons.Length == 0) ? null : sourceDrop.m_itemData.m_shared.m_icons[0]); if ((Object)(object)val == (Object)null || (Object)(object)val.texture == (Object)null) { return null; } RenderTexture active = RenderTexture.active; RenderTexture val2 = null; Texture2D val3 = null; try { Rect textureRect = val.textureRect; int num = Mathf.Clamp(Mathf.RoundToInt(((Rect)(ref textureRect)).x), 0, ((Texture)val.texture).width - 1); int num2 = Mathf.Clamp(Mathf.RoundToInt(((Rect)(ref textureRect)).y), 0, ((Texture)val.texture).height - 1); int num3 = Mathf.Clamp(Mathf.RoundToInt(((Rect)(ref textureRect)).width), 1, ((Texture)val.texture).width - num); int num4 = Mathf.Clamp(Mathf.RoundToInt(((Rect)(ref textureRect)).height), 1, ((Texture)val.texture).height - num2); val2 = RenderTexture.GetTemporary(((Texture)val.texture).width, ((Texture)val.texture).height, 0, (RenderTextureFormat)0); Graphics.Blit((Texture)(object)val.texture, val2); RenderTexture.active = val2; val3 = new Texture2D(num3, num4, (TextureFormat)4, false); val3.ReadPixels(new Rect((float)num, (float)num2, (float)num3, (float)num4), 0, 0, false); val3.Apply(false, false); Color[] pixels = val3.GetPixels(); int num5 = 0; for (int i = 0; i < pixels.Length; i++) { Color val4 = pixels[i]; if (val4.a > 0.01f) { num5++; } float num6 = val4.r * 0.2126f + val4.g * 0.7152f + val4.b * 0.0722f; float num7 = 0.22f + num6 * 0.98f; pixels[i] = new Color(tint.r * num7, tint.g * num7, tint.b * num7, val4.a); } if (num5 == 0) { Object.Destroy((Object)(object)val3); val3 = null; Plugin.Log.LogWarning((object)("Faction icon crop for '" + itemId + "' was transparent; keeping the vanilla icon.")); return val; } val3.SetPixels(pixels); val3.Apply(false, false); ((Object)val3).name = itemId + "_FactionIconTexture"; Sprite val5 = Sprite.Create(val3, new Rect(0f, 0f, (float)num3, (float)num4), new Vector2(0.5f, 0.5f), val.pixelsPerUnit); ((Object)val5).name = itemId + "_FactionIcon"; OwnedTextures.Add(val3); OwnedSprites.Add(val5); targetDrop.m_itemData.m_shared.m_icons = (Sprite[])(object)new Sprite[1] { val5 }; return val5; } catch (Exception ex) { if ((Object)(object)val3 != (Object)null) { Object.Destroy((Object)(object)val3); } Plugin.Log.LogWarning((object)("Could not create faction icon for '" + itemId + "'; keeping the vanilla icon: " + ex.Message)); return val; } finally { RenderTexture.active = active; if ((Object)(object)val2 != (Object)null) { RenderTexture.ReleaseTemporary(val2); } } } private static ObjectDB GetMenuObjectDatabase() { object obj = typeof(PrefabManager).GetProperty("Instance", BindingFlags.Static | BindingFlags.Public)?.GetValue(null, null); PropertyInfo property = typeof(PrefabManager).GetProperty("MenuObjectDB", BindingFlags.Instance | BindingFlags.NonPublic); if (obj != null) { object? obj2 = property?.GetValue(obj, null); return (ObjectDB)((obj2 is ObjectDB) ? obj2 : null); } return null; } private static void ApplyStats(ItemDrop drop, string slot, bool clearDamageModifiers = true) { if (!((Object)(object)drop == (Object)null)) { SharedData shared = drop.m_itemData.m_shared; if (clearDamageModifiers) { shared.m_damageModifiers = new List<DamageModPair>(); } switch (slot) { case "Helmet": shared.m_armor = FactionEquipmentConfig.HelmetArmor.Value; shared.m_weight = FactionEquipmentConfig.HelmetWeight.Value; shared.m_maxDurability = FactionEquipmentConfig.HelmetDurability.Value; break; case "Chest": shared.m_armor = FactionEquipmentConfig.ChestArmor.Value; shared.m_weight = FactionEquipmentConfig.ChestWeight.Value; shared.m_maxDurability = FactionEquipmentConfig.ChestDurability.Value; shared.m_movementModifier = FactionEquipmentConfig.ChestMovementModifier.Value; shared.m_heatResistanceModifier = FactionEquipmentConfig.ChestHeatResistance.Value; break; case "Legs": shared.m_armor = FactionEquipmentConfig.LegsArmor.Value; shared.m_weight = FactionEquipmentConfig.LegsWeight.Value; shared.m_maxDurability = FactionEquipmentConfig.LegsDurability.Value; shared.m_movementModifier = FactionEquipmentConfig.LegsMovementModifier.Value; shared.m_heatResistanceModifier = FactionEquipmentConfig.LegsHeatResistance.Value; break; case "Cape": shared.m_armor = FactionEquipmentConfig.CapeArmor.Value; shared.m_weight = FactionEquipmentConfig.CapeWeight.Value; shared.m_maxDurability = FactionEquipmentConfig.CapeDurability.Value; break; } } } private static void ApplyBodyMaterial(ItemDrop target, string sourceName, string textureSlot, bool coal) { //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_00a7: Expected O, but got Unknown GameObject prefab = Cache.GetPrefab<GameObject>(sourceName); ItemDrop val = default(ItemDrop); if ((Object)(object)prefab == (Object)null || !prefab.TryGetComponent<ItemDrop>(ref val)) { Plugin.Log.LogError((object)("Could not find vanilla body material source '" + sourceName + "' for '" + ((Object)target).name + "'.")); return; } Material armorMaterial = val.m_itemData.m_shared.m_armorMaterial; if ((Object)(object)armorMaterial == (Object)null) { Plugin.Log.LogError((object)("Vanilla body material source '" + sourceName + "' has no armor material.")); return; } Material val2 = new Material(armorMaterial) { name = ((Object)target).name + "_FactionBodyMaterial" }; if (coal) { Texture coalTexture = GetCoalTexture(); if ((Object)(object)coalTexture != (Object)null && val2.HasProperty(textureSlot)) { val2.SetTexture(textureSlot, coalTexture); } } else if (!val2.HasProperty(textureSlot) || (Object)(object)val2.GetTexture(textureSlot) == (Object)null) { Plugin.Log.LogError((object)("Body material '" + sourceName + "' is missing texture slot '" + textureSlot + "'.")); } target.m_itemData.m_shared.m_armorMaterial = val2; OwnedMaterials.Add(val2); } private static Texture GetCoalTexture() { GameObject prefab = Cache.GetPrefab<GameObject>("Coal"); if ((Object)(object)prefab == (Object)null) { return null; } Renderer[] componentsInChildren = prefab.GetComponentsInChildren<Renderer>(true); for (int i = 0; i < componentsInChildren.Length; i++) { Material[] sharedMaterials = componentsInChildren[i].sharedMaterials; foreach (Material val in sharedMaterials) { if ((Object)(object)val != (Object)null && val.HasProperty("_MainTex")) { return val.GetTexture("_MainTex"); } } } return null; } private static void SetArmorTexture(GameObject prefab, Texture texture) { SetRendererTexture(prefab, texture, "Custom/Creature"); } private static void SetCapeTexture(GameObject prefab, Texture texture) { SetRendererTexture(prefab, texture, null); } private static void SetRendererTexture(GameObject prefab, Texture texture, string shaderFilter) { Renderer[] componentsInChildren = prefab.GetComponentsInChildren<Renderer>(true); foreach (Renderer val in componentsInChildren) { Material[] materials = val.materials; foreach (Material val2 in materials) { if (!((Object)(object)val2 == (Object)null) && (shaderFilter == null || !(((Object)val2.shader).name != shaderFilter)) && val2.HasProperty("_MainTex")) { val2.SetTexture("_MainTex", texture); } } val.materials = materials; } } private static Texture2D LoadTexture(string folder, string file) { //IL_0065: Unknown result type (might be due to invalid IL or missing references) //IL_006c: Expected O, but got Unknown string resourceSuffix = ".Assets.FactionEquipment." + folder + "." + file; Assembly assembly = typeof(FactionArmorSets).Assembly; string text = assembly.GetManifestResourceNames().FirstOrDefault((string name) => name.EndsWith(resourceSuffix, StringComparison.OrdinalIgnoreCase)); if (text == null) { return null; } using Stream stream = assembly.GetManifestResourceStream(text); using MemoryStream memoryStream = new MemoryStream(); stream.CopyTo(memoryStream); Texture2D val = new Texture2D(2, 2, (TextureFormat)4, false); MethodInfo methodInfo = FindLoadImage(); object obj = ((methodInfo == null) ? null : ((methodInfo.GetParameters().Length == 1) ? methodInfo.Invoke(val, new object[1] { memoryStream.ToArray() }) : methodInfo.Invoke(null, new object[2] { val, memoryStream.ToArray() }))); if (!(obj is bool) || !(bool)obj) { return null; } ((Object)val).name = Path.GetFileNameWithoutExtension(file); OwnedTextures.Add(val); return val; } private static Sprite LoadSprite(string folder, string file) { //IL_002c: 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) Texture2D val = LoadTexture(folder, file); if ((Object)(object)val == (Object)null) { return null; } Sprite val2 = Sprite.Create(val, new Rect(0f, 0f, (float)((Texture)val).width, (float)((Texture)val).height), new Vector2(0.5f, 0.5f)); ((Object)val2).name = Path.GetFileNameWithoutExtension(file); OwnedSprites.Add(val2); return val2; } private static MethodInfo FindLoadImage() { if (_loadImage != null) { return _loadImage; } _loadImage = typeof(Texture2D).GetMethod("LoadImage", new Type[1] { typeof(byte[]) }); if (_loadImage != null) { return _loadImage; } _loadImage = Type.GetType("UnityEngine.ImageConversion, UnityEngine.ImageConversionModule")?.GetMethod("LoadImage", new Type[2] { typeof(Texture2D), typeof(byte[]) }); return _loadImage; } } [HarmonyPatch] public static class FactionCommands { [Serializable] [CompilerGenerated] private sealed class <>c { public static readonly <>c <>9 = new <>c(); public static ConsoleEvent <>9__4_0; public static ConsoleEvent <>9__4_1; public static ConsoleEvent <>9__4_2; public static ConsoleEvent <>9__4_3; public static ConsoleEvent <>9__4_4; public static ConsoleEvent <>9__4_5; public static Action<long, ZPackage> <>9__5_0; public static Action<long, ZPackage> <>9__5_1; internal void <RegisterCommands>b__4_0(ConsoleEventArgs args) { SendToServer(args, "assign"); } internal void <RegisterCommands>b__4_1(ConsoleEventArgs args) { SendToServer(args, "remove"); } internal void <RegisterCommands>b__4_2(ConsoleEventArgs args) { SendToServer(args, "list"); } internal void <RegisterCommands>b__4_3(ConsoleEventArgs args) { SendToServer(args, "freeze"); } internal void <RegisterCommands>b__4_4(ConsoleEventArgs args) { SendToServer(args, "unfreeze"); } internal void <RegisterCommands>b__4_5(ConsoleEventArgs args) { SendToServer(args, "freezestatus"); } internal void <RegisterRpcHandlers>b__5_0(long sender, ZPackage pkg) { HandleCmdServer(sender, pkg); } internal void <RegisterRpcHandlers>b__5_1(long sender, ZPackage pkg) { HandleReplyClient(pkg); } } private const string RpcCmd = "VH_AdminCmd"; private const string RpcReply = "VH_AdminCmdReply"; private static bool _registered; private static Terminal _pendingTerminal; [HarmonyPatch(typeof(Terminal), "InitTerminal")] [HarmonyPostfix] private static void RegisterCommands() { //IL_0041: Unknown result type (might be due to invalid IL or missing references) //IL_002c: Unknown result type (might be due to invalid IL or missing references) //IL_0031: Unknown result type (might be due to invalid IL or missing references) //IL_0037: Expected O, but got Unknown //IL_007a: 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_0070: Expected O, but got Unknown //IL_00b3: Unknown result type (might be due to invalid IL or missing references) //IL_009e: Unknown result type (might be due to invalid IL or missing references) //IL_00a3: Unknown result type (might be due to invalid IL or missing references) //IL_00a9: Expected O, but got Unknown //IL_00ec: Unknown result type (might be due to invalid IL or missing references) //IL_00d7: Unknown result type (might be due to invalid IL or missing references) //IL_00dc: Unknown result type (might be due to invalid IL or missing references) //IL_00e2: Expected O, but got Unknown //IL_0125: 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_011b: Expected O, but got Unknown //IL_015e: Unknown result type (might be due to invalid IL or missing references) //IL_0149: Unknown result type (might be due to invalid IL or missing references) //IL_014e: Unknown result type (might be due to invalid IL or missing references) //IL_0154: Expected O, but got Unknown if (_registered) { return; } _registered = true; object obj = <>c.<>9__4_0; if (obj == null) { ConsoleEvent val = delegate(ConsoleEventArgs args) { SendToServer(args, "assign"); }; <>c.<>9__4_0 = val; obj = (object)val; } new ConsoleCommand("vh_assignfaction", "<PlayerName> <1|2|3> — Assign a player to a faction.", (ConsoleEvent)obj, true, false, false, false, false, false, (ConsoleOptionsFetcher)null, false, false, false); object obj2 = <>c.<>9__4_1; if (obj2 == null) { ConsoleEvent val2 = delegate(ConsoleEventArgs args) { SendToServer(args, "remove"); }; <>c.<>9__4_1 = val2; obj2 = (object)val2; } new ConsoleCommand("vh_removefaction", "<PlayerName> — Remove a player's faction assignment.", (ConsoleEvent)obj2, true, false, false, false, false, false, (ConsoleOptionsFetcher)null, false, false, false); object obj3 = <>c.<>9__4_2; if (obj3 == null) { ConsoleEvent val3 = delegate(ConsoleEventArgs args) { SendToServer(args, "list"); }; <>c.<>9__4_2 = val3; obj3 = (object)val3; } new ConsoleCommand("vh_listfactions", "List all faction assignments.", (ConsoleEvent)obj3, true, false, false, false, false, false, (ConsoleOptionsFetcher)null, false, false, false); object obj4 = <>c.<>9__4_3; if (obj4 == null) { ConsoleEvent val4 = delegate(ConsoleEventArgs args) { SendToServer(args, "freeze"); }; <>c.<>9__4_3 = val4; obj4 = (object)val4; } new ConsoleCommand("vh_freeze", "[reason...] — Freeze the world (admin only).", (ConsoleEvent)obj4, true, false, false, false, false, false, (ConsoleOptionsFetcher)null, false, false, false); object obj5 = <>c.<>9__4_4; if (obj5 == null) { ConsoleEvent val5 = delegate(ConsoleEventArgs args) { SendToServer(args, "unfreeze"); }; <>c.<>9__4_4 = val5; obj5 = (object)val5; } new ConsoleCommand("vh_unfreeze", "Unfreeze the world (admin only).", (ConsoleEvent)obj5, true, false, false, false, false, false, (ConsoleOptionsFetcher)null, false, false, false); object obj6 = <>c.<>9__4_5; if (obj6 == null) { ConsoleEvent val6 = delegate(ConsoleEventArgs args) { SendToServer(args, "freezestatus"); }; <>c.<>9__4_5 = val6; obj6 = (object)val6; } new ConsoleCommand("vh_freezestatus", "Show current freeze state.", (ConsoleEvent)obj6, true, false, false, false, false, false, (ConsoleOptionsFetcher)null, false, false, false); Plugin.Log.LogInfo((object)"Registered console commands: vh_assignfaction, vh_removefaction, vh_listfactions, vh_freeze, vh_unfreeze, vh_freezestatus"); } public static void RegisterRpcHandlers(ZRoutedRpc rpc) { rpc.Register<ZPackage>("VH_AdminCmd", (Action<long, ZPackage>)delegate(long sender, ZPackage pkg) { HandleCmdServer(sender, pkg); }); rpc.Register<ZPackage>("VH_AdminCmdReply", (Action<long, ZPackage>)delegate(long sender, ZPackage pkg) { HandleReplyClient(pkg); }); } private static void SendToServer(ConsoleEventArgs args, string cmd) { //IL_00e5: Unknown result type (might be due to invalid IL or missing references) //IL_00eb: Expected O, but got Unknown string[] array = args.Args.Skip(1).ToArray(); if ((Object)(object)ZNet.instance == (Object)null) { args.Context.AddString("Not connected."); return; } if (ZNet.instance.IsServer()) { foreach (string item in RunCommand(cmd, array, isAdmin: true)) { args.Context.AddString(item); } return; } if (ZRoutedRpc.instance == null) { args.Context.AddString("RPC not ready."); return; } long num = 0L; try { num = Traverse.Create((object)ZRoutedRpc.instance).Field("m_serverPeerID").GetValue<long>(); } catch { } if (num == 0L) { ZNetPeer serverPeer = ZNet.instance.GetServerPeer(); if (serverPeer != null) { num = serverPeer.m_uid; } } if (num == 0L) { args.Context.AddString("No server peer."); return; } ZPackage val = new ZPackage(); val.Write(cmd); val.Write(array.Length); string[] array2 = array; foreach (string text in array2) { val.Write(text); } _pendingTerminal = args.Context; ZRoutedRpc.instance.InvokeRoutedRPC(num, "VH_AdminCmd", new object[1] { val }); } private static void HandleCmdServer(long sender, ZPackage pkg) { //IL_00d7: Unknown result type (might be due to invalid IL or missing references) //IL_00de: Expected O, but got Unknown if ((Object)(object)ZNet.instance == (Object)null || !ZNet.instance.IsServer()) { return; } ZNetPeer peer = ZNet.instance.GetPeer(sender); bool flag = peer != null && AuthHelper.IsAdmin(peer); string text; string[] array; try { text = pkg.ReadString(); int num = pkg.ReadInt(); array = new string[num]; for (int i = 0; i < num; i++) { array[i] = pkg.ReadString(); } } catch (Exception ex) { Plugin.Log.LogError((object)$"VH_AdminCmd: bad package from {sender}: {ex.Message}"); return; } Plugin.Log.LogInfo((object)string.Format("VH_AdminCmd: sender={0} cmd={1} args=[{2}] isAdmin={3}", sender, text, string.Join(",", array), flag)); List<string> list = RunCommand(text, array, flag); ZPackage val = new ZPackage(); val.Write(list.Count); foreach (string item in list) { val.Write(item ?? ""); } ZRoutedRpc.instance.InvokeRoutedRPC(sender, "VH_AdminCmdReply", new object[1] { val }); } private static void HandleReplyClient(ZPackage pkg) { List<string> list = new List<string>(); try { int num = pkg.ReadInt(); for (int i = 0; i < num; i++) { list.Add(pkg.ReadString()); } } catch (Exception ex) { Plugin.Log.LogError((object)("VH_AdminCmdReply: bad package: " + ex.Message)); return; } Terminal pendingTerminal = _pendingTerminal; if ((Object)(object)pendingTerminal == (Object)null) { foreach (string item in list) { Plugin.Log.LogInfo((object)("[reply] " + item)); } return; } foreach (string item2 in list) { pendingTerminal.AddString(item2); } } private static List<string> RunCommand(string cmd, string[] cmdArgs, bool isAdmin) { List<string> list = new List<string>(); if (!isAdmin) { list.Add("Admin only."); return list; } switch (cmd) { case "assign": return RunAssign(cmdArgs); case "remove": return RunRemove(cmdArgs); case "list": return RunList(); case "freeze": return RunFreeze(cmdArgs); case "unfreeze": return RunUnfreeze(); case "freezestatus": return RunFreezeStatus(); default: list.Add("Unknown command: " + cmd); return list; } } private static List<string> RunAssign(string[] cmdArgs) { List<string> list = new List<string>(); if (cmdArgs.Length < 2) { list.Add("Usage: vh_assignfaction <PlayerName> <1|2|3>"); return list; } string text = cmdArgs[0]; if (!int.TryParse(cmdArgs[1], out var result) || result < 1 || result > 3) { list.Add("Faction must be 1, 2, or 3."); return list; } string text2 = ResolveCanonicalName(text); if (text2 == null) { list.Add("No online player matches '" + text + "'."); return list; } var (flag, text3) = HouseholdData.SetFaction(text2, result); if (!flag) { list.Add("Failed to assign faction."); return list; } Patches.PushFactionToPlayer(text3); Patches.BroadcastAllFactionsToAll(); list.Add("Assigned " + text3 + " to " + ConfigValues.GetFactionName(result) + "."); Plugin.Log.LogInfo((object)("vh_assignfaction: " + text3 + " -> " + ConfigValues.GetFactionName(result))); return list; } private static List<string> RunRemove(string[] cmdArgs) { List<string> list = new List<string>(); if (cmdArgs.Length < 1) { list.Add("Usage: vh_removefaction <PlayerName>"); return list; } string text = cmdArgs[0]; string text2 = ResolveCanonicalName(text); if (text2 == null) { list.Add("No online player matches '" + text + "'."); return list; } if (!HouseholdData.RemoveFaction(text2)) { list.Add("No assignment found for '" + text + "'."); return list; } Patches.PushFactionToPlayer(text2); Patches.BroadcastAllFactionsToAll(); list.Add("Removed faction assignment for " + text2 + "."); Plugin.Log.LogInfo((object)("vh_removefaction: " + text2)); return list; } private static List<string> RunList() { List<string> list = new List<string>(); List<PlayerAssignment> list2 = HouseholdData.ListAssignments(); if (list2.Count == 0) { list.Add("No faction assignments."); return list; } list.Add($"=== Faction Assignments ({list2.Count}) ==="); foreach (PlayerAssignment item in list2) { list.Add(" " + item.name + " -> " + ConfigValues.GetFactionName(item.factionId)); } return list; } private static List<string> RunFreeze(string[] cmdArgs) { List<string> list = new List<string>(); string text = ((cmdArgs.Length != 0) ? string.Join(" ", cmdArgs) : null); FreezeManager.SetFrozen(value: true, text); string text2 = (string.IsNullOrEmpty(text) ? "" : (" — " + text)); list.Add("World frozen." + text2); Plugin.Log.LogInfo((object)("vh_freeze:" + text2)); return list; } private static List<string> RunUnfreeze() { List<string> list = new List<string>(); FreezeManager.SetFrozen(value: false); list.Add("World unfrozen."); Plugin.Log.LogInfo((object)"vh_unfreeze"); return list; } private static List<string> RunFreezeStatus() { List<string> list = new List<string>(); if (FreezeManager.IsFrozen) { string reason = FreezeManager.Reason; string text = (string.IsNullOrEmpty(reason) ? "" : (" — " + reason)); DateTime? frozenSince = FreezeManager.FrozenSince; if (frozenSince.HasValue) { list.Add($"Frozen for {(int)(DateTime.UtcNow - frozenSince.Value).TotalSeconds}s.{text}"); } else { list.Add("Frozen." + text); } } else { list.Add("Not frozen."); } return list; } private static string ResolveCanonicalName(string rawName) { //IL_0044: Unknown result type (might be due to invalid IL or missing references) List<PlayerInfo> list = (from p in ZNet.instance.GetPlayerList() where !string.IsNullOrEmpty(p.m_name) && string.Equals(p.m_name, rawName, StringComparison.OrdinalIgnoreCase) select p).ToList(); if (list.Count > 1) { return null; } if (list.Count == 1) { return list[0].m_name; } return rawName; } } internal sealed class FactionEquipmentConfigSyncTag { public bool? IsAdminOnly = true; } internal static class FactionEquipmentConfig { private static ConfigFile _config; private static readonly List<Action> StatRefreshers = new List<Action>(); internal static ConfigEntry<float> HelmetArmor; internal static ConfigEntry<float> HelmetWeight; internal static ConfigEntry<float> HelmetDurability; internal static ConfigEntry<float> ChestArmor; internal static ConfigEntry<float> ChestWeight; internal static ConfigEntry<float> ChestDurability; internal static ConfigEntry<float> ChestMovementModifier; internal static ConfigEntry<float> ChestHeatResistance; internal static ConfigEntry<float> WolfChestArmor; internal static ConfigEntry<float> WolfChestWeight; internal static ConfigEntry<float> WolfChestDurability; internal static ConfigEntry<float> WolfChestMovementModifier; internal static ConfigEntry<float> WolfChestHeatResistance; internal static ConfigEntry<float> TrollArmorBonus; internal static ConfigEntry<float> BronzeArmorBonus; internal static ConfigEntry<float> LegsArmor; internal static ConfigEntry<float> LegsWeight; internal static ConfigEntry<float> LegsDurability; internal static ConfigEntry<float> LegsMovementModifier; internal static ConfigEntry<float> LegsHeatResistance; internal static ConfigEntry<float> CapeArmor; internal static ConfigEntry<float> CapeWeight; internal static ConfigEntry<float> CapeDurability; internal static void Initialize(BepInPlugin pluginInfo) { //IL_0011: Unknown result type (might be due to invalid IL or missing references) //IL_001b: Expected O, but got Unknown _config = new ConfigFile(Path.Combine(Paths.ConfigPath, "HouseholdHeim.FactionEquipment.cfg"), true, pluginInfo); HelmetArmor = Bind("Helmet", "Armor", 38f, "Armor rating for the Ironforge, Stormwind, and Frosthelm faction helmets."); HelmetWeight = Bind("Helmet", "Weight", 3f, "Inventory weight for faction helmets."); HelmetDurability = Bind("Helmet", "Durability", 800f, "Maximum durability for faction helmets."); ChestArmor = Bind("Chest", "Armor", 38f, "Armor rating for faction chest armor."); ChestWeight = Bind("Chest", "Weight", 10f, "Inventory weight for faction chest armor."); ChestDurability = Bind("Chest", "Durability", 1000f, "Maximum durability for faction chest armor."); ChestMovementModifier = Bind("Chest", "MovementModifier", -0.05f, "Movement speed modifier for faction chest armor. Negative values reduce movement speed."); ChestHeatResistance = Bind("Chest", "HeatResistance", 0.2f, "Heat resistance modifier for faction chest armor."); WolfChestArmor = Bind("WolfChest", "Armor", 20f, "Armor rating for household wolf chest variants (ArmorFactionRed/Green/Blue and factionchestwolf_red/green/blue)."); WolfChestWeight = Bind("WolfChest", "Weight", 10f, "Inventory weight for household wolf chest variants."); WolfChestDurability = Bind("WolfChest", "Durability", 1000f, "Maximum durability for household wolf chest variants."); WolfChestMovementModifier = Bind("WolfChest", "MovementModifier", -0.05f, "Movement speed modifier for household wolf chest variants. Negative values reduce movement speed."); WolfChestHeatResistance = Bind("WolfChest", "HeatResistance", 0f, "Heat resistance modifier for household wolf chest variants."); TrollArmorBonus = Bind("TrollArmor", "ArmorBonus", 2f, "Extra armor added to each faction Troll hide piece over its vanilla base armor."); BronzeArmorBonus = Bind("BronzeArmor", "ArmorBonus", 2f, "Extra armor added to each faction Bronze piece over its vanilla base armor."); LegsArmor = Bind("Legs", "Armor", 38f, "Armor rating for faction leg armor."); LegsWeight = Bind("Legs", "Weight", 10f, "Inventory weight for faction leg armor."); LegsDurability = Bind("Legs", "Durability", 1000f, "Maximum durability for faction leg armor."); LegsMovementModifier = Bind("Legs", "MovementModifier", -0.05f, "Movement speed modifier for faction leg armor. Negative values reduce movement speed."); LegsHeatResistance = Bind("Legs", "HeatResistance", 0.2f, "Heat resistance modifier for faction leg armor."); CapeArmor = Bind("Cape", "Armor", 1f, "Armor rating for faction Lox capes."); CapeWeight = Bind("Cape", "Weight", 4f, "Inventory weight for faction Lox capes."); CapeDurability = Bind("Cape", "Durability", 1000f, "Maximum durability for faction Lox capes."); SynchronizationManager instance = SynchronizationManager.Instance; if (instance != null) { instance.RegisterCustomConfig(_config); SynchronizationManager.OnConfigurationSynchronized += delegate { RefreshRegisteredStats(); }; } else { Plugin.Log.LogError((object)"Jotunn SynchronizationManager is unavailable; household armor config cannot be server-synchronized."); } } internal static void RegisterStatRefresher(Action refresh) { if (refresh != null) { StatRefreshers.Add(refresh); } } private static void RefreshRegisteredStats() { foreach (Action statRefresher in StatRefreshers) { try { statRefresher(); } catch (Exception arg) { Plugin.Log.LogError((object)$"Could not reapply server-synced armor stats: {arg}"); } } } private static ConfigEntry<float> Bind(string section, string key, float value, string description) { //IL_0018: Unknown result type (might be due to invalid IL or missing references) //IL_0022: Expected O, but got Unknown return _config.Bind<float>(section, key, value, new ConfigDescription(description, (AcceptableValueBase)null, new object[1] { new FactionEquipmentConfigSyncTag() })); } } public static class FactionShirts { private static readonly string[] Factions = new string[3] { "Red", "Green", "Blue" }; private static MethodInfo _loadImage; public static void Register() { try { string[] factions = Factions; for (int i = 0; i < factions.Length; i++) { CreateFactionShirt(factions[i]); } factions = Factions; for (int i = 0; i < factions.Length; i++) { CreateNewFactionChest(factions[i]); } Plugin.Log.LogInfo((object)$"FactionShirts: registered {Factions.Length} legacy shirts and {Factions.Length} new wolf chests."); } finally { PrefabManager.OnVanillaPrefabsAvailable -= Register; } } private static void CreateFactionShirt(string faction) { //IL_0068: Unknown result type (might be due to invalid IL or missing references) //IL_0072: Expected O, but got Unknown //IL_006d: Unknown result type (might be due to invalid IL or missing references) //IL_0074: Expected O, but got Unknown //IL_0167: 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) string text = faction.ToLower(); Texture2D val = LoadEmbeddedTexture("ValheimHouseholds.Assets.shirt_chest_" + text + ".png"); Texture2D val2 = LoadEmbeddedTexture("ValheimHouseholds.Assets.icon_" + text + ".png"); if ((Object)(object)val == (Object)null) { Plugin.Log.LogError((object)("FactionShirts: missing chest texture for " + faction)); return; } CustomItem val3 = new CustomItem("ArmorFaction" + faction, "ArmorWolfChest", new ItemConfig()); ItemDrop drop = val3.ItemDrop; if ((Object)(object)drop == (Object)null) { Plugin.Log.LogError((object)("FactionShirts: ItemDrop null for " + faction)); return; } SharedData shared = drop.m_itemData.m_shared; shared.m_name = "Faction Shirt (" + faction + ")"; shared.m_description = "A wolf-armor chestpiece bearing the " + faction + " household crest."; shared.m_setStatusEffect = null; shared.m_equipStatusEffect = null; shared.m_setName = string.Empty; shared.m_setSize = 0; ApplyWolfChestStats(shared); if (shared.m_damageModifiers != null) { shared.m_damageModifiers.RemoveAll((DamageModPair m) => (int)m.m_type == 64); } if ((Object)(object)val2 != (Object)null) { Sprite val4 = Sprite.Create(val2, new Rect(0f, 0f, (float)((Texture)val2).width, (float)((Texture)val2).height), new Vector2(0.5f, 0.5f)); shared.m_icons = (Sprite[])(object)new Sprite[1] { val4 }; } ApplyFactionMaterial(val3.ItemPrefab, val, faction); ItemManager.Instance.AddItem(val3); FactionEquipmentConfig.RegisterStatRefresher(delegate { ApplyWolfChestStats(drop.m_itemData.m_shared); }); Plugin.Log.LogInfo((object)("FactionShirts: registered ArmorFaction" + faction)); } private static void CreateNewFactionChest(string faction) { //IL_006c: Unknown result type (might be due to invalid IL or missing references) //IL_0076: Expected O, but got Unknown //IL_0071: Unknown result type (might be due to invalid IL or missing references) //IL_0078: Expected O, but got Unknown //IL_016c: 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) string text = faction.ToLowerInvariant(); Texture2D val = LoadEmbeddedTexture("ValheimHouseholds.Assets.shirt_chest_" + text + "_new.png"); Texture2D val2 = LoadEmbeddedTexture("ValheimHouseholds.Assets.icon_" + text + ".png"); if ((Object)(object)val == (Object)null) { Plugin.Log.LogError((object)("FactionShirts: missing NEW chest texture for " + faction)); return; } string text2 = "factionchestwolf_" + text; CustomItem val3 = new CustomItem(text2, "ArmorWolfChest", new ItemConfig()); ItemDrop drop = val3.ItemDrop; if ((Object)(object)drop == (Object)null) { Plugin.Log.LogError((object)("FactionShirts: ItemDrop null for " + text2)); return; } SharedData shared = drop.m_itemData.m_shared; shared.m_name = "Faction Chest Wolf (" + faction + ") NEW"; shared.m_description = "A wolf-armor chestpiece with the new " + faction + " household rune crest."; shared.m_setStatusEffect = null; shared.m_equipStatusEffect = null; shared.m_setName = string.Empty; shared.m_setSize = 0; ApplyWolfChestStats(shared); if (shared.m_damageModifiers != null) { shared.m_damageModifiers.RemoveAll((DamageModPair m) => (int)m.m_type == 64); } if ((Object)(object)val2 != (Object)null) { Sprite val4 = Sprite.Create(val2, new Rect(0f, 0f, (float)((Texture)val2).width, (float)((Texture)val2).height), new Vector2(0.5f, 0.5f)); shared.m_icons = (Sprite[])(object)new Sprite[1] { val4 }; } ApplyFactionMaterial(val3.ItemPrefab, val, "NEW_" + faction); ItemManager.Instance.AddItem(val3); FactionEquipmentConfig.RegisterStatRefresher(delegate { ApplyWolfChestStats(drop.m_itemData.m_shared); }); Plugin.Log.LogInfo((object)("FactionShirts: registered " + text2)); } private static void ApplyWolfChestStats(SharedData shared) { shared.m_armor = FactionEquipmentConfig.WolfChestArmor.Value; shared.m_weight = FactionEquipmentConfig.WolfChestWeight.Value; shared.m_maxDurability = FactionEquipmentConfig.WolfChestDurability.Value; shared.m_movementModifier = FactionEquipmentConfig.WolfChestMovementModifier.Value; shared.m_heatResistanceModifier = FactionEquipmentConfig.WolfChestHeatResistance.Value; } private static void ApplyFactionMaterial(GameObject prefab, Texture2D chestTex, string faction) { //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_004b: Expected O, but got Unknown SkinnedMeshRenderer[] componentsInChildren = prefab.GetComponentsInChildren<SkinnedMeshRenderer>(true); foreach (SkinnedMeshRenderer val in componentsInChildren) { Material[] sharedMaterials = ((Renderer)val).sharedMaterials; for (int j = 0; j < sharedMaterials.Length; j++) { if (!((Object)(object)sharedMaterials[j] == (Object)null)) { Material val2 = new Material(sharedMaterials[j]) { name = $"FactionMat_{faction}_{j}" }; val2.SetTexture("_MainTex", (Texture)(object)chestTex); sharedMaterials[j] = val2; } } ((Renderer)val).sharedMaterials = sharedMaterials; } } private static Texture2D LoadEmbeddedTexture(string resourceName) { //IL_0033: Unknown result type (might be due to invalid IL or missing references) //IL_0039: Expected O, but got Unknown using Stream stream = Assembly.GetExecutingAssembly().GetManifestResourceStream(resourceName); if (stream == null) { return null; } byte[] array = new byte[stream.Length]; stream.Read(array, 0, array.Length); Texture2D val = new Texture2D(2, 2, (TextureFormat)4, false); if (_loadImage == null) { _loadImage = typeof(Texture2D).GetMethod("LoadImage", new Type[1] { typeof(byte[]) }); if (_loadImage == null) { Type type = Type.GetType("UnityEngine.ImageConversion, UnityEngine.ImageConversionModule"); if (type != null) { _loadImage = type.GetMethod("LoadImage", new Type[2] { typeof(Texture2D), typeof(byte[]) }); } } } if (_loadImage == null) { Plugin.Log.LogError((object)"LoadImage method not found"); return null; } return ((bool)((_loadImage.GetParameters().Length == 1) ? _loadImage.Invoke(val, new object[1] { array }) : _loadImage.Invoke(null, new object[2] { val, array }))) ? val : null; } } [HarmonyPatch] public static class Patches { private const string RpcName = "VH_AdminBypassSync"; private static readonly HashSet<string> BlockedEverywhere = new HashSet<string> { "portal_wood" }; private static readonly HashSet<string> AllowedOutsideWard = new HashSet<string> { "raft", "karve", "vikingship", "vikingship_ashlands", "fire_pit", "bed", "piece_bed02", "piece_workbench" }; [HarmonyPatch(/*Could not decode attribute arguments.*/)] [HarmonyPostfix] private static void OnZRoutedRpcCtor(ZRoutedRpc __instance) { if (__instance == null) { return; } ConfigValues.LocalCanBypass = false; ConfigValues.LocalFactionId = 0; FactionMap.Replace(null); __instance.Register<bool>("VH_AdminBypassSync", (Action<long, bool>)delegate(long sender, bool value) { ConfigValues.LocalCanBypass = value; Plugin.Log.LogInfo((object)$"Received server bypass-allowed: {value}"); }); __instance.Register<int>("VH_FactionSync", (Action<long, int>)delegate(long sender, int factionId) { ConfigValues.LocalFactionId = factionId; Plugin.Log.LogInfo((object)$"Received server faction: {factionId}"); }); __instance.Register<ZPackage>("VH_FactionsAllSync", (Action<long, ZPackage>)delegate(long sender, ZPackage pkg) { try { int num = pkg.ReadInt(); List<KeyValuePair<string, int>> list = new List<KeyValuePair<string, int>>(num); for (int i = 0; i < num; i++) { string key = pkg.ReadString(); int value = pkg.ReadInt(); list.Add(new KeyValuePair<string, int>(key, value)); } FactionMap.Replace(list); Plugin.Log.LogInfo((object)$"Received factions-all sync: {num} entries"); } catch (Exception ex) { Plugin.Log.LogError((object)("VH_FactionsAllSync: bad package: " + ex.Message)); } }); __instance.Register<ZPackage>("VH_FreezeSync", (Action<long, ZPackage>)delegate(long sender, ZPackage pkg) { try { bool flag = pkg.ReadBool(); bool flag2 = pkg.ReadBool(); string text = pkg.ReadString(); bool localIsFrozen = ConfigValues.LocalIsFrozen; ConfigValues.LocalIsFrozen = flag; ConfigValues.WorldIsFrozen = flag2; ConfigValues.LocalBannerSubtitle = text ?? ""; if (localIsFrozen && !flag) { ConfigValues.LocalDamageImmunityUntilTime = Time.time + 5f; Plugin.Log.LogInfo((object)$"Received unfreeze — damage immunity granted until {ConfigValues.LocalDamageImmunityUntilTime}"); } Plugin.Log.LogInfo((object)$"VH_FreezeSync: youAreFrozen={flag} worldIsFrozen={flag2} subtitle={text}"); } catch (Exception ex) { Plugin.Log.LogError((object)("VH_FreezeSync: bad package: " + ex.Message)); } }); FactionCommands.RegisterRpcHandlers(__instance); ChestDepositPatches.RegisterRpcHandlers(__instance); ChestCommands.RegisterRpcHandlers(__instance); __instance.Register<ZPackage>("VH_ChestBanner", (Action<long, ZPackage>)delegate(long sender, ZPackage pkg) { try { string message = pkg.ReadString(); float duration = pkg.ReadSingle(); ChestBanner.Instance?.ShowBanner(message, duration); } catch (Exception ex) { Plugin.Log.LogWarning((object)("VH_ChestBanner: bad package: " + ex.Message)); } }); Plugin.Log.LogInfo((object)"Registered RPCs VH_AdminBypassSync, VH_FactionSync, VH_FactionsAllSync, VH_ChestDeposit, VH_ChestDepositAck, VH_ChestBanner, VH_ChestAdminCmd, VH_AdminCmd, VH_AdminCmdReply (LocalCanBypass=false, LocalFactionId=0)"); } [HarmonyPatch(typeof(ZRoutedRpc), "AddPeer")] [HarmonyPostfix] private static void OnAddPeer(ZRoutedRpc __instance, ZNetPeer peer) { if (!((Object)(object)ZNet.instance == (Object)null) && ZNet.instance.IsServer() && peer != null) { SendBypassDecisionTo(peer); SendFactionToPeer(peer); BroadcastAllFactionsToAll(); if (FreezeManager.IsFrozen) { FreezeManager.SendFreezeToPeer(peer); } } } [HarmonyPatch(typeof(Player), "Awake")] [HarmonyPostfix] private static void OnPlayerAwake(Player __instance) { if ((Object)(object)__instance == (Object)null) { return; } try { if ((Object)(object)ZNet.instance != (Object)null && ZNet.instance.IsDedicated()) { return; } } catch { } if ((Object)(object)((Component)__instance).GetComponent<FactionParticles>() == (Object)null) { ((Component)__instance).gameObject.AddComponent<FactionParticles>(); } } private static void SendBypassDecisionTo(ZNetPeer peer) { try { bool value = ConfigValues.AdminBypassEnabled.Value; ISocket socket = peer.m_socket; string text = ((socket != null) ? socket.GetHostName() : null) ?? ""; bool flag = !string.IsNullOrEmpty(text) && ZNet.instance.IsAdmin(text); bool flag2 = value && flag; ZRoutedRpc.instance.InvokeRoutedRPC(peer.m_uid, "VH_AdminBypassSync", new object[1] { flag2 }); Plugin.Log.LogInfo((object)$"Sent bypass-allowed={flag2} to peer {peer.m_uid} (host={text} isAdmin={flag} bypassEnabled={value})"); } catch (Exception ex) { Plugin.Log.LogError((object)("SendBypassDecisionTo failed: " + ex.Message)); } } private static void SendFactionToPeer(ZNetPeer peer) { try { string playerName = peer.m_playerName; int factionId = HouseholdData.GetFactionId(playerName); ZRoutedRpc.instance.InvokeRoutedRPC(peer.m_uid, "VH_FactionSync", new object[1] { factionId }); Plugin.Log.LogInfo((object)$"Sent faction={factionId} to peer {peer.m_uid} (player={playerName})"); } catch (Exception ex) { Plugin.Log.LogError((object)("SendFactionToPeer failed: " + ex.Message)); } } public static void PushFactionToPlayer(string playerName) { int factionId = HouseholdData.GetFactionId(playerName); Player localPlayer = Player.m_localPlayer; if ((Object)(object)localPlayer != (Object)null && string.Equals(localPlayer.GetPlayerName(), playerName, StringComparison.OrdinalIgnoreCase)) { ConfigValues.LocalFactionId = factionId; Plugin.Log.LogInfo((object)$"Updated local faction state: player={playerName}, faction={factionId}"); } if (!((Object)(object)ZNet.instance == (Object)null) && ZNet.instance.IsServer() && ZRoutedRpc.instance != null) { ZNetPeer val = ((IEnumerable<ZNetPeer>)ZNet.instance.GetPeers()).FirstOrDefault((Func<ZNetPeer, bool>)((ZNetPeer p) => p != null && string.Equals(p.m_playerName, playerName, StringComparison.OrdinalIgnoreCase))); if (val != null) { ZRoutedRpc.instance.InvokeRoutedRPC(val.m_uid, "VH_FactionSync", new object[1] { factionId }); Plugin.Log.LogInfo((object)$"Pushed faction={factionId} to player {playerName} (peer {val.m_uid})"); } BroadcastAllFactionsToAll(); } } public static void BroadcastAdminBypassToAll() { if ((Object)(object)ZNet.instance == (Object)null || !ZNet.instance.IsServer() || ZRoutedRpc.instance == null) { return; } int num = 0; foreach (ZNetPeer peer in ZNet.instance.GetPeers()) { if (peer != null) { SendBypassDecisionTo(peer); num++; } } Plugin.Log.LogInfo((object)$"Rebroadcast bypass decision to {num} peer(s) after config change"); } public static void BroadcastFactionAssignmentsToAll() { if ((Object)(object)ZNet.instance == (Object)null || !ZNet.instance.IsServer() || ZRoutedRpc.instance == null) { return; } foreach (ZNetPeer peer in ZNet.instance.GetPeers()) { if (peer != null && !string.IsNullOrEmpty(peer.m_playerName)) { SendFactionToPeer(peer); } } BroadcastAllFactionsToAll(); } public static void BroadcastAllFactionsToAll() { //IL_004c: Unknown result type (might be due to invalid IL or missing references) //IL_0053: Expected O, but got Unknown if ((Object)(object)ZNet.instance == (Object)null || !ZNet.instance.IsServer() || ZRoutedRpc.instance == null) { return; } List<PlayerAssignment> list = HouseholdData.ListAssignments(); int num = 0; foreach (ZNetPeer peer in ZNet.instance.GetPeers()) { if (peer == null) { continue; } ZPackage val = new ZPackage(); val.Write(list.Count); foreach (PlayerAssignment item in list) { val.Write(item.name ?? ""); val.Write(item.factionId); } ZRoutedRpc.instance.InvokeRoutedRPC(peer.m_uid, "VH_FactionsAllSync", new object[1] { val }); num++; } Plugin.Log.LogInfo((object)$"Broadcast factions-all ({list.Count} entries) to {num} peer(s)"); } [HarmonyPatch(typeof(Player), "PlacePiece")] [HarmonyPrefix] private static bool OnPlacePiece(Player __instance, Piece piece) { //IL_007c: 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_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) //IL_00b4: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)piece == (Object)null) { return true; } bool flag = HasLocalHostAdminBypass(__instance); if (ConfigValues.LocalFactionId == 0) { if (flag) { return true; } ((Character)__instance).Message((MessageType)1, "You must be assigned to a faction to build. Ask an admin.", 0, (Sprite)null, false); return false; } string text = ((Object)piece).name.Replace("(Clone)", "").ToLower(); GameObject value = Traverse.Create((object)__instance).Field("m_placementGhost").GetValue<GameObject>(); Vector3 val = (((Object)(object)value != (Object)null) ? value.transform.position : ((Component)__instance).transform.position); GameObject wardAt = WardIsLoveHelper.GetWardAt(val); bool flag2 = (Object)(object)wardAt != (Object)null; Plugin.Log.LogInfo((object)$"PlacePiece: '{text}' insideWard={flag2} pos={val} canBypass={flag} factionId={ConfigValues.LocalFactionId}"); if (BlockedEverywhere.Contains(text)) { if (flag) { return true; } ((Character)__instance).Message((MessageType)1, "Portals are disabled on this server.", 0, (Sprite)null, false); return false; } if (!flag2) { if (!ConfigValues.AllowBuildingInWilds.Value) { if (flag) { return true; } ((Character)__instance).Message((MessageType)1, "Building in the Wilds is currently disabled.", 0, (Sprite)null, false); return false; } if (!AllowedOutsideWard.Contains(text)) { if (flag) { return true; } ((Character)__instance).Message((MessageType)1, "Permanent industry is restricted to Faction Hubs.", 0, (Sprite)null, false); return false; } } else if (!WardIsLoveHelper.IsPermittedInWard(wardAt, __instance)) { if (flag) { return true; } ((Character)__instance).Message((MessageType)1, "You are not permitted to build in this ward.", 0, (Sprite)null, false); return false; } return true; } private static bool HasLocalHostAdminBypass(Player player) { if (ConfigValues.LocalCanBypass) { return true; } if ((Object)(object)player == (Object)null || (Object)(object)player != (Object)(object)Player.m_localPlayer || ConfigValues.AdminBypassEnabled == null || !ConfigValues.AdminBypassEnabled.Value || (Object)(object)ZNet.instance == (Object)null || !ZNet.instance.IsServer() || ZNet.instance.IsDedicated()) { return false; } ConfigValues.LocalCanBypass = true; Plugin.Log.LogInfo((object)"Admin building bypass enabled for local singleplayer/listen-server host."); return true; } [HarmonyPatch(typeof(Player), "SetControls", new Type[] { typeof(Vector3), typeof(bool), typeof(bool), typeof(bool), typeof(bool), typeof(bool), typeof(bool), typeof(bool), typeof(bool), typeof(bool), typeof(bool), typeof(bool) })] [HarmonyPrefix] private static void OnPlayerSetControls(Player __instance, ref Vector3 movedir, ref bool attack, ref bool attackHold, ref bool secondaryAttack, ref bool secondaryAttackHold, ref bool block, ref bool blockHold, ref bool jump, ref bool crouch, ref bool run, ref bool autoRun, ref bool dodge) { //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) if (!((Object)(object)__instance == (Object)null) && !((Object)(object)__instance != (Object)(object)Player.m_localPlayer) && ConfigValues.LocalIsFrozen) { movedir = Vector3.zero; attack = false; attackHold = false; secondaryAttack = false; secondaryAttackHold = false; block = false; blockHold = false; jump = false; crouch = false; run = false; autoRun = false; dodge = false; } } [HarmonyPatch(typeof(Character), "CustomFixedUpdate", new Type[] { typeof(float) })] [HarmonyPrefix] private static bool OnCharacterCustomFixedUpdate(Character __instance, float dt) { if (__instance is Player && ConfigValues.LocalIsFrozen) { return false; } return true; } [HarmonyPatch(typeof(BaseAI), "UpdateAI", new Type[] { typeof(float) })] [HarmonyPrefix] private static bool OnBaseAIUpdateAI(BaseAI __instance, ref bool __result, float dt) { if (ConfigValues.WorldIsFrozen) { __result = false; return false; } return true; } [HarmonyPatch(typeof(Ship), "CustomFixedUpdate", new Type[] { typeof(float) })] [HarmonyPrefix] private static bool OnShipCustomFixedUpdate(Ship __instance, float fixedDeltaTime) { if (ConfigValues.WorldIsFrozen) { return false; } return true; } [HarmonyPatch(typeof(Character), "RPC_Damage")] [HarmonyPrefix] private static bool OnCharacterRPCDamage(Character __instance, long sender, HitData hit) { if ((Object)(object)__instance == (Object)null) { return true; } if ((Object)(object)__instance != (Object)(object)Player.m_localPlayer) { return true; } if (ConfigValues.LocalIsFrozen || Time.time < ConfigValues.LocalDamageImmunityUntilTime) { Plugin.Log.LogInfo((object)$"Freeze immunity blocked a hit (frozen={ConfigValues.LocalIsFrozen}, graceUntil={ConfigValues.LocalDamageImmunityUntilTime}, now={Time.time})"); return false; } return true; } } [BepInPlugin("com.householdheim.valheimhouseholds", "ValheimHouseholds", "1.0.8")] public class Plugin : BaseUnityPlugin { public const string GUID = "com.householdheim.valheimhouseholds"; public const string NAME = "ValheimHouseholds"; public const string VERSION = "1.0.8"; private static Harmony _harmony; internal static ManualLogSource Log; internal static GameObject PrefabRoot; private FileSystemWatcher _configWatcher; private DateTime _lastReload = DateTime.MinValue; private volatile bool _pendingConfigReload; public void Awake() { //IL_0010: Unknown result type (might be due to invalid IL or missing references) //IL_001a: Expected O, but got Unknown //IL_040c: Unknown result type (might be due to invalid IL or missing references) //IL_0416: Expected O, but got Unknown Log = ((BaseUnityPlugin)this).Logger; PrefabRoot = new GameObject("ValheimHouseholds_Prefabs"); PrefabRoot.SetActive(false); Object.DontDestroyOnLoad((Object)(object)PrefabRoot); WardIsLoveHelper.InitializeFromPlugin(); ConfigValues.AdminBypassEnabled = ((BaseUnityPlugin)this).Config.Bind<bool>("General", "AdminBypassEnabled", false, "If true, admins can place any piece anywhere regardless of ward restrictions"); ConfigValues.AllowBuildingInWilds = ((BaseUnityPlugin)this).Config.Bind<bool>("General", "AllowBuildingInWilds", true, "If false, players cannot build outside any ward — useful for expeditions"); ConfigValues.Faction1Name = ((BaseUnityPlugin)this).Config.Bind<string>("Factions", "Faction1Name", "Ironforge (Red Team)", "Display name for faction 1"); ConfigValues.Faction2Name = ((BaseUnityPlugin)this).Config.Bind<string>("Factions", "Faction2Name", "Frosthelm (White Team)", "Display name for faction 2"); ConfigValues.Faction3Name = ((BaseUnityPlugin)this).Config.Bind<string>("Factions", "Faction3Name", "Stormwind (Blue Team)", "Display name for faction 3"); FactionEquipmentConfig.Initialize(((BaseUnityPlugin)this).Info.Metadata); ConfigValues.FreezeAdmins = ((BaseUnityPlugin)this).Config.Bind<bool>("Freeze", "FreezeAdmins", false, "If true, vh_freeze also freezes admins. Default false so admins remain mobile for Ghostmode camera work."); ConfigValues.FreezeBannerText = ((BaseUnityPlugin)this).Config.Bind<string>("Freeze", "FreezeBannerText", "WORLD FROZEN", "Persistent center-screen text shown to all frozen clients."); ConfigValues.FreezeBannerColor = ((BaseUnityPlugin)this).Config.Bind<string>("Freeze", "FreezeBannerColor", "#FF3333", "Hex color for the freeze banner."); ConfigValues.DiscordWebhookUrl = ((BaseUnityPlugin)this).Config.Bind<string>("Discord", "WebhookUrl", "", "Discord webhook URL to post freeze/unfreeze notifications. Leave blank to disable."); ConfigValues.DiscordServerLabel = ((BaseUnityPlugin)this).Config.Bind<string>("Discord", "ServerLabel", "", "Short server name shown in Discord messages, e.g. 'HouseholdHeim S01'. Optional."); ConfigValues.DiscordPostFreezeToggle = ((BaseUnityPlugin)this).Config.Bind<bool>("Discord", "PostFreezeToggle", true, "If true, post a message to the webhook whenever the world is frozen or unfrozen."); ConfigValues.ChestDetectedItems = ((BaseUnityPlugin)this).Config.Bind<string>("Chest", "DetectedItems", "", "Comma-separated item PREFAB names to watch for in Household Chests. Empty = mod inert. Example: ArmorWolfChest, TrophyBonemass"); ConfigValues.ChestMatchMode = ((BaseUnityPlugin)this).Config.Bind<string>("Chest", "MatchMode", "Exact", "Exact (default) or Contains. Exact requires full prefab name match. Contains matches any prefab whose name includes the listed keyword."); ConfigValues.ChestIgnoreNonFactionPlayers = ((BaseUnityPlugin)this).Config.Bind<bool>("Chest", "IgnoreNonFactionPlayers", true, "If true (default), deposits by unassigned players are silently ignored. Set false to record all players regardless of faction assignment."); ConfigValues.ChestWebhookOnDeposit = ((BaseUnityPlugin)this).Config.Bind<bool>("Chest", "WebhookOnDeposit", true, "If true, post a Discord message for every detected item deposited. Reuses [Discord] WebhookUrl. Tokens: {faction} {itemName} {count} {player} {serverLabel}"); ConfigValues.ChestWebhookMessageTemplate = ((BaseUnityPlugin)this).Config.Bind<string>("Chest", "WebhookMessageTemplate", "**[ValheimHouseholds]** {faction} deposited **{count}× {itemName}** into a Household Chest.", "Discord message template for per-deposit posts. Tokens: {faction} {itemName} {count} {player} {serverLabel}"); ConfigValues.ChestFreezeMode = ((BaseUnityPlugin)this).Config.Bind<string>("Chest", "FreezeMode", "Off", "Off | Any | All. Off = never freeze. Any = freeze when first qualifying deposit arrives (first-to-deposit wins). All = freeze when a faction has deposited every item in DetectedItems."); ConfigValues.ChestFreezeMessage = ((BaseUnityPlugin)this).Config.Bind<string>("Chest", "FreezeMessage", "{faction} has claimed all spoils!", "Center-screen freeze banner text when completion triggers a world freeze. Token: {faction} = winning faction name. Also used as the Discord freeze post."); ConfigValues.ChestFreezeOnlyOnce = ((BaseUnityPlugin)this).Config.Bind<bool>("Chest", "FreezeOnlyOnce", true, "If true (default), freeze fires at most once per faction. Prevents double-freeze on server restart or repeated deposits after /vh_unfreeze."); ConfigValues.ChestBroadcastInGame = ((BaseUnityPlugin)this).Config.Bind<bool>("Chest", "BroadcastInGame", true, "If true, show a center-screen banner to all players on each detected deposit."); ConfigValues.ChestBroadcastTemplate = ((BaseUnityPlugin)this).Config.Bind<string>("Chest", "BroadcastTemplate", "{faction} deposited {itemName} into the Household Chest.", "Center-screen banner text for per-deposit events. Tokens: {faction} {itemName} {count} {player}"); ConfigValues.ChestBroadcastCompletion = ((BaseUnityPlugin)this).Config.Bind<bool>("Chest", "BroadcastCompletion", true, "If true, show a center-screen banner to all players when a faction completes the deposit set. Fires even when FreezeMode = Off."); ConfigValues.ChestBroadcastCompletionTemplate = ((BaseUnityPlugin)this).Config.Bind<string>("Chest", "BroadcastCompletionTemplate", "{faction} has claimed all spoils!", "Center-screen banner text shown to all players on completion. Token: {faction}"); ConfigValues.ChestBroadcastCompletionDuration = ((BaseUnityPlugin)this).Config.Bind<float>("Chest", "BroadcastCompletionDuration", 10f, "Seconds the center-screen completion banner is displayed before disappearing."); ConfigValues.ChestConsumeOnDeposit = ((BaseUnityPlugin)this).Config.Bind<bool>("Chest", "ConsumeOnDeposit", true, "If true, the chest consumes (removes) a credited item after deposit is confirmed by the server. Prevents rivals from withdrawing and re-depositing to steal credit. Set false for display/museum chest presets."); ConfigValues.AdminBypassEnabled.SettingChanged += delegate { Log.LogInfo((object)$"AdminBypassEnabled changed to {ConfigValues.AdminBypassEnabled.Value} — broadcasting to peers"); Patches.BroadcastAdminBypassToAll(); }; ConfigValues.FreezeAdmins.SettingChanged += delegate { Log.LogInfo((object)$"FreezeAdmins changed to {ConfigValues.FreezeAdmins.Value} — rebroadcasting freeze state"); FreezeManager.BroadcastFreezeToAll(); }; SetupConfigWatcher(); HouseholdData.Load(); FreezeManager.LoadOnStartup(); ChestState.Initialize(); _harmony = new Harmony("com.householdheim.valheimhouseholds"); PatchOrLog(typeof(Patches)); PatchOrLog(typeof(FactionCommands)); PatchOrLog(typeof(ChestDepositPatches)); try { ChestCommands.Register(); Log.LogInfo((object)"Chest console commands registered"); } catch (Exception arg) { Log.LogError((object)$"ChestCommands.Register failed: {arg}"); } PrefabManager.OnVanillaPrefabsAvailable += FactionShirts.Register; PrefabManager.OnVanillaPrefabsAvailable += FactionArmorSets.Register; PrefabManager.OnVanillaPrefabsAvailable += delegate { try { HouseholdChestPiece.Register(); } catch (Exception arg2) { Log.LogError((object)$"HouseholdChestPiece.Register failed: {arg2}"); } try { HouseholdSortingPortalPiece.Register(); } catch (Exception arg3) { Log.LogError((object)$"HouseholdSortingPortal