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 FehuHeraldry v420.101.3
FehuHeraldry.dll
Decompiled a week 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.Reflection; using System.Runtime.CompilerServices; using System.Runtime.Versioning; using BepInEx; using BepInEx.Configuration; using BepInEx.Logging; using FehuHeraldry.Assets; using FehuHeraldry.Core; using FehuHeraldry.Rendering; using FehuHeraldry.UI; using HarmonyLib; using UnityEngine; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)] [assembly: TargetFramework(".NETStandard,Version=v2.1", FrameworkDisplayName = ".NET Standard 2.1")] [assembly: AssemblyCompany("FehuHeraldry")] [assembly: AssemblyConfiguration("Release")] [assembly: AssemblyFileVersion("420.101.3.0")] [assembly: AssemblyInformationalVersion("420.101.3")] [assembly: AssemblyProduct("FehuHeraldry")] [assembly: AssemblyTitle("FehuHeraldry")] [assembly: AssemblyVersion("420.101.3.0")] namespace FehuHeraldry { [BepInPlugin("net.forteca.fehuheraldry", "Fehu Heraldry", "420.101.3")] [BepInDependency(/*Could not decode attribute arguments.*/)] public sealed class Plugin : BaseUnityPlugin { public const string PluginGuid = "net.forteca.fehuheraldry"; private HeraldryWindow _window; private HeraldryRenderer _renderer; private HeraldryEmblemLibrary _emblems; private ConfigEntry<KeyboardShortcut> _toggleKey; private ConfigEntry<KeyboardShortcut> _targetKey; private ShipHeraldryHotkeys _hotkeys; private CapeHeraldryRuntime _cape; private Harmony _harmony; private void Awake() { //IL_001b: Unknown result type (might be due to invalid IL or missing references) //IL_004a: Unknown result type (might be due to invalid IL or missing references) //IL_0064: Unknown result type (might be due to invalid IL or missing references) //IL_0069: Unknown result type (might be due to invalid IL or missing references) //IL_006c: Unknown result type (might be due to invalid IL or missing references) //IL_0076: Invalid comparison between Unknown and I4 //IL_00a8: Unknown result type (might be due to invalid IL or missing references) //IL_00ad: Unknown result type (might be due to invalid IL or missing references) //IL_00b0: Unknown result type (might be due to invalid IL or missing references) //IL_00ba: Invalid comparison between Unknown and I4 //IL_0088: Unknown result type (might be due to invalid IL or missing references) //IL_018f: Unknown result type (might be due to invalid IL or missing references) //IL_0199: Expected O, but got Unknown //IL_00cc: Unknown result type (might be due to invalid IL or missing references) _toggleKey = ((BaseUnityPlugin)this).Config.Bind<KeyboardShortcut>("UI", "ToggleKey", new KeyboardShortcut((KeyCode)289, Array.Empty<KeyCode>()), "Key used to open Fehu Heraldry. F7 is reserved and cannot be used."); _targetKey = ((BaseUnityPlugin)this).Config.Bind<KeyboardShortcut>("UI", "TargetKey", new KeyboardShortcut((KeyCode)290, Array.Empty<KeyCode>()), "Key used to target a supported Heraldry ship. F7 is reserved and cannot be used."); KeyboardShortcut value = _toggleKey.Value; if ((int)((KeyboardShortcut)(ref value)).MainKey == 288) { _toggleKey.Value = new KeyboardShortcut((KeyCode)289, Array.Empty<KeyCode>()); ((BaseUnityPlugin)this).Logger.LogInfo((object)"Migrated the old Heraldry F7 panel shortcut to F8."); } value = _targetKey.Value; if ((int)((KeyboardShortcut)(ref value)).MainKey == 288) { _targetKey.Value = new KeyboardShortcut((KeyCode)290, Array.Empty<KeyCode>()); } HeraldryAssetLoader heraldryAssetLoader = new HeraldryAssetLoader(Paths.PluginPath, ((BaseUnityPlugin)this).Logger); HeraldryRegistry registry = heraldryAssetLoader.LoadRegistry(); _emblems = new HeraldryEmblemLibrary(Paths.ConfigPath, registry, ((BaseUnityPlugin)this).Logger); HeraldrySaveManager heraldrySaveManager = new HeraldrySaveManager(Paths.ConfigPath, registry, ((BaseUnityPlugin)this).Logger); _renderer = new HeraldryRenderer(registry, heraldryAssetLoader, _emblems); HeraldryRuntime.Log = ((BaseUnityPlugin)this).Logger; HeraldryRuntime.Compositor = _renderer; HeraldryRuntime.Saves = heraldrySaveManager; HeraldryRuntime.SelectionStore = new HeraldrySelectionStore { Sail = heraldrySaveManager.Get("Sail").Clone() }; _cape = ((Component)this).gameObject.AddComponent<CapeHeraldryRuntime>(); _cape.Configure(heraldrySaveManager, _renderer); _harmony = new Harmony("net.forteca.fehuheraldry"); _harmony.PatchAll(typeof(Plugin).Assembly); _window = new HeraldryWindow(registry, heraldrySaveManager, _renderer, _emblems); _hotkeys = ((Component)this).gameObject.AddComponent<ShipHeraldryHotkeys>(); _hotkeys.Configure(_targetKey); ((BaseUnityPlugin)this).Logger.LogInfo((object)"Fehu Heraldry 420.101.3 loaded. Shared cape/sail/banner pipeline; local appearance only. Panel default F8; ship target default F9; ClanWarfare emblems preferred."); } private void Update() { //IL_0006: Unknown result type (might be due to invalid IL or missing references) //IL_000b: Unknown result type (might be due to invalid IL or missing references) //IL_000e: Unknown result type (might be due to invalid IL or missing references) //IL_0018: Invalid comparison between Unknown and I4 //IL_0027: Unknown result type (might be due to invalid IL or missing references) //IL_002c: Unknown result type (might be due to invalid IL or missing references) KeyboardShortcut value = _toggleKey.Value; if ((int)((KeyboardShortcut)(ref value)).MainKey != 288 && HeraldryInput.AllowsShortcut()) { value = _toggleKey.Value; if (((KeyboardShortcut)(ref value)).IsDown()) { _window.Toggle(); } } _renderer.UpdatePreview(); } private void OnDestroy() { ShipHeraldryTarget.RestoreAll(); BannerHeraldryTarget.RestoreAll(); if (_harmony != null) { _harmony.UnpatchSelf(); } if ((Object)(object)_cape != (Object)null) { _cape.Shutdown(); } HeraldryRuntime.SelectionStore = null; HeraldryRuntime.Compositor = null; HeraldryRuntime.Saves = null; if (_renderer != null) { _renderer.Dispose(); } if (_emblems != null) { _emblems.Dispose(); } } private void OnGUI() { _window.Draw(); } } } namespace FehuHeraldry.UI { public sealed class HeraldryWindow { private readonly HeraldryRegistry _registry; private readonly HeraldrySaveManager _saves; private readonly HeraldryRenderer _renderer; private readonly HeraldryEmblemLibrary _emblems; private readonly string[] _types = new string[3] { "Sail", "Banner", "Cape" }; private readonly Dictionary<string, HeraldrySelection> _drafts = new Dictionary<string, HeraldrySelection>(); private bool _visible; private int _tab; private Rect _window = new Rect(180f, 100f, 720f, 500f); private Vector2 _scroll; private string _draftTarget; private string _message; public HeraldryWindow(HeraldryRegistry registry, HeraldrySaveManager saves, HeraldryRenderer renderer, HeraldryEmblemLibrary emblems) { //IL_0044: Unknown result type (might be due to invalid IL or missing references) //IL_0049: Unknown result type (might be due to invalid IL or missing references) _registry = registry; _saves = saves; _renderer = renderer; _emblems = emblems; } public void Toggle() { _visible = !_visible; if (_visible) { BeginEdit(); } } private void BeginEdit() { _drafts.Clear(); string[] types = _types; foreach (string text in types) { _drafts[text] = _saves.Get(text).Clone(); } _draftTarget = null; _message = null; } public void Draw() { //IL_000f: Unknown result type (might be due to invalid IL or missing references) //IL_001b: Unknown result type (might be due to invalid IL or missing references) //IL_002f: Expected O, but got Unknown //IL_002a: Unknown result type (might be due to invalid IL or missing references) //IL_002f: Unknown result type (might be due to invalid IL or missing references) if (_visible) { _window = GUILayout.Window(811024, _window, new WindowFunction(DrawWindow), "Fehu Heraldry", Array.Empty<GUILayoutOption>()); } } private void DrawWindow(int id) { //IL_0130: Unknown result type (might be due to invalid IL or missing references) //IL_0308: Unknown result type (might be due to invalid IL or missing references) //IL_032d: Unknown result type (might be due to invalid IL or missing references) //IL_0332: Unknown result type (might be due to invalid IL or missing references) //IL_07f2: Unknown result type (might be due to invalid IL or missing references) GUILayout.BeginHorizontal(Array.Empty<GUILayoutOption>()); for (int i = 0; i < _types.Length; i++) { if (GUILayout.Toggle(_tab == i, _types[i], GUI.skin.button, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(110f) })) { _tab = i; } } GUILayout.EndHorizontal(); string text = _types[_tab]; GUILayout.Label("EMBLEM LIBRARY: " + _emblems.Count + " | ClanWarfare + local Emblems", Array.Empty<GUILayoutOption>()); if (text == "Banner" && GUILayout.Button("Target Banner (crosshair, 15 m)", Array.Empty<GUILayoutOption>())) { BannerHeraldryTarget.SelectCrosshair(); } IHeraldryTarget target = ResolveTarget(text); string reason = ((text == "Sail") ? "Select a supported ship with F9." : ((text == "Banner") ? "Aim at a vanilla banner and click Target Banner." : "Equip a supported cape.")); bool flag = target != null && target.CanApply(out reason); string text2 = text + "|" + ((target == null) ? "none" : (((Object)target).GetInstanceID() + "|" + target.DisplayName)); if (_draftTarget != text2) { _draftTarget = text2; _drafts[text] = ((target == null || target.SavedSelection == null) ? _saves.Get(text) : target.SavedSelection).Clone(); _message = null; } HeraldryStyleDef[] array = _registry.Styles(text).ToArray(); Ship val = ((text == "Sail" && HeraldryRuntime.SelectionStore != null) ? HeraldryRuntime.SelectionStore.Target : null); HeraldryProfileDef shipProfile = (((Object)(object)val == (Object)null) ? null : HeraldryRuntime.ProfileForShip(HeraldryRuntime.NormalizePrefabIdentity(((Object)val).name))); if (text == "Sail" && shipProfile != null) { array = array.Where((HeraldryStyleDef x) => x.profileId == shipProfile.profileId).ToArray(); } if (text == "Cape" && flag) { array = array.Where((HeraldryStyleDef x) => x.prefabName == target.DisplayName).ToArray(); } if (text == "Banner" && flag) { array = array.Where((HeraldryStyleDef x) => x.prefabName == ((WorldHeraldryTarget)target).Prefab).ToArray(); } HeraldrySelection draft = _drafts[text]; if (array.Length != 0 && !array.Any((HeraldryStyleDef x) => x.styleId == draft.styleId)) { draft.styleId = array[0].styleId; draft.placementId = array[0].profileId; } GUILayout.BeginHorizontal(Array.Empty<GUILayoutOption>()); GUILayout.BeginVertical(Array.Empty<GUILayoutOption>()); _scroll = GUILayout.BeginScrollView(_scroll, (GUILayoutOption[])(object)new GUILayoutOption[2] { GUILayout.Width(420f), GUILayout.Height(305f) }); Field("Style", array.Cast<object>().ToArray(), draft, 0); Field("Emblem", _registry.Emblems.Cast<object>().ToArray(), draft, 1); Field("Emblem Color", _registry.Colors.Cast<object>().ToArray(), draft, 3); GUILayout.BeginHorizontal(Array.Empty<GUILayoutOption>()); GUILayout.Label("Emblem Scale", (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(130f) }); if (GUILayout.Button("-", (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(36f) })) { draft.emblemScale = Mathf.Clamp(draft.emblemScale - 0.1f, 0.4f, 2f); } GUILayout.Label(draft.emblemScale.ToString("0.0"), (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(45f) }); if (GUILayout.Button("+", (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(36f) })) { draft.emblemScale = Mathf.Clamp(draft.emblemScale + 0.1f, 0.4f, 2f); } GUILayout.EndHorizontal(); GUILayout.Label("PNG colors and original fabric are preserved. Emblem Color affects built-in symbols.", Array.Empty<GUILayoutOption>()); GUILayout.EndScrollView(); GUILayout.EndVertical(); GUILayout.BeginVertical((GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(250f) }); GUILayout.Label("Preview", GUI.skin.box, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Height(28f) }); Texture2D val2 = (flag ? target.Preview(draft) : null); if ((Object)(object)val2 != (Object)null) { GUILayout.Label((Texture)(object)val2, (GUILayoutOption[])(object)new GUILayoutOption[2] { GUILayout.Width(220f), GUILayout.Height(220f) }); } else { GUILayout.Label((_registry.Emblem(draft.emblemId) == null) ? ("Missing: " + draft.emblemId) : "Select a live target to preview original fabric + emblem.", Array.Empty<GUILayoutOption>()); } if (GUILayout.Button("Reload Emblems", Array.Empty<GUILayoutOption>())) { _emblems.Reload(); _renderer.Invalidate(); if ((Object)(object)CapeHeraldryRuntime.Instance != (Object)null) { CapeHeraldryRuntime.Instance.EmblemsReloaded(); } WorldHeraldryTarget.RefreshAll(); } GUILayout.Label("Local appearance; multiplayer synchronization is pending.", Array.Empty<GUILayoutOption>()); GUILayout.EndVertical(); GUILayout.EndHorizontal(); GUILayout.Label(((text == "Cape") ? "CURRENT CAPE: " : "TARGET: ") + ((target == null) ? "none" : target.DisplayName), Array.Empty<GUILayoutOption>()); GUILayout.Label("SELECTED EMBLEM: " + ((_registry.Emblem(draft.emblemId) == null) ? "Missing: " : "") + draft.emblemId, Array.Empty<GUILayoutOption>()); if (!flag) { GUILayout.Label(reason, Array.Empty<GUILayoutOption>()); } if (!string.IsNullOrEmpty(_message)) { GUILayout.Label(_message, Array.Empty<GUILayoutOption>()); } GUILayout.BeginHorizontal(Array.Empty<GUILayoutOption>()); if (GUILayout.Button("Reset", (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Height(32f) })) { _drafts[text] = ((array.Length != 0) ? _registry.DefaultForStyle(array[0]) : _registry.Default(text)); } GUI.enabled = flag && _registry.Validate(text, draft); if (GUILayout.Button("Apply", (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Height(32f) })) { if (target.Apply(draft, out var reason2)) { _message = text + " heraldry applied."; if (text == "Sail") { HeraldryRuntime.SelectionStore.Sail = draft.Clone(); } } else { _message = reason2; } } GUI.enabled = flag; if (GUILayout.Button("Restore Original", (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Height(32f) })) { _message = (target.RestoreOriginal(out var reason3) ? "Original appearance restored; saved assignment removed." : reason3); } GUI.enabled = true; if (GUILayout.Button("Cancel", (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Height(32f) })) { BeginEdit(); _visible = false; } GUILayout.EndHorizontal(); GUI.DragWindow(new Rect(0f, 0f, 10000f, 22f)); } private static IHeraldryTarget ResolveTarget(string type) { if (type == "Cape") { return CapeHeraldryRuntime.Instance; } if (type == "Banner") { return BannerHeraldryTarget.Selected; } Ship val = ((HeraldryRuntime.SelectionStore == null) ? null : HeraldryRuntime.SelectionStore.Target); if ((Object)(object)val == (Object)null) { return null; } ShipHeraldryTarget component = ((Component)val).GetComponent<ShipHeraldryTarget>(); if (!((Object)(object)component == (Object)null)) { return component; } return ((Component)val).gameObject.AddComponent<ShipHeraldryTarget>(); } private void Field(string label, object[] items, HeraldrySelection selection, int field) { if (items.Length == 0) { GUILayout.Label(label + " — None", Array.Empty<GUILayoutOption>()); return; } GUILayout.BeginHorizontal(Array.Empty<GUILayoutOption>()); GUILayout.Label(label, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(130f) }); if (GUILayout.Button("<", (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(34f) })) { Move(selection, field, items, -1); } GUILayout.Label(CurrentName(selection, field, items), (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(190f) }); if (GUILayout.Button(">", (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(34f) })) { Move(selection, field, items, 1); } GUILayout.EndHorizontal(); } private static string CurrentId(HeraldrySelection s, int f) { return f switch { 3 => s.secondaryColorId, 2 => s.primaryColorId, 1 => s.emblemId, 0 => s.styleId, _ => s.trimColorId, }; } private static string CurrentName(HeraldrySelection s, int f, object[] items) { string text = CurrentId(s, f); foreach (object obj in items) { if (obj is HeraldryStyleDef heraldryStyleDef && heraldryStyleDef.styleId == text) { return heraldryStyleDef.displayName; } if (obj is HeraldryEmblemDef heraldryEmblemDef && heraldryEmblemDef.emblemId == text) { return heraldryEmblemDef.displayName + " — " + heraldryEmblemDef.category + " (" + (string.IsNullOrEmpty(heraldryEmblemDef.source) ? "Built-in" : heraldryEmblemDef.source) + ")"; } if (obj is HeraldryColorDef heraldryColorDef && heraldryColorDef.colorId == text) { return heraldryColorDef.displayName; } } if (f != 1) { return text; } return "Missing: " + text; } private static void Move(HeraldrySelection s, int field, object[] items, int delta) { if (items.Length != 0) { int num = Array.FindIndex(items, (object x) => IdOf(x) == CurrentId(s, field)); if (num < 0) { num = 0; } num = (num + delta + items.Length) % items.Length; string text = IdOf(items[num]); if (field == 0) { s.styleId = text; s.placementId = ((HeraldryStyleDef)items[num]).profileId; } else if (field == 1) { s.emblemId = text; } else if (field == 2) { s.primaryColorId = text; } else if (field == 3) { s.secondaryColorId = text; } else { s.trimColorId = text; } } } private static string IdOf(object value) { if (value is HeraldryStyleDef heraldryStyleDef) { return heraldryStyleDef.styleId; } if (!(value is HeraldryEmblemDef heraldryEmblemDef)) { return ((HeraldryColorDef)value).colorId; } return heraldryEmblemDef.emblemId; } } } namespace FehuHeraldry.Rendering { public sealed class BannerHeraldryTarget : WorldHeraldryTarget { private static readonly HashSet<BannerHeraldryTarget> Active = new HashSet<BannerHeraldryTarget>(); public static BannerHeraldryTarget Selected { get; private set; } public override string TargetType => "Banner"; private void Awake() { Active.Add(this); } public static bool Supported(string prefab) { if (prefab != null && prefab.StartsWith("piece_banner", StringComparison.Ordinal) && prefab.Length == 14 && int.TryParse(prefab.Substring(12), out var result) && result >= 1) { return result <= 11; } return false; } public static void SelectCrosshair() { //IL_002b: Unknown result type (might be due to invalid IL or missing references) //IL_003a: Unknown result type (might be due to invalid IL or missing references) Selected = null; RaycastHit val = default(RaycastHit); if ((Object)(object)Player.m_localPlayer == (Object)null || (Object)(object)GameCamera.instance == (Object)null || !Physics.Raycast(((Component)GameCamera.instance).transform.position, ((Component)GameCamera.instance).transform.forward, ref val, 15f)) { return; } Piece componentInParent = ((Component)((RaycastHit)(ref val)).collider).GetComponentInParent<Piece>(); if (!((Object)(object)componentInParent == (Object)null) && Supported(HeraldryRuntime.NormalizePrefabIdentity(((Object)componentInParent).name))) { Selected = ((Component)componentInParent).GetComponent<BannerHeraldryTarget>(); if ((Object)(object)Selected == (Object)null) { Selected = ((Component)componentInParent).gameObject.AddComponent<BannerHeraldryTarget>(); } } } public static void RestoreAll() { BannerHeraldryTarget[] array = Active.ToArray(); foreach (BannerHeraldryTarget bannerHeraldryTarget in array) { if ((Object)(object)bannerHeraldryTarget != (Object)null) { bannerHeraldryTarget.Restore(); } } Selected = null; } protected override void OnDestroy() { base.OnDestroy(); Active.Remove(this); if ((Object)(object)Selected == (Object)(object)this) { Selected = null; } } protected override bool Resolve(out List<HeraldrySurface> surfaces, out string reason) { surfaces = new List<HeraldrySurface>(); if (!Ready(out reason)) { return false; } if (!Supported(base.Prefab)) { reason = "Target a vanilla banner (piece_banner01 through piece_banner11)."; return false; } WearNTear component = ((Component)this).GetComponent<WearNTear>(); Transform[] source = (Transform[])(((Object)(object)component == (Object)null || component.m_fragmentRoots == null) ? ((Array)new Transform[0]) : ((Array)(from x in component.m_fragmentRoots where (Object)(object)x != (Object)null select x.transform).ToArray())); Renderer[] componentsInChildren = ((Component)this).GetComponentsInChildren<Renderer>(true); foreach (Renderer renderer in componentsInChildren) { if ((!(renderer is MeshRenderer) && !(renderer is SkinnedMeshRenderer)) || source.Any((Transform x) => (Object)(object)((Component)renderer).transform == (Object)(object)x || ((Component)renderer).transform.IsChildOf(x))) { continue; } MeshFilter component2 = ((Component)renderer).GetComponent<MeshFilter>(); if (!(renderer is SkinnedMeshRenderer) && ((Object)(object)component2 == (Object)null || (Object)(object)component2.sharedMesh == (Object)null)) { continue; } Material[] sharedMaterials = renderer.sharedMaterials; for (int num2 = 0; num2 < sharedMaterials.Length; num2++) { Material val = Materials.Source(renderer, num2); if (!((Object)(object)val == (Object)null) && !((Object)(object)val.shader == (Object)null) && !(((Object)val.shader).name != "Custom/Vegetation")) { string text = HeraldryAlbedo.Property(val); if (text != null) { surfaces.Add(new HeraldrySurface { Renderer = renderer, Slot = num2, Property = text }); } } } } reason = ((surfaces.Count == 0) ? "No supported live banner cloth albedo surface was found." : null); return surfaces.Count > 0; } } [HarmonyPatch(typeof(Piece), "Awake")] internal static class HeraldryBannerLifecyclePatch { [HarmonyPostfix] private static void Postfix(Piece __instance) { if (BannerHeraldryTarget.Supported(HeraldryRuntime.NormalizePrefabIdentity(((Object)__instance).name)) && (Object)(object)((Component)__instance).GetComponent<BannerHeraldryTarget>() == (Object)null) { ((Component)__instance).gameObject.AddComponent<BannerHeraldryTarget>(); } } } public sealed class CapeHeraldryRuntime : MonoBehaviour, IHeraldryTarget { private static readonly FieldInfo Shoulder = AccessTools.Field(typeof(Humanoid), "m_shoulderItem"); private static readonly FieldInfo Instances = AccessTools.Field(typeof(VisEquipment), "m_shoulderItemInstances"); private static readonly FieldInfo CurrentHash = AccessTools.Field(typeof(VisEquipment), "m_currentShoulderItemHash"); private static readonly HashSet<string> Supported = new HashSet<string>(StringComparer.Ordinal) { "CapeLinen", "CapeDeerHide", "CapeTrollHide", "CapeWolf", "CapeLox", "CapeFeather", "CapeAsksvin" }; private static readonly Dictionary<string, string> ClothRenderers = new Dictionary<string, string>(StringComparer.Ordinal) { { "CapeLinen", "cape1" }, { "CapeDeerHide", "cape2" }, { "CapeTrollHide", "cape2" }, { "CapeWolf", "WolfCape_cloth" }, { "CapeLox", "LoxCape" }, { "CapeFeather", "MageCape" }, { "CapeAsksvin", "Asksvincape" } }; private HeraldryMaterialController _materials; private readonly HashSet<string> _diagnosed = new HashSet<string>(); private HeraldrySaveManager _saves; private HeraldryRenderer _compositor; private Player _player; private string _prefab; private string _appliedKey; private string _previewKey; private string _lastError; private long _playerId; private GameObject[] _roots = (GameObject[])(object)new GameObject[0]; private float _nextCheck; private Texture2D _preview; private Coroutine _reapply; public static CapeHeraldryRuntime Instance { get; private set; } public string CurrentCape => _prefab ?? "none"; public string DisplayName => CurrentCape; public HeraldrySelection SavedSelection { get { if (_saves != null) { return _saves.GetCape(_playerId, _prefab); } return null; } } public bool RestoreOriginal(out string reason) { return Remove(out reason); } public void Configure(HeraldrySaveManager saves, HeraldryRenderer compositor) { _saves = saves; _compositor = compositor; _materials = new HeraldryMaterialController(compositor); Instance = this; } public static bool IsLocalShoulder(VisEquipment equipment, int itemHash) { //IL_005e: Unknown result type (might be due to invalid IL or missing references) //IL_0065: Invalid comparison between Unknown and I4 Player localPlayer = Player.m_localPlayer; ItemData val = (ItemData)(((Object)(object)localPlayer == (Object)null || Shoulder == null) ? null : /*isinst with value type is only supported in some contexts*/); if ((Object)(object)localPlayer != (Object)null && (Object)(object)equipment == (Object)(object)((Humanoid)localPlayer).GetVisEquipment() && val != null && (Object)(object)val.m_dropPrefab != (Object)null && (int)val.m_shared.m_itemType == 17 && Supported.Contains(((Object)val.m_dropPrefab).name)) { return StringExtensionMethods.GetStableHashCode(((Object)val.m_dropPrefab).name) == itemHash; } return false; } public void EquipmentAttached() { if (_reapply != null) { ((MonoBehaviour)this).StopCoroutine(_reapply); } _reapply = ((MonoBehaviour)this).StartCoroutine(ReapplyAfterInitialization()); } private IEnumerator ReapplyAfterInitialization() { yield return null; Refresh(); yield return (object)new WaitForEndOfFrame(); Refresh(); yield return (object)new WaitForSecondsRealtime(0.15f); Refresh(); yield return (object)new WaitForSecondsRealtime(0.85f); Refresh(); _reapply = null; } private void Update() { if (_saves != null && !(Time.unscaledTime < _nextCheck)) { _nextCheck = Time.unscaledTime + 0.5f; Refresh(); } } private bool Resolve(out List<Renderer> renderers, out string reason) { //IL_01ce: Unknown result type (might be due to invalid IL or missing references) //IL_01d5: Invalid comparison between Unknown and I4 renderers = new List<Renderer>(); reason = null; Player localPlayer = Player.m_localPlayer; ItemData val = (ItemData)(((Object)(object)localPlayer == (Object)null || Shoulder == null) ? null : /*isinst with value type is only supported in some contexts*/); string prefab = ((val == null || (Object)(object)val.m_dropPrefab == (Object)null) ? null : ((Object)val.m_dropPrefab).name); long num = (((Object)(object)localPlayer == (Object)null) ? 0 : localPlayer.GetPlayerID()); VisEquipment val2 = (((Object)(object)localPlayer == (Object)null) ? null : ((Humanoid)localPlayer).GetVisEquipment()); List<GameObject> list = (((Object)(object)val2 == (Object)null || Instances == null) ? null : (Instances.GetValue(val2) as List<GameObject>)); GameObject[] array = (GameObject[])((list == null) ? ((Array)new GameObject[0]) : ((Array)list.Where((GameObject x) => (Object)(object)x != (Object)null).ToArray())); if ((Object)(object)_player != (Object)(object)localPlayer || _playerId != num || _prefab != prefab || !_roots.SequenceEqual(array)) { _materials.Restore(); ClearPreview(); _appliedKey = null; _player = localPlayer; _playerId = num; _prefab = prefab; _roots = array; } if (Shoulder == null || Instances == null || CurrentHash == null) { reason = "Installed equipment API is unsupported."; return false; } if ((Object)(object)localPlayer == (Object)null || ((Character)localPlayer).IsDead() || num == 0L || val == null) { reason = "Equip a supported cape on the local player."; return false; } if (!Supported.Contains(prefab) || (int)val.m_shared.m_itemType != 17) { reason = "Unsupported cape: " + prefab; return false; } if ((Object)(object)val2 == (Object)null || (int)CurrentHash.GetValue(val2) != StringExtensionMethods.GetStableHashCode(prefab) || array.Length == 0) { reason = "Waiting for the equipped cape visual."; return false; } renderers.AddRange((from x in array.SelectMany((GameObject x) => x.GetComponentsInChildren<SkinnedMeshRenderer>(true)) where (Object)(object)x.sharedMesh != (Object)null && ((Object)x).name == ClothRenderers[prefab] select x).Cast<Renderer>().Distinct()); if (!renderers.Any((Renderer x) => x.sharedMaterials.Any((Material m) => (Object)(object)m != (Object)null && HeraldryAlbedo.Property(m) != null))) { reason = "No supported skinned cape albedo material was found."; return false; } return true; } public bool CanApply(out string reason) { List<Renderer> renderers; return Resolve(out renderers, out reason); } public Texture2D Preview(HeraldrySelection selection) { if (!Resolve(out var renderers, out var _) || !_compositor.Registry.ValidateTarget("Cape", _prefab, selection)) { ClearPreview(); return null; } HeraldrySurface surface = Surfaces(renderers).First(); Texture val = _materials.SourceTexture(surface); string text = ((Object)val).GetInstanceID() + "|" + Key(selection); if (_previewKey == text && (Object)(object)_preview != (Object)null) { return _preview; } ClearPreview(); try { _preview = _compositor.ComposeCapeBaseAware(val, selection); _previewKey = text; } catch (Exception ex) { Report("Cape preview failed: " + ex.Message); } return _preview; } public bool Apply(HeraldrySelection selection, out string reason) { if (!Resolve(out var renderers, out reason) || !_compositor.Registry.ValidateTarget("Cape", _prefab, selection)) { if (reason == null) { reason = "Select an available emblem and a valid Cape style."; } return false; } HeraldrySelection savedSelection = SavedSelection; if (!ApplyMaterials(renderers, selection, out reason)) { return false; } try { _saves.SetCape(_playerId, _prefab, selection); } catch (Exception ex) { _materials.Restore(); _appliedKey = null; _saves.RevertCapeInMemory(_playerId, _prefab, savedSelection); Refresh(); reason = "Cape save failed: " + ex.Message; return false; } _appliedKey = Key(selection); HeraldryRuntime.Log.LogInfo((object)("Cape Apply: prefab=" + _prefab + " player=" + _playerId + " emblem=" + selection.emblemId + " slots=" + _materials.Count)); return true; } public bool Remove(out string reason) { reason = null; if (_playerId == 0L || string.IsNullOrEmpty(_prefab)) { reason = "Equip a cape first."; return false; } HeraldrySelection savedSelection = SavedSelection; try { _saves.SetCape(_playerId, _prefab, null); } catch (Exception ex) { _saves.RevertCapeInMemory(_playerId, _prefab, savedSelection); reason = "Cape restore save failed: " + ex.Message; return false; } _materials.Restore(); ClearPreview(); _appliedKey = null; HeraldryRuntime.Log.LogInfo((object)("Cape Restore Original: prefab=" + _prefab + " player=" + _playerId)); return true; } public void EmblemsReloaded() { ClearPreview(); _appliedKey = null; Refresh(); } private string Key(HeraldrySelection selection) { return _compositor.CacheRevision + "|" + selection.styleId + "|" + selection.emblemId + "|" + selection.secondaryColorId + "|" + selection.emblemScale.ToString(CultureInfo.InvariantCulture); } private void Refresh() { try { if (!Resolve(out var renderers, out var reason)) { _materials.Restore(); _appliedKey = null; return; } HeraldrySelection savedSelection = SavedSelection; if (savedSelection == null) { _materials.Restore(); _appliedKey = null; return; } if (!_compositor.Registry.ValidateTarget("Cape", _prefab, savedSelection)) { _materials.Restore(); _appliedKey = null; Report("Cape emblem/style unavailable; saved ID retained: " + savedSelection.emblemId); return; } bool flag = _materials.Count == 0 || _materials.HasChanged(); if (!(_appliedKey == Key(savedSelection)) || flag) { if (ApplyMaterials(renderers, savedSelection, out reason)) { _appliedKey = Key(savedSelection); _lastError = null; HeraldryRuntime.Log.LogInfo((object)("Cape Reapply: prefab=" + _prefab + " emblem=" + savedSelection.emblemId + " slots=" + _materials.Count)); } else { Report(reason); } } } catch (Exception ex) { Report("Cape refresh failed: " + ex.Message); } } private IEnumerable<HeraldrySurface> Surfaces(List<Renderer> renderers) { foreach (Renderer renderer in renderers) { Material[] materials = renderer.sharedMaterials; for (int i = 0; i < materials.Length; i++) { Material val = _materials.Source(renderer, i); string text = HeraldryAlbedo.Property(val); if (text != null) { HeraldrySurface heraldrySurface = new HeraldrySurface { Renderer = renderer, Slot = i, Property = text }; if (_diagnosed.Add(_prefab + "|" + ((Object)renderer).name + "|" + i + "|" + ((Object)val).name)) { HeraldryAlbedo.Diagnose(((Component)_player).gameObject, _prefab, heraldrySurface, val); } yield return heraldrySurface; } } } } private bool ApplyMaterials(List<Renderer> renderers, HeraldrySelection selection, out string reason) { return _materials.Apply(Surfaces(renderers), (Texture texture) => _compositor.ComposeCapeBaseAware(texture, selection), out reason); } private void ClearPreview() { if ((Object)(object)_preview != (Object)null && _compositor != null) { _compositor.ReleaseGenerated((Texture)(object)_preview); } _preview = null; _previewKey = null; } private void Report(string message) { if (!(_lastError == message)) { _lastError = message; HeraldryRuntime.Log.LogWarning((object)message); } } public void Shutdown() { ((MonoBehaviour)this).StopAllCoroutines(); _reapply = null; if (_materials != null) { _materials.Restore(); } ClearPreview(); _saves = null; if ((Object)(object)Instance == (Object)(object)this) { Instance = null; } } private void OnDestroy() { Shutdown(); } } [HarmonyPatch(typeof(VisEquipment), "AttachArmor")] internal static class CapeArmorAttachmentPatch { [HarmonyPostfix] [HarmonyPriority(0)] private static void Postfix(VisEquipment __instance, int itemHash, List<GameObject> __result) { try { CapeHeraldryRuntime instance = CapeHeraldryRuntime.Instance; if ((Object)(object)instance != (Object)null && __result != null && CapeHeraldryRuntime.IsLocalShoulder(__instance, itemHash)) { instance.EquipmentAttached(); } } catch (Exception ex) { if (HeraldryRuntime.Log != null) { HeraldryRuntime.Log.LogWarning((object)("Cape attachment hook failed: " + ex.Message)); } } } } public sealed class HeraldrySurface { public Renderer Renderer; public int Slot; public string Property; } public sealed class HeraldryMaterialController : IDisposable { private sealed class State { public HeraldrySurface Surface; public Material Original; public Material Owned; public Texture OriginalTexture; public Texture Generated; public MaterialPropertyBlock OriginalSlot; public bool OverridesSlot; } private List<State> _states = new List<State>(); private readonly HeraldryRenderer _compositor; public int Count => _states.Count; public HeraldryMaterialController(HeraldryRenderer compositor) { _compositor = compositor; } public Material Source(Renderer renderer, int slot) { Material[] materials = renderer.sharedMaterials; if (slot < 0 || slot >= materials.Length) { return null; } State state = _states.FirstOrDefault((State x) => (Object)(object)x.Surface.Renderer == (Object)(object)renderer && x.Surface.Slot == slot && (Object)(object)x.Owned == (Object)(object)materials[slot]); if (state != null) { return state.Original; } return materials[slot]; } public Texture SourceTexture(HeraldrySurface surface) { //IL_006a: Unknown result type (might be due to invalid IL or missing references) //IL_0070: Expected O, but got Unknown State state = _states.FirstOrDefault((State x) => (Object)(object)x.Surface.Renderer == (Object)(object)surface.Renderer && x.Surface.Slot == surface.Slot && surface.Slot < surface.Renderer.sharedMaterials.Length && (Object)(object)x.Owned == (Object)(object)surface.Renderer.sharedMaterials[surface.Slot]); if (state != null) { return state.OriginalTexture; } Material val = Source(surface.Renderer, surface.Slot); if ((Object)(object)val == (Object)null || !val.HasProperty(surface.Property)) { return null; } MaterialPropertyBlock val2 = new MaterialPropertyBlock(); surface.Renderer.GetPropertyBlock(val2, surface.Slot); if (val2.isEmpty) { surface.Renderer.GetPropertyBlock(val2); } return val2.GetTexture(surface.Property) ?? val.GetTexture(surface.Property); } public bool HasChanged() { //IL_0074: Unknown result type (might be due to invalid IL or missing references) //IL_007b: Expected O, but got Unknown foreach (State state in _states) { Renderer renderer = state.Surface.Renderer; if ((Object)(object)renderer == (Object)null) { return true; } Material[] sharedMaterials = renderer.sharedMaterials; if (state.Surface.Slot >= sharedMaterials.Length || (Object)(object)sharedMaterials[state.Surface.Slot] != (Object)(object)state.Owned) { return true; } if (state.OverridesSlot) { MaterialPropertyBlock val = new MaterialPropertyBlock(); renderer.GetPropertyBlock(val, state.Surface.Slot); if ((Object)(object)val.GetTexture(state.Surface.Property) != (Object)(object)state.Generated) { return true; } } } return false; } public bool Apply(IEnumerable<HeraldrySurface> surfaces, Func<Texture, Texture2D> compose, out string reason) { //IL_00a2: Unknown result type (might be due to invalid IL or missing references) //IL_00a7: Unknown result type (might be due to invalid IL or missing references) //IL_00e0: Expected O, but got Unknown //IL_01ad: Unknown result type (might be due to invalid IL or missing references) //IL_01b7: Expected O, but got Unknown //IL_01d1: Unknown result type (might be due to invalid IL or missing references) //IL_01d8: Expected O, but got Unknown reason = null; List<State> list = new List<State>(); try { foreach (HeraldrySurface surface in surfaces) { Material val = Source(surface.Renderer, surface.Slot); Texture val2 = SourceTexture(surface); if (!((Object)(object)val == (Object)null) && !((Object)(object)val2 == (Object)null)) { State state = new State { Surface = surface, Original = val, OriginalTexture = val2 }; list.Add(state); state.Generated = (Texture)(object)compose(val2); if ((Object)(object)state.Generated == (Object)null) { throw new InvalidOperationException("Composition returned no texture."); } state.Owned = new Material(val) { name = "FehuHeraldry_" + ((Object)surface.Renderer).GetInstanceID() + "_" + surface.Slot }; state.Owned.SetTexture(surface.Property, state.Generated); } } if (list.Count == 0) { throw new InvalidOperationException("No supported albedo surface was found."); } } catch (Exception ex) { foreach (State item in list) { Release(item); } reason = ex.Message; return false; } Restore(); _states = list; try { foreach (State state2 in _states) { Renderer renderer = state2.Surface.Renderer; Material[] sharedMaterials = renderer.sharedMaterials; state2.OriginalSlot = new MaterialPropertyBlock(); renderer.GetPropertyBlock(state2.OriginalSlot, state2.Surface.Slot); MaterialPropertyBlock val3 = new MaterialPropertyBlock(); renderer.GetPropertyBlock(val3, state2.Surface.Slot); if (val3.isEmpty) { renderer.GetPropertyBlock(val3); } state2.OverridesSlot = (Object)(object)val3.GetTexture(state2.Surface.Property) != (Object)null; if (state2.OverridesSlot) { val3.SetTexture(state2.Surface.Property, state2.Generated); } sharedMaterials[state2.Surface.Slot] = state2.Owned; renderer.sharedMaterials = sharedMaterials; if (state2.OverridesSlot) { renderer.SetPropertyBlock(val3, state2.Surface.Slot); } } return true; } catch (Exception ex2) { Restore(); reason = ex2.Message; return false; } } public void Restore() { //IL_007c: Unknown result type (might be due to invalid IL or missing references) //IL_0083: Expected O, but got Unknown foreach (State state in _states) { Renderer renderer = state.Surface.Renderer; if ((Object)(object)renderer != (Object)null) { Material[] sharedMaterials = renderer.sharedMaterials; if (state.Surface.Slot < sharedMaterials.Length && (Object)(object)sharedMaterials[state.Surface.Slot] == (Object)(object)state.Owned) { sharedMaterials[state.Surface.Slot] = state.Original; renderer.sharedMaterials = sharedMaterials; } MaterialPropertyBlock val = new MaterialPropertyBlock(); renderer.GetPropertyBlock(val, state.Surface.Slot); if (state.OverridesSlot && (Object)(object)val.GetTexture(state.Surface.Property) == (Object)(object)state.Generated) { renderer.SetPropertyBlock((state.OriginalSlot == null || state.OriginalSlot.isEmpty) ? null : state.OriginalSlot, state.Surface.Slot); } } Release(state); } _states.Clear(); } private void Release(State state) { if ((Object)(object)state.Generated != (Object)null) { _compositor.ReleaseGenerated(state.Generated); } if ((Object)(object)state.Owned != (Object)null) { Object.Destroy((Object)(object)state.Owned); } } public void Dispose() { Restore(); } } public static class HeraldryAlbedo { public static string Property(Material material) { if ((Object)(object)material == (Object)null) { return null; } if (material.HasProperty("_UseStyles") && material.GetFloat("_UseStyles") > 0.5f && material.HasProperty("_StyleTex") && (Object)(object)material.GetTexture("_StyleTex") != (Object)null) { return "_StyleTex"; } return new string[3] { "_BaseMap", "_BaseColorMap", "_MainTex" }.FirstOrDefault((string x) => material.HasProperty(x) && (Object)(object)material.GetTexture(x) != (Object)null); } public static void Diagnose(GameObject target, string prefab, HeraldrySurface surface, Material source) { HeraldryRuntime.Log.LogInfo((object)("Heraldry target=" + ((Object)target).GetInstanceID() + " prefab=" + prefab + " path=" + HeraldryRuntime.HierarchyPath(target.transform, ((Component)surface.Renderer).transform) + " renderer=" + ((object)surface.Renderer).GetType().Name + " material=" + ((Object)source).name + " slot=" + surface.Slot + " shader=" + (((Object)(object)source.shader == (Object)null) ? "<none>" : ((Object)source.shader).name) + " albedo=" + surface.Property + " properties=" + string.Join(",", source.GetTexturePropertyNames()))); } } public static class HeraldryPixelComposer { public static Color32 Blend(Color32 fabric, Color32 emblem, float opacity) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_0018: Unknown result type (might be due to invalid IL or missing references) //IL_0008: Unknown result type (might be due to invalid IL or missing references) //IL_0025: Unknown result type (might be due to invalid IL or missing references) //IL_0043: Unknown result type (might be due to invalid IL or missing references) //IL_0050: Unknown result type (might be due to invalid IL or missing references) //IL_005e: Unknown result type (might be due to invalid IL or missing references) //IL_0070: Unknown result type (might be due to invalid IL or missing references) //IL_0077: Unknown result type (might be due to invalid IL or missing references) //IL_0086: Unknown result type (might be due to invalid IL or missing references) //IL_008d: Unknown result type (might be due to invalid IL or missing references) //IL_009c: Unknown result type (might be due to invalid IL or missing references) //IL_00a3: Unknown result type (might be due to invalid IL or missing references) //IL_00b2: Unknown result type (might be due to invalid IL or missing references) //IL_00b8: Unknown result type (might be due to invalid IL or missing references) if (fabric.a == 0 || emblem.a == 0 || opacity <= 0f) { return fabric; } float amount = Math.Min(1f, opacity) * (float)(int)emblem.a / 255f; float num = 0.5f + 0.5f * (0.2126f * (float)(int)fabric.r + 0.7152f * (float)(int)fabric.g + 0.0722f * (float)(int)fabric.b) / 255f; return new Color32(Channel((int)fabric.r, (float)(int)emblem.r * num, amount), Channel((int)fabric.g, (float)(int)emblem.g * num, amount), Channel((int)fabric.b, (float)(int)emblem.b * num, amount), fabric.a); } private static byte Channel(float original, float art, float amount) { return (byte)Math.Max(0.0, Math.Min(255.0, Math.Round(original + (art - original) * amount))); } public static void Overlay(Color32[] pixels, int width, int height, Color32[] art, int artWidth, int artHeight, HeraldryProfileDef profile, float scale) { //IL_026f: Unknown result type (might be due to invalid IL or missing references) //IL_027d: Unknown result type (might be due to invalid IL or missing references) //IL_0285: Unknown result type (might be due to invalid IL or missing references) //IL_028a: Unknown result type (might be due to invalid IL or missing references) int num = Math.Max(1, profile.styleGrid); double num2 = Math.Max(0.001, profile.uvMaxX - profile.uvMinX); double num3 = Math.Max(0.001, profile.uvMaxY - profile.uvMinY); double num4 = ((profile.surfaceAspect > 0f) ? ((double)profile.surfaceAspect) : (num2 * (double)width / (num3 * (double)height))); double num5 = Math.Max(0.001, profile.emblemScale * scale); double num6 = num5 * num4 * (double)artHeight / (double)artWidth; double num7 = (double)profile.emblemRotation * Math.PI / 180.0; double num8 = Math.Cos(num7); double num9 = Math.Sin(num7); double num10 = Math.Max(0f, Math.Min(1f, profile.emblemOpacity)); for (int i = 0; i < height; i++) { for (int j = 0; j < width; j++) { double num11 = ((double)j + 0.5) / (double)width; double num12 = ((double)i + 0.5) / (double)height; num11 = (num11 * (double)num % 1.0 - (double)profile.uvMinX) / num2; num12 = (num12 * (double)num % 1.0 - (double)profile.uvMinY) / num3; if (!(num11 < 0.0) && !(num11 > 1.0) && !(num12 < 0.0) && !(num12 > 1.0)) { if (profile.mirrorU) { num11 = 1.0 - num11; } if (profile.mirrorV) { num12 = 1.0 - num12; } double num13 = (num11 - (double)profile.emblemCenterX) * num4; double num14 = num12 - (double)profile.emblemCenterY; double num15 = (num13 * num8 + num14 * num9) / (num5 * num4) + 0.5; double num16 = ((0.0 - num13) * num9 + num14 * num8) / num6 + 0.5; if (!(num15 < 0.0) && !(num15 > 1.0) && !(num16 < 0.0) && !(num16 > 1.0)) { int num17 = Math.Min(artWidth - 1, (int)(num15 * (double)artWidth)); int num18 = Math.Min(artHeight - 1, (int)(num16 * (double)artHeight)); int num19 = i * width + j; pixels[num19] = Blend(pixels[num19], art[num18 * artWidth + num17], (float)num10); } } } } } public static void OverlayNormalized(Color32[] pixels, int width, int height, Color32[] art, int artWidth, int artHeight) { //IL_0012: Unknown result type (might be due to invalid IL or missing references) //IL_0028: Unknown result type (might be due to invalid IL or missing references) //IL_0032: Unknown result type (might be due to invalid IL or missing references) //IL_0037: Unknown result type (might be due to invalid IL or missing references) for (int i = 0; i < height; i++) { for (int j = 0; j < width; j++) { int num = i * width + j; pixels[num] = Blend(pixels[num], art[i * artHeight / height * artWidth + j * artWidth / width], 1f); } } } } public sealed class HeraldryRenderer : IDisposable { private sealed class GeneratedTexture { public Texture2D Texture; public int References; } private const int Size = 128; private readonly HeraldryRegistry _registry; private readonly HeraldryAssetLoader _assets; private readonly HeraldryEmblemLibrary _emblems; private Texture2D _previewTexture; private Texture2D _composedTexture; private Texture2D _emblemMask; private string _lastKey; private Renderer _targetRenderer; private Material _originalMaterial; private Material _previewMaterial; private int _cacheRevision; private readonly Dictionary<string, GeneratedTexture> _baseCache = new Dictionary<string, GeneratedTexture>(); public Texture2D PreviewTexture => _previewTexture; public Texture2D ComposedTexture => _composedTexture; public HeraldryRegistry Registry => _registry; public int CacheRevision => _cacheRevision; public HeraldryRenderer(HeraldryRegistry registry, HeraldryAssetLoader assets, HeraldryEmblemLibrary emblems) { //IL_0033: Unknown result type (might be due to invalid IL or missing references) //IL_003d: Expected O, but got Unknown //IL_004a: Unknown result type (might be due to invalid IL or missing references) //IL_0054: Expected O, but got Unknown //IL_0061: Unknown result type (might be due to invalid IL or missing references) //IL_006b: Expected O, but got Unknown _registry = registry; _assets = assets; _emblems = emblems; _previewTexture = new Texture2D(128, 128, (TextureFormat)4, false); _composedTexture = new Texture2D(128, 128, (TextureFormat)4, false); _emblemMask = new Texture2D(128, 128, (TextureFormat)4, false); } public void UpdatePreview() { } public void Invalidate() { _lastKey = null; _cacheRevision++; } public void Compose(string type, HeraldrySelection selection) { //IL_0139: Unknown result type (might be due to invalid IL or missing references) //IL_013e: Unknown result type (might be due to invalid IL or missing references) //IL_02c2: 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_01b9: Unknown result type (might be due to invalid IL or missing references) //IL_01be: Unknown result type (might be due to invalid IL or missing references) //IL_0186: Unknown result type (might be due to invalid IL or missing references) //IL_018b: Unknown result type (might be due to invalid IL or missing references) //IL_0324: Unknown result type (might be due to invalid IL or missing references) //IL_032d: Unknown result type (might be due to invalid IL or missing references) //IL_0347: Unknown result type (might be due to invalid IL or missing references) //IL_034c: Unknown result type (might be due to invalid IL or missing references) //IL_025f: Unknown result type (might be due to invalid IL or missing references) //IL_0266: Unknown result type (might be due to invalid IL or missing references) //IL_0280: Unknown result type (might be due to invalid IL or missing references) //IL_0285: Unknown result type (might be due to invalid IL or missing references) //IL_01f5: Unknown result type (might be due to invalid IL or missing references) //IL_01f7: Unknown result type (might be due to invalid IL or missing references) //IL_01e1: Unknown result type (might be due to invalid IL or missing references) //IL_01e6: Unknown result type (might be due to invalid IL or missing references) if (selection == null) { return; } string text = string.Join("|", type, selection.styleId, selection.emblemId, selection.primaryColorId, selection.secondaryColorId, selection.trimColorId, selection.placementId, selection.emblemScale.ToString(CultureInfo.InvariantCulture)); if (_lastKey == text) { return; } _lastKey = text; HeraldryStyleDef heraldryStyleDef = _registry.Style(selection.styleId); HeraldryProfileDef heraldryProfileDef = ((heraldryStyleDef == null) ? null : _registry.Profile(heraldryStyleDef.profileId)); HeraldryEmblemDef heraldryEmblemDef = _registry.Emblem(selection.emblemId) ?? _registry.Emblem("fehu_rune") ?? _registry.Emblems.FirstOrDefault(); HeraldryColorDef heraldryColorDef = _registry.Color(selection.primaryColorId); HeraldryColorDef heraldryColorDef2 = _registry.Color(selection.secondaryColorId); HeraldryColorDef heraldryColorDef3 = _registry.Color(selection.trimColorId); if (heraldryStyleDef == null || heraldryProfileDef == null || heraldryEmblemDef == null || heraldryColorDef == null || heraldryColorDef2 == null || heraldryColorDef3 == null) { return; } Color32[] array = (Color32[])(object)new Color32[16384]; Color32 val = ToColor(heraldryColorDef); for (int i = 0; i < 128; i++) { for (int j = 0; j < 128; j++) { float num = (float)j / 127f; float num2 = (float)i / 127f; Color32 val2 = val; if (heraldryProfileDef.supportsTrim && (num2 < 0.11f || num2 > 0.89f)) { val2 = ToColor(heraldryColorDef3); } else if (Math.Abs(num - 0.13f) < 0.035f || Math.Abs(num - 0.87f) < 0.035f) { val2 = ToColor(heraldryColorDef2); } if (heraldryProfileDef.mirrorU && num > 0.5f) { val2 = array[i * 128 + (127 - j)]; } array[i * 128 + j] = val2; } } Color32[] array2 = _assets.LoadMask(heraldryProfileDef.maskAsset, 128, 128); if (array2 != null) { for (int k = 0; k < array.Length; k++) { if (array2[k].a > 0) { array[k] = Color32.Lerp(array[k], ToColor(heraldryColorDef2), (float)(int)array2[k].a / 255f); } } } _emblemMask.SetPixels32((Color32[])(object)new Color32[16384]); Color32[] array3 = (Color32[])array.Clone(); DrawEmblem(array, heraldryEmblemDef, heraldryProfileDef, ToColor(heraldryColorDef2), selection.emblemScale); _emblemMask.Apply(false, false); _composedTexture.SetPixels32(array); _composedTexture.Apply(false, false); Color32[] pixels = _emblemMask.GetPixels32(); for (int l = 0; l < pixels.Length; l++) { if (pixels[l].a > 0) { array3[l] = Color32.Lerp(array3[l], array[l], (float)(int)pixels[l].a / 255f); } } _previewTexture.SetPixels32(array3); _previewTexture.Apply(false, false); ApplyToTarget(); } public Texture2D ComposeBaseAware(Texture baseTexture, Texture2D overlay, HeraldrySelection selection) { return ComposeOverlay(selection.objectType, baseTexture, selection); } public Texture2D ComposeCapeBaseAware(Texture baseTexture, HeraldrySelection selection) { return ComposeOverlay("Cape", baseTexture, selection); } public string SelectionKey(string type, HeraldrySelection selection) { return string.Join("|", type, _cacheRevision, selection.styleId, selection.emblemId, selection.secondaryColorId, selection.emblemScale.ToString(CultureInfo.InvariantCulture)); } public Texture2D ComposeOverlay(string type, Texture baseTexture, HeraldrySelection selection) { //IL_02de: Unknown result type (might be due to invalid IL or missing references) //IL_02e3: Unknown result type (might be due to invalid IL or missing references) //IL_02ff: Unknown result type (might be due to invalid IL or missing references) //IL_0301: Unknown result type (might be due to invalid IL or missing references) //IL_030b: Unknown result type (might be due to invalid IL or missing references) //IL_030d: Unknown result type (might be due to invalid IL or missing references) //IL_0317: Unknown result type (might be due to invalid IL or missing references) //IL_0325: Expected O, but got Unknown //IL_0337: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)baseTexture == (Object)null || !_registry.Validate(type, selection)) { return null; } string key = "overlay|" + ((Object)baseTexture).GetInstanceID() + "|" + SelectionKey(type, selection); if (_baseCache.TryGetValue(key, out var value) && (Object)(object)value.Texture != (Object)null) { value.References++; return value.Texture; } HeraldryStyleDef heraldryStyleDef = _registry.Style(selection.styleId); HeraldryProfileDef heraldryProfileDef = _registry.Profile(heraldryStyleDef.profileId); HeraldryEmblemDef heraldryEmblemDef = _registry.Emblem(selection.emblemId); Texture2D texture = _emblems.GetTexture(heraldryEmblemDef); if ((Object)(object)texture == (Object)null && string.IsNullOrEmpty(heraldryEmblemDef.shape)) { return null; } int num; Color32[] art; int num2; if ((Object)(object)texture != (Object)null) { art = texture.GetPixels32(); num = ((Texture)texture).width; num2 = ((Texture)texture).height; } else { Compose(type, selection); art = _composedTexture.GetPixels32(); Color32[] pixels = _emblemMask.GetPixels32(); for (int i = 0; i < art.Length; i++) { art[i].a = pixels[i].a; } num2 = 128; int num3 = 128; int num4 = 128; int num5 = -1; int num6 = -1; for (int j = 0; j < 128; j++) { for (int k = 0; k < 128; k++) { if (art[j * 128 + k].a > 0) { num3 = Math.Min(num3, k); num4 = Math.Min(num4, j); num5 = Math.Max(num5, k); num6 = Math.Max(num6, j); } } } if (num5 < num3) { return null; } num = num5 - num3 + 1; num2 = num6 - num4 + 1; Color32[] array = (Color32[])(object)new Color32[num * num2]; for (int l = 0; l < num2; l++) { Array.Copy(art, (num4 + l) * 128 + num3, array, l * num, num); } art = array; } int num7 = baseTexture.width; int num8 = baseTexture.height; if (num7 < 1 || num8 < 1 || num7 > 4096 || num8 > 4096) { throw new InvalidOperationException("Unsupported base texture dimensions."); } int num9 = Math.Max(0, Math.Min(2048, heraldryProfileDef.minimumResolution)); if (Math.Max(num7, num8) < num9) { float num10 = (float)num9 / (float)Math.Max(num7, num8); num7 = (int)((float)num7 * num10); num8 = (int)((float)num8 * num10); } RenderTexture temporary = RenderTexture.GetTemporary(num7, num8, 0, (RenderTextureFormat)0); RenderTexture active = RenderTexture.active; Texture2D val = null; try { Graphics.Blit(baseTexture, temporary); RenderTexture.active = temporary; val = new Texture2D(num7, num8, (TextureFormat)4, true) { name = "FehuHeraldry_" + type + "_" + ((Object)baseTexture).name, wrapMode = baseTexture.wrapMode, filterMode = baseTexture.filterMode, anisoLevel = baseTexture.anisoLevel }; val.ReadPixels(new Rect(0f, 0f, (float)num7, (float)num8), 0, 0, false); Color32[] pixels2 = val.GetPixels32(); HeraldryPixelComposer.Overlay(pixels2, num7, num8, art, num, num2, heraldryProfileDef, selection.emblemScale); val.SetPixels32(pixels2); val.Apply(true, true); _baseCache[key] = new GeneratedTexture { Texture = val, References = 1 }; return val; } catch { if ((Object)(object)val != (Object)null) { Object.Destroy((Object)(object)val); } throw; } finally { RenderTexture.active = active; RenderTexture.ReleaseTemporary(temporary); } } public void ReleaseGenerated(Texture texture) { if ((Object)(object)texture == (Object)null) { return; } string text = null; foreach (KeyValuePair<string, GeneratedTexture> item in _baseCache) { if ((Object)(object)item.Value.Texture == (Object)(object)texture) { text = item.Key; break; } } if (text != null) { GeneratedTexture generatedTexture = _baseCache[text]; generatedTexture.References = Math.Max(0, generatedTexture.References - 1); if (generatedTexture.References == 0) { _baseCache.Remove(text); Object.Destroy((Object)(object)generatedTexture.Texture); } } } public void Bind(Renderer target) { RestoreTarget(); _targetRenderer = target; _originalMaterial = (((Object)(object)target == (Object)null) ? null : target.sharedMaterial); ApplyToTarget(); } private void ApplyToTarget() { //IL_0037: Unknown result type (might be due to invalid IL or missing references) //IL_0041: Expected O, but got Unknown if (!((Object)(object)_targetRenderer == (Object)null) && !((Object)(object)_composedTexture == (Object)null)) { if ((Object)(object)_previewMaterial == (Object)null) { _previewMaterial = new Material(_targetRenderer.sharedMaterial); } _previewMaterial.mainTexture = (Texture)(object)_composedTexture; _targetRenderer.material = _previewMaterial; } } private void RestoreTarget() { if ((Object)(object)_targetRenderer != (Object)null && (Object)(object)_originalMaterial != (Object)null) { _targetRenderer.sharedMaterial = _originalMaterial; } _targetRenderer = null; _originalMaterial = null; } private void DrawEmblem(Color32[] pixels, HeraldryEmblemDef emblem, HeraldryProfileDef profile, Color32 color, float scale) { //IL_0137: Unknown result type (might be due to invalid IL or missing references) //IL_013c: Unknown result type (might be due to invalid IL or missing references) //IL_013e: 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_0159: Unknown result type (might be due to invalid IL or missing references) //IL_0177: Unknown result type (might be due to invalid IL or missing references) //IL_017e: Unknown result type (might be due to invalid IL or missing references) //IL_0183: Unknown result type (might be due to invalid IL or missing references) //IL_034e: Unknown result type (might be due to invalid IL or missing references) //IL_0350: Unknown result type (might be due to invalid IL or missing references) //IL_035f: Unknown result type (might be due to invalid IL or missing references) int num = Mathf.Clamp(Mathf.RoundToInt(128f * scale * profile.emblemScale * 0.5f), 5, 60); int num2 = Mathf.RoundToInt(profile.emblemCenterX * 127f); int num3 = Mathf.RoundToInt(profile.emblemCenterY * 127f); Texture2D texture = _emblems.GetTexture(emblem); if ((Object)(object)texture != (Object)null) { Color32[] pixels2 = texture.GetPixels32(); int width = ((Texture)texture).width; int height = ((Texture)texture).height; for (int i = -num; i <= num; i++) { for (int j = -num; j <= num; j++) { if (Math.Abs(j) > num || Math.Abs(i) > num) { continue; } int num4 = num2 + j; int num5 = num3 + i; if (profile.mirrorU && num4 >= 64) { num4 = num2 - j; } if (profile.mirrorV) { num5 = 127 - num5; } if (num4 >= 0 && num4 < 128 && num5 >= 0 && num5 < 128) { int num6 = Mathf.Clamp((j + num) * width / (num * 2 + 1), 0, width - 1); int num7 = Mathf.Clamp((i + num) * height / (num * 2 + 1), 0, height - 1); Color32 val = pixels2[num7 * width + num6]; if (val.a != 0) { int num8 = num5 * 128 + num4; pixels[num8] = val; _emblemMask.SetPixel(num4, num5, Color32.op_Implicit(new Color32(byte.MaxValue, byte.MaxValue, byte.MaxValue, val.a))); } } } } } else { if (string.IsNullOrEmpty(emblem.shape)) { return; } for (int k = -num; k <= num; k++) { for (int l = -num; l <= num; l++) { float num9 = (float)l / (float)num; float num10 = (float)k / (float)num; bool num11 = ((emblem.shape == "diamond") ? (Math.Abs(num9) + Math.Abs(num10) < 0.9f) : ((!(emblem.shape == "cross")) ? ((!(emblem.shape == "chevron")) ? ((emblem.shape == "ring") ? (Math.Abs(Math.Sqrt(num9 * num9 + num10 * num10) - 0.6200000047683716) < 0.1599999964237213) : (num9 * num9 + num10 * num10 < 0.72f)) : (Math.Abs(num10 + 0.7f + Math.Abs(num9) * 0.9f) < 0.16f || Math.Abs(num9) < 0.13f)) : ((Math.Abs(num9) < 0.23f || Math.Abs(num10) < 0.23f) && Math.Abs(num9) + Math.Abs(num10) < 1.1f))); int num12 = ((profile.mirrorU && num2 + l >= 64) ? (num2 - l) : (num2 + l)); int num13 = (profile.mirrorV ? (127 - (num3 + k)) : (num3 + k)); if (num11 && num12 >= 0 && num12 < 128 && num13 >= 0 && num13 < 128) { pixels[num13 * 128 + num12] = color; _emblemMask.SetPixel(num12, num13, Color.white); } } } } } private static Color32 ToColor(HeraldryColorDef def) { //IL_0020: Unknown result type (might be due to invalid IL or missing references) //IL_0025: Unknown result type (might be due to invalid IL or missing references) float[] rgba = def.rgba; return Color32.op_Implicit(new Color(rgba[0], rgba[1], rgba[2], (rgba.Length > 3) ? rgba[3] : 1f)); } public void Dispose() { RestoreTarget(); if ((Object)(object)_previewTexture != (Object)null) { Object.Destroy((Object)(object)_previewTexture); } if ((Object)(object)_composedTexture != (Object)null) { Object.Destroy((Object)(object)_composedTexture); } if ((Object)(object)_emblemMask != (Object)null) { Object.Destroy((Object)(object)_emblemMask); } foreach (GeneratedTexture value in _baseCache.Values) { if ((Object)(object)value.Texture != (Object)null) { Object.Destroy((Object)(object)value.Texture); } } _baseCache.Clear(); if ((Object)(object)_previewMaterial != (Object)null) { Object.Destroy((Object)(object)_previewMaterial); } } } public static class HeraldryRuntime { public static ManualLogSource Log; public static HeraldrySelectionStore SelectionStore; public static HeraldryRenderer Compositor; public static HeraldrySaveManager Saves; public static void AuditPrefab(Ship ship) { if (!((Object)(object)ship == (Object)null)) { string text = NormalizePrefabIdentity(((Object)((Component)ship).gameObject).name); HeraldryProfileDef heraldryProfileDef = ProfileForShip(text); Log.LogInfo((object)("Normalized ship identity: " + text)); Log.LogInfo((object)((heraldryProfileDef == null) ? ("Unsupported Sail target=" + ((Object)((Component)ship).gameObject).name) : ("Supported Sail profile matched: " + heraldryProfileDef.prefabName))); if (heraldryProfileDef != null) { Renderer resolved; string reason; bool flag = ShipHeraldryTarget.CanApply(ship, heraldryProfileDef, out resolved, out reason); Log.LogInfo((object)(flag ? "Renderer validation PASS; Sail Apply ready." : ("Renderer validation failed: " + reason))); } } } public static IEnumerable<GameObject> FindVanillaShipPrefabs() { ZNetScene instance = ZNetScene.instance; if ((Object)(object)instance == (Object)null || instance.m_prefabs == null) { yield break; } foreach (GameObject prefab in instance.m_prefabs) { if ((Object)(object)prefab != (Object)null && (Object)(object)prefab.GetComponent<Ship>() != (Object)null) { yield return prefab; } } } public static void AuditLoadedPrefabs() { } public static string NormalizePrefabIdentity(string runtimeName) { if (string.IsNullOrEmpty(runtimeName) || !runtimeName.EndsWith("(Clone)", StringComparison.Ordinal)) { return runtimeName; } return runtimeName.Substring(0, runtimeName.Length - "(Clone)".Length); } public static HeraldryProfileDef ProfileForShip(string instanceName) { if (string.IsNullOrEmpty(instanceName) || Compositor == null) { return null; } string prefabName = instanceName; HeraldryRegistry registry = Compositor.Registry; return (from x in registry.Styles("Sail") select registry.Profile(x.profileId)).FirstOrDefault((HeraldryProfileDef x) => x != null && x.prefabName == prefabName); } public static string HierarchyPath(Transform root, Transform child) { if ((Object)(object)root == (Object)null || (Object)(object)child == (Object)null) { return "<null>"; } List<string> list = new List<string>(); Transform val = child; while ((Object)(object)val != (Object)null) { list.Add(((Object)val).name); if ((Object)(object)val == (Object)(object)root) { break; } val = val.parent; } list.Reverse(); return string.Join("/", list); } public static string TextureDescription(Texture texture) { if (!((Object)(object)texture == (Object)null)) { return ((Object)texture).name + " " + texture.width + "x" + texture.height + " " + ((object)texture).GetType().Name + " id=" + ((Object)texture).GetInstanceID(); } return "<null>"; } } public sealed class ShipSailAudit : MonoBehaviour { private int _lastActive = -1; private void LateUpdate() { if (!((Object)(object)((Component)this).GetComponent<Ship>() == (Object)null)) { int num = ((Component)this).GetComponentsInChildren<Renderer>(true).Count((Renderer x) => x.enabled && ((Component)x).gameObject.activeInHierarchy); if (_lastActive != num) { _lastActive = num; HeraldryRuntime.Log.LogInfo((object)("Sail renderer visibility changed ship=" + ((Object)((Component)this).gameObject).name + " activeRenderers=" + num)); } } } } public sealed class HeraldrySelectionStore { public HeraldrySelection Sail; public Ship Target; } public static class HeraldryInput { public static bool AllowsShortcut() { if ((Object)(object)Player.m_localPlayer != (Object)null && ((Object)(object)Chat.instance == (Object)null || !Chat.instance.HasFocus()) && !Console.IsVisible() && !TextInput.IsVisible() && !Menu.IsVisible() && !InventoryGui.IsVisible()) { return !Minimap.InTextInput(); } return false; } } public sealed class ShipHeraldryHotkeys : MonoBehaviour { private ConfigEntry<KeyboardShortcut> _targetKey; public void Configure(ConfigEntry<KeyboardShortcut> targetKey) { _targetKey = targetKey; } private void Update() { //IL_001d: Unknown result type (might be due to invalid IL or missing references) //IL_0022: Unknown result type (might be due to invalid IL or missing references) //IL_0025: Unknown result type (might be due to invalid IL or missing references) //IL_002f: Invalid comparison between Unknown and I4 //IL_003a: Unknown result type (might be due to invalid IL or missing references) //IL_003f: Unknown result type (might be due to invalid IL or missing references) if (HeraldryRuntime.SelectionStore == null || _targetKey == null || !HeraldryInput.AllowsShortcut()) { return; } KeyboardShortcut value = _targetKey.Value; if ((int)((KeyboardShortcut)(ref value)).MainKey == 288) { return; } value = _targetKey.Value; if (!((KeyboardShortcut)(ref value)).IsDown()) { return; } Ship val = FindLookTarget(Player.m_localPlayer); HeraldryRuntime.SelectionStore.Target = val; if ((Object)(object)val != (Object)null) { HeraldryRuntime.AuditPrefab(val); if ((Object)(object)((Component)val).GetComponent<ShipSailAudit>() == (Object)null) { ((Component)val).gameObject.AddComponent<ShipSailAudit>(); } HeraldryRuntime.Log.LogInfo((object)("Targeted one ship using Heraldry target hotkey: " + ((Object)((Component)val).gameObject).name + " instance=" + ((Object)val).GetInstanceID())); } else { HeraldryRuntime.Log.LogInfo((object)"Heraldry targeting found no vanilla ship under player crosshair."); } } private static Ship FindLookTarget(Player player) { //IL_0019: Unknown result type (might be due to invalid IL or missing references) //IL_0028: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)GameCamera.instance == (Object)null) { return null; } RaycastHit val = default(RaycastHit); if (!Physics.Raycast(((Component)GameCamera.instance).transform.position, ((Component)GameCamera.instance).transform.forward, ref val, 15f)) { return null; } return ((Component)((RaycastHit)(ref val)).collider).GetComponentInParent<Ship>(); } } public sealed class ShipHeraldryTarget : WorldHeraldryTarget { private static readonly HashSet<ShipHeraldryTarget> Active = new HashSet<ShipHeraldryTarget>(); public override string TargetType => "Sail"; private void Awake() { Active.Add(this); } public static void RestoreAll() { ShipHeraldryTarget[] array = Active.ToArray(); foreach (ShipHeraldryTarget shipHeraldryTarget in array) { if ((Object)(object)shipHeraldryTarget != (Object)null) { shipHeraldryTarget.Restore(); } } } protected override void OnDestroy() { base.OnDestroy(); Active.Remove(this); } public static bool CanApply(Ship ship, HeraldryProfileDef profile, out Renderer resolved, out string reason) { resolved = null; reason = null; if ((Object)(object)ship == (Object)null || profile == null || HeraldryRuntime.NormalizePrefabIdentity(((Object)((Component)ship).gameObject).name) != profile.prefabName) { reason = "Target a supported ship with F9."; return false; } if (profile.rendererPaths == null || profile.rendererPaths.Length != 1 || string.IsNullOrEmpty(profile.materialProperty)) { reason = "Sail profile is incomplete."; return false; } Renderer[] array = (from x in ((Component)ship).GetComponentsInChildren<Renderer>(true) where HeraldryRuntime.HierarchyPath(((Component)ship).transform, ((Component)x).transform) == profile.rendererPaths[0] select x).ToArray(); if (array.Length != 1) { reason = "Verified sail renderer path was not found or is ambiguous."; return false; } Renderer val = array[0]; if (((object)val).GetType().Name != profile.rendererType) { reason = "Sail renderer type mismatch."; return false; } ShipHeraldryTarget component = ((Component)ship).GetComponent<ShipHeraldryTarget>(); Material val2 = (((Object)(object)component == (Object)null || component.Materials == null) ? val.sharedMaterial : component.Materials.Source(val, 0)); if ((Object)(object)val2 == (Object)null || (Object)(object)val2.shader == (Object)null || ((Object)val2.shader).name != profile.shaderName || ((Object)val2).name != profile.materialName) { reason = "Sail material or shader mismatch."; return false; } if (!val2.HasProperty(profile.materialProperty)) { reason = "Verified sail albedo property is missing."; return false; } Texture texture = val2.GetTexture(profile.materialProperty); if ((Object)(object)texture == (Object)null || ((Object)texture).name != profile.sourceTextureName || texture.width != profile.sourceTextureWidth || texture.height != profile.sourceTextureHeight) { reason = "Sail source texture mismatch."; return false; } resolved = val; return true; } protected override bool Resolve(out List<HeraldrySurface> surfaces, out string reason) { surfaces = new List<HeraldrySurface>(); if (!Ready(out reason)) { return false; } HeraldryProfileDef heraldryProfileDef = HeraldryRuntime.ProfileForShip(base.Prefab); if (!CanApply(((Component)this).GetComponent<Ship>(), heraldryProfileDef, out var resolved, out reason)) { return false; } surfaces.Add(new HeraldrySurface { Renderer = resolved, Slot = 0, Property = heraldryProfileDef.materialProperty }); return true; } } [HarmonyPatch(typeof(Ship), "Awake")] internal static class HeraldryShipLifecyclePatch { [HarmonyPostfix] private static void Postfix(Ship __instance) { if (HeraldryRuntime.ProfileForShip(HeraldryRuntime.NormalizePrefabIdentity(((Object)__instance).name)) != null && (Object)(object)((Component)__instance).GetComponent<ShipHeraldryTarget>() == (Object)null) { ((Component)__instance).gameObject.AddComponent<ShipHeraldryTarget>(); } } } public interface IHeraldryTarget { string DisplayName { get; } HeraldrySelection SavedSelection { get; } bool CanApply(out string reason); bool Apply(HeraldrySelection selection, out string reason); bool RestoreOriginal(out string reason); Texture2D Preview(HeraldrySelection selection); } public abstract class WorldHeraldryTarget : MonoBehaviour, IHeraldryTarget { private static readonly HashSet<WorldHeraldryTarget> Active = new HashSet<WorldHeraldryTarget>(); protected HeraldryMaterialController Materials; private float _nextCheck; private string _appliedKey; private string _previewKey; private string _lastWarning; private Texture2D _preview; private readonly HashSet<string> _diagnosed = new HashSet<string>(); public abstract string TargetType { get; } public string Prefab => HeraldryRuntime.NormalizePrefabIdentity(((Object)((Component)this).gameObject).name); public string DisplayName => Prefab + " (single instance)"; public HeraldrySelection SavedSelection { get { if (HeraldryRuntime.Saves == null || !Identity(out var worldId, out var objectId)) { return null; } return HeraldryRuntime.Saves.GetWorld(worldId, objectId, Prefab, TargetType); } } private void OnEnable() { Active.Add(this); } public static void RefreshAll() { WorldHeraldryTarget[] array = Active.ToArray(); foreach (WorldHeraldryTarget worldHeraldryTarget in array) { if ((Object)(object)worldHeraldryTarget != (Object)null) { worldHeraldryTarget.Refresh(); } } } protected abstract bool Resolve(out List<HeraldrySurface> surfaces, out string reason); private unsafe bool Identity(out long worldId, out string objectId) { //IL_0054: Unknown result type (might be due to invalid IL or missing references) //IL_0059: Unknown result type (might be due to invalid IL or missing references) //IL_005e: Unknown result type (might be due to invalid IL or missing references) //IL_005f: Unknown result type (might be due to invalid IL or missing references) worldId = 0L; objectId = null; ZNetView component = ((Component)this).GetComponent<ZNetView>(); if ((Object)(object)ZNet.instance == (Object)null || ZNet.instance.GetWorld() == null || (Object)(object)component == (Object)null || !component.IsValid() || component.GetZDO() == null) { return false; } worldId = ZNet.instance.GetWorldUID(); ZDOID uid = component.GetZDO().m_uid; if (worldId == 0L || uid == ZDOID.None) { return false; } objectId = ((object)(*(ZDOID*)(&uid))/*cast due to .constrained prefix*/).ToString(); return true; } protected bool Ready(out string reason) { reason = null; if (HeraldryRuntime.Compositor == null || HeraldryRuntime.Saves == null) { reason = "Heraldry is not initialized."; return false; } if (Materials == null) { Materials = new HeraldryMaterialController(HeraldryRuntime.Compositor); } return true; } public bool CanApply(out string reason) { if (!Ready(out reason) || !Resolve(out var _, out reason)) { return false; } if (!Identity(out var _, out var _)) { reason = "Waiting for world object identity."; return false; } return true; } public bool Apply(HeraldrySelection selection, out string reason) { reason = null; if (!CanApply(out reason) || !HeraldryRuntime.Compositor.Registry.ValidateTarget(TargetType, Prefab, selection)) { if (reason == null) { reason = "Select an available emblem and valid style."; } return false; } if (!Resolve(out var surfaces, out reason) || !ApplyVisual(surfaces, selection, out reason)) { return false; } Identity(out var worldId, out var objectId); HeraldrySelection savedSelection = SavedSelection; try { HeraldryRuntime.Saves.SetWorld(worldId, objectId, Prefab, TargetType, selection); } catch (Exception ex) { Materials.Restore(); _appliedKey = null; HeraldryRuntime.Saves.RevertWorldInMemory(worldId, objectId, Prefab, TargetType, savedSelection); reason = "Heraldry save failed: " + ex.Message; Refresh(); return false; } _appliedKey = HeraldryRuntime.Compositor.SelectionKey(TargetType, selection); HeraldryRuntime.Log.LogInfo((object)(TargetType + " Apply: prefab=" + Prefab + " object=" + objectId + " emblem=" + selection.emblemId)); return true; } public bool RestoreOriginal(out string reason) { reason = null; if (!Ready(out reason) || !Identity(out var worldId, out var objectId)) { reason = "Waiting for world object identity."; return false; } HeraldrySelection savedSelection = SavedSelection; try { HeraldryRuntime.Saves.SetWorld(worldId, objectId, Prefab, TargetType, null); } catch (Exception ex) { HeraldryRuntime.Saves.RevertWorldInMemory(worldId, objectId, Prefab, TargetType, savedSelection); reason = ex.Message; return false; } Restore(); HeraldryRuntime.Log.LogInfo((object)(TargetType + " Restore Original: prefab=" + Prefab + " object=" + objectId)); return true; } public Texture2D Preview(HeraldrySelection selection) { if (!Ready(out var reason) || !Resolve(out var surfaces, out reason) || !HeraldryRuntime.Compositor.Registry.ValidateTarget(TargetType, Prefab, selection)) { ClearPreview(); return null; } Texture val = Materials.SourceTexture(surfaces[0]); if ((Object)(object)val == (Object)null) { return null; } string text = ((Object)val).GetInstanceID() + "|" + HeraldryRuntime.Compositor.SelectionKey(TargetType, selection); if ((Object)(object)_preview != (Object)null && text == _previewKey) { return _preview; } ClearPreview(); try { _preview = HeraldryRuntime.Compositor.ComposeOverlay(TargetType, val, selection); _previewKey = text; } catch (Exception ex) { Warn("Preview failed: " + ex.Message); } return _preview; } private bool ApplyVisual(List<HeraldrySurface> surfaces, HeraldrySelection selection, out string reason) { foreach (HeraldrySurface surface in surfaces) { Material val = Materials.Source(surface.Renderer, surface.Slot); if (_diagnosed.Add(((Object)surface.Renderer).name + "|" + surface.Slot + "|" + ((Object)val).name)) { HeraldryAlbedo.Diagnose(((Component)this).gameObject, Prefab, surface, val); } } return Materials.Apply(surfaces, (Texture texture) => HeraldryRuntime.Compositor.ComposeOverlay(TargetType, texture, selection), out reason); } private void Update() { if (!(Time.unscaledTime < _nextCheck)) { _nextCheck = Time.unscaledTime + 0.5f; Refresh(); } } public void Refresh() { try { if (!Ready(out var reason)) { return; } HeraldrySelection savedSelection = SavedSelection; if (savedSelection == null) { if (Materials.Count > 0 || _appliedKey != null) { Restore(); } return; } if (!HeraldryRuntime.Compositor.Registry.ValidateTarget(TargetType, Prefab, savedSelection)) { Restore(); Warn("Missing emblem/style; saved ID retained: " + savedSelection.emblemId); return; } string text = HeraldryRuntime.Compositor.SelectionKey(TargetType, savedSelection); if (!(_appliedKey == text) || Materials.Count <= 0 || Materials.HasChanged()) { if (!Resolve(out var surfaces, out reason)) { Restore(); Warn(reason); } else if (ApplyVisual(surfaces, savedSelection, out reason)) { _appliedKey = text; _lastWarning = null; HeraldryRuntime.Log.LogInfo((object)(TargetType + " Reapply: " + Prefab + " emblem=" + savedSelection.emblemId)); } else { Warn(reason); } } } catch (Exception ex) { Warn("Refresh failed: " + ex.Message); } } private void Warn(string reason) { if (!(_lastWarning == reason)) { _lastWarning = reason; HeraldryRuntime.Log.LogWarning((object)(TargetType + ": " + reason)); } } private void ClearPreview() { if ((Object)(object)_preview != (Object)null && HeraldryRuntime.Compositor != null) { HeraldryRuntime.Compositor.ReleaseGenerated((Texture)(object)_preview); } _preview = null; _previewKey = null; } public void Restore() { if (Materials != null) { Materials.Restore(); } ClearPreview(); _appliedKey = null; } protected virtual void OnDestroy() { Restore(); Active.Remove(this); } } } namespace FehuHeraldry.Core { public enum HeraldryObjectType { Sail, Banner, Cape } [Serializable] public sealed class HeraldryRegistryDef { public int schemaVersion = 1; public HeraldryStyleDef[] styles = new HeraldryStyleDef[0]; public HeraldryEmblemDef[] emblems = new HeraldryEmblemDef[0]; public HeraldryColorDef[] colors = new HeraldryColorDef[0]; public HeraldryProfileDef[] profiles = new HeraldryProfileDef[0]; } [Serializable] public sealed class HeraldryStyleDef { public string styleId; public string displayName; public string objectType; public string profileId; public string prefabName; public bool enabled = true; public bool allowHeraldry = true; public string baseColorId = "natural"; } [Serializable] public sealed class HeraldryEmblemDef { public string emblemId; public string displayName; public string category; public string shape; public string source; [NonSerialized] public Texture2D texture; [NonSerialized] public bool ownsTexture; [NonSerialized] public string texturePath; public bool enabled = true; } [Serializable] public sealed class HeraldryColorDef { public string colorId; public string displayName; public float[] rgba; public bool enabled = true; } [Serializable] public sealed class HeraldryProfileDef { public string profileId; public string objectType; public string rendererName; public string prefabName; public string rendererType; public string materialName; public string shaderName; public string sourceTextureName; public int sourceTextureWidth; public int sourceTextureHeight; public string[] rendererPaths = new string[0]; public string materialProperty = "_MainTex"; public string maskAsset; public float emblemCenterX = 0.5f; public float emblemCenterY = 0.5f; public float emblemScale = 0.28f; public float emblemRotation; public float emblemOpacity = 1f; public float uvMinX; public float uvMinY; public float uvMaxX = 1f; public float uvMaxY = 1f; public float surfaceAspect; public int styleGrid = 1; public int minimumResolution; public bool tintPrimary = true; public bool supportsTrim = true; public bool mirrorU; public bool mirrorV; } [Serializable] public sealed class HeraldrySelection { public string styleId; public string emblemId; public string primaryColorId; public string secondaryColorId; public string trimColorId; public string placementId; public string objectType = "Sail"; public float emblemScale = 1f; public HeraldrySelection Clone() { return (HeraldrySelection)MemberwiseClone(); } } [Serializable] public sealed class PlayerHeraldrySave { public int schemaVersion = 1; public HeraldrySelection sail; public HeraldrySelection banner; public HeraldrySelection cape; public CapeHeraldryAssignment[] capeAssignments = new CapeHeraldryAssignment[0]; public WorldHeraldryAssignment[] worldAssignments = new WorldHeraldryAssignment[0]; } [Serializable] public sealed class CapeHeraldryAssignment { public long playerId; public string prefabName; public HeraldrySelection selection; } [Serializable] public sealed class WorldHeraldryAssignment { public long worldId; public string objectId; public string prefabName; public string objectType; public HeraldrySelection selection; } public sealed class HeraldryRegistry { private HeraldryEmblemDef[] _externalEmblems = new HeraldryEmblemDef[0]; public HeraldryRegistryDef Data { get; private set; } public IEnumerable<HeraldryEmblemDef> Emblems => from x in (from x in _externalEmblems.Concat(Data.emblems) where x.enabled select x).GroupBy<HeraldryEmblemDef, string>((HeraldryEmblemDef x) => x.emblemId, StringComparer.OrdinalIgnoreCase) select x.First(); public IEnumerable<HeraldryColorDef> Colors => Data.colors.Where((HeraldryColorDef x) => x.enabled); public HeraldryRegistry(HeraldryRegistryDef data) { Data = data; } public IEnumerable<HeraldryStyleDef> Styles(string type) { return Data.styles.Where((HeraldryStyleDef x) => x.enabled && x.allowHeraldry && x.objectType == type && Profile(x.profileId) != null); } public HeraldryStyleDef Style(string id) { return Data.styles.FirstOrDefault((HeraldryStyleDef x) => x.styleId == id && x.enabled); } public HeraldryEmblemDef Emblem(string id) { return Emblems.FirstOrDefault((HeraldryEmblemDef x) => string.Equals(x.emblemId, id, StringComparison.OrdinalIgnoreCase)); } public void SetExternalEmblems(IEnumerable<HeraldryEmblemDef> emblems) { _externalEmblems = (emblems ?? Enumerable.Empty<HeraldryEmblemDef>()).ToArray(); } public HeraldryColorDef Color(string id) { return Data.colors.FirstOrDefault((HeraldryColorDef x) => x.colorId == id && x.enabled); } public HeraldryProfileDef Profile(string id) { return Data.profiles.FirstOrDefault((HeraldryProfileDef x) => x.profileId == id); } public HeraldrySelection Default(string type) { HeraldryStyleDef heraldryStyleDef = Styles(type).FirstOrDefault(); HeraldryColorDef[] array = Colors.ToArray(); HeraldryEmblemDef heraldryEmblemDef = Emblems.FirstOrDefault(); return new HeraldrySelection { objectType = type, styleId = ((heraldryStyleDef == null) ? "" : heraldryStyleDef.styleId), emblemId = ((heraldryEmblemDef == null) ? "" : heraldryEmblemDef.emblemId), primaryColorId = ((array.Length > 1) ? array[1].colorId : "natural"), secondaryColorId = ((array.Length > 2) ? array[2].colorId : "white"), trimColorId = ((array.Length > 3) ? array[3].colorId : "gold"), placementId = ((heraldryStyleDef == null) ? "" : heraldryStyleDef.profileId), emblemScale = 1f }; } public HeraldrySelection DefaultForStyle(HeraldryStyleDef style) { HeraldrySelection heraldrySelection = Default(style.objectType); heraldrySelection.styleId = style.styleId; heraldrySelection.placementId = style.profileId; heraldrySelection.primaryColorId = ((Color("red") != null) ? "red" : heraldrySelection.primaryColorId); heraldrySelection.secondaryColorId = ((Color("black") != null) ? "black" : heraldrySelection.secondaryColorId); heraldrySelection.trimColorId = ((Color("black") != null) ? "black" : heraldrySelection.trimColorId); heraldrySelection.emblemId = ((Emblem("fehu_rune") != null) ? "fehu_rune" : heraldrySelection.emblemId); heraldrySelection.emblemScale = 1.5f; return heraldrySelection; } public bool Validate(string type, HeraldrySelection s) { if (ValidateExceptEmblem(type, s)) { return Emblem(s.emblemId) != null; } return false; } public bool ValidateTarget(string type, string prefab, HeraldrySelection selection) { HeraldryStyleDef heraldryStyleDef = ((selection == null) ? null : Style(selection.styleId)); if (Validate(type, selection)) { return heraldryStyleDef.prefabName == prefab; } return false; } public bool ValidateExceptEmblem(string type, HeraldrySelection s) { HeraldryStyleDef heraldryStyleDef = ((s == null) ? null : Style(s.styleId)); if (heraldryStyleDef != null && s.objectType == type && heraldryStyleDef.objectType == type && Profile(heraldryStyleDef.profileId) != null && Color(s.primaryColorId) != null && Color(s.secondaryColorId) != null && Color(s.trimColorId) != null && s.emblemScale >= 0.4f) { return s.emblemScale <= 2f; } return false; } } public sealed class HeraldrySaveManager { private readonly string _path; private readonly HeraldryRegistry _registry; private readonly ManualLogSource _log; public PlayerHeraldrySave Current { get; private set; } public HeraldrySaveManager(string configPath, HeraldryRegistry registry, ManualLogSource log) { _path = Path.Combine(configPath, "FehuHeraldry", "player-heraldry.json"); _registry = registry; _log = log; Load(); } private PlayerHeraldrySave Defaults() { return new PlayerHeraldrySave { sail = _registry.Default("Sail"), banner = _registry.Default("Banner"), cape = _registry.Default("Cape") }; } private void Load() { if (!File.Exists(_path)) { Current = Defaults(); return; } try { Current = JsonUtility.FromJson<PlayerHeraldrySave>(File.ReadAllText(_path)) ?? Defaults(); if (!_registry.ValidateExceptEmblem("Sail", Current.sail)) { Current.sail = _registry.Default("Sail"); } else { WarnMissingEmblem("Sail", Current.sail); } if (!_registry.ValidateExceptEmblem("Banner", Current.banner)) { Current.banner = _registry.Default("Banner"); } else { WarnMissingEmblem("Banner", Current.banner); } if (!_registry.ValidateExceptEmblem("Cape", Current.cape)) { Current.cape = _registry.Default("Cape"); } else { WarnMissingEmblem("Cape", Current.cape); } } catch (Exception ex) { _log.LogWarning((object)("Heraldry save could not be loaded; defaults are active. " + ex.Message)); Current = Defaults(); } } private void WarnMissingEmblem(string type, HeraldrySelection selection) { if (_registry.Emblem(selection.emblemId) == null) { _log.LogWarning((object)("Saved " + type + " emblem is unavailable; retaining its ID until the library is restored.")); } } public void Save() { Directory.CreateDirectory(Path.GetDirectoryName(_path)); string text = _path + ".tmp"; File.WriteAllText(text, JsonUtility.ToJson((object)Current, true)); if (File.Exists(_path)) { File.Replace(text, _path, _path + ".bak"); } else { File.Move(text, _path); } } public HeraldrySelection Get(string type) { if (!(type == "Banner")) { if (!(type == "Cape")) { return Current.sail; } return Current.cape; } return Current.banner; } public void Set(string type, HeraldrySelection selection) { if (type == "Banner") { Current.banner = selection; } else if (type == "Cape") { Current.cape = selection; } else { Current.sail = selection; } Save(); } public HeraldrySelection GetCape(long playerId, string prefab) { return (Current.capeAssignments ?? new CapeHeraldryAssignment[0]).FirstOrDefault((CapeHeraldryAssignment x) => x != null && x.playerId == playerId && x.prefabName == prefab)?.selection; } public void RevertCapeInMemory(long playerId, string prefab, HeraldrySelection selection) { List<CapeHeraldryAssignment> list = (Current.capeAssignments ?? new CapeHeraldryAssignment[0]).Where((CapeHeraldryAssignment x) => x != null && (x.playerId != playerId || !(x.prefabName == prefab))).ToList(); if (selection != null) { list.Add(new CapeHeraldryAssignment { playerId = playerId, prefabName = prefab, selection = selection.Clone() }); } Current.capeAssignments = list.ToArray(); } public void SetCape(long playerId, string prefab, HeraldrySelection selection) { if (playerId == 0L || string.IsNullOrEmpty(prefab)) { throw new ArgumentException("A real local character and cape are required."); } RevertCapeInMemory(playerId, prefab, selection); Save(); } public HeraldrySelection GetWorld(long worldId, string objectId, string prefab, string type) { return (Current.worldAssignments ?? new WorldHeraldryAssignment[0]).FirstOrDefault((WorldHeraldryAssignment x) => x != null && x.worldId == worldId && x.objectId == objectId && x.prefabName == prefab && x.objectType == type)?.selection; } public void RevertWorldInMemory(long worldId, string objectId, string prefab, string type, HeraldrySelection selection) { List<WorldHeraldryAssignment> list = (Current.worldAssignments ?? new WorldHeraldryAssignment[0]).Where((WorldHeraldryAssignment x) => x != null && (x.worldId != worldId || !(x.objectId == objectId) || !(x.prefabName == prefab) || !(x.objectType == type))).ToList(); if (selection != null) { list.Add(new WorldHeraldryAssignment { worldId = worldId, objectId = objectId, prefabName = prefab, objectType = type, selection = selection.Clone() }); } Current.worldAssignments = list.ToArray(); } public void SetWorld(long worldId, string objectId, string prefab, string type, HeraldrySelection selection) { if (worldId == 0L || string.IsNullOrEmpty(objectId) || string.IsNullOrEmpty(prefab)) { throw new ArgumentException("A real world object identity is required."); } RevertWorldInMemory(worldId, objectId, prefab, type, selection); Save(); } } } namespace FehuHeraldry.Assets { public sealed class HeraldryAssetLoader { private readonly string _assetPath; private readonly ManualLogSource _log; public HeraldryAssetLoader(string pluginPath, ManualLogSource log) { _assetPath = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location); _log = log; } public HeraldryRegistry LoadRegistry() { string text = Path.Combine(_assetPath, "Config", "registry.json"); if (!File.Exists(text)) { Directory.CreateDirectory(Path.GetDirectoryName(text)); File.WriteAllText(text, JsonUtility.ToJson((object)new HeraldryRegistryDef(), true)); _log.LogWarning((object)("Registry missing; created empty registry at " + text)); } HeraldryRegistryDef heraldryRegistryDef = JsonUtility.FromJson<HeraldryRegistryDef>(File.ReadAllText(text)); if (heraldryRegistryDef == null || heraldryRegistryDef.schemaVersion != 1) { throw new InvalidDataException("Unsupported Heraldry registry schema at " + text); } return new HeraldryRegistry(heraldryRegistryDef); } public Color32[] LoadMask(string relativePath, int width, int height) { //IL_004f: Unknown result type (might be due to invalid IL or missing references) //IL_0055: Expected O, but got Unknown //IL_006f: Unknown result type (might be due to invalid IL or missing references) //IL_0092: Unknown result type (might be due to invalid IL or missing references) //IL_00a1: Unknown result type (might be due to invalid IL or missing references) //IL_00ad: Unknown result type (might be due to invalid IL or missing references) //IL_00c6: Unknown result type (might be due to invalid IL or missing references) //IL_00d3: Expected O, but got Unknown if (string.IsNullOrEmpty(relativePath)) { return null; } string fullPath = Path.GetFullPath(_assetPath + Path.DirectorySeparatorChar); string fullPath2 = Path.GetFullPath(Path.Combine(_assetPath, relativePath)); if (!fullPath2.StartsWith(fullPath, StringComparison.OrdinalIgnoreCase) || !File.Exists(fullPath2)) { return null; } Texture2D val = new Texture2D(2, 2, (TextureFormat)4, false); if (!ImageCo